Skip to content

Commit 72beea3

Browse files
Ben Bensonroot
authored andcommitted
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 72beea3

File tree

2 files changed

+265
-49
lines changed

2 files changed

+265
-49
lines changed

drivers/video/fbdev/core/fb_logo.c

Lines changed: 253 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,21 @@
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 __initdata;
18+
static char *fullscreen_logo_path __initdata;
19+
typedef long ImagePalette[224][3];
1020

1121
static inline unsigned int safe_shift(unsigned int d, int n)
1222
{
@@ -79,6 +89,21 @@ static void fb_set_logo_truepalette(struct fb_info *info,
7989
}
8090
}
8191

92+
static void fb_set_logo_RGB_palette(ImagePalette palette, u32 *palette_to_write, int current_rows)
93+
{
94+
// Set the kernel palette from an array of RGB values
95+
uint32_t color_code;
96+
int i;
97+
98+
// Color format is RGB565, remove LSB 3 bits, and move to correct position
99+
for (i = 0; i < current_rows; i++) {
100+
color_code = ((((uint16_t)palette[i][0]) >> 3) << 11) |
101+
((((uint16_t)palette[i][1]) >> 2) << 5) |
102+
(((uint16_t)palette[i][2]) >> 3);
103+
palette_to_write[i+32] = color_code;
104+
}
105+
}
106+
82107
static void fb_set_logo_directpalette(struct fb_info *info,
83108
const struct linux_logo *logo,
84109
u32 *palette)
@@ -275,6 +300,166 @@ static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
275300
}
276301
}
277302

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

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

294-
if (fb_logo.needs_cmapreset)
295-
fb_set_logocmap(info, logo);
483+
if (fb_logo.needs_cmapreset)
484+
fb_set_logocmap(info, logo);
296485

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;
486+
if (fb_logo.needs_truepalette ||
487+
fb_logo.needs_directpalette) {
488+
palette = kmalloc(256 * 4, GFP_KERNEL);
489+
if (palette == NULL)
490+
return 0;
302491

303-
if (fb_logo.needs_truepalette)
304-
fb_set_logo_truepalette(info, logo, palette);
305-
else
306-
fb_set_logo_directpalette(info, logo, palette);
492+
if (fb_logo.needs_truepalette)
493+
fb_set_logo_truepalette(info, logo, palette);
494+
else
495+
fb_set_logo_directpalette(info, logo, palette);
307496

308-
saved_pseudo_palette = info->pseudo_palette;
309-
info->pseudo_palette = palette;
310-
}
497+
saved_pseudo_palette = info->pseudo_palette;
498+
info->pseudo_palette = palette;
499+
}
311500

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;
501+
if (fb_logo.depth <= 4) {
502+
logo_new = kmalloc_array(logo->width, logo->height,
503+
GFP_KERNEL);
504+
if (logo_new == NULL) {
505+
kfree(palette);
506+
if (saved_pseudo_palette)
507+
info->pseudo_palette = saved_pseudo_palette;
508+
return 0;
509+
}
510+
image.data = logo_new;
511+
fb_set_logo(info, logo, logo_new, fb_logo.depth);
320512
}
321-
image.data = logo_new;
322-
fb_set_logo(info, logo, logo_new, fb_logo.depth);
323-
}
324513

325-
if (fb_center_logo) {
326-
int xres = info->var.xres;
327-
int yres = info->var.yres;
514+
if (fb_center_logo) {
515+
int xres = info->var.xres;
516+
int yres = info->var.yres;
328517

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

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-
}
523+
while (n && (n * (logo->width + 8) - 8 > xres))
524+
--n;
525+
image.dx = (xres - (n * (logo->width + 8) - 8)) / 2;
526+
image.dy = y ?: (yres - logo->height) / 2;
527+
} else {
528+
image.dx = 0;
529+
image.dy = y;
530+
}
342531

343-
image.width = logo->width;
344-
image.height = logo->height;
532+
image.width = logo->width;
533+
image.height = logo->height;
345534

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);
535+
if (rotate) {
536+
logo_rotate = kmalloc_array(logo->width, logo->height,
537+
GFP_KERNEL);
538+
if (logo_rotate)
539+
fb_rotate_logo(info, logo_rotate, &image, rotate);
540+
}
541+
}
542+
if (fullscreen_logo_enabled) {
543+
// Fullscreen logo data may not fill screen
544+
// Fill remainder of screen with border color of logo for continuous feel
545+
u32 fill_color = image.data[0];
546+
struct fb_fillrect region;
547+
548+
region.color = fill_color;
549+
region.dx = 0;
550+
region.dy = 0;
551+
region.width = info->var.xres;
552+
region.height = info->var.yres;
553+
region.rop = ROP_COPY;
554+
info->fbops->fb_fillrect(info, &region);
351555
}
352556

353557
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)