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;
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)
#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;
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) */
/* 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);
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);
/* 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__ */
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;
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;
}
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;
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);
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;
{
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) {
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;
}
};
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);
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; \
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)
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)
{
*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;
+}
+