diff options
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 19 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c | 172 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 170 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 3 | ||||
-rw-r--r-- | include/drm/vmwgfx_drm.h | 63 |
6 files changed, 440 insertions, 0 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 73757c3..ace4402 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -94,6 +94,12 @@ #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[] = { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 2124fbc..fc0e3bc 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -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 diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index 5ecf966..c0284a4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -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; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 8628bc7..41916b5 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -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; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index ee16a06..08d2630a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -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) diff --git a/include/drm/vmwgfx_drm.h b/include/drm/vmwgfx_drm.h index 29cd9cf..5b5b0a8 100644 --- a/include/drm/vmwgfx_drm.h +++ b/include/drm/vmwgfx_drm.h @@ -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 |