On 22/01/2020 12:55, Yuti Amonkar wrote: > This patch adds new DRM driver for Cadence MHDP DPTX IP used on J721e SoC. > MHDP DPTX IP is the component that complies with VESA DisplayPort (DP) and > embedded Display Port (eDP) standards.It integrates uCPU running the > embedded Firmware(FW) interfaced over APB interface. > Basically, it takes a DPI stream as input and output it encoded in DP > format. Currently, it supports only SST mode. > > Signed-off-by: Yuti Amonkar <yamonkar@xxxxxxxxxxx> > --- > drivers/gpu/drm/bridge/Kconfig | 11 + > drivers/gpu/drm/bridge/Makefile | 3 + > drivers/gpu/drm/bridge/cdns-mhdp.c | 2202 ++++++++++++++++++++++++++++++++++++ > drivers/gpu/drm/bridge/cdns-mhdp.h | 380 +++++++ > 4 files changed, 2596 insertions(+) > create mode 100644 drivers/gpu/drm/bridge/cdns-mhdp.c > create mode 100644 drivers/gpu/drm/bridge/cdns-mhdp.h > ... > diff --git a/drivers/gpu/drm/bridge/cdns-mhdp.c b/drivers/gpu/drm/bridge/cdns-mhdp.c > new file mode 100644 > index 0000000..0bc7fba > --- /dev/null > +++ b/drivers/gpu/drm/bridge/cdns-mhdp.c > @@ -0,0 +1,2202 @@ > +// SPDX-License-Identifier: GPL-2.0 ... > + > +static int mhdp_probe(struct platform_device *pdev) > +{ > + const struct of_device_id *match; > + struct resource *regs; > + struct cdns_mhdp_device *mhdp; > + struct clk *clk; > + int ret; > + unsigned long rate; > + int irq; > + u32 lanes_prop; > + unsigned int link_rate; > + > + mhdp = devm_kzalloc(&pdev->dev, sizeof(struct cdns_mhdp_device), > + GFP_KERNEL); > + if (!mhdp) > + return -ENOMEM; > + > + clk = devm_clk_get(&pdev->dev, NULL); > + if (IS_ERR(clk)) { > + dev_err(&pdev->dev, "couldn't get clk: %ld\n", PTR_ERR(clk)); > + return PTR_ERR(clk); > + } > + > + mhdp->clk = clk; > + mhdp->dev = &pdev->dev; > + mhdp->conn_bus_flags_defaults = DRM_BUS_FLAG_DE_HIGH; > + mutex_init(&mhdp->mbox_mutex); > + spin_lock_init(&mhdp->start_lock); > + dev_set_drvdata(&pdev->dev, mhdp); > + > + drm_dp_aux_init(&mhdp->aux); > + mhdp->aux.dev = &pdev->dev; > + mhdp->aux.transfer = mhdp_transfer; > + > + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + mhdp->regs = devm_ioremap_resource(&pdev->dev, regs); > + if (IS_ERR(mhdp->regs)) > + return PTR_ERR(mhdp->regs); > + > + mhdp->phy = devm_phy_get(&pdev->dev, "dpphy"); > + if (IS_ERR(mhdp->phy)) { > + dev_err(&pdev->dev, "no PHY configured\n"); > + return PTR_ERR(mhdp->phy); > + } > + > + platform_set_drvdata(pdev, mhdp); > + > + clk_prepare_enable(clk); > + > + match = of_match_device(mhdp_ids, &pdev->dev); > + if (!match) > + return -ENODEV; > + mhdp->ops = (struct mhdp_platform_ops *)match->data; > + > + pm_runtime_enable(&pdev->dev); > + ret = pm_runtime_get_sync(&pdev->dev); > + if (ret < 0) { > + dev_err(&pdev->dev, "pm_runtime_get_sync failed\n"); > + pm_runtime_disable(&pdev->dev); > + goto clk_disable; > + } > + > + if (mhdp->ops && mhdp->ops->init) { > + ret = mhdp->ops->init(mhdp); > + if (ret != 0) { > + dev_err(&pdev->dev, "MHDP platform initialization failed: %d\n", > + ret); > + goto runtime_put; > + } > + } > + > + rate = clk_get_rate(clk); > + writel(rate % 1000000, mhdp->regs + CDNS_SW_CLK_L); > + writel(rate / 1000000, mhdp->regs + CDNS_SW_CLK_H); > + > + dev_dbg(&pdev->dev, "func clk rate %lu Hz\n", rate); > + > + writel(~0, mhdp->regs + CDNS_MB_INT_MASK); > + writel(~0, mhdp->regs + CDNS_APB_INT_MASK); > + > + irq = platform_get_irq(pdev, 0); > + ret = devm_request_threaded_irq(mhdp->dev, irq, NULL, mhdp_irq_handler, > + IRQF_ONESHOT, "mhdp8546", mhdp); > + if (ret) { > + dev_err(&pdev->dev, "cannot install IRQ %d\n", irq); > + ret = -EIO; > + goto plat_fini; > + } > + > + /* Read source capabilities, based on PHY's device tree properties. */ > + ret = device_property_read_u32(&mhdp->phy->dev, "cdns,num-lanes", > + &(lanes_prop)); > + if (ret) > + mhdp->host.lanes_cnt = CDNS_LANE_4; > + else > + mhdp->host.lanes_cnt = lanes_prop; > + > + ret = device_property_read_u32(&mhdp->phy->dev, "cdns,max-bit-rate", > + &(link_rate)); > + if (ret) > + link_rate = drm_dp_bw_code_to_link_rate(DP_LINK_BW_8_1); > + else > + /* PHY uses Mb/s, DRM uses tens of kb/s. */ > + link_rate *= 100; > + > + mhdp->host.link_rate = link_rate; > + mhdp->host.volt_swing = CDNS_VOLT_SWING(3); > + mhdp->host.pre_emphasis = CDNS_PRE_EMPHASIS(3); > + mhdp->host.pattern_supp = CDNS_SUPPORT_TPS(1) | > + CDNS_SUPPORT_TPS(2) | CDNS_SUPPORT_TPS(3) | > + CDNS_SUPPORT_TPS(4); > + mhdp->host.lane_mapping = CDNS_LANE_MAPPING_NORMAL; > + mhdp->host.fast_link = false; > + mhdp->host.enhanced = true; > + mhdp->host.scrambler = true; > + mhdp->host.ssc = false; > + > + /* The only currently supported format */ > + mhdp->display_fmt.y_only = false; > + mhdp->display_fmt.color_format = DRM_COLOR_FORMAT_RGB444; > + mhdp->display_fmt.bpc = 8; > + > + mhdp->bridge.of_node = pdev->dev.of_node; > + mhdp->bridge.funcs = &cdns_mhdp_bridge_funcs; > + > + ret = phy_init(mhdp->phy); > + if (ret) { > + dev_err(mhdp->dev, "Failed to initialize PHY: %d\n", ret); > + goto runtime_put; > + } > + > + drm_bridge_add(&mhdp->bridge); There is a bug here. If the load_firmeare() bellow ever fails, the probe fails, but leaves the bridge added. request_firmware_nowait() hardly ever fails, but still this should be fixed by moving the drm_bridge_add() couple of lines down just before "return 0;". > + > + ret = load_firmware(mhdp); > + if (ret) > + goto phy_exit; > + > + return 0; > + > +phy_exit: > + phy_exit(mhdp->phy); > +plat_fini: > + if (mhdp->ops && mhdp->ops->exit) > + mhdp->ops->exit(mhdp); > +runtime_put: > + pm_runtime_put_sync(&pdev->dev); > + pm_runtime_disable(&pdev->dev); > +clk_disable: > + clk_disable_unprepare(mhdp->clk); > + > + return ret; > +} > + > +MODULE_FIRMWARE(FW_NAME); > + > +static int mhdp_remove(struct platform_device *pdev) > +{ > + struct cdns_mhdp_device *mhdp = dev_get_drvdata(&pdev->dev); > + unsigned int timeout = 10; > + bool stop_fw = false; > + int ret = 0; > + > + if (mhdp->ops && mhdp->ops->exit) > + mhdp->ops->exit(mhdp); > + > + drm_bridge_remove(&mhdp->bridge); > + > +wait_loading: > + spin_lock(&mhdp->start_lock); > + if (mhdp->hw_state == MHDP_HW_LOADING && timeout-- > 0) { > + spin_unlock(&mhdp->start_lock); > + msleep(100); > + goto wait_loading; > + } else if (mhdp->hw_state == MHDP_HW_READY) { > + stop_fw = true; > + timeout = 1; /* We were successful even if counter reached 0 */ > + } > + mhdp->hw_state = MHDP_HW_STOPPED; > + spin_unlock(&mhdp->start_lock); > + > + if (timeout == 0) > + dev_err(mhdp->dev, "%s: Timeout waiting for fw loading\n", > + __func__); > + > + if (stop_fw) { > + ret = cdns_mhdp_set_firmware_active(mhdp, false); > + if (ret) > + dev_err(mhdp->dev, "%s: De-activate FW failed: %d\n", > + __func__, ret); > + } > + > + phy_exit(mhdp->phy); > + > + pm_runtime_put_sync(&pdev->dev); > + pm_runtime_disable(&pdev->dev); > + > + clk_disable_unprepare(mhdp->clk); > + > + /* FIXME: check for missing functions */ > + > + return ret; > +} > + > +static struct platform_driver mhdp_driver = { > + .driver = { > + .name = "cdns-mhdp", > + .of_match_table = of_match_ptr(mhdp_ids), > + }, > + .probe = mhdp_probe, > + .remove = mhdp_remove, > +}; > +module_platform_driver(mhdp_driver); > + > +MODULE_AUTHOR("Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxx>"); > +MODULE_AUTHOR("Przemyslaw Gaj <pgaj@xxxxxxxxxxx>"); > +MODULE_AUTHOR("Damian Kos <dkos@xxxxxxxxxxx>"); > +MODULE_AUTHOR("Piotr Sroka <piotrs@xxxxxxxxxxx>"); > +MODULE_AUTHOR("Swapnil Jakhade <sjakhade@xxxxxxxxxxx>"); > +MODULE_DESCRIPTION("Cadence MHDP DP bridge driver"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:cdns-mhdp"); ... -- Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki