Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21b2f0c0 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drm/msm/sde: output black frame during resume operation"

parents a6e2c298 e6710781
Loading
Loading
Loading
Loading
+99 −2
Original line number Diff line number Diff line
@@ -1923,8 +1923,75 @@ static struct drm_driver msm_driver = {
#ifdef CONFIG_PM_SLEEP
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);

	return 0;
@@ -1932,8 +1999,38 @@ static int msm_pm_suspend(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);

	return 0;
+12 −0
Original line number Diff line number Diff line
@@ -367,6 +367,9 @@ struct msm_drm_private {

	struct msm_vblank_ctrl vblank_ctrl;

	/* saved atomic state during system suspend */
	struct drm_atomic_state *suspend_state;

	/* list of clients waiting for events */
	struct list_head client_event_list;
};
@@ -414,6 +417,15 @@ void __msm_fence_worker(struct work_struct *work);
		(_cb)->func = _func;                         \
	} 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,
		struct drm_atomic_state *state, bool async);

+150 −27
Original line number Diff line number Diff line
@@ -57,7 +57,17 @@

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);
}
@@ -77,10 +87,10 @@ static void sde_crtc_destroy(struct drm_crtc *crtc)
	sde_cp_crtc_destroy_properties(crtc);

	debugfs_remove_recursive(sde_crtc->debugfs_root);
	mutex_destroy(&sde_crtc->crtc_lock);
	sde_fence_deinit(&sde_crtc->output_fence);

	drm_crtc_cleanup(crtc);
	mutex_destroy(&sde_crtc->crtc_lock);
	kfree(sde_crtc);
}

@@ -940,6 +950,112 @@ end:
	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
 * @crtc: Pointer to drm crtc structure
@@ -990,6 +1106,10 @@ static void sde_crtc_reset(struct drm_crtc *crtc)
		return;
	}

	/* revert suspend actions, if necessary */
	if (msm_is_suspend_state(crtc->dev))
		_sde_crtc_set_suspend(crtc, false);

	/* remove previous state, if present */
	if (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)
{
	struct msm_drm_private *priv;
	struct sde_crtc *sde_crtc;
	struct drm_encoder *encoder;
	struct sde_crtc *sde_crtc;
	struct sde_kms *sde_kms;
	struct msm_drm_private *priv;

	if (!crtc) {
	if (!crtc || !crtc->dev || !crtc->state) {
		SDE_ERROR("invalid crtc\n");
		return;
	}
	sde_crtc = to_sde_crtc(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;

	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);
	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",
				crtc->base.id);
		SDE_EVT32(DRMID(crtc));
@@ -1262,40 +1389,36 @@ end:

int sde_crtc_vblank(struct drm_crtc *crtc, bool en)
{
	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
	struct drm_encoder *encoder;
	struct drm_device *dev = crtc->dev;
	struct sde_crtc *sde_crtc;
	int rc = 0;

	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) {
		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) {
		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) {
		SDE_DEBUG("crtc%d vblank disable\n", crtc->base.id);
		if (!sde_crtc->suspend)
			_sde_crtc_vblank_enable_nolock(sde_crtc, false);
	} else {
		SDE_DEBUG("crtc%d vblank %s refcount:%d\n",
				crtc->base.id,
				en ? "enable" : "disable",
				atomic_read(&sde_crtc->vblank_refcount));
		return 0;
	}

	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
		if (encoder->crtc != crtc)
			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;
	mutex_unlock(&sde_crtc->crtc_lock);
	return rc;
}

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;
	atomic_set(&sde_crtc->vblank_refcount, 0);

	mutex_init(&sde_crtc->crtc_lock);
	spin_lock_init(&sde_crtc->spin_lock);
	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);

	/* initialize output fence support */
	mutex_init(&sde_crtc->crtc_lock);
	sde_fence_init(&sde_crtc->output_fence, sde_crtc->name, crtc->base.id);

	/* initialize debugfs support */
+2 −0
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ struct sde_crtc_frame_event {
 * @vblank_cb_count : count of vblank callback since last reset
 * @vblank_cb_time  : ktime at vblank count reset
 * @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
 * @active_list   : list of color processing features are active
 * @dirty_list    : list of color processing features are dirty
@@ -117,6 +118,7 @@ struct sde_crtc {
	u32 vblank_cb_count;
	ktime_t vblank_cb_time;
	atomic_t vblank_refcount;
	bool suspend;

	struct list_head feature_list;
	struct list_head active_list;
+0 −12
Original line number Diff line number Diff line
@@ -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)
{
	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);
}

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_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
}

static void sde_kms_prepare_commit(struct msm_kms *kms,
Loading