summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/omapdrm/omap_crtc.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2017-01-23 10:17:06 +1000
committerDave Airlie <airlied@redhat.com>2017-01-23 10:17:06 +1000
commit01f5e6912c8abc8a23248e6cf66939756f0fb27c (patch)
treed2bfbad945b715a433eec6200c827f5b4e2138b4 /drivers/gpu/drm/omapdrm/omap_crtc.c
parentd64a1661c8f783214e1a4fd9d38c2919d5b8231d (diff)
parent42f7f3c4811b3149253ecf2e133832c969884466 (diff)
Merge tag 'omapdrm-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux into drm-next
omapdrm changes for 4.11 The main change here is the IRQ code cleanup, which gives us properly working vblank counts and timestamps. We also get much less calls to runtime PM gets & puts. * tag 'omapdrm-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux: (26 commits) drm/omap: panel-sony-acx565akm.c: Add MODULE_ALIAS drm/omap: dsi: fix compile errors when enabling debug prints drm: omapdrm: Perform initialization/cleanup at probe/remove time drm: Move vblank cleanup from unregister to release drm: omapdrm: Use sizeof(*var) instead of sizeof(type) for structures drm: omapdrm: Remove global variables drm: omapdrm: Simplify IRQ wait implementation drm: omapdrm: Inline the pipe2vbl function drm: omapdrm: Don't call DISPC power handling in IRQ wait functions drm: omapdrm: Remove unused parameter from omap_drm_irq handler drm: omapdrm: Don't expose the omap_irq_(un)register() functions drm: omapdrm: Keep vblank interrupt enabled while CRTC is active drm: omapdrm: Use a spinlock to protect the CRTC pending flag drm: omapdrm: Prevent processing the same event multiple times drm: omapdrm: Check the CRTC software state at enable/disable time drm: omapdrm: Let the DRM core skip plane commit on inactive CRTCs drm: omapdrm: Replace DSS manager state check with omapdrm CRTC state drm: omapdrm: Handle OCP error IRQ directly drm: omapdrm: Handle CRTC error IRQs directly drm: omapdrm: Handle FIFO underflow IRQs internally ...
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_crtc.c')
-rw-r--r--drivers/gpu/drm/omapdrm/omap_crtc.c154
1 files changed, 77 insertions, 77 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index 8dea89030e66..dd47dc191e6b 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -36,26 +36,18 @@ struct omap_crtc {
struct videomode vm;
- struct omap_drm_irq vblank_irq;
- struct omap_drm_irq error_irq;
-
bool ignore_digit_sync_lost;
+ bool enabled;
bool pending;
wait_queue_head_t pending_wait;
+ struct drm_pending_vblank_event *event;
};
/* -----------------------------------------------------------------------------
* Helper Functions
*/
-uint32_t pipe2vbl(struct drm_crtc *crtc)
-{
- struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-
- return dispc_mgr_get_vsync_irq(omap_crtc->channel);
-}
-
struct videomode *omap_crtc_timings(struct drm_crtc *crtc)
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
@@ -68,6 +60,19 @@ enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
return omap_crtc->channel;
}
+static bool omap_crtc_is_pending(struct drm_crtc *crtc)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ unsigned long flags;
+ bool pending;
+
+ spin_lock_irqsave(&crtc->dev->event_lock, flags);
+ pending = omap_crtc->pending;
+ spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+
+ return pending;
+}
+
int omap_crtc_wait_pending(struct drm_crtc *crtc)
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
@@ -77,7 +82,7 @@ int omap_crtc_wait_pending(struct drm_crtc *crtc)
* a single frame refresh even on slower displays.
*/
return wait_event_timeout(omap_crtc->pending_wait,
- !omap_crtc->pending,
+ !omap_crtc_is_pending(crtc),
msecs_to_jiffies(250));
}
@@ -135,14 +140,15 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable)
u32 framedone_irq, vsync_irq;
int ret;
+ if (WARN_ON(omap_crtc->enabled == enable))
+ return;
+
if (omap_crtc_output[channel]->output_type == OMAP_DISPLAY_TYPE_HDMI) {
dispc_mgr_enable(channel, enable);
+ omap_crtc->enabled = enable;
return;
}
- if (dispc_mgr_is_enabled(channel) == enable)
- return;
-
if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
/*
* Digit output produces some sync lost interrupts during the
@@ -173,6 +179,7 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable)
}
dispc_mgr_enable(channel, enable);
+ omap_crtc->enabled = enable;
ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
if (ret) {
@@ -259,26 +266,9 @@ static const struct dss_mgr_ops mgr_ops = {
* Setup, Flush and Page Flip
*/
-static void omap_crtc_complete_page_flip(struct drm_crtc *crtc)
+void omap_crtc_error_irq(struct drm_crtc *crtc, uint32_t irqstatus)
{
- struct drm_pending_vblank_event *event;
- struct drm_device *dev = crtc->dev;
- unsigned long flags;
-
- event = crtc->state->event;
-
- if (!event)
- return;
-
- spin_lock_irqsave(&dev->event_lock, flags);
- drm_crtc_send_vblank_event(crtc, event);
- spin_unlock_irqrestore(&dev->event_lock, flags);
-}
-
-static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
-{
- struct omap_crtc *omap_crtc =
- container_of(irq, struct omap_crtc, error_irq);
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
if (omap_crtc->ignore_digit_sync_lost) {
irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
@@ -289,29 +279,38 @@ static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus);
}
-static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
+void omap_crtc_vblank_irq(struct drm_crtc *crtc)
{
- struct omap_crtc *omap_crtc =
- container_of(irq, struct omap_crtc, vblank_irq);
- struct drm_device *dev = omap_crtc->base.dev;
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ bool pending;
- if (dispc_mgr_go_busy(omap_crtc->channel))
+ spin_lock(&crtc->dev->event_lock);
+ /*
+ * If the dispc is busy we're racing the flush operation. Try again on
+ * the next vblank interrupt.
+ */
+ if (dispc_mgr_go_busy(omap_crtc->channel)) {
+ spin_unlock(&crtc->dev->event_lock);
return;
+ }
- DBG("%s: apply done", omap_crtc->name);
-
- __omap_irq_unregister(dev, &omap_crtc->vblank_irq);
+ /* Send the vblank event if one has been requested. */
+ if (omap_crtc->event) {
+ drm_crtc_send_vblank_event(crtc, omap_crtc->event);
+ omap_crtc->event = NULL;
+ }
- rmb();
- WARN_ON(!omap_crtc->pending);
+ pending = omap_crtc->pending;
omap_crtc->pending = false;
- wmb();
+ spin_unlock(&crtc->dev->event_lock);
- /* wake up userspace */
- omap_crtc_complete_page_flip(&omap_crtc->base);
+ if (pending)
+ drm_crtc_vblank_put(crtc);
- /* wake up omap_atomic_complete */
+ /* Wake up omap_atomic_complete. */
wake_up(&omap_crtc->pending_wait);
+
+ DBG("%s: apply done", omap_crtc->name);
}
/* -----------------------------------------------------------------------------
@@ -324,9 +323,6 @@ static void omap_crtc_destroy(struct drm_crtc *crtc)
DBG("%s", omap_crtc->name);
- WARN_ON(omap_crtc->vblank_irq.registered);
- omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
-
drm_crtc_cleanup(crtc);
kfree(omap_crtc);
@@ -335,17 +331,18 @@ static void omap_crtc_destroy(struct drm_crtc *crtc)
static void omap_crtc_enable(struct drm_crtc *crtc)
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ int ret;
DBG("%s", omap_crtc->name);
- rmb();
+ spin_lock_irq(&crtc->dev->event_lock);
+ drm_crtc_vblank_on(crtc);
+ ret = drm_crtc_vblank_get(crtc);
+ WARN_ON(ret != 0);
+
WARN_ON(omap_crtc->pending);
omap_crtc->pending = true;
- wmb();
-
- omap_irq_register(crtc->dev, &omap_crtc->vblank_irq);
-
- drm_crtc_vblank_on(crtc);
+ spin_unlock_irq(&crtc->dev->event_lock);
}
static void omap_crtc_disable(struct drm_crtc *crtc)
@@ -390,16 +387,15 @@ static int omap_crtc_atomic_check(struct drm_crtc *crtc,
}
static void omap_crtc_atomic_begin(struct drm_crtc *crtc,
- struct drm_crtc_state *old_crtc_state)
+ struct drm_crtc_state *old_crtc_state)
{
}
static void omap_crtc_atomic_flush(struct drm_crtc *crtc,
- struct drm_crtc_state *old_crtc_state)
+ struct drm_crtc_state *old_crtc_state)
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-
- WARN_ON(omap_crtc->vblank_irq.registered);
+ int ret;
if (crtc->state->color_mgmt_changed) {
struct drm_color_lut *lut = NULL;
@@ -414,18 +410,30 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc,
dispc_mgr_set_gamma(omap_crtc->channel, lut, length);
}
- if (dispc_mgr_is_enabled(omap_crtc->channel)) {
+ /*
+ * Only flush the CRTC if it is currently enabled. CRTCs that require a
+ * mode set are disabled prior plane updates and enabled afterwards.
+ * They are thus not active (regardless of what their CRTC core state
+ * reports) and the DRM core could thus call this function even though
+ * the CRTC is currently disabled. Do nothing in that case.
+ */
+ if (!omap_crtc->enabled)
+ return;
+
+ DBG("%s: GO", omap_crtc->name);
- DBG("%s: GO", omap_crtc->name);
+ ret = drm_crtc_vblank_get(crtc);
+ WARN_ON(ret != 0);
- rmb();
- WARN_ON(omap_crtc->pending);
- omap_crtc->pending = true;
- wmb();
+ spin_lock_irq(&crtc->dev->event_lock);
+ dispc_mgr_go(omap_crtc->channel);
- dispc_mgr_go(omap_crtc->channel);
- omap_irq_register(crtc->dev, &omap_crtc->vblank_irq);
- }
+ WARN_ON(omap_crtc->pending);
+ omap_crtc->pending = true;
+
+ if (crtc->state->event)
+ omap_crtc->event = crtc->state->event;
+ spin_unlock_irq(&crtc->dev->event_lock);
}
static bool omap_crtc_is_plane_prop(struct drm_crtc *crtc,
@@ -546,14 +554,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
omap_crtc->channel = channel;
omap_crtc->name = channel_names[channel];
- omap_crtc->vblank_irq.irqmask = pipe2vbl(crtc);
- omap_crtc->vblank_irq.irq = omap_crtc_vblank_irq;
-
- omap_crtc->error_irq.irqmask =
- dispc_mgr_get_sync_lost_irq(channel);
- omap_crtc->error_irq.irq = omap_crtc_error_irq;
- omap_irq_register(dev, &omap_crtc->error_irq);
-
ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
&omap_crtc_funcs, NULL);
if (ret < 0) {