Commit 161d0dc1dccb17ff7a38f462c7c0d4ef8bcc5662

Authored by Matt Roper
Committed by Daniel Vetter
1 parent b36552b32a

drm: Support legacy cursor ioctls via universal planes when possible (v4)

If drivers support universal planes and have registered a cursor plane
with the DRM core, we should use that universal plane support when
handling legacy cursor ioctls.  Drivers that transition to universal
planes won't have to maintain separate legacy ioctl handling; drivers
that don't transition to universal planes will continue to operate
without any change to behavior.

Note that there's a bit of a mismatch between the legacy cursor ioctls
and the universal plane API's --- legacy ioctl's use driver buffer
handles directly whereas the universal plane API takes drm_framebuffers.
Since there's no way to recover the driver handle from a
drm_framebuffer, we can implement legacy ioctl's in terms of universal
plane interfaces, but cannot implement universal plane interfaces in
terms of legacy ioctls.  Specifically, there's no way to create a
general cursor helper in the way we previously created a primary plane
helper.

It's important to land this patch before any patches that add universal
cursor support to individual drivers so that drivers don't have to worry
about juggling two different styles of reference counting for cursor
buffers when userspace mixes and matches legacy and universal cursor
calls.  With this patch, a driver that switches to universal cursor
support may assume that all cursor buffers are wrapped in a
drm_framebuffer and can rely on framebuffer reference counting for all
cursor operations.

v4:
 - Add comments pointing out setplane_internal's reference-eating
   semantics.
v3:
 - Drop drm_mode_rmfb() call that is no longer needed now that we're
   using setplane_internal(), which takes care of deref'ing the
   appropriate framebuffer.
v2:
 - Use new add_framebuffer_internal() function to create framebuffer
   rather than trying to call directly into the ioctl interface and
   look up the handle returned.
 - Use new setplane_internal() function to update the cursor plane
   rather than calling through the ioctl interface.  Note that since
   we're no longer looking up an fb_id, no extra reference will be
   taken here.
 - Grab extra reference to fb under lock in !BO case to avoid issues
   where racing userspace could cause the fb to be destroyed out from
   under us after we grab the fb pointer.

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Matt Roper <matthew.d.roper@intel.com>
Reviewed-by: Pallavi G<pallavi.g@intel.com>
Acked-by: Dave Airlie <airlied@linux.ie>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>

Showing 2 changed files with 111 additions and 0 deletions Side-by-side Diff

drivers/gpu/drm/drm_crtc.c
... ... @@ -2288,6 +2288,10 @@
2288 2288 crtc = obj_to_crtc(obj);
2289 2289 }
2290 2290  
  2291 + /*
  2292 + * setplane_internal will take care of deref'ing either the old or new
  2293 + * framebuffer depending on success.
  2294 + */
2291 2295 return setplane_internal(crtc, plane, fb,
2292 2296 plane_req->crtc_x, plane_req->crtc_y,
2293 2297 plane_req->crtc_w, plane_req->crtc_h,
... ... @@ -2541,6 +2545,102 @@
2541 2545 return ret;
2542 2546 }
2543 2547  
  2548 +/**
  2549 + * drm_mode_cursor_universal - translate legacy cursor ioctl call into a
  2550 + * universal plane handler call
  2551 + * @crtc: crtc to update cursor for
  2552 + * @req: data pointer for the ioctl
  2553 + * @file_priv: drm file for the ioctl call
  2554 + *
  2555 + * Legacy cursor ioctl's work directly with driver buffer handles. To
  2556 + * translate legacy ioctl calls into universal plane handler calls, we need to
  2557 + * wrap the native buffer handle in a drm_framebuffer.
  2558 + *
  2559 + * Note that we assume any handle passed to the legacy ioctls was a 32-bit ARGB
  2560 + * buffer with a pitch of 4*width; the universal plane interface should be used
  2561 + * directly in cases where the hardware can support other buffer settings and
  2562 + * userspace wants to make use of these capabilities.
  2563 + *
  2564 + * Returns:
  2565 + * Zero on success, errno on failure.
  2566 + */
  2567 +static int drm_mode_cursor_universal(struct drm_crtc *crtc,
  2568 + struct drm_mode_cursor2 *req,
  2569 + struct drm_file *file_priv)
  2570 +{
  2571 + struct drm_device *dev = crtc->dev;
  2572 + struct drm_framebuffer *fb = NULL;
  2573 + struct drm_mode_fb_cmd2 fbreq = {
  2574 + .width = req->width,
  2575 + .height = req->height,
  2576 + .pixel_format = DRM_FORMAT_ARGB8888,
  2577 + .pitches = { req->width * 4 },
  2578 + .handles = { req->handle },
  2579 + };
  2580 + int32_t crtc_x, crtc_y;
  2581 + uint32_t crtc_w = 0, crtc_h = 0;
  2582 + uint32_t src_w = 0, src_h = 0;
  2583 + int ret = 0;
  2584 +
  2585 + BUG_ON(!crtc->cursor);
  2586 +
  2587 + /*
  2588 + * Obtain fb we'll be using (either new or existing) and take an extra
  2589 + * reference to it if fb != null. setplane will take care of dropping
  2590 + * the reference if the plane update fails.
  2591 + */
  2592 + if (req->flags & DRM_MODE_CURSOR_BO) {
  2593 + if (req->handle) {
  2594 + fb = add_framebuffer_internal(dev, &fbreq, file_priv);
  2595 + if (IS_ERR(fb)) {
  2596 + DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n");
  2597 + return PTR_ERR(fb);
  2598 + }
  2599 +
  2600 + drm_framebuffer_reference(fb);
  2601 + } else {
  2602 + fb = NULL;
  2603 + }
  2604 + } else {
  2605 + mutex_lock(&dev->mode_config.mutex);
  2606 + fb = crtc->cursor->fb;
  2607 + if (fb)
  2608 + drm_framebuffer_reference(fb);
  2609 + mutex_unlock(&dev->mode_config.mutex);
  2610 + }
  2611 +
  2612 + if (req->flags & DRM_MODE_CURSOR_MOVE) {
  2613 + crtc_x = req->x;
  2614 + crtc_y = req->y;
  2615 + } else {
  2616 + crtc_x = crtc->cursor_x;
  2617 + crtc_y = crtc->cursor_y;
  2618 + }
  2619 +
  2620 + if (fb) {
  2621 + crtc_w = fb->width;
  2622 + crtc_h = fb->height;
  2623 + src_w = fb->width << 16;
  2624 + src_h = fb->height << 16;
  2625 + }
  2626 +
  2627 + /*
  2628 + * setplane_internal will take care of deref'ing either the old or new
  2629 + * framebuffer depending on success.
  2630 + */
  2631 + ret = setplane_internal(crtc, crtc->cursor, fb,
  2632 + crtc_x, crtc_y, crtc_w, crtc_h,
  2633 + 0, 0, src_w, src_h);
  2634 +
  2635 + /* Update successful; save new cursor position, if necessary */
  2636 + if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) {
  2637 + crtc->cursor_x = req->x;
  2638 + crtc->cursor_y = req->y;
  2639 + }
  2640 +
  2641 + return ret;
  2642 +}
  2643 +
2544 2644 static int drm_mode_cursor_common(struct drm_device *dev,
2545 2645 struct drm_mode_cursor2 *req,
2546 2646 struct drm_file *file_priv)
... ... @@ -2559,6 +2659,13 @@
2559 2659 DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
2560 2660 return -ENOENT;
2561 2661 }
  2662 +
  2663 + /*
  2664 + * If this crtc has a universal cursor plane, call that plane's update
  2665 + * handler rather than using legacy cursor handlers.
  2666 + */
  2667 + if (crtc->cursor)
  2668 + return drm_mode_cursor_universal(crtc, req, file_priv);
2562 2669  
2563 2670 drm_modeset_lock(&crtc->mutex, NULL);
2564 2671 if (req->flags & DRM_MODE_CURSOR_BO) {
include/drm/drm_crtc.h
... ... @@ -331,6 +331,10 @@
331 331 struct drm_plane *primary;
332 332 struct drm_plane *cursor;
333 333  
  334 + /* position of cursor plane on crtc */
  335 + int cursor_x;
  336 + int cursor_y;
  337 +
334 338 /* Temporary tracking of the old fb while a modeset is ongoing. Used
335 339 * by drm_mode_set_config_internal to implement correct refcounting. */
336 340 struct drm_framebuffer *old_fb;