diff options
Diffstat (limited to 'meta-moblin/packages/linux/linux-moblin-2.6.27-rc6/0011-drm-vblank-rework.patch')
-rw-r--r-- | meta-moblin/packages/linux/linux-moblin-2.6.27-rc6/0011-drm-vblank-rework.patch | 1534 |
1 files changed, 1534 insertions, 0 deletions
diff --git a/meta-moblin/packages/linux/linux-moblin-2.6.27-rc6/0011-drm-vblank-rework.patch b/meta-moblin/packages/linux/linux-moblin-2.6.27-rc6/0011-drm-vblank-rework.patch new file mode 100644 index 0000000000..6161a71f04 --- /dev/null +++ b/meta-moblin/packages/linux/linux-moblin-2.6.27-rc6/0011-drm-vblank-rework.patch | |||
@@ -0,0 +1,1534 @@ | |||
1 | commit 2aebb4e4e62d09b4a95be7be7c24a7f6528385b7 | ||
2 | Author: Jesse Barnes <jbarnes@virtuousgeek.org> | ||
3 | Date: Tue Sep 30 12:14:26 2008 -0700 | ||
4 | |||
5 | drm: Rework vblank-wait handling to allow interrupt reduction. | ||
6 | |||
7 | Previously, drivers supporting vblank interrupt waits would run the interrupt | ||
8 | all the time, or all the time that any 3d client was running, preventing the | ||
9 | CPU from sleeping for long when the system was otherwise idle. Now, interrupts | ||
10 | are disabled any time that no client is waiting on a vblank event. The new | ||
11 | method uses vblank counters on the chipsets when the interrupts are turned | ||
12 | off, rather than counting interrupts, so that we can continue to present | ||
13 | accurate vblank numbers. | ||
14 | |||
15 | Co-author: Michel Dänzer <michel@tungstengraphics.com> | ||
16 | Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> | ||
17 | Signed-off-by: Eric Anholt <eric@anholt.net> | ||
18 | Signed-off-by: Dave Airlie <airlied@redhat.com> | ||
19 | |||
20 | diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c | ||
21 | index 452c2d8..fb45fe7 100644 | ||
22 | --- a/drivers/gpu/drm/drm_drv.c | ||
23 | +++ b/drivers/gpu/drm/drm_drv.c | ||
24 | @@ -116,6 +116,8 @@ static struct drm_ioctl_desc drm_ioctls[] = { | ||
25 | |||
26 | DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, 0), | ||
27 | |||
28 | + DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0), | ||
29 | + | ||
30 | DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_update_drawable_info, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), | ||
31 | }; | ||
32 | |||
33 | diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c | ||
34 | index 61ed515..d0c13d9 100644 | ||
35 | --- a/drivers/gpu/drm/drm_irq.c | ||
36 | +++ b/drivers/gpu/drm/drm_irq.c | ||
37 | @@ -71,19 +71,131 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, | ||
38 | return 0; | ||
39 | } | ||
40 | |||
41 | +static void vblank_disable_fn(unsigned long arg) | ||
42 | +{ | ||
43 | + struct drm_device *dev = (struct drm_device *)arg; | ||
44 | + unsigned long irqflags; | ||
45 | + int i; | ||
46 | + | ||
47 | + if (!dev->vblank_disable_allowed) | ||
48 | + return; | ||
49 | + | ||
50 | + for (i = 0; i < dev->num_crtcs; i++) { | ||
51 | + spin_lock_irqsave(&dev->vbl_lock, irqflags); | ||
52 | + if (atomic_read(&dev->vblank_refcount[i]) == 0 && | ||
53 | + dev->vblank_enabled[i]) { | ||
54 | + DRM_DEBUG("disabling vblank on crtc %d\n", i); | ||
55 | + dev->last_vblank[i] = | ||
56 | + dev->driver->get_vblank_counter(dev, i); | ||
57 | + dev->driver->disable_vblank(dev, i); | ||
58 | + dev->vblank_enabled[i] = 0; | ||
59 | + } | ||
60 | + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
61 | + } | ||
62 | +} | ||
63 | + | ||
64 | +static void drm_vblank_cleanup(struct drm_device *dev) | ||
65 | +{ | ||
66 | + /* Bail if the driver didn't call drm_vblank_init() */ | ||
67 | + if (dev->num_crtcs == 0) | ||
68 | + return; | ||
69 | + | ||
70 | + del_timer(&dev->vblank_disable_timer); | ||
71 | + | ||
72 | + vblank_disable_fn((unsigned long)dev); | ||
73 | + | ||
74 | + drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs, | ||
75 | + DRM_MEM_DRIVER); | ||
76 | + drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs, | ||
77 | + DRM_MEM_DRIVER); | ||
78 | + drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * | ||
79 | + dev->num_crtcs, DRM_MEM_DRIVER); | ||
80 | + drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * | ||
81 | + dev->num_crtcs, DRM_MEM_DRIVER); | ||
82 | + drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) * | ||
83 | + dev->num_crtcs, DRM_MEM_DRIVER); | ||
84 | + drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs, | ||
85 | + DRM_MEM_DRIVER); | ||
86 | + drm_free(dev->vblank_inmodeset, sizeof(*dev->vblank_inmodeset) * | ||
87 | + dev->num_crtcs, DRM_MEM_DRIVER); | ||
88 | + | ||
89 | + dev->num_crtcs = 0; | ||
90 | +} | ||
91 | + | ||
92 | +int drm_vblank_init(struct drm_device *dev, int num_crtcs) | ||
93 | +{ | ||
94 | + int i, ret = -ENOMEM; | ||
95 | + | ||
96 | + setup_timer(&dev->vblank_disable_timer, vblank_disable_fn, | ||
97 | + (unsigned long)dev); | ||
98 | + spin_lock_init(&dev->vbl_lock); | ||
99 | + atomic_set(&dev->vbl_signal_pending, 0); | ||
100 | + dev->num_crtcs = num_crtcs; | ||
101 | + | ||
102 | + dev->vbl_queue = drm_alloc(sizeof(wait_queue_head_t) * num_crtcs, | ||
103 | + DRM_MEM_DRIVER); | ||
104 | + if (!dev->vbl_queue) | ||
105 | + goto err; | ||
106 | + | ||
107 | + dev->vbl_sigs = drm_alloc(sizeof(struct list_head) * num_crtcs, | ||
108 | + DRM_MEM_DRIVER); | ||
109 | + if (!dev->vbl_sigs) | ||
110 | + goto err; | ||
111 | + | ||
112 | + dev->_vblank_count = drm_alloc(sizeof(atomic_t) * num_crtcs, | ||
113 | + DRM_MEM_DRIVER); | ||
114 | + if (!dev->_vblank_count) | ||
115 | + goto err; | ||
116 | + | ||
117 | + dev->vblank_refcount = drm_alloc(sizeof(atomic_t) * num_crtcs, | ||
118 | + DRM_MEM_DRIVER); | ||
119 | + if (!dev->vblank_refcount) | ||
120 | + goto err; | ||
121 | + | ||
122 | + dev->vblank_enabled = drm_calloc(num_crtcs, sizeof(int), | ||
123 | + DRM_MEM_DRIVER); | ||
124 | + if (!dev->vblank_enabled) | ||
125 | + goto err; | ||
126 | + | ||
127 | + dev->last_vblank = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); | ||
128 | + if (!dev->last_vblank) | ||
129 | + goto err; | ||
130 | + | ||
131 | + dev->vblank_inmodeset = drm_calloc(num_crtcs, sizeof(int), | ||
132 | + DRM_MEM_DRIVER); | ||
133 | + if (!dev->vblank_inmodeset) | ||
134 | + goto err; | ||
135 | + | ||
136 | + /* Zero per-crtc vblank stuff */ | ||
137 | + for (i = 0; i < num_crtcs; i++) { | ||
138 | + init_waitqueue_head(&dev->vbl_queue[i]); | ||
139 | + INIT_LIST_HEAD(&dev->vbl_sigs[i]); | ||
140 | + atomic_set(&dev->_vblank_count[i], 0); | ||
141 | + atomic_set(&dev->vblank_refcount[i], 0); | ||
142 | + } | ||
143 | + | ||
144 | + dev->vblank_disable_allowed = 0; | ||
145 | + | ||
146 | + return 0; | ||
147 | + | ||
148 | +err: | ||
149 | + drm_vblank_cleanup(dev); | ||
150 | + return ret; | ||
151 | +} | ||
152 | +EXPORT_SYMBOL(drm_vblank_init); | ||
153 | + | ||
154 | /** | ||
155 | * Install IRQ handler. | ||
156 | * | ||
157 | * \param dev DRM device. | ||
158 | - * \param irq IRQ number. | ||
159 | * | ||
160 | - * Initializes the IRQ related data, and setups drm_device::vbl_queue. Installs the handler, calling the driver | ||
161 | + * Initializes the IRQ related data. Installs the handler, calling the driver | ||
162 | * \c drm_driver_irq_preinstall() and \c drm_driver_irq_postinstall() functions | ||
163 | * before and after the installation. | ||
164 | */ | ||
165 | -static int drm_irq_install(struct drm_device * dev) | ||
166 | +int drm_irq_install(struct drm_device *dev) | ||
167 | { | ||
168 | - int ret; | ||
169 | + int ret = 0; | ||
170 | unsigned long sh_flags = 0; | ||
171 | |||
172 | if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) | ||
173 | @@ -109,17 +221,6 @@ static int drm_irq_install(struct drm_device * dev) | ||
174 | |||
175 | DRM_DEBUG("irq=%d\n", dev->pdev->irq); | ||
176 | |||
177 | - if (drm_core_check_feature(dev, DRIVER_IRQ_VBL)) { | ||
178 | - init_waitqueue_head(&dev->vbl_queue); | ||
179 | - | ||
180 | - spin_lock_init(&dev->vbl_lock); | ||
181 | - | ||
182 | - INIT_LIST_HEAD(&dev->vbl_sigs); | ||
183 | - INIT_LIST_HEAD(&dev->vbl_sigs2); | ||
184 | - | ||
185 | - dev->vbl_pending = 0; | ||
186 | - } | ||
187 | - | ||
188 | /* Before installing handler */ | ||
189 | dev->driver->irq_preinstall(dev); | ||
190 | |||
191 | @@ -141,10 +242,16 @@ static int drm_irq_install(struct drm_device * dev) | ||
192 | } | ||
193 | |||
194 | /* After installing handler */ | ||
195 | - dev->driver->irq_postinstall(dev); | ||
196 | + ret = dev->driver->irq_postinstall(dev); | ||
197 | + if (ret < 0) { | ||
198 | + mutex_lock(&dev->struct_mutex); | ||
199 | + dev->irq_enabled = 0; | ||
200 | + mutex_unlock(&dev->struct_mutex); | ||
201 | + } | ||
202 | |||
203 | - return 0; | ||
204 | + return ret; | ||
205 | } | ||
206 | +EXPORT_SYMBOL(drm_irq_install); | ||
207 | |||
208 | /** | ||
209 | * Uninstall the IRQ handler. | ||
210 | @@ -174,11 +281,12 @@ int drm_irq_uninstall(struct drm_device * dev) | ||
211 | |||
212 | free_irq(dev->pdev->irq, dev); | ||
213 | |||
214 | + drm_vblank_cleanup(dev); | ||
215 | + | ||
216 | dev->locked_tasklet_func = NULL; | ||
217 | |||
218 | return 0; | ||
219 | } | ||
220 | - | ||
221 | EXPORT_SYMBOL(drm_irq_uninstall); | ||
222 | |||
223 | /** | ||
224 | @@ -218,6 +326,174 @@ int drm_control(struct drm_device *dev, void *data, | ||
225 | } | ||
226 | |||
227 | /** | ||
228 | + * drm_vblank_count - retrieve "cooked" vblank counter value | ||
229 | + * @dev: DRM device | ||
230 | + * @crtc: which counter to retrieve | ||
231 | + * | ||
232 | + * Fetches the "cooked" vblank count value that represents the number of | ||
233 | + * vblank events since the system was booted, including lost events due to | ||
234 | + * modesetting activity. | ||
235 | + */ | ||
236 | +u32 drm_vblank_count(struct drm_device *dev, int crtc) | ||
237 | +{ | ||
238 | + return atomic_read(&dev->_vblank_count[crtc]); | ||
239 | +} | ||
240 | +EXPORT_SYMBOL(drm_vblank_count); | ||
241 | + | ||
242 | +/** | ||
243 | + * drm_update_vblank_count - update the master vblank counter | ||
244 | + * @dev: DRM device | ||
245 | + * @crtc: counter to update | ||
246 | + * | ||
247 | + * Call back into the driver to update the appropriate vblank counter | ||
248 | + * (specified by @crtc). Deal with wraparound, if it occurred, and | ||
249 | + * update the last read value so we can deal with wraparound on the next | ||
250 | + * call if necessary. | ||
251 | + * | ||
252 | + * Only necessary when going from off->on, to account for frames we | ||
253 | + * didn't get an interrupt for. | ||
254 | + * | ||
255 | + * Note: caller must hold dev->vbl_lock since this reads & writes | ||
256 | + * device vblank fields. | ||
257 | + */ | ||
258 | +static void drm_update_vblank_count(struct drm_device *dev, int crtc) | ||
259 | +{ | ||
260 | + u32 cur_vblank, diff; | ||
261 | + | ||
262 | + /* | ||
263 | + * Interrupts were disabled prior to this call, so deal with counter | ||
264 | + * wrap if needed. | ||
265 | + * NOTE! It's possible we lost a full dev->max_vblank_count events | ||
266 | + * here if the register is small or we had vblank interrupts off for | ||
267 | + * a long time. | ||
268 | + */ | ||
269 | + cur_vblank = dev->driver->get_vblank_counter(dev, crtc); | ||
270 | + diff = cur_vblank - dev->last_vblank[crtc]; | ||
271 | + if (cur_vblank < dev->last_vblank[crtc]) { | ||
272 | + diff += dev->max_vblank_count; | ||
273 | + | ||
274 | + DRM_DEBUG("last_vblank[%d]=0x%x, cur_vblank=0x%x => diff=0x%x\n", | ||
275 | + crtc, dev->last_vblank[crtc], cur_vblank, diff); | ||
276 | + } | ||
277 | + | ||
278 | + DRM_DEBUG("enabling vblank interrupts on crtc %d, missed %d\n", | ||
279 | + crtc, diff); | ||
280 | + | ||
281 | + atomic_add(diff, &dev->_vblank_count[crtc]); | ||
282 | +} | ||
283 | + | ||
284 | +/** | ||
285 | + * drm_vblank_get - get a reference count on vblank events | ||
286 | + * @dev: DRM device | ||
287 | + * @crtc: which CRTC to own | ||
288 | + * | ||
289 | + * Acquire a reference count on vblank events to avoid having them disabled | ||
290 | + * while in use. | ||
291 | + * | ||
292 | + * RETURNS | ||
293 | + * Zero on success, nonzero on failure. | ||
294 | + */ | ||
295 | +int drm_vblank_get(struct drm_device *dev, int crtc) | ||
296 | +{ | ||
297 | + unsigned long irqflags; | ||
298 | + int ret = 0; | ||
299 | + | ||
300 | + spin_lock_irqsave(&dev->vbl_lock, irqflags); | ||
301 | + /* Going from 0->1 means we have to enable interrupts again */ | ||
302 | + if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1 && | ||
303 | + !dev->vblank_enabled[crtc]) { | ||
304 | + ret = dev->driver->enable_vblank(dev, crtc); | ||
305 | + DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret); | ||
306 | + if (ret) | ||
307 | + atomic_dec(&dev->vblank_refcount[crtc]); | ||
308 | + else { | ||
309 | + dev->vblank_enabled[crtc] = 1; | ||
310 | + drm_update_vblank_count(dev, crtc); | ||
311 | + } | ||
312 | + } | ||
313 | + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
314 | + | ||
315 | + return ret; | ||
316 | +} | ||
317 | +EXPORT_SYMBOL(drm_vblank_get); | ||
318 | + | ||
319 | +/** | ||
320 | + * drm_vblank_put - give up ownership of vblank events | ||
321 | + * @dev: DRM device | ||
322 | + * @crtc: which counter to give up | ||
323 | + * | ||
324 | + * Release ownership of a given vblank counter, turning off interrupts | ||
325 | + * if possible. | ||
326 | + */ | ||
327 | +void drm_vblank_put(struct drm_device *dev, int crtc) | ||
328 | +{ | ||
329 | + /* Last user schedules interrupt disable */ | ||
330 | + if (atomic_dec_and_test(&dev->vblank_refcount[crtc])) | ||
331 | + mod_timer(&dev->vblank_disable_timer, jiffies + 5*DRM_HZ); | ||
332 | +} | ||
333 | +EXPORT_SYMBOL(drm_vblank_put); | ||
334 | + | ||
335 | +/** | ||
336 | + * drm_modeset_ctl - handle vblank event counter changes across mode switch | ||
337 | + * @DRM_IOCTL_ARGS: standard ioctl arguments | ||
338 | + * | ||
339 | + * Applications should call the %_DRM_PRE_MODESET and %_DRM_POST_MODESET | ||
340 | + * ioctls around modesetting so that any lost vblank events are accounted for. | ||
341 | + * | ||
342 | + * Generally the counter will reset across mode sets. If interrupts are | ||
343 | + * enabled around this call, we don't have to do anything since the counter | ||
344 | + * will have already been incremented. | ||
345 | + */ | ||
346 | +int drm_modeset_ctl(struct drm_device *dev, void *data, | ||
347 | + struct drm_file *file_priv) | ||
348 | +{ | ||
349 | + struct drm_modeset_ctl *modeset = data; | ||
350 | + unsigned long irqflags; | ||
351 | + int crtc, ret = 0; | ||
352 | + | ||
353 | + /* If drm_vblank_init() hasn't been called yet, just no-op */ | ||
354 | + if (!dev->num_crtcs) | ||
355 | + goto out; | ||
356 | + | ||
357 | + crtc = modeset->crtc; | ||
358 | + if (crtc >= dev->num_crtcs) { | ||
359 | + ret = -EINVAL; | ||
360 | + goto out; | ||
361 | + } | ||
362 | + | ||
363 | + /* | ||
364 | + * To avoid all the problems that might happen if interrupts | ||
365 | + * were enabled/disabled around or between these calls, we just | ||
366 | + * have the kernel take a reference on the CRTC (just once though | ||
367 | + * to avoid corrupting the count if multiple, mismatch calls occur), | ||
368 | + * so that interrupts remain enabled in the interim. | ||
369 | + */ | ||
370 | + switch (modeset->cmd) { | ||
371 | + case _DRM_PRE_MODESET: | ||
372 | + if (!dev->vblank_inmodeset[crtc]) { | ||
373 | + dev->vblank_inmodeset[crtc] = 1; | ||
374 | + drm_vblank_get(dev, crtc); | ||
375 | + } | ||
376 | + break; | ||
377 | + case _DRM_POST_MODESET: | ||
378 | + if (dev->vblank_inmodeset[crtc]) { | ||
379 | + spin_lock_irqsave(&dev->vbl_lock, irqflags); | ||
380 | + dev->vblank_disable_allowed = 1; | ||
381 | + dev->vblank_inmodeset[crtc] = 0; | ||
382 | + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
383 | + drm_vblank_put(dev, crtc); | ||
384 | + } | ||
385 | + break; | ||
386 | + default: | ||
387 | + ret = -EINVAL; | ||
388 | + break; | ||
389 | + } | ||
390 | + | ||
391 | +out: | ||
392 | + return ret; | ||
393 | +} | ||
394 | + | ||
395 | +/** | ||
396 | * Wait for VBLANK. | ||
397 | * | ||
398 | * \param inode device inode. | ||
399 | @@ -236,12 +512,12 @@ int drm_control(struct drm_device *dev, void *data, | ||
400 | * | ||
401 | * If a signal is not requested, then calls vblank_wait(). | ||
402 | */ | ||
403 | -int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv) | ||
404 | +int drm_wait_vblank(struct drm_device *dev, void *data, | ||
405 | + struct drm_file *file_priv) | ||
406 | { | ||
407 | union drm_wait_vblank *vblwait = data; | ||
408 | - struct timeval now; | ||
409 | int ret = 0; | ||
410 | - unsigned int flags, seq; | ||
411 | + unsigned int flags, seq, crtc; | ||
412 | |||
413 | if ((!dev->pdev->irq) || (!dev->irq_enabled)) | ||
414 | return -EINVAL; | ||
415 | @@ -255,13 +531,17 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr | ||
416 | } | ||
417 | |||
418 | flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; | ||
419 | + crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; | ||
420 | |||
421 | - if (!drm_core_check_feature(dev, (flags & _DRM_VBLANK_SECONDARY) ? | ||
422 | - DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL)) | ||
423 | + if (crtc >= dev->num_crtcs) | ||
424 | return -EINVAL; | ||
425 | |||
426 | - seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2 | ||
427 | - : &dev->vbl_received); | ||
428 | + ret = drm_vblank_get(dev, crtc); | ||
429 | + if (ret) { | ||
430 | + DRM_ERROR("failed to acquire vblank counter, %d\n", ret); | ||
431 | + return ret; | ||
432 | + } | ||
433 | + seq = drm_vblank_count(dev, crtc); | ||
434 | |||
435 | switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { | ||
436 | case _DRM_VBLANK_RELATIVE: | ||
437 | @@ -270,7 +550,8 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr | ||
438 | case _DRM_VBLANK_ABSOLUTE: | ||
439 | break; | ||
440 | default: | ||
441 | - return -EINVAL; | ||
442 | + ret = -EINVAL; | ||
443 | + goto done; | ||
444 | } | ||
445 | |||
446 | if ((flags & _DRM_VBLANK_NEXTONMISS) && | ||
447 | @@ -280,8 +561,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr | ||
448 | |||
449 | if (flags & _DRM_VBLANK_SIGNAL) { | ||
450 | unsigned long irqflags; | ||
451 | - struct list_head *vbl_sigs = (flags & _DRM_VBLANK_SECONDARY) | ||
452 | - ? &dev->vbl_sigs2 : &dev->vbl_sigs; | ||
453 | + struct list_head *vbl_sigs = &dev->vbl_sigs[crtc]; | ||
454 | struct drm_vbl_sig *vbl_sig; | ||
455 | |||
456 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | ||
457 | @@ -302,22 +582,29 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr | ||
458 | } | ||
459 | } | ||
460 | |||
461 | - if (dev->vbl_pending >= 100) { | ||
462 | + if (atomic_read(&dev->vbl_signal_pending) >= 100) { | ||
463 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
464 | - return -EBUSY; | ||
465 | + ret = -EBUSY; | ||
466 | + goto done; | ||
467 | } | ||
468 | |||
469 | - dev->vbl_pending++; | ||
470 | - | ||
471 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
472 | |||
473 | - if (! | ||
474 | - (vbl_sig = | ||
475 | - drm_alloc(sizeof(struct drm_vbl_sig), DRM_MEM_DRIVER))) { | ||
476 | - return -ENOMEM; | ||
477 | + vbl_sig = drm_calloc(1, sizeof(struct drm_vbl_sig), | ||
478 | + DRM_MEM_DRIVER); | ||
479 | + if (!vbl_sig) { | ||
480 | + ret = -ENOMEM; | ||
481 | + goto done; | ||
482 | + } | ||
483 | + | ||
484 | + ret = drm_vblank_get(dev, crtc); | ||
485 | + if (ret) { | ||
486 | + drm_free(vbl_sig, sizeof(struct drm_vbl_sig), | ||
487 | + DRM_MEM_DRIVER); | ||
488 | + return ret; | ||
489 | } | ||
490 | |||
491 | - memset((void *)vbl_sig, 0, sizeof(*vbl_sig)); | ||
492 | + atomic_inc(&dev->vbl_signal_pending); | ||
493 | |||
494 | vbl_sig->sequence = vblwait->request.sequence; | ||
495 | vbl_sig->info.si_signo = vblwait->request.signal; | ||
496 | @@ -331,20 +618,29 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr | ||
497 | |||
498 | vblwait->reply.sequence = seq; | ||
499 | } else { | ||
500 | - if (flags & _DRM_VBLANK_SECONDARY) { | ||
501 | - if (dev->driver->vblank_wait2) | ||
502 | - ret = dev->driver->vblank_wait2(dev, &vblwait->request.sequence); | ||
503 | - } else if (dev->driver->vblank_wait) | ||
504 | - ret = | ||
505 | - dev->driver->vblank_wait(dev, | ||
506 | - &vblwait->request.sequence); | ||
507 | - | ||
508 | - do_gettimeofday(&now); | ||
509 | - vblwait->reply.tval_sec = now.tv_sec; | ||
510 | - vblwait->reply.tval_usec = now.tv_usec; | ||
511 | + DRM_DEBUG("waiting on vblank count %d, crtc %d\n", | ||
512 | + vblwait->request.sequence, crtc); | ||
513 | + DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, | ||
514 | + ((drm_vblank_count(dev, crtc) | ||
515 | + - vblwait->request.sequence) <= (1 << 23))); | ||
516 | + | ||
517 | + if (ret != -EINTR) { | ||
518 | + struct timeval now; | ||
519 | + | ||
520 | + do_gettimeofday(&now); | ||
521 | + | ||
522 | + vblwait->reply.tval_sec = now.tv_sec; | ||
523 | + vblwait->reply.tval_usec = now.tv_usec; | ||
524 | + vblwait->reply.sequence = drm_vblank_count(dev, crtc); | ||
525 | + DRM_DEBUG("returning %d to client\n", | ||
526 | + vblwait->reply.sequence); | ||
527 | + } else { | ||
528 | + DRM_DEBUG("vblank wait interrupted by signal\n"); | ||
529 | + } | ||
530 | } | ||
531 | |||
532 | - done: | ||
533 | +done: | ||
534 | + drm_vblank_put(dev, crtc); | ||
535 | return ret; | ||
536 | } | ||
537 | |||
538 | @@ -352,44 +648,57 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr | ||
539 | * Send the VBLANK signals. | ||
540 | * | ||
541 | * \param dev DRM device. | ||
542 | + * \param crtc CRTC where the vblank event occurred | ||
543 | * | ||
544 | * Sends a signal for each task in drm_device::vbl_sigs and empties the list. | ||
545 | * | ||
546 | * If a signal is not requested, then calls vblank_wait(). | ||
547 | */ | ||
548 | -void drm_vbl_send_signals(struct drm_device * dev) | ||
549 | +static void drm_vbl_send_signals(struct drm_device *dev, int crtc) | ||
550 | { | ||
551 | + struct drm_vbl_sig *vbl_sig, *tmp; | ||
552 | + struct list_head *vbl_sigs; | ||
553 | + unsigned int vbl_seq; | ||
554 | unsigned long flags; | ||
555 | - int i; | ||
556 | |||
557 | spin_lock_irqsave(&dev->vbl_lock, flags); | ||
558 | |||
559 | - for (i = 0; i < 2; i++) { | ||
560 | - struct drm_vbl_sig *vbl_sig, *tmp; | ||
561 | - struct list_head *vbl_sigs = i ? &dev->vbl_sigs2 : &dev->vbl_sigs; | ||
562 | - unsigned int vbl_seq = atomic_read(i ? &dev->vbl_received2 : | ||
563 | - &dev->vbl_received); | ||
564 | + vbl_sigs = &dev->vbl_sigs[crtc]; | ||
565 | + vbl_seq = drm_vblank_count(dev, crtc); | ||
566 | |||
567 | - list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { | ||
568 | - if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { | ||
569 | - vbl_sig->info.si_code = vbl_seq; | ||
570 | - send_sig_info(vbl_sig->info.si_signo, | ||
571 | - &vbl_sig->info, vbl_sig->task); | ||
572 | + list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { | ||
573 | + if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { | ||
574 | + vbl_sig->info.si_code = vbl_seq; | ||
575 | + send_sig_info(vbl_sig->info.si_signo, | ||
576 | + &vbl_sig->info, vbl_sig->task); | ||
577 | |||
578 | - list_del(&vbl_sig->head); | ||
579 | - | ||
580 | - drm_free(vbl_sig, sizeof(*vbl_sig), | ||
581 | - DRM_MEM_DRIVER); | ||
582 | + list_del(&vbl_sig->head); | ||
583 | |||
584 | - dev->vbl_pending--; | ||
585 | - } | ||
586 | - } | ||
587 | + drm_free(vbl_sig, sizeof(*vbl_sig), | ||
588 | + DRM_MEM_DRIVER); | ||
589 | + atomic_dec(&dev->vbl_signal_pending); | ||
590 | + drm_vblank_put(dev, crtc); | ||
591 | + } | ||
592 | } | ||
593 | |||
594 | spin_unlock_irqrestore(&dev->vbl_lock, flags); | ||
595 | } | ||
596 | |||
597 | -EXPORT_SYMBOL(drm_vbl_send_signals); | ||
598 | +/** | ||
599 | + * drm_handle_vblank - handle a vblank event | ||
600 | + * @dev: DRM device | ||
601 | + * @crtc: where this event occurred | ||
602 | + * | ||
603 | + * Drivers should call this routine in their vblank interrupt handlers to | ||
604 | + * update the vblank counter and send any signals that may be pending. | ||
605 | + */ | ||
606 | +void drm_handle_vblank(struct drm_device *dev, int crtc) | ||
607 | +{ | ||
608 | + atomic_inc(&dev->_vblank_count[crtc]); | ||
609 | + DRM_WAKEUP(&dev->vbl_queue[crtc]); | ||
610 | + drm_vbl_send_signals(dev, crtc); | ||
611 | +} | ||
612 | +EXPORT_SYMBOL(drm_handle_vblank); | ||
613 | |||
614 | /** | ||
615 | * Tasklet wrapper function. | ||
616 | diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c | ||
617 | index cead62f..8609ec2 100644 | ||
618 | --- a/drivers/gpu/drm/i915/i915_dma.c | ||
619 | +++ b/drivers/gpu/drm/i915/i915_dma.c | ||
620 | @@ -673,7 +673,7 @@ static int i915_getparam(struct drm_device *dev, void *data, | ||
621 | |||
622 | switch (param->param) { | ||
623 | case I915_PARAM_IRQ_ACTIVE: | ||
624 | - value = dev->irq_enabled; | ||
625 | + value = dev->pdev->irq ? 1 : 0; | ||
626 | break; | ||
627 | case I915_PARAM_ALLOW_BATCHBUFFER: | ||
628 | value = dev_priv->allow_batchbuffer ? 1 : 0; | ||
629 | @@ -808,7 +808,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) | ||
630 | * and the registers being closely associated. | ||
631 | */ | ||
632 | if (!IS_I945G(dev) && !IS_I945GM(dev)) | ||
633 | - pci_enable_msi(dev->pdev); | ||
634 | + if (pci_enable_msi(dev->pdev)) | ||
635 | + DRM_ERROR("failed to enable MSI\n"); | ||
636 | |||
637 | intel_opregion_init(dev); | ||
638 | |||
639 | diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c | ||
640 | index eff66ed..37af03f 100644 | ||
641 | --- a/drivers/gpu/drm/i915/i915_drv.c | ||
642 | +++ b/drivers/gpu/drm/i915/i915_drv.c | ||
643 | @@ -85,10 +85,8 @@ static struct drm_driver driver = { | ||
644 | /* don't use mtrr's here, the Xserver or user space app should | ||
645 | * deal with them for intel hardware. | ||
646 | */ | ||
647 | - .driver_features = | ||
648 | - DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR |*/ | ||
649 | - DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL | | ||
650 | - DRIVER_IRQ_VBL2, | ||
651 | + .driver_features = DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | | ||
652 | + DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, | ||
653 | .load = i915_driver_load, | ||
654 | .unload = i915_driver_unload, | ||
655 | .lastclose = i915_driver_lastclose, | ||
656 | @@ -96,8 +94,9 @@ static struct drm_driver driver = { | ||
657 | .suspend = i915_suspend, | ||
658 | .resume = i915_resume, | ||
659 | .device_is_agp = i915_driver_device_is_agp, | ||
660 | - .vblank_wait = i915_driver_vblank_wait, | ||
661 | - .vblank_wait2 = i915_driver_vblank_wait2, | ||
662 | + .get_vblank_counter = i915_get_vblank_counter, | ||
663 | + .enable_vblank = i915_enable_vblank, | ||
664 | + .disable_vblank = i915_disable_vblank, | ||
665 | .irq_preinstall = i915_driver_irq_preinstall, | ||
666 | .irq_postinstall = i915_driver_irq_postinstall, | ||
667 | .irq_uninstall = i915_driver_irq_uninstall, | ||
668 | diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h | ||
669 | index 71326ca..d1a02be 100644 | ||
670 | --- a/drivers/gpu/drm/i915/i915_drv.h | ||
671 | +++ b/drivers/gpu/drm/i915/i915_drv.h | ||
672 | @@ -83,10 +83,15 @@ struct mem_block { | ||
673 | typedef struct _drm_i915_vbl_swap { | ||
674 | struct list_head head; | ||
675 | drm_drawable_t drw_id; | ||
676 | - unsigned int pipe; | ||
677 | + unsigned int plane; | ||
678 | unsigned int sequence; | ||
679 | } drm_i915_vbl_swap_t; | ||
680 | |||
681 | +struct opregion_header; | ||
682 | +struct opregion_acpi; | ||
683 | +struct opregion_swsci; | ||
684 | +struct opregion_asle; | ||
685 | + | ||
686 | struct intel_opregion { | ||
687 | struct opregion_header *header; | ||
688 | struct opregion_acpi *acpi; | ||
689 | @@ -105,7 +110,7 @@ typedef struct drm_i915_private { | ||
690 | drm_dma_handle_t *status_page_dmah; | ||
691 | void *hw_status_page; | ||
692 | dma_addr_t dma_status_page; | ||
693 | - unsigned long counter; | ||
694 | + uint32_t counter; | ||
695 | unsigned int status_gfx_addr; | ||
696 | drm_local_map_t hws_map; | ||
697 | |||
698 | @@ -247,16 +252,17 @@ extern int i915_irq_emit(struct drm_device *dev, void *data, | ||
699 | extern int i915_irq_wait(struct drm_device *dev, void *data, | ||
700 | struct drm_file *file_priv); | ||
701 | |||
702 | -extern int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence); | ||
703 | -extern int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence); | ||
704 | extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS); | ||
705 | extern void i915_driver_irq_preinstall(struct drm_device * dev); | ||
706 | -extern void i915_driver_irq_postinstall(struct drm_device * dev); | ||
707 | +extern int i915_driver_irq_postinstall(struct drm_device *dev); | ||
708 | extern void i915_driver_irq_uninstall(struct drm_device * dev); | ||
709 | extern int i915_vblank_pipe_set(struct drm_device *dev, void *data, | ||
710 | struct drm_file *file_priv); | ||
711 | extern int i915_vblank_pipe_get(struct drm_device *dev, void *data, | ||
712 | struct drm_file *file_priv); | ||
713 | +extern int i915_enable_vblank(struct drm_device *dev, int crtc); | ||
714 | +extern void i915_disable_vblank(struct drm_device *dev, int crtc); | ||
715 | +extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc); | ||
716 | extern int i915_vblank_swap(struct drm_device *dev, void *data, | ||
717 | struct drm_file *file_priv); | ||
718 | extern void i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask); | ||
719 | @@ -278,6 +284,10 @@ extern void i915_mem_release(struct drm_device * dev, | ||
720 | extern int i915_save_state(struct drm_device *dev); | ||
721 | extern int i915_restore_state(struct drm_device *dev); | ||
722 | |||
723 | +/* i915_suspend.c */ | ||
724 | +extern int i915_save_state(struct drm_device *dev); | ||
725 | +extern int i915_restore_state(struct drm_device *dev); | ||
726 | + | ||
727 | /* i915_opregion.c */ | ||
728 | extern int intel_opregion_init(struct drm_device *dev); | ||
729 | extern void intel_opregion_free(struct drm_device *dev); | ||
730 | diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c | ||
731 | index ae7d3a8..f875959 100644 | ||
732 | --- a/drivers/gpu/drm/i915/i915_irq.c | ||
733 | +++ b/drivers/gpu/drm/i915/i915_irq.c | ||
734 | @@ -35,9 +35,8 @@ | ||
735 | |||
736 | /** These are the interrupts used by the driver */ | ||
737 | #define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT | \ | ||
738 | - I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | \ | ||
739 | - I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT | \ | ||
740 | I915_ASLE_INTERRUPT | \ | ||
741 | + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ | ||
742 | I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) | ||
743 | |||
744 | void | ||
745 | @@ -61,6 +60,64 @@ i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask) | ||
746 | } | ||
747 | |||
748 | /** | ||
749 | + * i915_get_pipe - return the the pipe associated with a given plane | ||
750 | + * @dev: DRM device | ||
751 | + * @plane: plane to look for | ||
752 | + * | ||
753 | + * The Intel Mesa & 2D drivers call the vblank routines with a plane number | ||
754 | + * rather than a pipe number, since they may not always be equal. This routine | ||
755 | + * maps the given @plane back to a pipe number. | ||
756 | + */ | ||
757 | +static int | ||
758 | +i915_get_pipe(struct drm_device *dev, int plane) | ||
759 | +{ | ||
760 | + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | ||
761 | + u32 dspcntr; | ||
762 | + | ||
763 | + dspcntr = plane ? I915_READ(DSPBCNTR) : I915_READ(DSPACNTR); | ||
764 | + | ||
765 | + return dspcntr & DISPPLANE_SEL_PIPE_MASK ? 1 : 0; | ||
766 | +} | ||
767 | + | ||
768 | +/** | ||
769 | + * i915_get_plane - return the the plane associated with a given pipe | ||
770 | + * @dev: DRM device | ||
771 | + * @pipe: pipe to look for | ||
772 | + * | ||
773 | + * The Intel Mesa & 2D drivers call the vblank routines with a plane number | ||
774 | + * rather than a plane number, since they may not always be equal. This routine | ||
775 | + * maps the given @pipe back to a plane number. | ||
776 | + */ | ||
777 | +static int | ||
778 | +i915_get_plane(struct drm_device *dev, int pipe) | ||
779 | +{ | ||
780 | + if (i915_get_pipe(dev, 0) == pipe) | ||
781 | + return 0; | ||
782 | + return 1; | ||
783 | +} | ||
784 | + | ||
785 | +/** | ||
786 | + * i915_pipe_enabled - check if a pipe is enabled | ||
787 | + * @dev: DRM device | ||
788 | + * @pipe: pipe to check | ||
789 | + * | ||
790 | + * Reading certain registers when the pipe is disabled can hang the chip. | ||
791 | + * Use this routine to make sure the PLL is running and the pipe is active | ||
792 | + * before reading such registers if unsure. | ||
793 | + */ | ||
794 | +static int | ||
795 | +i915_pipe_enabled(struct drm_device *dev, int pipe) | ||
796 | +{ | ||
797 | + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | ||
798 | + unsigned long pipeconf = pipe ? PIPEBCONF : PIPEACONF; | ||
799 | + | ||
800 | + if (I915_READ(pipeconf) & PIPEACONF_ENABLE) | ||
801 | + return 1; | ||
802 | + | ||
803 | + return 0; | ||
804 | +} | ||
805 | + | ||
806 | +/** | ||
807 | * Emit blits for scheduled buffer swaps. | ||
808 | * | ||
809 | * This function will be called with the HW lock held. | ||
810 | @@ -71,8 +128,7 @@ static void i915_vblank_tasklet(struct drm_device *dev) | ||
811 | unsigned long irqflags; | ||
812 | struct list_head *list, *tmp, hits, *hit; | ||
813 | int nhits, nrects, slice[2], upper[2], lower[2], i; | ||
814 | - unsigned counter[2] = { atomic_read(&dev->vbl_received), | ||
815 | - atomic_read(&dev->vbl_received2) }; | ||
816 | + unsigned counter[2]; | ||
817 | struct drm_drawable_info *drw; | ||
818 | drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; | ||
819 | u32 cpp = dev_priv->cpp; | ||
820 | @@ -94,6 +150,9 @@ static void i915_vblank_tasklet(struct drm_device *dev) | ||
821 | src_pitch >>= 2; | ||
822 | } | ||
823 | |||
824 | + counter[0] = drm_vblank_count(dev, 0); | ||
825 | + counter[1] = drm_vblank_count(dev, 1); | ||
826 | + | ||
827 | DRM_DEBUG("\n"); | ||
828 | |||
829 | INIT_LIST_HEAD(&hits); | ||
830 | @@ -106,12 +165,14 @@ static void i915_vblank_tasklet(struct drm_device *dev) | ||
831 | list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { | ||
832 | drm_i915_vbl_swap_t *vbl_swap = | ||
833 | list_entry(list, drm_i915_vbl_swap_t, head); | ||
834 | + int pipe = i915_get_pipe(dev, vbl_swap->plane); | ||
835 | |||
836 | - if ((counter[vbl_swap->pipe] - vbl_swap->sequence) > (1<<23)) | ||
837 | + if ((counter[pipe] - vbl_swap->sequence) > (1<<23)) | ||
838 | continue; | ||
839 | |||
840 | list_del(list); | ||
841 | dev_priv->swaps_pending--; | ||
842 | + drm_vblank_put(dev, pipe); | ||
843 | |||
844 | spin_unlock(&dev_priv->swaps_lock); | ||
845 | spin_lock(&dev->drw_lock); | ||
846 | @@ -204,7 +265,7 @@ static void i915_vblank_tasklet(struct drm_device *dev) | ||
847 | drm_i915_vbl_swap_t *swap_hit = | ||
848 | list_entry(hit, drm_i915_vbl_swap_t, head); | ||
849 | struct drm_clip_rect *rect; | ||
850 | - int num_rects, pipe; | ||
851 | + int num_rects, plane; | ||
852 | unsigned short top, bottom; | ||
853 | |||
854 | drw = drm_get_drawable_info(dev, swap_hit->drw_id); | ||
855 | @@ -213,9 +274,9 @@ static void i915_vblank_tasklet(struct drm_device *dev) | ||
856 | continue; | ||
857 | |||
858 | rect = drw->rects; | ||
859 | - pipe = swap_hit->pipe; | ||
860 | - top = upper[pipe]; | ||
861 | - bottom = lower[pipe]; | ||
862 | + plane = swap_hit->plane; | ||
863 | + top = upper[plane]; | ||
864 | + bottom = lower[plane]; | ||
865 | |||
866 | for (num_rects = drw->num_rects; num_rects--; rect++) { | ||
867 | int y1 = max(rect->y1, top); | ||
868 | @@ -252,22 +313,54 @@ static void i915_vblank_tasklet(struct drm_device *dev) | ||
869 | } | ||
870 | } | ||
871 | |||
872 | +u32 i915_get_vblank_counter(struct drm_device *dev, int plane) | ||
873 | +{ | ||
874 | + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | ||
875 | + unsigned long high_frame; | ||
876 | + unsigned long low_frame; | ||
877 | + u32 high1, high2, low, count; | ||
878 | + int pipe; | ||
879 | + | ||
880 | + pipe = i915_get_pipe(dev, plane); | ||
881 | + high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH; | ||
882 | + low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; | ||
883 | + | ||
884 | + if (!i915_pipe_enabled(dev, pipe)) { | ||
885 | + DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe); | ||
886 | + return 0; | ||
887 | + } | ||
888 | + | ||
889 | + /* | ||
890 | + * High & low register fields aren't synchronized, so make sure | ||
891 | + * we get a low value that's stable across two reads of the high | ||
892 | + * register. | ||
893 | + */ | ||
894 | + do { | ||
895 | + high1 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> | ||
896 | + PIPE_FRAME_HIGH_SHIFT); | ||
897 | + low = ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >> | ||
898 | + PIPE_FRAME_LOW_SHIFT); | ||
899 | + high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> | ||
900 | + PIPE_FRAME_HIGH_SHIFT); | ||
901 | + } while (high1 != high2); | ||
902 | + | ||
903 | + count = (high1 << 8) | low; | ||
904 | + | ||
905 | + return count; | ||
906 | +} | ||
907 | + | ||
908 | irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | ||
909 | { | ||
910 | struct drm_device *dev = (struct drm_device *) arg; | ||
911 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | ||
912 | - u32 pipea_stats, pipeb_stats; | ||
913 | u32 iir; | ||
914 | - | ||
915 | - pipea_stats = I915_READ(PIPEASTAT); | ||
916 | - pipeb_stats = I915_READ(PIPEBSTAT); | ||
917 | + u32 pipea_stats, pipeb_stats; | ||
918 | + int vblank = 0; | ||
919 | |||
920 | if (dev->pdev->msi_enabled) | ||
921 | I915_WRITE(IMR, ~0); | ||
922 | iir = I915_READ(IIR); | ||
923 | |||
924 | - DRM_DEBUG("iir=%08x\n", iir); | ||
925 | - | ||
926 | if (iir == 0) { | ||
927 | if (dev->pdev->msi_enabled) { | ||
928 | I915_WRITE(IMR, dev_priv->irq_mask_reg); | ||
929 | @@ -276,48 +369,56 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | ||
930 | return IRQ_NONE; | ||
931 | } | ||
932 | |||
933 | - I915_WRITE(PIPEASTAT, pipea_stats); | ||
934 | - I915_WRITE(PIPEBSTAT, pipeb_stats); | ||
935 | - | ||
936 | - I915_WRITE(IIR, iir); | ||
937 | - if (dev->pdev->msi_enabled) | ||
938 | - I915_WRITE(IMR, dev_priv->irq_mask_reg); | ||
939 | - (void) I915_READ(IIR); /* Flush posted writes */ | ||
940 | - | ||
941 | - dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); | ||
942 | - | ||
943 | - if (iir & I915_USER_INTERRUPT) | ||
944 | - DRM_WAKEUP(&dev_priv->irq_queue); | ||
945 | - | ||
946 | - if (iir & (I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | | ||
947 | - I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)) { | ||
948 | - int vblank_pipe = dev_priv->vblank_pipe; | ||
949 | - | ||
950 | - if ((vblank_pipe & | ||
951 | - (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) | ||
952 | - == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) { | ||
953 | - if (iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) | ||
954 | - atomic_inc(&dev->vbl_received); | ||
955 | - if (iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) | ||
956 | - atomic_inc(&dev->vbl_received2); | ||
957 | - } else if (((iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) && | ||
958 | - (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) || | ||
959 | - ((iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) && | ||
960 | - (vblank_pipe & DRM_I915_VBLANK_PIPE_B))) | ||
961 | - atomic_inc(&dev->vbl_received); | ||
962 | + /* | ||
963 | + * Clear the PIPE(A|B)STAT regs before the IIR otherwise | ||
964 | + * we may get extra interrupts. | ||
965 | + */ | ||
966 | + if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { | ||
967 | + pipea_stats = I915_READ(PIPEASTAT); | ||
968 | + if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)) | ||
969 | + pipea_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | | ||
970 | + PIPE_VBLANK_INTERRUPT_ENABLE); | ||
971 | + else if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| | ||
972 | + PIPE_VBLANK_INTERRUPT_STATUS)) { | ||
973 | + vblank++; | ||
974 | + drm_handle_vblank(dev, i915_get_plane(dev, 0)); | ||
975 | + } | ||
976 | |||
977 | - DRM_WAKEUP(&dev->vbl_queue); | ||
978 | - drm_vbl_send_signals(dev); | ||
979 | + I915_WRITE(PIPEASTAT, pipea_stats); | ||
980 | + } | ||
981 | + if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { | ||
982 | + pipeb_stats = I915_READ(PIPEBSTAT); | ||
983 | + /* Ack the event */ | ||
984 | + I915_WRITE(PIPEBSTAT, pipeb_stats); | ||
985 | + | ||
986 | + /* The vblank interrupt gets enabled even if we didn't ask for | ||
987 | + it, so make sure it's shut down again */ | ||
988 | + if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)) | ||
989 | + pipeb_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | | ||
990 | + PIPE_VBLANK_INTERRUPT_ENABLE); | ||
991 | + else if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| | ||
992 | + PIPE_VBLANK_INTERRUPT_STATUS)) { | ||
993 | + vblank++; | ||
994 | + drm_handle_vblank(dev, i915_get_plane(dev, 1)); | ||
995 | + } | ||
996 | |||
997 | - if (dev_priv->swaps_pending > 0) | ||
998 | - drm_locked_tasklet(dev, i915_vblank_tasklet); | ||
999 | + if (pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) | ||
1000 | + opregion_asle_intr(dev); | ||
1001 | + I915_WRITE(PIPEBSTAT, pipeb_stats); | ||
1002 | } | ||
1003 | |||
1004 | if (iir & I915_ASLE_INTERRUPT) | ||
1005 | opregion_asle_intr(dev); | ||
1006 | |||
1007 | - if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) | ||
1008 | - opregion_asle_intr(dev); | ||
1009 | + dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); | ||
1010 | + | ||
1011 | + if (dev->pdev->msi_enabled) | ||
1012 | + I915_WRITE(IMR, dev_priv->irq_mask_reg); | ||
1013 | + I915_WRITE(IIR, iir); | ||
1014 | + (void) I915_READ(IIR); | ||
1015 | + | ||
1016 | + if (vblank && dev_priv->swaps_pending > 0) | ||
1017 | + drm_locked_tasklet(dev, i915_vblank_tasklet); | ||
1018 | |||
1019 | return IRQ_HANDLED; | ||
1020 | } | ||
1021 | @@ -358,7 +459,7 @@ static void i915_user_irq_get(struct drm_device *dev) | ||
1022 | spin_unlock(&dev_priv->user_irq_lock); | ||
1023 | } | ||
1024 | |||
1025 | -static void i915_user_irq_put(struct drm_device *dev) | ||
1026 | +void i915_user_irq_put(struct drm_device *dev) | ||
1027 | { | ||
1028 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | ||
1029 | |||
1030 | @@ -395,41 +496,10 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr) | ||
1031 | } | ||
1032 | |||
1033 | dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); | ||
1034 | - return ret; | ||
1035 | -} | ||
1036 | - | ||
1037 | -static int i915_driver_vblank_do_wait(struct drm_device *dev, unsigned int *sequence, | ||
1038 | - atomic_t *counter) | ||
1039 | -{ | ||
1040 | - drm_i915_private_t *dev_priv = dev->dev_private; | ||
1041 | - unsigned int cur_vblank; | ||
1042 | - int ret = 0; | ||
1043 | - | ||
1044 | - if (!dev_priv) { | ||
1045 | - DRM_ERROR("called with no initialization\n"); | ||
1046 | - return -EINVAL; | ||
1047 | - } | ||
1048 | - | ||
1049 | - DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, | ||
1050 | - (((cur_vblank = atomic_read(counter)) | ||
1051 | - - *sequence) <= (1<<23))); | ||
1052 | - | ||
1053 | - *sequence = cur_vblank; | ||
1054 | |||
1055 | return ret; | ||
1056 | } | ||
1057 | |||
1058 | - | ||
1059 | -int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence) | ||
1060 | -{ | ||
1061 | - return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received); | ||
1062 | -} | ||
1063 | - | ||
1064 | -int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence) | ||
1065 | -{ | ||
1066 | - return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received2); | ||
1067 | -} | ||
1068 | - | ||
1069 | /* Needs the lock as it touches the ring. | ||
1070 | */ | ||
1071 | int i915_irq_emit(struct drm_device *dev, void *data, | ||
1072 | @@ -472,40 +542,88 @@ int i915_irq_wait(struct drm_device *dev, void *data, | ||
1073 | return i915_wait_irq(dev, irqwait->irq_seq); | ||
1074 | } | ||
1075 | |||
1076 | +int i915_enable_vblank(struct drm_device *dev, int plane) | ||
1077 | +{ | ||
1078 | + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | ||
1079 | + int pipe = i915_get_pipe(dev, plane); | ||
1080 | + u32 pipestat_reg = 0; | ||
1081 | + u32 pipestat; | ||
1082 | + | ||
1083 | + switch (pipe) { | ||
1084 | + case 0: | ||
1085 | + pipestat_reg = PIPEASTAT; | ||
1086 | + i915_enable_irq(dev_priv, I915_DISPLAY_PIPE_A_EVENT_INTERRUPT); | ||
1087 | + break; | ||
1088 | + case 1: | ||
1089 | + pipestat_reg = PIPEBSTAT; | ||
1090 | + i915_enable_irq(dev_priv, I915_DISPLAY_PIPE_B_EVENT_INTERRUPT); | ||
1091 | + break; | ||
1092 | + default: | ||
1093 | + DRM_ERROR("tried to enable vblank on non-existent pipe %d\n", | ||
1094 | + pipe); | ||
1095 | + break; | ||
1096 | + } | ||
1097 | + | ||
1098 | + if (pipestat_reg) { | ||
1099 | + pipestat = I915_READ(pipestat_reg); | ||
1100 | + if (IS_I965G(dev)) | ||
1101 | + pipestat |= PIPE_START_VBLANK_INTERRUPT_ENABLE; | ||
1102 | + else | ||
1103 | + pipestat |= PIPE_VBLANK_INTERRUPT_ENABLE; | ||
1104 | + /* Clear any stale interrupt status */ | ||
1105 | + pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | | ||
1106 | + PIPE_VBLANK_INTERRUPT_STATUS); | ||
1107 | + I915_WRITE(pipestat_reg, pipestat); | ||
1108 | + } | ||
1109 | + | ||
1110 | + return 0; | ||
1111 | +} | ||
1112 | + | ||
1113 | +void i915_disable_vblank(struct drm_device *dev, int plane) | ||
1114 | +{ | ||
1115 | + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | ||
1116 | + int pipe = i915_get_pipe(dev, plane); | ||
1117 | + u32 pipestat_reg = 0; | ||
1118 | + u32 pipestat; | ||
1119 | + | ||
1120 | + switch (pipe) { | ||
1121 | + case 0: | ||
1122 | + pipestat_reg = PIPEASTAT; | ||
1123 | + i915_disable_irq(dev_priv, I915_DISPLAY_PIPE_A_EVENT_INTERRUPT); | ||
1124 | + break; | ||
1125 | + case 1: | ||
1126 | + pipestat_reg = PIPEBSTAT; | ||
1127 | + i915_disable_irq(dev_priv, I915_DISPLAY_PIPE_B_EVENT_INTERRUPT); | ||
1128 | + break; | ||
1129 | + default: | ||
1130 | + DRM_ERROR("tried to disable vblank on non-existent pipe %d\n", | ||
1131 | + pipe); | ||
1132 | + break; | ||
1133 | + } | ||
1134 | + | ||
1135 | + if (pipestat_reg) { | ||
1136 | + pipestat = I915_READ(pipestat_reg); | ||
1137 | + pipestat &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | | ||
1138 | + PIPE_VBLANK_INTERRUPT_ENABLE); | ||
1139 | + /* Clear any stale interrupt status */ | ||
1140 | + pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | | ||
1141 | + PIPE_VBLANK_INTERRUPT_STATUS); | ||
1142 | + I915_WRITE(pipestat_reg, pipestat); | ||
1143 | + } | ||
1144 | +} | ||
1145 | + | ||
1146 | /* Set the vblank monitor pipe | ||
1147 | */ | ||
1148 | int i915_vblank_pipe_set(struct drm_device *dev, void *data, | ||
1149 | struct drm_file *file_priv) | ||
1150 | { | ||
1151 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
1152 | - drm_i915_vblank_pipe_t *pipe = data; | ||
1153 | - u32 enable_mask = 0, disable_mask = 0; | ||
1154 | |||
1155 | if (!dev_priv) { | ||
1156 | DRM_ERROR("called with no initialization\n"); | ||
1157 | return -EINVAL; | ||
1158 | } | ||
1159 | |||
1160 | - if (pipe->pipe & ~(DRM_I915_VBLANK_PIPE_A|DRM_I915_VBLANK_PIPE_B)) { | ||
1161 | - DRM_ERROR("called with invalid pipe 0x%x\n", pipe->pipe); | ||
1162 | - return -EINVAL; | ||
1163 | - } | ||
1164 | - | ||
1165 | - if (pipe->pipe & DRM_I915_VBLANK_PIPE_A) | ||
1166 | - enable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; | ||
1167 | - else | ||
1168 | - disable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; | ||
1169 | - | ||
1170 | - if (pipe->pipe & DRM_I915_VBLANK_PIPE_B) | ||
1171 | - enable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; | ||
1172 | - else | ||
1173 | - disable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; | ||
1174 | - | ||
1175 | - i915_enable_irq(dev_priv, enable_mask); | ||
1176 | - i915_disable_irq(dev_priv, disable_mask); | ||
1177 | - | ||
1178 | - dev_priv->vblank_pipe = pipe->pipe; | ||
1179 | - | ||
1180 | return 0; | ||
1181 | } | ||
1182 | |||
1183 | @@ -514,19 +632,13 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data, | ||
1184 | { | ||
1185 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
1186 | drm_i915_vblank_pipe_t *pipe = data; | ||
1187 | - u16 flag; | ||
1188 | |||
1189 | if (!dev_priv) { | ||
1190 | DRM_ERROR("called with no initialization\n"); | ||
1191 | return -EINVAL; | ||
1192 | } | ||
1193 | |||
1194 | - flag = I915_READ(IMR); | ||
1195 | - pipe->pipe = 0; | ||
1196 | - if (flag & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) | ||
1197 | - pipe->pipe |= DRM_I915_VBLANK_PIPE_A; | ||
1198 | - if (flag & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) | ||
1199 | - pipe->pipe |= DRM_I915_VBLANK_PIPE_B; | ||
1200 | + pipe->pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; | ||
1201 | |||
1202 | return 0; | ||
1203 | } | ||
1204 | @@ -540,9 +652,10 @@ int i915_vblank_swap(struct drm_device *dev, void *data, | ||
1205 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
1206 | drm_i915_vblank_swap_t *swap = data; | ||
1207 | drm_i915_vbl_swap_t *vbl_swap; | ||
1208 | - unsigned int pipe, seqtype, curseq; | ||
1209 | + unsigned int pipe, seqtype, curseq, plane; | ||
1210 | unsigned long irqflags; | ||
1211 | struct list_head *list; | ||
1212 | + int ret; | ||
1213 | |||
1214 | if (!dev_priv) { | ||
1215 | DRM_ERROR("%s called with no initialization\n", __func__); | ||
1216 | @@ -560,7 +673,8 @@ int i915_vblank_swap(struct drm_device *dev, void *data, | ||
1217 | return -EINVAL; | ||
1218 | } | ||
1219 | |||
1220 | - pipe = (swap->seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0; | ||
1221 | + plane = (swap->seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0; | ||
1222 | + pipe = i915_get_pipe(dev, plane); | ||
1223 | |||
1224 | seqtype = swap->seqtype & (_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE); | ||
1225 | |||
1226 | @@ -579,7 +693,14 @@ int i915_vblank_swap(struct drm_device *dev, void *data, | ||
1227 | |||
1228 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); | ||
1229 | |||
1230 | - curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received); | ||
1231 | + /* | ||
1232 | + * We take the ref here and put it when the swap actually completes | ||
1233 | + * in the tasklet. | ||
1234 | + */ | ||
1235 | + ret = drm_vblank_get(dev, pipe); | ||
1236 | + if (ret) | ||
1237 | + return ret; | ||
1238 | + curseq = drm_vblank_count(dev, pipe); | ||
1239 | |||
1240 | if (seqtype == _DRM_VBLANK_RELATIVE) | ||
1241 | swap->sequence += curseq; | ||
1242 | @@ -589,6 +710,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data, | ||
1243 | swap->sequence = curseq + 1; | ||
1244 | } else { | ||
1245 | DRM_DEBUG("Missed target sequence\n"); | ||
1246 | + drm_vblank_put(dev, pipe); | ||
1247 | return -EINVAL; | ||
1248 | } | ||
1249 | } | ||
1250 | @@ -599,7 +721,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data, | ||
1251 | vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head); | ||
1252 | |||
1253 | if (vbl_swap->drw_id == swap->drawable && | ||
1254 | - vbl_swap->pipe == pipe && | ||
1255 | + vbl_swap->plane == plane && | ||
1256 | vbl_swap->sequence == swap->sequence) { | ||
1257 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | ||
1258 | DRM_DEBUG("Already scheduled\n"); | ||
1259 | @@ -611,6 +733,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data, | ||
1260 | |||
1261 | if (dev_priv->swaps_pending >= 100) { | ||
1262 | DRM_DEBUG("Too many swaps queued\n"); | ||
1263 | + drm_vblank_put(dev, pipe); | ||
1264 | return -EBUSY; | ||
1265 | } | ||
1266 | |||
1267 | @@ -618,13 +741,14 @@ int i915_vblank_swap(struct drm_device *dev, void *data, | ||
1268 | |||
1269 | if (!vbl_swap) { | ||
1270 | DRM_ERROR("Failed to allocate memory to queue swap\n"); | ||
1271 | + drm_vblank_put(dev, pipe); | ||
1272 | return -ENOMEM; | ||
1273 | } | ||
1274 | |||
1275 | DRM_DEBUG("\n"); | ||
1276 | |||
1277 | vbl_swap->drw_id = swap->drawable; | ||
1278 | - vbl_swap->pipe = pipe; | ||
1279 | + vbl_swap->plane = plane; | ||
1280 | vbl_swap->sequence = swap->sequence; | ||
1281 | |||
1282 | spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); | ||
1283 | @@ -643,28 +767,32 @@ void i915_driver_irq_preinstall(struct drm_device * dev) | ||
1284 | { | ||
1285 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | ||
1286 | |||
1287 | - I915_WRITE(HWSTAM, 0xfffe); | ||
1288 | - I915_WRITE(IMR, 0x0); | ||
1289 | + I915_WRITE(HWSTAM, 0xeffe); | ||
1290 | + I915_WRITE(IMR, 0xffffffff); | ||
1291 | I915_WRITE(IER, 0x0); | ||
1292 | } | ||
1293 | |||
1294 | -void i915_driver_irq_postinstall(struct drm_device * dev) | ||
1295 | +int i915_driver_irq_postinstall(struct drm_device *dev) | ||
1296 | { | ||
1297 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | ||
1298 | + int ret, num_pipes = 2; | ||
1299 | |||
1300 | spin_lock_init(&dev_priv->swaps_lock); | ||
1301 | INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); | ||
1302 | dev_priv->swaps_pending = 0; | ||
1303 | |||
1304 | - if (!dev_priv->vblank_pipe) | ||
1305 | - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A; | ||
1306 | - | ||
1307 | /* Set initial unmasked IRQs to just the selected vblank pipes. */ | ||
1308 | dev_priv->irq_mask_reg = ~0; | ||
1309 | - if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A) | ||
1310 | - dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; | ||
1311 | - if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B) | ||
1312 | - dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; | ||
1313 | + | ||
1314 | + ret = drm_vblank_init(dev, num_pipes); | ||
1315 | + if (ret) | ||
1316 | + return ret; | ||
1317 | + | ||
1318 | + dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; | ||
1319 | + dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; | ||
1320 | + dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; | ||
1321 | + | ||
1322 | + dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ | ||
1323 | |||
1324 | dev_priv->irq_mask_reg &= I915_INTERRUPT_ENABLE_MASK; | ||
1325 | |||
1326 | @@ -673,22 +801,29 @@ void i915_driver_irq_postinstall(struct drm_device * dev) | ||
1327 | (void) I915_READ(IER); | ||
1328 | |||
1329 | opregion_enable_asle(dev); | ||
1330 | - | ||
1331 | DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); | ||
1332 | + | ||
1333 | + return 0; | ||
1334 | } | ||
1335 | |||
1336 | void i915_driver_irq_uninstall(struct drm_device * dev) | ||
1337 | { | ||
1338 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | ||
1339 | - u16 temp; | ||
1340 | + u32 temp; | ||
1341 | |||
1342 | if (!dev_priv) | ||
1343 | return; | ||
1344 | |||
1345 | - I915_WRITE(HWSTAM, 0xffff); | ||
1346 | - I915_WRITE(IMR, 0xffff); | ||
1347 | + dev_priv->vblank_pipe = 0; | ||
1348 | + | ||
1349 | + I915_WRITE(HWSTAM, 0xffffffff); | ||
1350 | + I915_WRITE(IMR, 0xffffffff); | ||
1351 | I915_WRITE(IER, 0x0); | ||
1352 | |||
1353 | + temp = I915_READ(PIPEASTAT); | ||
1354 | + I915_WRITE(PIPEASTAT, temp); | ||
1355 | + temp = I915_READ(PIPEBSTAT); | ||
1356 | + I915_WRITE(PIPEBSTAT, temp); | ||
1357 | temp = I915_READ(IIR); | ||
1358 | I915_WRITE(IIR, temp); | ||
1359 | } | ||
1360 | diff --git a/include/drm/drm.h b/include/drm/drm.h | ||
1361 | index 0864c69..15e5503 100644 | ||
1362 | --- a/include/drm/drm.h | ||
1363 | +++ b/include/drm/drm.h | ||
1364 | @@ -454,6 +454,7 @@ struct drm_irq_busid { | ||
1365 | enum drm_vblank_seq_type { | ||
1366 | _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ | ||
1367 | _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ | ||
1368 | + _DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */ | ||
1369 | _DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ | ||
1370 | _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ | ||
1371 | _DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking */ | ||
1372 | @@ -486,6 +487,19 @@ union drm_wait_vblank { | ||
1373 | struct drm_wait_vblank_reply reply; | ||
1374 | }; | ||
1375 | |||
1376 | +#define _DRM_PRE_MODESET 1 | ||
1377 | +#define _DRM_POST_MODESET 2 | ||
1378 | + | ||
1379 | +/** | ||
1380 | + * DRM_IOCTL_MODESET_CTL ioctl argument type | ||
1381 | + * | ||
1382 | + * \sa drmModesetCtl(). | ||
1383 | + */ | ||
1384 | +struct drm_modeset_ctl { | ||
1385 | + uint32_t crtc; | ||
1386 | + uint32_t cmd; | ||
1387 | +}; | ||
1388 | + | ||
1389 | /** | ||
1390 | * DRM_IOCTL_AGP_ENABLE ioctl argument type. | ||
1391 | * | ||
1392 | @@ -570,6 +584,7 @@ struct drm_set_version { | ||
1393 | #define DRM_IOCTL_GET_CLIENT DRM_IOWR(0x05, struct drm_client) | ||
1394 | #define DRM_IOCTL_GET_STATS DRM_IOR( 0x06, struct drm_stats) | ||
1395 | #define DRM_IOCTL_SET_VERSION DRM_IOWR(0x07, struct drm_set_version) | ||
1396 | +#define DRM_IOCTL_MODESET_CTL DRM_IOW(0x08, struct drm_modeset_ctl) | ||
1397 | |||
1398 | #define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, struct drm_unique) | ||
1399 | #define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, struct drm_auth) | ||
1400 | diff --git a/include/drm/drmP.h b/include/drm/drmP.h | ||
1401 | index 1c1b13e..e79ce07 100644 | ||
1402 | --- a/include/drm/drmP.h | ||
1403 | +++ b/include/drm/drmP.h | ||
1404 | @@ -580,11 +580,54 @@ struct drm_driver { | ||
1405 | int (*kernel_context_switch) (struct drm_device *dev, int old, | ||
1406 | int new); | ||
1407 | void (*kernel_context_switch_unlock) (struct drm_device *dev); | ||
1408 | - int (*vblank_wait) (struct drm_device *dev, unsigned int *sequence); | ||
1409 | - int (*vblank_wait2) (struct drm_device *dev, unsigned int *sequence); | ||
1410 | int (*dri_library_name) (struct drm_device *dev, char *buf); | ||
1411 | |||
1412 | /** | ||
1413 | + * get_vblank_counter - get raw hardware vblank counter | ||
1414 | + * @dev: DRM device | ||
1415 | + * @crtc: counter to fetch | ||
1416 | + * | ||
1417 | + * Driver callback for fetching a raw hardware vblank counter | ||
1418 | + * for @crtc. If a device doesn't have a hardware counter, the | ||
1419 | + * driver can simply return the value of drm_vblank_count and | ||
1420 | + * make the enable_vblank() and disable_vblank() hooks into no-ops, | ||
1421 | + * leaving interrupts enabled at all times. | ||
1422 | + * | ||
1423 | + * Wraparound handling and loss of events due to modesetting is dealt | ||
1424 | + * with in the DRM core code. | ||
1425 | + * | ||
1426 | + * RETURNS | ||
1427 | + * Raw vblank counter value. | ||
1428 | + */ | ||
1429 | + u32 (*get_vblank_counter) (struct drm_device *dev, int crtc); | ||
1430 | + | ||
1431 | + /** | ||
1432 | + * enable_vblank - enable vblank interrupt events | ||
1433 | + * @dev: DRM device | ||
1434 | + * @crtc: which irq to enable | ||
1435 | + * | ||
1436 | + * Enable vblank interrupts for @crtc. If the device doesn't have | ||
1437 | + * a hardware vblank counter, this routine should be a no-op, since | ||
1438 | + * interrupts will have to stay on to keep the count accurate. | ||
1439 | + * | ||
1440 | + * RETURNS | ||
1441 | + * Zero on success, appropriate errno if the given @crtc's vblank | ||
1442 | + * interrupt cannot be enabled. | ||
1443 | + */ | ||
1444 | + int (*enable_vblank) (struct drm_device *dev, int crtc); | ||
1445 | + | ||
1446 | + /** | ||
1447 | + * disable_vblank - disable vblank interrupt events | ||
1448 | + * @dev: DRM device | ||
1449 | + * @crtc: which irq to enable | ||
1450 | + * | ||
1451 | + * Disable vblank interrupts for @crtc. If the device doesn't have | ||
1452 | + * a hardware vblank counter, this routine should be a no-op, since | ||
1453 | + * interrupts will have to stay on to keep the count accurate. | ||
1454 | + */ | ||
1455 | + void (*disable_vblank) (struct drm_device *dev, int crtc); | ||
1456 | + | ||
1457 | + /** | ||
1458 | * Called by \c drm_device_is_agp. Typically used to determine if a | ||
1459 | * card is really attached to AGP or not. | ||
1460 | * | ||
1461 | @@ -601,7 +644,7 @@ struct drm_driver { | ||
1462 | |||
1463 | irqreturn_t(*irq_handler) (DRM_IRQ_ARGS); | ||
1464 | void (*irq_preinstall) (struct drm_device *dev); | ||
1465 | - void (*irq_postinstall) (struct drm_device *dev); | ||
1466 | + int (*irq_postinstall) (struct drm_device *dev); | ||
1467 | void (*irq_uninstall) (struct drm_device *dev); | ||
1468 | void (*reclaim_buffers) (struct drm_device *dev, | ||
1469 | struct drm_file * file_priv); | ||
1470 | @@ -730,13 +773,28 @@ struct drm_device { | ||
1471 | /** \name VBLANK IRQ support */ | ||
1472 | /*@{ */ | ||
1473 | |||
1474 | - wait_queue_head_t vbl_queue; /**< VBLANK wait queue */ | ||
1475 | - atomic_t vbl_received; | ||
1476 | - atomic_t vbl_received2; /**< number of secondary VBLANK interrupts */ | ||
1477 | + /* | ||
1478 | + * At load time, disabling the vblank interrupt won't be allowed since | ||
1479 | + * old clients may not call the modeset ioctl and therefore misbehave. | ||
1480 | + * Once the modeset ioctl *has* been called though, we can safely | ||
1481 | + * disable them when unused. | ||
1482 | + */ | ||
1483 | + int vblank_disable_allowed; | ||
1484 | + | ||
1485 | + wait_queue_head_t *vbl_queue; /**< VBLANK wait queue */ | ||
1486 | + atomic_t *_vblank_count; /**< number of VBLANK interrupts (driver must alloc the right number of counters) */ | ||
1487 | spinlock_t vbl_lock; | ||
1488 | - struct list_head vbl_sigs; /**< signal list to send on VBLANK */ | ||
1489 | - struct list_head vbl_sigs2; /**< signals to send on secondary VBLANK */ | ||
1490 | - unsigned int vbl_pending; | ||
1491 | + struct list_head *vbl_sigs; /**< signal list to send on VBLANK */ | ||
1492 | + atomic_t vbl_signal_pending; /* number of signals pending on all crtcs*/ | ||
1493 | + atomic_t *vblank_refcount; /* number of users of vblank interruptsper crtc */ | ||
1494 | + u32 *last_vblank; /* protected by dev->vbl_lock, used */ | ||
1495 | + /* for wraparound handling */ | ||
1496 | + int *vblank_enabled; /* so we don't call enable more than | ||
1497 | + once per disable */ | ||
1498 | + int *vblank_inmodeset; /* Display driver is setting mode */ | ||
1499 | + struct timer_list vblank_disable_timer; | ||
1500 | + | ||
1501 | + u32 max_vblank_count; /**< size of vblank counter register */ | ||
1502 | spinlock_t tasklet_lock; /**< For drm_locked_tasklet */ | ||
1503 | void (*locked_tasklet_func)(struct drm_device *dev); | ||
1504 | |||
1505 | @@ -757,6 +815,7 @@ struct drm_device { | ||
1506 | struct pci_controller *hose; | ||
1507 | #endif | ||
1508 | struct drm_sg_mem *sg; /**< Scatter gather memory */ | ||
1509 | + int num_crtcs; /**< Number of CRTCs on this device */ | ||
1510 | void *dev_private; /**< device private data */ | ||
1511 | struct drm_sigdata sigdata; /**< For block_all_signals */ | ||
1512 | sigset_t sigmask; | ||
1513 | @@ -990,10 +1049,19 @@ extern void drm_driver_irq_preinstall(struct drm_device *dev); | ||
1514 | extern void drm_driver_irq_postinstall(struct drm_device *dev); | ||
1515 | extern void drm_driver_irq_uninstall(struct drm_device *dev); | ||
1516 | |||
1517 | +extern int drm_vblank_init(struct drm_device *dev, int num_crtcs); | ||
1518 | extern int drm_wait_vblank(struct drm_device *dev, void *data, | ||
1519 | - struct drm_file *file_priv); | ||
1520 | + struct drm_file *filp); | ||
1521 | extern int drm_vblank_wait(struct drm_device *dev, unsigned int *vbl_seq); | ||
1522 | -extern void drm_vbl_send_signals(struct drm_device *dev); | ||
1523 | +extern void drm_locked_tasklet(struct drm_device *dev, | ||
1524 | + void(*func)(struct drm_device *)); | ||
1525 | +extern u32 drm_vblank_count(struct drm_device *dev, int crtc); | ||
1526 | +extern void drm_handle_vblank(struct drm_device *dev, int crtc); | ||
1527 | +extern int drm_vblank_get(struct drm_device *dev, int crtc); | ||
1528 | +extern void drm_vblank_put(struct drm_device *dev, int crtc); | ||
1529 | +/* Modesetting support */ | ||
1530 | +extern int drm_modeset_ctl(struct drm_device *dev, void *data, | ||
1531 | + struct drm_file *file_priv); | ||
1532 | extern void drm_locked_tasklet(struct drm_device *dev, void(*func)(struct drm_device*)); | ||
1533 | |||
1534 | /* AGP/GART support (drm_agpsupport.h) */ | ||