1 /* 2 * This program is free software; you can redistribute it and/or modify 3 * it under the terms of the GNU General Public License as published by 4 * the Free Software Foundation; either version 2 of the License, or 5 * (at your option) any later version. 6 */ 7 8 #include <linux/module.h> 9 #include <drm/drm_gem.h> 10 #include <drm/drm_crtc_helper.h> 11 #include <drm/drm_atomic_helper.h> 12 #include <drm/drm_gem_framebuffer_helper.h> 13 #include <drm/drm_fb_helper.h> 14 #include "vkms_drv.h" 15 16 #define DRIVER_NAME "vkms" 17 #define DRIVER_DESC "Virtual Kernel Mode Setting" 18 #define DRIVER_DATE "20180514" 19 #define DRIVER_MAJOR 1 20 #define DRIVER_MINOR 0 21 22 static struct vkms_device *vkms_device; 23 24 static const struct file_operations vkms_driver_fops = { 25 .owner = THIS_MODULE, 26 .open = drm_open, 27 .mmap = drm_gem_mmap, 28 .unlocked_ioctl = drm_ioctl, 29 .compat_ioctl = drm_compat_ioctl, 30 .poll = drm_poll, 31 .read = drm_read, 32 .llseek = no_llseek, 33 .release = drm_release, 34 }; 35 36 static const struct vm_operations_struct vkms_gem_vm_ops = { 37 .fault = vkms_gem_fault, 38 .open = drm_gem_vm_open, 39 .close = drm_gem_vm_close, 40 }; 41 42 static void vkms_release(struct drm_device *dev) 43 { 44 struct vkms_device *vkms = container_of(dev, struct vkms_device, drm); 45 46 platform_device_unregister(vkms->platform); 47 drm_atomic_helper_shutdown(&vkms->drm); 48 drm_mode_config_cleanup(&vkms->drm); 49 drm_dev_fini(&vkms->drm); 50 } 51 52 static struct drm_driver vkms_driver = { 53 .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM, 54 .release = vkms_release, 55 .fops = &vkms_driver_fops, 56 .dumb_create = vkms_dumb_create, 57 .dumb_map_offset = vkms_dumb_map, 58 .gem_vm_ops = &vkms_gem_vm_ops, 59 .gem_free_object_unlocked = vkms_gem_free_object, 60 .get_vblank_timestamp = vkms_get_vblank_timestamp, 61 62 .name = DRIVER_NAME, 63 .desc = DRIVER_DESC, 64 .date = DRIVER_DATE, 65 .major = DRIVER_MAJOR, 66 .minor = DRIVER_MINOR, 67 }; 68 69 static const struct drm_mode_config_funcs vkms_mode_funcs = { 70 .fb_create = drm_gem_fb_create, 71 .atomic_check = drm_atomic_helper_check, 72 .atomic_commit = drm_atomic_helper_commit, 73 }; 74 75 static int vkms_modeset_init(struct vkms_device *vkmsdev) 76 { 77 struct drm_device *dev = &vkmsdev->drm; 78 79 drm_mode_config_init(dev); 80 dev->mode_config.funcs = &vkms_mode_funcs; 81 dev->mode_config.min_width = XRES_MIN; 82 dev->mode_config.min_height = YRES_MIN; 83 dev->mode_config.max_width = XRES_MAX; 84 dev->mode_config.max_height = YRES_MAX; 85 86 return vkms_output_init(vkmsdev); 87 } 88 89 static int __init vkms_init(void) 90 { 91 int ret; 92 93 vkms_device = kzalloc(sizeof(*vkms_device), GFP_KERNEL); 94 if (!vkms_device) 95 return -ENOMEM; 96 97 ret = drm_dev_init(&vkms_device->drm, &vkms_driver, NULL); 98 if (ret) 99 goto out_free; 100 101 vkms_device->platform = 102 platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); 103 if (IS_ERR(vkms_device->platform)) { 104 ret = PTR_ERR(vkms_device->platform); 105 goto out_fini; 106 } 107 108 vkms_device->drm.irq_enabled = true; 109 110 ret = drm_vblank_init(&vkms_device->drm, 1); 111 if (ret) { 112 DRM_ERROR("Failed to vblank\n"); 113 goto out_fini; 114 } 115 116 ret = vkms_modeset_init(vkms_device); 117 if (ret) 118 goto out_unregister; 119 120 ret = drm_dev_register(&vkms_device->drm, 0); 121 if (ret) 122 goto out_unregister; 123 124 return 0; 125 126 out_unregister: 127 platform_device_unregister(vkms_device->platform); 128 129 out_fini: 130 drm_dev_fini(&vkms_device->drm); 131 132 out_free: 133 kfree(vkms_device); 134 return ret; 135 } 136 137 static void __exit vkms_exit(void) 138 { 139 if (!vkms_device) { 140 DRM_INFO("vkms_device is NULL.\n"); 141 return; 142 } 143 144 drm_dev_unregister(&vkms_device->drm); 145 drm_dev_put(&vkms_device->drm); 146 147 kfree(vkms_device); 148 } 149 150 module_init(vkms_init); 151 module_exit(vkms_exit); 152 153 MODULE_AUTHOR("Haneen Mohammed <hamohammed.sa@gmail.com>"); 154 MODULE_AUTHOR("Rodrigo Siqueira <rodrigosiqueiramelo@gmail.com>"); 155 MODULE_DESCRIPTION(DRIVER_DESC); 156 MODULE_LICENSE("GPL"); 157