1 // SPDX-License-Identifier: GPL-2.0
4 #include <linux/linux_logo.h>
6 #include "fb_internal.h"
8 bool fb_center_logo __read_mostly
;
9 int fb_logo_count __read_mostly
= -1;
11 static inline unsigned int safe_shift(unsigned int d
, int n
)
13 return n
< 0 ? d
>> -n
: d
<< n
;
16 static void fb_set_logocmap(struct fb_info
*info
,
17 const struct linux_logo
*logo
)
19 struct fb_cmap palette_cmap
;
20 u16 palette_green
[16];
24 const unsigned char *clut
= logo
->clut
;
26 palette_cmap
.start
= 0;
27 palette_cmap
.len
= 16;
28 palette_cmap
.red
= palette_red
;
29 palette_cmap
.green
= palette_green
;
30 palette_cmap
.blue
= palette_blue
;
31 palette_cmap
.transp
= NULL
;
33 for (i
= 0; i
< logo
->clutsize
; i
+= n
) {
34 n
= logo
->clutsize
- i
;
35 /* palette_cmap provides space for only 16 colors at once */
38 palette_cmap
.start
= 32 + i
;
40 for (j
= 0; j
< n
; ++j
) {
41 palette_cmap
.red
[j
] = clut
[0] << 8 | clut
[0];
42 palette_cmap
.green
[j
] = clut
[1] << 8 | clut
[1];
43 palette_cmap
.blue
[j
] = clut
[2] << 8 | clut
[2];
46 fb_set_cmap(&palette_cmap
, info
);
50 static void fb_set_logo_truepalette(struct fb_info
*info
,
51 const struct linux_logo
*logo
,
54 static const unsigned char mask
[] = {
55 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
57 unsigned char redmask
, greenmask
, bluemask
;
58 int redshift
, greenshift
, blueshift
;
60 const unsigned char *clut
= logo
->clut
;
63 * We have to create a temporary palette since console palette is only
66 /* Bug: Doesn't obey msb_right ... (who needs that?) */
67 redmask
= mask
[info
->var
.red
.length
< 8 ? info
->var
.red
.length
: 8];
68 greenmask
= mask
[info
->var
.green
.length
< 8 ? info
->var
.green
.length
: 8];
69 bluemask
= mask
[info
->var
.blue
.length
< 8 ? info
->var
.blue
.length
: 8];
70 redshift
= info
->var
.red
.offset
- (8 - info
->var
.red
.length
);
71 greenshift
= info
->var
.green
.offset
- (8 - info
->var
.green
.length
);
72 blueshift
= info
->var
.blue
.offset
- (8 - info
->var
.blue
.length
);
74 for (i
= 0; i
< logo
->clutsize
; i
++) {
75 palette
[i
+32] = (safe_shift((clut
[0] & redmask
), redshift
) |
76 safe_shift((clut
[1] & greenmask
), greenshift
) |
77 safe_shift((clut
[2] & bluemask
), blueshift
));
82 static void fb_set_logo_directpalette(struct fb_info
*info
,
83 const struct linux_logo
*logo
,
86 int redshift
, greenshift
, blueshift
;
89 redshift
= info
->var
.red
.offset
;
90 greenshift
= info
->var
.green
.offset
;
91 blueshift
= info
->var
.blue
.offset
;
93 for (i
= 32; i
< 32 + logo
->clutsize
; i
++)
94 palette
[i
] = i
<< redshift
| i
<< greenshift
| i
<< blueshift
;
97 static void fb_set_logo(struct fb_info
*info
,
98 const struct linux_logo
*logo
, u8
*dst
,
102 const u8
*src
= logo
->data
;
103 u8
xor = (info
->fix
.visual
== FB_VISUAL_MONO01
) ? 0xff : 0;
106 switch (fb_get_color_depth(&info
->var
, &info
->fix
)) {
118 if (info
->fix
.visual
== FB_VISUAL_MONO01
||
119 info
->fix
.visual
== FB_VISUAL_MONO10
)
120 fg
= ~((u8
) (0xfff << info
->var
.green
.length
));
124 for (i
= 0; i
< logo
->height
; i
++)
125 for (j
= 0; j
< logo
->width
; src
++) {
128 if (j
< logo
->width
) {
129 *dst
++ = *src
& 0x0f;
135 for (i
= 0; i
< logo
->height
; i
++) {
136 for (j
= 0; j
< logo
->width
; src
++) {
138 for (k
= 7; k
>= 0 && j
< logo
->width
; k
--) {
139 *dst
++ = ((d
>> k
) & 1) ? fg
: 0;
149 * Three (3) kinds of logo maps exist. linux_logo_clut224 (>16 colors),
150 * linux_logo_vga16 (16 colors) and linux_logo_mono (2 colors). Depending on
151 * the visual format and color depth of the framebuffer, the DAC, the
152 * pseudo_palette, and the logo data will be adjusted accordingly.
154 * Case 1 - linux_logo_clut224:
155 * Color exceeds the number of console colors (16), thus we set the hardware DAC
156 * using fb_set_cmap() appropriately. The "needs_cmapreset" flag will be set.
158 * For visuals that require color info from the pseudo_palette, we also construct
159 * one for temporary use. The "needs_directpalette" or "needs_truepalette" flags
162 * Case 2 - linux_logo_vga16:
163 * The number of colors just matches the console colors, thus there is no need
164 * to set the DAC or the pseudo_palette. However, the bitmap is packed, ie,
165 * each byte contains color information for two pixels (upper and lower nibble).
166 * To be consistent with fb_imageblit() usage, we therefore separate the two
167 * nibbles into separate bytes. The "depth" flag will be set to 4.
169 * Case 3 - linux_logo_mono:
170 * This is similar with Case 2. Each byte contains information for 8 pixels.
171 * We isolate each bit and expand each into a byte. The "depth" flag will
174 static struct logo_data
{
176 int needs_directpalette
;
177 int needs_truepalette
;
179 const struct linux_logo
*logo
;
180 } fb_logo __read_mostly
;
182 static void fb_rotate_logo_ud(const u8
*in
, u8
*out
, u32 width
, u32 height
)
184 u32 size
= width
* height
, i
;
188 for (i
= size
; i
--; )
192 static void fb_rotate_logo_cw(const u8
*in
, u8
*out
, u32 width
, u32 height
)
194 int i
, j
, h
= height
- 1;
196 for (i
= 0; i
< height
; i
++)
197 for (j
= 0; j
< width
; j
++)
198 out
[height
* j
+ h
- i
] = *in
++;
201 static void fb_rotate_logo_ccw(const u8
*in
, u8
*out
, u32 width
, u32 height
)
203 int i
, j
, w
= width
- 1;
205 for (i
= 0; i
< height
; i
++)
206 for (j
= 0; j
< width
; j
++)
207 out
[height
* (w
- j
) + i
] = *in
++;
210 static void fb_rotate_logo(struct fb_info
*info
, u8
*dst
,
211 struct fb_image
*image
, int rotate
)
215 if (rotate
== FB_ROTATE_UD
) {
216 fb_rotate_logo_ud(image
->data
, dst
, image
->width
,
218 image
->dx
= info
->var
.xres
- image
->width
- image
->dx
;
219 image
->dy
= info
->var
.yres
- image
->height
- image
->dy
;
220 } else if (rotate
== FB_ROTATE_CW
) {
221 fb_rotate_logo_cw(image
->data
, dst
, image
->width
,
223 swap(image
->width
, image
->height
);
225 image
->dy
= image
->dx
;
226 image
->dx
= info
->var
.xres
- image
->width
- tmp
;
227 } else if (rotate
== FB_ROTATE_CCW
) {
228 fb_rotate_logo_ccw(image
->data
, dst
, image
->width
,
230 swap(image
->width
, image
->height
);
232 image
->dx
= image
->dy
;
233 image
->dy
= info
->var
.yres
- image
->height
- tmp
;
239 static void fb_do_show_logo(struct fb_info
*info
, struct fb_image
*image
,
240 int rotate
, unsigned int num
)
244 if (image
->width
> info
->var
.xres
|| image
->height
> info
->var
.yres
)
247 if (rotate
== FB_ROTATE_UR
) {
249 x
< num
&& image
->dx
+ image
->width
<= info
->var
.xres
;
251 info
->fbops
->fb_imageblit(info
, image
);
252 image
->dx
+= image
->width
+ 8;
254 } else if (rotate
== FB_ROTATE_UD
) {
257 for (x
= 0; x
< num
&& image
->dx
<= dx
; x
++) {
258 info
->fbops
->fb_imageblit(info
, image
);
259 image
->dx
-= image
->width
+ 8;
261 } else if (rotate
== FB_ROTATE_CW
) {
263 x
< num
&& image
->dy
+ image
->height
<= info
->var
.yres
;
265 info
->fbops
->fb_imageblit(info
, image
);
266 image
->dy
+= image
->height
+ 8;
268 } else if (rotate
== FB_ROTATE_CCW
) {
271 for (x
= 0; x
< num
&& image
->dy
<= dy
; x
++) {
272 info
->fbops
->fb_imageblit(info
, image
);
273 image
->dy
-= image
->height
+ 8;
278 static int fb_show_logo_line(struct fb_info
*info
, int rotate
,
279 const struct linux_logo
*logo
, int y
,
282 u32
*palette
= NULL
, *saved_pseudo_palette
= NULL
;
283 unsigned char *logo_new
= NULL
, *logo_rotate
= NULL
;
284 struct fb_image image
;
286 /* Return if the frame buffer is not mapped or suspended */
287 if (logo
== NULL
|| info
->state
!= FBINFO_STATE_RUNNING
||
292 image
.data
= logo
->data
;
294 if (fb_logo
.needs_cmapreset
)
295 fb_set_logocmap(info
, logo
);
297 if (fb_logo
.needs_truepalette
||
298 fb_logo
.needs_directpalette
) {
299 palette
= kmalloc(256 * 4, GFP_KERNEL
);
303 if (fb_logo
.needs_truepalette
)
304 fb_set_logo_truepalette(info
, logo
, palette
);
306 fb_set_logo_directpalette(info
, logo
, palette
);
308 saved_pseudo_palette
= info
->pseudo_palette
;
309 info
->pseudo_palette
= palette
;
312 if (fb_logo
.depth
<= 4) {
313 logo_new
= kmalloc_array(logo
->width
, logo
->height
,
315 if (logo_new
== NULL
) {
317 if (saved_pseudo_palette
)
318 info
->pseudo_palette
= saved_pseudo_palette
;
321 image
.data
= logo_new
;
322 fb_set_logo(info
, logo
, logo_new
, fb_logo
.depth
);
325 if (fb_center_logo
) {
326 int xres
= info
->var
.xres
;
327 int yres
= info
->var
.yres
;
329 if (rotate
== FB_ROTATE_CW
|| rotate
== FB_ROTATE_CCW
) {
330 xres
= info
->var
.yres
;
331 yres
= info
->var
.xres
;
334 while (n
&& (n
* (logo
->width
+ 8) - 8 > xres
))
336 image
.dx
= (xres
- (n
* (logo
->width
+ 8) - 8)) / 2;
337 image
.dy
= y
?: (yres
- logo
->height
) / 2;
343 image
.width
= logo
->width
;
344 image
.height
= logo
->height
;
347 logo_rotate
= kmalloc_array(logo
->width
, logo
->height
,
350 fb_rotate_logo(info
, logo_rotate
, &image
, rotate
);
353 fb_do_show_logo(info
, &image
, rotate
, n
);
356 if (saved_pseudo_palette
!= NULL
)
357 info
->pseudo_palette
= saved_pseudo_palette
;
360 return image
.dy
+ logo
->height
;
363 #ifdef CONFIG_FB_LOGO_EXTRA
365 #define FB_LOGO_EX_NUM_MAX 10
366 static struct logo_data_extra
{
367 const struct linux_logo
*logo
;
369 } fb_logo_ex
[FB_LOGO_EX_NUM_MAX
];
370 static unsigned int fb_logo_ex_num
;
372 void fb_append_extra_logo(const struct linux_logo
*logo
, unsigned int n
)
374 if (!n
|| fb_logo_ex_num
== FB_LOGO_EX_NUM_MAX
)
377 fb_logo_ex
[fb_logo_ex_num
].logo
= logo
;
378 fb_logo_ex
[fb_logo_ex_num
].n
= n
;
382 static int fb_prepare_extra_logos(struct fb_info
*info
, unsigned int height
,
387 /* FIXME: logo_ex supports only truecolor fb. */
388 if (info
->fix
.visual
!= FB_VISUAL_TRUECOLOR
)
391 for (i
= 0; i
< fb_logo_ex_num
; i
++) {
392 if (fb_logo_ex
[i
].logo
->type
!= fb_logo
.logo
->type
) {
393 fb_logo_ex
[i
].logo
= NULL
;
396 height
+= fb_logo_ex
[i
].logo
->height
;
398 height
-= fb_logo_ex
[i
].logo
->height
;
406 static int fb_show_extra_logos(struct fb_info
*info
, int y
, int rotate
)
410 for (i
= 0; i
< fb_logo_ex_num
; i
++)
411 y
= fb_show_logo_line(info
, rotate
,
412 fb_logo_ex
[i
].logo
, y
, fb_logo_ex
[i
].n
);
416 #endif /* CONFIG_FB_LOGO_EXTRA */
418 int fb_prepare_logo(struct fb_info
*info
, int rotate
)
420 int depth
= fb_get_color_depth(&info
->var
, &info
->fix
);
424 memset(&fb_logo
, 0, sizeof(struct logo_data
));
426 if (info
->flags
& FBINFO_MISC_TILEBLITTING
||
427 info
->fbops
->owner
|| !fb_logo_count
)
430 if (info
->fix
.visual
== FB_VISUAL_DIRECTCOLOR
) {
431 depth
= info
->var
.blue
.length
;
432 if (info
->var
.red
.length
< depth
)
433 depth
= info
->var
.red
.length
;
434 if (info
->var
.green
.length
< depth
)
435 depth
= info
->var
.green
.length
;
438 if (info
->fix
.visual
== FB_VISUAL_STATIC_PSEUDOCOLOR
&& depth
> 4) {
439 /* assume console colormap */
443 /* Return if no suitable logo was found */
444 fb_logo
.logo
= fb_find_logo(depth
);
449 if (rotate
== FB_ROTATE_UR
|| rotate
== FB_ROTATE_UD
)
450 yres
= info
->var
.yres
;
452 yres
= info
->var
.xres
;
454 if (fb_logo
.logo
->height
> yres
) {
459 /* What depth we asked for might be different from what we get */
460 if (fb_logo
.logo
->type
== LINUX_LOGO_CLUT224
)
462 else if (fb_logo
.logo
->type
== LINUX_LOGO_VGA16
)
468 if (fb_logo
.depth
> 4 && depth
> 4) {
469 switch (info
->fix
.visual
) {
470 case FB_VISUAL_TRUECOLOR
:
471 fb_logo
.needs_truepalette
= 1;
473 case FB_VISUAL_DIRECTCOLOR
:
474 fb_logo
.needs_directpalette
= 1;
475 fb_logo
.needs_cmapreset
= 1;
477 case FB_VISUAL_PSEUDOCOLOR
:
478 fb_logo
.needs_cmapreset
= 1;
483 height
= fb_logo
.logo
->height
;
485 height
+= (yres
- fb_logo
.logo
->height
) / 2;
486 #ifdef CONFIG_FB_LOGO_EXTRA
487 height
= fb_prepare_extra_logos(info
, height
, yres
);
493 int fb_show_logo(struct fb_info
*info
, int rotate
)
501 count
= fb_logo_count
< 0 ? num_online_cpus() : fb_logo_count
;
502 y
= fb_show_logo_line(info
, rotate
, fb_logo
.logo
, 0, count
);
503 #ifdef CONFIG_FB_LOGO_EXTRA
504 y
= fb_show_extra_logos(info
, y
, rotate
);