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/export.h>
11 #include <linux/font.h>
12 #include <linux/memblock.h>
13 #include <linux/pgtable.h>
14 #include <linux/of.h>
15
16 #include <asm/sections.h>
17 #include <asm/btext.h>
18 #include <asm/page.h>
19 #include <asm/mmu.h>
20 #include <asm/io.h>
21 #include <asm/processor.h>
22 #include <asm/udbg.h>
23 #include <asm/setup.h>
24
25 #define NO_SCROLL
26
27 #ifndef NO_SCROLL
28 static void scrollscreen(void);
29 #endif
30
31 #define __force_data __section(".data")
32
33 static int g_loc_X __force_data;
34 static int g_loc_Y __force_data;
35 static int g_max_loc_X __force_data;
36 static int g_max_loc_Y __force_data;
37
38 static int dispDeviceRowBytes __force_data;
39 static int dispDeviceDepth __force_data;
40 static int dispDeviceRect[4] __force_data;
41 static unsigned char *dispDeviceBase __force_data;
42 static unsigned char *logicalDisplayBase __force_data;
43
44 unsigned long disp_BAT[2] __initdata = {0, 0};
45
46 static int boot_text_mapped __force_data;
47
48 extern void rmci_on(void);
49 extern void rmci_off(void);
50
rmci_maybe_on(void)51 static inline void rmci_maybe_on(void)
52 {
53 #if defined(CONFIG_PPC_EARLY_DEBUG_BOOTX) && defined(CONFIG_PPC64)
54 if (!(mfmsr() & MSR_DR))
55 rmci_on();
56 #endif
57 }
58
rmci_maybe_off(void)59 static inline void rmci_maybe_off(void)
60 {
61 #if defined(CONFIG_PPC_EARLY_DEBUG_BOOTX) && defined(CONFIG_PPC64)
62 if (!(mfmsr() & MSR_DR))
63 rmci_off();
64 #endif
65 }
66
67
68 #ifdef CONFIG_PPC32
69 /* Calc BAT values for mapping the display and store them
70 * in disp_BAT. Those values are then used from head.S to map
71 * the display during identify_machine() and MMU_Init()
72 *
73 * The display is mapped to virtual address 0xD0000000, rather
74 * than 1:1, because some CHRP machines put the frame buffer
75 * in the region starting at 0xC0000000 (PAGE_OFFSET).
76 * This mapping is temporary and will disappear as soon as the
77 * setup done by MMU_Init() is applied.
78 *
79 * For now, we align the BAT and then map 8Mb on 601 and 16Mb
80 * on other PPCs. This may cause trouble if the framebuffer
81 * is really badly aligned, but I didn't encounter this case
82 * yet.
83 */
btext_prepare_BAT(void)84 void __init btext_prepare_BAT(void)
85 {
86 unsigned long vaddr = PAGE_OFFSET + 0x10000000;
87 unsigned long addr;
88 unsigned long lowbits;
89
90 addr = (unsigned long)dispDeviceBase;
91 if (!addr) {
92 boot_text_mapped = 0;
93 return;
94 }
95 lowbits = addr & ~0xFF000000UL;
96 addr &= 0xFF000000UL;
97 disp_BAT[0] = vaddr | (BL_16M<<2) | 2;
98 disp_BAT[1] = addr | (_PAGE_NO_CACHE | _PAGE_GUARDED | BPP_RW);
99 logicalDisplayBase = (void *) (vaddr + lowbits);
100 }
101 #endif
102
103
104 /* This function can be used to enable the early boot text when doing
105 * OF booting or within bootx init. It must be followed by a btext_unmap()
106 * call before the logical address becomes unusable
107 */
btext_setup_display(int width,int height,int depth,int pitch,unsigned long address)108 void __init btext_setup_display(int width, int height, int depth, int pitch,
109 unsigned long address)
110 {
111 g_loc_X = 0;
112 g_loc_Y = 0;
113 g_max_loc_X = width / 8;
114 g_max_loc_Y = height / 16;
115 logicalDisplayBase = (unsigned char *)address;
116 dispDeviceBase = (unsigned char *)address;
117 dispDeviceRowBytes = pitch;
118 dispDeviceDepth = depth == 15 ? 16 : depth;
119 dispDeviceRect[0] = dispDeviceRect[1] = 0;
120 dispDeviceRect[2] = width;
121 dispDeviceRect[3] = height;
122 boot_text_mapped = 1;
123 }
124
btext_unmap(void)125 void __init btext_unmap(void)
126 {
127 boot_text_mapped = 0;
128 }
129
130 /* Here's a small text engine to use during early boot
131 * or for debugging purposes
132 *
133 * todo:
134 *
135 * - build some kind of vgacon with it to enable early printk
136 * - move to a separate file
137 * - add a few video driver hooks to keep in sync with display
138 * changes.
139 */
140
btext_map(void)141 void btext_map(void)
142 {
143 unsigned long base, offset, size;
144 unsigned char *vbase;
145
146 /* By default, we are no longer mapped */
147 boot_text_mapped = 0;
148 if (!dispDeviceBase)
149 return;
150 base = ((unsigned long) dispDeviceBase) & 0xFFFFF000UL;
151 offset = ((unsigned long) dispDeviceBase) - base;
152 size = dispDeviceRowBytes * dispDeviceRect[3] + offset
153 + dispDeviceRect[0];
154 vbase = ioremap_wc(base, size);
155 if (!vbase)
156 return;
157 logicalDisplayBase = vbase + offset;
158 boot_text_mapped = 1;
159 }
160
btext_initialize(struct device_node * np)161 static int __init btext_initialize(struct device_node *np)
162 {
163 unsigned int width, height, depth, pitch;
164 unsigned long address = 0;
165 const u32 *prop;
166
167 prop = of_get_property(np, "linux,bootx-width", NULL);
168 if (prop == NULL)
169 prop = of_get_property(np, "width", NULL);
170 if (prop == NULL)
171 return -EINVAL;
172 width = *prop;
173 prop = of_get_property(np, "linux,bootx-height", NULL);
174 if (prop == NULL)
175 prop = of_get_property(np, "height", NULL);
176 if (prop == NULL)
177 return -EINVAL;
178 height = *prop;
179 prop = of_get_property(np, "linux,bootx-depth", NULL);
180 if (prop == NULL)
181 prop = of_get_property(np, "depth", NULL);
182 if (prop == NULL)
183 return -EINVAL;
184 depth = *prop;
185 pitch = width * ((depth + 7) / 8);
186 prop = of_get_property(np, "linux,bootx-linebytes", NULL);
187 if (prop == NULL)
188 prop = of_get_property(np, "linebytes", NULL);
189 if (prop && *prop != 0xffffffffu)
190 pitch = *prop;
191 if (pitch == 1)
192 pitch = 0x1000;
193 prop = of_get_property(np, "linux,bootx-addr", NULL);
194 if (prop == NULL)
195 prop = of_get_property(np, "address", NULL);
196 if (prop)
197 address = *prop;
198
199 /* FIXME: Add support for PCI reg properties. Right now, only
200 * reliable on macs
201 */
202 if (address == 0)
203 return -EINVAL;
204
205 g_loc_X = 0;
206 g_loc_Y = 0;
207 g_max_loc_X = width / 8;
208 g_max_loc_Y = height / 16;
209 dispDeviceBase = (unsigned char *)address;
210 dispDeviceRowBytes = pitch;
211 dispDeviceDepth = depth == 15 ? 16 : depth;
212 dispDeviceRect[0] = dispDeviceRect[1] = 0;
213 dispDeviceRect[2] = width;
214 dispDeviceRect[3] = height;
215
216 btext_map();
217
218 return 0;
219 }
220
btext_find_display(int allow_nonstdout)221 int __init btext_find_display(int allow_nonstdout)
222 {
223 struct device_node *np = of_stdout;
224 int rc = -ENODEV;
225
226 if (!of_node_is_type(np, "display")) {
227 printk("boot stdout isn't a display !\n");
228 np = NULL;
229 }
230 if (np)
231 rc = btext_initialize(np);
232 if (rc == 0 || !allow_nonstdout)
233 return rc;
234
235 for_each_node_by_type(np, "display") {
236 if (of_property_read_bool(np, "linux,opened")) {
237 printk("trying %pOF ...\n", np);
238 rc = btext_initialize(np);
239 printk("result: %d\n", rc);
240 }
241 if (rc == 0) {
242 of_node_put(np);
243 break;
244 }
245 }
246 return rc;
247 }
248
249 /* Calc the base address of a given point (x,y) */
calc_base(int x,int y)250 static unsigned char * calc_base(int x, int y)
251 {
252 unsigned char *base;
253
254 base = logicalDisplayBase;
255 if (!base)
256 base = dispDeviceBase;
257 base += (x + dispDeviceRect[0]) * (dispDeviceDepth >> 3);
258 base += (y + dispDeviceRect[1]) * dispDeviceRowBytes;
259 return base;
260 }
261
262 /* Adjust the display to a new resolution */
btext_update_display(unsigned long phys,int width,int height,int depth,int pitch)263 void btext_update_display(unsigned long phys, int width, int height,
264 int depth, int pitch)
265 {
266 if (!dispDeviceBase)
267 return;
268
269 /* check it's the same frame buffer (within 256MB) */
270 if ((phys ^ (unsigned long)dispDeviceBase) & 0xf0000000)
271 return;
272
273 dispDeviceBase = (__u8 *) phys;
274 dispDeviceRect[0] = 0;
275 dispDeviceRect[1] = 0;
276 dispDeviceRect[2] = width;
277 dispDeviceRect[3] = height;
278 dispDeviceDepth = depth;
279 dispDeviceRowBytes = pitch;
280 if (boot_text_mapped) {
281 iounmap(logicalDisplayBase);
282 boot_text_mapped = 0;
283 }
284 btext_map();
285 g_loc_X = 0;
286 g_loc_Y = 0;
287 g_max_loc_X = width / 8;
288 g_max_loc_Y = height / 16;
289 }
290 EXPORT_SYMBOL(btext_update_display);
291
btext_clearscreen(void)292 void __init btext_clearscreen(void)
293 {
294 unsigned int *base = (unsigned int *)calc_base(0, 0);
295 unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) *
296 (dispDeviceDepth >> 3)) >> 2;
297 int i,j;
298
299 rmci_maybe_on();
300 for (i=0; i<(dispDeviceRect[3] - dispDeviceRect[1]); i++)
301 {
302 unsigned int *ptr = base;
303 for(j=width; j; --j)
304 *(ptr++) = 0;
305 base += (dispDeviceRowBytes >> 2);
306 }
307 rmci_maybe_off();
308 }
309
btext_flushscreen(void)310 void __init btext_flushscreen(void)
311 {
312 unsigned int *base = (unsigned int *)calc_base(0, 0);
313 unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) *
314 (dispDeviceDepth >> 3)) >> 2;
315 int i,j;
316
317 for (i=0; i < (dispDeviceRect[3] - dispDeviceRect[1]); i++)
318 {
319 unsigned int *ptr = base;
320 for(j = width; j > 0; j -= 8) {
321 __asm__ __volatile__ ("dcbst 0,%0" :: "r" (ptr));
322 ptr += 8;
323 }
324 base += (dispDeviceRowBytes >> 2);
325 }
326 __asm__ __volatile__ ("sync" ::: "memory");
327 }
328
btext_flushline(void)329 void __init btext_flushline(void)
330 {
331 unsigned int *base = (unsigned int *)calc_base(0, g_loc_Y << 4);
332 unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) *
333 (dispDeviceDepth >> 3)) >> 2;
334 int i,j;
335
336 for (i=0; i < 16; i++)
337 {
338 unsigned int *ptr = base;
339 for(j = width; j > 0; j -= 8) {
340 __asm__ __volatile__ ("dcbst 0,%0" :: "r" (ptr));
341 ptr += 8;
342 }
343 base += (dispDeviceRowBytes >> 2);
344 }
345 __asm__ __volatile__ ("sync" ::: "memory");
346 }
347
348
349 #ifndef NO_SCROLL
scrollscreen(void)350 static void scrollscreen(void)
351 {
352 unsigned int *src = (unsigned int *)calc_base(0,16);
353 unsigned int *dst = (unsigned int *)calc_base(0,0);
354 unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) *
355 (dispDeviceDepth >> 3)) >> 2;
356 int i,j;
357
358 rmci_maybe_on();
359
360 for (i=0; i<(dispDeviceRect[3] - dispDeviceRect[1] - 16); i++)
361 {
362 unsigned int *src_ptr = src;
363 unsigned int *dst_ptr = dst;
364 for(j=width; j; --j)
365 *(dst_ptr++) = *(src_ptr++);
366 src += (dispDeviceRowBytes >> 2);
367 dst += (dispDeviceRowBytes >> 2);
368 }
369 for (i=0; i<16; i++)
370 {
371 unsigned int *dst_ptr = dst;
372 for(j=width; j; --j)
373 *(dst_ptr++) = 0;
374 dst += (dispDeviceRowBytes >> 2);
375 }
376
377 rmci_maybe_off();
378 }
379 #endif /* ndef NO_SCROLL */
380
381 static unsigned int expand_bits_8[16] = {
382 0x00000000,
383 0x000000ff,
384 0x0000ff00,
385 0x0000ffff,
386 0x00ff0000,
387 0x00ff00ff,
388 0x00ffff00,
389 0x00ffffff,
390 0xff000000,
391 0xff0000ff,
392 0xff00ff00,
393 0xff00ffff,
394 0xffff0000,
395 0xffff00ff,
396 0xffffff00,
397 0xffffffff
398 };
399
400 static unsigned int expand_bits_16[4] = {
401 0x00000000,
402 0x0000ffff,
403 0xffff0000,
404 0xffffffff
405 };
406
407
draw_byte_32(const unsigned char * font,unsigned int * base,int rb)408 static void draw_byte_32(const unsigned char *font, unsigned int *base, int rb)
409 {
410 int l, bits;
411 int fg = 0xFFFFFFFFUL;
412 int bg = 0x00000000UL;
413
414 for (l = 0; l < 16; ++l)
415 {
416 bits = *font++;
417 base[0] = (-(bits >> 7) & fg) ^ bg;
418 base[1] = (-((bits >> 6) & 1) & fg) ^ bg;
419 base[2] = (-((bits >> 5) & 1) & fg) ^ bg;
420 base[3] = (-((bits >> 4) & 1) & fg) ^ bg;
421 base[4] = (-((bits >> 3) & 1) & fg) ^ bg;
422 base[5] = (-((bits >> 2) & 1) & fg) ^ bg;
423 base[6] = (-((bits >> 1) & 1) & fg) ^ bg;
424 base[7] = (-(bits & 1) & fg) ^ bg;
425 base = (unsigned int *) ((char *)base + rb);
426 }
427 }
428
draw_byte_16(const unsigned char * font,unsigned int * base,int rb)429 static inline void draw_byte_16(const unsigned char *font, unsigned int *base, int rb)
430 {
431 int l, bits;
432 int fg = 0xFFFFFFFFUL;
433 int bg = 0x00000000UL;
434 unsigned int *eb = (int *)expand_bits_16;
435
436 for (l = 0; l < 16; ++l)
437 {
438 bits = *font++;
439 base[0] = (eb[bits >> 6] & fg) ^ bg;
440 base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg;
441 base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg;
442 base[3] = (eb[bits & 3] & fg) ^ bg;
443 base = (unsigned int *) ((char *)base + rb);
444 }
445 }
446
draw_byte_8(const unsigned char * font,unsigned int * base,int rb)447 static inline void draw_byte_8(const unsigned char *font, unsigned int *base, int rb)
448 {
449 int l, bits;
450 int fg = 0x0F0F0F0FUL;
451 int bg = 0x00000000UL;
452 unsigned int *eb = (int *)expand_bits_8;
453
454 for (l = 0; l < 16; ++l)
455 {
456 bits = *font++;
457 base[0] = (eb[bits >> 4] & fg) ^ bg;
458 base[1] = (eb[bits & 0xf] & fg) ^ bg;
459 base = (unsigned int *) ((char *)base + rb);
460 }
461 }
462
draw_byte(unsigned char c,long locX,long locY)463 static noinline void draw_byte(unsigned char c, long locX, long locY)
464 {
465 unsigned char *base = calc_base(locX << 3, locY << 4);
466 unsigned int font_index = c * 16;
467 const unsigned char *font = PTRRELOC(font_sun_8x16.data) + font_index;
468 int rb = dispDeviceRowBytes;
469
470 rmci_maybe_on();
471 switch(dispDeviceDepth) {
472 case 24:
473 case 32:
474 draw_byte_32(font, (unsigned int *)base, rb);
475 break;
476 case 15:
477 case 16:
478 draw_byte_16(font, (unsigned int *)base, rb);
479 break;
480 case 8:
481 draw_byte_8(font, (unsigned int *)base, rb);
482 break;
483 }
484 rmci_maybe_off();
485 }
486
btext_drawchar(char c)487 void btext_drawchar(char c)
488 {
489 int cline = 0;
490 #ifdef NO_SCROLL
491 int x;
492 #endif
493 if (!boot_text_mapped)
494 return;
495
496 switch (c) {
497 case '\b':
498 if (g_loc_X > 0)
499 --g_loc_X;
500 break;
501 case '\t':
502 g_loc_X = (g_loc_X & -8) + 8;
503 break;
504 case '\r':
505 g_loc_X = 0;
506 break;
507 case '\n':
508 g_loc_X = 0;
509 g_loc_Y++;
510 cline = 1;
511 break;
512 default:
513 draw_byte(c, g_loc_X++, g_loc_Y);
514 }
515 if (g_loc_X >= g_max_loc_X) {
516 g_loc_X = 0;
517 g_loc_Y++;
518 cline = 1;
519 }
520 #ifndef NO_SCROLL
521 while (g_loc_Y >= g_max_loc_Y) {
522 scrollscreen();
523 g_loc_Y--;
524 }
525 #else
526 /* wrap around from bottom to top of screen so we don't
527 waste time scrolling each line. -- paulus. */
528 if (g_loc_Y >= g_max_loc_Y)
529 g_loc_Y = 0;
530 if (cline) {
531 for (x = 0; x < g_max_loc_X; ++x)
532 draw_byte(' ', x, g_loc_Y);
533 }
534 #endif
535 }
536
btext_drawstring(const char * c)537 void btext_drawstring(const char *c)
538 {
539 if (!boot_text_mapped)
540 return;
541 while (*c)
542 btext_drawchar(*c++);
543 }
544
btext_drawtext(const char * c,unsigned int len)545 void __init btext_drawtext(const char *c, unsigned int len)
546 {
547 if (!boot_text_mapped)
548 return;
549 while (len--)
550 btext_drawchar(*c++);
551 }
552
btext_drawhex(unsigned long v)553 void __init btext_drawhex(unsigned long v)
554 {
555 if (!boot_text_mapped)
556 return;
557 #ifdef CONFIG_PPC64
558 btext_drawchar(hex_asc_hi(v >> 56));
559 btext_drawchar(hex_asc_lo(v >> 56));
560 btext_drawchar(hex_asc_hi(v >> 48));
561 btext_drawchar(hex_asc_lo(v >> 48));
562 btext_drawchar(hex_asc_hi(v >> 40));
563 btext_drawchar(hex_asc_lo(v >> 40));
564 btext_drawchar(hex_asc_hi(v >> 32));
565 btext_drawchar(hex_asc_lo(v >> 32));
566 #endif
567 btext_drawchar(hex_asc_hi(v >> 24));
568 btext_drawchar(hex_asc_lo(v >> 24));
569 btext_drawchar(hex_asc_hi(v >> 16));
570 btext_drawchar(hex_asc_lo(v >> 16));
571 btext_drawchar(hex_asc_hi(v >> 8));
572 btext_drawchar(hex_asc_lo(v >> 8));
573 btext_drawchar(hex_asc_hi(v));
574 btext_drawchar(hex_asc_lo(v));
575 btext_drawchar(' ');
576 }
577
udbg_init_btext(void)578 void __init udbg_init_btext(void)
579 {
580 /* If btext is enabled, we might have a BAT setup for early display,
581 * thus we do enable some very basic udbg output
582 */
583 udbg_putc = btext_drawchar;
584 }
585