drm/rockchip: add support for gamma table
authorMark Yao <mark.yao@rock-chips.com>
Mon, 27 Apr 2015 18:39:35 +0000 (11:39 -0700)
committerHuang, Tao <huangtao@rock-chips.com>
Thu, 11 May 2017 07:31:04 +0000 (15:31 +0800)
Introduce support for rockchip gamma table,

Rockchip have two version gamma table design:
The old version design was introduced by Dominik Behr's gamma patch
(https://chromium-review.googlesource.com/272209):
 Gamma table has to be uploaded when the LUT is disabled which only takes
 effect at the end of a frame, therefore actual hardware updates is done
 from a worker and can take more than one frame.

In order to solve gamma table switch issues, after rk3399,
H/W add a gamma table update mechanism, can update without lut disable.

And gamma table's size also has two version:
  one is 10 bit per component, 1024 entries,
  the other one is 8 bit per component, 256 entries

Change-Id: I8145d1c42a28d57f11e95d24be2341011360334d
Signed-off-by: Dominik Behr <dbehr@chromium.org>
Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
drivers/gpu/drm/rockchip/rockchip_drm_vop.c
drivers/gpu/drm/rockchip/rockchip_drm_vop.h
drivers/gpu/drm/rockchip/rockchip_vop_reg.c
drivers/gpu/drm/rockchip/rockchip_vop_reg.h

index 8c9fa5a7c9de674a9e85b7c54df3e6c26b172312..cf605ba5ec16b0ea32c6fb1275d7be7bfa9b61c0 100644 (file)
@@ -20,6 +20,7 @@
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
 #include "rockchip_drm_fb.h"
+#include "rockchip_drm_fbdev.h"
 
 #define PREFERRED_BPP          32
 
@@ -122,6 +123,8 @@ err_rockchip_gem_free_object:
 }
 
 static const struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
+       .gamma_set = rockchip_vop_crtc_fb_gamma_set,
+       .gamma_get = rockchip_vop_crtc_fb_gamma_get,
        .fb_probe = rockchip_drm_fbdev_create,
 };
 
index 73718c5f5bbf720ca6151fc44ca670fc8646c6a9..a67243f726592af079cb64c2d17652da0b5007e5 100644 (file)
@@ -29,4 +29,10 @@ static inline void rockchip_drm_fbdev_fini(struct drm_device *dev)
 }
 #endif
 
+void rockchip_vop_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+                                   u16 blue, int regno);
+
+void rockchip_vop_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+                                   u16 *blue, int regno);
+
 #endif /* _ROCKCHIP_DRM_FBDEV_H */
index 5ff202b129fbc0fd4e81710f207d4421ca8c9b10..9219d9672d5429105c74d227215ec00fa8e8c914 100644 (file)
@@ -191,6 +191,11 @@ struct vop {
        /* physical map length of vop register */
        uint32_t len;
 
+       void __iomem *lut_regs;
+       u32 *lut;
+       u32 lut_len;
+       bool lut_active;
+
        /* one time only one process allowed to config the register */
        spinlock_t reg_lock;
        /* lock vop irq reg */
@@ -325,6 +330,16 @@ static bool vop_line_flag_is_active(struct vop *vop)
        return VOP_INTR_GET_TYPE(vop, status, LINE_FLAG_INTR);
 }
 
+static inline void vop_write_lut(struct vop *vop, uint32_t offset, uint32_t v)
+{
+       writel(v, vop->lut_regs + offset);
+}
+
+static inline uint32_t vop_read_lut(struct vop *vop, uint32_t offset)
+{
+       return readl(vop->lut_regs + offset);
+}
+
 static bool has_rb_swapped(uint32_t format)
 {
        switch (format) {
@@ -775,6 +790,88 @@ static void vop_line_flag_irq_disable(struct vop *vop)
        spin_unlock_irqrestore(&vop->irq_lock, flags);
 }
 
+static void vop_crtc_load_lut(struct drm_crtc *crtc)
+{
+       struct vop *vop = to_vop(crtc);
+       int i, dle, lut_idx;
+
+       if (!vop->is_enabled || !vop->lut || !vop->lut_regs)
+               return;
+
+       if (WARN_ON(!drm_modeset_is_locked(&crtc->mutex)))
+               return;
+
+       if (!VOP_CTRL_SUPPORT(vop, update_gamma_lut)) {
+               spin_lock(&vop->reg_lock);
+               VOP_CTRL_SET(vop, dsp_lut_en, 0);
+               vop_cfg_done(vop);
+               spin_unlock(&vop->reg_lock);
+
+#define CTRL_GET(name) VOP_CTRL_GET(vop, name)
+               readx_poll_timeout(CTRL_GET, dsp_lut_en,
+                               dle, !dle, 5, 33333);
+       } else {
+               lut_idx = CTRL_GET(lut_buffer_index);
+       }
+
+       for (i = 0; i < vop->lut_len; i++)
+               vop_write_lut(vop, i << 2, vop->lut[i]);
+
+       spin_lock(&vop->reg_lock);
+
+       VOP_CTRL_SET(vop, dsp_lut_en, 1);
+       VOP_CTRL_SET(vop, update_gamma_lut, 1);
+       vop_cfg_done(vop);
+       vop->lut_active = true;
+
+       spin_unlock(&vop->reg_lock);
+
+       if (VOP_CTRL_SUPPORT(vop, update_gamma_lut)) {
+               readx_poll_timeout(CTRL_GET, lut_buffer_index,
+                                  dle, dle != lut_idx, 5, 33333);
+               /* FIXME:
+                * update_gamma value auto clean to 0 by HW, should not
+                * bakeup it.
+                */
+               VOP_CTRL_SET(vop, update_gamma_lut, 0);
+       }
+#undef CTRL_GET
+}
+
+void rockchip_vop_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+                                   u16 blue, int regno)
+{
+       struct vop *vop = to_vop(crtc);
+       u32 lut_len = vop->lut_len;
+       u32 r, g, b;
+
+       if (regno >= lut_len || !vop->lut)
+               return;
+
+       r = red * (lut_len - 1) / 0xffff;
+       g = green * (lut_len - 1) / 0xffff;
+       b = blue * (lut_len - 1) / 0xffff;
+       vop->lut[regno] = r * lut_len * lut_len + g * lut_len + b;
+}
+
+void rockchip_vop_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+                                   u16 *blue, int regno)
+{
+       struct vop *vop = to_vop(crtc);
+       u32 lut_len = vop->lut_len;
+       u32 r, g, b;
+
+       if (regno >= lut_len || !vop->lut)
+               return;
+
+       r = (vop->lut[regno] / lut_len / lut_len) & (lut_len - 1);
+       g = (vop->lut[regno] / lut_len) & (lut_len - 1);
+       b = vop->lut[regno] & (lut_len - 1);
+       *red = r * 0xffff / (lut_len - 1);
+       *green = g * 0xffff / (lut_len - 1);
+       *blue = b * 0xffff / (lut_len - 1);
+}
+
 static void vop_power_enable(struct drm_crtc *crtc)
 {
        struct vop *vop = to_vop(crtc);
@@ -826,6 +923,12 @@ static void vop_initial(struct drm_crtc *crtc)
        VOP_CTRL_SET(vop, global_regdone_en, 1);
        VOP_CTRL_SET(vop, dsp_blank, 0);
 
+       /*
+        * restore the lut table.
+        */
+       if (vop->lut_active)
+               vop_crtc_load_lut(crtc);
+
        /*
         * We need to make sure that all windows are disabled before resume
         * the crtc. Otherwise we might try to scan from a destroyed
@@ -1923,6 +2026,7 @@ static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
 }
 
 static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
+       .load_lut = vop_crtc_load_lut,
        .enable = vop_crtc_enable,
        .disable = vop_crtc_disable,
        .mode_fixup = vop_crtc_mode_fixup,
@@ -2044,7 +2148,25 @@ static int vop_crtc_atomic_set_property(struct drm_crtc *crtc,
        return -EINVAL;
 }
 
+static void vop_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
+                              u16 *blue, uint32_t start, uint32_t size)
+{
+       struct vop *vop = to_vop(crtc);
+       int end = min_t(u32, start + size, vop->lut_len);
+       int i;
+
+       if (!vop->lut)
+               return;
+
+       for (i = start; i < end; i++)
+               rockchip_vop_crtc_fb_gamma_set(crtc, red[i], green[i],
+                                              blue[i], i);
+
+       vop_crtc_load_lut(crtc);
+}
+
 static const struct drm_crtc_funcs vop_crtc_funcs = {
+       .gamma_set = vop_crtc_gamma_set,
        .set_config = drm_atomic_helper_set_config,
        .page_flip = drm_atomic_helper_page_flip,
        .destroy = vop_crtc_destroy,
@@ -2270,6 +2392,27 @@ static int vop_create_crtc(struct vop *vop)
                feature |= BIT(ROCKCHIP_DRM_CRTC_FEATURE_AFBDC);
        drm_object_attach_property(&crtc->base, vop->feature_prop,
                                   feature);
+       if (vop->lut_regs) {
+               u16 *r_base, *g_base, *b_base;
+               u32 lut_len = vop->lut_len;
+
+               drm_mode_crtc_set_gamma_size(crtc, lut_len);
+               vop->lut = devm_kmalloc_array(dev, lut_len, sizeof(*vop->lut),
+                                             GFP_KERNEL);
+               if (!vop->lut)
+                       return -ENOMEM;
+
+               r_base = crtc->gamma_store;
+               g_base = r_base + crtc->gamma_size;
+               b_base = g_base + crtc->gamma_size;
+
+               for (i = 0; i < lut_len; i++) {
+                       vop->lut[i] = i * lut_len * lut_len | i * lut_len | i;
+                       rockchip_vop_crtc_fb_gamma_get(crtc, &r_base[i],
+                                                      &g_base[i], &b_base[i],
+                                                      i);
+               }
+       }
 
        return 0;
 
@@ -2521,6 +2664,21 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
        if (!vop->regsbak)
                return -ENOMEM;
 
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       vop->lut_regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(vop->lut_regs)) {
+               dev_warn(vop->dev, "failed to get vop lut registers\n");
+               vop->lut_regs = NULL;
+       }
+       if (vop->lut_regs) {
+               vop->lut_len = resource_size(res) / sizeof(*vop->lut);
+               if (vop->lut_len != 256 && vop->lut_len != 1024) {
+                       dev_err(vop->dev, "unsupport lut sizes %d\n",
+                               vop->lut_len);
+                       return -EINVAL;
+               }
+       }
+
        vop->hclk = devm_clk_get(vop->dev, "hclk_vop");
        if (IS_ERR(vop->hclk)) {
                dev_err(vop->dev, "failed to get hclk source\n");
index 3519520c4a7ab622c9761b493d050ac389f796a8..cd36b6cd339175183d357b7d861af0aedae1768b 100644 (file)
@@ -115,6 +115,8 @@ struct vop_ctrl {
        struct vop_reg dsp_black;
        struct vop_reg dsp_blank;
        struct vop_reg dsp_outzero;
+       struct vop_reg update_gamma_lut;
+       struct vop_reg lut_buffer_index;
        struct vop_reg dsp_lut_en;
 
        struct vop_reg out_mode;
index 34a778f4c5e55e906dd9076a1fa670e5ff6e88f8..e3e2b08377d3cc061176a2fb7c75991fbe708b1c 100644 (file)
@@ -210,6 +210,8 @@ static const struct vop_ctrl rk3288_ctrl_data = {
        .dsp_data_swap = VOP_REG(RK3288_DSP_CTRL0, 0x1f, 12),
        .dsp_ccir656_avg = VOP_REG(RK3288_DSP_CTRL0, 0x1, 20),
        .dsp_blank = VOP_REG(RK3288_DSP_CTRL0, 0x3, 18),
+       .update_gamma_lut = VOP_REG_VER(RK3288_DSP_CTRL1, 0x1, 7, 3, 5, -1),
+       .lut_buffer_index = VOP_REG_VER(RK3399_DBG_POST_REG1, 0x1, 1, 3, 5, -1),
        .dsp_lut_en = VOP_REG(RK3288_DSP_CTRL1, 0x1, 0),
        .out_mode = VOP_REG(RK3288_DSP_CTRL0, 0xf, 0),
 
index 56a723af025fb47734d54c7b0cca9ae31b8f367b..20f3b603dbed706bf395ddc4234c58ce5f213573 100644 (file)
 #define RK3399_YUV2YUV_WIN                     0x02c0
 #define RK3399_YUV2YUV_POST                    0x02c4
 #define RK3399_AUTO_GATING_EN                  0x02cc
+#define RK3399_DBG_POST_REG1                   0x036c
 #define RK3399_WIN0_CSC_COE                    0x03a0
 #define RK3399_WIN1_CSC_COE                    0x03c0
 #define RK3399_WIN2_CSC_COE                    0x03e0