1/*
2 * linux/drivers/video/hgafb.c -- Hercules graphics adaptor frame buffer device
3 *
4 *      Created 25 Nov 1999 by Ferenc Bakonyi (fero@drama.obuda.kando.hu)
5 *      Based on skeletonfb.c by Geert Uytterhoeven and
6 *               mdacon.c by Andrew Apted
7 *
8 * History:
9 *
10 * - Revision 0.1.7 (23 Jan 2001): fix crash resulting from MDA only cards
11 *				   being detected as Hercules.	 (Paul G.)
12 * - Revision 0.1.6 (17 Aug 2000): new style structs
13 *                                 documentation
14 * - Revision 0.1.5 (13 Mar 2000): spinlocks instead of saveflags();cli();etc
15 *                                 minor fixes
16 * - Revision 0.1.4 (24 Jan 2000): fixed a bug in hga_card_detect() for
17 *                                  HGA-only systems
18 * - Revision 0.1.3 (22 Jan 2000): modified for the new fb_info structure
19 *                                 screen is cleared after rmmod
20 *                                 virtual resolutions
21 *                                 kernel parameter 'video=hga:font:{fontname}'
22 *                                 module parameter 'font={fontname}'
23 *                                 module parameter 'nologo={0|1}'
24 *                                 the most important: boot logo :)
25 * - Revision 0.1.0  (6 Dec 1999): faster scrolling and minor fixes
26 * - First release  (25 Nov 1999)
27 *
28 * This file is subject to the terms and conditions of the GNU General Public
29 * License.  See the file COPYING in the main directory of this archive
30 * for more details.
31 */
32
33#include <linux/module.h>
34#include <linux/kernel.h>
35#include <linux/errno.h>
36#include <linux/spinlock.h>
37#include <linux/string.h>
38#include <linux/mm.h>
39#include <linux/tty.h>
40#include <linux/slab.h>
41#include <linux/delay.h>
42#include <linux/fb.h>
43#include <linux/init.h>
44#include <linux/ioport.h>
45#include <asm/io.h>
46#include <asm/vga.h>
47#include <video/fbcon.h>
48#include <video/fbcon-hga.h>
49
50#ifdef MODULE
51
52#define INCLUDE_LINUX_LOGO_DATA
53#include <linux/linux_logo.h>
54
55#endif /* MODULE */
56
57#define DPRINTK(args...)
58
59#define CHKINFO(ret)
60
61/* Description of the hardware layout */
62
63static unsigned long hga_vram_base;		/* Base of video memory */
64static unsigned long hga_vram_len;		/* Size of video memory */
65
66#define HGA_TXT			0
67#define HGA_GFX			1
68
69static int hga_mode = -1;			/* 0 = txt, 1 = gfx mode */
70
71static enum { TYPE_HERC, TYPE_HERCPLUS, TYPE_HERCCOLOR } hga_type;
72static char *hga_type_name;
73
74#define HGA_INDEX_PORT		0x3b4		/* Register select port */
75#define HGA_VALUE_PORT		0x3b5		/* Register value port */
76#define HGA_MODE_PORT		0x3b8		/* Mode control port */
77#define HGA_STATUS_PORT		0x3ba		/* Status and Config port */
78#define HGA_GFX_PORT		0x3bf		/* Graphics control port */
79
80/* HGA register values */
81
82#define HGA_CURSOR_BLINKING	0x00
83#define HGA_CURSOR_OFF		0x20
84#define HGA_CURSOR_SLOWBLINK	0x60
85
86#define HGA_MODE_GRAPHICS	0x02
87#define HGA_MODE_VIDEO_EN	0x08
88#define HGA_MODE_BLINK_EN	0x20
89#define HGA_MODE_GFX_PAGE1	0x80
90
91#define HGA_STATUS_HSYNC	0x01
92#define HGA_STATUS_VSYNC	0x80
93#define HGA_STATUS_VIDEO	0x08
94
95#define HGA_CONFIG_COL132	0x08
96#define HGA_GFX_MODE_EN		0x01
97#define HGA_GFX_PAGE_EN		0x02
98
99/* Global locks */
100
101static spinlock_t hga_reg_lock = SPIN_LOCK_UNLOCKED;
102
103/* Framebuffer driver structures */
104
105static struct fb_var_screeninfo hga_default_var = {
106	xres:		720,
107	yres:		348,
108	xres_virtual:	720,
109	yres_virtual:	348,
110	xoffset:	0,
111	yoffset:	0,
112	bits_per_pixel:	1,
113	grayscale:	0,
114	red:		{0, 1, 0},
115	green:		{0, 1, 0},
116	blue:		{0, 1, 0},
117	transp:		{0, 0, 0},
118	nonstd:		0,			/* (FB_NONSTD_HGA ?) */
119	activate:	0,
120	height:		-1,
121	width:		-1,
122	accel_flags:	0,
123	/* pixclock */
124	/* left_margin, right_margin */
125	/* upper_margin, lower_margin */
126	/* hsync_len, vsync_len */
127	/* sync */
128	/* vmode */
129};
130
131static struct fb_fix_screeninfo hga_fix = {
132	id:		"HGA",
133	smem_start:	(unsigned long) NULL,
134	smem_len:	0,
135	type:		FB_TYPE_PACKED_PIXELS,	/* (not sure) */
136	type_aux:	0,			/* (not sure) */
137	visual:		FB_VISUAL_MONO10,
138	xpanstep:	8,
139	ypanstep:	8,
140	ywrapstep:	0,
141	line_length:	90,
142	mmio_start:	0,
143	mmio_len:	0,
144	accel:		FB_ACCEL_NONE
145};
146
147static struct fb_info fb_info;
148static struct display disp;
149
150/* Don't assume that tty1 will be the initial current console. */
151static int currcon = -1;
152static int release_io_port = 0;
153static int release_io_ports = 0;
154
155#ifdef MODULE
156static char *font = NULL;
157static int nologo = 0;
158#endif
159
160/* -------------------------------------------------------------------------
161 *
162 * Low level hardware functions
163 *
164 * ------------------------------------------------------------------------- */
165
166static void write_hga_b(unsigned int val, unsigned char reg)
167{
168	outb_p(reg, HGA_INDEX_PORT);
169	outb_p(val, HGA_VALUE_PORT);
170}
171
172static void write_hga_w(unsigned int val, unsigned char reg)
173{
174	outb_p(reg,   HGA_INDEX_PORT); outb_p(val >> 8,   HGA_VALUE_PORT);
175	outb_p(reg+1, HGA_INDEX_PORT); outb_p(val & 0xff, HGA_VALUE_PORT);
176}
177
178static int test_hga_b(unsigned char val, unsigned char reg)
179{
180	outb_p(reg, HGA_INDEX_PORT);
181	outb  (val, HGA_VALUE_PORT);
182	udelay(20); val = (inb_p(HGA_VALUE_PORT) == val);
183	return val;
184}
185
186static void hga_clear_screen(void)
187{
188	unsigned char fillchar = 0xbf; /* magic */
189	unsigned long flags;
190
191	spin_lock_irqsave(&hga_reg_lock, flags);
192	if (hga_mode == HGA_TXT)
193		fillchar = ' ';
194	else if (hga_mode == HGA_GFX)
195		fillchar = 0x00;
196	spin_unlock_irqrestore(&hga_reg_lock, flags);
197	if (fillchar != 0xbf)
198		isa_memset_io(hga_vram_base, fillchar, hga_vram_len);
199}
200
201
202#ifdef MODULE
203static void hga_txt_mode(void)
204{
205	unsigned long flags;
206
207	spin_lock_irqsave(&hga_reg_lock, flags);
208	outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_BLINK_EN, HGA_MODE_PORT);
209	outb_p(0x00, HGA_GFX_PORT);
210	outb_p(0x00, HGA_STATUS_PORT);
211
212	write_hga_b(0x61, 0x00);	/* horizontal total */
213	write_hga_b(0x50, 0x01);	/* horizontal displayed */
214	write_hga_b(0x52, 0x02);	/* horizontal sync pos */
215	write_hga_b(0x0f, 0x03);	/* horizontal sync width */
216
217	write_hga_b(0x19, 0x04);	/* vertical total */
218	write_hga_b(0x06, 0x05);	/* vertical total adjust */
219	write_hga_b(0x19, 0x06);	/* vertical displayed */
220	write_hga_b(0x19, 0x07);	/* vertical sync pos */
221
222	write_hga_b(0x02, 0x08);	/* interlace mode */
223	write_hga_b(0x0d, 0x09);	/* maximum scanline */
224	write_hga_b(0x0c, 0x0a);	/* cursor start */
225	write_hga_b(0x0d, 0x0b);	/* cursor end */
226
227	write_hga_w(0x0000, 0x0c);	/* start address */
228	write_hga_w(0x0000, 0x0e);	/* cursor location */
229
230	hga_mode = HGA_TXT;
231	spin_unlock_irqrestore(&hga_reg_lock, flags);
232}
233#endif /* MODULE */
234
235static void hga_gfx_mode(void)
236{
237	unsigned long flags;
238
239	spin_lock_irqsave(&hga_reg_lock, flags);
240	outb_p(0x00, HGA_STATUS_PORT);
241	outb_p(HGA_GFX_MODE_EN, HGA_GFX_PORT);
242	outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT);
243
244	write_hga_b(0x35, 0x00);	/* horizontal total */
245	write_hga_b(0x2d, 0x01);	/* horizontal displayed */
246	write_hga_b(0x2e, 0x02);	/* horizontal sync pos */
247	write_hga_b(0x07, 0x03);	/* horizontal sync width */
248
249	write_hga_b(0x5b, 0x04);	/* vertical total */
250	write_hga_b(0x02, 0x05);	/* vertical total adjust */
251	write_hga_b(0x57, 0x06);	/* vertical displayed */
252	write_hga_b(0x57, 0x07);	/* vertical sync pos */
253
254	write_hga_b(0x02, 0x08);	/* interlace mode */
255	write_hga_b(0x03, 0x09);	/* maximum scanline */
256	write_hga_b(0x00, 0x0a);	/* cursor start */
257	write_hga_b(0x00, 0x0b);	/* cursor end */
258
259	write_hga_w(0x0000, 0x0c);	/* start address */
260	write_hga_w(0x0000, 0x0e);	/* cursor location */
261
262	hga_mode = HGA_GFX;
263	spin_unlock_irqrestore(&hga_reg_lock, flags);
264}
265
266#ifdef MODULE
267static void hga_show_logo(void)
268{
269	int x, y;
270	unsigned long dest = hga_vram_base;
271	char *logo = linux_logo_bw;
272	for (y = 134; y < 134 + 80 ; y++) /* this needs some cleanup */
273		for (x = 0; x < 10 ; x++)
274			isa_writeb(~*(logo++),
275				   (dest + (y%4)*8192 + (y>>2)*90 + x + 40));
276}
277#endif /* MODULE */
278
279static void hga_pan(unsigned int xoffset, unsigned int yoffset)
280{
281	unsigned int base;
282	unsigned long flags;
283
284	base = (yoffset / 8) * 90 + xoffset;
285	spin_lock_irqsave(&hga_reg_lock, flags);
286	write_hga_w(base, 0x0c);	/* start address */
287	spin_unlock_irqrestore(&hga_reg_lock, flags);
288	DPRINTK("hga_pan: base:%d\n", base);
289}
290
291static void hga_blank(int blank_mode)
292{
293	unsigned long flags;
294
295	spin_lock_irqsave(&hga_reg_lock, flags);
296	if (blank_mode) {
297		outb_p(0x00, HGA_MODE_PORT);	/* disable video */
298	} else {
299		outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT);
300	}
301	spin_unlock_irqrestore(&hga_reg_lock, flags);
302}
303
304static int __init hga_card_detect(void)
305{
306	int count=0;
307	unsigned long p, q;
308	unsigned short p_save, q_save;
309
310	hga_vram_base = 0xb0000;
311	hga_vram_len  = 0x08000;
312
313	if (request_region(0x3b0, 12, "hgafb"))
314		release_io_ports = 1;
315	if (request_region(0x3bf, 1, "hgafb"))
316		release_io_port = 1;
317
318	/* do a memory check */
319
320	p = hga_vram_base;
321	q = hga_vram_base + 0x01000;
322
323	p_save = isa_readw(p); q_save = isa_readw(q);
324
325	isa_writew(0xaa55, p); if (isa_readw(p) == 0xaa55) count++;
326	isa_writew(0x55aa, p); if (isa_readw(p) == 0x55aa) count++;
327	isa_writew(p_save, p);
328
329	if (count != 2) {
330		return 0;
331	}
332
333	/* Ok, there is definitely a card registering at the correct
334	 * memory location, so now we do an I/O port test.
335	 */
336
337	if (!test_hga_b(0x66, 0x0f)) {	    /* cursor low register */
338		return 0;
339	}
340	if (!test_hga_b(0x99, 0x0f)) {     /* cursor low register */
341		return 0;
342	}
343
344	/* See if the card is a Hercules, by checking whether the vsync
345	 * bit of the status register is changing.  This test lasts for
346	 * approximately 1/10th of a second.
347	 */
348
349	p_save = q_save = inb_p(HGA_STATUS_PORT) & HGA_STATUS_VSYNC;
350
351	for (count=0; count < 50000 && p_save == q_save; count++) {
352		q_save = inb(HGA_STATUS_PORT) & HGA_STATUS_VSYNC;
353		udelay(2);
354	}
355
356	if (p_save == q_save)
357		return 0;
358
359	switch (inb_p(HGA_STATUS_PORT) & 0x70) {
360		case 0x10:
361			hga_type = TYPE_HERCPLUS;
362			hga_type_name = "HerculesPlus";
363			break;
364		case 0x50:
365			hga_type = TYPE_HERCCOLOR;
366			hga_type_name = "HerculesColor";
367			break;
368		default:
369			hga_type = TYPE_HERC;
370			hga_type_name = "Hercules";
371			break;
372	}
373	return 1;
374}
375
376/* ------------------------------------------------------------------------- *
377 *
378 * dispsw functions
379 *
380 * ------------------------------------------------------------------------- */
381
382/**
383 *	hga_get_fix - get the fixed part of the display
384 *	@fix:struct fb_fix_screeninfo to fill in
385 *	@con:unused
386 *	@info:pointer to fb_info object containing info for current hga board
387 *
388 *	This wrapper function copies @info->fix to @fix.
389 *	A zero is returned on success and %-EINVAL for failure.
390 */
391
392int hga_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
393{
394	CHKINFO(-EINVAL);
395	DPRINTK("hga_get_fix: con:%d, info:%x, fb_info:%x\n", con, (unsigned)info, (unsigned)&fb_info);
396
397	*fix = info->fix;
398	return 0;
399}
400
401/**
402 *	hga_get_var - get the user defined part of the display
403 *	@var:struct fb_var_screeninfo to fill in
404 *	@con:unused
405 *	@info:pointer to fb_info object containing info for current hga board
406 *
407 *	This wrapper function copies @info->var to @var.
408 *	A zero is returned on success and %-EINVAL for failure.
409 */
410
411int hga_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
412{
413	CHKINFO(-EINVAL);
414	DPRINTK("hga_get_var: con:%d, info:%x, fb_info:%x\n", con, (unsigned)info, (unsigned)&fb_info);
415
416	*var = info->var;
417	return 0;
418}
419
420
421int hga_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
422{
423	CHKINFO(-EINVAL);
424	DPRINTK("hga_set_var: con:%d, activate:%x, info:0x%x, fb_info:%x\n", con, var->activate, (unsigned)info, (unsigned)&fb_info);
425
426	if (var->xres != 720 ||	var->yres != 348 ||
427	    var->xres_virtual != 720 ||
428	    var->yres_virtual < 348 || var->yres_virtual > 348 + 16 ||
429	    var->bits_per_pixel != 1 || var->grayscale != 0) {
430		return -EINVAL;
431	}
432	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
433		info->var = *var;
434		if (info->changevar)
435			(*info->changevar)(con);
436	}
437	return 0;
438}
439
440/**
441 *	hga_getcolreg - read color registers
442 *	@regno:register index to read out
443 *	@red:red value
444 *	@green:green value
445 *	@blue:blue value
446 *	@transp:transparency value
447 *	@info:unused
448 *
449 *	This callback function is used to read the color registers of a HGA
450 *	board. Since we have only two fixed colors, RGB values are 0x0000
451 *	for register0 and 0xaaaa for register1.
452 *	A zero is returned on success and 1 for failure.
453 */
454
455static int hga_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
456			 u_int *transp, struct fb_info *info)
457{
458	if (regno == 0) {
459		*red = *green = *blue = 0x0000;
460		*transp = 0;
461	} else if (regno == 1) {
462		*red = *green = *blue = 0xaaaa;
463		*transp = 0;
464	} else
465		return 1;
466	return 0;
467}
468
469/**
470 *	hga_get_cmap - get the colormap
471 *	@cmap:struct fb_cmap to fill in
472 *	@kspc:called from kernel space?
473 *	@con:unused
474 *	@info:pointer to fb_info object containing info for current hga board
475 *
476 *	This wrapper function passes it's input parameters to fb_get_cmap().
477 *	Callback function hga_getcolreg() is used to read the color registers.
478 */
479
480int hga_get_cmap(struct fb_cmap *cmap, int kspc, int con,
481                 struct fb_info *info)
482{
483	CHKINFO(-EINVAL);
484	DPRINTK("hga_get_cmap: con:%d\n", con);
485	return fb_get_cmap(cmap, kspc, hga_getcolreg, info);
486}
487
488/**
489 *	hga_setcolreg - set color registers
490 *	@regno:register index to set
491 *	@red:red value, unused
492 *	@green:green value, unused
493 *	@blue:blue value, unused
494 *	@transp:transparency value, unused
495 *	@info:unused
496 *
497 *	This callback function is used to set the color registers of a HGA
498 *	board. Since we have only two fixed colors only @regno is checked.
499 *	A zero is returned on success and 1 for failure.
500 */
501
502static int hga_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
503			 u_int transp, struct fb_info *info)
504{
505	if (regno > 1)
506		return 1;
507	return 0;
508}
509
510/**
511 *	hga_set_cmap - set the colormap
512 *	@cmap:struct fb_cmap to set
513 *	@kspc:called from kernel space?
514 *	@con:unused
515 *	@info:pointer to fb_info object containing info for current hga board
516 *
517 *	This wrapper function passes it's input parameters to fb_set_cmap().
518 *	Callback function hga_setcolreg() is used to set the color registers.
519 */
520
521int hga_set_cmap(struct fb_cmap *cmap, int kspc, int con,
522                 struct fb_info *info)
523{
524	CHKINFO(-EINVAL);
525	DPRINTK("hga_set_cmap: con:%d\n", con);
526	return fb_set_cmap(cmap, kspc, hga_setcolreg, info);
527}
528
529/**
530 *	hga_pan_display - pan or wrap the display
531 *	@var:contains new xoffset, yoffset and vmode values
532 *	@con:unused
533 *	@info:pointer to fb_info object containing info for current hga board
534 *
535 *	This function looks only at xoffset, yoffset and the %FB_VMODE_YWRAP
536 *	flag in @var. If input parameters are correct it calls hga_pan() to
537 *	program the hardware. @info->var is updated to the new values.
538 *	A zero is returned on success and %-EINVAL for failure.
539 */
540
541int hga_pan_display(struct fb_var_screeninfo *var, int con,
542                    struct fb_info *info)
543{
544	CHKINFO(-EINVAL);
545	DPRINTK("pan_disp: con:%d, wrap:%d, xoff:%d, yoff:%d\n", con, var->vmode & FB_VMODE_YWRAP, var->xoffset, var->yoffset);
546
547	if (var->vmode & FB_VMODE_YWRAP) {
548		if (var->yoffset < 0 ||
549		    var->yoffset >= info->var.yres_virtual ||
550		    var->xoffset)
551			return -EINVAL;
552	} else {
553		if (var->xoffset + var->xres > info->var.xres_virtual
554		 || var->yoffset + var->yres > info->var.yres_virtual
555		 || var->yoffset % 8)
556			return -EINVAL;
557	}
558
559	hga_pan(var->xoffset, var->yoffset);
560
561	info->var.xoffset = var->xoffset;
562	info->var.yoffset = var->yoffset;
563	if (var->vmode & FB_VMODE_YWRAP)
564		info->var.vmode |= FB_VMODE_YWRAP;
565	else
566		info->var.vmode &= ~FB_VMODE_YWRAP;
567	return 0;
568}
569
570
571static struct fb_ops hgafb_ops = {
572	owner:		THIS_MODULE,
573	fb_get_fix:	hga_get_fix,
574	fb_get_var:	hga_get_var,
575	fb_set_var:	hga_set_var,
576	fb_get_cmap:	hga_get_cmap,
577	fb_set_cmap:	hga_set_cmap,
578	fb_pan_display:	hga_pan_display,
579};
580
581
582/* ------------------------------------------------------------------------- *
583 *
584 * Functions in fb_info
585 *
586 * ------------------------------------------------------------------------- */
587
588/**
589 *	hgafbcon_switch - switch console
590 *	@con:new console to switch to
591 *	@info:pointer to fb_info object containing info for current hga board
592 *
593 *	This function should install a new colormap and change the video mode.
594 *	Since we have fixed colors and only one video mode we have nothing to
595 *	do.
596 *	Only console administration is done but it should go to fbcon.c IMHO.
597 *	A zero is returned on success and %-EINVAL for failure.
598 */
599
600static int hgafbcon_switch(int con, struct fb_info *info)
601{
602	CHKINFO(-EINVAL);
603	DPRINTK("hgafbcon_switch: currcon:%d, con:%d, info:%x, fb_info:%x\n", currcon, con, (unsigned)info, (unsigned)&fb_info);
604
605	/* Save the colormap and video mode */
606
607	if (currcon != -1) /* this check is absolute necessary! */
608		memcpy(&fb_display[currcon].var, &info->var,
609				sizeof(struct fb_var_screeninfo));
610
611	/* Install a new colormap and change the video mode. By default fbcon
612	 * sets all the colormaps and video modes to the default values at
613	 * bootup.
614	 */
615
616	memcpy(&info->var, &fb_display[con].var,
617			sizeof(struct fb_var_screeninfo));
618	/* hga_set_var(&info->var, con, &fb_info); is it necessary? */
619	currcon = con;
620
621	/* Hack to work correctly with XF86_Mono */
622	hga_gfx_mode();
623	return 0;
624}
625
626/**
627 *	hgafbcon_updatevar - update the user defined part of the display
628 *	@con:console to update or -1 when no consoles defined on this fb
629 *	@info:pointer to fb_info object containing info for current hga board
630 *
631 *	This function is called when @var is changed by fbcon.c without calling
632 *	hga_set_var(). It usually means scrolling.  hga_pan_display() is called
633 *	to update the hardware and @info->var.
634 *	A zero is returned on success and %-EINVAL for failure.
635 */
636
637static int hgafbcon_updatevar(int con, struct fb_info *info)
638{
639	CHKINFO(-EINVAL);
640	DPRINTK("hga_update_var: con:%d, info:%x, fb_info:%x\n", con, (unsigned)info, (unsigned)&fb_info);
641	return (con < 0) ? -EINVAL : hga_pan_display(&fb_display[con].var, con, info);
642}
643
644/**
645 *	hgafbcon_blank - (un)blank the screen
646 *	@blank_mode:blanking method to use
647 *	@info:unused
648 *
649 *	Blank the screen if blank_mode != 0, else unblank.
650 *	Implements VESA suspend and powerdown modes on hardware that supports
651 *	disabling hsync/vsync:
652 *		@blank_mode == 2 means suspend vsync,
653 *		@blank_mode == 3 means suspend hsync,
654 *		@blank_mode == 4 means powerdown.
655 */
656
657static void hgafbcon_blank(int blank_mode, struct fb_info *info)
658{
659	CHKINFO( );
660	DPRINTK("hga_blank: blank_mode:%d, info:%x, fb_info:%x\n", blank_mode, (unsigned)info, (unsigned)&fb_info);
661
662	hga_blank(blank_mode);
663}
664
665
666/* ------------------------------------------------------------------------- */
667
668	/*
669	 *  Initialization
670	 */
671
672int __init hgafb_init(void)
673{
674	if (! hga_card_detect()) {
675		printk(KERN_ERR "hgafb: HGA card not detected.\n");
676		return -EINVAL;
677	}
678
679	printk(KERN_INFO "hgafb: %s with %ldK of memory detected.\n",
680		hga_type_name, hga_vram_len/1024);
681
682	hga_gfx_mode();
683	hga_clear_screen();
684#ifdef MODULE
685	if (!nologo) hga_show_logo();
686#endif /* MODULE */
687
688	hga_fix.smem_start = VGA_MAP_MEM(hga_vram_base);
689	hga_fix.smem_len = hga_vram_len;
690
691	disp.var = hga_default_var;
692/*	disp.cmap = ???; */
693	disp.screen_base = (char*)hga_fix.smem_start;
694	disp.visual = hga_fix.visual;
695	disp.type = hga_fix.type;
696	disp.type_aux = hga_fix.type_aux;
697	disp.ypanstep = hga_fix.ypanstep;
698	disp.ywrapstep = hga_fix.ywrapstep;
699	disp.line_length = hga_fix.line_length;
700	disp.can_soft_blank = 1;
701	disp.inverse = 0;
702#ifdef FBCON_HAS_HGA
703	disp.dispsw = &fbcon_hga;
704#else
705#warning HGAFB will not work as a console!
706	disp.dispsw = &fbcon_dummy;
707#endif
708	disp.dispsw_data = NULL;
709
710	disp.scrollmode = SCROLL_YREDRAW;
711
712	strcpy (fb_info.modename, hga_fix.id);
713	fb_info.node = -1;
714	fb_info.flags = FBINFO_FLAG_DEFAULT;
715/*	fb_info.open = ??? */
716	fb_info.var = hga_default_var;
717	fb_info.fix = hga_fix;
718	fb_info.monspecs.hfmin = 0;
719	fb_info.monspecs.hfmax = 0;
720	fb_info.monspecs.vfmin = 10000;
721	fb_info.monspecs.vfmax = 10000;
722	fb_info.monspecs.dpms = 0;
723	fb_info.fbops = &hgafb_ops;
724	fb_info.screen_base = (char *)hga_fix.smem_start;
725	fb_info.disp = &disp;
726/*	fb_info.display_fg = ??? */
727/*	fb_info.fontname initialized later */
728	fb_info.changevar = NULL;
729	fb_info.switch_con = hgafbcon_switch;
730	fb_info.updatevar = hgafbcon_updatevar;
731	fb_info.blank = hgafbcon_blank;
732	fb_info.pseudo_palette = NULL; /* ??? */
733	fb_info.par = NULL;
734
735        if (register_framebuffer(&fb_info) < 0)
736                return -EINVAL;
737
738        printk(KERN_INFO "fb%d: %s frame buffer device\n",
739               GET_FB_IDX(fb_info.node), fb_info.modename);
740
741	return 0;
742}
743
744	/*
745	 *  Setup
746	 */
747
748#ifndef MODULE
749int __init hgafb_setup(char *options)
750{
751	/*
752	 * Parse user speficied options
753	 * `video=hga:font:VGA8x16' or
754	 * `video=hga:font:SUN8x16' recommended
755	 * Other supported fonts: VGA8x8, Acorn8x8, PEARL8x8
756	 * More different fonts can be used with the `setfont' utility.
757	 */
758
759	char *this_opt;
760
761	fb_info.fontname[0] = '\0';
762
763	if (!options || !*options)
764		return 0;
765
766	while ((this_opt = strsep(&options, ","))) {
767		if (!strncmp(this_opt, "font:", 5))
768			strcpy(fb_info.fontname, this_opt+5);
769	}
770	return 0;
771}
772#endif /* !MODULE */
773
774
775	/*
776	 * Cleanup
777	 */
778
779#ifdef MODULE
780static void hgafb_cleanup(struct fb_info *info)
781{
782	hga_txt_mode();
783	hga_clear_screen();
784	unregister_framebuffer(info);
785	if (release_io_ports) release_region(0x3b0, 12);
786	if (release_io_port) release_region(0x3bf, 1);
787}
788#endif /* MODULE */
789
790
791
792/* -------------------------------------------------------------------------
793 *
794 *  Modularization
795 *
796 * ------------------------------------------------------------------------- */
797
798#ifdef MODULE
799int init_module(void)
800{
801	if (font)
802		strncpy(fb_info.fontname, font, sizeof(fb_info.fontname)-1);
803	else
804		fb_info.fontname[0] = '\0';
805
806	return hgafb_init();
807}
808
809void cleanup_module(void)
810{
811	hgafb_cleanup(&fb_info);
812}
813
814MODULE_AUTHOR("Ferenc Bakonyi (fero@drama.obuda.kando.hu)");
815MODULE_DESCRIPTION("FBDev driver for Hercules Graphics Adaptor");
816MODULE_LICENSE("GPL");
817
818MODULE_PARM(font, "s");
819MODULE_PARM_DESC(font, "Specifies one of the compiled-in fonts (VGA8x8, VGA8x16, SUN8x16, Acorn8x8, PEARL8x8) (default=none)");
820MODULE_PARM(nologo, "i");
821MODULE_PARM_DESC(nologo, "Disables startup logo if != 0 (default=0)");
822
823#endif /* MODULE */
824