xref: /linux/drivers/video/fbdev/core/fb_fillrect.h (revision 06a88f47990974f1322c2bf2e8c5125f8a2f69fe)
1 /* SPDX-License-Identifier: GPL-2.0-only
2  *
3  *  Generic bit area filler and twister engine for packed pixel framebuffers
4  *
5  *	Rewritten by:
6  *	Copyright (C)  2025 Zsolt Kajtar (soci@c64.rulez.org)
7  *
8  *	Based on earlier work of:
9  *	Copyright (C)  2000 James Simmons (jsimmons@linux-fbdev.org)
10  *	Michal Januszewski <spock@gentoo.org>
11  *	Anton Vorontsov <avorontsov@ru.mvista.com>
12  *	Pavel Pisa <pisa@cmp.felk.cvut.cz>
13  *	Antonino A. Daplas <adaplas@gmail.com>
14  *	Geert Uytterhoeven
15  *	and others
16  *
17  * NOTES:
18  *
19  * Handles native and foreign byte order on both endians, standard and
20  * reverse pixel order in a byte (<8 BPP), word length of 32/64 bits,
21  * bits per pixel from 1 to the word length. Handles line lengths at byte
22  * granularity while maintaining aligned accesses.
23  *
24  * Optimized path for power of two bits per pixel modes.
25  */
26 #include "fb_draw.h"
27 
28 /* inverts bits at a given offset */
fb_invert_offset(unsigned long pat,int offset,const struct fb_address * dst)29 static inline void fb_invert_offset(unsigned long pat, int offset, const struct fb_address *dst)
30 {
31 	fb_write_offset(fb_read_offset(offset, dst) ^ pat, offset, dst);
32 }
33 
34 /* state for pattern generator and whether swapping is necessary */
35 struct fb_pattern {
36 	unsigned long pixels;
37 	int left, right;
38 	struct fb_reverse reverse;
39 };
40 
41 /* used to get the pattern in native order */
fb_pattern_get(struct fb_pattern * pattern)42 static unsigned long fb_pattern_get(struct fb_pattern *pattern)
43 {
44 	return pattern->pixels;
45 }
46 
47 /* used to get the pattern in reverse order */
fb_pattern_get_reverse(struct fb_pattern * pattern)48 static unsigned long fb_pattern_get_reverse(struct fb_pattern *pattern)
49 {
50 	return swab_long(pattern->pixels);
51 }
52 
53 /* next static pattern */
fb_pattern_static(struct fb_pattern * pattern)54 static void fb_pattern_static(struct fb_pattern *pattern)
55 {
56 	/* nothing to do */
57 }
58 
59 /* next rotating pattern */
fb_pattern_rotate(struct fb_pattern * pattern)60 static void fb_pattern_rotate(struct fb_pattern *pattern)
61 {
62 	pattern->pixels = fb_left(pattern->pixels, pattern->left)
63 		| fb_right(pattern->pixels, pattern->right);
64 }
65 
66 #define FB_PAT(i) (((1UL<<(BITS_PER_LONG-1)/(i)*(i))/((1<<(i))-1)<<(i))|1)
67 
68 /* create the filling pattern from a given color */
pixel_to_pat(int bpp,u32 color)69 static unsigned long pixel_to_pat(int bpp, u32 color)
70 {
71 	static const unsigned long mulconst[BITS_PER_LONG/4] = {
72 		0, ~0UL, FB_PAT(2), FB_PAT(3),
73 		FB_PAT(4), FB_PAT(5), FB_PAT(6), FB_PAT(7),
74 #if BITS_PER_LONG == 64
75 		FB_PAT(8), FB_PAT(9), FB_PAT(10), FB_PAT(11),
76 		FB_PAT(12), FB_PAT(13), FB_PAT(14), FB_PAT(15),
77 #endif
78 	};
79 	unsigned long pattern;
80 
81 	switch (bpp) {
82 	case 0 ... BITS_PER_LONG/4-1:
83 		pattern = mulconst[bpp] * color;
84 		break;
85 	case BITS_PER_LONG/4 ... BITS_PER_LONG/2-1:
86 		pattern = color;
87 		pattern = pattern | pattern << bpp;
88 		pattern = pattern | pattern << bpp*2;
89 		break;
90 	case BITS_PER_LONG/2 ... BITS_PER_LONG-1:
91 		pattern = color;
92 		pattern = pattern | pattern << bpp;
93 		break;
94 	default:
95 		return color;
96 	}
97 #ifndef __LITTLE_ENDIAN
98 	pattern <<= (BITS_PER_LONG % bpp);
99 	pattern |= pattern >> bpp;
100 #endif
101 	return pattern;
102 }
103 
104 /* overwrite bits according to a pattern in a line */
bitfill(const struct fb_address * dst,struct fb_pattern * pattern,unsigned long (* get)(struct fb_pattern * pattern),void (* rotate)(struct fb_pattern * pattern),int end)105 static __always_inline void bitfill(const struct fb_address *dst,
106 				    struct fb_pattern *pattern,
107 				    unsigned long (*get)(struct fb_pattern *pattern),
108 				    void (*rotate)(struct fb_pattern *pattern),
109 				    int end)
110 {
111 	unsigned long first, last;
112 
113 	end += dst->bits;
114 	first = fb_pixel_mask(dst->bits, pattern->reverse);
115 	last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), pattern->reverse);
116 
117 	if (end <= BITS_PER_LONG) {
118 		last = last ? (last & first) : first;
119 		first = get(pattern);
120 		if (last == ~0UL)
121 			fb_write_offset(first, 0, dst);
122 		else if (last)
123 			fb_modify_offset(first, last, 0, dst);
124 	} else {
125 		int offset = first != ~0UL;
126 
127 		if (offset) {
128 			fb_modify_offset(get(pattern), first, 0, dst);
129 			rotate(pattern);
130 		}
131 		end /= BITS_PER_LONG;
132 		for (; offset + 4 <= end; offset += 4) {
133 			fb_write_offset(get(pattern), offset + 0, dst);
134 			rotate(pattern);
135 			fb_write_offset(get(pattern), offset + 1, dst);
136 			rotate(pattern);
137 			fb_write_offset(get(pattern), offset + 2, dst);
138 			rotate(pattern);
139 			fb_write_offset(get(pattern), offset + 3, dst);
140 			rotate(pattern);
141 		}
142 		while (offset < end) {
143 			fb_write_offset(get(pattern), offset++, dst);
144 			rotate(pattern);
145 		}
146 
147 		if (last)
148 			fb_modify_offset(get(pattern), last, offset, dst);
149 	}
150 }
151 
152 /* inverts bits according to a pattern in a line */
bitinvert(const struct fb_address * dst,struct fb_pattern * pattern,unsigned long (* get)(struct fb_pattern * pattern),void (* rotate)(struct fb_pattern * pattern),int end)153 static __always_inline void bitinvert(const struct fb_address *dst,
154 				      struct fb_pattern *pattern,
155 				      unsigned long (*get)(struct fb_pattern *pattern),
156 				      void (*rotate)(struct fb_pattern *pattern),
157 				      int end)
158 {
159 	unsigned long first, last;
160 	int offset;
161 
162 	end += dst->bits;
163 	first = fb_pixel_mask(dst->bits, pattern->reverse);
164 	last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), pattern->reverse);
165 
166 	if (end <= BITS_PER_LONG) {
167 		offset = 0;
168 		last = last ? (last & first) : first;
169 	} else {
170 		offset = first != ~0UL;
171 
172 		if (offset) {
173 			first &= get(pattern);
174 			if (first)
175 				fb_invert_offset(first, 0, dst);
176 			rotate(pattern);
177 		}
178 
179 		end /= BITS_PER_LONG;
180 		for (; offset + 4 <= end; offset += 4) {
181 			fb_invert_offset(get(pattern), offset + 0, dst);
182 			rotate(pattern);
183 			fb_invert_offset(get(pattern), offset + 1, dst);
184 			rotate(pattern);
185 			fb_invert_offset(get(pattern), offset + 2, dst);
186 			rotate(pattern);
187 			fb_invert_offset(get(pattern), offset + 3, dst);
188 			rotate(pattern);
189 		}
190 		while (offset < end) {
191 			fb_invert_offset(get(pattern), offset++, dst);
192 			rotate(pattern);
193 		}
194 	}
195 
196 	last &= get(pattern);
197 	if (last)
198 		fb_invert_offset(last, offset, dst);
199 }
200 
201 /* pattern doesn't change. 1, 2, 4, 8, 16, 32, 64 bpp */
fb_fillrect_static(const struct fb_fillrect * rect,int bpp,struct fb_address * dst,struct fb_pattern * pattern,unsigned int bits_per_line)202 static inline void fb_fillrect_static(const struct fb_fillrect *rect, int bpp,
203 				      struct fb_address *dst, struct fb_pattern *pattern,
204 				      unsigned int bits_per_line)
205 {
206 	u32 height = rect->height;
207 	int width = rect->width * bpp;
208 
209 	if (bpp > 8 && pattern->reverse.byte)
210 		pattern->pixels = swab_long(pattern->pixels);
211 
212 	if (rect->rop == ROP_XOR)
213 		while (height--) {
214 			bitinvert(dst, pattern, fb_pattern_get, fb_pattern_static, width);
215 			fb_address_forward(dst, bits_per_line);
216 		}
217 	else
218 		while (height--) {
219 			bitfill(dst, pattern, fb_pattern_get, fb_pattern_static, width);
220 			fb_address_forward(dst, bits_per_line);
221 		}
222 }
223 
224 /* rotate pattern to the correct position */
fb_rotate(unsigned long pattern,int shift,int bpp)225 static inline unsigned long fb_rotate(unsigned long pattern, int shift, int bpp)
226 {
227 	shift %= bpp;
228 	return fb_right(pattern, shift) | fb_left(pattern, bpp - shift);
229 }
230 
231 /* rotating pattern, for example 24 bpp */
fb_fillrect_rotating(const struct fb_fillrect * rect,int bpp,struct fb_address * dst,struct fb_pattern * pattern,unsigned long (* get)(struct fb_pattern * pattern),unsigned int bits_per_line)232 static __always_inline void fb_fillrect_rotating(const struct fb_fillrect *rect,
233 						 int bpp, struct fb_address *dst,
234 						 struct fb_pattern *pattern,
235 						 unsigned long (*get)(struct fb_pattern *pattern),
236 						 unsigned int bits_per_line)
237 {
238 	unsigned long pat = pattern->pixels;
239 	u32 height = rect->height;
240 	int width = rect->width * bpp;
241 
242 	if (rect->rop == ROP_XOR)
243 		while (height--) {
244 			pattern->pixels = fb_rotate(pat, dst->bits, bpp);
245 			bitinvert(dst, pattern, get, fb_pattern_rotate, width);
246 			fb_address_forward(dst, bits_per_line);
247 		}
248 	else
249 		while (height--) {
250 			pattern->pixels = fb_rotate(pat, dst->bits, bpp);
251 			bitfill(dst, pattern, get, fb_pattern_rotate, width);
252 			fb_address_forward(dst, bits_per_line);
253 		}
254 }
255 
fb_fillrect(struct fb_info * p,const struct fb_fillrect * rect)256 static inline void fb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
257 {
258 	int bpp = p->var.bits_per_pixel;
259 	unsigned int bits_per_line = BYTES_TO_BITS(p->fix.line_length);
260 	const u32 *palette = fb_palette(p);
261 	struct fb_address dst = fb_address_init(p);
262 	struct fb_pattern pattern;
263 
264 	fb_address_forward(&dst, rect->dy * bits_per_line + rect->dx * bpp);
265 
266 	pattern.pixels = pixel_to_pat(bpp, palette ? palette[rect->color] : rect->color);
267 	pattern.reverse = fb_reverse_init(p);
268 	pattern.left = BITS_PER_LONG % bpp;
269 	if (pattern.left) {
270 		pattern.right = bpp - pattern.left;
271 		if (pattern.reverse.byte)
272 			fb_fillrect_rotating(rect, bpp, &dst, &pattern,
273 					     fb_pattern_get_reverse, bits_per_line);
274 		else
275 			fb_fillrect_rotating(rect, bpp, &dst, &pattern,
276 					     fb_pattern_get, bits_per_line);
277 	} else
278 		fb_fillrect_static(rect, bpp, &dst, &pattern, bits_per_line);
279 }
280