blob: 3b5c71148c9e50ac627613c02966fd3df8c2ab0d [file] [log] [blame]
Kevin O'Connore1e000b2011-12-31 03:30:40 -05001#include "vgabios.h" // struct vbe_modeinfo
Julian Pidancet87879e22011-12-19 05:08:00 +00002#include "vbe.h"
Kevin O'Connoracdcbf02011-12-31 03:09:55 -05003#include "bochsvga.h"
Julian Pidancet8bd766f2011-12-19 05:08:01 +00004#include "util.h"
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;
12 u16 width;
13 u16 height;
14 u8 depth;
Kevin O'Connorf1e217d2011-12-31 03:18:18 -050015} bochsvga_modes[] VAR16 = {
Julian Pidancet87879e22011-12-19 05:08:00 +000016 /* standard modes */
17 { 0x100, 640, 400, 8 },
18 { 0x101, 640, 480, 8 },
19 { 0x102, 800, 600, 4 },
20 { 0x103, 800, 600, 8 },
21 { 0x104, 1024, 768, 4 },
22 { 0x105, 1024, 768, 8 },
23 { 0x106, 1280, 1024, 4 },
24 { 0x107, 1280, 1024, 8 },
25 { 0x10D, 320, 200, 15 },
26 { 0x10E, 320, 200, 16 },
27 { 0x10F, 320, 200, 24 },
28 { 0x110, 640, 480, 15 },
29 { 0x111, 640, 480, 16 },
30 { 0x112, 640, 480, 24 },
31 { 0x113, 800, 600, 15 },
32 { 0x114, 800, 600, 16 },
33 { 0x115, 800, 600, 24 },
34 { 0x116, 1024, 768, 15 },
35 { 0x117, 1024, 768, 16 },
36 { 0x118, 1024, 768, 24 },
37 { 0x119, 1280, 1024, 15 },
38 { 0x11A, 1280, 1024, 16 },
39 { 0x11B, 1280, 1024, 24 },
40 { 0x11C, 1600, 1200, 8 },
41 { 0x11D, 1600, 1200, 15 },
42 { 0x11E, 1600, 1200, 16 },
43 { 0x11F, 1600, 1200, 24 },
44 /* BOCHS modes */
45 { 0x140, 320, 200, 32 },
46 { 0x141, 640, 400, 32 },
47 { 0x142, 640, 480, 32 },
48 { 0x143, 800, 600, 32 },
49 { 0x144, 1024, 768, 32 },
50 { 0x145, 1280, 1024, 32 },
51 { 0x146, 320, 200, 8 },
52 { 0x147, 1600, 1200, 32 },
53 { 0x148, 1152, 864, 8 },
54 { 0x149, 1152, 864, 15 },
55 { 0x14a, 1152, 864, 16 },
56 { 0x14b, 1152, 864, 24 },
57 { 0x14c, 1152, 864, 32 },
58 { 0x178, 1280, 800, 16 },
59 { 0x179, 1280, 800, 24 },
60 { 0x17a, 1280, 800, 32 },
61 { 0x17b, 1280, 960, 16 },
62 { 0x17c, 1280, 960, 24 },
63 { 0x17d, 1280, 960, 32 },
64 { 0x17e, 1440, 900, 16 },
65 { 0x17f, 1440, 900, 24 },
66 { 0x180, 1440, 900, 32 },
67 { 0x181, 1400, 1050, 16 },
68 { 0x182, 1400, 1050, 24 },
69 { 0x183, 1400, 1050, 32 },
70 { 0x184, 1680, 1050, 16 },
71 { 0x185, 1680, 1050, 24 },
72 { 0x186, 1680, 1050, 32 },
73 { 0x187, 1920, 1200, 16 },
74 { 0x188, 1920, 1200, 24 },
75 { 0x189, 1920, 1200, 32 },
76 { 0x18a, 2560, 1600, 16 },
77 { 0x18b, 2560, 1600, 24 },
78 { 0x18c, 2560, 1600, 32 },
79 { 0, },
80};
81
Julian Pidancet8bd766f2011-12-19 05:08:01 +000082#define BYTES_PER_PIXEL(m) ((GET_GLOBAL((m)->depth) + 7) / 8)
83
84u32 pci_lfb_addr VAR16;
85
86static inline u32 pci_config_readl(u8 bus, u8 devfn, u16 addr)
87{
88 int status;
89 u32 val;
90 u16 bdf = (bus << 16) | devfn;
91
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'Connorf1e217d2011-12-31 03:18:18 -0500139bochsvga_init(u8 bus, u8 devfn)
Julian Pidancet87879e22011-12-19 05:08:00 +0000140{
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000141 u32 lfb_addr;
142
143 if (!CONFIG_VGA_BOCHS)
144 return -1;
145
146 /* Sanity checks */
147 dispi_write(VBE_DISPI_INDEX_ID, VBE_DISPI_ID0);
148 if (dispi_read(VBE_DISPI_INDEX_ID) != VBE_DISPI_ID0) {
149 dprintf(1, "No VBE DISPI interface detected\n");
150 return -1;
151 }
152
153 SET_BDA(vbe_flag, 0x1);
154 dispi_write(VBE_DISPI_INDEX_ID, VBE_DISPI_ID5);
155
156 if (CONFIG_VGA_PCI)
157 lfb_addr = pci_config_readl(bus, devfn, 0x10) & ~0xf;
158 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
262void
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500263bochsvga_set_mode(u16 mode, struct vbe_modeinfo *info)
Julian Pidancet87879e22011-12-19 05:08:00 +0000264{
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000265 if (info->depth == 4)
266 vga_set_mode(0x6a, 0);
267 if (info->depth == 8)
268 // XXX load_dac_palette(3);
269 ;
Julian Pidancet87879e22011-12-19 05:08:00 +0000270
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000271 dispi_write(VBE_DISPI_INDEX_BPP, info->depth);
272 dispi_write(VBE_DISPI_INDEX_XRES, info->width);
273 dispi_write(VBE_DISPI_INDEX_YRES, info->height);
274 dispi_write(VBE_DISPI_INDEX_BANK, 0);
275
276 /* VGA compat setup */
277 //XXX: This probably needs some reverse engineering
278 u8 v;
279 outw(0x0011, VGAREG_VGA_CRTC_ADDRESS);
280 outw(((info->width * 4 - 1) << 8) | 0x1, VGAREG_VGA_CRTC_ADDRESS);
281 dispi_write(VBE_DISPI_INDEX_VIRT_WIDTH, info->width);
282 outw(((info->height - 1) << 8) | 0x12, VGAREG_VGA_CRTC_ADDRESS);
283 outw(((info->height - 1) & 0xff00) | 0x7, VGAREG_VGA_CRTC_ADDRESS);
284 v = inb(VGAREG_VGA_CRTC_DATA) & 0xbd;
285 if (v & 0x1)
286 v |= 0x2;
287 if (v & 0x2)
288 v |= 0x40;
289 outb(v, VGAREG_VGA_CRTC_DATA);
290
291 outw(0x9, VGAREG_VGA_CRTC_ADDRESS);
292 outb(0x17, VGAREG_VGA_CRTC_ADDRESS);
293 outb(inb(VGAREG_VGA_CRTC_DATA) | 0x3, VGAREG_VGA_CRTC_DATA);
294 v = inb(VGAREG_ACTL_RESET);
295 outw(0x10, VGAREG_ACTL_ADDRESS);
296 v = inb(VGAREG_ACTL_READ_DATA) | 0x1;
297 outb(v, VGAREG_ACTL_ADDRESS);
298 outb(0x20, VGAREG_ACTL_ADDRESS);
299 outw(0x0506, VGAREG_GRDC_ADDRESS);
300 outw(0x0f02, VGAREG_SEQU_ADDRESS);
301 if (info->depth >= 8) {
302 outb(0x14, VGAREG_VGA_CRTC_ADDRESS);
303 outb(inb(VGAREG_VGA_CRTC_DATA) | 0x40, VGAREG_VGA_CRTC_DATA);
304 v = inb(VGAREG_ACTL_RESET);
305 outw(0x10, VGAREG_ACTL_ADDRESS);
306 v = inb(VGAREG_ACTL_READ_DATA) | 0x40;
307 outb(v, VGAREG_ACTL_ADDRESS);
308 outb(0x20, VGAREG_ACTL_ADDRESS);
309 outb(0x04, VGAREG_SEQU_ADDRESS);
310 v = inb(VGAREG_SEQU_DATA) | 0x08;
311 outb(v, VGAREG_SEQU_DATA);
312 outb(0x05, VGAREG_GRDC_ADDRESS);
313 v = inb(VGAREG_GRDC_DATA) & 0x9f;
314 outb(v | 0x40, VGAREG_GRDC_DATA);
315 }
316
317 SET_BDA(vbe_mode, mode);
Julian Pidancet87879e22011-12-19 05:08:00 +0000318}
319
320void
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500321bochsvga_clear_scr(void)
Julian Pidancet87879e22011-12-19 05:08:00 +0000322{
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000323 u16 en;
Julian Pidancet87879e22011-12-19 05:08:00 +0000324
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000325 en = dispi_read(VBE_DISPI_INDEX_ENABLE);
326 en &= ~VBE_DISPI_NOCLEARMEM;
327 dispi_write(VBE_DISPI_INDEX_ENABLE, en);
Julian Pidancet87879e22011-12-19 05:08:00 +0000328}
329
330int
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500331bochsvga_hires_enabled(void)
Julian Pidancet87879e22011-12-19 05:08:00 +0000332{
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000333 return dispi_read(VBE_DISPI_INDEX_ENABLE) & VBE_DISPI_ENABLED;
Julian Pidancet87879e22011-12-19 05:08:00 +0000334}
335
336u16
Kevin O'Connorf1e217d2011-12-31 03:18:18 -0500337bochsvga_curr_mode(void)
Julian Pidancet87879e22011-12-19 05:08:00 +0000338{
Julian Pidancet8bd766f2011-12-19 05:08:01 +0000339 return GET_BDA(vbe_mode);
Julian Pidancet87879e22011-12-19 05:08:00 +0000340}