drm/exynos: ipp: Port IPPv2 from mainline 47/171647/2
authorMarek Szyprowski <m.szyprowski@samsung.com>
Thu, 25 Jan 2018 09:12:35 +0000 (10:12 +0100)
committerInki Dae <inki.dae@samsung.com>
Thu, 15 Mar 2018 06:38:31 +0000 (06:38 +0000)
Port Exynos DRM IPPv2 support from mainline V6 patchset:
"Exynos DRM: rewrite IPP subsystem and userspace API"
https://lists.freedesktop.org/archives/dri-devel/2017-November/157130.html

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Change-Id: I7c0e256aaa9d726ab1beb73579846f6d4763e346

14 files changed:
drivers/gpu/drm/exynos/Kconfig
drivers/gpu/drm/exynos/Makefile
drivers/gpu/drm/exynos/exynos_drm_drv.h
drivers/gpu/drm/exynos/exynos_drm_fimc.c
drivers/gpu/drm/exynos/exynos_drm_fimc.h [deleted file]
drivers/gpu/drm/exynos/exynos_drm_gsc.c
drivers/gpu/drm/exynos/exynos_drm_gsc.h [deleted file]
drivers/gpu/drm/exynos/exynos_drm_ipp.c
drivers/gpu/drm/exynos/exynos_drm_ipp.h
drivers/gpu/drm/exynos/exynos_drm_rotator.c
drivers/gpu/drm/exynos/exynos_drm_rotator.h [deleted file]
drivers/gpu/drm/exynos/exynos_drm_scaler.c [new file with mode: 0644]
drivers/gpu/drm/exynos/regs-scaler.h [new file with mode: 0644]
include/uapi/drm/exynos_drm.h

index 89d9371d192353e1351932154b83f7dc1f8cb506..1699139d0fa3a0c4bd9ae25c12f2f6ae6bf2e64d 100644 (file)
@@ -82,26 +82,30 @@ config DRM_EXYNOS_G2D
          Choose this option if you want to use Exynos G2D for DRM.
 
 config DRM_EXYNOS_IPP
-       bool "Exynos DRM IPP"
-       depends on DRM_EXYNOS
-       help
-         Choose this option if you want to use IPP feature for DRM.
+       bool
 
 config DRM_EXYNOS_FIMC
-       bool "Exynos DRM FIMC"
-       depends on DRM_EXYNOS_IPP && MFD_SYSCON
+       bool "FIMC"
+       select DRM_EXYNOS_IPP
        help
          Choose this option if you want to use Exynos FIMC for DRM.
 
 config DRM_EXYNOS_ROTATOR
-       bool "Exynos DRM Rotator"
-       depends on DRM_EXYNOS_IPP
+       bool "Rotator"
+       select DRM_EXYNOS_IPP
        help
          Choose this option if you want to use Exynos Rotator for DRM.
 
+config DRM_EXYNOS_SCALER
+       bool "Scaler"
+       select DRM_EXYNOS_IPP
+       help
+         Choose this option if you want to use Exynos Scaler for DRM.
+
 config DRM_EXYNOS_GSC
-       bool "Exynos DRM GSC"
-       depends on DRM_EXYNOS_IPP && ARCH_EXYNOS
+       bool "GScaler"
+       depends on VIDEO_SAMSUNG_EXYNOS_GSC=n
+       select DRM_EXYNOS_IPP
        help
          Choose this option if you want to use Exynos GSC for DRM.
 
index 2c38400f27578aa7f5f46db97064d6b9d8f7af85..9c9a08bb37766af37851acf92b453d45bd8c9b80 100644 (file)
@@ -21,6 +21,7 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_G2D)    += exynos_drm_g2d.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_IPP)     += exynos_drm_ipp.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC)    += exynos_drm_fimc.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_SCALER)  += exynos_drm_scaler.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_GSC)     += exynos_drm_gsc.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_MIC)     += exynos_drm_mic.o
 exynosdrm-$(CONFIG_DEBUG_FS)           += exynos_drm_debugfs.o
index da1c3a64619c740d31705afac13d45632d967bcd..5a15602fddad38798ebc65bcff976108ff2747a2 100644 (file)
@@ -359,6 +359,15 @@ int exynos_drm_component_add(struct device *dev,
 void exynos_drm_component_del(struct device *dev,
                                enum exynos_drm_device_type dev_type);
 
+#ifdef CONFIG_DRM_EXYNOS_FIMC
+int exynos_drm_check_fimc_device(struct device *dev);
+#else
+static inline int exynos_drm_check_fimc_device(struct device *dev)
+{
+       return 0;
+}
+#endif
+
 extern struct platform_driver fimd_driver;
 extern struct platform_driver exynos5433_decon_driver;
 extern struct platform_driver decon_driver;
@@ -371,7 +380,7 @@ extern struct platform_driver vidi_driver;
 extern struct platform_driver g2d_driver;
 extern struct platform_driver fimc_driver;
 extern struct platform_driver rotator_driver;
+extern struct platform_driver scaler_driver;
 extern struct platform_driver gsc_driver;
-extern struct platform_driver ipp_driver;
 extern struct platform_driver mic_driver;
 #endif
index f63ea1a2626b294aa212f1f3b6256b784d3f4876..f3053d0985e85827752ce5481db61fb2b0989c52 100644 (file)
@@ -12,6 +12,7 @@
  *
  */
 #include <linux/kernel.h>
+#include <linux/component.h>
 #include <linux/platform_device.h>
 #include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
@@ -24,8 +25,8 @@
 #include <drm/exynos_drm.h>
 #include "regs-fimc.h"
 #include "exynos_drm_drv.h"
+#include "exynos_drm_iommu.h"
 #include "exynos_drm_ipp.h"
-#include "exynos_drm_fimc.h"
 
 /*
  * FIMC stands for Fully Interactive Mobile Camera and
  * input DMA reads image data from the memory.
  * output DMA writes image data to memory.
  * FIMC supports image rotation and image effect functions.
- *
- * M2M operation : supports crop/scale/rotation/csc so on.
- * Memory ----> FIMC H/W ----> Memory.
- * Writeback operation : supports cloned screen with FIMD.
- * FIMD ----> FIMC H/W ----> Memory.
- * Output operation : supports direct display using local path.
- * Memory ----> FIMC H/W ----> FIMD.
- */
-
-/*
- * TODO
- * 1. check suspend/resume api if needed.
- * 2. need to check use case platform_device_id.
- * 3. check src/dst size with, height.
- * 4. added check_prepare api for right register.
- * 5. need to add supported list in prop_list.
- * 6. check prescaler/scaler optimization.
  */
 
 #define FIMC_MAX_DEVS  4
 #define FIMC_BUF_STOP  1
 #define FIMC_BUF_START 2
 #define FIMC_WIDTH_ITU_709     1280
-#define FIMC_REFRESH_MAX       60
-#define FIMC_REFRESH_MIN       12
-#define FIMC_CROP_MAX  8192
-#define FIMC_CROP_MIN  32
-#define FIMC_SCALE_MAX 4224
-#define FIMC_SCALE_MIN 32
+#define FIMC_AUTOSUSPEND_DELAY 2000
+
+static unsigned int fimc_mask = 0xc;
+module_param_named(fimc_devs, fimc_mask, uint, 0644);
+MODULE_PARM_DESC(fimc_devs, "Alias mask for assigning FIMC devices to Exynos DRM");
 
 #define get_fimc_context(dev)  platform_get_drvdata(to_platform_device(dev))
-#define get_ctx_from_ippdrv(ippdrv)    container_of(ippdrv,\
-                                       struct fimc_context, ippdrv);
-enum fimc_wb {
-       FIMC_WB_NONE,
-       FIMC_WB_A,
-       FIMC_WB_B,
-};
 
 enum {
        FIMC_CLK_LCLK,
        FIMC_CLK_GATE,
        FIMC_CLK_WB_A,
        FIMC_CLK_WB_B,
-       FIMC_CLK_MUX,
-       FIMC_CLK_PARENT,
        FIMC_CLKS_MAX
 };
 
@@ -90,12 +64,8 @@ static const char * const fimc_clock_names[] = {
        [FIMC_CLK_GATE]   = "fimc",
        [FIMC_CLK_WB_A]   = "pxl_async0",
        [FIMC_CLK_WB_B]   = "pxl_async1",
-       [FIMC_CLK_MUX]    = "mux",
-       [FIMC_CLK_PARENT] = "parent",
 };
 
-#define FIMC_DEFAULT_LCLK_FREQUENCY 133000000UL
-
 /*
  * A structure of scaler.
  *
@@ -107,7 +77,7 @@ static const char * const fimc_clock_names[] = {
  * @vratio: vertical ratio.
  */
 struct fimc_scaler {
-       bool    range;
+       bool range;
        bool bypass;
        bool up_h;
        bool up_v;
@@ -115,58 +85,33 @@ struct fimc_scaler {
        u32 vratio;
 };
 
-/*
- * A structure of scaler capability.
- *
- * find user manual table 43-1.
- * @in_hori: scaler input horizontal size.
- * @bypass: scaler bypass mode.
- * @dst_h_wo_rot: target horizontal size without output rotation.
- * @dst_h_rot: target horizontal size with output rotation.
- * @rl_w_wo_rot: real width without input rotation.
- * @rl_h_rot: real height without output rotation.
- */
-struct fimc_capability {
-       /* scaler */
-       u32     in_hori;
-       u32     bypass;
-       /* output rotator */
-       u32     dst_h_wo_rot;
-       u32     dst_h_rot;
-       /* input rotator */
-       u32     rl_w_wo_rot;
-       u32     rl_h_rot;
-};
-
 /*
  * A structure of fimc context.
  *
- * @ippdrv: prepare initialization using ippdrv.
  * @regs_res: register resources.
  * @regs: memory mapped io registers.
  * @lock: locking of operations.
  * @clocks: fimc clocks.
- * @clk_frequency: LCLK clock frequency.
- * @sysreg: handle to SYSREG block regmap.
  * @sc: scaler infomations.
  * @pol: porarity of writeback.
  * @id: fimc id.
  * @irq: irq number.
- * @suspended: qos operations.
  */
 struct fimc_context {
-       struct exynos_drm_ippdrv        ippdrv;
+       struct exynos_drm_ipp ipp;
+       struct drm_device *drm_dev;
+       struct device   *dev;
+       struct exynos_drm_ipp_task      *task;
+       struct exynos_drm_ipp_formats   *formats;
+       unsigned int                    num_formats;
+
        struct resource *regs_res;
        void __iomem    *regs;
        spinlock_t      lock;
        struct clk      *clocks[FIMC_CLKS_MAX];
-       u32             clk_frequency;
-       struct regmap   *sysreg;
        struct fimc_scaler      sc;
-       struct exynos_drm_ipp_pol       pol;
        int     id;
        int     irq;
-       bool    suspended;
 };
 
 static u32 fimc_read(struct fimc_context *ctx, u32 reg)
@@ -218,19 +163,10 @@ static void fimc_sw_reset(struct fimc_context *ctx)
        fimc_write(ctx, 0x0, EXYNOS_CIFCNTSEQ);
 }
 
-static int fimc_set_camblk_fimd0_wb(struct fimc_context *ctx)
-{
-       return regmap_update_bits(ctx->sysreg, SYSREG_CAMERA_BLK,
-                                 SYSREG_FIMD0WB_DEST_MASK,
-                                 ctx->id << SYSREG_FIMD0WB_DEST_SHIFT);
-}
-
-static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb)
+static void fimc_set_type_ctrl(struct fimc_context *ctx)
 {
        u32 cfg;
 
-       DRM_DEBUG_KMS("wb[%d]\n", wb);
-
        cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
        cfg &= ~(EXYNOS_CIGCTRL_TESTPATTERN_MASK |
                EXYNOS_CIGCTRL_SELCAM_ITU_MASK |
@@ -239,49 +175,10 @@ static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb)
                EXYNOS_CIGCTRL_SELWB_CAMIF_MASK |
                EXYNOS_CIGCTRL_SELWRITEBACK_MASK);
 
-       switch (wb) {
-       case FIMC_WB_A:
-               cfg |= (EXYNOS_CIGCTRL_SELWRITEBACK_A |
-                       EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK);
-               break;
-       case FIMC_WB_B:
-               cfg |= (EXYNOS_CIGCTRL_SELWRITEBACK_B |
-                       EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK);
-               break;
-       case FIMC_WB_NONE:
-       default:
-               cfg |= (EXYNOS_CIGCTRL_SELCAM_ITU_A |
-                       EXYNOS_CIGCTRL_SELWRITEBACK_A |
-                       EXYNOS_CIGCTRL_SELCAM_MIPI_A |
-                       EXYNOS_CIGCTRL_SELCAM_FIMC_ITU);
-               break;
-       }
-
-       fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
-}
-
-static void fimc_set_polarity(struct fimc_context *ctx,
-               struct exynos_drm_ipp_pol *pol)
-{
-       u32 cfg;
-
-       DRM_DEBUG_KMS("inv_pclk[%d]inv_vsync[%d]\n",
-               pol->inv_pclk, pol->inv_vsync);
-       DRM_DEBUG_KMS("inv_href[%d]inv_hsync[%d]\n",
-               pol->inv_href, pol->inv_hsync);
-
-       cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
-       cfg &= ~(EXYNOS_CIGCTRL_INVPOLPCLK | EXYNOS_CIGCTRL_INVPOLVSYNC |
-                EXYNOS_CIGCTRL_INVPOLHREF | EXYNOS_CIGCTRL_INVPOLHSYNC);
-
-       if (pol->inv_pclk)
-               cfg |= EXYNOS_CIGCTRL_INVPOLPCLK;
-       if (pol->inv_vsync)
-               cfg |= EXYNOS_CIGCTRL_INVPOLVSYNC;
-       if (pol->inv_href)
-               cfg |= EXYNOS_CIGCTRL_INVPOLHREF;
-       if (pol->inv_hsync)
-               cfg |= EXYNOS_CIGCTRL_INVPOLHSYNC;
+       cfg |= (EXYNOS_CIGCTRL_SELCAM_ITU_A |
+               EXYNOS_CIGCTRL_SELWRITEBACK_A |
+               EXYNOS_CIGCTRL_SELCAM_MIPI_A |
+               EXYNOS_CIGCTRL_SELCAM_FIMC_ITU);
 
        fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
 }
@@ -323,7 +220,6 @@ static void fimc_clear_irq(struct fimc_context *ctx)
 
 static bool fimc_check_ovf(struct fimc_context *ctx)
 {
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 status, flag;
 
        status = fimc_read(ctx, EXYNOS_CISTATUS);
@@ -337,7 +233,7 @@ static bool fimc_check_ovf(struct fimc_context *ctx)
                        EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
                        EXYNOS_CIWDOFST_CLROVFICR);
 
-               dev_err(ippdrv->dev, "occurred overflow at %d, status 0x%x.\n",
+               dev_err(ctx->dev, "occurred overflow at %d, status 0x%x.\n",
                        ctx->id, status);
                return true;
        }
@@ -403,101 +299,8 @@ static void fimc_handle_lastend(struct fimc_context *ctx, bool enable)
        fimc_write(ctx, cfg, EXYNOS_CIOCTRL);
 }
 
-static int fimc_set_planar_addr(struct drm_exynos_ipp_buf_info *buf_info,
-                               u32 fmt, struct drm_exynos_sz *sz)
+static void fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
 {
-       dma_addr_t *base[EXYNOS_DRM_PLANAR_MAX];
-       uint64_t size[EXYNOS_DRM_PLANAR_MAX];
-       uint64_t ofs[EXYNOS_DRM_PLANAR_MAX];
-       bool bypass = false;
-       uint64_t tsize = 0;
-       int i;
-
-       for_each_ipp_planar(i) {
-               base[i] = &buf_info->base[i];
-               size[i] = buf_info->size[i];
-               ofs[i] = 0;
-               tsize += size[i];
-       }
-
-       if (!tsize) {
-               DRM_INFO("%s:failed to get buffer size.\n", __func__);
-               return 0;
-       }
-
-       switch (fmt) {
-       case DRM_FORMAT_NV12:
-       case DRM_FORMAT_NV21:
-       case DRM_FORMAT_NV16:
-       case DRM_FORMAT_NV61:
-               ofs[0] = sz->hsize * sz->vsize;
-               ofs[1] = ofs[0] >> 1;
-               if (*base[0] && *base[1]) {
-                       if (size[0] + size[1] < ofs[0] + ofs[1])
-                               goto err_info;
-                       bypass = true;
-               }
-               break;
-       case DRM_FORMAT_YUV410:
-       case DRM_FORMAT_YVU410:
-       case DRM_FORMAT_YUV411:
-       case DRM_FORMAT_YVU411:
-       case DRM_FORMAT_YUV420:
-       case DRM_FORMAT_YVU420:
-       case DRM_FORMAT_YUV422:
-       case DRM_FORMAT_YVU422:
-       case DRM_FORMAT_YUV444:
-       case DRM_FORMAT_YVU444:
-               ofs[0] = sz->hsize * sz->vsize;
-               ofs[1] = ofs[2] = ofs[0] >> 2;
-               if (*base[0] && *base[1] && *base[2]) {
-                       if (size[0]+size[1]+size[2] < ofs[0]+ofs[1]+ofs[2])
-                               goto err_info;
-                       bypass = true;
-               }
-               break;
-       case DRM_FORMAT_XRGB8888:
-       case DRM_FORMAT_ARGB8888:
-               ofs[0] = sz->hsize * sz->vsize << 2;
-               if (*base[0]) {
-                       if (size[0] < ofs[0])
-                               goto err_info;
-               }
-               bypass = true;
-               break;
-       default:
-               bypass = true;
-               break;
-       }
-
-       if (!bypass) {
-               *base[1] = *base[0] + ofs[0];
-               if (ofs[1] && ofs[2])
-                       *base[2] = *base[1] + ofs[1];
-       }
-
-       DRM_DEBUG_KMS("%s:y[0x%x],cb[0x%x],cr[0x%x]\n", __func__,
-               *base[0], *base[1], *base[2]);
-
-       return 0;
-
-err_info:
-       DRM_ERROR("invalid size for fmt[0x%x]\n", fmt);
-
-       for_each_ipp_planar(i) {
-               base[i] = &buf_info->base[i];
-               size[i] = buf_info->size[i];
-
-               DRM_ERROR("buf[%d] - base[0x%x] sz[%llu] ofs[%llu]\n",
-                       i, *base[i], size[i], ofs[i]);
-       }
-
-       return -EINVAL;
-}
-
-static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
-{
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
 
        DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
@@ -510,13 +313,12 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
        case DRM_FORMAT_RGB565:
                cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB565;
                fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
-               return 0;
+               return;
        case DRM_FORMAT_RGB888:
        case DRM_FORMAT_XRGB8888:
-       case DRM_FORMAT_ARGB8888:
                cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB888;
                fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
-               return 0;
+               return;
        default:
                /* bypass */
                break;
@@ -557,20 +359,13 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
                cfg |= (EXYNOS_MSCTRL_ORDER2P_LSB_CBCR |
                        EXYNOS_MSCTRL_C_INT_IN_2PLANE);
                break;
-       default:
-               dev_err(ippdrv->dev, "invalid source yuv order 0x%x.\n", fmt);
-               return -EINVAL;
        }
 
        fimc_write(ctx, cfg, EXYNOS_MSCTRL);
-
-       return 0;
 }
 
-static int fimc_src_set_fmt(struct device *dev, u32 fmt)
+static void fimc_src_set_fmt(struct fimc_context *ctx, u32 fmt)
 {
-       struct fimc_context *ctx = get_fimc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
 
        DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
@@ -582,7 +377,6 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
        case DRM_FORMAT_RGB565:
        case DRM_FORMAT_RGB888:
        case DRM_FORMAT_XRGB8888:
-       case DRM_FORMAT_ARGB8888:
                cfg |= EXYNOS_MSCTRL_INFORMAT_RGB;
                break;
        case DRM_FORMAT_YUV444:
@@ -605,9 +399,6 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
        case DRM_FORMAT_NV21:
                cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR420;
                break;
-       default:
-               dev_err(ippdrv->dev, "invalid source format 0x%x.\n", fmt);
-               return -EINVAL;
        }
 
        fimc_write(ctx, cfg, EXYNOS_MSCTRL);
@@ -619,18 +410,15 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
 
        fimc_write(ctx, cfg, EXYNOS_CIDMAPARAM);
 
-       return fimc_src_set_fmt_order(ctx, fmt);
+       fimc_src_set_fmt_order(ctx, fmt);
 }
 
-static int fimc_src_set_transf(struct device *dev,
-               enum drm_exynos_degree degree,
-               enum drm_exynos_flip flip, bool *swap)
+static void fimc_src_set_transf(struct fimc_context *ctx, unsigned int rotation)
 {
-       struct fimc_context *ctx = get_fimc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
+       unsigned int degree = rotation & DRM_MODE_ROTATE_MASK;
        u32 cfg1, cfg2;
 
-       DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip);
+       DRM_DEBUG_KMS("rotation[%x]\n", rotation);
 
        cfg1 = fimc_read(ctx, EXYNOS_MSCTRL);
        cfg1 &= ~(EXYNOS_MSCTRL_FLIP_X_MIRROR |
@@ -640,61 +428,56 @@ static int fimc_src_set_transf(struct device *dev,
        cfg2 &= ~EXYNOS_CITRGFMT_INROT90_CLOCKWISE;
 
        switch (degree) {
-       case EXYNOS_DRM_DEGREE_0:
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+       case DRM_MODE_ROTATE_0:
+               if (rotation & DRM_MODE_REFLECT_X)
                        cfg1 |= EXYNOS_MSCTRL_FLIP_X_MIRROR;
-               if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+               if (rotation & DRM_MODE_REFLECT_Y)
                        cfg1 |= EXYNOS_MSCTRL_FLIP_Y_MIRROR;
                break;
-       case EXYNOS_DRM_DEGREE_90:
+       case DRM_MODE_ROTATE_90:
                cfg2 |= EXYNOS_CITRGFMT_INROT90_CLOCKWISE;
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+               if (rotation & DRM_MODE_REFLECT_X)
                        cfg1 |= EXYNOS_MSCTRL_FLIP_X_MIRROR;
-               if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+               if (rotation & DRM_MODE_REFLECT_Y)
                        cfg1 |= EXYNOS_MSCTRL_FLIP_Y_MIRROR;
                break;
-       case EXYNOS_DRM_DEGREE_180:
+       case DRM_MODE_ROTATE_180:
                cfg1 |= (EXYNOS_MSCTRL_FLIP_X_MIRROR |
                        EXYNOS_MSCTRL_FLIP_Y_MIRROR);
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+               if (rotation & DRM_MODE_REFLECT_X)
                        cfg1 &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR;
-               if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+               if (rotation & DRM_MODE_REFLECT_Y)
                        cfg1 &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR;
                break;
-       case EXYNOS_DRM_DEGREE_270:
+       case DRM_MODE_ROTATE_270:
                cfg1 |= (EXYNOS_MSCTRL_FLIP_X_MIRROR |
                        EXYNOS_MSCTRL_FLIP_Y_MIRROR);
                cfg2 |= EXYNOS_CITRGFMT_INROT90_CLOCKWISE;
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+               if (rotation & DRM_MODE_REFLECT_X)
                        cfg1 &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR;
-               if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+               if (rotation & DRM_MODE_REFLECT_Y)
                        cfg1 &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR;
                break;
-       default:
-               dev_err(ippdrv->dev, "invalid degree value %d.\n", degree);
-               return -EINVAL;
        }
 
        fimc_write(ctx, cfg1, EXYNOS_MSCTRL);
        fimc_write(ctx, cfg2, EXYNOS_CITRGFMT);
-       *swap = (cfg2 & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) ? 1 : 0;
-
-       return 0;
 }
 
-static int fimc_set_window(struct fimc_context *ctx,
-               struct drm_exynos_pos *pos, struct drm_exynos_sz *sz)
+static void fimc_set_window(struct fimc_context *ctx,
+                           struct exynos_drm_ipp_buffer *buf)
 {
        u32 cfg, h1, h2, v1, v2;
 
        /* cropped image */
-       h1 = pos->x;
-       h2 = sz->hsize - pos->w - pos->x;
-       v1 = pos->y;
-       v2 = sz->vsize - pos->h - pos->y;
+       h1 = buf->rect.x;
+       h2 = buf->buf.width - buf->rect.w - buf->rect.x;
+       v1 = buf->rect.y;
+       v2 = buf->buf.height - buf->rect.h - buf->rect.y;
 
        DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]hsize[%d]vsize[%d]\n",
-               pos->x, pos->y, pos->w, pos->h, sz->hsize, sz->vsize);
+               buf->rect.x, buf->rect.y, buf->rect.w, buf->rect.h,
+               buf->buf.width, buf->buf.height);
        DRM_DEBUG_KMS("h1[%d]h2[%d]v1[%d]v2[%d]\n", h1, h2, v1, v2);
 
        /*
@@ -712,42 +495,30 @@ static int fimc_set_window(struct fimc_context *ctx,
        cfg = (EXYNOS_CIWDOFST2_WINHOROFST2(h2) |
                EXYNOS_CIWDOFST2_WINVEROFST2(v2));
        fimc_write(ctx, cfg, EXYNOS_CIWDOFST2);
-
-       return 0;
 }
 
-static int fimc_src_set_size(struct device *dev, int swap,
-               struct drm_exynos_pos *pos, struct drm_exynos_sz *sz)
+static void fimc_src_set_size(struct fimc_context *ctx,
+                             struct exynos_drm_ipp_buffer *buf)
 {
-       struct fimc_context *ctx = get_fimc_context(dev);
-       struct drm_exynos_pos img_pos = *pos;
-       struct drm_exynos_sz img_sz = *sz;
        u32 cfg;
 
-       DRM_DEBUG_KMS("swap[%d]hsize[%d]vsize[%d]\n",
-               swap, sz->hsize, sz->vsize);
+       DRM_DEBUG_KMS("hsize[%d]vsize[%d]\n", buf->buf.width, buf->buf.height);
 
        /* original size */
-       cfg = (EXYNOS_ORGISIZE_HORIZONTAL(img_sz.hsize) |
-               EXYNOS_ORGISIZE_VERTICAL(img_sz.vsize));
+       cfg = (EXYNOS_ORGISIZE_HORIZONTAL(buf->buf.width) |
+               EXYNOS_ORGISIZE_VERTICAL(buf->buf.height));
 
        fimc_write(ctx, cfg, EXYNOS_ORGISIZE);
 
-       DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h);
-
-       if (swap) {
-               img_pos.w = pos->h;
-               img_pos.h = pos->w;
-               img_sz.hsize = sz->vsize;
-               img_sz.vsize = sz->hsize;
-       }
+       DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", buf->rect.x, buf->rect.y,
+               buf->rect.w, buf->rect.h);
 
        /* set input DMA image size */
        cfg = fimc_read(ctx, EXYNOS_CIREAL_ISIZE);
        cfg &= ~(EXYNOS_CIREAL_ISIZE_HEIGHT_MASK |
                EXYNOS_CIREAL_ISIZE_WIDTH_MASK);
-       cfg |= (EXYNOS_CIREAL_ISIZE_WIDTH(img_pos.w) |
-               EXYNOS_CIREAL_ISIZE_HEIGHT(img_pos.h));
+       cfg |= (EXYNOS_CIREAL_ISIZE_WIDTH(buf->rect.w) |
+               EXYNOS_CIREAL_ISIZE_HEIGHT(buf->rect.h));
        fimc_write(ctx, cfg, EXYNOS_CIREAL_ISIZE);
 
        /*
@@ -755,95 +526,34 @@ static int fimc_src_set_size(struct device *dev, int swap,
         * for now, we support only ITU601 8 bit mode
         */
        cfg = (EXYNOS_CISRCFMT_ITU601_8BIT |
-               EXYNOS_CISRCFMT_SOURCEHSIZE(img_sz.hsize) |
-               EXYNOS_CISRCFMT_SOURCEVSIZE(img_sz.vsize));
+               EXYNOS_CISRCFMT_SOURCEHSIZE(buf->buf.width) |
+               EXYNOS_CISRCFMT_SOURCEVSIZE(buf->buf.height));
        fimc_write(ctx, cfg, EXYNOS_CISRCFMT);
 
        /* offset Y(RGB), Cb, Cr */
-       cfg = (EXYNOS_CIIYOFF_HORIZONTAL(img_pos.x) |
-               EXYNOS_CIIYOFF_VERTICAL(img_pos.y));
+       cfg = (EXYNOS_CIIYOFF_HORIZONTAL(buf->rect.x) |
+               EXYNOS_CIIYOFF_VERTICAL(buf->rect.y));
        fimc_write(ctx, cfg, EXYNOS_CIIYOFF);
-       cfg = (EXYNOS_CIICBOFF_HORIZONTAL(img_pos.x) |
-               EXYNOS_CIICBOFF_VERTICAL(img_pos.y));
+       cfg = (EXYNOS_CIICBOFF_HORIZONTAL(buf->rect.x) |
+               EXYNOS_CIICBOFF_VERTICAL(buf->rect.y));
        fimc_write(ctx, cfg, EXYNOS_CIICBOFF);
-       cfg = (EXYNOS_CIICROFF_HORIZONTAL(img_pos.x) |
-               EXYNOS_CIICROFF_VERTICAL(img_pos.y));
+       cfg = (EXYNOS_CIICROFF_HORIZONTAL(buf->rect.x) |
+               EXYNOS_CIICROFF_VERTICAL(buf->rect.y));
        fimc_write(ctx, cfg, EXYNOS_CIICROFF);
 
-       return fimc_set_window(ctx, &img_pos, &img_sz);
+       fimc_set_window(ctx, buf);
 }
 
-static int fimc_src_set_addr(struct device *dev,
-               struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id,
-               enum drm_exynos_ipp_buf_type buf_type)
+static void fimc_src_set_addr(struct fimc_context *ctx,
+                             struct exynos_drm_ipp_buffer *buf)
 {
-       struct fimc_context *ctx = get_fimc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node;
-       struct drm_exynos_ipp_property *property;
-       struct drm_exynos_ipp_config *config;
-       int ret;
-
-       if (!c_node) {
-               DRM_ERROR("failed to get c_node.\n");
-               return -EINVAL;
-       }
-
-       property = &c_node->property;
-
-       DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n",
-               property->prop_id, buf_id, buf_type);
-
-       buf_id %= FIMC_MAX_SRC;
-
-       /* address register set */
-       switch (buf_type) {
-       case IPP_BUF_ENQUEUE:
-               config = &property->config[EXYNOS_DRM_OPS_SRC];
-               ret = fimc_set_planar_addr(buf_info, config->fmt, &config->sz);
-               if (ret) {
-                       dev_err(dev, "failed to set plane src addr.\n");
-                       return ret;
-               }
-
-               fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y],
-                       EXYNOS_CIIYSA0);
-
-               if (config->fmt == DRM_FORMAT_YVU420) {
-                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
-                               EXYNOS_CIICBSA0);
-                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
-                               EXYNOS_CIICRSA0);
-               } else {
-                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
-                               EXYNOS_CIICBSA0);
-                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
-                               EXYNOS_CIICRSA0);
-               }
-               break;
-       case IPP_BUF_DEQUEUE:
-               fimc_write(ctx, 0x0, EXYNOS_CIIYSA0);
-               fimc_write(ctx, 0x0, EXYNOS_CIICBSA0);
-               fimc_write(ctx, 0x0, EXYNOS_CIICRSA0);
-               break;
-       default:
-               /* bypass */
-               break;
-       }
-
-       return 0;
+       fimc_write(ctx, buf->dma_addr[0], EXYNOS_CIIYSA(0));
+       fimc_write(ctx, buf->dma_addr[1], EXYNOS_CIICBSA(0));
+       fimc_write(ctx, buf->dma_addr[2], EXYNOS_CIICRSA(0));
 }
 
-static struct exynos_drm_ipp_ops fimc_src_ops = {
-       .set_fmt = fimc_src_set_fmt,
-       .set_transf = fimc_src_set_transf,
-       .set_size = fimc_src_set_size,
-       .set_addr = fimc_src_set_addr,
-};
-
-static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
+static void fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
 {
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
 
        DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
@@ -856,13 +566,12 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
        case DRM_FORMAT_RGB565:
                cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565;
                fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
-               return 0;
+               return;
        case DRM_FORMAT_RGB888:
                cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888;
                fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
-               return 0;
+               return;
        case DRM_FORMAT_XRGB8888:
-       case DRM_FORMAT_ARGB8888:
                cfg |= (EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 |
                        EXYNOS_CISCCTRL_EXTRGB_EXTENSION);
                fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
@@ -880,7 +589,6 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
 
        switch (fmt) {
        case DRM_FORMAT_XRGB8888:
-       case DRM_FORMAT_ARGB8888:
                cfg |= EXYNOS_CIOCTRL_ALPHA_OUT;
                break;
        case DRM_FORMAT_YUYV:
@@ -910,20 +618,13 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
                cfg |= EXYNOS_CIOCTRL_ORDER2P_LSB_CBCR;
                cfg |= EXYNOS_CIOCTRL_YCBCR_2PLANE;
                break;
-       default:
-               dev_err(ippdrv->dev, "invalid target yuv order 0x%x.\n", fmt);
-               return -EINVAL;
        }
 
        fimc_write(ctx, cfg, EXYNOS_CIOCTRL);
-
-       return 0;
 }
 
-static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
+static void fimc_dst_set_fmt(struct fimc_context *ctx, u32 fmt)
 {
-       struct fimc_context *ctx = get_fimc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
 
        DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
@@ -944,7 +645,6 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
                case DRM_FORMAT_RGB565:
                case DRM_FORMAT_RGB888:
                case DRM_FORMAT_XRGB8888:
-               case DRM_FORMAT_ARGB8888:
                        cfg |= EXYNOS_CITRGFMT_OUTFORMAT_RGB;
                        break;
                case DRM_FORMAT_YUYV:
@@ -964,10 +664,6 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
                case DRM_FORMAT_NV21:
                        cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR420;
                        break;
-               default:
-                       dev_err(ippdrv->dev, "invalid target format 0x%x.\n",
-                               fmt);
-                       return -EINVAL;
                }
 
                fimc_write(ctx, cfg, EXYNOS_CITRGFMT);
@@ -980,69 +676,60 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
 
        fimc_write(ctx, cfg, EXYNOS_CIDMAPARAM);
 
-       return fimc_dst_set_fmt_order(ctx, fmt);
+       fimc_dst_set_fmt_order(ctx, fmt);
 }
 
-static int fimc_dst_set_transf(struct device *dev,
-               enum drm_exynos_degree degree,
-               enum drm_exynos_flip flip, bool *swap)
+static void fimc_dst_set_transf(struct fimc_context *ctx, unsigned int rotation)
 {
-       struct fimc_context *ctx = get_fimc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
+       unsigned int degree = rotation & DRM_MODE_ROTATE_MASK;
        u32 cfg;
 
-       DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip);
+       DRM_DEBUG_KMS("rotation[0x%x]\n", rotation);
 
        cfg = fimc_read(ctx, EXYNOS_CITRGFMT);
        cfg &= ~EXYNOS_CITRGFMT_FLIP_MASK;
        cfg &= ~EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE;
 
        switch (degree) {
-       case EXYNOS_DRM_DEGREE_0:
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+       case DRM_MODE_ROTATE_0:
+               if (rotation & DRM_MODE_REFLECT_X)
                        cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR;
-               if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+               if (rotation & DRM_MODE_REFLECT_Y)
                        cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR;
                break;
-       case EXYNOS_DRM_DEGREE_90:
+       case DRM_MODE_ROTATE_90:
                cfg |= EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE;
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+               if (rotation & DRM_MODE_REFLECT_X)
                        cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR;
-               if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+               if (rotation & DRM_MODE_REFLECT_Y)
                        cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR;
                break;
-       case EXYNOS_DRM_DEGREE_180:
+       case DRM_MODE_ROTATE_180:
                cfg |= (EXYNOS_CITRGFMT_FLIP_X_MIRROR |
                        EXYNOS_CITRGFMT_FLIP_Y_MIRROR);
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+               if (rotation & DRM_MODE_REFLECT_X)
                        cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR;
-               if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+               if (rotation & DRM_MODE_REFLECT_Y)
                        cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR;
                break;
-       case EXYNOS_DRM_DEGREE_270:
+       case DRM_MODE_ROTATE_270:
                cfg |= (EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE |
                        EXYNOS_CITRGFMT_FLIP_X_MIRROR |
                        EXYNOS_CITRGFMT_FLIP_Y_MIRROR);
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+               if (rotation & DRM_MODE_REFLECT_X)
                        cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR;
-               if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+               if (rotation & DRM_MODE_REFLECT_Y)
                        cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR;
                break;
-       default:
-               dev_err(ippdrv->dev, "invalid degree value %d.\n", degree);
-               return -EINVAL;
        }
 
        fimc_write(ctx, cfg, EXYNOS_CITRGFMT);
-       *swap = (cfg & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) ? 1 : 0;
-
-       return 0;
 }
 
 static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc,
-               struct drm_exynos_pos *src, struct drm_exynos_pos *dst)
+                             struct drm_exynos_ipp_task_rect *src,
+                             struct drm_exynos_ipp_task_rect *dst)
 {
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg, cfg_ext, shfactor;
        u32 pre_dst_width, pre_dst_height;
        u32 hfactor, vfactor;
@@ -1069,13 +756,13 @@ static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc,
        /* fimc_ippdrv_check_property assures that dividers are not null */
        hfactor = fls(src_w / dst_w / 2);
        if (hfactor > FIMC_SHFACTOR / 2) {
-               dev_err(ippdrv->dev, "failed to get ratio horizontal.\n");
+               dev_err(ctx->dev, "failed to get ratio horizontal.\n");
                return -EINVAL;
        }
 
        vfactor = fls(src_h / dst_h / 2);
        if (vfactor > FIMC_SHFACTOR / 2) {
-               dev_err(ippdrv->dev, "failed to get ratio vertical.\n");
+               dev_err(ctx->dev, "failed to get ratio vertical.\n");
                return -EINVAL;
        }
 
@@ -1146,83 +833,77 @@ static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc)
        fimc_write(ctx, cfg_ext, EXYNOS_CIEXTEN);
 }
 
-static int fimc_dst_set_size(struct device *dev, int swap,
-               struct drm_exynos_pos *pos, struct drm_exynos_sz *sz)
+static void fimc_dst_set_size(struct fimc_context *ctx,
+                            struct exynos_drm_ipp_buffer *buf)
 {
-       struct fimc_context *ctx = get_fimc_context(dev);
-       struct drm_exynos_pos img_pos = *pos;
-       struct drm_exynos_sz img_sz = *sz;
-       u32 cfg;
+       u32 cfg, cfg_ext;
 
-       DRM_DEBUG_KMS("swap[%d]hsize[%d]vsize[%d]\n",
-               swap, sz->hsize, sz->vsize);
+       DRM_DEBUG_KMS("hsize[%d]vsize[%d]\n", buf->buf.width, buf->buf.height);
 
        /* original size */
-       cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(img_sz.hsize) |
-               EXYNOS_ORGOSIZE_VERTICAL(img_sz.vsize));
+       cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(buf->buf.width) |
+               EXYNOS_ORGOSIZE_VERTICAL(buf->buf.height));
 
        fimc_write(ctx, cfg, EXYNOS_ORGOSIZE);
 
-       DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h);
+       DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", buf->rect.x, buf->rect.y,
+               buf->rect.w, buf->rect.h);
 
        /* CSC ITU */
        cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
        cfg &= ~EXYNOS_CIGCTRL_CSC_MASK;
 
-       if (sz->hsize >= FIMC_WIDTH_ITU_709)
+       if (buf->buf.width >= FIMC_WIDTH_ITU_709)
                cfg |= EXYNOS_CIGCTRL_CSC_ITU709;
        else
                cfg |= EXYNOS_CIGCTRL_CSC_ITU601;
 
        fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
 
-       if (swap) {
-               img_pos.w = pos->h;
-               img_pos.h = pos->w;
-               img_sz.hsize = sz->vsize;
-               img_sz.vsize = sz->hsize;
-       }
+       cfg_ext = fimc_read(ctx, EXYNOS_CITRGFMT);
 
        /* target image size */
        cfg = fimc_read(ctx, EXYNOS_CITRGFMT);
        cfg &= ~(EXYNOS_CITRGFMT_TARGETH_MASK |
                EXYNOS_CITRGFMT_TARGETV_MASK);
-       cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(img_pos.w) |
-               EXYNOS_CITRGFMT_TARGETVSIZE(img_pos.h));
+       if (cfg_ext & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE)
+               cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(buf->rect.h) |
+                       EXYNOS_CITRGFMT_TARGETVSIZE(buf->rect.w));
+       else
+               cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(buf->rect.w) |
+                       EXYNOS_CITRGFMT_TARGETVSIZE(buf->rect.h));
        fimc_write(ctx, cfg, EXYNOS_CITRGFMT);
 
        /* target area */
-       cfg = EXYNOS_CITAREA_TARGET_AREA(img_pos.w * img_pos.h);
+       cfg = EXYNOS_CITAREA_TARGET_AREA(buf->rect.w * buf->rect.h);
        fimc_write(ctx, cfg, EXYNOS_CITAREA);
 
        /* offset Y(RGB), Cb, Cr */
-       cfg = (EXYNOS_CIOYOFF_HORIZONTAL(img_pos.x) |
-               EXYNOS_CIOYOFF_VERTICAL(img_pos.y));
+       cfg = (EXYNOS_CIOYOFF_HORIZONTAL(buf->rect.x) |
+               EXYNOS_CIOYOFF_VERTICAL(buf->rect.y));
        fimc_write(ctx, cfg, EXYNOS_CIOYOFF);
-       cfg = (EXYNOS_CIOCBOFF_HORIZONTAL(img_pos.x) |
-               EXYNOS_CIOCBOFF_VERTICAL(img_pos.y));
+       cfg = (EXYNOS_CIOCBOFF_HORIZONTAL(buf->rect.x) |
+               EXYNOS_CIOCBOFF_VERTICAL(buf->rect.y));
        fimc_write(ctx, cfg, EXYNOS_CIOCBOFF);
-       cfg = (EXYNOS_CIOCROFF_HORIZONTAL(img_pos.x) |
-               EXYNOS_CIOCROFF_VERTICAL(img_pos.y));
+       cfg = (EXYNOS_CIOCROFF_HORIZONTAL(buf->rect.x) |
+               EXYNOS_CIOCROFF_VERTICAL(buf->rect.y));
        fimc_write(ctx, cfg, EXYNOS_CIOCROFF);
-
-       return 0;
 }
 
 static void fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id,
-               enum drm_exynos_ipp_buf_type buf_type)
+               bool enqueue)
 {
        unsigned long flags;
        u32 buf_num;
        u32 cfg;
 
-       DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type);
+       DRM_DEBUG_KMS("buf_id[%d]enqueu[%d]\n", buf_id, enqueue);
 
        spin_lock_irqsave(&ctx->lock, flags);
 
        cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ);
 
-       if (buf_type == IPP_BUF_ENQUEUE)
+       if (enqueue)
                cfg |= (1 << buf_id);
        else
                cfg &= ~(1 << buf_id);
@@ -1231,108 +912,29 @@ static void fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id,
 
        buf_num = hweight32(cfg);
 
-       if (buf_type == IPP_BUF_ENQUEUE && buf_num >= FIMC_BUF_START)
+       if (enqueue && buf_num >= FIMC_BUF_START)
                fimc_mask_irq(ctx, true);
-       else if (buf_type == IPP_BUF_DEQUEUE && buf_num <= FIMC_BUF_STOP)
+       else if (!enqueue && buf_num <= FIMC_BUF_STOP)
                fimc_mask_irq(ctx, false);
 
        spin_unlock_irqrestore(&ctx->lock, flags);
 }
 
-static int fimc_dst_set_addr(struct device *dev,
-               struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id,
-               enum drm_exynos_ipp_buf_type buf_type)
+static void fimc_dst_set_addr(struct fimc_context *ctx,
+                            struct exynos_drm_ipp_buffer *buf)
 {
-       struct fimc_context *ctx = get_fimc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node;
-       struct drm_exynos_ipp_property *property;
-       struct drm_exynos_ipp_config *config;
-       int ret;
-
-       if (!c_node) {
-               DRM_ERROR("failed to get c_node.\n");
-               return -EINVAL;
-       }
-
-       property = &c_node->property;
-
-       DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n",
-               property->prop_id, buf_id, buf_type);
-
-       buf_id %= FIMC_MAX_DST;
-
-       /* address register set */
-       switch (buf_type) {
-       case IPP_BUF_ENQUEUE:
-               config = &property->config[EXYNOS_DRM_OPS_DST];
-               ret = fimc_set_planar_addr(buf_info, config->fmt, &config->sz);
-               if (ret) {
-                       dev_err(dev, "failed to set plane dst addr.\n");
-                       return ret;
-               }
+       fimc_write(ctx, buf->dma_addr[0], EXYNOS_CIOYSA(0));
+       fimc_write(ctx, buf->dma_addr[1], EXYNOS_CIOCBSA(0));
+       fimc_write(ctx, buf->dma_addr[2], EXYNOS_CIOCRSA(0));
 
-               fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y],
-                       EXYNOS_CIOYSA(buf_id));
-
-               if (config->fmt == DRM_FORMAT_YVU420) {
-                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
-                               EXYNOS_CIOCBSA(buf_id));
-                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
-                               EXYNOS_CIOCRSA(buf_id));
-               } else {
-                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
-                               EXYNOS_CIOCBSA(buf_id));
-                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
-                               EXYNOS_CIOCRSA(buf_id));
-               }
-               break;
-       case IPP_BUF_DEQUEUE:
-               fimc_write(ctx, 0x0, EXYNOS_CIOYSA(buf_id));
-               fimc_write(ctx, 0x0, EXYNOS_CIOCBSA(buf_id));
-               fimc_write(ctx, 0x0, EXYNOS_CIOCRSA(buf_id));
-               break;
-       default:
-               /* bypass */
-               break;
-       }
-
-       fimc_dst_set_buf_seq(ctx, buf_id, buf_type);
-
-       return 0;
+       fimc_dst_set_buf_seq(ctx, 0, true);
 }
 
-static struct exynos_drm_ipp_ops fimc_dst_ops = {
-       .set_fmt = fimc_dst_set_fmt,
-       .set_transf = fimc_dst_set_transf,
-       .set_size = fimc_dst_set_size,
-       .set_addr = fimc_dst_set_addr,
-};
-
-static int fimc_clk_ctrl(struct fimc_context *ctx, bool enable)
-{
-       DRM_DEBUG_KMS("enable[%d]\n", enable);
-
-       if (enable) {
-               clk_prepare_enable(ctx->clocks[FIMC_CLK_GATE]);
-               clk_prepare_enable(ctx->clocks[FIMC_CLK_WB_A]);
-               ctx->suspended = false;
-       } else {
-               clk_disable_unprepare(ctx->clocks[FIMC_CLK_GATE]);
-               clk_disable_unprepare(ctx->clocks[FIMC_CLK_WB_A]);
-               ctx->suspended = true;
-       }
-
-       return 0;
-}
+static void fimc_stop(struct fimc_context *ctx);
 
 static irqreturn_t fimc_irq_handler(int irq, void *dev_id)
 {
        struct fimc_context *ctx = dev_id;
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node;
-       struct drm_exynos_ipp_event_work *event_work =
-               c_node->event_work;
        int buf_id;
 
        DRM_DEBUG_KMS("fimc id[%d]\n", ctx->id);
@@ -1350,170 +952,19 @@ static irqreturn_t fimc_irq_handler(int irq, void *dev_id)
 
        DRM_DEBUG_KMS("buf_id[%d]\n", buf_id);
 
-       fimc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE);
-
-       event_work->ippdrv = ippdrv;
-       event_work->buf_id[EXYNOS_DRM_OPS_DST] = buf_id;
-       queue_work(ippdrv->event_workq, &event_work->work);
-
-       return IRQ_HANDLED;
-}
-
-static int fimc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
-{
-       struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
-
-       prop_list->version = 1;
-       prop_list->writeback = 1;
-       prop_list->refresh_min = FIMC_REFRESH_MIN;
-       prop_list->refresh_max = FIMC_REFRESH_MAX;
-       prop_list->flip = (1 << EXYNOS_DRM_FLIP_NONE) |
-                               (1 << EXYNOS_DRM_FLIP_VERTICAL) |
-                               (1 << EXYNOS_DRM_FLIP_HORIZONTAL);
-       prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) |
-                               (1 << EXYNOS_DRM_DEGREE_90) |
-                               (1 << EXYNOS_DRM_DEGREE_180) |
-                               (1 << EXYNOS_DRM_DEGREE_270);
-       prop_list->csc = 1;
-       prop_list->crop = 1;
-       prop_list->crop_max.hsize = FIMC_CROP_MAX;
-       prop_list->crop_max.vsize = FIMC_CROP_MAX;
-       prop_list->crop_min.hsize = FIMC_CROP_MIN;
-       prop_list->crop_min.vsize = FIMC_CROP_MIN;
-       prop_list->scale = 1;
-       prop_list->scale_max.hsize = FIMC_SCALE_MAX;
-       prop_list->scale_max.vsize = FIMC_SCALE_MAX;
-       prop_list->scale_min.hsize = FIMC_SCALE_MIN;
-       prop_list->scale_min.vsize = FIMC_SCALE_MIN;
-
-       return 0;
-}
-
-static inline bool fimc_check_drm_flip(enum drm_exynos_flip flip)
-{
-       switch (flip) {
-       case EXYNOS_DRM_FLIP_NONE:
-       case EXYNOS_DRM_FLIP_VERTICAL:
-       case EXYNOS_DRM_FLIP_HORIZONTAL:
-       case EXYNOS_DRM_FLIP_BOTH:
-               return true;
-       default:
-               DRM_DEBUG_KMS("invalid flip\n");
-               return false;
-       }
-}
-
-static int fimc_ippdrv_check_property(struct device *dev,
-               struct drm_exynos_ipp_property *property)
-{
-       struct fimc_context *ctx = get_fimc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       struct drm_exynos_ipp_prop_list *pp = &ippdrv->prop_list;
-       struct drm_exynos_ipp_config *config;
-       struct drm_exynos_pos *pos;
-       struct drm_exynos_sz *sz;
-       bool swap;
-       int i;
-
-       for_each_ipp_ops(i) {
-               if ((i == EXYNOS_DRM_OPS_SRC) &&
-                       (property->cmd == IPP_CMD_WB))
-                       continue;
-
-               config = &property->config[i];
-               pos = &config->pos;
-               sz = &config->sz;
-
-               /* check for flip */
-               if (!fimc_check_drm_flip(config->flip)) {
-                       DRM_ERROR("invalid flip.\n");
-                       goto err_property;
-               }
-
-               /* check for degree */
-               switch (config->degree) {
-               case EXYNOS_DRM_DEGREE_90:
-               case EXYNOS_DRM_DEGREE_270:
-                       swap = true;
-                       break;
-               case EXYNOS_DRM_DEGREE_0:
-               case EXYNOS_DRM_DEGREE_180:
-                       swap = false;
-                       break;
-               default:
-                       DRM_ERROR("invalid degree.\n");
-                       goto err_property;
-               }
-
-               /* check for buffer bound */
-               if ((pos->x + pos->w > sz->hsize) ||
-                       (pos->y + pos->h > sz->vsize)) {
-                       DRM_ERROR("out of buf bound.\n");
-                       goto err_property;
-               }
+       if (ctx->task) {
+               struct exynos_drm_ipp_task *task = ctx->task;
 
-               /* check for crop */
-               if ((i == EXYNOS_DRM_OPS_SRC) && (pp->crop)) {
-                       if (swap) {
-                               if ((pos->h < pp->crop_min.hsize) ||
-                                       (sz->vsize > pp->crop_max.hsize) ||
-                                       (pos->w < pp->crop_min.vsize) ||
-                                       (sz->hsize > pp->crop_max.vsize)) {
-                                       DRM_ERROR("out of crop size.\n");
-                                       goto err_property;
-                               }
-                       } else {
-                               if ((pos->w < pp->crop_min.hsize) ||
-                                       (sz->hsize > pp->crop_max.hsize) ||
-                                       (pos->h < pp->crop_min.vsize) ||
-                                       (sz->vsize > pp->crop_max.vsize)) {
-                                       DRM_ERROR("out of crop size.\n");
-                                       goto err_property;
-                               }
-                       }
-               }
-
-               /* check for scale */
-               if ((i == EXYNOS_DRM_OPS_DST) && (pp->scale)) {
-                       if (swap) {
-                               if ((pos->h < pp->scale_min.hsize) ||
-                                       (sz->vsize > pp->scale_max.hsize) ||
-                                       (pos->w < pp->scale_min.vsize) ||
-                                       (sz->hsize > pp->scale_max.vsize)) {
-                                       DRM_ERROR("out of scale size.\n");
-                                       goto err_property;
-                               }
-                       } else {
-                               if ((pos->w < pp->scale_min.hsize) ||
-                                       (sz->hsize > pp->scale_max.hsize) ||
-                                       (pos->h < pp->scale_min.vsize) ||
-                                       (sz->vsize > pp->scale_max.vsize)) {
-                                       DRM_ERROR("out of scale size.\n");
-                                       goto err_property;
-                               }
-                       }
-               }
+               ctx->task = NULL;
+               pm_runtime_mark_last_busy(ctx->dev);
+               pm_runtime_put_autosuspend(ctx->dev);
+               exynos_drm_ipp_task_done(task, 0);
        }
 
-       return 0;
-
-err_property:
-       for_each_ipp_ops(i) {
-               if ((i == EXYNOS_DRM_OPS_SRC) &&
-                       (property->cmd == IPP_CMD_WB))
-                       continue;
-
-               config = &property->config[i];
-               pos = &config->pos;
-               sz = &config->sz;
-
-               DRM_ERROR("[%s]f[%d]r[%d]pos[%d %d %d %d]sz[%d %d]\n",
-                       i ? "dst" : "src", config->flip, config->degree,
-                       pos->x, pos->y, pos->w, pos->h,
-                       sz->hsize, sz->vsize);
-       }
+       fimc_dst_set_buf_seq(ctx, buf_id, false);
+       fimc_stop(ctx);
 
-       return -EINVAL;
+       return IRQ_HANDLED;
 }
 
 static void fimc_clear_addr(struct fimc_context *ctx)
@@ -1533,10 +984,8 @@ static void fimc_clear_addr(struct fimc_context *ctx)
        }
 }
 
-static int fimc_ippdrv_reset(struct device *dev)
+static void fimc_reset(struct fimc_context *ctx)
 {
-       struct fimc_context *ctx = get_fimc_context(dev);
-
        /* reset h/w block */
        fimc_sw_reset(ctx);
 
@@ -1544,83 +993,26 @@ static int fimc_ippdrv_reset(struct device *dev)
        memset(&ctx->sc, 0x0, sizeof(ctx->sc));
 
        fimc_clear_addr(ctx);
-
-       return 0;
 }
 
-static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
+static void fimc_start(struct fimc_context *ctx)
 {
-       struct fimc_context *ctx = get_fimc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node;
-       struct drm_exynos_ipp_property *property;
-       struct drm_exynos_ipp_config *config;
-       struct drm_exynos_pos   img_pos[EXYNOS_DRM_OPS_MAX];
-       struct drm_exynos_ipp_set_wb set_wb;
-       int ret, i;
        u32 cfg0, cfg1;
 
-       DRM_DEBUG_KMS("cmd[%d]\n", cmd);
-
-       if (!c_node) {
-               DRM_ERROR("failed to get c_node.\n");
-               return -EINVAL;
-       }
-
-       property = &c_node->property;
-
        fimc_mask_irq(ctx, true);
 
-       for_each_ipp_ops(i) {
-               config = &property->config[i];
-               img_pos[i] = config->pos;
-       }
-
-       ret = fimc_set_prescaler(ctx, &ctx->sc,
-               &img_pos[EXYNOS_DRM_OPS_SRC],
-               &img_pos[EXYNOS_DRM_OPS_DST]);
-       if (ret) {
-               dev_err(dev, "failed to set precalser.\n");
-               return ret;
-       }
-
-       /* If set ture, we can save jpeg about screen */
+       /* If set true, we can save jpeg about screen */
        fimc_handle_jpeg(ctx, false);
        fimc_set_scaler(ctx, &ctx->sc);
-       fimc_set_polarity(ctx, &ctx->pol);
-
-       switch (cmd) {
-       case IPP_CMD_M2M:
-               fimc_set_type_ctrl(ctx, FIMC_WB_NONE);
-               fimc_handle_lastend(ctx, false);
-
-               /* setup dma */
-               cfg0 = fimc_read(ctx, EXYNOS_MSCTRL);
-               cfg0 &= ~EXYNOS_MSCTRL_INPUT_MASK;
-               cfg0 |= EXYNOS_MSCTRL_INPUT_MEMORY;
-               fimc_write(ctx, cfg0, EXYNOS_MSCTRL);
-               break;
-       case IPP_CMD_WB:
-               fimc_set_type_ctrl(ctx, FIMC_WB_A);
-               fimc_handle_lastend(ctx, true);
-
-               /* setup FIMD */
-               ret = fimc_set_camblk_fimd0_wb(ctx);
-               if (ret < 0) {
-                       dev_err(dev, "camblk setup failed.\n");
-                       return ret;
-               }
 
-               set_wb.enable = 1;
-               set_wb.refresh = property->refresh_rate;
-               exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb);
-               break;
-       case IPP_CMD_OUTPUT:
-       default:
-               ret = -EINVAL;
-               dev_err(dev, "invalid operations.\n");
-               return ret;
-       }
+       fimc_set_type_ctrl(ctx);
+       fimc_handle_lastend(ctx, false);
+
+       /* setup dma */
+       cfg0 = fimc_read(ctx, EXYNOS_MSCTRL);
+       cfg0 &= ~EXYNOS_MSCTRL_INPUT_MASK;
+       cfg0 |= EXYNOS_MSCTRL_INPUT_MEMORY;
+       fimc_write(ctx, cfg0, EXYNOS_MSCTRL);
 
        /* Reset status */
        fimc_write(ctx, 0x0, EXYNOS_CISTATUS);
@@ -1646,44 +1038,24 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
 
        fimc_clear_bits(ctx, EXYNOS_CIOCTRL, EXYNOS_CIOCTRL_WEAVE_MASK);
 
-       if (cmd == IPP_CMD_M2M)
-               fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
-
-       return 0;
+       fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
 }
 
-static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
+static void fimc_stop(struct fimc_context *ctx)
 {
-       struct fimc_context *ctx = get_fimc_context(dev);
-       struct drm_exynos_ipp_set_wb set_wb = {0, 0};
        u32 cfg;
 
-       DRM_DEBUG_KMS("cmd[%d]\n", cmd);
-
-       switch (cmd) {
-       case IPP_CMD_M2M:
-               /* Source clear */
-               cfg = fimc_read(ctx, EXYNOS_MSCTRL);
-               cfg &= ~EXYNOS_MSCTRL_INPUT_MASK;
-               cfg &= ~EXYNOS_MSCTRL_ENVID;
-               fimc_write(ctx, cfg, EXYNOS_MSCTRL);
-               break;
-       case IPP_CMD_WB:
-               exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb);
-               break;
-       case IPP_CMD_OUTPUT:
-       default:
-               dev_err(dev, "invalid operations.\n");
-               break;
-       }
+       /* Source clear */
+       cfg = fimc_read(ctx, EXYNOS_MSCTRL);
+       cfg &= ~EXYNOS_MSCTRL_INPUT_MASK;
+       cfg &= ~EXYNOS_MSCTRL_ENVID;
+       fimc_write(ctx, cfg, EXYNOS_MSCTRL);
 
        fimc_mask_irq(ctx, false);
 
        /* reset sequence */
        fimc_write(ctx, 0x0, EXYNOS_CIFCNTSEQ);
 
-       fimc_clear_addr(ctx);
-
        /* Scaler disable */
        fimc_clear_bits(ctx, EXYNOS_CISCCTRL, EXYNOS_CISCCTRL_SCALERSTART);
 
@@ -1695,6 +1067,87 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
        fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_END_DISABLE);
 }
 
+static int fimc_commit(struct exynos_drm_ipp *ipp,
+                         struct exynos_drm_ipp_task *task)
+{
+       struct fimc_context *ctx =
+                       container_of(ipp, struct fimc_context, ipp);
+
+       pm_runtime_get_sync(ctx->dev);
+       ctx->task = task;
+
+       fimc_src_set_fmt(ctx, task->src.buf.fourcc);
+       fimc_src_set_size(ctx, &task->src);
+       fimc_src_set_transf(ctx, DRM_MODE_ROTATE_0);
+       fimc_src_set_addr(ctx, &task->src);
+       fimc_dst_set_fmt(ctx, task->dst.buf.fourcc);
+       fimc_dst_set_transf(ctx, task->transform.rotation);
+       fimc_dst_set_size(ctx, &task->dst);
+       fimc_dst_set_addr(ctx, &task->dst);
+       fimc_set_prescaler(ctx, &ctx->sc, &task->src.rect, &task->dst.rect);
+       fimc_start(ctx);
+
+       return 0;
+}
+
+static void fimc_abort(struct exynos_drm_ipp *ipp,
+                         struct exynos_drm_ipp_task *task)
+{
+       struct fimc_context *ctx =
+                       container_of(ipp, struct fimc_context, ipp);
+
+       fimc_reset(ctx);
+
+       if (ctx->task) {
+               struct exynos_drm_ipp_task *task = ctx->task;
+
+               ctx->task = NULL;
+               pm_runtime_mark_last_busy(ctx->dev);
+               pm_runtime_put_autosuspend(ctx->dev);
+               exynos_drm_ipp_task_done(task, -EIO);
+       }
+}
+
+static struct exynos_drm_ipp_funcs ipp_funcs = {
+       .commit = fimc_commit,
+       .abort = fimc_abort,
+};
+
+static int fimc_bind(struct device *dev, struct device *master, void *data)
+{
+       struct fimc_context *ctx = dev_get_drvdata(dev);
+       struct drm_device *drm_dev = data;
+       struct exynos_drm_ipp *ipp = &ctx->ipp;
+
+       ctx->drm_dev = drm_dev;
+       drm_iommu_attach_device(drm_dev, dev);
+
+       exynos_drm_ipp_register(drm_dev, ipp, &ipp_funcs,
+                       DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE |
+                       DRM_EXYNOS_IPP_CAP_SCALE | DRM_EXYNOS_IPP_CAP_CONVERT,
+                       ctx->formats, ctx->num_formats, "fimc");
+
+       dev_info(dev, "The exynos fimc has been probed successfully\n");
+
+       return 0;
+}
+
+static void fimc_unbind(struct device *dev, struct device *master,
+                       void *data)
+{
+       struct fimc_context *ctx = dev_get_drvdata(dev);
+       struct drm_device *drm_dev = data;
+       struct exynos_drm_ipp *ipp = &ctx->ipp;
+
+       exynos_drm_ipp_unregister(drm_dev, ipp);
+       drm_iommu_detach_device(drm_dev, dev);
+}
+
+static const struct component_ops fimc_component_ops = {
+       .bind   = fimc_bind,
+       .unbind = fimc_unbind,
+};
+
 static void fimc_put_clocks(struct fimc_context *ctx)
 {
        int i;
@@ -1709,7 +1162,7 @@ static void fimc_put_clocks(struct fimc_context *ctx)
 
 static int fimc_setup_clocks(struct fimc_context *ctx)
 {
-       struct device *fimc_dev = ctx->ippdrv.dev;
+       struct device *fimc_dev = ctx->dev;
        struct device *dev;
        int ret, i;
 
@@ -1724,8 +1177,6 @@ static int fimc_setup_clocks(struct fimc_context *ctx)
 
                ctx->clocks[i] = clk_get(dev, fimc_clock_names[i]);
                if (IS_ERR(ctx->clocks[i])) {
-                       if (i >= FIMC_CLK_MUX)
-                               break;
                        ret = PTR_ERR(ctx->clocks[i]);
                        dev_err(fimc_dev, "failed to get clock: %s\n",
                                                fimc_clock_names[i]);
@@ -1733,20 +1184,6 @@ static int fimc_setup_clocks(struct fimc_context *ctx)
                }
        }
 
-       /* Optional FIMC LCLK parent clock setting */
-       if (!IS_ERR(ctx->clocks[FIMC_CLK_PARENT])) {
-               ret = clk_set_parent(ctx->clocks[FIMC_CLK_MUX],
-                                    ctx->clocks[FIMC_CLK_PARENT]);
-               if (ret < 0) {
-                       dev_err(fimc_dev, "failed to set parent.\n");
-                       goto e_clk_free;
-               }
-       }
-
-       ret = clk_set_rate(ctx->clocks[FIMC_CLK_LCLK], ctx->clk_frequency);
-       if (ret < 0)
-               goto e_clk_free;
-
        ret = clk_prepare_enable(ctx->clocks[FIMC_CLK_LCLK]);
        if (!ret)
                return ret;
@@ -1755,57 +1192,81 @@ e_clk_free:
        return ret;
 }
 
-static int fimc_parse_dt(struct fimc_context *ctx)
+int exynos_drm_check_fimc_device(struct device *dev)
 {
-       struct device_node *node = ctx->ippdrv.dev->of_node;
+       unsigned int id = of_alias_get_id(dev->of_node, "fimc");
 
-       /* Handle only devices that support the LCD Writeback data path */
-       if (!of_property_read_bool(node, "samsung,lcd-wb"))
-               return -ENODEV;
-
-       if (of_property_read_u32(node, "clock-frequency",
-                                       &ctx->clk_frequency))
-               ctx->clk_frequency = FIMC_DEFAULT_LCLK_FREQUENCY;
+       if (id >= 0 && (BIT(id) & fimc_mask))
+               return 0;
+       return -ENODEV;
+}
 
-       ctx->id = of_alias_get_id(node, "fimc");
+static const unsigned int fimc_formats[] = {
+       DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB565,
+       DRM_FORMAT_NV12, DRM_FORMAT_NV16, DRM_FORMAT_NV21, DRM_FORMAT_NV61,
+       DRM_FORMAT_UYVY, DRM_FORMAT_VYUY, DRM_FORMAT_YUYV, DRM_FORMAT_YVYU,
+       DRM_FORMAT_YUV420, DRM_FORMAT_YVU420, DRM_FORMAT_YUV422,
+       DRM_FORMAT_YUV444,
+};
 
-       if (ctx->id < 0) {
-               dev_err(ctx->ippdrv.dev, "failed to get node alias id.\n");
-               return -EINVAL;
-       }
+static const struct drm_exynos_ipp_limit fimc_4210_limits_v1[] = {
+       { IPP_SIZE_LIMIT(BUFFER, .h = { 32, 8192, 8 }, .v = { 32, 8192, 2 }) },
+       { IPP_SIZE_LIMIT(AREA, .h = { 32, 4224, 2 }, .v = { 32, 0, 2 }) },
+       { IPP_SIZE_LIMIT(ROTATED, .h = { 128, 1920 }, .v = { 128, 0 }) },
+       { IPP_SCALE_LIMIT(.h = { (1 << 16) / 64, (1 << 16) * 64 },
+                         .v = { (1 << 16) / 64, (1 << 16) * 64 }) },
+};
 
-       return 0;
-}
+static const struct drm_exynos_ipp_limit fimc_4210_limits_v2[] = {
+       { IPP_SIZE_LIMIT(BUFFER, .h = { 32, 8192, 8 }, .v = { 32, 8192, 2 }) },
+       { IPP_SIZE_LIMIT(AREA, .h = { 32, 1920, 2 }, .v = { 32, 0, 2 }) },
+       { IPP_SIZE_LIMIT(ROTATED, .h = { 128, 1366 }, .v = { 128, 0 }) },
+       { IPP_SCALE_LIMIT(.h = { (1 << 16) / 64, (1 << 16) * 64 },
+                         .v = { (1 << 16) / 64, (1 << 16) * 64 }) },
+};
 
 static int fimc_probe(struct platform_device *pdev)
 {
+       const struct drm_exynos_ipp_limit *limits;
+       struct exynos_drm_ipp_formats *formats;
        struct device *dev = &pdev->dev;
        struct fimc_context *ctx;
        struct resource *res;
-       struct exynos_drm_ippdrv *ippdrv;
        int ret;
+       int i, num_limits;
 
-       if (!dev->of_node) {
-               dev_err(dev, "device tree node not found.\n");
+       if (exynos_drm_check_fimc_device(dev) != 0)
                return -ENODEV;
-       }
 
        ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
        if (!ctx)
                return -ENOMEM;
 
-       ctx->ippdrv.dev = dev;
+       ctx->dev = dev;
+       ctx->id = of_alias_get_id(dev->of_node, "fimc");
 
-       ret = fimc_parse_dt(ctx);
-       if (ret < 0)
-               return ret;
+       /* construct formats/limits array */
+       formats = devm_kzalloc(dev, sizeof(*formats) *
+                              (ARRAY_SIZE(fimc_formats)), GFP_KERNEL);
+       if (!formats)
+               return -ENOMEM;
 
-       ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
-                                               "samsung,sysreg");
-       if (IS_ERR(ctx->sysreg)) {
-               dev_err(dev, "syscon regmap lookup failed.\n");
-               return PTR_ERR(ctx->sysreg);
+       if (ctx->id < 3) {
+               limits = fimc_4210_limits_v1;
+               num_limits = ARRAY_SIZE(fimc_4210_limits_v1);
+       } else {
+               limits = fimc_4210_limits_v2;
+               num_limits = ARRAY_SIZE(fimc_4210_limits_v2);
        }
+       for (i = 0; i < ARRAY_SIZE(fimc_formats); i++) {
+               formats[i].fourcc = fimc_formats[i];
+               formats[i].type = DRM_EXYNOS_IPP_FORMAT_SOURCE |
+                                 DRM_EXYNOS_IPP_FORMAT_DESTINATION;
+               formats[i].limits = limits;
+               formats[i].num_limits = num_limits;
+       }
+       ctx->formats = formats;
+       ctx->num_formats = ARRAY_SIZE(fimc_formats);
 
        /* resource memory */
        ctx->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1820,9 +1281,8 @@ static int fimc_probe(struct platform_device *pdev)
                return -ENOENT;
        }
 
-       ctx->irq = res->start;
-       ret = devm_request_threaded_irq(dev, ctx->irq, NULL, fimc_irq_handler,
-               IRQF_ONESHOT, "drm_fimc", ctx);
+       ret = devm_request_irq(dev, res->start, fimc_irq_handler,
+               0, dev_name(dev), ctx);
        if (ret < 0) {
                dev_err(dev, "failed to request irq.\n");
                return ret;
@@ -1832,39 +1292,24 @@ static int fimc_probe(struct platform_device *pdev)
        if (ret < 0)
                return ret;
 
-       ippdrv = &ctx->ippdrv;
-       ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &fimc_src_ops;
-       ippdrv->ops[EXYNOS_DRM_OPS_DST] = &fimc_dst_ops;
-       ippdrv->check_property = fimc_ippdrv_check_property;
-       ippdrv->reset = fimc_ippdrv_reset;
-       ippdrv->start = fimc_ippdrv_start;
-       ippdrv->stop = fimc_ippdrv_stop;
-       ret = fimc_init_prop_list(ippdrv);
-       if (ret < 0) {
-               dev_err(dev, "failed to init property list.\n");
-               goto err_put_clk;
-       }
-
-       DRM_DEBUG_KMS("id[%d]ippdrv[%p]\n", ctx->id, ippdrv);
-
        spin_lock_init(&ctx->lock);
        platform_set_drvdata(pdev, ctx);
 
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_set_autosuspend_delay(dev, FIMC_AUTOSUSPEND_DELAY);
        pm_runtime_enable(dev);
 
-       ret = exynos_drm_ippdrv_register(ippdrv);
-       if (ret < 0) {
-               dev_err(dev, "failed to register drm fimc device.\n");
+       ret = component_add(dev, &fimc_component_ops);
+       if (ret)
                goto err_pm_dis;
-       }
 
        dev_info(dev, "drm fimc registered successfully.\n");
 
        return 0;
 
 err_pm_dis:
+       pm_runtime_dont_use_autosuspend(dev);
        pm_runtime_disable(dev);
-err_put_clk:
        fimc_put_clocks(ctx);
 
        return ret;
@@ -1874,42 +1319,15 @@ static int fimc_remove(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct fimc_context *ctx = get_fimc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
 
-       exynos_drm_ippdrv_unregister(ippdrv);
-
-       fimc_put_clocks(ctx);
-       pm_runtime_set_suspended(dev);
+       component_del(dev, &fimc_component_ops);
+       pm_runtime_dont_use_autosuspend(dev);
        pm_runtime_disable(dev);
 
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int fimc_suspend(struct device *dev)
-{
-       struct fimc_context *ctx = get_fimc_context(dev);
-
-       DRM_DEBUG_KMS("id[%d]\n", ctx->id);
-
-       if (pm_runtime_suspended(dev))
-               return 0;
-
-       return fimc_clk_ctrl(ctx, false);
-}
-
-static int fimc_resume(struct device *dev)
-{
-       struct fimc_context *ctx = get_fimc_context(dev);
-
-       DRM_DEBUG_KMS("id[%d]\n", ctx->id);
-
-       if (!pm_runtime_suspended(dev))
-               return fimc_clk_ctrl(ctx, true);
+       fimc_put_clocks(ctx);
 
        return 0;
 }
-#endif
 
 #ifdef CONFIG_PM
 static int fimc_runtime_suspend(struct device *dev)
@@ -1917,8 +1335,8 @@ static int fimc_runtime_suspend(struct device *dev)
        struct fimc_context *ctx = get_fimc_context(dev);
 
        DRM_DEBUG_KMS("id[%d]\n", ctx->id);
-
-       return  fimc_clk_ctrl(ctx, false);
+       clk_disable_unprepare(ctx->clocks[FIMC_CLK_GATE]);
+       return 0;
 }
 
 static int fimc_runtime_resume(struct device *dev)
@@ -1926,13 +1344,13 @@ static int fimc_runtime_resume(struct device *dev)
        struct fimc_context *ctx = get_fimc_context(dev);
 
        DRM_DEBUG_KMS("id[%d]\n", ctx->id);
-
-       return  fimc_clk_ctrl(ctx, true);
+       return clk_prepare_enable(ctx->clocks[FIMC_CLK_GATE]);
 }
 #endif
 
 static const struct dev_pm_ops fimc_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume)
+       SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                               pm_runtime_force_resume)
        SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL)
 };
 
@@ -1953,4 +1371,3 @@ struct platform_driver fimc_driver = {
                .pm     = &fimc_pm_ops,
        },
 };
-
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.h b/drivers/gpu/drm/exynos/exynos_drm_fimc.h
deleted file mode 100644 (file)
index 127a424..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (c) 2012 Samsung Electronics Co., Ltd.
- *
- * Authors:
- *     Eunchul Kim <chulspro.kim@samsung.com>
- *     Jinyoung Jeon <jy0.jeon@samsung.com>
- *     Sangmin Lee <lsmin.lee@samsung.com>
- *
- * 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;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#ifndef _EXYNOS_DRM_FIMC_H_
-#define _EXYNOS_DRM_FIMC_H_
-
-/*
- * TODO
- * FIMD output interface notifier callback.
- */
-
-#endif /* _EXYNOS_DRM_FIMC_H_ */
index afbb3f1e0c590be4f0c3819c3006445cdc88770b..1fdb20d2a9f4a75241ebec763338834b6a402867 100644 (file)
  *
  */
 #include <linux/kernel.h>
+#include <linux/component.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/pm_runtime.h>
 #include <linux/mfd/syscon.h>
+#include <linux/of_device.h>
 #include <linux/regmap.h>
 
 #include <drm/drmP.h>
 #include <drm/exynos_drm.h>
 #include "regs-gsc.h"
 #include "exynos_drm_drv.h"
+#include "exynos_drm_iommu.h"
 #include "exynos_drm_ipp.h"
-#include "exynos_drm_gsc.h"
 
 /*
  * GSC stands for General SCaler and
  * input DMA reads image data from the memory.
  * output DMA writes image data to memory.
  * GSC supports image rotation and image effect functions.
- *
- * M2M operation : supports crop/scale/rotation/csc so on.
- * Memory ----> GSC H/W ----> Memory.
- * Writeback operation : supports cloned screen with FIMD.
- * FIMD ----> GSC H/W ----> Memory.
- * Output operation : supports direct display using local path.
- * Memory ----> GSC H/W ----> FIMD, Mixer.
  */
 
-/*
- * TODO
- * 1. check suspend/resume api if needed.
- * 2. need to check use case platform_device_id.
- * 3. check src/dst size with, height.
- * 4. added check_prepare api for right register.
- * 5. need to add supported list in prop_list.
- * 6. check prescaler/scaler optimization.
- */
 
-#define GSC_MAX_DEVS   4
 #define GSC_MAX_CLOCKS 8
 #define GSC_MAX_SRC            4
 #define GSC_MAX_DST            16
@@ -66,8 +51,6 @@
 #define GSC_SC_DOWN_RATIO_4_8          131072
 #define GSC_SC_DOWN_RATIO_3_8          174762
 #define GSC_SC_DOWN_RATIO_2_8          262144
-#define GSC_REFRESH_MIN        12
-#define GSC_REFRESH_MAX        60
 #define GSC_CROP_MAX   8192
 #define GSC_CROP_MIN   32
 #define GSC_SCALE_MAX  4224
 #define GSC_COEF_H_8T  8
 #define GSC_COEF_V_4T  4
 #define GSC_COEF_DEPTH 3
+#define GSC_AUTOSUSPEND_DELAY          2000
 
 #define get_gsc_context(dev)   platform_get_drvdata(to_platform_device(dev))
-#define get_ctx_from_ippdrv(ippdrv)    container_of(ippdrv,\
-                                       struct gsc_context, ippdrv);
 #define gsc_read(offset)               readl(ctx->regs + (offset))
 #define gsc_write(cfg, offset) writel(cfg, ctx->regs + (offset))
 
@@ -104,45 +86,27 @@ struct gsc_scaler {
        unsigned long main_vratio;
 };
 
-/*
- * A structure of scaler capability.
- *
- * find user manual 49.2 features.
- * @tile_w: tile mode or rotation width.
- * @tile_h: tile mode or rotation height.
- * @w: other cases width.
- * @h: other cases height.
- */
-struct gsc_capability {
-       /* tile or rotation */
-       u32     tile_w;
-       u32     tile_h;
-       /* other cases */
-       u32     w;
-       u32     h;
-};
-
 /*
  * A structure of gsc context.
  *
- * @ippdrv: prepare initialization using ippdrv.
  * @regs_res: register resources.
  * @regs: memory mapped io registers.
- * @sysreg: handle to SYSREG block regmap.
- * @lock: locking of operations.
  * @gsc_clk: gsc gate clock.
  * @sc: scaler infomations.
  * @id: gsc id.
  * @irq: irq number.
  * @rotation: supports rotation of src.
- * @suspended: qos operations.
  */
 struct gsc_context {
-       struct exynos_drm_ippdrv        ippdrv;
+       struct exynos_drm_ipp ipp;
+       struct drm_device *drm_dev;
+       struct device   *dev;
+       struct exynos_drm_ipp_task      *task;
+       struct exynos_drm_ipp_formats   *formats;
+       unsigned int                    num_formats;
+
        struct resource *regs_res;
        void __iomem    *regs;
-       struct regmap   *sysreg;
-       struct mutex    lock;
        const char      **clk_names;
        struct clk      *clocks[GSC_MAX_CLOCKS];
        int             num_clocks;
@@ -150,16 +114,18 @@ struct gsc_context {
        int     id;
        int     irq;
        bool    rotation;
-       bool    suspended;
 };
 
 /**
  * struct gsc_driverdata - per device type driver data for init time.
  *
+ * @limits: picture size limits array
  * @clk_names: names of clocks needed by this variant
  * @num_clocks: the number of clocks needed by this variant
  */
 struct gsc_driverdata {
+       const struct drm_exynos_ipp_limit *limits;
+       int             num_limits;
        const char      *clk_names[GSC_MAX_CLOCKS];
        int             num_clocks;
 };
@@ -452,25 +418,6 @@ static int gsc_sw_reset(struct gsc_context *ctx)
        return 0;
 }
 
-static void gsc_set_gscblk_fimd_wb(struct gsc_context *ctx, bool enable)
-{
-       unsigned int gscblk_cfg;
-
-       if (!ctx->sysreg)
-               return;
-
-       regmap_read(ctx->sysreg, SYSREG_GSCBLK_CFG1, &gscblk_cfg);
-
-       if (enable)
-               gscblk_cfg |= GSC_BLK_DISP1WB_DEST(ctx->id) |
-                               GSC_BLK_GSCL_WB_IN_SRC_SEL(ctx->id) |
-                               GSC_BLK_SW_RESET_WB_DEST(ctx->id);
-       else
-               gscblk_cfg |= GSC_BLK_PXLASYNC_LO_MASK_WB(ctx->id);
-
-       regmap_write(ctx->sysreg, SYSREG_GSCBLK_CFG1, gscblk_cfg);
-}
-
 static void gsc_handle_irq(struct gsc_context *ctx, bool enable,
                bool overflow, bool done)
 {
@@ -500,103 +447,9 @@ static void gsc_handle_irq(struct gsc_context *ctx, bool enable,
        gsc_write(cfg, GSC_IRQ);
 }
 
-static int gsc_set_planar_addr(struct drm_exynos_ipp_buf_info *buf_info,
-               u32 fmt, struct drm_exynos_sz *sz)
-{
-       dma_addr_t *base[EXYNOS_DRM_PLANAR_MAX];
-       uint64_t size[EXYNOS_DRM_PLANAR_MAX];
-       uint64_t ofs[EXYNOS_DRM_PLANAR_MAX];
-       bool bypass = false;
-       uint64_t tsize = 0;
-       int i;
-
-       for_each_ipp_planar(i) {
-               base[i] = &buf_info->base[i];
-               size[i] = buf_info->size[i];
-               ofs[i] = 0;
-               tsize += size[i];
-               DRM_DEBUG_KMS("base[%d][0x%lx]s[%d][%llu]\n",
-                               i, (unsigned long)*base[i], i, size[i]);
-       }
-
-       if (!tsize) {
-               DRM_INFO("failed to get buffer size.\n");
-               return 0;
-       }
-
-       switch (fmt) {
-       case DRM_FORMAT_NV12:
-       case DRM_FORMAT_NV21:
-       case DRM_FORMAT_NV16:
-       case DRM_FORMAT_NV61:
-               ofs[0] = sz->hsize * sz->vsize;
-               ofs[1] = ofs[0] >> 1;
-               if (size[0] + size[1] < ofs[0] + ofs[1])
-                       goto err_info;
-
-               if (*base[0] && *base[1])
-                       bypass = true;
-               break;
-       case DRM_FORMAT_YUV410:
-       case DRM_FORMAT_YVU410:
-       case DRM_FORMAT_YUV411:
-       case DRM_FORMAT_YVU411:
-       case DRM_FORMAT_YUV420:
-       case DRM_FORMAT_YVU420:
-       case DRM_FORMAT_YUV422:
-       case DRM_FORMAT_YVU422:
-       case DRM_FORMAT_YUV444:
-       case DRM_FORMAT_YVU444:
-               ofs[0] = sz->hsize * sz->vsize;
-               ofs[1] = ofs[2] = ofs[0] >> 2;
-               if (size[0]+size[1]+size[2] < ofs[0]+ofs[1]+ofs[2])
-                       goto err_info;
-
-               if (*base[0] && *base[1] && *base[2])
-                       bypass = true;
-       break;
-       case DRM_FORMAT_ARGB8888:
-       case DRM_FORMAT_XRGB8888:
-               ofs[0] = sz->hsize * sz->vsize << 2;
-               if (size[0] < ofs[0])
-                       goto err_info;
-
-               bypass = true;
-               break;
-       default:
-               bypass = true;
-               break;
-       }
-
-       if (!bypass) {
-               *base[1] = *base[0] + ofs[0];
-               if (ofs[1] && ofs[2])
-                       *base[2] = *base[1] + ofs[1];
-       }
-
-       DRM_DEBUG_KMS("y[0x%lx],cb[0x%lx],cr[0x%lx]\n", (unsigned long)*base[0],
-                       (unsigned long)*base[1], (unsigned long)*base[2]);
-
-       return 0;
-
-err_info:
-       DRM_ERROR("invalid size for fmt[0x%x]\n", fmt);
-
-       for_each_ipp_planar(i) {
-               base[i] = &buf_info->base[i];
-               size[i] = buf_info->size[i];
 
-               DRM_ERROR("base[%d][0x%lx]s[%d][%llu]ofs[%d][%llu]\n",
-                       i, (unsigned long)*base[i], i, size[i], i, ofs[i]);
-       }
-
-       return -EINVAL;
-}
-
-static int gsc_src_set_fmt(struct device *dev, u32 fmt)
+static void gsc_src_set_fmt(struct gsc_context *ctx, u32 fmt)
 {
-       struct gsc_context *ctx = get_gsc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
 
        DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
@@ -611,7 +464,6 @@ static int gsc_src_set_fmt(struct device *dev, u32 fmt)
        case DRM_FORMAT_RGB565:
                cfg |= GSC_IN_RGB565;
                break;
-       case DRM_FORMAT_ARGB8888:
        case DRM_FORMAT_XRGB8888:
                cfg |= GSC_IN_XRGB8888;
                break;
@@ -655,115 +507,85 @@ static int gsc_src_set_fmt(struct device *dev, u32 fmt)
                cfg |= (GSC_IN_CHROMA_ORDER_CBCR |
                        GSC_IN_YUV420_2P);
                break;
-       default:
-               dev_err(ippdrv->dev, "invalid target yuv order 0x%x.\n", fmt);
-               return -EINVAL;
        }
 
        gsc_write(cfg, GSC_IN_CON);
-
-       return 0;
 }
 
-static int gsc_src_set_transf(struct device *dev,
-               enum drm_exynos_degree degree,
-               enum drm_exynos_flip flip, bool *swap)
+static void gsc_src_set_transf(struct gsc_context *ctx, unsigned int rotation)
 {
-       struct gsc_context *ctx = get_gsc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
+       unsigned int degree = rotation & DRM_MODE_ROTATE_MASK;
        u32 cfg;
 
-       DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip);
-
        cfg = gsc_read(GSC_IN_CON);
        cfg &= ~GSC_IN_ROT_MASK;
 
        switch (degree) {
-       case EXYNOS_DRM_DEGREE_0:
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+       case DRM_MODE_ROTATE_0:
+               if (rotation & DRM_MODE_REFLECT_Y)
                        cfg |= GSC_IN_ROT_XFLIP;
-               if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+               if (rotation & DRM_MODE_REFLECT_X)
                        cfg |= GSC_IN_ROT_YFLIP;
                break;
-       case EXYNOS_DRM_DEGREE_90:
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+       case DRM_MODE_ROTATE_90:
+               if (rotation & DRM_MODE_REFLECT_Y)
                        cfg |= GSC_IN_ROT_90_XFLIP;
-               else if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+               else if (rotation & DRM_MODE_REFLECT_X)
                        cfg |= GSC_IN_ROT_90_YFLIP;
                else
                        cfg |= GSC_IN_ROT_90;
                break;
-       case EXYNOS_DRM_DEGREE_180:
+       case DRM_MODE_ROTATE_180:
                cfg |= GSC_IN_ROT_180;
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+               if (rotation & DRM_MODE_REFLECT_Y)
                        cfg &= ~GSC_IN_ROT_XFLIP;
-               if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+               if (rotation & DRM_MODE_REFLECT_X)
                        cfg &= ~GSC_IN_ROT_YFLIP;
                break;
-       case EXYNOS_DRM_DEGREE_270:
+       case DRM_MODE_ROTATE_270:
                cfg |= GSC_IN_ROT_270;
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+               if (rotation & DRM_MODE_REFLECT_Y)
                        cfg &= ~GSC_IN_ROT_XFLIP;
-               if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+               if (rotation & DRM_MODE_REFLECT_X)
                        cfg &= ~GSC_IN_ROT_YFLIP;
                break;
-       default:
-               dev_err(ippdrv->dev, "invalid degree value %d.\n", degree);
-               return -EINVAL;
        }
 
        gsc_write(cfg, GSC_IN_CON);
 
        ctx->rotation = (cfg & GSC_IN_ROT_90) ? 1 : 0;
-       *swap = ctx->rotation;
-
-       return 0;
 }
 
-static int gsc_src_set_size(struct device *dev, int swap,
-               struct drm_exynos_pos *pos, struct drm_exynos_sz *sz)
+static void gsc_src_set_size(struct gsc_context *ctx,
+                            struct exynos_drm_ipp_buffer *buf)
 {
-       struct gsc_context *ctx = get_gsc_context(dev);
-       struct drm_exynos_pos img_pos = *pos;
        struct gsc_scaler *sc = &ctx->sc;
        u32 cfg;
 
-       DRM_DEBUG_KMS("swap[%d]x[%d]y[%d]w[%d]h[%d]\n",
-               swap, pos->x, pos->y, pos->w, pos->h);
-
-       if (swap) {
-               img_pos.w = pos->h;
-               img_pos.h = pos->w;
-       }
-
        /* pixel offset */
-       cfg = (GSC_SRCIMG_OFFSET_X(img_pos.x) |
-               GSC_SRCIMG_OFFSET_Y(img_pos.y));
+       cfg = (GSC_SRCIMG_OFFSET_X(buf->rect.x) |
+               GSC_SRCIMG_OFFSET_Y(buf->rect.y));
        gsc_write(cfg, GSC_SRCIMG_OFFSET);
 
        /* cropped size */
-       cfg = (GSC_CROPPED_WIDTH(img_pos.w) |
-               GSC_CROPPED_HEIGHT(img_pos.h));
+       cfg = (GSC_CROPPED_WIDTH(buf->rect.w) |
+               GSC_CROPPED_HEIGHT(buf->rect.h));
        gsc_write(cfg, GSC_CROPPED_SIZE);
 
-       DRM_DEBUG_KMS("hsize[%d]vsize[%d]\n", sz->hsize, sz->vsize);
-
        /* original size */
        cfg = gsc_read(GSC_SRCIMG_SIZE);
        cfg &= ~(GSC_SRCIMG_HEIGHT_MASK |
                GSC_SRCIMG_WIDTH_MASK);
 
-       cfg |= (GSC_SRCIMG_WIDTH(sz->hsize) |
-               GSC_SRCIMG_HEIGHT(sz->vsize));
+       cfg |= (GSC_SRCIMG_WIDTH(buf->buf.width) |
+               GSC_SRCIMG_HEIGHT(buf->buf.height));
 
        gsc_write(cfg, GSC_SRCIMG_SIZE);
 
        cfg = gsc_read(GSC_IN_CON);
        cfg &= ~GSC_IN_RGB_TYPE_MASK;
 
-       DRM_DEBUG_KMS("width[%d]range[%d]\n", pos->w, sc->range);
-
-       if (pos->w >= GSC_WIDTH_ITU_709)
+       if (buf->rect.w >= GSC_WIDTH_ITU_709)
                if (sc->range)
                        cfg |= GSC_IN_RGB_HD_WIDE;
                else
@@ -775,116 +597,39 @@ static int gsc_src_set_size(struct device *dev, int swap,
                        cfg |= GSC_IN_RGB_SD_NARROW;
 
        gsc_write(cfg, GSC_IN_CON);
-
-       return 0;
 }
 
-static int gsc_src_set_buf_seq(struct gsc_context *ctx, u32 buf_id,
-               enum drm_exynos_ipp_buf_type buf_type)
+static void gsc_src_set_buf_seq(struct gsc_context *ctx, u32 buf_id,
+                              bool enqueue)
 {
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       bool masked;
+       bool masked = !enqueue;
        u32 cfg;
        u32 mask = 0x00000001 << buf_id;
 
-       DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type);
-
        /* mask register set */
        cfg = gsc_read(GSC_IN_BASE_ADDR_Y_MASK);
 
-       switch (buf_type) {
-       case IPP_BUF_ENQUEUE:
-               masked = false;
-               break;
-       case IPP_BUF_DEQUEUE:
-               masked = true;
-               break;
-       default:
-               dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n");
-               return -EINVAL;
-       }
-
        /* sequence id */
        cfg &= ~mask;
        cfg |= masked << buf_id;
        gsc_write(cfg, GSC_IN_BASE_ADDR_Y_MASK);
        gsc_write(cfg, GSC_IN_BASE_ADDR_CB_MASK);
        gsc_write(cfg, GSC_IN_BASE_ADDR_CR_MASK);
-
-       return 0;
 }
 
-static int gsc_src_set_addr(struct device *dev,
-               struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id,
-               enum drm_exynos_ipp_buf_type buf_type)
+static void gsc_src_set_addr(struct gsc_context *ctx, u32 buf_id,
+                           struct exynos_drm_ipp_buffer *buf)
 {
-       struct gsc_context *ctx = get_gsc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node;
-       struct drm_exynos_ipp_property *property;
-       struct drm_exynos_ipp_config *config;
-       struct drm_exynos_sz sz;
-       int ret;
-
-       if (!c_node) {
-               DRM_ERROR("failed to get c_node.\n");
-               return -EFAULT;
-       }
-
-       property = &c_node->property;
-
-       DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n",
-               property->prop_id, buf_id, buf_type);
-
-       buf_id = 0;
-
        /* address register set */
-       switch (buf_type) {
-       case IPP_BUF_ENQUEUE:
-               config = &property->config[EXYNOS_DRM_OPS_SRC];
-               /*
-                * The resolution is adjusted to a multiple of 16
-                * to see if an overflow has occurred.
-                */
-               sz.hsize = ALIGN(config->pos.w, 16);
-               sz.vsize = ALIGN(config->pos.h, 16);
-               ret = gsc_set_planar_addr(buf_info, config->fmt, &sz);
-               if (ret) {
-                       dev_err(dev, "failed to set plane src addr.\n");
-                       return ret;
-               }
+       gsc_write(buf->dma_addr[0], GSC_IN_BASE_ADDR_Y(buf_id));
+       gsc_write(buf->dma_addr[1], GSC_IN_BASE_ADDR_CB(buf_id));
+       gsc_write(buf->dma_addr[2], GSC_IN_BASE_ADDR_CR(buf_id));
 
-               gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y],
-                       GSC_IN_BASE_ADDR_Y(buf_id));
-               gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB],
-                       GSC_IN_BASE_ADDR_CB(buf_id));
-               gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR],
-                       GSC_IN_BASE_ADDR_CR(buf_id));
-               break;
-       case IPP_BUF_DEQUEUE:
-               gsc_write(0x0, GSC_IN_BASE_ADDR_Y(buf_id));
-               gsc_write(0x0, GSC_IN_BASE_ADDR_CB(buf_id));
-               gsc_write(0x0, GSC_IN_BASE_ADDR_CR(buf_id));
-               break;
-       default:
-               /* bypass */
-               break;
-       }
-
-       return gsc_src_set_buf_seq(ctx, buf_id, buf_type);
+       gsc_src_set_buf_seq(ctx, buf_id, true);
 }
 
-static struct exynos_drm_ipp_ops gsc_src_ops = {
-       .set_fmt = gsc_src_set_fmt,
-       .set_transf = gsc_src_set_transf,
-       .set_size = gsc_src_set_size,
-       .set_addr = gsc_src_set_addr,
-};
-
-static int gsc_dst_set_fmt(struct device *dev, u32 fmt)
+static void gsc_dst_set_fmt(struct gsc_context *ctx, u32 fmt)
 {
-       struct gsc_context *ctx = get_gsc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
 
        DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
@@ -899,9 +644,8 @@ static int gsc_dst_set_fmt(struct device *dev, u32 fmt)
        case DRM_FORMAT_RGB565:
                cfg |= GSC_OUT_RGB565;
                break;
-       case DRM_FORMAT_ARGB8888:
        case DRM_FORMAT_XRGB8888:
-               cfg |= (GSC_OUT_XRGB8888 | GSC_OUT_GLOBAL_ALPHA(0xff));
+               cfg |= GSC_OUT_XRGB8888;
                break;
        case DRM_FORMAT_BGRX8888:
                cfg |= (GSC_OUT_XRGB8888 | GSC_OUT_RB_SWAP);
@@ -940,69 +684,9 @@ static int gsc_dst_set_fmt(struct device *dev, u32 fmt)
                cfg |= (GSC_OUT_CHROMA_ORDER_CBCR |
                        GSC_OUT_YUV420_2P);
                break;
-       default:
-               dev_err(ippdrv->dev, "invalid target yuv order 0x%x.\n", fmt);
-               return -EINVAL;
        }
 
        gsc_write(cfg, GSC_OUT_CON);
-
-       return 0;
-}
-
-static int gsc_dst_set_transf(struct device *dev,
-               enum drm_exynos_degree degree,
-               enum drm_exynos_flip flip, bool *swap)
-{
-       struct gsc_context *ctx = get_gsc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       u32 cfg;
-
-       DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip);
-
-       cfg = gsc_read(GSC_IN_CON);
-       cfg &= ~GSC_IN_ROT_MASK;
-
-       switch (degree) {
-       case EXYNOS_DRM_DEGREE_0:
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
-                       cfg |= GSC_IN_ROT_XFLIP;
-               if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
-                       cfg |= GSC_IN_ROT_YFLIP;
-               break;
-       case EXYNOS_DRM_DEGREE_90:
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
-                       cfg |= GSC_IN_ROT_90_XFLIP;
-               else if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
-                       cfg |= GSC_IN_ROT_90_YFLIP;
-               else
-                       cfg |= GSC_IN_ROT_90;
-               break;
-       case EXYNOS_DRM_DEGREE_180:
-               cfg |= GSC_IN_ROT_180;
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
-                       cfg &= ~GSC_IN_ROT_XFLIP;
-               if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
-                       cfg &= ~GSC_IN_ROT_YFLIP;
-               break;
-       case EXYNOS_DRM_DEGREE_270:
-               cfg |= GSC_IN_ROT_270;
-               if (flip & EXYNOS_DRM_FLIP_VERTICAL)
-                       cfg &= ~GSC_IN_ROT_XFLIP;
-               if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
-                       cfg &= ~GSC_IN_ROT_YFLIP;
-               break;
-       default:
-               dev_err(ippdrv->dev, "invalid degree value %d.\n", degree);
-               return -EINVAL;
-       }
-
-       gsc_write(cfg, GSC_IN_CON);
-
-       ctx->rotation = (cfg & GSC_IN_ROT_90) ? 1 : 0;
-       *swap = ctx->rotation;
-
-       return 0;
 }
 
 static int gsc_get_ratio_shift(u32 src, u32 dst, u32 *ratio)
@@ -1040,9 +724,9 @@ static void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *shfactor)
 }
 
 static int gsc_set_prescaler(struct gsc_context *ctx, struct gsc_scaler *sc,
-               struct drm_exynos_pos *src, struct drm_exynos_pos *dst)
+                            struct drm_exynos_ipp_task_rect *src,
+                            struct drm_exynos_ipp_task_rect *dst)
 {
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
        u32 src_w, src_h, dst_w, dst_h;
        int ret = 0;
@@ -1060,13 +744,13 @@ static int gsc_set_prescaler(struct gsc_context *ctx, struct gsc_scaler *sc,
 
        ret = gsc_get_ratio_shift(src_w, dst_w, &sc->pre_hratio);
        if (ret) {
-               dev_err(ippdrv->dev, "failed to get ratio horizontal.\n");
+               dev_err(ctx->dev, "failed to get ratio horizontal.\n");
                return ret;
        }
 
        ret = gsc_get_ratio_shift(src_h, dst_h, &sc->pre_vratio);
        if (ret) {
-               dev_err(ippdrv->dev, "failed to get ratio vertical.\n");
+               dev_err(ctx->dev, "failed to get ratio vertical.\n");
                return ret;
        }
 
@@ -1160,47 +844,37 @@ static void gsc_set_scaler(struct gsc_context *ctx, struct gsc_scaler *sc)
        gsc_write(cfg, GSC_MAIN_V_RATIO);
 }
 
-static int gsc_dst_set_size(struct device *dev, int swap,
-               struct drm_exynos_pos *pos, struct drm_exynos_sz *sz)
+static void gsc_dst_set_size(struct gsc_context *ctx,
+                            struct exynos_drm_ipp_buffer *buf)
 {
-       struct gsc_context *ctx = get_gsc_context(dev);
-       struct drm_exynos_pos img_pos = *pos;
        struct gsc_scaler *sc = &ctx->sc;
        u32 cfg;
 
-       DRM_DEBUG_KMS("swap[%d]x[%d]y[%d]w[%d]h[%d]\n",
-               swap, pos->x, pos->y, pos->w, pos->h);
-
-       if (swap) {
-               img_pos.w = pos->h;
-               img_pos.h = pos->w;
-       }
-
        /* pixel offset */
-       cfg = (GSC_DSTIMG_OFFSET_X(pos->x) |
-               GSC_DSTIMG_OFFSET_Y(pos->y));
+       cfg = (GSC_DSTIMG_OFFSET_X(buf->rect.x) |
+               GSC_DSTIMG_OFFSET_Y(buf->rect.y));
        gsc_write(cfg, GSC_DSTIMG_OFFSET);
 
        /* scaled size */
-       cfg = (GSC_SCALED_WIDTH(img_pos.w) | GSC_SCALED_HEIGHT(img_pos.h));
+       if (ctx->rotation)
+               cfg = (GSC_SCALED_WIDTH(buf->rect.h) |
+                      GSC_SCALED_HEIGHT(buf->rect.w));
+       else
+               cfg = (GSC_SCALED_WIDTH(buf->rect.w) |
+                      GSC_SCALED_HEIGHT(buf->rect.h));
        gsc_write(cfg, GSC_SCALED_SIZE);
 
-       DRM_DEBUG_KMS("hsize[%d]vsize[%d]\n", sz->hsize, sz->vsize);
-
        /* original size */
        cfg = gsc_read(GSC_DSTIMG_SIZE);
-       cfg &= ~(GSC_DSTIMG_HEIGHT_MASK |
-               GSC_DSTIMG_WIDTH_MASK);
-       cfg |= (GSC_DSTIMG_WIDTH(sz->hsize) |
-               GSC_DSTIMG_HEIGHT(sz->vsize));
+       cfg &= ~(GSC_DSTIMG_HEIGHT_MASK | GSC_DSTIMG_WIDTH_MASK);
+       cfg |= GSC_DSTIMG_WIDTH(buf->buf.width) |
+              GSC_DSTIMG_HEIGHT(buf->buf.height);
        gsc_write(cfg, GSC_DSTIMG_SIZE);
 
        cfg = gsc_read(GSC_OUT_CON);
        cfg &= ~GSC_OUT_RGB_TYPE_MASK;
 
-       DRM_DEBUG_KMS("width[%d]range[%d]\n", pos->w, sc->range);
-
-       if (pos->w >= GSC_WIDTH_ITU_709)
+       if (buf->rect.w >= GSC_WIDTH_ITU_709)
                if (sc->range)
                        cfg |= GSC_OUT_RGB_HD_WIDE;
                else
@@ -1212,8 +886,6 @@ static int gsc_dst_set_size(struct device *dev, int swap,
                        cfg |= GSC_OUT_RGB_SD_NARROW;
 
        gsc_write(cfg, GSC_OUT_CON);
-
-       return 0;
 }
 
 static int gsc_dst_get_buf_seq(struct gsc_context *ctx)
@@ -1232,35 +904,16 @@ static int gsc_dst_get_buf_seq(struct gsc_context *ctx)
        return buf_num;
 }
 
-static int gsc_dst_set_buf_seq(struct gsc_context *ctx, u32 buf_id,
-               enum drm_exynos_ipp_buf_type buf_type)
+static void gsc_dst_set_buf_seq(struct gsc_context *ctx, u32 buf_id,
+                               bool enqueue)
 {
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       bool masked;
+       bool masked = !enqueue;
        u32 cfg;
        u32 mask = 0x00000001 << buf_id;
-       int ret = 0;
-
-       DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type);
-
-       mutex_lock(&ctx->lock);
 
        /* mask register set */
        cfg = gsc_read(GSC_OUT_BASE_ADDR_Y_MASK);
 
-       switch (buf_type) {
-       case IPP_BUF_ENQUEUE:
-               masked = false;
-               break;
-       case IPP_BUF_DEQUEUE:
-               masked = true;
-               break;
-       default:
-               dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n");
-               ret =  -EINVAL;
-               goto err_unlock;
-       }
-
        /* sequence id */
        cfg &= ~mask;
        cfg |= masked << buf_id;
@@ -1269,372 +922,137 @@ static int gsc_dst_set_buf_seq(struct gsc_context *ctx, u32 buf_id,
        gsc_write(cfg, GSC_OUT_BASE_ADDR_CR_MASK);
 
        /* interrupt enable */
-       if (buf_type == IPP_BUF_ENQUEUE &&
-           gsc_dst_get_buf_seq(ctx) >= GSC_BUF_START)
+       if (enqueue && gsc_dst_get_buf_seq(ctx) >= GSC_BUF_START)
                gsc_handle_irq(ctx, true, false, true);
 
        /* interrupt disable */
-       if (buf_type == IPP_BUF_DEQUEUE &&
-           gsc_dst_get_buf_seq(ctx) <= GSC_BUF_STOP)
+       if (!enqueue && gsc_dst_get_buf_seq(ctx) <= GSC_BUF_STOP)
                gsc_handle_irq(ctx, false, false, true);
-
-err_unlock:
-       mutex_unlock(&ctx->lock);
-       return ret;
 }
 
-static int gsc_dst_set_addr(struct device *dev,
-               struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id,
-               enum drm_exynos_ipp_buf_type buf_type)
+static void gsc_dst_set_addr(struct gsc_context *ctx,
+                            u32 buf_id, struct exynos_drm_ipp_buffer *buf)
 {
-       struct gsc_context *ctx = get_gsc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node;
-       struct drm_exynos_ipp_property *property;
-       struct drm_exynos_ipp_config *config;
-       int ret;
-
-       if (!c_node) {
-               DRM_ERROR("failed to get c_node.\n");
-               return -EFAULT;
-       }
-
-       property = &c_node->property;
-
-       DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n",
-               property->prop_id, buf_id, buf_type);
-
-       buf_id = 0;
-
        /* address register set */
-       switch (buf_type) {
-       case IPP_BUF_ENQUEUE:
-               config = &property->config[EXYNOS_DRM_OPS_DST];
-               ret = gsc_set_planar_addr(buf_info, config->fmt, &config->sz);
-               if (ret) {
-                       dev_err(dev, "failed to set plane dst addr.\n");
-                       return ret;
-               }
-
-               gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y],
-                       GSC_OUT_BASE_ADDR_Y(buf_id));
-               gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB],
-                       GSC_OUT_BASE_ADDR_CB(buf_id));
-               gsc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR],
-                       GSC_OUT_BASE_ADDR_CR(buf_id));
-               break;
-       case IPP_BUF_DEQUEUE:
-               gsc_write(0x0, GSC_OUT_BASE_ADDR_Y(buf_id));
-               gsc_write(0x0, GSC_OUT_BASE_ADDR_CB(buf_id));
-               gsc_write(0x0, GSC_OUT_BASE_ADDR_CR(buf_id));
-               break;
-       default:
-               /* bypass */
-               break;
-       }
+       gsc_write(buf->dma_addr[0], GSC_OUT_BASE_ADDR_Y(buf_id));
+       gsc_write(buf->dma_addr[1], GSC_OUT_BASE_ADDR_CB(buf_id));
+       gsc_write(buf->dma_addr[2], GSC_OUT_BASE_ADDR_CR(buf_id));
 
-       return gsc_dst_set_buf_seq(ctx, buf_id, buf_type);
+       gsc_dst_set_buf_seq(ctx, buf_id, true);
 }
 
-static struct exynos_drm_ipp_ops gsc_dst_ops = {
-       .set_fmt = gsc_dst_set_fmt,
-       .set_transf = gsc_dst_set_transf,
-       .set_size = gsc_dst_set_size,
-       .set_addr = gsc_dst_set_addr,
-};
-
-static int gsc_clk_ctrl(struct gsc_context *ctx, bool enable)
+static int gsc_get_src_buf_index(struct gsc_context *ctx)
 {
-       int i;
-       DRM_DEBUG_KMS("enable[%d]\n", enable);
-
-       if (enable) {
-               for (i = 0; i < ctx->num_clocks; i++) {
-                       int ret = clk_prepare_enable(ctx->clocks[i]);
-                       if (ret) {
-                               while (--i > 0)
-                                       clk_disable_unprepare(ctx->clocks[i]);
-                               return ret;
-                       }
-               }
+       u32 cfg, curr_index, i;
+       u32 buf_id = GSC_MAX_SRC;
 
-               ctx->suspended = false;
-       } else {
-               for (i = ctx->num_clocks - 1; i >= 0; i--)
-                       clk_disable_unprepare(ctx->clocks[i]);
-
-               ctx->suspended = true;
-       }
+       DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id);
 
-       return 0;
-}
+       cfg = gsc_read(GSC_IN_BASE_ADDR_Y_MASK);
+       curr_index = GSC_IN_CURR_GET_INDEX(cfg);
 
-static int gsc_get_src_buf_index(struct gsc_context *ctx)
-{
-       u32 buf_id = 0;
-       int ret;
+       for (i = curr_index; i < GSC_MAX_SRC; i++) {
+               if (!((cfg >> i) & 0x1)) {
+                       buf_id = i;
+                       break;
+               }
+       }
 
-       DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id);
+       DRM_DEBUG_KMS("cfg[0x%x]curr_index[%d]buf_id[%d]\n", cfg,
+               curr_index, buf_id);
 
-       ret = gsc_src_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE);
-       if (ret < 0) {
-               DRM_ERROR("failed to dequeue.\n");
-               return ret;
+       if (buf_id == GSC_MAX_SRC) {
+               DRM_ERROR("failed to get in buffer index.\n");
+               return -EINVAL;
        }
 
+       gsc_src_set_buf_seq(ctx, buf_id, false);
+
        return buf_id;
 }
 
 static int gsc_get_dst_buf_index(struct gsc_context *ctx)
 {
-       u32 buf_id = 0;
-       int ret;
+       u32 cfg, curr_index, i;
+       u32 buf_id = GSC_MAX_DST;
 
        DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id);
 
-       ret = gsc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE);
-       if (ret < 0) {
-               DRM_ERROR("failed to dequeue.\n");
-               return ret;
+       cfg = gsc_read(GSC_OUT_BASE_ADDR_Y_MASK);
+       curr_index = GSC_OUT_CURR_GET_INDEX(cfg);
+
+       for (i = curr_index; i < GSC_MAX_DST; i++) {
+               if (!((cfg >> i) & 0x1)) {
+                       buf_id = i;
+                       break;
+               }
+       }
+
+       if (buf_id == GSC_MAX_DST) {
+               DRM_ERROR("failed to get out buffer index.\n");
+               return -EINVAL;
        }
 
+       gsc_dst_set_buf_seq(ctx, buf_id, false);
+
+       DRM_DEBUG_KMS("cfg[0x%x]curr_index[%d]buf_id[%d]\n", cfg,
+               curr_index, buf_id);
+
        return buf_id;
 }
 
 static irqreturn_t gsc_irq_handler(int irq, void *dev_id)
 {
        struct gsc_context *ctx = dev_id;
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node;
-       struct drm_exynos_ipp_event_work *event_work =
-               c_node->event_work;
        u32 status;
-       int buf_id[EXYNOS_DRM_OPS_MAX];
+       int err = 0;
 
        DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id);
 
        status = gsc_read(GSC_IRQ);
        if (status & GSC_IRQ_STATUS_OR_IRQ) {
-               dev_err(ippdrv->dev, "occurred overflow at %d, status 0x%x.\n",
+               dev_err(ctx->dev, "occurred overflow at %d, status 0x%x.\n",
                        ctx->id, status);
-               return IRQ_NONE;
-       }
-
-       if (c_node->state == IPP_STATE_STOP) {
-               DRM_ERROR("invalid state: prop_id[%d]\n",
-                               c_node->property.prop_id);
-               return IRQ_HANDLED;
+               err = -EINVAL;
        }
 
        if (status & GSC_IRQ_STATUS_OR_FRM_DONE) {
-               dev_dbg(ippdrv->dev, "occurred frame done at %d, status 0x%x.\n",
-                       ctx->id, status);
-
-               buf_id[EXYNOS_DRM_OPS_SRC] = gsc_get_src_buf_index(ctx);
-               if (buf_id[EXYNOS_DRM_OPS_SRC] < 0)
-                       return IRQ_HANDLED;
-
-               buf_id[EXYNOS_DRM_OPS_DST] = gsc_get_dst_buf_index(ctx);
-               if (buf_id[EXYNOS_DRM_OPS_DST] < 0)
-                       return IRQ_HANDLED;
-
-               DRM_DEBUG_KMS("buf_id_src[%d]buf_id_dst[%d]\n",
-                       buf_id[EXYNOS_DRM_OPS_SRC], buf_id[EXYNOS_DRM_OPS_DST]);
-
-               event_work->ippdrv = ippdrv;
-               event_work->buf_id[EXYNOS_DRM_OPS_SRC] =
-                       buf_id[EXYNOS_DRM_OPS_SRC];
-               event_work->buf_id[EXYNOS_DRM_OPS_DST] =
-                       buf_id[EXYNOS_DRM_OPS_DST];
-               queue_work(ippdrv->event_workq, &event_work->work);
-       }
+               int src_buf_id, dst_buf_id;
 
-       return IRQ_HANDLED;
-}
+               dev_dbg(ctx->dev, "occurred frame done at %d, status 0x%x.\n",
+                       ctx->id, status);
 
-static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
-{
-       struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
-
-       prop_list->version = 1;
-       prop_list->writeback = 1;
-       prop_list->refresh_min = GSC_REFRESH_MIN;
-       prop_list->refresh_max = GSC_REFRESH_MAX;
-       prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
-                               (1 << EXYNOS_DRM_FLIP_HORIZONTAL);
-       prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) |
-                               (1 << EXYNOS_DRM_DEGREE_90) |
-                               (1 << EXYNOS_DRM_DEGREE_180) |
-                               (1 << EXYNOS_DRM_DEGREE_270);
-       prop_list->csc = 1;
-       prop_list->crop = 1;
-       prop_list->crop_max.hsize = GSC_CROP_MAX;
-       prop_list->crop_max.vsize = GSC_CROP_MAX;
-       prop_list->crop_min.hsize = GSC_CROP_MIN;
-       prop_list->crop_min.vsize = GSC_CROP_MIN;
-       prop_list->scale = 1;
-       prop_list->scale_max.hsize = GSC_SCALE_MAX;
-       prop_list->scale_max.vsize = GSC_SCALE_MAX;
-       prop_list->scale_min.hsize = GSC_SCALE_MIN;
-       prop_list->scale_min.vsize = GSC_SCALE_MIN;
+               src_buf_id = gsc_get_src_buf_index(ctx);
+               dst_buf_id = gsc_get_dst_buf_index(ctx);
 
-       return 0;
-}
+               DRM_DEBUG_KMS("buf_id_src[%d]buf_id_dst[%d]\n", src_buf_id,
+                             dst_buf_id);
 
-static inline bool gsc_check_drm_flip(enum drm_exynos_flip flip)
-{
-       switch (flip) {
-       case EXYNOS_DRM_FLIP_NONE:
-       case EXYNOS_DRM_FLIP_VERTICAL:
-       case EXYNOS_DRM_FLIP_HORIZONTAL:
-       case EXYNOS_DRM_FLIP_BOTH:
-               return true;
-       default:
-               DRM_DEBUG_KMS("invalid flip\n");
-               return false;
+               if (src_buf_id < 0 || dst_buf_id < 0)
+                       err = -EINVAL;
        }
-}
-
-static int gsc_ippdrv_check_property(struct device *dev,
-               struct drm_exynos_ipp_property *property)
-{
-       struct gsc_context *ctx = get_gsc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       struct drm_exynos_ipp_prop_list *pp = &ippdrv->prop_list;
-       struct drm_exynos_ipp_config *config;
-       struct drm_exynos_pos *pos;
-       struct drm_exynos_sz *sz;
-       bool swap;
-       int i;
 
-       config = &property->config[EXYNOS_DRM_OPS_DST];
+       if (ctx->task) {
+               struct exynos_drm_ipp_task *task = ctx->task;
 
-       /* check for degree */
-       switch (config->degree) {
-       case EXYNOS_DRM_DEGREE_90:
-       case EXYNOS_DRM_DEGREE_270:
-               swap = true;
-               break;
-       case EXYNOS_DRM_DEGREE_0:
-       case EXYNOS_DRM_DEGREE_180:
-               swap = false;
-               break;
-       default:
-               DRM_ERROR("invalid degree.\n");
-               goto err_property;
-       }
-
-       for_each_ipp_ops(i) {
-               if ((i == EXYNOS_DRM_OPS_SRC) &&
-                       (property->cmd == IPP_CMD_WB))
-                       continue;
-
-               config = &property->config[i];
-               pos = &config->pos;
-               sz = &config->sz;
-
-               /*
-                * Multiples of the source resolution must be a multiple of 16.
-                * Check the source resolution.
-                */
-               if (i == EXYNOS_DRM_OPS_SRC &&
-                       ((sz->hsize & 0xf) || (sz->vsize & 0xf)))
-                       dev_warn(dev, "resolution must be a multiple of 16.\n");
-
-               /* check for flip */
-               if (!gsc_check_drm_flip(config->flip)) {
-                       DRM_ERROR("invalid flip.\n");
-                       goto err_property;
-               }
-
-               /* check for buffer bound */
-               if ((pos->x + pos->w > sz->hsize) ||
-                       (pos->y + pos->h > sz->vsize)) {
-                       DRM_ERROR("out of buf bound.\n");
-                       goto err_property;
-               }
-
-               /*
-                * The rotation hardware limits are related to the cropped
-                * source size. So use rot_max size to check the limits when
-                * swap happens. And also the scaling limits are related to pos
-                * size, use pos size to check the limits.
-                */
-               /* check for crop */
-               if ((i == EXYNOS_DRM_OPS_SRC) && (pp->crop)) {
-                       if (swap) {
-                               if ((pos->h < pp->crop_min.hsize) ||
-                                       (pos->h > pp->rot_max.hsize) ||
-                                       (pos->w < pp->crop_min.vsize) ||
-                                       (pos->w > pp->rot_max.vsize)) {
-                                       DRM_ERROR("out of crop size.\n");
-                                       goto err_property;
-                               }
-                       } else {
-                               if ((pos->w < pp->crop_min.hsize) ||
-                                       (pos->w > pp->crop_max.hsize) ||
-                                       (pos->h < pp->crop_min.vsize) ||
-                                       (pos->h > pp->crop_max.vsize)) {
-                                       DRM_ERROR("out of crop size.\n");
-                                       goto err_property;
-                               }
-                       }
-               }
-
-               /* check for scale */
-               if ((i == EXYNOS_DRM_OPS_DST) && (pp->scale)) {
-                       if (swap) {
-                               if ((pos->h < pp->scale_min.hsize) ||
-                                       (pos->h > pp->scale_max.hsize) ||
-                                       (pos->w < pp->scale_min.vsize) ||
-                                       (pos->w > pp->scale_max.vsize)) {
-                                       DRM_ERROR("out of scale size.\n");
-                                       goto err_property;
-                               }
-                       } else {
-                               if ((pos->w < pp->scale_min.hsize) ||
-                                       (pos->w > pp->scale_max.hsize) ||
-                                       (pos->h < pp->scale_min.vsize) ||
-                                       (pos->h > pp->scale_max.vsize)) {
-                                       DRM_ERROR("out of scale size.\n");
-                                       goto err_property;
-                               }
-                       }
-               }
+               ctx->task = NULL;
+               pm_runtime_mark_last_busy(ctx->dev);
+               pm_runtime_put_autosuspend(ctx->dev);
+               exynos_drm_ipp_task_done(task, err);
        }
 
-       return 0;
-
-err_property:
-       for_each_ipp_ops(i) {
-               if ((i == EXYNOS_DRM_OPS_SRC) &&
-                       (property->cmd == IPP_CMD_WB))
-                       continue;
-
-               config = &property->config[i];
-               pos = &config->pos;
-               sz = &config->sz;
-
-               DRM_ERROR("[%s]f[%d]r[%d]pos[%d %d %d %d]sz[%d %d]\n",
-                       i ? "dst" : "src", config->flip, config->degree,
-                       pos->x, pos->y, pos->w, pos->h,
-                       sz->hsize, sz->vsize);
-       }
-
-       return -EINVAL;
+       return IRQ_HANDLED;
 }
 
-
-static int gsc_ippdrv_reset(struct device *dev)
+static int gsc_reset(struct gsc_context *ctx)
 {
-       struct gsc_context *ctx = get_gsc_context(dev);
        struct gsc_scaler *sc = &ctx->sc;
        int ret;
 
        /* reset h/w block */
        ret = gsc_sw_reset(ctx);
        if (ret < 0) {
-               dev_err(dev, "failed to reset hardware.\n");
+               dev_err(ctx->dev, "failed to reset hardware.\n");
                return ret;
        }
 
@@ -1645,210 +1063,162 @@ static int gsc_ippdrv_reset(struct device *dev)
        return 0;
 }
 
-static int gsc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
+static void gsc_start(struct gsc_context *ctx)
 {
-       struct gsc_context *ctx = get_gsc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node;
-       struct drm_exynos_ipp_property *property;
-       struct drm_exynos_ipp_config *config;
-       struct drm_exynos_pos   img_pos[EXYNOS_DRM_OPS_MAX];
-       struct drm_exynos_ipp_set_wb set_wb;
        u32 cfg;
-       int ret, i;
-
-       DRM_DEBUG_KMS("cmd[%d]\n", cmd);
-
-       if (!c_node) {
-               DRM_ERROR("failed to get c_node.\n");
-               return -EINVAL;
-       }
-
-       property = &c_node->property;
 
        gsc_handle_irq(ctx, true, false, true);
 
-       for_each_ipp_ops(i) {
-               config = &property->config[i];
-               img_pos[i] = config->pos;
-       }
+       /* enable one shot */
+       cfg = gsc_read(GSC_ENABLE);
+       cfg &= ~(GSC_ENABLE_ON_CLEAR_MASK |
+               GSC_ENABLE_CLK_GATE_MODE_MASK);
+       cfg |= GSC_ENABLE_ON_CLEAR_ONESHOT;
+       gsc_write(cfg, GSC_ENABLE);
 
-       switch (cmd) {
-       case IPP_CMD_M2M:
-               /* enable one shot */
-               cfg = gsc_read(GSC_ENABLE);
-               cfg &= ~(GSC_ENABLE_ON_CLEAR_MASK |
-                       GSC_ENABLE_CLK_GATE_MODE_MASK);
-               cfg |= GSC_ENABLE_ON_CLEAR_ONESHOT;
-               gsc_write(cfg, GSC_ENABLE);
-
-               /* src dma memory */
-               cfg = gsc_read(GSC_IN_CON);
-               cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK);
-               cfg |= GSC_IN_PATH_MEMORY;
-               gsc_write(cfg, GSC_IN_CON);
-
-               /* dst dma memory */
-               cfg = gsc_read(GSC_OUT_CON);
-               cfg |= GSC_OUT_PATH_MEMORY;
-               gsc_write(cfg, GSC_OUT_CON);
-               break;
-       case IPP_CMD_WB:
-               set_wb.enable = 1;
-               set_wb.refresh = property->refresh_rate;
-               gsc_set_gscblk_fimd_wb(ctx, set_wb.enable);
-               exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb);
-
-               /* src local path */
-               cfg = gsc_read(GSC_IN_CON);
-               cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK);
-               cfg |= (GSC_IN_PATH_LOCAL | GSC_IN_LOCAL_FIMD_WB);
-               gsc_write(cfg, GSC_IN_CON);
-
-               /* dst dma memory */
-               cfg = gsc_read(GSC_OUT_CON);
-               cfg |= GSC_OUT_PATH_MEMORY;
-               gsc_write(cfg, GSC_OUT_CON);
-               break;
-       case IPP_CMD_OUTPUT:
-               /* src dma memory */
-               cfg = gsc_read(GSC_IN_CON);
-               cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK);
-               cfg |= GSC_IN_PATH_MEMORY;
-               gsc_write(cfg, GSC_IN_CON);
-
-               /* dst local path */
-               cfg = gsc_read(GSC_OUT_CON);
-               cfg |= GSC_OUT_PATH_MEMORY;
-               gsc_write(cfg, GSC_OUT_CON);
-               break;
-       default:
-               ret = -EINVAL;
-               dev_err(dev, "invalid operations.\n");
-               return ret;
-       }
+       /* src dma memory */
+       cfg = gsc_read(GSC_IN_CON);
+       cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK);
+       cfg |= GSC_IN_PATH_MEMORY;
+       gsc_write(cfg, GSC_IN_CON);
 
-       ret = gsc_set_prescaler(ctx, &ctx->sc,
-               &img_pos[EXYNOS_DRM_OPS_SRC],
-               &img_pos[EXYNOS_DRM_OPS_DST]);
-       if (ret) {
-               dev_err(dev, "failed to set precalser.\n");
-               return ret;
-       }
+       /* dst dma memory */
+       cfg = gsc_read(GSC_OUT_CON);
+       cfg |= GSC_OUT_PATH_MEMORY;
+       gsc_write(cfg, GSC_OUT_CON);
 
        gsc_set_scaler(ctx, &ctx->sc);
 
        cfg = gsc_read(GSC_ENABLE);
        cfg |= GSC_ENABLE_ON;
        gsc_write(cfg, GSC_ENABLE);
+}
+
+static int gsc_commit(struct exynos_drm_ipp *ipp,
+                         struct exynos_drm_ipp_task *task)
+{
+       struct gsc_context *ctx = container_of(ipp, struct gsc_context, ipp);
+       int ret;
+
+       pm_runtime_get_sync(ctx->dev);
+       ctx->task = task;
+
+       ret = gsc_reset(ctx);
+       if (ret) {
+               pm_runtime_put_autosuspend(ctx->dev);
+               ctx->task = NULL;
+               return ret;
+       }
+
+       gsc_src_set_fmt(ctx, task->src.buf.fourcc);
+       gsc_src_set_transf(ctx, task->transform.rotation);
+       gsc_src_set_size(ctx, &task->src);
+       gsc_src_set_addr(ctx, 0, &task->src);
+       gsc_dst_set_fmt(ctx, task->dst.buf.fourcc);
+       gsc_dst_set_size(ctx, &task->dst);
+       gsc_dst_set_addr(ctx, 0, &task->dst);
+       gsc_set_prescaler(ctx, &ctx->sc, &task->src.rect, &task->dst.rect);
+       gsc_start(ctx);
 
        return 0;
 }
 
-static void gsc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
+static void gsc_abort(struct exynos_drm_ipp *ipp,
+                         struct exynos_drm_ipp_task *task)
 {
-       struct gsc_context *ctx = get_gsc_context(dev);
-       struct drm_exynos_ipp_set_wb set_wb = {0, 0};
-       u32 cfg;
+       struct gsc_context *ctx =
+                       container_of(ipp, struct gsc_context, ipp);
 
-       DRM_DEBUG_KMS("cmd[%d]\n", cmd);
+       gsc_reset(ctx);
+       if (ctx->task) {
+               struct exynos_drm_ipp_task *task = ctx->task;
 
-       switch (cmd) {
-       case IPP_CMD_M2M:
-               /* bypass */
-               break;
-       case IPP_CMD_WB:
-               gsc_set_gscblk_fimd_wb(ctx, set_wb.enable);
-               exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb);
-               break;
-       case IPP_CMD_OUTPUT:
-       default:
-               dev_err(dev, "invalid operations.\n");
-               break;
+               ctx->task = NULL;
+               pm_runtime_mark_last_busy(ctx->dev);
+               pm_runtime_put_autosuspend(ctx->dev);
+               exynos_drm_ipp_task_done(task, -EIO);
        }
+}
 
-       gsc_handle_irq(ctx, false, false, true);
+static struct exynos_drm_ipp_funcs ipp_funcs = {
+       .commit = gsc_commit,
+       .abort = gsc_abort,
+};
 
-       /* reset sequence */
-       gsc_write(0xff, GSC_OUT_BASE_ADDR_Y_MASK);
-       gsc_write(0xff, GSC_OUT_BASE_ADDR_CB_MASK);
-       gsc_write(0xff, GSC_OUT_BASE_ADDR_CR_MASK);
+static int gsc_bind(struct device *dev, struct device *master, void *data)
+{
+       struct gsc_context *ctx = dev_get_drvdata(dev);
+       struct drm_device *drm_dev = data;
+       struct exynos_drm_ipp *ipp = &ctx->ipp;
 
-       cfg = gsc_read(GSC_ENABLE);
-       cfg &= ~GSC_ENABLE_ON;
-       gsc_write(cfg, GSC_ENABLE);
+       ctx->drm_dev = drm_dev;
+       drm_iommu_attach_device(drm_dev, dev);
+
+       exynos_drm_ipp_register(drm_dev, ipp, &ipp_funcs,
+                       DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE |
+                       DRM_EXYNOS_IPP_CAP_SCALE | DRM_EXYNOS_IPP_CAP_CONVERT,
+                       ctx->formats, ctx->num_formats, "gsc");
+
+       dev_info(dev, "The exynos gscaler has been probed successfully\n");
+
+       return 0;
 }
 
-static struct gsc_driverdata gsc_exynos5_drvdata = {
-       .clk_names = {"gscl"},
-       .num_clocks = 1,
-};
+static void gsc_unbind(struct device *dev, struct device *master,
+                       void *data)
+{
+       struct gsc_context *ctx = dev_get_drvdata(dev);
+       struct drm_device *drm_dev = data;
+       struct exynos_drm_ipp *ipp = &ctx->ipp;
 
-static struct gsc_driverdata gsc_exynos5433_drvdata = {
-       .clk_names = {"pclk", "aclk", "aclk_xiu", "aclk_gsclbend"},
-       .num_clocks = 4,
+       exynos_drm_ipp_unregister(drm_dev, ipp);
+       drm_iommu_detach_device(drm_dev, dev);
+}
+
+static const struct component_ops gsc_component_ops = {
+       .bind   = gsc_bind,
+       .unbind = gsc_unbind,
 };
 
-static const struct of_device_id exynos_drm_gsc_of_match[] = {
-       {
-               .compatible = "samsung,exynos5-gsc",
-               .data = &gsc_exynos5_drvdata,
-       },
-       {
-               .compatible = "samsung,exynos5433-gsc",
-               .data = &gsc_exynos5433_drvdata,
-       },
-       { },
+static const unsigned int gsc_formats[] = {
+       DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB565, DRM_FORMAT_BGRX8888,
+       DRM_FORMAT_NV12, DRM_FORMAT_NV16, DRM_FORMAT_NV21, DRM_FORMAT_NV61,
+       DRM_FORMAT_UYVY, DRM_FORMAT_VYUY, DRM_FORMAT_YUYV, DRM_FORMAT_YVYU,
+       DRM_FORMAT_YUV420, DRM_FORMAT_YVU420, DRM_FORMAT_YUV422,
 };
-MODULE_DEVICE_TABLE(of, exynos_drm_gsc_of_match);
 
 static int gsc_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
+       struct gsc_driverdata *driver_data;
+       struct exynos_drm_ipp_formats *formats;
        struct gsc_context *ctx;
        struct resource *res;
-       struct exynos_drm_ippdrv *ippdrv;
        int ret, i;
 
        ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
        if (!ctx)
                return -ENOMEM;
 
-       if (dev->of_node) {
-               const struct of_device_id *match;
-               struct gsc_driverdata *driver_data;
-
-               /* Handle only devices that support the LCD Writeback path */
-               if (!of_property_read_bool(dev->of_node, "samsung,lcd-wb"))
-                       return -ENODEV;
-
-               ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
-                                                       "samsung,sysreg");
-               if (IS_ERR(ctx->sysreg)) {
-                       dev_warn(dev, "failed to get system register.\n");
-                       ctx->sysreg = NULL;
-               }
-
-               match = of_match_node(exynos_drm_gsc_of_match, dev->of_node);
-               if (!match)
-                       return -ENODEV;
-
-               driver_data = (struct gsc_driverdata *)match->data;
-
-               ctx->num_clocks = driver_data->num_clocks;
-               ctx->clk_names = driver_data->clk_names;
+       formats = devm_kzalloc(dev, sizeof(*formats) *
+                              (ARRAY_SIZE(gsc_formats)), GFP_KERNEL);
+       if (!formats)
+               return -ENOMEM;
 
-               ret = of_property_read_u32(dev->of_node, "rot-max-hsize",
-                                       &ctx->ippdrv.prop_list.rot_max.hsize);
-               ret |= of_property_read_u32(dev->of_node, "rot-max-vsize",
-                                       &ctx->ippdrv.prop_list.rot_max.vsize);
-               if (ret) {
-                       dev_err(dev, "rot-max property should be provided by device tree.\n");
-                       return -EINVAL;
-               }
-       } else {
-               return -ENODEV;
+       driver_data = (struct gsc_driverdata *)of_device_get_match_data(dev);
+       ctx->dev = dev;
+       ctx->num_clocks = driver_data->num_clocks;
+       ctx->clk_names = driver_data->clk_names;
+
+       for (i = 0; i < ARRAY_SIZE(gsc_formats); i++) {
+               formats[i].fourcc = gsc_formats[i];
+               formats[i].type = DRM_EXYNOS_IPP_FORMAT_SOURCE |
+                                 DRM_EXYNOS_IPP_FORMAT_DESTINATION;
+               formats[i].limits = driver_data->limits;
+               formats[i].num_limits = driver_data->num_limits;
        }
+       ctx->formats = formats;
+       ctx->num_formats = ARRAY_SIZE(gsc_formats);
 
        /* clock control */
        for (i = 0; i < ctx->num_clocks; i++) {
@@ -1860,10 +1230,6 @@ static int gsc_probe(struct platform_device *pdev)
                }
        }
 
-       /* WORKAROUND: force enable pclk to avoid system freeze */
-       if (ctx->num_clocks > 1)
-               clk_prepare_enable(ctx->clocks[0]);
-
        /* resource memory */
        ctx->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        ctx->regs = devm_ioremap_resource(dev, ctx->regs_res);
@@ -1878,8 +1244,8 @@ static int gsc_probe(struct platform_device *pdev)
        }
 
        ctx->irq = res->start;
-       ret = devm_request_threaded_irq(dev, ctx->irq, NULL, gsc_irq_handler,
-               IRQF_ONESHOT, "drm_gsc", ctx);
+       ret = devm_request_irq(dev, ctx->irq, gsc_irq_handler, 0,
+                              dev_name(dev), ctx);
        if (ret < 0) {
                dev_err(dev, "failed to request irq.\n");
                return ret;
@@ -1888,38 +1254,22 @@ static int gsc_probe(struct platform_device *pdev)
        /* context initailization */
        ctx->id = pdev->id;
 
-       ippdrv = &ctx->ippdrv;
-       ippdrv->dev = dev;
-       ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &gsc_src_ops;
-       ippdrv->ops[EXYNOS_DRM_OPS_DST] = &gsc_dst_ops;
-       ippdrv->check_property = gsc_ippdrv_check_property;
-       ippdrv->reset = gsc_ippdrv_reset;
-       ippdrv->start = gsc_ippdrv_start;
-       ippdrv->stop = gsc_ippdrv_stop;
-       ret = gsc_init_prop_list(ippdrv);
-       if (ret < 0) {
-               dev_err(dev, "failed to init property list.\n");
-               return ret;
-       }
-
-       DRM_DEBUG_KMS("id[%d]ippdrv[%p]\n", ctx->id, ippdrv);
-
-       mutex_init(&ctx->lock);
        platform_set_drvdata(pdev, ctx);
 
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_set_autosuspend_delay(dev, GSC_AUTOSUSPEND_DELAY);
        pm_runtime_enable(dev);
 
-       ret = exynos_drm_ippdrv_register(ippdrv);
-       if (ret < 0) {
-               dev_err(dev, "failed to register drm gsc device.\n");
-               goto err_ippdrv_register;
-       }
+       ret = component_add(dev, &gsc_component_ops);
+       if (ret)
+               goto err_pm_dis;
 
        dev_info(dev, "drm gsc registered successfully.\n");
 
        return 0;
 
-err_ippdrv_register:
+err_pm_dis:
+       pm_runtime_dont_use_autosuspend(dev);
        pm_runtime_disable(dev);
        return ret;
 }
@@ -1927,73 +1277,113 @@ err_ippdrv_register:
 static int gsc_remove(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
-       struct gsc_context *ctx = get_gsc_context(dev);
-       struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-
-       /* WORKAROUND: disable pclk enabled in the probe() */
-       if (ctx->num_clocks > 1)
-               clk_disable_unprepare(ctx->clocks[0]);
-
-       exynos_drm_ippdrv_unregister(ippdrv);
-       mutex_destroy(&ctx->lock);
 
-       pm_runtime_set_suspended(dev);
+       pm_runtime_dont_use_autosuspend(dev);
        pm_runtime_disable(dev);
 
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int gsc_suspend(struct device *dev)
+static int __maybe_unused gsc_runtime_suspend(struct device *dev)
 {
        struct gsc_context *ctx = get_gsc_context(dev);
+       int i;
 
        DRM_DEBUG_KMS("id[%d]\n", ctx->id);
 
-       if (pm_runtime_suspended(dev))
-               return 0;
+       for (i = ctx->num_clocks - 1; i >= 0; i--)
+               clk_disable_unprepare(ctx->clocks[i]);
 
-       return gsc_clk_ctrl(ctx, false);
+       return 0;
 }
 
-static int gsc_resume(struct device *dev)
+static int __maybe_unused gsc_runtime_resume(struct device *dev)
 {
        struct gsc_context *ctx = get_gsc_context(dev);
+       int i, ret;
 
        DRM_DEBUG_KMS("id[%d]\n", ctx->id);
 
-       if (!pm_runtime_suspended(dev))
-               return gsc_clk_ctrl(ctx, true);
-
+       for (i = 0; i < ctx->num_clocks; i++) {
+               ret = clk_prepare_enable(ctx->clocks[i]);
+               if (ret) {
+                       while (--i > 0)
+                               clk_disable_unprepare(ctx->clocks[i]);
+                       return ret;
+               }
+       }
        return 0;
 }
-#endif
 
-#ifdef CONFIG_PM
-static int gsc_runtime_suspend(struct device *dev)
-{
-       struct gsc_context *ctx = get_gsc_context(dev);
+static const struct dev_pm_ops gsc_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                               pm_runtime_force_resume)
+       SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL)
+};
 
-       DRM_DEBUG_KMS("id[%d]\n", ctx->id);
+static const struct drm_exynos_ipp_limit gsc_5250_limits[] = {
+       { IPP_SIZE_LIMIT(BUFFER, .h = { 32, 4800, 8 }, .v = { 16, 3344, 8 }) },
+       { IPP_SIZE_LIMIT(AREA, .h = { 16, 4800, 2 }, .v = { 8, 3344, 2 }) },
+       { IPP_SIZE_LIMIT(ROTATED, .h = { 32, 2048 }, .v = { 16, 2048 }) },
+       { IPP_SCALE_LIMIT(.h = { (1 << 16) / 16, (1 << 16) * 8 },
+                         .v = { (1 << 16) / 16, (1 << 16) * 8 }) },
+};
 
-       return  gsc_clk_ctrl(ctx, false);
-}
+static const struct drm_exynos_ipp_limit gsc_5420_limits[] = {
+       { IPP_SIZE_LIMIT(BUFFER, .h = { 32, 4800, 8 }, .v = { 16, 3344, 8 }) },
+       { IPP_SIZE_LIMIT(AREA, .h = { 16, 4800, 2 }, .v = { 8, 3344, 2 }) },
+       { IPP_SIZE_LIMIT(ROTATED, .h = { 16, 2016 }, .v = { 8, 2016 }) },
+       { IPP_SCALE_LIMIT(.h = { (1 << 16) / 16, (1 << 16) * 8 },
+                         .v = { (1 << 16) / 16, (1 << 16) * 8 }) },
+};
 
-static int gsc_runtime_resume(struct device *dev)
-{
-       struct gsc_context *ctx = get_gsc_context(dev);
+static const struct drm_exynos_ipp_limit gsc_5433_limits[] = {
+       { IPP_SIZE_LIMIT(BUFFER, .h = { 32, 8191, 8 }, .v = { 16, 8191, 8 }) },
+       { IPP_SIZE_LIMIT(AREA, .h = { 16, 4800, 2 }, .v = { 8, 3344, 2 }) },
+       { IPP_SIZE_LIMIT(ROTATED, .h = { 16, 2047 }, .v = { 8, 2047 }) },
+       { IPP_SCALE_LIMIT(.h = { (1 << 16) / 16, (1 << 16) * 8 },
+                         .v = { (1 << 16) / 16, (1 << 16) * 8 }) },
+};
 
-       DRM_DEBUG_KMS("id[%d]\n", ctx->id);
+static struct gsc_driverdata gsc_exynos5250_drvdata = {
+       .clk_names = {"gscl"},
+       .num_clocks = 1,
+       .limits = gsc_5250_limits,
+       .num_limits = ARRAY_SIZE(gsc_5250_limits),
+};
 
-       return  gsc_clk_ctrl(ctx, true);
-}
-#endif
+static struct gsc_driverdata gsc_exynos5420_drvdata = {
+       .clk_names = {"gscl"},
+       .num_clocks = 1,
+       .limits = gsc_5420_limits,
+       .num_limits = ARRAY_SIZE(gsc_5420_limits),
+};
 
-static const struct dev_pm_ops gsc_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(gsc_suspend, gsc_resume)
-       SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL)
+static struct gsc_driverdata gsc_exynos5433_drvdata = {
+       .clk_names = {"pclk", "aclk", "aclk_xiu", "aclk_gsclbend"},
+       .num_clocks = 4,
+       .limits = gsc_5433_limits,
+       .num_limits = ARRAY_SIZE(gsc_5433_limits),
 };
 
+static const struct of_device_id exynos_drm_gsc_of_match[] = {
+       {
+               .compatible = "samsung,exynos5-gsc",
+               .data = &gsc_exynos5250_drvdata,
+       }, {
+               .compatible = "samsung,exynos5250-gsc",
+               .data = &gsc_exynos5250_drvdata,
+       }, {
+               .compatible = "samsung,exynos5420-gsc",
+               .data = &gsc_exynos5420_drvdata,
+       }, {
+               .compatible = "samsung,exynos5433-gsc",
+               .data = &gsc_exynos5433_drvdata,
+       }, {
+       },
+};
+MODULE_DEVICE_TABLE(of, exynos_drm_gsc_of_match);
+
 struct platform_driver gsc_driver = {
        .probe          = gsc_probe,
        .remove         = gsc_remove,
@@ -2004,4 +1394,3 @@ struct platform_driver gsc_driver = {
                .of_match_table = of_match_ptr(exynos_drm_gsc_of_match),
        },
 };
-
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.h b/drivers/gpu/drm/exynos/exynos_drm_gsc.h
deleted file mode 100644 (file)
index 29ec1c5..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2012 Samsung Electronics Co., Ltd.
- *
- * Authors:
- *     Eunchul Kim <chulspro.kim@samsung.com>
- *     Jinyoung Jeon <jy0.jeon@samsung.com>
- *     Sangmin Lee <lsmin.lee@samsung.com>
- *
- * 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;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#ifndef _EXYNOS_DRM_GSC_H_
-#define _EXYNOS_DRM_GSC_H_
-
-/*
- * TODO
- * FIMD output interface notifier callback.
- * Mixer output interface notifier callback.
- */
-
-#endif /* _EXYNOS_DRM_GSC_H_ */
index f3a29f7d413b82021eee9e758ead9f9ac327c772..9b4ffec400bfe27d7395953c4d4dae02bf50ef55 100644 (file)
 /*
- * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ * Copyright (C) 2017 Samsung Electronics Co.Ltd
  * Authors:
- *     Eunchul Kim <chulspro.kim@samsung.com>
- *     Jinyoung Jeon <jy0.jeon@samsung.com>
- *     Sangmin Lee <lsmin.lee@samsung.com>
+ *     Marek Szyprowski <m.szyprowski@samsung.com>
  *
- * 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;  either version 2 of the  License, or (at your
- * option) any later version.
+ * Exynos DRM Image Post Processing (IPP) related functions
  *
+ * 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.
  */
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/types.h>
-#include <linux/clk.h>
-#include <linux/pm_runtime.h>
+
 
 #include <drm/drmP.h>
-#include <drm/exynos_drm.h>
+#include <drm/drm_mode.h>
+#include <uapi/drm/exynos_drm.h>
+
 #include "exynos_drm_drv.h"
 #include "exynos_drm_gem.h"
 #include "exynos_drm_ipp.h"
-#include "exynos_drm_iommu.h"
-
-/*
- * IPP stands for Image Post Processing and
- * supports image scaler/rotator and input/output DMA operations.
- * using FIMC, GSC, Rotator, so on.
- * IPP is integration device driver of same attribute h/w
- */
-
-/*
- * TODO
- * 1. expand command control id.
- * 2. integrate        property and config.
- * 3. removed send_event id check routine.
- * 4. compare send_event id if needed.
- * 5. free subdrv_remove notifier callback list if needed.
- * 6. need to check subdrv_open about multi-open.
- * 7. need to power_on implement power and sysmmu ctrl.
- */
-
-#define get_ipp_context(dev)   platform_get_drvdata(to_platform_device(dev))
-#define ipp_is_m2m_cmd(c)      (c == IPP_CMD_M2M)
 
-/*
- * A structure of event.
+static int num_ipp;
+static LIST_HEAD(ipp_list);
+
+/**
+ * exynos_drm_ipp_register - Register a new picture processor hardware module
+ * @dev: DRM device
+ * @ipp: ipp module to init
+ * @funcs: callbacks for the new ipp object
+ * @caps: bitmask of ipp capabilities (%DRM_EXYNOS_IPP_CAP_*)
+ * @formats: array of supported formats
+ * @num_formats: size of the supported formats array
+ * @name: name (for debugging purposes)
  *
- * @base: base of event.
- * @event: ipp event.
- */
-struct drm_exynos_ipp_send_event {
-       struct drm_pending_event        base;
-       struct drm_exynos_ipp_event     event;
-};
-
-/*
- * A structure of memory node.
+ * Initializes a ipp module.
  *
- * @list: list head to memory queue information.
- * @ops_id: id of operations.
- * @prop_id: id of property.
- * @buf_id: id of buffer.
- * @buf_info: gem objects and dma address, size.
- * @filp: a pointer to drm_file.
+ * Returns:
+ * Zero on success, error code on failure.
  */
-struct drm_exynos_ipp_mem_node {
-       struct list_head        list;
-       enum drm_exynos_ops_id  ops_id;
-       u32     prop_id;
-       u32     buf_id;
-       struct drm_exynos_ipp_buf_info  buf_info;
-};
-
-/*
- * A structure of ipp context.
- *
- * @subdrv: prepare initialization using subdrv.
- * @ipp_lock: lock for synchronization of access to ipp_idr.
- * @prop_lock: lock for synchronization of access to prop_idr.
- * @ipp_idr: ipp driver idr.
- * @prop_idr: property idr.
- * @event_workq: event work queue.
- * @cmd_workq: command work queue.
- */
-struct ipp_context {
-       struct exynos_drm_subdrv        subdrv;
-       struct mutex    ipp_lock;
-       struct mutex    prop_lock;
-       struct idr      ipp_idr;
-       struct idr      prop_idr;
-       struct workqueue_struct *event_workq;
-       struct workqueue_struct *cmd_workq;
-};
-
-static LIST_HEAD(exynos_drm_ippdrv_list);
-static DEFINE_MUTEX(exynos_drm_ippdrv_lock);
-static BLOCKING_NOTIFIER_HEAD(exynos_drm_ippnb_list);
-
-int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv)
-{
-       mutex_lock(&exynos_drm_ippdrv_lock);
-       list_add_tail(&ippdrv->drv_list, &exynos_drm_ippdrv_list);
-       mutex_unlock(&exynos_drm_ippdrv_lock);
+int exynos_drm_ipp_register(struct drm_device *dev, struct exynos_drm_ipp *ipp,
+               const struct exynos_drm_ipp_funcs *funcs, unsigned int caps,
+               const struct exynos_drm_ipp_formats *formats,
+               unsigned int num_formats, const char *name)
+{
+       WARN_ON(!ipp);
+       WARN_ON(!funcs);
+       WARN_ON(!formats);
+       WARN_ON(!num_formats);
+
+       spin_lock_init(&ipp->lock);
+       INIT_LIST_HEAD(&ipp->todo_list);
+       init_waitqueue_head(&ipp->done_wq);
+       ipp->dev = dev;
+       ipp->funcs = funcs;
+       ipp->capabilities = caps;
+       ipp->name = name;
+       ipp->formats = formats;
+       ipp->num_formats = num_formats;
+
+       /* ipp_list modification is serialized by component framework */
+       list_add_tail(&ipp->head, &ipp_list);
+       ipp->id = num_ipp++;
+
+       DRM_DEBUG_DRIVER("Registered ipp %d\n", ipp->id);
 
        return 0;
 }
 
-int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv)
-{
-       mutex_lock(&exynos_drm_ippdrv_lock);
-       list_del(&ippdrv->drv_list);
-       mutex_unlock(&exynos_drm_ippdrv_lock);
-
-       return 0;
-}
-
-static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj)
-{
-       int ret;
-
-       mutex_lock(lock);
-       ret = idr_alloc(id_idr, obj, 1, 0, GFP_KERNEL);
-       mutex_unlock(lock);
-
-       return ret;
-}
-
-static void ipp_remove_id(struct idr *id_idr, struct mutex *lock, u32 id)
-{
-       mutex_lock(lock);
-       idr_remove(id_idr, id);
-       mutex_unlock(lock);
-}
-
-static void *ipp_find_obj(struct idr *id_idr, struct mutex *lock, u32 id)
-{
-       void *obj;
-
-       mutex_lock(lock);
-       obj = idr_find(id_idr, id);
-       mutex_unlock(lock);
-
-       return obj;
-}
-
-static int ipp_check_driver(struct exynos_drm_ippdrv *ippdrv,
-                           struct drm_exynos_ipp_property *property)
-{
-       if (ippdrv->dedicated || (!ipp_is_m2m_cmd(property->cmd) &&
-                                 !pm_runtime_suspended(ippdrv->dev)))
-               return -EBUSY;
-
-       if (ippdrv->check_property &&
-           ippdrv->check_property(ippdrv->dev, property))
-               return -EINVAL;
-
-       return 0;
-}
-
-static struct exynos_drm_ippdrv *ipp_find_driver(struct ipp_context *ctx,
-               struct drm_exynos_ipp_property *property)
+/**
+ * exynos_drm_ipp_unregister - Unregister the picture processor module
+ * @dev: DRM device
+ * @ipp: ipp module
+ */
+void exynos_drm_ipp_unregister(struct drm_device *dev,
+                              struct exynos_drm_ipp *ipp)
 {
-       struct exynos_drm_ippdrv *ippdrv;
-       u32 ipp_id = property->ipp_id;
-       int ret;
-
-       if (ipp_id) {
-               ippdrv = ipp_find_obj(&ctx->ipp_idr, &ctx->ipp_lock, ipp_id);
-               if (!ippdrv) {
-                       DRM_DEBUG("ipp%d driver not found\n", ipp_id);
-                       return ERR_PTR(-ENODEV);
-               }
-
-               ret = ipp_check_driver(ippdrv, property);
-               if (ret < 0) {
-                       DRM_DEBUG("ipp%d driver check error %d\n", ipp_id, ret);
-                       return ERR_PTR(ret);
-               }
-
-               return ippdrv;
-       } else {
-               list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
-                       ret = ipp_check_driver(ippdrv, property);
-                       if (ret == 0)
-                               return ippdrv;
-               }
-
-               DRM_DEBUG("cannot find driver suitable for given property.\n");
-       }
-
-       return ERR_PTR(-ENODEV);
+       WARN_ON(ipp->task);
+       WARN_ON(!list_empty(&ipp->todo_list));
+       list_del(&ipp->head);
 }
 
-static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id)
+/**
+ * exynos_drm_ipp_ioctl_get_res_ioctl - enumerate all ipp modules
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Construct a list of ipp ids.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int exynos_drm_ipp_get_res_ioctl(struct drm_device *dev, void *data,
+                                struct drm_file *file_priv)
 {
-       struct exynos_drm_ippdrv *ippdrv;
-       struct drm_exynos_ipp_cmd_node *c_node;
-       int count = 0;
-
-       DRM_DEBUG_KMS("prop_id[%d]\n", prop_id);
+       struct drm_exynos_ioctl_ipp_get_res *resp = data;
+       struct exynos_drm_ipp *ipp;
+       uint32_t __user *ipp_ptr = (uint32_t __user *)
+                                               (unsigned long)resp->ipp_id_ptr;
+       unsigned int count = num_ipp, copied = 0;
 
        /*
-        * This case is search ipp driver by prop_id handle.
-        * sometimes, ipp subsystem find driver by prop_id.
-        * e.g PAUSE state, queue buf, command control.
+        * This ioctl is called twice, once to determine how much space is
+        * needed, and the 2nd time to fill it.
         */
-       list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
-               DRM_DEBUG_KMS("count[%d]ippdrv[%p]\n", count++, ippdrv);
-
-               mutex_lock(&ippdrv->cmd_lock);
-               list_for_each_entry(c_node, &ippdrv->cmd_list, list) {
-                       if (c_node->property.prop_id == prop_id) {
-                               mutex_unlock(&ippdrv->cmd_lock);
-                               return ippdrv;
-                       }
+       if (count && resp->count_ipps >= count) {
+               list_for_each_entry(ipp, &ipp_list, head) {
+                       if (put_user(ipp->id, ipp_ptr + copied))
+                               return -EFAULT;
+                       copied++;
                }
-               mutex_unlock(&ippdrv->cmd_lock);
-       }
-
-       return ERR_PTR(-ENODEV);
-}
-
-int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
-               struct drm_file *file)
-{
-       struct drm_exynos_file_private *file_priv = file->driver_priv;
-       struct device *dev = file_priv->ipp_dev;
-       struct ipp_context *ctx = get_ipp_context(dev);
-       struct drm_exynos_ipp_prop_list *prop_list = data;
-       struct exynos_drm_ippdrv *ippdrv;
-       int count = 0;
-
-       if (!ctx) {
-               DRM_ERROR("invalid context.\n");
-               return -EINVAL;
-       }
-
-       if (!prop_list) {
-               DRM_ERROR("invalid property parameter.\n");
-               return -EINVAL;
-       }
-
-       DRM_DEBUG_KMS("ipp_id[%d]\n", prop_list->ipp_id);
-
-       if (!prop_list->ipp_id) {
-               list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list)
-                       count++;
-
-               /*
-                * Supports ippdrv list count for user application.
-                * First step user application getting ippdrv count.
-                * and second step getting ippdrv capability using ipp_id.
-                */
-               prop_list->count = count;
-       } else {
-               /*
-                * Getting ippdrv capability by ipp_id.
-                * some device not supported wb, output interface.
-                * so, user application detect correct ipp driver
-                * using this ioctl.
-                */
-               ippdrv = ipp_find_obj(&ctx->ipp_idr, &ctx->ipp_lock,
-                                               prop_list->ipp_id);
-               if (!ippdrv) {
-                       DRM_ERROR("not found ipp%d driver.\n",
-                                       prop_list->ipp_id);
-                       return -ENODEV;
-               }
-
-               *prop_list = ippdrv->prop_list;
        }
+       resp->count_ipps = count;
 
        return 0;
 }
 
-static void ipp_print_property(struct drm_exynos_ipp_property *property,
-               int idx)
+static inline struct exynos_drm_ipp *__ipp_get(uint32_t id)
 {
-       struct drm_exynos_ipp_config *config = &property->config[idx];
-       struct drm_exynos_pos *pos = &config->pos;
-       struct drm_exynos_sz *sz = &config->sz;
-
-       DRM_DEBUG_KMS("prop_id[%d]ops[%s]fmt[0x%x]\n",
-               property->prop_id, idx ? "dst" : "src", config->fmt);
+       struct exynos_drm_ipp *ipp;
 
-       DRM_DEBUG_KMS("pos[%d %d %d %d]sz[%d %d]f[%d]r[%d]\n",
-               pos->x, pos->y, pos->w, pos->h,
-               sz->hsize, sz->vsize, config->flip, config->degree);
-}
-
-static struct drm_exynos_ipp_cmd_work *ipp_create_cmd_work(void)
-{
-       struct drm_exynos_ipp_cmd_work *cmd_work;
-
-       cmd_work = kzalloc(sizeof(*cmd_work), GFP_KERNEL);
-       if (!cmd_work)
-               return ERR_PTR(-ENOMEM);
-
-       INIT_WORK((struct work_struct *)cmd_work, ipp_sched_cmd);
-
-       return cmd_work;
-}
-
-static struct drm_exynos_ipp_event_work *ipp_create_event_work(void)
-{
-       struct drm_exynos_ipp_event_work *event_work;
-
-       event_work = kzalloc(sizeof(*event_work), GFP_KERNEL);
-       if (!event_work)
-               return ERR_PTR(-ENOMEM);
-
-       INIT_WORK(&event_work->work, ipp_sched_event);
-
-       return event_work;
+       list_for_each_entry(ipp, &ipp_list, head)
+               if (ipp->id == id)
+                       return ipp;
+       return NULL;
 }
 
-int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
-               struct drm_file *file)
+/**
+ * exynos_drm_ipp_ioctl_get_caps - get ipp module capabilities and formats
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Construct a structure describing ipp module capabilities.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int exynos_drm_ipp_get_caps_ioctl(struct drm_device *dev, void *data,
+                                 struct drm_file *file_priv)
 {
-       struct drm_exynos_file_private *file_priv = file->driver_priv;
-       struct device *dev = file_priv->ipp_dev;
-       struct ipp_context *ctx = get_ipp_context(dev);
-       struct drm_exynos_ipp_property *property = data;
-       struct exynos_drm_ippdrv *ippdrv;
-       struct drm_exynos_ipp_cmd_node *c_node;
-       u32 prop_id;
-       int ret, i;
-
-       if (!ctx) {
-               DRM_ERROR("invalid context.\n");
-               return -EINVAL;
-       }
-
-       if (!property) {
-               DRM_ERROR("invalid property parameter.\n");
-               return -EINVAL;
-       }
+       struct drm_exynos_ioctl_ipp_get_caps *resp = data;
+       void __user *ptr = (void __user *)(unsigned long)resp->formats_ptr;
+       struct exynos_drm_ipp *ipp;
+       int i;
 
-       prop_id = property->prop_id;
+       ipp = __ipp_get(resp->ipp_id);
+       if (!ipp)
+               return -ENOENT;
 
-       /*
-        * This is log print for user application property.
-        * user application set various property.
-        */
-       for_each_ipp_ops(i)
-               ipp_print_property(property, i);
+       resp->ipp_id = ipp->id;
+       resp->capabilities = ipp->capabilities;
 
        /*
-        * In case prop_id is not zero try to set existing property.
+        * This ioctl is called twice, once to determine how much space is
+        * needed, and the 2nd time to fill it.
         */
-       if (prop_id) {
-               c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock, prop_id);
+       if (resp->formats_count >= ipp->num_formats) {
+               for (i = 0; i < ipp->num_formats; i++) {
+                       struct drm_exynos_ipp_format tmp = {
+                               .fourcc = ipp->formats[i].fourcc,
+                               .type = ipp->formats[i].type,
+                               .modifier = ipp->formats[i].modifier,
+                       };
 
-               if (!c_node || c_node->filp != file) {
-                       DRM_DEBUG_KMS("prop_id[%d] not found\n", prop_id);
-                       return -EINVAL;
-               }
-
-               if (c_node->state != IPP_STATE_STOP) {
-                       DRM_DEBUG_KMS("prop_id[%d] not stopped\n", prop_id);
-                       return -EINVAL;
-               }
-
-               c_node->property = *property;
-
-               ippdrv = ipp_find_drv_by_handle(prop_id);
-               if (IS_ERR(ippdrv)) {
-                       DRM_ERROR("failed to get ipp driver.\n");
-                       return -EINVAL;
+                       if (copy_to_user(ptr, &tmp, sizeof(tmp)))
+                               return -EFAULT;
+                       ptr += sizeof(tmp);
                }
-
-               ret = ipp_check_driver(ippdrv, property);
-               if (ret < 0)
-                       return ret;
-
-               return 0;
-       }
-
-       /* find ipp driver using ipp id */
-       ippdrv = ipp_find_driver(ctx, property);
-       if (IS_ERR(ippdrv)) {
-               DRM_ERROR("failed to get ipp driver.\n");
-               return -EINVAL;
-       }
-
-       /* allocate command node */
-       c_node = kzalloc(sizeof(*c_node), GFP_KERNEL);
-       if (!c_node)
-               return -ENOMEM;
-
-       ret = ipp_create_id(&ctx->prop_idr, &ctx->prop_lock, c_node);
-       if (ret < 0) {
-               DRM_ERROR("failed to create id.\n");
-               goto err_clear;
-       }
-       property->prop_id = ret;
-
-       DRM_DEBUG_KMS("created prop_id[%d]cmd[%d]ippdrv[%p]\n",
-               property->prop_id, property->cmd, ippdrv);
-
-       /* stored property information and ippdrv in private data */
-       c_node->property = *property;
-       c_node->state = IPP_STATE_IDLE;
-       c_node->filp = file;
-
-       c_node->start_work = ipp_create_cmd_work();
-       if (IS_ERR(c_node->start_work)) {
-               DRM_ERROR("failed to create start work.\n");
-               ret = PTR_ERR(c_node->start_work);
-               goto err_remove_id;
-       }
-
-       c_node->stop_work = ipp_create_cmd_work();
-       if (IS_ERR(c_node->stop_work)) {
-               DRM_ERROR("failed to create stop work.\n");
-               ret = PTR_ERR(c_node->stop_work);
-               goto err_free_start;
        }
-
-       c_node->event_work = ipp_create_event_work();
-       if (IS_ERR(c_node->event_work)) {
-               DRM_ERROR("failed to create event work.\n");
-               ret = PTR_ERR(c_node->event_work);
-               goto err_free_stop;
-       }
-
-       mutex_init(&c_node->lock);
-       mutex_init(&c_node->mem_lock);
-       mutex_init(&c_node->event_lock);
-
-       init_completion(&c_node->start_complete);
-       init_completion(&c_node->stop_complete);
-
-       for_each_ipp_ops(i)
-               INIT_LIST_HEAD(&c_node->mem_list[i]);
-
-       INIT_LIST_HEAD(&c_node->event_list);
-       mutex_lock(&ippdrv->cmd_lock);
-       list_add_tail(&c_node->list, &ippdrv->cmd_list);
-       mutex_unlock(&ippdrv->cmd_lock);
-
-       /* make dedicated state without m2m */
-       if (!ipp_is_m2m_cmd(property->cmd))
-               ippdrv->dedicated = true;
+       resp->formats_count = ipp->num_formats;
 
        return 0;
-
-err_free_stop:
-       kfree(c_node->stop_work);
-err_free_start:
-       kfree(c_node->start_work);
-err_remove_id:
-       ipp_remove_id(&ctx->prop_idr, &ctx->prop_lock, property->prop_id);
-err_clear:
-       kfree(c_node);
-       return ret;
 }
 
-static int ipp_validate_mem_node(struct drm_device *drm_dev,
-                                struct drm_exynos_ipp_mem_node *m_node,
-                                struct drm_exynos_ipp_cmd_node *c_node)
+static inline const struct exynos_drm_ipp_formats *__ipp_format_get(
+                               struct exynos_drm_ipp *ipp, uint32_t fourcc,
+                               uint64_t mod, unsigned int type)
 {
-       struct drm_exynos_ipp_config *ipp_cfg;
-       unsigned int num_plane;
-       unsigned long size, buf_size = 0, plane_size, img_size = 0;
-       unsigned int bpp, width, height;
        int i;
 
-       ipp_cfg = &c_node->property.config[m_node->ops_id];
-       num_plane = drm_format_num_planes(ipp_cfg->fmt);
-
-       /**
-        * This is a rather simplified validation of a memory node.
-        * It basically verifies provided gem object handles
-        * and the buffer sizes with respect to current configuration.
-        * This is not the best that can be done
-        * but it seems more than enough
-        */
-       for (i = 0; i < num_plane; ++i) {
-               width = ipp_cfg->sz.hsize;
-               height = ipp_cfg->sz.vsize;
-               bpp = drm_format_plane_cpp(ipp_cfg->fmt, i);
-
-               /*
-                * The result of drm_format_plane_cpp() for chroma planes must
-                * be used with drm_format_xxxx_chroma_subsampling() for
-                * correct result.
-                */
-               if (i > 0) {
-                       width /= drm_format_horz_chroma_subsampling(
-                                                               ipp_cfg->fmt);
-                       height /= drm_format_vert_chroma_subsampling(
-                                                               ipp_cfg->fmt);
-               }
-               plane_size = width * height * bpp;
-               img_size += plane_size;
-
-               if (m_node->buf_info.handles[i]) {
-                       size = exynos_drm_gem_get_size(drm_dev,
-                                       m_node->buf_info.handles[i],
-                                       c_node->filp);
-                       if (plane_size > size) {
-                               DRM_ERROR(
-                                       "buffer %d is smaller than required\n",
-                                       i);
-                               return -EINVAL;
-                       }
-
-                       buf_size += size;
-               }
-       }
-
-       if (buf_size < img_size) {
-               DRM_ERROR("size of buffers(%lu) is smaller than image(%lu)\n",
-                       buf_size, img_size);
-               return -EINVAL;
+       for (i = 0; i < ipp->num_formats; i++) {
+               if ((ipp->formats[i].type & type) &&
+                   ipp->formats[i].fourcc == fourcc &&
+                   ipp->formats[i].modifier == mod)
+                       return &ipp->formats[i];
        }
-
-       return 0;
+       return NULL;
 }
 
-static int ipp_put_mem_node(struct drm_device *drm_dev,
-               struct drm_exynos_ipp_cmd_node *c_node,
-               struct drm_exynos_ipp_mem_node *m_node)
+/**
+ * exynos_drm_ipp_get_limits_ioctl - get ipp module limits
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Construct a structure describing ipp module limitations for provided
+ * picture format.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int exynos_drm_ipp_get_limits_ioctl(struct drm_device *dev, void *data,
+                                   struct drm_file *file_priv)
 {
-       int i;
+       struct drm_exynos_ioctl_ipp_get_limits *resp = data;
+       void __user *ptr = (void __user *)(unsigned long)resp->limits_ptr;
+       const struct exynos_drm_ipp_formats *format;
+       struct exynos_drm_ipp *ipp;
 
-       DRM_DEBUG_KMS("node[%p]\n", m_node);
-
-       if (!m_node) {
-               DRM_ERROR("invalid dequeue node.\n");
-               return -EFAULT;
-       }
+       if (resp->type != DRM_EXYNOS_IPP_FORMAT_SOURCE &&
+           resp->type != DRM_EXYNOS_IPP_FORMAT_DESTINATION)
+               return -EINVAL;
 
-       DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id);
+       ipp = __ipp_get(resp->ipp_id);
+       if (!ipp)
+               return -ENOENT;
 
-       /* put gem buffer */
-       for_each_ipp_planar(i) {
-               unsigned long handle = m_node->buf_info.handles[i];
-               if (handle)
-                       exynos_drm_gem_put_dma_addr(drm_dev, handle,
-                                                       c_node->filp);
-       }
+       format = __ipp_format_get(ipp, resp->fourcc, resp->modifier,
+                                 resp->type);
+       if (!format)
+               return -EINVAL;
 
-       list_del(&m_node->list);
-       kfree(m_node);
+       /*
+        * This ioctl is called twice, once to determine how much space is
+        * needed, and the 2nd time to fill it.
+        */
+       if (format->num_limits && resp->limits_count >= format->num_limits)
+               if (copy_to_user((void __user *)ptr, format->limits,
+                                sizeof(*format->limits) * format->num_limits))
+                       return -EFAULT;
+       resp->limits_count = format->num_limits;
 
        return 0;
 }
 
-static struct drm_exynos_ipp_mem_node
-               *ipp_get_mem_node(struct drm_device *drm_dev,
-               struct drm_exynos_ipp_cmd_node *c_node,
-               struct drm_exynos_ipp_queue_buf *qbuf)
-{
-       struct drm_exynos_ipp_mem_node *m_node;
-       struct drm_exynos_ipp_buf_info *buf_info;
-       int i;
-
-       m_node = kzalloc(sizeof(*m_node), GFP_KERNEL);
-       if (!m_node)
-               return ERR_PTR(-ENOMEM);
-
-       buf_info = &m_node->buf_info;
-
-       /* operations, buffer id */
-       m_node->ops_id = qbuf->ops_id;
-       m_node->prop_id = qbuf->prop_id;
-       m_node->buf_id = qbuf->buf_id;
-       INIT_LIST_HEAD(&m_node->list);
-
-       DRM_DEBUG_KMS("m_node[%p]ops_id[%d]\n", m_node, qbuf->ops_id);
-       DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]\n", qbuf->prop_id, m_node->buf_id);
-
-       for_each_ipp_planar(i) {
-               DRM_DEBUG_KMS("i[%d]handle[0x%x]\n", i, qbuf->handle[i]);
-
-               /* get dma address by handle */
-               if (qbuf->handle[i]) {
-                       dma_addr_t *addr;
-                       unsigned long size;
-
-                       addr = exynos_drm_gem_get_dma_addr(drm_dev,
-                                       qbuf->handle[i], c_node->filp);
-                       if (IS_ERR(addr)) {
-                               DRM_ERROR("failed to get addr.\n");
-                               ipp_put_mem_node(drm_dev, c_node, m_node);
-                               return ERR_PTR(-EFAULT);
-                       }
-
-                       size = exynos_drm_gem_get_size(drm_dev,
-                                       qbuf->handle[i], c_node->filp);
-                       if (!size) {
-                               DRM_ERROR("failed to get size.\n");
-                               ipp_put_mem_node(drm_dev, c_node, m_node);
-                               return ERR_PTR(-EFAULT);
-                       }
-
-                       buf_info->handles[i] = qbuf->handle[i];
-                       buf_info->base[i] = *addr;
-                       buf_info->size[i] = (uint64_t)size;
-                       DRM_DEBUG_KMS("i[%d]base[%pad]hd[0x%lx]sz[%llx]\n", i,
-                                     &buf_info->base[i], buf_info->handles[i],
-                                     buf_info->size[i]);
-               }
-       }
-
-       mutex_lock(&c_node->mem_lock);
-       if (ipp_validate_mem_node(drm_dev, m_node, c_node)) {
-               ipp_put_mem_node(drm_dev, c_node, m_node);
-               mutex_unlock(&c_node->mem_lock);
-               return ERR_PTR(-EFAULT);
-       }
-       list_add_tail(&m_node->list, &c_node->mem_list[qbuf->ops_id]);
-       mutex_unlock(&c_node->mem_lock);
+struct drm_pending_exynos_ipp_event {
+       struct drm_pending_event base;
+       struct drm_exynos_ipp_event event;
+};
 
-       return m_node;
-}
+static inline struct exynos_drm_ipp_task *
+                       exynos_drm_ipp_task_alloc(struct exynos_drm_ipp *ipp)
+{
+       struct exynos_drm_ipp_task *task;
+
+       task = kzalloc(sizeof(*task), GFP_KERNEL);
+       if (!task)
+               return NULL;
+
+       task->dev = ipp->dev;
+       task->ipp = ipp;
+
+       /* some defaults */
+       task->src.rect.w = task->dst.rect.w = UINT_MAX;
+       task->src.rect.h = task->dst.rect.h = UINT_MAX;
+       task->transform.rotation = DRM_MODE_ROTATE_0;
+
+       DRM_DEBUG_DRIVER("Allocated task %pK\n", task);
+
+       return task;
+}
+
+static const struct exynos_drm_param_map {
+       unsigned int id;
+       unsigned int size;
+       unsigned int offset;
+} exynos_drm_ipp_params_maps[] = {
+       {
+               DRM_EXYNOS_IPP_TASK_BUFFER | DRM_EXYNOS_IPP_TASK_TYPE_SOURCE,
+               sizeof(struct drm_exynos_ipp_task_buffer),
+               offsetof(struct exynos_drm_ipp_task, src.buf),
+       }, {
+               DRM_EXYNOS_IPP_TASK_BUFFER |
+                       DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION,
+               sizeof(struct drm_exynos_ipp_task_buffer),
+               offsetof(struct exynos_drm_ipp_task, dst.buf),
+       }, {
+               DRM_EXYNOS_IPP_TASK_RECTANGLE | DRM_EXYNOS_IPP_TASK_TYPE_SOURCE,
+               sizeof(struct drm_exynos_ipp_task_rect),
+               offsetof(struct exynos_drm_ipp_task, src.rect),
+       }, {
+               DRM_EXYNOS_IPP_TASK_RECTANGLE |
+                       DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION,
+               sizeof(struct drm_exynos_ipp_task_rect),
+               offsetof(struct exynos_drm_ipp_task, dst.rect),
+       }, {
+               DRM_EXYNOS_IPP_TASK_TRANSFORM,
+               sizeof(struct drm_exynos_ipp_task_transform),
+               offsetof(struct exynos_drm_ipp_task, transform),
+       }, {
+               DRM_EXYNOS_IPP_TASK_ALPHA,
+               sizeof(struct drm_exynos_ipp_task_alpha),
+               offsetof(struct exynos_drm_ipp_task, alpha),
+       },
+};
 
-static void ipp_clean_mem_nodes(struct drm_device *drm_dev,
-                              struct drm_exynos_ipp_cmd_node *c_node, int ops)
+static int exynos_drm_ipp_task_set(struct exynos_drm_ipp_task *task,
+                                  struct drm_exynos_ioctl_ipp_commit *arg)
 {
-       struct drm_exynos_ipp_mem_node *m_node, *tm_node;
-       struct list_head *head = &c_node->mem_list[ops];
-
-       mutex_lock(&c_node->mem_lock);
-
-       list_for_each_entry_safe(m_node, tm_node, head, list) {
-               int ret;
-
-               ret = ipp_put_mem_node(drm_dev, c_node, m_node);
-               if (ret)
-                       DRM_ERROR("failed to put m_node.\n");
-       }
-       c_node->last_buf_id[ops] = -1;
+       const struct exynos_drm_param_map *map = exynos_drm_ipp_params_maps;
+       void __user *params = (void __user *)(unsigned long)arg->params_ptr;
+       unsigned int size = arg->params_size;
+       uint32_t id;
+       int i;
 
-       mutex_unlock(&c_node->mem_lock);
-}
+       while (size) {
+               if (get_user(id, (uint32_t __user *)params))
+                       return -EFAULT;
 
-static void ipp_free_event(struct drm_pending_event *event)
-{
-       kfree(event);
-}
-
-static int ipp_get_event(struct drm_device *drm_dev,
-               struct drm_exynos_ipp_cmd_node *c_node,
-               struct drm_exynos_ipp_queue_buf *qbuf)
-{
-       struct drm_exynos_ipp_send_event *e;
-       unsigned long flags;
+               for (i = 0; i < ARRAY_SIZE(exynos_drm_ipp_params_maps); i++)
+                       if (map[i].id == id)
+                               break;
+               if (i == ARRAY_SIZE(exynos_drm_ipp_params_maps) ||
+                   map[i].size > size)
+                       return -EINVAL;
 
-       DRM_DEBUG_KMS("ops_id[%d]buf_id[%d]\n", qbuf->ops_id, qbuf->buf_id);
+               if (copy_from_user((void *)task + map[i].offset, params,
+                                  map[i].size))
+                       return -EFAULT;
 
-       e = kzalloc(sizeof(*e), GFP_KERNEL);
-       if (!e) {
-               spin_lock_irqsave(&drm_dev->event_lock, flags);
-               c_node->filp->event_space += sizeof(e->event);
-               spin_unlock_irqrestore(&drm_dev->event_lock, flags);
-               return -ENOMEM;
+               params += map[i].size;
+               size -= map[i].size;
        }
 
-       /* make event */
-       e->event.base.type = DRM_EXYNOS_IPP_EVENT;
-       e->event.base.length = sizeof(e->event);
-       e->event.user_data = qbuf->user_data;
-       e->event.prop_id = qbuf->prop_id;
-       e->event.buf_id[EXYNOS_DRM_OPS_DST] = qbuf->buf_id;
-       e->base.event = &e->event.base;
-       e->base.file_priv = c_node->filp;
-       e->base.destroy = ipp_free_event;
-       mutex_lock(&c_node->event_lock);
-       list_add_tail(&e->base.link, &c_node->event_list);
-       mutex_unlock(&c_node->event_lock);
-
+       DRM_DEBUG_DRIVER("Got task %pK configuration from userspace\n", task);
        return 0;
 }
 
-static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node,
-               struct drm_exynos_ipp_queue_buf *qbuf)
+static int exynos_drm_ipp_task_setup_buffer(struct exynos_drm_ipp_buffer *buf,
+                                           struct drm_file *filp)
 {
-       struct drm_exynos_ipp_send_event *e, *te;
-       int count = 0;
+       int ret = 0;
+       int i;
 
-       mutex_lock(&c_node->event_lock);
-       list_for_each_entry_safe(e, te, &c_node->event_list, base.link) {
-               DRM_DEBUG_KMS("count[%d]e[%p]\n", count++, e);
+       /* basic checks */
+       if (buf->buf.width == 0 || buf->buf.height == 0)
+               return -EINVAL;
+       buf->format = drm_format_info(buf->buf.fourcc);
+       for (i = 0; i < buf->format->num_planes; i++) {
+               unsigned int width = (i == 0) ? buf->buf.width :
+                            DIV_ROUND_UP(buf->buf.width, buf->format->hsub);
+
+               if (buf->buf.pitch[i] == 0)
+                       buf->buf.pitch[i] = width * buf->format->cpp[i];
+               if (buf->buf.pitch[i] < width * buf->format->cpp[i])
+                       return -EINVAL;
+               if (!buf->buf.gem_id[i])
+                       return -ENOENT;
+       }
 
-               /*
-                * qbuf == NULL condition means all event deletion.
-                * stop operations want to delete all event list.
-                * another case delete only same buf id.
-                */
-               if (!qbuf) {
-                       /* delete list */
-                       list_del(&e->base.link);
-                       kfree(e);
+       /* pitch for additional planes must match */
+       if (buf->format->num_planes > 2 &&
+           buf->buf.pitch[1] != buf->buf.pitch[2])
+               return -EINVAL;
+
+       /* get GEM buffers and check their size */
+       for (i = 0; i < buf->format->num_planes; i++) {
+               unsigned int height = (i == 0) ? buf->buf.height :
+                            DIV_ROUND_UP(buf->buf.height, buf->format->vsub);
+               unsigned long size = height * buf->buf.pitch[i] +
+                                    buf->buf.offset[i];
+               struct drm_gem_object *obj = drm_gem_object_lookup(filp,
+                                                           buf->buf.gem_id[i]);
+               if (!obj) {
+                       ret = -ENOENT;
+                       goto gem_free;
                }
+               buf->exynos_gem[i] = to_exynos_gem(obj);
 
-               /* compare buffer id */
-               if (qbuf && (qbuf->buf_id ==
-                   e->event.buf_id[EXYNOS_DRM_OPS_DST])) {
-                       /* delete list */
-                       list_del(&e->base.link);
-                       kfree(e);
-                       goto out_unlock;
+               if (size > buf->exynos_gem[i]->size) {
+                       i++;
+                       ret = -EINVAL;
+                       goto gem_free;
                }
+               buf->dma_addr[i] = buf->exynos_gem[i]->dma_addr +
+                                  buf->buf.offset[i];
        }
 
-out_unlock:
-       mutex_unlock(&c_node->event_lock);
-       return;
+       return 0;
+gem_free:
+       while (i--) {
+               drm_gem_object_put_unlocked(&buf->exynos_gem[i]->base);
+               buf->exynos_gem[i] = NULL;
+       }
+       return ret;
 }
 
-static void ipp_clean_cmd_node(struct ipp_context *ctx,
-                               struct drm_exynos_ipp_cmd_node *c_node)
+static void exynos_drm_ipp_task_release_buf(struct exynos_drm_ipp_buffer *buf)
 {
        int i;
 
-       /* cancel works */
-       cancel_work_sync(&c_node->start_work->work);
-       cancel_work_sync(&c_node->stop_work->work);
-       cancel_work_sync(&c_node->event_work->work);
-
-       /* put event */
-       ipp_put_event(c_node, NULL);
-
-       for_each_ipp_ops(i)
-               ipp_clean_mem_nodes(ctx->subdrv.drm_dev, c_node, i);
-
-       /* delete list */
-       list_del(&c_node->list);
-
-       ipp_remove_id(&ctx->prop_idr, &ctx->prop_lock,
-                       c_node->property.prop_id);
-
-       /* destroy mutex */
-       mutex_destroy(&c_node->lock);
-       mutex_destroy(&c_node->mem_lock);
-       mutex_destroy(&c_node->event_lock);
-
-       /* free command node */
-       kfree(c_node->start_work);
-       kfree(c_node->stop_work);
-       kfree(c_node->event_work);
-       kfree(c_node);
-}
-
-static bool ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node)
-{
-       switch (c_node->property.cmd) {
-       case IPP_CMD_WB:
-               return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_DST]);
-       case IPP_CMD_OUTPUT:
-               return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_SRC]);
-       case IPP_CMD_M2M:
-       default:
-               return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_SRC]) &&
-                      !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_DST]);
-       }
+       if (!buf->exynos_gem[0])
+               return;
+       for (i = 0; i < buf->format->num_planes; i++)
+               drm_gem_object_put_unlocked(&buf->exynos_gem[i]->base);
 }
 
-static struct drm_exynos_ipp_mem_node
-               *ipp_find_mem_node(struct drm_exynos_ipp_cmd_node *c_node,
-               struct drm_exynos_ipp_queue_buf *qbuf)
+static void exynos_drm_ipp_task_free(struct exynos_drm_ipp *ipp,
+                                struct exynos_drm_ipp_task *task)
 {
-       struct drm_exynos_ipp_mem_node *m_node;
-       struct list_head *head;
-       int count = 0;
-
-       DRM_DEBUG_KMS("buf_id[%d]\n", qbuf->buf_id);
-
-       /* source/destination memory list */
-       head = &c_node->mem_list[qbuf->ops_id];
-
-       /* find memory node from memory list */
-       list_for_each_entry(m_node, head, list) {
-               DRM_DEBUG_KMS("count[%d]m_node[%p]\n", count++, m_node);
-
-               /* compare buffer id */
-               if (m_node->buf_id == qbuf->buf_id)
-                       return m_node;
-       }
+       DRM_DEBUG_DRIVER("Freeing task %pK\n", task);
 
-       return NULL;
+       exynos_drm_ipp_task_release_buf(&task->src);
+       exynos_drm_ipp_task_release_buf(&task->dst);
+       if (task->event)
+               drm_event_cancel_free(ipp->dev, &task->event->base);
+       kfree(task);
 }
 
-static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv,
-               struct drm_exynos_ipp_cmd_node *c_node,
-               struct drm_exynos_ipp_mem_node *m_node)
-{
-       struct exynos_drm_ipp_ops *ops = NULL;
-       int ret = 0;
-
-       DRM_DEBUG_KMS("node[%p]\n", m_node);
-
-       if (!m_node) {
-               DRM_ERROR("invalid queue node.\n");
-               return -EFAULT;
-       }
-
-       DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id);
-
-       /* get operations callback */
-       ops = ippdrv->ops[m_node->ops_id];
-       if (!ops) {
-               DRM_ERROR("not support ops.\n");
-               return -EFAULT;
-       }
+struct drm_ipp_limit {
+       struct drm_exynos_ipp_limit_val h;
+       struct drm_exynos_ipp_limit_val v;
+};
 
-       c_node->last_buf_id[m_node->ops_id] = m_node->buf_id;
+enum drm_ipp_size_id {
+       IPP_LIMIT_BUFFER, IPP_LIMIT_AREA, IPP_LIMIT_ROTATED, IPP_LIMIT_MAX
+};
 
-       /* set address and enable irq */
-       if (ops->set_addr) {
-               ret = ops->set_addr(ippdrv->dev, &m_node->buf_info,
-                       m_node->buf_id, IPP_BUF_ENQUEUE);
-               if (ret) {
-                       DRM_ERROR("failed to set addr.\n");
-                       return ret;
-               }
-       }
+static const enum drm_ipp_size_id limit_id_fallback[IPP_LIMIT_MAX][4] = {
+       [IPP_LIMIT_BUFFER]  = { DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER },
+       [IPP_LIMIT_AREA]    = { DRM_EXYNOS_IPP_LIMIT_SIZE_AREA,
+                               DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER },
+       [IPP_LIMIT_ROTATED] = { DRM_EXYNOS_IPP_LIMIT_SIZE_ROTATED,
+                               DRM_EXYNOS_IPP_LIMIT_SIZE_AREA,
+                               DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER },
+};
 
-       return ret;
+static inline void __limit_set_val(unsigned int *ptr, unsigned int val)
+{
+       if (!*ptr)
+               *ptr = val;
 }
 
-static void ipp_handle_cmd_work(struct device *dev,
-               struct exynos_drm_ippdrv *ippdrv,
-               struct drm_exynos_ipp_cmd_work *cmd_work,
-               struct drm_exynos_ipp_cmd_node *c_node)
+static void __get_size_limit(const struct drm_exynos_ipp_limit *limits,
+                            unsigned int num_limits, enum drm_ipp_size_id id,
+                            struct drm_ipp_limit *res)
 {
-       struct ipp_context *ctx = get_ipp_context(dev);
+       const struct drm_exynos_ipp_limit *l = limits;
+       int i = 0;
 
-       cmd_work->ippdrv = ippdrv;
-       cmd_work->c_node = c_node;
-       queue_work(ctx->cmd_workq, &cmd_work->work);
+       memset(res, 0, sizeof(*res));
+       for (i = 0; limit_id_fallback[id][i]; i++)
+               for (l = limits; l - limits < num_limits; l++) {
+                       if (((l->type & DRM_EXYNOS_IPP_LIMIT_TYPE_MASK) !=
+                             DRM_EXYNOS_IPP_LIMIT_TYPE_SIZE) ||
+                           ((l->type & DRM_EXYNOS_IPP_LIMIT_SIZE_MASK) !=
+                                                    limit_id_fallback[id][i]))
+                               continue;
+                       __limit_set_val(&res->h.min, l->h.min);
+                       __limit_set_val(&res->h.max, l->h.max);
+                       __limit_set_val(&res->h.align, l->h.align);
+                       __limit_set_val(&res->v.min, l->v.min);
+                       __limit_set_val(&res->v.max, l->v.max);
+                       __limit_set_val(&res->v.align, l->v.align);
+               }
 }
 
-static int ipp_queue_buf_with_run(struct device *dev,
-               struct drm_exynos_ipp_cmd_node *c_node,
-               struct drm_exynos_ipp_mem_node *m_node,
-               struct drm_exynos_ipp_queue_buf *qbuf)
+static inline bool __align_check(unsigned int val, unsigned int align)
 {
-       struct exynos_drm_ippdrv *ippdrv;
-       struct drm_exynos_ipp_property *property;
-       struct exynos_drm_ipp_ops *ops;
-       int ret;
-
-       ippdrv = ipp_find_drv_by_handle(qbuf->prop_id);
-       if (IS_ERR(ippdrv)) {
-               DRM_ERROR("failed to get ipp driver.\n");
-               return -EFAULT;
-       }
-
-       ops = ippdrv->ops[qbuf->ops_id];
-       if (!ops) {
-               DRM_ERROR("failed to get ops.\n");
-               return -EFAULT;
-       }
-
-       property = &c_node->property;
-
-       if (c_node->state != IPP_STATE_START) {
-               DRM_DEBUG_KMS("bypass for invalid state.\n");
-               return 0;
-       }
-
-       mutex_lock(&c_node->mem_lock);
-       if (!ipp_check_mem_list(c_node)) {
-               mutex_unlock(&c_node->mem_lock);
-               DRM_DEBUG_KMS("empty memory.\n");
-               return 0;
+       if (align && (val & (align - 1))) {
+               DRM_DEBUG_DRIVER("Value %d exceeds HW limits (align %d)\n",
+                                val, align);
+               return false;
        }
-
-       /*
-        * If set destination buffer and enabled clock,
-        * then m2m operations need start operations at queue_buf
-        */
-       if (ipp_is_m2m_cmd(property->cmd)) {
-               struct drm_exynos_ipp_cmd_work *cmd_work = c_node->start_work;
-
-               cmd_work->ctrl = IPP_CTRL_PLAY;
-               ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node);
-       } else {
-               ret = ipp_set_mem_node(ippdrv, c_node, m_node);
-               if (ret) {
-                       mutex_unlock(&c_node->mem_lock);
-                       DRM_ERROR("failed to set m node.\n");
-                       return ret;
-               }
-       }
-       mutex_unlock(&c_node->mem_lock);
-
-       return 0;
+       return true;
 }
 
-static void ipp_clean_queue_buf(struct drm_device *drm_dev,
-               struct drm_exynos_ipp_cmd_node *c_node,
-               struct drm_exynos_ipp_queue_buf *qbuf)
+static inline bool __size_limit_check(unsigned int val,
+                                struct drm_exynos_ipp_limit_val *l)
 {
-       struct drm_exynos_ipp_mem_node *m_node, *tm_node;
-
-       /* delete list */
-       mutex_lock(&c_node->mem_lock);
-       list_for_each_entry_safe(m_node, tm_node,
-               &c_node->mem_list[qbuf->ops_id], list) {
-               if (m_node->buf_id == qbuf->buf_id &&
-                   m_node->ops_id == qbuf->ops_id)
-                       ipp_put_mem_node(drm_dev, c_node, m_node);
+       if ((l->min && val < l->min) || (l->max && val > l->max)) {
+               DRM_DEBUG_DRIVER("Value %d exceeds HW limits (min %d, max %d)\n",
+                                val, l->min, l->max);
+               return false;
        }
-       mutex_unlock(&c_node->mem_lock);
+       return __align_check(val, l->align);
 }
 
-int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
-               struct drm_file *file)
+static int exynos_drm_ipp_check_size_limits(struct exynos_drm_ipp_buffer *buf,
+       const struct drm_exynos_ipp_limit *limits, unsigned int num_limits,
+       bool rotate, bool swap)
 {
-       struct drm_exynos_file_private *file_priv = file->driver_priv;
-       struct device *dev = file_priv->ipp_dev;
-       struct ipp_context *ctx = get_ipp_context(dev);
-       struct drm_exynos_ipp_queue_buf *qbuf = data;
-       struct drm_exynos_ipp_cmd_node *c_node;
-       struct drm_exynos_ipp_mem_node *m_node;
-       int ret;
+       enum drm_ipp_size_id id = rotate ? IPP_LIMIT_ROTATED : IPP_LIMIT_AREA;
+       struct drm_ipp_limit l;
+       struct drm_exynos_ipp_limit_val *lh = &l.h, *lv = &l.v;
 
-       if (!qbuf) {
-               DRM_ERROR("invalid buf parameter.\n");
-               return -EINVAL;
-       }
+       if (!limits)
+               return 0;
 
-       if (qbuf->ops_id >= EXYNOS_DRM_OPS_MAX) {
-               DRM_ERROR("invalid ops parameter.\n");
+       __get_size_limit(limits, num_limits, IPP_LIMIT_BUFFER, &l);
+       if (!__size_limit_check(buf->buf.width, &l.h) ||
+           !__size_limit_check(buf->buf.height, &l.v))
                return -EINVAL;
-       }
-
-       DRM_DEBUG_KMS("prop_id[%d]ops_id[%s]buf_id[%d]buf_type[%d]\n",
-               qbuf->prop_id, qbuf->ops_id ? "dst" : "src",
-               qbuf->buf_id, qbuf->buf_type);
 
-       /* find command node */
-       c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock,
-               qbuf->prop_id);
-       if (!c_node || c_node->filp != file) {
-               DRM_ERROR("failed to get command node.\n");
-               return -ENODEV;
+       if (swap) {
+               lv = &l.h;
+               lh = &l.v;
        }
-
-       /* buffer control */
-       switch (qbuf->buf_type) {
-       case IPP_BUF_ENQUEUE:
-               /* get memory node */
-               m_node = ipp_get_mem_node(drm_dev, c_node, qbuf);
-               if (IS_ERR(m_node)) {
-                       DRM_ERROR("failed to get m_node.\n");
-                       return PTR_ERR(m_node);
-               }
-
-               /*
-                * first step get event for destination buffer.
-                * and second step when M2M case run with destination buffer
-                * if needed.
-                */
-               if (qbuf->ops_id == EXYNOS_DRM_OPS_DST) {
-                       /* get event for destination buffer */
-                       ret = ipp_get_event(drm_dev, c_node, qbuf);
-                       if (ret) {
-                               DRM_ERROR("failed to get event.\n");
-                               goto err_clean_node;
-                       }
-
-                       /*
-                        * M2M case run play control for streaming feature.
-                        * other case set address and waiting.
-                        */
-                       ret = ipp_queue_buf_with_run(dev, c_node, m_node, qbuf);
-                       if (ret) {
-                               DRM_ERROR("failed to run command.\n");
-                               goto err_clean_node;
-                       }
-               }
-               break;
-       case IPP_BUF_DEQUEUE:
-               mutex_lock(&c_node->lock);
-
-               /* put event for destination buffer */
-               if (qbuf->ops_id == EXYNOS_DRM_OPS_DST)
-                       ipp_put_event(c_node, qbuf);
-
-               ipp_clean_queue_buf(drm_dev, c_node, qbuf);
-
-               mutex_unlock(&c_node->lock);
-               break;
-       default:
-               DRM_ERROR("invalid buffer control.\n");
+       __get_size_limit(limits, num_limits, id, &l);
+       if (!__size_limit_check(buf->rect.w, lh) ||
+           !__align_check(buf->rect.x, lh->align) ||
+           !__size_limit_check(buf->rect.h, lv) ||
+           !__align_check(buf->rect.y, lv->align))
                return -EINVAL;
-       }
 
        return 0;
-
-err_clean_node:
-       DRM_ERROR("clean memory nodes.\n");
-
-       ipp_clean_queue_buf(drm_dev, c_node, qbuf);
-       return ret;
 }
 
-static bool exynos_drm_ipp_check_valid(struct device *dev,
-               enum drm_exynos_ipp_ctrl ctrl, enum drm_exynos_ipp_state state)
+static inline bool __scale_limit_check(unsigned int src, unsigned int dst,
+                                      unsigned int min, unsigned int max)
 {
-       if (ctrl != IPP_CTRL_PLAY) {
-               if (pm_runtime_suspended(dev)) {
-                       DRM_ERROR("pm:runtime_suspended.\n");
-                       goto err_status;
-               }
-       }
-
-       switch (ctrl) {
-       case IPP_CTRL_PLAY:
-               if (state != IPP_STATE_IDLE)
-                       goto err_status;
-               break;
-       case IPP_CTRL_STOP:
-               if (state == IPP_STATE_STOP)
-                       goto err_status;
-               break;
-       case IPP_CTRL_PAUSE:
-               if (state != IPP_STATE_START)
-                       goto err_status;
-               break;
-       case IPP_CTRL_RESUME:
-               if (state != IPP_STATE_STOP)
-                       goto err_status;
-               break;
-       default:
-               DRM_ERROR("invalid state.\n");
-               goto err_status;
+       if ((max && (dst << 16) > src * max) ||
+           (min && (dst << 16) < src * min)) {
+               DRM_DEBUG_DRIVER("Scale from %d to %d exceeds HW limits (ratio min %d.%05d, max %d.%05d)\n",
+                        src, dst,
+                        min >> 16, 100000 * (min & 0xffff) / (1 << 16),
+                        max >> 16, 100000 * (max & 0xffff) / (1 << 16));
+               return false;
        }
-
        return true;
-
-err_status:
-       DRM_ERROR("invalid status:ctrl[%d]state[%d]\n", ctrl, state);
-       return false;
 }
 
-int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
-               struct drm_file *file)
+static int exynos_drm_ipp_check_scale_limits(
+                               struct drm_exynos_ipp_task_rect *src,
+                               struct drm_exynos_ipp_task_rect *dst,
+                               const struct drm_exynos_ipp_limit *limits,
+                               unsigned int num_limits, bool swap)
 {
-       struct drm_exynos_file_private *file_priv = file->driver_priv;
-       struct exynos_drm_ippdrv *ippdrv = NULL;
-       struct device *dev = file_priv->ipp_dev;
-       struct ipp_context *ctx = get_ipp_context(dev);
-       struct drm_exynos_ipp_cmd_ctrl *cmd_ctrl = data;
-       struct drm_exynos_ipp_cmd_work *cmd_work;
-       struct drm_exynos_ipp_cmd_node *c_node;
-
-       if (!ctx) {
-               DRM_ERROR("invalid context.\n");
-               return -EINVAL;
-       }
-
-       if (!cmd_ctrl) {
-               DRM_ERROR("invalid control parameter.\n");
-               return -EINVAL;
-       }
-
-       DRM_DEBUG_KMS("ctrl[%d]prop_id[%d]\n",
-               cmd_ctrl->ctrl, cmd_ctrl->prop_id);
-
-       ippdrv = ipp_find_drv_by_handle(cmd_ctrl->prop_id);
-       if (IS_ERR(ippdrv)) {
-               DRM_ERROR("failed to get ipp driver.\n");
-               return PTR_ERR(ippdrv);
-       }
-
-       c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock,
-               cmd_ctrl->prop_id);
-       if (!c_node || c_node->filp != file) {
-               DRM_ERROR("invalid command node list.\n");
-               return -ENODEV;
-       }
-
-       if (!exynos_drm_ipp_check_valid(ippdrv->dev, cmd_ctrl->ctrl,
-           c_node->state)) {
-               DRM_ERROR("invalid state.\n");
-               return -EINVAL;
-       }
+       const struct drm_exynos_ipp_limit_val *lh, *lv;
 
-       switch (cmd_ctrl->ctrl) {
-       case IPP_CTRL_PLAY:
-               if (pm_runtime_suspended(ippdrv->dev))
-                       pm_runtime_get_sync(ippdrv->dev);
-
-               c_node->state = IPP_STATE_START;
-
-               cmd_work = c_node->start_work;
-               cmd_work->ctrl = cmd_ctrl->ctrl;
-               ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node);
-               break;
-       case IPP_CTRL_STOP:
-               cmd_work = c_node->stop_work;
-               cmd_work->ctrl = cmd_ctrl->ctrl;
-               ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node);
-
-               if (!wait_for_completion_timeout(&c_node->stop_complete,
-                   msecs_to_jiffies(300))) {
-                       DRM_ERROR("timeout stop:prop_id[%d]\n",
-                               c_node->property.prop_id);
-               }
+       for (; num_limits; limits++, num_limits--)
+               if ((limits->type & DRM_EXYNOS_IPP_LIMIT_TYPE_MASK) ==
+                   DRM_EXYNOS_IPP_LIMIT_TYPE_SCALE)
+                       break;
+       if (!num_limits)
+               return 0;
 
-               c_node->state = IPP_STATE_STOP;
-               ippdrv->dedicated = false;
-               mutex_lock(&ippdrv->cmd_lock);
-               ipp_clean_cmd_node(ctx, c_node);
-
-               if (list_empty(&ippdrv->cmd_list))
-                       pm_runtime_put_sync(ippdrv->dev);
-               mutex_unlock(&ippdrv->cmd_lock);
-               break;
-       case IPP_CTRL_PAUSE:
-               cmd_work = c_node->stop_work;
-               cmd_work->ctrl = cmd_ctrl->ctrl;
-               ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node);
-
-               if (!wait_for_completion_timeout(&c_node->stop_complete,
-                   msecs_to_jiffies(200))) {
-                       DRM_ERROR("timeout stop:prop_id[%d]\n",
-                               c_node->property.prop_id);
-               }
+       lh = (!swap) ? &limits->h : &limits->v;
+       lv = (!swap) ? &limits->v : &limits->h;
 
-               c_node->state = IPP_STATE_STOP;
-               break;
-       case IPP_CTRL_RESUME:
-               c_node->state = IPP_STATE_START;
-               cmd_work = c_node->start_work;
-               cmd_work->ctrl = cmd_ctrl->ctrl;
-               ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node);
-               break;
-       default:
-               DRM_ERROR("could not support this state currently.\n");
+       if (!__scale_limit_check(src->w, dst->w, lh->min, lh->max) ||
+           !__scale_limit_check(src->h, dst->h, lv->min, lv->max))
                return -EINVAL;
-       }
-
-       DRM_DEBUG_KMS("done ctrl[%d]prop_id[%d]\n",
-               cmd_ctrl->ctrl, cmd_ctrl->prop_id);
 
        return 0;
 }
 
-int exynos_drm_ippnb_register(struct notifier_block *nb)
-{
-       return blocking_notifier_chain_register(
-               &exynos_drm_ippnb_list, nb);
-}
-
-int exynos_drm_ippnb_unregister(struct notifier_block *nb)
+static int exynos_drm_ipp_task_check(struct exynos_drm_ipp_task *task,
+                                    struct drm_file *filp)
 {
-       return blocking_notifier_chain_unregister(
-               &exynos_drm_ippnb_list, nb);
-}
-
-int exynos_drm_ippnb_send_event(unsigned long val, void *v)
-{
-       return blocking_notifier_call_chain(
-               &exynos_drm_ippnb_list, val, v);
-}
-
-static int ipp_set_property(struct exynos_drm_ippdrv *ippdrv,
-               struct drm_exynos_ipp_property *property)
-{
-       struct exynos_drm_ipp_ops *ops = NULL;
-       bool swap = false;
-       int ret, i;
-
-       if (!property) {
-               DRM_ERROR("invalid property parameter.\n");
+       struct exynos_drm_ipp *ipp = task->ipp;
+       const struct exynos_drm_ipp_formats *src_fmt, *dst_fmt;
+       struct exynos_drm_ipp_buffer *src = &task->src, *dst = &task->dst;
+       unsigned int rotation = task->transform.rotation;
+       int ret = 0;
+       bool swap = drm_rotation_90_or_270(rotation);
+       bool rotate = (rotation != DRM_MODE_ROTATE_0);
+       bool scale = false;
+
+       DRM_DEBUG_DRIVER("Checking task %pK\n", task);
+
+       if (src->rect.w == UINT_MAX)
+               src->rect.w = src->buf.width;
+       if (src->rect.h == UINT_MAX)
+               src->rect.h = src->buf.height;
+       if (dst->rect.w == UINT_MAX)
+               dst->rect.w = dst->buf.width;
+       if (dst->rect.h == UINT_MAX)
+               dst->rect.h = dst->buf.height;
+
+       if (src->rect.x + src->rect.w > (src->buf.width) ||
+           src->rect.y + src->rect.h > (src->buf.height) ||
+           dst->rect.x + dst->rect.w > (dst->buf.width) ||
+           dst->rect.y + dst->rect.h > (dst->buf.height)) {
+               DRM_DEBUG_DRIVER("Task %pK: defined area is outside provided buffers\n",
+                                task);
                return -EINVAL;
        }
 
-       DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id);
+       if ((!swap && (src->rect.w != dst->rect.w ||
+                      src->rect.h != dst->rect.h)) ||
+           (swap && (src->rect.w != dst->rect.h ||
+                     src->rect.h != dst->rect.w)))
+               scale = true;
 
-       /* reset h/w block */
-       if (ippdrv->reset &&
-           ippdrv->reset(ippdrv->dev)) {
+       if ((!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_CROP) &&
+            (src->rect.x || src->rect.y || dst->rect.x || dst->rect.y)) ||
+           (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_ROTATE) && rotate) ||
+           (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_SCALE) && scale) ||
+           (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_CONVERT) &&
+            src->buf.fourcc != dst->buf.fourcc)) {
+               DRM_DEBUG_DRIVER("Task %pK: hw capabilities exceeded\n", task);
                return -EINVAL;
        }
 
-       /* set source,destination operations */
-       for_each_ipp_ops(i) {
-               struct drm_exynos_ipp_config *config =
-                       &property->config[i];
-
-               ops = ippdrv->ops[i];
-               if (!ops || !config) {
-                       DRM_ERROR("not support ops and config.\n");
-                       return -EINVAL;
-               }
-
-               /* set format */
-               if (ops->set_fmt) {
-                       ret = ops->set_fmt(ippdrv->dev, config->fmt);
-                       if (ret)
-                               return ret;
-               }
-
-               /* set transform for rotation, flip */
-               if (ops->set_transf) {
-                       ret = ops->set_transf(ippdrv->dev, config->degree,
-                               config->flip, &swap);
-                       if (ret)
-                               return ret;
-               }
-
-               /* set size */
-               if (ops->set_size) {
-                       ret = ops->set_size(ippdrv->dev, swap, &config->pos,
-                               &config->sz);
-                       if (ret)
-                               return ret;
-               }
+       src_fmt = __ipp_format_get(ipp, src->buf.fourcc, src->buf.modifier,
+                                  DRM_EXYNOS_IPP_FORMAT_SOURCE);
+       if (!src_fmt) {
+               DRM_DEBUG_DRIVER("Task %pK: src format not supported\n", task);
+               return -EINVAL;
        }
-
-       return 0;
-}
-
-static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
-               struct drm_exynos_ipp_cmd_node *c_node)
-{
-       struct drm_exynos_ipp_mem_node *m_node;
-       struct drm_exynos_ipp_property *property = &c_node->property;
-       struct list_head *head;
-       int ret, i;
-
-       DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id);
-
-       /* store command info in ippdrv */
-       ippdrv->c_node = c_node;
-
-       mutex_lock(&c_node->mem_lock);
-       if (!ipp_check_mem_list(c_node)) {
-               DRM_DEBUG_KMS("empty memory.\n");
-               ret = -ENOMEM;
-               goto err_unlock;
+       ret = exynos_drm_ipp_check_size_limits(src, src_fmt->limits,
+                                              src_fmt->num_limits,
+                                              rotate, false);
+       if (ret)
+               return ret;
+       ret = exynos_drm_ipp_check_scale_limits(&src->rect, &dst->rect,
+                                               src_fmt->limits,
+                                               src_fmt->num_limits, swap);
+       if (ret)
+               return ret;
+
+       dst_fmt = __ipp_format_get(ipp, dst->buf.fourcc, dst->buf.modifier,
+                                  DRM_EXYNOS_IPP_FORMAT_DESTINATION);
+       if (!dst_fmt) {
+               DRM_DEBUG_DRIVER("Task %pK: dst format not supported\n", task);
+               return -EINVAL;
        }
-
-       /* set current property in ippdrv */
-       ret = ipp_set_property(ippdrv, property);
+       ret = exynos_drm_ipp_check_size_limits(dst, dst_fmt->limits,
+                                              dst_fmt->num_limits,
+                                              rotate, swap);
+       if (ret)
+               return ret;
+       ret = exynos_drm_ipp_check_scale_limits(&src->rect, &dst->rect,
+                                               dst_fmt->limits,
+                                               dst_fmt->num_limits, swap);
+       if (ret)
+               return ret;
+
+       ret = exynos_drm_ipp_task_setup_buffer(src, filp);
        if (ret) {
-               DRM_ERROR("failed to set property.\n");
-               ippdrv->c_node = NULL;
-               goto err_unlock;
+               DRM_DEBUG_DRIVER("Task %pK: src buffer setup failed\n", task);
+               return ret;
        }
-
-       /* check command */
-       switch (property->cmd) {
-       case IPP_CMD_M2M:
-               for_each_ipp_ops(i) {
-                       /* source/destination memory list */
-                       head = &c_node->mem_list[i];
-
-                       m_node = list_first_entry(head,
-                               struct drm_exynos_ipp_mem_node, list);
-
-                       DRM_DEBUG_KMS("m_node[%p]\n", m_node);
-
-                       ret = ipp_set_mem_node(ippdrv, c_node, m_node);
-                       if (ret) {
-                               DRM_ERROR("failed to set m node.\n");
-                               goto err_unlock;
-                       }
-               }
-               break;
-       case IPP_CMD_WB:
-               /* destination memory list */
-               head = &c_node->mem_list[EXYNOS_DRM_OPS_DST];
-
-               list_for_each_entry(m_node, head, list) {
-                       ret = ipp_set_mem_node(ippdrv, c_node, m_node);
-                       if (ret) {
-                               DRM_ERROR("failed to set m node.\n");
-                               goto err_unlock;
-                       }
-               }
-               break;
-       case IPP_CMD_OUTPUT:
-               /* source memory list */
-               head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC];
-
-               list_for_each_entry(m_node, head, list) {
-                       ret = ipp_set_mem_node(ippdrv, c_node, m_node);
-                       if (ret) {
-                               DRM_ERROR("failed to set m node.\n");
-                               goto err_unlock;
-                       }
-               }
-               break;
-       default:
-               DRM_ERROR("invalid operations.\n");
-               ret = -EINVAL;
-               goto err_unlock;
-       }
-       mutex_unlock(&c_node->mem_lock);
-
-       DRM_DEBUG_KMS("cmd[%d]\n", property->cmd);
-
-       /* start operations */
-       if (ippdrv->start) {
-               ret = ippdrv->start(ippdrv->dev, property->cmd);
-               if (ret) {
-                       DRM_ERROR("failed to start ops.\n");
-                       ippdrv->c_node = NULL;
-                       return ret;
-               }
+       ret = exynos_drm_ipp_task_setup_buffer(dst, filp);
+       if (ret) {
+               DRM_DEBUG_DRIVER("Task %pK: dst buffer setup failed\n", task);
+               return ret;
        }
 
-       return 0;
+       DRM_DEBUG_DRIVER("Task %pK: all checks done.\n", task);
 
-err_unlock:
-       mutex_unlock(&c_node->mem_lock);
-       ippdrv->c_node = NULL;
        return ret;
 }
 
-static int ipp_stop_property(struct drm_device *drm_dev,
-               struct exynos_drm_ippdrv *ippdrv,
-               struct drm_exynos_ipp_cmd_node *c_node)
-{
-       struct drm_exynos_ipp_property *property = &c_node->property;
-       int i;
 
-       DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id);
-
-       /* stop operations */
-       if (ippdrv->stop)
-               ippdrv->stop(ippdrv->dev, property->cmd);
-
-       /* check command */
-       switch (property->cmd) {
-       case IPP_CMD_M2M:
-               for_each_ipp_ops(i)
-                       ipp_clean_mem_nodes(drm_dev, c_node, i);
-               break;
-       case IPP_CMD_WB:
-               ipp_clean_mem_nodes(drm_dev, c_node, EXYNOS_DRM_OPS_DST);
-               break;
-       case IPP_CMD_OUTPUT:
-               ipp_clean_mem_nodes(drm_dev, c_node, EXYNOS_DRM_OPS_SRC);
-               break;
-       default:
-               DRM_ERROR("invalid operations.\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-void ipp_sched_cmd(struct work_struct *work)
+static int exynos_drm_ipp_event_create(struct exynos_drm_ipp_task *task,
+                                struct drm_file *file_priv, uint64_t user_data)
 {
-       struct drm_exynos_ipp_cmd_work *cmd_work =
-               container_of(work, struct drm_exynos_ipp_cmd_work, work);
-       struct exynos_drm_ippdrv *ippdrv;
-       struct drm_exynos_ipp_cmd_node *c_node;
-       struct drm_exynos_ipp_property *property;
+       struct drm_pending_exynos_ipp_event *e = NULL;
        int ret;
 
-       ippdrv = cmd_work->ippdrv;
-       if (!ippdrv) {
-               DRM_ERROR("invalid ippdrv list.\n");
-               return;
-       }
-
-       c_node = cmd_work->c_node;
-       if (!c_node) {
-               DRM_ERROR("invalid command node list.\n");
-               return;
-       }
-
-       mutex_lock(&c_node->lock);
+       e = kzalloc(sizeof(*e), GFP_KERNEL);
+       if (!e)
+               return -ENOMEM;
 
-       property = &c_node->property;
+       e->event.base.type = DRM_EXYNOS_IPP_EVENT;
+       e->event.base.length = sizeof(e->event);
+       e->event.user_data = user_data;
 
-       switch (cmd_work->ctrl) {
-       case IPP_CTRL_PLAY:
-       case IPP_CTRL_RESUME:
-               ret = ipp_start_property(ippdrv, c_node);
-               if (ret) {
-                       DRM_ERROR("failed to start property:prop_id[%d]\n",
-                               c_node->property.prop_id);
-                       goto err_unlock;
-               }
+       ret = drm_event_reserve_init(task->dev, file_priv, &e->base,
+                                    &e->event.base);
+       if (ret)
+               goto free;
 
-               /*
-                * M2M case supports wait_completion of transfer.
-                * because M2M case supports single unit operation
-                * with multiple queue.
-                * M2M need to wait completion of data transfer.
-                */
-               if (ipp_is_m2m_cmd(property->cmd)) {
-                       if (!wait_for_completion_timeout
-                           (&c_node->start_complete, msecs_to_jiffies(200))) {
-                               DRM_ERROR("timeout event:prop_id[%d]\n",
-                                       c_node->property.prop_id);
-                               goto err_unlock;
-                       }
-               }
-               break;
-       case IPP_CTRL_STOP:
-       case IPP_CTRL_PAUSE:
-               ret = ipp_stop_property(ippdrv->drm_dev, ippdrv,
-                       c_node);
-               if (ret) {
-                       DRM_ERROR("failed to stop property.\n");
-                       goto err_unlock;
-               }
+       task->event = e;
+       return 0;
+free:
+       kfree(e);
+       return ret;
+}
 
-               complete(&c_node->stop_complete);
-               break;
-       default:
-               DRM_ERROR("unknown control type\n");
-               break;
-       }
+static void exynos_drm_ipp_event_send(struct exynos_drm_ipp_task *task)
+{
+       struct timespec64 now;
 
-       DRM_DEBUG_KMS("ctrl[%d] done.\n", cmd_work->ctrl);
+       ktime_get_ts64(&now);
+       task->event->event.tv_sec = now.tv_sec;
+       task->event->event.tv_usec = now.tv_nsec / NSEC_PER_USEC;
+       task->event->event.sequence = atomic_inc_return(&task->ipp->sequence);
 
-err_unlock:
-       mutex_unlock(&c_node->lock);
+       drm_send_event(task->dev, &task->event->base);
 }
 
-static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
-               struct drm_exynos_ipp_cmd_node *c_node, int *buf_id)
+static int exynos_drm_ipp_task_cleanup(struct exynos_drm_ipp_task *task)
 {
-       struct drm_device *drm_dev = ippdrv->drm_dev;
-       struct drm_exynos_ipp_property *property = &c_node->property;
-       struct drm_exynos_ipp_mem_node *m_node;
-       struct drm_exynos_ipp_queue_buf qbuf;
-       struct drm_exynos_ipp_send_event *e;
-       struct list_head *head;
-       struct timeval now;
-       unsigned long flags;
-       u32 tbuf_id[EXYNOS_DRM_OPS_MAX] = {0, };
-       int ret, i;
-
-       for_each_ipp_ops(i)
-               DRM_DEBUG_KMS("%s buf_id[%d]\n", i ? "dst" : "src", buf_id[i]);
-
-       if (!drm_dev) {
-               DRM_ERROR("failed to get drm_dev.\n");
-               return -EINVAL;
-       }
+       int ret = task->ret;
 
-       mutex_lock(&c_node->event_lock);
-       if (list_empty(&c_node->event_list)) {
-               DRM_DEBUG_KMS("event list is empty.\n");
-               ret = 0;
-               goto err_event_unlock;
+       if (ret == 0 && task->event) {
+               exynos_drm_ipp_event_send(task);
+               /* ensure event won't be canceled on task free */
+               task->event = NULL;
        }
 
-       mutex_lock(&c_node->mem_lock);
-       if (!ipp_check_mem_list(c_node)) {
-               DRM_DEBUG_KMS("empty memory.\n");
-               ret = 0;
-               goto err_mem_unlock;
-       }
+       exynos_drm_ipp_task_free(task->ipp, task);
+       return ret;
+}
 
-       /* check command */
-       switch (property->cmd) {
-       case IPP_CMD_M2M:
-               for_each_ipp_ops(i) {
-                       /* source/destination memory list */
-                       head = &c_node->mem_list[i];
+static void exynos_drm_ipp_cleanup_work(struct work_struct *work)
+{
+       struct exynos_drm_ipp_task *task = container_of(work,
+                                     struct exynos_drm_ipp_task, cleanup_work);
 
-                       m_node = list_first_entry(head,
-                               struct drm_exynos_ipp_mem_node, list);
+       exynos_drm_ipp_task_cleanup(task);
+}
 
-                       tbuf_id[i] = m_node->buf_id;
-                       DRM_DEBUG_KMS("%s buf_id[%d]\n",
-                               i ? "dst" : "src", tbuf_id[i]);
+static void exynos_drm_ipp_next_task(struct exynos_drm_ipp *ipp);
 
-                       ret = ipp_put_mem_node(drm_dev, c_node, m_node);
-                       if (ret)
-                               DRM_ERROR("failed to put m_node.\n");
-               }
-               break;
-       case IPP_CMD_WB:
-               /* clear buf for finding */
-               memset(&qbuf, 0x0, sizeof(qbuf));
-               qbuf.ops_id = EXYNOS_DRM_OPS_DST;
-               qbuf.buf_id = buf_id[EXYNOS_DRM_OPS_DST];
-
-               /* get memory node entry */
-               m_node = ipp_find_mem_node(c_node, &qbuf);
-               if (!m_node) {
-                       DRM_ERROR("empty memory node.\n");
-                       ret = -ENOMEM;
-                       goto err_mem_unlock;
-               }
-
-               tbuf_id[EXYNOS_DRM_OPS_DST] = m_node->buf_id;
+/**
+ * exynos_drm_ipp_task_done - finish given task and set return code
+ * @task: ipp task to finish
+ * @ret: error code or 0 if operation has been performed successfully
+ */
+void exynos_drm_ipp_task_done(struct exynos_drm_ipp_task *task, int ret)
+{
+       struct exynos_drm_ipp *ipp = task->ipp;
+       unsigned long flags;
 
-               ret = ipp_put_mem_node(drm_dev, c_node, m_node);
-               if (ret)
-                       DRM_ERROR("failed to put m_node.\n");
-               break;
-       case IPP_CMD_OUTPUT:
-               /* source memory list */
-               head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC];
+       DRM_DEBUG_DRIVER("ipp: %d, task %pK done\n", ipp->id, task);
 
-               m_node = list_first_entry(head,
-                       struct drm_exynos_ipp_mem_node, list);
+       spin_lock_irqsave(&ipp->lock, flags);
+       if (ipp->task == task)
+               ipp->task = NULL;
+       task->flags |= DRM_EXYNOS_IPP_TASK_DONE;
+       task->ret = ret;
+       spin_unlock_irqrestore(&ipp->lock, flags);
 
-               tbuf_id[EXYNOS_DRM_OPS_SRC] = m_node->buf_id;
+       exynos_drm_ipp_next_task(ipp);
+       wake_up(&ipp->done_wq);
 
-               ret = ipp_put_mem_node(drm_dev, c_node, m_node);
-               if (ret)
-                       DRM_ERROR("failed to put m_node.\n");
-               break;
-       default:
-               DRM_ERROR("invalid operations.\n");
-               ret = -EINVAL;
-               goto err_mem_unlock;
+       if (task->flags & DRM_EXYNOS_IPP_TASK_ASYNC) {
+               INIT_WORK(&task->cleanup_work, exynos_drm_ipp_cleanup_work);
+               schedule_work(&task->cleanup_work);
        }
-       mutex_unlock(&c_node->mem_lock);
-
-       if (tbuf_id[EXYNOS_DRM_OPS_DST] != buf_id[EXYNOS_DRM_OPS_DST])
-               DRM_ERROR("failed to match buf_id[%d %d]prop_id[%d]\n",
-                       tbuf_id[1], buf_id[1], property->prop_id);
-
-       /*
-        * command node have event list of destination buffer
-        * If destination buffer enqueue to mem list,
-        * then we make event and link to event list tail.
-        * so, we get first event for first enqueued buffer.
-        */
-       e = list_first_entry(&c_node->event_list,
-               struct drm_exynos_ipp_send_event, base.link);
-
-       do_gettimeofday(&now);
-       DRM_DEBUG_KMS("tv_sec[%ld]tv_usec[%ld]\n", now.tv_sec, now.tv_usec);
-       e->event.tv_sec = now.tv_sec;
-       e->event.tv_usec = now.tv_usec;
-       e->event.prop_id = property->prop_id;
-
-       /* set buffer id about source destination */
-       for_each_ipp_ops(i)
-               e->event.buf_id[i] = tbuf_id[i];
-
-       spin_lock_irqsave(&drm_dev->event_lock, flags);
-       list_move_tail(&e->base.link, &e->base.file_priv->event_list);
-       wake_up_interruptible(&e->base.file_priv->event_wait);
-       spin_unlock_irqrestore(&drm_dev->event_lock, flags);
-       mutex_unlock(&c_node->event_lock);
-
-       DRM_DEBUG_KMS("done cmd[%d]prop_id[%d]buf_id[%d]\n",
-               property->cmd, property->prop_id, tbuf_id[EXYNOS_DRM_OPS_DST]);
-
-       return 0;
-
-err_mem_unlock:
-       mutex_unlock(&c_node->mem_lock);
-err_event_unlock:
-       mutex_unlock(&c_node->event_lock);
-       return ret;
 }
 
-void ipp_sched_event(struct work_struct *work)
+static void exynos_drm_ipp_next_task(struct exynos_drm_ipp *ipp)
 {
-       struct drm_exynos_ipp_event_work *event_work;
-       struct exynos_drm_ippdrv *ippdrv;
-       struct drm_exynos_ipp_cmd_node *c_node;
+       struct exynos_drm_ipp_task *task;
+       unsigned long flags;
        int ret;
 
-       if (!work) {
-               DRM_ERROR("work is NULL\n");
-               return;
-       }
-       event_work = container_of(work, struct drm_exynos_ipp_event_work, work);
+       DRM_DEBUG_DRIVER("ipp: %d, try to run new task\n", ipp->id);
 
-       DRM_DEBUG_KMS("buf_id[%d]\n", event_work->buf_id[EXYNOS_DRM_OPS_DST]);
+       spin_lock_irqsave(&ipp->lock, flags);
 
-       ippdrv = event_work->ippdrv;
-       if (!ippdrv) {
-               DRM_ERROR("failed to get ipp driver.\n");
+       if (ipp->task || list_empty(&ipp->todo_list)) {
+               spin_unlock_irqrestore(&ipp->lock, flags);
                return;
        }
 
-       c_node = ippdrv->c_node;
-       if (!c_node) {
-               DRM_ERROR("failed to get command node.\n");
-               return;
-       }
+       task = list_first_entry(&ipp->todo_list, struct exynos_drm_ipp_task,
+                               head);
+       list_del_init(&task->head);
+       ipp->task = task;
 
-       event_work->buf_id[EXYNOS_DRM_OPS_SRC] =
-                               c_node->last_buf_id[EXYNOS_DRM_OPS_SRC];
-       event_work->buf_id[EXYNOS_DRM_OPS_DST] =
-                               c_node->last_buf_id[EXYNOS_DRM_OPS_DST];
+       spin_unlock_irqrestore(&ipp->lock, flags);
 
-       /*
-        * IPP supports command thread, event thread synchronization.
-        * If IPP close immediately from user land, then IPP make
-        * synchronization with command thread, so make complete event.
-        * or going out operations.
-        */
-       if (c_node->state != IPP_STATE_START) {
-               DRM_DEBUG_KMS("bypass state[%d]prop_id[%d]\n",
-                       c_node->state, c_node->property.prop_id);
-               goto err_completion;
-       }
+       DRM_DEBUG_DRIVER("ipp: %d, selected task %pK to run\n", ipp->id, task);
 
-       ret = ipp_send_event(ippdrv, c_node, event_work->buf_id);
-       if (ret) {
-               DRM_ERROR("failed to send event.\n");
-               goto err_completion;
-       }
-
-err_completion:
-       if (ipp_is_m2m_cmd(c_node->property.cmd))
-               complete(&c_node->start_complete);
+       ret = ipp->funcs->commit(ipp, task);
+       if (ret)
+               exynos_drm_ipp_task_done(task, ret);
 }
 
-static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
+static void exynos_drm_ipp_schedule_task(struct exynos_drm_ipp *ipp,
+                                        struct exynos_drm_ipp_task *task)
 {
-       struct ipp_context *ctx = get_ipp_context(dev);
-       struct exynos_drm_ippdrv *ippdrv;
-       int ret, count = 0;
-
-       /* get ipp driver entry */
-       list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
-               ippdrv->drm_dev = drm_dev;
-
-               ret = ipp_create_id(&ctx->ipp_idr, &ctx->ipp_lock, ippdrv);
-               if (ret < 0) {
-                       DRM_ERROR("failed to create id.\n");
-                       goto err;
-               }
-               ippdrv->prop_list.ipp_id = ret;
-
-               DRM_DEBUG_KMS("count[%d]ippdrv[%p]ipp_id[%d]\n",
-                       count++, ippdrv, ret);
-
-               /* store parent device for node */
-               ippdrv->parent_dev = dev;
-
-               /* store event work queue and handler */
-               ippdrv->event_workq = ctx->event_workq;
-               ippdrv->sched_event = ipp_sched_event;
-               INIT_LIST_HEAD(&ippdrv->cmd_list);
-               mutex_init(&ippdrv->cmd_lock);
-
-               if (is_drm_iommu_supported(drm_dev)) {
-                       ret = drm_iommu_attach_device(drm_dev, ippdrv->dev);
-                       if (ret) {
-                               DRM_ERROR("failed to activate iommu\n");
-                               goto err;
-                       }
-               }
-       }
-
-       return 0;
-
-err:
-       /* get ipp driver entry */
-       list_for_each_entry_continue_reverse(ippdrv, &exynos_drm_ippdrv_list,
-                                               drv_list) {
-               if (is_drm_iommu_supported(drm_dev))
-                       drm_iommu_detach_device(drm_dev, ippdrv->dev);
+       unsigned long flags;
 
-               ipp_remove_id(&ctx->ipp_idr, &ctx->ipp_lock,
-                               ippdrv->prop_list.ipp_id);
-       }
+       spin_lock_irqsave(&ipp->lock, flags);
+       list_add(&task->head, &ipp->todo_list);
+       spin_unlock_irqrestore(&ipp->lock, flags);
 
-       return ret;
+       exynos_drm_ipp_next_task(ipp);
 }
 
-static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
+static void exynos_drm_ipp_task_abort(struct exynos_drm_ipp *ipp,
+                                     struct exynos_drm_ipp_task *task)
 {
-       struct exynos_drm_ippdrv *ippdrv, *t;
-       struct ipp_context *ctx = get_ipp_context(dev);
-
-       /* get ipp driver entry */
-       list_for_each_entry_safe(ippdrv, t, &exynos_drm_ippdrv_list, drv_list) {
-               if (is_drm_iommu_supported(drm_dev))
-                       drm_iommu_detach_device(drm_dev, ippdrv->dev);
-
-               ipp_remove_id(&ctx->ipp_idr, &ctx->ipp_lock,
-                               ippdrv->prop_list.ipp_id);
+       unsigned long flags;
 
-               ippdrv->drm_dev = NULL;
-               exynos_drm_ippdrv_unregister(ippdrv);
+       spin_lock_irqsave(&ipp->lock, flags);
+       if (task->flags & DRM_EXYNOS_IPP_TASK_DONE) {
+               /* already completed task */
+               exynos_drm_ipp_task_cleanup(task);
+       } else if (ipp->task != task) {
+               /* task has not been scheduled for execution yet */
+               list_del_init(&task->head);
+               exynos_drm_ipp_task_cleanup(task);
+       } else {
+               /*
+                * currently processed task, call abort() and perform
+                * cleanup with async worker
+                */
+               task->flags |= DRM_EXYNOS_IPP_TASK_ASYNC;
+               spin_unlock_irqrestore(&ipp->lock, flags);
+               if (ipp->funcs->abort)
+                       ipp->funcs->abort(ipp, task);
+               return;
        }
+       spin_unlock_irqrestore(&ipp->lock, flags);
 }
 
-static int ipp_subdrv_open(struct drm_device *drm_dev, struct device *dev,
-               struct drm_file *file)
+/**
+ * exynos_drm_ipp_commit_ioctl - perform image processing operation
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Construct a ipp task from the set of properties provided from the user
+ * and try to schedule it to framebuffer processor hardware.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int exynos_drm_ipp_commit_ioctl(struct drm_device *dev, void *data,
+                               struct drm_file *file_priv)
 {
-       struct drm_exynos_file_private *file_priv = file->driver_priv;
-
-       file_priv->ipp_dev = dev;
-
-       DRM_DEBUG_KMS("done priv[%p]\n", dev);
-
-       return 0;
-}
+       struct drm_exynos_ioctl_ipp_commit *arg = data;
+       struct exynos_drm_ipp *ipp;
+       struct exynos_drm_ipp_task *task;
+       int ret = 0;
 
-static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev,
-               struct drm_file *file)
-{
-       struct exynos_drm_ippdrv *ippdrv = NULL;
-       struct ipp_context *ctx = get_ipp_context(dev);
-       struct drm_exynos_ipp_cmd_node *c_node, *tc_node;
-       int count = 0;
-
-       list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
-               mutex_lock(&ippdrv->cmd_lock);
-               list_for_each_entry_safe(c_node, tc_node,
-                       &ippdrv->cmd_list, list) {
-                       DRM_DEBUG_KMS("count[%d]ippdrv[%p]\n",
-                               count++, ippdrv);
-
-                       if (c_node->filp == file) {
-                               /*
-                                * userland goto unnormal state. process killed.
-                                * and close the file.
-                                * so, IPP didn't called stop cmd ctrl.
-                                * so, we are make stop operation in this state.
-                                */
-                               if (c_node->state == IPP_STATE_START) {
-                                       ipp_stop_property(drm_dev, ippdrv,
-                                               c_node);
-                                       c_node->state = IPP_STATE_STOP;
-                               }
-
-                               ippdrv->dedicated = false;
-                               ipp_clean_cmd_node(ctx, c_node);
-                               if (list_empty(&ippdrv->cmd_list))
-                                       pm_runtime_put_sync(ippdrv->dev);
-                       }
-               }
-               mutex_unlock(&ippdrv->cmd_lock);
-       }
+       if ((arg->flags & ~DRM_EXYNOS_IPP_FLAGS) || arg->reserved)
+               return -EINVAL;
 
-       return;
-}
+       /* can't test and expect an event at the same time */
+       if ((arg->flags & DRM_EXYNOS_IPP_FLAG_TEST_ONLY) &&
+                       (arg->flags & DRM_EXYNOS_IPP_FLAG_EVENT))
+               return -EINVAL;
 
-static int ipp_probe(struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-       struct ipp_context *ctx;
-       struct exynos_drm_subdrv *subdrv;
-       int ret;
+       ipp = __ipp_get(arg->ipp_id);
+       if (!ipp)
+               return -ENOENT;
 
-       ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
-       if (!ctx)
+       task = exynos_drm_ipp_task_alloc(ipp);
+       if (!task)
                return -ENOMEM;
 
-       mutex_init(&ctx->ipp_lock);
-       mutex_init(&ctx->prop_lock);
+       ret = exynos_drm_ipp_task_set(task, arg);
+       if (ret)
+               goto free;
 
-       idr_init(&ctx->ipp_idr);
-       idr_init(&ctx->prop_idr);
+       ret = exynos_drm_ipp_task_check(task, file_priv);
+       if (ret || arg->flags & DRM_EXYNOS_IPP_FLAG_TEST_ONLY)
+               goto free;
 
-       /*
-        * create single thread for ipp event
-        * IPP supports event thread for IPP drivers.
-        * IPP driver send event_work to this thread.
-        * and IPP event thread send event to user process.
-        */
-       ctx->event_workq = create_singlethread_workqueue("ipp_event");
-       if (!ctx->event_workq) {
-               dev_err(dev, "failed to create event workqueue\n");
-               return -EINVAL;
+       if (arg->flags & DRM_EXYNOS_IPP_FLAG_EVENT) {
+               ret = exynos_drm_ipp_event_create(task, file_priv,
+                                                arg->user_data);
+               if (ret)
+                       goto free;
        }
 
        /*
-        * create single thread for ipp command
-        * IPP supports command thread for user process.
-        * user process make command node using set property ioctl.
-        * and make start_work and send this work to command thread.
-        * and then this command thread start property.
+        * Queue task for processing on the hardware. task object will be
+        * then freed after exynos_drm_ipp_task_done()
         */
-       ctx->cmd_workq = create_singlethread_workqueue("ipp_cmd");
-       if (!ctx->cmd_workq) {
-               dev_err(dev, "failed to create cmd workqueue\n");
-               ret = -EINVAL;
-               goto err_event_workq;
-       }
+       if (arg->flags & DRM_EXYNOS_IPP_FLAG_NONBLOCK) {
+               DRM_DEBUG_DRIVER("ipp: %d, nonblocking processing task %pK\n",
+                                ipp->id, task);
 
-       /* set sub driver informations */
-       subdrv = &ctx->subdrv;
-       subdrv->dev = dev;
-       subdrv->probe = ipp_subdrv_probe;
-       subdrv->remove = ipp_subdrv_remove;
-       subdrv->open = ipp_subdrv_open;
-       subdrv->close = ipp_subdrv_close;
-
-       platform_set_drvdata(pdev, ctx);
-
-       ret = exynos_drm_subdrv_register(subdrv);
-       if (ret < 0) {
-               DRM_ERROR("failed to register drm ipp device.\n");
-               goto err_cmd_workq;
+               task->flags |= DRM_EXYNOS_IPP_TASK_ASYNC;
+               exynos_drm_ipp_schedule_task(task->ipp, task);
+               ret = 0;
+       } else {
+               DRM_DEBUG_DRIVER("ipp: %d, processing task %pK\n", ipp->id,
+                                task);
+               exynos_drm_ipp_schedule_task(ipp, task);
+               ret = wait_event_interruptible(ipp->done_wq,
+                                       task->flags & DRM_EXYNOS_IPP_TASK_DONE);
+               if (ret)
+                       exynos_drm_ipp_task_abort(ipp, task);
+               else
+                       ret = exynos_drm_ipp_task_cleanup(task);
        }
-
-       dev_info(dev, "drm ipp registered successfully.\n");
-
-       return 0;
-
-err_cmd_workq:
-       destroy_workqueue(ctx->cmd_workq);
-err_event_workq:
-       destroy_workqueue(ctx->event_workq);
        return ret;
-}
-
-static int ipp_remove(struct platform_device *pdev)
-{
-       struct ipp_context *ctx = platform_get_drvdata(pdev);
-
-       /* unregister sub driver */
-       exynos_drm_subdrv_unregister(&ctx->subdrv);
-
-       /* remove,destroy ipp idr */
-       idr_destroy(&ctx->ipp_idr);
-       idr_destroy(&ctx->prop_idr);
-
-       mutex_destroy(&ctx->ipp_lock);
-       mutex_destroy(&ctx->prop_lock);
+free:
+       exynos_drm_ipp_task_free(ipp, task);
 
-       /* destroy command, event work queue */
-       destroy_workqueue(ctx->cmd_workq);
-       destroy_workqueue(ctx->event_workq);
-
-       return 0;
+       return ret;
 }
-
-struct platform_driver ipp_driver = {
-       .probe          = ipp_probe,
-       .remove         = ipp_remove,
-       .driver         = {
-               .name   = "exynos-drm-ipp",
-               .owner  = THIS_MODULE,
-       },
-};
-
index a09fc1152186de297b10ba0e4475a110f181fb94..0b27d4a9bf94ea854fa63c4865761c8bd0c810f3 100644 (file)
@@ -1,10 +1,5 @@
 /*
- * Copyright (c) 2012 Samsung Electronics Co., Ltd.
- *
- * Authors:
- *     Eunchul Kim <chulspro.kim@samsung.com>
- *     Jinyoung Jeon <jy0.jeon@samsung.com>
- *     Sangmin Lee <lsmin.lee@samsung.com>
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
  *
  * 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
 #ifndef _EXYNOS_DRM_IPP_H_
 #define _EXYNOS_DRM_IPP_H_
 
-#define for_each_ipp_ops(pos)  \
-       for (pos = 0; pos < EXYNOS_DRM_OPS_MAX; pos++)
-#define for_each_ipp_planar(pos)       \
-       for (pos = 0; pos < EXYNOS_DRM_PLANAR_MAX; pos++)
-
-#define IPP_GET_LCD_WIDTH      _IOR('F', 302, int)
-#define IPP_GET_LCD_HEIGHT     _IOR('F', 303, int)
-#define IPP_SET_WRITEBACK      _IOW('F', 304, u32)
+#include <drm/drmP.h>
 
-/* definition of state */
-enum drm_exynos_ipp_state {
-       IPP_STATE_IDLE,
-       IPP_STATE_START,
-       IPP_STATE_STOP,
-};
+struct exynos_drm_ipp;
+struct exynos_drm_ipp_task;
 
-/*
- * A structure of command work information.
- * @work: work structure.
- * @ippdrv: current work ippdrv.
- * @c_node: command node information.
- * @ctrl: command control.
+/**
+ * struct exynos_drm_ipp_funcs - exynos_drm_ipp control functions
  */
-struct drm_exynos_ipp_cmd_work {
-       struct work_struct      work;
-       struct exynos_drm_ippdrv        *ippdrv;
-       struct drm_exynos_ipp_cmd_node *c_node;
-       enum drm_exynos_ipp_ctrl        ctrl;
+struct exynos_drm_ipp_funcs {
+       /**
+        * @commit:
+        *
+        * This is the main entry point to start framebuffer processing
+        * in the hardware. The exynos_drm_ipp_task has been already validated.
+        * This function must not wait until the device finishes processing.
+        * When the driver finishes processing, it has to call
+        * exynos_exynos_drm_ipp_task_done() function.
+        *
+        * RETURNS:
+        *
+        * 0 on success or negative error codes in case of failure.
+        */
+       int (*commit)(struct exynos_drm_ipp *ipp,
+                     struct exynos_drm_ipp_task *task);
+
+       /**
+        * @abort:
+        *
+        * Informs the driver that it has to abort the currently running
+        * task as soon as possible (i.e. as soon as it can stop the device
+        * safely), even if the task would not have been finished by then.
+        * After the driver performs the necessary steps, it has to call
+        * exynos_drm_ipp_task_done() (as if the task ended normally).
+        * This function does not have to (and will usually not) wait
+        * until the device enters a state when it can be stopped.
+        */
+       void (*abort)(struct exynos_drm_ipp *ipp,
+                     struct exynos_drm_ipp_task *task);
 };
 
-/*
- * A structure of command node.
- *
- * @list: list head to command queue information.
- * @event_list: list head of event.
- * @mem_list: list head to source,destination memory queue information.
- * @lock: lock for synchronization of access to ioctl.
- * @mem_lock: lock for synchronization of access to memory nodes.
- * @event_lock: lock for synchronization of access to scheduled event.
- * @start_complete: completion of start of command.
- * @stop_complete: completion of stop of command.
- * @property: property information.
- * @start_work: start command work structure.
- * @stop_work: stop command work structure.
- * @event_work: event work structure.
- * @state: state of command node.
- * @filp: associated file pointer.
- * @last_buf_id: store the current processing frame buf id from userspace
+/**
+ * struct exynos_drm_ipp - central picture processor module structure
  */
-struct drm_exynos_ipp_cmd_node {
-       struct list_head        list;
-       struct list_head        event_list;
-       struct list_head        mem_list[EXYNOS_DRM_OPS_MAX];
-       struct mutex    lock;
-       struct mutex    mem_lock;
-       struct mutex    event_lock;
-       struct completion       start_complete;
-       struct completion       stop_complete;
-       struct drm_exynos_ipp_property  property;
-       struct drm_exynos_ipp_cmd_work *start_work;
-       struct drm_exynos_ipp_cmd_work *stop_work;
-       struct drm_exynos_ipp_event_work *event_work;
-       enum drm_exynos_ipp_state       state;
-       struct drm_file *filp;
-       unsigned int last_buf_id[EXYNOS_DRM_OPS_MAX];
+struct exynos_drm_ipp {
+       struct drm_device *dev;
+       struct list_head head;
+       unsigned int id;
+
+       const char *name;
+       const struct exynos_drm_ipp_funcs *funcs;
+       unsigned int capabilities;
+       const struct exynos_drm_ipp_formats *formats;
+       unsigned int num_formats;
+       atomic_t sequence;
+
+       spinlock_t lock;
+       struct exynos_drm_ipp_task *task;
+       struct list_head todo_list;
+       wait_queue_head_t done_wq;
 };
 
-/*
- * A structure of buffer information.
- *
- * @handles: Y, Cb, Cr each gem object handle.
- * @base: Y, Cb, Cr each planar address.
- * @size: Y, Cb, Cr each planar size.
- */
-struct drm_exynos_ipp_buf_info {
-       unsigned long   handles[EXYNOS_DRM_PLANAR_MAX];
-       dma_addr_t      base[EXYNOS_DRM_PLANAR_MAX];
-       uint64_t        size[EXYNOS_DRM_PLANAR_MAX];
-};
+struct exynos_drm_ipp_buffer {
+       struct drm_exynos_ipp_task_buffer buf;
+       struct drm_exynos_ipp_task_rect rect;
 
-/*
- * A structure of wb setting information.
- *
- * @enable: enable flag for wb.
- * @refresh: HZ of the refresh rate.
- */
-struct drm_exynos_ipp_set_wb {
-       __u32   enable;
-       __u32   refresh;
+       struct exynos_drm_gem *exynos_gem[MAX_FB_BUFFER];
+       const struct drm_format_info *format;
+       dma_addr_t dma_addr[MAX_FB_BUFFER];
 };
 
-/*
- * A structure of event work information.
- *
- * @work: work structure.
- * @ippdrv: current work ippdrv.
- * @buf_id: id of src, dst buffer.
+/**
+ * struct exynos_drm_ipp_task - a structure describing transformation that
+ * has to be performed by the picture processor hardware module
  */
-struct drm_exynos_ipp_event_work {
-       struct work_struct      work;
-       struct exynos_drm_ippdrv *ippdrv;
-       u32     buf_id[EXYNOS_DRM_OPS_MAX];
-};
+struct exynos_drm_ipp_task {
+       struct drm_device *dev;
+       struct exynos_drm_ipp *ipp;
+       struct list_head head;
 
-/*
- * A structure of source,destination operations.
- *
- * @set_fmt: set format of image.
- * @set_transf: set transform(rotations, flip).
- * @set_size: set size of region.
- * @set_addr: set address for dma.
- */
-struct exynos_drm_ipp_ops {
-       int (*set_fmt)(struct device *dev, u32 fmt);
-       int (*set_transf)(struct device *dev,
-               enum drm_exynos_degree degree,
-               enum drm_exynos_flip flip, bool *swap);
-       int (*set_size)(struct device *dev, int swap,
-               struct drm_exynos_pos *pos, struct drm_exynos_sz *sz);
-       int (*set_addr)(struct device *dev,
-                struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id,
-               enum drm_exynos_ipp_buf_type buf_type);
+       struct exynos_drm_ipp_buffer src;
+       struct exynos_drm_ipp_buffer dst;
+
+       struct drm_exynos_ipp_task_transform transform;
+       struct drm_exynos_ipp_task_alpha alpha;
+
+       struct work_struct cleanup_work;
+       unsigned int flags;
+       int ret;
+
+       struct drm_pending_exynos_ipp_event *event;
 };
 
-/*
- * A structure of ipp driver.
- *
- * @drv_list: list head for registed sub driver information.
- * @parent_dev: parent device information.
- * @dev: platform device.
- * @drm_dev: drm device.
- * @dedicated: dedicated ipp device.
- * @ops: source, destination operations.
- * @event_workq: event work queue.
- * @c_node: current command information.
- * @cmd_list: list head for command information.
- * @cmd_lock: lock for synchronization of access to cmd_list.
- * @prop_list: property informations of current ipp driver.
- * @check_property: check property about format, size, buffer.
- * @reset: reset ipp block.
- * @start: ipp each device start.
- * @stop: ipp each device stop.
- * @sched_event: work schedule handler.
- */
-struct exynos_drm_ippdrv {
-       struct list_head        drv_list;
-       struct device   *parent_dev;
-       struct device   *dev;
-       struct drm_device       *drm_dev;
-       bool    dedicated;
-       struct exynos_drm_ipp_ops       *ops[EXYNOS_DRM_OPS_MAX];
-       struct workqueue_struct *event_workq;
-       struct drm_exynos_ipp_cmd_node *c_node;
-       struct list_head        cmd_list;
-       struct mutex    cmd_lock;
-       struct drm_exynos_ipp_prop_list prop_list;
+#define DRM_EXYNOS_IPP_TASK_DONE       (1 << 0)
+#define DRM_EXYNOS_IPP_TASK_ASYNC      (1 << 1)
 
-       int (*check_property)(struct device *dev,
-               struct drm_exynos_ipp_property *property);
-       int (*reset)(struct device *dev);
-       int (*start)(struct device *dev, enum drm_exynos_ipp_cmd cmd);
-       void (*stop)(struct device *dev, enum drm_exynos_ipp_cmd cmd);
-       void (*sched_event)(struct work_struct *work);
+struct exynos_drm_ipp_formats {
+       uint32_t fourcc;
+       uint32_t type;
+       uint64_t modifier;
+       const struct drm_exynos_ipp_limit *limits;
+       unsigned int num_limits;
 };
 
-#ifdef CONFIG_DRM_EXYNOS_IPP
-extern int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv);
-extern int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv);
-extern int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
-                                        struct drm_file *file);
-extern int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
-                                        struct drm_file *file);
-extern int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
-                                        struct drm_file *file);
-extern int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
-                                        struct drm_file *file);
-extern int exynos_drm_ippnb_register(struct notifier_block *nb);
-extern int exynos_drm_ippnb_unregister(struct notifier_block *nb);
-extern int exynos_drm_ippnb_send_event(unsigned long val, void *v);
-extern void ipp_sched_cmd(struct work_struct *work);
-extern void ipp_sched_event(struct work_struct *work);
+/* helper macros to set exynos_drm_ipp_formats structure and limits*/
+#define IPP_SRCDST_MFORMAT(f, m, l) \
+       .fourcc = DRM_FORMAT_##f, .modifier = m, .limits = l, \
+       .num_limits = ARRAY_SIZE(l), \
+       .type = (DRM_EXYNOS_IPP_FORMAT_SOURCE | \
+                DRM_EXYNOS_IPP_FORMAT_DESTINATION)
 
-#else
-static inline int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv)
-{
-       return -ENODEV;
-}
+#define IPP_SRCDST_FORMAT(f, l) IPP_SRCDST_MFORMAT(f, 0, l)
 
-static inline int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv)
-{
-       return -ENODEV;
-}
+#define IPP_SIZE_LIMIT(l, val...)      \
+       .type = (DRM_EXYNOS_IPP_LIMIT_TYPE_SIZE | \
+                DRM_EXYNOS_IPP_LIMIT_SIZE_##l), val
 
-static inline int exynos_drm_ipp_get_property(struct drm_device *drm_dev,
-                                               void *data,
-                                               struct drm_file *file_priv)
-{
-       return -ENOTTY;
-}
+#define IPP_SCALE_LIMIT(val...)                \
+       .type = (DRM_EXYNOS_IPP_LIMIT_TYPE_SCALE), val
 
-static inline int exynos_drm_ipp_set_property(struct drm_device *drm_dev,
-                                               void *data,
-                                               struct drm_file *file_priv)
-{
-       return -ENOTTY;
-}
+int exynos_drm_ipp_register(struct drm_device *dev, struct exynos_drm_ipp *ipp,
+               const struct exynos_drm_ipp_funcs *funcs, unsigned int caps,
+               const struct exynos_drm_ipp_formats *formats,
+               unsigned int num_formats, const char *name);
+void exynos_drm_ipp_unregister(struct drm_device *dev,
+                              struct exynos_drm_ipp *ipp);
 
-static inline int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev,
-                                               void *data,
-                                               struct drm_file *file)
-{
-       return -ENOTTY;
-}
+void exynos_drm_ipp_task_done(struct exynos_drm_ipp_task *task, int ret);
 
-static inline int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev,
-                                               void *data,
-                                               struct drm_file *file)
+#ifdef CONFIG_DRM_EXYNOS_IPP
+int exynos_drm_ipp_get_res_ioctl(struct drm_device *dev, void *data,
+                                struct drm_file *file_priv);
+int exynos_drm_ipp_get_caps_ioctl(struct drm_device *dev, void *data,
+                                 struct drm_file *file_priv);
+int exynos_drm_ipp_get_limits_ioctl(struct drm_device *dev, void *data,
+                                   struct drm_file *file_priv);
+int exynos_drm_ipp_commit_ioctl(struct drm_device *dev,
+                               void *data, struct drm_file *file_priv);
+#else
+static inline int exynos_drm_ipp_get_res_ioctl(struct drm_device *dev,
+        void *data, struct drm_file *file_priv)
 {
-       return -ENOTTY;
-}
+       struct drm_exynos_ioctl_ipp_get_res *resp = data;
 
-static inline int exynos_drm_ippnb_register(struct notifier_block *nb)
+       resp->count_ipps = 0;
+       return 0;
+}
+static inline int exynos_drm_ipp_get_caps_ioctl(struct drm_device *dev,
+        void *data, struct drm_file *file_priv)
 {
        return -ENODEV;
 }
-
-static inline int exynos_drm_ippnb_unregister(struct notifier_block *nb)
+static inline int exynos_drm_ipp_get_limits_ioctl(struct drm_device *dev,
+        void *data, struct drm_file *file_priv)
 {
        return -ENODEV;
 }
-
-static inline int exynos_drm_ippnb_send_event(unsigned long val, void *v)
+static inline int exynos_drm_ipp_commit_ioctl(struct drm_device *dev,
+        void *data, struct drm_file *file_priv)
 {
-       return -ENOTTY;
+       return -ENODEV;
 }
 #endif
-
-#endif /* _EXYNOS_DRM_IPP_H_ */
-
+#endif
index 3987928256ba1ae0c392baf9d3f516b0a0938a96..1a76dd3d52e1dc5b63d81bd19b49e4120e23891d 100644 (file)
  */
 
 #include <linux/kernel.h>
+#include <linux/component.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/of_device.h>
 #include <linux/pm_runtime.h>
 
 #include <drm/drmP.h>
 #include <drm/exynos_drm.h>
 #include "regs-rotator.h"
-#include "exynos_drm.h"
 #include "exynos_drm_drv.h"
+#include "exynos_drm_iommu.h"
 #include "exynos_drm_ipp.h"
 
 /*
  * Rotator supports image crop/rotator and input/output DMA operations.
  * input DMA reads image data from the memory.
  * output DMA writes image data to memory.
- *
- * M2M operation : supports crop/scale/rotation/csc so on.
- * Memory ----> Rotator H/W ----> Memory.
  */
 
-/*
- * TODO
- * 1. check suspend/resume api if needed.
- * 2. need to check use case platform_device_id.
- * 3. check src/dst size with, height.
- * 4. need to add supported list in prop_list.
- */
+#define ROTATOR_AUTOSUSPEND_DELAY      2000
 
-#define get_rot_context(dev)   platform_get_drvdata(to_platform_device(dev))
-#define get_ctx_from_ippdrv(ippdrv)    container_of(ippdrv,\
-                                       struct rot_context, ippdrv);
-#define rot_read(offset)               readl(rot->regs + (offset))
+#define rot_read(offset)       readl(rot->regs + (offset))
 #define rot_write(cfg, offset) writel(cfg, rot->regs + (offset))
 
 enum rot_irq_status {
@@ -52,52 +42,28 @@ enum rot_irq_status {
        ROT_IRQ_STATUS_ILLEGAL  = 9,
 };
 
-/*
- * A structure of limitation.
- *
- * @min_w: minimum width.
- * @min_h: minimum height.
- * @max_w: maximum width.
- * @max_h: maximum height.
- * @align: align size.
- */
-struct rot_limit {
-       u32     min_w;
-       u32     min_h;
-       u32     max_w;
-       u32     max_h;
-       u32     align;
-};
-
-/*
- * A structure of limitation table.
- *
- * @ycbcr420_2p: case of YUV.
- * @rgb888: case of RGB.
- */
-struct rot_limit_table {
-       struct rot_limit        ycbcr420_2p;
-       struct rot_limit        rgb888;
+struct rot_variant {
+       const struct exynos_drm_ipp_formats *formats;
+       unsigned int    num_formats;
 };
 
 /*
  * A structure of rotator context.
  * @ippdrv: prepare initialization using ippdrv.
- * @regs_res: register resources.
  * @regs: memory mapped io registers.
  * @clock: rotator gate clock.
  * @limit_tbl: limitation of rotator.
  * @irq: irq number.
- * @suspended: suspended state.
  */
 struct rot_context {
-       struct exynos_drm_ippdrv        ippdrv;
-       struct resource *regs_res;
+       struct exynos_drm_ipp ipp;
+       struct drm_device *drm_dev;
+       struct device   *dev;
        void __iomem    *regs;
        struct clk      *clock;
-       struct rot_limit_table  *limit_tbl;
-       int     irq;
-       bool    suspended;
+       const struct exynos_drm_ipp_formats *formats;
+       unsigned int    num_formats;
+       struct exynos_drm_ipp_task      *task;
 };
 
 static void rotator_reg_set_irq(struct rot_context *rot, bool enable)
@@ -112,15 +78,6 @@ static void rotator_reg_set_irq(struct rot_context *rot, bool enable)
        rot_write(val, ROT_CONFIG);
 }
 
-static u32 rotator_reg_get_fmt(struct rot_context *rot)
-{
-       u32 val = rot_read(ROT_CONTROL);
-
-       val &= ROT_CONTROL_FMT_MASK;
-
-       return val;
-}
-
 static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot)
 {
        u32 val = rot_read(ROT_STATUS);
@@ -136,9 +93,6 @@ static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot)
 static irqreturn_t rotator_irq_handler(int irq, void *arg)
 {
        struct rot_context *rot = arg;
-       struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
-       struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node;
-       struct drm_exynos_ipp_event_work *event_work = c_node->event_work;
        enum rot_irq_status irq_status;
        u32 val;
 
@@ -150,54 +104,21 @@ static irqreturn_t rotator_irq_handler(int irq, void *arg)
        val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
        rot_write(val, ROT_STATUS);
 
-       if (irq_status == ROT_IRQ_STATUS_COMPLETE) {
-               event_work->ippdrv = ippdrv;
-               queue_work(ippdrv->event_workq, &event_work->work);
-       } else {
-               DRM_ERROR("the SFR is set illegally\n");
+       if (rot->task) {
+               struct exynos_drm_ipp_task *task = rot->task;
+
+               rot->task = NULL;
+               pm_runtime_mark_last_busy(rot->dev);
+               pm_runtime_put_autosuspend(rot->dev);
+               exynos_drm_ipp_task_done(task,
+                       irq_status == ROT_IRQ_STATUS_COMPLETE ? 0 : -EINVAL);
        }
 
        return IRQ_HANDLED;
 }
 
-static void rotator_align_size(struct rot_context *rot, u32 fmt, u32 *hsize,
-               u32 *vsize)
-{
-       struct rot_limit_table *limit_tbl = rot->limit_tbl;
-       struct rot_limit *limit;
-       u32 mask, val;
-
-       /* Get size limit */
-       if (fmt == ROT_CONTROL_FMT_RGB888)
-               limit = &limit_tbl->rgb888;
-       else
-               limit = &limit_tbl->ycbcr420_2p;
-
-       /* Get mask for rounding to nearest aligned val */
-       mask = ~((1 << limit->align) - 1);
-
-       /* Set aligned width */
-       val = ROT_ALIGN(*hsize, limit->align, mask);
-       if (val < limit->min_w)
-               *hsize = ROT_MIN(limit->min_w, mask);
-       else if (val > limit->max_w)
-               *hsize = ROT_MAX(limit->max_w, mask);
-       else
-               *hsize = val;
-
-       /* Set aligned height */
-       val = ROT_ALIGN(*vsize, limit->align, mask);
-       if (val < limit->min_h)
-               *vsize = ROT_MIN(limit->min_h, mask);
-       else if (val > limit->max_h)
-               *vsize = ROT_MAX(limit->max_h, mask);
-       else
-               *vsize = val;
-}
-
-static int rotator_src_set_fmt(struct device *dev, u32 fmt)
+static void rotator_src_set_fmt(struct rot_context *rot, u32 fmt)
 {
-       struct rot_context *rot = dev_get_drvdata(dev);
        u32 val;
 
        val = rot_read(ROT_CONTROL);
@@ -210,515 +131,176 @@ static int rotator_src_set_fmt(struct device *dev, u32 fmt)
        case DRM_FORMAT_XRGB8888:
                val |= ROT_CONTROL_FMT_RGB888;
                break;
-       default:
-               DRM_ERROR("invalid image format\n");
-               return -EINVAL;
        }
 
        rot_write(val, ROT_CONTROL);
-
-       return 0;
-}
-
-static inline bool rotator_check_reg_fmt(u32 fmt)
-{
-       if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) ||
-           (fmt == ROT_CONTROL_FMT_RGB888))
-               return true;
-
-       return false;
 }
 
-static int rotator_src_set_size(struct device *dev, int swap,
-               struct drm_exynos_pos *pos,
-               struct drm_exynos_sz *sz)
+static void rotator_src_set_buf(struct rot_context *rot,
+                               struct exynos_drm_ipp_buffer *buf)
 {
-       struct rot_context *rot = dev_get_drvdata(dev);
-       u32 fmt, hsize, vsize;
        u32 val;
 
-       /* Get format */
-       fmt = rotator_reg_get_fmt(rot);
-       if (!rotator_check_reg_fmt(fmt)) {
-               DRM_ERROR("invalid format.\n");
-               return -EINVAL;
-       }
-
-       /* Align buffer size */
-       hsize = sz->hsize;
-       vsize = sz->vsize;
-       rotator_align_size(rot, fmt, &hsize, &vsize);
-
        /* Set buffer size configuration */
-       val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
+       val = ROT_SET_BUF_SIZE_H(buf->buf.height) |
+             ROT_SET_BUF_SIZE_W(buf->buf.pitch[0] / buf->format->cpp[0]);
        rot_write(val, ROT_SRC_BUF_SIZE);
 
        /* Set crop image position configuration */
-       val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
+       val = ROT_CROP_POS_Y(buf->rect.y) | ROT_CROP_POS_X(buf->rect.x);
        rot_write(val, ROT_SRC_CROP_POS);
-       val = ROT_SRC_CROP_SIZE_H(pos->h) | ROT_SRC_CROP_SIZE_W(pos->w);
+       val = ROT_SRC_CROP_SIZE_H(buf->rect.h) |
+             ROT_SRC_CROP_SIZE_W(buf->rect.w);
        rot_write(val, ROT_SRC_CROP_SIZE);
 
-       return 0;
-}
-
-static int rotator_src_set_addr(struct device *dev,
-               struct drm_exynos_ipp_buf_info *buf_info,
-               u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
-{
-       struct rot_context *rot = dev_get_drvdata(dev);
-       dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
-       u32 val, fmt, hsize, vsize;
-       int i;
-
-       switch (buf_type) {
-       case IPP_BUF_ENQUEUE:
-               /* Set address configuration */
-               for_each_ipp_planar(i)
-                       addr[i] = buf_info->base[i];
-
-               /* Get format */
-               fmt = rotator_reg_get_fmt(rot);
-               if (!rotator_check_reg_fmt(fmt)) {
-                       DRM_ERROR("invalid format.\n");
-                       return -EINVAL;
-               }
-
-               /* Re-set cb planar for NV12 format */
-               if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
-                   !addr[EXYNOS_DRM_PLANAR_CB]) {
-
-                       val = rot_read(ROT_SRC_BUF_SIZE);
-                       hsize = ROT_GET_BUF_SIZE_W(val);
-                       vsize = ROT_GET_BUF_SIZE_H(val);
-
-                       /* Set cb planar */
-                       addr[EXYNOS_DRM_PLANAR_CB] =
-                               addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
-               }
-
-               for_each_ipp_planar(i)
-                       rot_write(addr[i], ROT_SRC_BUF_ADDR(i));
-               break;
-       case IPP_BUF_DEQUEUE:
-               for_each_ipp_planar(i)
-                       rot_write(0x0, ROT_SRC_BUF_ADDR(i));
-               break;
-       default:
-               /* Nothing to do */
-               break;
-       }
-
-       return 0;
+       /* Set buffer DMA address */
+       rot_write(buf->dma_addr[0], ROT_SRC_BUF_ADDR(0));
+       rot_write(buf->dma_addr[1], ROT_SRC_BUF_ADDR(1));
 }
 
-static int rotator_dst_set_transf(struct device *dev,
-               enum drm_exynos_degree degree,
-               enum drm_exynos_flip flip, bool *swap)
+static void rotator_dst_set_transf(struct rot_context *rot,
+                                  unsigned int rotation)
 {
-       struct rot_context *rot = dev_get_drvdata(dev);
        u32 val;
 
        /* Set transform configuration */
        val = rot_read(ROT_CONTROL);
        val &= ~ROT_CONTROL_FLIP_MASK;
 
-       switch (flip) {
-       case EXYNOS_DRM_FLIP_VERTICAL:
-               val |= ROT_CONTROL_FLIP_VERTICAL;
-               break;
-       case EXYNOS_DRM_FLIP_HORIZONTAL:
+       if (rotation & DRM_MODE_REFLECT_X)
                val |= ROT_CONTROL_FLIP_HORIZONTAL;
-               break;
-       default:
-               /* Flip None */
-               break;
-       }
+       if (rotation & DRM_MODE_REFLECT_Y)
+               val |= ROT_CONTROL_FLIP_VERTICAL;
 
        val &= ~ROT_CONTROL_ROT_MASK;
 
-       switch (degree) {
-       case EXYNOS_DRM_DEGREE_90:
+       if (rotation & DRM_MODE_ROTATE_90)
                val |= ROT_CONTROL_ROT_90;
-               break;
-       case EXYNOS_DRM_DEGREE_180:
+       else if (rotation & DRM_MODE_ROTATE_180)
                val |= ROT_CONTROL_ROT_180;
-               break;
-       case EXYNOS_DRM_DEGREE_270:
+       else if (rotation & DRM_MODE_ROTATE_270)
                val |= ROT_CONTROL_ROT_270;
-               break;
-       default:
-               /* Rotation 0 Degree */
-               break;
-       }
 
        rot_write(val, ROT_CONTROL);
-
-       /* Check degree for setting buffer size swap */
-       if ((degree == EXYNOS_DRM_DEGREE_90) ||
-           (degree == EXYNOS_DRM_DEGREE_270))
-               *swap = true;
-       else
-               *swap = false;
-
-       return 0;
 }
 
-static int rotator_dst_set_size(struct device *dev, int swap,
-               struct drm_exynos_pos *pos,
-               struct drm_exynos_sz *sz)
+static void rotator_dst_set_buf(struct rot_context *rot,
+                               struct exynos_drm_ipp_buffer *buf)
 {
-       struct rot_context *rot = dev_get_drvdata(dev);
-       u32 val, fmt, hsize, vsize;
-
-       /* Get format */
-       fmt = rotator_reg_get_fmt(rot);
-       if (!rotator_check_reg_fmt(fmt)) {
-               DRM_ERROR("invalid format.\n");
-               return -EINVAL;
-       }
-
-       /* Align buffer size */
-       hsize = sz->hsize;
-       vsize = sz->vsize;
-       rotator_align_size(rot, fmt, &hsize, &vsize);
+       u32 val;
 
        /* Set buffer size configuration */
-       val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
+       val = ROT_SET_BUF_SIZE_H(buf->buf.height) |
+             ROT_SET_BUF_SIZE_W(buf->buf.pitch[0] / buf->format->cpp[0]);
        rot_write(val, ROT_DST_BUF_SIZE);
 
        /* Set crop image position configuration */
-       val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
+       val = ROT_CROP_POS_Y(buf->rect.y) | ROT_CROP_POS_X(buf->rect.x);
        rot_write(val, ROT_DST_CROP_POS);
 
-       return 0;
+       /* Set buffer DMA address */
+       rot_write(buf->dma_addr[0], ROT_DST_BUF_ADDR(0));
+       rot_write(buf->dma_addr[1], ROT_DST_BUF_ADDR(1));
 }
 
-static int rotator_dst_set_addr(struct device *dev,
-               struct drm_exynos_ipp_buf_info *buf_info,
-               u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
+static void rotator_start(struct rot_context *rot)
 {
-       struct rot_context *rot = dev_get_drvdata(dev);
-       dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
-       u32 val, fmt, hsize, vsize;
-       int i;
-
-       switch (buf_type) {
-       case IPP_BUF_ENQUEUE:
-               /* Set address configuration */
-               for_each_ipp_planar(i)
-                       addr[i] = buf_info->base[i];
-
-               /* Get format */
-               fmt = rotator_reg_get_fmt(rot);
-               if (!rotator_check_reg_fmt(fmt)) {
-                       DRM_ERROR("invalid format.\n");
-                       return -EINVAL;
-               }
-
-               /* Re-set cb planar for NV12 format */
-               if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
-                   !addr[EXYNOS_DRM_PLANAR_CB]) {
-                       /* Get buf size */
-                       val = rot_read(ROT_DST_BUF_SIZE);
-
-                       hsize = ROT_GET_BUF_SIZE_W(val);
-                       vsize = ROT_GET_BUF_SIZE_H(val);
-
-                       /* Set cb planar */
-                       addr[EXYNOS_DRM_PLANAR_CB] =
-                               addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
-               }
-
-               for_each_ipp_planar(i)
-                       rot_write(addr[i], ROT_DST_BUF_ADDR(i));
-               break;
-       case IPP_BUF_DEQUEUE:
-               for_each_ipp_planar(i)
-                       rot_write(0x0, ROT_DST_BUF_ADDR(i));
-               break;
-       default:
-               /* Nothing to do */
-               break;
-       }
+       u32 val;
 
-       return 0;
+       /* Set interrupt enable */
+       rotator_reg_set_irq(rot, true);
+
+       val = rot_read(ROT_CONTROL);
+       val |= ROT_CONTROL_START;
+       rot_write(val, ROT_CONTROL);
 }
 
-static struct exynos_drm_ipp_ops rot_src_ops = {
-       .set_fmt        =       rotator_src_set_fmt,
-       .set_size       =       rotator_src_set_size,
-       .set_addr       =       rotator_src_set_addr,
-};
+static int rotator_commit(struct exynos_drm_ipp *ipp,
+                         struct exynos_drm_ipp_task *task)
+{
+       struct rot_context *rot =
+                       container_of(ipp, struct rot_context, ipp);
 
-static struct exynos_drm_ipp_ops rot_dst_ops = {
-       .set_transf     =       rotator_dst_set_transf,
-       .set_size       =       rotator_dst_set_size,
-       .set_addr       =       rotator_dst_set_addr,
-};
+       pm_runtime_get_sync(rot->dev);
+       rot->task = task;
 
-static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
-{
-       struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
-
-       prop_list->version = 1;
-       prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
-                               (1 << EXYNOS_DRM_FLIP_HORIZONTAL);
-       prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) |
-                               (1 << EXYNOS_DRM_DEGREE_90) |
-                               (1 << EXYNOS_DRM_DEGREE_180) |
-                               (1 << EXYNOS_DRM_DEGREE_270);
-       prop_list->csc = 0;
-       prop_list->crop = 0;
-       prop_list->scale = 0;
+       rotator_src_set_fmt(rot, task->src.buf.fourcc);
+       rotator_src_set_buf(rot, &task->src);
+       rotator_dst_set_transf(rot, task->transform.rotation);
+       rotator_dst_set_buf(rot, &task->dst);
+       rotator_start(rot);
 
        return 0;
 }
 
-static inline bool rotator_check_drm_fmt(u32 fmt)
-{
-       switch (fmt) {
-       case DRM_FORMAT_XRGB8888:
-       case DRM_FORMAT_NV12:
-               return true;
-       default:
-               DRM_DEBUG_KMS("not support format\n");
-               return false;
-       }
-}
-
-static inline bool rotator_check_drm_flip(enum drm_exynos_flip flip)
-{
-       switch (flip) {
-       case EXYNOS_DRM_FLIP_NONE:
-       case EXYNOS_DRM_FLIP_VERTICAL:
-       case EXYNOS_DRM_FLIP_HORIZONTAL:
-       case EXYNOS_DRM_FLIP_BOTH:
-               return true;
-       default:
-               DRM_DEBUG_KMS("invalid flip\n");
-               return false;
-       }
-}
+static const struct exynos_drm_ipp_funcs ipp_funcs = {
+       .commit = rotator_commit,
+};
 
-static int rotator_ippdrv_check_property(struct device *dev,
-               struct drm_exynos_ipp_property *property)
+static int rotator_bind(struct device *dev, struct device *master, void *data)
 {
-       struct drm_exynos_ipp_config *src_config =
-                                       &property->config[EXYNOS_DRM_OPS_SRC];
-       struct drm_exynos_ipp_config *dst_config =
-                                       &property->config[EXYNOS_DRM_OPS_DST];
-       struct drm_exynos_pos *src_pos = &src_config->pos;
-       struct drm_exynos_pos *dst_pos = &dst_config->pos;
-       struct drm_exynos_sz *src_sz = &src_config->sz;
-       struct drm_exynos_sz *dst_sz = &dst_config->sz;
-       bool swap = false;
-
-       /* Check format configuration */
-       if (src_config->fmt != dst_config->fmt) {
-               DRM_DEBUG_KMS("not support csc feature\n");
-               return -EINVAL;
-       }
-
-       if (!rotator_check_drm_fmt(dst_config->fmt)) {
-               DRM_DEBUG_KMS("invalid format\n");
-               return -EINVAL;
-       }
-
-       /* Check transform configuration */
-       if (src_config->degree != EXYNOS_DRM_DEGREE_0) {
-               DRM_DEBUG_KMS("not support source-side rotation\n");
-               return -EINVAL;
-       }
-
-       switch (dst_config->degree) {
-       case EXYNOS_DRM_DEGREE_90:
-       case EXYNOS_DRM_DEGREE_270:
-               swap = true;
-       case EXYNOS_DRM_DEGREE_0:
-       case EXYNOS_DRM_DEGREE_180:
-               /* No problem */
-               break;
-       default:
-               DRM_DEBUG_KMS("invalid degree\n");
-               return -EINVAL;
-       }
+       struct rot_context *rot = dev_get_drvdata(dev);
+       struct drm_device *drm_dev = data;
+       struct exynos_drm_ipp *ipp = &rot->ipp;
 
-       if (src_config->flip != EXYNOS_DRM_FLIP_NONE) {
-               DRM_DEBUG_KMS("not support source-side flip\n");
-               return -EINVAL;
-       }
+       rot->drm_dev = drm_dev;
+       drm_iommu_attach_device(drm_dev, dev);
 
-       if (!rotator_check_drm_flip(dst_config->flip)) {
-               DRM_DEBUG_KMS("invalid flip\n");
-               return -EINVAL;
-       }
+       exynos_drm_ipp_register(drm_dev, ipp, &ipp_funcs,
+                          DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE,
+                          rot->formats, rot->num_formats, "rotator");
 
-       /* Check size configuration */
-       if ((src_pos->x + src_pos->w > src_sz->hsize) ||
-               (src_pos->y + src_pos->h > src_sz->vsize)) {
-               DRM_DEBUG_KMS("out of source buffer bound\n");
-               return -EINVAL;
-       }
-
-       if (swap) {
-               if ((dst_pos->x + dst_pos->h > dst_sz->vsize) ||
-                       (dst_pos->y + dst_pos->w > dst_sz->hsize)) {
-                       DRM_DEBUG_KMS("out of destination buffer bound\n");
-                       return -EINVAL;
-               }
-
-               if ((src_pos->w != dst_pos->h) || (src_pos->h != dst_pos->w)) {
-                       DRM_DEBUG_KMS("not support scale feature\n");
-                       return -EINVAL;
-               }
-       } else {
-               if ((dst_pos->x + dst_pos->w > dst_sz->hsize) ||
-                       (dst_pos->y + dst_pos->h > dst_sz->vsize)) {
-                       DRM_DEBUG_KMS("out of destination buffer bound\n");
-                       return -EINVAL;
-               }
-
-               if ((src_pos->w != dst_pos->w) || (src_pos->h != dst_pos->h)) {
-                       DRM_DEBUG_KMS("not support scale feature\n");
-                       return -EINVAL;
-               }
-       }
+       dev_info(dev, "The exynos rotator has been probed successfully\n");
 
        return 0;
 }
 
-static int rotator_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
+static void rotator_unbind(struct device *dev, struct device *master,
+                       void *data)
 {
        struct rot_context *rot = dev_get_drvdata(dev);
-       u32 val;
-
-       if (rot->suspended) {
-               DRM_ERROR("suspended state\n");
-               return -EPERM;
-       }
+       struct drm_device *drm_dev = data;
+       struct exynos_drm_ipp *ipp = &rot->ipp;
 
-       if (cmd != IPP_CMD_M2M) {
-               DRM_ERROR("not support cmd: %d\n", cmd);
-               return -EINVAL;
-       }
-
-       /* Set interrupt enable */
-       rotator_reg_set_irq(rot, true);
-
-       val = rot_read(ROT_CONTROL);
-       val |= ROT_CONTROL_START;
-
-       rot_write(val, ROT_CONTROL);
-
-       return 0;
+       exynos_drm_ipp_unregister(drm_dev, ipp);
+       drm_iommu_detach_device(rot->drm_dev, rot->dev);
 }
 
-static struct rot_limit_table rot_limit_tbl_4210 = {
-       .ycbcr420_2p = {
-               .min_w = 32,
-               .min_h = 32,
-               .max_w = SZ_64K,
-               .max_h = SZ_64K,
-               .align = 3,
-       },
-       .rgb888 = {
-               .min_w = 8,
-               .min_h = 8,
-               .max_w = SZ_16K,
-               .max_h = SZ_16K,
-               .align = 2,
-       },
-};
-
-static struct rot_limit_table rot_limit_tbl_4x12 = {
-       .ycbcr420_2p = {
-               .min_w = 32,
-               .min_h = 32,
-               .max_w = SZ_32K,
-               .max_h = SZ_32K,
-               .align = 3,
-       },
-       .rgb888 = {
-               .min_w = 8,
-               .min_h = 8,
-               .max_w = SZ_8K,
-               .max_h = SZ_8K,
-               .align = 2,
-       },
+static const struct component_ops rotator_component_ops = {
+       .bind   = rotator_bind,
+       .unbind = rotator_unbind,
 };
 
-static struct rot_limit_table rot_limit_tbl_5250 = {
-       .ycbcr420_2p = {
-               .min_w = 32,
-               .min_h = 32,
-               .max_w = SZ_32K,
-               .max_h = SZ_32K,
-               .align = 3,
-       },
-       .rgb888 = {
-               .min_w = 8,
-               .min_h = 8,
-               .max_w = SZ_8K,
-               .max_h = SZ_8K,
-               .align = 1,
-       },
-};
-
-static const struct of_device_id exynos_rotator_match[] = {
-       {
-               .compatible = "samsung,exynos4210-rotator",
-               .data = &rot_limit_tbl_4210,
-       },
-       {
-               .compatible = "samsung,exynos4212-rotator",
-               .data = &rot_limit_tbl_4x12,
-       },
-       {
-               .compatible = "samsung,exynos5250-rotator",
-               .data = &rot_limit_tbl_5250,
-       },
-       {},
-};
-MODULE_DEVICE_TABLE(of, exynos_rotator_match);
-
 static int rotator_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
+       struct resource *regs_res;
        struct rot_context *rot;
-       struct exynos_drm_ippdrv *ippdrv;
-       const struct of_device_id *match;
+       const struct rot_variant *variant;
+       int irq;
        int ret;
 
-       if (!dev->of_node) {
-               dev_err(dev, "cannot find of_node.\n");
-               return -ENODEV;
-       }
-
        rot = devm_kzalloc(dev, sizeof(*rot), GFP_KERNEL);
        if (!rot)
                return -ENOMEM;
 
-       match = of_match_node(exynos_rotator_match, dev->of_node);
-       if (!match) {
-               dev_err(dev, "failed to match node\n");
-               return -ENODEV;
-       }
-       rot->limit_tbl = (struct rot_limit_table *)match->data;
-
-       rot->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       rot->regs = devm_ioremap_resource(dev, rot->regs_res);
+       variant = of_device_get_match_data(dev);
+       rot->formats = variant->formats;
+       rot->num_formats = variant->num_formats;
+       rot->dev = dev;
+       regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       rot->regs = devm_ioremap_resource(dev, regs_res);
        if (IS_ERR(rot->regs))
                return PTR_ERR(rot->regs);
 
-       rot->irq = platform_get_irq(pdev, 0);
-       if (rot->irq < 0) {
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
                dev_err(dev, "failed to get irq\n");
-               return rot->irq;
+               return irq;
        }
 
-       ret = devm_request_threaded_irq(dev, rot->irq, NULL,
-                       rotator_irq_handler, IRQF_ONESHOT, "drm_rotator", rot);
+       ret = devm_request_irq(dev, irq, rotator_irq_handler, 0, dev_name(dev),
+                              rot);
        if (ret < 0) {
                dev_err(dev, "failed to request irq\n");
                return ret;
@@ -730,35 +312,19 @@ static int rotator_probe(struct platform_device *pdev)
                return PTR_ERR(rot->clock);
        }
 
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_set_autosuspend_delay(dev, ROTATOR_AUTOSUSPEND_DELAY);
        pm_runtime_enable(dev);
-
-       ippdrv = &rot->ippdrv;
-       ippdrv->dev = dev;
-       ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops;
-       ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops;
-       ippdrv->check_property = rotator_ippdrv_check_property;
-       ippdrv->start = rotator_ippdrv_start;
-       ret = rotator_init_prop_list(ippdrv);
-       if (ret < 0) {
-               dev_err(dev, "failed to init property list.\n");
-               goto err_ippdrv_register;
-       }
-
-       DRM_DEBUG_KMS("ippdrv[%p]\n", ippdrv);
-
        platform_set_drvdata(pdev, rot);
 
-       ret = exynos_drm_ippdrv_register(ippdrv);
-       if (ret < 0) {
-               dev_err(dev, "failed to register drm rotator device\n");
-               goto err_ippdrv_register;
-       }
-
-       dev_info(dev, "The exynos rotator is probed successfully\n");
+       ret = component_add(dev, &rotator_component_ops);
+       if (ret)
+               goto err_component;
 
        return 0;
 
-err_ippdrv_register:
+err_component:
+       pm_runtime_dont_use_autosuspend(dev);
        pm_runtime_disable(dev);
        return ret;
 }
@@ -766,70 +332,104 @@ err_ippdrv_register:
 static int rotator_remove(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
-       struct rot_context *rot = dev_get_drvdata(dev);
-       struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
-
-       exynos_drm_ippdrv_unregister(ippdrv);
 
+       component_del(dev, &rotator_component_ops);
+       pm_runtime_dont_use_autosuspend(dev);
        pm_runtime_disable(dev);
 
        return 0;
 }
 
-static int rotator_clk_crtl(struct rot_context *rot, bool enable)
+#ifdef CONFIG_PM
+static int rotator_runtime_suspend(struct device *dev)
 {
-       if (enable) {
-               clk_enable(rot->clock);
-               rot->suspended = false;
-       } else {
-               clk_disable(rot->clock);
-               rot->suspended = true;
-       }
+       struct rot_context *rot = dev_get_drvdata(dev);
 
+       clk_disable_unprepare(rot->clock);
        return 0;
 }
 
-
-#ifdef CONFIG_PM_SLEEP
-static int rotator_suspend(struct device *dev)
+static int rotator_runtime_resume(struct device *dev)
 {
        struct rot_context *rot = dev_get_drvdata(dev);
 
-       if (pm_runtime_suspended(dev))
-               return 0;
-
-       return rotator_clk_crtl(rot, false);
+       return clk_prepare_enable(rot->clock);
 }
+#endif
 
-static int rotator_resume(struct device *dev)
-{
-       struct rot_context *rot = dev_get_drvdata(dev);
+static const struct drm_exynos_ipp_limit rotator_4210_rbg888_limits[] = {
+       { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_16K }, .v = { 8, SZ_16K }) },
+       { IPP_SIZE_LIMIT(AREA, .h.align = 4, .v.align = 4) },
+};
 
-       if (!pm_runtime_suspended(dev))
-               return rotator_clk_crtl(rot, true);
+static const struct drm_exynos_ipp_limit rotator_4412_rbg888_limits[] = {
+       { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_8K }, .v = { 8, SZ_8K }) },
+       { IPP_SIZE_LIMIT(AREA, .h.align = 4, .v.align = 4) },
+};
 
-       return 0;
-}
-#endif
+static const struct drm_exynos_ipp_limit rotator_5250_rbg888_limits[] = {
+       { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_8K }, .v = { 8, SZ_8K }) },
+       { IPP_SIZE_LIMIT(AREA, .h.align = 2, .v.align = 2) },
+};
 
-#ifdef CONFIG_PM
-static int rotator_runtime_suspend(struct device *dev)
-{
-       struct rot_context *rot = dev_get_drvdata(dev);
+static const struct drm_exynos_ipp_limit rotator_4210_yuv_limits[] = {
+       { IPP_SIZE_LIMIT(BUFFER, .h = { 32, SZ_64K }, .v = { 32, SZ_64K }) },
+       { IPP_SIZE_LIMIT(AREA, .h.align = 8, .v.align = 8) },
+};
 
-       return  rotator_clk_crtl(rot, false);
-}
+static const struct drm_exynos_ipp_limit rotator_4412_yuv_limits[] = {
+       { IPP_SIZE_LIMIT(BUFFER, .h = { 32, SZ_32K }, .v = { 32, SZ_32K }) },
+       { IPP_SIZE_LIMIT(AREA, .h.align = 8, .v.align = 8) },
+};
 
-static int rotator_runtime_resume(struct device *dev)
-{
-       struct rot_context *rot = dev_get_drvdata(dev);
+static const struct exynos_drm_ipp_formats rotator_4210_formats[] = {
+       { IPP_SRCDST_FORMAT(XRGB8888, rotator_4210_rbg888_limits) },
+       { IPP_SRCDST_FORMAT(NV12, rotator_4210_yuv_limits) },
+};
 
-       return  rotator_clk_crtl(rot, true);
-}
-#endif
+static const struct exynos_drm_ipp_formats rotator_4412_formats[] = {
+       { IPP_SRCDST_FORMAT(XRGB8888, rotator_4412_rbg888_limits) },
+       { IPP_SRCDST_FORMAT(NV12, rotator_4412_yuv_limits) },
+};
+
+static const struct exynos_drm_ipp_formats rotator_5250_formats[] = {
+       { IPP_SRCDST_FORMAT(XRGB8888, rotator_5250_rbg888_limits) },
+       { IPP_SRCDST_FORMAT(NV12, rotator_4412_yuv_limits) },
+};
+
+static const struct rot_variant rotator_4210_data = {
+       .formats = rotator_4210_formats,
+       .num_formats = ARRAY_SIZE(rotator_4210_formats),
+};
+
+static const struct rot_variant rotator_4412_data = {
+       .formats = rotator_4412_formats,
+       .num_formats = ARRAY_SIZE(rotator_4412_formats),
+};
+
+static const struct rot_variant rotator_5250_data = {
+       .formats = rotator_5250_formats,
+       .num_formats = ARRAY_SIZE(rotator_5250_formats),
+};
+
+static const struct of_device_id exynos_rotator_match[] = {
+       {
+               .compatible = "samsung,exynos4210-rotator",
+               .data = &rotator_4210_data,
+       }, {
+               .compatible = "samsung,exynos4212-rotator",
+               .data = &rotator_4412_data,
+       }, {
+               .compatible = "samsung,exynos5250-rotator",
+               .data = &rotator_5250_data,
+       }, {
+       },
+};
+MODULE_DEVICE_TABLE(of, exynos_rotator_match);
 
 static const struct dev_pm_ops rotator_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(rotator_suspend, rotator_resume)
+       SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                               pm_runtime_force_resume)
        SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume,
                                                                        NULL)
 };
@@ -838,7 +438,7 @@ struct platform_driver rotator_driver = {
        .probe          = rotator_probe,
        .remove         = rotator_remove,
        .driver         = {
-               .name   = "exynos-rot",
+               .name   = "exynos-rotator",
                .owner  = THIS_MODULE,
                .pm     = &rotator_pm_ops,
                .of_match_table = exynos_rotator_match,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.h b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
deleted file mode 100644 (file)
index 71a0b4c..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (c) 2012 Samsung Electronics Co., Ltd.
- *
- * Authors:
- *     YoungJun Cho <yj44.cho@samsung.com>
- *     Eunchul Kim <chulspro.kim@samsung.com>
- *
- * 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;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#ifndef        _EXYNOS_DRM_ROTATOR_H_
-#define        _EXYNOS_DRM_ROTATOR_H_
-
-/* TODO */
-
-#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_scaler.c b/drivers/gpu/drm/exynos/exynos_drm_scaler.c
new file mode 100644 (file)
index 0000000..63b05b7
--- /dev/null
@@ -0,0 +1,694 @@
+/*
+ * Copyright (C) 2017 Samsung Electronics Co.Ltd
+ * Author:
+ *     Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundationr
+ */
+
+#include <linux/kernel.h>
+#include <linux/component.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drmP.h>
+#include <drm/exynos_drm.h>
+#include "regs-scaler.h"
+#include "exynos_drm_fb.h"
+#include "exynos_drm_drv.h"
+#include "exynos_drm_iommu.h"
+#include "exynos_drm_ipp.h"
+
+#define scaler_read(offset)            readl(scaler->regs + (offset))
+#define scaler_write(cfg, offset)      writel(cfg, scaler->regs + (offset))
+#define SCALER_MAX_CLK                 4
+#define SCALER_AUTOSUSPEND_DELAY       2000
+
+struct scaler_data {
+       const char      *clk_name[SCALER_MAX_CLK];
+       unsigned int    num_clk;
+       const struct exynos_drm_ipp_formats *formats;
+       unsigned int    num_formats;
+};
+
+struct scaler_context {
+       struct exynos_drm_ipp           ipp;
+       struct drm_device               *drm_dev;
+       struct device                   *dev;
+       void __iomem                    *regs;
+       struct clk                      *clock[SCALER_MAX_CLK];
+       struct exynos_drm_ipp_task      *task;
+       const struct scaler_data        *scaler_data;
+};
+
+static u32 scaler_get_format(u32 drm_fmt)
+{
+       switch (drm_fmt) {
+       case DRM_FORMAT_NV21:
+               return SCALER_YUV420_2P_UV;
+       case DRM_FORMAT_NV12:
+               return SCALER_YUV420_2P_VU;
+       case DRM_FORMAT_YUV420:
+               return SCALER_YUV420_3P;
+       case DRM_FORMAT_YUYV:
+               return SCALER_YUV422_1P_YUYV;
+       case DRM_FORMAT_UYVY:
+               return SCALER_YUV422_1P_UYVY;
+       case DRM_FORMAT_YVYU:
+               return SCALER_YUV422_1P_YVYU;
+       case DRM_FORMAT_NV61:
+               return SCALER_YUV422_2P_UV;
+       case DRM_FORMAT_NV16:
+               return SCALER_YUV422_2P_VU;
+       case DRM_FORMAT_YUV422:
+               return SCALER_YUV422_3P;
+       case DRM_FORMAT_NV42:
+               return SCALER_YUV444_2P_UV;
+       case DRM_FORMAT_NV24:
+               return SCALER_YUV444_2P_VU;
+       case DRM_FORMAT_YUV444:
+               return SCALER_YUV444_3P;
+       case DRM_FORMAT_RGB565:
+               return SCALER_RGB_565;
+       case DRM_FORMAT_XRGB1555:
+               return SCALER_ARGB1555;
+       case DRM_FORMAT_ARGB1555:
+               return SCALER_ARGB1555;
+       case DRM_FORMAT_XRGB4444:
+               return SCALER_ARGB4444;
+       case DRM_FORMAT_ARGB4444:
+               return SCALER_ARGB4444;
+       case DRM_FORMAT_XRGB8888:
+               return SCALER_ARGB8888;
+       case DRM_FORMAT_ARGB8888:
+               return SCALER_ARGB8888;
+       case DRM_FORMAT_RGBX8888:
+               return SCALER_RGBA8888;
+       case DRM_FORMAT_RGBA8888:
+               return SCALER_RGBA8888;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static inline void scaler_enable_int(struct scaler_context *scaler)
+{
+       u32 val;
+
+       val = SCALER_INT_EN_TIMEOUT |
+               SCALER_INT_EN_ILLEGAL_BLEND |
+               SCALER_INT_EN_ILLEGAL_RATIO |
+               SCALER_INT_EN_ILLEGAL_DST_HEIGHT |
+               SCALER_INT_EN_ILLEGAL_DST_WIDTH |
+               SCALER_INT_EN_ILLEGAL_DST_V_POS |
+               SCALER_INT_EN_ILLEGAL_DST_H_POS |
+               SCALER_INT_EN_ILLEGAL_DST_C_SPAN |
+               SCALER_INT_EN_ILLEGAL_DST_Y_SPAN |
+               SCALER_INT_EN_ILLEGAL_DST_CR_BASE |
+               SCALER_INT_EN_ILLEGAL_DST_CB_BASE |
+               SCALER_INT_EN_ILLEGAL_DST_Y_BASE |
+               SCALER_INT_EN_ILLEGAL_DST_COLOR |
+               SCALER_INT_EN_ILLEGAL_SRC_HEIGHT |
+               SCALER_INT_EN_ILLEGAL_SRC_WIDTH |
+               SCALER_INT_EN_ILLEGAL_SRC_CV_POS |
+               SCALER_INT_EN_ILLEGAL_SRC_CH_POS |
+               SCALER_INT_EN_ILLEGAL_SRC_YV_POS |
+               SCALER_INT_EN_ILLEGAL_SRC_YH_POS |
+               SCALER_INT_EN_ILLEGAL_DST_SPAN |
+               SCALER_INT_EN_ILLEGAL_SRC_Y_SPAN |
+               SCALER_INT_EN_ILLEGAL_SRC_CR_BASE |
+               SCALER_INT_EN_ILLEGAL_SRC_CB_BASE |
+               SCALER_INT_EN_ILLEGAL_SRC_Y_BASE |
+               SCALER_INT_EN_ILLEGAL_SRC_COLOR |
+               SCALER_INT_EN_FRAME_END;
+       scaler_write(val, SCALER_INT_EN);
+}
+
+static inline void scaler_set_src_fmt(struct scaler_context *scaler,
+       u32 src_fmt)
+{
+       u32 val;
+
+       val = SCALER_SRC_CFG_SET_COLOR_FORMAT(src_fmt);
+       scaler_write(val, SCALER_SRC_CFG);
+}
+
+static inline void scaler_set_src_base(struct scaler_context *scaler,
+       struct exynos_drm_ipp_buffer *src_buf)
+{
+       static unsigned int bases[] = {
+               SCALER_SRC_Y_BASE,
+               SCALER_SRC_CB_BASE,
+               SCALER_SRC_CR_BASE,
+       };
+       int i;
+
+       for (i = 0; i < src_buf->format->num_planes; ++i)
+               scaler_write(src_buf->dma_addr[i], bases[i]);
+}
+
+static inline void scaler_set_src_span(struct scaler_context *scaler,
+       struct exynos_drm_ipp_buffer *src_buf)
+{
+       u32 val;
+
+       val = SCALER_SRC_SPAN_SET_Y_SPAN(src_buf->buf.pitch[0] /
+               src_buf->format->cpp[0]);
+
+       if (src_buf->format->num_planes > 1)
+               val |= SCALER_SRC_SPAN_SET_C_SPAN(src_buf->buf.pitch[1]);
+
+       scaler_write(val, SCALER_SRC_SPAN);
+}
+
+static inline void scaler_set_src_luma_pos(struct scaler_context *scaler,
+       struct drm_exynos_ipp_task_rect *src_pos)
+{
+       u32 val;
+
+       val = SCALER_SRC_Y_POS_SET_YH_POS(src_pos->x << 2);
+       val |=  SCALER_SRC_Y_POS_SET_YV_POS(src_pos->y << 2);
+       scaler_write(val, SCALER_SRC_Y_POS);
+       scaler_write(val, SCALER_SRC_C_POS); /* ATTENTION! */
+}
+
+static inline void scaler_set_src_wh(struct scaler_context *scaler,
+       struct drm_exynos_ipp_task_rect *src_pos)
+{
+       u32 val;
+
+       val = SCALER_SRC_WH_SET_WIDTH(src_pos->w);
+       val |= SCALER_SRC_WH_SET_HEIGHT(src_pos->h);
+       scaler_write(val, SCALER_SRC_WH);
+}
+
+static inline void scaler_set_dst_fmt(struct scaler_context *scaler,
+       u32 dst_fmt)
+{
+       u32 val;
+
+       val = SCALER_DST_CFG_SET_COLOR_FORMAT(dst_fmt);
+       scaler_write(val, SCALER_DST_CFG);
+}
+
+static inline void scaler_set_dst_base(struct scaler_context *scaler,
+       struct exynos_drm_ipp_buffer *dst_buf)
+{
+       static unsigned int bases[] = {
+               SCALER_DST_Y_BASE,
+               SCALER_DST_CB_BASE,
+               SCALER_DST_CR_BASE,
+       };
+       int i;
+
+       for (i = 0; i < dst_buf->format->num_planes; ++i)
+               scaler_write(dst_buf->dma_addr[i], bases[i]);
+}
+
+static inline void scaler_set_dst_span(struct scaler_context *scaler,
+       struct exynos_drm_ipp_buffer *dst_buf)
+{
+       u32 val;
+
+       val = SCALER_DST_SPAN_SET_Y_SPAN(dst_buf->buf.pitch[0] /
+               dst_buf->format->cpp[0]);
+
+       if (dst_buf->format->num_planes > 1)
+               val |= SCALER_DST_SPAN_SET_C_SPAN(dst_buf->buf.pitch[1]);
+
+       scaler_write(val, SCALER_DST_SPAN);
+}
+
+static inline void scaler_set_dst_luma_pos(struct scaler_context *scaler,
+       struct drm_exynos_ipp_task_rect *dst_pos)
+{
+       u32 val;
+
+       val = SCALER_DST_WH_SET_WIDTH(dst_pos->w);
+       val |= SCALER_DST_WH_SET_HEIGHT(dst_pos->h);
+       scaler_write(val, SCALER_DST_WH);
+}
+
+static inline void scaler_set_dst_wh(struct scaler_context *scaler,
+       struct drm_exynos_ipp_task_rect *dst_pos)
+{
+       u32 val;
+
+       val = SCALER_DST_POS_SET_H_POS(dst_pos->x);
+       val |= SCALER_DST_POS_SET_V_POS(dst_pos->y);
+       scaler_write(val, SCALER_DST_POS);
+}
+
+static inline void scaler_set_hv_ratio(struct scaler_context *scaler,
+       unsigned int rotation,
+       struct drm_exynos_ipp_task_rect *src_pos,
+       struct drm_exynos_ipp_task_rect *dst_pos)
+{
+       u32 val, h_ratio, v_ratio;
+
+       if (drm_rotation_90_or_270(rotation)) {
+               h_ratio = (src_pos->h << 16) / dst_pos->w;
+               v_ratio = (src_pos->w << 16) / dst_pos->h;
+       } else {
+               h_ratio = (src_pos->w << 16) / dst_pos->w;
+               v_ratio = (src_pos->h << 16) / dst_pos->h;
+       }
+
+       val = SCALER_H_RATIO_SET(h_ratio);
+       scaler_write(val, SCALER_H_RATIO);
+
+       val = SCALER_V_RATIO_SET(v_ratio);
+       scaler_write(val, SCALER_V_RATIO);
+}
+
+static inline void scaler_set_rotation(struct scaler_context *scaler,
+       unsigned int rotation)
+{
+       u32 val = 0;
+
+       if (rotation & DRM_MODE_ROTATE_90)
+               val |= SCALER_ROT_CFG_SET_ROTMODE(SCALER_ROT_MODE_90);
+       else if (rotation & DRM_MODE_ROTATE_180)
+               val |= SCALER_ROT_CFG_SET_ROTMODE(SCALER_ROT_MODE_180);
+       else if (rotation & DRM_MODE_ROTATE_270)
+               val |= SCALER_ROT_CFG_SET_ROTMODE(SCALER_ROT_MODE_270);
+       if (rotation & DRM_MODE_REFLECT_X)
+               val |= SCALER_ROT_CFG_FLIP_X_EN;
+       if (rotation & DRM_MODE_REFLECT_Y)
+               val |= SCALER_ROT_CFG_FLIP_Y_EN;
+       scaler_write(val, SCALER_ROT_CFG);
+}
+
+static inline void scaler_set_csc(struct scaler_context *scaler,
+       const struct drm_format_info *fmt)
+{
+       static const u32 csc_mtx[2][3][3] = {
+               { /* YCbCr to RGB */
+                       {0x254, 0x000, 0x331},
+                       {0x254, 0xf38, 0xe60},
+                       {0x254, 0x409, 0x000},
+               },
+               { /* RGB to YCbCr */
+                       {0x084, 0x102, 0x032},
+                       {0xfb4, 0xf6b, 0x0e1},
+                       {0x0e1, 0xf44, 0xfdc},
+               },
+       };
+       int i, j, dir;
+
+       switch (fmt->format) {
+       case DRM_FORMAT_RGB565:
+       case DRM_FORMAT_XRGB1555:
+       case DRM_FORMAT_ARGB1555:
+       case DRM_FORMAT_XRGB4444:
+       case DRM_FORMAT_ARGB4444:
+       case DRM_FORMAT_XRGB8888:
+       case DRM_FORMAT_ARGB8888:
+       case DRM_FORMAT_RGBX8888:
+       case DRM_FORMAT_RGBA8888:
+               dir = 1;
+               break;
+       default:
+               dir = 0;
+       }
+
+       for (i = 0; i < 3; i++)
+               for (j = 0; j < 3; j++)
+                       scaler_write(csc_mtx[dir][i][j], SCALER_CSC_COEF(j, i));
+}
+
+static inline void scaler_set_timer(struct scaler_context *scaler,
+       unsigned int timer, unsigned int divider)
+{
+       u32 val;
+
+       val = SCALER_TIMEOUT_CTRL_TIMER_ENABLE;
+       val |= SCALER_TIMEOUT_CTRL_SET_TIMER_VALUE(timer);
+       val |= SCALER_TIMEOUT_CTRL_SET_TIMER_DIV(divider);
+       scaler_write(val, SCALER_TIMEOUT_CTRL);
+}
+
+static inline void scaler_start_hw(struct scaler_context *scaler)
+{
+       scaler_write(SCALER_CFG_START_CMD, SCALER_CFG);
+}
+
+static int scaler_commit(struct exynos_drm_ipp *ipp,
+                         struct exynos_drm_ipp_task *task)
+{
+       struct scaler_context *scaler =
+                       container_of(ipp, struct scaler_context, ipp);
+
+       u32 src_fmt = scaler_get_format(task->src.buf.fourcc);
+       struct drm_exynos_ipp_task_rect *src_pos = &task->src.rect;
+
+       u32 dst_fmt = scaler_get_format(task->dst.buf.fourcc);
+       struct drm_exynos_ipp_task_rect *dst_pos = &task->dst.rect;
+
+       scaler->task = task;
+
+       pm_runtime_get_sync(scaler->dev);
+
+       scaler_set_src_fmt(scaler, src_fmt);
+       scaler_set_src_base(scaler, &task->src);
+       scaler_set_src_span(scaler, &task->src);
+       scaler_set_src_luma_pos(scaler, src_pos);
+       scaler_set_src_wh(scaler, src_pos);
+
+       scaler_set_dst_fmt(scaler, dst_fmt);
+       scaler_set_dst_base(scaler, &task->dst);
+       scaler_set_dst_span(scaler, &task->dst);
+       scaler_set_dst_luma_pos(scaler, dst_pos);
+       scaler_set_dst_wh(scaler, dst_pos);
+
+       scaler_set_hv_ratio(scaler, task->transform.rotation, src_pos, dst_pos);
+       scaler_set_rotation(scaler, task->transform.rotation);
+
+       scaler_set_csc(scaler, task->src.format);
+
+       scaler_set_timer(scaler, 0xffff, 0xf);
+
+       scaler_enable_int(scaler);
+       scaler_start_hw(scaler);
+
+       return 0;
+}
+
+static struct exynos_drm_ipp_funcs ipp_funcs = {
+       .commit = scaler_commit,
+};
+
+static inline void scaler_disable_int(struct scaler_context *scaler)
+{
+       scaler_write(0, SCALER_INT_EN);
+}
+
+static inline u32 scaler_get_int_status(struct scaler_context *scaler)
+{
+       return scaler_read(SCALER_INT_STATUS);
+}
+
+static inline bool scaler_task_done(u32 val)
+{
+       return val & SCALER_INT_STATUS_FRAME_END ? 0 : -EINVAL;
+}
+
+static irqreturn_t scaler_irq_handler(int irq, void *arg)
+{
+       struct scaler_context *scaler = arg;
+
+       u32 val = scaler_get_int_status(scaler);
+
+       scaler_disable_int(scaler);
+
+       if (scaler->task) {
+               struct exynos_drm_ipp_task *task = scaler->task;
+
+               scaler->task = NULL;
+               pm_runtime_mark_last_busy(scaler->dev);
+               pm_runtime_put_autosuspend(scaler->dev);
+               exynos_drm_ipp_task_done(task, scaler_task_done(val));
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int scaler_bind(struct device *dev, struct device *master, void *data)
+{
+       struct scaler_context *scaler = dev_get_drvdata(dev);
+       struct drm_device *drm_dev = data;
+       struct exynos_drm_ipp *ipp = &scaler->ipp;
+
+       scaler->drm_dev = drm_dev;
+       drm_iommu_attach_device(drm_dev, dev);
+
+       exynos_drm_ipp_register(drm_dev, ipp, &ipp_funcs,
+                       DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE |
+                       DRM_EXYNOS_IPP_CAP_SCALE | DRM_EXYNOS_IPP_CAP_CONVERT,
+                       scaler->scaler_data->formats,
+                       scaler->scaler_data->num_formats, "scaler");
+
+       dev_info(dev, "The exynos scaler has been probed successfully\n");
+
+       return 0;
+}
+
+static void scaler_unbind(struct device *dev, struct device *master,
+                       void *data)
+{
+       struct scaler_context *scaler = dev_get_drvdata(dev);
+       struct drm_device *drm_dev = data;
+       struct exynos_drm_ipp *ipp = &scaler->ipp;
+
+       exynos_drm_ipp_unregister(drm_dev, ipp);
+       drm_iommu_detach_device(scaler->drm_dev, scaler->dev);
+}
+
+static const struct component_ops scaler_component_ops = {
+       .bind   = scaler_bind,
+       .unbind = scaler_unbind,
+};
+
+static int scaler_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct resource *regs_res;
+       struct scaler_context *scaler;
+       int irq;
+       int ret, i;
+
+       scaler = devm_kzalloc(dev, sizeof(*scaler), GFP_KERNEL);
+       if (!scaler)
+               return -ENOMEM;
+
+       scaler->scaler_data =
+               (struct scaler_data *)of_device_get_match_data(dev);
+
+       scaler->dev = dev;
+       regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       scaler->regs = devm_ioremap_resource(dev, regs_res);
+       if (IS_ERR(scaler->regs))
+               return PTR_ERR(scaler->regs);
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(dev, "failed to get irq\n");
+               return irq;
+       }
+
+       ret = devm_request_threaded_irq(dev, irq, NULL, scaler_irq_handler,
+                                       IRQF_ONESHOT, "drm_scaler", scaler);
+       if (ret < 0) {
+               dev_err(dev, "failed to request irq\n");
+               return ret;
+       }
+
+       for (i = 0; i < scaler->scaler_data->num_clk; ++i) {
+               scaler->clock[i] = devm_clk_get(dev,
+                                             scaler->scaler_data->clk_name[i]);
+               if (IS_ERR(scaler->clock[i])) {
+                       dev_err(dev, "failed to get clock\n");
+                       return PTR_ERR(scaler->clock[i]);
+               }
+       }
+
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_set_autosuspend_delay(dev, SCALER_AUTOSUSPEND_DELAY);
+       pm_runtime_enable(dev);
+       platform_set_drvdata(pdev, scaler);
+
+       ret = component_add(dev, &scaler_component_ops);
+       if (ret)
+               goto err_ippdrv_register;
+
+       return 0;
+
+err_ippdrv_register:
+       pm_runtime_dont_use_autosuspend(dev);
+       pm_runtime_disable(dev);
+       return ret;
+}
+
+static int scaler_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+
+       component_del(dev, &scaler_component_ops);
+       pm_runtime_dont_use_autosuspend(dev);
+       pm_runtime_disable(dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int clk_disable_unprepare_wrapper(struct clk *clk)
+{
+       clk_disable_unprepare(clk);
+
+       return 0;
+}
+
+static int scaler_clk_ctrl(struct scaler_context *scaler, bool enable)
+{
+       int (*clk_fun)(struct clk *clk), i;
+
+       clk_fun = enable ? clk_prepare_enable : clk_disable_unprepare_wrapper;
+
+       for (i = 0; i < scaler->scaler_data->num_clk; ++i)
+               clk_fun(scaler->clock[i]);
+
+       return 0;
+}
+
+static int scaler_runtime_suspend(struct device *dev)
+{
+       struct scaler_context *scaler = dev_get_drvdata(dev);
+
+       return  scaler_clk_ctrl(scaler, false);
+}
+
+static int scaler_runtime_resume(struct device *dev)
+{
+       struct scaler_context *scaler = dev_get_drvdata(dev);
+
+       return  scaler_clk_ctrl(scaler, true);
+}
+#endif
+
+static const struct dev_pm_ops scaler_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                               pm_runtime_force_resume)
+       SET_RUNTIME_PM_OPS(scaler_runtime_suspend, scaler_runtime_resume, NULL)
+};
+
+static const struct drm_exynos_ipp_limit scaler_5420_two_pixel_hv_limits[] = {
+       { IPP_SIZE_LIMIT(BUFFER, .h = { 16, SZ_8K }, .v = { 16, SZ_8K }) },
+       { IPP_SIZE_LIMIT(AREA, .h.align = 2, .v.align = 2) },
+       { IPP_SCALE_LIMIT(.h = { 65536 * 1 / 4, 65536 * 16 },
+                         .v = { 65536 * 1 / 4, 65536 * 16 }) },
+};
+
+static const struct drm_exynos_ipp_limit scaler_5420_two_pixel_h_limits[] = {
+       { IPP_SIZE_LIMIT(BUFFER, .h = { 16, SZ_8K }, .v = { 16, SZ_8K }) },
+       { IPP_SIZE_LIMIT(AREA, .h.align = 2, .v.align = 1) },
+       { IPP_SCALE_LIMIT(.h = { 65536 * 1 / 4, 65536 * 16 },
+                         .v = { 65536 * 1 / 4, 65536 * 16 }) },
+};
+
+static const struct drm_exynos_ipp_limit scaler_5420_one_pixel_limits[] = {
+       { IPP_SIZE_LIMIT(BUFFER, .h = { 16, SZ_8K }, .v = { 16, SZ_8K }) },
+       { IPP_SCALE_LIMIT(.h = { 65536 * 1 / 4, 65536 * 16 },
+                         .v = { 65536 * 1 / 4, 65536 * 16 }) },
+};
+
+static const struct exynos_drm_ipp_formats exynos5420_formats[] = {
+       /* SCALER_YUV420_2P_UV */
+       { IPP_SRCDST_FORMAT(NV21, scaler_5420_two_pixel_hv_limits) },
+
+       /* SCALER_YUV420_2P_VU */
+       { IPP_SRCDST_FORMAT(NV12, scaler_5420_two_pixel_hv_limits) },
+
+       /* SCALER_YUV420_3P */
+       { IPP_SRCDST_FORMAT(YUV420, scaler_5420_two_pixel_hv_limits) },
+
+       /* SCALER_YUV422_1P_YUYV */
+       { IPP_SRCDST_FORMAT(YUYV, scaler_5420_two_pixel_h_limits) },
+
+       /* SCALER_YUV422_1P_UYVY */
+       { IPP_SRCDST_FORMAT(UYVY, scaler_5420_two_pixel_h_limits) },
+
+       /* SCALER_YUV422_1P_YVYU */
+       { IPP_SRCDST_FORMAT(YVYU, scaler_5420_two_pixel_h_limits) },
+
+       /* SCALER_YUV422_2P_UV */
+       { IPP_SRCDST_FORMAT(NV61, scaler_5420_two_pixel_h_limits) },
+
+       /* SCALER_YUV422_2P_VU */
+       { IPP_SRCDST_FORMAT(NV16, scaler_5420_two_pixel_h_limits) },
+
+       /* SCALER_YUV422_3P */
+       { IPP_SRCDST_FORMAT(YUV422, scaler_5420_two_pixel_h_limits) },
+
+       /* SCALER_YUV444_2P_UV */
+       { IPP_SRCDST_FORMAT(NV42, scaler_5420_one_pixel_limits) },
+
+       /* SCALER_YUV444_2P_VU */
+       { IPP_SRCDST_FORMAT(NV24, scaler_5420_one_pixel_limits) },
+
+       /* SCALER_YUV444_3P */
+       { IPP_SRCDST_FORMAT(YUV444, scaler_5420_one_pixel_limits) },
+
+       /* SCALER_RGB_565 */
+       { IPP_SRCDST_FORMAT(RGB565, scaler_5420_one_pixel_limits) },
+
+       /* SCALER_ARGB1555 */
+       { IPP_SRCDST_FORMAT(XRGB1555, scaler_5420_one_pixel_limits) },
+
+       /* SCALER_ARGB1555 */
+       { IPP_SRCDST_FORMAT(ARGB1555, scaler_5420_one_pixel_limits) },
+
+       /* SCALER_ARGB4444 */
+       { IPP_SRCDST_FORMAT(XRGB4444, scaler_5420_one_pixel_limits) },
+
+       /* SCALER_ARGB4444 */
+       { IPP_SRCDST_FORMAT(ARGB4444, scaler_5420_one_pixel_limits) },
+
+       /* SCALER_ARGB8888 */
+       { IPP_SRCDST_FORMAT(XRGB8888, scaler_5420_one_pixel_limits) },
+
+       /* SCALER_ARGB8888 */
+       { IPP_SRCDST_FORMAT(ARGB8888, scaler_5420_one_pixel_limits) },
+
+       /* SCALER_RGBA8888 */
+       { IPP_SRCDST_FORMAT(RGBX8888, scaler_5420_one_pixel_limits) },
+
+       /* SCALER_RGBA8888 */
+       { IPP_SRCDST_FORMAT(RGBA8888, scaler_5420_one_pixel_limits) },
+};
+
+static const struct scaler_data exynos5420_data = {
+       .clk_name       = {"mscl"},
+       .num_clk        = 1,
+       .formats        = exynos5420_formats,
+       .num_formats    = ARRAY_SIZE(exynos5420_formats),
+};
+
+static const struct scaler_data exynos5433_data = {
+       .clk_name       = {"pclk", "aclk", "aclk_xiu"},
+       .num_clk        = 3,
+       .formats        = exynos5420_formats, /* intentional */
+       .num_formats    = ARRAY_SIZE(exynos5420_formats),
+};
+
+static const struct of_device_id exynos_scaler_match[] = {
+       {
+               .compatible = "samsung,exynos5420-scaler",
+               .data = &exynos5420_data,
+       }, {
+               .compatible = "samsung,exynos5433-scaler",
+               .data = &exynos5433_data,
+       }, {
+       },
+};
+MODULE_DEVICE_TABLE(of, exynos_scaler_match);
+
+struct platform_driver scaler_driver = {
+       .probe          = scaler_probe,
+       .remove         = scaler_remove,
+       .driver         = {
+               .name   = "exynos-scaler",
+               .owner  = THIS_MODULE,
+               .pm     = &scaler_pm_ops,
+               .of_match_table = exynos_scaler_match,
+       },
+};
diff --git a/drivers/gpu/drm/exynos/regs-scaler.h b/drivers/gpu/drm/exynos/regs-scaler.h
new file mode 100644 (file)
index 0000000..fc7ccad
--- /dev/null
@@ -0,0 +1,426 @@
+/* drivers/gpu/drm/exynos/regs-scaler.h
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com/
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * Register definition file for Samsung scaler driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef EXYNOS_REGS_SCALER_H
+#define EXYNOS_REGS_SCALER_H
+
+/* Register part */
+
+/* Global setting */
+#define SCALER_STATUS                  0x0     /* no shadow */
+#define SCALER_CFG                     0x4
+
+/* Interrupt */
+#define SCALER_INT_EN                  0x8     /* no shadow */
+#define SCALER_INT_STATUS              0xc     /* no shadow */
+
+/* SRC */
+#define SCALER_SRC_CFG                 0x10
+#define SCALER_SRC_Y_BASE              0x14
+#define SCALER_SRC_CB_BASE             0x18
+#define SCALER_SRC_CR_BASE             0x294
+#define SCALER_SRC_SPAN                        0x1c
+#define SCALER_SRC_Y_POS               0x20
+#define SCALER_SRC_WH                  0x24
+#define SCALER_SRC_C_POS               0x28
+
+/* DST */
+#define SCALER_DST_CFG                 0x30
+#define SCALER_DST_Y_BASE              0x34
+#define SCALER_DST_CB_BASE             0x38
+#define SCALER_DST_CR_BASE             0x298
+#define SCALER_DST_SPAN                        0x3c
+#define SCALER_DST_WH                  0x40
+#define SCALER_DST_POS                 0x44
+
+/* Ratio */
+#define SCALER_H_RATIO                 0x50
+#define SCALER_V_RATIO                 0x54
+
+/* Rotation */
+#define SCALER_ROT_CFG                 0x58
+
+/* Coefficient */
+/*
+ * YHCOEF_{x}{A|B|C|D}                 CHCOEF_{x}{A|B|C|D}
+ *
+ *     A       B       C       D       A       B       C       D
+ * 0   60      64      68      6c      140     144     148     14c
+ * 1   70      74      78      7c      150     154     158     15c
+ * 2   80      84      88      8c      160     164     168     16c
+ * 3   90      94      98      9c      170     174     178     17c
+ * 4   a0      a4      a8      ac      180     184     188     18c
+ * 5   b0      b4      b8      bc      190     194     198     19c
+ * 6   c0      c4      c8      cc      1a0     1a4     1a8     1ac
+ * 7   d0      d4      d8      dc      1b0     1b4     1b8     1bc
+ * 8   e0      e4      e8      ec      1c0     1c4     1c8     1cc
+ *
+ *
+ * YVCOEF_{x}{A|B}                     CVCOEF_{x}{A|B}
+ *
+ *     A       B                       A       B
+ * 0   f0      f4                      1d0     1d4
+ * 1   f8      fc                      1d8     1dc
+ * 2   100     104                     1e0     1e4
+ * 3   108     10c                     1e8     1ec
+ * 4   110     114                     1f0     1f4
+ * 5   118     11c                     1f8     1fc
+ * 6   120     124                     200     204
+ * 7   128     12c                     208     20c
+ * 8   130     134                     210     214
+ */
+#define _SCALER_HCOEF_DELTA(r, c)      ((r) * 0x10 + (c) * 0x4)
+#define _SCALER_VCOEF_DELTA(r, c)      ((r) * 0x8 + (c) * 0x4)
+
+#define SCALER_YHCOEF(r, c)            (0x60 + _SCALER_HCOEF_DELTA((r), (c)))
+#define SCALER_YVCOEF(r, c)            (0xf0 + _SCALER_VCOEF_DELTA((r), (c)))
+#define SCALER_CHCOEF(r, c)            (0x140 + _SCALER_HCOEF_DELTA((r), (c)))
+#define SCALER_CVCOEF(r, c)            (0x1d0 + _SCALER_VCOEF_DELTA((r), (c)))
+
+
+/* Color Space Conversion */
+#define SCALER_CSC_COEF(x, y)          (0x220 + (y) * 0xc + (x) * 0x4)
+
+/* Dithering */
+#define SCALER_DITH_CFG                        0x250
+
+/* Version Number */
+#define SCALER_VER                     0x260   /* no shadow */
+
+/* Cycle count and Timeout */
+#define SCALER_CYCLE_COUNT             0x278   /* no shadow */
+#define SCALER_TIMEOUT_CTRL            0x2c0   /* no shadow */
+#define SCALER_TIMEOUT_CNT             0x2c4   /* no shadow */
+
+/* Blending */
+#define SCALER_SRC_BLEND_COLOR         0x280
+#define SCALER_SRC_BLEND_ALPHA         0x284
+#define SCALER_DST_BLEND_COLOR         0x288
+#define SCALER_DST_BLEND_ALPHA         0x28c
+
+/* Color Fill */
+#define SCALER_FILL_COLOR              0x290
+
+/* Multiple Command Queue */
+#define SCALER_ADDR_Q_CONFIG           0x2a0   /* no shadow */
+#define SCALER_SRC_ADDR_Q_STATUS       0x2a4   /* no shadow */
+#define SCALER_SRC_ADDR_Q              0x2a8   /* no shadow */
+
+/* CRC */
+#define SCALER_CRC_COLOR00_10          0x2b0   /* no shadow */
+#define SCALER_CRC_COLOR20_30          0x2b4   /* no shadow */
+#define SCALER_CRC_COLOR01_11          0x2b8   /* no shadow */
+#define SCALER_CRC_COLOR21_31          0x2bc   /* no shadow */
+
+/* Shadow Registers */
+#define SCALER_SHADOW_OFFSET           0x1000
+
+
+/* Bit definition part */
+#define SCALER_MASK(hi_b, lo_b)                ((1 << ((hi_b) - (lo_b) + 1)) - 1)
+#define SCALER_GET(reg, hi_b, lo_b)    \
+       (((reg) >> (lo_b)) & SCALER_MASK(hi_b, lo_b))
+#define SCALER_SET(val, hi_b, lo_b) \
+       (((val) & SCALER_MASK(hi_b, lo_b)) << lo_b)
+
+/* SCALER_STATUS */
+#define SCALER_STATUS_SCALER_RUNNING           (1 << 1)
+#define SCALER_STATUS_SCALER_READY_CLK_DOWN    (1 << 0)
+
+/* SCALER_CFG */
+#define SCALER_CFG_FILL_EN                     (1 << 24)
+#define SCALER_CFG_BLEND_COLOR_DIVIDE_ALPHA_EN (1 << 17)
+#define SCALER_CFG_BLEND_EN                    (1 << 16)
+#define SCALER_CFG_CSC_Y_OFFSET_SRC_EN         (1 << 10)
+#define SCALER_CFG_CSC_Y_OFFSET_DST_EN         (1 << 9)
+#define SCALER_CFG_16_BURST_MODE               (1 << 8)
+#define SCALER_CFG_SOFT_RESET                  (1 << 1)
+#define SCALER_CFG_START_CMD                   (1 << 0)
+
+/* SCALER_INT_EN */
+#define SCALER_INT_EN_TIMEOUT                  (1 << 31)
+#define SCALER_INT_EN_ILLEGAL_BLEND            (1 << 24)
+#define SCALER_INT_EN_ILLEGAL_RATIO            (1 << 23)
+#define SCALER_INT_EN_ILLEGAL_DST_HEIGHT       (1 << 22)
+#define SCALER_INT_EN_ILLEGAL_DST_WIDTH                (1 << 21)
+#define SCALER_INT_EN_ILLEGAL_DST_V_POS                (1 << 20)
+#define SCALER_INT_EN_ILLEGAL_DST_H_POS                (1 << 19)
+#define SCALER_INT_EN_ILLEGAL_DST_C_SPAN       (1 << 18)
+#define SCALER_INT_EN_ILLEGAL_DST_Y_SPAN       (1 << 17)
+#define SCALER_INT_EN_ILLEGAL_DST_CR_BASE      (1 << 16)
+#define SCALER_INT_EN_ILLEGAL_DST_CB_BASE      (1 << 15)
+#define SCALER_INT_EN_ILLEGAL_DST_Y_BASE       (1 << 14)
+#define SCALER_INT_EN_ILLEGAL_DST_COLOR                (1 << 13)
+#define SCALER_INT_EN_ILLEGAL_SRC_HEIGHT       (1 << 12)
+#define SCALER_INT_EN_ILLEGAL_SRC_WIDTH                (1 << 11)
+#define SCALER_INT_EN_ILLEGAL_SRC_CV_POS       (1 << 10)
+#define SCALER_INT_EN_ILLEGAL_SRC_CH_POS       (1 << 9)
+#define SCALER_INT_EN_ILLEGAL_SRC_YV_POS       (1 << 8)
+#define SCALER_INT_EN_ILLEGAL_SRC_YH_POS       (1 << 7)
+#define SCALER_INT_EN_ILLEGAL_DST_SPAN         (1 << 6)
+#define SCALER_INT_EN_ILLEGAL_SRC_Y_SPAN       (1 << 5)
+#define SCALER_INT_EN_ILLEGAL_SRC_CR_BASE      (1 << 4)
+#define SCALER_INT_EN_ILLEGAL_SRC_CB_BASE      (1 << 3)
+#define SCALER_INT_EN_ILLEGAL_SRC_Y_BASE       (1 << 2)
+#define SCALER_INT_EN_ILLEGAL_SRC_COLOR                (1 << 1)
+#define SCALER_INT_EN_FRAME_END                        (1 << 0)
+
+/* SCALER_INT_STATUS */
+#define SCALER_INT_STATUS_TIMEOUT              (1 << 31)
+#define SCALER_INT_STATUS_ILLEGAL_BLEND                (1 << 24)
+#define SCALER_INT_STATUS_ILLEGAL_RATIO                (1 << 23)
+#define SCALER_INT_STATUS_ILLEGAL_DST_HEIGHT   (1 << 22)
+#define SCALER_INT_STATUS_ILLEGAL_DST_WIDTH    (1 << 21)
+#define SCALER_INT_STATUS_ILLEGAL_DST_V_POS    (1 << 20)
+#define SCALER_INT_STATUS_ILLEGAL_DST_H_POS    (1 << 19)
+#define SCALER_INT_STATUS_ILLEGAL_DST_C_SPAN   (1 << 18)
+#define SCALER_INT_STATUS_ILLEGAL_DST_Y_SPAN   (1 << 17)
+#define SCALER_INT_STATUS_ILLEGAL_DST_CR_BASE  (1 << 16)
+#define SCALER_INT_STATUS_ILLEGAL_DST_CB_BASE  (1 << 15)
+#define SCALER_INT_STATUS_ILLEGAL_DST_Y_BASE   (1 << 14)
+#define SCALER_INT_STATUS_ILLEGAL_DST_COLOR    (1 << 13)
+#define SCALER_INT_STATUS_ILLEGAL_SRC_HEIGHT   (1 << 12)
+#define SCALER_INT_STATUS_ILLEGAL_SRC_WIDTH    (1 << 11)
+#define SCALER_INT_STATUS_ILLEGAL_SRC_CV_POS   (1 << 10)
+#define SCALER_INT_STATUS_ILLEGAL_SRC_CH_POS   (1 << 9)
+#define SCALER_INT_STATUS_ILLEGAL_SRC_YV_POS   (1 << 8)
+#define SCALER_INT_STATUS_ILLEGAL_SRC_YH_POS   (1 << 7)
+#define SCALER_INT_STATUS_ILLEGAL_DST_SPAN     (1 << 6)
+#define SCALER_INT_STATUS_ILLEGAL_SRC_Y_SPAN   (1 << 5)
+#define SCALER_INT_STATUS_ILLEGAL_SRC_CR_BASE  (1 << 4)
+#define SCALER_INT_STATUS_ILLEGAL_SRC_CB_BASE  (1 << 3)
+#define SCALER_INT_STATUS_ILLEGAL_SRC_Y_BASE   (1 << 2)
+#define SCALER_INT_STATUS_ILLEGAL_SRC_COLOR    (1 << 1)
+#define SCALER_INT_STATUS_FRAME_END            (1 << 0)
+
+/* SCALER_SRC_CFG */
+#define SCALER_SRC_CFG_TILE_EN                 (1 << 10)
+#define SCALER_SRC_CFG_GET_BYTE_SWAP(r)                SCALER_GET(r, 6, 5)
+#define SCALER_SRC_CFG_SET_BYTE_SWAP(v)                SCALER_SET(v, 6, 5)
+#define SCALER_SRC_CFG_GET_COLOR_FORMAT(r)     SCALER_GET(r, 4, 0)
+#define SCALER_SRC_CFG_SET_COLOR_FORMAT(v)     SCALER_SET(v, 4, 0)
+#define SCALER_YUV420_2P_UV                    0
+#define SCALER_YUV422_2P_UV                    2
+#define SCALER_YUV444_2P_UV                    3
+#define SCALER_RGB_565                         4
+#define SCALER_ARGB1555                                5
+#define SCALER_ARGB8888                                6
+#define SCALER_ARGB8888_PRE                    7
+#define SCALER_YUV422_1P_YVYU                  9
+#define SCALER_YUV422_1P_YUYV                  10
+#define SCALER_YUV422_1P_UYVY                  11
+#define SCALER_ARGB4444                                12
+#define SCALER_L8A8                            13
+#define SCALER_RGBA8888                                14
+#define SCALER_L8                              15
+#define SCALER_YUV420_2P_VU                    16
+#define SCALER_YUV422_2P_VU                    18
+#define SCALER_YUV444_2P_VU                    19
+#define SCALER_YUV420_3P                       20
+#define SCALER_YUV422_3P                       22
+#define SCALER_YUV444_3P                       23
+
+/* SCALER_SRC_SPAN */
+#define SCALER_SRC_SPAN_GET_C_SPAN(r)          SCALER_GET(r, 29, 16)
+#define SCALER_SRC_SPAN_SET_C_SPAN(v)          SCALER_SET(v, 29, 16)
+#define SCALER_SRC_SPAN_GET_Y_SPAN(r)          SCALER_GET(r, 13, 0)
+#define SCALER_SRC_SPAN_SET_Y_SPAN(v)          SCALER_SET(v, 13, 0)
+
+/* SCALER_SRC_Y_POS */
+#define SCALER_SRC_Y_POS_GET_YH_POS(r)         SCALER_GET(r, 31, 16)
+#define SCALER_SRC_Y_POS_SET_YH_POS(v)         SCALER_SET(v, 31, 16)
+#define SCALER_SRC_Y_POS_GET_YV_POS(r)         SCALER_GET(r, 15, 0)
+#define SCALER_SRC_Y_POS_SET_YV_POS(v)         SCALER_SET(v, 15, 0)
+
+/* SCALER_SRC_WH */
+#define SCALER_SRC_WH_GET_WIDTH(r)             SCALER_GET(r, 29, 16)
+#define SCALER_SRC_WH_SET_WIDTH(v)             SCALER_SET(v, 29, 16)
+#define SCALER_SRC_WH_GET_HEIGHT(r)            SCALER_GET(r, 13, 0)
+#define SCALER_SRC_WH_SET_HEIGHT(v)            SCALER_SET(v, 13, 0)
+
+/* SCALER_SRC_C_POS */
+#define SCALER_SRC_C_POS_GET_CH_POS(r)         SCALER_GET(r, 31, 16)
+#define SCALER_SRC_C_POS_SET_CH_POS(v)         SCALER_SET(v, 31, 16)
+#define SCALER_SRC_C_POS_GET_CV_POS(r)         SCALER_GET(r, 15, 0)
+#define SCALER_SRC_C_POS_SET_CV_POS(v)         SCALER_SET(v, 15, 0)
+
+/* SCALER_DST_CFG */
+#define SCALER_DST_CFG_GET_BYTE_SWAP(r)                SCALER_GET(r, 6, 5)
+#define SCALER_DST_CFG_SET_BYTE_SWAP(v)                SCALER_SET(v, 6, 5)
+#define SCALER_DST_CFG_GET_COLOR_FORMAT(r)     SCALER_GET(r, 4, 0)
+#define SCALER_DST_CFG_SET_COLOR_FORMAT(v)     SCALER_SET(v, 4, 0)
+
+/* SCALER_DST_SPAN */
+#define SCALER_DST_SPAN_GET_C_SPAN(r)          SCALER_GET(r, 29, 16)
+#define SCALER_DST_SPAN_SET_C_SPAN(v)          SCALER_SET(v, 29, 16)
+#define SCALER_DST_SPAN_GET_Y_SPAN(r)          SCALER_GET(r, 13, 0)
+#define SCALER_DST_SPAN_SET_Y_SPAN(v)          SCALER_SET(v, 13, 0)
+
+/* SCALER_DST_WH */
+#define SCALER_DST_WH_GET_WIDTH(r)             SCALER_GET(r, 29, 16)
+#define SCALER_DST_WH_SET_WIDTH(v)             SCALER_SET(v, 29, 16)
+#define SCALER_DST_WH_GET_HEIGHT(r)            SCALER_GET(r, 13, 0)
+#define SCALER_DST_WH_SET_HEIGHT(v)            SCALER_SET(v, 13, 0)
+
+/* SCALER_DST_POS */
+#define SCALER_DST_POS_GET_H_POS(r)            SCALER_GET(r, 29, 16)
+#define SCALER_DST_POS_SET_H_POS(v)            SCALER_SET(v, 29, 16)
+#define SCALER_DST_POS_GET_V_POS(r)            SCALER_GET(r, 13, 0)
+#define SCALER_DST_POS_SET_V_POS(v)            SCALER_SET(v, 13, 0)
+
+/* SCALER_H_RATIO */
+#define SCALER_H_RATIO_GET(r)                  SCALER_GET(r, 18, 0)
+#define SCALER_H_RATIO_SET(v)                  SCALER_SET(v, 18, 0)
+
+/* SCALER_V_RATIO */
+#define SCALER_V_RATIO_GET(r)                  SCALER_GET(r, 18, 0)
+#define SCALER_V_RATIO_SET(v)                  SCALER_SET(v, 18, 0)
+
+/* SCALER_ROT_CFG */
+#define SCALER_ROT_CFG_FLIP_X_EN               (1 << 3)
+#define SCALER_ROT_CFG_FLIP_Y_EN               (1 << 2)
+#define SCALER_ROT_CFG_GET_ROTMODE(r)          SCALER_GET(r, 1, 0)
+#define SCALER_ROT_CFG_SET_ROTMODE(v)          SCALER_SET(v, 1, 0)
+#define SCALER_ROT_MODE_90                     1
+#define SCALER_ROT_MODE_180                    2
+#define SCALER_ROT_MODE_270                    3
+
+/* SCALER_HCOEF, SCALER_VCOEF */
+#define SCALER_COEF_SHIFT(i)                   (16 * (1 - (i) % 2))
+#define SCALER_COEF_GET(r, i)                  \
+                               (((r) >> SCALER_COEF_SHIFT(i)) & 0x1ff)
+#define SCALER_COEF_SET(v, i)                  \
+                               (((v) & 0x1ff) << SCALER_COEF_SHIFT(i))
+
+/* SCALER_CSC_COEFxy */
+#define SCALER_CSC_COEF_GET(r)                 SCALER_GET(r, 11, 0)
+#define SCALER_CSC_COEF_SET(v)                 SCALER_SET(v, 11, 0)
+
+/* SCALER_DITH_CFG */
+#define SCALER_DITH_CFG_GET_R_TYPE(r)          SCALER_GET(r, 8, 6)
+#define SCALER_DITH_CFG_SET_R_TYPE(v)          SCALER_SET(v, 8, 6)
+#define SCALER_DITH_CFG_GET_G_TYPE(r)          SCALER_GET(r, 5, 3)
+#define SCALER_DITH_CFG_SET_G_TYPE(v)          SCALER_SET(v, 5, 3)
+#define SCALER_DITH_CFG_GET_B_TYPE(r)          SCALER_GET(r, 2, 0)
+#define SCALER_DITH_CFG_SET_B_TYPE(v)          SCALER_SET(v, 2, 0)
+
+/* SCALER_TIMEOUT_CTRL */
+#define SCALER_TIMEOUT_CTRL_GET_TIMER_VALUE(r) SCALER_GET(r, 31, 16)
+#define SCALER_TIMEOUT_CTRL_SET_TIMER_VALUE(v) SCALER_SET(v, 31, 16)
+#define SCALER_TIMEOUT_CTRL_GET_TIMER_DIV(r)   SCALER_GET(r, 7, 4)
+#define SCALER_TIMEOUT_CTRL_SET_TIMER_DIV(v)   SCALER_SET(v, 7, 4)
+#define SCALER_TIMEOUT_CTRL_TIMER_ENABLE       (1 << 0)
+
+/* SCALER_TIMEOUT_CNT */
+#define SCALER_TIMEOUT_CTRL_GET_TIMER_COUNT(r) SCALER_GET(r, 31, 16)
+
+/* SCALER_SRC_BLEND_COLOR */
+#define SCALER_SRC_BLEND_COLOR_SEL_INV         (1 << 31)
+#define SCALER_SRC_BLEND_COLOR_GET_SEL(r)      SCALER_GET(r, 30, 29)
+#define SCALER_SRC_BLEND_COLOR_SET_SEL(v)      SCALER_SET(v, 30, 29)
+#define SCALER_SRC_BLEND_COLOR_OP_SEL_INV      (1 << 28)
+#define SCALER_SRC_BLEND_COLOR_GET_OP_SEL(r)   SCALER_GET(r, 27, 24)
+#define SCALER_SRC_BLEND_COLOR_SET_OP_SEL(v)   SCALER_SET(v, 27, 24)
+#define SCALER_SRC_BLEND_COLOR_GET_COLOR0(r)   SCALER_GET(r, 23, 16)
+#define SCALER_SRC_BLEND_COLOR_SET_COLOR0(v)   SCALER_SET(v, 23, 16)
+#define SCALER_SRC_BLEND_COLOR_GET_COLOR1(r)   SCALER_GET(r, 15, 8)
+#define SCALER_SRC_BLEND_COLOR_SET_COLOR1(v)   SCALER_SET(v, 15, 8)
+#define SCALER_SRC_BLEND_COLOR_GET_COLOR2(r)   SCALER_GET(r, 7, 0)
+#define SCALER_SRC_BLEND_COLOR_SET_COLOR2(v)   SCALER_SET(v, 7, 0)
+
+/* SCALER_SRC_BLEND_ALPHA */
+#define SCALER_SRC_BLEND_ALPHA_SEL_INV         (1 << 31)
+#define SCALER_SRC_BLEND_ALPHA_GET_SEL(r)      SCALER_GET(r, 30, 29)
+#define SCALER_SRC_BLEND_ALPHA_SET_SEL(v)      SCALER_SET(v, 30, 29)
+#define SCALER_SRC_BLEND_ALPHA_OP_SEL_INV      (1 << 28)
+#define SCALER_SRC_BLEND_ALPHA_GET_OP_SEL(r)   SCALER_GET(r, 27, 24)
+#define SCALER_SRC_BLEND_ALPHA_SET_OP_SEL(v)   SCALER_SET(v, 27, 24)
+#define SCALER_SRC_BLEND_ALPHA_GET_ALPHA(r)    SCALER_GET(r, 7, 0)
+#define SCALER_SRC_BLEND_ALPHA_SET_ALPHA(v)    SCALER_SET(v, 7, 0)
+
+/* SCALER_DST_BLEND_COLOR */
+#define SCALER_DST_BLEND_COLOR_SEL_INV         (1 << 31)
+#define SCALER_DST_BLEND_COLOR_GET_SEL(r)      SCALER_GET(r, 30, 29)
+#define SCALER_DST_BLEND_COLOR_SET_SEL(v)      SCALER_SET(v, 30, 29)
+#define SCALER_DST_BLEND_COLOR_OP_SEL_INV      (1 << 28)
+#define SCALER_DST_BLEND_COLOR_GET_OP_SEL(r)   SCALER_GET(r, 27, 24)
+#define SCALER_DST_BLEND_COLOR_SET_OP_SEL(v)   SCALER_SET(v, 27, 24)
+#define SCALER_DST_BLEND_COLOR_GET_COLOR0(r)   SCALER_GET(r, 23, 16)
+#define SCALER_DST_BLEND_COLOR_SET_COLOR0(v)   SCALER_SET(v, 23, 16)
+#define SCALER_DST_BLEND_COLOR_GET_COLOR1(r)   SCALER_GET(r, 15, 8)
+#define SCALER_DST_BLEND_COLOR_SET_COLOR1(v)   SCALER_SET(v, 15, 8)
+#define SCALER_DST_BLEND_COLOR_GET_COLOR2(r)   SCALER_GET(r, 7, 0)
+#define SCALER_DST_BLEND_COLOR_SET_COLOR2(v)   SCALER_SET(v, 7, 0)
+
+/* SCALER_DST_BLEND_ALPHA */
+#define SCALER_DST_BLEND_ALPHA_SEL_INV         (1 << 31)
+#define SCALER_DST_BLEND_ALPHA_GET_SEL(r)      SCALER_GET(r, 30, 29)
+#define SCALER_DST_BLEND_ALPHA_SET_SEL(v)      SCALER_SET(v, 30, 29)
+#define SCALER_DST_BLEND_ALPHA_OP_SEL_INV      (1 << 28)
+#define SCALER_DST_BLEND_ALPHA_GET_OP_SEL(r)   SCALER_GET(r, 27, 24)
+#define SCALER_DST_BLEND_ALPHA_SET_OP_SEL(v)   SCALER_SET(v, 27, 24)
+#define SCALER_DST_BLEND_ALPHA_GET_ALPHA(r)    SCALER_GET(r, 7, 0)
+#define SCALER_DST_BLEND_ALPHA_SET_ALPHA(v)    SCALER_SET(v, 7, 0)
+
+/* SCALER_FILL_COLOR */
+#define SCALER_FILL_COLOR_GET_ALPHA(r)         SCALER_GET(r, 31, 24)
+#define SCALER_FILL_COLOR_SET_ALPHA(v)         SCALER_SET(v, 31, 24)
+#define SCALER_FILL_COLOR_GET_FILL_COLOR0(r)   SCALER_GET(r, 23, 16)
+#define SCALER_FILL_COLOR_SET_FILL_COLOR0(v)   SCALER_SET(v, 23, 16)
+#define SCALER_FILL_COLOR_GET_FILL_COLOR1(r)   SCALER_GET(r, 15, 8)
+#define SCALER_FILL_COLOR_SET_FILL_COLOR1(v)   SCALER_SET(v, 15, 8)
+#define SCALER_FILL_COLOR_GET_FILL_COLOR2(r)   SCALER_GET(r, 7, 0)
+#define SCALER_FILL_COLOR_SET_FILL_COLOR2(v)   SCALER_SET(v, 7, 0)
+
+/* SCALER_ADDR_Q_CONFIG */
+#define SCALER_ADDR_Q_CONFIG_RST               (1 << 0)
+
+/* SCALER_SRC_ADDR_Q_STATUS */
+#define SCALER_SRC_ADDR_Q_STATUS_Y_FULL                (1 << 23)
+#define SCALER_SRC_ADDR_Q_STATUS_Y_EMPTY       (1 << 22)
+#define SCALER_SRC_ADDR_Q_STATUS_GET_Y_WR_IDX(r)       SCALER_GET(r, 21, 16)
+#define SCALER_SRC_ADDR_Q_STATUS_CB_FULL       (1 << 15)
+#define SCALER_SRC_ADDR_Q_STATUS_CB_EMPTY      (1 << 14)
+#define SCALER_SRC_ADDR_Q_STATUS_GET_CB_WR_IDX(r)      SCALER_GET(r, 13, 8)
+#define SCALER_SRC_ADDR_Q_STATUS_CR_FULL       (1 << 7)
+#define SCALER_SRC_ADDR_Q_STATUS_CR_EMPTY      (1 << 6)
+#define SCALER_SRC_ADDR_Q_STATUS_GET_CR_WR_IDX(r)      SCALER_GET(r, 5, 0)
+
+/* SCALER_DST_ADDR_Q_STATUS */
+#define SCALER_DST_ADDR_Q_STATUS_Y_FULL                (1 << 23)
+#define SCALER_DST_ADDR_Q_STATUS_Y_EMPTY       (1 << 22)
+#define SCALER_DST_ADDR_Q_STATUS_GET_Y_WR_IDX(r)       SCALER_GET(r, 21, 16)
+#define SCALER_DST_ADDR_Q_STATUS_CB_FULL       (1 << 15)
+#define SCALER_DST_ADDR_Q_STATUS_CB_EMPTY      (1 << 14)
+#define SCALER_DST_ADDR_Q_STATUS_GET_CB_WR_IDX(r)      SCALER_GET(r, 13, 8)
+#define SCALER_DST_ADDR_Q_STATUS_CR_FULL       (1 << 7)
+#define SCALER_DST_ADDR_Q_STATUS_CR_EMPTY      (1 << 6)
+#define SCALER_DST_ADDR_Q_STATUS_GET_CR_WR_IDX(r)      SCALER_GET(r, 5, 0)
+
+/* SCALER_CRC_COLOR00_10 */
+#define SCALER_CRC_COLOR00_10_GET_00(r)                SCALER_GET(r, 31, 16)
+#define SCALER_CRC_COLOR00_10_GET_10(r)                SCALER_GET(r, 15, 0)
+
+/* SCALER_CRC_COLOR20_30 */
+#define SCALER_CRC_COLOR20_30_GET_20(r)                SCALER_GET(r, 31, 16)
+#define SCALER_CRC_COLOR20_30_GET_30(r)                SCALER_GET(r, 15, 0)
+
+/* SCALER_CRC_COLOR01_11 */
+#define SCALER_CRC_COLOR01_11_GET_01(r)                SCALER_GET(r, 31, 16)
+#define SCALER_CRC_COLOR01_11_GET_11(r)                SCALER_GET(r, 15, 0)
+
+/* SCALER_CRC_COLOR21_31 */
+#define SCALER_CRC_COLOR21_31_GET_21(r)                SCALER_GET(r, 31, 16)
+#define SCALER_CRC_COLOR21_31_GET_31(r)                SCALER_GET(r, 15, 0)
+
+#endif /* EXYNOS_REGS_SCALER_H */
index 7d31ad7c0957749c44e7d2ee05bb4d06fefc09a2..ac8c3e0ee0776d4ca1bd79e0467104f88b3e2d36 100644 (file)
@@ -130,172 +130,217 @@ struct drm_exynos_g2d_exec {
        __u64                                   async;
 };
 
-enum drm_exynos_ops_id {
-       EXYNOS_DRM_OPS_SRC,
-       EXYNOS_DRM_OPS_DST,
-       EXYNOS_DRM_OPS_MAX,
+/* Exynos DRM IPP v2 API */
+
+/**
+ * Enumerate available IPP hardware modules.
+ *
+ * @count_ipps: size of ipp_id array / number of ipp modules (set by driver)
+ * @reserved: padding
+ * @ipp_id_ptr: pointer to ipp_id array or NULL
+ */
+struct drm_exynos_ioctl_ipp_get_res {
+       __u32 count_ipps;
+       __u32 reserved;
+       __u64 ipp_id_ptr;
 };
 
-struct drm_exynos_sz {
-       __u32   hsize;
-       __u32   vsize;
+enum drm_exynos_ipp_format_type {
+       DRM_EXYNOS_IPP_FORMAT_SOURCE            = 0x01,
+       DRM_EXYNOS_IPP_FORMAT_DESTINATION       = 0x02,
 };
 
-struct drm_exynos_pos {
-       __u32   x;
-       __u32   y;
-       __u32   w;
-       __u32   h;
+struct drm_exynos_ipp_format {
+       __u32 fourcc;
+       __u32 type;
+       __u64 modifier;
 };
 
-enum drm_exynos_flip {
-       EXYNOS_DRM_FLIP_NONE = (0 << 0),
-       EXYNOS_DRM_FLIP_VERTICAL = (1 << 0),
-       EXYNOS_DRM_FLIP_HORIZONTAL = (1 << 1),
-       EXYNOS_DRM_FLIP_BOTH = EXYNOS_DRM_FLIP_VERTICAL |
-                       EXYNOS_DRM_FLIP_HORIZONTAL,
+enum drm_exynos_ipp_capability {
+       DRM_EXYNOS_IPP_CAP_CROP         = 0x01,
+       DRM_EXYNOS_IPP_CAP_ROTATE       = 0x02,
+       DRM_EXYNOS_IPP_CAP_SCALE        = 0x04,
+       DRM_EXYNOS_IPP_CAP_CONVERT      = 0x08,
 };
 
-enum drm_exynos_degree {
-       EXYNOS_DRM_DEGREE_0,
-       EXYNOS_DRM_DEGREE_90,
-       EXYNOS_DRM_DEGREE_180,
-       EXYNOS_DRM_DEGREE_270,
+/**
+ * Get IPP hardware capabilities and supported image formats.
+ *
+ * @ipp_id: id of IPP module to query
+ * @capabilities: bitmask of drm_exynos_ipp_capability (set by driver)
+ * @reserved: padding
+ * @formats_count: size of formats array (in entries) / number of filled
+ *                formats (set by driver)
+ * @formats_ptr: pointer to formats array or NULL
+ */
+struct drm_exynos_ioctl_ipp_get_caps {
+       __u32 ipp_id;
+       __u32 capabilities;
+       __u32 reserved;
+       __u32 formats_count;
+       __u64 formats_ptr;
 };
 
-enum drm_exynos_planer {
-       EXYNOS_DRM_PLANAR_Y,
-       EXYNOS_DRM_PLANAR_CB,
-       EXYNOS_DRM_PLANAR_CR,
-       EXYNOS_DRM_PLANAR_MAX,
+enum drm_exynos_ipp_limit_type {
+       /* size (horizontal/vertial) limits, in pixels (min, max, alignment) */
+       DRM_EXYNOS_IPP_LIMIT_TYPE_SIZE          = 0x0001,
+       /* scale ratio (horizonta/vertial), 16.16 fixed point (min, max) */
+       DRM_EXYNOS_IPP_LIMIT_TYPE_SCALE         = 0x0002,
+
+       /* image buffer area */
+       DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER        = 0x0001 << 16,
+       /* src/dst rectangle area */
+       DRM_EXYNOS_IPP_LIMIT_SIZE_AREA          = 0x0002 << 16,
+       /* src/dst rectangle area when rotation enabled */
+       DRM_EXYNOS_IPP_LIMIT_SIZE_ROTATED       = 0x0003 << 16,
+
+       DRM_EXYNOS_IPP_LIMIT_TYPE_MASK          = 0x000f,
+       DRM_EXYNOS_IPP_LIMIT_SIZE_MASK          = 0x000f << 16,
+};
+
+struct drm_exynos_ipp_limit_val {
+       __u32 min;
+       __u32 max;
+       __u32 align;
+       __u32 reserved;
 };
 
 /**
- * A structure for ipp supported property list.
+ * IPP module limitation.
  *
- * @version: version of this structure.
- * @ipp_id: id of ipp driver.
- * @count: count of ipp driver.
- * @writeback: flag of writeback supporting.
- * @flip: flag of flip supporting.
- * @degree: flag of degree information.
- * @csc: flag of csc supporting.
- * @crop: flag of crop supporting.
- * @scale: flag of scale supporting.
- * @refresh_min: min hz of refresh.
- * @refresh_max: max hz of refresh.
- * @crop_min: crop min resolution.
- * @crop_max: crop max resolution.
- * @scale_min: scale min resolution.
- * @scale_max: scale max resolution.
- * @rot_max: rotation max resolution.
+ * @type: limit type (see drm_exynos_ipp_limit_type enum)
+ * @reserved: padding
+ * @h: horizontal limits
+ * @v: vertical limits
  */
-struct drm_exynos_ipp_prop_list {
-       __u32   version;
-       __u32   ipp_id;
-       __u32   count;
-       __u32   writeback;
-       __u32   flip;
-       __u32   degree;
-       __u32   csc;
-       __u32   crop;
-       __u32   scale;
-       __u32   refresh_min;
-       __u32   refresh_max;
-       __u32   reserved;
-       struct drm_exynos_sz    crop_min;
-       struct drm_exynos_sz    crop_max;
-       struct drm_exynos_sz    scale_min;
-       struct drm_exynos_sz    scale_max;
-       struct drm_exynos_sz    rot_max;
+struct drm_exynos_ipp_limit {
+       __u32 type;
+       __u32 reserved;
+       struct drm_exynos_ipp_limit_val h;
+       struct drm_exynos_ipp_limit_val v;
 };
 
 /**
- * A structure for ipp config.
+ * Get IPP limits for given image format.
  *
- * @ops_id: property of operation directions.
- * @flip: property of mirror, flip.
- * @degree: property of rotation degree.
- * @fmt: property of image format.
- * @sz: property of image size.
- * @pos: property of image position(src-cropped,dst-scaler).
+ * @ipp_id: id of IPP module to query
+ * @fourcc: image format code (see DRM_FORMAT_* in drm_fourcc.h)
+ * @modifier: image format modifier (see DRM_FORMAT_MOD_* in drm_fourcc.h)
+ * @type: source/destination identifier (drm_exynos_ipp_format_flag enum)
+ * @limits_count: size of limits array (in entries) / number of filled entries
+ *              (set by driver)
+ * @limits_ptr: pointer to limits array or NULL
  */
-struct drm_exynos_ipp_config {
-       enum drm_exynos_ops_id ops_id;
-       enum drm_exynos_flip    flip;
-       enum drm_exynos_degree  degree;
-       __u32   fmt;
-       struct drm_exynos_sz    sz;
-       struct drm_exynos_pos   pos;
+struct drm_exynos_ioctl_ipp_get_limits {
+       __u32 ipp_id;
+       __u32 fourcc;
+       __u64 modifier;
+       __u32 type;
+       __u32 limits_count;
+       __u64 limits_ptr;
 };
 
-enum drm_exynos_ipp_cmd {
-       IPP_CMD_NONE,
-       IPP_CMD_M2M,
-       IPP_CMD_WB,
-       IPP_CMD_OUTPUT,
-       IPP_CMD_MAX,
+enum drm_exynos_ipp_task_id {
+       /* buffer described by struct drm_exynos_ipp_task_buffer */
+       DRM_EXYNOS_IPP_TASK_BUFFER              = 0x0001,
+       /* rectangle described by struct drm_exynos_ipp_task_rect */
+       DRM_EXYNOS_IPP_TASK_RECTANGLE           = 0x0002,
+       /* transformation described by struct drm_exynos_ipp_task_transform */
+       DRM_EXYNOS_IPP_TASK_TRANSFORM           = 0x0003,
+       /* alpha configuration described by struct drm_exynos_ipp_task_alpha */
+       DRM_EXYNOS_IPP_TASK_ALPHA               = 0x0004,
+
+       /* source image data (for buffer and rectangle chunks) */
+       DRM_EXYNOS_IPP_TASK_TYPE_SOURCE         = 0x0001 << 16,
+       /* destination image data (for buffer and rectangle chunks) */
+       DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION    = 0x0002 << 16,
 };
 
 /**
- * A structure for ipp property.
+ * Memory buffer with image data.
  *
- * @config: source, destination config.
- * @cmd: definition of command.
- * @ipp_id: id of ipp driver.
- * @prop_id: id of property.
- * @refresh_rate: refresh rate.
+ * @id: must be DRM_EXYNOS_IPP_TASK_BUFFER
+ * other parameters are same as for AddFB2 generic DRM ioctl
  */
-struct drm_exynos_ipp_property {
-       struct drm_exynos_ipp_config config[EXYNOS_DRM_OPS_MAX];
-       enum drm_exynos_ipp_cmd cmd;
-       __u32   ipp_id;
-       __u32   prop_id;
-       __u32   refresh_rate;
+struct drm_exynos_ipp_task_buffer {
+       __u32   id;
+       __u32   fourcc;
+       __u32   width, height;
+       __u32   gem_id[4];
+       __u32   offset[4];
+       __u32   pitch[4];
+       __u64   modifier;
 };
 
-enum drm_exynos_ipp_buf_type {
-       IPP_BUF_ENQUEUE,
-       IPP_BUF_DEQUEUE,
+/**
+ * Rectangle for processing.
+ *
+ * @id: must be DRM_EXYNOS_IPP_TASK_RECTANGLE
+ * @reserved: padding
+ * @x,@y: left corner in pixels
+ * @w,@h: width/height in pixels
+ */
+struct drm_exynos_ipp_task_rect {
+       __u32   id;
+       __u32   reserved;
+       __u32   x;
+       __u32   y;
+       __u32   w;
+       __u32   h;
 };
 
 /**
- * A structure for ipp buffer operations.
+ * Image tranformation description.
  *
- * @ops_id: operation directions.
- * @buf_type: definition of buffer.
- * @prop_id: id of property.
- * @buf_id: id of buffer.
- * @handle: Y, Cb, Cr each planar handle.
- * @user_data: user data.
+ * @id: must be DRM_EXYNOS_IPP_TASK_TRANSFORM
+ * @rotation: DRM_MODE_ROTATE_* and DRM_MODE_REFLECT_* values
  */
-struct drm_exynos_ipp_queue_buf {
-       enum drm_exynos_ops_id  ops_id;
-       enum drm_exynos_ipp_buf_type    buf_type;
-       __u32   prop_id;
-       __u32   buf_id;
-       __u32   handle[EXYNOS_DRM_PLANAR_MAX];
-       __u32   reserved;
-       __u64   user_data;
+struct drm_exynos_ipp_task_transform {
+       __u32   id;
+       __u32   rotation;
 };
 
-enum drm_exynos_ipp_ctrl {
-       IPP_CTRL_PLAY,
-       IPP_CTRL_STOP,
-       IPP_CTRL_PAUSE,
-       IPP_CTRL_RESUME,
-       IPP_CTRL_MAX,
+/**
+ * Image global alpha configuration for formats without alpha values.
+ *
+ * @id: must be DRM_EXYNOS_IPP_TASK_ALPHA
+ * @value: global alpha value (0-255)
+ */
+struct drm_exynos_ipp_task_alpha {
+       __u32   id;
+       __u32   value;
+};
+
+enum drm_exynos_ipp_flag {
+       /* generate DRM event after processing */
+       DRM_EXYNOS_IPP_FLAG_EVENT       = 0x01,
+       /* dry run, only check task parameters */
+       DRM_EXYNOS_IPP_FLAG_TEST_ONLY   = 0x02,
+       /* non-blocking processing */
+       DRM_EXYNOS_IPP_FLAG_NONBLOCK    = 0x04,
 };
 
+#define DRM_EXYNOS_IPP_FLAGS (DRM_EXYNOS_IPP_FLAG_EVENT |\
+               DRM_EXYNOS_IPP_FLAG_TEST_ONLY | DRM_EXYNOS_IPP_FLAG_NONBLOCK)
+
 /**
- * A structure for ipp start/stop operations.
+ * Perform image processing described by array of drm_exynos_ipp_task_*
+ * structures (parameters array).
  *
- * @prop_id: id of property.
- * @ctrl: definition of control.
+ * @ipp_id: id of IPP module to run the task
+ * @flags: bitmask of drm_exynos_ipp_flag values
+ * @reserved: padding
+ * @params_size: size of parameters array (in bytes)
+ * @params_ptr: pointer to parameters array or NULL
+ * @user_data: (optional) data for drm event
  */
-struct drm_exynos_ipp_cmd_ctrl {
-       __u32   prop_id;
-       enum drm_exynos_ipp_ctrl        ctrl;
+struct drm_exynos_ioctl_ipp_commit {
+       __u32 ipp_id;
+       __u32 flags;
+       __u32 reserved;
+       __u32 params_size;
+       __u64 params_ptr;
+       __u64 user_data;
 };
 
 #define DRM_EXYNOS_GEM_CREATE          0x00
@@ -309,11 +354,12 @@ struct drm_exynos_ipp_cmd_ctrl {
 #define DRM_EXYNOS_G2D_SET_CMDLIST     0x21
 #define DRM_EXYNOS_G2D_EXEC            0x22
 
+/* Reserved 0x30 ~ 0x33 for obsolete Exynos IPP ioctls */
 /* IPP - Image Post Processing */
-#define DRM_EXYNOS_IPP_GET_PROPERTY    0x30
-#define DRM_EXYNOS_IPP_SET_PROPERTY    0x31
-#define DRM_EXYNOS_IPP_QUEUE_BUF       0x32
-#define DRM_EXYNOS_IPP_CMD_CTRL        0x33
+#define DRM_EXYNOS_IPP_GET_RESOURCES   0x40
+#define DRM_EXYNOS_IPP_GET_CAPS                0x41
+#define DRM_EXYNOS_IPP_GET_LIMITS      0x42
+#define DRM_EXYNOS_IPP_COMMIT          0x43
 
 #define DRM_IOCTL_EXYNOS_GEM_CREATE            DRM_IOWR(DRM_COMMAND_BASE + \
                DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create)
@@ -332,18 +378,20 @@ struct drm_exynos_ipp_cmd_ctrl {
 #define DRM_IOCTL_EXYNOS_G2D_EXEC              DRM_IOWR(DRM_COMMAND_BASE + \
                DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec)
 
-#define DRM_IOCTL_EXYNOS_IPP_GET_PROPERTY      DRM_IOWR(DRM_COMMAND_BASE + \
-               DRM_EXYNOS_IPP_GET_PROPERTY, struct drm_exynos_ipp_prop_list)
-#define DRM_IOCTL_EXYNOS_IPP_SET_PROPERTY      DRM_IOWR(DRM_COMMAND_BASE + \
-               DRM_EXYNOS_IPP_SET_PROPERTY, struct drm_exynos_ipp_property)
-#define DRM_IOCTL_EXYNOS_IPP_QUEUE_BUF DRM_IOWR(DRM_COMMAND_BASE + \
-               DRM_EXYNOS_IPP_QUEUE_BUF, struct drm_exynos_ipp_queue_buf)
-#define DRM_IOCTL_EXYNOS_IPP_CMD_CTRL          DRM_IOWR(DRM_COMMAND_BASE + \
-               DRM_EXYNOS_IPP_CMD_CTRL, struct drm_exynos_ipp_cmd_ctrl)
+#define DRM_IOCTL_EXYNOS_IPP_GET_RESOURCES     DRM_IOWR(DRM_COMMAND_BASE + \
+               DRM_EXYNOS_IPP_GET_RESOURCES, \
+               struct drm_exynos_ioctl_ipp_get_res)
+#define DRM_IOCTL_EXYNOS_IPP_GET_CAPS          DRM_IOWR(DRM_COMMAND_BASE + \
+               DRM_EXYNOS_IPP_GET_CAPS, struct drm_exynos_ioctl_ipp_get_caps)
+#define DRM_IOCTL_EXYNOS_IPP_GET_LIMITS                DRM_IOWR(DRM_COMMAND_BASE + \
+               DRM_EXYNOS_IPP_GET_LIMITS, \
+               struct drm_exynos_ioctl_ipp_get_limits)
+#define DRM_IOCTL_EXYNOS_IPP_COMMIT            DRM_IOWR(DRM_COMMAND_BASE + \
+               DRM_EXYNOS_IPP_COMMIT, struct drm_exynos_ioctl_ipp_commit)
 
 /* EXYNOS specific events */
 #define DRM_EXYNOS_G2D_EVENT           0x80000000
-#define DRM_EXYNOS_IPP_EVENT           0x80000001
+#define DRM_EXYNOS_IPP_EVENT           0x80000002
 
 struct drm_exynos_g2d_event {
        struct drm_event        base;
@@ -359,9 +407,9 @@ struct drm_exynos_ipp_event {
        __u64                   user_data;
        __u32                   tv_sec;
        __u32                   tv_usec;
-       __u32                   prop_id;
-       __u32                   reserved;
-       __u32                   buf_id[EXYNOS_DRM_OPS_MAX];
+       __u32                   ipp_id;
+       __u32                   sequence;
+       __u64                   reserved;
 };
 
 #endif /* _UAPI_EXYNOS_DRM_H_ */