Merge "drm/msm/sde: add frame done callback event to crtc" into msm-4.8
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index a05f566..b0bde45 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -478,6 +478,111 @@
SDE_EVT32_IRQ(DRMID(crtc));
}
+static void sde_crtc_frame_event_work(struct kthread_work *work)
+{
+ struct sde_crtc_frame_event *fevent;
+ struct drm_crtc *crtc;
+ struct sde_crtc *sde_crtc;
+ struct sde_kms *sde_kms;
+ unsigned long flags;
+
+ if (!work) {
+ SDE_ERROR("invalid work handle\n");
+ return;
+ }
+
+ fevent = container_of(work, struct sde_crtc_frame_event, work);
+ if (!fevent->crtc) {
+ SDE_ERROR("invalid crtc\n");
+ return;
+ }
+
+ crtc = fevent->crtc;
+ sde_crtc = to_sde_crtc(crtc);
+
+ sde_kms = _sde_crtc_get_kms(crtc);
+ if (!sde_kms) {
+ SDE_ERROR("invalid kms handle\n");
+ return;
+ }
+
+ SDE_DEBUG("crtc%d event:%u ts:%lld\n", crtc->base.id, fevent->event,
+ ktime_to_ns(fevent->ts));
+
+ if (fevent->event == SDE_ENCODER_FRAME_EVENT_DONE ||
+ fevent->event == SDE_ENCODER_FRAME_EVENT_ERROR) {
+
+ if (atomic_read(&sde_crtc->frame_pending) < 1) {
+ /* this should not happen */
+ SDE_ERROR("crtc%d ts:%lld invalid frame_pending:%d\n",
+ crtc->base.id,
+ ktime_to_ns(fevent->ts),
+ atomic_read(&sde_crtc->frame_pending));
+ SDE_EVT32(DRMID(crtc), fevent->event, 0);
+ } else if (atomic_dec_return(&sde_crtc->frame_pending) == 0) {
+ /* release bandwidth and other resources */
+ SDE_DEBUG("crtc%d ts:%lld last pending\n",
+ crtc->base.id,
+ ktime_to_ns(fevent->ts));
+ SDE_EVT32(DRMID(crtc), fevent->event, 1);
+ } else {
+ SDE_EVT32(DRMID(crtc), fevent->event, 2);
+ }
+ } else {
+ SDE_ERROR("crtc%d ts:%lld unknown event %u\n", crtc->base.id,
+ ktime_to_ns(fevent->ts),
+ fevent->event);
+ SDE_EVT32(DRMID(crtc), fevent->event, 3);
+ }
+
+ spin_lock_irqsave(&sde_crtc->spin_lock, flags);
+ list_add_tail(&fevent->list, &sde_crtc->frame_event_list);
+ spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
+}
+
+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 list_head *list, *next;
+ struct sde_crtc_frame_event *fevent;
+ unsigned long flags;
+ int pipe_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;
+ pipe_id = drm_crtc_index(crtc);
+
+ SDE_DEBUG("crtc%d\n", crtc->base.id);
+
+ SDE_EVT32(DRMID(crtc), event);
+
+ spin_lock_irqsave(&sde_crtc->spin_lock, flags);
+ list_for_each_safe(list, next, &sde_crtc->frame_event_list) {
+ list_del_init(list);
+ break;
+ }
+ spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
+
+ if (!list) {
+ SDE_ERROR("crtc%d event %d overflow\n",
+ crtc->base.id, event);
+ SDE_EVT32(DRMID(crtc), event);
+ return;
+ }
+
+ fevent = container_of(list, struct sde_crtc_frame_event, list);
+ fevent->event = event;
+ fevent->crtc = crtc;
+ fevent->ts = ktime_get();
+ kthread_queue_work(&priv->disp_thread[pipe_id].worker, &fevent->work);
+}
+
void sde_crtc_complete_commit(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
@@ -839,12 +944,14 @@
{
struct drm_encoder *encoder;
struct drm_device *dev;
+ struct sde_crtc *sde_crtc;
if (!crtc) {
SDE_ERROR("invalid argument\n");
return;
}
dev = crtc->dev;
+ sde_crtc = to_sde_crtc(crtc);
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (encoder->crtc != crtc)
@@ -854,7 +961,29 @@
* Encoder will flush/start now, unless it has a tx pending.
* If so, it may delay and flush at an irq event (e.g. ppdone)
*/
- sde_encoder_schedule_kickoff(encoder);
+ sde_encoder_prepare_for_kickoff(encoder);
+ }
+
+ 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);
+ return;
+ } else 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);
+ } else {
+ SDE_DEBUG("crtc%d commit\n", crtc->base.id);
+ SDE_EVT32(DRMID(crtc), 2);
+ }
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (encoder->crtc != crtc)
+ continue;
+
+ sde_encoder_kickoff(encoder);
}
}
@@ -945,10 +1074,12 @@
SDE_DEBUG("crtc%d\n", crtc->base.id);
mutex_lock(&sde_crtc->crtc_lock);
+ SDE_EVT32(DRMID(crtc));
+
if (atomic_read(&sde_crtc->vblank_refcount)) {
- SDE_ERROR("crtc%d invalid vblank refcount %d\n",
- crtc->base.id,
- atomic_read(&sde_crtc->vblank_refcount));
+ SDE_ERROR("crtc%d invalid vblank refcount\n",
+ crtc->base.id);
+ SDE_EVT32(DRMID(crtc));
drm_for_each_encoder(encoder, crtc->dev) {
if (encoder->crtc != crtc)
continue;
@@ -958,6 +1089,20 @@
atomic_set(&sde_crtc->vblank_refcount, 0);
}
+ 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_set(&sde_crtc->frame_pending, 0);
+ }
+
+ drm_for_each_encoder(encoder, crtc->dev) {
+ if (encoder->crtc != crtc)
+ continue;
+ sde_encoder_register_frame_event_callback(encoder, NULL, NULL);
+ }
+
memset(sde_crtc->mixers, 0, sizeof(sde_crtc->mixers));
sde_crtc->num_mixers = 0;
mutex_unlock(&sde_crtc->crtc_lock);
@@ -970,6 +1115,7 @@
struct sde_hw_mixer *lm;
struct drm_display_mode *mode;
struct sde_hw_mixer_cfg cfg;
+ struct drm_encoder *encoder;
int i;
if (!crtc) {
@@ -978,6 +1124,7 @@
}
SDE_DEBUG("crtc%d\n", crtc->base.id);
+ SDE_EVT32(DRMID(crtc));
sde_crtc = to_sde_crtc(crtc);
mixer = sde_crtc->mixers;
@@ -989,6 +1136,13 @@
drm_mode_debug_printmodeline(mode);
+ drm_for_each_encoder(encoder, crtc->dev) {
+ if (encoder->crtc != crtc)
+ continue;
+ sde_encoder_register_frame_event_callback(encoder,
+ sde_crtc_frame_event_cb, (void *)crtc);
+ }
+
for (i = 0; i < sde_crtc->num_mixers; i++) {
lm = mixer[i].hw_lm;
cfg.out_width = sde_crtc_mixer_width(sde_crtc, mode);
@@ -1557,6 +1711,7 @@
struct sde_crtc *sde_crtc = NULL;
struct msm_drm_private *priv = NULL;
struct sde_kms *kms = NULL;
+ int i;
priv = dev->dev_private;
kms = to_sde_kms(priv->kms);
@@ -1569,6 +1724,18 @@
crtc->dev = dev;
atomic_set(&sde_crtc->vblank_refcount, 0);
+ spin_lock_init(&sde_crtc->spin_lock);
+ atomic_set(&sde_crtc->frame_pending, 0);
+
+ INIT_LIST_HEAD(&sde_crtc->frame_event_list);
+ for (i = 0; i < ARRAY_SIZE(sde_crtc->frame_events); i++) {
+ INIT_LIST_HEAD(&sde_crtc->frame_events[i].list);
+ list_add(&sde_crtc->frame_events[i].list,
+ &sde_crtc->frame_event_list);
+ kthread_init_work(&sde_crtc->frame_events[i].work,
+ sde_crtc_frame_event_work);
+ }
+
drm_crtc_init_with_planes(dev, crtc, plane, NULL, &sde_crtc_funcs,
NULL);
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 8ad9685..d06955d 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -26,6 +26,9 @@
#define SDE_CRTC_NAME_SIZE 12
+/* define the maximum number of in-flight frame events */
+#define SDE_CRTC_FRAME_EVENT_SIZE 2
+
/**
* struct sde_crtc_mixer: stores the map for each virtual pipeline in the CRTC
* @hw_lm: LM HW Driver context
@@ -45,6 +48,22 @@
};
/**
+ * struct sde_crtc_frame_event: stores crtc frame event for crtc processing
+ * @work: base work structure
+ * @crtc: Pointer to crtc handling this event
+ * @list: event list
+ * @ts: timestamp at queue entry
+ * @event: event identifier
+ */
+struct sde_crtc_frame_event {
+ struct kthread_work work;
+ struct drm_crtc *crtc;
+ struct list_head list;
+ ktime_t ts;
+ u32 event;
+};
+
+/**
* struct sde_crtc - virtualized CRTC data structure
* @base : Base drm crtc structure
* @name : ASCII description of this crtc
@@ -53,7 +72,6 @@
* @mixer : List of active mixers
* @event : Pointer to last received drm vblank event. If there is a
* pending vblank event, this will be non-null.
- * @pending : Whether or not an update is pending
* @vsync_count : Running count of received vsync events
* @drm_requested_vblank : Whether vblanks have been enabled in the encoder
* @property_info : Opaque structure for generic property support
@@ -67,6 +85,10 @@
* @active_list : list of color processing features are active
* @dirty_list : list of color processing features are dirty
* @crtc_lock : crtc lock around create, destroy and access.
+ * @frame_pending : Whether or not an update is pending
+ * @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...
*/
struct sde_crtc {
struct drm_crtc base;
@@ -99,6 +121,11 @@
struct list_head dirty_list;
struct mutex crtc_lock;
+
+ atomic_t frame_pending;
+ struct sde_crtc_frame_event frame_events[SDE_CRTC_FRAME_EVENT_SIZE];
+ struct list_head frame_event_list;
+ spinlock_t spin_lock;
};
#define to_sde_crtc(x) container_of(x, struct sde_crtc, base)
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index b7b8a15..ebae360 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -39,6 +39,9 @@
#define SDE_ERROR_ENC(e, fmt, ...) SDE_ERROR("enc%d " fmt,\
(e) ? (e)->base.base.id : -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
@@ -75,6 +78,14 @@
* @debugfs_root: Debug file system root file node
* @enc_lock: Lock around physical encoder create/destroy and
access.
+ * @frame_busy_mask: Bitmask tracking which phys_enc we are still
+ * busy processing current command.
+ * Bit0 = phys_encs[0] etc.
+ * @crtc_frame_event_cb: callback handler for frame event
+ * @crtc_frame_event_cb_data: callback handler private data
+ * @crtc_frame_event: callback event
+ * @frame_done_timeout: frame done timeout in Hz
+ * @frame_done_timer: watchdog timer for frame done event
*/
struct sde_encoder_virt {
struct drm_encoder base;
@@ -93,6 +104,13 @@
struct dentry *debugfs_root;
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;
+ u32 crtc_frame_event;
+
+ atomic_t frame_done_timeout;
+ struct timer_list frame_done_timer;
};
#define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base)
@@ -511,6 +529,11 @@
SDE_EVT32(DRMID(drm_enc));
+ if (atomic_xchg(&sde_enc->frame_done_timeout, 0)) {
+ SDE_ERROR("enc%d timeout pending\n", drm_enc->base.id);
+ del_timer_sync(&sde_enc->frame_done_timer);
+ }
+
for (i = 0; i < sde_enc->num_phys_encs; i++) {
struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
@@ -627,6 +650,56 @@
}
}
+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 sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
+ unsigned long lock_flags;
+ bool enable;
+
+ enable = frame_event_cb ? true : false;
+
+ if (!drm_enc) {
+ SDE_ERROR("invalid encoder\n");
+ return;
+ }
+ SDE_DEBUG_ENC(sde_enc, "\n");
+ SDE_EVT32(DRMID(drm_enc), enable, 0);
+
+ 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;
+ spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags);
+}
+
+static void sde_encoder_frame_done_callback(
+ struct drm_encoder *drm_enc,
+ struct sde_encoder_phys *ready_phys, u32 event)
+{
+ struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
+ unsigned int i;
+
+ /* One of the physical encoders has become idle */
+ for (i = 0; i < sde_enc->num_phys_encs; i++)
+ if (sde_enc->phys_encs[i] == ready_phys) {
+ clear_bit(i, sde_enc->frame_busy_mask);
+ sde_enc->crtc_frame_event |= event;
+ SDE_EVT32(DRMID(drm_enc), i,
+ sde_enc->frame_busy_mask[0]);
+ }
+
+ if (!sde_enc->frame_busy_mask[0]) {
+ atomic_set(&sde_enc->frame_done_timeout, 0);
+ del_timer(&sde_enc->frame_done_timer);
+
+ 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);
+ }
+}
+
/**
* _sde_encoder_trigger_flush - trigger flush for a physical encoder
* drm_enc: Pointer to drm encoder structure
@@ -742,6 +815,7 @@
}
pending_flush = 0x0;
+ sde_enc->crtc_frame_event = 0;
/* update pending counts and trigger kickoff ctl flush atomically */
spin_lock_irqsave(&sde_enc->enc_spinlock, lock_flags);
@@ -757,6 +831,8 @@
if (!ctl)
continue;
+ set_bit(i, sde_enc->frame_busy_mask);
+
if (!phys->ops.needs_single_flush ||
!phys->ops.needs_single_flush(phys))
_sde_encoder_trigger_flush(&sde_enc->base, phys, 0x0);
@@ -777,7 +853,7 @@
spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags);
}
-void sde_encoder_schedule_kickoff(struct drm_encoder *drm_enc)
+void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
{
struct sde_encoder_virt *sde_enc;
struct sde_encoder_phys *phys;
@@ -798,8 +874,29 @@
if (phys && phys->ops.prepare_for_kickoff)
phys->ops.prepare_for_kickoff(phys);
}
+}
- /* all phys encs are ready to go, trigger the kickoff */
+void sde_encoder_kickoff(struct drm_encoder *drm_enc)
+{
+ struct sde_encoder_virt *sde_enc;
+ struct sde_encoder_phys *phys;
+ unsigned int i;
+
+ if (!drm_enc) {
+ SDE_ERROR("invalid encoder\n");
+ return;
+ }
+ sde_enc = to_sde_encoder_virt(drm_enc);
+
+ SDE_DEBUG_ENC(sde_enc, "\n");
+
+ atomic_set(&sde_enc->frame_done_timeout,
+ SDE_ENCODER_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));
+
+ /* All phys encs are ready to go, trigger the kickoff */
_sde_encoder_kickoff_phys(sde_enc);
/* allow phys encs to handle any post-kickoff business */
@@ -1094,6 +1191,7 @@
struct sde_encoder_virt_ops parent_ops = {
sde_encoder_vblank_callback,
sde_encoder_underrun_callback,
+ sde_encoder_frame_done_callback,
};
struct sde_enc_phys_init_params phys_params;
@@ -1196,6 +1294,34 @@
return ret;
}
+static void sde_encoder_frame_done_timeout(unsigned long data)
+{
+ struct drm_encoder *drm_enc = (struct drm_encoder *) data;
+ struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
+ struct msm_drm_private *priv;
+
+ if (!drm_enc || !drm_enc->dev || !drm_enc->dev->dev_private) {
+ SDE_ERROR("invalid parameters\n");
+ return;
+ }
+ priv = drm_enc->dev->dev_private;
+
+ if (!sde_enc->frame_busy_mask[0] || !sde_enc->crtc_frame_event_cb) {
+ SDE_DEBUG("enc%d invalid timeout\n", drm_enc->base.id);
+ SDE_EVT32(DRMID(drm_enc),
+ sde_enc->frame_busy_mask[0], 0);
+ return;
+ } else if (!atomic_xchg(&sde_enc->frame_done_timeout, 0)) {
+ SDE_ERROR("enc%d invalid timeout\n", drm_enc->base.id);
+ SDE_EVT32(DRMID(drm_enc), 0, 1);
+ return;
+ }
+
+ SDE_EVT32(DRMID(drm_enc), 0, 2);
+ sde_enc->crtc_frame_event_cb(sde_enc->crtc_frame_event_cb_data,
+ SDE_ENCODER_FRAME_EVENT_ERROR);
+}
+
struct drm_encoder *sde_encoder_init(
struct drm_device *dev,
struct msm_display_info *disp_info)
@@ -1226,6 +1352,10 @@
drm_encoder_helper_add(drm_enc, &sde_encoder_helper_funcs);
bs_init(sde_enc);
+ atomic_set(&sde_enc->frame_done_timeout, 0);
+ setup_timer(&sde_enc->frame_done_timer, sde_encoder_frame_done_timeout,
+ (unsigned long) sde_enc);
+
_sde_encoder_init_debugfs(drm_enc, sde_enc, sde_kms);
SDE_DEBUG_ENC(sde_enc, "created\n");
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index 0818a9d..ab8ff5a 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -24,6 +24,9 @@
#include "msm_prop.h"
#include "sde_hw_mdss.h"
+#define SDE_ENCODER_FRAME_EVENT_DONE BIT(0)
+#define SDE_ENCODER_FRAME_EVENT_ERROR BIT(1)
+
/**
* Encoder functions and data types
* @intfs: Interfaces this encoder is using, INTF_MODE_NONE if unused
@@ -59,16 +62,30 @@
void (*cb)(void *), void *data);
/**
- * sde_encoder_schedule_kickoff - Register a callback with the encoder to
- * trigger a double buffer flip of the ctl path (i.e. ctl flush and start)
- * at the appropriate time.
+ * sde_encoder_register_frame_event_callback - provide callback to encoder that
+ * 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
+ */
+void sde_encoder_register_frame_event_callback(struct drm_encoder *encoder,
+ void (*cb)(void *, u32), void *data);
+
+/**
+ * sde_encoder_prepare_for_kickoff - schedule double buffer flip of the ctl
+ * path (i.e. ctl flush and start) at next appropriate time.
* Immediately: if no previous commit is outstanding.
- * Delayed: Save the callback, and return. Does not block. Callback will
- * be triggered later. E.g. cmd encoder will trigger at pp_done irq
- * irq if it outstanding.
+ * Delayed: Block until next trigger can be issued.
* @encoder: encoder pointer
*/
-void sde_encoder_schedule_kickoff(struct drm_encoder *encoder);
+void sde_encoder_prepare_for_kickoff(struct drm_encoder *encoder);
+
+/**
+ * sde_encoder_kickoff - trigger a double buffer flip of the ctl path
+ * (i.e. ctl flush and start) immediately.
+ * @encoder: encoder pointer
+ */
+void sde_encoder_kickoff(struct drm_encoder *drm_enc);
/**
* sde_encoder_wait_nxt_committed - Wait for hardware to have flushed the
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 7a11db3..2ac0a64 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -56,12 +56,16 @@
* Note: This is called from IRQ handler context.
* @handle_underrun_virt: Notify virtual encoder of underrun IRQ reception
* Note: This is called from IRQ handler context.
+ * @handle_frame_done: Notify virtual encoder that this phys encoder
+ * completes last request frame.
*/
struct sde_encoder_virt_ops {
void (*handle_vblank_virt)(struct drm_encoder *,
struct sde_encoder_phys *phys);
void (*handle_underrun_virt)(struct drm_encoder *,
struct sde_encoder_phys *phys);
+ void (*handle_frame_done)(struct drm_encoder *,
+ struct sde_encoder_phys *phys, u32 event);
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index 5d317d0..64c70a2 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -104,6 +104,11 @@
phys_enc = &cmd_enc->base;
+ /* notify all synchronous clients first, then asynchronous clients */
+ if (phys_enc->parent_ops.handle_frame_done)
+ phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
+ phys_enc, SDE_ENCODER_FRAME_EVENT_DONE);
+
spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
@@ -191,6 +196,10 @@
phys_enc->hw_pp->idx - PINGPONG_0);
SDE_ERROR_CMDENC(cmd_enc, "pp:%d kickoff timed out\n",
phys_enc->hw_pp->idx - PINGPONG_0);
+ if (phys_enc->parent_ops.handle_frame_done)
+ phys_enc->parent_ops.handle_frame_done(
+ phys_enc->parent, phys_enc,
+ SDE_ENCODER_FRAME_EVENT_ERROR);
ret = -ETIMEDOUT;
}
} else {
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index abad24d..b5f170c 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -570,16 +570,22 @@
hw_res->intfs[vid_enc->hw_intf->idx - INTF_0] = INTF_MODE_VIDEO;
}
-static int sde_encoder_phys_vid_wait_for_commit_done(
- struct sde_encoder_phys *phys_enc)
+static int sde_encoder_phys_vid_wait_for_vblank(
+ struct sde_encoder_phys *phys_enc, bool notify)
{
struct sde_encoder_phys_vid *vid_enc =
to_sde_encoder_phys_vid(phys_enc);
u32 irq_status;
int ret;
- if (!sde_encoder_phys_vid_is_master(phys_enc))
+ if (!sde_encoder_phys_vid_is_master(phys_enc)) {
+ /* always signal done for slave video encoder */
+ if (notify && phys_enc->parent_ops.handle_frame_done)
+ phys_enc->parent_ops.handle_frame_done(
+ phys_enc->parent, phys_enc,
+ SDE_ENCODER_FRAME_EVENT_DONE);
return 0;
+ }
if (phys_enc->enable_state != SDE_ENC_ENABLED) {
SDE_ERROR("encoder not enabled\n");
@@ -603,6 +609,10 @@
SDE_EVT32(DRMID(phys_enc->parent),
vid_enc->hw_intf->idx - INTF_0);
SDE_DEBUG_VIDENC(vid_enc, "done, irq not triggered\n");
+ if (notify && phys_enc->parent_ops.handle_frame_done)
+ phys_enc->parent_ops.handle_frame_done(
+ phys_enc->parent, phys_enc,
+ SDE_ENCODER_FRAME_EVENT_DONE);
sde_encoder_phys_vid_vblank_irq(vid_enc,
INTR_IDX_VSYNC);
ret = 0;
@@ -610,15 +620,33 @@
SDE_EVT32(DRMID(phys_enc->parent),
vid_enc->hw_intf->idx - INTF_0);
SDE_ERROR_VIDENC(vid_enc, "kickoff timed out\n");
+ if (notify && phys_enc->parent_ops.handle_frame_done)
+ phys_enc->parent_ops.handle_frame_done(
+ phys_enc->parent, phys_enc,
+ SDE_ENCODER_FRAME_EVENT_ERROR);
ret = -ETIMEDOUT;
}
} else {
+ if (notify && phys_enc->parent_ops.handle_frame_done)
+ phys_enc->parent_ops.handle_frame_done(
+ phys_enc->parent, phys_enc,
+ SDE_ENCODER_FRAME_EVENT_DONE);
ret = 0;
}
return 0;
}
+static int sde_encoder_phys_vid_wait_for_commit_done(
+ struct sde_encoder_phys *phys_enc)
+{
+ int ret;
+
+ ret = sde_encoder_phys_vid_wait_for_vblank(phys_enc, true);
+
+ return ret;
+}
+
static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc)
{
struct sde_encoder_phys_vid *vid_enc;
@@ -662,7 +690,7 @@
* scanout buffer) don't latch properly..
*/
if (sde_encoder_phys_vid_is_master(phys_enc)) {
- ret = sde_encoder_phys_vid_wait_for_commit_done(phys_enc);
+ ret = sde_encoder_phys_vid_wait_for_vblank(phys_enc, false);
if (ret) {
atomic_set(&phys_enc->pending_kickoff_cnt, 0);
SDE_ERROR_VIDENC(vid_enc,
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
index 768f59c..3aa4e16 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -537,10 +537,14 @@
SDE_DEBUG("[wb:%d,%u]\n", hw_wb->idx - WB_0,
wb_enc->frame_count);
- complete_all(&wb_enc->wbdone_complete);
+ if (phys_enc->parent_ops.handle_frame_done)
+ phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
+ phys_enc, SDE_ENCODER_FRAME_EVENT_DONE);
phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent,
phys_enc);
+
+ complete_all(&wb_enc->wbdone_complete);
}
/**
@@ -689,6 +693,10 @@
} else {
SDE_ERROR("wb:%d kickoff timed out\n",
wb_enc->wb_dev->wb_idx - WB_0);
+ if (phys_enc->parent_ops.handle_frame_done)
+ phys_enc->parent_ops.handle_frame_done(
+ phys_enc->parent, phys_enc,
+ SDE_ENCODER_FRAME_EVENT_ERROR);
rc = -ETIMEDOUT;
}
}