File Index
Files and directories
Sign in
FILE

fb_fillrect.h

File preview with syntax highlighting.

linux-6.19.14/drivers/video/fbdev/core/fb_fillrect.h C
  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		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 */
105static __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 */
153static __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 */
202static 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 */
225static 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 */
232static __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
256static 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}
~/ Read-only file preview.