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

source code of linux/arch/powerpc/kernel/btext.c