v2.4.9.9 -> v2.4.9.10
[opensuse:kernel.git] / drivers / video / newport_con.c
1 /*
2  * newport_con.c: Abscon for newport hardware
3  * 
4  * (C) 1998 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
5  * (C) 1999 Ulf Carlsson (ulfc@thepuffingruop.com)
6  * 
7  * This driver is based on sgicons.c and cons_newport.
8  * 
9  * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
10  * Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx)
11  */
12 #include <linux/init.h>
13 #include <linux/kernel.h>
14 #include <linux/errno.h>
15 #include <linux/tty.h>
16 #include <linux/kd.h>
17 #include <linux/selection.h>
18 #include <linux/console.h>
19 #include <linux/console_struct.h>
20 #include <linux/vt_kern.h>
21 #include <linux/mm.h>
22 #include <linux/module.h>
23 #include <linux/slab.h>
24
25 #include <asm/uaccess.h>
26 #include <asm/system.h>
27 #include <asm/page.h>
28 #include <asm/pgtable.h>
29 #include <video/newport.h>
30 #define INCLUDE_LINUX_LOGO_DATA
31 #include <asm/linux_logo.h>
32
33 #include <video/font.h>
34
35 #define LOGO_W          80
36 #define LOGO_H          80
37
38 extern struct fbcon_font_desc font_vga_8x16;
39
40 #define FONT_DATA ((unsigned char *)font_vga_8x16.data)
41
42 /* borrowed from fbcon.c */
43 #define REFCOUNT(fd)    (((int *)(fd))[-1])
44 #define FNTSIZE(fd)     (((int *)(fd))[-2])
45 #define FNTCHARCNT(fd)  (((int *)(fd))[-3])
46 #define FONT_EXTRA_WORDS 3
47
48 static unsigned char *font_data[MAX_NR_CONSOLES];
49
50 extern struct newport_regs *npregs;
51
52 static int logo_active;
53 static int topscan;
54 static int xcurs_correction = 29;
55 static int newport_xsize;
56 static int newport_ysize;
57
58 #define BMASK(c) (c << 24)
59
60 #define RENDER(regs, cp) do { \
61 (regs)->go.zpattern = BMASK((cp)[0x0]); (regs)->go.zpattern = BMASK((cp)[0x1]); \
62 (regs)->go.zpattern = BMASK((cp)[0x2]); (regs)->go.zpattern = BMASK((cp)[0x3]); \
63 (regs)->go.zpattern = BMASK((cp)[0x4]); (regs)->go.zpattern = BMASK((cp)[0x5]); \
64 (regs)->go.zpattern = BMASK((cp)[0x6]); (regs)->go.zpattern = BMASK((cp)[0x7]); \
65 (regs)->go.zpattern = BMASK((cp)[0x8]); (regs)->go.zpattern = BMASK((cp)[0x9]); \
66 (regs)->go.zpattern = BMASK((cp)[0xa]); (regs)->go.zpattern = BMASK((cp)[0xb]); \
67 (regs)->go.zpattern = BMASK((cp)[0xc]); (regs)->go.zpattern = BMASK((cp)[0xd]); \
68 (regs)->go.zpattern = BMASK((cp)[0xe]); (regs)->go.zpattern = BMASK((cp)[0xf]); \
69 } while(0)
70
71 #define TESTVAL 0xdeadbeef
72 #define XSTI_TO_FXSTART(val) (((val) & 0xffff) << 11)
73
74 static inline void newport_render_background(int xstart, int ystart,
75                                              int xend, int yend, int ci)
76 {
77         newport_wait();
78         npregs->set.wrmask = 0xffffffff;
79         npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
80                                  NPORT_DMODE0_DOSETUP | NPORT_DMODE0_STOPX
81                                  | NPORT_DMODE0_STOPY);
82         npregs->set.colori = ci;
83         npregs->set.xystarti =
84             (xstart << 16) | ((ystart + topscan) & 0x3ff);
85         npregs->go.xyendi =
86             ((xend + 7) << 16) | ((yend + topscan + 15) & 0x3ff);
87 }
88
89 static inline void newport_init_cmap(void)
90 {
91         unsigned short i;
92
93         for (i = 0; i < 16; i++) {
94                 newport_bfwait();
95                 newport_cmap_setaddr(npregs, color_table[i]);
96                 newport_cmap_setrgb(npregs,
97                                     default_red[i],
98                                     default_grn[i], default_blu[i]);
99         }
100 }
101
102 static inline void newport_show_logo(void)
103 {
104         unsigned long i;
105
106         for (i = 0; i < LINUX_LOGO_COLORS; i++) {
107                 newport_bfwait();
108                 newport_cmap_setaddr(npregs, i + 0x20);
109                 newport_cmap_setrgb(npregs,
110                                     linux_logo_red[i],
111                                     linux_logo_green[i],
112                                     linux_logo_blue[i]);
113         }
114
115         newport_wait();
116         npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
117                                  NPORT_DMODE0_CHOST);
118
119         npregs->set.xystarti = ((newport_xsize - LOGO_W) << 16) | (0);
120         npregs->set.xyendi = ((newport_xsize - 1) << 16);
121         newport_wait();
122
123         for (i = 0; i < LOGO_W * LOGO_H; i++)
124                 npregs->go.hostrw0 = linux_logo[i] << 24;
125 }
126
127 static inline void newport_clear_screen(int xstart, int ystart, int xend,
128                                         int yend, int ci)
129 {
130         if (logo_active)
131                 return;
132
133         newport_wait();
134         npregs->set.wrmask = 0xffffffff;
135         npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
136                                  NPORT_DMODE0_DOSETUP | NPORT_DMODE0_STOPX
137                                  | NPORT_DMODE0_STOPY);
138         npregs->set.colori = ci;
139         npregs->set.xystarti = (xstart << 16) | ystart;
140         npregs->go.xyendi = (xend << 16) | yend;
141 }
142
143 static inline void newport_clear_lines(int ystart, int yend, int ci)
144 {
145         ystart = ((ystart << 4) + topscan) & 0x3ff;
146         yend = ((yend << 4) + topscan + 15) & 0x3ff;
147         newport_clear_screen(0, ystart, 1280 + 63, yend, ci);
148 }
149
150 void newport_reset(void)
151 {
152         unsigned short treg;
153         int i;
154
155         newport_wait();
156         treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
157         newport_vc2_set(npregs, VC2_IREG_CONTROL,
158                         (treg | VC2_CTRL_EVIDEO));
159
160         treg = newport_vc2_get(npregs, VC2_IREG_CENTRY);
161         newport_vc2_set(npregs, VC2_IREG_RADDR, treg);
162         npregs->set.dcbmode = (NPORT_DMODE_AVC2 | VC2_REGADDR_RAM |
163                                NPORT_DMODE_W2 | VC2_PROTOCOL);
164         for (i = 0; i < 128; i++) {
165                 newport_bfwait();
166                 if (i == 92 || i == 94)
167                         npregs->set.dcbdata0.byshort.s1 = 0xff00;
168                 else
169                         npregs->set.dcbdata0.byshort.s1 = 0x0000;
170         }
171
172         newport_init_cmap();
173
174         /* turn off popup plane */
175         npregs->set.dcbmode = (DCB_XMAP0 | R_DCB_XMAP9_PROTOCOL |
176                                XM9_CRS_CONFIG | NPORT_DMODE_W1);
177         npregs->set.dcbdata0.bybytes.b3 &= ~XM9_PUPMODE;
178         npregs->set.dcbmode = (DCB_XMAP1 | R_DCB_XMAP9_PROTOCOL |
179                                XM9_CRS_CONFIG | NPORT_DMODE_W1);
180         npregs->set.dcbdata0.bybytes.b3 &= ~XM9_PUPMODE;
181
182         topscan = 0;
183         npregs->cset.topscan = 0x3ff;
184         npregs->cset.xywin = (4096 << 16) | 4096;
185
186         /* Clear the screen. */
187         newport_clear_screen(0, 0, 1280 + 63, 1024, 0);
188 }
189
190 /*
191  * calculate the actual screen size by reading
192  * the video timing out of the VC2
193  */
194 void newport_get_screensize(void)
195 {
196         int i, cols;
197         unsigned short ventry, treg;
198         unsigned short linetable[128];  /* should be enough */
199
200         ventry = newport_vc2_get(npregs, VC2_IREG_VENTRY);
201         newport_vc2_set(npregs, VC2_IREG_RADDR, ventry);
202         npregs->set.dcbmode = (NPORT_DMODE_AVC2 | VC2_REGADDR_RAM |
203                                NPORT_DMODE_W2 | VC2_PROTOCOL);
204         for (i = 0; i < 128; i++) {
205                 newport_bfwait();
206                 linetable[i] = npregs->set.dcbdata0.byshort.s1;
207         }
208
209         newport_xsize = newport_ysize = 0;
210         for (i = 0; linetable[i + 1] && (i < sizeof(linetable)); i += 2) {
211                 cols = 0;
212                 newport_vc2_set(npregs, VC2_IREG_RADDR, linetable[i]);
213                 npregs->set.dcbmode = (NPORT_DMODE_AVC2 | VC2_REGADDR_RAM |
214                                        NPORT_DMODE_W2 | VC2_PROTOCOL);
215                 do {
216                         newport_bfwait();
217                         treg = npregs->set.dcbdata0.byshort.s1;
218                         if ((treg & 1) == 0)
219                                 cols += (treg >> 7) & 0xfe;
220                         if ((treg & 0x80) == 0) {
221                                 newport_bfwait();
222                                 treg = npregs->set.dcbdata0.byshort.s1;
223                         }
224                 } while ((treg & 0x8000) == 0);
225                 if (cols) {
226                         if (cols > newport_xsize)
227                                 newport_xsize = cols;
228                         newport_ysize += linetable[i + 1];
229                 }
230         }
231         printk("NG1: Screensize %dx%d\n", newport_xsize, newport_ysize);
232 }
233
234 static void newport_get_revisions(void)
235 {
236         unsigned int tmp;
237         unsigned int board_rev;
238         unsigned int rex3_rev;
239         unsigned int vc2_rev;
240         unsigned int cmap_rev;
241         unsigned int xmap9_rev;
242         unsigned int bt445_rev;
243         unsigned int bitplanes;
244
245         rex3_rev = npregs->cset.status & NPORT_STAT_VERS;
246
247         npregs->set.dcbmode = (DCB_CMAP0 | NCMAP_PROTOCOL |
248                                NCMAP_REGADDR_RREG | NPORT_DMODE_W1);
249         tmp = npregs->set.dcbdata0.bybytes.b3;
250         cmap_rev = tmp & 7;
251         board_rev = (tmp >> 4) & 7;
252         bitplanes = ((board_rev > 1) && (tmp & 0x80)) ? 8 : 24;
253
254         npregs->set.dcbmode = (DCB_CMAP1 | NCMAP_PROTOCOL |
255                                NCMAP_REGADDR_RREG | NPORT_DMODE_W1);
256         tmp = npregs->set.dcbdata0.bybytes.b3;
257         if ((tmp & 7) < cmap_rev)
258                 cmap_rev = (tmp & 7);
259
260         vc2_rev = (newport_vc2_get(npregs, VC2_IREG_CONFIG) >> 5) & 7;
261
262         npregs->set.dcbmode = (DCB_XMAP0 | R_DCB_XMAP9_PROTOCOL |
263                                XM9_CRS_REVISION | NPORT_DMODE_W1);
264         xmap9_rev = npregs->set.dcbdata0.bybytes.b3 & 7;
265
266         npregs->set.dcbmode = (DCB_BT445 | BT445_PROTOCOL |
267                                BT445_CSR_ADDR_REG | NPORT_DMODE_W1);
268         npregs->set.dcbdata0.bybytes.b3 = BT445_REVISION_REG;
269         npregs->set.dcbmode = (DCB_BT445 | BT445_PROTOCOL |
270                                BT445_CSR_REVISION | NPORT_DMODE_W1);
271         bt445_rev = (npregs->set.dcbdata0.bybytes.b3 >> 4) - 0x0a;
272
273 #define L(a)     (char)('A'+(a))
274         printk
275             ("NG1: Revision %d, %d bitplanes, REX3 revision %c, VC2 revision %c, xmap9 revision %c, cmap revision %c, bt445 revision %c\n",
276              board_rev, bitplanes, L(rex3_rev), L(vc2_rev), L(xmap9_rev),
277              L(cmap_rev ? (cmap_rev + 1) : 0), L(bt445_rev));
278 #undef L
279
280         if (board_rev == 3)     /* I don't know all affected revisions */
281                 xcurs_correction = 21;
282 }
283
284 #ifdef MODULE
285 static const char *newport_startup(void)
286 #else
287 static const char *__init newport_startup(void)
288 #endif
289 {
290         int i;
291         struct newport_regs *p;
292
293         npregs = (struct newport_regs *) (KSEG1 + 0x1f0f0000);
294
295         p = npregs;
296         p->cset.config = NPORT_CFG_GD0;
297
298         if (newport_wait()) {
299                 return NULL;
300         }
301
302         p->set.xstarti = TESTVAL;
303         if (p->set._xstart.word != XSTI_TO_FXSTART(TESTVAL))
304                 return NULL;
305
306         for (i = 0; i < MAX_NR_CONSOLES; i++)
307                 font_data[i] = FONT_DATA;
308
309         newport_reset();
310         newport_get_revisions();
311         newport_get_screensize();
312
313         /* gfx_init (display_desc); */
314
315         return "SGI Newport";
316 }
317
318 static void newport_init(struct vc_data *vc, int init)
319 {
320         vc->vc_cols = newport_xsize / 8;
321         vc->vc_rows = newport_ysize / 16;
322         vc->vc_can_do_color = 1;
323 }
324
325 static void newport_clear(struct vc_data *vc, int sy, int sx, int height,
326                           int width)
327 {
328         int xend = ((sx + width) << 3) - 1;
329         int ystart = ((sy << 4) + topscan) & 0x3ff;
330         int yend = (((sy + height) << 4) + topscan - 1) & 0x3ff;
331
332         if (logo_active)
333                 return;
334
335         if (ystart < yend) {
336                 newport_clear_screen(sx << 3, ystart, xend, yend,
337                                      (vc->vc_color & 0xf0) >> 4);
338         } else {
339                 newport_clear_screen(sx << 3, ystart, xend, 1023,
340                                      (vc->vc_color & 0xf0) >> 4);
341                 newport_clear_screen(sx << 3, 0, xend, yend,
342                                      (vc->vc_color & 0xf0) >> 4);
343         }
344 }
345
346 static void newport_putc(struct vc_data *vc, int charattr, int ypos,
347                          int xpos)
348 {
349         unsigned char *p;
350
351         p = &font_data[vc->vc_num][(charattr & 0xff) << 4];
352         charattr = (charattr >> 8) & 0xff;
353         xpos <<= 3;
354         ypos <<= 4;
355
356         newport_render_background(xpos, ypos, xpos, ypos,
357                                   (charattr & 0xf0) >> 4);
358
359         /* Set the color and drawing mode. */
360         newport_wait();
361         npregs->set.colori = charattr & 0xf;
362         npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
363                                  NPORT_DMODE0_STOPX | NPORT_DMODE0_ZPENAB |
364                                  NPORT_DMODE0_L32);
365
366         /* Set coordinates for bitmap operation. */
367         npregs->set.xystarti = (xpos << 16) | ((ypos + topscan) & 0x3ff);
368         npregs->set.xyendi = ((xpos + 7) << 16);
369         newport_wait();
370
371         /* Go, baby, go... */
372         RENDER(npregs, p);
373 }
374
375 static void newport_putcs(struct vc_data *vc, const unsigned short *s,
376                           int count, int ypos, int xpos)
377 {
378         int i;
379         int charattr;
380         unsigned char *p;
381
382         charattr = (*s >> 8) & 0xff;
383
384         xpos <<= 3;
385         ypos <<= 4;
386
387         if (!logo_active)
388                 /* Clear the area behing the string */
389                 newport_render_background(xpos, ypos,
390                                           xpos + ((count - 1) << 3), ypos,
391                                           (charattr & 0xf0) >> 4);
392
393         newport_wait();
394
395         /* Set the color and drawing mode. */
396         npregs->set.colori = charattr & 0xf;
397         npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
398                                  NPORT_DMODE0_STOPX | NPORT_DMODE0_ZPENAB |
399                                  NPORT_DMODE0_L32);
400
401         for (i = 0; i < count; i++, xpos += 8) {
402                 p = &font_data[vc->vc_num][(s[i] & 0xff) << 4];
403
404                 newport_wait();
405
406                 /* Set coordinates for bitmap operation. */
407                 npregs->set.xystarti =
408                     (xpos << 16) | ((ypos + topscan) & 0x3ff);
409                 npregs->set.xyendi = ((xpos + 7) << 16);
410
411                 /* Go, baby, go... */
412                 RENDER(npregs, p);
413         }
414 }
415
416 static void newport_cursor(struct vc_data *vc, int mode)
417 {
418         unsigned short treg;
419         int xcurs, ycurs;
420
421         switch (mode) {
422         case CM_ERASE:
423                 treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
424                 newport_vc2_set(npregs, VC2_IREG_CONTROL,
425                                 (treg & ~(VC2_CTRL_ECDISP)));
426                 break;
427
428         case CM_MOVE:
429         case CM_DRAW:
430                 treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
431                 newport_vc2_set(npregs, VC2_IREG_CONTROL,
432                                 (treg | VC2_CTRL_ECDISP));
433                 xcurs = (vc->vc_pos - vc->vc_visible_origin) / 2;
434                 ycurs = ((xcurs / vc->vc_cols) << 4) + 31;
435                 xcurs = ((xcurs % vc->vc_cols) << 3) + xcurs_correction;
436                 newport_vc2_set(npregs, VC2_IREG_CURSX, xcurs);
437                 newport_vc2_set(npregs, VC2_IREG_CURSY, ycurs);
438         }
439 }
440
441 static int newport_switch(struct vc_data *vc)
442 {
443         static int logo_drawn = 0;
444
445         topscan = 0;
446         npregs->cset.topscan = 0x3ff;
447
448         if (!logo_drawn) {
449                 newport_show_logo();
450                 logo_drawn = 1;
451                 logo_active = 1;
452         }
453
454         return 1;
455 }
456
457 static int newport_blank(struct vc_data *c, int blank)
458 {
459         unsigned short treg;
460
461         if (blank == 0) {
462                 /* unblank console */
463                 treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
464                 newport_vc2_set(npregs, VC2_IREG_CONTROL,
465                                 (treg | VC2_CTRL_EDISP));
466         } else {
467                 /* blank console */
468                 treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
469                 newport_vc2_set(npregs, VC2_IREG_CONTROL,
470                                 (treg & ~(VC2_CTRL_EDISP)));
471         }
472         return 1;
473 }
474
475 static int newport_set_font(int unit, struct console_font_op *op)
476 {
477         int w = op->width;
478         int h = op->height;
479         int size = h * op->charcount;
480         int i;
481         unsigned char *new_data, *data = op->data, *p;
482
483         /* ladis: when I grow up, there will be a day... and more sizes will
484          * be supported ;-) */
485         if ((w != 8) || (h != 16)
486             || (op->charcount != 256 && op->charcount != 512))
487                 return -EINVAL;
488
489         if (!(new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size,
490              GFP_USER))) return -ENOMEM;
491
492         new_data += FONT_EXTRA_WORDS * sizeof(int);
493         FNTSIZE(new_data) = size;
494         FNTCHARCNT(new_data) = op->charcount;
495         REFCOUNT(new_data) = 0; /* usage counter */
496
497         p = new_data;
498         for (i = 0; i < op->charcount; i++) {
499                 memcpy(p, data, h);
500                 data += 32;
501                 p += h;
502         }
503
504         /* check if font is already used by other console */
505         for (i = 0; i < MAX_NR_CONSOLES; i++) {
506                 if (font_data[i] != FONT_DATA
507                     && FNTSIZE(font_data[i]) == size
508                     && !memcmp(font_data[i], new_data, size)) {
509                         kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
510                         /* current font is the same as the new one */
511                         if (i == unit)
512                                 return 0;
513                         new_data = font_data[i];
514                         break;
515                 }
516         }
517         /* old font is user font */
518         if (font_data[unit] != FONT_DATA) {
519                 if (--REFCOUNT(font_data[unit]) == 0)
520                         kfree(font_data[unit] -
521                               FONT_EXTRA_WORDS * sizeof(int));
522         }
523         REFCOUNT(new_data)++;
524         font_data[unit] = new_data;
525
526         return 0;
527 }
528
529 static int newport_set_def_font(int unit, struct console_font_op *op)
530 {
531         if (font_data[unit] != FONT_DATA) {
532                 if (--REFCOUNT(font_data[unit]) == 0)
533                         kfree(font_data[unit] -
534                               FONT_EXTRA_WORDS * sizeof(int));
535                 font_data[unit] = FONT_DATA;
536         }
537
538         return 0;
539 }
540
541 static int newport_font_op(struct vc_data *vc, struct console_font_op *op)
542 {
543         int unit = vc->vc_num;
544
545         switch (op->op) {
546         case KD_FONT_OP_SET:
547                 return newport_set_font(unit, op);
548         case KD_FONT_OP_SET_DEFAULT:
549                 return newport_set_def_font(unit, op);
550         default:
551                 return -ENOSYS;
552         }
553 }
554
555 static int newport_set_palette(struct vc_data *vc, unsigned char *table)
556 {
557         return -EINVAL;
558 }
559
560 static int newport_scrolldelta(struct vc_data *vc, int lines)
561 {
562         /* there is (nearly) no off-screen memory, so we can't scroll back */
563         return 0;
564 }
565
566 static int newport_scroll(struct vc_data *vc, int t, int b, int dir,
567                           int lines)
568 {
569         int count, x, y;
570         unsigned short *s, *d;
571         unsigned short chattr;
572
573         logo_active = 0;        /* it's time to disable the logo now.. */
574
575         if (t == 0 && b == vc->vc_rows) {
576                 if (dir == SM_UP) {
577                         topscan = (topscan + (lines << 4)) & 0x3ff;
578                         newport_clear_lines(vc->vc_rows - lines,
579                                             vc->vc_rows - 1,
580                                             (vc->vc_color & 0xf0) >> 4);
581                 } else {
582                         topscan = (topscan + (-lines << 4)) & 0x3ff;
583                         newport_clear_lines(0, lines - 1,
584                                             (vc->vc_color & 0xf0) >> 4);
585                 }
586                 npregs->cset.topscan = (topscan - 1) & 0x3ff;
587                 return 0;
588         }
589
590         count = (b - t - lines) * vc->vc_cols;
591         if (dir == SM_UP) {
592                 x = 0;
593                 y = t;
594                 s = (unsigned short *) (vc->vc_origin +
595                                         vc->vc_size_row * (t + lines));
596                 d = (unsigned short *) (vc->vc_origin +
597                                         vc->vc_size_row * t);
598                 while (count--) {
599                         chattr = scr_readw(s++);
600                         if (chattr != scr_readw(d)) {
601                                 newport_putc(vc, chattr, y, x);
602                                 scr_writew(chattr, d);
603                         }
604                         d++;
605                         if (++x == vc->vc_cols) {
606                                 x = 0;
607                                 y++;
608                         }
609                 }
610                 d = (unsigned short *) (vc->vc_origin +
611                                         vc->vc_size_row * (b - lines));
612                 x = 0;
613                 y = b - lines;
614                 for (count = 0; count < (lines * vc->vc_cols); count++) {
615                         if (scr_readw(d) != vc->vc_video_erase_char) {
616                                 newport_putc(vc, vc->vc_video_erase_char,
617                                              y, x);
618                                 scr_writew(vc->vc_video_erase_char, d);
619                         }
620                         d++;
621                         if (++x == vc->vc_cols) {
622                                 x = 0;
623                                 y++;
624                         }
625                 }
626         } else {
627                 x = vc->vc_cols - 1;
628                 y = b - 1;
629                 s = (unsigned short *) (vc->vc_origin +
630                                         vc->vc_size_row * (b - lines) - 2);
631                 d = (unsigned short *) (vc->vc_origin +
632                                         vc->vc_size_row * b - 2);
633                 while (count--) {
634                         chattr = scr_readw(s--);
635                         if (chattr != scr_readw(d)) {
636                                 newport_putc(vc, chattr, y, x);
637                                 scr_writew(chattr, d);
638                         }
639                         d--;
640                         if (x-- == 0) {
641                                 x = vc->vc_cols - 1;
642                                 y--;
643                         }
644                 }
645                 d = (unsigned short *) (vc->vc_origin +
646                                         vc->vc_size_row * t);
647                 x = 0;
648                 y = t;
649                 for (count = 0; count < (lines * vc->vc_cols); count++) {
650                         if (scr_readw(d) != vc->vc_video_erase_char) {
651                                 newport_putc(vc, vc->vc_video_erase_char,
652                                              y, x);
653                                 scr_writew(vc->vc_video_erase_char, d);
654                         }
655                         d++;
656                         if (++x == vc->vc_cols) {
657                                 x = 0;
658                                 y++;
659                         }
660                 }
661         }
662         return 1;
663 }
664
665 static void newport_bmove(struct vc_data *vc, int sy, int sx, int dy,
666                           int dx, int h, int w)
667 {
668         short xs, ys, xe, ye, xoffs, yoffs, tmp;
669
670         xs = sx << 3;
671         xe = ((sx + w) << 3) - 1;
672         /*
673          * as bmove is only used to move stuff around in the same line
674          * (h == 1), we don't care about wrap arounds caused by topscan != 0
675          */
676         ys = ((sy << 4) + topscan) & 0x3ff;
677         ye = (((sy + h) << 4) - 1 + topscan) & 0x3ff;
678         xoffs = (dx - sx) << 3;
679         yoffs = (dy - sy) << 4;
680         if (xoffs > 0) {
681                 /* move to the right, exchange starting points */
682                 tmp = xe;
683                 xe = xs;
684                 xs = tmp;
685         }
686         newport_wait();
687         npregs->set.drawmode0 = (NPORT_DMODE0_S2S | NPORT_DMODE0_BLOCK |
688                                  NPORT_DMODE0_DOSETUP | NPORT_DMODE0_STOPX
689                                  | NPORT_DMODE0_STOPY);
690         npregs->set.xystarti = (xs << 16) | ys;
691         npregs->set.xyendi = (xe << 16) | ye;
692         npregs->go.xymove = (xoffs << 16) | yoffs;
693 }
694
695 static int newport_dummy(struct vc_data *c)
696 {
697         return 0;
698 }
699
700 #define DUMMY (void *) newport_dummy
701
702 const struct consw newport_con = {
703     con_startup:        newport_startup,
704     con_init:           newport_init,
705     con_deinit:         DUMMY,
706     con_clear:          newport_clear,
707     con_putc:           newport_putc,
708     con_putcs:          newport_putcs,
709     con_cursor:         newport_cursor,
710     con_scroll:         newport_scroll,
711     con_bmove:          newport_bmove,
712     con_switch:         newport_switch,
713     con_blank:          newport_blank,
714     con_font_op:        newport_font_op,
715     con_set_palette:    newport_set_palette,
716     con_scrolldelta:    newport_scrolldelta,
717     con_set_origin:     DUMMY,
718     con_save_screen:    DUMMY
719 };
720
721 #ifdef MODULE
722 MODULE_LICENSE("GPL");
723
724 int init_module(void)
725 {
726         if (!newport_startup())
727                 printk("Error loading SGI Newport Console driver\n");
728         else
729                 printk("Loading SGI Newport Console Driver\n");
730         take_over_console(&newport_con, 0, MAX_NR_CONSOLES - 1, 1);
731
732         return 0;
733 }
734
735 int cleanup_module(void)
736 {
737         int i;
738
739         printk("Unloading SGI Newport Console Driver\n");
740         /* free memory used by user font */
741         for (i = 0; i < MAX_NR_CONSOLES; i++)
742                 newport_set_def_font(i, NULL);
743
744         return 0;
745 }
746 #endif