vga(4): Remove VGA_SLOW_IOACCESS option.
[dragonfly.git] / sys / dev / video / fb / vga.c
blobdee045c71a785524d5fc7d4f94238749cfec2faa
1 /*-
2 * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
3 * Copyright (c) 1992-1998 Søren Schmidt
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer as
11 * the first lines of this file unmodified.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 * $FreeBSD: src/sys/dev/fb/vga.c,v 1.9.2.1 2001/08/11 02:58:44 yokota Exp $
30 * $DragonFly: src/sys/dev/video/fb/vga.c,v 1.25 2008/01/19 08:50:12 swildner Exp $
33 #include "opt_vga.h"
34 #include "opt_fb.h"
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/conf.h>
40 #include <sys/fcntl.h>
41 #include <sys/malloc.h>
42 #include <sys/fbio.h>
43 #include <sys/thread2.h>
45 #include <bus/isa/isareg.h>
47 #include <machine/clock.h>
48 #include <machine/md_var.h>
49 #ifdef __i386__
50 #include <machine/pc/bios.h>
51 #endif
53 #include <vm/vm.h>
54 #include <vm/vm_param.h>
55 #include <vm/pmap.h>
57 #include "fbreg.h"
58 #include "vgareg.h"
60 #ifndef VGA_DEBUG
61 #define VGA_DEBUG 0
62 #endif
64 /* machine/pc/bios.h has got too much i386-specific stuff in it */
65 #ifndef BIOS_PADDRTOVADDR
66 #define BIOS_PADDRTOVADDR(x) (((x) - ISA_HOLE_START) + atdevbase)
67 #endif
68 int
69 vga_probe_unit(int unit, video_adapter_t *buf, int flags)
71 video_adapter_t *adp;
72 video_switch_t *sw;
73 int error;
75 sw = vid_get_switch(VGA_DRIVER_NAME);
76 if (sw == NULL)
77 return 0;
78 error = (*sw->probe)(unit, &adp, NULL, flags);
79 if (error)
80 return error;
81 bcopy(adp, buf, sizeof(*buf));
82 return 0;
85 int
86 vga_attach_unit(int unit, vga_softc_t *sc, int flags)
88 video_switch_t *sw;
89 int error;
91 sw = vid_get_switch(VGA_DRIVER_NAME);
92 if (sw == NULL)
93 return ENXIO;
95 error = (*sw->probe)(unit, &sc->adp, NULL, flags);
96 if (error)
97 return error;
98 return (*sw->init)(unit, sc->adp, flags);
101 /* cdev driver functions */
103 #ifdef FB_INSTALL_CDEV
105 struct ucred;
108 vga_open(cdev_t dev, vga_softc_t *sc, int flag, int mode, struct ucred *cred)
110 if (sc == NULL)
111 return ENXIO;
112 if (mode & (O_CREAT | O_APPEND | O_TRUNC))
113 return ENODEV;
115 return genfbopen(&sc->gensc, sc->adp, flag, mode, cred);
119 vga_close(cdev_t dev, vga_softc_t *sc, int flag, int mode)
121 return genfbclose(&sc->gensc, sc->adp, flag, mode);
125 vga_read(cdev_t dev, vga_softc_t *sc, struct uio *uio, int flag)
127 return genfbread(&sc->gensc, sc->adp, uio, flag);
131 vga_write(cdev_t dev, vga_softc_t *sc, struct uio *uio, int flag)
133 return genfbread(&sc->gensc, sc->adp, uio, flag);
137 vga_ioctl(cdev_t dev, vga_softc_t *sc, u_long cmd, caddr_t arg, int flag,
138 struct ucred *cred)
140 return genfbioctl(&sc->gensc, sc->adp, cmd, arg, flag, cred);
144 vga_mmap(cdev_t dev, vga_softc_t *sc, vm_offset_t offset, int prot)
146 return genfbmmap(&sc->gensc, sc->adp, offset, prot);
149 #endif /* FB_INSTALL_CDEV */
151 /* LOW-LEVEL */
153 #define probe_done(adp) ((adp)->va_flags & V_ADP_PROBED)
154 #define init_done(adp) ((adp)->va_flags & V_ADP_INITIALIZED)
155 #define config_done(adp) ((adp)->va_flags & V_ADP_REGISTERED)
157 /* various sizes */
158 #define V_MODE_MAP_SIZE (M_VGA_CG320 + 1)
159 #define V_MODE_PARAM_SIZE 64
161 /* video adapter state buffer */
162 struct adp_state {
163 int sig;
164 #define V_STATE_SIG 0x736f6962
165 u_char regs[V_MODE_PARAM_SIZE];
167 typedef struct adp_state adp_state_t;
170 * NOTE: `va_window' should have a virtual address, but is initialized
171 * with a physical address in the following table, as verify_adapter()
172 * will perform address conversion at run-time.
174 static video_adapter_t biosadapter = {
175 0, KD_VGA, VGA_DRIVER_NAME, 0, 0, V_ADP_COLOR, IO_VGA, 32,
176 EGA_BUF_BASE, EGA_BUF_SIZE, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE,
177 0, 0, 0, M_VGA_C80x25, M_C80x25, M_VGA_C80x25
180 /* video driver declarations */
181 static int vga_configure(int flags);
182 int (*vga_sub_configure)(int flags);
183 #if 0
184 static int vga_nop(void);
185 #endif
186 static int vga_error(void);
187 static vi_probe_t vga_probe;
188 static vi_init_t vga_init;
189 static vi_get_info_t vga_get_info;
190 static vi_query_mode_t vga_query_mode;
191 static vi_set_mode_t vga_set_mode;
192 static vi_save_font_t vga_save_font;
193 static vi_load_font_t vga_load_font;
194 static vi_show_font_t vga_show_font;
195 static vi_save_palette_t vga_save_palette;
196 static vi_load_palette_t vga_load_palette;
197 static vi_set_border_t vga_set_border;
198 static vi_save_state_t vga_save_state;
199 static vi_load_state_t vga_load_state;
200 static vi_set_win_org_t vga_set_origin;
201 static vi_read_hw_cursor_t vga_read_hw_cursor;
202 static vi_set_hw_cursor_t vga_set_hw_cursor;
203 static vi_set_hw_cursor_shape_t vga_set_hw_cursor_shape;
204 static vi_blank_display_t vga_blank_display;
205 static vi_mmap_t vga_mmap_buf;
206 static vi_ioctl_t vga_dev_ioctl;
207 #ifndef VGA_NO_MODE_CHANGE
208 static vi_clear_t vga_clear;
209 static vi_fill_rect_t vga_fill_rect;
210 static vi_bitblt_t vga_bitblt;
211 #else /* VGA_NO_MODE_CHANGE */
212 #define vga_clear (vi_clear_t *)vga_error
213 #define vga_fill_rect (vi_fill_rect_t *)vga_error
214 #define vga_bitblt (vi_bitblt_t *)vga_error
215 #endif
216 static vi_diag_t vga_diag;
218 static video_switch_t vgavidsw = {
219 vga_probe,
220 vga_init,
221 vga_get_info,
222 vga_query_mode,
223 vga_set_mode,
224 vga_save_font,
225 vga_load_font,
226 vga_show_font,
227 vga_save_palette,
228 vga_load_palette,
229 vga_set_border,
230 vga_save_state,
231 vga_load_state,
232 vga_set_origin,
233 vga_read_hw_cursor,
234 vga_set_hw_cursor,
235 vga_set_hw_cursor_shape,
236 vga_blank_display,
237 vga_mmap_buf,
238 vga_dev_ioctl,
239 vga_clear,
240 vga_fill_rect,
241 vga_bitblt,
242 vga_error,
243 vga_error,
244 vga_diag,
247 VIDEO_DRIVER(vga, vgavidsw, vga_configure);
249 /* VGA BIOS standard video modes */
250 #define EOT (-1)
251 #define NA (-2)
253 static video_info_t bios_vmode[] = {
254 /* CGA */
255 { M_B40x25, V_INFO_COLOR, 40, 25, 8, 8, 2, 1,
256 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
257 { M_C40x25, V_INFO_COLOR, 40, 25, 8, 8, 4, 1,
258 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
259 { M_B80x25, V_INFO_COLOR, 80, 25, 8, 8, 2, 1,
260 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
261 { M_C80x25, V_INFO_COLOR, 80, 25, 8, 8, 4, 1,
262 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
263 /* EGA */
264 { M_ENH_B40x25, V_INFO_COLOR, 40, 25, 8, 14, 2, 1,
265 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
266 { M_ENH_C40x25, V_INFO_COLOR, 40, 25, 8, 14, 4, 1,
267 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
268 { M_ENH_B80x25, V_INFO_COLOR, 80, 25, 8, 14, 2, 1,
269 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
270 { M_ENH_C80x25, V_INFO_COLOR, 80, 25, 8, 14, 4, 1,
271 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
272 /* VGA */
273 { M_VGA_C40x25, V_INFO_COLOR, 40, 25, 8, 16, 4, 1,
274 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
275 { M_VGA_M80x25, 0, 80, 25, 8, 16, 2, 1,
276 MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
277 { M_VGA_C80x25, V_INFO_COLOR, 80, 25, 8, 16, 4, 1,
278 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
279 /* MDA */
280 { M_EGAMONO80x25, 0, 80, 25, 8, 14, 2, 1,
281 MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
282 /* EGA */
283 { M_ENH_B80x43, 0, 80, 43, 8, 8, 2, 1,
284 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
285 { M_ENH_C80x43, V_INFO_COLOR, 80, 43, 8, 8, 4, 1,
286 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
287 /* VGA */
288 { M_VGA_M80x30, 0, 80, 30, 8, 16, 2, 1,
289 MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
290 { M_VGA_C80x30, V_INFO_COLOR, 80, 30, 8, 16, 4, 1,
291 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
292 { M_VGA_M80x50, 0, 80, 50, 8, 8, 2, 1,
293 MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
294 { M_VGA_C80x50, V_INFO_COLOR, 80, 50, 8, 8, 4, 1,
295 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
296 { M_VGA_M80x60, 0, 80, 60, 8, 8, 2, 1,
297 MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
298 { M_VGA_C80x60, V_INFO_COLOR, 80, 60, 8, 8, 4, 1,
299 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
301 #ifndef VGA_NO_MODE_CHANGE
303 #ifdef VGA_WIDTH90
304 { M_VGA_M90x25, 0, 90, 25, 8, 16, 2, 1,
305 MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
306 { M_VGA_C90x25, V_INFO_COLOR, 90, 25, 8, 16, 4, 1,
307 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
308 { M_VGA_M90x30, 0, 90, 30, 8, 16, 2, 1,
309 MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
310 { M_VGA_C90x30, V_INFO_COLOR, 90, 30, 8, 16, 4, 1,
311 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
312 { M_VGA_M90x43, 0, 90, 43, 8, 8, 2, 1,
313 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
314 { M_VGA_C90x43, V_INFO_COLOR, 90, 43, 8, 8, 4, 1,
315 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
316 { M_VGA_M90x50, 0, 90, 50, 8, 8, 2, 1,
317 MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
318 { M_VGA_C90x50, V_INFO_COLOR, 90, 50, 8, 8, 4, 1,
319 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
320 { M_VGA_M90x60, 0, 90, 60, 8, 8, 2, 1,
321 MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
322 { M_VGA_C90x60, V_INFO_COLOR, 90, 60, 8, 8, 4, 1,
323 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
324 #endif /* VGA_WIDTH90 */
326 /* CGA */
327 { M_BG320, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 2, 1,
328 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_CGA },
329 { M_CG320, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 2, 1,
330 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_CGA },
331 { M_BG640, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 200, 8, 8, 1, 1,
332 CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_CGA },
333 /* EGA */
334 { M_CG320_D, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 4, 4,
335 GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0,
336 V_INFO_MM_PLANAR },
337 { M_CG640_E, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 200, 8, 8, 4, 4,
338 GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 ,
339 V_INFO_MM_PLANAR },
340 { M_EGAMONOAPA, V_INFO_GRAPHICS, 640, 350, 8, 14, 4, 4,
341 GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, 64*1024, 0, 0 ,
342 V_INFO_MM_PLANAR },
343 { M_ENHMONOAPA2,V_INFO_GRAPHICS, 640, 350, 8, 14, 4, 4,
344 GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 ,
345 V_INFO_MM_PLANAR },
346 { M_CG640x350, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 350, 8, 14, 2, 2,
347 GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 ,
348 V_INFO_MM_PLANAR },
349 { M_ENH_CG640, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 350, 8, 14, 4, 4,
350 GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 ,
351 V_INFO_MM_PLANAR },
352 /* VGA */
353 { M_BG640x480, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 480, 8, 16, 4, 4,
354 GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 ,
355 V_INFO_MM_PLANAR },
356 { M_CG640x480, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 480, 8, 16, 4, 4,
357 GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 ,
358 V_INFO_MM_PLANAR },
359 { M_VGA_CG320, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 8, 1,
360 GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0,
361 V_INFO_MM_PACKED, 1 },
362 { M_VGA_MODEX, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 240, 8, 8, 8, 4,
363 GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0,
364 V_INFO_MM_VGAX, 1 },
365 #endif /* VGA_NO_MODE_CHANGE */
367 { EOT },
370 static int vga_init_done = FALSE;
371 #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
372 static u_char *video_mode_ptr = NULL;
373 #endif
374 static u_char *mode_map[V_MODE_MAP_SIZE];
375 static adp_state_t adpstate;
376 static adp_state_t adpstate2;
377 static int rows_offset = 1;
379 /* local macros and functions */
380 #define BIOS_SADDRTOLADDR(p) ((((p) & 0xffff0000) >> 12) + ((p) & 0x0000ffff))
382 #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
383 static void map_mode_table(u_char **, u_char *, int);
384 #endif
385 #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
386 static int map_mode_num(int);
387 #endif
388 static int map_bios_mode_num(int);
389 static u_char *get_mode_param(int);
390 static int verify_adapter(video_adapter_t *);
391 static void update_adapter_info(video_adapter_t *, video_info_t *);
392 #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
393 #define COMP_IDENTICAL 0
394 #define COMP_SIMILAR 1
395 #define COMP_DIFFERENT 2
396 static int comp_adpregs(u_char *, u_char *);
397 #endif
398 static int probe_adapters(void);
399 static int set_line_length(video_adapter_t *, int);
400 static int set_display_start(video_adapter_t *, int, int);
402 #ifndef VGA_NO_MODE_CHANGE
403 #ifdef VGA_WIDTH90
404 static void set_width90(adp_state_t *);
405 #endif
406 #endif /* !VGA_NO_MODE_CHANGE */
408 #ifndef VGA_NO_FONT_LOADING
409 #define PARAM_BUFSIZE 6
410 static void set_font_mode(video_adapter_t *, u_char *);
411 static void set_normal_mode(video_adapter_t *, u_char *);
412 #endif
414 #ifndef VGA_NO_MODE_CHANGE
415 static void filll_io(int, vm_offset_t, size_t);
416 static void planar_fill(video_adapter_t *, int);
417 static void packed_fill(video_adapter_t *, int);
418 static void direct_fill(video_adapter_t *, int);
419 #ifdef notyet
420 static void planar_fill_rect(video_adapter_t *, int, int, int, int, int);
421 static void packed_fill_rect(video_adapter_t *, int, int, int, int, int);
422 static void direct_fill_rect16(video_adapter_t *, int, int, int, int, int);
423 static void direct_fill_rect24(video_adapter_t *, int, int, int, int, int);
424 static void direct_fill_rect32(video_adapter_t *, int, int, int, int, int);
425 #endif /* notyet */
426 #endif /* !VGA_NO_MODE_CHANGE */
428 static void dump_buffer(u_char *, size_t);
430 #define ISMAPPED(pa, width) \
431 (((pa) <= (u_long)0x1000 - (width)) \
432 || ((pa) >= ISA_HOLE_START && (pa) <= 0x100000 - (width)))
434 #define prologue(adp, flag, err) \
435 if (!vga_init_done || !((adp)->va_flags & (flag))) \
436 return (err)
438 /* a backdoor for the console driver */
439 static int
440 vga_configure(int flags)
442 probe_adapters();
443 if (probe_done(&biosadapter)) {
444 biosadapter.va_flags |= V_ADP_INITIALIZED;
445 if (!config_done(&biosadapter) && !(vid_register(&biosadapter) < 0))
446 biosadapter.va_flags |= V_ADP_REGISTERED;
448 if (vga_sub_configure != NULL)
449 (*vga_sub_configure)(flags);
451 return 1;
454 /* local subroutines */
456 #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
457 /* construct the mode parameter map */
458 static void
459 map_mode_table(u_char *map[], u_char *table, int max)
461 int i;
463 for(i = 0; i < max; ++i)
464 map[i] = table + i*V_MODE_PARAM_SIZE;
465 for(; i < V_MODE_MAP_SIZE; ++i)
466 map[i] = NULL;
468 #endif /* !VGA_NO_BIOS && !VGA_NO_MODE_CHANGE */
470 #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
471 /* map the non-standard video mode to a known mode number */
472 static int
473 map_mode_num(int mode)
475 static struct {
476 int from;
477 int to;
478 } mode_map[] = {
479 { M_ENH_B80x43, M_ENH_B80x25 },
480 { M_ENH_C80x43, M_ENH_C80x25 },
481 { M_VGA_M80x30, M_VGA_M80x25 },
482 { M_VGA_C80x30, M_VGA_C80x25 },
483 { M_VGA_M80x50, M_VGA_M80x25 },
484 { M_VGA_C80x50, M_VGA_C80x25 },
485 { M_VGA_M80x60, M_VGA_M80x25 },
486 { M_VGA_C80x60, M_VGA_C80x25 },
487 #ifdef VGA_WIDTH90
488 { M_VGA_M90x25, M_VGA_M80x25 },
489 { M_VGA_C90x25, M_VGA_C80x25 },
490 { M_VGA_M90x30, M_VGA_M80x25 },
491 { M_VGA_C90x30, M_VGA_C80x25 },
492 { M_VGA_M90x43, M_ENH_B80x25 },
493 { M_VGA_C90x43, M_ENH_C80x25 },
494 { M_VGA_M90x50, M_VGA_M80x25 },
495 { M_VGA_C90x50, M_VGA_C80x25 },
496 { M_VGA_M90x60, M_VGA_M80x25 },
497 { M_VGA_C90x60, M_VGA_C80x25 },
498 #endif
499 { M_VGA_MODEX, M_VGA_CG320 },
501 int i;
503 for (i = 0; i < sizeof(mode_map)/sizeof(mode_map[0]); ++i) {
504 if (mode_map[i].from == mode)
505 return mode_map[i].to;
507 return mode;
509 #endif /* !VGA_NO_BIOS && !VGA_NO_MODE_CHANGE */
511 /* turn the BIOS video number into our video mode number */
512 static int
513 map_bios_mode_num(int bios_mode)
515 static int vga_modes[20] = {
516 M_VGA_C40x25, M_VGA_C40x25, /* 0, 1 */
517 M_VGA_C80x25, M_VGA_C80x25, /* 2, 3 */
518 M_BG320, M_CG320,
519 M_BG640,
520 M_VGA_M80x25, /* 7 */
521 8, 9, 10, 11, 12,
522 M_CG320_D,
523 M_CG640_E,
524 M_ENHMONOAPA2,
525 M_ENH_CG640,
526 M_BG640x480, M_CG640x480,
527 M_VGA_CG320,
530 if (bios_mode < sizeof(vga_modes)/sizeof(vga_modes[0]))
531 return vga_modes[bios_mode];
533 return M_VGA_C80x25;
536 /* look up a parameter table entry */
537 static u_char *
538 get_mode_param(int mode)
540 #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
541 if (mode >= V_MODE_MAP_SIZE)
542 mode = map_mode_num(mode);
543 #endif
544 if ((mode >= 0) && (mode < V_MODE_MAP_SIZE))
545 return mode_map[mode];
546 else
547 return NULL;
550 static int
551 verify_adapter(video_adapter_t *adp)
553 vm_offset_t buf;
554 u_int16_t v;
555 #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
556 u_int32_t p;
557 #endif
559 buf = BIOS_PADDRTOVADDR(adp->va_window);
560 v = readw(buf);
561 writew(buf, 0xA55A);
562 if (readw(buf) != 0xA55A)
563 return ENXIO;
564 writew(buf, v);
566 outb(CRTC, 7);
567 if (inb(CRTC) != 7)
568 return ENXIO;
570 adp->va_flags |= V_ADP_STATELOAD | V_ADP_STATESAVE | V_ADP_PALETTE |
571 V_ADP_BORDER;
573 #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
574 /* get the BIOS video mode pointer */
575 p = *(u_int32_t *)BIOS_PADDRTOVADDR(0x4a8);
576 p = BIOS_SADDRTOLADDR(p);
577 if (ISMAPPED(p, sizeof(u_int32_t))) {
578 p = *(u_int32_t *)BIOS_PADDRTOVADDR(p);
579 p = BIOS_SADDRTOLADDR(p);
580 if (ISMAPPED(p, V_MODE_PARAM_SIZE))
581 video_mode_ptr = (u_char *)BIOS_PADDRTOVADDR(p);
583 #endif
585 return 0;
588 static void
589 update_adapter_info(video_adapter_t *adp, video_info_t *info)
591 adp->va_flags |= V_ADP_COLOR;
592 adp->va_window = BIOS_PADDRTOVADDR(info->vi_window);
593 adp->va_window_size = info->vi_window_size;
594 adp->va_window_gran = info->vi_window_gran;
595 adp->va_window_orig = 0;
596 /* XXX */
597 adp->va_buffer = info->vi_buffer;
598 adp->va_buffer_size = info->vi_buffer_size;
599 if (info->vi_mem_model == V_INFO_MM_VGAX) {
600 adp->va_line_width = info->vi_width/2;
601 } else if (info->vi_flags & V_INFO_GRAPHICS) {
602 switch (info->vi_depth/info->vi_planes) {
603 case 1:
604 adp->va_line_width = info->vi_width/8;
605 break;
606 case 2:
607 adp->va_line_width = info->vi_width/4;
608 break;
609 case 4:
610 adp->va_line_width = info->vi_width/2;
611 break;
612 case 8:
613 default: /* shouldn't happen */
614 adp->va_line_width = info->vi_width;
615 break;
617 } else {
618 adp->va_line_width = info->vi_width;
620 adp->va_disp_start.x = 0;
621 adp->va_disp_start.y = 0;
622 bcopy(info, &adp->va_info, sizeof(adp->va_info));
625 #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
626 /* compare two parameter table entries */
627 static int
628 comp_adpregs(u_char *buf1, u_char *buf2)
630 static struct {
631 u_char mask;
632 } params[V_MODE_PARAM_SIZE] = {
633 {0xff}, {0x00}, {0xff}, /* COLS}, ROWS}, POINTS */
634 {0x00}, {0x00}, /* page length */
635 {0xfe}, {0xff}, {0xff}, {0xff}, /* sequencer registers */
636 {0xf3}, /* misc register */
637 {0xff}, {0xff}, {0xff}, {0x7f}, {0xff}, /* CRTC */
638 {0xff}, {0xff}, {0xff}, {0x7f}, {0xff},
639 {0x00}, {0x00}, {0x00}, {0x00}, {0x00},
640 {0x00}, {0xff}, {0x7f}, {0xff}, {0xff},
641 {0x7f}, {0xff}, {0xff}, {0xef}, {0xff},
642 {0xff}, {0xff}, {0xff}, {0xff}, {0xff}, /* attribute controller regs */
643 {0xff}, {0xff}, {0xff}, {0xff}, {0xff},
644 {0xff}, {0xff}, {0xff}, {0xff}, {0xff},
645 {0xff}, {0xff}, {0xff}, {0xff}, {0xf0},
646 {0xff}, {0xff}, {0xff}, {0xff}, {0xff}, /* GDC register */
647 {0xff}, {0xff}, {0xff}, {0xff},
649 int identical = TRUE;
650 int i;
652 if ((buf1 == NULL) || (buf2 == NULL))
653 return COMP_DIFFERENT;
655 for (i = 0; i < sizeof(params)/sizeof(params[0]); ++i) {
656 if (params[i].mask == 0) /* don't care */
657 continue;
658 if ((buf1[i] & params[i].mask) != (buf2[i] & params[i].mask))
659 return COMP_DIFFERENT;
660 if (buf1[i] != buf2[i])
661 identical = FALSE;
663 return (identical) ? COMP_IDENTICAL : COMP_SIMILAR;
665 #endif /* !VGA_NO_BIOS && !VGA_NO_MODE_CHANGE */
667 /* probe video adapters and return the number of detected adapters */
668 static int
669 probe_adapters(void)
671 video_adapter_t *adp;
672 video_info_t info;
673 #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
674 u_char *mp;
675 #endif
676 int i;
678 /* do this test only once */
679 if (vga_init_done)
680 return 1;
681 vga_init_done = TRUE;
684 * Check BIOS data area.
685 * The VGA BIOS has more sophisticated mechanism and has this
686 * information in BIOSDATA_DCCINDEX (40:8a), but it also maintains
687 * compatibility with the EGA BIOS by updating BIOSDATA_VIDEOSWITCH.
691 * XXX: we don't use BIOSDATA_EQUIPMENT, since it is not a dead
692 * copy of RTC_EQUIPMENT. Bits 4 and 5 of ETC_EQUIPMENT are
693 * zeros for VGA. However, VGA BIOS sets these bits in
694 * BIOSDATA_EQUIPMENT according to the monitor type detected.
696 #ifndef VGA_NO_BIOS
697 if ((readb(BIOS_PADDRTOVADDR(0x488)) & 0x0f) != 0x09)
698 return 0;
699 #endif /* VGA_NO_BIOS */
701 if (verify_adapter(&biosadapter) != 0)
702 return 0;
704 biosadapter.va_flags |= V_ADP_PROBED;
705 #ifndef VGA_NO_BIOS
706 biosadapter.va_initial_bios_mode = readb(BIOS_PADDRTOVADDR(0x449));
707 biosadapter.va_mode = biosadapter.va_initial_mode =
708 map_bios_mode_num(biosadapter.va_initial_bios_mode);
709 #endif
712 * Ensure a zero start address. This is mainly to recover after
713 * switching from pcvt using userconfig(). The registers are w/o
714 * for old hardware so it's too hard to relocate the active screen
715 * memory.
716 * This must be done before vga_save_state() for VGA.
718 outb(CRTC, 12);
719 outb(CRTC + 1, 0);
720 outb(CRTC, 13);
721 outb(CRTC + 1, 0);
723 /* the video mode parameter table in VGA BIOS */
724 /* NOTE: there can be only one VGA recognized by the video BIOS.
726 adp = &biosadapter;
727 bzero(mode_map, sizeof(mode_map));
728 vga_save_state(adp, &adpstate, sizeof(adpstate));
729 for(i = 0; i < 16; i++)
730 adp->va_palette_regs[i] = adpstate.regs[35 + i];
731 #if defined(VGA_NO_BIOS) || defined(VGA_NO_MODE_CHANGE)
732 mode_map[adp->va_initial_mode] = adpstate.regs;
733 rows_offset = 1;
734 #else /* VGA_NO_BIOS || VGA_NO_MODE_CHANGE */
735 if (video_mode_ptr == NULL) {
736 mode_map[adp->va_initial_mode] = adpstate.regs;
737 rows_offset = 1;
738 } else {
739 /* discard the table if we are not familiar with it... */
740 map_mode_table(mode_map, video_mode_ptr, M_VGA_CG320 + 1);
741 mp = get_mode_param(adp->va_initial_mode);
742 if (mp != NULL)
743 bcopy(mp, adpstate2.regs, sizeof(adpstate2.regs));
744 switch (comp_adpregs(adpstate.regs, mp)) {
745 case COMP_IDENTICAL:
747 * OK, this parameter table looks reasonably familiar
748 * to us...
751 * This is a kludge for Toshiba DynaBook SS433
752 * whose BIOS video mode table entry has the actual #
753 * of rows at the offset 1; BIOSes from other
754 * manufacturers store the # of rows - 1 there. XXX
756 rows_offset = adpstate.regs[1] + 1 - mp[1];
757 break;
759 case COMP_SIMILAR:
761 * Not exactly the same, but similar enough to be
762 * trusted. However, use the saved register values
763 * for the initial mode and other modes which are
764 * based on the initial mode.
766 mode_map[adp->va_initial_mode] = adpstate.regs;
767 rows_offset = adpstate.regs[1] + 1 - mp[1];
768 adpstate.regs[1] -= rows_offset - 1;
769 break;
771 case COMP_DIFFERENT:
772 default:
774 * Don't use the paramter table in BIOS. It doesn't
775 * look familiar to us. Video mode switching is allowed
776 * only if the new mode is the same as or based on
777 * the initial mode.
779 video_mode_ptr = NULL;
780 bzero(mode_map, sizeof(mode_map));
781 mode_map[adp->va_initial_mode] = adpstate.regs;
782 rows_offset = 1;
783 break;
786 #endif /* VGA_NO_BIOS || VGA_NO_MODE_CHANGE */
788 #ifndef VGA_NO_MODE_CHANGE
789 adp->va_flags |= V_ADP_MODECHANGE;
790 #endif
791 #ifndef VGA_NO_FONT_LOADING
792 adp->va_flags |= V_ADP_FONT;
793 #endif
795 /* XXX remove conflicting modes */
796 for (i = 0; i < M_VGA_CG320; i++) {
797 if (vga_get_info(&biosadapter, i, &info))
798 continue;
799 if ((info.vi_flags & V_INFO_COLOR) != V_ADP_COLOR)
800 mode_map[i] = NULL;
803 /* buffer address */
804 vga_get_info(&biosadapter, biosadapter.va_initial_mode, &info);
805 info.vi_flags &= ~V_INFO_LINEAR; /* XXX */
806 update_adapter_info(&biosadapter, &info);
809 * XXX: we should verify the following values for the primary adapter...
810 * crtc I/O port address: *(u_int16_t *)BIOS_PADDRTOVADDR(0x463);
811 * color/mono display: (*(u_int8_t *)BIOS_PADDRTOVADDR(0x487) & 0x02)
812 * ? 0 : V_ADP_COLOR;
813 * columns: *(u_int8_t *)BIOS_PADDRTOVADDR(0x44a);
814 * rows: *(u_int8_t *)BIOS_PADDRTOVADDR(0x484);
815 * font size: *(u_int8_t *)BIOS_PADDRTOVADDR(0x485);
816 * buffer size: *(u_int16_t *)BIOS_PADDRTOVADDR(0x44c);
819 return 1;
822 /* set the scan line length in pixel */
823 static int
824 set_line_length(video_adapter_t *adp, int pixel)
826 u_char *mp;
827 int ppw; /* pixels per word */
828 int bpl; /* bytes per line */
829 int count;
831 mp = get_mode_param(adp->va_mode);
832 if (mp == NULL)
833 return EINVAL;
835 switch (adp->va_info.vi_mem_model) {
836 case V_INFO_MM_PLANAR:
837 ppw = 16/(adp->va_info.vi_depth/adp->va_info.vi_planes);
838 count = (pixel + ppw - 1)/ppw/2;
839 bpl = ((pixel + ppw - 1)/ppw/2)*4;
840 break;
841 case V_INFO_MM_PACKED:
842 count = (pixel + 7)/8;
843 bpl = ((pixel + 7)/8)*8;
844 break;
845 case V_INFO_MM_TEXT:
846 count = (pixel + 7)/8; /* columns */
847 bpl = (pixel + 7)/8; /* columns */
848 break;
849 default:
850 return ENODEV;
853 if (mp[10 + 0x17] & 0x40) /* CRTC mode control reg */
854 count *= 2; /* byte mode */
855 outb(CRTC, 0x13);
856 outb(CRTC, count);
857 adp->va_line_width = bpl;
859 return 0;
862 static int
863 set_display_start(video_adapter_t *adp, int x, int y)
865 int off; /* byte offset (graphics mode)/word offset (text mode) */
866 int poff; /* pixel offset */
867 int roff; /* row offset */
868 int ppb; /* pixels per byte */
870 if (adp->va_info.vi_flags & V_INFO_GRAPHICS) {
871 ppb = 8/(adp->va_info.vi_depth/adp->va_info.vi_planes);
872 off = y*adp->va_line_width + x/ppb;
873 roff = 0;
874 poff = x%ppb;
875 } else {
876 outb(TSIDX, 1);
877 if (inb(TSREG) & 1)
878 ppb = 9;
879 else
880 ppb = 8;
881 off = y/adp->va_info.vi_cheight*adp->va_line_width + x/ppb;
882 roff = y%adp->va_info.vi_cheight;
883 /* FIXME: is this correct? XXX */
884 if (ppb == 8)
885 poff = x%ppb;
886 else
887 poff = (x + 8)%ppb;
890 /* start address */
891 outb(CRTC, 0xc); /* high */
892 outb(CRTC + 1, off >> 8);
893 outb(CRTC, 0xd); /* low */
894 outb(CRTC + 1, off & 0xff);
896 /* horizontal pel pan */
897 inb(CRTC + 6);
898 outb(ATC, 0x13 | 0x20);
899 outb(ATC, poff);
900 inb(CRTC + 6);
901 outb(ATC, 0x20);
903 /* preset raw scan */
904 outb(CRTC, 8);
905 outb(CRTC + 1, roff);
907 adp->va_disp_start.x = x;
908 adp->va_disp_start.y = y;
909 return 0;
912 #ifndef VGA_NO_MODE_CHANGE
913 #if defined(__i386__) || defined(__x86_64__) /* XXX */
914 static void
915 fill(int val, void *d, size_t size)
917 u_char *p = d;
919 while (size-- > 0)
920 *p++ = val;
922 #endif /* __i386__ || __x86_64__ */
924 static void
925 filll_io(int val, vm_offset_t d, size_t size)
927 while (size-- > 0) {
928 writel(d, val);
929 d += sizeof(u_int32_t);
932 #endif /* !VGA_NO_MODE_CHANGE */
934 /* entry points */
936 #if 0
937 static int
938 vga_nop(void)
940 return 0;
942 #endif
944 static int
945 vga_error(void)
947 return ENODEV;
950 static int
951 vga_probe(int unit, video_adapter_t **adpp, void *arg, int flags)
953 probe_adapters();
954 if (unit != 0)
955 return ENXIO;
957 *adpp = &biosadapter;
959 return 0;
962 static int
963 vga_init(int unit, video_adapter_t *adp, int flags)
965 if ((unit != 0) || (adp == NULL) || !probe_done(adp))
966 return ENXIO;
968 if (!init_done(adp)) {
969 /* nothing to do really... */
970 adp->va_flags |= V_ADP_INITIALIZED;
973 if (!config_done(adp)) {
974 if (vid_register(adp) < 0)
975 return ENXIO;
976 adp->va_flags |= V_ADP_REGISTERED;
978 if (vga_sub_configure != NULL)
979 (*vga_sub_configure)(0);
981 return 0;
985 * get_info():
986 * Return the video_info structure of the requested video mode.
988 static int
989 vga_get_info(video_adapter_t *adp, int mode, video_info_t *info)
991 int i;
993 if (!vga_init_done)
994 return ENXIO;
996 #ifndef VGA_NO_MODE_CHANGE
997 if (adp->va_flags & V_ADP_MODECHANGE) {
999 * If the parameter table entry for this mode is not found,
1000 * the mode is not supported...
1002 if (get_mode_param(mode) == NULL)
1003 return EINVAL;
1004 } else
1005 #endif /* VGA_NO_MODE_CHANGE */
1008 * Even if we don't support video mode switching on this adapter,
1009 * the information on the initial (thus current) video mode
1010 * should be made available.
1012 if (mode != adp->va_initial_mode)
1013 return EINVAL;
1016 for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) {
1017 if (bios_vmode[i].vi_mode == NA)
1018 continue;
1019 if (mode == bios_vmode[i].vi_mode) {
1020 *info = bios_vmode[i];
1021 /* XXX */
1022 info->vi_buffer_size = info->vi_window_size*info->vi_planes;
1023 return 0;
1026 return EINVAL;
1030 * query_mode():
1031 * Find a video mode matching the requested parameters.
1032 * Fields filled with 0 are considered "don't care" fields and
1033 * match any modes.
1035 static int
1036 vga_query_mode(video_adapter_t *adp, video_info_t *info)
1038 int i;
1040 if (!vga_init_done)
1041 return ENXIO;
1043 for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) {
1044 if (bios_vmode[i].vi_mode == NA)
1045 continue;
1047 if ((info->vi_width != 0)
1048 && (info->vi_width != bios_vmode[i].vi_width))
1049 continue;
1050 if ((info->vi_height != 0)
1051 && (info->vi_height != bios_vmode[i].vi_height))
1052 continue;
1053 if ((info->vi_cwidth != 0)
1054 && (info->vi_cwidth != bios_vmode[i].vi_cwidth))
1055 continue;
1056 if ((info->vi_cheight != 0)
1057 && (info->vi_cheight != bios_vmode[i].vi_cheight))
1058 continue;
1059 if ((info->vi_depth != 0)
1060 && (info->vi_depth != bios_vmode[i].vi_depth))
1061 continue;
1062 if ((info->vi_planes != 0)
1063 && (info->vi_planes != bios_vmode[i].vi_planes))
1064 continue;
1065 /* XXX: should check pixel format, memory model */
1066 if ((info->vi_flags != 0)
1067 && (info->vi_flags != bios_vmode[i].vi_flags))
1068 continue;
1070 /* verify if this mode is supported on this adapter */
1071 if (vga_get_info(adp, bios_vmode[i].vi_mode, info))
1072 continue;
1073 return 0;
1075 return ENODEV;
1079 * set_mode():
1080 * Change the video mode.
1083 #ifndef VGA_NO_MODE_CHANGE
1084 #ifdef VGA_WIDTH90
1085 static void
1086 set_width90(adp_state_t *params)
1089 * Based on code submitted by Kelly Yancey (kbyanc@freedomnet.com)
1090 * and alexv@sui.gda.itesm.mx.
1092 params->regs[5] |= 1; /* toggle 8 pixel wide fonts */
1093 params->regs[10+0x0] = 0x6b;
1094 params->regs[10+0x1] = 0x59;
1095 params->regs[10+0x2] = 0x5a;
1096 params->regs[10+0x3] = 0x8e;
1097 params->regs[10+0x4] = 0x5e;
1098 params->regs[10+0x5] = 0x8a;
1099 params->regs[10+0x13] = 45;
1100 params->regs[35+0x13] = 0;
1102 #endif /* VGA_WIDTH90 */
1103 #endif /* !VGA_NO_MODE_CHANGE */
1105 static int
1106 vga_set_mode(video_adapter_t *adp, int mode)
1108 #ifndef VGA_NO_MODE_CHANGE
1109 video_info_t info;
1110 adp_state_t params;
1112 prologue(adp, V_ADP_MODECHANGE, ENODEV);
1114 if (vga_get_info(adp, mode, &info))
1115 return EINVAL;
1117 #if VGA_DEBUG > 1
1118 kprintf("vga_set_mode(): setting mode %d\n", mode);
1119 #endif
1121 params.sig = V_STATE_SIG;
1122 bcopy(get_mode_param(mode), params.regs, sizeof(params.regs));
1124 switch (mode) {
1125 #ifdef VGA_WIDTH90
1126 case M_VGA_C90x60: case M_VGA_M90x60:
1127 set_width90(&params);
1128 /* FALLTHROUGH */
1129 #endif
1130 case M_VGA_C80x60: case M_VGA_M80x60:
1131 params.regs[2] = 0x08;
1132 params.regs[19] = 0x47;
1133 goto special_480l;
1135 #ifdef VGA_WIDTH90
1136 case M_VGA_C90x30: case M_VGA_M90x30:
1137 set_width90(&params);
1138 /* FALLTHROUGH */
1139 #endif
1140 case M_VGA_C80x30: case M_VGA_M80x30:
1141 params.regs[19] = 0x4f;
1142 special_480l:
1143 params.regs[9] |= 0xc0;
1144 params.regs[16] = 0x08;
1145 params.regs[17] = 0x3e;
1146 params.regs[26] = 0xea;
1147 params.regs[28] = 0xdf;
1148 params.regs[31] = 0xe7;
1149 params.regs[32] = 0x04;
1150 goto setup_mode;
1152 #ifdef VGA_WIDTH90
1153 case M_VGA_C90x43: case M_VGA_M90x43:
1154 set_width90(&params);
1155 /* FALLTHROUGH */
1156 #endif
1157 case M_ENH_C80x43: case M_ENH_B80x43:
1158 params.regs[28] = 87;
1159 goto special_80x50;
1161 #ifdef VGA_WIDTH90
1162 case M_VGA_C90x50: case M_VGA_M90x50:
1163 set_width90(&params);
1164 /* FALLTHROUGH */
1165 #endif
1166 case M_VGA_C80x50: case M_VGA_M80x50:
1167 special_80x50:
1168 params.regs[2] = 8;
1169 params.regs[19] = 7;
1170 goto setup_mode;
1172 #ifdef VGA_WIDTH90
1173 case M_VGA_C90x25: case M_VGA_M90x25:
1174 set_width90(&params);
1175 /* FALLTHROUGH */
1176 #endif
1177 case M_VGA_C40x25: case M_VGA_C80x25:
1178 case M_VGA_M80x25:
1179 case M_B40x25: case M_C40x25:
1180 case M_B80x25: case M_C80x25:
1181 case M_ENH_B40x25: case M_ENH_C40x25:
1182 case M_ENH_B80x25: case M_ENH_C80x25:
1183 case M_EGAMONO80x25:
1185 setup_mode:
1186 vga_load_state(adp, &params);
1187 break;
1189 case M_VGA_MODEX:
1190 /* "unchain" the VGA mode */
1191 params.regs[5-1+0x04] &= 0xf7;
1192 params.regs[5-1+0x04] |= 0x04;
1193 /* turn off doubleword mode */
1194 params.regs[10+0x14] &= 0xbf;
1195 /* turn off word addressing */
1196 params.regs[10+0x17] |= 0x40;
1197 /* set logical screen width */
1198 params.regs[10+0x13] = 80;
1199 /* set 240 lines */
1200 params.regs[10+0x11] = 0x2c;
1201 params.regs[10+0x06] = 0x0d;
1202 params.regs[10+0x07] = 0x3e;
1203 params.regs[10+0x10] = 0xea;
1204 params.regs[10+0x11] = 0xac;
1205 params.regs[10+0x12] = 0xdf;
1206 params.regs[10+0x15] = 0xe7;
1207 params.regs[10+0x16] = 0x06;
1208 /* set vertical sync polarity to reflect aspect ratio */
1209 params.regs[9] = 0xe3;
1210 goto setup_grmode;
1212 case M_BG320: case M_CG320: case M_BG640:
1213 case M_CG320_D: case M_CG640_E:
1214 case M_CG640x350: case M_ENH_CG640:
1215 case M_BG640x480: case M_CG640x480: case M_VGA_CG320:
1217 setup_grmode:
1218 vga_load_state(adp, &params);
1219 break;
1221 default:
1222 return EINVAL;
1225 adp->va_mode = mode;
1226 info.vi_flags &= ~V_INFO_LINEAR; /* XXX */
1227 update_adapter_info(adp, &info);
1229 /* move hardware cursor out of the way */
1230 (*vidsw[adp->va_index]->set_hw_cursor)(adp, -1, -1);
1232 return 0;
1233 #else /* VGA_NO_MODE_CHANGE */
1234 return ENODEV;
1235 #endif /* VGA_NO_MODE_CHANGE */
1238 #ifndef VGA_NO_FONT_LOADING
1240 static void
1241 set_font_mode(video_adapter_t *adp, u_char *buf)
1243 crit_enter();
1245 /* save register values */
1246 outb(TSIDX, 0x02); buf[0] = inb(TSREG);
1247 outb(TSIDX, 0x04); buf[1] = inb(TSREG);
1248 outb(GDCIDX, 0x04); buf[2] = inb(GDCREG);
1249 outb(GDCIDX, 0x05); buf[3] = inb(GDCREG);
1250 outb(GDCIDX, 0x06); buf[4] = inb(GDCREG);
1251 inb(CRTC + 6);
1252 outb(ATC, 0x10); buf[5] = inb(ATC + 1);
1254 /* setup vga for loading fonts */
1255 inb(CRTC + 6); /* reset flip-flop */
1256 outb(ATC, 0x10); outb(ATC, buf[5] & ~0x01);
1257 inb(CRTC + 6); /* reset flip-flop */
1258 outb(ATC, 0x20); /* enable palette */
1260 #ifdef VGA_ALT_SEQACCESS
1261 outw(TSIDX, 0x0100);
1262 #endif
1263 outw(TSIDX, 0x0402);
1264 outw(TSIDX, 0x0704);
1265 #ifdef VGA_ALT_SEQACCESS
1266 outw(TSIDX, 0x0300);
1267 #endif
1268 outw(GDCIDX, 0x0204);
1269 outw(GDCIDX, 0x0005);
1270 outw(GDCIDX, 0x0406); /* addr = a0000, 64kb */
1272 crit_exit();
1275 static void
1276 set_normal_mode(video_adapter_t *adp, u_char *buf)
1278 crit_enter();
1280 /* setup vga for normal operation mode again */
1281 inb(CRTC + 6); /* reset flip-flop */
1282 outb(ATC, 0x10); outb(ATC, buf[5]);
1283 inb(CRTC + 6); /* reset flip-flop */
1284 outb(ATC, 0x20); /* enable palette */
1286 #ifdef VGA_ALT_SEQACCESS
1287 outw(TSIDX, 0x0100);
1288 #endif
1289 outw(TSIDX, 0x0002 | (buf[0] << 8));
1290 outw(TSIDX, 0x0004 | (buf[1] << 8));
1291 #ifdef VGA_ALT_SEQACCESS
1292 outw(TSIDX, 0x0300);
1293 #endif
1294 outw(GDCIDX, 0x0004 | (buf[2] << 8));
1295 outw(GDCIDX, 0x0005 | (buf[3] << 8));
1296 outw(GDCIDX, 0x0006 | (((buf[4] & 0x03) | 0x0c)<<8));
1298 crit_exit();
1301 #endif /* VGA_NO_FONT_LOADING */
1304 * save_font():
1305 * Read the font data in the requested font page from the video adapter.
1307 static int
1308 vga_save_font(video_adapter_t *adp, int page, int fontsize, u_char *data,
1309 int ch, int count)
1311 #ifndef VGA_NO_FONT_LOADING
1312 u_char buf[PARAM_BUFSIZE];
1313 vm_offset_t segment;
1314 int c;
1315 #ifdef VGA_ALT_SEQACCESS
1316 u_char val = 0;
1317 #endif
1319 prologue(adp, V_ADP_FONT, ENODEV);
1321 if (fontsize < 14) {
1322 /* FONT_8 */
1323 fontsize = 8;
1324 } else if (fontsize >= 32) {
1325 fontsize = 32;
1326 } else if (fontsize >= 16) {
1327 /* FONT_16 */
1328 fontsize = 16;
1329 } else {
1330 /* FONT_14 */
1331 fontsize = 14;
1334 if (page < 0 || page >= 8)
1335 return EINVAL;
1336 segment = FONT_BUF + 0x4000*page;
1337 if (page > 3)
1338 segment -= 0xe000;
1340 #ifdef VGA_ALT_SEQACCESS
1341 crit_enter();
1342 outb(TSIDX, 0x00); outb(TSREG, 0x01);
1343 outb(TSIDX, 0x01); val = inb(TSREG); /* disable screen */
1344 outb(TSIDX, 0x01); outb(TSREG, val | 0x20);
1345 outb(TSIDX, 0x00); outb(TSREG, 0x03);
1346 crit_exit();
1347 #endif
1349 set_font_mode(adp, buf);
1350 if (fontsize == 32) {
1351 bcopy_fromio((uintptr_t)segment + ch*32, data, fontsize*count);
1352 } else {
1353 for (c = ch; count > 0; ++c, --count) {
1354 bcopy_fromio((uintptr_t)segment + c*32, data, fontsize);
1355 data += fontsize;
1358 set_normal_mode(adp, buf);
1360 #ifdef VGA_ALT_SEQACCESS
1361 crit_enter();
1362 outb(TSIDX, 0x00); outb(TSREG, 0x01);
1363 outb(TSIDX, 0x01); outb(TSREG, val & 0xdf); /* enable screen */
1364 outb(TSIDX, 0x00); outb(TSREG, 0x03);
1365 crit_exit();
1366 #endif
1368 return 0;
1369 #else /* VGA_NO_FONT_LOADING */
1370 return ENODEV;
1371 #endif /* VGA_NO_FONT_LOADING */
1375 * load_font():
1376 * Set the font data in the requested font page.
1377 * NOTE: it appears that some recent video adapters do not support
1378 * the font page other than 0... XXX
1380 static int
1381 vga_load_font(video_adapter_t *adp, int page, int fontsize, u_char *data,
1382 int ch, int count)
1384 #ifndef VGA_NO_FONT_LOADING
1385 u_char buf[PARAM_BUFSIZE];
1386 vm_offset_t segment;
1387 int c;
1388 #ifdef VGA_ALT_SEQACCESS
1389 u_char val = 0;
1390 #endif
1392 prologue(adp, V_ADP_FONT, ENODEV);
1394 if (fontsize < 14) {
1395 /* FONT_8 */
1396 fontsize = 8;
1397 } else if (fontsize >= 32) {
1398 fontsize = 32;
1399 } else if (fontsize >= 16) {
1400 /* FONT_16 */
1401 fontsize = 16;
1402 } else {
1403 /* FONT_14 */
1404 fontsize = 14;
1407 if (page < 0 || page >= 8)
1408 return EINVAL;
1409 segment = FONT_BUF + 0x4000*page;
1410 if (page > 3)
1411 segment -= 0xe000;
1413 #ifdef VGA_ALT_SEQACCESS
1414 crit_enter();
1415 outb(TSIDX, 0x00); outb(TSREG, 0x01);
1416 outb(TSIDX, 0x01); val = inb(TSREG); /* disable screen */
1417 outb(TSIDX, 0x01); outb(TSREG, val | 0x20);
1418 outb(TSIDX, 0x00); outb(TSREG, 0x03);
1419 crit_exit();
1420 #endif
1422 set_font_mode(adp, buf);
1423 if (fontsize == 32) {
1424 bcopy_toio(data, (uintptr_t)segment + ch*32, fontsize*count);
1425 } else {
1426 for (c = ch; count > 0; ++c, --count) {
1427 bcopy_toio(data, (uintptr_t)segment + c*32, fontsize);
1428 data += fontsize;
1431 set_normal_mode(adp, buf);
1433 #ifdef VGA_ALT_SEQACCESS
1434 crit_enter();
1435 outb(TSIDX, 0x00); outb(TSREG, 0x01);
1436 outb(TSIDX, 0x01); outb(TSREG, val & 0xdf); /* enable screen */
1437 outb(TSIDX, 0x00); outb(TSREG, 0x03);
1438 crit_exit();
1439 #endif
1441 return 0;
1442 #else /* VGA_NO_FONT_LOADING */
1443 return ENODEV;
1444 #endif /* VGA_NO_FONT_LOADING */
1448 * show_font():
1449 * Activate the requested font page.
1450 * NOTE: it appears that some recent video adapters do not support
1451 * the font page other than 0... XXX
1453 static int
1454 vga_show_font(video_adapter_t *adp, int page)
1456 #ifndef VGA_NO_FONT_LOADING
1457 static u_char cg[] = { 0x00, 0x05, 0x0a, 0x0f, 0x30, 0x35, 0x3a, 0x3f };
1459 prologue(adp, V_ADP_FONT, ENODEV);
1460 if (page < 0 || page >= 8)
1461 return EINVAL;
1463 crit_enter();
1464 outb(TSIDX, 0x03); outb(TSREG, cg[page]);
1465 crit_exit();
1467 return 0;
1468 #else /* VGA_NO_FONT_LOADING */
1469 return ENODEV;
1470 #endif /* VGA_NO_FONT_LOADING */
1474 * save_palette():
1475 * Read DAC values. The values have expressed in 8 bits.
1477 * VGA
1479 static int
1480 vga_save_palette(video_adapter_t *adp, u_char *palette)
1482 int i;
1484 prologue(adp, V_ADP_PALETTE, ENODEV);
1487 * We store 8 bit values in the palette buffer, while the standard
1488 * VGA has 6 bit DAC .
1490 outb(PALRADR, 0x00);
1491 for (i = 0; i < 256*3; ++i)
1492 palette[i] = inb(PALDATA) << 2;
1493 inb(CRTC + 6); /* reset flip/flop */
1494 return 0;
1497 static int
1498 vga_save_palette2(video_adapter_t *adp, int base, int count,
1499 u_char *r, u_char *g, u_char *b)
1501 int i;
1503 prologue(adp, V_ADP_PALETTE, ENODEV);
1505 outb(PALRADR, base);
1506 for (i = 0; i < count; ++i) {
1507 r[i] = inb(PALDATA) << 2;
1508 g[i] = inb(PALDATA) << 2;
1509 b[i] = inb(PALDATA) << 2;
1511 inb(CRTC + 6); /* reset flip/flop */
1512 return 0;
1516 * load_palette():
1517 * Set DAC values.
1519 * VGA
1521 static int
1522 vga_load_palette(video_adapter_t *adp, const u_char *palette)
1524 int i;
1526 prologue(adp, V_ADP_PALETTE, ENODEV);
1528 outb(PIXMASK, 0xff); /* no pixelmask */
1529 outb(PALWADR, 0x00);
1530 for (i = 0; i < 256*3; ++i)
1531 outb(PALDATA, palette[i] >> 2);
1532 inb(CRTC + 6); /* reset flip/flop */
1533 outb(ATC, 0x20); /* enable palette */
1534 return 0;
1537 static int
1538 vga_load_palette2(video_adapter_t *adp, int base, int count,
1539 u_char *r, u_char *g, u_char *b)
1541 int i;
1543 prologue(adp, V_ADP_PALETTE, ENODEV);
1545 outb(PIXMASK, 0xff); /* no pixelmask */
1546 outb(PALWADR, base);
1547 for (i = 0; i < count; ++i) {
1548 outb(PALDATA, r[i] >> 2);
1549 outb(PALDATA, g[i] >> 2);
1550 outb(PALDATA, b[i] >> 2);
1552 inb(CRTC + 6); /* reset flip/flop */
1553 outb(ATC, 0x20); /* enable palette */
1554 return 0;
1558 * set_border():
1559 * Change the border color.
1561 static int
1562 vga_set_border(video_adapter_t *adp, int color)
1564 prologue(adp, V_ADP_BORDER, ENODEV);
1566 inb(CRTC + 6); /* reset flip-flop */
1567 outb(ATC, 0x31); outb(ATC, color & 0xff);
1569 return 0;
1573 * save_state():
1574 * Read video register values.
1575 * NOTE: this function only reads the standard VGA registers.
1576 * any extra/extended registers of SVGA adapters are not saved.
1578 static int
1579 vga_save_state(video_adapter_t *adp, void *p, size_t size)
1581 video_info_t info;
1582 u_char *buf;
1583 int crtc_addr;
1584 int i, j;
1586 if (size == 0) {
1587 /* return the required buffer size */
1588 prologue(adp, V_ADP_STATESAVE, 0);
1589 return sizeof(adp_state_t);
1590 } else {
1591 prologue(adp, V_ADP_STATESAVE, ENODEV);
1592 if (size < sizeof(adp_state_t))
1593 return EINVAL;
1595 ((adp_state_t *)p)->sig = V_STATE_SIG;
1596 buf = ((adp_state_t *)p)->regs;
1597 bzero(buf, V_MODE_PARAM_SIZE);
1598 crtc_addr = CRTC;
1600 crit_enter();
1602 outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */
1603 for (i = 0, j = 5; i < 4; i++) {
1604 outb(TSIDX, i + 1);
1605 buf[j++] = inb(TSREG);
1607 buf[9] = inb(MISC + 10); /* dot-clock */
1608 outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */
1610 for (i = 0, j = 10; i < 25; i++) { /* crtc */
1611 outb(crtc_addr, i);
1612 buf[j++] = inb(crtc_addr + 1);
1614 for (i = 0, j = 35; i < 20; i++) { /* attribute ctrl */
1615 inb(crtc_addr + 6); /* reset flip-flop */
1616 outb(ATC, i);
1617 buf[j++] = inb(ATC + 1);
1619 for (i = 0, j = 55; i < 9; i++) { /* graph data ctrl */
1620 outb(GDCIDX, i);
1621 buf[j++] = inb(GDCREG);
1623 inb(crtc_addr + 6); /* reset flip-flop */
1624 outb(ATC, 0x20); /* enable palette */
1626 crit_exit();
1628 #if 1
1629 if (vga_get_info(adp, adp->va_mode, &info) == 0) {
1630 if (info.vi_flags & V_INFO_GRAPHICS) {
1631 buf[0] = info.vi_width/info.vi_cwidth; /* COLS */
1632 buf[1] = info.vi_height/info.vi_cheight - 1; /* ROWS */
1633 } else {
1634 buf[0] = info.vi_width; /* COLS */
1635 buf[1] = info.vi_height - 1; /* ROWS */
1637 buf[2] = info.vi_cheight; /* POINTS */
1638 } else {
1639 /* XXX: shouldn't be happening... */
1640 kprintf("vga%d: %s: failed to obtain mode info. (vga_save_state())\n",
1641 adp->va_unit, adp->va_name);
1643 #else
1644 buf[0] = readb(BIOS_PADDRTOVADDR(0x44a)); /* COLS */
1645 buf[1] = readb(BIOS_PADDRTOVADDR(0x484)); /* ROWS */
1646 buf[2] = readb(BIOS_PADDRTOVADDR(0x485)); /* POINTS */
1647 buf[3] = readb(BIOS_PADDRTOVADDR(0x44c));
1648 buf[4] = readb(BIOS_PADDRTOVADDR(0x44d));
1649 #endif
1651 return 0;
1655 * load_state():
1656 * Set video registers at once.
1657 * NOTE: this function only updates the standard VGA registers.
1658 * any extra/extended registers of SVGA adapters are not changed.
1660 static int
1661 vga_load_state(video_adapter_t *adp, void *p)
1663 u_char *buf;
1664 int crtc_addr;
1665 int i;
1667 prologue(adp, V_ADP_STATELOAD, ENODEV);
1668 if (((adp_state_t *)p)->sig != V_STATE_SIG)
1669 return EINVAL;
1671 buf = ((adp_state_t *)p)->regs;
1672 crtc_addr = CRTC;
1674 #if VGA_DEBUG > 1
1675 dump_buffer(buf, V_MODE_PARAM_SIZE);
1676 #endif
1678 crit_enter();
1680 outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */
1681 for (i = 0; i < 4; ++i) { /* program sequencer */
1682 outb(TSIDX, i + 1);
1683 outb(TSREG, buf[i + 5]);
1685 outb(MISC, buf[9]); /* set dot-clock */
1686 outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */
1687 outb(crtc_addr, 0x11);
1688 outb(crtc_addr + 1, inb(crtc_addr + 1) & 0x7F);
1689 for (i = 0; i < 25; ++i) { /* program crtc */
1690 outb(crtc_addr, i);
1691 outb(crtc_addr + 1, buf[i + 10]);
1693 inb(crtc_addr+6); /* reset flip-flop */
1694 for (i = 0; i < 20; ++i) { /* program attribute ctrl */
1695 outb(ATC, i);
1696 outb(ATC, buf[i + 35]);
1698 for (i = 0; i < 9; ++i) { /* program graph data ctrl */
1699 outb(GDCIDX, i);
1700 outb(GDCREG, buf[i + 55]);
1702 inb(crtc_addr + 6); /* reset flip-flop */
1703 outb(ATC, 0x20); /* enable palette */
1705 #if notyet /* a temporary workaround for kernel panic, XXX */
1706 #ifndef VGA_NO_BIOS
1707 if (adp->va_unit == V_ADP_PRIMARY) {
1708 writeb(BIOS_PADDRTOVADDR(0x44a), buf[0]); /* COLS */
1709 writeb(BIOS_PADDRTOVADDR(0x484), buf[1] + rows_offset - 1); /* ROWS */
1710 writeb(BIOS_PADDRTOVADDR(0x485), buf[2]); /* POINTS */
1711 #if 0
1712 writeb(BIOS_PADDRTOVADDR(0x44c), buf[3]);
1713 writeb(BIOS_PADDRTOVADDR(0x44d), buf[4]);
1714 #endif
1716 #endif /* VGA_NO_BIOS */
1717 #endif /* notyet */
1719 crit_exit();
1720 return 0;
1724 * set_origin():
1725 * Change the origin (window mapping) of the banked frame buffer.
1727 static int
1728 vga_set_origin(video_adapter_t *adp, off_t offset)
1731 * The standard video modes do not require window mapping;
1732 * always return error.
1734 return ENODEV;
1738 * read_hw_cursor():
1739 * Read the position of the hardware text cursor.
1741 static int
1742 vga_read_hw_cursor(video_adapter_t *adp, int *col, int *row)
1744 u_int16_t off;
1746 if (!vga_init_done)
1747 return ENXIO;
1749 if (adp->va_info.vi_flags & V_INFO_GRAPHICS)
1750 return ENODEV;
1752 crit_enter();
1753 outb(CRTC, 14);
1754 off = inb(CRTC + 1);
1755 outb(CRTC, 15);
1756 off = (off << 8) | inb(CRTC + 1);
1757 crit_exit();
1759 *row = off / adp->va_info.vi_width;
1760 *col = off % adp->va_info.vi_width;
1762 return 0;
1766 * set_hw_cursor():
1767 * Move the hardware text cursor. If col and row are both -1,
1768 * the cursor won't be shown.
1770 static int
1771 vga_set_hw_cursor(video_adapter_t *adp, int col, int row)
1773 u_int16_t off;
1775 if (!vga_init_done)
1776 return ENXIO;
1778 if ((col == -1) && (row == -1)) {
1779 off = -1;
1780 } else {
1781 if (adp->va_info.vi_flags & V_INFO_GRAPHICS)
1782 return ENODEV;
1783 off = row*adp->va_info.vi_width + col;
1786 crit_enter();
1787 outb(CRTC, 14);
1788 outb(CRTC + 1, off >> 8);
1789 outb(CRTC, 15);
1790 outb(CRTC + 1, off & 0x00ff);
1791 crit_exit();
1793 return 0;
1797 * set_hw_cursor_shape():
1798 * Change the shape of the hardware text cursor. If the height is
1799 * zero or negative, the cursor won't be shown.
1801 static int
1802 vga_set_hw_cursor_shape(video_adapter_t *adp, int base, int height,
1803 int celsize, int blink)
1805 if (!vga_init_done)
1806 return ENXIO;
1808 crit_enter();
1809 if (height <= 0) {
1810 /* make the cursor invisible */
1811 outb(CRTC, 10);
1812 outb(CRTC + 1, 32);
1813 outb(CRTC, 11);
1814 outb(CRTC + 1, 0);
1815 } else {
1816 outb(CRTC, 10);
1817 outb(CRTC + 1, celsize - base - height);
1818 outb(CRTC, 11);
1819 outb(CRTC + 1, celsize - base - 1);
1821 crit_exit();
1823 return 0;
1827 * blank_display()
1828 * Put the display in power save/power off mode.
1830 static int
1831 vga_blank_display(video_adapter_t *adp, int mode)
1833 u_char val;
1835 crit_enter();
1836 switch (mode) {
1837 case V_DISPLAY_SUSPEND:
1838 case V_DISPLAY_STAND_BY:
1839 outb(TSIDX, 0x01);
1840 val = inb(TSREG);
1841 outb(TSIDX, 0x01);
1842 outb(TSREG, val | 0x20);
1843 outb(CRTC, 0x17);
1844 val = inb(CRTC + 1);
1845 outb(CRTC + 1, val & ~0x80);
1846 break;
1847 case V_DISPLAY_OFF:
1848 outb(TSIDX, 0x01);
1849 val = inb(TSREG);
1850 outb(TSIDX, 0x01);
1851 outb(TSREG, val | 0x20);
1852 break;
1853 case V_DISPLAY_ON:
1854 outb(TSIDX, 0x01);
1855 val = inb(TSREG);
1856 outb(TSIDX, 0x01);
1857 outb(TSREG, val & 0xDF);
1858 outb(CRTC, 0x17);
1859 val = inb(CRTC + 1);
1860 outb(CRTC + 1, val | 0x80);
1861 break;
1863 crit_exit();
1865 return 0;
1869 * mmap():
1870 * Mmap frame buffer.
1872 static int
1873 vga_mmap_buf(video_adapter_t *adp, vm_offset_t offset, int prot)
1875 if (adp->va_info.vi_flags & V_INFO_LINEAR)
1876 return -1;
1878 #if VGA_DEBUG > 0
1879 kprintf("vga_mmap_buf(): window:0x%x, offset:%p\n",
1880 adp->va_info.vi_window, (void *)offset);
1881 #endif
1883 /* XXX: is this correct? */
1884 if (offset > adp->va_window_size - PAGE_SIZE)
1885 return -1;
1887 #if defined(__i386__)
1888 return i386_btop(adp->va_info.vi_window + offset);
1889 #elif defined(__x86_64__)
1890 return x86_64_btop(adp->va_info.vi_window + offset);
1891 #else
1892 #error "vga_mmap_buf needs to return something"
1893 #endif
1896 #ifndef VGA_NO_MODE_CHANGE
1898 static void
1899 planar_fill(video_adapter_t *adp, int val)
1901 int length;
1902 int at; /* position in the frame buffer */
1903 int l;
1905 outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */
1906 outw(GDCIDX, 0x0003); /* data rotate/function select */
1907 outw(GDCIDX, 0x0f01); /* set/reset enable */
1908 outw(GDCIDX, 0xff08); /* bit mask */
1909 outw(GDCIDX, (val << 8) | 0x00); /* set/reset */
1910 at = 0;
1911 length = adp->va_line_width*adp->va_info.vi_height;
1912 while (length > 0) {
1913 l = imin(length, adp->va_window_size);
1914 (*vidsw[adp->va_index]->set_win_org)(adp, at);
1915 bzero_io(adp->va_window, l);
1916 length -= l;
1917 at += l;
1919 outw(GDCIDX, 0x0000); /* set/reset */
1920 outw(GDCIDX, 0x0001); /* set/reset enable */
1923 static void
1924 packed_fill(video_adapter_t *adp, int val)
1926 int length;
1927 int at; /* position in the frame buffer */
1928 int l;
1930 at = 0;
1931 length = adp->va_line_width*adp->va_info.vi_height;
1932 while (length > 0) {
1933 l = imin(length, adp->va_window_size);
1934 (*vidsw[adp->va_index]->set_win_org)(adp, at);
1935 fill_io(val, adp->va_window, l);
1936 length -= l;
1937 at += l;
1941 static void
1942 direct_fill(video_adapter_t *adp, int val)
1944 int length;
1945 int at; /* position in the frame buffer */
1946 int l;
1948 at = 0;
1949 length = adp->va_line_width*adp->va_info.vi_height;
1950 while (length > 0) {
1951 l = imin(length, adp->va_window_size);
1952 (*vidsw[adp->va_index]->set_win_org)(adp, at);
1953 switch (adp->va_info.vi_pixel_size) {
1954 case sizeof(u_int16_t):
1955 fillw_io(val, adp->va_window, l/sizeof(u_int16_t));
1956 break;
1957 case 3:
1958 /* FIXME */
1959 break;
1960 case sizeof(u_int32_t):
1961 filll_io(val, adp->va_window, l/sizeof(u_int32_t));
1962 break;
1964 length -= l;
1965 at += l;
1969 static int
1970 vga_clear(video_adapter_t *adp)
1972 switch (adp->va_info.vi_mem_model) {
1973 case V_INFO_MM_TEXT:
1974 /* do nothing? XXX */
1975 break;
1976 case V_INFO_MM_PLANAR:
1977 planar_fill(adp, 0);
1978 break;
1979 case V_INFO_MM_PACKED:
1980 packed_fill(adp, 0);
1981 break;
1982 case V_INFO_MM_DIRECT:
1983 direct_fill(adp, 0);
1984 break;
1986 return 0;
1989 #ifdef notyet
1990 static void
1991 planar_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
1993 int banksize;
1994 int bank;
1995 int pos;
1996 int offset; /* offset within window */
1997 int bx;
1998 int l;
2000 outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */
2001 outw(GDCIDX, 0x0003); /* data rotate/function select */
2002 outw(GDCIDX, 0x0f01); /* set/reset enable */
2003 outw(GDCIDX, 0xff08); /* bit mask */
2004 outw(GDCIDX, (val << 8) | 0x00); /* set/reset */
2006 banksize = adp->va_window_size;
2007 bank = -1;
2008 while (cy > 0) {
2009 pos = adp->va_line_width*y + x/8;
2010 if (bank != pos/banksize) {
2011 (*vidsw[adp->va_index]->set_win_org)(adp, pos);
2012 bank = pos/banksize;
2014 offset = pos%banksize;
2015 bx = (x + cx)/8 - x/8;
2016 if (x % 8) {
2017 outw(GDCIDX, ((0xff00 >> (x % 8)) & 0xff00) | 0x08);
2018 writeb(adp->va_window + offset, 0);
2019 ++offset;
2020 --bx;
2021 if (offset >= banksize) {
2022 offset = 0;
2023 ++bank; /* next bank */
2024 (*vidsw[adp->va_index]->set_win_org)(adp, bank*banksize);
2026 outw(GDCIDX, 0xff08); /* bit mask */
2028 while (bx > 0) {
2029 l = imin(bx, banksize);
2030 bzero_io(adp->va_window + offset, l);
2031 offset += l;
2032 bx -= l;
2033 if (offset >= banksize) {
2034 offset = 0;
2035 ++bank; /* next bank */
2036 (*vidsw[adp->va_index]->set_win_org)(adp, bank*banksize);
2039 if ((x + cx) % 8) {
2040 outw(GDCIDX, (~(0xff00 >> ((x + cx) % 8)) & 0xff00) | 0x08);
2041 writeb(adp->va_window + offset, 0);
2042 ++offset;
2043 if (offset >= banksize) {
2044 offset = 0;
2045 ++bank; /* next bank */
2046 (*vidsw[adp->va_index]->set_win_org)(adp, bank*banksize);
2048 outw(GDCIDX, 0xff08); /* bit mask */
2050 ++y;
2051 --cy;
2054 outw(GDCIDX, 0xff08); /* bit mask */
2055 outw(GDCIDX, 0x0000); /* set/reset */
2056 outw(GDCIDX, 0x0001); /* set/reset enable */
2059 static void
2060 packed_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
2062 int banksize;
2063 int bank;
2064 int pos;
2065 int offset; /* offset within window */
2066 int end;
2068 banksize = adp->va_window_size;
2069 bank = -1;
2070 cx *= adp->va_info.vi_pixel_size;
2071 while (cy > 0) {
2072 pos = adp->va_line_width*y + x*adp->va_info.vi_pixel_size;
2073 if (bank != pos/banksize) {
2074 (*vidsw[adp->va_index]->set_win_org)(adp, pos);
2075 bank = pos/banksize;
2077 offset = pos%banksize;
2078 end = imin(offset + cx, banksize);
2079 fill_io(val, adp->va_window + offset,
2080 (end - offset)/adp->va_info.vi_pixel_size);
2081 /* the line may cross the window boundary */
2082 if (offset + cx > banksize) {
2083 ++bank; /* next bank */
2084 (*vidsw[adp->va_index]->set_win_org)(adp, bank*banksize);
2085 end = offset + cx - banksize;
2086 fill_io(val, adp->va_window, end/adp->va_info.vi_pixel_size);
2088 ++y;
2089 --cy;
2093 static void
2094 direct_fill_rect16(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
2096 int banksize;
2097 int bank;
2098 int pos;
2099 int offset; /* offset within window */
2100 int end;
2103 * XXX: the function assumes that banksize is a muliple of
2104 * sizeof(u_int16_t).
2106 banksize = adp->va_window_size;
2107 bank = -1;
2108 cx *= sizeof(u_int16_t);
2109 while (cy > 0) {
2110 pos = adp->va_line_width*y + x*sizeof(u_int16_t);
2111 if (bank != pos/banksize) {
2112 (*vidsw[adp->va_index]->set_win_org)(adp, pos);
2113 bank = pos/banksize;
2115 offset = pos%banksize;
2116 end = imin(offset + cx, banksize);
2117 fillw_io(val, adp->va_window + offset,
2118 (end - offset)/sizeof(u_int16_t));
2119 /* the line may cross the window boundary */
2120 if (offset + cx > banksize) {
2121 ++bank; /* next bank */
2122 (*vidsw[adp->va_index]->set_win_org)(adp, bank*banksize);
2123 end = offset + cx - banksize;
2124 fillw_io(val, adp->va_window, end/sizeof(u_int16_t));
2126 ++y;
2127 --cy;
2131 static void
2132 direct_fill_rect24(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
2134 int banksize;
2135 int bank;
2136 int pos;
2137 int offset; /* offset within window */
2138 int end;
2139 int i;
2140 int j;
2141 u_int8_t b[3];
2143 b[0] = val & 0x0000ff;
2144 b[1] = (val >> 8) & 0x0000ff;
2145 b[2] = (val >> 16) & 0x0000ff;
2146 banksize = adp->va_window_size;
2147 bank = -1;
2148 cx *= 3;
2149 while (cy > 0) {
2150 pos = adp->va_line_width*y + x*3;
2151 if (bank != pos/banksize) {
2152 (*vidsw[adp->va_index]->set_win_org)(adp, pos);
2153 bank = pos/banksize;
2155 offset = pos%banksize;
2156 end = imin(offset + cx, banksize);
2157 for (i = 0, j = offset; j < end; i = (++i)%3, ++j) {
2158 writeb(adp->va_window + j, b[i]);
2160 /* the line may cross the window boundary */
2161 if (offset + cx >= banksize) {
2162 ++bank; /* next bank */
2163 (*vidsw[adp->va_index]->set_win_org)(adp, bank*banksize);
2164 j = 0;
2165 end = offset + cx - banksize;
2166 for (; j < end; i = (++i)%3, ++j) {
2167 writeb(adp->va_window + j, b[i]);
2170 ++y;
2171 --cy;
2175 static void
2176 direct_fill_rect32(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
2178 int banksize;
2179 int bank;
2180 int pos;
2181 int offset; /* offset within window */
2182 int end;
2185 * XXX: the function assumes that banksize is a muliple of
2186 * sizeof(u_int32_t).
2188 banksize = adp->va_window_size;
2189 bank = -1;
2190 cx *= sizeof(u_int32_t);
2191 while (cy > 0) {
2192 pos = adp->va_line_width*y + x*sizeof(u_int32_t);
2193 if (bank != pos/banksize) {
2194 (*vidsw[adp->va_index]->set_win_org)(adp, pos);
2195 bank = pos/banksize;
2197 offset = pos%banksize;
2198 end = imin(offset + cx, banksize);
2199 filll_io(val, adp->va_window + offset,
2200 (end - offset)/sizeof(u_int32_t));
2201 /* the line may cross the window boundary */
2202 if (offset + cx > banksize) {
2203 ++bank; /* next bank */
2204 (*vidsw[adp->va_index]->set_win_org)(adp, bank*banksize);
2205 end = offset + cx - banksize;
2206 filll_io(val, adp->va_window, end/sizeof(u_int32_t));
2208 ++y;
2209 --cy;
2213 static int
2214 vga_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
2216 switch (adp->va_info.vi_mem_model) {
2217 case V_INFO_MM_TEXT:
2218 /* do nothing? XXX */
2219 break;
2220 case V_INFO_MM_PLANAR:
2221 planar_fill_rect(adp, val, x, y, cx, cy);
2222 break;
2223 case V_INFO_MM_PACKED:
2224 packed_fill_rect(adp, val, x, y, cx, cy);
2225 break;
2226 case V_INFO_MM_DIRECT:
2227 switch (adp->va_info.vi_pixel_size) {
2228 case sizeof(u_int16_t):
2229 direct_fill_rect16(adp, val, x, y, cx, cy);
2230 break;
2231 case 3:
2232 direct_fill_rect24(adp, val, x, y, cx, cy);
2233 break;
2234 case sizeof(u_int32_t):
2235 direct_fill_rect32(adp, val, x, y, cx, cy);
2236 break;
2238 break;
2240 return 0;
2242 #else /* !notyet */
2243 static int
2244 vga_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
2246 return ENODEV;
2248 #endif /* notyet */
2250 static int
2251 vga_bitblt(video_adapter_t *adp,...)
2253 /* FIXME */
2254 return ENODEV;
2257 #endif /* !VGA_NO_MODE_CHANGE */
2259 static int
2260 get_palette(video_adapter_t *adp, int base, int count,
2261 u_char *red, u_char *green, u_char *blue, u_char *trans)
2263 u_char *r;
2264 u_char *g;
2265 u_char *b;
2267 if (count < 0 || base < 0 || count > 256 || base > 256 ||
2268 base + count > 256)
2269 return EINVAL;
2271 r = kmalloc(count*3, M_DEVBUF, M_WAITOK);
2272 g = r + count;
2273 b = g + count;
2274 if (vga_save_palette2(adp, base, count, r, g, b)) {
2275 kfree(r, M_DEVBUF);
2276 return ENODEV;
2278 copyout(r, red, count);
2279 copyout(g, green, count);
2280 copyout(b, blue, count);
2281 if (trans != NULL) {
2282 bzero(r, count);
2283 copyout(r, trans, count);
2285 kfree(r, M_DEVBUF);
2287 return 0;
2290 static int
2291 set_palette(video_adapter_t *adp, int base, int count,
2292 u_char *red, u_char *green, u_char *blue, u_char *trans)
2294 u_char *r;
2295 u_char *g;
2296 u_char *b;
2297 int err;
2299 if (count < 0 || base < 0 || count > 256 || base > 256 ||
2300 base + count > 256)
2301 return EINVAL;
2303 r = kmalloc(count*3, M_DEVBUF, M_WAITOK);
2304 g = r + count;
2305 b = g + count;
2306 err = copyin(red, r, count);
2307 if (!err)
2308 err = copyin(green, g, count);
2309 if (!err)
2310 err = copyin(blue, b, count);
2311 if (!err)
2312 err = vga_load_palette2(adp, base, count, r, g, b);
2313 kfree(r, M_DEVBUF);
2315 return (err ? ENODEV : 0);
2318 static int
2319 vga_dev_ioctl(video_adapter_t *adp, u_long cmd, caddr_t arg)
2321 switch (cmd) {
2322 case FBIO_GETWINORG: /* get frame buffer window origin */
2323 *(u_int *)arg = 0;
2324 return 0;
2326 case FBIO_SETWINORG: /* set frame buffer window origin */
2327 return ENODEV;
2329 case FBIO_SETDISPSTART: /* set display start address */
2330 return (set_display_start(adp,
2331 ((video_display_start_t *)arg)->x,
2332 ((video_display_start_t *)arg)->y)
2333 ? ENODEV : 0);
2335 case FBIO_SETLINEWIDTH: /* set scan line length in pixel */
2336 return (set_line_length(adp, *(u_int *)arg) ? ENODEV : 0);
2338 case FBIO_GETPALETTE: /* get color palette */
2339 return get_palette(adp, ((video_color_palette_t *)arg)->index,
2340 ((video_color_palette_t *)arg)->count,
2341 ((video_color_palette_t *)arg)->red,
2342 ((video_color_palette_t *)arg)->green,
2343 ((video_color_palette_t *)arg)->blue,
2344 ((video_color_palette_t *)arg)->transparent);
2346 case FBIO_SETPALETTE: /* set color palette */
2347 return set_palette(adp, ((video_color_palette_t *)arg)->index,
2348 ((video_color_palette_t *)arg)->count,
2349 ((video_color_palette_t *)arg)->red,
2350 ((video_color_palette_t *)arg)->green,
2351 ((video_color_palette_t *)arg)->blue,
2352 ((video_color_palette_t *)arg)->transparent);
2354 case FBIOGTYPE: /* get frame buffer type info. */
2355 ((struct fbtype *)arg)->fb_type = fb_type(adp->va_type);
2356 ((struct fbtype *)arg)->fb_height = adp->va_info.vi_height;
2357 ((struct fbtype *)arg)->fb_width = adp->va_info.vi_width;
2358 ((struct fbtype *)arg)->fb_depth = adp->va_info.vi_depth;
2359 if ((adp->va_info.vi_depth <= 1) || (adp->va_info.vi_depth > 8))
2360 ((struct fbtype *)arg)->fb_cmsize = 0;
2361 else
2362 ((struct fbtype *)arg)->fb_cmsize = 1 << adp->va_info.vi_depth;
2363 ((struct fbtype *)arg)->fb_size = adp->va_buffer_size;
2364 return 0;
2366 case FBIOGETCMAP: /* get color palette */
2367 return get_palette(adp, ((struct fbcmap *)arg)->index,
2368 ((struct fbcmap *)arg)->count,
2369 ((struct fbcmap *)arg)->red,
2370 ((struct fbcmap *)arg)->green,
2371 ((struct fbcmap *)arg)->blue, NULL);
2373 case FBIOPUTCMAP: /* set color palette */
2374 return set_palette(adp, ((struct fbcmap *)arg)->index,
2375 ((struct fbcmap *)arg)->count,
2376 ((struct fbcmap *)arg)->red,
2377 ((struct fbcmap *)arg)->green,
2378 ((struct fbcmap *)arg)->blue, NULL);
2380 default:
2381 return fb_commonioctl(adp, cmd, arg);
2385 static void
2386 dump_buffer(u_char *buf, size_t len)
2388 int i;
2390 for(i = 0; i < len;) {
2391 kprintf("%02x ", buf[i]);
2392 if ((++i % 16) == 0)
2393 kprintf("\n");
2398 * diag():
2399 * Print some information about the video adapter and video modes,
2400 * with requested level of details.
2402 static int
2403 vga_diag(video_adapter_t *adp, int level)
2405 u_char *mp;
2406 #if FB_DEBUG > 1
2407 video_info_t info;
2408 int i;
2409 #endif
2411 if (!vga_init_done)
2412 return ENXIO;
2414 #if FB_DEBUG > 1
2415 #ifndef VGA_NO_BIOS
2416 kprintf("vga: DCC code:0x%02x\n",
2417 readb(BIOS_PADDRTOVADDR(0x488)));
2418 kprintf("vga: CRTC:0x%x, video option:0x%02x, ",
2419 readw(BIOS_PADDRTOVADDR(0x463)),
2420 readb(BIOS_PADDRTOVADDR(0x487)));
2421 kprintf("rows:%d, cols:%d, font height:%d\n",
2422 readb(BIOS_PADDRTOVADDR(0x44a)),
2423 readb(BIOS_PADDRTOVADDR(0x484)) + 1,
2424 readb(BIOS_PADDRTOVADDR(0x485)));
2425 #endif /* VGA_NO_BIOS */
2426 #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
2427 kprintf("vga: param table:%p\n", video_mode_ptr);
2428 kprintf("vga: rows_offset:%d\n", rows_offset);
2429 #endif
2430 #endif /* FB_DEBUG > 1 */
2432 fb_dump_adp_info(VGA_DRIVER_NAME, adp, level);
2434 #if FB_DEBUG > 1
2435 if (adp->va_flags & V_ADP_MODECHANGE) {
2436 for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) {
2437 if (bios_vmode[i].vi_mode == NA)
2438 continue;
2439 if (get_mode_param(bios_vmode[i].vi_mode) == NULL)
2440 continue;
2441 fb_dump_mode_info(VGA_DRIVER_NAME, adp, &bios_vmode[i], level);
2443 } else {
2444 vga_get_info(adp, adp->va_initial_mode, &info); /* shouldn't fail */
2445 fb_dump_mode_info(VGA_DRIVER_NAME, adp, &info, level);
2447 #endif /* FB_DEBUG > 1 */
2449 #if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
2450 if (video_mode_ptr == NULL)
2451 kprintf("vga%d: %s: WARNING: video mode switching is not "
2452 "fully supported on this adapter\n",
2453 adp->va_unit, adp->va_name);
2454 #endif
2455 if (level <= 0)
2456 return 0;
2458 kprintf("VGA parameters upon power-up\n");
2459 dump_buffer(adpstate.regs, sizeof(adpstate.regs));
2460 kprintf("VGA parameters in BIOS for mode %d\n", adp->va_initial_mode);
2461 dump_buffer(adpstate2.regs, sizeof(adpstate2.regs));
2463 mp = get_mode_param(adp->va_initial_mode);
2464 if (mp == NULL) /* this shouldn't be happening */
2465 return 0;
2466 kprintf("VGA parameters to be used for mode %d\n", adp->va_initial_mode);
2467 dump_buffer(mp, V_MODE_PARAM_SIZE);
2469 return 0;