linux/drivers/gpu/drm/vkms/vkms_drv.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2
   3/**
   4 * DOC: vkms (Virtual Kernel Modesetting)
   5 *
   6 * VKMS is a software-only model of a KMS driver that is useful for testing
   7 * and for running X (or similar) on headless machines. VKMS aims to enable
   8 * a virtual display with no need of a hardware display capability, releasing
   9 * the GPU in DRM API tests.
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/platform_device.h>
  14#include <linux/dma-mapping.h>
  15
  16#include <drm/drm_gem.h>
  17#include <drm/drm_atomic.h>
  18#include <drm/drm_atomic_helper.h>
  19#include <drm/drm_drv.h>
  20#include <drm/drm_fb_helper.h>
  21#include <drm/drm_file.h>
  22#include <drm/drm_gem_framebuffer_helper.h>
  23#include <drm/drm_ioctl.h>
  24#include <drm/drm_managed.h>
  25#include <drm/drm_probe_helper.h>
  26#include <drm/drm_gem_shmem_helper.h>
  27#include <drm/drm_vblank.h>
  28
  29#include "vkms_drv.h"
  30
  31#include <drm/drm_print.h>
  32#include <drm/drm_debugfs.h>
  33
  34#define DRIVER_NAME     "vkms"
  35#define DRIVER_DESC     "Virtual Kernel Mode Setting"
  36#define DRIVER_DATE     "20180514"
  37#define DRIVER_MAJOR    1
  38#define DRIVER_MINOR    0
  39
  40static struct vkms_config *default_config;
  41
  42static bool enable_cursor = true;
  43module_param_named(enable_cursor, enable_cursor, bool, 0444);
  44MODULE_PARM_DESC(enable_cursor, "Enable/Disable cursor support");
  45
  46static bool enable_writeback = true;
  47module_param_named(enable_writeback, enable_writeback, bool, 0444);
  48MODULE_PARM_DESC(enable_writeback, "Enable/Disable writeback connector support");
  49
  50static bool enable_overlay;
  51module_param_named(enable_overlay, enable_overlay, bool, 0444);
  52MODULE_PARM_DESC(enable_overlay, "Enable/Disable overlay support");
  53
  54DEFINE_DRM_GEM_FOPS(vkms_driver_fops);
  55
  56static void vkms_release(struct drm_device *dev)
  57{
  58        struct vkms_device *vkms = drm_device_to_vkms_device(dev);
  59
  60        destroy_workqueue(vkms->output.composer_workq);
  61}
  62
  63static void vkms_atomic_commit_tail(struct drm_atomic_state *old_state)
  64{
  65        struct drm_device *dev = old_state->dev;
  66        struct drm_crtc *crtc;
  67        struct drm_crtc_state *old_crtc_state;
  68        int i;
  69
  70        drm_atomic_helper_commit_modeset_disables(dev, old_state);
  71
  72        drm_atomic_helper_commit_planes(dev, old_state, 0);
  73
  74        drm_atomic_helper_commit_modeset_enables(dev, old_state);
  75
  76        drm_atomic_helper_fake_vblank(old_state);
  77
  78        drm_atomic_helper_commit_hw_done(old_state);
  79
  80        drm_atomic_helper_wait_for_flip_done(dev, old_state);
  81
  82        for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) {
  83                struct vkms_crtc_state *vkms_state =
  84                        to_vkms_crtc_state(old_crtc_state);
  85
  86                flush_work(&vkms_state->composer_work);
  87        }
  88
  89        drm_atomic_helper_cleanup_planes(dev, old_state);
  90}
  91
  92static int vkms_config_show(struct seq_file *m, void *data)
  93{
  94        struct drm_info_node *node = (struct drm_info_node *)m->private;
  95        struct drm_device *dev = node->minor->dev;
  96        struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
  97
  98        seq_printf(m, "writeback=%d\n", vkmsdev->config->writeback);
  99        seq_printf(m, "cursor=%d\n", vkmsdev->config->cursor);
 100        seq_printf(m, "overlay=%d\n", vkmsdev->config->overlay);
 101
 102        return 0;
 103}
 104
 105static const struct drm_info_list vkms_config_debugfs_list[] = {
 106        { "vkms_config", vkms_config_show, 0 },
 107};
 108
 109static void vkms_config_debugfs_init(struct drm_minor *minor)
 110{
 111        drm_debugfs_create_files(vkms_config_debugfs_list, ARRAY_SIZE(vkms_config_debugfs_list),
 112                                 minor->debugfs_root, minor);
 113}
 114
 115static const struct drm_driver vkms_driver = {
 116        .driver_features        = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
 117        .release                = vkms_release,
 118        .fops                   = &vkms_driver_fops,
 119        DRM_GEM_SHMEM_DRIVER_OPS,
 120
 121        .debugfs_init           = vkms_config_debugfs_init,
 122
 123        .name                   = DRIVER_NAME,
 124        .desc                   = DRIVER_DESC,
 125        .date                   = DRIVER_DATE,
 126        .major                  = DRIVER_MAJOR,
 127        .minor                  = DRIVER_MINOR,
 128};
 129
 130static const struct drm_mode_config_funcs vkms_mode_funcs = {
 131        .fb_create = drm_gem_fb_create,
 132        .atomic_check = drm_atomic_helper_check,
 133        .atomic_commit = drm_atomic_helper_commit,
 134};
 135
 136static const struct drm_mode_config_helper_funcs vkms_mode_config_helpers = {
 137        .atomic_commit_tail = vkms_atomic_commit_tail,
 138};
 139
 140static int vkms_modeset_init(struct vkms_device *vkmsdev)
 141{
 142        struct drm_device *dev = &vkmsdev->drm;
 143
 144        drm_mode_config_init(dev);
 145        dev->mode_config.funcs = &vkms_mode_funcs;
 146        dev->mode_config.min_width = XRES_MIN;
 147        dev->mode_config.min_height = YRES_MIN;
 148        dev->mode_config.max_width = XRES_MAX;
 149        dev->mode_config.max_height = YRES_MAX;
 150        dev->mode_config.cursor_width = 512;
 151        dev->mode_config.cursor_height = 512;
 152        /* FIXME: There's a confusion between bpp and depth between this and
 153         * fbdev helpers. We have to go with 0, meaning "pick the default",
 154         * which ix XRGB8888 in all cases. */
 155        dev->mode_config.preferred_depth = 0;
 156        dev->mode_config.helper_private = &vkms_mode_config_helpers;
 157
 158        return vkms_output_init(vkmsdev, 0);
 159}
 160
 161static int vkms_create(struct vkms_config *config)
 162{
 163        int ret;
 164        struct platform_device *pdev;
 165        struct vkms_device *vkms_device;
 166
 167        pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
 168        if (IS_ERR(pdev))
 169                return PTR_ERR(pdev);
 170
 171        if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) {
 172                ret = -ENOMEM;
 173                goto out_unregister;
 174        }
 175
 176        vkms_device = devm_drm_dev_alloc(&pdev->dev, &vkms_driver,
 177                                         struct vkms_device, drm);
 178        if (IS_ERR(vkms_device)) {
 179                ret = PTR_ERR(vkms_device);
 180                goto out_devres;
 181        }
 182        vkms_device->platform = pdev;
 183        vkms_device->config = config;
 184        config->dev = vkms_device;
 185
 186        ret = dma_coerce_mask_and_coherent(vkms_device->drm.dev,
 187                                           DMA_BIT_MASK(64));
 188
 189        if (ret) {
 190                DRM_ERROR("Could not initialize DMA support\n");
 191                goto out_devres;
 192        }
 193
 194        ret = drm_vblank_init(&vkms_device->drm, 1);
 195        if (ret) {
 196                DRM_ERROR("Failed to vblank\n");
 197                goto out_devres;
 198        }
 199
 200        ret = vkms_modeset_init(vkms_device);
 201        if (ret)
 202                goto out_devres;
 203
 204        ret = drm_dev_register(&vkms_device->drm, 0);
 205        if (ret)
 206                goto out_devres;
 207
 208        drm_fbdev_generic_setup(&vkms_device->drm, 0);
 209
 210        return 0;
 211
 212out_devres:
 213        devres_release_group(&pdev->dev, NULL);
 214out_unregister:
 215        platform_device_unregister(pdev);
 216        return ret;
 217}
 218
 219static int __init vkms_init(void)
 220{
 221        struct vkms_config *config;
 222
 223        config = kmalloc(sizeof(*config), GFP_KERNEL);
 224        if (!config)
 225                return -ENOMEM;
 226
 227        default_config = config;
 228
 229        config->cursor = enable_cursor;
 230        config->writeback = enable_writeback;
 231        config->overlay = enable_overlay;
 232
 233        return vkms_create(config);
 234}
 235
 236static void vkms_destroy(struct vkms_config *config)
 237{
 238        struct platform_device *pdev;
 239
 240        if (!config->dev) {
 241                DRM_INFO("vkms_device is NULL.\n");
 242                return;
 243        }
 244
 245        pdev = config->dev->platform;
 246
 247        drm_dev_unregister(&config->dev->drm);
 248        drm_atomic_helper_shutdown(&config->dev->drm);
 249        devres_release_group(&pdev->dev, NULL);
 250        platform_device_unregister(pdev);
 251
 252        config->dev = NULL;
 253}
 254
 255static void __exit vkms_exit(void)
 256{
 257        if (default_config->dev)
 258                vkms_destroy(default_config);
 259
 260        kfree(default_config);
 261}
 262
 263module_init(vkms_init);
 264module_exit(vkms_exit);
 265
 266MODULE_AUTHOR("Haneen Mohammed <hamohammed.sa@gmail.com>");
 267MODULE_AUTHOR("Rodrigo Siqueira <rodrigosiqueiramelo@gmail.com>");
 268MODULE_DESCRIPTION(DRIVER_DESC);
 269MODULE_LICENSE("GPL");
 270