summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c144
-rw-r--r--drivers/gpu/drm/msm/msm_kms.h3
-rw-r--r--drivers/gpu/drm/msm/sde/sde_connector.c17
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.c8
-rw-r--r--drivers/gpu/drm/msm/sde/sde_splash.c19
-rw-r--r--include/linux/suspend.h2
6 files changed, 180 insertions, 13 deletions
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 <robdclark@gmail.com>
*
@@ -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 <robdclark@gmail.com>
*
@@ -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 <linux/suspend.h>
+
#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 <linux/debugfs.h>
#include <linux/memblock.h>
#include <soc/qcom/early_domain.h>
+#include <linux/suspend.h>
+
#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; }