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/console.h>
11#include <linux/font.h>
12
13#include <asm/btext.h>
14#include <asm/oplib.h>
15#include <asm/io.h>
16
17#define NO_SCROLL
18
19#ifndef NO_SCROLL
20static void scrollscreen(void);
21#endif
22
23static void draw_byte(unsigned char c, long locX, long locY);
24static void draw_byte_32(const unsigned char *bits, unsigned int *base, int rb);
25static void draw_byte_16(const unsigned char *bits, unsigned int *base, int rb);
26static void draw_byte_8(const unsigned char *bits, unsigned int *base, int rb);
27
28#define __force_data __section(".data")
29
30static int g_loc_X __force_data;
31static int g_loc_Y __force_data;
32static int g_max_loc_X __force_data;
33static int g_max_loc_Y __force_data;
34
35static int dispDeviceRowBytes __force_data;
36static int dispDeviceDepth  __force_data;
37static int dispDeviceRect[4] __force_data;
38static unsigned char *dispDeviceBase __force_data;
39
40static int __init btext_initialize(phandle node)
41{
42	unsigned int width, height, depth, pitch;
43	unsigned long address = 0;
44	u32 prop;
45
46	if (prom_getproperty(node, "width", (char *)&width, 4) < 0)
47		return -EINVAL;
48	if (prom_getproperty(node, "height", (char *)&height, 4) < 0)
49		return -EINVAL;
50	if (prom_getproperty(node, "depth", (char *)&depth, 4) < 0)
51		return -EINVAL;
52	pitch = width * ((depth + 7) / 8);
53
54	if (prom_getproperty(node, "linebytes", (char *)&prop, 4) >= 0 &&
55	    prop != 0xffffffffu)
56		pitch = prop;
57
58	if (pitch == 1)
59		pitch = 0x1000;
60
61	if (prom_getproperty(node, "address", (char *)&prop, 4) >= 0)
62		address = prop;
63
64	/* FIXME: Add support for PCI reg properties. Right now, only
65	 * reliable on macs
66	 */
67	if (address == 0)
68		return -EINVAL;
69
70	g_loc_X = 0;
71	g_loc_Y = 0;
72	g_max_loc_X = width / 8;
73	g_max_loc_Y = height / 16;
74	dispDeviceBase = (unsigned char *)address;
75	dispDeviceRowBytes = pitch;
76	dispDeviceDepth = depth == 15 ? 16 : depth;
77	dispDeviceRect[0] = dispDeviceRect[1] = 0;
78	dispDeviceRect[2] = width;
79	dispDeviceRect[3] = height;
80
81	return 0;
82}
83
84/* Calc the base address of a given point (x,y) */
85static unsigned char * calc_base(int x, int y)
86{
87	unsigned char *base = dispDeviceBase;
88
89	base += (x + dispDeviceRect[0]) * (dispDeviceDepth >> 3);
90	base += (y + dispDeviceRect[1]) * dispDeviceRowBytes;
91	return base;
92}
93
94static void btext_clearscreen(void)
95{
96	unsigned int *base	= (unsigned int *)calc_base(0, 0);
97	unsigned long width 	= ((dispDeviceRect[2] - dispDeviceRect[0]) *
98					(dispDeviceDepth >> 3)) >> 2;
99	int i,j;
100
101	for (i=0; i<(dispDeviceRect[3] - dispDeviceRect[1]); i++)
102	{
103		unsigned int *ptr = base;
104		for(j=width; j; --j)
105			*(ptr++) = 0;
106		base += (dispDeviceRowBytes >> 2);
107	}
108}
109
110#ifndef NO_SCROLL
111static void scrollscreen(void)
112{
113	unsigned int *src     	= (unsigned int *)calc_base(0,16);
114	unsigned int *dst     	= (unsigned int *)calc_base(0,0);
115	unsigned long width    	= ((dispDeviceRect[2] - dispDeviceRect[0]) *
116				   (dispDeviceDepth >> 3)) >> 2;
117	int i,j;
118
119	for (i=0; i<(dispDeviceRect[3] - dispDeviceRect[1] - 16); i++)
120	{
121		unsigned int *src_ptr = src;
122		unsigned int *dst_ptr = dst;
123		for(j=width; j; --j)
124			*(dst_ptr++) = *(src_ptr++);
125		src += (dispDeviceRowBytes >> 2);
126		dst += (dispDeviceRowBytes >> 2);
127	}
128	for (i=0; i<16; i++)
129	{
130		unsigned int *dst_ptr = dst;
131		for(j=width; j; --j)
132			*(dst_ptr++) = 0;
133		dst += (dispDeviceRowBytes >> 2);
134	}
135}
136#endif /* ndef NO_SCROLL */
137
138static void btext_drawchar(char c)
139{
140	int cline = 0;
141#ifdef NO_SCROLL
142	int x;
143#endif
144	switch (c) {
145	case '\b':
146		if (g_loc_X > 0)
147			--g_loc_X;
148		break;
149	case '\t':
150		g_loc_X = (g_loc_X & -8) + 8;
151		break;
152	case '\r':
153		g_loc_X = 0;
154		break;
155	case '\n':
156		g_loc_X = 0;
157		g_loc_Y++;
158		cline = 1;
159		break;
160	default:
161		draw_byte(c, g_loc_X++, g_loc_Y);
162	}
163	if (g_loc_X >= g_max_loc_X) {
164		g_loc_X = 0;
165		g_loc_Y++;
166		cline = 1;
167	}
168#ifndef NO_SCROLL
169	while (g_loc_Y >= g_max_loc_Y) {
170		scrollscreen();
171		g_loc_Y--;
172	}
173#else
174	/* wrap around from bottom to top of screen so we don't
175	   waste time scrolling each line.  -- paulus. */
176	if (g_loc_Y >= g_max_loc_Y)
177		g_loc_Y = 0;
178	if (cline) {
179		for (x = 0; x < g_max_loc_X; ++x)
180			draw_byte(' ', x, g_loc_Y);
181	}
182#endif
183}
184
185static void btext_drawtext(const char *c, unsigned int len)
186{
187	while (len--)
188		btext_drawchar(*c++);
189}
190
191static void draw_byte(unsigned char c, long locX, long locY)
192{
193	unsigned char *base	= calc_base(locX << 3, locY << 4);
194	unsigned int font_index = c * 16;
195	const unsigned char *font	= font_sun_8x16.data + font_index;
196	int rb			= dispDeviceRowBytes;
197
198	switch(dispDeviceDepth) {
199	case 24:
200	case 32:
201		draw_byte_32(font, (unsigned int *)base, rb);
202		break;
203	case 15:
204	case 16:
205		draw_byte_16(font, (unsigned int *)base, rb);
206		break;
207	case 8:
208		draw_byte_8(font, (unsigned int *)base, rb);
209		break;
210	}
211}
212
213static unsigned int expand_bits_8[16] = {
214	0x00000000,
215	0x000000ff,
216	0x0000ff00,
217	0x0000ffff,
218	0x00ff0000,
219	0x00ff00ff,
220	0x00ffff00,
221	0x00ffffff,
222	0xff000000,
223	0xff0000ff,
224	0xff00ff00,
225	0xff00ffff,
226	0xffff0000,
227	0xffff00ff,
228	0xffffff00,
229	0xffffffff
230};
231
232static unsigned int expand_bits_16[4] = {
233	0x00000000,
234	0x0000ffff,
235	0xffff0000,
236	0xffffffff
237};
238
239
240static void draw_byte_32(const unsigned char *font, unsigned int *base, int rb)
241{
242	int l, bits;
243	int fg = 0xFFFFFFFFUL;
244	int bg = 0x00000000UL;
245
246	for (l = 0; l < 16; ++l)
247	{
248		bits = *font++;
249		base[0] = (-(bits >> 7) & fg) ^ bg;
250		base[1] = (-((bits >> 6) & 1) & fg) ^ bg;
251		base[2] = (-((bits >> 5) & 1) & fg) ^ bg;
252		base[3] = (-((bits >> 4) & 1) & fg) ^ bg;
253		base[4] = (-((bits >> 3) & 1) & fg) ^ bg;
254		base[5] = (-((bits >> 2) & 1) & fg) ^ bg;
255		base[6] = (-((bits >> 1) & 1) & fg) ^ bg;
256		base[7] = (-(bits & 1) & fg) ^ bg;
257		base = (unsigned int *) ((char *)base + rb);
258	}
259}
260
261static void draw_byte_16(const unsigned char *font, unsigned int *base, int rb)
262{
263	int l, bits;
264	int fg = 0xFFFFFFFFUL;
265	int bg = 0x00000000UL;
266	unsigned int *eb = (int *)expand_bits_16;
267
268	for (l = 0; l < 16; ++l)
269	{
270		bits = *font++;
271		base[0] = (eb[bits >> 6] & fg) ^ bg;
272		base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg;
273		base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg;
274		base[3] = (eb[bits & 3] & fg) ^ bg;
275		base = (unsigned int *) ((char *)base + rb);
276	}
277}
278
279static void draw_byte_8(const unsigned char *font, unsigned int *base, int rb)
280{
281	int l, bits;
282	int fg = 0x0F0F0F0FUL;
283	int bg = 0x00000000UL;
284	unsigned int *eb = (int *)expand_bits_8;
285
286	for (l = 0; l < 16; ++l)
287	{
288		bits = *font++;
289		base[0] = (eb[bits >> 4] & fg) ^ bg;
290		base[1] = (eb[bits & 0xf] & fg) ^ bg;
291		base = (unsigned int *) ((char *)base + rb);
292	}
293}
294
295static void btext_console_write(struct console *con, const char *s,
296				unsigned int n)
297{
298	btext_drawtext(s, n);
299}
300
301static struct console btext_console = {
302	.name	= "btext",
303	.write	= btext_console_write,
304	.flags	= CON_PRINTBUFFER | CON_ENABLED | CON_BOOT | CON_ANYTIME,
305	.index	= 0,
306};
307
308int __init btext_find_display(void)
309{
310	phandle node;
311	char type[32];
312	int ret;
313
314	node = prom_inst2pkg(prom_stdout);
315	if (prom_getproperty(node, "device_type", type, 32) < 0)
316		return -ENODEV;
317	if (strcmp(type, "display"))
318		return -ENODEV;
319
320	ret = btext_initialize(node);
321	if (!ret) {
322		btext_clearscreen();
323		register_console(&btext_console);
324	}
325	return ret;
326}
327