0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025 #ifndef DRM_FIXED_H
0026 #define DRM_FIXED_H
0027
0028 #include <linux/math64.h>
0029
0030 typedef union dfixed {
0031 u32 full;
0032 } fixed20_12;
0033
0034
0035 #define dfixed_const(A) (u32)(((A) << 12))
0036 #define dfixed_const_half(A) (u32)(((A) << 12) + 2048)
0037 #define dfixed_const_666(A) (u32)(((A) << 12) + 2731)
0038 #define dfixed_const_8(A) (u32)(((A) << 12) + 3277)
0039 #define dfixed_mul(A, B) ((u64)((u64)(A).full * (B).full + 2048) >> 12)
0040 #define dfixed_init(A) { .full = dfixed_const((A)) }
0041 #define dfixed_init_half(A) { .full = dfixed_const_half((A)) }
0042 #define dfixed_trunc(A) ((A).full >> 12)
0043 #define dfixed_frac(A) ((A).full & ((1 << 12) - 1))
0044
0045 static inline u32 dfixed_floor(fixed20_12 A)
0046 {
0047 u32 non_frac = dfixed_trunc(A);
0048
0049 return dfixed_const(non_frac);
0050 }
0051
0052 static inline u32 dfixed_ceil(fixed20_12 A)
0053 {
0054 u32 non_frac = dfixed_trunc(A);
0055
0056 if (A.full > dfixed_const(non_frac))
0057 return dfixed_const(non_frac + 1);
0058 else
0059 return dfixed_const(non_frac);
0060 }
0061
0062 static inline u32 dfixed_div(fixed20_12 A, fixed20_12 B)
0063 {
0064 u64 tmp = ((u64)A.full << 13);
0065
0066 do_div(tmp, B.full);
0067 tmp += 1;
0068 tmp /= 2;
0069 return lower_32_bits(tmp);
0070 }
0071
0072 #define DRM_FIXED_POINT 32
0073 #define DRM_FIXED_ONE (1ULL << DRM_FIXED_POINT)
0074 #define DRM_FIXED_DECIMAL_MASK (DRM_FIXED_ONE - 1)
0075 #define DRM_FIXED_DIGITS_MASK (~DRM_FIXED_DECIMAL_MASK)
0076 #define DRM_FIXED_EPSILON 1LL
0077 #define DRM_FIXED_ALMOST_ONE (DRM_FIXED_ONE - DRM_FIXED_EPSILON)
0078
0079 static inline s64 drm_int2fixp(int a)
0080 {
0081 return ((s64)a) << DRM_FIXED_POINT;
0082 }
0083
0084 static inline int drm_fixp2int(s64 a)
0085 {
0086 return ((s64)a) >> DRM_FIXED_POINT;
0087 }
0088
0089 static inline int drm_fixp2int_ceil(s64 a)
0090 {
0091 if (a > 0)
0092 return drm_fixp2int(a + DRM_FIXED_ALMOST_ONE);
0093 else
0094 return drm_fixp2int(a - DRM_FIXED_ALMOST_ONE);
0095 }
0096
0097 static inline unsigned drm_fixp_msbset(s64 a)
0098 {
0099 unsigned shift, sign = (a >> 63) & 1;
0100
0101 for (shift = 62; shift > 0; --shift)
0102 if (((a >> shift) & 1) != sign)
0103 return shift;
0104
0105 return 0;
0106 }
0107
0108 static inline s64 drm_fixp_mul(s64 a, s64 b)
0109 {
0110 unsigned shift = drm_fixp_msbset(a) + drm_fixp_msbset(b);
0111 s64 result;
0112
0113 if (shift > 61) {
0114 shift = shift - 61;
0115 a >>= (shift >> 1) + (shift & 1);
0116 b >>= shift >> 1;
0117 } else
0118 shift = 0;
0119
0120 result = a * b;
0121
0122 if (shift > DRM_FIXED_POINT)
0123 return result << (shift - DRM_FIXED_POINT);
0124
0125 if (shift < DRM_FIXED_POINT)
0126 return result >> (DRM_FIXED_POINT - shift);
0127
0128 return result;
0129 }
0130
0131 static inline s64 drm_fixp_div(s64 a, s64 b)
0132 {
0133 unsigned shift = 62 - drm_fixp_msbset(a);
0134 s64 result;
0135
0136 a <<= shift;
0137
0138 if (shift < DRM_FIXED_POINT)
0139 b >>= (DRM_FIXED_POINT - shift);
0140
0141 result = div64_s64(a, b);
0142
0143 if (shift > DRM_FIXED_POINT)
0144 return result >> (shift - DRM_FIXED_POINT);
0145
0146 return result;
0147 }
0148
0149 static inline s64 drm_fixp_from_fraction(s64 a, s64 b)
0150 {
0151 s64 res;
0152 bool a_neg = a < 0;
0153 bool b_neg = b < 0;
0154 u64 a_abs = a_neg ? -a : a;
0155 u64 b_abs = b_neg ? -b : b;
0156 u64 rem;
0157
0158
0159 u64 res_abs = div64_u64_rem(a_abs, b_abs, &rem);
0160
0161
0162 {
0163 u32 i = DRM_FIXED_POINT;
0164
0165 do {
0166 rem <<= 1;
0167 res_abs <<= 1;
0168 if (rem >= b_abs) {
0169 res_abs |= 1;
0170 rem -= b_abs;
0171 }
0172 } while (--i != 0);
0173 }
0174
0175
0176 {
0177 u64 summand = (rem << 1) >= b_abs;
0178
0179 res_abs += summand;
0180 }
0181
0182 res = (s64) res_abs;
0183 if (a_neg ^ b_neg)
0184 res = -res;
0185 return res;
0186 }
0187
0188 static inline s64 drm_fixp_exp(s64 x)
0189 {
0190 s64 tolerance = div64_s64(DRM_FIXED_ONE, 1000000);
0191 s64 sum = DRM_FIXED_ONE, term, y = x;
0192 u64 count = 1;
0193
0194 if (x < 0)
0195 y = -1 * x;
0196
0197 term = y;
0198
0199 while (term >= tolerance) {
0200 sum = sum + term;
0201 count = count + 1;
0202 term = drm_fixp_mul(term, div64_s64(y, count));
0203 }
0204
0205 if (x < 0)
0206 sum = drm_fixp_div(DRM_FIXED_ONE, sum);
0207
0208 return sum;
0209 }
0210
0211 #endif