浏览代码

disp: msm: sde: move vblank signaling to event thread

When precise vsync timestamp feature is enabled, move the vblank
signaling from interrupt context to event thread. This helps in
freeing up the interrupt context soon. The precise vsync timestamp
feature along with DRM hooks to get the vblank timestamp will get
the correct timestamp though the event thread is scheduled later.

Change-Id: I77002913f222ff422b6118f9fc952533065c07aa
Signed-off-by: Veera Sundaram Sankaran <[email protected]>
Veera Sundaram Sankaran 4 年之前
父节点
当前提交
24b4c7cb64
共有 2 个文件被更改,包括 129 次插入13 次删除
  1. 107 9
      msm/sde/sde_crtc.c
  2. 22 4
      msm/sde/sde_crtc.h

+ 107 - 9
msm/sde/sde_crtc.c

@@ -2816,12 +2816,12 @@ static void sde_crtc_frame_event_cb(void *data, u32 event, ktime_t ts)
 	SDE_DEBUG("crtc%d\n", crtc->base.id);
 	SDE_EVT32_VERBOSE(DRMID(crtc), event);
 
-	spin_lock_irqsave(&sde_crtc->fevent_spin_lock, flags);
+	spin_lock_irqsave(&sde_crtc->event_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->fevent_spin_lock, flags);
+	spin_unlock_irqrestore(&sde_crtc->event_spin_lock, flags);
 
 	if (!fevent) {
 		SDE_ERROR("crtc%d event %d overflow\n", DRMID(crtc), event);
@@ -3000,9 +3000,8 @@ struct drm_encoder *sde_crtc_get_src_encoder_of_clone(struct drm_crtc *crtc)
 	return NULL;
 }
 
-static void sde_crtc_vblank_cb(void *data, ktime_t ts)
+static void sde_crtc_vblank_notify(struct drm_crtc *crtc, ktime_t ts)
 {
-	struct drm_crtc *crtc = (struct drm_crtc *)data;
 	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
 
 	/* keep statistics on vblank callback - with auto reset via debugfs */
@@ -3016,7 +3015,76 @@ static void sde_crtc_vblank_cb(void *data, ktime_t ts)
 
 	drm_crtc_handle_vblank(crtc);
 	DRM_DEBUG_VBL("crtc%d, ts:%llu\n", crtc->base.id, ktime_to_us(ts));
-	SDE_EVT32_VERBOSE(DRMID(crtc), ktime_to_us(ts));
+	SDE_EVT32(DRMID(crtc), ktime_to_us(ts));
+}
+
+static void sde_crtc_vblank_notify_work(struct kthread_work *work)
+{
+	struct drm_crtc *crtc;
+	struct sde_crtc *sde_crtc;
+	struct sde_crtc_vblank_event *vevent = container_of(work,
+					struct sde_crtc_vblank_event, work);
+
+	if (!vevent->crtc) {
+		SDE_ERROR("invalid crtc\n");
+		return;
+	}
+
+	crtc = vevent->crtc;
+	sde_crtc = to_sde_crtc(crtc);
+
+	sde_crtc_vblank_notify(vevent->crtc, vevent->ts);
+
+	spin_lock(&sde_crtc->event_spin_lock);
+	list_add_tail(&vevent->list, &sde_crtc->vblank_event_list);
+	spin_unlock(&sde_crtc->event_spin_lock);
+}
+
+static void sde_crtc_vblank_cb(void *data, ktime_t ts)
+{
+	struct drm_crtc *crtc = (struct drm_crtc *)data;
+	struct sde_kms *sde_kms;
+	struct msm_drm_private *priv;
+	int crtc_id = drm_crtc_index(crtc);
+	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
+	struct sde_crtc_vblank_event *vevent;
+	unsigned long flags;
+
+	sde_kms = _sde_crtc_get_kms(crtc);
+	if (!sde_kms) {
+		SDE_ERROR("invalid kms handle\n");
+		return;
+	}
+
+	if (!test_bit(SDE_FEATURE_HW_VSYNC_TS, sde_kms->catalog->features)) {
+		sde_crtc_vblank_notify(crtc, ts);
+		return;
+	}
+
+	spin_lock_irqsave(&sde_crtc->event_spin_lock, flags);
+	vevent = list_first_entry_or_null(&sde_crtc->vblank_event_list,
+			struct sde_crtc_vblank_event, list);
+	if (vevent)
+		list_del_init(&vevent->list);
+	spin_unlock_irqrestore(&sde_crtc->event_spin_lock, flags);
+
+	/*
+	 * schedule vblank notification to event thread when precise vsync
+	 * timestamp feature is supported. This would ensure the vblank hook
+	 * gets the precise hw timestamp even if the event thread is scheduled
+	 * with slight delays
+	 */
+	priv = sde_kms->dev->dev_private;
+
+	if (!vevent) {
+		SDE_ERROR("crtc%d vblank event overflow\n", DRMID(crtc));
+		SDE_EVT32(DRMID(crtc), SDE_EVTLOG_ERROR);
+		return;
+	}
+
+	vevent->ts = ts;
+	vevent->crtc = crtc;
+	kthread_queue_work(&priv->event_thread[crtc_id].worker, &vevent->work);
 }
 
 static void _sde_crtc_retire_event(struct drm_connector *connector,
@@ -3184,9 +3252,9 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)
 		SDE_ERROR("crtc%d ts:%lld received panel dead event\n",
 				crtc->base.id, ktime_to_ns(fevent->ts));
 
-	spin_lock_irqsave(&sde_crtc->fevent_spin_lock, flags);
+	spin_lock_irqsave(&sde_crtc->event_spin_lock, flags);
 	list_add_tail(&fevent->list, &sde_crtc->frame_event_list);
-	spin_unlock_irqrestore(&sde_crtc->fevent_spin_lock, flags);
+	spin_unlock_irqrestore(&sde_crtc->event_spin_lock, flags);
 	SDE_ATRACE_END("crtc_frame_event");
 }
 
@@ -4479,6 +4547,25 @@ static int _sde_crtc_flush_frame_events(struct drm_crtc *crtc)
 	return 0;
 }
 
+static void _sde_crtc_flush_vblank_events(struct drm_crtc *crtc)
+{
+	struct sde_crtc *sde_crtc;
+	int i;
+
+	if (!crtc) {
+		SDE_ERROR("invalid argument\n");
+		return;
+	}
+	sde_crtc = to_sde_crtc(crtc);
+
+	for (i = 0; i < ARRAY_SIZE(sde_crtc->vblank_events); i++) {
+		if (list_empty(&sde_crtc->vblank_events[i].list))
+			kthread_flush_work(&sde_crtc->vblank_events[i].work);
+	}
+
+	SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FUNC_EXIT);
+}
+
 /**
  * _sde_crtc_remove_pipe_flush - remove staged pipes from flush mask
  * @crtc: Pointer to crtc structure
@@ -5164,8 +5251,10 @@ static void sde_crtc_disable(struct drm_crtc *crtc)
 
 	/* avoid vblank on/off for virtual display */
 	intf_mode = sde_crtc_get_intf_mode(crtc, crtc->state);
-	if ((intf_mode != INTF_MODE_WB_BLOCK) && (intf_mode != INTF_MODE_WB_LINE))
+	if ((intf_mode != INTF_MODE_WB_BLOCK) && (intf_mode != INTF_MODE_WB_LINE)) {
+		_sde_crtc_flush_vblank_events(crtc);
 		drm_crtc_vblank_off(crtc);
+	}
 
 	mutex_lock(&sde_crtc->crtc_lock);
 	SDE_EVT32_VERBOSE(DRMID(crtc));
@@ -8017,7 +8106,7 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
 
 	mutex_init(&sde_crtc->crtc_lock);
 	spin_lock_init(&sde_crtc->spin_lock);
-	spin_lock_init(&sde_crtc->fevent_spin_lock);
+	spin_lock_init(&sde_crtc->event_spin_lock);
 	atomic_set(&sde_crtc->frame_pending, 0);
 
 	sde_crtc->enabled = false;
@@ -8044,6 +8133,15 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
 				sde_crtc_frame_event_work);
 	}
 
+	INIT_LIST_HEAD(&sde_crtc->vblank_event_list);
+	for (i = 0; i < ARRAY_SIZE(sde_crtc->vblank_events); i++) {
+		INIT_LIST_HEAD(&sde_crtc->vblank_events[i].list);
+		list_add(&sde_crtc->vblank_events[i].list,
+				&sde_crtc->vblank_event_list);
+		kthread_init_work(&sde_crtc->vblank_events[i].work,
+				sde_crtc_vblank_notify_work);
+	}
+
 	crtc_funcs = test_bit(SDE_FEATURE_HW_VSYNC_TS, kms->catalog->features) ?
 			&sde_crtc_funcs_v1 : &sde_crtc_funcs;
 	drm_crtc_init_with_planes(dev, crtc, plane, NULL, crtc_funcs, NULL);

+ 22 - 4
msm/sde/sde_crtc.h

@@ -36,7 +36,7 @@
 
 /* define the maximum number of in-flight frame events */
 /* Expand it to 2x for handling atleast 2 connectors safely */
-#define SDE_CRTC_FRAME_EVENT_SIZE	(4 * 2)
+#define SDE_CRTC_EVENT_SIZE	(4 * 2)
 
 /**
  * enum sde_crtc_client_type: crtc client type
@@ -135,6 +135,20 @@ struct sde_crtc_frame_event {
 	u32 event;
 };
 
+/**
+ * struct sde_crtc_vblank_event: vblank notify event
+ * @work:	base work structure
+ * @crtc:	Pointer to crtc handling this event
+ * @list:	vblank list
+ * @ts:		vblank timestamp
+ */
+struct sde_crtc_vblank_event {
+	struct kthread_work work;
+	struct drm_crtc *crtc;
+	struct list_head list;
+	ktime_t ts;
+};
+
 /**
  * struct sde_crtc_event - event callback tracking structure
  * @list:     Linked list tracking node
@@ -299,8 +313,10 @@ enum sde_crtc_hw_fence_flags {
  * @kickoff_in_progress : boolean entry to check if kickoff is in progress
  * @frame_events  : static allocation of in-flight frame events
  * @frame_event_list : available frame event list
+ * @vblank_events  : static allocation of in-flight vblank events
+ * @vblank_event_list : available vblank event list
  * @spin_lock     : spin lock for transaction status, etc...
- * @fevent_spin_lock     : spin lock for frame event
+ * @event_spin_lock     : spin lock for all the event lists
  * @event_thread  : Pointer to event handler thread
  * @event_worker  : Event worker queue
  * @event_cache   : Local cache of event worker structures
@@ -395,10 +411,12 @@ struct sde_crtc {
 	struct mutex crtc_cp_lock;
 
 	atomic_t frame_pending;
-	struct sde_crtc_frame_event frame_events[SDE_CRTC_FRAME_EVENT_SIZE];
+	struct sde_crtc_frame_event frame_events[SDE_CRTC_EVENT_SIZE];
 	struct list_head frame_event_list;
+	struct sde_crtc_vblank_event vblank_events[SDE_CRTC_EVENT_SIZE];
+	struct list_head vblank_event_list;
 	spinlock_t spin_lock;
-	spinlock_t fevent_spin_lock;
+	spinlock_t event_spin_lock;
 	bool kickoff_in_progress;
 	unsigned long revalidate_mask;