video: tegra: dc: add ioctl for setting gamma lut
David Schalig [Thu, 6 Oct 2011 11:24:07 +0000 (20:24 +0900)]
Adds ioctl TEGRA_DC_EXT_SET_LUT to dc_ext driver for setting
a DC window's color palette.

Bug 868060

Change-Id: I57ffcf3a3f91e76efd1c7f1f972b73c2edbaed82
Reviewed-on: http://git-master/r/56392
Reviewed-by: Rohan Somvanshi <rsomvanshi@nvidia.com>
Tested-by: Rohan Somvanshi <rsomvanshi@nvidia.com>

Rebase-Id: R712c71151d0c3e3d274279f334bedf312e26e75d

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 277c38d..72d6750 100644 (file)
@@ -364,6 +364,13 @@ struct tegra_dc_csc {
        unsigned short kvb;
 };
 
+/* palette lookup table */
+struct tegra_dc_lut {
+       u8 r[256];
+       u8 g[256];
+       u8 b[256];
+};
+
 struct tegra_dc_win {
        u8                      idx;
        u8                      fmt;
@@ -394,6 +401,7 @@ struct tegra_dc_win {
        struct nvmap_handle_ref *cur_handle;
        unsigned                bandwidth;
        unsigned                new_bandwidth;
+       struct tegra_dc_lut     lut;
 };
 
 
@@ -502,6 +510,8 @@ 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);
 
+int tegra_dc_update_lut(struct tegra_dc *dc, int win_index, int start, int len);
+
 /*
  * In order to get a dc's current EDID, first call tegra_dc_get_edid() from an
  * interruptible context.  The returned value (if non-NULL) points to a
index f40e8ca..2c85a48 100644 (file)
@@ -665,6 +665,48 @@ int tegra_dc_update_csc(struct tegra_dc *dc, int win_idx)
 }
 EXPORT_SYMBOL(tegra_dc_update_csc);
 
+static void tegra_dc_init_lut_defaults(struct tegra_dc_lut *lut)
+{
+       int i;
+       for(i=0; i<256; i++) {
+               lut->r[i] = lut->g[i] = lut->b[i] = (u8)i;
+       }
+}
+
+static void tegra_dc_set_lut(struct tegra_dc *dc,
+                            struct tegra_dc_lut *lut,
+                            int start,
+                            int len)
+{
+       int i;
+       for (i = start, len += start; i < len; i++) {
+               u32 rgb = ((u32)lut->r[i]) |
+                         ((u32)lut->g[i]<<8) |
+                         ((u32)lut->b[i]<<16);
+               tegra_dc_writel(dc, rgb, DC_WIN_COLOR_PALETTE(i));
+       }
+}
+
+int tegra_dc_update_lut(struct tegra_dc *dc, int win_idx, int start, int len)
+{
+       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_lut(dc, &dc->windows[win_idx].lut, start, len);
+
+       mutex_unlock(&dc->lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(tegra_dc_update_lut);
+
 static void tegra_dc_set_scaling_filter(struct tegra_dc *dc)
 {
        unsigned i;
@@ -1075,7 +1117,7 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
                                        DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV,
                                        DC_WIN_BUFFER_ADDR_MODE);
 
-               val = WIN_ENABLE;
+               val = WIN_ENABLE | CP_ENABLE;
                if (yuvp)
                        val |= CSC_ENABLE;
                else if (tegra_dc_fmt_bpp(win->fmt) < 24)
@@ -2185,6 +2227,8 @@ static void tegra_dc_init(struct tegra_dc *dc)
                                DC_CMD_DISPLAY_WINDOW_HEADER);
                tegra_dc_init_csc_defaults(&dc->windows[i].csc);
                tegra_dc_set_csc(dc, &dc->windows[i].csc);
+               tegra_dc_init_lut_defaults(&dc->windows[i].lut);
+               tegra_dc_set_lut(dc, &dc->windows[i].lut, 0, 256);
                tegra_dc_set_scaling_filter(dc);
        }
 
index 01454f5..b8993dd 100644 (file)
@@ -520,6 +520,67 @@ static int tegra_dc_ext_set_csc(struct tegra_dc_ext_user *user,
        return 0;
 }
 
+static int set_lut_channel(u16 *channel_from_user,
+                          u8 *channel_to,
+                          u32 start,
+                          u32 len)
+{
+       int i;
+       u16 lut16bpp[256];
+
+       if (copy_from_user(lut16bpp, channel_from_user, len<<1))
+               return 1;
+
+       for (i=0; i<len; i++)
+               channel_to[start+i] = lut16bpp[i]>>8;
+
+       return 0;
+}
+
+static int tegra_dc_ext_set_lut(struct tegra_dc_ext_user *user,
+                               struct tegra_dc_ext_lut *new_lut)
+{
+       int err;
+       unsigned int index = new_lut->win_index;
+       u32 start = new_lut->start;
+       u32 len = new_lut->len;
+
+       struct tegra_dc *dc = user->ext->dc;
+       struct tegra_dc_ext_win *ext_win;
+       struct tegra_dc_lut *lut;
+
+       if (index >= DC_N_WINDOWS)
+               return -EINVAL;
+
+       if ((start >= 256) || (len > 256) || ((start + len) > 256))
+               return -EINVAL;
+
+       ext_win = &user->ext->win[index];
+       lut = &dc->windows[index].lut;
+
+       mutex_lock(&ext_win->lock);
+
+       if (ext_win->user != user) {
+               mutex_unlock(&ext_win->lock);
+               return -EACCES;
+       }
+
+       err = set_lut_channel(new_lut->r, lut->r, start, len) |
+             set_lut_channel(new_lut->g, lut->g, start, len) |
+             set_lut_channel(new_lut->b, lut->b, start, len);
+
+       if (err) {
+               mutex_unlock(&ext_win->lock);
+               return -EFAULT;
+       }
+
+       tegra_dc_update_lut(dc, index, start, len);
+
+       mutex_unlock(&ext_win->lock);
+
+       return 0;
+}
+
 static u32 tegra_dc_ext_get_vblank_syncpt(struct tegra_dc_ext_user *user)
 {
        struct tegra_dc *dc = user->ext->dc;
@@ -627,6 +688,16 @@ static long tegra_dc_ioctl(struct file *filp, unsigned int cmd,
                return ret;
        }
 
+       case TEGRA_DC_EXT_SET_LUT:
+       {
+               struct tegra_dc_ext_lut args;
+
+               if (copy_from_user(&args, user_arg, sizeof(args)))
+                       return -EFAULT;
+
+               return tegra_dc_ext_set_lut(user, &args);
+       }
+
        default:
                return -EINVAL;
        }
index bdfebae..77d4e62 100644 (file)
@@ -164,6 +164,29 @@ struct tegra_dc_ext_csc {
        __u16 kvb;      /* s.2.8 */
 };
 
+/*
+ * RGB Lookup table
+ *
+ * In true-color and YUV modes this is used for post-CSC RGB->RGB lookup, i.e.
+ * gamma-correction. In palette-indexed RGB modes, this table designates the
+ * mode's color palette.
+ *
+ * To convert 8-bit per channel RGB values to 16-bit, duplicate the 8 bits
+ * in low and high byte, e.g. r=r|(r<<8)
+ *
+ * Current Tegra DC hardware supports 8-bit per channel to 8-bit per channel,
+ * and each hardware window (overlay) uses its own lookup table.
+ *
+ */
+struct tegra_dc_ext_lut {
+       __u32  win_index; /* window index to set lut for */
+       __u32  start;     /* start index to update lut from */
+       __u32  len;       /* number of valid lut entries */
+       __u16* r;         /* array of size 16-bit red values */
+       __u16* g;         /* array of size 16-bit green values */
+       __u16* b;         /* array of size 16-bit blue values */
+};
+
 
 #define TEGRA_DC_EXT_FLAGS_ENABLED     1
 struct tegra_dc_ext_status {
@@ -205,6 +228,8 @@ struct tegra_dc_ext_status {
 #define TEGRA_DC_EXT_GET_VBLANK_SYNCPT \
        _IOR('D', 0x09, __u32)
 
+#define TEGRA_DC_EXT_SET_LUT \
+       _IOR('D', 0x0A, struct tegra_dc_ext_lut)
 
 enum tegra_dc_ext_control_output_type {
        TEGRA_DC_EXT_DSI,