Message ID | 1432300312-24792-5-git-send-email-kraxel@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Fri, May 22, 2015 at 03:11:52PM +0200, Gerd Hoffmann wrote: > From: Dave Airlie <airlied@gmail.com> > > This patch adds a kms driver for the virtio gpu. The xorg modesetting > driver can handle the device just fine, the framebuffer for fbcon is > there too. > > Qemu patches for the host side are under review currently. > > The pci version of the device comes in two variants: with and without > vga compatibility. The former has a extra memory bar for the vga > framebuffer, the later is a pure virtio device. The only concern for > this driver is that in the virtio-vga case we have to kick out the > firmware framebuffer. > > Initial revision has only 2d support, 3d (virgl) support requires > some more work on the qemu side and will be added later. > > Signed-off-by: Dave Airlie <airlied@redhat.com> > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> I think it's ok to merge this through the dri tree. Acked-by: Michael S. Tsirkin <mst@redhat.com> > --- > drivers/gpu/drm/Kconfig | 2 + > drivers/gpu/drm/Makefile | 1 + > drivers/gpu/drm/virtio/Kconfig | 14 + > drivers/gpu/drm/virtio/Makefile | 11 + > drivers/gpu/drm/virtio/virtgpu_debugfs.c | 64 ++++ > drivers/gpu/drm/virtio/virtgpu_display.c | 485 ++++++++++++++++++++++++ > drivers/gpu/drm/virtio/virtgpu_drm_bus.c | 91 +++++ > drivers/gpu/drm/virtio/virtgpu_drv.c | 136 +++++++ > drivers/gpu/drm/virtio/virtgpu_drv.h | 350 ++++++++++++++++++ > drivers/gpu/drm/virtio/virtgpu_fb.c | 431 ++++++++++++++++++++++ > drivers/gpu/drm/virtio/virtgpu_fence.c | 119 ++++++ > drivers/gpu/drm/virtio/virtgpu_gem.c | 140 +++++++ > drivers/gpu/drm/virtio/virtgpu_kms.c | 164 +++++++++ > drivers/gpu/drm/virtio/virtgpu_object.c | 170 +++++++++ > drivers/gpu/drm/virtio/virtgpu_plane.c | 120 ++++++ > drivers/gpu/drm/virtio/virtgpu_ttm.c | 469 +++++++++++++++++++++++ > drivers/gpu/drm/virtio/virtgpu_vq.c | 614 +++++++++++++++++++++++++++++++ > include/drm/drmP.h | 1 + > include/uapi/linux/Kbuild | 1 + > include/uapi/linux/virtio_gpu.h | 204 ++++++++++ > include/uapi/linux/virtio_ids.h | 1 + > 21 files changed, 3588 insertions(+) > create mode 100644 drivers/gpu/drm/virtio/Kconfig > create mode 100644 drivers/gpu/drm/virtio/Makefile > create mode 100644 drivers/gpu/drm/virtio/virtgpu_debugfs.c > create mode 100644 drivers/gpu/drm/virtio/virtgpu_display.c > create mode 100644 drivers/gpu/drm/virtio/virtgpu_drm_bus.c > create mode 100644 drivers/gpu/drm/virtio/virtgpu_drv.c > create mode 100644 drivers/gpu/drm/virtio/virtgpu_drv.h > create mode 100644 drivers/gpu/drm/virtio/virtgpu_fb.c > create mode 100644 drivers/gpu/drm/virtio/virtgpu_fence.c > create mode 100644 drivers/gpu/drm/virtio/virtgpu_gem.c > create mode 100644 drivers/gpu/drm/virtio/virtgpu_kms.c > create mode 100644 drivers/gpu/drm/virtio/virtgpu_object.c > create mode 100644 drivers/gpu/drm/virtio/virtgpu_plane.c > create mode 100644 drivers/gpu/drm/virtio/virtgpu_ttm.c > create mode 100644 drivers/gpu/drm/virtio/virtgpu_vq.c > create mode 100644 include/uapi/linux/virtio_gpu.h > > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig > index 47f2ce8..d4b6545 100644 > --- a/drivers/gpu/drm/Kconfig > +++ b/drivers/gpu/drm/Kconfig > @@ -206,6 +206,8 @@ source "drivers/gpu/drm/qxl/Kconfig" > > source "drivers/gpu/drm/bochs/Kconfig" > > +source "drivers/gpu/drm/virtio/Kconfig" > + > source "drivers/gpu/drm/msm/Kconfig" > > source "drivers/gpu/drm/tegra/Kconfig" > diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile > index 7d4944e..dbbc101 100644 > --- a/drivers/gpu/drm/Makefile > +++ b/drivers/gpu/drm/Makefile > @@ -63,6 +63,7 @@ obj-$(CONFIG_DRM_OMAP) += omapdrm/ > obj-$(CONFIG_DRM_TILCDC) += tilcdc/ > obj-$(CONFIG_DRM_QXL) += qxl/ > obj-$(CONFIG_DRM_BOCHS) += bochs/ > +obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/ > obj-$(CONFIG_DRM_MSM) += msm/ > obj-$(CONFIG_DRM_TEGRA) += tegra/ > obj-$(CONFIG_DRM_STI) += sti/ > diff --git a/drivers/gpu/drm/virtio/Kconfig b/drivers/gpu/drm/virtio/Kconfig > new file mode 100644 > index 0000000..9983ead > --- /dev/null > +++ b/drivers/gpu/drm/virtio/Kconfig > @@ -0,0 +1,14 @@ > +config DRM_VIRTIO_GPU > + tristate "Virtio GPU driver" > + depends on DRM && VIRTIO > + select FB_SYS_FILLRECT > + select FB_SYS_COPYAREA > + select FB_SYS_IMAGEBLIT > + select DRM_KMS_HELPER > + select DRM_KMS_FB_HELPER > + select DRM_TTM > + help > + This is the virtual GPU driver for virtio. It can be used with > + QEMU based VMMs (like KVM or Xen). > + > + If unsure say M. > diff --git a/drivers/gpu/drm/virtio/Makefile b/drivers/gpu/drm/virtio/Makefile > new file mode 100644 > index 0000000..2ee1602 > --- /dev/null > +++ b/drivers/gpu/drm/virtio/Makefile > @@ -0,0 +1,11 @@ > +# > +# Makefile for the drm device driver. This driver provides support for the > +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. > + > +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 > + > +obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio-gpu.o > diff --git a/drivers/gpu/drm/virtio/virtgpu_debugfs.c b/drivers/gpu/drm/virtio/virtgpu_debugfs.c > new file mode 100644 > index 0000000..db8b491 > --- /dev/null > +++ b/drivers/gpu/drm/virtio/virtgpu_debugfs.c > @@ -0,0 +1,64 @@ > +/* > + * Copyright (C) 2015 Red Hat, Inc. > + * All Rights Reserved. > + * > + * Permission is hereby granted, free of charge, to any person obtaining > + * a copy of this software and associated documentation files (the > + * "Software"), to deal in the Software without restriction, including > + * without limitation the rights to use, copy, modify, merge, publish, > + * distribute, 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 (including the > + * next paragraph) shall be included in all copies or substantial > + * portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. > + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE > + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION > + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION > + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. > + */ > + > +#include <linux/debugfs.h> > + > +#include "drmP.h" > +#include "virtgpu_drv.h" > + > +static int > +virtio_gpu_debugfs_irq_info(struct seq_file *m, void *data) > +{ > + struct drm_info_node *node = (struct drm_info_node *) m->private; > + struct virtio_gpu_device *vgdev = node->minor->dev->dev_private; > + > + seq_printf(m, "fence %ld %lld\n", > + atomic64_read(&vgdev->fence_drv.last_seq), > + vgdev->fence_drv.sync_seq); > + return 0; > +} > + > +static struct drm_info_list virtio_gpu_debugfs_list[] = { > + { "irq_fence", virtio_gpu_debugfs_irq_info, 0, NULL }, > +}; > + > +#define VIRTIO_GPU_DEBUGFS_ENTRIES ARRAY_SIZE(virtio_gpu_debugfs_list) > + > +int > +virtio_gpu_debugfs_init(struct drm_minor *minor) > +{ > + drm_debugfs_create_files(virtio_gpu_debugfs_list, > + VIRTIO_GPU_DEBUGFS_ENTRIES, > + minor->debugfs_root, minor); > + return 0; > +} > + > +void > +virtio_gpu_debugfs_takedown(struct drm_minor *minor) > +{ > + drm_debugfs_remove_files(virtio_gpu_debugfs_list, > + VIRTIO_GPU_DEBUGFS_ENTRIES, > + minor); > +} > diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c > new file mode 100644 > index 0000000..a2eca5f > --- /dev/null > +++ b/drivers/gpu/drm/virtio/virtgpu_display.c > @@ -0,0 +1,485 @@ > +/* > + * 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 "virtgpu_drv.h" > +#include <drm/drm_crtc_helper.h> > +#include <drm/drm_atomic_helper.h> > + > +#define XRES_MIN 320 > +#define YRES_MIN 200 > + > +#define XRES_DEF 1024 > +#define YRES_DEF 768 > + > +#define XRES_MAX 8192 > +#define YRES_MAX 8192 > + > +static int virtio_gpu_fbdev = 1; > + > +MODULE_PARM_DESC(fbdev, "Disable/Enable framebuffer device & console"); > +module_param_named(fbdev, virtio_gpu_fbdev, int, 0400); > + > +static void virtio_gpu_crtc_gamma_set(struct drm_crtc *crtc, > + u16 *red, u16 *green, u16 *blue, > + uint32_t start, uint32_t size) > +{ > + /* TODO */ > +} > + > +static void > +virtio_gpu_hide_cursor(struct virtio_gpu_device *vgdev, > + struct virtio_gpu_output *output) > +{ > + output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR); > + output->cursor.resource_id = 0; > + virtio_gpu_cursor_ping(vgdev, output); > +} > + > +static int virtio_gpu_crtc_cursor_set(struct drm_crtc *crtc, > + struct drm_file *file_priv, > + uint32_t handle, > + uint32_t width, > + uint32_t height, > + int32_t hot_x, int32_t hot_y) > +{ > + struct virtio_gpu_device *vgdev = crtc->dev->dev_private; > + struct virtio_gpu_output *output = > + container_of(crtc, struct virtio_gpu_output, crtc); > + struct drm_gem_object *gobj = NULL; > + struct virtio_gpu_object *qobj = NULL; > + struct virtio_gpu_fence *fence = NULL; > + int ret = 0; > + > + if (handle == 0) { > + virtio_gpu_hide_cursor(vgdev, output); > + return 0; > + } > + > + /* lookup the cursor */ > + gobj = drm_gem_object_lookup(crtc->dev, file_priv, handle); > + if (gobj == NULL) > + return -ENOENT; > + > + qobj = gem_to_virtio_gpu_obj(gobj); > + > + if (!qobj->hw_res_handle) { > + ret = -EINVAL; > + goto out; > + } > + > + virtio_gpu_cmd_transfer_to_host_2d(vgdev, qobj->hw_res_handle, 0, > + cpu_to_le32(64), > + cpu_to_le32(64), > + 0, 0, &fence); > + > + output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR); > + output->cursor.resource_id = cpu_to_le32(qobj->hw_res_handle); > + output->cursor.hot_x = cpu_to_le32(hot_x); > + output->cursor.hot_y = cpu_to_le32(hot_y); > + virtio_gpu_cursor_ping(vgdev, output); > + ret = 0; > + > +out: > + drm_gem_object_unreference_unlocked(gobj); > + return ret; > +} > + > +static int virtio_gpu_crtc_cursor_move(struct drm_crtc *crtc, > + int x, int y) > +{ > + struct virtio_gpu_device *vgdev = crtc->dev->dev_private; > + struct virtio_gpu_output *output = > + container_of(crtc, struct virtio_gpu_output, crtc); > + > + output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_MOVE_CURSOR); > + output->cursor.pos.x = cpu_to_le32(x); > + output->cursor.pos.y = cpu_to_le32(y); > + virtio_gpu_cursor_ping(vgdev, output); > + 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, > + .gamma_set = virtio_gpu_crtc_gamma_set, > + .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 > + .reset = drm_atomic_helper_crtc_reset, > + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, > +}; > + > +static void virtio_gpu_user_framebuffer_destroy(struct drm_framebuffer *fb) > +{ > + struct virtio_gpu_framebuffer *virtio_gpu_fb > + = to_virtio_gpu_framebuffer(fb); > + > + if (virtio_gpu_fb->obj) > + drm_gem_object_unreference_unlocked(virtio_gpu_fb->obj); > + drm_framebuffer_cleanup(fb); > + kfree(virtio_gpu_fb); > +} > + > +static int > +virtio_gpu_framebuffer_surface_dirty(struct drm_framebuffer *fb, > + struct drm_file *file_priv, > + unsigned flags, unsigned color, > + struct drm_clip_rect *clips, > + unsigned num_clips) > +{ > + struct virtio_gpu_framebuffer *virtio_gpu_fb > + = to_virtio_gpu_framebuffer(fb); > + > + return virtio_gpu_surface_dirty(virtio_gpu_fb, clips, num_clips); > +} > + > +static const struct drm_framebuffer_funcs virtio_gpu_fb_funcs = { > + .destroy = virtio_gpu_user_framebuffer_destroy, > + .dirty = virtio_gpu_framebuffer_surface_dirty, > +}; > + > +int > +virtio_gpu_framebuffer_init(struct drm_device *dev, > + struct virtio_gpu_framebuffer *vgfb, > + struct drm_mode_fb_cmd2 *mode_cmd, > + struct drm_gem_object *obj) > +{ > + int ret; > + struct virtio_gpu_object *bo; > + vgfb->obj = obj; > + > + bo = gem_to_virtio_gpu_obj(obj); > + > + ret = drm_framebuffer_init(dev, &vgfb->base, &virtio_gpu_fb_funcs); > + if (ret) { > + vgfb->obj = NULL; > + return ret; > + } > + drm_helper_mode_fill_fb_struct(&vgfb->base, mode_cmd); > + > + spin_lock_init(&vgfb->dirty_lock); > + vgfb->x1 = vgfb->y1 = INT_MAX; > + vgfb->x2 = vgfb->y2 = 0; > + return 0; > +} > + > +static bool virtio_gpu_crtc_mode_fixup(struct drm_crtc *crtc, > + const struct drm_display_mode *mode, > + struct drm_display_mode *adjusted_mode) > +{ > + return true; > +} > + > +static void virtio_gpu_crtc_mode_set_nofb(struct drm_crtc *crtc) > +{ > + struct drm_device *dev = crtc->dev; > + struct virtio_gpu_device *vgdev = dev->dev_private; > + struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc); > + > + virtio_gpu_cmd_set_scanout(vgdev, output->index, 0, > + crtc->mode.hdisplay, > + crtc->mode.vdisplay, 0, 0); > +} > + > +static void virtio_gpu_crtc_enable(struct drm_crtc *crtc) > +{ > +} > + > +static void virtio_gpu_crtc_disable(struct drm_crtc *crtc) > +{ > + struct drm_device *dev = crtc->dev; > + struct virtio_gpu_device *vgdev = dev->dev_private; > + struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc); > + > + virtio_gpu_cmd_set_scanout(vgdev, output->index, 0, 0, 0, 0, 0); > +} > + > +static int virtio_gpu_crtc_atomic_check(struct drm_crtc *crtc, > + struct drm_crtc_state *state) > +{ > + return 0; > +} > + > +static const struct drm_crtc_helper_funcs virtio_gpu_crtc_helper_funcs = { > + .enable = virtio_gpu_crtc_enable, > + .disable = virtio_gpu_crtc_disable, > + .mode_fixup = virtio_gpu_crtc_mode_fixup, > + .mode_set_nofb = virtio_gpu_crtc_mode_set_nofb, > + .atomic_check = virtio_gpu_crtc_atomic_check, > +}; > + > +static bool virtio_gpu_enc_mode_fixup(struct drm_encoder *encoder, > + const struct drm_display_mode *mode, > + struct drm_display_mode *adjusted_mode) > +{ > + return true; > +} > + > +static void virtio_gpu_enc_mode_set(struct drm_encoder *encoder, > + struct drm_display_mode *mode, > + struct drm_display_mode *adjusted_mode) > +{ > +} > + > +static void virtio_gpu_enc_enable(struct drm_encoder *encoder) > +{ > +} > + > +static void virtio_gpu_enc_disable(struct drm_encoder *encoder) > +{ > +} > + > +static int virtio_gpu_conn_get_modes(struct drm_connector *connector) > +{ > + struct virtio_gpu_output *output = > + drm_connector_to_virtio_gpu_output(connector); > + struct drm_display_mode *mode = NULL; > + int count, width, height; > + > + width = le32_to_cpu(output->info.r.width); > + height = le32_to_cpu(output->info.r.height); > + count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX); > + > + if (width == 0 || height == 0) { > + width = XRES_DEF; > + height = YRES_DEF; > + drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF); > + } else { > + DRM_DEBUG("add mode: %dx%d\n", width, height); > + mode = drm_cvt_mode(connector->dev, width, height, 60, > + false, false, false); > + mode->type |= DRM_MODE_TYPE_PREFERRED; > + drm_mode_probed_add(connector, mode); > + count++; > + } > + > + return count; > +} > + > +static int virtio_gpu_conn_mode_valid(struct drm_connector *connector, > + struct drm_display_mode *mode) > +{ > + struct virtio_gpu_output *output = > + drm_connector_to_virtio_gpu_output(connector); > + int width, height; > + > + width = le32_to_cpu(output->info.r.width); > + height = le32_to_cpu(output->info.r.height); > + > + if (!(mode->type & DRM_MODE_TYPE_PREFERRED)) > + return MODE_OK; > + if (mode->hdisplay == XRES_DEF && mode->vdisplay == YRES_DEF) > + return MODE_OK; > + if (mode->hdisplay <= width && mode->hdisplay >= width - 16 && > + mode->vdisplay <= height && mode->vdisplay >= height - 16) > + return MODE_OK; > + > + DRM_DEBUG("del mode: %dx%d\n", mode->hdisplay, mode->vdisplay); > + return MODE_BAD; > +} > + > +static struct drm_encoder* > +virtio_gpu_best_encoder(struct drm_connector *connector) > +{ > + struct virtio_gpu_output *virtio_gpu_output = > + drm_connector_to_virtio_gpu_output(connector); > + > + return &virtio_gpu_output->enc; > +} > + > +static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = { > + .mode_fixup = virtio_gpu_enc_mode_fixup, > + .mode_set = virtio_gpu_enc_mode_set, > + .enable = virtio_gpu_enc_enable, > + .disable = virtio_gpu_enc_disable, > +}; > + > +static const struct drm_connector_helper_funcs virtio_gpu_conn_helper_funcs = { > + .get_modes = virtio_gpu_conn_get_modes, > + .mode_valid = virtio_gpu_conn_mode_valid, > + .best_encoder = virtio_gpu_best_encoder, > +}; > + > +static void virtio_gpu_conn_save(struct drm_connector *connector) > +{ > + DRM_DEBUG("\n"); > +} > + > +static void virtio_gpu_conn_restore(struct drm_connector *connector) > +{ > + DRM_DEBUG("\n"); > +} > + > +static enum drm_connector_status virtio_gpu_conn_detect( > + struct drm_connector *connector, > + bool force) > +{ > + struct virtio_gpu_output *output = > + drm_connector_to_virtio_gpu_output(connector); > + > + if (output->info.enabled) > + return connector_status_connected; > + else > + return connector_status_disconnected; > +} > + > +static void virtio_gpu_conn_destroy(struct drm_connector *connector) > +{ > + struct virtio_gpu_output *virtio_gpu_output = > + drm_connector_to_virtio_gpu_output(connector); > + > + drm_connector_unregister(connector); > + drm_connector_cleanup(connector); > + kfree(virtio_gpu_output); > +} > + > +static const struct drm_connector_funcs virtio_gpu_connector_funcs = { > + .dpms = drm_atomic_helper_connector_dpms, > + .save = virtio_gpu_conn_save, > + .restore = virtio_gpu_conn_restore, > + .detect = virtio_gpu_conn_detect, > + .fill_modes = drm_helper_probe_single_connector_modes, > + .destroy = virtio_gpu_conn_destroy, > + .reset = drm_atomic_helper_connector_reset, > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > +}; > + > +static const struct drm_encoder_funcs virtio_gpu_enc_funcs = { > + .destroy = drm_encoder_cleanup, > +}; > + > +static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index) > +{ > + struct drm_device *dev = vgdev->ddev; > + struct virtio_gpu_output *output = vgdev->outputs + index; > + struct drm_connector *connector = &output->conn; > + struct drm_encoder *encoder = &output->enc; > + struct drm_crtc *crtc = &output->crtc; > + struct drm_plane *plane; > + > + output->index = index; > + if (index == 0) { > + output->info.enabled = cpu_to_le32(true); > + output->info.r.width = cpu_to_le32(XRES_DEF); > + output->info.r.height = cpu_to_le32(YRES_DEF); > + } > + > + plane = virtio_gpu_plane_init(vgdev, index); > + if (IS_ERR(plane)) > + return PTR_ERR(plane); > + drm_crtc_init_with_planes(dev, crtc, plane, NULL, > + &virtio_gpu_crtc_funcs); > + drm_mode_crtc_set_gamma_size(crtc, 256); > + drm_crtc_helper_add(crtc, &virtio_gpu_crtc_helper_funcs); > + plane->crtc = crtc; > + > + drm_connector_init(dev, connector, &virtio_gpu_connector_funcs, > + DRM_MODE_CONNECTOR_VIRTUAL); > + drm_connector_helper_add(connector, &virtio_gpu_conn_helper_funcs); > + > + drm_encoder_init(dev, encoder, &virtio_gpu_enc_funcs, > + DRM_MODE_ENCODER_VIRTUAL); > + drm_encoder_helper_add(encoder, &virtio_gpu_enc_helper_funcs); > + encoder->possible_crtcs = 1 << index; > + > + drm_mode_connector_attach_encoder(connector, encoder); > + drm_connector_register(connector); > + return 0; > +} > + > +static struct drm_framebuffer * > +virtio_gpu_user_framebuffer_create(struct drm_device *dev, > + struct drm_file *file_priv, > + struct drm_mode_fb_cmd2 *mode_cmd) > +{ > + struct drm_gem_object *obj = NULL; > + struct virtio_gpu_framebuffer *virtio_gpu_fb; > + int ret; > + > + /* lookup object associated with res handle */ > + obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]); > + if (!obj) > + return ERR_PTR(-EINVAL); > + > + virtio_gpu_fb = kzalloc(sizeof(*virtio_gpu_fb), GFP_KERNEL); > + if (virtio_gpu_fb == NULL) > + return ERR_PTR(-ENOMEM); > + > + ret = virtio_gpu_framebuffer_init(dev, virtio_gpu_fb, mode_cmd, obj); > + if (ret) { > + kfree(virtio_gpu_fb); > + if (obj) > + drm_gem_object_unreference_unlocked(obj); > + return NULL; > + } > + > + return &virtio_gpu_fb->base; > +} > + > +static const struct drm_mode_config_funcs virtio_gpu_mode_funcs = { > + .fb_create = virtio_gpu_user_framebuffer_create, > + .atomic_check = drm_atomic_helper_check, > + .atomic_commit = drm_atomic_helper_commit, > +}; > + > +int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev) > +{ > + int i, ret; > + > + drm_mode_config_init(vgdev->ddev); > + vgdev->ddev->mode_config.funcs = (void *)&virtio_gpu_mode_funcs; > + > + /* modes will be validated against the framebuffer size */ > + vgdev->ddev->mode_config.min_width = XRES_MIN; > + vgdev->ddev->mode_config.min_height = YRES_MIN; > + vgdev->ddev->mode_config.max_width = XRES_MAX; > + vgdev->ddev->mode_config.max_height = YRES_MAX; > + > + for (i = 0 ; i < vgdev->num_scanouts; ++i) > + vgdev_output_init(vgdev, i); > + > + drm_mode_config_reset(vgdev->ddev); > + > + if (virtio_gpu_fbdev) { > + ret = virtio_gpu_fbdev_init(vgdev); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + > +void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev) > +{ > + virtio_gpu_fbdev_fini(vgdev); > + drm_mode_config_cleanup(vgdev->ddev); > +} > diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c > new file mode 100644 > index 0000000..f4ec816 > --- /dev/null > +++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c > @@ -0,0 +1,91 @@ > +/* > + * Copyright (C) 2015 Red Hat, Inc. > + * All Rights Reserved. > + * > + * Permission is hereby granted, free of charge, to any person obtaining > + * a copy of this software and associated documentation files (the > + * "Software"), to deal in the Software without restriction, including > + * without limitation the rights to use, copy, modify, merge, publish, > + * distribute, 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 (including the > + * next paragraph) shall be included in all copies or substantial > + * portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. > + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE > + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION > + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION > + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. > + */ > + > +#include <linux/pci.h> > + > +#include "virtgpu_drv.h" > + > +int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master) > +{ > + struct pci_dev *pdev = dev->pdev; > + > + if (pdev) { > + return drm_pci_set_busid(dev, master); > + } > + return 0; > +} > + > +int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev) > +{ > + struct drm_device *dev; > + int ret; > + > + dev = drm_dev_alloc(driver, &vdev->dev); > + if (!dev) > + return -ENOMEM; > + dev->virtdev = vdev; > + vdev->priv = dev; > + > + if (strcmp(vdev->dev.parent->bus->name, "pci") == 0) { > + struct pci_dev *pdev = to_pci_dev(vdev->dev.parent); > + bool vga = (pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA; > + > + if (vga) { > + /* > + * Need to make sure we don't have two drivers > + * for the same hardware here. Some day we > + * will simply kick out the firmware > + * (vesa/efi) framebuffer. > + * > + * Virtual hardware specs for virtio-vga are > + * not finalized yet, therefore we can't add > + * code for that yet. > + * > + * So ignore the device for the time being, > + * and suggest to the user use the device > + * variant without vga compatibility mode. > + */ > + DRM_ERROR("virtio-vga not (yet) supported\n"); > + DRM_ERROR("please use virtio-gpu-pci instead\n"); > + ret = -ENODEV; > + goto err_free; > + } > + dev->pdev = pdev; > + } > + > + ret = drm_dev_register(dev, 0); > + if (ret) > + goto err_free; > + > + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name, > + driver->major, driver->minor, driver->patchlevel, > + driver->date, dev->primary->index); > + > + return 0; > + > +err_free: > + drm_dev_unref(dev); > + return ret; > +} > diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c > new file mode 100644 > index 0000000..7d9610a > --- /dev/null > +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c > @@ -0,0 +1,136 @@ > +/* > + * Copyright (C) 2015 Red Hat, Inc. > + * All Rights Reserved. > + * > + * Authors: > + * Dave Airlie <airlied@redhat.com> > + * Gerd Hoffmann <kraxel@redhat.com> > + * > + * 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 (including the next > + * paragraph) shall be included in all copies or substantial portions of the > + * Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR > + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, > + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR > + * OTHER DEALINGS IN THE SOFTWARE. > + */ > + > +#include <linux/module.h> > +#include <linux/console.h> > +#include <linux/pci.h> > +#include "drmP.h" > +#include "drm/drm.h" > + > +#include "virtgpu_drv.h" > +static struct drm_driver driver; > + > +static int virtio_gpu_modeset = -1; > + > +MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); > +module_param_named(modeset, virtio_gpu_modeset, int, 0400); > + > +static int virtio_gpu_probe(struct virtio_device *vdev) > +{ > +#ifdef CONFIG_VGA_CONSOLE > + if (vgacon_text_force() && virtio_gpu_modeset == -1) > + return -EINVAL; > +#endif > + > + if (virtio_gpu_modeset == 0) > + return -EINVAL; > + > + return drm_virtio_init(&driver, vdev); > +} > + > +static void virtio_gpu_remove(struct virtio_device *vdev) > +{ > + struct drm_device *dev = vdev->priv; > + drm_put_dev(dev); > +} > + > +static void virtio_gpu_config_changed(struct virtio_device *vdev) > +{ > + struct drm_device *dev = vdev->priv; > + struct virtio_gpu_device *vgdev = dev->dev_private; > + > + schedule_work(&vgdev->config_changed_work); > +} > + > +static struct virtio_device_id id_table[] = { > + { VIRTIO_ID_GPU, VIRTIO_DEV_ANY_ID }, > + { 0 }, > +}; > + > +static unsigned int features[] = { > +}; > +static struct virtio_driver virtio_gpu_driver = { > + .feature_table = features, > + .feature_table_size = ARRAY_SIZE(features), > + .driver.name = KBUILD_MODNAME, > + .driver.owner = THIS_MODULE, > + .id_table = id_table, > + .probe = virtio_gpu_probe, > + .remove = virtio_gpu_remove, > + .config_changed = virtio_gpu_config_changed > +}; > + > +module_virtio_driver(virtio_gpu_driver); > + > +MODULE_DEVICE_TABLE(virtio, id_table); > +MODULE_DESCRIPTION("Virtio GPU driver"); > +MODULE_LICENSE("GPL and additional rights"); > +MODULE_AUTHOR("Dave Airlie <airlied@redhat.com>"); > +MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>"); > +MODULE_AUTHOR("Alon Levy"); > + > +static const struct file_operations virtio_gpu_driver_fops = { > + .owner = THIS_MODULE, > + .open = drm_open, > + .mmap = virtio_gpu_mmap, > + .poll = drm_poll, > + .read = drm_read, > + .unlocked_ioctl = drm_ioctl, > + .release = drm_release, > +#ifdef CONFIG_COMPAT > + .compat_ioctl = drm_compat_ioctl, > +#endif > + .llseek = noop_llseek, > +}; > + > + > +static struct drm_driver driver = { > + .driver_features = DRIVER_MODESET | DRIVER_GEM, > + .set_busid = drm_virtio_set_busid, > + .load = virtio_gpu_driver_load, > + .unload = virtio_gpu_driver_unload, > + > + .dumb_create = virtio_gpu_mode_dumb_create, > + .dumb_map_offset = virtio_gpu_mode_dumb_mmap, > + .dumb_destroy = virtio_gpu_mode_dumb_destroy, > + > +#if defined(CONFIG_DEBUG_FS) > + .debugfs_init = virtio_gpu_debugfs_init, > + .debugfs_cleanup = virtio_gpu_debugfs_takedown, > +#endif > + > + .gem_free_object = virtio_gpu_gem_free_object, > + .fops = &virtio_gpu_driver_fops, > + > + .name = DRIVER_NAME, > + .desc = DRIVER_DESC, > + .date = DRIVER_DATE, > + .major = DRIVER_MAJOR, > + .minor = DRIVER_MINOR, > + .patchlevel = DRIVER_PATCHLEVEL, > +}; > diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h > new file mode 100644 > index 0000000..e5a2c09 > --- /dev/null > +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h > @@ -0,0 +1,350 @@ > +/* > + * Copyright (C) 2015 Red Hat, Inc. > + * All Rights Reserved. > + * > + * Permission is hereby granted, free of charge, to any person obtaining > + * a copy of this software and associated documentation files (the > + * "Software"), to deal in the Software without restriction, including > + * without limitation the rights to use, copy, modify, merge, publish, > + * distribute, 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 (including the > + * next paragraph) shall be included in all copies or substantial > + * portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. > + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE > + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION > + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION > + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. > + */ > + > +#ifndef VIRTIO_DRV_H > +#define VIRTIO_DRV_H > + > +#include <linux/virtio.h> > +#include <linux/virtio_ids.h> > +#include <linux/virtio_config.h> > +#include <linux/virtio_gpu.h> > + > +#include <drm/drmP.h> > +#include <drm/drm_gem.h> > +#include <drm/drm_crtc_helper.h> > +#include <ttm/ttm_bo_api.h> > +#include <ttm/ttm_bo_driver.h> > +#include <ttm/ttm_placement.h> > +#include <ttm/ttm_module.h> > + > +#define DRIVER_NAME "virtio_gpu" > +#define DRIVER_DESC "virtio GPU" > +#define DRIVER_DATE "0" > + > +#define DRIVER_MAJOR 0 > +#define DRIVER_MINOR 0 > +#define DRIVER_PATCHLEVEL 1 > + > +/* virtgpu_drm_bus.c */ > +int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master); > +int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev); > + > +struct virtio_gpu_object { > + struct drm_gem_object gem_base; > + uint32_t hw_res_handle; > + > + struct sg_table *pages; > + void *vmap; > + bool dumb; > + struct ttm_place placement_code; > + struct ttm_placement placement; > + struct ttm_buffer_object tbo; > + struct ttm_bo_kmap_obj kmap; > +}; > +#define gem_to_virtio_gpu_obj(gobj) \ > + container_of((gobj), struct virtio_gpu_object, gem_base) > + > +struct virtio_gpu_vbuffer; > +struct virtio_gpu_device; > + > +typedef void (*virtio_gpu_resp_cb)(struct virtio_gpu_device *vgdev, > + struct virtio_gpu_vbuffer *vbuf); > + > +struct virtio_gpu_fence_driver { > + atomic64_t last_seq; > + uint64_t sync_seq; > + struct list_head fences; > + spinlock_t lock; > +}; > + > +struct virtio_gpu_fence { > + struct fence f; > + struct virtio_gpu_fence_driver *drv; > + struct list_head node; > + uint64_t seq; > +}; > +#define to_virtio_fence(x) \ > + container_of(x, struct virtio_gpu_fence, f) > + > +struct virtio_gpu_vbuffer { > + char *buf; > + int size; > + > + void *data_buf; > + uint32_t data_size; > + > + char *resp_buf; > + int resp_size; > + > + virtio_gpu_resp_cb resp_cb; > + > + struct list_head list; > +}; > + > +struct virtio_gpu_output { > + int index; > + struct drm_crtc crtc; > + struct drm_connector conn; > + struct drm_encoder enc; > + struct virtio_gpu_display_one info; > + struct virtio_gpu_update_cursor cursor; > + int cur_x; > + int cur_y; > +}; > +#define drm_crtc_to_virtio_gpu_output(x) \ > + container_of(x, struct virtio_gpu_output, crtc) > +#define drm_connector_to_virtio_gpu_output(x) \ > + container_of(x, struct virtio_gpu_output, conn) > +#define drm_encoder_to_virtio_gpu_output(x) \ > + container_of(x, struct virtio_gpu_output, enc) > + > +struct virtio_gpu_framebuffer { > + struct drm_framebuffer base; > + struct drm_gem_object *obj; > + int x1, y1, x2, y2; /* dirty rect */ > + spinlock_t dirty_lock; > + uint32_t hw_res_handle; > +}; > +#define to_virtio_gpu_framebuffer(x) \ > + container_of(x, struct virtio_gpu_framebuffer, base) > + > +struct virtio_gpu_mman { > + struct ttm_bo_global_ref bo_global_ref; > + struct drm_global_reference mem_global_ref; > + bool mem_global_referenced; > + struct ttm_bo_device bdev; > +}; > + > +struct virtio_gpu_fbdev; > + > +struct virtio_gpu_queue { > + struct virtqueue *vq; > + spinlock_t qlock; > + wait_queue_head_t ack_queue; > + struct work_struct dequeue_work; > +}; > + > +struct virtio_gpu_device { > + struct device *dev; > + struct drm_device *ddev; > + > + struct virtio_device *vdev; > + > + struct virtio_gpu_mman mman; > + > + /* pointer to fbdev info structure */ > + struct virtio_gpu_fbdev *vgfbdev; > + struct virtio_gpu_output outputs[VIRTIO_GPU_MAX_SCANOUTS]; > + uint32_t num_scanouts; > + > + struct virtio_gpu_queue ctrlq; > + struct virtio_gpu_queue cursorq; > + struct list_head free_vbufs; > + void *vbufs; > + bool vqs_ready; > + > + struct idr resource_idr; > + spinlock_t resource_idr_lock; > + > + wait_queue_head_t resp_wq; > + /* current display info */ > + spinlock_t display_info_lock; > + > + struct virtio_gpu_fence_driver fence_drv; > + > + struct idr ctx_id_idr; > + spinlock_t ctx_id_idr_lock; > + > + struct work_struct config_changed_work; > +}; > + > +struct virtio_gpu_fpriv { > + uint32_t ctx_id; > +}; > + > +/* virtio_ioctl.c */ > +#define DRM_VIRTIO_NUM_IOCTLS 10 > +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); > + > +/* virtio_gem.c */ > +void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj); > +int virtio_gpu_gem_init(struct virtio_gpu_device *vgdev); > +void virtio_gpu_gem_fini(struct virtio_gpu_device *vgdev); > +int virtio_gpu_gem_create(struct drm_file *file, > + struct drm_device *dev, > + uint64_t size, > + struct drm_gem_object **obj_p, > + uint32_t *handle_p); > +struct virtio_gpu_object *virtio_gpu_alloc_object(struct drm_device *dev, > + size_t size, bool kernel, > + bool pinned); > +int virtio_gpu_mode_dumb_create(struct drm_file *file_priv, > + struct drm_device *dev, > + struct drm_mode_create_dumb *args); > +int virtio_gpu_mode_dumb_destroy(struct drm_file *file_priv, > + struct drm_device *dev, > + uint32_t handle); > +int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv, > + struct drm_device *dev, > + uint32_t handle, uint64_t *offset_p); > + > +/* virtio_fb */ > +#define VIRTIO_GPUFB_CONN_LIMIT 1 > +int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev); > +void virtio_gpu_fbdev_fini(struct virtio_gpu_device *vgdev); > +int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *qfb, > + struct drm_clip_rect *clips, > + unsigned num_clips); > +/* virtio vg */ > +int virtio_gpu_alloc_vbufs(struct virtio_gpu_device *vgdev); > +void virtio_gpu_free_vbufs(struct virtio_gpu_device *vgdev); > +void virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, > + uint32_t *resid); > +void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id); > +void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev, > + uint32_t resource_id, > + uint32_t format, > + uint32_t width, > + uint32_t height); > +void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev, > + uint32_t resource_id); > +void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev, > + uint32_t resource_id, uint64_t offset, > + __le32 width, __le32 height, > + __le32 x, __le32 y, > + struct virtio_gpu_fence **fence); > +void virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev, > + uint32_t resource_id, > + uint32_t x, uint32_t y, > + uint32_t width, uint32_t height); > +void virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev, > + uint32_t scanout_id, uint32_t resource_id, > + uint32_t width, uint32_t height, > + uint32_t x, uint32_t y); > +int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, > + struct virtio_gpu_object *obj, > + uint32_t resource_id, > + struct virtio_gpu_fence **fence); > +int virtio_gpu_attach_status_page(struct virtio_gpu_device *vgdev); > +int virtio_gpu_detach_status_page(struct virtio_gpu_device *vgdev); > +void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev, > + struct virtio_gpu_output *output); > +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); > +void virtio_gpu_ctrl_ack(struct virtqueue *vq); > +void virtio_gpu_cursor_ack(struct virtqueue *vq); > +void virtio_gpu_dequeue_ctrl_func(struct work_struct *work); > +void virtio_gpu_dequeue_cursor_func(struct work_struct *work); > + > +/* virtio_gpu_display.c */ > +int virtio_gpu_framebuffer_init(struct drm_device *dev, > + struct virtio_gpu_framebuffer *vgfb, > + struct drm_mode_fb_cmd2 *mode_cmd, > + struct drm_gem_object *obj); > +int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev); > +void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev); > + > +/* virtio_gpu_plane.c */ > +struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev, > + int index); > + > +/* virtio_gpu_ttm.c */ > +int virtio_gpu_ttm_init(struct virtio_gpu_device *vgdev); > +void virtio_gpu_ttm_fini(struct virtio_gpu_device *vgdev); > +int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma); > + > +/* virtio_gpu_fence.c */ > +int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev, > + struct virtio_gpu_ctrl_hdr *cmd_hdr, > + struct virtio_gpu_fence **fence); > +void virtio_gpu_fence_event_process(struct virtio_gpu_device *vdev, > + u64 last_seq); > + > +/* virtio_gpu_object */ > +int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, > + unsigned long size, bool kernel, bool pinned, > + struct virtio_gpu_object **bo_ptr); > +int virtio_gpu_object_kmap(struct virtio_gpu_object *bo, void **ptr); > +int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev, > + struct virtio_gpu_object *bo); > +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); > + > +static inline struct virtio_gpu_object* > +virtio_gpu_object_ref(struct virtio_gpu_object *bo) > +{ > + ttm_bo_reference(&bo->tbo); > + return bo; > +} > + > +static inline void virtio_gpu_object_unref(struct virtio_gpu_object **bo) > +{ > + struct ttm_buffer_object *tbo; > + > + if ((*bo) == NULL) > + return; > + tbo = &((*bo)->tbo); > + ttm_bo_unref(&tbo); > + if (tbo == NULL) > + *bo = NULL; > +} > + > +static inline u64 virtio_gpu_object_mmap_offset(struct virtio_gpu_object *bo) > +{ > + return drm_vma_node_offset_addr(&bo->tbo.vma_node); > +} > + > +static inline int virtio_gpu_object_reserve(struct virtio_gpu_object *bo, > + bool no_wait) > +{ > + int r; > + > + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL); > + if (unlikely(r != 0)) { > + if (r != -ERESTARTSYS) { > + struct virtio_gpu_device *qdev = > + bo->gem_base.dev->dev_private; > + dev_err(qdev->dev, "%p reserve failed\n", bo); > + } > + return r; > + } > + return 0; > +} > + > +static inline void virtio_gpu_object_unreserve(struct virtio_gpu_object *bo) > +{ > + ttm_bo_unreserve(&bo->tbo); > +} > + > +/* virgl debufs */ > +int virtio_gpu_debugfs_init(struct drm_minor *minor); > +void virtio_gpu_debugfs_takedown(struct drm_minor *minor); > + > +#endif > diff --git a/drivers/gpu/drm/virtio/virtgpu_fb.c b/drivers/gpu/drm/virtio/virtgpu_fb.c > new file mode 100644 > index 0000000..25bf333 > --- /dev/null > +++ b/drivers/gpu/drm/virtio/virtgpu_fb.c > @@ -0,0 +1,431 @@ > +/* > + * Copyright (C) 2015 Red Hat, Inc. > + * All Rights Reserved. > + * > + * Permission is hereby granted, free of charge, to any person obtaining > + * a copy of this software and associated documentation files (the > + * "Software"), to deal in the Software without restriction, including > + * without limitation the rights to use, copy, modify, merge, publish, > + * distribute, 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 (including the > + * next paragraph) shall be included in all copies or substantial > + * portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. > + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE > + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION > + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION > + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. > + */ > + > +#include <drm/drmP.h> > +#include <drm/drm_fb_helper.h> > +#include "virtgpu_drv.h" > + > +#define VIRTIO_GPU_FBCON_POLL_PERIOD (HZ / 60) > + > +struct virtio_gpu_fbdev { > + struct drm_fb_helper helper; > + struct virtio_gpu_framebuffer vgfb; > + struct list_head fbdev_list; > + struct virtio_gpu_device *vgdev; > + struct delayed_work work; > +}; > + > +static int virtio_gpu_dirty_update(struct virtio_gpu_framebuffer *fb, > + bool store, int x, int y, > + int width, int height) > +{ > + struct drm_device *dev = fb->base.dev; > + struct virtio_gpu_device *vgdev = dev->dev_private; > + bool store_for_later = false; > + int bpp = fb->base.bits_per_pixel / 8; > + int x2, y2; > + unsigned long flags; > + struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(fb->obj); > + > + if ((width <= 0) || > + (x + width > fb->base.width) || > + (y + height > fb->base.height)) { > + DRM_DEBUG("values out of range %dx%d+%d+%d, fb %dx%d\n", > + width, height, x, y, > + fb->base.width, fb->base.height); > + return -EINVAL; > + } > + > + /* > + * Can be called with pretty much any context (console output > + * path). If we are in atomic just store the dirty rect info > + * to send out the update later. > + * > + * Can't test inside spin lock. > + */ > + if (in_atomic() || store) > + store_for_later = true; > + > + x2 = x + width - 1; > + y2 = y + height - 1; > + > + spin_lock_irqsave(&fb->dirty_lock, flags); > + > + if (fb->y1 < y) > + y = fb->y1; > + if (fb->y2 > y2) > + y2 = fb->y2; > + if (fb->x1 < x) > + x = fb->x1; > + if (fb->x2 > x2) > + x2 = fb->x2; > + > + if (store_for_later) { > + fb->x1 = x; > + fb->x2 = x2; > + fb->y1 = y; > + fb->y2 = y2; > + spin_unlock_irqrestore(&fb->dirty_lock, flags); > + return 0; > + } > + > + fb->x1 = fb->y1 = INT_MAX; > + fb->x2 = fb->y2 = 0; > + > + spin_unlock_irqrestore(&fb->dirty_lock, flags); > + > + { > + uint32_t offset; > + uint32_t w = x2 - x + 1; > + uint32_t h = y2 - y + 1; > + > + offset = (y * fb->base.pitches[0]) + x * bpp; > + > + virtio_gpu_cmd_transfer_to_host_2d(vgdev, obj->hw_res_handle, > + offset, > + cpu_to_le32(w), > + cpu_to_le32(h), > + cpu_to_le32(x), > + cpu_to_le32(y), > + NULL); > + > + } > + virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle, > + x, y, x2 - x + 1, y2 - y + 1); > + return 0; > +} > + > +int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *vgfb, > + struct drm_clip_rect *clips, > + unsigned num_clips) > +{ > + struct virtio_gpu_device *vgdev = vgfb->base.dev->dev_private; > + struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(vgfb->obj); > + struct drm_clip_rect norect; > + struct drm_clip_rect *clips_ptr; > + int left, right, top, bottom; > + int i; > + int inc = 1; > + if (!num_clips) { > + num_clips = 1; > + clips = &norect; > + norect.x1 = norect.y1 = 0; > + norect.x2 = vgfb->base.width; > + norect.y2 = vgfb->base.height; > + } > + left = clips->x1; > + right = clips->x2; > + top = clips->y1; > + bottom = clips->y2; > + > + /* skip the first clip rect */ > + for (i = 1, clips_ptr = clips + inc; > + i < num_clips; i++, clips_ptr += inc) { > + left = min_t(int, left, (int)clips_ptr->x1); > + right = max_t(int, right, (int)clips_ptr->x2); > + top = min_t(int, top, (int)clips_ptr->y1); > + bottom = max_t(int, bottom, (int)clips_ptr->y2); > + } > + > + if (obj->dumb) > + return virtio_gpu_dirty_update(vgfb, false, left, top, > + right - left, bottom - top); > + > + virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle, > + left, top, right - left, bottom - top); > + return 0; > +} > + > +static void virtio_gpu_fb_dirty_work(struct work_struct *work) > +{ > + struct delayed_work *delayed_work = to_delayed_work(work); > + struct virtio_gpu_fbdev *vfbdev = > + container_of(delayed_work, struct virtio_gpu_fbdev, work); > + struct virtio_gpu_framebuffer *vgfb = &vfbdev->vgfb; > + > + virtio_gpu_dirty_update(&vfbdev->vgfb, false, vgfb->x1, vgfb->y1, > + vgfb->x2 - vgfb->x1, vgfb->y2 - vgfb->y1); > +} > + > +static void virtio_gpu_3d_fillrect(struct fb_info *info, > + const struct fb_fillrect *rect) > +{ > + struct virtio_gpu_fbdev *vfbdev = info->par; > + sys_fillrect(info, rect); > + virtio_gpu_dirty_update(&vfbdev->vgfb, true, rect->dx, rect->dy, > + rect->width, rect->height); > + schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD); > +} > + > +static void virtio_gpu_3d_copyarea(struct fb_info *info, > + const struct fb_copyarea *area) > +{ > + struct virtio_gpu_fbdev *vfbdev = info->par; > + sys_copyarea(info, area); > + virtio_gpu_dirty_update(&vfbdev->vgfb, true, area->dx, area->dy, > + area->width, area->height); > + schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD); > +} > + > +static void virtio_gpu_3d_imageblit(struct fb_info *info, > + const struct fb_image *image) > +{ > + struct virtio_gpu_fbdev *vfbdev = info->par; > + sys_imageblit(info, image); > + virtio_gpu_dirty_update(&vfbdev->vgfb, true, image->dx, image->dy, > + image->width, image->height); > + schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD); > +} > + > +static struct fb_ops virtio_gpufb_ops = { > + .owner = THIS_MODULE, > + .fb_check_var = drm_fb_helper_check_var, > + .fb_set_par = drm_fb_helper_set_par, /* TODO: copy vmwgfx */ > + .fb_fillrect = virtio_gpu_3d_fillrect, > + .fb_copyarea = virtio_gpu_3d_copyarea, > + .fb_imageblit = virtio_gpu_3d_imageblit, > + .fb_pan_display = drm_fb_helper_pan_display, > + .fb_blank = drm_fb_helper_blank, > + .fb_setcmap = drm_fb_helper_setcmap, > + .fb_debug_enter = drm_fb_helper_debug_enter, > + .fb_debug_leave = drm_fb_helper_debug_leave, > +}; > + > +static int virtio_gpu_vmap_fb(struct virtio_gpu_device *vgdev, > + struct virtio_gpu_object *obj) > +{ > + return virtio_gpu_object_kmap(obj, NULL); > +} > + > +static int virtio_gpufb_create(struct drm_fb_helper *helper, > + struct drm_fb_helper_surface_size *sizes) > +{ > + struct virtio_gpu_fbdev *vfbdev = > + container_of(helper, struct virtio_gpu_fbdev, helper); > + struct drm_device *dev = helper->dev; > + struct virtio_gpu_device *vgdev = dev->dev_private; > + struct fb_info *info; > + struct drm_framebuffer *fb; > + struct drm_mode_fb_cmd2 mode_cmd = {}; > + struct virtio_gpu_object *obj; > + struct device *device = vgdev->dev; > + uint32_t resid, format, size; > + int ret; > + > + mode_cmd.width = sizes->surface_width; > + mode_cmd.height = sizes->surface_height; > + mode_cmd.pitches[0] = mode_cmd.width * 4; > + mode_cmd.pixel_format = drm_mode_legacy_fb_format(32, 24); > + > + switch (mode_cmd.pixel_format) { > +#ifdef __BIG_ENDIAN > + case DRM_FORMAT_XRGB8888: > + format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM; > + break; > + case DRM_FORMAT_ARGB8888: > + format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM; > + break; > + case DRM_FORMAT_BGRX8888: > + format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; > + break; > + case DRM_FORMAT_BGRA8888: > + format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM; > + break; > + case DRM_FORMAT_RGBX8888: > + format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM; > + break; > + case DRM_FORMAT_RGBA8888: > + format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM; > + break; > + case DRM_FORMAT_XBGR8888: > + format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM; > + break; > + case DRM_FORMAT_ABGR8888: > + format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM; > + break; > +#else > + case DRM_FORMAT_XRGB8888: > + format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; > + break; > + case DRM_FORMAT_ARGB8888: > + format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM; > + break; > + case DRM_FORMAT_BGRX8888: > + format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM; > + break; > + case DRM_FORMAT_BGRA8888: > + format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM; > + break; > + case DRM_FORMAT_RGBX8888: > + format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM; > + break; > + case DRM_FORMAT_RGBA8888: > + format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM; > + break; > + case DRM_FORMAT_XBGR8888: > + format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM; > + break; > + case DRM_FORMAT_ABGR8888: > + format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM; > + break; > +#endif > + default: > + DRM_ERROR("failed to find virtio gpu format for %d\n", > + mode_cmd.pixel_format); > + return -EINVAL; > + } > + > + size = mode_cmd.pitches[0] * mode_cmd.height; > + obj = virtio_gpu_alloc_object(dev, size, false, true); > + if (!obj) > + return -ENOMEM; > + > + virtio_gpu_resource_id_get(vgdev, &resid); > + virtio_gpu_cmd_create_resource(vgdev, resid, format, > + mode_cmd.width, mode_cmd.height); > + > + ret = virtio_gpu_vmap_fb(vgdev, obj); > + if (ret) { > + DRM_ERROR("failed to vmap fb %d\n", ret); > + goto err_obj_vmap; > + } > + > + /* attach the object to the resource */ > + ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL); > + if (ret) > + goto err_obj_attach; > + > + info = framebuffer_alloc(0, device); > + if (!info) { > + ret = -ENOMEM; > + goto err_fb_alloc; > + } > + > + ret = fb_alloc_cmap(&info->cmap, 256, 0); > + if (ret) { > + ret = -ENOMEM; > + goto err_fb_alloc_cmap; > + } > + > + info->par = helper; > + > + ret = virtio_gpu_framebuffer_init(dev, &vfbdev->vgfb, > + &mode_cmd, &obj->gem_base); > + if (ret) > + goto err_fb_init; > + > + fb = &vfbdev->vgfb.base; > + > + vfbdev->helper.fb = fb; > + vfbdev->helper.fbdev = info; > + > + strcpy(info->fix.id, "virtiodrmfb"); > + info->flags = FBINFO_DEFAULT; > + info->fbops = &virtio_gpufb_ops; > + info->pixmap.flags = FB_PIXMAP_SYSTEM; > + > + info->screen_base = obj->vmap; > + info->screen_size = obj->gem_base.size; > + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); > + drm_fb_helper_fill_var(info, &vfbdev->helper, > + sizes->fb_width, sizes->fb_height); > + > + info->fix.mmio_start = 0; > + info->fix.mmio_len = 0; > + return 0; > + > +err_fb_init: > + fb_dealloc_cmap(&info->cmap); > +err_fb_alloc_cmap: > + framebuffer_release(info); > +err_fb_alloc: > + virtio_gpu_cmd_resource_inval_backing(vgdev, resid); > +err_obj_attach: > +err_obj_vmap: > + virtio_gpu_gem_free_object(&obj->gem_base); > + return ret; > +} > + > +static int virtio_gpu_fbdev_destroy(struct drm_device *dev, > + struct virtio_gpu_fbdev *vgfbdev) > +{ > + struct fb_info *info; > + struct virtio_gpu_framebuffer *vgfb = &vgfbdev->vgfb; > + > + if (vgfbdev->helper.fbdev) { > + info = vgfbdev->helper.fbdev; > + > + unregister_framebuffer(info); > + framebuffer_release(info); > + } > + if (vgfb->obj) > + vgfb->obj = NULL; > + drm_fb_helper_fini(&vgfbdev->helper); > + drm_framebuffer_cleanup(&vgfb->base); > + > + return 0; > +} > +static struct drm_fb_helper_funcs virtio_gpu_fb_helper_funcs = { > + .fb_probe = virtio_gpufb_create, > +}; > + > +int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev) > +{ > + struct virtio_gpu_fbdev *vgfbdev; > + int bpp_sel = 32; /* TODO: parameter from somewhere? */ > + int ret; > + > + vgfbdev = kzalloc(sizeof(struct virtio_gpu_fbdev), GFP_KERNEL); > + if (!vgfbdev) > + return -ENOMEM; > + > + vgfbdev->vgdev = vgdev; > + vgdev->vgfbdev = vgfbdev; > + INIT_DELAYED_WORK(&vgfbdev->work, virtio_gpu_fb_dirty_work); > + > + drm_fb_helper_prepare(vgdev->ddev, &vgfbdev->helper, > + &virtio_gpu_fb_helper_funcs); > + ret = drm_fb_helper_init(vgdev->ddev, &vgfbdev->helper, > + vgdev->num_scanouts, > + VIRTIO_GPUFB_CONN_LIMIT); > + if (ret) { > + kfree(vgfbdev); > + return ret; > + } > + > + drm_fb_helper_single_add_all_connectors(&vgfbdev->helper); > + drm_fb_helper_initial_config(&vgfbdev->helper, bpp_sel); > + return 0; > +} > + > +void virtio_gpu_fbdev_fini(struct virtio_gpu_device *vgdev) > +{ > + if (!vgdev->vgfbdev) > + return; > + > + virtio_gpu_fbdev_destroy(vgdev->ddev, vgdev->vgfbdev); > + kfree(vgdev->vgfbdev); > + vgdev->vgfbdev = NULL; > +} > diff --git a/drivers/gpu/drm/virtio/virtgpu_fence.c b/drivers/gpu/drm/virtio/virtgpu_fence.c > new file mode 100644 > index 0000000..1da6326 > --- /dev/null > +++ b/drivers/gpu/drm/virtio/virtgpu_fence.c > @@ -0,0 +1,119 @@ > +/* > + * Copyright (C) 2015 Red Hat, Inc. > + * All Rights Reserved. > + * > + * Permission is hereby granted, free of charge, to any person obtaining > + * a copy of this software and associated documentation files (the > + * "Software"), to deal in the Software without restriction, including > + * without limitation the rights to use, copy, modify, merge, publish, > + * distribute, 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 (including the > + * next paragraph) shall be included in all copies or substantial > + * portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. > + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE > + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION > + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION > + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. > + */ > + > +#include <drm/drmP.h> > +#include "virtgpu_drv.h" > + > +static const char *virtio_get_driver_name(struct fence *f) > +{ > + return "virtio_gpu"; > +} > + > +static const char *virtio_get_timeline_name(struct fence *f) > +{ > + return "controlq"; > +} > + > +static bool virtio_enable_signaling(struct fence *f) > +{ > + return true; > +} > + > +static bool virtio_signaled(struct fence *f) > +{ > + struct virtio_gpu_fence *fence = to_virtio_fence(f); > + > + if (atomic64_read(&fence->drv->last_seq) >= fence->seq) > + return true; > + return false; > +} > + > +static void virtio_fence_value_str(struct fence *f, char *str, int size) > +{ > + struct virtio_gpu_fence *fence = to_virtio_fence(f); > + > + snprintf(str, size, "%llu", fence->seq); > +} > + > +static void virtio_timeline_value_str(struct fence *f, char *str, int size) > +{ > + struct virtio_gpu_fence *fence = to_virtio_fence(f); > + > + snprintf(str, size, "%lu", atomic64_read(&fence->drv->last_seq)); > +} > + > +static const struct fence_ops virtio_fence_ops = { > + .get_driver_name = virtio_get_driver_name, > + .get_timeline_name = virtio_get_timeline_name, > + .enable_signaling = virtio_enable_signaling, > + .signaled = virtio_signaled, > + .wait = fence_default_wait, > + .fence_value_str = virtio_fence_value_str, > + .timeline_value_str = virtio_timeline_value_str, > +}; > + > +int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev, > + struct virtio_gpu_ctrl_hdr *cmd_hdr, > + struct virtio_gpu_fence **fence) > +{ > + struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv; > + unsigned long irq_flags; > + > + *fence = kmalloc(sizeof(struct virtio_gpu_fence), GFP_KERNEL); > + if ((*fence) == NULL) > + return -ENOMEM; > + > + spin_lock_irqsave(&drv->lock, irq_flags); > + (*fence)->drv = drv; > + (*fence)->seq = ++drv->sync_seq; > + fence_init(&(*fence)->f, &virtio_fence_ops, &drv->lock, > + 0, (*fence)->seq); > + fence_get(&(*fence)->f); > + list_add_tail(&(*fence)->node, &drv->fences); > + spin_unlock_irqrestore(&drv->lock, irq_flags); > + > + cmd_hdr->flags |= cpu_to_le32(VIRTIO_GPU_FLAG_FENCE); > + cmd_hdr->fence_id = cpu_to_le64((*fence)->seq); > + return 0; > +} > + > +void virtio_gpu_fence_event_process(struct virtio_gpu_device *vgdev, > + u64 last_seq) > +{ > + struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv; > + struct virtio_gpu_fence *fence, *tmp; > + unsigned long irq_flags; > + > + spin_lock_irqsave(&drv->lock, irq_flags); > + atomic64_set(&vgdev->fence_drv.last_seq, last_seq); > + list_for_each_entry_safe(fence, tmp, &drv->fences, node) { > + if (last_seq < fence->seq) > + continue; > + fence_signal_locked(&fence->f); > + list_del(&fence->node); > + fence_put(&fence->f); > + } > + spin_unlock_irqrestore(&drv->lock, irq_flags); > +} > diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c > new file mode 100644 > index 0000000..cfa0d27 > --- /dev/null > +++ b/drivers/gpu/drm/virtio/virtgpu_gem.c > @@ -0,0 +1,140 @@ > +/* > + * Copyright (C) 2015 Red Hat, Inc. > + * All Rights Reserved. > + * > + * Permission is hereby granted, free of charge, to any person obtaining > + * a copy of this software and associated documentation files (the > + * "Software"), to deal in the Software without restriction, including > + * without limitation the rights to use, copy, modify, merge, publish, > + * distribute, 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 (including the > + * next paragraph) shall be included in all copies or substantial > + * portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. > + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE > + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION > + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION > + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. > + */ > + > +#include <drm/drmP.h> > +#include "virtgpu_drv.h" > + > +void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj) > +{ > + struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(gem_obj); > + > + if (obj) > + virtio_gpu_object_unref(&obj); > +} > + > +struct virtio_gpu_object *virtio_gpu_alloc_object(struct drm_device *dev, > + size_t size, bool kernel, > + bool pinned) > +{ > + struct virtio_gpu_device *vgdev = dev->dev_private; > + struct virtio_gpu_object *obj; > + int ret; > + > + ret = virtio_gpu_object_create(vgdev, size, kernel, pinned, &obj); > + if (ret) > + return ERR_PTR(ret); > + > + return obj; > +} > + > +int virtio_gpu_gem_create(struct drm_file *file, > + struct drm_device *dev, > + uint64_t size, > + struct drm_gem_object **obj_p, > + uint32_t *handle_p) > +{ > + struct virtio_gpu_object *obj; > + int ret; > + u32 handle; > + > + obj = virtio_gpu_alloc_object(dev, size, false, false); > + if (IS_ERR(obj)) > + return PTR_ERR(obj); > + > + ret = drm_gem_handle_create(file, &obj->gem_base, &handle); > + if (ret) { > + drm_gem_object_release(&obj->gem_base); > + return ret; > + } > + > + *obj_p = &obj->gem_base; > + > + /* drop reference from allocate - handle holds it now */ > + drm_gem_object_unreference_unlocked(&obj->gem_base); > + > + *handle_p = handle; > + return 0; > +} > + > +int virtio_gpu_mode_dumb_create(struct drm_file *file_priv, > + struct drm_device *dev, > + struct drm_mode_create_dumb *args) > +{ > + struct virtio_gpu_device *vgdev = dev->dev_private; > + struct drm_gem_object *gobj; > + struct virtio_gpu_object *obj; > + int ret; > + uint32_t pitch; > + uint32_t resid; > + > + pitch = args->width * ((args->bpp + 1) / 8); > + args->size = pitch * args->height; > + args->size = ALIGN(args->size, PAGE_SIZE); > + > + ret = virtio_gpu_gem_create(file_priv, dev, args->size, &gobj, > + &args->handle); > + if (ret) > + goto fail; > + > + virtio_gpu_resource_id_get(vgdev, &resid); > + virtio_gpu_cmd_create_resource(vgdev, resid, > + 2, args->width, args->height); > + > + /* attach the object to the resource */ > + obj = gem_to_virtio_gpu_obj(gobj); > + ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL); > + if (ret) > + goto fail; > + > + obj->dumb = true; > + args->pitch = pitch; > + return ret; > + > +fail: > + return ret; > +} > + > +int virtio_gpu_mode_dumb_destroy(struct drm_file *file_priv, > + struct drm_device *dev, > + uint32_t handle) > +{ > + return drm_gem_handle_delete(file_priv, handle); > +} > + > +int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv, > + struct drm_device *dev, > + uint32_t handle, uint64_t *offset_p) > +{ > + struct drm_gem_object *gobj; > + struct virtio_gpu_object *obj; > + BUG_ON(!offset_p); > + gobj = drm_gem_object_lookup(dev, file_priv, handle); > + if (gobj == NULL) > + return -ENOENT; > + obj = gem_to_virtio_gpu_obj(gobj); > + *offset_p = virtio_gpu_object_mmap_offset(obj); > + drm_gem_object_unreference_unlocked(gobj); > + return 0; > +} > diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c > new file mode 100644 > index 0000000..e503ffb > --- /dev/null > +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c > @@ -0,0 +1,164 @@ > +/* > + * Copyright (C) 2015 Red Hat, Inc. > + * All Rights Reserved. > + * > + * Permission is hereby granted, free of charge, to any person obtaining > + * a copy of this software and associated documentation files (the > + * "Software"), to deal in the Software without restriction, including > + * without limitation the rights to use, copy, modify, merge, publish, > + * distribute, 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 (including the > + * next paragraph) shall be included in all copies or substantial > + * portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. > + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE > + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION > + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION > + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. > + */ > + > +#include <linux/virtio.h> > +#include <linux/virtio_config.h> > +#include <drm/drmP.h> > +#include "virtgpu_drv.h" > + > +static void virtio_gpu_config_changed_work_func(struct work_struct *work) > +{ > + struct virtio_gpu_device *vgdev = > + container_of(work, struct virtio_gpu_device, > + config_changed_work); > + u32 events_read, events_clear = 0; > + > + /* read the config space */ > + virtio_cread(vgdev->vdev, struct virtio_gpu_config, > + events_read, &events_read); > + if (events_read & VIRTIO_GPU_EVENT_DISPLAY) { > + virtio_gpu_cmd_get_display_info(vgdev); > + drm_helper_hpd_irq_event(vgdev->ddev); > + events_clear |= VIRTIO_GPU_EVENT_DISPLAY; > + } > + virtio_cwrite(vgdev->vdev, struct virtio_gpu_config, > + events_clear, &events_clear); > +} > + > +static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq, > + void (*work_func)(struct work_struct *work)) > +{ > + spin_lock_init(&vgvq->qlock); > + init_waitqueue_head(&vgvq->ack_queue); > + INIT_WORK(&vgvq->dequeue_work, work_func); > +} > + > +int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags) > +{ > + static vq_callback_t *callbacks[] = { > + virtio_gpu_ctrl_ack, virtio_gpu_cursor_ack > + }; > + static const char *names[] = { "control", "cursor" }; > + > + struct virtio_gpu_device *vgdev; > + /* this will expand later */ > + struct virtqueue *vqs[2]; > + u32 num_scanouts; > + int ret; > + > + if (!virtio_has_feature(dev->virtdev, VIRTIO_F_VERSION_1)) > + return -ENODEV; > + > + vgdev = kzalloc(sizeof(struct virtio_gpu_device), GFP_KERNEL); > + if (!vgdev) > + return -ENOMEM; > + > + vgdev->ddev = dev; > + dev->dev_private = vgdev; > + vgdev->vdev = dev->virtdev; > + vgdev->dev = dev->dev; > + > + spin_lock_init(&vgdev->display_info_lock); > + spin_lock_init(&vgdev->ctx_id_idr_lock); > + idr_init(&vgdev->ctx_id_idr); > + spin_lock_init(&vgdev->resource_idr_lock); > + idr_init(&vgdev->resource_idr); > + init_waitqueue_head(&vgdev->resp_wq); > + virtio_gpu_init_vq(&vgdev->ctrlq, virtio_gpu_dequeue_ctrl_func); > + virtio_gpu_init_vq(&vgdev->cursorq, virtio_gpu_dequeue_cursor_func); > + > + spin_lock_init(&vgdev->fence_drv.lock); > + INIT_LIST_HEAD(&vgdev->fence_drv.fences); > + INIT_WORK(&vgdev->config_changed_work, > + virtio_gpu_config_changed_work_func); > + > + ret = vgdev->vdev->config->find_vqs(vgdev->vdev, 2, vqs, > + callbacks, names); > + if (ret) { > + DRM_ERROR("failed to find virt queues\n"); > + goto err_vqs; > + } > + vgdev->ctrlq.vq = vqs[0]; > + vgdev->cursorq.vq = vqs[1]; > + ret = virtio_gpu_alloc_vbufs(vgdev); > + if (ret) { > + DRM_ERROR("failed to alloc vbufs\n"); > + goto err_vbufs; > + } > + > + ret = virtio_gpu_ttm_init(vgdev); > + if (ret) { > + DRM_ERROR("failed to init ttm %d\n", ret); > + goto err_ttm; > + } > + > + /* get display info */ > + virtio_cread(vgdev->vdev, struct virtio_gpu_config, > + num_scanouts, &num_scanouts); > + vgdev->num_scanouts = min_t(uint32_t, num_scanouts, > + VIRTIO_GPU_MAX_SCANOUTS); > + if (!vgdev->num_scanouts) { > + DRM_ERROR("num_scanouts is zero\n"); > + ret = -EINVAL; > + goto err_scanouts; > + } > + > + ret = virtio_gpu_modeset_init(vgdev); > + if (ret) > + goto err_modeset; > + > + virtio_device_ready(vgdev->vdev); > + vgdev->vqs_ready = true; > + virtio_gpu_cmd_get_display_info(vgdev); > + return 0; > + > +err_modeset: > +err_scanouts: > + virtio_gpu_ttm_fini(vgdev); > +err_ttm: > + virtio_gpu_free_vbufs(vgdev); > +err_vbufs: > + vgdev->vdev->config->del_vqs(vgdev->vdev); > +err_vqs: > + kfree(vgdev); > + return ret; > +} > + > +int virtio_gpu_driver_unload(struct drm_device *dev) > +{ > + struct virtio_gpu_device *vgdev = dev->dev_private; > + > + vgdev->vqs_ready = false; > + flush_work(&vgdev->ctrlq.dequeue_work); > + flush_work(&vgdev->cursorq.dequeue_work); > + flush_work(&vgdev->config_changed_work); > + vgdev->vdev->config->del_vqs(vgdev->vdev); > + > + virtio_gpu_modeset_fini(vgdev); > + virtio_gpu_ttm_fini(vgdev); > + virtio_gpu_free_vbufs(vgdev); > + kfree(vgdev); > + return 0; > +} > diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c > new file mode 100644 > index 0000000..2c624c7 > --- /dev/null > +++ b/drivers/gpu/drm/virtio/virtgpu_object.c > @@ -0,0 +1,170 @@ > +/* > + * Copyright (C) 2015 Red Hat, Inc. > + * All Rights Reserved. > + * > + * Permission is hereby granted, free of charge, to any person obtaining > + * a copy of this software and associated documentation files (the > + * "Software"), to deal in the Software without restriction, including > + * without limitation the rights to use, copy, modify, merge, publish, > + * distribute, 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 (including the > + * next paragraph) shall be included in all copies or substantial > + * portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. > + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE > + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION > + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION > + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. > + */ > + > +#include "virtgpu_drv.h" > + > +static void virtio_gpu_ttm_bo_destroy(struct ttm_buffer_object *tbo) > +{ > + struct virtio_gpu_object *bo; > + struct virtio_gpu_device *vgdev; > + > + bo = container_of(tbo, struct virtio_gpu_object, tbo); > + vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private; > + > + if (bo->hw_res_handle) > + virtio_gpu_cmd_unref_resource(vgdev, bo->hw_res_handle); > + if (bo->pages) > + virtio_gpu_object_free_sg_table(bo); > + drm_gem_object_release(&bo->gem_base); > + kfree(bo); > +} > + > +static void virtio_gpu_init_ttm_placement(struct virtio_gpu_object *vgbo, > + bool pinned) > +{ > + u32 c = 1; > + u32 pflag = pinned ? TTM_PL_FLAG_NO_EVICT : 0; > + > + vgbo->placement.placement = &vgbo->placement_code; > + vgbo->placement.busy_placement = &vgbo->placement_code; > + vgbo->placement_code.fpfn = 0; > + vgbo->placement_code.lpfn = 0; > + vgbo->placement_code.flags = > + TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT | pflag; > + vgbo->placement.num_placement = c; > + vgbo->placement.num_busy_placement = c; > + > +} > + > +int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, > + unsigned long size, bool kernel, bool pinned, > + struct virtio_gpu_object **bo_ptr) > +{ > + struct virtio_gpu_object *bo; > + enum ttm_bo_type type; > + size_t acc_size; > + int ret; > + > + if (kernel) > + type = ttm_bo_type_kernel; > + else > + type = ttm_bo_type_device; > + *bo_ptr = NULL; > + > + acc_size = ttm_bo_dma_acc_size(&vgdev->mman.bdev, size, > + sizeof(struct virtio_gpu_object)); > + > + bo = kzalloc(sizeof(struct virtio_gpu_object), GFP_KERNEL); > + if (bo == NULL) > + return -ENOMEM; > + size = roundup(size, PAGE_SIZE); > + ret = drm_gem_object_init(vgdev->ddev, &bo->gem_base, size); > + if (ret != 0) > + goto err_gem_init; > + 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); > + if (ret != 0) > + goto err_ttm_init; > + > + *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) > +{ > + bool is_iomem; > + int r; > + > + if (bo->vmap) { > + if (ptr) > + *ptr = bo->vmap; > + return 0; > + } > + r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->kmap); > + if (r) > + return r; > + bo->vmap = ttm_kmap_obj_virtual(&bo->kmap, &is_iomem); > + if (ptr) > + *ptr = bo->vmap; > + return 0; > +} > + > +int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev, > + struct virtio_gpu_object *bo) > +{ > + int ret; > + struct page **pages = bo->tbo.ttm->pages; > + int nr_pages = bo->tbo.num_pages; > + > + /* wtf swapping */ > + if (bo->pages) > + return 0; > + > + if (bo->tbo.ttm->state == tt_unpopulated) > + bo->tbo.ttm->bdev->driver->ttm_tt_populate(bo->tbo.ttm); > + bo->pages = kmalloc(sizeof(struct sg_table), GFP_KERNEL); > + if (!bo->pages) > + goto out; > + > + ret = sg_alloc_table_from_pages(bo->pages, pages, nr_pages, 0, > + nr_pages << PAGE_SHIFT, GFP_KERNEL); > + if (ret) > + goto out; > + return 0; > +out: > + kfree(bo->pages); > + bo->pages = NULL; > + return -ENOMEM; > +} > + > +void virtio_gpu_object_free_sg_table(struct virtio_gpu_object *bo) > +{ > + sg_free_table(bo->pages); > + kfree(bo->pages); > + bo->pages = NULL; > +} > + > +int virtio_gpu_object_wait(struct virtio_gpu_object *bo, bool no_wait) > +{ > + int r; > + > + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL); > + if (unlikely(r != 0)) > + return r; > + r = ttm_bo_wait(&bo->tbo, true, true, no_wait); > + ttm_bo_unreserve(&bo->tbo); > + return r; > +} > + > diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c > new file mode 100644 > index 0000000..4a74129 > --- /dev/null > +++ b/drivers/gpu/drm/virtio/virtgpu_plane.c > @@ -0,0 +1,120 @@ > +/* > + * Copyright (C) 2015 Red Hat, Inc. > + * All Rights Reserved. > + * > + * Permission is hereby granted, free of charge, to any person obtaining > + * a copy of this software and associated documentation files (the > + * "Software"), to deal in the Software without restriction, including > + * without limitation the rights to use, copy, modify, merge, publish, > + * distribute, 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 (including the > + * next paragraph) shall be included in all copies or substantial > + * portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. > + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE > + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION > + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION > + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. > + */ > + > +#include "virtgpu_drv.h" > +#include <drm/drm_plane_helper.h> > +#include <drm/drm_atomic_helper.h> > + > +static const uint32_t virtio_gpu_formats[] = { > + DRM_FORMAT_XRGB8888, > + DRM_FORMAT_ARGB8888, > + DRM_FORMAT_BGRX8888, > + DRM_FORMAT_BGRA8888, > + DRM_FORMAT_RGBX8888, > + DRM_FORMAT_RGBA8888, > + DRM_FORMAT_XBGR8888, > + DRM_FORMAT_ABGR8888, > +}; > + > +static void virtio_gpu_plane_destroy(struct drm_plane *plane) > +{ > + kfree(plane); > +} > + > +static const struct drm_plane_funcs virtio_gpu_plane_funcs = { > + .update_plane = drm_atomic_helper_update_plane, > + .disable_plane = drm_atomic_helper_disable_plane, > + .destroy = virtio_gpu_plane_destroy, > + .reset = drm_atomic_helper_plane_reset, > + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, > +}; > + > +static int virtio_gpu_plane_atomic_check(struct drm_plane *plane, > + struct drm_plane_state *state) > +{ > + return 0; > +} > + > +static void virtio_gpu_plane_atomic_update(struct drm_plane *plane, > + struct drm_plane_state *old_state) > +{ > + struct drm_device *dev = plane->dev; > + struct virtio_gpu_device *vgdev = dev->dev_private; > + struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(plane->crtc); > + struct virtio_gpu_framebuffer *vgfb; > + struct virtio_gpu_object *bo; > + uint32_t handle; > + > + if (plane->fb) { > + vgfb = to_virtio_gpu_framebuffer(plane->fb); > + bo = gem_to_virtio_gpu_obj(vgfb->obj); > + handle = bo->hw_res_handle; > + } else { > + handle = 0; > + } > + > + DRM_DEBUG("handle 0x%x, crtc %dx%d+%d+%d\n", handle, > + plane->state->crtc_w, plane->state->crtc_h, > + plane->state->crtc_x, plane->state->crtc_y); > + virtio_gpu_cmd_set_scanout(vgdev, output->index, handle, > + plane->state->crtc_w, > + plane->state->crtc_h, > + plane->state->crtc_x, > + plane->state->crtc_y); > +} > + > + > +static const struct drm_plane_helper_funcs virtio_gpu_plane_helper_funcs = { > + .atomic_check = virtio_gpu_plane_atomic_check, > + .atomic_update = virtio_gpu_plane_atomic_update, > +}; > + > +struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev, > + int index) > +{ > + struct drm_device *dev = vgdev->ddev; > + struct drm_plane *plane; > + int ret; > + > + plane = kzalloc(sizeof(*plane), GFP_KERNEL); > + if (!plane) > + return ERR_PTR(-ENOMEM); > + > + ret = drm_universal_plane_init(dev, plane, 1 << index, > + &virtio_gpu_plane_funcs, > + virtio_gpu_formats, > + ARRAY_SIZE(virtio_gpu_formats), > + DRM_PLANE_TYPE_PRIMARY); > + if (ret) > + goto err_plane_init; > + > + drm_plane_helper_add(plane, &virtio_gpu_plane_helper_funcs); > + return plane; > + > +err_plane_init: > + kfree(plane); > + return ERR_PTR(ret); > +} > diff --git a/drivers/gpu/drm/virtio/virtgpu_ttm.c b/drivers/gpu/drm/virtio/virtgpu_ttm.c > new file mode 100644 > index 0000000..e0e74c6 > --- /dev/null > +++ b/drivers/gpu/drm/virtio/virtgpu_ttm.c > @@ -0,0 +1,469 @@ > +/* > + * 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 <ttm/ttm_bo_api.h> > +#include <ttm/ttm_bo_driver.h> > +#include <ttm/ttm_placement.h> > +#include <ttm/ttm_page_alloc.h> > +#include <ttm/ttm_module.h> > +#include <drm/drmP.h> > +#include <drm/drm.h> > +#include "virtgpu_drv.h" > + > +#include <linux/delay.h> > + > +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) > + > +static struct > +virtio_gpu_device *virtio_gpu_get_vgdev(struct ttm_bo_device *bdev) > +{ > + struct virtio_gpu_mman *mman; > + struct virtio_gpu_device *vgdev; > + > + mman = container_of(bdev, struct virtio_gpu_mman, bdev); > + vgdev = container_of(mman, struct virtio_gpu_device, mman); > + return vgdev; > +} > + > +static int virtio_gpu_ttm_mem_global_init(struct drm_global_reference *ref) > +{ > + return ttm_mem_global_init(ref->object); > +} > + > +static void virtio_gpu_ttm_mem_global_release(struct drm_global_reference *ref) > +{ > + ttm_mem_global_release(ref->object); > +} > + > +static int virtio_gpu_ttm_global_init(struct virtio_gpu_device *vgdev) > +{ > + struct drm_global_reference *global_ref; > + int r; > + > + vgdev->mman.mem_global_referenced = false; > + global_ref = &vgdev->mman.mem_global_ref; > + global_ref->global_type = DRM_GLOBAL_TTM_MEM; > + global_ref->size = sizeof(struct ttm_mem_global); > + global_ref->init = &virtio_gpu_ttm_mem_global_init; > + global_ref->release = &virtio_gpu_ttm_mem_global_release; > + > + r = drm_global_item_ref(global_ref); > + if (r != 0) { > + DRM_ERROR("Failed setting up TTM memory accounting " > + "subsystem.\n"); > + return r; > + } > + > + vgdev->mman.bo_global_ref.mem_glob = > + vgdev->mman.mem_global_ref.object; > + global_ref = &vgdev->mman.bo_global_ref.ref; > + global_ref->global_type = DRM_GLOBAL_TTM_BO; > + global_ref->size = sizeof(struct ttm_bo_global); > + global_ref->init = &ttm_bo_global_init; > + global_ref->release = &ttm_bo_global_release; > + r = drm_global_item_ref(global_ref); > + if (r != 0) { > + DRM_ERROR("Failed setting up TTM BO subsystem.\n"); > + drm_global_item_unref(&vgdev->mman.mem_global_ref); > + return r; > + } > + > + vgdev->mman.mem_global_referenced = true; > + return 0; > +} > + > +static void virtio_gpu_ttm_global_fini(struct virtio_gpu_device *vgdev) > +{ > + if (vgdev->mman.mem_global_referenced) { > + drm_global_item_unref(&vgdev->mman.bo_global_ref.ref); > + drm_global_item_unref(&vgdev->mman.mem_global_ref); > + vgdev->mman.mem_global_referenced = false; > + } > +} > + > +#if 0 > +/* > + * Hmm, seems to not do anything useful. Leftover debug hack? > + * Something like printing pagefaults to kernel log? > + */ > +static struct vm_operations_struct virtio_gpu_ttm_vm_ops; > +static const struct vm_operations_struct *ttm_vm_ops; > + > +static int virtio_gpu_ttm_fault(struct vm_area_struct *vma, > + struct vm_fault *vmf) > +{ > + struct ttm_buffer_object *bo; > + struct virtio_gpu_device *vgdev; > + int r; > + > + bo = (struct ttm_buffer_object *)vma->vm_private_data; > + if (bo == NULL) > + return VM_FAULT_NOPAGE; > + vgdev = virtio_gpu_get_vgdev(bo->bdev); > + r = ttm_vm_ops->fault(vma, vmf); > + return r; > +} > +#endif > + > +int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma) > +{ > + struct drm_file *file_priv; > + struct virtio_gpu_device *vgdev; > + int r; > + > + file_priv = filp->private_data; > + vgdev = file_priv->minor->dev->dev_private; > + if (vgdev == NULL) { > + DRM_ERROR( > + "filp->private_data->minor->dev->dev_private == NULL\n"); > + return -EINVAL; > + } > + r = ttm_bo_mmap(filp, vma, &vgdev->mman.bdev); > +#if 0 > + if (unlikely(r != 0)) > + return r; > + if (unlikely(ttm_vm_ops == NULL)) { > + ttm_vm_ops = vma->vm_ops; > + virtio_gpu_ttm_vm_ops = *ttm_vm_ops; > + virtio_gpu_ttm_vm_ops.fault = &virtio_gpu_ttm_fault; > + } > + vma->vm_ops = &virtio_gpu_ttm_vm_ops; > + return 0; > +#else > + return r; > +#endif > +} > + > +static int virtio_gpu_invalidate_caches(struct ttm_bo_device *bdev, > + uint32_t flags) > +{ > + return 0; > +} > + > +static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, > + struct ttm_buffer_object *bo, > + const struct ttm_place *place, > + struct ttm_mem_reg *mem) > +{ > + mem->mm_node = (void *)1; > + return 0; > +} > + > +static void ttm_bo_man_put_node(struct ttm_mem_type_manager *man, > + struct ttm_mem_reg *mem) > +{ > + mem->mm_node = (void *)NULL; > + return; > +} > + > +static int ttm_bo_man_init(struct ttm_mem_type_manager *man, > + unsigned long p_size) > +{ > + return 0; > +} > + > +static int ttm_bo_man_takedown(struct ttm_mem_type_manager *man) > +{ > + return 0; > +} > + > +static void ttm_bo_man_debug(struct ttm_mem_type_manager *man, > + const char *prefix) > +{ > +} > + > +static const struct ttm_mem_type_manager_func virtio_gpu_bo_manager_func = { > + ttm_bo_man_init, > + ttm_bo_man_takedown, > + ttm_bo_man_get_node, > + ttm_bo_man_put_node, > + ttm_bo_man_debug > +}; > + > +static int virtio_gpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, > + struct ttm_mem_type_manager *man) > +{ > + struct virtio_gpu_device *vgdev; > + > + vgdev = virtio_gpu_get_vgdev(bdev); > + > + switch (type) { > + case TTM_PL_SYSTEM: > + /* System memory */ > + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; > + man->available_caching = TTM_PL_MASK_CACHING; > + man->default_caching = TTM_PL_FLAG_CACHED; > + break; > + case TTM_PL_TT: > + man->func = &virtio_gpu_bo_manager_func; > + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; > + man->available_caching = TTM_PL_MASK_CACHING; > + man->default_caching = TTM_PL_FLAG_CACHED; > + break; > + default: > + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); > + return -EINVAL; > + } > + return 0; > +} > + > +static void virtio_gpu_evict_flags(struct ttm_buffer_object *bo, > + struct ttm_placement *placement) > +{ > + static struct ttm_place placements = { > + .fpfn = 0, > + .lpfn = 0, > + .flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM, > + }; > + > + placement->placement = &placements; > + placement->busy_placement = &placements; > + placement->num_placement = 1; > + placement->num_busy_placement = 1; > + return; > +} > + > +static int virtio_gpu_verify_access(struct ttm_buffer_object *bo, > + struct file *filp) > +{ > + return 0; > +} > + > +static int virtio_gpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, > + struct ttm_mem_reg *mem) > +{ > + struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; > + > + mem->bus.addr = NULL; > + mem->bus.offset = 0; > + mem->bus.size = mem->num_pages << PAGE_SHIFT; > + mem->bus.base = 0; > + mem->bus.is_iomem = false; > + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) > + return -EINVAL; > + switch (mem->mem_type) { > + case TTM_PL_SYSTEM: > + case TTM_PL_TT: > + /* system memory */ > + return 0; > + default: > + return -EINVAL; > + } > + return 0; > +} > + > +static void virtio_gpu_ttm_io_mem_free(struct ttm_bo_device *bdev, > + struct ttm_mem_reg *mem) > +{ > +} > + > +/* > + * TTM backend functions. > + */ > +struct virtio_gpu_ttm_tt { > + struct ttm_dma_tt ttm; > + struct virtio_gpu_device *vgdev; > + u64 offset; > +}; > + > +static int virtio_gpu_ttm_backend_bind(struct ttm_tt *ttm, > + struct ttm_mem_reg *bo_mem) > +{ > + struct virtio_gpu_ttm_tt *gtt = (void *)ttm; > + > + gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT); > + if (!ttm->num_pages) > + WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n", > + ttm->num_pages, bo_mem, ttm); > + > + /* Not implemented */ > + return 0; > +} > + > +static int virtio_gpu_ttm_backend_unbind(struct ttm_tt *ttm) > +{ > + /* Not implemented */ > + return 0; > +} > + > +static void virtio_gpu_ttm_backend_destroy(struct ttm_tt *ttm) > +{ > + struct virtio_gpu_ttm_tt *gtt = (void *)ttm; > + > + ttm_dma_tt_fini(>t->ttm); > + kfree(gtt); > +} > + > +static struct ttm_backend_func virtio_gpu_backend_func = { > + .bind = &virtio_gpu_ttm_backend_bind, > + .unbind = &virtio_gpu_ttm_backend_unbind, > + .destroy = &virtio_gpu_ttm_backend_destroy, > +}; > + > +static int virtio_gpu_ttm_tt_populate(struct ttm_tt *ttm) > +{ > + if (ttm->state != tt_unpopulated) > + return 0; > + > + return ttm_pool_populate(ttm); > +} > + > +static void virtio_gpu_ttm_tt_unpopulate(struct ttm_tt *ttm) > +{ > + ttm_pool_unpopulate(ttm); > +} > + > +static struct ttm_tt *virtio_gpu_ttm_tt_create(struct ttm_bo_device *bdev, > + unsigned long size, > + uint32_t page_flags, > + struct page *dummy_read_page) > +{ > + struct virtio_gpu_device *vgdev; > + struct virtio_gpu_ttm_tt *gtt; > + > + vgdev = virtio_gpu_get_vgdev(bdev); > + gtt = kzalloc(sizeof(struct virtio_gpu_ttm_tt), GFP_KERNEL); > + if (gtt == NULL) > + return NULL; > + gtt->ttm.ttm.func = &virtio_gpu_backend_func; > + gtt->vgdev = vgdev; > + if (ttm_dma_tt_init(>t->ttm, bdev, size, page_flags, > + dummy_read_page)) { > + kfree(gtt); > + return NULL; > + } > + return >t->ttm.ttm; > +} > + > +static void virtio_gpu_move_null(struct ttm_buffer_object *bo, > + struct ttm_mem_reg *new_mem) > +{ > + struct ttm_mem_reg *old_mem = &bo->mem; > + > + BUG_ON(old_mem->mm_node != NULL); > + *old_mem = *new_mem; > + new_mem->mm_node = NULL; > +} > + > +static int virtio_gpu_bo_move(struct ttm_buffer_object *bo, > + bool evict, bool interruptible, > + bool no_wait_gpu, > + struct ttm_mem_reg *new_mem) > +{ > + virtio_gpu_move_null(bo, new_mem); > + return 0; > +} > + > +static void virtio_gpu_bo_move_notify(struct ttm_buffer_object *tbo, > + struct ttm_mem_reg *new_mem) > +{ > + struct virtio_gpu_object *bo; > + struct virtio_gpu_device *vgdev; > + > + bo = container_of(tbo, struct virtio_gpu_object, tbo); > + vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private; > + > + if (!new_mem || (new_mem->placement & TTM_PL_FLAG_SYSTEM)) { > + if (bo->hw_res_handle) > + virtio_gpu_cmd_resource_inval_backing(vgdev, > + bo->hw_res_handle); > + > + } else if (new_mem->placement & TTM_PL_FLAG_TT) { > + if (bo->hw_res_handle) { > + virtio_gpu_object_attach(vgdev, bo, bo->hw_res_handle, > + NULL); > + } > + } > +} > + > +static void virtio_gpu_bo_swap_notify(struct ttm_buffer_object *tbo) > +{ > + struct virtio_gpu_object *bo; > + struct virtio_gpu_device *vgdev; > + > + bo = container_of(tbo, struct virtio_gpu_object, tbo); > + vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private; > + > + if (bo->pages) > + virtio_gpu_object_free_sg_table(bo); > +} > + > +static struct ttm_bo_driver virtio_gpu_bo_driver = { > + .ttm_tt_create = &virtio_gpu_ttm_tt_create, > + .ttm_tt_populate = &virtio_gpu_ttm_tt_populate, > + .ttm_tt_unpopulate = &virtio_gpu_ttm_tt_unpopulate, > + .invalidate_caches = &virtio_gpu_invalidate_caches, > + .init_mem_type = &virtio_gpu_init_mem_type, > + .evict_flags = &virtio_gpu_evict_flags, > + .move = &virtio_gpu_bo_move, > + .verify_access = &virtio_gpu_verify_access, > + .io_mem_reserve = &virtio_gpu_ttm_io_mem_reserve, > + .io_mem_free = &virtio_gpu_ttm_io_mem_free, > + .move_notify = &virtio_gpu_bo_move_notify, > + .swap_notify = &virtio_gpu_bo_swap_notify, > +}; > + > +int virtio_gpu_ttm_init(struct virtio_gpu_device *vgdev) > +{ > + int r; > + > + r = virtio_gpu_ttm_global_init(vgdev); > + if (r) > + return r; > + /* No others user of address space so set it to 0 */ > + r = ttm_bo_device_init(&vgdev->mman.bdev, > + vgdev->mman.bo_global_ref.ref.object, > + &virtio_gpu_bo_driver, > + vgdev->ddev->anon_inode->i_mapping, > + DRM_FILE_PAGE_OFFSET, 0); > + if (r) { > + DRM_ERROR("failed initializing buffer object driver(%d).\n", r); > + goto err_dev_init; > + return r; > + } > + > + r = ttm_bo_init_mm(&vgdev->mman.bdev, TTM_PL_TT, 0); > + if (r) { > + DRM_ERROR("Failed initializing GTT heap.\n"); > + goto err_mm_init; > + return r; > + } > + return 0; > + > +err_mm_init: > + ttm_bo_device_release(&vgdev->mman.bdev); > +err_dev_init: > + virtio_gpu_ttm_global_fini(vgdev); > + return r; > +} > + > +void virtio_gpu_ttm_fini(struct virtio_gpu_device *vgdev) > +{ > + ttm_bo_device_release(&vgdev->mman.bdev); > + virtio_gpu_ttm_global_fini(vgdev); > + DRM_INFO("virtio_gpu: ttm finalized\n"); > +} > diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c > new file mode 100644 > index 0000000..8fa6513e > --- /dev/null > +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c > @@ -0,0 +1,614 @@ > +/* > + * Copyright (C) 2015 Red Hat, Inc. > + * All Rights Reserved. > + * > + * Authors: > + * Dave Airlie <airlied@redhat.com> > + * Gerd Hoffmann <kraxel@redhat.com> > + * > + * 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 (including the next > + * paragraph) shall be included in all copies or substantial portions of the > + * Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR > + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, > + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR > + * OTHER DEALINGS IN THE SOFTWARE. > + */ > + > +#include <drm/drmP.h> > +#include "virtgpu_drv.h" > +#include <linux/virtio.h> > +#include <linux/virtio_config.h> > +#include <linux/virtio_ring.h> > + > +#define MAX_INLINE_CMD_SIZE 96 > +#define MAX_INLINE_RESP_SIZE 24 > +#define VBUFFER_SIZE (sizeof(struct virtio_gpu_vbuffer) \ > + + MAX_INLINE_CMD_SIZE \ > + + MAX_INLINE_RESP_SIZE) > + > +void virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, > + uint32_t *resid) > +{ > + int handle; > + > + idr_preload(GFP_KERNEL); > + spin_lock(&vgdev->resource_idr_lock); > + handle = idr_alloc(&vgdev->resource_idr, NULL, 1, 0, GFP_NOWAIT); > + spin_unlock(&vgdev->resource_idr_lock); > + idr_preload_end(); > + *resid = handle; > +} > + > +void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id) > +{ > + spin_lock(&vgdev->resource_idr_lock); > + idr_remove(&vgdev->resource_idr, id); > + spin_unlock(&vgdev->resource_idr_lock); > +} > + > +void virtio_gpu_ctrl_ack(struct virtqueue *vq) > +{ > + struct drm_device *dev = vq->vdev->priv; > + struct virtio_gpu_device *vgdev = dev->dev_private; > + schedule_work(&vgdev->ctrlq.dequeue_work); > +} > + > +void virtio_gpu_cursor_ack(struct virtqueue *vq) > +{ > + struct drm_device *dev = vq->vdev->priv; > + struct virtio_gpu_device *vgdev = dev->dev_private; > + schedule_work(&vgdev->cursorq.dequeue_work); > +} > + > +int virtio_gpu_alloc_vbufs(struct virtio_gpu_device *vgdev) > +{ > + struct virtio_gpu_vbuffer *vbuf; > + int i, size, count = 0; > + void *ptr; > + > + INIT_LIST_HEAD(&vgdev->free_vbufs); > + count += virtqueue_get_vring_size(vgdev->ctrlq.vq); > + count += virtqueue_get_vring_size(vgdev->cursorq.vq); > + size = count * VBUFFER_SIZE; > + DRM_INFO("virtio vbuffers: %d bufs, %zdB each, %dkB total.\n", > + count, VBUFFER_SIZE, size / 1024); > + > + vgdev->vbufs = kzalloc(size, GFP_KERNEL); > + if (!vgdev->vbufs) > + return -ENOMEM; > + > + for (i = 0, ptr = vgdev->vbufs; > + i < count; > + i++, ptr += VBUFFER_SIZE) { > + vbuf = ptr; > + list_add(&vbuf->list, &vgdev->free_vbufs); > + } > + return 0; > +} > + > +void virtio_gpu_free_vbufs(struct virtio_gpu_device *vgdev) > +{ > + struct virtio_gpu_vbuffer *vbuf; > + int i, count = 0; > + > + count += virtqueue_get_vring_size(vgdev->ctrlq.vq); > + count += virtqueue_get_vring_size(vgdev->cursorq.vq); > + > + for (i = 0; i < count; i++) { > + if (WARN_ON(list_empty(&vgdev->free_vbufs))) > + return; > + vbuf = list_first_entry(&vgdev->free_vbufs, > + struct virtio_gpu_vbuffer, list); > + list_del(&vbuf->list); > + } > + kfree(vgdev->vbufs); > +} > + > +static struct virtio_gpu_vbuffer* > +virtio_gpu_get_vbuf(struct virtio_gpu_device *vgdev, > + int size, int resp_size, void *resp_buf, > + virtio_gpu_resp_cb resp_cb) > +{ > + struct virtio_gpu_vbuffer *vbuf; > + > + BUG_ON(list_empty(&vgdev->free_vbufs)); > + vbuf = list_first_entry(&vgdev->free_vbufs, > + struct virtio_gpu_vbuffer, list); > + list_del(&vbuf->list); > + memset(vbuf, 0, VBUFFER_SIZE); > + > + BUG_ON(size > MAX_INLINE_CMD_SIZE); > + vbuf->buf = (void *)vbuf + sizeof(*vbuf); > + vbuf->size = size; > + > + vbuf->resp_cb = resp_cb; > + vbuf->resp_size = resp_size; > + if (resp_size <= MAX_INLINE_RESP_SIZE) > + vbuf->resp_buf = (void *)vbuf->buf + size; > + else > + vbuf->resp_buf = resp_buf; > + BUG_ON(!vbuf->resp_buf); > + return vbuf; > +} > + > +static void *virtio_gpu_alloc_cmd(struct virtio_gpu_device *vgdev, > + struct virtio_gpu_vbuffer **vbuffer_p, > + int size) > +{ > + struct virtio_gpu_vbuffer *vbuf; > + > + vbuf = virtio_gpu_get_vbuf(vgdev, size, > + sizeof(struct virtio_gpu_ctrl_hdr), > + NULL, NULL); > + if (IS_ERR(vbuf)) { > + *vbuffer_p = NULL; > + return ERR_CAST(vbuf); > + } > + *vbuffer_p = vbuf; > + return vbuf->buf; > +} > + > +static struct virtio_gpu_update_cursor* > +virtio_gpu_alloc_cursor(struct virtio_gpu_device *vgdev, > + struct virtio_gpu_vbuffer **vbuffer_p) > +{ > + struct virtio_gpu_vbuffer *vbuf; > + > + vbuf = virtio_gpu_get_vbuf > + (vgdev, sizeof(struct virtio_gpu_update_cursor), > + 0, NULL, NULL); > + if (IS_ERR(vbuf)) { > + *vbuffer_p = NULL; > + return ERR_CAST(vbuf); > + } > + *vbuffer_p = vbuf; > + return (struct virtio_gpu_update_cursor *)vbuf->buf; > +} > + > +static void *virtio_gpu_alloc_cmd_resp(struct virtio_gpu_device *vgdev, > + virtio_gpu_resp_cb cb, > + struct virtio_gpu_vbuffer **vbuffer_p, > + int cmd_size, int resp_size, > + void *resp_buf) > +{ > + struct virtio_gpu_vbuffer *vbuf; > + > + vbuf = virtio_gpu_get_vbuf(vgdev, cmd_size, > + resp_size, resp_buf, cb); > + if (IS_ERR(vbuf)) { > + *vbuffer_p = NULL; > + return ERR_CAST(vbuf); > + } > + *vbuffer_p = vbuf; > + return (struct virtio_gpu_command *)vbuf->buf; > +} > + > +static void free_vbuf(struct virtio_gpu_device *vgdev, > + struct virtio_gpu_vbuffer *vbuf) > +{ > + if (vbuf->resp_size > MAX_INLINE_RESP_SIZE) > + kfree(vbuf->resp_buf); > + kfree(vbuf->data_buf); > + list_add(&vbuf->list, &vgdev->free_vbufs); > +} > + > +static void reclaim_vbufs(struct virtqueue *vq, struct list_head *reclaim_list) > +{ > + struct virtio_gpu_vbuffer *vbuf; > + unsigned int len; > + int freed = 0; > + > + while ((vbuf = virtqueue_get_buf(vq, &len))) { > + list_add_tail(&vbuf->list, reclaim_list); > + freed++; > + } > + if (freed == 0) > + DRM_DEBUG("Huh? zero vbufs reclaimed"); > +} > + > +void virtio_gpu_dequeue_ctrl_func(struct work_struct *work) > +{ > + struct virtio_gpu_device *vgdev = > + container_of(work, struct virtio_gpu_device, > + ctrlq.dequeue_work); > + struct list_head reclaim_list; > + struct virtio_gpu_vbuffer *entry, *tmp; > + struct virtio_gpu_ctrl_hdr *resp; > + u64 fence_id = 0; > + > + INIT_LIST_HEAD(&reclaim_list); > + spin_lock(&vgdev->ctrlq.qlock); > + do { > + virtqueue_disable_cb(vgdev->ctrlq.vq); > + reclaim_vbufs(vgdev->ctrlq.vq, &reclaim_list); > + > + } while (!virtqueue_enable_cb(vgdev->ctrlq.vq)); > + spin_unlock(&vgdev->ctrlq.qlock); > + > + list_for_each_entry_safe(entry, tmp, &reclaim_list, list) { > + resp = (struct virtio_gpu_ctrl_hdr *)entry->resp_buf; > + if (resp->type != cpu_to_le32(VIRTIO_GPU_RESP_OK_NODATA)) > + DRM_DEBUG("response 0x%x\n", le32_to_cpu(resp->type)); > + if (resp->flags & cpu_to_le32(VIRTIO_GPU_FLAG_FENCE)) { > + u64 f = le64_to_cpu(resp->fence_id); > + > + if (fence_id > f) { > + DRM_ERROR("%s: Oops: fence %llx -> %llx\n", > + __func__, fence_id, f); > + } else { > + fence_id = f; > + } > + } > + if (entry->resp_cb) > + entry->resp_cb(vgdev, entry); > + > + list_del(&entry->list); > + free_vbuf(vgdev, entry); > + } > + wake_up(&vgdev->ctrlq.ack_queue); > + > + if (fence_id) > + virtio_gpu_fence_event_process(vgdev, fence_id); > +} > + > +void virtio_gpu_dequeue_cursor_func(struct work_struct *work) > +{ > + struct virtio_gpu_device *vgdev = > + container_of(work, struct virtio_gpu_device, > + cursorq.dequeue_work); > + struct list_head reclaim_list; > + struct virtio_gpu_vbuffer *entry, *tmp; > + > + INIT_LIST_HEAD(&reclaim_list); > + spin_lock(&vgdev->cursorq.qlock); > + do { > + virtqueue_disable_cb(vgdev->cursorq.vq); > + reclaim_vbufs(vgdev->cursorq.vq, &reclaim_list); > + } while (!virtqueue_enable_cb(vgdev->cursorq.vq)); > + spin_unlock(&vgdev->cursorq.qlock); > + > + list_for_each_entry_safe(entry, tmp, &reclaim_list, list) { > + list_del(&entry->list); > + free_vbuf(vgdev, entry); > + } > + wake_up(&vgdev->cursorq.ack_queue); > +} > + > +static int virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev, > + struct virtio_gpu_vbuffer *vbuf) > +{ > + struct virtqueue *vq = vgdev->ctrlq.vq; > + struct scatterlist *sgs[3], vcmd, vout, vresp; > + int outcnt = 0, incnt = 0; > + int ret; > + > + if (!vgdev->vqs_ready) > + return -ENODEV; > + > + sg_init_one(&vcmd, vbuf->buf, vbuf->size); > + sgs[outcnt+incnt] = &vcmd; > + outcnt++; > + > + if (vbuf->data_size) { > + sg_init_one(&vout, vbuf->data_buf, vbuf->data_size); > + sgs[outcnt + incnt] = &vout; > + outcnt++; > + } > + > + if (vbuf->resp_size) { > + sg_init_one(&vresp, vbuf->resp_buf, vbuf->resp_size); > + sgs[outcnt + incnt] = &vresp; > + incnt++; > + } > + > + spin_lock(&vgdev->ctrlq.qlock); > +retry: > + ret = virtqueue_add_sgs(vq, sgs, outcnt, incnt, vbuf, GFP_ATOMIC); > + if (ret == -ENOSPC) { > + spin_unlock(&vgdev->ctrlq.qlock); > + wait_event(vgdev->ctrlq.ack_queue, vq->num_free); > + spin_lock(&vgdev->ctrlq.qlock); > + goto retry; > + } else { > + virtqueue_kick(vq); > + } > + spin_unlock(&vgdev->ctrlq.qlock); > + > + if (!ret) > + ret = vq->num_free; > + return ret; > +} > + > +static int virtio_gpu_queue_cursor(struct virtio_gpu_device *vgdev, > + struct virtio_gpu_vbuffer *vbuf) > +{ > + struct virtqueue *vq = vgdev->cursorq.vq; > + struct scatterlist *sgs[1], ccmd; > + int ret; > + int outcnt; > + > + if (!vgdev->vqs_ready) > + return -ENODEV; > + > + sg_init_one(&ccmd, vbuf->buf, vbuf->size); > + sgs[0] = &ccmd; > + outcnt = 1; > + > + spin_lock(&vgdev->cursorq.qlock); > +retry: > + ret = virtqueue_add_sgs(vq, sgs, outcnt, 0, vbuf, GFP_ATOMIC); > + if (ret == -ENOSPC) { > + spin_unlock(&vgdev->cursorq.qlock); > + wait_event(vgdev->cursorq.ack_queue, vq->num_free); > + spin_lock(&vgdev->cursorq.qlock); > + goto retry; > + } else { > + virtqueue_kick(vq); > + } > + > + spin_unlock(&vgdev->cursorq.qlock); > + > + if (!ret) > + ret = vq->num_free; > + return ret; > +} > + > +/* just create gem objects for userspace and long lived objects, > + just use dma_alloced pages for the queue objects? */ > + > +/* create a basic resource */ > +void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev, > + uint32_t resource_id, > + uint32_t format, > + uint32_t width, > + uint32_t height) > +{ > + struct virtio_gpu_resource_create_2d *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_RESOURCE_CREATE_2D); > + cmd_p->resource_id = cpu_to_le32(resource_id); > + cmd_p->format = cpu_to_le32(format); > + cmd_p->width = cpu_to_le32(width); > + cmd_p->height = cpu_to_le32(height); > + > + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); > +} > + > +void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev, > + uint32_t resource_id) > +{ > + struct virtio_gpu_resource_unref *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_RESOURCE_UNREF); > + cmd_p->resource_id = cpu_to_le32(resource_id); > + > + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); > +} > + > +void virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev, > + uint32_t resource_id) > +{ > + struct virtio_gpu_resource_detach_backing *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_RESOURCE_DETACH_BACKING); > + cmd_p->resource_id = cpu_to_le32(resource_id); > + > + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); > +} > + > +void virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev, > + uint32_t scanout_id, uint32_t resource_id, > + uint32_t width, uint32_t height, > + uint32_t x, uint32_t y) > +{ > + struct virtio_gpu_set_scanout *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_SET_SCANOUT); > + cmd_p->resource_id = cpu_to_le32(resource_id); > + cmd_p->scanout_id = cpu_to_le32(scanout_id); > + cmd_p->r.width = cpu_to_le32(width); > + cmd_p->r.height = cpu_to_le32(height); > + cmd_p->r.x = cpu_to_le32(x); > + cmd_p->r.y = cpu_to_le32(y); > + > + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); > +} > + > +void virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev, > + uint32_t resource_id, > + uint32_t x, uint32_t y, > + uint32_t width, uint32_t height) > +{ > + struct virtio_gpu_resource_flush *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_RESOURCE_FLUSH); > + cmd_p->resource_id = cpu_to_le32(resource_id); > + cmd_p->r.width = cpu_to_le32(width); > + cmd_p->r.height = cpu_to_le32(height); > + cmd_p->r.x = cpu_to_le32(x); > + cmd_p->r.y = cpu_to_le32(y); > + > + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); > +} > + > +void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev, > + uint32_t resource_id, uint64_t offset, > + __le32 width, __le32 height, > + __le32 x, __le32 y, > + struct virtio_gpu_fence **fence) > +{ > + struct virtio_gpu_transfer_to_host_2d *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_2D); > + cmd_p->resource_id = cpu_to_le32(resource_id); > + cmd_p->offset = cpu_to_le64(offset); > + cmd_p->r.width = width; > + cmd_p->r.height = height; > + 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); > +} > + > +static void > +virtio_gpu_cmd_resource_attach_backing(struct virtio_gpu_device *vgdev, > + uint32_t resource_id, > + struct virtio_gpu_mem_entry *ents, > + uint32_t nents, > + struct virtio_gpu_fence **fence) > +{ > + struct virtio_gpu_resource_attach_backing *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_RESOURCE_ATTACH_BACKING); > + cmd_p->resource_id = cpu_to_le32(resource_id); > + cmd_p->nr_entries = cpu_to_le32(nents); > + > + 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); > +} > + > +static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev, > + struct virtio_gpu_vbuffer *vbuf) > +{ > + struct virtio_gpu_resp_display_info *resp = > + (struct virtio_gpu_resp_display_info *)vbuf->resp_buf; > + int i; > + > + spin_lock(&vgdev->display_info_lock); > + for (i = 0; i < vgdev->num_scanouts; i++) { > + vgdev->outputs[i].info = resp->pmodes[i]; > + if (resp->pmodes[i].enabled) { > + DRM_DEBUG("output %d: %dx%d+%d+%d", i, > + le32_to_cpu(resp->pmodes[i].r.width), > + le32_to_cpu(resp->pmodes[i].r.height), > + le32_to_cpu(resp->pmodes[i].r.x), > + le32_to_cpu(resp->pmodes[i].r.y)); > + } else { > + DRM_DEBUG("output %d: disabled", i); > + } > + } > + > + spin_unlock(&vgdev->display_info_lock); > + wake_up(&vgdev->resp_wq); > + > + if (!drm_helper_hpd_irq_event(vgdev->ddev)) > + drm_kms_helper_hotplug_event(vgdev->ddev); > +} > + > +int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev) > +{ > + struct virtio_gpu_ctrl_hdr *cmd_p; > + struct virtio_gpu_vbuffer *vbuf; > + void *resp_buf; > + > + resp_buf = kzalloc(sizeof(struct virtio_gpu_resp_display_info), > + GFP_KERNEL); > + if (!resp_buf) > + return -ENOMEM; > + > + cmd_p = virtio_gpu_alloc_cmd_resp > + (vgdev, &virtio_gpu_cmd_get_display_info_cb, &vbuf, > + sizeof(*cmd_p), sizeof(struct virtio_gpu_resp_display_info), > + resp_buf); > + memset(cmd_p, 0, sizeof(*cmd_p)); > + > + cmd_p->type = cpu_to_le32(VIRTIO_GPU_CMD_GET_DISPLAY_INFO); > + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); > + return 0; > +} > + > +int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, > + struct virtio_gpu_object *obj, > + uint32_t resource_id, > + struct virtio_gpu_fence **fence) > +{ > + struct virtio_gpu_mem_entry *ents; > + struct scatterlist *sg; > + int si; > + > + if (!obj->pages) { > + int ret; > + ret = virtio_gpu_object_get_sg_table(vgdev, obj); > + if (ret) > + return ret; > + } > + > + /* gets freed when the ring has consumed it */ > + ents = kmalloc_array(obj->pages->nents, > + sizeof(struct virtio_gpu_mem_entry), > + GFP_KERNEL); > + if (!ents) { > + DRM_ERROR("failed to allocate ent list\n"); > + return -ENOMEM; > + } > + > + for_each_sg(obj->pages->sgl, sg, obj->pages->nents, si) { > + ents[si].addr = cpu_to_le64(sg_phys(sg)); > + ents[si].length = cpu_to_le32(sg->length); > + ents[si].padding = 0; > + } > + > + virtio_gpu_cmd_resource_attach_backing(vgdev, resource_id, > + ents, obj->pages->nents, > + fence); > + obj->hw_res_handle = resource_id; > + return 0; > +} > + > +void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev, > + struct virtio_gpu_output *output) > +{ > + struct virtio_gpu_vbuffer *vbuf; > + struct virtio_gpu_update_cursor *cur_p; > + > + output->cursor.pos.scanout_id = cpu_to_le32(output->index); > + cur_p = virtio_gpu_alloc_cursor(vgdev, &vbuf); > + memcpy(cur_p, &output->cursor, sizeof(output->cursor)); > + virtio_gpu_queue_cursor(vgdev, vbuf); > +} > diff --git a/include/drm/drmP.h b/include/drm/drmP.h > index 62c40777..1544bb1 100644 > --- a/include/drm/drmP.h > +++ b/include/drm/drmP.h > @@ -812,6 +812,7 @@ struct drm_device { > #endif > > struct platform_device *platformdev; /**< Platform device struture */ > + struct virtio_device *virtdev; > > struct drm_sg_mem *sg; /**< Scatter gather memory */ > unsigned int num_crtcs; /**< Number of CRTCs on this device */ > diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild > index 1a0006a..4460e58 100644 > --- a/include/uapi/linux/Kbuild > +++ b/include/uapi/linux/Kbuild > @@ -430,6 +430,7 @@ header-y += virtio_balloon.h > header-y += virtio_blk.h > header-y += virtio_config.h > header-y += virtio_console.h > +header-y += virtio_gpu.h > header-y += virtio_ids.h > header-y += virtio_input.h > header-y += virtio_net.h > diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h > new file mode 100644 > index 0000000..571c4cb > --- /dev/null > +++ b/include/uapi/linux/virtio_gpu.h > @@ -0,0 +1,204 @@ > +/* > + * Virtio GPU Device > + * > + * Copyright Red Hat, Inc. 2013-2014 > + * > + * Authors: > + * Dave Airlie <airlied@redhat.com> > + * Gerd Hoffmann <kraxel@redhat.com> > + * > + * This header is BSD licensed so anyone can use the definitions > + * to implement compatible drivers/servers: > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * 2. Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in the > + * documentation and/or other materials provided with the distribution. > + * 3. Neither the name of IBM nor the names of its contributors > + * may be used to endorse or promote products derived from this software > + * without specific prior written permission. > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS > + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR > + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF > + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND > + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, > + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT > + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > + * SUCH DAMAGE. > + */ > + > +#ifndef VIRTIO_GPU_HW_H > +#define VIRTIO_GPU_HW_H > + > +enum virtio_gpu_ctrl_type { > + VIRTIO_GPU_UNDEFINED = 0, > + > + /* 2d commands */ > + VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100, > + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, > + VIRTIO_GPU_CMD_RESOURCE_UNREF, > + VIRTIO_GPU_CMD_SET_SCANOUT, > + VIRTIO_GPU_CMD_RESOURCE_FLUSH, > + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, > + VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, > + VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, > + > + /* cursor commands */ > + VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300, > + VIRTIO_GPU_CMD_MOVE_CURSOR, > + > + /* success responses */ > + VIRTIO_GPU_RESP_OK_NODATA = 0x1100, > + VIRTIO_GPU_RESP_OK_DISPLAY_INFO, > + > + /* error responses */ > + VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200, > + VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, > + VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, > + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, > + VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID, > + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, > +}; > + > +#define VIRTIO_GPU_FLAG_FENCE (1 << 0) > + > +struct virtio_gpu_ctrl_hdr { > + __le32 type; > + __le32 flags; > + __le64 fence_id; > + __le32 ctx_id; > + __le32 padding; > +}; > + > +/* data passed in the cursor vq */ > + > +struct virtio_gpu_cursor_pos { > + __le32 scanout_id; > + __le32 x; > + __le32 y; > + __le32 padding; > +}; > + > +/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */ > +struct virtio_gpu_update_cursor { > + struct virtio_gpu_ctrl_hdr hdr; > + struct virtio_gpu_cursor_pos pos; /* update & move */ > + __le32 resource_id; /* update only */ > + __le32 hot_x; /* update only */ > + __le32 hot_y; /* update only */ > + __le32 padding; > +}; > + > +/* data passed in the control vq, 2d related */ > + > +struct virtio_gpu_rect { > + __le32 x; > + __le32 y; > + __le32 width; > + __le32 height; > +}; > + > +/* VIRTIO_GPU_CMD_RESOURCE_UNREF */ > +struct virtio_gpu_resource_unref { > + struct virtio_gpu_ctrl_hdr hdr; > + __le32 resource_id; > + __le32 padding; > +}; > + > +/* VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: create a 2d resource with a format */ > +struct virtio_gpu_resource_create_2d { > + struct virtio_gpu_ctrl_hdr hdr; > + __le32 resource_id; > + __le32 format; > + __le32 width; > + __le32 height; > +}; > + > +/* VIRTIO_GPU_CMD_SET_SCANOUT */ > +struct virtio_gpu_set_scanout { > + struct virtio_gpu_ctrl_hdr hdr; > + struct virtio_gpu_rect r; > + __le32 scanout_id; > + __le32 resource_id; > +}; > + > +/* VIRTIO_GPU_CMD_RESOURCE_FLUSH */ > +struct virtio_gpu_resource_flush { > + struct virtio_gpu_ctrl_hdr hdr; > + struct virtio_gpu_rect r; > + __le32 resource_id; > + __le32 padding; > +}; > + > +/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: simple transfer to_host */ > +struct virtio_gpu_transfer_to_host_2d { > + struct virtio_gpu_ctrl_hdr hdr; > + struct virtio_gpu_rect r; > + __le64 offset; > + __le32 resource_id; > + __le32 padding; > +}; > + > +struct virtio_gpu_mem_entry { > + __le64 addr; > + __le32 length; > + __le32 padding; > +}; > + > +/* VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING */ > +struct virtio_gpu_resource_attach_backing { > + struct virtio_gpu_ctrl_hdr hdr; > + __le32 resource_id; > + __le32 nr_entries; > +}; > + > +/* VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING */ > +struct virtio_gpu_resource_detach_backing { > + struct virtio_gpu_ctrl_hdr hdr; > + __le32 resource_id; > + __le32 padding; > +}; > + > +/* VIRTIO_GPU_RESP_OK_DISPLAY_INFO */ > +#define VIRTIO_GPU_MAX_SCANOUTS 16 > +struct virtio_gpu_resp_display_info { > + struct virtio_gpu_ctrl_hdr hdr; > + struct virtio_gpu_display_one { > + struct virtio_gpu_rect r; > + __le32 enabled; > + __le32 flags; > + } pmodes[VIRTIO_GPU_MAX_SCANOUTS]; > +}; > + > +#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0) > + > +struct virtio_gpu_config { > + __u32 events_read; > + __u32 events_clear; > + __u32 num_scanouts; > + __u32 reserved; > +}; > + > +/* simple formats for fbcon/X use */ > +enum virtio_gpu_formats { > + VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM = 1, > + VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM = 2, > + VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM = 3, > + VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM = 4, > + > + VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM = 67, > + VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM = 68, > + > + VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM = 121, > + VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM = 134, > +}; > + > +#endif > diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h > index 5f60aa4..77925f5 100644 > --- a/include/uapi/linux/virtio_ids.h > +++ b/include/uapi/linux/virtio_ids.h > @@ -39,6 +39,7 @@ > #define VIRTIO_ID_9P 9 /* 9p virtio console */ > #define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */ > #define VIRTIO_ID_CAIF 12 /* Virtio caif */ > +#define VIRTIO_ID_GPU 16 /* virtio GPU */ > #define VIRTIO_ID_INPUT 18 /* virtio input */ > > #endif /* _LINUX_VIRTIO_IDS_H */ > -- > 1.8.3.1
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 47f2ce8..d4b6545 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -206,6 +206,8 @@ source "drivers/gpu/drm/qxl/Kconfig" source "drivers/gpu/drm/bochs/Kconfig" +source "drivers/gpu/drm/virtio/Kconfig" + source "drivers/gpu/drm/msm/Kconfig" source "drivers/gpu/drm/tegra/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 7d4944e..dbbc101 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_DRM_OMAP) += omapdrm/ obj-$(CONFIG_DRM_TILCDC) += tilcdc/ obj-$(CONFIG_DRM_QXL) += qxl/ obj-$(CONFIG_DRM_BOCHS) += bochs/ +obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/ obj-$(CONFIG_DRM_MSM) += msm/ obj-$(CONFIG_DRM_TEGRA) += tegra/ obj-$(CONFIG_DRM_STI) += sti/ diff --git a/drivers/gpu/drm/virtio/Kconfig b/drivers/gpu/drm/virtio/Kconfig new file mode 100644 index 0000000..9983ead --- /dev/null +++ b/drivers/gpu/drm/virtio/Kconfig @@ -0,0 +1,14 @@ +config DRM_VIRTIO_GPU + tristate "Virtio GPU driver" + depends on DRM && VIRTIO + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select DRM_KMS_HELPER + select DRM_KMS_FB_HELPER + select DRM_TTM + help + This is the virtual GPU driver for virtio. It can be used with + QEMU based VMMs (like KVM or Xen). + + If unsure say M. diff --git a/drivers/gpu/drm/virtio/Makefile b/drivers/gpu/drm/virtio/Makefile new file mode 100644 index 0000000..2ee1602 --- /dev/null +++ b/drivers/gpu/drm/virtio/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + +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 + +obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio-gpu.o diff --git a/drivers/gpu/drm/virtio/virtgpu_debugfs.c b/drivers/gpu/drm/virtio/virtgpu_debugfs.c new file mode 100644 index 0000000..db8b491 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_debugfs.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <linux/debugfs.h> + +#include "drmP.h" +#include "virtgpu_drv.h" + +static int +virtio_gpu_debugfs_irq_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct virtio_gpu_device *vgdev = node->minor->dev->dev_private; + + seq_printf(m, "fence %ld %lld\n", + atomic64_read(&vgdev->fence_drv.last_seq), + vgdev->fence_drv.sync_seq); + return 0; +} + +static struct drm_info_list virtio_gpu_debugfs_list[] = { + { "irq_fence", virtio_gpu_debugfs_irq_info, 0, NULL }, +}; + +#define VIRTIO_GPU_DEBUGFS_ENTRIES ARRAY_SIZE(virtio_gpu_debugfs_list) + +int +virtio_gpu_debugfs_init(struct drm_minor *minor) +{ + drm_debugfs_create_files(virtio_gpu_debugfs_list, + VIRTIO_GPU_DEBUGFS_ENTRIES, + minor->debugfs_root, minor); + return 0; +} + +void +virtio_gpu_debugfs_takedown(struct drm_minor *minor) +{ + drm_debugfs_remove_files(virtio_gpu_debugfs_list, + VIRTIO_GPU_DEBUGFS_ENTRIES, + minor); +} diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c new file mode 100644 index 0000000..a2eca5f --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -0,0 +1,485 @@ +/* + * 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 "virtgpu_drv.h" +#include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic_helper.h> + +#define XRES_MIN 320 +#define YRES_MIN 200 + +#define XRES_DEF 1024 +#define YRES_DEF 768 + +#define XRES_MAX 8192 +#define YRES_MAX 8192 + +static int virtio_gpu_fbdev = 1; + +MODULE_PARM_DESC(fbdev, "Disable/Enable framebuffer device & console"); +module_param_named(fbdev, virtio_gpu_fbdev, int, 0400); + +static void virtio_gpu_crtc_gamma_set(struct drm_crtc *crtc, + u16 *red, u16 *green, u16 *blue, + uint32_t start, uint32_t size) +{ + /* TODO */ +} + +static void +virtio_gpu_hide_cursor(struct virtio_gpu_device *vgdev, + struct virtio_gpu_output *output) +{ + output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR); + output->cursor.resource_id = 0; + virtio_gpu_cursor_ping(vgdev, output); +} + +static int virtio_gpu_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, + uint32_t height, + int32_t hot_x, int32_t hot_y) +{ + struct virtio_gpu_device *vgdev = crtc->dev->dev_private; + struct virtio_gpu_output *output = + container_of(crtc, struct virtio_gpu_output, crtc); + struct drm_gem_object *gobj = NULL; + struct virtio_gpu_object *qobj = NULL; + struct virtio_gpu_fence *fence = NULL; + int ret = 0; + + if (handle == 0) { + virtio_gpu_hide_cursor(vgdev, output); + return 0; + } + + /* lookup the cursor */ + gobj = drm_gem_object_lookup(crtc->dev, file_priv, handle); + if (gobj == NULL) + return -ENOENT; + + qobj = gem_to_virtio_gpu_obj(gobj); + + if (!qobj->hw_res_handle) { + ret = -EINVAL; + goto out; + } + + virtio_gpu_cmd_transfer_to_host_2d(vgdev, qobj->hw_res_handle, 0, + cpu_to_le32(64), + cpu_to_le32(64), + 0, 0, &fence); + + output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR); + output->cursor.resource_id = cpu_to_le32(qobj->hw_res_handle); + output->cursor.hot_x = cpu_to_le32(hot_x); + output->cursor.hot_y = cpu_to_le32(hot_y); + virtio_gpu_cursor_ping(vgdev, output); + ret = 0; + +out: + drm_gem_object_unreference_unlocked(gobj); + return ret; +} + +static int virtio_gpu_crtc_cursor_move(struct drm_crtc *crtc, + int x, int y) +{ + struct virtio_gpu_device *vgdev = crtc->dev->dev_private; + struct virtio_gpu_output *output = + container_of(crtc, struct virtio_gpu_output, crtc); + + output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_MOVE_CURSOR); + output->cursor.pos.x = cpu_to_le32(x); + output->cursor.pos.y = cpu_to_le32(y); + virtio_gpu_cursor_ping(vgdev, output); + 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, + .gamma_set = virtio_gpu_crtc_gamma_set, + .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 + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + +static void virtio_gpu_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct virtio_gpu_framebuffer *virtio_gpu_fb + = to_virtio_gpu_framebuffer(fb); + + if (virtio_gpu_fb->obj) + drm_gem_object_unreference_unlocked(virtio_gpu_fb->obj); + drm_framebuffer_cleanup(fb); + kfree(virtio_gpu_fb); +} + +static int +virtio_gpu_framebuffer_surface_dirty(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips) +{ + struct virtio_gpu_framebuffer *virtio_gpu_fb + = to_virtio_gpu_framebuffer(fb); + + return virtio_gpu_surface_dirty(virtio_gpu_fb, clips, num_clips); +} + +static const struct drm_framebuffer_funcs virtio_gpu_fb_funcs = { + .destroy = virtio_gpu_user_framebuffer_destroy, + .dirty = virtio_gpu_framebuffer_surface_dirty, +}; + +int +virtio_gpu_framebuffer_init(struct drm_device *dev, + struct virtio_gpu_framebuffer *vgfb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj) +{ + int ret; + struct virtio_gpu_object *bo; + vgfb->obj = obj; + + bo = gem_to_virtio_gpu_obj(obj); + + ret = drm_framebuffer_init(dev, &vgfb->base, &virtio_gpu_fb_funcs); + if (ret) { + vgfb->obj = NULL; + return ret; + } + drm_helper_mode_fill_fb_struct(&vgfb->base, mode_cmd); + + spin_lock_init(&vgfb->dirty_lock); + vgfb->x1 = vgfb->y1 = INT_MAX; + vgfb->x2 = vgfb->y2 = 0; + return 0; +} + +static bool virtio_gpu_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void virtio_gpu_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct virtio_gpu_device *vgdev = dev->dev_private; + struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc); + + virtio_gpu_cmd_set_scanout(vgdev, output->index, 0, + crtc->mode.hdisplay, + crtc->mode.vdisplay, 0, 0); +} + +static void virtio_gpu_crtc_enable(struct drm_crtc *crtc) +{ +} + +static void virtio_gpu_crtc_disable(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct virtio_gpu_device *vgdev = dev->dev_private; + struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc); + + virtio_gpu_cmd_set_scanout(vgdev, output->index, 0, 0, 0, 0, 0); +} + +static int virtio_gpu_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + return 0; +} + +static const struct drm_crtc_helper_funcs virtio_gpu_crtc_helper_funcs = { + .enable = virtio_gpu_crtc_enable, + .disable = virtio_gpu_crtc_disable, + .mode_fixup = virtio_gpu_crtc_mode_fixup, + .mode_set_nofb = virtio_gpu_crtc_mode_set_nofb, + .atomic_check = virtio_gpu_crtc_atomic_check, +}; + +static bool virtio_gpu_enc_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void virtio_gpu_enc_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ +} + +static void virtio_gpu_enc_enable(struct drm_encoder *encoder) +{ +} + +static void virtio_gpu_enc_disable(struct drm_encoder *encoder) +{ +} + +static int virtio_gpu_conn_get_modes(struct drm_connector *connector) +{ + struct virtio_gpu_output *output = + drm_connector_to_virtio_gpu_output(connector); + struct drm_display_mode *mode = NULL; + int count, width, height; + + width = le32_to_cpu(output->info.r.width); + height = le32_to_cpu(output->info.r.height); + count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX); + + if (width == 0 || height == 0) { + width = XRES_DEF; + height = YRES_DEF; + drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF); + } else { + DRM_DEBUG("add mode: %dx%d\n", width, height); + mode = drm_cvt_mode(connector->dev, width, height, 60, + false, false, false); + mode->type |= DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, mode); + count++; + } + + return count; +} + +static int virtio_gpu_conn_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct virtio_gpu_output *output = + drm_connector_to_virtio_gpu_output(connector); + int width, height; + + width = le32_to_cpu(output->info.r.width); + height = le32_to_cpu(output->info.r.height); + + if (!(mode->type & DRM_MODE_TYPE_PREFERRED)) + return MODE_OK; + if (mode->hdisplay == XRES_DEF && mode->vdisplay == YRES_DEF) + return MODE_OK; + if (mode->hdisplay <= width && mode->hdisplay >= width - 16 && + mode->vdisplay <= height && mode->vdisplay >= height - 16) + return MODE_OK; + + DRM_DEBUG("del mode: %dx%d\n", mode->hdisplay, mode->vdisplay); + return MODE_BAD; +} + +static struct drm_encoder* +virtio_gpu_best_encoder(struct drm_connector *connector) +{ + struct virtio_gpu_output *virtio_gpu_output = + drm_connector_to_virtio_gpu_output(connector); + + return &virtio_gpu_output->enc; +} + +static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = { + .mode_fixup = virtio_gpu_enc_mode_fixup, + .mode_set = virtio_gpu_enc_mode_set, + .enable = virtio_gpu_enc_enable, + .disable = virtio_gpu_enc_disable, +}; + +static const struct drm_connector_helper_funcs virtio_gpu_conn_helper_funcs = { + .get_modes = virtio_gpu_conn_get_modes, + .mode_valid = virtio_gpu_conn_mode_valid, + .best_encoder = virtio_gpu_best_encoder, +}; + +static void virtio_gpu_conn_save(struct drm_connector *connector) +{ + DRM_DEBUG("\n"); +} + +static void virtio_gpu_conn_restore(struct drm_connector *connector) +{ + DRM_DEBUG("\n"); +} + +static enum drm_connector_status virtio_gpu_conn_detect( + struct drm_connector *connector, + bool force) +{ + struct virtio_gpu_output *output = + drm_connector_to_virtio_gpu_output(connector); + + if (output->info.enabled) + return connector_status_connected; + else + return connector_status_disconnected; +} + +static void virtio_gpu_conn_destroy(struct drm_connector *connector) +{ + struct virtio_gpu_output *virtio_gpu_output = + drm_connector_to_virtio_gpu_output(connector); + + drm_connector_unregister(connector); + drm_connector_cleanup(connector); + kfree(virtio_gpu_output); +} + +static const struct drm_connector_funcs virtio_gpu_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .save = virtio_gpu_conn_save, + .restore = virtio_gpu_conn_restore, + .detect = virtio_gpu_conn_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = virtio_gpu_conn_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static const struct drm_encoder_funcs virtio_gpu_enc_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index) +{ + struct drm_device *dev = vgdev->ddev; + struct virtio_gpu_output *output = vgdev->outputs + index; + struct drm_connector *connector = &output->conn; + struct drm_encoder *encoder = &output->enc; + struct drm_crtc *crtc = &output->crtc; + struct drm_plane *plane; + + output->index = index; + if (index == 0) { + output->info.enabled = cpu_to_le32(true); + output->info.r.width = cpu_to_le32(XRES_DEF); + output->info.r.height = cpu_to_le32(YRES_DEF); + } + + plane = virtio_gpu_plane_init(vgdev, index); + if (IS_ERR(plane)) + return PTR_ERR(plane); + drm_crtc_init_with_planes(dev, crtc, plane, NULL, + &virtio_gpu_crtc_funcs); + drm_mode_crtc_set_gamma_size(crtc, 256); + drm_crtc_helper_add(crtc, &virtio_gpu_crtc_helper_funcs); + plane->crtc = crtc; + + drm_connector_init(dev, connector, &virtio_gpu_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + drm_connector_helper_add(connector, &virtio_gpu_conn_helper_funcs); + + drm_encoder_init(dev, encoder, &virtio_gpu_enc_funcs, + DRM_MODE_ENCODER_VIRTUAL); + drm_encoder_helper_add(encoder, &virtio_gpu_enc_helper_funcs); + encoder->possible_crtcs = 1 << index; + + drm_mode_connector_attach_encoder(connector, encoder); + drm_connector_register(connector); + return 0; +} + +static struct drm_framebuffer * +virtio_gpu_user_framebuffer_create(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct drm_gem_object *obj = NULL; + struct virtio_gpu_framebuffer *virtio_gpu_fb; + int ret; + + /* lookup object associated with res handle */ + obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]); + if (!obj) + return ERR_PTR(-EINVAL); + + virtio_gpu_fb = kzalloc(sizeof(*virtio_gpu_fb), GFP_KERNEL); + if (virtio_gpu_fb == NULL) + return ERR_PTR(-ENOMEM); + + ret = virtio_gpu_framebuffer_init(dev, virtio_gpu_fb, mode_cmd, obj); + if (ret) { + kfree(virtio_gpu_fb); + if (obj) + drm_gem_object_unreference_unlocked(obj); + return NULL; + } + + return &virtio_gpu_fb->base; +} + +static const struct drm_mode_config_funcs virtio_gpu_mode_funcs = { + .fb_create = virtio_gpu_user_framebuffer_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev) +{ + int i, ret; + + drm_mode_config_init(vgdev->ddev); + vgdev->ddev->mode_config.funcs = (void *)&virtio_gpu_mode_funcs; + + /* modes will be validated against the framebuffer size */ + vgdev->ddev->mode_config.min_width = XRES_MIN; + vgdev->ddev->mode_config.min_height = YRES_MIN; + vgdev->ddev->mode_config.max_width = XRES_MAX; + vgdev->ddev->mode_config.max_height = YRES_MAX; + + for (i = 0 ; i < vgdev->num_scanouts; ++i) + vgdev_output_init(vgdev, i); + + drm_mode_config_reset(vgdev->ddev); + + if (virtio_gpu_fbdev) { + ret = virtio_gpu_fbdev_init(vgdev); + if (ret) + return ret; + } + + return 0; +} + +void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev) +{ + virtio_gpu_fbdev_fini(vgdev); + drm_mode_config_cleanup(vgdev->ddev); +} diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c new file mode 100644 index 0000000..f4ec816 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <linux/pci.h> + +#include "virtgpu_drv.h" + +int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master) +{ + struct pci_dev *pdev = dev->pdev; + + if (pdev) { + return drm_pci_set_busid(dev, master); + } + return 0; +} + +int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev) +{ + struct drm_device *dev; + int ret; + + dev = drm_dev_alloc(driver, &vdev->dev); + if (!dev) + return -ENOMEM; + dev->virtdev = vdev; + vdev->priv = dev; + + if (strcmp(vdev->dev.parent->bus->name, "pci") == 0) { + struct pci_dev *pdev = to_pci_dev(vdev->dev.parent); + bool vga = (pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA; + + if (vga) { + /* + * Need to make sure we don't have two drivers + * for the same hardware here. Some day we + * will simply kick out the firmware + * (vesa/efi) framebuffer. + * + * Virtual hardware specs for virtio-vga are + * not finalized yet, therefore we can't add + * code for that yet. + * + * So ignore the device for the time being, + * and suggest to the user use the device + * variant without vga compatibility mode. + */ + DRM_ERROR("virtio-vga not (yet) supported\n"); + DRM_ERROR("please use virtio-gpu-pci instead\n"); + ret = -ENODEV; + goto err_free; + } + dev->pdev = pdev; + } + + ret = drm_dev_register(dev, 0); + if (ret) + goto err_free; + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name, + driver->major, driver->minor, driver->patchlevel, + driver->date, dev->primary->index); + + return 0; + +err_free: + drm_dev_unref(dev); + return ret; +} diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c new file mode 100644 index 0000000..7d9610a --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Authors: + * Dave Airlie <airlied@redhat.com> + * Gerd Hoffmann <kraxel@redhat.com> + * + * 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 (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/console.h> +#include <linux/pci.h> +#include "drmP.h" +#include "drm/drm.h" + +#include "virtgpu_drv.h" +static struct drm_driver driver; + +static int virtio_gpu_modeset = -1; + +MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); +module_param_named(modeset, virtio_gpu_modeset, int, 0400); + +static int virtio_gpu_probe(struct virtio_device *vdev) +{ +#ifdef CONFIG_VGA_CONSOLE + if (vgacon_text_force() && virtio_gpu_modeset == -1) + return -EINVAL; +#endif + + if (virtio_gpu_modeset == 0) + return -EINVAL; + + return drm_virtio_init(&driver, vdev); +} + +static void virtio_gpu_remove(struct virtio_device *vdev) +{ + struct drm_device *dev = vdev->priv; + drm_put_dev(dev); +} + +static void virtio_gpu_config_changed(struct virtio_device *vdev) +{ + struct drm_device *dev = vdev->priv; + struct virtio_gpu_device *vgdev = dev->dev_private; + + schedule_work(&vgdev->config_changed_work); +} + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_GPU, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static unsigned int features[] = { +}; +static struct virtio_driver virtio_gpu_driver = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = virtio_gpu_probe, + .remove = virtio_gpu_remove, + .config_changed = virtio_gpu_config_changed +}; + +module_virtio_driver(virtio_gpu_driver); + +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Virtio GPU driver"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Dave Airlie <airlied@redhat.com>"); +MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>"); +MODULE_AUTHOR("Alon Levy"); + +static const struct file_operations virtio_gpu_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .mmap = virtio_gpu_mmap, + .poll = drm_poll, + .read = drm_read, + .unlocked_ioctl = drm_ioctl, + .release = drm_release, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .llseek = noop_llseek, +}; + + +static struct drm_driver driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM, + .set_busid = drm_virtio_set_busid, + .load = virtio_gpu_driver_load, + .unload = virtio_gpu_driver_unload, + + .dumb_create = virtio_gpu_mode_dumb_create, + .dumb_map_offset = virtio_gpu_mode_dumb_mmap, + .dumb_destroy = virtio_gpu_mode_dumb_destroy, + +#if defined(CONFIG_DEBUG_FS) + .debugfs_init = virtio_gpu_debugfs_init, + .debugfs_cleanup = virtio_gpu_debugfs_takedown, +#endif + + .gem_free_object = virtio_gpu_gem_free_object, + .fops = &virtio_gpu_driver_fops, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h new file mode 100644 index 0000000..e5a2c09 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef VIRTIO_DRV_H +#define VIRTIO_DRV_H + +#include <linux/virtio.h> +#include <linux/virtio_ids.h> +#include <linux/virtio_config.h> +#include <linux/virtio_gpu.h> + +#include <drm/drmP.h> +#include <drm/drm_gem.h> +#include <drm/drm_crtc_helper.h> +#include <ttm/ttm_bo_api.h> +#include <ttm/ttm_bo_driver.h> +#include <ttm/ttm_placement.h> +#include <ttm/ttm_module.h> + +#define DRIVER_NAME "virtio_gpu" +#define DRIVER_DESC "virtio GPU" +#define DRIVER_DATE "0" + +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 1 + +/* virtgpu_drm_bus.c */ +int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master); +int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev); + +struct virtio_gpu_object { + struct drm_gem_object gem_base; + uint32_t hw_res_handle; + + struct sg_table *pages; + void *vmap; + bool dumb; + struct ttm_place placement_code; + struct ttm_placement placement; + struct ttm_buffer_object tbo; + struct ttm_bo_kmap_obj kmap; +}; +#define gem_to_virtio_gpu_obj(gobj) \ + container_of((gobj), struct virtio_gpu_object, gem_base) + +struct virtio_gpu_vbuffer; +struct virtio_gpu_device; + +typedef void (*virtio_gpu_resp_cb)(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer *vbuf); + +struct virtio_gpu_fence_driver { + atomic64_t last_seq; + uint64_t sync_seq; + struct list_head fences; + spinlock_t lock; +}; + +struct virtio_gpu_fence { + struct fence f; + struct virtio_gpu_fence_driver *drv; + struct list_head node; + uint64_t seq; +}; +#define to_virtio_fence(x) \ + container_of(x, struct virtio_gpu_fence, f) + +struct virtio_gpu_vbuffer { + char *buf; + int size; + + void *data_buf; + uint32_t data_size; + + char *resp_buf; + int resp_size; + + virtio_gpu_resp_cb resp_cb; + + struct list_head list; +}; + +struct virtio_gpu_output { + int index; + struct drm_crtc crtc; + struct drm_connector conn; + struct drm_encoder enc; + struct virtio_gpu_display_one info; + struct virtio_gpu_update_cursor cursor; + int cur_x; + int cur_y; +}; +#define drm_crtc_to_virtio_gpu_output(x) \ + container_of(x, struct virtio_gpu_output, crtc) +#define drm_connector_to_virtio_gpu_output(x) \ + container_of(x, struct virtio_gpu_output, conn) +#define drm_encoder_to_virtio_gpu_output(x) \ + container_of(x, struct virtio_gpu_output, enc) + +struct virtio_gpu_framebuffer { + struct drm_framebuffer base; + struct drm_gem_object *obj; + int x1, y1, x2, y2; /* dirty rect */ + spinlock_t dirty_lock; + uint32_t hw_res_handle; +}; +#define to_virtio_gpu_framebuffer(x) \ + container_of(x, struct virtio_gpu_framebuffer, base) + +struct virtio_gpu_mman { + struct ttm_bo_global_ref bo_global_ref; + struct drm_global_reference mem_global_ref; + bool mem_global_referenced; + struct ttm_bo_device bdev; +}; + +struct virtio_gpu_fbdev; + +struct virtio_gpu_queue { + struct virtqueue *vq; + spinlock_t qlock; + wait_queue_head_t ack_queue; + struct work_struct dequeue_work; +}; + +struct virtio_gpu_device { + struct device *dev; + struct drm_device *ddev; + + struct virtio_device *vdev; + + struct virtio_gpu_mman mman; + + /* pointer to fbdev info structure */ + struct virtio_gpu_fbdev *vgfbdev; + struct virtio_gpu_output outputs[VIRTIO_GPU_MAX_SCANOUTS]; + uint32_t num_scanouts; + + struct virtio_gpu_queue ctrlq; + struct virtio_gpu_queue cursorq; + struct list_head free_vbufs; + void *vbufs; + bool vqs_ready; + + struct idr resource_idr; + spinlock_t resource_idr_lock; + + wait_queue_head_t resp_wq; + /* current display info */ + spinlock_t display_info_lock; + + struct virtio_gpu_fence_driver fence_drv; + + struct idr ctx_id_idr; + spinlock_t ctx_id_idr_lock; + + struct work_struct config_changed_work; +}; + +struct virtio_gpu_fpriv { + uint32_t ctx_id; +}; + +/* virtio_ioctl.c */ +#define DRM_VIRTIO_NUM_IOCTLS 10 +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); + +/* virtio_gem.c */ +void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj); +int virtio_gpu_gem_init(struct virtio_gpu_device *vgdev); +void virtio_gpu_gem_fini(struct virtio_gpu_device *vgdev); +int virtio_gpu_gem_create(struct drm_file *file, + struct drm_device *dev, + uint64_t size, + struct drm_gem_object **obj_p, + uint32_t *handle_p); +struct virtio_gpu_object *virtio_gpu_alloc_object(struct drm_device *dev, + size_t size, bool kernel, + bool pinned); +int virtio_gpu_mode_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); +int virtio_gpu_mode_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle); +int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle, uint64_t *offset_p); + +/* virtio_fb */ +#define VIRTIO_GPUFB_CONN_LIMIT 1 +int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev); +void virtio_gpu_fbdev_fini(struct virtio_gpu_device *vgdev); +int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *qfb, + struct drm_clip_rect *clips, + unsigned num_clips); +/* virtio vg */ +int virtio_gpu_alloc_vbufs(struct virtio_gpu_device *vgdev); +void virtio_gpu_free_vbufs(struct virtio_gpu_device *vgdev); +void virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, + uint32_t *resid); +void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id); +void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev, + uint32_t resource_id, + uint32_t format, + uint32_t width, + uint32_t height); +void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev, + uint32_t resource_id); +void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev, + uint32_t resource_id, uint64_t offset, + __le32 width, __le32 height, + __le32 x, __le32 y, + struct virtio_gpu_fence **fence); +void virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev, + uint32_t resource_id, + uint32_t x, uint32_t y, + uint32_t width, uint32_t height); +void virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev, + uint32_t scanout_id, uint32_t resource_id, + uint32_t width, uint32_t height, + uint32_t x, uint32_t y); +int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, + struct virtio_gpu_object *obj, + uint32_t resource_id, + struct virtio_gpu_fence **fence); +int virtio_gpu_attach_status_page(struct virtio_gpu_device *vgdev); +int virtio_gpu_detach_status_page(struct virtio_gpu_device *vgdev); +void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev, + struct virtio_gpu_output *output); +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); +void virtio_gpu_ctrl_ack(struct virtqueue *vq); +void virtio_gpu_cursor_ack(struct virtqueue *vq); +void virtio_gpu_dequeue_ctrl_func(struct work_struct *work); +void virtio_gpu_dequeue_cursor_func(struct work_struct *work); + +/* virtio_gpu_display.c */ +int virtio_gpu_framebuffer_init(struct drm_device *dev, + struct virtio_gpu_framebuffer *vgfb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj); +int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev); +void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev); + +/* virtio_gpu_plane.c */ +struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev, + int index); + +/* virtio_gpu_ttm.c */ +int virtio_gpu_ttm_init(struct virtio_gpu_device *vgdev); +void virtio_gpu_ttm_fini(struct virtio_gpu_device *vgdev); +int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma); + +/* virtio_gpu_fence.c */ +int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev, + struct virtio_gpu_ctrl_hdr *cmd_hdr, + struct virtio_gpu_fence **fence); +void virtio_gpu_fence_event_process(struct virtio_gpu_device *vdev, + u64 last_seq); + +/* virtio_gpu_object */ +int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, + unsigned long size, bool kernel, bool pinned, + struct virtio_gpu_object **bo_ptr); +int virtio_gpu_object_kmap(struct virtio_gpu_object *bo, void **ptr); +int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev, + struct virtio_gpu_object *bo); +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); + +static inline struct virtio_gpu_object* +virtio_gpu_object_ref(struct virtio_gpu_object *bo) +{ + ttm_bo_reference(&bo->tbo); + return bo; +} + +static inline void virtio_gpu_object_unref(struct virtio_gpu_object **bo) +{ + struct ttm_buffer_object *tbo; + + if ((*bo) == NULL) + return; + tbo = &((*bo)->tbo); + ttm_bo_unref(&tbo); + if (tbo == NULL) + *bo = NULL; +} + +static inline u64 virtio_gpu_object_mmap_offset(struct virtio_gpu_object *bo) +{ + return drm_vma_node_offset_addr(&bo->tbo.vma_node); +} + +static inline int virtio_gpu_object_reserve(struct virtio_gpu_object *bo, + bool no_wait) +{ + int r; + + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL); + if (unlikely(r != 0)) { + if (r != -ERESTARTSYS) { + struct virtio_gpu_device *qdev = + bo->gem_base.dev->dev_private; + dev_err(qdev->dev, "%p reserve failed\n", bo); + } + return r; + } + return 0; +} + +static inline void virtio_gpu_object_unreserve(struct virtio_gpu_object *bo) +{ + ttm_bo_unreserve(&bo->tbo); +} + +/* virgl debufs */ +int virtio_gpu_debugfs_init(struct drm_minor *minor); +void virtio_gpu_debugfs_takedown(struct drm_minor *minor); + +#endif diff --git a/drivers/gpu/drm/virtio/virtgpu_fb.c b/drivers/gpu/drm/virtio/virtgpu_fb.c new file mode 100644 index 0000000..25bf333 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_fb.c @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <drm/drmP.h> +#include <drm/drm_fb_helper.h> +#include "virtgpu_drv.h" + +#define VIRTIO_GPU_FBCON_POLL_PERIOD (HZ / 60) + +struct virtio_gpu_fbdev { + struct drm_fb_helper helper; + struct virtio_gpu_framebuffer vgfb; + struct list_head fbdev_list; + struct virtio_gpu_device *vgdev; + struct delayed_work work; +}; + +static int virtio_gpu_dirty_update(struct virtio_gpu_framebuffer *fb, + bool store, int x, int y, + int width, int height) +{ + struct drm_device *dev = fb->base.dev; + struct virtio_gpu_device *vgdev = dev->dev_private; + bool store_for_later = false; + int bpp = fb->base.bits_per_pixel / 8; + int x2, y2; + unsigned long flags; + struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(fb->obj); + + if ((width <= 0) || + (x + width > fb->base.width) || + (y + height > fb->base.height)) { + DRM_DEBUG("values out of range %dx%d+%d+%d, fb %dx%d\n", + width, height, x, y, + fb->base.width, fb->base.height); + return -EINVAL; + } + + /* + * Can be called with pretty much any context (console output + * path). If we are in atomic just store the dirty rect info + * to send out the update later. + * + * Can't test inside spin lock. + */ + if (in_atomic() || store) + store_for_later = true; + + x2 = x + width - 1; + y2 = y + height - 1; + + spin_lock_irqsave(&fb->dirty_lock, flags); + + if (fb->y1 < y) + y = fb->y1; + if (fb->y2 > y2) + y2 = fb->y2; + if (fb->x1 < x) + x = fb->x1; + if (fb->x2 > x2) + x2 = fb->x2; + + if (store_for_later) { + fb->x1 = x; + fb->x2 = x2; + fb->y1 = y; + fb->y2 = y2; + spin_unlock_irqrestore(&fb->dirty_lock, flags); + return 0; + } + + fb->x1 = fb->y1 = INT_MAX; + fb->x2 = fb->y2 = 0; + + spin_unlock_irqrestore(&fb->dirty_lock, flags); + + { + uint32_t offset; + uint32_t w = x2 - x + 1; + uint32_t h = y2 - y + 1; + + offset = (y * fb->base.pitches[0]) + x * bpp; + + virtio_gpu_cmd_transfer_to_host_2d(vgdev, obj->hw_res_handle, + offset, + cpu_to_le32(w), + cpu_to_le32(h), + cpu_to_le32(x), + cpu_to_le32(y), + NULL); + + } + virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle, + x, y, x2 - x + 1, y2 - y + 1); + return 0; +} + +int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *vgfb, + struct drm_clip_rect *clips, + unsigned num_clips) +{ + struct virtio_gpu_device *vgdev = vgfb->base.dev->dev_private; + struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(vgfb->obj); + struct drm_clip_rect norect; + struct drm_clip_rect *clips_ptr; + int left, right, top, bottom; + int i; + int inc = 1; + if (!num_clips) { + num_clips = 1; + clips = &norect; + norect.x1 = norect.y1 = 0; + norect.x2 = vgfb->base.width; + norect.y2 = vgfb->base.height; + } + left = clips->x1; + right = clips->x2; + top = clips->y1; + bottom = clips->y2; + + /* skip the first clip rect */ + for (i = 1, clips_ptr = clips + inc; + i < num_clips; i++, clips_ptr += inc) { + left = min_t(int, left, (int)clips_ptr->x1); + right = max_t(int, right, (int)clips_ptr->x2); + top = min_t(int, top, (int)clips_ptr->y1); + bottom = max_t(int, bottom, (int)clips_ptr->y2); + } + + if (obj->dumb) + return virtio_gpu_dirty_update(vgfb, false, left, top, + right - left, bottom - top); + + virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle, + left, top, right - left, bottom - top); + return 0; +} + +static void virtio_gpu_fb_dirty_work(struct work_struct *work) +{ + struct delayed_work *delayed_work = to_delayed_work(work); + struct virtio_gpu_fbdev *vfbdev = + container_of(delayed_work, struct virtio_gpu_fbdev, work); + struct virtio_gpu_framebuffer *vgfb = &vfbdev->vgfb; + + virtio_gpu_dirty_update(&vfbdev->vgfb, false, vgfb->x1, vgfb->y1, + vgfb->x2 - vgfb->x1, vgfb->y2 - vgfb->y1); +} + +static void virtio_gpu_3d_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct virtio_gpu_fbdev *vfbdev = info->par; + sys_fillrect(info, rect); + virtio_gpu_dirty_update(&vfbdev->vgfb, true, rect->dx, rect->dy, + rect->width, rect->height); + schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD); +} + +static void virtio_gpu_3d_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + struct virtio_gpu_fbdev *vfbdev = info->par; + sys_copyarea(info, area); + virtio_gpu_dirty_update(&vfbdev->vgfb, true, area->dx, area->dy, + area->width, area->height); + schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD); +} + +static void virtio_gpu_3d_imageblit(struct fb_info *info, + const struct fb_image *image) +{ + struct virtio_gpu_fbdev *vfbdev = info->par; + sys_imageblit(info, image); + virtio_gpu_dirty_update(&vfbdev->vgfb, true, image->dx, image->dy, + image->width, image->height); + schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD); +} + +static struct fb_ops virtio_gpufb_ops = { + .owner = THIS_MODULE, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, /* TODO: copy vmwgfx */ + .fb_fillrect = virtio_gpu_3d_fillrect, + .fb_copyarea = virtio_gpu_3d_copyarea, + .fb_imageblit = virtio_gpu_3d_imageblit, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_blank = drm_fb_helper_blank, + .fb_setcmap = drm_fb_helper_setcmap, + .fb_debug_enter = drm_fb_helper_debug_enter, + .fb_debug_leave = drm_fb_helper_debug_leave, +}; + +static int virtio_gpu_vmap_fb(struct virtio_gpu_device *vgdev, + struct virtio_gpu_object *obj) +{ + return virtio_gpu_object_kmap(obj, NULL); +} + +static int virtio_gpufb_create(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct virtio_gpu_fbdev *vfbdev = + container_of(helper, struct virtio_gpu_fbdev, helper); + struct drm_device *dev = helper->dev; + struct virtio_gpu_device *vgdev = dev->dev_private; + struct fb_info *info; + struct drm_framebuffer *fb; + struct drm_mode_fb_cmd2 mode_cmd = {}; + struct virtio_gpu_object *obj; + struct device *device = vgdev->dev; + uint32_t resid, format, size; + int ret; + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + mode_cmd.pitches[0] = mode_cmd.width * 4; + mode_cmd.pixel_format = drm_mode_legacy_fb_format(32, 24); + + switch (mode_cmd.pixel_format) { +#ifdef __BIG_ENDIAN + case DRM_FORMAT_XRGB8888: + format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM; + break; + case DRM_FORMAT_ARGB8888: + format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM; + break; + case DRM_FORMAT_BGRX8888: + format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; + break; + case DRM_FORMAT_BGRA8888: + format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM; + break; + case DRM_FORMAT_RGBX8888: + format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM; + break; + case DRM_FORMAT_RGBA8888: + format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM; + break; + case DRM_FORMAT_XBGR8888: + format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM; + break; + case DRM_FORMAT_ABGR8888: + format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM; + break; +#else + case DRM_FORMAT_XRGB8888: + format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; + break; + case DRM_FORMAT_ARGB8888: + format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM; + break; + case DRM_FORMAT_BGRX8888: + format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM; + break; + case DRM_FORMAT_BGRA8888: + format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM; + break; + case DRM_FORMAT_RGBX8888: + format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM; + break; + case DRM_FORMAT_RGBA8888: + format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM; + break; + case DRM_FORMAT_XBGR8888: + format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM; + break; + case DRM_FORMAT_ABGR8888: + format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM; + break; +#endif + default: + DRM_ERROR("failed to find virtio gpu format for %d\n", + mode_cmd.pixel_format); + return -EINVAL; + } + + size = mode_cmd.pitches[0] * mode_cmd.height; + obj = virtio_gpu_alloc_object(dev, size, false, true); + if (!obj) + return -ENOMEM; + + virtio_gpu_resource_id_get(vgdev, &resid); + virtio_gpu_cmd_create_resource(vgdev, resid, format, + mode_cmd.width, mode_cmd.height); + + ret = virtio_gpu_vmap_fb(vgdev, obj); + if (ret) { + DRM_ERROR("failed to vmap fb %d\n", ret); + goto err_obj_vmap; + } + + /* attach the object to the resource */ + ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL); + if (ret) + goto err_obj_attach; + + info = framebuffer_alloc(0, device); + if (!info) { + ret = -ENOMEM; + goto err_fb_alloc; + } + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + ret = -ENOMEM; + goto err_fb_alloc_cmap; + } + + info->par = helper; + + ret = virtio_gpu_framebuffer_init(dev, &vfbdev->vgfb, + &mode_cmd, &obj->gem_base); + if (ret) + goto err_fb_init; + + fb = &vfbdev->vgfb.base; + + vfbdev->helper.fb = fb; + vfbdev->helper.fbdev = info; + + strcpy(info->fix.id, "virtiodrmfb"); + info->flags = FBINFO_DEFAULT; + info->fbops = &virtio_gpufb_ops; + info->pixmap.flags = FB_PIXMAP_SYSTEM; + + info->screen_base = obj->vmap; + info->screen_size = obj->gem_base.size; + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_var(info, &vfbdev->helper, + sizes->fb_width, sizes->fb_height); + + info->fix.mmio_start = 0; + info->fix.mmio_len = 0; + return 0; + +err_fb_init: + fb_dealloc_cmap(&info->cmap); +err_fb_alloc_cmap: + framebuffer_release(info); +err_fb_alloc: + virtio_gpu_cmd_resource_inval_backing(vgdev, resid); +err_obj_attach: +err_obj_vmap: + virtio_gpu_gem_free_object(&obj->gem_base); + return ret; +} + +static int virtio_gpu_fbdev_destroy(struct drm_device *dev, + struct virtio_gpu_fbdev *vgfbdev) +{ + struct fb_info *info; + struct virtio_gpu_framebuffer *vgfb = &vgfbdev->vgfb; + + if (vgfbdev->helper.fbdev) { + info = vgfbdev->helper.fbdev; + + unregister_framebuffer(info); + framebuffer_release(info); + } + if (vgfb->obj) + vgfb->obj = NULL; + drm_fb_helper_fini(&vgfbdev->helper); + drm_framebuffer_cleanup(&vgfb->base); + + return 0; +} +static struct drm_fb_helper_funcs virtio_gpu_fb_helper_funcs = { + .fb_probe = virtio_gpufb_create, +}; + +int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev) +{ + struct virtio_gpu_fbdev *vgfbdev; + int bpp_sel = 32; /* TODO: parameter from somewhere? */ + int ret; + + vgfbdev = kzalloc(sizeof(struct virtio_gpu_fbdev), GFP_KERNEL); + if (!vgfbdev) + return -ENOMEM; + + vgfbdev->vgdev = vgdev; + vgdev->vgfbdev = vgfbdev; + INIT_DELAYED_WORK(&vgfbdev->work, virtio_gpu_fb_dirty_work); + + drm_fb_helper_prepare(vgdev->ddev, &vgfbdev->helper, + &virtio_gpu_fb_helper_funcs); + ret = drm_fb_helper_init(vgdev->ddev, &vgfbdev->helper, + vgdev->num_scanouts, + VIRTIO_GPUFB_CONN_LIMIT); + if (ret) { + kfree(vgfbdev); + return ret; + } + + drm_fb_helper_single_add_all_connectors(&vgfbdev->helper); + drm_fb_helper_initial_config(&vgfbdev->helper, bpp_sel); + return 0; +} + +void virtio_gpu_fbdev_fini(struct virtio_gpu_device *vgdev) +{ + if (!vgdev->vgfbdev) + return; + + virtio_gpu_fbdev_destroy(vgdev->ddev, vgdev->vgfbdev); + kfree(vgdev->vgfbdev); + vgdev->vgfbdev = NULL; +} diff --git a/drivers/gpu/drm/virtio/virtgpu_fence.c b/drivers/gpu/drm/virtio/virtgpu_fence.c new file mode 100644 index 0000000..1da6326 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_fence.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <drm/drmP.h> +#include "virtgpu_drv.h" + +static const char *virtio_get_driver_name(struct fence *f) +{ + return "virtio_gpu"; +} + +static const char *virtio_get_timeline_name(struct fence *f) +{ + return "controlq"; +} + +static bool virtio_enable_signaling(struct fence *f) +{ + return true; +} + +static bool virtio_signaled(struct fence *f) +{ + struct virtio_gpu_fence *fence = to_virtio_fence(f); + + if (atomic64_read(&fence->drv->last_seq) >= fence->seq) + return true; + return false; +} + +static void virtio_fence_value_str(struct fence *f, char *str, int size) +{ + struct virtio_gpu_fence *fence = to_virtio_fence(f); + + snprintf(str, size, "%llu", fence->seq); +} + +static void virtio_timeline_value_str(struct fence *f, char *str, int size) +{ + struct virtio_gpu_fence *fence = to_virtio_fence(f); + + snprintf(str, size, "%lu", atomic64_read(&fence->drv->last_seq)); +} + +static const struct fence_ops virtio_fence_ops = { + .get_driver_name = virtio_get_driver_name, + .get_timeline_name = virtio_get_timeline_name, + .enable_signaling = virtio_enable_signaling, + .signaled = virtio_signaled, + .wait = fence_default_wait, + .fence_value_str = virtio_fence_value_str, + .timeline_value_str = virtio_timeline_value_str, +}; + +int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev, + struct virtio_gpu_ctrl_hdr *cmd_hdr, + struct virtio_gpu_fence **fence) +{ + struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv; + unsigned long irq_flags; + + *fence = kmalloc(sizeof(struct virtio_gpu_fence), GFP_KERNEL); + if ((*fence) == NULL) + return -ENOMEM; + + spin_lock_irqsave(&drv->lock, irq_flags); + (*fence)->drv = drv; + (*fence)->seq = ++drv->sync_seq; + fence_init(&(*fence)->f, &virtio_fence_ops, &drv->lock, + 0, (*fence)->seq); + fence_get(&(*fence)->f); + list_add_tail(&(*fence)->node, &drv->fences); + spin_unlock_irqrestore(&drv->lock, irq_flags); + + cmd_hdr->flags |= cpu_to_le32(VIRTIO_GPU_FLAG_FENCE); + cmd_hdr->fence_id = cpu_to_le64((*fence)->seq); + return 0; +} + +void virtio_gpu_fence_event_process(struct virtio_gpu_device *vgdev, + u64 last_seq) +{ + struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv; + struct virtio_gpu_fence *fence, *tmp; + unsigned long irq_flags; + + spin_lock_irqsave(&drv->lock, irq_flags); + atomic64_set(&vgdev->fence_drv.last_seq, last_seq); + list_for_each_entry_safe(fence, tmp, &drv->fences, node) { + if (last_seq < fence->seq) + continue; + fence_signal_locked(&fence->f); + list_del(&fence->node); + fence_put(&fence->f); + } + spin_unlock_irqrestore(&drv->lock, irq_flags); +} diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c new file mode 100644 index 0000000..cfa0d27 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_gem.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <drm/drmP.h> +#include "virtgpu_drv.h" + +void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj) +{ + struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(gem_obj); + + if (obj) + virtio_gpu_object_unref(&obj); +} + +struct virtio_gpu_object *virtio_gpu_alloc_object(struct drm_device *dev, + size_t size, bool kernel, + bool pinned) +{ + struct virtio_gpu_device *vgdev = dev->dev_private; + struct virtio_gpu_object *obj; + int ret; + + ret = virtio_gpu_object_create(vgdev, size, kernel, pinned, &obj); + if (ret) + return ERR_PTR(ret); + + return obj; +} + +int virtio_gpu_gem_create(struct drm_file *file, + struct drm_device *dev, + uint64_t size, + struct drm_gem_object **obj_p, + uint32_t *handle_p) +{ + struct virtio_gpu_object *obj; + int ret; + u32 handle; + + obj = virtio_gpu_alloc_object(dev, size, false, false); + if (IS_ERR(obj)) + return PTR_ERR(obj); + + ret = drm_gem_handle_create(file, &obj->gem_base, &handle); + if (ret) { + drm_gem_object_release(&obj->gem_base); + return ret; + } + + *obj_p = &obj->gem_base; + + /* drop reference from allocate - handle holds it now */ + drm_gem_object_unreference_unlocked(&obj->gem_base); + + *handle_p = handle; + return 0; +} + +int virtio_gpu_mode_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct virtio_gpu_device *vgdev = dev->dev_private; + struct drm_gem_object *gobj; + struct virtio_gpu_object *obj; + int ret; + uint32_t pitch; + uint32_t resid; + + pitch = args->width * ((args->bpp + 1) / 8); + args->size = pitch * args->height; + args->size = ALIGN(args->size, PAGE_SIZE); + + ret = virtio_gpu_gem_create(file_priv, dev, args->size, &gobj, + &args->handle); + if (ret) + goto fail; + + virtio_gpu_resource_id_get(vgdev, &resid); + virtio_gpu_cmd_create_resource(vgdev, resid, + 2, args->width, args->height); + + /* attach the object to the resource */ + obj = gem_to_virtio_gpu_obj(gobj); + ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL); + if (ret) + goto fail; + + obj->dumb = true; + args->pitch = pitch; + return ret; + +fail: + return ret; +} + +int virtio_gpu_mode_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle) +{ + return drm_gem_handle_delete(file_priv, handle); +} + +int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle, uint64_t *offset_p) +{ + struct drm_gem_object *gobj; + struct virtio_gpu_object *obj; + BUG_ON(!offset_p); + gobj = drm_gem_object_lookup(dev, file_priv, handle); + if (gobj == NULL) + return -ENOENT; + obj = gem_to_virtio_gpu_obj(gobj); + *offset_p = virtio_gpu_object_mmap_offset(obj); + drm_gem_object_unreference_unlocked(gobj); + return 0; +} diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c new file mode 100644 index 0000000..e503ffb --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <linux/virtio.h> +#include <linux/virtio_config.h> +#include <drm/drmP.h> +#include "virtgpu_drv.h" + +static void virtio_gpu_config_changed_work_func(struct work_struct *work) +{ + struct virtio_gpu_device *vgdev = + container_of(work, struct virtio_gpu_device, + config_changed_work); + u32 events_read, events_clear = 0; + + /* read the config space */ + virtio_cread(vgdev->vdev, struct virtio_gpu_config, + events_read, &events_read); + if (events_read & VIRTIO_GPU_EVENT_DISPLAY) { + virtio_gpu_cmd_get_display_info(vgdev); + drm_helper_hpd_irq_event(vgdev->ddev); + events_clear |= VIRTIO_GPU_EVENT_DISPLAY; + } + virtio_cwrite(vgdev->vdev, struct virtio_gpu_config, + events_clear, &events_clear); +} + +static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq, + void (*work_func)(struct work_struct *work)) +{ + spin_lock_init(&vgvq->qlock); + init_waitqueue_head(&vgvq->ack_queue); + INIT_WORK(&vgvq->dequeue_work, work_func); +} + +int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags) +{ + static vq_callback_t *callbacks[] = { + virtio_gpu_ctrl_ack, virtio_gpu_cursor_ack + }; + static const char *names[] = { "control", "cursor" }; + + struct virtio_gpu_device *vgdev; + /* this will expand later */ + struct virtqueue *vqs[2]; + u32 num_scanouts; + int ret; + + if (!virtio_has_feature(dev->virtdev, VIRTIO_F_VERSION_1)) + return -ENODEV; + + vgdev = kzalloc(sizeof(struct virtio_gpu_device), GFP_KERNEL); + if (!vgdev) + return -ENOMEM; + + vgdev->ddev = dev; + dev->dev_private = vgdev; + vgdev->vdev = dev->virtdev; + vgdev->dev = dev->dev; + + spin_lock_init(&vgdev->display_info_lock); + spin_lock_init(&vgdev->ctx_id_idr_lock); + idr_init(&vgdev->ctx_id_idr); + spin_lock_init(&vgdev->resource_idr_lock); + idr_init(&vgdev->resource_idr); + init_waitqueue_head(&vgdev->resp_wq); + virtio_gpu_init_vq(&vgdev->ctrlq, virtio_gpu_dequeue_ctrl_func); + virtio_gpu_init_vq(&vgdev->cursorq, virtio_gpu_dequeue_cursor_func); + + spin_lock_init(&vgdev->fence_drv.lock); + INIT_LIST_HEAD(&vgdev->fence_drv.fences); + INIT_WORK(&vgdev->config_changed_work, + virtio_gpu_config_changed_work_func); + + ret = vgdev->vdev->config->find_vqs(vgdev->vdev, 2, vqs, + callbacks, names); + if (ret) { + DRM_ERROR("failed to find virt queues\n"); + goto err_vqs; + } + vgdev->ctrlq.vq = vqs[0]; + vgdev->cursorq.vq = vqs[1]; + ret = virtio_gpu_alloc_vbufs(vgdev); + if (ret) { + DRM_ERROR("failed to alloc vbufs\n"); + goto err_vbufs; + } + + ret = virtio_gpu_ttm_init(vgdev); + if (ret) { + DRM_ERROR("failed to init ttm %d\n", ret); + goto err_ttm; + } + + /* get display info */ + virtio_cread(vgdev->vdev, struct virtio_gpu_config, + num_scanouts, &num_scanouts); + vgdev->num_scanouts = min_t(uint32_t, num_scanouts, + VIRTIO_GPU_MAX_SCANOUTS); + if (!vgdev->num_scanouts) { + DRM_ERROR("num_scanouts is zero\n"); + ret = -EINVAL; + goto err_scanouts; + } + + ret = virtio_gpu_modeset_init(vgdev); + if (ret) + goto err_modeset; + + virtio_device_ready(vgdev->vdev); + vgdev->vqs_ready = true; + virtio_gpu_cmd_get_display_info(vgdev); + return 0; + +err_modeset: +err_scanouts: + virtio_gpu_ttm_fini(vgdev); +err_ttm: + virtio_gpu_free_vbufs(vgdev); +err_vbufs: + vgdev->vdev->config->del_vqs(vgdev->vdev); +err_vqs: + kfree(vgdev); + return ret; +} + +int virtio_gpu_driver_unload(struct drm_device *dev) +{ + struct virtio_gpu_device *vgdev = dev->dev_private; + + vgdev->vqs_ready = false; + flush_work(&vgdev->ctrlq.dequeue_work); + flush_work(&vgdev->cursorq.dequeue_work); + flush_work(&vgdev->config_changed_work); + vgdev->vdev->config->del_vqs(vgdev->vdev); + + virtio_gpu_modeset_fini(vgdev); + virtio_gpu_ttm_fini(vgdev); + virtio_gpu_free_vbufs(vgdev); + kfree(vgdev); + return 0; +} diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c new file mode 100644 index 0000000..2c624c7 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "virtgpu_drv.h" + +static void virtio_gpu_ttm_bo_destroy(struct ttm_buffer_object *tbo) +{ + struct virtio_gpu_object *bo; + struct virtio_gpu_device *vgdev; + + bo = container_of(tbo, struct virtio_gpu_object, tbo); + vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private; + + if (bo->hw_res_handle) + virtio_gpu_cmd_unref_resource(vgdev, bo->hw_res_handle); + if (bo->pages) + virtio_gpu_object_free_sg_table(bo); + drm_gem_object_release(&bo->gem_base); + kfree(bo); +} + +static void virtio_gpu_init_ttm_placement(struct virtio_gpu_object *vgbo, + bool pinned) +{ + u32 c = 1; + u32 pflag = pinned ? TTM_PL_FLAG_NO_EVICT : 0; + + vgbo->placement.placement = &vgbo->placement_code; + vgbo->placement.busy_placement = &vgbo->placement_code; + vgbo->placement_code.fpfn = 0; + vgbo->placement_code.lpfn = 0; + vgbo->placement_code.flags = + TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT | pflag; + vgbo->placement.num_placement = c; + vgbo->placement.num_busy_placement = c; + +} + +int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, + unsigned long size, bool kernel, bool pinned, + struct virtio_gpu_object **bo_ptr) +{ + struct virtio_gpu_object *bo; + enum ttm_bo_type type; + size_t acc_size; + int ret; + + if (kernel) + type = ttm_bo_type_kernel; + else + type = ttm_bo_type_device; + *bo_ptr = NULL; + + acc_size = ttm_bo_dma_acc_size(&vgdev->mman.bdev, size, + sizeof(struct virtio_gpu_object)); + + bo = kzalloc(sizeof(struct virtio_gpu_object), GFP_KERNEL); + if (bo == NULL) + return -ENOMEM; + size = roundup(size, PAGE_SIZE); + ret = drm_gem_object_init(vgdev->ddev, &bo->gem_base, size); + if (ret != 0) + goto err_gem_init; + 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); + if (ret != 0) + goto err_ttm_init; + + *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) +{ + bool is_iomem; + int r; + + if (bo->vmap) { + if (ptr) + *ptr = bo->vmap; + return 0; + } + r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->kmap); + if (r) + return r; + bo->vmap = ttm_kmap_obj_virtual(&bo->kmap, &is_iomem); + if (ptr) + *ptr = bo->vmap; + return 0; +} + +int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev, + struct virtio_gpu_object *bo) +{ + int ret; + struct page **pages = bo->tbo.ttm->pages; + int nr_pages = bo->tbo.num_pages; + + /* wtf swapping */ + if (bo->pages) + return 0; + + if (bo->tbo.ttm->state == tt_unpopulated) + bo->tbo.ttm->bdev->driver->ttm_tt_populate(bo->tbo.ttm); + bo->pages = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!bo->pages) + goto out; + + ret = sg_alloc_table_from_pages(bo->pages, pages, nr_pages, 0, + nr_pages << PAGE_SHIFT, GFP_KERNEL); + if (ret) + goto out; + return 0; +out: + kfree(bo->pages); + bo->pages = NULL; + return -ENOMEM; +} + +void virtio_gpu_object_free_sg_table(struct virtio_gpu_object *bo) +{ + sg_free_table(bo->pages); + kfree(bo->pages); + bo->pages = NULL; +} + +int virtio_gpu_object_wait(struct virtio_gpu_object *bo, bool no_wait) +{ + int r; + + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL); + if (unlikely(r != 0)) + return r; + r = ttm_bo_wait(&bo->tbo, true, true, no_wait); + ttm_bo_unreserve(&bo->tbo); + return r; +} + diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c new file mode 100644 index 0000000..4a74129 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_plane.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, 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 (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "virtgpu_drv.h" +#include <drm/drm_plane_helper.h> +#include <drm/drm_atomic_helper.h> + +static const uint32_t virtio_gpu_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, +}; + +static void virtio_gpu_plane_destroy(struct drm_plane *plane) +{ + kfree(plane); +} + +static const struct drm_plane_funcs virtio_gpu_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = virtio_gpu_plane_destroy, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static int virtio_gpu_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + return 0; +} + +static void virtio_gpu_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_device *dev = plane->dev; + struct virtio_gpu_device *vgdev = dev->dev_private; + struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(plane->crtc); + struct virtio_gpu_framebuffer *vgfb; + struct virtio_gpu_object *bo; + uint32_t handle; + + if (plane->fb) { + vgfb = to_virtio_gpu_framebuffer(plane->fb); + bo = gem_to_virtio_gpu_obj(vgfb->obj); + handle = bo->hw_res_handle; + } else { + handle = 0; + } + + DRM_DEBUG("handle 0x%x, crtc %dx%d+%d+%d\n", handle, + plane->state->crtc_w, plane->state->crtc_h, + plane->state->crtc_x, plane->state->crtc_y); + virtio_gpu_cmd_set_scanout(vgdev, output->index, handle, + plane->state->crtc_w, + plane->state->crtc_h, + plane->state->crtc_x, + plane->state->crtc_y); +} + + +static const struct drm_plane_helper_funcs virtio_gpu_plane_helper_funcs = { + .atomic_check = virtio_gpu_plane_atomic_check, + .atomic_update = virtio_gpu_plane_atomic_update, +}; + +struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev, + int index) +{ + struct drm_device *dev = vgdev->ddev; + struct drm_plane *plane; + int ret; + + plane = kzalloc(sizeof(*plane), GFP_KERNEL); + if (!plane) + return ERR_PTR(-ENOMEM); + + ret = drm_universal_plane_init(dev, plane, 1 << index, + &virtio_gpu_plane_funcs, + virtio_gpu_formats, + ARRAY_SIZE(virtio_gpu_formats), + DRM_PLANE_TYPE_PRIMARY); + if (ret) + goto err_plane_init; + + drm_plane_helper_add(plane, &virtio_gpu_plane_helper_funcs); + return plane; + +err_plane_init: + kfree(plane); + return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/virtio/virtgpu_ttm.c b/drivers/gpu/drm/virtio/virtgpu_ttm.c new file mode 100644 index 0000000..e0e74c6 --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_ttm.c @@ -0,0 +1,469 @@ +/* + * 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 <ttm/ttm_bo_api.h> +#include <ttm/ttm_bo_driver.h> +#include <ttm/ttm_placement.h> +#include <ttm/ttm_page_alloc.h> +#include <ttm/ttm_module.h> +#include <drm/drmP.h> +#include <drm/drm.h> +#include "virtgpu_drv.h" + +#include <linux/delay.h> + +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) + +static struct +virtio_gpu_device *virtio_gpu_get_vgdev(struct ttm_bo_device *bdev) +{ + struct virtio_gpu_mman *mman; + struct virtio_gpu_device *vgdev; + + mman = container_of(bdev, struct virtio_gpu_mman, bdev); + vgdev = container_of(mman, struct virtio_gpu_device, mman); + return vgdev; +} + +static int virtio_gpu_ttm_mem_global_init(struct drm_global_reference *ref) +{ + return ttm_mem_global_init(ref->object); +} + +static void virtio_gpu_ttm_mem_global_release(struct drm_global_reference *ref) +{ + ttm_mem_global_release(ref->object); +} + +static int virtio_gpu_ttm_global_init(struct virtio_gpu_device *vgdev) +{ + struct drm_global_reference *global_ref; + int r; + + vgdev->mman.mem_global_referenced = false; + global_ref = &vgdev->mman.mem_global_ref; + global_ref->global_type = DRM_GLOBAL_TTM_MEM; + global_ref->size = sizeof(struct ttm_mem_global); + global_ref->init = &virtio_gpu_ttm_mem_global_init; + global_ref->release = &virtio_gpu_ttm_mem_global_release; + + r = drm_global_item_ref(global_ref); + if (r != 0) { + DRM_ERROR("Failed setting up TTM memory accounting " + "subsystem.\n"); + return r; + } + + vgdev->mman.bo_global_ref.mem_glob = + vgdev->mman.mem_global_ref.object; + global_ref = &vgdev->mman.bo_global_ref.ref; + global_ref->global_type = DRM_GLOBAL_TTM_BO; + global_ref->size = sizeof(struct ttm_bo_global); + global_ref->init = &ttm_bo_global_init; + global_ref->release = &ttm_bo_global_release; + r = drm_global_item_ref(global_ref); + if (r != 0) { + DRM_ERROR("Failed setting up TTM BO subsystem.\n"); + drm_global_item_unref(&vgdev->mman.mem_global_ref); + return r; + } + + vgdev->mman.mem_global_referenced = true; + return 0; +} + +static void virtio_gpu_ttm_global_fini(struct virtio_gpu_device *vgdev) +{ + if (vgdev->mman.mem_global_referenced) { + drm_global_item_unref(&vgdev->mman.bo_global_ref.ref); + drm_global_item_unref(&vgdev->mman.mem_global_ref); + vgdev->mman.mem_global_referenced = false; + } +} + +#if 0 +/* + * Hmm, seems to not do anything useful. Leftover debug hack? + * Something like printing pagefaults to kernel log? + */ +static struct vm_operations_struct virtio_gpu_ttm_vm_ops; +static const struct vm_operations_struct *ttm_vm_ops; + +static int virtio_gpu_ttm_fault(struct vm_area_struct *vma, + struct vm_fault *vmf) +{ + struct ttm_buffer_object *bo; + struct virtio_gpu_device *vgdev; + int r; + + bo = (struct ttm_buffer_object *)vma->vm_private_data; + if (bo == NULL) + return VM_FAULT_NOPAGE; + vgdev = virtio_gpu_get_vgdev(bo->bdev); + r = ttm_vm_ops->fault(vma, vmf); + return r; +} +#endif + +int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv; + struct virtio_gpu_device *vgdev; + int r; + + file_priv = filp->private_data; + vgdev = file_priv->minor->dev->dev_private; + if (vgdev == NULL) { + DRM_ERROR( + "filp->private_data->minor->dev->dev_private == NULL\n"); + return -EINVAL; + } + r = ttm_bo_mmap(filp, vma, &vgdev->mman.bdev); +#if 0 + if (unlikely(r != 0)) + return r; + if (unlikely(ttm_vm_ops == NULL)) { + ttm_vm_ops = vma->vm_ops; + virtio_gpu_ttm_vm_ops = *ttm_vm_ops; + virtio_gpu_ttm_vm_ops.fault = &virtio_gpu_ttm_fault; + } + vma->vm_ops = &virtio_gpu_ttm_vm_ops; + return 0; +#else + return r; +#endif +} + +static int virtio_gpu_invalidate_caches(struct ttm_bo_device *bdev, + uint32_t flags) +{ + return 0; +} + +static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, + struct ttm_buffer_object *bo, + const struct ttm_place *place, + struct ttm_mem_reg *mem) +{ + mem->mm_node = (void *)1; + return 0; +} + +static void ttm_bo_man_put_node(struct ttm_mem_type_manager *man, + struct ttm_mem_reg *mem) +{ + mem->mm_node = (void *)NULL; + return; +} + +static int ttm_bo_man_init(struct ttm_mem_type_manager *man, + unsigned long p_size) +{ + return 0; +} + +static int ttm_bo_man_takedown(struct ttm_mem_type_manager *man) +{ + return 0; +} + +static void ttm_bo_man_debug(struct ttm_mem_type_manager *man, + const char *prefix) +{ +} + +static const struct ttm_mem_type_manager_func virtio_gpu_bo_manager_func = { + ttm_bo_man_init, + ttm_bo_man_takedown, + ttm_bo_man_get_node, + ttm_bo_man_put_node, + ttm_bo_man_debug +}; + +static int virtio_gpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, + struct ttm_mem_type_manager *man) +{ + struct virtio_gpu_device *vgdev; + + vgdev = virtio_gpu_get_vgdev(bdev); + + switch (type) { + case TTM_PL_SYSTEM: + /* System memory */ + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_TT: + man->func = &virtio_gpu_bo_manager_func; + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + default: + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +static void virtio_gpu_evict_flags(struct ttm_buffer_object *bo, + struct ttm_placement *placement) +{ + static struct ttm_place placements = { + .fpfn = 0, + .lpfn = 0, + .flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM, + }; + + placement->placement = &placements; + placement->busy_placement = &placements; + placement->num_placement = 1; + placement->num_busy_placement = 1; + return; +} + +static int virtio_gpu_verify_access(struct ttm_buffer_object *bo, + struct file *filp) +{ + return 0; +} + +static int virtio_gpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; + + mem->bus.addr = NULL; + mem->bus.offset = 0; + mem->bus.size = mem->num_pages << PAGE_SHIFT; + mem->bus.base = 0; + mem->bus.is_iomem = false; + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) + return -EINVAL; + switch (mem->mem_type) { + case TTM_PL_SYSTEM: + case TTM_PL_TT: + /* system memory */ + return 0; + default: + return -EINVAL; + } + return 0; +} + +static void virtio_gpu_ttm_io_mem_free(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ +} + +/* + * TTM backend functions. + */ +struct virtio_gpu_ttm_tt { + struct ttm_dma_tt ttm; + struct virtio_gpu_device *vgdev; + u64 offset; +}; + +static int virtio_gpu_ttm_backend_bind(struct ttm_tt *ttm, + struct ttm_mem_reg *bo_mem) +{ + struct virtio_gpu_ttm_tt *gtt = (void *)ttm; + + gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT); + if (!ttm->num_pages) + WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n", + ttm->num_pages, bo_mem, ttm); + + /* Not implemented */ + return 0; +} + +static int virtio_gpu_ttm_backend_unbind(struct ttm_tt *ttm) +{ + /* Not implemented */ + return 0; +} + +static void virtio_gpu_ttm_backend_destroy(struct ttm_tt *ttm) +{ + struct virtio_gpu_ttm_tt *gtt = (void *)ttm; + + ttm_dma_tt_fini(>t->ttm); + kfree(gtt); +} + +static struct ttm_backend_func virtio_gpu_backend_func = { + .bind = &virtio_gpu_ttm_backend_bind, + .unbind = &virtio_gpu_ttm_backend_unbind, + .destroy = &virtio_gpu_ttm_backend_destroy, +}; + +static int virtio_gpu_ttm_tt_populate(struct ttm_tt *ttm) +{ + if (ttm->state != tt_unpopulated) + return 0; + + return ttm_pool_populate(ttm); +} + +static void virtio_gpu_ttm_tt_unpopulate(struct ttm_tt *ttm) +{ + ttm_pool_unpopulate(ttm); +} + +static struct ttm_tt *virtio_gpu_ttm_tt_create(struct ttm_bo_device *bdev, + unsigned long size, + uint32_t page_flags, + struct page *dummy_read_page) +{ + struct virtio_gpu_device *vgdev; + struct virtio_gpu_ttm_tt *gtt; + + vgdev = virtio_gpu_get_vgdev(bdev); + gtt = kzalloc(sizeof(struct virtio_gpu_ttm_tt), GFP_KERNEL); + if (gtt == NULL) + return NULL; + gtt->ttm.ttm.func = &virtio_gpu_backend_func; + gtt->vgdev = vgdev; + if (ttm_dma_tt_init(>t->ttm, bdev, size, page_flags, + dummy_read_page)) { + kfree(gtt); + return NULL; + } + return >t->ttm.ttm; +} + +static void virtio_gpu_move_null(struct ttm_buffer_object *bo, + struct ttm_mem_reg *new_mem) +{ + struct ttm_mem_reg *old_mem = &bo->mem; + + BUG_ON(old_mem->mm_node != NULL); + *old_mem = *new_mem; + new_mem->mm_node = NULL; +} + +static int virtio_gpu_bo_move(struct ttm_buffer_object *bo, + bool evict, bool interruptible, + bool no_wait_gpu, + struct ttm_mem_reg *new_mem) +{ + virtio_gpu_move_null(bo, new_mem); + return 0; +} + +static void virtio_gpu_bo_move_notify(struct ttm_buffer_object *tbo, + struct ttm_mem_reg *new_mem) +{ + struct virtio_gpu_object *bo; + struct virtio_gpu_device *vgdev; + + bo = container_of(tbo, struct virtio_gpu_object, tbo); + vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private; + + if (!new_mem || (new_mem->placement & TTM_PL_FLAG_SYSTEM)) { + if (bo->hw_res_handle) + virtio_gpu_cmd_resource_inval_backing(vgdev, + bo->hw_res_handle); + + } else if (new_mem->placement & TTM_PL_FLAG_TT) { + if (bo->hw_res_handle) { + virtio_gpu_object_attach(vgdev, bo, bo->hw_res_handle, + NULL); + } + } +} + +static void virtio_gpu_bo_swap_notify(struct ttm_buffer_object *tbo) +{ + struct virtio_gpu_object *bo; + struct virtio_gpu_device *vgdev; + + bo = container_of(tbo, struct virtio_gpu_object, tbo); + vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private; + + if (bo->pages) + virtio_gpu_object_free_sg_table(bo); +} + +static struct ttm_bo_driver virtio_gpu_bo_driver = { + .ttm_tt_create = &virtio_gpu_ttm_tt_create, + .ttm_tt_populate = &virtio_gpu_ttm_tt_populate, + .ttm_tt_unpopulate = &virtio_gpu_ttm_tt_unpopulate, + .invalidate_caches = &virtio_gpu_invalidate_caches, + .init_mem_type = &virtio_gpu_init_mem_type, + .evict_flags = &virtio_gpu_evict_flags, + .move = &virtio_gpu_bo_move, + .verify_access = &virtio_gpu_verify_access, + .io_mem_reserve = &virtio_gpu_ttm_io_mem_reserve, + .io_mem_free = &virtio_gpu_ttm_io_mem_free, + .move_notify = &virtio_gpu_bo_move_notify, + .swap_notify = &virtio_gpu_bo_swap_notify, +}; + +int virtio_gpu_ttm_init(struct virtio_gpu_device *vgdev) +{ + int r; + + r = virtio_gpu_ttm_global_init(vgdev); + if (r) + return r; + /* No others user of address space so set it to 0 */ + r = ttm_bo_device_init(&vgdev->mman.bdev, + vgdev->mman.bo_global_ref.ref.object, + &virtio_gpu_bo_driver, + vgdev->ddev->anon_inode->i_mapping, + DRM_FILE_PAGE_OFFSET, 0); + if (r) { + DRM_ERROR("failed initializing buffer object driver(%d).\n", r); + goto err_dev_init; + return r; + } + + r = ttm_bo_init_mm(&vgdev->mman.bdev, TTM_PL_TT, 0); + if (r) { + DRM_ERROR("Failed initializing GTT heap.\n"); + goto err_mm_init; + return r; + } + return 0; + +err_mm_init: + ttm_bo_device_release(&vgdev->mman.bdev); +err_dev_init: + virtio_gpu_ttm_global_fini(vgdev); + return r; +} + +void virtio_gpu_ttm_fini(struct virtio_gpu_device *vgdev) +{ + ttm_bo_device_release(&vgdev->mman.bdev); + virtio_gpu_ttm_global_fini(vgdev); + DRM_INFO("virtio_gpu: ttm finalized\n"); +} diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c new file mode 100644 index 0000000..8fa6513e --- /dev/null +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -0,0 +1,614 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * All Rights Reserved. + * + * Authors: + * Dave Airlie <airlied@redhat.com> + * Gerd Hoffmann <kraxel@redhat.com> + * + * 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 (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <drm/drmP.h> +#include "virtgpu_drv.h" +#include <linux/virtio.h> +#include <linux/virtio_config.h> +#include <linux/virtio_ring.h> + +#define MAX_INLINE_CMD_SIZE 96 +#define MAX_INLINE_RESP_SIZE 24 +#define VBUFFER_SIZE (sizeof(struct virtio_gpu_vbuffer) \ + + MAX_INLINE_CMD_SIZE \ + + MAX_INLINE_RESP_SIZE) + +void virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, + uint32_t *resid) +{ + int handle; + + idr_preload(GFP_KERNEL); + spin_lock(&vgdev->resource_idr_lock); + handle = idr_alloc(&vgdev->resource_idr, NULL, 1, 0, GFP_NOWAIT); + spin_unlock(&vgdev->resource_idr_lock); + idr_preload_end(); + *resid = handle; +} + +void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id) +{ + spin_lock(&vgdev->resource_idr_lock); + idr_remove(&vgdev->resource_idr, id); + spin_unlock(&vgdev->resource_idr_lock); +} + +void virtio_gpu_ctrl_ack(struct virtqueue *vq) +{ + struct drm_device *dev = vq->vdev->priv; + struct virtio_gpu_device *vgdev = dev->dev_private; + schedule_work(&vgdev->ctrlq.dequeue_work); +} + +void virtio_gpu_cursor_ack(struct virtqueue *vq) +{ + struct drm_device *dev = vq->vdev->priv; + struct virtio_gpu_device *vgdev = dev->dev_private; + schedule_work(&vgdev->cursorq.dequeue_work); +} + +int virtio_gpu_alloc_vbufs(struct virtio_gpu_device *vgdev) +{ + struct virtio_gpu_vbuffer *vbuf; + int i, size, count = 0; + void *ptr; + + INIT_LIST_HEAD(&vgdev->free_vbufs); + count += virtqueue_get_vring_size(vgdev->ctrlq.vq); + count += virtqueue_get_vring_size(vgdev->cursorq.vq); + size = count * VBUFFER_SIZE; + DRM_INFO("virtio vbuffers: %d bufs, %zdB each, %dkB total.\n", + count, VBUFFER_SIZE, size / 1024); + + vgdev->vbufs = kzalloc(size, GFP_KERNEL); + if (!vgdev->vbufs) + return -ENOMEM; + + for (i = 0, ptr = vgdev->vbufs; + i < count; + i++, ptr += VBUFFER_SIZE) { + vbuf = ptr; + list_add(&vbuf->list, &vgdev->free_vbufs); + } + return 0; +} + +void virtio_gpu_free_vbufs(struct virtio_gpu_device *vgdev) +{ + struct virtio_gpu_vbuffer *vbuf; + int i, count = 0; + + count += virtqueue_get_vring_size(vgdev->ctrlq.vq); + count += virtqueue_get_vring_size(vgdev->cursorq.vq); + + for (i = 0; i < count; i++) { + if (WARN_ON(list_empty(&vgdev->free_vbufs))) + return; + vbuf = list_first_entry(&vgdev->free_vbufs, + struct virtio_gpu_vbuffer, list); + list_del(&vbuf->list); + } + kfree(vgdev->vbufs); +} + +static struct virtio_gpu_vbuffer* +virtio_gpu_get_vbuf(struct virtio_gpu_device *vgdev, + int size, int resp_size, void *resp_buf, + virtio_gpu_resp_cb resp_cb) +{ + struct virtio_gpu_vbuffer *vbuf; + + BUG_ON(list_empty(&vgdev->free_vbufs)); + vbuf = list_first_entry(&vgdev->free_vbufs, + struct virtio_gpu_vbuffer, list); + list_del(&vbuf->list); + memset(vbuf, 0, VBUFFER_SIZE); + + BUG_ON(size > MAX_INLINE_CMD_SIZE); + vbuf->buf = (void *)vbuf + sizeof(*vbuf); + vbuf->size = size; + + vbuf->resp_cb = resp_cb; + vbuf->resp_size = resp_size; + if (resp_size <= MAX_INLINE_RESP_SIZE) + vbuf->resp_buf = (void *)vbuf->buf + size; + else + vbuf->resp_buf = resp_buf; + BUG_ON(!vbuf->resp_buf); + return vbuf; +} + +static void *virtio_gpu_alloc_cmd(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer **vbuffer_p, + int size) +{ + struct virtio_gpu_vbuffer *vbuf; + + vbuf = virtio_gpu_get_vbuf(vgdev, size, + sizeof(struct virtio_gpu_ctrl_hdr), + NULL, NULL); + if (IS_ERR(vbuf)) { + *vbuffer_p = NULL; + return ERR_CAST(vbuf); + } + *vbuffer_p = vbuf; + return vbuf->buf; +} + +static struct virtio_gpu_update_cursor* +virtio_gpu_alloc_cursor(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer **vbuffer_p) +{ + struct virtio_gpu_vbuffer *vbuf; + + vbuf = virtio_gpu_get_vbuf + (vgdev, sizeof(struct virtio_gpu_update_cursor), + 0, NULL, NULL); + if (IS_ERR(vbuf)) { + *vbuffer_p = NULL; + return ERR_CAST(vbuf); + } + *vbuffer_p = vbuf; + return (struct virtio_gpu_update_cursor *)vbuf->buf; +} + +static void *virtio_gpu_alloc_cmd_resp(struct virtio_gpu_device *vgdev, + virtio_gpu_resp_cb cb, + struct virtio_gpu_vbuffer **vbuffer_p, + int cmd_size, int resp_size, + void *resp_buf) +{ + struct virtio_gpu_vbuffer *vbuf; + + vbuf = virtio_gpu_get_vbuf(vgdev, cmd_size, + resp_size, resp_buf, cb); + if (IS_ERR(vbuf)) { + *vbuffer_p = NULL; + return ERR_CAST(vbuf); + } + *vbuffer_p = vbuf; + return (struct virtio_gpu_command *)vbuf->buf; +} + +static void free_vbuf(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer *vbuf) +{ + if (vbuf->resp_size > MAX_INLINE_RESP_SIZE) + kfree(vbuf->resp_buf); + kfree(vbuf->data_buf); + list_add(&vbuf->list, &vgdev->free_vbufs); +} + +static void reclaim_vbufs(struct virtqueue *vq, struct list_head *reclaim_list) +{ + struct virtio_gpu_vbuffer *vbuf; + unsigned int len; + int freed = 0; + + while ((vbuf = virtqueue_get_buf(vq, &len))) { + list_add_tail(&vbuf->list, reclaim_list); + freed++; + } + if (freed == 0) + DRM_DEBUG("Huh? zero vbufs reclaimed"); +} + +void virtio_gpu_dequeue_ctrl_func(struct work_struct *work) +{ + struct virtio_gpu_device *vgdev = + container_of(work, struct virtio_gpu_device, + ctrlq.dequeue_work); + struct list_head reclaim_list; + struct virtio_gpu_vbuffer *entry, *tmp; + struct virtio_gpu_ctrl_hdr *resp; + u64 fence_id = 0; + + INIT_LIST_HEAD(&reclaim_list); + spin_lock(&vgdev->ctrlq.qlock); + do { + virtqueue_disable_cb(vgdev->ctrlq.vq); + reclaim_vbufs(vgdev->ctrlq.vq, &reclaim_list); + + } while (!virtqueue_enable_cb(vgdev->ctrlq.vq)); + spin_unlock(&vgdev->ctrlq.qlock); + + list_for_each_entry_safe(entry, tmp, &reclaim_list, list) { + resp = (struct virtio_gpu_ctrl_hdr *)entry->resp_buf; + if (resp->type != cpu_to_le32(VIRTIO_GPU_RESP_OK_NODATA)) + DRM_DEBUG("response 0x%x\n", le32_to_cpu(resp->type)); + if (resp->flags & cpu_to_le32(VIRTIO_GPU_FLAG_FENCE)) { + u64 f = le64_to_cpu(resp->fence_id); + + if (fence_id > f) { + DRM_ERROR("%s: Oops: fence %llx -> %llx\n", + __func__, fence_id, f); + } else { + fence_id = f; + } + } + if (entry->resp_cb) + entry->resp_cb(vgdev, entry); + + list_del(&entry->list); + free_vbuf(vgdev, entry); + } + wake_up(&vgdev->ctrlq.ack_queue); + + if (fence_id) + virtio_gpu_fence_event_process(vgdev, fence_id); +} + +void virtio_gpu_dequeue_cursor_func(struct work_struct *work) +{ + struct virtio_gpu_device *vgdev = + container_of(work, struct virtio_gpu_device, + cursorq.dequeue_work); + struct list_head reclaim_list; + struct virtio_gpu_vbuffer *entry, *tmp; + + INIT_LIST_HEAD(&reclaim_list); + spin_lock(&vgdev->cursorq.qlock); + do { + virtqueue_disable_cb(vgdev->cursorq.vq); + reclaim_vbufs(vgdev->cursorq.vq, &reclaim_list); + } while (!virtqueue_enable_cb(vgdev->cursorq.vq)); + spin_unlock(&vgdev->cursorq.qlock); + + list_for_each_entry_safe(entry, tmp, &reclaim_list, list) { + list_del(&entry->list); + free_vbuf(vgdev, entry); + } + wake_up(&vgdev->cursorq.ack_queue); +} + +static int virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer *vbuf) +{ + struct virtqueue *vq = vgdev->ctrlq.vq; + struct scatterlist *sgs[3], vcmd, vout, vresp; + int outcnt = 0, incnt = 0; + int ret; + + if (!vgdev->vqs_ready) + return -ENODEV; + + sg_init_one(&vcmd, vbuf->buf, vbuf->size); + sgs[outcnt+incnt] = &vcmd; + outcnt++; + + if (vbuf->data_size) { + sg_init_one(&vout, vbuf->data_buf, vbuf->data_size); + sgs[outcnt + incnt] = &vout; + outcnt++; + } + + if (vbuf->resp_size) { + sg_init_one(&vresp, vbuf->resp_buf, vbuf->resp_size); + sgs[outcnt + incnt] = &vresp; + incnt++; + } + + spin_lock(&vgdev->ctrlq.qlock); +retry: + ret = virtqueue_add_sgs(vq, sgs, outcnt, incnt, vbuf, GFP_ATOMIC); + if (ret == -ENOSPC) { + spin_unlock(&vgdev->ctrlq.qlock); + wait_event(vgdev->ctrlq.ack_queue, vq->num_free); + spin_lock(&vgdev->ctrlq.qlock); + goto retry; + } else { + virtqueue_kick(vq); + } + spin_unlock(&vgdev->ctrlq.qlock); + + if (!ret) + ret = vq->num_free; + return ret; +} + +static int virtio_gpu_queue_cursor(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer *vbuf) +{ + struct virtqueue *vq = vgdev->cursorq.vq; + struct scatterlist *sgs[1], ccmd; + int ret; + int outcnt; + + if (!vgdev->vqs_ready) + return -ENODEV; + + sg_init_one(&ccmd, vbuf->buf, vbuf->size); + sgs[0] = &ccmd; + outcnt = 1; + + spin_lock(&vgdev->cursorq.qlock); +retry: + ret = virtqueue_add_sgs(vq, sgs, outcnt, 0, vbuf, GFP_ATOMIC); + if (ret == -ENOSPC) { + spin_unlock(&vgdev->cursorq.qlock); + wait_event(vgdev->cursorq.ack_queue, vq->num_free); + spin_lock(&vgdev->cursorq.qlock); + goto retry; + } else { + virtqueue_kick(vq); + } + + spin_unlock(&vgdev->cursorq.qlock); + + if (!ret) + ret = vq->num_free; + return ret; +} + +/* just create gem objects for userspace and long lived objects, + just use dma_alloced pages for the queue objects? */ + +/* create a basic resource */ +void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev, + uint32_t resource_id, + uint32_t format, + uint32_t width, + uint32_t height) +{ + struct virtio_gpu_resource_create_2d *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_RESOURCE_CREATE_2D); + cmd_p->resource_id = cpu_to_le32(resource_id); + cmd_p->format = cpu_to_le32(format); + cmd_p->width = cpu_to_le32(width); + cmd_p->height = cpu_to_le32(height); + + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); +} + +void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev, + uint32_t resource_id) +{ + struct virtio_gpu_resource_unref *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_RESOURCE_UNREF); + cmd_p->resource_id = cpu_to_le32(resource_id); + + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); +} + +void virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev, + uint32_t resource_id) +{ + struct virtio_gpu_resource_detach_backing *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_RESOURCE_DETACH_BACKING); + cmd_p->resource_id = cpu_to_le32(resource_id); + + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); +} + +void virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev, + uint32_t scanout_id, uint32_t resource_id, + uint32_t width, uint32_t height, + uint32_t x, uint32_t y) +{ + struct virtio_gpu_set_scanout *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_SET_SCANOUT); + cmd_p->resource_id = cpu_to_le32(resource_id); + cmd_p->scanout_id = cpu_to_le32(scanout_id); + cmd_p->r.width = cpu_to_le32(width); + cmd_p->r.height = cpu_to_le32(height); + cmd_p->r.x = cpu_to_le32(x); + cmd_p->r.y = cpu_to_le32(y); + + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); +} + +void virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev, + uint32_t resource_id, + uint32_t x, uint32_t y, + uint32_t width, uint32_t height) +{ + struct virtio_gpu_resource_flush *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_RESOURCE_FLUSH); + cmd_p->resource_id = cpu_to_le32(resource_id); + cmd_p->r.width = cpu_to_le32(width); + cmd_p->r.height = cpu_to_le32(height); + cmd_p->r.x = cpu_to_le32(x); + cmd_p->r.y = cpu_to_le32(y); + + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); +} + +void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev, + uint32_t resource_id, uint64_t offset, + __le32 width, __le32 height, + __le32 x, __le32 y, + struct virtio_gpu_fence **fence) +{ + struct virtio_gpu_transfer_to_host_2d *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_2D); + cmd_p->resource_id = cpu_to_le32(resource_id); + cmd_p->offset = cpu_to_le64(offset); + cmd_p->r.width = width; + cmd_p->r.height = height; + 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); +} + +static void +virtio_gpu_cmd_resource_attach_backing(struct virtio_gpu_device *vgdev, + uint32_t resource_id, + struct virtio_gpu_mem_entry *ents, + uint32_t nents, + struct virtio_gpu_fence **fence) +{ + struct virtio_gpu_resource_attach_backing *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_RESOURCE_ATTACH_BACKING); + cmd_p->resource_id = cpu_to_le32(resource_id); + cmd_p->nr_entries = cpu_to_le32(nents); + + 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); +} + +static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer *vbuf) +{ + struct virtio_gpu_resp_display_info *resp = + (struct virtio_gpu_resp_display_info *)vbuf->resp_buf; + int i; + + spin_lock(&vgdev->display_info_lock); + for (i = 0; i < vgdev->num_scanouts; i++) { + vgdev->outputs[i].info = resp->pmodes[i]; + if (resp->pmodes[i].enabled) { + DRM_DEBUG("output %d: %dx%d+%d+%d", i, + le32_to_cpu(resp->pmodes[i].r.width), + le32_to_cpu(resp->pmodes[i].r.height), + le32_to_cpu(resp->pmodes[i].r.x), + le32_to_cpu(resp->pmodes[i].r.y)); + } else { + DRM_DEBUG("output %d: disabled", i); + } + } + + spin_unlock(&vgdev->display_info_lock); + wake_up(&vgdev->resp_wq); + + if (!drm_helper_hpd_irq_event(vgdev->ddev)) + drm_kms_helper_hotplug_event(vgdev->ddev); +} + +int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev) +{ + struct virtio_gpu_ctrl_hdr *cmd_p; + struct virtio_gpu_vbuffer *vbuf; + void *resp_buf; + + resp_buf = kzalloc(sizeof(struct virtio_gpu_resp_display_info), + GFP_KERNEL); + if (!resp_buf) + return -ENOMEM; + + cmd_p = virtio_gpu_alloc_cmd_resp + (vgdev, &virtio_gpu_cmd_get_display_info_cb, &vbuf, + sizeof(*cmd_p), sizeof(struct virtio_gpu_resp_display_info), + resp_buf); + memset(cmd_p, 0, sizeof(*cmd_p)); + + cmd_p->type = cpu_to_le32(VIRTIO_GPU_CMD_GET_DISPLAY_INFO); + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); + return 0; +} + +int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, + struct virtio_gpu_object *obj, + uint32_t resource_id, + struct virtio_gpu_fence **fence) +{ + struct virtio_gpu_mem_entry *ents; + struct scatterlist *sg; + int si; + + if (!obj->pages) { + int ret; + ret = virtio_gpu_object_get_sg_table(vgdev, obj); + if (ret) + return ret; + } + + /* gets freed when the ring has consumed it */ + ents = kmalloc_array(obj->pages->nents, + sizeof(struct virtio_gpu_mem_entry), + GFP_KERNEL); + if (!ents) { + DRM_ERROR("failed to allocate ent list\n"); + return -ENOMEM; + } + + for_each_sg(obj->pages->sgl, sg, obj->pages->nents, si) { + ents[si].addr = cpu_to_le64(sg_phys(sg)); + ents[si].length = cpu_to_le32(sg->length); + ents[si].padding = 0; + } + + virtio_gpu_cmd_resource_attach_backing(vgdev, resource_id, + ents, obj->pages->nents, + fence); + obj->hw_res_handle = resource_id; + return 0; +} + +void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev, + struct virtio_gpu_output *output) +{ + struct virtio_gpu_vbuffer *vbuf; + struct virtio_gpu_update_cursor *cur_p; + + output->cursor.pos.scanout_id = cpu_to_le32(output->index); + cur_p = virtio_gpu_alloc_cursor(vgdev, &vbuf); + memcpy(cur_p, &output->cursor, sizeof(output->cursor)); + virtio_gpu_queue_cursor(vgdev, vbuf); +} diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 62c40777..1544bb1 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -812,6 +812,7 @@ struct drm_device { #endif struct platform_device *platformdev; /**< Platform device struture */ + struct virtio_device *virtdev; struct drm_sg_mem *sg; /**< Scatter gather memory */ unsigned int num_crtcs; /**< Number of CRTCs on this device */ diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 1a0006a..4460e58 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -430,6 +430,7 @@ header-y += virtio_balloon.h header-y += virtio_blk.h header-y += virtio_config.h header-y += virtio_console.h +header-y += virtio_gpu.h header-y += virtio_ids.h header-y += virtio_input.h header-y += virtio_net.h diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h new file mode 100644 index 0000000..571c4cb --- /dev/null +++ b/include/uapi/linux/virtio_gpu.h @@ -0,0 +1,204 @@ +/* + * Virtio GPU Device + * + * Copyright Red Hat, Inc. 2013-2014 + * + * Authors: + * Dave Airlie <airlied@redhat.com> + * Gerd Hoffmann <kraxel@redhat.com> + * + * This header is BSD licensed so anyone can use the definitions + * to implement compatible drivers/servers: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef VIRTIO_GPU_HW_H +#define VIRTIO_GPU_HW_H + +enum virtio_gpu_ctrl_type { + VIRTIO_GPU_UNDEFINED = 0, + + /* 2d commands */ + VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100, + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, + VIRTIO_GPU_CMD_RESOURCE_UNREF, + VIRTIO_GPU_CMD_SET_SCANOUT, + VIRTIO_GPU_CMD_RESOURCE_FLUSH, + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, + VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, + VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, + + /* cursor commands */ + VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300, + VIRTIO_GPU_CMD_MOVE_CURSOR, + + /* success responses */ + VIRTIO_GPU_RESP_OK_NODATA = 0x1100, + VIRTIO_GPU_RESP_OK_DISPLAY_INFO, + + /* error responses */ + VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200, + VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, + VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID, + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, +}; + +#define VIRTIO_GPU_FLAG_FENCE (1 << 0) + +struct virtio_gpu_ctrl_hdr { + __le32 type; + __le32 flags; + __le64 fence_id; + __le32 ctx_id; + __le32 padding; +}; + +/* data passed in the cursor vq */ + +struct virtio_gpu_cursor_pos { + __le32 scanout_id; + __le32 x; + __le32 y; + __le32 padding; +}; + +/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */ +struct virtio_gpu_update_cursor { + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_cursor_pos pos; /* update & move */ + __le32 resource_id; /* update only */ + __le32 hot_x; /* update only */ + __le32 hot_y; /* update only */ + __le32 padding; +}; + +/* data passed in the control vq, 2d related */ + +struct virtio_gpu_rect { + __le32 x; + __le32 y; + __le32 width; + __le32 height; +}; + +/* VIRTIO_GPU_CMD_RESOURCE_UNREF */ +struct virtio_gpu_resource_unref { + struct virtio_gpu_ctrl_hdr hdr; + __le32 resource_id; + __le32 padding; +}; + +/* VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: create a 2d resource with a format */ +struct virtio_gpu_resource_create_2d { + struct virtio_gpu_ctrl_hdr hdr; + __le32 resource_id; + __le32 format; + __le32 width; + __le32 height; +}; + +/* VIRTIO_GPU_CMD_SET_SCANOUT */ +struct virtio_gpu_set_scanout { + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_rect r; + __le32 scanout_id; + __le32 resource_id; +}; + +/* VIRTIO_GPU_CMD_RESOURCE_FLUSH */ +struct virtio_gpu_resource_flush { + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_rect r; + __le32 resource_id; + __le32 padding; +}; + +/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: simple transfer to_host */ +struct virtio_gpu_transfer_to_host_2d { + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_rect r; + __le64 offset; + __le32 resource_id; + __le32 padding; +}; + +struct virtio_gpu_mem_entry { + __le64 addr; + __le32 length; + __le32 padding; +}; + +/* VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING */ +struct virtio_gpu_resource_attach_backing { + struct virtio_gpu_ctrl_hdr hdr; + __le32 resource_id; + __le32 nr_entries; +}; + +/* VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING */ +struct virtio_gpu_resource_detach_backing { + struct virtio_gpu_ctrl_hdr hdr; + __le32 resource_id; + __le32 padding; +}; + +/* VIRTIO_GPU_RESP_OK_DISPLAY_INFO */ +#define VIRTIO_GPU_MAX_SCANOUTS 16 +struct virtio_gpu_resp_display_info { + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_display_one { + struct virtio_gpu_rect r; + __le32 enabled; + __le32 flags; + } pmodes[VIRTIO_GPU_MAX_SCANOUTS]; +}; + +#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0) + +struct virtio_gpu_config { + __u32 events_read; + __u32 events_clear; + __u32 num_scanouts; + __u32 reserved; +}; + +/* simple formats for fbcon/X use */ +enum virtio_gpu_formats { + VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM = 1, + VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM = 2, + VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM = 3, + VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM = 4, + + VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM = 67, + VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM = 68, + + VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM = 121, + VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM = 134, +}; + +#endif diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index 5f60aa4..77925f5 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/linux/virtio_ids.h @@ -39,6 +39,7 @@ #define VIRTIO_ID_9P 9 /* 9p virtio console */ #define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */ #define VIRTIO_ID_CAIF 12 /* Virtio caif */ +#define VIRTIO_ID_GPU 16 /* virtio GPU */ #define VIRTIO_ID_INPUT 18 /* virtio input */ #endif /* _LINUX_VIRTIO_IDS_H */