diff options
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_crtc.c')
-rw-r--r-- | drivers/gpu/drm/omapdrm/omap_crtc.c | 100 |
1 files changed, 72 insertions, 28 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index 355157e4f78d..e3c47a8005ff 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c | |||
@@ -33,6 +33,7 @@ struct omap_crtc { | |||
33 | int pipe; | 33 | int pipe; |
34 | enum omap_channel channel; | 34 | enum omap_channel channel; |
35 | struct omap_overlay_manager_info info; | 35 | struct omap_overlay_manager_info info; |
36 | struct drm_encoder *current_encoder; | ||
36 | 37 | ||
37 | /* | 38 | /* |
38 | * Temporary: eventually this will go away, but it is needed | 39 | * Temporary: eventually this will go away, but it is needed |
@@ -120,13 +121,25 @@ static void omap_crtc_start_update(struct omap_overlay_manager *mgr) | |||
120 | { | 121 | { |
121 | } | 122 | } |
122 | 123 | ||
124 | static void set_enabled(struct drm_crtc *crtc, bool enable); | ||
125 | |||
123 | static int omap_crtc_enable(struct omap_overlay_manager *mgr) | 126 | static int omap_crtc_enable(struct omap_overlay_manager *mgr) |
124 | { | 127 | { |
128 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; | ||
129 | |||
130 | dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info); | ||
131 | dispc_mgr_set_timings(omap_crtc->channel, | ||
132 | &omap_crtc->timings); | ||
133 | set_enabled(&omap_crtc->base, true); | ||
134 | |||
125 | return 0; | 135 | return 0; |
126 | } | 136 | } |
127 | 137 | ||
128 | static void omap_crtc_disable(struct omap_overlay_manager *mgr) | 138 | static void omap_crtc_disable(struct omap_overlay_manager *mgr) |
129 | { | 139 | { |
140 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; | ||
141 | |||
142 | set_enabled(&omap_crtc->base, false); | ||
130 | } | 143 | } |
131 | 144 | ||
132 | static void omap_crtc_set_timings(struct omap_overlay_manager *mgr, | 145 | static void omap_crtc_set_timings(struct omap_overlay_manager *mgr, |
@@ -184,7 +197,6 @@ static void omap_crtc_destroy(struct drm_crtc *crtc) | |||
184 | WARN_ON(omap_crtc->apply_irq.registered); | 197 | WARN_ON(omap_crtc->apply_irq.registered); |
185 | omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); | 198 | omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); |
186 | 199 | ||
187 | omap_crtc->plane->funcs->destroy(omap_crtc->plane); | ||
188 | drm_crtc_cleanup(crtc); | 200 | drm_crtc_cleanup(crtc); |
189 | 201 | ||
190 | kfree(omap_crtc); | 202 | kfree(omap_crtc); |
@@ -338,17 +350,23 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, | |||
338 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | 350 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
339 | struct drm_plane *primary = crtc->primary; | 351 | struct drm_plane *primary = crtc->primary; |
340 | struct drm_gem_object *bo; | 352 | struct drm_gem_object *bo; |
353 | unsigned long flags; | ||
341 | 354 | ||
342 | DBG("%d -> %d (event=%p)", primary->fb ? primary->fb->base.id : -1, | 355 | DBG("%d -> %d (event=%p)", primary->fb ? primary->fb->base.id : -1, |
343 | fb->base.id, event); | 356 | fb->base.id, event); |
344 | 357 | ||
358 | spin_lock_irqsave(&dev->event_lock, flags); | ||
359 | |||
345 | if (omap_crtc->old_fb) { | 360 | if (omap_crtc->old_fb) { |
361 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
346 | dev_err(dev->dev, "already a pending flip\n"); | 362 | dev_err(dev->dev, "already a pending flip\n"); |
347 | return -EINVAL; | 363 | return -EINVAL; |
348 | } | 364 | } |
349 | 365 | ||
350 | omap_crtc->event = event; | 366 | omap_crtc->event = event; |
351 | primary->fb = fb; | 367 | omap_crtc->old_fb = primary->fb = fb; |
368 | |||
369 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
352 | 370 | ||
353 | /* | 371 | /* |
354 | * Hold a reference temporarily until the crtc is updated | 372 | * Hold a reference temporarily until the crtc is updated |
@@ -528,38 +546,46 @@ static void set_enabled(struct drm_crtc *crtc, bool enable) | |||
528 | struct drm_device *dev = crtc->dev; | 546 | struct drm_device *dev = crtc->dev; |
529 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | 547 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
530 | enum omap_channel channel = omap_crtc->channel; | 548 | enum omap_channel channel = omap_crtc->channel; |
531 | struct omap_irq_wait *wait = NULL; | 549 | struct omap_irq_wait *wait; |
550 | u32 framedone_irq, vsync_irq; | ||
551 | int ret; | ||
532 | 552 | ||
533 | if (dispc_mgr_is_enabled(channel) == enable) | 553 | if (dispc_mgr_is_enabled(channel) == enable) |
534 | return; | 554 | return; |
535 | 555 | ||
536 | /* ignore sync-lost irqs during enable/disable */ | 556 | /* |
557 | * Digit output produces some sync lost interrupts during the first | ||
558 | * frame when enabling, so we need to ignore those. | ||
559 | */ | ||
537 | omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); | 560 | omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); |
538 | 561 | ||
539 | if (dispc_mgr_get_framedone_irq(channel)) { | 562 | framedone_irq = dispc_mgr_get_framedone_irq(channel); |
540 | if (!enable) { | 563 | vsync_irq = dispc_mgr_get_vsync_irq(channel); |
541 | wait = omap_irq_wait_init(dev, | 564 | |
542 | dispc_mgr_get_framedone_irq(channel), 1); | 565 | if (enable) { |
543 | } | 566 | wait = omap_irq_wait_init(dev, vsync_irq, 1); |
544 | } else { | 567 | } else { |
545 | /* | 568 | /* |
546 | * When we disable digit output, we need to wait until fields | 569 | * When we disable the digit output, we need to wait for |
547 | * are done. Otherwise the DSS is still working, and turning | 570 | * FRAMEDONE to know that DISPC has finished with the output. |
548 | * off the clocks prevents DSS from going to OFF mode. And when | 571 | * |
549 | * enabling, we need to wait for the extra sync losts | 572 | * OMAP2/3 does not have FRAMEDONE irq for digit output, and in |
573 | * that case we need to use vsync interrupt, and wait for both | ||
574 | * even and odd frames. | ||
550 | */ | 575 | */ |
551 | wait = omap_irq_wait_init(dev, | 576 | |
552 | dispc_mgr_get_vsync_irq(channel), 2); | 577 | if (framedone_irq) |
578 | wait = omap_irq_wait_init(dev, framedone_irq, 1); | ||
579 | else | ||
580 | wait = omap_irq_wait_init(dev, vsync_irq, 2); | ||
553 | } | 581 | } |
554 | 582 | ||
555 | dispc_mgr_enable(channel, enable); | 583 | dispc_mgr_enable(channel, enable); |
556 | 584 | ||
557 | if (wait) { | 585 | ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100)); |
558 | int ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100)); | 586 | if (ret) { |
559 | if (ret) { | 587 | dev_err(dev->dev, "%s: timeout waiting for %s\n", |
560 | dev_err(dev->dev, "%s: timeout waiting for %s\n", | 588 | omap_crtc->name, enable ? "enable" : "disable"); |
561 | omap_crtc->name, enable ? "enable" : "disable"); | ||
562 | } | ||
563 | } | 589 | } |
564 | 590 | ||
565 | omap_irq_register(crtc->dev, &omap_crtc->error_irq); | 591 | omap_irq_register(crtc->dev, &omap_crtc->error_irq); |
@@ -586,8 +612,12 @@ static void omap_crtc_pre_apply(struct omap_drm_apply *apply) | |||
586 | } | 612 | } |
587 | } | 613 | } |
588 | 614 | ||
615 | if (omap_crtc->current_encoder && encoder != omap_crtc->current_encoder) | ||
616 | omap_encoder_set_enabled(omap_crtc->current_encoder, false); | ||
617 | |||
618 | omap_crtc->current_encoder = encoder; | ||
619 | |||
589 | if (!omap_crtc->enabled) { | 620 | if (!omap_crtc->enabled) { |
590 | set_enabled(&omap_crtc->base, false); | ||
591 | if (encoder) | 621 | if (encoder) |
592 | omap_encoder_set_enabled(encoder, false); | 622 | omap_encoder_set_enabled(encoder, false); |
593 | } else { | 623 | } else { |
@@ -596,13 +626,7 @@ static void omap_crtc_pre_apply(struct omap_drm_apply *apply) | |||
596 | omap_encoder_update(encoder, omap_crtc->mgr, | 626 | omap_encoder_update(encoder, omap_crtc->mgr, |
597 | &omap_crtc->timings); | 627 | &omap_crtc->timings); |
598 | omap_encoder_set_enabled(encoder, true); | 628 | omap_encoder_set_enabled(encoder, true); |
599 | omap_crtc->full_update = false; | ||
600 | } | 629 | } |
601 | |||
602 | dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info); | ||
603 | dispc_mgr_set_timings(omap_crtc->channel, | ||
604 | &omap_crtc->timings); | ||
605 | set_enabled(&omap_crtc->base, true); | ||
606 | } | 630 | } |
607 | 631 | ||
608 | omap_crtc->full_update = false; | 632 | omap_crtc->full_update = false; |
@@ -613,10 +637,30 @@ static void omap_crtc_post_apply(struct omap_drm_apply *apply) | |||
613 | /* nothing needed for post-apply */ | 637 | /* nothing needed for post-apply */ |
614 | } | 638 | } |
615 | 639 | ||
640 | void omap_crtc_flush(struct drm_crtc *crtc) | ||
641 | { | ||
642 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | ||
643 | int loops = 0; | ||
644 | |||
645 | while (!list_empty(&omap_crtc->pending_applies) || | ||
646 | !list_empty(&omap_crtc->queued_applies) || | ||
647 | omap_crtc->event || omap_crtc->old_fb) { | ||
648 | |||
649 | if (++loops > 10) { | ||
650 | dev_err(crtc->dev->dev, | ||
651 | "omap_crtc_flush() timeout\n"); | ||
652 | break; | ||
653 | } | ||
654 | |||
655 | schedule_timeout_uninterruptible(msecs_to_jiffies(20)); | ||
656 | } | ||
657 | } | ||
658 | |||
616 | static const char *channel_names[] = { | 659 | static const char *channel_names[] = { |
617 | [OMAP_DSS_CHANNEL_LCD] = "lcd", | 660 | [OMAP_DSS_CHANNEL_LCD] = "lcd", |
618 | [OMAP_DSS_CHANNEL_DIGIT] = "tv", | 661 | [OMAP_DSS_CHANNEL_DIGIT] = "tv", |
619 | [OMAP_DSS_CHANNEL_LCD2] = "lcd2", | 662 | [OMAP_DSS_CHANNEL_LCD2] = "lcd2", |
663 | [OMAP_DSS_CHANNEL_LCD3] = "lcd3", | ||
620 | }; | 664 | }; |
621 | 665 | ||
622 | void omap_crtc_pre_init(void) | 666 | void omap_crtc_pre_init(void) |