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