Hi Niklas, Thank you for the patch. On Tuesday, 13 February 2018 01:01:32 EEST Niklas Söderlund wrote: > A V4L2 driver for Renesas R-Car MIPI CSI-2 receiver. The driver > supports the R-Car Gen3 SoCs where separate CSI-2 hardware blocks are > connected between the video sources and the video grabbers (VIN). > > Driver is based on a prototype by Koji Matsuoka in the Renesas BSP. > > Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@xxxxxxxxxxxx> > Reviewed-by: Hans Verkuil <hans.verkuil@xxxxxxxxx> > --- > drivers/media/platform/rcar-vin/Kconfig | 12 + > drivers/media/platform/rcar-vin/Makefile | 1 + > drivers/media/platform/rcar-vin/rcar-csi2.c | 884 +++++++++++++++++++++++++ > 3 files changed, 897 insertions(+) > create mode 100644 drivers/media/platform/rcar-vin/rcar-csi2.c [snip] > diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c > b/drivers/media/platform/rcar-vin/rcar-csi2.c new file mode 100644 > index 0000000000000000..c0c2a763151bc928 > --- /dev/null > +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c [snip] > +static const struct phypll_hsfreqrange hsfreqrange_m3w_h3es1[] = { > + { .mbps = 80, .reg = 0x00 }, > + { .mbps = 90, .reg = 0x10 }, > + { .mbps = 100, .reg = 0x20 }, > + { .mbps = 110, .reg = 0x30 }, > + { .mbps = 120, .reg = 0x01 }, > + { .mbps = 130, .reg = 0x11 }, > + { .mbps = 140, .reg = 0x21 }, > + { .mbps = 150, .reg = 0x31 }, > + { .mbps = 160, .reg = 0x02 }, > + { .mbps = 170, .reg = 0x12 }, > + { .mbps = 180, .reg = 0x22 }, > + { .mbps = 190, .reg = 0x32 }, > + { .mbps = 205, .reg = 0x03 }, > + { .mbps = 220, .reg = 0x13 }, > + { .mbps = 235, .reg = 0x23 }, > + { .mbps = 250, .reg = 0x33 }, > + { .mbps = 275, .reg = 0x04 }, > + { .mbps = 300, .reg = 0x14 }, > + { .mbps = 325, .reg = 0x05 }, > + { .mbps = 350, .reg = 0x15 }, > + { .mbps = 400, .reg = 0x25 }, > + { .mbps = 450, .reg = 0x06 }, > + { .mbps = 500, .reg = 0x16 }, > + { .mbps = 550, .reg = 0x07 }, > + { .mbps = 600, .reg = 0x17 }, > + { .mbps = 650, .reg = 0x08 }, > + { .mbps = 700, .reg = 0x18 }, > + { .mbps = 750, .reg = 0x09 }, > + { .mbps = 800, .reg = 0x19 }, > + { .mbps = 850, .reg = 0x29 }, > + { .mbps = 900, .reg = 0x39 }, > + { .mbps = 950, .reg = 0x0A }, > + { .mbps = 1000, .reg = 0x1A }, > + { .mbps = 1050, .reg = 0x2A }, > + { .mbps = 1100, .reg = 0x3A }, > + { .mbps = 1150, .reg = 0x0B }, > + { .mbps = 1200, .reg = 0x1B }, > + { .mbps = 1250, .reg = 0x2B }, > + { .mbps = 1300, .reg = 0x3B }, > + { .mbps = 1350, .reg = 0x0C }, > + { .mbps = 1400, .reg = 0x1C }, > + { .mbps = 1450, .reg = 0x2C }, > + { .mbps = 1500, .reg = 0x3C }, All the other hex values in the file are lowercase, I'd do the same here. > + /* guard */ > + { .mbps = 0, .reg = 0x00 }, > +}; [snip] > +static int rcar_csi2_start(struct rcar_csi2 *priv) > +{ > + const struct rcar_csi2_format *format; > + u32 phycnt, phypll, vcdt = 0, vcdt2 = 0; > + unsigned int i; > + int ret; > + > + dev_dbg(priv->dev, "Input size (%ux%u%c)\n", > + priv->mf.width, priv->mf.height, > + priv->mf.field == V4L2_FIELD_NONE ? 'p' : 'i'); > + > + /* Code is validated in set_fmt */ > + format = rcar_csi2_code_to_fmt(priv->mf.code); You could store the format pointer iin the rcar_csi2 structure to avoid looking it up here. > + /* > + * Enable all Virtual Channels > + * > + * NOTE: It's not possible to get individual datatype for each > + * source virtual channel. Once this is possible in V4L2 > + * it should be used here. > + */ > + for (i = 0; i < 4; i++) { > + u32 vcdt_part; > + > + vcdt_part = VCDT_SEL_VC(i) | VCDT_VCDTN_EN | VCDT_SEL_DTN_ON | > + VCDT_SEL_DT(format->datatype); > + > + /* Store in correct reg and offset */ > + if (i < 2) > + vcdt |= vcdt_part << ((i % 2) * 16); > + else > + vcdt2 |= vcdt_part << ((i % 2) * 16); > + } > + > + switch (priv->lanes) { > + case 1: > + phycnt = PHYCNT_ENABLECLK | PHYCNT_ENABLE_0; > + break; > + case 2: > + phycnt = PHYCNT_ENABLECLK | PHYCNT_ENABLE_1 | PHYCNT_ENABLE_0; > + break; > + case 4: > + phycnt = PHYCNT_ENABLECLK | PHYCNT_ENABLE_3 | PHYCNT_ENABLE_2 | > + PHYCNT_ENABLE_1 | PHYCNT_ENABLE_0; > + break; > + default: > + return -EINVAL; > + } > + > + ret = rcar_csi2_calc_phypll(priv, format->bpp, &phypll); > + if (ret) > + return ret; > + > + /* Clear Ultra Low Power interrupt */ > + if (priv->info->clear_ulps) > + rcar_csi2_write(priv, INTSTATE_REG, > + INTSTATE_INT_ULPS_START | > + INTSTATE_INT_ULPS_END); > + > + /* Init */ > + rcar_csi2_write(priv, TREF_REG, TREF_TREF); > + rcar_csi2_reset(priv); > + rcar_csi2_write(priv, PHTC_REG, 0); > + > + /* Configure */ > + rcar_csi2_write(priv, FLD_REG, FLD_FLD_NUM(2) | FLD_FLD_EN4 | > + FLD_FLD_EN3 | FLD_FLD_EN2 | FLD_FLD_EN); > + rcar_csi2_write(priv, VCDT_REG, vcdt); > + rcar_csi2_write(priv, VCDT2_REG, vcdt2); > + /* Lanes are zero indexed */ > + rcar_csi2_write(priv, LSWAP_REG, > + LSWAP_L0SEL(priv->lane_swap[0] - 1) | > + LSWAP_L1SEL(priv->lane_swap[1] - 1) | > + LSWAP_L2SEL(priv->lane_swap[2] - 1) | > + LSWAP_L3SEL(priv->lane_swap[3] - 1)); > + > + if (priv->info->init_phtw) { > + /* > + * This is for H3 ES2.0 > + * > + * NOTE: Additional logic is needed here when > + * support for V3H and/or M3-N is added > + */ > + rcar_csi2_write(priv, PHTW_REG, 0x01cc01e2); > + rcar_csi2_write(priv, PHTW_REG, 0x010101e3); > + rcar_csi2_write(priv, PHTW_REG, 0x010101e4); > + rcar_csi2_write(priv, PHTW_REG, 0x01100104); > + rcar_csi2_write(priv, PHTW_REG, 0x01030100); > + rcar_csi2_write(priv, PHTW_REG, 0x01800100); > + } > + > + /* Start */ > + rcar_csi2_write(priv, PHYPLL_REG, phypll); > + > + /* Set frequency range if we have it */ > + if (priv->info->csi0clkfreqrange) > + rcar_csi2_write(priv, CSI0CLKFCPR_REG, > + CSI0CLKFREQRANGE(priv->info->csi0clkfreqrange)); > + > + rcar_csi2_write(priv, PHYCNT_REG, phycnt); > + rcar_csi2_write(priv, LINKCNT_REG, LINKCNT_MONITOR_EN | > + LINKCNT_REG_MONI_PACT_EN | LINKCNT_ICLK_NONSTOP); > + rcar_csi2_write(priv, PHYCNT_REG, phycnt | PHYCNT_SHUTDOWNZ); > + rcar_csi2_write(priv, PHYCNT_REG, phycnt | PHYCNT_SHUTDOWNZ | > + PHYCNT_RSTZ); > + > + return rcar_csi2_wait_phy_start(priv); > +} [snip] > +static int rcar_csi2_parse_v4l2(struct rcar_csi2 *priv, > + struct v4l2_fwnode_endpoint *vep) > +{ > + unsigned int i; > + > + /* Only port 0 endpoint 0 is valid */ > + if (vep->base.port || vep->base.id) > + return -ENOTCONN; > + > + if (vep->bus_type != V4L2_MBUS_CSI2) { > + dev_err(priv->dev, "Unsupported bus: 0x%x\n", vep->bus_type); I think you can print this in decimal with %u. > + return -EINVAL; > + } > + > + priv->lanes = vep->bus.mipi_csi2.num_data_lanes; > + if (priv->lanes != 1 && priv->lanes != 2 && priv->lanes != 4) { > + dev_err(priv->dev, "Unsupported number of data-lanes: %u\n", > + priv->lanes); > + return -EINVAL; > + } > + > + for (i = 0; i < ARRAY_SIZE(priv->lane_swap); i++) { > + priv->lane_swap[i] = i < priv->lanes ? > + vep->bus.mipi_csi2.data_lanes[i] : i; > + > + /* Check for valid lane number */ > + if (priv->lane_swap[i] < 1 || priv->lane_swap[i] > 4) { > + dev_err(priv->dev, "data-lanes must be in 1-4 range\n"); > + return -EINVAL; > + } > + } > + > + return 0; > +} [snip] > +static int rcar_csi2_probe_resources(struct rcar_csi2 *priv, > + struct platform_device *pdev) > +{ > + struct resource *res; > + int irq; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + priv->base = devm_ioremap_resource(&pdev->dev, res); > + if (IS_ERR(priv->base)) > + return PTR_ERR(priv->base); > + > + irq = platform_get_irq(pdev, 0); > + if (irq < 0) > + return irq; You don't seem to use the IRQ. Is this meant to catch invalid DT that don't specify an IRQ, to make sure we'll always have one available when we'll need to later ? > + return 0; > +} [snip] > +static int rcar_csi2_probe(struct platform_device *pdev) > +{ > + const struct soc_device_attribute *attr; > + struct rcar_csi2 *priv; > + unsigned int i; > + int ret; > + > + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->info = of_device_get_match_data(&pdev->dev); > + > + /* r8a7795 ES1.x behaves different then ES2.0+ but no own compat */ > + attr = soc_device_match(r8a7795es1); > + if (attr) > + priv->info = attr->data; > + > + priv->dev = &pdev->dev; > + > + mutex_init(&priv->lock); > + priv->stream_count = 0; > + > + ret = rcar_csi2_probe_resources(priv, pdev); > + if (ret) { > + dev_err(priv->dev, "Failed to get resources\n"); > + return ret; > + } > + > + platform_set_drvdata(pdev, priv); > + > + ret = rcar_csi2_parse_dt(priv); > + if (ret) > + return ret; > + > + priv->subdev.owner = THIS_MODULE; > + priv->subdev.dev = &pdev->dev; > + v4l2_subdev_init(&priv->subdev, &rcar_csi2_subdev_ops); > + v4l2_set_subdevdata(&priv->subdev, &pdev->dev); > + snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s %s", > + KBUILD_MODNAME, dev_name(&pdev->dev)); > + priv->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; > + > + priv->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; > + priv->subdev.entity.ops = &rcar_csi2_entity_ops; > + > + priv->pads[RCAR_CSI2_SINK].flags = MEDIA_PAD_FL_SINK; > + for (i = RCAR_CSI2_SOURCE_VC0; i < NR_OF_RCAR_CSI2_PAD; i++) > + priv->pads[i].flags = MEDIA_PAD_FL_SOURCE; > + > + ret = media_entity_pads_init(&priv->subdev.entity, NR_OF_RCAR_CSI2_PAD, > + priv->pads); > + if (ret) > + goto error; > + > + pm_runtime_enable(&pdev->dev); > + > + ret = v4l2_async_register_subdev(&priv->subdev); > + if (ret < 0) > + goto error; > + > + dev_info(priv->dev, "%d lanes found\n", priv->lanes); priv->lanes is unsigned you should use %u. > + > + return 0; > + > +error: > + v4l2_async_notifier_unregister(&priv->notifier); > + v4l2_async_notifier_cleanup(&priv->notifier); > + > + return ret; > +} [snip] With these small issues fixed and Kieran's and Maxime's comments addressed as you see fit, Reviewed-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx> -- Regards, Laurent Pinchart