Commit fb1d9738 authored by Jakob Bornecrantz's avatar Jakob Bornecrantz Committed by Dave Airlie

drm/vmwgfx: Add DRM driver for VMware Virtual GPU

This commit adds the vmwgfx driver for the VWware Virtual GPU aka SVGA.
The driver is under staging the same as Nouveau and Radeon KMS. Hopefully
the 2D ioctls are bug free and don't need changing, so that part of the
API should be stable. But there there is a pretty big chance that the 3D API
will change in the future.
Signed-off-by: default avatarThomas Hellström <thellstrom@vmware.com>
Signed-off-by: default avatarJakob Bornecrantz <jakob@vmware.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 632f6117
......@@ -30,4 +30,5 @@ obj-$(CONFIG_DRM_I830) += i830/
obj-$(CONFIG_DRM_I915) += i915/
obj-$(CONFIG_DRM_SIS) += sis/
obj-$(CONFIG_DRM_SAVAGE)+= savage/
obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
obj-$(CONFIG_DRM_VIA) +=via/
config DRM_VMWGFX
tristate "DRM driver for VMware Virtual GPU"
depends on DRM && PCI
select FB_DEFERRED_IO
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select DRM_TTM
help
KMS enabled DRM driver for SVGA2 virtual hardware.
If unsure say n. The compiled module will be
called vmwgfx.ko
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
obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o
/**************************************************************************
*
* Copyright © 2009 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"
#include "ttm/ttm_bo_driver.h"
#include "ttm/ttm_placement.h"
static uint32_t vram_placement_flags = TTM_PL_FLAG_VRAM |
TTM_PL_FLAG_CACHED;
static uint32_t vram_ne_placement_flags = TTM_PL_FLAG_VRAM |
TTM_PL_FLAG_CACHED |
TTM_PL_FLAG_NO_EVICT;
static uint32_t sys_placement_flags = TTM_PL_FLAG_SYSTEM |
TTM_PL_FLAG_CACHED;
struct ttm_placement vmw_vram_placement = {
.fpfn = 0,
.lpfn = 0,
.num_placement = 1,
.placement = &vram_placement_flags,
.num_busy_placement = 1,
.busy_placement = &vram_placement_flags
};
struct ttm_placement vmw_vram_ne_placement = {
.fpfn = 0,
.lpfn = 0,
.num_placement = 1,
.placement = &vram_ne_placement_flags,
.num_busy_placement = 1,
.busy_placement = &vram_ne_placement_flags
};
struct ttm_placement vmw_sys_placement = {
.fpfn = 0,
.lpfn = 0,
.num_placement = 1,
.placement = &sys_placement_flags,
.num_busy_placement = 1,
.busy_placement = &sys_placement_flags
};
struct vmw_ttm_backend {
struct ttm_backend backend;
};
static int vmw_ttm_populate(struct ttm_backend *backend,
unsigned long num_pages, struct page **pages,
struct page *dummy_read_page)
{
return 0;
}
static int vmw_ttm_bind(struct ttm_backend *backend, struct ttm_mem_reg *bo_mem)
{
return 0;
}
static int vmw_ttm_unbind(struct ttm_backend *backend)
{
return 0;
}
static void vmw_ttm_clear(struct ttm_backend *backend)
{
}
static void vmw_ttm_destroy(struct ttm_backend *backend)
{
struct vmw_ttm_backend *vmw_be =
container_of(backend, struct vmw_ttm_backend, backend);
kfree(vmw_be);
}
static struct ttm_backend_func vmw_ttm_func = {
.populate = vmw_ttm_populate,
.clear = vmw_ttm_clear,
.bind = vmw_ttm_bind,
.unbind = vmw_ttm_unbind,
.destroy = vmw_ttm_destroy,
};
struct ttm_backend *vmw_ttm_backend_init(struct ttm_bo_device *bdev)
{
struct vmw_ttm_backend *vmw_be;
vmw_be = kmalloc(sizeof(*vmw_be), GFP_KERNEL);
if (!vmw_be)
return NULL;
vmw_be->backend.func = &vmw_ttm_func;
return &vmw_be->backend;
}
int vmw_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags)
{
return 0;
}
int vmw_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
struct ttm_mem_type_manager *man)
{
struct vmw_private *dev_priv =
container_of(bdev, struct vmw_private, bdev);
switch (type) {
case TTM_PL_SYSTEM:
/* System memory */
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
man->available_caching = TTM_PL_MASK_CACHING;
man->default_caching = TTM_PL_FLAG_CACHED;
break;
case TTM_PL_VRAM:
/* "On-card" video ram */
man->gpu_offset = 0;
man->io_offset = dev_priv->vram_start;
man->io_size = dev_priv->vram_size;
man->flags = TTM_MEMTYPE_FLAG_FIXED |
TTM_MEMTYPE_FLAG_NEEDS_IOREMAP | TTM_MEMTYPE_FLAG_MAPPABLE;
man->io_addr = NULL;
man->available_caching = TTM_PL_MASK_CACHING;
man->default_caching = TTM_PL_FLAG_WC;
break;
default:
DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
return -EINVAL;
}
return 0;
}
void vmw_evict_flags(struct ttm_buffer_object *bo,
struct ttm_placement *placement)
{
*placement = vmw_sys_placement;
}
/**
* FIXME: Proper access checks on buffers.
*/
static int vmw_verify_access(struct ttm_buffer_object *bo, struct file *filp)
{
return 0;
}
/**
* FIXME: We're using the old vmware polling method to sync.
* Do this with fences instead.
*/
static void *vmw_sync_obj_ref(void *sync_obj)
{
return sync_obj;
}
static void vmw_sync_obj_unref(void **sync_obj)
{
*sync_obj = NULL;
}
static int vmw_sync_obj_flush(void *sync_obj, void *sync_arg)
{
struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
mutex_lock(&dev_priv->hw_mutex);
vmw_write(dev_priv, SVGA_REG_SYNC, SVGA_SYNC_GENERIC);
mutex_unlock(&dev_priv->hw_mutex);
return 0;
}
static bool vmw_sync_obj_signaled(void *sync_obj, void *sync_arg)
{
struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
uint32_t sequence = (unsigned long) sync_obj;
return vmw_fence_signaled(dev_priv, sequence);
}
static int vmw_sync_obj_wait(void *sync_obj, void *sync_arg,
bool lazy, bool interruptible)
{
struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
uint32_t sequence = (unsigned long) sync_obj;
return vmw_wait_fence(dev_priv, false, sequence, false, 3*HZ);
}
struct ttm_bo_driver vmw_bo_driver = {
.create_ttm_backend_entry = vmw_ttm_backend_init,
.invalidate_caches = vmw_invalidate_caches,
.init_mem_type = vmw_init_mem_type,
.evict_flags = vmw_evict_flags,
.move = NULL,
.verify_access = vmw_verify_access,
.sync_obj_signaled = vmw_sync_obj_signaled,
.sync_obj_wait = vmw_sync_obj_wait,
.sync_obj_flush = vmw_sync_obj_flush,
.sync_obj_unref = vmw_sync_obj_unref,
.sync_obj_ref = vmw_sync_obj_ref
};
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/**************************************************************************
*
* Copyright © 2009 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"
#include "drmP.h"
#include "ttm/ttm_bo_driver.h"
/**
* FIXME: Adjust to the ttm lowmem / highmem storage to minimize
* the number of used descriptors.
*/
static int vmw_gmr_build_descriptors(struct list_head *desc_pages,
struct page *pages[],
unsigned long num_pages)
{
struct page *page, *next;
struct svga_guest_mem_descriptor *page_virtual = NULL;
struct svga_guest_mem_descriptor *desc_virtual = NULL;
unsigned int desc_per_page;
unsigned long prev_pfn;
unsigned long pfn;
int ret;
desc_per_page = PAGE_SIZE /
sizeof(struct svga_guest_mem_descriptor) - 1;
while (likely(num_pages != 0)) {
page = alloc_page(__GFP_HIGHMEM);
if (unlikely(page == NULL)) {
ret = -ENOMEM;
goto out_err;
}
list_add_tail(&page->lru, desc_pages);
/*
* Point previous page terminating descriptor to this
* page before unmapping it.
*/
if (likely(page_virtual != NULL)) {
desc_virtual->ppn = page_to_pfn(page);
kunmap_atomic(page_virtual, KM_USER0);
}
page_virtual = kmap_atomic(page, KM_USER0);
desc_virtual = page_virtual - 1;
prev_pfn = ~(0UL);
while (likely(num_pages != 0)) {
pfn = page_to_pfn(*pages);
if (pfn != prev_pfn + 1) {
if (desc_virtual - page_virtual ==
desc_per_page - 1)
break;
(++desc_virtual)->ppn = cpu_to_le32(pfn);
desc_virtual->num_pages = cpu_to_le32(1);
} else {
uint32_t tmp =
le32_to_cpu(desc_virtual->num_pages);
desc_virtual->num_pages = cpu_to_le32(tmp + 1);
}
prev_pfn = pfn;
--num_pages;
++pages;
}
(++desc_virtual)->ppn = cpu_to_le32(0);
desc_virtual->num_pages = cpu_to_le32(0);
}
if (likely(page_virtual != NULL))
kunmap_atomic(page_virtual, KM_USER0);
return 0;
out_err:
list_for_each_entry_safe(page, next, desc_pages, lru) {
list_del_init(&page->lru);
__free_page(page);
}
return ret;
}
static inline void vmw_gmr_free_descriptors(struct list_head *desc_pages)
{
struct page *page, *next;
list_for_each_entry_safe(page, next, desc_pages, lru) {
list_del_init(&page->lru);
__free_page(page);
}
}
static void vmw_gmr_fire_descriptors(struct vmw_private *dev_priv,
int gmr_id, struct list_head *desc_pages)
{
struct page *page;
if (unlikely(list_empty(desc_pages)))
return;
page = list_entry(desc_pages->next, struct page, lru);
mutex_lock(&dev_priv->hw_mutex);
vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id);
wmb();
vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, page_to_pfn(page));
mb();
mutex_unlock(&dev_priv->hw_mutex);
}
/**
* FIXME: Adjust to the ttm lowmem / highmem storage to minimize
* the number of used descriptors.
*/
static unsigned long vmw_gmr_count_descriptors(struct page *pages[],
unsigned long num_pages)
{
unsigned long prev_pfn = ~(0UL);
unsigned long pfn;
unsigned long descriptors = 0;
while (num_pages--) {
pfn = page_to_pfn(*pages++);
if (prev_pfn + 1 != pfn)
++descriptors;
prev_pfn = pfn;
}
return descriptors;
}
int vmw_gmr_bind(struct vmw_private *dev_priv,
struct ttm_buffer_object *bo)
{
struct ttm_tt *ttm = bo->ttm;
unsigned long descriptors;
int ret;
uint32_t id;
struct list_head desc_pages;
if (!(dev_priv->capabilities & SVGA_CAP_GMR))
return -EINVAL;
ret = ttm_tt_populate(ttm);
if (unlikely(ret != 0))
return ret;
descriptors = vmw_gmr_count_descriptors(ttm->pages, ttm->num_pages);
if (unlikely(descriptors > dev_priv->max_gmr_descriptors))
return -EINVAL;
INIT_LIST_HEAD(&desc_pages);
ret = vmw_gmr_build_descriptors(&desc_pages, ttm->pages,
ttm->num_pages);
if (unlikely(ret != 0))
return ret;
ret = vmw_gmr_id_alloc(dev_priv, &id);
if (unlikely(ret != 0))
goto out_no_id;
vmw_gmr_fire_descriptors(dev_priv, id, &desc_pages);
vmw_gmr_free_descriptors(&desc_pages);
vmw_dmabuf_set_gmr(bo, id);
return 0;
out_no_id:
vmw_gmr_free_descriptors(&desc_pages);
return ret;
}
void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id)
{
mutex_lock(&dev_priv->hw_mutex);
vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id);
wmb();
vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, 0);
mb();
mutex_unlock(&dev_priv->hw_mutex);
}
/**************************************************************************
*
* Copyright © 2009 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"
#include "vmwgfx_drm.h"
int vmw_getparam_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct drm_vmw_getparam_arg *param =
(struct drm_vmw_getparam_arg *)data;
switch (param->param) {
case DRM_VMW_PARAM_NUM_STREAMS:
param->value = vmw_overlay_num_overlays(dev_priv);
break;
case DRM_VMW_PARAM_NUM_FREE_STREAMS:
param->value = vmw_overlay_num_free_overlays(dev_priv);
break;
case DRM_VMW_PARAM_3D:
param->value = dev_priv->capabilities & SVGA_CAP_3D ? 1 : 0;
break;
case DRM_VMW_PARAM_FIFO_OFFSET:
param->value = dev_priv->mmio_start;
break;
default:
DRM_ERROR("Illegal vmwgfx get param request: %d\n",
param->param);
return -EINVAL;
}
return 0;
}
int vmw_fifo_debug_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
struct drm_vmw_fifo_debug_arg *arg =
(struct drm_vmw_fifo_debug_arg *)data;
__le32 __user *buffer = (__le32 __user *)
(unsigned long)arg->debug_buffer;
if (unlikely(fifo_state->last_buffer == NULL))
return -EINVAL;
if (arg->debug_buffer_size < fifo_state->last_data_size) {
arg->used_size = arg->debug_buffer_size;
arg->did_not_fit = 1;
} else {
arg->used_size = fifo_state->last_data_size;
arg->did_not_fit = 0;
}
return copy_to_user(buffer, fifo_state->last_buffer, arg->used_size);
}
/**************************************************************************
*
* Copyright © 2009 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 "drmP.h"
#include "vmwgfx_drv.h"
#define VMW_FENCE_WRAP (1 << 24)
irqreturn_t vmw_irq_handler(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *)arg;
struct vmw_private *dev_priv = vmw_priv(dev);
uint32_t status;
spin_lock(&dev_priv->irq_lock);
status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
spin_unlock(&dev_priv->irq_lock);
if (status & SVGA_IRQFLAG_ANY_FENCE)
wake_up_all(&dev_priv->fence_queue);
if (status & SVGA_IRQFLAG_FIFO_PROGRESS)
wake_up_all(&dev_priv->fifo_queue);
if (likely(status)) {
outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t sequence)
{
uint32_t busy;
mutex_lock(&dev_priv->hw_mutex);
busy = vmw_read(dev_priv, SVGA_REG_BUSY);
mutex_unlock(&dev_priv->hw_mutex);
return (busy == 0);
}
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);
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;
/**
* Below is to signal stale fences that have wrapped.
* First, block fence submission.
*/
down_read(&fifo_state->rwsem);
/**
* Then check if the sequence is higher than what we've actually
* emitted. Then the fence is stale and signaled.
*/
ret = ((dev_priv->fence_seq - sequence) > VMW_FENCE_WRAP);
up_read(&fifo_state->rwsem);
return ret;
}
int vmw_fallback_wait(struct vmw_private *dev_priv,
bool lazy,
bool fifo_idle,
uint32_t sequence,
bool interruptible,
unsigned long timeout)
{
struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
uint32_t count = 0;
uint32_t signal_seq;
int ret;
unsigned long end_jiffies = jiffies + timeout;
bool (*wait_condition)(struct vmw_private *, uint32_t);
DEFINE_WAIT(__wait);
wait_condition = (fifo_idle) ? &vmw_fifo_idle :
&vmw_fence_signaled;
/**
* Block command submission while waiting for idle.
*/
if (fifo_idle)
down_read(&fifo_state->rwsem);
signal_seq = dev_priv->fence_seq;
ret = 0;
for (;;) {
prepare_to_wait(&dev_priv->fence_queue, &__wait,
(interruptible) ?
TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
if (wait_condition(dev_priv, sequence))
break;
if (time_after_eq(jiffies, end_jiffies)) {
DRM_ERROR("SVGA device lockup.\n");
break;
}
if (lazy)
schedule_timeout(1);
else if ((++count & 0x0F) == 0) {
/**
* FIXME: Use schedule_hr_timeout here for
* newer kernels and lower CPU utilization.
*/
__set_current_state(TASK_RUNNING);
schedule();
__set_current_state((interruptible) ?
TASK_INTERRUPTIBLE :
TASK_UNINTERRUPTIBLE);
}
if (interruptible && signal_pending(current)) {
ret = -ERESTART;
break;
}
}
finish_wait(&dev_priv->fence_queue, &__wait);
if (ret == 0 && fifo_idle) {
__le32 __iomem *fifo_mem = dev_priv->mmio_virt;
iowrite32(signal_seq, fifo_mem + SVGA_FIFO_FENCE);
}
wake_up_all(&dev_priv->fence_queue);
if (fifo_idle)
up_read(&fifo_state->rwsem);
return ret;
}
int vmw_wait_fence(struct vmw_private *dev_priv,
bool lazy, uint32_t sequence,
bool interruptible, unsigned long timeout)
{
long ret;
unsigned long irq_flags;
struct vmw_fifo_state *fifo = &dev_priv->fifo;
if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP))
return 0;
if (likely(vmw_fence_signaled(dev_priv, sequence)))
return 0;
vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
if (!(fifo->capabilities & SVGA_FIFO_CAP_FENCE))
return vmw_fallback_wait(dev_priv, lazy, true, sequence,
interruptible, timeout);
if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
return vmw_fallback_wait(dev_priv, lazy, false, sequence,
interruptible, timeout);
mutex_lock(&dev_priv->hw_mutex);
if (atomic_add_return(1, &dev_priv->fence_queue_waiters) > 0) {
spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
outl(SVGA_IRQFLAG_ANY_FENCE,
dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
vmw_write(dev_priv, SVGA_REG_IRQMASK,
vmw_read(dev_priv, SVGA_REG_IRQMASK) |
SVGA_IRQFLAG_ANY_FENCE);
spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
}
mutex_unlock(&dev_priv->hw_mutex);
if (interruptible)
ret = wait_event_interruptible_timeout
(dev_priv->fence_queue,
vmw_fence_signaled(dev_priv, sequence),
timeout);
else
ret = wait_event_timeout
(dev_priv->fence_queue,
vmw_fence_signaled(dev_priv, sequence),
timeout);
if (unlikely(ret == -ERESTARTSYS))
ret = -ERESTART;
else if (unlikely(ret == 0))
ret = -EBUSY;
else if (likely(ret > 0))
ret = 0;
mutex_lock(&dev_priv->hw_mutex);
if (atomic_dec_and_test(&dev_priv->fence_queue_waiters)) {
spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
vmw_write(dev_priv, SVGA_REG_IRQMASK,
vmw_read(dev_priv, SVGA_REG_IRQMASK) &
~SVGA_IRQFLAG_ANY_FENCE);
spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
}
mutex_unlock(&dev_priv->hw_mutex);
return ret;
}
void vmw_irq_preinstall(struct drm_device *dev)
{
struct vmw_private *dev_priv = vmw_priv(dev);
uint32_t status;
if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
return;
spin_lock_init(&dev_priv->irq_lock);
status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
}
int vmw_irq_postinstall(struct drm_device *dev)
{
return 0;
}
void vmw_irq_uninstall(struct drm_device *dev)
{
struct vmw_private *dev_priv = vmw_priv(dev);
uint32_t status;
if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
return;
mutex_lock(&dev_priv->hw_mutex);
vmw_write(dev_priv, SVGA_REG_IRQMASK, 0);
mutex_unlock(&dev_priv->hw_mutex);
status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
}
#define VMW_FENCE_WAIT_TIMEOUT 3*HZ;
int vmw_fence_wait_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_vmw_fence_wait_arg *arg =
(struct drm_vmw_fence_wait_arg *)data;
unsigned long timeout;
if (!arg->cookie_valid) {
arg->cookie_valid = 1;
arg->kernel_cookie = jiffies + VMW_FENCE_WAIT_TIMEOUT;
}
timeout = jiffies;
if (time_after_eq(timeout, (unsigned long)arg->kernel_cookie))
return -EBUSY;
timeout = (unsigned long)arg->kernel_cookie - timeout;
return vmw_wait_fence(vmw_priv(dev), true, arg->sequence, true, timeout);
}
This diff is collapsed.
/**************************************************************************
*
* Copyright © 2009 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.
*
**************************************************************************/
#ifndef VMWGFX_KMS_H_
#define VMWGFX_KMS_H_
#include "drmP.h"
#include "vmwgfx_drv.h"
#define vmw_framebuffer_to_vfb(x) \
container_of(x, struct vmw_framebuffer, base)
/**
* Base class for framebuffers
*
* @pin is called the when ever a crtc uses this framebuffer
* @unpin is called
*/
struct vmw_framebuffer {
struct drm_framebuffer base;
int (*pin)(struct vmw_framebuffer *fb);
int (*unpin)(struct vmw_framebuffer *fb);
};
#define vmw_crtc_to_du(x) \
container_of(x, struct vmw_display_unit, crtc)
/*
* Basic cursor manipulation
*/
int vmw_cursor_update_image(struct vmw_private *dev_priv,
u32 *image, u32 width, u32 height,
u32 hotspotX, u32 hotspotY);
void vmw_cursor_update_position(struct vmw_private *dev_priv,
bool show, int x, int y);
/**
* Base class display unit.
*
* Since the SVGA hw doesn't have a concept of a crtc, encoder or connector
* so the display unit is all of them at the same time. This is true for both
* legacy multimon and screen objects.
*/
struct vmw_display_unit {
struct drm_crtc crtc;
struct drm_encoder encoder;
struct drm_connector connector;
struct vmw_surface *cursor_surface;
struct vmw_dma_buffer *cursor_dmabuf;
size_t cursor_age;
int cursor_x;
int cursor_y;
int hotspot_x;
int hotspot_y;
unsigned unit;
};
/*
* Shared display unit functions - vmwgfx_kms.c
*/
void vmw_display_unit_cleanup(struct vmw_display_unit *du);
int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
uint32_t handle, uint32_t width, uint32_t height);
int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y);
/*
* Legacy display unit functions - vmwgfx_ldu.h
*/
int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv);
int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv);
#endif
This diff is collapsed.
This diff is collapsed.
/**************************************************************************
*
* Copyright © 2009 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.
*
**************************************************************************/
/**
* This file contains virtual hardware defines for kernel space.
*/
#ifndef _VMWGFX_REG_H_
#define _VMWGFX_REG_H_
#include <linux/types.h>
#define VMWGFX_INDEX_PORT 0x0
#define VMWGFX_VALUE_PORT 0x1
#define VMWGFX_IRQSTATUS_PORT 0x8
struct svga_guest_mem_descriptor {
__le32 ppn;
__le32 num_pages;
};
struct svga_fifo_cmd_fence {
__le32 fence;
};
#define SVGA_SYNC_GENERIC 1
#define SVGA_SYNC_FIFOFULL 2
#include "svga_types.h"
#include "svga3d_reg.h"
#endif
This diff is collapsed.
/**************************************************************************
*
* Copyright © 2009 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 "drmP.h"
#include "vmwgfx_drv.h"
int vmw_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct drm_file *file_priv;
struct vmw_private *dev_priv;
if (unlikely(vma->vm_pgoff < VMWGFX_FILE_PAGE_OFFSET)) {
if (vmw_fifo_mmap(filp, vma) == 0)
return 0;
return drm_mmap(filp, vma);
}
file_priv = (struct drm_file *)filp->private_data;
dev_priv = vmw_priv(file_priv->minor->dev);
return ttm_bo_mmap(filp, vma, &dev_priv->bdev);
}
static int vmw_ttm_mem_global_init(struct ttm_global_reference *ref)
{
DRM_INFO("global init.\n");
return ttm_mem_global_init(ref->object);
}
static void vmw_ttm_mem_global_release(struct ttm_global_reference *ref)
{
ttm_mem_global_release(ref->object);
}
int vmw_ttm_global_init(struct vmw_private *dev_priv)
{
struct ttm_global_reference *global_ref;
int ret;
global_ref = &dev_priv->mem_global_ref;
global_ref->global_type = TTM_GLOBAL_TTM_MEM;
global_ref->size = sizeof(struct ttm_mem_global);
global_ref->init = &vmw_ttm_mem_global_init;
global_ref->release = &vmw_ttm_mem_global_release;
ret = ttm_global_item_ref(global_ref);
if (unlikely(ret != 0)) {
DRM_ERROR("Failed setting up TTM memory accounting.\n");
return ret;
}
dev_priv->bo_global_ref.mem_glob =
dev_priv->mem_global_ref.object;
global_ref = &dev_priv->bo_global_ref.ref;
global_ref->global_type = TTM_GLOBAL_TTM_BO;
global_ref->size = sizeof(struct ttm_bo_global);
global_ref->init = &ttm_bo_global_init;
global_ref->release = &ttm_bo_global_release;
ret = ttm_global_item_ref(global_ref);
if (unlikely(ret != 0)) {
DRM_ERROR("Failed setting up TTM buffer objects.\n");
goto out_no_bo;
}
return 0;
out_no_bo:
ttm_global_item_unref(&dev_priv->mem_global_ref);
return ret;
}
void vmw_ttm_global_release(struct vmw_private *dev_priv)
{
ttm_global_item_unref(&dev_priv->bo_global_ref.ref);
ttm_global_item_unref(&dev_priv->mem_global_ref);
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment