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