forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathps2fb.c
2362 lines (2088 loc) · 65 KB
/
ps2fb.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// SPDX-License-Identifier: GPL-2.0
/*
* PlayStation 2 frame buffer driver
*
* Copyright (C) 2019 Fredrik Noring
*/
/**
* DOC: The PlayStation 2 frame buffer console
*
* The frame buffer supports a tiled frame buffer console. The main limitation
* is the lack of memory mapping (mmap), since the Graphics Synthesizer has
* local frame buffer memory that is not directly accessible from the main bus.
* The GS has 4 MiB of local memory.
*
* The console drawing primitives are synchronous to allow printk at any time.
* This is highly useful for debugging but it is not the fastest possible
* implementation. The console is nevertheless very fast and makes use of
* several hardware accelerated features of the Graphics Synthesizer.
*
* The maximum practical resolution is 1920x1080p at 16 bits per pixel that
* requires 4147200 bytes of local memory, leaving 47104 bytes for a tiled
* font, which at 8x8 pixels and a minimum 4 bits indexed texture palette is
* at most 1464 characters. The indexed palette makes switching colours easy.
* &struct fb_tile_ops is accelerated with GS texture sprites that are fast
* (GS local copy) for the kernel via simple DMA GS commands via the GIF.
*
* The local memory is organised as follows: first comes the display buffer,
* then one block of a palette, and finally the font installed as a texture.
*
* All frame buffer transmissions are done by DMA via GIF PATH3.
*/
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <asm/mach-ps2/dmac.h>
#include <asm/mach-ps2/gif.h>
#include <asm/mach-ps2/gs.h>
#include <asm/mach-ps2/gs-registers.h>
#include <uapi/asm/gif.h>
#include <uapi/asm/gs.h>
#define DEVICE_NAME "ps2fb"
#define PALETTE_SIZE 256
#define PALETTE_BLOCK_COUNT 1 /* One block is used for the indexed colors */
#define GIF_PACKAGE_TAG(package) ((package)++)->gif.tag = (struct gif_tag)
#define GIF_PACKAGE_REG(package) ((package)++)->gif.reg = (struct gif_data_reg)
#define GIF_PACKAGE_AD(package) ((package)++)->gif.packed.ad = (struct gif_packed_ad)
#define DMA_PACKAGE_TAG(package) ((package)++)->dma = (struct dma_tag)
/* Module parameters */
static char *mode_option;
static char *mode_margin = "";
union package {
union gif_data gif;
struct dma_tag dma;
};
/**
* struct tile_texture - texture representing a tile
* @tbp: texture base pointer
* @u: texel u coordinate (x coordinate)
* @v: texel v coordinate (y coordinate)
*/
struct tile_texture {
u32 tbp;
u32 u;
u32 v;
};
/**
* struct console_buffer - console buffer
* @block_count: number of frame buffer blocks
* @bg: background color index
* @fg: foreground color index
* @tile: tile dimensions
* @tile.width: width in pixels
* @tile.height: height in pixels
* @tile.width2: least width in pixels, power of 2
* @tile.height2: least height in pixels, power of 2
* @tile.block: tiles are stored as textures in the PSMT4 pixel storage format
* with both cols and rows as powers of 2
* @tile.block.cols: tile columns per GS block
* @tile.block.rows: tile rows per GS block
*/
struct console_buffer {
u32 block_count;
u32 bg;
u32 fg;
struct cb_tile {
u32 width;
u32 height;
u32 width2;
u32 height2;
struct {
u32 cols;
u32 rows;
} block;
} tile;
};
/**
* struct ps2fb_par - driver specific structure
* @lock: spin lock to be taken for all structure operations
* @mode: frame buffer video mode
* @pseudo_palette: pseudo palette, used for texture colouring
* @grayscale: perform grayscale colour conversion if %true
* @cb: console buffer definition
* @package: tags and datafor the GIF
* @package.capacity: maximum number of GIF packages in 16-byte unit
* @package.buffer: DMA buffer for GIF packages
*/
struct ps2fb_par {
spinlock_t lock;
struct fb_videomode mode;
struct gs_rgba32 pseudo_palette[PALETTE_SIZE];
bool grayscale;
struct console_buffer cb;
struct {
size_t capacity;
union package *buffer;
} package;
};
/**
* struct gs_sync_param - Graphics Synthesizer registers for video modes
* @smode1: SMODE1 register
* @smode2: SMODE2 register
* @srfsh: SRFSH register
* @synch1: SYNCH1 register
* @synch2: SYNCH2 register
* @syncv: SYNCV register
* @display: DISPLAY1 or DISPLAY2 register
*
* These are the essential Graphics Synthesizer video synchronisation register
* parameters.
*/
struct gs_sync_param {
struct gs_smode1 smode1;
struct gs_smode2 smode2;
struct gs_srfsh srfsh;
struct gs_synch1 synch1;
struct gs_synch2 synch2;
struct gs_syncv syncv;
struct gs_display display;
};
static const struct fb_videomode standard_modes[] = {
/* PAL */
{ "256p", 50, 640, 256, 74074, 100, 61, 34, 22, 63, 2,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
{ "288p", 50, 720, 288, 74074, 70, 11, 19, 3, 63, 3,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
{ "512i", 50, 640, 512, 74074, 100, 61, 67, 41, 63, 5,
FB_SYNC_BROADCAST, FB_VMODE_INTERLACED, FB_MODE_IS_STANDARD },
{ "576i", 50, 720, 576, 74074, 70, 11, 39, 5, 63, 5,
FB_SYNC_BROADCAST, FB_VMODE_INTERLACED, FB_MODE_IS_STANDARD },
{ "576p", 50, 720, 576, 37037, 70, 11, 39, 5, 63, 5,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
{ "720p", 50, 1280, 720, 13468, 220, 400, 19, 6, 80, 5,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
{ "1080i", 50, 1920, 1080, 13468, 148, 484, 36, 4, 88, 5,
FB_SYNC_BROADCAST, FB_VMODE_INTERLACED, FB_MODE_IS_STANDARD },
{ "1080p", 50, 1920, 1080, 6734, 148, 484, 36, 4, 88, 5,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
/* PAL with borders to ensure that the whole screen is visible */
{ "460i", 50, 576, 460, 74074, 142, 83, 97, 63, 63, 5,
FB_SYNC_BROADCAST, FB_VMODE_INTERLACED },
{ "460p", 50, 576, 460, 37037, 142, 83, 97, 63, 63, 5,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
{ "644p", 50, 1124, 644, 13468, 298, 478, 57, 44, 80, 5,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
{ "964i", 50, 1688, 964, 13468, 264, 600, 94, 62, 88, 5,
FB_SYNC_BROADCAST, FB_VMODE_INTERLACED },
{ "964p", 50, 1688, 964, 6734, 264, 600, 94, 62, 88, 5,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
/* NTSC */
{ "224p", 60, 640, 224, 74074, 95, 60, 22, 14, 63, 3,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
{ "240p", 60, 720, 240, 74074, 58, 17, 15, 5, 63, 3,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
{ "448i", 60, 640, 448, 74074, 95, 60, 44, 27, 63, 6,
FB_SYNC_BROADCAST, FB_VMODE_INTERLACED, FB_MODE_IS_STANDARD },
{ "480i", 60, 720, 480, 74074, 58, 17, 30, 9, 63, 6,
FB_SYNC_BROADCAST, FB_VMODE_INTERLACED, FB_MODE_IS_STANDARD },
{ "480p", 60, 720, 480, 37037, 58, 17, 30, 9, 63, 6,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
{ "720p", 60, 1280, 720, 13481, 220, 70, 19, 6, 80, 5,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
{ "1080i", 60, 1920, 1080, 13481, 148, 44, 36, 4, 88, 5,
FB_SYNC_BROADCAST, FB_VMODE_INTERLACED, FB_MODE_IS_STANDARD },
{ "1080p", 60, 1920, 1080, 6741, 148, 44, 36, 4, 88, 5,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
/* NTSC with borders to ensure that the whole screen is visible */
{ "384i", 60, 576, 384, 74074, 130, 89, 78, 57, 63, 6,
FB_SYNC_BROADCAST, FB_VMODE_INTERLACED },
{ "384p", 60, 576, 384, 37037, 130, 89, 78, 57, 63, 6,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
{ "644p", 60, 1124, 644, 13481, 298, 148, 57, 44, 80, 5,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
{ "964i", 60, 1688, 964, 13481, 264, 160, 94, 62, 88, 5,
FB_SYNC_BROADCAST, FB_VMODE_INTERLACED },
{ "964p", 60, 1688, 964, 6741, 264, 160, 94, 62, 88, 5,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
/* VESA */
{ "vesa-1a", 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2,
0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
{ "vesa-1c", 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3,
0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
{ "vesa-2b", 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
{ "vesa-2d", 75, 800, 600, 20202, 160, 16, 21, 1, 80, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
{ "vesa-3b", 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6,
0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
{ "vesa-3d", 75, 1024, 768, 12690, 176, 16, 28, 1, 96, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
{ "vesa-4a", 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
{ "vesa-4b", 75, 1280, 1024, 7407, 248, 16, 38, 1, 144, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }
};
/**
* console_pseudo_palette - Graphics Synthesizer RGBA for a palette index
* @regno: pseudo palette index number
* @par: driver specific object
*
* Return: RGBA colour object for the Graphics Synthesizer
*/
static struct gs_rgbaq console_pseudo_palette(const u32 regno,
const struct ps2fb_par *par)
{
const struct gs_rgba32 c = regno < PALETTE_SIZE ?
par->pseudo_palette[regno] : (struct gs_rgba32) { };
const u32 a = (c.a + 1) / 2; /* 0x80 = GS_ALPHA_ONE = 1.0 */
if (par->grayscale) {
/*
* Construct luminance Y' = 0.299R' + 0.587G' + 0.114B' with
* fixed-point integer arithmetic, where 77 + 150 + 29 = 256.
*/
const u32 y = (c.r*77 + c.g*150 + c.b*29) >> 8;
return (struct gs_rgbaq) { .r = y, .g = y, .b = y, .a = a };
}
return (struct gs_rgbaq) { .r = c.r, .g = c.g, .b = c.b, .a = a };
}
/**
* var_to_fbw - frame buffer width for a given virtual x resolution
* @var: screen info object to compute FBW for
*
* Return: frame buffer width (FBW) in 64-pixel unit
*/
static u32 var_to_fbw(const struct fb_var_screeninfo *var)
{
/*
* Round up to nearest GS_FB_PAGE_WIDTH (64 px) since there are
* valid resolutions such as 720 px that do not divide 64 properly.
*/
return (var->xres_virtual + GS_FB_PAGE_WIDTH - 1) / GS_FB_PAGE_WIDTH;
}
/**
* var_to_psm - frame buffer pixel storage mode for a given bits per pixel
* @var: screen info object to compute PSM for
* @info: frame buffer info object
*
* Return: frame buffer pixel storage mode
*/
static enum gs_psm var_to_psm(const struct fb_var_screeninfo *var,
const struct fb_info *info)
{
if (var->bits_per_pixel == 1)
return gs_psm_ct16;
if (var->bits_per_pixel == 16)
return gs_psm_ct16;
if (var->bits_per_pixel == 32)
return gs_psm_ct32;
fb_warn_once(info, "%s: Unsupported bits per pixel %u\n",
__func__, var->bits_per_pixel);
return gs_psm_ct32;
}
/**
* var_to_block_count - number of frame buffer blocks for a given video mode
* @var: screen info object to compute the number of blocks for
*
* The Graphics Synthesizer frame buffer is subdivided into rectangular pages,
* from left to right, top to bottom. Pages are further subdivided into blocks,
* with different arrangements for PSMCT16 and PSMCT32. Blocks are further
* subdivided into columns, which are finally subdivided into pixels.
*
* The video display buffer, textures and palettes share the same frame buffer.
* This function can be used to compute the first free block after the video
* display buffer.
*
* Return: number of blocks, or zero for unsupported pixel storage modes
*/
static u32 var_to_block_count(const struct fb_info *info)
{
const struct fb_var_screeninfo *var = &info->var;
const enum gs_psm psm = var_to_psm(var, info);
const u32 fbw = var_to_fbw(var);
if (psm == gs_psm_ct16)
return gs_psm_ct16_block_count(fbw, var->yres_virtual);
if (psm == gs_psm_ct32)
return gs_psm_ct32_block_count(fbw, var->yres_virtual);
fb_warn_once(info, "%s: Unsupported pixel storage mode %u\n",
__func__, psm);
return 0;
}
/**
* block_address_for_index - frame buffer block address for given block index
* @block_index: index of block to compute the address of
* @info: frame buffer info object
*
* Return: block address, or zero for unsupported pixel storage modes
*/
static u32 block_address_for_index(const u32 block_index,
const struct fb_info *info)
{
const struct fb_var_screeninfo *var = &info->var;
const enum gs_psm psm = var_to_psm(var, info);
const u32 fbw = var_to_fbw(var);
if (psm == gs_psm_ct16)
return gs_psm_ct16_block_address(fbw, block_index);
if (psm == gs_psm_ct32)
return gs_psm_ct32_block_address(fbw, block_index);
fb_warn_once(info, "%s: Unsupported pixel storage format %u\n",
__func__, psm);
return 0;
}
/**
* color_base_pointer - colour base pointer
* @info: frame buffer info object
*
* Return: block index of the colour palette, that follows the display buffer
*/
static u32 color_base_pointer(const struct fb_info *info)
{
const struct ps2fb_par *par = info->par;
return par->cb.block_count;
}
/**
* texture_address_for_index - frame buffer texture address for given index
* @block_index: index of block to compute the address of
* @info: frame buffer info object
*
* Return: texture address, or zero for unsupported pixel storage modes
*/
static u32 texture_address_for_index(const u32 block_index,
const struct fb_info *info)
{
const struct ps2fb_par *par = info->par;
return block_address_for_index(
par->cb.block_count + PALETTE_BLOCK_COUNT + block_index,
info);
}
/**
* texture_least_power_of_2 - round up to a power of 2, not less than 8
* @n: integer to round up
*
* Return: least integer that is a power of 2 and not less than @n or 8
*/
static u32 texture_least_power_of_2(u32 n)
{
return max(1 << get_count_order(n), 8);
}
/**
* cb_tile - create a console buffer tile object
* @width: width of tile in pixels
* @height: height of tile in pixels
*
* Return: a console buffer tile object with the given width and height
*/
static struct cb_tile cb_tile(u32 width, u32 height)
{
const u32 width2 = texture_least_power_of_2(width);
const u32 height2 = texture_least_power_of_2(height);
return (struct cb_tile) {
.width = width,
.height = height,
.width2 = width2,
.height2 = height2,
.block = {
.cols = GS_PSMT4_BLOCK_WIDTH / width2,
.rows = GS_PSMT4_BLOCK_HEIGHT / height2,
},
};
}
/**
* texture_for_tile - texture base pointer and texel coordinates for tile index
* @tile_index: index of tile to compute the texture for
* @info: frame buffer info object
*
* Returns texture base pointer and texel coordinates
*/
static struct tile_texture texture_for_tile(const u32 tile_index,
const struct fb_info *info)
{
const struct ps2fb_par *par = info->par;
const u32 texture_tile_count =
par->cb.tile.block.cols * par->cb.tile.block.rows;
const u32 block_tile = tile_index / texture_tile_count;
const u32 texture_tile = tile_index % texture_tile_count;
const u32 block_address = texture_address_for_index(block_tile, info);
const u32 row = texture_tile / par->cb.tile.block.cols;
const u32 col = texture_tile % par->cb.tile.block.cols;
return (struct tile_texture) {
.tbp = block_address,
.u = col * par->cb.tile.width2,
.v = row * par->cb.tile.height2
};
}
/**
* valid_bitbltbuf_width - is the BITBLTBUF width valid?
* @width: width in pixels to check
* @psm: pixel storage mode
*
* In local-to-local BITBLTBUF transmissions, the following restrictions
* to the TRXREG register width field apply depending on the pixel storage
* mode[1]:
*
* - multiple of 2: PSMCT32, PSMZ32
* - multiple of 4: PSMCT16, PSMCT16S, PSMZ16, PSMZ16S
* - multiple of 8: PSMCT24, PSMZ24, PSMT8, PSMT8H, PSMT4, PSMT4HL, PSMT4HH
*
* Return: %true if the given width is valid, otherwise %false
*/
static bool valid_bitbltbuf_width(int width, enum gs_psm psm)
{
if (width < 1)
return false;
if (psm == gs_psm_ct32 && (width & 1) != 0)
return false;
if (psm == gs_psm_ct16 && (width & 3) != 0)
return false;
return true;
}
/**
* display_buffer_size - display buffer size for a given video resolution
*
* This calculation is a lower bound estimate. A precise calculation would have
* to take memory pages, blocks and column arrangements into account. To choose
* the appropriate standard video mode such details can be disregarded, though.
*
* Return: the size in bytes of the display buffer
*/
static u32 display_buffer_size(const u32 xres_virtual, const u32 yres_virtual,
const u32 bits_per_pixel)
{
return (xres_virtual * yres_virtual * bits_per_pixel) / 8;
}
/**
* struct environment - Graphics Synthesizer drawing environment context
* @xres: x resolution in pixels
* @yres: y resolution in pixels
* @fbw: frame buffer width in 64-pixel unit
* @psm: pixel storage mode
* @fbp: frame buffer base pointer in 2048-pixel unit
*/
struct environment {
u32 xres;
u32 yres;
u32 fbw;
enum gs_psm psm;
u32 fbp;
};
/**
* var_to_env - Graphics Synthesizer drawing environment for a given video mode
* @var: screen info object
* @info: frame buffer info object
*
* Return: Graphics Synthesizer drawing environment parameters
*/
static struct environment var_to_env(const struct fb_var_screeninfo *var,
const struct fb_info *info)
{
return (struct environment) {
.xres = var->xres,
.yres = var->yres,
.fbw = var_to_fbw(var),
.psm = var_to_psm(var, info)
};
}
/**
* package_environment - package drawing environment tags and data for the GIF
* @package: DMA buffer to put packages in
* @env: drawing environment to package
*
* Various parameters are used to draw Graphics Synthesizer primitives, for
* example texture information and drawing attributes set by the PRIM register.
* These parameters are called the drawing environment. The environment remains
* in effect for multiple primitives until it is reset.
*
* Some environment registers such as XYOFFSET_1 and XYOFFSET_2 represent the
* same function and can be chosen with the CTXT primitive attribute.
*
* The following registers are available:
*
* ============ =============================================================
* XYOFFSET_1/2 offset value of vertex coordinates
* PRMODECONT PRIM attributes enabled/disabled
* TEX0_1/2 attributes of texture buffer and texture mapping
* TEX1_1/2 attributes of texture mapping
* TEX2_1/2 colour lookup table entry
* CLAMP_1/2 wrap mode of texture mapping
* TEXCLUT colour lookup table setting
* SCANMSK drawing control with y coordinate of pixel
* MIPTBP1_1/2 base pointer for MIPMAP on each level
* MIPTBP2_1/2 base pointer for MIPMAP on each level
* TEXA reference value when expanding alpha value of TEX16 and TEX24
* FOGCOL fogging distant color
* SCISSOR_1/2 scissoring area
* ALPHA_1/2 alpha-blending attributes
* DIMX dither matrix
* DTHE dithering enabled/disabled
* COLCLAMP color clamp/mask
* TEST_1/2 pixel operation
* PABE alpha-blending in pixel units enabled/disabled
* FBA_1/2 alpha correction value
* FRAME_1/2 frame buffer setting
* ZBUF_1/2 z buffer setting
* ============ =============================================================
*
* Return: number of generated GIF packages in 16-byte unit
*/
static size_t package_environment(union package *package,
const struct environment env)
{
union package * const base_package = package;
GIF_PACKAGE_TAG(package) {
.flg = gif_packed_mode,
.reg0 = gif_reg_ad,
.nreg = 1,
.nloop = 11
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_frame_1,
.data.frame_1 = {
.fbw = env.fbw,
.fbp = env.fbp,
.psm = env.psm
}
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_xyoffset_1,
.data.xyoffset_1 = {
.ofx = 0,
.ofy = 0,
}
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_scissor_1,
.data.scissor_1 = {
.scax0 = 0, .scax1 = env.xres,
.scay0 = 0, .scay1 = env.yres
}
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_scanmsk,
.data.scanmsk = {
.msk = gs_scanmsk_normal
}
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_prmode,
.data.prmode = { } /* Reset PRMODE to a known value */
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_prmodecont,
.data.prmodecont = {
.ac = 1
}
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_test_1,
.data.test_1 = {
.zte = gs_depth_test_on, /* Must always be ON */
.ztst = gs_depth_pass /* Emulate OFF */
}
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_texa,
.data.texa = {
.ta0 = GS_ALPHA_ONE,
.aem = gs_aem_normal,
.ta1 = GS_ALPHA_ONE
}
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_tex1_1,
.data.tex1 = {
.lcm = gs_lcm_fixed,
.mmag = gs_lod_nearest,
.mmin = gs_lod_nearest,
.k = 0
}
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_zbuf_1,
.data.zbuf = {
.zmsk = gs_zbuf_off
}
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_dthe,
.data.dthe = {
.dthe = gs_dthe_off
}
};
return package - base_package;
}
/**
* write_cb_environment - write console buffer GS drawing environment to the GIF
* @info: frame buffer info object
*
* Write various parameters used to draw Graphics Synthesizer primitives, for
* example texture information and drawing attributes set by the PRIM register.
* The environment remains in effect for multiple primitives until it is reset.
*/
void write_cb_environment(struct fb_info *info)
{
if (gif_wait()) {
struct ps2fb_par *par = info->par;
union package * const base_package = par->package.buffer;
union package *package = base_package;
package += package_environment(package,
var_to_env(&info->var, info));
gif_write(&base_package->gif, package - base_package);
} else
fb_err(info, "Failed to write GS environment, GIF is busy\n");
}
/**
* package_copyarea - package copy area tags and data for the GIF
* @package: DMA buffer to put packages in
* @area: area to copy
* @info: frame buffer info object
*
* Return: number of generated GIF packages in 16-byte unit
*/
static size_t package_copyarea(union package *package,
const struct fb_copyarea *area, const struct fb_info *info)
{
const struct fb_var_screeninfo *var = &info->var;
union package * const base_package = package;
const int psm = var_to_psm(var, info);
const int fbw = var_to_fbw(var);
GIF_PACKAGE_TAG(package) {
.flg = gif_packed_mode,
.reg0 = gif_reg_ad,
.nreg = 1,
.nloop = 4
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_bitbltbuf,
.data.bitbltbuf = {
.spsm = psm, .sbw = fbw,
.dpsm = psm, .dbw = fbw
}
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_trxpos,
.data.trxpos = {
.ssax = area->sx, .ssay = area->sy,
.dsax = area->dx, .dsay = area->dy,
.dir = area->dy < area->sy ||
(area->dy == area->sy && area->dx < area->sx) ?
gs_trxpos_dir_ul_lr : gs_trxpos_dir_lr_ul
}
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_trxreg,
.data.trxreg = {
.rrw = area->width,
.rrh = area->height
}
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_trxdir,
.data.trxdir = { .xdir = gs_trxdir_local_to_local }
};
return package - base_package;
}
/**
* ps2fb_cb_copyarea - copy console buffer area
* @area: area to copy
* @info: frame buffer info object
*/
void ps2fb_cb_copyarea(const struct fb_copyarea *area, struct fb_info *info)
{
const enum gs_psm psm = var_to_psm(&info->var, info);
struct ps2fb_par *par = info->par;
unsigned long flags;
if (info->state != FBINFO_STATE_RUNNING)
return;
if (area->width < 1 || area->height < 1)
return;
if (!valid_bitbltbuf_width(area->width, psm)) {
/*
* Some widths are not entirely supported with BITBLTBUF,
* but there will be more graphical glitches by refusing
* to proceed. Besides, info->pixmap.blit_x says that
* they are unsupported so unless someone wants to have
* odd fonts we will not end up here anyway. Warn once
* here for the protocol.
*/
fb_warn_once(info, "%s: "
"Unsupported width %u for pixel storage format %u\n",
__func__, area->width, psm);
}
spin_lock_irqsave(&par->lock, flags);
if (gif_wait()) {
union package * const base_package = par->package.buffer;
union package *package = base_package;
package += package_copyarea(package, area, info);
gif_write(&base_package->gif, package - base_package);
}
spin_unlock_irqrestore(&par->lock, flags);
}
/**
* pixel - background or foreground pixel palette index for given coordinates
* @image: image to sample pixel from
* @x: x coordinate, relative to the top left corner
* @y: y coordinate, relative to the top left corner
* @info: frame buffer info object
*
* The background palette index is given for coordinates outside of the image.
*
* Return: background or foreground palette index
*/
static u32 pixel(const struct fb_image * const image,
const int x, const int y, struct fb_info *info)
{
if (x < 0 || x >= image->width ||
y < 0 || y >= image->height)
return image->bg_color;
if (image->depth == 1)
return (image->data[y*((image->width + 7) >> 3) + (x >> 3)] &
(0x80 >> (x & 0x7))) ?
image->fg_color : image->bg_color;
fb_warn_once(info, "%s: Unsupported image depth %u\n",
__func__, image->depth);
return 0;
}
/**
* ps2fb_cb_texflush - flush texture buffer after palette or texture updates
* @info: frame buffer info object
*
* Before using converted data from host-to-local or local-to-local
* transmissions as texture or colour lookup tables for the first time, the
* texture buffer must be disabled with the TEXFLUSH register.
*
* The register write waits for the completion of the current drawing process,
* and then disables the texture data read to the texture page buffer. Any
* data can be written to it. A drawing process succeeding the write to the
* register is started after the texture page buffer is disabled.
*/
static void ps2fb_cb_texflush(struct fb_info *info)
{
struct ps2fb_par *par = info->par;
union package * const base_package = par->package.buffer;
union package *package = base_package;
unsigned long flags;
if (info->state != FBINFO_STATE_RUNNING)
return;
spin_lock_irqsave(&par->lock, flags);
if (!gif_wait())
goto timeout;
GIF_PACKAGE_TAG(package) {
.flg = gif_packed_mode,
.reg0 = gif_reg_ad,
.nreg = 1,
.nloop = 1
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_texflush
};
gif_write(&base_package->gif, package - base_package);
timeout:
spin_unlock_irqrestore(&par->lock, flags);
}
/**
* package_psmt4_texture - package PSMT4 texture tags and data for the GIF
* @package: DMA buffer to put packages in
* @image: image to copy
* @info: frame buffer info object
*
* Return: number of generated GIF packages in 16-byte unit
*/
static size_t package_psmt4_texture(union package *package,
const struct fb_image *image, struct fb_info *info)
{
union package * const base_package = package;
const u32 width2 = texture_least_power_of_2(image->width);
const u32 height2 = texture_least_power_of_2(image->height);
const u32 texels_per_quadword = 32; /* PSMT4 are 4 bit texels */
const u32 nloop = (width2 * height2 + texels_per_quadword - 1) /
texels_per_quadword;
u32 x, y;
GIF_PACKAGE_TAG(package) {
.flg = gif_image_mode,
.nloop = nloop,
.eop = 1
};
for (y = 0; y < height2; y++)
for (x = 0; x < width2; x += 2) {
const int p0 = pixel(image, x + 0, y, info);
const int p1 = pixel(image, x + 1, y, info);
const int i = 4*y + x/2;
package[i/16].gif.image[i%16] =
(p1 ? 0x10 : 0) | (p0 ? 0x01 : 0);
}
package += nloop;
return package - base_package;
}
/**
* write_cb_tile - write console buffer tile to the GIF
* @tile_index: index of the tile, starting from zero for the first glyph
* @image: the image of the tile to write
* @info: frame buffer info object
*/
static void write_cb_tile(const int tile_index,
const struct fb_image *image, struct fb_info *info)
{
struct ps2fb_par *par = info->par;
const struct tile_texture tt = texture_for_tile(tile_index, info);
union package * const base_package = par->package.buffer;
union package *package = base_package;
if (!gif_wait())
return;
GIF_PACKAGE_TAG(package) {
.flg = gif_packed_mode,
.reg0 = gif_reg_ad,
.nreg = 1,
.nloop = 4
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_bitbltbuf,
.data.bitbltbuf = {
.dpsm = gs_psm_t4,
.dbw = GS_PSMT4_BLOCK_WIDTH / 64,
.dbp = tt.tbp
}
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_trxpos,
.data.trxpos = {
.dsax = tt.u,
.dsay = tt.v
}
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_trxreg,
.data.trxreg = {
.rrw = texture_least_power_of_2(image->width),
.rrh = texture_least_power_of_2(image->height)
}
};
GIF_PACKAGE_AD(package) {
.addr = gs_addr_trxdir,
.data.trxdir = { .xdir = gs_trxdir_host_to_local }
};
package += package_psmt4_texture(package, image, info);
gif_write(&base_package->gif, package - base_package);
}
/**
* ps2fb_cb_settile - set console buffer font tiles
* @info: frame buffer info object
* @map: font map use as tiles
*/
static void ps2fb_cb_settile(struct fb_info *info, struct fb_tilemap *map)
{
const u32 glyph_size = ALIGN(map->width, 8) * map->height / 8;
struct ps2fb_par *par = info->par;
const u8 *font = map->data;
int i;
if (!font)
return; /* FIXME: Why is fb_settile called with a NULL font? */
if (info->state != FBINFO_STATE_RUNNING)
return;
if (map->width > GS_PSMT4_BLOCK_WIDTH ||
map->height > GS_PSMT4_BLOCK_HEIGHT ||
map->depth != 1) {
fb_err(info, "Unsupported font parameters: "
"width %d height %d depth %d length %d\n",
map->width, map->height, map->depth, map->length);
return;
}
par->cb.tile = cb_tile(map->width, map->height);
for (i = 0; i < map->length; i++) {
const struct fb_image image = {
.width = map->width,
.height = map->height,
.fg_color = 1,
.bg_color = 0,