Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fbdev: Refactoring the fbcon packed pixel drawing routines

The original version duplicated more or less the same algorithms for
both system and i/o memory.

In this version the drawing algorithms (copy/fill/blit) are separate
from the memory access (system and i/o). The two parts are getting
combined in the loadable module sources. This also makes it more robust
against wrong memory access type or alignment mistakes as there's no
direct pointer access or arithmetic in the algorithm sources anymore.

Due to liberal use of inlining the compiled result is a single function
in all 6 cases, without unnecessary function calls. Unlike earlier the
use of macros could be minimized as apparently both gcc and clang is
capable now to do the same with inline functions just as well.

What wasn't quite the same in the two variants is the support for pixel
order reversing. This version is capable to do that for both system and
I/O memory, and not only for the latter. As demand for low bits per
pixel modes isn't high there's a configuration option to enable this
separately for the CFB and SYS modules.

The pixel reversing algorithm is different than earlier and was designed
so that it can take advantage of bit order reversing instructions on
architectures which have them. And even for higher bits per pixel modes
like four bpp.

One of the shortcomings of the earlier version was the incomplete
support for foreign endian framebuffers. Now all three drawing
algorithms produce correct output on both endians with native and
foreign framebuffers. This is one of the important differences even if
otherwise the algorithms don't look too different than before.

All three routines work now with aligned native word accesses. As a
consequence blitting isn't limited to 32 bits on 64 bit architectures as
it was before.

The old routines silently assumed that rows are a multiple of the word
size. Due to how the new routines function this isn't a requirement any
more and access will be done aligned regardless. However if the
framebuffer is configured like that then some of the fast paths won't be
available.

As this code is supposed to be running on all supported architectures it
wasn't optimized for a particular one. That doesn't mean I haven't
looked at the disassembly. That's where I noticed that it isn't a good
idea to use the fallback bitreversing code for example.

The low bits per pixel modes should be faster than before as the new
routines can blit 4 pixels at a time.

On the higher bits per pixel modes I retained the specialized aligned
routines so it should be more or less the same, except on 64 bit
architectures. There the blitting word size is double now which means 32
BPP isn't done a single pixel a time now.

The code was tested on x86, amd64, mips32 and mips64. The latter two in
big endian configuration. Originally thought I can get away with the
first two, but with such bit twisting code byte ordering is tricky and
not really possible to get right without actually verifying it.

While writing such routines isn't rocket science a lot of time was spent
on making sure that pixel ordering, foreign byte order, various bits per
pixels, cpu endianness and word size will give the expected result in
all sorts of combinations without making it overly complicated or full
with special cases.

Signed-off-by: Zsolt Kajtar <soci@c64.rulez.org>
Signed-off-by: Helge Deller <deller@gmx.de>

authored by

Zsolt Kajtar and committed by
Helge Deller
eabb0329 1a78d9a3

+1472 -2263
+9 -1
drivers/video/fbdev/core/Kconfig
··· 69 69 bool 70 70 depends on FB_CORE 71 71 help 72 - Allow generic frame-buffer functions to work on displays with 1, 2 72 + Allow I/O memory frame-buffer functions to work on displays with 1, 2 73 73 and 4 bits per pixel depths which has opposite order of pixels in 74 74 byte order to bytes in long order. 75 75 ··· 96 96 Include the sys_imageblit function for generic software image 97 97 blitting. This is used by drivers that don't provide their own 98 98 (accelerated) version and the framebuffer is in system RAM. 99 + 100 + config FB_SYS_REV_PIXELS_IN_BYTE 101 + bool 102 + depends on FB_CORE 103 + help 104 + Allow virtual memory frame-buffer functions to work on displays with 1, 2 105 + and 4 bits per pixel depths which has opposite order of pixels in 106 + byte order to bytes in long order. 99 107 100 108 config FB_PROVIDE_GET_FB_UNMAPPED_AREA 101 109 bool
+11 -417
drivers/video/fbdev/core/cfbcopyarea.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 1 2 /* 2 - * Generic function for frame buffer with packed pixels of any depth. 3 - * 4 - * Copyright (C) 1999-2005 James Simmons <jsimmons@www.infradead.org> 5 - * 6 - * This file is subject to the terms and conditions of the GNU General Public 7 - * License. See the file COPYING in the main directory of this archive for 8 - * more details. 9 - * 10 - * NOTES: 11 - * 12 - * This is for cfb packed pixels. Iplan and such are incorporated in the 13 - * drivers that need them. 14 - * 15 - * FIXME 16 - * 17 - * Also need to add code to deal with cards endians that are different than 18 - * the native cpu endians. I also need to deal with MSB position in the word. 19 - * 20 - * The two functions or copying forward and backward could be split up like 21 - * the ones for filling, i.e. in aligned and unaligned versions. This would 22 - * help moving some redundant computations and branches out of the loop, too. 3 + * Copyright (C) 2025 Zsolt Kajtar (soci@c64.rulez.org) 23 4 */ 24 - 25 5 #include <linux/module.h> 26 - #include <linux/kernel.h> 27 - #include <linux/string.h> 28 6 #include <linux/fb.h> 7 + #include <linux/bitrev.h> 29 8 #include <asm/types.h> 30 - #include <asm/io.h> 31 - #include "fb_draw.h" 32 9 33 - #if BITS_PER_LONG == 32 34 - # define FB_WRITEL fb_writel 35 - # define FB_READL fb_readl 36 - #else 37 - # define FB_WRITEL fb_writeq 38 - # define FB_READL fb_readq 10 + #ifdef CONFIG_FB_CFB_REV_PIXELS_IN_BYTE 11 + #define FB_REV_PIXELS_IN_BYTE 39 12 #endif 40 13 41 - /* 42 - * Generic bitwise copy algorithm 43 - */ 44 - 45 - static void 46 - bitcpy(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx, 47 - const unsigned long __iomem *src, unsigned src_idx, int bits, 48 - unsigned n, u32 bswapmask) 49 - { 50 - unsigned long first, last; 51 - int const shift = dst_idx-src_idx; 52 - 53 - #if 0 54 - /* 55 - * If you suspect bug in this function, compare it with this simple 56 - * memmove implementation. 57 - */ 58 - memmove((char *)dst + ((dst_idx & (bits - 1))) / 8, 59 - (char *)src + ((src_idx & (bits - 1))) / 8, n / 8); 60 - return; 61 - #endif 62 - 63 - first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask); 64 - last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask); 65 - 66 - if (!shift) { 67 - // Same alignment for source and dest 68 - 69 - if (dst_idx+n <= bits) { 70 - // Single word 71 - if (last) 72 - first &= last; 73 - FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst); 74 - } else { 75 - // Multiple destination words 76 - 77 - // Leading bits 78 - if (first != ~0UL) { 79 - FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst); 80 - dst++; 81 - src++; 82 - n -= bits - dst_idx; 83 - } 84 - 85 - // Main chunk 86 - n /= bits; 87 - while (n >= 8) { 88 - FB_WRITEL(FB_READL(src++), dst++); 89 - FB_WRITEL(FB_READL(src++), dst++); 90 - FB_WRITEL(FB_READL(src++), dst++); 91 - FB_WRITEL(FB_READL(src++), dst++); 92 - FB_WRITEL(FB_READL(src++), dst++); 93 - FB_WRITEL(FB_READL(src++), dst++); 94 - FB_WRITEL(FB_READL(src++), dst++); 95 - FB_WRITEL(FB_READL(src++), dst++); 96 - n -= 8; 97 - } 98 - while (n--) 99 - FB_WRITEL(FB_READL(src++), dst++); 100 - 101 - // Trailing bits 102 - if (last) 103 - FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst); 104 - } 105 - } else { 106 - /* Different alignment for source and dest */ 107 - unsigned long d0, d1; 108 - int m; 109 - 110 - int const left = shift & (bits - 1); 111 - int const right = -shift & (bits - 1); 112 - 113 - if (dst_idx+n <= bits) { 114 - // Single destination word 115 - if (last) 116 - first &= last; 117 - d0 = FB_READL(src); 118 - d0 = fb_rev_pixels_in_long(d0, bswapmask); 119 - if (shift > 0) { 120 - // Single source word 121 - d0 <<= left; 122 - } else if (src_idx+n <= bits) { 123 - // Single source word 124 - d0 >>= right; 125 - } else { 126 - // 2 source words 127 - d1 = FB_READL(src + 1); 128 - d1 = fb_rev_pixels_in_long(d1, bswapmask); 129 - d0 = d0 >> right | d1 << left; 130 - } 131 - d0 = fb_rev_pixels_in_long(d0, bswapmask); 132 - FB_WRITEL(comp(d0, FB_READL(dst), first), dst); 133 - } else { 134 - // Multiple destination words 135 - /** We must always remember the last value read, because in case 136 - SRC and DST overlap bitwise (e.g. when moving just one pixel in 137 - 1bpp), we always collect one full long for DST and that might 138 - overlap with the current long from SRC. We store this value in 139 - 'd0'. */ 140 - d0 = FB_READL(src++); 141 - d0 = fb_rev_pixels_in_long(d0, bswapmask); 142 - // Leading bits 143 - if (shift > 0) { 144 - // Single source word 145 - d1 = d0; 146 - d0 <<= left; 147 - n -= bits - dst_idx; 148 - } else { 149 - // 2 source words 150 - d1 = FB_READL(src++); 151 - d1 = fb_rev_pixels_in_long(d1, bswapmask); 152 - 153 - d0 = d0 >> right | d1 << left; 154 - n -= bits - dst_idx; 155 - } 156 - d0 = fb_rev_pixels_in_long(d0, bswapmask); 157 - FB_WRITEL(comp(d0, FB_READL(dst), first), dst); 158 - d0 = d1; 159 - dst++; 160 - 161 - // Main chunk 162 - m = n % bits; 163 - n /= bits; 164 - while ((n >= 4) && !bswapmask) { 165 - d1 = FB_READL(src++); 166 - FB_WRITEL(d0 >> right | d1 << left, dst++); 167 - d0 = d1; 168 - d1 = FB_READL(src++); 169 - FB_WRITEL(d0 >> right | d1 << left, dst++); 170 - d0 = d1; 171 - d1 = FB_READL(src++); 172 - FB_WRITEL(d0 >> right | d1 << left, dst++); 173 - d0 = d1; 174 - d1 = FB_READL(src++); 175 - FB_WRITEL(d0 >> right | d1 << left, dst++); 176 - d0 = d1; 177 - n -= 4; 178 - } 179 - while (n--) { 180 - d1 = FB_READL(src++); 181 - d1 = fb_rev_pixels_in_long(d1, bswapmask); 182 - d0 = d0 >> right | d1 << left; 183 - d0 = fb_rev_pixels_in_long(d0, bswapmask); 184 - FB_WRITEL(d0, dst++); 185 - d0 = d1; 186 - } 187 - 188 - // Trailing bits 189 - if (m) { 190 - if (m <= bits - right) { 191 - // Single source word 192 - d0 >>= right; 193 - } else { 194 - // 2 source words 195 - d1 = FB_READL(src); 196 - d1 = fb_rev_pixels_in_long(d1, 197 - bswapmask); 198 - d0 = d0 >> right | d1 << left; 199 - } 200 - d0 = fb_rev_pixels_in_long(d0, bswapmask); 201 - FB_WRITEL(comp(d0, FB_READL(dst), last), dst); 202 - } 203 - } 204 - } 205 - } 206 - 207 - /* 208 - * Generic bitwise copy algorithm, operating backward 209 - */ 210 - 211 - static void 212 - bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx, 213 - const unsigned long __iomem *src, unsigned src_idx, int bits, 214 - unsigned n, u32 bswapmask) 215 - { 216 - unsigned long first, last; 217 - int shift; 218 - 219 - #if 0 220 - /* 221 - * If you suspect bug in this function, compare it with this simple 222 - * memmove implementation. 223 - */ 224 - memmove((char *)dst + ((dst_idx & (bits - 1))) / 8, 225 - (char *)src + ((src_idx & (bits - 1))) / 8, n / 8); 226 - return; 227 - #endif 228 - 229 - dst += (dst_idx + n - 1) / bits; 230 - src += (src_idx + n - 1) / bits; 231 - dst_idx = (dst_idx + n - 1) % bits; 232 - src_idx = (src_idx + n - 1) % bits; 233 - 234 - shift = dst_idx-src_idx; 235 - 236 - first = ~fb_shifted_pixels_mask_long(p, (dst_idx + 1) % bits, bswapmask); 237 - last = fb_shifted_pixels_mask_long(p, (bits + dst_idx + 1 - n) % bits, bswapmask); 238 - 239 - if (!shift) { 240 - // Same alignment for source and dest 241 - 242 - if ((unsigned long)dst_idx+1 >= n) { 243 - // Single word 244 - if (first) 245 - last &= first; 246 - FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst); 247 - } else { 248 - // Multiple destination words 249 - 250 - // Leading bits 251 - if (first) { 252 - FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst); 253 - dst--; 254 - src--; 255 - n -= dst_idx+1; 256 - } 257 - 258 - // Main chunk 259 - n /= bits; 260 - while (n >= 8) { 261 - FB_WRITEL(FB_READL(src--), dst--); 262 - FB_WRITEL(FB_READL(src--), dst--); 263 - FB_WRITEL(FB_READL(src--), dst--); 264 - FB_WRITEL(FB_READL(src--), dst--); 265 - FB_WRITEL(FB_READL(src--), dst--); 266 - FB_WRITEL(FB_READL(src--), dst--); 267 - FB_WRITEL(FB_READL(src--), dst--); 268 - FB_WRITEL(FB_READL(src--), dst--); 269 - n -= 8; 270 - } 271 - while (n--) 272 - FB_WRITEL(FB_READL(src--), dst--); 273 - 274 - // Trailing bits 275 - if (last != -1UL) 276 - FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst); 277 - } 278 - } else { 279 - // Different alignment for source and dest 280 - unsigned long d0, d1; 281 - int m; 282 - 283 - int const left = shift & (bits-1); 284 - int const right = -shift & (bits-1); 285 - 286 - if ((unsigned long)dst_idx+1 >= n) { 287 - // Single destination word 288 - if (first) 289 - last &= first; 290 - d0 = FB_READL(src); 291 - if (shift < 0) { 292 - // Single source word 293 - d0 >>= right; 294 - } else if (1+(unsigned long)src_idx >= n) { 295 - // Single source word 296 - d0 <<= left; 297 - } else { 298 - // 2 source words 299 - d1 = FB_READL(src - 1); 300 - d1 = fb_rev_pixels_in_long(d1, bswapmask); 301 - d0 = d0 << left | d1 >> right; 302 - } 303 - d0 = fb_rev_pixels_in_long(d0, bswapmask); 304 - FB_WRITEL(comp(d0, FB_READL(dst), last), dst); 305 - } else { 306 - // Multiple destination words 307 - /** We must always remember the last value read, because in case 308 - SRC and DST overlap bitwise (e.g. when moving just one pixel in 309 - 1bpp), we always collect one full long for DST and that might 310 - overlap with the current long from SRC. We store this value in 311 - 'd0'. */ 312 - 313 - d0 = FB_READL(src--); 314 - d0 = fb_rev_pixels_in_long(d0, bswapmask); 315 - // Leading bits 316 - if (shift < 0) { 317 - // Single source word 318 - d1 = d0; 319 - d0 >>= right; 320 - } else { 321 - // 2 source words 322 - d1 = FB_READL(src--); 323 - d1 = fb_rev_pixels_in_long(d1, bswapmask); 324 - d0 = d0 << left | d1 >> right; 325 - } 326 - d0 = fb_rev_pixels_in_long(d0, bswapmask); 327 - if (!first) 328 - FB_WRITEL(d0, dst); 329 - else 330 - FB_WRITEL(comp(d0, FB_READL(dst), first), dst); 331 - d0 = d1; 332 - dst--; 333 - n -= dst_idx+1; 334 - 335 - // Main chunk 336 - m = n % bits; 337 - n /= bits; 338 - while ((n >= 4) && !bswapmask) { 339 - d1 = FB_READL(src--); 340 - FB_WRITEL(d0 << left | d1 >> right, dst--); 341 - d0 = d1; 342 - d1 = FB_READL(src--); 343 - FB_WRITEL(d0 << left | d1 >> right, dst--); 344 - d0 = d1; 345 - d1 = FB_READL(src--); 346 - FB_WRITEL(d0 << left | d1 >> right, dst--); 347 - d0 = d1; 348 - d1 = FB_READL(src--); 349 - FB_WRITEL(d0 << left | d1 >> right, dst--); 350 - d0 = d1; 351 - n -= 4; 352 - } 353 - while (n--) { 354 - d1 = FB_READL(src--); 355 - d1 = fb_rev_pixels_in_long(d1, bswapmask); 356 - d0 = d0 << left | d1 >> right; 357 - d0 = fb_rev_pixels_in_long(d0, bswapmask); 358 - FB_WRITEL(d0, dst--); 359 - d0 = d1; 360 - } 361 - 362 - // Trailing bits 363 - if (m) { 364 - if (m <= bits - left) { 365 - // Single source word 366 - d0 <<= left; 367 - } else { 368 - // 2 source words 369 - d1 = FB_READL(src); 370 - d1 = fb_rev_pixels_in_long(d1, 371 - bswapmask); 372 - d0 = d0 << left | d1 >> right; 373 - } 374 - d0 = fb_rev_pixels_in_long(d0, bswapmask); 375 - FB_WRITEL(comp(d0, FB_READL(dst), last), dst); 376 - } 377 - } 378 - } 379 - } 14 + #include "cfbmem.h" 15 + #include "fb_copyarea.h" 380 16 381 17 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) 382 18 { 383 - u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy; 384 - u32 height = area->height, width = area->width; 385 - unsigned int const bits_per_line = p->fix.line_length * 8u; 386 - unsigned long __iomem *base = NULL; 387 - int bits = BITS_PER_LONG, bytes = bits >> 3; 388 - unsigned dst_idx = 0, src_idx = 0, rev_copy = 0; 389 - u32 bswapmask = fb_compute_bswapmask(p); 390 - 391 19 if (p->state != FBINFO_STATE_RUNNING) 392 20 return; 393 21 394 22 if (p->flags & FBINFO_VIRTFB) 395 - fb_warn_once(p, "Framebuffer is not in I/O address space."); 396 - 397 - /* if the beginning of the target area might overlap with the end of 398 - the source area, be have to copy the area reverse. */ 399 - if ((dy == sy && dx > sx) || (dy > sy)) { 400 - dy += height; 401 - sy += height; 402 - rev_copy = 1; 403 - } 404 - 405 - // split the base of the framebuffer into a long-aligned address and the 406 - // index of the first bit 407 - base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1)); 408 - dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1)); 409 - // add offset of source and target area 410 - dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel; 411 - src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel; 23 + fb_warn_once(p, "%s: framebuffer is not in I/O address space.\n", __func__); 412 24 413 25 if (p->fbops->fb_sync) 414 26 p->fbops->fb_sync(p); 415 27 416 - if (rev_copy) { 417 - while (height--) { 418 - dst_idx -= bits_per_line; 419 - src_idx -= bits_per_line; 420 - bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits, 421 - base + (src_idx / bits), src_idx % bits, bits, 422 - width*p->var.bits_per_pixel, bswapmask); 423 - } 424 - } else { 425 - while (height--) { 426 - bitcpy(p, base + (dst_idx / bits), dst_idx % bits, 427 - base + (src_idx / bits), src_idx % bits, bits, 428 - width*p->var.bits_per_pixel, bswapmask); 429 - dst_idx += bits_per_line; 430 - src_idx += bits_per_line; 431 - } 432 - } 28 + fb_copyarea(p, area); 433 29 } 434 - 435 30 EXPORT_SYMBOL(cfb_copyarea); 436 31 437 - MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>"); 438 - MODULE_DESCRIPTION("Generic software accelerated copyarea"); 32 + MODULE_AUTHOR("Zsolt Kajtar <soci@c64.rulez.org>"); 33 + MODULE_DESCRIPTION("I/O memory packed pixel framebuffer area copy"); 439 34 MODULE_LICENSE("GPL"); 440 -
+11 -351
drivers/video/fbdev/core/cfbfillrect.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 1 2 /* 2 - * Generic fillrect for frame buffers with packed pixels of any depth. 3 - * 4 - * Copyright (C) 2000 James Simmons (jsimmons@linux-fbdev.org) 5 - * 6 - * This file is subject to the terms and conditions of the GNU General Public 7 - * License. See the file COPYING in the main directory of this archive for 8 - * more details. 9 - * 10 - * NOTES: 11 - * 12 - * Also need to add code to deal with cards endians that are different than 13 - * the native cpu endians. I also need to deal with MSB position in the word. 14 - * 3 + * Copyright (C) 2025 Zsolt Kajtar (soci@c64.rulez.org) 15 4 */ 16 5 #include <linux/module.h> 17 - #include <linux/string.h> 18 6 #include <linux/fb.h> 7 + #include <linux/bitrev.h> 19 8 #include <asm/types.h> 20 - #include "fb_draw.h" 21 9 22 - #if BITS_PER_LONG == 32 23 - # define FB_WRITEL fb_writel 24 - # define FB_READL fb_readl 25 - #else 26 - # define FB_WRITEL fb_writeq 27 - # define FB_READL fb_readq 10 + #ifdef CONFIG_FB_CFB_REV_PIXELS_IN_BYTE 11 + #define FB_REV_PIXELS_IN_BYTE 28 12 #endif 29 13 30 - /* 31 - * Aligned pattern fill using 32/64-bit memory accesses 32 - */ 33 - 34 - static void 35 - bitfill_aligned(struct fb_info *p, unsigned long __iomem *dst, int dst_idx, 36 - unsigned long pat, unsigned n, int bits, u32 bswapmask) 37 - { 38 - unsigned long first, last; 39 - 40 - if (!n) 41 - return; 42 - 43 - first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask); 44 - last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask); 45 - 46 - if (dst_idx+n <= bits) { 47 - // Single word 48 - if (last) 49 - first &= last; 50 - FB_WRITEL(comp(pat, FB_READL(dst), first), dst); 51 - } else { 52 - // Multiple destination words 53 - 54 - // Leading bits 55 - if (first!= ~0UL) { 56 - FB_WRITEL(comp(pat, FB_READL(dst), first), dst); 57 - dst++; 58 - n -= bits - dst_idx; 59 - } 60 - 61 - // Main chunk 62 - n /= bits; 63 - while (n >= 8) { 64 - FB_WRITEL(pat, dst++); 65 - FB_WRITEL(pat, dst++); 66 - FB_WRITEL(pat, dst++); 67 - FB_WRITEL(pat, dst++); 68 - FB_WRITEL(pat, dst++); 69 - FB_WRITEL(pat, dst++); 70 - FB_WRITEL(pat, dst++); 71 - FB_WRITEL(pat, dst++); 72 - n -= 8; 73 - } 74 - while (n--) 75 - FB_WRITEL(pat, dst++); 76 - 77 - // Trailing bits 78 - if (last) 79 - FB_WRITEL(comp(pat, FB_READL(dst), last), dst); 80 - } 81 - } 82 - 83 - 84 - /* 85 - * Unaligned generic pattern fill using 32/64-bit memory accesses 86 - * The pattern must have been expanded to a full 32/64-bit value 87 - * Left/right are the appropriate shifts to convert to the pattern to be 88 - * used for the next 32/64-bit word 89 - */ 90 - 91 - static void 92 - bitfill_unaligned(struct fb_info *p, unsigned long __iomem *dst, int dst_idx, 93 - unsigned long pat, int left, int right, unsigned n, int bits) 94 - { 95 - unsigned long first, last; 96 - 97 - if (!n) 98 - return; 99 - 100 - first = FB_SHIFT_HIGH(p, ~0UL, dst_idx); 101 - last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits)); 102 - 103 - if (dst_idx+n <= bits) { 104 - // Single word 105 - if (last) 106 - first &= last; 107 - FB_WRITEL(comp(pat, FB_READL(dst), first), dst); 108 - } else { 109 - // Multiple destination words 110 - // Leading bits 111 - if (first) { 112 - FB_WRITEL(comp(pat, FB_READL(dst), first), dst); 113 - dst++; 114 - pat = pat << left | pat >> right; 115 - n -= bits - dst_idx; 116 - } 117 - 118 - // Main chunk 119 - n /= bits; 120 - while (n >= 4) { 121 - FB_WRITEL(pat, dst++); 122 - pat = pat << left | pat >> right; 123 - FB_WRITEL(pat, dst++); 124 - pat = pat << left | pat >> right; 125 - FB_WRITEL(pat, dst++); 126 - pat = pat << left | pat >> right; 127 - FB_WRITEL(pat, dst++); 128 - pat = pat << left | pat >> right; 129 - n -= 4; 130 - } 131 - while (n--) { 132 - FB_WRITEL(pat, dst++); 133 - pat = pat << left | pat >> right; 134 - } 135 - 136 - // Trailing bits 137 - if (last) 138 - FB_WRITEL(comp(pat, FB_READL(dst), last), dst); 139 - } 140 - } 141 - 142 - /* 143 - * Aligned pattern invert using 32/64-bit memory accesses 144 - */ 145 - static void 146 - bitfill_aligned_rev(struct fb_info *p, unsigned long __iomem *dst, 147 - int dst_idx, unsigned long pat, unsigned n, int bits, 148 - u32 bswapmask) 149 - { 150 - unsigned long val = pat, dat; 151 - unsigned long first, last; 152 - 153 - if (!n) 154 - return; 155 - 156 - first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask); 157 - last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask); 158 - 159 - if (dst_idx+n <= bits) { 160 - // Single word 161 - if (last) 162 - first &= last; 163 - dat = FB_READL(dst); 164 - FB_WRITEL(comp(dat ^ val, dat, first), dst); 165 - } else { 166 - // Multiple destination words 167 - // Leading bits 168 - if (first!=0UL) { 169 - dat = FB_READL(dst); 170 - FB_WRITEL(comp(dat ^ val, dat, first), dst); 171 - dst++; 172 - n -= bits - dst_idx; 173 - } 174 - 175 - // Main chunk 176 - n /= bits; 177 - while (n >= 8) { 178 - FB_WRITEL(FB_READL(dst) ^ val, dst); 179 - dst++; 180 - FB_WRITEL(FB_READL(dst) ^ val, dst); 181 - dst++; 182 - FB_WRITEL(FB_READL(dst) ^ val, dst); 183 - dst++; 184 - FB_WRITEL(FB_READL(dst) ^ val, dst); 185 - dst++; 186 - FB_WRITEL(FB_READL(dst) ^ val, dst); 187 - dst++; 188 - FB_WRITEL(FB_READL(dst) ^ val, dst); 189 - dst++; 190 - FB_WRITEL(FB_READL(dst) ^ val, dst); 191 - dst++; 192 - FB_WRITEL(FB_READL(dst) ^ val, dst); 193 - dst++; 194 - n -= 8; 195 - } 196 - while (n--) { 197 - FB_WRITEL(FB_READL(dst) ^ val, dst); 198 - dst++; 199 - } 200 - // Trailing bits 201 - if (last) { 202 - dat = FB_READL(dst); 203 - FB_WRITEL(comp(dat ^ val, dat, last), dst); 204 - } 205 - } 206 - } 207 - 208 - 209 - /* 210 - * Unaligned generic pattern invert using 32/64-bit memory accesses 211 - * The pattern must have been expanded to a full 32/64-bit value 212 - * Left/right are the appropriate shifts to convert to the pattern to be 213 - * used for the next 32/64-bit word 214 - */ 215 - 216 - static void 217 - bitfill_unaligned_rev(struct fb_info *p, unsigned long __iomem *dst, 218 - int dst_idx, unsigned long pat, int left, int right, 219 - unsigned n, int bits) 220 - { 221 - unsigned long first, last, dat; 222 - 223 - if (!n) 224 - return; 225 - 226 - first = FB_SHIFT_HIGH(p, ~0UL, dst_idx); 227 - last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits)); 228 - 229 - if (dst_idx+n <= bits) { 230 - // Single word 231 - if (last) 232 - first &= last; 233 - dat = FB_READL(dst); 234 - FB_WRITEL(comp(dat ^ pat, dat, first), dst); 235 - } else { 236 - // Multiple destination words 237 - 238 - // Leading bits 239 - if (first != 0UL) { 240 - dat = FB_READL(dst); 241 - FB_WRITEL(comp(dat ^ pat, dat, first), dst); 242 - dst++; 243 - pat = pat << left | pat >> right; 244 - n -= bits - dst_idx; 245 - } 246 - 247 - // Main chunk 248 - n /= bits; 249 - while (n >= 4) { 250 - FB_WRITEL(FB_READL(dst) ^ pat, dst); 251 - dst++; 252 - pat = pat << left | pat >> right; 253 - FB_WRITEL(FB_READL(dst) ^ pat, dst); 254 - dst++; 255 - pat = pat << left | pat >> right; 256 - FB_WRITEL(FB_READL(dst) ^ pat, dst); 257 - dst++; 258 - pat = pat << left | pat >> right; 259 - FB_WRITEL(FB_READL(dst) ^ pat, dst); 260 - dst++; 261 - pat = pat << left | pat >> right; 262 - n -= 4; 263 - } 264 - while (n--) { 265 - FB_WRITEL(FB_READL(dst) ^ pat, dst); 266 - dst++; 267 - pat = pat << left | pat >> right; 268 - } 269 - 270 - // Trailing bits 271 - if (last) { 272 - dat = FB_READL(dst); 273 - FB_WRITEL(comp(dat ^ pat, dat, last), dst); 274 - } 275 - } 276 - } 14 + #include "cfbmem.h" 15 + #include "fb_fillrect.h" 277 16 278 17 void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) 279 18 { 280 - unsigned long pat, pat2, fg; 281 - unsigned long width = rect->width, height = rect->height; 282 - int bits = BITS_PER_LONG, bytes = bits >> 3; 283 - u32 bpp = p->var.bits_per_pixel; 284 - unsigned long __iomem *dst; 285 - int dst_idx, left; 286 - 287 19 if (p->state != FBINFO_STATE_RUNNING) 288 20 return; 289 21 290 22 if (p->flags & FBINFO_VIRTFB) 291 - fb_warn_once(p, "Framebuffer is not in I/O address space."); 23 + fb_warn_once(p, "%s: framebuffer is not in I/O address space.\n", __func__); 292 24 293 - if (p->fix.visual == FB_VISUAL_TRUECOLOR || 294 - p->fix.visual == FB_VISUAL_DIRECTCOLOR ) 295 - fg = ((u32 *) (p->pseudo_palette))[rect->color]; 296 - else 297 - fg = rect->color; 298 - 299 - pat = pixel_to_pat(bpp, fg); 300 - 301 - dst = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1)); 302 - dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8; 303 - dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp; 304 - /* FIXME For now we support 1-32 bpp only */ 305 - left = bits % bpp; 306 25 if (p->fbops->fb_sync) 307 26 p->fbops->fb_sync(p); 308 - if (!left) { 309 - u32 bswapmask = fb_compute_bswapmask(p); 310 - void (*fill_op32)(struct fb_info *p, 311 - unsigned long __iomem *dst, int dst_idx, 312 - unsigned long pat, unsigned n, int bits, 313 - u32 bswapmask) = NULL; 314 27 315 - switch (rect->rop) { 316 - case ROP_XOR: 317 - fill_op32 = bitfill_aligned_rev; 318 - break; 319 - case ROP_COPY: 320 - fill_op32 = bitfill_aligned; 321 - break; 322 - default: 323 - printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n"); 324 - fill_op32 = bitfill_aligned; 325 - break; 326 - } 327 - while (height--) { 328 - dst += dst_idx >> (ffs(bits) - 1); 329 - dst_idx &= (bits - 1); 330 - fill_op32(p, dst, dst_idx, pat, width*bpp, bits, 331 - bswapmask); 332 - dst_idx += p->fix.line_length*8; 333 - } 334 - } else { 335 - int right, r; 336 - void (*fill_op)(struct fb_info *p, unsigned long __iomem *dst, 337 - int dst_idx, unsigned long pat, int left, 338 - int right, unsigned n, int bits) = NULL; 339 - #ifdef __LITTLE_ENDIAN 340 - right = left; 341 - left = bpp - right; 342 - #else 343 - right = bpp - left; 344 - #endif 345 - switch (rect->rop) { 346 - case ROP_XOR: 347 - fill_op = bitfill_unaligned_rev; 348 - break; 349 - case ROP_COPY: 350 - fill_op = bitfill_unaligned; 351 - break; 352 - default: 353 - printk(KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n"); 354 - fill_op = bitfill_unaligned; 355 - break; 356 - } 357 - while (height--) { 358 - dst += dst_idx / bits; 359 - dst_idx &= (bits - 1); 360 - r = dst_idx % bpp; 361 - /* rotate pattern to the correct start position */ 362 - pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp)); 363 - fill_op(p, dst, dst_idx, pat2, left, right, 364 - width*bpp, bits); 365 - dst_idx += p->fix.line_length*8; 366 - } 367 - } 28 + fb_fillrect(p, rect); 368 29 } 369 - 370 30 EXPORT_SYMBOL(cfb_fillrect); 371 31 372 - MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>"); 373 - MODULE_DESCRIPTION("Generic software accelerated fill rectangle"); 32 + MODULE_AUTHOR("Zsolt Kajtar <soci@c64.rulez.org>"); 33 + MODULE_DESCRIPTION("I/O memory packed pixel framebuffer area fill"); 374 34 MODULE_LICENSE("GPL");
+11 -346
drivers/video/fbdev/core/cfbimgblt.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 1 2 /* 2 - * Generic BitBLT function for frame buffer with packed pixels of any depth. 3 - * 4 - * Copyright (C) June 1999 James Simmons 5 - * 6 - * This file is subject to the terms and conditions of the GNU General Public 7 - * License. See the file COPYING in the main directory of this archive for 8 - * more details. 9 - * 10 - * NOTES: 11 - * 12 - * This function copys a image from system memory to video memory. The 13 - * image can be a bitmap where each 0 represents the background color and 14 - * each 1 represents the foreground color. Great for font handling. It can 15 - * also be a color image. This is determined by image_depth. The color image 16 - * must be laid out exactly in the same format as the framebuffer. Yes I know 17 - * their are cards with hardware that coverts images of various depths to the 18 - * framebuffer depth. But not every card has this. All images must be rounded 19 - * up to the nearest byte. For example a bitmap 12 bits wide must be two 20 - * bytes width. 21 - * 22 - * Tony: 23 - * Incorporate mask tables similar to fbcon-cfb*.c in 2.4 API. This speeds 24 - * up the code significantly. 25 - * 26 - * Code for depths not multiples of BITS_PER_LONG is still kludgy, which is 27 - * still processed a bit at a time. 28 - * 29 - * Also need to add code to deal with cards endians that are different than 30 - * the native cpu endians. I also need to deal with MSB position in the word. 3 + * Copyright (C) 2025 Zsolt Kajtar (soci@c64.rulez.org) 31 4 */ 32 5 #include <linux/module.h> 33 - #include <linux/string.h> 34 6 #include <linux/fb.h> 7 + #include <linux/bitrev.h> 35 8 #include <asm/types.h> 36 - #include "fb_draw.h" 37 9 38 - #define DEBUG 39 - 40 - #ifdef DEBUG 41 - #define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt,__func__,## args) 42 - #else 43 - #define DPRINTK(fmt, args...) 10 + #ifdef CONFIG_FB_CFB_REV_PIXELS_IN_BYTE 11 + #define FB_REV_PIXELS_IN_BYTE 44 12 #endif 45 13 46 - static const u32 cfb_tab8_be[] = { 47 - 0x00000000,0x000000ff,0x0000ff00,0x0000ffff, 48 - 0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff, 49 - 0xff000000,0xff0000ff,0xff00ff00,0xff00ffff, 50 - 0xffff0000,0xffff00ff,0xffffff00,0xffffffff 51 - }; 52 - 53 - static const u32 cfb_tab8_le[] = { 54 - 0x00000000,0xff000000,0x00ff0000,0xffff0000, 55 - 0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00, 56 - 0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff, 57 - 0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff 58 - }; 59 - 60 - static const u32 cfb_tab16_be[] = { 61 - 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff 62 - }; 63 - 64 - static const u32 cfb_tab16_le[] = { 65 - 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff 66 - }; 67 - 68 - static const u32 cfb_tab32[] = { 69 - 0x00000000, 0xffffffff 70 - }; 71 - 72 - #define FB_WRITEL fb_writel 73 - #define FB_READL fb_readl 74 - 75 - static inline void color_imageblit(const struct fb_image *image, 76 - struct fb_info *p, u8 __iomem *dst1, 77 - u32 start_index, 78 - u32 pitch_index) 79 - { 80 - /* Draw the penguin */ 81 - u32 __iomem *dst, *dst2; 82 - u32 color = 0, val, shift; 83 - int i, n, bpp = p->var.bits_per_pixel; 84 - u32 null_bits = 32 - bpp; 85 - u32 *palette = (u32 *) p->pseudo_palette; 86 - const u8 *src = image->data; 87 - u32 bswapmask = fb_compute_bswapmask(p); 88 - 89 - dst2 = (u32 __iomem *) dst1; 90 - for (i = image->height; i--; ) { 91 - n = image->width; 92 - dst = (u32 __iomem *) dst1; 93 - shift = 0; 94 - val = 0; 95 - 96 - if (start_index) { 97 - u32 start_mask = ~fb_shifted_pixels_mask_u32(p, 98 - start_index, bswapmask); 99 - val = FB_READL(dst) & start_mask; 100 - shift = start_index; 101 - } 102 - while (n--) { 103 - if (p->fix.visual == FB_VISUAL_TRUECOLOR || 104 - p->fix.visual == FB_VISUAL_DIRECTCOLOR ) 105 - color = palette[*src]; 106 - else 107 - color = *src; 108 - color <<= FB_LEFT_POS(p, bpp); 109 - val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask); 110 - if (shift >= null_bits) { 111 - FB_WRITEL(val, dst++); 112 - 113 - val = (shift == null_bits) ? 0 : 114 - FB_SHIFT_LOW(p, color, 32 - shift); 115 - } 116 - shift += bpp; 117 - shift &= (32 - 1); 118 - src++; 119 - } 120 - if (shift) { 121 - u32 end_mask = fb_shifted_pixels_mask_u32(p, shift, 122 - bswapmask); 123 - 124 - FB_WRITEL((FB_READL(dst) & end_mask) | val, dst); 125 - } 126 - dst1 += p->fix.line_length; 127 - if (pitch_index) { 128 - dst2 += p->fix.line_length; 129 - dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1)); 130 - 131 - start_index += pitch_index; 132 - start_index &= 32 - 1; 133 - } 134 - } 135 - } 136 - 137 - static inline void slow_imageblit(const struct fb_image *image, struct fb_info *p, 138 - u8 __iomem *dst1, u32 fgcolor, 139 - u32 bgcolor, 140 - u32 start_index, 141 - u32 pitch_index) 142 - { 143 - u32 shift, color = 0, bpp = p->var.bits_per_pixel; 144 - u32 __iomem *dst, *dst2; 145 - u32 val, pitch = p->fix.line_length; 146 - u32 null_bits = 32 - bpp; 147 - u32 spitch = (image->width+7)/8; 148 - const u8 *src = image->data, *s; 149 - u32 i, j, l; 150 - u32 bswapmask = fb_compute_bswapmask(p); 151 - 152 - dst2 = (u32 __iomem *) dst1; 153 - fgcolor <<= FB_LEFT_POS(p, bpp); 154 - bgcolor <<= FB_LEFT_POS(p, bpp); 155 - 156 - for (i = image->height; i--; ) { 157 - shift = val = 0; 158 - l = 8; 159 - j = image->width; 160 - dst = (u32 __iomem *) dst1; 161 - s = src; 162 - 163 - /* write leading bits */ 164 - if (start_index) { 165 - u32 start_mask = ~fb_shifted_pixels_mask_u32(p, 166 - start_index, bswapmask); 167 - val = FB_READL(dst) & start_mask; 168 - shift = start_index; 169 - } 170 - 171 - while (j--) { 172 - l--; 173 - color = (*s & (1 << l)) ? fgcolor : bgcolor; 174 - val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask); 175 - 176 - /* Did the bitshift spill bits to the next long? */ 177 - if (shift >= null_bits) { 178 - FB_WRITEL(val, dst++); 179 - val = (shift == null_bits) ? 0 : 180 - FB_SHIFT_LOW(p, color, 32 - shift); 181 - } 182 - shift += bpp; 183 - shift &= (32 - 1); 184 - if (!l) { l = 8; s++; } 185 - } 186 - 187 - /* write trailing bits */ 188 - if (shift) { 189 - u32 end_mask = fb_shifted_pixels_mask_u32(p, shift, 190 - bswapmask); 191 - 192 - FB_WRITEL((FB_READL(dst) & end_mask) | val, dst); 193 - } 194 - 195 - dst1 += pitch; 196 - src += spitch; 197 - if (pitch_index) { 198 - dst2 += pitch; 199 - dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1)); 200 - start_index += pitch_index; 201 - start_index &= 32 - 1; 202 - } 203 - 204 - } 205 - } 206 - 207 - /* 208 - * fast_imageblit - optimized monochrome color expansion 209 - * 210 - * Only if: bits_per_pixel == 8, 16, or 32 211 - * image->width is divisible by pixel/dword (ppw); 212 - * fix->line_legth is divisible by 4; 213 - * beginning and end of a scanline is dword aligned 214 - */ 215 - static inline void fast_imageblit(const struct fb_image *image, struct fb_info *p, 216 - u8 __iomem *dst1, u32 fgcolor, 217 - u32 bgcolor) 218 - { 219 - u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel; 220 - u32 ppw = 32/bpp, spitch = (image->width + 7)/8; 221 - u32 bit_mask, eorx, shift; 222 - const char *s = image->data, *src; 223 - u32 __iomem *dst; 224 - const u32 *tab = NULL; 225 - size_t tablen; 226 - u32 colortab[16]; 227 - int i, j, k; 228 - 229 - switch (bpp) { 230 - case 8: 231 - tab = fb_be_math(p) ? cfb_tab8_be : cfb_tab8_le; 232 - tablen = 16; 233 - break; 234 - case 16: 235 - tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le; 236 - tablen = 4; 237 - break; 238 - case 32: 239 - tab = cfb_tab32; 240 - tablen = 2; 241 - break; 242 - default: 243 - return; 244 - } 245 - 246 - for (i = ppw-1; i--; ) { 247 - fgx <<= bpp; 248 - bgx <<= bpp; 249 - fgx |= fgcolor; 250 - bgx |= bgcolor; 251 - } 252 - 253 - bit_mask = (1 << ppw) - 1; 254 - eorx = fgx ^ bgx; 255 - k = image->width/ppw; 256 - 257 - for (i = 0; i < tablen; ++i) 258 - colortab[i] = (tab[i] & eorx) ^ bgx; 259 - 260 - for (i = image->height; i--; ) { 261 - dst = (u32 __iomem *)dst1; 262 - shift = 8; 263 - src = s; 264 - 265 - /* 266 - * Manually unroll the per-line copying loop for better 267 - * performance. This works until we processed the last 268 - * completely filled source byte (inclusive). 269 - */ 270 - switch (ppw) { 271 - case 4: /* 8 bpp */ 272 - for (j = k; j >= 2; j -= 2, ++src) { 273 - FB_WRITEL(colortab[(*src >> 4) & bit_mask], dst++); 274 - FB_WRITEL(colortab[(*src >> 0) & bit_mask], dst++); 275 - } 276 - break; 277 - case 2: /* 16 bpp */ 278 - for (j = k; j >= 4; j -= 4, ++src) { 279 - FB_WRITEL(colortab[(*src >> 6) & bit_mask], dst++); 280 - FB_WRITEL(colortab[(*src >> 4) & bit_mask], dst++); 281 - FB_WRITEL(colortab[(*src >> 2) & bit_mask], dst++); 282 - FB_WRITEL(colortab[(*src >> 0) & bit_mask], dst++); 283 - } 284 - break; 285 - case 1: /* 32 bpp */ 286 - for (j = k; j >= 8; j -= 8, ++src) { 287 - FB_WRITEL(colortab[(*src >> 7) & bit_mask], dst++); 288 - FB_WRITEL(colortab[(*src >> 6) & bit_mask], dst++); 289 - FB_WRITEL(colortab[(*src >> 5) & bit_mask], dst++); 290 - FB_WRITEL(colortab[(*src >> 4) & bit_mask], dst++); 291 - FB_WRITEL(colortab[(*src >> 3) & bit_mask], dst++); 292 - FB_WRITEL(colortab[(*src >> 2) & bit_mask], dst++); 293 - FB_WRITEL(colortab[(*src >> 1) & bit_mask], dst++); 294 - FB_WRITEL(colortab[(*src >> 0) & bit_mask], dst++); 295 - } 296 - break; 297 - } 298 - 299 - /* 300 - * For image widths that are not a multiple of 8, there 301 - * are trailing pixels left on the current line. Print 302 - * them as well. 303 - */ 304 - for (; j--; ) { 305 - shift -= ppw; 306 - FB_WRITEL(colortab[(*src >> shift) & bit_mask], dst++); 307 - if (!shift) { 308 - shift = 8; 309 - ++src; 310 - } 311 - } 312 - 313 - dst1 += p->fix.line_length; 314 - s += spitch; 315 - } 316 - } 14 + #include "cfbmem.h" 15 + #include "fb_imageblit.h" 317 16 318 17 void cfb_imageblit(struct fb_info *p, const struct fb_image *image) 319 18 { 320 - u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0; 321 - u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel; 322 - u32 width = image->width; 323 - u32 dx = image->dx, dy = image->dy; 324 - u8 __iomem *dst1; 325 - 326 19 if (p->state != FBINFO_STATE_RUNNING) 327 20 return; 328 21 329 22 if (p->flags & FBINFO_VIRTFB) 330 - fb_warn_once(p, "Framebuffer is not in I/O address space."); 331 - 332 - bitstart = (dy * p->fix.line_length * 8) + (dx * bpp); 333 - start_index = bitstart & (32 - 1); 334 - pitch_index = (p->fix.line_length & (bpl - 1)) * 8; 335 - 336 - bitstart /= 8; 337 - bitstart &= ~(bpl - 1); 338 - dst1 = p->screen_base + bitstart; 23 + fb_warn_once(p, "%s: framebuffer is not in I/O address space.\n", __func__); 339 24 340 25 if (p->fbops->fb_sync) 341 26 p->fbops->fb_sync(p); 342 27 343 - if (image->depth == 1) { 344 - if (p->fix.visual == FB_VISUAL_TRUECOLOR || 345 - p->fix.visual == FB_VISUAL_DIRECTCOLOR) { 346 - fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color]; 347 - bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color]; 348 - } else { 349 - fgcolor = image->fg_color; 350 - bgcolor = image->bg_color; 351 - } 352 - 353 - if (32 % bpp == 0 && !start_index && !pitch_index && 354 - ((width & (32/bpp-1)) == 0) && 355 - bpp >= 8 && bpp <= 32) 356 - fast_imageblit(image, p, dst1, fgcolor, bgcolor); 357 - else 358 - slow_imageblit(image, p, dst1, fgcolor, bgcolor, 359 - start_index, pitch_index); 360 - } else 361 - color_imageblit(image, p, dst1, start_index, pitch_index); 28 + fb_imageblit(p, image); 362 29 } 363 - 364 30 EXPORT_SYMBOL(cfb_imageblit); 365 31 366 - MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>"); 367 - MODULE_DESCRIPTION("Generic software accelerated imaging drawing"); 32 + MODULE_AUTHOR("Zsolt Kajtar <soci@c64.rulez.org>"); 33 + MODULE_DESCRIPTION("I/O memory packed pixel framebuffer image draw"); 368 34 MODULE_LICENSE("GPL"); 369 -
+43
drivers/video/fbdev/core/cfbmem.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only 2 + * 3 + * I/O memory framebuffer access for drawing routines 4 + * 5 + * Copyright (C) 2025 Zsolt Kajtar (soci@c64.rulez.org) 6 + */ 7 + 8 + /* keeps track of a bit address in framebuffer memory */ 9 + struct fb_address { 10 + void __iomem *address; 11 + int bits; 12 + }; 13 + 14 + /* initialize the bit address pointer to the beginning of the frame buffer */ 15 + static inline struct fb_address fb_address_init(struct fb_info *p) 16 + { 17 + void __iomem *base = p->screen_base; 18 + struct fb_address ptr; 19 + 20 + ptr.address = PTR_ALIGN_DOWN(base, BITS_PER_LONG / BITS_PER_BYTE); 21 + ptr.bits = (base - ptr.address) * BITS_PER_BYTE; 22 + return ptr; 23 + } 24 + 25 + /* framebuffer write access */ 26 + static inline void fb_write_offset(unsigned long val, int offset, const struct fb_address *dst) 27 + { 28 + #if BITS_PER_LONG == 32 29 + fb_writel(val, dst->address + offset * (BITS_PER_LONG / BITS_PER_BYTE)); 30 + #else 31 + fb_writeq(val, dst->address + offset * (BITS_PER_LONG / BITS_PER_BYTE)); 32 + #endif 33 + } 34 + 35 + /* framebuffer read access */ 36 + static inline unsigned long fb_read_offset(int offset, const struct fb_address *src) 37 + { 38 + #if BITS_PER_LONG == 32 39 + return fb_readl(src->address + offset * (BITS_PER_LONG / BITS_PER_BYTE)); 40 + #else 41 + return fb_readq(src->address + offset * (BITS_PER_LONG / BITS_PER_BYTE)); 42 + #endif 43 + }
+405
drivers/video/fbdev/core/fb_copyarea.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only 2 + * 3 + * Generic bit area copy 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 previous work of: 9 + * Copyright (C) 1999-2005 James Simmons <jsimmons@www.infradead.org> 10 + * Anton Vorontsov <avorontsov@ru.mvista.com> 11 + * Pavel Pisa <pisa@cmp.felk.cvut.cz> 12 + * Antonino Daplas <adaplas@hotpop.com> 13 + * Geert Uytterhoeven 14 + * and others 15 + * 16 + * NOTES: 17 + * 18 + * Handles native and foreign byte order on both endians, standard and 19 + * reverse pixel order in a byte (<8 BPP), word length of 32/64 bits, 20 + * bits per pixel from 1 to the word length. Handles line lengths at byte 21 + * granularity while maintaining aligned accesses. 22 + * 23 + * Optimized routines for word aligned copying and byte aligned copying 24 + * on reverse pixel framebuffers. 25 + */ 26 + #include "fb_draw.h" 27 + 28 + /* used when no reversing is necessary */ 29 + static inline unsigned long fb_no_reverse(unsigned long val, struct fb_reverse reverse) 30 + { 31 + return val; 32 + } 33 + 34 + /* modifies the masked area in a word */ 35 + static inline void fb_copy_offset_masked(unsigned long mask, int offset, 36 + const struct fb_address *dst, 37 + const struct fb_address *src) 38 + { 39 + fb_modify_offset(fb_read_offset(offset, src), mask, offset, dst); 40 + } 41 + 42 + /* copies the whole word */ 43 + static inline void fb_copy_offset(int offset, const struct fb_address *dst, 44 + const struct fb_address *src) 45 + { 46 + fb_write_offset(fb_read_offset(offset, src), offset, dst); 47 + } 48 + 49 + /* forward aligned copy */ 50 + static inline void fb_copy_aligned_fwd(const struct fb_address *dst, 51 + const struct fb_address *src, 52 + int end, struct fb_reverse reverse) 53 + { 54 + unsigned long first, last; 55 + 56 + first = fb_pixel_mask(dst->bits, reverse); 57 + last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), reverse); 58 + 59 + /* Same alignment for source and dest */ 60 + if (end <= BITS_PER_LONG) { 61 + /* Single word */ 62 + last = last ? (last & first) : first; 63 + 64 + /* Trailing bits */ 65 + if (last == ~0UL) 66 + fb_copy_offset(0, dst, src); 67 + else 68 + fb_copy_offset_masked(last, 0, dst, src); 69 + } else { 70 + /* Multiple destination words */ 71 + int offset = first != ~0UL; 72 + 73 + /* Leading bits */ 74 + if (offset) 75 + fb_copy_offset_masked(first, 0, dst, src); 76 + 77 + /* Main chunk */ 78 + end /= BITS_PER_LONG; 79 + while (offset + 4 <= end) { 80 + fb_copy_offset(offset + 0, dst, src); 81 + fb_copy_offset(offset + 1, dst, src); 82 + fb_copy_offset(offset + 2, dst, src); 83 + fb_copy_offset(offset + 3, dst, src); 84 + offset += 4; 85 + } 86 + while (offset < end) 87 + fb_copy_offset(offset++, dst, src); 88 + 89 + /* Trailing bits */ 90 + if (last) 91 + fb_copy_offset_masked(last, offset, dst, src); 92 + } 93 + } 94 + 95 + /* reverse aligned copy */ 96 + static inline void fb_copy_aligned_rev(const struct fb_address *dst, 97 + const struct fb_address *src, 98 + int end, struct fb_reverse reverse) 99 + { 100 + unsigned long first, last; 101 + 102 + first = fb_pixel_mask(dst->bits, reverse); 103 + last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), reverse); 104 + 105 + if (end <= BITS_PER_LONG) { 106 + /* Single word */ 107 + if (last) 108 + first &= last; 109 + if (first == ~0UL) 110 + fb_copy_offset(0, dst, src); 111 + else 112 + fb_copy_offset_masked(first, 0, dst, src); 113 + } else { 114 + /* Multiple destination words */ 115 + int offset = first != ~0UL; 116 + 117 + /* Trailing bits */ 118 + end /= BITS_PER_LONG; 119 + 120 + if (last) 121 + fb_copy_offset_masked(last, end, dst, src); 122 + 123 + /* Main chunk */ 124 + while (end >= offset + 4) { 125 + fb_copy_offset(end - 1, dst, src); 126 + fb_copy_offset(end - 2, dst, src); 127 + fb_copy_offset(end - 3, dst, src); 128 + fb_copy_offset(end - 4, dst, src); 129 + end -= 4; 130 + } 131 + while (end > offset) 132 + fb_copy_offset(--end, dst, src); 133 + 134 + /* Leading bits */ 135 + if (offset) 136 + fb_copy_offset_masked(first, 0, dst, src); 137 + } 138 + } 139 + 140 + static inline void fb_copy_aligned(struct fb_address *dst, struct fb_address *src, 141 + int width, u32 height, unsigned int bits_per_line, 142 + struct fb_reverse reverse, bool rev_copy) 143 + { 144 + if (rev_copy) 145 + while (height--) { 146 + fb_copy_aligned_rev(dst, src, width + dst->bits, reverse); 147 + fb_address_backward(dst, bits_per_line); 148 + fb_address_backward(src, bits_per_line); 149 + } 150 + else 151 + while (height--) { 152 + fb_copy_aligned_fwd(dst, src, width + dst->bits, reverse); 153 + fb_address_forward(dst, bits_per_line); 154 + fb_address_forward(src, bits_per_line); 155 + } 156 + } 157 + 158 + static __always_inline void fb_copy_fwd(const struct fb_address *dst, 159 + const struct fb_address *src, int width, 160 + unsigned long (*reorder)(unsigned long val, 161 + struct fb_reverse reverse), 162 + struct fb_reverse reverse) 163 + { 164 + unsigned long first, last; 165 + unsigned long d0, d1; 166 + int end = dst->bits + width; 167 + int shift, left, right; 168 + 169 + first = fb_pixel_mask(dst->bits, reverse); 170 + last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), reverse); 171 + 172 + shift = dst->bits - src->bits; 173 + right = shift & (BITS_PER_LONG - 1); 174 + left = -shift & (BITS_PER_LONG - 1); 175 + 176 + if (end <= BITS_PER_LONG) { 177 + /* Single destination word */ 178 + last = last ? (last & first) : first; 179 + if (shift < 0) { 180 + d0 = fb_left(reorder(fb_read_offset(-1, src), reverse), left); 181 + if (src->bits + width > BITS_PER_LONG) 182 + d0 |= fb_right(reorder(fb_read_offset(0, src), reverse), right); 183 + 184 + if (last == ~0UL) 185 + fb_write_offset(reorder(d0, reverse), 0, dst); 186 + else 187 + fb_modify_offset(reorder(d0, reverse), last, 0, dst); 188 + } else { 189 + d0 = fb_right(reorder(fb_read_offset(0, src), reverse), right); 190 + fb_modify_offset(reorder(d0, reverse), last, 0, dst); 191 + } 192 + } else { 193 + /* Multiple destination words */ 194 + int offset = first != ~0UL; 195 + 196 + /* Leading bits */ 197 + if (shift < 0) 198 + d0 = reorder(fb_read_offset(-1, src), reverse); 199 + else 200 + d0 = 0; 201 + 202 + /* 2 source words */ 203 + if (offset) { 204 + d1 = reorder(fb_read_offset(0, src), reverse); 205 + d0 = fb_left(d0, left) | fb_right(d1, right); 206 + fb_modify_offset(reorder(d0, reverse), first, 0, dst); 207 + d0 = d1; 208 + } 209 + 210 + /* Main chunk */ 211 + end /= BITS_PER_LONG; 212 + if (reorder == fb_no_reverse) 213 + while (offset + 4 <= end) { 214 + d1 = fb_read_offset(offset + 0, src); 215 + d0 = fb_left(d0, left) | fb_right(d1, right); 216 + fb_write_offset(d0, offset + 0, dst); 217 + d0 = d1; 218 + d1 = fb_read_offset(offset + 1, src); 219 + d0 = fb_left(d0, left) | fb_right(d1, right); 220 + fb_write_offset(d0, offset + 1, dst); 221 + d0 = d1; 222 + d1 = fb_read_offset(offset + 2, src); 223 + d0 = fb_left(d0, left) | fb_right(d1, right); 224 + fb_write_offset(d0, offset + 2, dst); 225 + d0 = d1; 226 + d1 = fb_read_offset(offset + 3, src); 227 + d0 = fb_left(d0, left) | fb_right(d1, right); 228 + fb_write_offset(d0, offset + 3, dst); 229 + d0 = d1; 230 + offset += 4; 231 + } 232 + 233 + while (offset < end) { 234 + d1 = reorder(fb_read_offset(offset, src), reverse); 235 + d0 = fb_left(d0, left) | fb_right(d1, right); 236 + fb_write_offset(reorder(d0, reverse), offset, dst); 237 + d0 = d1; 238 + offset++; 239 + } 240 + 241 + /* Trailing bits */ 242 + if (last) { 243 + d0 = fb_left(d0, left); 244 + if (src->bits + width 245 + > offset * BITS_PER_LONG + ((shift < 0) ? BITS_PER_LONG : 0)) 246 + d0 |= fb_right(reorder(fb_read_offset(offset, src), reverse), 247 + right); 248 + fb_modify_offset(reorder(d0, reverse), last, offset, dst); 249 + } 250 + } 251 + } 252 + 253 + static __always_inline void fb_copy_rev(const struct fb_address *dst, 254 + const struct fb_address *src, int end, 255 + unsigned long (*reorder)(unsigned long val, 256 + struct fb_reverse reverse), 257 + struct fb_reverse reverse) 258 + { 259 + unsigned long first, last; 260 + unsigned long d0, d1; 261 + int shift, left, right; 262 + 263 + first = fb_pixel_mask(dst->bits, reverse); 264 + last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), reverse); 265 + 266 + shift = dst->bits - src->bits; 267 + right = shift & (BITS_PER_LONG-1); 268 + left = -shift & (BITS_PER_LONG-1); 269 + 270 + if (end <= BITS_PER_LONG) { 271 + /* Single destination word */ 272 + if (last) 273 + first &= last; 274 + 275 + if (shift > 0) { 276 + d0 = fb_right(reorder(fb_read_offset(1, src), reverse), right); 277 + if (src->bits > left) 278 + d0 |= fb_left(reorder(fb_read_offset(0, src), reverse), left); 279 + fb_modify_offset(reorder(d0, reverse), first, 0, dst); 280 + } else { 281 + d0 = fb_left(reorder(fb_read_offset(0, src), reverse), left); 282 + if (src->bits + end - dst->bits > BITS_PER_LONG) 283 + d0 |= fb_right(reorder(fb_read_offset(1, src), reverse), right); 284 + if (first == ~0UL) 285 + fb_write_offset(reorder(d0, reverse), 0, dst); 286 + else 287 + fb_modify_offset(reorder(d0, reverse), first, 0, dst); 288 + } 289 + } else { 290 + /* Multiple destination words */ 291 + int offset = first != ~0UL; 292 + 293 + end /= BITS_PER_LONG; 294 + 295 + /* 2 source words */ 296 + if (fb_right(~0UL, right) & last) 297 + d0 = fb_right(reorder(fb_read_offset(end + 1, src), reverse), right); 298 + else 299 + d0 = 0; 300 + 301 + /* Trailing bits */ 302 + d1 = reorder(fb_read_offset(end, src), reverse); 303 + if (last) 304 + fb_modify_offset(reorder(fb_left(d1, left) | d0, reverse), 305 + last, end, dst); 306 + d0 = d1; 307 + 308 + /* Main chunk */ 309 + if (reorder == fb_no_reverse) 310 + while (end >= offset + 4) { 311 + d1 = fb_read_offset(end - 1, src); 312 + d0 = fb_left(d1, left) | fb_right(d0, right); 313 + fb_write_offset(d0, end - 1, dst); 314 + d0 = d1; 315 + d1 = fb_read_offset(end - 2, src); 316 + d0 = fb_left(d1, left) | fb_right(d0, right); 317 + fb_write_offset(d0, end - 2, dst); 318 + d0 = d1; 319 + d1 = fb_read_offset(end - 3, src); 320 + d0 = fb_left(d1, left) | fb_right(d0, right); 321 + fb_write_offset(d0, end - 3, dst); 322 + d0 = d1; 323 + d1 = fb_read_offset(end - 4, src); 324 + d0 = fb_left(d1, left) | fb_right(d0, right); 325 + fb_write_offset(d0, end - 4, dst); 326 + d0 = d1; 327 + end -= 4; 328 + } 329 + 330 + while (end > offset) { 331 + end--; 332 + d1 = reorder(fb_read_offset(end, src), reverse); 333 + d0 = fb_left(d1, left) | fb_right(d0, right); 334 + fb_write_offset(reorder(d0, reverse), end, dst); 335 + d0 = d1; 336 + } 337 + 338 + /* Leading bits */ 339 + if (offset) { 340 + d0 = fb_right(d0, right); 341 + if (src->bits > left) 342 + d0 |= fb_left(reorder(fb_read_offset(0, src), reverse), left); 343 + fb_modify_offset(reorder(d0, reverse), first, 0, dst); 344 + } 345 + } 346 + } 347 + 348 + static __always_inline void fb_copy(struct fb_address *dst, struct fb_address *src, 349 + int width, u32 height, unsigned int bits_per_line, 350 + unsigned long (*reorder)(unsigned long val, 351 + struct fb_reverse reverse), 352 + struct fb_reverse reverse, bool rev_copy) 353 + { 354 + if (rev_copy) 355 + while (height--) { 356 + int move = src->bits < dst->bits ? -1 : 0; 357 + 358 + fb_address_move_long(src, move); 359 + fb_copy_rev(dst, src, width + dst->bits, reorder, reverse); 360 + fb_address_backward(dst, bits_per_line); 361 + fb_address_backward(src, bits_per_line); 362 + fb_address_move_long(src, -move); 363 + } 364 + else 365 + while (height--) { 366 + int move = src->bits > dst->bits ? 1 : 0; 367 + 368 + fb_address_move_long(src, move); 369 + fb_copy_fwd(dst, src, width, reorder, reverse); 370 + fb_address_forward(dst, bits_per_line); 371 + fb_address_forward(src, bits_per_line); 372 + fb_address_move_long(src, -move); 373 + } 374 + } 375 + 376 + static inline void fb_copyarea(struct fb_info *p, const struct fb_copyarea *area) 377 + { 378 + int bpp = p->var.bits_per_pixel; 379 + u32 dy = area->dy; 380 + u32 sy = area->sy; 381 + u32 height = area->height; 382 + int width = area->width * bpp; 383 + unsigned int bits_per_line = BYTES_TO_BITS(p->fix.line_length); 384 + struct fb_reverse reverse = fb_reverse_init(p); 385 + struct fb_address dst = fb_address_init(p); 386 + struct fb_address src = dst; 387 + bool rev_copy = (dy > sy) || (dy == sy && area->dx > area->sx); 388 + 389 + if (rev_copy) { 390 + dy += height - 1; 391 + sy += height - 1; 392 + } 393 + fb_address_forward(&dst, dy*bits_per_line + area->dx*bpp); 394 + fb_address_forward(&src, sy*bits_per_line + area->sx*bpp); 395 + 396 + if (src.bits == dst.bits) 397 + fb_copy_aligned(&dst, &src, width, height, bits_per_line, reverse, rev_copy); 398 + else if (!reverse.byte && (!reverse.pixel || 399 + !((src.bits ^ dst.bits) & (BITS_PER_BYTE-1)))) { 400 + fb_copy(&dst, &src, width, height, bits_per_line, 401 + fb_no_reverse, reverse, rev_copy); 402 + } else 403 + fb_copy(&dst, &src, width, height, bits_per_line, 404 + fb_reverse_long, reverse, rev_copy); 405 + }
+133 -157
drivers/video/fbdev/core/fb_draw.h
··· 1 - /* SPDX-License-Identifier: GPL-2.0 */ 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 + */ 2 7 #ifndef _FB_DRAW_H 3 8 #define _FB_DRAW_H 4 9 5 - #include <asm/types.h> 6 - #include <linux/fb.h> 7 - #include <linux/bug.h> 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 8 14 9 - /* 10 - * Compose two values, using a bitmask as decision value 11 - * This is equivalent to (a & mask) | (b & ~mask) 12 - */ 13 - 14 - static inline unsigned long 15 - comp(unsigned long a, unsigned long b, unsigned long mask) 15 + /* move the address pointer by the number of words */ 16 + static inline void fb_address_move_long(struct fb_address *adr, int offset) 16 17 { 17 - return ((a ^ b) & mask) ^ b; 18 + adr->address += offset * (BITS_PER_LONG / BITS_PER_BYTE); 18 19 } 19 20 20 - /* 21 - * Create a pattern with the given pixel's color 22 - */ 23 - 24 - #if BITS_PER_LONG == 64 25 - static inline unsigned long 26 - pixel_to_pat( u32 bpp, u32 pixel) 21 + /* move the address pointer forward with the number of bits */ 22 + static inline void fb_address_forward(struct fb_address *adr, unsigned int offset) 27 23 { 28 - switch (bpp) { 29 - case 1: 30 - return 0xfffffffffffffffful*pixel; 31 - case 2: 32 - return 0x5555555555555555ul*pixel; 33 - case 4: 34 - return 0x1111111111111111ul*pixel; 35 - case 8: 36 - return 0x0101010101010101ul*pixel; 37 - case 12: 38 - return 0x1001001001001001ul*pixel; 39 - case 16: 40 - return 0x0001000100010001ul*pixel; 41 - case 24: 42 - return 0x0001000001000001ul*pixel; 43 - case 32: 44 - return 0x0000000100000001ul*pixel; 45 - default: 46 - WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp); 47 - return 0; 48 - } 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; 49 28 } 29 + 30 + /* move the address pointer backwards with the number of bits */ 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 */ 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 */ 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 + */ 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 */ 78 + static inline unsigned long fb_right(unsigned long value, int index) 79 + { 80 + #ifdef __LITTLE_ENDIAN 81 + return value << index; 50 82 #else 51 - static inline unsigned long 52 - pixel_to_pat( u32 bpp, u32 pixel) 53 - { 54 - switch (bpp) { 55 - case 1: 56 - return 0xfffffffful*pixel; 57 - case 2: 58 - return 0x55555555ul*pixel; 59 - case 4: 60 - return 0x11111111ul*pixel; 61 - case 8: 62 - return 0x01010101ul*pixel; 63 - case 12: 64 - return 0x01001001ul*pixel; 65 - case 16: 66 - return 0x00010001ul*pixel; 67 - case 24: 68 - return 0x01000001ul*pixel; 69 - case 32: 70 - return 0x00000001ul*pixel; 71 - default: 72 - WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp); 73 - return 0; 74 - } 75 - } 83 + return value >> index; 76 84 #endif 85 + } 77 86 78 - #ifdef CONFIG_FB_CFB_REV_PIXELS_IN_BYTE 79 - #if BITS_PER_LONG == 64 80 - #define REV_PIXELS_MASK1 0x5555555555555555ul 81 - #define REV_PIXELS_MASK2 0x3333333333333333ul 82 - #define REV_PIXELS_MASK4 0x0f0f0f0f0f0f0f0ful 87 + /* move pixels left on screen when framebuffer is in native order */ 88 + static inline unsigned long fb_left(unsigned long value, int index) 89 + { 90 + #ifdef __LITTLE_ENDIAN 91 + return value >> index; 83 92 #else 84 - #define REV_PIXELS_MASK1 0x55555555ul 85 - #define REV_PIXELS_MASK2 0x33333333ul 86 - #define REV_PIXELS_MASK4 0x0f0f0f0ful 93 + return value << index; 87 94 #endif 88 - 89 - static inline unsigned long fb_rev_pixels_in_long(unsigned long val, 90 - u32 bswapmask) 91 - { 92 - if (bswapmask & 1) 93 - val = comp(val >> 1, val << 1, REV_PIXELS_MASK1); 94 - if (bswapmask & 2) 95 - val = comp(val >> 2, val << 2, REV_PIXELS_MASK2); 96 - if (bswapmask & 3) 97 - val = comp(val >> 4, val << 4, REV_PIXELS_MASK4); 98 - return val; 99 95 } 100 96 101 - static inline u32 fb_shifted_pixels_mask_u32(struct fb_info *p, u32 index, 102 - u32 bswapmask) 103 - { 104 - u32 mask; 97 + /* reversal options */ 98 + struct fb_reverse { 99 + bool byte, pixel; 100 + }; 105 101 106 - if (!bswapmask) { 107 - mask = FB_SHIFT_HIGH(p, ~(u32)0, index); 108 - } else { 109 - mask = 0xff << FB_LEFT_POS(p, 8); 110 - mask = FB_SHIFT_LOW(p, mask, index & (bswapmask)) & mask; 111 - mask = FB_SHIFT_HIGH(p, mask, index & ~(bswapmask)); 112 - #if defined(__i386__) || defined(__x86_64__) 113 - /* Shift argument is limited to 0 - 31 on x86 based CPU's */ 114 - if(index + bswapmask < 32) 102 + /* reverse bits of each byte in a long */ 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); 115 111 #endif 116 - mask |= FB_SHIFT_HIGH(p, ~(u32)0, 117 - (index + bswapmask) & ~(bswapmask)); 118 - } 119 - return mask; 120 112 } 121 113 122 - static inline unsigned long fb_shifted_pixels_mask_long(struct fb_info *p, 123 - u32 index, 124 - u32 bswapmask) 114 + /* apply byte and bit reversals as necessary */ 115 + static inline unsigned long fb_reverse_long(unsigned long val, 116 + struct fb_reverse reverse) 125 117 { 126 - unsigned long mask; 118 + if (reverse.pixel) 119 + val = fb_reverse_bits_long(val); 120 + return reverse.byte ? swab_long(val) : val; 121 + } 127 122 128 - if (!bswapmask) { 129 - mask = FB_SHIFT_HIGH(p, ~0UL, index); 130 - } else { 131 - mask = 0xff << FB_LEFT_POS(p, 8); 132 - mask = FB_SHIFT_LOW(p, mask, index & (bswapmask)) & mask; 133 - mask = FB_SHIFT_HIGH(p, mask, index & ~(bswapmask)); 134 - #if defined(__i386__) || defined(__x86_64__) 135 - /* Shift argument is limited to 0 - 31 on x86 based CPU's */ 136 - if(index + bswapmask < BITS_PER_LONG) 123 + /* calculate a pixel mask for the given reversal */ 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); 137 133 #endif 138 - mask |= FB_SHIFT_HIGH(p, ~0UL, 139 - (index + bswapmask) & ~(bswapmask)); 140 - } 141 - return mask; 142 134 } 143 135 144 136 145 - static inline u32 fb_compute_bswapmask(struct fb_info *info) 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 + */ 146 + static inline struct fb_reverse fb_reverse_init(struct fb_info *info) 146 147 { 147 - u32 bswapmask = 0; 148 - unsigned bpp = info->var.bits_per_pixel; 149 - 150 - if ((bpp < 8) && (info->var.nonstd & FB_NONSTD_REV_PIX_IN_B)) { 151 - /* 152 - * Reversed order of pixel layout in bytes 153 - * works only for 1, 2 and 4 bpp 154 - */ 155 - bswapmask = 7 - bpp + 1; 156 - } 157 - return bswapmask; 158 - } 159 - 160 - #else /* CONFIG_FB_CFB_REV_PIXELS_IN_BYTE */ 161 - 162 - static inline unsigned long fb_rev_pixels_in_long(unsigned long val, 163 - u32 bswapmask) 164 - { 165 - return val; 166 - } 167 - 168 - #define fb_shifted_pixels_mask_u32(p, i, b) FB_SHIFT_HIGH((p), ~(u32)0, (i)) 169 - #define fb_shifted_pixels_mask_long(p, i, b) FB_SHIFT_HIGH((p), ~0UL, (i)) 170 - #define fb_compute_bswapmask(...) 0 171 - 172 - #endif /* CONFIG_FB_CFB_REV_PIXELS_IN_BYTE */ 173 - 174 - #define cpu_to_le_long _cpu_to_le_long(BITS_PER_LONG) 175 - #define _cpu_to_le_long(x) __cpu_to_le_long(x) 176 - #define __cpu_to_le_long(x) cpu_to_le##x 177 - 178 - #define le_long_to_cpu _le_long_to_cpu(BITS_PER_LONG) 179 - #define _le_long_to_cpu(x) __le_long_to_cpu(x) 180 - #define __le_long_to_cpu(x) le##x##_to_cpu 181 - 182 - static inline unsigned long rolx(unsigned long word, unsigned int shift, unsigned int x) 183 - { 184 - return (word << shift) | (word >> (x - shift)); 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; 185 161 } 186 162 187 163 #endif /* FB_DRAW_H */
+280
drivers/video/fbdev/core/fb_fillrect.h
··· 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 + }
+495
drivers/video/fbdev/core/fb_imageblit.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only 2 + * 3 + * Generic bitmap / 8 bpp image bitstreamer for packed pixel framebuffers 4 + * 5 + * Rewritten by: 6 + * Copyright (C) 2025 Zsolt Kajtar (soci@c64.rulez.org) 7 + * 8 + * Based on previous work of: 9 + * Copyright (C) June 1999 James Simmons 10 + * Anton Vorontsov <avorontsov@ru.mvista.com> 11 + * Pavel Pisa <pisa@cmp.felk.cvut.cz> 12 + * Antonino A. Daplas <adaplas@gmail.com> 13 + * and others 14 + * 15 + * NOTES: 16 + * 17 + * Handles native and foreign byte order on both endians, standard and 18 + * reverse pixel order in a byte (<8 BPP), word length of 32/64 bits, 19 + * bits per pixel from 1 to the word length. Handles line lengths at byte 20 + * granularity while maintaining aligned accesses. 21 + * 22 + * Optimized routines for word aligned 1, 2, 4 pixel per word for high 23 + * bpp modes and 4 pixel at a time operation for low bpp. 24 + * 25 + * The color image is expected to be one byte per pixel, and values should 26 + * not exceed the bitdepth or the pseudo palette (if used). 27 + */ 28 + #include "fb_draw.h" 29 + 30 + /* bitmap image iterator, one pixel at a time */ 31 + struct fb_bitmap_iter { 32 + const u8 *data; 33 + unsigned long colors[2]; 34 + int width, i; 35 + }; 36 + 37 + static bool fb_bitmap_image(void *iterator, unsigned long *pixels, int *bits) 38 + { 39 + struct fb_bitmap_iter *iter = iterator; 40 + 41 + if (iter->i < iter->width) { 42 + int bit = ~iter->i & (BITS_PER_BYTE-1); 43 + int byte = iter->i++ / BITS_PER_BYTE; 44 + 45 + *pixels = iter->colors[(iter->data[byte] >> bit) & 1]; 46 + return true; 47 + } 48 + iter->data += BITS_TO_BYTES(iter->width); 49 + iter->i = 0; 50 + return false; 51 + } 52 + 53 + /* color image iterator, one pixel at a time */ 54 + struct fb_color_iter { 55 + const u8 *data; 56 + const u32 *palette; 57 + struct fb_reverse reverse; 58 + int shift; 59 + int width, i; 60 + }; 61 + 62 + static bool fb_color_image(void *iterator, unsigned long *pixels, int *bits) 63 + { 64 + struct fb_color_iter *iter = iterator; 65 + 66 + if (iter->i < iter->width) { 67 + unsigned long color = iter->data[iter->i++]; 68 + 69 + if (iter->palette) 70 + color = iter->palette[color]; 71 + *pixels = color << iter->shift; 72 + if (iter->reverse.pixel) 73 + *pixels = fb_reverse_bits_long(*pixels); 74 + return true; 75 + } 76 + iter->data += iter->width; 77 + iter->i = 0; 78 + return false; 79 + } 80 + 81 + /* bitmap image iterator, 4 pixels at a time */ 82 + struct fb_bitmap4x_iter { 83 + const u8 *data; 84 + u32 fgxcolor, bgcolor; 85 + int width, i; 86 + const u32 *expand; 87 + int bpp; 88 + bool top; 89 + }; 90 + 91 + static bool fb_bitmap4x_image(void *iterator, unsigned long *pixels, int *bits) 92 + { 93 + struct fb_bitmap4x_iter *iter = iterator; 94 + u8 data; 95 + 96 + if (iter->i >= BITS_PER_BYTE/2) { 97 + iter->i -= BITS_PER_BYTE/2; 98 + iter->top = !iter->top; 99 + if (iter->top) 100 + data = *iter->data++ >> BITS_PER_BYTE/2; 101 + else 102 + data = iter->data[-1] & ((1 << BITS_PER_BYTE/2)-1); 103 + } else if (iter->i != 0) { 104 + *bits = iter->bpp * iter->i; 105 + if (iter->top) 106 + data = iter->data[-1] & ((1 << BITS_PER_BYTE/2)-1); 107 + else 108 + data = *iter->data++ >> BITS_PER_BYTE/2; 109 + #ifndef __LITTLE_ENDIAN 110 + data >>= BITS_PER_BYTE/2 - iter->i; 111 + #endif 112 + iter->i = 0; 113 + } else { 114 + *bits = iter->bpp * BITS_PER_BYTE/2; 115 + iter->i = iter->width; 116 + iter->top = false; 117 + return false; 118 + } 119 + *pixels = (iter->fgxcolor & iter->expand[data]) ^ iter->bgcolor; 120 + #ifndef __LITTLE_ENDIAN 121 + *pixels <<= BITS_PER_LONG - *bits; 122 + #endif 123 + return true; 124 + } 125 + 126 + /* draw a line a group of pixels at a time */ 127 + static __always_inline void fb_bitblit(bool (*get)(void *iter, unsigned long *pixels, 128 + int *bits), 129 + void *iter, int bits, struct fb_address *dst, 130 + struct fb_reverse reverse) 131 + { 132 + unsigned long pixels, val, mask, old; 133 + int offset = 0; 134 + int shift = dst->bits; 135 + 136 + if (shift) { 137 + old = fb_read_offset(0, dst); 138 + val = fb_reverse_long(old, reverse); 139 + val &= ~fb_right(~0UL, shift); 140 + } else { 141 + old = 0; 142 + val = 0; 143 + } 144 + 145 + while (get(iter, &pixels, &bits)) { 146 + val |= fb_right(pixels, shift); 147 + shift += bits; 148 + 149 + if (shift < BITS_PER_LONG) 150 + continue; 151 + 152 + val = fb_reverse_long(val, reverse); 153 + fb_write_offset(val, offset++, dst); 154 + shift &= BITS_PER_LONG - 1; 155 + val = !shift ? 0 : fb_left(pixels, bits - shift); 156 + } 157 + 158 + if (shift) { 159 + mask = ~fb_pixel_mask(shift, reverse); 160 + val = fb_reverse_long(val, reverse); 161 + if (offset || !dst->bits) 162 + old = fb_read_offset(offset, dst); 163 + fb_write_offset(fb_comp(val, old, mask), offset, dst); 164 + } 165 + } 166 + 167 + /* draw a color image a pixel at a time */ 168 + static inline void fb_color_imageblit(const struct fb_image *image, struct fb_address *dst, 169 + unsigned int bits_per_line, const u32 *palette, int bpp, 170 + struct fb_reverse reverse) 171 + { 172 + struct fb_color_iter iter; 173 + u32 height; 174 + 175 + iter.data = (const u8 *)image->data; 176 + iter.palette = palette; 177 + iter.reverse = reverse; 178 + #ifdef __LITTLE_ENDIAN 179 + if (reverse.pixel) 180 + iter.shift = BITS_PER_BYTE - bpp; 181 + else 182 + iter.shift = 0; 183 + #else 184 + if (reverse.pixel) 185 + iter.shift = BITS_PER_LONG - BITS_PER_BYTE; 186 + else 187 + iter.shift = BITS_PER_LONG - bpp; 188 + #endif 189 + iter.width = image->width; 190 + iter.i = 0; 191 + 192 + height = image->height; 193 + while (height--) { 194 + fb_bitblit(fb_color_image, &iter, bpp, dst, reverse); 195 + fb_address_forward(dst, bits_per_line); 196 + } 197 + } 198 + 199 + #ifdef __LITTLE_ENDIAN 200 + #define FB_GEN(a, b) (((a)/8+(((a)&4)<<((b)-2)) \ 201 + +(((a)&2)<<((b)*2-1))+(((a)&1u)<<((b)*3)))*((1<<(b))-1)) 202 + #define FB_GEN1(a) ((a)/8+((a)&4)/2+((a)&2)*2+((a)&1)*8) 203 + #else 204 + #define FB_GEN(a, b) (((((a)/8)<<((b)*3))+(((a)&4)<<((b)*2-2)) \ 205 + +(((a)&2)<<(b-1))+((a)&1u))*((1<<(b))-1)) 206 + #define FB_GEN1(a) (a) 207 + #endif 208 + 209 + #define FB_GENx(a) { FB_GEN(0, (a)), FB_GEN(1, (a)), FB_GEN(2, (a)), FB_GEN(3, (a)), \ 210 + FB_GEN(4, (a)), FB_GEN(5, (a)), FB_GEN(6, (a)), FB_GEN(7, (a)), \ 211 + FB_GEN(8, (a)), FB_GEN(9, (a)), FB_GEN(10, (a)), FB_GEN(11, (a)), \ 212 + FB_GEN(12, (a)), FB_GEN(13, (a)), FB_GEN(14, (a)), FB_GEN(15, (a)) } 213 + 214 + /* draw a 2 color image four pixels at a time (for 1-8 bpp only) */ 215 + static inline void fb_bitmap4x_imageblit(const struct fb_image *image, struct fb_address *dst, 216 + unsigned long fgcolor, unsigned long bgcolor, int bpp, 217 + unsigned int bits_per_line, struct fb_reverse reverse) 218 + { 219 + static const u32 mul[BITS_PER_BYTE] = { 220 + 0xf, 0x55, 0x249, 0x1111, 0x8421, 0x41041, 0x204081, 0x01010101 221 + }; 222 + static const u32 expand[BITS_PER_BYTE][1 << 4] = { 223 + { 224 + FB_GEN1(0), FB_GEN1(1), FB_GEN1(2), FB_GEN1(3), 225 + FB_GEN1(4), FB_GEN1(5), FB_GEN1(6), FB_GEN1(7), 226 + FB_GEN1(8), FB_GEN1(9), FB_GEN1(10), FB_GEN1(11), 227 + FB_GEN1(12), FB_GEN1(13), FB_GEN1(14), FB_GEN1(15) 228 + }, 229 + FB_GENx(2), FB_GENx(3), FB_GENx(4), 230 + FB_GENx(5), FB_GENx(6), FB_GENx(7), FB_GENx(8), 231 + }; 232 + struct fb_bitmap4x_iter iter; 233 + u32 height; 234 + 235 + iter.data = (const u8 *)image->data; 236 + if (reverse.pixel) { 237 + fgcolor = fb_reverse_bits_long(fgcolor << (BITS_PER_BYTE - bpp)); 238 + bgcolor = fb_reverse_bits_long(bgcolor << (BITS_PER_BYTE - bpp)); 239 + } 240 + iter.fgxcolor = (fgcolor ^ bgcolor) * mul[bpp-1]; 241 + iter.bgcolor = bgcolor * mul[bpp-1]; 242 + iter.width = image->width; 243 + iter.i = image->width; 244 + iter.expand = expand[bpp-1]; 245 + iter.bpp = bpp; 246 + iter.top = false; 247 + 248 + height = image->height; 249 + while (height--) { 250 + fb_bitblit(fb_bitmap4x_image, &iter, bpp * BITS_PER_BYTE/2, dst, reverse); 251 + fb_address_forward(dst, bits_per_line); 252 + } 253 + } 254 + 255 + /* draw a bitmap image 1 pixel at a time (for >8 bpp) */ 256 + static inline void fb_bitmap1x_imageblit(const struct fb_image *image, struct fb_address *dst, 257 + unsigned long fgcolor, unsigned long bgcolor, int bpp, 258 + unsigned int bits_per_line, struct fb_reverse reverse) 259 + { 260 + struct fb_bitmap_iter iter; 261 + u32 height; 262 + 263 + iter.colors[0] = bgcolor; 264 + iter.colors[1] = fgcolor; 265 + #ifndef __LITTLE_ENDIAN 266 + iter.colors[0] <<= BITS_PER_LONG - bpp; 267 + iter.colors[1] <<= BITS_PER_LONG - bpp; 268 + #endif 269 + iter.data = (const u8 *)image->data; 270 + iter.width = image->width; 271 + iter.i = 0; 272 + 273 + height = image->height; 274 + while (height--) { 275 + fb_bitblit(fb_bitmap_image, &iter, bpp, dst, reverse); 276 + fb_address_forward(dst, bits_per_line); 277 + } 278 + } 279 + 280 + /* one pixel per word, 64/32 bpp blitting */ 281 + static inline void fb_bitmap_1ppw(const struct fb_image *image, struct fb_address *dst, 282 + unsigned long fgcolor, unsigned long bgcolor, 283 + int words_per_line, struct fb_reverse reverse) 284 + { 285 + unsigned long tab[2]; 286 + const u8 *src = (u8 *)image->data; 287 + int width = image->width; 288 + int offset; 289 + u32 height; 290 + 291 + if (reverse.byte) { 292 + tab[0] = swab_long(bgcolor); 293 + tab[1] = swab_long(fgcolor); 294 + } else { 295 + tab[0] = bgcolor; 296 + tab[1] = fgcolor; 297 + } 298 + 299 + height = image->height; 300 + while (height--) { 301 + for (offset = 0; offset + 8 <= width; offset += 8) { 302 + unsigned int srcbyte = *src++; 303 + 304 + fb_write_offset(tab[(srcbyte >> 7) & 1], offset + 0, dst); 305 + fb_write_offset(tab[(srcbyte >> 6) & 1], offset + 1, dst); 306 + fb_write_offset(tab[(srcbyte >> 5) & 1], offset + 2, dst); 307 + fb_write_offset(tab[(srcbyte >> 4) & 1], offset + 3, dst); 308 + fb_write_offset(tab[(srcbyte >> 3) & 1], offset + 4, dst); 309 + fb_write_offset(tab[(srcbyte >> 2) & 1], offset + 5, dst); 310 + fb_write_offset(tab[(srcbyte >> 1) & 1], offset + 6, dst); 311 + fb_write_offset(tab[(srcbyte >> 0) & 1], offset + 7, dst); 312 + } 313 + 314 + if (offset < width) { 315 + unsigned int srcbyte = *src++; 316 + 317 + while (offset < width) { 318 + fb_write_offset(tab[(srcbyte >> 7) & 1], offset, dst); 319 + srcbyte <<= 1; 320 + offset++; 321 + } 322 + } 323 + fb_address_move_long(dst, words_per_line); 324 + } 325 + } 326 + 327 + static inline unsigned long fb_pack(unsigned long left, unsigned long right, int bits) 328 + { 329 + #ifdef __LITTLE_ENDIAN 330 + return left | right << bits; 331 + #else 332 + return right | left << bits; 333 + #endif 334 + } 335 + 336 + /* aligned 32/16 bpp blitting */ 337 + static inline void fb_bitmap_2ppw(const struct fb_image *image, struct fb_address *dst, 338 + unsigned long fgcolor, unsigned long bgcolor, 339 + int words_per_line, struct fb_reverse reverse) 340 + { 341 + unsigned long tab[4]; 342 + const u8 *src = (u8 *)image->data; 343 + int width = image->width / 2; 344 + int offset; 345 + u32 height; 346 + 347 + tab[0] = fb_pack(bgcolor, bgcolor, BITS_PER_LONG/2); 348 + tab[1] = fb_pack(bgcolor, fgcolor, BITS_PER_LONG/2); 349 + tab[2] = fb_pack(fgcolor, bgcolor, BITS_PER_LONG/2); 350 + tab[3] = fb_pack(fgcolor, fgcolor, BITS_PER_LONG/2); 351 + 352 + if (reverse.byte) { 353 + tab[0] = swab_long(tab[0]); 354 + tab[1] = swab_long(tab[1]); 355 + tab[2] = swab_long(tab[2]); 356 + tab[3] = swab_long(tab[3]); 357 + } 358 + 359 + height = image->height; 360 + while (height--) { 361 + for (offset = 0; offset + 4 <= width; offset += 4) { 362 + unsigned int srcbyte = *src++; 363 + 364 + fb_write_offset(tab[(srcbyte >> 6) & 3], offset + 0, dst); 365 + fb_write_offset(tab[(srcbyte >> 4) & 3], offset + 1, dst); 366 + fb_write_offset(tab[(srcbyte >> 2) & 3], offset + 2, dst); 367 + fb_write_offset(tab[(srcbyte >> 0) & 3], offset + 3, dst); 368 + } 369 + 370 + if (offset < width) { 371 + unsigned int srcbyte = *src++; 372 + 373 + while (offset < width) { 374 + fb_write_offset(tab[(srcbyte >> 6) & 3], offset, dst); 375 + srcbyte <<= 2; 376 + offset++; 377 + } 378 + } 379 + fb_address_move_long(dst, words_per_line); 380 + } 381 + } 382 + 383 + #define FB_PATP(a, b) (((a)<<((b)*BITS_PER_LONG/4))*((1UL<<BITS_PER_LONG/4)-1UL)) 384 + #define FB_PAT4(a) (FB_PATP((a)&1, 0)|FB_PATP(((a)&2)/2, 1)| \ 385 + FB_PATP(((a)&4)/4, 2)|FB_PATP(((a)&8)/8, 3)) 386 + 387 + /* aligned 16/8 bpp blitting */ 388 + static inline void fb_bitmap_4ppw(const struct fb_image *image, struct fb_address *dst, 389 + unsigned long fgcolor, unsigned long bgcolor, 390 + int words_per_line, struct fb_reverse reverse) 391 + { 392 + static const unsigned long tab16_be[] = { 393 + 0, FB_PAT4(1UL), FB_PAT4(2UL), FB_PAT4(3UL), 394 + FB_PAT4(4UL), FB_PAT4(5UL), FB_PAT4(6UL), FB_PAT4(7UL), 395 + FB_PAT4(8UL), FB_PAT4(9UL), FB_PAT4(10UL), FB_PAT4(11UL), 396 + FB_PAT4(12UL), FB_PAT4(13UL), FB_PAT4(14UL), ~0UL 397 + }; 398 + static const unsigned long tab16_le[] = { 399 + 0, FB_PAT4(8UL), FB_PAT4(4UL), FB_PAT4(12UL), 400 + FB_PAT4(2UL), FB_PAT4(10UL), FB_PAT4(6UL), FB_PAT4(14UL), 401 + FB_PAT4(1UL), FB_PAT4(9UL), FB_PAT4(5UL), FB_PAT4(13UL), 402 + FB_PAT4(3UL), FB_PAT4(11UL), FB_PAT4(7UL), ~0UL 403 + }; 404 + const unsigned long *tab; 405 + const u8 *src = (u8 *)image->data; 406 + int width = image->width / 4; 407 + int offset; 408 + u32 height; 409 + 410 + fgcolor = fgcolor | fgcolor << BITS_PER_LONG/4; 411 + bgcolor = bgcolor | bgcolor << BITS_PER_LONG/4; 412 + fgcolor = fgcolor | fgcolor << BITS_PER_LONG/2; 413 + bgcolor = bgcolor | bgcolor << BITS_PER_LONG/2; 414 + fgcolor ^= bgcolor; 415 + 416 + if (BITS_PER_LONG/4 > BITS_PER_BYTE && reverse.byte) { 417 + fgcolor = swab_long(fgcolor); 418 + bgcolor = swab_long(bgcolor); 419 + } 420 + 421 + #ifdef __LITTLE_ENDIAN 422 + tab = reverse.byte ? tab16_be : tab16_le; 423 + #else 424 + tab = reverse.byte ? tab16_le : tab16_be; 425 + #endif 426 + 427 + height = image->height; 428 + while (height--) { 429 + for (offset = 0; offset + 2 <= width; offset += 2, src++) { 430 + fb_write_offset((fgcolor & tab[*src >> 4]) ^ bgcolor, offset + 0, dst); 431 + fb_write_offset((fgcolor & tab[*src & 0xf]) ^ bgcolor, offset + 1, dst); 432 + } 433 + 434 + if (offset < width) 435 + fb_write_offset((fgcolor & tab[*src++ >> 4]) ^ bgcolor, offset, dst); 436 + 437 + fb_address_move_long(dst, words_per_line); 438 + } 439 + } 440 + 441 + static inline void fb_bitmap_imageblit(const struct fb_image *image, struct fb_address *dst, 442 + unsigned int bits_per_line, const u32 *palette, int bpp, 443 + struct fb_reverse reverse) 444 + { 445 + unsigned long fgcolor, bgcolor; 446 + 447 + if (palette) { 448 + fgcolor = palette[image->fg_color]; 449 + bgcolor = palette[image->bg_color]; 450 + } else { 451 + fgcolor = image->fg_color; 452 + bgcolor = image->bg_color; 453 + } 454 + 455 + if (!dst->bits && !(bits_per_line & (BITS_PER_LONG-1))) { 456 + if (bpp == BITS_PER_LONG && BITS_PER_LONG == 32) { 457 + fb_bitmap_1ppw(image, dst, fgcolor, bgcolor, 458 + bits_per_line / BITS_PER_LONG, reverse); 459 + return; 460 + } 461 + if (bpp == BITS_PER_LONG/2 && !(image->width & 1)) { 462 + fb_bitmap_2ppw(image, dst, fgcolor, bgcolor, 463 + bits_per_line / BITS_PER_LONG, reverse); 464 + return; 465 + } 466 + if (bpp == BITS_PER_LONG/4 && !(image->width & 3)) { 467 + fb_bitmap_4ppw(image, dst, fgcolor, bgcolor, 468 + bits_per_line / BITS_PER_LONG, reverse); 469 + return; 470 + } 471 + } 472 + 473 + if (bpp > 0 && bpp <= BITS_PER_BYTE) 474 + fb_bitmap4x_imageblit(image, dst, fgcolor, bgcolor, bpp, 475 + bits_per_line, reverse); 476 + else if (bpp > BITS_PER_BYTE && bpp <= BITS_PER_LONG) 477 + fb_bitmap1x_imageblit(image, dst, fgcolor, bgcolor, bpp, 478 + bits_per_line, reverse); 479 + } 480 + 481 + static inline void fb_imageblit(struct fb_info *p, const struct fb_image *image) 482 + { 483 + int bpp = p->var.bits_per_pixel; 484 + unsigned int bits_per_line = BYTES_TO_BITS(p->fix.line_length); 485 + struct fb_address dst = fb_address_init(p); 486 + struct fb_reverse reverse = fb_reverse_init(p); 487 + const u32 *palette = fb_palette(p); 488 + 489 + fb_address_forward(&dst, image->dy * bits_per_line + image->dx * bpp); 490 + 491 + if (image->depth == 1) 492 + fb_bitmap_imageblit(image, &dst, bits_per_line, palette, bpp, reverse); 493 + else 494 + fb_color_imageblit(image, &dst, bits_per_line, palette, bpp, reverse); 495 + }
+12 -357
drivers/video/fbdev/core/syscopyarea.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 1 2 /* 2 - * Generic Bit Block Transfer for frame buffers located in system RAM with 3 - * packed pixels of any depth. 4 - * 5 - * Based almost entirely from cfbcopyarea.c (which is based almost entirely 6 - * on Geert Uytterhoeven's copyarea routine) 7 - * 8 - * Copyright (C) 2007 Antonino Daplas <adaplas@pol.net> 9 - * 10 - * This file is subject to the terms and conditions of the GNU General Public 11 - * License. See the file COPYING in the main directory of this archive for 12 - * more details. 13 - * 3 + * Copyright (C) 2025 Zsolt Kajtar (soci@c64.rulez.org) 14 4 */ 15 5 #include <linux/module.h> 16 - #include <linux/kernel.h> 17 - #include <linux/string.h> 18 6 #include <linux/fb.h> 7 + #include <linux/bitrev.h> 19 8 #include <asm/types.h> 20 - #include <asm/io.h> 21 - #include "fb_draw.h" 22 9 23 - /* 24 - * Generic bitwise copy algorithm 25 - */ 10 + #ifdef CONFIG_FB_SYS_REV_PIXELS_IN_BYTE 11 + #define FB_REV_PIXELS_IN_BYTE 12 + #endif 26 13 27 - static void 28 - bitcpy(struct fb_info *p, unsigned long *dst, unsigned dst_idx, 29 - const unsigned long *src, unsigned src_idx, int bits, unsigned n) 30 - { 31 - unsigned long first, last; 32 - int const shift = dst_idx-src_idx; 33 - int left, right; 34 - 35 - first = FB_SHIFT_HIGH(p, ~0UL, dst_idx); 36 - last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits)); 37 - 38 - if (!shift) { 39 - /* Same alignment for source and dest */ 40 - if (dst_idx+n <= bits) { 41 - /* Single word */ 42 - if (last) 43 - first &= last; 44 - *dst = comp(*src, *dst, first); 45 - } else { 46 - /* Multiple destination words */ 47 - /* Leading bits */ 48 - if (first != ~0UL) { 49 - *dst = comp(*src, *dst, first); 50 - dst++; 51 - src++; 52 - n -= bits - dst_idx; 53 - } 54 - 55 - /* Main chunk */ 56 - n /= bits; 57 - while (n >= 8) { 58 - *dst++ = *src++; 59 - *dst++ = *src++; 60 - *dst++ = *src++; 61 - *dst++ = *src++; 62 - *dst++ = *src++; 63 - *dst++ = *src++; 64 - *dst++ = *src++; 65 - *dst++ = *src++; 66 - n -= 8; 67 - } 68 - while (n--) 69 - *dst++ = *src++; 70 - 71 - /* Trailing bits */ 72 - if (last) 73 - *dst = comp(*src, *dst, last); 74 - } 75 - } else { 76 - unsigned long d0, d1; 77 - int m; 78 - 79 - /* Different alignment for source and dest */ 80 - right = shift & (bits - 1); 81 - left = -shift & (bits - 1); 82 - 83 - if (dst_idx+n <= bits) { 84 - /* Single destination word */ 85 - if (last) 86 - first &= last; 87 - if (shift > 0) { 88 - /* Single source word */ 89 - *dst = comp(*src << left, *dst, first); 90 - } else if (src_idx+n <= bits) { 91 - /* Single source word */ 92 - *dst = comp(*src >> right, *dst, first); 93 - } else { 94 - /* 2 source words */ 95 - d0 = *src++; 96 - d1 = *src; 97 - *dst = comp(d0 >> right | d1 << left, *dst, 98 - first); 99 - } 100 - } else { 101 - /* Multiple destination words */ 102 - /** We must always remember the last value read, 103 - because in case SRC and DST overlap bitwise (e.g. 104 - when moving just one pixel in 1bpp), we always 105 - collect one full long for DST and that might 106 - overlap with the current long from SRC. We store 107 - this value in 'd0'. */ 108 - d0 = *src++; 109 - /* Leading bits */ 110 - if (shift > 0) { 111 - /* Single source word */ 112 - *dst = comp(d0 << left, *dst, first); 113 - dst++; 114 - n -= bits - dst_idx; 115 - } else { 116 - /* 2 source words */ 117 - d1 = *src++; 118 - *dst = comp(d0 >> right | d1 << left, *dst, 119 - first); 120 - d0 = d1; 121 - dst++; 122 - n -= bits - dst_idx; 123 - } 124 - 125 - /* Main chunk */ 126 - m = n % bits; 127 - n /= bits; 128 - while (n >= 4) { 129 - d1 = *src++; 130 - *dst++ = d0 >> right | d1 << left; 131 - d0 = d1; 132 - d1 = *src++; 133 - *dst++ = d0 >> right | d1 << left; 134 - d0 = d1; 135 - d1 = *src++; 136 - *dst++ = d0 >> right | d1 << left; 137 - d0 = d1; 138 - d1 = *src++; 139 - *dst++ = d0 >> right | d1 << left; 140 - d0 = d1; 141 - n -= 4; 142 - } 143 - while (n--) { 144 - d1 = *src++; 145 - *dst++ = d0 >> right | d1 << left; 146 - d0 = d1; 147 - } 148 - 149 - /* Trailing bits */ 150 - if (m) { 151 - if (m <= bits - right) { 152 - /* Single source word */ 153 - d0 >>= right; 154 - } else { 155 - /* 2 source words */ 156 - d1 = *src; 157 - d0 = d0 >> right | d1 << left; 158 - } 159 - *dst = comp(d0, *dst, last); 160 - } 161 - } 162 - } 163 - } 164 - 165 - /* 166 - * Generic bitwise copy algorithm, operating backward 167 - */ 168 - 169 - static void 170 - bitcpy_rev(struct fb_info *p, unsigned long *dst, unsigned dst_idx, 171 - const unsigned long *src, unsigned src_idx, unsigned bits, 172 - unsigned n) 173 - { 174 - unsigned long first, last; 175 - int shift; 176 - 177 - dst += (dst_idx + n - 1) / bits; 178 - src += (src_idx + n - 1) / bits; 179 - dst_idx = (dst_idx + n - 1) % bits; 180 - src_idx = (src_idx + n - 1) % bits; 181 - 182 - shift = dst_idx-src_idx; 183 - 184 - first = ~FB_SHIFT_HIGH(p, ~0UL, (dst_idx + 1) % bits); 185 - last = FB_SHIFT_HIGH(p, ~0UL, (bits + dst_idx + 1 - n) % bits); 186 - 187 - if (!shift) { 188 - /* Same alignment for source and dest */ 189 - if ((unsigned long)dst_idx+1 >= n) { 190 - /* Single word */ 191 - if (first) 192 - last &= first; 193 - *dst = comp(*src, *dst, last); 194 - } else { 195 - /* Multiple destination words */ 196 - 197 - /* Leading bits */ 198 - if (first) { 199 - *dst = comp(*src, *dst, first); 200 - dst--; 201 - src--; 202 - n -= dst_idx+1; 203 - } 204 - 205 - /* Main chunk */ 206 - n /= bits; 207 - while (n >= 8) { 208 - *dst-- = *src--; 209 - *dst-- = *src--; 210 - *dst-- = *src--; 211 - *dst-- = *src--; 212 - *dst-- = *src--; 213 - *dst-- = *src--; 214 - *dst-- = *src--; 215 - *dst-- = *src--; 216 - n -= 8; 217 - } 218 - while (n--) 219 - *dst-- = *src--; 220 - /* Trailing bits */ 221 - if (last != -1UL) 222 - *dst = comp(*src, *dst, last); 223 - } 224 - } else { 225 - /* Different alignment for source and dest */ 226 - 227 - int const left = shift & (bits-1); 228 - int const right = -shift & (bits-1); 229 - 230 - if ((unsigned long)dst_idx+1 >= n) { 231 - /* Single destination word */ 232 - if (first) 233 - last &= first; 234 - if (shift < 0) { 235 - /* Single source word */ 236 - *dst = comp(*src >> right, *dst, last); 237 - } else if (1+(unsigned long)src_idx >= n) { 238 - /* Single source word */ 239 - *dst = comp(*src << left, *dst, last); 240 - } else { 241 - /* 2 source words */ 242 - *dst = comp(*src << left | *(src-1) >> right, 243 - *dst, last); 244 - } 245 - } else { 246 - /* Multiple destination words */ 247 - /** We must always remember the last value read, 248 - because in case SRC and DST overlap bitwise (e.g. 249 - when moving just one pixel in 1bpp), we always 250 - collect one full long for DST and that might 251 - overlap with the current long from SRC. We store 252 - this value in 'd0'. */ 253 - unsigned long d0, d1; 254 - int m; 255 - 256 - d0 = *src--; 257 - /* Leading bits */ 258 - if (shift < 0) { 259 - /* Single source word */ 260 - d1 = d0; 261 - d0 >>= right; 262 - } else { 263 - /* 2 source words */ 264 - d1 = *src--; 265 - d0 = d0 << left | d1 >> right; 266 - } 267 - if (!first) 268 - *dst = d0; 269 - else 270 - *dst = comp(d0, *dst, first); 271 - d0 = d1; 272 - dst--; 273 - n -= dst_idx+1; 274 - 275 - /* Main chunk */ 276 - m = n % bits; 277 - n /= bits; 278 - while (n >= 4) { 279 - d1 = *src--; 280 - *dst-- = d0 << left | d1 >> right; 281 - d0 = d1; 282 - d1 = *src--; 283 - *dst-- = d0 << left | d1 >> right; 284 - d0 = d1; 285 - d1 = *src--; 286 - *dst-- = d0 << left | d1 >> right; 287 - d0 = d1; 288 - d1 = *src--; 289 - *dst-- = d0 << left | d1 >> right; 290 - d0 = d1; 291 - n -= 4; 292 - } 293 - while (n--) { 294 - d1 = *src--; 295 - *dst-- = d0 << left | d1 >> right; 296 - d0 = d1; 297 - } 298 - 299 - /* Trailing bits */ 300 - if (m) { 301 - if (m <= bits - left) { 302 - /* Single source word */ 303 - d0 <<= left; 304 - } else { 305 - /* 2 source words */ 306 - d1 = *src; 307 - d0 = d0 << left | d1 >> right; 308 - } 309 - *dst = comp(d0, *dst, last); 310 - } 311 - } 312 - } 313 - } 14 + #include "sysmem.h" 15 + #include "fb_copyarea.h" 314 16 315 17 void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area) 316 18 { 317 - u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy; 318 - u32 height = area->height, width = area->width; 319 - unsigned int const bits_per_line = p->fix.line_length * 8u; 320 - unsigned long *base = NULL; 321 - int bits = BITS_PER_LONG, bytes = bits >> 3; 322 - unsigned dst_idx = 0, src_idx = 0, rev_copy = 0; 323 - 324 - if (p->state != FBINFO_STATE_RUNNING) 325 - return; 326 - 327 19 if (!(p->flags & FBINFO_VIRTFB)) 328 - fb_warn_once(p, "Framebuffer is not in virtual address space."); 20 + fb_warn_once(p, "%s: framebuffer is not in virtual address space.\n", __func__); 329 21 330 - /* if the beginning of the target area might overlap with the end of 331 - the source area, be have to copy the area reverse. */ 332 - if ((dy == sy && dx > sx) || (dy > sy)) { 333 - dy += height; 334 - sy += height; 335 - rev_copy = 1; 336 - } 337 - 338 - /* split the base of the framebuffer into a long-aligned address and 339 - the index of the first bit */ 340 - base = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1)); 341 - dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1)); 342 - /* add offset of source and target area */ 343 - dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel; 344 - src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel; 345 - 346 - if (p->fbops->fb_sync) 347 - p->fbops->fb_sync(p); 348 - 349 - if (rev_copy) { 350 - while (height--) { 351 - dst_idx -= bits_per_line; 352 - src_idx -= bits_per_line; 353 - bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits, 354 - base + (src_idx / bits), src_idx % bits, bits, 355 - width*p->var.bits_per_pixel); 356 - } 357 - } else { 358 - while (height--) { 359 - bitcpy(p, base + (dst_idx / bits), dst_idx % bits, 360 - base + (src_idx / bits), src_idx % bits, bits, 361 - width*p->var.bits_per_pixel); 362 - dst_idx += bits_per_line; 363 - src_idx += bits_per_line; 364 - } 365 - } 22 + fb_copyarea(p, area); 366 23 } 367 - 368 24 EXPORT_SYMBOL(sys_copyarea); 369 25 370 - MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>"); 371 - MODULE_DESCRIPTION("Generic copyarea (sys-to-sys)"); 26 + MODULE_AUTHOR("Zsolt Kajtar <soci@c64.rulez.org>"); 27 + MODULE_DESCRIPTION("Virtual memory packed pixel framebuffer area copy"); 372 28 MODULE_LICENSE("GPL"); 373 -
+12 -312
drivers/video/fbdev/core/sysfillrect.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 1 2 /* 2 - * Generic fillrect for frame buffers in system RAM with packed pixels of 3 - * any depth. 4 - * 5 - * Based almost entirely from cfbfillrect.c (which is based almost entirely 6 - * on Geert Uytterhoeven's fillrect routine) 7 - * 8 - * Copyright (C) 2007 Antonino Daplas <adaplas@pol.net> 9 - * 10 - * This file is subject to the terms and conditions of the GNU General Public 11 - * License. See the file COPYING in the main directory of this archive for 12 - * more details. 3 + * Copyright (C) 2025 Zsolt Kajtar (soci@c64.rulez.org) 13 4 */ 14 5 #include <linux/module.h> 15 - #include <linux/string.h> 16 6 #include <linux/fb.h> 7 + #include <linux/bitrev.h> 17 8 #include <asm/types.h> 18 - #include "fb_draw.h" 19 9 20 - /* 21 - * Aligned pattern fill using 32/64-bit memory accesses 22 - */ 10 + #ifdef CONFIG_FB_SYS_REV_PIXELS_IN_BYTE 11 + #define FB_REV_PIXELS_IN_BYTE 12 + #endif 23 13 24 - static void 25 - bitfill_aligned(struct fb_info *p, unsigned long *dst, int dst_idx, 26 - unsigned long pat, unsigned n, int bits) 27 - { 28 - unsigned long first, last; 29 - 30 - if (!n) 31 - return; 32 - 33 - first = FB_SHIFT_HIGH(p, ~0UL, dst_idx); 34 - last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits)); 35 - 36 - if (dst_idx+n <= bits) { 37 - /* Single word */ 38 - if (last) 39 - first &= last; 40 - *dst = comp(pat, *dst, first); 41 - } else { 42 - /* Multiple destination words */ 43 - 44 - /* Leading bits */ 45 - if (first!= ~0UL) { 46 - *dst = comp(pat, *dst, first); 47 - dst++; 48 - n -= bits - dst_idx; 49 - } 50 - 51 - /* Main chunk */ 52 - n /= bits; 53 - memset_l(dst, pat, n); 54 - dst += n; 55 - 56 - /* Trailing bits */ 57 - if (last) 58 - *dst = comp(pat, *dst, last); 59 - } 60 - } 61 - 62 - 63 - /* 64 - * Unaligned generic pattern fill using 32/64-bit memory accesses 65 - * The pattern must have been expanded to a full 32/64-bit value 66 - * Left/right are the appropriate shifts to convert to the pattern to be 67 - * used for the next 32/64-bit word 68 - */ 69 - 70 - static void 71 - bitfill_unaligned(struct fb_info *p, unsigned long *dst, int dst_idx, 72 - unsigned long pat, int left, int right, unsigned n, int bits) 73 - { 74 - unsigned long first, last; 75 - 76 - if (!n) 77 - return; 78 - 79 - first = FB_SHIFT_HIGH(p, ~0UL, dst_idx); 80 - last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits)); 81 - 82 - if (dst_idx+n <= bits) { 83 - /* Single word */ 84 - if (last) 85 - first &= last; 86 - *dst = comp(pat, *dst, first); 87 - } else { 88 - /* Multiple destination words */ 89 - /* Leading bits */ 90 - if (first) { 91 - *dst = comp(pat, *dst, first); 92 - dst++; 93 - pat = pat << left | pat >> right; 94 - n -= bits - dst_idx; 95 - } 96 - 97 - /* Main chunk */ 98 - n /= bits; 99 - while (n >= 4) { 100 - *dst++ = pat; 101 - pat = pat << left | pat >> right; 102 - *dst++ = pat; 103 - pat = pat << left | pat >> right; 104 - *dst++ = pat; 105 - pat = pat << left | pat >> right; 106 - *dst++ = pat; 107 - pat = pat << left | pat >> right; 108 - n -= 4; 109 - } 110 - while (n--) { 111 - *dst++ = pat; 112 - pat = pat << left | pat >> right; 113 - } 114 - 115 - /* Trailing bits */ 116 - if (last) 117 - *dst = comp(pat, *dst, last); 118 - } 119 - } 120 - 121 - /* 122 - * Aligned pattern invert using 32/64-bit memory accesses 123 - */ 124 - static void 125 - bitfill_aligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx, 126 - unsigned long pat, unsigned n, int bits) 127 - { 128 - unsigned long val = pat; 129 - unsigned long first, last; 130 - 131 - if (!n) 132 - return; 133 - 134 - first = FB_SHIFT_HIGH(p, ~0UL, dst_idx); 135 - last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits)); 136 - 137 - if (dst_idx+n <= bits) { 138 - /* Single word */ 139 - if (last) 140 - first &= last; 141 - *dst = comp(*dst ^ val, *dst, first); 142 - } else { 143 - /* Multiple destination words */ 144 - /* Leading bits */ 145 - if (first!=0UL) { 146 - *dst = comp(*dst ^ val, *dst, first); 147 - dst++; 148 - n -= bits - dst_idx; 149 - } 150 - 151 - /* Main chunk */ 152 - n /= bits; 153 - while (n >= 8) { 154 - *dst++ ^= val; 155 - *dst++ ^= val; 156 - *dst++ ^= val; 157 - *dst++ ^= val; 158 - *dst++ ^= val; 159 - *dst++ ^= val; 160 - *dst++ ^= val; 161 - *dst++ ^= val; 162 - n -= 8; 163 - } 164 - while (n--) 165 - *dst++ ^= val; 166 - /* Trailing bits */ 167 - if (last) 168 - *dst = comp(*dst ^ val, *dst, last); 169 - } 170 - } 171 - 172 - 173 - /* 174 - * Unaligned generic pattern invert using 32/64-bit memory accesses 175 - * The pattern must have been expanded to a full 32/64-bit value 176 - * Left/right are the appropriate shifts to convert to the pattern to be 177 - * used for the next 32/64-bit word 178 - */ 179 - 180 - static void 181 - bitfill_unaligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx, 182 - unsigned long pat, int left, int right, unsigned n, 183 - int bits) 184 - { 185 - unsigned long first, last; 186 - 187 - if (!n) 188 - return; 189 - 190 - first = FB_SHIFT_HIGH(p, ~0UL, dst_idx); 191 - last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits)); 192 - 193 - if (dst_idx+n <= bits) { 194 - /* Single word */ 195 - if (last) 196 - first &= last; 197 - *dst = comp(*dst ^ pat, *dst, first); 198 - } else { 199 - /* Multiple destination words */ 200 - 201 - /* Leading bits */ 202 - if (first != 0UL) { 203 - *dst = comp(*dst ^ pat, *dst, first); 204 - dst++; 205 - pat = pat << left | pat >> right; 206 - n -= bits - dst_idx; 207 - } 208 - 209 - /* Main chunk */ 210 - n /= bits; 211 - while (n >= 4) { 212 - *dst++ ^= pat; 213 - pat = pat << left | pat >> right; 214 - *dst++ ^= pat; 215 - pat = pat << left | pat >> right; 216 - *dst++ ^= pat; 217 - pat = pat << left | pat >> right; 218 - *dst++ ^= pat; 219 - pat = pat << left | pat >> right; 220 - n -= 4; 221 - } 222 - while (n--) { 223 - *dst ^= pat; 224 - pat = pat << left | pat >> right; 225 - } 226 - 227 - /* Trailing bits */ 228 - if (last) 229 - *dst = comp(*dst ^ pat, *dst, last); 230 - } 231 - } 14 + #include "sysmem.h" 15 + #include "fb_fillrect.h" 232 16 233 17 void sys_fillrect(struct fb_info *p, const struct fb_fillrect *rect) 234 18 { 235 - unsigned long pat, pat2, fg; 236 - unsigned long width = rect->width, height = rect->height; 237 - int bits = BITS_PER_LONG, bytes = bits >> 3; 238 - u32 bpp = p->var.bits_per_pixel; 239 - unsigned long *dst; 240 - int dst_idx, left; 241 - 242 - if (p->state != FBINFO_STATE_RUNNING) 243 - return; 244 - 245 19 if (!(p->flags & FBINFO_VIRTFB)) 246 - fb_warn_once(p, "Framebuffer is not in virtual address space."); 20 + fb_warn_once(p, "%s: framebuffer is not in virtual address space.\n", __func__); 247 21 248 - if (p->fix.visual == FB_VISUAL_TRUECOLOR || 249 - p->fix.visual == FB_VISUAL_DIRECTCOLOR ) 250 - fg = ((u32 *) (p->pseudo_palette))[rect->color]; 251 - else 252 - fg = rect->color; 253 - 254 - pat = pixel_to_pat( bpp, fg); 255 - 256 - dst = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1)); 257 - dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8; 258 - dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp; 259 - /* FIXME For now we support 1-32 bpp only */ 260 - left = bits % bpp; 261 - if (p->fbops->fb_sync) 262 - p->fbops->fb_sync(p); 263 - if (!left) { 264 - void (*fill_op32)(struct fb_info *p, unsigned long *dst, 265 - int dst_idx, unsigned long pat, unsigned n, 266 - int bits) = NULL; 267 - 268 - switch (rect->rop) { 269 - case ROP_XOR: 270 - fill_op32 = bitfill_aligned_rev; 271 - break; 272 - case ROP_COPY: 273 - fill_op32 = bitfill_aligned; 274 - break; 275 - default: 276 - printk( KERN_ERR "cfb_fillrect(): unknown rop, " 277 - "defaulting to ROP_COPY\n"); 278 - fill_op32 = bitfill_aligned; 279 - break; 280 - } 281 - while (height--) { 282 - dst += dst_idx >> (ffs(bits) - 1); 283 - dst_idx &= (bits - 1); 284 - fill_op32(p, dst, dst_idx, pat, width*bpp, bits); 285 - dst_idx += p->fix.line_length*8; 286 - } 287 - } else { 288 - int right, r; 289 - void (*fill_op)(struct fb_info *p, unsigned long *dst, 290 - int dst_idx, unsigned long pat, int left, 291 - int right, unsigned n, int bits) = NULL; 292 - #ifdef __LITTLE_ENDIAN 293 - right = left; 294 - left = bpp - right; 295 - #else 296 - right = bpp - left; 297 - #endif 298 - switch (rect->rop) { 299 - case ROP_XOR: 300 - fill_op = bitfill_unaligned_rev; 301 - break; 302 - case ROP_COPY: 303 - fill_op = bitfill_unaligned; 304 - break; 305 - default: 306 - printk(KERN_ERR "sys_fillrect(): unknown rop, " 307 - "defaulting to ROP_COPY\n"); 308 - fill_op = bitfill_unaligned; 309 - break; 310 - } 311 - while (height--) { 312 - dst += dst_idx / bits; 313 - dst_idx &= (bits - 1); 314 - r = dst_idx % bpp; 315 - /* rotate pattern to the correct start position */ 316 - pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp)); 317 - fill_op(p, dst, dst_idx, pat2, left, right, 318 - width*bpp, bits); 319 - dst_idx += p->fix.line_length*8; 320 - } 321 - } 22 + fb_fillrect(p, rect); 322 23 } 323 - 324 24 EXPORT_SYMBOL(sys_fillrect); 325 25 326 - MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>"); 327 - MODULE_DESCRIPTION("Generic fill rectangle (sys-to-sys)"); 26 + MODULE_AUTHOR("Zsolt Kajtar <soci@c64.rulez.org>"); 27 + MODULE_DESCRIPTION("Virtual memory packed pixel framebuffer area fill"); 328 28 MODULE_LICENSE("GPL");
+11 -322
drivers/video/fbdev/core/sysimgblt.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 1 2 /* 2 - * Generic 1-bit or 8-bit source to 1-32 bit destination expansion 3 - * for frame buffer located in system RAM with packed pixels of any depth. 4 - * 5 - * Based almost entirely on cfbimgblt.c 6 - * 7 - * Copyright (C) April 2007 Antonino Daplas <adaplas@pol.net> 8 - * 9 - * This file is subject to the terms and conditions of the GNU General Public 10 - * License. See the file COPYING in the main directory of this archive for 11 - * more details. 3 + * Copyright (C) 2025 Zsolt Kajtar (soci@c64.rulez.org) 12 4 */ 13 5 #include <linux/module.h> 14 - #include <linux/string.h> 15 6 #include <linux/fb.h> 7 + #include <linux/bitrev.h> 16 8 #include <asm/types.h> 17 9 18 - #define DEBUG 19 - 20 - #ifdef DEBUG 21 - #define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt,__func__,## args) 22 - #else 23 - #define DPRINTK(fmt, args...) 10 + #ifdef CONFIG_FB_SYS_REV_PIXELS_IN_BYTE 11 + #define FB_REV_PIXELS_IN_BYTE 24 12 #endif 25 13 26 - static const u32 cfb_tab8_be[] = { 27 - 0x00000000,0x000000ff,0x0000ff00,0x0000ffff, 28 - 0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff, 29 - 0xff000000,0xff0000ff,0xff00ff00,0xff00ffff, 30 - 0xffff0000,0xffff00ff,0xffffff00,0xffffffff 31 - }; 32 - 33 - static const u32 cfb_tab8_le[] = { 34 - 0x00000000,0xff000000,0x00ff0000,0xffff0000, 35 - 0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00, 36 - 0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff, 37 - 0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff 38 - }; 39 - 40 - static const u32 cfb_tab16_be[] = { 41 - 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff 42 - }; 43 - 44 - static const u32 cfb_tab16_le[] = { 45 - 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff 46 - }; 47 - 48 - static const u32 cfb_tab32[] = { 49 - 0x00000000, 0xffffffff 50 - }; 51 - 52 - static void color_imageblit(const struct fb_image *image, struct fb_info *p, 53 - void *dst1, u32 start_index, u32 pitch_index) 54 - { 55 - /* Draw the penguin */ 56 - u32 *dst, *dst2; 57 - u32 color = 0, val, shift; 58 - int i, n, bpp = p->var.bits_per_pixel; 59 - u32 null_bits = 32 - bpp; 60 - u32 *palette = (u32 *) p->pseudo_palette; 61 - const u8 *src = image->data; 62 - 63 - dst2 = dst1; 64 - for (i = image->height; i--; ) { 65 - n = image->width; 66 - dst = dst1; 67 - shift = 0; 68 - val = 0; 69 - 70 - if (start_index) { 71 - u32 start_mask = ~(FB_SHIFT_HIGH(p, ~(u32)0, 72 - start_index)); 73 - val = *dst & start_mask; 74 - shift = start_index; 75 - } 76 - while (n--) { 77 - if (p->fix.visual == FB_VISUAL_TRUECOLOR || 78 - p->fix.visual == FB_VISUAL_DIRECTCOLOR ) 79 - color = palette[*src]; 80 - else 81 - color = *src; 82 - color <<= FB_LEFT_POS(p, bpp); 83 - val |= FB_SHIFT_HIGH(p, color, shift); 84 - if (shift >= null_bits) { 85 - *dst++ = val; 86 - 87 - val = (shift == null_bits) ? 0 : 88 - FB_SHIFT_LOW(p, color, 32 - shift); 89 - } 90 - shift += bpp; 91 - shift &= (32 - 1); 92 - src++; 93 - } 94 - if (shift) { 95 - u32 end_mask = FB_SHIFT_HIGH(p, ~(u32)0, shift); 96 - 97 - *dst &= end_mask; 98 - *dst |= val; 99 - } 100 - dst1 += p->fix.line_length; 101 - if (pitch_index) { 102 - dst2 += p->fix.line_length; 103 - dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1)); 104 - 105 - start_index += pitch_index; 106 - start_index &= 32 - 1; 107 - } 108 - } 109 - } 110 - 111 - static void slow_imageblit(const struct fb_image *image, struct fb_info *p, 112 - void *dst1, u32 fgcolor, u32 bgcolor, 113 - u32 start_index, u32 pitch_index) 114 - { 115 - u32 shift, color = 0, bpp = p->var.bits_per_pixel; 116 - u32 *dst, *dst2; 117 - u32 val, pitch = p->fix.line_length; 118 - u32 null_bits = 32 - bpp; 119 - u32 spitch = (image->width+7)/8; 120 - const u8 *src = image->data, *s; 121 - u32 i, j, l; 122 - 123 - dst2 = dst1; 124 - fgcolor <<= FB_LEFT_POS(p, bpp); 125 - bgcolor <<= FB_LEFT_POS(p, bpp); 126 - 127 - for (i = image->height; i--; ) { 128 - shift = val = 0; 129 - l = 8; 130 - j = image->width; 131 - dst = dst1; 132 - s = src; 133 - 134 - /* write leading bits */ 135 - if (start_index) { 136 - u32 start_mask = ~(FB_SHIFT_HIGH(p, ~(u32)0, 137 - start_index)); 138 - val = *dst & start_mask; 139 - shift = start_index; 140 - } 141 - 142 - while (j--) { 143 - l--; 144 - color = (*s & (1 << l)) ? fgcolor : bgcolor; 145 - val |= FB_SHIFT_HIGH(p, color, shift); 146 - 147 - /* Did the bitshift spill bits to the next long? */ 148 - if (shift >= null_bits) { 149 - *dst++ = val; 150 - val = (shift == null_bits) ? 0 : 151 - FB_SHIFT_LOW(p, color, 32 - shift); 152 - } 153 - shift += bpp; 154 - shift &= (32 - 1); 155 - if (!l) { l = 8; s++; } 156 - } 157 - 158 - /* write trailing bits */ 159 - if (shift) { 160 - u32 end_mask = FB_SHIFT_HIGH(p, ~(u32)0, shift); 161 - 162 - *dst &= end_mask; 163 - *dst |= val; 164 - } 165 - 166 - dst1 += pitch; 167 - src += spitch; 168 - if (pitch_index) { 169 - dst2 += pitch; 170 - dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1)); 171 - start_index += pitch_index; 172 - start_index &= 32 - 1; 173 - } 174 - 175 - } 176 - } 177 - 178 - /* 179 - * fast_imageblit - optimized monochrome color expansion 180 - * 181 - * Only if: bits_per_pixel == 8, 16, or 32 182 - * image->width is divisible by pixel/dword (ppw); 183 - * fix->line_legth is divisible by 4; 184 - * beginning and end of a scanline is dword aligned 185 - */ 186 - static void fast_imageblit(const struct fb_image *image, struct fb_info *p, 187 - void *dst1, u32 fgcolor, u32 bgcolor) 188 - { 189 - u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel; 190 - u32 ppw = 32/bpp, spitch = (image->width + 7)/8; 191 - u32 bit_mask, eorx, shift; 192 - const u8 *s = image->data, *src; 193 - u32 *dst; 194 - const u32 *tab; 195 - size_t tablen; 196 - u32 colortab[16]; 197 - int i, j, k; 198 - 199 - switch (bpp) { 200 - case 8: 201 - tab = fb_be_math(p) ? cfb_tab8_be : cfb_tab8_le; 202 - tablen = 16; 203 - break; 204 - case 16: 205 - tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le; 206 - tablen = 4; 207 - break; 208 - case 32: 209 - tab = cfb_tab32; 210 - tablen = 2; 211 - break; 212 - default: 213 - return; 214 - } 215 - 216 - for (i = ppw-1; i--; ) { 217 - fgx <<= bpp; 218 - bgx <<= bpp; 219 - fgx |= fgcolor; 220 - bgx |= bgcolor; 221 - } 222 - 223 - bit_mask = (1 << ppw) - 1; 224 - eorx = fgx ^ bgx; 225 - k = image->width/ppw; 226 - 227 - for (i = 0; i < tablen; ++i) 228 - colortab[i] = (tab[i] & eorx) ^ bgx; 229 - 230 - for (i = image->height; i--; ) { 231 - dst = dst1; 232 - shift = 8; 233 - src = s; 234 - 235 - /* 236 - * Manually unroll the per-line copying loop for better 237 - * performance. This works until we processed the last 238 - * completely filled source byte (inclusive). 239 - */ 240 - switch (ppw) { 241 - case 4: /* 8 bpp */ 242 - for (j = k; j >= 2; j -= 2, ++src) { 243 - *dst++ = colortab[(*src >> 4) & bit_mask]; 244 - *dst++ = colortab[(*src >> 0) & bit_mask]; 245 - } 246 - break; 247 - case 2: /* 16 bpp */ 248 - for (j = k; j >= 4; j -= 4, ++src) { 249 - *dst++ = colortab[(*src >> 6) & bit_mask]; 250 - *dst++ = colortab[(*src >> 4) & bit_mask]; 251 - *dst++ = colortab[(*src >> 2) & bit_mask]; 252 - *dst++ = colortab[(*src >> 0) & bit_mask]; 253 - } 254 - break; 255 - case 1: /* 32 bpp */ 256 - for (j = k; j >= 8; j -= 8, ++src) { 257 - *dst++ = colortab[(*src >> 7) & bit_mask]; 258 - *dst++ = colortab[(*src >> 6) & bit_mask]; 259 - *dst++ = colortab[(*src >> 5) & bit_mask]; 260 - *dst++ = colortab[(*src >> 4) & bit_mask]; 261 - *dst++ = colortab[(*src >> 3) & bit_mask]; 262 - *dst++ = colortab[(*src >> 2) & bit_mask]; 263 - *dst++ = colortab[(*src >> 1) & bit_mask]; 264 - *dst++ = colortab[(*src >> 0) & bit_mask]; 265 - } 266 - break; 267 - } 268 - 269 - /* 270 - * For image widths that are not a multiple of 8, there 271 - * are trailing pixels left on the current line. Print 272 - * them as well. 273 - */ 274 - for (; j--; ) { 275 - shift -= ppw; 276 - *dst++ = colortab[(*src >> shift) & bit_mask]; 277 - if (!shift) { 278 - shift = 8; 279 - ++src; 280 - } 281 - } 282 - 283 - dst1 += p->fix.line_length; 284 - s += spitch; 285 - } 286 - } 14 + #include "sysmem.h" 15 + #include "fb_imageblit.h" 287 16 288 17 void sys_imageblit(struct fb_info *p, const struct fb_image *image) 289 18 { 290 - u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0; 291 - u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel; 292 - u32 width = image->width; 293 - u32 dx = image->dx, dy = image->dy; 294 - void *dst1; 295 - 296 - if (p->state != FBINFO_STATE_RUNNING) 297 - return; 298 - 299 19 if (!(p->flags & FBINFO_VIRTFB)) 300 - fb_warn_once(p, "Framebuffer is not in virtual address space."); 20 + fb_warn_once(p, "%s: framebuffer is not in virtual address space.\n", __func__); 301 21 302 - bitstart = (dy * p->fix.line_length * 8) + (dx * bpp); 303 - start_index = bitstart & (32 - 1); 304 - pitch_index = (p->fix.line_length & (bpl - 1)) * 8; 305 - 306 - bitstart /= 8; 307 - bitstart &= ~(bpl - 1); 308 - dst1 = (void __force *)p->screen_base + bitstart; 309 - 310 - if (p->fbops->fb_sync) 311 - p->fbops->fb_sync(p); 312 - 313 - if (image->depth == 1) { 314 - if (p->fix.visual == FB_VISUAL_TRUECOLOR || 315 - p->fix.visual == FB_VISUAL_DIRECTCOLOR) { 316 - fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color]; 317 - bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color]; 318 - } else { 319 - fgcolor = image->fg_color; 320 - bgcolor = image->bg_color; 321 - } 322 - 323 - if (32 % bpp == 0 && !start_index && !pitch_index && 324 - ((width & (32/bpp-1)) == 0) && 325 - bpp >= 8 && bpp <= 32) 326 - fast_imageblit(image, p, dst1, fgcolor, bgcolor); 327 - else 328 - slow_imageblit(image, p, dst1, fgcolor, bgcolor, 329 - start_index, pitch_index); 330 - } else 331 - color_imageblit(image, p, dst1, start_index, pitch_index); 22 + fb_imageblit(p, image); 332 23 } 333 - 334 24 EXPORT_SYMBOL(sys_imageblit); 335 25 336 - MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>"); 337 - MODULE_DESCRIPTION("1-bit/8-bit to 1-32 bit color expansion (sys-to-sys)"); 26 + MODULE_AUTHOR("Zsolt Kajtar <soci@c64.rulez.org>"); 27 + MODULE_DESCRIPTION("Virtual memory packed pixel framebuffer image draw"); 338 28 MODULE_LICENSE("GPL"); 339 -
+39
drivers/video/fbdev/core/sysmem.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only 2 + * 3 + * Virtual memory framebuffer access for drawing routines 4 + * 5 + * Copyright (C) 2025 Zsolt Kajtar (soci@c64.rulez.org) 6 + */ 7 + 8 + /* keeps track of a bit address in framebuffer memory */ 9 + struct fb_address { 10 + void *address; 11 + int bits; 12 + }; 13 + 14 + /* initialize the bit address pointer to the beginning of the frame buffer */ 15 + static inline struct fb_address fb_address_init(struct fb_info *p) 16 + { 17 + void *base = p->screen_buffer; 18 + struct fb_address ptr; 19 + 20 + ptr.address = PTR_ALIGN_DOWN(base, BITS_PER_LONG / BITS_PER_BYTE); 21 + ptr.bits = (base - ptr.address) * BITS_PER_BYTE; 22 + return ptr; 23 + } 24 + 25 + /* framebuffer write access */ 26 + static inline void fb_write_offset(unsigned long val, int offset, const struct fb_address *dst) 27 + { 28 + unsigned long *mem = dst->address; 29 + 30 + mem[offset] = val; 31 + } 32 + 33 + /* framebuffer read access */ 34 + static inline unsigned long fb_read_offset(int offset, const struct fb_address *src) 35 + { 36 + unsigned long *mem = src->address; 37 + 38 + return mem[offset]; 39 + }