Re: [PATCH v3 2/3] drm: bridge: Add support for Cadence MHDP DPI/DP bridge

2020-01-29 Thread Jyri Sarha
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 
> ---
>  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 000..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(>dev, sizeof(struct cdns_mhdp_device),
> + GFP_KERNEL);
> + if (!mhdp)
> + return -ENOMEM;
> +
> + clk = devm_clk_get(>dev, NULL);
> + if (IS_ERR(clk)) {
> + dev_err(>dev, "couldn't get clk: %ld\n", PTR_ERR(clk));
> + return PTR_ERR(clk);
> + }
> +
> + mhdp->clk = clk;
> + mhdp->dev = >dev;
> + mhdp->conn_bus_flags_defaults = DRM_BUS_FLAG_DE_HIGH;
> + mutex_init(>mbox_mutex);
> + spin_lock_init(>start_lock);
> + dev_set_drvdata(>dev, mhdp);
> +
> + drm_dp_aux_init(>aux);
> + mhdp->aux.dev = >dev;
> + mhdp->aux.transfer = mhdp_transfer;
> +
> + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + mhdp->regs = devm_ioremap_resource(>dev, regs);
> + if (IS_ERR(mhdp->regs))
> + return PTR_ERR(mhdp->regs);
> +
> + mhdp->phy = devm_phy_get(>dev, "dpphy");
> + if (IS_ERR(mhdp->phy)) {
> + dev_err(>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, >dev);
> + if (!match)
> + return -ENODEV;
> + mhdp->ops = (struct mhdp_platform_ops *)match->data;
> +
> + pm_runtime_enable(>dev);
> + ret = pm_runtime_get_sync(>dev);
> + if (ret < 0) {
> + dev_err(>dev, "pm_runtime_get_sync failed\n");
> + pm_runtime_disable(>dev);
> + goto clk_disable;
> + }
> +
> + if (mhdp->ops && mhdp->ops->init) {
> + ret = mhdp->ops->init(mhdp);
> + if (ret != 0) {
> + dev_err(>dev, "MHDP platform initialization 
> failed: %d\n",
> + ret);
> + goto runtime_put;
> + }
> + }
> +
> + rate = clk_get_rate(clk);
> + writel(rate % 100, mhdp->regs + CDNS_SW_CLK_L);
> + writel(rate / 100, mhdp->regs + CDNS_SW_CLK_H);
> +
> + dev_dbg(>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(>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(>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(>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) |
> +

[PATCH v3 2/3] drm: bridge: Add support for Cadence MHDP DPI/DP bridge

2020-01-22 Thread Yuti Amonkar
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 
---
 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/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 3436297..616c05f 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -37,6 +37,17 @@ config DRM_CDNS_DSI
  Support Cadence DPI to DSI bridge. This is an internal
  bridge and is meant to be directly embedded in a SoC.
 
+config DRM_CDNS_MHDP
+   tristate "Cadence DPI/DP bridge"
+   select DRM_KMS_HELPER
+   select DRM_PANEL_BRIDGE
+   depends on OF
+   help
+ Support Cadence DPI to DP bridge. This is an internal
+ bridge and is meant to be directly embedded in a SoC.
+ It takes a DPI stream as input and output it encoded
+ in DP format.
+
 config DRM_DUMB_VGA_DAC
tristate "Dumb VGA DAC Bridge support"
depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 4934fcf..c1a0da7 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -16,4 +16,7 @@ obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
 obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
 obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o
 obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o
+obj-$(CONFIG_DRM_CDNS_MHDP) += mhdp8546.o
 obj-y += synopsys/
+
+mhdp8546-objs := cdns-mhdp.o
diff --git a/drivers/gpu/drm/bridge/cdns-mhdp.c 
b/drivers/gpu/drm/bridge/cdns-mhdp.c
new file mode 100644
index 000..0bc7fba
--- /dev/null
+++ b/drivers/gpu/drm/bridge/cdns-mhdp.c
@@ -0,0 +1,2202 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence MHDP DP bridge driver.
+ *
+ * Copyright: 2019 Cadence Design Systems, Inc.
+ *
+ * Author: Quentin Schulz 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#include "cdns-mhdp.h"
+
+static const struct of_device_id mhdp_ids[] = {
+   { .compatible = "cdns,mhdp8546", },
+   { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mhdp_ids);
+
+static inline u32 get_unaligned_be24(const void *p)
+{
+   const u8 *_p = p;
+
+   return _p[0] << 16 | _p[1] << 8 | _p[2];
+}
+
+static inline void put_unaligned_be24(u32 val, void *p)
+{
+   u8 *_p = p;
+
+   _p[0] = val >> 16;
+   _p[1] = val >> 8;
+   _p[2] = val;
+}
+
+static int cdns_mhdp_mailbox_read(struct cdns_mhdp_device *mhdp)
+{
+   int val, ret;
+
+   WARN_ON(!mutex_is_locked(>mbox_mutex));
+
+   ret = readx_poll_timeout(readl, mhdp->regs + CDNS_MAILBOX_EMPTY,
+val, !val, MAILBOX_RETRY_US,
+MAILBOX_TIMEOUT_US);
+   if (ret < 0)
+   return ret;
+
+   return readl(mhdp->regs + CDNS_MAILBOX_RX_DATA) & 0xff;
+}
+
+static int cdns_mhdp_mailbox_write(struct cdns_mhdp_device *mhdp, u8 val)
+{
+   int ret, full;
+
+   WARN_ON(!mutex_is_locked(>mbox_mutex));
+
+   ret = readx_poll_timeout(readl, mhdp->regs + CDNS_MAILBOX_FULL,
+full, !full, MAILBOX_RETRY_US,
+MAILBOX_TIMEOUT_US);
+   if (ret < 0)
+   return ret;
+
+   writel(val, mhdp->regs + CDNS_MAILBOX_TX_DATA);
+
+   return 0;
+}
+
+static int cdns_mhdp_mailbox_validate_receive(struct cdns_mhdp_device *mhdp,
+ u8 module_id, u8 opcode,
+ u16 req_size)
+{
+   u32 mbox_size, i;
+   u8 header[4];
+   int ret;
+
+   /* read the header of the message */
+   for (i = 0; i < 4; i++) {
+   ret = cdns_mhdp_mailbox_read(mhdp);
+   if (ret < 0)
+   return ret;
+
+   header[i] = ret;
+   }
+
+   mbox_size = get_unaligned_be16(header + 2);
+
+   if (opcode != header[0] || module_id != header[1] ||
+   req_size != mbox_size) {
+   /*
+* If the message in mailbox is not what we want, we need to
+* clear the mailbox by reading its contents.
+*/