xref: /linux-6.15/include/linux/fixp-arith.h (revision 5e7a0af7)
11a59d1b8SThomas Gleixner /* SPDX-License-Identifier: GPL-2.0-or-later */
2c8e1fb4aSAntonio Ospite #ifndef _FIXP_ARITH_H
3c8e1fb4aSAntonio Ospite #define _FIXP_ARITH_H
4c8e1fb4aSAntonio Ospite 
5*5e7a0af7SMatti Vaittinen #include <linux/bug.h>
6559addc2SMauro Carvalho Chehab #include <linux/math64.h>
7559addc2SMauro Carvalho Chehab 
8c8e1fb4aSAntonio Ospite /*
9c8e1fb4aSAntonio Ospite  * Simplistic fixed-point arithmetics.
10c8e1fb4aSAntonio Ospite  * Hmm, I'm probably duplicating some code :(
11c8e1fb4aSAntonio Ospite  *
12c8e1fb4aSAntonio Ospite  * Copyright (c) 2002 Johann Deneux
13c8e1fb4aSAntonio Ospite  */
14c8e1fb4aSAntonio Ospite 
15c8e1fb4aSAntonio Ospite /*
16c8e1fb4aSAntonio Ospite  *
17c8e1fb4aSAntonio Ospite  * Should you need to contact me, the author, you can do so by
18c8e1fb4aSAntonio Ospite  * e-mail - mail your message to <[email protected]>
19c8e1fb4aSAntonio Ospite  */
20c8e1fb4aSAntonio Ospite 
21c8e1fb4aSAntonio Ospite #include <linux/types.h>
22c8e1fb4aSAntonio Ospite 
23559addc2SMauro Carvalho Chehab static const s32 sin_table[] = {
24559addc2SMauro Carvalho Chehab 	0x00000000, 0x023be165, 0x04779632, 0x06b2f1d2, 0x08edc7b6, 0x0b27eb5c,
25559addc2SMauro Carvalho Chehab 	0x0d61304d, 0x0f996a26, 0x11d06c96, 0x14060b67, 0x163a1a7d, 0x186c6ddd,
26559addc2SMauro Carvalho Chehab 	0x1a9cd9ac, 0x1ccb3236, 0x1ef74bf2, 0x2120fb82, 0x234815ba, 0x256c6f9e,
27559addc2SMauro Carvalho Chehab 	0x278dde6e, 0x29ac379f, 0x2bc750e8, 0x2ddf003f, 0x2ff31bdd, 0x32037a44,
28559addc2SMauro Carvalho Chehab 	0x340ff241, 0x36185aee, 0x381c8bb5, 0x3a1c5c56, 0x3c17a4e7, 0x3e0e3ddb,
29559addc2SMauro Carvalho Chehab 	0x3fffffff, 0x41ecc483, 0x43d464fa, 0x45b6bb5d, 0x4793a20f, 0x496af3e1,
30559addc2SMauro Carvalho Chehab 	0x4b3c8c11, 0x4d084650, 0x4ecdfec6, 0x508d9210, 0x5246dd48, 0x53f9be04,
31559addc2SMauro Carvalho Chehab 	0x55a6125a, 0x574bb8e5, 0x58ea90c2, 0x5a827999, 0x5c135399, 0x5d9cff82,
32559addc2SMauro Carvalho Chehab 	0x5f1f5ea0, 0x609a52d1, 0x620dbe8a, 0x637984d3, 0x64dd894f, 0x6639b039,
33559addc2SMauro Carvalho Chehab 	0x678dde6d, 0x68d9f963, 0x6a1de735, 0x6b598ea1, 0x6c8cd70a, 0x6db7a879,
34559addc2SMauro Carvalho Chehab 	0x6ed9eba0, 0x6ff389de, 0x71046d3c, 0x720c8074, 0x730baeec, 0x7401e4bf,
35559addc2SMauro Carvalho Chehab 	0x74ef0ebb, 0x75d31a5f, 0x76adf5e5, 0x777f903b, 0x7847d908, 0x7906c0af,
36559addc2SMauro Carvalho Chehab 	0x79bc384c, 0x7a6831b8, 0x7b0a9f8c, 0x7ba3751c, 0x7c32a67c, 0x7cb82884,
37559addc2SMauro Carvalho Chehab 	0x7d33f0c8, 0x7da5f5a3, 0x7e0e2e31, 0x7e6c924f, 0x7ec11aa3, 0x7f0bc095,
38559addc2SMauro Carvalho Chehab 	0x7f4c7e52, 0x7f834ecf, 0x7fb02dc4, 0x7fd317b3, 0x7fec09e1, 0x7ffb025e,
39559addc2SMauro Carvalho Chehab 	0x7fffffff
40c8e1fb4aSAntonio Ospite };
41c8e1fb4aSAntonio Ospite 
42559addc2SMauro Carvalho Chehab /**
43559addc2SMauro Carvalho Chehab  * __fixp_sin32() returns the sin of an angle in degrees
44559addc2SMauro Carvalho Chehab  *
45559addc2SMauro Carvalho Chehab  * @degrees: angle, in degrees, from 0 to 360.
46559addc2SMauro Carvalho Chehab  *
47559addc2SMauro Carvalho Chehab  * The returned value ranges from -0x7fffffff to +0x7fffffff.
48c8e1fb4aSAntonio Ospite  */
__fixp_sin32(int degrees)49559addc2SMauro Carvalho Chehab static inline s32 __fixp_sin32(int degrees)
50c8e1fb4aSAntonio Ospite {
51559addc2SMauro Carvalho Chehab 	s32 ret;
52559addc2SMauro Carvalho Chehab 	bool negative = false;
53559addc2SMauro Carvalho Chehab 
54559addc2SMauro Carvalho Chehab 	if (degrees > 180) {
55559addc2SMauro Carvalho Chehab 		negative = true;
56559addc2SMauro Carvalho Chehab 		degrees -= 180;
57559addc2SMauro Carvalho Chehab 	}
58559addc2SMauro Carvalho Chehab 	if (degrees > 90)
59559addc2SMauro Carvalho Chehab 		degrees = 180 - degrees;
60559addc2SMauro Carvalho Chehab 
61559addc2SMauro Carvalho Chehab 	ret = sin_table[degrees];
62559addc2SMauro Carvalho Chehab 
63559addc2SMauro Carvalho Chehab 	return negative ? -ret : ret;
64c8e1fb4aSAntonio Ospite }
65c8e1fb4aSAntonio Ospite 
66559addc2SMauro Carvalho Chehab /**
67559addc2SMauro Carvalho Chehab  * fixp_sin32() returns the sin of an angle in degrees
68559addc2SMauro Carvalho Chehab  *
69559addc2SMauro Carvalho Chehab  * @degrees: angle, in degrees. The angle can be positive or negative
70559addc2SMauro Carvalho Chehab  *
71559addc2SMauro Carvalho Chehab  * The returned value ranges from -0x7fffffff to +0x7fffffff.
72559addc2SMauro Carvalho Chehab  */
fixp_sin32(int degrees)73559addc2SMauro Carvalho Chehab static inline s32 fixp_sin32(int degrees)
74c8e1fb4aSAntonio Ospite {
75559addc2SMauro Carvalho Chehab 	degrees = (degrees % 360 + 360) % 360;
76c8e1fb4aSAntonio Ospite 
77559addc2SMauro Carvalho Chehab 	return __fixp_sin32(degrees);
78c8e1fb4aSAntonio Ospite }
79c8e1fb4aSAntonio Ospite 
80559addc2SMauro Carvalho Chehab /* cos(x) = sin(x + 90 degrees) */
81559addc2SMauro Carvalho Chehab #define fixp_cos32(v) fixp_sin32((v) + 90)
82559addc2SMauro Carvalho Chehab 
83559addc2SMauro Carvalho Chehab /*
84559addc2SMauro Carvalho Chehab  * 16 bits variants
85559addc2SMauro Carvalho Chehab  *
86559addc2SMauro Carvalho Chehab  * The returned value ranges from -0x7fff to 0x7fff
87559addc2SMauro Carvalho Chehab  */
88559addc2SMauro Carvalho Chehab 
89559addc2SMauro Carvalho Chehab #define fixp_sin16(v) (fixp_sin32(v) >> 16)
90559addc2SMauro Carvalho Chehab #define fixp_cos16(v) (fixp_cos32(v) >> 16)
91559addc2SMauro Carvalho Chehab 
92559addc2SMauro Carvalho Chehab /**
93559addc2SMauro Carvalho Chehab  * fixp_sin32_rad() - calculates the sin of an angle in radians
94559addc2SMauro Carvalho Chehab  *
95559addc2SMauro Carvalho Chehab  * @radians: angle, in radians
96559addc2SMauro Carvalho Chehab  * @twopi: value to be used for 2*pi
97559addc2SMauro Carvalho Chehab  *
98559addc2SMauro Carvalho Chehab  * Provides a variant for the cases where just 360
99559addc2SMauro Carvalho Chehab  * values is not enough. This function uses linear
100559addc2SMauro Carvalho Chehab  * interpolation to a wider range of values given by
101559addc2SMauro Carvalho Chehab  * twopi var.
102559addc2SMauro Carvalho Chehab  *
103559addc2SMauro Carvalho Chehab  * Experimental tests gave a maximum difference of
104559addc2SMauro Carvalho Chehab  * 0.000038 between the value calculated by sin() and
105559addc2SMauro Carvalho Chehab  * the one produced by this function, when twopi is
106559addc2SMauro Carvalho Chehab  * equal to 360000. That seems to be enough precision
107559addc2SMauro Carvalho Chehab  * for practical purposes.
108559addc2SMauro Carvalho Chehab  *
109559addc2SMauro Carvalho Chehab  * Please notice that two high numbers for twopi could cause
110559addc2SMauro Carvalho Chehab  * overflows, so the routine will not allow values of twopi
111559addc2SMauro Carvalho Chehab  * bigger than 1^18.
112559addc2SMauro Carvalho Chehab  */
fixp_sin32_rad(u32 radians,u32 twopi)113559addc2SMauro Carvalho Chehab static inline s32 fixp_sin32_rad(u32 radians, u32 twopi)
114c8e1fb4aSAntonio Ospite {
115559addc2SMauro Carvalho Chehab 	int degrees;
116559addc2SMauro Carvalho Chehab 	s32 v1, v2, dx, dy;
117559addc2SMauro Carvalho Chehab 	s64 tmp;
118559addc2SMauro Carvalho Chehab 
119559addc2SMauro Carvalho Chehab 	/*
120559addc2SMauro Carvalho Chehab 	 * Avoid too large values for twopi, as we don't want overflows.
121559addc2SMauro Carvalho Chehab 	 */
122559addc2SMauro Carvalho Chehab 	BUG_ON(twopi > 1 << 18);
123559addc2SMauro Carvalho Chehab 
124559addc2SMauro Carvalho Chehab 	degrees = (radians * 360) / twopi;
125559addc2SMauro Carvalho Chehab 	tmp = radians - (degrees * twopi) / 360;
126559addc2SMauro Carvalho Chehab 
127559addc2SMauro Carvalho Chehab 	degrees = (degrees % 360 + 360) % 360;
128559addc2SMauro Carvalho Chehab 	v1 = __fixp_sin32(degrees);
129559addc2SMauro Carvalho Chehab 
130559addc2SMauro Carvalho Chehab 	v2 = fixp_sin32(degrees + 1);
131559addc2SMauro Carvalho Chehab 
132559addc2SMauro Carvalho Chehab 	dx = twopi / 360;
133559addc2SMauro Carvalho Chehab 	dy = v2 - v1;
134559addc2SMauro Carvalho Chehab 
135559addc2SMauro Carvalho Chehab 	tmp *= dy;
136559addc2SMauro Carvalho Chehab 
137559addc2SMauro Carvalho Chehab 	return v1 +  div_s64(tmp, dx);
138c8e1fb4aSAntonio Ospite }
139c8e1fb4aSAntonio Ospite 
140559addc2SMauro Carvalho Chehab /* cos(x) = sin(x + pi/2 radians) */
141559addc2SMauro Carvalho Chehab 
142559addc2SMauro Carvalho Chehab #define fixp_cos32_rad(rad, twopi)	\
143559addc2SMauro Carvalho Chehab 	fixp_sin32_rad(rad + twopi / 4, twopi)
144c8e1fb4aSAntonio Ospite 
1458d502ef6SCraig Tatlor /**
1468d502ef6SCraig Tatlor  * fixp_linear_interpolate() - interpolates a value from two known points
1478d502ef6SCraig Tatlor  *
1488d502ef6SCraig Tatlor  * @x0: x value of point 0
1498d502ef6SCraig Tatlor  * @y0: y value of point 0
1508d502ef6SCraig Tatlor  * @x1: x value of point 1
1518d502ef6SCraig Tatlor  * @y1: y value of point 1
1528d502ef6SCraig Tatlor  * @x: the linear interpolant
1538d502ef6SCraig Tatlor  */
fixp_linear_interpolate(int x0,int y0,int x1,int y1,int x)1548d502ef6SCraig Tatlor static inline int fixp_linear_interpolate(int x0, int y0, int x1, int y1, int x)
1558d502ef6SCraig Tatlor {
1568d502ef6SCraig Tatlor 	if (y0 == y1 || x == x0)
1578d502ef6SCraig Tatlor 		return y0;
1588d502ef6SCraig Tatlor 	if (x1 == x0 || x == x1)
1598d502ef6SCraig Tatlor 		return y1;
1608d502ef6SCraig Tatlor 
1618d502ef6SCraig Tatlor 	return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
1628d502ef6SCraig Tatlor }
1638d502ef6SCraig Tatlor 
164c8e1fb4aSAntonio Ospite #endif
165