This patch series adds support to DSI connector virtualization for Dual-DSI configuration.
These changes have been tested using dual-dsi truly panel on sdm845 platform.
Additional changes that will be needed to have end-to-end functionality: --> DSI6G-v2 changes: https://patchwork.kernel.org/patch/10294605/ --> truly panel patches: https://patchwork.kernel.org/patch/10327749/ --> DPU changes that will be uploaded soon.
Chandan Uddaraju (2): drm/msm/dsi: adjust dsi timing for dual dsi mode drm/msm/dsi: Use one connector for dual DSI mode
drivers/gpu/drm/msm/dsi/dsi.c | 3 + drivers/gpu/drm/msm/dsi/dsi.h | 2 + drivers/gpu/drm/msm/dsi/dsi_host.c | 17 +++++ drivers/gpu/drm/msm/dsi/dsi_manager.c | 125 +++++++++++----------------------- 4 files changed, 62 insertions(+), 85 deletions(-)
For dual dsi mode, the horizontal timing needs to be divided by half since both the dsi controllers will be driving this panel. Adjust the pixel clock and DSI timing accordingly.
Change-Id: Iee1226b2eef9eea23d9653e3d738ee8cd2a2dd8e Signed-off-by: Chandan Uddaraju chandanu@codeaurora.org --- drivers/gpu/drm/msm/dsi/dsi.h | 1 + drivers/gpu/drm/msm/dsi/dsi_host.c | 17 +++++++++++++++++ drivers/gpu/drm/msm/dsi/dsi_manager.c | 15 +++++++++++++++ 3 files changed, 33 insertions(+)
diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h index 70d9a9a..4131b47 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.h +++ b/drivers/gpu/drm/msm/dsi/dsi.h @@ -161,6 +161,7 @@ void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, u32 dma_base, u32 len); int msm_dsi_host_enable(struct mipi_dsi_host *host); int msm_dsi_host_disable(struct mipi_dsi_host *host); +void msm_dsi_host_adjust_timing_config(struct mipi_dsi_host *host); int msm_dsi_host_power_on(struct mipi_dsi_host *host, struct msm_dsi_phy_shared_timings *phy_shared_timings); int msm_dsi_host_power_off(struct mipi_dsi_host *host); diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 7a03a94..66a21cb 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -2237,6 +2237,23 @@ static void msm_dsi_sfpb_config(struct msm_dsi_host *msm_host, bool enable) SFPB_GPREG_MASTER_PORT_EN(en)); }
+void msm_dsi_host_adjust_timing_config(struct mipi_dsi_host *host) +{ + struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + struct drm_display_mode *mode = NULL; + + mode = msm_host->mode; + + mutex_lock(&msm_host->dev_mutex); + mode->htotal >>= 1; + mode->hdisplay >>= 1; + mode->hsync_start >>= 1; + mode->hsync_end >>= 1; + + mode->clock >>= 1; + mutex_unlock(&msm_host->dev_mutex); +} + int msm_dsi_host_power_on(struct mipi_dsi_host *host, struct msm_dsi_phy_shared_timings *phy_shared_timings) { diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index 4cb1cb6..8ef1c3d 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -627,6 +627,21 @@ static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge, msm_dsi_host_set_display_mode(host, adjusted_mode); if (is_dual_dsi && other_dsi) msm_dsi_host_set_display_mode(other_dsi->host, adjusted_mode); + + /* + * For dual DSI mode, the current DRM mode has + * the complete width of the panel. Since, the complete + * panel is driven by two DSI controllers, the + * horizontal timings and the pixel clock have to be + * split between the two dsi controllers. Adjust the + * DSI host timing structures accordingly. + */ + if (is_dual_dsi) { + msm_dsi_host_adjust_timing_config(host); + if (other_dsi) + msm_dsi_host_adjust_timing_config(other_dsi->host); + } + }
static const struct drm_connector_funcs dsi_mgr_connector_funcs = {
On Tue, Apr 10, 2018 at 06:16:07PM -0700, Chandan Uddaraju wrote:
For dual dsi mode, the horizontal timing needs to be divided by half since both the dsi controllers will be driving this panel. Adjust the pixel clock and DSI timing accordingly.
Change-Id: Iee1226b2eef9eea23d9653e3d738ee8cd2a2dd8e Signed-off-by: Chandan Uddaraju chandanu@codeaurora.org
drivers/gpu/drm/msm/dsi/dsi.h | 1 + drivers/gpu/drm/msm/dsi/dsi_host.c | 17 +++++++++++++++++ drivers/gpu/drm/msm/dsi/dsi_manager.c | 15 +++++++++++++++ 3 files changed, 33 insertions(+)
diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h index 70d9a9a..4131b47 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.h +++ b/drivers/gpu/drm/msm/dsi/dsi.h @@ -161,6 +161,7 @@ void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, u32 dma_base, u32 len); int msm_dsi_host_enable(struct mipi_dsi_host *host); int msm_dsi_host_disable(struct mipi_dsi_host *host); +void msm_dsi_host_adjust_timing_config(struct mipi_dsi_host *host); int msm_dsi_host_power_on(struct mipi_dsi_host *host, struct msm_dsi_phy_shared_timings *phy_shared_timings); int msm_dsi_host_power_off(struct mipi_dsi_host *host); diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 7a03a94..66a21cb 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -2237,6 +2237,23 @@ static void msm_dsi_sfpb_config(struct msm_dsi_host *msm_host, bool enable) SFPB_GPREG_MASTER_PORT_EN(en)); }
+void msm_dsi_host_adjust_timing_config(struct mipi_dsi_host *host) +{
- struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
- struct drm_display_mode *mode = NULL;
- mode = msm_host->mode;
- mutex_lock(&msm_host->dev_mutex);
- mode->htotal >>= 1;
- mode->hdisplay >>= 1;
- mode->hsync_start >>= 1;
- mode->hsync_end >>= 1;
- mode->clock >>= 1;
I don't think you should alter the mode. Instead, apply the division when you're writing out to hardware. Also, no need to get fancy with a bitshift, just use /2 and trust that the compiler is smart :)
Sean
- mutex_unlock(&msm_host->dev_mutex);
+}
int msm_dsi_host_power_on(struct mipi_dsi_host *host, struct msm_dsi_phy_shared_timings *phy_shared_timings) { diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index 4cb1cb6..8ef1c3d 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -627,6 +627,21 @@ static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge, msm_dsi_host_set_display_mode(host, adjusted_mode); if (is_dual_dsi && other_dsi) msm_dsi_host_set_display_mode(other_dsi->host, adjusted_mode);
- /*
* For dual DSI mode, the current DRM mode has
* the complete width of the panel. Since, the complete
* panel is driven by two DSI controllers, the
* horizontal timings and the pixel clock have to be
* split between the two dsi controllers. Adjust the
* DSI host timing structures accordingly.
*/
- if (is_dual_dsi) {
msm_dsi_host_adjust_timing_config(host);
if (other_dsi)
msm_dsi_host_adjust_timing_config(other_dsi->host);
- }
}
static const struct drm_connector_funcs dsi_mgr_connector_funcs = {
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
Current DSI driver uses two connectors for dual DSI case even though we only have one panel. Fix this by implementing one connector/bridge for dual DSI use case. Use master DSI controllers to register one connector/bridge.
Change-Id: I067b39f3b32eb3aa92d4155d4ca703ca7690645b Signed-off-by: Chandan Uddaraju chandanu@codeaurora.org --- drivers/gpu/drm/msm/dsi/dsi.c | 3 + drivers/gpu/drm/msm/dsi/dsi.h | 1 + drivers/gpu/drm/msm/dsi/dsi_manager.c | 110 ++++++++-------------------------- 3 files changed, 29 insertions(+), 85 deletions(-)
diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c index b744bcc..ff8164c 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.c +++ b/drivers/gpu/drm/msm/dsi/dsi.c @@ -208,6 +208,9 @@ int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev, goto fail; }
+ if (!msm_dsi_manager_validate_current_config(msm_dsi->id)) + goto fail; + msm_dsi->encoder = encoder;
msm_dsi->bridge = msm_dsi_manager_bridge_init(msm_dsi->id); diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h index 4131b47..d487d94 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.h +++ b/drivers/gpu/drm/msm/dsi/dsi.h @@ -100,6 +100,7 @@ struct msm_dsi { void msm_dsi_manager_attach_dsi_device(int id, u32 device_flags); int msm_dsi_manager_register(struct msm_dsi *msm_dsi); void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi); +bool msm_dsi_manager_validate_current_config(u8 id);
/* msm dsi */ static inline bool msm_dsi_device_connected(struct msm_dsi *msm_dsi) diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index 8ef1c3d..5817f59 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -305,67 +305,6 @@ static void dsi_mgr_connector_destroy(struct drm_connector *connector) kfree(dsi_connector); }
-static void dsi_dual_connector_fix_modes(struct drm_connector *connector) -{ - struct drm_display_mode *mode, *m; - - /* Only support left-right mode */ - list_for_each_entry_safe(mode, m, &connector->probed_modes, head) { - mode->clock >>= 1; - mode->hdisplay >>= 1; - mode->hsync_start >>= 1; - mode->hsync_end >>= 1; - mode->htotal >>= 1; - drm_mode_set_name(mode); - } -} - -static int dsi_dual_connector_tile_init( - struct drm_connector *connector, int id) -{ - struct drm_display_mode *mode; - /* Fake topology id */ - char topo_id[8] = {'M', 'S', 'M', 'D', 'U', 'D', 'S', 'I'}; - - if (connector->tile_group) { - DBG("Tile property has been initialized"); - return 0; - } - - /* Use the first mode only for now */ - mode = list_first_entry(&connector->probed_modes, - struct drm_display_mode, - head); - if (!mode) - return -EINVAL; - - connector->tile_group = drm_mode_get_tile_group( - connector->dev, topo_id); - if (!connector->tile_group) - connector->tile_group = drm_mode_create_tile_group( - connector->dev, topo_id); - if (!connector->tile_group) { - pr_err("%s: failed to create tile group\n", __func__); - return -ENOMEM; - } - - connector->has_tile = true; - connector->tile_is_single_monitor = true; - - /* mode has been fixed */ - connector->tile_h_size = mode->hdisplay; - connector->tile_v_size = mode->vdisplay; - - /* Only support left-right mode */ - connector->num_h_tile = 2; - connector->num_v_tile = 1; - - connector->tile_v_loc = 0; - connector->tile_h_loc = (id == DSI_RIGHT) ? 1 : 0; - - return 0; -} - static int dsi_mgr_connector_get_modes(struct drm_connector *connector) { int id = dsi_mgr_connector_get_id(connector); @@ -376,31 +315,15 @@ static int dsi_mgr_connector_get_modes(struct drm_connector *connector) if (!panel) return 0;
- /* Since we have 2 connectors, but only 1 drm_panel in dual DSI mode, - * panel should not attach to any connector. - * Only temporarily attach panel to the current connector here, - * to let panel set mode to this connector. + /* + * In dual DSI mode, we have one connector that can be + * attached to the drm_panel. */ drm_panel_attach(panel, connector); num = drm_panel_get_modes(panel); - drm_panel_detach(panel); if (!num) return 0;
- if (IS_DUAL_DSI()) { - /* report half resolution to user */ - dsi_dual_connector_fix_modes(connector); - ret = dsi_dual_connector_tile_init(connector, id); - if (ret) - return ret; - ret = drm_mode_connector_set_tile_property(connector); - if (ret) { - pr_err("%s: set tile property failed, %d\n", - __func__, ret); - return ret; - } - } - return num; }
@@ -454,8 +377,8 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) if (ret) goto phy_en_fail;
- /* Do nothing with the host if it is DSI 1 in case of dual DSI */ - if (is_dual_dsi && (DSI_1 == id)) + /* Do nothing with the host if it is slave-DSI in case of dual DSI */ + if (is_dual_dsi && (!IS_MASTER_DSI_LINK(id))) return;
ret = msm_dsi_host_power_on(host, &phy_shared_timings[id]); @@ -556,11 +479,11 @@ static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge) return;
/* - * Do nothing with the host if it is DSI 1 in case of dual DSI. + * Do nothing with the host if it is slave-DSI in case of dual 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_dual_dsi && (DSI_1 == id)) + if (is_dual_dsi && (!IS_MASTER_DSI_LINK(id))) goto disable_phy;
if (panel) { @@ -621,7 +544,7 @@ static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge, mode->vsync_end, mode->vtotal, mode->type, mode->flags);
- if (is_dual_dsi && (DSI_1 == id)) + if (is_dual_dsi && (!IS_MASTER_DSI_LINK(id))) return;
msm_dsi_host_set_display_mode(host, adjusted_mode); @@ -704,6 +627,23 @@ struct drm_connector *msm_dsi_manager_connector_init(u8 id) return connector; }
+bool msm_dsi_manager_validate_current_config(u8 id) +{ + bool is_dual_dsi = IS_DUAL_DSI(); + + /* + * For dual DSI, we only have one drm panel. For this + * use case, we register only one bridge/connector. + * Skip bridge/connector initialisation if it is + * slave-DSI for dual DSI configuration. + */ + if (is_dual_dsi && (!IS_MASTER_DSI_LINK(id))) { + DBG("Skip bridge registration for slave DSI->id: %d\n", id); + return false; + } + return true; +} + /* initialize bridge */ struct drm_bridge *msm_dsi_manager_bridge_init(u8 id) {
On Tue, Apr 10, 2018 at 06:16:08PM -0700, Chandan Uddaraju wrote:
Current DSI driver uses two connectors for dual DSI case even though we only have one panel. Fix this by implementing one connector/bridge for dual DSI use case. Use master DSI controllers to register one connector/bridge.
Change-Id: I067b39f3b32eb3aa92d4155d4ca703ca7690645b
Please strip Change-Id when sending patches upstream.
Signed-off-by: Chandan Uddaraju chandanu@codeaurora.org
drivers/gpu/drm/msm/dsi/dsi.c | 3 + drivers/gpu/drm/msm/dsi/dsi.h | 1 + drivers/gpu/drm/msm/dsi/dsi_manager.c | 110 ++++++++-------------------------- 3 files changed, 29 insertions(+), 85 deletions(-)
diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c index b744bcc..ff8164c 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.c +++ b/drivers/gpu/drm/msm/dsi/dsi.c @@ -208,6 +208,9 @@ int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev, goto fail; }
if (!msm_dsi_manager_validate_current_config(msm_dsi->id))
goto fail;
msm_dsi->encoder = encoder;
msm_dsi->bridge = msm_dsi_manager_bridge_init(msm_dsi->id);
diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h index 4131b47..d487d94 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.h +++ b/drivers/gpu/drm/msm/dsi/dsi.h @@ -100,6 +100,7 @@ struct msm_dsi { void msm_dsi_manager_attach_dsi_device(int id, u32 device_flags); int msm_dsi_manager_register(struct msm_dsi *msm_dsi); void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi); +bool msm_dsi_manager_validate_current_config(u8 id);
/* msm dsi */ static inline bool msm_dsi_device_connected(struct msm_dsi *msm_dsi) diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index 8ef1c3d..5817f59 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -305,67 +305,6 @@ static void dsi_mgr_connector_destroy(struct drm_connector *connector) kfree(dsi_connector); }
-static void dsi_dual_connector_fix_modes(struct drm_connector *connector) -{
- struct drm_display_mode *mode, *m;
- /* Only support left-right mode */
- list_for_each_entry_safe(mode, m, &connector->probed_modes, head) {
mode->clock >>= 1;
mode->hdisplay >>= 1;
mode->hsync_start >>= 1;
mode->hsync_end >>= 1;
mode->htotal >>= 1;
drm_mode_set_name(mode);
- }
-}
-static int dsi_dual_connector_tile_init(
struct drm_connector *connector, int id)
-{
- struct drm_display_mode *mode;
- /* Fake topology id */
- char topo_id[8] = {'M', 'S', 'M', 'D', 'U', 'D', 'S', 'I'};
- if (connector->tile_group) {
DBG("Tile property has been initialized");
return 0;
- }
- /* Use the first mode only for now */
- mode = list_first_entry(&connector->probed_modes,
struct drm_display_mode,
head);
- if (!mode)
return -EINVAL;
- connector->tile_group = drm_mode_get_tile_group(
connector->dev, topo_id);
- if (!connector->tile_group)
connector->tile_group = drm_mode_create_tile_group(
connector->dev, topo_id);
- if (!connector->tile_group) {
pr_err("%s: failed to create tile group\n", __func__);
return -ENOMEM;
- }
- connector->has_tile = true;
- connector->tile_is_single_monitor = true;
- /* mode has been fixed */
- connector->tile_h_size = mode->hdisplay;
- connector->tile_v_size = mode->vdisplay;
- /* Only support left-right mode */
- connector->num_h_tile = 2;
- connector->num_v_tile = 1;
- connector->tile_v_loc = 0;
- connector->tile_h_loc = (id == DSI_RIGHT) ? 1 : 0;
- return 0;
-}
static int dsi_mgr_connector_get_modes(struct drm_connector *connector) { int id = dsi_mgr_connector_get_id(connector); @@ -376,31 +315,15 @@ static int dsi_mgr_connector_get_modes(struct drm_connector *connector) if (!panel) return 0;
- /* Since we have 2 connectors, but only 1 drm_panel in dual DSI mode,
* panel should not attach to any connector.
* Only temporarily attach panel to the current connector here,
* to let panel set mode to this connector.
- /*
* In dual DSI mode, we have one connector that can be
*/ drm_panel_attach(panel, connector); num = drm_panel_get_modes(panel);* attached to the drm_panel.
drm_panel_detach(panel); if (!num) return 0;
if (IS_DUAL_DSI()) {
/* report half resolution to user */
dsi_dual_connector_fix_modes(connector);
ret = dsi_dual_connector_tile_init(connector, id);
if (ret)
return ret;
ret = drm_mode_connector_set_tile_property(connector);
if (ret) {
pr_err("%s: set tile property failed, %d\n",
__func__, ret);
return ret;
}
}
return num;
}
@@ -454,8 +377,8 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) if (ret) goto phy_en_fail;
- /* Do nothing with the host if it is DSI 1 in case of dual DSI */
- if (is_dual_dsi && (DSI_1 == id))
- /* Do nothing with the host if it is slave-DSI in case of dual DSI */
- if (is_dual_dsi && (!IS_MASTER_DSI_LINK(id)))
You have extra parentheses here and below.
Sean
return;
ret = msm_dsi_host_power_on(host, &phy_shared_timings[id]); @@ -556,11 +479,11 @@ static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge) return;
/*
* Do nothing with the host if it is DSI 1 in case of dual DSI.
* Do nothing with the host if it is slave-DSI in case of dual 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_dual_dsi && (DSI_1 == id))
if (is_dual_dsi && (!IS_MASTER_DSI_LINK(id))) goto disable_phy;
if (panel) {
@@ -621,7 +544,7 @@ static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge, mode->vsync_end, mode->vtotal, mode->type, mode->flags);
- if (is_dual_dsi && (DSI_1 == id))
if (is_dual_dsi && (!IS_MASTER_DSI_LINK(id))) return;
msm_dsi_host_set_display_mode(host, adjusted_mode);
@@ -704,6 +627,23 @@ struct drm_connector *msm_dsi_manager_connector_init(u8 id) return connector; }
+bool msm_dsi_manager_validate_current_config(u8 id) +{
- bool is_dual_dsi = IS_DUAL_DSI();
- /*
* For dual DSI, we only have one drm panel. For this
* use case, we register only one bridge/connector.
* Skip bridge/connector initialisation if it is
* slave-DSI for dual DSI configuration.
*/
- if (is_dual_dsi && (!IS_MASTER_DSI_LINK(id))) {
DBG("Skip bridge registration for slave DSI->id: %d\n", id);
return false;
- }
- return true;
+}
/* initialize bridge */ struct drm_bridge *msm_dsi_manager_bridge_init(u8 id) { -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
dri-devel@lists.freedesktop.org