xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/bochs/bochs_hw.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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