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

Pull drm updates from Dave Airlie:
 "I Was Almost Tempted To Capitalise Every Word, but then I decided I
  couldn't read it myself!

  I've also got one pull request for the sti driver outstanding.  It
  relied on a commit in Greg's tree and I didn't find out in time, that
  commit is in your tree now so I might send that along once this is
  merged.

  I also had the accidental misfortune to have access to a Skylake on my
  desk for a few days, and I've had to encourage Intel to try harder,
  which seems to be happening now.

  Here is the main drm-next pull request for 4.4.

  Highlights:

  New driver:
        vc4 driver for the Rasberry Pi VPU.
        (From Eric Anholt at Broadcom.)

  Core:
        Atomic fbdev support
        Atomic helpers for runtime pm
        dp/aux i2c STATUS_UPDATE handling
        struct_mutex usage cleanups.
        Generic of probing support.

  Documentation:
        Kerneldoc for VGA switcheroo code.
        Rename to gpu instead of drm to reflect scope.

  i915:
        Skylake GuC firmware fixes
        HPD A support
        VBT backlight fallbacks
        Fastboot by default for some systems
        FBC work
        BXT/SKL workarounds
        Skylake deeper sleep state fixes

  amdgpu:
        Enable GPU scheduler by default
        New atombios opcodes
        GPUVM debugging options
        Stoney support.
        Fencing cleanups.

  radeon:
        More efficient CS checking

  nouveau:
        gk20a instance memory handling improvements.
        Improved PGOB detection and GK107 support
        Kepler GDDR5 PLL statbility improvement
        G8x/GT2xx reclock improvements
        new userspace API compatiblity fixes.

  virtio-gpu:
        Add 3D support - qemu 2.5 has it merged for it's gtk backend.

  msm:
        Initial msm88896 (snapdragon 8200)

  exynos:
        HDMI cleanups
        Enable mixer driver byt default
        Add DECON-TV support

  vmwgfx:
        Move to using memremap + fixes.

  rcar-du:
        Add support for R8A7793/4 DU

  armada:
        Remove support for non-component mode
        Improved plane handling
        Power savings while in DPMS off.

  tda998x:
        Remove unused slave encoder support
        Use more HDMI helpers
        Fix EDID read handling

  dwhdmi:
        Interlace video mode support for ipu-v3/dw_hdmi
        Hotplug state fixes
        Audio driver integration

  imx:
        More color formats support.

  tegra:
        Minor fixes/improvements"

[ Merge fixup: remove unused variable 'dev' that had all uses removed in
  commit 4e270f0880: "drm/gem: Drop struct_mutex requirement from
  drm_gem_mmap_obj" ]

* 'drm-next' of git://people.freedesktop.org/~airlied/linux: (764 commits)
  drm/vmwgfx: Relax irq locking somewhat
  drm/vmwgfx: Properly flush cursor updates and page-flips
  drm/i915/skl: disable display side power well support for now
  drm/i915: Extend DSL readout fix to BDW and SKL.
  drm/i915: Do graphics device reset under forcewake
  drm/i915: Skip fence installation for objects with rotated views (v4)
  vga_switcheroo: Drop client power state VGA_SWITCHEROO_INIT
  drm/amdgpu: group together common fence implementation
  drm/amdgpu: remove AMDGPU_FENCE_OWNER_MOVE
  drm/amdgpu: remove now unused fence functions
  drm/amdgpu: fix fence fallback check
  drm/amdgpu: fix stoping the scheduler timeout
  drm/amdgpu: cleanup on error in amdgpu_cs_ioctl()
  drm/i915: Fix locking around GuC firmware load
  drm/amdgpu: update Fiji's Golden setting
  drm/amdgpu: update Fiji's rev id
  drm/amdgpu: extract common code in vi_common_early_init
  drm/amd/scheduler: don't oops on failure to load
  drm/amdgpu: don't oops on failure to load (v2)
  drm/amdgpu: don't VT switch on suspend
  ...
This commit is contained in:
Linus Torvalds
2015-11-10 09:33:06 -08:00
468 changed files with 53369 additions and 9828 deletions

View File

@@ -6,6 +6,7 @@ ccflags-y := -Iinclude/drm
virtio-gpu-y := virtgpu_drv.o virtgpu_kms.o virtgpu_drm_bus.o virtgpu_gem.o \
virtgpu_fb.o virtgpu_display.o virtgpu_vq.o virtgpu_ttm.o \
virtgpu_fence.o virtgpu_object.o virtgpu_debugfs.o virtgpu_plane.o
virtgpu_fence.o virtgpu_object.o virtgpu_debugfs.o virtgpu_plane.o \
virtgpu_ioctl.o virtgpu_prime.o
obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio-gpu.o

View File

@@ -90,6 +90,14 @@ static int virtio_gpu_crtc_cursor_set(struct drm_crtc *crtc,
cpu_to_le32(64),
cpu_to_le32(64),
0, 0, &fence);
ret = virtio_gpu_object_reserve(qobj, false);
if (!ret) {
reservation_object_add_excl_fence(qobj->tbo.resv,
&fence->f);
fence_put(&fence->f);
virtio_gpu_object_unreserve(qobj);
virtio_gpu_object_wait(qobj, false);
}
output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR);
output->cursor.resource_id = cpu_to_le32(qobj->hw_res_handle);
@@ -117,6 +125,51 @@ static int virtio_gpu_crtc_cursor_move(struct drm_crtc *crtc,
return 0;
}
static int virtio_gpu_page_flip(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event,
uint32_t flags)
{
struct virtio_gpu_device *vgdev = crtc->dev->dev_private;
struct virtio_gpu_output *output =
container_of(crtc, struct virtio_gpu_output, crtc);
struct drm_plane *plane = crtc->primary;
struct virtio_gpu_framebuffer *vgfb;
struct virtio_gpu_object *bo;
unsigned long irqflags;
uint32_t handle;
plane->fb = fb;
vgfb = to_virtio_gpu_framebuffer(plane->fb);
bo = gem_to_virtio_gpu_obj(vgfb->obj);
handle = bo->hw_res_handle;
DRM_DEBUG("handle 0x%x%s, crtc %dx%d\n", handle,
bo->dumb ? ", dumb" : "",
crtc->mode.hdisplay, crtc->mode.vdisplay);
if (bo->dumb) {
virtio_gpu_cmd_transfer_to_host_2d
(vgdev, handle, 0,
cpu_to_le32(crtc->mode.hdisplay),
cpu_to_le32(crtc->mode.vdisplay),
0, 0, NULL);
}
virtio_gpu_cmd_set_scanout(vgdev, output->index, handle,
crtc->mode.hdisplay,
crtc->mode.vdisplay, 0, 0);
virtio_gpu_cmd_resource_flush(vgdev, handle, 0, 0,
crtc->mode.hdisplay,
crtc->mode.vdisplay);
if (event) {
spin_lock_irqsave(&crtc->dev->event_lock, irqflags);
drm_send_vblank_event(crtc->dev, -1, event);
spin_unlock_irqrestore(&crtc->dev->event_lock, irqflags);
}
return 0;
}
static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = {
.cursor_set2 = virtio_gpu_crtc_cursor_set,
.cursor_move = virtio_gpu_crtc_cursor_move,
@@ -124,9 +177,7 @@ static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.destroy = drm_crtc_cleanup,
#if 0 /* not (yet) working without vblank support according to docs */
.page_flip = drm_atomic_helper_page_flip,
#endif
.page_flip = virtio_gpu_page_flip,
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,

View File

@@ -73,6 +73,14 @@ static struct virtio_device_id id_table[] = {
};
static unsigned int features[] = {
#ifdef __LITTLE_ENDIAN
/*
* Gallium command stream send by virgl is native endian.
* Because of that we only support little endian guests on
* little endian hosts.
*/
VIRTIO_GPU_F_VIRGL,
#endif
};
static struct virtio_driver virtio_gpu_driver = {
.feature_table = features,
@@ -110,10 +118,12 @@ static const struct file_operations virtio_gpu_driver_fops = {
static struct drm_driver driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM,
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_RENDER,
.set_busid = drm_virtio_set_busid,
.load = virtio_gpu_driver_load,
.unload = virtio_gpu_driver_unload,
.open = virtio_gpu_driver_open,
.postclose = virtio_gpu_driver_postclose,
.dumb_create = virtio_gpu_mode_dumb_create,
.dumb_map_offset = virtio_gpu_mode_dumb_mmap,
@@ -123,10 +133,26 @@ static struct drm_driver driver = {
.debugfs_init = virtio_gpu_debugfs_init,
.debugfs_cleanup = virtio_gpu_debugfs_takedown,
#endif
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_export = drm_gem_prime_export,
.gem_prime_import = drm_gem_prime_import,
.gem_prime_pin = virtgpu_gem_prime_pin,
.gem_prime_unpin = virtgpu_gem_prime_unpin,
.gem_prime_get_sg_table = virtgpu_gem_prime_get_sg_table,
.gem_prime_import_sg_table = virtgpu_gem_prime_import_sg_table,
.gem_prime_vmap = virtgpu_gem_prime_vmap,
.gem_prime_vunmap = virtgpu_gem_prime_vunmap,
.gem_prime_mmap = virtgpu_gem_prime_mmap,
.gem_free_object = virtio_gpu_gem_free_object,
.gem_open_object = virtio_gpu_gem_object_open,
.gem_close_object = virtio_gpu_gem_object_close,
.fops = &virtio_gpu_driver_fops,
.ioctls = virtio_gpu_ioctls,
.num_ioctls = DRM_VIRTIO_NUM_IOCTLS,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,

View File

@@ -146,6 +146,21 @@ struct virtio_gpu_queue {
struct work_struct dequeue_work;
};
struct virtio_gpu_drv_capset {
uint32_t id;
uint32_t max_version;
uint32_t max_size;
};
struct virtio_gpu_drv_cap_cache {
struct list_head head;
void *caps_cache;
uint32_t id;
uint32_t version;
uint32_t size;
atomic_t is_valid;
};
struct virtio_gpu_device {
struct device *dev;
struct drm_device *ddev;
@@ -179,7 +194,13 @@ struct virtio_gpu_device {
struct idr ctx_id_idr;
spinlock_t ctx_id_idr_lock;
bool has_virgl_3d;
struct work_struct config_changed_work;
struct virtio_gpu_drv_capset *capsets;
uint32_t num_capsets;
struct list_head cap_cache;
};
struct virtio_gpu_fpriv {
@@ -193,6 +214,8 @@ extern struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS];
/* virtio_kms.c */
int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags);
int virtio_gpu_driver_unload(struct drm_device *dev);
int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file);
void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file);
/* virtio_gem.c */
void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj);
@@ -203,6 +226,10 @@ int virtio_gpu_gem_create(struct drm_file *file,
uint64_t size,
struct drm_gem_object **obj_p,
uint32_t *handle_p);
int virtio_gpu_gem_object_open(struct drm_gem_object *obj,
struct drm_file *file);
void virtio_gpu_gem_object_close(struct drm_gem_object *obj,
struct drm_file *file);
struct virtio_gpu_object *virtio_gpu_alloc_object(struct drm_device *dev,
size_t size, bool kernel,
bool pinned);
@@ -260,10 +287,43 @@ void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev,
int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev);
void virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev,
uint32_t resource_id);
int virtio_gpu_cmd_get_capset_info(struct virtio_gpu_device *vgdev, int idx);
int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev,
int idx, int version,
struct virtio_gpu_drv_cap_cache **cache_p);
void virtio_gpu_cmd_context_create(struct virtio_gpu_device *vgdev, uint32_t id,
uint32_t nlen, const char *name);
void virtio_gpu_cmd_context_destroy(struct virtio_gpu_device *vgdev,
uint32_t id);
void virtio_gpu_cmd_context_attach_resource(struct virtio_gpu_device *vgdev,
uint32_t ctx_id,
uint32_t resource_id);
void virtio_gpu_cmd_context_detach_resource(struct virtio_gpu_device *vgdev,
uint32_t ctx_id,
uint32_t resource_id);
void virtio_gpu_cmd_submit(struct virtio_gpu_device *vgdev,
void *data, uint32_t data_size,
uint32_t ctx_id, struct virtio_gpu_fence **fence);
void virtio_gpu_cmd_transfer_from_host_3d(struct virtio_gpu_device *vgdev,
uint32_t resource_id, uint32_t ctx_id,
uint64_t offset, uint32_t level,
struct virtio_gpu_box *box,
struct virtio_gpu_fence **fence);
void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev,
uint32_t resource_id, uint32_t ctx_id,
uint64_t offset, uint32_t level,
struct virtio_gpu_box *box,
struct virtio_gpu_fence **fence);
void
virtio_gpu_cmd_resource_create_3d(struct virtio_gpu_device *vgdev,
struct virtio_gpu_resource_create_3d *rc_3d,
struct virtio_gpu_fence **fence);
void virtio_gpu_ctrl_ack(struct virtqueue *vq);
void virtio_gpu_cursor_ack(struct virtqueue *vq);
void virtio_gpu_fence_ack(struct virtqueue *vq);
void virtio_gpu_dequeue_ctrl_func(struct work_struct *work);
void virtio_gpu_dequeue_cursor_func(struct work_struct *work);
void virtio_gpu_dequeue_fence_func(struct work_struct *work);
/* virtio_gpu_display.c */
int virtio_gpu_framebuffer_init(struct drm_device *dev,
@@ -299,6 +359,18 @@ int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev,
void virtio_gpu_object_free_sg_table(struct virtio_gpu_object *bo);
int virtio_gpu_object_wait(struct virtio_gpu_object *bo, bool no_wait);
/* virtgpu_prime.c */
int virtgpu_gem_prime_pin(struct drm_gem_object *obj);
void virtgpu_gem_prime_unpin(struct drm_gem_object *obj);
struct sg_table *virtgpu_gem_prime_get_sg_table(struct drm_gem_object *obj);
struct drm_gem_object *virtgpu_gem_prime_import_sg_table(
struct drm_device *dev, struct dma_buf_attachment *attach,
struct sg_table *sgt);
void *virtgpu_gem_prime_vmap(struct drm_gem_object *obj);
void virtgpu_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
int virtgpu_gem_prime_mmap(struct drm_gem_object *obj,
struct vm_area_struct *vma);
static inline struct virtio_gpu_object*
virtio_gpu_object_ref(struct virtio_gpu_object *bo)
{

View File

@@ -81,7 +81,7 @@ int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev,
struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv;
unsigned long irq_flags;
*fence = kmalloc(sizeof(struct virtio_gpu_fence), GFP_KERNEL);
*fence = kmalloc(sizeof(struct virtio_gpu_fence), GFP_ATOMIC);
if ((*fence) == NULL)
return -ENOMEM;

View File

@@ -138,3 +138,44 @@ int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv,
drm_gem_object_unreference_unlocked(gobj);
return 0;
}
int virtio_gpu_gem_object_open(struct drm_gem_object *obj,
struct drm_file *file)
{
struct virtio_gpu_device *vgdev = obj->dev->dev_private;
struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
struct virtio_gpu_object *qobj = gem_to_virtio_gpu_obj(obj);
int r;
if (!vgdev->has_virgl_3d)
return 0;
r = virtio_gpu_object_reserve(qobj, false);
if (r)
return r;
virtio_gpu_cmd_context_attach_resource(vgdev, vfpriv->ctx_id,
qobj->hw_res_handle);
virtio_gpu_object_unreserve(qobj);
return 0;
}
void virtio_gpu_gem_object_close(struct drm_gem_object *obj,
struct drm_file *file)
{
struct virtio_gpu_device *vgdev = obj->dev->dev_private;
struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
struct virtio_gpu_object *qobj = gem_to_virtio_gpu_obj(obj);
int r;
if (!vgdev->has_virgl_3d)
return;
r = virtio_gpu_object_reserve(qobj, false);
if (r)
return;
virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id,
qobj->hw_res_handle);
virtio_gpu_object_unreserve(qobj);
}

View File

@@ -0,0 +1,573 @@
/*
* Copyright (C) 2015 Red Hat, Inc.
* All Rights Reserved.
*
* Authors:
* Dave Airlie
* Alon Levy
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <drm/drmP.h>
#include "virtgpu_drv.h"
#include <drm/virtgpu_drm.h>
#include "ttm/ttm_execbuf_util.h"
static void convert_to_hw_box(struct virtio_gpu_box *dst,
const struct drm_virtgpu_3d_box *src)
{
dst->x = cpu_to_le32(src->x);
dst->y = cpu_to_le32(src->y);
dst->z = cpu_to_le32(src->z);
dst->w = cpu_to_le32(src->w);
dst->h = cpu_to_le32(src->h);
dst->d = cpu_to_le32(src->d);
}
static int virtio_gpu_map_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct virtio_gpu_device *vgdev = dev->dev_private;
struct drm_virtgpu_map *virtio_gpu_map = data;
return virtio_gpu_mode_dumb_mmap(file_priv, vgdev->ddev,
virtio_gpu_map->handle,
&virtio_gpu_map->offset);
}
static int virtio_gpu_object_list_validate(struct ww_acquire_ctx *ticket,
struct list_head *head)
{
struct ttm_validate_buffer *buf;
struct ttm_buffer_object *bo;
struct virtio_gpu_object *qobj;
int ret;
ret = ttm_eu_reserve_buffers(ticket, head, true, NULL);
if (ret != 0)
return ret;
list_for_each_entry(buf, head, head) {
bo = buf->bo;
qobj = container_of(bo, struct virtio_gpu_object, tbo);
ret = ttm_bo_validate(bo, &qobj->placement, false, false);
if (ret) {
ttm_eu_backoff_reservation(ticket, head);
return ret;
}
}
return 0;
}
static void virtio_gpu_unref_list(struct list_head *head)
{
struct ttm_validate_buffer *buf;
struct ttm_buffer_object *bo;
struct virtio_gpu_object *qobj;
list_for_each_entry(buf, head, head) {
bo = buf->bo;
qobj = container_of(bo, struct virtio_gpu_object, tbo);
drm_gem_object_unreference_unlocked(&qobj->gem_base);
}
}
static int virtio_gpu_execbuffer(struct drm_device *dev,
struct drm_virtgpu_execbuffer *exbuf,
struct drm_file *drm_file)
{
struct virtio_gpu_device *vgdev = dev->dev_private;
struct virtio_gpu_fpriv *vfpriv = drm_file->driver_priv;
struct drm_gem_object *gobj;
struct virtio_gpu_fence *fence;
struct virtio_gpu_object *qobj;
int ret;
uint32_t *bo_handles = NULL;
void __user *user_bo_handles = NULL;
struct list_head validate_list;
struct ttm_validate_buffer *buflist = NULL;
int i;
struct ww_acquire_ctx ticket;
void *buf;
if (vgdev->has_virgl_3d == false)
return -ENOSYS;
INIT_LIST_HEAD(&validate_list);
if (exbuf->num_bo_handles) {
bo_handles = drm_malloc_ab(exbuf->num_bo_handles,
sizeof(uint32_t));
buflist = drm_calloc_large(exbuf->num_bo_handles,
sizeof(struct ttm_validate_buffer));
if (!bo_handles || !buflist) {
drm_free_large(bo_handles);
drm_free_large(buflist);
return -ENOMEM;
}
user_bo_handles = (void __user *)(uintptr_t)exbuf->bo_handles;
if (copy_from_user(bo_handles, user_bo_handles,
exbuf->num_bo_handles * sizeof(uint32_t))) {
ret = -EFAULT;
drm_free_large(bo_handles);
drm_free_large(buflist);
return ret;
}
for (i = 0; i < exbuf->num_bo_handles; i++) {
gobj = drm_gem_object_lookup(dev,
drm_file, bo_handles[i]);
if (!gobj) {
drm_free_large(bo_handles);
drm_free_large(buflist);
return -ENOENT;
}
qobj = gem_to_virtio_gpu_obj(gobj);
buflist[i].bo = &qobj->tbo;
list_add(&buflist[i].head, &validate_list);
}
drm_free_large(bo_handles);
}
ret = virtio_gpu_object_list_validate(&ticket, &validate_list);
if (ret)
goto out_free;
buf = kmalloc(exbuf->size, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto out_unresv;
}
if (copy_from_user(buf, (void __user *)(uintptr_t)exbuf->command,
exbuf->size)) {
kfree(buf);
ret = -EFAULT;
goto out_unresv;
}
virtio_gpu_cmd_submit(vgdev, buf, exbuf->size,
vfpriv->ctx_id, &fence);
ttm_eu_fence_buffer_objects(&ticket, &validate_list, &fence->f);
/* fence the command bo */
virtio_gpu_unref_list(&validate_list);
drm_free_large(buflist);
fence_put(&fence->f);
return 0;
out_unresv:
ttm_eu_backoff_reservation(&ticket, &validate_list);
out_free:
virtio_gpu_unref_list(&validate_list);
drm_free_large(buflist);
return ret;
}
/*
* Usage of execbuffer:
* Relocations need to take into account the full VIRTIO_GPUDrawable size.
* However, the command as passed from user space must *not* contain the initial
* VIRTIO_GPUReleaseInfo struct (first XXX bytes)
*/
static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_virtgpu_execbuffer *execbuffer = data;
return virtio_gpu_execbuffer(dev, execbuffer, file_priv);
}
static int virtio_gpu_getparam_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct virtio_gpu_device *vgdev = dev->dev_private;
struct drm_virtgpu_getparam *param = data;
int value;
switch (param->param) {
case VIRTGPU_PARAM_3D_FEATURES:
value = vgdev->has_virgl_3d == true ? 1 : 0;
break;
default:
return -EINVAL;
}
if (copy_to_user((void __user *)(unsigned long)param->value,
&value, sizeof(int))) {
return -EFAULT;
}
return 0;
}
static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct virtio_gpu_device *vgdev = dev->dev_private;
struct drm_virtgpu_resource_create *rc = data;
int ret;
uint32_t res_id;
struct virtio_gpu_object *qobj;
struct drm_gem_object *obj;
uint32_t handle = 0;
uint32_t size;
struct list_head validate_list;
struct ttm_validate_buffer mainbuf;
struct virtio_gpu_fence *fence = NULL;
struct ww_acquire_ctx ticket;
struct virtio_gpu_resource_create_3d rc_3d;
if (vgdev->has_virgl_3d == false) {
if (rc->depth > 1)
return -EINVAL;
if (rc->nr_samples > 1)
return -EINVAL;
if (rc->last_level > 1)
return -EINVAL;
if (rc->target != 2)
return -EINVAL;
if (rc->array_size > 1)
return -EINVAL;
}
INIT_LIST_HEAD(&validate_list);
memset(&mainbuf, 0, sizeof(struct ttm_validate_buffer));
virtio_gpu_resource_id_get(vgdev, &res_id);
size = rc->size;
/* allocate a single page size object */
if (size == 0)
size = PAGE_SIZE;
qobj = virtio_gpu_alloc_object(dev, size, false, false);
if (IS_ERR(qobj)) {
ret = PTR_ERR(qobj);
goto fail_id;
}
obj = &qobj->gem_base;
if (!vgdev->has_virgl_3d) {
virtio_gpu_cmd_create_resource(vgdev, res_id, rc->format,
rc->width, rc->height);
ret = virtio_gpu_object_attach(vgdev, qobj, res_id, NULL);
} else {
/* use a gem reference since unref list undoes them */
drm_gem_object_reference(&qobj->gem_base);
mainbuf.bo = &qobj->tbo;
list_add(&mainbuf.head, &validate_list);
ret = virtio_gpu_object_list_validate(&ticket, &validate_list);
if (ret) {
DRM_DEBUG("failed to validate\n");
goto fail_unref;
}
rc_3d.resource_id = cpu_to_le32(res_id);
rc_3d.target = cpu_to_le32(rc->target);
rc_3d.format = cpu_to_le32(rc->format);
rc_3d.bind = cpu_to_le32(rc->bind);
rc_3d.width = cpu_to_le32(rc->width);
rc_3d.height = cpu_to_le32(rc->height);
rc_3d.depth = cpu_to_le32(rc->depth);
rc_3d.array_size = cpu_to_le32(rc->array_size);
rc_3d.last_level = cpu_to_le32(rc->last_level);
rc_3d.nr_samples = cpu_to_le32(rc->nr_samples);
rc_3d.flags = cpu_to_le32(rc->flags);
virtio_gpu_cmd_resource_create_3d(vgdev, &rc_3d, NULL);
ret = virtio_gpu_object_attach(vgdev, qobj, res_id, &fence);
if (ret) {
ttm_eu_backoff_reservation(&ticket, &validate_list);
goto fail_unref;
}
ttm_eu_fence_buffer_objects(&ticket, &validate_list, &fence->f);
}
qobj->hw_res_handle = res_id;
ret = drm_gem_handle_create(file_priv, obj, &handle);
if (ret) {
drm_gem_object_release(obj);
if (vgdev->has_virgl_3d) {
virtio_gpu_unref_list(&validate_list);
fence_put(&fence->f);
}
return ret;
}
drm_gem_object_unreference_unlocked(obj);
rc->res_handle = res_id; /* similiar to a VM address */
rc->bo_handle = handle;
if (vgdev->has_virgl_3d) {
virtio_gpu_unref_list(&validate_list);
fence_put(&fence->f);
}
return 0;
fail_unref:
if (vgdev->has_virgl_3d) {
virtio_gpu_unref_list(&validate_list);
fence_put(&fence->f);
}
//fail_obj:
// drm_gem_object_handle_unreference_unlocked(obj);
fail_id:
virtio_gpu_resource_id_put(vgdev, res_id);
return ret;
}
static int virtio_gpu_resource_info_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_virtgpu_resource_info *ri = data;
struct drm_gem_object *gobj = NULL;
struct virtio_gpu_object *qobj = NULL;
gobj = drm_gem_object_lookup(dev, file_priv, ri->bo_handle);
if (gobj == NULL)
return -ENOENT;
qobj = gem_to_virtio_gpu_obj(gobj);
ri->size = qobj->gem_base.size;
ri->res_handle = qobj->hw_res_handle;
drm_gem_object_unreference_unlocked(gobj);
return 0;
}
static int virtio_gpu_transfer_from_host_ioctl(struct drm_device *dev,
void *data,
struct drm_file *file)
{
struct virtio_gpu_device *vgdev = dev->dev_private;
struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
struct drm_virtgpu_3d_transfer_from_host *args = data;
struct drm_gem_object *gobj = NULL;
struct virtio_gpu_object *qobj = NULL;
struct virtio_gpu_fence *fence;
int ret;
u32 offset = args->offset;
struct virtio_gpu_box box;
if (vgdev->has_virgl_3d == false)
return -ENOSYS;
gobj = drm_gem_object_lookup(dev, file, args->bo_handle);
if (gobj == NULL)
return -ENOENT;
qobj = gem_to_virtio_gpu_obj(gobj);
ret = virtio_gpu_object_reserve(qobj, false);
if (ret)
goto out;
ret = ttm_bo_validate(&qobj->tbo, &qobj->placement,
true, false);
if (unlikely(ret))
goto out_unres;
convert_to_hw_box(&box, &args->box);
virtio_gpu_cmd_transfer_from_host_3d
(vgdev, qobj->hw_res_handle,
vfpriv->ctx_id, offset, args->level,
&box, &fence);
reservation_object_add_excl_fence(qobj->tbo.resv,
&fence->f);
fence_put(&fence->f);
out_unres:
virtio_gpu_object_unreserve(qobj);
out:
drm_gem_object_unreference_unlocked(gobj);
return ret;
}
static int virtio_gpu_transfer_to_host_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct virtio_gpu_device *vgdev = dev->dev_private;
struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
struct drm_virtgpu_3d_transfer_to_host *args = data;
struct drm_gem_object *gobj = NULL;
struct virtio_gpu_object *qobj = NULL;
struct virtio_gpu_fence *fence;
struct virtio_gpu_box box;
int ret;
u32 offset = args->offset;
gobj = drm_gem_object_lookup(dev, file, args->bo_handle);
if (gobj == NULL)
return -ENOENT;
qobj = gem_to_virtio_gpu_obj(gobj);
ret = virtio_gpu_object_reserve(qobj, false);
if (ret)
goto out;
ret = ttm_bo_validate(&qobj->tbo, &qobj->placement,
true, false);
if (unlikely(ret))
goto out_unres;
convert_to_hw_box(&box, &args->box);
if (!vgdev->has_virgl_3d) {
virtio_gpu_cmd_transfer_to_host_2d
(vgdev, qobj->hw_res_handle, offset,
box.w, box.h, box.x, box.y, NULL);
} else {
virtio_gpu_cmd_transfer_to_host_3d
(vgdev, qobj->hw_res_handle,
vfpriv ? vfpriv->ctx_id : 0, offset,
args->level, &box, &fence);
reservation_object_add_excl_fence(qobj->tbo.resv,
&fence->f);
fence_put(&fence->f);
}
out_unres:
virtio_gpu_object_unreserve(qobj);
out:
drm_gem_object_unreference_unlocked(gobj);
return ret;
}
static int virtio_gpu_wait_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_virtgpu_3d_wait *args = data;
struct drm_gem_object *gobj = NULL;
struct virtio_gpu_object *qobj = NULL;
int ret;
bool nowait = false;
gobj = drm_gem_object_lookup(dev, file, args->handle);
if (gobj == NULL)
return -ENOENT;
qobj = gem_to_virtio_gpu_obj(gobj);
if (args->flags & VIRTGPU_WAIT_NOWAIT)
nowait = true;
ret = virtio_gpu_object_wait(qobj, nowait);
drm_gem_object_unreference_unlocked(gobj);
return ret;
}
static int virtio_gpu_get_caps_ioctl(struct drm_device *dev,
void *data, struct drm_file *file)
{
struct virtio_gpu_device *vgdev = dev->dev_private;
struct drm_virtgpu_get_caps *args = data;
int size;
int i;
int found_valid = -1;
int ret;
struct virtio_gpu_drv_cap_cache *cache_ent;
void *ptr;
if (vgdev->num_capsets == 0)
return -ENOSYS;
spin_lock(&vgdev->display_info_lock);
for (i = 0; i < vgdev->num_capsets; i++) {
if (vgdev->capsets[i].id == args->cap_set_id) {
if (vgdev->capsets[i].max_version >= args->cap_set_ver) {
found_valid = i;
break;
}
}
}
if (found_valid == -1) {
spin_unlock(&vgdev->display_info_lock);
return -EINVAL;
}
size = vgdev->capsets[found_valid].max_size;
if (args->size > size) {
spin_unlock(&vgdev->display_info_lock);
return -EINVAL;
}
list_for_each_entry(cache_ent, &vgdev->cap_cache, head) {
if (cache_ent->id == args->cap_set_id &&
cache_ent->version == args->cap_set_ver) {
ptr = cache_ent->caps_cache;
spin_unlock(&vgdev->display_info_lock);
goto copy_exit;
}
}
spin_unlock(&vgdev->display_info_lock);
/* not in cache - need to talk to hw */
virtio_gpu_cmd_get_capset(vgdev, found_valid, args->cap_set_ver,
&cache_ent);
ret = wait_event_timeout(vgdev->resp_wq,
atomic_read(&cache_ent->is_valid), 5 * HZ);
ptr = cache_ent->caps_cache;
copy_exit:
if (copy_to_user((void __user *)(unsigned long)args->addr, ptr, size))
return -EFAULT;
return 0;
}
struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS] = {
DRM_IOCTL_DEF_DRV(VIRTGPU_MAP, virtio_gpu_map_ioctl,
DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(VIRTGPU_EXECBUFFER, virtio_gpu_execbuffer_ioctl,
DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(VIRTGPU_GETPARAM, virtio_gpu_getparam_ioctl,
DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(VIRTGPU_RESOURCE_CREATE,
virtio_gpu_resource_create_ioctl,
DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(VIRTGPU_RESOURCE_INFO, virtio_gpu_resource_info_ioctl,
DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
/* make transfer async to the main ring? - no sure, can we
thread these in the underlying GL */
DRM_IOCTL_DEF_DRV(VIRTGPU_TRANSFER_FROM_HOST,
virtio_gpu_transfer_from_host_ioctl,
DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(VIRTGPU_TRANSFER_TO_HOST,
virtio_gpu_transfer_to_host_ioctl,
DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(VIRTGPU_WAIT, virtio_gpu_wait_ioctl,
DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(VIRTGPU_GET_CAPS, virtio_gpu_get_caps_ioctl,
DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
};

View File

@@ -52,6 +52,41 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work)
events_clear, &events_clear);
}
static void virtio_gpu_ctx_id_get(struct virtio_gpu_device *vgdev,
uint32_t *resid)
{
int handle;
idr_preload(GFP_KERNEL);
spin_lock(&vgdev->ctx_id_idr_lock);
handle = idr_alloc(&vgdev->ctx_id_idr, NULL, 1, 0, 0);
spin_unlock(&vgdev->ctx_id_idr_lock);
idr_preload_end();
*resid = handle;
}
static void virtio_gpu_ctx_id_put(struct virtio_gpu_device *vgdev, uint32_t id)
{
spin_lock(&vgdev->ctx_id_idr_lock);
idr_remove(&vgdev->ctx_id_idr, id);
spin_unlock(&vgdev->ctx_id_idr_lock);
}
static void virtio_gpu_context_create(struct virtio_gpu_device *vgdev,
uint32_t nlen, const char *name,
uint32_t *ctx_id)
{
virtio_gpu_ctx_id_get(vgdev, ctx_id);
virtio_gpu_cmd_context_create(vgdev, *ctx_id, nlen, name);
}
static void virtio_gpu_context_destroy(struct virtio_gpu_device *vgdev,
uint32_t ctx_id)
{
virtio_gpu_cmd_context_destroy(vgdev, ctx_id);
virtio_gpu_ctx_id_put(vgdev, ctx_id);
}
static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq,
void (*work_func)(struct work_struct *work))
{
@@ -60,6 +95,36 @@ static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq,
INIT_WORK(&vgvq->dequeue_work, work_func);
}
static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev,
int num_capsets)
{
int i, ret;
vgdev->capsets = kcalloc(num_capsets,
sizeof(struct virtio_gpu_drv_capset),
GFP_KERNEL);
if (!vgdev->capsets) {
DRM_ERROR("failed to allocate cap sets\n");
return;
}
for (i = 0; i < num_capsets; i++) {
virtio_gpu_cmd_get_capset_info(vgdev, i);
ret = wait_event_timeout(vgdev->resp_wq,
vgdev->capsets[i].id > 0, 5 * HZ);
if (ret == 0) {
DRM_ERROR("timed out waiting for cap set %d\n", i);
kfree(vgdev->capsets);
vgdev->capsets = NULL;
return;
}
DRM_INFO("cap set %d: id %d, max-version %d, max-size %d\n",
i, vgdev->capsets[i].id,
vgdev->capsets[i].max_version,
vgdev->capsets[i].max_size);
}
vgdev->num_capsets = num_capsets;
}
int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
{
static vq_callback_t *callbacks[] = {
@@ -70,7 +135,7 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
struct virtio_gpu_device *vgdev;
/* this will expand later */
struct virtqueue *vqs[2];
u32 num_scanouts;
u32 num_scanouts, num_capsets;
int ret;
if (!virtio_has_feature(dev->virtdev, VIRTIO_F_VERSION_1))
@@ -96,9 +161,15 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
spin_lock_init(&vgdev->fence_drv.lock);
INIT_LIST_HEAD(&vgdev->fence_drv.fences);
INIT_LIST_HEAD(&vgdev->cap_cache);
INIT_WORK(&vgdev->config_changed_work,
virtio_gpu_config_changed_work_func);
if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_F_VIRGL))
vgdev->has_virgl_3d = true;
DRM_INFO("virgl 3d acceleration %s\n",
vgdev->has_virgl_3d ? "enabled" : "not available");
ret = vgdev->vdev->config->find_vqs(vgdev->vdev, 2, vqs,
callbacks, names);
if (ret) {
@@ -129,6 +200,11 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
ret = -EINVAL;
goto err_scanouts;
}
DRM_INFO("number of scanouts: %d\n", num_scanouts);
virtio_cread(vgdev->vdev, struct virtio_gpu_config,
num_capsets, &num_capsets);
DRM_INFO("number of cap sets: %d\n", num_capsets);
ret = virtio_gpu_modeset_init(vgdev);
if (ret)
@@ -137,6 +213,8 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
virtio_device_ready(vgdev->vdev);
vgdev->vqs_ready = true;
if (num_capsets)
virtio_gpu_get_capsets(vgdev, num_capsets);
virtio_gpu_cmd_get_display_info(vgdev);
wait_event_timeout(vgdev->resp_wq, !vgdev->display_info_pending,
5 * HZ);
@@ -157,6 +235,16 @@ err_vqs:
return ret;
}
static void virtio_gpu_cleanup_cap_cache(struct virtio_gpu_device *vgdev)
{
struct virtio_gpu_drv_cap_cache *cache_ent, *tmp;
list_for_each_entry_safe(cache_ent, tmp, &vgdev->cap_cache, head) {
kfree(cache_ent->caps_cache);
kfree(cache_ent);
}
}
int virtio_gpu_driver_unload(struct drm_device *dev)
{
struct virtio_gpu_device *vgdev = dev->dev_private;
@@ -170,6 +258,49 @@ int virtio_gpu_driver_unload(struct drm_device *dev)
virtio_gpu_modeset_fini(vgdev);
virtio_gpu_ttm_fini(vgdev);
virtio_gpu_free_vbufs(vgdev);
virtio_gpu_cleanup_cap_cache(vgdev);
kfree(vgdev->capsets);
kfree(vgdev);
return 0;
}
int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file)
{
struct virtio_gpu_device *vgdev = dev->dev_private;
struct virtio_gpu_fpriv *vfpriv;
uint32_t id;
char dbgname[64], tmpname[TASK_COMM_LEN];
/* can't create contexts without 3d renderer */
if (!vgdev->has_virgl_3d)
return 0;
get_task_comm(tmpname, current);
snprintf(dbgname, sizeof(dbgname), "%s", tmpname);
dbgname[63] = 0;
/* allocate a virt GPU context for this opener */
vfpriv = kzalloc(sizeof(*vfpriv), GFP_KERNEL);
if (!vfpriv)
return -ENOMEM;
virtio_gpu_context_create(vgdev, strlen(dbgname), dbgname, &id);
vfpriv->ctx_id = id;
file->driver_priv = vfpriv;
return 0;
}
void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file)
{
struct virtio_gpu_device *vgdev = dev->dev_private;
struct virtio_gpu_fpriv *vfpriv;
if (!vgdev->has_virgl_3d)
return;
vfpriv = file->driver_priv;
virtio_gpu_context_destroy(vgdev, vfpriv->ctx_id);
kfree(vfpriv);
file->driver_priv = NULL;
}

View File

@@ -82,24 +82,19 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
size = roundup(size, PAGE_SIZE);
ret = drm_gem_object_init(vgdev->ddev, &bo->gem_base, size);
if (ret != 0)
goto err_gem_init;
return ret;
bo->dumb = false;
virtio_gpu_init_ttm_placement(bo, pinned);
ret = ttm_bo_init(&vgdev->mman.bdev, &bo->tbo, size, type,
&bo->placement, 0, !kernel, NULL, acc_size,
NULL, NULL, &virtio_gpu_ttm_bo_destroy);
/* ttm_bo_init failure will call the destroy */
if (ret != 0)
goto err_ttm_init;
return ret;
*bo_ptr = bo;
return 0;
err_ttm_init:
drm_gem_object_release(&bo->gem_base);
err_gem_init:
kfree(bo);
return ret;
}
int virtio_gpu_object_kmap(struct virtio_gpu_object *bo, void **ptr)

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2014 Canonical
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Andreas Pokorny
*/
#include "virtgpu_drv.h"
/* Empty Implementations as there should not be any other driver for a virtual
* device that might share buffers with virtgpu */
int virtgpu_gem_prime_pin(struct drm_gem_object *obj)
{
WARN_ONCE(1, "not implemented");
return -ENODEV;
}
void virtgpu_gem_prime_unpin(struct drm_gem_object *obj)
{
WARN_ONCE(1, "not implemented");
}
struct sg_table *virtgpu_gem_prime_get_sg_table(struct drm_gem_object *obj)
{
WARN_ONCE(1, "not implemented");
return ERR_PTR(-ENODEV);
}
struct drm_gem_object *virtgpu_gem_prime_import_sg_table(
struct drm_device *dev, struct dma_buf_attachment *attach,
struct sg_table *table)
{
WARN_ONCE(1, "not implemented");
return ERR_PTR(-ENODEV);
}
void *virtgpu_gem_prime_vmap(struct drm_gem_object *obj)
{
WARN_ONCE(1, "not implemented");
return ERR_PTR(-ENODEV);
}
void virtgpu_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
{
WARN_ONCE(1, "not implemented");
}
int virtgpu_gem_prime_mmap(struct drm_gem_object *obj,
struct vm_area_struct *area)
{
return -ENODEV;
}

View File

@@ -32,6 +32,7 @@
#include <ttm/ttm_module.h>
#include <drm/drmP.h>
#include <drm/drm.h>
#include <drm/virtgpu_drm.h>
#include "virtgpu_drv.h"
#include <linux/delay.h>

View File

@@ -293,8 +293,8 @@ void virtio_gpu_dequeue_cursor_func(struct work_struct *work)
wake_up(&vgdev->cursorq.ack_queue);
}
static int virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev,
struct virtio_gpu_vbuffer *vbuf)
static int virtio_gpu_queue_ctrl_buffer_locked(struct virtio_gpu_device *vgdev,
struct virtio_gpu_vbuffer *vbuf)
{
struct virtqueue *vq = vgdev->ctrlq.vq;
struct scatterlist *sgs[3], vcmd, vout, vresp;
@@ -320,7 +320,6 @@ static int virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev,
incnt++;
}
spin_lock(&vgdev->ctrlq.qlock);
retry:
ret = virtqueue_add_sgs(vq, sgs, outcnt, incnt, vbuf, GFP_ATOMIC);
if (ret == -ENOSPC) {
@@ -331,13 +330,55 @@ retry:
} else {
virtqueue_kick(vq);
}
spin_unlock(&vgdev->ctrlq.qlock);
if (!ret)
ret = vq->num_free;
return ret;
}
static int virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev,
struct virtio_gpu_vbuffer *vbuf)
{
int rc;
spin_lock(&vgdev->ctrlq.qlock);
rc = virtio_gpu_queue_ctrl_buffer_locked(vgdev, vbuf);
spin_unlock(&vgdev->ctrlq.qlock);
return rc;
}
static int virtio_gpu_queue_fenced_ctrl_buffer(struct virtio_gpu_device *vgdev,
struct virtio_gpu_vbuffer *vbuf,
struct virtio_gpu_ctrl_hdr *hdr,
struct virtio_gpu_fence **fence)
{
struct virtqueue *vq = vgdev->ctrlq.vq;
int rc;
again:
spin_lock(&vgdev->ctrlq.qlock);
/*
* Make sure we have enouth space in the virtqueue. If not
* wait here until we have.
*
* Without that virtio_gpu_queue_ctrl_buffer_nolock might have
* to wait for free space, which can result in fence ids being
* submitted out-of-order.
*/
if (vq->num_free < 3) {
spin_unlock(&vgdev->ctrlq.qlock);
wait_event(vgdev->ctrlq.ack_queue, vq->num_free >= 3);
goto again;
}
if (fence)
virtio_gpu_fence_emit(vgdev, hdr, fence);
rc = virtio_gpu_queue_ctrl_buffer_locked(vgdev, vbuf);
spin_unlock(&vgdev->ctrlq.qlock);
return rc;
}
static int virtio_gpu_queue_cursor(struct virtio_gpu_device *vgdev,
struct virtio_gpu_vbuffer *vbuf)
{
@@ -490,9 +531,7 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
cmd_p->r.x = x;
cmd_p->r.y = y;
if (fence)
virtio_gpu_fence_emit(vgdev, &cmd_p->hdr, fence);
virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
}
static void
@@ -515,9 +554,7 @@ virtio_gpu_cmd_resource_attach_backing(struct virtio_gpu_device *vgdev,
vbuf->data_buf = ents;
vbuf->data_size = sizeof(*ents) * nents;
if (fence)
virtio_gpu_fence_emit(vgdev, &cmd_p->hdr, fence);
virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
}
static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
@@ -549,6 +586,47 @@ static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
drm_kms_helper_hotplug_event(vgdev->ddev);
}
static void virtio_gpu_cmd_get_capset_info_cb(struct virtio_gpu_device *vgdev,
struct virtio_gpu_vbuffer *vbuf)
{
struct virtio_gpu_get_capset_info *cmd =
(struct virtio_gpu_get_capset_info *)vbuf->buf;
struct virtio_gpu_resp_capset_info *resp =
(struct virtio_gpu_resp_capset_info *)vbuf->resp_buf;
int i = le32_to_cpu(cmd->capset_index);
spin_lock(&vgdev->display_info_lock);
vgdev->capsets[i].id = le32_to_cpu(resp->capset_id);
vgdev->capsets[i].max_version = le32_to_cpu(resp->capset_max_version);
vgdev->capsets[i].max_size = le32_to_cpu(resp->capset_max_size);
spin_unlock(&vgdev->display_info_lock);
wake_up(&vgdev->resp_wq);
}
static void virtio_gpu_cmd_capset_cb(struct virtio_gpu_device *vgdev,
struct virtio_gpu_vbuffer *vbuf)
{
struct virtio_gpu_get_capset *cmd =
(struct virtio_gpu_get_capset *)vbuf->buf;
struct virtio_gpu_resp_capset *resp =
(struct virtio_gpu_resp_capset *)vbuf->resp_buf;
struct virtio_gpu_drv_cap_cache *cache_ent;
spin_lock(&vgdev->display_info_lock);
list_for_each_entry(cache_ent, &vgdev->cap_cache, head) {
if (cache_ent->version == le32_to_cpu(cmd->capset_version) &&
cache_ent->id == le32_to_cpu(cmd->capset_id)) {
memcpy(cache_ent->caps_cache, resp->capset_data,
cache_ent->size);
atomic_set(&cache_ent->is_valid, 1);
break;
}
}
spin_unlock(&vgdev->display_info_lock);
wake_up(&vgdev->resp_wq);
}
int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev)
{
struct virtio_gpu_ctrl_hdr *cmd_p;
@@ -572,6 +650,230 @@ int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev)
return 0;
}
int virtio_gpu_cmd_get_capset_info(struct virtio_gpu_device *vgdev, int idx)
{
struct virtio_gpu_get_capset_info *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
void *resp_buf;
resp_buf = kzalloc(sizeof(struct virtio_gpu_resp_capset_info),
GFP_KERNEL);
if (!resp_buf)
return -ENOMEM;
cmd_p = virtio_gpu_alloc_cmd_resp
(vgdev, &virtio_gpu_cmd_get_capset_info_cb, &vbuf,
sizeof(*cmd_p), sizeof(struct virtio_gpu_resp_capset_info),
resp_buf);
memset(cmd_p, 0, sizeof(*cmd_p));
cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_GET_CAPSET_INFO);
cmd_p->capset_index = cpu_to_le32(idx);
virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
return 0;
}
int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev,
int idx, int version,
struct virtio_gpu_drv_cap_cache **cache_p)
{
struct virtio_gpu_get_capset *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
int max_size = vgdev->capsets[idx].max_size;
struct virtio_gpu_drv_cap_cache *cache_ent;
void *resp_buf;
if (idx > vgdev->num_capsets)
return -EINVAL;
if (version > vgdev->capsets[idx].max_version)
return -EINVAL;
cache_ent = kzalloc(sizeof(*cache_ent), GFP_KERNEL);
if (!cache_ent)
return -ENOMEM;
cache_ent->caps_cache = kmalloc(max_size, GFP_KERNEL);
if (!cache_ent->caps_cache) {
kfree(cache_ent);
return -ENOMEM;
}
resp_buf = kzalloc(sizeof(struct virtio_gpu_resp_capset) + max_size,
GFP_KERNEL);
if (!resp_buf) {
kfree(cache_ent->caps_cache);
kfree(cache_ent);
return -ENOMEM;
}
cache_ent->version = version;
cache_ent->id = vgdev->capsets[idx].id;
atomic_set(&cache_ent->is_valid, 0);
cache_ent->size = max_size;
spin_lock(&vgdev->display_info_lock);
list_add_tail(&cache_ent->head, &vgdev->cap_cache);
spin_unlock(&vgdev->display_info_lock);
cmd_p = virtio_gpu_alloc_cmd_resp
(vgdev, &virtio_gpu_cmd_capset_cb, &vbuf, sizeof(*cmd_p),
sizeof(struct virtio_gpu_resp_capset) + max_size,
resp_buf);
cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_GET_CAPSET);
cmd_p->capset_id = cpu_to_le32(vgdev->capsets[idx].id);
cmd_p->capset_version = cpu_to_le32(version);
*cache_p = cache_ent;
virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
return 0;
}
void virtio_gpu_cmd_context_create(struct virtio_gpu_device *vgdev, uint32_t id,
uint32_t nlen, const char *name)
{
struct virtio_gpu_ctx_create *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
memset(cmd_p, 0, sizeof(*cmd_p));
cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_CTX_CREATE);
cmd_p->hdr.ctx_id = cpu_to_le32(id);
cmd_p->nlen = cpu_to_le32(nlen);
strncpy(cmd_p->debug_name, name, sizeof(cmd_p->debug_name)-1);
cmd_p->debug_name[sizeof(cmd_p->debug_name)-1] = 0;
virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
}
void virtio_gpu_cmd_context_destroy(struct virtio_gpu_device *vgdev,
uint32_t id)
{
struct virtio_gpu_ctx_destroy *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
memset(cmd_p, 0, sizeof(*cmd_p));
cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_CTX_DESTROY);
cmd_p->hdr.ctx_id = cpu_to_le32(id);
virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
}
void virtio_gpu_cmd_context_attach_resource(struct virtio_gpu_device *vgdev,
uint32_t ctx_id,
uint32_t resource_id)
{
struct virtio_gpu_ctx_resource *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
memset(cmd_p, 0, sizeof(*cmd_p));
cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE);
cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
cmd_p->resource_id = cpu_to_le32(resource_id);
virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
}
void virtio_gpu_cmd_context_detach_resource(struct virtio_gpu_device *vgdev,
uint32_t ctx_id,
uint32_t resource_id)
{
struct virtio_gpu_ctx_resource *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
memset(cmd_p, 0, sizeof(*cmd_p));
cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE);
cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
cmd_p->resource_id = cpu_to_le32(resource_id);
virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
}
void
virtio_gpu_cmd_resource_create_3d(struct virtio_gpu_device *vgdev,
struct virtio_gpu_resource_create_3d *rc_3d,
struct virtio_gpu_fence **fence)
{
struct virtio_gpu_resource_create_3d *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
memset(cmd_p, 0, sizeof(*cmd_p));
*cmd_p = *rc_3d;
cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_CREATE_3D);
cmd_p->hdr.flags = 0;
virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
}
void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev,
uint32_t resource_id, uint32_t ctx_id,
uint64_t offset, uint32_t level,
struct virtio_gpu_box *box,
struct virtio_gpu_fence **fence)
{
struct virtio_gpu_transfer_host_3d *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
memset(cmd_p, 0, sizeof(*cmd_p));
cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D);
cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
cmd_p->resource_id = cpu_to_le32(resource_id);
cmd_p->box = *box;
cmd_p->offset = cpu_to_le64(offset);
cmd_p->level = cpu_to_le32(level);
virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
}
void virtio_gpu_cmd_transfer_from_host_3d(struct virtio_gpu_device *vgdev,
uint32_t resource_id, uint32_t ctx_id,
uint64_t offset, uint32_t level,
struct virtio_gpu_box *box,
struct virtio_gpu_fence **fence)
{
struct virtio_gpu_transfer_host_3d *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
memset(cmd_p, 0, sizeof(*cmd_p));
cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D);
cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
cmd_p->resource_id = cpu_to_le32(resource_id);
cmd_p->box = *box;
cmd_p->offset = cpu_to_le64(offset);
cmd_p->level = cpu_to_le32(level);
virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
}
void virtio_gpu_cmd_submit(struct virtio_gpu_device *vgdev,
void *data, uint32_t data_size,
uint32_t ctx_id, struct virtio_gpu_fence **fence)
{
struct virtio_gpu_cmd_submit *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
memset(cmd_p, 0, sizeof(*cmd_p));
vbuf->data_buf = data;
vbuf->data_size = data_size;
cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_SUBMIT_3D);
cmd_p->hdr.ctx_id = cpu_to_le32(ctx_id);
cmd_p->size = cpu_to_le32(data_size);
virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence);
}
int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *obj,
uint32_t resource_id,