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(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(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
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, "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) */
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(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(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
309void __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
328void __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
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(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
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, 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
536void btext_drawstring(const char *c)
537{
538	if (!boot_text_mapped)
539		return;
540	while (*c)
541		btext_drawchar(*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++);
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(' ');
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