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/pci.h> 12 #include <linux/vbox_err.h> 13 14 #include <drm/drm_fb_helper.h> 15 #include <drm/drm_crtc_helper.h> 16 #include <drm/drm_damage_helper.h> 17 18 #include "vbox_drv.h" 19 #include "vboxvideo_guest.h" 20 #include "vboxvideo_vbe.h" 21 22 void vbox_report_caps(struct vbox_private *vbox) 23 { 24 u32 caps = VBVACAPS_DISABLE_CURSOR_INTEGRATION | 25 VBVACAPS_IRQ | VBVACAPS_USE_VBVA_ONLY; 26 27 /* The host only accepts VIDEO_MODE_HINTS if it is send separately. */ 28 hgsmi_send_caps_info(vbox->guest_pool, caps); 29 caps |= VBVACAPS_VIDEO_MODE_HINTS; 30 hgsmi_send_caps_info(vbox->guest_pool, caps); 31 } 32 33 static int vbox_accel_init(struct vbox_private *vbox) 34 { 35 struct pci_dev *pdev = to_pci_dev(vbox->ddev.dev); 36 struct vbva_buffer *vbva; 37 unsigned int i; 38 39 vbox->vbva_info = devm_kcalloc(vbox->ddev.dev, vbox->num_crtcs, 40 sizeof(*vbox->vbva_info), GFP_KERNEL); 41 if (!vbox->vbva_info) 42 return -ENOMEM; 43 44 /* Take a command buffer for each screen from the end of usable VRAM. */ 45 vbox->available_vram_size -= vbox->num_crtcs * VBVA_MIN_BUFFER_SIZE; 46 47 vbox->vbva_buffers = pci_iomap_range(pdev, 0, 48 vbox->available_vram_size, 49 vbox->num_crtcs * 50 VBVA_MIN_BUFFER_SIZE); 51 if (!vbox->vbva_buffers) 52 return -ENOMEM; 53 54 for (i = 0; i < vbox->num_crtcs; ++i) { 55 vbva_setup_buffer_context(&vbox->vbva_info[i], 56 vbox->available_vram_size + 57 i * VBVA_MIN_BUFFER_SIZE, 58 VBVA_MIN_BUFFER_SIZE); 59 vbva = (void __force *)vbox->vbva_buffers + 60 i * VBVA_MIN_BUFFER_SIZE; 61 if (!vbva_enable(&vbox->vbva_info[i], 62 vbox->guest_pool, vbva, i)) { 63 /* very old host or driver error. */ 64 DRM_ERROR("vboxvideo: vbva_enable failed\n"); 65 } 66 } 67 68 return 0; 69 } 70 71 static void vbox_accel_fini(struct vbox_private *vbox) 72 { 73 unsigned int i; 74 75 for (i = 0; i < vbox->num_crtcs; ++i) 76 vbva_disable(&vbox->vbva_info[i], vbox->guest_pool, i); 77 } 78 79 /* Do we support the 4.3 plus mode hint reporting interface? */ 80 static bool have_hgsmi_mode_hints(struct vbox_private *vbox) 81 { 82 u32 have_hints, have_cursor; 83 int ret; 84 85 ret = hgsmi_query_conf(vbox->guest_pool, 86 VBOX_VBVA_CONF32_MODE_HINT_REPORTING, 87 &have_hints); 88 if (ret) 89 return false; 90 91 ret = hgsmi_query_conf(vbox->guest_pool, 92 VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING, 93 &have_cursor); 94 if (ret) 95 return false; 96 97 return have_hints == VINF_SUCCESS && have_cursor == VINF_SUCCESS; 98 } 99 100 bool vbox_check_supported(u16 id) 101 { 102 u16 dispi_id; 103 104 vbox_write_ioport(VBE_DISPI_INDEX_ID, id); 105 dispi_id = inw(VBE_DISPI_IOPORT_DATA); 106 107 return dispi_id == id; 108 } 109 110 int vbox_hw_init(struct vbox_private *vbox) 111 { 112 struct pci_dev *pdev = to_pci_dev(vbox->ddev.dev); 113 int ret = -ENOMEM; 114 115 vbox->full_vram_size = inl(VBE_DISPI_IOPORT_DATA); 116 vbox->any_pitch = vbox_check_supported(VBE_DISPI_ID_ANYX); 117 118 DRM_INFO("VRAM %08x\n", vbox->full_vram_size); 119 120 /* Map guest-heap at end of vram */ 121 vbox->guest_heap = 122 pci_iomap_range(pdev, 0, GUEST_HEAP_OFFSET(vbox), 123 GUEST_HEAP_SIZE); 124 if (!vbox->guest_heap) 125 return -ENOMEM; 126 127 /* Create guest-heap mem-pool use 2^4 = 16 byte chunks */ 128 vbox->guest_pool = devm_gen_pool_create(vbox->ddev.dev, 4, -1, 129 "vboxvideo-accel"); 130 if (!vbox->guest_pool) 131 return -ENOMEM; 132 133 ret = gen_pool_add_virt(vbox->guest_pool, 134 (unsigned long)vbox->guest_heap, 135 GUEST_HEAP_OFFSET(vbox), 136 GUEST_HEAP_USABLE_SIZE, -1); 137 if (ret) 138 return ret; 139 140 ret = hgsmi_test_query_conf(vbox->guest_pool); 141 if (ret) { 142 DRM_ERROR("vboxvideo: hgsmi_test_query_conf failed\n"); 143 return ret; 144 } 145 146 /* Reduce available VRAM size to reflect the guest heap. */ 147 vbox->available_vram_size = GUEST_HEAP_OFFSET(vbox); 148 /* Linux drm represents monitors as a 32-bit array. */ 149 hgsmi_query_conf(vbox->guest_pool, VBOX_VBVA_CONF32_MONITOR_COUNT, 150 &vbox->num_crtcs); 151 vbox->num_crtcs = clamp_t(u32, vbox->num_crtcs, 1, VBOX_MAX_SCREENS); 152 153 if (!have_hgsmi_mode_hints(vbox)) { 154 ret = -ENOTSUPP; 155 return ret; 156 } 157 158 vbox->last_mode_hints = devm_kcalloc(vbox->ddev.dev, vbox->num_crtcs, 159 sizeof(struct vbva_modehint), 160 GFP_KERNEL); 161 if (!vbox->last_mode_hints) 162 return -ENOMEM; 163 164 ret = vbox_accel_init(vbox); 165 if (ret) 166 return ret; 167 168 return 0; 169 } 170 171 void vbox_hw_fini(struct vbox_private *vbox) 172 { 173 vbox_accel_fini(vbox); 174 } 175