Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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 */
29static 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 */
35struct 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 */
42static 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 */
48static unsigned long fb_pattern_get_reverse(struct fb_pattern *pattern)
49{
50 return swab_long(pattern->pixels);
51}
52
53/* next static pattern */
54static void fb_pattern_static(struct fb_pattern *pattern)
55{
56 /* nothing to do */
57}
58
59/* next rotating pattern */
60static 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 */
69static 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 pattern = color;
96 break;
97 }
98#ifndef __LITTLE_ENDIAN
99 pattern <<= (BITS_PER_LONG % bpp);
100 pattern |= pattern >> bpp;
101#endif
102 return pattern;
103}
104
105/* overwrite bits according to a pattern in a line */
106static __always_inline void bitfill(const struct fb_address *dst,
107 struct fb_pattern *pattern,
108 unsigned long (*get)(struct fb_pattern *pattern),
109 void (*rotate)(struct fb_pattern *pattern),
110 int end)
111{
112 unsigned long first, last;
113
114 end += dst->bits;
115 first = fb_pixel_mask(dst->bits, pattern->reverse);
116 last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), pattern->reverse);
117
118 if (end <= BITS_PER_LONG) {
119 last = last ? (last & first) : first;
120 first = get(pattern);
121 if (last == ~0UL)
122 fb_write_offset(first, 0, dst);
123 else if (last)
124 fb_modify_offset(first, last, 0, dst);
125 } else {
126 int offset = first != ~0UL;
127
128 if (offset) {
129 fb_modify_offset(get(pattern), first, 0, dst);
130 rotate(pattern);
131 }
132 end /= BITS_PER_LONG;
133 for (; offset + 4 <= end; offset += 4) {
134 fb_write_offset(get(pattern), offset + 0, dst);
135 rotate(pattern);
136 fb_write_offset(get(pattern), offset + 1, dst);
137 rotate(pattern);
138 fb_write_offset(get(pattern), offset + 2, dst);
139 rotate(pattern);
140 fb_write_offset(get(pattern), offset + 3, dst);
141 rotate(pattern);
142 }
143 while (offset < end) {
144 fb_write_offset(get(pattern), offset++, dst);
145 rotate(pattern);
146 }
147
148 if (last)
149 fb_modify_offset(get(pattern), last, offset, dst);
150 }
151}
152
153/* inverts bits according to a pattern in a line */
154static __always_inline void bitinvert(const struct fb_address *dst,
155 struct fb_pattern *pattern,
156 unsigned long (*get)(struct fb_pattern *pattern),
157 void (*rotate)(struct fb_pattern *pattern),
158 int end)
159{
160 unsigned long first, last;
161 int offset;
162
163 end += dst->bits;
164 first = fb_pixel_mask(dst->bits, pattern->reverse);
165 last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), pattern->reverse);
166
167 if (end <= BITS_PER_LONG) {
168 offset = 0;
169 last = last ? (last & first) : first;
170 } else {
171 offset = first != ~0UL;
172
173 if (offset) {
174 first &= get(pattern);
175 if (first)
176 fb_invert_offset(first, 0, dst);
177 rotate(pattern);
178 }
179
180 end /= BITS_PER_LONG;
181 for (; offset + 4 <= end; offset += 4) {
182 fb_invert_offset(get(pattern), offset + 0, dst);
183 rotate(pattern);
184 fb_invert_offset(get(pattern), offset + 1, dst);
185 rotate(pattern);
186 fb_invert_offset(get(pattern), offset + 2, dst);
187 rotate(pattern);
188 fb_invert_offset(get(pattern), offset + 3, dst);
189 rotate(pattern);
190 }
191 while (offset < end) {
192 fb_invert_offset(get(pattern), offset++, dst);
193 rotate(pattern);
194 }
195 }
196
197 last &= get(pattern);
198 if (last)
199 fb_invert_offset(last, offset, dst);
200}
201
202/* pattern doesn't change. 1, 2, 4, 8, 16, 32, 64 bpp */
203static inline void fb_fillrect_static(const struct fb_fillrect *rect, int bpp,
204 struct fb_address *dst, struct fb_pattern *pattern,
205 unsigned int bits_per_line)
206{
207 u32 height = rect->height;
208 int width = rect->width * bpp;
209
210 if (bpp > 8 && pattern->reverse.byte)
211 pattern->pixels = swab_long(pattern->pixels);
212
213 if (rect->rop == ROP_XOR)
214 while (height--) {
215 bitinvert(dst, pattern, fb_pattern_get, fb_pattern_static, width);
216 fb_address_forward(dst, bits_per_line);
217 }
218 else
219 while (height--) {
220 bitfill(dst, pattern, fb_pattern_get, fb_pattern_static, width);
221 fb_address_forward(dst, bits_per_line);
222 }
223}
224
225/* rotate pattern to the correct position */
226static inline unsigned long fb_rotate(unsigned long pattern, int shift, int bpp)
227{
228 shift %= bpp;
229 return fb_right(pattern, shift) | fb_left(pattern, bpp - shift);
230}
231
232/* rotating pattern, for example 24 bpp */
233static __always_inline void fb_fillrect_rotating(const struct fb_fillrect *rect,
234 int bpp, struct fb_address *dst,
235 struct fb_pattern *pattern,
236 unsigned long (*get)(struct fb_pattern *pattern),
237 unsigned int bits_per_line)
238{
239 unsigned long pat = pattern->pixels;
240 u32 height = rect->height;
241 int width = rect->width * bpp;
242
243 if (rect->rop == ROP_XOR)
244 while (height--) {
245 pattern->pixels = fb_rotate(pat, dst->bits, bpp);
246 bitinvert(dst, pattern, get, fb_pattern_rotate, width);
247 fb_address_forward(dst, bits_per_line);
248 }
249 else
250 while (height--) {
251 pattern->pixels = fb_rotate(pat, dst->bits, bpp);
252 bitfill(dst, pattern, get, fb_pattern_rotate, width);
253 fb_address_forward(dst, bits_per_line);
254 }
255}
256
257static inline void fb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
258{
259 int bpp = p->var.bits_per_pixel;
260 unsigned int bits_per_line = BYTES_TO_BITS(p->fix.line_length);
261 const u32 *palette = fb_palette(p);
262 struct fb_address dst = fb_address_init(p);
263 struct fb_pattern pattern;
264
265 fb_address_forward(&dst, rect->dy * bits_per_line + rect->dx * bpp);
266
267 pattern.pixels = pixel_to_pat(bpp, palette ? palette[rect->color] : rect->color);
268 pattern.reverse = fb_reverse_init(p);
269 pattern.left = BITS_PER_LONG % bpp;
270 if (pattern.left) {
271 pattern.right = bpp - pattern.left;
272 if (pattern.reverse.byte)
273 fb_fillrect_rotating(rect, bpp, &dst, &pattern,
274 fb_pattern_get_reverse, bits_per_line);
275 else
276 fb_fillrect_rotating(rect, bpp, &dst, &pattern,
277 fb_pattern_get, bits_per_line);
278 } else
279 fb_fillrect_static(rect, bpp, &dst, &pattern, bits_per_line);
280}