diff options
Diffstat (limited to 'drivers/gpu/drm/virtio')
-rw-r--r-- | drivers/gpu/drm/virtio/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/virtio/virtio_display.c | 512 | ||||
-rw-r--r-- | drivers/gpu/drm/virtio/virtio_drv.c | 37 | ||||
-rw-r--r-- | drivers/gpu/drm/virtio/virtio_drv.h | 85 | ||||
-rw-r--r-- | drivers/gpu/drm/virtio/virtio_fb.c | 109 | ||||
-rw-r--r-- | drivers/gpu/drm/virtio/virtio_gem.c | 26 | ||||
-rw-r--r-- | drivers/gpu/drm/virtio/virtio_kms.c | 37 | ||||
-rw-r--r-- | drivers/gpu/drm/virtio/virtio_object.c | 31 | ||||
-rw-r--r-- | drivers/gpu/drm/virtio/virtio_ttm.c | 121 |
9 files changed, 933 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); +} |