diff options
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r-- | drivers/gpu/drm/vmwgfx/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 32 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 81 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_fence.c | 173 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c | 23 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_irq.c | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 120 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 106 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c | 2 |
11 files changed, 433 insertions, 149 deletions
diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile index 1a3cb6816d1c..4505e17df3f5 100644 --- a/drivers/gpu/drm/vmwgfx/Makefile +++ b/drivers/gpu/drm/vmwgfx/Makefile @@ -4,6 +4,6 @@ ccflags-y := -Iinclude/drm vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ vmwgfx_fb.o vmwgfx_ioctl.o vmwgfx_resource.o vmwgfx_buffer.o \ vmwgfx_fifo.o vmwgfx_irq.o vmwgfx_ldu.o vmwgfx_ttm_glue.o \ - vmwgfx_overlay.o + vmwgfx_overlay.o vmwgfx_fence.o obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 0c9c0811f42d..7597323d5a5a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -318,6 +318,15 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) goto out_err3; } + /* Need mmio memory to check for fifo pitchlock cap. */ + if (!(dev_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY) && + !(dev_priv->capabilities & SVGA_CAP_PITCHLOCK) && + !vmw_fifo_have_pitchlock(dev_priv)) { + ret = -ENOSYS; + DRM_ERROR("Hardware has no pitchlock\n"); + goto out_err4; + } + dev_priv->tdev = ttm_object_device_init (dev_priv->mem_global_ref.object, 12); @@ -399,8 +408,6 @@ static int vmw_driver_unload(struct drm_device *dev) { struct vmw_private *dev_priv = vmw_priv(dev); - DRM_INFO(VMWGFX_DRIVER_NAME " unload.\n"); - unregister_pm_notifier(&dev_priv->pm_nb); vmw_fb_close(dev_priv); @@ -546,7 +553,6 @@ static int vmw_master_create(struct drm_device *dev, { struct vmw_master *vmaster; - DRM_INFO("Master create.\n"); vmaster = kzalloc(sizeof(*vmaster), GFP_KERNEL); if (unlikely(vmaster == NULL)) return -ENOMEM; @@ -563,7 +569,6 @@ static void vmw_master_destroy(struct drm_device *dev, { struct vmw_master *vmaster = vmw_master(master); - DRM_INFO("Master destroy.\n"); master->driver_priv = NULL; kfree(vmaster); } @@ -579,8 +584,6 @@ static int vmw_master_set(struct drm_device *dev, struct vmw_master *vmaster = vmw_master(file_priv->master); int ret = 0; - DRM_INFO("Master set.\n"); - if (active) { BUG_ON(active != &dev_priv->fbdev_master); ret = ttm_vt_lock(&active->lock, false, vmw_fp->tfile); @@ -622,8 +625,6 @@ static void vmw_master_drop(struct drm_device *dev, struct vmw_master *vmaster = vmw_master(file_priv->master); int ret; - DRM_INFO("Master drop.\n"); - /** * Make sure the master doesn't disappear while we have * it locked. diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 356dc935ec13..1341adef408d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -41,7 +41,7 @@ #define VMWGFX_DRIVER_DATE "20100209" #define VMWGFX_DRIVER_MAJOR 1 -#define VMWGFX_DRIVER_MINOR 0 +#define VMWGFX_DRIVER_MINOR 1 #define VMWGFX_DRIVER_PATCHLEVEL 0 #define VMWGFX_FILE_PAGE_OFFSET 0x00100000 #define VMWGFX_FIFO_STATIC_SIZE (1024*1024) @@ -102,6 +102,13 @@ struct vmw_surface { struct vmw_cursor_snooper snooper; }; +struct vmw_fence_queue { + struct list_head head; + struct timespec lag; + struct timespec lag_time; + spinlock_t lock; +}; + struct vmw_fifo_state { unsigned long reserved_size; __le32 *dynamic_buffer; @@ -115,6 +122,7 @@ struct vmw_fifo_state { uint32_t capabilities; struct mutex fifo_mutex; struct rw_semaphore rwsem; + struct vmw_fence_queue fence_queue; }; struct vmw_relocation { @@ -179,6 +187,7 @@ struct vmw_private { uint32_t vga_red_mask; uint32_t vga_blue_mask; uint32_t vga_green_mask; + uint32_t vga_pitchlock; /* * Framebuffer info. @@ -393,6 +402,7 @@ extern int vmw_fifo_send_fence(struct vmw_private *dev_priv, extern void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason); extern int vmw_fifo_mmap(struct file *filp, struct vm_area_struct *vma); extern bool vmw_fifo_have_3d(struct vmw_private *dev_priv); +extern bool vmw_fifo_have_pitchlock(struct vmw_private *dev_priv); /** * TTM glue - vmwgfx_ttm_glue.c @@ -441,6 +451,23 @@ extern int vmw_fallback_wait(struct vmw_private *dev_priv, uint32_t sequence, bool interruptible, unsigned long timeout); +extern void vmw_update_sequence(struct vmw_private *dev_priv, + struct vmw_fifo_state *fifo_state); + + +/** + * Rudimentary fence objects currently used only for throttling - + * vmwgfx_fence.c + */ + +extern void vmw_fence_queue_init(struct vmw_fence_queue *queue); +extern void vmw_fence_queue_takedown(struct vmw_fence_queue *queue); +extern int vmw_fence_push(struct vmw_fence_queue *queue, + uint32_t sequence); +extern int vmw_fence_pull(struct vmw_fence_queue *queue, + uint32_t signaled_sequence); +extern int vmw_wait_lag(struct vmw_private *dev_priv, + struct vmw_fence_queue *queue, uint32_t us); /** * Kernel framebuffer - vmwgfx_fb.c @@ -466,6 +493,9 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf, struct ttm_object_file *tfile, struct ttm_buffer_object *bo, SVGA3dCmdHeader *header); +void vmw_kms_write_svga(struct vmw_private *vmw_priv, + unsigned width, unsigned height, unsigned pitch, + unsigned bbp, unsigned depth); /** * Overlay control - vmwgfx_overlay.c diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index dbd36b8910cf..bdd67cf83315 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -669,6 +669,15 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data, goto out_err; vmw_apply_relocations(sw_context); + + if (arg->throttle_us) { + ret = vmw_wait_lag(dev_priv, &dev_priv->fifo.fence_queue, + arg->throttle_us); + + if (unlikely(ret != 0)) + goto out_err; + } + vmw_fifo_commit(dev_priv, arg->command_size); ret = vmw_fifo_send_fence(dev_priv, &sequence); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index 7421aaad8d09..181f47222580 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -132,16 +132,14 @@ static int vmw_fb_check_var(struct fb_var_screeninfo *var, return -EINVAL; } - /* without multimon its hard to resize */ - if (!(vmw_priv->capabilities & SVGA_CAP_MULTIMON) && - (var->xres != par->max_width || - var->yres != par->max_height)) { - DRM_ERROR("Tried to resize, but we don't have multimon\n"); + if (!(vmw_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY) && + (var->xoffset != 0 || var->yoffset != 0)) { + DRM_ERROR("Can not handle panning without display topology\n"); return -EINVAL; } - if (var->xres > par->max_width || - var->yres > par->max_height) { + if ((var->xoffset + var->xres) > par->max_width || + (var->yoffset + var->yres) > par->max_height) { DRM_ERROR("Requested geom can not fit in framebuffer\n"); return -EINVAL; } @@ -154,8 +152,7 @@ static int vmw_fb_set_par(struct fb_info *info) struct vmw_fb_par *par = info->par; struct vmw_private *vmw_priv = par->vmw_priv; - if (vmw_priv->capabilities & SVGA_CAP_MULTIMON) { - vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1); + if (vmw_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY) { vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, 0); vmw_write(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true); vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_X, 0); @@ -164,18 +161,11 @@ static int vmw_fb_set_par(struct fb_info *info) vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, 0); vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID); - vmw_write(vmw_priv, SVGA_REG_ENABLE, 1); - vmw_write(vmw_priv, SVGA_REG_WIDTH, par->max_width); - vmw_write(vmw_priv, SVGA_REG_HEIGHT, par->max_height); - vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, par->bpp); - vmw_write(vmw_priv, SVGA_REG_DEPTH, par->depth); - vmw_write(vmw_priv, SVGA_REG_RED_MASK, 0x00ff0000); - vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, 0x0000ff00); - vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, 0x000000ff); + vmw_kms_write_svga(vmw_priv, info->var.xres, info->var.yres, + info->fix.line_length, + par->bpp, par->depth); /* TODO check if pitch and offset changes */ - - vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1); vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, 0); vmw_write(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true); vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_X, info->var.xoffset); @@ -183,13 +173,19 @@ static int vmw_fb_set_par(struct fb_info *info) vmw_write(vmw_priv, SVGA_REG_DISPLAY_WIDTH, info->var.xres); vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, info->var.yres); vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID); + vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1); } else { - vmw_write(vmw_priv, SVGA_REG_WIDTH, info->var.xres); - vmw_write(vmw_priv, SVGA_REG_HEIGHT, info->var.yres); + vmw_kms_write_svga(vmw_priv, info->var.xres, info->var.yres, + info->fix.line_length, + par->bpp, par->depth); - /* TODO check if pitch and offset changes */ } + /* This is really helpful since if this fails the user + * can probably not see anything on the screen. + */ + WARN_ON(vmw_read(vmw_priv, SVGA_REG_FB_OFFSET) != 0); + return 0; } @@ -416,48 +412,23 @@ int vmw_fb_init(struct vmw_private *vmw_priv) unsigned fb_bbp, fb_depth, fb_offset, fb_pitch, fb_size; int ret; + /* XXX These shouldn't be hardcoded. */ initial_width = 800; initial_height = 600; fb_bbp = 32; fb_depth = 24; - if (vmw_priv->capabilities & SVGA_CAP_MULTIMON) { - fb_width = min(vmw_priv->fb_max_width, (unsigned)2048); - fb_height = min(vmw_priv->fb_max_height, (unsigned)2048); - } else { - fb_width = min(vmw_priv->fb_max_width, initial_width); - fb_height = min(vmw_priv->fb_max_height, initial_height); - } + /* XXX As shouldn't these be as well. */ + fb_width = min(vmw_priv->fb_max_width, (unsigned)2048); + fb_height = min(vmw_priv->fb_max_height, (unsigned)2048); initial_width = min(fb_width, initial_width); initial_height = min(fb_height, initial_height); - vmw_write(vmw_priv, SVGA_REG_WIDTH, fb_width); - vmw_write(vmw_priv, SVGA_REG_HEIGHT, fb_height); - vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, fb_bbp); - vmw_write(vmw_priv, SVGA_REG_DEPTH, fb_depth); - vmw_write(vmw_priv, SVGA_REG_RED_MASK, 0x00ff0000); - vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, 0x0000ff00); - vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, 0x000000ff); - - fb_size = vmw_read(vmw_priv, SVGA_REG_FB_SIZE); + fb_pitch = fb_width * fb_bbp / 8; + fb_size = fb_pitch * fb_height; fb_offset = vmw_read(vmw_priv, SVGA_REG_FB_OFFSET); - fb_pitch = vmw_read(vmw_priv, SVGA_REG_BYTES_PER_LINE); - - DRM_DEBUG("width %u\n", vmw_read(vmw_priv, SVGA_REG_MAX_WIDTH)); - DRM_DEBUG("height %u\n", vmw_read(vmw_priv, SVGA_REG_MAX_HEIGHT)); - DRM_DEBUG("width %u\n", vmw_read(vmw_priv, SVGA_REG_WIDTH)); - DRM_DEBUG("height %u\n", vmw_read(vmw_priv, SVGA_REG_HEIGHT)); - DRM_DEBUG("bpp %u\n", vmw_read(vmw_priv, SVGA_REG_BITS_PER_PIXEL)); - DRM_DEBUG("depth %u\n", vmw_read(vmw_priv, SVGA_REG_DEPTH)); - DRM_DEBUG("bpl %u\n", vmw_read(vmw_priv, SVGA_REG_BYTES_PER_LINE)); - DRM_DEBUG("r mask %08x\n", vmw_read(vmw_priv, SVGA_REG_RED_MASK)); - DRM_DEBUG("g mask %08x\n", vmw_read(vmw_priv, SVGA_REG_GREEN_MASK)); - DRM_DEBUG("b mask %08x\n", vmw_read(vmw_priv, SVGA_REG_BLUE_MASK)); - DRM_DEBUG("fb_offset 0x%08x\n", fb_offset); - DRM_DEBUG("fb_pitch %u\n", fb_pitch); - DRM_DEBUG("fb_size %u kiB\n", fb_size / 1024); info = framebuffer_alloc(sizeof(*par), device); if (!info) @@ -659,6 +630,10 @@ int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv, goto err_unlock; ret = ttm_bo_validate(bo, &ne_placement, false, false, false); + + /* Could probably bug on */ + WARN_ON(bo->offset != 0); + ttm_bo_unreserve(bo); err_unlock: ttm_write_unlock(&vmw_priv->active_master->lock); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c new file mode 100644 index 000000000000..61eacc1b5ca3 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -0,0 +1,173 @@ +/************************************************************************** + * + * Copyright (C) 2010 VMware, Inc., Palo Alto, CA., USA + * 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_drv.h" + +struct vmw_fence { + struct list_head head; + uint32_t sequence; + struct timespec submitted; +}; + +void vmw_fence_queue_init(struct vmw_fence_queue *queue) +{ + INIT_LIST_HEAD(&queue->head); + queue->lag = ns_to_timespec(0); + getrawmonotonic(&queue->lag_time); + spin_lock_init(&queue->lock); +} + +void vmw_fence_queue_takedown(struct vmw_fence_queue *queue) +{ + struct vmw_fence *fence, *next; + + spin_lock(&queue->lock); + list_for_each_entry_safe(fence, next, &queue->head, head) { + kfree(fence); + } + spin_unlock(&queue->lock); +} + +int vmw_fence_push(struct vmw_fence_queue *queue, + uint32_t sequence) +{ + struct vmw_fence *fence = kmalloc(sizeof(*fence), GFP_KERNEL); + + if (unlikely(!fence)) + return -ENOMEM; + + fence->sequence = sequence; + getrawmonotonic(&fence->submitted); + spin_lock(&queue->lock); + list_add_tail(&fence->head, &queue->head); + spin_unlock(&queue->lock); + + return 0; +} + +int vmw_fence_pull(struct vmw_fence_queue *queue, + uint32_t signaled_sequence) +{ + struct vmw_fence *fence, *next; + struct timespec now; + bool updated = false; + + spin_lock(&queue->lock); + getrawmonotonic(&now); + + if (list_empty(&queue->head)) { + queue->lag = ns_to_timespec(0); + queue->lag_time = now; + updated = true; + goto out_unlock; + } + + list_for_each_entry_safe(fence, next, &queue->head, head) { + if (signaled_sequence - fence->sequence > (1 << 30)) + continue; + + queue->lag = timespec_sub(now, fence->submitted); + queue->lag_time = now; + updated = true; + list_del(&fence->head); + kfree(fence); + } + +out_unlock: + spin_unlock(&queue->lock); + + return (updated) ? 0 : -EBUSY; +} + +static struct timespec vmw_timespec_add(struct timespec t1, + struct timespec t2) +{ + t1.tv_sec += t2.tv_sec; + t1.tv_nsec += t2.tv_nsec; + if (t1.tv_nsec >= 1000000000L) { + t1.tv_sec += 1; + t1.tv_nsec -= 1000000000L; + } + + return t1; +} + +static struct timespec vmw_fifo_lag(struct vmw_fence_queue *queue) +{ + struct timespec now; + + spin_lock(&queue->lock); + getrawmonotonic(&now); + queue->lag = vmw_timespec_add(queue->lag, + timespec_sub(now, queue->lag_time)); + queue->lag_time = now; + spin_unlock(&queue->lock); + return queue->lag; +} + + +static bool vmw_lag_lt(struct vmw_fence_queue *queue, + uint32_t us) +{ + struct timespec lag, cond; + + cond = ns_to_timespec((s64) us * 1000); + lag = vmw_fifo_lag(queue); + return (timespec_compare(&lag, &cond) < 1); +} + +int vmw_wait_lag(struct vmw_private *dev_priv, + struct vmw_fence_queue *queue, uint32_t us) +{ + struct vmw_fence *fence; + uint32_t sequence; + int ret; + + while (!vmw_lag_lt(queue, us)) { + spin_lock(&queue->lock); + if (list_empty(&queue->head)) + sequence = atomic_read(&dev_priv->fence_seq); + else { + fence = list_first_entry(&queue->head, + struct vmw_fence, head); + sequence = fence->sequence; + } + spin_unlock(&queue->lock); + + ret = vmw_wait_fence(dev_priv, false, sequence, true, + 3*HZ); + + if (unlikely(ret != 0)) + return ret; + + (void) vmw_fence_pull(queue, sequence); + } + return 0; +} + + diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c index 39d43a01d846..e6a1eb7ea954 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c @@ -34,6 +34,9 @@ bool vmw_fifo_have_3d(struct vmw_private *dev_priv) __le32 __iomem *fifo_mem = dev_priv->mmio_virt; uint32_t fifo_min, hwversion; + if (!(dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO)) + return false; + fifo_min = ioread32(fifo_mem + SVGA_FIFO_MIN); if (fifo_min <= SVGA_FIFO_3D_HWVERSION * sizeof(unsigned int)) return false; @@ -48,6 +51,21 @@ bool vmw_fifo_have_3d(struct vmw_private *dev_priv) return true; } +bool vmw_fifo_have_pitchlock(struct vmw_private *dev_priv) +{ + __le32 __iomem *fifo_mem = dev_priv->mmio_virt; + uint32_t caps; + + if (!(dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO)) + return false; + + caps = ioread32(fifo_mem + SVGA_FIFO_CAPABILITIES); + if (caps & SVGA_FIFO_CAP_PITCHLOCK) + return true; + + return false; +} + int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) { __le32 __iomem *fifo_mem = dev_priv->mmio_virt; @@ -120,7 +138,7 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) atomic_set(&dev_priv->fence_seq, dev_priv->last_read_sequence); iowrite32(dev_priv->last_read_sequence, fifo_mem + SVGA_FIFO_FENCE); - + vmw_fence_queue_init(&fifo->fence_queue); return vmw_fifo_send_fence(dev_priv, &dummy); out_err: vfree(fifo->static_buffer); @@ -159,6 +177,7 @@ void vmw_fifo_release(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) dev_priv->enable_state); mutex_unlock(&dev_priv->hw_mutex); + vmw_fence_queue_takedown(&fifo->fence_queue); if (likely(fifo->last_buffer != NULL)) { vfree(fifo->last_buffer); @@ -484,6 +503,8 @@ int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *sequence) fifo_state->last_buffer_add = true; vmw_fifo_commit(dev_priv, bytes); fifo_state->last_buffer_add = false; + (void) vmw_fence_push(&fifo_state->fence_queue, *sequence); + vmw_update_sequence(dev_priv, fifo_state); out_err: return ret; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c index 4d7cb5393860..e92298a6a383 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c @@ -64,22 +64,33 @@ static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t sequence) return (busy == 0); } +void vmw_update_sequence(struct vmw_private *dev_priv, + struct vmw_fifo_state *fifo_state) +{ + __le32 __iomem *fifo_mem = dev_priv->mmio_virt; + + uint32_t sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE); + + if (dev_priv->last_read_sequence != sequence) { + dev_priv->last_read_sequence = sequence; + vmw_fence_pull(&fifo_state->fence_queue, sequence); + } +} bool vmw_fence_signaled(struct vmw_private *dev_priv, uint32_t sequence) { - __le32 __iomem *fifo_mem = dev_priv->mmio_virt; struct vmw_fifo_state *fifo_state; bool ret; if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) return true; - dev_priv->last_read_sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE); + fifo_state = &dev_priv->fifo; + vmw_update_sequence(dev_priv, fifo_state); if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) return true; - fifo_state = &dev_priv->fifo; if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE) && vmw_fifo_idle(dev_priv, sequence)) return true; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index bbc7c4c30bc7..b78dcf001858 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -30,6 +30,8 @@ /* Might need a hrtimer here? */ #define VMWGFX_PRESENT_RATE ((HZ / 60 > 0) ? HZ / 60 : 1) +static int vmw_surface_dmabuf_pin(struct vmw_framebuffer *vfb); +static int vmw_surface_dmabuf_unpin(struct vmw_framebuffer *vfb); void vmw_display_unit_cleanup(struct vmw_display_unit *du) { @@ -326,6 +328,7 @@ int vmw_framebuffer_create_handle(struct drm_framebuffer *fb, struct vmw_framebuffer_surface { struct vmw_framebuffer base; struct vmw_surface *surface; + struct vmw_dma_buffer *buffer; struct delayed_work d_work; struct mutex work_lock; bool present_fs; @@ -500,8 +503,8 @@ int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, vfbs->base.base.depth = 24; vfbs->base.base.width = width; vfbs->base.base.height = height; - vfbs->base.pin = NULL; - vfbs->base.unpin = NULL; + vfbs->base.pin = &vmw_surface_dmabuf_pin; + vfbs->base.unpin = &vmw_surface_dmabuf_unpin; vfbs->surface = surface; mutex_init(&vfbs->work_lock); INIT_DELAYED_WORK(&vfbs->d_work, &vmw_framebuffer_present_fs_callback); @@ -589,6 +592,40 @@ static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = { .create_handle = vmw_framebuffer_create_handle, }; +static int vmw_surface_dmabuf_pin(struct vmw_framebuffer *vfb) +{ + struct vmw_private *dev_priv = vmw_priv(vfb->base.dev); + struct vmw_framebuffer_surface *vfbs = + vmw_framebuffer_to_vfbs(&vfb->base); + unsigned long size = vfbs->base.base.pitch * vfbs->base.base.height; + int ret; + + vfbs->buffer = kzalloc(sizeof(*vfbs->buffer), GFP_KERNEL); + if (unlikely(vfbs->buffer == NULL)) + return -ENOMEM; + + vmw_overlay_pause_all(dev_priv); + ret = vmw_dmabuf_init(dev_priv, vfbs->buffer, size, + &vmw_vram_ne_placement, + false, &vmw_dmabuf_bo_free); + vmw_overlay_resume_all(dev_priv); + + return ret; +} + +static int vmw_surface_dmabuf_unpin(struct vmw_framebuffer *vfb) +{ + struct ttm_buffer_object *bo; + struct vmw_framebuffer_surface *vfbs = + vmw_framebuffer_to_vfbs(&vfb->base); + + bo = &vfbs->buffer->base; + ttm_bo_unref(&bo); + vfbs->buffer = NULL; + + return 0; +} + static int vmw_framebuffer_dmabuf_pin(struct vmw_framebuffer *vfb) { struct vmw_private *dev_priv = vmw_priv(vfb->base.dev); @@ -596,33 +633,15 @@ static int vmw_framebuffer_dmabuf_pin(struct vmw_framebuffer *vfb) vmw_framebuffer_to_vfbd(&vfb->base); int ret; + vmw_overlay_pause_all(dev_priv); ret = vmw_dmabuf_to_start_of_vram(dev_priv, vfbd->buffer); - if (dev_priv->capabilities & SVGA_CAP_MULTIMON) { - vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1); - vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, 0); - vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true); - vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, 0); - vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, 0); - vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, 0); - vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, 0); - vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID); - - vmw_write(dev_priv, SVGA_REG_ENABLE, 1); - vmw_write(dev_priv, SVGA_REG_WIDTH, vfb->base.width); - vmw_write(dev_priv, SVGA_REG_HEIGHT, vfb->base.height); - vmw_write(dev_priv, SVGA_REG_BITS_PER_PIXEL, vfb->base.bits_per_pixel); - vmw_write(dev_priv, SVGA_REG_DEPTH, vfb->base.depth); - vmw_write(dev_priv, SVGA_REG_RED_MASK, 0x00ff0000); - vmw_write(dev_priv, SVGA_REG_GREEN_MASK, 0x0000ff00); - vmw_write(dev_priv, SVGA_REG_BLUE_MASK, 0x000000ff); - } else - WARN_ON(true); - vmw_overlay_resume_all(dev_priv); + WARN_ON(ret != 0); + return 0; } @@ -668,7 +687,7 @@ int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, /* XXX get the first 3 from the surface info */ vfbd->base.base.bits_per_pixel = 32; - vfbd->base.base.pitch = width * 32 / 4; + vfbd->base.base.pitch = width * vfbd->base.base.bits_per_pixel / 8; vfbd->base.base.depth = 24; vfbd->base.base.width = width; vfbd->base.base.height = height; @@ -765,8 +784,9 @@ int vmw_kms_init(struct vmw_private *dev_priv) dev->mode_config.funcs = &vmw_kms_funcs; dev->mode_config.min_width = 1; dev->mode_config.min_height = 1; - dev->mode_config.max_width = dev_priv->fb_max_width; - dev->mode_config.max_height = dev_priv->fb_max_height; + /* assumed largest fb size */ + dev->mode_config.max_width = 8192; + dev->mode_config.max_height = 8192; ret = vmw_kms_init_legacy_display_system(dev_priv); @@ -826,24 +846,25 @@ out: return ret; } -int vmw_kms_save_vga(struct vmw_private *vmw_priv) +void vmw_kms_write_svga(struct vmw_private *vmw_priv, + unsigned width, unsigned height, unsigned pitch, + unsigned bbp, unsigned depth) { - /* - * setup a single multimon monitor with the size - * of 0x0, this stops the UI from resizing when we - * change the framebuffer size - */ - if (vmw_priv->capabilities & SVGA_CAP_MULTIMON) { - vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1); - vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, 0); - vmw_write(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true); - vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_X, 0); - vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y, 0); - vmw_write(vmw_priv, SVGA_REG_DISPLAY_WIDTH, 0); - vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, 0); - vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID); - } + if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK) + vmw_write(vmw_priv, SVGA_REG_PITCHLOCK, pitch); + else if (vmw_fifo_have_pitchlock(vmw_priv)) + iowrite32(pitch, vmw_priv->mmio_virt + SVGA_FIFO_PITCHLOCK); + vmw_write(vmw_priv, SVGA_REG_WIDTH, width); + vmw_write(vmw_priv, SVGA_REG_HEIGHT, height); + vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, bbp); + vmw_write(vmw_priv, SVGA_REG_DEPTH, depth); + vmw_write(vmw_priv, SVGA_REG_RED_MASK, 0x00ff0000); + vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, 0x0000ff00); + vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, 0x000000ff); +} +int vmw_kms_save_vga(struct vmw_private *vmw_priv) +{ vmw_priv->vga_width = vmw_read(vmw_priv, SVGA_REG_WIDTH); vmw_priv->vga_height = vmw_read(vmw_priv, SVGA_REG_HEIGHT); vmw_priv->vga_bpp = vmw_read(vmw_priv, SVGA_REG_BITS_PER_PIXEL); @@ -852,6 +873,12 @@ int vmw_kms_save_vga(struct vmw_private *vmw_priv) vmw_priv->vga_red_mask = vmw_read(vmw_priv, SVGA_REG_RED_MASK); vmw_priv->vga_green_mask = vmw_read(vmw_priv, SVGA_REG_GREEN_MASK); vmw_priv->vga_blue_mask = vmw_read(vmw_priv, SVGA_REG_BLUE_MASK); + if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK) + vmw_priv->vga_pitchlock = + vmw_read(vmw_priv, SVGA_REG_PITCHLOCK); + else if (vmw_fifo_have_pitchlock(vmw_priv)) + vmw_priv->vga_pitchlock = + ioread32(vmw_priv->mmio_virt + SVGA_FIFO_PITCHLOCK); return 0; } @@ -866,9 +893,12 @@ int vmw_kms_restore_vga(struct vmw_private *vmw_priv) vmw_write(vmw_priv, SVGA_REG_RED_MASK, vmw_priv->vga_red_mask); vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, vmw_priv->vga_green_mask); vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, vmw_priv->vga_blue_mask); - - /* TODO check for multimon */ - vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 0); + 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)) + iowrite32(vmw_priv->vga_pitchlock, + vmw_priv->mmio_virt + SVGA_FIFO_PITCHLOCK); return 0; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 90891593bf6c..f7094dde18f9 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -38,6 +38,7 @@ struct vmw_legacy_display { struct list_head active; unsigned num_active; + unsigned last_num_active; struct vmw_framebuffer *fb; }; @@ -49,8 +50,6 @@ struct vmw_legacy_display_unit { struct vmw_display_unit base; struct list_head active; - - unsigned unit; }; static void vmw_ldu_destroy(struct vmw_legacy_display_unit *ldu) @@ -88,23 +87,44 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) { struct vmw_legacy_display *lds = dev_priv->ldu_priv; struct vmw_legacy_display_unit *entry; - struct drm_crtc *crtc; + struct drm_framebuffer *fb = NULL; + struct drm_crtc *crtc = NULL; int i = 0; - /* to stop the screen from changing size on resize */ - vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 0); - for (i = 0; i < lds->num_active; i++) { - vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, i); - vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, !i); - vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, 0); - vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, 0); - vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, 0); - vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, 0); - vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID); + /* If there is no display topology the host just assumes + * that the guest will set the same layout as the host. + */ + if (!(dev_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY)) { + int w = 0, h = 0; + list_for_each_entry(entry, &lds->active, active) { + crtc = &entry->base.crtc; + w = max(w, crtc->x + crtc->mode.hdisplay); + h = max(h, crtc->y + crtc->mode.vdisplay); + i++; + } + + if (crtc == NULL) + return 0; + fb = entry->base.crtc.fb; + + vmw_kms_write_svga(dev_priv, w, h, fb->pitch, + fb->bits_per_pixel, fb->depth); + + return 0; + } + + if (!list_empty(&lds->active)) { + entry = list_entry(lds->active.next, typeof(*entry), active); + fb = entry->base.crtc.fb; + + vmw_kms_write_svga(dev_priv, fb->width, fb->height, fb->pitch, + fb->bits_per_pixel, fb->depth); } - /* Now set the mode */ - vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, lds->num_active); + /* Make sure we always show something. */ + vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, + lds->num_active ? lds->num_active : 1); + i = 0; list_for_each_entry(entry, &lds->active, active) { crtc = &entry->base.crtc; @@ -120,6 +140,10 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) i++; } + BUG_ON(i != lds->num_active); + + lds->last_num_active = lds->num_active; + return 0; } @@ -130,6 +154,7 @@ static int vmw_ldu_del_active(struct vmw_private *vmw_priv, if (list_empty(&ldu->active)) return 0; + /* Must init otherwise list_empty(&ldu->active) will not work. */ list_del_init(&ldu->active); if (--(ld->num_active) == 0) { BUG_ON(!ld->fb); @@ -149,24 +174,29 @@ static int vmw_ldu_add_active(struct vmw_private *vmw_priv, struct vmw_legacy_display_unit *entry; struct list_head *at; + BUG_ON(!ld->num_active && ld->fb); + if (vfb != ld->fb) { + if (ld->fb && ld->fb->unpin) + ld->fb->unpin(ld->fb); + if (vfb->pin) + vfb->pin(vfb); + ld->fb = vfb; + } + if (!list_empty(&ldu->active)) return 0; at = &ld->active; list_for_each_entry(entry, &ld->active, active) { - if (entry->unit > ldu->unit) + if (entry->base.unit > ldu->base.unit) break; at = &entry->active; } list_add(&ldu->active, at); - if (ld->num_active++ == 0) { - BUG_ON(ld->fb); - if (vfb->pin) - vfb->pin(vfb); - ld->fb = vfb; - } + + ld->num_active++; return 0; } @@ -208,6 +238,8 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set) /* ldu only supports one fb active at the time */ if (dev_priv->ldu_priv->fb && vfb && + !(dev_priv->ldu_priv->num_active == 1 && + !list_empty(&ldu->active)) && dev_priv->ldu_priv->fb != vfb) { DRM_ERROR("Multiple framebuffers not supported\n"); return -EINVAL; @@ -443,18 +475,16 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) if (!ldu) return -ENOMEM; - ldu->unit = unit; + ldu->base.unit = unit; crtc = &ldu->base.crtc; encoder = &ldu->base.encoder; connector = &ldu->base.connector; + INIT_LIST_HEAD(&ldu->active); + drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, DRM_MODE_CONNECTOR_LVDS); - /* Initial status */ - if (unit == 0) - connector->status = connector_status_connected; - else - connector->status = connector_status_disconnected; + connector->status = vmw_ldu_connector_detect(connector); drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs, DRM_MODE_ENCODER_LVDS); @@ -462,8 +492,6 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) encoder->possible_crtcs = (1 << unit); encoder->possible_clones = 0; - INIT_LIST_HEAD(&ldu->active); - drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs); drm_connector_attach_property(connector, @@ -487,18 +515,22 @@ int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv) INIT_LIST_HEAD(&dev_priv->ldu_priv->active); dev_priv->ldu_priv->num_active = 0; + dev_priv->ldu_priv->last_num_active = 0; dev_priv->ldu_priv->fb = NULL; drm_mode_create_dirty_info_property(dev_priv->dev); vmw_ldu_init(dev_priv, 0); - vmw_ldu_init(dev_priv, 1); - vmw_ldu_init(dev_priv, 2); - vmw_ldu_init(dev_priv, 3); - vmw_ldu_init(dev_priv, 4); - vmw_ldu_init(dev_priv, 5); - vmw_ldu_init(dev_priv, 6); - vmw_ldu_init(dev_priv, 7); + /* for old hardware without multimon only enable one display */ + if (dev_priv->capabilities & SVGA_CAP_MULTIMON) { + vmw_ldu_init(dev_priv, 1); + vmw_ldu_init(dev_priv, 2); + vmw_ldu_init(dev_priv, 3); + vmw_ldu_init(dev_priv, 4); + vmw_ldu_init(dev_priv, 5); + vmw_ldu_init(dev_priv, 6); + vmw_ldu_init(dev_priv, 7); + } return 0; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c index ad566c85b075..df2036ed18d5 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c @@ -358,6 +358,8 @@ static int vmw_overlay_update_stream(struct vmw_private *dev_priv, if (stream->buf != buf) stream->buf = vmw_dmabuf_reference(buf); stream->saved = *arg; + /* stream is no longer stopped/paused */ + stream->paused = false; return 0; } |