aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/input/ff-memless.c18
-rw-r--r--drivers/media/usb/gspca/ov534.c11
-rw-r--r--include/linux/fixp-arith.h145
3 files changed, 125 insertions, 49 deletions
diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c
index 74c0d8c6002a..fcc6c3368182 100644
--- a/drivers/input/ff-memless.c
+++ b/drivers/input/ff-memless.c
@@ -237,6 +237,18 @@ static u16 ml_calculate_direction(u16 direction, u16 force,
237 (force + new_force)) << 1; 237 (force + new_force)) << 1;
238} 238}
239 239
240#define FRAC_N 8
241static inline s16 fixp_new16(s16 a)
242{
243 return ((s32)a) >> (16 - FRAC_N);
244}
245
246static inline s16 fixp_mult(s16 a, s16 b)
247{
248 a = ((s32)a * 0x100) / 0x7fff;
249 return ((s32)(a * b)) >> FRAC_N;
250}
251
240/* 252/*
241 * Combine two effects and apply gain. 253 * Combine two effects and apply gain.
242 */ 254 */
@@ -247,7 +259,7 @@ static void ml_combine_effects(struct ff_effect *effect,
247 struct ff_effect *new = state->effect; 259 struct ff_effect *new = state->effect;
248 unsigned int strong, weak, i; 260 unsigned int strong, weak, i;
249 int x, y; 261 int x, y;
250 fixp_t level; 262 s16 level;
251 263
252 switch (new->type) { 264 switch (new->type) {
253 case FF_CONSTANT: 265 case FF_CONSTANT:
@@ -255,8 +267,8 @@ static void ml_combine_effects(struct ff_effect *effect,
255 level = fixp_new16(apply_envelope(state, 267 level = fixp_new16(apply_envelope(state,
256 new->u.constant.level, 268 new->u.constant.level,
257 &new->u.constant.envelope)); 269 &new->u.constant.envelope));
258 x = fixp_mult(fixp_sin(i), level) * gain / 0xffff; 270 x = fixp_mult(fixp_sin16(i), level) * gain / 0xffff;
259 y = fixp_mult(-fixp_cos(i), level) * gain / 0xffff; 271 y = fixp_mult(-fixp_cos16(i), level) * gain / 0xffff;
260 /* 272 /*
261 * here we abuse ff_ramp to hold x and y of constant force 273 * here we abuse ff_ramp to hold x and y of constant force
262 * If in future any driver wants something else than x and y 274 * If in future any driver wants something else than x and y
diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c
index a9c866d6d82d..146071b8e116 100644
--- a/drivers/media/usb/gspca/ov534.c
+++ b/drivers/media/usb/gspca/ov534.c
@@ -816,21 +816,16 @@ static void sethue(struct gspca_dev *gspca_dev, s32 val)
816 s16 huesin; 816 s16 huesin;
817 s16 huecos; 817 s16 huecos;
818 818
819 /* fixp_sin and fixp_cos accept only positive values, while
820 * our val is between -90 and 90
821 */
822 val += 360;
823
824 /* According to the datasheet the registers expect HUESIN and 819 /* According to the datasheet the registers expect HUESIN and
825 * HUECOS to be the result of the trigonometric functions, 820 * HUECOS to be the result of the trigonometric functions,
826 * scaled by 0x80. 821 * scaled by 0x80.
827 * 822 *
828 * The 0x100 here represents the maximun absolute value 823 * The 0x7fff here represents the maximum absolute value
829 * returned byt fixp_sin and fixp_cos, so the scaling will 824 * returned byt fixp_sin and fixp_cos, so the scaling will
830 * consider the result like in the interval [-1.0, 1.0]. 825 * consider the result like in the interval [-1.0, 1.0].
831 */ 826 */
832 huesin = fixp_sin(val) * 0x80 / 0x100; 827 huesin = fixp_sin16(val) * 0x80 / 0x7fff;
833 huecos = fixp_cos(val) * 0x80 / 0x100; 828 huecos = fixp_cos16(val) * 0x80 / 0x7fff;
834 829
835 if (huesin < 0) { 830 if (huesin < 0) {
836 sccb_reg_write(gspca_dev, 0xab, 831 sccb_reg_write(gspca_dev, 0xab,
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