diff options
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_crtc.c')
-rw-r--r-- | drivers/gpu/drm/omapdrm/omap_crtc.c | 114 |
1 files changed, 109 insertions, 5 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index 5b6a18a62bbb..d61215494617 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c | |||
@@ -32,6 +32,7 @@ struct omap_crtc_state { | |||
32 | /* Shadow values for legacy userspace support. */ | 32 | /* Shadow values for legacy userspace support. */ |
33 | unsigned int rotation; | 33 | unsigned int rotation; |
34 | unsigned int zpos; | 34 | unsigned int zpos; |
35 | bool manually_updated; | ||
35 | }; | 36 | }; |
36 | 37 | ||
37 | #define to_omap_crtc(x) container_of(x, struct omap_crtc, base) | 38 | #define to_omap_crtc(x) container_of(x, struct omap_crtc, base) |
@@ -51,6 +52,7 @@ struct omap_crtc { | |||
51 | bool pending; | 52 | bool pending; |
52 | wait_queue_head_t pending_wait; | 53 | wait_queue_head_t pending_wait; |
53 | struct drm_pending_vblank_event *event; | 54 | struct drm_pending_vblank_event *event; |
55 | struct delayed_work update_work; | ||
54 | 56 | ||
55 | void (*framedone_handler)(void *); | 57 | void (*framedone_handler)(void *); |
56 | void *framedone_handler_data; | 58 | void *framedone_handler_data; |
@@ -105,21 +107,18 @@ int omap_crtc_wait_pending(struct drm_crtc *crtc) | |||
105 | /* | 107 | /* |
106 | * Manager-ops, callbacks from output when they need to configure | 108 | * Manager-ops, callbacks from output when they need to configure |
107 | * the upstream part of the video pipe. | 109 | * the upstream part of the video pipe. |
108 | * | ||
109 | * Most of these we can ignore until we add support for command-mode | ||
110 | * panels.. for video-mode the crtc-helpers already do an adequate | ||
111 | * job of sequencing the setup of the video pipe in the proper order | ||
112 | */ | 110 | */ |
113 | 111 | ||
114 | /* we can probably ignore these until we support command-mode panels: */ | ||
115 | static void omap_crtc_dss_start_update(struct omap_drm_private *priv, | 112 | static void omap_crtc_dss_start_update(struct omap_drm_private *priv, |
116 | enum omap_channel channel) | 113 | enum omap_channel channel) |
117 | { | 114 | { |
115 | priv->dispc_ops->mgr_enable(priv->dispc, channel, true); | ||
118 | } | 116 | } |
119 | 117 | ||
120 | /* Called only from the encoder enable/disable and suspend/resume handlers. */ | 118 | /* Called only from the encoder enable/disable and suspend/resume handlers. */ |
121 | static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) | 119 | static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) |
122 | { | 120 | { |
121 | struct omap_crtc_state *omap_state = to_omap_crtc_state(crtc->state); | ||
123 | struct drm_device *dev = crtc->dev; | 122 | struct drm_device *dev = crtc->dev; |
124 | struct omap_drm_private *priv = dev->dev_private; | 123 | struct omap_drm_private *priv = dev->dev_private; |
125 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | 124 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
@@ -131,6 +130,12 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) | |||
131 | if (WARN_ON(omap_crtc->enabled == enable)) | 130 | if (WARN_ON(omap_crtc->enabled == enable)) |
132 | return; | 131 | return; |
133 | 132 | ||
133 | if (omap_state->manually_updated) { | ||
134 | omap_irq_enable_framedone(crtc, enable); | ||
135 | omap_crtc->enabled = enable; | ||
136 | return; | ||
137 | } | ||
138 | |||
134 | if (omap_crtc->pipe->output->type == OMAP_DISPLAY_TYPE_HDMI) { | 139 | if (omap_crtc->pipe->output->type == OMAP_DISPLAY_TYPE_HDMI) { |
135 | priv->dispc_ops->mgr_enable(priv->dispc, channel, enable); | 140 | priv->dispc_ops->mgr_enable(priv->dispc, channel, enable); |
136 | omap_crtc->enabled = enable; | 141 | omap_crtc->enabled = enable; |
@@ -350,6 +355,51 @@ void omap_crtc_framedone_irq(struct drm_crtc *crtc, uint32_t irqstatus) | |||
350 | wake_up(&omap_crtc->pending_wait); | 355 | wake_up(&omap_crtc->pending_wait); |
351 | } | 356 | } |
352 | 357 | ||
358 | void omap_crtc_flush(struct drm_crtc *crtc) | ||
359 | { | ||
360 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | ||
361 | struct omap_crtc_state *omap_state = to_omap_crtc_state(crtc->state); | ||
362 | |||
363 | if (!omap_state->manually_updated) | ||
364 | return; | ||
365 | |||
366 | if (!delayed_work_pending(&omap_crtc->update_work)) | ||
367 | schedule_delayed_work(&omap_crtc->update_work, 0); | ||
368 | } | ||
369 | |||
370 | static void omap_crtc_manual_display_update(struct work_struct *data) | ||
371 | { | ||
372 | struct omap_crtc *omap_crtc = | ||
373 | container_of(data, struct omap_crtc, update_work.work); | ||
374 | struct drm_display_mode *mode = &omap_crtc->pipe->crtc->mode; | ||
375 | struct omap_dss_device *dssdev = omap_crtc->pipe->output->next; | ||
376 | struct drm_device *dev = omap_crtc->base.dev; | ||
377 | const struct omap_dss_driver *dssdrv; | ||
378 | int ret; | ||
379 | |||
380 | if (!dssdev) { | ||
381 | dev_err_once(dev->dev, "missing display dssdev!"); | ||
382 | return; | ||
383 | } | ||
384 | |||
385 | dssdrv = dssdev->driver; | ||
386 | if (!dssdrv || !dssdrv->update) { | ||
387 | dev_err_once(dev->dev, "missing or incorrect dssdrv!"); | ||
388 | return; | ||
389 | } | ||
390 | |||
391 | if (dssdrv->sync) | ||
392 | dssdrv->sync(dssdev); | ||
393 | |||
394 | ret = dssdrv->update(dssdev, 0, 0, mode->hdisplay, mode->vdisplay); | ||
395 | if (ret < 0) { | ||
396 | spin_lock_irq(&dev->event_lock); | ||
397 | omap_crtc->pending = false; | ||
398 | spin_unlock_irq(&dev->event_lock); | ||
399 | wake_up(&omap_crtc->pending_wait); | ||
400 | } | ||
401 | } | ||
402 | |||
353 | static void omap_crtc_write_crtc_properties(struct drm_crtc *crtc) | 403 | static void omap_crtc_write_crtc_properties(struct drm_crtc *crtc) |
354 | { | 404 | { |
355 | struct omap_drm_private *priv = crtc->dev->dev_private; | 405 | struct omap_drm_private *priv = crtc->dev->dev_private; |
@@ -399,12 +449,17 @@ static void omap_crtc_atomic_enable(struct drm_crtc *crtc, | |||
399 | { | 449 | { |
400 | struct omap_drm_private *priv = crtc->dev->dev_private; | 450 | struct omap_drm_private *priv = crtc->dev->dev_private; |
401 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | 451 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
452 | struct omap_crtc_state *omap_state = to_omap_crtc_state(crtc->state); | ||
402 | int ret; | 453 | int ret; |
403 | 454 | ||
404 | DBG("%s", omap_crtc->name); | 455 | DBG("%s", omap_crtc->name); |
405 | 456 | ||
406 | priv->dispc_ops->runtime_get(priv->dispc); | 457 | priv->dispc_ops->runtime_get(priv->dispc); |
407 | 458 | ||
459 | /* manual updated display will not trigger vsync irq */ | ||
460 | if (omap_state->manually_updated) | ||
461 | return; | ||
462 | |||
408 | spin_lock_irq(&crtc->dev->event_lock); | 463 | spin_lock_irq(&crtc->dev->event_lock); |
409 | drm_crtc_vblank_on(crtc); | 464 | drm_crtc_vblank_on(crtc); |
410 | ret = drm_crtc_vblank_get(crtc); | 465 | ret = drm_crtc_vblank_get(crtc); |
@@ -419,6 +474,7 @@ static void omap_crtc_atomic_disable(struct drm_crtc *crtc, | |||
419 | { | 474 | { |
420 | struct omap_drm_private *priv = crtc->dev->dev_private; | 475 | struct omap_drm_private *priv = crtc->dev->dev_private; |
421 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | 476 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
477 | struct drm_device *dev = crtc->dev; | ||
422 | 478 | ||
423 | DBG("%s", omap_crtc->name); | 479 | DBG("%s", omap_crtc->name); |
424 | 480 | ||
@@ -429,6 +485,11 @@ static void omap_crtc_atomic_disable(struct drm_crtc *crtc, | |||
429 | } | 485 | } |
430 | spin_unlock_irq(&crtc->dev->event_lock); | 486 | spin_unlock_irq(&crtc->dev->event_lock); |
431 | 487 | ||
488 | cancel_delayed_work(&omap_crtc->update_work); | ||
489 | |||
490 | if (!omap_crtc_wait_pending(crtc)) | ||
491 | dev_warn(dev->dev, "manual display update did not finish!"); | ||
492 | |||
432 | drm_crtc_vblank_off(crtc); | 493 | drm_crtc_vblank_off(crtc); |
433 | 494 | ||
434 | priv->dispc_ops->runtime_put(priv->dispc); | 495 | priv->dispc_ops->runtime_put(priv->dispc); |
@@ -499,6 +560,22 @@ static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc) | |||
499 | drm_display_mode_to_videomode(mode, &omap_crtc->vm); | 560 | drm_display_mode_to_videomode(mode, &omap_crtc->vm); |
500 | } | 561 | } |
501 | 562 | ||
563 | static bool omap_crtc_is_manually_updated(struct drm_crtc *crtc) | ||
564 | { | ||
565 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | ||
566 | struct omap_dss_device *display = omap_crtc->pipe->output->next; | ||
567 | |||
568 | if (!display) | ||
569 | return false; | ||
570 | |||
571 | if (display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { | ||
572 | DBG("detected manually updated display!"); | ||
573 | return true; | ||
574 | } | ||
575 | |||
576 | return false; | ||
577 | } | ||
578 | |||
502 | static int omap_crtc_atomic_check(struct drm_crtc *crtc, | 579 | static int omap_crtc_atomic_check(struct drm_crtc *crtc, |
503 | struct drm_crtc_state *state) | 580 | struct drm_crtc_state *state) |
504 | { | 581 | { |
@@ -520,6 +597,9 @@ static int omap_crtc_atomic_check(struct drm_crtc *crtc, | |||
520 | /* Mirror new values for zpos and rotation in omap_crtc_state */ | 597 | /* Mirror new values for zpos and rotation in omap_crtc_state */ |
521 | omap_crtc_state->zpos = pri_state->zpos; | 598 | omap_crtc_state->zpos = pri_state->zpos; |
522 | omap_crtc_state->rotation = pri_state->rotation; | 599 | omap_crtc_state->rotation = pri_state->rotation; |
600 | |||
601 | /* Check if this CRTC is for a manually updated display */ | ||
602 | omap_crtc_state->manually_updated = omap_crtc_is_manually_updated(crtc); | ||
523 | } | 603 | } |
524 | 604 | ||
525 | return 0; | 605 | return 0; |
@@ -535,6 +615,7 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc, | |||
535 | { | 615 | { |
536 | struct omap_drm_private *priv = crtc->dev->dev_private; | 616 | struct omap_drm_private *priv = crtc->dev->dev_private; |
537 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | 617 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
618 | struct omap_crtc_state *omap_crtc_state = to_omap_crtc_state(crtc->state); | ||
538 | int ret; | 619 | int ret; |
539 | 620 | ||
540 | if (crtc->state->color_mgmt_changed) { | 621 | if (crtc->state->color_mgmt_changed) { |
@@ -559,6 +640,15 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc, | |||
559 | 640 | ||
560 | DBG("%s: GO", omap_crtc->name); | 641 | DBG("%s: GO", omap_crtc->name); |
561 | 642 | ||
643 | if (omap_crtc_state->manually_updated) { | ||
644 | /* send new image for page flips and modeset changes */ | ||
645 | spin_lock_irq(&crtc->dev->event_lock); | ||
646 | omap_crtc_flush(crtc); | ||
647 | omap_crtc_arm_event(crtc); | ||
648 | spin_unlock_irq(&crtc->dev->event_lock); | ||
649 | return; | ||
650 | } | ||
651 | |||
562 | ret = drm_crtc_vblank_get(crtc); | 652 | ret = drm_crtc_vblank_get(crtc); |
563 | WARN_ON(ret != 0); | 653 | WARN_ON(ret != 0); |
564 | 654 | ||
@@ -644,6 +734,7 @@ omap_crtc_duplicate_state(struct drm_crtc *crtc) | |||
644 | 734 | ||
645 | state->zpos = current_state->zpos; | 735 | state->zpos = current_state->zpos; |
646 | state->rotation = current_state->rotation; | 736 | state->rotation = current_state->rotation; |
737 | state->manually_updated = current_state->manually_updated; | ||
647 | 738 | ||
648 | return &state->base; | 739 | return &state->base; |
649 | } | 740 | } |
@@ -720,6 +811,19 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, | |||
720 | omap_crtc->channel = channel; | 811 | omap_crtc->channel = channel; |
721 | omap_crtc->name = channel_names[channel]; | 812 | omap_crtc->name = channel_names[channel]; |
722 | 813 | ||
814 | /* | ||
815 | * We want to refresh manually updated displays from dirty callback, | ||
816 | * which is called quite often (e.g. for each drawn line). This will | ||
817 | * be used to do the display update asynchronously to avoid blocking | ||
818 | * the rendering process and merges multiple dirty calls into one | ||
819 | * update if they arrive very fast. We also call this function for | ||
820 | * atomic display updates (e.g. for page flips), which means we do | ||
821 | * not need extra locking. Atomic updates should be synchronous, but | ||
822 | * need to wait for the framedone interrupt anyways. | ||
823 | */ | ||
824 | INIT_DELAYED_WORK(&omap_crtc->update_work, | ||
825 | omap_crtc_manual_display_update); | ||
826 | |||
723 | ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, | 827 | ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, |
724 | &omap_crtc_funcs, NULL); | 828 | &omap_crtc_funcs, NULL); |
725 | if (ret < 0) { | 829 | if (ret < 0) { |