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 ? Can't I simply check the value of v4l2_epn.bus_type , as I am doing it already ? > > 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"); >>> >>