video: tegra: dc: support global fbdev gamma table
David Schalig [Tue, 25 Oct 2011 10:28:24 +0000 (19:28 +0900)]
Add support to set a global gamma correction table via fbdev cmap
API. The 3 Tegra DC windows have their own local gamma tables, which
can either override or alter the global table.

Bug 868060

Change-Id: I0be1c5e4afa8fd8c010b772c7808c883c0848ab4
Reviewed-on: http://git-master/r/60201
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>

Rebase-Id: R7e613b1c8ac469242172bd81db9dfba25176e0c3

drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/dc_priv.h
drivers/video/tegra/fb.c
include/video/tegra_dc_ext.h

index b1c4413..4f8da4e 100644 (file)
@@ -672,29 +672,49 @@ static void tegra_dc_init_lut_defaults(struct tegra_dc_lut *lut)
                lut->r[i] = lut->g[i] = lut->b[i] = (u8)i;
 }
 
-static int tegra_dc_lut_is_defaults(struct tegra_dc_lut *lut)
+static int tegra_dc_loop_lut(struct tegra_dc *dc,
+                            struct tegra_dc_win *win,
+                            int(*lambda)(struct tegra_dc *dc, int i, u32 rgb))
 {
-       unsigned int i;
-       for (i = 0; i < 256; i++)
-               if ((lut->r[i] != i) || (lut->g[i] != i) || (lut->b[i] != i))
+       struct tegra_dc_lut *lut = &win->lut;
+       struct tegra_dc_lut *global_lut = &dc->fb_lut;
+       int i;
+       for (i = 0; i < 256; i++) {
+
+               u32 r = (u32)lut->r[i];
+               u32 g = (u32)lut->g[i];
+               u32 b = (u32)lut->b[i];
+
+               if (!(win->ppflags & TEGRA_WIN_PPFLAG_CP_FBOVERRIDE)) {
+                       r = (u32)global_lut->r[r];
+                       g = (u32)global_lut->g[g];
+                       b = (u32)global_lut->b[b];
+               }
+
+               if (!lambda(dc, i, r | (g<<8) | (b<<16)))
                        return 0;
+       }
        return 1;
 }
 
-static void tegra_dc_set_lut(struct tegra_dc *dc, struct tegra_dc_win* win)
+static int tegra_dc_lut_isdefaults_lambda(struct tegra_dc *dc, int i, u32 rgb)
 {
-       unsigned int i;
-       unsigned long val;
-       struct tegra_dc_lut *lut = &win->lut;
+       if (rgb != (i | (i<<8) | (i<<16)))
+               return 0;
+       return 1;
+}
 
-       for (i = 0; i < 256; 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));
-       }
+static int tegra_dc_set_lut_setreg_lambda(struct tegra_dc *dc, int i, u32 rgb)
+{
+       tegra_dc_writel(dc, rgb, DC_WIN_COLOR_PALETTE(i));
+       return 1;
+}
+
+static void tegra_dc_set_lut(struct tegra_dc *dc, struct tegra_dc_win* win)
+{
+       unsigned long val = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
 
-       val = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
+       tegra_dc_loop_lut(dc, win, tegra_dc_set_lut_setreg_lambda);
 
        if (win->ppflags & TEGRA_WIN_PPFLAG_CP_ENABLE)
                val |= CP_ENABLE;
@@ -704,7 +724,7 @@ static void tegra_dc_set_lut(struct tegra_dc *dc, struct tegra_dc_win* win)
        tegra_dc_writel(dc, val, DC_WIN_WIN_OPTIONS);
 }
 
-int tegra_dc_update_lut(struct tegra_dc *dc, int win_idx, int fboveride)
+static int tegra_dc_update_winlut(struct tegra_dc *dc, int win_idx, int fbovr)
 {
        struct tegra_dc_win *win = &dc->windows[win_idx];
 
@@ -715,16 +735,16 @@ int tegra_dc_update_lut(struct tegra_dc *dc, int win_idx, int fboveride)
                return -EFAULT;
        }
 
-       if (!tegra_dc_lut_is_defaults(&win->lut))
+       if (fbovr > 0)
+               win->ppflags |= TEGRA_WIN_PPFLAG_CP_FBOVERRIDE;
+       else if (fbovr == 0)
+               win->ppflags &= ~TEGRA_WIN_PPFLAG_CP_FBOVERRIDE;
+
+       if (!tegra_dc_loop_lut(dc, win, tegra_dc_lut_isdefaults_lambda))
                win->ppflags |= TEGRA_WIN_PPFLAG_CP_ENABLE;
        else
                win->ppflags &= ~TEGRA_WIN_PPFLAG_CP_ENABLE;
 
-       if (fboveride)
-               win->ppflags |= TEGRA_WIN_PPFLAG_CP_FBOVERRIDE;
-       else
-               win->ppflags &= ~TEGRA_WIN_PPFLAG_CP_FBOVERRIDE;
-
        tegra_dc_writel(dc, WINDOW_A_SELECT << win_idx,
                        DC_CMD_DISPLAY_WINDOW_HEADER);
 
@@ -734,6 +754,20 @@ int tegra_dc_update_lut(struct tegra_dc *dc, int win_idx, int fboveride)
 
        return 0;
 }
+
+int tegra_dc_update_lut(struct tegra_dc *dc, int win_idx, int fboveride)
+{
+       if (win_idx > -1)
+               return tegra_dc_update_winlut(dc, win_idx, fboveride);
+
+       for (win_idx = 0; win_idx < DC_N_WINDOWS; win_idx++) {
+               int err = tegra_dc_update_winlut(dc, win_idx, fboveride);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
 EXPORT_SYMBOL(tegra_dc_update_lut);
 
 static void tegra_dc_set_scaling_filter(struct tegra_dc *dc)
@@ -2656,6 +2690,8 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
 #endif
        INIT_WORK(&dc->vblank_work, tegra_dc_vblank);
 
+       tegra_dc_init_lut_defaults(&dc->fb_lut);
+
        dc->n_windows = DC_N_WINDOWS;
        for (i = 0; i < dc->n_windows; i++) {
                struct tegra_dc_win *win = &dc->windows[i];
index 5eaa8be..83f26b8 100644 (file)
@@ -132,6 +132,7 @@ struct tegra_dc {
 #ifdef CONFIG_DEBUG_FS
        struct dentry                   *debugdir;
 #endif
+       struct tegra_dc_lut             fb_lut;
 };
 
 static inline void tegra_dc_io_start(struct tegra_dc *dc)
index 7b01f1e..44be010 100644 (file)
@@ -146,27 +146,28 @@ static int tegra_fb_set_par(struct fb_info *info)
        return 0;
 }
 
-static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
-       unsigned blue, unsigned transp, struct fb_info *info)
+static int tegra_fb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
 {
-       struct fb_var_screeninfo *var = &info->var;
+       struct tegra_fb_info *tegra_fb = info->par;
+       struct tegra_dc *dc = tegra_fb->win->dc;
+       int i;
+       u16 *red = cmap->red;
+       u16 *green = cmap->green;
+       u16 *blue = cmap->blue;
+       int start = cmap->start;
+
+       if (((unsigned)start > 255) || ((start + cmap->len) > 255))
+               return -EINVAL;
 
        if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
            info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
-               u32 v;
-
-               if (regno >= 16)
-                       return -EINVAL;
-
-               red = (red >> (16 - info->var.red.length));
-               green = (green >> (16 - info->var.green.length));
-               blue = (blue >> (16 - info->var.blue.length));
-
-               v = (red << var->red.offset) |
-                       (green << var->green.offset) |
-                       (blue << var->blue.offset);
+               for (i = 0; i < cmap->len; i++) {
+                       dc->fb_lut.r[start+i] = *red++ >> 8;
+                       dc->fb_lut.g[start+i] = *green++ >> 8;
+                       dc->fb_lut.b[start+i] = *blue++ >> 8;
+               }
 
-               ((u32 *)info->pseudo_palette)[regno] = v;
+               tegra_dc_update_lut(dc, -1, -1);
        }
 
        return 0;
@@ -302,7 +303,7 @@ static struct fb_ops tegra_fb_ops = {
        .owner = THIS_MODULE,
        .fb_check_var = tegra_fb_check_var,
        .fb_set_par = tegra_fb_set_par,
-       .fb_setcolreg = tegra_fb_setcolreg,
+       .fb_setcmap = tegra_fb_setcmap,
        .fb_blank = tegra_fb_blank,
        .fb_pan_display = tegra_fb_pan_display,
        .fb_fillrect = tegra_fb_fillrect,
index 6c1a4e3..380b026 100644 (file)
@@ -174,6 +174,8 @@ struct tegra_dc_ext_csc {
  * 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)
  *
+ * To just update flags, set len to 0.
+ *
  * Current Tegra DC hardware supports 8-bit per channel to 8-bit per channel,
  * and each hardware window (overlay) uses its own lookup table.
  *
@@ -188,7 +190,9 @@ struct tegra_dc_ext_lut {
        __u16 *b;         /* array of 16-bit blue values, 0 to reset */
 };
 
-/* tegra_dc_ext_lut.flags - override fb device palette. Default is multiply. */
+/* tegra_dc_ext_lut.flags - override global fb device lookup table.
+ * Default behaviour is double-lookup.
+ */
 #define TEGRA_DC_EXT_LUT_FLAGS_FBOVERRIDE 0x01
 
 #define TEGRA_DC_EXT_FLAGS_ENABLED     1