vmwgfx: Add present and readback ioctls
authorJakob Bornecrantz <jakob@vmware.com>
Tue, 4 Oct 2011 18:13:26 +0000 (20:13 +0200)
committerDave Airlie <airlied@redhat.com>
Wed, 5 Oct 2011 09:17:17 +0000 (10:17 +0100)
Signed-off-by: Jakob Bornecrantz <jakob@vmware.com>
Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
include/drm/vmwgfx_drm.h

index 73757c3db8eb75908ca3e8dd9543cabe674dd61c..ace4402214c682abacb4a3ddacf1171e0d202886 100644 (file)
 #define DRM_IOCTL_VMW_FENCE_UNREF                              \
        DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_FENCE_UNREF,         \
                 struct drm_vmw_fence_arg)
+#define DRM_IOCTL_VMW_PRESENT                                  \
+       DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_PRESENT,             \
+                struct drm_vmw_present_arg)
+#define DRM_IOCTL_VMW_PRESENT_READBACK                         \
+       DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_PRESENT_READBACK,    \
+                struct drm_vmw_present_readback_arg)
 
 /**
  * The core DRM version of this macro doesn't account for
@@ -146,6 +152,13 @@ static struct drm_ioctl_desc vmw_ioctls[] = {
                      DRM_AUTH | DRM_UNLOCKED),
        VMW_IOCTL_DEF(VMW_GET_3D_CAP, vmw_get_cap_3d_ioctl,
                      DRM_AUTH | DRM_UNLOCKED),
+
+       /* these allow direct access to the framebuffers mark as master only */
+       VMW_IOCTL_DEF(VMW_PRESENT, vmw_present_ioctl,
+                     DRM_MASTER | DRM_AUTH | DRM_UNLOCKED),
+       VMW_IOCTL_DEF(VMW_PRESENT_READBACK,
+                     vmw_present_readback_ioctl,
+                     DRM_MASTER | DRM_AUTH | DRM_UNLOCKED),
 };
 
 static struct pci_device_id vmw_pci_id_list[] = {
index 2124fbc919aa9d40504b452fa3fc9631795b83e4..fc0e3bc63ec0df577bb08236123e19c928016696 100644 (file)
@@ -97,6 +97,8 @@ struct vmw_cursor_snooper {
        uint32_t *image;
 };
 
+struct vmw_framebuffer;
+
 struct vmw_surface {
        struct vmw_resource res;
        uint32_t flags;
@@ -430,6 +432,10 @@ extern int vmw_getparam_ioctl(struct drm_device *dev, void *data,
                              struct drm_file *file_priv);
 extern int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data,
                                struct drm_file *file_priv);
+extern int vmw_present_ioctl(struct drm_device *dev, void *data,
+                            struct drm_file *file_priv);
+extern int vmw_present_readback_ioctl(struct drm_device *dev, void *data,
+                                     struct drm_file *file_priv);
 
 /**
  * Fifo utilities - vmwgfx_fifo.c
@@ -554,6 +560,19 @@ bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv,
                                uint32_t pitch,
                                uint32_t height);
 u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc);
+int vmw_kms_present(struct vmw_private *dev_priv,
+                   struct drm_file *file_priv,
+                   struct vmw_framebuffer *vfb,
+                   struct vmw_surface *surface,
+                   uint32_t sid, int32_t destX, int32_t destY,
+                   struct drm_vmw_rect *clips,
+                   uint32_t num_clips);
+int vmw_kms_readback(struct vmw_private *dev_priv,
+                    struct drm_file *file_priv,
+                    struct vmw_framebuffer *vfb,
+                    struct drm_vmw_fence_rep __user *user_fence_rep,
+                    struct drm_vmw_rect *clips,
+                    uint32_t num_clips);
 
 /**
  * Overlay control - vmwgfx_overlay.c
index 5ecf966606447bf06913af811bd72f98ccd89b9e..c0284a4784c9616d0d90d6edd6cd40170ca190b2 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "vmwgfx_drv.h"
 #include "vmwgfx_drm.h"
+#include "vmwgfx_kms.h"
 
 int vmw_getparam_ioctl(struct drm_device *dev, void *data,
                       struct drm_file *file_priv)
@@ -110,3 +111,174 @@ int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data,
 
        return ret;
 }
+
+int vmw_present_ioctl(struct drm_device *dev, void *data,
+                     struct drm_file *file_priv)
+{
+       struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+       struct vmw_private *dev_priv = vmw_priv(dev);
+       struct drm_vmw_present_arg *arg =
+               (struct drm_vmw_present_arg *)data;
+       struct vmw_surface *surface;
+       struct vmw_master *vmaster = vmw_master(file_priv->master);
+       struct drm_vmw_rect __user *clips_ptr;
+       struct drm_vmw_rect *clips = NULL;
+       struct drm_mode_object *obj;
+       struct vmw_framebuffer *vfb;
+       uint32_t num_clips;
+       int ret;
+
+       num_clips = arg->num_clips;
+       clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr;
+
+       if (unlikely(num_clips == 0))
+               return 0;
+
+       if (clips_ptr == NULL) {
+               DRM_ERROR("Variable clips_ptr must be specified.\n");
+               ret = -EINVAL;
+               goto out_clips;
+       }
+
+       clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL);
+       if (clips == NULL) {
+               DRM_ERROR("Failed to allocate clip rect list.\n");
+               ret = -ENOMEM;
+               goto out_clips;
+       }
+
+       ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips));
+       if (ret) {
+               DRM_ERROR("Failed to copy clip rects from userspace.\n");
+               goto out_no_copy;
+       }
+
+       ret = mutex_lock_interruptible(&dev->mode_config.mutex);
+       if (unlikely(ret != 0)) {
+               ret = -ERESTARTSYS;
+               goto out_no_mode_mutex;
+       }
+
+       obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB);
+       if (!obj) {
+               DRM_ERROR("Invalid framebuffer id.\n");
+               ret = -EINVAL;
+               goto out_no_fb;
+       }
+
+       vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj));
+       if (!vfb->dmabuf) {
+               DRM_ERROR("Framebuffer not dmabuf backed.\n");
+               ret = -EINVAL;
+               goto out_no_fb;
+       }
+
+       ret = ttm_read_lock(&vmaster->lock, true);
+       if (unlikely(ret != 0))
+               goto out_no_ttm_lock;
+
+       ret = vmw_user_surface_lookup_handle(dev_priv, tfile, arg->sid,
+                                            &surface);
+       if (ret)
+               goto out_no_surface;
+
+       ret = vmw_kms_present(dev_priv, file_priv,
+                             vfb, surface, arg->sid,
+                             arg->dest_x, arg->dest_y,
+                             clips, num_clips);
+
+       /* vmw_user_surface_lookup takes one ref so does new_fb */
+       vmw_surface_unreference(&surface);
+
+out_no_surface:
+       ttm_read_unlock(&vmaster->lock);
+out_no_ttm_lock:
+out_no_fb:
+       mutex_unlock(&dev->mode_config.mutex);
+out_no_mode_mutex:
+out_no_copy:
+       kfree(clips);
+out_clips:
+       return ret;
+}
+
+int vmw_present_readback_ioctl(struct drm_device *dev, void *data,
+                              struct drm_file *file_priv)
+{
+       struct vmw_private *dev_priv = vmw_priv(dev);
+       struct drm_vmw_present_readback_arg *arg =
+               (struct drm_vmw_present_readback_arg *)data;
+       struct drm_vmw_fence_rep __user *user_fence_rep =
+               (struct drm_vmw_fence_rep __user *)
+               (unsigned long)arg->fence_rep;
+       struct vmw_master *vmaster = vmw_master(file_priv->master);
+       struct drm_vmw_rect __user *clips_ptr;
+       struct drm_vmw_rect *clips = NULL;
+       struct drm_mode_object *obj;
+       struct vmw_framebuffer *vfb;
+       uint32_t num_clips;
+       int ret;
+
+       num_clips = arg->num_clips;
+       clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr;
+
+       if (unlikely(num_clips == 0))
+               return 0;
+
+       if (clips_ptr == NULL) {
+               DRM_ERROR("Argument clips_ptr must be specified.\n");
+               ret = -EINVAL;
+               goto out_clips;
+       }
+
+       clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL);
+       if (clips == NULL) {
+               DRM_ERROR("Failed to allocate clip rect list.\n");
+               ret = -ENOMEM;
+               goto out_clips;
+       }
+
+       ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips));
+       if (ret) {
+               DRM_ERROR("Failed to copy clip rects from userspace.\n");
+               goto out_no_copy;
+       }
+
+       ret = mutex_lock_interruptible(&dev->mode_config.mutex);
+       if (unlikely(ret != 0)) {
+               ret = -ERESTARTSYS;
+               goto out_no_mode_mutex;
+       }
+
+       obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB);
+       if (!obj) {
+               DRM_ERROR("Invalid framebuffer id.\n");
+               ret = -EINVAL;
+               goto out_no_fb;
+       }
+
+       vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj));
+       if (!vfb->dmabuf) {
+               DRM_ERROR("Framebuffer not dmabuf backed.\n");
+               ret = -EINVAL;
+               goto out_no_fb;
+       }
+
+       ret = ttm_read_lock(&vmaster->lock, true);
+       if (unlikely(ret != 0))
+               goto out_no_ttm_lock;
+
+       ret = vmw_kms_readback(dev_priv, file_priv,
+                              vfb, user_fence_rep,
+                              clips, num_clips);
+
+       ttm_read_unlock(&vmaster->lock);
+out_no_ttm_lock:
+out_no_fb:
+       mutex_unlock(&dev->mode_config.mutex);
+out_no_mode_mutex:
+out_no_copy:
+       kfree(clips);
+out_clips:
+       return ret;
+}
index 8628bc7cc0d60620323ca727b8885eda1ad979cf..41916b58a3fb99e050f8e13a2270dadd5aab4ab6 100644 (file)
@@ -800,6 +800,7 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
                vfbd->base.pin = vmw_framebuffer_dmabuf_pin;
                vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin;
        }
+       vfbd->base.dmabuf = true;
        vfbd->buffer = dmabuf;
        vfbd->handle = mode_cmd->handle;
        *out = &vfbd->base;
@@ -900,6 +901,175 @@ static struct drm_mode_config_funcs vmw_kms_funcs = {
        .fb_create = vmw_kms_fb_create,
 };
 
+int vmw_kms_present(struct vmw_private *dev_priv,
+                   struct drm_file *file_priv,
+                   struct vmw_framebuffer *vfb,
+                   struct vmw_surface *surface,
+                   uint32_t sid,
+                   int32_t destX, int32_t destY,
+                   struct drm_vmw_rect *clips,
+                   uint32_t num_clips)
+{
+       size_t fifo_size;
+       int i, ret;
+
+       struct {
+               SVGA3dCmdHeader header;
+               SVGA3dCmdBlitSurfaceToScreen body;
+       } *cmd;
+       SVGASignedRect *blits;
+
+       BUG_ON(surface == NULL);
+       BUG_ON(!clips || !num_clips);
+
+       fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num_clips;
+       cmd = kmalloc(fifo_size, GFP_KERNEL);
+       if (unlikely(cmd == NULL)) {
+               DRM_ERROR("Failed to allocate temporary fifo memory.\n");
+               return -ENOMEM;
+       }
+
+       memset(cmd, 0, fifo_size);
+
+       cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN);
+       cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header));
+
+       cmd->body.srcImage.sid = sid;
+       cmd->body.destScreenId = SVGA_ID_INVALID; /* virtual coords */
+
+       cmd->body.srcRect.left = 0;
+       cmd->body.srcRect.right = surface->sizes[0].width;
+       cmd->body.srcRect.top = 0;
+       cmd->body.srcRect.bottom = surface->sizes[0].height;
+
+       cmd->body.destRect.left = destX;
+       cmd->body.destRect.right = destX + surface->sizes[0].width;
+       cmd->body.destRect.top = destY;
+       cmd->body.destRect.bottom = destY + surface->sizes[0].height;
+
+       blits = (SVGASignedRect *)&cmd[1];
+       for (i = 0; i < num_clips; i++) {
+               blits[i].left   = clips[i].x;
+               blits[i].right  = clips[i].x + clips[i].w;
+               blits[i].top    = clips[i].y;
+               blits[i].bottom = clips[i].y + clips[i].h;
+       }
+
+       ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd,
+                                 fifo_size, 0, NULL);
+
+       kfree(cmd);
+
+       return ret;
+}
+
+int vmw_kms_readback(struct vmw_private *dev_priv,
+                    struct drm_file *file_priv,
+                    struct vmw_framebuffer *vfb,
+                    struct drm_vmw_fence_rep __user *user_fence_rep,
+                    struct drm_vmw_rect *clips,
+                    uint32_t num_clips)
+{
+       struct vmw_framebuffer_dmabuf *vfbd =
+               vmw_framebuffer_to_vfbd(&vfb->base);
+       struct vmw_dma_buffer *dmabuf = vfbd->buffer;
+       struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
+       struct drm_crtc *crtc;
+       size_t fifo_size;
+       int i, k, ret, num_units, blits_pos;
+
+       struct {
+               uint32_t header;
+               SVGAFifoCmdDefineGMRFB body;
+       } *cmd;
+       struct {
+               uint32_t header;
+               SVGAFifoCmdBlitScreenToGMRFB body;
+       } *blits;
+
+       num_units = 0;
+       list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) {
+               if (crtc->fb != &vfb->base)
+                       continue;
+               units[num_units++] = vmw_crtc_to_du(crtc);
+       }
+
+       BUG_ON(dmabuf == NULL);
+       BUG_ON(!clips || !num_clips);
+
+       /* take a safe guess at fifo size */
+       fifo_size = sizeof(*cmd) + sizeof(*blits) * num_clips * num_units;
+       cmd = kmalloc(fifo_size, GFP_KERNEL);
+       if (unlikely(cmd == NULL)) {
+               DRM_ERROR("Failed to allocate temporary fifo memory.\n");
+               return -ENOMEM;
+       }
+
+       memset(cmd, 0, fifo_size);
+       cmd->header = SVGA_CMD_DEFINE_GMRFB;
+       cmd->body.format.bitsPerPixel = vfb->base.bits_per_pixel;
+       cmd->body.format.colorDepth = vfb->base.depth;
+       cmd->body.format.reserved = 0;
+       cmd->body.bytesPerLine = vfb->base.pitch;
+       cmd->body.ptr.gmrId = vfbd->handle;
+       cmd->body.ptr.offset = 0;
+
+       blits = (void *)&cmd[1];
+       blits_pos = 0;
+       for (i = 0; i < num_units; i++) {
+               struct drm_vmw_rect *c = clips;
+               for (k = 0; k < num_clips; k++, c++) {
+                       /* transform clip coords to crtc origin based coords */
+                       int clip_x1 = c->x - units[i]->crtc.x;
+                       int clip_x2 = c->x - units[i]->crtc.x + c->w;
+                       int clip_y1 = c->y - units[i]->crtc.y;
+                       int clip_y2 = c->y - units[i]->crtc.y + c->h;
+                       int dest_x = c->x;
+                       int dest_y = c->y;
+
+                       /* compensate for clipping, we negate
+                        * a negative number and add that.
+                        */
+                       if (clip_x1 < 0)
+                               dest_x += -clip_x1;
+                       if (clip_y1 < 0)
+                               dest_y += -clip_y1;
+
+                       /* clip */
+                       clip_x1 = max(clip_x1, 0);
+                       clip_y1 = max(clip_y1, 0);
+                       clip_x2 = min(clip_x2, units[i]->crtc.mode.hdisplay);
+                       clip_y2 = min(clip_y2, units[i]->crtc.mode.vdisplay);
+
+                       /* and cull any rects that misses the crtc */
+                       if (clip_x1 >= units[i]->crtc.mode.hdisplay ||
+                           clip_y1 >= units[i]->crtc.mode.vdisplay ||
+                           clip_x2 <= 0 || clip_y2 <= 0)
+                               continue;
+
+                       blits[blits_pos].header = SVGA_CMD_BLIT_SCREEN_TO_GMRFB;
+                       blits[blits_pos].body.srcScreenId = units[i]->unit;
+                       blits[blits_pos].body.destOrigin.x = dest_x;
+                       blits[blits_pos].body.destOrigin.y = dest_y;
+
+                       blits[blits_pos].body.srcRect.left = clip_x1;
+                       blits[blits_pos].body.srcRect.top = clip_y1;
+                       blits[blits_pos].body.srcRect.right = clip_x2;
+                       blits[blits_pos].body.srcRect.bottom = clip_y2;
+                       blits_pos++;
+               }
+       }
+       /* reset size here and use calculated exact size from loops */
+       fifo_size = sizeof(*cmd) + sizeof(*blits) * blits_pos;
+
+       ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, fifo_size,
+                                 0, user_fence_rep);
+
+       kfree(cmd);
+
+       return ret;
+}
+
 int vmw_kms_init(struct vmw_private *dev_priv)
 {
        struct drm_device *dev = dev_priv->dev;
index ee16a06e4ca13fc2953e0775c97cf5af48966a32..08d2630ac3a7f8f3afe77a71cfc95e468cfd1ed8 100644 (file)
@@ -47,6 +47,7 @@ struct vmw_framebuffer {
        struct drm_framebuffer base;
        int (*pin)(struct vmw_framebuffer *fb);
        int (*unpin)(struct vmw_framebuffer *fb);
+       bool dmabuf;
 };
 
 
@@ -95,6 +96,8 @@ struct vmw_display_unit {
        struct drm_display_mode *pref_mode;
 };
 
+#define vmw_crtc_to_du(x) \
+       container_of(x, struct vmw_display_unit, crtc)
 #define vmw_connector_to_du(x) \
        container_of(x, struct vmw_display_unit, connector)
 
index 29cd9cfdd6115c27dedc68c83f79a730e988533a..5b5b0a891f36e4ebff7c06bacb0d002f407c0ab9 100644 (file)
@@ -52,6 +52,8 @@
 #define DRM_VMW_FENCE_SIGNALED       15
 #define DRM_VMW_FENCE_UNREF          16
 #define DRM_VMW_FENCE_EVENT          17
+#define DRM_VMW_PRESENT              18
+#define DRM_VMW_PRESENT_READBACK     19
 
 
 /*************************************************************************/
@@ -681,5 +683,66 @@ struct drm_vmw_fence_arg {
 };
 
 
+/*************************************************************************/
+/**
+ * DRM_VMW_PRESENT
+ *
+ * Executes an SVGA present on a given fb for a given surface. The surface
+ * is placed on the framebuffer. Cliprects are given relative to the given
+ * point (the point disignated by dest_{x|y}).
+ *
+ */
+
+/**
+ * struct drm_vmw_present_arg
+ * @fb_id: framebuffer id to present / read back from.
+ * @sid: Surface id to present from.
+ * @dest_x: X placement coordinate for surface.
+ * @dest_y: Y placement coordinate for surface.
+ * @clips_ptr: Pointer to an array of clip rects cast to an uint64_t.
+ * @num_clips: Number of cliprects given relative to the framebuffer origin,
+ * in the same coordinate space as the frame buffer.
+ * @pad64: Unused 64-bit padding.
+ *
+ * Input argument to the DRM_VMW_PRESENT ioctl.
+ */
+
+struct drm_vmw_present_arg {
+       uint32_t fb_id;
+       uint32_t sid;
+       int32_t dest_x;
+       int32_t dest_y;
+       uint64_t clips_ptr;
+       uint32_t num_clips;
+       uint32_t pad64;
+};
+
+
+/*************************************************************************/
+/**
+ * DRM_VMW_PRESENT_READBACK
+ *
+ * Executes an SVGA present readback from a given fb to the dma buffer
+ * currently bound as the fb. If there is no dma buffer bound to the fb,
+ * an error will be returned.
+ *
+ */
+
+/**
+ * struct drm_vmw_present_arg
+ * @fb_id: fb_id to present / read back from.
+ * @num_clips: Number of cliprects.
+ * @clips_ptr: Pointer to an array of clip rects cast to an uint64_t.
+ * @fence_rep: Pointer to a struct drm_vmw_fence_rep, cast to an uint64_t.
+ * If this member is NULL, then the ioctl should not return a fence.
+ */
+
+struct drm_vmw_present_readback_arg {
+        uint32_t fb_id;
+        uint32_t num_clips;
+        uint64_t clips_ptr;
+        uint64_t fence_rep;
+};
+
 
 #endif