Hi Eugene On Mon, Apr 12, 2021 at 02:15:32PM +0000, Eugen.Hristev@xxxxxxxxxxxxx wrote: > On 4/12/21 4:41 PM, Jacopo Mondi wrote: > > Hi Eugene, > > > > On Mon, Apr 12, 2021 at 12:37:41PM +0000, Eugen.Hristev@xxxxxxxxxxxxx wrote: > >>> +static int xisc_parse_dt(struct device *dev, struct isc_device *isc) > >>> +{ > >>> + struct device_node *np = dev->of_node; > >>> + struct device_node *epn = NULL; > >>> + struct isc_subdev_entity *subdev_entity; > >>> + unsigned int flags; > >>> + int ret; > >>> + bool mipi_mode; > >>> + > >>> + INIT_LIST_HEAD(&isc->subdev_entities); > >>> + > >>> + mipi_mode = of_property_read_bool(np, "microchip,mipi-mode"); > >>> + > >>> + while (1) { > >>> + struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; > >>> + > >>> + epn = of_graph_get_next_endpoint(np, epn); > >>> + if (!epn) > >>> + return 0; > >>> + > >>> + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), > >>> + &v4l2_epn); > >>> + if (ret) { > >>> + ret = -EINVAL; > >>> + dev_err(dev, "Could not parse the endpoint\n"); > >>> + break; > >>> + } > >>> + > >>> + subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), > >>> + GFP_KERNEL); > >>> + if (!subdev_entity) { > >>> + ret = -ENOMEM; > >>> + break; > >>> + } > >>> + subdev_entity->epn = epn; > >>> + > >>> + flags = v4l2_epn.bus.parallel.flags; > >>> + > >>> + if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) > >>> + subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW; > >>> + > >>> + if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) > >>> + subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW; > >>> + > >>> + if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) > >>> + subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW; > >>> + > >>> + if (v4l2_epn.bus_type == V4L2_MBUS_BT656) > >>> + subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC | > >>> + ISC_PFE_CFG0_CCIR656; > >> > >> Hi Jacopo, > >> > >> If I use the bus-type property for the 'port' , do I actually have to > >> change something here ? > > > > You can set bus_type to the desired type, if it doesn't match the > > 'bus-type' property you will have an immediate error and a more strict > > check on the properties. > > > > You would likely: > > > > v4l2_epn.bus_type = V4L2_MBUS_PARALLEL; > > ret = v4l2_fwnode_endpoint_parse() > > if (!ret) { > > /* It's parallel */ > > } else { > > v4l2_epn.bus_type = V4L2_MBUS_BT656; > > ret = v4l2_fwnode_endpoint_parse() > > if (ret) { > > /* Unsupported bus type: error out. */ > > } > > > > /* It's BT656 */ > > } > > if the v4l2_fwnode_endpoint_parse will already fill in the > v4l2_epn.bus_type based on what is found in the 'bus-type' , why do I > need to do this assumption-fail-assumption-fail scenario ? Because you would know exactly what bus type you're parsing, as v4l2_fwnode_endpoint_parse() would make sure the type you intend to parse matches what the endpoint reports if (vep->bus_type != V4L2_MBUS_UNKNOWN) { if (mbus_type != V4L2_MBUS_UNKNOWN && vep->bus_type != mbus_type) { pr_debug("expecting bus type %s\n", v4l2_fwnode_mbus_type_to_string(vep->bus_type)); return -ENXIO; } I guess this is mostly useful if you want to specify default values the v4l2_fwnode_endpoint. If you go with autoguessing, a parallel endpoint without polarities flag specified can be identified as BT656 if I'm not mistaken. > Can't I simply check the value of v4l2_epn.bus_type , as I am doing it > already ? Only if you make bus-type required in DTS, then you can specify here bus_type = 0 like you're doing and rely on v4l2_fwnode_endpoint_parse() to set it to what it has found in DTS. Otherwise you rely on auto-guessing, if I got this right. Thanks j > > > > > Not the greatest API, but it's more robust. > > > >> the v4l2_epn.bus_type won't be set automatically ? by the endpoint > >> parser I mean. > > > > Yes, that's what auto-discovery is, the endpoint parser tries to > > deduce the bus type from the properties that are there specified. It > > works, but leaves quite some ambiguity between ie PARALLEL and BT656 > > as some polarities might not be necessarily specified even for > > PARALLEL bus types. > > > > As I've said, there's still plenty of code that relies on > > auto-discovery so I don't think this is blocking, also because making > > bus-type mandatory on existing DTS is quite painful. Since this is a > > new DTS you can consider this solution if you want to. > > > > Thanks > > j > > > >> > >> Thanks, > >> Eugen > >> > >>> + > >>> + if (mipi_mode) > >>> + subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI; > >>> + > >>> + list_add_tail(&subdev_entity->list, &isc->subdev_entities); > >>> + } > >>> + of_node_put(epn); > >>> + > >>> + return ret; > >>> +} > >>> + > >>> +static int microchip_xisc_probe(struct platform_device *pdev) > >>> +{ > >>> + struct device *dev = &pdev->dev; > >>> + struct isc_device *isc; > >>> + struct resource *res; > >>> + void __iomem *io_base; > >>> + struct isc_subdev_entity *subdev_entity; > >>> + int irq; > >>> + int ret; > >>> + u32 ver; > >>> + > >>> + isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL); > >>> + if (!isc) > >>> + return -ENOMEM; > >>> + > >>> + platform_set_drvdata(pdev, isc); > >>> + isc->dev = dev; > >>> + > >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > >>> + io_base = devm_ioremap_resource(dev, res); > >>> + if (IS_ERR(io_base)) > >>> + return PTR_ERR(io_base); > >>> + > >>> + isc->regmap = devm_regmap_init_mmio(dev, io_base, &isc_regmap_config); > >>> + if (IS_ERR(isc->regmap)) { > >>> + ret = PTR_ERR(isc->regmap); > >>> + dev_err(dev, "failed to init register map: %d\n", ret); > >>> + return ret; > >>> + } > >>> + > >>> + irq = platform_get_irq(pdev, 0); > >>> + if (irq < 0) > >>> + return irq; > >>> + > >>> + ret = devm_request_irq(dev, irq, isc_interrupt, 0, > >>> + "microchip-sama7g5-xisc", isc); > >>> + if (ret < 0) { > >>> + dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", > >>> + irq, ret); > >>> + return ret; > >>> + } > >>> + > >>> + isc->gamma_table = isc_sama7g5_gamma_table; > >>> + isc->gamma_max = 0; > >>> + > >>> + isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH; > >>> + isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT; > >>> + > >>> + isc->config_dpc = isc_sama7g5_config_dpc; > >>> + isc->config_csc = isc_sama7g5_config_csc; > >>> + isc->config_cbc = isc_sama7g5_config_cbc; > >>> + isc->config_cc = isc_sama7g5_config_cc; > >>> + isc->config_gam = isc_sama7g5_config_gam; > >>> + isc->config_rlp = isc_sama7g5_config_rlp; > >>> + isc->config_ctrls = isc_sama7g5_config_ctrls; > >>> + > >>> + isc->adapt_pipeline = isc_sama7g5_adapt_pipeline; > >>> + > >>> + isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET; > >>> + isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET; > >>> + isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET; > >>> + isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET; > >>> + isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET; > >>> + isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET; > >>> + isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET; > >>> + isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET; > >>> + isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET; > >>> + > >>> + isc->controller_formats = sama7g5_controller_formats; > >>> + isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats); > >>> + isc->formats_list = sama7g5_formats_list; > >>> + isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list); > >>> + > >>> + /* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */ > >>> + isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32; > >>> + > >>> + ret = isc_pipeline_init(isc); > >>> + if (ret) > >>> + return ret; > >>> + > >>> + isc->hclock = devm_clk_get(dev, "hclock"); > >>> + if (IS_ERR(isc->hclock)) { > >>> + ret = PTR_ERR(isc->hclock); > >>> + dev_err(dev, "failed to get hclock: %d\n", ret); > >>> + return ret; > >>> + } > >>> + > >>> + ret = clk_prepare_enable(isc->hclock); > >>> + if (ret) { > >>> + dev_err(dev, "failed to enable hclock: %d\n", ret); > >>> + return ret; > >>> + } > >>> + > >>> + ret = isc_clk_init(isc); > >>> + if (ret) { > >>> + dev_err(dev, "failed to init isc clock: %d\n", ret); > >>> + goto unprepare_hclk; > >>> + } > >>> + > >>> + isc->ispck = isc->isc_clks[ISC_ISPCK].clk; > >>> + > >>> + ret = clk_prepare_enable(isc->ispck); > >>> + if (ret) { > >>> + dev_err(dev, "failed to enable ispck: %d\n", ret); > >>> + goto unprepare_hclk; > >>> + } > >>> + > >>> + /* ispck should be greater or equal to hclock */ > >>> + ret = clk_set_rate(isc->ispck, clk_get_rate(isc->hclock)); > >>> + if (ret) { > >>> + dev_err(dev, "failed to set ispck rate: %d\n", ret); > >>> + goto unprepare_clk; > >>> + } > >>> + > >>> + ret = v4l2_device_register(dev, &isc->v4l2_dev); > >>> + if (ret) { > >>> + dev_err(dev, "unable to register v4l2 device.\n"); > >>> + goto unprepare_clk; > >>> + } > >>> + > >>> + ret = xisc_parse_dt(dev, isc); > >>> + if (ret) { > >>> + dev_err(dev, "fail to parse device tree\n"); > >>> + goto unregister_v4l2_device; > >>> + } > >>> + > >>> + if (list_empty(&isc->subdev_entities)) { > >>> + dev_err(dev, "no subdev found\n"); > >>> + ret = -ENODEV; > >>> + goto unregister_v4l2_device; > >>> + } > >>> + > >>> + list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { > >>> + struct v4l2_async_subdev *asd; > >>> + > >>> + v4l2_async_notifier_init(&subdev_entity->notifier); > >>> + > >>> + asd = v4l2_async_notifier_add_fwnode_remote_subdev( > >>> + &subdev_entity->notifier, > >>> + of_fwnode_handle(subdev_entity->epn), > >>> + struct v4l2_async_subdev); > >>> + > >>> + of_node_put(subdev_entity->epn); > >>> + subdev_entity->epn = NULL; > >>> + > >>> + if (IS_ERR(asd)) { > >>> + ret = PTR_ERR(asd); > >>> + goto cleanup_subdev; > >>> + } > >>> + > >>> + subdev_entity->notifier.ops = &isc_async_ops; > >>> + > >>> + ret = v4l2_async_notifier_register(&isc->v4l2_dev, > >>> + &subdev_entity->notifier); > >>> + if (ret) { > >>> + dev_err(dev, "fail to register async notifier\n"); > >>> + goto cleanup_subdev; > >>> + } > >>> + > >>> + if (video_is_registered(&isc->video_dev)) > >>> + break; > >>> + } > >>> + > >>> + pm_runtime_set_active(dev); > >>> + pm_runtime_enable(dev); > >>> + pm_request_idle(dev); > >>> + > >>> + regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver); > >>> + dev_info(dev, "Microchip XISC version %x\n", ver); > >>> + > >>> + return 0; > >>> + > >>> +cleanup_subdev: > >>> + isc_subdev_cleanup(isc); > >>> + > >>> +unregister_v4l2_device: > >>> + v4l2_device_unregister(&isc->v4l2_dev); > >>> + > >>> +unprepare_clk: > >>> + clk_disable_unprepare(isc->ispck); > >>> +unprepare_hclk: > >>> + clk_disable_unprepare(isc->hclock); > >>> + > >>> + isc_clk_cleanup(isc); > >>> + > >>> + return ret; > >>> +} > >>> + > >>> +static int microchip_xisc_remove(struct platform_device *pdev) > >>> +{ > >>> + struct isc_device *isc = platform_get_drvdata(pdev); > >>> + > >>> + pm_runtime_disable(&pdev->dev); > >>> + > >>> + isc_subdev_cleanup(isc); > >>> + > >>> + v4l2_device_unregister(&isc->v4l2_dev); > >>> + > >>> + clk_disable_unprepare(isc->ispck); > >>> + clk_disable_unprepare(isc->hclock); > >>> + > >>> + isc_clk_cleanup(isc); > >>> + > >>> + return 0; > >>> +} > >>> + > >>> +static int __maybe_unused xisc_runtime_suspend(struct device *dev) > >>> +{ > >>> + struct isc_device *isc = dev_get_drvdata(dev); > >>> + > >>> + clk_disable_unprepare(isc->ispck); > >>> + clk_disable_unprepare(isc->hclock); > >>> + > >>> + return 0; > >>> +} > >>> + > >>> +static int __maybe_unused xisc_runtime_resume(struct device *dev) > >>> +{ > >>> + struct isc_device *isc = dev_get_drvdata(dev); > >>> + int ret; > >>> + > >>> + ret = clk_prepare_enable(isc->hclock); > >>> + if (ret) > >>> + return ret; > >>> + > >>> + ret = clk_prepare_enable(isc->ispck); > >>> + if (ret) > >>> + clk_disable_unprepare(isc->hclock); > >>> + > >>> + return ret; > >>> +} > >>> + > >>> +static const struct dev_pm_ops microchip_xisc_dev_pm_ops = { > >>> + SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL) > >>> +}; > >>> + > >>> +static const struct of_device_id microchip_xisc_of_match[] = { > >>> + { .compatible = "microchip,sama7g5-isc" }, > >>> + { } > >>> +}; > >>> +MODULE_DEVICE_TABLE(of, microchip_xisc_of_match); > >>> + > >>> +static struct platform_driver microchip_xisc_driver = { > >>> + .probe = microchip_xisc_probe, > >>> + .remove = microchip_xisc_remove, > >>> + .driver = { > >>> + .name = "microchip-sama7g5-xisc", > >>> + .pm = µchip_xisc_dev_pm_ops, > >>> + .of_match_table = of_match_ptr(microchip_xisc_of_match), > >>> + }, > >>> +}; > >>> + > >>> +module_platform_driver(microchip_xisc_driver); > >>> + > >>> +MODULE_AUTHOR("Eugen Hristev <eugen.hristev@xxxxxxxxxxxxx>"); > >>> +MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC"); > >>> +MODULE_LICENSE("GPL v2"); > >>> +MODULE_SUPPORTED_DEVICE("video"); > >>> > >> >