/* According to the current drm framework sequence, take the encoder of * DSI_1 as master encoder
*/ #define DSI_ENCODER_MASTER DSI_1 #define DSI_ENCODER_SLAVE DSI_0
/* We assume 2 dsi nodes have the same information of bonded dsi and * sync-mode, and only one node specifies master in case of bonded mode.
*/ if (!msm_dsim->is_bonded_dsi)
msm_dsim->is_bonded_dsi = of_property_read_bool(np, "qcom,dual-dsi-mode");
if (msm_dsim->is_bonded_dsi) { if (of_property_read_bool(np, "qcom,master-dsi"))
msm_dsim->master_dsi_link_id = id; if (!msm_dsim->is_sync_needed)
msm_dsim->is_sync_needed = of_property_read_bool(
np, "qcom,sync-dual-dsi");
}
if (!IS_BONDED_DSI()) { /* * Set the usecase before calling msm_dsi_host_register(), which would * already program the PLL source mux based on a default usecase.
*/
msm_dsi_phy_set_usecase(msm_dsi->phy, MSM_DSI_PHY_STANDALONE);
msm_dsi_host_set_phy_mode(msm_dsi->host, msm_dsi->phy);
/* * PLL0 is to drive both DSI link clocks in bonded DSI mode. * * Set the usecase before calling msm_dsi_host_register(), which would * already program the PLL source mux based on a default usecase.
*/
msm_dsi_phy_set_usecase(clk_master_dsi->phy,
MSM_DSI_PHY_MASTER);
msm_dsi_phy_set_usecase(clk_slave_dsi->phy,
MSM_DSI_PHY_SLAVE);
msm_dsi_host_set_phy_mode(msm_dsi->host, msm_dsi->phy);
msm_dsi_host_set_phy_mode(other_dsi->host, other_dsi->phy);
/* 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(slave_link_dsi->host); if (ret) return ret;
ret = msm_dsi_host_register(master_link_dsi->host); if (ret) return ret;
}
/* In case of bonded DSI, some registers in PHY1 have been programmed * during PLL0 clock's set_rate. The PHY1 reset called by host1 here * will silently reset those PHY1 registers. Therefore we need to reset * and enable both PHYs before any PLL clock operation.
*/ if (IS_BONDED_DSI() && mdsi && sdsi) { if (!mdsi->phy_enabled && !sdsi->phy_enabled) {
msm_dsi_host_reset_phy(mdsi->host);
msm_dsi_host_reset_phy(sdsi->host);
ret = enable_phy(mdsi,
&shared_timings[DSI_CLOCK_MASTER]); if (ret) return ret;
ret = enable_phy(sdsi,
&shared_timings[DSI_CLOCK_SLAVE]); if (ret) {
msm_dsi_phy_disable(mdsi->phy); return ret;
}
}
} else {
msm_dsi_host_reset_phy(msm_dsi->host);
ret = enable_phy(msm_dsi, &shared_timings[id]); if (ret) return ret;
}
/* disable DSI phy * In bonded dsi configuration, the phy should be disabled for the * first controller only when the second controller is disabled.
*/
msm_dsi->phy_enabled = false; if (IS_BONDED_DSI() && mdsi && sdsi) { if (!mdsi->phy_enabled && !sdsi->phy_enabled) {
msm_dsi_phy_disable(sdsi->phy);
msm_dsi_phy_disable(mdsi->phy);
}
} else {
msm_dsi_phy_disable(msm_dsi->phy);
}
}
struct dsi_bridge { struct drm_bridge base; int id;
};
ret = dsi_mgr_phy_enable(id, phy_shared_timings); if (ret) goto phy_en_fail;
ret = msm_dsi_host_power_on(host, &phy_shared_timings[id], is_bonded_dsi, msm_dsi->phy); if (ret) {
pr_err("%s: power on host %d failed, %d\n", __func__, id, ret); goto host_on_fail;
}
if (is_bonded_dsi && msm_dsi1) {
ret = msm_dsi_host_power_on(msm_dsi1->host,
&phy_shared_timings[DSI_1], is_bonded_dsi, msm_dsi1->phy); if (ret) {
pr_err("%s: power on host1 failed, %d\n",
__func__, ret); goto host1_on_fail;
}
}
/* * Enable before preparing the panel, disable after unpreparing, so * that the panel can communicate over the DSI link.
*/
msm_dsi_host_enable_irq(host); if (is_bonded_dsi && msm_dsi1)
msm_dsi_host_enable_irq(msm_dsi1->host);
/* if dual dsi, trigger tpg on master first then slave */ if (m_dsi) {
msm_dsi_host_test_pattern_en(m_dsi->host); if (IS_BONDED_DSI() && s_dsi)
msm_dsi_host_test_pattern_en(s_dsi->host);
}
}
staticvoid dsi_mgr_bridge_post_disable(struct drm_bridge *bridge)
{ int id = dsi_mgr_bridge_get_id(bridge); struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1); struct mipi_dsi_host *host = msm_dsi->host; bool is_bonded_dsi = IS_BONDED_DSI(); int ret;
DBG("id=%d", id);
/* * Do nothing with the host if it is slave-DSI in case of bonded DSI. * It is safe to call dsi_mgr_phy_disable() here because a single PHY * won't be diabled until both PHYs request disable.
*/ if (is_bonded_dsi && !IS_MASTER_DSI_LINK(id)) goto disable_phy;
ret = msm_dsi_host_disable(host); if (ret)
pr_err("%s: host %d disable failed, %d\n", __func__, id, ret);
if (is_bonded_dsi && msm_dsi1) {
ret = msm_dsi_host_disable(msm_dsi1->host); if (ret)
pr_err("%s: host1 disable failed, %d\n", __func__, ret);
}
msm_dsi_host_disable_irq(host); if (is_bonded_dsi && msm_dsi1)
msm_dsi_host_disable_irq(msm_dsi1->host);
/* Save PHY status if it is a clock source */
msm_dsi_phy_pll_save_state(msm_dsi->phy);
ret = msm_dsi_host_power_off(host); if (ret)
pr_err("%s: host %d power off failed,%d\n", __func__, id, ret);
if (is_bonded_dsi && msm_dsi1) {
ret = msm_dsi_host_power_off(msm_dsi1->host); if (ret)
pr_err("%s: host1 power off failed, %d\n",
__func__, ret);
}
opp = dev_pm_opp_find_freq_ceil(&pdev->dev, &byte_clk_rate); if (!IS_ERR(opp)) {
dev_pm_opp_put(opp);
} elseif (PTR_ERR(opp) == -ERANGE) { /* * An empty table is created by devm_pm_opp_set_clkname() even * if there is none. Thus find_freq_ceil will still return * -ERANGE in such case.
*/ if (dev_pm_opp_get_opp_count(&pdev->dev) != 0) return MODE_CLOCK_RANGE;
} else { return MODE_ERROR;
}
return msm_dsi_host_check_dsc(host, mode);
}
staticint dsi_mgr_bridge_attach(struct drm_bridge *bridge, struct drm_encoder *encoder, enum drm_bridge_attach_flags flags)
{ int id = dsi_mgr_bridge_get_id(bridge); struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
/* In bonded master case, panel requires the same commands sent to * both DSI links. Host issues the command trigger to both links * when DSI_1 calls the cmd transfer function, no matter it happens * before or after DSI_0 cmd transfer.
*/ if (need_sync && (id == DSI_0)) return is_read ? msg->rx_len : msg->tx_len;
if (need_sync && msm_dsi0) {
ret = msm_dsi_host_xfer_prepare(msm_dsi0->host, msg); if (ret) {
pr_err("%s: failed to prepare non-trigger host, %d\n",
__func__, ret); return ret;
}
}
ret = msm_dsi_host_xfer_prepare(host, msg); if (ret) {
pr_err("%s: failed to prepare host, %d\n", __func__, ret); goto restore_host0;
}
ret = is_read ? msm_dsi_host_cmd_rx(host, msg) :
msm_dsi_host_cmd_tx(host, msg);
msm_dsi_host_xfer_restore(host, msg);
restore_host0: if (need_sync && msm_dsi0)
msm_dsi_host_xfer_restore(msm_dsi0->host, msg);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.