On 3/18/19 8:16 PM, Sakari Ailus wrote: > Rework OF endpoint parsing for the omap3isp driver. This does add some > lines of code. The benefits are still clear: > > - the great complication related to callbacks in endpoint parsing is gone; > instead endpoints are obtained port by port and > > - endpoints may now have a default bus configuration which was not > possible while using callbacks. This driver does not benefit from that > feature, but as the omap3isp is one of the exemplary drivers, this works > as an example for driver developers. > > Depends-on: ("device property: Add fwnode_graph_get_endpoint_by_id") > Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx> > --- > drivers/media/platform/omap3isp/isp.c | 334 ++++++++++++++++++++-------------- > 1 file changed, 200 insertions(+), 134 deletions(-) > > diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c > index bd57174d81a7..ac23948f6ade 100644 > --- a/drivers/media/platform/omap3isp/isp.c > +++ b/drivers/media/platform/omap3isp/isp.c > @@ -2015,136 +2015,6 @@ enum isp_of_phy { > ISP_OF_PHY_CSIPHY2, > }; > > -static int isp_fwnode_parse(struct device *dev, > - struct v4l2_fwnode_endpoint *vep, > - struct v4l2_async_subdev *asd) > -{ > - struct isp_async_subdev *isd = > - container_of(asd, struct isp_async_subdev, asd); > - struct isp_bus_cfg *buscfg = &isd->bus; > - bool csi1 = false; > - unsigned int i; > - > - dev_dbg(dev, "parsing endpoint %pOF, interface %u\n", > - to_of_node(vep->base.local_fwnode), vep->base.port); > - > - switch (vep->base.port) { > - case ISP_OF_PHY_PARALLEL: > - buscfg->interface = ISP_INTERFACE_PARALLEL; > - buscfg->bus.parallel.data_lane_shift = > - vep->bus.parallel.data_shift; > - buscfg->bus.parallel.clk_pol = > - !!(vep->bus.parallel.flags > - & V4L2_MBUS_PCLK_SAMPLE_FALLING); > - buscfg->bus.parallel.hs_pol = > - !!(vep->bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW); > - buscfg->bus.parallel.vs_pol = > - !!(vep->bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW); > - buscfg->bus.parallel.fld_pol = > - !!(vep->bus.parallel.flags & V4L2_MBUS_FIELD_EVEN_LOW); > - buscfg->bus.parallel.data_pol = > - !!(vep->bus.parallel.flags & V4L2_MBUS_DATA_ACTIVE_LOW); > - buscfg->bus.parallel.bt656 = vep->bus_type == V4L2_MBUS_BT656; > - break; > - > - case ISP_OF_PHY_CSIPHY1: > - case ISP_OF_PHY_CSIPHY2: > - switch (vep->bus_type) { > - case V4L2_MBUS_CCP2: > - case V4L2_MBUS_CSI1: > - dev_dbg(dev, "CSI-1/CCP-2 configuration\n"); > - csi1 = true; > - break; > - case V4L2_MBUS_CSI2_DPHY: > - dev_dbg(dev, "CSI-2 configuration\n"); > - csi1 = false; > - break; > - default: > - dev_err(dev, "unsupported bus type %u\n", > - vep->bus_type); > - return -EINVAL; > - } > - > - switch (vep->base.port) { > - case ISP_OF_PHY_CSIPHY1: > - if (csi1) > - buscfg->interface = ISP_INTERFACE_CCP2B_PHY1; > - else > - buscfg->interface = ISP_INTERFACE_CSI2C_PHY1; > - break; > - case ISP_OF_PHY_CSIPHY2: > - if (csi1) > - buscfg->interface = ISP_INTERFACE_CCP2B_PHY2; > - else > - buscfg->interface = ISP_INTERFACE_CSI2A_PHY2; > - break; > - } > - if (csi1) { > - buscfg->bus.ccp2.lanecfg.clk.pos = > - vep->bus.mipi_csi1.clock_lane; > - buscfg->bus.ccp2.lanecfg.clk.pol = > - vep->bus.mipi_csi1.lane_polarity[0]; > - dev_dbg(dev, "clock lane polarity %u, pos %u\n", > - buscfg->bus.ccp2.lanecfg.clk.pol, > - buscfg->bus.ccp2.lanecfg.clk.pos); > - > - buscfg->bus.ccp2.lanecfg.data[0].pos = > - vep->bus.mipi_csi1.data_lane; > - buscfg->bus.ccp2.lanecfg.data[0].pol = > - vep->bus.mipi_csi1.lane_polarity[1]; > - > - dev_dbg(dev, "data lane polarity %u, pos %u\n", > - buscfg->bus.ccp2.lanecfg.data[0].pol, > - buscfg->bus.ccp2.lanecfg.data[0].pos); > - > - buscfg->bus.ccp2.strobe_clk_pol = > - vep->bus.mipi_csi1.clock_inv; > - buscfg->bus.ccp2.phy_layer = vep->bus.mipi_csi1.strobe; > - buscfg->bus.ccp2.ccp2_mode = > - vep->bus_type == V4L2_MBUS_CCP2; > - buscfg->bus.ccp2.vp_clk_pol = 1; > - > - buscfg->bus.ccp2.crc = 1; > - } else { > - buscfg->bus.csi2.lanecfg.clk.pos = > - vep->bus.mipi_csi2.clock_lane; > - buscfg->bus.csi2.lanecfg.clk.pol = > - vep->bus.mipi_csi2.lane_polarities[0]; > - dev_dbg(dev, "clock lane polarity %u, pos %u\n", > - buscfg->bus.csi2.lanecfg.clk.pol, > - buscfg->bus.csi2.lanecfg.clk.pos); > - > - buscfg->bus.csi2.num_data_lanes = > - vep->bus.mipi_csi2.num_data_lanes; > - > - for (i = 0; i < buscfg->bus.csi2.num_data_lanes; i++) { > - buscfg->bus.csi2.lanecfg.data[i].pos = > - vep->bus.mipi_csi2.data_lanes[i]; > - buscfg->bus.csi2.lanecfg.data[i].pol = > - vep->bus.mipi_csi2.lane_polarities[i + 1]; > - dev_dbg(dev, > - "data lane %u polarity %u, pos %u\n", i, > - buscfg->bus.csi2.lanecfg.data[i].pol, > - buscfg->bus.csi2.lanecfg.data[i].pos); > - } > - /* > - * FIXME: now we assume the CRC is always there. > - * Implement a way to obtain this information from the > - * sensor. Frame descriptors, perhaps? > - */ > - buscfg->bus.csi2.crc = 1; > - } > - break; > - > - default: > - dev_warn(dev, "%pOF: invalid interface %u\n", > - to_of_node(vep->base.local_fwnode), vep->base.port); > - return -EINVAL; > - } > - > - return 0; > -} > - > static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async) > { > struct isp_device *isp = container_of(async, struct isp_device, > @@ -2174,6 +2044,204 @@ static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async) > return media_device_register(&isp->media_dev); > } > > +static void isp_parse_of_parallel_endpoint(struct device *dev, > + struct v4l2_fwnode_endpoint *vep, > + struct isp_bus_cfg *buscfg) > +{ > + buscfg->interface = ISP_INTERFACE_PARALLEL; > + buscfg->bus.parallel.data_lane_shift = vep->bus.parallel.data_shift; > + buscfg->bus.parallel.clk_pol = > + !!(vep->bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING); > + buscfg->bus.parallel.hs_pol = > + !!(vep->bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW); > + buscfg->bus.parallel.vs_pol = > + !!(vep->bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW); > + buscfg->bus.parallel.fld_pol = > + !!(vep->bus.parallel.flags & V4L2_MBUS_FIELD_EVEN_LOW); > + buscfg->bus.parallel.data_pol = > + !!(vep->bus.parallel.flags & V4L2_MBUS_DATA_ACTIVE_LOW); > + buscfg->bus.parallel.bt656 = vep->bus_type == V4L2_MBUS_BT656; > +} > + > +static void isp_parse_of_csi2_endpoint(struct device *dev, > + struct v4l2_fwnode_endpoint *vep, > + struct isp_bus_cfg *buscfg) > +{ > + unsigned int i; > + > + buscfg->bus.csi2.lanecfg.clk.pos = vep->bus.mipi_csi2.clock_lane; > + buscfg->bus.csi2.lanecfg.clk.pol = > + vep->bus.mipi_csi2.lane_polarities[0]; > + dev_dbg(dev, "clock lane polarity %u, pos %u\n", > + buscfg->bus.csi2.lanecfg.clk.pol, > + buscfg->bus.csi2.lanecfg.clk.pos); > + > + buscfg->bus.csi2.num_data_lanes = vep->bus.mipi_csi2.num_data_lanes; > + > + for (i = 0; i < buscfg->bus.csi2.num_data_lanes; i++) { > + buscfg->bus.csi2.lanecfg.data[i].pos = > + vep->bus.mipi_csi2.data_lanes[i]; > + buscfg->bus.csi2.lanecfg.data[i].pol = > + vep->bus.mipi_csi2.lane_polarities[i + 1]; > + dev_dbg(dev, > + "data lane %u polarity %u, pos %u\n", i, > + buscfg->bus.csi2.lanecfg.data[i].pol, > + buscfg->bus.csi2.lanecfg.data[i].pos); > + } > + /* > + * FIXME: now we assume the CRC is always there. Implement a way to > + * obtain this information from the sensor. Frame descriptors, perhaps? > + */ > + buscfg->bus.csi2.crc = 1; > +} > + > +static void isp_parse_of_csi1_endpoint(struct device *dev, > + struct v4l2_fwnode_endpoint *vep, > + struct isp_bus_cfg *buscfg) > +{ > + buscfg->bus.ccp2.lanecfg.clk.pos = vep->bus.mipi_csi1.clock_lane; > + buscfg->bus.ccp2.lanecfg.clk.pol = vep->bus.mipi_csi1.lane_polarity[0]; > + dev_dbg(dev, "clock lane polarity %u, pos %u\n", > + buscfg->bus.ccp2.lanecfg.clk.pol, > + buscfg->bus.ccp2.lanecfg.clk.pos); > + > + buscfg->bus.ccp2.lanecfg.data[0].pos = vep->bus.mipi_csi1.data_lane; > + buscfg->bus.ccp2.lanecfg.data[0].pol = > + vep->bus.mipi_csi1.lane_polarity[1]; > + > + dev_dbg(dev, "data lane polarity %u, pos %u\n", > + buscfg->bus.ccp2.lanecfg.data[0].pol, > + buscfg->bus.ccp2.lanecfg.data[0].pos); > + > + buscfg->bus.ccp2.strobe_clk_pol = vep->bus.mipi_csi1.clock_inv; > + buscfg->bus.ccp2.phy_layer = vep->bus.mipi_csi1.strobe; > + buscfg->bus.ccp2.ccp2_mode = vep->bus_type == V4L2_MBUS_CCP2; > + buscfg->bus.ccp2.vp_clk_pol = 1; > + > + buscfg->bus.ccp2.crc = 1; > +} > + > +static int isp_alloc_isd(struct isp_async_subdev **isd, > + struct isp_bus_cfg **buscfg) > +{ > + struct isp_async_subdev *__isd; > + > + __isd = kzalloc(sizeof(*isd), GFP_KERNEL); > + if (!__isd) > + return -ENOMEM; > + > + *isd = __isd; > + *buscfg = &__isd->bus; > + > + return 0; > +} > + > +static struct { > + u32 phy; > + u32 csi2_if; > + u32 csi1_if; > +} isp_bus_interfaces[2] = { > + { ISP_OF_PHY_CSIPHY1, > + ISP_INTERFACE_CSI2C_PHY1, ISP_INTERFACE_CCP2B_PHY1 }, > + { ISP_OF_PHY_CSIPHY2, > + ISP_INTERFACE_CSI2A_PHY2, ISP_INTERFACE_CCP2B_PHY2 }, > +}; > + > +static int isp_parse_of_endpoints(struct isp_device *isp) > +{ > + struct fwnode_handle *ep; > + struct isp_async_subdev *isd; > + struct isp_bus_cfg *buscfg; > + unsigned int i; > + > + ep = fwnode_graph_get_endpoint_by_id( > + dev_fwnode(isp->dev), ISP_OF_PHY_PARALLEL, 0, > + FWNODE_GRAPH_ENDPOINT_NEXT); > + > + if (ep) { > + struct v4l2_fwnode_endpoint vep = { > + .bus_type = V4L2_MBUS_PARALLEL > + }; > + int ret; > + > + dev_dbg(isp->dev, "parsing parallel interface\n"); > + > + ret = v4l2_fwnode_endpoint_parse(ep, &vep); > + if (!ret) > + ret = isp_alloc_isd(&isd, &buscfg); > + > + if (!ret) { > + isp_parse_of_parallel_endpoint(isp->dev, &vep, buscfg); > + ret = v4l2_async_notifier_add_fwnode_remote_subdev( > + &isp->notifier, ep, &isd->asd); > + } > + > + if (ret) { > + kfree(isd); > + fwnode_handle_put(ep); > + } If ret == 0, then who calls 'fwnode_handle_put(ep);'? Am I missing something? Regards, Hans > + } > + > + for (i = 0; i < ARRAY_SIZE(isp_bus_interfaces); i++) { > + ep = fwnode_graph_get_endpoint_by_id( > + dev_fwnode(isp->dev), isp_bus_interfaces[i].phy, 0, > + FWNODE_GRAPH_ENDPOINT_NEXT); > + > + if (ep) { > + struct v4l2_fwnode_endpoint vep = { > + .bus_type = V4L2_MBUS_CSI2_DPHY > + }; > + int ret; > + > + dev_dbg(isp->dev, > + "parsing serial interface %u, node %pOF\n", i, > + to_of_node(ep)); > + > + ret = isp_alloc_isd(&isd, &buscfg); > + if (ret) > + return ret; > + > + ret = v4l2_fwnode_endpoint_parse(ep, &vep); > + if (!ret) { > + buscfg->interface = > + isp_bus_interfaces[i].csi2_if; > + isp_parse_of_csi2_endpoint(isp->dev, &vep, > + buscfg); > + } else if (ret == -ENXIO) { > + vep = (struct v4l2_fwnode_endpoint) > + { .bus_type = V4L2_MBUS_CSI1 }; > + ret = v4l2_fwnode_endpoint_parse(ep, &vep); > + > + if (ret == -ENXIO) { > + vep = (struct v4l2_fwnode_endpoint) > + { .bus_type = V4L2_MBUS_CCP2 }; > + ret = v4l2_fwnode_endpoint_parse(ep, > + &vep); > + } > + if (!ret) { > + buscfg->interface = > + isp_bus_interfaces[i].csi1_if; > + isp_parse_of_csi1_endpoint(isp->dev, > + &vep, > + buscfg); > + } > + } > + > + if (!ret) > + ret = v4l2_async_notifier_add_fwnode_remote_subdev( > + &isp->notifier, ep, &isd->asd); > + > + if (ret) { > + kfree(isd); > + fwnode_handle_put(ep); > + return ret; > + } > + } > + } > + > + return 0; > +} > + > static const struct v4l2_async_notifier_operations isp_subdev_notifier_ops = { > .complete = isp_subdev_notifier_complete, > }; > @@ -2222,14 +2290,12 @@ static int isp_probe(struct platform_device *pdev) > mutex_init(&isp->isp_mutex); > spin_lock_init(&isp->stat_lock); > v4l2_async_notifier_init(&isp->notifier); > + isp->dev = &pdev->dev; > > - ret = v4l2_async_notifier_parse_fwnode_endpoints( > - &pdev->dev, &isp->notifier, sizeof(struct isp_async_subdev), > - isp_fwnode_parse); > + ret = isp_parse_of_endpoints(isp); > if (ret < 0) > goto error; > > - isp->dev = &pdev->dev; > isp->ref_count = 0; > > ret = dma_coerce_mask_and_coherent(isp->dev, DMA_BIT_MASK(32)); >