aboutsummaryrefslogtreecommitdiffstats
path: root/include/linux/fixp-arith.h
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@osg.samsung.com>2015-02-04 04:07:30 -0500
committerMauro Carvalho Chehab <mchehab@osg.samsung.com>2015-03-02 12:50:06 -0500
commit559addc25b00ff3a40eff03a0b3873c2b6d726f8 (patch)
tree1bad1442f4bd55fe063f6b04ce00573927fc9517 /include/linux/fixp-arith.h
parent96df988bb935959e6380c2333e03911839079ee1 (diff)
[media] fixp-arith: replace sin/cos table by a better precision one
The cos table used at fixp-arith.h has only 8 bits of precision. That causes problems if it is reused on other drivers. As some media drivers require a higher precision sin/cos implementation, replace the current implementation by one that will provide 32 bits precision. The values generated by the new implementation matches the 32 bit precision of glibc's sin for an angle measured in integer degrees. It also provides support for fractional angles via linear interpolation. On experimental calculus, when used a table with a 0.001 degree angle, the maximum error for sin is 0.000038, which is likely good enough for practical purposes. There are some logic there that seems to be specific to the usage inside ff-memless.c. Move those logic to there, as they're not needed elsewhere. Cc: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com> Signed-off-by: Prashant Laddha <prladdha@cisco.com> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Diffstat (limited to 'include/linux/fixp-arith.h')
-rw-r--r--include/linux/fixp-arith.h145
1 files changed, 107 insertions, 38 deletions
diff --git a/include/linux/fixp-arith.h b/include/linux/fixp-arith.h
index 3089d7382325..d4686fe1cac7 100644
--- a/include/linux/fixp-arith.h
+++ b/include/linux/fixp-arith.h
@@ -1,6 +1,8 @@
1#ifndef _FIXP_ARITH_H 1#ifndef _FIXP_ARITH_H
2#define _FIXP_ARITH_H 2#define _FIXP_ARITH_H
3 3
4#include <linux/math64.h>
5
4/* 6/*
5 * Simplistic fixed-point arithmetics. 7 * Simplistic fixed-point arithmetics.
6 * Hmm, I'm probably duplicating some code :( 8 * Hmm, I'm probably duplicating some code :(
@@ -29,59 +31,126 @@
29 31
30#include <linux/types.h> 32#include <linux/types.h>
31 33
32/* The type representing fixed-point values */ 34static const s32 sin_table[] = {
33typedef s16 fixp_t; 35 0x00000000, 0x023be165, 0x04779632, 0x06b2f1d2, 0x08edc7b6, 0x0b27eb5c,
36 0x0d61304d, 0x0f996a26, 0x11d06c96, 0x14060b67, 0x163a1a7d, 0x186c6ddd,
37 0x1a9cd9ac, 0x1ccb3236, 0x1ef74bf2, 0x2120fb82, 0x234815ba, 0x256c6f9e,
38 0x278dde6e, 0x29ac379f, 0x2bc750e8, 0x2ddf003f, 0x2ff31bdd, 0x32037a44,
39 0x340ff241, 0x36185aee, 0x381c8bb5, 0x3a1c5c56, 0x3c17a4e7, 0x3e0e3ddb,
40 0x3fffffff, 0x41ecc483, 0x43d464fa, 0x45b6bb5d, 0x4793a20f, 0x496af3e1,
41 0x4b3c8c11, 0x4d084650, 0x4ecdfec6, 0x508d9210, 0x5246dd48, 0x53f9be04,
42 0x55a6125a, 0x574bb8e5, 0x58ea90c2, 0x5a827999, 0x5c135399, 0x5d9cff82,
43 0x5f1f5ea0, 0x609a52d1, 0x620dbe8a, 0x637984d3, 0x64dd894f, 0x6639b039,
44 0x678dde6d, 0x68d9f963, 0x6a1de735, 0x6b598ea1, 0x6c8cd70a, 0x6db7a879,
45 0x6ed9eba0, 0x6ff389de, 0x71046d3c, 0x720c8074, 0x730baeec, 0x7401e4bf,
46 0x74ef0ebb, 0x75d31a5f, 0x76adf5e5, 0x777f903b, 0x7847d908, 0x7906c0af,
47 0x79bc384c, 0x7a6831b8, 0x7b0a9f8c, 0x7ba3751c, 0x7c32a67c, 0x7cb82884,
48 0x7d33f0c8, 0x7da5f5a3, 0x7e0e2e31, 0x7e6c924f, 0x7ec11aa3, 0x7f0bc095,
49 0x7f4c7e52, 0x7f834ecf, 0x7fb02dc4, 0x7fd317b3, 0x7fec09e1, 0x7ffb025e,
50 0x7fffffff
51};
34 52
35#define FRAC_N 8 53/**
36#define FRAC_MASK ((1<<FRAC_N)-1) 54 * __fixp_sin32() returns the sin of an angle in degrees
55 *
56 * @degrees: angle, in degrees, from 0 to 360.
57 *
58 * The returned value ranges from -0x7fffffff to +0x7fffffff.
59 */
60static inline s32 __fixp_sin32(int degrees)
61{
62 s32 ret;
63 bool negative = false;
37 64
38/* Not to be used directly. Use fixp_{cos,sin} */ 65 if (degrees > 180) {
39static const fixp_t cos_table[46] = { 66 negative = true;
40 0x0100, 0x00FF, 0x00FF, 0x00FE, 0x00FD, 0x00FC, 0x00FA, 0x00F8, 67 degrees -= 180;
41 0x00F6, 0x00F3, 0x00F0, 0x00ED, 0x00E9, 0x00E6, 0x00E2, 0x00DD, 68 }
42 0x00D9, 0x00D4, 0x00CF, 0x00C9, 0x00C4, 0x00BE, 0x00B8, 0x00B1, 69 if (degrees > 90)
43 0x00AB, 0x00A4, 0x009D, 0x0096, 0x008F, 0x0087, 0x0080, 0x0078, 70 degrees = 180 - degrees;
44 0x0070, 0x0068, 0x005F, 0x0057, 0x004F, 0x0046, 0x003D, 0x0035,
45 0x002C, 0x0023, 0x001A, 0x0011, 0x0008, 0x0000
46};
47 71
72 ret = sin_table[degrees];
48 73
49/* a: 123 -> 123.0 */ 74 return negative ? -ret : ret;
50static inline fixp_t fixp_new(s16 a)
51{
52 return a<<FRAC_N;
53} 75}
54 76
55/* a: 0xFFFF -> -1.0 77/**
56 0x8000 -> 1.0 78 * fixp_sin32() returns the sin of an angle in degrees
57 0x0000 -> 0.0 79 *
58*/ 80 * @degrees: angle, in degrees. The angle can be positive or negative
59static inline fixp_t fixp_new16(s16 a) 81 *
82 * The returned value ranges from -0x7fffffff to +0x7fffffff.
83 */
84static inline s32 fixp_sin32(int degrees)
60{ 85{
61 return ((s32)a)>>(16-FRAC_N); 86 degrees = (degrees % 360 + 360) % 360;
87
88 return __fixp_sin32(degrees);
62} 89}
63 90
64static inline fixp_t fixp_cos(unsigned int degrees) 91/* cos(x) = sin(x + 90 degrees) */
92#define fixp_cos32(v) fixp_sin32((v) + 90)
93
94/*
95 * 16 bits variants
96 *
97 * The returned value ranges from -0x7fff to 0x7fff
98 */
99
100#define fixp_sin16(v) (fixp_sin32(v) >> 16)
101#define fixp_cos16(v) (fixp_cos32(v) >> 16)
102
103/**
104 * fixp_sin32_rad() - calculates the sin of an angle in radians
105 *
106 * @radians: angle, in radians
107 * @twopi: value to be used for 2*pi
108 *
109 * Provides a variant for the cases where just 360
110 * values is not enough. This function uses linear
111 * interpolation to a wider range of values given by
112 * twopi var.
113 *
114 * Experimental tests gave a maximum difference of
115 * 0.000038 between the value calculated by sin() and
116 * the one produced by this function, when twopi is
117 * equal to 360000. That seems to be enough precision
118 * for practical purposes.
119 *
120 * Please notice that two high numbers for twopi could cause
121 * overflows, so the routine will not allow values of twopi
122 * bigger than 1^18.
123 */
124static inline s32 fixp_sin32_rad(u32 radians, u32 twopi)
65{ 125{
66 int quadrant = (degrees / 90) & 3; 126 int degrees;
67 unsigned int i = degrees % 90; 127 s32 v1, v2, dx, dy;
128 s64 tmp;
68 129
69 if (quadrant == 1 || quadrant == 3) 130 /*
70 i = 90 - i; 131 * Avoid too large values for twopi, as we don't want overflows.
132 */
133 BUG_ON(twopi > 1 << 18);
71 134
72 i >>= 1; 135 degrees = (radians * 360) / twopi;
136 tmp = radians - (degrees * twopi) / 360;
73 137
74 return (quadrant == 1 || quadrant == 2)? -cos_table[i] : cos_table[i]; 138 degrees = (degrees % 360 + 360) % 360;
75} 139 v1 = __fixp_sin32(degrees);
76 140
77static inline fixp_t fixp_sin(unsigned int degrees) 141 v2 = fixp_sin32(degrees + 1);
78{
79 return -fixp_cos(degrees + 90);
80}
81 142
82static inline fixp_t fixp_mult(fixp_t a, fixp_t b) 143 dx = twopi / 360;
83{ 144 dy = v2 - v1;
84 return ((s32)(a*b))>>FRAC_N; 145
146 tmp *= dy;
147
148 return v1 + div_s64(tmp, dx);
85} 149}
86 150
151/* cos(x) = sin(x + pi/2 radians) */
152
153#define fixp_cos32_rad(rad, twopi) \
154 fixp_sin32_rad(rad + twopi / 4, twopi)
155
87#endif 156#endif