File Index
Files and directories
Sign in
FILE

btext.c

File preview with syntax highlighting.

linux-6.19.14/arch/powerpc/kernel/btext.c C
  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
 28static void scrollscreen(void);
 29#endif
 30
 31#define __force_data __section(".data")
 32
 33static int g_loc_X __force_data;
 34static int g_loc_Y __force_data;
 35static int g_max_loc_X __force_data;
 36static int g_max_loc_Y __force_data;
 37
 38static int dispDeviceRowBytes __force_data;
 39static int dispDeviceDepth  __force_data;
 40static int dispDeviceRect[4] __force_data;
 41static unsigned char *dispDeviceBase __force_data;
 42static unsigned char *logicalDisplayBase __force_data;
 43
 44unsigned long disp_BAT[2] __initdata = {0, 0};
 45
 46static int boot_text_mapped __force_data;
 47
 48extern void rmci_on(void);
 49extern void rmci_off(void);
 50
 51static 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
 59static 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 */
 84void __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 */
108void __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
125void __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
141void 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
161static 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
221int __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) */
250static 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 */
263void 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}
290EXPORT_SYMBOL(btext_update_display);
291
292void __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
310void __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
329void __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
350static 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
381static 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
400static unsigned int expand_bits_16[4] = {
401	0x00000000,
402	0x0000ffff,
403	0xffff0000,
404	0xffffffff
405};
406
407
408static 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
429static 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
447static 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
463static 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
487void 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
537void btext_drawstring(const char *c)
538{
539	if (!boot_text_mapped)
540		return;
541	while (*c)
542		btext_drawchar(*c++);
543}
544
545void __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
553void __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
578void __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}
~/ Read-only file preview.