aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/radeon/radeon_display.c
diff options
context:
space:
mode:
authorAlex Deucher <alexdeucher@gmail.com>2010-11-21 10:59:01 -0500
committerDave Airlie <airlied@redhat.com>2010-11-21 20:51:08 -0500
commit6f34be50bd1bdd2ff3c955940e033a80d05f248a (patch)
tree7e9635a2e589cd3a49490a4656611c112e485059 /drivers/gpu/drm/radeon/radeon_display.c
parentf5a8020903932624cf020dc72455a10a3e005087 (diff)
drm/radeon/kms: add pageflip ioctl support (v3)
This adds support for dri2 pageflipping. v2: precision updates from Mario Kleiner. v3: Multihead fixes from Mario Kleiner; missing crtc offset add note about update pending bit on pre-avivo chips Signed-off-by: Alex Deucher <alexdeucher@gmail.com> Signed-off-by: Mario Kleiner <mario.kleiner@tuebingen.mpg.de> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_display.c')
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c261
1 files changed, 261 insertions, 0 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index eeea7cbb9517..f6493f444faa 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -183,12 +183,273 @@ static void radeon_crtc_destroy(struct drm_crtc *crtc)
183 kfree(radeon_crtc); 183 kfree(radeon_crtc);
184} 184}
185 185
186/*
187 * Handle unpin events outside the interrupt handler proper.
188 */
189static void radeon_unpin_work_func(struct work_struct *__work)
190{
191 struct radeon_unpin_work *work =
192 container_of(__work, struct radeon_unpin_work, work);
193 int r;
194
195 /* unpin of the old buffer */
196 r = radeon_bo_reserve(work->old_rbo, false);
197 if (likely(r == 0)) {
198 r = radeon_bo_unpin(work->old_rbo);
199 if (unlikely(r != 0)) {
200 DRM_ERROR("failed to unpin buffer after flip\n");
201 }
202 radeon_bo_unreserve(work->old_rbo);
203 } else
204 DRM_ERROR("failed to reserve buffer after flip\n");
205 kfree(work);
206}
207
208void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
209{
210 struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
211 struct radeon_unpin_work *work;
212 struct drm_pending_vblank_event *e;
213 struct timeval now;
214 unsigned long flags;
215 u32 update_pending;
216 int vpos, hpos;
217
218 spin_lock_irqsave(&rdev->ddev->event_lock, flags);
219 work = radeon_crtc->unpin_work;
220 if (work == NULL ||
221 !radeon_fence_signaled(work->fence)) {
222 spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
223 return;
224 }
225 /* New pageflip, or just completion of a previous one? */
226 if (!radeon_crtc->deferred_flip_completion) {
227 /* do the flip (mmio) */
228 update_pending = radeon_page_flip(rdev, crtc_id, work->new_crtc_base);
229 } else {
230 /* This is just a completion of a flip queued in crtc
231 * at last invocation. Make sure we go directly to
232 * completion routine.
233 */
234 update_pending = 0;
235 radeon_crtc->deferred_flip_completion = 0;
236 }
237
238 /* Has the pageflip already completed in crtc, or is it certain
239 * to complete in this vblank?
240 */
241 if (update_pending &&
242 (DRM_SCANOUTPOS_VALID & radeon_get_crtc_scanoutpos(rdev->ddev, crtc_id,
243 &vpos, &hpos)) &&
244 (vpos >=0) &&
245 (vpos < (99 * rdev->mode_info.crtcs[crtc_id]->base.hwmode.crtc_vdisplay)/100)) {
246 /* crtc didn't flip in this target vblank interval,
247 * but flip is pending in crtc. It will complete it
248 * in next vblank interval, so complete the flip at
249 * next vblank irq.
250 */
251 radeon_crtc->deferred_flip_completion = 1;
252 spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
253 return;
254 }
255
256 /* Pageflip (will be) certainly completed in this vblank. Clean up. */
257 radeon_crtc->unpin_work = NULL;
258
259 /* wakeup userspace */
260 if (work->event) {
261 e = work->event;
262 do_gettimeofday(&now);
263 e->event.sequence = drm_vblank_count(rdev->ddev, radeon_crtc->crtc_id);
264 e->event.tv_sec = now.tv_sec;
265 e->event.tv_usec = now.tv_usec;
266 list_add_tail(&e->base.link, &e->base.file_priv->event_list);
267 wake_up_interruptible(&e->base.file_priv->event_wait);
268 }
269 spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
270
271 drm_vblank_put(rdev->ddev, radeon_crtc->crtc_id);
272 radeon_fence_unref(&work->fence);
273 radeon_post_page_flip(work->rdev, work->crtc_id);
274 schedule_work(&work->work);
275}
276
277static int radeon_crtc_page_flip(struct drm_crtc *crtc,
278 struct drm_framebuffer *fb,
279 struct drm_pending_vblank_event *event)
280{
281 struct drm_device *dev = crtc->dev;
282 struct radeon_device *rdev = dev->dev_private;
283 struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
284 struct radeon_framebuffer *old_radeon_fb;
285 struct radeon_framebuffer *new_radeon_fb;
286 struct drm_gem_object *obj;
287 struct radeon_bo *rbo;
288 struct radeon_fence *fence;
289 struct radeon_unpin_work *work;
290 unsigned long flags;
291 u32 tiling_flags, pitch_pixels;
292 u64 base;
293 int r;
294
295 work = kzalloc(sizeof *work, GFP_KERNEL);
296 if (work == NULL)
297 return -ENOMEM;
298
299 r = radeon_fence_create(rdev, &fence);
300 if (unlikely(r != 0)) {
301 kfree(work);
302 DRM_ERROR("flip queue: failed to create fence.\n");
303 return -ENOMEM;
304 }
305 work->event = event;
306 work->rdev = rdev;
307 work->crtc_id = radeon_crtc->crtc_id;
308 work->fence = radeon_fence_ref(fence);
309 old_radeon_fb = to_radeon_framebuffer(crtc->fb);
310 new_radeon_fb = to_radeon_framebuffer(fb);
311 /* schedule unpin of the old buffer */
312 obj = old_radeon_fb->obj;
313 rbo = obj->driver_private;
314 work->old_rbo = rbo;
315 INIT_WORK(&work->work, radeon_unpin_work_func);
316
317 /* We borrow the event spin lock for protecting unpin_work */
318 spin_lock_irqsave(&dev->event_lock, flags);
319 if (radeon_crtc->unpin_work) {
320 spin_unlock_irqrestore(&dev->event_lock, flags);
321 kfree(work);
322 radeon_fence_unref(&fence);
323
324 DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
325 return -EBUSY;
326 }
327 radeon_crtc->unpin_work = work;
328 radeon_crtc->deferred_flip_completion = 0;
329 spin_unlock_irqrestore(&dev->event_lock, flags);
330
331 /* pin the new buffer */
332 obj = new_radeon_fb->obj;
333 rbo = obj->driver_private;
334
335 DRM_DEBUG_DRIVER("flip-ioctl() cur_fbo = %p, cur_bbo = %p\n",
336 work->old_rbo, rbo);
337
338 r = radeon_bo_reserve(rbo, false);
339 if (unlikely(r != 0)) {
340 DRM_ERROR("failed to reserve new rbo buffer before flip\n");
341 goto pflip_cleanup;
342 }
343 r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &base);
344 if (unlikely(r != 0)) {
345 radeon_bo_unreserve(rbo);
346 r = -EINVAL;
347 DRM_ERROR("failed to pin new rbo buffer before flip\n");
348 goto pflip_cleanup;
349 }
350 radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
351 radeon_bo_unreserve(rbo);
352
353 if (!ASIC_IS_AVIVO(rdev)) {
354 /* crtc offset is from display base addr not FB location */
355 base -= radeon_crtc->legacy_display_base_addr;
356 pitch_pixels = fb->pitch / (fb->bits_per_pixel / 8);
357
358 if (tiling_flags & RADEON_TILING_MACRO) {
359 if (ASIC_IS_R300(rdev)) {
360 base &= ~0x7ff;
361 } else {
362 int byteshift = fb->bits_per_pixel >> 4;
363 int tile_addr = (((crtc->y >> 3) * pitch_pixels + crtc->x) >> (8 - byteshift)) << 11;
364 base += tile_addr + ((crtc->x << byteshift) % 256) + ((crtc->y % 8) << 8);
365 }
366 } else {
367 int offset = crtc->y * pitch_pixels + crtc->x;
368 switch (fb->bits_per_pixel) {
369 case 8:
370 default:
371 offset *= 1;
372 break;
373 case 15:
374 case 16:
375 offset *= 2;
376 break;
377 case 24:
378 offset *= 3;
379 break;
380 case 32:
381 offset *= 4;
382 break;
383 }
384 base += offset;
385 }
386 base &= ~7;
387 }
388
389 spin_lock_irqsave(&dev->event_lock, flags);
390 work->new_crtc_base = base;
391 spin_unlock_irqrestore(&dev->event_lock, flags);
392
393 /* update crtc fb */
394 crtc->fb = fb;
395
396 r = drm_vblank_get(dev, radeon_crtc->crtc_id);
397 if (r) {
398 DRM_ERROR("failed to get vblank before flip\n");
399 goto pflip_cleanup1;
400 }
401
402 /* 32 ought to cover us */
403 r = radeon_ring_lock(rdev, 32);
404 if (r) {
405 DRM_ERROR("failed to lock the ring before flip\n");
406 goto pflip_cleanup2;
407 }
408
409 /* emit the fence */
410 radeon_fence_emit(rdev, fence);
411 /* set the proper interrupt */
412 radeon_pre_page_flip(rdev, radeon_crtc->crtc_id);
413 /* fire the ring */
414 radeon_ring_unlock_commit(rdev);
415
416 return 0;
417
418pflip_cleanup2:
419 drm_vblank_put(dev, radeon_crtc->crtc_id);
420
421pflip_cleanup1:
422 r = radeon_bo_reserve(rbo, false);
423 if (unlikely(r != 0)) {
424 DRM_ERROR("failed to reserve new rbo in error path\n");
425 goto pflip_cleanup;
426 }
427 r = radeon_bo_unpin(rbo);
428 if (unlikely(r != 0)) {
429 radeon_bo_unreserve(rbo);
430 r = -EINVAL;
431 DRM_ERROR("failed to unpin new rbo in error path\n");
432 goto pflip_cleanup;
433 }
434 radeon_bo_unreserve(rbo);
435
436pflip_cleanup:
437 spin_lock_irqsave(&dev->event_lock, flags);
438 radeon_crtc->unpin_work = NULL;
439 spin_unlock_irqrestore(&dev->event_lock, flags);
440 radeon_fence_unref(&fence);
441 kfree(work);
442
443 return r;
444}
445
186static const struct drm_crtc_funcs radeon_crtc_funcs = { 446static const struct drm_crtc_funcs radeon_crtc_funcs = {
187 .cursor_set = radeon_crtc_cursor_set, 447 .cursor_set = radeon_crtc_cursor_set,
188 .cursor_move = radeon_crtc_cursor_move, 448 .cursor_move = radeon_crtc_cursor_move,
189 .gamma_set = radeon_crtc_gamma_set, 449 .gamma_set = radeon_crtc_gamma_set,
190 .set_config = drm_crtc_helper_set_config, 450 .set_config = drm_crtc_helper_set_config,
191 .destroy = radeon_crtc_destroy, 451 .destroy = radeon_crtc_destroy,
452 .page_flip = radeon_crtc_page_flip,
192}; 453};
193 454
194static void radeon_crtc_init(struct drm_device *dev, int index) 455static void radeon_crtc_init(struct drm_device *dev, int index)