summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev/core/fb_draw.h
blob: 8eb13f7b3972ac3df9aa26a680c46e0f65e164d3 (plain)
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 */