]> git.ipfire.org Git - thirdparty/kernel/stable.git/blob - drivers/gpu/drm/bochs/bochs_hw.c
Merge branch 'drm-next-5.1' of git://people.freedesktop.org/~agd5f/linux into drm...
[thirdparty/kernel/stable.git] / drivers / gpu / drm / bochs / bochs_hw.c
1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 */
7
8 #include "bochs.h"
9
10 /* ---------------------------------------------------------------------- */
11
12 static void bochs_vga_writeb(struct bochs_device *bochs, u16 ioport, u8 val)
13 {
14 if (WARN_ON(ioport < 0x3c0 || ioport > 0x3df))
15 return;
16
17 if (bochs->mmio) {
18 int offset = ioport - 0x3c0 + 0x400;
19 writeb(val, bochs->mmio + offset);
20 } else {
21 outb(val, ioport);
22 }
23 }
24
25 static u16 bochs_dispi_read(struct bochs_device *bochs, u16 reg)
26 {
27 u16 ret = 0;
28
29 if (bochs->mmio) {
30 int offset = 0x500 + (reg << 1);
31 ret = readw(bochs->mmio + offset);
32 } else {
33 outw(reg, VBE_DISPI_IOPORT_INDEX);
34 ret = inw(VBE_DISPI_IOPORT_DATA);
35 }
36 return ret;
37 }
38
39 static void bochs_dispi_write(struct bochs_device *bochs, u16 reg, u16 val)
40 {
41 if (bochs->mmio) {
42 int offset = 0x500 + (reg << 1);
43 writew(val, bochs->mmio + offset);
44 } else {
45 outw(reg, VBE_DISPI_IOPORT_INDEX);
46 outw(val, VBE_DISPI_IOPORT_DATA);
47 }
48 }
49
50 static void bochs_hw_set_big_endian(struct bochs_device *bochs)
51 {
52 if (bochs->qext_size < 8)
53 return;
54
55 writel(0xbebebebe, bochs->mmio + 0x604);
56 }
57
58 static void bochs_hw_set_little_endian(struct bochs_device *bochs)
59 {
60 if (bochs->qext_size < 8)
61 return;
62
63 writel(0x1e1e1e1e, bochs->mmio + 0x604);
64 }
65
66 #ifdef __BIG_ENDIAN
67 #define bochs_hw_set_native_endian(_b) bochs_hw_set_big_endian(_b)
68 #else
69 #define bochs_hw_set_native_endian(_b) bochs_hw_set_little_endian(_b)
70 #endif
71
72 static int bochs_get_edid_block(void *data, u8 *buf,
73 unsigned int block, size_t len)
74 {
75 struct bochs_device *bochs = data;
76 size_t i, start = block * EDID_LENGTH;
77
78 if (start + len > 0x400 /* vga register offset */)
79 return -1;
80
81 for (i = 0; i < len; i++) {
82 buf[i] = readb(bochs->mmio + start + i);
83 }
84 return 0;
85 }
86
87 int bochs_hw_load_edid(struct bochs_device *bochs)
88 {
89 u8 header[8];
90
91 if (!bochs->mmio)
92 return -1;
93
94 /* check header to detect whenever edid support is enabled in qemu */
95 bochs_get_edid_block(bochs, header, 0, ARRAY_SIZE(header));
96 if (drm_edid_header_is_valid(header) != 8)
97 return -1;
98
99 kfree(bochs->edid);
100 bochs->edid = drm_do_get_edid(&bochs->connector,
101 bochs_get_edid_block, bochs);
102 if (bochs->edid == NULL)
103 return -1;
104
105 return 0;
106 }
107
108 int bochs_hw_init(struct drm_device *dev)
109 {
110 struct bochs_device *bochs = dev->dev_private;
111 struct pci_dev *pdev = dev->pdev;
112 unsigned long addr, size, mem, ioaddr, iosize;
113 u16 id;
114
115 if (pdev->resource[2].flags & IORESOURCE_MEM) {
116 /* mmio bar with vga and bochs registers present */
117 if (pci_request_region(pdev, 2, "bochs-drm") != 0) {
118 DRM_ERROR("Cannot request mmio region\n");
119 return -EBUSY;
120 }
121 ioaddr = pci_resource_start(pdev, 2);
122 iosize = pci_resource_len(pdev, 2);
123 bochs->mmio = ioremap(ioaddr, iosize);
124 if (bochs->mmio == NULL) {
125 DRM_ERROR("Cannot map mmio region\n");
126 return -ENOMEM;
127 }
128 } else {
129 ioaddr = VBE_DISPI_IOPORT_INDEX;
130 iosize = 2;
131 if (!request_region(ioaddr, iosize, "bochs-drm")) {
132 DRM_ERROR("Cannot request ioports\n");
133 return -EBUSY;
134 }
135 bochs->ioports = 1;
136 }
137
138 id = bochs_dispi_read(bochs, VBE_DISPI_INDEX_ID);
139 mem = bochs_dispi_read(bochs, VBE_DISPI_INDEX_VIDEO_MEMORY_64K)
140 * 64 * 1024;
141 if ((id & 0xfff0) != VBE_DISPI_ID0) {
142 DRM_ERROR("ID mismatch\n");
143 return -ENODEV;
144 }
145
146 if ((pdev->resource[0].flags & IORESOURCE_MEM) == 0)
147 return -ENODEV;
148 addr = pci_resource_start(pdev, 0);
149 size = pci_resource_len(pdev, 0);
150 if (addr == 0)
151 return -ENODEV;
152 if (size != mem) {
153 DRM_ERROR("Size mismatch: pci=%ld, bochs=%ld\n",
154 size, mem);
155 size = min(size, mem);
156 }
157
158 if (pci_request_region(pdev, 0, "bochs-drm") != 0) {
159 DRM_ERROR("Cannot request framebuffer\n");
160 return -EBUSY;
161 }
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
192 void bochs_hw_fini(struct drm_device *dev)
193 {
194 struct bochs_device *bochs = dev->dev_private;
195
196 if (bochs->mmio)
197 iounmap(bochs->mmio);
198 if (bochs->ioports)
199 release_region(VBE_DISPI_IOPORT_INDEX, 2);
200 if (bochs->fb_map)
201 iounmap(bochs->fb_map);
202 pci_release_regions(dev->pdev);
203 kfree(bochs->edid);
204 }
205
206 void bochs_hw_setmode(struct bochs_device *bochs,
207 struct drm_display_mode *mode)
208 {
209 bochs->xres = mode->hdisplay;
210 bochs->yres = mode->vdisplay;
211 bochs->bpp = 32;
212 bochs->stride = mode->hdisplay * (bochs->bpp / 8);
213 bochs->yres_virtual = bochs->fb_size / bochs->stride;
214
215 DRM_DEBUG_DRIVER("%dx%d @ %d bpp, vy %d\n",
216 bochs->xres, bochs->yres, bochs->bpp,
217 bochs->yres_virtual);
218
219 bochs_vga_writeb(bochs, 0x3c0, 0x20); /* unblank */
220
221 bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE, 0);
222 bochs_dispi_write(bochs, VBE_DISPI_INDEX_BPP, bochs->bpp);
223 bochs_dispi_write(bochs, VBE_DISPI_INDEX_XRES, bochs->xres);
224 bochs_dispi_write(bochs, VBE_DISPI_INDEX_YRES, bochs->yres);
225 bochs_dispi_write(bochs, VBE_DISPI_INDEX_BANK, 0);
226 bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_WIDTH, bochs->xres);
227 bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_HEIGHT,
228 bochs->yres_virtual);
229 bochs_dispi_write(bochs, VBE_DISPI_INDEX_X_OFFSET, 0);
230 bochs_dispi_write(bochs, VBE_DISPI_INDEX_Y_OFFSET, 0);
231
232 bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE,
233 VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
234 }
235
236 void bochs_hw_setformat(struct bochs_device *bochs,
237 const struct drm_format_info *format)
238 {
239 DRM_DEBUG_DRIVER("format %c%c%c%c\n",
240 (format->format >> 0) & 0xff,
241 (format->format >> 8) & 0xff,
242 (format->format >> 16) & 0xff,
243 (format->format >> 24) & 0xff);
244
245 switch (format->format) {
246 case DRM_FORMAT_XRGB8888:
247 bochs_hw_set_little_endian(bochs);
248 break;
249 case DRM_FORMAT_BGRX8888:
250 bochs_hw_set_big_endian(bochs);
251 break;
252 default:
253 /* should not happen */
254 DRM_ERROR("%s: Huh? Got framebuffer format 0x%x",
255 __func__, format->format);
256 break;
257 };
258 }
259
260 void bochs_hw_setbase(struct bochs_device *bochs,
261 int x, int y, u64 addr)
262 {
263 unsigned long offset = (unsigned long)addr +
264 y * bochs->stride +
265 x * (bochs->bpp / 8);
266 int vy = offset / bochs->stride;
267 int vx = (offset % bochs->stride) * 8 / bochs->bpp;
268
269 DRM_DEBUG_DRIVER("x %d, y %d, addr %llx -> offset %lx, vx %d, vy %d\n",
270 x, y, addr, offset, vx, vy);
271 bochs_dispi_write(bochs, VBE_DISPI_INDEX_X_OFFSET, vx);
272 bochs_dispi_write(bochs, VBE_DISPI_INDEX_Y_OFFSET, vy);
273 }