Skip to content

Commit f4a7526

Browse files
author
Ben Benson
committed
Fullscreen Splash Images
Enable by adding the following to cmdline.txt: `fullscreen_logo_name=logo.tga fullscreen_logo=1` Will show the logo file present in /lib/firmware/ on the screen. This will be fullscreen and rendered early at boot. Any remaining space is filled with solid color from the image border. If TGA file is too big, image is clipped accordingly. Signed-off-by: Ben Benson <[email protected]>
1 parent 5a72e3a commit f4a7526

File tree

2 files changed

+267
-49
lines changed

2 files changed

+267
-49
lines changed

drivers/video/fbdev/core/fb_logo.c

Lines changed: 255 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,24 @@
22

33
#include <linux/fb.h>
44
#include <linux/linux_logo.h>
5+
#include <linux/fs.h>
6+
#include <linux/slab.h>
7+
#include <linux/string.h>
8+
#include <linux/uaccess.h>
9+
#include <linux/file.h>
10+
#include <linux/kernel.h>
11+
#include <linux/firmware.h>
512

613
#include "fb_internal.h"
714

815
bool fb_center_logo __read_mostly;
916
int fb_logo_count __read_mostly = -1;
17+
static int fullscreen_logo_enabled;
18+
static char *fullscreen_logo_path;
19+
20+
struct image_palette {
21+
u8 colors[224][3];
22+
};
1023

1124
static inline unsigned int safe_shift(unsigned int d, int n)
1225
{
@@ -79,6 +92,22 @@ static void fb_set_logo_truepalette(struct fb_info *info,
7992
}
8093
}
8194

95+
static void fb_set_logo_RGB_palette(struct image_palette *palette,
96+
u32 *palette_to_write, int current_rows)
97+
{
98+
// Set the kernel palette from an array of RGB values
99+
uint32_t color_code;
100+
int i;
101+
102+
// Color format is RGB565, remove LSB 3 bits, and move to correct position
103+
for (i = 0; i < current_rows; i++) {
104+
color_code = ((((uint16_t)palette->colors[i][0]) >> 3) << 11) |
105+
((((uint16_t)palette->colors[i][1]) >> 2) << 5) |
106+
(((uint16_t)palette->colors[i][2]) >> 3);
107+
palette_to_write[i+32] = color_code;
108+
}
109+
}
110+
82111
static void fb_set_logo_directpalette(struct fb_info *info,
83112
const struct linux_logo *logo,
84113
u32 *palette)
@@ -275,6 +304,164 @@ static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
275304
}
276305
}
277306

307+
static int __init fb_fullscreen_logo_setup(char *str)
308+
{
309+
fullscreen_logo_enabled = 1;
310+
fullscreen_logo_path = str;
311+
pr_info("Fullscreen Logo Enabled: %d", fullscreen_logo_enabled);
312+
pr_info("Fullscreen Logo Path: %s", fullscreen_logo_path);
313+
return 1;
314+
}
315+
316+
__setup("fullscreen_logo_name=", fb_fullscreen_logo_setup);
317+
318+
static bool fb_palette_contains_entry(struct image_palette *palette, int num_existing_rows,
319+
u8 *entry_to_add, int cols, int *index)
320+
{
321+
for (int i = 0; i < num_existing_rows; i++) {
322+
bool match = true;
323+
324+
for (int j = 0; j < cols; j++) {
325+
if (palette->colors[i][j] != entry_to_add[j]) {
326+
match = false;
327+
break;
328+
}
329+
}
330+
if (match) {
331+
*index = i; // Update the index
332+
return true; // Found a duplicate
333+
}
334+
}
335+
return false; // No duplicate found
336+
}
337+
338+
static void fb_set_logo_from_file(struct fb_info *info, const char *filepath,
339+
struct fb_image *image, u32 *palette)
340+
{
341+
struct image_palette image_palette;
342+
const char *file_content = NULL;
343+
unsigned char *read_logo = NULL;
344+
long width = 0, height = 0;
345+
ssize_t len;
346+
int ret;
347+
const struct firmware *fw;
348+
349+
ret = request_firmware(&fw, filepath, info->device);
350+
if (ret) {
351+
pr_info("Failed to load logo file '%s': %d\n", filepath, ret);
352+
goto cleanup;
353+
}
354+
len = fw->size;
355+
file_content = fw->data;
356+
357+
if (len > 0) {
358+
int current_rows = 0;
359+
const char *current_ptr = file_content;
360+
const char *end_ptr = file_content + len;
361+
362+
if (len < 18) {
363+
pr_info("Invalid logo file: TGA file too small for header\n");
364+
goto cleanup;
365+
}
366+
367+
unsigned char *header = (unsigned char *)file_content;
368+
369+
// Parse TGA header
370+
unsigned char id_length = header[0];
371+
unsigned char image_type = header[2];
372+
// Skip color map info (bytes 3-7)
373+
// Skip image origin (bytes 8-11)
374+
width = header[12] | (header[13] << 8);
375+
height = header[14] | (header[15] << 8);
376+
image->width = width;
377+
image->height = height;
378+
unsigned char pixel_depth = header[16];
379+
unsigned char image_descriptor = header[17];
380+
381+
// Only supports uncompressed true-color images (type 2) with 24-bit depth
382+
if (image_type != 2 || pixel_depth != 24) {
383+
pr_info("Unsupported TGA logo format: Type=%d, Depth=%d (only support Type=2, Depth=24)\n",
384+
image_type, pixel_depth);
385+
goto cleanup;
386+
}
387+
// Skip header + ID field
388+
current_ptr = file_content + 18 + id_length;
389+
390+
read_logo = kmalloc_array(width, height, GFP_KERNEL);
391+
if (read_logo == NULL)
392+
goto cleanup;
393+
394+
image->data = read_logo;
395+
396+
// TGA pixels are stored bottom-to-top by default, unless bit 5 of
397+
// image_descriptor is set
398+
bool top_to_bottom = (image_descriptor & 0x20) != 0;
399+
int skip_x = 0, skip_y = 0;
400+
401+
if (image->width > info->var.xres) {
402+
pr_info("Logo is larger than screen, clipping horizontally");
403+
skip_x = (image->width - info->var.xres) / 2;
404+
}
405+
if (image->height > info->var.yres) {
406+
pr_info("Logo is larger than screen, clipping vertically");
407+
skip_y = (image->height - info->var.yres) / 2;
408+
}
409+
current_ptr += skip_y * width * 3 + skip_x * 3;
410+
// Parse pixel data (BGR format in TGA)
411+
for (int i = 0; i < height - 2 * skip_y; i++) {
412+
for (int j = 0; j < width - 2 * skip_x; j++) {
413+
if (current_ptr + 3 > end_ptr) {
414+
pr_info("TGA: Unexpected end of file\n");
415+
goto cleanup;
416+
}
417+
u8 B = (unsigned char)*current_ptr++;
418+
u8 G = (unsigned char)*current_ptr++;
419+
u8 R = (unsigned char)*current_ptr++;
420+
u8 entry[3] = {R, G, B};
421+
int palette_index = 0;
422+
423+
if (!fb_palette_contains_entry(&image_palette, current_rows,
424+
entry, 3, &palette_index)) {
425+
for (int k = 0; k < 3; k++)
426+
image_palette.colors[current_rows][k] = entry[k];
427+
palette_index = current_rows;
428+
current_rows++;
429+
}
430+
int actual_row = top_to_bottom ? i : (height - 1 - i);
431+
432+
read_logo[actual_row * (width - 2 * skip_x) + j] =
433+
palette_index + 32;
434+
}
435+
current_ptr += skip_x * 3 * 2;
436+
}
437+
438+
// Set logo palette
439+
palette = kmalloc(256 * 4, GFP_KERNEL);
440+
if (palette == NULL)
441+
goto cleanup;
442+
fb_set_logo_RGB_palette(&image_palette, palette, current_rows);
443+
info->pseudo_palette = palette;
444+
445+
} else {
446+
pr_info("Error: logo TGA file is empty. Not drawing fullscreen logo.\n");
447+
}
448+
449+
image->width = min_t(unsigned int, width, info->var.xres);
450+
image->height = min_t(unsigned int, height, info->var.yres);
451+
image->dx = 0;
452+
image->dy = 0;
453+
image->depth = 8;
454+
455+
if (image->height < info->var.yres)
456+
image->dy = (info->var.yres - image->height) / 2;
457+
if (image->width < info->var.xres)
458+
image->dx = (info->var.xres - image->width) / 2;
459+
460+
cleanup:
461+
if (file_content)
462+
kvfree(file_content);
463+
}
464+
278465
static int fb_show_logo_line(struct fb_info *info, int rotate,
279466
const struct linux_logo *logo, int y,
280467
unsigned int n)
@@ -288,66 +475,85 @@ static int fb_show_logo_line(struct fb_info *info, int rotate,
288475
info->fbops->owner)
289476
return 0;
290477

291-
image.depth = 8;
292-
image.data = logo->data;
478+
if (fullscreen_logo_enabled) {
479+
fb_set_logo_from_file(info, fullscreen_logo_path,
480+
&image, palette);
481+
} else {
482+
image.depth = 8;
483+
image.data = logo->data;
293484

294-
if (fb_logo.needs_cmapreset)
295-
fb_set_logocmap(info, logo);
485+
if (fb_logo.needs_cmapreset)
486+
fb_set_logocmap(info, logo);
296487

297-
if (fb_logo.needs_truepalette ||
298-
fb_logo.needs_directpalette) {
299-
palette = kmalloc(256 * 4, GFP_KERNEL);
300-
if (palette == NULL)
301-
return 0;
488+
if (fb_logo.needs_truepalette ||
489+
fb_logo.needs_directpalette) {
490+
palette = kmalloc(256 * 4, GFP_KERNEL);
491+
if (palette == NULL)
492+
return 0;
302493

303-
if (fb_logo.needs_truepalette)
304-
fb_set_logo_truepalette(info, logo, palette);
305-
else
306-
fb_set_logo_directpalette(info, logo, palette);
494+
if (fb_logo.needs_truepalette)
495+
fb_set_logo_truepalette(info, logo, palette);
496+
else
497+
fb_set_logo_directpalette(info, logo, palette);
307498

308-
saved_pseudo_palette = info->pseudo_palette;
309-
info->pseudo_palette = palette;
310-
}
499+
saved_pseudo_palette = info->pseudo_palette;
500+
info->pseudo_palette = palette;
501+
}
311502

312-
if (fb_logo.depth <= 4) {
313-
logo_new = kmalloc_array(logo->width, logo->height,
314-
GFP_KERNEL);
315-
if (logo_new == NULL) {
316-
kfree(palette);
317-
if (saved_pseudo_palette)
318-
info->pseudo_palette = saved_pseudo_palette;
319-
return 0;
503+
if (fb_logo.depth <= 4) {
504+
logo_new = kmalloc_array(logo->width, logo->height,
505+
GFP_KERNEL);
506+
if (logo_new == NULL) {
507+
kfree(palette);
508+
if (saved_pseudo_palette)
509+
info->pseudo_palette = saved_pseudo_palette;
510+
return 0;
511+
}
512+
image.data = logo_new;
513+
fb_set_logo(info, logo, logo_new, fb_logo.depth);
320514
}
321-
image.data = logo_new;
322-
fb_set_logo(info, logo, logo_new, fb_logo.depth);
323-
}
324515

325-
if (fb_center_logo) {
326-
int xres = info->var.xres;
327-
int yres = info->var.yres;
516+
if (fb_center_logo) {
517+
int xres = info->var.xres;
518+
int yres = info->var.yres;
328519

329-
if (rotate == FB_ROTATE_CW || rotate == FB_ROTATE_CCW) {
330-
xres = info->var.yres;
331-
yres = info->var.xres;
332-
}
520+
if (rotate == FB_ROTATE_CW || rotate == FB_ROTATE_CCW) {
521+
xres = info->var.yres;
522+
yres = info->var.xres;
523+
}
333524

334-
while (n && (n * (logo->width + 8) - 8 > xres))
335-
--n;
336-
image.dx = (xres - (n * (logo->width + 8) - 8)) / 2;
337-
image.dy = y ?: (yres - logo->height) / 2;
338-
} else {
339-
image.dx = 0;
340-
image.dy = y;
341-
}
525+
while (n && (n * (logo->width + 8) - 8 > xres))
526+
--n;
527+
image.dx = (xres - (n * (logo->width + 8) - 8)) / 2;
528+
image.dy = y ?: (yres - logo->height) / 2;
529+
} else {
530+
image.dx = 0;
531+
image.dy = y;
532+
}
342533

343-
image.width = logo->width;
344-
image.height = logo->height;
534+
image.width = logo->width;
535+
image.height = logo->height;
345536

346-
if (rotate) {
347-
logo_rotate = kmalloc_array(logo->width, logo->height,
348-
GFP_KERNEL);
349-
if (logo_rotate)
350-
fb_rotate_logo(info, logo_rotate, &image, rotate);
537+
if (rotate) {
538+
logo_rotate = kmalloc_array(logo->width, logo->height,
539+
GFP_KERNEL);
540+
if (logo_rotate)
541+
fb_rotate_logo(info, logo_rotate, &image, rotate);
542+
}
543+
}
544+
if (fullscreen_logo_enabled) {
545+
// Fullscreen logo data may not fill screen
546+
// Fill remainder of screen with border color of logo for continuous feel
547+
u32 fill_color = image.data[0];
548+
struct fb_fillrect region;
549+
550+
region.color = fill_color;
551+
region.dx = 0;
552+
region.dy = 0;
553+
region.width = info->var.xres;
554+
region.height = info->var.yres;
555+
region.rop = ROP_COPY;
556+
info->fbops->fb_fillrect(info, &region);
351557
}
352558

353559
fb_do_show_logo(info, &image, rotate, n);

drivers/video/fbdev/core/fbcon.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ static struct fbcon_display fb_display[MAX_NR_CONSOLES];
107107

108108
static struct fb_info *fbcon_registered_fb[FB_MAX];
109109
static int fbcon_num_registered_fb;
110+
static int fullscreen_logo_enabled __initdata;
110111

111112
#define fbcon_for_each_registered_fb(i) \
112113
for (i = 0; WARN_CONSOLE_UNLOCKED(), i < FB_MAX; i++) \
@@ -510,6 +511,15 @@ static int __init fb_console_setup(char *this_opt)
510511
}
511512

512513
__setup("fbcon=", fb_console_setup);
514+
515+
static int __init fullscreen_logo_setup(char *str)
516+
{
517+
pr_info("Fullscreen Logo Enabled");
518+
fullscreen_logo_enabled = 1;
519+
return 1;
520+
}
521+
522+
__setup("fullscreen_logo=", fullscreen_logo_setup);
513523
#endif
514524

515525
static int search_fb_in_map(int idx)
@@ -593,6 +603,8 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
593603
if (fb_get_color_depth(&info->var, &info->fix) == 1)
594604
erase &= ~0x400;
595605
logo_height = fb_prepare_logo(info, ops->rotate);
606+
if (fullscreen_logo_enabled)
607+
logo_height = info->var.yres - vc->vc_font.height;
596608
logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height);
597609
q = (unsigned short *) (vc->vc_origin +
598610
vc->vc_size_row * rows);

0 commit comments

Comments
 (0)