Skip to content

Commit 8d7849d

Browse files
vsyrjaladanvet
authored andcommitted
drm/i915: Make sprite updates atomic
Add a mechanism by which we can evade the leading edge of vblank. This guarantees that no two sprite register writes will straddle on either side of the vblank start, and that means all the writes will be latched together in one atomic operation. We do the vblank evade by checking the scanline counter, and if it's too close to the start of vblank (too close has been hardcoded to 100usec for now), we will wait for the vblank start to pass. In order to eliminate random delayes from the rest of the system, we operate with interrupts disabled, except when waiting for the vblank obviously. Note that we now go digging through pipe_to_crtc_mapping[] in the vblank interrupt handler, which is a bit dangerous since we set up interrupts before the crtcs. However in this case since it's the vblank interrupt, we don't actually unmask it until some piece of code requests it. v2: preempt_check_resched() calls after local_irq_enable() (Jesse) Hook up the vblank irq stuff on BDW as well v3: Pass intel_crtc instead of drm_crtc (Daniel) Warn if crtc.mutex isn't locked (Daniel) Add an explicit compiler barrier and document the barriers (Daniel) Note the irq vs. modeset setup madness in the commit message (Daniel) v4: Use prepare_to_wait() & co. directly and eliminate vbl_received v5: Refactor intel_pipe_handle_vblank() vs. drm_handle_vblank() (Chris) Check for min/max scanline <= 0 (Chris) Don't call intel_pipe_update_end() if start failed totally (Chris) Check that the vblank counters match on both sides of the critical section (Chris) v6: Fix atomic update for interlaced modes v7: Reorder code for better readability (Chris) v8: Drop preempt_check_resched(). It's not available to modules anymore and isn't even needed unless we ourselves cause a wakeup needing reschedule while interrupts are off Reviewed-by: Jesse Barnes <[email protected]> Reviewed-by: Sourab Gupta <[email protected]> Reviewed-by: Akash Goel <[email protected]> Signed-off-by: Ville Syrjälä <[email protected]> Signed-off-by: Daniel Vetter <[email protected]>
1 parent d9ceb95 commit 8d7849d

4 files changed

Lines changed: 154 additions & 6 deletions

File tree

drivers/gpu/drm/i915/i915_irq.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,19 @@ static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir)
15561556
}
15571557
}
15581558

1559+
static bool intel_pipe_handle_vblank(struct drm_device *dev, enum pipe pipe)
1560+
{
1561+
struct intel_crtc *crtc;
1562+
1563+
if (!drm_handle_vblank(dev, pipe))
1564+
return false;
1565+
1566+
crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe));
1567+
wake_up(&crtc->vbl_wait);
1568+
1569+
return true;
1570+
}
1571+
15591572
static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
15601573
{
15611574
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1607,7 +1620,7 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
16071620

16081621
for_each_pipe(pipe) {
16091622
if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS)
1610-
drm_handle_vblank(dev, pipe);
1623+
intel_pipe_handle_vblank(dev, pipe);
16111624

16121625
if (pipe_stats[pipe] & PLANE_FLIP_DONE_INT_STATUS_VLV) {
16131626
intel_prepare_page_flip(dev, pipe);
@@ -1850,7 +1863,7 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
18501863

18511864
for_each_pipe(pipe) {
18521865
if (de_iir & DE_PIPE_VBLANK(pipe))
1853-
drm_handle_vblank(dev, pipe);
1866+
intel_pipe_handle_vblank(dev, pipe);
18541867

18551868
if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe))
18561869
if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, false))
@@ -1900,7 +1913,7 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
19001913

19011914
for_each_pipe(pipe) {
19021915
if (de_iir & (DE_PIPE_VBLANK_IVB(pipe)))
1903-
drm_handle_vblank(dev, pipe);
1916+
intel_pipe_handle_vblank(dev, pipe);
19041917

19051918
/* plane/pipes map 1:1 on ilk+ */
19061919
if (de_iir & DE_PLANE_FLIP_DONE_IVB(pipe)) {
@@ -2043,7 +2056,7 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
20432056

20442057
pipe_iir = I915_READ(GEN8_DE_PIPE_IIR(pipe));
20452058
if (pipe_iir & GEN8_PIPE_VBLANK)
2046-
drm_handle_vblank(dev, pipe);
2059+
intel_pipe_handle_vblank(dev, pipe);
20472060

20482061
if (pipe_iir & GEN8_PIPE_PRIMARY_FLIP_DONE) {
20492062
intel_prepare_page_flip(dev, pipe);
@@ -3390,7 +3403,7 @@ static bool i8xx_handle_vblank(struct drm_device *dev,
33903403
struct drm_i915_private *dev_priv = dev->dev_private;
33913404
u16 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane);
33923405

3393-
if (!drm_handle_vblank(dev, pipe))
3406+
if (!intel_pipe_handle_vblank(dev, pipe))
33943407
return false;
33953408

33963409
if ((iir & flip_pending) == 0)
@@ -3575,7 +3588,7 @@ static bool i915_handle_vblank(struct drm_device *dev,
35753588
struct drm_i915_private *dev_priv = dev->dev_private;
35763589
u32 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane);
35773590

3578-
if (!drm_handle_vblank(dev, pipe))
3591+
if (!intel_pipe_handle_vblank(dev, pipe))
35793592
return false;
35803593

35813594
if ((iir & flip_pending) == 0)

drivers/gpu/drm/i915/intel_display.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10566,6 +10566,8 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
1056610566
intel_crtc->plane = !pipe;
1056710567
}
1056810568

10569+
init_waitqueue_head(&intel_crtc->vbl_wait);
10570+
1056910571
BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
1057010572
dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL);
1057110573
dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base;

drivers/gpu/drm/i915/intel_drv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,8 @@ struct intel_crtc {
401401
/* watermarks currently being used */
402402
struct intel_pipe_wm active;
403403
} wm;
404+
405+
wait_queue_head_t vbl_wait;
404406
};
405407

406408
struct intel_plane_wm_parameters {

drivers/gpu/drm/i915/intel_sprite.c

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,89 @@
3737
#include <drm/i915_drm.h>
3838
#include "i915_drv.h"
3939

40+
static int usecs_to_scanlines(const struct drm_display_mode *mode, int usecs)
41+
{
42+
/* paranoia */
43+
if (!mode->crtc_htotal)
44+
return 1;
45+
46+
return DIV_ROUND_UP(usecs * mode->crtc_clock, 1000 * mode->crtc_htotal);
47+
}
48+
49+
static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl_count)
50+
{
51+
struct drm_device *dev = crtc->base.dev;
52+
const struct drm_display_mode *mode = &crtc->config.adjusted_mode;
53+
enum pipe pipe = crtc->pipe;
54+
long timeout = msecs_to_jiffies_timeout(1);
55+
int scanline, min, max, vblank_start;
56+
DEFINE_WAIT(wait);
57+
58+
WARN_ON(!mutex_is_locked(&crtc->base.mutex));
59+
60+
vblank_start = mode->crtc_vblank_start;
61+
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
62+
vblank_start = DIV_ROUND_UP(vblank_start, 2);
63+
64+
/* FIXME needs to be calibrated sensibly */
65+
min = vblank_start - usecs_to_scanlines(mode, 100);
66+
max = vblank_start - 1;
67+
68+
if (min <= 0 || max <= 0)
69+
return false;
70+
71+
if (WARN_ON(drm_vblank_get(dev, pipe)))
72+
return false;
73+
74+
local_irq_disable();
75+
76+
for (;;) {
77+
/*
78+
* prepare_to_wait() has a memory barrier, which guarantees
79+
* other CPUs can see the task state update by the time we
80+
* read the scanline.
81+
*/
82+
prepare_to_wait(&crtc->vbl_wait, &wait, TASK_UNINTERRUPTIBLE);
83+
84+
scanline = intel_get_crtc_scanline(crtc);
85+
if (scanline < min || scanline > max)
86+
break;
87+
88+
if (timeout <= 0) {
89+
DRM_ERROR("Potential atomic update failure on pipe %c\n",
90+
pipe_name(crtc->pipe));
91+
break;
92+
}
93+
94+
local_irq_enable();
95+
96+
timeout = schedule_timeout(timeout);
97+
98+
local_irq_disable();
99+
}
100+
101+
finish_wait(&crtc->vbl_wait, &wait);
102+
103+
drm_vblank_put(dev, pipe);
104+
105+
*start_vbl_count = dev->driver->get_vblank_counter(dev, pipe);
106+
107+
return true;
108+
}
109+
110+
static void intel_pipe_update_end(struct intel_crtc *crtc, u32 start_vbl_count)
111+
{
112+
struct drm_device *dev = crtc->base.dev;
113+
enum pipe pipe = crtc->pipe;
114+
u32 end_vbl_count = dev->driver->get_vblank_counter(dev, pipe);
115+
116+
local_irq_enable();
117+
118+
if (start_vbl_count != end_vbl_count)
119+
DRM_ERROR("Atomic update failure on pipe %c (start=%u end=%u)\n",
120+
pipe_name(pipe), start_vbl_count, end_vbl_count);
121+
}
122+
40123
static void
41124
vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
42125
struct drm_framebuffer *fb,
@@ -48,11 +131,14 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
48131
struct drm_device *dev = dplane->dev;
49132
struct drm_i915_private *dev_priv = dev->dev_private;
50133
struct intel_plane *intel_plane = to_intel_plane(dplane);
134+
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
51135
int pipe = intel_plane->pipe;
52136
int plane = intel_plane->plane;
53137
u32 sprctl;
54138
unsigned long sprsurf_offset, linear_offset;
55139
int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
140+
u32 start_vbl_count;
141+
bool atomic_update;
56142

57143
sprctl = I915_READ(SPCNTR(pipe, plane));
58144

@@ -131,6 +217,8 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
131217
fb->pitches[0]);
132218
linear_offset -= sprsurf_offset;
133219

220+
atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
221+
134222
I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]);
135223
I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x);
136224

@@ -144,6 +232,9 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
144232
I915_WRITE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) +
145233
sprsurf_offset);
146234
POSTING_READ(SPSURF(pipe, plane));
235+
236+
if (atomic_update)
237+
intel_pipe_update_end(intel_crtc, start_vbl_count);
147238
}
148239

149240
static void
@@ -152,15 +243,23 @@ vlv_disable_plane(struct drm_plane *dplane, struct drm_crtc *crtc)
152243
struct drm_device *dev = dplane->dev;
153244
struct drm_i915_private *dev_priv = dev->dev_private;
154245
struct intel_plane *intel_plane = to_intel_plane(dplane);
246+
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
155247
int pipe = intel_plane->pipe;
156248
int plane = intel_plane->plane;
249+
u32 start_vbl_count;
250+
bool atomic_update;
251+
252+
atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
157253

158254
I915_WRITE(SPCNTR(pipe, plane), I915_READ(SPCNTR(pipe, plane)) &
159255
~SP_ENABLE);
160256
/* Activate double buffered register update */
161257
I915_WRITE(SPSURF(pipe, plane), 0);
162258
POSTING_READ(SPSURF(pipe, plane));
163259

260+
if (atomic_update)
261+
intel_pipe_update_end(intel_crtc, start_vbl_count);
262+
164263
intel_update_sprite_watermarks(dplane, crtc, 0, 0, false, false);
165264
}
166265

@@ -226,10 +325,13 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
226325
struct drm_device *dev = plane->dev;
227326
struct drm_i915_private *dev_priv = dev->dev_private;
228327
struct intel_plane *intel_plane = to_intel_plane(plane);
328+
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
229329
int pipe = intel_plane->pipe;
230330
u32 sprctl, sprscale = 0;
231331
unsigned long sprsurf_offset, linear_offset;
232332
int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
333+
u32 start_vbl_count;
334+
bool atomic_update;
233335

234336
sprctl = I915_READ(SPRCTL(pipe));
235337

@@ -299,6 +401,8 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
299401
pixel_size, fb->pitches[0]);
300402
linear_offset -= sprsurf_offset;
301403

404+
atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
405+
302406
I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]);
303407
I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x);
304408

@@ -318,6 +422,9 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
318422
I915_WRITE(SPRSURF(pipe),
319423
i915_gem_obj_ggtt_offset(obj) + sprsurf_offset);
320424
POSTING_READ(SPRSURF(pipe));
425+
426+
if (atomic_update)
427+
intel_pipe_update_end(intel_crtc, start_vbl_count);
321428
}
322429

323430
static void
@@ -326,7 +433,12 @@ ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
326433
struct drm_device *dev = plane->dev;
327434
struct drm_i915_private *dev_priv = dev->dev_private;
328435
struct intel_plane *intel_plane = to_intel_plane(plane);
436+
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
329437
int pipe = intel_plane->pipe;
438+
u32 start_vbl_count;
439+
bool atomic_update;
440+
441+
atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
330442

331443
I915_WRITE(SPRCTL(pipe), I915_READ(SPRCTL(pipe)) & ~SPRITE_ENABLE);
332444
/* Can't leave the scaler enabled... */
@@ -336,6 +448,9 @@ ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
336448
I915_WRITE(SPRSURF(pipe), 0);
337449
POSTING_READ(SPRSURF(pipe));
338450

451+
if (atomic_update)
452+
intel_pipe_update_end(intel_crtc, start_vbl_count);
453+
339454
/*
340455
* Avoid underruns when disabling the sprite.
341456
* FIXME remove once watermark updates are done properly.
@@ -410,10 +525,13 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
410525
struct drm_device *dev = plane->dev;
411526
struct drm_i915_private *dev_priv = dev->dev_private;
412527
struct intel_plane *intel_plane = to_intel_plane(plane);
528+
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
413529
int pipe = intel_plane->pipe;
414530
unsigned long dvssurf_offset, linear_offset;
415531
u32 dvscntr, dvsscale;
416532
int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
533+
u32 start_vbl_count;
534+
bool atomic_update;
417535

418536
dvscntr = I915_READ(DVSCNTR(pipe));
419537

@@ -478,6 +596,8 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
478596
pixel_size, fb->pitches[0]);
479597
linear_offset -= dvssurf_offset;
480598

599+
atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
600+
481601
I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]);
482602
I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x);
483603

@@ -492,6 +612,9 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
492612
I915_WRITE(DVSSURF(pipe),
493613
i915_gem_obj_ggtt_offset(obj) + dvssurf_offset);
494614
POSTING_READ(DVSSURF(pipe));
615+
616+
if (atomic_update)
617+
intel_pipe_update_end(intel_crtc, start_vbl_count);
495618
}
496619

497620
static void
@@ -500,7 +623,12 @@ ilk_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
500623
struct drm_device *dev = plane->dev;
501624
struct drm_i915_private *dev_priv = dev->dev_private;
502625
struct intel_plane *intel_plane = to_intel_plane(plane);
626+
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
503627
int pipe = intel_plane->pipe;
628+
u32 start_vbl_count;
629+
bool atomic_update;
630+
631+
atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
504632

505633
I915_WRITE(DVSCNTR(pipe), I915_READ(DVSCNTR(pipe)) & ~DVS_ENABLE);
506634
/* Disable the scaler */
@@ -509,6 +637,9 @@ ilk_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
509637
I915_WRITE(DVSSURF(pipe), 0);
510638
POSTING_READ(DVSSURF(pipe));
511639

640+
if (atomic_update)
641+
intel_pipe_update_end(intel_crtc, start_vbl_count);
642+
512643
/*
513644
* Avoid underruns when disabling the sprite.
514645
* FIXME remove once watermark updates are done properly.

0 commit comments

Comments
 (0)