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
btext_initialize(phandle node)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) */
calc_base(int x,int 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
btext_clearscreen(void)94 static void btext_clearscreen(void)
95 {
96 unsigned int *base = (unsigned int *)calc_base(0, 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
scrollscreen(void)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
btext_drawchar(char c)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, g_loc_X++, 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(' ', x, g_loc_Y);
181 }
182 #endif
183 }
184
btext_drawtext(const char * c,unsigned int len)185 static void btext_drawtext(const char *c, unsigned int len)
186 {
187 while (len--)
188 btext_drawchar(*c++);
189 }
190
draw_byte(unsigned char c,long locX,long locY)191 static void draw_byte(unsigned char c, long locX, long locY)
192 {
193 unsigned char *base = calc_base(locX << 3, 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(font, (unsigned int *)base, rb);
202 break;
203 case 15:
204 case 16:
205 draw_byte_16(font, (unsigned int *)base, rb);
206 break;
207 case 8:
208 draw_byte_8(font, (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
draw_byte_32(const unsigned char * font,unsigned int * base,int rb)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
draw_byte_16(const unsigned char * font,unsigned int * base,int rb)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
draw_byte_8(const unsigned char * font,unsigned int * base,int rb)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
btext_console_write(struct console * con,const char * s,unsigned int n)295 static void btext_console_write(struct console *con, const char *s,
296 unsigned int n)
297 {
298 btext_drawtext(s, 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
btext_find_display(void)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