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 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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.