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