linux/drivers/gpu/drm/virtio/virtgpu_display.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015 Red Hat, Inc.
   3 * All Rights Reserved.
   4 *
   5 * Authors:
   6 *    Dave Airlie
   7 *    Alon Levy
   8 *
   9 * Permission is hereby granted, free of charge, to any person obtaining a
  10 * copy of this software and associated documentation files (the "Software"),
  11 * to deal in the Software without restriction, including without limitation
  12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  13 * and/or sell copies of the Software, and to permit persons to whom the
  14 * Software is furnished to do so, subject to the following conditions:
  15 *
  16 * The above copyright notice and this permission notice shall be included in
  17 * all copies or substantial portions of the Software.
  18 *
  19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  22 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  23 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  24 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  25 * OTHER DEALINGS IN THE SOFTWARE.
  26 */
  27
  28#include <drm/drm_atomic_helper.h>
  29#include <drm/drm_damage_helper.h>
  30#include <drm/drm_fourcc.h>
  31#include <drm/drm_gem_framebuffer_helper.h>
  32#include <drm/drm_probe_helper.h>
  33#include <drm/drm_vblank.h>
  34
  35#include "virtgpu_drv.h"
  36
  37#define XRES_MIN    32
  38#define YRES_MIN    32
  39
  40#define XRES_DEF  1024
  41#define YRES_DEF   768
  42
  43#define XRES_MAX  8192
  44#define YRES_MAX  8192
  45
  46static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = {
  47        .set_config             = drm_atomic_helper_set_config,
  48        .destroy                = drm_crtc_cleanup,
  49
  50        .page_flip              = drm_atomic_helper_page_flip,
  51        .reset                  = drm_atomic_helper_crtc_reset,
  52        .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
  53        .atomic_destroy_state   = drm_atomic_helper_crtc_destroy_state,
  54};
  55
  56static const struct drm_framebuffer_funcs virtio_gpu_fb_funcs = {
  57        .create_handle = drm_gem_fb_create_handle,
  58        .destroy = drm_gem_fb_destroy,
  59        .dirty = drm_atomic_helper_dirtyfb,
  60};
  61
  62int
  63virtio_gpu_framebuffer_init(struct drm_device *dev,
  64                            struct virtio_gpu_framebuffer *vgfb,
  65                            const struct drm_mode_fb_cmd2 *mode_cmd,
  66                            struct drm_gem_object *obj)
  67{
  68        int ret;
  69
  70        vgfb->base.obj[0] = obj;
  71
  72        drm_helper_mode_fill_fb_struct(dev, &vgfb->base, mode_cmd);
  73
  74        ret = drm_framebuffer_init(dev, &vgfb->base, &virtio_gpu_fb_funcs);
  75        if (ret) {
  76                vgfb->base.obj[0] = NULL;
  77                return ret;
  78        }
  79        return 0;
  80}
  81
  82static void virtio_gpu_crtc_mode_set_nofb(struct drm_crtc *crtc)
  83{
  84        struct drm_device *dev = crtc->dev;
  85        struct virtio_gpu_device *vgdev = dev->dev_private;
  86        struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc);
  87
  88        virtio_gpu_cmd_set_scanout(vgdev, output->index, 0,
  89                                   crtc->mode.hdisplay,
  90                                   crtc->mode.vdisplay, 0, 0);
  91}
  92
  93static void virtio_gpu_crtc_atomic_enable(struct drm_crtc *crtc,
  94                                          struct drm_crtc_state *old_state)
  95{
  96        struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc);
  97
  98        output->enabled = true;
  99}
 100
 101static void virtio_gpu_crtc_atomic_disable(struct drm_crtc *crtc,
 102                                           struct drm_crtc_state *old_state)
 103{
 104        struct drm_device *dev = crtc->dev;
 105        struct virtio_gpu_device *vgdev = dev->dev_private;
 106        struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc);
 107
 108        virtio_gpu_cmd_set_scanout(vgdev, output->index, 0, 0, 0, 0, 0);
 109        output->enabled = false;
 110}
 111
 112static int virtio_gpu_crtc_atomic_check(struct drm_crtc *crtc,
 113                                        struct drm_crtc_state *state)
 114{
 115        return 0;
 116}
 117
 118static void virtio_gpu_crtc_atomic_flush(struct drm_crtc *crtc,
 119                                         struct drm_crtc_state *old_state)
 120{
 121        unsigned long flags;
 122
 123        spin_lock_irqsave(&crtc->dev->event_lock, flags);
 124        if (crtc->state->event)
 125                drm_crtc_send_vblank_event(crtc, crtc->state->event);
 126        crtc->state->event = NULL;
 127        spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
 128}
 129
 130static const struct drm_crtc_helper_funcs virtio_gpu_crtc_helper_funcs = {
 131        .mode_set_nofb = virtio_gpu_crtc_mode_set_nofb,
 132        .atomic_check  = virtio_gpu_crtc_atomic_check,
 133        .atomic_flush  = virtio_gpu_crtc_atomic_flush,
 134        .atomic_enable = virtio_gpu_crtc_atomic_enable,
 135        .atomic_disable = virtio_gpu_crtc_atomic_disable,
 136};
 137
 138static void virtio_gpu_enc_mode_set(struct drm_encoder *encoder,
 139                                    struct drm_display_mode *mode,
 140                                    struct drm_display_mode *adjusted_mode)
 141{
 142}
 143
 144static void virtio_gpu_enc_enable(struct drm_encoder *encoder)
 145{
 146}
 147
 148static void virtio_gpu_enc_disable(struct drm_encoder *encoder)
 149{
 150}
 151
 152static int virtio_gpu_conn_get_modes(struct drm_connector *connector)
 153{
 154        struct virtio_gpu_output *output =
 155                drm_connector_to_virtio_gpu_output(connector);
 156        struct drm_display_mode *mode = NULL;
 157        int count, width, height;
 158
 159        if (output->edid) {
 160                count = drm_add_edid_modes(connector, output->edid);
 161                if (count)
 162                        return count;
 163        }
 164
 165        width  = le32_to_cpu(output->info.r.width);
 166        height = le32_to_cpu(output->info.r.height);
 167        count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX);
 168
 169        if (width == 0 || height == 0) {
 170                width = XRES_DEF;
 171                height = YRES_DEF;
 172                drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF);
 173        } else {
 174                DRM_DEBUG("add mode: %dx%d\n", width, height);
 175                mode = drm_cvt_mode(connector->dev, width, height, 60,
 176                                    false, false, false);
 177                mode->type |= DRM_MODE_TYPE_PREFERRED;
 178                drm_mode_probed_add(connector, mode);
 179                count++;
 180        }
 181
 182        return count;
 183}
 184
 185static enum drm_mode_status virtio_gpu_conn_mode_valid(struct drm_connector *connector,
 186                                      struct drm_display_mode *mode)
 187{
 188        struct virtio_gpu_output *output =
 189                drm_connector_to_virtio_gpu_output(connector);
 190        int width, height;
 191
 192        width  = le32_to_cpu(output->info.r.width);
 193        height = le32_to_cpu(output->info.r.height);
 194
 195        if (!(mode->type & DRM_MODE_TYPE_PREFERRED))
 196                return MODE_OK;
 197        if (mode->hdisplay == XRES_DEF && mode->vdisplay == YRES_DEF)
 198                return MODE_OK;
 199        if (mode->hdisplay <= width  && mode->hdisplay >= width - 16 &&
 200            mode->vdisplay <= height && mode->vdisplay >= height - 16)
 201                return MODE_OK;
 202
 203        DRM_DEBUG("del mode: %dx%d\n", mode->hdisplay, mode->vdisplay);
 204        return MODE_BAD;
 205}
 206
 207static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = {
 208        .mode_set   = virtio_gpu_enc_mode_set,
 209        .enable     = virtio_gpu_enc_enable,
 210        .disable    = virtio_gpu_enc_disable,
 211};
 212
 213static const struct drm_connector_helper_funcs virtio_gpu_conn_helper_funcs = {
 214        .get_modes    = virtio_gpu_conn_get_modes,
 215        .mode_valid   = virtio_gpu_conn_mode_valid,
 216};
 217
 218static enum drm_connector_status virtio_gpu_conn_detect(
 219                        struct drm_connector *connector,
 220                        bool force)
 221{
 222        struct virtio_gpu_output *output =
 223                drm_connector_to_virtio_gpu_output(connector);
 224
 225        if (output->info.enabled)
 226                return connector_status_connected;
 227        else
 228                return connector_status_disconnected;
 229}
 230
 231static void virtio_gpu_conn_destroy(struct drm_connector *connector)
 232{
 233        drm_connector_unregister(connector);
 234        drm_connector_cleanup(connector);
 235}
 236
 237static const struct drm_connector_funcs virtio_gpu_connector_funcs = {
 238        .detect = virtio_gpu_conn_detect,
 239        .fill_modes = drm_helper_probe_single_connector_modes,
 240        .destroy = virtio_gpu_conn_destroy,
 241        .reset = drm_atomic_helper_connector_reset,
 242        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 243        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 244};
 245
 246static const struct drm_encoder_funcs virtio_gpu_enc_funcs = {
 247        .destroy = drm_encoder_cleanup,
 248};
 249
 250static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index)
 251{
 252        struct drm_device *dev = vgdev->ddev;
 253        struct virtio_gpu_output *output = vgdev->outputs + index;
 254        struct drm_connector *connector = &output->conn;
 255        struct drm_encoder *encoder = &output->enc;
 256        struct drm_crtc *crtc = &output->crtc;
 257        struct drm_plane *primary, *cursor;
 258
 259        output->index = index;
 260        if (index == 0) {
 261                output->info.enabled = cpu_to_le32(true);
 262                output->info.r.width = cpu_to_le32(XRES_DEF);
 263                output->info.r.height = cpu_to_le32(YRES_DEF);
 264        }
 265
 266        primary = virtio_gpu_plane_init(vgdev, DRM_PLANE_TYPE_PRIMARY, index);
 267        if (IS_ERR(primary))
 268                return PTR_ERR(primary);
 269        cursor = virtio_gpu_plane_init(vgdev, DRM_PLANE_TYPE_CURSOR, index);
 270        if (IS_ERR(cursor))
 271                return PTR_ERR(cursor);
 272        drm_crtc_init_with_planes(dev, crtc, primary, cursor,
 273                                  &virtio_gpu_crtc_funcs, NULL);
 274        drm_crtc_helper_add(crtc, &virtio_gpu_crtc_helper_funcs);
 275
 276        drm_connector_init(dev, connector, &virtio_gpu_connector_funcs,
 277                           DRM_MODE_CONNECTOR_VIRTUAL);
 278        drm_connector_helper_add(connector, &virtio_gpu_conn_helper_funcs);
 279        if (vgdev->has_edid)
 280                drm_connector_attach_edid_property(connector);
 281
 282        drm_encoder_init(dev, encoder, &virtio_gpu_enc_funcs,
 283                         DRM_MODE_ENCODER_VIRTUAL, NULL);
 284        drm_encoder_helper_add(encoder, &virtio_gpu_enc_helper_funcs);
 285        encoder->possible_crtcs = 1 << index;
 286
 287        drm_connector_attach_encoder(connector, encoder);
 288        drm_connector_register(connector);
 289        return 0;
 290}
 291
 292static struct drm_framebuffer *
 293virtio_gpu_user_framebuffer_create(struct drm_device *dev,
 294                                   struct drm_file *file_priv,
 295                                   const struct drm_mode_fb_cmd2 *mode_cmd)
 296{
 297        struct drm_gem_object *obj = NULL;
 298        struct virtio_gpu_framebuffer *virtio_gpu_fb;
 299        int ret;
 300
 301        if (mode_cmd->pixel_format != DRM_FORMAT_HOST_XRGB8888 &&
 302            mode_cmd->pixel_format != DRM_FORMAT_HOST_ARGB8888)
 303                return ERR_PTR(-ENOENT);
 304
 305        /* lookup object associated with res handle */
 306        obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]);
 307        if (!obj)
 308                return ERR_PTR(-EINVAL);
 309
 310        virtio_gpu_fb = kzalloc(sizeof(*virtio_gpu_fb), GFP_KERNEL);
 311        if (virtio_gpu_fb == NULL)
 312                return ERR_PTR(-ENOMEM);
 313
 314        ret = virtio_gpu_framebuffer_init(dev, virtio_gpu_fb, mode_cmd, obj);
 315        if (ret) {
 316                kfree(virtio_gpu_fb);
 317                drm_gem_object_put_unlocked(obj);
 318                return NULL;
 319        }
 320
 321        return &virtio_gpu_fb->base;
 322}
 323
 324static void vgdev_atomic_commit_tail(struct drm_atomic_state *state)
 325{
 326        struct drm_device *dev = state->dev;
 327
 328        drm_atomic_helper_commit_modeset_disables(dev, state);
 329        drm_atomic_helper_commit_modeset_enables(dev, state);
 330        drm_atomic_helper_commit_planes(dev, state, 0);
 331
 332        drm_atomic_helper_commit_hw_done(state);
 333
 334        drm_atomic_helper_wait_for_vblanks(dev, state);
 335        drm_atomic_helper_cleanup_planes(dev, state);
 336}
 337
 338static const struct drm_mode_config_helper_funcs virtio_mode_config_helpers = {
 339        .atomic_commit_tail = vgdev_atomic_commit_tail,
 340};
 341
 342static const struct drm_mode_config_funcs virtio_gpu_mode_funcs = {
 343        .fb_create = virtio_gpu_user_framebuffer_create,
 344        .atomic_check = drm_atomic_helper_check,
 345        .atomic_commit = drm_atomic_helper_commit,
 346};
 347
 348void virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev)
 349{
 350        int i;
 351
 352        drm_mode_config_init(vgdev->ddev);
 353        vgdev->ddev->mode_config.quirk_addfb_prefer_host_byte_order = true;
 354        vgdev->ddev->mode_config.funcs = &virtio_gpu_mode_funcs;
 355        vgdev->ddev->mode_config.helper_private = &virtio_mode_config_helpers;
 356
 357        /* modes will be validated against the framebuffer size */
 358        vgdev->ddev->mode_config.min_width = XRES_MIN;
 359        vgdev->ddev->mode_config.min_height = YRES_MIN;
 360        vgdev->ddev->mode_config.max_width = XRES_MAX;
 361        vgdev->ddev->mode_config.max_height = YRES_MAX;
 362
 363        for (i = 0 ; i < vgdev->num_scanouts; ++i)
 364                vgdev_output_init(vgdev, i);
 365
 366        drm_mode_config_reset(vgdev->ddev);
 367}
 368
 369void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev)
 370{
 371        int i;
 372
 373        for (i = 0 ; i < vgdev->num_scanouts; ++i)
 374                kfree(vgdev->outputs[i].edid);
 375        drm_atomic_helper_shutdown(vgdev->ddev);
 376        drm_mode_config_cleanup(vgdev->ddev);
 377}
 378