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 */ 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 */ 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 */ 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 */ 54 static void fb_pattern_static(struct fb_pattern *pattern) 55 { 56 /* nothing to do */ 57 } 58 59 /* next rotating 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 */ 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 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 */ 106 static __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 */ 154 static __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 */ 203 static 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 */ 226 static 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 */ 233 static __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 257 static 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 } 281