xref: /OK3568_Linux_fs/kernel/drivers/video/fbdev/hgafb.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * linux/drivers/video/hgafb.c -- Hercules graphics adaptor frame buffer device
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  *      Created 25 Nov 1999 by Ferenc Bakonyi (fero@drama.obuda.kando.hu)
5*4882a593Smuzhiyun  *      Based on skeletonfb.c by Geert Uytterhoeven and
6*4882a593Smuzhiyun  *               mdacon.c by Andrew Apted
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * History:
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  * - Revision 0.1.8 (23 Oct 2002): Ported to new framebuffer api.
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  * - Revision 0.1.7 (23 Jan 2001): fix crash resulting from MDA only cards
13*4882a593Smuzhiyun  *				   being detected as Hercules.	 (Paul G.)
14*4882a593Smuzhiyun  * - Revision 0.1.6 (17 Aug 2000): new style structs
15*4882a593Smuzhiyun  *                                 documentation
16*4882a593Smuzhiyun  * - Revision 0.1.5 (13 Mar 2000): spinlocks instead of saveflags();cli();etc
17*4882a593Smuzhiyun  *                                 minor fixes
18*4882a593Smuzhiyun  * - Revision 0.1.4 (24 Jan 2000): fixed a bug in hga_card_detect() for
19*4882a593Smuzhiyun  *                                  HGA-only systems
20*4882a593Smuzhiyun  * - Revision 0.1.3 (22 Jan 2000): modified for the new fb_info structure
21*4882a593Smuzhiyun  *                                 screen is cleared after rmmod
22*4882a593Smuzhiyun  *                                 virtual resolutions
23*4882a593Smuzhiyun  *                                 module parameter 'nologo={0|1}'
24*4882a593Smuzhiyun  *                                 the most important: boot logo :)
25*4882a593Smuzhiyun  * - Revision 0.1.0  (6 Dec 1999): faster scrolling and minor fixes
26*4882a593Smuzhiyun  * - First release  (25 Nov 1999)
27*4882a593Smuzhiyun  *
28*4882a593Smuzhiyun  * This file is subject to the terms and conditions of the GNU General Public
29*4882a593Smuzhiyun  * License.  See the file COPYING in the main directory of this archive
30*4882a593Smuzhiyun  * for more details.
31*4882a593Smuzhiyun  */
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun #include <linux/module.h>
34*4882a593Smuzhiyun #include <linux/kernel.h>
35*4882a593Smuzhiyun #include <linux/errno.h>
36*4882a593Smuzhiyun #include <linux/spinlock.h>
37*4882a593Smuzhiyun #include <linux/string.h>
38*4882a593Smuzhiyun #include <linux/mm.h>
39*4882a593Smuzhiyun #include <linux/delay.h>
40*4882a593Smuzhiyun #include <linux/fb.h>
41*4882a593Smuzhiyun #include <linux/init.h>
42*4882a593Smuzhiyun #include <linux/ioport.h>
43*4882a593Smuzhiyun #include <linux/platform_device.h>
44*4882a593Smuzhiyun #include <asm/io.h>
45*4882a593Smuzhiyun #include <asm/vga.h>
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun #if 0
48*4882a593Smuzhiyun #define DPRINTK(args...) printk(KERN_DEBUG __FILE__": " ##args)
49*4882a593Smuzhiyun #else
50*4882a593Smuzhiyun #define DPRINTK(args...)
51*4882a593Smuzhiyun #endif
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun #if 0
54*4882a593Smuzhiyun #define CHKINFO(ret) if (info != &fb_info) { printk(KERN_DEBUG __FILE__": This should never happen, line:%d \n", __LINE__); return ret; }
55*4882a593Smuzhiyun #else
56*4882a593Smuzhiyun #define CHKINFO(ret)
57*4882a593Smuzhiyun #endif
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun /* Description of the hardware layout */
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun static void __iomem *hga_vram;			/* Base of video memory */
62*4882a593Smuzhiyun static unsigned long hga_vram_len;		/* Size of video memory */
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun #define HGA_ROWADDR(row) ((row%4)*8192 + (row>>2)*90)
65*4882a593Smuzhiyun #define HGA_TXT			0
66*4882a593Smuzhiyun #define HGA_GFX			1
67*4882a593Smuzhiyun 
rowaddr(struct fb_info * info,u_int row)68*4882a593Smuzhiyun static inline u8 __iomem * rowaddr(struct fb_info *info, u_int row)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun 	return info->screen_base + HGA_ROWADDR(row);
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun static int hga_mode = -1;			/* 0 = txt, 1 = gfx mode */
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun static enum { TYPE_HERC, TYPE_HERCPLUS, TYPE_HERCCOLOR } hga_type;
76*4882a593Smuzhiyun static char *hga_type_name;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun #define HGA_INDEX_PORT		0x3b4		/* Register select port */
79*4882a593Smuzhiyun #define HGA_VALUE_PORT		0x3b5		/* Register value port */
80*4882a593Smuzhiyun #define HGA_MODE_PORT		0x3b8		/* Mode control port */
81*4882a593Smuzhiyun #define HGA_STATUS_PORT		0x3ba		/* Status and Config port */
82*4882a593Smuzhiyun #define HGA_GFX_PORT		0x3bf		/* Graphics control port */
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun /* HGA register values */
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun #define HGA_CURSOR_BLINKING	0x00
87*4882a593Smuzhiyun #define HGA_CURSOR_OFF		0x20
88*4882a593Smuzhiyun #define HGA_CURSOR_SLOWBLINK	0x60
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun #define HGA_MODE_GRAPHICS	0x02
91*4882a593Smuzhiyun #define HGA_MODE_VIDEO_EN	0x08
92*4882a593Smuzhiyun #define HGA_MODE_BLINK_EN	0x20
93*4882a593Smuzhiyun #define HGA_MODE_GFX_PAGE1	0x80
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun #define HGA_STATUS_HSYNC	0x01
96*4882a593Smuzhiyun #define HGA_STATUS_VSYNC	0x80
97*4882a593Smuzhiyun #define HGA_STATUS_VIDEO	0x08
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun #define HGA_CONFIG_COL132	0x08
100*4882a593Smuzhiyun #define HGA_GFX_MODE_EN		0x01
101*4882a593Smuzhiyun #define HGA_GFX_PAGE_EN		0x02
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun /* Global locks */
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun static DEFINE_SPINLOCK(hga_reg_lock);
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun /* Framebuffer driver structures */
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun static const struct fb_var_screeninfo hga_default_var = {
110*4882a593Smuzhiyun 	.xres		= 720,
111*4882a593Smuzhiyun 	.yres 		= 348,
112*4882a593Smuzhiyun 	.xres_virtual 	= 720,
113*4882a593Smuzhiyun 	.yres_virtual	= 348,
114*4882a593Smuzhiyun 	.bits_per_pixel = 1,
115*4882a593Smuzhiyun 	.red 		= {0, 1, 0},
116*4882a593Smuzhiyun 	.green 		= {0, 1, 0},
117*4882a593Smuzhiyun 	.blue 		= {0, 1, 0},
118*4882a593Smuzhiyun 	.transp 	= {0, 0, 0},
119*4882a593Smuzhiyun 	.height 	= -1,
120*4882a593Smuzhiyun 	.width 		= -1,
121*4882a593Smuzhiyun };
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun static struct fb_fix_screeninfo hga_fix = {
124*4882a593Smuzhiyun 	.id 		= "HGA",
125*4882a593Smuzhiyun 	.type 		= FB_TYPE_PACKED_PIXELS,	/* (not sure) */
126*4882a593Smuzhiyun 	.visual 	= FB_VISUAL_MONO10,
127*4882a593Smuzhiyun 	.xpanstep 	= 8,
128*4882a593Smuzhiyun 	.ypanstep 	= 8,
129*4882a593Smuzhiyun 	.line_length 	= 90,
130*4882a593Smuzhiyun 	.accel 		= FB_ACCEL_NONE
131*4882a593Smuzhiyun };
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun /* Don't assume that tty1 will be the initial current console. */
134*4882a593Smuzhiyun static int release_io_port = 0;
135*4882a593Smuzhiyun static int release_io_ports = 0;
136*4882a593Smuzhiyun static bool nologo = 0;
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun /* -------------------------------------------------------------------------
139*4882a593Smuzhiyun  *
140*4882a593Smuzhiyun  * Low level hardware functions
141*4882a593Smuzhiyun  *
142*4882a593Smuzhiyun  * ------------------------------------------------------------------------- */
143*4882a593Smuzhiyun 
write_hga_b(unsigned int val,unsigned char reg)144*4882a593Smuzhiyun static void write_hga_b(unsigned int val, unsigned char reg)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun 	outb_p(reg, HGA_INDEX_PORT);
147*4882a593Smuzhiyun 	outb_p(val, HGA_VALUE_PORT);
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun 
write_hga_w(unsigned int val,unsigned char reg)150*4882a593Smuzhiyun static void write_hga_w(unsigned int val, unsigned char reg)
151*4882a593Smuzhiyun {
152*4882a593Smuzhiyun 	outb_p(reg,   HGA_INDEX_PORT); outb_p(val >> 8,   HGA_VALUE_PORT);
153*4882a593Smuzhiyun 	outb_p(reg+1, HGA_INDEX_PORT); outb_p(val & 0xff, HGA_VALUE_PORT);
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun 
test_hga_b(unsigned char val,unsigned char reg)156*4882a593Smuzhiyun static int test_hga_b(unsigned char val, unsigned char reg)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun 	outb_p(reg, HGA_INDEX_PORT);
159*4882a593Smuzhiyun 	outb  (val, HGA_VALUE_PORT);
160*4882a593Smuzhiyun 	udelay(20); val = (inb_p(HGA_VALUE_PORT) == val);
161*4882a593Smuzhiyun 	return val;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun 
hga_clear_screen(void)164*4882a593Smuzhiyun static void hga_clear_screen(void)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun 	unsigned char fillchar = 0xbf; /* magic */
167*4882a593Smuzhiyun 	unsigned long flags;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	spin_lock_irqsave(&hga_reg_lock, flags);
170*4882a593Smuzhiyun 	if (hga_mode == HGA_TXT)
171*4882a593Smuzhiyun 		fillchar = ' ';
172*4882a593Smuzhiyun 	else if (hga_mode == HGA_GFX)
173*4882a593Smuzhiyun 		fillchar = 0x00;
174*4882a593Smuzhiyun 	spin_unlock_irqrestore(&hga_reg_lock, flags);
175*4882a593Smuzhiyun 	if (fillchar != 0xbf)
176*4882a593Smuzhiyun 		memset_io(hga_vram, fillchar, hga_vram_len);
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun 
hga_txt_mode(void)179*4882a593Smuzhiyun static void hga_txt_mode(void)
180*4882a593Smuzhiyun {
181*4882a593Smuzhiyun 	unsigned long flags;
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	spin_lock_irqsave(&hga_reg_lock, flags);
184*4882a593Smuzhiyun 	outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_BLINK_EN, HGA_MODE_PORT);
185*4882a593Smuzhiyun 	outb_p(0x00, HGA_GFX_PORT);
186*4882a593Smuzhiyun 	outb_p(0x00, HGA_STATUS_PORT);
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	write_hga_b(0x61, 0x00);	/* horizontal total */
189*4882a593Smuzhiyun 	write_hga_b(0x50, 0x01);	/* horizontal displayed */
190*4882a593Smuzhiyun 	write_hga_b(0x52, 0x02);	/* horizontal sync pos */
191*4882a593Smuzhiyun 	write_hga_b(0x0f, 0x03);	/* horizontal sync width */
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	write_hga_b(0x19, 0x04);	/* vertical total */
194*4882a593Smuzhiyun 	write_hga_b(0x06, 0x05);	/* vertical total adjust */
195*4882a593Smuzhiyun 	write_hga_b(0x19, 0x06);	/* vertical displayed */
196*4882a593Smuzhiyun 	write_hga_b(0x19, 0x07);	/* vertical sync pos */
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	write_hga_b(0x02, 0x08);	/* interlace mode */
199*4882a593Smuzhiyun 	write_hga_b(0x0d, 0x09);	/* maximum scanline */
200*4882a593Smuzhiyun 	write_hga_b(0x0c, 0x0a);	/* cursor start */
201*4882a593Smuzhiyun 	write_hga_b(0x0d, 0x0b);	/* cursor end */
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	write_hga_w(0x0000, 0x0c);	/* start address */
204*4882a593Smuzhiyun 	write_hga_w(0x0000, 0x0e);	/* cursor location */
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	hga_mode = HGA_TXT;
207*4882a593Smuzhiyun 	spin_unlock_irqrestore(&hga_reg_lock, flags);
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun 
hga_gfx_mode(void)210*4882a593Smuzhiyun static void hga_gfx_mode(void)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun 	unsigned long flags;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	spin_lock_irqsave(&hga_reg_lock, flags);
215*4882a593Smuzhiyun 	outb_p(0x00, HGA_STATUS_PORT);
216*4882a593Smuzhiyun 	outb_p(HGA_GFX_MODE_EN, HGA_GFX_PORT);
217*4882a593Smuzhiyun 	outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT);
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	write_hga_b(0x35, 0x00);	/* horizontal total */
220*4882a593Smuzhiyun 	write_hga_b(0x2d, 0x01);	/* horizontal displayed */
221*4882a593Smuzhiyun 	write_hga_b(0x2e, 0x02);	/* horizontal sync pos */
222*4882a593Smuzhiyun 	write_hga_b(0x07, 0x03);	/* horizontal sync width */
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	write_hga_b(0x5b, 0x04);	/* vertical total */
225*4882a593Smuzhiyun 	write_hga_b(0x02, 0x05);	/* vertical total adjust */
226*4882a593Smuzhiyun 	write_hga_b(0x57, 0x06);	/* vertical displayed */
227*4882a593Smuzhiyun 	write_hga_b(0x57, 0x07);	/* vertical sync pos */
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	write_hga_b(0x02, 0x08);	/* interlace mode */
230*4882a593Smuzhiyun 	write_hga_b(0x03, 0x09);	/* maximum scanline */
231*4882a593Smuzhiyun 	write_hga_b(0x00, 0x0a);	/* cursor start */
232*4882a593Smuzhiyun 	write_hga_b(0x00, 0x0b);	/* cursor end */
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	write_hga_w(0x0000, 0x0c);	/* start address */
235*4882a593Smuzhiyun 	write_hga_w(0x0000, 0x0e);	/* cursor location */
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	hga_mode = HGA_GFX;
238*4882a593Smuzhiyun 	spin_unlock_irqrestore(&hga_reg_lock, flags);
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun 
hga_show_logo(struct fb_info * info)241*4882a593Smuzhiyun static void hga_show_logo(struct fb_info *info)
242*4882a593Smuzhiyun {
243*4882a593Smuzhiyun /*
244*4882a593Smuzhiyun 	void __iomem *dest = hga_vram;
245*4882a593Smuzhiyun 	char *logo = linux_logo_bw;
246*4882a593Smuzhiyun 	int x, y;
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	for (y = 134; y < 134 + 80 ; y++) * this needs some cleanup *
249*4882a593Smuzhiyun 		for (x = 0; x < 10 ; x++)
250*4882a593Smuzhiyun 			writeb(~*(logo++),(dest + HGA_ROWADDR(y) + x + 40));
251*4882a593Smuzhiyun */
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun 
hga_pan(unsigned int xoffset,unsigned int yoffset)254*4882a593Smuzhiyun static void hga_pan(unsigned int xoffset, unsigned int yoffset)
255*4882a593Smuzhiyun {
256*4882a593Smuzhiyun 	unsigned int base;
257*4882a593Smuzhiyun 	unsigned long flags;
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	base = (yoffset / 8) * 90 + xoffset;
260*4882a593Smuzhiyun 	spin_lock_irqsave(&hga_reg_lock, flags);
261*4882a593Smuzhiyun 	write_hga_w(base, 0x0c);	/* start address */
262*4882a593Smuzhiyun 	spin_unlock_irqrestore(&hga_reg_lock, flags);
263*4882a593Smuzhiyun 	DPRINTK("hga_pan: base:%d\n", base);
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun 
hga_blank(int blank_mode)266*4882a593Smuzhiyun static void hga_blank(int blank_mode)
267*4882a593Smuzhiyun {
268*4882a593Smuzhiyun 	unsigned long flags;
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	spin_lock_irqsave(&hga_reg_lock, flags);
271*4882a593Smuzhiyun 	if (blank_mode) {
272*4882a593Smuzhiyun 		outb_p(0x00, HGA_MODE_PORT);	/* disable video */
273*4882a593Smuzhiyun 	} else {
274*4882a593Smuzhiyun 		outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT);
275*4882a593Smuzhiyun 	}
276*4882a593Smuzhiyun 	spin_unlock_irqrestore(&hga_reg_lock, flags);
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun 
hga_card_detect(void)279*4882a593Smuzhiyun static int hga_card_detect(void)
280*4882a593Smuzhiyun {
281*4882a593Smuzhiyun 	int count = 0;
282*4882a593Smuzhiyun 	void __iomem *p, *q;
283*4882a593Smuzhiyun 	unsigned short p_save, q_save;
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	hga_vram_len  = 0x08000;
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	hga_vram = ioremap(0xb0000, hga_vram_len);
288*4882a593Smuzhiyun 	if (!hga_vram)
289*4882a593Smuzhiyun 		return -ENOMEM;
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	if (request_region(0x3b0, 12, "hgafb"))
292*4882a593Smuzhiyun 		release_io_ports = 1;
293*4882a593Smuzhiyun 	if (request_region(0x3bf, 1, "hgafb"))
294*4882a593Smuzhiyun 		release_io_port = 1;
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	/* do a memory check */
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	p = hga_vram;
299*4882a593Smuzhiyun 	q = hga_vram + 0x01000;
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	p_save = readw(p); q_save = readw(q);
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 	writew(0xaa55, p); if (readw(p) == 0xaa55) count++;
304*4882a593Smuzhiyun 	writew(0x55aa, p); if (readw(p) == 0x55aa) count++;
305*4882a593Smuzhiyun 	writew(p_save, p);
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	if (count != 2)
308*4882a593Smuzhiyun 		goto error;
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	/* Ok, there is definitely a card registering at the correct
311*4882a593Smuzhiyun 	 * memory location, so now we do an I/O port test.
312*4882a593Smuzhiyun 	 */
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	if (!test_hga_b(0x66, 0x0f))	    /* cursor low register */
315*4882a593Smuzhiyun 		goto error;
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	if (!test_hga_b(0x99, 0x0f))     /* cursor low register */
318*4882a593Smuzhiyun 		goto error;
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	/* See if the card is a Hercules, by checking whether the vsync
321*4882a593Smuzhiyun 	 * bit of the status register is changing.  This test lasts for
322*4882a593Smuzhiyun 	 * approximately 1/10th of a second.
323*4882a593Smuzhiyun 	 */
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun 	p_save = q_save = inb_p(HGA_STATUS_PORT) & HGA_STATUS_VSYNC;
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	for (count=0; count < 50000 && p_save == q_save; count++) {
328*4882a593Smuzhiyun 		q_save = inb(HGA_STATUS_PORT) & HGA_STATUS_VSYNC;
329*4882a593Smuzhiyun 		udelay(2);
330*4882a593Smuzhiyun 	}
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	if (p_save == q_save)
333*4882a593Smuzhiyun 		goto error;
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	switch (inb_p(HGA_STATUS_PORT) & 0x70) {
336*4882a593Smuzhiyun 		case 0x10:
337*4882a593Smuzhiyun 			hga_type = TYPE_HERCPLUS;
338*4882a593Smuzhiyun 			hga_type_name = "HerculesPlus";
339*4882a593Smuzhiyun 			break;
340*4882a593Smuzhiyun 		case 0x50:
341*4882a593Smuzhiyun 			hga_type = TYPE_HERCCOLOR;
342*4882a593Smuzhiyun 			hga_type_name = "HerculesColor";
343*4882a593Smuzhiyun 			break;
344*4882a593Smuzhiyun 		default:
345*4882a593Smuzhiyun 			hga_type = TYPE_HERC;
346*4882a593Smuzhiyun 			hga_type_name = "Hercules";
347*4882a593Smuzhiyun 			break;
348*4882a593Smuzhiyun 	}
349*4882a593Smuzhiyun 	return 0;
350*4882a593Smuzhiyun error:
351*4882a593Smuzhiyun 	if (release_io_ports)
352*4882a593Smuzhiyun 		release_region(0x3b0, 12);
353*4882a593Smuzhiyun 	if (release_io_port)
354*4882a593Smuzhiyun 		release_region(0x3bf, 1);
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	iounmap(hga_vram);
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 	pr_err("hgafb: HGA card not detected.\n");
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	return -EINVAL;
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun /**
364*4882a593Smuzhiyun  *	hgafb_open - open the framebuffer device
365*4882a593Smuzhiyun  *	@info:pointer to fb_info object containing info for current hga board
366*4882a593Smuzhiyun  *	@int:open by console system or userland.
367*4882a593Smuzhiyun  */
368*4882a593Smuzhiyun 
hgafb_open(struct fb_info * info,int init)369*4882a593Smuzhiyun static int hgafb_open(struct fb_info *info, int init)
370*4882a593Smuzhiyun {
371*4882a593Smuzhiyun 	hga_gfx_mode();
372*4882a593Smuzhiyun 	hga_clear_screen();
373*4882a593Smuzhiyun 	if (!nologo) hga_show_logo(info);
374*4882a593Smuzhiyun 	return 0;
375*4882a593Smuzhiyun }
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun /**
378*4882a593Smuzhiyun  *	hgafb_open - open the framebuffer device
379*4882a593Smuzhiyun  *	@info:pointer to fb_info object containing info for current hga board
380*4882a593Smuzhiyun  *	@int:open by console system or userland.
381*4882a593Smuzhiyun  */
382*4882a593Smuzhiyun 
hgafb_release(struct fb_info * info,int init)383*4882a593Smuzhiyun static int hgafb_release(struct fb_info *info, int init)
384*4882a593Smuzhiyun {
385*4882a593Smuzhiyun 	hga_txt_mode();
386*4882a593Smuzhiyun 	hga_clear_screen();
387*4882a593Smuzhiyun 	return 0;
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun /**
391*4882a593Smuzhiyun  *	hgafb_setcolreg - set color registers
392*4882a593Smuzhiyun  *	@regno:register index to set
393*4882a593Smuzhiyun  *	@red:red value, unused
394*4882a593Smuzhiyun  *	@green:green value, unused
395*4882a593Smuzhiyun  *	@blue:blue value, unused
396*4882a593Smuzhiyun  *	@transp:transparency value, unused
397*4882a593Smuzhiyun  *	@info:unused
398*4882a593Smuzhiyun  *
399*4882a593Smuzhiyun  *	This callback function is used to set the color registers of a HGA
400*4882a593Smuzhiyun  *	board. Since we have only two fixed colors only @regno is checked.
401*4882a593Smuzhiyun  *	A zero is returned on success and 1 for failure.
402*4882a593Smuzhiyun  */
403*4882a593Smuzhiyun 
hgafb_setcolreg(u_int regno,u_int red,u_int green,u_int blue,u_int transp,struct fb_info * info)404*4882a593Smuzhiyun static int hgafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
405*4882a593Smuzhiyun 			   u_int transp, struct fb_info *info)
406*4882a593Smuzhiyun {
407*4882a593Smuzhiyun 	if (regno > 1)
408*4882a593Smuzhiyun 		return 1;
409*4882a593Smuzhiyun 	return 0;
410*4882a593Smuzhiyun }
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun /**
413*4882a593Smuzhiyun  *	hga_pan_display - pan or wrap the display
414*4882a593Smuzhiyun  *	@var:contains new xoffset, yoffset and vmode values
415*4882a593Smuzhiyun  *	@info:pointer to fb_info object containing info for current hga board
416*4882a593Smuzhiyun  *
417*4882a593Smuzhiyun  *	This function looks only at xoffset, yoffset and the %FB_VMODE_YWRAP
418*4882a593Smuzhiyun  *	flag in @var. If input parameters are correct it calls hga_pan() to
419*4882a593Smuzhiyun  *	program the hardware. @info->var is updated to the new values.
420*4882a593Smuzhiyun  *	A zero is returned on success and %-EINVAL for failure.
421*4882a593Smuzhiyun  */
422*4882a593Smuzhiyun 
hgafb_pan_display(struct fb_var_screeninfo * var,struct fb_info * info)423*4882a593Smuzhiyun static int hgafb_pan_display(struct fb_var_screeninfo *var,
424*4882a593Smuzhiyun 			     struct fb_info *info)
425*4882a593Smuzhiyun {
426*4882a593Smuzhiyun 	if (var->vmode & FB_VMODE_YWRAP) {
427*4882a593Smuzhiyun 		if (var->yoffset >= info->var.yres_virtual ||
428*4882a593Smuzhiyun 		    var->xoffset)
429*4882a593Smuzhiyun 			return -EINVAL;
430*4882a593Smuzhiyun 	} else {
431*4882a593Smuzhiyun 		if (var->xoffset + info->var.xres > info->var.xres_virtual
432*4882a593Smuzhiyun 		 || var->yoffset + info->var.yres > info->var.yres_virtual
433*4882a593Smuzhiyun 		 || var->yoffset % 8)
434*4882a593Smuzhiyun 			return -EINVAL;
435*4882a593Smuzhiyun 	}
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	hga_pan(var->xoffset, var->yoffset);
438*4882a593Smuzhiyun 	return 0;
439*4882a593Smuzhiyun }
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun /**
442*4882a593Smuzhiyun  *	hgafb_blank - (un)blank the screen
443*4882a593Smuzhiyun  *	@blank_mode:blanking method to use
444*4882a593Smuzhiyun  *	@info:unused
445*4882a593Smuzhiyun  *
446*4882a593Smuzhiyun  *	Blank the screen if blank_mode != 0, else unblank.
447*4882a593Smuzhiyun  *	Implements VESA suspend and powerdown modes on hardware that supports
448*4882a593Smuzhiyun  *	disabling hsync/vsync:
449*4882a593Smuzhiyun  *		@blank_mode == 2 means suspend vsync,
450*4882a593Smuzhiyun  *		@blank_mode == 3 means suspend hsync,
451*4882a593Smuzhiyun  *		@blank_mode == 4 means powerdown.
452*4882a593Smuzhiyun  */
453*4882a593Smuzhiyun 
hgafb_blank(int blank_mode,struct fb_info * info)454*4882a593Smuzhiyun static int hgafb_blank(int blank_mode, struct fb_info *info)
455*4882a593Smuzhiyun {
456*4882a593Smuzhiyun 	hga_blank(blank_mode);
457*4882a593Smuzhiyun 	return 0;
458*4882a593Smuzhiyun }
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun /*
461*4882a593Smuzhiyun  * Accel functions
462*4882a593Smuzhiyun  */
hgafb_fillrect(struct fb_info * info,const struct fb_fillrect * rect)463*4882a593Smuzhiyun static void hgafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
464*4882a593Smuzhiyun {
465*4882a593Smuzhiyun 	u_int rows, y;
466*4882a593Smuzhiyun 	u8 __iomem *dest;
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 	y = rect->dy;
469*4882a593Smuzhiyun 
470*4882a593Smuzhiyun 	for (rows = rect->height; rows--; y++) {
471*4882a593Smuzhiyun 		dest = rowaddr(info, y) + (rect->dx >> 3);
472*4882a593Smuzhiyun 		switch (rect->rop) {
473*4882a593Smuzhiyun 		case ROP_COPY:
474*4882a593Smuzhiyun 			memset_io(dest, rect->color, (rect->width >> 3));
475*4882a593Smuzhiyun 			break;
476*4882a593Smuzhiyun 		case ROP_XOR:
477*4882a593Smuzhiyun 			fb_writeb(~(fb_readb(dest)), dest);
478*4882a593Smuzhiyun 			break;
479*4882a593Smuzhiyun 		}
480*4882a593Smuzhiyun 	}
481*4882a593Smuzhiyun }
482*4882a593Smuzhiyun 
hgafb_copyarea(struct fb_info * info,const struct fb_copyarea * area)483*4882a593Smuzhiyun static void hgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
484*4882a593Smuzhiyun {
485*4882a593Smuzhiyun 	u_int rows, y1, y2;
486*4882a593Smuzhiyun 	u8 __iomem *src;
487*4882a593Smuzhiyun 	u8 __iomem *dest;
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun 	if (area->dy <= area->sy) {
490*4882a593Smuzhiyun 		y1 = area->sy;
491*4882a593Smuzhiyun 		y2 = area->dy;
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun 		for (rows = area->height; rows--; ) {
494*4882a593Smuzhiyun 			src = rowaddr(info, y1) + (area->sx >> 3);
495*4882a593Smuzhiyun 			dest = rowaddr(info, y2) + (area->dx >> 3);
496*4882a593Smuzhiyun 			memmove(dest, src, (area->width >> 3));
497*4882a593Smuzhiyun 			y1++;
498*4882a593Smuzhiyun 			y2++;
499*4882a593Smuzhiyun 		}
500*4882a593Smuzhiyun 	} else {
501*4882a593Smuzhiyun 		y1 = area->sy + area->height - 1;
502*4882a593Smuzhiyun 		y2 = area->dy + area->height - 1;
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 		for (rows = area->height; rows--;) {
505*4882a593Smuzhiyun 			src = rowaddr(info, y1) + (area->sx >> 3);
506*4882a593Smuzhiyun 			dest = rowaddr(info, y2) + (area->dx >> 3);
507*4882a593Smuzhiyun 			memmove(dest, src, (area->width >> 3));
508*4882a593Smuzhiyun 			y1--;
509*4882a593Smuzhiyun 			y2--;
510*4882a593Smuzhiyun 		}
511*4882a593Smuzhiyun 	}
512*4882a593Smuzhiyun }
513*4882a593Smuzhiyun 
hgafb_imageblit(struct fb_info * info,const struct fb_image * image)514*4882a593Smuzhiyun static void hgafb_imageblit(struct fb_info *info, const struct fb_image *image)
515*4882a593Smuzhiyun {
516*4882a593Smuzhiyun 	u8 __iomem *dest;
517*4882a593Smuzhiyun 	u8 *cdat = (u8 *) image->data;
518*4882a593Smuzhiyun 	u_int rows, y = image->dy;
519*4882a593Smuzhiyun 	u_int x;
520*4882a593Smuzhiyun 	u8 d;
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun 	for (rows = image->height; rows--; y++) {
523*4882a593Smuzhiyun 		for (x = 0; x < image->width; x+= 8) {
524*4882a593Smuzhiyun 			d = *cdat++;
525*4882a593Smuzhiyun 			dest = rowaddr(info, y) + ((image->dx + x)>> 3);
526*4882a593Smuzhiyun 			fb_writeb(d, dest);
527*4882a593Smuzhiyun 		}
528*4882a593Smuzhiyun 	}
529*4882a593Smuzhiyun }
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun static const struct fb_ops hgafb_ops = {
532*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
533*4882a593Smuzhiyun 	.fb_open	= hgafb_open,
534*4882a593Smuzhiyun 	.fb_release	= hgafb_release,
535*4882a593Smuzhiyun 	.fb_setcolreg	= hgafb_setcolreg,
536*4882a593Smuzhiyun 	.fb_pan_display	= hgafb_pan_display,
537*4882a593Smuzhiyun 	.fb_blank	= hgafb_blank,
538*4882a593Smuzhiyun 	.fb_fillrect	= hgafb_fillrect,
539*4882a593Smuzhiyun 	.fb_copyarea	= hgafb_copyarea,
540*4882a593Smuzhiyun 	.fb_imageblit	= hgafb_imageblit,
541*4882a593Smuzhiyun };
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun /* ------------------------------------------------------------------------- *
544*4882a593Smuzhiyun  *
545*4882a593Smuzhiyun  * Functions in fb_info
546*4882a593Smuzhiyun  *
547*4882a593Smuzhiyun  * ------------------------------------------------------------------------- */
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun /* ------------------------------------------------------------------------- */
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 	/*
552*4882a593Smuzhiyun 	 *  Initialization
553*4882a593Smuzhiyun 	 */
554*4882a593Smuzhiyun 
hgafb_probe(struct platform_device * pdev)555*4882a593Smuzhiyun static int hgafb_probe(struct platform_device *pdev)
556*4882a593Smuzhiyun {
557*4882a593Smuzhiyun 	struct fb_info *info;
558*4882a593Smuzhiyun 	int ret;
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun 	ret = hga_card_detect();
561*4882a593Smuzhiyun 	if (ret)
562*4882a593Smuzhiyun 		return ret;
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	printk(KERN_INFO "hgafb: %s with %ldK of memory detected.\n",
565*4882a593Smuzhiyun 		hga_type_name, hga_vram_len/1024);
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	info = framebuffer_alloc(0, &pdev->dev);
568*4882a593Smuzhiyun 	if (!info) {
569*4882a593Smuzhiyun 		iounmap(hga_vram);
570*4882a593Smuzhiyun 		return -ENOMEM;
571*4882a593Smuzhiyun 	}
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 	hga_fix.smem_start = (unsigned long)hga_vram;
574*4882a593Smuzhiyun 	hga_fix.smem_len = hga_vram_len;
575*4882a593Smuzhiyun 
576*4882a593Smuzhiyun 	info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
577*4882a593Smuzhiyun 	info->var = hga_default_var;
578*4882a593Smuzhiyun 	info->fix = hga_fix;
579*4882a593Smuzhiyun 	info->monspecs.hfmin = 0;
580*4882a593Smuzhiyun 	info->monspecs.hfmax = 0;
581*4882a593Smuzhiyun 	info->monspecs.vfmin = 10000;
582*4882a593Smuzhiyun 	info->monspecs.vfmax = 10000;
583*4882a593Smuzhiyun 	info->monspecs.dpms = 0;
584*4882a593Smuzhiyun 	info->fbops = &hgafb_ops;
585*4882a593Smuzhiyun 	info->screen_base = hga_vram;
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun         if (register_framebuffer(info) < 0) {
588*4882a593Smuzhiyun 		framebuffer_release(info);
589*4882a593Smuzhiyun 		iounmap(hga_vram);
590*4882a593Smuzhiyun 		return -EINVAL;
591*4882a593Smuzhiyun 	}
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	fb_info(info, "%s frame buffer device\n", info->fix.id);
594*4882a593Smuzhiyun 	platform_set_drvdata(pdev, info);
595*4882a593Smuzhiyun 	return 0;
596*4882a593Smuzhiyun }
597*4882a593Smuzhiyun 
hgafb_remove(struct platform_device * pdev)598*4882a593Smuzhiyun static int hgafb_remove(struct platform_device *pdev)
599*4882a593Smuzhiyun {
600*4882a593Smuzhiyun 	struct fb_info *info = platform_get_drvdata(pdev);
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 	hga_txt_mode();
603*4882a593Smuzhiyun 	hga_clear_screen();
604*4882a593Smuzhiyun 
605*4882a593Smuzhiyun 	if (info) {
606*4882a593Smuzhiyun 		unregister_framebuffer(info);
607*4882a593Smuzhiyun 		framebuffer_release(info);
608*4882a593Smuzhiyun 	}
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun 	iounmap(hga_vram);
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun 	if (release_io_ports)
613*4882a593Smuzhiyun 		release_region(0x3b0, 12);
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 	if (release_io_port)
616*4882a593Smuzhiyun 		release_region(0x3bf, 1);
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun 	return 0;
619*4882a593Smuzhiyun }
620*4882a593Smuzhiyun 
621*4882a593Smuzhiyun static struct platform_driver hgafb_driver = {
622*4882a593Smuzhiyun 	.probe = hgafb_probe,
623*4882a593Smuzhiyun 	.remove = hgafb_remove,
624*4882a593Smuzhiyun 	.driver = {
625*4882a593Smuzhiyun 		.name = "hgafb",
626*4882a593Smuzhiyun 	},
627*4882a593Smuzhiyun };
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun static struct platform_device *hgafb_device;
630*4882a593Smuzhiyun 
hgafb_init(void)631*4882a593Smuzhiyun static int __init hgafb_init(void)
632*4882a593Smuzhiyun {
633*4882a593Smuzhiyun 	int ret;
634*4882a593Smuzhiyun 
635*4882a593Smuzhiyun 	if (fb_get_options("hgafb", NULL))
636*4882a593Smuzhiyun 		return -ENODEV;
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun 	ret = platform_driver_register(&hgafb_driver);
639*4882a593Smuzhiyun 
640*4882a593Smuzhiyun 	if (!ret) {
641*4882a593Smuzhiyun 		hgafb_device = platform_device_register_simple("hgafb", 0, NULL, 0);
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun 		if (IS_ERR(hgafb_device)) {
644*4882a593Smuzhiyun 			platform_driver_unregister(&hgafb_driver);
645*4882a593Smuzhiyun 			ret = PTR_ERR(hgafb_device);
646*4882a593Smuzhiyun 		}
647*4882a593Smuzhiyun 	}
648*4882a593Smuzhiyun 
649*4882a593Smuzhiyun 	return ret;
650*4882a593Smuzhiyun }
651*4882a593Smuzhiyun 
hgafb_exit(void)652*4882a593Smuzhiyun static void __exit hgafb_exit(void)
653*4882a593Smuzhiyun {
654*4882a593Smuzhiyun 	platform_device_unregister(hgafb_device);
655*4882a593Smuzhiyun 	platform_driver_unregister(&hgafb_driver);
656*4882a593Smuzhiyun }
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun /* -------------------------------------------------------------------------
659*4882a593Smuzhiyun  *
660*4882a593Smuzhiyun  *  Modularization
661*4882a593Smuzhiyun  *
662*4882a593Smuzhiyun  * ------------------------------------------------------------------------- */
663*4882a593Smuzhiyun 
664*4882a593Smuzhiyun MODULE_AUTHOR("Ferenc Bakonyi (fero@drama.obuda.kando.hu)");
665*4882a593Smuzhiyun MODULE_DESCRIPTION("FBDev driver for Hercules Graphics Adaptor");
666*4882a593Smuzhiyun MODULE_LICENSE("GPL");
667*4882a593Smuzhiyun 
668*4882a593Smuzhiyun module_param(nologo, bool, 0);
669*4882a593Smuzhiyun MODULE_PARM_DESC(nologo, "Disables startup logo if != 0 (default=0)");
670*4882a593Smuzhiyun module_init(hgafb_init);
671*4882a593Smuzhiyun module_exit(hgafb_exit);
672