Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: MIT
2//
3// Copyright 2024 Advanced Micro Devices, Inc.
4
5#include "spl_fixpt31_32.h"
6
7static const struct spl_fixed31_32 spl_fixpt_two_pi = { 26986075409LL };
8static const struct spl_fixed31_32 spl_fixpt_ln2 = { 2977044471LL };
9static const struct spl_fixed31_32 spl_fixpt_ln2_div_2 = { 1488522236LL };
10
11static inline unsigned long long abs_i64(
12 long long arg)
13{
14 if (arg > 0)
15 return (unsigned long long)arg;
16 else
17 return (unsigned long long)(-arg);
18}
19
20/*
21 * @brief
22 * result = dividend / divisor
23 * *remainder = dividend % divisor
24 */
25static inline unsigned long long spl_complete_integer_division_u64(
26 unsigned long long dividend,
27 unsigned long long divisor,
28 unsigned long long *remainder)
29{
30 unsigned long long result;
31
32 result = spl_div64_u64_rem(dividend, divisor, remainder);
33
34 return result;
35}
36
37
38#define FRACTIONAL_PART_MASK \
39 ((1ULL << FIXED31_32_BITS_PER_FRACTIONAL_PART) - 1)
40
41#define GET_INTEGER_PART(x) \
42 ((x) >> FIXED31_32_BITS_PER_FRACTIONAL_PART)
43
44#define GET_FRACTIONAL_PART(x) \
45 (FRACTIONAL_PART_MASK & (x))
46
47struct spl_fixed31_32 SPL_NAMESPACE(spl_fixpt_from_fraction(
48 long long numerator, long long denominator))
49{
50 struct spl_fixed31_32 res;
51
52 bool arg1_negative = numerator < 0;
53 bool arg2_negative = denominator < 0;
54
55 unsigned long long arg1_value = arg1_negative ? -numerator : numerator;
56 unsigned long long arg2_value = arg2_negative ? -denominator : denominator;
57
58 unsigned long long remainder;
59
60 /* determine integer part */
61
62 unsigned long long res_value = spl_complete_integer_division_u64(
63 arg1_value, arg2_value, &remainder);
64
65 SPL_ASSERT(res_value <= (unsigned long long)LONG_MAX);
66
67 /* determine fractional part */
68 {
69 unsigned int i = FIXED31_32_BITS_PER_FRACTIONAL_PART;
70
71 do {
72 remainder <<= 1;
73
74 res_value <<= 1;
75
76 if (remainder >= arg2_value) {
77 res_value |= 1;
78 remainder -= arg2_value;
79 }
80 } while (--i != 0);
81 }
82
83 /* round up LSB */
84 {
85 unsigned long long summand = (remainder << 1) >= arg2_value;
86
87 SPL_ASSERT(res_value <= (unsigned long long)LLONG_MAX - summand);
88
89 res_value += summand;
90 }
91
92 res.value = (long long)res_value;
93
94 if (arg1_negative ^ arg2_negative)
95 res.value = -res.value;
96
97 return res;
98}
99
100struct spl_fixed31_32 SPL_NAMESPACE(spl_fixpt_mul(
101 struct spl_fixed31_32 arg1, struct spl_fixed31_32 arg2))
102{
103 struct spl_fixed31_32 res;
104
105 bool arg1_negative = arg1.value < 0;
106 bool arg2_negative = arg2.value < 0;
107
108 unsigned long long arg1_value = arg1_negative ? -arg1.value : arg1.value;
109 unsigned long long arg2_value = arg2_negative ? -arg2.value : arg2.value;
110
111 unsigned long long arg1_int = GET_INTEGER_PART(arg1_value);
112 unsigned long long arg2_int = GET_INTEGER_PART(arg2_value);
113
114 unsigned long long arg1_fra = GET_FRACTIONAL_PART(arg1_value);
115 unsigned long long arg2_fra = GET_FRACTIONAL_PART(arg2_value);
116
117 unsigned long long tmp;
118
119 res.value = arg1_int * arg2_int;
120
121 SPL_ASSERT(res.value <= (long long)LONG_MAX);
122
123 res.value <<= FIXED31_32_BITS_PER_FRACTIONAL_PART;
124
125 tmp = arg1_int * arg2_fra;
126
127 SPL_ASSERT(tmp <= (unsigned long long)(LLONG_MAX - res.value));
128
129 res.value += tmp;
130
131 tmp = arg2_int * arg1_fra;
132
133 SPL_ASSERT(tmp <= (unsigned long long)(LLONG_MAX - res.value));
134
135 res.value += tmp;
136
137 tmp = arg1_fra * arg2_fra;
138
139 tmp = (tmp >> FIXED31_32_BITS_PER_FRACTIONAL_PART) +
140 (tmp >= (unsigned long long)spl_fixpt_half.value);
141
142 SPL_ASSERT(tmp <= (unsigned long long)(LLONG_MAX - res.value));
143
144 res.value += tmp;
145
146 if (arg1_negative ^ arg2_negative)
147 res.value = -res.value;
148
149 return res;
150}
151
152struct spl_fixed31_32 SPL_NAMESPACE(spl_fixpt_sqr(struct spl_fixed31_32 arg))
153{
154 struct spl_fixed31_32 res;
155
156 unsigned long long arg_value = abs_i64(arg.value);
157
158 unsigned long long arg_int = GET_INTEGER_PART(arg_value);
159
160 unsigned long long arg_fra = GET_FRACTIONAL_PART(arg_value);
161
162 unsigned long long tmp;
163
164 res.value = arg_int * arg_int;
165
166 SPL_ASSERT(res.value <= (long long)LONG_MAX);
167
168 res.value <<= FIXED31_32_BITS_PER_FRACTIONAL_PART;
169
170 tmp = arg_int * arg_fra;
171
172 SPL_ASSERT(tmp <= (unsigned long long)(LLONG_MAX - res.value));
173
174 res.value += tmp;
175
176 SPL_ASSERT(tmp <= (unsigned long long)(LLONG_MAX - res.value));
177
178 res.value += tmp;
179
180 tmp = arg_fra * arg_fra;
181
182 tmp = (tmp >> FIXED31_32_BITS_PER_FRACTIONAL_PART) +
183 (tmp >= (unsigned long long)spl_fixpt_half.value);
184
185 SPL_ASSERT(tmp <= (unsigned long long)(LLONG_MAX - res.value));
186
187 res.value += tmp;
188
189 return res;
190}
191
192struct spl_fixed31_32 SPL_NAMESPACE(spl_fixpt_recip(struct spl_fixed31_32 arg))
193{
194 /*
195 * @note
196 * Good idea to use Newton's method
197 */
198
199 return SPL_NAMESPACE(spl_fixpt_from_fraction(
200 spl_fixpt_one.value,
201 arg.value));
202}
203
204struct spl_fixed31_32 SPL_NAMESPACE(spl_fixpt_sinc(struct spl_fixed31_32 arg))
205{
206 struct spl_fixed31_32 square;
207
208 struct spl_fixed31_32 res = spl_fixpt_one;
209
210 int n = 27;
211
212 struct spl_fixed31_32 arg_norm = arg;
213
214 if (spl_fixpt_le(
215 spl_fixpt_two_pi,
216 spl_fixpt_abs(arg))) {
217 arg_norm = spl_fixpt_sub(
218 arg_norm,
219 spl_fixpt_mul_int(
220 spl_fixpt_two_pi,
221 (int)spl_div64_s64(
222 arg_norm.value,
223 spl_fixpt_two_pi.value)));
224 }
225
226 square = SPL_NAMESPACE(spl_fixpt_sqr(arg_norm));
227
228 do {
229 res = spl_fixpt_sub(
230 spl_fixpt_one,
231 spl_fixpt_div_int(
232 SPL_NAMESPACE(spl_fixpt_mul(
233 square,
234 res)),
235 n * (n - 1)));
236
237 n -= 2;
238 } while (n > 2);
239
240 if (arg.value != arg_norm.value)
241 res = spl_fixpt_div(
242 SPL_NAMESPACE(spl_fixpt_mul(res, arg_norm)),
243 arg);
244
245 return res;
246}
247
248struct spl_fixed31_32 SPL_NAMESPACE(spl_fixpt_sin(struct spl_fixed31_32 arg))
249{
250 return SPL_NAMESPACE(spl_fixpt_mul(
251 arg,
252 SPL_NAMESPACE(spl_fixpt_sinc(arg))));
253}
254
255struct spl_fixed31_32 SPL_NAMESPACE(spl_fixpt_cos(struct spl_fixed31_32 arg))
256{
257 /* TODO implement argument normalization */
258
259 const struct spl_fixed31_32 square = SPL_NAMESPACE(spl_fixpt_sqr(arg));
260
261 struct spl_fixed31_32 res = spl_fixpt_one;
262
263 int n = 26;
264
265 do {
266 res = spl_fixpt_sub(
267 spl_fixpt_one,
268 spl_fixpt_div_int(
269 SPL_NAMESPACE(spl_fixpt_mul(
270 square,
271 res)),
272 n * (n - 1)));
273
274 n -= 2;
275 } while (n != 0);
276
277 return res;
278}
279
280/*
281 * @brief
282 * result = exp(arg),
283 * where abs(arg) < 1
284 *
285 * Calculated as Taylor series.
286 */
287static struct spl_fixed31_32 spl_fixed31_32_exp_from_taylor_series(struct spl_fixed31_32 arg)
288{
289 unsigned int n = 9;
290
291 struct spl_fixed31_32 res = SPL_NAMESPACE(spl_fixpt_from_fraction(
292 n + 2,
293 n + 1));
294 /* TODO find correct res */
295
296 SPL_ASSERT(spl_fixpt_lt(arg, spl_fixpt_one));
297
298 do
299 res = spl_fixpt_add(
300 spl_fixpt_one,
301 spl_fixpt_div_int(
302 SPL_NAMESPACE(spl_fixpt_mul(
303 arg,
304 res)),
305 n));
306 while (--n != 1);
307
308 return spl_fixpt_add(
309 spl_fixpt_one,
310 SPL_NAMESPACE(spl_fixpt_mul(
311 arg,
312 res)));
313}
314
315struct spl_fixed31_32 SPL_NAMESPACE(spl_fixpt_exp(struct spl_fixed31_32 arg))
316{
317 /*
318 * @brief
319 * Main equation is:
320 * exp(x) = exp(r + m * ln(2)) = (1 << m) * exp(r),
321 * where m = round(x / ln(2)), r = x - m * ln(2)
322 */
323
324 if (spl_fixpt_le(
325 spl_fixpt_ln2_div_2,
326 spl_fixpt_abs(arg))) {
327 int m = spl_fixpt_round(
328 spl_fixpt_div(
329 arg,
330 spl_fixpt_ln2));
331
332 struct spl_fixed31_32 r = spl_fixpt_sub(
333 arg,
334 spl_fixpt_mul_int(
335 spl_fixpt_ln2,
336 m));
337
338 SPL_ASSERT(m != 0);
339
340 SPL_ASSERT(spl_fixpt_lt(
341 spl_fixpt_abs(r),
342 spl_fixpt_one));
343
344 if (m > 0)
345 return spl_fixpt_shl(
346 spl_fixed31_32_exp_from_taylor_series(r),
347 (unsigned int)m);
348 else
349 return spl_fixpt_div_int(
350 spl_fixed31_32_exp_from_taylor_series(r),
351 1LL << -m);
352 } else if (arg.value != 0)
353 return spl_fixed31_32_exp_from_taylor_series(arg);
354 else
355 return spl_fixpt_one;
356}
357
358struct spl_fixed31_32 SPL_NAMESPACE(spl_fixpt_log(struct spl_fixed31_32 arg))
359{
360 struct spl_fixed31_32 res = spl_fixpt_neg(spl_fixpt_one);
361 /* TODO improve 1st estimation */
362
363 struct spl_fixed31_32 error;
364
365 SPL_ASSERT(arg.value > 0);
366 /* TODO if arg is negative, return NaN */
367 /* TODO if arg is zero, return -INF */
368
369 do {
370 struct spl_fixed31_32 res1 = spl_fixpt_add(
371 spl_fixpt_sub(
372 res,
373 spl_fixpt_one),
374 spl_fixpt_div(
375 arg,
376 SPL_NAMESPACE(spl_fixpt_exp(res))));
377
378 error = spl_fixpt_sub(
379 res,
380 res1);
381
382 res = res1;
383 /* TODO determine max_allowed_error based on quality of exp() */
384 } while (abs_i64(error.value) > 100ULL);
385
386 return res;
387}
388
389
390/* this function is a generic helper to translate fixed point value to
391 * specified integer format that will consist of integer_bits integer part and
392 * fractional_bits fractional part. For example it is used in
393 * spl_fixpt_u2d19 to receive 2 bits integer part and 19 bits fractional
394 * part in 32 bits. It is used in hw programming (scaler)
395 */
396
397static inline unsigned int spl_ux_dy(
398 long long value,
399 unsigned int integer_bits,
400 unsigned int fractional_bits)
401{
402 /* 1. create mask of integer part */
403 unsigned int result = (1 << integer_bits) - 1;
404 /* 2. mask out fractional part */
405 unsigned int fractional_part = FRACTIONAL_PART_MASK & value;
406 /* 3. shrink fixed point integer part to be of integer_bits width*/
407 result &= GET_INTEGER_PART(value);
408 /* 4. make space for fractional part to be filled in after integer */
409 result <<= fractional_bits;
410 /* 5. shrink fixed point fractional part to of fractional_bits width*/
411 fractional_part >>= FIXED31_32_BITS_PER_FRACTIONAL_PART - fractional_bits;
412 /* 6. merge the result */
413 return result | fractional_part;
414}
415
416static inline unsigned int spl_clamp_ux_dy(
417 long long value,
418 unsigned int integer_bits,
419 unsigned int fractional_bits,
420 unsigned int min_clamp)
421{
422 unsigned int truncated_val = spl_ux_dy(value, integer_bits, fractional_bits);
423
424 if (value >= (1LL << (integer_bits + FIXED31_32_BITS_PER_FRACTIONAL_PART)))
425 return (1 << (integer_bits + fractional_bits)) - 1;
426 else if (truncated_val > min_clamp)
427 return truncated_val;
428 else
429 return min_clamp;
430}
431
432unsigned int SPL_NAMESPACE(spl_fixpt_u4d19(struct spl_fixed31_32 arg))
433{
434 return spl_ux_dy(arg.value, 4, 19);
435}
436
437unsigned int SPL_NAMESPACE(spl_fixpt_u3d19(struct spl_fixed31_32 arg))
438{
439 return spl_ux_dy(arg.value, 3, 19);
440}
441
442unsigned int SPL_NAMESPACE(spl_fixpt_u2d19(struct spl_fixed31_32 arg))
443{
444 return spl_ux_dy(arg.value, 2, 19);
445}
446
447unsigned int SPL_NAMESPACE(spl_fixpt_u0d19(struct spl_fixed31_32 arg))
448{
449 return spl_ux_dy(arg.value, 0, 19);
450}
451
452unsigned int SPL_NAMESPACE(spl_fixpt_clamp_u0d14(struct spl_fixed31_32 arg))
453{
454 return spl_clamp_ux_dy(arg.value, 0, 14, 1);
455}
456
457unsigned int SPL_NAMESPACE(spl_fixpt_clamp_u0d10(struct spl_fixed31_32 arg))
458{
459 return spl_clamp_ux_dy(arg.value, 0, 10, 1);
460}
461
462int SPL_NAMESPACE(spl_fixpt_s4d19(struct spl_fixed31_32 arg))
463{
464 if (arg.value < 0)
465 return -(int)spl_ux_dy(spl_fixpt_abs(arg).value, 4, 19);
466 else
467 return spl_ux_dy(arg.value, 4, 19);
468}
469
470struct spl_fixed31_32 SPL_NAMESPACE(spl_fixpt_from_ux_dy(unsigned int value,
471 unsigned int integer_bits,
472 unsigned int fractional_bits))
473{
474 struct spl_fixed31_32 fixpt_value = spl_fixpt_zero;
475 struct spl_fixed31_32 fixpt_int_value = spl_fixpt_zero;
476 long long frac_mask = ((long long)1 << (long long)integer_bits) - 1;
477
478 fixpt_value.value = (long long)value << (FIXED31_32_BITS_PER_FRACTIONAL_PART - fractional_bits);
479 frac_mask = frac_mask << fractional_bits;
480 fixpt_int_value.value = value & frac_mask;
481 fixpt_int_value.value <<= (FIXED31_32_BITS_PER_FRACTIONAL_PART - fractional_bits);
482 fixpt_value.value |= fixpt_int_value.value;
483 return fixpt_value;
484}
485
486struct spl_fixed31_32 SPL_NAMESPACE(spl_fixpt_from_int_dy(unsigned int int_value,
487 unsigned int frac_value,
488 unsigned int integer_bits,
489 unsigned int fractional_bits))
490{
491 struct spl_fixed31_32 fixpt_value = spl_fixpt_from_int(int_value);
492
493 fixpt_value.value |= (long long)frac_value << (FIXED31_32_BITS_PER_FRACTIONAL_PART - fractional_bits);
494 return fixpt_value;
495}