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) ...@@ -255,6 +255,54 @@ static int ttm_copy_io_page(void *dst, void *src, unsigned long page)
return 0; 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, static int ttm_copy_io_ttm_page(struct ttm_tt *ttm, void *src,
unsigned long page, unsigned long page,
pgprot_t prot) pgprot_t prot)
...@@ -266,28 +314,13 @@ static int ttm_copy_io_ttm_page(struct ttm_tt *ttm, void *src, ...@@ -266,28 +314,13 @@ static int ttm_copy_io_ttm_page(struct ttm_tt *ttm, void *src,
return -ENOMEM; return -ENOMEM;
src = (void *)((unsigned long)src + (page << PAGE_SHIFT)); src = (void *)((unsigned long)src + (page << PAGE_SHIFT));
dst = ttm_kmap_atomic_prot(d, prot);
#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
if (!dst) if (!dst)
return -ENOMEM; return -ENOMEM;
memcpy_fromio(dst, src, PAGE_SIZE); memcpy_fromio(dst, src, PAGE_SIZE);
#ifdef CONFIG_X86 ttm_kunmap_atomic_prot(dst, prot);
kunmap_atomic(dst);
#else
if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL))
vunmap(dst);
else
kunmap(d);
#endif
return 0; return 0;
} }
...@@ -303,27 +336,13 @@ static int ttm_copy_ttm_io_page(struct ttm_tt *ttm, void *dst, ...@@ -303,27 +336,13 @@ static int ttm_copy_ttm_io_page(struct ttm_tt *ttm, void *dst,
return -ENOMEM; return -ENOMEM;
dst = (void *)((unsigned long)dst + (page << PAGE_SHIFT)); dst = (void *)((unsigned long)dst + (page << PAGE_SHIFT));
#ifdef CONFIG_X86 src = ttm_kmap_atomic_prot(s, prot);
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
if (!src) if (!src)
return -ENOMEM; return -ENOMEM;
memcpy_toio(dst, src, PAGE_SIZE); memcpy_toio(dst, src, PAGE_SIZE);
#ifdef CONFIG_X86 ttm_kunmap_atomic_prot(src, prot);
kunmap_atomic(src);
#else
if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL))
vunmap(src);
else
kunmap(s);
#endif
return 0; return 0;
} }
......
...@@ -7,6 +7,6 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ ...@@ -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_surface.o vmwgfx_prime.o vmwgfx_mob.o vmwgfx_shader.o \
vmwgfx_cmdbuf_res.o vmwgfx_cmdbuf.o vmwgfx_stdu.o \ vmwgfx_cmdbuf_res.o vmwgfx_cmdbuf.o vmwgfx_stdu.o \
vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o vmwgfx_msg.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 obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o
...@@ -372,6 +372,14 @@ SVGAGuestPtr; ...@@ -372,6 +372,14 @@ SVGAGuestPtr;
* PA, not biased by the offset. When the command buffer is finished * 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 * the guest should not read the offset field as there is no guarantee
* what it will set to. * 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 */ #define SVGA_CB_MAX_SIZE (512 * 1024) /* 512 KB */
...@@ -382,7 +390,8 @@ SVGAGuestPtr; ...@@ -382,7 +390,8 @@ SVGAGuestPtr;
typedef enum { typedef enum {
SVGA_CB_CONTEXT_DEVICE = 0x3f, SVGA_CB_CONTEXT_DEVICE = 0x3f,
SVGA_CB_CONTEXT_0 = 0x0, 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; } SVGACBContext;
...@@ -689,6 +698,7 @@ SVGASignedPoint; ...@@ -689,6 +698,7 @@ SVGASignedPoint;
#define SVGA_CAP_CMD_BUFFERS_2 0x04000000 #define SVGA_CAP_CMD_BUFFERS_2 0x04000000
#define SVGA_CAP_GBOBJECTS 0x08000000 #define SVGA_CAP_GBOBJECTS 0x08000000
#define SVGA_CAP_DX 0x10000000 #define SVGA_CAP_DX 0x10000000
#define SVGA_CAP_HP_CMD_QUEUE 0x20000000
#define SVGA_CAP_CMD_RESERVED 0x80000000 #define SVGA_CAP_CMD_RESERVED 0x80000000
......
This diff is collapsed.
...@@ -185,6 +185,22 @@ static const struct ttm_place evictable_placement_flags[] = { ...@@ -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 = { struct ttm_placement vmw_evictable_placement = {
.num_placement = 4, .num_placement = 4,
.placement = evictable_placement_flags, .placement = evictable_placement_flags,
...@@ -213,6 +229,13 @@ struct ttm_placement vmw_mob_ne_placement = { ...@@ -213,6 +229,13 @@ struct ttm_placement vmw_mob_ne_placement = {
.busy_placement = &mob_ne_placement_flags .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 vmw_ttm_tt {
struct ttm_dma_tt dma_ttm; struct ttm_dma_tt dma_ttm;
struct vmw_private *dev_priv; struct vmw_private *dev_priv;
...@@ -841,6 +864,7 @@ static void vmw_move_notify(struct ttm_buffer_object *bo, ...@@ -841,6 +864,7 @@ static void vmw_move_notify(struct ttm_buffer_object *bo,
*/ */
static void vmw_swap_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); (void) ttm_bo_wait(bo, false, false);
} }
......
...@@ -101,6 +101,7 @@ struct vmw_cmdbuf_context { ...@@ -101,6 +101,7 @@ struct vmw_cmdbuf_context {
* @handle: DMA address handle for the command buffer space if @using_mob is * @handle: DMA address handle for the command buffer space if @using_mob is
* false. Immutable. * false. Immutable.
* @size: The size of the command buffer space. Immutable. * @size: The size of the command buffer space. Immutable.
* @num_contexts: Number of contexts actually enabled.
*/ */
struct vmw_cmdbuf_man { struct vmw_cmdbuf_man {
struct mutex cur_mutex; struct mutex cur_mutex;
...@@ -128,6 +129,7 @@ struct vmw_cmdbuf_man { ...@@ -128,6 +129,7 @@ struct vmw_cmdbuf_man {
bool has_pool; bool has_pool;
dma_addr_t handle; dma_addr_t handle;
size_t size; size_t size;
u32 num_contexts;
}; };
/** /**
...@@ -185,7 +187,7 @@ struct vmw_cmdbuf_alloc_info { ...@@ -185,7 +187,7 @@ struct vmw_cmdbuf_alloc_info {
/* Loop over each context in the command buffer manager. */ /* Loop over each context in the command buffer manager. */
#define for_each_cmdbuf_ctx(_man, _i, _ctx) \ #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)) ++(_i), ++(_ctx))
static int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, u32 context, 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) ...@@ -514,6 +516,7 @@ static void vmw_cmdbuf_work_func(struct work_struct *work)
struct list_head restart_head[SVGA_CB_CONTEXT_MAX]; struct list_head restart_head[SVGA_CB_CONTEXT_MAX];
int i; int i;
struct vmw_cmdbuf_context *ctx; struct vmw_cmdbuf_context *ctx;
bool global_block = false;
for_each_cmdbuf_ctx(man, i, ctx) { for_each_cmdbuf_ctx(man, i, ctx) {
INIT_LIST_HEAD(&restart_head[i]); INIT_LIST_HEAD(&restart_head[i]);
...@@ -531,6 +534,7 @@ static void vmw_cmdbuf_work_func(struct work_struct *work) ...@@ -531,6 +534,7 @@ static void vmw_cmdbuf_work_func(struct work_struct *work)
list_del_init(&entry->list); list_del_init(&entry->list);
restart[entry->cb_context] = true; restart[entry->cb_context] = true;
global_block = true;
if (!vmw_cmd_describe(header, &error_cmd_size, &cmd_name)) { if (!vmw_cmd_describe(header, &error_cmd_size, &cmd_name)) {
DRM_ERROR("Unknown command causing device error.\n"); DRM_ERROR("Unknown command causing device error.\n");
...@@ -564,23 +568,21 @@ static void vmw_cmdbuf_work_func(struct work_struct *work) ...@@ -564,23 +568,21 @@ static void vmw_cmdbuf_work_func(struct work_struct *work)
cb_hdr->length -= new_start_offset; cb_hdr->length -= new_start_offset;
cb_hdr->errorOffset = 0; cb_hdr->errorOffset = 0;
cb_hdr->offset = 0; cb_hdr->offset = 0;
list_add_tail(&entry->list, &restart_head[entry->cb_context]); 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); spin_unlock(&man->lock);
/* Preempt all contexts with errors */ /* Preempt all contexts */
for_each_cmdbuf_ctx(man, i, ctx) { if (global_block && vmw_cmdbuf_preempt(man, 0))
if (ctx->block_submission && vmw_cmdbuf_preempt(man, i)) DRM_ERROR("Failed preempting command buffer contexts\n");
DRM_ERROR("Failed preempting command buffer "
"context %u.\n", i);
}
spin_lock(&man->lock); spin_lock(&man->lock);
for_each_cmdbuf_ctx(man, i, ctx) { for_each_cmdbuf_ctx(man, i, ctx) {
if (!ctx->block_submission)
continue;
/* Move preempted command buffers to the preempted queue. */ /* Move preempted command buffers to the preempted queue. */
vmw_cmdbuf_ctx_process(man, ctx, &dummy); vmw_cmdbuf_ctx_process(man, ctx, &dummy);
...@@ -594,19 +596,16 @@ static void vmw_cmdbuf_work_func(struct work_struct *work) ...@@ -594,19 +596,16 @@ static void vmw_cmdbuf_work_func(struct work_struct *work)
* Finally add all command buffers first in the submitted * Finally add all command buffers first in the submitted
* queue, to rerun them. * queue, to rerun them.
*/ */
list_splice_init(&restart_head[i], &ctx->submitted);
ctx->block_submission = false; ctx->block_submission = false;
list_splice_init(&restart_head[i], &ctx->submitted);
} }
vmw_cmdbuf_man_process(man); vmw_cmdbuf_man_process(man);
spin_unlock(&man->lock); spin_unlock(&man->lock);
for_each_cmdbuf_ctx(man, i, ctx) { if (global_block && vmw_cmdbuf_startstop(man, 0, true))
if (restart[i] && vmw_cmdbuf_startstop(man, i, true)) DRM_ERROR("Failed restarting command buffer contexts\n");
DRM_ERROR("Failed restarting command buffer "
"context %u.\n", i);
}
/* Send a new fence in case one was removed */ /* Send a new fence in case one was removed */
if (send_fence) { if (send_fence) {
...@@ -1307,6 +1306,8 @@ struct vmw_cmdbuf_man *vmw_cmdbuf_man_create(struct vmw_private *dev_priv) ...@@ -1307,6 +1306,8 @@ struct vmw_cmdbuf_man *vmw_cmdbuf_man_create(struct vmw_private *dev_priv)
if (!man) if (!man)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
man->num_contexts = (dev_priv->capabilities & SVGA_CAP_HP_CMD_QUEUE) ?
2 : 1;
man->headers = dma_pool_create("vmwgfx cmdbuf", man->headers = dma_pool_create("vmwgfx cmdbuf",
&dev_priv->dev->pdev->dev, &dev_priv->dev->pdev->dev,
sizeof(SVGACBHeader), sizeof(SVGACBHeader),
...@@ -1341,14 +1342,11 @@ struct vmw_cmdbuf_man *vmw_cmdbuf_man_create(struct vmw_private *dev_priv) ...@@ -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); INIT_WORK(&man->work, &vmw_cmdbuf_work_func);
vmw_generic_waiter_add(dev_priv, SVGA_IRQFLAG_ERROR, vmw_generic_waiter_add(dev_priv, SVGA_IRQFLAG_ERROR,
&dev_priv->error_waiters); &dev_priv->error_waiters);
for_each_cmdbuf_ctx(man, i, ctx) { ret = vmw_cmdbuf_startstop(man, 0, true);
ret = vmw_cmdbuf_startstop(man, i, true); if (ret) {
if (ret) { DRM_ERROR("Failed starting command buffer contexts\n");
DRM_ERROR("Failed starting command buffer " vmw_cmdbuf_man_destroy(man);
"context %u.\n", i); return ERR_PTR(ret);
vmw_cmdbuf_man_destroy(man);
return ERR_PTR(ret);
}
} }
return man; return man;
...@@ -1398,16 +1396,11 @@ void vmw_cmdbuf_remove_pool(struct vmw_cmdbuf_man *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) void vmw_cmdbuf_man_destroy(struct vmw_cmdbuf_man *man)
{ {
struct vmw_cmdbuf_context *ctx;
unsigned int i;
WARN_ON_ONCE(man->has_pool); WARN_ON_ONCE(man->has_pool);
(void) vmw_cmdbuf_idle(man, false, 10*HZ); (void) vmw_cmdbuf_idle(man, false, 10*HZ);
for_each_cmdbuf_ctx(man, i, ctx) if (vmw_cmdbuf_startstop(man, 0, false))
if (vmw_cmdbuf_startstop(man, i, false)) DRM_ERROR("Failed stopping command buffer contexts.\n");
DRM_ERROR("Failed stopping command buffer "
"context %u.\n", i);
vmw_generic_waiter_remove(man->dev_priv, SVGA_IRQFLAG_ERROR, vmw_generic_waiter_remove(man->dev_priv, SVGA_IRQFLAG_ERROR,
&man->dev_priv->error_waiters); &man->dev_priv->error_waiters);
......
...@@ -323,3 +323,54 @@ void vmw_bo_pin_reserved(struct vmw_dma_buffer *vbo, bool pin) ...@@ -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); 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) ...@@ -301,6 +301,8 @@ static void vmw_print_capabilities(uint32_t capabilities)
DRM_INFO(" Guest Backed Resources.\n"); DRM_INFO(" Guest Backed Resources.\n");
if (capabilities & SVGA_CAP_DX) if (capabilities & SVGA_CAP_DX)
DRM_INFO(" DX Features.\n"); 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, ...@@ -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_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM);
ttm_vt_unlock(&dev_priv->fbdev_master.lock); ttm_vt_unlock(&dev_priv->fbdev_master.lock);
if (dev_priv->enable_fb) vmw_fb_refresh(dev_priv);
vmw_fb_on(dev_priv);
} }
/** /**
...@@ -1368,28 +1369,23 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, ...@@ -1368,28 +1369,23 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
switch (val) { switch (val) {
case PM_HIBERNATION_PREPARE: 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. * Take the reservation sem in write mode, which will make sure
* Buffer contents is moved to swappable memory. * 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); ttm_suspend_lock(&dev_priv->reservation_sem);
vmw_resource_evict_all(dev_priv); dev_priv->suspend_locked = true;
vmw_release_device_early(dev_priv);
ttm_bo_swapout_all(&dev_priv->bdev);
vmw_fence_fifo_down(dev_priv->fman);
break; break;
case PM_POST_HIBERNATION: case PM_POST_HIBERNATION:
case PM_POST_RESTORE: case PM_POST_RESTORE:
vmw_fence_fifo_up(dev_priv->fman); if (READ_ONCE(dev_priv->suspend_locked)) {
ttm_suspend_unlock(&dev_priv->reservation_sem); dev_priv->suspend_locked = false;
if (dev_priv->enable_fb) ttm_suspend_unlock(&dev_priv->reservation_sem);
vmw_fb_on(dev_priv); }
break;
case PM_RESTORE_PREPARE:
break; break;
default: default:
break; break;
...@@ -1440,25 +1436,48 @@ static int vmw_pm_freeze(struct device *kdev) ...@@ -1440,25 +1436,48 @@ static int vmw_pm_freeze(struct device *kdev)
struct pci_dev *pdev = to_pci_dev(kdev); struct pci_dev *pdev = to_pci_dev(kdev);
struct drm_device *dev = pci_get_drvdata(pdev); struct drm_device *dev = pci_get_drvdata(pdev);
struct vmw_private *dev_priv = vmw_priv(dev); 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) 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) { if (atomic_read(&dev_priv->num_fifo_resources) != 0) {
DRM_ERROR("Can't hibernate while 3D resources are active.\n"); DRM_ERROR("Can't hibernate while 3D resources are active.\n");
if (dev_priv->enable_fb) if (dev_priv->enable_fb)
vmw_fifo_resource_inc(dev_priv); vmw_fifo_resource_inc(dev_priv);
WARN_ON(vmw_request_device_late(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; return -EBUSY;
} }
if (dev_priv->enable_fb) vmw_fence_fifo_down(dev_priv->fman);
__vmw_svga_disable(dev_priv); __vmw_svga_disable(dev_priv);
vmw_release_device_late(dev_priv); vmw_release_device_late(dev_priv);
return 0; return 0;
} }
...@@ -1482,7 +1501,16 @@ static int vmw_pm_restore(struct device *kdev) ...@@ -1482,7 +1501,16 @@ static int vmw_pm_restore(struct device *kdev)
if (dev_priv->enable_fb) if (dev_priv->enable_fb)
__vmw_svga_enable(dev_priv); __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; return 0;
} }
......
...@@ -43,10 +43,10 @@ ...@@ -43,10 +43,10 @@
#include <linux/sync_file.h> #include <linux/sync_file.h>
#define VMWGFX_DRIVER_NAME "vmwgfx" #define VMWGFX_DRIVER_NAME "vmwgfx"
#define VMWGFX_DRIVER_DATE "20170612" #define VMWGFX_DRIVER_DATE "20180322"
#define VMWGFX_DRIVER_MAJOR 2 #define VMWGFX_DRIVER_MAJOR 2
#define VMWGFX_DRIVER_MINOR 14 #define VMWGFX_DRIVER_MINOR 14
#define VMWGFX_DRIVER_PATCHLEVEL 0 #define VMWGFX_DRIVER_PATCHLEVEL 1
#define VMWGFX_FILE_PAGE_OFFSET 0x00100000 #define VMWGFX_FILE_PAGE_OFFSET 0x00100000
#define VMWGFX_FIFO_STATIC_SIZE (1024*1024) #define VMWGFX_FIFO_STATIC_SIZE (1024*1024)
#define VMWGFX_MAX_RELOCATIONS 2048 #define VMWGFX_MAX_RELOCATIONS 2048
...@@ -92,6 +92,8 @@ struct vmw_dma_buffer { ...@@ -92,6 +92,8 @@ struct vmw_dma_buffer {
s32 pin_count; s32 pin_count;
/* Not ref-counted. Protected by binding_mutex */ /* Not ref-counted. Protected by binding_mutex */
struct vmw_resource *dx_query_ctx; struct vmw_resource *dx_query_ctx;
/* Protected by reservation */
struct ttm_bo_kmap_obj map;
}; };
/** /**
...@@ -423,6 +425,7 @@ struct vmw_private { ...@@ -423,6 +425,7 @@ struct vmw_private {
struct vmw_framebuffer *implicit_fb; struct vmw_framebuffer *implicit_fb;
struct mutex global_kms_state_mutex; struct mutex global_kms_state_mutex;
spinlock_t cursor_lock; spinlock_t cursor_lock;
struct drm_atomic_state *suspend_state;
/* /*
* Context and surface management. * Context and surface management.
...@@ -494,8 +497,8 @@ struct vmw_private { ...@@ -494,8 +497,8 @@ struct vmw_private {
struct vmw_master *active_master; struct vmw_master *active_master;
struct vmw_master fbdev_master; struct vmw_master fbdev_master;
struct notifier_block pm_nb; struct notifier_block pm_nb;
bool suspended;
bool refuse_hibernation; bool refuse_hibernation;
bool suspend_locked;
struct mutex release_mutex; struct mutex release_mutex;
atomic_t num_fifo_resources; atomic_t num_fifo_resources;
...@@ -673,11 +676,13 @@ extern void vmw_resource_move_notify(struct ttm_buffer_object *bo, ...@@ -673,11 +676,13 @@ extern void vmw_resource_move_notify(struct ttm_buffer_object *bo,
struct ttm_mem_reg *mem); struct ttm_mem_reg *mem);
extern void vmw_query_move_notify(struct ttm_buffer_object *bo, extern void vmw_query_move_notify(struct ttm_buffer_object *bo,
struct ttm_mem_reg *mem); 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 int vmw_query_readback_all(struct vmw_dma_buffer *dx_query_mob);
extern void vmw_fence_single_bo(struct ttm_buffer_object *bo, extern void vmw_fence_single_bo(struct ttm_buffer_object *bo,
struct vmw_fence_obj *fence); struct vmw_fence_obj *fence);
extern void vmw_resource_evict_all(struct vmw_private *dev_priv); extern void vmw_resource_evict_all(struct vmw_private *dev_priv);
/** /**
* DMA buffer helper routines - vmwgfx_dmabuf.c * DMA buffer helper routines - vmwgfx_dmabuf.c
*/ */
...@@ -700,6 +705,8 @@ extern int vmw_dmabuf_unpin(struct vmw_private *vmw_priv, ...@@ -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, extern void vmw_bo_get_guest_ptr(const struct ttm_buffer_object *buf,
SVGAGuestPtr *ptr); SVGAGuestPtr *ptr);
extern void vmw_bo_pin_reserved(struct vmw_dma_buffer *bo, bool pin); 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 * Misc Ioctl functionality - vmwgfx_ioctl.c
...@@ -766,6 +773,7 @@ extern struct ttm_placement vmw_evictable_placement; ...@@ -766,6 +773,7 @@ extern struct ttm_placement vmw_evictable_placement;
extern struct ttm_placement vmw_srf_placement; extern struct ttm_placement vmw_srf_placement;
extern struct ttm_placement vmw_mob_placement; extern struct ttm_placement vmw_mob_placement;
extern struct ttm_placement vmw_mob_ne_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 struct ttm_bo_driver vmw_bo_driver;
extern int vmw_dma_quiescent(struct drm_device *dev); extern int vmw_dma_quiescent(struct drm_device *dev);
extern int vmw_bo_map_dma(struct ttm_buffer_object *bo); extern int vmw_bo_map_dma(struct ttm_buffer_object *bo);
...@@ -902,6 +910,7 @@ int vmw_fb_init(struct vmw_private *vmw_priv); ...@@ -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_close(struct vmw_private *dev_priv);
int vmw_fb_off(struct vmw_private *vmw_priv); int vmw_fb_off(struct vmw_private *vmw_priv);
int vmw_fb_on(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 * Kernel modesetting - vmwgfx_kms.c
...@@ -938,6 +947,8 @@ int vmw_kms_present(struct vmw_private *dev_priv, ...@@ -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, int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv); struct drm_file *file_priv);
void vmw_kms_legacy_hotspot_clear(struct vmw_private *dev_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, int vmw_dumb_create(struct drm_file *file_priv,
struct drm_device *dev, struct drm_device *dev,
...@@ -1165,6 +1176,53 @@ extern int vmw_cmdbuf_cur_flush(struct vmw_cmdbuf_man *man, ...@@ -1165,6 +1176,53 @@ extern int vmw_cmdbuf_cur_flush(struct vmw_cmdbuf_man *man,
bool interruptible); bool interruptible);
extern void vmw_cmdbuf_irqthread(struct vmw_cmdbuf_man *man); 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 * Inline helper functions
......
...@@ -43,8 +43,6 @@ struct vmw_fb_par { ...@@ -43,8 +43,6 @@ struct vmw_fb_par {
struct mutex bo_mutex; struct mutex bo_mutex;
struct vmw_dma_buffer *vmw_bo; struct vmw_dma_buffer *vmw_bo;
struct ttm_bo_kmap_obj map;
void *bo_ptr;
unsigned bo_size; unsigned bo_size;
struct drm_framebuffer *set_fb; struct drm_framebuffer *set_fb;
struct drm_display_mode *set_mode; struct drm_display_mode *set_mode;
...@@ -163,10 +161,17 @@ static int vmw_fb_blank(int blank, struct fb_info *info) ...@@ -163,10 +161,17 @@ static int vmw_fb_blank(int blank, struct fb_info *info)
return 0; 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) static void vmw_fb_dirty_flush(struct work_struct *work)
{ {
struct vmw_fb_par *par = container_of(work, struct vmw_fb_par, 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) ...@@ -174,13 +179,15 @@ static void vmw_fb_dirty_flush(struct work_struct *work)
struct vmw_private *vmw_priv = par->vmw_priv; struct vmw_private *vmw_priv = par->vmw_priv;
struct fb_info *info = vmw_priv->fb_info; struct fb_info *info = vmw_priv->fb_info;
unsigned long irq_flags; 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; u32 cpp, max_x, max_y;
struct drm_clip_rect clip; struct drm_clip_rect clip;
struct drm_framebuffer *cur_fb; struct drm_framebuffer *cur_fb;
u8 *src_ptr, *dst_ptr; 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; return;
mutex_lock(&par->bo_mutex); mutex_lock(&par->bo_mutex);
...@@ -188,10 +195,16 @@ static void vmw_fb_dirty_flush(struct work_struct *work) ...@@ -188,10 +195,16 @@ static void vmw_fb_dirty_flush(struct work_struct *work)
if (!cur_fb) if (!cur_fb)
goto out_unlock; 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); spin_lock_irqsave(&par->dirty.lock, irq_flags);
if (!par->dirty.active) { if (!par->dirty.active) {
spin_unlock_irqrestore(&par->dirty.lock, irq_flags); 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) ...@@ -221,7 +234,7 @@ static void vmw_fb_dirty_flush(struct work_struct *work)
spin_unlock_irqrestore(&par->dirty.lock, irq_flags); spin_unlock_irqrestore(&par->dirty.lock, irq_flags);
if (w && h) { if (w && h) {
dst_ptr = (u8 *)par->bo_ptr + dst_ptr = (u8 *)virtual +
(dst_y1 * par->set_fb->pitches[0] + dst_x1 * cpp); (dst_y1 * par->set_fb->pitches[0] + dst_x1 * cpp);
src_ptr = (u8 *)par->vmalloc + src_ptr = (u8 *)par->vmalloc +
((dst_y1 + par->fb_y) * info->fix.line_length + ((dst_y1 + par->fb_y) * info->fix.line_length +
...@@ -237,7 +250,12 @@ static void vmw_fb_dirty_flush(struct work_struct *work) ...@@ -237,7 +250,12 @@ static void vmw_fb_dirty_flush(struct work_struct *work)
clip.x2 = dst_x2; clip.x2 = dst_x2;
clip.y1 = dst_y1; clip.y1 = dst_y1;
clip.y2 = dst_y2; 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, WARN_ON_ONCE(par->set_fb->funcs->dirty(cur_fb, NULL, 0, 0,
&clip, 1)); &clip, 1));
vmw_fifo_flush(vmw_priv, false); vmw_fifo_flush(vmw_priv, false);
...@@ -504,18 +522,8 @@ static int vmw_fb_kms_detach(struct vmw_fb_par *par, ...@@ -504,18 +522,8 @@ static int vmw_fb_kms_detach(struct vmw_fb_par *par,
par->set_fb = NULL; par->set_fb = NULL;
} }
if (par->vmw_bo && detach_bo) { if (par->vmw_bo && detach_bo && unref_bo)
struct vmw_private *vmw_priv = par->vmw_priv; vmw_dmabuf_unreference(&par->vmw_bo);
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);
}
return 0; return 0;
} }
...@@ -636,38 +644,6 @@ static int vmw_fb_set_par(struct fb_info *info) ...@@ -636,38 +644,6 @@ static int vmw_fb_set_par(struct fb_info *info)
if (ret) if (ret)
goto out_unlock; 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, vmw_fb_dirty_mark(par, par->fb_x, par->fb_y,
par->set_fb->width, par->set_fb->height); par->set_fb->width, par->set_fb->height);
...@@ -883,12 +859,6 @@ int vmw_fb_off(struct vmw_private *vmw_priv) ...@@ -883,12 +859,6 @@ int vmw_fb_off(struct vmw_private *vmw_priv)
flush_delayed_work(&info->deferred_work); flush_delayed_work(&info->deferred_work);
flush_delayed_work(&par->local_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; return 0;
} }
...@@ -904,10 +874,24 @@ int vmw_fb_on(struct vmw_private *vmw_priv) ...@@ -904,10 +874,24 @@ int vmw_fb_on(struct vmw_private *vmw_priv)
info = vmw_priv->fb_info; info = vmw_priv->fb_info;
par = info->par; par = info->par;
vmw_fb_set_par(info);
spin_lock_irqsave(&par->dirty.lock, flags); spin_lock_irqsave(&par->dirty.lock, flags);
par->dirty.active = true; par->dirty.active = true;
spin_unlock_irqrestore(&par->dirty.lock, flags); spin_unlock_irqrestore(&par->dirty.lock, flags);
return 0; 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) ...@@ -901,11 +901,12 @@ static void vmw_event_fence_action_seq_passed(struct vmw_fence_action *action)
spin_lock_irq(&dev->event_lock); spin_lock_irq(&dev->event_lock);
if (likely(eaction->tv_sec != NULL)) { if (likely(eaction->tv_sec != NULL)) {
struct timeval tv; struct timespec64 ts;
do_gettimeofday(&tv); ktime_get_ts64(&ts);
*eaction->tv_sec = tv.tv_sec; /* monotonic time, so no y2038 overflow */
*eaction->tv_usec = tv.tv_usec; *eaction->tv_sec = ts.tv_sec;
*eaction->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
} }
drm_send_event_locked(dev, eaction->event); drm_send_event_locked(dev, eaction->event);
......
...@@ -393,13 +393,13 @@ vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, ...@@ -393,13 +393,13 @@ vmw_du_cursor_plane_atomic_update(struct drm_plane *plane,
du->cursor_surface = vps->surf; du->cursor_surface = vps->surf;
du->cursor_dmabuf = vps->dmabuf; du->cursor_dmabuf = vps->dmabuf;
/* setup new image */
if (vps->surf) { if (vps->surf) {
du->cursor_age = du->cursor_surface->snooper.age; du->cursor_age = du->cursor_surface->snooper.age;
ret = vmw_cursor_update_image(dev_priv, ret = vmw_cursor_update_image(dev_priv,
vps->surf->snooper.image, vps->surf->snooper.image,
64, 64, hotspot_x, hotspot_y); 64, 64, hotspot_x,
hotspot_y);
} else if (vps->dmabuf) { } else if (vps->dmabuf) {
ret = vmw_cursor_update_dmabuf(dev_priv, vps->dmabuf, ret = vmw_cursor_update_dmabuf(dev_priv, vps->dmabuf,
plane->state->crtc_w, plane->state->crtc_w,
...@@ -497,11 +497,22 @@ int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, ...@@ -497,11 +497,22 @@ int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane,
struct vmw_surface *surface = NULL; struct vmw_surface *surface = NULL;
struct drm_framebuffer *fb = new_state->fb; 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 */ /* Turning off */
if (!fb) if (!fb)
return ret; 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 */ /* A lot of the code assumes this */
if (new_state->crtc_w != 64 || new_state->crtc_h != 64) { if (new_state->crtc_w != 64 || new_state->crtc_h != 64) {
DRM_ERROR("Invalid cursor dimensions (%d, %d)\n", DRM_ERROR("Invalid cursor dimensions (%d, %d)\n",
...@@ -566,13 +577,9 @@ void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc, ...@@ -566,13 +577,9 @@ void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
crtc->state->event = NULL; crtc->state->event = NULL;
spin_lock_irq(&crtc->dev->event_lock); spin_lock_irq(&crtc->dev->event_lock);
if (drm_crtc_vblank_get(crtc) == 0) drm_crtc_send_vblank_event(crtc, event);
drm_crtc_arm_vblank_event(crtc, event);
else
drm_crtc_send_vblank_event(crtc, event);
spin_unlock_irq(&crtc->dev->event_lock); spin_unlock_irq(&crtc->dev->event_lock);
} }
} }
...@@ -675,9 +682,6 @@ vmw_du_plane_duplicate_state(struct drm_plane *plane) ...@@ -675,9 +682,6 @@ vmw_du_plane_duplicate_state(struct drm_plane *plane)
return NULL; return NULL;
vps->pinned = 0; vps->pinned = 0;
/* Mapping is managed by prepare_fb/cleanup_fb */
memset(&vps->host_map, 0, sizeof(vps->host_map));
vps->cpp = 0; vps->cpp = 0;
/* Each ref counted resource needs to be acquired again */ /* Each ref counted resource needs to be acquired again */
...@@ -739,11 +743,6 @@ vmw_du_plane_destroy_state(struct drm_plane *plane, ...@@ -739,11 +743,6 @@ vmw_du_plane_destroy_state(struct drm_plane *plane,
/* Should have been freed by cleanup_fb */ /* 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) if (vps->surf)
vmw_surface_unreference(&vps->surf); vmw_surface_unreference(&vps->surf);
...@@ -888,11 +887,11 @@ static int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, ...@@ -888,11 +887,11 @@ static int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
if (dev_priv->active_display_unit == vmw_du_screen_object) if (dev_priv->active_display_unit == vmw_du_screen_object)
ret = vmw_kms_sou_do_surface_dirty(dev_priv, &vfbs->base, ret = vmw_kms_sou_do_surface_dirty(dev_priv, &vfbs->base,
clips, NULL, NULL, 0, 0, clips, NULL, NULL, 0, 0,
num_clips, inc, NULL); num_clips, inc, NULL, NULL);
else else
ret = vmw_kms_stdu_surface_dirty(dev_priv, &vfbs->base, ret = vmw_kms_stdu_surface_dirty(dev_priv, &vfbs->base,
clips, NULL, NULL, 0, 0, clips, NULL, NULL, 0, 0,
num_clips, inc, NULL); num_clips, inc, NULL, NULL);
vmw_fifo_flush(dev_priv, false); vmw_fifo_flush(dev_priv, false);
ttm_read_unlock(&dev_priv->reservation_sem); ttm_read_unlock(&dev_priv->reservation_sem);
...@@ -928,11 +927,12 @@ int vmw_kms_readback(struct vmw_private *dev_priv, ...@@ -928,11 +927,12 @@ int vmw_kms_readback(struct vmw_private *dev_priv,
switch (dev_priv->active_display_unit) { switch (dev_priv->active_display_unit) {
case vmw_du_screen_object: case vmw_du_screen_object:
return vmw_kms_sou_readback(dev_priv, file_priv, vfb, 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: case vmw_du_screen_target:
return vmw_kms_stdu_dma(dev_priv, file_priv, vfb, return vmw_kms_stdu_dma(dev_priv, file_priv, vfb,
user_fence_rep, NULL, vclips, num_clips, user_fence_rep, NULL, vclips, num_clips,
1, false, true); 1, false, true, NULL);
default: default:
WARN_ONCE(true, WARN_ONCE(true,
"Readback called with invalid display system.\n"); "Readback called with invalid display system.\n");
...@@ -1090,12 +1090,12 @@ static int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, ...@@ -1090,12 +1090,12 @@ static int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
case vmw_du_screen_target: case vmw_du_screen_target:
ret = vmw_kms_stdu_dma(dev_priv, NULL, &vfbd->base, NULL, ret = vmw_kms_stdu_dma(dev_priv, NULL, &vfbd->base, NULL,
clips, NULL, num_clips, increment, clips, NULL, num_clips, increment,
true, true); true, true, NULL);
break; break;
case vmw_du_screen_object: case vmw_du_screen_object:
ret = vmw_kms_sou_do_dmabuf_dirty(dev_priv, &vfbd->base, ret = vmw_kms_sou_do_dmabuf_dirty(dev_priv, &vfbd->base,
clips, NULL, num_clips, clips, NULL, num_clips,
increment, true, NULL); increment, true, NULL, NULL);
break; break;
case vmw_du_legacy: case vmw_du_legacy:
ret = vmw_kms_ldu_do_dmabuf_dirty(dev_priv, &vfbd->base, 0, 0, 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 = { ...@@ -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) static int vmw_framebuffer_pin(struct vmw_framebuffer *vfb)
{ {
struct vmw_private *dev_priv = vmw_priv(vfb->base.dev); struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
struct vmw_dma_buffer *buf; struct vmw_dma_buffer *buf;
struct ttm_placement *placement;
int ret; int ret;
buf = vfb->dmabuf ? vmw_framebuffer_to_vfbd(&vfb->base)->buffer : buf = vfb->dmabuf ? vmw_framebuffer_to_vfbd(&vfb->base)->buffer :
...@@ -1143,12 +1145,24 @@ static int vmw_framebuffer_pin(struct vmw_framebuffer *vfb) ...@@ -1143,12 +1145,24 @@ static int vmw_framebuffer_pin(struct vmw_framebuffer *vfb)
break; break;
case vmw_du_screen_object: case vmw_du_screen_object:
case vmw_du_screen_target: case vmw_du_screen_target:
if (vfb->dmabuf) if (vfb->dmabuf) {
return vmw_dmabuf_pin_in_vram_or_gmr(dev_priv, buf, if (dev_priv->capabilities & SVGA_CAP_3D) {
false); /*
* 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, return vmw_dmabuf_pin_in_placement(dev_priv, buf, placement,
&vmw_mob_placement, false); false);
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -1539,35 +1553,10 @@ vmw_kms_atomic_check_modeset(struct drm_device *dev, ...@@ -1539,35 +1553,10 @@ vmw_kms_atomic_check_modeset(struct drm_device *dev,
return drm_atomic_helper_check(dev, state); 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 = { static const struct drm_mode_config_funcs vmw_kms_funcs = {
.fb_create = vmw_kms_fb_create, .fb_create = vmw_kms_fb_create,
.atomic_check = vmw_kms_atomic_check_modeset, .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, 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, ...@@ -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, return vmw_kms_sou_do_surface_dirty(dev_priv, vfb, NULL, clips,
&surface->res, destX, destY, &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, ...@@ -1600,7 +1589,7 @@ int vmw_kms_present(struct vmw_private *dev_priv,
case vmw_du_screen_target: case vmw_du_screen_target:
ret = vmw_kms_stdu_surface_dirty(dev_priv, vfb, NULL, clips, ret = vmw_kms_stdu_surface_dirty(dev_priv, vfb, NULL, clips,
&surface->res, destX, destY, &surface->res, destX, destY,
num_clips, 1, NULL); num_clips, 1, NULL, NULL);
break; break;
case vmw_du_screen_object: case vmw_du_screen_object:
ret = vmw_kms_generic_present(dev_priv, file_priv, vfb, surface, 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, ...@@ -2328,10 +2317,16 @@ int vmw_kms_helper_dirty(struct vmw_private *dev_priv,
dirty->dev_priv = dev_priv; dirty->dev_priv = dev_priv;
list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) { /* If crtc is passed, no need to iterate over other display units */
if (crtc->primary->fb != &framebuffer->base) if (dirty->crtc) {
continue; units[num_units++] = vmw_crtc_to_du(dirty->crtc);
units[num_units++] = vmw_crtc_to_du(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++) { for (k = 0; k < num_units; k++) {
...@@ -2430,14 +2425,21 @@ int vmw_kms_helper_dirty(struct vmw_private *dev_priv, ...@@ -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, int vmw_kms_helper_buffer_prepare(struct vmw_private *dev_priv,
struct vmw_dma_buffer *buf, struct vmw_dma_buffer *buf,
bool interruptible, 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; struct ttm_buffer_object *bo = &buf->base;
int ret; int ret;
ttm_bo_reserve(bo, false, false, NULL); ttm_bo_reserve(bo, false, false, NULL);
ret = vmw_validate_single_buffer(dev_priv, bo, interruptible, if (for_cpu_blit)
validate_as_mob); ret = ttm_bo_validate(bo, &vmw_nonfixed_placement, &ctx);
else
ret = vmw_validate_single_buffer(dev_priv, bo, interruptible,
validate_as_mob);
if (ret) if (ret)
ttm_bo_unreserve(bo); ttm_bo_unreserve(bo);
...@@ -2549,7 +2551,8 @@ int vmw_kms_helper_resource_prepare(struct vmw_resource *res, ...@@ -2549,7 +2551,8 @@ int vmw_kms_helper_resource_prepare(struct vmw_resource *res,
if (res->backup) { if (res->backup) {
ret = vmw_kms_helper_buffer_prepare(res->dev_priv, res->backup, ret = vmw_kms_helper_buffer_prepare(res->dev_priv, res->backup,
interruptible, interruptible,
res->dev_priv->has_mob); res->dev_priv->has_mob,
false);
if (ret) if (ret)
goto out_unreserve; goto out_unreserve;
} }
...@@ -2845,3 +2848,51 @@ int vmw_kms_set_config(struct drm_mode_set *set, ...@@ -2845,3 +2848,51 @@ int vmw_kms_set_config(struct drm_mode_set *set,
return drm_atomic_helper_set_config(set, ctx); 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 @@ ...@@ -50,6 +50,7 @@
* @unit: The current display unit. Set up by the helper before a call to @clip. * @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 * @cmd: The allocated fifo space. Set up by the helper before the first @clip
* call. * call.
* @crtc: The crtc for which to build dirty commands.
* @num_hits: Number of clip rect commands for this display unit. * @num_hits: Number of clip rect commands for this display unit.
* Cleared by the helper before the first @clip call. Updated by the @clip * Cleared by the helper before the first @clip call. Updated by the @clip
* callback. * callback.
...@@ -71,6 +72,7 @@ struct vmw_kms_dirty { ...@@ -71,6 +72,7 @@ struct vmw_kms_dirty {
struct vmw_private *dev_priv; struct vmw_private *dev_priv;
struct vmw_display_unit *unit; struct vmw_display_unit *unit;
void *cmd; void *cmd;
struct drm_crtc *crtc;
u32 num_hits; u32 num_hits;
s32 fb_x; s32 fb_x;
s32 fb_y; s32 fb_y;
...@@ -175,7 +177,6 @@ struct vmw_plane_state { ...@@ -175,7 +177,6 @@ struct vmw_plane_state {
int pinned; int pinned;
/* For CPU Blit */ /* For CPU Blit */
struct ttm_bo_kmap_obj host_map;
unsigned int cpp; unsigned int cpp;
}; };
...@@ -287,7 +288,8 @@ int vmw_kms_helper_dirty(struct vmw_private *dev_priv, ...@@ -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, int vmw_kms_helper_buffer_prepare(struct vmw_private *dev_priv,
struct vmw_dma_buffer *buf, struct vmw_dma_buffer *buf,
bool interruptible, 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_revert(struct vmw_dma_buffer *buf);
void vmw_kms_helper_buffer_finish(struct vmw_private *dev_priv, void vmw_kms_helper_buffer_finish(struct vmw_private *dev_priv,
struct drm_file *file_priv, struct drm_file *file_priv,
...@@ -398,20 +400,23 @@ int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv, ...@@ -398,20 +400,23 @@ int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv,
s32 dest_x, s32 dest_x,
s32 dest_y, s32 dest_y,
unsigned num_clips, int inc, 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, int vmw_kms_sou_do_dmabuf_dirty(struct vmw_private *dev_priv,
struct vmw_framebuffer *framebuffer, struct vmw_framebuffer *framebuffer,
struct drm_clip_rect *clips, struct drm_clip_rect *clips,
struct drm_vmw_rect *vclips, struct drm_vmw_rect *vclips,
unsigned num_clips, int increment, unsigned num_clips, int increment,
bool interruptible, 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, int vmw_kms_sou_readback(struct vmw_private *dev_priv,
struct drm_file *file_priv, struct drm_file *file_priv,
struct vmw_framebuffer *vfb, struct vmw_framebuffer *vfb,
struct drm_vmw_fence_rep __user *user_fence_rep, struct drm_vmw_fence_rep __user *user_fence_rep,
struct drm_vmw_rect *vclips, 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 * Screen Target Display Unit functions - vmwgfx_stdu.c
...@@ -425,7 +430,8 @@ int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv, ...@@ -425,7 +430,8 @@ int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv,
s32 dest_x, s32 dest_x,
s32 dest_y, s32 dest_y,
unsigned num_clips, int inc, 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, int vmw_kms_stdu_dma(struct vmw_private *dev_priv,
struct drm_file *file_priv, struct drm_file *file_priv,
struct vmw_framebuffer *vfb, struct vmw_framebuffer *vfb,
...@@ -435,7 +441,8 @@ int vmw_kms_stdu_dma(struct vmw_private *dev_priv, ...@@ -435,7 +441,8 @@ int vmw_kms_stdu_dma(struct vmw_private *dev_priv,
uint32_t num_clips, uint32_t num_clips,
int increment, int increment,
bool to_surface, bool to_surface,
bool interruptible); bool interruptible,
struct drm_crtc *crtc);
int vmw_kms_set_config(struct drm_mode_set *set, int vmw_kms_set_config(struct drm_mode_set *set,
struct drm_modeset_acquire_ctx *ctx); struct drm_modeset_acquire_ctx *ctx);
......
...@@ -328,7 +328,7 @@ int vmw_host_get_guestinfo(const char *guest_info_param, ...@@ -328,7 +328,7 @@ int vmw_host_get_guestinfo(const char *guest_info_param,
{ {
struct rpc_channel channel; struct rpc_channel channel;
char *msg, *reply = NULL; char *msg, *reply = NULL;
size_t msg_len, reply_len = 0; size_t reply_len = 0;
int ret = 0; int ret = 0;
...@@ -338,15 +338,12 @@ int vmw_host_get_guestinfo(const char *guest_info_param, ...@@ -338,15 +338,12 @@ int vmw_host_get_guestinfo(const char *guest_info_param,
if (!guest_info_param || !length) if (!guest_info_param || !length)
return -EINVAL; return -EINVAL;
msg_len = strlen(guest_info_param) + strlen("info-get ") + 1; msg = kasprintf(GFP_KERNEL, "info-get %s", guest_info_param);
msg = kzalloc(msg_len, GFP_KERNEL);
if (!msg) { if (!msg) {
DRM_ERROR("Cannot allocate memory to get %s", guest_info_param); DRM_ERROR("Cannot allocate memory to get %s", guest_info_param);
return -ENOMEM; return -ENOMEM;
} }
sprintf(msg, "info-get %s", guest_info_param);
if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM) || if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM) ||
vmw_send_msg(&channel, msg) || vmw_send_msg(&channel, msg) ||
vmw_recv_msg(&channel, (void *) &reply, &reply_len) || vmw_recv_msg(&channel, (void *) &reply, &reply_len) ||
...@@ -388,7 +385,6 @@ int vmw_host_log(const char *log) ...@@ -388,7 +385,6 @@ int vmw_host_log(const char *log)
{ {
struct rpc_channel channel; struct rpc_channel channel;
char *msg; char *msg;
int msg_len;
int ret = 0; int ret = 0;
...@@ -398,15 +394,12 @@ int vmw_host_log(const char *log) ...@@ -398,15 +394,12 @@ int vmw_host_log(const char *log)
if (!log) if (!log)
return ret; return ret;
msg_len = strlen(log) + strlen("log ") + 1; msg = kasprintf(GFP_KERNEL, "log %s", log);
msg = kzalloc(msg_len, GFP_KERNEL);
if (!msg) { if (!msg) {
DRM_ERROR("Cannot allocate memory for log message\n"); DRM_ERROR("Cannot allocate memory for log message\n");
return -ENOMEM; return -ENOMEM;
} }
sprintf(msg, "log %s", log);
if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM) || if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM) ||
vmw_send_msg(&channel, msg) || vmw_send_msg(&channel, msg) ||
vmw_close_channel(&channel)) { vmw_close_channel(&channel)) {
......
...@@ -354,6 +354,7 @@ void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo) ...@@ -354,6 +354,7 @@ void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo)
{ {
struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo); struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
vmw_dma_buffer_unmap(vmw_bo);
kfree(vmw_bo); kfree(vmw_bo);
} }
...@@ -361,6 +362,7 @@ static void vmw_user_dmabuf_destroy(struct ttm_buffer_object *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); 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); ttm_prime_object_kfree(vmw_user_bo, prime);
} }
...@@ -1239,6 +1241,12 @@ void vmw_resource_move_notify(struct ttm_buffer_object *bo, ...@@ -1239,6 +1241,12 @@ void vmw_resource_move_notify(struct ttm_buffer_object *bo,
dma_buf = container_of(bo, struct vmw_dma_buffer, base); 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) { if (mem->mem_type != VMW_PL_MOB) {
struct vmw_resource *res, *n; struct vmw_resource *res, *n;
struct ttm_validate_buffer val_buf; struct ttm_validate_buffer val_buf;
...@@ -1262,6 +1270,21 @@ void vmw_resource_move_notify(struct ttm_buffer_object *bo, ...@@ -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 * vmw_query_readback_all - Read back cached query states
......
...@@ -316,69 +316,21 @@ static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, ...@@ -316,69 +316,21 @@ static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc,
struct drm_modeset_acquire_ctx *ctx) struct drm_modeset_acquire_ctx *ctx)
{ {
struct vmw_private *dev_priv = vmw_priv(crtc->dev); 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; int ret;
if (!vmw_kms_crtc_flippable(dev_priv, crtc)) if (!vmw_kms_crtc_flippable(dev_priv, crtc))
return -EINVAL; return -EINVAL;
flags &= ~DRM_MODE_PAGE_FLIP_ASYNC; ret = drm_atomic_helper_page_flip(crtc, new_fb, event, flags, ctx);
ret = drm_atomic_helper_page_flip(crtc, new_fb, NULL, flags, ctx);
if (ret) { if (ret) {
DRM_ERROR("Page flip error %d.\n", ret); DRM_ERROR("Page flip error %d.\n", ret);
return 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) if (vmw_crtc_to_du(crtc)->is_implicit)
vmw_kms_update_implicit_fb(dev_priv, crtc); vmw_kms_update_implicit_fb(dev_priv, crtc);
return ret; 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 = { 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, ...@@ -453,7 +405,11 @@ vmw_sou_primary_plane_cleanup_fb(struct drm_plane *plane,
struct drm_plane_state *old_state) struct drm_plane_state *old_state)
{ {
struct vmw_plane_state *vps = vmw_plane_state_to_vps(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); vmw_dmabuf_unreference(&vps->dmabuf);
vps->dmabuf_size = 0; vps->dmabuf_size = 0;
...@@ -491,10 +447,17 @@ vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane, ...@@ -491,10 +447,17 @@ vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane,
} }
size = new_state->crtc_w * new_state->crtc_h * 4; size = new_state->crtc_w * new_state->crtc_h * 4;
dev_priv = vmw_priv(crtc->dev);
if (vps->dmabuf) { if (vps->dmabuf) {
if (vps->dmabuf_size == size) if (vps->dmabuf_size == size) {
return 0; /*
* 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); vmw_dmabuf_unreference(&vps->dmabuf);
vps->dmabuf_size = 0; vps->dmabuf_size = 0;
...@@ -504,7 +467,6 @@ vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane, ...@@ -504,7 +467,6 @@ vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane,
if (!vps->dmabuf) if (!vps->dmabuf)
return -ENOMEM; return -ENOMEM;
dev_priv = vmw_priv(crtc->dev);
vmw_svga_enable(dev_priv); vmw_svga_enable(dev_priv);
/* After we have alloced the backing store might not be able to /* 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, ...@@ -515,13 +477,16 @@ vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane,
&vmw_vram_ne_placement, &vmw_vram_ne_placement,
false, &vmw_dmabuf_bo_free); false, &vmw_dmabuf_bo_free);
vmw_overlay_resume_all(dev_priv); vmw_overlay_resume_all(dev_priv);
if (ret) {
if (ret != 0)
vps->dmabuf = NULL; /* vmw_dmabuf_init frees on error */ vps->dmabuf = NULL; /* vmw_dmabuf_init frees on error */
else return ret;
vps->dmabuf_size = size; }
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, ...@@ -530,9 +495,71 @@ vmw_sou_primary_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_state) struct drm_plane_state *old_state)
{ {
struct drm_crtc *crtc = plane->state->crtc; 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; 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) ...@@ -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 * @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 * struct vmw_fence_obj. The returned fence pointer may be NULL in which
* case the device has already synchronized. * 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 * Returns 0 on success, negative error code on failure. -ERESTARTSYS if
* interrupted. * interrupted.
...@@ -904,7 +932,8 @@ int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv, ...@@ -904,7 +932,8 @@ int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv,
s32 dest_x, s32 dest_x,
s32 dest_y, s32 dest_y,
unsigned num_clips, int inc, 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 = struct vmw_framebuffer_surface *vfbs =
container_of(framebuffer, typeof(*vfbs), base); container_of(framebuffer, typeof(*vfbs), base);
...@@ -923,6 +952,7 @@ int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv, ...@@ -923,6 +952,7 @@ int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv,
sdirty.base.dev_priv = dev_priv; sdirty.base.dev_priv = dev_priv;
sdirty.base.fifo_reserve_size = sizeof(struct vmw_kms_sou_dirty_cmd) + sdirty.base.fifo_reserve_size = sizeof(struct vmw_kms_sou_dirty_cmd) +
sizeof(SVGASignedRect) * num_clips; sizeof(SVGASignedRect) * num_clips;
sdirty.base.crtc = crtc;
sdirty.sid = srf->id; sdirty.sid = srf->id;
sdirty.left = sdirty.top = S32_MAX; sdirty.left = sdirty.top = S32_MAX;
...@@ -994,6 +1024,7 @@ static void vmw_sou_dmabuf_clip(struct vmw_kms_dirty *dirty) ...@@ -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 * @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 * struct vmw_fence_obj. The returned fence pointer may be NULL in which
* case the device has already synchronized. * 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 * Returns 0 on success, negative error code on failure. -ERESTARTSYS if
* interrupted. * interrupted.
...@@ -1004,7 +1035,8 @@ int vmw_kms_sou_do_dmabuf_dirty(struct vmw_private *dev_priv, ...@@ -1004,7 +1035,8 @@ int vmw_kms_sou_do_dmabuf_dirty(struct vmw_private *dev_priv,
struct drm_vmw_rect *vclips, struct drm_vmw_rect *vclips,
unsigned num_clips, int increment, unsigned num_clips, int increment,
bool interruptible, bool interruptible,
struct vmw_fence_obj **out_fence) struct vmw_fence_obj **out_fence,
struct drm_crtc *crtc)
{ {
struct vmw_dma_buffer *buf = struct vmw_dma_buffer *buf =
container_of(framebuffer, struct vmw_framebuffer_dmabuf, container_of(framebuffer, struct vmw_framebuffer_dmabuf,
...@@ -1013,7 +1045,7 @@ int vmw_kms_sou_do_dmabuf_dirty(struct vmw_private *dev_priv, ...@@ -1013,7 +1045,7 @@ int vmw_kms_sou_do_dmabuf_dirty(struct vmw_private *dev_priv,
int ret; int ret;
ret = vmw_kms_helper_buffer_prepare(dev_priv, buf, interruptible, ret = vmw_kms_helper_buffer_prepare(dev_priv, buf, interruptible,
false); false, false);
if (ret) if (ret)
return ret; return ret;
...@@ -1021,6 +1053,7 @@ int vmw_kms_sou_do_dmabuf_dirty(struct vmw_private *dev_priv, ...@@ -1021,6 +1053,7 @@ int vmw_kms_sou_do_dmabuf_dirty(struct vmw_private *dev_priv,
if (unlikely(ret != 0)) if (unlikely(ret != 0))
goto out_revert; goto out_revert;
dirty.crtc = crtc;
dirty.fifo_commit = vmw_sou_dmabuf_fifo_commit; dirty.fifo_commit = vmw_sou_dmabuf_fifo_commit;
dirty.clip = vmw_sou_dmabuf_clip; dirty.clip = vmw_sou_dmabuf_clip;
dirty.fifo_reserve_size = sizeof(struct vmw_kms_sou_dmabuf_blit) * 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) ...@@ -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. * Must be set to non-NULL if @file_priv is non-NULL.
* @vclips: Array of clip rects. * @vclips: Array of clip rects.
* @num_clips: Number of clip rects in @vclips. * @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 * Returns 0 on success, negative error code on failure. -ERESTARTSYS if
* interrupted. * interrupted.
...@@ -1101,14 +1135,16 @@ int vmw_kms_sou_readback(struct vmw_private *dev_priv, ...@@ -1101,14 +1135,16 @@ int vmw_kms_sou_readback(struct vmw_private *dev_priv,
struct vmw_framebuffer *vfb, struct vmw_framebuffer *vfb,
struct drm_vmw_fence_rep __user *user_fence_rep, struct drm_vmw_fence_rep __user *user_fence_rep,
struct drm_vmw_rect *vclips, struct drm_vmw_rect *vclips,
uint32_t num_clips) uint32_t num_clips,
struct drm_crtc *crtc)
{ {
struct vmw_dma_buffer *buf = struct vmw_dma_buffer *buf =
container_of(vfb, struct vmw_framebuffer_dmabuf, base)->buffer; container_of(vfb, struct vmw_framebuffer_dmabuf, base)->buffer;
struct vmw_kms_dirty dirty; struct vmw_kms_dirty dirty;
int ret; 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) if (ret)
return ret; return ret;
...@@ -1116,6 +1152,7 @@ int vmw_kms_sou_readback(struct vmw_private *dev_priv, ...@@ -1116,6 +1152,7 @@ int vmw_kms_sou_readback(struct vmw_private *dev_priv,
if (unlikely(ret != 0)) if (unlikely(ret != 0))
goto out_revert; goto out_revert;
dirty.crtc = crtc;
dirty.fifo_commit = vmw_sou_readback_fifo_commit; dirty.fifo_commit = vmw_sou_readback_fifo_commit;
dirty.clip = vmw_sou_readback_clip; dirty.clip = vmw_sou_readback_clip;
dirty.fifo_reserve_size = sizeof(struct vmw_kms_sou_readback_blit) * 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) ...@@ -345,7 +345,6 @@ static void vmw_hw_surface_destroy(struct vmw_resource *res)
dev_priv->used_memory_size -= res->backup_size; dev_priv->used_memory_size -= res->backup_size;
mutex_unlock(&dev_priv->cmdbuf_mutex); 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) ...@@ -407,6 +406,8 @@ static int vmw_legacy_srf_create(struct vmw_resource *res)
vmw_surface_define_encode(srf, cmd); vmw_surface_define_encode(srf, cmd);
vmw_fifo_commit(dev_priv, submit_size); vmw_fifo_commit(dev_priv, submit_size);
vmw_fifo_resource_inc(dev_priv);
/* /*
* Surface memory usage accounting. * Surface memory usage accounting.
*/ */
...@@ -558,6 +559,7 @@ static int vmw_legacy_srf_destroy(struct vmw_resource *res) ...@@ -558,6 +559,7 @@ static int vmw_legacy_srf_destroy(struct vmw_resource *res)
*/ */
vmw_resource_release_id(res); vmw_resource_release_id(res);
vmw_fifo_resource_dec(dev_priv);
return 0; return 0;
} }
...@@ -579,15 +581,11 @@ static int vmw_surface_init(struct vmw_private *dev_priv, ...@@ -579,15 +581,11 @@ static int vmw_surface_init(struct vmw_private *dev_priv,
struct vmw_resource *res = &srf->res; struct vmw_resource *res = &srf->res;
BUG_ON(!res_free); 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, ret = vmw_resource_init(dev_priv, res, true, res_free,
(dev_priv->has_mob) ? &vmw_gb_surface_func : (dev_priv->has_mob) ? &vmw_gb_surface_func :
&vmw_legacy_surface_func); &vmw_legacy_surface_func);
if (unlikely(ret != 0)) { if (unlikely(ret != 0)) {
if (!dev_priv->has_mob)
vmw_fifo_resource_dec(dev_priv);
res_free(res); res_free(res);
return ret; return ret;
} }
......
...@@ -709,6 +709,10 @@ int ttm_fbdev_mmap(struct vm_area_struct *vma, struct ttm_buffer_object *bo); ...@@ -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, int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
struct ttm_bo_device *bdev); 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 * 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