| 1 | |
| 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/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 | |
| 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 |
|
| 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
|
| 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 | |
| 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 | }
|