1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 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 */
|