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