1367466Sdim/*-
2260684Skaiw * Copyright (c) 1999 FreeBSD(98) port team.
3260684Skaiw * All rights reserved.
4260684Skaiw *
5260684Skaiw * Redistribution and use in source and binary forms, with or without
6260684Skaiw * modification, are permitted provided that the following conditions
7260684Skaiw * are met:
8260684Skaiw * 1. Redistributions of source code must retain the above copyright
9260684Skaiw *    notice, this list of conditions and the following disclaimer as
10260684Skaiw *    the first lines of this file unmodified.
11260684Skaiw * 2. Redistributions in binary form must reproduce the above copyright
12260684Skaiw *    notice, this list of conditions and the following disclaimer in the
13260684Skaiw *    documentation and/or other materials provided with the distribution.
14260684Skaiw * 3. The name of the author may not be used to endorse or promote products
15260684Skaiw *    derived from this software without specific prior written permission.
16260684Skaiw *
17260684Skaiw * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18260684Skaiw * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19260684Skaiw * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20260684Skaiw * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21260684Skaiw * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22260684Skaiw * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23260684Skaiw * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24367466Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25260684Skaiw * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26367466Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27367466Sdim *
28260684Skaiw * $FreeBSD$
29260684Skaiw */
30260684Skaiw
31260684Skaiw#include "opt_gdc.h"
32260684Skaiw#include "opt_fb.h"
33260684Skaiw#include "opt_syscons.h"
34260684Skaiw
35260684Skaiw#include <sys/param.h>
36260684Skaiw#include <sys/systm.h>
37260684Skaiw#include <sys/kernel.h>
38260684Skaiw#include <sys/module.h>
39367466Sdim#include <sys/conf.h>
40260684Skaiw#include <sys/bus.h>
41367466Sdim#include <machine/bus.h>
42260684Skaiw#include <sys/rman.h>
43367466Sdim#include <machine/resource.h>
44260684Skaiw
45367466Sdim#include <sys/fbio.h>
46260684Skaiw#include <sys/fcntl.h>
47260684Skaiw
48260684Skaiw#include <vm/vm.h>
49260684Skaiw#include <vm/pmap.h>
50260684Skaiw#include <vm/vm_param.h>
51260684Skaiw
52367466Sdim#include <machine/md_var.h>
53260684Skaiw#include <machine/pc/bios.h>
54260684Skaiw
55260684Skaiw#include <dev/fb/fbreg.h>
56260684Skaiw
57260684Skaiw#ifdef LINE30
58260684Skaiw#include <pc98/cbus/cbus.h>
59367466Sdim#endif
60260684Skaiw#include <pc98/pc98/pc98_machdep.h>
61260684Skaiw#include <isa/isavar.h>
62260684Skaiw
63260684Skaiw#define	TEXT_GDC	0x60
64367466Sdim#define	GRAPHIC_GDC	0xa0
65260684Skaiw#define	ROW		25
66260684Skaiw#define	COL		80
67367466Sdim
68367466Sdim#define DRIVER_NAME	"gdc"
69367466Sdim
70367466Sdim/* cdev driver declaration */
71367466Sdim
72260684Skaiw#define GDC_UNIT(dev)	dev2unit(dev)
73260684Skaiw#define GDC_MKMINOR(unit) (unit)
74260684Skaiw
75260684Skaiwtypedef struct gdc_softc {
76260684Skaiw	video_adapter_t	*adp;
77260684Skaiw	struct resource *res_tgdc, *res_ggdc;
78260684Skaiw	struct resource *res_egc, *res_pegc, *res_grcg, *res_kcg;
79260684Skaiw	struct resource *res_tmem, *res_gmem1, *res_gmem2;
80260684Skaiw#ifdef FB_INSTALL_CDEV
81260684Skaiw	genfb_softc_t gensc;
82260684Skaiw#endif
83260684Skaiw} gdc_softc_t;
84260684Skaiw
85260684Skaiw#define GDC_SOFTC(unit)	\
86260684Skaiw	((gdc_softc_t *)devclass_get_softc(gdc_devclass, unit))
87260684Skaiw
88260684Skaiwstatic bus_addr_t	gdc_iat[] = {0, 2, 4, 6, 8, 10, 12, 14};
89260684Skaiw
90260684Skaiwstatic devclass_t	gdc_devclass;
91260684Skaiw
92260684Skaiwstatic int		gdc_probe_unit(int unit, gdc_softc_t *sc, int flags);
93260684Skaiwstatic int		gdc_attach_unit(int unit, gdc_softc_t *sc, int flags);
94260684Skaiwstatic int		gdc_alloc_resource(device_t dev);
95260684Skaiwstatic int		gdc_release_resource(device_t dev);
96260684Skaiw
97260684Skaiw#ifdef FB_INSTALL_CDEV
98260684Skaiw
99260684Skaiwstatic d_open_t		gdcopen;
100260684Skaiwstatic d_close_t	gdcclose;
101260684Skaiwstatic d_read_t		gdcread;
102260684Skaiwstatic d_write_t	gdcwrite;
103260684Skaiwstatic d_ioctl_t	gdcioctl;
104260684Skaiwstatic d_mmap_t		gdcmmap;
105260684Skaiw
106260684Skaiwstatic struct cdevsw gdc_cdevsw = {
107260684Skaiw	.d_version =	D_VERSION,
108260684Skaiw	.d_flags =	D_NEEDGIANT,
109260684Skaiw	.d_open =	gdcopen,
110260684Skaiw	.d_close =	gdcclose,
111260684Skaiw	.d_read =	gdcread,
112260684Skaiw	.d_write =	gdcwrite,
113260684Skaiw	.d_ioctl =	gdcioctl,
114260684Skaiw	.d_mmap =	gdcmmap,
115260684Skaiw	.d_name =	DRIVER_NAME,
116260684Skaiw};
117260684Skaiw
118260684Skaiw#endif /* FB_INSTALL_CDEV */
119260684Skaiw
120260684Skaiwstatic void
121260684Skaiwgdc_identify(driver_t *driver, device_t parent)
122260684Skaiw{
123260684Skaiw	BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, DRIVER_NAME, 0);
124260684Skaiw}
125260684Skaiw
126260684Skaiwstatic int
127260684Skaiwgdcprobe(device_t dev)
128260684Skaiw{
129260684Skaiw	int error;
130260684Skaiw
131260684Skaiw	/* Check isapnp ids */
132260684Skaiw	if (isa_get_vendorid(dev))
133260684Skaiw		return (ENXIO);
134260684Skaiw
135260684Skaiw	device_set_desc(dev, "Generic GDC");
136260684Skaiw
137260684Skaiw	error = gdc_alloc_resource(dev);
138260684Skaiw	if (error)
139260684Skaiw		return (error);
140260684Skaiw
141260684Skaiw	error = gdc_probe_unit(device_get_unit(dev),
142260684Skaiw			       device_get_softc(dev),
143260684Skaiw			       device_get_flags(dev));
144260684Skaiw
145260684Skaiw	gdc_release_resource(dev);
146260684Skaiw
147260684Skaiw	return (error);
148260684Skaiw}
149260684Skaiw
150260684Skaiwstatic int
151260684Skaiwgdc_attach(device_t dev)
152260684Skaiw{
153260684Skaiw	gdc_softc_t *sc;
154260684Skaiw	int error;
155260684Skaiw
156260684Skaiw	error = gdc_alloc_resource(dev);
157260684Skaiw	if (error)
158260684Skaiw		return (error);
159367466Sdim
160260684Skaiw	sc = device_get_softc(dev);
161260684Skaiw	error = gdc_attach_unit(device_get_unit(dev),
162260684Skaiw				sc,
163260684Skaiw				device_get_flags(dev));
164260684Skaiw	if (error) {
165260684Skaiw		gdc_release_resource(dev);
166260684Skaiw		return error;
167260684Skaiw	}
168260684Skaiw
169260684Skaiw#ifdef FB_INSTALL_CDEV
170260684Skaiw	/* attach a virtual frame buffer device */
171260684Skaiw	error = fb_attach(GDC_MKMINOR(device_get_unit(dev)),
172260684Skaiw				  sc->adp, &gdc_cdevsw);
173260684Skaiw	if (error) {
174260684Skaiw		gdc_release_resource(dev);
175367466Sdim		return error;
176367466Sdim	}
177260684Skaiw#endif /* FB_INSTALL_CDEV */
178260684Skaiw
179260684Skaiw	if (bootverbose)
180260684Skaiw		vidd_diag(sc->adp, bootverbose);
181260684Skaiw
182260684Skaiw	return 0;
183367466Sdim}
184367466Sdim
185367466Sdimstatic int
186367466Sdimgdc_probe_unit(int unit, gdc_softc_t *sc, int flags)
187367466Sdim{
188367466Sdim	video_switch_t *sw;
189367466Sdim
190367466Sdim	sw = vid_get_switch(DRIVER_NAME);
191367466Sdim	if (sw == NULL)
192367466Sdim		return ENXIO;
193367466Sdim	return (*sw->probe)(unit, &sc->adp, NULL, flags);
194367466Sdim}
195367466Sdim
196367466Sdimstatic int
197367466Sdimgdc_attach_unit(int unit, gdc_softc_t *sc, int flags)
198367466Sdim{
199367466Sdim	video_switch_t *sw;
200367466Sdim
201367466Sdim	sw = vid_get_switch(DRIVER_NAME);
202367466Sdim	if (sw == NULL)
203367466Sdim		return ENXIO;
204367466Sdim	return (*sw->init)(unit, sc->adp, flags);
205367466Sdim}
206367466Sdim
207367466Sdim
208367466Sdimstatic int
209367466Sdimgdc_alloc_resource(device_t dev)
210260684Skaiw{
211260684Skaiw	int rid;
212260684Skaiw	gdc_softc_t *sc;
213260684Skaiw
214260684Skaiw	sc = device_get_softc(dev);
215260684Skaiw
216260684Skaiw	/* TEXT GDC */
217260684Skaiw	rid = 0;
218260684Skaiw	bus_set_resource(dev, SYS_RES_IOPORT, rid, TEXT_GDC, 1);
219260684Skaiw	sc->res_tgdc = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid,
220260684Skaiw					   gdc_iat, 8, RF_ACTIVE);
221260684Skaiw	if (sc->res_tgdc == NULL) {
222260684Skaiw		gdc_release_resource(dev);
223260684Skaiw		return (ENXIO);
224260684Skaiw	}
225260684Skaiw	isa_load_resourcev(sc->res_tgdc, gdc_iat, 8);
226260684Skaiw
227260684Skaiw	/* GRAPHIC GDC */
228260684Skaiw	rid = 8;
229260684Skaiw	bus_set_resource(dev, SYS_RES_IOPORT, rid, GRAPHIC_GDC, 1);
230260684Skaiw	sc->res_ggdc = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid,
231260684Skaiw					   gdc_iat, 8, RF_ACTIVE);
232260684Skaiw	if (sc->res_ggdc == NULL) {
233260684Skaiw		gdc_release_resource(dev);
234260684Skaiw		return (ENXIO);
235260684Skaiw	}
236260684Skaiw	isa_load_resourcev(sc->res_ggdc, gdc_iat, 8);
237260684Skaiw
238260684Skaiw	/* EGC */
239260684Skaiw	rid = 16;
240260684Skaiw	bus_set_resource(dev, SYS_RES_IOPORT, rid, 0x4a0, 1);
241260684Skaiw	sc->res_egc = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid,
242260684Skaiw					   gdc_iat, 8, RF_ACTIVE);
243260684Skaiw	if (sc->res_egc == NULL) {
244260684Skaiw		gdc_release_resource(dev);
245260684Skaiw		return (ENXIO);
246260684Skaiw	}
247260684Skaiw	isa_load_resourcev(sc->res_egc, gdc_iat, 8);
248260684Skaiw
249260684Skaiw	/* PEGC */
250260684Skaiw	rid = 24;
251260684Skaiw	bus_set_resource(dev, SYS_RES_IOPORT, rid, 0x9a0, 1);
252260684Skaiw	sc->res_pegc = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid,
253260684Skaiw					   gdc_iat, 8, RF_ACTIVE);
254260684Skaiw	if (sc->res_pegc == NULL) {
255260684Skaiw		gdc_release_resource(dev);
256260684Skaiw		return (ENXIO);
257260684Skaiw	}
258260684Skaiw	isa_load_resourcev(sc->res_pegc, gdc_iat, 8);
259260684Skaiw
260260684Skaiw	/* CRTC/GRCG */
261260684Skaiw	rid = 32;
262260684Skaiw	bus_set_resource(dev, SYS_RES_IOPORT, rid, 0x70, 1);
263260684Skaiw	sc->res_grcg = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid,
264260684Skaiw					   gdc_iat, 8, RF_ACTIVE);
265260684Skaiw	if (sc->res_grcg == NULL) {
266260684Skaiw		gdc_release_resource(dev);
267260684Skaiw		return (ENXIO);
268260684Skaiw	}
269260684Skaiw	isa_load_resourcev(sc->res_grcg, gdc_iat, 8);
270260684Skaiw
271260684Skaiw	/* KCG */
272260684Skaiw	rid = 40;
273260684Skaiw	bus_set_resource(dev, SYS_RES_IOPORT, rid, 0xa1, 1);
274260684Skaiw	sc->res_kcg = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid,
275260684Skaiw					  gdc_iat, 8, RF_ACTIVE);
276260684Skaiw	if (sc->res_kcg == NULL) {
277260684Skaiw		gdc_release_resource(dev);
278		return (ENXIO);
279	}
280	isa_load_resourcev(sc->res_kcg, gdc_iat, 8);
281
282
283	/* TEXT Memory */
284	rid = 0;
285	sc->res_tmem = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
286					  0xa0000, 0xa4fff, 0x5000, RF_ACTIVE);
287	if (sc->res_tmem == NULL) {
288		gdc_release_resource(dev);
289		return (ENXIO);
290	}
291
292	/* GRAPHIC Memory */
293	rid = 1;
294	sc->res_gmem1 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
295					   0xa8000, 0xbffff, 0x18000,
296					   RF_ACTIVE);
297	if (sc->res_gmem1 == NULL) {
298		gdc_release_resource(dev);
299		return (ENXIO);
300	}
301	rid = 2;
302	sc->res_gmem2 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
303					   0xe0000, 0xe7fff, 0x8000,
304					   RF_ACTIVE);
305	if (sc->res_gmem2 == NULL) {
306		gdc_release_resource(dev);
307		return (ENXIO);
308	}
309
310	return (0);
311}
312
313static int
314gdc_release_resource(device_t dev)
315{
316	gdc_softc_t *sc;
317
318	sc = device_get_softc(dev);
319
320	if (sc->res_tgdc)
321		bus_release_resource(dev, SYS_RES_IOPORT,  0, sc->res_tgdc);
322	if (sc->res_ggdc)
323		bus_release_resource(dev, SYS_RES_IOPORT,  8, sc->res_ggdc);
324	if (sc->res_egc)
325		bus_release_resource(dev, SYS_RES_IOPORT, 16, sc->res_egc);
326	if (sc->res_pegc)
327		bus_release_resource(dev, SYS_RES_IOPORT, 24, sc->res_pegc);
328	if (sc->res_grcg)
329		bus_release_resource(dev, SYS_RES_IOPORT, 32, sc->res_grcg);
330	if (sc->res_kcg)
331		bus_release_resource(dev, SYS_RES_IOPORT, 40, sc->res_kcg);
332
333	if (sc->res_tmem)
334		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->res_tmem);
335	if (sc->res_gmem1)
336		bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->res_gmem1);
337	if (sc->res_gmem2)
338		bus_release_resource(dev, SYS_RES_MEMORY, 2, sc->res_gmem2);
339
340	return (0);
341}
342
343/* cdev driver functions */
344
345#ifdef FB_INSTALL_CDEV
346
347static int
348gdcopen(struct cdev *dev, int flag, int mode, struct thread *td)
349{
350    gdc_softc_t *sc;
351
352    sc = GDC_SOFTC(GDC_UNIT(dev));
353    if (sc == NULL)
354	return ENXIO;
355    if (mode & (O_CREAT | O_APPEND | O_TRUNC))
356	return ENODEV;
357
358    return genfbopen(&sc->gensc, sc->adp, flag, mode, td);
359}
360
361static int
362gdcclose(struct cdev *dev, int flag, int mode, struct thread *td)
363{
364    gdc_softc_t *sc;
365
366    sc = GDC_SOFTC(GDC_UNIT(dev));
367    return genfbclose(&sc->gensc, sc->adp, flag, mode, td);
368}
369
370static int
371gdcread(struct cdev *dev, struct uio *uio, int flag)
372{
373    gdc_softc_t *sc;
374
375    sc = GDC_SOFTC(GDC_UNIT(dev));
376    return genfbread(&sc->gensc, sc->adp, uio, flag);
377}
378
379static int
380gdcwrite(struct cdev *dev, struct uio *uio, int flag)
381{
382    gdc_softc_t *sc;
383
384    sc = GDC_SOFTC(GDC_UNIT(dev));
385    return genfbread(&sc->gensc, sc->adp, uio, flag);
386}
387
388static int
389gdcioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
390{
391    gdc_softc_t *sc;
392
393    sc = GDC_SOFTC(GDC_UNIT(dev));
394    return genfbioctl(&sc->gensc, sc->adp, cmd, arg, flag, td);
395}
396
397static int
398gdcmmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
399    int prot, vm_memattr_t *memattr)
400{
401    gdc_softc_t *sc;
402
403    sc = GDC_SOFTC(GDC_UNIT(dev));
404    return genfbmmap(&sc->gensc, sc->adp, offset, paddr, prot, memattr);
405}
406
407#endif /* FB_INSTALL_CDEV */
408
409static device_method_t gdc_methods[] = {
410	DEVMETHOD(device_identify,	gdc_identify),
411	DEVMETHOD(device_probe,		gdcprobe),
412	DEVMETHOD(device_attach,	gdc_attach),
413	{ 0, 0 }
414};
415
416static driver_t gdcdriver = {
417	DRIVER_NAME,
418	gdc_methods,
419	sizeof(gdc_softc_t),
420};
421
422DRIVER_MODULE(gdc, isa, gdcdriver, gdc_devclass, 0, 0);
423
424/* LOW-LEVEL */
425
426
427#include <pc98/cbus/30line.h>
428
429#define TEXT_BUF_BASE		0x000a0000
430#define TEXT_BUF_SIZE		0x00008000
431#define GRAPHICS_BUF_BASE	0x000a8000
432#define GRAPHICS_BUF_SIZE	0x00040000
433#define VIDEO_BUF_BASE		0x000a0000
434#define VIDEO_BUF_SIZE		0x00048000
435
436#define probe_done(adp)		((adp)->va_flags & V_ADP_PROBED)
437#define init_done(adp)		((adp)->va_flags & V_ADP_INITIALIZED)
438#define config_done(adp)	((adp)->va_flags & V_ADP_REGISTERED)
439
440/*
441 * NOTE: `va_window' should have a virtual address, but is initialized
442 * with a physical address in the following table, they will be
443 * converted at run-time.
444 */
445static video_adapter_t adapter_init_value[] = {
446    { 0,
447      KD_PC98, "gdc",			/* va_type, va_name */
448      0, 0, 				/* va_unit, va_minor */
449      V_ADP_COLOR | V_ADP_MODECHANGE | V_ADP_BORDER,
450      TEXT_GDC, 16, TEXT_GDC,		/* va_io*, XXX */
451      VIDEO_BUF_BASE, VIDEO_BUF_SIZE,	/* va_mem* */
452      TEXT_BUF_BASE, TEXT_BUF_SIZE, TEXT_BUF_SIZE, 0, /* va_window* */
453      0, 0, 				/* va_buffer, va_buffer_size */
454      0, M_PC98_80x25, 0, 		/* va_*mode* */
455    },
456};
457
458static video_adapter_t	biosadapter[1];
459
460/* video driver declarations */
461static int			gdc_configure(int flags);
462static int			gdc_err(video_adapter_t *adp, ...);
463static vi_probe_t		gdc_probe;
464static vi_init_t		gdc_init;
465static vi_get_info_t		gdc_get_info;
466static vi_query_mode_t		gdc_query_mode;
467static vi_set_mode_t		gdc_set_mode;
468static vi_set_border_t		gdc_set_border;
469static vi_save_state_t		gdc_save_state;
470static vi_load_state_t		gdc_load_state;
471static vi_read_hw_cursor_t	gdc_read_hw_cursor;
472static vi_set_hw_cursor_t	gdc_set_hw_cursor;
473static vi_set_hw_cursor_shape_t	gdc_set_hw_cursor_shape;
474static vi_blank_display_t	gdc_blank_display;
475static vi_mmap_t		gdc_mmap_buf;
476static vi_ioctl_t		gdc_dev_ioctl;
477static vi_clear_t		gdc_clear;
478static vi_fill_rect_t		gdc_fill_rect;
479static vi_bitblt_t		gdc_bitblt;
480static vi_diag_t		gdc_diag;
481static vi_save_palette_t	gdc_save_palette;
482static vi_load_palette_t	gdc_load_palette;
483static vi_set_win_org_t		gdc_set_origin;
484
485static video_switch_t gdcvidsw = {
486	gdc_probe,
487	gdc_init,
488	gdc_get_info,
489	gdc_query_mode,
490	gdc_set_mode,
491	(vi_save_font_t *)gdc_err,
492	(vi_load_font_t *)gdc_err,
493	(vi_show_font_t *)gdc_err,
494	gdc_save_palette,
495	gdc_load_palette,
496	gdc_set_border,
497	gdc_save_state,
498	gdc_load_state,
499	gdc_set_origin,
500	gdc_read_hw_cursor,
501	gdc_set_hw_cursor,
502	gdc_set_hw_cursor_shape,
503	gdc_blank_display,
504	gdc_mmap_buf,
505	gdc_dev_ioctl,
506	gdc_clear,
507	gdc_fill_rect,
508	gdc_bitblt,
509	(int (*)(void))gdc_err,
510	(int (*)(void))gdc_err,
511	gdc_diag,
512};
513
514VIDEO_DRIVER(gdc, gdcvidsw, gdc_configure);
515
516/* GDC BIOS standard video modes */
517#define EOT		(-1)
518#define NA		(-2)
519
520static video_info_t bios_vmode[] = {
521    { M_PC98_80x25, V_INFO_COLOR, 80, 25, 8, 16, 4, 1,
522      TEXT_BUF_BASE, TEXT_BUF_SIZE, TEXT_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
523#ifdef LINE30
524    { M_PC98_80x30, V_INFO_COLOR, 80, 30, 8, 16, 4, 1,
525      TEXT_BUF_BASE, TEXT_BUF_SIZE, TEXT_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
526#endif
527#ifndef GDC_NOGRAPHICS
528    { M_PC98_EGC640x400, V_INFO_COLOR | V_INFO_GRAPHICS,
529      640, 400, 8, 16, 4, 4,
530      GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0,
531      V_INFO_MM_PLANAR },
532    { M_PC98_PEGC640x400, V_INFO_COLOR | V_INFO_GRAPHICS | V_INFO_VESA,
533      640, 400, 8, 16, 8, 1,
534      GRAPHICS_BUF_BASE, 0x00008000, 0x00008000, 0, 0,
535      V_INFO_MM_PACKED, 1 },
536#ifdef LINE30
537    { M_PC98_PEGC640x480, V_INFO_COLOR | V_INFO_GRAPHICS | V_INFO_VESA,
538      640, 480, 8, 16, 8, 1,
539      GRAPHICS_BUF_BASE, 0x00008000, 0x00008000, 0, 0,
540      V_INFO_MM_PACKED, 1 },
541#endif
542#endif
543    { EOT },
544};
545
546static int		gdc_init_done = FALSE;
547
548/* local functions */
549static int map_gen_mode_num(int type, int color, int mode);
550static int probe_adapters(void);
551
552#define	prologue(adp, flag, err)			\
553	if (!gdc_init_done || !((adp)->va_flags & (flag)))	\
554	    return (err)
555
556/* a backdoor for the console driver */
557static int
558gdc_configure(int flags)
559{
560    probe_adapters();
561    biosadapter[0].va_flags |= V_ADP_INITIALIZED;
562    if (!config_done(&biosadapter[0])) {
563	if (vid_register(&biosadapter[0]) < 0)
564	    return 1;
565	biosadapter[0].va_flags |= V_ADP_REGISTERED;
566    }
567
568    return 1;
569}
570
571/* local subroutines */
572
573/* map a generic video mode to a known mode number */
574static int
575map_gen_mode_num(int type, int color, int mode)
576{
577    static struct {
578	int from;
579	int to;
580    } mode_map[] = {
581	{ M_TEXT_80x25,	M_PC98_80x25, },
582#ifdef LINE30
583	{ M_TEXT_80x30,	M_PC98_80x30, },
584#endif
585    };
586    int i;
587
588    for (i = 0; i < nitems(mode_map); ++i) {
589        if (mode_map[i].from == mode)
590            return mode_map[i].to;
591    }
592    return mode;
593}
594
595static int
596verify_adapter(video_adapter_t *adp)
597{
598#ifndef GDC_NOGRAPHICS
599    int i;
600
601    if (PC98_SYSTEM_PARAMETER(0x45c) & 0x40) {		/* PEGC exists */
602	adp->va_flags |= V_ADP_VESA;			/* XXX */
603    } else {
604	for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) {
605	    if (bios_vmode[i].vi_flags & V_INFO_VESA)
606		bios_vmode[i].vi_mode = NA;
607	}
608    }
609#endif
610    return 0;
611}
612
613/* probe video adapters and return the number of detected adapters */
614static int
615probe_adapters(void)
616{
617    video_info_t info;
618
619    /* do this test only once */
620    if (gdc_init_done)
621	return 1;
622    gdc_init_done = TRUE;
623
624    biosadapter[0] = adapter_init_value[0];
625    biosadapter[0].va_flags |= V_ADP_PROBED;
626    biosadapter[0].va_mode =
627	biosadapter[0].va_initial_mode = biosadapter[0].va_initial_bios_mode;
628
629    if ((PC98_SYSTEM_PARAMETER(0x597) & 0x80) ||
630	(PC98_SYSTEM_PARAMETER(0x458) & 0x80)) {
631	gdc_FH = (inb(0x9a8) & 1) ? _31KHZ : _24KHZ;
632    } else {
633	gdc_FH = _24KHZ;
634    }
635
636    gdc_get_info(&biosadapter[0], biosadapter[0].va_initial_mode, &info);
637    initialize_gdc(T25_G400, info.vi_flags & V_INFO_GRAPHICS);
638
639    biosadapter[0].va_window = BIOS_PADDRTOVADDR(info.vi_window);
640    biosadapter[0].va_window_size = info.vi_window_size;
641    biosadapter[0].va_window_gran = info.vi_window_gran;
642    biosadapter[0].va_buffer = 0;
643    biosadapter[0].va_buffer_size = 0;
644    if (info.vi_flags & V_INFO_GRAPHICS) {
645	switch (info.vi_depth/info.vi_planes) {
646	case 1:
647	    biosadapter[0].va_line_width = info.vi_width/8;
648	    break;
649	case 2:
650	    biosadapter[0].va_line_width = info.vi_width/4;
651	    break;
652	case 4:
653	    biosadapter[0].va_line_width = info.vi_width/2;
654	    break;
655	case 8:
656	default: /* shouldn't happen */
657	    biosadapter[0].va_line_width = info.vi_width;
658	    break;
659	}
660    } else {
661	biosadapter[0].va_line_width = info.vi_width;
662    }
663    bcopy(&info, &biosadapter[0].va_info, sizeof(info));
664
665    verify_adapter(&biosadapter[0]);
666
667    return 1;
668}
669
670static void master_gdc_cmd(unsigned int cmd)
671{
672    while ( (inb(TEXT_GDC) & 2) != 0);
673    outb(TEXT_GDC+2, cmd);
674}
675
676static void master_gdc_prm(unsigned int pmtr)
677{
678    while ( (inb(TEXT_GDC) & 2) != 0);
679    outb(TEXT_GDC, pmtr);
680}
681
682static void master_gdc_word_prm(unsigned int wpmtr)
683{
684    master_gdc_prm(wpmtr & 0x00ff);
685    master_gdc_prm((wpmtr >> 8) & 0x00ff);
686}
687
688#ifdef LINE30
689static void master_gdc_fifo_empty(void)
690{
691    while ( (inb(TEXT_GDC) & 4) == 0);
692}
693#endif
694
695static void master_gdc_wait_vsync(void)
696{
697    while ( (inb(TEXT_GDC) & 0x20) != 0);
698    while ( (inb(TEXT_GDC) & 0x20) == 0);
699}
700
701static void gdc_cmd(unsigned int cmd)
702{
703    while ( (inb(GRAPHIC_GDC) & 2) != 0);
704    outb( GRAPHIC_GDC+2, cmd);
705}
706
707#ifdef LINE30
708static void gdc_prm(unsigned int pmtr)
709{
710    while ( (inb(GRAPHIC_GDC) & 2) != 0);
711    outb( GRAPHIC_GDC, pmtr);
712}
713
714static void gdc_word_prm(unsigned int wpmtr)
715{
716    gdc_prm(wpmtr & 0x00ff);
717    gdc_prm((wpmtr >> 8) & 0x00ff);
718}
719
720static void gdc_fifo_empty(void)
721{
722    while ( (inb(GRAPHIC_GDC) & 0x04) == 0);
723}
724#endif
725
726static void gdc_wait_vsync(void)
727{
728    while ( (inb(GRAPHIC_GDC) & 0x20) != 0);
729    while ( (inb(GRAPHIC_GDC) & 0x20) == 0);
730}
731
732#ifdef LINE30
733static int check_gdc_clock(void)
734{
735    if ((inb(IO_SYSPORT) & 0x80) == 0){
736       	return _5MHZ;
737    } else {
738       	return _2_5MHZ;
739    }
740}
741#endif
742
743static void initialize_gdc(unsigned int mode, int isGraph)
744{
745#ifdef LINE30
746    /* start 30line initialize */
747    int m_mode, s_mode, gdc_clock, hsync_clock;
748
749    gdc_clock = check_gdc_clock();
750    m_mode = (mode == T25_G400) ? _25L : _30L;
751    s_mode = 2*mode+gdc_clock;
752    gdc_INFO = m_mode;
753
754    master_gdc_wait_vsync();
755
756    if ((PC98_SYSTEM_PARAMETER(0x597) & 0x80) ||
757	(PC98_SYSTEM_PARAMETER(0x458) & 0x80)) {
758	if (PC98_SYSTEM_PARAMETER(0x481) & 0x08) {
759	    hsync_clock = (m_mode == _25L) ? gdc_FH : _31KHZ;
760	    outb(0x9a8, (hsync_clock == _31KHZ) ? 1 : 0);
761	} else {
762	    hsync_clock = gdc_FH;
763	}
764    } else {
765	hsync_clock = _24KHZ;
766    }
767
768    if ((gdc_clock == _2_5MHZ) &&
769	(slave_param[hsync_clock][s_mode][GDC_LF] > 400)) {
770	outb(0x6a, 0x83);
771	outb(0x6a, 0x85);
772	gdc_clock = _5MHZ;
773	s_mode = 2*mode+gdc_clock;
774    }
775
776    master_gdc_cmd(_GDC_RESET);
777    master_gdc_cmd(_GDC_MASTER);
778    gdc_cmd(_GDC_RESET);
779    gdc_cmd(_GDC_SLAVE);
780
781    /* GDC Master */
782    master_gdc_cmd(_GDC_SYNC);
783    master_gdc_prm(0x00);	/* flush less */ /* text & graph */
784    master_gdc_prm(master_param[hsync_clock][m_mode][GDC_CR]);
785    master_gdc_word_prm(((master_param[hsync_clock][m_mode][GDC_HFP] << 10)
786		     + (master_param[hsync_clock][m_mode][GDC_VS] << 5)
787		     + master_param[hsync_clock][m_mode][GDC_HS]));
788    master_gdc_prm(master_param[hsync_clock][m_mode][GDC_HBP]);
789    master_gdc_prm(master_param[hsync_clock][m_mode][GDC_VFP]);
790    master_gdc_word_prm(((master_param[hsync_clock][m_mode][GDC_VBP] << 10)
791       		     + (master_param[hsync_clock][m_mode][GDC_LF])));
792    master_gdc_fifo_empty();
793    master_gdc_cmd(_GDC_PITCH);
794    master_gdc_prm(MasterPCH);
795    master_gdc_fifo_empty();
796
797    /* GDC slave */
798    gdc_cmd(_GDC_SYNC);
799    gdc_prm(0x06);
800    gdc_prm(slave_param[hsync_clock][s_mode][GDC_CR]);
801    gdc_word_prm((slave_param[hsync_clock][s_mode][GDC_HFP] << 10)
802		+ (slave_param[hsync_clock][s_mode][GDC_VS] << 5)
803		+ (slave_param[hsync_clock][s_mode][GDC_HS]));
804    gdc_prm(slave_param[hsync_clock][s_mode][GDC_HBP]);
805    gdc_prm(slave_param[hsync_clock][s_mode][GDC_VFP]);
806    gdc_word_prm((slave_param[hsync_clock][s_mode][GDC_VBP] << 10)
807		+ (slave_param[hsync_clock][s_mode][GDC_LF]));
808    gdc_fifo_empty();
809    gdc_cmd(_GDC_PITCH);
810    gdc_prm(SlavePCH[gdc_clock]);
811    gdc_fifo_empty();
812
813    /* set Master GDC scroll param */
814    master_gdc_wait_vsync();
815    master_gdc_wait_vsync();
816    master_gdc_wait_vsync();
817    master_gdc_cmd(_GDC_SCROLL);
818    master_gdc_word_prm(0);
819    master_gdc_word_prm((master_param[hsync_clock][m_mode][GDC_LF] << 4)
820			| 0x0000);
821    master_gdc_fifo_empty();
822
823    /* set Slave GDC scroll param */
824    gdc_wait_vsync();
825    gdc_cmd(_GDC_SCROLL);
826    gdc_word_prm(0);
827    if (gdc_clock == _5MHZ) {
828	gdc_word_prm((SlaveScrlLF[mode] << 4)  | 0x4000);
829    } else {
830	gdc_word_prm(SlaveScrlLF[mode] << 4);
831    }
832    gdc_fifo_empty();
833
834    gdc_word_prm(0);
835    if (gdc_clock == _5MHZ) {
836	gdc_word_prm((SlaveScrlLF[mode] << 4)  | 0x4000);
837    } else {
838	gdc_word_prm(SlaveScrlLF[mode] << 4);
839    }
840    gdc_fifo_empty();
841
842    /* sync start */
843    gdc_cmd(isGraph ? _GDC_START : _GDC_STOP);
844
845    gdc_wait_vsync();
846    gdc_wait_vsync();
847    gdc_wait_vsync();
848
849    master_gdc_cmd(isGraph ? _GDC_STOP : _GDC_START);
850#else
851    master_gdc_wait_vsync();
852    master_gdc_cmd(isGraph ? _GDC_STOP : _GDC_START);	/* text */
853    gdc_wait_vsync();
854    gdc_cmd(isGraph ? _GDC_START : _GDC_STOP);		/* graphics */
855#endif
856}
857
858#ifndef GDC_NOGRAPHICS
859static u_char b_palette[] = {
860    /* R     G     B */
861    0x00, 0x00, 0x00,	/* 0 */
862    0x00, 0x00, 0x7f,	/* 1 */
863    0x7f, 0x00, 0x00,	/* 2 */
864    0x7f, 0x00, 0x7f,	/* 3 */
865    0x00, 0x7f, 0x00,	/* 4 */
866    0x00, 0x7f, 0x7f,	/* 5 */
867    0x7f, 0x7f, 0x00,	/* 6 */
868    0x7f, 0x7f, 0x7f,	/* 7 */
869    0x40, 0x40, 0x40,	/* 8 */
870    0x00, 0x00, 0xff,	/* 9 */
871    0xff, 0x00, 0x00,	/* 10 */
872    0xff, 0x00, 0xff,	/* 11 */
873    0x00, 0xff, 0x00,	/* 12 */
874    0x00, 0xff, 0xff,	/* 13 */
875    0xff, 0xff, 0x00,	/* 14 */
876    0xff, 0xff, 0xff,	/* 15 */
877};
878#endif
879
880static int
881gdc_load_palette(video_adapter_t *adp, u_char *palette)
882{
883#ifndef GDC_NOGRAPHICS
884    int i;
885
886    if (adp->va_info.vi_flags & V_INFO_VESA) {
887	gdc_wait_vsync();
888	for (i = 0; i < 256; ++i) {
889	    outb(0xa8, i);
890	    outb(0xac, *palette++);	/* R */
891	    outb(0xaa, *palette++);	/* G */
892	    outb(0xae, *palette++);	/* B */
893	}
894    } else {
895	/*
896	 * XXX - Even though PC-98 text color is independent of palette,
897	 * we should set palette in text mode.
898	 * Because the background color of text mode is palette 0's one.
899	 */
900	outb(0x6a, 1);		/* 16 colors mode */
901	bcopy(palette, b_palette, sizeof(b_palette));
902
903	gdc_wait_vsync();
904	for (i = 0; i < 16; ++i) {
905	    outb(0xa8, i);
906	    outb(0xac, *palette++ >> 4);	/* R */
907	    outb(0xaa, *palette++ >> 4);	/* G */
908	    outb(0xae, *palette++ >> 4);	/* B */
909	}
910    }
911#endif
912    return 0;
913}
914
915static int
916gdc_save_palette(video_adapter_t *adp, u_char *palette)
917{
918#ifndef GDC_NOGRAPHICS
919    int i;
920
921    if (adp->va_info.vi_flags & V_INFO_VESA) {
922	for (i = 0; i < 256; ++i) {
923	    outb(0xa8, i);
924	    *palette++ = inb(0xac);	/* R */
925	    *palette++ = inb(0xaa);	/* G */
926	    *palette++ = inb(0xae);	/* B */
927	}
928    } else {
929	bcopy(b_palette, palette, sizeof(b_palette));
930    }
931#endif
932    return 0;
933}
934
935static int
936gdc_set_origin(video_adapter_t *adp, off_t offset)
937{
938#ifndef GDC_NOGRAPHICS
939    if (adp->va_info.vi_flags & V_INFO_VESA) {
940	writew(BIOS_PADDRTOVADDR(0x000e0004), offset >> 15);
941    }
942#endif
943    return 0;
944}
945
946/* entry points */
947
948static int
949gdc_err(video_adapter_t *adp, ...)
950{
951    return ENODEV;
952}
953
954static int
955gdc_probe(int unit, video_adapter_t **adpp, void *arg, int flags)
956{
957    probe_adapters();
958    if (unit >= 1)
959	return ENXIO;
960
961    *adpp = &biosadapter[unit];
962
963    return 0;
964}
965
966static int
967gdc_init(int unit, video_adapter_t *adp, int flags)
968{
969    if ((unit >= 1) || (adp == NULL) || !probe_done(adp))
970	return ENXIO;
971
972    if (!init_done(adp)) {
973	/* nothing to do really... */
974	adp->va_flags |= V_ADP_INITIALIZED;
975    }
976
977    if (!config_done(adp)) {
978	if (vid_register(adp) < 0)
979		return ENXIO;
980	adp->va_flags |= V_ADP_REGISTERED;
981    }
982
983    return 0;
984}
985
986/*
987 * get_info():
988 * Return the video_info structure of the requested video mode.
989 */
990static int
991gdc_get_info(video_adapter_t *adp, int mode, video_info_t *info)
992{
993    int i;
994
995    if (!gdc_init_done)
996	return ENXIO;
997
998    mode = map_gen_mode_num(adp->va_type, adp->va_flags & V_ADP_COLOR, mode);
999    for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) {
1000	if (bios_vmode[i].vi_mode == NA)
1001	    continue;
1002	if (mode == bios_vmode[i].vi_mode) {
1003	    *info = bios_vmode[i];
1004	    info->vi_buffer_size = info->vi_window_size*info->vi_planes;
1005	    return 0;
1006	}
1007    }
1008    return EINVAL;
1009}
1010
1011/*
1012 * query_mode():
1013 * Find a video mode matching the requested parameters.
1014 * Fields filled with 0 are considered "don't care" fields and
1015 * match any modes.
1016 */
1017static int
1018gdc_query_mode(video_adapter_t *adp, video_info_t *info)
1019{
1020    int i;
1021
1022    if (!gdc_init_done)
1023	return ENXIO;
1024
1025    for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) {
1026	if (bios_vmode[i].vi_mode == NA)
1027	    continue;
1028
1029	if ((info->vi_width != 0)
1030	    && (info->vi_width != bios_vmode[i].vi_width))
1031		continue;
1032	if ((info->vi_height != 0)
1033	    && (info->vi_height != bios_vmode[i].vi_height))
1034		continue;
1035	if ((info->vi_cwidth != 0)
1036	    && (info->vi_cwidth != bios_vmode[i].vi_cwidth))
1037		continue;
1038	if ((info->vi_cheight != 0)
1039	    && (info->vi_cheight != bios_vmode[i].vi_cheight))
1040		continue;
1041	if ((info->vi_depth != 0)
1042	    && (info->vi_depth != bios_vmode[i].vi_depth))
1043		continue;
1044	if ((info->vi_planes != 0)
1045	    && (info->vi_planes != bios_vmode[i].vi_planes))
1046		continue;
1047	/* XXX: should check pixel format, memory model */
1048	if ((info->vi_flags != 0)
1049	    && (info->vi_flags != bios_vmode[i].vi_flags))
1050		continue;
1051
1052	/* verify if this mode is supported on this adapter */
1053	if (gdc_get_info(adp, bios_vmode[i].vi_mode, info))
1054		continue;
1055	return 0;
1056    }
1057    return ENODEV;
1058}
1059
1060/*
1061 * set_mode():
1062 * Change the video mode.
1063 */
1064static int
1065gdc_set_mode(video_adapter_t *adp, int mode)
1066{
1067    video_info_t info;
1068
1069    prologue(adp, V_ADP_MODECHANGE, ENODEV);
1070
1071    mode = map_gen_mode_num(adp->va_type,
1072			    adp->va_flags & V_ADP_COLOR, mode);
1073    if (gdc_get_info(adp, mode, &info))
1074	return EINVAL;
1075
1076    switch (info.vi_mode) {
1077#ifndef GDC_NOGRAPHICS
1078	case M_PC98_PEGC640x480:	/* PEGC 640x480 */
1079	    initialize_gdc(T30_G480, info.vi_flags & V_INFO_GRAPHICS);
1080	    break;
1081	case M_PC98_PEGC640x400:	/* PEGC 640x400 */
1082	case M_PC98_EGC640x400:		/* EGC GRAPHICS */
1083#endif
1084	case M_PC98_80x25:		/* VGA TEXT */
1085	    initialize_gdc(T25_G400, info.vi_flags & V_INFO_GRAPHICS);
1086	    break;
1087	case M_PC98_80x30:		/* VGA TEXT */
1088	    initialize_gdc(T30_G400, info.vi_flags & V_INFO_GRAPHICS);
1089	    break;
1090	default:
1091	    break;
1092    }
1093
1094#ifndef GDC_NOGRAPHICS
1095    if (info.vi_flags & V_INFO_VESA) {
1096	outb(0x6a, 0x07);		/* enable mode F/F change */
1097	outb(0x6a, 0x21);		/* enhanced graphics */
1098	if (info.vi_height > 400)
1099	    outb(0x6a, 0x69);		/* 800 lines */
1100	writeb(BIOS_PADDRTOVADDR(0x000e0100), 0);	/* packed pixel */
1101    } else {
1102	if (adp->va_flags & V_ADP_VESA) {
1103	    outb(0x6a, 0x07);		/* enable mode F/F change */
1104	    outb(0x6a, 0x20);		/* normal graphics */
1105	    outb(0x6a, 0x68);		/* 400 lines */
1106	}
1107	outb(0x6a, 1);			/* 16 colors */
1108    }
1109#endif
1110
1111    adp->va_mode = mode;
1112    adp->va_flags &= ~V_ADP_COLOR;
1113    adp->va_flags |=
1114	(info.vi_flags & V_INFO_COLOR) ? V_ADP_COLOR : 0;
1115#if 0
1116    adp->va_crtc_addr =
1117	(adp->va_flags & V_ADP_COLOR) ? COLOR_CRTC : MONO_CRTC;
1118#endif
1119    adp->va_window = BIOS_PADDRTOVADDR(info.vi_window);
1120    adp->va_window_size = info.vi_window_size;
1121    adp->va_window_gran = info.vi_window_gran;
1122    if (info.vi_buffer_size == 0) {
1123    	adp->va_buffer = 0;
1124    	adp->va_buffer_size = 0;
1125    } else {
1126    	adp->va_buffer = BIOS_PADDRTOVADDR(info.vi_buffer);
1127    	adp->va_buffer_size = info.vi_buffer_size;
1128    }
1129    if (info.vi_flags & V_INFO_GRAPHICS) {
1130	switch (info.vi_depth/info.vi_planes) {
1131	case 1:
1132	    adp->va_line_width = info.vi_width/8;
1133	    break;
1134	case 2:
1135	    adp->va_line_width = info.vi_width/4;
1136	    break;
1137	case 4:
1138	    adp->va_line_width = info.vi_width/2;
1139	    break;
1140	case 8:
1141	default: /* shouldn't happen */
1142	    adp->va_line_width = info.vi_width;
1143	    break;
1144	}
1145    } else {
1146	adp->va_line_width = info.vi_width;
1147    }
1148    bcopy(&info, &adp->va_info, sizeof(info));
1149
1150    /* move hardware cursor out of the way */
1151    vidd_set_hw_cursor(adp, -1, -1);
1152
1153    return 0;
1154}
1155
1156/*
1157 * set_border():
1158 * Change the border color.
1159 */
1160static int
1161gdc_set_border(video_adapter_t *adp, int color)
1162{
1163    outb(0x6c, color << 4);
1164    return 0;
1165}
1166
1167/*
1168 * save_state():
1169 * Read video card register values.
1170 */
1171static int
1172gdc_save_state(video_adapter_t *adp, void *p, size_t size)
1173{
1174    return ENODEV;
1175}
1176
1177/*
1178 * load_state():
1179 * Set video card registers at once.
1180 */
1181static int
1182gdc_load_state(video_adapter_t *adp, void *p)
1183{
1184    return ENODEV;
1185}
1186
1187/*
1188 * read_hw_cursor():
1189 * Read the position of the hardware text cursor.
1190 */
1191static int
1192gdc_read_hw_cursor(video_adapter_t *adp, int *col, int *row)
1193{
1194    u_int16_t off;
1195    int s;
1196
1197    if (!gdc_init_done)
1198	return ENXIO;
1199
1200    if (adp->va_info.vi_flags & V_INFO_GRAPHICS)
1201	return ENODEV;
1202
1203    s = spltty();
1204    master_gdc_cmd(0xe0);	/* _GDC_CSRR */
1205    while((inb(TEXT_GDC + 0) & 0x1) == 0) {}	/* GDC wait */
1206    off = inb(TEXT_GDC + 2);			/* EADl */
1207    off |= (inb(TEXT_GDC + 2) << 8);		/* EADh */
1208    inb(TEXT_GDC + 2);				/* dummy */
1209    inb(TEXT_GDC + 2);				/* dummy */
1210    inb(TEXT_GDC + 2);				/* dummy */
1211    splx(s);
1212
1213    if (off >= ROW*COL)
1214	off = 0;
1215    *row = off / adp->va_info.vi_width;
1216    *col = off % adp->va_info.vi_width;
1217
1218    return 0;
1219}
1220
1221/*
1222 * set_hw_cursor():
1223 * Move the hardware text cursor.  If col and row are both -1,
1224 * the cursor won't be shown.
1225 */
1226static int
1227gdc_set_hw_cursor(video_adapter_t *adp, int col, int row)
1228{
1229    u_int16_t off;
1230    int s;
1231
1232    if (!gdc_init_done)
1233	return ENXIO;
1234
1235    if ((col == -1) && (row == -1)) {
1236	off = -1;
1237    } else {
1238	if (adp->va_info.vi_flags & V_INFO_GRAPHICS)
1239	    return ENODEV;
1240	off = row*adp->va_info.vi_width + col;
1241    }
1242
1243    s = spltty();
1244    master_gdc_cmd(0x49);	/* _GDC_CSRW */
1245    master_gdc_word_prm(off);
1246    splx(s);
1247
1248    return 0;
1249}
1250
1251/*
1252 * set_hw_cursor_shape():
1253 * Change the shape of the hardware text cursor.  If the height is zero
1254 * or negative, the cursor won't be shown.
1255 */
1256static int
1257gdc_set_hw_cursor_shape(video_adapter_t *adp, int base, int height,
1258			int celsize, int blink)
1259{
1260    int start;
1261    int end;
1262    int s;
1263
1264    if (!gdc_init_done)
1265	return ENXIO;
1266
1267    start = celsize - (base + height);
1268    end = celsize - base - 1;
1269
1270#if 0
1271    /*
1272     * muPD7220 GDC has anomaly that if end == celsize - 1 then start
1273     * must be 0, otherwise the cursor won't be correctly shown
1274     * in the first row in the screen.  We shall set end to celsize - 2;
1275     * if end == celsize -1 && start > 0. XXX
1276     */
1277    if ((end == celsize - 1) && (start > 0) && (start < end))
1278	--end;
1279#endif
1280
1281    s = spltty();
1282    master_gdc_cmd(0x4b);			/* _GDC_CSRFORM */
1283    master_gdc_prm(((height > 0) ? 0x80 : 0)	/* cursor on/off */
1284	| ((celsize - 1) & 0x1f));		/* cel size */
1285    master_gdc_word_prm(((end & 0x1f) << 11)	/* end line */
1286	| (12 << 6)				/* blink rate */
1287	| (blink ? 0 : 0x20)			/* blink on/off */
1288	| (start & 0x1f));			/* start line */
1289    splx(s);
1290
1291    return 0;
1292}
1293
1294/*
1295 * blank_display()
1296 * Put the display in power save/power off mode.
1297 */
1298static int
1299gdc_blank_display(video_adapter_t *adp, int mode)
1300{
1301    int s;
1302    static int standby = 0;
1303
1304    if (!gdc_init_done)
1305	return ENXIO;
1306
1307    s = splhigh();
1308    switch (mode) {
1309    case V_DISPLAY_SUSPEND:
1310    case V_DISPLAY_STAND_BY:
1311	outb(0x09a2, 0x80 | 0x40);		/* V/H-SYNC mask */
1312	if (inb(0x09a2) == (0x80 | 0x40))
1313	    standby = 1;
1314	/* FALLTHROUGH */
1315
1316    case V_DISPLAY_BLANK:
1317	while (!(inb(TEXT_GDC) & 0x20))		/* V-SYNC wait */
1318	    ;
1319	outb(TEXT_GDC + 8, 0x0e);		/* DISP off */
1320	break;
1321
1322    case V_DISPLAY_ON:
1323	while (!(inb(TEXT_GDC) & 0x20))		/* V-SYNC wait */
1324	    ;
1325	outb(TEXT_GDC + 8, 0x0f);		/* DISP on */
1326	if (standby) {
1327	    outb(0x09a2, 0x00);			/* V/H-SYNC unmask */
1328	    standby = 0;
1329	}
1330	break;
1331    }
1332    splx(s);
1333    return 0;
1334}
1335
1336/*
1337 * mmap():
1338 * Mmap frame buffer.
1339 */
1340static int
1341gdc_mmap_buf(video_adapter_t *adp, vm_ooffset_t offset, vm_offset_t *paddr,
1342	     int prot, vm_memattr_t *memattr)
1343{
1344    /* FIXME: is this correct? XXX */
1345    if (offset > VIDEO_BUF_SIZE - PAGE_SIZE)
1346	return -1;
1347    *paddr = adp->va_info.vi_window + offset;
1348    return 0;
1349}
1350
1351#ifndef GDC_NOGRAPHICS
1352static void
1353planar_fill(video_adapter_t *adp, int val)
1354{
1355
1356    outb(0x7c, 0x80);				/* GRCG on & TDW mode */
1357    outb(0x7e, 0);				/* tile B */
1358    outb(0x7e, 0);				/* tile R */
1359    outb(0x7e, 0);				/* tile G */
1360    outb(0x7e, 0);				/* tile I */
1361
1362    fillw_io(0, adp->va_window, 0x8000 / 2);	/* XXX */
1363
1364    outb(0x7c, 0);				/* GRCG off */
1365}
1366
1367static void
1368packed_fill(video_adapter_t *adp, int val)
1369{
1370    int length;
1371    int at;			/* position in the frame buffer */
1372    int l;
1373
1374    at = 0;
1375    length = adp->va_line_width*adp->va_info.vi_height;
1376    while (length > 0) {
1377	l = imin(length, adp->va_window_size);
1378	vidd_set_win_org(adp, at);
1379	bzero_io(adp->va_window, l);
1380	length -= l;
1381	at += l;
1382    }
1383}
1384
1385static int
1386gdc_clear(video_adapter_t *adp)
1387{
1388
1389    switch (adp->va_info.vi_mem_model) {
1390    case V_INFO_MM_TEXT:
1391	/* do nothing? XXX */
1392	break;
1393    case V_INFO_MM_PLANAR:
1394	planar_fill(adp, 0);
1395	break;
1396    case V_INFO_MM_PACKED:
1397	packed_fill(adp, 0);
1398	break;
1399    }
1400
1401    return 0;
1402}
1403#else /* GDC_NOGRAPHICS */
1404static int
1405gdc_clear(video_adapter_t *adp)
1406{
1407
1408    return 0;
1409}
1410#endif /* GDC_NOGRAPHICS */
1411
1412static int
1413gdc_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
1414{
1415    return ENODEV;
1416}
1417
1418static int
1419gdc_bitblt(video_adapter_t *adp,...)
1420{
1421    /* FIXME */
1422    return ENODEV;
1423}
1424
1425static int
1426gdc_dev_ioctl(video_adapter_t *adp, u_long cmd, caddr_t arg)
1427{
1428    switch (cmd) {
1429    case FBIO_GETWINORG:	/* get frame buffer window origin */
1430	*(u_int *)arg = 0;
1431	return 0;
1432
1433    case FBIO_SETWINORG:	/* set frame buffer window origin */
1434    case FBIO_SETDISPSTART:	/* set display start address */
1435    case FBIO_SETLINEWIDTH:	/* set scan line length in pixel */
1436    case FBIO_GETPALETTE:	/* get color palette */
1437    case FBIO_SETPALETTE:	/* set color palette */
1438    case FBIOGETCMAP:		/* get color palette */
1439    case FBIOPUTCMAP:		/* set color palette */
1440	return ENODEV;
1441
1442    case FBIOGTYPE:		/* get frame buffer type info. */
1443	((struct fbtype *)arg)->fb_type = fb_type(adp->va_type);
1444	((struct fbtype *)arg)->fb_height = adp->va_info.vi_height;
1445	((struct fbtype *)arg)->fb_width = adp->va_info.vi_width;
1446	((struct fbtype *)arg)->fb_depth = adp->va_info.vi_depth;
1447	if ((adp->va_info.vi_depth <= 1) || (adp->va_info.vi_depth > 8))
1448	    ((struct fbtype *)arg)->fb_cmsize = 0;
1449	else
1450	    ((struct fbtype *)arg)->fb_cmsize = 1 << adp->va_info.vi_depth;
1451	((struct fbtype *)arg)->fb_size = adp->va_buffer_size;
1452	return 0;
1453
1454    default:
1455	return fb_commonioctl(adp, cmd, arg);
1456    }
1457}
1458
1459/*
1460 * diag():
1461 * Print some information about the video adapter and video modes,
1462 * with requested level of details.
1463 */
1464static int
1465gdc_diag(video_adapter_t *adp, int level)
1466{
1467#if defined(FB_DEBUG) && FB_DEBUG > 1
1468    int i;
1469#endif
1470
1471    if (!gdc_init_done)
1472	return ENXIO;
1473
1474    fb_dump_adp_info(DRIVER_NAME, adp, level);
1475
1476#if defined(FB_DEBUG) && FB_DEBUG > 1
1477    for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) {
1478	 if (bios_vmode[i].vi_mode == NA)
1479	    continue;
1480	 if (get_mode_param(bios_vmode[i].vi_mode) == NULL)
1481	    continue;
1482	 fb_dump_mode_info(DRIVER_NAME, adp, &bios_vmode[i], level);
1483    }
1484#endif
1485
1486    return 0;
1487}
1488