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

Commit 7ee99097 authored by Veera Sundaram Sankaran's avatar Veera Sundaram Sankaran
Browse files

drm/msm/sde: move crtc frame event handling to event thread



Currently crtc frame events are handled by display thread, which
might cause issues during suspend usecases. During suspend, the
encoder disable waits for pingpong done and sends the frame done
event to crtc which in turn reduces the frame_pending count. But
this might not execute until the display thread is done with suspend.
To avoid such cases and also to free up the display thread, move these
events to the event thread. Add completion logic to synchronizes the
frame_done event.

Change-Id: I2166d051e0cf68f7a7434df42263c43f588c094d
Signed-off-by: default avatarVeera Sundaram Sankaran <veeras@codeaurora.org>
parent 13ff59f2
Loading
Loading
Loading
Loading
+58 −23
Original line number Diff line number Diff line
@@ -1517,7 +1517,6 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)
	struct sde_crtc_state *cstate;
	struct sde_kms *sde_kms;
	unsigned long flags;
	bool disable_inprogress = false;

	if (!work) {
		SDE_ERROR("invalid work handle\n");
@@ -1543,9 +1542,6 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)

	SDE_DEBUG("crtc%d event:%u ts:%lld\n", crtc->base.id, fevent->event,
			ktime_to_ns(fevent->ts));
	disable_inprogress = fevent->event &
					SDE_ENCODER_FRAME_EVENT_DURING_DISABLE;
	fevent->event &= ~SDE_ENCODER_FRAME_EVENT_DURING_DISABLE;

	if (fevent->event == SDE_ENCODER_FRAME_EVENT_DONE ||
			(fevent->event & SDE_ENCODER_FRAME_EVENT_ERROR) ||
@@ -1566,15 +1562,17 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)
					ktime_to_ns(fevent->ts));
			SDE_EVT32(DRMID(crtc), fevent->event,
							SDE_EVTLOG_FUNC_CASE2);
			if (!disable_inprogress)
			sde_core_perf_crtc_release_bw(crtc);
		} else {
			SDE_EVT32_VERBOSE(DRMID(crtc), fevent->event,
							SDE_EVTLOG_FUNC_CASE3);
		}

		if (fevent->event == SDE_ENCODER_FRAME_EVENT_DONE &&
							!disable_inprogress)
		if (fevent->event == SDE_ENCODER_FRAME_EVENT_DONE ||
			(fevent->event & SDE_ENCODER_FRAME_EVENT_ERROR))
			complete_all(&sde_crtc->frame_done_comp);

		if (fevent->event == SDE_ENCODER_FRAME_EVENT_DONE)
			sde_core_perf_crtc_update(crtc, 0, false);
	} else {
		SDE_ERROR("crtc%d ts:%lld unknown event %u\n", crtc->base.id,
@@ -1629,11 +1627,7 @@ static void sde_crtc_frame_event_cb(void *data, u32 event)
	fevent->event = event;
	fevent->crtc = crtc;
	fevent->ts = ktime_get();
	if (event & SDE_ENCODER_FRAME_EVENT_DURING_DISABLE)
		sde_crtc_frame_event_work(&fevent->work);
	else
		kthread_queue_work(&priv->disp_thread[pipe_id].worker,
								&fevent->work);
	kthread_queue_work(&sde_crtc->event_worker, &fevent->work);
}

void sde_crtc_complete_commit(struct drm_crtc *crtc,
@@ -2085,6 +2079,36 @@ static void sde_crtc_destroy_state(struct drm_crtc *crtc,
			cstate->property_values, cstate->property_blobs);
}

static int _sde_crtc_wait_for_frame_done(struct drm_crtc *crtc)
{
	struct sde_crtc *sde_crtc;
	int ret, rc = 0;

	if (!crtc) {
		SDE_ERROR("invalid argument\n");
		return -EINVAL;
	}
	sde_crtc = to_sde_crtc(crtc);

	if (!atomic_read(&sde_crtc->frame_pending)) {
		SDE_DEBUG("no frames pending\n");
		return 0;
	}

	SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FUNC_ENTRY);
	ret = wait_for_completion_timeout(&sde_crtc->frame_done_comp,
			msecs_to_jiffies(SDE_FRAME_DONE_TIMEOUT));
	if (!ret) {
		SDE_ERROR("frame done completion wait timed out, ret:%d\n",
				ret);
		SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FATAL);
		rc = -ETIMEDOUT;
	}
	SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FUNC_EXIT);

	return rc;
}

void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
{
	struct drm_encoder *encoder;
@@ -2129,19 +2153,21 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
		sde_encoder_prepare_for_kickoff(encoder, &params);
	}

	if (atomic_read(&sde_crtc->frame_pending) > 2) {
		/* framework allows only 1 outstanding + current */
		SDE_ERROR("crtc%d invalid frame pending\n",
				crtc->base.id);
		SDE_EVT32(DRMID(crtc), 0);
	/* wait for frame_event_done completion */
	if (_sde_crtc_wait_for_frame_done(crtc)) {
		SDE_ERROR("crtc%d wait for frame done failed;frame_pending%d\n",
				crtc->base.id,
				atomic_read(&sde_crtc->frame_pending));
		goto end;
	} else if (atomic_inc_return(&sde_crtc->frame_pending) == 1) {
	}

	if (atomic_inc_return(&sde_crtc->frame_pending) == 1) {
		/* acquire bandwidth and other resources */
		SDE_DEBUG("crtc%d first commit\n", crtc->base.id);
		SDE_EVT32(DRMID(crtc), 1);
		SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FUNC_CASE1);
	} else {
		SDE_DEBUG("crtc%d commit\n", crtc->base.id);
		SDE_EVT32(DRMID(crtc), 2);
		SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FUNC_CASE2);
	}
	sde_crtc->play_count++;

@@ -2151,6 +2177,9 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)

		sde_encoder_kickoff(encoder);
	}

	reinit_completion(&sde_crtc->frame_done_comp);

end:
	SDE_ATRACE_END("crtc_commit");
	return;
@@ -2444,6 +2473,12 @@ static void sde_crtc_disable(struct drm_crtc *crtc)
	mutex_lock(&sde_crtc->crtc_lock);
	SDE_EVT32(DRMID(crtc));

	/* wait for frame_event_done completion */
	if (_sde_crtc_wait_for_frame_done(crtc))
		SDE_ERROR("crtc%d wait for frame done failed;frame_pending%d\n",
				crtc->base.id,
				atomic_read(&sde_crtc->frame_pending));

	if (atomic_read(&sde_crtc->vblank_refcount) && !sde_crtc->suspend) {
		SDE_ERROR("crtc%d invalid vblank refcount\n",
				crtc->base.id);
@@ -2455,8 +2490,6 @@ static void sde_crtc_disable(struct drm_crtc *crtc)
	}

	if (atomic_read(&sde_crtc->frame_pending)) {
		/* release bandwidth and other resources */
		SDE_ERROR("crtc%d invalid frame pending\n", crtc->base.id);
		SDE_EVT32(DRMID(crtc), atomic_read(&sde_crtc->frame_pending),
							SDE_EVTLOG_FUNC_CASE2);
		sde_core_perf_crtc_release_bw(crtc);
@@ -3714,6 +3747,8 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
	spin_lock_init(&sde_crtc->spin_lock);
	atomic_set(&sde_crtc->frame_pending, 0);

	init_completion(&sde_crtc->frame_done_comp);

	INIT_LIST_HEAD(&sde_crtc->frame_event_list);
	INIT_LIST_HEAD(&sde_crtc->user_event_list);
	for (i = 0; i < ARRAY_SIZE(sde_crtc->frame_events); i++) {
+2 −0
Original line number Diff line number Diff line
@@ -135,6 +135,7 @@ struct sde_crtc_event {
 * @frame_events  : static allocation of in-flight frame events
 * @frame_event_list : available frame event list
 * @spin_lock     : spin lock for frame event, transaction status, etc...
 * @frame_done_comp    : for frame_event_done synchronization
 * @event_thread  : Pointer to event handler thread
 * @event_worker  : Event worker queue
 * @event_cache   : Local cache of event worker structures
@@ -186,6 +187,7 @@ struct sde_crtc {
	struct sde_crtc_frame_event frame_events[SDE_CRTC_FRAME_EVENT_SIZE];
	struct list_head frame_event_list;
	spinlock_t spin_lock;
	struct completion frame_done_comp;

	/* for handling internal event thread */
	struct task_struct *event_thread;
+2 −15
Original line number Diff line number Diff line
@@ -56,9 +56,6 @@
		(p) ? ((p)->hw_pp ? (p)->hw_pp->idx - PINGPONG_0 : -1) : -1, \
		##__VA_ARGS__)

/* timeout in frames waiting for frame done */
#define SDE_ENCODER_FRAME_DONE_TIMEOUT	60

/*
 * Two to anticipate panels that can do cmd/vid dynamic switching
 * plan is to create all possible physical encoder types, and switch between
@@ -173,7 +170,6 @@ enum sde_enc_rc_states {
 * @rsc_cfg:			rsc configuration
 * @cur_conn_roi:		current connector roi
 * @prv_conn_roi:		previous connector roi to optimize if unchanged
 * @disable_inprogress:		sde encoder disable is in progress.
 */
struct sde_encoder_virt {
	struct drm_encoder base;
@@ -217,7 +213,6 @@ struct sde_encoder_virt {
	struct sde_encoder_rsc_config rsc_cfg;
	struct sde_rect cur_conn_roi;
	struct sde_rect prv_conn_roi;
	bool disable_inprogress;
};

#define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base)
@@ -1643,7 +1638,6 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc)
	SDE_EVT32(DRMID(drm_enc));

	sde_enc->cur_master = NULL;
	sde_enc->disable_inprogress = false;
	for (i = 0; i < sde_enc->num_phys_encs; i++) {
		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];

@@ -1702,7 +1696,6 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc)

	priv = drm_enc->dev->dev_private;
	sde_kms = to_sde_kms(priv->kms);
	sde_enc->disable_inprogress = true;

	SDE_EVT32(DRMID(drm_enc));

@@ -1868,9 +1861,6 @@ static void sde_encoder_frame_done_callback(
		sde_encoder_resource_control(drm_enc,
				SDE_ENC_RC_EVENT_FRAME_DONE);

		if (sde_enc->disable_inprogress)
			event |= SDE_ENCODER_FRAME_EVENT_DURING_DISABLE;

		if (sde_enc->crtc_frame_event_cb)
			sde_enc->crtc_frame_event_cb(
				sde_enc->crtc_frame_event_cb_data, event);
@@ -2332,7 +2322,7 @@ void sde_encoder_kickoff(struct drm_encoder *drm_enc)
	SDE_DEBUG_ENC(sde_enc, "\n");

	atomic_set(&sde_enc->frame_done_timeout,
			SDE_ENCODER_FRAME_DONE_TIMEOUT * 1000 /
			SDE_FRAME_DONE_TIMEOUT * 1000 /
			drm_enc->crtc->state->adjusted_mode.vrefresh);
	mod_timer(&sde_enc->frame_done_timer, jiffies +
		((atomic_read(&sde_enc->frame_done_timeout) * HZ) / 1000));
@@ -2913,9 +2903,6 @@ static void sde_encoder_frame_done_timeout(unsigned long data)
	SDE_ERROR_ENC(sde_enc, "frame done timeout\n");

	event = SDE_ENCODER_FRAME_EVENT_ERROR;
	if (sde_enc->disable_inprogress)
		event |= SDE_ENCODER_FRAME_EVENT_DURING_DISABLE;

	SDE_EVT32(DRMID(drm_enc), event);
	sde_enc->crtc_frame_event_cb(sde_enc->crtc_frame_event_cb_data, event);
}
+0 −1
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@
#define SDE_ENCODER_FRAME_EVENT_DONE		BIT(0)
#define SDE_ENCODER_FRAME_EVENT_ERROR		BIT(1)
#define SDE_ENCODER_FRAME_EVENT_PANEL_DEAD	BIT(2)
#define SDE_ENCODER_FRAME_EVENT_DURING_DISABLE	BIT(3)

/**
 * Encoder functions and data types
+4 −0
Original line number Diff line number Diff line
@@ -88,6 +88,10 @@

#define SDE_NAME_SIZE  12


/* timeout in frames waiting for frame done */
#define SDE_FRAME_DONE_TIMEOUT	60

/*
 * struct sde_irq_callback - IRQ callback handlers
 * @list: list to callback