Commit 5f741b39 authored by Tomi Valkeinen's avatar Tomi Valkeinen Committed by Laurent Pinchart

drm: omapdrm: new vblank and event handling

Rework the crtc event/flip_wait system as follows:

- If we enable a crtc (full modeset), we set omap_crtc->pending and
  register vblank irq.

- If we need to set GO bit (page flip), we do the same but also set the
  GO bit.

- On vblank we unregister the irq, clear the 'pending' flag, send vblank
  event to userspace if crtc->state->event != NULL, and wake up
  'pending_wait' wq.

- In omap_atomic_complete() we wait for the 'pending' flag to get reset
  for all enabled crtcs  using 'pending_wait' wq.

The above ensures that we send the events to userspace in vblank, and
that after the wait in omap_atomic_complete() everything for the
affected crtcs has been completed.
Signed-off-by: default avatarTomi Valkeinen <tomi.valkeinen@ti.com>
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
parent 6646dfd0
...@@ -17,8 +17,6 @@ ...@@ -17,8 +17,6 @@
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <linux/completion.h>
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
...@@ -49,13 +47,10 @@ struct omap_crtc { ...@@ -49,13 +47,10 @@ struct omap_crtc {
struct omap_drm_irq vblank_irq; struct omap_drm_irq vblank_irq;
struct omap_drm_irq error_irq; struct omap_drm_irq error_irq;
/* pending event */
struct drm_pending_vblank_event *event;
wait_queue_head_t flip_wait;
struct completion completion;
bool ignore_digit_sync_lost; bool ignore_digit_sync_lost;
bool pending;
wait_queue_head_t pending_wait;
}; };
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
...@@ -81,6 +76,15 @@ enum omap_channel omap_crtc_channel(struct drm_crtc *crtc) ...@@ -81,6 +76,15 @@ enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
return omap_crtc->channel; return omap_crtc->channel;
} }
int omap_crtc_wait_pending(struct drm_crtc *crtc)
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
return wait_event_timeout(omap_crtc->pending_wait,
!omap_crtc->pending,
msecs_to_jiffies(50));
}
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* DSS Manager Functions * DSS Manager Functions
*/ */
...@@ -255,61 +259,29 @@ static const struct dss_mgr_ops mgr_ops = { ...@@ -255,61 +259,29 @@ static const struct dss_mgr_ops mgr_ops = {
static void omap_crtc_complete_page_flip(struct drm_crtc *crtc) static void omap_crtc_complete_page_flip(struct drm_crtc *crtc)
{ {
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
struct drm_pending_vblank_event *event; struct drm_pending_vblank_event *event;
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&dev->event_lock, flags); event = crtc->state->event;
event = omap_crtc->event;
omap_crtc->event = NULL;
if (event) { if (!event)
list_del(&event->base.link); return;
/*
* Queue the event for delivery if it's still linked to a file
* handle, otherwise just destroy it.
*/
if (event->base.file_priv)
drm_crtc_send_vblank_event(crtc, event);
else
event->base.destroy(&event->base);
wake_up(&omap_crtc->flip_wait);
drm_crtc_vblank_put(crtc);
}
spin_unlock_irqrestore(&dev->event_lock, flags);
}
static bool omap_crtc_page_flip_pending(struct drm_crtc *crtc)
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
struct drm_device *dev = crtc->dev;
unsigned long flags;
bool pending;
spin_lock_irqsave(&dev->event_lock, flags); spin_lock_irqsave(&dev->event_lock, flags);
pending = omap_crtc->event != NULL;
spin_unlock_irqrestore(&dev->event_lock, flags);
return pending; list_del(&event->base.link);
}
static void omap_crtc_wait_page_flip(struct drm_crtc *crtc) /*
{ * Queue the event for delivery if it's still linked to a file
struct omap_crtc *omap_crtc = to_omap_crtc(crtc); * handle, otherwise just destroy it.
*/
if (wait_event_timeout(omap_crtc->flip_wait, if (event->base.file_priv)
!omap_crtc_page_flip_pending(crtc), drm_crtc_send_vblank_event(crtc, event);
msecs_to_jiffies(50))) else
return; event->base.destroy(&event->base);
dev_warn(crtc->dev->dev, "page flip timeout!\n");
omap_crtc_complete_page_flip(crtc); spin_unlock_irqrestore(&dev->event_lock, flags);
} }
static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
...@@ -336,12 +308,19 @@ static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus) ...@@ -336,12 +308,19 @@ static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
return; return;
DBG("%s: apply done", omap_crtc->name); DBG("%s: apply done", omap_crtc->name);
__omap_irq_unregister(dev, &omap_crtc->vblank_irq); __omap_irq_unregister(dev, &omap_crtc->vblank_irq);
/* wakeup userspace */ rmb();
WARN_ON(!omap_crtc->pending);
omap_crtc->pending = false;
wmb();
/* wake up userspace */
omap_crtc_complete_page_flip(&omap_crtc->base); omap_crtc_complete_page_flip(&omap_crtc->base);
complete(&omap_crtc->completion); /* wake up omap_atomic_complete */
wake_up(&omap_crtc->pending_wait);
} }
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
...@@ -375,6 +354,13 @@ static void omap_crtc_enable(struct drm_crtc *crtc) ...@@ -375,6 +354,13 @@ static void omap_crtc_enable(struct drm_crtc *crtc)
DBG("%s", omap_crtc->name); DBG("%s", omap_crtc->name);
rmb();
WARN_ON(omap_crtc->pending);
omap_crtc->pending = true;
wmb();
omap_irq_register(crtc->dev, &omap_crtc->vblank_irq);
drm_crtc_vblank_on(crtc); drm_crtc_vblank_on(crtc);
} }
...@@ -384,7 +370,6 @@ static void omap_crtc_disable(struct drm_crtc *crtc) ...@@ -384,7 +370,6 @@ static void omap_crtc_disable(struct drm_crtc *crtc)
DBG("%s", omap_crtc->name); DBG("%s", omap_crtc->name);
omap_crtc_wait_page_flip(crtc);
drm_crtc_vblank_off(crtc); drm_crtc_vblank_off(crtc);
} }
...@@ -405,19 +390,6 @@ static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc) ...@@ -405,19 +390,6 @@ static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc)
static void omap_crtc_atomic_begin(struct drm_crtc *crtc) static void omap_crtc_atomic_begin(struct drm_crtc *crtc)
{ {
struct drm_pending_vblank_event *event = crtc->state->event;
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
struct drm_device *dev = crtc->dev;
unsigned long flags;
if (event) {
WARN_ON(omap_crtc->event);
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
spin_lock_irqsave(&dev->event_lock, flags);
omap_crtc->event = event;
spin_unlock_irqrestore(&dev->event_lock, flags);
}
} }
static void omap_crtc_atomic_flush(struct drm_crtc *crtc) static void omap_crtc_atomic_flush(struct drm_crtc *crtc)
...@@ -427,14 +399,16 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc) ...@@ -427,14 +399,16 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc)
WARN_ON(omap_crtc->vblank_irq.registered); WARN_ON(omap_crtc->vblank_irq.registered);
if (dispc_mgr_is_enabled(omap_crtc->channel)) { if (dispc_mgr_is_enabled(omap_crtc->channel)) {
DBG("%s: GO", omap_crtc->name); DBG("%s: GO", omap_crtc->name);
rmb();
WARN_ON(omap_crtc->pending);
omap_crtc->pending = true;
wmb();
dispc_mgr_go(omap_crtc->channel); dispc_mgr_go(omap_crtc->channel);
omap_irq_register(crtc->dev, &omap_crtc->vblank_irq); omap_irq_register(crtc->dev, &omap_crtc->vblank_irq);
WARN_ON(!wait_for_completion_timeout(&omap_crtc->completion,
msecs_to_jiffies(100)));
reinit_completion(&omap_crtc->completion);
} }
crtc->invert_dimensions = !!(crtc->primary->state->rotation & crtc->invert_dimensions = !!(crtc->primary->state->rotation &
...@@ -534,8 +508,7 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, ...@@ -534,8 +508,7 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
crtc = &omap_crtc->base; crtc = &omap_crtc->base;
init_waitqueue_head(&omap_crtc->flip_wait); init_waitqueue_head(&omap_crtc->pending_wait);
init_completion(&omap_crtc->completion);
omap_crtc->channel = channel; omap_crtc->channel = channel;
omap_crtc->name = channel_names[channel]; omap_crtc->name = channel_names[channel];
......
...@@ -66,6 +66,26 @@ struct omap_atomic_state_commit { ...@@ -66,6 +66,26 @@ struct omap_atomic_state_commit {
u32 crtcs; u32 crtcs;
}; };
static void omap_atomic_wait_for_completion(struct drm_device *dev,
struct drm_atomic_state *old_state)
{
struct drm_crtc_state *old_crtc_state;
struct drm_crtc *crtc;
unsigned int i;
int ret;
for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
if (!crtc->state->enable)
continue;
ret = omap_crtc_wait_pending(crtc);
if (!ret)
dev_warn(dev->dev,
"atomic complete timeout (pipe %u)!\n", i);
}
}
static void omap_atomic_complete(struct omap_atomic_state_commit *commit) static void omap_atomic_complete(struct omap_atomic_state_commit *commit)
{ {
struct drm_device *dev = commit->dev; struct drm_device *dev = commit->dev;
...@@ -79,7 +99,7 @@ static void omap_atomic_complete(struct omap_atomic_state_commit *commit) ...@@ -79,7 +99,7 @@ static void omap_atomic_complete(struct omap_atomic_state_commit *commit)
drm_atomic_helper_commit_planes(dev, old_state); drm_atomic_helper_commit_planes(dev, old_state);
drm_atomic_helper_commit_modeset_enables(dev, old_state); drm_atomic_helper_commit_modeset_enables(dev, old_state);
drm_atomic_helper_wait_for_vblanks(dev, old_state); omap_atomic_wait_for_completion(dev, old_state);
drm_atomic_helper_cleanup_planes(dev, old_state); drm_atomic_helper_cleanup_planes(dev, old_state);
......
...@@ -147,6 +147,7 @@ void omap_crtc_pre_init(void); ...@@ -147,6 +147,7 @@ void omap_crtc_pre_init(void);
void omap_crtc_pre_uninit(void); void omap_crtc_pre_uninit(void);
struct drm_crtc *omap_crtc_init(struct drm_device *dev, struct drm_crtc *omap_crtc_init(struct drm_device *dev,
struct drm_plane *plane, enum omap_channel channel, int id); struct drm_plane *plane, enum omap_channel channel, int id);
int omap_crtc_wait_pending(struct drm_crtc *crtc);
struct drm_plane *omap_plane_init(struct drm_device *dev, struct drm_plane *omap_plane_init(struct drm_device *dev,
int id, enum drm_plane_type type); int id, enum drm_plane_type type);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment