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

Commit 5023c3cc authored by Dhaval Patel's avatar Dhaval Patel
Browse files

drm/msm/sde: store connector list associated with commit state



DRM framework pushes the commit with state and attaches
all the connectors and crtc associated with this state. SDE
crtc module resets the connectors associated with crtc during
this state processing (prepare_commit) phase. A retire fence
might triggered at the same time and it can try to access this
resent connector list. Such race condition will lead to fence
timeline update skip. Such cases can be avoided by storing
connector list into crtc object and retrieve it when fence
is ready to signal.

Change-Id: I87347a745413b422d26187000c1773ed7088002a
Signed-off-by: default avatarDhaval Patel <pdhaval@codeaurora.org>
parent fd8f774f
Loading
Loading
Loading
Loading
+66 −10
Original line number Diff line number Diff line
@@ -1873,6 +1873,9 @@ void sde_crtc_prepare_commit(struct drm_crtc *crtc,
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *cstate;
	struct drm_connector *conn;
	struct sde_crtc_retire_event *retire_event = NULL;
	unsigned long flags;
	int i;

	if (!crtc || !crtc->state) {
		SDE_ERROR("invalid crtc\n");
@@ -1893,6 +1896,27 @@ void sde_crtc_prepare_commit(struct drm_crtc *crtc,
			sde_connector_prepare_fence(conn);
		}

	for (i = 0; i < SDE_CRTC_FRAME_EVENT_SIZE; i++) {
		retire_event = &sde_crtc->retire_events[i];
		if (list_empty(&retire_event->list))
			break;
		retire_event = NULL;
	}

	if (retire_event) {
		retire_event->num_connectors = cstate->num_connectors;
		for (i = 0; i < cstate->num_connectors; i++)
			retire_event->connectors[i] = cstate->connectors[i];

		spin_lock_irqsave(&sde_crtc->spin_lock, flags);
		list_add_tail(&retire_event->list,
						&sde_crtc->retire_event_list);
		spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
	} else {
		SDE_ERROR("crtc%d retire event overflow\n", crtc->base.id);
		SDE_EVT32(DRMID(crtc), SDE_EVTLOG_ERROR);
	}

	/* prepare main output fence */
	sde_fence_prepare(&sde_crtc->output_fence);
}
@@ -1966,17 +1990,50 @@ static void sde_crtc_vblank_cb(void *data)
	SDE_EVT32_VERBOSE(DRMID(crtc));
}

static void _sde_crtc_retire_event(struct drm_crtc *crtc, ktime_t ts)
{
	struct sde_crtc_retire_event *retire_event;
	struct sde_crtc *sde_crtc;
	unsigned long flags;
	int i;

	if (!crtc) {
		SDE_ERROR("invalid param\n");
		return;
	}

	sde_crtc = to_sde_crtc(crtc);
	spin_lock_irqsave(&sde_crtc->spin_lock, flags);
	retire_event = list_first_entry_or_null(&sde_crtc->retire_event_list,
				struct sde_crtc_retire_event, list);
	if (retire_event)
		list_del_init(&retire_event->list);
	spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);

	if (!retire_event) {
		SDE_ERROR("crtc%d retire event without kickoff\n",
								crtc->base.id);
		SDE_EVT32(DRMID(crtc), SDE_EVTLOG_ERROR);
		return;
	}

	SDE_ATRACE_BEGIN("signal_retire_fence");
	for (i = 0; (i < retire_event->num_connectors) &&
					retire_event->connectors[i]; ++i)
		sde_connector_complete_commit(
					retire_event->connectors[i], ts);
	SDE_ATRACE_END("signal_retire_fence");
}

static void sde_crtc_frame_event_work(struct kthread_work *work)
{
	struct msm_drm_private *priv;
	struct sde_crtc_frame_event *fevent;
	struct drm_crtc *crtc;
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *cstate;
	struct sde_kms *sde_kms;
	unsigned long flags;
	bool frame_done = false;
	int i;

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

	crtc = fevent->crtc;
	sde_crtc = to_sde_crtc(crtc);
	cstate = to_sde_crtc_state(crtc->state);

	sde_kms = _sde_crtc_get_kms(crtc);
	if (!sde_kms) {
@@ -2045,13 +2101,9 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)
		SDE_ATRACE_END("signal_release_fence");
	}

	if (fevent->event & SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE) {
		SDE_ATRACE_BEGIN("signal_retire_fence");
		for (i = 0; i < cstate->num_connectors; ++i)
			sde_connector_complete_commit(cstate->connectors[i],
					fevent->ts);
		SDE_ATRACE_END("signal_retire_fence");
	}
	if (fevent->event & SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE)
		/* this api should be called without spin_lock */
		_sde_crtc_retire_event(crtc, fevent->ts);

	if (fevent->event & SDE_ENCODER_FRAME_EVENT_PANEL_DEAD)
		SDE_ERROR("crtc%d ts:%lld received panel dead event\n",
@@ -4441,6 +4493,10 @@ static int _sde_crtc_init_events(struct sde_crtc *sde_crtc)
		list_add_tail(&sde_crtc->event_cache[i].list,
				&sde_crtc->event_free_list);

	INIT_LIST_HEAD(&sde_crtc->retire_event_list);
	for (i = 0; i < ARRAY_SIZE(sde_crtc->retire_events); i++)
		INIT_LIST_HEAD(&sde_crtc->retire_events[i].list);

	return rc;
}

+15 −0
Original line number Diff line number Diff line
@@ -90,6 +90,17 @@ struct sde_crtc_smmu_state_data {
	uint32_t transition_error;
};

/**
 * @connectors    : Currently associated drm connectors for retire event
 * @num_connectors: Number of associated drm connectors for retire event
 * @list:	event list
 */
struct sde_crtc_retire_event {
	struct drm_connector *connectors[MAX_CONNECTORS];
	int num_connectors;
	struct list_head list;
};

/**
 * struct sde_crtc_mixer: stores the map for each virtual pipeline in the CRTC
 * @hw_lm:	LM HW Driver context
@@ -182,6 +193,8 @@ 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...
 * @retire_events  : static allocation of retire fence connector
 * @retire_event_list : available retire fence connector list
 * @frame_done_comp    : for frame_event_done synchronization
 * @event_thread  : Pointer to event handler thread
 * @event_worker  : Event worker queue
@@ -239,6 +252,8 @@ 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 sde_crtc_retire_event retire_events[SDE_CRTC_FRAME_EVENT_SIZE];
	struct list_head retire_event_list;
	struct completion frame_done_comp;

	/* for handling internal event thread */