uint32_t *regsbak;
        void __iomem *regs;
+       void __iomem *lut_regs;
 
        /* physical map length of vop register */
        uint32_t len;
        return true;
 }
 
+static bool vop_dsp_lut_is_enabled(struct vop *vop)
+{
+       return vop_read_reg(vop, 0, &vop->data->common->dsp_lut_en);
+}
+
+static void vop_crtc_write_gamma_lut(struct vop *vop, struct drm_crtc *crtc)
+{
+       struct drm_color_lut *lut = crtc->state->gamma_lut->data;
+       unsigned int i;
+
+       for (i = 0; i < crtc->gamma_size; i++) {
+               u32 word;
+
+               word = (drm_color_lut_extract(lut[i].red, 10) << 20) |
+                      (drm_color_lut_extract(lut[i].green, 10) << 10) |
+                       drm_color_lut_extract(lut[i].blue, 10);
+               writel(word, vop->lut_regs + i * 4);
+       }
+}
+
+static void vop_crtc_gamma_set(struct vop *vop, struct drm_crtc *crtc,
+                              struct drm_crtc_state *old_state)
+{
+       struct drm_crtc_state *state = crtc->state;
+       unsigned int idle;
+       int ret;
+
+       if (!vop->lut_regs)
+               return;
+       /*
+        * To disable gamma (gamma_lut is null) or to write
+        * an update to the LUT, clear dsp_lut_en.
+        */
+       spin_lock(&vop->reg_lock);
+       VOP_REG_SET(vop, common, dsp_lut_en, 0);
+       vop_cfg_done(vop);
+       spin_unlock(&vop->reg_lock);
+
+       /*
+        * In order to write the LUT to the internal memory,
+        * we need to first make sure the dsp_lut_en bit is cleared.
+        */
+       ret = readx_poll_timeout(vop_dsp_lut_is_enabled, vop,
+                                idle, !idle, 5, 30 * 1000);
+       if (ret) {
+               DRM_DEV_ERROR(vop->dev, "display LUT RAM enable timeout!\n");
+               return;
+       }
+
+       if (!state->gamma_lut)
+               return;
+
+       spin_lock(&vop->reg_lock);
+       vop_crtc_write_gamma_lut(vop, crtc);
+       VOP_REG_SET(vop, common, dsp_lut_en, 1);
+       vop_cfg_done(vop);
+       spin_unlock(&vop->reg_lock);
+}
+
+static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
+                                 struct drm_crtc_state *old_crtc_state)
+{
+       struct vop *vop = to_vop(crtc);
+
+       /*
+        * Only update GAMMA if the 'active' flag is not changed,
+        * otherwise it's updated by .atomic_enable.
+        */
+       if (crtc->state->color_mgmt_changed &&
+           !crtc->state->active_changed)
+               vop_crtc_gamma_set(vop, crtc, old_crtc_state);
+}
+
 static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
                                   struct drm_crtc_state *old_state)
 {
                return;
        }
 
+       /*
+        * If we have a GAMMA LUT in the state, then let's make sure
+        * it's updated. We might be coming out of suspend,
+        * which means the LUT internal memory needs to be re-written.
+        */
+       if (crtc->state->gamma_lut)
+               vop_crtc_gamma_set(vop, crtc, old_state);
+
        mutex_lock(&vop->vop_lock);
 
        WARN_ON(vop->event);
        synchronize_irq(vop->irq);
 }
 
+static int vop_crtc_atomic_check(struct drm_crtc *crtc,
+                                struct drm_crtc_state *crtc_state)
+{
+       struct vop *vop = to_vop(crtc);
+
+       if (vop->lut_regs && crtc_state->color_mgmt_changed &&
+           crtc_state->gamma_lut) {
+               unsigned int len;
+
+               len = drm_color_lut_size(crtc_state->gamma_lut);
+               if (len != crtc->gamma_size) {
+                       DRM_DEBUG_KMS("Invalid LUT size; got %d, expected %d\n",
+                                     len, crtc->gamma_size);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
                                  struct drm_crtc_state *old_crtc_state)
 {
 
 static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
        .mode_fixup = vop_crtc_mode_fixup,
+       .atomic_check = vop_crtc_atomic_check,
+       .atomic_begin = vop_crtc_atomic_begin,
        .atomic_flush = vop_crtc_atomic_flush,
        .atomic_enable = vop_crtc_atomic_enable,
        .atomic_disable = vop_crtc_atomic_disable,
        .disable_vblank = vop_crtc_disable_vblank,
        .set_crc_source = vop_crtc_set_crc_source,
        .verify_crc_source = vop_crtc_verify_crc_source,
+       .gamma_set = drm_atomic_helper_legacy_gamma_set,
 };
 
 static void vop_fb_unref_worker(struct drm_flip_work *work, void *val)
                goto err_cleanup_planes;
 
        drm_crtc_helper_add(crtc, &vop_crtc_helper_funcs);
+       if (vop->lut_regs) {
+               drm_mode_crtc_set_gamma_size(crtc, vop_data->lut_size);
+               drm_crtc_enable_color_mgmt(crtc, 0, false, vop_data->lut_size);
+       }
 
        /*
         * Create drm_planes for overlay windows with possible_crtcs restricted
        if (IS_ERR(vop->regs))
                return PTR_ERR(vop->regs);
 
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (res) {
+               if (!vop_data->lut_size) {
+                       DRM_DEV_ERROR(dev, "no gamma LUT size defined\n");
+                       return -EINVAL;
+               }
+               vop->lut_regs = devm_ioremap_resource(dev, res);
+               if (IS_ERR(vop->lut_regs))
+                       return PTR_ERR(vop->lut_regs);
+       }
+
        vop->regsbak = devm_kzalloc(dev, vop->len, GFP_KERNEL);
        if (!vop->regsbak)
                return -ENOMEM;