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/module.h> 1616102edbSEunchul Kim #include <linux/platform_device.h> 1716102edbSEunchul Kim #include <linux/clk.h> 1816102edbSEunchul Kim #include <linux/pm_runtime.h> 1916102edbSEunchul Kim #include <plat/map-base.h> 2016102edbSEunchul Kim 2116102edbSEunchul Kim #include <drm/drmP.h> 2216102edbSEunchul Kim #include <drm/exynos_drm.h> 2316102edbSEunchul Kim #include "regs-fimc.h" 2416102edbSEunchul Kim #include "exynos_drm_ipp.h" 2516102edbSEunchul Kim #include "exynos_drm_fimc.h" 2616102edbSEunchul Kim 2716102edbSEunchul Kim /* 286fe891f6SEunchul Kim * FIMC stands for Fully Interactive Mobile Camera and 2916102edbSEunchul Kim * supports image scaler/rotator and input/output DMA operations. 3016102edbSEunchul Kim * input DMA reads image data from the memory. 3116102edbSEunchul Kim * output DMA writes image data to memory. 3216102edbSEunchul Kim * FIMC supports image rotation and image effect functions. 3316102edbSEunchul Kim * 3416102edbSEunchul Kim * M2M operation : supports crop/scale/rotation/csc so on. 3516102edbSEunchul Kim * Memory ----> FIMC H/W ----> Memory. 3616102edbSEunchul Kim * Writeback operation : supports cloned screen with FIMD. 3716102edbSEunchul Kim * FIMD ----> FIMC H/W ----> Memory. 3816102edbSEunchul Kim * Output operation : supports direct display using local path. 3916102edbSEunchul Kim * Memory ----> FIMC H/W ----> FIMD. 4016102edbSEunchul Kim */ 4116102edbSEunchul Kim 4216102edbSEunchul Kim /* 4316102edbSEunchul Kim * TODO 4416102edbSEunchul Kim * 1. check suspend/resume api if needed. 4516102edbSEunchul Kim * 2. need to check use case platform_device_id. 4616102edbSEunchul Kim * 3. check src/dst size with, height. 4716102edbSEunchul Kim * 4. added check_prepare api for right register. 4816102edbSEunchul Kim * 5. need to add supported list in prop_list. 4916102edbSEunchul Kim * 6. check prescaler/scaler optimization. 5016102edbSEunchul Kim */ 5116102edbSEunchul Kim 5216102edbSEunchul Kim #define FIMC_MAX_DEVS 4 5316102edbSEunchul Kim #define FIMC_MAX_SRC 2 5416102edbSEunchul Kim #define FIMC_MAX_DST 32 5516102edbSEunchul Kim #define FIMC_SHFACTOR 10 5616102edbSEunchul Kim #define FIMC_BUF_STOP 1 5716102edbSEunchul Kim #define FIMC_BUF_START 2 5816102edbSEunchul Kim #define FIMC_REG_SZ 32 5916102edbSEunchul Kim #define FIMC_WIDTH_ITU_709 1280 6016102edbSEunchul Kim #define FIMC_REFRESH_MAX 60 6116102edbSEunchul Kim #define FIMC_REFRESH_MIN 12 6216102edbSEunchul Kim #define FIMC_CROP_MAX 8192 6316102edbSEunchul Kim #define FIMC_CROP_MIN 32 6416102edbSEunchul Kim #define FIMC_SCALE_MAX 4224 6516102edbSEunchul Kim #define FIMC_SCALE_MIN 32 6616102edbSEunchul Kim 6716102edbSEunchul Kim #define get_fimc_context(dev) platform_get_drvdata(to_platform_device(dev)) 6816102edbSEunchul Kim #define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\ 6916102edbSEunchul Kim struct fimc_context, ippdrv); 7016102edbSEunchul Kim #define fimc_read(offset) readl(ctx->regs + (offset)) 7116102edbSEunchul Kim #define fimc_write(cfg, offset) writel(cfg, ctx->regs + (offset)) 7216102edbSEunchul Kim 7316102edbSEunchul Kim enum fimc_wb { 7416102edbSEunchul Kim FIMC_WB_NONE, 7516102edbSEunchul Kim FIMC_WB_A, 7616102edbSEunchul Kim FIMC_WB_B, 7716102edbSEunchul Kim }; 7816102edbSEunchul Kim 7916102edbSEunchul Kim /* 8016102edbSEunchul Kim * A structure of scaler. 8116102edbSEunchul Kim * 8216102edbSEunchul Kim * @range: narrow, wide. 8316102edbSEunchul Kim * @bypass: unused scaler path. 8416102edbSEunchul Kim * @up_h: horizontal scale up. 8516102edbSEunchul Kim * @up_v: vertical scale up. 8616102edbSEunchul Kim * @hratio: horizontal ratio. 8716102edbSEunchul Kim * @vratio: vertical ratio. 8816102edbSEunchul Kim */ 8916102edbSEunchul Kim struct fimc_scaler { 9016102edbSEunchul Kim bool range; 9116102edbSEunchul Kim bool bypass; 9216102edbSEunchul Kim bool up_h; 9316102edbSEunchul Kim bool up_v; 9416102edbSEunchul Kim u32 hratio; 9516102edbSEunchul Kim u32 vratio; 9616102edbSEunchul Kim }; 9716102edbSEunchul Kim 9816102edbSEunchul Kim /* 9916102edbSEunchul Kim * A structure of scaler capability. 10016102edbSEunchul Kim * 10116102edbSEunchul Kim * find user manual table 43-1. 10216102edbSEunchul Kim * @in_hori: scaler input horizontal size. 10316102edbSEunchul Kim * @bypass: scaler bypass mode. 10416102edbSEunchul Kim * @dst_h_wo_rot: target horizontal size without output rotation. 10516102edbSEunchul Kim * @dst_h_rot: target horizontal size with output rotation. 10616102edbSEunchul Kim * @rl_w_wo_rot: real width without input rotation. 10716102edbSEunchul Kim * @rl_h_rot: real height without output rotation. 10816102edbSEunchul Kim */ 10916102edbSEunchul Kim struct fimc_capability { 11016102edbSEunchul Kim /* scaler */ 11116102edbSEunchul Kim u32 in_hori; 11216102edbSEunchul Kim u32 bypass; 11316102edbSEunchul Kim /* output rotator */ 11416102edbSEunchul Kim u32 dst_h_wo_rot; 11516102edbSEunchul Kim u32 dst_h_rot; 11616102edbSEunchul Kim /* input rotator */ 11716102edbSEunchul Kim u32 rl_w_wo_rot; 11816102edbSEunchul Kim u32 rl_h_rot; 11916102edbSEunchul Kim }; 12016102edbSEunchul Kim 12116102edbSEunchul Kim /* 12216102edbSEunchul Kim * A structure of fimc driver data. 12316102edbSEunchul Kim * 12416102edbSEunchul Kim * @parent_clk: name of parent clock. 12516102edbSEunchul Kim */ 12616102edbSEunchul Kim struct fimc_driverdata { 12716102edbSEunchul Kim char *parent_clk; 12816102edbSEunchul Kim }; 12916102edbSEunchul Kim 13016102edbSEunchul Kim /* 13116102edbSEunchul Kim * A structure of fimc context. 13216102edbSEunchul Kim * 13316102edbSEunchul Kim * @ippdrv: prepare initialization using ippdrv. 13416102edbSEunchul Kim * @regs_res: register resources. 13516102edbSEunchul Kim * @regs: memory mapped io registers. 13616102edbSEunchul Kim * @lock: locking of operations. 13716102edbSEunchul Kim * @sclk_fimc_clk: fimc source clock. 13816102edbSEunchul Kim * @fimc_clk: fimc clock. 13916102edbSEunchul Kim * @wb_clk: writeback a clock. 14016102edbSEunchul Kim * @wb_b_clk: writeback b clock. 14116102edbSEunchul Kim * @sc: scaler infomations. 14216102edbSEunchul Kim * @odr: ordering of YUV. 14316102edbSEunchul Kim * @ver: fimc version. 14416102edbSEunchul Kim * @pol: porarity of writeback. 14516102edbSEunchul Kim * @id: fimc id. 14616102edbSEunchul Kim * @irq: irq number. 14716102edbSEunchul Kim * @suspended: qos operations. 14816102edbSEunchul Kim */ 14916102edbSEunchul Kim struct fimc_context { 15016102edbSEunchul Kim struct exynos_drm_ippdrv ippdrv; 15116102edbSEunchul Kim struct resource *regs_res; 15216102edbSEunchul Kim void __iomem *regs; 15316102edbSEunchul Kim struct mutex lock; 15416102edbSEunchul Kim struct clk *sclk_fimc_clk; 15516102edbSEunchul Kim struct clk *fimc_clk; 15616102edbSEunchul Kim struct clk *wb_clk; 15716102edbSEunchul Kim struct clk *wb_b_clk; 15816102edbSEunchul Kim struct fimc_scaler sc; 15916102edbSEunchul Kim struct fimc_driverdata *ddata; 16016102edbSEunchul Kim struct exynos_drm_ipp_pol pol; 16116102edbSEunchul Kim int id; 16216102edbSEunchul Kim int irq; 16316102edbSEunchul Kim bool suspended; 16416102edbSEunchul Kim }; 16516102edbSEunchul Kim 166b5c0b552SJoongMock Shin static void fimc_sw_reset(struct fimc_context *ctx) 16716102edbSEunchul Kim { 16816102edbSEunchul Kim u32 cfg; 16916102edbSEunchul Kim 170b5c0b552SJoongMock Shin DRM_DEBUG_KMS("%s\n", __func__); 17116102edbSEunchul Kim 172e39d5ce1SJinyoung Jeon /* stop dma operation */ 173e39d5ce1SJinyoung Jeon cfg = fimc_read(EXYNOS_CISTATUS); 174e39d5ce1SJinyoung Jeon if (EXYNOS_CISTATUS_GET_ENVID_STATUS(cfg)) { 175e39d5ce1SJinyoung Jeon cfg = fimc_read(EXYNOS_MSCTRL); 176e39d5ce1SJinyoung Jeon cfg &= ~EXYNOS_MSCTRL_ENVID; 177e39d5ce1SJinyoung Jeon fimc_write(cfg, EXYNOS_MSCTRL); 178e39d5ce1SJinyoung Jeon } 179e39d5ce1SJinyoung Jeon 18016102edbSEunchul Kim cfg = fimc_read(EXYNOS_CISRCFMT); 18116102edbSEunchul Kim cfg |= EXYNOS_CISRCFMT_ITU601_8BIT; 18216102edbSEunchul Kim fimc_write(cfg, EXYNOS_CISRCFMT); 18316102edbSEunchul Kim 184e39d5ce1SJinyoung Jeon /* disable image capture */ 185e39d5ce1SJinyoung Jeon cfg = fimc_read(EXYNOS_CIIMGCPT); 186e39d5ce1SJinyoung Jeon cfg &= ~(EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN); 187e39d5ce1SJinyoung Jeon fimc_write(cfg, EXYNOS_CIIMGCPT); 188e39d5ce1SJinyoung Jeon 18916102edbSEunchul Kim /* s/w reset */ 19016102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIGCTRL); 19116102edbSEunchul Kim cfg |= (EXYNOS_CIGCTRL_SWRST); 19216102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIGCTRL); 19316102edbSEunchul Kim 19416102edbSEunchul Kim /* s/w reset complete */ 19516102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIGCTRL); 19616102edbSEunchul Kim cfg &= ~EXYNOS_CIGCTRL_SWRST; 19716102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIGCTRL); 19816102edbSEunchul Kim 19916102edbSEunchul Kim /* reset sequence */ 20016102edbSEunchul Kim fimc_write(0x0, EXYNOS_CIFCNTSEQ); 20116102edbSEunchul Kim } 20216102edbSEunchul Kim 20316102edbSEunchul Kim static void fimc_set_camblk_fimd0_wb(struct fimc_context *ctx) 20416102edbSEunchul Kim { 20516102edbSEunchul Kim u32 camblk_cfg; 20616102edbSEunchul Kim 20716102edbSEunchul Kim DRM_DEBUG_KMS("%s\n", __func__); 20816102edbSEunchul Kim 20916102edbSEunchul Kim camblk_cfg = readl(SYSREG_CAMERA_BLK); 21016102edbSEunchul Kim camblk_cfg &= ~(SYSREG_FIMD0WB_DEST_MASK); 21116102edbSEunchul Kim camblk_cfg |= ctx->id << (SYSREG_FIMD0WB_DEST_SHIFT); 21216102edbSEunchul Kim 21316102edbSEunchul Kim writel(camblk_cfg, SYSREG_CAMERA_BLK); 21416102edbSEunchul Kim } 21516102edbSEunchul Kim 21616102edbSEunchul Kim static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb) 21716102edbSEunchul Kim { 21816102edbSEunchul Kim u32 cfg; 21916102edbSEunchul Kim 22016102edbSEunchul Kim DRM_DEBUG_KMS("%s:wb[%d]\n", __func__, wb); 22116102edbSEunchul Kim 22216102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIGCTRL); 22316102edbSEunchul Kim cfg &= ~(EXYNOS_CIGCTRL_TESTPATTERN_MASK | 22416102edbSEunchul Kim EXYNOS_CIGCTRL_SELCAM_ITU_MASK | 22516102edbSEunchul Kim EXYNOS_CIGCTRL_SELCAM_MIPI_MASK | 22616102edbSEunchul Kim EXYNOS_CIGCTRL_SELCAM_FIMC_MASK | 22716102edbSEunchul Kim EXYNOS_CIGCTRL_SELWB_CAMIF_MASK | 22816102edbSEunchul Kim EXYNOS_CIGCTRL_SELWRITEBACK_MASK); 22916102edbSEunchul Kim 23016102edbSEunchul Kim switch (wb) { 23116102edbSEunchul Kim case FIMC_WB_A: 23216102edbSEunchul Kim cfg |= (EXYNOS_CIGCTRL_SELWRITEBACK_A | 23316102edbSEunchul Kim EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK); 23416102edbSEunchul Kim break; 23516102edbSEunchul Kim case FIMC_WB_B: 23616102edbSEunchul Kim cfg |= (EXYNOS_CIGCTRL_SELWRITEBACK_B | 23716102edbSEunchul Kim EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK); 23816102edbSEunchul Kim break; 23916102edbSEunchul Kim case FIMC_WB_NONE: 24016102edbSEunchul Kim default: 24116102edbSEunchul Kim cfg |= (EXYNOS_CIGCTRL_SELCAM_ITU_A | 24216102edbSEunchul Kim EXYNOS_CIGCTRL_SELWRITEBACK_A | 24316102edbSEunchul Kim EXYNOS_CIGCTRL_SELCAM_MIPI_A | 24416102edbSEunchul Kim EXYNOS_CIGCTRL_SELCAM_FIMC_ITU); 24516102edbSEunchul Kim break; 24616102edbSEunchul Kim } 24716102edbSEunchul Kim 24816102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIGCTRL); 24916102edbSEunchul Kim } 25016102edbSEunchul Kim 25116102edbSEunchul Kim static void fimc_set_polarity(struct fimc_context *ctx, 25216102edbSEunchul Kim struct exynos_drm_ipp_pol *pol) 25316102edbSEunchul Kim { 25416102edbSEunchul Kim u32 cfg; 25516102edbSEunchul Kim 25616102edbSEunchul Kim DRM_DEBUG_KMS("%s:inv_pclk[%d]inv_vsync[%d]\n", 25716102edbSEunchul Kim __func__, pol->inv_pclk, pol->inv_vsync); 25816102edbSEunchul Kim DRM_DEBUG_KMS("%s:inv_href[%d]inv_hsync[%d]\n", 25916102edbSEunchul Kim __func__, pol->inv_href, pol->inv_hsync); 26016102edbSEunchul Kim 26116102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIGCTRL); 26216102edbSEunchul Kim cfg &= ~(EXYNOS_CIGCTRL_INVPOLPCLK | EXYNOS_CIGCTRL_INVPOLVSYNC | 26316102edbSEunchul Kim EXYNOS_CIGCTRL_INVPOLHREF | EXYNOS_CIGCTRL_INVPOLHSYNC); 26416102edbSEunchul Kim 26516102edbSEunchul Kim if (pol->inv_pclk) 26616102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_INVPOLPCLK; 26716102edbSEunchul Kim if (pol->inv_vsync) 26816102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_INVPOLVSYNC; 26916102edbSEunchul Kim if (pol->inv_href) 27016102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_INVPOLHREF; 27116102edbSEunchul Kim if (pol->inv_hsync) 27216102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_INVPOLHSYNC; 27316102edbSEunchul Kim 27416102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIGCTRL); 27516102edbSEunchul Kim } 27616102edbSEunchul Kim 27716102edbSEunchul Kim static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable) 27816102edbSEunchul Kim { 27916102edbSEunchul Kim u32 cfg; 28016102edbSEunchul Kim 28116102edbSEunchul Kim DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); 28216102edbSEunchul Kim 28316102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIGCTRL); 28416102edbSEunchul Kim if (enable) 28516102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_CAM_JPEG; 28616102edbSEunchul Kim else 28716102edbSEunchul Kim cfg &= ~EXYNOS_CIGCTRL_CAM_JPEG; 28816102edbSEunchul Kim 28916102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIGCTRL); 29016102edbSEunchul Kim } 29116102edbSEunchul Kim 29216102edbSEunchul Kim static void fimc_handle_irq(struct fimc_context *ctx, bool enable, 29316102edbSEunchul Kim bool overflow, bool level) 29416102edbSEunchul Kim { 29516102edbSEunchul Kim u32 cfg; 29616102edbSEunchul Kim 29716102edbSEunchul Kim DRM_DEBUG_KMS("%s:enable[%d]overflow[%d]level[%d]\n", __func__, 29816102edbSEunchul Kim enable, overflow, level); 29916102edbSEunchul Kim 30016102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIGCTRL); 30116102edbSEunchul Kim if (enable) { 30216102edbSEunchul Kim cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_LEVEL); 30316102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_IRQ_ENABLE; 30416102edbSEunchul Kim if (overflow) 30516102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_IRQ_OVFEN; 30616102edbSEunchul Kim if (level) 30716102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_IRQ_LEVEL; 30816102edbSEunchul Kim } else 30916102edbSEunchul Kim cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_ENABLE); 31016102edbSEunchul Kim 31116102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIGCTRL); 31216102edbSEunchul Kim } 31316102edbSEunchul Kim 31416102edbSEunchul Kim static void fimc_clear_irq(struct fimc_context *ctx) 31516102edbSEunchul Kim { 31616102edbSEunchul Kim u32 cfg; 31716102edbSEunchul Kim 31816102edbSEunchul Kim DRM_DEBUG_KMS("%s\n", __func__); 31916102edbSEunchul Kim 32016102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIGCTRL); 32116102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_IRQ_CLR; 32216102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIGCTRL); 32316102edbSEunchul Kim } 32416102edbSEunchul Kim 32516102edbSEunchul Kim static bool fimc_check_ovf(struct fimc_context *ctx) 32616102edbSEunchul Kim { 32716102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 32816102edbSEunchul Kim u32 cfg, status, flag; 32916102edbSEunchul Kim 33016102edbSEunchul Kim status = fimc_read(EXYNOS_CISTATUS); 33116102edbSEunchul Kim flag = EXYNOS_CISTATUS_OVFIY | EXYNOS_CISTATUS_OVFICB | 33216102edbSEunchul Kim EXYNOS_CISTATUS_OVFICR; 33316102edbSEunchul Kim 33416102edbSEunchul Kim DRM_DEBUG_KMS("%s:flag[0x%x]\n", __func__, flag); 33516102edbSEunchul Kim 33616102edbSEunchul Kim if (status & flag) { 33716102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIWDOFST); 33816102edbSEunchul Kim cfg |= (EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB | 33916102edbSEunchul Kim EXYNOS_CIWDOFST_CLROVFICR); 34016102edbSEunchul Kim 34116102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIWDOFST); 34216102edbSEunchul Kim 34316102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIWDOFST); 34416102edbSEunchul Kim cfg &= ~(EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB | 34516102edbSEunchul Kim EXYNOS_CIWDOFST_CLROVFICR); 34616102edbSEunchul Kim 34716102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIWDOFST); 34816102edbSEunchul Kim 34916102edbSEunchul Kim dev_err(ippdrv->dev, "occured overflow at %d, status 0x%x.\n", 35016102edbSEunchul Kim ctx->id, status); 35116102edbSEunchul Kim return true; 35216102edbSEunchul Kim } 35316102edbSEunchul Kim 35416102edbSEunchul Kim return false; 35516102edbSEunchul Kim } 35616102edbSEunchul Kim 35716102edbSEunchul Kim static bool fimc_check_frame_end(struct fimc_context *ctx) 35816102edbSEunchul Kim { 35916102edbSEunchul Kim u32 cfg; 36016102edbSEunchul Kim 36116102edbSEunchul Kim cfg = fimc_read(EXYNOS_CISTATUS); 36216102edbSEunchul Kim 36316102edbSEunchul Kim DRM_DEBUG_KMS("%s:cfg[0x%x]\n", __func__, cfg); 36416102edbSEunchul Kim 36516102edbSEunchul Kim if (!(cfg & EXYNOS_CISTATUS_FRAMEEND)) 36616102edbSEunchul Kim return false; 36716102edbSEunchul Kim 36816102edbSEunchul Kim cfg &= ~(EXYNOS_CISTATUS_FRAMEEND); 36916102edbSEunchul Kim fimc_write(cfg, EXYNOS_CISTATUS); 37016102edbSEunchul Kim 37116102edbSEunchul Kim return true; 37216102edbSEunchul Kim } 37316102edbSEunchul Kim 37416102edbSEunchul Kim static int fimc_get_buf_id(struct fimc_context *ctx) 37516102edbSEunchul Kim { 37616102edbSEunchul Kim u32 cfg; 37716102edbSEunchul Kim int frame_cnt, buf_id; 37816102edbSEunchul Kim 37916102edbSEunchul Kim DRM_DEBUG_KMS("%s\n", __func__); 38016102edbSEunchul Kim 38116102edbSEunchul Kim cfg = fimc_read(EXYNOS_CISTATUS2); 38216102edbSEunchul Kim frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg); 38316102edbSEunchul Kim 38416102edbSEunchul Kim if (frame_cnt == 0) 38516102edbSEunchul Kim frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(cfg); 38616102edbSEunchul Kim 38716102edbSEunchul Kim DRM_DEBUG_KMS("%s:present[%d]before[%d]\n", __func__, 38816102edbSEunchul Kim EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(cfg), 38916102edbSEunchul Kim EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg)); 39016102edbSEunchul Kim 39116102edbSEunchul Kim if (frame_cnt == 0) { 39216102edbSEunchul Kim DRM_ERROR("failed to get frame count.\n"); 39316102edbSEunchul Kim return -EIO; 39416102edbSEunchul Kim } 39516102edbSEunchul Kim 39616102edbSEunchul Kim buf_id = frame_cnt - 1; 39716102edbSEunchul Kim DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, buf_id); 39816102edbSEunchul Kim 39916102edbSEunchul Kim return buf_id; 40016102edbSEunchul Kim } 40116102edbSEunchul Kim 40216102edbSEunchul Kim static void fimc_handle_lastend(struct fimc_context *ctx, bool enable) 40316102edbSEunchul Kim { 40416102edbSEunchul Kim u32 cfg; 40516102edbSEunchul Kim 40616102edbSEunchul Kim DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); 40716102edbSEunchul Kim 40816102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIOCTRL); 40916102edbSEunchul Kim if (enable) 41016102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_LASTENDEN; 41116102edbSEunchul Kim else 41216102edbSEunchul Kim cfg &= ~EXYNOS_CIOCTRL_LASTENDEN; 41316102edbSEunchul Kim 41416102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIOCTRL); 41516102edbSEunchul Kim } 41616102edbSEunchul Kim 41716102edbSEunchul Kim 41816102edbSEunchul Kim static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt) 41916102edbSEunchul Kim { 42016102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 42116102edbSEunchul Kim u32 cfg; 42216102edbSEunchul Kim 42316102edbSEunchul Kim DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); 42416102edbSEunchul Kim 42516102edbSEunchul Kim /* RGB */ 42616102edbSEunchul Kim cfg = fimc_read(EXYNOS_CISCCTRL); 42716102edbSEunchul Kim cfg &= ~EXYNOS_CISCCTRL_INRGB_FMT_RGB_MASK; 42816102edbSEunchul Kim 42916102edbSEunchul Kim switch (fmt) { 43016102edbSEunchul Kim case DRM_FORMAT_RGB565: 43116102edbSEunchul Kim cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB565; 43216102edbSEunchul Kim fimc_write(cfg, EXYNOS_CISCCTRL); 43316102edbSEunchul Kim return 0; 43416102edbSEunchul Kim case DRM_FORMAT_RGB888: 43516102edbSEunchul Kim case DRM_FORMAT_XRGB8888: 43616102edbSEunchul Kim cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB888; 43716102edbSEunchul Kim fimc_write(cfg, EXYNOS_CISCCTRL); 43816102edbSEunchul Kim return 0; 43916102edbSEunchul Kim default: 44016102edbSEunchul Kim /* bypass */ 44116102edbSEunchul Kim break; 44216102edbSEunchul Kim } 44316102edbSEunchul Kim 44416102edbSEunchul Kim /* YUV */ 44516102edbSEunchul Kim cfg = fimc_read(EXYNOS_MSCTRL); 44616102edbSEunchul Kim cfg &= ~(EXYNOS_MSCTRL_ORDER2P_SHIFT_MASK | 44716102edbSEunchul Kim EXYNOS_MSCTRL_C_INT_IN_2PLANE | 44816102edbSEunchul Kim EXYNOS_MSCTRL_ORDER422_YCBYCR); 44916102edbSEunchul Kim 45016102edbSEunchul Kim switch (fmt) { 45116102edbSEunchul Kim case DRM_FORMAT_YUYV: 45216102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_ORDER422_YCBYCR; 45316102edbSEunchul Kim break; 45416102edbSEunchul Kim case DRM_FORMAT_YVYU: 45516102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_ORDER422_YCRYCB; 45616102edbSEunchul Kim break; 45716102edbSEunchul Kim case DRM_FORMAT_UYVY: 45816102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_ORDER422_CBYCRY; 45916102edbSEunchul Kim break; 46016102edbSEunchul Kim case DRM_FORMAT_VYUY: 46116102edbSEunchul Kim case DRM_FORMAT_YUV444: 46216102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_ORDER422_CRYCBY; 46316102edbSEunchul Kim break; 46416102edbSEunchul Kim case DRM_FORMAT_NV21: 46516102edbSEunchul Kim case DRM_FORMAT_NV61: 46616102edbSEunchul Kim cfg |= (EXYNOS_MSCTRL_ORDER2P_LSB_CRCB | 46716102edbSEunchul Kim EXYNOS_MSCTRL_C_INT_IN_2PLANE); 46816102edbSEunchul Kim break; 46916102edbSEunchul Kim case DRM_FORMAT_YUV422: 47016102edbSEunchul Kim case DRM_FORMAT_YUV420: 47116102edbSEunchul Kim case DRM_FORMAT_YVU420: 47216102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_C_INT_IN_3PLANE; 47316102edbSEunchul Kim break; 47416102edbSEunchul Kim case DRM_FORMAT_NV12: 47516102edbSEunchul Kim case DRM_FORMAT_NV12MT: 47616102edbSEunchul Kim case DRM_FORMAT_NV16: 47716102edbSEunchul Kim cfg |= (EXYNOS_MSCTRL_ORDER2P_LSB_CBCR | 47816102edbSEunchul Kim EXYNOS_MSCTRL_C_INT_IN_2PLANE); 47916102edbSEunchul Kim break; 48016102edbSEunchul Kim default: 48116102edbSEunchul Kim dev_err(ippdrv->dev, "inavlid source yuv order 0x%x.\n", fmt); 48216102edbSEunchul Kim return -EINVAL; 48316102edbSEunchul Kim } 48416102edbSEunchul Kim 48516102edbSEunchul Kim fimc_write(cfg, EXYNOS_MSCTRL); 48616102edbSEunchul Kim 48716102edbSEunchul Kim return 0; 48816102edbSEunchul Kim } 48916102edbSEunchul Kim 49016102edbSEunchul Kim static int fimc_src_set_fmt(struct device *dev, u32 fmt) 49116102edbSEunchul Kim { 49216102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 49316102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 49416102edbSEunchul Kim u32 cfg; 49516102edbSEunchul Kim 49616102edbSEunchul Kim DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); 49716102edbSEunchul Kim 49816102edbSEunchul Kim cfg = fimc_read(EXYNOS_MSCTRL); 49916102edbSEunchul Kim cfg &= ~EXYNOS_MSCTRL_INFORMAT_RGB; 50016102edbSEunchul Kim 50116102edbSEunchul Kim switch (fmt) { 50216102edbSEunchul Kim case DRM_FORMAT_RGB565: 50316102edbSEunchul Kim case DRM_FORMAT_RGB888: 50416102edbSEunchul Kim case DRM_FORMAT_XRGB8888: 50516102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_INFORMAT_RGB; 50616102edbSEunchul Kim break; 50716102edbSEunchul Kim case DRM_FORMAT_YUV444: 50816102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR420; 50916102edbSEunchul Kim break; 51016102edbSEunchul Kim case DRM_FORMAT_YUYV: 51116102edbSEunchul Kim case DRM_FORMAT_YVYU: 51216102edbSEunchul Kim case DRM_FORMAT_UYVY: 51316102edbSEunchul Kim case DRM_FORMAT_VYUY: 51416102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR422_1PLANE; 51516102edbSEunchul Kim break; 51616102edbSEunchul Kim case DRM_FORMAT_NV16: 51716102edbSEunchul Kim case DRM_FORMAT_NV61: 51816102edbSEunchul Kim case DRM_FORMAT_YUV422: 51916102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR422; 52016102edbSEunchul Kim break; 52116102edbSEunchul Kim case DRM_FORMAT_YUV420: 52216102edbSEunchul Kim case DRM_FORMAT_YVU420: 52316102edbSEunchul Kim case DRM_FORMAT_NV12: 52416102edbSEunchul Kim case DRM_FORMAT_NV21: 52516102edbSEunchul Kim case DRM_FORMAT_NV12MT: 52616102edbSEunchul Kim cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR420; 52716102edbSEunchul Kim break; 52816102edbSEunchul Kim default: 52916102edbSEunchul Kim dev_err(ippdrv->dev, "inavlid source format 0x%x.\n", fmt); 53016102edbSEunchul Kim return -EINVAL; 53116102edbSEunchul Kim } 53216102edbSEunchul Kim 53316102edbSEunchul Kim fimc_write(cfg, EXYNOS_MSCTRL); 53416102edbSEunchul Kim 53516102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIDMAPARAM); 53616102edbSEunchul Kim cfg &= ~EXYNOS_CIDMAPARAM_R_MODE_MASK; 53716102edbSEunchul Kim 53816102edbSEunchul Kim if (fmt == DRM_FORMAT_NV12MT) 53916102edbSEunchul Kim cfg |= EXYNOS_CIDMAPARAM_R_MODE_64X32; 54016102edbSEunchul Kim else 54116102edbSEunchul Kim cfg |= EXYNOS_CIDMAPARAM_R_MODE_LINEAR; 54216102edbSEunchul Kim 54316102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIDMAPARAM); 54416102edbSEunchul Kim 54516102edbSEunchul Kim return fimc_src_set_fmt_order(ctx, fmt); 54616102edbSEunchul Kim } 54716102edbSEunchul Kim 54816102edbSEunchul Kim static int fimc_src_set_transf(struct device *dev, 54916102edbSEunchul Kim enum drm_exynos_degree degree, 55016102edbSEunchul Kim enum drm_exynos_flip flip, bool *swap) 55116102edbSEunchul Kim { 55216102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 55316102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 55416102edbSEunchul Kim u32 cfg1, cfg2; 55516102edbSEunchul Kim 55616102edbSEunchul Kim DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, 55716102edbSEunchul Kim degree, flip); 55816102edbSEunchul Kim 55916102edbSEunchul Kim cfg1 = fimc_read(EXYNOS_MSCTRL); 56016102edbSEunchul Kim cfg1 &= ~(EXYNOS_MSCTRL_FLIP_X_MIRROR | 56116102edbSEunchul Kim EXYNOS_MSCTRL_FLIP_Y_MIRROR); 56216102edbSEunchul Kim 56316102edbSEunchul Kim cfg2 = fimc_read(EXYNOS_CITRGFMT); 56416102edbSEunchul Kim cfg2 &= ~EXYNOS_CITRGFMT_INROT90_CLOCKWISE; 56516102edbSEunchul Kim 56616102edbSEunchul Kim switch (degree) { 56716102edbSEunchul Kim case EXYNOS_DRM_DEGREE_0: 56816102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_VERTICAL) 56916102edbSEunchul Kim cfg1 |= EXYNOS_MSCTRL_FLIP_X_MIRROR; 57016102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) 57116102edbSEunchul Kim cfg1 |= EXYNOS_MSCTRL_FLIP_Y_MIRROR; 57216102edbSEunchul Kim break; 57316102edbSEunchul Kim case EXYNOS_DRM_DEGREE_90: 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 case EXYNOS_DRM_DEGREE_180: 58116102edbSEunchul Kim cfg1 |= (EXYNOS_MSCTRL_FLIP_X_MIRROR | 58216102edbSEunchul Kim EXYNOS_MSCTRL_FLIP_Y_MIRROR); 58316102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_VERTICAL) 58416102edbSEunchul Kim cfg1 &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR; 58516102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) 58616102edbSEunchul Kim cfg1 &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR; 58716102edbSEunchul Kim break; 58816102edbSEunchul Kim case EXYNOS_DRM_DEGREE_270: 58916102edbSEunchul Kim cfg1 |= (EXYNOS_MSCTRL_FLIP_X_MIRROR | 59016102edbSEunchul Kim EXYNOS_MSCTRL_FLIP_Y_MIRROR); 59116102edbSEunchul Kim cfg2 |= EXYNOS_CITRGFMT_INROT90_CLOCKWISE; 59216102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_VERTICAL) 59316102edbSEunchul Kim cfg1 &= ~EXYNOS_MSCTRL_FLIP_X_MIRROR; 59416102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) 59516102edbSEunchul Kim cfg1 &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR; 59616102edbSEunchul Kim break; 59716102edbSEunchul Kim default: 59816102edbSEunchul Kim dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); 59916102edbSEunchul Kim return -EINVAL; 60016102edbSEunchul Kim } 60116102edbSEunchul Kim 60216102edbSEunchul Kim fimc_write(cfg1, EXYNOS_MSCTRL); 60316102edbSEunchul Kim fimc_write(cfg2, EXYNOS_CITRGFMT); 60416102edbSEunchul Kim *swap = (cfg2 & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) ? 1 : 0; 60516102edbSEunchul Kim 60616102edbSEunchul Kim return 0; 60716102edbSEunchul Kim } 60816102edbSEunchul Kim 60916102edbSEunchul Kim static int fimc_set_window(struct fimc_context *ctx, 61016102edbSEunchul Kim struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) 61116102edbSEunchul Kim { 61216102edbSEunchul Kim u32 cfg, h1, h2, v1, v2; 61316102edbSEunchul Kim 61416102edbSEunchul Kim /* cropped image */ 61516102edbSEunchul Kim h1 = pos->x; 61616102edbSEunchul Kim h2 = sz->hsize - pos->w - pos->x; 61716102edbSEunchul Kim v1 = pos->y; 61816102edbSEunchul Kim v2 = sz->vsize - pos->h - pos->y; 61916102edbSEunchul Kim 62016102edbSEunchul Kim DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]hsize[%d]vsize[%d]\n", 62116102edbSEunchul Kim __func__, pos->x, pos->y, pos->w, pos->h, sz->hsize, sz->vsize); 62216102edbSEunchul Kim DRM_DEBUG_KMS("%s:h1[%d]h2[%d]v1[%d]v2[%d]\n", __func__, 62316102edbSEunchul Kim h1, h2, v1, v2); 62416102edbSEunchul Kim 62516102edbSEunchul Kim /* 62616102edbSEunchul Kim * set window offset 1, 2 size 62716102edbSEunchul Kim * check figure 43-21 in user manual 62816102edbSEunchul Kim */ 62916102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIWDOFST); 63016102edbSEunchul Kim cfg &= ~(EXYNOS_CIWDOFST_WINHOROFST_MASK | 63116102edbSEunchul Kim EXYNOS_CIWDOFST_WINVEROFST_MASK); 63216102edbSEunchul Kim cfg |= (EXYNOS_CIWDOFST_WINHOROFST(h1) | 63316102edbSEunchul Kim EXYNOS_CIWDOFST_WINVEROFST(v1)); 63416102edbSEunchul Kim cfg |= EXYNOS_CIWDOFST_WINOFSEN; 63516102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIWDOFST); 63616102edbSEunchul Kim 63716102edbSEunchul Kim cfg = (EXYNOS_CIWDOFST2_WINHOROFST2(h2) | 63816102edbSEunchul Kim EXYNOS_CIWDOFST2_WINVEROFST2(v2)); 63916102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIWDOFST2); 64016102edbSEunchul Kim 64116102edbSEunchul Kim return 0; 64216102edbSEunchul Kim } 64316102edbSEunchul Kim 64416102edbSEunchul Kim static int fimc_src_set_size(struct device *dev, int swap, 64516102edbSEunchul Kim struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) 64616102edbSEunchul Kim { 64716102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 64816102edbSEunchul Kim struct drm_exynos_pos img_pos = *pos; 64916102edbSEunchul Kim struct drm_exynos_sz img_sz = *sz; 65016102edbSEunchul Kim u32 cfg; 65116102edbSEunchul Kim 65216102edbSEunchul Kim DRM_DEBUG_KMS("%s:swap[%d]hsize[%d]vsize[%d]\n", 65316102edbSEunchul Kim __func__, swap, sz->hsize, sz->vsize); 65416102edbSEunchul Kim 65516102edbSEunchul Kim /* original size */ 65616102edbSEunchul Kim cfg = (EXYNOS_ORGISIZE_HORIZONTAL(img_sz.hsize) | 65716102edbSEunchul Kim EXYNOS_ORGISIZE_VERTICAL(img_sz.vsize)); 65816102edbSEunchul Kim 65916102edbSEunchul Kim fimc_write(cfg, EXYNOS_ORGISIZE); 66016102edbSEunchul Kim 66116102edbSEunchul Kim DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]\n", __func__, 66216102edbSEunchul Kim pos->x, pos->y, pos->w, pos->h); 66316102edbSEunchul Kim 66416102edbSEunchul Kim if (swap) { 66516102edbSEunchul Kim img_pos.w = pos->h; 66616102edbSEunchul Kim img_pos.h = pos->w; 66716102edbSEunchul Kim img_sz.hsize = sz->vsize; 66816102edbSEunchul Kim img_sz.vsize = sz->hsize; 66916102edbSEunchul Kim } 67016102edbSEunchul Kim 67116102edbSEunchul Kim /* set input DMA image size */ 67216102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIREAL_ISIZE); 67316102edbSEunchul Kim cfg &= ~(EXYNOS_CIREAL_ISIZE_HEIGHT_MASK | 67416102edbSEunchul Kim EXYNOS_CIREAL_ISIZE_WIDTH_MASK); 67516102edbSEunchul Kim cfg |= (EXYNOS_CIREAL_ISIZE_WIDTH(img_pos.w) | 67616102edbSEunchul Kim EXYNOS_CIREAL_ISIZE_HEIGHT(img_pos.h)); 67716102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIREAL_ISIZE); 67816102edbSEunchul Kim 67916102edbSEunchul Kim /* 68016102edbSEunchul Kim * set input FIFO image size 68116102edbSEunchul Kim * for now, we support only ITU601 8 bit mode 68216102edbSEunchul Kim */ 68316102edbSEunchul Kim cfg = (EXYNOS_CISRCFMT_ITU601_8BIT | 68416102edbSEunchul Kim EXYNOS_CISRCFMT_SOURCEHSIZE(img_sz.hsize) | 68516102edbSEunchul Kim EXYNOS_CISRCFMT_SOURCEVSIZE(img_sz.vsize)); 68616102edbSEunchul Kim fimc_write(cfg, EXYNOS_CISRCFMT); 68716102edbSEunchul Kim 68816102edbSEunchul Kim /* offset Y(RGB), Cb, Cr */ 68916102edbSEunchul Kim cfg = (EXYNOS_CIIYOFF_HORIZONTAL(img_pos.x) | 69016102edbSEunchul Kim EXYNOS_CIIYOFF_VERTICAL(img_pos.y)); 69116102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIIYOFF); 69216102edbSEunchul Kim cfg = (EXYNOS_CIICBOFF_HORIZONTAL(img_pos.x) | 69316102edbSEunchul Kim EXYNOS_CIICBOFF_VERTICAL(img_pos.y)); 69416102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIICBOFF); 69516102edbSEunchul Kim cfg = (EXYNOS_CIICROFF_HORIZONTAL(img_pos.x) | 69616102edbSEunchul Kim EXYNOS_CIICROFF_VERTICAL(img_pos.y)); 69716102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIICROFF); 69816102edbSEunchul Kim 69916102edbSEunchul Kim return fimc_set_window(ctx, &img_pos, &img_sz); 70016102edbSEunchul Kim } 70116102edbSEunchul Kim 70216102edbSEunchul Kim static int fimc_src_set_addr(struct device *dev, 70316102edbSEunchul Kim struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, 70416102edbSEunchul Kim enum drm_exynos_ipp_buf_type buf_type) 70516102edbSEunchul Kim { 70616102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 70716102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 7087259c3d6SEunchul Kim struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; 70916102edbSEunchul Kim struct drm_exynos_ipp_property *property; 71016102edbSEunchul Kim struct drm_exynos_ipp_config *config; 71116102edbSEunchul Kim 71216102edbSEunchul Kim if (!c_node) { 71316102edbSEunchul Kim DRM_ERROR("failed to get c_node.\n"); 71416102edbSEunchul Kim return -EINVAL; 71516102edbSEunchul Kim } 71616102edbSEunchul Kim 71716102edbSEunchul Kim property = &c_node->property; 71816102edbSEunchul Kim 71916102edbSEunchul Kim DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, 72016102edbSEunchul Kim property->prop_id, buf_id, buf_type); 72116102edbSEunchul Kim 72216102edbSEunchul Kim if (buf_id > FIMC_MAX_SRC) { 72316102edbSEunchul Kim dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id); 72416102edbSEunchul Kim return -ENOMEM; 72516102edbSEunchul Kim } 72616102edbSEunchul Kim 72716102edbSEunchul Kim /* address register set */ 72816102edbSEunchul Kim switch (buf_type) { 72916102edbSEunchul Kim case IPP_BUF_ENQUEUE: 73016102edbSEunchul Kim config = &property->config[EXYNOS_DRM_OPS_SRC]; 73116102edbSEunchul Kim fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], 73216102edbSEunchul Kim EXYNOS_CIIYSA(buf_id)); 73316102edbSEunchul Kim 73416102edbSEunchul Kim if (config->fmt == DRM_FORMAT_YVU420) { 73516102edbSEunchul Kim fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], 73616102edbSEunchul Kim EXYNOS_CIICBSA(buf_id)); 73716102edbSEunchul Kim fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], 73816102edbSEunchul Kim EXYNOS_CIICRSA(buf_id)); 73916102edbSEunchul Kim } else { 74016102edbSEunchul Kim fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], 74116102edbSEunchul Kim EXYNOS_CIICBSA(buf_id)); 74216102edbSEunchul Kim fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], 74316102edbSEunchul Kim EXYNOS_CIICRSA(buf_id)); 74416102edbSEunchul Kim } 74516102edbSEunchul Kim break; 74616102edbSEunchul Kim case IPP_BUF_DEQUEUE: 74716102edbSEunchul Kim fimc_write(0x0, EXYNOS_CIIYSA(buf_id)); 74816102edbSEunchul Kim fimc_write(0x0, EXYNOS_CIICBSA(buf_id)); 74916102edbSEunchul Kim fimc_write(0x0, EXYNOS_CIICRSA(buf_id)); 75016102edbSEunchul Kim break; 75116102edbSEunchul Kim default: 75216102edbSEunchul Kim /* bypass */ 75316102edbSEunchul Kim break; 75416102edbSEunchul Kim } 75516102edbSEunchul Kim 75616102edbSEunchul Kim return 0; 75716102edbSEunchul Kim } 75816102edbSEunchul Kim 75916102edbSEunchul Kim static struct exynos_drm_ipp_ops fimc_src_ops = { 76016102edbSEunchul Kim .set_fmt = fimc_src_set_fmt, 76116102edbSEunchul Kim .set_transf = fimc_src_set_transf, 76216102edbSEunchul Kim .set_size = fimc_src_set_size, 76316102edbSEunchul Kim .set_addr = fimc_src_set_addr, 76416102edbSEunchul Kim }; 76516102edbSEunchul Kim 76616102edbSEunchul Kim static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt) 76716102edbSEunchul Kim { 76816102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 76916102edbSEunchul Kim u32 cfg; 77016102edbSEunchul Kim 77116102edbSEunchul Kim DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); 77216102edbSEunchul Kim 77316102edbSEunchul Kim /* RGB */ 77416102edbSEunchul Kim cfg = fimc_read(EXYNOS_CISCCTRL); 77516102edbSEunchul Kim cfg &= ~EXYNOS_CISCCTRL_OUTRGB_FMT_RGB_MASK; 77616102edbSEunchul Kim 77716102edbSEunchul Kim switch (fmt) { 77816102edbSEunchul Kim case DRM_FORMAT_RGB565: 77916102edbSEunchul Kim cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565; 78016102edbSEunchul Kim fimc_write(cfg, EXYNOS_CISCCTRL); 78116102edbSEunchul Kim return 0; 78216102edbSEunchul Kim case DRM_FORMAT_RGB888: 78316102edbSEunchul Kim cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888; 78416102edbSEunchul Kim fimc_write(cfg, EXYNOS_CISCCTRL); 78516102edbSEunchul Kim return 0; 78616102edbSEunchul Kim case DRM_FORMAT_XRGB8888: 78716102edbSEunchul Kim cfg |= (EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 | 78816102edbSEunchul Kim EXYNOS_CISCCTRL_EXTRGB_EXTENSION); 78916102edbSEunchul Kim fimc_write(cfg, EXYNOS_CISCCTRL); 79016102edbSEunchul Kim break; 79116102edbSEunchul Kim default: 79216102edbSEunchul Kim /* bypass */ 79316102edbSEunchul Kim break; 79416102edbSEunchul Kim } 79516102edbSEunchul Kim 79616102edbSEunchul Kim /* YUV */ 79716102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIOCTRL); 79816102edbSEunchul Kim cfg &= ~(EXYNOS_CIOCTRL_ORDER2P_MASK | 79916102edbSEunchul Kim EXYNOS_CIOCTRL_ORDER422_MASK | 80016102edbSEunchul Kim EXYNOS_CIOCTRL_YCBCR_PLANE_MASK); 80116102edbSEunchul Kim 80216102edbSEunchul Kim switch (fmt) { 80316102edbSEunchul Kim case DRM_FORMAT_XRGB8888: 80416102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_ALPHA_OUT; 80516102edbSEunchul Kim break; 80616102edbSEunchul Kim case DRM_FORMAT_YUYV: 80716102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_ORDER422_YCBYCR; 80816102edbSEunchul Kim break; 80916102edbSEunchul Kim case DRM_FORMAT_YVYU: 81016102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_ORDER422_YCRYCB; 81116102edbSEunchul Kim break; 81216102edbSEunchul Kim case DRM_FORMAT_UYVY: 81316102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_ORDER422_CBYCRY; 81416102edbSEunchul Kim break; 81516102edbSEunchul Kim case DRM_FORMAT_VYUY: 81616102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_ORDER422_CRYCBY; 81716102edbSEunchul Kim break; 81816102edbSEunchul Kim case DRM_FORMAT_NV21: 81916102edbSEunchul Kim case DRM_FORMAT_NV61: 82016102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_ORDER2P_LSB_CRCB; 82116102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_YCBCR_2PLANE; 82216102edbSEunchul Kim break; 82316102edbSEunchul Kim case DRM_FORMAT_YUV422: 82416102edbSEunchul Kim case DRM_FORMAT_YUV420: 82516102edbSEunchul Kim case DRM_FORMAT_YVU420: 82616102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_YCBCR_3PLANE; 82716102edbSEunchul Kim break; 82816102edbSEunchul Kim case DRM_FORMAT_NV12: 82916102edbSEunchul Kim case DRM_FORMAT_NV12MT: 83016102edbSEunchul Kim case DRM_FORMAT_NV16: 83116102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_ORDER2P_LSB_CBCR; 83216102edbSEunchul Kim cfg |= EXYNOS_CIOCTRL_YCBCR_2PLANE; 83316102edbSEunchul Kim break; 83416102edbSEunchul Kim default: 83516102edbSEunchul Kim dev_err(ippdrv->dev, "inavlid target yuv order 0x%x.\n", fmt); 83616102edbSEunchul Kim return -EINVAL; 83716102edbSEunchul Kim } 83816102edbSEunchul Kim 83916102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIOCTRL); 84016102edbSEunchul Kim 84116102edbSEunchul Kim return 0; 84216102edbSEunchul Kim } 84316102edbSEunchul Kim 84416102edbSEunchul Kim static int fimc_dst_set_fmt(struct device *dev, u32 fmt) 84516102edbSEunchul Kim { 84616102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 84716102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 84816102edbSEunchul Kim u32 cfg; 84916102edbSEunchul Kim 85016102edbSEunchul Kim DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); 85116102edbSEunchul Kim 85216102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIEXTEN); 85316102edbSEunchul Kim 85416102edbSEunchul Kim if (fmt == DRM_FORMAT_AYUV) { 85516102edbSEunchul Kim cfg |= EXYNOS_CIEXTEN_YUV444_OUT; 85616102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIEXTEN); 85716102edbSEunchul Kim } else { 85816102edbSEunchul Kim cfg &= ~EXYNOS_CIEXTEN_YUV444_OUT; 85916102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIEXTEN); 86016102edbSEunchul Kim 86116102edbSEunchul Kim cfg = fimc_read(EXYNOS_CITRGFMT); 86216102edbSEunchul Kim cfg &= ~EXYNOS_CITRGFMT_OUTFORMAT_MASK; 86316102edbSEunchul Kim 86416102edbSEunchul Kim switch (fmt) { 86516102edbSEunchul Kim case DRM_FORMAT_RGB565: 86616102edbSEunchul Kim case DRM_FORMAT_RGB888: 86716102edbSEunchul Kim case DRM_FORMAT_XRGB8888: 86816102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_OUTFORMAT_RGB; 86916102edbSEunchul Kim break; 87016102edbSEunchul Kim case DRM_FORMAT_YUYV: 87116102edbSEunchul Kim case DRM_FORMAT_YVYU: 87216102edbSEunchul Kim case DRM_FORMAT_UYVY: 87316102edbSEunchul Kim case DRM_FORMAT_VYUY: 87416102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422_1PLANE; 87516102edbSEunchul Kim break; 87616102edbSEunchul Kim case DRM_FORMAT_NV16: 87716102edbSEunchul Kim case DRM_FORMAT_NV61: 87816102edbSEunchul Kim case DRM_FORMAT_YUV422: 87916102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422; 88016102edbSEunchul Kim break; 88116102edbSEunchul Kim case DRM_FORMAT_YUV420: 88216102edbSEunchul Kim case DRM_FORMAT_YVU420: 88316102edbSEunchul Kim case DRM_FORMAT_NV12: 88416102edbSEunchul Kim case DRM_FORMAT_NV12MT: 88516102edbSEunchul Kim case DRM_FORMAT_NV21: 88616102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR420; 88716102edbSEunchul Kim break; 88816102edbSEunchul Kim default: 88916102edbSEunchul Kim dev_err(ippdrv->dev, "inavlid target format 0x%x.\n", 89016102edbSEunchul Kim fmt); 89116102edbSEunchul Kim return -EINVAL; 89216102edbSEunchul Kim } 89316102edbSEunchul Kim 89416102edbSEunchul Kim fimc_write(cfg, EXYNOS_CITRGFMT); 89516102edbSEunchul Kim } 89616102edbSEunchul Kim 89716102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIDMAPARAM); 89816102edbSEunchul Kim cfg &= ~EXYNOS_CIDMAPARAM_W_MODE_MASK; 89916102edbSEunchul Kim 90016102edbSEunchul Kim if (fmt == DRM_FORMAT_NV12MT) 90116102edbSEunchul Kim cfg |= EXYNOS_CIDMAPARAM_W_MODE_64X32; 90216102edbSEunchul Kim else 90316102edbSEunchul Kim cfg |= EXYNOS_CIDMAPARAM_W_MODE_LINEAR; 90416102edbSEunchul Kim 90516102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIDMAPARAM); 90616102edbSEunchul Kim 90716102edbSEunchul Kim return fimc_dst_set_fmt_order(ctx, fmt); 90816102edbSEunchul Kim } 90916102edbSEunchul Kim 91016102edbSEunchul Kim static int fimc_dst_set_transf(struct device *dev, 91116102edbSEunchul Kim enum drm_exynos_degree degree, 91216102edbSEunchul Kim enum drm_exynos_flip flip, bool *swap) 91316102edbSEunchul Kim { 91416102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 91516102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 91616102edbSEunchul Kim u32 cfg; 91716102edbSEunchul Kim 91816102edbSEunchul Kim DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, 91916102edbSEunchul Kim degree, flip); 92016102edbSEunchul Kim 92116102edbSEunchul Kim cfg = fimc_read(EXYNOS_CITRGFMT); 92216102edbSEunchul Kim cfg &= ~EXYNOS_CITRGFMT_FLIP_MASK; 92316102edbSEunchul Kim cfg &= ~EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE; 92416102edbSEunchul Kim 92516102edbSEunchul Kim switch (degree) { 92616102edbSEunchul Kim case EXYNOS_DRM_DEGREE_0: 92716102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_VERTICAL) 92816102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR; 92916102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) 93016102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR; 93116102edbSEunchul Kim break; 93216102edbSEunchul Kim case EXYNOS_DRM_DEGREE_90: 93316102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE; 93416102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_VERTICAL) 93516102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_FLIP_X_MIRROR; 93616102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) 93716102edbSEunchul Kim cfg |= EXYNOS_CITRGFMT_FLIP_Y_MIRROR; 93816102edbSEunchul Kim break; 93916102edbSEunchul Kim case EXYNOS_DRM_DEGREE_180: 94016102edbSEunchul Kim cfg |= (EXYNOS_CITRGFMT_FLIP_X_MIRROR | 94116102edbSEunchul Kim EXYNOS_CITRGFMT_FLIP_Y_MIRROR); 94216102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_VERTICAL) 94316102edbSEunchul Kim cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR; 94416102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) 94516102edbSEunchul Kim cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR; 94616102edbSEunchul Kim break; 94716102edbSEunchul Kim case EXYNOS_DRM_DEGREE_270: 94816102edbSEunchul Kim cfg |= (EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE | 94916102edbSEunchul Kim EXYNOS_CITRGFMT_FLIP_X_MIRROR | 95016102edbSEunchul Kim EXYNOS_CITRGFMT_FLIP_Y_MIRROR); 95116102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_VERTICAL) 95216102edbSEunchul Kim cfg &= ~EXYNOS_CITRGFMT_FLIP_X_MIRROR; 95316102edbSEunchul Kim if (flip & EXYNOS_DRM_FLIP_HORIZONTAL) 95416102edbSEunchul Kim cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR; 95516102edbSEunchul Kim break; 95616102edbSEunchul Kim default: 95716102edbSEunchul Kim dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree); 95816102edbSEunchul Kim return -EINVAL; 95916102edbSEunchul Kim } 96016102edbSEunchul Kim 96116102edbSEunchul Kim fimc_write(cfg, EXYNOS_CITRGFMT); 96216102edbSEunchul Kim *swap = (cfg & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) ? 1 : 0; 96316102edbSEunchul Kim 96416102edbSEunchul Kim return 0; 96516102edbSEunchul Kim } 96616102edbSEunchul Kim 96716102edbSEunchul Kim static int fimc_get_ratio_shift(u32 src, u32 dst, u32 *ratio, u32 *shift) 96816102edbSEunchul Kim { 96916102edbSEunchul Kim DRM_DEBUG_KMS("%s:src[%d]dst[%d]\n", __func__, src, dst); 97016102edbSEunchul Kim 97116102edbSEunchul Kim if (src >= dst * 64) { 97216102edbSEunchul Kim DRM_ERROR("failed to make ratio and shift.\n"); 97316102edbSEunchul Kim return -EINVAL; 97416102edbSEunchul Kim } else if (src >= dst * 32) { 97516102edbSEunchul Kim *ratio = 32; 97616102edbSEunchul Kim *shift = 5; 97716102edbSEunchul Kim } else if (src >= dst * 16) { 97816102edbSEunchul Kim *ratio = 16; 97916102edbSEunchul Kim *shift = 4; 98016102edbSEunchul Kim } else if (src >= dst * 8) { 98116102edbSEunchul Kim *ratio = 8; 98216102edbSEunchul Kim *shift = 3; 98316102edbSEunchul Kim } else if (src >= dst * 4) { 98416102edbSEunchul Kim *ratio = 4; 98516102edbSEunchul Kim *shift = 2; 98616102edbSEunchul Kim } else if (src >= dst * 2) { 98716102edbSEunchul Kim *ratio = 2; 98816102edbSEunchul Kim *shift = 1; 98916102edbSEunchul Kim } else { 99016102edbSEunchul Kim *ratio = 1; 99116102edbSEunchul Kim *shift = 0; 99216102edbSEunchul Kim } 99316102edbSEunchul Kim 99416102edbSEunchul Kim return 0; 99516102edbSEunchul Kim } 99616102edbSEunchul Kim 99716102edbSEunchul Kim static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc, 99816102edbSEunchul Kim struct drm_exynos_pos *src, struct drm_exynos_pos *dst) 99916102edbSEunchul Kim { 100016102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 100116102edbSEunchul Kim u32 cfg, cfg_ext, shfactor; 100216102edbSEunchul Kim u32 pre_dst_width, pre_dst_height; 100316102edbSEunchul Kim u32 pre_hratio, hfactor, pre_vratio, vfactor; 100416102edbSEunchul Kim int ret = 0; 100516102edbSEunchul Kim u32 src_w, src_h, dst_w, dst_h; 100616102edbSEunchul Kim 100716102edbSEunchul Kim cfg_ext = fimc_read(EXYNOS_CITRGFMT); 100816102edbSEunchul Kim if (cfg_ext & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) { 100916102edbSEunchul Kim src_w = src->h; 101016102edbSEunchul Kim src_h = src->w; 101116102edbSEunchul Kim } else { 101216102edbSEunchul Kim src_w = src->w; 101316102edbSEunchul Kim src_h = src->h; 101416102edbSEunchul Kim } 101516102edbSEunchul Kim 101616102edbSEunchul Kim if (cfg_ext & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) { 101716102edbSEunchul Kim dst_w = dst->h; 101816102edbSEunchul Kim dst_h = dst->w; 101916102edbSEunchul Kim } else { 102016102edbSEunchul Kim dst_w = dst->w; 102116102edbSEunchul Kim dst_h = dst->h; 102216102edbSEunchul Kim } 102316102edbSEunchul Kim 102416102edbSEunchul Kim ret = fimc_get_ratio_shift(src_w, dst_w, &pre_hratio, &hfactor); 102516102edbSEunchul Kim if (ret) { 102616102edbSEunchul Kim dev_err(ippdrv->dev, "failed to get ratio horizontal.\n"); 102716102edbSEunchul Kim return ret; 102816102edbSEunchul Kim } 102916102edbSEunchul Kim 103016102edbSEunchul Kim ret = fimc_get_ratio_shift(src_h, dst_h, &pre_vratio, &vfactor); 103116102edbSEunchul Kim if (ret) { 103216102edbSEunchul Kim dev_err(ippdrv->dev, "failed to get ratio vertical.\n"); 103316102edbSEunchul Kim return ret; 103416102edbSEunchul Kim } 103516102edbSEunchul Kim 103616102edbSEunchul Kim pre_dst_width = src_w / pre_hratio; 103716102edbSEunchul Kim pre_dst_height = src_h / pre_vratio; 103816102edbSEunchul Kim DRM_DEBUG_KMS("%s:pre_dst_width[%d]pre_dst_height[%d]\n", __func__, 103916102edbSEunchul Kim pre_dst_width, pre_dst_height); 104016102edbSEunchul Kim DRM_DEBUG_KMS("%s:pre_hratio[%d]hfactor[%d]pre_vratio[%d]vfactor[%d]\n", 104116102edbSEunchul Kim __func__, pre_hratio, hfactor, pre_vratio, vfactor); 104216102edbSEunchul Kim 104316102edbSEunchul Kim sc->hratio = (src_w << 14) / (dst_w << hfactor); 104416102edbSEunchul Kim sc->vratio = (src_h << 14) / (dst_h << vfactor); 104516102edbSEunchul Kim sc->up_h = (dst_w >= src_w) ? true : false; 104616102edbSEunchul Kim sc->up_v = (dst_h >= src_h) ? true : false; 104716102edbSEunchul Kim DRM_DEBUG_KMS("%s:hratio[%d]vratio[%d]up_h[%d]up_v[%d]\n", 104816102edbSEunchul Kim __func__, sc->hratio, sc->vratio, sc->up_h, sc->up_v); 104916102edbSEunchul Kim 105016102edbSEunchul Kim shfactor = FIMC_SHFACTOR - (hfactor + vfactor); 105116102edbSEunchul Kim DRM_DEBUG_KMS("%s:shfactor[%d]\n", __func__, shfactor); 105216102edbSEunchul Kim 105316102edbSEunchul Kim cfg = (EXYNOS_CISCPRERATIO_SHFACTOR(shfactor) | 105416102edbSEunchul Kim EXYNOS_CISCPRERATIO_PREHORRATIO(pre_hratio) | 105516102edbSEunchul Kim EXYNOS_CISCPRERATIO_PREVERRATIO(pre_vratio)); 105616102edbSEunchul Kim fimc_write(cfg, EXYNOS_CISCPRERATIO); 105716102edbSEunchul Kim 105816102edbSEunchul Kim cfg = (EXYNOS_CISCPREDST_PREDSTWIDTH(pre_dst_width) | 105916102edbSEunchul Kim EXYNOS_CISCPREDST_PREDSTHEIGHT(pre_dst_height)); 106016102edbSEunchul Kim fimc_write(cfg, EXYNOS_CISCPREDST); 106116102edbSEunchul Kim 106216102edbSEunchul Kim return ret; 106316102edbSEunchul Kim } 106416102edbSEunchul Kim 106516102edbSEunchul Kim static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc) 106616102edbSEunchul Kim { 106716102edbSEunchul Kim u32 cfg, cfg_ext; 106816102edbSEunchul Kim 106916102edbSEunchul Kim DRM_DEBUG_KMS("%s:range[%d]bypass[%d]up_h[%d]up_v[%d]\n", 107016102edbSEunchul Kim __func__, sc->range, sc->bypass, sc->up_h, sc->up_v); 107116102edbSEunchul Kim DRM_DEBUG_KMS("%s:hratio[%d]vratio[%d]\n", 107216102edbSEunchul Kim __func__, sc->hratio, sc->vratio); 107316102edbSEunchul Kim 107416102edbSEunchul Kim cfg = fimc_read(EXYNOS_CISCCTRL); 107516102edbSEunchul Kim cfg &= ~(EXYNOS_CISCCTRL_SCALERBYPASS | 107616102edbSEunchul Kim EXYNOS_CISCCTRL_SCALEUP_H | EXYNOS_CISCCTRL_SCALEUP_V | 107716102edbSEunchul Kim EXYNOS_CISCCTRL_MAIN_V_RATIO_MASK | 107816102edbSEunchul Kim EXYNOS_CISCCTRL_MAIN_H_RATIO_MASK | 107916102edbSEunchul Kim EXYNOS_CISCCTRL_CSCR2Y_WIDE | 108016102edbSEunchul Kim EXYNOS_CISCCTRL_CSCY2R_WIDE); 108116102edbSEunchul Kim 108216102edbSEunchul Kim if (sc->range) 108316102edbSEunchul Kim cfg |= (EXYNOS_CISCCTRL_CSCR2Y_WIDE | 108416102edbSEunchul Kim EXYNOS_CISCCTRL_CSCY2R_WIDE); 108516102edbSEunchul Kim if (sc->bypass) 108616102edbSEunchul Kim cfg |= EXYNOS_CISCCTRL_SCALERBYPASS; 108716102edbSEunchul Kim if (sc->up_h) 108816102edbSEunchul Kim cfg |= EXYNOS_CISCCTRL_SCALEUP_H; 108916102edbSEunchul Kim if (sc->up_v) 109016102edbSEunchul Kim cfg |= EXYNOS_CISCCTRL_SCALEUP_V; 109116102edbSEunchul Kim 109216102edbSEunchul Kim cfg |= (EXYNOS_CISCCTRL_MAINHORRATIO((sc->hratio >> 6)) | 109316102edbSEunchul Kim EXYNOS_CISCCTRL_MAINVERRATIO((sc->vratio >> 6))); 109416102edbSEunchul Kim fimc_write(cfg, EXYNOS_CISCCTRL); 109516102edbSEunchul Kim 109616102edbSEunchul Kim cfg_ext = fimc_read(EXYNOS_CIEXTEN); 109716102edbSEunchul Kim cfg_ext &= ~EXYNOS_CIEXTEN_MAINHORRATIO_EXT_MASK; 109816102edbSEunchul Kim cfg_ext &= ~EXYNOS_CIEXTEN_MAINVERRATIO_EXT_MASK; 109916102edbSEunchul Kim cfg_ext |= (EXYNOS_CIEXTEN_MAINHORRATIO_EXT(sc->hratio) | 110016102edbSEunchul Kim EXYNOS_CIEXTEN_MAINVERRATIO_EXT(sc->vratio)); 110116102edbSEunchul Kim fimc_write(cfg_ext, EXYNOS_CIEXTEN); 110216102edbSEunchul Kim } 110316102edbSEunchul Kim 110416102edbSEunchul Kim static int fimc_dst_set_size(struct device *dev, int swap, 110516102edbSEunchul Kim struct drm_exynos_pos *pos, struct drm_exynos_sz *sz) 110616102edbSEunchul Kim { 110716102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 110816102edbSEunchul Kim struct drm_exynos_pos img_pos = *pos; 110916102edbSEunchul Kim struct drm_exynos_sz img_sz = *sz; 111016102edbSEunchul Kim u32 cfg; 111116102edbSEunchul Kim 111216102edbSEunchul Kim DRM_DEBUG_KMS("%s:swap[%d]hsize[%d]vsize[%d]\n", 111316102edbSEunchul Kim __func__, swap, sz->hsize, sz->vsize); 111416102edbSEunchul Kim 111516102edbSEunchul Kim /* original size */ 111616102edbSEunchul Kim cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(img_sz.hsize) | 111716102edbSEunchul Kim EXYNOS_ORGOSIZE_VERTICAL(img_sz.vsize)); 111816102edbSEunchul Kim 111916102edbSEunchul Kim fimc_write(cfg, EXYNOS_ORGOSIZE); 112016102edbSEunchul Kim 112116102edbSEunchul Kim DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]\n", 112216102edbSEunchul Kim __func__, pos->x, pos->y, pos->w, pos->h); 112316102edbSEunchul Kim 112416102edbSEunchul Kim /* CSC ITU */ 112516102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIGCTRL); 112616102edbSEunchul Kim cfg &= ~EXYNOS_CIGCTRL_CSC_MASK; 112716102edbSEunchul Kim 112816102edbSEunchul Kim if (sz->hsize >= FIMC_WIDTH_ITU_709) 112916102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_CSC_ITU709; 113016102edbSEunchul Kim else 113116102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_CSC_ITU601; 113216102edbSEunchul Kim 113316102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIGCTRL); 113416102edbSEunchul Kim 113516102edbSEunchul Kim if (swap) { 113616102edbSEunchul Kim img_pos.w = pos->h; 113716102edbSEunchul Kim img_pos.h = pos->w; 113816102edbSEunchul Kim img_sz.hsize = sz->vsize; 113916102edbSEunchul Kim img_sz.vsize = sz->hsize; 114016102edbSEunchul Kim } 114116102edbSEunchul Kim 114216102edbSEunchul Kim /* target image size */ 114316102edbSEunchul Kim cfg = fimc_read(EXYNOS_CITRGFMT); 114416102edbSEunchul Kim cfg &= ~(EXYNOS_CITRGFMT_TARGETH_MASK | 114516102edbSEunchul Kim EXYNOS_CITRGFMT_TARGETV_MASK); 114616102edbSEunchul Kim cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(img_pos.w) | 114716102edbSEunchul Kim EXYNOS_CITRGFMT_TARGETVSIZE(img_pos.h)); 114816102edbSEunchul Kim fimc_write(cfg, EXYNOS_CITRGFMT); 114916102edbSEunchul Kim 115016102edbSEunchul Kim /* target area */ 115116102edbSEunchul Kim cfg = EXYNOS_CITAREA_TARGET_AREA(img_pos.w * img_pos.h); 115216102edbSEunchul Kim fimc_write(cfg, EXYNOS_CITAREA); 115316102edbSEunchul Kim 115416102edbSEunchul Kim /* offset Y(RGB), Cb, Cr */ 115516102edbSEunchul Kim cfg = (EXYNOS_CIOYOFF_HORIZONTAL(img_pos.x) | 115616102edbSEunchul Kim EXYNOS_CIOYOFF_VERTICAL(img_pos.y)); 115716102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIOYOFF); 115816102edbSEunchul Kim cfg = (EXYNOS_CIOCBOFF_HORIZONTAL(img_pos.x) | 115916102edbSEunchul Kim EXYNOS_CIOCBOFF_VERTICAL(img_pos.y)); 116016102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIOCBOFF); 116116102edbSEunchul Kim cfg = (EXYNOS_CIOCROFF_HORIZONTAL(img_pos.x) | 116216102edbSEunchul Kim EXYNOS_CIOCROFF_VERTICAL(img_pos.y)); 116316102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIOCROFF); 116416102edbSEunchul Kim 116516102edbSEunchul Kim return 0; 116616102edbSEunchul Kim } 116716102edbSEunchul Kim 116816102edbSEunchul Kim static int fimc_dst_get_buf_seq(struct fimc_context *ctx) 116916102edbSEunchul Kim { 117016102edbSEunchul Kim u32 cfg, i, buf_num = 0; 117116102edbSEunchul Kim u32 mask = 0x00000001; 117216102edbSEunchul Kim 117316102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIFCNTSEQ); 117416102edbSEunchul Kim 117516102edbSEunchul Kim for (i = 0; i < FIMC_REG_SZ; i++) 117616102edbSEunchul Kim if (cfg & (mask << i)) 117716102edbSEunchul Kim buf_num++; 117816102edbSEunchul Kim 117916102edbSEunchul Kim DRM_DEBUG_KMS("%s:buf_num[%d]\n", __func__, buf_num); 118016102edbSEunchul Kim 118116102edbSEunchul Kim return buf_num; 118216102edbSEunchul Kim } 118316102edbSEunchul Kim 118416102edbSEunchul Kim static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id, 118516102edbSEunchul Kim enum drm_exynos_ipp_buf_type buf_type) 118616102edbSEunchul Kim { 118716102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 118816102edbSEunchul Kim bool enable; 118916102edbSEunchul Kim u32 cfg; 119016102edbSEunchul Kim u32 mask = 0x00000001 << buf_id; 119116102edbSEunchul Kim int ret = 0; 119216102edbSEunchul Kim 119316102edbSEunchul Kim DRM_DEBUG_KMS("%s:buf_id[%d]buf_type[%d]\n", __func__, 119416102edbSEunchul Kim buf_id, buf_type); 119516102edbSEunchul Kim 119616102edbSEunchul Kim mutex_lock(&ctx->lock); 119716102edbSEunchul Kim 119816102edbSEunchul Kim /* mask register set */ 119916102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIFCNTSEQ); 120016102edbSEunchul Kim 120116102edbSEunchul Kim switch (buf_type) { 120216102edbSEunchul Kim case IPP_BUF_ENQUEUE: 120316102edbSEunchul Kim enable = true; 120416102edbSEunchul Kim break; 120516102edbSEunchul Kim case IPP_BUF_DEQUEUE: 120616102edbSEunchul Kim enable = false; 120716102edbSEunchul Kim break; 120816102edbSEunchul Kim default: 120916102edbSEunchul Kim dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n"); 121016102edbSEunchul Kim ret = -EINVAL; 121116102edbSEunchul Kim goto err_unlock; 121216102edbSEunchul Kim } 121316102edbSEunchul Kim 121416102edbSEunchul Kim /* sequence id */ 121516102edbSEunchul Kim cfg &= (~mask); 121616102edbSEunchul Kim cfg |= (enable << buf_id); 121716102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIFCNTSEQ); 121816102edbSEunchul Kim 121916102edbSEunchul Kim /* interrupt enable */ 122016102edbSEunchul Kim if (buf_type == IPP_BUF_ENQUEUE && 122116102edbSEunchul Kim fimc_dst_get_buf_seq(ctx) >= FIMC_BUF_START) 122216102edbSEunchul Kim fimc_handle_irq(ctx, true, false, true); 122316102edbSEunchul Kim 122416102edbSEunchul Kim /* interrupt disable */ 122516102edbSEunchul Kim if (buf_type == IPP_BUF_DEQUEUE && 122616102edbSEunchul Kim fimc_dst_get_buf_seq(ctx) <= FIMC_BUF_STOP) 122716102edbSEunchul Kim fimc_handle_irq(ctx, false, false, true); 122816102edbSEunchul Kim 122916102edbSEunchul Kim err_unlock: 123016102edbSEunchul Kim mutex_unlock(&ctx->lock); 123116102edbSEunchul Kim return ret; 123216102edbSEunchul Kim } 123316102edbSEunchul Kim 123416102edbSEunchul Kim static int fimc_dst_set_addr(struct device *dev, 123516102edbSEunchul Kim struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, 123616102edbSEunchul Kim enum drm_exynos_ipp_buf_type buf_type) 123716102edbSEunchul Kim { 123816102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 123916102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 12407259c3d6SEunchul Kim struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; 124116102edbSEunchul Kim struct drm_exynos_ipp_property *property; 124216102edbSEunchul Kim struct drm_exynos_ipp_config *config; 124316102edbSEunchul Kim 124416102edbSEunchul Kim if (!c_node) { 124516102edbSEunchul Kim DRM_ERROR("failed to get c_node.\n"); 124616102edbSEunchul Kim return -EINVAL; 124716102edbSEunchul Kim } 124816102edbSEunchul Kim 124916102edbSEunchul Kim property = &c_node->property; 125016102edbSEunchul Kim 125116102edbSEunchul Kim DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, 125216102edbSEunchul Kim property->prop_id, buf_id, buf_type); 125316102edbSEunchul Kim 125416102edbSEunchul Kim if (buf_id > FIMC_MAX_DST) { 125516102edbSEunchul Kim dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id); 125616102edbSEunchul Kim return -ENOMEM; 125716102edbSEunchul Kim } 125816102edbSEunchul Kim 125916102edbSEunchul Kim /* address register set */ 126016102edbSEunchul Kim switch (buf_type) { 126116102edbSEunchul Kim case IPP_BUF_ENQUEUE: 126216102edbSEunchul Kim config = &property->config[EXYNOS_DRM_OPS_DST]; 126316102edbSEunchul Kim 126416102edbSEunchul Kim fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], 126516102edbSEunchul Kim EXYNOS_CIOYSA(buf_id)); 126616102edbSEunchul Kim 126716102edbSEunchul Kim if (config->fmt == DRM_FORMAT_YVU420) { 126816102edbSEunchul Kim fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], 126916102edbSEunchul Kim EXYNOS_CIOCBSA(buf_id)); 127016102edbSEunchul Kim fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], 127116102edbSEunchul Kim EXYNOS_CIOCRSA(buf_id)); 127216102edbSEunchul Kim } else { 127316102edbSEunchul Kim fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], 127416102edbSEunchul Kim EXYNOS_CIOCBSA(buf_id)); 127516102edbSEunchul Kim fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], 127616102edbSEunchul Kim EXYNOS_CIOCRSA(buf_id)); 127716102edbSEunchul Kim } 127816102edbSEunchul Kim break; 127916102edbSEunchul Kim case IPP_BUF_DEQUEUE: 128016102edbSEunchul Kim fimc_write(0x0, EXYNOS_CIOYSA(buf_id)); 128116102edbSEunchul Kim fimc_write(0x0, EXYNOS_CIOCBSA(buf_id)); 128216102edbSEunchul Kim fimc_write(0x0, EXYNOS_CIOCRSA(buf_id)); 128316102edbSEunchul Kim break; 128416102edbSEunchul Kim default: 128516102edbSEunchul Kim /* bypass */ 128616102edbSEunchul Kim break; 128716102edbSEunchul Kim } 128816102edbSEunchul Kim 128916102edbSEunchul Kim return fimc_dst_set_buf_seq(ctx, buf_id, buf_type); 129016102edbSEunchul Kim } 129116102edbSEunchul Kim 129216102edbSEunchul Kim static struct exynos_drm_ipp_ops fimc_dst_ops = { 129316102edbSEunchul Kim .set_fmt = fimc_dst_set_fmt, 129416102edbSEunchul Kim .set_transf = fimc_dst_set_transf, 129516102edbSEunchul Kim .set_size = fimc_dst_set_size, 129616102edbSEunchul Kim .set_addr = fimc_dst_set_addr, 129716102edbSEunchul Kim }; 129816102edbSEunchul Kim 129916102edbSEunchul Kim static int fimc_clk_ctrl(struct fimc_context *ctx, bool enable) 130016102edbSEunchul Kim { 130116102edbSEunchul Kim DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); 130216102edbSEunchul Kim 130316102edbSEunchul Kim if (enable) { 130416102edbSEunchul Kim clk_enable(ctx->sclk_fimc_clk); 130516102edbSEunchul Kim clk_enable(ctx->fimc_clk); 130616102edbSEunchul Kim clk_enable(ctx->wb_clk); 130716102edbSEunchul Kim ctx->suspended = false; 130816102edbSEunchul Kim } else { 130916102edbSEunchul Kim clk_disable(ctx->sclk_fimc_clk); 131016102edbSEunchul Kim clk_disable(ctx->fimc_clk); 131116102edbSEunchul Kim clk_disable(ctx->wb_clk); 131216102edbSEunchul Kim ctx->suspended = true; 131316102edbSEunchul Kim } 131416102edbSEunchul Kim 131516102edbSEunchul Kim return 0; 131616102edbSEunchul Kim } 131716102edbSEunchul Kim 131816102edbSEunchul Kim static irqreturn_t fimc_irq_handler(int irq, void *dev_id) 131916102edbSEunchul Kim { 132016102edbSEunchul Kim struct fimc_context *ctx = dev_id; 132116102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 13227259c3d6SEunchul Kim struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; 132316102edbSEunchul Kim struct drm_exynos_ipp_event_work *event_work = 132416102edbSEunchul Kim c_node->event_work; 132516102edbSEunchul Kim int buf_id; 132616102edbSEunchul Kim 132716102edbSEunchul Kim DRM_DEBUG_KMS("%s:fimc id[%d]\n", __func__, ctx->id); 132816102edbSEunchul Kim 132916102edbSEunchul Kim fimc_clear_irq(ctx); 133016102edbSEunchul Kim if (fimc_check_ovf(ctx)) 133116102edbSEunchul Kim return IRQ_NONE; 133216102edbSEunchul Kim 133316102edbSEunchul Kim if (!fimc_check_frame_end(ctx)) 133416102edbSEunchul Kim return IRQ_NONE; 133516102edbSEunchul Kim 133616102edbSEunchul Kim buf_id = fimc_get_buf_id(ctx); 133716102edbSEunchul Kim if (buf_id < 0) 133816102edbSEunchul Kim return IRQ_HANDLED; 133916102edbSEunchul Kim 134016102edbSEunchul Kim DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, buf_id); 134116102edbSEunchul Kim 134216102edbSEunchul Kim if (fimc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE) < 0) { 134316102edbSEunchul Kim DRM_ERROR("failed to dequeue.\n"); 134416102edbSEunchul Kim return IRQ_HANDLED; 134516102edbSEunchul Kim } 134616102edbSEunchul Kim 134716102edbSEunchul Kim event_work->ippdrv = ippdrv; 134816102edbSEunchul Kim event_work->buf_id[EXYNOS_DRM_OPS_DST] = buf_id; 134916102edbSEunchul Kim queue_work(ippdrv->event_workq, (struct work_struct *)event_work); 135016102edbSEunchul Kim 135116102edbSEunchul Kim return IRQ_HANDLED; 135216102edbSEunchul Kim } 135316102edbSEunchul Kim 135416102edbSEunchul Kim static int fimc_init_prop_list(struct exynos_drm_ippdrv *ippdrv) 135516102edbSEunchul Kim { 135616102edbSEunchul Kim struct drm_exynos_ipp_prop_list *prop_list; 135716102edbSEunchul Kim 135816102edbSEunchul Kim DRM_DEBUG_KMS("%s\n", __func__); 135916102edbSEunchul Kim 136016102edbSEunchul Kim prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL); 136116102edbSEunchul Kim if (!prop_list) { 136216102edbSEunchul Kim DRM_ERROR("failed to alloc property list.\n"); 136316102edbSEunchul Kim return -ENOMEM; 136416102edbSEunchul Kim } 136516102edbSEunchul Kim 136616102edbSEunchul Kim prop_list->version = 1; 136716102edbSEunchul Kim prop_list->writeback = 1; 136816102edbSEunchul Kim prop_list->refresh_min = FIMC_REFRESH_MIN; 136916102edbSEunchul Kim prop_list->refresh_max = FIMC_REFRESH_MAX; 137016102edbSEunchul Kim prop_list->flip = (1 << EXYNOS_DRM_FLIP_NONE) | 137116102edbSEunchul Kim (1 << EXYNOS_DRM_FLIP_VERTICAL) | 137216102edbSEunchul Kim (1 << EXYNOS_DRM_FLIP_HORIZONTAL); 137316102edbSEunchul Kim prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) | 137416102edbSEunchul Kim (1 << EXYNOS_DRM_DEGREE_90) | 137516102edbSEunchul Kim (1 << EXYNOS_DRM_DEGREE_180) | 137616102edbSEunchul Kim (1 << EXYNOS_DRM_DEGREE_270); 137716102edbSEunchul Kim prop_list->csc = 1; 137816102edbSEunchul Kim prop_list->crop = 1; 137916102edbSEunchul Kim prop_list->crop_max.hsize = FIMC_CROP_MAX; 138016102edbSEunchul Kim prop_list->crop_max.vsize = FIMC_CROP_MAX; 138116102edbSEunchul Kim prop_list->crop_min.hsize = FIMC_CROP_MIN; 138216102edbSEunchul Kim prop_list->crop_min.vsize = FIMC_CROP_MIN; 138316102edbSEunchul Kim prop_list->scale = 1; 138416102edbSEunchul Kim prop_list->scale_max.hsize = FIMC_SCALE_MAX; 138516102edbSEunchul Kim prop_list->scale_max.vsize = FIMC_SCALE_MAX; 138616102edbSEunchul Kim prop_list->scale_min.hsize = FIMC_SCALE_MIN; 138716102edbSEunchul Kim prop_list->scale_min.vsize = FIMC_SCALE_MIN; 138816102edbSEunchul Kim 138916102edbSEunchul Kim ippdrv->prop_list = prop_list; 139016102edbSEunchul Kim 139116102edbSEunchul Kim return 0; 139216102edbSEunchul Kim } 139316102edbSEunchul Kim 139416102edbSEunchul Kim static inline bool fimc_check_drm_flip(enum drm_exynos_flip flip) 139516102edbSEunchul Kim { 139616102edbSEunchul Kim switch (flip) { 139716102edbSEunchul Kim case EXYNOS_DRM_FLIP_NONE: 139816102edbSEunchul Kim case EXYNOS_DRM_FLIP_VERTICAL: 139916102edbSEunchul Kim case EXYNOS_DRM_FLIP_HORIZONTAL: 14004f21877cSEunchul Kim case EXYNOS_DRM_FLIP_BOTH: 140116102edbSEunchul Kim return true; 140216102edbSEunchul Kim default: 140316102edbSEunchul Kim DRM_DEBUG_KMS("%s:invalid flip\n", __func__); 140416102edbSEunchul Kim return false; 140516102edbSEunchul Kim } 140616102edbSEunchul Kim } 140716102edbSEunchul Kim 140816102edbSEunchul Kim static int fimc_ippdrv_check_property(struct device *dev, 140916102edbSEunchul Kim struct drm_exynos_ipp_property *property) 141016102edbSEunchul Kim { 141116102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 141216102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 141316102edbSEunchul Kim struct drm_exynos_ipp_prop_list *pp = ippdrv->prop_list; 141416102edbSEunchul Kim struct drm_exynos_ipp_config *config; 141516102edbSEunchul Kim struct drm_exynos_pos *pos; 141616102edbSEunchul Kim struct drm_exynos_sz *sz; 141716102edbSEunchul Kim bool swap; 141816102edbSEunchul Kim int i; 141916102edbSEunchul Kim 142016102edbSEunchul Kim DRM_DEBUG_KMS("%s\n", __func__); 142116102edbSEunchul Kim 142216102edbSEunchul Kim for_each_ipp_ops(i) { 142316102edbSEunchul Kim if ((i == EXYNOS_DRM_OPS_SRC) && 142416102edbSEunchul Kim (property->cmd == IPP_CMD_WB)) 142516102edbSEunchul Kim continue; 142616102edbSEunchul Kim 142716102edbSEunchul Kim config = &property->config[i]; 142816102edbSEunchul Kim pos = &config->pos; 142916102edbSEunchul Kim sz = &config->sz; 143016102edbSEunchul Kim 143116102edbSEunchul Kim /* check for flip */ 143216102edbSEunchul Kim if (!fimc_check_drm_flip(config->flip)) { 143316102edbSEunchul Kim DRM_ERROR("invalid flip.\n"); 143416102edbSEunchul Kim goto err_property; 143516102edbSEunchul Kim } 143616102edbSEunchul Kim 143716102edbSEunchul Kim /* check for degree */ 143816102edbSEunchul Kim switch (config->degree) { 143916102edbSEunchul Kim case EXYNOS_DRM_DEGREE_90: 144016102edbSEunchul Kim case EXYNOS_DRM_DEGREE_270: 144116102edbSEunchul Kim swap = true; 144216102edbSEunchul Kim break; 144316102edbSEunchul Kim case EXYNOS_DRM_DEGREE_0: 144416102edbSEunchul Kim case EXYNOS_DRM_DEGREE_180: 144516102edbSEunchul Kim swap = false; 144616102edbSEunchul Kim break; 144716102edbSEunchul Kim default: 144816102edbSEunchul Kim DRM_ERROR("invalid degree.\n"); 144916102edbSEunchul Kim goto err_property; 145016102edbSEunchul Kim } 145116102edbSEunchul Kim 145216102edbSEunchul Kim /* check for buffer bound */ 145316102edbSEunchul Kim if ((pos->x + pos->w > sz->hsize) || 145416102edbSEunchul Kim (pos->y + pos->h > sz->vsize)) { 145516102edbSEunchul Kim DRM_ERROR("out of buf bound.\n"); 145616102edbSEunchul Kim goto err_property; 145716102edbSEunchul Kim } 145816102edbSEunchul Kim 145916102edbSEunchul Kim /* check for crop */ 146016102edbSEunchul Kim if ((i == EXYNOS_DRM_OPS_SRC) && (pp->crop)) { 146116102edbSEunchul Kim if (swap) { 146216102edbSEunchul Kim if ((pos->h < pp->crop_min.hsize) || 146316102edbSEunchul Kim (sz->vsize > pp->crop_max.hsize) || 146416102edbSEunchul Kim (pos->w < pp->crop_min.vsize) || 146516102edbSEunchul Kim (sz->hsize > pp->crop_max.vsize)) { 146616102edbSEunchul Kim DRM_ERROR("out of crop size.\n"); 146716102edbSEunchul Kim goto err_property; 146816102edbSEunchul Kim } 146916102edbSEunchul Kim } else { 147016102edbSEunchul Kim if ((pos->w < pp->crop_min.hsize) || 147116102edbSEunchul Kim (sz->hsize > pp->crop_max.hsize) || 147216102edbSEunchul Kim (pos->h < pp->crop_min.vsize) || 147316102edbSEunchul Kim (sz->vsize > pp->crop_max.vsize)) { 147416102edbSEunchul Kim DRM_ERROR("out of crop size.\n"); 147516102edbSEunchul Kim goto err_property; 147616102edbSEunchul Kim } 147716102edbSEunchul Kim } 147816102edbSEunchul Kim } 147916102edbSEunchul Kim 148016102edbSEunchul Kim /* check for scale */ 148116102edbSEunchul Kim if ((i == EXYNOS_DRM_OPS_DST) && (pp->scale)) { 148216102edbSEunchul Kim if (swap) { 148316102edbSEunchul Kim if ((pos->h < pp->scale_min.hsize) || 148416102edbSEunchul Kim (sz->vsize > pp->scale_max.hsize) || 148516102edbSEunchul Kim (pos->w < pp->scale_min.vsize) || 148616102edbSEunchul Kim (sz->hsize > pp->scale_max.vsize)) { 148716102edbSEunchul Kim DRM_ERROR("out of scale size.\n"); 148816102edbSEunchul Kim goto err_property; 148916102edbSEunchul Kim } 149016102edbSEunchul Kim } else { 149116102edbSEunchul Kim if ((pos->w < pp->scale_min.hsize) || 149216102edbSEunchul Kim (sz->hsize > pp->scale_max.hsize) || 149316102edbSEunchul Kim (pos->h < pp->scale_min.vsize) || 149416102edbSEunchul Kim (sz->vsize > pp->scale_max.vsize)) { 149516102edbSEunchul Kim DRM_ERROR("out of scale size.\n"); 149616102edbSEunchul Kim goto err_property; 149716102edbSEunchul Kim } 149816102edbSEunchul Kim } 149916102edbSEunchul Kim } 150016102edbSEunchul Kim } 150116102edbSEunchul Kim 150216102edbSEunchul Kim return 0; 150316102edbSEunchul Kim 150416102edbSEunchul Kim err_property: 150516102edbSEunchul Kim for_each_ipp_ops(i) { 150616102edbSEunchul Kim if ((i == EXYNOS_DRM_OPS_SRC) && 150716102edbSEunchul Kim (property->cmd == IPP_CMD_WB)) 150816102edbSEunchul Kim continue; 150916102edbSEunchul Kim 151016102edbSEunchul Kim config = &property->config[i]; 151116102edbSEunchul Kim pos = &config->pos; 151216102edbSEunchul Kim sz = &config->sz; 151316102edbSEunchul Kim 151416102edbSEunchul Kim DRM_ERROR("[%s]f[%d]r[%d]pos[%d %d %d %d]sz[%d %d]\n", 151516102edbSEunchul Kim i ? "dst" : "src", config->flip, config->degree, 151616102edbSEunchul Kim pos->x, pos->y, pos->w, pos->h, 151716102edbSEunchul Kim sz->hsize, sz->vsize); 151816102edbSEunchul Kim } 151916102edbSEunchul Kim 152016102edbSEunchul Kim return -EINVAL; 152116102edbSEunchul Kim } 152216102edbSEunchul Kim 152316102edbSEunchul Kim static void fimc_clear_addr(struct fimc_context *ctx) 152416102edbSEunchul Kim { 152516102edbSEunchul Kim int i; 152616102edbSEunchul Kim 152716102edbSEunchul Kim DRM_DEBUG_KMS("%s:\n", __func__); 152816102edbSEunchul Kim 152916102edbSEunchul Kim for (i = 0; i < FIMC_MAX_SRC; i++) { 153016102edbSEunchul Kim fimc_write(0, EXYNOS_CIIYSA(i)); 153116102edbSEunchul Kim fimc_write(0, EXYNOS_CIICBSA(i)); 153216102edbSEunchul Kim fimc_write(0, EXYNOS_CIICRSA(i)); 153316102edbSEunchul Kim } 153416102edbSEunchul Kim 153516102edbSEunchul Kim for (i = 0; i < FIMC_MAX_DST; i++) { 153616102edbSEunchul Kim fimc_write(0, EXYNOS_CIOYSA(i)); 153716102edbSEunchul Kim fimc_write(0, EXYNOS_CIOCBSA(i)); 153816102edbSEunchul Kim fimc_write(0, EXYNOS_CIOCRSA(i)); 153916102edbSEunchul Kim } 154016102edbSEunchul Kim } 154116102edbSEunchul Kim 154216102edbSEunchul Kim static int fimc_ippdrv_reset(struct device *dev) 154316102edbSEunchul Kim { 154416102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 154516102edbSEunchul Kim 154616102edbSEunchul Kim DRM_DEBUG_KMS("%s\n", __func__); 154716102edbSEunchul Kim 154816102edbSEunchul Kim /* reset h/w block */ 1549b5c0b552SJoongMock Shin fimc_sw_reset(ctx); 155016102edbSEunchul Kim 155116102edbSEunchul Kim /* reset scaler capability */ 155216102edbSEunchul Kim memset(&ctx->sc, 0x0, sizeof(ctx->sc)); 155316102edbSEunchul Kim 155416102edbSEunchul Kim fimc_clear_addr(ctx); 155516102edbSEunchul Kim 155616102edbSEunchul Kim return 0; 155716102edbSEunchul Kim } 155816102edbSEunchul Kim 155916102edbSEunchul Kim static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) 156016102edbSEunchul Kim { 156116102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 156216102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 15637259c3d6SEunchul Kim struct drm_exynos_ipp_cmd_node *c_node = ippdrv->c_node; 156416102edbSEunchul Kim struct drm_exynos_ipp_property *property; 156516102edbSEunchul Kim struct drm_exynos_ipp_config *config; 156616102edbSEunchul Kim struct drm_exynos_pos img_pos[EXYNOS_DRM_OPS_MAX]; 156716102edbSEunchul Kim struct drm_exynos_ipp_set_wb set_wb; 156816102edbSEunchul Kim int ret, i; 156916102edbSEunchul Kim u32 cfg0, cfg1; 157016102edbSEunchul Kim 157116102edbSEunchul Kim DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); 157216102edbSEunchul Kim 157316102edbSEunchul Kim if (!c_node) { 157416102edbSEunchul Kim DRM_ERROR("failed to get c_node.\n"); 157516102edbSEunchul Kim return -EINVAL; 157616102edbSEunchul Kim } 157716102edbSEunchul Kim 157816102edbSEunchul Kim property = &c_node->property; 157916102edbSEunchul Kim 158016102edbSEunchul Kim fimc_handle_irq(ctx, true, false, true); 158116102edbSEunchul Kim 158216102edbSEunchul Kim for_each_ipp_ops(i) { 158316102edbSEunchul Kim config = &property->config[i]; 158416102edbSEunchul Kim img_pos[i] = config->pos; 158516102edbSEunchul Kim } 158616102edbSEunchul Kim 158716102edbSEunchul Kim ret = fimc_set_prescaler(ctx, &ctx->sc, 158816102edbSEunchul Kim &img_pos[EXYNOS_DRM_OPS_SRC], 158916102edbSEunchul Kim &img_pos[EXYNOS_DRM_OPS_DST]); 159016102edbSEunchul Kim if (ret) { 159116102edbSEunchul Kim dev_err(dev, "failed to set precalser.\n"); 159216102edbSEunchul Kim return ret; 159316102edbSEunchul Kim } 159416102edbSEunchul Kim 159516102edbSEunchul Kim /* If set ture, we can save jpeg about screen */ 159616102edbSEunchul Kim fimc_handle_jpeg(ctx, false); 159716102edbSEunchul Kim fimc_set_scaler(ctx, &ctx->sc); 159816102edbSEunchul Kim fimc_set_polarity(ctx, &ctx->pol); 159916102edbSEunchul Kim 160016102edbSEunchul Kim switch (cmd) { 160116102edbSEunchul Kim case IPP_CMD_M2M: 160216102edbSEunchul Kim fimc_set_type_ctrl(ctx, FIMC_WB_NONE); 160316102edbSEunchul Kim fimc_handle_lastend(ctx, false); 160416102edbSEunchul Kim 160516102edbSEunchul Kim /* setup dma */ 160616102edbSEunchul Kim cfg0 = fimc_read(EXYNOS_MSCTRL); 160716102edbSEunchul Kim cfg0 &= ~EXYNOS_MSCTRL_INPUT_MASK; 160816102edbSEunchul Kim cfg0 |= EXYNOS_MSCTRL_INPUT_MEMORY; 160916102edbSEunchul Kim fimc_write(cfg0, EXYNOS_MSCTRL); 161016102edbSEunchul Kim break; 161116102edbSEunchul Kim case IPP_CMD_WB: 161216102edbSEunchul Kim fimc_set_type_ctrl(ctx, FIMC_WB_A); 161316102edbSEunchul Kim fimc_handle_lastend(ctx, true); 161416102edbSEunchul Kim 161516102edbSEunchul Kim /* setup FIMD */ 161616102edbSEunchul Kim fimc_set_camblk_fimd0_wb(ctx); 161716102edbSEunchul Kim 161816102edbSEunchul Kim set_wb.enable = 1; 161916102edbSEunchul Kim set_wb.refresh = property->refresh_rate; 162016102edbSEunchul Kim exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); 162116102edbSEunchul Kim break; 162216102edbSEunchul Kim case IPP_CMD_OUTPUT: 162316102edbSEunchul Kim default: 162416102edbSEunchul Kim ret = -EINVAL; 162516102edbSEunchul Kim dev_err(dev, "invalid operations.\n"); 162616102edbSEunchul Kim return ret; 162716102edbSEunchul Kim } 162816102edbSEunchul Kim 162916102edbSEunchul Kim /* Reset status */ 163016102edbSEunchul Kim fimc_write(0x0, EXYNOS_CISTATUS); 163116102edbSEunchul Kim 163216102edbSEunchul Kim cfg0 = fimc_read(EXYNOS_CIIMGCPT); 163316102edbSEunchul Kim cfg0 &= ~EXYNOS_CIIMGCPT_IMGCPTEN_SC; 163416102edbSEunchul Kim cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN_SC; 163516102edbSEunchul Kim 163616102edbSEunchul Kim /* Scaler */ 163716102edbSEunchul Kim cfg1 = fimc_read(EXYNOS_CISCCTRL); 163816102edbSEunchul Kim cfg1 &= ~EXYNOS_CISCCTRL_SCAN_MASK; 163916102edbSEunchul Kim cfg1 |= (EXYNOS_CISCCTRL_PROGRESSIVE | 164016102edbSEunchul Kim EXYNOS_CISCCTRL_SCALERSTART); 164116102edbSEunchul Kim 164216102edbSEunchul Kim fimc_write(cfg1, EXYNOS_CISCCTRL); 164316102edbSEunchul Kim 164416102edbSEunchul Kim /* Enable image capture*/ 164516102edbSEunchul Kim cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN; 164616102edbSEunchul Kim fimc_write(cfg0, EXYNOS_CIIMGCPT); 164716102edbSEunchul Kim 164816102edbSEunchul Kim /* Disable frame end irq */ 164916102edbSEunchul Kim cfg0 = fimc_read(EXYNOS_CIGCTRL); 165016102edbSEunchul Kim cfg0 &= ~EXYNOS_CIGCTRL_IRQ_END_DISABLE; 165116102edbSEunchul Kim fimc_write(cfg0, EXYNOS_CIGCTRL); 165216102edbSEunchul Kim 165316102edbSEunchul Kim cfg0 = fimc_read(EXYNOS_CIOCTRL); 165416102edbSEunchul Kim cfg0 &= ~EXYNOS_CIOCTRL_WEAVE_MASK; 165516102edbSEunchul Kim fimc_write(cfg0, EXYNOS_CIOCTRL); 165616102edbSEunchul Kim 165716102edbSEunchul Kim if (cmd == IPP_CMD_M2M) { 165816102edbSEunchul Kim cfg0 = fimc_read(EXYNOS_MSCTRL); 165916102edbSEunchul Kim cfg0 |= EXYNOS_MSCTRL_ENVID; 166016102edbSEunchul Kim fimc_write(cfg0, EXYNOS_MSCTRL); 166116102edbSEunchul Kim 166216102edbSEunchul Kim cfg0 = fimc_read(EXYNOS_MSCTRL); 166316102edbSEunchul Kim cfg0 |= EXYNOS_MSCTRL_ENVID; 166416102edbSEunchul Kim fimc_write(cfg0, EXYNOS_MSCTRL); 166516102edbSEunchul Kim } 166616102edbSEunchul Kim 166716102edbSEunchul Kim return 0; 166816102edbSEunchul Kim } 166916102edbSEunchul Kim 167016102edbSEunchul Kim static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) 167116102edbSEunchul Kim { 167216102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 167316102edbSEunchul Kim struct drm_exynos_ipp_set_wb set_wb = {0, 0}; 167416102edbSEunchul Kim u32 cfg; 167516102edbSEunchul Kim 167616102edbSEunchul Kim DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); 167716102edbSEunchul Kim 167816102edbSEunchul Kim switch (cmd) { 167916102edbSEunchul Kim case IPP_CMD_M2M: 168016102edbSEunchul Kim /* Source clear */ 168116102edbSEunchul Kim cfg = fimc_read(EXYNOS_MSCTRL); 168216102edbSEunchul Kim cfg &= ~EXYNOS_MSCTRL_INPUT_MASK; 168316102edbSEunchul Kim cfg &= ~EXYNOS_MSCTRL_ENVID; 168416102edbSEunchul Kim fimc_write(cfg, EXYNOS_MSCTRL); 168516102edbSEunchul Kim break; 168616102edbSEunchul Kim case IPP_CMD_WB: 168716102edbSEunchul Kim exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); 168816102edbSEunchul Kim break; 168916102edbSEunchul Kim case IPP_CMD_OUTPUT: 169016102edbSEunchul Kim default: 169116102edbSEunchul Kim dev_err(dev, "invalid operations.\n"); 169216102edbSEunchul Kim break; 169316102edbSEunchul Kim } 169416102edbSEunchul Kim 169516102edbSEunchul Kim fimc_handle_irq(ctx, false, false, true); 169616102edbSEunchul Kim 169716102edbSEunchul Kim /* reset sequence */ 169816102edbSEunchul Kim fimc_write(0x0, EXYNOS_CIFCNTSEQ); 169916102edbSEunchul Kim 170016102edbSEunchul Kim /* Scaler disable */ 170116102edbSEunchul Kim cfg = fimc_read(EXYNOS_CISCCTRL); 170216102edbSEunchul Kim cfg &= ~EXYNOS_CISCCTRL_SCALERSTART; 170316102edbSEunchul Kim fimc_write(cfg, EXYNOS_CISCCTRL); 170416102edbSEunchul Kim 170516102edbSEunchul Kim /* Disable image capture */ 170616102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIIMGCPT); 170716102edbSEunchul Kim cfg &= ~(EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN); 170816102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIIMGCPT); 170916102edbSEunchul Kim 171016102edbSEunchul Kim /* Enable frame end irq */ 171116102edbSEunchul Kim cfg = fimc_read(EXYNOS_CIGCTRL); 171216102edbSEunchul Kim cfg |= EXYNOS_CIGCTRL_IRQ_END_DISABLE; 171316102edbSEunchul Kim fimc_write(cfg, EXYNOS_CIGCTRL); 171416102edbSEunchul Kim } 171516102edbSEunchul Kim 171616102edbSEunchul Kim static int __devinit fimc_probe(struct platform_device *pdev) 171716102edbSEunchul Kim { 171816102edbSEunchul Kim struct device *dev = &pdev->dev; 171916102edbSEunchul Kim struct fimc_context *ctx; 172016102edbSEunchul Kim struct clk *parent_clk; 172116102edbSEunchul Kim struct resource *res; 172216102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv; 172316102edbSEunchul Kim struct exynos_drm_fimc_pdata *pdata; 172416102edbSEunchul Kim struct fimc_driverdata *ddata; 172516102edbSEunchul Kim int ret; 172616102edbSEunchul Kim 172716102edbSEunchul Kim pdata = pdev->dev.platform_data; 172816102edbSEunchul Kim if (!pdata) { 172916102edbSEunchul Kim dev_err(dev, "no platform data specified.\n"); 173016102edbSEunchul Kim return -EINVAL; 173116102edbSEunchul Kim } 173216102edbSEunchul Kim 173316102edbSEunchul Kim ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 173416102edbSEunchul Kim if (!ctx) 173516102edbSEunchul Kim return -ENOMEM; 173616102edbSEunchul Kim 173716102edbSEunchul Kim ddata = (struct fimc_driverdata *) 173816102edbSEunchul Kim platform_get_device_id(pdev)->driver_data; 173916102edbSEunchul Kim 174016102edbSEunchul Kim /* clock control */ 174116102edbSEunchul Kim ctx->sclk_fimc_clk = clk_get(dev, "sclk_fimc"); 174216102edbSEunchul Kim if (IS_ERR(ctx->sclk_fimc_clk)) { 174316102edbSEunchul Kim dev_err(dev, "failed to get src fimc clock.\n"); 174416102edbSEunchul Kim ret = PTR_ERR(ctx->sclk_fimc_clk); 174516102edbSEunchul Kim goto err_ctx; 174616102edbSEunchul Kim } 174716102edbSEunchul Kim clk_enable(ctx->sclk_fimc_clk); 174816102edbSEunchul Kim 174916102edbSEunchul Kim ctx->fimc_clk = clk_get(dev, "fimc"); 175016102edbSEunchul Kim if (IS_ERR(ctx->fimc_clk)) { 175116102edbSEunchul Kim dev_err(dev, "failed to get fimc clock.\n"); 175216102edbSEunchul Kim ret = PTR_ERR(ctx->fimc_clk); 175316102edbSEunchul Kim clk_disable(ctx->sclk_fimc_clk); 175416102edbSEunchul Kim clk_put(ctx->sclk_fimc_clk); 175516102edbSEunchul Kim goto err_ctx; 175616102edbSEunchul Kim } 175716102edbSEunchul Kim 175816102edbSEunchul Kim ctx->wb_clk = clk_get(dev, "pxl_async0"); 175916102edbSEunchul Kim if (IS_ERR(ctx->wb_clk)) { 176016102edbSEunchul Kim dev_err(dev, "failed to get writeback a clock.\n"); 176116102edbSEunchul Kim ret = PTR_ERR(ctx->wb_clk); 176216102edbSEunchul Kim clk_disable(ctx->sclk_fimc_clk); 176316102edbSEunchul Kim clk_put(ctx->sclk_fimc_clk); 176416102edbSEunchul Kim clk_put(ctx->fimc_clk); 176516102edbSEunchul Kim goto err_ctx; 176616102edbSEunchul Kim } 176716102edbSEunchul Kim 176816102edbSEunchul Kim ctx->wb_b_clk = clk_get(dev, "pxl_async1"); 176916102edbSEunchul Kim if (IS_ERR(ctx->wb_b_clk)) { 177016102edbSEunchul Kim dev_err(dev, "failed to get writeback b clock.\n"); 177116102edbSEunchul Kim ret = PTR_ERR(ctx->wb_b_clk); 177216102edbSEunchul Kim clk_disable(ctx->sclk_fimc_clk); 177316102edbSEunchul Kim clk_put(ctx->sclk_fimc_clk); 177416102edbSEunchul Kim clk_put(ctx->fimc_clk); 177516102edbSEunchul Kim clk_put(ctx->wb_clk); 177616102edbSEunchul Kim goto err_ctx; 177716102edbSEunchul Kim } 177816102edbSEunchul Kim 177916102edbSEunchul Kim parent_clk = clk_get(dev, ddata->parent_clk); 178016102edbSEunchul Kim 178116102edbSEunchul Kim if (IS_ERR(parent_clk)) { 178216102edbSEunchul Kim dev_err(dev, "failed to get parent clock.\n"); 178316102edbSEunchul Kim ret = PTR_ERR(parent_clk); 178416102edbSEunchul Kim clk_disable(ctx->sclk_fimc_clk); 178516102edbSEunchul Kim clk_put(ctx->sclk_fimc_clk); 178616102edbSEunchul Kim clk_put(ctx->fimc_clk); 178716102edbSEunchul Kim clk_put(ctx->wb_clk); 178816102edbSEunchul Kim clk_put(ctx->wb_b_clk); 178916102edbSEunchul Kim goto err_ctx; 179016102edbSEunchul Kim } 179116102edbSEunchul Kim 179216102edbSEunchul Kim if (clk_set_parent(ctx->sclk_fimc_clk, parent_clk)) { 179316102edbSEunchul Kim dev_err(dev, "failed to set parent.\n"); 179416102edbSEunchul Kim ret = -EINVAL; 179516102edbSEunchul Kim clk_put(parent_clk); 179616102edbSEunchul Kim clk_disable(ctx->sclk_fimc_clk); 179716102edbSEunchul Kim clk_put(ctx->sclk_fimc_clk); 179816102edbSEunchul Kim clk_put(ctx->fimc_clk); 179916102edbSEunchul Kim clk_put(ctx->wb_clk); 180016102edbSEunchul Kim clk_put(ctx->wb_b_clk); 180116102edbSEunchul Kim goto err_ctx; 180216102edbSEunchul Kim } 180316102edbSEunchul Kim 180416102edbSEunchul Kim clk_put(parent_clk); 180516102edbSEunchul Kim clk_set_rate(ctx->sclk_fimc_clk, pdata->clk_rate); 180616102edbSEunchul Kim 180716102edbSEunchul Kim /* resource memory */ 180816102edbSEunchul Kim ctx->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 180916102edbSEunchul Kim if (!ctx->regs_res) { 181016102edbSEunchul Kim dev_err(dev, "failed to find registers.\n"); 181116102edbSEunchul Kim ret = -ENOENT; 181216102edbSEunchul Kim goto err_clk; 181316102edbSEunchul Kim } 181416102edbSEunchul Kim 181516102edbSEunchul Kim ctx->regs = devm_request_and_ioremap(dev, ctx->regs_res); 181616102edbSEunchul Kim if (!ctx->regs) { 181716102edbSEunchul Kim dev_err(dev, "failed to map registers.\n"); 181816102edbSEunchul Kim ret = -ENXIO; 181916102edbSEunchul Kim goto err_clk; 182016102edbSEunchul Kim } 182116102edbSEunchul Kim 182216102edbSEunchul Kim /* resource irq */ 182316102edbSEunchul Kim res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 182416102edbSEunchul Kim if (!res) { 182516102edbSEunchul Kim dev_err(dev, "failed to request irq resource.\n"); 182616102edbSEunchul Kim ret = -ENOENT; 182716102edbSEunchul Kim goto err_get_regs; 182816102edbSEunchul Kim } 182916102edbSEunchul Kim 183016102edbSEunchul Kim ctx->irq = res->start; 183116102edbSEunchul Kim ret = request_threaded_irq(ctx->irq, NULL, fimc_irq_handler, 183216102edbSEunchul Kim IRQF_ONESHOT, "drm_fimc", ctx); 183316102edbSEunchul Kim if (ret < 0) { 183416102edbSEunchul Kim dev_err(dev, "failed to request irq.\n"); 183516102edbSEunchul Kim goto err_get_regs; 183616102edbSEunchul Kim } 183716102edbSEunchul Kim 183816102edbSEunchul Kim /* context initailization */ 183916102edbSEunchul Kim ctx->id = pdev->id; 184016102edbSEunchul Kim ctx->pol = pdata->pol; 184116102edbSEunchul Kim ctx->ddata = ddata; 184216102edbSEunchul Kim 184316102edbSEunchul Kim ippdrv = &ctx->ippdrv; 184416102edbSEunchul Kim ippdrv->dev = dev; 184516102edbSEunchul Kim ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &fimc_src_ops; 184616102edbSEunchul Kim ippdrv->ops[EXYNOS_DRM_OPS_DST] = &fimc_dst_ops; 184716102edbSEunchul Kim ippdrv->check_property = fimc_ippdrv_check_property; 184816102edbSEunchul Kim ippdrv->reset = fimc_ippdrv_reset; 184916102edbSEunchul Kim ippdrv->start = fimc_ippdrv_start; 185016102edbSEunchul Kim ippdrv->stop = fimc_ippdrv_stop; 185116102edbSEunchul Kim ret = fimc_init_prop_list(ippdrv); 185216102edbSEunchul Kim if (ret < 0) { 185316102edbSEunchul Kim dev_err(dev, "failed to init property list.\n"); 185416102edbSEunchul Kim goto err_get_irq; 185516102edbSEunchul Kim } 185616102edbSEunchul Kim 185716102edbSEunchul Kim DRM_DEBUG_KMS("%s:id[%d]ippdrv[0x%x]\n", __func__, ctx->id, 185816102edbSEunchul Kim (int)ippdrv); 185916102edbSEunchul Kim 186016102edbSEunchul Kim mutex_init(&ctx->lock); 186116102edbSEunchul Kim platform_set_drvdata(pdev, ctx); 186216102edbSEunchul Kim 186316102edbSEunchul Kim pm_runtime_set_active(dev); 186416102edbSEunchul Kim pm_runtime_enable(dev); 186516102edbSEunchul Kim 186616102edbSEunchul Kim ret = exynos_drm_ippdrv_register(ippdrv); 186716102edbSEunchul Kim if (ret < 0) { 186816102edbSEunchul Kim dev_err(dev, "failed to register drm fimc device.\n"); 186916102edbSEunchul Kim goto err_ippdrv_register; 187016102edbSEunchul Kim } 187116102edbSEunchul Kim 187216102edbSEunchul Kim dev_info(&pdev->dev, "drm fimc registered successfully.\n"); 187316102edbSEunchul Kim 187416102edbSEunchul Kim return 0; 187516102edbSEunchul Kim 187616102edbSEunchul Kim err_ippdrv_register: 187716102edbSEunchul Kim devm_kfree(dev, ippdrv->prop_list); 187816102edbSEunchul Kim pm_runtime_disable(dev); 187916102edbSEunchul Kim err_get_irq: 188016102edbSEunchul Kim free_irq(ctx->irq, ctx); 188116102edbSEunchul Kim err_get_regs: 188216102edbSEunchul Kim devm_iounmap(dev, ctx->regs); 188316102edbSEunchul Kim err_clk: 188416102edbSEunchul Kim clk_put(ctx->sclk_fimc_clk); 188516102edbSEunchul Kim clk_put(ctx->fimc_clk); 188616102edbSEunchul Kim clk_put(ctx->wb_clk); 188716102edbSEunchul Kim clk_put(ctx->wb_b_clk); 188816102edbSEunchul Kim err_ctx: 188916102edbSEunchul Kim devm_kfree(dev, ctx); 189016102edbSEunchul Kim return ret; 189116102edbSEunchul Kim } 189216102edbSEunchul Kim 189316102edbSEunchul Kim static int __devexit fimc_remove(struct platform_device *pdev) 189416102edbSEunchul Kim { 189516102edbSEunchul Kim struct device *dev = &pdev->dev; 189616102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 189716102edbSEunchul Kim struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 189816102edbSEunchul Kim 189916102edbSEunchul Kim devm_kfree(dev, ippdrv->prop_list); 190016102edbSEunchul Kim exynos_drm_ippdrv_unregister(ippdrv); 190116102edbSEunchul Kim mutex_destroy(&ctx->lock); 190216102edbSEunchul Kim 190316102edbSEunchul Kim pm_runtime_set_suspended(dev); 190416102edbSEunchul Kim pm_runtime_disable(dev); 190516102edbSEunchul Kim 190616102edbSEunchul Kim free_irq(ctx->irq, ctx); 190716102edbSEunchul Kim devm_iounmap(dev, ctx->regs); 190816102edbSEunchul Kim 190916102edbSEunchul Kim clk_put(ctx->sclk_fimc_clk); 191016102edbSEunchul Kim clk_put(ctx->fimc_clk); 191116102edbSEunchul Kim clk_put(ctx->wb_clk); 191216102edbSEunchul Kim clk_put(ctx->wb_b_clk); 191316102edbSEunchul Kim 191416102edbSEunchul Kim devm_kfree(dev, ctx); 191516102edbSEunchul Kim 191616102edbSEunchul Kim return 0; 191716102edbSEunchul Kim } 191816102edbSEunchul Kim 191916102edbSEunchul Kim #ifdef CONFIG_PM_SLEEP 192016102edbSEunchul Kim static int fimc_suspend(struct device *dev) 192116102edbSEunchul Kim { 192216102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 192316102edbSEunchul Kim 192416102edbSEunchul Kim DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); 192516102edbSEunchul Kim 192616102edbSEunchul Kim if (pm_runtime_suspended(dev)) 192716102edbSEunchul Kim return 0; 192816102edbSEunchul Kim 192916102edbSEunchul Kim return fimc_clk_ctrl(ctx, false); 193016102edbSEunchul Kim } 193116102edbSEunchul Kim 193216102edbSEunchul Kim static int fimc_resume(struct device *dev) 193316102edbSEunchul Kim { 193416102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 193516102edbSEunchul Kim 193616102edbSEunchul Kim DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); 193716102edbSEunchul Kim 193816102edbSEunchul Kim if (!pm_runtime_suspended(dev)) 193916102edbSEunchul Kim return fimc_clk_ctrl(ctx, true); 194016102edbSEunchul Kim 194116102edbSEunchul Kim return 0; 194216102edbSEunchul Kim } 194316102edbSEunchul Kim #endif 194416102edbSEunchul Kim 194516102edbSEunchul Kim #ifdef CONFIG_PM_RUNTIME 194616102edbSEunchul Kim static int fimc_runtime_suspend(struct device *dev) 194716102edbSEunchul Kim { 194816102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 194916102edbSEunchul Kim 195016102edbSEunchul Kim DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); 195116102edbSEunchul Kim 195216102edbSEunchul Kim return fimc_clk_ctrl(ctx, false); 195316102edbSEunchul Kim } 195416102edbSEunchul Kim 195516102edbSEunchul Kim static int fimc_runtime_resume(struct device *dev) 195616102edbSEunchul Kim { 195716102edbSEunchul Kim struct fimc_context *ctx = get_fimc_context(dev); 195816102edbSEunchul Kim 195916102edbSEunchul Kim DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); 196016102edbSEunchul Kim 196116102edbSEunchul Kim return fimc_clk_ctrl(ctx, true); 196216102edbSEunchul Kim } 196316102edbSEunchul Kim #endif 196416102edbSEunchul Kim 196516102edbSEunchul Kim static struct fimc_driverdata exynos4210_fimc_data = { 196616102edbSEunchul Kim .parent_clk = "mout_mpll", 196716102edbSEunchul Kim }; 196816102edbSEunchul Kim 196916102edbSEunchul Kim static struct fimc_driverdata exynos4410_fimc_data = { 197016102edbSEunchul Kim .parent_clk = "mout_mpll_user", 197116102edbSEunchul Kim }; 197216102edbSEunchul Kim 197316102edbSEunchul Kim static struct platform_device_id fimc_driver_ids[] = { 197416102edbSEunchul Kim { 197516102edbSEunchul Kim .name = "exynos4210-fimc", 197616102edbSEunchul Kim .driver_data = (unsigned long)&exynos4210_fimc_data, 197716102edbSEunchul Kim }, { 197816102edbSEunchul Kim .name = "exynos4412-fimc", 197916102edbSEunchul Kim .driver_data = (unsigned long)&exynos4410_fimc_data, 198016102edbSEunchul Kim }, 198116102edbSEunchul Kim {}, 198216102edbSEunchul Kim }; 198316102edbSEunchul Kim MODULE_DEVICE_TABLE(platform, fimc_driver_ids); 198416102edbSEunchul Kim 198516102edbSEunchul Kim static const struct dev_pm_ops fimc_pm_ops = { 198616102edbSEunchul Kim SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume) 198716102edbSEunchul Kim SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL) 198816102edbSEunchul Kim }; 198916102edbSEunchul Kim 199016102edbSEunchul Kim struct platform_driver fimc_driver = { 199116102edbSEunchul Kim .probe = fimc_probe, 199216102edbSEunchul Kim .remove = __devexit_p(fimc_remove), 199316102edbSEunchul Kim .id_table = fimc_driver_ids, 199416102edbSEunchul Kim .driver = { 199516102edbSEunchul Kim .name = "exynos-drm-fimc", 199616102edbSEunchul Kim .owner = THIS_MODULE, 199716102edbSEunchul Kim .pm = &fimc_pm_ops, 199816102edbSEunchul Kim }, 199916102edbSEunchul Kim }; 200016102edbSEunchul Kim 2001