Merge "drm/msm/sde: output black frame during resume operation"
This commit is contained in:
commit
21b2f0c012
6 changed files with 299 additions and 46 deletions
|
@ -1923,8 +1923,75 @@ static struct drm_driver msm_driver = {
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static int msm_pm_suspend(struct device *dev)
|
static int msm_pm_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct drm_device *ddev = dev_get_drvdata(dev);
|
struct drm_device *ddev;
|
||||||
|
struct drm_modeset_acquire_ctx *ctx;
|
||||||
|
struct drm_connector *conn;
|
||||||
|
struct drm_atomic_state *state;
|
||||||
|
struct drm_crtc_state *crtc_state;
|
||||||
|
struct msm_drm_private *priv;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!dev)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ddev = dev_get_drvdata(dev);
|
||||||
|
if (!ddev || !ddev->dev_private)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
priv = ddev->dev_private;
|
||||||
|
SDE_EVT32(0);
|
||||||
|
|
||||||
|
/* acquire modeset lock(s) */
|
||||||
|
drm_modeset_lock_all(ddev);
|
||||||
|
ctx = ddev->mode_config.acquire_ctx;
|
||||||
|
|
||||||
|
/* save current state for resume */
|
||||||
|
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 state to disable all CRTCs */
|
||||||
|
state = drm_atomic_state_alloc(ddev);
|
||||||
|
if (IS_ERR_OR_NULL(state)) {
|
||||||
|
DRM_ERROR("failed to allocate crtc disable state\n");
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->acquire_ctx = ctx;
|
||||||
|
drm_for_each_connector(conn, ddev) {
|
||||||
|
|
||||||
|
if (!conn->state || !conn->state->crtc ||
|
||||||
|
conn->dpms != DRM_MODE_DPMS_ON)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* force CRTC to be inactive */
|
||||||
|
crtc_state = drm_atomic_get_crtc_state(state,
|
||||||
|
conn->state->crtc);
|
||||||
|
if (IS_ERR_OR_NULL(crtc_state)) {
|
||||||
|
DRM_ERROR("failed to get crtc %d state\n",
|
||||||
|
conn->state->crtc->base.id);
|
||||||
|
drm_atomic_state_free(state);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
crtc_state->active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* commit the "disable all" state */
|
||||||
|
ret = drm_atomic_commit(state);
|
||||||
|
if (ret < 0) {
|
||||||
|
DRM_ERROR("failed to disable crtcs, %d\n", ret);
|
||||||
|
drm_atomic_state_free(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
drm_modeset_unlock_all(ddev);
|
||||||
|
|
||||||
|
/* disable hot-plug polling */
|
||||||
drm_kms_helper_poll_disable(ddev);
|
drm_kms_helper_poll_disable(ddev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1932,8 +1999,38 @@ static int msm_pm_suspend(struct device *dev)
|
||||||
|
|
||||||
static int msm_pm_resume(struct device *dev)
|
static int msm_pm_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct drm_device *ddev = dev_get_drvdata(dev);
|
struct drm_device *ddev;
|
||||||
|
struct msm_drm_private *priv;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!dev)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ddev = dev_get_drvdata(dev);
|
||||||
|
if (!ddev || !ddev->dev_private)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
priv = ddev->dev_private;
|
||||||
|
|
||||||
|
SDE_EVT32(priv->suspend_state != NULL);
|
||||||
|
|
||||||
|
drm_mode_config_reset(ddev);
|
||||||
|
|
||||||
|
drm_modeset_lock_all(ddev);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* enable hot-plug polling */
|
||||||
drm_kms_helper_poll_enable(ddev);
|
drm_kms_helper_poll_enable(ddev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -367,6 +367,9 @@ struct msm_drm_private {
|
||||||
|
|
||||||
struct msm_vblank_ctrl vblank_ctrl;
|
struct msm_vblank_ctrl vblank_ctrl;
|
||||||
|
|
||||||
|
/* saved atomic state during system suspend */
|
||||||
|
struct drm_atomic_state *suspend_state;
|
||||||
|
|
||||||
/* list of clients waiting for events */
|
/* list of clients waiting for events */
|
||||||
struct list_head client_event_list;
|
struct list_head client_event_list;
|
||||||
};
|
};
|
||||||
|
@ -414,6 +417,15 @@ void __msm_fence_worker(struct work_struct *work);
|
||||||
(_cb)->func = _func; \
|
(_cb)->func = _func; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
static inline bool msm_is_suspend_state(struct drm_device *dev)
|
||||||
|
{
|
||||||
|
if (!dev || !dev->dev_private)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return ((struct msm_drm_private *)dev->dev_private)->suspend_state !=
|
||||||
|
NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int msm_atomic_commit(struct drm_device *dev,
|
int msm_atomic_commit(struct drm_device *dev,
|
||||||
struct drm_atomic_state *state, bool async);
|
struct drm_atomic_state *state, bool async);
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,17 @@
|
||||||
|
|
||||||
static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc)
|
static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc)
|
||||||
{
|
{
|
||||||
struct msm_drm_private *priv = crtc->dev->dev_private;
|
struct msm_drm_private *priv;
|
||||||
|
|
||||||
|
if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
|
||||||
|
SDE_ERROR("invalid crtc\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
priv = crtc->dev->dev_private;
|
||||||
|
if (!priv || !priv->kms) {
|
||||||
|
SDE_ERROR("invalid kms\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return to_sde_kms(priv->kms);
|
return to_sde_kms(priv->kms);
|
||||||
}
|
}
|
||||||
|
@ -77,10 +87,10 @@ static void sde_crtc_destroy(struct drm_crtc *crtc)
|
||||||
sde_cp_crtc_destroy_properties(crtc);
|
sde_cp_crtc_destroy_properties(crtc);
|
||||||
|
|
||||||
debugfs_remove_recursive(sde_crtc->debugfs_root);
|
debugfs_remove_recursive(sde_crtc->debugfs_root);
|
||||||
mutex_destroy(&sde_crtc->crtc_lock);
|
|
||||||
sde_fence_deinit(&sde_crtc->output_fence);
|
sde_fence_deinit(&sde_crtc->output_fence);
|
||||||
|
|
||||||
drm_crtc_cleanup(crtc);
|
drm_crtc_cleanup(crtc);
|
||||||
|
mutex_destroy(&sde_crtc->crtc_lock);
|
||||||
kfree(sde_crtc);
|
kfree(sde_crtc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -940,6 +950,112 @@ end:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _sde_crtc_vblank_enable_nolock - update power resource and vblank request
|
||||||
|
* @sde_crtc: Pointer to sde crtc structure
|
||||||
|
* @enable: Whether to enable/disable vblanks
|
||||||
|
*/
|
||||||
|
static void _sde_crtc_vblank_enable_nolock(
|
||||||
|
struct sde_crtc *sde_crtc, bool enable)
|
||||||
|
{
|
||||||
|
struct drm_device *dev;
|
||||||
|
struct drm_crtc *crtc;
|
||||||
|
struct drm_encoder *enc;
|
||||||
|
struct msm_drm_private *priv;
|
||||||
|
struct sde_kms *sde_kms;
|
||||||
|
|
||||||
|
if (!sde_crtc) {
|
||||||
|
SDE_ERROR("invalid crtc\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
crtc = &sde_crtc->base;
|
||||||
|
dev = crtc->dev;
|
||||||
|
priv = dev->dev_private;
|
||||||
|
|
||||||
|
if (!priv->kms) {
|
||||||
|
SDE_ERROR("invalid kms\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sde_kms = to_sde_kms(priv->kms);
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
sde_power_resource_enable(&priv->phandle,
|
||||||
|
sde_kms->core_client, true);
|
||||||
|
list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
|
||||||
|
if (enc->crtc != crtc)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SDE_EVT32(DRMID(crtc), DRMID(enc), enable);
|
||||||
|
|
||||||
|
sde_encoder_register_vblank_callback(enc,
|
||||||
|
sde_crtc_vblank_cb, (void *)crtc);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
|
||||||
|
if (enc->crtc != crtc)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SDE_EVT32(DRMID(crtc), DRMID(enc), enable);
|
||||||
|
|
||||||
|
sde_encoder_register_vblank_callback(enc, NULL, NULL);
|
||||||
|
}
|
||||||
|
sde_power_resource_enable(&priv->phandle,
|
||||||
|
sde_kms->core_client, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _sde_crtc_set_suspend - notify crtc of suspend enable/disable
|
||||||
|
* @crtc: Pointer to drm crtc object
|
||||||
|
* @enable: true to enable suspend, false to indicate resume
|
||||||
|
*/
|
||||||
|
static void _sde_crtc_set_suspend(struct drm_crtc *crtc, bool enable)
|
||||||
|
{
|
||||||
|
struct sde_crtc *sde_crtc;
|
||||||
|
struct msm_drm_private *priv;
|
||||||
|
struct sde_kms *sde_kms;
|
||||||
|
|
||||||
|
if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
|
||||||
|
SDE_ERROR("invalid crtc\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sde_crtc = to_sde_crtc(crtc);
|
||||||
|
priv = crtc->dev->dev_private;
|
||||||
|
|
||||||
|
if (!priv->kms) {
|
||||||
|
SDE_ERROR("invalid crtc kms\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sde_kms = to_sde_kms(priv->kms);
|
||||||
|
|
||||||
|
SDE_DEBUG("crtc%d suspend = %d\n", crtc->base.id, enable);
|
||||||
|
|
||||||
|
mutex_lock(&sde_crtc->crtc_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update CP on suspend/resume transitions
|
||||||
|
*/
|
||||||
|
if (enable && !sde_crtc->suspend)
|
||||||
|
sde_cp_crtc_suspend(crtc);
|
||||||
|
else if (!enable && sde_crtc->suspend)
|
||||||
|
sde_cp_crtc_resume(crtc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the vblank refcount != 0, release a power reference on suspend
|
||||||
|
* and take it back during resume (if it is still != 0).
|
||||||
|
*/
|
||||||
|
if (sde_crtc->suspend == enable)
|
||||||
|
SDE_DEBUG("crtc%d suspend already set to %d, ignoring update\n",
|
||||||
|
crtc->base.id, enable);
|
||||||
|
else if (atomic_read(&sde_crtc->vblank_refcount) != 0)
|
||||||
|
_sde_crtc_vblank_enable_nolock(sde_crtc, !enable);
|
||||||
|
|
||||||
|
sde_crtc->suspend = enable;
|
||||||
|
|
||||||
|
mutex_unlock(&sde_crtc->crtc_lock);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sde_crtc_duplicate_state - state duplicate hook
|
* sde_crtc_duplicate_state - state duplicate hook
|
||||||
* @crtc: Pointer to drm crtc structure
|
* @crtc: Pointer to drm crtc structure
|
||||||
|
@ -990,6 +1106,10 @@ static void sde_crtc_reset(struct drm_crtc *crtc)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* revert suspend actions, if necessary */
|
||||||
|
if (msm_is_suspend_state(crtc->dev))
|
||||||
|
_sde_crtc_set_suspend(crtc, false);
|
||||||
|
|
||||||
/* remove previous state, if present */
|
/* remove previous state, if present */
|
||||||
if (crtc->state) {
|
if (crtc->state) {
|
||||||
sde_crtc_destroy_state(crtc, crtc->state);
|
sde_crtc_destroy_state(crtc, crtc->state);
|
||||||
|
@ -1015,25 +1135,32 @@ static void sde_crtc_reset(struct drm_crtc *crtc)
|
||||||
|
|
||||||
static void sde_crtc_disable(struct drm_crtc *crtc)
|
static void sde_crtc_disable(struct drm_crtc *crtc)
|
||||||
{
|
{
|
||||||
struct msm_drm_private *priv;
|
|
||||||
struct sde_crtc *sde_crtc;
|
|
||||||
struct drm_encoder *encoder;
|
struct drm_encoder *encoder;
|
||||||
|
struct sde_crtc *sde_crtc;
|
||||||
struct sde_kms *sde_kms;
|
struct sde_kms *sde_kms;
|
||||||
|
struct msm_drm_private *priv;
|
||||||
|
|
||||||
if (!crtc) {
|
if (!crtc || !crtc->dev || !crtc->state) {
|
||||||
SDE_ERROR("invalid crtc\n");
|
SDE_ERROR("invalid crtc\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sde_crtc = to_sde_crtc(crtc);
|
sde_crtc = to_sde_crtc(crtc);
|
||||||
sde_kms = _sde_crtc_get_kms(crtc);
|
sde_kms = _sde_crtc_get_kms(crtc);
|
||||||
|
if (!sde_kms || !sde_kms->dev || !sde_kms->dev->dev_private) {
|
||||||
|
SDE_ERROR("invalid kms handle\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
priv = sde_kms->dev->dev_private;
|
priv = sde_kms->dev->dev_private;
|
||||||
|
|
||||||
SDE_DEBUG("crtc%d\n", crtc->base.id);
|
SDE_DEBUG("crtc%d\n", crtc->base.id);
|
||||||
|
|
||||||
|
if (msm_is_suspend_state(crtc->dev))
|
||||||
|
_sde_crtc_set_suspend(crtc, true);
|
||||||
|
|
||||||
mutex_lock(&sde_crtc->crtc_lock);
|
mutex_lock(&sde_crtc->crtc_lock);
|
||||||
SDE_EVT32(DRMID(crtc));
|
SDE_EVT32(DRMID(crtc));
|
||||||
|
|
||||||
if (atomic_read(&sde_crtc->vblank_refcount)) {
|
if (atomic_read(&sde_crtc->vblank_refcount) && !sde_crtc->suspend) {
|
||||||
SDE_ERROR("crtc%d invalid vblank refcount\n",
|
SDE_ERROR("crtc%d invalid vblank refcount\n",
|
||||||
crtc->base.id);
|
crtc->base.id);
|
||||||
SDE_EVT32(DRMID(crtc));
|
SDE_EVT32(DRMID(crtc));
|
||||||
|
@ -1262,40 +1389,36 @@ end:
|
||||||
|
|
||||||
int sde_crtc_vblank(struct drm_crtc *crtc, bool en)
|
int sde_crtc_vblank(struct drm_crtc *crtc, bool en)
|
||||||
{
|
{
|
||||||
struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
|
struct sde_crtc *sde_crtc;
|
||||||
struct drm_encoder *encoder;
|
int rc = 0;
|
||||||
struct drm_device *dev = crtc->dev;
|
|
||||||
|
|
||||||
|
if (!crtc) {
|
||||||
|
SDE_ERROR("invalid crtc\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
sde_crtc = to_sde_crtc(crtc);
|
||||||
|
|
||||||
|
mutex_lock(&sde_crtc->crtc_lock);
|
||||||
if (en && atomic_inc_return(&sde_crtc->vblank_refcount) == 1) {
|
if (en && atomic_inc_return(&sde_crtc->vblank_refcount) == 1) {
|
||||||
SDE_DEBUG("crtc%d vblank enable\n", crtc->base.id);
|
SDE_DEBUG("crtc%d vblank enable\n", crtc->base.id);
|
||||||
|
if (!sde_crtc->suspend)
|
||||||
|
_sde_crtc_vblank_enable_nolock(sde_crtc, true);
|
||||||
} else if (!en && atomic_read(&sde_crtc->vblank_refcount) < 1) {
|
} else if (!en && atomic_read(&sde_crtc->vblank_refcount) < 1) {
|
||||||
SDE_ERROR("crtc%d invalid vblank disable\n", crtc->base.id);
|
SDE_ERROR("crtc%d invalid vblank disable\n", crtc->base.id);
|
||||||
return -EINVAL;
|
rc = -EINVAL;
|
||||||
} else if (!en && atomic_dec_return(&sde_crtc->vblank_refcount) == 0) {
|
} else if (!en && atomic_dec_return(&sde_crtc->vblank_refcount) == 0) {
|
||||||
SDE_DEBUG("crtc%d vblank disable\n", crtc->base.id);
|
SDE_DEBUG("crtc%d vblank disable\n", crtc->base.id);
|
||||||
|
if (!sde_crtc->suspend)
|
||||||
|
_sde_crtc_vblank_enable_nolock(sde_crtc, false);
|
||||||
} else {
|
} else {
|
||||||
SDE_DEBUG("crtc%d vblank %s refcount:%d\n",
|
SDE_DEBUG("crtc%d vblank %s refcount:%d\n",
|
||||||
crtc->base.id,
|
crtc->base.id,
|
||||||
en ? "enable" : "disable",
|
en ? "enable" : "disable",
|
||||||
atomic_read(&sde_crtc->vblank_refcount));
|
atomic_read(&sde_crtc->vblank_refcount));
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
mutex_unlock(&sde_crtc->crtc_lock);
|
||||||
if (encoder->crtc != crtc)
|
return rc;
|
||||||
continue;
|
|
||||||
|
|
||||||
SDE_EVT32(DRMID(crtc), en);
|
|
||||||
|
|
||||||
if (en)
|
|
||||||
sde_encoder_register_vblank_callback(encoder,
|
|
||||||
sde_crtc_vblank_cb, (void *)crtc);
|
|
||||||
else
|
|
||||||
sde_encoder_register_vblank_callback(encoder, NULL,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc,
|
void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc,
|
||||||
|
@ -1739,6 +1862,7 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev,
|
||||||
crtc->dev = dev;
|
crtc->dev = dev;
|
||||||
atomic_set(&sde_crtc->vblank_refcount, 0);
|
atomic_set(&sde_crtc->vblank_refcount, 0);
|
||||||
|
|
||||||
|
mutex_init(&sde_crtc->crtc_lock);
|
||||||
spin_lock_init(&sde_crtc->spin_lock);
|
spin_lock_init(&sde_crtc->spin_lock);
|
||||||
atomic_set(&sde_crtc->frame_pending, 0);
|
atomic_set(&sde_crtc->frame_pending, 0);
|
||||||
|
|
||||||
|
@ -1760,7 +1884,6 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev,
|
||||||
snprintf(sde_crtc->name, SDE_CRTC_NAME_SIZE, "crtc%u", crtc->base.id);
|
snprintf(sde_crtc->name, SDE_CRTC_NAME_SIZE, "crtc%u", crtc->base.id);
|
||||||
|
|
||||||
/* initialize output fence support */
|
/* initialize output fence support */
|
||||||
mutex_init(&sde_crtc->crtc_lock);
|
|
||||||
sde_fence_init(&sde_crtc->output_fence, sde_crtc->name, crtc->base.id);
|
sde_fence_init(&sde_crtc->output_fence, sde_crtc->name, crtc->base.id);
|
||||||
|
|
||||||
/* initialize debugfs support */
|
/* initialize debugfs support */
|
||||||
|
|
|
@ -82,6 +82,7 @@ struct sde_crtc_frame_event {
|
||||||
* @vblank_cb_count : count of vblank callback since last reset
|
* @vblank_cb_count : count of vblank callback since last reset
|
||||||
* @vblank_cb_time : ktime at vblank count reset
|
* @vblank_cb_time : ktime at vblank count reset
|
||||||
* @vblank_refcount : reference count for vblank enable request
|
* @vblank_refcount : reference count for vblank enable request
|
||||||
|
* @suspend : whether or not a suspend operation is in progress
|
||||||
* @feature_list : list of color processing features supported on a crtc
|
* @feature_list : list of color processing features supported on a crtc
|
||||||
* @active_list : list of color processing features are active
|
* @active_list : list of color processing features are active
|
||||||
* @dirty_list : list of color processing features are dirty
|
* @dirty_list : list of color processing features are dirty
|
||||||
|
@ -117,6 +118,7 @@ struct sde_crtc {
|
||||||
u32 vblank_cb_count;
|
u32 vblank_cb_count;
|
||||||
ktime_t vblank_cb_time;
|
ktime_t vblank_cb_time;
|
||||||
atomic_t vblank_refcount;
|
atomic_t vblank_refcount;
|
||||||
|
bool suspend;
|
||||||
|
|
||||||
struct list_head feature_list;
|
struct list_head feature_list;
|
||||||
struct list_head active_list;
|
struct list_head active_list;
|
||||||
|
|
|
@ -328,24 +328,12 @@ static int sde_debugfs_danger_init(struct sde_kms *sde_kms,
|
||||||
|
|
||||||
static int sde_kms_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
|
static int sde_kms_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
|
||||||
{
|
{
|
||||||
struct sde_kms *sde_kms = to_sde_kms(kms);
|
|
||||||
struct drm_device *dev = sde_kms->dev;
|
|
||||||
struct msm_drm_private *priv = dev->dev_private;
|
|
||||||
|
|
||||||
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true);
|
|
||||||
|
|
||||||
return sde_crtc_vblank(crtc, true);
|
return sde_crtc_vblank(crtc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sde_kms_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
|
static void sde_kms_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc)
|
||||||
{
|
{
|
||||||
struct sde_kms *sde_kms = to_sde_kms(kms);
|
|
||||||
struct drm_device *dev = sde_kms->dev;
|
|
||||||
struct msm_drm_private *priv = dev->dev_private;
|
|
||||||
|
|
||||||
sde_crtc_vblank(crtc, false);
|
sde_crtc_vblank(crtc, false);
|
||||||
|
|
||||||
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sde_kms_prepare_commit(struct msm_kms *kms,
|
static void sde_kms_prepare_commit(struct msm_kms *kms,
|
||||||
|
|
|
@ -34,6 +34,14 @@
|
||||||
#include "sde_plane.h"
|
#include "sde_plane.h"
|
||||||
#include "sde_color_processing.h"
|
#include "sde_color_processing.h"
|
||||||
|
|
||||||
|
static bool suspend_blank = true;
|
||||||
|
module_param(suspend_blank, bool, 0400);
|
||||||
|
MODULE_PARM_DESC(suspend_blank,
|
||||||
|
"If set, active planes will force their outputs to black,\n"
|
||||||
|
"by temporarily enabling the color fill, when recovering\n"
|
||||||
|
"from a system resume instead of attempting to display the\n"
|
||||||
|
"last provided frame buffer.");
|
||||||
|
|
||||||
#define SDE_DEBUG_PLANE(pl, fmt, ...) SDE_DEBUG("plane%d " fmt,\
|
#define SDE_DEBUG_PLANE(pl, fmt, ...) SDE_DEBUG("plane%d " fmt,\
|
||||||
(pl) ? (pl)->base.base.id : -1, ##__VA_ARGS__)
|
(pl) ? (pl)->base.base.id : -1, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
@ -138,6 +146,7 @@ struct sde_plane {
|
||||||
struct sde_debugfs_regset32 debugfs_src;
|
struct sde_debugfs_regset32 debugfs_src;
|
||||||
struct sde_debugfs_regset32 debugfs_scaler;
|
struct sde_debugfs_regset32 debugfs_scaler;
|
||||||
struct sde_debugfs_regset32 debugfs_csc;
|
struct sde_debugfs_regset32 debugfs_csc;
|
||||||
|
bool debugfs_default_scale;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_sde_plane(x) container_of(x, struct sde_plane, base)
|
#define to_sde_plane(x) container_of(x, struct sde_plane, base)
|
||||||
|
@ -686,9 +695,20 @@ static int _sde_plane_setup_scaler3_lut(struct sde_phy_plane *pp,
|
||||||
struct sde_plane_state *pstate)
|
struct sde_plane_state *pstate)
|
||||||
{
|
{
|
||||||
struct sde_plane *psde = pp->sde_plane;
|
struct sde_plane *psde = pp->sde_plane;
|
||||||
struct sde_hw_scaler3_cfg *cfg = pp->scaler3_cfg;
|
struct sde_hw_scaler3_cfg *cfg;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!pp || !pp->scaler3_cfg) {
|
||||||
|
SDE_ERROR("invalid args\n");
|
||||||
|
return -EINVAL;
|
||||||
|
} else if (!pstate) {
|
||||||
|
/* pstate is expected to be null on forced color fill */
|
||||||
|
SDE_DEBUG("null pstate\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg = pp->scaler3_cfg;
|
||||||
|
|
||||||
cfg->dir_lut = msm_property_get_blob(
|
cfg->dir_lut = msm_property_get_blob(
|
||||||
&psde->property_info,
|
&psde->property_info,
|
||||||
pstate->property_blobs, &cfg->dir_len,
|
pstate->property_blobs, &cfg->dir_len,
|
||||||
|
@ -723,6 +743,7 @@ static void _sde_plane_setup_scaler3(struct sde_phy_plane *pp,
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(scale_cfg, 0, sizeof(*scale_cfg));
|
memset(scale_cfg, 0, sizeof(*scale_cfg));
|
||||||
|
memset(&pp->pixel_ext, 0, sizeof(struct sde_hw_pixel_ext));
|
||||||
|
|
||||||
decimated = DECIMATED_DIMENSION(src_w,
|
decimated = DECIMATED_DIMENSION(src_w,
|
||||||
pp->pipe_cfg.horz_decimation);
|
pp->pipe_cfg.horz_decimation);
|
||||||
|
@ -1070,7 +1091,8 @@ static void _sde_plane_setup_scaler(struct sde_phy_plane *pp,
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = _sde_plane_setup_scaler3_lut(pp, pstate);
|
error = _sde_plane_setup_scaler3_lut(pp, pstate);
|
||||||
if (error || !pp->pixel_ext_usr) {
|
if (error || !pp->pixel_ext_usr ||
|
||||||
|
psde->debugfs_default_scale) {
|
||||||
memset(pe, 0, sizeof(struct sde_hw_pixel_ext));
|
memset(pe, 0, sizeof(struct sde_hw_pixel_ext));
|
||||||
/* calculate default config for QSEED3 */
|
/* calculate default config for QSEED3 */
|
||||||
_sde_plane_setup_scaler3(pp,
|
_sde_plane_setup_scaler3(pp,
|
||||||
|
@ -1081,7 +1103,8 @@ static void _sde_plane_setup_scaler(struct sde_phy_plane *pp,
|
||||||
pp->scaler3_cfg, fmt,
|
pp->scaler3_cfg, fmt,
|
||||||
chroma_subsmpl_h, chroma_subsmpl_v);
|
chroma_subsmpl_h, chroma_subsmpl_v);
|
||||||
}
|
}
|
||||||
} else if (!pp->pixel_ext_usr) {
|
} else if (!pp->pixel_ext_usr || !pstate ||
|
||||||
|
psde->debugfs_default_scale) {
|
||||||
uint32_t deci_dim, i;
|
uint32_t deci_dim, i;
|
||||||
|
|
||||||
/* calculate default configuration for QSEED2 */
|
/* calculate default configuration for QSEED2 */
|
||||||
|
@ -1701,8 +1724,8 @@ void sde_plane_flush(struct drm_plane *plane)
|
||||||
*/
|
*/
|
||||||
list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) {
|
list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) {
|
||||||
if (psde->is_error)
|
if (psde->is_error)
|
||||||
/* force white frame with 0% alpha pipe output on error */
|
/* force white frame with 100% alpha pipe output on error */
|
||||||
_sde_plane_color_fill(pp, 0xFFFFFF, 0x0);
|
_sde_plane_color_fill(pp, 0xFFFFFF, 0xFF);
|
||||||
else if (pp->color_fill & SDE_PLANE_COLOR_FILL_FLAG)
|
else if (pp->color_fill & SDE_PLANE_COLOR_FILL_FLAG)
|
||||||
/* force 100% alpha */
|
/* force 100% alpha */
|
||||||
_sde_plane_color_fill(pp, pp->color_fill, 0xFF);
|
_sde_plane_color_fill(pp, pp->color_fill, 0xFF);
|
||||||
|
@ -1711,6 +1734,10 @@ void sde_plane_flush(struct drm_plane *plane)
|
||||||
pp->pipe_hw->ops.setup_csc(pp->pipe_hw, pp->csc_ptr);
|
pp->pipe_hw->ops.setup_csc(pp->pipe_hw, pp->csc_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* force black color fill during suspend */
|
||||||
|
if (msm_is_suspend_state(plane->dev) && suspend_blank)
|
||||||
|
_sde_plane_color_fill(pp, 0x0, 0x0);
|
||||||
|
|
||||||
/* flag h/w flush complete */
|
/* flag h/w flush complete */
|
||||||
if (plane->state)
|
if (plane->state)
|
||||||
to_sde_plane_state(plane->state)->pending = false;
|
to_sde_plane_state(plane->state)->pending = false;
|
||||||
|
@ -2582,6 +2609,10 @@ static void _sde_plane_init_debugfs(struct sde_plane *psde,
|
||||||
sde_debugfs_create_regset32("scaler_blk", S_IRUGO,
|
sde_debugfs_create_regset32("scaler_blk", S_IRUGO,
|
||||||
psde->debugfs_root,
|
psde->debugfs_root,
|
||||||
&psde->debugfs_scaler);
|
&psde->debugfs_scaler);
|
||||||
|
debugfs_create_bool("default_scaling",
|
||||||
|
0644,
|
||||||
|
psde->debugfs_root,
|
||||||
|
&psde->debugfs_default_scale);
|
||||||
|
|
||||||
sde_debugfs_setup_regset32(&psde->debugfs_csc,
|
sde_debugfs_setup_regset32(&psde->debugfs_csc,
|
||||||
sblk->csc_blk.base + cfg->base,
|
sblk->csc_blk.base + cfg->base,
|
||||||
|
|
Loading…
Add table
Reference in a new issue