diff options
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_display.c')
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_display.c | 261 |
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 | */ | ||
189 | static 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 | |||
208 | void 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 | |||
277 | static 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 | |||
418 | pflip_cleanup2: | ||
419 | drm_vblank_put(dev, radeon_crtc->crtc_id); | ||
420 | |||
421 | pflip_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 | |||
436 | pflip_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 | |||
186 | static const struct drm_crtc_funcs radeon_crtc_funcs = { | 446 | static 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 | ||
194 | static void radeon_crtc_init(struct drm_device *dev, int index) | 455 | static void radeon_crtc_init(struct drm_device *dev, int index) |