video: tegra: Add userspace CSC control
Robert Morell [Mon, 21 Mar 2011 21:32:29 +0000 (14:32 -0700)]
This adds configurability of the per-window color space conversion
support in the Tegra display controller through the dc extension
interface.  The CSC matrix defaults to its previously-hardcoded values,
but can be overridden by userspace.

bug 818525

Original-Change-Id: I00d8e48dd38a40e5b8c36d4624d31c834e5cd9de
Signed-off-by: Robert Morell <rmorell@nvidia.com>
Reviewed-on: http://git-master/r/40527
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>

Rebase-Id: R1f445ab544b4c06f56dde1e3f0e9db3c930a9c14

arch/arm/mach-tegra/include/mach/dc.h
drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/ext/dev.c
include/video/tegra_dc_ext.h

index 8500619..2482100 100644 (file)
@@ -349,6 +349,17 @@ struct tegra_dc_out {
 struct tegra_dc;
 struct nvmap_handle_ref;
 
+struct tegra_dc_csc {
+       unsigned short yof;
+       unsigned short kyrgb;
+       unsigned short kur;
+       unsigned short kvr;
+       unsigned short kug;
+       unsigned short kvg;
+       unsigned short kub;
+       unsigned short kvb;
+};
+
 struct tegra_dc_win {
        u8                      idx;
        u8                      fmt;
@@ -371,6 +382,8 @@ struct tegra_dc_win {
        unsigned                out_h;
        unsigned                z;
 
+       struct tegra_dc_csc     csc;
+
        int                     dirty;
        int                     underflows;
        struct tegra_dc         *dc;
@@ -482,4 +495,6 @@ struct tegra_dc_pwm_params {
 
 void tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg);
 
+int tegra_dc_update_csc(struct tegra_dc *dc, int win_index);
+
 #endif
index db79159..6087667 100644 (file)
@@ -621,18 +621,50 @@ static void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *bl
        }
 }
 
-static void tegra_dc_set_csc(struct tegra_dc *dc)
+static void tegra_dc_init_csc_defaults(struct tegra_dc_csc *csc)
 {
-       tegra_dc_writel(dc, 0x00f0, DC_WIN_CSC_YOF);
-       tegra_dc_writel(dc, 0x012a, DC_WIN_CSC_KYRGB);
-       tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KUR);
-       tegra_dc_writel(dc, 0x0198, DC_WIN_CSC_KVR);
-       tegra_dc_writel(dc, 0x039b, DC_WIN_CSC_KUG);
-       tegra_dc_writel(dc, 0x032f, DC_WIN_CSC_KVG);
-       tegra_dc_writel(dc, 0x0204, DC_WIN_CSC_KUB);
-       tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KVB);
+       csc->yof   = 0x00f0;
+       csc->kyrgb = 0x012a;
+       csc->kur   = 0x0000;
+       csc->kvr   = 0x0198;
+       csc->kug   = 0x039b;
+       csc->kvg   = 0x032f;
+       csc->kub   = 0x0204;
+       csc->kvb   = 0x0000;
 }
 
+static void tegra_dc_set_csc(struct tegra_dc *dc, struct tegra_dc_csc *csc)
+{
+       tegra_dc_writel(dc, csc->yof,   DC_WIN_CSC_YOF);
+       tegra_dc_writel(dc, csc->kyrgb, DC_WIN_CSC_KYRGB);
+       tegra_dc_writel(dc, csc->kur,   DC_WIN_CSC_KUR);
+       tegra_dc_writel(dc, csc->kvr,   DC_WIN_CSC_KVR);
+       tegra_dc_writel(dc, csc->kug,   DC_WIN_CSC_KUG);
+       tegra_dc_writel(dc, csc->kvg,   DC_WIN_CSC_KVG);
+       tegra_dc_writel(dc, csc->kub,   DC_WIN_CSC_KUB);
+       tegra_dc_writel(dc, csc->kvb,   DC_WIN_CSC_KVB);
+}
+
+int tegra_dc_update_csc(struct tegra_dc *dc, int win_idx)
+{
+       mutex_lock(&dc->lock);
+
+       if (!dc->enabled) {
+               mutex_unlock(&dc->lock);
+               return -EFAULT;
+       }
+
+       tegra_dc_writel(dc, WINDOW_A_SELECT << win_idx,
+                       DC_CMD_DISPLAY_WINDOW_HEADER);
+
+       tegra_dc_set_csc(dc, &dc->windows[win_idx].csc);
+
+       mutex_unlock(&dc->lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(tegra_dc_update_csc);
+
 static void tegra_dc_set_scaling_filter(struct tegra_dc *dc)
 {
        unsigned i;
@@ -2059,7 +2091,8 @@ static void tegra_dc_init(struct tegra_dc *dc)
        for (i = 0; i < DC_N_WINDOWS; i++) {
                tegra_dc_writel(dc, WINDOW_A_SELECT << i,
                                DC_CMD_DISPLAY_WINDOW_HEADER);
-               tegra_dc_set_csc(dc);
+               tegra_dc_init_csc_defaults(&dc->windows[i].csc);
+               tegra_dc_set_csc(dc, &dc->windows[i].csc);
                tegra_dc_set_scaling_filter(dc);
        }
 
index 788593e..b0e8c38 100644 (file)
@@ -439,6 +439,43 @@ fail_pin:
        return ret;
 }
 
+static int tegra_dc_ext_set_csc(struct tegra_dc_ext_user *user,
+                               struct tegra_dc_ext_csc *new_csc)
+{
+       unsigned int index = new_csc->win_index;
+       struct tegra_dc *dc = user->ext->dc;
+       struct tegra_dc_ext_win *ext_win;
+       struct tegra_dc_csc *csc;
+
+       if (index >= DC_N_WINDOWS)
+               return -EINVAL;
+
+       ext_win = &user->ext->win[index];
+       csc = &dc->windows[index].csc;
+
+       mutex_lock(&ext_win->lock);
+
+       if (ext_win->user != user) {
+               mutex_unlock(&ext_win->lock);
+               return -EACCES;
+       }
+
+       csc->yof =   new_csc->yof;
+       csc->kyrgb = new_csc->kyrgb;
+       csc->kur =   new_csc->kur;
+       csc->kvr =   new_csc->kvr;
+       csc->kug =   new_csc->kug;
+       csc->kvg =   new_csc->kvg;
+       csc->kub =   new_csc->kub;
+       csc->kvb =   new_csc->kvb;
+
+       tegra_dc_update_csc(dc, index);
+
+       mutex_unlock(&ext_win->lock);
+
+       return 0;
+}
+
 static long tegra_dc_ioctl(struct file *filp, unsigned int cmd,
                           unsigned long arg)
 {
@@ -493,6 +530,16 @@ static long tegra_dc_ioctl(struct file *filp, unsigned int cmd,
                return tegra_dc_ext_set_cursor(user, &args);
        }
 
+       case TEGRA_DC_EXT_SET_CSC:
+       {
+               struct tegra_dc_ext_csc args;
+
+               if (copy_from_user(&args, user_arg, sizeof(args)))
+                       return -EFAULT;
+
+               return tegra_dc_ext_set_csc(user, &args);
+       }
+
        default:
                return -EINVAL;
        }
index 2e89a12..a7c455c 100644 (file)
@@ -130,6 +130,37 @@ struct tegra_dc_ext_cursor {
        __u32 flags;
 };
 
+/*
+ * Color conversion is performed as follows:
+ *
+ * r = sat(kyrgb * sat(y + yof) + kur * u + kvr * v)
+ * g = sat(kyrgb * sat(y + yof) + kug * u + kvg * v)
+ * b = sat(kyrgb * sat(y + yof) + kub * u + kvb * v)
+ *
+ * Coefficients should be specified as fixed-point values; the exact format
+ * varies for each coefficient.
+ * The format for each coefficient is listed below with the syntax:
+ * - A "s." prefix means that the coefficient has a sign bit (twos complement).
+ * - The first number is the number of bits in the integer component (not
+ *   including the optional sign bit).
+ * - The second number is the number of bits in the fractional component.
+ *
+ * All three fields should be tightly packed, justified to the LSB of the
+ * 16-bit value.  For example, the "s.2.8" value should be packed as:
+ * (MSB) 5 bits of 0, 1 bit of sign, 2 bits of integer, 8 bits of frac (LSB)
+ */
+struct tegra_dc_ext_csc {
+       __u32 win_index;
+       __u16 yof;      /* s.7.0 */
+       __u16 kyrgb;    /*   2.8 */
+       __u16 kur;      /* s.2.8 */
+       __u16 kvr;      /* s.2.8 */
+       __u16 kug;      /* s.1.8 */
+       __u16 kvg;      /* s.1.8 */
+       __u16 kub;      /* s.2.8 */
+       __u16 kvb;      /* s.2.8 */
+};
+
 #define TEGRA_DC_EXT_SET_NVMAP_FD \
        _IOW('D', 0x00, __s32)
 
@@ -150,6 +181,9 @@ struct tegra_dc_ext_cursor {
 #define TEGRA_DC_EXT_SET_CURSOR \
        _IOW('D', 0x07, struct tegra_dc_ext_cursor)
 
+#define TEGRA_DC_EXT_SET_CSC \
+       _IOW('D', 0x08, struct tegra_dc_ext_csc)
+
 
 enum tegra_dc_ext_control_output_type {
        TEGRA_DC_EXT_DSI,