242 lines · c
1/*2 * Copyright 2009 Red Hat Inc.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice shall be included in12 * all copies or substantial portions of the Software.13 *14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR20 * OTHER DEALINGS IN THE SOFTWARE.21 *22 * Authors: Dave Airlie23 * Christian König24 */25#ifndef DRM_FIXED_H26#define DRM_FIXED_H27 28#include <linux/math64.h>29#include <linux/types.h>30#include <linux/wordpart.h>31 32typedef union dfixed {33 u32 full;34} fixed20_12;35 36 37#define dfixed_const(A) (u32)(((A) << 12))/* + ((B + 0.000122)*4096)) */38#define dfixed_const_half(A) (u32)(((A) << 12) + 2048)39#define dfixed_const_666(A) (u32)(((A) << 12) + 2731)40#define dfixed_const_8(A) (u32)(((A) << 12) + 3277)41#define dfixed_mul(A, B) ((u64)((u64)(A).full * (B).full + 2048) >> 12)42#define dfixed_init(A) { .full = dfixed_const((A)) }43#define dfixed_init_half(A) { .full = dfixed_const_half((A)) }44#define dfixed_trunc(A) ((A).full >> 12)45#define dfixed_frac(A) ((A).full & ((1 << 12) - 1))46 47static inline u32 dfixed_floor(fixed20_12 A)48{49 u32 non_frac = dfixed_trunc(A);50 51 return dfixed_const(non_frac);52}53 54static inline u32 dfixed_ceil(fixed20_12 A)55{56 u32 non_frac = dfixed_trunc(A);57 58 if (A.full > dfixed_const(non_frac))59 return dfixed_const(non_frac + 1);60 else61 return dfixed_const(non_frac);62}63 64static inline u32 dfixed_div(fixed20_12 A, fixed20_12 B)65{66 u64 tmp = ((u64)A.full << 13);67 68 do_div(tmp, B.full);69 tmp += 1;70 tmp /= 2;71 return lower_32_bits(tmp);72}73 74#define DRM_FIXED_POINT 3275#define DRM_FIXED_ONE (1ULL << DRM_FIXED_POINT)76#define DRM_FIXED_DECIMAL_MASK (DRM_FIXED_ONE - 1)77#define DRM_FIXED_DIGITS_MASK (~DRM_FIXED_DECIMAL_MASK)78#define DRM_FIXED_EPSILON 1LL79#define DRM_FIXED_ALMOST_ONE (DRM_FIXED_ONE - DRM_FIXED_EPSILON)80 81static inline s64 drm_int2fixp(int a)82{83 return ((s64)a) << DRM_FIXED_POINT;84}85 86static inline int drm_fixp2int(s64 a)87{88 return ((s64)a) >> DRM_FIXED_POINT;89}90 91static inline int drm_fixp2int_round(s64 a)92{93 return drm_fixp2int(a + DRM_FIXED_ONE / 2);94}95 96static inline int drm_fixp2int_ceil(s64 a)97{98 if (a >= 0)99 return drm_fixp2int(a + DRM_FIXED_ALMOST_ONE);100 else101 return drm_fixp2int(a - DRM_FIXED_ALMOST_ONE);102}103 104static inline unsigned drm_fixp_msbset(s64 a)105{106 unsigned shift, sign = (a >> 63) & 1;107 108 for (shift = 62; shift > 0; --shift)109 if (((a >> shift) & 1) != sign)110 return shift;111 112 return 0;113}114 115static inline s64 drm_fixp_mul(s64 a, s64 b)116{117 unsigned shift = drm_fixp_msbset(a) + drm_fixp_msbset(b);118 s64 result;119 120 if (shift > 61) {121 shift = shift - 61;122 a >>= (shift >> 1) + (shift & 1);123 b >>= shift >> 1;124 } else125 shift = 0;126 127 result = a * b;128 129 if (shift > DRM_FIXED_POINT)130 return result << (shift - DRM_FIXED_POINT);131 132 if (shift < DRM_FIXED_POINT)133 return result >> (DRM_FIXED_POINT - shift);134 135 return result;136}137 138static inline s64 drm_fixp_div(s64 a, s64 b)139{140 unsigned shift = 62 - drm_fixp_msbset(a);141 s64 result;142 143 a <<= shift;144 145 if (shift < DRM_FIXED_POINT)146 b >>= (DRM_FIXED_POINT - shift);147 148 result = div64_s64(a, b);149 150 if (shift > DRM_FIXED_POINT)151 return result >> (shift - DRM_FIXED_POINT);152 153 return result;154}155 156static inline s64 drm_fixp_from_fraction(s64 a, s64 b)157{158 s64 res;159 bool a_neg = a < 0;160 bool b_neg = b < 0;161 u64 a_abs = a_neg ? -a : a;162 u64 b_abs = b_neg ? -b : b;163 u64 rem;164 165 /* determine integer part */166 u64 res_abs = div64_u64_rem(a_abs, b_abs, &rem);167 168 /* determine fractional part */169 {170 u32 i = DRM_FIXED_POINT;171 172 do {173 rem <<= 1;174 res_abs <<= 1;175 if (rem >= b_abs) {176 res_abs |= 1;177 rem -= b_abs;178 }179 } while (--i != 0);180 }181 182 /* round up LSB */183 {184 u64 summand = (rem << 1) >= b_abs;185 186 res_abs += summand;187 }188 189 res = (s64) res_abs;190 if (a_neg ^ b_neg)191 res = -res;192 return res;193}194 195static inline s64 drm_fixp_exp(s64 x)196{197 s64 tolerance = div64_s64(DRM_FIXED_ONE, 1000000);198 s64 sum = DRM_FIXED_ONE, term, y = x;199 u64 count = 1;200 201 if (x < 0)202 y = -1 * x;203 204 term = y;205 206 while (term >= tolerance) {207 sum = sum + term;208 count = count + 1;209 term = drm_fixp_mul(term, div64_s64(y, count));210 }211 212 if (x < 0)213 sum = drm_fixp_div(DRM_FIXED_ONE, sum);214 215 return sum;216}217 218static inline int fxp_q4_from_int(int val_int)219{220 return val_int << 4;221}222 223static inline int fxp_q4_to_int(int val_q4)224{225 return val_q4 >> 4;226}227 228static inline int fxp_q4_to_int_roundup(int val_q4)229{230 return (val_q4 + 0xf) >> 4;231}232 233static inline int fxp_q4_to_frac(int val_q4)234{235 return val_q4 & 0xf;236}237 238#define FXP_Q4_FMT "%d.%04d"239#define FXP_Q4_ARGS(val_q4) fxp_q4_to_int(val_q4), (fxp_q4_to_frac(val_q4) * 625)240 241#endif242