[PATCH 2/3] drm/msm/dsi: Enable PLL driver in MSM DSI

Hai Li hali at codeaurora.org
Fri May 15 10:04:05 PDT 2015


This change activates PLL driver for DSI to work with
common clock framework.

Signed-off-by: Hai Li <hali at codeaurora.org>
---
 drivers/gpu/drm/msm/dsi/dsi.c         | 14 +++++++
 drivers/gpu/drm/msm/dsi/dsi.h         | 43 ++++++++++++++++---
 drivers/gpu/drm/msm/dsi/dsi_host.c    | 60 ++++++++++++++++++++++----
 drivers/gpu/drm/msm/dsi/dsi_manager.c | 79 +++++++++++++++++++++++++----------
 drivers/gpu/drm/msm/dsi/dsi_phy.c     | 68 ++++++++++++++++++++++++++++++
 5 files changed, 229 insertions(+), 35 deletions(-)

diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c
index ad50b80..beb26df 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.c
+++ b/drivers/gpu/drm/msm/dsi/dsi.c
@@ -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)
diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h
index 321964a..8022814 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.h
+++ b/drivers/gpu/drm/msm/dsi/dsi.h
@@ -38,6 +38,13 @@
 #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__ */
 
diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c
index f7ba890..1cf9f11 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -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;
 }
@@ -1508,15 +1527,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;
@@ -1824,6 +1837,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);
diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c
index 0a40f3c..87ac661 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_manager.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c
@@ -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;
 }
 
diff --git a/drivers/gpu/drm/msm/dsi/dsi_phy.c b/drivers/gpu/drm/msm/dsi/dsi_phy.c
index fc11474..e313e52 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_phy.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_phy.c
@@ -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;
+}
+
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation



More information about the dri-devel mailing list