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 */ 7 #ifndef _FB_DRAW_H 8 #define _FB_DRAW_H 9 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 14 15 /* move the address pointer by the number of words */ 16 static inline void fb_address_move_long(struct fb_address *adr, int offset) 17 { 18 adr->address += offset * (BITS_PER_LONG / BITS_PER_BYTE); 19 } 20 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) 23 { 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; 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; 82 #else 83 return value >> index; 84 #endif 85 } 86 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; 92 #else 93 return value << index; 94 #endif 95 } 96 97 /* reversal options */ 98 struct fb_reverse { 99 bool byte, pixel; 100 }; 101 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); 111 #endif 112 } 113 114 /* apply byte and bit reversals as necessary */ 115 static inline unsigned long fb_reverse_long(unsigned long val, 116 struct fb_reverse reverse) 117 { 118 if (reverse.pixel) 119 val = fb_reverse_bits_long(val); 120 return reverse.byte ? swab_long(val) : val; 121 } 122 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); 133 #endif 134 } 135 136 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) 147 { 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; 161 } 162 163 #endif /* FB_DRAW_H */ 164