summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@gmail.com>2013-09-09 10:53:35 +1000
committerDave Airlie <airlied@gmail.com>2013-09-09 10:53:35 +1000
commitf92ebf1f3ba73e2c06b3acca0dcc55229054c89d (patch)
tree21de40e7a59004f1905ce34c4d3b55eea1c91636
parent1ad3b28fe38a769a62fec053496e0eb6d55ad746 (diff)
further boilerplating
-rw-r--r--drivers/gpu/drm/virtio/Makefile2
-rw-r--r--drivers/gpu/drm/virtio/virtio_display.c512
-rw-r--r--drivers/gpu/drm/virtio/virtio_drv.c37
-rw-r--r--drivers/gpu/drm/virtio/virtio_drv.h85
-rw-r--r--drivers/gpu/drm/virtio/virtio_fb.c109
-rw-r--r--drivers/gpu/drm/virtio/virtio_gem.c26
-rw-r--r--drivers/gpu/drm/virtio/virtio_kms.c37
-rw-r--r--drivers/gpu/drm/virtio/virtio_object.c31
-rw-r--r--drivers/gpu/drm/virtio/virtio_ttm.c121
-rw-r--r--include/drm/drmP.h1
10 files changed, 934 insertions, 27 deletions
diff --git a/drivers/gpu/drm/virtio/Makefile b/drivers/gpu/drm/virtio/Makefile
index c59113cab259..3d62b402f6d2 100644
--- a/drivers/gpu/drm/virtio/Makefile
+++ b/drivers/gpu/drm/virtio/Makefile
@@ -4,6 +4,6 @@
ccflags-y := -Iinclude/drm
-virtio-gpu-y := virtio_drv.o virtio_kms.o virtio_drm_bus.o
+virtio-gpu-y := virtio_drv.o virtio_kms.o virtio_drm_bus.o virtio_gem.o virtio_ttm.o virtio_object.o virtio_fb.o virtio_display.o
obj-$(CONFIG_DRM_VIRTIO_GPU)+= virtio-gpu.o
diff --git a/drivers/gpu/drm/virtio/virtio_display.c b/drivers/gpu/drm/virtio/virtio_display.c
new file mode 100644
index 000000000000..9e8e0e36ed5d
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtio_display.c
@@ -0,0 +1,512 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Dave Airlie
+ * Alon Levy
+ */
+
+#include "virtio_drv.h"
+#include "drm_crtc_helper.h"
+
+static int virtgpu_add_common_modes(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_display_mode *mode = NULL;
+ int i;
+ struct mode_size {
+ int w;
+ int h;
+ } common_modes[] = {
+ { 640, 480},
+ { 720, 480},
+ { 800, 600},
+ { 848, 480},
+ {1024, 768},
+ {1152, 768},
+ {1280, 720},
+ {1280, 800},
+ {1280, 854},
+ {1280, 960},
+ {1280, 1024},
+ {1440, 900},
+ {1400, 1050},
+ {1680, 1050},
+ {1600, 1200},
+ {1920, 1080},
+ {1920, 1200}
+ };
+
+ for (i = 0; i < ARRAY_SIZE(common_modes); i++) {
+ if (common_modes[i].w < 320 || common_modes[i].h < 200)
+ continue;
+
+ mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h,
+ 60, false, false, false);
+ if (common_modes[i].w == 1024 && common_modes[i].h == 768)
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector, mode);
+ }
+ return i - 1;
+}
+
+static void virtgpu_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
+ u16 *blue, uint32_t start, uint32_t size)
+{
+ /* TODO */
+}
+
+static void virtgpu_crtc_destroy(struct drm_crtc *crtc)
+{
+ struct virtgpu_crtc *virtgpu_crtc = to_virtgpu_crtc(crtc);
+
+ drm_crtc_cleanup(crtc);
+ kfree(virtgpu_crtc);
+}
+
+static void
+virtgpu_hide_cursor(struct virtgpu_device *vgdev)
+{
+
+}
+
+static int virtgpu_crtc_cursor_set(struct drm_crtc *crtc,
+ struct drm_file *file_priv,
+ uint32_t handle,
+ uint32_t width,
+ uint32_t height)
+{
+ struct virtgpu_device *vgdev = crtc->dev->dev_private;
+ struct drm_gem_object *gobj = NULL;
+ struct virtgpu_bo *qobj = NULL;
+ int ret = 0;
+ if (handle == 0) {
+ virtgpu_hide_cursor(vgdev);
+ return 0;
+ }
+
+ /* lookup the cursor */
+ gobj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
+ if (gobj == NULL)
+ return -ENOENT;
+
+ qobj = gem_to_virtgpu_bo(gobj);
+
+ if (!qobj->hw_res_handle) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+out:
+ drm_gem_object_unreference_unlocked(gobj);
+ return ret;
+}
+
+static int virtgpu_crtc_cursor_move(struct drm_crtc *crtc,
+ int x, int y)
+{
+ struct virtgpu_device *vgdev = crtc->dev->dev_private;
+ return 0;
+}
+
+static int virtgpu_crtc_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event)
+{
+ return -EINVAL;
+}
+
+
+static const struct drm_crtc_funcs virtgpu_crtc_funcs = {
+ .cursor_set = virtgpu_crtc_cursor_set,
+ .cursor_move = virtgpu_crtc_cursor_move,
+ .gamma_set = virtgpu_crtc_gamma_set,
+ .set_config = drm_crtc_helper_set_config,
+ .page_flip = virtgpu_crtc_page_flip,
+ .destroy = virtgpu_crtc_destroy,
+};
+
+static void virtgpu_user_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+ struct virtgpu_framebuffer *virtgpu_fb = to_virtgpu_framebuffer(fb);
+
+ if (virtgpu_fb->obj)
+ drm_gem_object_unreference_unlocked(virtgpu_fb->obj);
+ drm_framebuffer_cleanup(fb);
+ kfree(virtgpu_fb);
+}
+
+int virtgpu_framebuffer_surface_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned flags, unsigned color,
+ struct drm_clip_rect *clips,
+ unsigned num_clips)
+{
+ return -EINVAL;
+}
+
+static const struct drm_framebuffer_funcs virtgpu_fb_funcs = {
+ .destroy = virtgpu_user_framebuffer_destroy,
+ .dirty = virtgpu_framebuffer_surface_dirty,
+};
+
+int
+virtgpu_framebuffer_init(struct drm_device *dev,
+ struct virtgpu_framebuffer *vgfb,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object *obj)
+{
+ int ret;
+ struct virtgpu_bo *bo;
+ vgfb->obj = obj;
+
+ bo = gem_to_virtgpu_bo(obj);
+// vgfb->res_3d_handle = bo->res_handle;
+
+ ret = drm_framebuffer_init(dev, &vgfb->base, &virtgpu_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 void virtgpu_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+}
+
+static bool virtgpu_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = crtc->dev;
+ struct virtgpu_device *vgdev = dev->dev_private;
+
+ return true;
+}
+
+static int virtgpu_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct drm_device *dev = crtc->dev;
+ struct virtgpu_device *vgdev = dev->dev_private;
+ struct virtgpu_framebuffer *vgfb;
+ struct virtgpu_bo *bo, *old_bo = NULL;
+ int ret;
+
+ if (!crtc->fb) {
+ DRM_DEBUG_KMS("No FB bound\n");
+ return 0;
+ }
+
+ if (old_fb) {
+ vgfb = to_virtgpu_framebuffer(old_fb);
+ old_bo = gem_to_virtgpu_bo(vgfb->obj);
+ }
+ vgfb = to_virtgpu_framebuffer(crtc->fb);
+ bo = gem_to_virtgpu_bo(vgfb->obj);
+ DRM_DEBUG("+%d+%d (%d,%d) => (%d,%d)\n",
+ x, y,
+ mode->hdisplay, mode->vdisplay,
+ adjusted_mode->hdisplay,
+ adjusted_mode->vdisplay);
+
+ return 0;
+}
+
+static void virtgpu_crtc_prepare(struct drm_crtc *crtc)
+{
+ DRM_DEBUG("current: %dx%d+%d+%d (%d).\n",
+ crtc->mode.hdisplay, crtc->mode.vdisplay,
+ crtc->x, crtc->y, crtc->enabled);
+}
+
+static void virtgpu_crtc_commit(struct drm_crtc *crtc)
+{
+ DRM_DEBUG("\n");
+}
+
+void virtgpu_crtc_load_lut(struct drm_crtc *crtc)
+{
+ DRM_DEBUG("\n");
+}
+
+static const struct drm_crtc_helper_funcs virtgpu_crtc_helper_funcs = {
+ .dpms = virtgpu_crtc_dpms,
+ .mode_fixup = virtgpu_crtc_mode_fixup,
+ .mode_set = virtgpu_crtc_mode_set,
+ .prepare = virtgpu_crtc_prepare,
+ .commit = virtgpu_crtc_commit,
+ .load_lut = virtgpu_crtc_load_lut,
+};
+
+int vgdev_crtc_init(struct drm_device *dev, int num_crtc)
+{
+ struct virtgpu_crtc *virtgpu_crtc;
+
+ virtgpu_crtc = kzalloc(sizeof(struct virtgpu_crtc), GFP_KERNEL);
+ if (!virtgpu_crtc)
+ return -ENOMEM;
+
+ drm_crtc_init(dev, &virtgpu_crtc->base, &virtgpu_crtc_funcs);
+
+ drm_mode_crtc_set_gamma_size(&virtgpu_crtc->base, 256);
+ drm_crtc_helper_add(&virtgpu_crtc->base, &virtgpu_crtc_helper_funcs);
+ return 0;
+}
+
+static void virtgpu_enc_dpms(struct drm_encoder *encoder, int mode)
+{
+ DRM_DEBUG("\n");
+}
+
+static bool virtgpu_enc_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ DRM_DEBUG("\n");
+ return true;
+}
+
+static void virtgpu_enc_prepare(struct drm_encoder *encoder)
+{
+ DRM_DEBUG("\n");
+}
+
+static void virtgpu_enc_commit(struct drm_encoder *encoder)
+{
+ DRM_DEBUG("\n");
+}
+
+static void virtgpu_enc_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ DRM_DEBUG("\n");
+}
+
+static int virtgpu_conn_get_modes(struct drm_connector *connector)
+{
+ int ret = 0;
+ struct virtgpu_device *vgdev = connector->dev->dev_private;
+
+ ret += virtgpu_add_common_modes(connector);
+ return ret;
+}
+
+static int virtgpu_conn_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+struct drm_encoder *virtgpu_best_encoder(struct drm_connector *connector)
+{
+ struct virtgpu_output *virtgpu_output =
+ drm_connector_to_virtgpu_output(connector);
+
+ DRM_DEBUG("\n");
+ return &virtgpu_output->enc;
+}
+
+
+static const struct drm_encoder_helper_funcs virtgpu_enc_helper_funcs = {
+ .dpms = virtgpu_enc_dpms,
+ .mode_fixup = virtgpu_enc_mode_fixup,
+ .prepare = virtgpu_enc_prepare,
+ .mode_set = virtgpu_enc_mode_set,
+ .commit = virtgpu_enc_commit,
+};
+
+static const struct drm_connector_helper_funcs virtgpu_connector_helper_funcs = {
+ .get_modes = virtgpu_conn_get_modes,
+ .mode_valid = virtgpu_conn_mode_valid,
+ .best_encoder = virtgpu_best_encoder,
+};
+
+static void virtgpu_conn_save(struct drm_connector *connector)
+{
+ DRM_DEBUG("\n");
+}
+
+static void virtgpu_conn_restore(struct drm_connector *connector)
+{
+ DRM_DEBUG("\n");
+}
+
+static enum drm_connector_status virtgpu_conn_detect(
+ struct drm_connector *connector,
+ bool force)
+{
+ struct virtgpu_output *output =
+ drm_connector_to_virtgpu_output(connector);
+ struct drm_device *ddev = connector->dev;
+ struct virtgpu_device *vgdev = ddev->dev_private;
+ int connected;
+
+ return connector_status_connected;
+}
+
+static int virtgpu_conn_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t value)
+{
+ DRM_DEBUG("\n");
+ return 0;
+}
+
+static void virtgpu_conn_destroy(struct drm_connector *connector)
+{
+ struct virtgpu_output *virtgpu_output =
+ drm_connector_to_virtgpu_output(connector);
+
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(virtgpu_output);
+}
+
+static const struct drm_connector_funcs virtgpu_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .save = virtgpu_conn_save,
+ .restore = virtgpu_conn_restore,
+ .detect = virtgpu_conn_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .set_property = virtgpu_conn_set_property,
+ .destroy = virtgpu_conn_destroy,
+};
+
+static void virtgpu_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs virtgpu_enc_funcs = {
+ .destroy = virtgpu_enc_destroy,
+};
+
+int vgdev_output_init(struct drm_device *dev, int num_output)
+{
+ struct virtgpu_output *virtgpu_output;
+ struct drm_connector *connector;
+ struct drm_encoder *encoder;
+
+ virtgpu_output = kzalloc(sizeof(struct virtgpu_output), GFP_KERNEL);
+ if (!virtgpu_output)
+ return -ENOMEM;
+
+ virtgpu_output->index = num_output;
+
+ connector = &virtgpu_output->base;
+ encoder = &virtgpu_output->enc;
+ drm_connector_init(dev, &virtgpu_output->base,
+ &virtgpu_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL);
+
+ drm_encoder_init(dev, &virtgpu_output->enc, &virtgpu_enc_funcs,
+ DRM_MODE_ENCODER_VIRTUAL);
+
+ encoder->possible_crtcs = 1 << num_output;
+ drm_mode_connector_attach_encoder(&virtgpu_output->base,
+ &virtgpu_output->enc);
+ drm_encoder_helper_add(encoder, &virtgpu_enc_helper_funcs);
+ drm_connector_helper_add(connector, &virtgpu_connector_helper_funcs);
+
+ drm_sysfs_connector_add(connector);
+ return 0;
+}
+
+static struct drm_framebuffer *
+virtgpu_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 virtgpu_framebuffer *virtgpu_fb;
+ struct virtgpu_device *vgdev = dev->dev_private;
+ 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);
+
+ virtgpu_fb = kzalloc(sizeof(*virtgpu_fb), GFP_KERNEL);
+ if (virtgpu_fb == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ ret = virtgpu_framebuffer_init(dev, virtgpu_fb, mode_cmd, obj);
+ if (ret) {
+ kfree(virtgpu_fb);
+ if (obj)
+ drm_gem_object_unreference_unlocked(obj);
+ return NULL;
+ }
+
+ return &virtgpu_fb->base;
+}
+
+static const struct drm_mode_config_funcs virtgpu_mode_funcs = {
+ .fb_create = virtgpu_user_framebuffer_create,
+};
+
+int virtgpu_modeset_init(struct virtgpu_device *vgdev)
+{
+ int i;
+ int ret;
+ struct drm_gem_object *gobj;
+ int max_allowed = VIRTGPU_NUM_OUTPUTS;
+
+ drm_mode_config_init(vgdev->ddev);
+ vgdev->ddev->mode_config.funcs = (void *)&virtgpu_mode_funcs;
+
+ /* modes will be validated against the framebuffer size */
+ vgdev->ddev->mode_config.min_width = 320;
+ vgdev->ddev->mode_config.min_height = 200;
+ vgdev->ddev->mode_config.max_width = 8192;
+ vgdev->ddev->mode_config.max_height = 8192;
+
+// vgdev->ddev->mode_config.fb_base = vgdev->vram_base;
+ for (i = 0 ; i < VIRTGPU_NUM_OUTPUTS; ++i) {
+ vgdev_crtc_init(vgdev->ddev, i);
+ vgdev_output_init(vgdev->ddev, i);
+ }
+
+ /* primary surface must be created by this point, to allow
+ * issuing command queue commands and having them read by
+ * spice server. */
+ virtgpu_fbdev_init(vgdev);
+
+ ret = drm_vblank_init(vgdev->ddev, 1);
+
+ return ret;
+}
+
+void virtgpu_modeset_fini(struct virtgpu_device *vgdev)
+{
+ virtgpu_fbdev_fini(vgdev);
+ drm_mode_config_cleanup(vgdev->ddev);
+}
diff --git a/drivers/gpu/drm/virtio/virtio_drv.c b/drivers/gpu/drm/virtio/virtio_drv.c
index 6a4c5bfeb90e..77753d6c53ee 100644
--- a/drivers/gpu/drm/virtio/virtio_drv.c
+++ b/drivers/gpu/drm/virtio/virtio_drv.c
@@ -35,26 +35,26 @@
#include "virtio_drv.h"
static struct drm_driver driver;
-int virtio_gpu_modeset = -1;
+int virtgpu_modeset = -1;
MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
-module_param_named(modeset, virtio_gpu_modeset, int, 0400);
+module_param_named(modeset, virtgpu_modeset, int, 0400);
-extern int virtio_gpu_max_ioctls;
+extern int virtgpu_max_ioctls;
-static int virtio_gpu_probe(struct virtio_device *vdev)
+static int virtgpu_probe(struct virtio_device *vdev)
{
struct drm_device *dev;
int ret;
#ifdef CONFIG_VGA_CONSOLE
- if (vgacon_text_force() && virtio_gpu_modeset == -1)
+ if (vgacon_text_force() && virtgpu_modeset == -1)
return -EINVAL;
#endif
- if (virtio_gpu_modeset == 0)
+ if (virtgpu_modeset == 0)
return -EINVAL;
- driver.num_ioctls = virtio_gpu_max_ioctls;
+ driver.num_ioctls = virtgpu_max_ioctls;
virtio_set_driver_bus(&driver);
INIT_LIST_HEAD(&driver.device_list);
@@ -62,6 +62,7 @@ static int virtio_gpu_probe(struct virtio_device *vdev)
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
dev->dev = &vdev->dev;
+ dev->virtdev = vdev;
vdev->priv = dev;
mutex_lock(&drm_global_mutex);
@@ -109,7 +110,7 @@ err_g1:
return 0;
}
-static void virtio_gpu_remove(struct virtio_device *vdev)
+static void virtgpu_remove(struct virtio_device *vdev)
{
struct drm_device *dev = vdev->priv;
drm_put_dev(dev);
@@ -120,21 +121,21 @@ static struct virtio_device_id id_table[] = {
{ 0 },
};
-static struct virtio_driver virtio_gpu_driver = {
+static struct virtio_driver virtgpu_driver = {
.driver.name = KBUILD_MODNAME,
.driver.owner = THIS_MODULE,
.id_table = id_table,
- .probe = virtio_gpu_probe,
- .remove = virtio_gpu_remove,
+ .probe = virtgpu_probe,
+ .remove = virtgpu_remove,
};
-module_virtio_driver(virtio_gpu_driver);
+module_virtio_driver(virtgpu_driver);
MODULE_DEVICE_TABLE(virtio, id_table);
MODULE_DESCRIPTION("Virtio GPU driver");
MODULE_LICENSE("GPL");
-static const struct file_operations virtio_gpu_driver_fops = {
+static const struct file_operations virtgpu_driver_fops = {
.owner = THIS_MODULE,
.open = drm_open,
// .mmap = udl_drm_gem_mmap,
@@ -150,11 +151,13 @@ static const struct file_operations virtio_gpu_driver_fops = {
};
static struct drm_driver driver = {
- .driver_features = DRIVER_MODESET,
- .load = virtio_gpu_driver_load,
- .unload = virtio_gpu_driver_unload,
+ .driver_features = DRIVER_MODESET | DRIVER_GEM,
+ .load = virtgpu_driver_load,
+ .unload = virtgpu_driver_unload,
- .fops = &virtio_gpu_driver_fops,
+ .gem_init_object = virtgpu_gem_object_init,
+ .gem_free_object = virtgpu_gem_object_free,
+ .fops = &virtgpu_driver_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
diff --git a/drivers/gpu/drm/virtio/virtio_drv.h b/drivers/gpu/drm/virtio/virtio_drv.h
index 0479c235f3e7..18c0ed10cf03 100644
--- a/drivers/gpu/drm/virtio/virtio_drv.h
+++ b/drivers/gpu/drm/virtio/virtio_drv.h
@@ -9,6 +9,13 @@
#ifndef VIRTIO_DRV_H
#define VIRTIO_DRV_H
+#include <drm/drmP.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 ""
@@ -17,13 +24,85 @@
#define DRIVER_MINOR 0
#define DRIVER_PATCHLEVEL 1
+#define VIRTGPU_NUM_OUTPUTS 1
+
void virtio_set_driver_bus(struct drm_driver *driver);
-struct virtio_gpu_device {
+struct virtgpu_bo {
+ struct drm_gem_object gem_base;
+ struct ttm_buffer_object tbo;
+ struct ttm_bo_kmap_obj kmap;
+ struct ttm_placement placement;
+ u32 placements[3];
+ uint32_t hw_res_handle;
+};
+#define gem_to_virtgpu_bo(gobj) container_of((gobj), struct virtgpu_bo, gem_base)
+
+struct virtgpu_crtc {
+ struct drm_crtc base;
+ int cur_x;
+ int cur_y;
+};
+
+struct virtgpu_output {
+ int index;
+ struct drm_connector base;
+ struct drm_encoder enc;
+};
+
+struct virtgpu_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_virtgpu_crtc(x) container_of(x, struct virtgpu_crtc, base)
+#define drm_connector_to_virtgpu_output(x) container_of(x, struct virtgpu_output, base)
+#define drm_encoder_to_virtgpu_output(x) container_of(x, struct virtgpu_output, base)
+#define to_virtgpu_framebuffer(x) container_of(x, struct virtgpu_framebuffer, base)
+
+struct virtgpu_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 virtgpu_fbdev;
+
+struct virtgpu_device {
struct device *dev;
struct drm_device *ddev;
+
+ struct virtio_device *vdev;
+
+ struct virtgpu_mman mman;
+
+ /* pointer to fbdev info structure */
+ struct virtgpu_fbdev *vgfbdev;
+
+ struct virtqueue *ctrlq;
};
-int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags);
-int virtio_gpu_driver_unload(struct drm_device *dev);
+int virtgpu_driver_load(struct drm_device *dev, unsigned long flags);
+int virtgpu_driver_unload(struct drm_device *dev);
+
+/* virtio_gem.c */
+int virtgpu_gem_object_init(struct drm_gem_object *obj);
+void virtgpu_gem_object_free(struct drm_gem_object *gobj);
+int virtgpu_gem_init(struct virtgpu_device *qdev);
+void virtgpu_gem_fini(struct virtgpu_device *qdev);
+
+/* virtio_ttm.c */
+int virtgpu_ttm_init(struct virtgpu_device *vgdev);
+void virtgpu_ttm_fini(struct virtgpu_device *vgdev);
+
+/* virtio_object */
+extern void virtgpu_bo_unref(struct virtgpu_bo **bo);
+
+/* virtio_fb */
+#define VIRTGPUFB_CONN_LIMIT 1
+int virtgpu_fbdev_init(struct virtgpu_device *vgdev);
+void virtgpu_fbdev_fini(struct virtgpu_device *vgdev);
#endif
diff --git a/drivers/gpu/drm/virtio/virtio_fb.c b/drivers/gpu/drm/virtio/virtio_fb.c
new file mode 100644
index 000000000000..c11f9a7a7a52
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtio_fb.c
@@ -0,0 +1,109 @@
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include "virtio_drv.h"
+
+struct virtgpu_fbdev {
+ struct drm_fb_helper helper;
+ struct virtgpu_framebuffer vgfb;
+ struct list_head fbdev_list;
+ struct virtgpu_device *vgdev;
+ struct delayed_work work;
+};
+
+static struct fb_ops virglfb_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 = virgl_3d_fillrect,
+/// .fb_copyarea = virgl_3d_copyarea,
+// .fb_imageblit = virgl_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 virtgpufb_create(struct virtgpu_fbdev *qfbdev,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ return -EINVAL;
+}
+
+static int virtgpu_fb_find_or_create_single(
+ struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct virtgpu_fbdev *vgfbdev = (struct virtgpu_fbdev *)helper;
+ int new_fb = 0;
+ int ret;
+
+ if (!helper->fb) {
+ ret = virtgpufb_create(vgfbdev, sizes);
+ if (ret)
+ return ret;
+ new_fb = 1;
+ }
+ return new_fb;
+}
+
+static int virtgpu_fbdev_destroy(struct drm_device *dev, struct virtgpu_fbdev *vgfbdev)
+{
+ struct fb_info *info;
+ struct virtgpu_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 virtgpu_fb_helper_funcs = {
+ .fb_probe = virtgpu_fb_find_or_create_single,
+};
+
+int virtgpu_fbdev_init(struct virtgpu_device *vgdev)
+{
+ struct virtgpu_fbdev *vgfbdev;
+ int bpp_sel = 32; /* TODO: parameter from somewhere? */
+ int ret;
+
+ vgfbdev = kzalloc(sizeof(struct virtgpu_fbdev), GFP_KERNEL);
+ if (!vgfbdev)
+ return -ENOMEM;
+
+ vgfbdev->vgdev = vgdev;
+ vgdev->vgfbdev = vgfbdev;
+ vgfbdev->helper.funcs = &virtgpu_fb_helper_funcs;
+ // INIT_DELAYED_WORK(&vgfbdev->work, virtgpu_fb_dirty_work);
+
+ ret = drm_fb_helper_init(vgdev->ddev, &vgfbdev->helper,
+ 1 /* num_crtc - VIRTGPU supports just 1 */,
+ VIRTGPUFB_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 virtgpu_fbdev_fini(struct virtgpu_device *vgdev)
+{
+ if (!vgdev->vgfbdev)
+ return;
+
+ virtgpu_fbdev_destroy(vgdev->ddev, vgdev->vgfbdev);
+ kfree(vgdev->vgfbdev);
+ vgdev->vgfbdev = NULL;
+}
diff --git a/drivers/gpu/drm/virtio/virtio_gem.c b/drivers/gpu/drm/virtio/virtio_gem.c
new file mode 100644
index 000000000000..46672bb33b61
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtio_gem.c
@@ -0,0 +1,26 @@
+
+#include <drm/drmP.h>
+#include "virtio_drv.h"
+
+int virtgpu_gem_object_init(struct drm_gem_object *obj)
+{
+ /* we do nothings here */
+ return 0;
+}
+
+void virtgpu_gem_object_free(struct drm_gem_object *gobj)
+{
+ struct virtgpu_bo *qobj = gem_to_virtgpu_bo(gobj);
+
+ if (qobj)
+ virtgpu_bo_unref(&qobj);
+}
+
+int virtgpu_gem_init(struct virtgpu_device *qdev)
+{
+ return 0;
+}
+
+void virtgpu_gem_fini(struct virtgpu_device *qdev)
+{
+}
diff --git a/drivers/gpu/drm/virtio/virtio_kms.c b/drivers/gpu/drm/virtio/virtio_kms.c
index 0fedd63cc1a2..59d98a6103ef 100644
--- a/drivers/gpu/drm/virtio/virtio_kms.c
+++ b/drivers/gpu/drm/virtio/virtio_kms.c
@@ -1,25 +1,50 @@
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
#include <drm/drmP.h>
#include "virtio_drv.h"
-int virtio_gpu_max_ioctls;
+int virtgpu_max_ioctls;
-int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
+static void virtgpu_ctrl_ack(struct virtqueue *vq)
{
- struct virtio_gpu_device *vgdev;
- vgdev = kzalloc(sizeof(struct virtio_gpu_device), GFP_KERNEL);
+}
+
+int virtgpu_driver_load(struct drm_device *dev, unsigned long flags)
+{
+ struct virtgpu_device *vgdev;
+ /* this will expand later */
+ struct virtqueue *vqs[1];
+ vq_callback_t *callbacks[] = { virtgpu_ctrl_ack };
+ const char *names[] = { "control" };
+ int nvqs;
+ int ret;
+
+ vgdev = kzalloc(sizeof(struct virtgpu_device), GFP_KERNEL);
if (!vgdev)
return -ENOMEM;
vgdev->ddev = dev;
dev->dev_private = vgdev;
+ vgdev->vdev = dev->virtdev;
+
+ nvqs = 1;
+
+ ret = vgdev->vdev->config->find_vqs(vgdev->vdev, nvqs, vqs, callbacks, names);
+ if (ret) {
+ DRM_ERROR("failed to find virt queues\n");
+ kfree(vgdev);
+ return ret;
+ }
+
+ vgdev->ctrlq = vqs[0];
return 0;
}
-int virtio_gpu_driver_unload(struct drm_device *dev)
+int virtgpu_driver_unload(struct drm_device *dev)
{
- struct virtio_gpu_device *vgdev = dev->dev_private;
+ struct virtgpu_device *vgdev = dev->dev_private;
kfree(vgdev);
return 0;
diff --git a/drivers/gpu/drm/virtio/virtio_object.c b/drivers/gpu/drm/virtio/virtio_object.c
new file mode 100644
index 000000000000..898a774bb6c0
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtio_object.c
@@ -0,0 +1,31 @@
+#include <drm/drmP.h>
+#include "virtio_drv.h"
+
+static void virtgpu_ttm_bo_destroy(struct ttm_buffer_object *tbo)
+{
+ struct virtgpu_bo *bo;
+
+ bo = container_of(tbo, struct virtgpu_bo, tbo);
+
+ drm_gem_object_release(&bo->gem_base);
+ kfree(bo);
+}
+
+bool virtgpu_ttm_bo_is_virtgpu_bo(struct ttm_buffer_object *bo)
+{
+ if (bo->destroy == &virtgpu_ttm_bo_destroy)
+ return true;
+ return false;
+}
+
+void virtgpu_bo_unref(struct virtgpu_bo **bo)
+{
+ struct ttm_buffer_object *tbo;
+
+ if ((*bo) == NULL)
+ return;
+ tbo = &((*bo)->tbo);
+ ttm_bo_unref(&tbo);
+ if (tbo == NULL)
+ *bo = NULL;
+}
diff --git a/drivers/gpu/drm/virtio/virtio_ttm.c b/drivers/gpu/drm/virtio/virtio_ttm.c
new file mode 100644
index 000000000000..73a3daefdddf
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtio_ttm.c
@@ -0,0 +1,121 @@
+#include <drm/drmP.h>
+#include <drm/drm.h>
+
+#include "virtio_drv.h"
+
+
+#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
+
+static struct virtgpu_device *virtgpu_get_dev(struct ttm_bo_device *bdev)
+{
+ struct virtgpu_mman *mman;
+ struct virtgpu_device *vdev;
+
+ mman = container_of(bdev, struct virtgpu_mman, bdev);
+ vdev = container_of(mman, struct virtgpu_device, mman);
+ return vdev;
+}
+
+
+static int virtgpu_ttm_mem_global_init(struct drm_global_reference *ref)
+{
+ return ttm_mem_global_init(ref->object);
+}
+
+static void virtgpu_ttm_mem_global_release(struct drm_global_reference *ref)
+{
+ ttm_mem_global_release(ref->object);
+}
+
+static int virtgpu_ttm_global_init(struct virtgpu_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 = &virtgpu_ttm_mem_global_init;
+ global_ref->release = &virtgpu_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 virtgpu_ttm_global_fini(struct virtgpu_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;
+ }
+}
+
+static int virtgpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
+ struct ttm_mem_type_manager *man)
+{
+ struct virtgpu_device *vgdev;
+
+ vgdev = virtgpu_get_dev(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;
+ default:
+ DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct ttm_bo_driver virtgpu_bo_driver = {
+ .init_mem_type = &virtgpu_init_mem_type,
+};
+
+int virtgpu_ttm_init(struct virtgpu_device *vgdev)
+{
+ int ret;
+
+ ret = virtgpu_ttm_global_init(vgdev);
+ if (ret)
+ return ret;
+
+ ret = ttm_bo_device_init(&vgdev->mman.bdev,
+ vgdev->mman.bo_global_ref.ref.object,
+ &virtgpu_bo_driver, DRM_FILE_PAGE_OFFSET, 0);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+void virtgpu_ttm_fini(struct virtgpu_device *vgdev)
+{
+ ttm_bo_device_release(&vgdev->mman.bdev);
+ virtgpu_ttm_global_fini(vgdev);
+}
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 1331ce50bf10..170a624ab938 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -1194,6 +1194,7 @@ struct drm_device {
struct platform_device *platformdev; /**< Platform device struture */
struct usb_device *usbdev;
+ struct virtio_device *virtdev;
struct drm_sg_mem *sg; /**< Scatter gather memory */
unsigned int num_crtcs; /**< Number of CRTCs on this device */