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
815bool fb_center_logo __read_mostly ;
916int fb_logo_count __read_mostly = -1 ;
17+ static int fullscreen_logo_enabled __initdata ;
18+ static char * fullscreen_logo_path __initdata ;
19+
20+ struct image_palette {
21+ u8 colors [224 ][3 ];
22+ };
1023
1124static 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+
82111static void fb_set_logo_directpalette (struct fb_info * info ,
83112 const struct linux_logo * logo ,
84113 u32 * palette )
@@ -275,6 +304,166 @@ 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+ u32 * saved_pseudo_palette )
341+ {
342+ struct image_palette image_palette ;
343+ const char * file_content = NULL ;
344+ unsigned char * read_logo = NULL ;
345+ long width = 0 , height = 0 ;
346+ ssize_t len ;
347+ int ret ;
348+ const struct firmware * fw ;
349+
350+ ret = request_firmware (& fw , filepath , info -> device );
351+ if (ret ) {
352+ pr_info ("Failed to load logo file '%s': %d\n" , filepath , ret );
353+ goto cleanup ;
354+ }
355+ len = fw -> size ;
356+ file_content = fw -> data ;
357+
358+ if (len > 0 ) {
359+ int current_rows = 0 ;
360+ const char * current_ptr = file_content ;
361+ const char * end_ptr = file_content + len ;
362+
363+ if (len < 18 ) {
364+ pr_info ("Invalid logo file: TGA file too small for header\n" );
365+ goto cleanup ;
366+ }
367+
368+ unsigned char * header = (unsigned char * )file_content ;
369+
370+ // Parse TGA header
371+ unsigned char id_length = header [0 ];
372+ unsigned char image_type = header [2 ];
373+ // Skip color map info (bytes 3-7)
374+ // Skip image origin (bytes 8-11)
375+ width = header [12 ] | (header [13 ] << 8 );
376+ height = header [14 ] | (header [15 ] << 8 );
377+ image -> width = width ;
378+ image -> height = height ;
379+ unsigned char pixel_depth = header [16 ];
380+ unsigned char image_descriptor = header [17 ];
381+
382+ // Only supports uncompressed true-color images (type 2) with 24-bit depth
383+ if (image_type != 2 || pixel_depth != 24 ) {
384+ pr_info ("Unsupported TGA logo format: Type=%d, Depth=%d (only support Type=2, Depth=24)\n" ,
385+ image_type , pixel_depth );
386+ goto cleanup ;
387+ }
388+ // Skip header + ID field
389+ current_ptr = file_content + 18 + id_length ;
390+
391+ read_logo = kmalloc_array (width , height , GFP_KERNEL );
392+ if (read_logo == NULL )
393+ goto cleanup ;
394+
395+ image -> data = read_logo ;
396+
397+ // TGA pixels are stored bottom-to-top by default, unless bit 5 of
398+ // image_descriptor is set
399+ bool top_to_bottom = (image_descriptor & 0x20 ) != 0 ;
400+ int skip_x = 0 , skip_y = 0 ;
401+
402+ if (image -> width > info -> var .xres ) {
403+ pr_info ("Logo is larger than screen, clipping horizontally" );
404+ skip_x = (image -> width - info -> var .xres ) / 2 ;
405+ }
406+ if (image -> height > info -> var .yres ) {
407+ pr_info ("Logo is larger than screen, clipping vertically" );
408+ skip_y = (image -> height - info -> var .yres ) / 2 ;
409+ }
410+ current_ptr += skip_y * width * 3 + skip_x * 3 ;
411+ // Parse pixel data (BGR format in TGA)
412+ for (int i = 0 ; i < height - 2 * skip_y ; i ++ ) {
413+ for (int j = 0 ; j < width - 2 * skip_x ; j ++ ) {
414+ if (current_ptr + 3 > end_ptr ) {
415+ pr_info ("TGA: Unexpected end of file\n" );
416+ goto cleanup ;
417+ }
418+ u8 B = (unsigned char )* current_ptr ++ ;
419+ u8 G = (unsigned char )* current_ptr ++ ;
420+ u8 R = (unsigned char )* current_ptr ++ ;
421+ u8 entry [3 ] = {R , G , B };
422+ int palette_index = 0 ;
423+
424+ if (!fb_palette_contains_entry (& image_palette , current_rows ,
425+ entry , 3 , & palette_index )) {
426+ for (int k = 0 ; k < 3 ; k ++ )
427+ image_palette .colors [current_rows ][k ] = entry [k ];
428+ palette_index = current_rows ;
429+ current_rows ++ ;
430+ }
431+ int actual_row = top_to_bottom ? i : (height - 1 - i );
432+
433+ read_logo [actual_row * (width - 2 * skip_x ) + j ] =
434+ palette_index + 32 ;
435+ }
436+ current_ptr += skip_x * 3 * 2 ;
437+ }
438+
439+ // Set logo palette
440+ palette = kmalloc (256 * 4 , GFP_KERNEL );
441+ if (palette == NULL )
442+ goto cleanup ;
443+ fb_set_logo_RGB_palette (& image_palette , palette , current_rows );
444+ saved_pseudo_palette = info -> pseudo_palette ;
445+ info -> pseudo_palette = palette ;
446+
447+ } else {
448+ pr_info ("Error: logo TGA file is empty. Not drawing fullscreen logo.\n" );
449+ }
450+
451+ image -> width = min_t (unsigned int , width , info -> var .xres );
452+ image -> height = min_t (unsigned int , height , info -> var .yres );
453+ image -> dx = 0 ;
454+ image -> dy = 0 ;
455+ image -> depth = 8 ;
456+
457+ if (image -> height < info -> var .yres )
458+ image -> dy = (info -> var .yres - image -> height ) / 2 ;
459+ if (image -> width < info -> var .xres )
460+ image -> dx = (info -> var .xres - image -> width ) / 2 ;
461+
462+ cleanup :
463+ if (file_content )
464+ kvfree (file_content );
465+ }
466+
278467static int fb_show_logo_line (struct fb_info * info , int rotate ,
279468 const struct linux_logo * logo , int y ,
280469 unsigned int n )
@@ -288,66 +477,85 @@ static int fb_show_logo_line(struct fb_info *info, int rotate,
288477 info -> fbops -> owner )
289478 return 0 ;
290479
291- image .depth = 8 ;
292- image .data = logo -> data ;
480+ if (fullscreen_logo_enabled ) {
481+ fb_set_logo_from_file (info , fullscreen_logo_path ,
482+ & image , palette , saved_pseudo_palette );
483+ } else {
484+ image .depth = 8 ;
485+ image .data = logo -> data ;
293486
294- if (fb_logo .needs_cmapreset )
295- fb_set_logocmap (info , logo );
487+ if (fb_logo .needs_cmapreset )
488+ fb_set_logocmap (info , logo );
296489
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 ;
490+ if (fb_logo .needs_truepalette ||
491+ fb_logo .needs_directpalette ) {
492+ palette = kmalloc (256 * 4 , GFP_KERNEL );
493+ if (palette == NULL )
494+ return 0 ;
302495
303- if (fb_logo .needs_truepalette )
304- fb_set_logo_truepalette (info , logo , palette );
305- else
306- fb_set_logo_directpalette (info , logo , palette );
496+ if (fb_logo .needs_truepalette )
497+ fb_set_logo_truepalette (info , logo , palette );
498+ else
499+ fb_set_logo_directpalette (info , logo , palette );
307500
308- saved_pseudo_palette = info -> pseudo_palette ;
309- info -> pseudo_palette = palette ;
310- }
501+ saved_pseudo_palette = info -> pseudo_palette ;
502+ info -> pseudo_palette = palette ;
503+ }
311504
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 ;
505+ if (fb_logo .depth <= 4 ) {
506+ logo_new = kmalloc_array (logo -> width , logo -> height ,
507+ GFP_KERNEL );
508+ if (logo_new == NULL ) {
509+ kfree (palette );
510+ if (saved_pseudo_palette )
511+ info -> pseudo_palette = saved_pseudo_palette ;
512+ return 0 ;
513+ }
514+ image .data = logo_new ;
515+ fb_set_logo (info , logo , logo_new , fb_logo .depth );
320516 }
321- image .data = logo_new ;
322- fb_set_logo (info , logo , logo_new , fb_logo .depth );
323- }
324517
325- if (fb_center_logo ) {
326- int xres = info -> var .xres ;
327- int yres = info -> var .yres ;
518+ if (fb_center_logo ) {
519+ int xres = info -> var .xres ;
520+ int yres = info -> var .yres ;
328521
329- if (rotate == FB_ROTATE_CW || rotate == FB_ROTATE_CCW ) {
330- xres = info -> var .yres ;
331- yres = info -> var .xres ;
332- }
522+ if (rotate == FB_ROTATE_CW || rotate == FB_ROTATE_CCW ) {
523+ xres = info -> var .yres ;
524+ yres = info -> var .xres ;
525+ }
333526
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- }
527+ while (n && (n * (logo -> width + 8 ) - 8 > xres ))
528+ -- n ;
529+ image .dx = (xres - (n * (logo -> width + 8 ) - 8 )) / 2 ;
530+ image .dy = y ?: (yres - logo -> height ) / 2 ;
531+ } else {
532+ image .dx = 0 ;
533+ image .dy = y ;
534+ }
342535
343- image .width = logo -> width ;
344- image .height = logo -> height ;
536+ image .width = logo -> width ;
537+ image .height = logo -> height ;
345538
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 );
539+ if (rotate ) {
540+ logo_rotate = kmalloc_array (logo -> width , logo -> height ,
541+ GFP_KERNEL );
542+ if (logo_rotate )
543+ fb_rotate_logo (info , logo_rotate , & image , rotate );
544+ }
545+ }
546+ if (fullscreen_logo_enabled ) {
547+ // Fullscreen logo data may not fill screen
548+ // Fill remainder of screen with border color of logo for continuous feel
549+ u32 fill_color = image .data [0 ];
550+ struct fb_fillrect region ;
551+
552+ region .color = fill_color ;
553+ region .dx = 0 ;
554+ region .dy = 0 ;
555+ region .width = info -> var .xres ;
556+ region .height = info -> var .yres ;
557+ region .rop = ROP_COPY ;
558+ info -> fbops -> fb_fillrect (info , & region );
351559 }
352560
353561 fb_do_show_logo (info , & image , rotate , n );
0 commit comments