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> 141a74ccfaSThomas Zimmermann #include <drm/drm_damage_helper.h> 15131abc56SHans de Goede 16131abc56SHans de Goede #include "vbox_drv.h" 17131abc56SHans de Goede #include "vboxvideo_guest.h" 18131abc56SHans de Goede #include "vboxvideo_vbe.h" 19131abc56SHans de Goede 20131abc56SHans de Goede static void vbox_user_framebuffer_destroy(struct drm_framebuffer *fb) 21131abc56SHans de Goede { 22131abc56SHans de Goede struct vbox_framebuffer *vbox_fb = to_vbox_framebuffer(fb); 23131abc56SHans de Goede 24131abc56SHans de Goede if (vbox_fb->obj) 25131abc56SHans de Goede drm_gem_object_put_unlocked(vbox_fb->obj); 26131abc56SHans de Goede 27131abc56SHans de Goede drm_framebuffer_cleanup(fb); 28131abc56SHans de Goede kfree(fb); 29131abc56SHans de Goede } 30131abc56SHans de Goede 31131abc56SHans de Goede void vbox_report_caps(struct vbox_private *vbox) 32131abc56SHans de Goede { 33131abc56SHans de Goede u32 caps = VBVACAPS_DISABLE_CURSOR_INTEGRATION | 34131abc56SHans de Goede VBVACAPS_IRQ | VBVACAPS_USE_VBVA_ONLY; 35131abc56SHans de Goede 36131abc56SHans de Goede /* The host only accepts VIDEO_MODE_HINTS if it is send separately. */ 37131abc56SHans de Goede hgsmi_send_caps_info(vbox->guest_pool, caps); 38131abc56SHans de Goede caps |= VBVACAPS_VIDEO_MODE_HINTS; 39131abc56SHans de Goede hgsmi_send_caps_info(vbox->guest_pool, caps); 40131abc56SHans de Goede } 41131abc56SHans de Goede 42131abc56SHans de Goede static const struct drm_framebuffer_funcs vbox_fb_funcs = { 43131abc56SHans de Goede .destroy = vbox_user_framebuffer_destroy, 441a74ccfaSThomas Zimmermann .dirty = drm_atomic_helper_dirtyfb, 45131abc56SHans de Goede }; 46131abc56SHans de Goede 47131abc56SHans de Goede int vbox_framebuffer_init(struct vbox_private *vbox, 48131abc56SHans de Goede struct vbox_framebuffer *vbox_fb, 49131abc56SHans de Goede const struct drm_mode_fb_cmd2 *mode_cmd, 50131abc56SHans de Goede struct drm_gem_object *obj) 51131abc56SHans de Goede { 52131abc56SHans de Goede int ret; 53131abc56SHans de Goede 54131abc56SHans de Goede drm_helper_mode_fill_fb_struct(&vbox->ddev, &vbox_fb->base, mode_cmd); 55131abc56SHans de Goede vbox_fb->obj = obj; 56131abc56SHans de Goede ret = drm_framebuffer_init(&vbox->ddev, &vbox_fb->base, &vbox_fb_funcs); 57131abc56SHans de Goede if (ret) { 58131abc56SHans de Goede DRM_ERROR("framebuffer init failed %d\n", ret); 59131abc56SHans de Goede return ret; 60131abc56SHans de Goede } 61131abc56SHans de Goede 62131abc56SHans de Goede return 0; 63131abc56SHans de Goede } 64131abc56SHans de Goede 65131abc56SHans de Goede static int vbox_accel_init(struct vbox_private *vbox) 66131abc56SHans de Goede { 67131abc56SHans de Goede struct vbva_buffer *vbva; 68131abc56SHans de Goede unsigned int i; 69131abc56SHans de Goede 70131abc56SHans de Goede vbox->vbva_info = devm_kcalloc(vbox->ddev.dev, vbox->num_crtcs, 71131abc56SHans de Goede sizeof(*vbox->vbva_info), GFP_KERNEL); 72131abc56SHans de Goede if (!vbox->vbva_info) 73131abc56SHans de Goede return -ENOMEM; 74131abc56SHans de Goede 75131abc56SHans de Goede /* Take a command buffer for each screen from the end of usable VRAM. */ 76131abc56SHans de Goede vbox->available_vram_size -= vbox->num_crtcs * VBVA_MIN_BUFFER_SIZE; 77131abc56SHans de Goede 78131abc56SHans de Goede vbox->vbva_buffers = pci_iomap_range(vbox->ddev.pdev, 0, 79131abc56SHans de Goede vbox->available_vram_size, 80131abc56SHans de Goede vbox->num_crtcs * 81131abc56SHans de Goede VBVA_MIN_BUFFER_SIZE); 82131abc56SHans de Goede if (!vbox->vbva_buffers) 83131abc56SHans de Goede return -ENOMEM; 84131abc56SHans de Goede 85131abc56SHans de Goede for (i = 0; i < vbox->num_crtcs; ++i) { 86131abc56SHans de Goede vbva_setup_buffer_context(&vbox->vbva_info[i], 87131abc56SHans de Goede vbox->available_vram_size + 88131abc56SHans de Goede i * VBVA_MIN_BUFFER_SIZE, 89131abc56SHans de Goede VBVA_MIN_BUFFER_SIZE); 90131abc56SHans de Goede vbva = (void __force *)vbox->vbva_buffers + 91131abc56SHans de Goede i * VBVA_MIN_BUFFER_SIZE; 92131abc56SHans de Goede if (!vbva_enable(&vbox->vbva_info[i], 93131abc56SHans de Goede vbox->guest_pool, vbva, i)) { 94131abc56SHans de Goede /* very old host or driver error. */ 95131abc56SHans de Goede DRM_ERROR("vboxvideo: vbva_enable failed\n"); 96131abc56SHans de Goede } 97131abc56SHans de Goede } 98131abc56SHans de Goede 99131abc56SHans de Goede return 0; 100131abc56SHans de Goede } 101131abc56SHans de Goede 102131abc56SHans de Goede static void vbox_accel_fini(struct vbox_private *vbox) 103131abc56SHans de Goede { 104131abc56SHans de Goede unsigned int i; 105131abc56SHans de Goede 106131abc56SHans de Goede for (i = 0; i < vbox->num_crtcs; ++i) 107131abc56SHans de Goede vbva_disable(&vbox->vbva_info[i], vbox->guest_pool, i); 108131abc56SHans de Goede 109131abc56SHans de Goede pci_iounmap(vbox->ddev.pdev, vbox->vbva_buffers); 110131abc56SHans de Goede } 111131abc56SHans de Goede 112131abc56SHans de Goede /* Do we support the 4.3 plus mode hint reporting interface? */ 113131abc56SHans de Goede static bool have_hgsmi_mode_hints(struct vbox_private *vbox) 114131abc56SHans de Goede { 115131abc56SHans de Goede u32 have_hints, have_cursor; 116131abc56SHans de Goede int ret; 117131abc56SHans de Goede 118131abc56SHans de Goede ret = hgsmi_query_conf(vbox->guest_pool, 119131abc56SHans de Goede VBOX_VBVA_CONF32_MODE_HINT_REPORTING, 120131abc56SHans de Goede &have_hints); 121131abc56SHans de Goede if (ret) 122131abc56SHans de Goede return false; 123131abc56SHans de Goede 124131abc56SHans de Goede ret = hgsmi_query_conf(vbox->guest_pool, 125131abc56SHans de Goede VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING, 126131abc56SHans de Goede &have_cursor); 127131abc56SHans de Goede if (ret) 128131abc56SHans de Goede return false; 129131abc56SHans de Goede 130131abc56SHans de Goede return have_hints == VINF_SUCCESS && have_cursor == VINF_SUCCESS; 131131abc56SHans de Goede } 132131abc56SHans de Goede 133131abc56SHans de Goede bool vbox_check_supported(u16 id) 134131abc56SHans de Goede { 135131abc56SHans de Goede u16 dispi_id; 136131abc56SHans de Goede 137131abc56SHans de Goede vbox_write_ioport(VBE_DISPI_INDEX_ID, id); 138131abc56SHans de Goede dispi_id = inw(VBE_DISPI_IOPORT_DATA); 139131abc56SHans de Goede 140131abc56SHans de Goede return dispi_id == id; 141131abc56SHans de Goede } 142131abc56SHans de Goede 143131abc56SHans de Goede int vbox_hw_init(struct vbox_private *vbox) 144131abc56SHans de Goede { 145131abc56SHans de Goede int ret = -ENOMEM; 146131abc56SHans de Goede 147131abc56SHans de Goede vbox->full_vram_size = inl(VBE_DISPI_IOPORT_DATA); 148131abc56SHans de Goede vbox->any_pitch = vbox_check_supported(VBE_DISPI_ID_ANYX); 149131abc56SHans de Goede 150131abc56SHans de Goede DRM_INFO("VRAM %08x\n", vbox->full_vram_size); 151131abc56SHans de Goede 152131abc56SHans de Goede /* Map guest-heap at end of vram */ 153131abc56SHans de Goede vbox->guest_heap = 154131abc56SHans de Goede pci_iomap_range(vbox->ddev.pdev, 0, GUEST_HEAP_OFFSET(vbox), 155131abc56SHans de Goede GUEST_HEAP_SIZE); 156131abc56SHans de Goede if (!vbox->guest_heap) 157131abc56SHans de Goede return -ENOMEM; 158131abc56SHans de Goede 159131abc56SHans de Goede /* Create guest-heap mem-pool use 2^4 = 16 byte chunks */ 160131abc56SHans de Goede vbox->guest_pool = gen_pool_create(4, -1); 161131abc56SHans de Goede if (!vbox->guest_pool) 162131abc56SHans de Goede goto err_unmap_guest_heap; 163131abc56SHans de Goede 164131abc56SHans de Goede ret = gen_pool_add_virt(vbox->guest_pool, 165131abc56SHans de Goede (unsigned long)vbox->guest_heap, 166131abc56SHans de Goede GUEST_HEAP_OFFSET(vbox), 167131abc56SHans de Goede GUEST_HEAP_USABLE_SIZE, -1); 168131abc56SHans de Goede if (ret) 169131abc56SHans de Goede goto err_destroy_guest_pool; 170131abc56SHans de Goede 171131abc56SHans de Goede ret = hgsmi_test_query_conf(vbox->guest_pool); 172131abc56SHans de Goede if (ret) { 173131abc56SHans de Goede DRM_ERROR("vboxvideo: hgsmi_test_query_conf failed\n"); 174131abc56SHans de Goede goto err_destroy_guest_pool; 175131abc56SHans de Goede } 176131abc56SHans de Goede 177131abc56SHans de Goede /* Reduce available VRAM size to reflect the guest heap. */ 178131abc56SHans de Goede vbox->available_vram_size = GUEST_HEAP_OFFSET(vbox); 179131abc56SHans de Goede /* Linux drm represents monitors as a 32-bit array. */ 180131abc56SHans de Goede hgsmi_query_conf(vbox->guest_pool, VBOX_VBVA_CONF32_MONITOR_COUNT, 181131abc56SHans de Goede &vbox->num_crtcs); 182131abc56SHans de Goede vbox->num_crtcs = clamp_t(u32, vbox->num_crtcs, 1, VBOX_MAX_SCREENS); 183131abc56SHans de Goede 184131abc56SHans de Goede if (!have_hgsmi_mode_hints(vbox)) { 185131abc56SHans de Goede ret = -ENOTSUPP; 186131abc56SHans de Goede goto err_destroy_guest_pool; 187131abc56SHans de Goede } 188131abc56SHans de Goede 189131abc56SHans de Goede vbox->last_mode_hints = devm_kcalloc(vbox->ddev.dev, vbox->num_crtcs, 190131abc56SHans de Goede sizeof(struct vbva_modehint), 191131abc56SHans de Goede GFP_KERNEL); 192131abc56SHans de Goede if (!vbox->last_mode_hints) { 193131abc56SHans de Goede ret = -ENOMEM; 194131abc56SHans de Goede goto err_destroy_guest_pool; 195131abc56SHans de Goede } 196131abc56SHans de Goede 197131abc56SHans de Goede ret = vbox_accel_init(vbox); 198131abc56SHans de Goede if (ret) 199131abc56SHans de Goede goto err_destroy_guest_pool; 200131abc56SHans de Goede 201131abc56SHans de Goede return 0; 202131abc56SHans de Goede 203131abc56SHans de Goede err_destroy_guest_pool: 204131abc56SHans de Goede gen_pool_destroy(vbox->guest_pool); 205131abc56SHans de Goede err_unmap_guest_heap: 206131abc56SHans de Goede pci_iounmap(vbox->ddev.pdev, vbox->guest_heap); 207131abc56SHans de Goede return ret; 208131abc56SHans de Goede } 209131abc56SHans de Goede 210131abc56SHans de Goede void vbox_hw_fini(struct vbox_private *vbox) 211131abc56SHans de Goede { 212131abc56SHans de Goede vbox_accel_fini(vbox); 213131abc56SHans de Goede gen_pool_destroy(vbox->guest_pool); 214131abc56SHans de Goede pci_iounmap(vbox->ddev.pdev, vbox->guest_heap); 215131abc56SHans de Goede } 216131abc56SHans de Goede 217131abc56SHans de Goede int vbox_gem_create(struct vbox_private *vbox, 218131abc56SHans de Goede u32 size, bool iskernel, struct drm_gem_object **obj) 219131abc56SHans de Goede { 2205e6b9255SThomas Zimmermann struct drm_gem_vram_object *gbo; 221131abc56SHans de Goede int ret; 222131abc56SHans de Goede 223131abc56SHans de Goede *obj = NULL; 224131abc56SHans de Goede 225131abc56SHans de Goede size = roundup(size, PAGE_SIZE); 226131abc56SHans de Goede if (size == 0) 227131abc56SHans de Goede return -EINVAL; 228131abc56SHans de Goede 22994065bf5SThomas Zimmermann gbo = drm_gem_vram_create(&vbox->ddev, &vbox->ddev.vram_mm->bdev, 23094065bf5SThomas Zimmermann size, 0, false); 2315e6b9255SThomas Zimmermann if (IS_ERR(gbo)) { 2325e6b9255SThomas Zimmermann ret = PTR_ERR(gbo); 233131abc56SHans de Goede if (ret != -ERESTARTSYS) 234131abc56SHans de Goede DRM_ERROR("failed to allocate GEM object\n"); 235131abc56SHans de Goede return ret; 236131abc56SHans de Goede } 237131abc56SHans de Goede 2380e580c6dSGerd Hoffmann *obj = &gbo->bo.base; 239131abc56SHans de Goede 240131abc56SHans de Goede return 0; 241131abc56SHans de Goede } 242