Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 */
4
5#include <linux/pci.h>
6
7#include <drm/drm_drv.h>
8#include <drm/drm_fourcc.h>
9
10#include <video/vga.h>
11#include "bochs.h"
12
13/* ---------------------------------------------------------------------- */
14
15static void bochs_vga_writeb(struct bochs_device *bochs, u16 ioport, u8 val)
16{
17 if (WARN_ON(ioport < 0x3c0 || ioport > 0x3df))
18 return;
19
20 if (bochs->mmio) {
21 int offset = ioport - 0x3c0 + 0x400;
22 writeb(val, bochs->mmio + offset);
23 } else {
24 outb(val, ioport);
25 }
26}
27
28static u8 bochs_vga_readb(struct bochs_device *bochs, u16 ioport)
29{
30 if (WARN_ON(ioport < 0x3c0 || ioport > 0x3df))
31 return 0xff;
32
33 if (bochs->mmio) {
34 int offset = ioport - 0x3c0 + 0x400;
35 return readb(bochs->mmio + offset);
36 } else {
37 return inb(ioport);
38 }
39}
40
41static u16 bochs_dispi_read(struct bochs_device *bochs, u16 reg)
42{
43 u16 ret = 0;
44
45 if (bochs->mmio) {
46 int offset = 0x500 + (reg << 1);
47 ret = readw(bochs->mmio + offset);
48 } else {
49 outw(reg, VBE_DISPI_IOPORT_INDEX);
50 ret = inw(VBE_DISPI_IOPORT_DATA);
51 }
52 return ret;
53}
54
55static void bochs_dispi_write(struct bochs_device *bochs, u16 reg, u16 val)
56{
57 if (bochs->mmio) {
58 int offset = 0x500 + (reg << 1);
59 writew(val, bochs->mmio + offset);
60 } else {
61 outw(reg, VBE_DISPI_IOPORT_INDEX);
62 outw(val, VBE_DISPI_IOPORT_DATA);
63 }
64}
65
66static void bochs_hw_set_big_endian(struct bochs_device *bochs)
67{
68 if (bochs->qext_size < 8)
69 return;
70
71 writel(0xbebebebe, bochs->mmio + 0x604);
72}
73
74static void bochs_hw_set_little_endian(struct bochs_device *bochs)
75{
76 if (bochs->qext_size < 8)
77 return;
78
79 writel(0x1e1e1e1e, bochs->mmio + 0x604);
80}
81
82#ifdef __BIG_ENDIAN
83#define bochs_hw_set_native_endian(_b) bochs_hw_set_big_endian(_b)
84#else
85#define bochs_hw_set_native_endian(_b) bochs_hw_set_little_endian(_b)
86#endif
87
88static int bochs_get_edid_block(void *data, u8 *buf,
89 unsigned int block, size_t len)
90{
91 struct bochs_device *bochs = data;
92 size_t i, start = block * EDID_LENGTH;
93
94 if (start + len > 0x400 /* vga register offset */)
95 return -1;
96
97 for (i = 0; i < len; i++) {
98 buf[i] = readb(bochs->mmio + start + i);
99 }
100 return 0;
101}
102
103int bochs_hw_load_edid(struct bochs_device *bochs)
104{
105 u8 header[8];
106
107 if (!bochs->mmio)
108 return -1;
109
110 /* check header to detect whenever edid support is enabled in qemu */
111 bochs_get_edid_block(bochs, header, 0, ARRAY_SIZE(header));
112 if (drm_edid_header_is_valid(header) != 8)
113 return -1;
114
115 kfree(bochs->edid);
116 bochs->edid = drm_do_get_edid(&bochs->connector,
117 bochs_get_edid_block, bochs);
118 if (bochs->edid == NULL)
119 return -1;
120
121 return 0;
122}
123
124int bochs_hw_init(struct drm_device *dev)
125{
126 struct bochs_device *bochs = dev->dev_private;
127 struct pci_dev *pdev = to_pci_dev(dev->dev);
128 unsigned long addr, size, mem, ioaddr, iosize;
129 u16 id;
130
131 if (pdev->resource[2].flags & IORESOURCE_MEM) {
132 /* mmio bar with vga and bochs registers present */
133 if (pci_request_region(pdev, 2, "bochs-drm") != 0) {
134 DRM_ERROR("Cannot request mmio region\n");
135 return -EBUSY;
136 }
137 ioaddr = pci_resource_start(pdev, 2);
138 iosize = pci_resource_len(pdev, 2);
139 bochs->mmio = ioremap(ioaddr, iosize);
140 if (bochs->mmio == NULL) {
141 DRM_ERROR("Cannot map mmio region\n");
142 return -ENOMEM;
143 }
144 } else {
145 ioaddr = VBE_DISPI_IOPORT_INDEX;
146 iosize = 2;
147 if (!request_region(ioaddr, iosize, "bochs-drm")) {
148 DRM_ERROR("Cannot request ioports\n");
149 return -EBUSY;
150 }
151 bochs->ioports = 1;
152 }
153
154 id = bochs_dispi_read(bochs, VBE_DISPI_INDEX_ID);
155 mem = bochs_dispi_read(bochs, VBE_DISPI_INDEX_VIDEO_MEMORY_64K)
156 * 64 * 1024;
157 if ((id & 0xfff0) != VBE_DISPI_ID0) {
158 DRM_ERROR("ID mismatch\n");
159 return -ENODEV;
160 }
161
162 if ((pdev->resource[0].flags & IORESOURCE_MEM) == 0)
163 return -ENODEV;
164 addr = pci_resource_start(pdev, 0);
165 size = pci_resource_len(pdev, 0);
166 if (addr == 0)
167 return -ENODEV;
168 if (size != mem) {
169 DRM_ERROR("Size mismatch: pci=%ld, bochs=%ld\n",
170 size, mem);
171 size = min(size, mem);
172 }
173
174 if (pci_request_region(pdev, 0, "bochs-drm") != 0)
175 DRM_WARN("Cannot request framebuffer, boot fb still active?\n");
176
177 bochs->fb_map = ioremap(addr, size);
178 if (bochs->fb_map == NULL) {
179 DRM_ERROR("Cannot map framebuffer\n");
180 return -ENOMEM;
181 }
182 bochs->fb_base = addr;
183 bochs->fb_size = size;
184
185 DRM_INFO("Found bochs VGA, ID 0x%x.\n", id);
186 DRM_INFO("Framebuffer size %ld kB @ 0x%lx, %s @ 0x%lx.\n",
187 size / 1024, addr,
188 bochs->ioports ? "ioports" : "mmio",
189 ioaddr);
190
191 if (bochs->mmio && pdev->revision >= 2) {
192 bochs->qext_size = readl(bochs->mmio + 0x600);
193 if (bochs->qext_size < 4 || bochs->qext_size > iosize) {
194 bochs->qext_size = 0;
195 goto noext;
196 }
197 DRM_DEBUG("Found qemu ext regs, size %ld\n",
198 bochs->qext_size);
199 bochs_hw_set_native_endian(bochs);
200 }
201
202noext:
203 return 0;
204}
205
206void bochs_hw_fini(struct drm_device *dev)
207{
208 struct bochs_device *bochs = dev->dev_private;
209
210 /* TODO: shot down existing vram mappings */
211
212 if (bochs->mmio)
213 iounmap(bochs->mmio);
214 if (bochs->ioports)
215 release_region(VBE_DISPI_IOPORT_INDEX, 2);
216 if (bochs->fb_map)
217 iounmap(bochs->fb_map);
218 pci_release_regions(to_pci_dev(dev->dev));
219 kfree(bochs->edid);
220}
221
222void bochs_hw_blank(struct bochs_device *bochs, bool blank)
223{
224 DRM_DEBUG_DRIVER("hw_blank %d\n", blank);
225 /* discard ar_flip_flop */
226 (void)bochs_vga_readb(bochs, VGA_IS1_RC);
227 /* blank or unblank; we need only update index and set 0x20 */
228 bochs_vga_writeb(bochs, VGA_ATT_W, blank ? 0 : 0x20);
229}
230
231void bochs_hw_setmode(struct bochs_device *bochs,
232 struct drm_display_mode *mode)
233{
234 int idx;
235
236 if (!drm_dev_enter(bochs->dev, &idx))
237 return;
238
239 bochs->xres = mode->hdisplay;
240 bochs->yres = mode->vdisplay;
241 bochs->bpp = 32;
242 bochs->stride = mode->hdisplay * (bochs->bpp / 8);
243 bochs->yres_virtual = bochs->fb_size / bochs->stride;
244
245 DRM_DEBUG_DRIVER("%dx%d @ %d bpp, vy %d\n",
246 bochs->xres, bochs->yres, bochs->bpp,
247 bochs->yres_virtual);
248
249 bochs_hw_blank(bochs, false);
250
251 bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE, 0);
252 bochs_dispi_write(bochs, VBE_DISPI_INDEX_BPP, bochs->bpp);
253 bochs_dispi_write(bochs, VBE_DISPI_INDEX_XRES, bochs->xres);
254 bochs_dispi_write(bochs, VBE_DISPI_INDEX_YRES, bochs->yres);
255 bochs_dispi_write(bochs, VBE_DISPI_INDEX_BANK, 0);
256 bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_WIDTH, bochs->xres);
257 bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_HEIGHT,
258 bochs->yres_virtual);
259 bochs_dispi_write(bochs, VBE_DISPI_INDEX_X_OFFSET, 0);
260 bochs_dispi_write(bochs, VBE_DISPI_INDEX_Y_OFFSET, 0);
261
262 bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE,
263 VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
264
265 drm_dev_exit(idx);
266}
267
268void bochs_hw_setformat(struct bochs_device *bochs,
269 const struct drm_format_info *format)
270{
271 int idx;
272
273 if (!drm_dev_enter(bochs->dev, &idx))
274 return;
275
276 DRM_DEBUG_DRIVER("format %c%c%c%c\n",
277 (format->format >> 0) & 0xff,
278 (format->format >> 8) & 0xff,
279 (format->format >> 16) & 0xff,
280 (format->format >> 24) & 0xff);
281
282 switch (format->format) {
283 case DRM_FORMAT_XRGB8888:
284 bochs_hw_set_little_endian(bochs);
285 break;
286 case DRM_FORMAT_BGRX8888:
287 bochs_hw_set_big_endian(bochs);
288 break;
289 default:
290 /* should not happen */
291 DRM_ERROR("%s: Huh? Got framebuffer format 0x%x",
292 __func__, format->format);
293 break;
294 }
295
296 drm_dev_exit(idx);
297}
298
299void bochs_hw_setbase(struct bochs_device *bochs,
300 int x, int y, int stride, u64 addr)
301{
302 unsigned long offset;
303 unsigned int vx, vy, vwidth, idx;
304
305 if (!drm_dev_enter(bochs->dev, &idx))
306 return;
307
308 bochs->stride = stride;
309 offset = (unsigned long)addr +
310 y * bochs->stride +
311 x * (bochs->bpp / 8);
312 vy = offset / bochs->stride;
313 vx = (offset % bochs->stride) * 8 / bochs->bpp;
314 vwidth = stride * 8 / bochs->bpp;
315
316 DRM_DEBUG_DRIVER("x %d, y %d, addr %llx -> offset %lx, vx %d, vy %d\n",
317 x, y, addr, offset, vx, vy);
318 bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_WIDTH, vwidth);
319 bochs_dispi_write(bochs, VBE_DISPI_INDEX_X_OFFSET, vx);
320 bochs_dispi_write(bochs, VBE_DISPI_INDEX_Y_OFFSET, vy);
321
322 drm_dev_exit(idx);
323}