Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/kernel/btext.c
26424 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
24
#define NO_SCROLL
25
26
#ifndef NO_SCROLL
27
static void scrollscreen(void);
28
#endif
29
30
#define __force_data __section(".data")
31
32
static int g_loc_X __force_data;
33
static int g_loc_Y __force_data;
34
static int g_max_loc_X __force_data;
35
static int g_max_loc_Y __force_data;
36
37
static int dispDeviceRowBytes __force_data;
38
static int dispDeviceDepth __force_data;
39
static int dispDeviceRect[4] __force_data;
40
static unsigned char *dispDeviceBase __force_data;
41
static unsigned char *logicalDisplayBase __force_data;
42
43
unsigned long disp_BAT[2] __initdata = {0, 0};
44
45
static int boot_text_mapped __force_data;
46
47
extern void rmci_on(void);
48
extern void rmci_off(void);
49
50
static 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
58
static 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
*/
83
void __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
*/
107
void __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
124
void __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
140
void 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
160
static 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
220
int __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) */
249
static 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 */
262
void 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
}
289
EXPORT_SYMBOL(btext_update_display);
290
291
void __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
309
void __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
328
void __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
349
static 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
380
static 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
399
static unsigned int expand_bits_16[4] = {
400
0x00000000,
401
0x0000ffff,
402
0xffff0000,
403
0xffffffff
404
};
405
406
407
static 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
428
static 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
446
static 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
462
static 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
486
void 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
536
void btext_drawstring(const char *c)
537
{
538
if (!boot_text_mapped)
539
return;
540
while (*c)
541
btext_drawchar(*c++);
542
}
543
544
void __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
552
void __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
577
void __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
585