blob: 7987d28a0389432970b7f0c0296b42b35abf6915 [file] [log] [blame]
Kevin O'Connor1f2c3072009-05-06 23:35:59 -04001// QEMU Cirrus CLGD 54xx VGABIOS Extension.
2//
3// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net>
4// Copyright (c) 2004 Makoto Suzuki (suzu)
5//
6// This file may be distributed under the terms of the GNU LGPLv3 license.
7
Kevin O'Connore1e000b2011-12-31 03:30:40 -05008#include "vgabios.h" // cirrus_init
Kevin O'Connor1f2c3072009-05-06 23:35:59 -04009#include "biosvar.h" // GET_GLOBAL
10#include "util.h" // dprintf
Kevin O'Connor3c065362011-12-27 21:34:33 -050011#include "bregs.h" // struct bregs
Kevin O'Connor8f4c0192011-12-31 03:00:59 -050012#include "vbe.h" // struct vbe_info
Kevin O'Connored68e5b2011-12-31 04:15:12 -050013#include "stdvga.h" // VGAREG_SEQU_ADDRESS
Kevin O'Connor1f2c3072009-05-06 23:35:59 -040014
Kevin O'Connore48a5372011-12-20 23:56:14 -050015
16/****************************************************************
17 * tables
18 ****************************************************************/
19
Kevin O'Connor1f2c3072009-05-06 23:35:59 -040020struct cirrus_mode_s {
21 /* + 0 */
22 u16 mode;
23 u16 width;
24 u16 height;
25 u16 depth;
26 /* + 8 */
27 u16 hidden_dac; /* 0x3c6 */
28 u16 *seq; /* 0x3c4 */
29 u16 *graph; /* 0x3ce */
30 u16 *crtc; /* 0x3d4 */
31 /* +16 */
32 u8 bitsperpixel;
33 u8 vesacolortype;
34 u8 vesaredmask;
35 u8 vesaredpos;
36 u8 vesagreenmask;
37 u8 vesagreenpos;
38 u8 vesabluemask;
39 u8 vesabluepos;
40 /* +24 */
41 u8 vesareservedmask;
42 u8 vesareservedpos;
43};
44
45/* VGA */
46static u16 cseq_vga[] VAR16 = {0x0007,0xffff};
47static u16 cgraph_vga[] VAR16 = {0x0009,0x000a,0x000b,0xffff};
48static u16 ccrtc_vga[] VAR16 = {0x001a,0x001b,0x001d,0xffff};
49
50/* extensions */
51static u16 cgraph_svgacolor[] VAR16 = {
52 0x0000,0x0001,0x0002,0x0003,0x0004,0x4005,0x0506,0x0f07,0xff08,
53 0x0009,0x000a,0x000b,
54 0xffff
55};
56/* 640x480x8 */
57static u16 cseq_640x480x8[] VAR16 = {
58 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107,
59 0x580b,0x580c,0x580d,0x580e,
60 0x0412,0x0013,0x2017,
61 0x331b,0x331c,0x331d,0x331e,
62 0xffff
63};
64static u16 ccrtc_640x480x8[] VAR16 = {
65 0x2c11,
66 0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07,
67 0x4009,0x000c,0x000d,
68 0xea10,0xdf12,0x5013,0x4014,0xdf15,0x0b16,0xc317,0xff18,
69 0x001a,0x221b,0x001d,
70 0xffff
71};
72/* 640x480x16 */
73static u16 cseq_640x480x16[] VAR16 = {
74 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707,
75 0x580b,0x580c,0x580d,0x580e,
76 0x0412,0x0013,0x2017,
77 0x331b,0x331c,0x331d,0x331e,
78 0xffff
79};
80static u16 ccrtc_640x480x16[] VAR16 = {
81 0x2c11,
82 0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07,
83 0x4009,0x000c,0x000d,
84 0xea10,0xdf12,0xa013,0x4014,0xdf15,0x0b16,0xc317,0xff18,
85 0x001a,0x221b,0x001d,
86 0xffff
87};
88/* 640x480x24 */
89static u16 cseq_640x480x24[] VAR16 = {
90 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507,
91 0x580b,0x580c,0x580d,0x580e,
92 0x0412,0x0013,0x2017,
93 0x331b,0x331c,0x331d,0x331e,
94 0xffff
95};
96static u16 ccrtc_640x480x24[] VAR16 = {
97 0x2c11,
98 0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07,
99 0x4009,0x000c,0x000d,
100 0xea10,0xdf12,0x0013,0x4014,0xdf15,0x0b16,0xc317,0xff18,
101 0x001a,0x321b,0x001d,
102 0xffff
103};
104/* 800x600x8 */
105static u16 cseq_800x600x8[] VAR16 = {
106 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107,
107 0x230b,0x230c,0x230d,0x230e,
108 0x0412,0x0013,0x2017,
109 0x141b,0x141c,0x141d,0x141e,
110 0xffff
111};
112static u16 ccrtc_800x600x8[] VAR16 = {
113 0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007,
114 0x6009,0x000c,0x000d,
115 0x7d10,0x5712,0x6413,0x4014,0x5715,0x9816,0xc317,0xff18,
116 0x001a,0x221b,0x001d,
117 0xffff
118};
119/* 800x600x16 */
120static u16 cseq_800x600x16[] VAR16 = {
121 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707,
122 0x230b,0x230c,0x230d,0x230e,
123 0x0412,0x0013,0x2017,
124 0x141b,0x141c,0x141d,0x141e,
125 0xffff
126};
127static u16 ccrtc_800x600x16[] VAR16 = {
128 0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007,
129 0x6009,0x000c,0x000d,
130 0x7d10,0x5712,0xc813,0x4014,0x5715,0x9816,0xc317,0xff18,
131 0x001a,0x221b,0x001d,
132 0xffff
133};
134/* 800x600x24 */
135static u16 cseq_800x600x24[] VAR16 = {
136 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507,
137 0x230b,0x230c,0x230d,0x230e,
138 0x0412,0x0013,0x2017,
139 0x141b,0x141c,0x141d,0x141e,
140 0xffff
141};
142static u16 ccrtc_800x600x24[] VAR16 = {
143 0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007,
144 0x6009,0x000c,0x000d,
145 0x7d10,0x5712,0x2c13,0x4014,0x5715,0x9816,0xc317,0xff18,
146 0x001a,0x321b,0x001d,
147 0xffff
148};
149/* 1024x768x8 */
150static u16 cseq_1024x768x8[] VAR16 = {
151 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107,
152 0x760b,0x760c,0x760d,0x760e,
153 0x0412,0x0013,0x2017,
154 0x341b,0x341c,0x341d,0x341e,
155 0xffff
156};
157static u16 ccrtc_1024x768x8[] VAR16 = {
158 0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507,
159 0x6009,0x000c,0x000d,
160 0x0310,0xff12,0x8013,0x4014,0xff15,0x2416,0xc317,0xff18,
161 0x001a,0x221b,0x001d,
162 0xffff
163};
164/* 1024x768x16 */
165static u16 cseq_1024x768x16[] VAR16 = {
166 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707,
167 0x760b,0x760c,0x760d,0x760e,
168 0x0412,0x0013,0x2017,
169 0x341b,0x341c,0x341d,0x341e,
170 0xffff
171};
172static u16 ccrtc_1024x768x16[] VAR16 = {
173 0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507,
174 0x6009,0x000c,0x000d,
175 0x0310,0xff12,0x0013,0x4014,0xff15,0x2416,0xc317,0xff18,
176 0x001a,0x321b,0x001d,
177 0xffff
178};
179/* 1024x768x24 */
180static u16 cseq_1024x768x24[] VAR16 = {
181 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507,
182 0x760b,0x760c,0x760d,0x760e,
183 0x0412,0x0013,0x2017,
184 0x341b,0x341c,0x341d,0x341e,
185 0xffff
186};
187static u16 ccrtc_1024x768x24[] VAR16 = {
188 0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507,
189 0x6009,0x000c,0x000d,
190 0x0310,0xff12,0x8013,0x4014,0xff15,0x2416,0xc317,0xff18,
191 0x001a,0x321b,0x001d,
192 0xffff
193};
194/* 1280x1024x8 */
195static u16 cseq_1280x1024x8[] VAR16 = {
196 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107,
197 0x760b,0x760c,0x760d,0x760e,
198 0x0412,0x0013,0x2017,
199 0x341b,0x341c,0x341d,0x341e,
200 0xffff
201};
202static u16 ccrtc_1280x1024x8[] VAR16 = {
203 0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707,
204 0x6009,0x000c,0x000d,
205 0x0310,0xff12,0xa013,0x4014,0xff15,0x2416,0xc317,0xff18,
206 0x001a,0x221b,0x001d,
207 0xffff
208};
209/* 1280x1024x16 */
210static u16 cseq_1280x1024x16[] VAR16 = {
211 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707,
212 0x760b,0x760c,0x760d,0x760e,
213 0x0412,0x0013,0x2017,
214 0x341b,0x341c,0x341d,0x341e,
215 0xffff
216};
217static u16 ccrtc_1280x1024x16[] VAR16 = {
218 0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707,
219 0x6009,0x000c,0x000d,
220 0x0310,0xff12,0x4013,0x4014,0xff15,0x2416,0xc317,0xff18,
221 0x001a,0x321b,0x001d,
222 0xffff
223};
224
225/* 1600x1200x8 */
226static u16 cseq_1600x1200x8[] VAR16 = {
227 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107,
228 0x760b,0x760c,0x760d,0x760e,
229 0x0412,0x0013,0x2017,
230 0x341b,0x341c,0x341d,0x341e,
231 0xffff
232};
233static u16 ccrtc_1600x1200x8[] VAR16 = {
234 0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707,
235 0x6009,0x000c,0x000d,
236 0x0310,0xff12,0xa013,0x4014,0xff15,0x2416,0xc317,0xff18,
237 0x001a,0x221b,0x001d,
238 0xffff
239};
240
241static struct cirrus_mode_s cirrus_modes[] VAR16 = {
242 {0x5f,640,480,8,0x00,
243 cseq_640x480x8,cgraph_svgacolor,ccrtc_640x480x8,8,
244 4,0,0,0,0,0,0,0,0},
245 {0x64,640,480,16,0xe1,
246 cseq_640x480x16,cgraph_svgacolor,ccrtc_640x480x16,16,
247 6,5,11,6,5,5,0,0,0},
248 {0x66,640,480,15,0xf0,
249 cseq_640x480x16,cgraph_svgacolor,ccrtc_640x480x16,16,
250 6,5,10,5,5,5,0,1,15},
251 {0x71,640,480,24,0xe5,
252 cseq_640x480x24,cgraph_svgacolor,ccrtc_640x480x24,24,
253 6,8,16,8,8,8,0,0,0},
254
255 {0x5c,800,600,8,0x00,
256 cseq_800x600x8,cgraph_svgacolor,ccrtc_800x600x8,8,
257 4,0,0,0,0,0,0,0,0},
258 {0x65,800,600,16,0xe1,
259 cseq_800x600x16,cgraph_svgacolor,ccrtc_800x600x16,16,
260 6,5,11,6,5,5,0,0,0},
261 {0x67,800,600,15,0xf0,
262 cseq_800x600x16,cgraph_svgacolor,ccrtc_800x600x16,16,
263 6,5,10,5,5,5,0,1,15},
264
265 {0x60,1024,768,8,0x00,
266 cseq_1024x768x8,cgraph_svgacolor,ccrtc_1024x768x8,8,
267 4,0,0,0,0,0,0,0,0},
268 {0x74,1024,768,16,0xe1,
269 cseq_1024x768x16,cgraph_svgacolor,ccrtc_1024x768x16,16,
270 6,5,11,6,5,5,0,0,0},
271 {0x68,1024,768,15,0xf0,
272 cseq_1024x768x16,cgraph_svgacolor,ccrtc_1024x768x16,16,
273 6,5,10,5,5,5,0,1,15},
274
275 {0x78,800,600,24,0xe5,
276 cseq_800x600x24,cgraph_svgacolor,ccrtc_800x600x24,24,
277 6,8,16,8,8,8,0,0,0},
278 {0x79,1024,768,24,0xe5,
279 cseq_1024x768x24,cgraph_svgacolor,ccrtc_1024x768x24,24,
280 6,8,16,8,8,8,0,0,0},
281
282 {0x6d,1280,1024,8,0x00,
283 cseq_1280x1024x8,cgraph_svgacolor,ccrtc_1280x1024x8,8,
284 4,0,0,0,0,0,0,0,0},
285 {0x69,1280,1024,15,0xf0,
286 cseq_1280x1024x16,cgraph_svgacolor,ccrtc_1280x1024x16,16,
287 6,5,10,5,5,5,0,1,15},
288 {0x75,1280,1024,16,0xe1,
289 cseq_1280x1024x16,cgraph_svgacolor,ccrtc_1280x1024x16,16,
290 6,5,11,6,5,5,0,0,0},
291
292 {0x7b,1600,1200,8,0x00,
293 cseq_1600x1200x8,cgraph_svgacolor,ccrtc_1600x1200x8,8,
294 4,0,0,0,0,0,0,0,0},
295
296 {0xfe,0,0,0,0,cseq_vga,cgraph_vga,ccrtc_vga,0,
297 0xff,0,0,0,0,0,0,0,0},
298};
299
Kevin O'Connore48a5372011-12-20 23:56:14 -0500300
301/****************************************************************
302 * helper functions
303 ****************************************************************/
304
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400305static struct cirrus_mode_s *
306cirrus_get_modeentry(u8 mode)
307{
Kevin O'Connor4f792742009-05-16 14:55:01 -0400308 struct cirrus_mode_s *table_g = cirrus_modes;
309 while (table_g < &cirrus_modes[ARRAY_SIZE(cirrus_modes)]) {
310 u16 tmode = GET_GLOBAL(table_g->mode);
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400311 if (tmode == mode)
Kevin O'Connor4f792742009-05-16 14:55:01 -0400312 return table_g;
313 table_g++;
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400314 }
315 return NULL;
316}
317
318static void
319cirrus_switch_mode_setregs(u16 *data, u16 port)
320{
321 for (;;) {
322 u16 val = GET_GLOBAL(*data);
323 if (val == 0xffff)
324 return;
325 outw(val, port);
326 data++;
327 }
328}
329
330static u16
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500331cirrus_get_crtc(void)
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400332{
Kevin O'Connord9fc0a02009-05-07 22:00:25 -0400333 if (inb(VGAREG_READ_MISC_OUTPUT) & 1)
334 return VGAREG_VGA_CRTC_ADDRESS;
335 return VGAREG_MDA_CRTC_ADDRESS;
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400336}
337
338static void
339cirrus_switch_mode(struct cirrus_mode_s *table)
340{
341 // Unlock cirrus special
Kevin O'Connord9fc0a02009-05-07 22:00:25 -0400342 outw(0x1206, VGAREG_SEQU_ADDRESS);
343 cirrus_switch_mode_setregs(GET_GLOBAL(table->seq), VGAREG_SEQU_ADDRESS);
344 cirrus_switch_mode_setregs(GET_GLOBAL(table->graph), VGAREG_GRDC_ADDRESS);
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400345 cirrus_switch_mode_setregs(GET_GLOBAL(table->crtc), cirrus_get_crtc());
346
Kevin O'Connord9fc0a02009-05-07 22:00:25 -0400347 outb(0x00, VGAREG_PEL_MASK);
348 inb(VGAREG_PEL_MASK);
349 inb(VGAREG_PEL_MASK);
350 inb(VGAREG_PEL_MASK);
351 inb(VGAREG_PEL_MASK);
352 outb(GET_GLOBAL(table->hidden_dac), VGAREG_PEL_MASK);
353 outb(0xff, VGAREG_PEL_MASK);
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400354
355 u8 vesacolortype = GET_GLOBAL(table->vesacolortype);
Kevin O'Connor88ca7412011-12-31 04:24:20 -0500356 u8 v = stdvga_get_single_palette_reg(0x10) & 0xfe;
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400357 if (vesacolortype == 3)
358 v |= 0x41;
359 else if (vesacolortype)
360 v |= 0x01;
Kevin O'Connor88ca7412011-12-31 04:24:20 -0500361 stdvga_set_single_palette_reg(0x10, v);
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400362}
363
Kevin O'Connore48a5372011-12-20 23:56:14 -0500364static u8
365cirrus_get_memsize(void)
366{
367 // get DRAM band width
368 outb(0x0f, VGAREG_SEQU_ADDRESS);
369 u8 v = inb(VGAREG_SEQU_DATA);
370 u8 x = (v >> 3) & 0x03;
371 if (x == 0x03) {
372 if (v & 0x80)
373 // 4MB
374 return 0x40;
375 // 2MB
376 return 0x20;
377 }
378 return 0x04 << x;
379}
380
381static void
382cirrus_enable_16k_granularity(void)
383{
384 outb(0x0b, VGAREG_GRDC_ADDRESS);
385 u8 v = inb(VGAREG_GRDC_DATA);
386 outb(v | 0x20, VGAREG_GRDC_DATA);
387}
388
389static void
390cirrus_clear_vram(u16 param)
391{
392 cirrus_enable_16k_granularity();
393 u8 count = cirrus_get_memsize() * 4;
394 u8 i;
395 for (i=0; i<count; i++) {
396 outw((i<<8) | 0x09, VGAREG_GRDC_ADDRESS);
397 memset16_far(SEG_GRAPH, 0, param, 16 * 1024);
398 }
399 outw(0x0009, VGAREG_GRDC_ADDRESS);
400}
401
402int
403cirrus_set_video_mode(u8 mode, u8 noclearmem)
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400404{
405 dprintf(1, "cirrus mode %d\n", mode);
406 SET_BDA(vbe_mode, 0);
Kevin O'Connore48a5372011-12-20 23:56:14 -0500407 struct cirrus_mode_s *table_g = cirrus_get_modeentry(mode);
Kevin O'Connor4f792742009-05-16 14:55:01 -0400408 if (table_g) {
Kevin O'Connore48a5372011-12-20 23:56:14 -0500409 cirrus_switch_mode(table_g);
410 if (!noclearmem)
411 cirrus_clear_vram(0xffff);
412 SET_BDA(video_mode, mode);
413 return 1;
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400414 }
Kevin O'Connor4f792742009-05-16 14:55:01 -0400415 table_g = cirrus_get_modeentry(0xfe);
416 cirrus_switch_mode(table_g);
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400417 dprintf(1, "cirrus mode switch regular\n");
Kevin O'Connore48a5372011-12-20 23:56:14 -0500418 return 0;
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400419}
420
421static int
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500422cirrus_check(void)
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400423{
Kevin O'Connord9fc0a02009-05-07 22:00:25 -0400424 outw(0x9206, VGAREG_SEQU_ADDRESS);
425 return inb(VGAREG_SEQU_DATA) == 0x12;
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400426}
427
Kevin O'Connore48a5372011-12-20 23:56:14 -0500428
429/****************************************************************
430 * extbios
431 ****************************************************************/
432
433static void
434cirrus_extbios_80h(struct bregs *regs)
435{
436 u16 crtc_addr = cirrus_get_crtc();
437 outb(0x27, crtc_addr);
438 u8 v = inb(crtc_addr + 1);
439 if (v == 0xa0)
440 // 5430
441 regs->ax = 0x0032;
442 else if (v == 0xb8)
443 // 5446
444 regs->ax = 0x0039;
445 else
446 regs->ax = 0x00ff;
447 regs->bx = 0x00;
448 return;
449}
450
451static void
452cirrus_extbios_81h(struct bregs *regs)
453{
454 // XXX
455 regs->ax = 0x0100;
456}
457
458static void
459cirrus_extbios_82h(struct bregs *regs)
460{
461 u16 crtc_addr = cirrus_get_crtc();
462 outb(0x27, crtc_addr);
463 regs->al = inb(crtc_addr + 1) & 0x03;
464 regs->ah = 0xAF;
465}
466
467static void
468cirrus_extbios_85h(struct bregs *regs)
469{
470 regs->al = cirrus_get_memsize();
471}
472
473static void
474cirrus_extbios_9Ah(struct bregs *regs)
475{
476 regs->ax = 0x4060;
477 regs->cx = 0x1132;
478}
479
480extern void a0h_callback(void);
481ASM16(
482 // fatal: not implemented yet
483 "a0h_callback:"
484 "cli\n"
485 "hlt\n"
486 "retf");
487
488static void
489cirrus_extbios_A0h(struct bregs *regs)
490{
491 struct cirrus_mode_s *table_g = cirrus_get_modeentry(regs->al & 0x7f);
492 regs->ah = (table_g ? 1 : 0);
493 regs->si = 0xffff;
494 regs->di = regs->ds = regs->es = regs->bx = (u32)a0h_callback;
495}
496
497static void
498cirrus_extbios_A1h(struct bregs *regs)
499{
500 regs->bx = 0x0e00; // IBM 8512/8513, color
501}
502
503static void
504cirrus_extbios_A2h(struct bregs *regs)
505{
506 regs->al = 0x07; // HSync 31.5 - 64.0 kHz
507}
508
509static void
510cirrus_extbios_AEh(struct bregs *regs)
511{
512 regs->al = 0x01; // High Refresh 75Hz
513}
514
515void
516cirrus_extbios(struct bregs *regs)
517{
518 // XXX - regs->bl < 0x80 or > 0xaf call regular handlers.
519 switch (regs->bl) {
520 case 0x80: cirrus_extbios_80h(regs); break;
521 case 0x81: cirrus_extbios_81h(regs); break;
522 case 0x82: cirrus_extbios_82h(regs); break;
523 case 0x85: cirrus_extbios_85h(regs); break;
524 case 0x9a: cirrus_extbios_9Ah(regs); break;
525 case 0xa0: cirrus_extbios_A0h(regs); break;
526 case 0xa1: cirrus_extbios_A1h(regs); break;
527 case 0xa2: cirrus_extbios_A2h(regs); break;
528 case 0xae: cirrus_extbios_AEh(regs); break;
529 default: break;
530 }
531}
532
533
534/****************************************************************
535 * vesa calls
536 ****************************************************************/
537
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500538static struct {
539 u16 vesamode, mode;
540} cirrus_vesa_modelist[] VAR16 = {
541 // 640x480x8
542 { 0x101, 0x5f },
543 // 640x480x15
544 { 0x110, 0x66 },
545 // 640x480x16
546 { 0x111, 0x64 },
547 // 640x480x24
548 { 0x112, 0x71 },
549 // 800x600x8
550 { 0x103, 0x5c },
551 // 800x600x15
552 { 0x113, 0x67 },
553 // 800x600x16
554 { 0x114, 0x65 },
555 // 800x600x24
556 { 0x115, 0x78 },
557 // 1024x768x8
558 { 0x105, 0x60 },
559 // 1024x768x15
560 { 0x116, 0x68 },
561 // 1024x768x16
562 { 0x117, 0x74 },
563 // 1024x768x24
564 { 0x118, 0x79 },
565 // 1280x1024x8
566 { 0x107, 0x6d },
567 // 1280x1024x15
568 { 0x119, 0x69 },
569 // 1280x1024x16
570 { 0x11a, 0x75 },
571};
572
Kevin O'Connore48a5372011-12-20 23:56:14 -0500573static u16
574cirrus_vesamode_to_mode(u16 vesamode)
575{
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500576 int i;
577 for (i=0; i<ARRAY_SIZE(cirrus_vesa_modelist); i++)
578 if (GET_GLOBAL(cirrus_vesa_modelist[i].vesamode) == vesamode)
579 return GET_GLOBAL(cirrus_vesa_modelist[i].mode);
Kevin O'Connore48a5372011-12-20 23:56:14 -0500580 return 0;
581}
582
583static u8
584cirrus_get_bpp_bytes(void)
585{
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500586 outb(0x07, VGAREG_SEQU_ADDRESS);
587 u8 v = inb(VGAREG_SEQU_DATA) & 0x0e;
588 if (v == 0x06)
589 v &= 0x02;
590 v >>= 1;
591 if (v != 0x04)
592 v++;
593 return v;
Kevin O'Connore48a5372011-12-20 23:56:14 -0500594}
595
596static void
597cirrus_set_line_offset(u16 new_line_offset)
598{
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500599 u16 crtc_addr = cirrus_get_crtc();
600 outb(0x13, crtc_addr);
601 outb(new_line_offset / 8, crtc_addr + 1);
602
603 outb(0x1b, crtc_addr);
604 u8 v = inb(crtc_addr + 1);
605 outb((((new_line_offset / 8) & 0x100) >> 4) | (v & 0xef), crtc_addr + 1);
Kevin O'Connore48a5372011-12-20 23:56:14 -0500606}
607
608static u16
609cirrus_get_line_offset(void)
610{
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500611 u16 crtc_addr = cirrus_get_crtc();
612 outb(0x13, crtc_addr);
613 u8 reg13 = inb(crtc_addr + 1);
614 outb(0x1b, crtc_addr);
615 u8 reg1b = inb(crtc_addr + 1);
616
617 return (((reg1b << 4) & 0x100) + reg13) * 8;
Kevin O'Connore48a5372011-12-20 23:56:14 -0500618}
619
620static u16
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500621cirrus_get_line_offset_entry(struct cirrus_mode_s *table_g)
Kevin O'Connore48a5372011-12-20 23:56:14 -0500622{
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500623 u16 *crtc = GET_GLOBAL(table_g->crtc);
624
625 u16 *c = crtc;
626 u16 reg13;
627 for (;;) {
628 reg13 = GET_GLOBAL(*c);
629 if ((reg13 & 0xff) == 0x13)
630 break;
631 c++;
632 }
633 reg13 >>= 8;
634
635 c = crtc;
636 u16 reg1b;
637 for (;;) {
638 reg1b = GET_GLOBAL(*c);
639 if ((reg1b & 0xff) == 0x1b)
640 break;
641 c++;
642 }
643 reg1b >>= 8;
644
645 return (((reg1b << 4) & 0x100) + reg13) * 8;
Kevin O'Connore48a5372011-12-20 23:56:14 -0500646}
647
648static void
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500649cirrus_set_start_addr(u32 addr)
Kevin O'Connore48a5372011-12-20 23:56:14 -0500650{
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500651 u16 crtc_addr = cirrus_get_crtc();
652 outb(0x0d, crtc_addr);
653 outb(addr, crtc_addr + 1);
654
655 outb(0x0c, crtc_addr);
656 outb(addr>>8, crtc_addr + 1);
657
658 outb(0x1d, crtc_addr);
659 u8 v = inb(crtc_addr + 1);
660 outb(((addr & 0x0800) >> 4) | (v & 0x7f), crtc_addr + 1);
661
662 outb(0x1b, crtc_addr);
663 v = inb(crtc_addr + 1);
664 outb(((addr & 0x0100) >> 8) | ((addr & 0x0600) >> 7) | (v & 0xf2)
665 , crtc_addr + 1);
Kevin O'Connore48a5372011-12-20 23:56:14 -0500666}
667
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500668static u32
Kevin O'Connore48a5372011-12-20 23:56:14 -0500669cirrus_get_start_addr(void)
670{
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500671 u16 crtc_addr = cirrus_get_crtc();
672 outb(0x0c, crtc_addr);
673 u8 b2 = inb(crtc_addr + 1);
674
675 outb(0x0d, crtc_addr);
676 u8 b1 = inb(crtc_addr + 1);
677
678 outb(0x1b, crtc_addr);
679 u8 b3 = inb(crtc_addr + 1);
680
681 outb(0x1d, crtc_addr);
682 u8 b4 = inb(crtc_addr + 1);
683
684 return (b1 | (b2<<8) | ((b3 & 0x01) << 16) | ((b3 & 0x0c) << 15)
685 | ((b4 & 0x80) << 12));
Kevin O'Connore48a5372011-12-20 23:56:14 -0500686}
Kevin O'Connore48a5372011-12-20 23:56:14 -0500687
688static void
689cirrus_vesa_00h(struct bregs *regs)
690{
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500691 u16 seg = regs->es;
692 struct vbe_info *info = (void*)(regs->di+0);
693
694 if (GET_FARVAR(seg, info->signature) == VBE2_SIGNATURE) {
695 SET_FARVAR(seg, info->oem_revision, 0x0100);
696 SET_FARVAR(seg, info->oem_vendor_string,
697 SEGOFF(get_global_seg(), (u32)VBE_VENDOR_STRING));
698 SET_FARVAR(seg, info->oem_product_string,
699 SEGOFF(get_global_seg(), (u32)VBE_PRODUCT_STRING));
700 SET_FARVAR(seg, info->oem_revision_string,
701 SEGOFF(get_global_seg(), (u32)VBE_REVISION_STRING));
702 }
703 SET_FARVAR(seg, info->signature, VESA_SIGNATURE);
704
705 SET_FARVAR(seg, info->version, 0x0200);
706
707 SET_FARVAR(seg, info->oem_string
708 , SEGOFF(get_global_seg(), (u32)VBE_OEM_STRING));
709 SET_FARVAR(seg, info->capabilities, 0);
710 SET_FARVAR(seg, info->total_memory, cirrus_get_memsize());
711
712 u16 *destmode = (void*)info->reserved;
713 SET_FARVAR(seg, info->video_mode, SEGOFF(seg, (u32)destmode));
714 int i;
715 for (i=0; i<ARRAY_SIZE(cirrus_vesa_modelist); i++)
716 SET_FARVAR(seg, destmode[i]
717 , GET_GLOBAL(cirrus_vesa_modelist[i].vesamode));
718 SET_FARVAR(seg, destmode[i], 0xffff);
719
720 regs->ax = 0x004f;
Kevin O'Connore48a5372011-12-20 23:56:14 -0500721}
722
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500723static u32 cirrus_lfb_addr VAR16;
724
Kevin O'Connore48a5372011-12-20 23:56:14 -0500725static void
726cirrus_vesa_01h(struct bregs *regs)
727{
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500728 u16 mode = cirrus_vesamode_to_mode(regs->cx & 0x3fff);
729 if (!mode) {
730 regs->ax = 0x014f;
731 return;
732 }
733 struct cirrus_mode_s *table_g = cirrus_get_modeentry(mode);
734 u32 lfb = GET_GLOBAL(cirrus_lfb_addr); // XXX
735 if ((regs->cx & 0x4000) && !lfb) {
736 regs->ax = 0x014f;
737 return;
738 }
739
740 u16 seg = regs->es;
741 struct vbe_mode_info *info = (void*)(regs->di+0);
742 memset_far(seg, info, 0, sizeof(*info));
743
744 SET_FARVAR(seg, info->mode_attributes, lfb ? 0xbb : 0x3b);
745 SET_FARVAR(seg, info->winA_attributes, 0x07);
746 SET_FARVAR(seg, info->winB_attributes, 0);
747 SET_FARVAR(seg, info->win_granularity, 16);
748 SET_FARVAR(seg, info->win_size, 64);
749 SET_FARVAR(seg, info->winA_seg, SEG_GRAPH);
750 SET_FARVAR(seg, info->winB_seg, 0x0);
751 SET_FARVAR(seg, info->win_func_ptr.segoff, 0x0); // XXX
752 u16 linesize = cirrus_get_line_offset_entry(table_g);
753 SET_FARVAR(seg, info->bytes_per_scanline, linesize);
754 SET_FARVAR(seg, info->xres, GET_GLOBAL(table_g->width));
755 u16 height = GET_GLOBAL(table_g->height);
756 SET_FARVAR(seg, info->yres, height);
757 SET_FARVAR(seg, info->xcharsize, 8);
758 SET_FARVAR(seg, info->ycharsize, 16);
759 SET_FARVAR(seg, info->planes, 1);
760 SET_FARVAR(seg, info->bits_per_pixel, GET_GLOBAL(table_g->depth));
761 SET_FARVAR(seg, info->banks, 1);
762 SET_FARVAR(seg, info->mem_model, GET_GLOBAL(table_g->vesacolortype));
763 SET_FARVAR(seg, info->bank_size, 0);
764
765 int pages = (cirrus_get_memsize() * 64 * 1024) / (height * linesize);
766 SET_FARVAR(seg, info->pages, pages - 1);
767 SET_FARVAR(seg, info->reserved0, 0);
768
769 SET_FARVAR(seg, info->red_size, GET_GLOBAL(table_g->vesaredmask));
770 SET_FARVAR(seg, info->red_pos, GET_GLOBAL(table_g->vesaredpos));
771 SET_FARVAR(seg, info->green_size, GET_GLOBAL(table_g->vesagreenmask));
772 SET_FARVAR(seg, info->green_pos, GET_GLOBAL(table_g->vesagreenpos));
773 SET_FARVAR(seg, info->blue_size, GET_GLOBAL(table_g->vesabluemask));
774 SET_FARVAR(seg, info->blue_pos, GET_GLOBAL(table_g->vesabluepos));
775 SET_FARVAR(seg, info->alpha_size, GET_GLOBAL(table_g->vesareservedmask));
776 SET_FARVAR(seg, info->alpha_pos, GET_GLOBAL(table_g->vesareservedpos));
777 u8 directcolor_info = GET_GLOBAL(table_g->bitsperpixel) <= 8;
778 SET_FARVAR(seg, info->directcolor_info, directcolor_info);
779
780 SET_FARVAR(seg, info->phys_base, lfb);
781
782 regs->ax = 0x004f;
Kevin O'Connore48a5372011-12-20 23:56:14 -0500783}
784
785static void
786cirrus_vesa_02h(struct bregs *regs)
787{
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500788 if (regs->bx & 0x3e00) {
789 regs->ax = 0x014f;
790 return;
791 }
792 if ((regs->bx & 0x1ff) < 0x100) {
793 // XXX - call legacy mode switch
794 regs->ax = 0x004f;
795 return;
796 }
797
798 u16 mode = cirrus_vesamode_to_mode(regs->cx & 0x3fff);
799 if (!mode) {
800 regs->ax = 0x014f;
801 return;
802 }
803 struct cirrus_mode_s *table_g = cirrus_get_modeentry(mode);
804 cirrus_switch_mode(table_g);
805
806 if (!(regs->bx & 0x4000))
807 cirrus_enable_16k_granularity();
808 if (!(regs->bx & 0x8000))
809 cirrus_clear_vram(0);
810 SET_BDA(video_mode, mode);
811 SET_BDA(vbe_mode, regs->bx);
812
813 regs->ax = 0x004f;
Kevin O'Connore48a5372011-12-20 23:56:14 -0500814}
815
816static void
817cirrus_vesa_03h(struct bregs *regs)
818{
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500819 u16 mode = GET_BDA(vbe_mode);
820 if (!mode)
821 mode = GET_BDA(video_mode);
822 regs->bx = mode;
823
824 regs->ax = 0x004f;
Kevin O'Connore48a5372011-12-20 23:56:14 -0500825}
826
827// XXX - add cirrus_vesa_05h_farentry to vgaentry.S
828
829static void
830cirrus_vesa_05h(struct bregs *regs)
831{
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500832 if (regs->bl > 1)
833 goto fail;
834 if (regs->bh == 0) {
835 // set mempage
836 if (regs->dx >= 0x100)
837 goto fail;
838 outw((regs->dx << 8) | (regs->bl + 9), VGAREG_GRDC_ADDRESS);
839 } else if (regs->bh == 1) {
840 // get mempage
841 outb(regs->bl + 9, VGAREG_GRDC_ADDRESS);
842 regs->dx = inb(VGAREG_GRDC_DATA);
843 } else
844 goto fail;
845
846 regs->ax = 0x004f;
847 return;
848fail:
849 regs->ax = 0x014f;
Kevin O'Connore48a5372011-12-20 23:56:14 -0500850}
851
852static void
853cirrus_vesa_06h(struct bregs *regs)
854{
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500855 if (regs->bl > 2) {
856 regs->ax = 0x0100;
857 return;
858 }
859
860 if (regs->bl == 0x00) {
861 cirrus_set_line_offset(cirrus_get_bpp_bytes() * regs->cx);
862 } else if (regs->bl == 0x02) {
863 cirrus_set_line_offset(regs->cx);
864 }
865
866 u32 v = cirrus_get_line_offset();
867 regs->cx = v / cirrus_get_bpp_bytes();
868 regs->bx = v;
869 regs->dx = (cirrus_get_memsize() * 64 * 1024) / v;
870 regs->ax = 0x004f;
Kevin O'Connore48a5372011-12-20 23:56:14 -0500871}
872
873static void
874cirrus_vesa_07h(struct bregs *regs)
875{
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500876 if (regs->bl == 0x80 || regs->bl == 0x00) {
877 u32 addr = (cirrus_get_bpp_bytes() * regs->cx
878 + cirrus_get_line_offset() * regs->dx);
879 cirrus_set_start_addr(addr / 4);
880 } else if (regs->bl == 0x01) {
881 u32 addr = cirrus_get_start_addr() * 4;
882 u32 linelength = cirrus_get_line_offset();
883 regs->dx = addr / linelength;
884 regs->cx = (addr % linelength) / cirrus_get_bpp_bytes();
885 } else {
886 regs->ax = 0x0100;
887 return;
888 }
889
890 regs->ax = 0x004f;
Kevin O'Connore48a5372011-12-20 23:56:14 -0500891}
892
893static void
894cirrus_vesa_10h(struct bregs *regs)
895{
Kevin O'Connor8f4c0192011-12-31 03:00:59 -0500896 if (regs->bl == 0x00) {
897 regs->bx = 0x0f30;
898 regs->ax = 0x004f;
899 return;
900 }
901 if (regs->bl == 0x01) {
902 SET_BDA(vbe_flag, regs->bh);
903 regs->ax = 0x004f;
904 return;
905 }
906 if (regs->bl == 0x02) {
907 regs->bh = GET_BDA(vbe_flag);
908 regs->ax = 0x004f;
909 return;
910 }
911 regs->ax = 0x014f;
Kevin O'Connore48a5372011-12-20 23:56:14 -0500912}
913
914static void
915cirrus_vesa_not_handled(struct bregs *regs)
916{
917 debug_stub(regs);
918 regs->ax = 0x014f;
919}
920
921void
922cirrus_vesa(struct bregs *regs)
923{
924 switch (regs->al) {
925 case 0x00: cirrus_vesa_00h(regs); break;
926 case 0x01: cirrus_vesa_01h(regs); break;
927 case 0x02: cirrus_vesa_02h(regs); break;
928 case 0x03: cirrus_vesa_03h(regs); break;
929 case 0x05: cirrus_vesa_05h(regs); break;
930 case 0x06: cirrus_vesa_06h(regs); break;
931 case 0x07: cirrus_vesa_07h(regs); break;
932 case 0x10: cirrus_vesa_10h(regs); break;
933 default: cirrus_vesa_not_handled(regs); break;
934 }
935}
936
937
938/****************************************************************
939 * init
940 ****************************************************************/
941
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400942void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500943cirrus_init(void)
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400944{
945 dprintf(1, "cirrus init\n");
946 if (! cirrus_check())
947 return;
948 dprintf(1, "cirrus init 2\n");
949
950 // memory setup
Kevin O'Connord9fc0a02009-05-07 22:00:25 -0400951 outb(0x0f, VGAREG_SEQU_ADDRESS);
952 u8 v = inb(VGAREG_SEQU_DATA);
953 outb(((v & 0x18) << 8) | 0x0a, VGAREG_SEQU_ADDRESS);
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400954 // set vga mode
Kevin O'Connord9fc0a02009-05-07 22:00:25 -0400955 outw(0x0007, VGAREG_SEQU_ADDRESS);
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400956 // reset bitblt
Kevin O'Connord9fc0a02009-05-07 22:00:25 -0400957 outw(0x0431, VGAREG_GRDC_ADDRESS);
958 outw(0x0031, VGAREG_GRDC_ADDRESS);
Kevin O'Connor1f2c3072009-05-06 23:35:59 -0400959}