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