This change activates PLL driver for DSI to work with common clock framework. Signed-off-by: Hai Li <hali@xxxxxxxxxxxxxx> --- 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 -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html