Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  *  linux/drivers/video/mdacon.c -- Low level MDA based console driver
0003  *
0004  *  (c) 1998 Andrew Apted <ajapted@netspace.net.au>
0005  *
0006  *      including portions (c) 1995-1998 Patrick Caulfield.
0007  *
0008  *      slight improvements (c) 2000 Edward Betts <edward@debian.org>
0009  *
0010  *  This file is based on the VGA console driver (vgacon.c):
0011  *  
0012  *  Created 28 Sep 1997 by Geert Uytterhoeven
0013  *
0014  *  Rewritten by Martin Mares <mj@ucw.cz>, July 1998
0015  *
0016  *  and on the old console.c, vga.c and vesa_blank.c drivers:
0017  *
0018  *  Copyright (C) 1991, 1992  Linus Torvalds
0019  *              1995  Jay Estabrook
0020  *
0021  *  This file is subject to the terms and conditions of the GNU General Public
0022  *  License.  See the file COPYING in the main directory of this archive for
0023  *  more details.
0024  *
0025  *  Changelog:
0026  *  Paul G. (03/2001) Fix mdacon= boot prompt to use __setup().
0027  */
0028 
0029 #include <linux/types.h>
0030 #include <linux/fs.h>
0031 #include <linux/kernel.h>
0032 #include <linux/module.h>
0033 #include <linux/console.h>
0034 #include <linux/string.h>
0035 #include <linux/kd.h>
0036 #include <linux/vt_kern.h>
0037 #include <linux/vt_buffer.h>
0038 #include <linux/selection.h>
0039 #include <linux/spinlock.h>
0040 #include <linux/ioport.h>
0041 #include <linux/delay.h>
0042 #include <linux/init.h>
0043 
0044 #include <asm/io.h>
0045 #include <asm/vga.h>
0046 
0047 static DEFINE_SPINLOCK(mda_lock);
0048 
0049 /* description of the hardware layout */
0050 
0051 static u16      *mda_vram_base;     /* Base of video memory */
0052 static unsigned long    mda_vram_len;       /* Size of video memory */
0053 static unsigned int mda_num_columns;    /* Number of text columns */
0054 static unsigned int mda_num_lines;      /* Number of text lines */
0055 
0056 static unsigned int mda_index_port;     /* Register select port */
0057 static unsigned int mda_value_port;     /* Register value port */
0058 static unsigned int mda_mode_port;      /* Mode control port */
0059 static unsigned int mda_status_port;    /* Status and Config port */
0060 static unsigned int mda_gfx_port;       /* Graphics control port */
0061 
0062 /* current hardware state */
0063 
0064 static int  mda_cursor_loc=-1;
0065 static int  mda_cursor_size_from=-1;
0066 static int  mda_cursor_size_to=-1;
0067 
0068 static enum { TYPE_MDA, TYPE_HERC, TYPE_HERCPLUS, TYPE_HERCCOLOR } mda_type;
0069 static char *mda_type_name;
0070 
0071 /* console information */
0072 
0073 static int  mda_first_vc = 13;
0074 static int  mda_last_vc  = 16;
0075 
0076 static struct vc_data   *mda_display_fg = NULL;
0077 
0078 module_param(mda_first_vc, int, 0);
0079 MODULE_PARM_DESC(mda_first_vc, "First virtual console. Default: 13");
0080 module_param(mda_last_vc, int, 0);
0081 MODULE_PARM_DESC(mda_last_vc, "Last virtual console. Default: 16");
0082 
0083 /* MDA register values
0084  */
0085 
0086 #define MDA_CURSOR_BLINKING 0x00
0087 #define MDA_CURSOR_OFF      0x20
0088 #define MDA_CURSOR_SLOWBLINK    0x60
0089 
0090 #define MDA_MODE_GRAPHICS   0x02
0091 #define MDA_MODE_VIDEO_EN   0x08
0092 #define MDA_MODE_BLINK_EN   0x20
0093 #define MDA_MODE_GFX_PAGE1  0x80
0094 
0095 #define MDA_STATUS_HSYNC    0x01
0096 #define MDA_STATUS_VSYNC    0x80
0097 #define MDA_STATUS_VIDEO    0x08
0098 
0099 #define MDA_CONFIG_COL132   0x08
0100 #define MDA_GFX_MODE_EN     0x01
0101 #define MDA_GFX_PAGE_EN     0x02
0102 
0103 
0104 /*
0105  * MDA could easily be classified as "pre-dinosaur hardware".
0106  */
0107 
0108 static void write_mda_b(unsigned int val, unsigned char reg)
0109 {
0110     unsigned long flags;
0111 
0112     spin_lock_irqsave(&mda_lock, flags);    
0113 
0114     outb_p(reg, mda_index_port); 
0115     outb_p(val, mda_value_port);
0116 
0117     spin_unlock_irqrestore(&mda_lock, flags);
0118 }
0119 
0120 static void write_mda_w(unsigned int val, unsigned char reg)
0121 {
0122     unsigned long flags;
0123 
0124     spin_lock_irqsave(&mda_lock, flags);
0125 
0126     outb_p(reg,   mda_index_port); outb_p(val >> 8,   mda_value_port);
0127     outb_p(reg+1, mda_index_port); outb_p(val & 0xff, mda_value_port);
0128 
0129     spin_unlock_irqrestore(&mda_lock, flags);
0130 }
0131 
0132 #ifdef TEST_MDA_B
0133 static int test_mda_b(unsigned char val, unsigned char reg)
0134 {
0135     unsigned long flags;
0136 
0137     spin_lock_irqsave(&mda_lock, flags);
0138 
0139     outb_p(reg, mda_index_port); 
0140     outb  (val, mda_value_port);
0141 
0142     udelay(20); val = (inb_p(mda_value_port) == val);
0143 
0144     spin_unlock_irqrestore(&mda_lock, flags);
0145     return val;
0146 }
0147 #endif
0148 
0149 static inline void mda_set_cursor(unsigned int location) 
0150 {
0151     if (mda_cursor_loc == location)
0152         return;
0153 
0154     write_mda_w(location >> 1, 0x0e);
0155 
0156     mda_cursor_loc = location;
0157 }
0158 
0159 static inline void mda_set_cursor_size(int from, int to)
0160 {
0161     if (mda_cursor_size_from==from && mda_cursor_size_to==to)
0162         return;
0163     
0164     if (from > to) {
0165         write_mda_b(MDA_CURSOR_OFF, 0x0a);  /* disable cursor */
0166     } else {
0167         write_mda_b(from, 0x0a);    /* cursor start */
0168         write_mda_b(to,   0x0b);    /* cursor end */
0169     }
0170 
0171     mda_cursor_size_from = from;
0172     mda_cursor_size_to   = to;
0173 }
0174 
0175 
0176 #ifndef MODULE
0177 static int __init mdacon_setup(char *str)
0178 {
0179     /* command line format: mdacon=<first>,<last> */
0180 
0181     int ints[3];
0182 
0183     str = get_options(str, ARRAY_SIZE(ints), ints);
0184 
0185     if (ints[0] < 2)
0186         return 0;
0187 
0188     if (ints[1] < 1 || ints[1] > MAX_NR_CONSOLES || 
0189         ints[2] < 1 || ints[2] > MAX_NR_CONSOLES)
0190         return 0;
0191 
0192     mda_first_vc = ints[1];
0193     mda_last_vc  = ints[2];
0194     return 1;
0195 }
0196 
0197 __setup("mdacon=", mdacon_setup);
0198 #endif
0199 
0200 static int mda_detect(void)
0201 {
0202     int count=0;
0203     u16 *p, p_save;
0204     u16 *q, q_save;
0205 
0206     /* do a memory check */
0207 
0208     p = mda_vram_base;
0209     q = mda_vram_base + 0x01000 / 2;
0210 
0211     p_save = scr_readw(p);
0212     q_save = scr_readw(q);
0213 
0214     scr_writew(0xAA55, p);
0215     if (scr_readw(p) == 0xAA55)
0216         count++;
0217 
0218     scr_writew(0x55AA, p);
0219     if (scr_readw(p) == 0x55AA)
0220         count++;
0221 
0222     scr_writew(p_save, p);
0223 
0224     if (count != 2) {
0225         return 0;
0226     }
0227 
0228     /* check if we have 4K or 8K */
0229 
0230     scr_writew(0xA55A, q);
0231     scr_writew(0x0000, p);
0232     if (scr_readw(q) == 0xA55A)
0233         count++;
0234     
0235     scr_writew(0x5AA5, q);
0236     scr_writew(0x0000, p);
0237     if (scr_readw(q) == 0x5AA5)
0238         count++;
0239 
0240     scr_writew(p_save, p);
0241     scr_writew(q_save, q);
0242     
0243     if (count == 4) {
0244         mda_vram_len = 0x02000;
0245     }
0246     
0247     /* Ok, there is definitely a card registering at the correct
0248      * memory location, so now we do an I/O port test.
0249      */
0250 
0251 #ifdef TEST_MDA_B
0252     /* Edward: These two mess `tests' mess up my cursor on bootup */
0253 
0254     /* cursor low register */
0255     if (!test_mda_b(0x66, 0x0f))
0256         return 0;
0257 
0258     /* cursor low register */
0259     if (!test_mda_b(0x99, 0x0f))
0260         return 0;
0261 #endif
0262 
0263     /* See if the card is a Hercules, by checking whether the vsync
0264      * bit of the status register is changing.  This test lasts for
0265      * approximately 1/10th of a second.
0266      */
0267     
0268     p_save = q_save = inb_p(mda_status_port) & MDA_STATUS_VSYNC;
0269 
0270     for (count = 0; count < 50000 && p_save == q_save; count++) {
0271         q_save = inb(mda_status_port) & MDA_STATUS_VSYNC;
0272         udelay(2);
0273     }
0274 
0275     if (p_save != q_save) {
0276         switch (inb_p(mda_status_port) & 0x70) {
0277         case 0x10:
0278             mda_type = TYPE_HERCPLUS;
0279             mda_type_name = "HerculesPlus";
0280             break;
0281         case 0x50:
0282             mda_type = TYPE_HERCCOLOR;
0283             mda_type_name = "HerculesColor";
0284             break;
0285         default:
0286             mda_type = TYPE_HERC;
0287             mda_type_name = "Hercules";
0288             break;
0289         }
0290     }
0291 
0292     return 1;
0293 }
0294 
0295 static void mda_initialize(void)
0296 {
0297     write_mda_b(97, 0x00);      /* horizontal total */
0298     write_mda_b(80, 0x01);      /* horizontal displayed */
0299     write_mda_b(82, 0x02);      /* horizontal sync pos */
0300     write_mda_b(15, 0x03);      /* horizontal sync width */
0301 
0302     write_mda_b(25, 0x04);      /* vertical total */
0303     write_mda_b(6,  0x05);      /* vertical total adjust */
0304     write_mda_b(25, 0x06);      /* vertical displayed */
0305     write_mda_b(25, 0x07);      /* vertical sync pos */
0306 
0307     write_mda_b(2,  0x08);      /* interlace mode */
0308     write_mda_b(13, 0x09);      /* maximum scanline */
0309     write_mda_b(12, 0x0a);      /* cursor start */
0310     write_mda_b(13, 0x0b);      /* cursor end */
0311 
0312     write_mda_w(0x0000, 0x0c);  /* start address */
0313     write_mda_w(0x0000, 0x0e);  /* cursor location */
0314 
0315     outb_p(MDA_MODE_VIDEO_EN | MDA_MODE_BLINK_EN, mda_mode_port);
0316     outb_p(0x00, mda_status_port);
0317     outb_p(0x00, mda_gfx_port);
0318 }
0319 
0320 static const char *mdacon_startup(void)
0321 {
0322     mda_num_columns = 80;
0323     mda_num_lines   = 25;
0324 
0325     mda_vram_len  = 0x01000;
0326     mda_vram_base = (u16 *)VGA_MAP_MEM(0xb0000, mda_vram_len);
0327 
0328     mda_index_port  = 0x3b4;
0329     mda_value_port  = 0x3b5;
0330     mda_mode_port   = 0x3b8;
0331     mda_status_port = 0x3ba;
0332     mda_gfx_port    = 0x3bf;
0333 
0334     mda_type = TYPE_MDA;
0335     mda_type_name = "MDA";
0336 
0337     if (! mda_detect()) {
0338         printk("mdacon: MDA card not detected.\n");
0339         return NULL;
0340     }
0341 
0342     if (mda_type != TYPE_MDA) {
0343         mda_initialize();
0344     }
0345 
0346     /* cursor looks ugly during boot-up, so turn it off */
0347     mda_set_cursor(mda_vram_len - 1);
0348 
0349     printk("mdacon: %s with %ldK of memory detected.\n",
0350         mda_type_name, mda_vram_len/1024);
0351 
0352     return "MDA-2";
0353 }
0354 
0355 static void mdacon_init(struct vc_data *c, int init)
0356 {
0357     c->vc_complement_mask = 0x0800;  /* reverse video */
0358     c->vc_display_fg = &mda_display_fg;
0359 
0360     if (init) {
0361         c->vc_cols = mda_num_columns;
0362         c->vc_rows = mda_num_lines;
0363     } else
0364         vc_resize(c, mda_num_columns, mda_num_lines);
0365 
0366     /* make the first MDA console visible */
0367 
0368     if (mda_display_fg == NULL)
0369         mda_display_fg = c;
0370 }
0371 
0372 static void mdacon_deinit(struct vc_data *c)
0373 {
0374     /* con_set_default_unimap(c->vc_num); */
0375 
0376     if (mda_display_fg == c)
0377         mda_display_fg = NULL;
0378 }
0379 
0380 static inline u16 mda_convert_attr(u16 ch)
0381 {
0382     u16 attr = 0x0700;
0383 
0384     /* Underline and reverse-video are mutually exclusive on MDA.
0385      * Since reverse-video is used for cursors and selected areas,
0386      * it takes precedence. 
0387      */
0388 
0389     if (ch & 0x0800)    attr = 0x7000;  /* reverse */
0390     else if (ch & 0x0400)   attr = 0x0100;  /* underline */
0391 
0392     return ((ch & 0x0200) << 2) |       /* intensity */ 
0393         (ch & 0x8000) |         /* blink */ 
0394         (ch & 0x00ff) | attr;
0395 }
0396 
0397 static u8 mdacon_build_attr(struct vc_data *c, u8 color,
0398                 enum vc_intensity intensity,
0399                 bool blink, bool underline, bool reverse,
0400                 bool italic)
0401 {
0402     /* The attribute is just a bit vector:
0403      *
0404      *  Bit 0..1 : intensity (0..2)
0405      *  Bit 2    : underline
0406      *  Bit 3    : reverse
0407      *  Bit 7    : blink
0408      */
0409 
0410     return (intensity & VCI_MASK) |
0411         (underline << 2) |
0412         (reverse << 3) |
0413         (italic << 4) |
0414         (blink << 7);
0415 }
0416 
0417 static void mdacon_invert_region(struct vc_data *c, u16 *p, int count)
0418 {
0419     for (; count > 0; count--) {
0420         scr_writew(scr_readw(p) ^ 0x0800, p);
0421         p++;
0422     }
0423 }
0424 
0425 static inline u16 *mda_addr(unsigned int x, unsigned int y)
0426 {
0427     return mda_vram_base + y * mda_num_columns + x;
0428 }
0429 
0430 static void mdacon_putc(struct vc_data *c, int ch, int y, int x)
0431 {
0432     scr_writew(mda_convert_attr(ch), mda_addr(x, y));
0433 }
0434 
0435 static void mdacon_putcs(struct vc_data *c, const unsigned short *s,
0436                  int count, int y, int x)
0437 {
0438     u16 *dest = mda_addr(x, y);
0439 
0440     for (; count > 0; count--) {
0441         scr_writew(mda_convert_attr(scr_readw(s++)), dest++);
0442     }
0443 }
0444 
0445 static void mdacon_clear(struct vc_data *c, int y, int x, 
0446               int height, int width)
0447 {
0448     u16 *dest = mda_addr(x, y);
0449     u16 eattr = mda_convert_attr(c->vc_video_erase_char);
0450 
0451     if (width <= 0 || height <= 0)
0452         return;
0453 
0454     if (x==0 && width==mda_num_columns) {
0455         scr_memsetw(dest, eattr, height*width*2);
0456     } else {
0457         for (; height > 0; height--, dest+=mda_num_columns)
0458             scr_memsetw(dest, eattr, width*2);
0459     }
0460 }
0461                         
0462 static int mdacon_switch(struct vc_data *c)
0463 {
0464     return 1;   /* redrawing needed */
0465 }
0466 
0467 static int mdacon_blank(struct vc_data *c, int blank, int mode_switch)
0468 {
0469     if (mda_type == TYPE_MDA) {
0470         if (blank) 
0471             scr_memsetw(mda_vram_base,
0472                 mda_convert_attr(c->vc_video_erase_char),
0473                 c->vc_screenbuf_size);
0474         /* Tell console.c that it has to restore the screen itself */
0475         return 1;
0476     } else {
0477         if (blank)
0478             outb_p(0x00, mda_mode_port);    /* disable video */
0479         else
0480             outb_p(MDA_MODE_VIDEO_EN | MDA_MODE_BLINK_EN, 
0481                 mda_mode_port);
0482         return 0;
0483     }
0484 }
0485 
0486 static void mdacon_cursor(struct vc_data *c, int mode)
0487 {
0488     if (mode == CM_ERASE) {
0489         mda_set_cursor(mda_vram_len - 1);
0490         return;
0491     }
0492 
0493     mda_set_cursor(c->state.y * mda_num_columns * 2 + c->state.x * 2);
0494 
0495     switch (CUR_SIZE(c->vc_cursor_type)) {
0496 
0497         case CUR_LOWER_THIRD:   mda_set_cursor_size(10, 13); break;
0498         case CUR_LOWER_HALF:    mda_set_cursor_size(7,  13); break;
0499         case CUR_TWO_THIRDS:    mda_set_cursor_size(4,  13); break;
0500         case CUR_BLOCK:     mda_set_cursor_size(1,  13); break;
0501         case CUR_NONE:      mda_set_cursor_size(14, 13); break;
0502         default:        mda_set_cursor_size(12, 13); break;
0503     }
0504 }
0505 
0506 static bool mdacon_scroll(struct vc_data *c, unsigned int t, unsigned int b,
0507         enum con_scroll dir, unsigned int lines)
0508 {
0509     u16 eattr = mda_convert_attr(c->vc_video_erase_char);
0510 
0511     if (!lines)
0512         return false;
0513 
0514     if (lines > c->vc_rows)   /* maximum realistic size */
0515         lines = c->vc_rows;
0516 
0517     switch (dir) {
0518 
0519     case SM_UP:
0520         scr_memmovew(mda_addr(0, t), mda_addr(0, t + lines),
0521                 (b-t-lines)*mda_num_columns*2);
0522         scr_memsetw(mda_addr(0, b - lines), eattr,
0523                 lines*mda_num_columns*2);
0524         break;
0525 
0526     case SM_DOWN:
0527         scr_memmovew(mda_addr(0, t + lines), mda_addr(0, t),
0528                 (b-t-lines)*mda_num_columns*2);
0529         scr_memsetw(mda_addr(0, t), eattr, lines*mda_num_columns*2);
0530         break;
0531     }
0532 
0533     return false;
0534 }
0535 
0536 
0537 /*
0538  *  The console `switch' structure for the MDA based console
0539  */
0540 
0541 static const struct consw mda_con = {
0542     .owner =        THIS_MODULE,
0543     .con_startup =      mdacon_startup,
0544     .con_init =     mdacon_init,
0545     .con_deinit =       mdacon_deinit,
0546     .con_clear =        mdacon_clear,
0547     .con_putc =     mdacon_putc,
0548     .con_putcs =        mdacon_putcs,
0549     .con_cursor =       mdacon_cursor,
0550     .con_scroll =       mdacon_scroll,
0551     .con_switch =       mdacon_switch,
0552     .con_blank =        mdacon_blank,
0553     .con_build_attr =   mdacon_build_attr,
0554     .con_invert_region =    mdacon_invert_region,
0555 };
0556 
0557 int __init mda_console_init(void)
0558 {
0559     int err;
0560 
0561     if (mda_first_vc > mda_last_vc)
0562         return 1;
0563     console_lock();
0564     err = do_take_over_console(&mda_con, mda_first_vc-1, mda_last_vc-1, 0);
0565     console_unlock();
0566     return err;
0567 }
0568 
0569 static void __exit mda_console_exit(void)
0570 {
0571     give_up_console(&mda_con);
0572 }
0573 
0574 module_init(mda_console_init);
0575 module_exit(mda_console_exit);
0576 
0577 MODULE_LICENSE("GPL");
0578