]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
drm/rockchip: dsi: add dual mipi support
authorHeiko Stuebner <heiko@sntech.de>
Mon, 1 Oct 2018 12:38:45 +0000 (14:38 +0200)
committerAndrzej Hajda <a.hajda@samsung.com>
Tue, 30 Oct 2018 13:06:31 +0000 (14:06 +0100)
Add the Rockchip-sepcific dual-dsi setup and hook it into the VOP as well.
As described in the general dual-dsi devicetree binding, the panel should
define two input ports and point each of them to one of the used dsi-
controllers, as well as declare one of them as clock-master.
This is used to determine the dual-dsi state and get access to both
controller instances.

v6:
  handle master+slave component in dsi-attach
v5:
  use driver-internal mechanism to find dual dsi slave
v4:
  add component directly in probe when adding empty dsi slave controller

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20181001123845.11818-8-heiko@sntech.de
drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
drivers/gpu/drm/rockchip/rockchip_drm_drv.h
drivers/gpu/drm/rockchip/rockchip_drm_vop.c
drivers/gpu/drm/rockchip/rockchip_drm_vop.h
drivers/gpu/drm/rockchip/rockchip_vop_reg.c

index b3aae8439aa3c5a7e6513c19097e002aa5f5ce5d..7ee359bcee62521b5e83ef2533fcb065998440b9 100644 (file)
@@ -218,6 +218,10 @@ struct dw_mipi_dsi_rockchip {
        struct clk *grf_clk;
        struct clk *phy_cfg_clk;
 
+       /* dual-channel */
+       bool is_slave;
+       struct dw_mipi_dsi_rockchip *slave;
+
        unsigned int lane_mbps; /* per lane */
        u16 input_div;
        u16 feedback_div;
@@ -226,6 +230,7 @@ struct dw_mipi_dsi_rockchip {
        struct dw_mipi_dsi *dmd;
        const struct rockchip_dw_dsi_chip_data *cdata;
        struct dw_mipi_dsi_plat_data pdata;
+       int devcnt;
 };
 
 struct dphy_pll_parameter_map {
@@ -602,6 +607,8 @@ dw_mipi_dsi_encoder_atomic_check(struct drm_encoder *encoder,
        }
 
        s->output_type = DRM_MODE_CONNECTOR_DSI;
+       if (dsi->slave)
+               s->output_flags = ROCKCHIP_OUTPUT_DSI_DUAL;
 
        return 0;
 }
@@ -617,6 +624,8 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
                return;
 
        pm_runtime_get_sync(dsi->dev);
+       if (dsi->slave)
+               pm_runtime_get_sync(dsi->slave->dev);
 
        /*
         * For the RK3399, the clk of grf must be enabled before writing grf
@@ -630,6 +639,8 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
        }
 
        dw_mipi_dsi_rockchip_config(dsi, mux);
+       if (dsi->slave)
+               dw_mipi_dsi_rockchip_config(dsi->slave, mux);
 
        clk_disable_unprepare(dsi->grf_clk);
 }
@@ -638,6 +649,8 @@ static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
 {
        struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder);
 
+       if (dsi->slave)
+               pm_runtime_put(dsi->slave->dev);
        pm_runtime_put(dsi->dev);
 }
 
@@ -673,14 +686,113 @@ static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi,
        return 0;
 }
 
+static struct device
+*dw_mipi_dsi_rockchip_find_second(struct dw_mipi_dsi_rockchip *dsi)
+{
+       const struct of_device_id *match;
+       struct device_node *node = NULL, *local;
+
+       match = of_match_device(dsi->dev->driver->of_match_table, dsi->dev);
+
+       local = of_graph_get_remote_node(dsi->dev->of_node, 1, 0);
+       if (!local)
+               return NULL;
+
+       while ((node = of_find_compatible_node(node, NULL,
+                                              match->compatible))) {
+               struct device_node *remote;
+
+               /* found ourself */
+               if (node == dsi->dev->of_node)
+                       continue;
+
+               remote = of_graph_get_remote_node(node, 1, 0);
+               if (!remote)
+                       continue;
+
+               /* same display device in port1-ep0 for both */
+               if (remote == local) {
+                       struct dw_mipi_dsi_rockchip *dsi2;
+                       struct platform_device *pdev;
+
+                       pdev = of_find_device_by_node(node);
+
+                       /*
+                        * we have found the second, so will either return it
+                        * or return with an error. In any case won't need the
+                        * nodes anymore nor continue the loop.
+                        */
+                       of_node_put(remote);
+                       of_node_put(node);
+                       of_node_put(local);
+
+                       if (!pdev)
+                               return ERR_PTR(-EPROBE_DEFER);
+
+                       dsi2 = platform_get_drvdata(pdev);
+                       if (!dsi2) {
+                               platform_device_put(pdev);
+                               return ERR_PTR(-EPROBE_DEFER);
+                       }
+
+                       return &pdev->dev;
+               }
+
+               of_node_put(remote);
+       }
+
+       of_node_put(local);
+
+       return NULL;
+}
+
 static int dw_mipi_dsi_rockchip_bind(struct device *dev,
                                     struct device *master,
                                     void *data)
 {
        struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev);
        struct drm_device *drm_dev = data;
+       struct device *second;
+       bool master1, master2;
        int ret;
 
+       second = dw_mipi_dsi_rockchip_find_second(dsi);
+       if (IS_ERR(second))
+               return PTR_ERR(second);
+
+       if (second) {
+               master1 = of_property_read_bool(dsi->dev->of_node,
+                                               "clock-master");
+               master2 = of_property_read_bool(second->of_node,
+                                               "clock-master");
+
+               if (master1 && master2) {
+                       DRM_DEV_ERROR(dsi->dev, "only one clock-master allowed\n");
+                       return -EINVAL;
+               }
+
+               if (!master1 && !master2) {
+                       DRM_DEV_ERROR(dsi->dev, "no clock-master defined\n");
+                       return -EINVAL;
+               }
+
+               /* we are the slave in dual-DSI */
+               if (!master1) {
+                       dsi->is_slave = true;
+                       return 0;
+               }
+
+               dsi->slave = dev_get_drvdata(second);
+               if (!dsi->slave) {
+                       DRM_DEV_ERROR(dev, "could not get slaves data\n");
+                       return -ENODEV;
+               }
+
+               dsi->slave->is_slave = true;
+               dw_mipi_dsi_set_slave(dsi->dmd, dsi->slave->dmd);
+               put_device(second);
+       }
+
        ret = clk_prepare_enable(dsi->pllref_clk);
        if (ret) {
                DRM_DEV_ERROR(dev, "Failed to enable pllref_clk: %d\n", ret);
@@ -708,6 +820,9 @@ static void dw_mipi_dsi_rockchip_unbind(struct device *dev,
 {
        struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev);
 
+       if (dsi->is_slave)
+               return;
+
        dw_mipi_dsi_unbind(dsi->dmd);
 
        clk_disable_unprepare(dsi->pllref_clk);
@@ -722,6 +837,7 @@ static int dw_mipi_dsi_rockchip_host_attach(void *priv_data,
                                            struct mipi_dsi_device *device)
 {
        struct dw_mipi_dsi_rockchip *dsi = priv_data;
+       struct device *second;
        int ret;
 
        ret = component_add(dsi->dev, &dw_mipi_dsi_rockchip_ops);
@@ -731,6 +847,19 @@ static int dw_mipi_dsi_rockchip_host_attach(void *priv_data,
                return ret;
        }
 
+       second = dw_mipi_dsi_rockchip_find_second(dsi);
+       if (IS_ERR(second))
+               return PTR_ERR(second);
+       if (second) {
+               ret = component_add(second, &dw_mipi_dsi_rockchip_ops);
+               if (ret) {
+                       DRM_DEV_ERROR(second,
+                                     "Failed to register component: %d\n",
+                                     ret);
+                       return ret;
+               }
+       }
+
        return 0;
 }
 
@@ -738,6 +867,11 @@ static int dw_mipi_dsi_rockchip_host_detach(void *priv_data,
                                            struct mipi_dsi_device *device)
 {
        struct dw_mipi_dsi_rockchip *dsi = priv_data;
+       struct device *second;
+
+       second = dw_mipi_dsi_rockchip_find_second(dsi);
+       if (second && !IS_ERR(second))
+               component_del(second, &dw_mipi_dsi_rockchip_ops);
 
        component_del(dsi->dev, &dw_mipi_dsi_rockchip_ops);
 
@@ -846,6 +980,9 @@ static int dw_mipi_dsi_rockchip_remove(struct platform_device *pdev)
 {
        struct dw_mipi_dsi_rockchip *dsi = platform_get_drvdata(pdev);
 
+       if (dsi->devcnt == 0)
+               component_del(dsi->dev, &dw_mipi_dsi_rockchip_ops);
+
        dw_mipi_dsi_remove(dsi->dmd);
 
        return 0;
index 453fef6a55b9b1d6ba63f194d64c113e0b392b92..ce48568ec8a0bd13b1dc86ffa280f110b19c4342 100644 (file)
@@ -37,6 +37,7 @@ struct rockchip_crtc_state {
        int output_type;
        int output_mode;
        int output_bpc;
+       int output_flags;
 };
 #define to_rockchip_crtc_state(s) \
                container_of(s, struct rockchip_crtc_state, base)
index 0c35a88e33dde45fdf6e0184d0d09d1185ec10a0..fb70fb486fbf4a57da6755a4c513d2dd426d1f33 100644 (file)
@@ -916,6 +916,7 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
        pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) ?
                   BIT(VSYNC_POSITIVE) : 0;
        VOP_REG_SET(vop, output, pin_pol, pin_pol);
+       VOP_REG_SET(vop, output, mipi_dual_channel_en, 0);
 
        switch (s->output_type) {
        case DRM_MODE_CONNECTOR_LVDS:
@@ -933,6 +934,8 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
        case DRM_MODE_CONNECTOR_DSI:
                VOP_REG_SET(vop, output, mipi_pin_pol, pin_pol);
                VOP_REG_SET(vop, output, mipi_en, 1);
+               VOP_REG_SET(vop, output, mipi_dual_channel_en,
+                           !!(s->output_flags & ROCKCHIP_OUTPUT_DSI_DUAL));
                break;
        case DRM_MODE_CONNECTOR_DisplayPort:
                pin_pol &= ~BIT(DCLK_INVERT);
index fd5765dfd63752a4e7ba5c3bd7ed30e1e1722bf3..0fe40e1983d97bef4d12f9f8e305fdc38767066b 100644 (file)
@@ -60,6 +60,7 @@ struct vop_output {
        struct vop_reg edp_en;
        struct vop_reg hdmi_en;
        struct vop_reg mipi_en;
+       struct vop_reg mipi_dual_channel_en;
        struct vop_reg rgb_en;
 };
 
@@ -214,6 +215,9 @@ struct vop_data {
 /* for use special outface */
 #define ROCKCHIP_OUT_MODE_AAAA 15
 
+/* output flags */
+#define ROCKCHIP_OUTPUT_DSI_DUAL       BIT(0)
+
 enum alpha_mode {
        ALPHA_STRAIGHT,
        ALPHA_INVERSE,
index 047a8867af90421cd04d5d6511e9b2295609631c..08fc40af52c89d36b4964fc0714990d2d82b4ea6 100644 (file)
@@ -634,6 +634,7 @@ static const struct vop_output rk3399_output = {
        .hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13),
        .edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14),
        .mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15),
+       .mipi_dual_channel_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 3),
 };
 
 static const struct vop_data rk3399_vop_big = {