Merge remote-tracking branches 'asoc/fix/adsp', 'asoc/fix/arizona', 'asoc/fix/atmel...
[linux-drm-fsl-dcu.git] / arch / x86 / platform / efi / early_printk.c
1 /*
2  * Copyright (C) 2013 Intel Corporation; author Matt Fleming
3  *
4  *  This file is part of the Linux kernel, and is made available under
5  *  the terms of the GNU General Public License version 2.
6  */
7
8 #include <linux/console.h>
9 #include <linux/efi.h>
10 #include <linux/font.h>
11 #include <linux/io.h>
12 #include <linux/kernel.h>
13 #include <asm/setup.h>
14
15 static const struct font_desc *font;
16 static u32 efi_x, efi_y;
17
18 static __init void early_efi_clear_scanline(unsigned int y)
19 {
20         unsigned long base, *dst;
21         u16 len;
22
23         base = boot_params.screen_info.lfb_base;
24         len = boot_params.screen_info.lfb_linelength;
25
26         dst = early_ioremap(base + y*len, len);
27         if (!dst)
28                 return;
29
30         memset(dst, 0, len);
31         early_iounmap(dst, len);
32 }
33
34 static __init void early_efi_scroll_up(void)
35 {
36         unsigned long base, *dst, *src;
37         u16 len;
38         u32 i, height;
39
40         base = boot_params.screen_info.lfb_base;
41         len = boot_params.screen_info.lfb_linelength;
42         height = boot_params.screen_info.lfb_height;
43
44         for (i = 0; i < height - font->height; i++) {
45                 dst = early_ioremap(base + i*len, len);
46                 if (!dst)
47                         return;
48
49                 src = early_ioremap(base + (i + font->height) * len, len);
50                 if (!src) {
51                         early_iounmap(dst, len);
52                         return;
53                 }
54
55                 memmove(dst, src, len);
56
57                 early_iounmap(src, len);
58                 early_iounmap(dst, len);
59         }
60 }
61
62 static void early_efi_write_char(u32 *dst, unsigned char c, unsigned int h)
63 {
64         const u32 color_black = 0x00000000;
65         const u32 color_white = 0x00ffffff;
66         const u8 *src;
67         u8 s8;
68         int m;
69
70         src = font->data + c * font->height;
71         s8 = *(src + h);
72
73         for (m = 0; m < 8; m++) {
74                 if ((s8 >> (7 - m)) & 1)
75                         *dst = color_white;
76                 else
77                         *dst = color_black;
78                 dst++;
79         }
80 }
81
82 static __init void
83 early_efi_write(struct console *con, const char *str, unsigned int num)
84 {
85         struct screen_info *si;
86         unsigned long base;
87         unsigned int len;
88         const char *s;
89         void *dst;
90
91         base = boot_params.screen_info.lfb_base;
92         si = &boot_params.screen_info;
93         len = si->lfb_linelength;
94
95         while (num) {
96                 unsigned int linemax;
97                 unsigned int h, count = 0;
98
99                 for (s = str; *s && *s != '\n'; s++) {
100                         if (count == num)
101                                 break;
102                         count++;
103                 }
104
105                 linemax = (si->lfb_width - efi_x) / font->width;
106                 if (count > linemax)
107                         count = linemax;
108
109                 for (h = 0; h < font->height; h++) {
110                         unsigned int n, x;
111
112                         dst = early_ioremap(base + (efi_y + h) * len, len);
113                         if (!dst)
114                                 return;
115
116                         s = str;
117                         n = count;
118                         x = efi_x;
119
120                         while (n-- > 0) {
121                                 early_efi_write_char(dst + x*4, *s, h);
122                                 x += font->width;
123                                 s++;
124                         }
125
126                         early_iounmap(dst, len);
127                 }
128
129                 num -= count;
130                 efi_x += count * font->width;
131                 str += count;
132
133                 if (num > 0 && *s == '\n') {
134                         efi_x = 0;
135                         efi_y += font->height;
136                         str++;
137                         num--;
138                 }
139
140                 if (efi_x >= si->lfb_width) {
141                         efi_x = 0;
142                         efi_y += font->height;
143                 }
144
145                 if (efi_y + font->height > si->lfb_height) {
146                         u32 i;
147
148                         efi_y -= font->height;
149                         early_efi_scroll_up();
150
151                         for (i = 0; i < font->height; i++)
152                                 early_efi_clear_scanline(efi_y + i);
153                 }
154         }
155 }
156
157 static __init int early_efi_setup(struct console *con, char *options)
158 {
159         struct screen_info *si;
160         u16 xres, yres;
161         u32 i;
162
163         si = &boot_params.screen_info;
164         xres = si->lfb_width;
165         yres = si->lfb_height;
166
167         /*
168          * early_efi_write_char() implicitly assumes a framebuffer with
169          * 32-bits per pixel.
170          */
171         if (si->lfb_depth != 32)
172                 return -ENODEV;
173
174         font = get_default_font(xres, yres, -1, -1);
175         if (!font)
176                 return -ENODEV;
177
178         efi_y = rounddown(yres, font->height) - font->height;
179         for (i = 0; i < (yres - efi_y) / font->height; i++)
180                 early_efi_scroll_up();
181
182         return 0;
183 }
184
185 struct console early_efi_console = {
186         .name =         "earlyefi",
187         .write =        early_efi_write,
188         .setup =        early_efi_setup,
189         .flags =        CON_PRINTBUFFER,
190         .index =        -1,
191 };