Skip to content

Commit c35e8a4

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, fullscreen at boot Any remaining space is filled with solid color from the image border. If TGA file is too big, image is clipped accordingly
1 parent 5a72e3a commit c35e8a4

File tree

2 files changed

+212
-0
lines changed

2 files changed

+212
-0
lines changed

drivers/video/fbdev/core/fb_logo.c

Lines changed: 199 additions & 0 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,19 @@ 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) | ((((uint16_t)palette[i][1]) >> 2) << 5) | (((uint16_t)palette[i][2]) >> 3);
101+
palette_to_write[i+32] = color_code;
102+
}
103+
}
104+
82105
static void fb_set_logo_directpalette(struct fb_info *info,
83106
const struct linux_logo *logo,
84107
u32 *palette)
@@ -275,6 +298,162 @@ static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
275298
}
276299
}
277300

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

470+
471+
if (fullscreen_logo_enabled) {
472+
fb_set_logo_from_file(info, fullscreen_logo_path, &image, palette, saved_pseudo_palette);
473+
} else {
474+
291475
image.depth = 8;
292476
image.data = logo->data;
293477

@@ -350,6 +534,21 @@ static int fb_show_logo_line(struct fb_info *info, int rotate,
350534
fb_rotate_logo(info, logo_rotate, &image, rotate);
351535
}
352536

537+
}
538+
if (fullscreen_logo_enabled) {
539+
// Fullscreen logo data may not fill screen
540+
// Fill remainder of screen with border color of logo for continuous feel
541+
u32 fill_color = image.data[0];
542+
struct fb_fillrect region;
543+
region.color = fill_color;
544+
region.dx = 0;
545+
region.dy = 0;
546+
region.width = info->var.xres;
547+
region.height = info->var.yres;
548+
region.rop = ROP_COPY;
549+
info->fbops->fb_fillrect(info, &region);
550+
}
551+
353552
fb_do_show_logo(info, &image, rotate, n);
354553

355554
kfree(palette);

drivers/video/fbdev/core/fbcon.c

Lines changed: 13 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+
printk(KERN_ALERT "Fullscreen Logo Setup within FBCON");
517+
fullscreen_logo_enabled = 1;
518+
printk(KERN_ALERT "Fullscreen Logo Enabled (FBCON): %d", fullscreen_logo_enabled);
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,9 @@ 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;
608+
}
596609
logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height);
597610
q = (unsigned short *) (vc->vc_origin +
598611
vc->vc_size_row * rows);

0 commit comments

Comments
 (0)