2009-12-10 08:19:58 +08:00
|
|
|
/**************************************************************************
|
|
|
|
*
|
2015-07-30 03:38:02 +08:00
|
|
|
* Copyright © 2009-2015 VMware, Inc., Palo Alto, CA., USA
|
2009-12-10 08:19:58 +08:00
|
|
|
* All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
* copy of this software and associated documentation files (the
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
* distribute, sub license, and/or sell copies of the Software, and to
|
|
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
* the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice (including the
|
|
|
|
* next paragraph) shall be included in all copies or substantial portions
|
|
|
|
* of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
|
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
|
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
|
|
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
|
|
#include "vmwgfx_kms.h"
|
2017-03-24 02:33:39 +08:00
|
|
|
#include <drm/drm_atomic.h>
|
|
|
|
#include <drm/drm_atomic_helper.h>
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2011-10-05 02:13:22 +08:00
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
/* Might need a hrtimer here? */
|
|
|
|
#define VMWGFX_PRESENT_RATE ((HZ / 60 > 0) ? HZ / 60 : 1)
|
|
|
|
|
2015-06-26 16:23:42 +08:00
|
|
|
void vmw_du_cleanup(struct vmw_display_unit *du)
|
2009-12-10 08:19:58 +08:00
|
|
|
{
|
2017-03-24 02:28:11 +08:00
|
|
|
drm_plane_cleanup(&du->primary);
|
|
|
|
drm_plane_cleanup(&du->cursor);
|
|
|
|
|
2014-05-29 23:57:41 +08:00
|
|
|
drm_connector_unregister(&du->connector);
|
2009-12-10 08:19:58 +08:00
|
|
|
drm_crtc_cleanup(&du->crtc);
|
|
|
|
drm_encoder_cleanup(&du->encoder);
|
|
|
|
drm_connector_cleanup(&du->connector);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Display Unit Cursor functions
|
|
|
|
*/
|
|
|
|
|
2017-03-24 02:28:11 +08:00
|
|
|
static int vmw_cursor_update_image(struct vmw_private *dev_priv,
|
|
|
|
u32 *image, u32 width, u32 height,
|
|
|
|
u32 hotspotX, u32 hotspotY)
|
2009-12-10 08:19:58 +08:00
|
|
|
{
|
|
|
|
struct {
|
|
|
|
u32 cmd;
|
|
|
|
SVGAFifoCmdDefineAlphaCursor cursor;
|
|
|
|
} *cmd;
|
|
|
|
u32 image_size = width * height * 4;
|
|
|
|
u32 cmd_size = sizeof(*cmd) + image_size;
|
|
|
|
|
|
|
|
if (!image)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
cmd = vmw_fifo_reserve(dev_priv, cmd_size);
|
|
|
|
if (unlikely(cmd == NULL)) {
|
|
|
|
DRM_ERROR("Fifo reserve failed.\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(cmd, 0, sizeof(*cmd));
|
|
|
|
|
|
|
|
memcpy(&cmd[1], image, image_size);
|
|
|
|
|
2015-04-02 17:39:45 +08:00
|
|
|
cmd->cmd = SVGA_CMD_DEFINE_ALPHA_CURSOR;
|
|
|
|
cmd->cursor.id = 0;
|
|
|
|
cmd->cursor.width = width;
|
|
|
|
cmd->cursor.height = height;
|
|
|
|
cmd->cursor.hotspotX = hotspotX;
|
|
|
|
cmd->cursor.hotspotY = hotspotY;
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2015-11-05 18:18:55 +08:00
|
|
|
vmw_fifo_commit_flush(dev_priv, cmd_size);
|
2009-12-10 08:19:58 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-24 02:28:11 +08:00
|
|
|
static int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv,
|
|
|
|
struct vmw_dma_buffer *dmabuf,
|
|
|
|
u32 width, u32 height,
|
|
|
|
u32 hotspotX, u32 hotspotY)
|
2011-11-28 20:19:10 +08:00
|
|
|
{
|
|
|
|
struct ttm_bo_kmap_obj map;
|
|
|
|
unsigned long kmap_offset;
|
|
|
|
unsigned long kmap_num;
|
|
|
|
void *virtual;
|
|
|
|
bool dummy;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
kmap_offset = 0;
|
|
|
|
kmap_num = (width*height*4 + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
|
|
|
2016-04-06 17:12:03 +08:00
|
|
|
ret = ttm_bo_reserve(&dmabuf->base, true, false, NULL);
|
2011-11-28 20:19:10 +08:00
|
|
|
if (unlikely(ret != 0)) {
|
|
|
|
DRM_ERROR("reserve failed\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ttm_bo_kmap(&dmabuf->base, kmap_offset, kmap_num, &map);
|
|
|
|
if (unlikely(ret != 0))
|
|
|
|
goto err_unreserve;
|
|
|
|
|
|
|
|
virtual = ttm_kmap_obj_virtual(&map, &dummy);
|
|
|
|
ret = vmw_cursor_update_image(dev_priv, virtual, width, height,
|
|
|
|
hotspotX, hotspotY);
|
|
|
|
|
|
|
|
ttm_bo_kunmap(&map);
|
|
|
|
err_unreserve:
|
|
|
|
ttm_bo_unreserve(&dmabuf->base);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-24 02:28:11 +08:00
|
|
|
static void vmw_cursor_update_position(struct vmw_private *dev_priv,
|
|
|
|
bool show, int x, int y)
|
2009-12-10 08:19:58 +08:00
|
|
|
{
|
2015-10-28 17:44:04 +08:00
|
|
|
u32 *fifo_mem = dev_priv->mmio_virt;
|
2009-12-10 08:19:58 +08:00
|
|
|
uint32_t count;
|
|
|
|
|
2017-03-24 02:28:11 +08:00
|
|
|
spin_lock(&dev_priv->cursor_lock);
|
2015-10-28 17:44:04 +08:00
|
|
|
vmw_mmio_write(show ? 1 : 0, fifo_mem + SVGA_FIFO_CURSOR_ON);
|
|
|
|
vmw_mmio_write(x, fifo_mem + SVGA_FIFO_CURSOR_X);
|
|
|
|
vmw_mmio_write(y, fifo_mem + SVGA_FIFO_CURSOR_Y);
|
|
|
|
count = vmw_mmio_read(fifo_mem + SVGA_FIFO_CURSOR_COUNT);
|
|
|
|
vmw_mmio_write(++count, fifo_mem + SVGA_FIFO_CURSOR_COUNT);
|
2017-03-24 02:28:11 +08:00
|
|
|
spin_unlock(&dev_priv->cursor_lock);
|
2009-12-10 08:19:58 +08:00
|
|
|
}
|
|
|
|
|
2015-11-27 02:45:16 +08:00
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
void vmw_kms_cursor_snoop(struct vmw_surface *srf,
|
|
|
|
struct ttm_object_file *tfile,
|
|
|
|
struct ttm_buffer_object *bo,
|
|
|
|
SVGA3dCmdHeader *header)
|
|
|
|
{
|
|
|
|
struct ttm_bo_kmap_obj map;
|
|
|
|
unsigned long kmap_offset;
|
|
|
|
unsigned long kmap_num;
|
|
|
|
SVGA3dCopyBox *box;
|
|
|
|
unsigned box_count;
|
|
|
|
void *virtual;
|
|
|
|
bool dummy;
|
|
|
|
struct vmw_dma_cmd {
|
|
|
|
SVGA3dCmdHeader header;
|
|
|
|
SVGA3dCmdSurfaceDMA dma;
|
|
|
|
} *cmd;
|
2011-11-04 04:03:08 +08:00
|
|
|
int i, ret;
|
2009-12-10 08:19:58 +08:00
|
|
|
|
|
|
|
cmd = container_of(header, struct vmw_dma_cmd, header);
|
|
|
|
|
|
|
|
/* No snooper installed */
|
|
|
|
if (!srf->snooper.image)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (cmd->dma.host.face != 0 || cmd->dma.host.mipmap != 0) {
|
|
|
|
DRM_ERROR("face and mipmap for cursors should never != 0\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd->header.size < 64) {
|
|
|
|
DRM_ERROR("at least one full copy box must be given\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
box = (SVGA3dCopyBox *)&cmd[1];
|
|
|
|
box_count = (cmd->header.size - sizeof(SVGA3dCmdSurfaceDMA)) /
|
|
|
|
sizeof(SVGA3dCopyBox);
|
|
|
|
|
2011-11-04 04:03:08 +08:00
|
|
|
if (cmd->dma.guest.ptr.offset % PAGE_SIZE ||
|
2009-12-10 08:19:58 +08:00
|
|
|
box->x != 0 || box->y != 0 || box->z != 0 ||
|
|
|
|
box->srcx != 0 || box->srcy != 0 || box->srcz != 0 ||
|
2011-11-04 04:03:08 +08:00
|
|
|
box->d != 1 || box_count != 1) {
|
2009-12-10 08:19:58 +08:00
|
|
|
/* TODO handle none page aligned offsets */
|
2011-11-04 04:03:08 +08:00
|
|
|
/* TODO handle more dst & src != 0 */
|
|
|
|
/* TODO handle more then one copy */
|
|
|
|
DRM_ERROR("Cant snoop dma request for cursor!\n");
|
|
|
|
DRM_ERROR("(%u, %u, %u) (%u, %u, %u) (%ux%ux%u) %u %u\n",
|
|
|
|
box->srcx, box->srcy, box->srcz,
|
|
|
|
box->x, box->y, box->z,
|
|
|
|
box->w, box->h, box->d, box_count,
|
|
|
|
cmd->dma.guest.ptr.offset);
|
2009-12-10 08:19:58 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
kmap_offset = cmd->dma.guest.ptr.offset >> PAGE_SHIFT;
|
|
|
|
kmap_num = (64*64*4) >> PAGE_SHIFT;
|
|
|
|
|
2016-04-06 17:12:03 +08:00
|
|
|
ret = ttm_bo_reserve(bo, true, false, NULL);
|
2009-12-10 08:19:58 +08:00
|
|
|
if (unlikely(ret != 0)) {
|
|
|
|
DRM_ERROR("reserve failed\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ttm_bo_kmap(bo, kmap_offset, kmap_num, &map);
|
|
|
|
if (unlikely(ret != 0))
|
|
|
|
goto err_unreserve;
|
|
|
|
|
|
|
|
virtual = ttm_kmap_obj_virtual(&map, &dummy);
|
|
|
|
|
2011-11-04 04:03:08 +08:00
|
|
|
if (box->w == 64 && cmd->dma.guest.pitch == 64*4) {
|
|
|
|
memcpy(srf->snooper.image, virtual, 64*64*4);
|
|
|
|
} else {
|
|
|
|
/* Image is unsigned pointer. */
|
|
|
|
for (i = 0; i < box->h; i++)
|
|
|
|
memcpy(srf->snooper.image + i * 64,
|
|
|
|
virtual + i * cmd->dma.guest.pitch,
|
|
|
|
box->w * 4);
|
|
|
|
}
|
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
srf->snooper.age++;
|
|
|
|
|
|
|
|
ttm_bo_kunmap(&map);
|
|
|
|
err_unreserve:
|
|
|
|
ttm_bo_unreserve(bo);
|
|
|
|
}
|
|
|
|
|
2015-11-27 02:45:16 +08:00
|
|
|
/**
|
|
|
|
* vmw_kms_legacy_hotspot_clear - Clear legacy hotspots
|
|
|
|
*
|
|
|
|
* @dev_priv: Pointer to the device private struct.
|
|
|
|
*
|
|
|
|
* Clears all legacy hotspots.
|
|
|
|
*/
|
|
|
|
void vmw_kms_legacy_hotspot_clear(struct vmw_private *dev_priv)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = dev_priv->dev;
|
|
|
|
struct vmw_display_unit *du;
|
|
|
|
struct drm_crtc *crtc;
|
|
|
|
|
|
|
|
drm_modeset_lock_all(dev);
|
|
|
|
drm_for_each_crtc(crtc, dev) {
|
|
|
|
du = vmw_crtc_to_du(crtc);
|
|
|
|
|
|
|
|
du->hotspot_x = 0;
|
|
|
|
du->hotspot_y = 0;
|
|
|
|
}
|
|
|
|
drm_modeset_unlock_all(dev);
|
|
|
|
}
|
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = dev_priv->dev;
|
|
|
|
struct vmw_display_unit *du;
|
|
|
|
struct drm_crtc *crtc;
|
|
|
|
|
|
|
|
mutex_lock(&dev->mode_config.mutex);
|
|
|
|
|
|
|
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
|
|
|
du = vmw_crtc_to_du(crtc);
|
|
|
|
if (!du->cursor_surface ||
|
|
|
|
du->cursor_age == du->cursor_surface->snooper.age)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
du->cursor_age = du->cursor_surface->snooper.age;
|
|
|
|
vmw_cursor_update_image(dev_priv,
|
|
|
|
du->cursor_surface->snooper.image,
|
2015-11-27 02:45:16 +08:00
|
|
|
64, 64,
|
|
|
|
du->hotspot_x + du->core_hotspot_x,
|
|
|
|
du->hotspot_y + du->core_hotspot_y);
|
2009-12-10 08:19:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&dev->mode_config.mutex);
|
|
|
|
}
|
|
|
|
|
2017-03-24 02:28:11 +08:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_du_cursor_plane_update() - Update cursor image and location
|
|
|
|
*
|
|
|
|
* @plane: plane object to update
|
|
|
|
* @crtc: owning CRTC of @plane
|
|
|
|
* @fb: framebuffer to flip onto plane
|
|
|
|
* @crtc_x: x offset of plane on crtc
|
|
|
|
* @crtc_y: y offset of plane on crtc
|
|
|
|
* @crtc_w: width of plane rectangle on crtc
|
|
|
|
* @crtc_h: height of plane rectangle on crtc
|
|
|
|
* @src_x: Not used
|
|
|
|
* @src_y: Not used
|
|
|
|
* @src_w: Not used
|
|
|
|
* @src_h: Not used
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* RETURNS:
|
|
|
|
* Zero on success, error code on failure
|
|
|
|
*/
|
|
|
|
int vmw_du_cursor_plane_update(struct drm_plane *plane,
|
|
|
|
struct drm_crtc *crtc,
|
|
|
|
struct drm_framebuffer *fb,
|
|
|
|
int crtc_x, int crtc_y,
|
|
|
|
unsigned int crtc_w,
|
|
|
|
unsigned int crtc_h,
|
|
|
|
uint32_t src_x, uint32_t src_y,
|
|
|
|
uint32_t src_w, uint32_t src_h)
|
|
|
|
{
|
|
|
|
struct vmw_private *dev_priv = vmw_priv(crtc->dev);
|
|
|
|
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
|
|
|
struct vmw_surface *surface = NULL;
|
|
|
|
struct vmw_dma_buffer *dmabuf = NULL;
|
|
|
|
s32 hotspot_x, hotspot_y;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
hotspot_x = du->hotspot_x + fb->hot_x;
|
|
|
|
hotspot_y = du->hotspot_y + fb->hot_y;
|
|
|
|
|
|
|
|
/* A lot of the code assumes this */
|
|
|
|
if (crtc_w != 64 || crtc_h != 64) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vmw_framebuffer_to_vfb(fb)->dmabuf)
|
|
|
|
dmabuf = vmw_framebuffer_to_vfbd(fb)->buffer;
|
|
|
|
else
|
|
|
|
surface = vmw_framebuffer_to_vfbs(fb)->surface;
|
|
|
|
|
|
|
|
if (surface && !surface->snooper.image) {
|
|
|
|
DRM_ERROR("surface not suitable for cursor\n");
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* setup new image */
|
|
|
|
ret = 0;
|
|
|
|
if (surface) {
|
|
|
|
/* vmw_user_surface_lookup takes one reference */
|
|
|
|
du->cursor_surface = surface;
|
|
|
|
|
|
|
|
du->cursor_age = du->cursor_surface->snooper.age;
|
|
|
|
|
|
|
|
ret = vmw_cursor_update_image(dev_priv, surface->snooper.image,
|
|
|
|
64, 64, hotspot_x, hotspot_y);
|
|
|
|
} else if (dmabuf) {
|
|
|
|
/* vmw_user_surface_lookup takes one reference */
|
|
|
|
du->cursor_dmabuf = dmabuf;
|
|
|
|
|
|
|
|
ret = vmw_cursor_update_dmabuf(dev_priv, dmabuf, crtc_w, crtc_h,
|
|
|
|
hotspot_x, hotspot_y);
|
|
|
|
} else {
|
|
|
|
vmw_cursor_update_position(dev_priv, false, 0, 0);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
du->cursor_x = crtc_x + du->set_gui_x;
|
|
|
|
du->cursor_y = crtc_y + du->set_gui_y;
|
|
|
|
|
|
|
|
vmw_cursor_update_position(dev_priv, true,
|
|
|
|
du->cursor_x + hotspot_x,
|
|
|
|
du->cursor_y + hotspot_y);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int vmw_du_cursor_plane_disable(struct drm_plane *plane)
|
|
|
|
{
|
|
|
|
if (plane->fb) {
|
|
|
|
drm_framebuffer_unreference(plane->fb);
|
|
|
|
plane->fb = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void vmw_du_cursor_plane_destroy(struct drm_plane *plane)
|
|
|
|
{
|
|
|
|
vmw_cursor_update_position(plane->dev->dev_private, false, 0, 0);
|
|
|
|
|
|
|
|
drm_plane_cleanup(plane);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void vmw_du_primary_plane_destroy(struct drm_plane *plane)
|
|
|
|
{
|
|
|
|
drm_plane_cleanup(plane);
|
|
|
|
|
|
|
|
/* Planes are static in our case so we don't free it */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-24 02:33:39 +08:00
|
|
|
/**
|
|
|
|
* vmw_du_crtc_duplicate_state - duplicate crtc state
|
|
|
|
* @crtc: DRM crtc
|
|
|
|
*
|
|
|
|
* Allocates and returns a copy of the crtc state (both common and
|
|
|
|
* vmw-specific) for the specified crtc.
|
|
|
|
*
|
|
|
|
* Returns: The newly allocated crtc state, or NULL on failure.
|
|
|
|
*/
|
|
|
|
struct drm_crtc_state *
|
|
|
|
vmw_du_crtc_duplicate_state(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct drm_crtc_state *state;
|
|
|
|
struct vmw_crtc_state *vcs;
|
|
|
|
|
|
|
|
if (WARN_ON(!crtc->state))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
vcs = kmemdup(crtc->state, sizeof(*vcs), GFP_KERNEL);
|
|
|
|
|
|
|
|
if (!vcs)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
state = &vcs->base;
|
|
|
|
|
|
|
|
__drm_atomic_helper_crtc_duplicate_state(crtc, state);
|
|
|
|
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_du_crtc_reset - creates a blank vmw crtc state
|
|
|
|
* @crtc: DRM crtc
|
|
|
|
*
|
|
|
|
* Resets the atomic state for @crtc by freeing the state pointer (which
|
|
|
|
* might be NULL, e.g. at driver load time) and allocating a new empty state
|
|
|
|
* object.
|
|
|
|
*/
|
|
|
|
void vmw_du_crtc_reset(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct vmw_crtc_state *vcs;
|
|
|
|
|
|
|
|
|
|
|
|
if (crtc->state) {
|
|
|
|
__drm_atomic_helper_crtc_destroy_state(crtc->state);
|
|
|
|
|
|
|
|
kfree(vmw_crtc_state_to_vcs(crtc->state));
|
|
|
|
}
|
|
|
|
|
|
|
|
vcs = kzalloc(sizeof(*vcs), GFP_KERNEL);
|
|
|
|
|
|
|
|
if (!vcs) {
|
|
|
|
DRM_ERROR("Cannot allocate vmw_crtc_state\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
crtc->state = &vcs->base;
|
|
|
|
crtc->state->crtc = crtc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_du_crtc_destroy_state - destroy crtc state
|
|
|
|
* @crtc: DRM crtc
|
|
|
|
* @state: state object to destroy
|
|
|
|
*
|
|
|
|
* Destroys the crtc state (both common and vmw-specific) for the
|
|
|
|
* specified plane.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
vmw_du_crtc_destroy_state(struct drm_crtc *crtc,
|
|
|
|
struct drm_crtc_state *state)
|
|
|
|
{
|
|
|
|
drm_atomic_helper_crtc_destroy_state(crtc, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
/*
|
|
|
|
* Generic framebuffer code
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Surface framebuffer code
|
|
|
|
*/
|
|
|
|
|
2014-01-07 00:48:10 +08:00
|
|
|
static void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer)
|
2009-12-10 08:19:58 +08:00
|
|
|
{
|
2010-10-05 18:43:03 +08:00
|
|
|
struct vmw_framebuffer_surface *vfbs =
|
2009-12-10 08:19:58 +08:00
|
|
|
vmw_framebuffer_to_vfbs(framebuffer);
|
2010-10-05 18:43:03 +08:00
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
drm_framebuffer_cleanup(framebuffer);
|
2010-10-05 18:43:03 +08:00
|
|
|
vmw_surface_unreference(&vfbs->surface);
|
2015-06-30 03:55:07 +08:00
|
|
|
if (vfbs->base.user_obj)
|
|
|
|
ttm_base_object_unref(&vfbs->base.user_obj);
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2010-10-05 18:43:03 +08:00
|
|
|
kfree(vfbs);
|
2009-12-10 08:19:58 +08:00
|
|
|
}
|
|
|
|
|
2014-01-07 00:48:10 +08:00
|
|
|
static int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
|
2010-10-05 18:43:02 +08:00
|
|
|
struct drm_file *file_priv,
|
2009-12-10 08:19:58 +08:00
|
|
|
unsigned flags, unsigned color,
|
|
|
|
struct drm_clip_rect *clips,
|
|
|
|
unsigned num_clips)
|
|
|
|
{
|
|
|
|
struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
|
|
|
|
struct vmw_framebuffer_surface *vfbs =
|
|
|
|
vmw_framebuffer_to_vfbs(framebuffer);
|
|
|
|
struct drm_clip_rect norect;
|
2011-10-05 02:13:18 +08:00
|
|
|
int ret, inc = 1;
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2015-06-26 16:23:42 +08:00
|
|
|
/* Legacy Display Unit does not support 3D */
|
|
|
|
if (dev_priv->active_display_unit == vmw_du_legacy)
|
2011-10-05 02:13:24 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2013-12-04 20:13:58 +08:00
|
|
|
drm_modeset_lock_all(dev_priv->dev);
|
|
|
|
|
2014-02-27 19:34:51 +08:00
|
|
|
ret = ttm_read_lock(&dev_priv->reservation_sem, true);
|
2013-12-04 20:13:58 +08:00
|
|
|
if (unlikely(ret != 0)) {
|
|
|
|
drm_modeset_unlock_all(dev_priv->dev);
|
2010-10-05 18:43:03 +08:00
|
|
|
return ret;
|
2013-12-04 20:13:58 +08:00
|
|
|
}
|
2010-10-05 18:43:03 +08:00
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
if (!num_clips) {
|
|
|
|
num_clips = 1;
|
|
|
|
clips = &norect;
|
|
|
|
norect.x1 = norect.y1 = 0;
|
|
|
|
norect.x2 = framebuffer->width;
|
|
|
|
norect.y2 = framebuffer->height;
|
|
|
|
} else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
|
|
|
|
num_clips /= 2;
|
|
|
|
inc = 2; /* skip source rects */
|
|
|
|
}
|
|
|
|
|
2015-06-26 16:23:42 +08:00
|
|
|
if (dev_priv->active_display_unit == vmw_du_screen_object)
|
2015-06-26 17:14:27 +08:00
|
|
|
ret = vmw_kms_sou_do_surface_dirty(dev_priv, &vfbs->base,
|
|
|
|
clips, NULL, NULL, 0, 0,
|
|
|
|
num_clips, inc, NULL);
|
2015-06-26 16:42:06 +08:00
|
|
|
else
|
2015-06-26 17:22:40 +08:00
|
|
|
ret = vmw_kms_stdu_surface_dirty(dev_priv, &vfbs->base,
|
|
|
|
clips, NULL, NULL, 0, 0,
|
|
|
|
num_clips, inc, NULL);
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2015-06-26 02:57:56 +08:00
|
|
|
vmw_fifo_flush(dev_priv, false);
|
2014-02-27 19:34:51 +08:00
|
|
|
ttm_read_unlock(&dev_priv->reservation_sem);
|
2013-12-04 20:13:58 +08:00
|
|
|
|
|
|
|
drm_modeset_unlock_all(dev_priv->dev);
|
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-26 17:14:27 +08:00
|
|
|
/**
|
|
|
|
* vmw_kms_readback - Perform a readback from the screen system to
|
|
|
|
* a dma-buffer backed framebuffer.
|
|
|
|
*
|
|
|
|
* @dev_priv: Pointer to the device private structure.
|
|
|
|
* @file_priv: Pointer to a struct drm_file identifying the caller.
|
|
|
|
* Must be set to NULL if @user_fence_rep is NULL.
|
|
|
|
* @vfb: Pointer to the dma-buffer backed framebuffer.
|
|
|
|
* @user_fence_rep: User-space provided structure for fence information.
|
|
|
|
* Must be set to non-NULL if @file_priv is non-NULL.
|
|
|
|
* @vclips: Array of clip rects.
|
|
|
|
* @num_clips: Number of clip rects in @vclips.
|
|
|
|
*
|
|
|
|
* Returns 0 on success, negative error code on failure. -ERESTARTSYS if
|
|
|
|
* interrupted.
|
|
|
|
*/
|
|
|
|
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 *vclips,
|
|
|
|
uint32_t num_clips)
|
|
|
|
{
|
|
|
|
switch (dev_priv->active_display_unit) {
|
|
|
|
case vmw_du_screen_object:
|
|
|
|
return vmw_kms_sou_readback(dev_priv, file_priv, vfb,
|
|
|
|
user_fence_rep, vclips, num_clips);
|
2015-06-26 17:22:40 +08:00
|
|
|
case vmw_du_screen_target:
|
|
|
|
return vmw_kms_stdu_dma(dev_priv, file_priv, vfb,
|
|
|
|
user_fence_rep, NULL, vclips, num_clips,
|
|
|
|
1, false, true);
|
2015-06-26 17:14:27 +08:00
|
|
|
default:
|
|
|
|
WARN_ONCE(true,
|
|
|
|
"Readback called with invalid display system.\n");
|
2015-06-26 17:22:40 +08:00
|
|
|
}
|
2015-06-26 17:14:27 +08:00
|
|
|
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-12-15 19:21:15 +08:00
|
|
|
static const struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = {
|
2009-12-10 08:19:58 +08:00
|
|
|
.destroy = vmw_framebuffer_surface_destroy,
|
|
|
|
.dirty = vmw_framebuffer_surface_dirty,
|
|
|
|
};
|
|
|
|
|
2010-10-05 18:42:59 +08:00
|
|
|
static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
|
|
|
|
struct vmw_surface *surface,
|
|
|
|
struct vmw_framebuffer **out,
|
2016-12-02 15:07:40 +08:00
|
|
|
const struct drm_mode_fb_cmd2
|
2015-06-26 16:54:28 +08:00
|
|
|
*mode_cmd,
|
|
|
|
bool is_dmabuf_proxy)
|
2009-12-10 08:19:58 +08:00
|
|
|
|
|
|
|
{
|
|
|
|
struct drm_device *dev = dev_priv->dev;
|
|
|
|
struct vmw_framebuffer_surface *vfbs;
|
2010-10-05 18:42:59 +08:00
|
|
|
enum SVGA3dSurfaceFormat format;
|
2009-12-10 08:19:58 +08:00
|
|
|
int ret;
|
2016-12-02 15:07:40 +08:00
|
|
|
struct drm_format_name_buf format_name;
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2015-06-26 16:23:42 +08:00
|
|
|
/* 3D is only supported on HWv8 and newer hosts */
|
|
|
|
if (dev_priv->active_display_unit == vmw_du_legacy)
|
2011-10-05 02:13:24 +08:00
|
|
|
return -ENOSYS;
|
|
|
|
|
2010-10-05 18:42:59 +08:00
|
|
|
/*
|
|
|
|
* Sanity checks.
|
|
|
|
*/
|
|
|
|
|
2011-11-28 20:19:12 +08:00
|
|
|
/* Surface must be marked as a scanout. */
|
|
|
|
if (unlikely(!surface->scanout))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2010-10-05 18:42:59 +08:00
|
|
|
if (unlikely(surface->mip_levels[0] != 1 ||
|
|
|
|
surface->num_sizes != 1 ||
|
2014-01-15 15:51:36 +08:00
|
|
|
surface->base_size.width < mode_cmd->width ||
|
|
|
|
surface->base_size.height < mode_cmd->height ||
|
|
|
|
surface->base_size.depth != 1)) {
|
2010-10-05 18:42:59 +08:00
|
|
|
DRM_ERROR("Incompatible surface dimensions "
|
|
|
|
"for requested mode.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-12-02 15:07:40 +08:00
|
|
|
switch (mode_cmd->pixel_format) {
|
|
|
|
case DRM_FORMAT_ARGB8888:
|
2010-10-05 18:42:59 +08:00
|
|
|
format = SVGA3D_A8R8G8B8;
|
|
|
|
break;
|
2016-12-02 15:07:40 +08:00
|
|
|
case DRM_FORMAT_XRGB8888:
|
2010-10-05 18:42:59 +08:00
|
|
|
format = SVGA3D_X8R8G8B8;
|
|
|
|
break;
|
2016-12-02 15:07:40 +08:00
|
|
|
case DRM_FORMAT_RGB565:
|
2010-10-05 18:42:59 +08:00
|
|
|
format = SVGA3D_R5G6B5;
|
|
|
|
break;
|
2016-12-02 15:07:40 +08:00
|
|
|
case DRM_FORMAT_XRGB1555:
|
2010-10-05 18:42:59 +08:00
|
|
|
format = SVGA3D_A1R5G5B5;
|
|
|
|
break;
|
|
|
|
default:
|
2016-12-02 15:07:40 +08:00
|
|
|
DRM_ERROR("Invalid pixel format: %s\n",
|
|
|
|
drm_get_format_name(mode_cmd->pixel_format, &format_name));
|
2010-10-05 18:42:59 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2015-08-11 01:39:35 +08:00
|
|
|
/*
|
|
|
|
* For DX, surface format validation is done when surface->scanout
|
|
|
|
* is set.
|
|
|
|
*/
|
|
|
|
if (!dev_priv->has_dx && format != surface->format) {
|
2010-10-05 18:42:59 +08:00
|
|
|
DRM_ERROR("Invalid surface format for requested mode.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
vfbs = kzalloc(sizeof(*vfbs), GFP_KERNEL);
|
|
|
|
if (!vfbs) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out_err1;
|
|
|
|
}
|
|
|
|
|
drm: Pass 'dev' to drm_helper_mode_fill_fb_struct()
Pass the drm_device to drm_helper_mode_fill_fb_struct() so that we can
populate fb->dev early. Will make it easier to use the fb before we
register it.
@@
identifier fb, mode_cmd;
@@
void drm_helper_mode_fill_fb_struct(
+ struct drm_device *dev,
struct drm_framebuffer *fb,
const struct drm_mode_fb_cmd2 *mode_cmd
);
@@
identifier fb, mode_cmd;
@@
void drm_helper_mode_fill_fb_struct(
+ struct drm_device *dev,
struct drm_framebuffer *fb,
const struct drm_mode_fb_cmd2 *mode_cmd
)
{ ... }
@@
function func;
identifier dev;
expression E1, E2;
@@
func(struct drm_device *dev, ...)
{
...
drm_helper_mode_fill_fb_struct(
+ dev,
E1, E2);
...
}
@@
expression E1, E2;
@@
drm_helper_mode_fill_fb_struct(
+ dev,
E1, E2);
v2: Rerun spatch due to code changes
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1481748539-18283-1-git-send-email-ville.syrjala@linux.intel.com
2016-12-15 04:48:59 +08:00
|
|
|
drm_helper_mode_fill_fb_struct(dev, &vfbs->base.base, mode_cmd);
|
2015-08-12 13:53:39 +08:00
|
|
|
vfbs->surface = vmw_surface_reference(surface);
|
2016-12-02 15:07:40 +08:00
|
|
|
vfbs->base.user_handle = mode_cmd->handles[0];
|
2015-06-26 16:54:28 +08:00
|
|
|
vfbs->is_dmabuf_proxy = is_dmabuf_proxy;
|
2010-10-05 18:43:03 +08:00
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
*out = &vfbs->base;
|
|
|
|
|
2012-12-14 06:39:01 +08:00
|
|
|
ret = drm_framebuffer_init(dev, &vfbs->base.base,
|
|
|
|
&vmw_framebuffer_surface_funcs);
|
|
|
|
if (ret)
|
2015-08-12 13:53:39 +08:00
|
|
|
goto out_err2;
|
2012-12-14 06:39:01 +08:00
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_err2:
|
2015-08-12 13:53:39 +08:00
|
|
|
vmw_surface_unreference(&surface);
|
2009-12-10 08:19:58 +08:00
|
|
|
kfree(vfbs);
|
|
|
|
out_err1:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Dmabuf framebuffer code
|
|
|
|
*/
|
|
|
|
|
2014-01-07 00:48:10 +08:00
|
|
|
static void vmw_framebuffer_dmabuf_destroy(struct drm_framebuffer *framebuffer)
|
2009-12-10 08:19:58 +08:00
|
|
|
{
|
|
|
|
struct vmw_framebuffer_dmabuf *vfbd =
|
|
|
|
vmw_framebuffer_to_vfbd(framebuffer);
|
|
|
|
|
|
|
|
drm_framebuffer_cleanup(framebuffer);
|
|
|
|
vmw_dmabuf_unreference(&vfbd->buffer);
|
2015-06-30 03:55:07 +08:00
|
|
|
if (vfbd->base.user_obj)
|
|
|
|
ttm_base_object_unref(&vfbd->base.user_obj);
|
2009-12-10 08:19:58 +08:00
|
|
|
|
|
|
|
kfree(vfbd);
|
|
|
|
}
|
|
|
|
|
2014-01-07 00:48:10 +08:00
|
|
|
static int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
|
2010-10-05 18:43:02 +08:00
|
|
|
struct drm_file *file_priv,
|
2009-12-10 08:19:58 +08:00
|
|
|
unsigned flags, unsigned color,
|
|
|
|
struct drm_clip_rect *clips,
|
|
|
|
unsigned num_clips)
|
|
|
|
{
|
|
|
|
struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
|
2011-10-05 02:13:18 +08:00
|
|
|
struct vmw_framebuffer_dmabuf *vfbd =
|
|
|
|
vmw_framebuffer_to_vfbd(framebuffer);
|
2009-12-10 08:19:58 +08:00
|
|
|
struct drm_clip_rect norect;
|
2011-10-05 02:13:18 +08:00
|
|
|
int ret, increment = 1;
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2013-12-04 20:13:58 +08:00
|
|
|
drm_modeset_lock_all(dev_priv->dev);
|
|
|
|
|
2014-02-27 19:34:51 +08:00
|
|
|
ret = ttm_read_lock(&dev_priv->reservation_sem, true);
|
2013-12-04 20:13:58 +08:00
|
|
|
if (unlikely(ret != 0)) {
|
|
|
|
drm_modeset_unlock_all(dev_priv->dev);
|
2010-10-05 18:43:03 +08:00
|
|
|
return ret;
|
2013-12-04 20:13:58 +08:00
|
|
|
}
|
2010-10-05 18:43:03 +08:00
|
|
|
|
2010-01-14 05:28:36 +08:00
|
|
|
if (!num_clips) {
|
2009-12-10 08:19:58 +08:00
|
|
|
num_clips = 1;
|
|
|
|
clips = &norect;
|
|
|
|
norect.x1 = norect.y1 = 0;
|
|
|
|
norect.x2 = framebuffer->width;
|
|
|
|
norect.y2 = framebuffer->height;
|
|
|
|
} else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
|
|
|
|
num_clips /= 2;
|
|
|
|
increment = 2;
|
|
|
|
}
|
|
|
|
|
2015-06-26 17:22:40 +08:00
|
|
|
switch (dev_priv->active_display_unit) {
|
|
|
|
case vmw_du_screen_target:
|
|
|
|
ret = vmw_kms_stdu_dma(dev_priv, NULL, &vfbd->base, NULL,
|
|
|
|
clips, NULL, num_clips, increment,
|
|
|
|
true, true);
|
|
|
|
break;
|
|
|
|
case vmw_du_screen_object:
|
2015-06-26 17:14:27 +08:00
|
|
|
ret = vmw_kms_sou_do_dmabuf_dirty(dev_priv, &vfbd->base,
|
2016-02-12 15:32:08 +08:00
|
|
|
clips, NULL, num_clips,
|
|
|
|
increment, true, NULL);
|
2015-06-26 17:22:40 +08:00
|
|
|
break;
|
2015-06-30 03:57:37 +08:00
|
|
|
case vmw_du_legacy:
|
|
|
|
ret = vmw_kms_ldu_do_dmabuf_dirty(dev_priv, &vfbd->base, 0, 0,
|
|
|
|
clips, num_clips, increment);
|
|
|
|
break;
|
2015-06-26 17:22:40 +08:00
|
|
|
default:
|
2015-06-30 03:57:37 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
WARN_ONCE(true, "Dirty called with invalid display system.\n");
|
2015-06-26 17:22:40 +08:00
|
|
|
break;
|
2011-10-05 02:13:22 +08:00
|
|
|
}
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2015-06-26 02:57:56 +08:00
|
|
|
vmw_fifo_flush(dev_priv, false);
|
2014-02-27 19:34:51 +08:00
|
|
|
ttm_read_unlock(&dev_priv->reservation_sem);
|
2013-12-04 20:13:58 +08:00
|
|
|
|
|
|
|
drm_modeset_unlock_all(dev_priv->dev);
|
|
|
|
|
2011-10-05 02:13:18 +08:00
|
|
|
return ret;
|
2009-12-10 08:19:58 +08:00
|
|
|
}
|
|
|
|
|
2015-12-15 19:21:15 +08:00
|
|
|
static const struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = {
|
2009-12-10 08:19:58 +08:00
|
|
|
.destroy = vmw_framebuffer_dmabuf_destroy,
|
|
|
|
.dirty = vmw_framebuffer_dmabuf_dirty,
|
|
|
|
};
|
|
|
|
|
2011-10-05 02:13:14 +08:00
|
|
|
/**
|
|
|
|
* Pin the dmabuffer to the start of vram.
|
|
|
|
*/
|
2015-06-28 17:50:56 +08:00
|
|
|
static int vmw_framebuffer_pin(struct vmw_framebuffer *vfb)
|
2009-12-10 08:19:58 +08:00
|
|
|
{
|
|
|
|
struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
|
2015-06-28 17:50:56 +08:00
|
|
|
struct vmw_dma_buffer *buf;
|
2009-12-10 08:19:58 +08:00
|
|
|
int ret;
|
|
|
|
|
2015-06-28 17:50:56 +08:00
|
|
|
buf = vfb->dmabuf ? vmw_framebuffer_to_vfbd(&vfb->base)->buffer :
|
|
|
|
vmw_framebuffer_to_vfbs(&vfb->base)->surface->res.backup;
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2015-06-28 17:50:56 +08:00
|
|
|
if (!buf)
|
|
|
|
return 0;
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2015-06-28 17:50:56 +08:00
|
|
|
switch (dev_priv->active_display_unit) {
|
|
|
|
case vmw_du_legacy:
|
|
|
|
vmw_overlay_pause_all(dev_priv);
|
|
|
|
ret = vmw_dmabuf_pin_in_start_of_vram(dev_priv, buf, false);
|
|
|
|
vmw_overlay_resume_all(dev_priv);
|
|
|
|
break;
|
|
|
|
case vmw_du_screen_object:
|
|
|
|
case vmw_du_screen_target:
|
|
|
|
if (vfb->dmabuf)
|
|
|
|
return vmw_dmabuf_pin_in_vram_or_gmr(dev_priv, buf,
|
|
|
|
false);
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2015-06-28 17:50:56 +08:00
|
|
|
return vmw_dmabuf_pin_in_placement(dev_priv, buf,
|
|
|
|
&vmw_mob_placement, false);
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2010-05-28 17:22:05 +08:00
|
|
|
|
2015-06-28 17:50:56 +08:00
|
|
|
return ret;
|
2009-12-10 08:19:58 +08:00
|
|
|
}
|
|
|
|
|
2015-06-28 17:50:56 +08:00
|
|
|
static int vmw_framebuffer_unpin(struct vmw_framebuffer *vfb)
|
2009-12-10 08:19:58 +08:00
|
|
|
{
|
|
|
|
struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
|
2015-06-28 17:50:56 +08:00
|
|
|
struct vmw_dma_buffer *buf;
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2015-06-28 17:50:56 +08:00
|
|
|
buf = vfb->dmabuf ? vmw_framebuffer_to_vfbd(&vfb->base)->buffer :
|
|
|
|
vmw_framebuffer_to_vfbs(&vfb->base)->surface->res.backup;
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2015-06-28 17:50:56 +08:00
|
|
|
if (WARN_ON(!buf))
|
2009-12-10 08:19:58 +08:00
|
|
|
return 0;
|
|
|
|
|
2015-06-28 17:50:56 +08:00
|
|
|
return vmw_dmabuf_unpin(dev_priv, buf, false);
|
2009-12-10 08:19:58 +08:00
|
|
|
}
|
|
|
|
|
2015-06-26 16:54:28 +08:00
|
|
|
/**
|
|
|
|
* vmw_create_dmabuf_proxy - create a proxy surface for the DMA buf
|
|
|
|
*
|
|
|
|
* @dev: DRM device
|
|
|
|
* @mode_cmd: parameters for the new surface
|
|
|
|
* @dmabuf_mob: MOB backing the DMA buf
|
|
|
|
* @srf_out: newly created surface
|
|
|
|
*
|
|
|
|
* When the content FB is a DMA buf, we create a surface as a proxy to the
|
|
|
|
* same buffer. This way we can do a surface copy rather than a surface DMA.
|
|
|
|
* This is a more efficient approach
|
|
|
|
*
|
|
|
|
* RETURNS:
|
|
|
|
* 0 on success, error code otherwise
|
|
|
|
*/
|
|
|
|
static int vmw_create_dmabuf_proxy(struct drm_device *dev,
|
2016-12-02 15:07:40 +08:00
|
|
|
const struct drm_mode_fb_cmd2 *mode_cmd,
|
2015-06-26 16:54:28 +08:00
|
|
|
struct vmw_dma_buffer *dmabuf_mob,
|
|
|
|
struct vmw_surface **srf_out)
|
|
|
|
{
|
|
|
|
uint32_t format;
|
2017-01-20 02:53:02 +08:00
|
|
|
struct drm_vmw_size content_base_size = {0};
|
2015-06-26 17:22:40 +08:00
|
|
|
struct vmw_resource *res;
|
2016-01-09 03:29:40 +08:00
|
|
|
unsigned int bytes_pp;
|
2016-12-02 15:07:40 +08:00
|
|
|
struct drm_format_name_buf format_name;
|
2015-06-26 16:54:28 +08:00
|
|
|
int ret;
|
|
|
|
|
2016-12-02 15:07:40 +08:00
|
|
|
switch (mode_cmd->pixel_format) {
|
|
|
|
case DRM_FORMAT_ARGB8888:
|
|
|
|
case DRM_FORMAT_XRGB8888:
|
2015-06-26 16:54:28 +08:00
|
|
|
format = SVGA3D_X8R8G8B8;
|
2016-01-09 03:29:40 +08:00
|
|
|
bytes_pp = 4;
|
2015-06-26 16:54:28 +08:00
|
|
|
break;
|
|
|
|
|
2016-12-02 15:07:40 +08:00
|
|
|
case DRM_FORMAT_RGB565:
|
|
|
|
case DRM_FORMAT_XRGB1555:
|
2015-06-26 16:54:28 +08:00
|
|
|
format = SVGA3D_R5G6B5;
|
2016-01-09 03:29:40 +08:00
|
|
|
bytes_pp = 2;
|
2015-06-26 16:54:28 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 8:
|
|
|
|
format = SVGA3D_P8;
|
2016-01-09 03:29:40 +08:00
|
|
|
bytes_pp = 1;
|
2015-06-26 16:54:28 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2016-12-02 15:07:40 +08:00
|
|
|
DRM_ERROR("Invalid framebuffer format %s\n",
|
|
|
|
drm_get_format_name(mode_cmd->pixel_format, &format_name));
|
2015-06-26 16:54:28 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-12-02 15:07:40 +08:00
|
|
|
content_base_size.width = mode_cmd->pitches[0] / bytes_pp;
|
2015-06-26 16:54:28 +08:00
|
|
|
content_base_size.height = mode_cmd->height;
|
|
|
|
content_base_size.depth = 1;
|
|
|
|
|
|
|
|
ret = vmw_surface_gb_priv_define(dev,
|
|
|
|
0, /* kernel visible only */
|
|
|
|
0, /* flags */
|
|
|
|
format,
|
|
|
|
true, /* can be a scanout buffer */
|
|
|
|
1, /* num of mip levels */
|
|
|
|
0,
|
2015-08-11 01:39:35 +08:00
|
|
|
0,
|
2015-06-26 16:54:28 +08:00
|
|
|
content_base_size,
|
|
|
|
srf_out);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("Failed to allocate proxy content buffer\n");
|
|
|
|
return ret;
|
2009-12-10 08:19:58 +08:00
|
|
|
}
|
|
|
|
|
2015-06-26 17:22:40 +08:00
|
|
|
res = &(*srf_out)->res;
|
2015-06-26 16:54:28 +08:00
|
|
|
|
2015-06-26 17:22:40 +08:00
|
|
|
/* Reserve and switch the backing mob. */
|
|
|
|
mutex_lock(&res->dev_priv->cmdbuf_mutex);
|
|
|
|
(void) vmw_resource_reserve(res, false, true);
|
|
|
|
vmw_dmabuf_unreference(&res->backup);
|
|
|
|
res->backup = vmw_dmabuf_reference(dmabuf_mob);
|
|
|
|
res->backup_offset = 0;
|
2015-08-11 01:39:35 +08:00
|
|
|
vmw_resource_unreserve(res, false, NULL, 0);
|
2015-06-26 17:22:40 +08:00
|
|
|
mutex_unlock(&res->dev_priv->cmdbuf_mutex);
|
2015-06-26 16:54:28 +08:00
|
|
|
|
2015-06-26 17:22:40 +08:00
|
|
|
return 0;
|
2009-12-10 08:19:58 +08:00
|
|
|
}
|
|
|
|
|
2015-06-26 16:54:28 +08:00
|
|
|
|
|
|
|
|
2010-10-05 18:42:59 +08:00
|
|
|
static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
|
|
|
|
struct vmw_dma_buffer *dmabuf,
|
|
|
|
struct vmw_framebuffer **out,
|
2016-12-02 15:07:40 +08:00
|
|
|
const struct drm_mode_fb_cmd2
|
2010-10-05 18:42:59 +08:00
|
|
|
*mode_cmd)
|
2009-12-10 08:19:58 +08:00
|
|
|
|
|
|
|
{
|
|
|
|
struct drm_device *dev = dev_priv->dev;
|
|
|
|
struct vmw_framebuffer_dmabuf *vfbd;
|
2010-10-05 18:42:59 +08:00
|
|
|
unsigned int requested_size;
|
2016-12-02 15:07:40 +08:00
|
|
|
struct drm_format_name_buf format_name;
|
2009-12-10 08:19:58 +08:00
|
|
|
int ret;
|
|
|
|
|
2016-12-02 15:07:40 +08:00
|
|
|
requested_size = mode_cmd->height * mode_cmd->pitches[0];
|
2010-10-05 18:42:59 +08:00
|
|
|
if (unlikely(requested_size > dmabuf->base.num_pages * PAGE_SIZE)) {
|
|
|
|
DRM_ERROR("Screen buffer object size is too small "
|
|
|
|
"for requested mode.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-10-05 02:13:34 +08:00
|
|
|
/* Limited framebuffer color depth support for screen objects */
|
2015-06-26 16:23:42 +08:00
|
|
|
if (dev_priv->active_display_unit == vmw_du_screen_object) {
|
2016-12-02 15:07:40 +08:00
|
|
|
switch (mode_cmd->pixel_format) {
|
|
|
|
case DRM_FORMAT_XRGB8888:
|
|
|
|
case DRM_FORMAT_ARGB8888:
|
|
|
|
break;
|
|
|
|
case DRM_FORMAT_XRGB1555:
|
|
|
|
case DRM_FORMAT_RGB565:
|
|
|
|
break;
|
2011-10-05 02:13:34 +08:00
|
|
|
default:
|
2016-12-02 15:07:40 +08:00
|
|
|
DRM_ERROR("Invalid pixel format: %s\n",
|
|
|
|
drm_get_format_name(mode_cmd->pixel_format, &format_name));
|
2011-10-05 02:13:34 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL);
|
|
|
|
if (!vfbd) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out_err1;
|
|
|
|
}
|
|
|
|
|
drm: Pass 'dev' to drm_helper_mode_fill_fb_struct()
Pass the drm_device to drm_helper_mode_fill_fb_struct() so that we can
populate fb->dev early. Will make it easier to use the fb before we
register it.
@@
identifier fb, mode_cmd;
@@
void drm_helper_mode_fill_fb_struct(
+ struct drm_device *dev,
struct drm_framebuffer *fb,
const struct drm_mode_fb_cmd2 *mode_cmd
);
@@
identifier fb, mode_cmd;
@@
void drm_helper_mode_fill_fb_struct(
+ struct drm_device *dev,
struct drm_framebuffer *fb,
const struct drm_mode_fb_cmd2 *mode_cmd
)
{ ... }
@@
function func;
identifier dev;
expression E1, E2;
@@
func(struct drm_device *dev, ...)
{
...
drm_helper_mode_fill_fb_struct(
+ dev,
E1, E2);
...
}
@@
expression E1, E2;
@@
drm_helper_mode_fill_fb_struct(
+ dev,
E1, E2);
v2: Rerun spatch due to code changes
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1481748539-18283-1-git-send-email-ville.syrjala@linux.intel.com
2016-12-15 04:48:59 +08:00
|
|
|
drm_helper_mode_fill_fb_struct(dev, &vfbd->base.base, mode_cmd);
|
2011-10-05 02:13:26 +08:00
|
|
|
vfbd->base.dmabuf = true;
|
2015-08-12 13:53:39 +08:00
|
|
|
vfbd->buffer = vmw_dmabuf_reference(dmabuf);
|
2016-12-02 15:07:40 +08:00
|
|
|
vfbd->base.user_handle = mode_cmd->handles[0];
|
2009-12-10 08:19:58 +08:00
|
|
|
*out = &vfbd->base;
|
|
|
|
|
2012-12-14 06:39:01 +08:00
|
|
|
ret = drm_framebuffer_init(dev, &vfbd->base.base,
|
|
|
|
&vmw_framebuffer_dmabuf_funcs);
|
|
|
|
if (ret)
|
2015-08-12 13:53:39 +08:00
|
|
|
goto out_err2;
|
2012-12-14 06:39:01 +08:00
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_err2:
|
2015-08-12 13:53:39 +08:00
|
|
|
vmw_dmabuf_unreference(&dmabuf);
|
2009-12-10 08:19:58 +08:00
|
|
|
kfree(vfbd);
|
|
|
|
out_err1:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-06-28 17:50:56 +08:00
|
|
|
/**
|
|
|
|
* vmw_kms_new_framebuffer - Create a new framebuffer.
|
|
|
|
*
|
|
|
|
* @dev_priv: Pointer to device private struct.
|
|
|
|
* @dmabuf: Pointer to dma buffer to wrap the kms framebuffer around.
|
|
|
|
* Either @dmabuf or @surface must be NULL.
|
|
|
|
* @surface: Pointer to a surface to wrap the kms framebuffer around.
|
|
|
|
* Either @dmabuf or @surface must be NULL.
|
|
|
|
* @only_2d: No presents will occur to this dma buffer based framebuffer. This
|
|
|
|
* Helps the code to do some important optimizations.
|
|
|
|
* @mode_cmd: Frame-buffer metadata.
|
2009-12-10 08:19:58 +08:00
|
|
|
*/
|
2015-06-28 17:50:56 +08:00
|
|
|
struct vmw_framebuffer *
|
|
|
|
vmw_kms_new_framebuffer(struct vmw_private *dev_priv,
|
|
|
|
struct vmw_dma_buffer *dmabuf,
|
|
|
|
struct vmw_surface *surface,
|
|
|
|
bool only_2d,
|
2016-12-02 15:07:40 +08:00
|
|
|
const struct drm_mode_fb_cmd2 *mode_cmd)
|
2009-12-10 08:19:58 +08:00
|
|
|
{
|
|
|
|
struct vmw_framebuffer *vfb = NULL;
|
2015-06-28 17:50:56 +08:00
|
|
|
bool is_dmabuf_proxy = false;
|
2009-12-10 08:19:58 +08:00
|
|
|
int ret;
|
|
|
|
|
2015-06-28 17:50:56 +08:00
|
|
|
/*
|
|
|
|
* We cannot use the SurfaceDMA command in an non-accelerated VM,
|
|
|
|
* therefore, wrap the DMA buf in a surface so we can use the
|
|
|
|
* SurfaceCopy command.
|
|
|
|
*/
|
|
|
|
if (dmabuf && only_2d &&
|
|
|
|
dev_priv->active_display_unit == vmw_du_screen_target) {
|
|
|
|
ret = vmw_create_dmabuf_proxy(dev_priv->dev, mode_cmd,
|
|
|
|
dmabuf, &surface);
|
|
|
|
if (ret)
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
|
|
|
|
is_dmabuf_proxy = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create the new framebuffer depending one what we have */
|
2015-08-12 13:53:39 +08:00
|
|
|
if (surface) {
|
2015-06-28 17:50:56 +08:00
|
|
|
ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb,
|
|
|
|
mode_cmd,
|
|
|
|
is_dmabuf_proxy);
|
2015-08-12 13:53:39 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* vmw_create_dmabuf_proxy() adds a reference that is no longer
|
|
|
|
* needed
|
|
|
|
*/
|
|
|
|
if (is_dmabuf_proxy)
|
|
|
|
vmw_surface_unreference(&surface);
|
|
|
|
} else if (dmabuf) {
|
2015-06-28 17:50:56 +08:00
|
|
|
ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, dmabuf, &vfb,
|
|
|
|
mode_cmd);
|
2015-08-12 13:53:39 +08:00
|
|
|
} else {
|
2015-06-28 17:50:56 +08:00
|
|
|
BUG();
|
2015-08-12 13:53:39 +08:00
|
|
|
}
|
2015-06-28 17:50:56 +08:00
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
|
|
|
|
vfb->pin = vmw_framebuffer_pin;
|
|
|
|
vfb->unpin = vmw_framebuffer_unpin;
|
|
|
|
|
|
|
|
return vfb;
|
|
|
|
}
|
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
/*
|
|
|
|
* Generic Kernel modesetting functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
|
|
|
|
struct drm_file *file_priv,
|
2016-12-02 15:07:40 +08:00
|
|
|
const struct drm_mode_fb_cmd2 *mode_cmd)
|
2009-12-10 08:19:58 +08:00
|
|
|
{
|
|
|
|
struct vmw_private *dev_priv = vmw_priv(dev);
|
|
|
|
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
|
|
|
|
struct vmw_framebuffer *vfb = NULL;
|
|
|
|
struct vmw_surface *surface = NULL;
|
|
|
|
struct vmw_dma_buffer *bo = NULL;
|
2011-10-05 02:13:32 +08:00
|
|
|
struct ttm_base_object *user_obj;
|
2009-12-10 08:19:58 +08:00
|
|
|
int ret;
|
|
|
|
|
2010-10-05 18:42:59 +08:00
|
|
|
/**
|
|
|
|
* This code should be conditioned on Screen Objects not being used.
|
|
|
|
* If screen objects are used, we can allocate a GMR to hold the
|
|
|
|
* requested framebuffer.
|
|
|
|
*/
|
|
|
|
|
2011-12-21 18:18:33 +08:00
|
|
|
if (!vmw_kms_validate_mode_vram(dev_priv,
|
2016-12-02 15:07:40 +08:00
|
|
|
mode_cmd->pitches[0],
|
|
|
|
mode_cmd->height)) {
|
2015-06-26 16:23:42 +08:00
|
|
|
DRM_ERROR("Requested mode exceed bounding box limit.\n");
|
2011-11-04 04:03:04 +08:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2010-10-05 18:42:59 +08:00
|
|
|
}
|
|
|
|
|
2011-10-05 02:13:32 +08:00
|
|
|
/*
|
|
|
|
* Take a reference on the user object of the resource
|
|
|
|
* backing the kms fb. This ensures that user-space handle
|
|
|
|
* lookups on that resource will always work as long as
|
|
|
|
* it's registered with a kms framebuffer. This is important,
|
|
|
|
* since vmw_execbuf_process identifies resources in the
|
|
|
|
* command stream using user-space handles.
|
|
|
|
*/
|
|
|
|
|
2016-12-02 15:07:40 +08:00
|
|
|
user_obj = ttm_base_object_lookup(tfile, mode_cmd->handles[0]);
|
2011-10-05 02:13:32 +08:00
|
|
|
if (unlikely(user_obj == NULL)) {
|
|
|
|
DRM_ERROR("Could not locate requested kms frame buffer.\n");
|
|
|
|
return ERR_PTR(-ENOENT);
|
|
|
|
}
|
|
|
|
|
2010-10-05 18:42:59 +08:00
|
|
|
/**
|
|
|
|
* End conditioned code.
|
|
|
|
*/
|
|
|
|
|
2011-11-28 20:19:12 +08:00
|
|
|
/* returns either a dmabuf or surface */
|
|
|
|
ret = vmw_user_lookup_handle(dev_priv, tfile,
|
2016-12-02 15:07:40 +08:00
|
|
|
mode_cmd->handles[0],
|
2011-11-28 20:19:12 +08:00
|
|
|
&surface, &bo);
|
2009-12-10 08:19:58 +08:00
|
|
|
if (ret)
|
2011-11-28 20:19:12 +08:00
|
|
|
goto err_out;
|
|
|
|
|
2015-06-28 17:50:56 +08:00
|
|
|
vfb = vmw_kms_new_framebuffer(dev_priv, bo, surface,
|
|
|
|
!(dev_priv->capabilities & SVGA_CAP_3D),
|
2016-12-02 15:07:40 +08:00
|
|
|
mode_cmd);
|
2015-06-28 17:50:56 +08:00
|
|
|
if (IS_ERR(vfb)) {
|
|
|
|
ret = PTR_ERR(vfb);
|
|
|
|
goto err_out;
|
|
|
|
}
|
2011-11-28 20:19:12 +08:00
|
|
|
|
|
|
|
err_out:
|
|
|
|
/* vmw_user_lookup_handle takes one ref so does new_fb */
|
|
|
|
if (bo)
|
|
|
|
vmw_dmabuf_unreference(&bo);
|
|
|
|
if (surface)
|
|
|
|
vmw_surface_unreference(&surface);
|
2009-12-10 08:19:58 +08:00
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
|
2011-10-05 02:13:32 +08:00
|
|
|
ttm_base_object_unref(&user_obj);
|
2010-08-08 20:36:38 +08:00
|
|
|
return ERR_PTR(ret);
|
2011-10-05 02:13:32 +08:00
|
|
|
} else
|
|
|
|
vfb->user_obj = user_obj;
|
2009-12-10 08:19:58 +08:00
|
|
|
|
|
|
|
return &vfb->base;
|
|
|
|
}
|
|
|
|
|
2012-05-17 19:27:23 +08:00
|
|
|
static const struct drm_mode_config_funcs vmw_kms_funcs = {
|
2009-12-10 08:19:58 +08:00
|
|
|
.fb_create = vmw_kms_fb_create,
|
|
|
|
};
|
|
|
|
|
2015-04-02 17:39:45 +08:00
|
|
|
static int vmw_kms_generic_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)
|
2011-10-05 02:13:26 +08:00
|
|
|
{
|
2015-06-26 17:14:27 +08:00
|
|
|
return vmw_kms_sou_do_surface_dirty(dev_priv, vfb, NULL, clips,
|
|
|
|
&surface->res, destX, destY,
|
|
|
|
num_clips, 1, NULL);
|
2011-10-05 02:13:26 +08:00
|
|
|
}
|
|
|
|
|
2015-06-26 17:22:40 +08:00
|
|
|
|
2011-10-05 02:13:26 +08:00
|
|
|
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)
|
|
|
|
{
|
2015-06-26 16:42:06 +08:00
|
|
|
int ret;
|
2011-10-05 02:13:26 +08:00
|
|
|
|
2015-06-26 17:22:40 +08:00
|
|
|
switch (dev_priv->active_display_unit) {
|
|
|
|
case vmw_du_screen_target:
|
|
|
|
ret = vmw_kms_stdu_surface_dirty(dev_priv, vfb, NULL, clips,
|
|
|
|
&surface->res, destX, destY,
|
|
|
|
num_clips, 1, NULL);
|
|
|
|
break;
|
|
|
|
case vmw_du_screen_object:
|
|
|
|
ret = vmw_kms_generic_present(dev_priv, file_priv, vfb, surface,
|
|
|
|
sid, destX, destY, clips,
|
|
|
|
num_clips);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
WARN_ONCE(true,
|
|
|
|
"Present called with invalid display system.\n");
|
|
|
|
ret = -ENOSYS;
|
|
|
|
break;
|
2011-10-05 02:13:26 +08:00
|
|
|
}
|
2015-06-26 16:42:06 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2011-10-05 02:13:26 +08:00
|
|
|
|
2015-06-26 16:42:06 +08:00
|
|
|
vmw_fifo_flush(dev_priv, false);
|
2011-10-05 02:13:26 +08:00
|
|
|
|
2015-06-26 16:42:06 +08:00
|
|
|
return 0;
|
2011-10-05 02:13:26 +08:00
|
|
|
}
|
|
|
|
|
2016-02-12 16:45:42 +08:00
|
|
|
static void
|
|
|
|
vmw_kms_create_hotplug_mode_update_property(struct vmw_private *dev_priv)
|
|
|
|
{
|
|
|
|
if (dev_priv->hotplug_mode_update_property)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dev_priv->hotplug_mode_update_property =
|
|
|
|
drm_property_create_range(dev_priv->dev,
|
|
|
|
DRM_MODE_PROP_IMMUTABLE,
|
|
|
|
"hotplug_mode_update", 0, 1);
|
|
|
|
|
|
|
|
if (!dev_priv->hotplug_mode_update_property)
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
int vmw_kms_init(struct vmw_private *dev_priv)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = dev_priv->dev;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
drm_mode_config_init(dev);
|
|
|
|
dev->mode_config.funcs = &vmw_kms_funcs;
|
2010-02-10 03:41:57 +08:00
|
|
|
dev->mode_config.min_width = 1;
|
|
|
|
dev->mode_config.min_height = 1;
|
2015-07-17 01:49:13 +08:00
|
|
|
dev->mode_config.max_width = dev_priv->texture_max_width;
|
|
|
|
dev->mode_config.max_height = dev_priv->texture_max_height;
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2016-02-12 16:45:42 +08:00
|
|
|
drm_mode_create_suggested_offset_properties(dev);
|
|
|
|
vmw_kms_create_hotplug_mode_update_property(dev_priv);
|
|
|
|
|
2015-06-26 16:42:06 +08:00
|
|
|
ret = vmw_kms_stdu_init_display(dev_priv);
|
|
|
|
if (ret) {
|
|
|
|
ret = vmw_kms_sou_init_display(dev_priv);
|
|
|
|
if (ret) /* Fallback */
|
|
|
|
ret = vmw_kms_ldu_init_display(dev_priv);
|
|
|
|
}
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2015-06-26 16:23:42 +08:00
|
|
|
return ret;
|
2009-12-10 08:19:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int vmw_kms_close(struct vmw_private *dev_priv)
|
|
|
|
{
|
2015-06-26 16:23:42 +08:00
|
|
|
int ret;
|
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
/*
|
|
|
|
* Docs says we should take the lock before calling this function
|
|
|
|
* but since it destroys encoders and our destructor calls
|
|
|
|
* drm_encoder_cleanup which takes the lock we deadlock.
|
|
|
|
*/
|
|
|
|
drm_mode_config_cleanup(dev_priv->dev);
|
2015-06-26 16:23:42 +08:00
|
|
|
if (dev_priv->active_display_unit == vmw_du_screen_object)
|
|
|
|
ret = vmw_kms_sou_close_display(dev_priv);
|
2015-06-26 16:42:06 +08:00
|
|
|
else if (dev_priv->active_display_unit == vmw_du_screen_target)
|
|
|
|
ret = vmw_kms_stdu_close_display(dev_priv);
|
2011-11-09 17:25:26 +08:00
|
|
|
else
|
2015-06-26 16:23:42 +08:00
|
|
|
ret = vmw_kms_ldu_close_display(dev_priv);
|
|
|
|
|
|
|
|
return ret;
|
2009-12-10 08:19:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
|
|
|
|
struct drm_file *file_priv)
|
|
|
|
{
|
|
|
|
struct drm_vmw_cursor_bypass_arg *arg = data;
|
|
|
|
struct vmw_display_unit *du;
|
|
|
|
struct drm_crtc *crtc;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
|
|
mutex_lock(&dev->mode_config.mutex);
|
|
|
|
if (arg->flags & DRM_VMW_CURSOR_BYPASS_ALL) {
|
|
|
|
|
|
|
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
|
|
|
du = vmw_crtc_to_du(crtc);
|
|
|
|
du->hotspot_x = arg->xhot;
|
|
|
|
du->hotspot_y = arg->yhot;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&dev->mode_config.mutex);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-07-18 11:30:02 +08:00
|
|
|
crtc = drm_crtc_find(dev, arg->crtc_id);
|
|
|
|
if (!crtc) {
|
2013-10-17 18:35:05 +08:00
|
|
|
ret = -ENOENT;
|
2009-12-10 08:19:58 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
du = vmw_crtc_to_du(crtc);
|
|
|
|
|
|
|
|
du->hotspot_x = arg->xhot;
|
|
|
|
du->hotspot_y = arg->yhot;
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&dev->mode_config.mutex);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-08-31 15:42:50 +08:00
|
|
|
int vmw_kms_write_svga(struct vmw_private *vmw_priv,
|
2010-05-28 17:21:59 +08:00
|
|
|
unsigned width, unsigned height, unsigned pitch,
|
2011-08-31 15:42:49 +08:00
|
|
|
unsigned bpp, unsigned depth)
|
2009-12-10 08:19:58 +08:00
|
|
|
{
|
2010-05-28 17:21:59 +08:00
|
|
|
if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK)
|
|
|
|
vmw_write(vmw_priv, SVGA_REG_PITCHLOCK, pitch);
|
|
|
|
else if (vmw_fifo_have_pitchlock(vmw_priv))
|
2015-10-28 17:44:04 +08:00
|
|
|
vmw_mmio_write(pitch, vmw_priv->mmio_virt +
|
|
|
|
SVGA_FIFO_PITCHLOCK);
|
2010-05-28 17:21:59 +08:00
|
|
|
vmw_write(vmw_priv, SVGA_REG_WIDTH, width);
|
|
|
|
vmw_write(vmw_priv, SVGA_REG_HEIGHT, height);
|
2011-08-31 15:42:49 +08:00
|
|
|
vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, bpp);
|
2011-08-31 15:42:50 +08:00
|
|
|
|
|
|
|
if (vmw_read(vmw_priv, SVGA_REG_DEPTH) != depth) {
|
|
|
|
DRM_ERROR("Invalid depth %u for %u bpp, host expects %u\n",
|
|
|
|
depth, bpp, vmw_read(vmw_priv, SVGA_REG_DEPTH));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2010-05-28 17:21:59 +08:00
|
|
|
}
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2010-05-28 17:21:59 +08:00
|
|
|
int vmw_kms_save_vga(struct vmw_private *vmw_priv)
|
|
|
|
{
|
2010-06-01 17:38:17 +08:00
|
|
|
struct vmw_vga_topology_state *save;
|
|
|
|
uint32_t i;
|
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
vmw_priv->vga_width = vmw_read(vmw_priv, SVGA_REG_WIDTH);
|
|
|
|
vmw_priv->vga_height = vmw_read(vmw_priv, SVGA_REG_HEIGHT);
|
2010-06-01 17:38:17 +08:00
|
|
|
vmw_priv->vga_bpp = vmw_read(vmw_priv, SVGA_REG_BITS_PER_PIXEL);
|
2010-05-28 17:21:59 +08:00
|
|
|
if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK)
|
|
|
|
vmw_priv->vga_pitchlock =
|
2010-06-01 17:38:17 +08:00
|
|
|
vmw_read(vmw_priv, SVGA_REG_PITCHLOCK);
|
2010-05-28 17:21:59 +08:00
|
|
|
else if (vmw_fifo_have_pitchlock(vmw_priv))
|
2015-10-28 17:44:04 +08:00
|
|
|
vmw_priv->vga_pitchlock = vmw_mmio_read(vmw_priv->mmio_virt +
|
|
|
|
SVGA_FIFO_PITCHLOCK);
|
2010-06-01 17:38:17 +08:00
|
|
|
|
|
|
|
if (!(vmw_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY))
|
|
|
|
return 0;
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2010-06-01 17:38:17 +08:00
|
|
|
vmw_priv->num_displays = vmw_read(vmw_priv,
|
|
|
|
SVGA_REG_NUM_GUEST_DISPLAYS);
|
|
|
|
|
2010-10-05 18:43:08 +08:00
|
|
|
if (vmw_priv->num_displays == 0)
|
|
|
|
vmw_priv->num_displays = 1;
|
|
|
|
|
2010-06-01 17:38:17 +08:00
|
|
|
for (i = 0; i < vmw_priv->num_displays; ++i) {
|
|
|
|
save = &vmw_priv->vga_save[i];
|
|
|
|
vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, i);
|
|
|
|
save->primary = vmw_read(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY);
|
|
|
|
save->pos_x = vmw_read(vmw_priv, SVGA_REG_DISPLAY_POSITION_X);
|
|
|
|
save->pos_y = vmw_read(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y);
|
|
|
|
save->width = vmw_read(vmw_priv, SVGA_REG_DISPLAY_WIDTH);
|
|
|
|
save->height = vmw_read(vmw_priv, SVGA_REG_DISPLAY_HEIGHT);
|
|
|
|
vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
|
2010-10-01 16:21:48 +08:00
|
|
|
if (i == 0 && vmw_priv->num_displays == 1 &&
|
|
|
|
save->width == 0 && save->height == 0) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It should be fairly safe to assume that these
|
|
|
|
* values are uninitialized.
|
|
|
|
*/
|
|
|
|
|
|
|
|
save->width = vmw_priv->vga_width - save->pos_x;
|
|
|
|
save->height = vmw_priv->vga_height - save->pos_y;
|
|
|
|
}
|
2010-06-01 17:38:17 +08:00
|
|
|
}
|
2010-10-01 16:21:48 +08:00
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int vmw_kms_restore_vga(struct vmw_private *vmw_priv)
|
|
|
|
{
|
2010-06-01 17:38:17 +08:00
|
|
|
struct vmw_vga_topology_state *save;
|
|
|
|
uint32_t i;
|
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
vmw_write(vmw_priv, SVGA_REG_WIDTH, vmw_priv->vga_width);
|
|
|
|
vmw_write(vmw_priv, SVGA_REG_HEIGHT, vmw_priv->vga_height);
|
2010-06-01 17:38:17 +08:00
|
|
|
vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, vmw_priv->vga_bpp);
|
2010-05-28 17:21:59 +08:00
|
|
|
if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK)
|
|
|
|
vmw_write(vmw_priv, SVGA_REG_PITCHLOCK,
|
|
|
|
vmw_priv->vga_pitchlock);
|
|
|
|
else if (vmw_fifo_have_pitchlock(vmw_priv))
|
2015-10-28 17:44:04 +08:00
|
|
|
vmw_mmio_write(vmw_priv->vga_pitchlock,
|
|
|
|
vmw_priv->mmio_virt + SVGA_FIFO_PITCHLOCK);
|
2009-12-10 08:19:58 +08:00
|
|
|
|
2010-06-01 17:38:17 +08:00
|
|
|
if (!(vmw_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < vmw_priv->num_displays; ++i) {
|
|
|
|
save = &vmw_priv->vga_save[i];
|
|
|
|
vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, i);
|
|
|
|
vmw_write(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY, save->primary);
|
|
|
|
vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_X, save->pos_x);
|
|
|
|
vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y, save->pos_y);
|
|
|
|
vmw_write(vmw_priv, SVGA_REG_DISPLAY_WIDTH, save->width);
|
|
|
|
vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, save->height);
|
|
|
|
vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
|
|
|
|
}
|
|
|
|
|
2009-12-10 08:19:58 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2010-06-01 17:54:20 +08:00
|
|
|
|
2010-10-05 18:43:04 +08:00
|
|
|
bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv,
|
|
|
|
uint32_t pitch,
|
|
|
|
uint32_t height)
|
|
|
|
{
|
2015-06-26 16:42:06 +08:00
|
|
|
return ((u64) pitch * (u64) height) < (u64)
|
|
|
|
((dev_priv->active_display_unit == vmw_du_screen_target) ?
|
|
|
|
dev_priv->prim_bb_mem : dev_priv->vram_size);
|
2010-10-05 18:43:04 +08:00
|
|
|
}
|
|
|
|
|
2011-10-17 17:59:45 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Function called by DRM code called with vbl_lock held.
|
|
|
|
*/
|
2015-09-25 00:35:31 +08:00
|
|
|
u32 vmw_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
|
2010-10-01 16:21:49 +08:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2011-10-05 02:13:20 +08:00
|
|
|
|
2011-10-17 17:59:45 +08:00
|
|
|
/**
|
|
|
|
* Function called by DRM code called with vbl_lock held.
|
|
|
|
*/
|
2015-09-25 00:35:31 +08:00
|
|
|
int vmw_enable_vblank(struct drm_device *dev, unsigned int pipe)
|
2011-10-17 17:59:45 +08:00
|
|
|
{
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function called by DRM code called with vbl_lock held.
|
|
|
|
*/
|
2015-09-25 00:35:31 +08:00
|
|
|
void vmw_disable_vblank(struct drm_device *dev, unsigned int pipe)
|
2011-10-17 17:59:45 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2011-10-05 02:13:20 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Small shared kms functions.
|
|
|
|
*/
|
|
|
|
|
2014-01-07 00:48:10 +08:00
|
|
|
static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num,
|
2011-10-05 02:13:20 +08:00
|
|
|
struct drm_vmw_rect *rects)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = dev_priv->dev;
|
|
|
|
struct vmw_display_unit *du;
|
|
|
|
struct drm_connector *con;
|
|
|
|
|
|
|
|
mutex_lock(&dev->mode_config.mutex);
|
|
|
|
|
|
|
|
#if 0
|
2011-10-05 02:13:36 +08:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
DRM_INFO("%s: new layout ", __func__);
|
|
|
|
for (i = 0; i < num; i++)
|
|
|
|
DRM_INFO("(%i, %i %ux%u) ", rects[i].x, rects[i].y,
|
|
|
|
rects[i].w, rects[i].h);
|
|
|
|
DRM_INFO("\n");
|
|
|
|
}
|
2011-10-05 02:13:20 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
list_for_each_entry(con, &dev->mode_config.connector_list, head) {
|
|
|
|
du = vmw_connector_to_du(con);
|
|
|
|
if (num > du->unit) {
|
|
|
|
du->pref_width = rects[du->unit].w;
|
|
|
|
du->pref_height = rects[du->unit].h;
|
|
|
|
du->pref_active = true;
|
2011-10-26 05:35:53 +08:00
|
|
|
du->gui_x = rects[du->unit].x;
|
|
|
|
du->gui_y = rects[du->unit].y;
|
2016-02-12 16:45:42 +08:00
|
|
|
drm_object_property_set_value
|
|
|
|
(&con->base, dev->mode_config.suggested_x_property,
|
|
|
|
du->gui_x);
|
|
|
|
drm_object_property_set_value
|
|
|
|
(&con->base, dev->mode_config.suggested_y_property,
|
|
|
|
du->gui_y);
|
2011-10-05 02:13:20 +08:00
|
|
|
} else {
|
|
|
|
du->pref_width = 800;
|
|
|
|
du->pref_height = 600;
|
|
|
|
du->pref_active = false;
|
2016-02-12 16:45:42 +08:00
|
|
|
drm_object_property_set_value
|
|
|
|
(&con->base, dev->mode_config.suggested_x_property,
|
|
|
|
0);
|
|
|
|
drm_object_property_set_value
|
|
|
|
(&con->base, dev->mode_config.suggested_y_property,
|
|
|
|
0);
|
2011-10-05 02:13:20 +08:00
|
|
|
}
|
|
|
|
con->status = vmw_du_connector_detect(con, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&dev->mode_config.mutex);
|
2016-02-12 16:45:42 +08:00
|
|
|
drm_sysfs_hotplug_event(dev);
|
2011-10-05 02:13:20 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-07 18:49:30 +08:00
|
|
|
int vmw_du_crtc_gamma_set(struct drm_crtc *crtc,
|
|
|
|
u16 *r, u16 *g, u16 *b,
|
|
|
|
uint32_t size)
|
2011-10-05 02:13:20 +08:00
|
|
|
{
|
|
|
|
struct vmw_private *dev_priv = vmw_priv(crtc->dev);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
DRM_DEBUG("%d r/g/b = 0x%04x / 0x%04x / 0x%04x\n", i,
|
|
|
|
r[i], g[i], b[i]);
|
|
|
|
vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 0, r[i] >> 8);
|
|
|
|
vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 1, g[i] >> 8);
|
|
|
|
vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 2, b[i] >> 8);
|
|
|
|
}
|
2016-06-07 18:49:30 +08:00
|
|
|
|
|
|
|
return 0;
|
2011-10-05 02:13:20 +08:00
|
|
|
}
|
|
|
|
|
2015-07-21 17:34:55 +08:00
|
|
|
int vmw_du_connector_dpms(struct drm_connector *connector, int mode)
|
2011-10-05 02:13:20 +08:00
|
|
|
{
|
2015-07-21 17:34:55 +08:00
|
|
|
return 0;
|
2011-10-05 02:13:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
enum drm_connector_status
|
|
|
|
vmw_du_connector_detect(struct drm_connector *connector, bool force)
|
|
|
|
{
|
|
|
|
uint32_t num_displays;
|
|
|
|
struct drm_device *dev = connector->dev;
|
|
|
|
struct vmw_private *dev_priv = vmw_priv(dev);
|
2011-10-26 05:35:53 +08:00
|
|
|
struct vmw_display_unit *du = vmw_connector_to_du(connector);
|
2011-10-05 02:13:20 +08:00
|
|
|
|
|
|
|
num_displays = vmw_read(dev_priv, SVGA_REG_NUM_DISPLAYS);
|
|
|
|
|
2011-10-26 05:35:53 +08:00
|
|
|
return ((vmw_connector_to_du(connector)->unit < num_displays &&
|
|
|
|
du->pref_active) ?
|
2011-10-05 02:13:20 +08:00
|
|
|
connector_status_connected : connector_status_disconnected);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct drm_display_mode vmw_kms_connector_builtin[] = {
|
|
|
|
/* 640x480@60Hz */
|
|
|
|
{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
|
|
|
|
752, 800, 0, 480, 489, 492, 525, 0,
|
|
|
|
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
|
|
|
/* 800x600@60Hz */
|
|
|
|
{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
|
|
|
|
968, 1056, 0, 600, 601, 605, 628, 0,
|
|
|
|
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
|
/* 1024x768@60Hz */
|
|
|
|
{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
|
|
|
|
1184, 1344, 0, 768, 771, 777, 806, 0,
|
|
|
|
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
|
|
|
/* 1152x864@75Hz */
|
|
|
|
{ DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
|
|
|
|
1344, 1600, 0, 864, 865, 868, 900, 0,
|
|
|
|
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
|
/* 1280x768@60Hz */
|
|
|
|
{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
|
|
|
|
1472, 1664, 0, 768, 771, 778, 798, 0,
|
|
|
|
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
|
/* 1280x800@60Hz */
|
|
|
|
{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
|
|
|
|
1480, 1680, 0, 800, 803, 809, 831, 0,
|
|
|
|
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
|
|
|
/* 1280x960@60Hz */
|
|
|
|
{ DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
|
|
|
|
1488, 1800, 0, 960, 961, 964, 1000, 0,
|
|
|
|
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
|
/* 1280x1024@60Hz */
|
|
|
|
{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
|
|
|
|
1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
|
|
|
|
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
|
/* 1360x768@60Hz */
|
|
|
|
{ DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
|
|
|
|
1536, 1792, 0, 768, 771, 777, 795, 0,
|
|
|
|
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
|
/* 1440x1050@60Hz */
|
|
|
|
{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
|
|
|
|
1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
|
|
|
|
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
|
/* 1440x900@60Hz */
|
|
|
|
{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
|
|
|
|
1672, 1904, 0, 900, 903, 909, 934, 0,
|
|
|
|
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
|
/* 1600x1200@60Hz */
|
|
|
|
{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
|
|
|
|
1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
|
|
|
|
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
|
/* 1680x1050@60Hz */
|
|
|
|
{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
|
|
|
|
1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
|
|
|
|
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
|
/* 1792x1344@60Hz */
|
|
|
|
{ DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
|
|
|
|
2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
|
|
|
|
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
|
/* 1853x1392@60Hz */
|
|
|
|
{ DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
|
|
|
|
2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
|
|
|
|
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
|
/* 1920x1200@60Hz */
|
|
|
|
{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
|
|
|
|
2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
|
|
|
|
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
|
/* 1920x1440@60Hz */
|
|
|
|
{ DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
|
|
|
|
2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
|
|
|
|
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
|
/* 2560x1600@60Hz */
|
|
|
|
{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
|
|
|
|
3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
|
|
|
|
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
|
|
|
/* Terminate */
|
|
|
|
{ DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) },
|
|
|
|
};
|
|
|
|
|
2011-11-02 16:43:10 +08:00
|
|
|
/**
|
|
|
|
* vmw_guess_mode_timing - Provide fake timings for a
|
|
|
|
* 60Hz vrefresh mode.
|
|
|
|
*
|
|
|
|
* @mode - Pointer to a struct drm_display_mode with hdisplay and vdisplay
|
|
|
|
* members filled in.
|
|
|
|
*/
|
2015-06-30 03:55:07 +08:00
|
|
|
void vmw_guess_mode_timing(struct drm_display_mode *mode)
|
2011-11-02 16:43:10 +08:00
|
|
|
{
|
|
|
|
mode->hsync_start = mode->hdisplay + 50;
|
|
|
|
mode->hsync_end = mode->hsync_start + 50;
|
|
|
|
mode->htotal = mode->hsync_end + 50;
|
|
|
|
|
|
|
|
mode->vsync_start = mode->vdisplay + 50;
|
|
|
|
mode->vsync_end = mode->vsync_start + 50;
|
|
|
|
mode->vtotal = mode->vsync_end + 50;
|
|
|
|
|
|
|
|
mode->clock = (u32)mode->htotal * (u32)mode->vtotal / 100 * 6;
|
|
|
|
mode->vrefresh = drm_mode_vrefresh(mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-05 02:13:20 +08:00
|
|
|
int vmw_du_connector_fill_modes(struct drm_connector *connector,
|
|
|
|
uint32_t max_width, uint32_t max_height)
|
|
|
|
{
|
|
|
|
struct vmw_display_unit *du = vmw_connector_to_du(connector);
|
|
|
|
struct drm_device *dev = connector->dev;
|
|
|
|
struct vmw_private *dev_priv = vmw_priv(dev);
|
|
|
|
struct drm_display_mode *mode = NULL;
|
|
|
|
struct drm_display_mode *bmode;
|
|
|
|
struct drm_display_mode prefmode = { DRM_MODE("preferred",
|
|
|
|
DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC)
|
|
|
|
};
|
|
|
|
int i;
|
drm/vmwgfx: Work around mode set failure in 2D VMs
In a low-memory 2D VM, fbdev can take up a large percentage of
available memory, making them unavailable for other DRM clients.
Since we do not take fbdev into account when filtering modes,
we end up claiming to support more modes than we actually do.
As a result, users get a black screen when setting a mode too
large for current available memory. In a low-memory VM
configuration, users can get a black screen for a mode as low
as 1024x768.
The current mode filtering mechanism keys off of
SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB, i.e. the maximum amount
of surface memory we have. Since this value is a performance
suggestion, not a hard limit, and since there should not be much
of a performance impact for a 2D VM, rather than filtering out
more modes, we will just allow ourselves to exceed the SVGA's
performance suggestion.
Also changed assumed bpp to 32 from 16 to make sure we can
actually support all the modes listed.
Signed-off-by: Sinclair Yeh <syeh@vmware.com>
Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com>
Cc: <stable@vger.kernel.org>
2016-06-30 02:29:47 +08:00
|
|
|
u32 assumed_bpp = 4;
|
2014-10-31 16:58:06 +08:00
|
|
|
|
2016-06-30 03:15:48 +08:00
|
|
|
if (dev_priv->assume_16bpp)
|
|
|
|
assumed_bpp = 2;
|
2011-10-05 02:13:20 +08:00
|
|
|
|
2015-06-26 16:42:06 +08:00
|
|
|
if (dev_priv->active_display_unit == vmw_du_screen_target) {
|
|
|
|
max_width = min(max_width, dev_priv->stdu_max_width);
|
|
|
|
max_height = min(max_height, dev_priv->stdu_max_height);
|
|
|
|
}
|
|
|
|
|
2011-10-05 02:13:20 +08:00
|
|
|
/* Add preferred mode */
|
2015-06-26 16:23:42 +08:00
|
|
|
mode = drm_mode_duplicate(dev, &prefmode);
|
|
|
|
if (!mode)
|
|
|
|
return 0;
|
|
|
|
mode->hdisplay = du->pref_width;
|
|
|
|
mode->vdisplay = du->pref_height;
|
|
|
|
vmw_guess_mode_timing(mode);
|
2011-10-05 02:13:20 +08:00
|
|
|
|
2015-06-26 16:23:42 +08:00
|
|
|
if (vmw_kms_validate_mode_vram(dev_priv,
|
|
|
|
mode->hdisplay * assumed_bpp,
|
|
|
|
mode->vdisplay)) {
|
|
|
|
drm_mode_probed_add(connector, mode);
|
|
|
|
} else {
|
|
|
|
drm_mode_destroy(dev, mode);
|
|
|
|
mode = NULL;
|
|
|
|
}
|
2011-11-04 04:03:05 +08:00
|
|
|
|
2015-06-26 16:23:42 +08:00
|
|
|
if (du->pref_mode) {
|
|
|
|
list_del_init(&du->pref_mode->head);
|
|
|
|
drm_mode_destroy(dev, du->pref_mode);
|
2011-10-05 02:13:20 +08:00
|
|
|
}
|
|
|
|
|
2015-06-26 16:23:42 +08:00
|
|
|
/* mode might be null here, this is intended */
|
|
|
|
du->pref_mode = mode;
|
|
|
|
|
2011-10-05 02:13:20 +08:00
|
|
|
for (i = 0; vmw_kms_connector_builtin[i].type != 0; i++) {
|
|
|
|
bmode = &vmw_kms_connector_builtin[i];
|
|
|
|
if (bmode->hdisplay > max_width ||
|
|
|
|
bmode->vdisplay > max_height)
|
|
|
|
continue;
|
|
|
|
|
2014-10-31 16:58:06 +08:00
|
|
|
if (!vmw_kms_validate_mode_vram(dev_priv,
|
|
|
|
bmode->hdisplay * assumed_bpp,
|
2011-10-05 02:13:20 +08:00
|
|
|
bmode->vdisplay))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
mode = drm_mode_duplicate(dev, bmode);
|
|
|
|
if (!mode)
|
|
|
|
return 0;
|
|
|
|
mode->vrefresh = drm_mode_vrefresh(mode);
|
|
|
|
|
|
|
|
drm_mode_probed_add(connector, mode);
|
|
|
|
}
|
|
|
|
|
2015-12-04 05:14:14 +08:00
|
|
|
drm_mode_connector_list_update(connector);
|
2015-06-30 03:59:58 +08:00
|
|
|
/* Move the prefered mode first, help apps pick the right mode. */
|
|
|
|
drm_mode_sort(&connector->modes);
|
2011-10-05 02:13:20 +08:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int vmw_du_connector_set_property(struct drm_connector *connector,
|
|
|
|
struct drm_property *property,
|
|
|
|
uint64_t val)
|
|
|
|
{
|
2016-02-12 16:55:45 +08:00
|
|
|
struct vmw_display_unit *du = vmw_connector_to_du(connector);
|
|
|
|
struct vmw_private *dev_priv = vmw_priv(connector->dev);
|
|
|
|
|
|
|
|
if (property == dev_priv->implicit_placement_property)
|
|
|
|
du->is_implicit = val;
|
|
|
|
|
2011-10-05 02:13:20 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2011-10-26 05:35:53 +08:00
|
|
|
|
|
|
|
|
2017-03-24 02:33:39 +08:00
|
|
|
|
2011-10-26 05:35:53 +08:00
|
|
|
int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
|
|
|
|
struct drm_file *file_priv)
|
|
|
|
{
|
|
|
|
struct vmw_private *dev_priv = vmw_priv(dev);
|
|
|
|
struct drm_vmw_update_layout_arg *arg =
|
|
|
|
(struct drm_vmw_update_layout_arg *)data;
|
|
|
|
void __user *user_rects;
|
|
|
|
struct drm_vmw_rect *rects;
|
|
|
|
unsigned rects_size;
|
|
|
|
int ret;
|
|
|
|
int i;
|
2015-07-17 01:49:13 +08:00
|
|
|
u64 total_pixels = 0;
|
2011-10-26 05:35:53 +08:00
|
|
|
struct drm_mode_config *mode_config = &dev->mode_config;
|
2015-06-26 16:23:42 +08:00
|
|
|
struct drm_vmw_rect bounding_box = {0};
|
2011-10-26 05:35:53 +08:00
|
|
|
|
|
|
|
if (!arg->num_outputs) {
|
|
|
|
struct drm_vmw_rect def_rect = {0, 0, 800, 600};
|
|
|
|
vmw_du_update_layout(dev_priv, 1, &def_rect);
|
2015-03-09 16:56:21 +08:00
|
|
|
return 0;
|
2011-10-26 05:35:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
rects_size = arg->num_outputs * sizeof(struct drm_vmw_rect);
|
2011-11-28 19:25:43 +08:00
|
|
|
rects = kcalloc(arg->num_outputs, sizeof(struct drm_vmw_rect),
|
|
|
|
GFP_KERNEL);
|
2015-03-09 16:56:21 +08:00
|
|
|
if (unlikely(!rects))
|
|
|
|
return -ENOMEM;
|
2011-10-26 05:35:53 +08:00
|
|
|
|
|
|
|
user_rects = (void __user *)(unsigned long)arg->rects;
|
|
|
|
ret = copy_from_user(rects, user_rects, rects_size);
|
|
|
|
if (unlikely(ret != 0)) {
|
|
|
|
DRM_ERROR("Failed to get rects.\n");
|
|
|
|
ret = -EFAULT;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < arg->num_outputs; ++i) {
|
2011-11-28 19:25:43 +08:00
|
|
|
if (rects[i].x < 0 ||
|
|
|
|
rects[i].y < 0 ||
|
|
|
|
rects[i].x + rects[i].w > mode_config->max_width ||
|
|
|
|
rects[i].y + rects[i].h > mode_config->max_height) {
|
2011-10-26 05:35:53 +08:00
|
|
|
DRM_ERROR("Invalid GUI layout.\n");
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out_free;
|
|
|
|
}
|
2015-06-26 16:23:42 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* bounding_box.w and bunding_box.h are used as
|
|
|
|
* lower-right coordinates
|
|
|
|
*/
|
|
|
|
if (rects[i].x + rects[i].w > bounding_box.w)
|
|
|
|
bounding_box.w = rects[i].x + rects[i].w;
|
|
|
|
|
|
|
|
if (rects[i].y + rects[i].h > bounding_box.h)
|
|
|
|
bounding_box.h = rects[i].y + rects[i].h;
|
2015-07-17 01:49:13 +08:00
|
|
|
|
|
|
|
total_pixels += (u64) rects[i].w * (u64) rects[i].h;
|
2011-10-26 05:35:53 +08:00
|
|
|
}
|
|
|
|
|
2015-07-17 01:49:13 +08:00
|
|
|
if (dev_priv->active_display_unit == vmw_du_screen_target) {
|
|
|
|
/*
|
|
|
|
* For Screen Targets, the limits for a toplogy are:
|
|
|
|
* 1. Bounding box (assuming 32bpp) must be < prim_bb_mem
|
|
|
|
* 2. Total pixels (assuming 32bpp) must be < prim_bb_mem
|
|
|
|
*/
|
2017-01-20 03:01:04 +08:00
|
|
|
u64 bb_mem = (u64) bounding_box.w * bounding_box.h * 4;
|
2015-07-17 01:49:13 +08:00
|
|
|
u64 pixel_mem = total_pixels * 4;
|
|
|
|
|
|
|
|
if (bb_mem > dev_priv->prim_bb_mem) {
|
|
|
|
DRM_ERROR("Topology is beyond supported limits.\n");
|
2015-06-26 16:42:06 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
|
2015-07-17 01:49:13 +08:00
|
|
|
if (pixel_mem > dev_priv->prim_bb_mem) {
|
|
|
|
DRM_ERROR("Combined output size too large\n");
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out_free;
|
|
|
|
}
|
2011-10-26 05:35:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
vmw_du_update_layout(dev_priv, arg->num_outputs, rects);
|
|
|
|
|
|
|
|
out_free:
|
|
|
|
kfree(rects);
|
|
|
|
return ret;
|
|
|
|
}
|
2015-06-26 17:03:53 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_kms_helper_dirty - Helper to build commands and perform actions based
|
|
|
|
* on a set of cliprects and a set of display units.
|
|
|
|
*
|
|
|
|
* @dev_priv: Pointer to a device private structure.
|
|
|
|
* @framebuffer: Pointer to the framebuffer on which to perform the actions.
|
|
|
|
* @clips: A set of struct drm_clip_rect. Either this os @vclips must be NULL.
|
|
|
|
* Cliprects are given in framebuffer coordinates.
|
|
|
|
* @vclips: A set of struct drm_vmw_rect cliprects. Either this or @clips must
|
|
|
|
* be NULL. Cliprects are given in source coordinates.
|
|
|
|
* @dest_x: X coordinate offset for the crtc / destination clip rects.
|
|
|
|
* @dest_y: Y coordinate offset for the crtc / destination clip rects.
|
|
|
|
* @num_clips: Number of cliprects in the @clips or @vclips array.
|
|
|
|
* @increment: Integer with which to increment the clip counter when looping.
|
|
|
|
* Used to skip a predetermined number of clip rects.
|
|
|
|
* @dirty: Closure structure. See the description of struct vmw_kms_dirty.
|
|
|
|
*/
|
|
|
|
int vmw_kms_helper_dirty(struct vmw_private *dev_priv,
|
|
|
|
struct vmw_framebuffer *framebuffer,
|
|
|
|
const struct drm_clip_rect *clips,
|
|
|
|
const struct drm_vmw_rect *vclips,
|
|
|
|
s32 dest_x, s32 dest_y,
|
|
|
|
int num_clips,
|
|
|
|
int increment,
|
|
|
|
struct vmw_kms_dirty *dirty)
|
|
|
|
{
|
|
|
|
struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
|
|
|
|
struct drm_crtc *crtc;
|
|
|
|
u32 num_units = 0;
|
|
|
|
u32 i, k;
|
|
|
|
|
|
|
|
dirty->dev_priv = dev_priv;
|
|
|
|
|
|
|
|
list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) {
|
|
|
|
if (crtc->primary->fb != &framebuffer->base)
|
|
|
|
continue;
|
|
|
|
units[num_units++] = vmw_crtc_to_du(crtc);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (k = 0; k < num_units; k++) {
|
|
|
|
struct vmw_display_unit *unit = units[k];
|
|
|
|
s32 crtc_x = unit->crtc.x;
|
|
|
|
s32 crtc_y = unit->crtc.y;
|
|
|
|
s32 crtc_width = unit->crtc.mode.hdisplay;
|
|
|
|
s32 crtc_height = unit->crtc.mode.vdisplay;
|
|
|
|
const struct drm_clip_rect *clips_ptr = clips;
|
|
|
|
const struct drm_vmw_rect *vclips_ptr = vclips;
|
|
|
|
|
|
|
|
dirty->unit = unit;
|
|
|
|
if (dirty->fifo_reserve_size > 0) {
|
|
|
|
dirty->cmd = vmw_fifo_reserve(dev_priv,
|
|
|
|
dirty->fifo_reserve_size);
|
|
|
|
if (!dirty->cmd) {
|
|
|
|
DRM_ERROR("Couldn't reserve fifo space "
|
|
|
|
"for dirty blits.\n");
|
2015-09-19 06:32:24 +08:00
|
|
|
return -ENOMEM;
|
2015-06-26 17:03:53 +08:00
|
|
|
}
|
|
|
|
memset(dirty->cmd, 0, dirty->fifo_reserve_size);
|
|
|
|
}
|
|
|
|
dirty->num_hits = 0;
|
|
|
|
for (i = 0; i < num_clips; i++, clips_ptr += increment,
|
|
|
|
vclips_ptr += increment) {
|
|
|
|
s32 clip_left;
|
|
|
|
s32 clip_top;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Select clip array type. Note that integer type
|
|
|
|
* in @clips is unsigned short, whereas in @vclips
|
|
|
|
* it's 32-bit.
|
|
|
|
*/
|
|
|
|
if (clips) {
|
|
|
|
dirty->fb_x = (s32) clips_ptr->x1;
|
|
|
|
dirty->fb_y = (s32) clips_ptr->y1;
|
|
|
|
dirty->unit_x2 = (s32) clips_ptr->x2 + dest_x -
|
|
|
|
crtc_x;
|
|
|
|
dirty->unit_y2 = (s32) clips_ptr->y2 + dest_y -
|
|
|
|
crtc_y;
|
|
|
|
} else {
|
|
|
|
dirty->fb_x = vclips_ptr->x;
|
|
|
|
dirty->fb_y = vclips_ptr->y;
|
|
|
|
dirty->unit_x2 = dirty->fb_x + vclips_ptr->w +
|
|
|
|
dest_x - crtc_x;
|
|
|
|
dirty->unit_y2 = dirty->fb_y + vclips_ptr->h +
|
|
|
|
dest_y - crtc_y;
|
|
|
|
}
|
|
|
|
|
|
|
|
dirty->unit_x1 = dirty->fb_x + dest_x - crtc_x;
|
|
|
|
dirty->unit_y1 = dirty->fb_y + dest_y - crtc_y;
|
|
|
|
|
|
|
|
/* Skip this clip if it's outside the crtc region */
|
|
|
|
if (dirty->unit_x1 >= crtc_width ||
|
|
|
|
dirty->unit_y1 >= crtc_height ||
|
|
|
|
dirty->unit_x2 <= 0 || dirty->unit_y2 <= 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Clip right and bottom to crtc limits */
|
|
|
|
dirty->unit_x2 = min_t(s32, dirty->unit_x2,
|
|
|
|
crtc_width);
|
|
|
|
dirty->unit_y2 = min_t(s32, dirty->unit_y2,
|
|
|
|
crtc_height);
|
|
|
|
|
|
|
|
/* Clip left and top to crtc limits */
|
|
|
|
clip_left = min_t(s32, dirty->unit_x1, 0);
|
|
|
|
clip_top = min_t(s32, dirty->unit_y1, 0);
|
|
|
|
dirty->unit_x1 -= clip_left;
|
|
|
|
dirty->unit_y1 -= clip_top;
|
|
|
|
dirty->fb_x -= clip_left;
|
|
|
|
dirty->fb_y -= clip_top;
|
|
|
|
|
|
|
|
dirty->clip(dirty);
|
|
|
|
}
|
|
|
|
|
|
|
|
dirty->fifo_commit(dirty);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_kms_helper_buffer_prepare - Reserve and validate a buffer object before
|
|
|
|
* command submission.
|
|
|
|
*
|
|
|
|
* @dev_priv. Pointer to a device private structure.
|
|
|
|
* @buf: The buffer object
|
|
|
|
* @interruptible: Whether to perform waits as interruptible.
|
|
|
|
* @validate_as_mob: Whether the buffer should be validated as a MOB. If false,
|
|
|
|
* The buffer will be validated as a GMR. Already pinned buffers will not be
|
|
|
|
* validated.
|
|
|
|
*
|
|
|
|
* Returns 0 on success, negative error code on failure, -ERESTARTSYS if
|
|
|
|
* interrupted by a signal.
|
|
|
|
*/
|
|
|
|
int vmw_kms_helper_buffer_prepare(struct vmw_private *dev_priv,
|
|
|
|
struct vmw_dma_buffer *buf,
|
|
|
|
bool interruptible,
|
|
|
|
bool validate_as_mob)
|
|
|
|
{
|
|
|
|
struct ttm_buffer_object *bo = &buf->base;
|
|
|
|
int ret;
|
|
|
|
|
2016-04-06 17:12:03 +08:00
|
|
|
ttm_bo_reserve(bo, false, false, NULL);
|
2015-06-26 17:03:53 +08:00
|
|
|
ret = vmw_validate_single_buffer(dev_priv, bo, interruptible,
|
|
|
|
validate_as_mob);
|
|
|
|
if (ret)
|
|
|
|
ttm_bo_unreserve(bo);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_kms_helper_buffer_revert - Undo the actions of
|
|
|
|
* vmw_kms_helper_buffer_prepare.
|
|
|
|
*
|
|
|
|
* @res: Pointer to the buffer object.
|
|
|
|
*
|
|
|
|
* Helper to be used if an error forces the caller to undo the actions of
|
|
|
|
* vmw_kms_helper_buffer_prepare.
|
|
|
|
*/
|
|
|
|
void vmw_kms_helper_buffer_revert(struct vmw_dma_buffer *buf)
|
|
|
|
{
|
|
|
|
if (buf)
|
|
|
|
ttm_bo_unreserve(&buf->base);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_kms_helper_buffer_finish - Unreserve and fence a buffer object after
|
|
|
|
* kms command submission.
|
|
|
|
*
|
|
|
|
* @dev_priv: Pointer to a device private structure.
|
|
|
|
* @file_priv: Pointer to a struct drm_file representing the caller's
|
|
|
|
* connection. Must be set to NULL if @user_fence_rep is NULL, and conversely
|
|
|
|
* if non-NULL, @user_fence_rep must be non-NULL.
|
|
|
|
* @buf: The buffer object.
|
|
|
|
* @out_fence: Optional pointer to a fence pointer. If non-NULL, a
|
|
|
|
* ref-counted fence pointer is returned here.
|
|
|
|
* @user_fence_rep: Optional pointer to a user-space provided struct
|
|
|
|
* drm_vmw_fence_rep. If provided, @file_priv must also be provided and the
|
|
|
|
* function copies fence data to user-space in a fail-safe manner.
|
|
|
|
*/
|
|
|
|
void vmw_kms_helper_buffer_finish(struct vmw_private *dev_priv,
|
|
|
|
struct drm_file *file_priv,
|
|
|
|
struct vmw_dma_buffer *buf,
|
|
|
|
struct vmw_fence_obj **out_fence,
|
|
|
|
struct drm_vmw_fence_rep __user *
|
|
|
|
user_fence_rep)
|
|
|
|
{
|
|
|
|
struct vmw_fence_obj *fence;
|
|
|
|
uint32_t handle;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = vmw_execbuf_fence_commands(file_priv, dev_priv, &fence,
|
|
|
|
file_priv ? &handle : NULL);
|
|
|
|
if (buf)
|
|
|
|
vmw_fence_single_bo(&buf->base, fence);
|
|
|
|
if (file_priv)
|
|
|
|
vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv),
|
|
|
|
ret, user_fence_rep, fence,
|
|
|
|
handle);
|
|
|
|
if (out_fence)
|
|
|
|
*out_fence = fence;
|
|
|
|
else
|
|
|
|
vmw_fence_obj_unreference(&fence);
|
|
|
|
|
|
|
|
vmw_kms_helper_buffer_revert(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_kms_helper_resource_revert - Undo the actions of
|
|
|
|
* vmw_kms_helper_resource_prepare.
|
|
|
|
*
|
|
|
|
* @res: Pointer to the resource. Typically a surface.
|
|
|
|
*
|
|
|
|
* Helper to be used if an error forces the caller to undo the actions of
|
|
|
|
* vmw_kms_helper_resource_prepare.
|
|
|
|
*/
|
|
|
|
void vmw_kms_helper_resource_revert(struct vmw_resource *res)
|
|
|
|
{
|
|
|
|
vmw_kms_helper_buffer_revert(res->backup);
|
2015-08-11 01:39:35 +08:00
|
|
|
vmw_resource_unreserve(res, false, NULL, 0);
|
2015-06-26 17:03:53 +08:00
|
|
|
mutex_unlock(&res->dev_priv->cmdbuf_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_kms_helper_resource_prepare - Reserve and validate a resource before
|
|
|
|
* command submission.
|
|
|
|
*
|
|
|
|
* @res: Pointer to the resource. Typically a surface.
|
|
|
|
* @interruptible: Whether to perform waits as interruptible.
|
|
|
|
*
|
|
|
|
* Reserves and validates also the backup buffer if a guest-backed resource.
|
|
|
|
* Returns 0 on success, negative error code on failure. -ERESTARTSYS if
|
|
|
|
* interrupted by a signal.
|
|
|
|
*/
|
|
|
|
int vmw_kms_helper_resource_prepare(struct vmw_resource *res,
|
|
|
|
bool interruptible)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (interruptible)
|
|
|
|
ret = mutex_lock_interruptible(&res->dev_priv->cmdbuf_mutex);
|
|
|
|
else
|
|
|
|
mutex_lock(&res->dev_priv->cmdbuf_mutex);
|
|
|
|
|
|
|
|
if (unlikely(ret != 0))
|
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
|
|
|
ret = vmw_resource_reserve(res, interruptible, false);
|
|
|
|
if (ret)
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
if (res->backup) {
|
|
|
|
ret = vmw_kms_helper_buffer_prepare(res->dev_priv, res->backup,
|
|
|
|
interruptible,
|
|
|
|
res->dev_priv->has_mob);
|
|
|
|
if (ret)
|
|
|
|
goto out_unreserve;
|
|
|
|
}
|
|
|
|
ret = vmw_resource_validate(res);
|
|
|
|
if (ret)
|
|
|
|
goto out_revert;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_revert:
|
|
|
|
vmw_kms_helper_buffer_revert(res->backup);
|
|
|
|
out_unreserve:
|
2015-08-11 01:39:35 +08:00
|
|
|
vmw_resource_unreserve(res, false, NULL, 0);
|
2015-06-26 17:03:53 +08:00
|
|
|
out_unlock:
|
|
|
|
mutex_unlock(&res->dev_priv->cmdbuf_mutex);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_kms_helper_resource_finish - Unreserve and fence a resource after
|
|
|
|
* kms command submission.
|
|
|
|
*
|
|
|
|
* @res: Pointer to the resource. Typically a surface.
|
|
|
|
* @out_fence: Optional pointer to a fence pointer. If non-NULL, a
|
|
|
|
* ref-counted fence pointer is returned here.
|
|
|
|
*/
|
|
|
|
void vmw_kms_helper_resource_finish(struct vmw_resource *res,
|
|
|
|
struct vmw_fence_obj **out_fence)
|
|
|
|
{
|
|
|
|
if (res->backup || out_fence)
|
|
|
|
vmw_kms_helper_buffer_finish(res->dev_priv, NULL, res->backup,
|
|
|
|
out_fence, NULL);
|
|
|
|
|
2015-08-11 01:39:35 +08:00
|
|
|
vmw_resource_unreserve(res, false, NULL, 0);
|
2015-06-26 17:03:53 +08:00
|
|
|
mutex_unlock(&res->dev_priv->cmdbuf_mutex);
|
|
|
|
}
|
2015-06-26 17:22:40 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_kms_update_proxy - Helper function to update a proxy surface from
|
|
|
|
* its backing MOB.
|
|
|
|
*
|
|
|
|
* @res: Pointer to the surface resource
|
|
|
|
* @clips: Clip rects in framebuffer (surface) space.
|
|
|
|
* @num_clips: Number of clips in @clips.
|
|
|
|
* @increment: Integer with which to increment the clip counter when looping.
|
|
|
|
* Used to skip a predetermined number of clip rects.
|
|
|
|
*
|
|
|
|
* This function makes sure the proxy surface is updated from its backing MOB
|
|
|
|
* using the region given by @clips. The surface resource @res and its backing
|
|
|
|
* MOB needs to be reserved and validated on call.
|
|
|
|
*/
|
|
|
|
int vmw_kms_update_proxy(struct vmw_resource *res,
|
|
|
|
const struct drm_clip_rect *clips,
|
|
|
|
unsigned num_clips,
|
|
|
|
int increment)
|
|
|
|
{
|
|
|
|
struct vmw_private *dev_priv = res->dev_priv;
|
|
|
|
struct drm_vmw_size *size = &vmw_res_to_srf(res)->base_size;
|
|
|
|
struct {
|
|
|
|
SVGA3dCmdHeader header;
|
|
|
|
SVGA3dCmdUpdateGBImage body;
|
|
|
|
} *cmd;
|
|
|
|
SVGA3dBox *box;
|
|
|
|
size_t copy_size = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!clips)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) * num_clips);
|
|
|
|
if (!cmd) {
|
|
|
|
DRM_ERROR("Couldn't reserve fifo space for proxy surface "
|
|
|
|
"update.\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < num_clips; ++i, clips += increment, ++cmd) {
|
|
|
|
box = &cmd->body.box;
|
|
|
|
|
|
|
|
cmd->header.id = SVGA_3D_CMD_UPDATE_GB_IMAGE;
|
|
|
|
cmd->header.size = sizeof(cmd->body);
|
|
|
|
cmd->body.image.sid = res->id;
|
|
|
|
cmd->body.image.face = 0;
|
|
|
|
cmd->body.image.mipmap = 0;
|
|
|
|
|
|
|
|
if (clips->x1 > size->width || clips->x2 > size->width ||
|
|
|
|
clips->y1 > size->height || clips->y2 > size->height) {
|
|
|
|
DRM_ERROR("Invalid clips outsize of framebuffer.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
box->x = clips->x1;
|
|
|
|
box->y = clips->y1;
|
|
|
|
box->z = 0;
|
|
|
|
box->w = clips->x2 - clips->x1;
|
|
|
|
box->h = clips->y2 - clips->y1;
|
|
|
|
box->d = 1;
|
|
|
|
|
|
|
|
copy_size += sizeof(*cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
vmw_fifo_commit(dev_priv, copy_size);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2015-06-30 03:55:07 +08:00
|
|
|
|
|
|
|
int vmw_kms_fbdev_init_data(struct vmw_private *dev_priv,
|
|
|
|
unsigned unit,
|
|
|
|
u32 max_width,
|
|
|
|
u32 max_height,
|
|
|
|
struct drm_connector **p_con,
|
|
|
|
struct drm_crtc **p_crtc,
|
|
|
|
struct drm_display_mode **p_mode)
|
|
|
|
{
|
|
|
|
struct drm_connector *con;
|
|
|
|
struct vmw_display_unit *du;
|
|
|
|
struct drm_display_mode *mode;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
list_for_each_entry(con, &dev_priv->dev->mode_config.connector_list,
|
|
|
|
head) {
|
|
|
|
if (i == unit)
|
|
|
|
break;
|
|
|
|
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i != unit) {
|
|
|
|
DRM_ERROR("Could not find initial display unit.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (list_empty(&con->modes))
|
|
|
|
(void) vmw_du_connector_fill_modes(con, max_width, max_height);
|
|
|
|
|
|
|
|
if (list_empty(&con->modes)) {
|
|
|
|
DRM_ERROR("Could not find initial display mode.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
du = vmw_connector_to_du(con);
|
|
|
|
*p_con = con;
|
|
|
|
*p_crtc = &du->crtc;
|
|
|
|
|
|
|
|
list_for_each_entry(mode, &con->modes, head) {
|
|
|
|
if (mode->type & DRM_MODE_TYPE_PREFERRED)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode->type & DRM_MODE_TYPE_PREFERRED)
|
|
|
|
*p_mode = mode;
|
|
|
|
else {
|
|
|
|
WARN_ONCE(true, "Could not find initial preferred mode.\n");
|
|
|
|
*p_mode = list_first_entry(&con->modes,
|
|
|
|
struct drm_display_mode,
|
|
|
|
head);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-02-12 16:00:26 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_kms_del_active - unregister a crtc binding to the implicit framebuffer
|
|
|
|
*
|
|
|
|
* @dev_priv: Pointer to a device private struct.
|
|
|
|
* @du: The display unit of the crtc.
|
|
|
|
*/
|
|
|
|
void vmw_kms_del_active(struct vmw_private *dev_priv,
|
|
|
|
struct vmw_display_unit *du)
|
|
|
|
{
|
2016-05-03 17:24:35 +08:00
|
|
|
mutex_lock(&dev_priv->global_kms_state_mutex);
|
2016-02-12 16:00:26 +08:00
|
|
|
if (du->active_implicit) {
|
|
|
|
if (--(dev_priv->num_implicit) == 0)
|
|
|
|
dev_priv->implicit_fb = NULL;
|
|
|
|
du->active_implicit = false;
|
|
|
|
}
|
2016-05-03 17:24:35 +08:00
|
|
|
mutex_unlock(&dev_priv->global_kms_state_mutex);
|
2016-02-12 16:00:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_kms_add_active - register a crtc binding to an implicit framebuffer
|
|
|
|
*
|
|
|
|
* @vmw_priv: Pointer to a device private struct.
|
|
|
|
* @du: The display unit of the crtc.
|
|
|
|
* @vfb: The implicit framebuffer
|
|
|
|
*
|
|
|
|
* Registers a binding to an implicit framebuffer.
|
|
|
|
*/
|
|
|
|
void vmw_kms_add_active(struct vmw_private *dev_priv,
|
|
|
|
struct vmw_display_unit *du,
|
|
|
|
struct vmw_framebuffer *vfb)
|
|
|
|
{
|
2016-05-03 17:24:35 +08:00
|
|
|
mutex_lock(&dev_priv->global_kms_state_mutex);
|
2016-02-12 16:00:26 +08:00
|
|
|
WARN_ON_ONCE(!dev_priv->num_implicit && dev_priv->implicit_fb);
|
|
|
|
|
|
|
|
if (!du->active_implicit && du->is_implicit) {
|
|
|
|
dev_priv->implicit_fb = vfb;
|
|
|
|
du->active_implicit = true;
|
|
|
|
dev_priv->num_implicit++;
|
|
|
|
}
|
2016-05-03 17:24:35 +08:00
|
|
|
mutex_unlock(&dev_priv->global_kms_state_mutex);
|
2016-02-12 16:00:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_kms_screen_object_flippable - Check whether we can page-flip a crtc.
|
|
|
|
*
|
|
|
|
* @dev_priv: Pointer to device-private struct.
|
|
|
|
* @crtc: The crtc we want to flip.
|
|
|
|
*
|
|
|
|
* Returns true or false depending whether it's OK to flip this crtc
|
|
|
|
* based on the criterion that we must not have more than one implicit
|
|
|
|
* frame-buffer at any one time.
|
|
|
|
*/
|
|
|
|
bool vmw_kms_crtc_flippable(struct vmw_private *dev_priv,
|
|
|
|
struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
2016-05-03 17:24:35 +08:00
|
|
|
bool ret;
|
2016-02-12 16:00:26 +08:00
|
|
|
|
2016-05-03 17:24:35 +08:00
|
|
|
mutex_lock(&dev_priv->global_kms_state_mutex);
|
|
|
|
ret = !du->is_implicit || dev_priv->num_implicit == 1;
|
|
|
|
mutex_unlock(&dev_priv->global_kms_state_mutex);
|
2016-02-12 16:00:26 +08:00
|
|
|
|
2016-05-03 17:24:35 +08:00
|
|
|
return ret;
|
2016-02-12 16:00:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_kms_update_implicit_fb - Update the implicit fb.
|
|
|
|
*
|
|
|
|
* @dev_priv: Pointer to device-private struct.
|
|
|
|
* @crtc: The crtc the new implicit frame-buffer is bound to.
|
|
|
|
*/
|
|
|
|
void vmw_kms_update_implicit_fb(struct vmw_private *dev_priv,
|
|
|
|
struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
|
|
|
struct vmw_framebuffer *vfb;
|
|
|
|
|
2016-05-03 17:24:35 +08:00
|
|
|
mutex_lock(&dev_priv->global_kms_state_mutex);
|
2016-02-12 16:00:26 +08:00
|
|
|
|
|
|
|
if (!du->is_implicit)
|
2016-05-03 17:24:35 +08:00
|
|
|
goto out_unlock;
|
2016-02-12 16:00:26 +08:00
|
|
|
|
|
|
|
vfb = vmw_framebuffer_to_vfb(crtc->primary->fb);
|
|
|
|
WARN_ON_ONCE(dev_priv->num_implicit != 1 &&
|
|
|
|
dev_priv->implicit_fb != vfb);
|
|
|
|
|
|
|
|
dev_priv->implicit_fb = vfb;
|
2016-05-03 17:24:35 +08:00
|
|
|
out_unlock:
|
|
|
|
mutex_unlock(&dev_priv->global_kms_state_mutex);
|
2016-02-12 16:00:26 +08:00
|
|
|
}
|
2016-02-12 16:55:45 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* vmw_kms_create_implicit_placement_proparty - Set up the implicit placement
|
|
|
|
* property.
|
|
|
|
*
|
|
|
|
* @dev_priv: Pointer to a device private struct.
|
|
|
|
* @immutable: Whether the property is immutable.
|
|
|
|
*
|
|
|
|
* Sets up the implicit placement property unless it's already set up.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv,
|
|
|
|
bool immutable)
|
|
|
|
{
|
|
|
|
if (dev_priv->implicit_placement_property)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dev_priv->implicit_placement_property =
|
|
|
|
drm_property_create_range(dev_priv->dev,
|
|
|
|
immutable ?
|
|
|
|
DRM_MODE_PROP_IMMUTABLE : 0,
|
|
|
|
"implicit_placement", 0, 1);
|
|
|
|
|
|
|
|
}
|