diff options
author | Hyungwon Hwang <human.hwang@samsung.com> | 2014-09-04 14:41:12 +0900 |
---|---|---|
committer | Chanho Park <chanho61.park@samsung.com> | 2014-11-18 12:00:43 +0900 |
commit | 8e8a23c37c1aaedd387318aa30dd41859439c6fd (patch) | |
tree | 88d0ec14f14fa90853f61ae1cd5b0b1a9ab77f5e /drivers/gpu/drm | |
parent | 4b1b13c1dd4a30294ceffba6ec83e777cf0602ad (diff) | |
download | linux-3.10-8e8a23c37c1aaedd387318aa30dd41859439c6fd.tar.gz linux-3.10-8e8a23c37c1aaedd387318aa30dd41859439c6fd.tar.bz2 linux-3.10-8e8a23c37c1aaedd387318aa30dd41859439c6fd.zip |
drm/exynos: scaler: add exynos drm sc driver
This patch adds exynos drm sc driver for Exynos 3250.
Change-Id: Ia71c5354390e67494fd4887c8861a624d21e3839
Signed-off-by: Hyungwon Hwang <human.hwang@samsung.com>
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r-- | drivers/gpu/drm/exynos/Kconfig | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_sc.c | 2026 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_sc.h | 33 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/regs-sc.h | 160 |
7 files changed, 2230 insertions, 0 deletions
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index ab5901411b3..100348b0ece 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -90,3 +90,9 @@ config DRM_EXYNOS_GSC depends on DRM_EXYNOS_IPP && (ARCH_EXYNOS3 || ARCH_EXYNOS5) help Choose this option if you want to use Exynos GSC for DRM. + +config DRM_EXYNOS_SC + bool "EXYNOS DRM SC" + depends on DRM_EXYNOS_IPP && (ARCH_EXYNOS3 || ARCH_EXYNOS5) + help + Choose this option if you want to use Exynos SC for DRM. diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 2428df862d2..6944db479c1 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -21,5 +21,6 @@ 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_GSC) += exynos_drm_gsc.o +exynosdrm-$(CONFIG_DRM_EXYNOS_SC) += exynos_drm_sc.o obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 773f8c3fdf5..5c5251b7450 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -464,6 +464,9 @@ static struct platform_driver *exynos_drm_subdrivers[] = { #ifdef CONFIG_DRM_EXYNOS_GSC &gsc_driver, #endif +#ifdef CONFIG_DRM_EXYNOS_SC + &sc_driver, +#endif #ifdef CONFIG_DRM_EXYNOS_IPP &ipp_driver, #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index c787172d3a7..1a790b1aa1c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -401,5 +401,6 @@ extern struct platform_driver g2d_driver; extern struct platform_driver fimc_driver; extern struct platform_driver rotator_driver; extern struct platform_driver gsc_driver; +extern struct platform_driver sc_driver; extern struct platform_driver ipp_driver; #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_sc.c b/drivers/gpu/drm/exynos/exynos_drm_sc.c new file mode 100644 index 00000000000..be732f6884b --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_sc.c @@ -0,0 +1,2026 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Authors: + * Eunchul Kim <chulspro.kim@samsung.com> + * Jinyoung Jeon <jy0.jeon@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. + * + */ +#include "drmP.h" +#include "drm_backlight.h" +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> +#include <plat/map-base.h> + +#include "regs-sc.h" +#include <drm/exynos_drm.h> +#include "exynos_drm_ipp.h" +#include "exynos_drm_sc.h" + +#define DEBUG + +/* + * SC stands for SCaler and + * supports image scaler/rotator and input/output DMA operations. + * input DMA reads image data from the memory. + * output DMA writes image data to memory. + * SC supports image rotation and image effect functions. + * + * M2M operation : supports crop/scale/rotation/csc/blending/ + * dithering/color fill so on. + * Memory ----> SC 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. + * 5. how can we fix different C src set_size. + */ + +#define SC_MAX_DEVS 1 +#define SC_MAX_SRC 8 +#define SC_MAX_DST 32 +#define SC_RESET_TIMEOUT 50 +#define SC_CLK_RATE 166750000 +#define SC_REG_SZ 32 +#define SC_WIDTH_ITU_709 1280 +#define SC_CROP_MAX 16384 +#define SC_CROP_MIN 16 +#define SC_SCALE_MAX 16384 +#define SC_SCALE_MIN 16 +#define SC_RATIO(x, y) ((65536 * x) / y) +#define SC_COEF_RATIO 7 +#define SC_COEF_PHASE 9 +#define SC_COEF_ATTR 16 +#define SC_COEF_H_8T 8 +#define SC_COEF_V_4T 4 +#define SC_COEF_DEPTH 3 +#define SC_UP_MAX SC_RATIO(1, 16) +#define SC_DOWN_MIN SC_RATIO(4, 1) +#define SC_DOWN_SWMIN SC_RATIO(16, 1) + +#define get_sc_context(dev) platform_get_drvdata(to_platform_device(dev)) +#define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\ + struct sc_context, ippdrv); +#define sc_read(offset) readl(ctx->regs + (offset)) +#define sc_write(cfg, offset) writel(cfg, ctx->regs + (offset)) + +/* definition of csc type */ +enum sc_csc_type { + CSC_TYPE_NO, + CSC_TYPE_Y2R, + CSC_TYPE_R2Y, +}; + +enum sc_version { + SC_VER_3250, + SC_VER_5260, + SC_VER_5410, +}; + +/* + * A structure of scaler. + * + * @range: narrow, wide. + * @hratio: the scaler's horizontal ratio. + * @vratio: the scaler's vertical ratio. + */ +struct sc_scaler { + bool range; + unsigned long hratio; + unsigned long vratio; +}; + +/* + * A structure of sc driver data. + * + * @aclk: name of system clock. + * @pclk: name of SFR clock. + * @e_version: specifies scaler version. + */ +struct sc_driverdata { + char *aclk; + char *pclk; + enum sc_version e_version; +}; + +/* + * A structure of sc context. + * + * @ippdrv: prepare initialization using ippdrv. + * @regs_res: register resources. + * @regs: memory mapped io registers. + * @sc_clk: sc a clock. + * @sc_clk: sc p clock. + * @sc: scaler infomations. + * @id: sc id. + * @irq: irq number. + * @cur_buf_id: id of current buffer. + * @nplanar: number of planar. + * @pre_multi: premultiplied format. + * @rotation: supports rotation of src. + */ +struct sc_context { + struct exynos_drm_ippdrv ippdrv; + struct resource *regs_res; + void __iomem *regs; + struct clk *sc_clk; + struct sc_scaler sc; + int id; + int irq; + int cur_buf_id[EXYNOS_DRM_OPS_MAX]; + int nplanar[EXYNOS_DRM_OPS_MAX]; + bool pre_multi; + bool rotation; +}; + +/* Scaling coefficient value */ +static const int sc_coef_8t[SC_COEF_RATIO][SC_COEF_ATTR][SC_COEF_H_8T] = { + { + /* 8:8 or zoom-in */ + {0, 0, 0, 0, 128, 0, 0, 0}, + {0, 1, -2, 7, 127, -6, 2, -1}, + {0, 1, -5, 16, 125, -12, 4, -1}, + {0, 2, -8, 25, 120, -15, 5, -1}, + {-1, 3, -10, 35, 114, -18, 6, -1}, + {-1, 4, -13, 46, 107, -20, 6, -1}, + {-1, 5, -16, 57, 99, -21, 7, -2}, + {-1, 5, -18, 68, 89, -20, 6, -1}, + {-1, 6, -20, 79, 79, -20, 6, -1}, + {-1, 6, -20, 89, 68, -18, 5, -1}, + {-2, 7, -21, 99, 57, -16, 5, -1}, + {-1, 6, -20, 107, 46, -13, 4, -1}, + {-1, 6, -18, 114, 35, -10, 3, -1}, + {-1, 5, -15, 120, 25, -8, 2, 0}, + {-1, 4, -12, 125, 16, -5, 1, 0}, + {-1, 2, -6, 127, 7, -2, 1, 0} + }, { + /* 8:7 Zoom-out */ + {0, 3, -8, 13, 111, 14, -8, 3}, + {-1, 3, -10, 21, 112, 7, -6, 2}, + {-1, 4, -12, 28, 110, 1, -4, 2}, + {-1, 4, -13, 36, 106, -3, -2, 1}, + {-1, 4, -15, 44, 103, -7, -1, 1}, + {-1, 4, -16, 53, 97, -11, 1, 1}, + {-1, 4, -16, 61, 91, -13, 2, 0}, + {-1, 4, -17, 69, 85, -15, 3, 0}, + {0, 3, -16, 77, 77, -16, 3, 0}, + {0, 3, -15, 85, 69, -17, 4, -1}, + {0, 2, -13, 91, 61, -16, 4, -1}, + {1, 1, -11, 97, 53, -16, 4, -1}, + {1, -1, -7, 103, 44, -15, 4, -1}, + {1, -2, -3, 106, 36, -13, 4, -1}, + {2, -4, 1, 110, 28, -12, 4, -1}, + {2, -6, 7, 112, 21, -10, 3, -1} + }, { + /* 8:6 Zoom-out */ + {0, 2, -11, 25, 96, 25, -11, 2}, + {0, 2, -12, 31, 96, 19, -10, 2}, + {0, 2, -12, 37, 94, 14, -9, 2}, + {0, 1, -12, 43, 92, 10, -8, 2}, + {0, 1, -12, 49, 90, 5, -7, 2}, + {1, 0, -12, 55, 86, 1, -5, 2}, + {1, -1, -11, 61, 82, -2, -4, 2}, + {1, -1, -9, 67, 77, -5, -3, 1}, + {1, -2, -7, 72, 72, -7, -2, 1}, + {1, -3, -5, 77, 67, -9, -1, 1}, + {2, -4, -2, 82, 61, -11, -1, 1}, + {2, -5, 1, 86, 55, -12, 0, 1}, + {2, -7, 5, 90, 49, -12, 1, 0}, + {2, -8, 10, 92, 43, -12, 1, 0}, + {2, -9, 14, 94, 37, -12, 2, 0}, + {2, -10, 19, 96, 31, -12, 2, 0} + }, { + /* 8:5 Zoom-out */ + {0, -1, -8, 33, 80, 33, -8, -1}, + {1, -2, -7, 37, 80, 28, -8, -1}, + {1, -2, -7, 41, 79, 24, -8, 0}, + {1, -3, -6, 46, 78, 20, -8, 0}, + {1, -3, -4, 50, 76, 16, -8, 0}, + {1, -4, -3, 54, 74, 13, -7, 0}, + {1, -5, -1, 58, 71, 10, -7, 1}, + {1, -5, 1, 62, 68, 6, -6, 1}, + {1, -6, 4, 65, 65, 4, -6, 1}, + {1, -6, 6, 68, 62, 1, -5, 1}, + {1, -7, 10, 71, 58, -1, -5, 1}, + {0, -7, 13, 74, 54, -3, -4, 1}, + {0, -8, 16, 76, 50, -4, -3, 1}, + {0, -8, 20, 78, 46, -6, -3, 1}, + {0, -8, 24, 79, 41, -7, -2, 1}, + {-1, -8, 28, 80, 37, -7, -2, 1} + }, { + /* 8:4 Zoom-out */ + {0, -3, 0, 35, 64, 35, 0, -3}, + {0, -3, 1, 38, 64, 32, -1, -3}, + {0, -3, 2, 41, 63, 29, -2, -2}, + {0, -4, 4, 43, 63, 27, -3, -2}, + {0, -4, 6, 46, 61, 24, -3, -2}, + {0, -4, 7, 49, 60, 21, -3, -2}, + {-1, -4, 9, 51, 59, 19, -4, -1}, + {-1, -4, 12, 53, 57, 16, -4, -1}, + {-1, -4, 14, 55, 55, 14, -4, -1}, + {-1, -4, 16, 57, 53, 12, -4, -1}, + {-1, -4, 19, 59, 51, 9, -4, -1}, + {-2, -3, 21, 60, 49, 7, -4, 0}, + {-2, -3, 24, 61, 46, 6, -4, 0}, + {-2, -3, 27, 63, 43, 4, -4, 0}, + {-2, -2, 29, 63, 41, 2, -3, 0}, + {-3, -1, 32, 64, 38, 1, -3, 0} + }, { + /* 8:3 Zoom-out */ + {0, -1, 8, 33, 48, 33, 8, -1}, + {-1, -1, 9, 35, 49, 31, 7, -1}, + {-1, -1, 10, 36, 49, 30, 6, -1}, + {-1, -1, 12, 38, 48, 28, 5, -1}, + {-1, 0, 13, 39, 48, 26, 4, -1}, + {-1, 0, 15, 41, 47, 24, 3, -1}, + {-1, 0, 16, 42, 47, 23, 2, -1}, + {-1, 1, 18, 43, 45, 21, 2, -1}, + {-1, 1, 19, 45, 45, 19, 1, -1}, + {-1, 2, 21, 45, 43, 18, 1, -1}, + {-1, 2, 23, 47, 42, 16, 0, -1}, + {-1, 3, 24, 47, 41, 15, 0, -1}, + {-1, 4, 26, 48, 39, 13, 0, -1}, + {-1, 5, 28, 48, 38, 12, -1, -1}, + {-1, 6, 30, 49, 36, 10, -1, -1}, + {-1, 7, 31, 49, 35, 9, -1, -1} + }, { + /* 8:2 Zoom-out */ + {0, 2, 13, 30, 38, 30, 13, 2}, + {0, 3, 14, 30, 38, 29, 12, 2}, + {0, 3, 15, 31, 38, 28, 11, 2}, + {0, 4, 16, 32, 38, 26, 10, 2}, + {0, 4, 17, 33, 37, 26, 10, 1}, + {0, 5, 18, 34, 37, 24, 9, 1}, + {0, 5, 19, 34, 37, 24, 8, 1}, + {1, 6, 20, 35, 36, 22, 7, 1}, + {1, 6, 21, 36, 36, 21, 6, 1}, + {1, 7, 22, 36, 35, 20, 6, 1}, + {1, 8, 24, 37, 34, 19, 5, 0}, + {1, 9, 24, 37, 34, 18, 5, 0}, + {1, 10, 26, 37, 33, 17, 4, 0}, + {2, 10, 26, 38, 32, 16, 4, 0}, + {2, 11, 28, 38, 31, 15, 3, 0}, + {2, 12, 29, 38, 30, 14, 3, 0} + } +}; + +static const int sc_coef_4t[SC_COEF_RATIO][SC_COEF_ATTR][SC_COEF_V_4T] = { + { + /* 8:8 or zoom-in */ + {0, 0, 128, 0}, + {0, 5, 127, -4}, + {-1, 11, 124, -6}, + {-1, 19, 118, -8}, + {-2, 27, 111, -8}, + {-3, 37, 102, -8}, + {-4, 48, 92, -8}, + {-5, 59, 81, -7}, + {-6, 70, 70, -6}, + {-7, 81, 59, -5}, + {-8, 92, 48, -4}, + {-8, 102, 37, -3}, + {-8, 111, 27, -2}, + {-8, 118, 19, -1}, + {-6, 124, 11, -1}, + {-4, 127, 5, 0} + }, { + /* 8:7 Zoom-out */ + {0, 8, 112, 8}, + {-1, 14, 111, 4}, + {-2, 20, 109, 1}, + {-2, 27, 105, -2}, + {-3, 34, 100, -3}, + {-3, 43, 93, -5}, + {-4, 51, 86, -5}, + {-4, 60, 77, -5}, + {-5, 69, 69, -5}, + {-5, 77, 60, -4}, + {-5, 86, 51, -4}, + {-5, 93, 43, -3}, + {-3, 100, 34, -3}, + {-2, 105, 27, -2}, + {1, 109, 20, -2}, + {4, 111, 14, -1} + }, { + /* 8:6 Zoom-out */ + {0, 16, 96, 16}, + {-2, 21, 97, 12}, + {-2, 26, 96, 8}, + {-2, 32, 93, 5}, + {-2, 39, 89, 2}, + {-2, 46, 84, 0}, + {-3, 53, 79, -1}, + {-2, 59, 73, -2}, + {-2, 66, 66, -2}, + {-2, 73, 59, -2}, + {-1, 79, 53, -3}, + {0, 84, 46, -2}, + {2, 89, 39, -2}, + {5, 93, 32, -2}, + {8, 96, 26, -2}, + {12, 97, 21, -2} + }, { + /* 8:5 Zoom-out */ + {0, 22, 84, 22}, + {-1, 26, 85, 18}, + {-1, 31, 84, 14}, + {-1, 36, 82, 11}, + {-1, 42, 79, 8}, + {-1, 47, 76, 6}, + {0, 52, 72, 4}, + {0, 58, 68, 2}, + {1, 63, 63, 1}, + {2, 68, 58, 0}, + {4, 72, 52, 0}, + {6, 76, 47, -1}, + {8, 79, 42, -1}, + {11, 82, 36, -1}, + {14, 84, 31, -1}, + {18, 85, 26, -1} + }, { + /* 8:4 Zoom-out */ + {0, 26, 76, 26}, + {0, 30, 76, 22}, + {0, 34, 75, 19}, + {1, 38, 73, 16}, + {1, 43, 71, 13}, + {2, 47, 69, 10}, + {3, 51, 66, 8}, + {4, 55, 63, 6}, + {5, 59, 59, 5}, + {6, 63, 55, 4}, + {8, 66, 51, 3}, + {10, 69, 47, 2}, + {13, 71, 43, 1}, + {16, 73, 38, 1}, + {19, 75, 34, 0}, + {22, 76, 30, 0} + }, { + /* 8:3 Zoom-out */ + {0, 29, 70, 29}, + {2, 32, 68, 26}, + {2, 36, 67, 23}, + {3, 39, 66, 20}, + {3, 43, 65, 17}, + {4, 46, 63, 15}, + {5, 50, 61, 12}, + {7, 53, 58, 10}, + {8, 56, 56, 8}, + {10, 58, 53, 7}, + {12, 61, 50, 5}, + {15, 63, 46, 4}, + {17, 65, 43, 3}, + {20, 66, 39, 3}, + {23, 67, 36, 2}, + {26, 68, 32, 2} + }, { + /* 8:2 Zoom-out */ + {0, 32, 64, 32}, + {3, 34, 63, 28}, + {4, 37, 62, 25}, + {4, 40, 62, 22}, + {5, 43, 61, 19}, + {6, 46, 59, 17}, + {7, 48, 58, 15}, + {9, 51, 55, 13}, + {11, 53, 53, 11}, + {13, 55, 51, 9}, + {15, 58, 48, 7}, + {17, 59, 46, 6}, + {19, 61, 43, 5}, + {22, 62, 40, 4}, + {25, 62, 37, 4}, + {28, 63, 34, 3} + }, +}; + +/* + * A structure of csc table. + * + * @narrow_601: narrow 601. + * @wide_601: wide 601. + * @narrow_709: narrow 709. + * @wide_709: wide 709. + */ +struct sc_csc_tab { + int narrow_601[9]; + int wide_601[9]; + int narrow_709[9]; + int wide_709[9]; +}; + +/* CSC(Color Space Conversion) coefficient value */ +static struct sc_csc_tab sc_no_csc = { + { 0x200, 0x000, 0x000, 0x000, 0x200, 0x000, 0x000, 0x000, 0x200 }, +}; + +static struct sc_csc_tab sc_y2r = { + /* (0,1) 601 Narrow */ + { 0x254, 0x000, 0x331, 0x254, 0xF38, 0xE60, 0x254, 0x409, 0x000 }, + /* (0,1) 601 Wide */ + { 0x200, 0x000, 0x2BE, 0x200, 0xF54, 0xE9B, 0x200, 0x377, 0x000 }, + /* (0,1) 709 Narrow */ + { 0x254, 0x000, 0x331, 0x254, 0xF38, 0xE60, 0x254, 0x409, 0x000 }, + /* (0,1) 709 Wide */ + { 0x200, 0x000, 0x314, 0x200, 0xFA2, 0xF15, 0x200, 0x3A2, 0x000 }, +}; + +static struct sc_csc_tab sc_r2y = { + /* (1,0) 601 Narrow */ + { 0x084, 0x102, 0x032, 0xFB4, 0xF6B, 0x0E1, 0x0E1, 0xF44, 0xFDC }, + /* (1,0) 601 Wide */ + { 0x099, 0x12D, 0x03A, 0xFA8, 0xF52, 0x106, 0x106, 0xF25, 0xFD6 }, + /* (1,0) 709 Narrow */ + { 0x05E, 0x13A, 0x020, 0xFCC, 0xF53, 0x0E1, 0x0E1, 0xF34, 0xFEC }, + /* (1,0) 709 Wide */ + { 0x06D, 0x16E, 0x025, 0xFC4, 0xF36, 0x106, 0x106, 0xF12, 0xFE8 }, +}; + +static struct sc_csc_tab *sc_csc_list[] = { + [CSC_TYPE_NO] = &sc_no_csc, + [CSC_TYPE_Y2R] = &sc_y2r, + [CSC_TYPE_R2Y] = &sc_r2y, +}; + +#define SC_BLENDING_INV_BIT_OFFSET 0x10 + +/* define of blending compare */ +enum sc_blending_comp { + ONE = 0, + SRC_A, + SRC_C, + DST_A, + INV_SA = 0x11, + INV_SC, + INV_DA, + ZERO = 0xff, +}; + +/* + * A structure of blending value. + * + * @src_color: source color. + * @src_alpha: source alpha. + * @dst_color: destination color. + * @dst_alpha: destination alpha. + */ +struct sc_blending_val { + u32 src_color; + u32 src_alpha; + u32 dst_color; + u32 dst_alpha; +}; + +static struct sc_blending_val sc_blending_tbl[] = { + /* Sc, Sa, Dc, Da */ + {ZERO, ZERO, ZERO, ZERO}, /* CLEAR */ + {ONE, ONE, ZERO, ZERO}, /* SRC */ + {ZERO, ZERO, ONE, ONE}, /* DST */ + {ONE, ONE, INV_SA, INV_SA}, /* SRC_OVER */ + {INV_DA, ONE, ONE, INV_SA}, /* DST_OVER */ + {DST_A, DST_A, ZERO, ZERO}, /* SRC_IN */ + {ZERO, ZERO, SRC_A, SRC_A}, /* DST_IN */ + {INV_DA, INV_DA, ZERO, ZERO}, /* SRC_OUT */ + {ZERO, ZERO, INV_SA, INV_SA}, /* DST_OUT */ + {DST_A, ZERO, INV_SA, ONE}, /* SRC_ATOP */ + {INV_DA, ONE, SRC_A, ZERO}, /* DST_ATOP */ + {INV_DA, ONE, INV_SA, ONE}, /* XOR: need to WA */ + {INV_DA, ONE, INV_SA, INV_SA}, /* DARKEN */ + {INV_DA, ONE, INV_SA, INV_SA}, /* LIGHTEN */ + {INV_DA, ONE, INV_SA, INV_SA}, /* MULTIPLY */ + {ONE, ONE, INV_SC, INV_SA}, /* SCREEN */ + {ONE, ONE, ONE, ONE}, /* ADD */ +}; + +static int sc_sw_reset(struct sc_context *ctx) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s\n", __func__); + + /* s/w reset */ + cfg = sc_read(SCALER_CFG); + cfg |= SCALER_CFG_SOFT_RST; + cfg &= ~((1 << 10) | (1 << 9)); + sc_write(cfg, SCALER_CFG); + + return 0; +} + +static void sc_handle_irq(struct sc_context *ctx, bool enable) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); + + cfg = sc_read(SCALER_INT_EN); + + if (enable) + cfg |= SCALER_INT_EN_FRAME_END; + else + cfg &= ~SCALER_INT_EN_FRAME_END; + + sc_write(cfg, SCALER_INT_EN); +} + +static bool sc_check_rgb(u32 fmt) +{ + bool is_rgb = false; + + switch (fmt) { + case DRM_FORMAT_RGB565: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + is_rgb = true; + break; + default: + break; + } + + return is_rgb; +} + +static int sc_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("%s:base[%d][0x%x]s[%d][%llu]\n", __func__, + i, *base[i], i, 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_NV12MT: + ofs[0] = ALIGN(ALIGN(sz->hsize, 128) * + ALIGN(sz->vsize, 32), SZ_8K); + ofs[1] = ALIGN(ALIGN(sz->hsize, 128) * + ALIGN(sz->vsize >> 1, 32), SZ_8K); + 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("base[%d][0x%x]s[%d][%llu]ofs[%d][%llu]\n", + i, *base[i], i, size[i], i, ofs[i]); + } + + return -EINVAL; +} + +static int sc_src_set_fmt_nplanar(struct sc_context *ctx, u32 fmt) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + + DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + + switch (fmt) { + case DRM_FORMAT_RGB565: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + ctx->nplanar[EXYNOS_DRM_OPS_SRC] = 1; + break; + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV61: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV12MT: + ctx->nplanar[EXYNOS_DRM_OPS_SRC] = 2; + break; + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + ctx->nplanar[EXYNOS_DRM_OPS_SRC] = 3; + break; + default: + dev_err(ippdrv->dev, "inavlid number of planar 0x%x.\n", fmt); + return -EINVAL; + } + + return 0; +} + +static int sc_src_set_fmt(struct device *dev, u32 fmt) +{ + struct sc_context *ctx = get_sc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + + cfg = sc_read(SCALER_SRC_CFG); + cfg &= ~(SCALER_CFG_TILE_EN|SCALER_CFG_FMT_MASK); + + switch (fmt) { + case DRM_FORMAT_RGB565: + cfg |= SCALER_CFG_FMT_RGB565; + break; + case DRM_FORMAT_ARGB1555: + cfg |= SCALER_CFG_FMT_ARGB1555; + break; + case DRM_FORMAT_ARGB4444: + cfg |= SCALER_CFG_FMT_ARGB4444; + break; + case DRM_FORMAT_XRGB8888: + cfg |= (SCALER_CFG_FMT_ARGB8888 | SCALER_CFG_FMT_P_ARGB8888); + break; + case DRM_FORMAT_ARGB8888: + cfg |= SCALER_CFG_FMT_ARGB8888; + break; + case DRM_FORMAT_YUYV: + cfg |= SCALER_CFG_FMT_YUYV; + break; + case DRM_FORMAT_YVYU: + cfg |= SCALER_CFG_FMT_YVYU; + break; + case DRM_FORMAT_UYVY: + cfg |= SCALER_CFG_FMT_UYVY; + break; + case DRM_FORMAT_NV21: + cfg |= SCALER_CFG_FMT_YCRCB420_2P; + break; + case DRM_FORMAT_NV61: + cfg |= SCALER_CFG_FMT_YCRCB422_2P; + break; + case DRM_FORMAT_NV12: + cfg |= SCALER_CFG_FMT_YCBCR420_2P; + break; + case DRM_FORMAT_NV16: + cfg |= SCALER_CFG_FMT_YCBCR422_2P; + break; + case DRM_FORMAT_NV12MT: + cfg |= (SCALER_CFG_FMT_YCBCR420_2P | SCALER_CFG_TILE_EN); + break; + case DRM_FORMAT_YUV422: + cfg |= SCALER_CFG_FMT_YCBCR422_3P; + break; + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + cfg |= SCALER_CFG_FMT_YCBCR420_3P; + break; + default: + dev_err(ippdrv->dev, "inavlid target yuv order 0x%x.\n", fmt); + return -EINVAL; + } + + sc_write(cfg, SCALER_SRC_CFG); + + return sc_src_set_fmt_nplanar(ctx, fmt); +} + +static int sc_src_set_transf(struct device *dev, + enum drm_exynos_degree degree, + enum drm_exynos_flip flip, bool *swap) +{ + struct sc_context *ctx = get_sc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, + degree, flip); + + cfg = sc_read(SCALER_ROT_CFG); + cfg &= ~(SCALER_FLIP_MASK | SCALER_ROT_MASK); + + switch (degree) { + case EXYNOS_DRM_DEGREE_0: + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg |= SCALER_FLIP_X_EN; + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg |= SCALER_FLIP_Y_EN; + break; + case EXYNOS_DRM_DEGREE_90: + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg |= SCALER_FLIP_X_EN | SCALER_ROT_270; + else if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg |= SCALER_FLIP_Y_EN | SCALER_ROT_270; + else + cfg |= SCALER_ROT_270; + break; + case EXYNOS_DRM_DEGREE_180: + cfg |= SCALER_ROT_180; + break; + case EXYNOS_DRM_DEGREE_270: + cfg |= SCALER_ROT_90; + break; + default: + dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); + return -EINVAL; + } + + sc_write(cfg, SCALER_ROT_CFG); + + ctx->rotation = cfg & (SCALER_ROT_90 | SCALER_ROT_270) ? 1 : 0; + *swap = ctx->rotation; + + return 0; +} + +static int sc_src_set_size(struct device *dev, int swap, + struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) +{ + struct sc_context *ctx = get_sc_context(dev); + struct drm_exynos_pos img_pos = *pos; + u32 cfg; + + if (swap) { + img_pos.w = pos->h; + img_pos.h = pos->w; + } + + DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]\n", + __func__, pos->x, pos->y, pos->w, pos->h); + + /* pixel offset */ + cfg = (SCALER_SRC_YX(pos->x) | + SCALER_SRC_YY(pos->y)); + sc_write(cfg, SCALER_SRC_Y_POS); + + /* cropped size */ + cfg = (SCALER_SRC_W(pos->w) | + SCALER_SRC_H(pos->h)); + sc_write(cfg, SCALER_SRC_WH); + + DRM_DEBUG_KMS("%s:swap[%d]hsize[%d]vsize[%d]\n", + __func__, swap, sz->hsize, sz->vsize); + + /* span size */ + cfg = sc_read(SCALER_SRC_SPAN); + cfg &= ~(SCALER_SRC_CSPAN_MASK | + SCALER_SRC_YSPAN_MASK); + + cfg |= sz->hsize; + + if (ctx->nplanar[EXYNOS_DRM_OPS_SRC] == 2) + cfg |= sz->hsize << 16; + + if (ctx->nplanar[EXYNOS_DRM_OPS_SRC] == 3) + cfg |= (sz->hsize >> 1) << 16; + + sc_write(cfg, SCALER_SRC_SPAN); + + return 0; +} + +static int sc_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 sc_context *ctx = get_sc_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; + if (!property) { + DRM_ERROR("failed to get property.\n"); + return -EINVAL; + } + + DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, + property->prop_id, buf_id, buf_type); + + /* Set current buf_id */ + ctx->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id; + + if (buf_id > SC_MAX_SRC) { + dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id); + return -ENOMEM; + } + + /* address register set */ + switch (buf_type) { + case IPP_BUF_ENQUEUE: + config = &property->config[EXYNOS_DRM_OPS_SRC]; + ret = sc_set_planar_addr(buf_info, config->fmt, &config->sz); + if (ret) { + dev_err(dev, "failed to set plane src addr.\n"); + return ret; + } + + sc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], + SCALER_SRC_Y_BASE); + sc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], + SCALER_SRC_CB_BASE); + sc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], + SCALER_SRC_CR_BASE); + + break; + case IPP_BUF_DEQUEUE: + default: + /* bypass */ + break; + } + + return 0; +} + +static struct exynos_drm_ipp_ops sc_src_ops = { + .set_fmt = sc_src_set_fmt, + .set_transf = sc_src_set_transf, + .set_size = sc_src_set_size, + .set_addr = sc_src_set_addr, +}; + +static int sc_dst_set_fmt_nplanar(struct sc_context *ctx, u32 fmt) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + + switch (fmt) { + case DRM_FORMAT_RGB565: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + cfg = sc_read(SCALER_CFG); + cfg &= ~SCALER_CFG_CSC_Y_OFFSET_DST; + sc_write(cfg, SCALER_CFG); + ctx->nplanar[EXYNOS_DRM_OPS_DST] = 1; + break; + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + ctx->nplanar[EXYNOS_DRM_OPS_DST] = 1; + break; + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV61: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV12MT: + ctx->nplanar[EXYNOS_DRM_OPS_DST] = 2; + break; + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + ctx->nplanar[EXYNOS_DRM_OPS_DST] = 3; + break; + default: + dev_err(ippdrv->dev, "inavlid target yuv order 0x%x.\n", fmt); + return -EINVAL; + } + + return 0; +} + +static int sc_dst_set_fmt(struct device *dev, u32 fmt) +{ + struct sc_context *ctx = get_sc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + + cfg = sc_read(SCALER_DST_CFG); + cfg &= ~(SCALER_CFG_SWAP_MASK|SCALER_CFG_FMT_MASK); + + switch (fmt) { + case DRM_FORMAT_RGB565: + cfg |= SCALER_CFG_FMT_RGB565; + break; + case DRM_FORMAT_ARGB1555: + cfg |= SCALER_CFG_FMT_ARGB1555; + break; + case DRM_FORMAT_ARGB4444: + cfg |= SCALER_CFG_FMT_ARGB4444; + break; + case DRM_FORMAT_XRGB8888: + cfg |= (SCALER_CFG_FMT_ARGB8888 | SCALER_CFG_FMT_P_ARGB8888); + ctx->pre_multi = true; + break; + case DRM_FORMAT_ARGB8888: + cfg |= SCALER_CFG_FMT_ARGB8888; + break; + case DRM_FORMAT_YUYV: + cfg |= SCALER_CFG_FMT_YUYV; + break; + case DRM_FORMAT_YVYU: + cfg |= SCALER_CFG_FMT_YVYU; + break; + case DRM_FORMAT_UYVY: + cfg |= SCALER_CFG_FMT_UYVY; + break; + case DRM_FORMAT_NV21: + cfg |= SCALER_CFG_FMT_YCRCB420_2P; + break; + case DRM_FORMAT_NV61: + cfg |= SCALER_CFG_FMT_YCRCB422_2P; + break; + case DRM_FORMAT_NV12: + cfg |= SCALER_CFG_FMT_YCBCR420_2P; + break; + case DRM_FORMAT_NV16: + cfg |= SCALER_CFG_FMT_YCBCR422_2P; + break; + case DRM_FORMAT_NV12MT: + cfg |= (SCALER_CFG_FMT_YCBCR420_2P | SCALER_CFG_TILE_EN); + break; + case DRM_FORMAT_YUV422: + cfg |= SCALER_CFG_FMT_YCBCR422_3P; + break; + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + cfg |= SCALER_CFG_FMT_YCBCR420_3P; + break; + default: + dev_err(ippdrv->dev, "inavlid target yuv order 0x%x.\n", fmt); + return -EINVAL; + } + + sc_write(cfg, SCALER_DST_CFG); + + return sc_dst_set_fmt_nplanar(ctx, fmt); +} + +static int sc_dst_set_transf(struct device *dev, + enum drm_exynos_degree degree, + enum drm_exynos_flip flip, bool *swap) +{ + struct sc_context *ctx = get_sc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + + DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, degree, flip); + + cfg = sc_read(SCALER_ROT_CFG); + cfg &= ~(SCALER_FLIP_MASK | SCALER_ROT_MASK); + + switch (degree) { + case EXYNOS_DRM_DEGREE_0: + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg |= SCALER_FLIP_X_EN; + if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg |= SCALER_FLIP_Y_EN; + break; + case EXYNOS_DRM_DEGREE_90: + if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) + cfg |= SCALER_FLIP_X_EN | SCALER_ROT_270; + else if (flip & EXYNOS_DRM_FLIP_VERTICAL) + cfg |= SCALER_FLIP_Y_EN | SCALER_ROT_270; + else + cfg |= SCALER_ROT_270; + break; + case EXYNOS_DRM_DEGREE_180: + cfg |= SCALER_ROT_180; + break; + case EXYNOS_DRM_DEGREE_270: + cfg |= SCALER_ROT_90; + break; + default: + dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); + return -EINVAL; + } + + sc_write(cfg, SCALER_ROT_CFG); + + ctx->rotation = cfg & + (SCALER_ROT_90 | SCALER_ROT_270) ? 1 : 0; + + *swap = ctx->rotation; + + return 0; +} + +static int sc_set_csc_coef(struct sc_context *ctx, struct sc_scaler *sc, + u32 *fmt, int width) +{ + enum sc_csc_type csc_type = CSC_TYPE_NO; + int *csc_eq_val, i, j; + u32 cfg; + bool itu_709; + + DRM_DEBUG_KMS("%s:sfmt[0x%x]dfmt[0x%x]range[%d]width[%d]\n", __func__, + fmt[EXYNOS_DRM_OPS_SRC], fmt[EXYNOS_DRM_OPS_DST], + sc->range, width); + + if (fmt[EXYNOS_DRM_OPS_SRC] == fmt[EXYNOS_DRM_OPS_DST]) { + csc_eq_val = sc_csc_list[csc_type]->narrow_601; + } else { + if (width >= SC_WIDTH_ITU_709) + itu_709 = true; + else + itu_709 = false; + + cfg = sc_read(SCALER_CFG); + if (sc_check_rgb(fmt[EXYNOS_DRM_OPS_DST])) { + csc_type = CSC_TYPE_Y2R; + if (sc->range) + cfg &= ~SCALER_CFG_CSC_Y_OFFSET_SRC; + else + cfg |= SCALER_CFG_CSC_Y_OFFSET_SRC; + } else { + csc_type = CSC_TYPE_R2Y; + if (sc->range) + cfg |= SCALER_CFG_CSC_Y_OFFSET_DST; + else + cfg &= ~SCALER_CFG_CSC_Y_OFFSET_DST; + } + sc_write(cfg, SCALER_CFG); + + if (itu_709) { + if (sc->range) + csc_eq_val = sc_csc_list[csc_type]->wide_709; + else + csc_eq_val = sc_csc_list[csc_type]->narrow_709; + } else { + if (sc->range) + csc_eq_val = sc_csc_list[csc_type]->wide_601; + else + csc_eq_val = sc_csc_list[csc_type]->narrow_601; + } + } + + for (i = 0, j = 0; i < SC_COEF_PHASE; i++, j += 4) { + cfg = sc_read(SCALER_CSC_COEF00 + j); + cfg &= ~SCALER_CSC_COEF_MASK; + cfg |= csc_eq_val[i]; + sc_write(cfg, SCALER_CSC_COEF00 + j); + } + + return 0; +} + +static void sc_set_scaler_ratio(struct sc_context *ctx, struct sc_scaler *sc) +{ + u32 cfg; + + DRM_DEBUG_KMS("%s:hratio[%ld]vratio[%ld]\n", + __func__, sc->hratio, sc->vratio); + + cfg = sc_read(SCALER_H_RATIO); + cfg &= ~SCALER_RATIO_MASK; + cfg |= sc->hratio; + sc_write(cfg, SCALER_H_RATIO); + + cfg = sc_read(SCALER_V_RATIO); + cfg &= ~SCALER_RATIO_MASK; + cfg |= sc->vratio; + sc_write(cfg, SCALER_V_RATIO); +} + +static void sc_set_h_coef(struct sc_context *ctx, int coef) +{ + u32 phase, tab, cnt = 0; + u32 cfg, val_h, val_l; + + DRM_DEBUG_KMS("%s:coef[%d]\n", __func__, coef); + + for (phase = 0; phase < SC_COEF_PHASE; phase++) { + for (tab = SC_COEF_H_8T; tab > 0; tab -= 2, cnt++) { + val_h = sc_coef_8t[coef][phase][tab - 1] & 0x1FF; + val_l = sc_coef_8t[coef][phase][tab - 2] & 0x1FF; + cfg = (val_h << 16) | (val_l << 0); + sc_write(cfg, SCALER_YHCOEF + cnt * 0x4); + sc_write(cfg, SCALER_CHCOEF + cnt * 0x4); + } + } +} + +static void sc_set_v_coef(struct sc_context *ctx, int coef) +{ + u32 phase, tab, cnt = 0; + u32 cfg, val_h, val_l; + + DRM_DEBUG_KMS("%s:coef[%d]\n", __func__, coef); + + for (phase = 0; phase < SC_COEF_PHASE; phase++) { + for (tab = SC_COEF_V_4T; tab > 0; tab -= 2, cnt++) { + val_h = sc_coef_4t[coef][phase][tab - 1] & 0x1FF; + val_l = sc_coef_4t[coef][phase][tab - 2] & 0x1FF; + cfg = (val_h << 16) | (val_l << 0); + sc_write(cfg, SCALER_YVCOEF + cnt * 0x4); + sc_write(cfg, SCALER_CVCOEF + cnt * 0x4); + } + } +} + +static int sc_get_scale_filter(u32 ratio) +{ + int filter; + + if (ratio <= 65536) + filter = 0; /* 8:8 or zoom-in */ + else if (ratio <= 74898) + filter = 1; /* 8:7 zoom-out */ + else if (ratio <= 87381) + filter = 2; /* 8:6 zoom-out */ + else if (ratio <= 104857) + filter = 3; /* 8:5 zoom-out */ + else if (ratio <= 131072) + filter = 4; /* 8:4 zoom-out */ + else if (ratio <= 174762) + filter = 5; /* 8:3 zoom-out */ + else + filter = 6; /* 8:2 zoom-out */ + + return filter; +} + +static int sc_set_scaler_coef(struct sc_context *ctx, struct sc_scaler *sc) +{ + int hcoef, vcoef; + + DRM_DEBUG_KMS("%s\n", __func__); + + hcoef = sc_get_scale_filter(sc->hratio); + vcoef = sc_get_scale_filter(sc->vratio); + + sc_set_h_coef(ctx, hcoef); + sc_set_v_coef(ctx, vcoef); + + return 0; +} + +static int sc_set_scaler(struct sc_context *ctx, struct sc_scaler *sc, + struct drm_exynos_pos *src, struct drm_exynos_pos *dst) +{ + DRM_DEBUG_KMS("%s\n", __func__); + + if (ctx->rotation) { + sc->hratio = SC_RATIO(src->h, dst->w); + sc->vratio = SC_RATIO(src->w, dst->h); + } else{ + sc->hratio = SC_RATIO(src->w, dst->w); + sc->vratio = SC_RATIO(src->h, dst->h); + } + + DRM_DEBUG_KMS("%s:hratio[%ld]vratio[%ld]\n", + __func__, sc->hratio, sc->vratio); + + sc_set_scaler_coef(ctx, &ctx->sc); + sc_set_scaler_ratio(ctx, &ctx->sc); + + return 0; +} + +static void sc_set_dithering(struct sc_context *ctx, + enum drm_exynos_ipp_dithering dithering) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg, val; + + DRM_DEBUG_KMS("%s:dithering[%d]\n", __func__, dithering); + + val = dithering; + + cfg = sc_read(SCALER_DITH_CFG); + cfg &= ~(SCALER_DITH_R_MASK | SCALER_DITH_G_MASK | SCALER_DITH_B_MASK); + cfg |= (val << SCALER_DITH_R_SHIFT) | + (val << SCALER_DITH_G_SHIFT) | + (val << SCALER_DITH_B_SHIFT); + sc_write(cfg, SCALER_DITH_CFG); +} + +static void sc_get_blending_value(u32 *cfg, u32 val, bool pre_multi) +{ + u32 tmp; + + *cfg &= ~(SCALER_SEL_INV_MASK | SCALER_SEL_MASK | + SCALER_OP_SEL_INV_MASK | SCALER_OP_SEL_MASK); + + if (val == 0xff) + *cfg |= (1 << SCALER_SEL_INV_SHIFT); + else { + if (pre_multi) + *cfg |= (1 << SCALER_SEL_SHIFT); + else + *cfg |= (2 << SCALER_SEL_SHIFT); + tmp = val & 0xf; + *cfg |= (tmp << SCALER_OP_SEL_SHIFT); + } + + if (val >= SC_BLENDING_INV_BIT_OFFSET) + *cfg |= (1 << SCALER_OP_SEL_INV_SHIFT); +} + +static void sc_set_blending(struct sc_context *ctx, + enum drm_exynos_ipp_blending blending, bool pre_multi) +{ + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + u32 cfg; + int idx = blending - 1; + + DRM_DEBUG_KMS("%s:blending[%d]\n", __func__, blending); + + if (blending == IPP_BLENDING_NO) + return; + + if (blending >= IPP_BLENDING_MAX) { + dev_err(ippdrv->dev, "invalid blending value %d.\n", blending); + return; + } + + cfg = sc_read(SCALER_CFG); + cfg |= SCALER_CFG_BLEND_EN; + sc_write(cfg, SCALER_CFG); + + cfg = sc_read(SCALER_SRC_BLEND_COLOR); + sc_get_blending_value(&cfg, sc_blending_tbl[idx].src_color, pre_multi); + sc_write(cfg, SCALER_SRC_BLEND_COLOR); + + cfg = sc_read(SCALER_SRC_BLEND_ALPHA); + sc_get_blending_value(&cfg, sc_blending_tbl[idx].src_alpha, 1); + sc_write(cfg, SCALER_SRC_BLEND_ALPHA); + + cfg = sc_read(SCALER_DST_BLEND_COLOR); + sc_get_blending_value(&cfg, sc_blending_tbl[idx].dst_color, pre_multi); + sc_write(cfg, SCALER_DST_BLEND_COLOR); + + cfg = sc_read(SCALER_DST_BLEND_ALPHA); + sc_get_blending_value(&cfg, sc_blending_tbl[idx].dst_alpha, 1); + sc_write(cfg, SCALER_DST_BLEND_ALPHA); + + /* + * If dst format is non-premultiplied format + * and blending operation is enabled, + * result image should be divided by alpha value + * because the result is always pre-multiplied. + */ + if (!pre_multi) { + cfg = sc_read(SCALER_CFG); + cfg |= SCALER_CFG_BL_DIV_ALPHA_EN; + sc_write(cfg, SCALER_CFG); + } +} + +static void sc_set_color_fill(struct sc_context *ctx, u32 color) +{ + u32 cfg = sc_read(SCALER_CFG); + + cfg |= SCALER_CFG_FILL_EN; + sc_write(cfg, SCALER_CFG); + + cfg = color; + sc_write(cfg, SCALER_FILL_COLOR); + +} + +static int sc_dst_set_size(struct device *dev, int swap, + struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) +{ + struct sc_context *ctx = get_sc_context(dev); + struct drm_exynos_pos img_pos = *pos; + u32 cfg; + + DRM_DEBUG_KMS("%s:swap[%d]x[%d]y[%d]w[%d]h[%d]\n", + __func__, swap, pos->x, pos->y, pos->w, pos->h); + + if (swap) { + img_pos.w = pos->h; + img_pos.h = pos->w; + } + + /* pixel offset */ + cfg = (SCALER_DST_X(pos->x) | SCALER_DST_Y(pos->y)); + sc_write(cfg, SCALER_DST_POS); + + /* scaled size */ + cfg = (SCALER_DST_W(pos->w) | SCALER_DST_H(pos->h)); + sc_write(cfg, SCALER_DST_WH); + + DRM_DEBUG_KMS("%s:hsize[%d]vsize[%d]\n", + __func__, sz->hsize, sz->vsize); + + /* span size */ + cfg = sc_read(SCALER_DST_SPAN); + cfg &= ~(SCALER_DST_CSPAN_MASK | SCALER_DST_YSPAN_MASK); + + cfg |= sz->hsize; + + if (ctx->nplanar[EXYNOS_DRM_OPS_DST] == 2) + cfg |= sz->hsize << 16; + + if (ctx->nplanar[EXYNOS_DRM_OPS_DST] == 3) + cfg |= (sz->hsize >> 1) << 16; + + sc_write(cfg, SCALER_DST_SPAN); + + return 0; +} + +static int sc_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) +{ + struct sc_context *ctx = get_sc_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("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, + property->prop_id, buf_id, buf_type); + + /* Set current buf_id */ + ctx->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id; + + /* address register set */ + switch (buf_type) { + case IPP_BUF_ENQUEUE: + config = &property->config[EXYNOS_DRM_OPS_DST]; + ret = sc_set_planar_addr(buf_info, config->fmt, &config->sz); + if (ret) { + dev_err(dev, "failed to set plane src addr.\n"); + return ret; + } + + sc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], + SCALER_DST_Y_BASE); + sc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], + SCALER_DST_CB_BASE); + sc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], + SCALER_DST_CR_BASE); + break; + case IPP_BUF_DEQUEUE: + default: + /* bypass */ + break; + } + + return 0; +} + +static struct exynos_drm_ipp_ops sc_dst_ops = { + .set_fmt = sc_dst_set_fmt, + .set_transf = sc_dst_set_transf, + .set_size = sc_dst_set_size, + .set_addr = sc_dst_set_addr, +}; + +static int sc_clk_ctrl(struct sc_context *ctx, bool enable) +{ + int ret = 0; + + DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); + + if (enable) + ret = clk_enable(ctx->sc_clk); + else + clk_disable(ctx->sc_clk); + + return ret; +} + +static irqreturn_t sc_irq_handler(int irq, void *dev_id) +{ + struct sc_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_property *property = &c_node->property; + struct drm_exynos_ipp_event_work *event = c_node->event_work; + u32 cfg; + int *buf_id = ctx->cur_buf_id; + + DRM_DEBUG_KMS("%s:sc id[%d]\n", __func__, ctx->id); + + cfg = sc_read(SCALER_INT_STATUS); + cfg |= SCALER_INT_STATUS_FRAME_END; + sc_write(cfg, SCALER_INT_STATUS); + + if (c_node->state == IPP_STATE_STOP) { + DRM_ERROR("invalid state:prop_id[%d]\n", property->prop_id); + return IRQ_HANDLED; + } + + DRM_DEBUG_KMS("%s:src buf_id[%d]dst buf_id[%d]\n", __func__, + buf_id[EXYNOS_DRM_OPS_SRC], buf_id[EXYNOS_DRM_OPS_DST]); + + event->ippdrv = ippdrv; + event->buf_id[EXYNOS_DRM_OPS_SRC] = buf_id[EXYNOS_DRM_OPS_SRC]; + event->buf_id[EXYNOS_DRM_OPS_DST] = buf_id[EXYNOS_DRM_OPS_DST]; + ippdrv->sched_event(&event->work); + + return IRQ_HANDLED; +} + +static int sc_init_prop_list(struct exynos_drm_ippdrv *ippdrv) +{ + struct drm_exynos_ipp_prop_list *prop_list; + + DRM_DEBUG_KMS("%s\n", __func__); + + prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL); + if (!prop_list) { + DRM_ERROR("failed to alloc prop_list.\n"); + return -ENOMEM; + } + + 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 = SC_CROP_MAX; + prop_list->crop_max.vsize = SC_CROP_MAX; + prop_list->crop_min.hsize = SC_CROP_MIN; + prop_list->crop_min.vsize = SC_CROP_MIN; + prop_list->scale = 1; + prop_list->blending = 1; + prop_list->dithering = 1; + prop_list->colorfill = 1; + prop_list->scale_max.hsize = SC_SCALE_MAX; + prop_list->scale_max.vsize = SC_SCALE_MAX; + prop_list->scale_min.hsize = SC_SCALE_MIN; + prop_list->scale_min.vsize = SC_SCALE_MIN; + + ippdrv->prop_list = prop_list; + + return 0; +} + +static inline bool sc_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("%s:invalid flip\n", __func__); + return false; + } +} + +static inline bool sc_check_fmt_limit(struct drm_exynos_ipp_property *property) +{ + 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; + unsigned int h_ratio, v_ratio, i; + + if (src_config->fmt == DRM_FORMAT_NV12MT || + dst_config->fmt == DRM_FORMAT_NV12MT) { + if (src_config->degree != EXYNOS_DRM_DEGREE_0) { + DRM_INFO("%s:not support rotating with tiled format\n", + __func__); + return false; + } + + if ((src_pos.w != dst_pos.w) || + (src_sz.hsize != dst_sz.hsize) || + (src_pos.h != dst_pos.h) || + (src_sz.vsize != dst_sz.vsize)) { + DRM_INFO("%s:not support scaling with tiled format\n", + __func__); + return false; + } + } + + for_each_ipp_ops(i) { + if ((property->config[i].fmt == EXYNOS_DRM_DEGREE_90) || + (property->config[i].fmt == EXYNOS_DRM_DEGREE_270)) + swap(src_pos.w, src_pos.h); + } + + h_ratio = SC_RATIO(src_pos.w, dst_pos.w); + v_ratio = SC_RATIO(src_pos.h, dst_pos.h); + + if ((h_ratio > SC_DOWN_MIN) || + (h_ratio < SC_UP_MAX)) { + DRM_INFO("%s:width scaling is out of range\n", __func__); + return false; + } + + if ((v_ratio > SC_DOWN_MIN) || + (v_ratio < SC_UP_MAX)) { + DRM_INFO("%s:height scaling is out of range\n", __func__); + return false; + } + + return true; +} + +static int sc_ippdrv_check_property(struct device *dev, + struct drm_exynos_ipp_property *property) +{ + struct sc_context *ctx = get_sc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + struct drm_exynos_ipp_prop_list *cp = ippdrv->prop_list; + struct drm_exynos_ipp_config *config; + struct drm_exynos_pos *pos; + struct drm_exynos_sz *sz; + bool swap; + int i; + + DRM_DEBUG_KMS("%s\n", __func__); + + if (property->cmd != IPP_CMD_M2M) + return -EPERM; + + if (!sc_check_fmt_limit(property)) + return -EPERM; + + for_each_ipp_ops(i) { + config = &property->config[i]; + pos = &config->pos; + sz = &config->sz; + + /* check for flip */ + if (!sc_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; + } + + /* check for crop */ + if ((i == EXYNOS_DRM_OPS_SRC) && (cp->crop)) { + if (swap) { + if ((pos->h < cp->crop_min.hsize) || + (sz->vsize > cp->crop_max.hsize) || + (pos->w < cp->crop_min.vsize) || + (sz->hsize > cp->crop_max.vsize)) { + DRM_ERROR("out of crop size.\n"); + goto err_property; + } + } else { + if ((pos->w < cp->crop_min.hsize) || + (sz->hsize > cp->crop_max.hsize) || + (pos->h < cp->crop_min.vsize) || + (sz->vsize > cp->crop_max.vsize)) { + DRM_ERROR("out of crop size.\n"); + goto err_property; + } + } + } + + /* check for scale */ + if ((i == EXYNOS_DRM_OPS_DST) && (cp->scale)) { + if (swap) { + if ((pos->h < cp->scale_min.hsize) || + (sz->vsize > cp->scale_max.hsize) || + (pos->w < cp->scale_min.vsize) || + (sz->hsize > cp->scale_max.vsize)) { + DRM_ERROR("out of scale size.\n"); + goto err_property; + } + } else { + if ((pos->w < cp->scale_min.hsize) || + (sz->hsize > cp->scale_max.hsize) || + (pos->h < cp->scale_min.vsize) || + (sz->vsize > cp->scale_max.vsize)) { + DRM_ERROR("out of scale size.\n"); + goto err_property; + } + } + } + } + + 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; +} + + +static int sc_ippdrv_reset(struct device *dev) +{ + struct sc_context *ctx = get_sc_context(dev); + struct sc_scaler *sc = &ctx->sc; + int ret; + + DRM_DEBUG_KMS("%s\n", __func__); + + /* reset h/w block */ + ret = sc_sw_reset(ctx); + if (ret < 0) { + dev_err(dev, "failed to reset hardware.\n"); + return ret; + } + + /* scaler setting */ + memset(&ctx->sc, 0x0, sizeof(ctx->sc)); + sc->range = true; + + return 0; +} + +static int sc_check_prepare(struct sc_context *ctx) +{ + DRM_DEBUG_KMS("%s\n", __func__); + + return 0; +} + +static int sc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) +{ + struct sc_context *ctx = get_sc_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]; + u32 fmt[EXYNOS_DRM_OPS_MAX]; + u32 cfg; + int ret, i; + + DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); + + if (!c_node) { + DRM_ERROR("failed to get c_node.\n"); + return -EINVAL; + } + + property = &c_node->property; + + ret = sc_check_prepare(ctx); + if (ret) { + dev_err(dev, "failed to check prepare.\n"); + return ret; + } + + sc_handle_irq(ctx, true); + + for_each_ipp_ops(i) { + config = &property->config[i]; + img_pos[i] = config->pos; + fmt[i] = config->fmt; + } + + switch (cmd) { + case IPP_CMD_M2M: + /* bypass */ + break; + case IPP_CMD_WB: + case IPP_CMD_OUTPUT: + default: + ret = -EINVAL; + dev_err(dev, "invalid operations.\n"); + return ret; + } + + /* set scaler */ + ret = sc_set_scaler(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; + } + + /* set coefficient */ + sc_set_csc_coef(ctx, &ctx->sc, fmt, img_pos[EXYNOS_DRM_OPS_DST].w); + + /* set dithering */ + if (property->dithering) + sc_set_dithering(ctx, property->dithering); + + /* set blending */ + if (property->blending) + sc_set_blending(ctx, property->blending, ctx->pre_multi); + + /* color fill */ + if (property->color_fill) + sc_set_color_fill(ctx, property->color_fill); + + cfg = sc_read(SCALER_CFG); + cfg |= SCALER_CFG_START_CMD; + sc_write(cfg, SCALER_CFG); + + return 0; +} + +static void sc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) +{ + struct sc_context *ctx = get_sc_context(dev); + u32 cfg; + + DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); + + switch (cmd) { + case IPP_CMD_M2M: + /* bypass */ + break; + case IPP_CMD_WB: + case IPP_CMD_OUTPUT: + default: + dev_err(dev, "invalid operations.\n"); + break; + } + + sc_handle_irq(ctx, false); + + cfg = sc_read(SCALER_CFG); + cfg &= ~SCALER_CFG_START_CMD; + sc_write(cfg, SCALER_CFG); +} + +static int sc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sc_context *ctx; + struct resource *res; + struct exynos_drm_ippdrv *ippdrv; + int ret = -EINVAL; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + /* clock control */ + ctx->sc_clk = devm_clk_get(dev, "mscl"); + if (IS_ERR(ctx->sc_clk)) { + dev_err(dev, "failed to get sc clock.\n"); + return PTR_ERR(ctx->sc_clk); + } + + /* resource memory */ + ctx->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctx->regs = devm_request_and_ioremap(dev, ctx->regs_res); + if (!ctx->regs) { + dev_err(dev, "failed to map registers.\n"); + return -EADDRNOTAVAIL; + } + + /* resource irq */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "failed to request irq resource.\n"); + return -EINVAL; + } + + ctx->irq = res->start; + ret = devm_request_threaded_irq(dev, ctx->irq, NULL, sc_irq_handler, + IRQF_ONESHOT, "drm_sc", ctx); + if (ret < 0) { + dev_err(dev, "failed to request irq.\n"); + return ret; + } + + /* context initailization */ + ctx->id = pdev->id; + + /* ToDo: iommu enable */ + ippdrv = &ctx->ippdrv; + ippdrv->dev = dev; + ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &sc_src_ops; + ippdrv->ops[EXYNOS_DRM_OPS_DST] = &sc_dst_ops; + ippdrv->check_property = sc_ippdrv_check_property; + ippdrv->reset = sc_ippdrv_reset; + ippdrv->start = sc_ippdrv_start; + ippdrv->stop = sc_ippdrv_stop; + ret = sc_init_prop_list(ippdrv); + if (ret < 0) { + dev_err(dev, "failed to init property list.\n"); + return ret; + } + + DRM_INFO("%s:id[%d]ippdrv[0x%x]\n", __func__, ctx->id, (int)ippdrv); + + platform_set_drvdata(pdev, ctx); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = exynos_drm_ippdrv_register(ippdrv); + if (ret < 0) { + dev_err(dev, "failed to register drm sc device.\n"); + goto err_ippdrv_register; + } + + dev_info(&pdev->dev, "drm sc registered successfully.\n"); + + return 0; + +err_ippdrv_register: + pm_runtime_disable(dev); + return ret; +} + +static int sc_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sc_context *ctx = get_sc_context(dev); + struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; + + exynos_drm_ippdrv_unregister(ippdrv); + + pm_runtime_set_suspended(dev); + pm_runtime_disable(dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int sc_suspend(struct device *dev) +{ + struct sc_context *ctx = get_sc_context(dev); + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + + if (pm_runtime_suspended(dev)) + return 0; + + return sc_clk_ctrl(ctx, false); +} + +static int sc_resume(struct device *dev) +{ + struct sc_context *ctx = get_sc_context(dev); + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + + if (!pm_runtime_suspended(dev)) + return sc_clk_ctrl(ctx, true); + + return 0; +} +#endif + +#ifdef CONFIG_PM_RUNTIME +static int sc_runtime_suspend(struct device *dev) +{ + struct sc_context *ctx = get_sc_context(dev); + + DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + + return sc_clk_ctrl(ctx, false); +} + +static int sc_runtime_resume(struct device *dev) +{ + struct sc_context *ctx = get_sc_context(dev); + + DRM_DEBUG_KMS("id[%d]\n", ctx->id); + + return sc_clk_ctrl(ctx, true); +} +#endif + +static const struct dev_pm_ops sc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(sc_suspend, sc_resume) + SET_RUNTIME_PM_OPS(sc_runtime_suspend, sc_runtime_resume, NULL) +}; + +static const struct of_device_id exynos_drm_sc_of_match[] = { + { .compatible = "samsung,exynos3250-sc" }, + { }, +}; +MODULE_DEVICE_TABLE(of, exynos_drm_sc_of_match); + +struct platform_driver sc_driver = { + .probe = sc_probe, + .remove = sc_remove, + .driver = { + .name = "exynos-drm-sc", + .owner = THIS_MODULE, + .pm = &sc_pm_ops, + .of_match_table = of_match_ptr(exynos_drm_sc_of_match), + }, +}; + diff --git a/drivers/gpu/drm/exynos/exynos_drm_sc.h b/drivers/gpu/drm/exynos/exynos_drm_sc.h new file mode 100644 index 00000000000..27237efe288 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_sc.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * + * Authors: + * Eunchul Kim <chulspro.kim@samsung.com> + * Jinyoung Jeon <jy0.jeon@samsung.com> + * + * 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 (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _EXYNOS_DRM_SC_H_ +#define _EXYNOS_DRM_SC_H_ + +/* ToDo */ + +#endif /* _EXYNOS_DRM_SC_H_ */ diff --git a/drivers/gpu/drm/exynos/regs-sc.h b/drivers/gpu/drm/exynos/regs-sc.h new file mode 100644 index 00000000000..de873563d4d --- /dev/null +++ b/drivers/gpu/drm/exynos/regs-sc.h @@ -0,0 +1,160 @@ +/* linux/drivers/gpu/drm/exynos/regs-sc.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.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 REGS_SC_H_ +#define REGS_SC_H_ + +/* Status */ +#define SCALER_STATUS 0x00 + +/* Configuration */ +#define SCALER_CFG 0x04 +#define SCALER_CFG_FILL_EN (1 << 24) +#define SCALER_CFG_BL_DIV_ALPHA_EN (1 << 17) +#define SCALER_CFG_BLEND_EN (1 << 16) +#define SCALER_CFG_CSC_Y_OFFSET_SRC (1 << 10) +#define SCALER_CFG_CSC_Y_OFFSET_DST (1 << 9) +#define SCALER_CFG_SOFT_RST (1 << 1) +#define SCALER_CFG_START_CMD (1 << 0) + +/* Interrupt */ +#define SCALER_INT_EN 0x08 +#define SCALER_INT_EN_FRAME_END (1 << 0) + +#define SCALER_INT_STATUS 0x0c +#define SCALER_INT_STATUS_FRAME_END (1 << 0) + +#define SCALER_SRC_CFG 0x10 + +/* Source Image Configuration */ +#define SCALER_CFG_TILE_EN (1 << 10) +#define SCALER_CFG_VHALF_PHASE_EN (1 << 9) +#define SCALER_CFG_BIG_ENDIAN (1 << 8) +#define SCALER_CFG_SWAP_MASK (3 << 5) +#define SCALER_CFG_BYTE_SWAP (1 << 5) +#define SCALER_CFG_HWORD_SWAP (2 << 5) +#define SCALER_CFG_BYTE_HWORD_SWAP (3 << 5) +#define SCALER_CFG_FMT_MASK (0x1f << 0) +#define SCALER_CFG_FMT_YCBCR420_2P (0 << 0) +#define SCALER_CFG_FMT_YUYV (0xa << 0) +#define SCALER_CFG_FMT_UYVY (0xb << 0) +#define SCALER_CFG_FMT_YVYU (9 << 0) +#define SCALER_CFG_FMT_YCBCR422_2P (2 << 0) +#define SCALER_CFG_FMT_YCBCR444_2P (3 << 0) +#define SCALER_CFG_FMT_RGB565 (4 << 0) +#define SCALER_CFG_FMT_ARGB1555 (5 << 0) +#define SCALER_CFG_FMT_ARGB4444 (0xc << 0) +#define SCALER_CFG_FMT_ARGB8888 (6 << 0) +#define SCALER_CFG_FMT_RGBA8888 (0xe << 0) +#define SCALER_CFG_FMT_P_ARGB8888 (7 << 0) +#define SCALER_CFG_FMT_L8A8 (0xd << 0) +#define SCALER_CFG_FMT_L8 (0xf << 0) +#define SCALER_CFG_FMT_YCRCB420_2P (0x10 << 0) +#define SCALER_CFG_FMT_YCRCB422_2P (0x12 << 0) +#define SCALER_CFG_FMT_YCRCB444_2P (0x13 << 0) +#define SCALER_CFG_FMT_YCBCR420_3P (0x14 << 0) +#define SCALER_CFG_FMT_YCBCR422_3P (0x16 << 0) +#define SCALER_CFG_FMT_YCBCR444_3P (0x17 << 0) + +/* Source Y Base Address */ +#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_CSPAN_MASK (0xffff << 16) +#define SCALER_SRC_YSPAN_MASK (0xffff << 0) + +#define SCALER_SRC_Y_POS 0x20 +#define SCALER_SRC_YX(x) ((x) << 18) +#define SCALER_SRC_YY(x) ((x) << 2) + +#define SCALER_SRC_WH 0x24 +#define SCALER_SRC_W(x) ((x) << 16) +#define SCALER_SRC_H(x) ((x) << 0) + +#define SCALER_SRC_C_POS 0x28 + +#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_CSPAN_MASK (0xffff << 16) +#define SCALER_DST_YSPAN_MASK (0xffff << 0) + +#define SCALER_DST_WH 0x40 +#define SCALER_DST_W(x) ((x) << 16) +#define SCALER_DST_H(x) ((x) << 0) + +#define SCALER_DST_POS 0x44 +#define SCALER_DST_X(x) ((x) << 16) +#define SCALER_DST_Y(x) ((x) << 0) + +#define SCALER_H_RATIO 0x50 +#define SCALER_V_RATIO 0x54 +#define SCALER_RATIO_MASK (0x7ffff << 0) + +#define SCALER_ROT_CFG 0x58 +#define SCALER_ROT_MASK (3 << 0) +#define SCALER_FLIP_MASK (3 << 2) +#define SCALER_FLIP_X_EN (1 << 3) +#define SCALER_FLIP_Y_EN (1 << 2) +#define SCALER_ROT_90 (1 << 0) +#define SCALER_ROT_180 (2 << 0) +#define SCALER_ROT_270 (3 << 0) + +#define SCALER_LAT_CON 0x5c + +#define SCALER_YHCOEF 0x60 +#define SCALER_YVCOEF 0xf0 +#define SCALER_CHCOEF 0x140 +#define SCALER_CVCOEF 0x1d0 + +#define SCALER_CSC_COEF00 0x220 +#define SCALER_CSC_COEF10 0x224 +#define SCALER_CSC_COEF20 0x228 +#define SCALER_CSC_COEF01 0x22c +#define SCALER_CSC_COEF11 0x230 +#define SCALER_CSC_COEF21 0x234 +#define SCALER_CSC_COEF02 0x238 +#define SCALER_CSC_COEF12 0x23c +#define SCALER_CSC_COEF22 0x240 +#define SCALER_CSC_COEF_MASK (0xfff << 0) + +#define SCALER_DITH_CFG 0x250 +#define SCALER_DITH_R_SHIFT 6 +#define SCALER_DITH_G_SHIFT 3 +#define SCALER_DITH_B_SHIFT 0 +#define SCALER_DITH_R_MASK (7 << SCALER_DITH_R_SHIFT) +#define SCALER_DITH_G_MASK (7 << SCALER_DITH_G_SHIFT) +#define SCALER_DITH_B_MASK (7 << SCALER_DITH_B_SHIFT) + +#define SCALER_CRC_COLOR01 0x270 +#define SCALER_CRC_COLOR23 0x274 +#define SCALER_CYCLE_COUNT 0x278 + +#define SCALER_SRC_BLEND_COLOR 0x280 +#define SCALER_SRC_BLEND_ALPHA 0x284 +#define SCALER_DST_BLEND_COLOR 0x288 +#define SCALER_DST_BLEND_ALPHA 0x28c +#define SCALER_SEL_INV_MASK (1 << 31) +#define SCALER_SEL_MASK (2 << 29) +#define SCALER_OP_SEL_INV_MASK (1 << 28) +#define SCALER_OP_SEL_MASK (0xf << 24) +#define SCALER_SEL_INV_SHIFT (31) +#define SCALER_SEL_SHIFT (29) +#define SCALER_OP_SEL_INV_SHIFT (28) +#define SCALER_OP_SEL_SHIFT (24) + +#define SCALER_FILL_COLOR 0x290 + +#endif /* REGS_GSC_H_ */ |