diff options
author | Dave Airlie <airlied@redhat.com> | 2015-08-30 20:25:45 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2015-08-30 20:25:45 -0400 |
commit | 879a37d00f1882b1e56a66e626af4194d592d257 (patch) | |
tree | 94400b07a9d12d4a672097eab494085a5476adfe /drivers/gpu/drm | |
parent | 701078d538e5b2bec95cbbc53cca71c120cd063f (diff) | |
parent | 50002d4c2176da6b2ed5a2529a2367c05f0fd73b (diff) |
Merge branch 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next
This is a second pull-request which adds last part of
atomic modeset/pageflip support, render node support,
clean-up, and fix-up.
* 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos:
drm/exynos: fix build warning to exynos_drm_gem.c
drm/exynos: Properly report supported formats for each device
drm/exynos: add render node support
drm/exynos: implement atomic_{begin/flush} of DECON
drm/exynos: remove legacy ->suspend()/resume()
drm/exynos: Enable atomic modesetting feature
drm/exynos: remove wait queue for pending page flip
drm/exynos: wait all planes updates to finish
drm/exynos: add atomic asynchronous commit
drm/exynos: fimd: only finish update if START == START_S
drm/exynos: add macro to get the address of START_S reg
drm/exynos: check for pending fb before finish update
drm/exynos: fimd: move window protect code to prepare/cleanup_plane
drm/exynos: add prepare and cleanup phases for planes
drm/exynos: fimd: unify call to exynos_drm_crtc_finish_pageflip()
drm/exynos: don't track enabled state at exynos_crtc
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos5433_drm_decon.c | 54 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos7_drm_decon.c | 55 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_crtc.c | 69 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_crtc.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.c | 196 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.h | 24 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fb.c | 35 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fimd.c | 92 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_gem.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_plane.c | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_plane.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_vidi.c | 19 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_mixer.c | 40 |
13 files changed, 451 insertions, 153 deletions
diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c index 484e312e0a22..b3c730770b0f 100644 --- a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c | |||
@@ -54,6 +54,13 @@ static const char * const decon_clks_name[] = { | |||
54 | "sclk_decon_eclk", | 54 | "sclk_decon_eclk", |
55 | }; | 55 | }; |
56 | 56 | ||
57 | static const uint32_t decon_formats[] = { | ||
58 | DRM_FORMAT_XRGB1555, | ||
59 | DRM_FORMAT_RGB565, | ||
60 | DRM_FORMAT_XRGB8888, | ||
61 | DRM_FORMAT_ARGB8888, | ||
62 | }; | ||
63 | |||
57 | static int decon_enable_vblank(struct exynos_drm_crtc *crtc) | 64 | static int decon_enable_vblank(struct exynos_drm_crtc *crtc) |
58 | { | 65 | { |
59 | struct decon_context *ctx = crtc->ctx; | 66 | struct decon_context *ctx = crtc->ctx; |
@@ -219,6 +226,17 @@ static void decon_shadow_protect_win(struct decon_context *ctx, int win, | |||
219 | writel(val, ctx->addr + DECON_SHADOWCON); | 226 | writel(val, ctx->addr + DECON_SHADOWCON); |
220 | } | 227 | } |
221 | 228 | ||
229 | static void decon_atomic_begin(struct exynos_drm_crtc *crtc, | ||
230 | struct exynos_drm_plane *plane) | ||
231 | { | ||
232 | struct decon_context *ctx = crtc->ctx; | ||
233 | |||
234 | if (ctx->suspended) | ||
235 | return; | ||
236 | |||
237 | decon_shadow_protect_win(ctx, plane->zpos, true); | ||
238 | } | ||
239 | |||
222 | static void decon_update_plane(struct exynos_drm_crtc *crtc, | 240 | static void decon_update_plane(struct exynos_drm_crtc *crtc, |
223 | struct exynos_drm_plane *plane) | 241 | struct exynos_drm_plane *plane) |
224 | { | 242 | { |
@@ -232,8 +250,6 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, | |||
232 | if (ctx->suspended) | 250 | if (ctx->suspended) |
233 | return; | 251 | return; |
234 | 252 | ||
235 | decon_shadow_protect_win(ctx, win, true); | ||
236 | |||
237 | val = COORDINATE_X(plane->crtc_x) | COORDINATE_Y(plane->crtc_y); | 253 | val = COORDINATE_X(plane->crtc_x) | COORDINATE_Y(plane->crtc_y); |
238 | writel(val, ctx->addr + DECON_VIDOSDxA(win)); | 254 | writel(val, ctx->addr + DECON_VIDOSDxA(win)); |
239 | 255 | ||
@@ -265,15 +281,10 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, | |||
265 | val |= WINCONx_ENWIN_F; | 281 | val |= WINCONx_ENWIN_F; |
266 | writel(val, ctx->addr + DECON_WINCONx(win)); | 282 | writel(val, ctx->addr + DECON_WINCONx(win)); |
267 | 283 | ||
268 | decon_shadow_protect_win(ctx, win, false); | ||
269 | |||
270 | /* standalone update */ | 284 | /* standalone update */ |
271 | val = readl(ctx->addr + DECON_UPDATE); | 285 | val = readl(ctx->addr + DECON_UPDATE); |
272 | val |= STANDALONE_UPDATE_F; | 286 | val |= STANDALONE_UPDATE_F; |
273 | writel(val, ctx->addr + DECON_UPDATE); | 287 | writel(val, ctx->addr + DECON_UPDATE); |
274 | |||
275 | if (ctx->i80_if) | ||
276 | atomic_set(&ctx->win_updated, 1); | ||
277 | } | 288 | } |
278 | 289 | ||
279 | static void decon_disable_plane(struct exynos_drm_crtc *crtc, | 290 | static void decon_disable_plane(struct exynos_drm_crtc *crtc, |
@@ -301,6 +312,20 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc, | |||
301 | writel(val, ctx->addr + DECON_UPDATE); | 312 | writel(val, ctx->addr + DECON_UPDATE); |
302 | } | 313 | } |
303 | 314 | ||
315 | static void decon_atomic_flush(struct exynos_drm_crtc *crtc, | ||
316 | struct exynos_drm_plane *plane) | ||
317 | { | ||
318 | struct decon_context *ctx = crtc->ctx; | ||
319 | |||
320 | if (ctx->suspended) | ||
321 | return; | ||
322 | |||
323 | decon_shadow_protect_win(ctx, plane->zpos, false); | ||
324 | |||
325 | if (ctx->i80_if) | ||
326 | atomic_set(&ctx->win_updated, 1); | ||
327 | } | ||
328 | |||
304 | static void decon_swreset(struct decon_context *ctx) | 329 | static void decon_swreset(struct decon_context *ctx) |
305 | { | 330 | { |
306 | unsigned int tries; | 331 | unsigned int tries; |
@@ -455,8 +480,10 @@ static struct exynos_drm_crtc_ops decon_crtc_ops = { | |||
455 | .enable_vblank = decon_enable_vblank, | 480 | .enable_vblank = decon_enable_vblank, |
456 | .disable_vblank = decon_disable_vblank, | 481 | .disable_vblank = decon_disable_vblank, |
457 | .commit = decon_commit, | 482 | .commit = decon_commit, |
483 | .atomic_begin = decon_atomic_begin, | ||
458 | .update_plane = decon_update_plane, | 484 | .update_plane = decon_update_plane, |
459 | .disable_plane = decon_disable_plane, | 485 | .disable_plane = decon_disable_plane, |
486 | .atomic_flush = decon_atomic_flush, | ||
460 | .te_handler = decon_te_irq_handler, | 487 | .te_handler = decon_te_irq_handler, |
461 | }; | 488 | }; |
462 | 489 | ||
@@ -477,7 +504,8 @@ static int decon_bind(struct device *dev, struct device *master, void *data) | |||
477 | type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY : | 504 | type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY : |
478 | DRM_PLANE_TYPE_OVERLAY; | 505 | DRM_PLANE_TYPE_OVERLAY; |
479 | ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], | 506 | ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], |
480 | 1 << ctx->pipe, type, zpos); | 507 | 1 << ctx->pipe, type, decon_formats, |
508 | ARRAY_SIZE(decon_formats), zpos); | ||
481 | if (ret) | 509 | if (ret) |
482 | return ret; | 510 | return ret; |
483 | } | 511 | } |
@@ -542,13 +570,21 @@ static irqreturn_t decon_lcd_sys_irq_handler(int irq, void *dev_id) | |||
542 | { | 570 | { |
543 | struct decon_context *ctx = dev_id; | 571 | struct decon_context *ctx = dev_id; |
544 | u32 val; | 572 | u32 val; |
573 | int win; | ||
545 | 574 | ||
546 | if (!test_bit(BIT_CLKS_ENABLED, &ctx->enabled)) | 575 | if (!test_bit(BIT_CLKS_ENABLED, &ctx->enabled)) |
547 | goto out; | 576 | goto out; |
548 | 577 | ||
549 | val = readl(ctx->addr + DECON_VIDINTCON1); | 578 | val = readl(ctx->addr + DECON_VIDINTCON1); |
550 | if (val & VIDINTCON1_INTFRMDONEPEND) { | 579 | if (val & VIDINTCON1_INTFRMDONEPEND) { |
551 | exynos_drm_crtc_finish_pageflip(ctx->crtc); | 580 | for (win = 0 ; win < WINDOWS_NR ; win++) { |
581 | struct exynos_drm_plane *plane = &ctx->planes[win]; | ||
582 | |||
583 | if (!plane->pending_fb) | ||
584 | continue; | ||
585 | |||
586 | exynos_drm_crtc_finish_update(ctx->crtc, plane); | ||
587 | } | ||
552 | 588 | ||
553 | /* clear */ | 589 | /* clear */ |
554 | writel(VIDINTCON1_INTFRMDONEPEND, | 590 | writel(VIDINTCON1_INTFRMDONEPEND, |
diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c index 07926547c94f..cbdb78ef3bac 100644 --- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c | |||
@@ -70,6 +70,18 @@ static const struct of_device_id decon_driver_dt_match[] = { | |||
70 | }; | 70 | }; |
71 | MODULE_DEVICE_TABLE(of, decon_driver_dt_match); | 71 | MODULE_DEVICE_TABLE(of, decon_driver_dt_match); |
72 | 72 | ||
73 | static const uint32_t decon_formats[] = { | ||
74 | DRM_FORMAT_RGB565, | ||
75 | DRM_FORMAT_XRGB8888, | ||
76 | DRM_FORMAT_XBGR8888, | ||
77 | DRM_FORMAT_RGBX8888, | ||
78 | DRM_FORMAT_BGRX8888, | ||
79 | DRM_FORMAT_ARGB8888, | ||
80 | DRM_FORMAT_ABGR8888, | ||
81 | DRM_FORMAT_RGBA8888, | ||
82 | DRM_FORMAT_BGRA8888, | ||
83 | }; | ||
84 | |||
73 | static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc) | 85 | static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc) |
74 | { | 86 | { |
75 | struct decon_context *ctx = crtc->ctx; | 87 | struct decon_context *ctx = crtc->ctx; |
@@ -383,6 +395,17 @@ static void decon_shadow_protect_win(struct decon_context *ctx, | |||
383 | writel(val, ctx->regs + SHADOWCON); | 395 | writel(val, ctx->regs + SHADOWCON); |
384 | } | 396 | } |
385 | 397 | ||
398 | static void decon_atomic_begin(struct exynos_drm_crtc *crtc, | ||
399 | struct exynos_drm_plane *plane) | ||
400 | { | ||
401 | struct decon_context *ctx = crtc->ctx; | ||
402 | |||
403 | if (ctx->suspended) | ||
404 | return; | ||
405 | |||
406 | decon_shadow_protect_win(ctx, plane->zpos, true); | ||
407 | } | ||
408 | |||
386 | static void decon_update_plane(struct exynos_drm_crtc *crtc, | 409 | static void decon_update_plane(struct exynos_drm_crtc *crtc, |
387 | struct exynos_drm_plane *plane) | 410 | struct exynos_drm_plane *plane) |
388 | { | 411 | { |
@@ -410,9 +433,6 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, | |||
410 | * is set. | 433 | * is set. |
411 | */ | 434 | */ |
412 | 435 | ||
413 | /* protect windows */ | ||
414 | decon_shadow_protect_win(ctx, win, true); | ||
415 | |||
416 | /* buffer start address */ | 436 | /* buffer start address */ |
417 | val = (unsigned long)plane->dma_addr[0]; | 437 | val = (unsigned long)plane->dma_addr[0]; |
418 | writel(val, ctx->regs + VIDW_BUF_START(win)); | 438 | writel(val, ctx->regs + VIDW_BUF_START(win)); |
@@ -510,14 +530,22 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc, | |||
510 | val &= ~WINCONx_ENWIN; | 530 | val &= ~WINCONx_ENWIN; |
511 | writel(val, ctx->regs + WINCON(win)); | 531 | writel(val, ctx->regs + WINCON(win)); |
512 | 532 | ||
513 | /* unprotect windows */ | ||
514 | decon_shadow_protect_win(ctx, win, false); | ||
515 | |||
516 | val = readl(ctx->regs + DECON_UPDATE); | 533 | val = readl(ctx->regs + DECON_UPDATE); |
517 | val |= DECON_UPDATE_STANDALONE_F; | 534 | val |= DECON_UPDATE_STANDALONE_F; |
518 | writel(val, ctx->regs + DECON_UPDATE); | 535 | writel(val, ctx->regs + DECON_UPDATE); |
519 | } | 536 | } |
520 | 537 | ||
538 | static void decon_atomic_flush(struct exynos_drm_crtc *crtc, | ||
539 | struct exynos_drm_plane *plane) | ||
540 | { | ||
541 | struct decon_context *ctx = crtc->ctx; | ||
542 | |||
543 | if (ctx->suspended) | ||
544 | return; | ||
545 | |||
546 | decon_shadow_protect_win(ctx, plane->zpos, false); | ||
547 | } | ||
548 | |||
521 | static void decon_init(struct decon_context *ctx) | 549 | static void decon_init(struct decon_context *ctx) |
522 | { | 550 | { |
523 | u32 val; | 551 | u32 val; |
@@ -614,8 +642,10 @@ static const struct exynos_drm_crtc_ops decon_crtc_ops = { | |||
614 | .enable_vblank = decon_enable_vblank, | 642 | .enable_vblank = decon_enable_vblank, |
615 | .disable_vblank = decon_disable_vblank, | 643 | .disable_vblank = decon_disable_vblank, |
616 | .wait_for_vblank = decon_wait_for_vblank, | 644 | .wait_for_vblank = decon_wait_for_vblank, |
645 | .atomic_begin = decon_atomic_begin, | ||
617 | .update_plane = decon_update_plane, | 646 | .update_plane = decon_update_plane, |
618 | .disable_plane = decon_disable_plane, | 647 | .disable_plane = decon_disable_plane, |
648 | .atomic_flush = decon_atomic_flush, | ||
619 | }; | 649 | }; |
620 | 650 | ||
621 | 651 | ||
@@ -623,6 +653,7 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id) | |||
623 | { | 653 | { |
624 | struct decon_context *ctx = (struct decon_context *)dev_id; | 654 | struct decon_context *ctx = (struct decon_context *)dev_id; |
625 | u32 val, clear_bit; | 655 | u32 val, clear_bit; |
656 | int win; | ||
626 | 657 | ||
627 | val = readl(ctx->regs + VIDINTCON1); | 658 | val = readl(ctx->regs + VIDINTCON1); |
628 | 659 | ||
@@ -636,7 +667,14 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id) | |||
636 | 667 | ||
637 | if (!ctx->i80_if) { | 668 | if (!ctx->i80_if) { |
638 | drm_crtc_handle_vblank(&ctx->crtc->base); | 669 | drm_crtc_handle_vblank(&ctx->crtc->base); |
639 | exynos_drm_crtc_finish_pageflip(ctx->crtc); | 670 | for (win = 0 ; win < WINDOWS_NR ; win++) { |
671 | struct exynos_drm_plane *plane = &ctx->planes[win]; | ||
672 | |||
673 | if (!plane->pending_fb) | ||
674 | continue; | ||
675 | |||
676 | exynos_drm_crtc_finish_update(ctx->crtc, plane); | ||
677 | } | ||
640 | 678 | ||
641 | /* set wait vsync event to zero and wake up queue. */ | 679 | /* set wait vsync event to zero and wake up queue. */ |
642 | if (atomic_read(&ctx->wait_vsync_event)) { | 680 | if (atomic_read(&ctx->wait_vsync_event)) { |
@@ -667,7 +705,8 @@ static int decon_bind(struct device *dev, struct device *master, void *data) | |||
667 | type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY : | 705 | type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY : |
668 | DRM_PLANE_TYPE_OVERLAY; | 706 | DRM_PLANE_TYPE_OVERLAY; |
669 | ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], | 707 | ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], |
670 | 1 << ctx->pipe, type, zpos); | 708 | 1 << ctx->pipe, type, decon_formats, |
709 | ARRAY_SIZE(decon_formats), zpos); | ||
671 | if (ret) | 710 | if (ret) |
672 | return ret; | 711 | return ret; |
673 | } | 712 | } |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index c47899738eb4..0872aa2f450f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c | |||
@@ -25,14 +25,9 @@ static void exynos_drm_crtc_enable(struct drm_crtc *crtc) | |||
25 | { | 25 | { |
26 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | 26 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); |
27 | 27 | ||
28 | if (exynos_crtc->enabled) | ||
29 | return; | ||
30 | |||
31 | if (exynos_crtc->ops->enable) | 28 | if (exynos_crtc->ops->enable) |
32 | exynos_crtc->ops->enable(exynos_crtc); | 29 | exynos_crtc->ops->enable(exynos_crtc); |
33 | 30 | ||
34 | exynos_crtc->enabled = true; | ||
35 | |||
36 | drm_crtc_vblank_on(crtc); | 31 | drm_crtc_vblank_on(crtc); |
37 | } | 32 | } |
38 | 33 | ||
@@ -40,20 +35,10 @@ static void exynos_drm_crtc_disable(struct drm_crtc *crtc) | |||
40 | { | 35 | { |
41 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | 36 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); |
42 | 37 | ||
43 | if (!exynos_crtc->enabled) | ||
44 | return; | ||
45 | |||
46 | /* wait for the completion of page flip. */ | ||
47 | if (!wait_event_timeout(exynos_crtc->pending_flip_queue, | ||
48 | (exynos_crtc->event == NULL), HZ/20)) | ||
49 | exynos_crtc->event = NULL; | ||
50 | |||
51 | drm_crtc_vblank_off(crtc); | 38 | drm_crtc_vblank_off(crtc); |
52 | 39 | ||
53 | if (exynos_crtc->ops->disable) | 40 | if (exynos_crtc->ops->disable) |
54 | exynos_crtc->ops->disable(exynos_crtc); | 41 | exynos_crtc->ops->disable(exynos_crtc); |
55 | |||
56 | exynos_crtc->enabled = false; | ||
57 | } | 42 | } |
58 | 43 | ||
59 | static bool | 44 | static bool |
@@ -83,16 +68,32 @@ static void exynos_crtc_atomic_begin(struct drm_crtc *crtc, | |||
83 | struct drm_crtc_state *old_crtc_state) | 68 | struct drm_crtc_state *old_crtc_state) |
84 | { | 69 | { |
85 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | 70 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); |
71 | struct drm_plane *plane; | ||
72 | |||
73 | exynos_crtc->event = crtc->state->event; | ||
86 | 74 | ||
87 | if (crtc->state->event) { | 75 | drm_atomic_crtc_for_each_plane(plane, crtc) { |
88 | WARN_ON(drm_crtc_vblank_get(crtc) != 0); | 76 | struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); |
89 | exynos_crtc->event = crtc->state->event; | 77 | |
78 | if (exynos_crtc->ops->atomic_begin) | ||
79 | exynos_crtc->ops->atomic_begin(exynos_crtc, | ||
80 | exynos_plane); | ||
90 | } | 81 | } |
91 | } | 82 | } |
92 | 83 | ||
93 | static void exynos_crtc_atomic_flush(struct drm_crtc *crtc, | 84 | static void exynos_crtc_atomic_flush(struct drm_crtc *crtc, |
94 | struct drm_crtc_state *old_crtc_state) | 85 | struct drm_crtc_state *old_crtc_state) |
95 | { | 86 | { |
87 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | ||
88 | struct drm_plane *plane; | ||
89 | |||
90 | drm_atomic_crtc_for_each_plane(plane, crtc) { | ||
91 | struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); | ||
92 | |||
93 | if (exynos_crtc->ops->atomic_flush) | ||
94 | exynos_crtc->ops->atomic_flush(exynos_crtc, | ||
95 | exynos_plane); | ||
96 | } | ||
96 | } | 97 | } |
97 | 98 | ||
98 | static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { | 99 | static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { |
@@ -140,13 +141,13 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, | |||
140 | if (!exynos_crtc) | 141 | if (!exynos_crtc) |
141 | return ERR_PTR(-ENOMEM); | 142 | return ERR_PTR(-ENOMEM); |
142 | 143 | ||
143 | init_waitqueue_head(&exynos_crtc->pending_flip_queue); | ||
144 | |||
145 | exynos_crtc->pipe = pipe; | 144 | exynos_crtc->pipe = pipe; |
146 | exynos_crtc->type = type; | 145 | exynos_crtc->type = type; |
147 | exynos_crtc->ops = ops; | 146 | exynos_crtc->ops = ops; |
148 | exynos_crtc->ctx = ctx; | 147 | exynos_crtc->ctx = ctx; |
149 | 148 | ||
149 | init_waitqueue_head(&exynos_crtc->wait_update); | ||
150 | |||
150 | crtc = &exynos_crtc->base; | 151 | crtc = &exynos_crtc->base; |
151 | 152 | ||
152 | private->crtc[pipe] = crtc; | 153 | private->crtc[pipe] = crtc; |
@@ -172,9 +173,6 @@ int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe) | |||
172 | struct exynos_drm_crtc *exynos_crtc = | 173 | struct exynos_drm_crtc *exynos_crtc = |
173 | to_exynos_crtc(private->crtc[pipe]); | 174 | to_exynos_crtc(private->crtc[pipe]); |
174 | 175 | ||
175 | if (!exynos_crtc->enabled) | ||
176 | return -EPERM; | ||
177 | |||
178 | if (exynos_crtc->ops->enable_vblank) | 176 | if (exynos_crtc->ops->enable_vblank) |
179 | return exynos_crtc->ops->enable_vblank(exynos_crtc); | 177 | return exynos_crtc->ops->enable_vblank(exynos_crtc); |
180 | 178 | ||
@@ -187,26 +185,31 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe) | |||
187 | struct exynos_drm_crtc *exynos_crtc = | 185 | struct exynos_drm_crtc *exynos_crtc = |
188 | to_exynos_crtc(private->crtc[pipe]); | 186 | to_exynos_crtc(private->crtc[pipe]); |
189 | 187 | ||
190 | if (!exynos_crtc->enabled) | ||
191 | return; | ||
192 | |||
193 | if (exynos_crtc->ops->disable_vblank) | 188 | if (exynos_crtc->ops->disable_vblank) |
194 | exynos_crtc->ops->disable_vblank(exynos_crtc); | 189 | exynos_crtc->ops->disable_vblank(exynos_crtc); |
195 | } | 190 | } |
196 | 191 | ||
197 | void exynos_drm_crtc_finish_pageflip(struct exynos_drm_crtc *exynos_crtc) | 192 | void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc) |
193 | { | ||
194 | wait_event_timeout(exynos_crtc->wait_update, | ||
195 | (atomic_read(&exynos_crtc->pending_update) == 0), | ||
196 | msecs_to_jiffies(50)); | ||
197 | } | ||
198 | |||
199 | void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc, | ||
200 | struct exynos_drm_plane *exynos_plane) | ||
198 | { | 201 | { |
199 | struct drm_crtc *crtc = &exynos_crtc->base; | 202 | struct drm_crtc *crtc = &exynos_crtc->base; |
200 | unsigned long flags; | 203 | unsigned long flags; |
201 | 204 | ||
202 | spin_lock_irqsave(&crtc->dev->event_lock, flags); | 205 | exynos_plane->pending_fb = NULL; |
203 | if (exynos_crtc->event) { | ||
204 | 206 | ||
205 | drm_crtc_send_vblank_event(crtc, exynos_crtc->event); | 207 | if (atomic_dec_and_test(&exynos_crtc->pending_update)) |
206 | drm_crtc_vblank_put(crtc); | 208 | wake_up(&exynos_crtc->wait_update); |
207 | wake_up(&exynos_crtc->pending_flip_queue); | ||
208 | 209 | ||
209 | } | 210 | spin_lock_irqsave(&crtc->dev->event_lock, flags); |
211 | if (exynos_crtc->event) | ||
212 | drm_crtc_send_vblank_event(crtc, exynos_crtc->event); | ||
210 | 213 | ||
211 | exynos_crtc->event = NULL; | 214 | exynos_crtc->event = NULL; |
212 | spin_unlock_irqrestore(&crtc->dev->event_lock, flags); | 215 | spin_unlock_irqrestore(&crtc->dev->event_lock, flags); |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index 9e7027d6c2f6..f87d4abda6f7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h | |||
@@ -25,7 +25,9 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, | |||
25 | void *context); | 25 | void *context); |
26 | int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe); | 26 | int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe); |
27 | void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe); | 27 | void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe); |
28 | void exynos_drm_crtc_finish_pageflip(struct exynos_drm_crtc *exynos_crtc); | 28 | void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc); |
29 | void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc, | ||
30 | struct exynos_drm_plane *exynos_plane); | ||
29 | void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb); | 31 | void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb); |
30 | 32 | ||
31 | /* This function gets pipe value to crtc device matched with out_type. */ | 33 | /* This function gets pipe value to crtc device matched with out_type. */ |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index fa5194caf259..831d2e4cacf9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c | |||
@@ -13,6 +13,8 @@ | |||
13 | 13 | ||
14 | #include <linux/pm_runtime.h> | 14 | #include <linux/pm_runtime.h> |
15 | #include <drm/drmP.h> | 15 | #include <drm/drmP.h> |
16 | #include <drm/drm_atomic.h> | ||
17 | #include <drm/drm_atomic_helper.h> | ||
16 | #include <drm/drm_crtc_helper.h> | 18 | #include <drm/drm_crtc_helper.h> |
17 | 19 | ||
18 | #include <linux/component.h> | 20 | #include <linux/component.h> |
@@ -36,6 +38,98 @@ | |||
36 | #define DRIVER_MAJOR 1 | 38 | #define DRIVER_MAJOR 1 |
37 | #define DRIVER_MINOR 0 | 39 | #define DRIVER_MINOR 0 |
38 | 40 | ||
41 | struct exynos_atomic_commit { | ||
42 | struct work_struct work; | ||
43 | struct drm_device *dev; | ||
44 | struct drm_atomic_state *state; | ||
45 | u32 crtcs; | ||
46 | }; | ||
47 | |||
48 | static void exynos_atomic_wait_for_commit(struct drm_atomic_state *state) | ||
49 | { | ||
50 | struct drm_crtc_state *crtc_state; | ||
51 | struct drm_crtc *crtc; | ||
52 | int i, ret; | ||
53 | |||
54 | for_each_crtc_in_state(state, crtc, crtc_state, i) { | ||
55 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | ||
56 | |||
57 | if (!crtc->state->enable) | ||
58 | continue; | ||
59 | |||
60 | ret = drm_crtc_vblank_get(crtc); | ||
61 | if (ret) | ||
62 | continue; | ||
63 | |||
64 | exynos_drm_crtc_wait_pending_update(exynos_crtc); | ||
65 | drm_crtc_vblank_put(crtc); | ||
66 | } | ||
67 | } | ||
68 | |||
69 | static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit) | ||
70 | { | ||
71 | struct drm_device *dev = commit->dev; | ||
72 | struct exynos_drm_private *priv = dev->dev_private; | ||
73 | struct drm_atomic_state *state = commit->state; | ||
74 | struct drm_plane *plane; | ||
75 | struct drm_crtc *crtc; | ||
76 | struct drm_plane_state *plane_state; | ||
77 | struct drm_crtc_state *crtc_state; | ||
78 | int i; | ||
79 | |||
80 | drm_atomic_helper_commit_modeset_disables(dev, state); | ||
81 | |||
82 | drm_atomic_helper_commit_modeset_enables(dev, state); | ||
83 | |||
84 | /* | ||
85 | * Exynos can't update planes with CRTCs and encoders disabled, | ||
86 | * its updates routines, specially for FIMD, requires the clocks | ||
87 | * to be enabled. So it is necessary to handle the modeset operations | ||
88 | * *before* the commit_planes() step, this way it will always | ||
89 | * have the relevant clocks enabled to perform the update. | ||
90 | */ | ||
91 | |||
92 | for_each_crtc_in_state(state, crtc, crtc_state, i) { | ||
93 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | ||
94 | |||
95 | atomic_set(&exynos_crtc->pending_update, 0); | ||
96 | } | ||
97 | |||
98 | for_each_plane_in_state(state, plane, plane_state, i) { | ||
99 | struct exynos_drm_crtc *exynos_crtc = | ||
100 | to_exynos_crtc(plane->crtc); | ||
101 | |||
102 | if (!plane->crtc) | ||
103 | continue; | ||
104 | |||
105 | atomic_inc(&exynos_crtc->pending_update); | ||
106 | } | ||
107 | |||
108 | drm_atomic_helper_commit_planes(dev, state); | ||
109 | |||
110 | exynos_atomic_wait_for_commit(state); | ||
111 | |||
112 | drm_atomic_helper_cleanup_planes(dev, state); | ||
113 | |||
114 | drm_atomic_state_free(state); | ||
115 | |||
116 | spin_lock(&priv->lock); | ||
117 | priv->pending &= ~commit->crtcs; | ||
118 | spin_unlock(&priv->lock); | ||
119 | |||
120 | wake_up_all(&priv->wait); | ||
121 | |||
122 | kfree(commit); | ||
123 | } | ||
124 | |||
125 | static void exynos_drm_atomic_work(struct work_struct *work) | ||
126 | { | ||
127 | struct exynos_atomic_commit *commit = container_of(work, | ||
128 | struct exynos_atomic_commit, work); | ||
129 | |||
130 | exynos_atomic_commit_complete(commit); | ||
131 | } | ||
132 | |||
39 | static int exynos_drm_load(struct drm_device *dev, unsigned long flags) | 133 | static int exynos_drm_load(struct drm_device *dev, unsigned long flags) |
40 | { | 134 | { |
41 | struct exynos_drm_private *private; | 135 | struct exynos_drm_private *private; |
@@ -47,6 +141,9 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) | |||
47 | if (!private) | 141 | if (!private) |
48 | return -ENOMEM; | 142 | return -ENOMEM; |
49 | 143 | ||
144 | init_waitqueue_head(&private->wait); | ||
145 | spin_lock_init(&private->lock); | ||
146 | |||
50 | dev_set_drvdata(dev->dev, dev); | 147 | dev_set_drvdata(dev->dev, dev); |
51 | dev->dev_private = (void *)private; | 148 | dev->dev_private = (void *)private; |
52 | 149 | ||
@@ -149,6 +246,64 @@ static int exynos_drm_unload(struct drm_device *dev) | |||
149 | return 0; | 246 | return 0; |
150 | } | 247 | } |
151 | 248 | ||
249 | static int commit_is_pending(struct exynos_drm_private *priv, u32 crtcs) | ||
250 | { | ||
251 | bool pending; | ||
252 | |||
253 | spin_lock(&priv->lock); | ||
254 | pending = priv->pending & crtcs; | ||
255 | spin_unlock(&priv->lock); | ||
256 | |||
257 | return pending; | ||
258 | } | ||
259 | |||
260 | int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, | ||
261 | bool async) | ||
262 | { | ||
263 | struct exynos_drm_private *priv = dev->dev_private; | ||
264 | struct exynos_atomic_commit *commit; | ||
265 | int i, ret; | ||
266 | |||
267 | commit = kzalloc(sizeof(*commit), GFP_KERNEL); | ||
268 | if (!commit) | ||
269 | return -ENOMEM; | ||
270 | |||
271 | ret = drm_atomic_helper_prepare_planes(dev, state); | ||
272 | if (ret) { | ||
273 | kfree(commit); | ||
274 | return ret; | ||
275 | } | ||
276 | |||
277 | /* This is the point of no return */ | ||
278 | |||
279 | INIT_WORK(&commit->work, exynos_drm_atomic_work); | ||
280 | commit->dev = dev; | ||
281 | commit->state = state; | ||
282 | |||
283 | /* Wait until all affected CRTCs have completed previous commits and | ||
284 | * mark them as pending. | ||
285 | */ | ||
286 | for (i = 0; i < dev->mode_config.num_crtc; ++i) { | ||
287 | if (state->crtcs[i]) | ||
288 | commit->crtcs |= 1 << drm_crtc_index(state->crtcs[i]); | ||
289 | } | ||
290 | |||
291 | wait_event(priv->wait, !commit_is_pending(priv, commit->crtcs)); | ||
292 | |||
293 | spin_lock(&priv->lock); | ||
294 | priv->pending |= commit->crtcs; | ||
295 | spin_unlock(&priv->lock); | ||
296 | |||
297 | drm_atomic_helper_swap_state(dev, state); | ||
298 | |||
299 | if (async) | ||
300 | schedule_work(&commit->work); | ||
301 | else | ||
302 | exynos_atomic_commit_complete(commit); | ||
303 | |||
304 | return 0; | ||
305 | } | ||
306 | |||
152 | static int exynos_drm_suspend(struct drm_device *dev, pm_message_t state) | 307 | static int exynos_drm_suspend(struct drm_device *dev, pm_message_t state) |
153 | { | 308 | { |
154 | struct drm_connector *connector; | 309 | struct drm_connector *connector; |
@@ -248,25 +403,25 @@ static const struct vm_operations_struct exynos_drm_gem_vm_ops = { | |||
248 | 403 | ||
249 | static const struct drm_ioctl_desc exynos_ioctls[] = { | 404 | static const struct drm_ioctl_desc exynos_ioctls[] = { |
250 | DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl, | 405 | DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl, |
406 | DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), | ||
407 | DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, exynos_drm_gem_get_ioctl, | ||
408 | DRM_UNLOCKED | DRM_RENDER_ALLOW), | ||
409 | DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, vidi_connection_ioctl, | ||
251 | DRM_UNLOCKED | DRM_AUTH), | 410 | DRM_UNLOCKED | DRM_AUTH), |
252 | DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, | 411 | DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, exynos_g2d_get_ver_ioctl, |
253 | exynos_drm_gem_get_ioctl, DRM_UNLOCKED), | 412 | DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), |
254 | DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, | 413 | DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, exynos_g2d_set_cmdlist_ioctl, |
255 | vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH), | 414 | DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), |
256 | DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, | 415 | DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl, |
257 | exynos_g2d_get_ver_ioctl, DRM_UNLOCKED | DRM_AUTH), | 416 | DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), |
258 | DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, | 417 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_PROPERTY, exynos_drm_ipp_get_property, |
259 | exynos_g2d_set_cmdlist_ioctl, DRM_UNLOCKED | DRM_AUTH), | 418 | DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), |
260 | DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, | 419 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_SET_PROPERTY, exynos_drm_ipp_set_property, |
261 | exynos_g2d_exec_ioctl, DRM_UNLOCKED | DRM_AUTH), | 420 | DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), |
262 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_PROPERTY, | 421 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_QUEUE_BUF, exynos_drm_ipp_queue_buf, |
263 | exynos_drm_ipp_get_property, DRM_UNLOCKED | DRM_AUTH), | 422 | DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), |
264 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_SET_PROPERTY, | 423 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_CMD_CTRL, exynos_drm_ipp_cmd_ctrl, |
265 | exynos_drm_ipp_set_property, DRM_UNLOCKED | DRM_AUTH), | 424 | DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), |
266 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_QUEUE_BUF, | ||
267 | exynos_drm_ipp_queue_buf, DRM_UNLOCKED | DRM_AUTH), | ||
268 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_CMD_CTRL, | ||
269 | exynos_drm_ipp_cmd_ctrl, DRM_UNLOCKED | DRM_AUTH), | ||
270 | }; | 425 | }; |
271 | 426 | ||
272 | static const struct file_operations exynos_drm_driver_fops = { | 427 | static const struct file_operations exynos_drm_driver_fops = { |
@@ -283,11 +438,10 @@ static const struct file_operations exynos_drm_driver_fops = { | |||
283 | }; | 438 | }; |
284 | 439 | ||
285 | static struct drm_driver exynos_drm_driver = { | 440 | static struct drm_driver exynos_drm_driver = { |
286 | .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, | 441 | .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
442 | | DRIVER_ATOMIC | DRIVER_RENDER, | ||
287 | .load = exynos_drm_load, | 443 | .load = exynos_drm_load, |
288 | .unload = exynos_drm_unload, | 444 | .unload = exynos_drm_unload, |
289 | .suspend = exynos_drm_suspend, | ||
290 | .resume = exynos_drm_resume, | ||
291 | .open = exynos_drm_open, | 445 | .open = exynos_drm_open, |
292 | .preclose = exynos_drm_preclose, | 446 | .preclose = exynos_drm_preclose, |
293 | .lastclose = exynos_drm_lastclose, | 447 | .lastclose = exynos_drm_lastclose, |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 6b8a30f23473..b7ba21dfb696 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h | |||
@@ -74,6 +74,7 @@ struct exynos_drm_plane { | |||
74 | unsigned int v_ratio; | 74 | unsigned int v_ratio; |
75 | dma_addr_t dma_addr[MAX_FB_BUFFER]; | 75 | dma_addr_t dma_addr[MAX_FB_BUFFER]; |
76 | unsigned int zpos; | 76 | unsigned int zpos; |
77 | struct drm_framebuffer *pending_fb; | ||
77 | }; | 78 | }; |
78 | 79 | ||
79 | /* | 80 | /* |
@@ -87,6 +88,8 @@ struct exynos_drm_plane { | |||
87 | * @disable_vblank: specific driver callback for disabling vblank interrupt. | 88 | * @disable_vblank: specific driver callback for disabling vblank interrupt. |
88 | * @wait_for_vblank: wait for vblank interrupt to make sure that | 89 | * @wait_for_vblank: wait for vblank interrupt to make sure that |
89 | * hardware overlay is updated. | 90 | * hardware overlay is updated. |
91 | * @atomic_begin: prepare a window to receive a update | ||
92 | * @atomic_flush: mark the end of a window update | ||
90 | * @update_plane: apply hardware specific overlay data to registers. | 93 | * @update_plane: apply hardware specific overlay data to registers. |
91 | * @disable_plane: disable hardware specific overlay. | 94 | * @disable_plane: disable hardware specific overlay. |
92 | * @te_handler: trigger to transfer video image at the tearing effect | 95 | * @te_handler: trigger to transfer video image at the tearing effect |
@@ -107,10 +110,14 @@ struct exynos_drm_crtc_ops { | |||
107 | int (*enable_vblank)(struct exynos_drm_crtc *crtc); | 110 | int (*enable_vblank)(struct exynos_drm_crtc *crtc); |
108 | void (*disable_vblank)(struct exynos_drm_crtc *crtc); | 111 | void (*disable_vblank)(struct exynos_drm_crtc *crtc); |
109 | void (*wait_for_vblank)(struct exynos_drm_crtc *crtc); | 112 | void (*wait_for_vblank)(struct exynos_drm_crtc *crtc); |
113 | void (*atomic_begin)(struct exynos_drm_crtc *crtc, | ||
114 | struct exynos_drm_plane *plane); | ||
110 | void (*update_plane)(struct exynos_drm_crtc *crtc, | 115 | void (*update_plane)(struct exynos_drm_crtc *crtc, |
111 | struct exynos_drm_plane *plane); | 116 | struct exynos_drm_plane *plane); |
112 | void (*disable_plane)(struct exynos_drm_crtc *crtc, | 117 | void (*disable_plane)(struct exynos_drm_crtc *crtc, |
113 | struct exynos_drm_plane *plane); | 118 | struct exynos_drm_plane *plane); |
119 | void (*atomic_flush)(struct exynos_drm_crtc *crtc, | ||
120 | struct exynos_drm_plane *plane); | ||
114 | void (*te_handler)(struct exynos_drm_crtc *crtc); | 121 | void (*te_handler)(struct exynos_drm_crtc *crtc); |
115 | void (*clock_enable)(struct exynos_drm_crtc *crtc, bool enable); | 122 | void (*clock_enable)(struct exynos_drm_crtc *crtc, bool enable); |
116 | }; | 123 | }; |
@@ -129,6 +136,8 @@ struct exynos_drm_crtc_ops { | |||
129 | * this pipe value. | 136 | * this pipe value. |
130 | * @enabled: if the crtc is enabled or not | 137 | * @enabled: if the crtc is enabled or not |
131 | * @event: vblank event that is currently queued for flip | 138 | * @event: vblank event that is currently queued for flip |
139 | * @wait_update: wait all pending planes updates to finish | ||
140 | * @pending_update: number of pending plane updates in this crtc | ||
132 | * @ops: pointer to callbacks for exynos drm specific functionality | 141 | * @ops: pointer to callbacks for exynos drm specific functionality |
133 | * @ctx: A pointer to the crtc's implementation specific context | 142 | * @ctx: A pointer to the crtc's implementation specific context |
134 | */ | 143 | */ |
@@ -136,9 +145,9 @@ struct exynos_drm_crtc { | |||
136 | struct drm_crtc base; | 145 | struct drm_crtc base; |
137 | enum exynos_drm_output_type type; | 146 | enum exynos_drm_output_type type; |
138 | unsigned int pipe; | 147 | unsigned int pipe; |
139 | bool enabled; | ||
140 | wait_queue_head_t pending_flip_queue; | ||
141 | struct drm_pending_vblank_event *event; | 148 | struct drm_pending_vblank_event *event; |
149 | wait_queue_head_t wait_update; | ||
150 | atomic_t pending_update; | ||
142 | const struct exynos_drm_crtc_ops *ops; | 151 | const struct exynos_drm_crtc_ops *ops; |
143 | void *ctx; | 152 | void *ctx; |
144 | }; | 153 | }; |
@@ -164,6 +173,9 @@ struct drm_exynos_file_private { | |||
164 | * @da_space_size: size of device address space. | 173 | * @da_space_size: size of device address space. |
165 | * if 0 then default value is used for it. | 174 | * if 0 then default value is used for it. |
166 | * @pipe: the pipe number for this crtc/manager. | 175 | * @pipe: the pipe number for this crtc/manager. |
176 | * @pending: the crtcs that have pending updates to finish | ||
177 | * @lock: protect access to @pending | ||
178 | * @wait: wait an atomic commit to finish | ||
167 | */ | 179 | */ |
168 | struct exynos_drm_private { | 180 | struct exynos_drm_private { |
169 | struct drm_fb_helper *fb_helper; | 181 | struct drm_fb_helper *fb_helper; |
@@ -179,6 +191,11 @@ struct exynos_drm_private { | |||
179 | unsigned long da_space_size; | 191 | unsigned long da_space_size; |
180 | 192 | ||
181 | unsigned int pipe; | 193 | unsigned int pipe; |
194 | |||
195 | /* for atomic commit */ | ||
196 | u32 pending; | ||
197 | spinlock_t lock; | ||
198 | wait_queue_head_t wait; | ||
182 | }; | 199 | }; |
183 | 200 | ||
184 | /* | 201 | /* |
@@ -237,6 +254,9 @@ static inline int exynos_dpi_bind(struct drm_device *dev, | |||
237 | } | 254 | } |
238 | #endif | 255 | #endif |
239 | 256 | ||
257 | int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, | ||
258 | bool async); | ||
259 | |||
240 | 260 | ||
241 | extern struct platform_driver fimd_driver; | 261 | extern struct platform_driver fimd_driver; |
242 | extern struct platform_driver exynos5433_decon_driver; | 262 | extern struct platform_driver exynos5433_decon_driver; |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 9738f4e0c6eb..59ebbe547290 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c | |||
@@ -267,41 +267,6 @@ static void exynos_drm_output_poll_changed(struct drm_device *dev) | |||
267 | exynos_drm_fbdev_init(dev); | 267 | exynos_drm_fbdev_init(dev); |
268 | } | 268 | } |
269 | 269 | ||
270 | static int exynos_atomic_commit(struct drm_device *dev, | ||
271 | struct drm_atomic_state *state, | ||
272 | bool async) | ||
273 | { | ||
274 | int ret; | ||
275 | |||
276 | ret = drm_atomic_helper_prepare_planes(dev, state); | ||
277 | if (ret) | ||
278 | return ret; | ||
279 | |||
280 | /* This is the point of no return */ | ||
281 | |||
282 | drm_atomic_helper_swap_state(dev, state); | ||
283 | |||
284 | drm_atomic_helper_commit_modeset_disables(dev, state); | ||
285 | |||
286 | drm_atomic_helper_commit_modeset_enables(dev, state); | ||
287 | |||
288 | /* | ||
289 | * Exynos can't update planes with CRTCs and encoders disabled, | ||
290 | * its updates routines, specially for FIMD, requires the clocks | ||
291 | * to be enabled. So it is necessary to handle the modeset operations | ||
292 | * *before* the commit_planes() step, this way it will always | ||
293 | * have the relevant clocks enabled to perform the update. | ||
294 | */ | ||
295 | |||
296 | drm_atomic_helper_commit_planes(dev, state); | ||
297 | |||
298 | drm_atomic_helper_cleanup_planes(dev, state); | ||
299 | |||
300 | drm_atomic_state_free(state); | ||
301 | |||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { | 270 | static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { |
306 | .fb_create = exynos_user_fb_create, | 271 | .fb_create = exynos_user_fb_create, |
307 | .output_poll_changed = exynos_drm_output_poll_changed, | 272 | .output_poll_changed = exynos_drm_output_poll_changed, |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 5def6bc073eb..750a9e6b9e8d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c | |||
@@ -59,6 +59,7 @@ | |||
59 | #define VIDWnALPHA1(win) (VIDW_ALPHA + 0x04 + (win) * 8) | 59 | #define VIDWnALPHA1(win) (VIDW_ALPHA + 0x04 + (win) * 8) |
60 | 60 | ||
61 | #define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8) | 61 | #define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8) |
62 | #define VIDWx_BUF_START_S(win, buf) (VIDW_BUF_START_S(buf) + (win) * 8) | ||
62 | #define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8) | 63 | #define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8) |
63 | #define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4) | 64 | #define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4) |
64 | 65 | ||
@@ -187,6 +188,14 @@ static const struct of_device_id fimd_driver_dt_match[] = { | |||
187 | }; | 188 | }; |
188 | MODULE_DEVICE_TABLE(of, fimd_driver_dt_match); | 189 | MODULE_DEVICE_TABLE(of, fimd_driver_dt_match); |
189 | 190 | ||
191 | static const uint32_t fimd_formats[] = { | ||
192 | DRM_FORMAT_C8, | ||
193 | DRM_FORMAT_XRGB1555, | ||
194 | DRM_FORMAT_RGB565, | ||
195 | DRM_FORMAT_XRGB8888, | ||
196 | DRM_FORMAT_ARGB8888, | ||
197 | }; | ||
198 | |||
190 | static inline struct fimd_driver_data *drm_fimd_get_driver_data( | 199 | static inline struct fimd_driver_data *drm_fimd_get_driver_data( |
191 | struct platform_device *pdev) | 200 | struct platform_device *pdev) |
192 | { | 201 | { |
@@ -591,6 +600,16 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx, | |||
591 | { | 600 | { |
592 | u32 reg, bits, val; | 601 | u32 reg, bits, val; |
593 | 602 | ||
603 | /* | ||
604 | * SHADOWCON/PRTCON register is used for enabling timing. | ||
605 | * | ||
606 | * for example, once only width value of a register is set, | ||
607 | * if the dma is started then fimd hardware could malfunction so | ||
608 | * with protect window setting, the register fields with prefix '_F' | ||
609 | * wouldn't be updated at vsync also but updated once unprotect window | ||
610 | * is set. | ||
611 | */ | ||
612 | |||
594 | if (ctx->driver_data->has_shadowcon) { | 613 | if (ctx->driver_data->has_shadowcon) { |
595 | reg = SHADOWCON; | 614 | reg = SHADOWCON; |
596 | bits = SHADOWCON_WINx_PROTECT(win); | 615 | bits = SHADOWCON_WINx_PROTECT(win); |
@@ -607,6 +626,28 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx, | |||
607 | writel(val, ctx->regs + reg); | 626 | writel(val, ctx->regs + reg); |
608 | } | 627 | } |
609 | 628 | ||
629 | static void fimd_atomic_begin(struct exynos_drm_crtc *crtc, | ||
630 | struct exynos_drm_plane *plane) | ||
631 | { | ||
632 | struct fimd_context *ctx = crtc->ctx; | ||
633 | |||
634 | if (ctx->suspended) | ||
635 | return; | ||
636 | |||
637 | fimd_shadow_protect_win(ctx, plane->zpos, true); | ||
638 | } | ||
639 | |||
640 | static void fimd_atomic_flush(struct exynos_drm_crtc *crtc, | ||
641 | struct exynos_drm_plane *plane) | ||
642 | { | ||
643 | struct fimd_context *ctx = crtc->ctx; | ||
644 | |||
645 | if (ctx->suspended) | ||
646 | return; | ||
647 | |||
648 | fimd_shadow_protect_win(ctx, plane->zpos, false); | ||
649 | } | ||
650 | |||
610 | static void fimd_update_plane(struct exynos_drm_crtc *crtc, | 651 | static void fimd_update_plane(struct exynos_drm_crtc *crtc, |
611 | struct exynos_drm_plane *plane) | 652 | struct exynos_drm_plane *plane) |
612 | { | 653 | { |
@@ -622,20 +663,6 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc, | |||
622 | if (ctx->suspended) | 663 | if (ctx->suspended) |
623 | return; | 664 | return; |
624 | 665 | ||
625 | /* | ||
626 | * SHADOWCON/PRTCON register is used for enabling timing. | ||
627 | * | ||
628 | * for example, once only width value of a register is set, | ||
629 | * if the dma is started then fimd hardware could malfunction so | ||
630 | * with protect window setting, the register fields with prefix '_F' | ||
631 | * wouldn't be updated at vsync also but updated once unprotect window | ||
632 | * is set. | ||
633 | */ | ||
634 | |||
635 | /* protect windows */ | ||
636 | fimd_shadow_protect_win(ctx, win, true); | ||
637 | |||
638 | |||
639 | offset = plane->src_x * bpp; | 666 | offset = plane->src_x * bpp; |
640 | offset += plane->src_y * pitch; | 667 | offset += plane->src_y * pitch; |
641 | 668 | ||
@@ -707,9 +734,6 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc, | |||
707 | if (ctx->driver_data->has_shadowcon) | 734 | if (ctx->driver_data->has_shadowcon) |
708 | fimd_enable_shadow_channel_path(ctx, win, true); | 735 | fimd_enable_shadow_channel_path(ctx, win, true); |
709 | 736 | ||
710 | /* Enable DMA channel and unprotect windows */ | ||
711 | fimd_shadow_protect_win(ctx, win, false); | ||
712 | |||
713 | if (ctx->i80_if) | 737 | if (ctx->i80_if) |
714 | atomic_set(&ctx->win_updated, 1); | 738 | atomic_set(&ctx->win_updated, 1); |
715 | } | 739 | } |
@@ -723,16 +747,10 @@ static void fimd_disable_plane(struct exynos_drm_crtc *crtc, | |||
723 | if (ctx->suspended) | 747 | if (ctx->suspended) |
724 | return; | 748 | return; |
725 | 749 | ||
726 | /* protect windows */ | ||
727 | fimd_shadow_protect_win(ctx, win, true); | ||
728 | |||
729 | fimd_enable_video_output(ctx, win, false); | 750 | fimd_enable_video_output(ctx, win, false); |
730 | 751 | ||
731 | if (ctx->driver_data->has_shadowcon) | 752 | if (ctx->driver_data->has_shadowcon) |
732 | fimd_enable_shadow_channel_path(ctx, win, false); | 753 | fimd_enable_shadow_channel_path(ctx, win, false); |
733 | |||
734 | /* unprotect windows */ | ||
735 | fimd_shadow_protect_win(ctx, win, false); | ||
736 | } | 754 | } |
737 | 755 | ||
738 | static void fimd_enable(struct exynos_drm_crtc *crtc) | 756 | static void fimd_enable(struct exynos_drm_crtc *crtc) |
@@ -875,8 +893,10 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = { | |||
875 | .enable_vblank = fimd_enable_vblank, | 893 | .enable_vblank = fimd_enable_vblank, |
876 | .disable_vblank = fimd_disable_vblank, | 894 | .disable_vblank = fimd_disable_vblank, |
877 | .wait_for_vblank = fimd_wait_for_vblank, | 895 | .wait_for_vblank = fimd_wait_for_vblank, |
896 | .atomic_begin = fimd_atomic_begin, | ||
878 | .update_plane = fimd_update_plane, | 897 | .update_plane = fimd_update_plane, |
879 | .disable_plane = fimd_disable_plane, | 898 | .disable_plane = fimd_disable_plane, |
899 | .atomic_flush = fimd_atomic_flush, | ||
880 | .te_handler = fimd_te_handler, | 900 | .te_handler = fimd_te_handler, |
881 | .clock_enable = fimd_dp_clock_enable, | 901 | .clock_enable = fimd_dp_clock_enable, |
882 | }; | 902 | }; |
@@ -884,7 +904,8 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = { | |||
884 | static irqreturn_t fimd_irq_handler(int irq, void *dev_id) | 904 | static irqreturn_t fimd_irq_handler(int irq, void *dev_id) |
885 | { | 905 | { |
886 | struct fimd_context *ctx = (struct fimd_context *)dev_id; | 906 | struct fimd_context *ctx = (struct fimd_context *)dev_id; |
887 | u32 val, clear_bit; | 907 | u32 val, clear_bit, start, start_s; |
908 | int win; | ||
888 | 909 | ||
889 | val = readl(ctx->regs + VIDINTCON1); | 910 | val = readl(ctx->regs + VIDINTCON1); |
890 | 911 | ||
@@ -896,15 +917,25 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) | |||
896 | if (ctx->pipe < 0 || !ctx->drm_dev) | 917 | if (ctx->pipe < 0 || !ctx->drm_dev) |
897 | goto out; | 918 | goto out; |
898 | 919 | ||
899 | if (ctx->i80_if) { | 920 | if (!ctx->i80_if) |
900 | exynos_drm_crtc_finish_pageflip(ctx->crtc); | 921 | drm_crtc_handle_vblank(&ctx->crtc->base); |
922 | |||
923 | for (win = 0 ; win < WINDOWS_NR ; win++) { | ||
924 | struct exynos_drm_plane *plane = &ctx->planes[win]; | ||
901 | 925 | ||
926 | if (!plane->pending_fb) | ||
927 | continue; | ||
928 | |||
929 | start = readl(ctx->regs + VIDWx_BUF_START(win, 0)); | ||
930 | start_s = readl(ctx->regs + VIDWx_BUF_START_S(win, 0)); | ||
931 | if (start == start_s) | ||
932 | exynos_drm_crtc_finish_update(ctx->crtc, plane); | ||
933 | } | ||
934 | |||
935 | if (ctx->i80_if) { | ||
902 | /* Exits triggering mode */ | 936 | /* Exits triggering mode */ |
903 | atomic_set(&ctx->triggering, 0); | 937 | atomic_set(&ctx->triggering, 0); |
904 | } else { | 938 | } else { |
905 | drm_crtc_handle_vblank(&ctx->crtc->base); | ||
906 | exynos_drm_crtc_finish_pageflip(ctx->crtc); | ||
907 | |||
908 | /* set wait vsync event to zero and wake up queue. */ | 939 | /* set wait vsync event to zero and wake up queue. */ |
909 | if (atomic_read(&ctx->wait_vsync_event)) { | 940 | if (atomic_read(&ctx->wait_vsync_event)) { |
910 | atomic_set(&ctx->wait_vsync_event, 0); | 941 | atomic_set(&ctx->wait_vsync_event, 0); |
@@ -933,7 +964,8 @@ static int fimd_bind(struct device *dev, struct device *master, void *data) | |||
933 | type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY : | 964 | type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY : |
934 | DRM_PLANE_TYPE_OVERLAY; | 965 | DRM_PLANE_TYPE_OVERLAY; |
935 | ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], | 966 | ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], |
936 | 1 << ctx->pipe, type, zpos); | 967 | 1 << ctx->pipe, type, fimd_formats, |
968 | ARRAY_SIZE(fimd_formats), zpos); | ||
937 | if (ret) | 969 | if (ret) |
938 | return ret; | 970 | return ret; |
939 | } | 971 | } |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 67461b77f040..62b9ea1b07fb 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c | |||
@@ -668,7 +668,7 @@ exynos_drm_gem_prime_import_sg_table(struct drm_device *dev, | |||
668 | exynos_gem_obj = exynos_drm_gem_init(dev, attach->dmabuf->size); | 668 | exynos_gem_obj = exynos_drm_gem_init(dev, attach->dmabuf->size); |
669 | if (IS_ERR(exynos_gem_obj)) { | 669 | if (IS_ERR(exynos_gem_obj)) { |
670 | ret = PTR_ERR(exynos_gem_obj); | 670 | ret = PTR_ERR(exynos_gem_obj); |
671 | goto err; | 671 | return ERR_PTR(ret); |
672 | } | 672 | } |
673 | 673 | ||
674 | exynos_gem_obj->dma_addr = sg_dma_address(sgt->sgl); | 674 | exynos_gem_obj->dma_addr = sg_dma_address(sgt->sgl); |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index d9a68fd83120..865d6eb0c845 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c | |||
@@ -20,12 +20,6 @@ | |||
20 | #include "exynos_drm_gem.h" | 20 | #include "exynos_drm_gem.h" |
21 | #include "exynos_drm_plane.h" | 21 | #include "exynos_drm_plane.h" |
22 | 22 | ||
23 | static const uint32_t formats[] = { | ||
24 | DRM_FORMAT_XRGB8888, | ||
25 | DRM_FORMAT_ARGB8888, | ||
26 | DRM_FORMAT_NV12, | ||
27 | }; | ||
28 | |||
29 | /* | 23 | /* |
30 | * This function is to get X or Y size shown via screen. This needs length and | 24 | * This function is to get X or Y size shown via screen. This needs length and |
31 | * start position of CRTC. | 25 | * start position of CRTC. |
@@ -168,6 +162,8 @@ static void exynos_plane_atomic_update(struct drm_plane *plane, | |||
168 | state->src_x >> 16, state->src_y >> 16, | 162 | state->src_x >> 16, state->src_y >> 16, |
169 | state->src_w >> 16, state->src_h >> 16); | 163 | state->src_w >> 16, state->src_h >> 16); |
170 | 164 | ||
165 | exynos_plane->pending_fb = state->fb; | ||
166 | |||
171 | if (exynos_crtc->ops->update_plane) | 167 | if (exynos_crtc->ops->update_plane) |
172 | exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane); | 168 | exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane); |
173 | } | 169 | } |
@@ -215,13 +211,14 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane, | |||
215 | int exynos_plane_init(struct drm_device *dev, | 211 | int exynos_plane_init(struct drm_device *dev, |
216 | struct exynos_drm_plane *exynos_plane, | 212 | struct exynos_drm_plane *exynos_plane, |
217 | unsigned long possible_crtcs, enum drm_plane_type type, | 213 | unsigned long possible_crtcs, enum drm_plane_type type, |
214 | const uint32_t *formats, unsigned int fcount, | ||
218 | unsigned int zpos) | 215 | unsigned int zpos) |
219 | { | 216 | { |
220 | int err; | 217 | int err; |
221 | 218 | ||
222 | err = drm_universal_plane_init(dev, &exynos_plane->base, possible_crtcs, | 219 | err = drm_universal_plane_init(dev, &exynos_plane->base, possible_crtcs, |
223 | &exynos_plane_funcs, formats, | 220 | &exynos_plane_funcs, formats, fcount, |
224 | ARRAY_SIZE(formats), type); | 221 | type); |
225 | if (err) { | 222 | if (err) { |
226 | DRM_ERROR("failed to initialize plane\n"); | 223 | DRM_ERROR("failed to initialize plane\n"); |
227 | return err; | 224 | return err; |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.h b/drivers/gpu/drm/exynos/exynos_drm_plane.h index 8c88ae983c38..476c9340b591 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.h +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.h | |||
@@ -12,4 +12,5 @@ | |||
12 | int exynos_plane_init(struct drm_device *dev, | 12 | int exynos_plane_init(struct drm_device *dev, |
13 | struct exynos_drm_plane *exynos_plane, | 13 | struct exynos_drm_plane *exynos_plane, |
14 | unsigned long possible_crtcs, enum drm_plane_type type, | 14 | unsigned long possible_crtcs, enum drm_plane_type type, |
15 | const uint32_t *formats, unsigned int fcount, | ||
15 | unsigned int zpos); | 16 | unsigned int zpos); |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 581af35861a6..75718e1bc3dd 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c | |||
@@ -83,6 +83,12 @@ static const char fake_edid_info[] = { | |||
83 | 0x00, 0x00, 0x00, 0x06 | 83 | 0x00, 0x00, 0x00, 0x06 |
84 | }; | 84 | }; |
85 | 85 | ||
86 | static const uint32_t formats[] = { | ||
87 | DRM_FORMAT_XRGB8888, | ||
88 | DRM_FORMAT_ARGB8888, | ||
89 | DRM_FORMAT_NV12, | ||
90 | }; | ||
91 | |||
86 | static int vidi_enable_vblank(struct exynos_drm_crtc *crtc) | 92 | static int vidi_enable_vblank(struct exynos_drm_crtc *crtc) |
87 | { | 93 | { |
88 | struct vidi_context *ctx = crtc->ctx; | 94 | struct vidi_context *ctx = crtc->ctx; |
@@ -179,6 +185,7 @@ static void vidi_fake_vblank_handler(struct work_struct *work) | |||
179 | { | 185 | { |
180 | struct vidi_context *ctx = container_of(work, struct vidi_context, | 186 | struct vidi_context *ctx = container_of(work, struct vidi_context, |
181 | work); | 187 | work); |
188 | int win; | ||
182 | 189 | ||
183 | if (ctx->pipe < 0) | 190 | if (ctx->pipe < 0) |
184 | return; | 191 | return; |
@@ -197,7 +204,14 @@ static void vidi_fake_vblank_handler(struct work_struct *work) | |||
197 | 204 | ||
198 | mutex_unlock(&ctx->lock); | 205 | mutex_unlock(&ctx->lock); |
199 | 206 | ||
200 | exynos_drm_crtc_finish_pageflip(ctx->crtc); | 207 | for (win = 0 ; win < WINDOWS_NR ; win++) { |
208 | struct exynos_drm_plane *plane = &ctx->planes[win]; | ||
209 | |||
210 | if (!plane->pending_fb) | ||
211 | continue; | ||
212 | |||
213 | exynos_drm_crtc_finish_update(ctx->crtc, plane); | ||
214 | } | ||
201 | } | 215 | } |
202 | 216 | ||
203 | static int vidi_show_connection(struct device *dev, | 217 | static int vidi_show_connection(struct device *dev, |
@@ -435,7 +449,8 @@ static int vidi_bind(struct device *dev, struct device *master, void *data) | |||
435 | type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY : | 449 | type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY : |
436 | DRM_PLANE_TYPE_OVERLAY; | 450 | DRM_PLANE_TYPE_OVERLAY; |
437 | ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], | 451 | ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], |
438 | 1 << ctx->pipe, type, zpos); | 452 | 1 << ctx->pipe, type, formats, |
453 | ARRAY_SIZE(formats), zpos); | ||
439 | if (ret) | 454 | if (ret) |
440 | return ret; | 455 | return ret; |
441 | } | 456 | } |
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index e68340c77676..7f81cce966d4 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c | |||
@@ -43,6 +43,7 @@ | |||
43 | 43 | ||
44 | #define MIXER_WIN_NR 3 | 44 | #define MIXER_WIN_NR 3 |
45 | #define MIXER_DEFAULT_WIN 0 | 45 | #define MIXER_DEFAULT_WIN 0 |
46 | #define VP_DEFAULT_WIN 2 | ||
46 | 47 | ||
47 | /* The pixelformats that are natively supported by the mixer. */ | 48 | /* The pixelformats that are natively supported by the mixer. */ |
48 | #define MXR_FORMAT_RGB565 4 | 49 | #define MXR_FORMAT_RGB565 4 |
@@ -74,6 +75,19 @@ enum mixer_flag_bits { | |||
74 | MXR_BIT_VSYNC, | 75 | MXR_BIT_VSYNC, |
75 | }; | 76 | }; |
76 | 77 | ||
78 | static const uint32_t mixer_formats[] = { | ||
79 | DRM_FORMAT_XRGB4444, | ||
80 | DRM_FORMAT_XRGB1555, | ||
81 | DRM_FORMAT_RGB565, | ||
82 | DRM_FORMAT_XRGB8888, | ||
83 | DRM_FORMAT_ARGB8888, | ||
84 | }; | ||
85 | |||
86 | static const uint32_t vp_formats[] = { | ||
87 | DRM_FORMAT_NV12, | ||
88 | DRM_FORMAT_NV21, | ||
89 | }; | ||
90 | |||
77 | struct mixer_context { | 91 | struct mixer_context { |
78 | struct platform_device *pdev; | 92 | struct platform_device *pdev; |
79 | struct device *dev; | 93 | struct device *dev; |
@@ -716,6 +730,7 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) | |||
716 | struct mixer_context *ctx = arg; | 730 | struct mixer_context *ctx = arg; |
717 | struct mixer_resources *res = &ctx->mixer_res; | 731 | struct mixer_resources *res = &ctx->mixer_res; |
718 | u32 val, base, shadow; | 732 | u32 val, base, shadow; |
733 | int win; | ||
719 | 734 | ||
720 | spin_lock(&res->reg_slock); | 735 | spin_lock(&res->reg_slock); |
721 | 736 | ||
@@ -742,7 +757,14 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) | |||
742 | } | 757 | } |
743 | 758 | ||
744 | drm_crtc_handle_vblank(&ctx->crtc->base); | 759 | drm_crtc_handle_vblank(&ctx->crtc->base); |
745 | exynos_drm_crtc_finish_pageflip(ctx->crtc); | 760 | for (win = 0 ; win < MIXER_WIN_NR ; win++) { |
761 | struct exynos_drm_plane *plane = &ctx->planes[win]; | ||
762 | |||
763 | if (!plane->pending_fb) | ||
764 | continue; | ||
765 | |||
766 | exynos_drm_crtc_finish_update(ctx->crtc, plane); | ||
767 | } | ||
746 | 768 | ||
747 | /* set wait vsync event to zero and wake up queue. */ | 769 | /* set wait vsync event to zero and wake up queue. */ |
748 | if (atomic_read(&ctx->wait_vsync_event)) { | 770 | if (atomic_read(&ctx->wait_vsync_event)) { |
@@ -1163,7 +1185,6 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data) | |||
1163 | struct mixer_context *ctx = dev_get_drvdata(dev); | 1185 | struct mixer_context *ctx = dev_get_drvdata(dev); |
1164 | struct drm_device *drm_dev = data; | 1186 | struct drm_device *drm_dev = data; |
1165 | struct exynos_drm_plane *exynos_plane; | 1187 | struct exynos_drm_plane *exynos_plane; |
1166 | enum drm_plane_type type; | ||
1167 | unsigned int zpos; | 1188 | unsigned int zpos; |
1168 | int ret; | 1189 | int ret; |
1169 | 1190 | ||
@@ -1172,10 +1193,23 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data) | |||
1172 | return ret; | 1193 | return ret; |
1173 | 1194 | ||
1174 | for (zpos = 0; zpos < MIXER_WIN_NR; zpos++) { | 1195 | for (zpos = 0; zpos < MIXER_WIN_NR; zpos++) { |
1196 | enum drm_plane_type type; | ||
1197 | const uint32_t *formats; | ||
1198 | unsigned int fcount; | ||
1199 | |||
1175 | type = (zpos == MIXER_DEFAULT_WIN) ? DRM_PLANE_TYPE_PRIMARY : | 1200 | type = (zpos == MIXER_DEFAULT_WIN) ? DRM_PLANE_TYPE_PRIMARY : |
1176 | DRM_PLANE_TYPE_OVERLAY; | 1201 | DRM_PLANE_TYPE_OVERLAY; |
1202 | if (zpos < VP_DEFAULT_WIN) { | ||
1203 | formats = mixer_formats; | ||
1204 | fcount = ARRAY_SIZE(mixer_formats); | ||
1205 | } else { | ||
1206 | formats = vp_formats; | ||
1207 | fcount = ARRAY_SIZE(vp_formats); | ||
1208 | } | ||
1209 | |||
1177 | ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], | 1210 | ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], |
1178 | 1 << ctx->pipe, type, zpos); | 1211 | 1 << ctx->pipe, type, formats, fcount, |
1212 | zpos); | ||
1179 | if (ret) | 1213 | if (ret) |
1180 | return ret; | 1214 | return ret; |
1181 | } | 1215 | } |