linux/drivers/gpu/drm/vboxvideo/vbox_main.c
<<
>>
Prefs
   1// SPDX-License-Identifier: MIT
   2/*
   3 * Copyright (C) 2013-2017 Oracle Corporation
   4 * This file is based on ast_main.c
   5 * Copyright 2012 Red Hat Inc.
   6 * Authors: Dave Airlie <airlied@redhat.com>,
   7 *          Michael Thayer <michael.thayer@oracle.com,
   8 *          Hans de Goede <hdegoede@redhat.com>
   9 */
  10
  11#include <linux/vbox_err.h>
  12#include <drm/drm_fb_helper.h>
  13#include <drm/drm_crtc_helper.h>
  14
  15#include "vbox_drv.h"
  16#include "vboxvideo_guest.h"
  17#include "vboxvideo_vbe.h"
  18
  19static void vbox_user_framebuffer_destroy(struct drm_framebuffer *fb)
  20{
  21        struct vbox_framebuffer *vbox_fb = to_vbox_framebuffer(fb);
  22
  23        if (vbox_fb->obj)
  24                drm_gem_object_put_unlocked(vbox_fb->obj);
  25
  26        drm_framebuffer_cleanup(fb);
  27        kfree(fb);
  28}
  29
  30void vbox_report_caps(struct vbox_private *vbox)
  31{
  32        u32 caps = VBVACAPS_DISABLE_CURSOR_INTEGRATION |
  33                   VBVACAPS_IRQ | VBVACAPS_USE_VBVA_ONLY;
  34
  35        /* The host only accepts VIDEO_MODE_HINTS if it is send separately. */
  36        hgsmi_send_caps_info(vbox->guest_pool, caps);
  37        caps |= VBVACAPS_VIDEO_MODE_HINTS;
  38        hgsmi_send_caps_info(vbox->guest_pool, caps);
  39}
  40
  41/* Send information about dirty rectangles to VBVA. */
  42void vbox_framebuffer_dirty_rectangles(struct drm_framebuffer *fb,
  43                                       struct drm_clip_rect *rects,
  44                                       unsigned int num_rects)
  45{
  46        struct vbox_private *vbox = fb->dev->dev_private;
  47        struct drm_display_mode *mode;
  48        struct drm_crtc *crtc;
  49        int crtc_x, crtc_y;
  50        unsigned int i;
  51
  52        mutex_lock(&vbox->hw_mutex);
  53        list_for_each_entry(crtc, &fb->dev->mode_config.crtc_list, head) {
  54                if (crtc->primary->state->fb != fb)
  55                        continue;
  56
  57                mode = &crtc->state->mode;
  58                crtc_x = crtc->primary->state->src_x >> 16;
  59                crtc_y = crtc->primary->state->src_y >> 16;
  60
  61                for (i = 0; i < num_rects; ++i) {
  62                        struct vbva_cmd_hdr cmd_hdr;
  63                        unsigned int crtc_id = to_vbox_crtc(crtc)->crtc_id;
  64
  65                        if (rects[i].x1 > crtc_x + mode->hdisplay ||
  66                            rects[i].y1 > crtc_y + mode->vdisplay ||
  67                            rects[i].x2 < crtc_x ||
  68                            rects[i].y2 < crtc_y)
  69                                continue;
  70
  71                        cmd_hdr.x = (s16)rects[i].x1;
  72                        cmd_hdr.y = (s16)rects[i].y1;
  73                        cmd_hdr.w = (u16)rects[i].x2 - rects[i].x1;
  74                        cmd_hdr.h = (u16)rects[i].y2 - rects[i].y1;
  75
  76                        if (!vbva_buffer_begin_update(&vbox->vbva_info[crtc_id],
  77                                                      vbox->guest_pool))
  78                                continue;
  79
  80                        vbva_write(&vbox->vbva_info[crtc_id], vbox->guest_pool,
  81                                   &cmd_hdr, sizeof(cmd_hdr));
  82                        vbva_buffer_end_update(&vbox->vbva_info[crtc_id]);
  83                }
  84        }
  85        mutex_unlock(&vbox->hw_mutex);
  86}
  87
  88static int vbox_user_framebuffer_dirty(struct drm_framebuffer *fb,
  89                                       struct drm_file *file_priv,
  90                                       unsigned int flags, unsigned int color,
  91                                       struct drm_clip_rect *rects,
  92                                       unsigned int num_rects)
  93{
  94        vbox_framebuffer_dirty_rectangles(fb, rects, num_rects);
  95
  96        return 0;
  97}
  98
  99static const struct drm_framebuffer_funcs vbox_fb_funcs = {
 100        .destroy = vbox_user_framebuffer_destroy,
 101        .dirty = vbox_user_framebuffer_dirty,
 102};
 103
 104int vbox_framebuffer_init(struct vbox_private *vbox,
 105                          struct vbox_framebuffer *vbox_fb,
 106                          const struct drm_mode_fb_cmd2 *mode_cmd,
 107                          struct drm_gem_object *obj)
 108{
 109        int ret;
 110
 111        drm_helper_mode_fill_fb_struct(&vbox->ddev, &vbox_fb->base, mode_cmd);
 112        vbox_fb->obj = obj;
 113        ret = drm_framebuffer_init(&vbox->ddev, &vbox_fb->base, &vbox_fb_funcs);
 114        if (ret) {
 115                DRM_ERROR("framebuffer init failed %d\n", ret);
 116                return ret;
 117        }
 118
 119        return 0;
 120}
 121
 122static int vbox_accel_init(struct vbox_private *vbox)
 123{
 124        struct vbva_buffer *vbva;
 125        unsigned int i;
 126
 127        vbox->vbva_info = devm_kcalloc(vbox->ddev.dev, vbox->num_crtcs,
 128                                       sizeof(*vbox->vbva_info), GFP_KERNEL);
 129        if (!vbox->vbva_info)
 130                return -ENOMEM;
 131
 132        /* Take a command buffer for each screen from the end of usable VRAM. */
 133        vbox->available_vram_size -= vbox->num_crtcs * VBVA_MIN_BUFFER_SIZE;
 134
 135        vbox->vbva_buffers = pci_iomap_range(vbox->ddev.pdev, 0,
 136                                             vbox->available_vram_size,
 137                                             vbox->num_crtcs *
 138                                             VBVA_MIN_BUFFER_SIZE);
 139        if (!vbox->vbva_buffers)
 140                return -ENOMEM;
 141
 142        for (i = 0; i < vbox->num_crtcs; ++i) {
 143                vbva_setup_buffer_context(&vbox->vbva_info[i],
 144                                          vbox->available_vram_size +
 145                                          i * VBVA_MIN_BUFFER_SIZE,
 146                                          VBVA_MIN_BUFFER_SIZE);
 147                vbva = (void __force *)vbox->vbva_buffers +
 148                        i * VBVA_MIN_BUFFER_SIZE;
 149                if (!vbva_enable(&vbox->vbva_info[i],
 150                                 vbox->guest_pool, vbva, i)) {
 151                        /* very old host or driver error. */
 152                        DRM_ERROR("vboxvideo: vbva_enable failed\n");
 153                }
 154        }
 155
 156        return 0;
 157}
 158
 159static void vbox_accel_fini(struct vbox_private *vbox)
 160{
 161        unsigned int i;
 162
 163        for (i = 0; i < vbox->num_crtcs; ++i)
 164                vbva_disable(&vbox->vbva_info[i], vbox->guest_pool, i);
 165
 166        pci_iounmap(vbox->ddev.pdev, vbox->vbva_buffers);
 167}
 168
 169/* Do we support the 4.3 plus mode hint reporting interface? */
 170static bool have_hgsmi_mode_hints(struct vbox_private *vbox)
 171{
 172        u32 have_hints, have_cursor;
 173        int ret;
 174
 175        ret = hgsmi_query_conf(vbox->guest_pool,
 176                               VBOX_VBVA_CONF32_MODE_HINT_REPORTING,
 177                               &have_hints);
 178        if (ret)
 179                return false;
 180
 181        ret = hgsmi_query_conf(vbox->guest_pool,
 182                               VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING,
 183                               &have_cursor);
 184        if (ret)
 185                return false;
 186
 187        return have_hints == VINF_SUCCESS && have_cursor == VINF_SUCCESS;
 188}
 189
 190bool vbox_check_supported(u16 id)
 191{
 192        u16 dispi_id;
 193
 194        vbox_write_ioport(VBE_DISPI_INDEX_ID, id);
 195        dispi_id = inw(VBE_DISPI_IOPORT_DATA);
 196
 197        return dispi_id == id;
 198}
 199
 200int vbox_hw_init(struct vbox_private *vbox)
 201{
 202        int ret = -ENOMEM;
 203
 204        vbox->full_vram_size = inl(VBE_DISPI_IOPORT_DATA);
 205        vbox->any_pitch = vbox_check_supported(VBE_DISPI_ID_ANYX);
 206
 207        DRM_INFO("VRAM %08x\n", vbox->full_vram_size);
 208
 209        /* Map guest-heap at end of vram */
 210        vbox->guest_heap =
 211            pci_iomap_range(vbox->ddev.pdev, 0, GUEST_HEAP_OFFSET(vbox),
 212                            GUEST_HEAP_SIZE);
 213        if (!vbox->guest_heap)
 214                return -ENOMEM;
 215
 216        /* Create guest-heap mem-pool use 2^4 = 16 byte chunks */
 217        vbox->guest_pool = gen_pool_create(4, -1);
 218        if (!vbox->guest_pool)
 219                goto err_unmap_guest_heap;
 220
 221        ret = gen_pool_add_virt(vbox->guest_pool,
 222                                (unsigned long)vbox->guest_heap,
 223                                GUEST_HEAP_OFFSET(vbox),
 224                                GUEST_HEAP_USABLE_SIZE, -1);
 225        if (ret)
 226                goto err_destroy_guest_pool;
 227
 228        ret = hgsmi_test_query_conf(vbox->guest_pool);
 229        if (ret) {
 230                DRM_ERROR("vboxvideo: hgsmi_test_query_conf failed\n");
 231                goto err_destroy_guest_pool;
 232        }
 233
 234        /* Reduce available VRAM size to reflect the guest heap. */
 235        vbox->available_vram_size = GUEST_HEAP_OFFSET(vbox);
 236        /* Linux drm represents monitors as a 32-bit array. */
 237        hgsmi_query_conf(vbox->guest_pool, VBOX_VBVA_CONF32_MONITOR_COUNT,
 238                         &vbox->num_crtcs);
 239        vbox->num_crtcs = clamp_t(u32, vbox->num_crtcs, 1, VBOX_MAX_SCREENS);
 240
 241        if (!have_hgsmi_mode_hints(vbox)) {
 242                ret = -ENOTSUPP;
 243                goto err_destroy_guest_pool;
 244        }
 245
 246        vbox->last_mode_hints = devm_kcalloc(vbox->ddev.dev, vbox->num_crtcs,
 247                                             sizeof(struct vbva_modehint),
 248                                             GFP_KERNEL);
 249        if (!vbox->last_mode_hints) {
 250                ret = -ENOMEM;
 251                goto err_destroy_guest_pool;
 252        }
 253
 254        ret = vbox_accel_init(vbox);
 255        if (ret)
 256                goto err_destroy_guest_pool;
 257
 258        return 0;
 259
 260err_destroy_guest_pool:
 261        gen_pool_destroy(vbox->guest_pool);
 262err_unmap_guest_heap:
 263        pci_iounmap(vbox->ddev.pdev, vbox->guest_heap);
 264        return ret;
 265}
 266
 267void vbox_hw_fini(struct vbox_private *vbox)
 268{
 269        vbox_accel_fini(vbox);
 270        gen_pool_destroy(vbox->guest_pool);
 271        pci_iounmap(vbox->ddev.pdev, vbox->guest_heap);
 272}
 273
 274int vbox_gem_create(struct vbox_private *vbox,
 275                    u32 size, bool iskernel, struct drm_gem_object **obj)
 276{
 277        struct drm_gem_vram_object *gbo;
 278        int ret;
 279
 280        *obj = NULL;
 281
 282        size = roundup(size, PAGE_SIZE);
 283        if (size == 0)
 284                return -EINVAL;
 285
 286        gbo = drm_gem_vram_create(&vbox->ddev, &vbox->ddev.vram_mm->bdev,
 287                                  size, 0, false);
 288        if (IS_ERR(gbo)) {
 289                ret = PTR_ERR(gbo);
 290                if (ret != -ERESTARTSYS)
 291                        DRM_ERROR("failed to allocate GEM object\n");
 292                return ret;
 293        }
 294
 295        *obj = &gbo->gem;
 296
 297        return 0;
 298}
 299