Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 
0003 /**
0004  * DOC: vkms (Virtual Kernel Modesetting)
0005  *
0006  * VKMS is a software-only model of a KMS driver that is useful for testing
0007  * and for running X (or similar) on headless machines. VKMS aims to enable
0008  * a virtual display with no need of a hardware display capability, releasing
0009  * the GPU in DRM API tests.
0010  */
0011 
0012 #include <linux/module.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/dma-mapping.h>
0015 
0016 #include <drm/drm_gem.h>
0017 #include <drm/drm_atomic.h>
0018 #include <drm/drm_atomic_helper.h>
0019 #include <drm/drm_drv.h>
0020 #include <drm/drm_fb_helper.h>
0021 #include <drm/drm_file.h>
0022 #include <drm/drm_gem_framebuffer_helper.h>
0023 #include <drm/drm_ioctl.h>
0024 #include <drm/drm_managed.h>
0025 #include <drm/drm_probe_helper.h>
0026 #include <drm/drm_gem_shmem_helper.h>
0027 #include <drm/drm_vblank.h>
0028 
0029 #include "vkms_drv.h"
0030 
0031 #include <drm/drm_print.h>
0032 #include <drm/drm_debugfs.h>
0033 
0034 #define DRIVER_NAME "vkms"
0035 #define DRIVER_DESC "Virtual Kernel Mode Setting"
0036 #define DRIVER_DATE "20180514"
0037 #define DRIVER_MAJOR    1
0038 #define DRIVER_MINOR    0
0039 
0040 static struct vkms_config *default_config;
0041 
0042 static bool enable_cursor = true;
0043 module_param_named(enable_cursor, enable_cursor, bool, 0444);
0044 MODULE_PARM_DESC(enable_cursor, "Enable/Disable cursor support");
0045 
0046 static bool enable_writeback = true;
0047 module_param_named(enable_writeback, enable_writeback, bool, 0444);
0048 MODULE_PARM_DESC(enable_writeback, "Enable/Disable writeback connector support");
0049 
0050 static bool enable_overlay;
0051 module_param_named(enable_overlay, enable_overlay, bool, 0444);
0052 MODULE_PARM_DESC(enable_overlay, "Enable/Disable overlay support");
0053 
0054 DEFINE_DRM_GEM_FOPS(vkms_driver_fops);
0055 
0056 static void vkms_release(struct drm_device *dev)
0057 {
0058     struct vkms_device *vkms = drm_device_to_vkms_device(dev);
0059 
0060     destroy_workqueue(vkms->output.composer_workq);
0061 }
0062 
0063 static void vkms_atomic_commit_tail(struct drm_atomic_state *old_state)
0064 {
0065     struct drm_device *dev = old_state->dev;
0066     struct drm_crtc *crtc;
0067     struct drm_crtc_state *old_crtc_state;
0068     int i;
0069 
0070     drm_atomic_helper_commit_modeset_disables(dev, old_state);
0071 
0072     drm_atomic_helper_commit_planes(dev, old_state, 0);
0073 
0074     drm_atomic_helper_commit_modeset_enables(dev, old_state);
0075 
0076     drm_atomic_helper_fake_vblank(old_state);
0077 
0078     drm_atomic_helper_commit_hw_done(old_state);
0079 
0080     drm_atomic_helper_wait_for_flip_done(dev, old_state);
0081 
0082     for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) {
0083         struct vkms_crtc_state *vkms_state =
0084             to_vkms_crtc_state(old_crtc_state);
0085 
0086         flush_work(&vkms_state->composer_work);
0087     }
0088 
0089     drm_atomic_helper_cleanup_planes(dev, old_state);
0090 }
0091 
0092 static int vkms_config_show(struct seq_file *m, void *data)
0093 {
0094     struct drm_info_node *node = (struct drm_info_node *)m->private;
0095     struct drm_device *dev = node->minor->dev;
0096     struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
0097 
0098     seq_printf(m, "writeback=%d\n", vkmsdev->config->writeback);
0099     seq_printf(m, "cursor=%d\n", vkmsdev->config->cursor);
0100     seq_printf(m, "overlay=%d\n", vkmsdev->config->overlay);
0101 
0102     return 0;
0103 }
0104 
0105 static const struct drm_info_list vkms_config_debugfs_list[] = {
0106     { "vkms_config", vkms_config_show, 0 },
0107 };
0108 
0109 static void vkms_config_debugfs_init(struct drm_minor *minor)
0110 {
0111     drm_debugfs_create_files(vkms_config_debugfs_list, ARRAY_SIZE(vkms_config_debugfs_list),
0112                  minor->debugfs_root, minor);
0113 }
0114 
0115 static const struct drm_driver vkms_driver = {
0116     .driver_features    = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
0117     .release        = vkms_release,
0118     .fops           = &vkms_driver_fops,
0119     DRM_GEM_SHMEM_DRIVER_OPS,
0120 
0121     .debugfs_init           = vkms_config_debugfs_init,
0122 
0123     .name           = DRIVER_NAME,
0124     .desc           = DRIVER_DESC,
0125     .date           = DRIVER_DATE,
0126     .major          = DRIVER_MAJOR,
0127     .minor          = DRIVER_MINOR,
0128 };
0129 
0130 static const struct drm_mode_config_funcs vkms_mode_funcs = {
0131     .fb_create = drm_gem_fb_create,
0132     .atomic_check = drm_atomic_helper_check,
0133     .atomic_commit = drm_atomic_helper_commit,
0134 };
0135 
0136 static const struct drm_mode_config_helper_funcs vkms_mode_config_helpers = {
0137     .atomic_commit_tail = vkms_atomic_commit_tail,
0138 };
0139 
0140 static int vkms_modeset_init(struct vkms_device *vkmsdev)
0141 {
0142     struct drm_device *dev = &vkmsdev->drm;
0143 
0144     drm_mode_config_init(dev);
0145     dev->mode_config.funcs = &vkms_mode_funcs;
0146     dev->mode_config.min_width = XRES_MIN;
0147     dev->mode_config.min_height = YRES_MIN;
0148     dev->mode_config.max_width = XRES_MAX;
0149     dev->mode_config.max_height = YRES_MAX;
0150     dev->mode_config.cursor_width = 512;
0151     dev->mode_config.cursor_height = 512;
0152     /* FIXME: There's a confusion between bpp and depth between this and
0153      * fbdev helpers. We have to go with 0, meaning "pick the default",
0154      * which ix XRGB8888 in all cases. */
0155     dev->mode_config.preferred_depth = 0;
0156     dev->mode_config.helper_private = &vkms_mode_config_helpers;
0157 
0158     return vkms_output_init(vkmsdev, 0);
0159 }
0160 
0161 static int vkms_create(struct vkms_config *config)
0162 {
0163     int ret;
0164     struct platform_device *pdev;
0165     struct vkms_device *vkms_device;
0166 
0167     pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
0168     if (IS_ERR(pdev))
0169         return PTR_ERR(pdev);
0170 
0171     if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) {
0172         ret = -ENOMEM;
0173         goto out_unregister;
0174     }
0175 
0176     vkms_device = devm_drm_dev_alloc(&pdev->dev, &vkms_driver,
0177                      struct vkms_device, drm);
0178     if (IS_ERR(vkms_device)) {
0179         ret = PTR_ERR(vkms_device);
0180         goto out_devres;
0181     }
0182     vkms_device->platform = pdev;
0183     vkms_device->config = config;
0184     config->dev = vkms_device;
0185 
0186     ret = dma_coerce_mask_and_coherent(vkms_device->drm.dev,
0187                        DMA_BIT_MASK(64));
0188 
0189     if (ret) {
0190         DRM_ERROR("Could not initialize DMA support\n");
0191         goto out_devres;
0192     }
0193 
0194     ret = drm_vblank_init(&vkms_device->drm, 1);
0195     if (ret) {
0196         DRM_ERROR("Failed to vblank\n");
0197         goto out_devres;
0198     }
0199 
0200     ret = vkms_modeset_init(vkms_device);
0201     if (ret)
0202         goto out_devres;
0203 
0204     ret = drm_dev_register(&vkms_device->drm, 0);
0205     if (ret)
0206         goto out_devres;
0207 
0208     drm_fbdev_generic_setup(&vkms_device->drm, 0);
0209 
0210     return 0;
0211 
0212 out_devres:
0213     devres_release_group(&pdev->dev, NULL);
0214 out_unregister:
0215     platform_device_unregister(pdev);
0216     return ret;
0217 }
0218 
0219 static int __init vkms_init(void)
0220 {
0221     struct vkms_config *config;
0222 
0223     config = kmalloc(sizeof(*config), GFP_KERNEL);
0224     if (!config)
0225         return -ENOMEM;
0226 
0227     default_config = config;
0228 
0229     config->cursor = enable_cursor;
0230     config->writeback = enable_writeback;
0231     config->overlay = enable_overlay;
0232 
0233     return vkms_create(config);
0234 }
0235 
0236 static void vkms_destroy(struct vkms_config *config)
0237 {
0238     struct platform_device *pdev;
0239 
0240     if (!config->dev) {
0241         DRM_INFO("vkms_device is NULL.\n");
0242         return;
0243     }
0244 
0245     pdev = config->dev->platform;
0246 
0247     drm_dev_unregister(&config->dev->drm);
0248     drm_atomic_helper_shutdown(&config->dev->drm);
0249     devres_release_group(&pdev->dev, NULL);
0250     platform_device_unregister(pdev);
0251 
0252     config->dev = NULL;
0253 }
0254 
0255 static void __exit vkms_exit(void)
0256 {
0257     if (default_config->dev)
0258         vkms_destroy(default_config);
0259 
0260     kfree(default_config);
0261 }
0262 
0263 module_init(vkms_init);
0264 module_exit(vkms_exit);
0265 
0266 MODULE_AUTHOR("Haneen Mohammed <hamohammed.sa@gmail.com>");
0267 MODULE_AUTHOR("Rodrigo Siqueira <rodrigosiqueiramelo@gmail.com>");
0268 MODULE_DESCRIPTION(DRIVER_DESC);
0269 MODULE_LICENSE("GPL");