Home | History | Annotate | Line # | Download | only in virtio
      1 /*	$NetBSD: virtgpu_drv.c,v 1.3 2021/12/18 23:45:45 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 2015 Red Hat, Inc.
      5  * All Rights Reserved.
      6  *
      7  * Authors:
      8  *    Dave Airlie <airlied (at) redhat.com>
      9  *    Gerd Hoffmann <kraxel (at) redhat.com>
     10  *
     11  * Permission is hereby granted, free of charge, to any person obtaining a
     12  * copy of this software and associated documentation files (the "Software"),
     13  * to deal in the Software without restriction, including without limitation
     14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     15  * and/or sell copies of the Software, and to permit persons to whom the
     16  * Software is furnished to do so, subject to the following conditions:
     17  *
     18  * The above copyright notice and this permission notice (including the next
     19  * paragraph) shall be included in all copies or substantial portions of the
     20  * Software.
     21  *
     22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     25  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
     26  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     27  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     28  * OTHER DEALINGS IN THE SOFTWARE.
     29  */
     30 
     31 #include <sys/cdefs.h>
     32 __KERNEL_RCSID(0, "$NetBSD: virtgpu_drv.c,v 1.3 2021/12/18 23:45:45 riastradh Exp $");
     33 
     34 #include <linux/module.h>
     35 #include <linux/console.h>
     36 #include <linux/pci.h>
     37 
     38 #include <drm/drm.h>
     39 #include <drm/drm_drv.h>
     40 #include <drm/drm_file.h>
     41 
     42 #include "virtgpu_drv.h"
     43 
     44 static struct drm_driver driver;
     45 
     46 static int virtio_gpu_modeset = -1;
     47 
     48 MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
     49 module_param_named(modeset, virtio_gpu_modeset, int, 0400);
     50 
     51 static int virtio_gpu_pci_quirk(struct drm_device *dev, struct virtio_device *vdev)
     52 {
     53 	struct pci_dev *pdev = to_pci_dev(vdev->dev.parent);
     54 	const char *pname = dev_name(&pdev->dev);
     55 	bool vga = (pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA;
     56 	char unique[20];
     57 
     58 	DRM_INFO("pci: %s detected at %s\n",
     59 		 vga ? "virtio-vga" : "virtio-gpu-pci",
     60 		 pname);
     61 	dev->pdev = pdev;
     62 	if (vga)
     63 		drm_fb_helper_remove_conflicting_pci_framebuffers(pdev,
     64 								  "virtiodrmfb");
     65 
     66 	/*
     67 	 * Normally the drm_dev_set_unique() call is done by core DRM.
     68 	 * The following comment covers, why virtio cannot rely on it.
     69 	 *
     70 	 * Unlike the other virtual GPU drivers, virtio abstracts the
     71 	 * underlying bus type by using struct virtio_device.
     72 	 *
     73 	 * Hence the dev_is_pci() check, used in core DRM, will fail
     74 	 * and the unique returned will be the virtio_device "virtio0",
     75 	 * while a "pci:..." one is required.
     76 	 *
     77 	 * A few other ideas were considered:
     78 	 * - Extend the dev_is_pci() check [in drm_set_busid] to
     79 	 *   consider virtio.
     80 	 *   Seems like a bigger hack than what we have already.
     81 	 *
     82 	 * - Point drm_device::dev to the parent of the virtio_device
     83 	 *   Semantic changes:
     84 	 *   * Using the wrong device for i2c, framebuffer_alloc and
     85 	 *     prime import.
     86 	 *   Visual changes:
     87 	 *   * Helpers such as DRM_DEV_ERROR, dev_info, drm_printer,
     88 	 *     will print the wrong information.
     89 	 *
     90 	 * We could address the latter issues, by introducing
     91 	 * drm_device::bus_dev, ... which would be used solely for this.
     92 	 *
     93 	 * So for the moment keep things as-is, with a bulky comment
     94 	 * for the next person who feels like removing this
     95 	 * drm_dev_set_unique() quirk.
     96 	 */
     97 	snprintf(unique, sizeof(unique), "pci:%s", pname);
     98 	return drm_dev_set_unique(dev, unique);
     99 }
    100 
    101 static int virtio_gpu_probe(struct virtio_device *vdev)
    102 {
    103 	struct drm_device *dev;
    104 	int ret;
    105 
    106 	if (vgacon_text_force() && virtio_gpu_modeset == -1)
    107 		return -EINVAL;
    108 
    109 	if (virtio_gpu_modeset == 0)
    110 		return -EINVAL;
    111 
    112 	dev = drm_dev_alloc(&driver, &vdev->dev);
    113 	if (IS_ERR(dev))
    114 		return PTR_ERR(dev);
    115 	vdev->priv = dev;
    116 
    117 	if (!strcmp(vdev->dev.parent->bus->name, "pci")) {
    118 		ret = virtio_gpu_pci_quirk(dev, vdev);
    119 		if (ret)
    120 			goto err_free;
    121 	}
    122 
    123 	ret = virtio_gpu_init(dev);
    124 	if (ret)
    125 		goto err_free;
    126 
    127 	ret = drm_dev_register(dev, 0);
    128 	if (ret)
    129 		goto err_free;
    130 
    131 	drm_fbdev_generic_setup(vdev->priv, 32);
    132 	return 0;
    133 
    134 err_free:
    135 	drm_dev_put(dev);
    136 	return ret;
    137 }
    138 
    139 static void virtio_gpu_remove(struct virtio_device *vdev)
    140 {
    141 	struct drm_device *dev = vdev->priv;
    142 
    143 	drm_dev_unregister(dev);
    144 	virtio_gpu_deinit(dev);
    145 	drm_dev_put(dev);
    146 }
    147 
    148 static void virtio_gpu_config_changed(struct virtio_device *vdev)
    149 {
    150 	struct drm_device *dev = vdev->priv;
    151 	struct virtio_gpu_device *vgdev = dev->dev_private;
    152 
    153 	schedule_work(&vgdev->config_changed_work);
    154 }
    155 
    156 static struct virtio_device_id id_table[] = {
    157 	{ VIRTIO_ID_GPU, VIRTIO_DEV_ANY_ID },
    158 	{ 0 },
    159 };
    160 
    161 static unsigned int features[] = {
    162 #ifdef __LITTLE_ENDIAN
    163 	/*
    164 	 * Gallium command stream send by virgl is native endian.
    165 	 * Because of that we only support little endian guests on
    166 	 * little endian hosts.
    167 	 */
    168 	VIRTIO_GPU_F_VIRGL,
    169 #endif
    170 	VIRTIO_GPU_F_EDID,
    171 };
    172 static struct virtio_driver virtio_gpu_driver = {
    173 	.feature_table = features,
    174 	.feature_table_size = ARRAY_SIZE(features),
    175 	.driver.name = KBUILD_MODNAME,
    176 	.driver.owner = THIS_MODULE,
    177 	.id_table = id_table,
    178 	.probe = virtio_gpu_probe,
    179 	.remove = virtio_gpu_remove,
    180 	.config_changed = virtio_gpu_config_changed
    181 };
    182 
    183 module_virtio_driver(virtio_gpu_driver);
    184 
    185 MODULE_DEVICE_TABLE(virtio, id_table);
    186 MODULE_DESCRIPTION("Virtio GPU driver");
    187 MODULE_LICENSE("GPL and additional rights");
    188 MODULE_AUTHOR("Dave Airlie <airlied (at) redhat.com>");
    189 MODULE_AUTHOR("Gerd Hoffmann <kraxel (at) redhat.com>");
    190 MODULE_AUTHOR("Alon Levy");
    191 
    192 DEFINE_DRM_GEM_FOPS(virtio_gpu_driver_fops);
    193 
    194 static struct drm_driver driver = {
    195 	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_RENDER | DRIVER_ATOMIC,
    196 	.open = virtio_gpu_driver_open,
    197 	.postclose = virtio_gpu_driver_postclose,
    198 
    199 	.dumb_create = virtio_gpu_mode_dumb_create,
    200 	.dumb_map_offset = virtio_gpu_mode_dumb_mmap,
    201 
    202 #if defined(CONFIG_DEBUG_FS)
    203 	.debugfs_init = virtio_gpu_debugfs_init,
    204 #endif
    205 	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
    206 	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
    207 	.gem_prime_mmap = drm_gem_prime_mmap,
    208 	.gem_prime_import_sg_table = virtgpu_gem_prime_import_sg_table,
    209 
    210 	.gem_create_object = virtio_gpu_create_object,
    211 	.fops = &virtio_gpu_driver_fops,
    212 
    213 	.ioctls = virtio_gpu_ioctls,
    214 	.num_ioctls = DRM_VIRTIO_NUM_IOCTLS,
    215 
    216 	.name = DRIVER_NAME,
    217 	.desc = DRIVER_DESC,
    218 	.date = DRIVER_DATE,
    219 	.major = DRIVER_MAJOR,
    220 	.minor = DRIVER_MINOR,
    221 	.patchlevel = DRIVER_PATCHLEVEL,
    222 };
    223