xref: /linux/arch/sparc/kernel/btext.c (revision a06c3fad49a50d5d5eb078f93e70f4d3eca5d5a5)
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
20 static void scrollscreen(void);
21 #endif
22 
23 static void draw_byte(unsigned char c, long locX, long locY);
24 static void draw_byte_32(const unsigned char *bits, unsigned int *base, int rb);
25 static void draw_byte_16(const unsigned char *bits, unsigned int *base, int rb);
26 static void draw_byte_8(const unsigned char *bits, unsigned int *base, int rb);
27 
28 #define __force_data __section(".data")
29 
30 static int g_loc_X __force_data;
31 static int g_loc_Y __force_data;
32 static int g_max_loc_X __force_data;
33 static int g_max_loc_Y __force_data;
34 
35 static int dispDeviceRowBytes __force_data;
36 static int dispDeviceDepth  __force_data;
37 static int dispDeviceRect[4] __force_data;
38 static unsigned char *dispDeviceBase __force_data;
39 
40 static 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) */
85 static 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 
94 static 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
111 static 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 
138 static 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 
185 static void btext_drawtext(const char *c, unsigned int len)
186 {
187 	while (len--)
188 		btext_drawchar(*c++);
189 }
190 
191 static 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 
213 static 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 
232 static unsigned int expand_bits_16[4] = {
233 	0x00000000,
234 	0x0000ffff,
235 	0xffff0000,
236 	0xffffffff
237 };
238 
239 
240 static 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 
261 static 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 
279 static 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 
295 static void btext_console_write(struct console *con, const char *s,
296 				unsigned int n)
297 {
298 	btext_drawtext(s, n);
299 }
300 
301 static 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 
308 int __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