diff options
Diffstat (limited to 'drivers/video/fbdev/core/fb_draw.h')
| -rw-r--r-- | drivers/video/fbdev/core/fb_draw.h | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/drivers/video/fbdev/core/fb_draw.h b/drivers/video/fbdev/core/fb_draw.h new file mode 100644 index 000000000000..8eb13f7b3972 --- /dev/null +++ b/drivers/video/fbdev/core/fb_draw.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Various common functions used by the framebuffer drawing code + * + * Copyright (C) 2025 Zsolt Kajtar (soci@c64.rulez.org) + */ +#ifndef _FB_DRAW_H +#define _FB_DRAW_H + +/* swap bytes in a long, independent of word size */ +#define swab_long _swab_long(BITS_PER_LONG) +#define _swab_long(x) __swab_long(x) +#define __swab_long(x) swab##x + +/* move the address pointer by the number of words */ +static inline void fb_address_move_long(struct fb_address *adr, int offset) +{ + adr->address += offset * (BITS_PER_LONG / BITS_PER_BYTE); +} + +/* move the address pointer forward with the number of bits */ +static inline void fb_address_forward(struct fb_address *adr, unsigned int offset) +{ + unsigned int bits = (unsigned int)adr->bits + offset; + + adr->bits = bits & (BITS_PER_LONG - 1u); + adr->address += (bits & ~(BITS_PER_LONG - 1u)) / BITS_PER_BYTE; +} + +/* move the address pointer backwards with the number of bits */ +static inline void fb_address_backward(struct fb_address *adr, unsigned int offset) +{ + int bits = adr->bits - (int)offset; + + adr->bits = bits & (BITS_PER_LONG - 1); + if (bits < 0) + adr->address -= (adr->bits - bits) / BITS_PER_BYTE; + else + adr->address += (bits - adr->bits) / BITS_PER_BYTE; +} + +/* compose pixels based on mask */ +static inline unsigned long fb_comp(unsigned long set, unsigned long unset, unsigned long mask) +{ + return ((set ^ unset) & mask) ^ unset; +} + +/* framebuffer read-modify-write access for replacing bits in the mask */ +static inline void fb_modify_offset(unsigned long val, unsigned long mask, + int offset, const struct fb_address *dst) +{ + fb_write_offset(fb_comp(val, fb_read_offset(offset, dst), mask), offset, dst); +} + +/* + * get current palette, if applicable for visual + * + * The pseudo color table entries (and colors) are right justified and in the + * same byte order as it's expected to be placed into a native ordered + * framebuffer memory. What that means: + * + * Expected bytes in framebuffer memory (in native order): + * RR GG BB RR GG BB RR GG BB ... + * + * Pseudo palette entry on little endian arch: + * RR | GG << 8 | BB << 16 + * + * Pseudo palette entry on a big endian arch: + * RR << 16 | GG << 8 | BB + */ +static inline const u32 *fb_palette(struct fb_info *info) +{ + return (info->fix.visual == FB_VISUAL_TRUECOLOR || + info->fix.visual == FB_VISUAL_DIRECTCOLOR) ? info->pseudo_palette : NULL; +} + +/* move pixels right on screen when framebuffer is in native order */ +static inline unsigned long fb_right(unsigned long value, int index) +{ +#ifdef __LITTLE_ENDIAN + return value << index; +#else + return value >> index; +#endif +} + +/* move pixels left on screen when framebuffer is in native order */ +static inline unsigned long fb_left(unsigned long value, int index) +{ +#ifdef __LITTLE_ENDIAN + return value >> index; +#else + return value << index; +#endif +} + +/* reversal options */ +struct fb_reverse { + bool byte, pixel; +}; + +/* reverse bits of each byte in a long */ +static inline unsigned long fb_reverse_bits_long(unsigned long val) +{ +#if defined(CONFIG_HAVE_ARCH_BITREVERSE) && BITS_PER_LONG == 32 + return bitrev8x4(val); +#else + val = fb_comp(val >> 1, val << 1, ~0UL / 3); + val = fb_comp(val >> 2, val << 2, ~0UL / 5); + return fb_comp(val >> 4, val << 4, ~0UL / 17); +#endif +} + +/* apply byte and bit reversals as necessary */ +static inline unsigned long fb_reverse_long(unsigned long val, + struct fb_reverse reverse) +{ + if (reverse.pixel) + val = fb_reverse_bits_long(val); + return reverse.byte ? swab_long(val) : val; +} + +/* calculate a pixel mask for the given reversal */ +static inline unsigned long fb_pixel_mask(int index, struct fb_reverse reverse) +{ +#ifdef FB_REV_PIXELS_IN_BYTE + if (reverse.byte) + return reverse.pixel ? fb_left(~0UL, index) : swab_long(fb_right(~0UL, index)); + else + return reverse.pixel ? swab_long(fb_left(~0UL, index)) : fb_right(~0UL, index); +#else + return reverse.byte ? swab_long(fb_right(~0UL, index)) : fb_right(~0UL, index); +#endif +} + + +/* + * initialise reversals based on info + * + * Normally the first byte is the low byte on little endian and in the high + * on big endian. If it's the other way around then that's reverse byte order. + * + * Normally the first pixel is the LSB on little endian and the MSB on big + * endian. If that's not the case that's reverse pixel order. + */ +static inline struct fb_reverse fb_reverse_init(struct fb_info *info) +{ + struct fb_reverse reverse; +#ifdef __LITTLE_ENDIAN + reverse.byte = fb_be_math(info) != 0; +#else + reverse.byte = fb_be_math(info) == 0; +#endif +#ifdef FB_REV_PIXELS_IN_BYTE + reverse.pixel = info->var.bits_per_pixel < BITS_PER_BYTE + && (info->var.nonstd & FB_NONSTD_REV_PIX_IN_B); +#else + reverse.pixel = false; +#endif + return reverse; +} + +#endif /* FB_DRAW_H */ |
