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

Commit d6dc46eb authored by Prabhanjan Kandula's avatar Prabhanjan Kandula Committed by Gerrit - the friendly Code Review server
Browse files

drm/msm/sde: extend frame retire event handling



Simplify retire event by avoiding special handling and extend
frame event to identify the connector or encoder firing the
frame event. As crtc can be really driving multiple connectors
whose frame events are not same or synchronized, this isolation
of events per connector is required to handle fences.

Change-Id: I2e82ae2288da4206afa197749aa57a2621b4d3f7
Signed-off-by: default avatarPrabhanjan Kandula <pkandula@codeaurora.org>
parent 99771a53
Loading
Loading
Loading
Loading
+68 −100
Original line number Diff line number Diff line
@@ -2126,6 +2126,55 @@ static void _sde_crtc_dest_scaler_setup(struct drm_crtc *crtc)
	}
}

static void sde_crtc_frame_event_cb(void *data, u32 event)
{
	struct drm_crtc *crtc = (struct drm_crtc *)data;
	struct sde_crtc *sde_crtc;
	struct msm_drm_private *priv;
	struct sde_crtc_frame_event *fevent;
	struct sde_crtc_frame_event_cb_data *cb_data;
	unsigned long flags;
	u32 crtc_id;

	cb_data = (struct sde_crtc_frame_event_cb_data *)data;
	if (!data) {
		SDE_ERROR("invalid parameters\n");
		return;
	}

	crtc = cb_data->crtc;
	if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
		SDE_ERROR("invalid parameters\n");
		return;
	}
	sde_crtc = to_sde_crtc(crtc);
	priv = crtc->dev->dev_private;
	crtc_id = drm_crtc_index(crtc);

	SDE_DEBUG("crtc%d\n", crtc->base.id);
	SDE_EVT32_VERBOSE(DRMID(crtc), event);

	spin_lock_irqsave(&sde_crtc->spin_lock, flags);
	fevent = list_first_entry_or_null(&sde_crtc->frame_event_list,
			struct sde_crtc_frame_event, list);
	if (fevent)
		list_del_init(&fevent->list);
	spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);

	if (!fevent) {
		SDE_ERROR("crtc%d event %d overflow\n",
				crtc->base.id, event);
		SDE_EVT32(DRMID(crtc), event);
		return;
	}

	fevent->event = event;
	fevent->crtc = crtc;
	fevent->connector = cb_data->connector;
	fevent->ts = ktime_get();
	kthread_queue_work(&priv->event_thread[crtc_id].worker, &fevent->work);
}

void sde_crtc_prepare_commit(struct drm_crtc *crtc,
		struct drm_crtc_state *old_state)
{
@@ -2133,10 +2182,8 @@ void sde_crtc_prepare_commit(struct drm_crtc *crtc,
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *cstate;
	struct drm_connector *conn;
	struct drm_encoder *encoder;
	struct drm_connector_list_iter conn_iter;
	struct sde_crtc_retire_event *retire_event = NULL;
	unsigned long flags;
	int i;

	if (!crtc || !crtc->state) {
		SDE_ERROR("invalid crtc\n");
@@ -2155,32 +2202,18 @@ void sde_crtc_prepare_commit(struct drm_crtc *crtc,
	drm_for_each_connector_iter(conn, &conn_iter)
		if (conn->state && conn->state->crtc == crtc &&
				cstate->num_connectors < MAX_CONNECTORS) {
			encoder = conn->state->best_encoder;
			if (encoder)
				sde_encoder_register_frame_event_callback(
						encoder,
						sde_crtc_frame_event_cb,
						crtc);

			cstate->connectors[cstate->num_connectors++] = conn;
			sde_connector_prepare_fence(conn);
		}
	drm_connector_list_iter_end(&conn_iter);

	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);
}
@@ -2258,39 +2291,16 @@ 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,
		bool is_error)
static void _sde_crtc_retire_event(struct drm_connector *connector,
		ktime_t ts, bool is_error)
{
	struct sde_crtc_retire_event *retire_event;
	struct sde_crtc *sde_crtc;
	unsigned long flags;
	int i;

	if (!crtc) {
	if (!connector) {
		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, is_error);
	sde_connector_complete_commit(connector, ts, is_error);
	SDE_ATRACE_END("signal_retire_fence");
}

@@ -2302,6 +2312,7 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)
	struct sde_crtc *sde_crtc;
	struct sde_kms *sde_kms;
	unsigned long flags;
	bool in_clone_mode = false;

	if (!work) {
		SDE_ERROR("invalid work handle\n");
@@ -2330,10 +2341,11 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)

	SDE_EVT32_VERBOSE(DRMID(crtc), fevent->event, SDE_EVTLOG_FUNC_ENTRY);

	if (fevent->event & (SDE_ENCODER_FRAME_EVENT_DONE
				| SDE_ENCODER_FRAME_EVENT_ERROR
				| SDE_ENCODER_FRAME_EVENT_PANEL_DEAD)) {
	in_clone_mode = sde_encoder_in_clone_mode(fevent->connector->encoder);

	if (!in_clone_mode && (fevent->event & (SDE_ENCODER_FRAME_EVENT_ERROR
					| SDE_ENCODER_FRAME_EVENT_PANEL_DEAD
					| SDE_ENCODER_FRAME_EVENT_DONE))) {
		if (atomic_read(&sde_crtc->frame_pending) < 1) {
			/* this should not happen */
			SDE_ERROR("crtc%d ts:%lld invalid frame_pending:%d\n",
@@ -2366,7 +2378,7 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)

	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,
		_sde_crtc_retire_event(fevent->connector, fevent->ts,
				(fevent->event & SDE_ENCODER_FRAME_EVENT_ERROR)
				? SDE_FENCE_SIGNAL_ERROR : SDE_FENCE_SIGNAL);

@@ -2380,46 +2392,6 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)
	SDE_ATRACE_END("crtc_frame_event");
}

static void sde_crtc_frame_event_cb(void *data, u32 event)
{
	struct drm_crtc *crtc = (struct drm_crtc *)data;
	struct sde_crtc *sde_crtc;
	struct msm_drm_private *priv;
	struct sde_crtc_frame_event *fevent;
	unsigned long flags;
	u32 crtc_id;

	if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
		SDE_ERROR("invalid parameters\n");
		return;
	}
	sde_crtc = to_sde_crtc(crtc);
	priv = crtc->dev->dev_private;
	crtc_id = drm_crtc_index(crtc);

	SDE_DEBUG("crtc%d\n", crtc->base.id);
	SDE_EVT32_VERBOSE(DRMID(crtc), event);

	spin_lock_irqsave(&sde_crtc->spin_lock, flags);
	fevent = list_first_entry_or_null(&sde_crtc->frame_event_list,
			struct sde_crtc_frame_event, list);
	if (fevent)
		list_del_init(&fevent->list);
	spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);

	if (!fevent) {
		SDE_ERROR("crtc%d event %d overflow\n",
				crtc->base.id, event);
		SDE_EVT32(DRMID(crtc), event);
		return;
	}

	fevent->event = event;
	fevent->crtc = crtc;
	fevent->ts = ktime_get();
	kthread_queue_work(&priv->event_thread[crtc_id].worker, &fevent->work);
}

void sde_crtc_complete_commit(struct drm_crtc *crtc,
		struct drm_crtc_state *old_state)
{
@@ -4270,7 +4242,7 @@ static void sde_crtc_enable(struct drm_crtc *crtc,
		if (encoder->crtc != crtc)
			continue;
		sde_encoder_register_frame_event_callback(encoder,
				sde_crtc_frame_event_cb, (void *)crtc);
				sde_crtc_frame_event_cb, crtc);
	}

	if (!sde_crtc->enabled && !sde_crtc->suspend &&
@@ -5934,10 +5906,6 @@ 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;
}

+14 −5
Original line number Diff line number Diff line
@@ -31,7 +31,8 @@
#define SDE_CRTC_NAME_SIZE	12

/* define the maximum number of in-flight frame events */
#define SDE_CRTC_FRAME_EVENT_SIZE	4
/* Expand it to 2x for handling atleast 2 connectors safely */
#define SDE_CRTC_FRAME_EVENT_SIZE	(4 * 2)

/**
 * enum sde_crtc_client_type: crtc client type
@@ -76,10 +77,21 @@ struct sde_crtc_mixer {
	u32 mixer_op_mode;
};

/**
 * struct sde_crtc_frame_event_cb_data : info of drm objects of a frame event
 * @crtc:       pointer to drm crtc object registered for frame event
 * @connector:  pointer to drm connector which is source of frame event
 */
struct sde_crtc_frame_event_cb_data {
	 struct drm_crtc *crtc;
	 struct drm_connector *connector;
};

/**
 * struct sde_crtc_frame_event: stores crtc frame event for crtc processing
 * @work:	base work structure
 * @crtc:	Pointer to crtc handling this event
 * @connector:  pointer to drm connector which is source of frame event
 * @list:	event list
 * @ts:		timestamp at queue entry
 * @event:	event identifier
@@ -87,6 +99,7 @@ struct sde_crtc_mixer {
struct sde_crtc_frame_event {
	struct kthread_work work;
	struct drm_crtc *crtc;
	struct drm_connector *connector;
	struct list_head list;
	ktime_t ts;
	u32 event;
@@ -154,8 +167,6 @@ 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
 * @event_thread  : Pointer to event handler thread
 * @event_worker  : Event worker queue
 * @event_cache   : Local cache of event worker structures
@@ -222,8 +233,6 @@ 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;

	/* for handling internal event thread */
	struct sde_crtc_event event_cache[SDE_CRTC_MAX_EVENT_COUNT];
+17 −6
Original line number Diff line number Diff line
@@ -240,7 +240,7 @@ struct sde_encoder_virt {
	struct mutex enc_lock;
	DECLARE_BITMAP(frame_busy_mask, MAX_PHYS_ENCODERS_PER_VIRTUAL);
	void (*crtc_frame_event_cb)(void *, u32 event);
	void *crtc_frame_event_cb_data;
	struct sde_crtc_frame_event_cb_data crtc_frame_event_cb_data;

	struct timer_list vsync_event_timer;

@@ -425,6 +425,14 @@ bool sde_encoder_is_dsc_merge(struct drm_encoder *drm_enc)
	return false;
}

int sde_encoder_in_clone_mode(struct drm_encoder *drm_enc)
{
	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);

	return sde_enc && sde_enc->cur_master &&
		sde_enc->cur_master->in_clone_mode;
}

static inline int _sde_encoder_power_enable(struct sde_encoder_virt *sde_enc,
								bool enable)
{
@@ -3065,7 +3073,7 @@ void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc,

void sde_encoder_register_frame_event_callback(struct drm_encoder *drm_enc,
			void (*frame_event_cb)(void *, u32 event),
		void *frame_event_cb_data)
			struct drm_crtc *crtc)
{
	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
	unsigned long lock_flags;
@@ -3082,7 +3090,7 @@ void sde_encoder_register_frame_event_callback(struct drm_encoder *drm_enc,

	spin_lock_irqsave(&sde_enc->enc_spinlock, lock_flags);
	sde_enc->crtc_frame_event_cb = frame_event_cb;
	sde_enc->crtc_frame_event_cb_data = frame_event_cb_data;
	sde_enc->crtc_frame_event_cb_data.crtc = crtc;
	spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags);
}

@@ -3093,6 +3101,9 @@ static void sde_encoder_frame_done_callback(
	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
	unsigned int i;

	sde_enc->crtc_frame_event_cb_data.connector =
				sde_enc->cur_master->connector;

	if (event & (SDE_ENCODER_FRAME_EVENT_DONE
			| SDE_ENCODER_FRAME_EVENT_ERROR
			| SDE_ENCODER_FRAME_EVENT_PANEL_DEAD)) {
@@ -3121,13 +3132,13 @@ static void sde_encoder_frame_done_callback(

			if (sde_enc->crtc_frame_event_cb)
				sde_enc->crtc_frame_event_cb(
					sde_enc->crtc_frame_event_cb_data,
					&sde_enc->crtc_frame_event_cb_data,
					event);
		}
	} else {
		if (sde_enc->crtc_frame_event_cb)
			sde_enc->crtc_frame_event_cb(
				sde_enc->crtc_frame_event_cb_data, event);
				&sde_enc->crtc_frame_event_cb_data, event);
	}
}

+9 −2
Original line number Diff line number Diff line
@@ -99,10 +99,10 @@ void sde_encoder_register_vblank_callback(struct drm_encoder *encoder,
 *	will be called after the request is complete, or other events.
 * @encoder:	encoder pointer
 * @cb:		callback pointer, provide NULL to deregister
 * @data:	user data provided to callback
 * @crtc:	pointer to drm_crtc object interested in frame events
 */
void sde_encoder_register_frame_event_callback(struct drm_encoder *encoder,
		void (*cb)(void *, u32), void *data);
		void (*cb)(void *, u32), struct drm_crtc *crtc);

/**
 * sde_encoder_get_rsc_client - gets the rsc client state for primary
@@ -269,5 +269,12 @@ bool sde_encoder_recovery_events_enabled(struct drm_encoder *encoder);
 */
void sde_encoder_recovery_events_handler(struct drm_encoder *encoder,
		bool val);
/**
 * sde_encoder_in_clone_mode - checks if underlying phys encoder is in clone
 *	mode or independent display mode. ref@ WB in Concurrent writeback mode.
 * @drm_enc:    Pointer to drm encoder structure
 * @Return:     true if successful in updating the encoder structure
 */
int sde_encoder_in_clone_mode(struct drm_encoder *enc);

#endif /* __SDE_ENCODER_H__ */
+2 −0
Original line number Diff line number Diff line
@@ -274,6 +274,7 @@ struct sde_encoder_irq {
 * @has_intf_te:		Interface TE configuration support
 * @cont_splash_single_flush	Variable to check if single flush is enabled.
 * @cont_splash_settings	Variable to store continuous splash settings.
 * @in_clone_mode		Indicates if encoder is in clone mode ref@CWB
 */
struct sde_encoder_phys {
	struct drm_encoder *parent;
@@ -307,6 +308,7 @@ struct sde_encoder_phys {
	bool has_intf_te;
	u32 cont_splash_single_flush;
	bool cont_splash_settings;
	bool in_clone_mode;
};

static inline int sde_encoder_phys_inc_pending(struct sde_encoder_phys *phys)