From: kalakodima venkata rajesh <venkatarajesh.kalakod...@in.bosch.com>

This is the out-of-tree patch for DU CMM driver support from
Yocto release v3.4.0.

Link: 
https://github.com/renesas-rcar/du_cmm/commit/2d8ea2b667ad4616aa639c54ecc11f7c4b58959d.patch

Following is from the patch description:

    du_cmm: Release for Yocto v3.4.0

    This patch made the following correspondence.

      - Corresponds to kernel v 4.14.
      - Double buffer only is supported.
      - Fix CLU / LUT update timing.
      - Add CMM Channel occupation mode.
      - Fix Close process.

Signed-off-by: Koji Matsuoka <koji.matsuoka...@renesas.com>
Signed-off-by: Tsutomu Muroya <mur...@ksk.co.jp>
Signed-off-by: Steve Longerbeam <steve_longerb...@mentor.com>

      - Removal of rcar specific ioctals
      - Resolved checkpatch errors
      - Resolved merge conflicts according to latest version
      - Included CMM drivers and included files from base patch
      - Removed rcar_du_drm.h include file

Signed-off-by: kalakodima venkata rajesh <venkatarajesh.kalakod...@in.bosch.com>
---
 drivers/gpu/drm/rcar-du/Makefile        |    2 +
 drivers/gpu/drm/rcar-du/rcar_du_cmm.c   | 1200 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/rcar-du/rcar_du_crtc.c  |   24 +
 drivers/gpu/drm/rcar-du/rcar_du_crtc.h  |   16 +
 drivers/gpu/drm/rcar-du/rcar_du_drv.c   |   43 +-
 drivers/gpu/drm/rcar-du/rcar_du_drv.h   |   16 +-
 drivers/gpu/drm/rcar-du/rcar_du_group.c |    5 +
 drivers/gpu/drm/rcar-du/rcar_du_regs.h  |   92 +++
 include/drm/drm_ioctl.h                 |    7 +
 9 files changed, 1398 insertions(+), 7 deletions(-)
 create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_cmm.c

diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
index 2a3b8d7..595e719 100644
--- a/drivers/gpu/drm/rcar-du/Makefile
+++ b/drivers/gpu/drm/rcar-du/Makefile
@@ -6,12 +6,14 @@ rcar-du-drm-y := rcar_du_crtc.o \
                 rcar_du_kms.o \
                 rcar_du_plane.o
 
+rcar-du-drm-y += rcar_du_cmm.o
 rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS)    += rcar_du_of.o \
                                           rcar_du_of_lvds_r8a7790.dtb.o \
                                           rcar_du_of_lvds_r8a7791.dtb.o \
                                           rcar_du_of_lvds_r8a7793.dtb.o \
                                           rcar_du_of_lvds_r8a7795.dtb.o \
                                           rcar_du_of_lvds_r8a7796.dtb.o
+
 rcar-du-drm-$(CONFIG_DRM_RCAR_VSP)     += rcar_du_vsp.o
 
 obj-$(CONFIG_DRM_RCAR_DU)              += rcar-du-drm.o
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_cmm.c 
b/drivers/gpu/drm/rcar-du/rcar_du_cmm.c
new file mode 100644
index 0000000..ac613a6e
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_cmm.c
@@ -0,0 +1,1200 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*************************************************************************/ /*
+ * DU CMM
+ *
+ * Copyright (C) 2018 Renesas Electronics Corporation
+ *
+ * License        Dual MIT/GPLv2
+ *
+ * The contents of this file are subject to the MIT license as set out below.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 ("GPL") in which case the 
provisions
+ * of GPL are applicable instead of those above.
+ *
+ * If you wish to allow use of your version of this file only under the terms 
of
+ * GPL, and not to allow others to use your version of this file under the 
terms
+ * of the MIT license, indicate your decision by deleting the provisions above
+ * and replace them with the notice and other provisions required by GPL as set
+ * out in the file called "GPL-COPYING" included in this distribution. If you 
do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under the terms of either the MIT license or GPL.
+ *
+ * This License is also included in this distribution in the file called
+ * "MIT-COPYING".
+ *
+ * EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS
+ * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
INCLUDING
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE 
AUTHORS
+ * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+ * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
SOFTWARE.
+ *
+ *
+ * GPLv2:
+ * If you wish to use this file under the terms of GPL, following terms are
+ * effective.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */ /*************************************************************************/
+#include <linux/syscalls.h>
+#include <linux/workqueue.h>
+
+#include <linux/reset.h>
+#include <linux/sys_soc.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_plane.h"
+#include "rcar_du_regs.h"
+#include <linux/clk.h>
+
+/* #define DEBUG_PROCE_TIME 1 */
+
+#define CMM_LUT_NUM 256
+#define CMM_CLU_NUM (17 * 17 * 17)
+#define CMM_HGO_NUM 64
+/* rcar_du_drm.h Include */
+#define LUT_DOUBLE_BUFFER_AUTO         0
+#define LUT_DOUBLE_BUFFER_A            1
+#define LUT_DOUBLE_BUFFER_B            2
+/* DRM_RCAR_DU_CMM_WAIT_EVENT: DU-CMM done event */
+#define CMM_EVENT_CLU_DONE             BIT(0)
+#define CMM_EVENT_HGO_DONE             BIT(1)
+#define CMM_EVENT_LUT_DONE             BIT(2)
+
+#define CLU_DOUBLE_BUFFER_AUTO         0
+#define CLU_DOUBLE_BUFFER_A            1
+#define CLU_DOUBLE_BUFFER_B            2
+enum {
+       QUE_STAT_PENDING,
+       QUE_STAT_ACTIVE,
+       QUE_STAT_DONE,
+};
+
+static const struct soc_device_attribute rcar_du_cmm_r8a7795_es1[] = {
+       { .soc_id = "r8a7795", .revision = "ES1.*" },
+       { /* sentinel */ }
+};
+
+struct rcar_du_cmm;
+struct rcar_du_cmm_file_priv;
+
+struct rcar_du_cmm_pending_event {
+       struct list_head link;
+       struct list_head  fpriv_link;
+       unsigned int event;
+       unsigned int stat;
+       unsigned long callback_data;
+       struct drm_gem_object *gem_obj;
+       struct rcar_du_cmm *du_cmm;
+       struct rcar_du_cmm_file_priv *fpriv;
+};
+
+struct cmm_module_t {
+       struct list_head list;
+       union {
+               struct {
+                       struct rcar_du_cmm_pending_event *p;
+                       int buf_mode;
+                       bool one_side;
+               };
+               int reset;
+       };
+};
+
+struct cmm_reg_save {
+#ifdef CONFIG_PM_SLEEP
+       wait_queue_head_t wait;
+
+       u32 *lut_table;
+       u32 *clu_table;
+#endif /* CONFIG_PM_SLEEP */
+
+       u32 cm2_ctl0;   /* CM2_CTL0 */
+       u32 hgo_offset; /* CMM_HGO_OFFSET */
+       u32 hgo_size;   /* CMM_HGO_SIZE */
+       u32 hgo_mode;   /* CMM_HGO_MODE */
+};
+
+struct rcar_du_cmm {
+       struct rcar_du_crtc *rcrtc;
+
+       /* CMM base address */
+       void __iomem *cmm_base;
+       struct clk *clock;
+
+       struct cmm_module_t lut;
+       struct cmm_module_t clu;
+       struct cmm_module_t hgo;
+
+       struct mutex lock;      /* lock for register setting */
+       struct workqueue_struct *workqueue;
+       struct work_struct work;
+
+       struct cmm_reg_save reg_save;
+       bool active;
+       bool dbuf;
+       bool clu_dbuf;
+       bool init;
+       bool direct;
+       bool vsync;
+       bool authority;
+       pid_t pid;
+       bool soc_support;
+};
+
+struct rcar_du_cmm_file_priv {
+       wait_queue_head_t event_wait;
+       struct list_head list;
+       struct list_head active_list;
+       struct list_head *done_list;
+};
+
+static DEFINE_MUTEX(cmm_event_lock);
+static DEFINE_SPINLOCK(cmm_direct_lock);
+
+static inline void event_prev_cancel_locked(struct cmm_module_t *module);
+
+static inline u32 cmm_index(struct rcar_du_cmm *_cmm)
+{
+       struct rcar_du_device *rcdu = _cmm->rcrtc->group->dev;
+
+       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_R8A77965_REGS)) {
+               if ((_cmm)->rcrtc->index == 3)
+                       return 2;
+       }
+       return (_cmm)->rcrtc->index;
+}
+
+#define cmm_done_list(_cmm, _fpriv) \
+       (&((_fpriv)->done_list[cmm_index(_cmm)]))
+
+static inline u32 rcar_du_cmm_read(struct rcar_du_cmm *du_cmm, u32 reg)
+{
+       return ioread32(du_cmm->cmm_base + reg);
+}
+
+static inline void rcar_du_cmm_write(struct rcar_du_cmm *du_cmm,
+                                    u32 reg, u32 data)
+{
+       iowrite32(data, du_cmm->cmm_base + reg);
+}
+
+/* create default CLU table data */
+static inline u32 index_to_clu_data(int index)
+{
+       int r, g, b;
+
+       r = index % 17;
+       index /= 17;
+       g = index % 17;
+       index /= 17;
+       b = index % 17;
+
+       r = (r << 20);
+       if (r > (255 << 16))
+               r = (255 << 16);
+       g = (g << 12);
+       if (g > (255 << 8))
+               g = (255 << 8);
+       b = (b << 4);
+       if (b > (255 << 0))
+               b = (255 << 0);
+
+       return r | g | b;
+}
+
+#ifdef DEBUG_PROCE_TIME
+static long long diff_timevals(struct timeval *start, struct timeval *end)
+{
+       return (end->tv_sec * 1000000LL + end->tv_usec) -
+               (start->tv_sec * 1000000LL + start->tv_usec);
+}
+#endif
+
+static void du_cmm_clk(struct rcar_du_cmm *du_cmm, bool on)
+{
+       if (on)
+               clk_prepare_enable(du_cmm->clock);
+       else
+               clk_disable_unprepare(du_cmm->clock);
+}
+
+int rcar_du_cmm_start_stop(struct rcar_du_crtc *rcrtc, bool on)
+{
+       struct rcar_du_cmm *du_cmm = rcrtc->cmm_handle;
+       int i;
+       u32 table_data;
+       const struct drm_display_mode *mode;
+       int w, h, x, y;
+
+       if (!du_cmm)
+               return -EINVAL;
+
+       mutex_lock(&du_cmm->lock);
+
+       if (!on) {
+               du_cmm->active = false;
+
+               rcar_du_cmm_write(du_cmm, CMM_LUT_CTRL, 0x00000000);
+               rcar_du_cmm_write(du_cmm, CMM_CLU_CTRL, 0x00000000);
+
+               du_cmm_clk(du_cmm, false);
+
+               goto end;
+       }
+
+       du_cmm_clk(du_cmm, true);
+
+       if (du_cmm->init)
+               goto init_done;
+
+       du_cmm->init = true;
+
+       mode = &du_cmm->rcrtc->crtc.mode;
+
+       x = (du_cmm->reg_save.hgo_offset >> 16) & 0xFFFF;
+       y = (du_cmm->reg_save.hgo_offset >> 0)  & 0xFFFF;
+       w = (du_cmm->reg_save.hgo_size >> 16) & 0xFFFF;
+       h = (du_cmm->reg_save.hgo_size >> 0)  & 0xFFFF;
+       if ((mode->hdisplay < (w + x)) || w == 0) {
+               x = 0;
+               w = mode->hdisplay;
+       }
+       if ((mode->vdisplay < (h + y)) || h == 0) {
+               y = 0;
+               h = mode->vdisplay;
+       }
+       du_cmm->reg_save.hgo_offset = (x << 16) | y;
+       du_cmm->reg_save.hgo_size = (w << 16) | h;
+
+       if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+               du_cmm->reg_save.cm2_ctl0 |= CMM_CTL0_VPOL;
+       else
+               du_cmm->reg_save.cm2_ctl0 &= ~CMM_CTL0_VPOL;
+
+       rcar_du_cmm_write(du_cmm, CM2_CTL0, du_cmm->reg_save.cm2_ctl0);
+       rcar_du_cmm_write(du_cmm, CMM_HGO_OFFSET, du_cmm->reg_save.hgo_offset);
+       rcar_du_cmm_write(du_cmm, CMM_HGO_SIZE, du_cmm->reg_save.hgo_size);
+       rcar_du_cmm_write(du_cmm, CMM_HGO_MODE, du_cmm->reg_save.hgo_mode);
+       rcar_du_cmm_write(du_cmm, CMM_HGO_LB_TH, 0);
+       rcar_du_cmm_write(du_cmm, CMM_HGO_LB0_H, 0);
+       rcar_du_cmm_write(du_cmm, CMM_HGO_LB0_V, 0);
+       rcar_du_cmm_write(du_cmm, CMM_HGO_LB1_H, 0);
+       rcar_du_cmm_write(du_cmm, CMM_HGO_LB1_V, 0);
+       rcar_du_cmm_write(du_cmm, CMM_HGO_LB2_H, 0);
+       rcar_du_cmm_write(du_cmm, CMM_HGO_LB2_V, 0);
+       rcar_du_cmm_write(du_cmm, CMM_HGO_LB3_H, 0);
+       rcar_du_cmm_write(du_cmm, CMM_HGO_LB3_V, 0);
+
+       /* init color table */
+       for (i = 0; i < CMM_LUT_NUM; i++) {
+       #ifdef CONFIG_PM_SLEEP
+               table_data = du_cmm->reg_save.lut_table[i];
+       #else
+               table_data = ((i << 16) | (i << 8) | (i << 0));
+       #endif /* CONFIG_PM_SLEEP */
+               rcar_du_cmm_write(du_cmm, CMM_LUT_TBLA(i), table_data);
+
+               if (du_cmm->dbuf)
+                       rcar_du_cmm_write(du_cmm, CMM_LUT_TBLB(i),
+                                         table_data);
+       }
+
+       rcar_du_cmm_write(du_cmm, CMM_CLU_CTRL,
+                         CMM_CLU_CTRL_AAI | CMM_CLU_CTRL_MVS);
+
+       rcar_du_cmm_write(du_cmm, CMM_CLU_ADDR, 0);
+       if (du_cmm->clu_dbuf)
+               rcar_du_cmm_write(du_cmm, CMM_CLU_ADDR2, 0);
+
+       for (i = 0; i < CMM_CLU_NUM; i++) {
+       #ifdef CONFIG_PM_SLEEP
+               table_data = du_cmm->reg_save.clu_table[i];
+       #else
+               table_data = index_to_clu_data(i);
+       #endif /* CONFIG_PM_SLEEP */
+               rcar_du_cmm_write(du_cmm, CMM_CLU_DATA, table_data);
+
+               if (du_cmm->dbuf)
+                       rcar_du_cmm_write(du_cmm, CMM_CLU_DATA2,
+                                         table_data);
+       }
+
+init_done:
+       /* enable color table */
+       rcar_du_cmm_write(du_cmm, CMM_LUT_CTRL, CMM_LUT_CTRL_EN);
+       rcar_du_cmm_write(du_cmm, CMM_CLU_CTRL, CMM_CLU_CTRL_AAI |
+                         CMM_CLU_CTRL_MVS | CMM_CLU_CTRL_EN);
+
+       du_cmm->active = true;
+end:
+       mutex_unlock(&du_cmm->lock);
+
+       return 0;
+}
+
+#define gem_to_vaddr(gem_obj) \
+       (container_of((gem_obj), struct drm_gem_cma_object, base)->vaddr)
+
+static inline void cmm_vblank_put(struct rcar_du_cmm_pending_event *p)
+{
+       if (p->du_cmm)
+               drm_crtc_vblank_put(&p->du_cmm->rcrtc->crtc);
+}
+
+static inline void
+cmm_gem_object_unreference(struct rcar_du_cmm_pending_event *p)
+{
+       if (p->gem_obj)
+               drm_gem_object_unreference_unlocked(p->gem_obj);
+}
+
+static inline void _event_done_locked(struct rcar_du_cmm_pending_event *p)
+{
+       cmm_gem_object_unreference(p);
+
+       if (p->fpriv) {
+               p->stat = QUE_STAT_DONE;
+               list_del(&p->link); /* delete from p->fpriv->active_list */
+               list_add_tail(&p->link, cmm_done_list(p->du_cmm, p->fpriv));
+               wake_up_interruptible(&p->fpriv->event_wait);
+       } else {
+               /* link deleted by rcar_du_cmm_postclose */
+               kfree(p);
+       }
+}
+
+/* cancel from active_list (case of LUT/CLU double buffer mode) */
+static inline void event_prev_cancel_locked(struct cmm_module_t *module)
+{
+       struct rcar_du_cmm_pending_event *p = module->p;
+
+       if (!p)
+               return;
+
+       module->p = NULL;
+
+       _event_done_locked(p);
+}
+
+static inline void event_done(struct rcar_du_cmm_pending_event *p)
+{
+       /* vblank is put */
+
+       mutex_lock(&cmm_event_lock);
+
+       _event_done_locked(p);
+
+       mutex_unlock(&cmm_event_lock);
+}
+
+static inline void lc_event_done(struct cmm_module_t *module,
+                                struct rcar_du_cmm_pending_event *p,
+                                bool done)
+{
+       /* vblank is put */
+
+       mutex_lock(&cmm_event_lock);
+
+       if (!done && list_empty(&module->list))
+               module->p = p;
+       else
+               _event_done_locked(p);
+
+       mutex_unlock(&cmm_event_lock);
+}
+
+static inline struct rcar_du_cmm_pending_event *
+event_pop_locked(struct cmm_module_t *module)
+{
+       struct rcar_du_cmm_pending_event *p =
+               list_first_entry(&module->list,
+                                struct rcar_du_cmm_pending_event,
+                                link);
+
+       p->stat = QUE_STAT_ACTIVE;
+       list_del(&p->link); /* delete from du_cmm->[lut|clu|hgo].list */
+       list_add_tail(&p->link, &p->fpriv->active_list);
+       cmm_vblank_put(p);
+
+       return p;
+}
+
+struct rcar_du_cmm_work_stat {
+       union {
+               struct {
+                       struct rcar_du_cmm_pending_event *p;
+                       bool done;
+                       bool table_copy;
+               };
+               struct {
+                       struct rcar_du_cmm_pending_event *p2;
+                       bool reset;
+               };
+       };
+};
+
+static inline void one_side(struct rcar_du_cmm *du_cmm,
+                           struct cmm_module_t *module,
+                           bool on)
+{
+       if (on && !module->one_side) {
+               module->one_side = true;
+               drm_crtc_vblank_get(&du_cmm->rcrtc->crtc);
+       } else if (!on && module->one_side) {
+               module->one_side = false;
+               drm_crtc_vblank_put(&du_cmm->rcrtc->crtc);
+       }
+}
+
+/* pop LUT que */
+static int lut_pop_locked(struct rcar_du_cmm *du_cmm,
+                         struct rcar_du_cmm_work_stat *stat)
+{
+       bool is_one_side = false;
+
+       stat->done = true;
+       stat->table_copy = false;
+
+       if (!list_empty(&du_cmm->lut.list)) {
+               stat->p = event_pop_locked(&du_cmm->lut);
+
+               /* prev lut table */
+               event_prev_cancel_locked(&du_cmm->lut);
+
+               if (du_cmm->lut.buf_mode == LUT_DOUBLE_BUFFER_AUTO) {
+                       is_one_side = true;
+                       if (list_empty(&du_cmm->lut.list))
+                               stat->done = false;
+               }
+
+       } else if (du_cmm->lut.p) {
+               /* prev lut table */
+               stat->p = du_cmm->lut.p;
+               du_cmm->lut.p = NULL;
+       } else {
+               stat->done = false;
+               stat->p = NULL;
+               stat->table_copy = du_cmm->lut.one_side;
+       }
+
+       one_side(du_cmm, &du_cmm->lut, is_one_side);
+
+       return 0;
+}
+
+static int lut_table_copy(struct rcar_du_cmm *du_cmm)
+{
+       int i;
+       u32 src, dst;
+
+       if (rcar_du_cmm_read(du_cmm, CM2_CTL1) & CMM_CTL1_BFS) {
+               dst = CMM_LUT_TBLA(0);
+               src = CMM_LUT_TBLB(0);
+       } else {
+               dst = CMM_LUT_TBLB(0);
+               src = CMM_LUT_TBLA(0);
+       }
+
+       for (i = 0; i < CMM_LUT_NUM; i++) {
+               rcar_du_cmm_write(du_cmm, dst, rcar_du_cmm_read(du_cmm, src));
+               dst += 4;
+               src += 4;
+       }
+
+       return 0;
+}
+
+/* set 1D look up table */
+static int lut_set(struct rcar_du_cmm *du_cmm,
+                  struct rcar_du_cmm_work_stat *stat)
+{
+       int i;
+       u32 lut_base;
+       u32 *lut_buf;
+
+       if (!stat->p) {
+               if (stat->table_copy)
+                       lut_table_copy(du_cmm);
+               return 0; /* skip */
+       }
+
+       /* set LUT */
+       switch (du_cmm->lut.buf_mode) {
+       case LUT_DOUBLE_BUFFER_A:
+               lut_base = CMM_LUT_TBLA(0);
+               break;
+
+       case LUT_DOUBLE_BUFFER_AUTO:
+               if (rcar_du_cmm_read(du_cmm, CM2_CTL1) & CMM_CTL1_BFS) {
+                       lut_base = CMM_LUT_TBLA(0);
+                       break;
+               }
+               lut_base = CMM_LUT_TBLB(0);
+               break;
+       case LUT_DOUBLE_BUFFER_B:
+               lut_base = CMM_LUT_TBLB(0);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       lut_buf = gem_to_vaddr(stat->p->gem_obj);
+       for (i = 0; i < CMM_LUT_NUM; i++)
+               rcar_du_cmm_write(du_cmm, lut_base + i * 4, lut_buf[i]);
+
+       lc_event_done(&du_cmm->lut, stat->p, stat->done);
+
+       return 0;
+}
+
+/* pop CLU que */
+static int clu_pop_locked(struct rcar_du_cmm *du_cmm,
+                         struct rcar_du_cmm_work_stat *stat)
+{
+       bool is_one_side = false;
+
+       stat->done = true;
+       stat->table_copy = false;
+
+       if (!list_empty(&du_cmm->clu.list)) {
+               stat->p = event_pop_locked(&du_cmm->clu);
+
+               /* prev clu table */
+               event_prev_cancel_locked(&du_cmm->clu);
+
+               if (du_cmm->clu.buf_mode == CLU_DOUBLE_BUFFER_AUTO) {
+                       is_one_side = true;
+                       if (list_empty(&du_cmm->clu.list))
+                               stat->done = false;
+               }
+
+       } else if (du_cmm->clu.p) {
+               /* prev clu table */
+               stat->p = du_cmm->clu.p;
+               du_cmm->clu.p = NULL;
+       } else {
+               stat->done = false;
+               stat->p = NULL;
+               stat->table_copy = du_cmm->clu.one_side;
+       }
+
+       one_side(du_cmm, &du_cmm->clu, is_one_side);
+
+       return 0;
+}
+
+static int clu_table_copy(struct rcar_du_cmm *du_cmm)
+{
+       int i, j, k;
+       u32 src_addr, src_data, dst_addr, dst_data;
+
+       if (rcar_du_cmm_read(du_cmm, CM2_CTL1) & CMM_CTL1_BFS) {
+               dst_addr = CMM_CLU_ADDR;
+               dst_data = CMM_CLU_DATA;
+               src_addr = CMM_CLU_ADDR2;
+               src_data = CMM_CLU_DATA2;
+       } else {
+               dst_addr = CMM_CLU_ADDR2;
+               dst_data = CMM_CLU_DATA2;
+               src_addr = CMM_CLU_ADDR;
+               src_data = CMM_CLU_DATA;
+       }
+
+       rcar_du_cmm_write(du_cmm, dst_addr, 0);
+       for (i = 0; i < 17; i++) {
+               for (j = 0; j < 17; j++) {
+                       for (k = 0; k < 17; k++) {
+                               rcar_du_cmm_write(du_cmm, src_addr,
+                                                 (k << 16) | (j << 8) |
+                                                 (i << 0));
+                               rcar_du_cmm_write(du_cmm, dst_data,
+                                                 rcar_du_cmm_read(du_cmm,
+                                                                  src_data));
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/* set 3D look up table */
+static int clu_set(struct rcar_du_cmm *du_cmm,
+                  struct rcar_du_cmm_work_stat *stat)
+{
+       int i;
+       u32 addr_reg, data_reg;
+       u32 *clu_buf;
+
+       if (!stat->p) {
+               if (stat->table_copy)
+                       clu_table_copy(du_cmm);
+               return 0; /* skip */
+       }
+
+       /* set CLU */
+       switch (du_cmm->clu.buf_mode) {
+       case CLU_DOUBLE_BUFFER_A:
+               addr_reg = CMM_CLU_ADDR;
+               data_reg = CMM_CLU_DATA;
+               break;
+
+       case CLU_DOUBLE_BUFFER_AUTO:
+               if (rcar_du_cmm_read(du_cmm, CM2_CTL1) & CMM_CTL1_BFS) {
+                       addr_reg = CMM_CLU_ADDR;
+                       data_reg = CMM_CLU_DATA;
+                       break;
+               }
+               addr_reg = CMM_CLU_ADDR2;
+               data_reg = CMM_CLU_DATA2;
+               break;
+       case CLU_DOUBLE_BUFFER_B:
+               addr_reg = CMM_CLU_ADDR2;
+               data_reg = CMM_CLU_DATA2;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       clu_buf = gem_to_vaddr(stat->p->gem_obj);
+       rcar_du_cmm_write(du_cmm, addr_reg, 0);
+       for (i = 0; i < CMM_CLU_NUM; i++)
+               rcar_du_cmm_write(du_cmm, data_reg, clu_buf[i]);
+
+       lc_event_done(&du_cmm->clu, stat->p, stat->done);
+
+       return 0;
+}
+
+/* pop HGO que */
+static int hgo_pop_locked(struct rcar_du_cmm *du_cmm,
+                         struct rcar_du_cmm_work_stat *stat)
+{
+       struct rcar_du_cmm_pending_event *_p = NULL;
+
+       if (!list_empty(&du_cmm->hgo.list))
+               _p = event_pop_locked(&du_cmm->hgo);
+
+       if (du_cmm->hgo.reset) {
+               drm_crtc_vblank_put(&du_cmm->rcrtc->crtc);
+               du_cmm->hgo.reset = 0;
+               stat->reset = true;
+       } else {
+               stat->reset = false;
+       }
+
+       stat->p2 = _p;
+
+       return 0;
+}
+
+/* get histogram */
+static int hgo_get(struct rcar_du_cmm *du_cmm,
+                  struct rcar_du_cmm_work_stat *stat)
+{
+       int i, j;
+       const u32 histo_offset[3] = {
+               CMM_HGO_R_HISTO(0),
+               CMM_HGO_G_HISTO(0),
+               CMM_HGO_B_HISTO(0),
+       };
+       void *vaddr;
+
+       if (!stat->p2) {
+               if (stat->reset)
+                       goto hgo_reset;
+
+               return 0; /* skip */
+       }
+
+       vaddr = gem_to_vaddr(stat->p2->gem_obj);
+       for (i = 0; i < 3; i++) {
+               u32 *hgo_buf = vaddr + CMM_HGO_NUM * 4 * i;
+
+               for (j = 0; j < CMM_HGO_NUM; j++)
+                       hgo_buf[j] = rcar_du_cmm_read(du_cmm,
+                                                     histo_offset[i] + j * 4);
+       }
+
+       event_done(stat->p2);
+
+hgo_reset:
+       rcar_du_cmm_write(du_cmm, CMM_HGO_REGRST, CMM_HGO_REGRST_RCLEA);
+
+       return 0;
+}
+
+static bool du_cmm_vsync_get(struct rcar_du_cmm *du_cmm)
+{
+       unsigned long flags;
+       bool vsync;
+
+       spin_lock_irqsave(&cmm_direct_lock, flags);
+       vsync = du_cmm->vsync;
+       du_cmm->vsync = false;
+       spin_unlock_irqrestore(&cmm_direct_lock, flags);
+
+       return vsync;
+}
+
+static void du_cmm_vsync_set(struct rcar_du_cmm *du_cmm, bool vsync)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&cmm_direct_lock, flags);
+       du_cmm->vsync = vsync;
+       spin_unlock_irqrestore(&cmm_direct_lock, flags);
+}
+
+static void du_cmm_work(struct work_struct *work)
+{
+       struct rcar_du_cmm *du_cmm =
+                       container_of(work, struct rcar_du_cmm, work);
+       struct rcar_du_cmm_work_stat s_lut;
+       struct rcar_du_cmm_work_stat s_clu;
+       struct rcar_du_cmm_work_stat s_hgo;
+#ifdef DEBUG_PROCE_TIME
+       struct timeval start_time, end_time;
+       unsigned long lut_time, clu_time, hgo_time;
+#endif
+       bool vsync_status = false;
+
+       memset(&s_lut, 0, sizeof(struct rcar_du_cmm_work_stat));
+       memset(&s_clu, 0, sizeof(struct rcar_du_cmm_work_stat));
+       memset(&s_hgo, 0, sizeof(struct rcar_du_cmm_work_stat));
+
+       vsync_status = du_cmm_vsync_get(du_cmm);
+
+       mutex_lock(&cmm_event_lock);
+
+       lut_pop_locked(du_cmm, &s_lut);
+       clu_pop_locked(du_cmm, &s_clu);
+       if (vsync_status)
+               hgo_pop_locked(du_cmm, &s_hgo);
+
+       mutex_unlock(&cmm_event_lock);
+
+       /* set LUT */
+#ifdef DEBUG_PROCE_TIME
+       do_gettimeofday(&start_time);
+#endif
+       lut_set(du_cmm, &s_lut);
+#ifdef DEBUG_PROCE_TIME
+       do_gettimeofday(&end_time);
+       lut_time = (long)diff_timevals(&start_time, &end_time);
+#endif
+
+       /* set CLU */
+#ifdef DEBUG_PROCE_TIME
+       do_gettimeofday(&start_time);
+#endif
+       clu_set(du_cmm, &s_clu);
+#ifdef DEBUG_PROCE_TIME
+       do_gettimeofday(&end_time);
+       clu_time = (long)diff_timevals(&start_time, &end_time);
+#endif
+
+       /* get HGO */
+#ifdef DEBUG_PROCE_TIME
+       do_gettimeofday(&start_time);
+#endif
+       if (vsync_status)
+               hgo_get(du_cmm, &s_hgo);
+#ifdef DEBUG_PROCE_TIME
+       do_gettimeofday(&end_time);
+       hgo_time = (long)diff_timevals(&start_time, &end_time);
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+       wake_up_interruptible(&du_cmm->reg_save.wait);
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef DEBUG_PROCE_TIME
+       {
+               struct rcar_du_device *rcdu = du_cmm->rcrtc->group->dev;
+
+               if (s_lut.p)
+                       dev_info(rcdu->dev, "LUT %ld usec.\n", lut_time);
+               if (s_clu.p)
+                       dev_info(rcdu->dev, "LUT %ld usec.\n", clu_time);
+               if (s_hgo.p2)
+                       dev_info(rcdu->dev, "HGO %ld usec.\n", hgo_time);
+       }
+#endif
+}
+
+static int du_cmm_que_empty(struct rcar_du_cmm *du_cmm)
+{
+       if (list_empty(&du_cmm->lut.list) && !du_cmm->lut.p &&
+           !du_cmm->lut.one_side &&
+           list_empty(&du_cmm->clu.list) && !du_cmm->clu.p &&
+           !du_cmm->clu.one_side &&
+           list_empty(&du_cmm->hgo.list) && !du_cmm->hgo.reset)
+               return 1;
+
+       return 0;
+}
+
+void rcar_du_cmm_kick(struct rcar_du_crtc *rcrtc)
+{
+       struct rcar_du_cmm *du_cmm = rcrtc->cmm_handle;
+
+       if (!du_cmm)
+               return;
+
+       if (!du_cmm->active)
+               return;
+
+       if (!du_cmm_que_empty(du_cmm)) {
+               du_cmm_vsync_set(du_cmm, true);
+               queue_work(du_cmm->workqueue, &du_cmm->work);
+       }
+}
+
+#ifdef CONFIG_PM_SLEEP
+int rcar_du_cmm_pm_suspend(struct rcar_du_crtc *rcrtc)
+{
+       struct rcar_du_cmm *du_cmm = rcrtc->cmm_handle;
+       struct rcar_du_device *rcdu = rcrtc->group->dev;
+       int i, j, k, index;
+       int ret;
+
+       if (!du_cmm)
+               return 0;
+
+       ret = wait_event_timeout(du_cmm->reg_save.wait,
+                                du_cmm_que_empty(du_cmm),
+                                msecs_to_jiffies(500));
+       if (ret == 0)
+               dev_err(rcdu->dev, "rcar-du cmm suspend : timeout\n");
+
+       if (!du_cmm->init)
+               return 0;
+
+       du_cmm->init = false;
+
+       if (!du_cmm->active)
+               du_cmm_clk(du_cmm, true);
+
+       /* table save */
+       for (i = 0; i < CMM_LUT_NUM; i++) {
+               du_cmm->reg_save.lut_table[i] =
+                       rcar_du_cmm_read(du_cmm, CMM_LUT_TBLA(i));
+       }
+
+       index = 0;
+       for (i = 0; i < 17; i++) {
+               for (j = 0; j < 17; j++) {
+                       for (k = 0; k < 17; k++) {
+                               rcar_du_cmm_write(du_cmm, CMM_CLU_ADDR,
+                                                 (k << 16) | (j << 8) |
+                                                 (i << 0));
+                               du_cmm->reg_save.clu_table[index++] =
+                                       rcar_du_cmm_read(du_cmm, CMM_CLU_DATA);
+                       }
+               }
+       }
+
+       if (!du_cmm->active)
+               du_cmm_clk(du_cmm, false);
+
+       return 0;
+}
+
+int rcar_du_cmm_pm_resume(struct rcar_du_crtc *rcrtc)
+{
+       /* none */
+       return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+int rcar_du_cmm_driver_open(struct drm_device *dev, struct drm_file *file_priv)
+{
+       struct rcar_du_device *rcdu = dev->dev_private;
+       struct rcar_du_cmm_file_priv *fpriv;
+       int i;
+
+       if (!rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM))
+               return 0;
+
+       file_priv->driver_priv = NULL;
+
+       fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
+       if (unlikely(!fpriv))
+               return -ENOMEM;
+
+       fpriv->done_list = kcalloc(rcdu->info->num_crtcs,
+                                  sizeof(*fpriv->done_list),
+                                  GFP_KERNEL);
+       if (unlikely(!fpriv->done_list)) {
+               kfree(fpriv);
+               return -ENOMEM;
+       }
+
+       init_waitqueue_head(&fpriv->event_wait);
+       INIT_LIST_HEAD(&fpriv->list);
+       INIT_LIST_HEAD(&fpriv->active_list);
+       for (i = 0; i < rcdu->info->num_crtcs; i++)
+               INIT_LIST_HEAD(&fpriv->done_list[i]);
+
+       file_priv->driver_priv = fpriv;
+
+       return 0;
+}
+
+void rcar_du_cmm_postclose(struct drm_device *dev, struct drm_file *file_priv)
+{
+       struct rcar_du_device *rcdu = dev->dev_private;
+       struct rcar_du_cmm_file_priv *fpriv = file_priv->driver_priv;
+       struct rcar_du_cmm_pending_event *p, *pt;
+       struct rcar_du_crtc *rcrtc;
+       struct rcar_du_cmm *du_cmm;
+       int i, crtcs_cnt, ret;
+       u32 table_data;
+
+       if (!rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM))
+               return;
+
+       mutex_lock(&cmm_event_lock);
+
+       /* Unlink file priv events */
+       list_for_each_entry_safe(p, pt, &fpriv->list, fpriv_link) {
+               list_del(&p->fpriv_link);
+               list_del(&p->link);
+               switch (p->stat) {
+               case QUE_STAT_PENDING:
+                       cmm_vblank_put(p);
+                       cmm_gem_object_unreference(p);
+                       kfree(p);
+                       break;
+               case QUE_STAT_DONE:
+                       kfree(p);
+                       break;
+               case QUE_STAT_ACTIVE:
+                       p->fpriv = NULL;
+                       break;
+               }
+       }
+
+       mutex_unlock(&cmm_event_lock);
+
+       kfree(fpriv->done_list);
+       kfree(fpriv);
+       file_priv->driver_priv = NULL;
+
+       for (crtcs_cnt = 0; crtcs_cnt < rcdu->num_crtcs; crtcs_cnt++) {
+               rcrtc = &rcdu->crtcs[crtcs_cnt];
+               du_cmm = rcrtc->cmm_handle;
+               if (du_cmm->authority && du_cmm->pid == task_pid_nr(current)) {
+                       du_cmm->authority = false;
+                       du_cmm->pid = 0;
+                       ret = wait_event_timeout(du_cmm->reg_save.wait,
+                                                du_cmm_que_empty(du_cmm),
+                                                msecs_to_jiffies(500));
+                       if (ret == 0)
+                               dev_err(rcdu->dev, "rcar-du cmm close : 
timeout\n");
+
+                       for (i = 0; i < CMM_LUT_NUM; i++)
+                               du_cmm->reg_save.lut_table[i] = (i << 16) |
+                                                               (i << 8) |
+                                                               (i << 0);
+
+                       for (i = 0; i < CMM_CLU_NUM; i++) {
+                               du_cmm->reg_save.clu_table[i] =
+                                                       index_to_clu_data(i);
+                       }
+
+                       for (i = 0; i < CMM_LUT_NUM; i++) {
+#ifdef CONFIG_PM_SLEEP
+                               table_data = du_cmm->reg_save.lut_table[i];
+#else
+                               table_data = ((i << 16) | (i << 8) | (i << 0));
+#endif /* CONFIG_PM_SLEEP */
+                               rcar_du_cmm_write(du_cmm, CMM_LUT_TBLA(i),
+                                                 table_data);
+                               if (du_cmm->dbuf) {
+                                       rcar_du_cmm_write(du_cmm,
+                                                         CMM_LUT_TBLB(i),
+                                                         table_data);
+                               }
+                       }
+
+                       for (i = 0; i < CMM_CLU_NUM; i++) {
+#ifdef CONFIG_PM_SLEEP
+                               table_data = du_cmm->reg_save.clu_table[i];
+#else
+                               table_data = index_to_clu_data(i);
+#endif /* CONFIG_PM_SLEEP */
+                               rcar_du_cmm_write(du_cmm, CMM_CLU_DATA,
+                                                 table_data);
+
+                               if (du_cmm->dbuf) {
+                                       rcar_du_cmm_write(du_cmm, CMM_CLU_DATA2,
+                                                         table_data);
+                               }
+                       }
+               }
+       }
+}
+
+int rcar_du_cmm_init(struct rcar_du_crtc *rcrtc)
+{
+       struct rcar_du_cmm *du_cmm;
+       int ret;
+       int i;
+       struct rcar_du_device *rcdu = rcrtc->group->dev;
+       char name[64];
+       struct resource *mem;
+
+       if (!rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM))
+               return 0;
+
+       du_cmm = devm_kzalloc(rcdu->dev, sizeof(*du_cmm), GFP_KERNEL);
+       if (!du_cmm) {
+               ret = -ENOMEM;
+               goto error_alloc;
+       }
+
+       /* DU-CMM mapping */
+       sprintf(name, "cmm.%u", rcrtc->index);
+       mem = platform_get_resource_byname(to_platform_device(rcdu->dev),
+                                          IORESOURCE_MEM, name);
+       if (!mem) {
+               dev_err(rcdu->dev, "rcar-du cmm init : failed to get memory 
resource\n");
+               ret = -EINVAL;
+               goto error_mapping_cmm;
+       }
+       du_cmm->cmm_base = devm_ioremap_nocache(rcdu->dev, mem->start,
+                                               resource_size(mem));
+       if (!du_cmm->cmm_base) {
+               dev_err(rcdu->dev, "rcar-du cmm init : failed to map iomem\n");
+               ret = -EINVAL;
+               goto error_mapping_cmm;
+       }
+       du_cmm->clock = devm_clk_get(rcdu->dev, name);
+       if (IS_ERR(du_cmm->clock)) {
+               dev_err(rcdu->dev, "failed to get clock\n");
+               ret = PTR_ERR(du_cmm->clock);
+               goto error_clock_cmm;
+       }
+
+       du_cmm->rcrtc = rcrtc;
+
+       du_cmm->reg_save.cm2_ctl0 = 0;
+       du_cmm->reg_save.hgo_offset = 0;
+       du_cmm->reg_save.hgo_size = 0;
+       du_cmm->reg_save.hgo_mode = 0;
+
+       du_cmm->dbuf = rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM_LUT_DBUF);
+       if (du_cmm->dbuf) {
+               du_cmm->lut.buf_mode = LUT_DOUBLE_BUFFER_AUTO;
+               du_cmm->reg_save.cm2_ctl0 |= CMM_CTL0_DBUF;
+       } else {
+               dev_err(rcdu->dev, "single buffer is not supported.\n");
+               du_cmm->dbuf = true;
+               du_cmm->lut.buf_mode = LUT_DOUBLE_BUFFER_AUTO;
+               du_cmm->reg_save.cm2_ctl0 |= CMM_CTL0_DBUF;
+       }
+
+       du_cmm->clu_dbuf = rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM_CLU_DBUF);
+       if (du_cmm->clu_dbuf) {
+               du_cmm->clu.buf_mode = CLU_DOUBLE_BUFFER_AUTO;
+               du_cmm->reg_save.cm2_ctl0 |= CMM_CTL0_CLUDB;
+       } else {
+               dev_err(rcdu->dev, "single buffer is not supported.\n");
+               du_cmm->clu_dbuf = true;
+               du_cmm->clu.buf_mode = CLU_DOUBLE_BUFFER_AUTO;
+               du_cmm->reg_save.cm2_ctl0 |= CMM_CTL0_CLUDB;
+       }
+
+#ifdef CONFIG_PM_SLEEP
+       du_cmm->reg_save.lut_table =
+               devm_kzalloc(rcdu->dev, CMM_LUT_NUM * 4, GFP_KERNEL);
+       if (!du_cmm->reg_save.lut_table) {
+               ret = -ENOMEM;
+               goto error_lut_reg_save_buf;
+       }
+       for (i = 0; i < CMM_LUT_NUM; i++)
+               du_cmm->reg_save.lut_table[i] = (i << 16) | (i << 8) | (i << 0);
+
+       du_cmm->reg_save.clu_table =
+               devm_kzalloc(rcdu->dev, CMM_CLU_NUM * 4, GFP_KERNEL);
+       if (!du_cmm->reg_save.clu_table) {
+               ret = -ENOMEM;
+               goto error_clu_reg_save_buf;
+       }
+       for (i = 0; i < CMM_CLU_NUM; i++)
+               du_cmm->reg_save.clu_table[i] = index_to_clu_data(i);
+
+       init_waitqueue_head(&du_cmm->reg_save.wait);
+#endif /* CONFIG_PM_SLEEP */
+       if (soc_device_match(rcar_du_cmm_r8a7795_es1))
+               du_cmm->soc_support = false;
+       else
+               du_cmm->soc_support = true;
+
+       du_cmm->active = false;
+       du_cmm->init = false;
+       du_cmm->direct = true;
+
+       mutex_init(&du_cmm->lock);
+       INIT_LIST_HEAD(&du_cmm->lut.list);
+       du_cmm->lut.p = NULL;
+       du_cmm->lut.one_side = false;
+       INIT_LIST_HEAD(&du_cmm->clu.list);
+       du_cmm->clu.p = NULL;
+       du_cmm->clu.one_side = false;
+       INIT_LIST_HEAD(&du_cmm->hgo.list);
+       du_cmm->hgo.reset = 0;
+
+       sprintf(name, "du-cmm%d", rcrtc->index);
+       du_cmm->workqueue = create_singlethread_workqueue(name);
+       INIT_WORK(&du_cmm->work, du_cmm_work);
+
+       rcrtc->cmm_handle = du_cmm;
+
+       dev_info(rcdu->dev, "DU%d use CMM(%s buffer)\n",
+                rcrtc->index, du_cmm->dbuf ? "Double" : "Single");
+
+       return 0;
+
+#ifdef CONFIG_PM_SLEEP
+error_clu_reg_save_buf:
+error_lut_reg_save_buf:
+#endif /* CONFIG_PM_SLEEP */
+error_clock_cmm:
+       devm_iounmap(rcdu->dev, du_cmm->cmm_base);
+error_mapping_cmm:
+       devm_kfree(rcdu->dev, du_cmm);
+error_alloc:
+       return ret;
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c 
b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 15dc9ca..864fb94 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -296,6 +296,19 @@ static void rcar_du_crtc_set_display_timing(struct 
rcar_du_crtc *rcrtc)
        rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start - 19);
        rcar_du_crtc_write(rcrtc, HDER, mode->htotal - mode->hsync_start +
                                        mode->hdisplay - 19);
+       if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_CMM)) {
+               rcar_du_crtc_write(rcrtc, HDSR, mode->htotal -
+                                               mode->hsync_start - 19 - 25);
+               rcar_du_crtc_write(rcrtc, HDER, mode->htotal -
+                                               mode->hsync_start +
+                                               mode->hdisplay - 19 - 25);
+       } else {
+               rcar_du_crtc_write(rcrtc, HDSR, mode->htotal -
+                                               mode->hsync_start - 19);
+               rcar_du_crtc_write(rcrtc, HDER, mode->htotal -
+                                               mode->hsync_start +
+                                               mode->hdisplay - 19);
+       }
        rcar_du_crtc_write(rcrtc, HSWR, mode->hsync_end -
                                        mode->hsync_start - 1);
        rcar_du_crtc_write(rcrtc, HCR,  mode->htotal - 1);
@@ -530,6 +543,9 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
                             DSYSR_TVM_MASTER);
 
        rcar_du_group_start_stop(rcrtc->group, true);
+
+       if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_CMM))
+               rcar_du_cmm_start_stop(rcrtc, true);
 }
 
 static void rcar_du_crtc_disable_planes(struct rcar_du_crtc *rcrtc)
@@ -565,6 +581,9 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
 {
        struct drm_crtc *crtc = &rcrtc->crtc;
 
+       if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_CMM))
+               rcar_du_cmm_start_stop(rcrtc, false);
+
        /*
         * Disable all planes and wait for the change to take effect. This is
         * required as the plane enable registers are updated on vblank, and no
@@ -899,6 +918,9 @@ static irqreturn_t rcar_du_crtc_irq(int irq, void *arg)
                        rcar_du_crtc_finish_page_flip(rcrtc);
                }
 
+               if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_CMM))
+                       rcar_du_cmm_kick(rcrtc);
+
                ret = IRQ_HANDLED;
        }
 
@@ -999,5 +1021,7 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, 
unsigned int swindex,
                return ret;
        }
 
+       rcar_du_cmm_init(rcrtc);
+
        return 0;
 }
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h 
b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
index 7680cb2..74e0a22 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -67,6 +67,10 @@ struct rcar_du_crtc {
        struct rcar_du_group *group;
        struct rcar_du_vsp *vsp;
        unsigned int vsp_pipe;
+       int lvds_ch;
+
+       void *cmm_handle;
+
 };
 
 #define to_rcar_crtc(c)        container_of(c, struct rcar_du_crtc, crtc)
@@ -104,4 +108,16 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc,
                               enum rcar_du_output output);
 void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc);
 
+/* DU-CMM functions */
+int rcar_du_cmm_init(struct rcar_du_crtc *rcrtc);
+int rcar_du_cmm_driver_open(struct drm_device *dev, struct drm_file 
*file_priv);
+void rcar_du_cmm_postclose(struct drm_device *dev, struct drm_file *file_priv);
+int rcar_du_cmm_start_stop(struct rcar_du_crtc *rcrtc, bool on);
+void rcar_du_cmm_kick(struct rcar_du_crtc *rcrtc);
+
+#ifdef CONFIG_PM_SLEEP
+int rcar_du_cmm_pm_suspend(struct rcar_du_crtc *rcrtc);
+int rcar_du_cmm_pm_resume(struct rcar_du_crtc *rcrtc);
+#endif /* CONFIG_PM_SLEEP */
+
 #endif /* __RCAR_DU_CRTC_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c 
b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index 02aee6c..838b7c9 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -26,8 +26,8 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
-
 #include "rcar_du_drv.h"
+#include "rcar_du_encoder.h"
 #include "rcar_du_kms.h"
 #include "rcar_du_of.h"
 #include "rcar_du_regs.h"
@@ -128,7 +128,9 @@ static const struct rcar_du_device_info 
rcar_du_r8a7790_info = {
 static const struct rcar_du_device_info rcar_du_r8a7791_info = {
        .gen = 2,
        .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
-                 | RCAR_DU_FEATURE_EXT_CTRL_REGS,
+                 | RCAR_DU_FEATURE_EXT_CTRL_REGS
+                 | RCAR_DU_FEATURE_CMM,
+       .num_crtcs = 2,
        .channels_mask = BIT(1) | BIT(0),
        .routes = {
                /*
@@ -190,7 +192,10 @@ static const struct rcar_du_device_info 
rcar_du_r8a7795_info = {
        .gen = 3,
        .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
                  | RCAR_DU_FEATURE_EXT_CTRL_REGS
-                 | RCAR_DU_FEATURE_VSP1_SOURCE,
+                 | RCAR_DU_FEATURE_VSP1_SOURCE
+                 | RCAR_DU_FEATURE_CMM | RCAR_DU_FEATURE_CMM_LUT_DBUF
+                 | RCAR_DU_FEATURE_CMM_CLU_DBUF,
+       .num_crtcs = 4,
        .channels_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0),
        .routes = {
                /*
@@ -222,7 +227,10 @@ static const struct rcar_du_device_info 
rcar_du_r8a7796_info = {
        .gen = 3,
        .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
                  | RCAR_DU_FEATURE_EXT_CTRL_REGS
-                 | RCAR_DU_FEATURE_VSP1_SOURCE,
+                 | RCAR_DU_FEATURE_VSP1_SOURCE
+                 | RCAR_DU_FEATURE_CMM | RCAR_DU_FEATURE_CMM_LUT_DBUF
+                 | RCAR_DU_FEATURE_CMM_CLU_DBUF,
+       .num_crtcs = 3,
        .channels_mask = BIT(2) | BIT(1) | BIT(0),
        .routes = {
                /*
@@ -250,7 +258,11 @@ static const struct rcar_du_device_info 
rcar_du_r8a77965_info = {
        .gen = 3,
        .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
                  | RCAR_DU_FEATURE_EXT_CTRL_REGS
-                 | RCAR_DU_FEATURE_VSP1_SOURCE,
+                 | RCAR_DU_FEATURE_VSP1_SOURCE
+                 | RCAR_DU_FEATURE_R8A77965_REGS
+                 | RCAR_DU_FEATURE_CMM | RCAR_DU_FEATURE_CMM_LUT_DBUF
+                 | RCAR_DU_FEATURE_CMM_CLU_DBUF,
+       .num_crtcs = 3,
        .channels_mask = BIT(3) | BIT(1) | BIT(0),
        .routes = {
                /*
@@ -328,6 +340,8 @@ DEFINE_DRM_GEM_CMA_FOPS(rcar_du_fops);
 static struct drm_driver rcar_du_driver = {
        .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME
                                | DRIVER_ATOMIC,
+       .open                   = rcar_du_cmm_driver_open,
+       .postclose              = rcar_du_cmm_postclose,
        .lastclose              = rcar_du_lastclose,
        .gem_free_object_unlocked = drm_gem_cma_free_object,
        .gem_vm_ops             = &drm_gem_cma_vm_ops,
@@ -358,6 +372,12 @@ static int rcar_du_pm_suspend(struct device *dev)
 {
        struct rcar_du_device *rcdu = dev_get_drvdata(dev);
        struct drm_atomic_state *state;
+       int i;
+
+       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM)) {
+               for (i = 0; i < rcdu->num_crtcs; ++i)
+                       rcar_du_cmm_pm_suspend(&rcdu->crtcs[i]);
+       }
 
        drm_kms_helper_poll_disable(rcdu->ddev);
        drm_fbdev_cma_set_suspend_unlocked(rcdu->fbdev, true);
@@ -377,7 +397,20 @@ static int rcar_du_pm_suspend(struct device *dev)
 static int rcar_du_pm_resume(struct device *dev)
 {
        struct rcar_du_device *rcdu = dev_get_drvdata(dev);
+#if IS_ENABLED(CONFIG_DRM_RCAR_DW_HDMI)
+       struct drm_encoder *encoder;
+       int i;
+
+       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CMM)) {
+               for (i = 0; (i < rcdu->num_crtcs); ++i)
+                       rcar_du_cmm_pm_resume(&rcdu->crtcs[i]);
+       }
 
+       list_for_each_entry(encoder, &rcdu->ddev->mode_config.encoder_list,
+                           head) {
+               to_rcar_encoder(encoder);
+       }
+#endif
        drm_atomic_helper_resume(rcdu->ddev, rcdu->suspend_state);
        drm_fbdev_cma_set_suspend_unlocked(rcdu->fbdev, false);
        drm_kms_helper_poll_enable(rcdu->ddev);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h 
b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
index b3a25e8..f2afe36 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
@@ -30,8 +30,19 @@ struct rcar_du_device;
 #define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0)        /* Per-CRTC IRQ and 
clock */
 #define RCAR_DU_FEATURE_EXT_CTRL_REGS  (1 << 1)        /* Has extended control 
registers */
 #define RCAR_DU_FEATURE_VSP1_SOURCE    (1 << 2)        /* Has inputs from VSP1 
*/
-
-#define RCAR_DU_QUIRK_ALIGN_128B       (1 << 0)        /* Align pitches to 128 
bytes */
+/* Use R8A77965 registers */
+#define RCAR_DU_FEATURE_R8A77965_REGS  BIT(3)
+
+/* Has DEF7R register & CMM */
+#define RCAR_DU_FEATURE_CMM            BIT(10)
+/* Has CMM LUT Double buffer */
+#define RCAR_DU_FEATURE_CMM_LUT_DBUF   BIT(11)
+/* Has CMM CLU Double buffer */
+#define RCAR_DU_FEATURE_CMM_CLU_DBUF   BIT(12)
+/* Align pitches to 128 bytes */
+#define RCAR_DU_QUIRK_ALIGN_128B       BIT(0)
+/* LVDS lanes 1 and 3 inverted */
+#define RCAR_DU_QUIRK_LVDS_LANES       BIT(1)
 
 /*
  * struct rcar_du_output_routing - Output routing specification
@@ -61,6 +72,7 @@ struct rcar_du_device_info {
        unsigned int features;
        unsigned int quirks;
        unsigned int channels_mask;
+       unsigned int num_crtcs;
        struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
        unsigned int num_lvds;
        unsigned int dpll_ch;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c 
b/drivers/gpu/drm/rcar-du/rcar_du_group.c
index d539cb2..83a2836 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_group.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c
@@ -130,6 +130,11 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp)
        if (rcdu->info->gen >= 3)
                rcar_du_group_write(rgrp, DEFR10, DEFR10_CODE | DEFR10_DEFE10);
 
+       if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_CMM)) {
+               rcar_du_group_write(rgrp, DEF7R, DEF7R_CODE |
+                                   DEF7R_CMME1 | DEF7R_CMME0);
+       }
+
        /*
         * Use DS1PR and DS2PR to configure planes priorities and connects the
         * superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h 
b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
index 9dfd220..b20e783 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
@@ -200,6 +200,11 @@
 #define DEFR6_MLOS1            (1 << 2)
 #define DEFR6_DEFAULT          (DEFR6_CODE | DEFR6_TCNE1)
 
+#define DEF7R                  0x000ec
+#define DEF7R_CODE             (0x7779 << 16)
+#define DEF7R_CMME1            BIT(6)
+#define DEF7R_CMME0            BIT(4)
+
 /* 
-----------------------------------------------------------------------------
  * R8A7790-only Control Registers
  */
@@ -552,4 +557,91 @@
 #define GCBCR                  0x11098
 #define BCBCR                  0x1109c
 
+/* 
-----------------------------------------------------------------------------
+ * DU Color Management Module Registers
+ */
+
+#define CMM_LUT_CTRL                   0x0000
+#define CMM_LUT_CTRL_EN                        BIT(0)
+
+#define CMM_CLU_CTRL                   0x0100
+#define CMM_CLU_CTRL_EN                        BIT(0)
+#define CMM_CLU_CTRL_MVS               BIT(24)
+#define CMM_CLU_CTRL_AAI               BIT(28)
+
+#define CMM_CTL0                       0x0180
+#define CM2_CTL0                       CMM_CTL0
+#define CMM_CTL0_CLUDB                 BIT(24)
+#define CMM_CTL0_HISTS                 BIT(20)
+#define CMM_CTL0_TM1_MASK              (3 << 16)
+#define CMM_CTL0_TM1_BT601_YC240       (0 << 16)
+#define CMM_CTL0_TM1_BT601_YC255       BIT(16)
+#define CMM_CTL0_TM1_BT709_RG255       (2 << 16)
+#define CMM_CTL0_TM1_BT709_RG235       (3 << 16)
+#define CMM_CTL0_TM0_MASK              (3 << 12)
+#define CMM_CTL0_TM0_BT601_YC240       (0 << 12)
+#define CMM_CTL0_TM0_BT601_YC255       BIT(12)
+#define CMM_CTL0_TM0_BT709_RG255       (2 << 12)
+#define CMM_CTL0_TM0_BT709_RG235       (3 << 12)
+#define CMM_CTL0_TM_BT601_YC240                (CMM_CTL0_TM1_BT601_YC240 |\
+                                        CMM_CTL0_TM0_BT601_YC240)
+#define CMM_CTL0_TM_BT601_YC255                (CMM_CTL0_TM1_BT601_YC255 |\
+                                        CMM_CTL0_TM0_BT601_YC255)
+#define CMM_CTL0_TM_BT709_RG255                (CMM_CTL0_TM1_BT709_RG255 |\
+                                        CMM_CTL0_TM0_BT709_RG255)
+#define CMM_CTL0_TM_BT709_RG235                (CMM_CTL0_TM1_BT709_RG235 |\
+                                        CMM_CTL0_TM0_BT709_RG235)
+#define CMM_CTL0_YC                    BIT(8)
+#define CMM_CTL0_VPOL                  BIT(4)
+#define CMM_CTL0_DBUF                  BIT(0)
+
+#define CMM_CTL1                       0x0184
+#define CM2_CTL1                       CMM_CTL1
+#define CMM_CTL1_BFS                   BIT(0)
+
+#define CMM_CTL2                       0x0188
+#define CMM_HGO_OFFSET                 0x0200
+#define CMM_HGO_SIZE                   0x0204
+#define CMM_HGO_MODE                   0x0208
+#define CMM_HGO_MODE_MASK              (0xFF)
+#define CMM_HGO_MODE_MAXRGB            BIT(7)
+#define CMM_HGO_MODE_OFSB_R            BIT(6)
+#define CMM_HGO_MODE_OFSB_G            BIT(5)
+#define CMM_HGO_MODE_OFSB_B            BIT(4)
+#define CMM_HGO_MODE_HRATIO_NO_SKIPP           (0 << 2)
+#define CMM_HGO_MODE_HRATIO_HALF_SKIPP         BIT(2)
+#define CMM_HGO_MODE_HRATIO_QUARTER_SKIPP      (2 << 2)
+#define CMM_HGO_MODE_VRATIO_NO_SKIPP           (0 << 0)
+#define CMM_HGO_MODE_VRATIO_HALF_SKIPP         BIT(0)
+#define CMM_HGO_MODE_VRATIO_QUARTER_SKIPP      (2 << 0)
+#define CMM_HGO_LB_TH                  0x020C
+#define CMM_HGO_LB0_H                  0x0210
+#define CMM_HGO_LB0_V                  0x0214
+#define CMM_HGO_LB1_H                  0x0218
+#define CMM_HGO_LB1_V                  0x021C
+#define CMM_HGO_LB2_H                  0x0220
+#define CMM_HGO_LB2_V                  0x0224
+#define CMM_HGO_LB3_H                  0x0228
+#define CMM_HGO_LB3_V                  0x022C
+#define CMM_HGO_R_HISTO(n)             (0x0230 + ((n) * 4))
+#define CMM_HGO_R_MAXMIN               0x0330
+#define CMM_HGO_R_SUM                  0x0334
+#define CMM_HGO_R_LB_DET               0x0338
+#define CMM_HGO_G_HISTO(n)             (0x0340 + ((n) * 4))
+#define CMM_HGO_G_MAXMIN               0x0440
+#define CMM_HGO_G_SUM                  0x0444
+#define CMM_HGO_G_LB_DET               0x0448
+#define CMM_HGO_B_HISTO(n)             (0x0450 + ((n) * 4))
+#define CMM_HGO_B_MAXMIN               0x0550
+#define CMM_HGO_B_SUM                  0x0554
+#define CMM_HGO_B_LB_DET               0x0558
+#define CMM_HGO_REGRST                 0x05FC
+#define CMM_HGO_REGRST_RCLEA           BIT(0)
+#define CMM_LUT_TBLA(n)                        (0x0600 + ((n) * 4))
+#define CMM_CLU_ADDR                   0x0A00
+#define CMM_CLU_DATA                   0x0A04
+#define CMM_LUT_TBLB(n)                        (0x0B00 + ((n) * 4))
+#define CMM_CLU_ADDR2                  0x0F00
+#define CMM_CLU_DATA2                  0x0F04
+
 #endif /* __RCAR_DU_REGS_H__ */
diff --git a/include/drm/drm_ioctl.h b/include/drm/drm_ioctl.h
index fafb6f5..add4280 100644
--- a/include/drm/drm_ioctl.h
+++ b/include/drm/drm_ioctl.h
@@ -109,6 +109,13 @@ enum drm_ioctl_flags {
         */
        DRM_ROOT_ONLY           = BIT(2),
        /**
+        * @DRM_CONTROL_ALLOW:
+        *
+        * Deprecated, do not use. Control nodes are in the process of getting
+        * removed.
+        */
+       DRM_CONTROL_ALLOW       = BIT(3),
+       /**
         * @DRM_UNLOCKED:
         *
         * Whether &drm_ioctl_desc.func should be called with the DRM BKL held
-- 
2.7.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to