staging: mrst: Add overlay color correction settings
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Wed, 14 Dec 2011 22:12:05 +0000 (00:12 +0200)
committerMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Tue, 3 Jul 2012 09:29:05 +0000 (12:29 +0300)
Allow the overlay brightness, contrast, hue and saturation to be
adjusted. Also allows one to select the input range (JPEG vs. MPEG)
for the color space conversion.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Acked-by: Pauli Nieminen <pauli.nieminen@linux.intel.com>
Reviewed-by: Jani Nikula <jani.nikula@intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
drivers/staging/mrst/Makefile
drivers/staging/mrst/drv/fp_trig.c [new file with mode: 0644]
drivers/staging/mrst/drv/fp_trig.h [new file with mode: 0644]
drivers/staging/mrst/drv/mdfld_overlay.c

index fa7066a..693cf60 100644 (file)
@@ -160,6 +160,7 @@ medfield_gfx-y += \
        $(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 \
diff --git a/drivers/staging/mrst/drv/fp_trig.c b/drivers/staging/mrst/drv/fp_trig.c
new file mode 100644 (file)
index 0000000..b73c2e1
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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);
+}
diff --git a/drivers/staging/mrst/drv/fp_trig.h b/drivers/staging/mrst/drv/fp_trig.h
new file mode 100644 (file)
index 0000000..f3f052d
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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
index 0e10997..5e821f5 100644 (file)
@@ -31,6 +31,8 @@
 #include "psb_gtt.h"
 #include "psb_fb.h"
 
+#include "fp_trig.h"
+
 enum {
        OVL_DIRTY_REGS  = 0x1,
        OVL_DIRTY_COEFS = 0x2,
@@ -542,9 +544,6 @@ static void ovl_setup_regs(struct mfld_overlay *ovl)
 
        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;
@@ -934,12 +933,120 @@ mfld_overlay_disable_plane(struct drm_plane *plane)
        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[] = {
@@ -1182,6 +1289,16 @@ int mdfld_overlay_init(struct drm_device *dev, int id)
        /* 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));