From b8e0c667eb237a62687d963343445114d4b6e109 Mon Sep 17 00:00:00 2001 From: Guchun Chen Date: Thu, 17 May 2018 12:28:55 +0800 Subject: [PATCH] drm: sde: Check commit's validity when starting splash handoff User maybe send null commit with invalid frame buffer to kernel, so early splash's handoff should wait for one valid commit coming with active fb. CRs-Fixed: 2225630 Change-Id: I5ef0809791d697be264787ebe7a6ec58704cb310 Signed-off-by: Guchun Chen --- drivers/gpu/drm/msm/sde/sde_connector.c | 15 ++- drivers/gpu/drm/msm/sde/sde_kms.c | 8 +- drivers/gpu/drm/msm/sde/sde_splash.c | 134 +++++++++++++++++++----- drivers/gpu/drm/msm/sde/sde_splash.h | 24 ++--- 4 files changed, 137 insertions(+), 44 deletions(-) diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index f74a682c5f04..1bc3d0a926eb 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.c +++ b/drivers/gpu/drm/msm/sde/sde_connector.c @@ -572,6 +572,7 @@ void sde_connector_complete_commit(struct drm_connector *connector) { struct drm_device *dev; struct msm_drm_private *priv; + struct sde_connector *c_conn; if (!connector) { SDE_ERROR("invalid connector\n"); @@ -584,11 +585,17 @@ void sde_connector_complete_commit(struct drm_connector *connector) /* signal connector's retire fence */ sde_fence_signal(&to_sde_connector(connector)->retire_fence, 0); - /* after first vsync comes, - * early splash resource should start to be released. + /* + * After LK totally exits, LK's early splash resource + * should be released. */ - if (sde_splash_get_lk_complete_status(priv->kms)) - sde_splash_free_resource(priv->kms, &priv->phandle); + if (sde_splash_get_lk_complete_status(priv->kms)) { + c_conn = to_sde_connector(connector); + + sde_splash_free_resource(priv->kms, &priv->phandle, + c_conn->connector_type, + c_conn->display); + } } diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index 1da8b5b4ff10..86a5c23b5258 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -345,10 +345,10 @@ static void sde_kms_prepare_commit(struct msm_kms *kms, if (sde_kms->splash_info.handoff && sde_kms->splash_info.display_splash_enabled) - sde_splash_lk_stop_splash(kms); - else - sde_power_resource_enable(&priv->phandle, - sde_kms->core_client, true); + sde_splash_lk_stop_splash(kms, state); + + sde_power_resource_enable(&priv->phandle, + sde_kms->core_client, true); } static void sde_kms_commit(struct msm_kms *kms, diff --git a/drivers/gpu/drm/msm/sde/sde_splash.c b/drivers/gpu/drm/msm/sde/sde_splash.c index f124bd7d5904..9c3964e99c1f 100644 --- a/drivers/gpu/drm/msm/sde/sde_splash.c +++ b/drivers/gpu/drm/msm/sde/sde_splash.c @@ -313,6 +313,15 @@ static void _sde_splash_sent_pipe_update_uevent(struct sde_kms *sde_kms) kfree(event_string); } +static void _sde_splash_get_connector_ref_cnt(struct sde_splash_info *sinfo, + u32 *hdmi_cnt, u32 *dsi_cnt) +{ + mutex_lock(&sde_splash_lock); + *hdmi_cnt = sinfo->hdmi_connector_cnt; + *dsi_cnt = sinfo->dsi_connector_cnt; + mutex_unlock(&sde_splash_lock); +} + static int _sde_splash_free_module_resource(struct msm_mmu *mmu, struct sde_splash_info *sinfo) { @@ -339,6 +348,29 @@ static int _sde_splash_free_module_resource(struct msm_mmu *mmu, return 0; } +static bool _sde_splash_validate_commit(struct sde_kms *sde_kms, + struct drm_atomic_state *state) +{ + int i, nplanes; + struct drm_plane *plane; + struct drm_device *dev = sde_kms->dev; + + nplanes = dev->mode_config.num_total_plane; + + for (i = 0; i < nplanes; i++) { + plane = state->planes[i]; + + /* + * As plane state has been swapped, we need to check + * fb in state->planes, not fb in state->plane_state. + */ + if (plane && plane->fb) + return true; + } + + return false; +} + __ref int sde_splash_init(struct sde_power_handle *phandle, struct msm_kms *kms) { struct sde_kms *sde_kms; @@ -369,8 +401,7 @@ __ref int sde_splash_init(struct sde_power_handle *phandle, struct msm_kms *kms) sde_power_data_bus_bandwidth_ctrl(phandle, sde_kms->core_client, false); - ret = -EINVAL; - break; + return -EINVAL; } } @@ -716,11 +747,17 @@ bool sde_splash_get_lk_complete_status(struct msm_kms *kms) } int sde_splash_free_resource(struct msm_kms *kms, - struct sde_power_handle *phandle) + struct sde_power_handle *phandle, + int connector_type, void *display) { struct sde_kms *sde_kms; struct sde_splash_info *sinfo; struct msm_mmu *mmu; + struct dsi_display *dsi_display = display; + int ret = 0; + int hdmi_conn_count = 0; + int dsi_conn_count = 0; + static const char *last_commit_display_type = "unknown"; if (!phandle || !kms) { SDE_ERROR("invalid phandle/kms.\n"); @@ -734,41 +771,88 @@ int sde_splash_free_resource(struct msm_kms *kms, return -EINVAL; } + /* Get connector number where the early splash in on. */ + _sde_splash_get_connector_ref_cnt(sinfo, &hdmi_conn_count, + &dsi_conn_count); + mutex_lock(&sde_splash_lock); if (!sinfo->handoff) { mutex_unlock(&sde_splash_lock); return 0; } - mmu = sde_kms->aspace[0]->mmu; - if (!mmu) { - mutex_unlock(&sde_splash_lock); - return -EINVAL; - } + /* + * Start to free all LK's resource till user commit happens + * on each display which early splash is enabled on. + */ + if (hdmi_conn_count == 0 && dsi_conn_count == 0) { + mmu = sde_kms->aspace[0]->mmu; + if (!mmu) { + mutex_unlock(&sde_splash_lock); + return -EINVAL; + } - /* free HDMI's, DSI's and early camera's reserved memory */ - _sde_splash_free_module_resource(mmu, sinfo); + /* free HDMI's, DSI's and early camera's reserved memory */ + _sde_splash_free_module_resource(mmu, sinfo); - _sde_splash_destroy_splash_node(sinfo); + _sde_splash_destroy_splash_node(sinfo); - /* free lk_pool heap memory */ - _sde_splash_free_bootup_memory_to_system(sinfo->lk_pool_paddr, + /* free lk_pool heap memory */ + _sde_splash_free_bootup_memory_to_system(sinfo->lk_pool_paddr, sinfo->lk_pool_size); - /* withdraw data bus vote */ - sde_power_data_bus_bandwidth_ctrl(phandle, - sde_kms->core_client, false); + /* withdraw data bus vote */ + sde_power_data_bus_bandwidth_ctrl(phandle, + sde_kms->core_client, false); - /* send uevent to notify user to recycle resource */ - _sde_splash_sent_pipe_update_uevent(sde_kms); + /* + * Turn off MDP core power to keep power on/off operations + * be matched, as MDP core power is enabled already when + * early splash is enabled. + */ + sde_power_resource_enable(phandle, + sde_kms->core_client, false); - /* Finally mark handoff flag to false to say handoff is complete */ - sinfo->handoff = false; + /* send uevent to notify user to recycle resource */ + _sde_splash_sent_pipe_update_uevent(sde_kms); - DRM_INFO("HDMI and DSI resource handoff is completed\n"); + /* Finally mark handoff flag to false to say + * handoff is complete. + */ + sinfo->handoff = false; + + DRM_INFO("HDMI and DSI resource handoff is completed\n"); + mutex_unlock(&sde_splash_lock); + return 0; + } + + /* + * Ensure user commit happens on different connectors + * who has splash. + */ + switch (connector_type) { + case DRM_MODE_CONNECTOR_HDMIA: + if (sinfo->hdmi_connector_cnt == 1) + sinfo->hdmi_connector_cnt--; + break; + case DRM_MODE_CONNECTOR_DSI: + if (strcmp(dsi_display->display_type, "unknown") && + strcmp(last_commit_display_type, + dsi_display->display_type)) { + if (sinfo->dsi_connector_cnt >= 1) + sinfo->dsi_connector_cnt--; + + last_commit_display_type = dsi_display->display_type; + } + break; + default: + ret = -EINVAL; + SDE_ERROR("%s: invalid connector_type %d\n", + __func__, connector_type); + } mutex_unlock(&sde_splash_lock); - return 0; + return ret; } /* @@ -776,7 +860,8 @@ int sde_splash_free_resource(struct msm_kms *kms, * 1. Notify LK to stop display splash. * 2. Set DOMAIN_ATTR_EARLY_MAP to 1 to enable stage 1 translation in iommu. */ -int sde_splash_lk_stop_splash(struct msm_kms *kms) +int sde_splash_lk_stop_splash(struct msm_kms *kms, + struct drm_atomic_state *state) { struct sde_splash_info *sinfo; struct msm_mmu *mmu; @@ -792,7 +877,8 @@ int sde_splash_lk_stop_splash(struct msm_kms *kms) /* Monitor LK's status and tell it to exit. */ mutex_lock(&sde_splash_lock); - if (sinfo->display_splash_enabled) { + if (_sde_splash_validate_commit(sde_kms, state) && + sinfo->display_splash_enabled) { if (_sde_splash_lk_check(sde_kms->hw_intr)) _sde_splash_notify_lk_stop_splash(sde_kms->hw_intr); diff --git a/drivers/gpu/drm/msm/sde/sde_splash.h b/drivers/gpu/drm/msm/sde/sde_splash.h index 2fd8ba03112f..c4bb7b08f817 100644 --- a/drivers/gpu/drm/msm/sde/sde_splash.h +++ b/drivers/gpu/drm/msm/sde/sde_splash.h @@ -17,9 +17,6 @@ #include "msm_mmu.h" #include "sde_hw_mdss.h" -#define SPLASH_CTL_MAX 5 -#define SPLASH_LM_MAX 7 - enum splash_connector_type { SPLASH_DSI = 0, SPLASH_HDMI, @@ -35,13 +32,13 @@ struct splash_ctl_top { u32 value; u8 intf_sel; u8 ctl_lm_cnt; - struct splash_lm_hw lm[SPLASH_LM_MAX]; + struct splash_lm_hw lm[LM_MAX - LM_0]; }; struct sde_res_data { - struct splash_ctl_top top[SPLASH_CTL_MAX]; - u8 ctl_ids[SPLASH_CTL_MAX]; - u8 lm_ids[SPLASH_LM_MAX]; + struct splash_ctl_top top[CTL_MAX - CTL_0]; + u8 ctl_ids[CTL_MAX - CTL_0]; + u8 lm_ids[LM_MAX - LM_0]; u8 ctl_top_cnt; u8 lm_cnt; }; @@ -121,18 +118,21 @@ void sde_splash_setup_connector_count(struct sde_splash_info *sinfo, /** * sde_splash_lk_stop_splash. * - * Tell LK to stop display splash. + * Tell LK to stop display splash once one valid user commit arrives. */ -int sde_splash_lk_stop_splash(struct msm_kms *kms); +int sde_splash_lk_stop_splash(struct msm_kms *kms, + struct drm_atomic_state *state); /** * sde_splash_free_resource. * - * According to input connector_type, free - * HDMI's and DSI's resource respectively. + * To free all LK's resource, including free reserved memory to system, + * withdraw data bus vote, disable MDP core power, send uevent to user + * to recycle pipe etc. */ int sde_splash_free_resource(struct msm_kms *kms, - struct sde_power_handle *phandle); + struct sde_power_handle *phandle, + int connector_type, void *display); /** * sde_splash_parse_memory_dt.