xref: /linux/arch/powerpc/kernel/btext.c (revision cd80afff4877e1151ce53d48b65eba9de80ac1d2)
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