Merge branch 'linux-linaro-lsk-v4.4-android' of git://git.linaro.org/kernel/linux...
[firefly-linux-kernel-4.4.55.git] / include / drm / drm_fixed.h
index 0ead502e17d27638fef2bb7df81d9118967b2010..553210c02ee0f655fd28b8ae64d370e7cee2fc70 100644 (file)
  * OTHER DEALINGS IN THE SOFTWARE.
  *
  * Authors: Dave Airlie
+ *          Christian König
  */
 #ifndef DRM_FIXED_H
 #define DRM_FIXED_H
 
+#include <linux/math64.h>
+
 typedef union dfixed {
        u32 full;
 } fixed20_12;
@@ -65,4 +68,144 @@ static inline u32 dfixed_div(fixed20_12 A, fixed20_12 B)
        tmp /= 2;
        return lower_32_bits(tmp);
 }
+
+#define DRM_FIXED_POINT                32
+#define DRM_FIXED_ONE          (1ULL << DRM_FIXED_POINT)
+#define DRM_FIXED_DECIMAL_MASK (DRM_FIXED_ONE - 1)
+#define DRM_FIXED_DIGITS_MASK  (~DRM_FIXED_DECIMAL_MASK)
+#define DRM_FIXED_EPSILON      1LL
+#define DRM_FIXED_ALMOST_ONE   (DRM_FIXED_ONE - DRM_FIXED_EPSILON)
+
+static inline s64 drm_int2fixp(int a)
+{
+       return ((s64)a) << DRM_FIXED_POINT;
+}
+
+static inline int drm_fixp2int(s64 a)
+{
+       return ((s64)a) >> DRM_FIXED_POINT;
+}
+
+static inline int drm_fixp2int_ceil(s64 a)
+{
+       if (a > 0)
+               return drm_fixp2int(a + DRM_FIXED_ALMOST_ONE);
+       else
+               return drm_fixp2int(a - DRM_FIXED_ALMOST_ONE);
+}
+
+static inline unsigned drm_fixp_msbset(s64 a)
+{
+       unsigned shift, sign = (a >> 63) & 1;
+
+       for (shift = 62; shift > 0; --shift)
+               if (((a >> shift) & 1) != sign)
+                       return shift;
+
+       return 0;
+}
+
+static inline s64 drm_fixp_mul(s64 a, s64 b)
+{
+       unsigned shift = drm_fixp_msbset(a) + drm_fixp_msbset(b);
+       s64 result;
+
+       if (shift > 61) {
+               shift = shift - 61;
+               a >>= (shift >> 1) + (shift & 1);
+               b >>= shift >> 1;
+       } else
+               shift = 0;
+
+       result = a * b;
+
+       if (shift > DRM_FIXED_POINT)
+               return result << (shift - DRM_FIXED_POINT);
+
+       if (shift < DRM_FIXED_POINT)
+               return result >> (DRM_FIXED_POINT - shift);
+
+       return result;
+}
+
+static inline s64 drm_fixp_div(s64 a, s64 b)
+{
+       unsigned shift = 62 - drm_fixp_msbset(a);
+       s64 result;
+
+       a <<= shift;
+
+       if (shift < DRM_FIXED_POINT)
+               b >>= (DRM_FIXED_POINT - shift);
+
+       result = div64_s64(a, b);
+
+       if (shift > DRM_FIXED_POINT)
+               return result >> (shift - DRM_FIXED_POINT);
+
+       return result;
+}
+
+static inline s64 drm_fixp_from_fraction(s64 a, s64 b)
+{
+       s64 res;
+       bool a_neg = a < 0;
+       bool b_neg = b < 0;
+       u64 a_abs = a_neg ? -a : a;
+       u64 b_abs = b_neg ? -b : b;
+       u64 rem;
+
+       /* determine integer part */
+       u64 res_abs  = div64_u64_rem(a_abs, b_abs, &rem);
+
+       /* determine fractional part */
+       {
+               u32 i = DRM_FIXED_POINT;
+
+               do {
+                       rem <<= 1;
+                       res_abs <<= 1;
+                       if (rem >= b_abs) {
+                               res_abs |= 1;
+                               rem -= b_abs;
+                       }
+               } while (--i != 0);
+       }
+
+       /* round up LSB */
+       {
+               u64 summand = (rem << 1) >= b_abs;
+
+               res_abs += summand;
+       }
+
+       res = (s64) res_abs;
+       if (a_neg ^ b_neg)
+               res = -res;
+       return res;
+}
+
+static inline s64 drm_fixp_exp(s64 x)
+{
+       s64 tolerance = div64_s64(DRM_FIXED_ONE, 1000000);
+       s64 sum = DRM_FIXED_ONE, term, y = x;
+       u64 count = 1;
+
+       if (x < 0)
+               y = -1 * x;
+
+       term = y;
+
+       while (term >= tolerance) {
+               sum = sum + term;
+               count = count + 1;
+               term = drm_fixp_mul(term, div64_s64(y, count));
+       }
+
+       if (x < 0)
+               sum = drm_fixp_div(DRM_FIXED_ONE, sum);
+
+       return sum;
+}
+
 #endif