drm/msm/dsi: Enable PLL driver in MSM DSI
authorHai Li <hali@codeaurora.org>
Fri, 15 May 2015 17:04:05 +0000 (13:04 -0400)
committerRob Clark <robdclark@gmail.com>
Thu, 11 Jun 2015 17:11:04 +0000 (13:11 -0400)
This change activates PLL driver for DSI to work with
common clock framework.

Signed-off-by: Hai Li <hali@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
drivers/gpu/drm/msm/dsi/dsi.c
drivers/gpu/drm/msm/dsi/dsi.h
drivers/gpu/drm/msm/dsi/dsi_host.c
drivers/gpu/drm/msm/dsi/dsi_manager.c
drivers/gpu/drm/msm/dsi/dsi_phy.c

index ad50b80225f5b95d7f90252ea4ba086109a730d7..beb26dfca2763d3f1eefded0766e437a840d6eea 100644 (file)
@@ -29,6 +29,12 @@ static void dsi_destroy(struct msm_dsi *msm_dsi)
                return;
 
        msm_dsi_manager_unregister(msm_dsi);
+
+       if (msm_dsi->phy) {
+               msm_dsi_phy_destroy(msm_dsi->phy);
+               msm_dsi->phy = NULL;
+       }
+
        if (msm_dsi->host) {
                msm_dsi_host_destroy(msm_dsi->host);
                msm_dsi->host = NULL;
@@ -63,6 +69,14 @@ static struct msm_dsi *dsi_init(struct platform_device *pdev)
        if (ret)
                goto fail;
 
+       /* Init dsi PHY */
+       msm_dsi->phy = msm_dsi_phy_init(pdev, msm_dsi->phy_type, msm_dsi->id);
+       if (!msm_dsi->phy) {
+               ret = -ENXIO;
+               pr_err("%s: phy init failed\n", __func__);
+               goto fail;
+       }
+
        /* Register to dsi manager */
        ret = msm_dsi_manager_register(msm_dsi);
        if (ret)
index 321964a6b27e26ba10f3eeee8b8321a6a333c65f..8022814f791480caa7d0243eb4d04e2f8beac8f6 100644 (file)
 #define DSI_ENCODER_MASTER     DSI_1
 #define DSI_ENCODER_SLAVE      DSI_0
 
+enum msm_dsi_phy_type {
+       MSM_DSI_PHY_UNKNOWN,
+       MSM_DSI_PHY_28NM_HPM,
+       MSM_DSI_PHY_28NM_LP,
+       MSM_DSI_PHY_MAX
+};
+
 struct msm_dsi {
        struct drm_device *dev;
        struct platform_device *pdev;
@@ -49,6 +56,8 @@ struct msm_dsi {
        struct msm_dsi_phy *phy;
        struct drm_panel *panel;
        unsigned long panel_flags;
+
+       enum msm_dsi_phy_type phy_type;
        bool phy_enabled;
 
        /* the encoders we are hooked to (outside of dsi block) */
@@ -73,6 +82,29 @@ void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi);
 /* msm dsi */
 struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi);
 
+/* dsi pll */
+struct msm_dsi_pll;
+#ifdef CONFIG_DRM_MSM_DSI_PLL
+struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev,
+                       enum msm_dsi_phy_type type, int dsi_id);
+void msm_dsi_pll_destroy(struct msm_dsi_pll *pll);
+int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll,
+       struct clk **byte_clk_provider, struct clk **pixel_clk_provider);
+#else
+static inline struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev,
+                        enum msm_dsi_phy_type type, int id) {
+       return ERR_PTR(-ENODEV);
+}
+static inline void msm_dsi_pll_destroy(struct msm_dsi_pll *pll)
+{
+}
+static inline int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll,
+       struct clk **byte_clk_provider, struct clk **pixel_clk_provider)
+{
+       return -ENODEV;
+}
+#endif
+
 /* dsi host */
 int msm_dsi_host_xfer_prepare(struct mipi_dsi_host *host,
                                        const struct mipi_dsi_msg *msg);
@@ -94,6 +126,8 @@ struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host,
                                        unsigned long *panel_flags);
 int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer);
 void msm_dsi_host_unregister(struct mipi_dsi_host *host);
+int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host,
+                       struct msm_dsi_pll *src_pll);
 void msm_dsi_host_destroy(struct mipi_dsi_host *host);
 int msm_dsi_host_modeset_init(struct mipi_dsi_host *host,
                                        struct drm_device *dev);
@@ -101,18 +135,15 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi);
 
 /* dsi phy */
 struct msm_dsi_phy;
-enum msm_dsi_phy_type {
-       MSM_DSI_PHY_UNKNOWN,
-       MSM_DSI_PHY_28NM_HPM,
-       MSM_DSI_PHY_28NM_LP,
-       MSM_DSI_PHY_MAX
-};
 struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev,
                        enum msm_dsi_phy_type type, int id);
+void msm_dsi_phy_destroy(struct msm_dsi_phy *phy);
 int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel,
        const unsigned long bit_rate, const unsigned long esc_rate);
 int msm_dsi_phy_disable(struct msm_dsi_phy *phy);
 void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy,
                                        u32 *clk_pre, u32 *clk_post);
+struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy);
+
 #endif /* __DSI_CONNECTOR_H__ */
 
index 8a246cfa5d195049287de3e8dc8c7a3c129f5ff6..43ea8a30c12129b6af8d367da2bcf1f60b39318f 100644 (file)
@@ -205,6 +205,9 @@ struct msm_dsi_host {
        struct clk *byte_clk;
        struct clk *esc_clk;
        struct clk *pixel_clk;
+       struct clk *byte_clk_src;
+       struct clk *pixel_clk_src;
+
        u32 byte_clk_rate;
 
        struct gpio_desc *disp_en_gpio;
@@ -463,6 +466,22 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host)
                goto exit;
        }
 
+       msm_host->byte_clk_src = devm_clk_get(dev, "byte_clk_src");
+       if (IS_ERR(msm_host->byte_clk_src)) {
+               ret = PTR_ERR(msm_host->byte_clk_src);
+               pr_err("%s: can't find byte_clk_src. ret=%d\n", __func__, ret);
+               msm_host->byte_clk_src = NULL;
+               goto exit;
+       }
+
+       msm_host->pixel_clk_src = devm_clk_get(dev, "pixel_clk_src");
+       if (IS_ERR(msm_host->pixel_clk_src)) {
+               ret = PTR_ERR(msm_host->pixel_clk_src);
+               pr_err("%s: can't find pixel_clk_src. ret=%d\n", __func__, ret);
+               msm_host->pixel_clk_src = NULL;
+               goto exit;
+       }
+
 exit:
        return ret;
 }
@@ -1513,15 +1532,9 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi)
        msm_host->workqueue = alloc_ordered_workqueue("dsi_drm_work", 0);
        INIT_WORK(&msm_host->err_work, dsi_err_worker);
 
-       msm_dsi->phy = msm_dsi_phy_init(pdev, msm_host->cfg->phy_type,
-                                       msm_host->id);
-       if (!msm_dsi->phy) {
-               ret = -EINVAL;
-               pr_err("%s: phy init failed\n", __func__);
-               goto fail;
-       }
        msm_dsi->host = &msm_host->base;
        msm_dsi->id = msm_host->id;
+       msm_dsi->phy_type = msm_host->cfg->phy_type;
 
        DBG("Dsi Host %d initialized", msm_host->id);
        return 0;
@@ -1829,6 +1842,39 @@ void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, u32 iova, u32 len)
        wmb();
 }
 
+int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host,
+       struct msm_dsi_pll *src_pll)
+{
+       struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
+       struct clk *byte_clk_provider, *pixel_clk_provider;
+       int ret;
+
+       ret = msm_dsi_pll_get_clk_provider(src_pll,
+                               &byte_clk_provider, &pixel_clk_provider);
+       if (ret) {
+               pr_info("%s: can't get provider from pll, don't set parent\n",
+                       __func__);
+               return 0;
+       }
+
+       ret = clk_set_parent(msm_host->byte_clk_src, byte_clk_provider);
+       if (ret) {
+               pr_err("%s: can't set parent to byte_clk_src. ret=%d\n",
+                       __func__, ret);
+               goto exit;
+       }
+
+       ret = clk_set_parent(msm_host->pixel_clk_src, pixel_clk_provider);
+       if (ret) {
+               pr_err("%s: can't set parent to pixel_clk_src. ret=%d\n",
+                       __func__, ret);
+               goto exit;
+       }
+
+exit:
+       return ret;
+}
+
 int msm_dsi_host_enable(struct mipi_dsi_host *host)
 {
        struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
index 0a40f3c64e8b3d05ce78af069f97851f8561c502..87ac6612b6f85a574f7f7f246c11888afaa08e7a 100644 (file)
@@ -60,6 +60,53 @@ static int dsi_mgr_parse_dual_panel(struct device_node *np, int id)
        return 0;
 }
 
+static int dsi_mgr_host_register(int id)
+{
+       struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
+       struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id);
+       struct msm_dsi *clk_master_dsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER);
+       struct msm_dsi_pll *src_pll;
+       int ret;
+
+       if (!IS_DUAL_PANEL()) {
+               ret = msm_dsi_host_register(msm_dsi->host, true);
+               if (ret)
+                       return ret;
+
+               src_pll = msm_dsi_phy_get_pll(msm_dsi->phy);
+               ret = msm_dsi_host_set_src_pll(msm_dsi->host, src_pll);
+       } else if (!other_dsi) {
+               ret = 0;
+       } else {
+               struct msm_dsi *mdsi = IS_MASTER_PANEL(id) ?
+                                       msm_dsi : other_dsi;
+               struct msm_dsi *sdsi = IS_MASTER_PANEL(id) ?
+                                       other_dsi : msm_dsi;
+               /* Register slave host first, so that slave DSI device
+                * has a chance to probe, and do not block the master
+                * DSI device's probe.
+                * Also, do not check defer for the slave host,
+                * because only master DSI device adds the panel to global
+                * panel list. The panel's device is the master DSI device.
+                */
+               ret = msm_dsi_host_register(sdsi->host, false);
+               if (ret)
+                       return ret;
+               ret = msm_dsi_host_register(mdsi->host, true);
+               if (ret)
+                       return ret;
+
+               /* PLL0 is to drive both 2 DSI link clocks in Dual DSI mode. */
+               src_pll = msm_dsi_phy_get_pll(clk_master_dsi->phy);
+               ret = msm_dsi_host_set_src_pll(msm_dsi->host, src_pll);
+               if (ret)
+                       return ret;
+               ret = msm_dsi_host_set_src_pll(other_dsi->host, src_pll);
+       }
+
+       return ret;
+}
+
 struct dsi_connector {
        struct drm_connector base;
        int id;
@@ -652,7 +699,6 @@ int msm_dsi_manager_register(struct msm_dsi *msm_dsi)
 {
        struct msm_dsi_manager *msm_dsim = &msm_dsim_glb;
        int id = msm_dsi->id;
-       struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id);
        int ret;
 
        if (id > DSI_MAX) {
@@ -670,31 +716,20 @@ int msm_dsi_manager_register(struct msm_dsi *msm_dsi)
        ret = dsi_mgr_parse_dual_panel(msm_dsi->pdev->dev.of_node, id);
        if (ret) {
                pr_err("%s: failed to parse dual panel info\n", __func__);
-               return ret;
+               goto fail;
        }
 
-       if (!IS_DUAL_PANEL()) {
-               ret = msm_dsi_host_register(msm_dsi->host, true);
-       } else if (!other_dsi) {
-               return 0;
-       } else {
-               struct msm_dsi *mdsi = IS_MASTER_PANEL(id) ?
-                                       msm_dsi : other_dsi;
-               struct msm_dsi *sdsi = IS_MASTER_PANEL(id) ?
-                                       other_dsi : msm_dsi;
-               /* Register slave host first, so that slave DSI device
-                * has a chance to probe, and do not block the master
-                * DSI device's probe.
-                * Also, do not check defer for the slave host,
-                * because only master DSI device adds the panel to global
-                * panel list. The panel's device is the master DSI device.
-                */
-               ret = msm_dsi_host_register(sdsi->host, false);
-               if (ret)
-                       return ret;
-               ret = msm_dsi_host_register(mdsi->host, true);
+       ret = dsi_mgr_host_register(id);
+       if (ret) {
+               pr_err("%s: failed to register mipi dsi host for DSI %d\n",
+                       __func__, id);
+               goto fail;
        }
 
+       return 0;
+
+fail:
+       msm_dsim->dsi[id] = NULL;
        return ret;
 }
 
index 4403f38bf220c2b83784447b01ee4cf072159c39..2b1c8fdb10de1678ea739c0496d5ce23d9e9fd1c 100644 (file)
@@ -34,10 +34,18 @@ struct dsi_dphy_timing {
 };
 
 struct msm_dsi_phy {
+       struct platform_device *pdev;
        void __iomem *base;
        void __iomem *reg_base;
        int id;
+
+       struct clk *ahb_clk;
+
        struct dsi_dphy_timing timing;
+       enum msm_dsi_phy_type type;
+
+       struct msm_dsi_pll *pll;
+
        int (*enable)(struct msm_dsi_phy *phy, bool is_dual_panel,
                const unsigned long bit_rate, const unsigned long esc_rate);
        int (*disable)(struct msm_dsi_phy *phy);
@@ -284,6 +292,27 @@ static int dsi_28nm_phy_disable(struct msm_dsi_phy *phy)
        return 0;
 }
 
+static int dsi_phy_enable_resource(struct msm_dsi_phy *phy)
+{
+       int ret;
+
+       pm_runtime_get_sync(&phy->pdev->dev);
+
+       ret = clk_prepare_enable(phy->ahb_clk);
+       if (ret) {
+               pr_err("%s: can't enable ahb clk, %d\n", __func__, ret);
+               pm_runtime_put_sync(&phy->pdev->dev);
+       }
+
+       return ret;
+}
+
+static void dsi_phy_disable_resource(struct msm_dsi_phy *phy)
+{
+       clk_disable_unprepare(phy->ahb_clk);
+       pm_runtime_put_sync(&phy->pdev->dev);
+}
+
 #define dsi_phy_func_init(name)        \
        do {    \
                phy->enable = dsi_##name##_phy_enable;  \
@@ -294,6 +323,7 @@ struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev,
                        enum msm_dsi_phy_type type, int id)
 {
        struct msm_dsi_phy *phy;
+       int ret;
 
        phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
        if (!phy)
@@ -320,11 +350,41 @@ struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev,
                return NULL;
        }
 
+       phy->type = type;
        phy->id = id;
+       phy->pdev = pdev;
+
+       phy->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
+       if (IS_ERR(phy->ahb_clk)) {
+               pr_err("%s: Unable to get ahb clk\n", __func__);
+               return NULL;
+       }
+
+       /* PLL init will call into clk_register which requires
+        * register access, so we need to enable power and ahb clock.
+        */
+       ret = dsi_phy_enable_resource(phy);
+       if (ret)
+               return NULL;
+
+       phy->pll = msm_dsi_pll_init(pdev, type, id);
+       if (!phy->pll)
+               pr_info("%s: pll init failed, need separate pll clk driver\n",
+                       __func__);
+
+       dsi_phy_disable_resource(phy);
 
        return phy;
 }
 
+void msm_dsi_phy_destroy(struct msm_dsi_phy *phy)
+{
+       if (phy->pll) {
+               msm_dsi_pll_destroy(phy->pll);
+               phy->pll = NULL;
+       }
+}
+
 int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel,
        const unsigned long bit_rate, const unsigned long esc_rate)
 {
@@ -351,3 +411,11 @@ void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy,
                *clk_post = phy->timing.clk_post;
 }
 
+struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy)
+{
+       if (!phy)
+               return NULL;
+
+       return phy->pll;
+}
+