1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Procedures for drawing on the screen early on in the boot process. |
4 | * |
5 | * Benjamin Herrenschmidt <benh@kernel.crashing.org> |
6 | */ |
7 | #include <linux/kernel.h> |
8 | #include <linux/string.h> |
9 | #include <linux/init.h> |
10 | #include <linux/console.h> |
11 | #include <linux/font.h> |
12 | |
13 | #include <asm/btext.h> |
14 | #include <asm/oplib.h> |
15 | #include <asm/io.h> |
16 | |
17 | #define NO_SCROLL |
18 | |
19 | #ifndef NO_SCROLL |
20 | static void scrollscreen(void); |
21 | #endif |
22 | |
23 | static void draw_byte(unsigned char c, long locX, long locY); |
24 | static void draw_byte_32(const unsigned char *bits, unsigned int *base, int rb); |
25 | static void draw_byte_16(const unsigned char *bits, unsigned int *base, int rb); |
26 | static void draw_byte_8(const unsigned char *bits, unsigned int *base, int rb); |
27 | |
28 | #define __force_data __section(".data") |
29 | |
30 | static int g_loc_X __force_data; |
31 | static int g_loc_Y __force_data; |
32 | static int g_max_loc_X __force_data; |
33 | static int g_max_loc_Y __force_data; |
34 | |
35 | static int dispDeviceRowBytes __force_data; |
36 | static int dispDeviceDepth __force_data; |
37 | static int dispDeviceRect[4] __force_data; |
38 | static unsigned char *dispDeviceBase __force_data; |
39 | |
40 | static int __init btext_initialize(phandle node) |
41 | { |
42 | unsigned int width, height, depth, pitch; |
43 | unsigned long address = 0; |
44 | u32 prop; |
45 | |
46 | if (prom_getproperty(node, "width" , (char *)&width, 4) < 0) |
47 | return -EINVAL; |
48 | if (prom_getproperty(node, "height" , (char *)&height, 4) < 0) |
49 | return -EINVAL; |
50 | if (prom_getproperty(node, "depth" , (char *)&depth, 4) < 0) |
51 | return -EINVAL; |
52 | pitch = width * ((depth + 7) / 8); |
53 | |
54 | if (prom_getproperty(node, "linebytes" , (char *)&prop, 4) >= 0 && |
55 | prop != 0xffffffffu) |
56 | pitch = prop; |
57 | |
58 | if (pitch == 1) |
59 | pitch = 0x1000; |
60 | |
61 | if (prom_getproperty(node, "address" , (char *)&prop, 4) >= 0) |
62 | address = prop; |
63 | |
64 | /* FIXME: Add support for PCI reg properties. Right now, only |
65 | * reliable on macs |
66 | */ |
67 | if (address == 0) |
68 | return -EINVAL; |
69 | |
70 | g_loc_X = 0; |
71 | g_loc_Y = 0; |
72 | g_max_loc_X = width / 8; |
73 | g_max_loc_Y = height / 16; |
74 | dispDeviceBase = (unsigned char *)address; |
75 | dispDeviceRowBytes = pitch; |
76 | dispDeviceDepth = depth == 15 ? 16 : depth; |
77 | dispDeviceRect[0] = dispDeviceRect[1] = 0; |
78 | dispDeviceRect[2] = width; |
79 | dispDeviceRect[3] = height; |
80 | |
81 | return 0; |
82 | } |
83 | |
84 | /* Calc the base address of a given point (x,y) */ |
85 | static unsigned char * calc_base(int x, int y) |
86 | { |
87 | unsigned char *base = dispDeviceBase; |
88 | |
89 | base += (x + dispDeviceRect[0]) * (dispDeviceDepth >> 3); |
90 | base += (y + dispDeviceRect[1]) * dispDeviceRowBytes; |
91 | return base; |
92 | } |
93 | |
94 | static void btext_clearscreen(void) |
95 | { |
96 | unsigned int *base = (unsigned int *)calc_base(x: 0, y: 0); |
97 | unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) * |
98 | (dispDeviceDepth >> 3)) >> 2; |
99 | int i,j; |
100 | |
101 | for (i=0; i<(dispDeviceRect[3] - dispDeviceRect[1]); i++) |
102 | { |
103 | unsigned int *ptr = base; |
104 | for(j=width; j; --j) |
105 | *(ptr++) = 0; |
106 | base += (dispDeviceRowBytes >> 2); |
107 | } |
108 | } |
109 | |
110 | #ifndef NO_SCROLL |
111 | static void scrollscreen(void) |
112 | { |
113 | unsigned int *src = (unsigned int *)calc_base(0,16); |
114 | unsigned int *dst = (unsigned int *)calc_base(0,0); |
115 | unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) * |
116 | (dispDeviceDepth >> 3)) >> 2; |
117 | int i,j; |
118 | |
119 | for (i=0; i<(dispDeviceRect[3] - dispDeviceRect[1] - 16); i++) |
120 | { |
121 | unsigned int *src_ptr = src; |
122 | unsigned int *dst_ptr = dst; |
123 | for(j=width; j; --j) |
124 | *(dst_ptr++) = *(src_ptr++); |
125 | src += (dispDeviceRowBytes >> 2); |
126 | dst += (dispDeviceRowBytes >> 2); |
127 | } |
128 | for (i=0; i<16; i++) |
129 | { |
130 | unsigned int *dst_ptr = dst; |
131 | for(j=width; j; --j) |
132 | *(dst_ptr++) = 0; |
133 | dst += (dispDeviceRowBytes >> 2); |
134 | } |
135 | } |
136 | #endif /* ndef NO_SCROLL */ |
137 | |
138 | static void btext_drawchar(char c) |
139 | { |
140 | int cline = 0; |
141 | #ifdef NO_SCROLL |
142 | int x; |
143 | #endif |
144 | switch (c) { |
145 | case '\b': |
146 | if (g_loc_X > 0) |
147 | --g_loc_X; |
148 | break; |
149 | case '\t': |
150 | g_loc_X = (g_loc_X & -8) + 8; |
151 | break; |
152 | case '\r': |
153 | g_loc_X = 0; |
154 | break; |
155 | case '\n': |
156 | g_loc_X = 0; |
157 | g_loc_Y++; |
158 | cline = 1; |
159 | break; |
160 | default: |
161 | draw_byte(c, locX: g_loc_X++, locY: g_loc_Y); |
162 | } |
163 | if (g_loc_X >= g_max_loc_X) { |
164 | g_loc_X = 0; |
165 | g_loc_Y++; |
166 | cline = 1; |
167 | } |
168 | #ifndef NO_SCROLL |
169 | while (g_loc_Y >= g_max_loc_Y) { |
170 | scrollscreen(); |
171 | g_loc_Y--; |
172 | } |
173 | #else |
174 | /* wrap around from bottom to top of screen so we don't |
175 | waste time scrolling each line. -- paulus. */ |
176 | if (g_loc_Y >= g_max_loc_Y) |
177 | g_loc_Y = 0; |
178 | if (cline) { |
179 | for (x = 0; x < g_max_loc_X; ++x) |
180 | draw_byte(c: ' ', locX: x, locY: g_loc_Y); |
181 | } |
182 | #endif |
183 | } |
184 | |
185 | static void btext_drawtext(const char *c, unsigned int len) |
186 | { |
187 | while (len--) |
188 | btext_drawchar(c: *c++); |
189 | } |
190 | |
191 | static void draw_byte(unsigned char c, long locX, long locY) |
192 | { |
193 | unsigned char *base = calc_base(x: locX << 3, y: locY << 4); |
194 | unsigned int font_index = c * 16; |
195 | const unsigned char *font = font_sun_8x16.data + font_index; |
196 | int rb = dispDeviceRowBytes; |
197 | |
198 | switch(dispDeviceDepth) { |
199 | case 24: |
200 | case 32: |
201 | draw_byte_32(bits: font, base: (unsigned int *)base, rb); |
202 | break; |
203 | case 15: |
204 | case 16: |
205 | draw_byte_16(bits: font, base: (unsigned int *)base, rb); |
206 | break; |
207 | case 8: |
208 | draw_byte_8(bits: font, base: (unsigned int *)base, rb); |
209 | break; |
210 | } |
211 | } |
212 | |
213 | static unsigned int expand_bits_8[16] = { |
214 | 0x00000000, |
215 | 0x000000ff, |
216 | 0x0000ff00, |
217 | 0x0000ffff, |
218 | 0x00ff0000, |
219 | 0x00ff00ff, |
220 | 0x00ffff00, |
221 | 0x00ffffff, |
222 | 0xff000000, |
223 | 0xff0000ff, |
224 | 0xff00ff00, |
225 | 0xff00ffff, |
226 | 0xffff0000, |
227 | 0xffff00ff, |
228 | 0xffffff00, |
229 | 0xffffffff |
230 | }; |
231 | |
232 | static unsigned int expand_bits_16[4] = { |
233 | 0x00000000, |
234 | 0x0000ffff, |
235 | 0xffff0000, |
236 | 0xffffffff |
237 | }; |
238 | |
239 | |
240 | static void draw_byte_32(const unsigned char *font, unsigned int *base, int rb) |
241 | { |
242 | int l, bits; |
243 | int fg = 0xFFFFFFFFUL; |
244 | int bg = 0x00000000UL; |
245 | |
246 | for (l = 0; l < 16; ++l) |
247 | { |
248 | bits = *font++; |
249 | base[0] = (-(bits >> 7) & fg) ^ bg; |
250 | base[1] = (-((bits >> 6) & 1) & fg) ^ bg; |
251 | base[2] = (-((bits >> 5) & 1) & fg) ^ bg; |
252 | base[3] = (-((bits >> 4) & 1) & fg) ^ bg; |
253 | base[4] = (-((bits >> 3) & 1) & fg) ^ bg; |
254 | base[5] = (-((bits >> 2) & 1) & fg) ^ bg; |
255 | base[6] = (-((bits >> 1) & 1) & fg) ^ bg; |
256 | base[7] = (-(bits & 1) & fg) ^ bg; |
257 | base = (unsigned int *) ((char *)base + rb); |
258 | } |
259 | } |
260 | |
261 | static void draw_byte_16(const unsigned char *font, unsigned int *base, int rb) |
262 | { |
263 | int l, bits; |
264 | int fg = 0xFFFFFFFFUL; |
265 | int bg = 0x00000000UL; |
266 | unsigned int *eb = (int *)expand_bits_16; |
267 | |
268 | for (l = 0; l < 16; ++l) |
269 | { |
270 | bits = *font++; |
271 | base[0] = (eb[bits >> 6] & fg) ^ bg; |
272 | base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg; |
273 | base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg; |
274 | base[3] = (eb[bits & 3] & fg) ^ bg; |
275 | base = (unsigned int *) ((char *)base + rb); |
276 | } |
277 | } |
278 | |
279 | static void draw_byte_8(const unsigned char *font, unsigned int *base, int rb) |
280 | { |
281 | int l, bits; |
282 | int fg = 0x0F0F0F0FUL; |
283 | int bg = 0x00000000UL; |
284 | unsigned int *eb = (int *)expand_bits_8; |
285 | |
286 | for (l = 0; l < 16; ++l) |
287 | { |
288 | bits = *font++; |
289 | base[0] = (eb[bits >> 4] & fg) ^ bg; |
290 | base[1] = (eb[bits & 0xf] & fg) ^ bg; |
291 | base = (unsigned int *) ((char *)base + rb); |
292 | } |
293 | } |
294 | |
295 | static void btext_console_write(struct console *con, const char *s, |
296 | unsigned int n) |
297 | { |
298 | btext_drawtext(c: s, len: n); |
299 | } |
300 | |
301 | static struct console btext_console = { |
302 | .name = "btext" , |
303 | .write = btext_console_write, |
304 | .flags = CON_PRINTBUFFER | CON_ENABLED | CON_BOOT | CON_ANYTIME, |
305 | .index = 0, |
306 | }; |
307 | |
308 | int __init btext_find_display(void) |
309 | { |
310 | phandle node; |
311 | char type[32]; |
312 | int ret; |
313 | |
314 | node = prom_inst2pkg(prom_stdout); |
315 | if (prom_getproperty(node, "device_type" , type, 32) < 0) |
316 | return -ENODEV; |
317 | if (strcmp(type, "display" )) |
318 | return -ENODEV; |
319 | |
320 | ret = btext_initialize(node); |
321 | if (!ret) { |
322 | btext_clearscreen(); |
323 | register_console(&btext_console); |
324 | } |
325 | return ret; |
326 | } |
327 | |