summary refs log tree commit diff
path: root/include/drm/drm_fixed.h
diff options
context:
space:
mode:
authorAlex Deucher <alexander.deucher@amd.com>2013-03-22 10:35:50 -0400
committerAlex Deucher <alexander.deucher@amd.com>2013-06-27 19:16:37 -0400
commit210a0b9e212370ed8c2784c2115e7ff4bb1259bd (patch)
tree35efeaaa2b97e7f4e0a9fe3475967bd859cf55d6 /include/drm/drm_fixed.h
parent9ed36f750534e2c6533fcbf32df89cf20cf87e91 (diff)
downloadlinux-210a0b9e212370ed8c2784c2115e7ff4bb1259bd.tar.gz
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>
Diffstat (limited to 'include/drm/drm_fixed.h')
-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