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