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 __initdata ;
18
+ static char * fullscreen_logo_path __initdata ;
19
+ typedef long ImagePalette [224 ][3 ];
10
20
11
21
static inline unsigned int safe_shift (unsigned int d , int n )
12
22
{
@@ -79,6 +89,21 @@ static void fb_set_logo_truepalette(struct fb_info *info,
79
89
}
80
90
}
81
91
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
+
82
107
static void fb_set_logo_directpalette (struct fb_info * info ,
83
108
const struct linux_logo * logo ,
84
109
u32 * palette )
@@ -275,6 +300,166 @@ static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
275
300
}
276
301
}
277
302
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
+
278
463
static int fb_show_logo_line (struct fb_info * info , int rotate ,
279
464
const struct linux_logo * logo , int y ,
280
465
unsigned int n )
@@ -288,66 +473,85 @@ static int fb_show_logo_line(struct fb_info *info, int rotate,
288
473
info -> fbops -> owner )
289
474
return 0 ;
290
475
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 ;
293
482
294
- if (fb_logo .needs_cmapreset )
295
- fb_set_logocmap (info , logo );
483
+ if (fb_logo .needs_cmapreset )
484
+ fb_set_logocmap (info , logo );
296
485
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 ;
302
491
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 );
307
496
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
+ }
311
500
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 );
320
512
}
321
- image .data = logo_new ;
322
- fb_set_logo (info , logo , logo_new , fb_logo .depth );
323
- }
324
513
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 ;
328
517
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
+ }
333
522
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
+ }
342
531
343
- image .width = logo -> width ;
344
- image .height = logo -> height ;
532
+ image .width = logo -> width ;
533
+ image .height = logo -> height ;
345
534
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 );
351
555
}
352
556
353
557
fb_do_show_logo (info , & image , rotate , n );
0 commit comments