vgabios: Add support for vbe get/set line length function.
[seabios.git] / vgasrc / stdvga.c
1 // Standard VGA driver code
2 //
3 // Copyright (C) 2009  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2001-2008 the LGPL VGABios developers Team
5 //
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
7
8 #include "stdvga.h" // stdvga_init
9 #include "ioport.h" // outb
10 #include "farptr.h" // SET_FARVAR
11 #include "biosvar.h" // GET_GLOBAL
12 #include "util.h" // memcpy_far
13
14
15 /****************************************************************
16  * Attribute control
17  ****************************************************************/
18
19 void
20 stdvga_set_border_color(u8 color)
21 {
22     u8 v1 = color & 0x0f;
23     if (v1 & 0x08)
24         v1 += 0x08;
25     stdvga_attr_write(0x00, v1);
26
27     int i;
28     for (i = 1; i < 4; i++)
29         stdvga_attr_mask(i, 0x10, color & 0x10);
30 }
31
32 void
33 stdvga_set_overscan_border_color(u8 color)
34 {
35     stdvga_attr_write(0x11, color);
36 }
37
38 u8
39 stdvga_get_overscan_border_color(void)
40 {
41     return stdvga_attr_read(0x11);
42 }
43
44 void
45 stdvga_set_palette(u8 palid)
46 {
47     int i;
48     for (i = 1; i < 4; i++)
49         stdvga_attr_mask(i, 0x01, palid & 0x01);
50 }
51
52 void
53 stdvga_set_all_palette_reg(u16 seg, u8 *data_far)
54 {
55     int i;
56     for (i = 0; i < 0x10; i++) {
57         stdvga_attr_write(i, GET_FARVAR(seg, *data_far));
58         data_far++;
59     }
60     stdvga_attr_write(0x11, GET_FARVAR(seg, *data_far));
61 }
62
63 void
64 stdvga_get_all_palette_reg(u16 seg, u8 *data_far)
65 {
66     int i;
67     for (i = 0; i < 0x10; i++) {
68         SET_FARVAR(seg, *data_far, stdvga_attr_read(i));
69         data_far++;
70     }
71     SET_FARVAR(seg, *data_far, stdvga_attr_read(0x11));
72 }
73
74 void
75 stdvga_toggle_intensity(u8 flag)
76 {
77     stdvga_attr_mask(0x10, 0x08, (flag & 0x01) << 3);
78 }
79
80 void
81 stdvga_select_video_dac_color_page(u8 flag, u8 data)
82 {
83     if (!(flag & 0x01)) {
84         // select paging mode
85         stdvga_attr_mask(0x10, 0x80, data << 7);
86         return;
87     }
88     // select page
89     u8 val = stdvga_attr_read(0x10);
90     if (!(val & 0x80))
91         data <<= 2;
92     data &= 0x0f;
93     stdvga_attr_write(0x14, data);
94 }
95
96 void
97 stdvga_read_video_dac_state(u8 *pmode, u8 *curpage)
98 {
99     u8 val1 = stdvga_attr_read(0x10) >> 7;
100     u8 val2 = stdvga_attr_read(0x14) & 0x0f;
101     if (!(val1 & 0x01))
102         val2 >>= 2;
103     *pmode = val1;
104     *curpage = val2;
105 }
106
107
108 /****************************************************************
109  * DAC control
110  ****************************************************************/
111
112 void
113 stdvga_save_dac_state(u16 seg, struct saveDACcolors *info)
114 {
115     /* XXX: check this */
116     SET_FARVAR(seg, info->rwmode, inb(VGAREG_DAC_STATE));
117     SET_FARVAR(seg, info->peladdr, inb(VGAREG_DAC_WRITE_ADDRESS));
118     SET_FARVAR(seg, info->pelmask, stdvga_pelmask_read());
119     stdvga_dac_read(seg, info->dac, 0, 256);
120     SET_FARVAR(seg, info->color_select, 0);
121 }
122
123 void
124 stdvga_restore_dac_state(u16 seg, struct saveDACcolors *info)
125 {
126     stdvga_pelmask_write(GET_FARVAR(seg, info->pelmask));
127     stdvga_dac_write(seg, info->dac, 0, 256);
128     outb(GET_FARVAR(seg, info->peladdr), VGAREG_DAC_WRITE_ADDRESS);
129 }
130
131 void
132 stdvga_perform_gray_scale_summing(u16 start, u16 count)
133 {
134     stdvga_attrindex_write(0x00);
135     int i;
136     for (i = start; i < start+count; i++) {
137         u8 rgb[3];
138         stdvga_dac_read(GET_SEG(SS), rgb, i, 1);
139
140         // intensity = ( 0.3 * Red ) + ( 0.59 * Green ) + ( 0.11 * Blue )
141         u16 intensity = ((77 * rgb[0] + 151 * rgb[1] + 28 * rgb[2]) + 0x80) >> 8;
142         if (intensity > 0x3f)
143             intensity = 0x3f;
144
145         stdvga_dac_write(GET_SEG(SS), rgb, i, 1);
146     }
147     stdvga_attrindex_write(0x20);
148 }
149
150
151 /****************************************************************
152  * Memory control
153  ****************************************************************/
154
155 void
156 stdvga_set_text_block_specifier(u8 spec)
157 {
158     stdvga_sequ_write(0x03, spec);
159 }
160
161 // Enable reads and writes to the given "plane" when in planar4 mode.
162 void
163 stdvga_planar4_plane(int plane)
164 {
165     if (plane < 0) {
166         // Return to default mode (read plane0, write all planes)
167         stdvga_sequ_write(0x02, 0x0f);
168         stdvga_grdc_write(0x04, 0);
169     } else {
170         stdvga_sequ_write(0x02, 1<<plane);
171         stdvga_grdc_write(0x04, plane);
172     }
173 }
174
175
176 /****************************************************************
177  * Font loading
178  ****************************************************************/
179
180 static void
181 get_font_access(void)
182 {
183     stdvga_sequ_write(0x00, 0x01);
184     stdvga_sequ_write(0x02, 0x04);
185     stdvga_sequ_write(0x04, 0x07);
186     stdvga_sequ_write(0x00, 0x03);
187     stdvga_grdc_write(0x04, 0x02);
188     stdvga_grdc_write(0x05, 0x00);
189     stdvga_grdc_write(0x06, 0x04);
190 }
191
192 static void
193 release_font_access(void)
194 {
195     stdvga_sequ_write(0x00, 0x01);
196     stdvga_sequ_write(0x02, 0x03);
197     stdvga_sequ_write(0x04, 0x03);
198     stdvga_sequ_write(0x00, 0x03);
199     u16 v = (stdvga_misc_read() & 0x01) ? 0x0e : 0x0a;
200     stdvga_grdc_write(0x06, v);
201     stdvga_grdc_write(0x04, 0x00);
202     stdvga_grdc_write(0x05, 0x10);
203 }
204
205 void
206 stdvga_load_font(u16 seg, void *src_far, u16 count
207                  , u16 start, u8 destflags, u8 fontsize)
208 {
209     get_font_access();
210     u16 blockaddr = ((destflags & 0x03) << 14) + ((destflags & 0x04) << 11);
211     void *dest_far = (void*)(blockaddr + start*32);
212     u16 i;
213     for (i = 0; i < count; i++)
214         memcpy_far(SEG_GRAPH, dest_far + i*32
215                    , seg, src_far + i*fontsize, fontsize);
216     release_font_access();
217 }
218
219
220 /****************************************************************
221  * CRTC registers
222  ****************************************************************/
223
224 u16
225 stdvga_get_crtc(void)
226 {
227     if (stdvga_misc_read() & 1)
228         return VGAREG_VGA_CRTC_ADDRESS;
229     return VGAREG_MDA_CRTC_ADDRESS;
230 }
231
232 // Return the multiplication factor needed for the vga offset register.
233 int
234 stdvga_bpp_factor(struct vgamode_s *vmode_g)
235 {
236     switch (GET_GLOBAL(vmode_g->memmodel)) {
237     case MM_TEXT:
238         return 2;
239     case MM_CGA:
240         return GET_GLOBAL(vmode_g->depth);
241     case MM_PLANAR:
242         return 1;
243     default:
244         return 4;
245     }
246 }
247
248 void
249 stdvga_set_cursor_shape(u8 start, u8 end)
250 {
251     u16 crtc_addr = stdvga_get_crtc();
252     stdvga_crtc_write(crtc_addr, 0x0a, start);
253     stdvga_crtc_write(crtc_addr, 0x0b, end);
254 }
255
256 void
257 stdvga_set_active_page(u16 address)
258 {
259     u16 crtc_addr = stdvga_get_crtc();
260     stdvga_crtc_write(crtc_addr, 0x0c, address >> 8);
261     stdvga_crtc_write(crtc_addr, 0x0d, address);
262 }
263
264 void
265 stdvga_set_cursor_pos(u16 address)
266 {
267     u16 crtc_addr = stdvga_get_crtc();
268     stdvga_crtc_write(crtc_addr, 0x0e, address >> 8);
269     stdvga_crtc_write(crtc_addr, 0x0f, address);
270 }
271
272 void
273 stdvga_set_scan_lines(u8 lines)
274 {
275     stdvga_crtc_mask(stdvga_get_crtc(), 0x09, 0x1f, lines - 1);
276 }
277
278 // Get vertical display end
279 u16
280 stdvga_get_vde(void)
281 {
282     u16 crtc_addr = stdvga_get_crtc();
283     u16 vde = stdvga_crtc_read(crtc_addr, 0x12);
284     u8 ovl = stdvga_crtc_read(crtc_addr, 0x07);
285     vde += (((ovl & 0x02) << 7) + ((ovl & 0x40) << 3) + 1);
286     return vde;
287 }
288
289 int
290 stdvga_get_window(struct vgamode_s *vmode_g, int window)
291 {
292     return -1;
293 }
294
295 int
296 stdvga_set_window(struct vgamode_s *vmode_g, int window, int val)
297 {
298     return -1;
299 }
300
301 int
302 stdvga_get_linelength(struct vgamode_s *vmode_g)
303 {
304     u8 val = stdvga_crtc_read(stdvga_get_crtc(), 0x13);
305     return val * stdvga_bpp_factor(vmode_g) * 2;
306 }
307
308 int
309 stdvga_set_linelength(struct vgamode_s *vmode_g, int val)
310 {
311     int factor = stdvga_bpp_factor(vmode_g) * 2;
312     stdvga_crtc_write(stdvga_get_crtc(), 0x13, DIV_ROUND_UP(val, factor));
313     return 0;
314 }
315
316
317 /****************************************************************
318  * Save/Restore/Set state
319  ****************************************************************/
320
321 void
322 stdvga_save_state(u16 seg, struct saveVideoHardware *info)
323 {
324     u16 crtc_addr = stdvga_get_crtc();
325     SET_FARVAR(seg, info->sequ_index, inb(VGAREG_SEQU_ADDRESS));
326     SET_FARVAR(seg, info->crtc_index, inb(crtc_addr));
327     SET_FARVAR(seg, info->grdc_index, inb(VGAREG_GRDC_ADDRESS));
328     SET_FARVAR(seg, info->actl_index, stdvga_attrindex_read());
329     SET_FARVAR(seg, info->feature, inb(VGAREG_READ_FEATURE_CTL));
330
331     int i;
332     for (i=0; i<4; i++)
333         SET_FARVAR(seg, info->sequ_regs[i], stdvga_sequ_read(i+1));
334     SET_FARVAR(seg, info->sequ0, stdvga_sequ_read(0));
335
336     for (i=0; i<25; i++)
337         SET_FARVAR(seg, info->crtc_regs[i], stdvga_crtc_read(crtc_addr, i));
338
339     for (i=0; i<20; i++)
340         SET_FARVAR(seg, info->actl_regs[i], stdvga_attr_read(i));
341
342     for (i=0; i<9; i++)
343         SET_FARVAR(seg, info->grdc_regs[i], stdvga_grdc_read(i));
344
345     SET_FARVAR(seg, info->crtc_addr, crtc_addr);
346
347     /* XXX: read plane latches */
348     for (i=0; i<4; i++)
349         SET_FARVAR(seg, info->plane_latch[i], 0);
350 }
351
352 void
353 stdvga_restore_state(u16 seg, struct saveVideoHardware *info)
354 {
355     int i;
356     for (i=0; i<4; i++)
357         stdvga_sequ_write(i+1, GET_FARVAR(seg, info->sequ_regs[i]));
358     stdvga_sequ_write(0x00, GET_FARVAR(seg, info->sequ0));
359
360     // Disable CRTC write protection
361     u16 crtc_addr = GET_FARVAR(seg, info->crtc_addr);
362     stdvga_crtc_write(crtc_addr, 0x11, 0x00);
363     // Set CRTC regs
364     for (i=0; i<25; i++)
365         if (i != 0x11)
366             stdvga_crtc_write(crtc_addr, i, GET_FARVAR(seg, info->crtc_regs[i]));
367     // select crtc base address
368     stdvga_misc_mask(0x01, crtc_addr == VGAREG_VGA_CRTC_ADDRESS ? 0x01 : 0x00);
369
370     // enable write protection if needed
371     stdvga_crtc_write(crtc_addr, 0x11, GET_FARVAR(seg, info->crtc_regs[0x11]));
372
373     // Set Attribute Ctl
374     for (i=0; i<20; i++)
375         stdvga_attr_write(i, GET_FARVAR(seg, info->actl_regs[i]));
376     stdvga_attrindex_write(GET_FARVAR(seg, info->actl_index));
377
378     for (i=0; i<9; i++)
379         stdvga_grdc_write(i, GET_FARVAR(seg, info->grdc_regs[i]));
380
381     outb(GET_FARVAR(seg, info->sequ_index), VGAREG_SEQU_ADDRESS);
382     outb(GET_FARVAR(seg, info->crtc_index), crtc_addr);
383     outb(GET_FARVAR(seg, info->grdc_index), VGAREG_GRDC_ADDRESS);
384     outb(GET_FARVAR(seg, info->feature), crtc_addr - 0x4 + 0xa);
385 }
386
387 static void
388 clear_screen(struct vgamode_s *vmode_g)
389 {
390     switch (GET_GLOBAL(vmode_g->memmodel)) {
391     case MM_TEXT:
392         memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0720, 32*1024);
393         break;
394     case MM_CGA:
395         memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 32*1024);
396         break;
397     default:
398         // XXX - old code gets/sets/restores sequ register 2 to 0xf -
399         // but it should always be 0xf anyway.
400         memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 64*1024);
401     }
402 }
403
404 int
405 stdvga_set_mode(struct vgamode_s *vmode_g, int flags)
406 {
407     if (! stdvga_is_mode(vmode_g)) {
408         warn_internalerror();
409         return -1;
410     }
411     struct stdvga_mode_s *stdmode_g = container_of(
412         vmode_g, struct stdvga_mode_s, info);
413
414     // if palette loading (bit 3 of modeset ctl = 0)
415     if (!(flags & MF_NOPALETTE)) {    // Set the PEL mask
416         stdvga_pelmask_write(GET_GLOBAL(stdmode_g->pelmask));
417
418         // From which palette
419         u8 *palette_g = GET_GLOBAL(stdmode_g->dac);
420         u16 palsize = GET_GLOBAL(stdmode_g->dacsize) / 3;
421
422         // Always 256*3 values
423         stdvga_dac_write(get_global_seg(), palette_g, 0, palsize);
424         int i;
425         for (i = palsize; i < 0x0100; i++) {
426             static u8 rgb[3] VAR16;
427             stdvga_dac_write(get_global_seg(), rgb, i, 1);
428         }
429
430         if (flags & MF_GRAYSUM)
431             stdvga_perform_gray_scale_summing(0x00, 0x100);
432     }
433
434     // Set Attribute Ctl
435     u8 *regs = GET_GLOBAL(stdmode_g->actl_regs);
436     int i;
437     for (i = 0; i <= 0x13; i++)
438         stdvga_attr_write(i, GET_GLOBAL(regs[i]));
439     stdvga_attr_write(0x14, 0x00);
440
441     // Set Sequencer Ctl
442     stdvga_sequ_write(0x00, 0x03);
443     regs = GET_GLOBAL(stdmode_g->sequ_regs);
444     for (i = 1; i <= 4; i++)
445         stdvga_sequ_write(i, GET_GLOBAL(regs[i - 1]));
446
447     // Set Grafx Ctl
448     regs = GET_GLOBAL(stdmode_g->grdc_regs);
449     for (i = 0; i <= 8; i++)
450         stdvga_grdc_write(i, GET_GLOBAL(regs[i]));
451
452     // Set CRTC address VGA or MDA
453     u8 miscreg = GET_GLOBAL(stdmode_g->miscreg);
454     u16 crtc_addr = VGAREG_VGA_CRTC_ADDRESS;
455     if (!(miscreg & 1))
456         crtc_addr = VGAREG_MDA_CRTC_ADDRESS;
457
458     // Disable CRTC write protection
459     stdvga_crtc_write(crtc_addr, 0x11, 0x00);
460     // Set CRTC regs
461     regs = GET_GLOBAL(stdmode_g->crtc_regs);
462     for (i = 0; i <= 0x18; i++)
463         stdvga_crtc_write(crtc_addr, i, GET_GLOBAL(regs[i]));
464
465     // Set the misc register
466     stdvga_misc_write(miscreg);
467
468     // Enable video
469     stdvga_attrindex_write(0x20);
470
471     // Clear screen
472     if (!(flags & MF_NOCLEARMEM))
473         clear_screen(vmode_g);
474
475     // Write the fonts in memory
476     u8 memmodel = GET_GLOBAL(vmode_g->memmodel);
477     if (memmodel == MM_TEXT)
478         stdvga_load_font(get_global_seg(), vgafont16, 0x100, 0, 0, 16);
479
480     return 0;
481 }
482
483
484 /****************************************************************
485  * Misc
486  ****************************************************************/
487
488 void
489 stdvga_list_modes(u16 seg, u16 *dest, u16 *last)
490 {
491     SET_FARVAR(seg, *dest, 0xffff);
492 }
493
494 void
495 stdvga_enable_video_addressing(u8 disable)
496 {
497     u8 v = (disable & 1) ? 0x00 : 0x02;
498     stdvga_misc_mask(0x02, v);
499 }
500
501 int
502 stdvga_init(void)
503 {
504     // switch to color mode and enable CPU access 480 lines
505     stdvga_misc_write(0xc3);
506     // more than 64k 3C4/04
507     stdvga_sequ_write(0x04, 0x02);
508
509     return 0;
510 }