Commit 2a2553cc authored by Dave Airlie's avatar Dave Airlie

Merge branch 'vmwgfx-next' of git://people.freedesktop.org/~thomash/linux into drm-next

A relative large set of various improvements for vmwgfx. Some of them
have been around for a while, some are relatively new, but functionality
should have been tested in our standalone repo.

* 'vmwgfx-next' of git://people.freedesktop.org/~thomash/linux:
  drm/vmwgfx: Bump version patchlevel and date
  drm/vmwgfx: use monotonic event timestamps
  drm/vmwgfx: Unpin the screen object backup buffer when not used
  drm/vmwgfx: Stricter count of legacy surface device resources
  drm/vmwgfx: Use kasprintf
  drm/vmwgfx: Get rid of the device-private suspended member
  drm/vmwgfx: Improve on hibernation
  drm/vmwgfx: Avoid pinning fbdev framebuffers
  drm/vmwgfx: Fix multiple command buffer context use
  drm/vmwgfx: Use the cpu blit utility for framebuffer to screen target blits
  drm/vmwgfx: Add a cpu blit utility that can be used for page-backed bos
  drm/ttm: Export the ttm_k[un]map_atomic_prot API.
  drm/ttm: Clean up kmap_atomic_prot selection code
  drm/vmwgfx: Cursor update fixes
  drm/vmwgfx: Send the correct nonblock option for atomic_commit
  drm/vmwgfx: Move the stdu vblank event to atomic function
  drm/vmwgfx: Move screen object page flip to atomic function
  drm/vmwgfx: Remove drm_crtc_arm_vblank_event from atomic flush
  drm/vmwgfx: Move surface copy cmd to atomic function
  drm/vmwgfx: Avoid iterating over display unit if crtc is available
parents f3924ae7 43bfefed
......@@ -255,6 +255,54 @@ static int ttm_copy_io_page(void *dst, void *src, unsigned long page)
return 0;
}
#ifdef CONFIG_X86
#define __ttm_kmap_atomic_prot(__page, __prot) kmap_atomic_prot(__page, __prot)
#define __ttm_kunmap_atomic(__addr) kunmap_atomic(__addr)
#else
#define __ttm_kmap_atomic_prot(__page, __prot) vmap(&__page, 1, 0, __prot)
#define __ttm_kunmap_atomic(__addr) vunmap(__addr)
#endif
/**
* ttm_kmap_atomic_prot - Efficient kernel map of a single page with
* specified page protection.
*
* @page: The page to map.
* @prot: The page protection.
*
* This function maps a TTM page using the kmap_atomic api if available,
* otherwise falls back to vmap. The user must make sure that the
* specified page does not have an aliased mapping with a different caching
* policy unless the architecture explicitly allows it. Also mapping and
* unmapping using this api must be correctly nested. Unmapping should
* occur in the reverse order of mapping.
*/
void *ttm_kmap_atomic_prot(struct page *page, pgprot_t prot)
{
if (pgprot_val(prot) == pgprot_val(PAGE_KERNEL))
return kmap_atomic(page);
else
return __ttm_kmap_atomic_prot(page, prot);
}
EXPORT_SYMBOL(ttm_kmap_atomic_prot);
/**
* ttm_kunmap_atomic_prot - Unmap a page that was mapped using
* ttm_kmap_atomic_prot.
*
* @addr: The virtual address from the map.
* @prot: The page protection.
*/
void ttm_kunmap_atomic_prot(void *addr, pgprot_t prot)
{
if (pgprot_val(prot) == pgprot_val(PAGE_KERNEL))
kunmap_atomic(addr);
else
__ttm_kunmap_atomic(addr);
}
EXPORT_SYMBOL(ttm_kunmap_atomic_prot);
static int ttm_copy_io_ttm_page(struct ttm_tt *ttm, void *src,
unsigned long page,
pgprot_t prot)
......@@ -266,28 +314,13 @@ static int ttm_copy_io_ttm_page(struct ttm_tt *ttm, void *src,
return -ENOMEM;
src = (void *)((unsigned long)src + (page << PAGE_SHIFT));
#ifdef CONFIG_X86
dst = kmap_atomic_prot(d, prot);
#else
if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL))
dst = vmap(&d, 1, 0, prot);
else
dst = kmap(d);
#endif
dst = ttm_kmap_atomic_prot(d, prot);
if (!dst)
return -ENOMEM;
memcpy_fromio(dst, src, PAGE_SIZE);
#ifdef CONFIG_X86
kunmap_atomic(dst);
#else
if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL))
vunmap(dst);
else
kunmap(d);
#endif
ttm_kunmap_atomic_prot(dst, prot);
return 0;
}
......@@ -303,27 +336,13 @@ static int ttm_copy_ttm_io_page(struct ttm_tt *ttm, void *dst,
return -ENOMEM;
dst = (void *)((unsigned long)dst + (page << PAGE_SHIFT));
#ifdef CONFIG_X86
src = kmap_atomic_prot(s, prot);
#else
if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL))
src = vmap(&s, 1, 0, prot);
else
src = kmap(s);
#endif
src = ttm_kmap_atomic_prot(s, prot);
if (!src)
return -ENOMEM;
memcpy_toio(dst, src, PAGE_SIZE);
#ifdef CONFIG_X86
kunmap_atomic(src);
#else
if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL))
vunmap(src);
else
kunmap(s);
#endif
ttm_kunmap_atomic_prot(src, prot);
return 0;
}
......
......@@ -7,6 +7,6 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \
vmwgfx_surface.o vmwgfx_prime.o vmwgfx_mob.o vmwgfx_shader.o \
vmwgfx_cmdbuf_res.o vmwgfx_cmdbuf.o vmwgfx_stdu.o \
vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o vmwgfx_msg.o \
vmwgfx_simple_resource.o vmwgfx_va.o
vmwgfx_simple_resource.o vmwgfx_va.o vmwgfx_blit.o
obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o
......@@ -372,6 +372,14 @@ SVGAGuestPtr;
* PA, not biased by the offset. When the command buffer is finished
* the guest should not read the offset field as there is no guarantee
* what it will set to.
*
* When the SVGA_CAP_HP_CMD_QUEUE cap bit is set a new command queue
* SVGA_CB_CONTEXT_1 is available. Commands submitted to this queue
* will be executed as quickly as possible by the SVGA device
* potentially before already queued commands on SVGA_CB_CONTEXT_0.
* The SVGA device guarantees that any command buffers submitted to
* SVGA_CB_CONTEXT_0 will be executed after any _already_ submitted
* command buffers to SVGA_CB_CONTEXT_1.
*/
#define SVGA_CB_MAX_SIZE (512 * 1024) /* 512 KB */
......@@ -382,7 +390,8 @@ SVGAGuestPtr;
typedef enum {
SVGA_CB_CONTEXT_DEVICE = 0x3f,
SVGA_CB_CONTEXT_0 = 0x0,
SVGA_CB_CONTEXT_MAX = 0x1,
SVGA_CB_CONTEXT_1 = 0x1, /* Supported with SVGA_CAP_HP_CMD_QUEUE */
SVGA_CB_CONTEXT_MAX = 0x2,
} SVGACBContext;
......@@ -689,6 +698,7 @@ SVGASignedPoint;
#define SVGA_CAP_CMD_BUFFERS_2 0x04000000
#define SVGA_CAP_GBOBJECTS 0x08000000
#define SVGA_CAP_DX 0x10000000
#define SVGA_CAP_HP_CMD_QUEUE 0x20000000
#define SVGA_CAP_CMD_RESERVED 0x80000000
......
This diff is collapsed.
......@@ -185,6 +185,22 @@ static const struct ttm_place evictable_placement_flags[] = {
}
};
static const struct ttm_place nonfixed_placement_flags[] = {
{
.fpfn = 0,
.lpfn = 0,
.flags = TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED
}, {
.fpfn = 0,
.lpfn = 0,
.flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED
}, {
.fpfn = 0,
.lpfn = 0,
.flags = VMW_PL_FLAG_MOB | TTM_PL_FLAG_CACHED
}
};
struct ttm_placement vmw_evictable_placement = {
.num_placement = 4,
.placement = evictable_placement_flags,
......@@ -213,6 +229,13 @@ struct ttm_placement vmw_mob_ne_placement = {
.busy_placement = &mob_ne_placement_flags
};
struct ttm_placement vmw_nonfixed_placement = {
.num_placement = 3,
.placement = nonfixed_placement_flags,
.num_busy_placement = 1,
.busy_placement = &sys_placement_flags
};
struct vmw_ttm_tt {
struct ttm_dma_tt dma_ttm;
struct vmw_private *dev_priv;
......@@ -841,6 +864,7 @@ static void vmw_move_notify(struct ttm_buffer_object *bo,
*/
static void vmw_swap_notify(struct ttm_buffer_object *bo)
{
vmw_resource_swap_notify(bo);
(void) ttm_bo_wait(bo, false, false);
}
......
......@@ -101,6 +101,7 @@ struct vmw_cmdbuf_context {
* @handle: DMA address handle for the command buffer space if @using_mob is
* false. Immutable.
* @size: The size of the command buffer space. Immutable.
* @num_contexts: Number of contexts actually enabled.
*/
struct vmw_cmdbuf_man {
struct mutex cur_mutex;
......@@ -128,6 +129,7 @@ struct vmw_cmdbuf_man {
bool has_pool;
dma_addr_t handle;
size_t size;
u32 num_contexts;
};
/**
......@@ -185,7 +187,7 @@ struct vmw_cmdbuf_alloc_info {
/* Loop over each context in the command buffer manager. */
#define for_each_cmdbuf_ctx(_man, _i, _ctx) \
for (_i = 0, _ctx = &(_man)->ctx[0]; (_i) < SVGA_CB_CONTEXT_MAX; \
for (_i = 0, _ctx = &(_man)->ctx[0]; (_i) < (_man)->num_contexts; \
++(_i), ++(_ctx))
static int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, u32 context,
......@@ -514,6 +516,7 @@ static void vmw_cmdbuf_work_func(struct work_struct *work)
struct list_head restart_head[SVGA_CB_CONTEXT_MAX];
int i;
struct vmw_cmdbuf_context *ctx;
bool global_block = false;
for_each_cmdbuf_ctx(man, i, ctx) {
INIT_LIST_HEAD(&restart_head[i]);
......@@ -531,6 +534,7 @@ static void vmw_cmdbuf_work_func(struct work_struct *work)
list_del_init(&entry->list);
restart[entry->cb_context] = true;
global_block = true;
if (!vmw_cmd_describe(header, &error_cmd_size, &cmd_name)) {
DRM_ERROR("Unknown command causing device error.\n");
......@@ -564,23 +568,21 @@ static void vmw_cmdbuf_work_func(struct work_struct *work)
cb_hdr->length -= new_start_offset;
cb_hdr->errorOffset = 0;
cb_hdr->offset = 0;
list_add_tail(&entry->list, &restart_head[entry->cb_context]);
man->ctx[entry->cb_context].block_submission = true;
}
for_each_cmdbuf_ctx(man, i, ctx)
man->ctx[i].block_submission = true;
spin_unlock(&man->lock);
/* Preempt all contexts with errors */
for_each_cmdbuf_ctx(man, i, ctx) {
if (ctx->block_submission && vmw_cmdbuf_preempt(man, i))
DRM_ERROR("Failed preempting command buffer "
"context %u.\n", i);
}
/* Preempt all contexts */
if (global_block && vmw_cmdbuf_preempt(man, 0))
DRM_ERROR("Failed preempting command buffer contexts\n");
spin_lock(&man->lock);
for_each_cmdbuf_ctx(man, i, ctx) {
if (!ctx->block_submission)
continue;
/* Move preempted command buffers to the preempted queue. */
vmw_cmdbuf_ctx_process(man, ctx, &dummy);
......@@ -594,19 +596,16 @@ static void vmw_cmdbuf_work_func(struct work_struct *work)
* Finally add all command buffers first in the submitted
* queue, to rerun them.
*/
list_splice_init(&restart_head[i], &ctx->submitted);
ctx->block_submission = false;
list_splice_init(&restart_head[i], &ctx->submitted);
}
vmw_cmdbuf_man_process(man);
spin_unlock(&man->lock);
for_each_cmdbuf_ctx(man, i, ctx) {
if (restart[i] && vmw_cmdbuf_startstop(man, i, true))
DRM_ERROR("Failed restarting command buffer "
"context %u.\n", i);
}
if (global_block && vmw_cmdbuf_startstop(man, 0, true))
DRM_ERROR("Failed restarting command buffer contexts\n");
/* Send a new fence in case one was removed */
if (send_fence) {
......@@ -1307,6 +1306,8 @@ struct vmw_cmdbuf_man *vmw_cmdbuf_man_create(struct vmw_private *dev_priv)
if (!man)
return ERR_PTR(-ENOMEM);
man->num_contexts = (dev_priv->capabilities & SVGA_CAP_HP_CMD_QUEUE) ?
2 : 1;
man->headers = dma_pool_create("vmwgfx cmdbuf",
&dev_priv->dev->pdev->dev,
sizeof(SVGACBHeader),
......@@ -1341,14 +1342,11 @@ struct vmw_cmdbuf_man *vmw_cmdbuf_man_create(struct vmw_private *dev_priv)
INIT_WORK(&man->work, &vmw_cmdbuf_work_func);
vmw_generic_waiter_add(dev_priv, SVGA_IRQFLAG_ERROR,
&dev_priv->error_waiters);
for_each_cmdbuf_ctx(man, i, ctx) {
ret = vmw_cmdbuf_startstop(man, i, true);
if (ret) {
DRM_ERROR("Failed starting command buffer "
"context %u.\n", i);
vmw_cmdbuf_man_destroy(man);
return ERR_PTR(ret);
}
ret = vmw_cmdbuf_startstop(man, 0, true);
if (ret) {
DRM_ERROR("Failed starting command buffer contexts\n");
vmw_cmdbuf_man_destroy(man);
return ERR_PTR(ret);
}
return man;
......@@ -1398,16 +1396,11 @@ void vmw_cmdbuf_remove_pool(struct vmw_cmdbuf_man *man)
*/
void vmw_cmdbuf_man_destroy(struct vmw_cmdbuf_man *man)
{
struct vmw_cmdbuf_context *ctx;
unsigned int i;
WARN_ON_ONCE(man->has_pool);
(void) vmw_cmdbuf_idle(man, false, 10*HZ);
for_each_cmdbuf_ctx(man, i, ctx)
if (vmw_cmdbuf_startstop(man, i, false))
DRM_ERROR("Failed stopping command buffer "
"context %u.\n", i);
if (vmw_cmdbuf_startstop(man, 0, false))
DRM_ERROR("Failed stopping command buffer contexts.\n");
vmw_generic_waiter_remove(man->dev_priv, SVGA_IRQFLAG_ERROR,
&man->dev_priv->error_waiters);
......
......@@ -323,3 +323,54 @@ void vmw_bo_pin_reserved(struct vmw_dma_buffer *vbo, bool pin)
BUG_ON(ret != 0 || bo->mem.mem_type != old_mem_type);
}
/*
* vmw_dma_buffer_unmap - Tear down a cached buffer object map.
*
* @vbo: The buffer object whose map we are tearing down.
*
* This function tears down a cached map set up using
* vmw_dma_buffer_map_and_cache().
*/
void vmw_dma_buffer_unmap(struct vmw_dma_buffer *vbo)
{
if (vbo->map.bo == NULL)
return;
ttm_bo_kunmap(&vbo->map);
}
/*
* vmw_dma_buffer_map_and_cache - Map a buffer object and cache the map
*
* @vbo: The buffer object to map
* Return: A kernel virtual address or NULL if mapping failed.
*
* This function maps a buffer object into the kernel address space, or
* returns the virtual kernel address of an already existing map. The virtual
* address remains valid as long as the buffer object is pinned or reserved.
* The cached map is torn down on either
* 1) Buffer object move
* 2) Buffer object swapout
* 3) Buffer object destruction
*
*/
void *vmw_dma_buffer_map_and_cache(struct vmw_dma_buffer *vbo)
{
struct ttm_buffer_object *bo = &vbo->base;
bool not_used;
void *virtual;
int ret;
virtual = ttm_kmap_obj_virtual(&vbo->map, &not_used);
if (virtual)
return virtual;
ret = ttm_bo_kmap(bo, 0, bo->num_pages, &vbo->map);
if (ret)
DRM_ERROR("Buffer object map failed: %d.\n", ret);
return ttm_kmap_obj_virtual(&vbo->map, &not_used);
}
......@@ -301,6 +301,8 @@ static void vmw_print_capabilities(uint32_t capabilities)
DRM_INFO(" Guest Backed Resources.\n");
if (capabilities & SVGA_CAP_DX)
DRM_INFO(" DX Features.\n");
if (capabilities & SVGA_CAP_HP_CMD_QUEUE)
DRM_INFO(" HP Command Queue.\n");
}
/**
......@@ -1277,8 +1279,7 @@ static void vmw_master_drop(struct drm_device *dev,
ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM);
ttm_vt_unlock(&dev_priv->fbdev_master.lock);
if (dev_priv->enable_fb)
vmw_fb_on(dev_priv);
vmw_fb_refresh(dev_priv);
}
/**
......@@ -1368,28 +1369,23 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
switch (val) {
case PM_HIBERNATION_PREPARE:
if (dev_priv->enable_fb)
vmw_fb_off(dev_priv);
ttm_suspend_lock(&dev_priv->reservation_sem);
/*
* This empties VRAM and unbinds all GMR bindings.
* Buffer contents is moved to swappable memory.
* Take the reservation sem in write mode, which will make sure
* there are no other processes holding a buffer object
* reservation, meaning we should be able to evict all buffer
* objects if needed.
* Once user-space processes have been frozen, we can release
* the lock again.
*/
vmw_execbuf_release_pinned_bo(dev_priv);
vmw_resource_evict_all(dev_priv);
vmw_release_device_early(dev_priv);
ttm_bo_swapout_all(&dev_priv->bdev);
vmw_fence_fifo_down(dev_priv->fman);
ttm_suspend_lock(&dev_priv->reservation_sem);
dev_priv->suspend_locked = true;
break;
case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
vmw_fence_fifo_up(dev_priv->fman);
ttm_suspend_unlock(&dev_priv->reservation_sem);
if (dev_priv->enable_fb)
vmw_fb_on(dev_priv);
break;
case PM_RESTORE_PREPARE:
if (READ_ONCE(dev_priv->suspend_locked)) {
dev_priv->suspend_locked = false;
ttm_suspend_unlock(&dev_priv->reservation_sem);
}
break;
default:
break;
......@@ -1440,25 +1436,48 @@ static int vmw_pm_freeze(struct device *kdev)
struct pci_dev *pdev = to_pci_dev(kdev);
struct drm_device *dev = pci_get_drvdata(pdev);
struct vmw_private *dev_priv = vmw_priv(dev);
int ret;
dev_priv->suspended = true;
/*
* Unlock for vmw_kms_suspend.
* No user-space processes should be running now.
*/
ttm_suspend_unlock(&dev_priv->reservation_sem);
ret = vmw_kms_suspend(dev_priv->dev);
if (ret) {
ttm_suspend_lock(&dev_priv->reservation_sem);
DRM_ERROR("Failed to freeze modesetting.\n");
return ret;
}
if (dev_priv->enable_fb)
vmw_fifo_resource_dec(dev_priv);
vmw_fb_off(dev_priv);
ttm_suspend_lock(&dev_priv->reservation_sem);
vmw_execbuf_release_pinned_bo(dev_priv);
vmw_resource_evict_all(dev_priv);
vmw_release_device_early(dev_priv);
ttm_bo_swapout_all(&dev_priv->bdev);
if (dev_priv->enable_fb)
vmw_fifo_resource_dec(dev_priv);
if (atomic_read(&dev_priv->num_fifo_resources) != 0) {
DRM_ERROR("Can't hibernate while 3D resources are active.\n");
if (dev_priv->enable_fb)
vmw_fifo_resource_inc(dev_priv);
WARN_ON(vmw_request_device_late(dev_priv));
dev_priv->suspended = false;
dev_priv->suspend_locked = false;
ttm_suspend_unlock(&dev_priv->reservation_sem);
if (dev_priv->suspend_state)
vmw_kms_resume(dev);
if (dev_priv->enable_fb)
vmw_fb_on(dev_priv);
vmw_fb_refresh(dev_priv);
return -EBUSY;
}
if (dev_priv->enable_fb)
__vmw_svga_disable(dev_priv);
vmw_fence_fifo_down(dev_priv->fman);
__vmw_svga_disable(dev_priv);
vmw_release_device_late(dev_priv);
return 0;
}
......@@ -1482,7 +1501,16 @@ static int vmw_pm_restore(struct device *kdev)
if (dev_priv->enable_fb)
__vmw_svga_enable(dev_priv);
dev_priv->suspended = false;
vmw_fence_fifo_up(dev_priv->fman);
dev_priv->suspend_locked = false;
ttm_suspend_unlock(&dev_priv->reservation_sem);
if (dev_priv->suspend_state)
vmw_kms_resume(dev_priv->dev);
if (dev_priv->enable_fb)
vmw_fb_on(dev_priv);
vmw_fb_refresh(dev_priv);
return 0;
}
......
......@@ -43,10 +43,10 @@
#include <linux/sync_file.h>
#define VMWGFX_DRIVER_NAME "vmwgfx"
#define VMWGFX_DRIVER_DATE "20170612"
#define VMWGFX_DRIVER_DATE "20180322"
#define VMWGFX_DRIVER_MAJOR 2
#define VMWGFX_DRIVER_MINOR 14
#define VMWGFX_DRIVER_PATCHLEVEL 0
#define VMWGFX_DRIVER_PATCHLEVEL 1
#define VMWGFX_FILE_PAGE_OFFSET 0x00100000
#define VMWGFX_FIFO_STATIC_SIZE (1024*1024)
#define VMWGFX_MAX_RELOCATIONS 2048
......@@ -92,6 +92,8 @@ struct vmw_dma_buffer {
s32 pin_count;
/* Not ref-counted. Protected by binding_mutex */
struct vmw_resource *dx_query_ctx;
/* Protected by reservation */
struct ttm_bo_kmap_obj map;
};
/**
......@@ -423,6 +425,7 @@ struct vmw_private {
struct vmw_framebuffer *implicit_fb;
struct mutex global_kms_state_mutex;
spinlock_t cursor_lock;
struct drm_atomic_state *suspend_state;
/*
* Context and surface management.
......@@ -494,8 +497,8 @@ struct vmw_private {
struct vmw_master *active_master;
struct vmw_master fbdev_master;
struct notifier_block pm_nb;
bool suspended;
bool refuse_hibernation;
bool suspend_locked;
struct mutex release_mutex;
atomic_t num_fifo_resources;
......@@ -673,11 +676,13 @@ extern void vmw_resource_move_notify(struct ttm_buffer_object *bo,
struct ttm_mem_reg *mem);
extern void vmw_query_move_notify(struct ttm_buffer_object *bo,
struct ttm_mem_reg *mem);
extern void vmw_resource_swap_notify(struct ttm_buffer_object *bo);
extern int vmw_query_readback_all(struct vmw_dma_buffer *dx_query_mob);
extern void vmw_fence_single_bo(struct ttm_buffer_object *bo,
struct vmw_fence_obj *fence);
extern void vmw_resource_evict_all(struct vmw_private *dev_priv);
/**
* DMA buffer helper routines - vmwgfx_dmabuf.c
*/
......@@ -700,6 +705,8 @@ extern int vmw_dmabuf_unpin(struct vmw_private *vmw_priv,
extern void vmw_bo_get_guest_ptr(const struct ttm_buffer_object *buf,
SVGAGuestPtr *ptr);
extern void vmw_bo_pin_reserved(struct vmw_dma_buffer *bo, bool pin);
extern void *vmw_dma_buffer_map_and_cache(struct vmw_dma_buffer *vbo);
extern void vmw_dma_buffer_unmap(struct vmw_dma_buffer *vbo);
/**
* Misc Ioctl functionality - vmwgfx_ioctl.c
......@@ -766,6 +773,7 @@ extern struct ttm_placement vmw_evictable_placement;
extern struct ttm_placement vmw_srf_placement;
extern struct ttm_placement vmw_mob_placement;
extern struct ttm_placement vmw_mob_ne_placement;
extern struct ttm_placement vmw_nonfixed_placement;
extern struct ttm_bo_driver vmw_bo_driver;
extern int vmw_dma_quiescent(struct drm_device *dev);
extern int vmw_bo_map_dma(struct ttm_buffer_object *bo);
......@@ -902,6 +910,7 @@ int vmw_fb_init(struct vmw_private *vmw_priv);
int vmw_fb_close(struct vmw_private *dev_priv);
int vmw_fb_off(struct vmw_private *vmw_priv);
int vmw_fb_on(struct vmw_private *vmw_priv);
void vmw_fb_refresh(struct vmw_private *vmw_priv);
/**
* Kernel modesetting - vmwgfx_kms.c
......@@ -938,6 +947,8 @@ int vmw_kms_present(struct vmw_private *dev_priv,
int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
void vmw_kms_legacy_hotspot_clear(struct vmw_private *dev_priv);
int vmw_kms_suspend(struct drm_device *dev);
int vmw_kms_resume(struct drm_device *dev);
int vmw_dumb_create(struct drm_file *file_priv,
struct drm_device *dev,
......@@ -1165,6 +1176,53 @@ extern int vmw_cmdbuf_cur_flush(struct vmw_cmdbuf_man *man,
bool interruptible);
extern void vmw_cmdbuf_irqthread(struct vmw_cmdbuf_man *man);
/* CPU blit utilities - vmwgfx_blit.c */
/**
* struct vmw_diff_cpy - CPU blit information structure
*
* @rect: The output bounding box rectangle.
* @line: The current line of the blit.
* @line_offset: Offset of the current line segment.
* @cpp: Bytes per pixel (granularity information).
* @memcpy: Which memcpy function to use.
*/
struct vmw_diff_cpy {
struct drm_rect rect;
size_t line;
size_t line_offset;
int cpp;
void (*do_cpy)(struct vmw_diff_cpy *diff, u8 *dest, const u8 *src,
size_t n);
};
#define VMW_CPU_BLIT_INITIALIZER { \
.do_cpy = vmw_memcpy, \
}
#define VMW_CPU_BLIT_DIFF_INITIALIZER(_cpp) { \
.line = 0, \
.line_offset = 0, \
.rect = { .x1 = INT_MAX/2, \
.y1 = INT_MAX/2, \
.x2 = INT_MIN/2, \
.y2 = INT_MIN/2 \
}, \
.cpp = _cpp, \
.do_cpy = vmw_diff_memcpy, \
}
void vmw_diff_memcpy(struct vmw_diff_cpy *diff, u8 *dest, const u8 *src,
size_t n);
void vmw_memcpy(struct vmw_diff_cpy *diff, u8 *dest, const u8 *src, size_t n);
int vmw_bo_cpu_blit(struct ttm_buffer_object *dst,
u32 dst_offset, u32 dst_stride,
struct ttm_buffer_object *src,
u32 src_offset, u32 src_stride,
u32 w, u32 h,
struct vmw_diff_cpy *diff);
/**
* Inline helper functions
......
......@@ -43,8 +43,6 @@ struct vmw_fb_par {
struct mutex bo_mutex;
struct vmw_dma_buffer *vmw_bo;
struct ttm_bo_kmap_obj map;
void *bo_ptr;
unsigned bo_size;
struct drm_framebuffer *set_fb;
struct drm_display_mode *set_mode;
......@@ -163,10 +161,17 @@ static int vmw_fb_blank(int blank, struct fb_info *info)
return 0;
}
/*
* Dirty code
/**
* vmw_fb_dirty_flush - flush dirty regions to the kms framebuffer
*
* @work: The struct work_struct associated with this task.
*
* This function flushes the dirty regions of the vmalloc framebuffer to the
* kms framebuffer, and if the kms framebuffer is visible, also updated the
* corresponding displays. Note that this function runs even if the kms
* framebuffer is not bound to a crtc and thus not visible, but it's turned
* off during hibernation using the par->dirty.active bool.
*/
static void vmw_fb_dirty_flush(struct work_struct *work)
{
struct vmw_fb_par *par = container_of(work, struct vmw_fb_par,
......@@ -174,13 +179,15 @@ static void vmw_fb_dirty_flush(struct work_struct *work)
struct vmw_private *vmw_priv = par->vmw_priv;
struct fb_info *info = vmw_priv->fb_info;
unsigned long irq_flags;
s32 dst_x1, dst_x2, dst_y1, dst_y2, w, h;
s32 dst_x1, dst_x2, dst_y1, dst_y2, w = 0, h = 0;
u32 cpp, max_x, max_y;
struct drm_clip_rect clip;
struct drm_framebuffer *cur_fb;
u8 *src_ptr, *dst_ptr;
struct vmw_dma_buffer *vbo = par->vmw_bo;
void *virtual;
if (vmw_priv->suspended)
if (!READ_ONCE(par->dirty.active))
return;
mutex_lock(&par->bo_mutex);
......@@ -188,10 +195,16 @@ static void vmw_fb_dirty_flush(struct work_struct *work)
if (!cur_fb)
goto out_unlock;
(void) ttm_read_lock(&vmw_priv->reservation_sem, false);
(void) ttm_bo_reserve(&vbo->base, false, false, NULL);
virtual = vmw_dma_buffer_map_and_cache(vbo);
if (!virtual)
goto out_unreserve;
spin_lock_irqsave(&par->dirty.lock, irq_flags);
if (!par->dirty.active) {
spin_unlock_irqrestore(&par->dirty.lock, irq_flags);
goto out_unlock;
goto out_unreserve;
}
/*
......@@ -221,7 +234,7 @@ static void vmw_fb_dirty_flush(struct work_struct *work)
spin_unlock_irqrestore(&par->dirty.lock, irq_flags);
if (w && h) {
dst_ptr = (u8 *)par->bo_ptr +
dst_ptr = (u8 *)virtual +
(dst_y1 * par->set_fb->pitches[0] + dst_x1 * cpp);
src_ptr = (u8 *)par->vmalloc +
((dst_y1 + par->fb_y) * info->fix.line_length +
......@@ -237,7 +250,12 @@ static void vmw_fb_dirty_flush(struct work_struct *work)
clip.x2 = dst_x2;
clip.y1 = dst_y1;
clip.y2 = dst_y2;
}
out_unreserve:
ttm_bo_unreserve(&vbo->base);
ttm_read_unlock(&vmw_priv->reservation_sem);
if (w && h) {
WARN_ON_ONCE(par->set_fb->funcs->dirty(cur_fb, NULL, 0, 0,
&clip, 1));
vmw_fifo_flush(vmw_priv, false);
......@@ -504,18 +522,8 @@ static int vmw_fb_kms_detach(struct vmw_fb_par *par,
par->set_fb = NULL;
}
if (par->vmw_bo && detach_bo) {
struct vmw_private *vmw_priv = par->vmw_priv;
if (par->bo_ptr) {
ttm_bo_kunmap(&par->map);
par->bo_ptr = NULL;
}
if (unref_bo)
vmw_dmabuf_unreference(&par->vmw_bo);
else if (vmw_priv->active_display_unit != vmw_du_legacy)
vmw_dmabuf_unpin(par->vmw_priv, par->vmw_bo, false);
}
if (par->vmw_bo && detach_bo && unref_bo)
vmw_dmabuf_unreference(&par->vmw_bo);
return 0;
}
......@@ -636,38 +644,6 @@ static int vmw_fb_set_par(struct fb_info *info)
if (ret)
goto out_unlock;
if (!par->bo_ptr) {
struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(set.fb);
/*
* Pin before mapping. Since we don't know in what placement
* to pin, call into KMS to do it for us. LDU doesn't require
* additional pinning because set_config() would've pinned
* it already
*/
if (vmw_priv->active_display_unit != vmw_du_legacy) {
ret = vfb->pin(vfb);
if (ret) {
DRM_ERROR("Could not pin the fbdev "
"framebuffer.\n");
goto out_unlock;
}
}
ret = ttm_bo_kmap(&par->vmw_bo->base, 0,
par->vmw_bo->base.num_pages, &par->map);
if (ret) {
if (vmw_priv->active_display_unit != vmw_du_legacy)
vfb->unpin(vfb);
DRM_ERROR("Could not map the fbdev framebuffer.\n");
goto out_unlock;
}
par->bo_ptr = ttm_kmap_obj_virtual(&par->map, &par->bo_iowrite);
}
vmw_fb_dirty_mark(par, par->fb_x, par->fb_y,
par->set_fb->width, par->set_fb->height);
......@@ -883,12 +859,6 @@ int vmw_fb_off(struct vmw_private *vmw_priv)
flush_delayed_work(&info->deferred_work);
flush_delayed_work(&par->local_work);
mutex_lock(&par->bo_mutex);
drm_modeset_lock_all(vmw_priv->dev);
(void) vmw_fb_kms_detach(par, true, false);
drm_modeset_unlock_all(vmw_priv->dev);
mutex_unlock(&par->bo_mutex);
return 0;
}
......@@ -904,10 +874,24 @@ int vmw_fb_on(struct vmw_private *vmw_priv)
info = vmw_priv->fb_info;
par = info->par;
vmw_fb_set_par(info);
spin_lock_irqsave(&par->dirty.lock, flags);
par->dirty.active = true;
spin_unlock_irqrestore(&par->dirty.lock, flags);
return 0;
}
/**
* vmw_fb_refresh - Refresh fb display
*
* @vmw_priv: Pointer to device private
*
* Call into kms to show the fbdev display(s).
*/
void vmw_fb_refresh(struct vmw_private *vmw_priv)
{
if (!vmw_priv->fb_info)
return;
vmw_fb_set_par(vmw_priv->fb_info);
}
......@@ -901,11 +901,12 @@ static void vmw_event_fence_action_seq_passed(struct vmw_fence_action *action)
spin_lock_irq(&dev->event_lock);
if (likely(eaction->tv_sec != NULL)) {
struct timeval tv;
struct timespec64 ts;
do_gettimeofday(&tv);
*eaction->tv_sec = tv.tv_sec;
*eaction->tv_usec = tv.tv_usec;
ktime_get_ts64(&ts);
/* monotonic time, so no y2038 overflow */
*eaction->tv_sec = ts.tv_sec;
*eaction->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
}
drm_send_event_locked(dev, eaction->event);
......
......@@ -393,13 +393,13 @@ vmw_du_cursor_plane_atomic_update(struct drm_plane *plane,
du->cursor_surface = vps->surf;
du->cursor_dmabuf = vps->dmabuf;
/* setup new image */
if (vps->surf) {
du->cursor_age = du->cursor_surface->snooper.age;
ret = vmw_cursor_update_image(dev_priv,
vps->surf->snooper.image,
64, 64, hotspot_x, hotspot_y);
64, 64, hotspot_x,
hotspot_y);
} else if (vps->dmabuf) {
ret = vmw_cursor_update_dmabuf(dev_priv, vps->dmabuf,
plane->state->crtc_w,
......@@ -497,11 +497,22 @@ int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane,
struct vmw_surface *surface = NULL;
struct drm_framebuffer *fb = new_state->fb;
struct drm_rect src = drm_plane_state_src(new_state);
struct drm_rect dest = drm_plane_state_dest(new_state);
/* Turning off */
if (!fb)
return ret;
ret = drm_plane_helper_check_update(plane, new_state->crtc, fb,
&src, &dest,
DRM_MODE_ROTATE_0,
DRM_PLANE_HELPER_NO_SCALING,
DRM_PLANE_HELPER_NO_SCALING,
true, true, &new_state->visible);
if (!ret)
return ret;
/* A lot of the code assumes this */
if (new_state->crtc_w != 64 || new_state->crtc_h != 64) {
DRM_ERROR("Invalid cursor dimensions (%d, %d)\n",
......@@ -566,13 +577,9 @@ void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
crtc->state->event = NULL;
spin_lock_irq(&crtc->dev->event_lock);
if (drm_crtc_vblank_get(crtc) == 0)
drm_crtc_arm_vblank_event(crtc, event);
else
drm_crtc_send_vblank_event(crtc, event);
drm_crtc_send_vblank_event(crtc, event);
spin_unlock_irq(&crtc->dev->event_lock);
}
}
......@@ -675,9 +682,6 @@ vmw_du_plane_duplicate_state(struct drm_plane *plane)
return NULL;
vps->pinned = 0;
/* Mapping is managed by prepare_fb/cleanup_fb */
memset(&vps->host_map, 0, sizeof(vps->host_map));
vps->cpp = 0;
/* Each ref counted resource needs to be acquired again */
......@@ -739,11 +743,6 @@ vmw_du_plane_destroy_state(struct drm_plane *plane,
/* Should have been freed by cleanup_fb */
if (vps->host_map.virtual) {
DRM_ERROR("Host mapping not freed\n");
ttm_bo_kunmap(&vps->host_map);
}
if (vps->surf)
vmw_surface_unreference(&vps->surf);
......@@ -888,11 +887,11 @@ static int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
if (dev_priv->active_display_unit == vmw_du_screen_object)
ret = vmw_kms_sou_do_surface_dirty(dev_priv, &vfbs->base,
clips, NULL, NULL, 0, 0,
num_clips, inc, NULL);
num_clips, inc, NULL, NULL);
else
ret = vmw_kms_stdu_surface_dirty(dev_priv, &vfbs->base,
clips, NULL, NULL, 0, 0,
num_clips, inc, NULL);
num_clips, inc, NULL, NULL);
vmw_fifo_flush(dev_priv, false);
ttm_read_unlock(&dev_priv->reservation_sem);
......@@ -928,11 +927,12 @@ int vmw_kms_readback(struct vmw_private *dev_priv,
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);
user_fence_rep, vclips, num_clips,
NULL);
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);
1, false, true, NULL);
default:
WARN_ONCE(true,
"Readback called with invalid display system.\n");
......@@ -1090,12 +1090,12 @@ static int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
case vmw_du_screen_target:
ret = vmw_kms_stdu_dma(dev_priv, NULL, &vfbd->base, NULL,
clips, NULL, num_clips, increment,
true, true);
true, true, NULL);
break;
case vmw_du_screen_object:
ret = vmw_kms_sou_do_dmabuf_dirty(dev_priv, &vfbd->base,
clips, NULL, num_clips,
increment, true, NULL);
increment, true, NULL, NULL);
break;
case vmw_du_legacy:
ret = vmw_kms_ldu_do_dmabuf_dirty(dev_priv, &vfbd->base, 0, 0,
......@@ -1121,12 +1121,14 @@ static const struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = {
};
/**
* Pin the dmabuffer to the start of vram.
* Pin the dmabuffer in a location suitable for access by the
* display system.
*/
static int vmw_framebuffer_pin(struct vmw_framebuffer *vfb)
{
struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
struct vmw_dma_buffer *buf;
struct ttm_placement *placement;
int ret;
buf = vfb->dmabuf ? vmw_framebuffer_to_vfbd(&vfb->base)->buffer :
......@@ -1143,12 +1145,24 @@ static int vmw_framebuffer_pin(struct vmw_framebuffer *vfb)
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);
if (vfb->dmabuf) {
if (dev_priv->capabilities & SVGA_CAP_3D) {
/*
* Use surface DMA to get content to
* sreen target surface.
*/
placement = &vmw_vram_gmr_placement;
} else {
/* Use CPU blit. */
placement = &vmw_sys_placement;
}
} else {
/* Use surface / image update */
placement = &vmw_mob_placement;
}
return vmw_dmabuf_pin_in_placement(dev_priv, buf,
&vmw_mob_placement, false);
return vmw_dmabuf_pin_in_placement(dev_priv, buf, placement,
false);
default:
return -EINVAL;
}
......@@ -1539,35 +1553,10 @@ vmw_kms_atomic_check_modeset(struct drm_device *dev,
return drm_atomic_helper_check(dev, state);
}
/**
* vmw_kms_atomic_commit - Perform an atomic state commit
*
* @dev: DRM device
* @state: the driver state object
* @nonblock: Whether nonblocking behaviour is requested
*
* This is a simple wrapper around drm_atomic_helper_commit() for
* us to clear the nonblocking value.
*
* Nonblocking commits currently cause synchronization issues
* for vmwgfx.
*
* RETURNS
* Zero for success or negative error code on failure.
*/
int vmw_kms_atomic_commit(struct drm_device *dev,
struct drm_atomic_state *state,
bool nonblock)
{
return drm_atomic_helper_commit(dev, state, false);
}
static const struct drm_mode_config_funcs vmw_kms_funcs = {
.fb_create = vmw_kms_fb_create,
.atomic_check = vmw_kms_atomic_check_modeset,
.atomic_commit = vmw_kms_atomic_commit,
.atomic_commit = drm_atomic_helper_commit,
};
static int vmw_kms_generic_present(struct vmw_private *dev_priv,
......@@ -1581,7 +1570,7 @@ static int vmw_kms_generic_present(struct vmw_private *dev_priv,
{
return vmw_kms_sou_do_surface_dirty(dev_priv, vfb, NULL, clips,
&surface->res, destX, destY,
num_clips, 1, NULL);
num_clips, 1, NULL, NULL);
}
......@@ -1600,7 +1589,7 @@ int vmw_kms_present(struct vmw_private *dev_priv,
case vmw_du_screen_target:
ret = vmw_kms_stdu_surface_dirty(dev_priv, vfb, NULL, clips,
&surface->res, destX, destY,
num_clips, 1, NULL);
num_clips, 1, NULL, NULL);
break;
case vmw_du_screen_object:
ret = vmw_kms_generic_present(dev_priv, file_priv, vfb, surface,
......@@ -2328,10 +2317,16 @@ int vmw_kms_helper_dirty(struct vmw_private *dev_priv,
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);
/* If crtc is passed, no need to iterate over other display units */
if (dirty->crtc) {
units[num_units++] = vmw_crtc_to_du(dirty->crtc);
} else {
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++) {
......@@ -2430,14 +2425,21 @@ int vmw_kms_helper_dirty(struct vmw_private *dev_priv,
int vmw_kms_helper_buffer_prepare(struct vmw_private *dev_priv,
struct vmw_dma_buffer *buf,
bool interruptible,
bool validate_as_mob)
bool validate_as_mob,
bool for_cpu_blit)
{
struct ttm_operation_ctx ctx = {
.interruptible = interruptible,
.no_wait_gpu = false};
struct ttm_buffer_object *bo = &buf->base;
int ret;
ttm_bo_reserve(bo, false, false, NULL);
ret = vmw_validate_single_buffer(dev_priv, bo, interruptible,
validate_as_mob);
if (for_cpu_blit)
ret = ttm_bo_validate(bo, &vmw_nonfixed_placement, &ctx);
else
ret = vmw_validate_single_buffer(dev_priv, bo, interruptible,
validate_as_mob);
if (ret)
ttm_bo_unreserve(bo);
......@@ -2549,7 +2551,8 @@ int vmw_kms_helper_resource_prepare(struct vmw_resource *res,
if (res->backup) {
ret = vmw_kms_helper_buffer_prepare(res->dev_priv, res->backup,
interruptible,
res->dev_priv->has_mob);
res->dev_priv->has_mob,
false);
if (ret)
goto out_unreserve;
}
......@@ -2845,3 +2848,51 @@ int vmw_kms_set_config(struct drm_mode_set *set,
return drm_atomic_helper_set_config(set, ctx);
}
/**
* vmw_kms_suspend - Save modesetting state and turn modesetting off.
*
* @dev: Pointer to the drm device
* Return: 0 on success. Negative error code on failure.
*/
int vmw_kms_suspend(struct drm_device *dev)
{
struct vmw_private *dev_priv = vmw_priv(dev);
dev_priv->suspend_state = drm_atomic_helper_suspend(dev);
if (IS_ERR(dev_priv->suspend_state)) {
int ret = PTR_ERR(dev_priv->suspend_state);
DRM_ERROR("Failed kms suspend: %d\n", ret);
dev_priv->suspend_state = NULL;
return ret;
}
return 0;
}
/**
* vmw_kms_resume - Re-enable modesetting and restore state
*
* @dev: Pointer to the drm device
* Return: 0 on success. Negative error code on failure.
*
* State is resumed from a previous vmw_kms_suspend(). It's illegal
* to call this function without a previous vmw_kms_suspend().
*/
int vmw_kms_resume(struct drm_device *dev)
{
struct vmw_private *dev_priv = vmw_priv(dev);
int ret;
if (WARN_ON(!dev_priv->suspend_state))
return 0;
ret = drm_atomic_helper_resume(dev, dev_priv->suspend_state);
dev_priv->suspend_state = NULL;
return ret;
}
......@@ -50,6 +50,7 @@
* @unit: The current display unit. Set up by the helper before a call to @clip.
* @cmd: The allocated fifo space. Set up by the helper before the first @clip
* call.
* @crtc: The crtc for which to build dirty commands.
* @num_hits: Number of clip rect commands for this display unit.
* Cleared by the helper before the first @clip call. Updated by the @clip
* callback.
......@@ -71,6 +72,7 @@ struct vmw_kms_dirty {
struct vmw_private *dev_priv;
struct vmw_display_unit *unit;
void *cmd;
struct drm_crtc *crtc;
u32 num_hits;
s32 fb_x;
s32 fb_y;
......@@ -175,7 +177,6 @@ struct vmw_plane_state {
int pinned;
/* For CPU Blit */
struct ttm_bo_kmap_obj host_map;
unsigned int cpp;
};
......@@ -287,7 +288,8 @@ int vmw_kms_helper_dirty(struct vmw_private *dev_priv,
int vmw_kms_helper_buffer_prepare(struct vmw_private *dev_priv,
struct vmw_dma_buffer *buf,
bool interruptible,
bool validate_as_mob);
bool validate_as_mob,
bool for_cpu_blit);
void vmw_kms_helper_buffer_revert(struct vmw_dma_buffer *buf);
void vmw_kms_helper_buffer_finish(struct vmw_private *dev_priv,
struct drm_file *file_priv,
......@@ -398,20 +400,23 @@ int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv,
s32 dest_x,
s32 dest_y,
unsigned num_clips, int inc,
struct vmw_fence_obj **out_fence);
struct vmw_fence_obj **out_fence,
struct drm_crtc *crtc);
int vmw_kms_sou_do_dmabuf_dirty(struct vmw_private *dev_priv,
struct vmw_framebuffer *framebuffer,
struct drm_clip_rect *clips,
struct drm_vmw_rect *vclips,
unsigned num_clips, int increment,
bool interruptible,
struct vmw_fence_obj **out_fence);
struct vmw_fence_obj **out_fence,
struct drm_crtc *crtc);
int vmw_kms_sou_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);
uint32_t num_clips,
struct drm_crtc *crtc);
/*
* Screen Target Display Unit functions - vmwgfx_stdu.c
......@@ -425,7 +430,8 @@ int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv,
s32 dest_x,
s32 dest_y,
unsigned num_clips, int inc,
struct vmw_fence_obj **out_fence);
struct vmw_fence_obj **out_fence,
struct drm_crtc *crtc);
int vmw_kms_stdu_dma(struct vmw_private *dev_priv,
struct drm_file *file_priv,
struct vmw_framebuffer *vfb,
......@@ -435,7 +441,8 @@ int vmw_kms_stdu_dma(struct vmw_private *dev_priv,
uint32_t num_clips,
int increment,
bool to_surface,
bool interruptible);
bool interruptible,
struct drm_crtc *crtc);
int vmw_kms_set_config(struct drm_mode_set *set,
struct drm_modeset_acquire_ctx *ctx);
......
......@@ -328,7 +328,7 @@ int vmw_host_get_guestinfo(const char *guest_info_param,
{
struct rpc_channel channel;
char *msg, *reply = NULL;
size_t msg_len, reply_len = 0;
size_t reply_len = 0;
int ret = 0;
......@@ -338,15 +338,12 @@ int vmw_host_get_guestinfo(const char *guest_info_param,
if (!guest_info_param || !length)
return -EINVAL;
msg_len = strlen(guest_info_param) + strlen("info-get ") + 1;
msg = kzalloc(msg_len, GFP_KERNEL);
msg = kasprintf(GFP_KERNEL, "info-get %s", guest_info_param);
if (!msg) {
DRM_ERROR("Cannot allocate memory to get %s", guest_info_param);
return -ENOMEM;
}
sprintf(msg, "info-get %s", guest_info_param);
if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM) ||
vmw_send_msg(&channel, msg) ||
vmw_recv_msg(&channel, (void *) &reply, &reply_len) ||
......@@ -388,7 +385,6 @@ int vmw_host_log(const char *log)
{
struct rpc_channel channel;
char *msg;
int msg_len;
int ret = 0;
......@@ -398,15 +394,12 @@ int vmw_host_log(const char *log)
if (!log)
return ret;
msg_len = strlen(log) + strlen("log ") + 1;
msg = kzalloc(msg_len, GFP_KERNEL);
msg = kasprintf(GFP_KERNEL, "log %s", log);
if (!msg) {
DRM_ERROR("Cannot allocate memory for log message\n");
return -ENOMEM;
}
sprintf(msg, "log %s", log);
if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM) ||
vmw_send_msg(&channel, msg) ||
vmw_close_channel(&channel)) {
......
......@@ -354,6 +354,7 @@ void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo)
{
struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
vmw_dma_buffer_unmap(vmw_bo);
kfree(vmw_bo);
}
......@@ -361,6 +362,7 @@ static void vmw_user_dmabuf_destroy(struct ttm_buffer_object *bo)
{
struct vmw_user_dma_buffer *vmw_user_bo = vmw_user_dma_buffer(bo);
vmw_dma_buffer_unmap(&vmw_user_bo->dma);
ttm_prime_object_kfree(vmw_user_bo, prime);
}
......@@ -1239,6 +1241,12 @@ void vmw_resource_move_notify(struct ttm_buffer_object *bo,
dma_buf = container_of(bo, struct vmw_dma_buffer, base);
/*
* Kill any cached kernel maps before move. An optimization could
* be to do this iff source or destination memory type is VRAM.
*/
vmw_dma_buffer_unmap(dma_buf);
if (mem->mem_type != VMW_PL_MOB) {
struct vmw_resource *res, *n;
struct ttm_validate_buffer val_buf;
......@@ -1262,6 +1270,21 @@ void vmw_resource_move_notify(struct ttm_buffer_object *bo,
}
/**
* vmw_resource_swap_notify - swapout notify callback.
*
* @bo: The buffer object to be swapped out.
*/
void vmw_resource_swap_notify(struct ttm_buffer_object *bo)
{
if (bo->destroy != vmw_dmabuf_bo_free &&
bo->destroy != vmw_user_dmabuf_destroy)
return;
/* Kill any cached kernel maps before swapout */
vmw_dma_buffer_unmap(vmw_dma_buffer(bo));
}
/**
* vmw_query_readback_all - Read back cached query states
......
......@@ -316,69 +316,21 @@ static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc,
struct drm_modeset_acquire_ctx *ctx)
{
struct vmw_private *dev_priv = vmw_priv(crtc->dev);
struct drm_framebuffer *old_fb = crtc->primary->fb;
struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb);
struct vmw_fence_obj *fence = NULL;
struct drm_vmw_rect vclips;
int ret;
if (!vmw_kms_crtc_flippable(dev_priv, crtc))
return -EINVAL;
flags &= ~DRM_MODE_PAGE_FLIP_ASYNC;
ret = drm_atomic_helper_page_flip(crtc, new_fb, NULL, flags, ctx);
ret = drm_atomic_helper_page_flip(crtc, new_fb, event, flags, ctx);
if (ret) {
DRM_ERROR("Page flip error %d.\n", ret);
return ret;
}
/* do a full screen dirty update */
vclips.x = crtc->x;
vclips.y = crtc->y;
vclips.w = crtc->mode.hdisplay;
vclips.h = crtc->mode.vdisplay;
if (vfb->dmabuf)
ret = vmw_kms_sou_do_dmabuf_dirty(dev_priv, vfb,
NULL, &vclips, 1, 1,
true, &fence);
else
ret = vmw_kms_sou_do_surface_dirty(dev_priv, vfb,
NULL, &vclips, NULL,
0, 0, 1, 1, &fence);
if (ret != 0)
goto out_no_fence;
if (!fence) {
ret = -EINVAL;
goto out_no_fence;
}
if (event) {
struct drm_file *file_priv = event->base.file_priv;
ret = vmw_event_fence_action_queue(file_priv, fence,
&event->base,
&event->event.vbl.tv_sec,
&event->event.vbl.tv_usec,
true);
}
/*
* No need to hold on to this now. The only cleanup
* we need to do if we fail is unref the fence.
*/
vmw_fence_obj_unreference(&fence);
if (vmw_crtc_to_du(crtc)->is_implicit)
vmw_kms_update_implicit_fb(dev_priv, crtc);
return ret;
out_no_fence:
drm_atomic_set_fb_for_plane(crtc->primary->state, old_fb);
return ret;
}
static const struct drm_crtc_funcs vmw_screen_object_crtc_funcs = {
......@@ -453,7 +405,11 @@ vmw_sou_primary_plane_cleanup_fb(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);
struct drm_crtc *crtc = plane->state->crtc ?
plane->state->crtc : old_state->crtc;
if (vps->dmabuf)
vmw_dmabuf_unpin(vmw_priv(crtc->dev), vps->dmabuf, false);
vmw_dmabuf_unreference(&vps->dmabuf);
vps->dmabuf_size = 0;
......@@ -491,10 +447,17 @@ vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane,
}
size = new_state->crtc_w * new_state->crtc_h * 4;
dev_priv = vmw_priv(crtc->dev);
if (vps->dmabuf) {
if (vps->dmabuf_size == size)
return 0;
if (vps->dmabuf_size == size) {
/*
* Note that this might temporarily up the pin-count
* to 2, until cleanup_fb() is called.
*/
return vmw_dmabuf_pin_in_vram(dev_priv, vps->dmabuf,
true);
}
vmw_dmabuf_unreference(&vps->dmabuf);
vps->dmabuf_size = 0;
......@@ -504,7 +467,6 @@ vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane,
if (!vps->dmabuf)
return -ENOMEM;
dev_priv = vmw_priv(crtc->dev);
vmw_svga_enable(dev_priv);
/* After we have alloced the backing store might not be able to
......@@ -515,13 +477,16 @@ vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane,
&vmw_vram_ne_placement,
false, &vmw_dmabuf_bo_free);
vmw_overlay_resume_all(dev_priv);
if (ret != 0)
if (ret) {
vps->dmabuf = NULL; /* vmw_dmabuf_init frees on error */
else
vps->dmabuf_size = size;
return ret;
}
return ret;
/*
* TTM already thinks the buffer is pinned, but make sure the
* pin_count is upped.
*/
return vmw_dmabuf_pin_in_vram(dev_priv, vps->dmabuf, true);
}
......@@ -530,9 +495,71 @@ vmw_sou_primary_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
struct drm_crtc *crtc = plane->state->crtc;
struct drm_pending_vblank_event *event = NULL;
struct vmw_fence_obj *fence = NULL;
int ret;
if (crtc && plane->state->fb) {
struct vmw_private *dev_priv = vmw_priv(crtc->dev);
struct vmw_framebuffer *vfb =
vmw_framebuffer_to_vfb(plane->state->fb);
struct drm_vmw_rect vclips;
vclips.x = crtc->x;
vclips.y = crtc->y;
vclips.w = crtc->mode.hdisplay;
vclips.h = crtc->mode.vdisplay;
if (vfb->dmabuf)
ret = vmw_kms_sou_do_dmabuf_dirty(dev_priv, vfb, NULL,
&vclips, 1, 1, true,
&fence, crtc);
else
ret = vmw_kms_sou_do_surface_dirty(dev_priv, vfb, NULL,
&vclips, NULL, 0, 0,
1, 1, &fence, crtc);
/*
* We cannot really fail this function, so if we do, then output
* an error and maintain consistent atomic state.
*/
if (ret != 0)
DRM_ERROR("Failed to update screen.\n");
if (crtc)
crtc->primary->fb = plane->state->fb;
} else {
/*
* When disabling a plane, CRTC and FB should always be NULL
* together, otherwise it's an error.
* Here primary plane is being disable so should really blank
* the screen object display unit, if not already done.
*/
return;
}
event = crtc->state->event;
/*
* In case of failure and other cases, vblank event will be sent in
* vmw_du_crtc_atomic_flush.
*/
if (event && fence) {
struct drm_file *file_priv = event->base.file_priv;
ret = vmw_event_fence_action_queue(file_priv,
fence,
&event->base,
&event->event.vbl.tv_sec,
&event->event.vbl.tv_usec,
true);
if (unlikely(ret != 0))
DRM_ERROR("Failed to queue event on fence.\n");
else
crtc->state->event = NULL;
}
if (fence)
vmw_fence_obj_unreference(&fence);
}
......@@ -892,6 +919,7 @@ static void vmw_sou_surface_clip(struct vmw_kms_dirty *dirty)
* @out_fence: If non-NULL, will return a ref-counted pointer to a
* struct vmw_fence_obj. The returned fence pointer may be NULL in which
* case the device has already synchronized.
* @crtc: If crtc is passed, perform surface dirty on that crtc only.
*
* Returns 0 on success, negative error code on failure. -ERESTARTSYS if
* interrupted.
......@@ -904,7 +932,8 @@ int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv,
s32 dest_x,
s32 dest_y,
unsigned num_clips, int inc,
struct vmw_fence_obj **out_fence)
struct vmw_fence_obj **out_fence,
struct drm_crtc *crtc)
{
struct vmw_framebuffer_surface *vfbs =
container_of(framebuffer, typeof(*vfbs), base);
......@@ -923,6 +952,7 @@ int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv,
sdirty.base.dev_priv = dev_priv;
sdirty.base.fifo_reserve_size = sizeof(struct vmw_kms_sou_dirty_cmd) +
sizeof(SVGASignedRect) * num_clips;
sdirty.base.crtc = crtc;
sdirty.sid = srf->id;
sdirty.left = sdirty.top = S32_MAX;
......@@ -994,6 +1024,7 @@ static void vmw_sou_dmabuf_clip(struct vmw_kms_dirty *dirty)
* @out_fence: If non-NULL, will return a ref-counted pointer to a
* struct vmw_fence_obj. The returned fence pointer may be NULL in which
* case the device has already synchronized.
* @crtc: If crtc is passed, perform dmabuf dirty on that crtc only.
*
* Returns 0 on success, negative error code on failure. -ERESTARTSYS if
* interrupted.
......@@ -1004,7 +1035,8 @@ int vmw_kms_sou_do_dmabuf_dirty(struct vmw_private *dev_priv,
struct drm_vmw_rect *vclips,
unsigned num_clips, int increment,
bool interruptible,
struct vmw_fence_obj **out_fence)
struct vmw_fence_obj **out_fence,
struct drm_crtc *crtc)
{
struct vmw_dma_buffer *buf =
container_of(framebuffer, struct vmw_framebuffer_dmabuf,
......@@ -1013,7 +1045,7 @@ int vmw_kms_sou_do_dmabuf_dirty(struct vmw_private *dev_priv,
int ret;
ret = vmw_kms_helper_buffer_prepare(dev_priv, buf, interruptible,
false);
false, false);
if (ret)
return ret;
......@@ -1021,6 +1053,7 @@ int vmw_kms_sou_do_dmabuf_dirty(struct vmw_private *dev_priv,
if (unlikely(ret != 0))
goto out_revert;
dirty.crtc = crtc;
dirty.fifo_commit = vmw_sou_dmabuf_fifo_commit;
dirty.clip = vmw_sou_dmabuf_clip;
dirty.fifo_reserve_size = sizeof(struct vmw_kms_sou_dmabuf_blit) *
......@@ -1092,6 +1125,7 @@ static void vmw_sou_readback_clip(struct vmw_kms_dirty *dirty)
* 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.
* @crtc: If crtc is passed, readback on that crtc only.
*
* Returns 0 on success, negative error code on failure. -ERESTARTSYS if
* interrupted.
......@@ -1101,14 +1135,16 @@ int vmw_kms_sou_readback(struct vmw_private *dev_priv,
struct vmw_framebuffer *vfb,
struct drm_vmw_fence_rep __user *user_fence_rep,
struct drm_vmw_rect *vclips,
uint32_t num_clips)
uint32_t num_clips,
struct drm_crtc *crtc)
{
struct vmw_dma_buffer *buf =
container_of(vfb, struct vmw_framebuffer_dmabuf, base)->buffer;
struct vmw_kms_dirty dirty;
int ret;
ret = vmw_kms_helper_buffer_prepare(dev_priv, buf, true, false);
ret = vmw_kms_helper_buffer_prepare(dev_priv, buf, true, false,
false);
if (ret)
return ret;
......@@ -1116,6 +1152,7 @@ int vmw_kms_sou_readback(struct vmw_private *dev_priv,
if (unlikely(ret != 0))
goto out_revert;
dirty.crtc = crtc;
dirty.fifo_commit = vmw_sou_readback_fifo_commit;
dirty.clip = vmw_sou_readback_clip;
dirty.fifo_reserve_size = sizeof(struct vmw_kms_sou_readback_blit) *
......
This diff is collapsed.
......@@ -345,7 +345,6 @@ static void vmw_hw_surface_destroy(struct vmw_resource *res)
dev_priv->used_memory_size -= res->backup_size;
mutex_unlock(&dev_priv->cmdbuf_mutex);
}
vmw_fifo_resource_dec(dev_priv);
}
/**
......@@ -407,6 +406,8 @@ static int vmw_legacy_srf_create(struct vmw_resource *res)
vmw_surface_define_encode(srf, cmd);
vmw_fifo_commit(dev_priv, submit_size);
vmw_fifo_resource_inc(dev_priv);
/*
* Surface memory usage accounting.
*/
......@@ -558,6 +559,7 @@ static int vmw_legacy_srf_destroy(struct vmw_resource *res)
*/
vmw_resource_release_id(res);
vmw_fifo_resource_dec(dev_priv);
return 0;
}
......@@ -579,15 +581,11 @@ static int vmw_surface_init(struct vmw_private *dev_priv,
struct vmw_resource *res = &srf->res;
BUG_ON(!res_free);
if (!dev_priv->has_mob)
vmw_fifo_resource_inc(dev_priv);
ret = vmw_resource_init(dev_priv, res, true, res_free,
(dev_priv->has_mob) ? &vmw_gb_surface_func :
&vmw_legacy_surface_func);
if (unlikely(ret != 0)) {
if (!dev_priv->has_mob)
vmw_fifo_resource_dec(dev_priv);
res_free(res);
return ret;
}
......
......@@ -709,6 +709,10 @@ int ttm_fbdev_mmap(struct vm_area_struct *vma, struct ttm_buffer_object *bo);
int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
struct ttm_bo_device *bdev);
void *ttm_kmap_atomic_prot(struct page *page, pgprot_t prot);
void ttm_kunmap_atomic_prot(void *addr, pgprot_t prot);
/**
* ttm_bo_io
*
......
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