116102edbSEunchul Kim /* 216102edbSEunchul Kim * Copyright (C) 2012 Samsung Electronics Co.Ltd 316102edbSEunchul Kim * Authors: 416102edbSEunchul Kim * Eunchul Kim <chulspro.kim@samsung.com> 516102edbSEunchul Kim * Jinyoung Jeon <jy0.jeon@samsung.com> 616102edbSEunchul Kim * Sangmin Lee <lsmin.lee@samsung.com> 716102edbSEunchul Kim * 816102edbSEunchul Kim * This program is free software; you can redistribute it and/or modify it 916102edbSEunchul Kim * under the terms of the GNU General Public License as published by the 1016102edbSEunchul Kim * Free Software Foundation; either version 2 of the License, or (at your 1116102edbSEunchul Kim * option) any later version. 1216102edbSEunchul Kim * 1316102edbSEunchul Kim */ 1416102edbSEunchul Kim #include <linux/kernel.h> 1516102edbSEunchul Kim #include <linux/platform_device.h> 16a3ad6976SSeung-Woo Kim #include <linux/mfd/syscon.h> 175186fc5eSSylwester Nawrocki #include <linux/regmap.h> 1816102edbSEunchul Kim #include <linux/clk.h> 1916102edbSEunchul Kim #include <linux/pm_runtime.h> 203f1c781dSSachin Kamat #include <linux/of.h> 2172d465aaSAndrzej Hajda #include <linux/spinlock.h> 2216102edbSEunchul Kim 2316102edbSEunchul Kim #include <drm/drmP.h> 2416102edbSEunchul Kim #include <drm/exynos_drm.h> 2516102edbSEunchul Kim #include "regs-fimc.h" 26e30655d0SMark Brown #include "exynos_drm_drv.h" 2716102edbSEunchul Kim #include "exynos_drm_ipp.h" 2816102edbSEunchul Kim #include "exynos_drm_fimc.h" 2916102edbSEunchul Kim 3016102edbSEunchul Kim /* 316fe891f6SEunchul Kim * FIMC stands for Fully Interactive Mobile Camera and 3216102edbSEunchul Kim * supports image scaler/rotator and input/output DMA operations. 3316102edbSEunchul Kim * input DMA reads image data from the memory. 3416102edbSEunchul Kim * output DMA writes image data to memory. 3516102edbSEunchul Kim * FIMC supports image rotation and image effect functions. 3616102edbSEunchul Kim * 3716102edbSEunchul Kim * M2M operation : supports crop/scale/rotation/csc so on. 3816102edbSEunchul Kim * Memory ----> FIMC H/W ----> Memory. 3916102edbSEunchul Kim * Writeback operation : supports cloned screen with FIMD. 4016102edbSEunchul Kim * FIMD ----> FIMC H/W ----> Memory. 4116102edbSEunchul Kim * Output operation : supports direct display using local path. 4216102edbSEunchul Kim * Memory ----> FIMC H/W ----> FIMD. 4316102edbSEunchul Kim */ 4416102edbSEunchul Kim 4516102edbSEunchul Kim /* 4616102edbSEunchul Kim * TODO 4716102edbSEunchul Kim * 1. check suspend/resume api if needed. 4816102edbSEunchul Kim * 2. need to check use case platform_device_id. 4916102edbSEunchul Kim * 3. check src/dst size with, height. 5016102edbSEunchul Kim * 4. added check_prepare api for right register. 5116102edbSEunchul Kim * 5. need to add supported list in prop_list. 5216102edbSEunchul Kim * 6. check prescaler/scaler optimization. 5316102edbSEunchul Kim */ 5416102edbSEunchul Kim 5516102edbSEunchul Kim #define FIMC_MAX_DEVS 4 5616102edbSEunchul Kim #define FIMC_MAX_SRC 2 5716102edbSEunchul Kim #define FIMC_MAX_DST 32 5816102edbSEunchul Kim #define FIMC_SHFACTOR 10 5916102edbSEunchul Kim #define FIMC_BUF_STOP 1 6016102edbSEunchul Kim #define FIMC_BUF_START 2 6116102edbSEunchul Kim #define FIMC_WIDTH_ITU_709 1280 6216102edbSEunchul Kim #define FIMC_REFRESH_MAX 60 6316102edbSEunchul Kim #define FIMC_REFRESH_MIN 12 6416102edbSEunchul Kim #define FIMC_CROP_MAX 8192 6516102edbSEunchul Kim #define FIMC_CROP_MIN 32 6616102edbSEunchul Kim #define FIMC_SCALE_MAX 4224 6716102edbSEunchul Kim #define FIMC_SCALE_MIN 32 6816102edbSEunchul Kim 6916102edbSEunchul Kim #define get_fimc_context(dev) platform_get_drvdata(to_platform_device(dev)) 7016102edbSEunchul Kim #define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\ 7116102edbSEunchul Kim struct fimc_context, ippdrv); 7216102edbSEunchul Kim enum fimc_wb { 7316102edbSEunchul Kim FIMC_WB_NONE, 7416102edbSEunchul Kim FIMC_WB_A, 7516102edbSEunchul Kim FIMC_WB_B, 7616102edbSEunchul Kim }; 7716102edbSEunchul Kim 78e5f86839SSylwester Nawrocki enum { 79e5f86839SSylwester Nawrocki FIMC_CLK_LCLK, 80e5f86839SSylwester Nawrocki FIMC_CLK_GATE, 81e5f86839SSylwester Nawrocki FIMC_CLK_WB_A, 82e5f86839SSylwester Nawrocki FIMC_CLK_WB_B, 83e5f86839SSylwester Nawrocki FIMC_CLK_MUX, 84e5f86839SSylwester Nawrocki FIMC_CLK_PARENT, 85e5f86839SSylwester Nawrocki FIMC_CLKS_MAX 86e5f86839SSylwester Nawrocki }; 87e5f86839SSylwester Nawrocki 88e5f86839SSylwester Nawrocki static const char * const fimc_clock_names[] = { 89e5f86839SSylwester Nawrocki [FIMC_CLK_LCLK] = "sclk_fimc", 90e5f86839SSylwester Nawrocki [FIMC_CLK_GATE] = "fimc", 91e5f86839SSylwester Nawrocki [FIMC_CLK_WB_A] = "pxl_async0", 92e5f86839SSylwester Nawrocki [FIMC_CLK_WB_B] = "pxl_async1", 93e5f86839SSylwester Nawrocki [FIMC_CLK_MUX] = "mux", 94e5f86839SSylwester Nawrocki [FIMC_CLK_PARENT] = "parent", 95e5f86839SSylwester Nawrocki }; 96e5f86839SSylwester Nawrocki 97e5f86839SSylwester Nawrocki #define FIMC_DEFAULT_LCLK_FREQUENCY 133000000UL 98e5f86839SSylwester Nawrocki 9916102edbSEunchul Kim /* 10016102edbSEunchul Kim * A structure of scaler. 10116102edbSEunchul Kim * 10216102edbSEunchul Kim * @range: narrow, wide. 10316102edbSEunchul Kim * @bypass: unused scaler path. 10416102edbSEunchul Kim * @up_h: horizontal scale up. 10516102edbSEunchul Kim * @up_v: vertical scale up. 10616102edbSEunchul Kim * @hratio: horizontal ratio. 10716102edbSEunchul Kim * @vratio: vertical ratio. 10816102edbSEunchul Kim */ 10916102edbSEunchul Kim struct fimc_scaler { 11016102edbSEunchul Kim bool range; 11116102edbSEunchul Kim bool bypass; 11216102edbSEunchul Kim bool up_h; 11316102edbSEunchul Kim bool up_v; 11416102edbSEunchul Kim u32 hratio; 11516102edbSEunchul Kim u32 vratio; 11616102edbSEunchul Kim }; 11716102edbSEunchul Kim 11816102edbSEunchul Kim /* 11916102edbSEunchul Kim * A structure of scaler capability. 12016102edbSEunchul Kim * 12116102edbSEunchul Kim * find user manual table 43-1. 12216102edbSEunchul Kim * @in_hori: scaler input horizontal size. 12316102edbSEunchul Kim * @bypass: scaler bypass mode. 12416102edbSEunchul Kim * @dst_h_wo_rot: target horizontal size without output rotation. 12516102edbSEunchul Kim * @dst_h_rot: target horizontal size with output rotation. 12616102edbSEunchul Kim * @rl_w_wo_rot: real width without input rotation. 12716102edbSEunchul Kim * @rl_h_rot: real height without output rotation. 12816102edbSEunchul Kim */ 12916102edbSEunchul Kim struct fimc_capability { 13016102edbSEunchul Kim /* scaler */ 13116102edbSEunchul Kim u32 in_hori; 13216102edbSEunchul Kim u32 bypass; 13316102edbSEunchul Kim /* output rotator */ 13416102edbSEunchul Kim u32 dst_h_wo_rot; 13516102edbSEunchul Kim u32 dst_h_rot; 13616102edbSEunchul Kim /* input rotator */ 13716102edbSEunchul Kim u32 rl_w_wo_rot; 13816102edbSEunchul Kim u32 rl_h_rot; 13916102edbSEunchul Kim }; 14016102edbSEunchul Kim 14116102edbSEunchul Kim /* 14216102edbSEunchul Kim * A structure of fimc context. 14316102edbSEunchul Kim * 14416102edbSEunchul Kim * @ippdrv: prepare initialization using ippdrv. 14516102edbSEunchul Kim * @regs_res: register resources. 14616102edbSEunchul Kim * @regs: memory mapped io registers. 14716102edbSEunchul Kim * @lock: locking of operations. 148e5f86839SSylwester Nawrocki * @clocks: fimc clocks. 149e5f86839SSylwester Nawrocki * @clk_frequency: LCLK clock frequency. 1505186fc5eSSylwester Nawrocki * @sysreg: handle to SYSREG block regmap. 15116102edbSEunchul Kim * @sc: scaler infomations. 15216102edbSEunchul Kim * @pol: porarity of writeback. 15316102edbSEunchul Kim * @id: fimc id. 15416102edbSEunchul Kim * @irq: irq number. 15516102edbSEunchul Kim * @suspended: qos operations. 15616102edbSEunchul Kim */ 15716102edbSEunchul Kim struct fimc_context { 15816102edbSEunchul Kim struct exynos_drm_ippdrv ippdrv; 15916102edbSEunchul Kim struct resource *regs_res; 16016102edbSEunchul Kim void __iomem *regs; 16172d465aaSAndrzej Hajda spinlock_t lock; 162e5f86839SSylwester Nawrocki struct clk *clocks[FIMC_CLKS_MAX]; 163e5f86839SSylwester Nawrocki u32 clk_frequency; 1645186fc5eSSylwester Nawrocki struct regmap *sysreg; 16516102edbSEunchul Kim struct fimc_scaler sc; 16616102edbSEunchul Kim struct exynos_drm_ipp_pol pol; 16716102edbSEunchul Kim int id; 16816102edbSEunchul Kim int irq; 16916102edbSEunchul Kim bool suspended; 17016102edbSEunchul Kim }; 17116102edbSEunchul Kim 172acd8afa8SAndrzej Hajda static u32 fimc_read(struct fimc_context *ctx, u32 reg) 173acd8afa8SAndrzej Hajda { 174acd8afa8SAndrzej Hajda return readl(ctx->regs + reg); 175acd8afa8SAndrzej Hajda } 176acd8afa8SAndrzej Hajda 177acd8afa8SAndrzej Hajda static void fimc_write(struct fimc_context *ctx, u32 val, u32 reg) 178acd8afa8SAndrzej Hajda { 179acd8afa8SAndrzej Hajda writel(val, ctx->regs + reg); 180acd8afa8SAndrzej Hajda } 181acd8afa8SAndrzej Hajda 182acd8afa8SAndrzej Hajda static void fimc_set_bits(struct fimc_context *ctx, u32 reg, u32 bits) 183acd8afa8SAndrzej Hajda { 184acd8afa8SAndrzej Hajda void __iomem *r = ctx->regs + reg; 185acd8afa8SAndrzej Hajda 186acd8afa8SAndrzej Hajda writel(readl(r) | bits, r); 187acd8afa8SAndrzej Hajda } 188acd8afa8SAndrzej Hajda 189acd8afa8SAndrzej Hajda static void fimc_clear_bits(struct fimc_context *ctx, u32 reg, u32 bits) 190acd8afa8SAndrzej Hajda { 191acd8afa8SAndrzej Hajda void __iomem *r = ctx->regs + reg; 192acd8afa8SAndrzej Hajda 193acd8afa8SAndrzej Hajda writel(readl(r) & ~bits, r); 194acd8afa8SAndrzej Hajda } 195acd8afa8SAndrzej Hajda 196b5c0b552SJoongMock Shin static void fimc_sw_reset(struct fimc_context *ctx) 19716102edbSEunchul Kim { 19816102edbSEunchul Kim u32 cfg; 19916102edbSEunchul Kim 200e39d5ce1SJinyoung Jeon /* stop dma operation */ 201acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CISTATUS); 202acd8afa8SAndrzej Hajda if (EXYNOS_CISTATUS_GET_ENVID_STATUS(cfg)) 203acd8afa8SAndrzej Hajda fimc_clear_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID); 20416102edbSEunchul Kim 205acd8afa8SAndrzej Hajda fimc_set_bits(ctx, EXYNOS_CISRCFMT, EXYNOS_CISRCFMT_ITU601_8BIT); 20616102edbSEunchul Kim 207e39d5ce1SJinyoung Jeon /* disable image capture */ 208acd8afa8SAndrzej Hajda fimc_clear_bits(ctx, EXYNOS_CIIMGCPT, 209acd8afa8SAndrzej Hajda EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN); 210e39d5ce1SJinyoung Jeon 21116102edbSEunchul Kim /* s/w reset */ 212acd8afa8SAndrzej Hajda fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_SWRST); 21316102edbSEunchul Kim 21416102edbSEunchul Kim /* s/w reset complete */ 215acd8afa8SAndrzej Hajda fimc_clear_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_SWRST); 21616102edbSEunchul Kim 21716102edbSEunchul Kim /* reset sequence */ 218acd8afa8SAndrzej Hajda fimc_write(ctx, 0x0, EXYNOS_CIFCNTSEQ); 21916102edbSEunchul Kim } 22016102edbSEunchul Kim 2215186fc5eSSylwester Nawrocki static int fimc_set_camblk_fimd0_wb(struct fimc_context *ctx) 22216102edbSEunchul Kim { 2235186fc5eSSylwester Nawrocki return regmap_update_bits(ctx->sysreg, SYSREG_CAMERA_BLK, 2245186fc5eSSylwester Nawrocki SYSREG_FIMD0WB_DEST_MASK, 2255186fc5eSSylwester Nawrocki ctx->id << SYSREG_FIMD0WB_DEST_SHIFT); 22616102edbSEunchul Kim } 22716102edbSEunchul Kim 22816102edbSEunchul Kim static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb) 22916102edbSEunchul Kim { 23016102edbSEunchul Kim u32 cfg; 23116102edbSEunchul Kim 232cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("wb[%d]\n", wb); 23316102edbSEunchul Kim 234acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CIGCTRL); 23516102edbSEunchul Kim cfg &= ~(EXYNOS_CIGCTRL_TESTPATTERN_MASK | 23616102edbSEunchul Kim EXYNOS_CIGCTRL_SELCAM_ITU_MASK | 23716102edbSEunchul Kim EXYNOS_CIGCTRL_SELCAM_MIPI_MASK | 23816102edbSEunchul Kim EXYNOS_CIGCTRL_SELCAM_FIMC_MASK | 23916102edbSEunchul Kim EXYNOS_CIGCTRL_SELWB_CAMIF_MASK | 24016102edbSEunchul Kim EXYNOS_CIGCTRL_SELWRITEBACK_MASK); 24116102edbSEunchul Kim 24216102edbSEunchul Kim switch (wb) { 24316102edbSEunchul Kim case FIMC_WB_A: 24416102edbSEunchul Kim cfg |= (EXYNOS_CIGCTRL_SELWRITEBACK_A | 24516102edbSEunchul Kim EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK); 24616102edbSEunchul Kim break; 24716102edbSEunchul Kim case FIMC_WB_B: 24816102edbSEunchul Kim cfg |= (EXYNOS_CIGCTRL_SELWRITEBACK_B | 24916102edbSEunchul Kim EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK); 25016102edbSEunchul Kim break; 25116102edbSEunchul Kim case FIMC_WB_NONE: 25216102edbSEunchul Kim default: 25316102edbSEunchul Kim cfg |= (EXYNOS_CIGCTRL_SELCAM_ITU_A | 25416102edbSEunchul Kim EXYNOS_CIGCTRL_SELWRITEBACK_A | 25516102edbSEunchul Kim EXYNOS_CIGCTRL_SELCAM_MIPI_A | 25616102edbSEunchul Kim EXYNOS_CIGCTRL_SELCAM_FIMC_ITU); 25716102edbSEunchul Kim break; 25816102edbSEunchul Kim } 25916102edbSEunchul Kim 260acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIGCTRL); 26116102edbSEunchul Kim } 26216102edbSEunchul Kim 26316102edbSEunchul Kim static void fimc_set_polarity(struct fimc_context *ctx, 26416102edbSEunchul Kim struct exynos_drm_ipp_pol *pol) 26516102edbSEunchul Kim { 26616102edbSEunchul Kim u32 cfg; 26716102edbSEunchul Kim 268cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("inv_pclk[%d]inv_vsync[%d]\n", 269cbc4c33dSYoungJun Cho pol->inv_pclk, pol->inv_vsync); 270cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("inv_href[%d]inv_hsync[%d]\n", 271cbc4c33dSYoungJun Cho pol->inv_href, pol->inv_hsync); 27216102edbSEunchul Kim 273acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CIGCTRL); 27416102edbSEunchul Kim cfg &= ~(EXYNOS_CIGCTRL_INVPOLPCLK | EXYNOS_CIGCTRL_INVPOLVSYNC | 27516102edbSEunchul Kim EXYNOS_CIGCTRL_INVPOLHREF | EXYNOS_CIGCTRL_INVPOLHSYNC); 27616102edbSEunchul Kim 27716102edbSEunchul Kim if (pol->inv_pclk) 27816102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_INVPOLPCLK; 27916102edbSEunchul Kim if (pol->inv_vsync) 28016102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_INVPOLVSYNC; 28116102edbSEunchul Kim if (pol->inv_href) 28216102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_INVPOLHREF; 28316102edbSEunchul Kim if (pol->inv_hsync) 28416102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_INVPOLHSYNC; 28516102edbSEunchul Kim 286acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIGCTRL); 28716102edbSEunchul Kim } 28816102edbSEunchul Kim 28916102edbSEunchul Kim static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable) 29016102edbSEunchul Kim { 29116102edbSEunchul Kim u32 cfg; 29216102edbSEunchul Kim 293cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("enable[%d]\n", enable); 29416102edbSEunchul Kim 295acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CIGCTRL); 29616102edbSEunchul Kim if (enable) 29716102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_CAM_JPEG; 29816102edbSEunchul Kim else 29916102edbSEunchul Kim cfg &= ~EXYNOS_CIGCTRL_CAM_JPEG; 30016102edbSEunchul Kim 301acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIGCTRL); 30216102edbSEunchul Kim } 30316102edbSEunchul Kim 3048b4609cdSAndrzej Hajda static void fimc_mask_irq(struct fimc_context *ctx, bool enable) 30516102edbSEunchul Kim { 30616102edbSEunchul Kim u32 cfg; 30716102edbSEunchul Kim 3088b4609cdSAndrzej Hajda DRM_DEBUG_KMS("enable[%d]\n", enable); 30916102edbSEunchul Kim 310acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CIGCTRL); 31116102edbSEunchul Kim if (enable) { 3128b4609cdSAndrzej Hajda cfg &= ~EXYNOS_CIGCTRL_IRQ_OVFEN; 3138b4609cdSAndrzej Hajda cfg |= EXYNOS_CIGCTRL_IRQ_ENABLE | EXYNOS_CIGCTRL_IRQ_LEVEL; 31416102edbSEunchul Kim } else 3158b4609cdSAndrzej Hajda cfg &= ~EXYNOS_CIGCTRL_IRQ_ENABLE; 316acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIGCTRL); 31716102edbSEunchul Kim } 31816102edbSEunchul Kim 31916102edbSEunchul Kim static void fimc_clear_irq(struct fimc_context *ctx) 32016102edbSEunchul Kim { 321acd8afa8SAndrzej Hajda fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_CLR); 32216102edbSEunchul Kim } 32316102edbSEunchul Kim 32416102edbSEunchul Kim static bool fimc_check_ovf(struct fimc_context *ctx) 32516102edbSEunchul Kim { 32616102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 327acd8afa8SAndrzej Hajda u32 status, flag; 32816102edbSEunchul Kim 329acd8afa8SAndrzej Hajda status = fimc_read(ctx, EXYNOS_CISTATUS); 33016102edbSEunchul Kim flag = EXYNOS_CISTATUS_OVFIY | EXYNOS_CISTATUS_OVFICB | 33116102edbSEunchul Kim EXYNOS_CISTATUS_OVFICR; 33216102edbSEunchul Kim 333cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("flag[0x%x]\n", flag); 33416102edbSEunchul Kim 33516102edbSEunchul Kim if (status & flag) { 336acd8afa8SAndrzej Hajda fimc_set_bits(ctx, EXYNOS_CIWDOFST, 337acd8afa8SAndrzej Hajda EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB | 33816102edbSEunchul Kim EXYNOS_CIWDOFST_CLROVFICR); 33916102edbSEunchul Kim 34077d84ff8SMasanari Iida dev_err(ippdrv->dev, "occurred overflow at %d, status 0x%x.\n", 34116102edbSEunchul Kim ctx->id, status); 34216102edbSEunchul Kim return true; 34316102edbSEunchul Kim } 34416102edbSEunchul Kim 34516102edbSEunchul Kim return false; 34616102edbSEunchul Kim } 34716102edbSEunchul Kim 34816102edbSEunchul Kim static bool fimc_check_frame_end(struct fimc_context *ctx) 34916102edbSEunchul Kim { 35016102edbSEunchul Kim u32 cfg; 35116102edbSEunchul Kim 352acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CISTATUS); 35316102edbSEunchul Kim 354cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("cfg[0x%x]\n", cfg); 35516102edbSEunchul Kim 35616102edbSEunchul Kim if (!(cfg & EXYNOS_CISTATUS_FRAMEEND)) 35716102edbSEunchul Kim return false; 35816102edbSEunchul Kim 35916102edbSEunchul Kim cfg &= ~(EXYNOS_CISTATUS_FRAMEEND); 360acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CISTATUS); 36116102edbSEunchul Kim 36216102edbSEunchul Kim return true; 36316102edbSEunchul Kim } 36416102edbSEunchul Kim 36516102edbSEunchul Kim static int fimc_get_buf_id(struct fimc_context *ctx) 36616102edbSEunchul Kim { 36716102edbSEunchul Kim u32 cfg; 36816102edbSEunchul Kim int frame_cnt, buf_id; 36916102edbSEunchul Kim 370acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CISTATUS2); 37116102edbSEunchul Kim frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg); 37216102edbSEunchul Kim 37316102edbSEunchul Kim if (frame_cnt == 0) 37416102edbSEunchul Kim frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(cfg); 37516102edbSEunchul Kim 376cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("present[%d]before[%d]\n", 37716102edbSEunchul Kim EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(cfg), 37816102edbSEunchul Kim EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg)); 37916102edbSEunchul Kim 38016102edbSEunchul Kim if (frame_cnt == 0) { 38116102edbSEunchul Kim DRM_ERROR("failed to get frame count.\n"); 38216102edbSEunchul Kim return -EIO; 38316102edbSEunchul Kim } 38416102edbSEunchul Kim 38516102edbSEunchul Kim buf_id = frame_cnt - 1; 386cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("buf_id[%d]\n", buf_id); 38716102edbSEunchul Kim 38816102edbSEunchul Kim return buf_id; 38916102edbSEunchul Kim } 39016102edbSEunchul Kim 39116102edbSEunchul Kim static void fimc_handle_lastend(struct fimc_context *ctx, bool enable) 39216102edbSEunchul Kim { 39316102edbSEunchul Kim u32 cfg; 39416102edbSEunchul Kim 395cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("enable[%d]\n", enable); 39616102edbSEunchul Kim 397acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CIOCTRL); 39816102edbSEunchul Kim if (enable) 39916102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_LASTENDEN; 40016102edbSEunchul Kim else 40116102edbSEunchul Kim cfg &= ~EXYNOS_CIOCTRL_LASTENDEN; 40216102edbSEunchul Kim 403acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIOCTRL); 40416102edbSEunchul Kim } 40516102edbSEunchul Kim 40616102edbSEunchul Kim 40716102edbSEunchul Kim static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt) 40816102edbSEunchul Kim { 40916102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 41016102edbSEunchul Kim u32 cfg; 41116102edbSEunchul Kim 412cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); 41316102edbSEunchul Kim 41416102edbSEunchul Kim /* RGB */ 415acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CISCCTRL); 41616102edbSEunchul Kim cfg &= ~EXYNOS_CISCCTRL_INRGB_FMT_RGB_MASK; 41716102edbSEunchul Kim 41816102edbSEunchul Kim switch (fmt) { 41916102edbSEunchul Kim case DRM_FORMAT_RGB565: 42016102edbSEunchul Kim cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB565; 421acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CISCCTRL); 42216102edbSEunchul Kim return 0; 42316102edbSEunchul Kim case DRM_FORMAT_RGB888: 42416102edbSEunchul Kim case DRM_FORMAT_XRGB8888: 42516102edbSEunchul Kim cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB888; 426acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CISCCTRL); 42716102edbSEunchul Kim return 0; 42816102edbSEunchul Kim default: 42916102edbSEunchul Kim /* bypass */ 43016102edbSEunchul Kim break; 43116102edbSEunchul Kim } 43216102edbSEunchul Kim 43316102edbSEunchul Kim /* YUV */ 434acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_MSCTRL); 43516102edbSEunchul Kim cfg &= ~(EXYNOS_MSCTRL_ORDER2P_SHIFT_MASK | 43616102edbSEunchul Kim EXYNOS_MSCTRL_C_INT_IN_2PLANE | 43716102edbSEunchul Kim EXYNOS_MSCTRL_ORDER422_YCBYCR); 43816102edbSEunchul Kim 43916102edbSEunchul Kim switch (fmt) { 44016102edbSEunchul Kim case DRM_FORMAT_YUYV: 44116102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_ORDER422_YCBYCR; 44216102edbSEunchul Kim break; 44316102edbSEunchul Kim case DRM_FORMAT_YVYU: 44416102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_ORDER422_YCRYCB; 44516102edbSEunchul Kim break; 44616102edbSEunchul Kim case DRM_FORMAT_UYVY: 44716102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_ORDER422_CBYCRY; 44816102edbSEunchul Kim break; 44916102edbSEunchul Kim case DRM_FORMAT_VYUY: 45016102edbSEunchul Kim case DRM_FORMAT_YUV444: 45116102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_ORDER422_CRYCBY; 45216102edbSEunchul Kim break; 45316102edbSEunchul Kim case DRM_FORMAT_NV21: 45416102edbSEunchul Kim case DRM_FORMAT_NV61: 45516102edbSEunchul Kim cfg |= (EXYNOS_MSCTRL_ORDER2P_LSB_CRCB | 45616102edbSEunchul Kim EXYNOS_MSCTRL_C_INT_IN_2PLANE); 45716102edbSEunchul Kim break; 45816102edbSEunchul Kim case DRM_FORMAT_YUV422: 45916102edbSEunchul Kim case DRM_FORMAT_YUV420: 46016102edbSEunchul Kim case DRM_FORMAT_YVU420: 46116102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_C_INT_IN_3PLANE; 46216102edbSEunchul Kim break; 46316102edbSEunchul Kim case DRM_FORMAT_NV12: 46416102edbSEunchul Kim case DRM_FORMAT_NV16: 46516102edbSEunchul Kim cfg |= (EXYNOS_MSCTRL_ORDER2P_LSB_CBCR | 46616102edbSEunchul Kim EXYNOS_MSCTRL_C_INT_IN_2PLANE); 46716102edbSEunchul Kim break; 46816102edbSEunchul Kim default: 46916102edbSEunchul Kim dev_err(ippdrv->dev, "inavlid source yuv order 0x%x.\n", fmt); 47016102edbSEunchul Kim return -EINVAL; 47116102edbSEunchul Kim } 47216102edbSEunchul Kim 473acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_MSCTRL); 47416102edbSEunchul Kim 47516102edbSEunchul Kim return 0; 47616102edbSEunchul Kim } 47716102edbSEunchul Kim 47816102edbSEunchul Kim static int fimc_src_set_fmt(struct device *dev, u32 fmt) 47916102edbSEunchul Kim { 48016102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 48116102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 48216102edbSEunchul Kim u32 cfg; 48316102edbSEunchul Kim 484cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); 48516102edbSEunchul Kim 486acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_MSCTRL); 48716102edbSEunchul Kim cfg &= ~EXYNOS_MSCTRL_INFORMAT_RGB; 48816102edbSEunchul Kim 48916102edbSEunchul Kim switch (fmt) { 49016102edbSEunchul Kim case DRM_FORMAT_RGB565: 49116102edbSEunchul Kim case DRM_FORMAT_RGB888: 49216102edbSEunchul Kim case DRM_FORMAT_XRGB8888: 49316102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_INFORMAT_RGB; 49416102edbSEunchul Kim break; 49516102edbSEunchul Kim case DRM_FORMAT_YUV444: 49616102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR420; 49716102edbSEunchul Kim break; 49816102edbSEunchul Kim case DRM_FORMAT_YUYV: 49916102edbSEunchul Kim case DRM_FORMAT_YVYU: 50016102edbSEunchul Kim case DRM_FORMAT_UYVY: 50116102edbSEunchul Kim case DRM_FORMAT_VYUY: 50216102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR422_1PLANE; 50316102edbSEunchul Kim break; 50416102edbSEunchul Kim case DRM_FORMAT_NV16: 50516102edbSEunchul Kim case DRM_FORMAT_NV61: 50616102edbSEunchul Kim case DRM_FORMAT_YUV422: 50716102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR422; 50816102edbSEunchul Kim break; 50916102edbSEunchul Kim case DRM_FORMAT_YUV420: 51016102edbSEunchul Kim case DRM_FORMAT_YVU420: 51116102edbSEunchul Kim case DRM_FORMAT_NV12: 51216102edbSEunchul Kim case DRM_FORMAT_NV21: 51316102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR420; 51416102edbSEunchul Kim break; 51516102edbSEunchul Kim default: 51616102edbSEunchul Kim dev_err(ippdrv->dev, "inavlid source format 0x%x.\n", fmt); 51716102edbSEunchul Kim return -EINVAL; 51816102edbSEunchul Kim } 51916102edbSEunchul Kim 520acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_MSCTRL); 52116102edbSEunchul Kim 522acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CIDMAPARAM); 52316102edbSEunchul Kim cfg &= ~EXYNOS_CIDMAPARAM_R_MODE_MASK; 52416102edbSEunchul Kim 52516102edbSEunchul Kim cfg |= EXYNOS_CIDMAPARAM_R_MODE_LINEAR; 52616102edbSEunchul Kim 527acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIDMAPARAM); 52816102edbSEunchul Kim 52916102edbSEunchul Kim return fimc_src_set_fmt_order(ctx, fmt); 53016102edbSEunchul Kim } 53116102edbSEunchul Kim 53216102edbSEunchul Kim static int fimc_src_set_transf(struct device *dev, 53316102edbSEunchul Kim enum drm_exynos_degree degree, 53416102edbSEunchul Kim enum drm_exynos_flip flip, bool *swap) 53516102edbSEunchul Kim { 53616102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 53716102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 53816102edbSEunchul Kim u32 cfg1, cfg2; 53916102edbSEunchul Kim 540cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip); 54116102edbSEunchul Kim 542acd8afa8SAndrzej Hajda cfg1 = fimc_read(ctx, EXYNOS_MSCTRL); 54316102edbSEunchul Kim cfg1 &= ~(EXYNOS_MSCTRL_FLIP_X_MIRROR | 54416102edbSEunchul Kim EXYNOS_MSCTRL_FLIP_Y_MIRROR); 54516102edbSEunchul Kim 546acd8afa8SAndrzej Hajda cfg2 = fimc_read(ctx, EXYNOS_CITRGFMT); 54716102edbSEunchul Kim cfg2 &= ~EXYNOS_CITRGFMT_INROT90_CLOCKWISE; 54816102edbSEunchul Kim 54916102edbSEunchul Kim switch (degree) { 55016102edbSEunchul Kim case EXYNOS_DRM_DEGREE_0: 55116102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_VERTICAL) 55216102edbSEunchul Kim cfg1 |= EXYNOS_MSCTRL_FLIP_X_MIRROR; 55316102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) 55416102edbSEunchul Kim cfg1 |= EXYNOS_MSCTRL_FLIP_Y_MIRROR; 55516102edbSEunchul Kim break; 55616102edbSEunchul Kim case EXYNOS_DRM_DEGREE_90: 55716102edbSEunchul Kim cfg2 |= EXYNOS_CITRGFMT_INROT90_CLOCKWISE; 55816102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_VERTICAL) 55916102edbSEunchul Kim cfg1 |= EXYNOS_MSCTRL_FLIP_X_MIRROR; 56016102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) 56116102edbSEunchul Kim cfg1 |= EXYNOS_MSCTRL_FLIP_Y_MIRROR; 56216102edbSEunchul Kim break; 56316102edbSEunchul Kim case EXYNOS_DRM_DEGREE_180: 56416102edbSEunchul Kim cfg1 |= (EXYNOS_MSCTRL_FLIP_X_MIRROR | 56516102edbSEunchul Kim EXYNOS_MSCTRL_FLIP_Y_MIRROR); 56616102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_VERTICAL) 56716102edbSEunchul Kim cfg1 &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR; 56816102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) 56916102edbSEunchul Kim cfg1 &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR; 57016102edbSEunchul Kim break; 57116102edbSEunchul Kim case EXYNOS_DRM_DEGREE_270: 57216102edbSEunchul Kim cfg1 |= (EXYNOS_MSCTRL_FLIP_X_MIRROR | 57316102edbSEunchul Kim EXYNOS_MSCTRL_FLIP_Y_MIRROR); 57416102edbSEunchul Kim cfg2 |= EXYNOS_CITRGFMT_INROT90_CLOCKWISE; 57516102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_VERTICAL) 57616102edbSEunchul Kim cfg1 &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR; 57716102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) 57816102edbSEunchul Kim cfg1 &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR; 57916102edbSEunchul Kim break; 58016102edbSEunchul Kim default: 58116102edbSEunchul Kim dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); 58216102edbSEunchul Kim return -EINVAL; 58316102edbSEunchul Kim } 58416102edbSEunchul Kim 585acd8afa8SAndrzej Hajda fimc_write(ctx, cfg1, EXYNOS_MSCTRL); 586acd8afa8SAndrzej Hajda fimc_write(ctx, cfg2, EXYNOS_CITRGFMT); 58716102edbSEunchul Kim *swap = (cfg2 & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) ? 1 : 0; 58816102edbSEunchul Kim 58916102edbSEunchul Kim return 0; 59016102edbSEunchul Kim } 59116102edbSEunchul Kim 59216102edbSEunchul Kim static int fimc_set_window(struct fimc_context *ctx, 59316102edbSEunchul Kim struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) 59416102edbSEunchul Kim { 59516102edbSEunchul Kim u32 cfg, h1, h2, v1, v2; 59616102edbSEunchul Kim 59716102edbSEunchul Kim /* cropped image */ 59816102edbSEunchul Kim h1 = pos->x; 59916102edbSEunchul Kim h2 = sz->hsize - pos->w - pos->x; 60016102edbSEunchul Kim v1 = pos->y; 60116102edbSEunchul Kim v2 = sz->vsize - pos->h - pos->y; 60216102edbSEunchul Kim 603cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]hsize[%d]vsize[%d]\n", 604cbc4c33dSYoungJun Cho pos->x, pos->y, pos->w, pos->h, sz->hsize, sz->vsize); 605cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("h1[%d]h2[%d]v1[%d]v2[%d]\n", h1, h2, v1, v2); 60616102edbSEunchul Kim 60716102edbSEunchul Kim /* 60816102edbSEunchul Kim * set window offset 1, 2 size 60916102edbSEunchul Kim * check figure 43-21 in user manual 61016102edbSEunchul Kim */ 611acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CIWDOFST); 61216102edbSEunchul Kim cfg &= ~(EXYNOS_CIWDOFST_WINHOROFST_MASK | 61316102edbSEunchul Kim EXYNOS_CIWDOFST_WINVEROFST_MASK); 61416102edbSEunchul Kim cfg |= (EXYNOS_CIWDOFST_WINHOROFST(h1) | 61516102edbSEunchul Kim EXYNOS_CIWDOFST_WINVEROFST(v1)); 61616102edbSEunchul Kim cfg |= EXYNOS_CIWDOFST_WINOFSEN; 617acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIWDOFST); 61816102edbSEunchul Kim 61916102edbSEunchul Kim cfg = (EXYNOS_CIWDOFST2_WINHOROFST2(h2) | 62016102edbSEunchul Kim EXYNOS_CIWDOFST2_WINVEROFST2(v2)); 621acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIWDOFST2); 62216102edbSEunchul Kim 62316102edbSEunchul Kim return 0; 62416102edbSEunchul Kim } 62516102edbSEunchul Kim 62616102edbSEunchul Kim static int fimc_src_set_size(struct device *dev, int swap, 62716102edbSEunchul Kim struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) 62816102edbSEunchul Kim { 62916102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 63016102edbSEunchul Kim struct drm_exynos_pos img_pos = *pos; 63116102edbSEunchul Kim struct drm_exynos_sz img_sz = *sz; 63216102edbSEunchul Kim u32 cfg; 63316102edbSEunchul Kim 634cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("swap[%d]hsize[%d]vsize[%d]\n", 635cbc4c33dSYoungJun Cho swap, sz->hsize, sz->vsize); 63616102edbSEunchul Kim 63716102edbSEunchul Kim /* original size */ 63816102edbSEunchul Kim cfg = (EXYNOS_ORGISIZE_HORIZONTAL(img_sz.hsize) | 63916102edbSEunchul Kim EXYNOS_ORGISIZE_VERTICAL(img_sz.vsize)); 64016102edbSEunchul Kim 641acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_ORGISIZE); 64216102edbSEunchul Kim 643cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h); 64416102edbSEunchul Kim 64516102edbSEunchul Kim if (swap) { 64616102edbSEunchul Kim img_pos.w = pos->h; 64716102edbSEunchul Kim img_pos.h = pos->w; 64816102edbSEunchul Kim img_sz.hsize = sz->vsize; 64916102edbSEunchul Kim img_sz.vsize = sz->hsize; 65016102edbSEunchul Kim } 65116102edbSEunchul Kim 65216102edbSEunchul Kim /* set input DMA image size */ 653acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CIREAL_ISIZE); 65416102edbSEunchul Kim cfg &= ~(EXYNOS_CIREAL_ISIZE_HEIGHT_MASK | 65516102edbSEunchul Kim EXYNOS_CIREAL_ISIZE_WIDTH_MASK); 65616102edbSEunchul Kim cfg |= (EXYNOS_CIREAL_ISIZE_WIDTH(img_pos.w) | 65716102edbSEunchul Kim EXYNOS_CIREAL_ISIZE_HEIGHT(img_pos.h)); 658acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIREAL_ISIZE); 65916102edbSEunchul Kim 66016102edbSEunchul Kim /* 66116102edbSEunchul Kim * set input FIFO image size 66216102edbSEunchul Kim * for now, we support only ITU601 8 bit mode 66316102edbSEunchul Kim */ 66416102edbSEunchul Kim cfg = (EXYNOS_CISRCFMT_ITU601_8BIT | 66516102edbSEunchul Kim EXYNOS_CISRCFMT_SOURCEHSIZE(img_sz.hsize) | 66616102edbSEunchul Kim EXYNOS_CISRCFMT_SOURCEVSIZE(img_sz.vsize)); 667acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CISRCFMT); 66816102edbSEunchul Kim 66916102edbSEunchul Kim /* offset Y(RGB), Cb, Cr */ 67016102edbSEunchul Kim cfg = (EXYNOS_CIIYOFF_HORIZONTAL(img_pos.x) | 67116102edbSEunchul Kim EXYNOS_CIIYOFF_VERTICAL(img_pos.y)); 672acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIIYOFF); 67316102edbSEunchul Kim cfg = (EXYNOS_CIICBOFF_HORIZONTAL(img_pos.x) | 67416102edbSEunchul Kim EXYNOS_CIICBOFF_VERTICAL(img_pos.y)); 675acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIICBOFF); 67616102edbSEunchul Kim cfg = (EXYNOS_CIICROFF_HORIZONTAL(img_pos.x) | 67716102edbSEunchul Kim EXYNOS_CIICROFF_VERTICAL(img_pos.y)); 678acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIICROFF); 67916102edbSEunchul Kim 68016102edbSEunchul Kim return fimc_set_window(ctx, &img_pos, &img_sz); 68116102edbSEunchul Kim } 68216102edbSEunchul Kim 68316102edbSEunchul Kim static int fimc_src_set_addr(struct device *dev, 68416102edbSEunchul Kim struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, 68516102edbSEunchul Kim enum drm_exynos_ipp_buf_type buf_type) 68616102edbSEunchul Kim { 68716102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 68816102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 6897259c3d6SEunchul Kim struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; 69016102edbSEunchul Kim struct drm_exynos_ipp_property *property; 69116102edbSEunchul Kim struct drm_exynos_ipp_config *config; 69216102edbSEunchul Kim 69316102edbSEunchul Kim if (!c_node) { 69416102edbSEunchul Kim DRM_ERROR("failed to get c_node.\n"); 69516102edbSEunchul Kim return -EINVAL; 69616102edbSEunchul Kim } 69716102edbSEunchul Kim 69816102edbSEunchul Kim property = &c_node->property; 69916102edbSEunchul Kim 700cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n", 70116102edbSEunchul Kim property->prop_id, buf_id, buf_type); 70216102edbSEunchul Kim 70316102edbSEunchul Kim if (buf_id > FIMC_MAX_SRC) { 70416102edbSEunchul Kim dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id); 70516102edbSEunchul Kim return -ENOMEM; 70616102edbSEunchul Kim } 70716102edbSEunchul Kim 70816102edbSEunchul Kim /* address register set */ 70916102edbSEunchul Kim switch (buf_type) { 71016102edbSEunchul Kim case IPP_BUF_ENQUEUE: 71116102edbSEunchul Kim config = &property->config[EXYNOS_DRM_OPS_SRC]; 712acd8afa8SAndrzej Hajda fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y], 71320ed715eSAndrzej Hajda EXYNOS_CIIYSA0); 71416102edbSEunchul Kim 71516102edbSEunchul Kim if (config->fmt == DRM_FORMAT_YVU420) { 716acd8afa8SAndrzej Hajda fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR], 71720ed715eSAndrzej Hajda EXYNOS_CIICBSA0); 718acd8afa8SAndrzej Hajda fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB], 71920ed715eSAndrzej Hajda EXYNOS_CIICRSA0); 72016102edbSEunchul Kim } else { 721acd8afa8SAndrzej Hajda fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB], 72220ed715eSAndrzej Hajda EXYNOS_CIICBSA0); 723acd8afa8SAndrzej Hajda fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR], 72420ed715eSAndrzej Hajda EXYNOS_CIICRSA0); 72516102edbSEunchul Kim } 72616102edbSEunchul Kim break; 72716102edbSEunchul Kim case IPP_BUF_DEQUEUE: 72820ed715eSAndrzej Hajda fimc_write(ctx, 0x0, EXYNOS_CIIYSA0); 72920ed715eSAndrzej Hajda fimc_write(ctx, 0x0, EXYNOS_CIICBSA0); 73020ed715eSAndrzej Hajda fimc_write(ctx, 0x0, EXYNOS_CIICRSA0); 73116102edbSEunchul Kim break; 73216102edbSEunchul Kim default: 73316102edbSEunchul Kim /* bypass */ 73416102edbSEunchul Kim break; 73516102edbSEunchul Kim } 73616102edbSEunchul Kim 73716102edbSEunchul Kim return 0; 73816102edbSEunchul Kim } 73916102edbSEunchul Kim 74016102edbSEunchul Kim static struct exynos_drm_ipp_ops fimc_src_ops = { 74116102edbSEunchul Kim .set_fmt = fimc_src_set_fmt, 74216102edbSEunchul Kim .set_transf = fimc_src_set_transf, 74316102edbSEunchul Kim .set_size = fimc_src_set_size, 74416102edbSEunchul Kim .set_addr = fimc_src_set_addr, 74516102edbSEunchul Kim }; 74616102edbSEunchul Kim 74716102edbSEunchul Kim static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt) 74816102edbSEunchul Kim { 74916102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 75016102edbSEunchul Kim u32 cfg; 75116102edbSEunchul Kim 752cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); 75316102edbSEunchul Kim 75416102edbSEunchul Kim /* RGB */ 755acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CISCCTRL); 75616102edbSEunchul Kim cfg &= ~EXYNOS_CISCCTRL_OUTRGB_FMT_RGB_MASK; 75716102edbSEunchul Kim 75816102edbSEunchul Kim switch (fmt) { 75916102edbSEunchul Kim case DRM_FORMAT_RGB565: 76016102edbSEunchul Kim cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565; 761acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CISCCTRL); 76216102edbSEunchul Kim return 0; 76316102edbSEunchul Kim case DRM_FORMAT_RGB888: 76416102edbSEunchul Kim cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888; 765acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CISCCTRL); 76616102edbSEunchul Kim return 0; 76716102edbSEunchul Kim case DRM_FORMAT_XRGB8888: 76816102edbSEunchul Kim cfg |= (EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 | 76916102edbSEunchul Kim EXYNOS_CISCCTRL_EXTRGB_EXTENSION); 770acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CISCCTRL); 77116102edbSEunchul Kim break; 77216102edbSEunchul Kim default: 77316102edbSEunchul Kim /* bypass */ 77416102edbSEunchul Kim break; 77516102edbSEunchul Kim } 77616102edbSEunchul Kim 77716102edbSEunchul Kim /* YUV */ 778acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CIOCTRL); 77916102edbSEunchul Kim cfg &= ~(EXYNOS_CIOCTRL_ORDER2P_MASK | 78016102edbSEunchul Kim EXYNOS_CIOCTRL_ORDER422_MASK | 78116102edbSEunchul Kim EXYNOS_CIOCTRL_YCBCR_PLANE_MASK); 78216102edbSEunchul Kim 78316102edbSEunchul Kim switch (fmt) { 78416102edbSEunchul Kim case DRM_FORMAT_XRGB8888: 78516102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_ALPHA_OUT; 78616102edbSEunchul Kim break; 78716102edbSEunchul Kim case DRM_FORMAT_YUYV: 78816102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_ORDER422_YCBYCR; 78916102edbSEunchul Kim break; 79016102edbSEunchul Kim case DRM_FORMAT_YVYU: 79116102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_ORDER422_YCRYCB; 79216102edbSEunchul Kim break; 79316102edbSEunchul Kim case DRM_FORMAT_UYVY: 79416102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_ORDER422_CBYCRY; 79516102edbSEunchul Kim break; 79616102edbSEunchul Kim case DRM_FORMAT_VYUY: 79716102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_ORDER422_CRYCBY; 79816102edbSEunchul Kim break; 79916102edbSEunchul Kim case DRM_FORMAT_NV21: 80016102edbSEunchul Kim case DRM_FORMAT_NV61: 80116102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_ORDER2P_LSB_CRCB; 80216102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_YCBCR_2PLANE; 80316102edbSEunchul Kim break; 80416102edbSEunchul Kim case DRM_FORMAT_YUV422: 80516102edbSEunchul Kim case DRM_FORMAT_YUV420: 80616102edbSEunchul Kim case DRM_FORMAT_YVU420: 80716102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_YCBCR_3PLANE; 80816102edbSEunchul Kim break; 80916102edbSEunchul Kim case DRM_FORMAT_NV12: 81016102edbSEunchul Kim case DRM_FORMAT_NV16: 81116102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_ORDER2P_LSB_CBCR; 81216102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_YCBCR_2PLANE; 81316102edbSEunchul Kim break; 81416102edbSEunchul Kim default: 81516102edbSEunchul Kim dev_err(ippdrv->dev, "inavlid target yuv order 0x%x.\n", fmt); 81616102edbSEunchul Kim return -EINVAL; 81716102edbSEunchul Kim } 81816102edbSEunchul Kim 819acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIOCTRL); 82016102edbSEunchul Kim 82116102edbSEunchul Kim return 0; 82216102edbSEunchul Kim } 82316102edbSEunchul Kim 82416102edbSEunchul Kim static int fimc_dst_set_fmt(struct device *dev, u32 fmt) 82516102edbSEunchul Kim { 82616102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 82716102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 82816102edbSEunchul Kim u32 cfg; 82916102edbSEunchul Kim 830cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); 83116102edbSEunchul Kim 832acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CIEXTEN); 83316102edbSEunchul Kim 83416102edbSEunchul Kim if (fmt == DRM_FORMAT_AYUV) { 83516102edbSEunchul Kim cfg |= EXYNOS_CIEXTEN_YUV444_OUT; 836acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIEXTEN); 83716102edbSEunchul Kim } else { 83816102edbSEunchul Kim cfg &= ~EXYNOS_CIEXTEN_YUV444_OUT; 839acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIEXTEN); 84016102edbSEunchul Kim 841acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CITRGFMT); 84216102edbSEunchul Kim cfg &= ~EXYNOS_CITRGFMT_OUTFORMAT_MASK; 84316102edbSEunchul Kim 84416102edbSEunchul Kim switch (fmt) { 84516102edbSEunchul Kim case DRM_FORMAT_RGB565: 84616102edbSEunchul Kim case DRM_FORMAT_RGB888: 84716102edbSEunchul Kim case DRM_FORMAT_XRGB8888: 84816102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_OUTFORMAT_RGB; 84916102edbSEunchul Kim break; 85016102edbSEunchul Kim case DRM_FORMAT_YUYV: 85116102edbSEunchul Kim case DRM_FORMAT_YVYU: 85216102edbSEunchul Kim case DRM_FORMAT_UYVY: 85316102edbSEunchul Kim case DRM_FORMAT_VYUY: 85416102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422_1PLANE; 85516102edbSEunchul Kim break; 85616102edbSEunchul Kim case DRM_FORMAT_NV16: 85716102edbSEunchul Kim case DRM_FORMAT_NV61: 85816102edbSEunchul Kim case DRM_FORMAT_YUV422: 85916102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422; 86016102edbSEunchul Kim break; 86116102edbSEunchul Kim case DRM_FORMAT_YUV420: 86216102edbSEunchul Kim case DRM_FORMAT_YVU420: 86316102edbSEunchul Kim case DRM_FORMAT_NV12: 86416102edbSEunchul Kim case DRM_FORMAT_NV21: 86516102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR420; 86616102edbSEunchul Kim break; 86716102edbSEunchul Kim default: 86816102edbSEunchul Kim dev_err(ippdrv->dev, "inavlid target format 0x%x.\n", 86916102edbSEunchul Kim fmt); 87016102edbSEunchul Kim return -EINVAL; 87116102edbSEunchul Kim } 87216102edbSEunchul Kim 873acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CITRGFMT); 87416102edbSEunchul Kim } 87516102edbSEunchul Kim 876acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CIDMAPARAM); 87716102edbSEunchul Kim cfg &= ~EXYNOS_CIDMAPARAM_W_MODE_MASK; 87816102edbSEunchul Kim 87916102edbSEunchul Kim cfg |= EXYNOS_CIDMAPARAM_W_MODE_LINEAR; 88016102edbSEunchul Kim 881acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIDMAPARAM); 88216102edbSEunchul Kim 88316102edbSEunchul Kim return fimc_dst_set_fmt_order(ctx, fmt); 88416102edbSEunchul Kim } 88516102edbSEunchul Kim 88616102edbSEunchul Kim static int fimc_dst_set_transf(struct device *dev, 88716102edbSEunchul Kim enum drm_exynos_degree degree, 88816102edbSEunchul Kim enum drm_exynos_flip flip, bool *swap) 88916102edbSEunchul Kim { 89016102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 89116102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 89216102edbSEunchul Kim u32 cfg; 89316102edbSEunchul Kim 894cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip); 89516102edbSEunchul Kim 896acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CITRGFMT); 89716102edbSEunchul Kim cfg &= ~EXYNOS_CITRGFMT_FLIP_MASK; 89816102edbSEunchul Kim cfg &= ~EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE; 89916102edbSEunchul Kim 90016102edbSEunchul Kim switch (degree) { 90116102edbSEunchul Kim case EXYNOS_DRM_DEGREE_0: 90216102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_VERTICAL) 90316102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR; 90416102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) 90516102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR; 90616102edbSEunchul Kim break; 90716102edbSEunchul Kim case EXYNOS_DRM_DEGREE_90: 90816102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE; 90916102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_VERTICAL) 91016102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR; 91116102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) 91216102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR; 91316102edbSEunchul Kim break; 91416102edbSEunchul Kim case EXYNOS_DRM_DEGREE_180: 91516102edbSEunchul Kim cfg |= (EXYNOS_CITRGFMT_FLIP_X_MIRROR | 91616102edbSEunchul Kim EXYNOS_CITRGFMT_FLIP_Y_MIRROR); 91716102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_VERTICAL) 91816102edbSEunchul Kim cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR; 91916102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) 92016102edbSEunchul Kim cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR; 92116102edbSEunchul Kim break; 92216102edbSEunchul Kim case EXYNOS_DRM_DEGREE_270: 92316102edbSEunchul Kim cfg |= (EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE | 92416102edbSEunchul Kim EXYNOS_CITRGFMT_FLIP_X_MIRROR | 92516102edbSEunchul Kim EXYNOS_CITRGFMT_FLIP_Y_MIRROR); 92616102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_VERTICAL) 92716102edbSEunchul Kim cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR; 92816102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) 92916102edbSEunchul Kim cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR; 93016102edbSEunchul Kim break; 93116102edbSEunchul Kim default: 93216102edbSEunchul Kim dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); 93316102edbSEunchul Kim return -EINVAL; 93416102edbSEunchul Kim } 93516102edbSEunchul Kim 936acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CITRGFMT); 93716102edbSEunchul Kim *swap = (cfg & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) ? 1 : 0; 93816102edbSEunchul Kim 93916102edbSEunchul Kim return 0; 94016102edbSEunchul Kim } 94116102edbSEunchul Kim 94216102edbSEunchul Kim static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc, 94316102edbSEunchul Kim struct drm_exynos_pos *src, struct drm_exynos_pos *dst) 94416102edbSEunchul Kim { 94516102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 94616102edbSEunchul Kim u32 cfg, cfg_ext, shfactor; 94716102edbSEunchul Kim u32 pre_dst_width, pre_dst_height; 948be6cdfd1SAndrzej Hajda u32 hfactor, vfactor; 94916102edbSEunchul Kim int ret = 0; 95016102edbSEunchul Kim u32 src_w, src_h, dst_w, dst_h; 95116102edbSEunchul Kim 952acd8afa8SAndrzej Hajda cfg_ext = fimc_read(ctx, EXYNOS_CITRGFMT); 95316102edbSEunchul Kim if (cfg_ext & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) { 95416102edbSEunchul Kim src_w = src->h; 95516102edbSEunchul Kim src_h = src->w; 95616102edbSEunchul Kim } else { 95716102edbSEunchul Kim src_w = src->w; 95816102edbSEunchul Kim src_h = src->h; 95916102edbSEunchul Kim } 96016102edbSEunchul Kim 96116102edbSEunchul Kim if (cfg_ext & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) { 96216102edbSEunchul Kim dst_w = dst->h; 96316102edbSEunchul Kim dst_h = dst->w; 96416102edbSEunchul Kim } else { 96516102edbSEunchul Kim dst_w = dst->w; 96616102edbSEunchul Kim dst_h = dst->h; 96716102edbSEunchul Kim } 96816102edbSEunchul Kim 969be6cdfd1SAndrzej Hajda /* fimc_ippdrv_check_property assures that dividers are not null */ 970be6cdfd1SAndrzej Hajda hfactor = fls(src_w / dst_w / 2); 971be6cdfd1SAndrzej Hajda if (hfactor > FIMC_SHFACTOR / 2) { 97216102edbSEunchul Kim dev_err(ippdrv->dev, "failed to get ratio horizontal.\n"); 973be6cdfd1SAndrzej Hajda return -EINVAL; 97416102edbSEunchul Kim } 97516102edbSEunchul Kim 976be6cdfd1SAndrzej Hajda vfactor = fls(src_h / dst_h / 2); 977be6cdfd1SAndrzej Hajda if (vfactor > FIMC_SHFACTOR / 2) { 97816102edbSEunchul Kim dev_err(ippdrv->dev, "failed to get ratio vertical.\n"); 979be6cdfd1SAndrzej Hajda return -EINVAL; 98016102edbSEunchul Kim } 98116102edbSEunchul Kim 982be6cdfd1SAndrzej Hajda pre_dst_width = src_w >> hfactor; 983be6cdfd1SAndrzej Hajda pre_dst_height = src_h >> vfactor; 984cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("pre_dst_width[%d]pre_dst_height[%d]\n", 98516102edbSEunchul Kim pre_dst_width, pre_dst_height); 986be6cdfd1SAndrzej Hajda DRM_DEBUG_KMS("hfactor[%d]vfactor[%d]\n", hfactor, vfactor); 98716102edbSEunchul Kim 98816102edbSEunchul Kim sc->hratio = (src_w << 14) / (dst_w << hfactor); 98916102edbSEunchul Kim sc->vratio = (src_h << 14) / (dst_h << vfactor); 99016102edbSEunchul Kim sc->up_h = (dst_w >= src_w) ? true : false; 99116102edbSEunchul Kim sc->up_v = (dst_h >= src_h) ? true : false; 992cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("hratio[%d]vratio[%d]up_h[%d]up_v[%d]\n", 993cbc4c33dSYoungJun Cho sc->hratio, sc->vratio, sc->up_h, sc->up_v); 99416102edbSEunchul Kim 99516102edbSEunchul Kim shfactor = FIMC_SHFACTOR - (hfactor + vfactor); 996cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("shfactor[%d]\n", shfactor); 99716102edbSEunchul Kim 99816102edbSEunchul Kim cfg = (EXYNOS_CISCPRERATIO_SHFACTOR(shfactor) | 999be6cdfd1SAndrzej Hajda EXYNOS_CISCPRERATIO_PREHORRATIO(1 << hfactor) | 1000be6cdfd1SAndrzej Hajda EXYNOS_CISCPRERATIO_PREVERRATIO(1 << vfactor)); 1001acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CISCPRERATIO); 100216102edbSEunchul Kim 100316102edbSEunchul Kim cfg = (EXYNOS_CISCPREDST_PREDSTWIDTH(pre_dst_width) | 100416102edbSEunchul Kim EXYNOS_CISCPREDST_PREDSTHEIGHT(pre_dst_height)); 1005acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CISCPREDST); 100616102edbSEunchul Kim 100716102edbSEunchul Kim return ret; 100816102edbSEunchul Kim } 100916102edbSEunchul Kim 101016102edbSEunchul Kim static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc) 101116102edbSEunchul Kim { 101216102edbSEunchul Kim u32 cfg, cfg_ext; 101316102edbSEunchul Kim 1014cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("range[%d]bypass[%d]up_h[%d]up_v[%d]\n", 1015cbc4c33dSYoungJun Cho sc->range, sc->bypass, sc->up_h, sc->up_v); 1016cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("hratio[%d]vratio[%d]\n", 1017cbc4c33dSYoungJun Cho sc->hratio, sc->vratio); 101816102edbSEunchul Kim 1019acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CISCCTRL); 102016102edbSEunchul Kim cfg &= ~(EXYNOS_CISCCTRL_SCALERBYPASS | 102116102edbSEunchul Kim EXYNOS_CISCCTRL_SCALEUP_H | EXYNOS_CISCCTRL_SCALEUP_V | 102216102edbSEunchul Kim EXYNOS_CISCCTRL_MAIN_V_RATIO_MASK | 102316102edbSEunchul Kim EXYNOS_CISCCTRL_MAIN_H_RATIO_MASK | 102416102edbSEunchul Kim EXYNOS_CISCCTRL_CSCR2Y_WIDE | 102516102edbSEunchul Kim EXYNOS_CISCCTRL_CSCY2R_WIDE); 102616102edbSEunchul Kim 102716102edbSEunchul Kim if (sc->range) 102816102edbSEunchul Kim cfg |= (EXYNOS_CISCCTRL_CSCR2Y_WIDE | 102916102edbSEunchul Kim EXYNOS_CISCCTRL_CSCY2R_WIDE); 103016102edbSEunchul Kim if (sc->bypass) 103116102edbSEunchul Kim cfg |= EXYNOS_CISCCTRL_SCALERBYPASS; 103216102edbSEunchul Kim if (sc->up_h) 103316102edbSEunchul Kim cfg |= EXYNOS_CISCCTRL_SCALEUP_H; 103416102edbSEunchul Kim if (sc->up_v) 103516102edbSEunchul Kim cfg |= EXYNOS_CISCCTRL_SCALEUP_V; 103616102edbSEunchul Kim 103716102edbSEunchul Kim cfg |= (EXYNOS_CISCCTRL_MAINHORRATIO((sc->hratio >> 6)) | 103816102edbSEunchul Kim EXYNOS_CISCCTRL_MAINVERRATIO((sc->vratio >> 6))); 1039acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CISCCTRL); 104016102edbSEunchul Kim 1041acd8afa8SAndrzej Hajda cfg_ext = fimc_read(ctx, EXYNOS_CIEXTEN); 104216102edbSEunchul Kim cfg_ext &= ~EXYNOS_CIEXTEN_MAINHORRATIO_EXT_MASK; 104316102edbSEunchul Kim cfg_ext &= ~EXYNOS_CIEXTEN_MAINVERRATIO_EXT_MASK; 104416102edbSEunchul Kim cfg_ext |= (EXYNOS_CIEXTEN_MAINHORRATIO_EXT(sc->hratio) | 104516102edbSEunchul Kim EXYNOS_CIEXTEN_MAINVERRATIO_EXT(sc->vratio)); 1046acd8afa8SAndrzej Hajda fimc_write(ctx, cfg_ext, EXYNOS_CIEXTEN); 104716102edbSEunchul Kim } 104816102edbSEunchul Kim 104916102edbSEunchul Kim static int fimc_dst_set_size(struct device *dev, int swap, 105016102edbSEunchul Kim struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) 105116102edbSEunchul Kim { 105216102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 105316102edbSEunchul Kim struct drm_exynos_pos img_pos = *pos; 105416102edbSEunchul Kim struct drm_exynos_sz img_sz = *sz; 105516102edbSEunchul Kim u32 cfg; 105616102edbSEunchul Kim 1057cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("swap[%d]hsize[%d]vsize[%d]\n", 1058cbc4c33dSYoungJun Cho swap, sz->hsize, sz->vsize); 105916102edbSEunchul Kim 106016102edbSEunchul Kim /* original size */ 106116102edbSEunchul Kim cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(img_sz.hsize) | 106216102edbSEunchul Kim EXYNOS_ORGOSIZE_VERTICAL(img_sz.vsize)); 106316102edbSEunchul Kim 1064acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_ORGOSIZE); 106516102edbSEunchul Kim 1066cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h); 106716102edbSEunchul Kim 106816102edbSEunchul Kim /* CSC ITU */ 1069acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CIGCTRL); 107016102edbSEunchul Kim cfg &= ~EXYNOS_CIGCTRL_CSC_MASK; 107116102edbSEunchul Kim 107216102edbSEunchul Kim if (sz->hsize >= FIMC_WIDTH_ITU_709) 107316102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_CSC_ITU709; 107416102edbSEunchul Kim else 107516102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_CSC_ITU601; 107616102edbSEunchul Kim 1077acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIGCTRL); 107816102edbSEunchul Kim 107916102edbSEunchul Kim if (swap) { 108016102edbSEunchul Kim img_pos.w = pos->h; 108116102edbSEunchul Kim img_pos.h = pos->w; 108216102edbSEunchul Kim img_sz.hsize = sz->vsize; 108316102edbSEunchul Kim img_sz.vsize = sz->hsize; 108416102edbSEunchul Kim } 108516102edbSEunchul Kim 108616102edbSEunchul Kim /* target image size */ 1087acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CITRGFMT); 108816102edbSEunchul Kim cfg &= ~(EXYNOS_CITRGFMT_TARGETH_MASK | 108916102edbSEunchul Kim EXYNOS_CITRGFMT_TARGETV_MASK); 109016102edbSEunchul Kim cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(img_pos.w) | 109116102edbSEunchul Kim EXYNOS_CITRGFMT_TARGETVSIZE(img_pos.h)); 1092acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CITRGFMT); 109316102edbSEunchul Kim 109416102edbSEunchul Kim /* target area */ 109516102edbSEunchul Kim cfg = EXYNOS_CITAREA_TARGET_AREA(img_pos.w * img_pos.h); 1096acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CITAREA); 109716102edbSEunchul Kim 109816102edbSEunchul Kim /* offset Y(RGB), Cb, Cr */ 109916102edbSEunchul Kim cfg = (EXYNOS_CIOYOFF_HORIZONTAL(img_pos.x) | 110016102edbSEunchul Kim EXYNOS_CIOYOFF_VERTICAL(img_pos.y)); 1101acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIOYOFF); 110216102edbSEunchul Kim cfg = (EXYNOS_CIOCBOFF_HORIZONTAL(img_pos.x) | 110316102edbSEunchul Kim EXYNOS_CIOCBOFF_VERTICAL(img_pos.y)); 1104acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIOCBOFF); 110516102edbSEunchul Kim cfg = (EXYNOS_CIOCROFF_HORIZONTAL(img_pos.x) | 110616102edbSEunchul Kim EXYNOS_CIOCROFF_VERTICAL(img_pos.y)); 1107acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIOCROFF); 110816102edbSEunchul Kim 110916102edbSEunchul Kim return 0; 111016102edbSEunchul Kim } 111116102edbSEunchul Kim 111256442d83SAndrzej Hajda static void fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id, 111316102edbSEunchul Kim enum drm_exynos_ipp_buf_type buf_type) 111416102edbSEunchul Kim { 111572d465aaSAndrzej Hajda unsigned long flags; 111656442d83SAndrzej Hajda u32 buf_num; 111756442d83SAndrzej Hajda u32 cfg; 111816102edbSEunchul Kim 1119cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type); 112016102edbSEunchul Kim 112172d465aaSAndrzej Hajda spin_lock_irqsave(&ctx->lock, flags); 112216102edbSEunchul Kim 1123acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ); 112416102edbSEunchul Kim 112556442d83SAndrzej Hajda if (buf_type == IPP_BUF_ENQUEUE) 112656442d83SAndrzej Hajda cfg |= (1 << buf_id); 112756442d83SAndrzej Hajda else 112856442d83SAndrzej Hajda cfg &= ~(1 << buf_id); 112916102edbSEunchul Kim 1130acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_CIFCNTSEQ); 113116102edbSEunchul Kim 113256442d83SAndrzej Hajda buf_num = hweight32(cfg); 113316102edbSEunchul Kim 113456442d83SAndrzej Hajda if (buf_type == IPP_BUF_ENQUEUE && buf_num >= FIMC_BUF_START) 113556442d83SAndrzej Hajda fimc_mask_irq(ctx, true); 113656442d83SAndrzej Hajda else if (buf_type == IPP_BUF_DEQUEUE && buf_num <= FIMC_BUF_STOP) 11378b4609cdSAndrzej Hajda fimc_mask_irq(ctx, false); 113816102edbSEunchul Kim 113972d465aaSAndrzej Hajda spin_unlock_irqrestore(&ctx->lock, flags); 114016102edbSEunchul Kim } 114116102edbSEunchul Kim 114216102edbSEunchul Kim static int fimc_dst_set_addr(struct device *dev, 114316102edbSEunchul Kim struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, 114416102edbSEunchul Kim enum drm_exynos_ipp_buf_type buf_type) 114516102edbSEunchul Kim { 114616102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 114716102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 11487259c3d6SEunchul Kim struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; 114916102edbSEunchul Kim struct drm_exynos_ipp_property *property; 115016102edbSEunchul Kim struct drm_exynos_ipp_config *config; 115116102edbSEunchul Kim 115216102edbSEunchul Kim if (!c_node) { 115316102edbSEunchul Kim DRM_ERROR("failed to get c_node.\n"); 115416102edbSEunchul Kim return -EINVAL; 115516102edbSEunchul Kim } 115616102edbSEunchul Kim 115716102edbSEunchul Kim property = &c_node->property; 115816102edbSEunchul Kim 1159cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n", 116016102edbSEunchul Kim property->prop_id, buf_id, buf_type); 116116102edbSEunchul Kim 116216102edbSEunchul Kim if (buf_id > FIMC_MAX_DST) { 116316102edbSEunchul Kim dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id); 116416102edbSEunchul Kim return -ENOMEM; 116516102edbSEunchul Kim } 116616102edbSEunchul Kim 116716102edbSEunchul Kim /* address register set */ 116816102edbSEunchul Kim switch (buf_type) { 116916102edbSEunchul Kim case IPP_BUF_ENQUEUE: 117016102edbSEunchul Kim config = &property->config[EXYNOS_DRM_OPS_DST]; 117116102edbSEunchul Kim 1172acd8afa8SAndrzej Hajda fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y], 117316102edbSEunchul Kim EXYNOS_CIOYSA(buf_id)); 117416102edbSEunchul Kim 117516102edbSEunchul Kim if (config->fmt == DRM_FORMAT_YVU420) { 1176acd8afa8SAndrzej Hajda fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR], 117716102edbSEunchul Kim EXYNOS_CIOCBSA(buf_id)); 1178acd8afa8SAndrzej Hajda fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB], 117916102edbSEunchul Kim EXYNOS_CIOCRSA(buf_id)); 118016102edbSEunchul Kim } else { 1181acd8afa8SAndrzej Hajda fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB], 118216102edbSEunchul Kim EXYNOS_CIOCBSA(buf_id)); 1183acd8afa8SAndrzej Hajda fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR], 118416102edbSEunchul Kim EXYNOS_CIOCRSA(buf_id)); 118516102edbSEunchul Kim } 118616102edbSEunchul Kim break; 118716102edbSEunchul Kim case IPP_BUF_DEQUEUE: 1188acd8afa8SAndrzej Hajda fimc_write(ctx, 0x0, EXYNOS_CIOYSA(buf_id)); 1189acd8afa8SAndrzej Hajda fimc_write(ctx, 0x0, EXYNOS_CIOCBSA(buf_id)); 1190acd8afa8SAndrzej Hajda fimc_write(ctx, 0x0, EXYNOS_CIOCRSA(buf_id)); 119116102edbSEunchul Kim break; 119216102edbSEunchul Kim default: 119316102edbSEunchul Kim /* bypass */ 119416102edbSEunchul Kim break; 119516102edbSEunchul Kim } 119616102edbSEunchul Kim 119756442d83SAndrzej Hajda fimc_dst_set_buf_seq(ctx, buf_id, buf_type); 119856442d83SAndrzej Hajda 119956442d83SAndrzej Hajda return 0; 120016102edbSEunchul Kim } 120116102edbSEunchul Kim 120216102edbSEunchul Kim static struct exynos_drm_ipp_ops fimc_dst_ops = { 120316102edbSEunchul Kim .set_fmt = fimc_dst_set_fmt, 120416102edbSEunchul Kim .set_transf = fimc_dst_set_transf, 120516102edbSEunchul Kim .set_size = fimc_dst_set_size, 120616102edbSEunchul Kim .set_addr = fimc_dst_set_addr, 120716102edbSEunchul Kim }; 120816102edbSEunchul Kim 120916102edbSEunchul Kim static irqreturn_t fimc_irq_handler(int irq, void *dev_id) 121016102edbSEunchul Kim { 121116102edbSEunchul Kim struct fimc_context *ctx = dev_id; 121216102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 12137259c3d6SEunchul Kim struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; 121416102edbSEunchul Kim struct drm_exynos_ipp_event_work *event_work = 121516102edbSEunchul Kim c_node->event_work; 121616102edbSEunchul Kim int buf_id; 121716102edbSEunchul Kim 1218cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("fimc id[%d]\n", ctx->id); 121916102edbSEunchul Kim 122016102edbSEunchul Kim fimc_clear_irq(ctx); 122116102edbSEunchul Kim if (fimc_check_ovf(ctx)) 122216102edbSEunchul Kim return IRQ_NONE; 122316102edbSEunchul Kim 122416102edbSEunchul Kim if (!fimc_check_frame_end(ctx)) 122516102edbSEunchul Kim return IRQ_NONE; 122616102edbSEunchul Kim 122716102edbSEunchul Kim buf_id = fimc_get_buf_id(ctx); 122816102edbSEunchul Kim if (buf_id < 0) 122916102edbSEunchul Kim return IRQ_HANDLED; 123016102edbSEunchul Kim 1231cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("buf_id[%d]\n", buf_id); 123216102edbSEunchul Kim 123356442d83SAndrzej Hajda fimc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE); 123416102edbSEunchul Kim 123516102edbSEunchul Kim event_work->ippdrv = ippdrv; 123616102edbSEunchul Kim event_work->buf_id[EXYNOS_DRM_OPS_DST] = buf_id; 123705afb1acSAndrzej Hajda queue_work(ippdrv->event_workq, &event_work->work); 123816102edbSEunchul Kim 123916102edbSEunchul Kim return IRQ_HANDLED; 124016102edbSEunchul Kim } 124116102edbSEunchul Kim 124216102edbSEunchul Kim static int fimc_init_prop_list(struct exynos_drm_ippdrv *ippdrv) 124316102edbSEunchul Kim { 124431646054SAndrzej Hajda struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list; 124516102edbSEunchul Kim 124616102edbSEunchul Kim prop_list->version = 1; 124716102edbSEunchul Kim prop_list->writeback = 1; 124816102edbSEunchul Kim prop_list->refresh_min = FIMC_REFRESH_MIN; 124916102edbSEunchul Kim prop_list->refresh_max = FIMC_REFRESH_MAX; 125016102edbSEunchul Kim prop_list->flip = (1 << EXYNOS_DRM_FLIP_NONE) | 125116102edbSEunchul Kim (1 << EXYNOS_DRM_FLIP_VERTICAL) | 125216102edbSEunchul Kim (1 << EXYNOS_DRM_FLIP_HORIZONTAL); 125316102edbSEunchul Kim prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) | 125416102edbSEunchul Kim (1 << EXYNOS_DRM_DEGREE_90) | 125516102edbSEunchul Kim (1 << EXYNOS_DRM_DEGREE_180) | 125616102edbSEunchul Kim (1 << EXYNOS_DRM_DEGREE_270); 125716102edbSEunchul Kim prop_list->csc = 1; 125816102edbSEunchul Kim prop_list->crop = 1; 125916102edbSEunchul Kim prop_list->crop_max.hsize = FIMC_CROP_MAX; 126016102edbSEunchul Kim prop_list->crop_max.vsize = FIMC_CROP_MAX; 126116102edbSEunchul Kim prop_list->crop_min.hsize = FIMC_CROP_MIN; 126216102edbSEunchul Kim prop_list->crop_min.vsize = FIMC_CROP_MIN; 126316102edbSEunchul Kim prop_list->scale = 1; 126416102edbSEunchul Kim prop_list->scale_max.hsize = FIMC_SCALE_MAX; 126516102edbSEunchul Kim prop_list->scale_max.vsize = FIMC_SCALE_MAX; 126616102edbSEunchul Kim prop_list->scale_min.hsize = FIMC_SCALE_MIN; 126716102edbSEunchul Kim prop_list->scale_min.vsize = FIMC_SCALE_MIN; 126816102edbSEunchul Kim 126916102edbSEunchul Kim return 0; 127016102edbSEunchul Kim } 127116102edbSEunchul Kim 127216102edbSEunchul Kim static inline bool fimc_check_drm_flip(enum drm_exynos_flip flip) 127316102edbSEunchul Kim { 127416102edbSEunchul Kim switch (flip) { 127516102edbSEunchul Kim case EXYNOS_DRM_FLIP_NONE: 127616102edbSEunchul Kim case EXYNOS_DRM_FLIP_VERTICAL: 127716102edbSEunchul Kim case EXYNOS_DRM_FLIP_HORIZONTAL: 12784f21877cSEunchul Kim case EXYNOS_DRM_FLIP_BOTH: 127916102edbSEunchul Kim return true; 128016102edbSEunchul Kim default: 1281cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("invalid flip\n"); 128216102edbSEunchul Kim return false; 128316102edbSEunchul Kim } 128416102edbSEunchul Kim } 128516102edbSEunchul Kim 128616102edbSEunchul Kim static int fimc_ippdrv_check_property(struct device *dev, 128716102edbSEunchul Kim struct drm_exynos_ipp_property *property) 128816102edbSEunchul Kim { 128916102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 129016102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 129131646054SAndrzej Hajda struct drm_exynos_ipp_prop_list *pp = &ippdrv->prop_list; 129216102edbSEunchul Kim struct drm_exynos_ipp_config *config; 129316102edbSEunchul Kim struct drm_exynos_pos *pos; 129416102edbSEunchul Kim struct drm_exynos_sz *sz; 129516102edbSEunchul Kim bool swap; 129616102edbSEunchul Kim int i; 129716102edbSEunchul Kim 129816102edbSEunchul Kim for_each_ipp_ops(i) { 129916102edbSEunchul Kim if ((i == EXYNOS_DRM_OPS_SRC) && 130016102edbSEunchul Kim (property->cmd == IPP_CMD_WB)) 130116102edbSEunchul Kim continue; 130216102edbSEunchul Kim 130316102edbSEunchul Kim config = &property->config[i]; 130416102edbSEunchul Kim pos = &config->pos; 130516102edbSEunchul Kim sz = &config->sz; 130616102edbSEunchul Kim 130716102edbSEunchul Kim /* check for flip */ 130816102edbSEunchul Kim if (!fimc_check_drm_flip(config->flip)) { 130916102edbSEunchul Kim DRM_ERROR("invalid flip.\n"); 131016102edbSEunchul Kim goto err_property; 131116102edbSEunchul Kim } 131216102edbSEunchul Kim 131316102edbSEunchul Kim /* check for degree */ 131416102edbSEunchul Kim switch (config->degree) { 131516102edbSEunchul Kim case EXYNOS_DRM_DEGREE_90: 131616102edbSEunchul Kim case EXYNOS_DRM_DEGREE_270: 131716102edbSEunchul Kim swap = true; 131816102edbSEunchul Kim break; 131916102edbSEunchul Kim case EXYNOS_DRM_DEGREE_0: 132016102edbSEunchul Kim case EXYNOS_DRM_DEGREE_180: 132116102edbSEunchul Kim swap = false; 132216102edbSEunchul Kim break; 132316102edbSEunchul Kim default: 132416102edbSEunchul Kim DRM_ERROR("invalid degree.\n"); 132516102edbSEunchul Kim goto err_property; 132616102edbSEunchul Kim } 132716102edbSEunchul Kim 132816102edbSEunchul Kim /* check for buffer bound */ 132916102edbSEunchul Kim if ((pos->x + pos->w > sz->hsize) || 133016102edbSEunchul Kim (pos->y + pos->h > sz->vsize)) { 133116102edbSEunchul Kim DRM_ERROR("out of buf bound.\n"); 133216102edbSEunchul Kim goto err_property; 133316102edbSEunchul Kim } 133416102edbSEunchul Kim 133516102edbSEunchul Kim /* check for crop */ 133616102edbSEunchul Kim if ((i == EXYNOS_DRM_OPS_SRC) && (pp->crop)) { 133716102edbSEunchul Kim if (swap) { 133816102edbSEunchul Kim if ((pos->h < pp->crop_min.hsize) || 133916102edbSEunchul Kim (sz->vsize > pp->crop_max.hsize) || 134016102edbSEunchul Kim (pos->w < pp->crop_min.vsize) || 134116102edbSEunchul Kim (sz->hsize > pp->crop_max.vsize)) { 134216102edbSEunchul Kim DRM_ERROR("out of crop size.\n"); 134316102edbSEunchul Kim goto err_property; 134416102edbSEunchul Kim } 134516102edbSEunchul Kim } else { 134616102edbSEunchul Kim if ((pos->w < pp->crop_min.hsize) || 134716102edbSEunchul Kim (sz->hsize > pp->crop_max.hsize) || 134816102edbSEunchul Kim (pos->h < pp->crop_min.vsize) || 134916102edbSEunchul Kim (sz->vsize > pp->crop_max.vsize)) { 135016102edbSEunchul Kim DRM_ERROR("out of crop size.\n"); 135116102edbSEunchul Kim goto err_property; 135216102edbSEunchul Kim } 135316102edbSEunchul Kim } 135416102edbSEunchul Kim } 135516102edbSEunchul Kim 135616102edbSEunchul Kim /* check for scale */ 135716102edbSEunchul Kim if ((i == EXYNOS_DRM_OPS_DST) && (pp->scale)) { 135816102edbSEunchul Kim if (swap) { 135916102edbSEunchul Kim if ((pos->h < pp->scale_min.hsize) || 136016102edbSEunchul Kim (sz->vsize > pp->scale_max.hsize) || 136116102edbSEunchul Kim (pos->w < pp->scale_min.vsize) || 136216102edbSEunchul Kim (sz->hsize > pp->scale_max.vsize)) { 136316102edbSEunchul Kim DRM_ERROR("out of scale size.\n"); 136416102edbSEunchul Kim goto err_property; 136516102edbSEunchul Kim } 136616102edbSEunchul Kim } else { 136716102edbSEunchul Kim if ((pos->w < pp->scale_min.hsize) || 136816102edbSEunchul Kim (sz->hsize > pp->scale_max.hsize) || 136916102edbSEunchul Kim (pos->h < pp->scale_min.vsize) || 137016102edbSEunchul Kim (sz->vsize > pp->scale_max.vsize)) { 137116102edbSEunchul Kim DRM_ERROR("out of scale size.\n"); 137216102edbSEunchul Kim goto err_property; 137316102edbSEunchul Kim } 137416102edbSEunchul Kim } 137516102edbSEunchul Kim } 137616102edbSEunchul Kim } 137716102edbSEunchul Kim 137816102edbSEunchul Kim return 0; 137916102edbSEunchul Kim 138016102edbSEunchul Kim err_property: 138116102edbSEunchul Kim for_each_ipp_ops(i) { 138216102edbSEunchul Kim if ((i == EXYNOS_DRM_OPS_SRC) && 138316102edbSEunchul Kim (property->cmd == IPP_CMD_WB)) 138416102edbSEunchul Kim continue; 138516102edbSEunchul Kim 138616102edbSEunchul Kim config = &property->config[i]; 138716102edbSEunchul Kim pos = &config->pos; 138816102edbSEunchul Kim sz = &config->sz; 138916102edbSEunchul Kim 139016102edbSEunchul Kim DRM_ERROR("[%s]f[%d]r[%d]pos[%d %d %d %d]sz[%d %d]\n", 139116102edbSEunchul Kim i ? "dst" : "src", config->flip, config->degree, 139216102edbSEunchul Kim pos->x, pos->y, pos->w, pos->h, 139316102edbSEunchul Kim sz->hsize, sz->vsize); 139416102edbSEunchul Kim } 139516102edbSEunchul Kim 139616102edbSEunchul Kim return -EINVAL; 139716102edbSEunchul Kim } 139816102edbSEunchul Kim 139916102edbSEunchul Kim static void fimc_clear_addr(struct fimc_context *ctx) 140016102edbSEunchul Kim { 140116102edbSEunchul Kim int i; 140216102edbSEunchul Kim 140316102edbSEunchul Kim for (i = 0; i < FIMC_MAX_SRC; i++) { 1404acd8afa8SAndrzej Hajda fimc_write(ctx, 0, EXYNOS_CIIYSA(i)); 1405acd8afa8SAndrzej Hajda fimc_write(ctx, 0, EXYNOS_CIICBSA(i)); 1406acd8afa8SAndrzej Hajda fimc_write(ctx, 0, EXYNOS_CIICRSA(i)); 140716102edbSEunchul Kim } 140816102edbSEunchul Kim 140916102edbSEunchul Kim for (i = 0; i < FIMC_MAX_DST; i++) { 1410acd8afa8SAndrzej Hajda fimc_write(ctx, 0, EXYNOS_CIOYSA(i)); 1411acd8afa8SAndrzej Hajda fimc_write(ctx, 0, EXYNOS_CIOCBSA(i)); 1412acd8afa8SAndrzej Hajda fimc_write(ctx, 0, EXYNOS_CIOCRSA(i)); 141316102edbSEunchul Kim } 141416102edbSEunchul Kim } 141516102edbSEunchul Kim 141616102edbSEunchul Kim static int fimc_ippdrv_reset(struct device *dev) 141716102edbSEunchul Kim { 141816102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 141916102edbSEunchul Kim 142016102edbSEunchul Kim /* reset h/w block */ 1421b5c0b552SJoongMock Shin fimc_sw_reset(ctx); 142216102edbSEunchul Kim 142316102edbSEunchul Kim /* reset scaler capability */ 142416102edbSEunchul Kim memset(&ctx->sc, 0x0, sizeof(ctx->sc)); 142516102edbSEunchul Kim 142616102edbSEunchul Kim fimc_clear_addr(ctx); 142716102edbSEunchul Kim 142816102edbSEunchul Kim return 0; 142916102edbSEunchul Kim } 143016102edbSEunchul Kim 143116102edbSEunchul Kim static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) 143216102edbSEunchul Kim { 143316102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 143416102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 14357259c3d6SEunchul Kim struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; 143616102edbSEunchul Kim struct drm_exynos_ipp_property *property; 143716102edbSEunchul Kim struct drm_exynos_ipp_config *config; 143816102edbSEunchul Kim struct drm_exynos_pos img_pos[EXYNOS_DRM_OPS_MAX]; 143916102edbSEunchul Kim struct drm_exynos_ipp_set_wb set_wb; 144016102edbSEunchul Kim int ret, i; 144116102edbSEunchul Kim u32 cfg0, cfg1; 144216102edbSEunchul Kim 1443cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("cmd[%d]\n", cmd); 144416102edbSEunchul Kim 144516102edbSEunchul Kim if (!c_node) { 144616102edbSEunchul Kim DRM_ERROR("failed to get c_node.\n"); 144716102edbSEunchul Kim return -EINVAL; 144816102edbSEunchul Kim } 144916102edbSEunchul Kim 145016102edbSEunchul Kim property = &c_node->property; 145116102edbSEunchul Kim 14528b4609cdSAndrzej Hajda fimc_mask_irq(ctx, true); 145316102edbSEunchul Kim 145416102edbSEunchul Kim for_each_ipp_ops(i) { 145516102edbSEunchul Kim config = &property->config[i]; 145616102edbSEunchul Kim img_pos[i] = config->pos; 145716102edbSEunchul Kim } 145816102edbSEunchul Kim 145916102edbSEunchul Kim ret = fimc_set_prescaler(ctx, &ctx->sc, 146016102edbSEunchul Kim &img_pos[EXYNOS_DRM_OPS_SRC], 146116102edbSEunchul Kim &img_pos[EXYNOS_DRM_OPS_DST]); 146216102edbSEunchul Kim if (ret) { 146316102edbSEunchul Kim dev_err(dev, "failed to set precalser.\n"); 146416102edbSEunchul Kim return ret; 146516102edbSEunchul Kim } 146616102edbSEunchul Kim 146716102edbSEunchul Kim /* If set ture, we can save jpeg about screen */ 146816102edbSEunchul Kim fimc_handle_jpeg(ctx, false); 146916102edbSEunchul Kim fimc_set_scaler(ctx, &ctx->sc); 147016102edbSEunchul Kim fimc_set_polarity(ctx, &ctx->pol); 147116102edbSEunchul Kim 147216102edbSEunchul Kim switch (cmd) { 147316102edbSEunchul Kim case IPP_CMD_M2M: 147416102edbSEunchul Kim fimc_set_type_ctrl(ctx, FIMC_WB_NONE); 147516102edbSEunchul Kim fimc_handle_lastend(ctx, false); 147616102edbSEunchul Kim 147716102edbSEunchul Kim /* setup dma */ 1478acd8afa8SAndrzej Hajda cfg0 = fimc_read(ctx, EXYNOS_MSCTRL); 147916102edbSEunchul Kim cfg0 &= ~EXYNOS_MSCTRL_INPUT_MASK; 148016102edbSEunchul Kim cfg0 |= EXYNOS_MSCTRL_INPUT_MEMORY; 1481acd8afa8SAndrzej Hajda fimc_write(ctx, cfg0, EXYNOS_MSCTRL); 148216102edbSEunchul Kim break; 148316102edbSEunchul Kim case IPP_CMD_WB: 148416102edbSEunchul Kim fimc_set_type_ctrl(ctx, FIMC_WB_A); 148516102edbSEunchul Kim fimc_handle_lastend(ctx, true); 148616102edbSEunchul Kim 148716102edbSEunchul Kim /* setup FIMD */ 14885186fc5eSSylwester Nawrocki ret = fimc_set_camblk_fimd0_wb(ctx); 14895186fc5eSSylwester Nawrocki if (ret < 0) { 14905186fc5eSSylwester Nawrocki dev_err(dev, "camblk setup failed.\n"); 14915186fc5eSSylwester Nawrocki return ret; 14925186fc5eSSylwester Nawrocki } 149316102edbSEunchul Kim 149416102edbSEunchul Kim set_wb.enable = 1; 149516102edbSEunchul Kim set_wb.refresh = property->refresh_rate; 149616102edbSEunchul Kim exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); 149716102edbSEunchul Kim break; 149816102edbSEunchul Kim case IPP_CMD_OUTPUT: 149916102edbSEunchul Kim default: 150016102edbSEunchul Kim ret = -EINVAL; 150116102edbSEunchul Kim dev_err(dev, "invalid operations.\n"); 150216102edbSEunchul Kim return ret; 150316102edbSEunchul Kim } 150416102edbSEunchul Kim 150516102edbSEunchul Kim /* Reset status */ 1506acd8afa8SAndrzej Hajda fimc_write(ctx, 0x0, EXYNOS_CISTATUS); 150716102edbSEunchul Kim 1508acd8afa8SAndrzej Hajda cfg0 = fimc_read(ctx, EXYNOS_CIIMGCPT); 150916102edbSEunchul Kim cfg0 &= ~EXYNOS_CIIMGCPT_IMGCPTEN_SC; 151016102edbSEunchul Kim cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN_SC; 151116102edbSEunchul Kim 151216102edbSEunchul Kim /* Scaler */ 1513acd8afa8SAndrzej Hajda cfg1 = fimc_read(ctx, EXYNOS_CISCCTRL); 151416102edbSEunchul Kim cfg1 &= ~EXYNOS_CISCCTRL_SCAN_MASK; 151516102edbSEunchul Kim cfg1 |= (EXYNOS_CISCCTRL_PROGRESSIVE | 151616102edbSEunchul Kim EXYNOS_CISCCTRL_SCALERSTART); 151716102edbSEunchul Kim 1518acd8afa8SAndrzej Hajda fimc_write(ctx, cfg1, EXYNOS_CISCCTRL); 151916102edbSEunchul Kim 152016102edbSEunchul Kim /* Enable image capture*/ 152116102edbSEunchul Kim cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN; 1522acd8afa8SAndrzej Hajda fimc_write(ctx, cfg0, EXYNOS_CIIMGCPT); 152316102edbSEunchul Kim 152416102edbSEunchul Kim /* Disable frame end irq */ 1525acd8afa8SAndrzej Hajda fimc_clear_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_END_DISABLE); 152616102edbSEunchul Kim 1527acd8afa8SAndrzej Hajda fimc_clear_bits(ctx, EXYNOS_CIOCTRL, EXYNOS_CIOCTRL_WEAVE_MASK); 152816102edbSEunchul Kim 1529c7b3014bSAndrzej Hajda if (cmd == IPP_CMD_M2M) 1530acd8afa8SAndrzej Hajda fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID); 153116102edbSEunchul Kim 153216102edbSEunchul Kim return 0; 153316102edbSEunchul Kim } 153416102edbSEunchul Kim 153516102edbSEunchul Kim static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) 153616102edbSEunchul Kim { 153716102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 153816102edbSEunchul Kim struct drm_exynos_ipp_set_wb set_wb = {0, 0}; 153916102edbSEunchul Kim u32 cfg; 154016102edbSEunchul Kim 1541cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("cmd[%d]\n", cmd); 154216102edbSEunchul Kim 154316102edbSEunchul Kim switch (cmd) { 154416102edbSEunchul Kim case IPP_CMD_M2M: 154516102edbSEunchul Kim /* Source clear */ 1546acd8afa8SAndrzej Hajda cfg = fimc_read(ctx, EXYNOS_MSCTRL); 154716102edbSEunchul Kim cfg &= ~EXYNOS_MSCTRL_INPUT_MASK; 154816102edbSEunchul Kim cfg &= ~EXYNOS_MSCTRL_ENVID; 1549acd8afa8SAndrzej Hajda fimc_write(ctx, cfg, EXYNOS_MSCTRL); 155016102edbSEunchul Kim break; 155116102edbSEunchul Kim case IPP_CMD_WB: 155216102edbSEunchul Kim exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); 155316102edbSEunchul Kim break; 155416102edbSEunchul Kim case IPP_CMD_OUTPUT: 155516102edbSEunchul Kim default: 155616102edbSEunchul Kim dev_err(dev, "invalid operations.\n"); 155716102edbSEunchul Kim break; 155816102edbSEunchul Kim } 155916102edbSEunchul Kim 15608b4609cdSAndrzej Hajda fimc_mask_irq(ctx, false); 156116102edbSEunchul Kim 156216102edbSEunchul Kim /* reset sequence */ 1563acd8afa8SAndrzej Hajda fimc_write(ctx, 0x0, EXYNOS_CIFCNTSEQ); 156416102edbSEunchul Kim 156516102edbSEunchul Kim /* Scaler disable */ 1566acd8afa8SAndrzej Hajda fimc_clear_bits(ctx, EXYNOS_CISCCTRL, EXYNOS_CISCCTRL_SCALERSTART); 156716102edbSEunchul Kim 156816102edbSEunchul Kim /* Disable image capture */ 1569acd8afa8SAndrzej Hajda fimc_clear_bits(ctx, EXYNOS_CIIMGCPT, 1570acd8afa8SAndrzej Hajda EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN); 157116102edbSEunchul Kim 157216102edbSEunchul Kim /* Enable frame end irq */ 1573acd8afa8SAndrzej Hajda fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_END_DISABLE); 157416102edbSEunchul Kim } 157516102edbSEunchul Kim 1576e5f86839SSylwester Nawrocki static void fimc_put_clocks(struct fimc_context *ctx) 1577e5f86839SSylwester Nawrocki { 1578e5f86839SSylwester Nawrocki int i; 1579e5f86839SSylwester Nawrocki 1580e5f86839SSylwester Nawrocki for (i = 0; i < FIMC_CLKS_MAX; i++) { 1581e5f86839SSylwester Nawrocki if (IS_ERR(ctx->clocks[i])) 1582e5f86839SSylwester Nawrocki continue; 1583e5f86839SSylwester Nawrocki clk_put(ctx->clocks[i]); 1584e5f86839SSylwester Nawrocki ctx->clocks[i] = ERR_PTR(-EINVAL); 1585e5f86839SSylwester Nawrocki } 1586e5f86839SSylwester Nawrocki } 1587e5f86839SSylwester Nawrocki 1588e5f86839SSylwester Nawrocki static int fimc_setup_clocks(struct fimc_context *ctx) 1589e5f86839SSylwester Nawrocki { 1590e5f86839SSylwester Nawrocki struct device *fimc_dev = ctx->ippdrv.dev; 1591e5f86839SSylwester Nawrocki struct device *dev; 1592e5f86839SSylwester Nawrocki int ret, i; 1593e5f86839SSylwester Nawrocki 1594e5f86839SSylwester Nawrocki for (i = 0; i < FIMC_CLKS_MAX; i++) 1595e5f86839SSylwester Nawrocki ctx->clocks[i] = ERR_PTR(-EINVAL); 1596e5f86839SSylwester Nawrocki 1597e5f86839SSylwester Nawrocki for (i = 0; i < FIMC_CLKS_MAX; i++) { 1598e5f86839SSylwester Nawrocki if (i == FIMC_CLK_WB_A || i == FIMC_CLK_WB_B) 1599e5f86839SSylwester Nawrocki dev = fimc_dev->parent; 1600e5f86839SSylwester Nawrocki else 1601e5f86839SSylwester Nawrocki dev = fimc_dev; 1602e5f86839SSylwester Nawrocki 1603e5f86839SSylwester Nawrocki ctx->clocks[i] = clk_get(dev, fimc_clock_names[i]); 1604e5f86839SSylwester Nawrocki if (IS_ERR(ctx->clocks[i])) { 1605e5f86839SSylwester Nawrocki if (i >= FIMC_CLK_MUX) 1606e5f86839SSylwester Nawrocki break; 1607e5f86839SSylwester Nawrocki ret = PTR_ERR(ctx->clocks[i]); 1608e5f86839SSylwester Nawrocki dev_err(fimc_dev, "failed to get clock: %s\n", 1609e5f86839SSylwester Nawrocki fimc_clock_names[i]); 1610e5f86839SSylwester Nawrocki goto e_clk_free; 1611e5f86839SSylwester Nawrocki } 1612e5f86839SSylwester Nawrocki } 1613e5f86839SSylwester Nawrocki 1614e5f86839SSylwester Nawrocki /* Optional FIMC LCLK parent clock setting */ 1615e5f86839SSylwester Nawrocki if (!IS_ERR(ctx->clocks[FIMC_CLK_PARENT])) { 1616e5f86839SSylwester Nawrocki ret = clk_set_parent(ctx->clocks[FIMC_CLK_MUX], 1617e5f86839SSylwester Nawrocki ctx->clocks[FIMC_CLK_PARENT]); 1618e5f86839SSylwester Nawrocki if (ret < 0) { 1619e5f86839SSylwester Nawrocki dev_err(fimc_dev, "failed to set parent.\n"); 1620e5f86839SSylwester Nawrocki goto e_clk_free; 1621e5f86839SSylwester Nawrocki } 1622e5f86839SSylwester Nawrocki } 1623e5f86839SSylwester Nawrocki 1624e5f86839SSylwester Nawrocki ret = clk_set_rate(ctx->clocks[FIMC_CLK_LCLK], ctx->clk_frequency); 1625e5f86839SSylwester Nawrocki if (ret < 0) 1626e5f86839SSylwester Nawrocki goto e_clk_free; 1627e5f86839SSylwester Nawrocki 1628e5f86839SSylwester Nawrocki ret = clk_prepare_enable(ctx->clocks[FIMC_CLK_LCLK]); 1629e5f86839SSylwester Nawrocki if (!ret) 1630e5f86839SSylwester Nawrocki return ret; 1631e5f86839SSylwester Nawrocki e_clk_free: 1632e5f86839SSylwester Nawrocki fimc_put_clocks(ctx); 1633e5f86839SSylwester Nawrocki return ret; 1634e5f86839SSylwester Nawrocki } 1635e5f86839SSylwester Nawrocki 16365186fc5eSSylwester Nawrocki static int fimc_parse_dt(struct fimc_context *ctx) 16375186fc5eSSylwester Nawrocki { 16385186fc5eSSylwester Nawrocki struct device_node *node = ctx->ippdrv.dev->of_node; 16395186fc5eSSylwester Nawrocki 16405186fc5eSSylwester Nawrocki /* Handle only devices that support the LCD Writeback data path */ 16415186fc5eSSylwester Nawrocki if (!of_property_read_bool(node, "samsung,lcd-wb")) 16425186fc5eSSylwester Nawrocki return -ENODEV; 16435186fc5eSSylwester Nawrocki 16445186fc5eSSylwester Nawrocki if (of_property_read_u32(node, "clock-frequency", 16455186fc5eSSylwester Nawrocki &ctx->clk_frequency)) 16465186fc5eSSylwester Nawrocki ctx->clk_frequency = FIMC_DEFAULT_LCLK_FREQUENCY; 16475186fc5eSSylwester Nawrocki 16485186fc5eSSylwester Nawrocki ctx->id = of_alias_get_id(node, "fimc"); 16495186fc5eSSylwester Nawrocki 16505186fc5eSSylwester Nawrocki if (ctx->id < 0) { 16515186fc5eSSylwester Nawrocki dev_err(ctx->ippdrv.dev, "failed to get node alias id.\n"); 16525186fc5eSSylwester Nawrocki return -EINVAL; 16535186fc5eSSylwester Nawrocki } 16545186fc5eSSylwester Nawrocki 16555186fc5eSSylwester Nawrocki return 0; 16565186fc5eSSylwester Nawrocki } 16575186fc5eSSylwester Nawrocki 165856550d94SGreg Kroah-Hartman static int fimc_probe(struct platform_device *pdev) 165916102edbSEunchul Kim { 166016102edbSEunchul Kim struct device *dev = &pdev->dev; 166116102edbSEunchul Kim struct fimc_context *ctx; 166216102edbSEunchul Kim struct resource *res; 166316102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv; 166416102edbSEunchul Kim int ret; 166516102edbSEunchul Kim 16665186fc5eSSylwester Nawrocki if (!dev->of_node) { 16675186fc5eSSylwester Nawrocki dev_err(dev, "device tree node not found.\n"); 16685186fc5eSSylwester Nawrocki return -ENODEV; 166916102edbSEunchul Kim } 167016102edbSEunchul Kim 167116102edbSEunchul Kim ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 167216102edbSEunchul Kim if (!ctx) 167316102edbSEunchul Kim return -ENOMEM; 167416102edbSEunchul Kim 16755186fc5eSSylwester Nawrocki ctx->ippdrv.dev = dev; 16765186fc5eSSylwester Nawrocki 16775186fc5eSSylwester Nawrocki ret = fimc_parse_dt(ctx); 16785186fc5eSSylwester Nawrocki if (ret < 0) 16795186fc5eSSylwester Nawrocki return ret; 16805186fc5eSSylwester Nawrocki 16815186fc5eSSylwester Nawrocki ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, 16825186fc5eSSylwester Nawrocki "samsung,sysreg"); 16835186fc5eSSylwester Nawrocki if (IS_ERR(ctx->sysreg)) { 16845186fc5eSSylwester Nawrocki dev_err(dev, "syscon regmap lookup failed.\n"); 16855186fc5eSSylwester Nawrocki return PTR_ERR(ctx->sysreg); 16865186fc5eSSylwester Nawrocki } 16875186fc5eSSylwester Nawrocki 168816102edbSEunchul Kim /* resource memory */ 168916102edbSEunchul Kim ctx->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1690d4ed6025SThierry Reding ctx->regs = devm_ioremap_resource(dev, ctx->regs_res); 1691d4ed6025SThierry Reding if (IS_ERR(ctx->regs)) 1692d4ed6025SThierry Reding return PTR_ERR(ctx->regs); 169316102edbSEunchul Kim 169416102edbSEunchul Kim /* resource irq */ 169516102edbSEunchul Kim res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 169616102edbSEunchul Kim if (!res) { 169716102edbSEunchul Kim dev_err(dev, "failed to request irq resource.\n"); 169815b3263eSSachin Kamat return -ENOENT; 169916102edbSEunchul Kim } 170016102edbSEunchul Kim 170116102edbSEunchul Kim ctx->irq = res->start; 1702dcb9a7c7SSeung-Woo Kim ret = devm_request_threaded_irq(dev, ctx->irq, NULL, fimc_irq_handler, 170316102edbSEunchul Kim IRQF_ONESHOT, "drm_fimc", ctx); 170416102edbSEunchul Kim if (ret < 0) { 170516102edbSEunchul Kim dev_err(dev, "failed to request irq.\n"); 170615b3263eSSachin Kamat return ret; 170716102edbSEunchul Kim } 170816102edbSEunchul Kim 1709e5f86839SSylwester Nawrocki ret = fimc_setup_clocks(ctx); 1710e5f86839SSylwester Nawrocki if (ret < 0) 1711dcb9a7c7SSeung-Woo Kim return ret; 171216102edbSEunchul Kim 171316102edbSEunchul Kim ippdrv = &ctx->ippdrv; 171416102edbSEunchul Kim ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &fimc_src_ops; 171516102edbSEunchul Kim ippdrv->ops[EXYNOS_DRM_OPS_DST] = &fimc_dst_ops; 171616102edbSEunchul Kim ippdrv->check_property = fimc_ippdrv_check_property; 171716102edbSEunchul Kim ippdrv->reset = fimc_ippdrv_reset; 171816102edbSEunchul Kim ippdrv->start = fimc_ippdrv_start; 171916102edbSEunchul Kim ippdrv->stop = fimc_ippdrv_stop; 172016102edbSEunchul Kim ret = fimc_init_prop_list(ippdrv); 172116102edbSEunchul Kim if (ret < 0) { 172216102edbSEunchul Kim dev_err(dev, "failed to init property list.\n"); 1723e5f86839SSylwester Nawrocki goto err_put_clk; 172416102edbSEunchul Kim } 172516102edbSEunchul Kim 1726cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("id[%d]ippdrv[0x%x]\n", ctx->id, (int)ippdrv); 172716102edbSEunchul Kim 172872d465aaSAndrzej Hajda spin_lock_init(&ctx->lock); 172916102edbSEunchul Kim platform_set_drvdata(pdev, ctx); 173016102edbSEunchul Kim 173116102edbSEunchul Kim pm_runtime_enable(dev); 173216102edbSEunchul Kim 173316102edbSEunchul Kim ret = exynos_drm_ippdrv_register(ippdrv); 173416102edbSEunchul Kim if (ret < 0) { 173516102edbSEunchul Kim dev_err(dev, "failed to register drm fimc device.\n"); 1736e5f86839SSylwester Nawrocki goto err_pm_dis; 173716102edbSEunchul Kim } 173816102edbSEunchul Kim 1739d873ab99SSeung-Woo Kim dev_info(dev, "drm fimc registered successfully.\n"); 174016102edbSEunchul Kim 174116102edbSEunchul Kim return 0; 174216102edbSEunchul Kim 1743e5f86839SSylwester Nawrocki err_pm_dis: 174416102edbSEunchul Kim pm_runtime_disable(dev); 1745e5f86839SSylwester Nawrocki err_put_clk: 1746e5f86839SSylwester Nawrocki fimc_put_clocks(ctx); 174787acdde5SSachin Kamat 174816102edbSEunchul Kim return ret; 174916102edbSEunchul Kim } 175016102edbSEunchul Kim 175156550d94SGreg Kroah-Hartman static int fimc_remove(struct platform_device *pdev) 175216102edbSEunchul Kim { 175316102edbSEunchul Kim struct device *dev = &pdev->dev; 175416102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 175516102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 175616102edbSEunchul Kim 175716102edbSEunchul Kim exynos_drm_ippdrv_unregister(ippdrv); 175816102edbSEunchul Kim 1759e5f86839SSylwester Nawrocki fimc_put_clocks(ctx); 176016102edbSEunchul Kim pm_runtime_set_suspended(dev); 176116102edbSEunchul Kim pm_runtime_disable(dev); 176216102edbSEunchul Kim 176316102edbSEunchul Kim return 0; 176416102edbSEunchul Kim } 176516102edbSEunchul Kim 1766641a2fefSThierry Reding #ifdef CONFIG_PM 1767641a2fefSThierry Reding static int fimc_clk_ctrl(struct fimc_context *ctx, bool enable) 1768641a2fefSThierry Reding { 1769641a2fefSThierry Reding DRM_DEBUG_KMS("enable[%d]\n", enable); 1770641a2fefSThierry Reding 1771641a2fefSThierry Reding if (enable) { 1772641a2fefSThierry Reding clk_prepare_enable(ctx->clocks[FIMC_CLK_GATE]); 1773641a2fefSThierry Reding clk_prepare_enable(ctx->clocks[FIMC_CLK_WB_A]); 1774641a2fefSThierry Reding ctx->suspended = false; 1775641a2fefSThierry Reding } else { 1776641a2fefSThierry Reding clk_disable_unprepare(ctx->clocks[FIMC_CLK_GATE]); 1777641a2fefSThierry Reding clk_disable_unprepare(ctx->clocks[FIMC_CLK_WB_A]); 1778641a2fefSThierry Reding ctx->suspended = true; 1779641a2fefSThierry Reding } 1780641a2fefSThierry Reding 1781641a2fefSThierry Reding return 0; 1782641a2fefSThierry Reding } 1783641a2fefSThierry Reding 178416102edbSEunchul Kim #ifdef CONFIG_PM_SLEEP 178516102edbSEunchul Kim static int fimc_suspend(struct device *dev) 178616102edbSEunchul Kim { 178716102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 178816102edbSEunchul Kim 1789cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("id[%d]\n", ctx->id); 179016102edbSEunchul Kim 179116102edbSEunchul Kim if (pm_runtime_suspended(dev)) 179216102edbSEunchul Kim return 0; 179316102edbSEunchul Kim 179416102edbSEunchul Kim return fimc_clk_ctrl(ctx, false); 179516102edbSEunchul Kim } 179616102edbSEunchul Kim 179716102edbSEunchul Kim static int fimc_resume(struct device *dev) 179816102edbSEunchul Kim { 179916102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 180016102edbSEunchul Kim 1801cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("id[%d]\n", ctx->id); 180216102edbSEunchul Kim 180316102edbSEunchul Kim if (!pm_runtime_suspended(dev)) 180416102edbSEunchul Kim return fimc_clk_ctrl(ctx, true); 180516102edbSEunchul Kim 180616102edbSEunchul Kim return 0; 180716102edbSEunchul Kim } 180816102edbSEunchul Kim #endif 180916102edbSEunchul Kim 181016102edbSEunchul Kim static int fimc_runtime_suspend(struct device *dev) 181116102edbSEunchul Kim { 181216102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 181316102edbSEunchul Kim 1814cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("id[%d]\n", ctx->id); 181516102edbSEunchul Kim 181616102edbSEunchul Kim return fimc_clk_ctrl(ctx, false); 181716102edbSEunchul Kim } 181816102edbSEunchul Kim 181916102edbSEunchul Kim static int fimc_runtime_resume(struct device *dev) 182016102edbSEunchul Kim { 182116102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 182216102edbSEunchul Kim 1823cbc4c33dSYoungJun Cho DRM_DEBUG_KMS("id[%d]\n", ctx->id); 182416102edbSEunchul Kim 182516102edbSEunchul Kim return fimc_clk_ctrl(ctx, true); 182616102edbSEunchul Kim } 182716102edbSEunchul Kim #endif 182816102edbSEunchul Kim 182916102edbSEunchul Kim static const struct dev_pm_ops fimc_pm_ops = { 183016102edbSEunchul Kim SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume) 183116102edbSEunchul Kim SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL) 183216102edbSEunchul Kim }; 183316102edbSEunchul Kim 18345186fc5eSSylwester Nawrocki static const struct of_device_id fimc_of_match[] = { 18355186fc5eSSylwester Nawrocki { .compatible = "samsung,exynos4210-fimc" }, 18365186fc5eSSylwester Nawrocki { .compatible = "samsung,exynos4212-fimc" }, 18375186fc5eSSylwester Nawrocki { }, 18385186fc5eSSylwester Nawrocki }; 183939b58a39SSjoerd Simons MODULE_DEVICE_TABLE(of, fimc_of_match); 18405186fc5eSSylwester Nawrocki 184116102edbSEunchul Kim struct platform_driver fimc_driver = { 184216102edbSEunchul Kim .probe = fimc_probe, 184356550d94SGreg Kroah-Hartman .remove = fimc_remove, 184416102edbSEunchul Kim .driver = { 18455186fc5eSSylwester Nawrocki .of_match_table = fimc_of_match, 184616102edbSEunchul Kim .name = "exynos-drm-fimc", 184716102edbSEunchul Kim .owner = THIS_MODULE, 184816102edbSEunchul Kim .pm = &fimc_pm_ops, 184916102edbSEunchul Kim }, 185016102edbSEunchul Kim }; 185116102edbSEunchul Kim 1852