1 /* SPDX-License-Identifier: GPL-2.0
2  *
3  *  Various common functions used by the framebuffer drawing code
4  *
5  *	Copyright (C)  2025 Zsolt Kajtar (soci@c64.rulez.org)
6  */
7 #ifndef _FB_DRAW_H
8 #define _FB_DRAW_H
9 
10 /* swap bytes in a long, independent of word size */
11 #define swab_long _swab_long(BITS_PER_LONG)
12 #define _swab_long(x) __swab_long(x)
13 #define __swab_long(x) swab##x
14 
15 /* move the address pointer by the number of words */
fb_address_move_long(struct fb_address * adr,int offset)16 static inline void fb_address_move_long(struct fb_address *adr, int offset)
17 {
18 	adr->address += offset * (BITS_PER_LONG / BITS_PER_BYTE);
19 }
20 
21 /* move the address pointer forward with the number of bits */
fb_address_forward(struct fb_address * adr,unsigned int offset)22 static inline void fb_address_forward(struct fb_address *adr, unsigned int offset)
23 {
24 	unsigned int bits = (unsigned int)adr->bits + offset;
25 
26 	adr->bits = bits & (BITS_PER_LONG - 1u);
27 	adr->address += (bits & ~(BITS_PER_LONG - 1u)) / BITS_PER_BYTE;
28 }
29 
30 /* move the address pointer backwards with the number of bits */
fb_address_backward(struct fb_address * adr,unsigned int offset)31 static inline void fb_address_backward(struct fb_address *adr, unsigned int offset)
32 {
33 	int bits = adr->bits - (int)offset;
34 
35 	adr->bits = bits & (BITS_PER_LONG - 1);
36 	if (bits < 0)
37 		adr->address -= (adr->bits - bits) / BITS_PER_BYTE;
38 	else
39 		adr->address += (bits - adr->bits) / BITS_PER_BYTE;
40 }
41 
42 /* compose pixels based on mask */
fb_comp(unsigned long set,unsigned long unset,unsigned long mask)43 static inline unsigned long fb_comp(unsigned long set, unsigned long unset, unsigned long mask)
44 {
45 	return ((set ^ unset) & mask) ^ unset;
46 }
47 
48 /* framebuffer read-modify-write access for replacing bits in the mask */
fb_modify_offset(unsigned long val,unsigned long mask,int offset,const struct fb_address * dst)49 static inline void fb_modify_offset(unsigned long val, unsigned long mask,
50 				    int offset, const struct fb_address *dst)
51 {
52 	fb_write_offset(fb_comp(val, fb_read_offset(offset, dst), mask), offset, dst);
53 }
54 
55 /*
56  * get current palette, if applicable for visual
57  *
58  * The pseudo color table entries (and colors) are right justified and in the
59  * same byte order as it's expected to be placed into a native ordered
60  * framebuffer memory. What that means:
61  *
62  * Expected bytes in framebuffer memory (in native order):
63  * RR GG BB RR GG BB RR GG BB ...
64  *
65  * Pseudo palette entry on little endian arch:
66  * RR | GG << 8 | BB << 16
67  *
68  * Pseudo palette entry on a big endian arch:
69  * RR << 16 | GG << 8 | BB
70  */
fb_palette(struct fb_info * info)71 static inline const u32 *fb_palette(struct fb_info *info)
72 {
73 	return (info->fix.visual == FB_VISUAL_TRUECOLOR ||
74 		info->fix.visual == FB_VISUAL_DIRECTCOLOR) ? info->pseudo_palette : NULL;
75 }
76 
77 /* move pixels right on screen when framebuffer is in native order */
fb_right(unsigned long value,int index)78 static inline unsigned long fb_right(unsigned long value, int index)
79 {
80 #ifdef __LITTLE_ENDIAN
81 	return value << index;
82 #else
83 	return value >> index;
84 #endif
85 }
86 
87 /* move pixels left on screen when framebuffer is in native order */
fb_left(unsigned long value,int index)88 static inline unsigned long fb_left(unsigned long value, int index)
89 {
90 #ifdef __LITTLE_ENDIAN
91 	return value >> index;
92 #else
93 	return value << index;
94 #endif
95 }
96 
97 /* reversal options */
98 struct fb_reverse {
99 	bool byte, pixel;
100 };
101 
102 /* reverse bits of each byte in a long */
fb_reverse_bits_long(unsigned long val)103 static inline unsigned long fb_reverse_bits_long(unsigned long val)
104 {
105 #if defined(CONFIG_HAVE_ARCH_BITREVERSE) && BITS_PER_LONG == 32
106 	return bitrev8x4(val);
107 #else
108 	val = fb_comp(val >> 1, val << 1, ~0UL / 3);
109 	val = fb_comp(val >> 2, val << 2, ~0UL / 5);
110 	return fb_comp(val >> 4, val << 4, ~0UL / 17);
111 #endif
112 }
113 
114 /* apply byte and bit reversals as necessary */
fb_reverse_long(unsigned long val,struct fb_reverse reverse)115 static inline unsigned long fb_reverse_long(unsigned long val,
116 					    struct fb_reverse reverse)
117 {
118 	if (reverse.pixel)
119 		val = fb_reverse_bits_long(val);
120 	return reverse.byte ? swab_long(val) : val;
121 }
122 
123 /* calculate a pixel mask for the given reversal */
fb_pixel_mask(int index,struct fb_reverse reverse)124 static inline unsigned long fb_pixel_mask(int index, struct fb_reverse reverse)
125 {
126 #ifdef FB_REV_PIXELS_IN_BYTE
127 	if (reverse.byte)
128 		return reverse.pixel ? fb_left(~0UL, index) : swab_long(fb_right(~0UL, index));
129 	else
130 		return reverse.pixel ? swab_long(fb_left(~0UL, index)) : fb_right(~0UL, index);
131 #else
132 	return reverse.byte ? swab_long(fb_right(~0UL, index)) : fb_right(~0UL, index);
133 #endif
134 }
135 
136 
137 /*
138  * initialise reversals based on info
139  *
140  * Normally the first byte is the low byte on little endian and in the high
141  * on big endian. If it's the other way around then that's reverse byte order.
142  *
143  * Normally the first pixel is the LSB on little endian and the MSB on big
144  * endian. If that's not the case that's reverse pixel order.
145  */
fb_reverse_init(struct fb_info * info)146 static inline struct fb_reverse fb_reverse_init(struct fb_info *info)
147 {
148 	struct fb_reverse reverse;
149 #ifdef __LITTLE_ENDIAN
150 	reverse.byte = fb_be_math(info) != 0;
151 #else
152 	reverse.byte = fb_be_math(info) == 0;
153 #endif
154 #ifdef FB_REV_PIXELS_IN_BYTE
155 	reverse.pixel = info->var.bits_per_pixel < BITS_PER_BYTE
156 		&& (info->var.nonstd & FB_NONSTD_REV_PIX_IN_B);
157 #else
158 	reverse.pixel = false;
159 #endif
160 	return reverse;
161 }
162 
163 #endif /* FB_DRAW_H */
164