diff --git a/msm/dsi/dsi_defs.h b/msm/dsi/dsi_defs.h index 31a90b5bb19c..9b081bd67683 100644 --- a/msm/dsi/dsi_defs.h +++ b/msm/dsi/dsi_defs.h @@ -74,6 +74,8 @@ enum dsi_op_mode { * @DSI_MODE_FLAG_DMS: Seamless transition is dynamic mode switch * @DSI_MODE_FLAG_VRR: Seamless transition is DynamicFPS. * New timing values are sent from DAL. + * @DSI_MODE_FLAG_POMS: + * Seamless transition is dynamic panel operating mode switch */ enum dsi_mode_flags { DSI_MODE_FLAG_SEAMLESS = BIT(0), @@ -81,6 +83,7 @@ enum dsi_mode_flags { DSI_MODE_FLAG_VBLANK_PRE_MODESET = BIT(2), DSI_MODE_FLAG_DMS = BIT(3), DSI_MODE_FLAG_VRR = BIT(4), + DSI_MODE_FLAG_POMS = BIT(5), }; /** @@ -555,12 +558,14 @@ struct dsi_display_mode_priv_info { * @timing: Timing parameters for the panel. * @pixel_clk_khz: Pixel clock in Khz. * @dsi_mode_flags: Flags to signal other drm components via private flags + * @panel_mode: Panel mode * @priv_info: Mode private info */ struct dsi_display_mode { struct dsi_mode_info timing; u32 pixel_clk_khz; u32 dsi_mode_flags; + enum dsi_op_mode panel_mode; struct dsi_display_mode_priv_info *priv_info; }; diff --git a/msm/dsi/dsi_display.c b/msm/dsi/dsi_display.c index e97442f7c535..f4f291e500ad 100644 --- a/msm/dsi/dsi_display.c +++ b/msm/dsi/dsi_display.c @@ -3971,6 +3971,10 @@ static int dsi_display_set_mode_sub(struct dsi_display *display, return -EINVAL; } + if (mode->dsi_mode_flags & DSI_MODE_FLAG_POMS) { + display->config.panel_mode = mode->panel_mode; + display->panel->panel_mode = mode->panel_mode; + } rc = dsi_panel_get_host_cfg_for_mode(display->panel, mode, &display->config); @@ -5039,7 +5043,8 @@ static int dsi_display_ext_get_info(struct drm_connector *connector, info->is_primary = false; info->capabilities |= (MSM_DISPLAY_CAP_VID_MODE | - MSM_DISPLAY_CAP_EDID | MSM_DISPLAY_CAP_HOT_PLUG); + MSM_DISPLAY_CAP_EDID | MSM_DISPLAY_CAP_HOT_PLUG); + info->curr_panel_mode = MSM_DISPLAY_VIDEO_MODE; mutex_unlock(&display->display_lock); return 0; @@ -5403,10 +5408,16 @@ int dsi_display_get_info(struct drm_connector *connector, switch (display->panel->panel_mode) { case DSI_OP_VIDEO_MODE: + info->curr_panel_mode = MSM_DISPLAY_VIDEO_MODE; info->capabilities |= MSM_DISPLAY_CAP_VID_MODE; + if (display->panel->panel_mode_switch_enabled) + info->capabilities |= MSM_DISPLAY_CAP_CMD_MODE; break; case DSI_OP_CMD_MODE: + info->curr_panel_mode = MSM_DISPLAY_CMD_MODE; info->capabilities |= MSM_DISPLAY_CAP_CMD_MODE; + if (display->panel->panel_mode_switch_enabled) + info->capabilities |= MSM_DISPLAY_CAP_VID_MODE; info->is_te_using_watchdog_timer = display->panel->te_using_watchdog_timer | display->sw_te_using_wd; @@ -5704,7 +5715,8 @@ int dsi_display_find_mode(struct dsi_display *display, if (cmp->timing.v_active == m->timing.v_active && cmp->timing.h_active == m->timing.h_active && - cmp->timing.refresh_rate == m->timing.refresh_rate) { + cmp->timing.refresh_rate == m->timing.refresh_rate && + cmp->panel_mode == m->panel_mode) { *out_mode = m; rc = 0; break; @@ -6314,7 +6326,8 @@ int dsi_display_prepare(struct dsi_display *display) goto error; } - if (!display->is_cont_splash_enabled) { + if (!(mode->dsi_mode_flags & DSI_MODE_FLAG_POMS) && + (!display->is_cont_splash_enabled)) { /* * For continuous splash usecase we skip panel * pre prepare since the regulator vote is already @@ -6403,11 +6416,13 @@ int dsi_display_prepare(struct dsi_display *display) goto error_ctrl_link_off; } - rc = dsi_panel_prepare(display->panel); - if (rc) { - pr_err("[%s] panel prepare failed, rc=%d\n", - display->name, rc); - goto error_ctrl_link_off; + if (!(mode->dsi_mode_flags & DSI_MODE_FLAG_POMS)) { + rc = dsi_panel_prepare(display->panel); + if (rc) { + pr_err("[%s] panel prepare failed, rc=%d\n", + display->name, rc); + goto error_ctrl_link_off; + } } } goto error; @@ -6728,7 +6743,8 @@ int dsi_display_enable(struct dsi_display *display) display->name, rc); goto error; } - } else { + } else if (!(display->panel->cur_mode->dsi_mode_flags & + DSI_MODE_FLAG_POMS)){ rc = dsi_panel_enable(display->panel); if (rc) { pr_err("[%s] failed to enable DSI panel, rc=%d\n", @@ -6757,6 +6773,7 @@ int dsi_display_enable(struct dsi_display *display) } if (display->config.panel_mode == DSI_OP_VIDEO_MODE) { + pr_debug("%s:enable video timing eng\n", __func__); rc = dsi_display_vid_engine_enable(display); if (rc) { pr_err("[%s]failed to enable DSI video engine, rc=%d\n", @@ -6764,6 +6781,7 @@ int dsi_display_enable(struct dsi_display *display) goto error_disable_panel; } } else if (display->config.panel_mode == DSI_OP_CMD_MODE) { + pr_debug("%s:enable command timing eng\n", __func__); rc = dsi_display_cmd_engine_enable(display); if (rc) { pr_err("[%s]failed to enable DSI cmd engine, rc=%d\n", @@ -6797,10 +6815,18 @@ int dsi_display_post_enable(struct dsi_display *display) mutex_lock(&display->display_lock); - rc = dsi_panel_post_enable(display->panel); - if (rc) - pr_err("[%s] panel post-enable failed, rc=%d\n", - display->name, rc); + if (display->panel->cur_mode->dsi_mode_flags & DSI_MODE_FLAG_POMS) { + if (display->config.panel_mode == DSI_OP_CMD_MODE) + dsi_panel_mode_switch_to_cmd(display->panel); + + if (display->config.panel_mode == DSI_OP_VIDEO_MODE) + dsi_panel_mode_switch_to_vid(display->panel); + } else { + rc = dsi_panel_post_enable(display->panel); + if (rc) + pr_err("[%s] panel post-enable failed, rc=%d\n", + display->name, rc); + } /* remove the clk vote for CMD mode panels */ if (display->config.panel_mode == DSI_OP_CMD_MODE) @@ -6826,12 +6852,18 @@ int dsi_display_pre_disable(struct dsi_display *display) if (display->config.panel_mode == DSI_OP_CMD_MODE) dsi_display_clk_ctrl(display->dsi_clk_handle, DSI_ALL_CLKS, DSI_CLK_ON); + if (display->poms_pending) { + if (display->config.panel_mode == DSI_OP_CMD_MODE) + dsi_panel_pre_mode_switch_to_video(display->panel); - rc = dsi_panel_pre_disable(display->panel); - if (rc) - pr_err("[%s] panel pre-disable failed, rc=%d\n", - display->name, rc); - + if (display->config.panel_mode == DSI_OP_VIDEO_MODE) + dsi_panel_pre_mode_switch_to_cmd(display->panel); + } else { + rc = dsi_panel_pre_disable(display->panel); + if (rc) + pr_err("[%s] panel pre-disable failed, rc=%d\n", + display->name, rc); + } mutex_unlock(&display->display_lock); return rc; } @@ -6868,11 +6900,12 @@ int dsi_display_disable(struct dsi_display *display) rc = -EINVAL; } - rc = dsi_panel_disable(display->panel); - if (rc) - pr_err("[%s] failed to disable DSI panel, rc=%d\n", - display->name, rc); - + if (!display->poms_pending) { + rc = dsi_panel_disable(display->panel); + if (rc) + pr_err("[%s] failed to disable DSI panel, rc=%d\n", + display->name, rc); + } mutex_unlock(&display->display_lock); SDE_EVT32(SDE_EVTLOG_FUNC_EXIT); return rc; @@ -6911,12 +6944,12 @@ int dsi_display_unprepare(struct dsi_display *display) if (rc) pr_err("[%s] display wake up failed, rc=%d\n", display->name, rc); - - rc = dsi_panel_unprepare(display->panel); - if (rc) - pr_err("[%s] panel unprepare failed, rc=%d\n", - display->name, rc); - + if (!display->poms_pending) { + rc = dsi_panel_unprepare(display->panel); + if (rc) + pr_err("[%s] panel unprepare failed, rc=%d\n", + display->name, rc); + } rc = dsi_display_ctrl_host_disable(display); if (rc) pr_err("[%s] failed to disable DSI host, rc=%d\n", @@ -6949,10 +6982,12 @@ int dsi_display_unprepare(struct dsi_display *display) /* destrory dsi isr set up */ dsi_display_ctrl_isr_configure(display, false); - rc = dsi_panel_post_unprepare(display->panel); - if (rc) - pr_err("[%s] panel post-unprepare failed, rc=%d\n", - display->name, rc); + if (!display->poms_pending) { + rc = dsi_panel_post_unprepare(display->panel); + if (rc) + pr_err("[%s] panel post-unprepare failed, rc=%d\n", + display->name, rc); + } mutex_unlock(&display->display_lock); diff --git a/msm/dsi/dsi_display.h b/msm/dsi/dsi_display.h index 1c9fd5bf752b..c5f30e87f059 100644 --- a/msm/dsi/dsi_display.h +++ b/msm/dsi/dsi_display.h @@ -166,6 +166,7 @@ struct dsi_display_ext_bridge { * @cmdline_topology: Display topology shared from kernel command line. * @cmdline_timing: Display timing shared from kernel command line. * @is_tpg_enabled: TPG state. + * @poms_pending; Flag indicating the pending panel operating mode switch. * @ulps_enabled: ulps state. * @clamp_enabled: clamp state. * @phy_idle_power_off: PHY power state. @@ -226,6 +227,7 @@ struct dsi_display { int cmdline_topology; int cmdline_timing; bool is_tpg_enabled; + bool poms_pending; bool ulps_enabled; bool clamp_enabled; bool phy_idle_power_off; diff --git a/msm/dsi/dsi_drm.c b/msm/dsi/dsi_drm.c index 68fc9015671c..26064cc4c4e2 100644 --- a/msm/dsi/dsi_drm.c +++ b/msm/dsi/dsi_drm.c @@ -71,11 +71,18 @@ static void convert_to_dsi_mode(const struct drm_display_mode *drm_mode, dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_DMS; if (msm_is_mode_seamless_vrr(drm_mode)) dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR; + if (msm_is_mode_seamless_poms(drm_mode)) + dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_POMS; dsi_mode->timing.h_sync_polarity = !!(drm_mode->flags & DRM_MODE_FLAG_PHSYNC); dsi_mode->timing.v_sync_polarity = !!(drm_mode->flags & DRM_MODE_FLAG_PVSYNC); + + if (drm_mode->flags & DRM_MODE_FLAG_VID_MODE_PANEL) + dsi_mode->panel_mode = DSI_OP_VIDEO_MODE; + if (drm_mode->flags & DRM_MODE_FLAG_CMD_MODE_PANEL) + dsi_mode->panel_mode = DSI_OP_CMD_MODE; } void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode, @@ -113,12 +120,19 @@ void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode, drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_DMS; if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_VRR) drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_VRR; + if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_POMS) + drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_POMS; if (dsi_mode->timing.h_sync_polarity) drm_mode->flags |= DRM_MODE_FLAG_PHSYNC; if (dsi_mode->timing.v_sync_polarity) drm_mode->flags |= DRM_MODE_FLAG_PVSYNC; + if (dsi_mode->panel_mode == DSI_OP_VIDEO_MODE) + drm_mode->flags |= DRM_MODE_FLAG_VID_MODE_PANEL; + if (dsi_mode->panel_mode == DSI_OP_CMD_MODE) + drm_mode->flags |= DRM_MODE_FLAG_CMD_MODE_PANEL; + drm_mode_set_name(drm_mode); } @@ -233,8 +247,18 @@ static void dsi_bridge_disable(struct drm_bridge *bridge) } display = c_bridge->display; - if (display && display->drm_conn) - sde_connector_helper_bridge_disable(display->drm_conn); + if (display && display->drm_conn) { + if (bridge->encoder->crtc->state->adjusted_mode.private_flags & + MSM_MODE_FLAG_SEAMLESS_POMS) { + display->poms_pending = true; + /* Disable ESD thread, during panel mode switch */ + sde_connector_schedule_status_work(display->drm_conn, + false); + } else { + display->poms_pending = false; + sde_connector_helper_bridge_disable(display->drm_conn); + } + } rc = dsi_display_pre_disable(c_bridge->display); if (rc) { @@ -363,9 +387,17 @@ static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge, cur_mode = crtc_state->crtc->mode; + /* No panel mode switch when drm pipeline is changing */ + if ((dsi_mode.panel_mode != cur_dsi_mode.panel_mode) && + (!(dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_VRR)) && + (!crtc_state->active_changed || + display->is_cont_splash_enabled)) + dsi_mode.dsi_mode_flags |= DSI_MODE_FLAG_POMS; + /* No DMS/VRR when drm pipeline is changing */ if (!drm_mode_equal(&cur_mode, adjusted_mode) && (!(dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_VRR)) && + (!(dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_POMS)) && (!crtc_state->active_changed || display->is_cont_splash_enabled)) dsi_mode.dsi_mode_flags |= DSI_MODE_FLAG_DMS; diff --git a/msm/dsi/dsi_panel.c b/msm/dsi/dsi_panel.c index 50da38013744..14d26201b1bf 100644 --- a/msm/dsi/dsi_panel.c +++ b/msm/dsi/dsi_panel.c @@ -1365,6 +1365,7 @@ static int dsi_panel_parse_panel_mode(struct dsi_panel *panel) { int rc = 0; struct dsi_parser_utils *utils = &panel->utils; + bool panel_mode_switch_enabled; enum dsi_op_mode panel_mode; const char *mode; @@ -1383,7 +1384,13 @@ static int dsi_panel_parse_panel_mode(struct dsi_panel *panel) goto error; } - if (panel_mode == DSI_OP_VIDEO_MODE) { + panel_mode_switch_enabled = utils->read_bool(utils->data, + "qcom,mdss-dsi-panel-mode-switch"); + + pr_info("%s: panel operating mode switch feature %s\n", __func__, + (panel_mode_switch_enabled ? "enabled" : "disabled")); + + if (panel_mode == DSI_OP_VIDEO_MODE || panel_mode_switch_enabled) { rc = dsi_panel_parse_video_host_config(&panel->video_config, utils, panel->name); @@ -1394,7 +1401,7 @@ static int dsi_panel_parse_panel_mode(struct dsi_panel *panel) } } - if (panel_mode == DSI_OP_CMD_MODE) { + if (panel_mode == DSI_OP_CMD_MODE || panel_mode_switch_enabled) { rc = dsi_panel_parse_cmd_host_config(&panel->cmd_config, utils, panel->name); @@ -1406,6 +1413,7 @@ static int dsi_panel_parse_panel_mode(struct dsi_panel *panel) } panel->panel_mode = panel_mode; + panel->panel_mode_switch_enabled = panel_mode_switch_enabled; error: return rc; } @@ -2663,6 +2671,33 @@ static int dsi_panel_parse_partial_update_caps(struct dsi_display_mode *mode, return rc; } +static int dsi_panel_parse_panel_mode_caps(struct dsi_display_mode *mode, + struct dsi_parser_utils *utils) +{ + bool vid_mode_support, cmd_mode_support; + + if (!mode || !mode->priv_info) { + pr_err("invalid arguments\n"); + return -EINVAL; + } + + vid_mode_support = utils->read_bool(utils->data, + "qcom,mdss-dsi-video-mode"); + + cmd_mode_support = utils->read_bool(utils->data, + "qcom,mdss-dsi-cmd-mode"); + + if (cmd_mode_support) + mode->panel_mode = DSI_OP_CMD_MODE; + else if (vid_mode_support) + mode->panel_mode = DSI_OP_VIDEO_MODE; + else + return -EINVAL; + + return 0; +}; + + static int dsi_panel_parse_dms_info(struct dsi_panel *panel) { int dms_enabled; @@ -3349,6 +3384,17 @@ int dsi_panel_get_mode(struct dsi_panel *panel, rc = dsi_panel_parse_partial_update_caps(mode, utils); if (rc) pr_err("failed to partial update caps, rc=%d\n", rc); + + if (panel->panel_mode_switch_enabled) { + rc = dsi_panel_parse_panel_mode_caps(mode, utils); + if (rc) { + pr_err("PMS: failed to parse panel mode\n"); + rc = 0; + mode->panel_mode = panel->panel_mode; + } + } else { + mode->panel_mode = panel->panel_mode; + } } goto done; @@ -3707,6 +3753,86 @@ int dsi_panel_send_roi_dcs(struct dsi_panel *panel, int ctrl_idx, return rc; } +int dsi_panel_pre_mode_switch_to_video(struct dsi_panel *panel) +{ + int rc = 0; + + if (!panel) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + mutex_lock(&panel->panel_lock); + + rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_CMD_TO_VID_SWITCH); + if (rc) + pr_err("[%s] failed to send DSI_CMD_SET_CMD_TO_VID_SWITCH cmds, rc=%d\n", + panel->name, rc); + + mutex_unlock(&panel->panel_lock); + return rc; +} + +int dsi_panel_pre_mode_switch_to_cmd(struct dsi_panel *panel) +{ + int rc = 0; + + if (!panel) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + mutex_lock(&panel->panel_lock); + + rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_VID_TO_CMD_SWITCH); + if (rc) + pr_err("[%s] failed to send DSI_CMD_SET_CMD_TO_VID_SWITCH cmds, rc=%d\n", + panel->name, rc); + + mutex_unlock(&panel->panel_lock); + return rc; +} + +int dsi_panel_mode_switch_to_cmd(struct dsi_panel *panel) +{ + int rc = 0; + + if (!panel) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + mutex_lock(&panel->panel_lock); + + rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_POST_VID_TO_CMD_SWITCH); + if (rc) + pr_err("[%s] failed to send DSI_CMD_SET_CMD_TO_VID_SWITCH cmds, rc=%d\n", + panel->name, rc); + + mutex_unlock(&panel->panel_lock); + return rc; +} + +int dsi_panel_mode_switch_to_vid(struct dsi_panel *panel) +{ + int rc = 0; + + if (!panel) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + mutex_lock(&panel->panel_lock); + + rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_POST_CMD_TO_VID_SWITCH); + if (rc) + pr_err("[%s] failed to send DSI_CMD_SET_CMD_TO_VID_SWITCH cmds, rc=%d\n", + panel->name, rc); + + mutex_unlock(&panel->panel_lock); + return rc; +} + int dsi_panel_switch(struct dsi_panel *panel) { int rc = 0; diff --git a/msm/dsi/dsi_panel.h b/msm/dsi/dsi_panel.h index a2dcebb10761..85be58839a9c 100644 --- a/msm/dsi/dsi_panel.h +++ b/msm/dsi/dsi_panel.h @@ -156,6 +156,7 @@ struct dsi_panel { struct dsi_video_engine_cfg video_config; struct dsi_cmd_engine_cfg cmd_config; enum dsi_op_mode panel_mode; + bool panel_mode_switch_enabled; struct dsi_dfps_capabilities dfps_caps; struct dsi_panel_phy_props phy_props; @@ -277,6 +278,11 @@ int dsi_panel_send_qsync_off_dcs(struct dsi_panel *panel, int dsi_panel_send_roi_dcs(struct dsi_panel *panel, int ctrl_idx, struct dsi_rect *roi); +int dsi_panel_pre_mode_switch_to_video(struct dsi_panel *panel); +int dsi_panel_pre_mode_switch_to_cmd(struct dsi_panel *panel); +int dsi_panel_mode_switch_to_cmd(struct dsi_panel *panel); +int dsi_panel_mode_switch_to_vid(struct dsi_panel *panel); + int dsi_panel_switch(struct dsi_panel *panel); int dsi_panel_post_switch(struct dsi_panel *panel); diff --git a/msm/msm_atomic.c b/msm/msm_atomic.c index 7eb55eee678b..180efcccdbde 100644 --- a/msm/msm_atomic.c +++ b/msm/msm_atomic.c @@ -86,7 +86,8 @@ static inline bool _msm_seamless_for_crtc(struct drm_atomic_state *state, int conn_cnt = 0; if (msm_is_mode_seamless(&crtc_state->mode) || - msm_is_mode_seamless_vrr(&crtc_state->adjusted_mode)) + msm_is_mode_seamless_vrr(&crtc_state->adjusted_mode) || + msm_is_mode_seamless_poms(&crtc_state->adjusted_mode)) return true; if (msm_is_mode_seamless_dms(&crtc_state->adjusted_mode) && !enable) diff --git a/msm/msm_drv.h b/msm/msm_drv.h index a0937dd65f7c..3f03d9e1bf40 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -248,6 +248,18 @@ enum msm_display_caps { MSM_DISPLAY_CAP_MST_MODE = BIT(5), }; +/** + * enum panel_mode - panel operation mode + * @MSM_DISPLAY_VIDEO_MODE: video mode panel + * @MSM_DISPLAY_CMD_MODE: Command mode panel + * @MODE_MAX: + */ +enum panel_op_mode { + MSM_DISPLAY_VIDEO_MODE = 0, + MSM_DISPLAY_CMD_MODE, + MSM_DISPLAY_MODE_MAX, +}; + /** * enum msm_event_wait - type of HW events to wait for * @MSM_ENC_COMMIT_DONE - wait for the driver to flush the registers to HW @@ -484,7 +496,7 @@ struct msm_mode_info { struct msm_display_info { int intf_type; uint32_t capabilities; - + enum panel_op_mode curr_panel_mode; uint32_t num_of_h_tiles; uint32_t h_tile_instance[MAX_H_TILES_PER_DISPLAY]; diff --git a/msm/msm_kms.h b/msm/msm_kms.h index e062b07715ac..3fce5ecc13b6 100644 --- a/msm/msm_kms.h +++ b/msm/msm_kms.h @@ -38,6 +38,8 @@ #define MSM_MODE_FLAG_SEAMLESS_DMS (1<<2) /* Request to switch the fps */ #define MSM_MODE_FLAG_SEAMLESS_VRR (1<<3) +/* Request to switch the panel mode */ +#define MSM_MODE_FLAG_SEAMLESS_POMS (1<<4) /* As there are different display controller blocks depending on the * snapdragon version, the kms support is split out and the appropriate @@ -212,6 +214,13 @@ static inline bool msm_is_mode_seamless_vrr(const struct drm_display_mode *mode) : false; } +static inline bool msm_is_mode_seamless_poms( + const struct drm_display_mode *mode) +{ + return mode ? (mode->private_flags & MSM_MODE_FLAG_SEAMLESS_POMS) + : false; +} + static inline bool msm_needs_vblank_pre_modeset( const struct drm_display_mode *mode) { diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index b63c1183505c..1516ab2116d0 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -1795,8 +1795,8 @@ int sde_crtc_get_secure_transition_ops(struct drm_crtc *crtc, */ drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) { - post_commit |= sde_encoder_check_mode(encoder, - MSM_DISPLAY_CAP_VID_MODE); + post_commit |= sde_encoder_check_curr_mode(encoder, + MSM_DISPLAY_VIDEO_MODE); } SDE_DEBUG("crtc%d: secure_level %d old_valid_fb %d post_commit %d\n", @@ -3136,8 +3136,8 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc, _sde_crtc_dest_scaler_setup(crtc); /* cancel the idle notify delayed work */ - if (sde_encoder_check_mode(sde_crtc->mixers[0].encoder, - MSM_DISPLAY_CAP_VID_MODE) && + if (sde_encoder_check_curr_mode(sde_crtc->mixers[0].encoder, + MSM_DISPLAY_VIDEO_MODE) && kthread_cancel_delayed_work_sync(&sde_crtc->idle_notify_work)) SDE_DEBUG("idle notify work cancelled\n"); @@ -3253,8 +3253,9 @@ static void sde_crtc_atomic_flush(struct drm_crtc *crtc, _sde_crtc_wait_for_fences(crtc); /* schedule the idle notify delayed work */ - if (idle_time && sde_encoder_check_mode(sde_crtc->mixers[0].encoder, - MSM_DISPLAY_CAP_VID_MODE)) { + if (idle_time && sde_encoder_check_curr_mode( + sde_crtc->mixers[0].encoder, + MSM_DISPLAY_VIDEO_MODE)) { kthread_queue_delayed_work(&event_thread->worker, &sde_crtc->idle_notify_work, msecs_to_jiffies(idle_time)); @@ -4322,8 +4323,8 @@ static int _sde_crtc_check_secure_state_smmu_translation(struct drm_crtc *crtc, drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) { - is_video_mode |= sde_encoder_check_mode(encoder, - MSM_DISPLAY_CAP_VID_MODE); + is_video_mode |= sde_encoder_check_curr_mode(encoder, + MSM_DISPLAY_VIDEO_MODE); } /* @@ -5007,8 +5008,9 @@ static int _sde_crtc_get_output_fence(struct drm_crtc *crtc, cstate = to_sde_crtc_state(state); drm_for_each_encoder_mask(encoder, crtc->dev, state->encoder_mask) { - is_vid |= sde_encoder_check_mode(encoder, - MSM_DISPLAY_CAP_VID_MODE); + if (sde_encoder_check_curr_mode(encoder, + MSM_DISPLAY_VIDEO_MODE)) + is_vid = true; if (is_vid) break; } diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index be393d993178..3191e06a370b 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -170,6 +170,8 @@ enum sde_enc_rc_states { * @te_source: vsync source pin information * @num_phys_encs: Actual number of physical encoders contained. * @phys_encs: Container of physical encoders managed. + * @phys_vid_encs: Video physical encoders for panel mode switch. + * @phys_cmd_encs: Command physical encoders for panel mode switch. * @cur_master: Pointer to the current master in this mode. Optimization * Only valid after enable. Cleared as disable. * @hw_pp Handle to the pingpong blocks used for the display. No. @@ -236,6 +238,8 @@ struct sde_encoder_virt { unsigned int num_phys_encs; struct sde_encoder_phys *phys_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL]; + struct sde_encoder_phys *phys_vid_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL]; + struct sde_encoder_phys *phys_cmd_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL]; struct sde_encoder_phys *cur_master; struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; @@ -677,8 +681,16 @@ void sde_encoder_destroy(struct drm_encoder *drm_enc) sde_rsc_client_destroy(sde_enc->rsc_client); for (i = 0; i < sde_enc->num_phys_encs; i++) { - struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; + struct sde_encoder_phys *phys; + phys = sde_enc->phys_vid_encs[i]; + if (phys && phys->ops.destroy) { + phys->ops.destroy(phys); + --sde_enc->num_phys_encs; + sde_enc->phys_encs[i] = NULL; + } + + phys = sde_enc->phys_cmd_encs[i]; if (phys && phys->ops.destroy) { phys->ops.destroy(phys); --sde_enc->num_phys_encs; @@ -1687,7 +1699,7 @@ static void _sde_encoder_update_vsync_source(struct sde_encoder_virt *sde_enc, return; } - if (disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) { + if (sde_encoder_check_curr_mode(&sde_enc->base, MSM_DISPLAY_CMD_MODE)) { if (is_dummy) vsync_source = SDE_VSYNC_SOURCE_WD_TIMER_0 - sde_enc->te_source; @@ -1892,9 +1904,9 @@ static int _sde_encoder_update_rsc_client( if (sde_encoder_in_clone_mode(drm_enc) || !disp_info->is_primary || (disp_info->is_primary && qsync_mode)) rsc_state = enable ? SDE_RSC_CLK_STATE : SDE_RSC_IDLE_STATE; - else if (disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) + else if (sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_CMD_MODE)) rsc_state = enable ? SDE_RSC_CMD_STATE : SDE_RSC_IDLE_STATE; - else if (disp_info->capabilities & MSM_DISPLAY_CAP_VID_MODE) + else if (sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_VIDEO_MODE)) rsc_state = enable ? SDE_RSC_VID_STATE : SDE_RSC_IDLE_STATE; SDE_EVT32(rsc_state, qsync_mode); @@ -2032,14 +2044,15 @@ static int _sde_encoder_resource_control_helper(struct drm_encoder *drm_enc, struct sde_kms *sde_kms; struct sde_encoder_virt *sde_enc; int rc; - bool is_cmd_mode, is_primary; + bool is_cmd_mode = false, is_primary; sde_enc = to_sde_encoder_virt(drm_enc); priv = drm_enc->dev->dev_private; sde_kms = to_sde_kms(priv->kms); - is_cmd_mode = sde_enc->disp_info.capabilities & - MSM_DISPLAY_CAP_CMD_MODE; + if (sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_CMD_MODE)) + is_cmd_mode = true; + is_primary = sde_enc->disp_info.is_primary; SDE_DEBUG_ENC(sde_enc, "enable:%d\n", enable); @@ -2612,9 +2625,8 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc, } sde_enc = to_sde_encoder_virt(drm_enc); priv = drm_enc->dev->dev_private; - is_vid_mode = sde_enc->disp_info.capabilities & - MSM_DISPLAY_CAP_VID_MODE; - + if (sde_encoder_check_curr_mode(&sde_enc->base, MSM_DISPLAY_VIDEO_MODE)) + is_vid_mode = true; /* * when idle_pc is not supported, process only KICKOFF, STOP and MODESET * events and return early for other events (ie wb display). @@ -2673,6 +2685,32 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc, return ret; } +static void sde_encoder_virt_mode_switch(enum sde_intf_mode intf_mode, + struct sde_encoder_virt *sde_enc, + struct drm_display_mode *adj_mode) +{ + int i = 0; + + if (intf_mode == INTF_MODE_CMD) { + for (i = 0; i < sde_enc->num_phys_encs; i++) + sde_enc->phys_encs[i] = sde_enc->phys_vid_encs[i]; + sde_enc->disp_info.curr_panel_mode = MSM_DISPLAY_VIDEO_MODE; + SDE_DEBUG_ENC(sde_enc, "switch to video physical encoder\n"); + SDE_EVT32(DRMID(&sde_enc->base), intf_mode, + msm_is_mode_seamless_poms(adj_mode), + SDE_EVTLOG_FUNC_CASE1); + } + if (intf_mode == INTF_MODE_VIDEO) { + for (i = 0; i < sde_enc->num_phys_encs; i++) + sde_enc->phys_encs[i] = sde_enc->phys_cmd_encs[i]; + sde_enc->disp_info.curr_panel_mode = MSM_DISPLAY_CMD_MODE; + SDE_EVT32(DRMID(&sde_enc->base), intf_mode, + msm_is_mode_seamless_poms(adj_mode), + SDE_EVTLOG_FUNC_CASE2); + SDE_DEBUG_ENC(sde_enc, "switch to command physical encoder\n"); + } +} + static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc, struct drm_display_mode *mode, struct drm_display_mode *adj_mode) @@ -2686,6 +2724,8 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc, struct sde_connector *sde_conn = NULL; struct sde_rm_hw_iter dsc_iter, pp_iter; struct sde_rm_hw_request request_hw; + enum sde_intf_mode intf_mode; + int i = 0, ret; if (!drm_enc) { @@ -2742,6 +2782,11 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc, return; } } + intf_mode = sde_encoder_get_intf_mode(drm_enc); + + /* Switch pysical encoder */ + if (msm_is_mode_seamless_poms(adj_mode)) + sde_encoder_virt_mode_switch(intf_mode, sde_enc, adj_mode); /* release resources before seamless mode change */ if (msm_is_mode_seamless_dms(adj_mode)) { @@ -3151,8 +3196,8 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) phys->ops.enable(phys); } - if (sde_enc->misr_enable && (sde_enc->disp_info.capabilities & - MSM_DISPLAY_CAP_VID_MODE) && phys->ops.setup_misr) + if (sde_enc->misr_enable && phys->ops.setup_misr && + (sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_VIDEO_MODE))) phys->ops.setup_misr(phys, true, sde_enc->misr_frame_count); } @@ -3509,7 +3554,8 @@ static void sde_encoder_frame_done_callback( { struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); unsigned int i; - bool trigger = true, is_cmd_mode; + bool trigger = true; + bool is_cmd_mode = false; enum sde_rm_topology_name topology = SDE_RM_TOPOLOGY_NONE; if (!drm_enc || !sde_enc->cur_master) { @@ -3520,8 +3566,8 @@ static void sde_encoder_frame_done_callback( sde_enc->crtc_frame_event_cb_data.connector = sde_enc->cur_master->connector; - is_cmd_mode = sde_enc->disp_info.capabilities & - MSM_DISPLAY_CAP_CMD_MODE; + if (sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_CMD_MODE)) + is_cmd_mode = true; if (event & (SDE_ENCODER_FRAME_EVENT_DONE | SDE_ENCODER_FRAME_EVENT_ERROR @@ -3840,8 +3886,8 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc) return; } - is_vid_mode = sde_enc->disp_info.capabilities & - MSM_DISPLAY_CAP_VID_MODE; + if (sde_encoder_check_curr_mode(&sde_enc->base, MSM_DISPLAY_VIDEO_MODE)) + is_vid_mode = true; /* don't perform flush/start operations for slave encoders */ for (i = 0; i < sde_enc->num_phys_encs; i++) { @@ -4047,7 +4093,7 @@ static void _sde_encoder_update_master(struct drm_encoder *drm_enc, } } -bool sde_encoder_check_mode(struct drm_encoder *drm_enc, u32 mode) +bool sde_encoder_check_curr_mode(struct drm_encoder *drm_enc, u32 mode) { struct sde_encoder_virt *sde_enc; struct msm_display_info *disp_info; @@ -4060,7 +4106,7 @@ bool sde_encoder_check_mode(struct drm_encoder *drm_enc, u32 mode) sde_enc = to_sde_encoder_virt(drm_enc); disp_info = &sde_enc->disp_info; - return (disp_info->capabilities & mode); + return (disp_info->curr_panel_mode == mode); } void sde_encoder_trigger_kickoff_pending(struct drm_encoder *drm_enc) @@ -4094,8 +4140,9 @@ void sde_encoder_trigger_kickoff_pending(struct drm_encoder *drm_enc) /* update only for command mode primary ctl */ if ((phys == sde_enc->cur_master) && - (disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) - && ctl->ops.trigger_pending) + (sde_encoder_check_curr_mode(drm_enc, + MSM_DISPLAY_CMD_MODE)) + && ctl->ops.trigger_pending) ctl->ops.trigger_pending(ctl); } } @@ -4583,7 +4630,7 @@ int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc, if (sde_enc->cur_master && sde_enc->cur_master->connector && - disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) + sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_CMD_MODE)) sde_enc->frame_trigger_mode = sde_connector_get_property( sde_enc->cur_master->connector->state, CONNECTOR_PROP_CMD_FRAME_TRIGGER_MODE); @@ -5115,11 +5162,12 @@ static void sde_encoder_early_unregister(struct drm_encoder *encoder) } static int sde_encoder_virt_add_phys_encs( - u32 display_caps, + struct msm_display_info *disp_info, struct sde_encoder_virt *sde_enc, struct sde_enc_phys_init_params *params) { struct sde_encoder_phys *enc = NULL; + u32 display_caps = disp_info->capabilities; SDE_DEBUG_ENC(sde_enc, "\n"); @@ -5143,8 +5191,7 @@ static int sde_encoder_virt_add_phys_encs( return !enc ? -EINVAL : PTR_ERR(enc); } - sde_enc->phys_encs[sde_enc->num_phys_encs] = enc; - ++sde_enc->num_phys_encs; + sde_enc->phys_vid_encs[sde_enc->num_phys_encs] = enc; } if (display_caps & MSM_DISPLAY_CAP_CMD_MODE) { @@ -5155,11 +5202,18 @@ static int sde_encoder_virt_add_phys_encs( PTR_ERR(enc)); return !enc ? -EINVAL : PTR_ERR(enc); } - - sde_enc->phys_encs[sde_enc->num_phys_encs] = enc; - ++sde_enc->num_phys_encs; + sde_enc->phys_cmd_encs[sde_enc->num_phys_encs] = enc; } + if (disp_info->curr_panel_mode == MSM_DISPLAY_VIDEO_MODE) + sde_enc->phys_encs[sde_enc->num_phys_encs] = + sde_enc->phys_vid_encs[sde_enc->num_phys_encs]; + else + sde_enc->phys_encs[sde_enc->num_phys_encs] = + sde_enc->phys_cmd_encs[sde_enc->num_phys_encs]; + + ++sde_enc->num_phys_encs; + return 0; } @@ -5308,7 +5362,7 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc, &phys_params); else ret = sde_encoder_virt_add_phys_encs( - disp_info->capabilities, + disp_info, sde_enc, &phys_params); if (ret) @@ -5318,12 +5372,19 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc, } for (i = 0; i < sde_enc->num_phys_encs; i++) { - struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; + struct sde_encoder_phys *vid_phys = sde_enc->phys_vid_encs[i]; + struct sde_encoder_phys *cmd_phys = sde_enc->phys_cmd_encs[i]; - if (phys) { - atomic_set(&phys->vsync_cnt, 0); - atomic_set(&phys->underrun_cnt, 0); + if (vid_phys) { + atomic_set(&vid_phys->vsync_cnt, 0); + atomic_set(&vid_phys->underrun_cnt, 0); } + + if (cmd_phys) { + atomic_set(&cmd_phys->vsync_cnt, 0); + atomic_set(&cmd_phys->underrun_cnt, 0); + } + } mutex_unlock(&sde_enc->enc_lock); @@ -5398,7 +5459,7 @@ struct drm_encoder *sde_encoder_init( sde_enc->rsc_client = NULL; } - if (disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) { + if (disp_info->curr_panel_mode == MSM_DISPLAY_CMD_MODE) { ret = _sde_encoder_input_handler(sde_enc); if (ret) SDE_ERROR( diff --git a/msm/sde/sde_encoder.h b/msm/sde/sde_encoder.h index a03ec6df1239..83c43d5b6d09 100644 --- a/msm/sde/sde_encoder.h +++ b/msm/sde/sde_encoder.h @@ -201,12 +201,12 @@ void sde_encoder_virt_restore(struct drm_encoder *encoder); bool sde_encoder_is_dsc_merge(struct drm_encoder *drm_enc); /** - * sde_encoder_check_mode - check if given mode is supported or not + * sde_encoder_check_curr_mode - check if given mode is supported or not * @drm_enc: Pointer to drm encoder object * @mode: Mode to be checked * @Return: true if it is cmd mode */ -bool sde_encoder_check_mode(struct drm_encoder *drm_enc, u32 mode); +bool sde_encoder_check_curr_mode(struct drm_encoder *drm_enc, u32 mode); /** * sde_encoder_init - initialize virtual encoder object