diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index c2f5621ddf8b..ad33e2fa11bb 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -2134,10 +2134,150 @@ static int msm_pm_resume(struct device *dev) return 0; } + +static int msm_pm_freeze(struct device *dev) +{ + struct drm_device *ddev; + struct drm_crtc *crtc; + struct drm_modeset_acquire_ctx *ctx; + struct drm_atomic_state *state; + struct msm_drm_private *priv; + struct msm_kms *kms; + int early_display = 0; + int ret = 0; + + if (!dev) + return -EINVAL; + + ddev = dev_get_drvdata(dev); + if (!ddev || !ddev->dev_private) + return -EINVAL; + + priv = ddev->dev_private; + + kms = priv->kms; + if (kms && kms->funcs && kms->funcs->early_display_status) + early_display = kms->funcs->early_display_status(kms); + + SDE_EVT32(0); + + if (early_display) { + /* acquire modeset lock(s) */ + drm_modeset_lock_all(ddev); + ctx = ddev->mode_config.acquire_ctx; + + /* save current state for restore */ + if (priv->suspend_state) + drm_atomic_state_free(priv->suspend_state); + + priv->suspend_state = + drm_atomic_helper_duplicate_state(ddev, ctx); + + if (IS_ERR_OR_NULL(priv->suspend_state)) { + DRM_ERROR("failed to back up suspend state\n"); + priv->suspend_state = NULL; + goto unlock; + } + + /* create atomic null state to idle CRTCs */ + state = drm_atomic_state_alloc(ddev); + if (IS_ERR_OR_NULL(state)) { + DRM_ERROR("failed to allocate null atomic state\n"); + goto unlock; + } + + state->acquire_ctx = ctx; + + /* commit the null state */ + ret = drm_atomic_commit(state); + if (ret < 0) { + DRM_ERROR("failed to commit null state, %d\n", ret); + drm_atomic_state_free(state); + } + + drm_for_each_crtc(crtc, ddev) + drm_crtc_vblank_off(crtc); + +unlock: + drm_modeset_unlock_all(ddev); + } else { + ret = msm_pm_suspend(dev); + if (ret) + return ret; + } + return 0; +} + +static int msm_pm_restore(struct device *dev) +{ + struct drm_device *ddev; + struct drm_crtc *crtc; + struct msm_drm_private *priv; + struct msm_kms *kms; + int early_display = 0; + int ret; + + if (!dev) + return -EINVAL; + + ddev = dev_get_drvdata(dev); + if (!ddev || !ddev->dev_private) + return -EINVAL; + + priv = ddev->dev_private; + + kms = priv->kms; + if (kms && kms->funcs && kms->funcs->early_display_status) + early_display = kms->funcs->early_display_status(kms); + + + SDE_EVT32(priv->suspend_state != NULL); + + if (early_display) { + drm_mode_config_reset(ddev); + + drm_modeset_lock_all(ddev); + + drm_for_each_crtc(crtc, ddev) + drm_crtc_vblank_on(crtc); + + if (priv->suspend_state) { + priv->suspend_state->acquire_ctx = + ddev->mode_config.acquire_ctx; + + ret = drm_atomic_commit(priv->suspend_state); + if (ret < 0) { + DRM_ERROR("failed to restore state, %d\n", ret); + drm_atomic_state_free(priv->suspend_state); + } + + priv->suspend_state = NULL; + } + + drm_modeset_unlock_all(ddev); + } else { + ret = msm_pm_resume(dev); + if (ret) + return ret; + } + + return 0; +} + +static int msm_pm_thaw(struct device *dev) +{ + msm_pm_restore(dev); + + return 0; +} #endif static const struct dev_pm_ops msm_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(msm_pm_suspend, msm_pm_resume) + .suspend = msm_pm_suspend, + .resume = msm_pm_resume, + .freeze = msm_pm_freeze, + .restore = msm_pm_restore, + .thaw = msm_pm_thaw, }; static int msm_drm_bind(struct device *dev) diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h index ed0ba928f170..6e3df60aac55 100644 --- a/drivers/gpu/drm/msm/msm_kms.h +++ b/drivers/gpu/drm/msm/msm_kms.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, 2019, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -98,6 +98,7 @@ struct msm_kms_funcs { struct drm_encoder *slave_encoder, bool is_cmd_mode); void (*postopen)(struct msm_kms *kms, struct drm_file *file); + bool (*early_display_status)(struct msm_kms *kms); /* cleanup: */ void (*preclose)(struct msm_kms *kms, struct drm_file *file); void (*postclose)(struct msm_kms *kms, struct drm_file *file); diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index d66f03bfc239..7930cc29f7f4 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.c +++ b/drivers/gpu/drm/msm/sde/sde_connector.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,6 +11,8 @@ */ #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ +#include + #include "msm_drv.h" #include "sde_kms.h" @@ -586,11 +588,16 @@ 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 LK totally exits, LK's early splash resource - * should be released. + /* If below both 2 conditions are met, LK's early splash resources + * should be freed. + * 1) When get_hibernation_status() is returned as true. + * a. hibernation image snapshot failed. + * b. hibernation restore successful. + * c. hibernation restore failed. + * 2) After LK totally exits. */ - if (sde_splash_get_lk_complete_status(priv->kms)) { + if (get_hibernation_status() && + sde_splash_get_lk_complete_status(priv->kms)) { c_conn = to_sde_connector(connector); sde_splash_free_resource(priv->kms, &priv->phandle, diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index e4d607568493..44a5f8c4535b 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -1171,6 +1171,13 @@ static void sde_kms_preclose(struct msm_kms *kms, struct drm_file *file) sde_crtc_cancel_pending_flip(priv->crtcs[i], file); } +static bool sde_kms_early_display_status(struct msm_kms *kms) +{ + struct sde_kms *sde_kms = to_sde_kms(kms); + + return sde_kms->splash_info.handoff; +} + static const struct msm_kms_funcs kms_funcs = { .hw_init = sde_kms_hw_init, .postinit = sde_kms_postinit, @@ -1190,6 +1197,7 @@ static const struct msm_kms_funcs kms_funcs = { .get_format = sde_get_msm_format, .round_pixclk = sde_kms_round_pixclk, .destroy = sde_kms_destroy, + .early_display_status = sde_kms_early_display_status, }; /* the caller api needs to turn on clock before calling it */ diff --git a/drivers/gpu/drm/msm/sde/sde_splash.c b/drivers/gpu/drm/msm/sde/sde_splash.c index b5b6915bb87c..b0d63ec4c64c 100644 --- a/drivers/gpu/drm/msm/sde/sde_splash.c +++ b/drivers/gpu/drm/msm/sde/sde_splash.c @@ -15,6 +15,8 @@ #include #include #include +#include + #include "msm_drv.h" #include "msm_mmu.h" #include "sde_kms.h" @@ -759,7 +761,6 @@ bool sde_splash_get_lk_complete_status(struct msm_kms *kms) intr = sde_kms->hw_intr; if (sde_kms->splash_info.handoff && - !sde_kms->splash_info.display_splash_enabled && !_sde_splash_lk_check()) { SDE_DEBUG("LK totally exits\n"); return true; @@ -948,12 +949,20 @@ int sde_splash_lk_stop_splash(struct msm_kms *kms, mutex_lock(&sde_splash_lock); if (_sde_splash_validate_commit(sde_kms, state) && sinfo->display_splash_enabled) { - if (_sde_splash_lk_check()) + if (_sde_splash_lk_check()) { _sde_splash_notify_lk_stop_splash(); + error = _sde_splash_clear_mixer_blendstage(kms, state); + } - sinfo->display_splash_enabled = false; - - error = _sde_splash_clear_mixer_blendstage(kms, state); + if (get_hibernation_status() == true) { + sinfo->display_splash_enabled = false; + } else { + /* preserve the display_splash_enabled state for + * case when system is restoring from hibernation + * image and splash is enabled. + */ + sinfo->display_splash_enabled = true; + } } mutex_unlock(&sde_splash_lock); diff --git a/include/linux/suspend.h b/include/linux/suspend.h index be1ab158ad1a..04218da27e3a 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -380,6 +380,7 @@ extern unsigned long get_safe_page(gfp_t gfp_mask); extern asmlinkage int swsusp_arch_suspend(void); extern asmlinkage int swsusp_arch_resume(void); +static inline bool get_hibernation_status(void) { return true; }; extern void hibernation_set_ops(const struct platform_hibernation_ops *ops); extern int hibernate(void); extern bool system_entering_hibernation(void); @@ -393,6 +394,7 @@ static inline int swsusp_page_is_forbidden(struct page *p) { return 0; } static inline void swsusp_set_page_free(struct page *p) {} static inline void swsusp_unset_page_free(struct page *p) {} +static inline bool get_hibernation_status(void) { return true; }; static inline void hibernation_set_ops(const struct platform_hibernation_ops *ops) {} static inline int hibernate(void) { return -ENOSYS; } static inline bool system_entering_hibernation(void) { return false; }