summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Deucher <alexander.deucher@amd.com>2013-03-22 18:35:50 +0400
committerAlex Deucher <alexander.deucher@amd.com>2013-06-28 03:16:37 +0400
commit210a0b9e212370ed8c2784c2115e7ff4bb1259bd (patch)
tree35efeaaa2b97e7f4e0a9fe3475967bd859cf55d6
parent9ed36f750534e2c6533fcbf32df89cf20cf87e91 (diff)
downloadlinux-210a0b9e212370ed8c2784c2115e7ff4bb1259bd.tar.xz
drm: add some additional fixed point helpers (v3)
Required for certain driver calculations. Code was written by Christian König and ported to the drm by me. v2: fix 64 bit divides v3: fix 64 bit for real (math64.h) Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r--include/drm/drm_fixed.h94
1 files changed, 94 insertions, 0 deletions
diff --git a/include/drm/drm_fixed.h b/include/drm/drm_fixed.h
index 0ead502e17d2..f5e1168c7647 100644
--- a/include/drm/drm_fixed.h
+++ b/include/drm/drm_fixed.h
@@ -20,10 +20,13 @@
* 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,95 @@ 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)
+
+static inline s64 drm_int2fixp(int a)
+{
+ return ((s64)a) << DRM_FIXED_POINT;
+}
+
+static inline int drm_fixp2int(int64_t a)
+{
+ return ((s64)a) >> DRM_FIXED_POINT;
+}
+
+static inline s64 drm_fixp_msbset(int64_t a)
+{
+ unsigned shift, sign = (a >> 63) & 1;
+
+ for (shift = 62; shift > 0; --shift)
+ if ((a >> shift) != 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 > 63) {
+ shift = shift - 63;
+ a >>= 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 = 63 - 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_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(1, sum);
+
+ return sum;
+}
+
#endif