Commit | Line | Data |
---|---|---|
0d6aa60b | 1 | /* i915_irq.c -- IRQ support for the I915 -*- linux-c -*- |
1da177e4 | 2 | */ |
0d6aa60b | 3 | /* |
1da177e4 LT |
4 | * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. |
5 | * All Rights Reserved. | |
bc54fd1a DA |
6 | * |
7 | * Permission is hereby granted, free of charge, to any person obtaining a | |
8 | * copy of this software and associated documentation files (the | |
9 | * "Software"), to deal in the Software without restriction, including | |
10 | * without limitation the rights to use, copy, modify, merge, publish, | |
11 | * distribute, sub license, and/or sell copies of the Software, and to | |
12 | * permit persons to whom the Software is furnished to do so, subject to | |
13 | * the following conditions: | |
14 | * | |
15 | * The above copyright notice and this permission notice (including the | |
16 | * next paragraph) shall be included in all copies or substantial portions | |
17 | * of the Software. | |
18 | * | |
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
20 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. | |
22 | * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR | |
23 | * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
24 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
25 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
26 | * | |
0d6aa60b | 27 | */ |
1da177e4 LT |
28 | |
29 | #include "drmP.h" | |
30 | #include "drm.h" | |
31 | #include "i915_drm.h" | |
32 | #include "i915_drv.h" | |
33 | ||
1da177e4 | 34 | #define MAX_NOPID ((u32)~0) |
1da177e4 | 35 | |
ed4cb414 EA |
36 | /** These are the interrupts used by the driver */ |
37 | #define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT | \ | |
8ee1c3db | 38 | I915_ASLE_INTERRUPT | \ |
0a3e67a4 | 39 | I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ |
8ee1c3db | 40 | I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) |
ed4cb414 | 41 | |
8ee1c3db | 42 | void |
ed4cb414 EA |
43 | i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask) |
44 | { | |
45 | if ((dev_priv->irq_mask_reg & mask) != 0) { | |
46 | dev_priv->irq_mask_reg &= ~mask; | |
47 | I915_WRITE(IMR, dev_priv->irq_mask_reg); | |
48 | (void) I915_READ(IMR); | |
49 | } | |
50 | } | |
51 | ||
52 | static inline void | |
53 | i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask) | |
54 | { | |
55 | if ((dev_priv->irq_mask_reg & mask) != mask) { | |
56 | dev_priv->irq_mask_reg |= mask; | |
57 | I915_WRITE(IMR, dev_priv->irq_mask_reg); | |
58 | (void) I915_READ(IMR); | |
59 | } | |
60 | } | |
61 | ||
0a3e67a4 JB |
62 | /** |
63 | * i915_pipe_enabled - check if a pipe is enabled | |
64 | * @dev: DRM device | |
65 | * @pipe: pipe to check | |
66 | * | |
67 | * Reading certain registers when the pipe is disabled can hang the chip. | |
68 | * Use this routine to make sure the PLL is running and the pipe is active | |
69 | * before reading such registers if unsure. | |
70 | */ | |
71 | static int | |
72 | i915_pipe_enabled(struct drm_device *dev, int pipe) | |
73 | { | |
74 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
75 | unsigned long pipeconf = pipe ? PIPEBCONF : PIPEACONF; | |
76 | ||
77 | if (I915_READ(pipeconf) & PIPEACONF_ENABLE) | |
78 | return 1; | |
79 | ||
80 | return 0; | |
81 | } | |
82 | ||
a6b54f3f MCA |
83 | /** |
84 | * Emit blits for scheduled buffer swaps. | |
85 | * | |
86 | * This function will be called with the HW lock held. | |
9e44af79 KP |
87 | * Because this function must grab the ring mutex (dev->struct_mutex), |
88 | * it can no longer run at soft irq time. We'll fix this when we do | |
89 | * the DRI2 swap buffer work. | |
a6b54f3f | 90 | */ |
84b1fd10 | 91 | static void i915_vblank_tasklet(struct drm_device *dev) |
a6b54f3f MCA |
92 | { |
93 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
af6061af | 94 | unsigned long irqflags; |
3188a24c | 95 | struct list_head *list, *tmp, hits, *hit; |
af6061af | 96 | int nhits, nrects, slice[2], upper[2], lower[2], i; |
0a3e67a4 | 97 | unsigned counter[2]; |
c60ce623 | 98 | struct drm_drawable_info *drw; |
3188a24c | 99 | drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; |
af6061af | 100 | u32 cpp = dev_priv->cpp; |
3188a24c MCA |
101 | u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD | |
102 | XY_SRC_COPY_BLT_WRITE_ALPHA | | |
103 | XY_SRC_COPY_BLT_WRITE_RGB) | |
104 | : XY_SRC_COPY_BLT_CMD; | |
7b832b56 KP |
105 | u32 src_pitch = sarea_priv->pitch * cpp; |
106 | u32 dst_pitch = sarea_priv->pitch * cpp; | |
107 | u32 ropcpp = (0xcc << 16) | ((cpp - 1) << 24); | |
3188a24c | 108 | RING_LOCALS; |
a6b54f3f | 109 | |
9e44af79 KP |
110 | mutex_lock(&dev->struct_mutex); |
111 | ||
3d25802e | 112 | if (IS_I965G(dev) && sarea_priv->front_tiled) { |
7b832b56 KP |
113 | cmd |= XY_SRC_COPY_BLT_DST_TILED; |
114 | dst_pitch >>= 2; | |
115 | } | |
3d25802e | 116 | if (IS_I965G(dev) && sarea_priv->back_tiled) { |
7b832b56 KP |
117 | cmd |= XY_SRC_COPY_BLT_SRC_TILED; |
118 | src_pitch >>= 2; | |
119 | } | |
120 | ||
42f52ef8 KP |
121 | counter[0] = drm_vblank_count(dev, 0); |
122 | counter[1] = drm_vblank_count(dev, 1); | |
0a3e67a4 | 123 | |
a6b54f3f MCA |
124 | DRM_DEBUG("\n"); |
125 | ||
3188a24c MCA |
126 | INIT_LIST_HEAD(&hits); |
127 | ||
128 | nhits = nrects = 0; | |
129 | ||
af6061af | 130 | spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); |
a6b54f3f | 131 | |
3188a24c | 132 | /* Find buffer swaps scheduled for this vertical blank */ |
a6b54f3f MCA |
133 | list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { |
134 | drm_i915_vbl_swap_t *vbl_swap = | |
135 | list_entry(list, drm_i915_vbl_swap_t, head); | |
9e44af79 | 136 | int pipe = vbl_swap->pipe; |
a6b54f3f | 137 | |
0a3e67a4 | 138 | if ((counter[pipe] - vbl_swap->sequence) > (1<<23)) |
3188a24c MCA |
139 | continue; |
140 | ||
141 | list_del(list); | |
142 | dev_priv->swaps_pending--; | |
0a3e67a4 | 143 | drm_vblank_put(dev, pipe); |
3188a24c MCA |
144 | |
145 | spin_unlock(&dev_priv->swaps_lock); | |
146 | spin_lock(&dev->drw_lock); | |
a6b54f3f | 147 | |
3188a24c | 148 | drw = drm_get_drawable_info(dev, vbl_swap->drw_id); |
a6b54f3f | 149 | |
3188a24c MCA |
150 | list_for_each(hit, &hits) { |
151 | drm_i915_vbl_swap_t *swap_cmp = | |
152 | list_entry(hit, drm_i915_vbl_swap_t, head); | |
c60ce623 | 153 | struct drm_drawable_info *drw_cmp = |
3188a24c | 154 | drm_get_drawable_info(dev, swap_cmp->drw_id); |
a6b54f3f | 155 | |
9e44af79 KP |
156 | /* Make sure both drawables are still |
157 | * around and have some rectangles before | |
158 | * we look inside to order them for the | |
159 | * blts below. | |
160 | */ | |
161 | if (drw_cmp && drw_cmp->num_rects > 0 && | |
162 | drw && drw->num_rects > 0 && | |
3188a24c MCA |
163 | drw_cmp->rects[0].y1 > drw->rects[0].y1) { |
164 | list_add_tail(list, hit); | |
165 | break; | |
a6b54f3f | 166 | } |
3188a24c | 167 | } |
a6b54f3f | 168 | |
3188a24c | 169 | spin_unlock(&dev->drw_lock); |
a6b54f3f | 170 | |
3188a24c MCA |
171 | /* List of hits was empty, or we reached the end of it */ |
172 | if (hit == &hits) | |
173 | list_add_tail(list, hits.prev); | |
a6b54f3f | 174 | |
3188a24c | 175 | nhits++; |
a6b54f3f | 176 | |
3188a24c MCA |
177 | spin_lock(&dev_priv->swaps_lock); |
178 | } | |
179 | ||
af6061af DA |
180 | if (nhits == 0) { |
181 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | |
9e44af79 | 182 | mutex_unlock(&dev->struct_mutex); |
ac741ab7 | 183 | return; |
af6061af DA |
184 | } |
185 | ||
186 | spin_unlock(&dev_priv->swaps_lock); | |
3188a24c | 187 | |
ac741ab7 | 188 | i915_kernel_lost_context(dev); |
3188a24c | 189 | |
af6061af DA |
190 | if (IS_I965G(dev)) { |
191 | BEGIN_LP_RING(4); | |
192 | ||
193 | OUT_RING(GFX_OP_DRAWRECT_INFO_I965); | |
194 | OUT_RING(0); | |
195 | OUT_RING(((sarea_priv->width - 1) & 0xffff) | ((sarea_priv->height - 1) << 16)); | |
196 | OUT_RING(0); | |
197 | ADVANCE_LP_RING(); | |
198 | } else { | |
199 | BEGIN_LP_RING(6); | |
ac741ab7 | 200 | |
af6061af DA |
201 | OUT_RING(GFX_OP_DRAWRECT_INFO); |
202 | OUT_RING(0); | |
203 | OUT_RING(0); | |
204 | OUT_RING(sarea_priv->width | sarea_priv->height << 16); | |
205 | OUT_RING(sarea_priv->width | sarea_priv->height << 16); | |
206 | OUT_RING(0); | |
207 | ||
208 | ADVANCE_LP_RING(); | |
209 | } | |
210 | ||
211 | sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT; | |
212 | ||
213 | upper[0] = upper[1] = 0; | |
214 | slice[0] = max(sarea_priv->pipeA_h / nhits, 1); | |
215 | slice[1] = max(sarea_priv->pipeB_h / nhits, 1); | |
216 | lower[0] = sarea_priv->pipeA_y + slice[0]; | |
217 | lower[1] = sarea_priv->pipeB_y + slice[0]; | |
3188a24c MCA |
218 | |
219 | spin_lock(&dev->drw_lock); | |
220 | ||
221 | /* Emit blits for buffer swaps, partitioning both outputs into as many | |
222 | * slices as there are buffer swaps scheduled in order to avoid tearing | |
223 | * (based on the assumption that a single buffer swap would always | |
224 | * complete before scanout starts). | |
225 | */ | |
226 | for (i = 0; i++ < nhits; | |
227 | upper[0] = lower[0], lower[0] += slice[0], | |
228 | upper[1] = lower[1], lower[1] += slice[1]) { | |
229 | if (i == nhits) | |
230 | lower[0] = lower[1] = sarea_priv->height; | |
231 | ||
232 | list_for_each(hit, &hits) { | |
233 | drm_i915_vbl_swap_t *swap_hit = | |
234 | list_entry(hit, drm_i915_vbl_swap_t, head); | |
c60ce623 | 235 | struct drm_clip_rect *rect; |
9e44af79 | 236 | int num_rects, pipe; |
3188a24c MCA |
237 | unsigned short top, bottom; |
238 | ||
239 | drw = drm_get_drawable_info(dev, swap_hit->drw_id); | |
240 | ||
9e44af79 KP |
241 | /* The drawable may have been destroyed since |
242 | * the vblank swap was queued | |
243 | */ | |
3188a24c MCA |
244 | if (!drw) |
245 | continue; | |
246 | ||
247 | rect = drw->rects; | |
9e44af79 KP |
248 | pipe = swap_hit->pipe; |
249 | top = upper[pipe]; | |
250 | bottom = lower[pipe]; | |
3188a24c MCA |
251 | |
252 | for (num_rects = drw->num_rects; num_rects--; rect++) { | |
253 | int y1 = max(rect->y1, top); | |
254 | int y2 = min(rect->y2, bottom); | |
255 | ||
256 | if (y1 >= y2) | |
257 | continue; | |
258 | ||
259 | BEGIN_LP_RING(8); | |
260 | ||
261 | OUT_RING(cmd); | |
7b832b56 | 262 | OUT_RING(ropcpp | dst_pitch); |
3188a24c MCA |
263 | OUT_RING((y1 << 16) | rect->x1); |
264 | OUT_RING((y2 << 16) | rect->x2); | |
af6061af | 265 | OUT_RING(sarea_priv->front_offset); |
3188a24c | 266 | OUT_RING((y1 << 16) | rect->x1); |
7b832b56 | 267 | OUT_RING(src_pitch); |
af6061af | 268 | OUT_RING(sarea_priv->back_offset); |
3188a24c MCA |
269 | |
270 | ADVANCE_LP_RING(); | |
271 | } | |
a6b54f3f MCA |
272 | } |
273 | } | |
274 | ||
af6061af | 275 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); |
9e44af79 | 276 | mutex_unlock(&dev->struct_mutex); |
3188a24c MCA |
277 | |
278 | list_for_each_safe(hit, tmp, &hits) { | |
279 | drm_i915_vbl_swap_t *swap_hit = | |
280 | list_entry(hit, drm_i915_vbl_swap_t, head); | |
281 | ||
282 | list_del(hit); | |
283 | ||
284 | drm_free(swap_hit, sizeof(*swap_hit), DRM_MEM_DRIVER); | |
285 | } | |
a6b54f3f MCA |
286 | } |
287 | ||
42f52ef8 KP |
288 | /* Called from drm generic code, passed a 'crtc', which |
289 | * we use as a pipe index | |
290 | */ | |
291 | u32 i915_get_vblank_counter(struct drm_device *dev, int pipe) | |
0a3e67a4 JB |
292 | { |
293 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
294 | unsigned long high_frame; | |
295 | unsigned long low_frame; | |
296 | u32 high1, high2, low, count; | |
0a3e67a4 | 297 | |
0a3e67a4 JB |
298 | high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH; |
299 | low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; | |
300 | ||
301 | if (!i915_pipe_enabled(dev, pipe)) { | |
302 | DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe); | |
303 | return 0; | |
304 | } | |
305 | ||
306 | /* | |
307 | * High & low register fields aren't synchronized, so make sure | |
308 | * we get a low value that's stable across two reads of the high | |
309 | * register. | |
310 | */ | |
311 | do { | |
312 | high1 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> | |
313 | PIPE_FRAME_HIGH_SHIFT); | |
314 | low = ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >> | |
315 | PIPE_FRAME_LOW_SHIFT); | |
316 | high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> | |
317 | PIPE_FRAME_HIGH_SHIFT); | |
318 | } while (high1 != high2); | |
319 | ||
320 | count = (high1 << 8) | low; | |
321 | ||
322 | return count; | |
323 | } | |
324 | ||
546b0974 | 325 | void |
9e44af79 | 326 | i915_vblank_work_handler(struct work_struct *work) |
546b0974 | 327 | { |
9e44af79 KP |
328 | drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, |
329 | vblank_work); | |
330 | struct drm_device *dev = dev_priv->dev; | |
331 | unsigned long irqflags; | |
332 | ||
333 | if (dev->lock.hw_lock == NULL) { | |
334 | i915_vblank_tasklet(dev); | |
335 | return; | |
336 | } | |
546b0974 | 337 | |
9e44af79 KP |
338 | spin_lock_irqsave(&dev->tasklet_lock, irqflags); |
339 | dev->locked_tasklet_func = i915_vblank_tasklet; | |
340 | spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); | |
341 | ||
342 | /* Try to get the lock now, if this fails, the lock | |
343 | * holder will execute the tasklet during unlock | |
344 | */ | |
345 | if (!drm_lock_take(&dev->lock, DRM_KERNEL_CONTEXT)) | |
346 | return; | |
347 | ||
348 | dev->lock.lock_time = jiffies; | |
349 | atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); | |
350 | ||
351 | spin_lock_irqsave(&dev->tasklet_lock, irqflags); | |
352 | dev->locked_tasklet_func = NULL; | |
353 | spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); | |
546b0974 | 354 | |
546b0974 | 355 | i915_vblank_tasklet(dev); |
9e44af79 | 356 | drm_lock_free(&dev->lock, DRM_KERNEL_CONTEXT); |
546b0974 EA |
357 | } |
358 | ||
1da177e4 LT |
359 | irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) |
360 | { | |
84b1fd10 | 361 | struct drm_device *dev = (struct drm_device *) arg; |
1da177e4 | 362 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; |
ed4cb414 | 363 | u32 iir; |
0a3e67a4 JB |
364 | u32 pipea_stats, pipeb_stats; |
365 | int vblank = 0; | |
6e5fca53 | 366 | |
630681d9 EA |
367 | atomic_inc(&dev_priv->irq_received); |
368 | ||
ed4cb414 EA |
369 | if (dev->pdev->msi_enabled) |
370 | I915_WRITE(IMR, ~0); | |
371 | iir = I915_READ(IIR); | |
a6b54f3f | 372 | |
ed4cb414 EA |
373 | if (iir == 0) { |
374 | if (dev->pdev->msi_enabled) { | |
375 | I915_WRITE(IMR, dev_priv->irq_mask_reg); | |
376 | (void) I915_READ(IMR); | |
377 | } | |
af6061af | 378 | return IRQ_NONE; |
ed4cb414 | 379 | } |
af6061af | 380 | |
0a3e67a4 JB |
381 | /* |
382 | * Clear the PIPE(A|B)STAT regs before the IIR otherwise | |
383 | * we may get extra interrupts. | |
384 | */ | |
385 | if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { | |
386 | pipea_stats = I915_READ(PIPEASTAT); | |
387 | if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)) | |
388 | pipea_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | | |
389 | PIPE_VBLANK_INTERRUPT_ENABLE); | |
390 | else if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| | |
391 | PIPE_VBLANK_INTERRUPT_STATUS)) { | |
392 | vblank++; | |
42f52ef8 | 393 | drm_handle_vblank(dev, 0); |
0a3e67a4 | 394 | } |
af6061af | 395 | |
0a3e67a4 JB |
396 | I915_WRITE(PIPEASTAT, pipea_stats); |
397 | } | |
398 | if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { | |
399 | pipeb_stats = I915_READ(PIPEBSTAT); | |
400 | /* Ack the event */ | |
401 | I915_WRITE(PIPEBSTAT, pipeb_stats); | |
402 | ||
403 | /* The vblank interrupt gets enabled even if we didn't ask for | |
404 | it, so make sure it's shut down again */ | |
405 | if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)) | |
406 | pipeb_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | | |
407 | PIPE_VBLANK_INTERRUPT_ENABLE); | |
408 | else if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| | |
409 | PIPE_VBLANK_INTERRUPT_STATUS)) { | |
410 | vblank++; | |
42f52ef8 | 411 | drm_handle_vblank(dev, 1); |
0a3e67a4 | 412 | } |
af6061af | 413 | |
0a3e67a4 JB |
414 | if (pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) |
415 | opregion_asle_intr(dev); | |
416 | I915_WRITE(PIPEBSTAT, pipeb_stats); | |
0d6aa60b | 417 | } |
1da177e4 | 418 | |
673a394b EA |
419 | I915_WRITE(IIR, iir); |
420 | if (dev->pdev->msi_enabled) | |
421 | I915_WRITE(IMR, dev_priv->irq_mask_reg); | |
422 | (void) I915_READ(IIR); /* Flush posted writes */ | |
8ee1c3db | 423 | |
c99b058f KH |
424 | if (dev_priv->sarea_priv) |
425 | dev_priv->sarea_priv->last_dispatch = | |
426 | READ_BREADCRUMB(dev_priv); | |
0a3e67a4 | 427 | |
673a394b EA |
428 | if (iir & I915_USER_INTERRUPT) { |
429 | dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); | |
430 | DRM_WAKEUP(&dev_priv->irq_queue); | |
431 | } | |
432 | ||
433 | if (iir & I915_ASLE_INTERRUPT) | |
434 | opregion_asle_intr(dev); | |
0a3e67a4 | 435 | |
9e44af79 KP |
436 | if (vblank && dev_priv->swaps_pending > 0) |
437 | schedule_work(&dev_priv->vblank_work); | |
8ee1c3db | 438 | |
1da177e4 LT |
439 | return IRQ_HANDLED; |
440 | } | |
441 | ||
af6061af | 442 | static int i915_emit_irq(struct drm_device * dev) |
1da177e4 LT |
443 | { |
444 | drm_i915_private_t *dev_priv = dev->dev_private; | |
1da177e4 LT |
445 | RING_LOCALS; |
446 | ||
447 | i915_kernel_lost_context(dev); | |
448 | ||
3e684eae | 449 | DRM_DEBUG("\n"); |
1da177e4 | 450 | |
c99b058f | 451 | dev_priv->counter++; |
c29b669c | 452 | if (dev_priv->counter > 0x7FFFFFFFUL) |
c99b058f KH |
453 | dev_priv->counter = 1; |
454 | if (dev_priv->sarea_priv) | |
455 | dev_priv->sarea_priv->last_enqueue = dev_priv->counter; | |
c29b669c AH |
456 | |
457 | BEGIN_LP_RING(6); | |
585fb111 JB |
458 | OUT_RING(MI_STORE_DWORD_INDEX); |
459 | OUT_RING(5 << MI_STORE_DWORD_INDEX_SHIFT); | |
c29b669c AH |
460 | OUT_RING(dev_priv->counter); |
461 | OUT_RING(0); | |
1da177e4 | 462 | OUT_RING(0); |
585fb111 | 463 | OUT_RING(MI_USER_INTERRUPT); |
1da177e4 | 464 | ADVANCE_LP_RING(); |
bc5f4523 | 465 | |
c29b669c | 466 | return dev_priv->counter; |
1da177e4 LT |
467 | } |
468 | ||
673a394b | 469 | void i915_user_irq_get(struct drm_device *dev) |
ed4cb414 EA |
470 | { |
471 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
e9d21d7f | 472 | unsigned long irqflags; |
ed4cb414 | 473 | |
e9d21d7f | 474 | spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); |
ed4cb414 EA |
475 | if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)) |
476 | i915_enable_irq(dev_priv, I915_USER_INTERRUPT); | |
e9d21d7f | 477 | spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); |
ed4cb414 EA |
478 | } |
479 | ||
0a3e67a4 | 480 | void i915_user_irq_put(struct drm_device *dev) |
ed4cb414 EA |
481 | { |
482 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
e9d21d7f | 483 | unsigned long irqflags; |
ed4cb414 | 484 | |
e9d21d7f | 485 | spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); |
ed4cb414 EA |
486 | BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0); |
487 | if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) | |
488 | i915_disable_irq(dev_priv, I915_USER_INTERRUPT); | |
e9d21d7f | 489 | spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); |
ed4cb414 EA |
490 | } |
491 | ||
84b1fd10 | 492 | static int i915_wait_irq(struct drm_device * dev, int irq_nr) |
1da177e4 LT |
493 | { |
494 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
495 | int ret = 0; | |
496 | ||
3e684eae | 497 | DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr, |
1da177e4 LT |
498 | READ_BREADCRUMB(dev_priv)); |
499 | ||
ed4cb414 | 500 | if (READ_BREADCRUMB(dev_priv) >= irq_nr) { |
c99b058f KH |
501 | if (dev_priv->sarea_priv) { |
502 | dev_priv->sarea_priv->last_dispatch = | |
503 | READ_BREADCRUMB(dev_priv); | |
504 | } | |
1da177e4 | 505 | return 0; |
ed4cb414 | 506 | } |
1da177e4 | 507 | |
c99b058f KH |
508 | if (dev_priv->sarea_priv) |
509 | dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT; | |
1da177e4 | 510 | |
ed4cb414 | 511 | i915_user_irq_get(dev); |
1da177e4 LT |
512 | DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ, |
513 | READ_BREADCRUMB(dev_priv) >= irq_nr); | |
ed4cb414 | 514 | i915_user_irq_put(dev); |
1da177e4 | 515 | |
20caafa6 | 516 | if (ret == -EBUSY) { |
3e684eae | 517 | DRM_ERROR("EBUSY -- rec: %d emitted: %d\n", |
1da177e4 LT |
518 | READ_BREADCRUMB(dev_priv), (int)dev_priv->counter); |
519 | } | |
520 | ||
c99b058f KH |
521 | if (dev_priv->sarea_priv) |
522 | dev_priv->sarea_priv->last_dispatch = | |
523 | READ_BREADCRUMB(dev_priv); | |
af6061af DA |
524 | |
525 | return ret; | |
526 | } | |
527 | ||
1da177e4 LT |
528 | /* Needs the lock as it touches the ring. |
529 | */ | |
c153f45f EA |
530 | int i915_irq_emit(struct drm_device *dev, void *data, |
531 | struct drm_file *file_priv) | |
1da177e4 | 532 | { |
1da177e4 | 533 | drm_i915_private_t *dev_priv = dev->dev_private; |
c153f45f | 534 | drm_i915_irq_emit_t *emit = data; |
1da177e4 LT |
535 | int result; |
536 | ||
546b0974 | 537 | RING_LOCK_TEST_WITH_RETURN(dev, file_priv); |
1da177e4 LT |
538 | |
539 | if (!dev_priv) { | |
3e684eae | 540 | DRM_ERROR("called with no initialization\n"); |
20caafa6 | 541 | return -EINVAL; |
1da177e4 | 542 | } |
546b0974 | 543 | mutex_lock(&dev->struct_mutex); |
1da177e4 | 544 | result = i915_emit_irq(dev); |
546b0974 | 545 | mutex_unlock(&dev->struct_mutex); |
1da177e4 | 546 | |
c153f45f | 547 | if (DRM_COPY_TO_USER(emit->irq_seq, &result, sizeof(int))) { |
1da177e4 | 548 | DRM_ERROR("copy_to_user\n"); |
20caafa6 | 549 | return -EFAULT; |
1da177e4 LT |
550 | } |
551 | ||
552 | return 0; | |
553 | } | |
554 | ||
555 | /* Doesn't need the hardware lock. | |
556 | */ | |
c153f45f EA |
557 | int i915_irq_wait(struct drm_device *dev, void *data, |
558 | struct drm_file *file_priv) | |
1da177e4 | 559 | { |
1da177e4 | 560 | drm_i915_private_t *dev_priv = dev->dev_private; |
c153f45f | 561 | drm_i915_irq_wait_t *irqwait = data; |
1da177e4 LT |
562 | |
563 | if (!dev_priv) { | |
3e684eae | 564 | DRM_ERROR("called with no initialization\n"); |
20caafa6 | 565 | return -EINVAL; |
1da177e4 LT |
566 | } |
567 | ||
c153f45f | 568 | return i915_wait_irq(dev, irqwait->irq_seq); |
1da177e4 LT |
569 | } |
570 | ||
42f52ef8 KP |
571 | /* Called from drm generic code, passed 'crtc' which |
572 | * we use as a pipe index | |
573 | */ | |
574 | int i915_enable_vblank(struct drm_device *dev, int pipe) | |
0a3e67a4 JB |
575 | { |
576 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
0a3e67a4 JB |
577 | u32 pipestat_reg = 0; |
578 | u32 pipestat; | |
e9d21d7f KP |
579 | u32 interrupt = 0; |
580 | unsigned long irqflags; | |
0a3e67a4 JB |
581 | |
582 | switch (pipe) { | |
583 | case 0: | |
584 | pipestat_reg = PIPEASTAT; | |
e9d21d7f | 585 | interrupt = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; |
0a3e67a4 JB |
586 | break; |
587 | case 1: | |
588 | pipestat_reg = PIPEBSTAT; | |
e9d21d7f | 589 | interrupt = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; |
0a3e67a4 JB |
590 | break; |
591 | default: | |
592 | DRM_ERROR("tried to enable vblank on non-existent pipe %d\n", | |
593 | pipe); | |
e9d21d7f | 594 | return 0; |
0a3e67a4 JB |
595 | } |
596 | ||
e9d21d7f | 597 | spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); |
053d7f24 EA |
598 | /* Enabling vblank events in IMR comes before PIPESTAT write, or |
599 | * there's a race where the PIPESTAT vblank bit gets set to 1, so | |
600 | * the OR of enabled PIPESTAT bits goes to 1, so the PIPExEVENT in | |
601 | * ISR flashes to 1, but the IIR bit doesn't get set to 1 because | |
602 | * IMR masks it. It doesn't ever get set after we clear the masking | |
603 | * in IMR because the ISR bit is edge, not level-triggered, on the | |
604 | * OR of PIPESTAT bits. | |
605 | */ | |
606 | i915_enable_irq(dev_priv, interrupt); | |
e9d21d7f KP |
607 | pipestat = I915_READ(pipestat_reg); |
608 | if (IS_I965G(dev)) | |
609 | pipestat |= PIPE_START_VBLANK_INTERRUPT_ENABLE; | |
610 | else | |
611 | pipestat |= PIPE_VBLANK_INTERRUPT_ENABLE; | |
612 | /* Clear any stale interrupt status */ | |
613 | pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | | |
614 | PIPE_VBLANK_INTERRUPT_STATUS); | |
615 | I915_WRITE(pipestat_reg, pipestat); | |
616 | (void) I915_READ(pipestat_reg); /* Posting read */ | |
e9d21d7f | 617 | spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); |
0a3e67a4 JB |
618 | |
619 | return 0; | |
620 | } | |
621 | ||
42f52ef8 KP |
622 | /* Called from drm generic code, passed 'crtc' which |
623 | * we use as a pipe index | |
624 | */ | |
625 | void i915_disable_vblank(struct drm_device *dev, int pipe) | |
0a3e67a4 JB |
626 | { |
627 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
0a3e67a4 JB |
628 | u32 pipestat_reg = 0; |
629 | u32 pipestat; | |
e9d21d7f KP |
630 | u32 interrupt = 0; |
631 | unsigned long irqflags; | |
0a3e67a4 JB |
632 | |
633 | switch (pipe) { | |
634 | case 0: | |
635 | pipestat_reg = PIPEASTAT; | |
e9d21d7f | 636 | interrupt = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; |
0a3e67a4 JB |
637 | break; |
638 | case 1: | |
639 | pipestat_reg = PIPEBSTAT; | |
e9d21d7f | 640 | interrupt = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; |
0a3e67a4 JB |
641 | break; |
642 | default: | |
643 | DRM_ERROR("tried to disable vblank on non-existent pipe %d\n", | |
644 | pipe); | |
e9d21d7f | 645 | return; |
0a3e67a4 JB |
646 | break; |
647 | } | |
648 | ||
e9d21d7f KP |
649 | spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); |
650 | i915_disable_irq(dev_priv, interrupt); | |
651 | pipestat = I915_READ(pipestat_reg); | |
652 | pipestat &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | | |
653 | PIPE_VBLANK_INTERRUPT_ENABLE); | |
654 | /* Clear any stale interrupt status */ | |
655 | pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | | |
656 | PIPE_VBLANK_INTERRUPT_STATUS); | |
657 | I915_WRITE(pipestat_reg, pipestat); | |
658 | (void) I915_READ(pipestat_reg); /* Posting read */ | |
659 | spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); | |
0a3e67a4 JB |
660 | } |
661 | ||
702880f2 DA |
662 | /* Set the vblank monitor pipe |
663 | */ | |
c153f45f EA |
664 | int i915_vblank_pipe_set(struct drm_device *dev, void *data, |
665 | struct drm_file *file_priv) | |
702880f2 | 666 | { |
702880f2 | 667 | drm_i915_private_t *dev_priv = dev->dev_private; |
702880f2 DA |
668 | |
669 | if (!dev_priv) { | |
3e684eae | 670 | DRM_ERROR("called with no initialization\n"); |
20caafa6 | 671 | return -EINVAL; |
702880f2 DA |
672 | } |
673 | ||
5b51694a | 674 | return 0; |
702880f2 DA |
675 | } |
676 | ||
c153f45f EA |
677 | int i915_vblank_pipe_get(struct drm_device *dev, void *data, |
678 | struct drm_file *file_priv) | |
702880f2 | 679 | { |
702880f2 | 680 | drm_i915_private_t *dev_priv = dev->dev_private; |
c153f45f | 681 | drm_i915_vblank_pipe_t *pipe = data; |
702880f2 DA |
682 | |
683 | if (!dev_priv) { | |
3e684eae | 684 | DRM_ERROR("called with no initialization\n"); |
20caafa6 | 685 | return -EINVAL; |
702880f2 DA |
686 | } |
687 | ||
0a3e67a4 | 688 | pipe->pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; |
c153f45f | 689 | |
702880f2 DA |
690 | return 0; |
691 | } | |
692 | ||
a6b54f3f MCA |
693 | /** |
694 | * Schedule buffer swap at given vertical blank. | |
695 | */ | |
c153f45f EA |
696 | int i915_vblank_swap(struct drm_device *dev, void *data, |
697 | struct drm_file *file_priv) | |
a6b54f3f | 698 | { |
a6b54f3f | 699 | drm_i915_private_t *dev_priv = dev->dev_private; |
c153f45f | 700 | drm_i915_vblank_swap_t *swap = data; |
9e44af79 | 701 | drm_i915_vbl_swap_t *vbl_swap, *vbl_old; |
42f52ef8 | 702 | unsigned int pipe, seqtype, curseq; |
a0b136bb | 703 | unsigned long irqflags; |
a6b54f3f | 704 | struct list_head *list; |
0a3e67a4 | 705 | int ret; |
a6b54f3f | 706 | |
c99b058f | 707 | if (!dev_priv || !dev_priv->sarea_priv) { |
a6b54f3f | 708 | DRM_ERROR("%s called with no initialization\n", __func__); |
20caafa6 | 709 | return -EINVAL; |
a6b54f3f MCA |
710 | } |
711 | ||
af6061af | 712 | if (dev_priv->sarea_priv->rotation) { |
a6b54f3f | 713 | DRM_DEBUG("Rotation not supported\n"); |
20caafa6 | 714 | return -EINVAL; |
a6b54f3f MCA |
715 | } |
716 | ||
c153f45f | 717 | if (swap->seqtype & ~(_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE | |
af6061af | 718 | _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS)) { |
c153f45f | 719 | DRM_ERROR("Invalid sequence type 0x%x\n", swap->seqtype); |
20caafa6 | 720 | return -EINVAL; |
541f29aa MCA |
721 | } |
722 | ||
42f52ef8 | 723 | pipe = (swap->seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0; |
541f29aa | 724 | |
c153f45f | 725 | seqtype = swap->seqtype & (_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE); |
541f29aa | 726 | |
541f29aa MCA |
727 | if (!(dev_priv->vblank_pipe & (1 << pipe))) { |
728 | DRM_ERROR("Invalid pipe %d\n", pipe); | |
20caafa6 | 729 | return -EINVAL; |
a6b54f3f MCA |
730 | } |
731 | ||
732 | spin_lock_irqsave(&dev->drw_lock, irqflags); | |
733 | ||
c153f45f | 734 | if (!drm_get_drawable_info(dev, swap->drawable)) { |
a6b54f3f | 735 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); |
c153f45f | 736 | DRM_DEBUG("Invalid drawable ID %d\n", swap->drawable); |
20caafa6 | 737 | return -EINVAL; |
a6b54f3f MCA |
738 | } |
739 | ||
740 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); | |
741 | ||
0a3e67a4 JB |
742 | /* |
743 | * We take the ref here and put it when the swap actually completes | |
744 | * in the tasklet. | |
745 | */ | |
746 | ret = drm_vblank_get(dev, pipe); | |
747 | if (ret) | |
748 | return ret; | |
749 | curseq = drm_vblank_count(dev, pipe); | |
541f29aa | 750 | |
2228ed67 | 751 | if (seqtype == _DRM_VBLANK_RELATIVE) |
c153f45f | 752 | swap->sequence += curseq; |
2228ed67 | 753 | |
c153f45f EA |
754 | if ((curseq - swap->sequence) <= (1<<23)) { |
755 | if (swap->seqtype & _DRM_VBLANK_NEXTONMISS) { | |
756 | swap->sequence = curseq + 1; | |
2228ed67 | 757 | } else { |
541f29aa | 758 | DRM_DEBUG("Missed target sequence\n"); |
0a3e67a4 | 759 | drm_vblank_put(dev, pipe); |
20caafa6 | 760 | return -EINVAL; |
541f29aa | 761 | } |
541f29aa MCA |
762 | } |
763 | ||
9e44af79 KP |
764 | vbl_swap = drm_calloc(1, sizeof(*vbl_swap), DRM_MEM_DRIVER); |
765 | ||
766 | if (!vbl_swap) { | |
767 | DRM_ERROR("Failed to allocate memory to queue swap\n"); | |
768 | drm_vblank_put(dev, pipe); | |
769 | return -ENOMEM; | |
770 | } | |
771 | ||
772 | vbl_swap->drw_id = swap->drawable; | |
773 | vbl_swap->pipe = pipe; | |
774 | vbl_swap->sequence = swap->sequence; | |
775 | ||
2228ed67 MCA |
776 | spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); |
777 | ||
a6b54f3f | 778 | list_for_each(list, &dev_priv->vbl_swaps.head) { |
9e44af79 | 779 | vbl_old = list_entry(list, drm_i915_vbl_swap_t, head); |
a6b54f3f | 780 | |
9e44af79 KP |
781 | if (vbl_old->drw_id == swap->drawable && |
782 | vbl_old->pipe == pipe && | |
783 | vbl_old->sequence == swap->sequence) { | |
a6b54f3f | 784 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); |
35ad68c1 | 785 | drm_vblank_put(dev, pipe); |
9e44af79 | 786 | drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); |
a6b54f3f MCA |
787 | DRM_DEBUG("Already scheduled\n"); |
788 | return 0; | |
789 | } | |
790 | } | |
791 | ||
9e44af79 | 792 | if (dev_priv->swaps_pending >= 10) { |
21fa60ed | 793 | DRM_DEBUG("Too many swaps queued\n"); |
9e44af79 | 794 | DRM_DEBUG(" pipe 0: %d pipe 1: %d\n", |
42f52ef8 KP |
795 | drm_vblank_count(dev, 0), |
796 | drm_vblank_count(dev, 1)); | |
9e44af79 KP |
797 | |
798 | list_for_each(list, &dev_priv->vbl_swaps.head) { | |
799 | vbl_old = list_entry(list, drm_i915_vbl_swap_t, head); | |
800 | DRM_DEBUG("\tdrw %x pipe %d seq %x\n", | |
801 | vbl_old->drw_id, vbl_old->pipe, | |
802 | vbl_old->sequence); | |
803 | } | |
804 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | |
0a3e67a4 | 805 | drm_vblank_put(dev, pipe); |
9e44af79 | 806 | drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); |
20caafa6 | 807 | return -EBUSY; |
21fa60ed MCA |
808 | } |
809 | ||
d5b0d1b5 | 810 | list_add_tail(&vbl_swap->head, &dev_priv->vbl_swaps.head); |
a6b54f3f MCA |
811 | dev_priv->swaps_pending++; |
812 | ||
813 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | |
814 | ||
815 | return 0; | |
816 | } | |
817 | ||
1da177e4 LT |
818 | /* drm_dma.h hooks |
819 | */ | |
84b1fd10 | 820 | void i915_driver_irq_preinstall(struct drm_device * dev) |
1da177e4 LT |
821 | { |
822 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
823 | ||
0a3e67a4 JB |
824 | I915_WRITE(HWSTAM, 0xeffe); |
825 | I915_WRITE(IMR, 0xffffffff); | |
ed4cb414 | 826 | I915_WRITE(IER, 0x0); |
1da177e4 LT |
827 | } |
828 | ||
0a3e67a4 | 829 | int i915_driver_irq_postinstall(struct drm_device *dev) |
1da177e4 LT |
830 | { |
831 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
0a3e67a4 | 832 | int ret, num_pipes = 2; |
1da177e4 | 833 | |
a6399bdd | 834 | spin_lock_init(&dev_priv->swaps_lock); |
a6b54f3f | 835 | INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); |
9e44af79 | 836 | INIT_WORK(&dev_priv->vblank_work, i915_vblank_work_handler); |
a6b54f3f MCA |
837 | dev_priv->swaps_pending = 0; |
838 | ||
ed4cb414 EA |
839 | /* Set initial unmasked IRQs to just the selected vblank pipes. */ |
840 | dev_priv->irq_mask_reg = ~0; | |
0a3e67a4 JB |
841 | |
842 | ret = drm_vblank_init(dev, num_pipes); | |
843 | if (ret) | |
844 | return ret; | |
845 | ||
846 | dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; | |
847 | dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; | |
848 | dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; | |
849 | ||
850 | dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ | |
ed4cb414 | 851 | |
8ee1c3db MG |
852 | dev_priv->irq_mask_reg &= I915_INTERRUPT_ENABLE_MASK; |
853 | ||
ed4cb414 EA |
854 | I915_WRITE(IMR, dev_priv->irq_mask_reg); |
855 | I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK); | |
856 | (void) I915_READ(IER); | |
857 | ||
8ee1c3db | 858 | opregion_enable_asle(dev); |
1da177e4 | 859 | DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); |
0a3e67a4 JB |
860 | |
861 | return 0; | |
1da177e4 LT |
862 | } |
863 | ||
84b1fd10 | 864 | void i915_driver_irq_uninstall(struct drm_device * dev) |
1da177e4 LT |
865 | { |
866 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
0a3e67a4 | 867 | u32 temp; |
91e3738e | 868 | |
1da177e4 LT |
869 | if (!dev_priv) |
870 | return; | |
871 | ||
0a3e67a4 JB |
872 | dev_priv->vblank_pipe = 0; |
873 | ||
874 | I915_WRITE(HWSTAM, 0xffffffff); | |
875 | I915_WRITE(IMR, 0xffffffff); | |
ed4cb414 | 876 | I915_WRITE(IER, 0x0); |
af6061af | 877 | |
0a3e67a4 JB |
878 | temp = I915_READ(PIPEASTAT); |
879 | I915_WRITE(PIPEASTAT, temp); | |
880 | temp = I915_READ(PIPEBSTAT); | |
881 | I915_WRITE(PIPEBSTAT, temp); | |
ed4cb414 EA |
882 | temp = I915_READ(IIR); |
883 | I915_WRITE(IIR, temp); | |
1da177e4 | 884 | } |