summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev/core/fb_draw.h
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/fbdev/core/fb_draw.h')
-rw-r--r--drivers/video/fbdev/core/fb_draw.h163
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 */