1131abc56SHans de Goede // SPDX-License-Identifier: MIT 2131abc56SHans de Goede /* 3131abc56SHans de Goede * Copyright (C) 2013-2017 Oracle Corporation 4131abc56SHans de Goede * This file is based on ast_main.c 5131abc56SHans de Goede * Copyright 2012 Red Hat Inc. 6131abc56SHans de Goede * Authors: Dave Airlie <airlied@redhat.com>, 7131abc56SHans de Goede * Michael Thayer <michael.thayer@oracle.com, 8131abc56SHans de Goede * Hans de Goede <hdegoede@redhat.com> 9131abc56SHans de Goede */ 10131abc56SHans de Goede 11131abc56SHans de Goede #include <linux/vbox_err.h> 12131abc56SHans de Goede #include <drm/drm_fb_helper.h> 13131abc56SHans de Goede #include <drm/drm_crtc_helper.h> 14131abc56SHans de Goede 15131abc56SHans de Goede #include "vbox_drv.h" 16131abc56SHans de Goede #include "vboxvideo_guest.h" 17131abc56SHans de Goede #include "vboxvideo_vbe.h" 18131abc56SHans de Goede 19131abc56SHans de Goede static void vbox_user_framebuffer_destroy(struct drm_framebuffer *fb) 20131abc56SHans de Goede { 21131abc56SHans de Goede struct vbox_framebuffer *vbox_fb = to_vbox_framebuffer(fb); 22131abc56SHans de Goede 23131abc56SHans de Goede if (vbox_fb->obj) 24131abc56SHans de Goede drm_gem_object_put_unlocked(vbox_fb->obj); 25131abc56SHans de Goede 26131abc56SHans de Goede drm_framebuffer_cleanup(fb); 27131abc56SHans de Goede kfree(fb); 28131abc56SHans de Goede } 29131abc56SHans de Goede 30131abc56SHans de Goede void vbox_report_caps(struct vbox_private *vbox) 31131abc56SHans de Goede { 32131abc56SHans de Goede u32 caps = VBVACAPS_DISABLE_CURSOR_INTEGRATION | 33131abc56SHans de Goede VBVACAPS_IRQ | VBVACAPS_USE_VBVA_ONLY; 34131abc56SHans de Goede 35131abc56SHans de Goede /* The host only accepts VIDEO_MODE_HINTS if it is send separately. */ 36131abc56SHans de Goede hgsmi_send_caps_info(vbox->guest_pool, caps); 37131abc56SHans de Goede caps |= VBVACAPS_VIDEO_MODE_HINTS; 38131abc56SHans de Goede hgsmi_send_caps_info(vbox->guest_pool, caps); 39131abc56SHans de Goede } 40131abc56SHans de Goede 41131abc56SHans de Goede /* Send information about dirty rectangles to VBVA. */ 42131abc56SHans de Goede void vbox_framebuffer_dirty_rectangles(struct drm_framebuffer *fb, 43131abc56SHans de Goede struct drm_clip_rect *rects, 44131abc56SHans de Goede unsigned int num_rects) 45131abc56SHans de Goede { 46131abc56SHans de Goede struct vbox_private *vbox = fb->dev->dev_private; 47131abc56SHans de Goede struct drm_display_mode *mode; 48131abc56SHans de Goede struct drm_crtc *crtc; 49131abc56SHans de Goede int crtc_x, crtc_y; 50131abc56SHans de Goede unsigned int i; 51131abc56SHans de Goede 52131abc56SHans de Goede mutex_lock(&vbox->hw_mutex); 53131abc56SHans de Goede list_for_each_entry(crtc, &fb->dev->mode_config.crtc_list, head) { 54131abc56SHans de Goede if (crtc->primary->state->fb != fb) 55131abc56SHans de Goede continue; 56131abc56SHans de Goede 57131abc56SHans de Goede mode = &crtc->state->mode; 58131abc56SHans de Goede crtc_x = crtc->primary->state->src_x >> 16; 59131abc56SHans de Goede crtc_y = crtc->primary->state->src_y >> 16; 60131abc56SHans de Goede 61131abc56SHans de Goede for (i = 0; i < num_rects; ++i) { 62131abc56SHans de Goede struct vbva_cmd_hdr cmd_hdr; 63131abc56SHans de Goede unsigned int crtc_id = to_vbox_crtc(crtc)->crtc_id; 64131abc56SHans de Goede 65131abc56SHans de Goede if (rects[i].x1 > crtc_x + mode->hdisplay || 66131abc56SHans de Goede rects[i].y1 > crtc_y + mode->vdisplay || 67131abc56SHans de Goede rects[i].x2 < crtc_x || 68131abc56SHans de Goede rects[i].y2 < crtc_y) 69131abc56SHans de Goede continue; 70131abc56SHans de Goede 71131abc56SHans de Goede cmd_hdr.x = (s16)rects[i].x1; 72131abc56SHans de Goede cmd_hdr.y = (s16)rects[i].y1; 73131abc56SHans de Goede cmd_hdr.w = (u16)rects[i].x2 - rects[i].x1; 74131abc56SHans de Goede cmd_hdr.h = (u16)rects[i].y2 - rects[i].y1; 75131abc56SHans de Goede 76131abc56SHans de Goede if (!vbva_buffer_begin_update(&vbox->vbva_info[crtc_id], 77131abc56SHans de Goede vbox->guest_pool)) 78131abc56SHans de Goede continue; 79131abc56SHans de Goede 80131abc56SHans de Goede vbva_write(&vbox->vbva_info[crtc_id], vbox->guest_pool, 81131abc56SHans de Goede &cmd_hdr, sizeof(cmd_hdr)); 82131abc56SHans de Goede vbva_buffer_end_update(&vbox->vbva_info[crtc_id]); 83131abc56SHans de Goede } 84131abc56SHans de Goede } 85131abc56SHans de Goede mutex_unlock(&vbox->hw_mutex); 86131abc56SHans de Goede } 87131abc56SHans de Goede 88131abc56SHans de Goede static int vbox_user_framebuffer_dirty(struct drm_framebuffer *fb, 89131abc56SHans de Goede struct drm_file *file_priv, 90131abc56SHans de Goede unsigned int flags, unsigned int color, 91131abc56SHans de Goede struct drm_clip_rect *rects, 92131abc56SHans de Goede unsigned int num_rects) 93131abc56SHans de Goede { 94131abc56SHans de Goede vbox_framebuffer_dirty_rectangles(fb, rects, num_rects); 95131abc56SHans de Goede 96131abc56SHans de Goede return 0; 97131abc56SHans de Goede } 98131abc56SHans de Goede 99131abc56SHans de Goede static const struct drm_framebuffer_funcs vbox_fb_funcs = { 100131abc56SHans de Goede .destroy = vbox_user_framebuffer_destroy, 101131abc56SHans de Goede .dirty = vbox_user_framebuffer_dirty, 102131abc56SHans de Goede }; 103131abc56SHans de Goede 104131abc56SHans de Goede int vbox_framebuffer_init(struct vbox_private *vbox, 105131abc56SHans de Goede struct vbox_framebuffer *vbox_fb, 106131abc56SHans de Goede const struct drm_mode_fb_cmd2 *mode_cmd, 107131abc56SHans de Goede struct drm_gem_object *obj) 108131abc56SHans de Goede { 109131abc56SHans de Goede int ret; 110131abc56SHans de Goede 111131abc56SHans de Goede drm_helper_mode_fill_fb_struct(&vbox->ddev, &vbox_fb->base, mode_cmd); 112131abc56SHans de Goede vbox_fb->obj = obj; 113131abc56SHans de Goede ret = drm_framebuffer_init(&vbox->ddev, &vbox_fb->base, &vbox_fb_funcs); 114131abc56SHans de Goede if (ret) { 115131abc56SHans de Goede DRM_ERROR("framebuffer init failed %d\n", ret); 116131abc56SHans de Goede return ret; 117131abc56SHans de Goede } 118131abc56SHans de Goede 119131abc56SHans de Goede return 0; 120131abc56SHans de Goede } 121131abc56SHans de Goede 122131abc56SHans de Goede static int vbox_accel_init(struct vbox_private *vbox) 123131abc56SHans de Goede { 124131abc56SHans de Goede struct vbva_buffer *vbva; 125131abc56SHans de Goede unsigned int i; 126131abc56SHans de Goede 127131abc56SHans de Goede vbox->vbva_info = devm_kcalloc(vbox->ddev.dev, vbox->num_crtcs, 128131abc56SHans de Goede sizeof(*vbox->vbva_info), GFP_KERNEL); 129131abc56SHans de Goede if (!vbox->vbva_info) 130131abc56SHans de Goede return -ENOMEM; 131131abc56SHans de Goede 132131abc56SHans de Goede /* Take a command buffer for each screen from the end of usable VRAM. */ 133131abc56SHans de Goede vbox->available_vram_size -= vbox->num_crtcs * VBVA_MIN_BUFFER_SIZE; 134131abc56SHans de Goede 135131abc56SHans de Goede vbox->vbva_buffers = pci_iomap_range(vbox->ddev.pdev, 0, 136131abc56SHans de Goede vbox->available_vram_size, 137131abc56SHans de Goede vbox->num_crtcs * 138131abc56SHans de Goede VBVA_MIN_BUFFER_SIZE); 139131abc56SHans de Goede if (!vbox->vbva_buffers) 140131abc56SHans de Goede return -ENOMEM; 141131abc56SHans de Goede 142131abc56SHans de Goede for (i = 0; i < vbox->num_crtcs; ++i) { 143131abc56SHans de Goede vbva_setup_buffer_context(&vbox->vbva_info[i], 144131abc56SHans de Goede vbox->available_vram_size + 145131abc56SHans de Goede i * VBVA_MIN_BUFFER_SIZE, 146131abc56SHans de Goede VBVA_MIN_BUFFER_SIZE); 147131abc56SHans de Goede vbva = (void __force *)vbox->vbva_buffers + 148131abc56SHans de Goede i * VBVA_MIN_BUFFER_SIZE; 149131abc56SHans de Goede if (!vbva_enable(&vbox->vbva_info[i], 150131abc56SHans de Goede vbox->guest_pool, vbva, i)) { 151131abc56SHans de Goede /* very old host or driver error. */ 152131abc56SHans de Goede DRM_ERROR("vboxvideo: vbva_enable failed\n"); 153131abc56SHans de Goede } 154131abc56SHans de Goede } 155131abc56SHans de Goede 156131abc56SHans de Goede return 0; 157131abc56SHans de Goede } 158131abc56SHans de Goede 159131abc56SHans de Goede static void vbox_accel_fini(struct vbox_private *vbox) 160131abc56SHans de Goede { 161131abc56SHans de Goede unsigned int i; 162131abc56SHans de Goede 163131abc56SHans de Goede for (i = 0; i < vbox->num_crtcs; ++i) 164131abc56SHans de Goede vbva_disable(&vbox->vbva_info[i], vbox->guest_pool, i); 165131abc56SHans de Goede 166131abc56SHans de Goede pci_iounmap(vbox->ddev.pdev, vbox->vbva_buffers); 167131abc56SHans de Goede } 168131abc56SHans de Goede 169131abc56SHans de Goede /* Do we support the 4.3 plus mode hint reporting interface? */ 170131abc56SHans de Goede static bool have_hgsmi_mode_hints(struct vbox_private *vbox) 171131abc56SHans de Goede { 172131abc56SHans de Goede u32 have_hints, have_cursor; 173131abc56SHans de Goede int ret; 174131abc56SHans de Goede 175131abc56SHans de Goede ret = hgsmi_query_conf(vbox->guest_pool, 176131abc56SHans de Goede VBOX_VBVA_CONF32_MODE_HINT_REPORTING, 177131abc56SHans de Goede &have_hints); 178131abc56SHans de Goede if (ret) 179131abc56SHans de Goede return false; 180131abc56SHans de Goede 181131abc56SHans de Goede ret = hgsmi_query_conf(vbox->guest_pool, 182131abc56SHans de Goede VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING, 183131abc56SHans de Goede &have_cursor); 184131abc56SHans de Goede if (ret) 185131abc56SHans de Goede return false; 186131abc56SHans de Goede 187131abc56SHans de Goede return have_hints == VINF_SUCCESS && have_cursor == VINF_SUCCESS; 188131abc56SHans de Goede } 189131abc56SHans de Goede 190131abc56SHans de Goede bool vbox_check_supported(u16 id) 191131abc56SHans de Goede { 192131abc56SHans de Goede u16 dispi_id; 193131abc56SHans de Goede 194131abc56SHans de Goede vbox_write_ioport(VBE_DISPI_INDEX_ID, id); 195131abc56SHans de Goede dispi_id = inw(VBE_DISPI_IOPORT_DATA); 196131abc56SHans de Goede 197131abc56SHans de Goede return dispi_id == id; 198131abc56SHans de Goede } 199131abc56SHans de Goede 200131abc56SHans de Goede int vbox_hw_init(struct vbox_private *vbox) 201131abc56SHans de Goede { 202131abc56SHans de Goede int ret = -ENOMEM; 203131abc56SHans de Goede 204131abc56SHans de Goede vbox->full_vram_size = inl(VBE_DISPI_IOPORT_DATA); 205131abc56SHans de Goede vbox->any_pitch = vbox_check_supported(VBE_DISPI_ID_ANYX); 206131abc56SHans de Goede 207131abc56SHans de Goede DRM_INFO("VRAM %08x\n", vbox->full_vram_size); 208131abc56SHans de Goede 209131abc56SHans de Goede /* Map guest-heap at end of vram */ 210131abc56SHans de Goede vbox->guest_heap = 211131abc56SHans de Goede pci_iomap_range(vbox->ddev.pdev, 0, GUEST_HEAP_OFFSET(vbox), 212131abc56SHans de Goede GUEST_HEAP_SIZE); 213131abc56SHans de Goede if (!vbox->guest_heap) 214131abc56SHans de Goede return -ENOMEM; 215131abc56SHans de Goede 216131abc56SHans de Goede /* Create guest-heap mem-pool use 2^4 = 16 byte chunks */ 217131abc56SHans de Goede vbox->guest_pool = gen_pool_create(4, -1); 218131abc56SHans de Goede if (!vbox->guest_pool) 219131abc56SHans de Goede goto err_unmap_guest_heap; 220131abc56SHans de Goede 221131abc56SHans de Goede ret = gen_pool_add_virt(vbox->guest_pool, 222131abc56SHans de Goede (unsigned long)vbox->guest_heap, 223131abc56SHans de Goede GUEST_HEAP_OFFSET(vbox), 224131abc56SHans de Goede GUEST_HEAP_USABLE_SIZE, -1); 225131abc56SHans de Goede if (ret) 226131abc56SHans de Goede goto err_destroy_guest_pool; 227131abc56SHans de Goede 228131abc56SHans de Goede ret = hgsmi_test_query_conf(vbox->guest_pool); 229131abc56SHans de Goede if (ret) { 230131abc56SHans de Goede DRM_ERROR("vboxvideo: hgsmi_test_query_conf failed\n"); 231131abc56SHans de Goede goto err_destroy_guest_pool; 232131abc56SHans de Goede } 233131abc56SHans de Goede 234131abc56SHans de Goede /* Reduce available VRAM size to reflect the guest heap. */ 235131abc56SHans de Goede vbox->available_vram_size = GUEST_HEAP_OFFSET(vbox); 236131abc56SHans de Goede /* Linux drm represents monitors as a 32-bit array. */ 237131abc56SHans de Goede hgsmi_query_conf(vbox->guest_pool, VBOX_VBVA_CONF32_MONITOR_COUNT, 238131abc56SHans de Goede &vbox->num_crtcs); 239131abc56SHans de Goede vbox->num_crtcs = clamp_t(u32, vbox->num_crtcs, 1, VBOX_MAX_SCREENS); 240131abc56SHans de Goede 241131abc56SHans de Goede if (!have_hgsmi_mode_hints(vbox)) { 242131abc56SHans de Goede ret = -ENOTSUPP; 243131abc56SHans de Goede goto err_destroy_guest_pool; 244131abc56SHans de Goede } 245131abc56SHans de Goede 246131abc56SHans de Goede vbox->last_mode_hints = devm_kcalloc(vbox->ddev.dev, vbox->num_crtcs, 247131abc56SHans de Goede sizeof(struct vbva_modehint), 248131abc56SHans de Goede GFP_KERNEL); 249131abc56SHans de Goede if (!vbox->last_mode_hints) { 250131abc56SHans de Goede ret = -ENOMEM; 251131abc56SHans de Goede goto err_destroy_guest_pool; 252131abc56SHans de Goede } 253131abc56SHans de Goede 254131abc56SHans de Goede ret = vbox_accel_init(vbox); 255131abc56SHans de Goede if (ret) 256131abc56SHans de Goede goto err_destroy_guest_pool; 257131abc56SHans de Goede 258131abc56SHans de Goede return 0; 259131abc56SHans de Goede 260131abc56SHans de Goede err_destroy_guest_pool: 261131abc56SHans de Goede gen_pool_destroy(vbox->guest_pool); 262131abc56SHans de Goede err_unmap_guest_heap: 263131abc56SHans de Goede pci_iounmap(vbox->ddev.pdev, vbox->guest_heap); 264131abc56SHans de Goede return ret; 265131abc56SHans de Goede } 266131abc56SHans de Goede 267131abc56SHans de Goede void vbox_hw_fini(struct vbox_private *vbox) 268131abc56SHans de Goede { 269131abc56SHans de Goede vbox_accel_fini(vbox); 270131abc56SHans de Goede gen_pool_destroy(vbox->guest_pool); 271131abc56SHans de Goede pci_iounmap(vbox->ddev.pdev, vbox->guest_heap); 272131abc56SHans de Goede } 273131abc56SHans de Goede 274131abc56SHans de Goede int vbox_gem_create(struct vbox_private *vbox, 275131abc56SHans de Goede u32 size, bool iskernel, struct drm_gem_object **obj) 276131abc56SHans de Goede { 2775e6b9255SThomas Zimmermann struct drm_gem_vram_object *gbo; 278131abc56SHans de Goede int ret; 279131abc56SHans de Goede 280131abc56SHans de Goede *obj = NULL; 281131abc56SHans de Goede 282131abc56SHans de Goede size = roundup(size, PAGE_SIZE); 283131abc56SHans de Goede if (size == 0) 284131abc56SHans de Goede return -EINVAL; 285131abc56SHans de Goede 28694065bf5SThomas Zimmermann gbo = drm_gem_vram_create(&vbox->ddev, &vbox->ddev.vram_mm->bdev, 28794065bf5SThomas Zimmermann size, 0, false); 2885e6b9255SThomas Zimmermann if (IS_ERR(gbo)) { 2895e6b9255SThomas Zimmermann ret = PTR_ERR(gbo); 290131abc56SHans de Goede if (ret != -ERESTARTSYS) 291131abc56SHans de Goede DRM_ERROR("failed to allocate GEM object\n"); 292131abc56SHans de Goede return ret; 293131abc56SHans de Goede } 294131abc56SHans de Goede 2955e6b9255SThomas Zimmermann *obj = &gbo->gem; 296131abc56SHans de Goede 297131abc56SHans de Goede return 0; 298131abc56SHans de Goede } 299