2
2
3
3
#include <linux/fb.h>
4
4
#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>
5
12
6
13
#include "fb_internal.h"
7
14
8
15
bool fb_center_logo __read_mostly ;
9
16
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
+ };
10
23
11
24
static inline unsigned int safe_shift (unsigned int d , int n )
12
25
{
@@ -79,6 +92,22 @@ static void fb_set_logo_truepalette(struct fb_info *info,
79
92
}
80
93
}
81
94
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
+
82
111
static void fb_set_logo_directpalette (struct fb_info * info ,
83
112
const struct linux_logo * logo ,
84
113
u32 * palette )
@@ -275,6 +304,164 @@ static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
275
304
}
276
305
}
277
306
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
+
278
465
static int fb_show_logo_line (struct fb_info * info , int rotate ,
279
466
const struct linux_logo * logo , int y ,
280
467
unsigned int n )
@@ -288,66 +475,85 @@ static int fb_show_logo_line(struct fb_info *info, int rotate,
288
475
info -> fbops -> owner )
289
476
return 0 ;
290
477
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 ;
293
484
294
- if (fb_logo .needs_cmapreset )
295
- fb_set_logocmap (info , logo );
485
+ if (fb_logo .needs_cmapreset )
486
+ fb_set_logocmap (info , logo );
296
487
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 ;
302
493
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 );
307
498
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
+ }
311
502
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 );
320
514
}
321
- image .data = logo_new ;
322
- fb_set_logo (info , logo , logo_new , fb_logo .depth );
323
- }
324
515
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 ;
328
519
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
+ }
333
524
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
+ }
342
533
343
- image .width = logo -> width ;
344
- image .height = logo -> height ;
534
+ image .width = logo -> width ;
535
+ image .height = logo -> height ;
345
536
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 );
351
557
}
352
558
353
559
fb_do_show_logo (info , & image , rotate , n );
0 commit comments