$(FBDEVDIR)/mrstlfb_linux.o
medfield_gfx-y += \
+ $(DRMDRVDIR)/fp_trig.o \
$(DRMDRVDIR)/mdfld_dsi_dbi.o \
$(DRMDRVDIR)/mdfld_dsi_dpi.o \
$(DRMDRVDIR)/mdfld_dsi_output.o \
--- /dev/null
+/*
+ * Copyright (C) 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include "fp_trig.h"
+
+enum {
+ COS_TAB_SIZE = 128,
+ LERP_ONE = 1 << 12,
+};
+
+/*
+ * cos_tab was generated like this:
+ *
+ * for (i = 0; i < COS_TAB_SIZE; i++)
+ * cos_tab[i] = trunc(cos(M_PI * i / ((COS_TAB_SIZE - 1) * 2)) * (1 << FP_FRAC_BITS));
+ *
+ * The table has one extra element to avoid out of
+ * bounds access when computing cosine at FP_PI/2.
+ */
+static const uint16_t cos_tab[COS_TAB_SIZE + 1] = {
+ 0x8000, 0x7ffd, 0x7ff5, 0x7fe9, 0x7fd7, 0x7fc1, 0x7fa5, 0x7f85,
+ 0x7f5f, 0x7f35, 0x7f05, 0x7ed1, 0x7e97, 0x7e59, 0x7e15, 0x7dcd,
+ 0x7d80, 0x7d2e, 0x7cd7, 0x7c7b, 0x7c1a, 0x7bb4, 0x7b4a, 0x7adb,
+ 0x7a66, 0x79ed, 0x7970, 0x78ed, 0x7866, 0x77da, 0x7749, 0x76b4,
+ 0x761a, 0x757c, 0x74d9, 0x7431, 0x7385, 0x72d4, 0x721e, 0x7165,
+ 0x70a6, 0x6fe4, 0x6f1d, 0x6e51, 0x6d82, 0x6cae, 0x6bd5, 0x6af9,
+ 0x6a18, 0x6934, 0x684b, 0x675e, 0x666d, 0x6578, 0x647e, 0x6382,
+ 0x6281, 0x617c, 0x6073, 0x5f67, 0x5e57, 0x5d43, 0x5c2c, 0x5b11,
+ 0x59f2, 0x58d0, 0x57ab, 0x5682, 0x5555, 0x5425, 0x52f2, 0x51bc,
+ 0x5083, 0x4f46, 0x4e06, 0x4cc3, 0x4b7e, 0x4a35, 0x48e9, 0x479b,
+ 0x4649, 0x44f5, 0x439e, 0x4245, 0x40e9, 0x3f8a, 0x3e29, 0x3cc6,
+ 0x3b60, 0x39f8, 0x388d, 0x3721, 0x35b2, 0x3441, 0x32ce, 0x3159,
+ 0x2fe2, 0x2e69, 0x2cef, 0x2b72, 0x29f4, 0x2874, 0x26f3, 0x2570,
+ 0x23ec, 0x2266, 0x20df, 0x1f57, 0x1dcd, 0x1c43, 0x1ab7, 0x192a,
+ 0x179c, 0x160d, 0x147e, 0x12ed, 0x115c, 0x0fca, 0x0e38, 0x0ca5,
+ 0x0b11, 0x097d, 0x07e9, 0x0654, 0x04bf, 0x032a, 0x0195, 0x0000,
+};
+
+static unsigned int lerp(unsigned int x1, unsigned int x2, unsigned int alpha)
+{
+ return ((x1 * (LERP_ONE - alpha)) + (x2 * alpha)) / LERP_ONE;
+}
+
+int fp_cos(unsigned int angle)
+{
+ unsigned int x, i, quadrant;
+ int y;
+
+ /* limit angle to [0:FP_PI/2] range */
+ quadrant = (angle / (FP_PI / 2)) & 3;
+ angle &= FP_PI / 2 - 1;
+ if (quadrant & 1)
+ angle = FP_PI / 2 - angle;
+
+ /*
+ * Perform linear interpolation between the points in the table.
+ * Round x up to keep fp_cos(x) <= cos(x).
+ */
+ x = DIV_ROUND_UP(angle * (COS_TAB_SIZE - 1) * LERP_ONE, FP_PI / 2);
+ i = x / LERP_ONE;
+ y = lerp(cos_tab[i], cos_tab[i+1], x & (LERP_ONE - 1));
+
+ if (quadrant == 1 || quadrant == 2)
+ y = -y;
+
+ return y;
+}
+
+int fp_sin(unsigned int angle)
+{
+ return -fp_cos(angle + FP_PI / 2);
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FP_TRIG_H
+#define FP_TRIG_H
+
+enum {
+ /* number of fractional bits in results */
+ FP_FRAC_BITS = 15,
+
+ /* value of PI */
+ FP_PI = 1 << 14,
+};
+
+/**
+ * fp_cos - Fixed point cosine
+ * @angle: positive angle (period 2*FP_PI)
+ *
+ * RETURNS:
+ * Cosine in signed 1.<FP_FRAC_BITS> fixed point format
+ */
+int fp_cos(unsigned int angle);
+
+/**
+ * fp_sin - Fixed point sine
+ * @angle: positive angle (period 2*FP_PI)
+ *
+ * RETURNS:
+ * Sine in signed 1.<FP_FRAC_BITS> fixed point format
+ */
+int fp_sin(unsigned int angle);
+
+#endif
#include "psb_gtt.h"
#include "psb_fb.h"
+#include "fp_trig.h"
+
enum {
OVL_DIRTY_REGS = 0x1,
OVL_DIRTY_COEFS = 0x2,
regs->OCONFIG = OVL_OCONFIG_IEP_BYPASS | OVL_OCONFIG_CSC_MODE;
- regs->OCLRC0 = 0x40 << 18;
- regs->OCLRC1 = 0x80 << 0;
-
regs->DCLRKM = OVL_DCLRKM_KEY | 0xffffff;
regs->SCHRKEN = 0xff;
return 0;
}
+static void ovl_set_brightness_contrast(struct mfld_overlay *ovl, const struct drm_plane_opts *opts)
+{
+ struct mfld_overlay_regs *regs = ovl->regs.virt;
+ int bri, con;
+
+ if (opts->csc_range == DRM_CSC_RANGE_MPEG) {
+ if (opts->contrast >= 0x8000)
+ /* 255/219 <= contrast <= 7.53125 */
+ con = (((opts->contrast - 0x8000) * 815) >> 16) + 75;
+ else
+ /* 0.0 <= contrast < 255/219 */
+ con = opts->contrast / 440;
+ } else {
+ if (opts->contrast >= 0x8000)
+ /* 1.0 <= contrast <= 7.53125 */
+ con = (((opts->contrast - 0x8000) * 837) >> 16) + 64;
+ else
+ /* 0.0 <= contrast < 1.0 */
+ con = opts->contrast >> 9;
+ }
+
+ if (opts->csc_range == DRM_CSC_RANGE_MPEG) {
+ /* 16 * 255/219 = ~19 */
+ if (opts->brightness >= 0x8000)
+ /* -19 <= bri <= 127 */
+ bri = ((opts->brightness - 0x8000) / 224 - 19);
+ else
+ /* -128 <= bri < -19 */
+ bri = (opts->brightness / 300 - 128);
+ } else {
+ /* -128 <= bri <= 127 */
+ bri = (opts->brightness >> 8) - 128;
+ }
+
+ /* Scare anyone who touches this code w/o thinking. */
+ WARN_ON(con < 0 || con > 482);
+ WARN_ON(bri < -128 || bri > 127);
+
+ regs->OCLRC0 = ((con & 0x1ff) << 18) | ((bri & 0xff) << 0);
+
+ ovl->dirty |= OVL_DIRTY_REGS;
+}
+
+static void ovl_set_hue_saturation(struct mfld_overlay *ovl, const struct drm_plane_opts *opts)
+{
+ struct mfld_overlay_regs *regs = ovl->regs.virt;
+ int sat;
+ /* [0:0xffff] -> [-FP_PI/2:FP_PI/2] shifted by 2*FP_PI */
+ unsigned int deg = ((opts->hue + 2) >> 2) + 3 * FP_PI / 2;
+ int sh_sin = fp_sin(deg);
+ int sh_cos = fp_cos(deg);
+
+ if (opts->csc_range == DRM_CSC_RANGE_MPEG) {
+ if (opts->saturation >= 0x8000)
+ /*
+ * abs(sh_sin) + abs(sh_cos) < 8.0, therefore
+ * 255/224 <= saturation <= 8/(2*sin(PI/4))
+ */
+ sat = (((opts->saturation - 0x8000) * 1157) >> 16) + 146;
+ else
+ /* 0.0 <= saturation < 255/224 */
+ sat = opts->saturation / 225;
+ } else {
+ if (opts->saturation >= 0x8000)
+ /*
+ * abs(sh_sin) + abs(sh_cos) < 8.0, therefore
+ * 1.0 <= saturation <= 8/(2*sin(PI/4))
+ */
+ sat = (((opts->saturation - 0x8000) * 1193) >> 16) + 128;
+ else
+ /* 0.0 <= saturation < 1.0 */
+ sat = opts->saturation >> 8;
+ }
+ sh_sin = (sh_sin * sat) / (1 << FP_FRAC_BITS);
+ sh_cos = (sh_cos * sat) / (1 << FP_FRAC_BITS);
+
+ /* Scare anyone who touches this code w/o thinking. */
+ WARN_ON(abs(sh_sin) > 724);
+ WARN_ON(sh_cos < 0 || sh_cos > 724);
+ WARN_ON(abs(sh_sin) + sh_cos >= 8 << 7);
+
+ regs->OCLRC1 = ((sh_sin & 0x7ff) << 16) | ((sh_cos & 0x3ff) << 0);
+
+ ovl->dirty |= OVL_DIRTY_REGS;
+}
+
+static int
+mfld_overlay_set_plane_opts(struct drm_plane *plane, uint32_t flags, struct drm_plane_opts *opts)
+{
+ struct mfld_overlay *ovl = to_mfld_overlay(plane);
+
+ /* Must re-compute color correction if YCbCr range is changed */
+ if (flags & DRM_MODE_PLANE_CSC_RANGE)
+ flags |= DRM_MODE_PLANE_BRIGHTNESS | DRM_MODE_PLANE_CONTRAST |
+ DRM_MODE_PLANE_HUE | DRM_MODE_PLANE_SATURATION;
+
+ if (flags & (DRM_MODE_PLANE_BRIGHTNESS | DRM_MODE_PLANE_CONTRAST))
+ ovl_set_brightness_contrast(ovl, opts);
+
+ if (flags & (DRM_MODE_PLANE_HUE | DRM_MODE_PLANE_SATURATION))
+ ovl_set_hue_saturation(ovl, opts);
+
+ ovl_commit(ovl);
+
+ return 0;
+}
+
static void mfld_overlay_destroy(struct drm_plane *plane);
static const struct drm_plane_funcs mfld_overlay_funcs = {
.update_plane = mfld_overlay_update_plane,
.disable_plane = mfld_overlay_disable_plane,
.destroy = mfld_overlay_destroy,
+ .set_plane_opts = mfld_overlay_set_plane_opts,
};
static const uint32_t mfld_overlay_formats[] = {
/* FIXME move */
ovl_setup_regs(ovl);
+ /* fill in some defaults */
+ drm_plane_opts_defaults(&ovl->base.opts);
+ ovl->base.opts.csc_range = DRM_CSC_RANGE_MPEG;
+ ovl->base.opts_flags = DRM_MODE_PLANE_BRIGHTNESS | DRM_MODE_PLANE_CONTRAST |
+ DRM_MODE_PLANE_HUE | DRM_MODE_PLANE_SATURATION |
+ DRM_MODE_PLANE_CSC_RANGE;
+
+ ovl_set_brightness_contrast(ovl, &ovl->base.opts);
+ ovl_set_hue_saturation(ovl, &ovl->base.opts);
+
drm_plane_init(dev, &ovl->base, possible_crtcs, &mfld_overlay_funcs,
mfld_overlay_formats, ARRAY_SIZE(mfld_overlay_formats));