Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/kernel/btext.c
49172 views
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 <[email protected]>
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
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
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
*/
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
*/
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
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
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
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
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) */
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 */
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
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
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
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
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
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
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
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
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
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
537
void btext_drawstring(const char *c)
538
{
539
if (!boot_text_mapped)
540
return;
541
while (*c)
542
btext_drawchar(*c++);
543
}
544
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
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
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
586