Commit 73488331eb9460d14974a1e2c734f77ce8869183

Authored by Andrzej Hajda
Committed by Inki Dae
1 parent a392276d1d

drm/exynos/decon5433: fix vblank event handling

Current implementation of event handling assumes that vblank interrupt is
always called at the right time. It is not true, it can be delayed due to
various reasons. As a result different races can happen. The patch fixes
the issue by using hardware frame counter present in DECON to serialize
vblank and commit completion events.

Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Inki Dae <inki.dae@samsung.com>

Showing 2 changed files with 85 additions and 1 deletions Side-by-side Diff

drivers/gpu/drm/exynos/exynos5433_drm_decon.c
... ... @@ -68,6 +68,8 @@
68 68 unsigned long flags;
69 69 unsigned long out_type;
70 70 int first_win;
  71 + spinlock_t vblank_lock;
  72 + u32 frame_id;
71 73 };
72 74  
73 75 static const uint32_t decon_formats[] = {
... ... @@ -122,6 +124,48 @@
122 124 writel(0, ctx->addr + DECON_VIDINTCON0);
123 125 }
124 126  
  127 +/* return number of starts/ends of frame transmissions since reset */
  128 +static u32 decon_get_frame_count(struct decon_context *ctx, bool end)
  129 +{
  130 + u32 frm, pfrm, status, cnt = 2;
  131 +
  132 + /* To get consistent result repeat read until frame id is stable.
  133 + * Usually the loop will be executed once, in rare cases when the loop
  134 + * is executed at frame change time 2nd pass will be needed.
  135 + */
  136 + frm = readl(ctx->addr + DECON_CRFMID);
  137 + do {
  138 + status = readl(ctx->addr + DECON_VIDCON1);
  139 + pfrm = frm;
  140 + frm = readl(ctx->addr + DECON_CRFMID);
  141 + } while (frm != pfrm && --cnt);
  142 +
  143 + /* CRFMID is incremented on BPORCH in case of I80 and on VSYNC in case
  144 + * of RGB, it should be taken into account.
  145 + */
  146 + if (!frm)
  147 + return 0;
  148 +
  149 + switch (status & (VIDCON1_VSTATUS_MASK | VIDCON1_I80_ACTIVE)) {
  150 + case VIDCON1_VSTATUS_VS:
  151 + if (!(ctx->out_type & IFTYPE_I80))
  152 + --frm;
  153 + break;
  154 + case VIDCON1_VSTATUS_BP:
  155 + --frm;
  156 + break;
  157 + case VIDCON1_I80_ACTIVE:
  158 + case VIDCON1_VSTATUS_AC:
  159 + if (end)
  160 + --frm;
  161 + break;
  162 + default:
  163 + break;
  164 + }
  165 +
  166 + return frm;
  167 +}
  168 +
125 169 static void decon_setup_trigger(struct decon_context *ctx)
126 170 {
127 171 if (!(ctx->out_type & (IFTYPE_I80 | I80_HW_TRG)))
128 172  
... ... @@ -365,11 +409,14 @@
365 409 static void decon_atomic_flush(struct exynos_drm_crtc *crtc)
366 410 {
367 411 struct decon_context *ctx = crtc->ctx;
  412 + unsigned long flags;
368 413 int i;
369 414  
370 415 if (test_bit(BIT_SUSPENDED, &ctx->flags))
371 416 return;
372 417  
  418 + spin_lock_irqsave(&ctx->vblank_lock, flags);
  419 +
373 420 for (i = ctx->first_win; i < WINDOWS_NR; i++)
374 421 decon_shadow_protect_win(ctx, i, false);
375 422  
376 423  
377 424  
... ... @@ -378,12 +425,18 @@
378 425  
379 426 if (ctx->out_type & IFTYPE_I80)
380 427 set_bit(BIT_WIN_UPDATED, &ctx->flags);
  428 +
  429 + ctx->frame_id = decon_get_frame_count(ctx, true);
  430 +
381 431 exynos_crtc_handle_event(crtc);
  432 +
  433 + spin_unlock_irqrestore(&ctx->vblank_lock, flags);
382 434 }
383 435  
384 436 static void decon_swreset(struct decon_context *ctx)
385 437 {
386 438 unsigned int tries;
  439 + unsigned long flags;
387 440  
388 441 writel(0, ctx->addr + DECON_VIDCON0);
389 442 for (tries = 2000; tries; --tries) {
... ... @@ -401,6 +454,10 @@
401 454  
402 455 WARN(tries == 0, "failed to software reset DECON\n");
403 456  
  457 + spin_lock_irqsave(&ctx->vblank_lock, flags);
  458 + ctx->frame_id = 0;
  459 + spin_unlock_irqrestore(&ctx->vblank_lock, flags);
  460 +
404 461 if (!(ctx->out_type & IFTYPE_HDMI))
405 462 return;
406 463  
... ... @@ -579,6 +636,24 @@
579 636 .unbind = decon_unbind,
580 637 };
581 638  
  639 +static void decon_handle_vblank(struct decon_context *ctx)
  640 +{
  641 + u32 frm;
  642 +
  643 + spin_lock(&ctx->vblank_lock);
  644 +
  645 + frm = decon_get_frame_count(ctx, true);
  646 +
  647 + if (frm != ctx->frame_id) {
  648 + /* handle only if incremented, take care of wrap-around */
  649 + if ((s32)(frm - ctx->frame_id) > 0)
  650 + drm_crtc_handle_vblank(&ctx->crtc->base);
  651 + ctx->frame_id = frm;
  652 + }
  653 +
  654 + spin_unlock(&ctx->vblank_lock);
  655 +}
  656 +
582 657 static irqreturn_t decon_irq_handler(int irq, void *dev_id)
583 658 {
584 659 struct decon_context *ctx = dev_id;
... ... @@ -599,7 +674,7 @@
599 674 (VIDOUT_INTERLACE_EN_F | VIDOUT_INTERLACE_FIELD_F))
600 675 return IRQ_HANDLED;
601 676 }
602   - drm_crtc_handle_vblank(&ctx->crtc->base);
  677 + decon_handle_vblank(ctx);
603 678 }
604 679  
605 680 out:
... ... @@ -672,6 +747,7 @@
672 747 __set_bit(BIT_SUSPENDED, &ctx->flags);
673 748 ctx->dev = dev;
674 749 ctx->out_type = (unsigned long)of_device_get_match_data(dev);
  750 + spin_lock_init(&ctx->vblank_lock);
675 751  
676 752 if (ctx->out_type & IFTYPE_HDMI) {
677 753 ctx->first_win = 1;
include/video/exynos5433_decon.h
... ... @@ -46,6 +46,7 @@
46 46 #define DECON_FRAMEFIFO_STATUS 0x0524
47 47 #define DECON_CMU 0x1404
48 48 #define DECON_UPDATE 0x1410
  49 +#define DECON_CRFMID 0x1414
49 50 #define DECON_UPDATE_SCHEME 0x1438
50 51 #define DECON_VIDCON1 0x2000
51 52 #define DECON_VIDCON2 0x2004
... ... @@ -142,6 +143,13 @@
142 143 #define STANDALONE_UPDATE_F (1 << 0)
143 144  
144 145 /* DECON_VIDCON1 */
  146 +#define VIDCON1_LINECNT_MASK (0x0fff << 16)
  147 +#define VIDCON1_I80_ACTIVE (1 << 15)
  148 +#define VIDCON1_VSTATUS_MASK (0x3 << 13)
  149 +#define VIDCON1_VSTATUS_VS (0 << 13)
  150 +#define VIDCON1_VSTATUS_BP (1 << 13)
  151 +#define VIDCON1_VSTATUS_AC (2 << 13)
  152 +#define VIDCON1_VSTATUS_FP (3 << 13)
145 153 #define VIDCON1_VCLK_MASK (0x3 << 9)
146 154 #define VIDCON1_VCLK_RUN_VDEN_DISABLE (0x3 << 9)
147 155 #define VIDCON1_VCLK_HOLD (0x0 << 9)