Merge "msm: drm: add hibernation support"

This commit is contained in:
Linux Build Service Account 2019-01-16 21:41:55 -08:00 committed by Gerrit - the friendly Code Review server
commit b8b0a039da
6 changed files with 180 additions and 13 deletions

View file

@ -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)

View file

@ -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);

View file

@ -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,

View file

@ -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 */

View file

@ -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,13 +949,21 @@ 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();
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);
return error;

View file

@ -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; }