1 | /* |
2 | * linux/drivers/video/console/sticon.c - console driver using HP's STI firmware |
3 | * |
4 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> |
5 | * Copyright (C) 2002-2020 Helge Deller <deller@gmx.de> |
6 | * |
7 | * Based on linux/drivers/video/vgacon.c and linux/drivers/video/fbcon.c, |
8 | * which were |
9 | * |
10 | * Created 28 Sep 1997 by Geert Uytterhoeven |
11 | * Rewritten by Martin Mares <mj@ucw.cz>, July 1998 |
12 | * Copyright (C) 1991, 1992 Linus Torvalds |
13 | * 1995 Jay Estabrook |
14 | * Copyright (C) 1995 Geert Uytterhoeven |
15 | * Copyright (C) 1993 Bjoern Brauel |
16 | * Roman Hodek |
17 | * Copyright (C) 1993 Hamish Macdonald |
18 | * Greg Harp |
19 | * Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk] |
20 | * |
21 | * with work by William Rucklidge (wjr@cs.cornell.edu) |
22 | * Geert Uytterhoeven |
23 | * Jes Sorensen (jds@kom.auc.dk) |
24 | * Martin Apel |
25 | * with work by Guenther Kelleter |
26 | * Martin Schaller |
27 | * Andreas Schwab |
28 | * Emmanuel Marty (core@ggi-project.org) |
29 | * Jakub Jelinek (jj@ultra.linux.cz) |
30 | * Martin Mares <mj@ucw.cz> |
31 | * |
32 | * This file is subject to the terms and conditions of the GNU General Public |
33 | * License. See the file COPYING in the main directory of this archive for |
34 | * more details. |
35 | * |
36 | */ |
37 | |
38 | #include <linux/init.h> |
39 | #include <linux/kernel.h> |
40 | #include <linux/console.h> |
41 | #include <linux/errno.h> |
42 | #include <linux/vt_kern.h> |
43 | #include <linux/kd.h> |
44 | #include <linux/selection.h> |
45 | #include <linux/module.h> |
46 | #include <linux/slab.h> |
47 | #include <linux/font.h> |
48 | #include <linux/crc32.h> |
49 | #include <linux/fb.h> |
50 | |
51 | #include <asm/io.h> |
52 | |
53 | #include <video/sticore.h> |
54 | |
55 | /* switching to graphics mode */ |
56 | #define BLANK 0 |
57 | static int vga_is_gfx; |
58 | |
59 | #define STI_DEF_FONT sticon_sti->font |
60 | |
61 | /* borrowed from fbcon.c */ |
62 | #define FNTREFCOUNT(fd) (fd->refcount) |
63 | #define FNTCRC(fd) (fd->crc) |
64 | static struct sti_cooked_font *font_data[MAX_NR_CONSOLES]; |
65 | |
66 | /* this is the sti_struct used for this console */ |
67 | static struct sti_struct *sticon_sti; |
68 | |
69 | static const char *sticon_startup(void) |
70 | { |
71 | return "STI console" ; |
72 | } |
73 | |
74 | static void sticon_putcs(struct vc_data *conp, const u16 *s, unsigned int count, |
75 | unsigned int ypos, unsigned int xpos) |
76 | { |
77 | if (vga_is_gfx || console_blanked) |
78 | return; |
79 | |
80 | if (conp->vc_mode != KD_TEXT) |
81 | return; |
82 | |
83 | while (count--) { |
84 | sti_putc(sti: sticon_sti, scr_readw(s++), y: ypos, x: xpos++, |
85 | font: font_data[conp->vc_num]); |
86 | } |
87 | } |
88 | |
89 | static void sticon_cursor(struct vc_data *conp, bool enable) |
90 | { |
91 | unsigned short car1; |
92 | |
93 | /* no cursor update if screen is blanked */ |
94 | if (vga_is_gfx || console_blanked) |
95 | return; |
96 | |
97 | car1 = conp->vc_screenbuf[conp->state.x + conp->state.y * conp->vc_cols]; |
98 | if (!enable) { |
99 | sti_putc(sti: sticon_sti, c: car1, y: conp->state.y, x: conp->state.x, |
100 | font: font_data[conp->vc_num]); |
101 | return; |
102 | } |
103 | |
104 | switch (CUR_SIZE(conp->vc_cursor_type)) { |
105 | case CUR_UNDERLINE: |
106 | case CUR_LOWER_THIRD: |
107 | case CUR_LOWER_HALF: |
108 | case CUR_TWO_THIRDS: |
109 | case CUR_BLOCK: |
110 | sti_putc(sti: sticon_sti, c: (car1 & 255) + (0 << 8) + (7 << 11), |
111 | y: conp->state.y, x: conp->state.x, font: font_data[conp->vc_num]); |
112 | break; |
113 | } |
114 | } |
115 | |
116 | static bool sticon_scroll(struct vc_data *conp, unsigned int t, |
117 | unsigned int b, enum con_scroll dir, unsigned int count) |
118 | { |
119 | struct sti_struct *sti = sticon_sti; |
120 | |
121 | if (vga_is_gfx) |
122 | return false; |
123 | |
124 | sticon_cursor(conp, enable: false); |
125 | |
126 | switch (dir) { |
127 | case SM_UP: |
128 | sti_bmove(sti, src_y: t + count, src_x: 0, dst_y: t, dst_x: 0, height: b - t - count, width: conp->vc_cols, |
129 | font: font_data[conp->vc_num]); |
130 | sti_clear(sti, src_y: b - count, src_x: 0, height: count, width: conp->vc_cols, |
131 | c: conp->vc_video_erase_char, font: font_data[conp->vc_num]); |
132 | break; |
133 | |
134 | case SM_DOWN: |
135 | sti_bmove(sti, src_y: t, src_x: 0, dst_y: t + count, dst_x: 0, height: b - t - count, width: conp->vc_cols, |
136 | font: font_data[conp->vc_num]); |
137 | sti_clear(sti, src_y: t, src_x: 0, height: count, width: conp->vc_cols, |
138 | c: conp->vc_video_erase_char, font: font_data[conp->vc_num]); |
139 | break; |
140 | } |
141 | |
142 | return false; |
143 | } |
144 | |
145 | static void sticon_set_def_font(int unit) |
146 | { |
147 | if (font_data[unit] != STI_DEF_FONT) { |
148 | if (--FNTREFCOUNT(font_data[unit]) == 0) { |
149 | kfree(objp: font_data[unit]->raw_ptr); |
150 | kfree(objp: font_data[unit]); |
151 | } |
152 | font_data[unit] = STI_DEF_FONT; |
153 | } |
154 | } |
155 | |
156 | static int sticon_set_font(struct vc_data *vc, const struct console_font *op, |
157 | unsigned int vpitch) |
158 | { |
159 | struct sti_struct *sti = sticon_sti; |
160 | int vc_cols, vc_rows, vc_old_cols, vc_old_rows; |
161 | int unit = vc->vc_num; |
162 | int w = op->width; |
163 | int h = op->height; |
164 | int size, i, bpc, pitch; |
165 | struct sti_rom_font *new_font; |
166 | struct sti_cooked_font *cooked_font; |
167 | unsigned char *data = op->data, *p; |
168 | |
169 | if ((w < 6) || (h < 6) || (w > 32) || (h > 32) || (vpitch != 32) |
170 | || (op->charcount != 256 && op->charcount != 512)) |
171 | return -EINVAL; |
172 | pitch = ALIGN(w, 8) / 8; |
173 | bpc = pitch * h; |
174 | size = bpc * op->charcount; |
175 | |
176 | new_font = kmalloc(size: sizeof(*new_font) + size, STI_LOWMEM); |
177 | if (!new_font) |
178 | return -ENOMEM; |
179 | |
180 | new_font->first_char = 0; |
181 | new_font->last_char = op->charcount - 1; |
182 | new_font->width = w; |
183 | new_font->height = h; |
184 | new_font->font_type = STI_FONT_HPROMAN8; |
185 | new_font->bytes_per_char = bpc; |
186 | new_font->underline_height = 0; |
187 | new_font->underline_pos = 0; |
188 | |
189 | cooked_font = kzalloc(size: sizeof(*cooked_font), GFP_KERNEL); |
190 | if (!cooked_font) { |
191 | kfree(objp: new_font); |
192 | return -ENOMEM; |
193 | } |
194 | cooked_font->raw = new_font; |
195 | cooked_font->raw_ptr = new_font; |
196 | cooked_font->width = w; |
197 | cooked_font->height = h; |
198 | FNTREFCOUNT(cooked_font) = 0; /* usage counter */ |
199 | |
200 | p = (unsigned char *) new_font; |
201 | p += sizeof(*new_font); |
202 | for (i = 0; i < op->charcount; i++) { |
203 | memcpy(p, data, bpc); |
204 | data += pitch*32; |
205 | p += bpc; |
206 | } |
207 | FNTCRC(cooked_font) = crc32(0, new_font, size + sizeof(*new_font)); |
208 | sti_font_convert_bytemode(sti, f: cooked_font); |
209 | new_font = cooked_font->raw_ptr; |
210 | |
211 | /* check if font is already used by other console */ |
212 | for (i = 0; i < MAX_NR_CONSOLES; i++) { |
213 | if (font_data[i] != STI_DEF_FONT |
214 | && (FNTCRC(font_data[i]) == FNTCRC(cooked_font))) { |
215 | kfree(objp: new_font); |
216 | kfree(objp: cooked_font); |
217 | /* current font is the same as the new one */ |
218 | if (i == unit) |
219 | return 0; |
220 | cooked_font = font_data[i]; |
221 | new_font = cooked_font->raw_ptr; |
222 | break; |
223 | } |
224 | } |
225 | |
226 | /* clear screen with old font: we now may have less rows */ |
227 | vc_old_rows = vc->vc_rows; |
228 | vc_old_cols = vc->vc_cols; |
229 | sti_clear(sti: sticon_sti, src_y: 0, src_x: 0, height: vc_old_rows, width: vc_old_cols, |
230 | c: vc->vc_video_erase_char, font: font_data[vc->vc_num]); |
231 | |
232 | /* delete old font in case it is a user font */ |
233 | sticon_set_def_font(unit); |
234 | |
235 | FNTREFCOUNT(cooked_font)++; |
236 | font_data[unit] = cooked_font; |
237 | |
238 | vc_cols = sti_onscreen_x(sti) / cooked_font->width; |
239 | vc_rows = sti_onscreen_y(sti) / cooked_font->height; |
240 | vc_resize(vc, cols: vc_cols, lines: vc_rows); |
241 | |
242 | /* need to repaint screen if cols & rows are same as old font */ |
243 | if (vc_cols == vc_old_cols && vc_rows == vc_old_rows) |
244 | update_screen(vc); |
245 | |
246 | return 0; |
247 | } |
248 | |
249 | static int sticon_font_default(struct vc_data *vc, struct console_font *op, |
250 | const char *name) |
251 | { |
252 | sticon_set_def_font(unit: vc->vc_num); |
253 | |
254 | return 0; |
255 | } |
256 | |
257 | static int sticon_font_set(struct vc_data *vc, const struct console_font *font, |
258 | unsigned int vpitch, unsigned int flags) |
259 | { |
260 | return sticon_set_font(vc, op: font, vpitch); |
261 | } |
262 | |
263 | static void sticon_init(struct vc_data *c, bool init) |
264 | { |
265 | struct sti_struct *sti = sticon_sti; |
266 | int vc_cols, vc_rows; |
267 | |
268 | sti_set(sti, src_y: 0, src_x: 0, sti_onscreen_y(sti), sti_onscreen_x(sti), color: 0); |
269 | vc_cols = sti_onscreen_x(sti) / sti->font->width; |
270 | vc_rows = sti_onscreen_y(sti) / sti->font->height; |
271 | c->vc_can_do_color = 1; |
272 | |
273 | if (init) { |
274 | c->vc_cols = vc_cols; |
275 | c->vc_rows = vc_rows; |
276 | } else { |
277 | vc_resize(vc: c, cols: vc_cols, lines: vc_rows); |
278 | } |
279 | } |
280 | |
281 | static void sticon_deinit(struct vc_data *c) |
282 | { |
283 | int i; |
284 | |
285 | /* free memory used by user font */ |
286 | for (i = 0; i < MAX_NR_CONSOLES; i++) |
287 | sticon_set_def_font(unit: i); |
288 | } |
289 | |
290 | static void sticon_clear(struct vc_data *conp, unsigned int sy, unsigned int sx, |
291 | unsigned int width) |
292 | { |
293 | sti_clear(sti: sticon_sti, src_y: sy, src_x: sx, height: 1, width, |
294 | c: conp->vc_video_erase_char, font: font_data[conp->vc_num]); |
295 | } |
296 | |
297 | static bool sticon_switch(struct vc_data *conp) |
298 | { |
299 | return true; /* needs refreshing */ |
300 | } |
301 | |
302 | static bool sticon_blank(struct vc_data *c, enum vesa_blank_mode blank, |
303 | bool mode_switch) |
304 | { |
305 | if (blank == VESA_NO_BLANKING) { |
306 | if (mode_switch) |
307 | vga_is_gfx = 0; |
308 | return true; |
309 | } |
310 | sti_clear(sti: sticon_sti, src_y: 0, src_x: 0, height: c->vc_rows, width: c->vc_cols, BLANK, |
311 | font: font_data[c->vc_num]); |
312 | if (mode_switch) |
313 | vga_is_gfx = 1; |
314 | |
315 | return true; |
316 | } |
317 | |
318 | static u8 sticon_build_attr(struct vc_data *conp, u8 color, |
319 | enum vc_intensity intens, |
320 | bool blink, bool underline, bool reverse, |
321 | bool italic) |
322 | { |
323 | u8 fg = color & 7; |
324 | u8 bg = (color & 0x70) >> 4; |
325 | |
326 | if (reverse) |
327 | return (fg << 3) | bg; |
328 | else |
329 | return (bg << 3) | fg; |
330 | } |
331 | |
332 | static void sticon_invert_region(struct vc_data *conp, u16 *p, int count) |
333 | { |
334 | int col = 1; /* vga_can_do_color; */ |
335 | |
336 | while (count--) { |
337 | u16 a = scr_readw(p); |
338 | |
339 | if (col) |
340 | a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4); |
341 | else |
342 | a = ((a & 0x0700) == 0x0100) ? 0x7000 : 0x7700; |
343 | |
344 | scr_writew(a, p++); |
345 | } |
346 | } |
347 | |
348 | static const struct consw sti_con = { |
349 | .owner = THIS_MODULE, |
350 | .con_startup = sticon_startup, |
351 | .con_init = sticon_init, |
352 | .con_deinit = sticon_deinit, |
353 | .con_clear = sticon_clear, |
354 | .con_putcs = sticon_putcs, |
355 | .con_cursor = sticon_cursor, |
356 | .con_scroll = sticon_scroll, |
357 | .con_switch = sticon_switch, |
358 | .con_blank = sticon_blank, |
359 | .con_font_set = sticon_font_set, |
360 | .con_font_default = sticon_font_default, |
361 | .con_build_attr = sticon_build_attr, |
362 | .con_invert_region = sticon_invert_region, |
363 | }; |
364 | |
365 | |
366 | |
367 | static int __init sticonsole_init(void) |
368 | { |
369 | int err, i; |
370 | |
371 | /* already initialized ? */ |
372 | if (sticon_sti) |
373 | return 0; |
374 | |
375 | sticon_sti = sti_get_rom(index: 0); |
376 | if (!sticon_sti) |
377 | return -ENODEV; |
378 | |
379 | for (i = 0; i < MAX_NR_CONSOLES; i++) |
380 | font_data[i] = STI_DEF_FONT; |
381 | |
382 | pr_info("sticon: Initializing STI text console on %s at [%s]\n" , |
383 | sticon_sti->sti_data->inq_outptr.dev_name, |
384 | sticon_sti->pa_path); |
385 | console_lock(); |
386 | err = do_take_over_console(sw: &sti_con, first: 0, MAX_NR_CONSOLES - 1, |
387 | deflt: PAGE0->mem_cons.cl_class != CL_DUPLEX); |
388 | console_unlock(); |
389 | |
390 | return err; |
391 | } |
392 | |
393 | module_init(sticonsole_init); |
394 | MODULE_LICENSE("GPL" ); |
395 | |