xref: /linux/arch/powerpc/kernel/btext.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
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
27 static void scrollscreen(void);
28 #endif
29 
30 #define __force_data __section(".data")
31 
32 static int g_loc_X __force_data;
33 static int g_loc_Y __force_data;
34 static int g_max_loc_X __force_data;
35 static int g_max_loc_Y __force_data;
36 
37 static int dispDeviceRowBytes __force_data;
38 static int dispDeviceDepth  __force_data;
39 static int dispDeviceRect[4] __force_data;
40 static unsigned char *dispDeviceBase __force_data;
41 static unsigned char *logicalDisplayBase __force_data;
42 
43 unsigned long disp_BAT[2] __initdata = {0, 0};
44 
45 static int boot_text_mapped __force_data;
46 
47 extern void rmci_on(void);
48 extern void rmci_off(void);
49 
rmci_maybe_on(void)50 static 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 
rmci_maybe_off(void)58 static 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  */
btext_prepare_BAT(void)83 void __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  */
btext_setup_display(int width,int height,int depth,int pitch,unsigned long address)107 void __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 
btext_unmap(void)124 void __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 
btext_map(void)140 void 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(base, size);
154 	if (!vbase)
155 		return;
156 	logicalDisplayBase = vbase + offset;
157 	boot_text_mapped = 1;
158 }
159 
btext_initialize(struct device_node * np)160 static 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(np, "linux,bootx-width", NULL);
167 	if (prop == NULL)
168 		prop = of_get_property(np, "width", NULL);
169 	if (prop == NULL)
170 		return -EINVAL;
171 	width = *prop;
172 	prop = of_get_property(np, "linux,bootx-height", NULL);
173 	if (prop == NULL)
174 		prop = of_get_property(np, "height", NULL);
175 	if (prop == NULL)
176 		return -EINVAL;
177 	height = *prop;
178 	prop = of_get_property(np, "linux,bootx-depth", NULL);
179 	if (prop == NULL)
180 		prop = of_get_property(np, "depth", NULL);
181 	if (prop == NULL)
182 		return -EINVAL;
183 	depth = *prop;
184 	pitch = width * ((depth + 7) / 8);
185 	prop = of_get_property(np, "linux,bootx-linebytes", NULL);
186 	if (prop == NULL)
187 		prop = of_get_property(np, "linebytes", NULL);
188 	if (prop && *prop != 0xffffffffu)
189 		pitch = *prop;
190 	if (pitch == 1)
191 		pitch = 0x1000;
192 	prop = of_get_property(np, "linux,bootx-addr", NULL);
193 	if (prop == NULL)
194 		prop = of_get_property(np, "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 
btext_find_display(int allow_nonstdout)220 int __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, "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, "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(np);
242 			break;
243 		}
244 	}
245 	return rc;
246 }
247 
248 /* Calc the base address of a given point (x,y) */
calc_base(int x,int y)249 static 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 */
btext_update_display(unsigned long phys,int width,int height,int depth,int pitch)262 void 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(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 }
289 EXPORT_SYMBOL(btext_update_display);
290 
btext_clearscreen(void)291 void __init btext_clearscreen(void)
292 {
293 	unsigned int *base	= (unsigned int *)calc_base(0, 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 
btext_flushscreen(void)309 void __init btext_flushscreen(void)
310 {
311 	unsigned int *base	= (unsigned int *)calc_base(0, 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 
btext_flushline(void)328 void __init btext_flushline(void)
329 {
330 	unsigned int *base	= (unsigned int *)calc_base(0, 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
scrollscreen(void)349 static 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 
380 static 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 
399 static unsigned int expand_bits_16[4] = {
400 	0x00000000,
401 	0x0000ffff,
402 	0xffff0000,
403 	0xffffffff
404 };
405 
406 
draw_byte_32(const unsigned char * font,unsigned int * base,int rb)407 static 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 
draw_byte_16(const unsigned char * font,unsigned int * base,int rb)428 static 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 
draw_byte_8(const unsigned char * font,unsigned int * base,int rb)446 static 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 
draw_byte(unsigned char c,long locX,long locY)462 static noinline void draw_byte(unsigned char c, long locX, long locY)
463 {
464 	unsigned char *base	= calc_base(locX << 3, 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, (unsigned int *)base, rb);
474 		break;
475 	case 15:
476 	case 16:
477 		draw_byte_16(font, (unsigned int *)base, rb);
478 		break;
479 	case 8:
480 		draw_byte_8(font, (unsigned int *)base, rb);
481 		break;
482 	}
483 	rmci_maybe_off();
484 }
485 
btext_drawchar(char c)486 void 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, g_loc_X++, 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(' ', x, g_loc_Y);
532 	}
533 #endif
534 }
535 
btext_drawstring(const char * c)536 void btext_drawstring(const char *c)
537 {
538 	if (!boot_text_mapped)
539 		return;
540 	while (*c)
541 		btext_drawchar(*c++);
542 }
543 
btext_drawtext(const char * c,unsigned int len)544 void __init btext_drawtext(const char *c, unsigned int len)
545 {
546 	if (!boot_text_mapped)
547 		return;
548 	while (len--)
549 		btext_drawchar(*c++);
550 }
551 
btext_drawhex(unsigned long v)552 void __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(' ');
575 }
576 
udbg_init_btext(void)577 void __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