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 ;
18+ static char * fullscreen_logo_path ;
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,32 @@ static void fb_set_logo_truepalette(struct fb_info *info,
7992 }
8093}
8194
95+ static void fb_set_logo_RGB_palette (struct fb_info * info ,
96+ struct image_palette * pal_in ,
97+ u32 * pal_out , int current_rows )
98+ {
99+ // Set the kernel palette from an array of RGB values
100+ static const unsigned char mask [] = {
101+ 0 , 0x80 , 0xc0 , 0xe0 , 0xf0 , 0xf8 , 0xfc , 0xfe , 0xff
102+ };
103+ unsigned char redmask , greenmask , bluemask ;
104+ int redshift , greenshift , blueshift ;
105+ int i ;
106+
107+ redmask = mask [info -> var .red .length < 8 ? info -> var .red .length : 8 ];
108+ greenmask = mask [info -> var .green .length < 8 ? info -> var .green .length : 8 ];
109+ bluemask = mask [info -> var .blue .length < 8 ? info -> var .blue .length : 8 ];
110+ redshift = info -> var .red .offset - (8 - info -> var .red .length );
111+ greenshift = info -> var .green .offset - (8 - info -> var .green .length );
112+ blueshift = info -> var .blue .offset - (8 - info -> var .blue .length );
113+
114+ for (i = 0 ; i < current_rows ; i ++ ) {
115+ pal_out [i + 32 ] = (safe_shift ((pal_in -> colors [i ][0 ] & redmask ), redshift ) |
116+ safe_shift ((pal_in -> colors [i ][1 ] & greenmask ), greenshift ) |
117+ safe_shift ((pal_in -> colors [i ][2 ] & bluemask ), blueshift ));
118+ }
119+ }
120+
82121static void fb_set_logo_directpalette (struct fb_info * info ,
83122 const struct linux_logo * logo ,
84123 u32 * palette )
@@ -275,6 +314,171 @@ static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
275314 }
276315}
277316
317+ static int __init fb_fullscreen_logo_setup (char * str )
318+ {
319+ fullscreen_logo_enabled = 1 ;
320+ fullscreen_logo_path = str ;
321+ pr_info ("Fullscreen splash enabled, using image path: %s" , fullscreen_logo_path );
322+ return 1 ;
323+ }
324+
325+ __setup ("fullscreen_logo_name=" , fb_fullscreen_logo_setup );
326+
327+ static bool fb_palette_contains_entry (struct image_palette * palette , int num_existing_rows ,
328+ u8 * entry_to_add , int cols , int * index )
329+ {
330+ for (int i = 0 ; i < num_existing_rows ; i ++ ) {
331+ bool match = true;
332+
333+ for (int j = 0 ; j < cols ; j ++ ) {
334+ if (palette -> colors [i ][j ] != entry_to_add [j ]) {
335+ match = false;
336+ break ;
337+ }
338+ }
339+ if (match ) {
340+ * index = i ; // Update the index
341+ return true; // Found a duplicate
342+ }
343+ }
344+ return false; // No duplicate found
345+ }
346+
347+ static void fb_set_logo_from_file (struct fb_info * info , const char * filepath ,
348+ struct fb_image * image , u32 * palette )
349+ {
350+ int current_rows = 0 , palette_index = 0 , actual_row , skip_x = 0 , skip_y = 0 , ret ;
351+ unsigned char * read_logo = NULL , * header ;
352+ const char * file_content ;
353+ const struct firmware * fw = NULL ;
354+ struct image_palette image_palette ;
355+ const char * current_ptr , * end_ptr ;
356+ long width = 0 , height = 0 ;
357+ bool top_to_bottom ;
358+ u8 B , G , R ;
359+ u8 entry [3 ];
360+ ssize_t len ;
361+
362+ ret = firmware_request_nowarn (& fw , filepath , info -> device );
363+ if (ret ) {
364+ pr_info ("Failed to load logo file '%s': %d\n" , filepath , ret );
365+ goto cleanup ;
366+ }
367+ len = fw -> size ;
368+ file_content = fw -> data ;
369+
370+ if (!len ) {
371+ pr_err ("Error: logo TGA file is empty. Not drawing fullscreen logo.\n" );
372+ goto cleanup ;
373+ }
374+
375+ current_ptr = file_content ;
376+ end_ptr = file_content + len ;
377+ if (len < 18 ) {
378+ pr_err ("Invalid logo file: TGA file too small for header\n" );
379+ goto cleanup ;
380+ }
381+ header = (unsigned char * )file_content ;
382+
383+ // Skip color map info (bytes 3-7)
384+ // Skip image origin (bytes 8-11)
385+ width = header [12 ] | (header [13 ] << 8 );
386+ height = header [14 ] | (header [15 ] << 8 );
387+
388+ // Only supports uncompressed true-color images (type 2) with 24-bit depth
389+ if (header [2 ] != 2 || header [16 ] != 24 ) {
390+ pr_err ("Unsupported TGA logo format: Type=%d, Depth=%d (only support Type=2, Depth=24)\n" ,
391+ header [2 ], header [16 ]);
392+ goto cleanup ;
393+ }
394+ // Skip header + ID field
395+ current_ptr = file_content + 18 + header [0 ];
396+
397+ read_logo = kmalloc_array (width , height , GFP_KERNEL );
398+ if (!read_logo )
399+ goto cleanup ;
400+
401+ image -> data = read_logo ;
402+
403+ // TGA pixels are stored bottom-to-top by default, unless bit 5 of
404+ // image_descriptor is set
405+ top_to_bottom = (header [17 ] & 0x20 ) != 0 ;
406+ skip_x = 0 ;
407+ skip_y = 0 ;
408+
409+ if (width > info -> var .xres ) {
410+ pr_info ("Logo is larger than screen (%lu vs %u), clipping horizontally\n" ,
411+ width , info -> var .xres );
412+ skip_x = (width - info -> var .xres ) / 2 ;
413+ }
414+ if (height > info -> var .yres ) {
415+ pr_info ("Logo is larger than screen (%lu vs %u), clipping vertically\n" ,
416+ height , info -> var .yres );
417+ skip_y = (height - info -> var .yres ) / 2 ;
418+ }
419+ current_ptr += skip_y * width * 3 + skip_x * 3 ;
420+ // Parse pixel data (BGR format in TGA)
421+ for (int i = 0 ; i < height - 2 * skip_y ; i ++ ) {
422+ for (int j = 0 ; j < width - 2 * skip_x ; j ++ ) {
423+ if (current_ptr + 3 > end_ptr ) {
424+ pr_info ("TGA: Unexpected end of file\n" );
425+ goto cleanup ;
426+ }
427+ B = (unsigned char )* current_ptr ++ ;
428+ G = (unsigned char )* current_ptr ++ ;
429+ R = (unsigned char )* current_ptr ++ ;
430+ entry [0 ] = R ;
431+ entry [1 ] = G ;
432+ entry [2 ] = B ;
433+ palette_index = 0 ;
434+
435+ if (!fb_palette_contains_entry (& image_palette , current_rows ,
436+ entry , 3 , & palette_index )) {
437+ if (current_rows < 224 ) {
438+ for (int k = 0 ; k < 3 ; k ++ )
439+ image_palette .colors [current_rows ][k ] =
440+ entry [k ];
441+ palette_index = current_rows ;
442+ }
443+ current_rows ++ ;
444+ }
445+ actual_row = top_to_bottom ? i : (height - 1 - i );
446+
447+ read_logo [actual_row * (width - 2 * skip_x ) + j ] =
448+ palette_index + 32 ;
449+ }
450+ current_ptr += skip_x * 3 * 2 ;
451+ }
452+
453+ if (current_rows >= 224 )
454+ pr_err ("Palette overflow. Entries clipped\n" );
455+
456+ // Set logo palette
457+ palette = kzalloc (256 * 4 , GFP_KERNEL );
458+ if (!palette )
459+ goto cleanup ;
460+ fb_set_logo_RGB_palette (info , & image_palette , palette , current_rows );
461+ if (info -> pseudo_palette )
462+ memcpy (palette , info -> pseudo_palette , 32 * sizeof (uint32_t ));
463+ info -> pseudo_palette = palette ;
464+
465+ image -> width = min_t (unsigned int , width , info -> var .xres );
466+ image -> height = min_t (unsigned int , height , info -> var .yres );
467+ image -> dx = 0 ;
468+ image -> dy = 0 ;
469+ image -> depth = 8 ;
470+
471+ if (image -> height < info -> var .yres )
472+ image -> dy = (info -> var .yres - image -> height ) / 2 ;
473+ if (image -> width < info -> var .xres )
474+ image -> dx = (info -> var .xres - image -> width ) / 2 ;
475+
476+ cleanup :
477+ kfree (read_logo );
478+ release_firmware (fw );
479+ }
480+
481+
278482static int fb_show_logo_line (struct fb_info * info , int rotate ,
279483 const struct linux_logo * logo , int y ,
280484 unsigned int n )
@@ -288,66 +492,87 @@ static int fb_show_logo_line(struct fb_info *info, int rotate,
288492 info -> fbops -> owner )
289493 return 0 ;
290494
291- image .depth = 8 ;
292- image .data = logo -> data ;
495+ if (fullscreen_logo_enabled ) {
496+ fb_set_logo_from_file (info , fullscreen_logo_path ,
497+ & image , palette );
498+ } else {
499+ image .depth = 8 ;
500+ image .data = logo -> data ;
293501
294- if (fb_logo .needs_cmapreset )
295- fb_set_logocmap (info , logo );
502+ if (fb_logo .needs_cmapreset )
503+ fb_set_logocmap (info , logo );
296504
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 ;
505+ if (fb_logo .needs_truepalette ||
506+ fb_logo .needs_directpalette ) {
507+ palette = kmalloc (256 * 4 , GFP_KERNEL );
508+ if (palette == NULL )
509+ return 0 ;
302510
303- if (fb_logo .needs_truepalette )
304- fb_set_logo_truepalette (info , logo , palette );
305- else
306- fb_set_logo_directpalette (info , logo , palette );
511+ if (fb_logo .needs_truepalette )
512+ fb_set_logo_truepalette (info , logo , palette );
513+ else
514+ fb_set_logo_directpalette (info , logo , palette );
307515
308- saved_pseudo_palette = info -> pseudo_palette ;
309- info -> pseudo_palette = palette ;
310- }
516+ saved_pseudo_palette = info -> pseudo_palette ;
517+ info -> pseudo_palette = palette ;
518+ }
311519
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 ;
520+ if (fb_logo .depth <= 4 ) {
521+ logo_new = kmalloc_array (logo -> width , logo -> height ,
522+ GFP_KERNEL );
523+ if (logo_new == NULL ) {
524+ kfree (palette );
525+ if (saved_pseudo_palette )
526+ info -> pseudo_palette = saved_pseudo_palette ;
527+ return 0 ;
528+ }
529+ image .data = logo_new ;
530+ fb_set_logo (info , logo , logo_new , fb_logo .depth );
320531 }
321- image .data = logo_new ;
322- fb_set_logo (info , logo , logo_new , fb_logo .depth );
323- }
324532
325- if (fb_center_logo ) {
326- int xres = info -> var .xres ;
327- int yres = info -> var .yres ;
533+ if (fb_center_logo ) {
534+ int xres = info -> var .xres ;
535+ int yres = info -> var .yres ;
328536
329- if (rotate == FB_ROTATE_CW || rotate == FB_ROTATE_CCW ) {
330- xres = info -> var .yres ;
331- yres = info -> var .xres ;
332- }
537+ if (rotate == FB_ROTATE_CW || rotate == FB_ROTATE_CCW ) {
538+ xres = info -> var .yres ;
539+ yres = info -> var .xres ;
540+ }
333541
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- }
542+ while (n && (n * (logo -> width + 8 ) - 8 > xres ))
543+ -- n ;
544+ image .dx = (xres - (n * (logo -> width + 8 ) - 8 )) / 2 ;
545+ image .dy = y ?: (yres - logo -> height ) / 2 ;
546+ } else {
547+ image .dx = 0 ;
548+ image .dy = y ;
549+ }
342550
343- image .width = logo -> width ;
344- image .height = logo -> height ;
551+ image .width = logo -> width ;
552+ image .height = logo -> height ;
345553
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 );
554+ if (rotate ) {
555+ logo_rotate = kmalloc_array (logo -> width , logo -> height ,
556+ GFP_KERNEL );
557+ if (logo_rotate )
558+ fb_rotate_logo (info , logo_rotate , & image , rotate );
559+ }
560+ }
561+ if (fullscreen_logo_enabled ) {
562+ // Fullscreen logo data may not fill screen
563+ // Fill remainder of screen with border color of logo for continuous feel
564+ u32 fill_color = image .data [0 ];
565+ struct fb_fillrect region ;
566+
567+ region .color = fill_color ;
568+ region .dx = 0 ;
569+ region .dy = 0 ;
570+ region .width = info -> var .xres ;
571+ region .height = info -> var .yres ;
572+ region .rop = ROP_COPY ;
573+ info -> fbops -> fb_fillrect (info , & region );
574+ // Enforce only one draw of the logo
575+ n = 1 ;
351576 }
352577
353578 fb_do_show_logo (info , & image , rotate , n );
0 commit comments