blob: 6652c2e9d3641e51c2e24181ce61c4ceae44a107 [file] [log] [blame]
Kevin O'Connore1e000b2011-12-31 03:30:40 -05001#include "vgabios.h" // struct vbe_modeinfo
Kevin O'Connor5108c692011-12-31 19:13:45 -05002#include "vbe.h" // VBE_MODE_VESA_DEFINED
3#include "bochsvga.h" // bochsvga_set_mode
4#include "util.h" // dprintf
Kevin O'Connore1e000b2011-12-31 03:30:40 -05005#include "config.h" // CONFIG_*
Julian Pidancet8bd766f2011-12-19 05:08:01 +00006#include "biosvar.h" // SET_BDA
Kevin O'Connored68e5b2011-12-31 04:15:12 -05007#include "stdvga.h" // VGAREG_SEQU_ADDRESS
Julian Pidancet87879e22011-12-19 05:08:00 +00008
Kevin O'Connorf1e217d2011-12-31 03:18:18 -05009static struct mode
Julian Pidancet87879e22011-12-19 05:08:00 +000010{
11 u16 mode;
Kevin O'Connorbb17d842012-01-02 11:32:11 -050012 u8 memmodel;
Julian Pidancet87879e22011-12-19 05:08:00 +000013 u16 width;
14 u16 height;
15 u8 depth;
Kevin O'Connorf1e217d2011-12-31 03:18:18 -050016} bochsvga_modes[] VAR16 = {
Julian Pidancet87879e22011-12-19 05:08:00 +000017 /* standard modes */
Kevin O'Connorbb17d842012-01-02 11:32:11 -050018 { 0x100, MM_PACKED, 640, 400, 8 },
19 { 0x101, MM_PACKED, 640, 480, 8 },
20 { 0x102, MM_PLANAR, 800, 600, 4 },
21 { 0x103, MM_PACKED, 800, 600, 8 },
22 { 0x104, MM_PLANAR, 1024, 768, 4 },
23 { 0x105, MM_PACKED, 1024, 768, 8 },
24 { 0x106, MM_PLANAR, 1280, 1024, 4 },
25 { 0x107, MM_PACKED, 1280, 1024, 8 },
26 { 0x10D, MM_DIRECT, 320, 200, 15 },
27 { 0x10E, MM_DIRECT, 320, 200, 16 },
28 { 0x10F, MM_DIRECT, 320, 200, 24 },
29 { 0x110, MM_DIRECT, 640, 480, 15 },
30 { 0x111, MM_DIRECT, 640, 480, 16 },
31 { 0x112, MM_DIRECT, 640, 480, 24 },
32 { 0x113, MM_DIRECT, 800, 600, 15 },
33 { 0x114, MM_DIRECT, 800, 600, 16 },
34 { 0x115, MM_DIRECT, 800, 600, 24 },
35 { 0x116, MM_DIRECT, 1024, 768, 15 },
36 { 0x117, MM_DIRECT, 1024, 768, 16 },
37 { 0x118, MM_DIRECT, 1024, 768, 24 },
38 { 0x119, MM_DIRECT, 1280, 1024, 15 },
39 { 0x11A, MM_DIRECT, 1280, 1024, 16 },
40 { 0x11B, MM_DIRECT, 1280, 1024, 24 },
41 { 0x11C, MM_PACKED, 1600, 1200, 8 },
42 { 0x11D, MM_DIRECT, 1600, 1200, 15 },
43 { 0x11E, MM_DIRECT, 1600, 1200, 16 },
44 { 0x11F, MM_DIRECT, 1600, 1200, 24 },
Julian Pidancet87879e22011-12-19 05:08:00 +000045 /* BOCHS modes */
Kevin O'Connorbb17d842012-01-02 11:32:11 -050046 { 0x140, MM_DIRECT, 320, 200, 32 },
47 { 0x141, MM_DIRECT, 640, 400, 32 },
48 { 0x142, MM_DIRECT, 640, 480, 32 },
49 { 0x143, MM_DIRECT, 800, 600, 32 },
50 { 0x144, MM_DIRECT, 1024, 768, 32 },
51 { 0x145, MM_DIRECT, 1280, 1024, 32 },
52 { 0x146, MM_PACKED, 320, 200, 8 },
53 { 0x147, MM_DIRECT, 1600, 1200, 32 },
54 { 0x148, MM_PACKED, 1152, 864, 8 },
55 { 0x149, MM_DIRECT, 1152, 864, 15 },
56 { 0x14a, MM_DIRECT, 1152, 864, 16 },
57 { 0x14b, MM_DIRECT, 1152, 864, 24 },
58 { 0x14c, MM_DIRECT, 1152, 864, 32 },
59 { 0x178, MM_DIRECT, 1280, 800, 16 },
60 { 0x179, MM_DIRECT, 1280, 800, 24 },
61 { 0x17a, MM_DIRECT, 1280, 800, 32 },
62 { 0x17b, MM_DIRECT, 1280, 960, 16 },
63 { 0x17c, MM_DIRECT, 1280, 960, 24 },
64 { 0x17d, MM_DIRECT, 1280, 960, 32 },
65 { 0x17e, MM_DIRECT, 1440, 900, 16 },
66 { 0x17f, MM_DIRECT, 1440, 900, 24 },
67 { 0x180, MM_DIRECT, 1440, 900, 32 },
68 { 0x181, MM_DIRECT, 1400, 1050, 16 },
69 { 0x182, MM_DIRECT, 1400, 1050, 24 },
70 { 0x183, MM_DIRECT, 1400, 1050, 32 },
71 { 0x184, MM_DIRECT, 1680, 1050, 16 },
72 { 0x185, MM_DIRECT, 1680, 1050, 24 },
73 { 0x186, MM_DIRECT, 1680, 1050, 32 },
74 { 0x187, MM_DIRECT, 1920, 1200, 16 },
75 { 0x188, MM_DIRECT, 1920, 1200, 24 },
76 { 0x189, MM_DIRECT, 1920, 1200, 32 },
77 { 0x18a, MM_DIRECT, 2560, 1600, 16 },
78 { 0x18b, MM_DIRECT, 2560, 1600, 24 },
79 { 0x18c, MM_DIRECT, 2560, 1600, 32 },
Julian Pidancet87879e22011-12-19 05:08:00 +000080 { 0, },
81};
82
Julian Pidancet8bd766f2011-12-19 05:08:01 +000083#define BYTES_PER_PIXEL(m) ((GET_GLOBAL((m)->depth) + 7) / 8)
84
85u32 pci_lfb_addr VAR16;
86
Kevin O'Connor161d2012011-12-31 19:42:21 -050087static inline u32 pci_config_readl(u16 bdf, u16 addr)
Julian Pidancet8bd766f2011-12-19 05:08:01 +000088{
89 int status;
90 u32 val;
Julian Pidancet8bd766f2011-12-19 05:08:01 +000091
92 addr &= ~3;
93
94 asm volatile(
95 "int $0x1a\n"
96 "cli\n"
97 "cld"
98 : "=a"(status), "=c"(val)
99 : "a"(0xb10a), "b"(bdf), "D"(addr)
100 : "cc", "memory");
101
102 if ((status >> 16))
103 return (u32)-1;
104
105 return val;
106}
107
108
109static u16 dispi_get_max_xres(void)
110{
111 u16 en;
112 u16 xres;
113
114 en = dispi_read(VBE_DISPI_INDEX_ENABLE);
115
116 dispi_write(VBE_DISPI_INDEX_ENABLE, en | VBE_DISPI_GETCAPS);
117 xres = dispi_read(VBE_DISPI_INDEX_XRES);
118 dispi_write(VBE_DISPI_INDEX_ENABLE, en);
119
120 return xres;
121}
122
123static u16 dispi_get_max_bpp(void)
124{
125 u16 en;
126 u16 bpp;
127
128 en = dispi_read(VBE_DISPI_INDEX_ENABLE);
129
130 dispi_write(VBE_DISPI_INDEX_ENABLE, en | VBE_DISPI_GETCAPS);
131 bpp = dispi_read(VBE_DISPI_INDEX_BPP);
132 dispi_write(VBE_DISPI_INDEX_ENABLE, en);
133
134 return bpp;
135}
136
Julian Pidancet87879e22011-12-19 05:08:00 +0000137/* Called only during POST */
138int
Kevin O'Connor161d2012011-12-31 19:42:21 -0500139bochsvga_init(void)
Julian Pidancet87879e22011-12-19 05:08:00 +0000140{
Kevin O'Connor161d2012011-12-31 19:42:21 -0500141 int ret = stdvga_init();
142 if (ret)
143 return ret;
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000144
145 /* Sanity checks */
146 dispi_write(VBE_DISPI_INDEX_ID, VBE_DISPI_ID0);
147 if (dispi_read(VBE_DISPI_INDEX_ID) != VBE_DISPI_ID0) {
148 dprintf(1, "No VBE DISPI interface detected\n");
149 return -1;
150 }
151
152 SET_BDA(vbe_flag, 0x1);
153 dispi_write(VBE_DISPI_INDEX_ID, VBE_DISPI_ID5);
154
Kevin O'Connor161d2012011-12-31 19:42:21 -0500155 u32 lfb_addr;
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000156 if (CONFIG_VGA_PCI)
Kevin O'Connor161d2012011-12-31 19:42:21 -0500157 lfb_addr = pci_config_readl(GET_GLOBAL(VgaBDF), 0x10) & ~0xf;
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000158 else
159 lfb_addr = VBE_DISPI_LFB_PHYSICAL_ADDRESS;
160
161 SET_FARVAR(get_global_seg(), pci_lfb_addr, lfb_addr);
162
163 dprintf(1, "VBE DISPI detected. lfb_addr=%x\n", GET_GLOBAL(pci_lfb_addr));
164
165 return 0;
Julian Pidancet87879e22011-12-19 05:08:00 +0000166}
167
168int
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500169bochsvga_enabled(void)
Julian Pidancet87879e22011-12-19 05:08:00 +0000170{
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000171 return GET_BDA(vbe_flag);
Julian Pidancet87879e22011-12-19 05:08:00 +0000172}
173
174u16
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500175bochsvga_total_mem(void)
Julian Pidancet87879e22011-12-19 05:08:00 +0000176{
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000177 return dispi_read(VBE_DISPI_INDEX_VIDEO_MEMORY_64K);
178}
179
180static struct mode *find_mode_entry(u16 mode)
181{
182 struct mode *m;
183
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500184 for (m = bochsvga_modes; GET_GLOBAL(m->mode); m++) {
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000185 if (GET_GLOBAL(m->mode) == mode)
186 return m;
187 }
188
189 return NULL;
190}
191
192static int mode_valid(struct mode *m)
193{
194 u16 max_xres = dispi_get_max_xres();
195 u16 max_bpp = dispi_get_max_bpp();
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500196 u32 max_mem = bochsvga_total_mem() * 64 * 1024;
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000197
198 u32 mem = GET_GLOBAL(m->width) * GET_GLOBAL(m->height) *
199 BYTES_PER_PIXEL(m);
200
201 if (GET_GLOBAL(m->width) > max_xres ||
202 GET_GLOBAL(m->depth) > max_bpp ||
203 mem > max_mem)
204 return 0;
205
206 return 1;
Julian Pidancet87879e22011-12-19 05:08:00 +0000207}
208
209int
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500210bochsvga_list_modes(u16 seg, u16 ptr)
Julian Pidancet87879e22011-12-19 05:08:00 +0000211{
212 int count = 0;
213 u16 *dest = (u16 *)(u32)ptr;
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000214 struct mode *m;
215
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500216 for (m = bochsvga_modes; GET_GLOBAL(m->mode); m++) {
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000217 if (!mode_valid(m))
218 continue;
219
220 dprintf(1, "VBE found mode %x valid.\n", GET_GLOBAL(m->mode));
221 SET_FARVAR(seg, dest[count], GET_GLOBAL(m->mode));
222
223 count++;
224 }
Julian Pidancet87879e22011-12-19 05:08:00 +0000225
226 SET_FARVAR(seg, dest[count], 0xffff); /* End of list */
227
228 return count;
229}
230
231int
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500232bochsvga_mode_info(u16 mode, struct vbe_modeinfo *info)
Julian Pidancet87879e22011-12-19 05:08:00 +0000233{
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000234 struct mode *m;
235
236 m = find_mode_entry(mode);
237 if (!m || !mode_valid(m))
238 return -1;
239
240 info->width = GET_GLOBAL(m->width);
241 info->height = GET_GLOBAL(m->height);
242 info->depth = GET_GLOBAL(m->depth);
243
244 info->linesize = info->width * ((info->depth + 7) / 8);
245 info->phys_base = GET_GLOBAL(pci_lfb_addr);
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500246 info->vram_size = bochsvga_total_mem() * 64 * 1024;
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000247
248 return 0;
Julian Pidancet87879e22011-12-19 05:08:00 +0000249}
250
251void
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500252bochsvga_hires_enable(int enable)
Julian Pidancet87879e22011-12-19 05:08:00 +0000253{
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000254 u16 flags = enable ?
255 VBE_DISPI_ENABLED |
256 VBE_DISPI_LFB_ENABLED |
257 VBE_DISPI_NOCLEARMEM : 0;
Julian Pidancet87879e22011-12-19 05:08:00 +0000258
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000259 dispi_write(VBE_DISPI_INDEX_ENABLE, flags);
Julian Pidancet87879e22011-12-19 05:08:00 +0000260}
261
Kevin O'Connor5108c692011-12-31 19:13:45 -0500262int
263bochsvga_set_mode(int mode, int flags)
Julian Pidancet87879e22011-12-19 05:08:00 +0000264{
Kevin O'Connor5108c692011-12-31 19:13:45 -0500265 if (!(mode & VBE_MODE_VESA_DEFINED)) {
266 dprintf(1, "set VGA mode %x\n", mode);
267
268 bochsvga_hires_enable(0);
269 return stdvga_set_mode(mode, flags);
270 }
271
272 struct vbe_modeinfo modeinfo, *info = &modeinfo;
273 int ret = bochsvga_mode_info(mode, &modeinfo);
274 if (ret) {
275 dprintf(1, "VBE mode %x not found\n", mode);
276 return VBE_RETURN_STATUS_FAILED;
277 }
278 bochsvga_hires_enable(1);
279
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000280 if (info->depth == 4)
Kevin O'Connor821d6b42011-12-31 18:19:22 -0500281 stdvga_set_mode(0x6a, 0);
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000282 if (info->depth == 8)
283 // XXX load_dac_palette(3);
284 ;
Julian Pidancet87879e22011-12-19 05:08:00 +0000285
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000286 dispi_write(VBE_DISPI_INDEX_BPP, info->depth);
287 dispi_write(VBE_DISPI_INDEX_XRES, info->width);
288 dispi_write(VBE_DISPI_INDEX_YRES, info->height);
289 dispi_write(VBE_DISPI_INDEX_BANK, 0);
290
291 /* VGA compat setup */
292 //XXX: This probably needs some reverse engineering
293 u8 v;
294 outw(0x0011, VGAREG_VGA_CRTC_ADDRESS);
295 outw(((info->width * 4 - 1) << 8) | 0x1, VGAREG_VGA_CRTC_ADDRESS);
296 dispi_write(VBE_DISPI_INDEX_VIRT_WIDTH, info->width);
297 outw(((info->height - 1) << 8) | 0x12, VGAREG_VGA_CRTC_ADDRESS);
298 outw(((info->height - 1) & 0xff00) | 0x7, VGAREG_VGA_CRTC_ADDRESS);
299 v = inb(VGAREG_VGA_CRTC_DATA) & 0xbd;
300 if (v & 0x1)
301 v |= 0x2;
302 if (v & 0x2)
303 v |= 0x40;
304 outb(v, VGAREG_VGA_CRTC_DATA);
305
306 outw(0x9, VGAREG_VGA_CRTC_ADDRESS);
307 outb(0x17, VGAREG_VGA_CRTC_ADDRESS);
308 outb(inb(VGAREG_VGA_CRTC_DATA) | 0x3, VGAREG_VGA_CRTC_DATA);
309 v = inb(VGAREG_ACTL_RESET);
310 outw(0x10, VGAREG_ACTL_ADDRESS);
311 v = inb(VGAREG_ACTL_READ_DATA) | 0x1;
312 outb(v, VGAREG_ACTL_ADDRESS);
313 outb(0x20, VGAREG_ACTL_ADDRESS);
314 outw(0x0506, VGAREG_GRDC_ADDRESS);
315 outw(0x0f02, VGAREG_SEQU_ADDRESS);
316 if (info->depth >= 8) {
317 outb(0x14, VGAREG_VGA_CRTC_ADDRESS);
318 outb(inb(VGAREG_VGA_CRTC_DATA) | 0x40, VGAREG_VGA_CRTC_DATA);
319 v = inb(VGAREG_ACTL_RESET);
320 outw(0x10, VGAREG_ACTL_ADDRESS);
321 v = inb(VGAREG_ACTL_READ_DATA) | 0x40;
322 outb(v, VGAREG_ACTL_ADDRESS);
323 outb(0x20, VGAREG_ACTL_ADDRESS);
324 outb(0x04, VGAREG_SEQU_ADDRESS);
325 v = inb(VGAREG_SEQU_DATA) | 0x08;
326 outb(v, VGAREG_SEQU_DATA);
327 outb(0x05, VGAREG_GRDC_ADDRESS);
328 v = inb(VGAREG_GRDC_DATA) & 0x9f;
329 outb(v | 0x40, VGAREG_GRDC_DATA);
330 }
331
332 SET_BDA(vbe_mode, mode);
Kevin O'Connor5108c692011-12-31 19:13:45 -0500333
334 if (flags & MF_LINEARFB) {
335 /* Linear frame buffer */
336 /* XXX: ??? */
337 }
338 if (!(mode & MF_NOCLEARMEM)) {
339 bochsvga_clear_scr();
340 }
341
342 return 0;
Julian Pidancet87879e22011-12-19 05:08:00 +0000343}
344
345void
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500346bochsvga_clear_scr(void)
Julian Pidancet87879e22011-12-19 05:08:00 +0000347{
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000348 u16 en;
Julian Pidancet87879e22011-12-19 05:08:00 +0000349
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000350 en = dispi_read(VBE_DISPI_INDEX_ENABLE);
351 en &= ~VBE_DISPI_NOCLEARMEM;
352 dispi_write(VBE_DISPI_INDEX_ENABLE, en);
Julian Pidancet87879e22011-12-19 05:08:00 +0000353}
354
355int
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500356bochsvga_hires_enabled(void)
Julian Pidancet87879e22011-12-19 05:08:00 +0000357{
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000358 return dispi_read(VBE_DISPI_INDEX_ENABLE) & VBE_DISPI_ENABLED;
Julian Pidancet87879e22011-12-19 05:08:00 +0000359}
360
361u16
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500362bochsvga_curr_mode(void)
Julian Pidancet87879e22011-12-19 05:08:00 +0000363{
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000364 return GET_BDA(vbe_mode);
Julian Pidancet87879e22011-12-19 05:08:00 +0000365}