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@cadence.com
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@free-electrons.com"); +MODULE_AUTHOR("Przemyslaw Gaj pgaj@cadence.com"); +MODULE_AUTHOR("Damian Kos dkos@cadence.com"); +MODULE_AUTHOR("Piotr Sroka piotrs@cadence.com"); +MODULE_AUTHOR("Swapnil Jakhade sjakhade@cadence.com"); +MODULE_DESCRIPTION("Cadence MHDP DP bridge driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cdns-mhdp");
...