2012-12-14 12:58:55 +04:00
/*
* Copyright ( C ) 2012 Samsung Electronics Co . Ltd
* Authors :
* Eunchul Kim < chulspro . kim @ samsung . com >
* Jinyoung Jeon < jy0 . jeon @ samsung . com >
* Sangmin Lee < lsmin . lee @ samsung . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
*/
# include <linux/kernel.h>
# include <linux/platform_device.h>
2013-05-22 16:14:15 +04:00
# include <linux/mfd/syscon.h>
2013-04-23 15:34:38 +04:00
# include <linux/regmap.h>
2012-12-14 12:58:55 +04:00
# include <linux/clk.h>
# include <linux/pm_runtime.h>
2013-08-14 15:08:01 +04:00
# include <linux/of.h>
2014-05-19 14:54:09 +04:00
# include <linux/spinlock.h>
2012-12-14 12:58:55 +04:00
# include <drm/drmP.h>
# include <drm/exynos_drm.h>
# include "regs-fimc.h"
2013-08-13 03:46:40 +04:00
# include "exynos_drm_drv.h"
2012-12-14 12:58:55 +04:00
# include "exynos_drm_ipp.h"
# include "exynos_drm_fimc.h"
/*
2012-12-22 12:49:26 +04:00
* FIMC stands for Fully Interactive Mobile Camera and
2012-12-14 12:58:55 +04:00
* supports image scaler / rotator and input / output DMA operations .
* input DMA reads image data from the memory .
* output DMA writes image data to memory .
* FIMC supports image rotation and image effect functions .
*
* M2M operation : supports crop / scale / rotation / csc so on .
* Memory - - - - > FIMC H / W - - - - > Memory .
* Writeback operation : supports cloned screen with FIMD .
* FIMD - - - - > FIMC H / W - - - - > Memory .
* Output operation : supports direct display using local path .
* Memory - - - - > FIMC H / W - - - - > FIMD .
*/
/*
* TODO
* 1. check suspend / resume api if needed .
* 2. need to check use case platform_device_id .
* 3. check src / dst size with , height .
* 4. added check_prepare api for right register .
* 5. need to add supported list in prop_list .
* 6. check prescaler / scaler optimization .
*/
# define FIMC_MAX_DEVS 4
# define FIMC_MAX_SRC 2
# define FIMC_MAX_DST 32
# define FIMC_SHFACTOR 10
# define FIMC_BUF_STOP 1
# define FIMC_BUF_START 2
# define FIMC_WIDTH_ITU_709 1280
# define FIMC_REFRESH_MAX 60
# define FIMC_REFRESH_MIN 12
# define FIMC_CROP_MAX 8192
# define FIMC_CROP_MIN 32
# define FIMC_SCALE_MAX 4224
# define FIMC_SCALE_MIN 32
# define get_fimc_context(dev) platform_get_drvdata(to_platform_device(dev))
# define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\
struct fimc_context , ippdrv ) ;
enum fimc_wb {
FIMC_WB_NONE ,
FIMC_WB_A ,
FIMC_WB_B ,
} ;
2013-04-23 15:34:37 +04:00
enum {
FIMC_CLK_LCLK ,
FIMC_CLK_GATE ,
FIMC_CLK_WB_A ,
FIMC_CLK_WB_B ,
FIMC_CLK_MUX ,
FIMC_CLK_PARENT ,
FIMC_CLKS_MAX
} ;
static const char * const fimc_clock_names [ ] = {
[ FIMC_CLK_LCLK ] = " sclk_fimc " ,
[ FIMC_CLK_GATE ] = " fimc " ,
[ FIMC_CLK_WB_A ] = " pxl_async0 " ,
[ FIMC_CLK_WB_B ] = " pxl_async1 " ,
[ FIMC_CLK_MUX ] = " mux " ,
[ FIMC_CLK_PARENT ] = " parent " ,
} ;
# define FIMC_DEFAULT_LCLK_FREQUENCY 133000000UL
2012-12-14 12:58:55 +04:00
/*
* A structure of scaler .
*
* @ range : narrow , wide .
* @ bypass : unused scaler path .
* @ up_h : horizontal scale up .
* @ up_v : vertical scale up .
* @ hratio : horizontal ratio .
* @ vratio : vertical ratio .
*/
struct fimc_scaler {
bool range ;
bool bypass ;
bool up_h ;
bool up_v ;
u32 hratio ;
u32 vratio ;
} ;
/*
* A structure of scaler capability .
*
* find user manual table 43 - 1.
* @ in_hori : scaler input horizontal size .
* @ bypass : scaler bypass mode .
* @ dst_h_wo_rot : target horizontal size without output rotation .
* @ dst_h_rot : target horizontal size with output rotation .
* @ rl_w_wo_rot : real width without input rotation .
* @ rl_h_rot : real height without output rotation .
*/
struct fimc_capability {
/* scaler */
u32 in_hori ;
u32 bypass ;
/* output rotator */
u32 dst_h_wo_rot ;
u32 dst_h_rot ;
/* input rotator */
u32 rl_w_wo_rot ;
u32 rl_h_rot ;
} ;
/*
* A structure of fimc context .
*
* @ ippdrv : prepare initialization using ippdrv .
* @ regs_res : register resources .
* @ regs : memory mapped io registers .
* @ lock : locking of operations .
2013-04-23 15:34:37 +04:00
* @ clocks : fimc clocks .
* @ clk_frequency : LCLK clock frequency .
2013-04-23 15:34:38 +04:00
* @ sysreg : handle to SYSREG block regmap .
2012-12-14 12:58:55 +04:00
* @ sc : scaler infomations .
* @ pol : porarity of writeback .
* @ id : fimc id .
* @ irq : irq number .
* @ suspended : qos operations .
*/
struct fimc_context {
struct exynos_drm_ippdrv ippdrv ;
struct resource * regs_res ;
void __iomem * regs ;
2014-05-19 14:54:09 +04:00
spinlock_t lock ;
2013-04-23 15:34:37 +04:00
struct clk * clocks [ FIMC_CLKS_MAX ] ;
u32 clk_frequency ;
2013-04-23 15:34:38 +04:00
struct regmap * sysreg ;
2012-12-14 12:58:55 +04:00
struct fimc_scaler sc ;
struct exynos_drm_ipp_pol pol ;
int id ;
int irq ;
bool suspended ;
} ;
2014-05-19 14:54:08 +04:00
static u32 fimc_read ( struct fimc_context * ctx , u32 reg )
{
return readl ( ctx - > regs + reg ) ;
}
static void fimc_write ( struct fimc_context * ctx , u32 val , u32 reg )
{
writel ( val , ctx - > regs + reg ) ;
}
static void fimc_set_bits ( struct fimc_context * ctx , u32 reg , u32 bits )
{
void __iomem * r = ctx - > regs + reg ;
writel ( readl ( r ) | bits , r ) ;
}
static void fimc_clear_bits ( struct fimc_context * ctx , u32 reg , u32 bits )
{
void __iomem * r = ctx - > regs + reg ;
writel ( readl ( r ) & ~ bits , r ) ;
}
2012-12-22 12:49:27 +04:00
static void fimc_sw_reset ( struct fimc_context * ctx )
2012-12-14 12:58:55 +04:00
{
u32 cfg ;
2012-12-22 12:49:28 +04:00
/* stop dma operation */
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CISTATUS ) ;
if ( EXYNOS_CISTATUS_GET_ENVID_STATUS ( cfg ) )
fimc_clear_bits ( ctx , EXYNOS_MSCTRL , EXYNOS_MSCTRL_ENVID ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
fimc_set_bits ( ctx , EXYNOS_CISRCFMT , EXYNOS_CISRCFMT_ITU601_8BIT ) ;
2012-12-14 12:58:55 +04:00
2012-12-22 12:49:28 +04:00
/* disable image capture */
2014-05-19 14:54:08 +04:00
fimc_clear_bits ( ctx , EXYNOS_CIIMGCPT ,
EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN ) ;
2012-12-22 12:49:28 +04:00
2012-12-14 12:58:55 +04:00
/* s/w reset */
2014-05-19 14:54:08 +04:00
fimc_set_bits ( ctx , EXYNOS_CIGCTRL , EXYNOS_CIGCTRL_SWRST ) ;
2012-12-14 12:58:55 +04:00
/* s/w reset complete */
2014-05-19 14:54:08 +04:00
fimc_clear_bits ( ctx , EXYNOS_CIGCTRL , EXYNOS_CIGCTRL_SWRST ) ;
2012-12-14 12:58:55 +04:00
/* reset sequence */
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , 0x0 , EXYNOS_CIFCNTSEQ ) ;
2012-12-14 12:58:55 +04:00
}
2013-04-23 15:34:38 +04:00
static int fimc_set_camblk_fimd0_wb ( struct fimc_context * ctx )
2012-12-14 12:58:55 +04:00
{
2013-04-23 15:34:38 +04:00
return regmap_update_bits ( ctx - > sysreg , SYSREG_CAMERA_BLK ,
SYSREG_FIMD0WB_DEST_MASK ,
ctx - > id < < SYSREG_FIMD0WB_DEST_SHIFT ) ;
2012-12-14 12:58:55 +04:00
}
static void fimc_set_type_ctrl ( struct fimc_context * ctx , enum fimc_wb wb )
{
u32 cfg ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " wb[%d] \n " , wb ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CIGCTRL ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ ( EXYNOS_CIGCTRL_TESTPATTERN_MASK |
EXYNOS_CIGCTRL_SELCAM_ITU_MASK |
EXYNOS_CIGCTRL_SELCAM_MIPI_MASK |
EXYNOS_CIGCTRL_SELCAM_FIMC_MASK |
EXYNOS_CIGCTRL_SELWB_CAMIF_MASK |
EXYNOS_CIGCTRL_SELWRITEBACK_MASK ) ;
switch ( wb ) {
case FIMC_WB_A :
cfg | = ( EXYNOS_CIGCTRL_SELWRITEBACK_A |
EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK ) ;
break ;
case FIMC_WB_B :
cfg | = ( EXYNOS_CIGCTRL_SELWRITEBACK_B |
EXYNOS_CIGCTRL_SELWB_CAMIF_WRITEBACK ) ;
break ;
case FIMC_WB_NONE :
default :
cfg | = ( EXYNOS_CIGCTRL_SELCAM_ITU_A |
EXYNOS_CIGCTRL_SELWRITEBACK_A |
EXYNOS_CIGCTRL_SELCAM_MIPI_A |
EXYNOS_CIGCTRL_SELCAM_FIMC_ITU ) ;
break ;
}
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIGCTRL ) ;
2012-12-14 12:58:55 +04:00
}
static void fimc_set_polarity ( struct fimc_context * ctx ,
struct exynos_drm_ipp_pol * pol )
{
u32 cfg ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " inv_pclk[%d]inv_vsync[%d] \n " ,
pol - > inv_pclk , pol - > inv_vsync ) ;
DRM_DEBUG_KMS ( " inv_href[%d]inv_hsync[%d] \n " ,
pol - > inv_href , pol - > inv_hsync ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CIGCTRL ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ ( EXYNOS_CIGCTRL_INVPOLPCLK | EXYNOS_CIGCTRL_INVPOLVSYNC |
EXYNOS_CIGCTRL_INVPOLHREF | EXYNOS_CIGCTRL_INVPOLHSYNC ) ;
if ( pol - > inv_pclk )
cfg | = EXYNOS_CIGCTRL_INVPOLPCLK ;
if ( pol - > inv_vsync )
cfg | = EXYNOS_CIGCTRL_INVPOLVSYNC ;
if ( pol - > inv_href )
cfg | = EXYNOS_CIGCTRL_INVPOLHREF ;
if ( pol - > inv_hsync )
cfg | = EXYNOS_CIGCTRL_INVPOLHSYNC ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIGCTRL ) ;
2012-12-14 12:58:55 +04:00
}
static void fimc_handle_jpeg ( struct fimc_context * ctx , bool enable )
{
u32 cfg ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " enable[%d] \n " , enable ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CIGCTRL ) ;
2012-12-14 12:58:55 +04:00
if ( enable )
cfg | = EXYNOS_CIGCTRL_CAM_JPEG ;
else
cfg & = ~ EXYNOS_CIGCTRL_CAM_JPEG ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIGCTRL ) ;
2012-12-14 12:58:55 +04:00
}
2014-05-19 14:54:07 +04:00
static void fimc_mask_irq ( struct fimc_context * ctx , bool enable )
2012-12-14 12:58:55 +04:00
{
u32 cfg ;
2014-05-19 14:54:07 +04:00
DRM_DEBUG_KMS ( " enable[%d] \n " , enable ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CIGCTRL ) ;
2012-12-14 12:58:55 +04:00
if ( enable ) {
2014-05-19 14:54:07 +04:00
cfg & = ~ EXYNOS_CIGCTRL_IRQ_OVFEN ;
cfg | = EXYNOS_CIGCTRL_IRQ_ENABLE | EXYNOS_CIGCTRL_IRQ_LEVEL ;
2012-12-14 12:58:55 +04:00
} else
2014-05-19 14:54:07 +04:00
cfg & = ~ EXYNOS_CIGCTRL_IRQ_ENABLE ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIGCTRL ) ;
2012-12-14 12:58:55 +04:00
}
static void fimc_clear_irq ( struct fimc_context * ctx )
{
2014-05-19 14:54:08 +04:00
fimc_set_bits ( ctx , EXYNOS_CIGCTRL , EXYNOS_CIGCTRL_IRQ_CLR ) ;
2012-12-14 12:58:55 +04:00
}
static bool fimc_check_ovf ( struct fimc_context * ctx )
{
struct exynos_drm_ippdrv * ippdrv = & ctx - > ippdrv ;
2014-05-19 14:54:08 +04:00
u32 status , flag ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
status = fimc_read ( ctx , EXYNOS_CISTATUS ) ;
2012-12-14 12:58:55 +04:00
flag = EXYNOS_CISTATUS_OVFIY | EXYNOS_CISTATUS_OVFICB |
EXYNOS_CISTATUS_OVFICR ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " flag[0x%x] \n " , flag ) ;
2012-12-14 12:58:55 +04:00
if ( status & flag ) {
2014-05-19 14:54:08 +04:00
fimc_set_bits ( ctx , EXYNOS_CIWDOFST ,
EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
2012-12-14 12:58:55 +04:00
EXYNOS_CIWDOFST_CLROVFICR ) ;
2013-12-08 19:22:53 +04:00
dev_err ( ippdrv - > dev , " occurred overflow at %d, status 0x%x. \n " ,
2012-12-14 12:58:55 +04:00
ctx - > id , status ) ;
return true ;
}
return false ;
}
static bool fimc_check_frame_end ( struct fimc_context * ctx )
{
u32 cfg ;
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CISTATUS ) ;
2012-12-14 12:58:55 +04:00
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " cfg[0x%x] \n " , cfg ) ;
2012-12-14 12:58:55 +04:00
if ( ! ( cfg & EXYNOS_CISTATUS_FRAMEEND ) )
return false ;
cfg & = ~ ( EXYNOS_CISTATUS_FRAMEEND ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CISTATUS ) ;
2012-12-14 12:58:55 +04:00
return true ;
}
static int fimc_get_buf_id ( struct fimc_context * ctx )
{
u32 cfg ;
int frame_cnt , buf_id ;
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CISTATUS2 ) ;
2012-12-14 12:58:55 +04:00
frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE ( cfg ) ;
if ( frame_cnt = = 0 )
frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT ( cfg ) ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " present[%d]before[%d] \n " ,
2012-12-14 12:58:55 +04:00
EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT ( cfg ) ,
EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE ( cfg ) ) ;
if ( frame_cnt = = 0 ) {
DRM_ERROR ( " failed to get frame count. \n " ) ;
return - EIO ;
}
buf_id = frame_cnt - 1 ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " buf_id[%d] \n " , buf_id ) ;
2012-12-14 12:58:55 +04:00
return buf_id ;
}
static void fimc_handle_lastend ( struct fimc_context * ctx , bool enable )
{
u32 cfg ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " enable[%d] \n " , enable ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CIOCTRL ) ;
2012-12-14 12:58:55 +04:00
if ( enable )
cfg | = EXYNOS_CIOCTRL_LASTENDEN ;
else
cfg & = ~ EXYNOS_CIOCTRL_LASTENDEN ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIOCTRL ) ;
2012-12-14 12:58:55 +04:00
}
static int fimc_src_set_fmt_order ( struct fimc_context * ctx , u32 fmt )
{
struct exynos_drm_ippdrv * ippdrv = & ctx - > ippdrv ;
u32 cfg ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " fmt[0x%x] \n " , fmt ) ;
2012-12-14 12:58:55 +04:00
/* RGB */
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CISCCTRL ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ EXYNOS_CISCCTRL_INRGB_FMT_RGB_MASK ;
switch ( fmt ) {
case DRM_FORMAT_RGB565 :
cfg | = EXYNOS_CISCCTRL_INRGB_FMT_RGB565 ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CISCCTRL ) ;
2012-12-14 12:58:55 +04:00
return 0 ;
case DRM_FORMAT_RGB888 :
case DRM_FORMAT_XRGB8888 :
cfg | = EXYNOS_CISCCTRL_INRGB_FMT_RGB888 ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CISCCTRL ) ;
2012-12-14 12:58:55 +04:00
return 0 ;
default :
/* bypass */
break ;
}
/* YUV */
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_MSCTRL ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ ( EXYNOS_MSCTRL_ORDER2P_SHIFT_MASK |
EXYNOS_MSCTRL_C_INT_IN_2PLANE |
EXYNOS_MSCTRL_ORDER422_YCBYCR ) ;
switch ( fmt ) {
case DRM_FORMAT_YUYV :
cfg | = EXYNOS_MSCTRL_ORDER422_YCBYCR ;
break ;
case DRM_FORMAT_YVYU :
cfg | = EXYNOS_MSCTRL_ORDER422_YCRYCB ;
break ;
case DRM_FORMAT_UYVY :
cfg | = EXYNOS_MSCTRL_ORDER422_CBYCRY ;
break ;
case DRM_FORMAT_VYUY :
case DRM_FORMAT_YUV444 :
cfg | = EXYNOS_MSCTRL_ORDER422_CRYCBY ;
break ;
case DRM_FORMAT_NV21 :
case DRM_FORMAT_NV61 :
cfg | = ( EXYNOS_MSCTRL_ORDER2P_LSB_CRCB |
EXYNOS_MSCTRL_C_INT_IN_2PLANE ) ;
break ;
case DRM_FORMAT_YUV422 :
case DRM_FORMAT_YUV420 :
case DRM_FORMAT_YVU420 :
cfg | = EXYNOS_MSCTRL_C_INT_IN_3PLANE ;
break ;
case DRM_FORMAT_NV12 :
case DRM_FORMAT_NV16 :
cfg | = ( EXYNOS_MSCTRL_ORDER2P_LSB_CBCR |
EXYNOS_MSCTRL_C_INT_IN_2PLANE ) ;
break ;
default :
dev_err ( ippdrv - > dev , " inavlid source yuv order 0x%x. \n " , fmt ) ;
return - EINVAL ;
}
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_MSCTRL ) ;
2012-12-14 12:58:55 +04:00
return 0 ;
}
static int fimc_src_set_fmt ( struct device * dev , u32 fmt )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
struct exynos_drm_ippdrv * ippdrv = & ctx - > ippdrv ;
u32 cfg ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " fmt[0x%x] \n " , fmt ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_MSCTRL ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ EXYNOS_MSCTRL_INFORMAT_RGB ;
switch ( fmt ) {
case DRM_FORMAT_RGB565 :
case DRM_FORMAT_RGB888 :
case DRM_FORMAT_XRGB8888 :
cfg | = EXYNOS_MSCTRL_INFORMAT_RGB ;
break ;
case DRM_FORMAT_YUV444 :
cfg | = EXYNOS_MSCTRL_INFORMAT_YCBCR420 ;
break ;
case DRM_FORMAT_YUYV :
case DRM_FORMAT_YVYU :
case DRM_FORMAT_UYVY :
case DRM_FORMAT_VYUY :
cfg | = EXYNOS_MSCTRL_INFORMAT_YCBCR422_1PLANE ;
break ;
case DRM_FORMAT_NV16 :
case DRM_FORMAT_NV61 :
case DRM_FORMAT_YUV422 :
cfg | = EXYNOS_MSCTRL_INFORMAT_YCBCR422 ;
break ;
case DRM_FORMAT_YUV420 :
case DRM_FORMAT_YVU420 :
case DRM_FORMAT_NV12 :
case DRM_FORMAT_NV21 :
cfg | = EXYNOS_MSCTRL_INFORMAT_YCBCR420 ;
break ;
default :
dev_err ( ippdrv - > dev , " inavlid source format 0x%x. \n " , fmt ) ;
return - EINVAL ;
}
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_MSCTRL ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CIDMAPARAM ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ EXYNOS_CIDMAPARAM_R_MODE_MASK ;
2015-02-03 18:37:45 +03:00
cfg | = EXYNOS_CIDMAPARAM_R_MODE_LINEAR ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIDMAPARAM ) ;
2012-12-14 12:58:55 +04:00
return fimc_src_set_fmt_order ( ctx , fmt ) ;
}
static int fimc_src_set_transf ( struct device * dev ,
enum drm_exynos_degree degree ,
enum drm_exynos_flip flip , bool * swap )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
struct exynos_drm_ippdrv * ippdrv = & ctx - > ippdrv ;
u32 cfg1 , cfg2 ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " degree[%d]flip[0x%x] \n " , degree , flip ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
cfg1 = fimc_read ( ctx , EXYNOS_MSCTRL ) ;
2012-12-14 12:58:55 +04:00
cfg1 & = ~ ( EXYNOS_MSCTRL_FLIP_X_MIRROR |
EXYNOS_MSCTRL_FLIP_Y_MIRROR ) ;
2014-05-19 14:54:08 +04:00
cfg2 = fimc_read ( ctx , EXYNOS_CITRGFMT ) ;
2012-12-14 12:58:55 +04:00
cfg2 & = ~ EXYNOS_CITRGFMT_INROT90_CLOCKWISE ;
switch ( degree ) {
case EXYNOS_DRM_DEGREE_0 :
if ( flip & EXYNOS_DRM_FLIP_VERTICAL )
cfg1 | = EXYNOS_MSCTRL_FLIP_X_MIRROR ;
if ( flip & EXYNOS_DRM_FLIP_HORIZONTAL )
cfg1 | = EXYNOS_MSCTRL_FLIP_Y_MIRROR ;
break ;
case EXYNOS_DRM_DEGREE_90 :
cfg2 | = EXYNOS_CITRGFMT_INROT90_CLOCKWISE ;
if ( flip & EXYNOS_DRM_FLIP_VERTICAL )
cfg1 | = EXYNOS_MSCTRL_FLIP_X_MIRROR ;
if ( flip & EXYNOS_DRM_FLIP_HORIZONTAL )
cfg1 | = EXYNOS_MSCTRL_FLIP_Y_MIRROR ;
break ;
case EXYNOS_DRM_DEGREE_180 :
cfg1 | = ( EXYNOS_MSCTRL_FLIP_X_MIRROR |
EXYNOS_MSCTRL_FLIP_Y_MIRROR ) ;
if ( flip & EXYNOS_DRM_FLIP_VERTICAL )
cfg1 & = ~ EXYNOS_MSCTRL_FLIP_X_MIRROR ;
if ( flip & EXYNOS_DRM_FLIP_HORIZONTAL )
cfg1 & = ~ EXYNOS_MSCTRL_FLIP_Y_MIRROR ;
break ;
case EXYNOS_DRM_DEGREE_270 :
cfg1 | = ( EXYNOS_MSCTRL_FLIP_X_MIRROR |
EXYNOS_MSCTRL_FLIP_Y_MIRROR ) ;
cfg2 | = EXYNOS_CITRGFMT_INROT90_CLOCKWISE ;
if ( flip & EXYNOS_DRM_FLIP_VERTICAL )
cfg1 & = ~ EXYNOS_MSCTRL_FLIP_X_MIRROR ;
if ( flip & EXYNOS_DRM_FLIP_HORIZONTAL )
cfg1 & = ~ EXYNOS_MSCTRL_FLIP_Y_MIRROR ;
break ;
default :
dev_err ( ippdrv - > dev , " inavlid degree value %d. \n " , degree ) ;
return - EINVAL ;
}
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg1 , EXYNOS_MSCTRL ) ;
fimc_write ( ctx , cfg2 , EXYNOS_CITRGFMT ) ;
2012-12-14 12:58:55 +04:00
* swap = ( cfg2 & EXYNOS_CITRGFMT_INROT90_CLOCKWISE ) ? 1 : 0 ;
return 0 ;
}
static int fimc_set_window ( struct fimc_context * ctx ,
struct drm_exynos_pos * pos , struct drm_exynos_sz * sz )
{
u32 cfg , h1 , h2 , v1 , v2 ;
/* cropped image */
h1 = pos - > x ;
h2 = sz - > hsize - pos - > w - pos - > x ;
v1 = pos - > y ;
v2 = sz - > vsize - pos - > h - pos - > y ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " x[%d]y[%d]w[%d]h[%d]hsize[%d]vsize[%d] \n " ,
pos - > x , pos - > y , pos - > w , pos - > h , sz - > hsize , sz - > vsize ) ;
DRM_DEBUG_KMS ( " h1[%d]h2[%d]v1[%d]v2[%d] \n " , h1 , h2 , v1 , v2 ) ;
2012-12-14 12:58:55 +04:00
/*
* set window offset 1 , 2 size
* check figure 43 - 21 in user manual
*/
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CIWDOFST ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ ( EXYNOS_CIWDOFST_WINHOROFST_MASK |
EXYNOS_CIWDOFST_WINVEROFST_MASK ) ;
cfg | = ( EXYNOS_CIWDOFST_WINHOROFST ( h1 ) |
EXYNOS_CIWDOFST_WINVEROFST ( v1 ) ) ;
cfg | = EXYNOS_CIWDOFST_WINOFSEN ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIWDOFST ) ;
2012-12-14 12:58:55 +04:00
cfg = ( EXYNOS_CIWDOFST2_WINHOROFST2 ( h2 ) |
EXYNOS_CIWDOFST2_WINVEROFST2 ( v2 ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIWDOFST2 ) ;
2012-12-14 12:58:55 +04:00
return 0 ;
}
static int fimc_src_set_size ( struct device * dev , int swap ,
struct drm_exynos_pos * pos , struct drm_exynos_sz * sz )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
struct drm_exynos_pos img_pos = * pos ;
struct drm_exynos_sz img_sz = * sz ;
u32 cfg ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " swap[%d]hsize[%d]vsize[%d] \n " ,
swap , sz - > hsize , sz - > vsize ) ;
2012-12-14 12:58:55 +04:00
/* original size */
cfg = ( EXYNOS_ORGISIZE_HORIZONTAL ( img_sz . hsize ) |
EXYNOS_ORGISIZE_VERTICAL ( img_sz . vsize ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_ORGISIZE ) ;
2012-12-14 12:58:55 +04:00
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " x[%d]y[%d]w[%d]h[%d] \n " , pos - > x , pos - > y , pos - > w , pos - > h ) ;
2012-12-14 12:58:55 +04:00
if ( swap ) {
img_pos . w = pos - > h ;
img_pos . h = pos - > w ;
img_sz . hsize = sz - > vsize ;
img_sz . vsize = sz - > hsize ;
}
/* set input DMA image size */
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CIREAL_ISIZE ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ ( EXYNOS_CIREAL_ISIZE_HEIGHT_MASK |
EXYNOS_CIREAL_ISIZE_WIDTH_MASK ) ;
cfg | = ( EXYNOS_CIREAL_ISIZE_WIDTH ( img_pos . w ) |
EXYNOS_CIREAL_ISIZE_HEIGHT ( img_pos . h ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIREAL_ISIZE ) ;
2012-12-14 12:58:55 +04:00
/*
* set input FIFO image size
* for now , we support only ITU601 8 bit mode
*/
cfg = ( EXYNOS_CISRCFMT_ITU601_8BIT |
EXYNOS_CISRCFMT_SOURCEHSIZE ( img_sz . hsize ) |
EXYNOS_CISRCFMT_SOURCEVSIZE ( img_sz . vsize ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CISRCFMT ) ;
2012-12-14 12:58:55 +04:00
/* offset Y(RGB), Cb, Cr */
cfg = ( EXYNOS_CIIYOFF_HORIZONTAL ( img_pos . x ) |
EXYNOS_CIIYOFF_VERTICAL ( img_pos . y ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIIYOFF ) ;
2012-12-14 12:58:55 +04:00
cfg = ( EXYNOS_CIICBOFF_HORIZONTAL ( img_pos . x ) |
EXYNOS_CIICBOFF_VERTICAL ( img_pos . y ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIICBOFF ) ;
2012-12-14 12:58:55 +04:00
cfg = ( EXYNOS_CIICROFF_HORIZONTAL ( img_pos . x ) |
EXYNOS_CIICROFF_VERTICAL ( img_pos . y ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIICROFF ) ;
2012-12-14 12:58:55 +04:00
return fimc_set_window ( ctx , & img_pos , & img_sz ) ;
}
static int fimc_src_set_addr ( struct device * dev ,
struct drm_exynos_ipp_buf_info * buf_info , u32 buf_id ,
enum drm_exynos_ipp_buf_type buf_type )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
struct exynos_drm_ippdrv * ippdrv = & ctx - > ippdrv ;
2012-12-22 12:49:22 +04:00
struct drm_exynos_ipp_cmd_node * c_node = ippdrv - > c_node ;
2012-12-14 12:58:55 +04:00
struct drm_exynos_ipp_property * property ;
struct drm_exynos_ipp_config * config ;
if ( ! c_node ) {
DRM_ERROR ( " failed to get c_node. \n " ) ;
return - EINVAL ;
}
property = & c_node - > property ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " prop_id[%d]buf_id[%d]buf_type[%d] \n " ,
2012-12-14 12:58:55 +04:00
property - > prop_id , buf_id , buf_type ) ;
if ( buf_id > FIMC_MAX_SRC ) {
dev_info ( ippdrv - > dev , " inavlid buf_id %d. \n " , buf_id ) ;
return - ENOMEM ;
}
/* address register set */
switch ( buf_type ) {
case IPP_BUF_ENQUEUE :
config = & property - > config [ EXYNOS_DRM_OPS_SRC ] ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , buf_info - > base [ EXYNOS_DRM_PLANAR_Y ] ,
2014-08-28 13:07:39 +04:00
EXYNOS_CIIYSA0 ) ;
2012-12-14 12:58:55 +04:00
if ( config - > fmt = = DRM_FORMAT_YVU420 ) {
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , buf_info - > base [ EXYNOS_DRM_PLANAR_CR ] ,
2014-08-28 13:07:39 +04:00
EXYNOS_CIICBSA0 ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , buf_info - > base [ EXYNOS_DRM_PLANAR_CB ] ,
2014-08-28 13:07:39 +04:00
EXYNOS_CIICRSA0 ) ;
2012-12-14 12:58:55 +04:00
} else {
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , buf_info - > base [ EXYNOS_DRM_PLANAR_CB ] ,
2014-08-28 13:07:39 +04:00
EXYNOS_CIICBSA0 ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , buf_info - > base [ EXYNOS_DRM_PLANAR_CR ] ,
2014-08-28 13:07:39 +04:00
EXYNOS_CIICRSA0 ) ;
2012-12-14 12:58:55 +04:00
}
break ;
case IPP_BUF_DEQUEUE :
2014-08-28 13:07:39 +04:00
fimc_write ( ctx , 0x0 , EXYNOS_CIIYSA0 ) ;
fimc_write ( ctx , 0x0 , EXYNOS_CIICBSA0 ) ;
fimc_write ( ctx , 0x0 , EXYNOS_CIICRSA0 ) ;
2012-12-14 12:58:55 +04:00
break ;
default :
/* bypass */
break ;
}
return 0 ;
}
static struct exynos_drm_ipp_ops fimc_src_ops = {
. set_fmt = fimc_src_set_fmt ,
. set_transf = fimc_src_set_transf ,
. set_size = fimc_src_set_size ,
. set_addr = fimc_src_set_addr ,
} ;
static int fimc_dst_set_fmt_order ( struct fimc_context * ctx , u32 fmt )
{
struct exynos_drm_ippdrv * ippdrv = & ctx - > ippdrv ;
u32 cfg ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " fmt[0x%x] \n " , fmt ) ;
2012-12-14 12:58:55 +04:00
/* RGB */
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CISCCTRL ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ EXYNOS_CISCCTRL_OUTRGB_FMT_RGB_MASK ;
switch ( fmt ) {
case DRM_FORMAT_RGB565 :
cfg | = EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565 ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CISCCTRL ) ;
2012-12-14 12:58:55 +04:00
return 0 ;
case DRM_FORMAT_RGB888 :
cfg | = EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CISCCTRL ) ;
2012-12-14 12:58:55 +04:00
return 0 ;
case DRM_FORMAT_XRGB8888 :
cfg | = ( EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 |
EXYNOS_CISCCTRL_EXTRGB_EXTENSION ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CISCCTRL ) ;
2012-12-14 12:58:55 +04:00
break ;
default :
/* bypass */
break ;
}
/* YUV */
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CIOCTRL ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ ( EXYNOS_CIOCTRL_ORDER2P_MASK |
EXYNOS_CIOCTRL_ORDER422_MASK |
EXYNOS_CIOCTRL_YCBCR_PLANE_MASK ) ;
switch ( fmt ) {
case DRM_FORMAT_XRGB8888 :
cfg | = EXYNOS_CIOCTRL_ALPHA_OUT ;
break ;
case DRM_FORMAT_YUYV :
cfg | = EXYNOS_CIOCTRL_ORDER422_YCBYCR ;
break ;
case DRM_FORMAT_YVYU :
cfg | = EXYNOS_CIOCTRL_ORDER422_YCRYCB ;
break ;
case DRM_FORMAT_UYVY :
cfg | = EXYNOS_CIOCTRL_ORDER422_CBYCRY ;
break ;
case DRM_FORMAT_VYUY :
cfg | = EXYNOS_CIOCTRL_ORDER422_CRYCBY ;
break ;
case DRM_FORMAT_NV21 :
case DRM_FORMAT_NV61 :
cfg | = EXYNOS_CIOCTRL_ORDER2P_LSB_CRCB ;
cfg | = EXYNOS_CIOCTRL_YCBCR_2PLANE ;
break ;
case DRM_FORMAT_YUV422 :
case DRM_FORMAT_YUV420 :
case DRM_FORMAT_YVU420 :
cfg | = EXYNOS_CIOCTRL_YCBCR_3PLANE ;
break ;
case DRM_FORMAT_NV12 :
case DRM_FORMAT_NV16 :
cfg | = EXYNOS_CIOCTRL_ORDER2P_LSB_CBCR ;
cfg | = EXYNOS_CIOCTRL_YCBCR_2PLANE ;
break ;
default :
dev_err ( ippdrv - > dev , " inavlid target yuv order 0x%x. \n " , fmt ) ;
return - EINVAL ;
}
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIOCTRL ) ;
2012-12-14 12:58:55 +04:00
return 0 ;
}
static int fimc_dst_set_fmt ( struct device * dev , u32 fmt )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
struct exynos_drm_ippdrv * ippdrv = & ctx - > ippdrv ;
u32 cfg ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " fmt[0x%x] \n " , fmt ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CIEXTEN ) ;
2012-12-14 12:58:55 +04:00
if ( fmt = = DRM_FORMAT_AYUV ) {
cfg | = EXYNOS_CIEXTEN_YUV444_OUT ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIEXTEN ) ;
2012-12-14 12:58:55 +04:00
} else {
cfg & = ~ EXYNOS_CIEXTEN_YUV444_OUT ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIEXTEN ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CITRGFMT ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ EXYNOS_CITRGFMT_OUTFORMAT_MASK ;
switch ( fmt ) {
case DRM_FORMAT_RGB565 :
case DRM_FORMAT_RGB888 :
case DRM_FORMAT_XRGB8888 :
cfg | = EXYNOS_CITRGFMT_OUTFORMAT_RGB ;
break ;
case DRM_FORMAT_YUYV :
case DRM_FORMAT_YVYU :
case DRM_FORMAT_UYVY :
case DRM_FORMAT_VYUY :
cfg | = EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422_1PLANE ;
break ;
case DRM_FORMAT_NV16 :
case DRM_FORMAT_NV61 :
case DRM_FORMAT_YUV422 :
cfg | = EXYNOS_CITRGFMT_OUTFORMAT_YCBCR422 ;
break ;
case DRM_FORMAT_YUV420 :
case DRM_FORMAT_YVU420 :
case DRM_FORMAT_NV12 :
case DRM_FORMAT_NV21 :
cfg | = EXYNOS_CITRGFMT_OUTFORMAT_YCBCR420 ;
break ;
default :
dev_err ( ippdrv - > dev , " inavlid target format 0x%x. \n " ,
fmt ) ;
return - EINVAL ;
}
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CITRGFMT ) ;
2012-12-14 12:58:55 +04:00
}
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CIDMAPARAM ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ EXYNOS_CIDMAPARAM_W_MODE_MASK ;
2015-02-03 18:37:45 +03:00
cfg | = EXYNOS_CIDMAPARAM_W_MODE_LINEAR ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIDMAPARAM ) ;
2012-12-14 12:58:55 +04:00
return fimc_dst_set_fmt_order ( ctx , fmt ) ;
}
static int fimc_dst_set_transf ( struct device * dev ,
enum drm_exynos_degree degree ,
enum drm_exynos_flip flip , bool * swap )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
struct exynos_drm_ippdrv * ippdrv = & ctx - > ippdrv ;
u32 cfg ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " degree[%d]flip[0x%x] \n " , degree , flip ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CITRGFMT ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ EXYNOS_CITRGFMT_FLIP_MASK ;
cfg & = ~ EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE ;
switch ( degree ) {
case EXYNOS_DRM_DEGREE_0 :
if ( flip & EXYNOS_DRM_FLIP_VERTICAL )
cfg | = EXYNOS_CITRGFMT_FLIP_X_MIRROR ;
if ( flip & EXYNOS_DRM_FLIP_HORIZONTAL )
cfg | = EXYNOS_CITRGFMT_FLIP_Y_MIRROR ;
break ;
case EXYNOS_DRM_DEGREE_90 :
cfg | = EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE ;
if ( flip & EXYNOS_DRM_FLIP_VERTICAL )
cfg | = EXYNOS_CITRGFMT_FLIP_X_MIRROR ;
if ( flip & EXYNOS_DRM_FLIP_HORIZONTAL )
cfg | = EXYNOS_CITRGFMT_FLIP_Y_MIRROR ;
break ;
case EXYNOS_DRM_DEGREE_180 :
cfg | = ( EXYNOS_CITRGFMT_FLIP_X_MIRROR |
EXYNOS_CITRGFMT_FLIP_Y_MIRROR ) ;
if ( flip & EXYNOS_DRM_FLIP_VERTICAL )
cfg & = ~ EXYNOS_CITRGFMT_FLIP_X_MIRROR ;
if ( flip & EXYNOS_DRM_FLIP_HORIZONTAL )
cfg & = ~ EXYNOS_CITRGFMT_FLIP_Y_MIRROR ;
break ;
case EXYNOS_DRM_DEGREE_270 :
cfg | = ( EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE |
EXYNOS_CITRGFMT_FLIP_X_MIRROR |
EXYNOS_CITRGFMT_FLIP_Y_MIRROR ) ;
if ( flip & EXYNOS_DRM_FLIP_VERTICAL )
cfg & = ~ EXYNOS_CITRGFMT_FLIP_X_MIRROR ;
if ( flip & EXYNOS_DRM_FLIP_HORIZONTAL )
cfg & = ~ EXYNOS_CITRGFMT_FLIP_Y_MIRROR ;
break ;
default :
dev_err ( ippdrv - > dev , " inavlid degree value %d. \n " , degree ) ;
return - EINVAL ;
}
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CITRGFMT ) ;
2012-12-14 12:58:55 +04:00
* swap = ( cfg & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE ) ? 1 : 0 ;
return 0 ;
}
static int fimc_set_prescaler ( struct fimc_context * ctx , struct fimc_scaler * sc ,
struct drm_exynos_pos * src , struct drm_exynos_pos * dst )
{
struct exynos_drm_ippdrv * ippdrv = & ctx - > ippdrv ;
u32 cfg , cfg_ext , shfactor ;
u32 pre_dst_width , pre_dst_height ;
2014-05-19 14:54:06 +04:00
u32 hfactor , vfactor ;
2012-12-14 12:58:55 +04:00
int ret = 0 ;
u32 src_w , src_h , dst_w , dst_h ;
2014-05-19 14:54:08 +04:00
cfg_ext = fimc_read ( ctx , EXYNOS_CITRGFMT ) ;
2012-12-14 12:58:55 +04:00
if ( cfg_ext & EXYNOS_CITRGFMT_INROT90_CLOCKWISE ) {
src_w = src - > h ;
src_h = src - > w ;
} else {
src_w = src - > w ;
src_h = src - > h ;
}
if ( cfg_ext & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE ) {
dst_w = dst - > h ;
dst_h = dst - > w ;
} else {
dst_w = dst - > w ;
dst_h = dst - > h ;
}
2014-05-19 14:54:06 +04:00
/* fimc_ippdrv_check_property assures that dividers are not null */
hfactor = fls ( src_w / dst_w / 2 ) ;
if ( hfactor > FIMC_SHFACTOR / 2 ) {
2012-12-14 12:58:55 +04:00
dev_err ( ippdrv - > dev , " failed to get ratio horizontal. \n " ) ;
2014-05-19 14:54:06 +04:00
return - EINVAL ;
2012-12-14 12:58:55 +04:00
}
2014-05-19 14:54:06 +04:00
vfactor = fls ( src_h / dst_h / 2 ) ;
if ( vfactor > FIMC_SHFACTOR / 2 ) {
2012-12-14 12:58:55 +04:00
dev_err ( ippdrv - > dev , " failed to get ratio vertical. \n " ) ;
2014-05-19 14:54:06 +04:00
return - EINVAL ;
2012-12-14 12:58:55 +04:00
}
2014-05-19 14:54:06 +04:00
pre_dst_width = src_w > > hfactor ;
pre_dst_height = src_h > > vfactor ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " pre_dst_width[%d]pre_dst_height[%d] \n " ,
2012-12-14 12:58:55 +04:00
pre_dst_width , pre_dst_height ) ;
2014-05-19 14:54:06 +04:00
DRM_DEBUG_KMS ( " hfactor[%d]vfactor[%d] \n " , hfactor , vfactor ) ;
2012-12-14 12:58:55 +04:00
sc - > hratio = ( src_w < < 14 ) / ( dst_w < < hfactor ) ;
sc - > vratio = ( src_h < < 14 ) / ( dst_h < < vfactor ) ;
sc - > up_h = ( dst_w > = src_w ) ? true : false ;
sc - > up_v = ( dst_h > = src_h ) ? true : false ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " hratio[%d]vratio[%d]up_h[%d]up_v[%d] \n " ,
sc - > hratio , sc - > vratio , sc - > up_h , sc - > up_v ) ;
2012-12-14 12:58:55 +04:00
shfactor = FIMC_SHFACTOR - ( hfactor + vfactor ) ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " shfactor[%d] \n " , shfactor ) ;
2012-12-14 12:58:55 +04:00
cfg = ( EXYNOS_CISCPRERATIO_SHFACTOR ( shfactor ) |
2014-05-19 14:54:06 +04:00
EXYNOS_CISCPRERATIO_PREHORRATIO ( 1 < < hfactor ) |
EXYNOS_CISCPRERATIO_PREVERRATIO ( 1 < < vfactor ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CISCPRERATIO ) ;
2012-12-14 12:58:55 +04:00
cfg = ( EXYNOS_CISCPREDST_PREDSTWIDTH ( pre_dst_width ) |
EXYNOS_CISCPREDST_PREDSTHEIGHT ( pre_dst_height ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CISCPREDST ) ;
2012-12-14 12:58:55 +04:00
return ret ;
}
static void fimc_set_scaler ( struct fimc_context * ctx , struct fimc_scaler * sc )
{
u32 cfg , cfg_ext ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " range[%d]bypass[%d]up_h[%d]up_v[%d] \n " ,
sc - > range , sc - > bypass , sc - > up_h , sc - > up_v ) ;
DRM_DEBUG_KMS ( " hratio[%d]vratio[%d] \n " ,
sc - > hratio , sc - > vratio ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CISCCTRL ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ ( EXYNOS_CISCCTRL_SCALERBYPASS |
EXYNOS_CISCCTRL_SCALEUP_H | EXYNOS_CISCCTRL_SCALEUP_V |
EXYNOS_CISCCTRL_MAIN_V_RATIO_MASK |
EXYNOS_CISCCTRL_MAIN_H_RATIO_MASK |
EXYNOS_CISCCTRL_CSCR2Y_WIDE |
EXYNOS_CISCCTRL_CSCY2R_WIDE ) ;
if ( sc - > range )
cfg | = ( EXYNOS_CISCCTRL_CSCR2Y_WIDE |
EXYNOS_CISCCTRL_CSCY2R_WIDE ) ;
if ( sc - > bypass )
cfg | = EXYNOS_CISCCTRL_SCALERBYPASS ;
if ( sc - > up_h )
cfg | = EXYNOS_CISCCTRL_SCALEUP_H ;
if ( sc - > up_v )
cfg | = EXYNOS_CISCCTRL_SCALEUP_V ;
cfg | = ( EXYNOS_CISCCTRL_MAINHORRATIO ( ( sc - > hratio > > 6 ) ) |
EXYNOS_CISCCTRL_MAINVERRATIO ( ( sc - > vratio > > 6 ) ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CISCCTRL ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
cfg_ext = fimc_read ( ctx , EXYNOS_CIEXTEN ) ;
2012-12-14 12:58:55 +04:00
cfg_ext & = ~ EXYNOS_CIEXTEN_MAINHORRATIO_EXT_MASK ;
cfg_ext & = ~ EXYNOS_CIEXTEN_MAINVERRATIO_EXT_MASK ;
cfg_ext | = ( EXYNOS_CIEXTEN_MAINHORRATIO_EXT ( sc - > hratio ) |
EXYNOS_CIEXTEN_MAINVERRATIO_EXT ( sc - > vratio ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg_ext , EXYNOS_CIEXTEN ) ;
2012-12-14 12:58:55 +04:00
}
static int fimc_dst_set_size ( struct device * dev , int swap ,
struct drm_exynos_pos * pos , struct drm_exynos_sz * sz )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
struct drm_exynos_pos img_pos = * pos ;
struct drm_exynos_sz img_sz = * sz ;
u32 cfg ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " swap[%d]hsize[%d]vsize[%d] \n " ,
swap , sz - > hsize , sz - > vsize ) ;
2012-12-14 12:58:55 +04:00
/* original size */
cfg = ( EXYNOS_ORGOSIZE_HORIZONTAL ( img_sz . hsize ) |
EXYNOS_ORGOSIZE_VERTICAL ( img_sz . vsize ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_ORGOSIZE ) ;
2012-12-14 12:58:55 +04:00
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " x[%d]y[%d]w[%d]h[%d] \n " , pos - > x , pos - > y , pos - > w , pos - > h ) ;
2012-12-14 12:58:55 +04:00
/* CSC ITU */
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CIGCTRL ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ EXYNOS_CIGCTRL_CSC_MASK ;
if ( sz - > hsize > = FIMC_WIDTH_ITU_709 )
cfg | = EXYNOS_CIGCTRL_CSC_ITU709 ;
else
cfg | = EXYNOS_CIGCTRL_CSC_ITU601 ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIGCTRL ) ;
2012-12-14 12:58:55 +04:00
if ( swap ) {
img_pos . w = pos - > h ;
img_pos . h = pos - > w ;
img_sz . hsize = sz - > vsize ;
img_sz . vsize = sz - > hsize ;
}
/* target image size */
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CITRGFMT ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ ( EXYNOS_CITRGFMT_TARGETH_MASK |
EXYNOS_CITRGFMT_TARGETV_MASK ) ;
cfg | = ( EXYNOS_CITRGFMT_TARGETHSIZE ( img_pos . w ) |
EXYNOS_CITRGFMT_TARGETVSIZE ( img_pos . h ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CITRGFMT ) ;
2012-12-14 12:58:55 +04:00
/* target area */
cfg = EXYNOS_CITAREA_TARGET_AREA ( img_pos . w * img_pos . h ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CITAREA ) ;
2012-12-14 12:58:55 +04:00
/* offset Y(RGB), Cb, Cr */
cfg = ( EXYNOS_CIOYOFF_HORIZONTAL ( img_pos . x ) |
EXYNOS_CIOYOFF_VERTICAL ( img_pos . y ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIOYOFF ) ;
2012-12-14 12:58:55 +04:00
cfg = ( EXYNOS_CIOCBOFF_HORIZONTAL ( img_pos . x ) |
EXYNOS_CIOCBOFF_VERTICAL ( img_pos . y ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIOCBOFF ) ;
2012-12-14 12:58:55 +04:00
cfg = ( EXYNOS_CIOCROFF_HORIZONTAL ( img_pos . x ) |
EXYNOS_CIOCROFF_VERTICAL ( img_pos . y ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIOCROFF ) ;
2012-12-14 12:58:55 +04:00
return 0 ;
}
2014-08-28 13:07:38 +04:00
static void fimc_dst_set_buf_seq ( struct fimc_context * ctx , u32 buf_id ,
2012-12-14 12:58:55 +04:00
enum drm_exynos_ipp_buf_type buf_type )
{
2014-05-19 14:54:09 +04:00
unsigned long flags ;
2014-08-28 13:07:38 +04:00
u32 buf_num ;
u32 cfg ;
2012-12-14 12:58:55 +04:00
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " buf_id[%d]buf_type[%d] \n " , buf_id , buf_type ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:09 +04:00
spin_lock_irqsave ( & ctx - > lock , flags ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_CIFCNTSEQ ) ;
2012-12-14 12:58:55 +04:00
2014-08-28 13:07:38 +04:00
if ( buf_type = = IPP_BUF_ENQUEUE )
cfg | = ( 1 < < buf_id ) ;
else
cfg & = ~ ( 1 < < buf_id ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_CIFCNTSEQ ) ;
2012-12-14 12:58:55 +04:00
2014-08-28 13:07:38 +04:00
buf_num = hweight32 ( cfg ) ;
2012-12-14 12:58:55 +04:00
2014-08-28 13:07:38 +04:00
if ( buf_type = = IPP_BUF_ENQUEUE & & buf_num > = FIMC_BUF_START )
fimc_mask_irq ( ctx , true ) ;
else if ( buf_type = = IPP_BUF_DEQUEUE & & buf_num < = FIMC_BUF_STOP )
2014-05-19 14:54:07 +04:00
fimc_mask_irq ( ctx , false ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:09 +04:00
spin_unlock_irqrestore ( & ctx - > lock , flags ) ;
2012-12-14 12:58:55 +04:00
}
static int fimc_dst_set_addr ( struct device * dev ,
struct drm_exynos_ipp_buf_info * buf_info , u32 buf_id ,
enum drm_exynos_ipp_buf_type buf_type )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
struct exynos_drm_ippdrv * ippdrv = & ctx - > ippdrv ;
2012-12-22 12:49:22 +04:00
struct drm_exynos_ipp_cmd_node * c_node = ippdrv - > c_node ;
2012-12-14 12:58:55 +04:00
struct drm_exynos_ipp_property * property ;
struct drm_exynos_ipp_config * config ;
if ( ! c_node ) {
DRM_ERROR ( " failed to get c_node. \n " ) ;
return - EINVAL ;
}
property = & c_node - > property ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " prop_id[%d]buf_id[%d]buf_type[%d] \n " ,
2012-12-14 12:58:55 +04:00
property - > prop_id , buf_id , buf_type ) ;
if ( buf_id > FIMC_MAX_DST ) {
dev_info ( ippdrv - > dev , " inavlid buf_id %d. \n " , buf_id ) ;
return - ENOMEM ;
}
/* address register set */
switch ( buf_type ) {
case IPP_BUF_ENQUEUE :
config = & property - > config [ EXYNOS_DRM_OPS_DST ] ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , buf_info - > base [ EXYNOS_DRM_PLANAR_Y ] ,
2012-12-14 12:58:55 +04:00
EXYNOS_CIOYSA ( buf_id ) ) ;
if ( config - > fmt = = DRM_FORMAT_YVU420 ) {
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , buf_info - > base [ EXYNOS_DRM_PLANAR_CR ] ,
2012-12-14 12:58:55 +04:00
EXYNOS_CIOCBSA ( buf_id ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , buf_info - > base [ EXYNOS_DRM_PLANAR_CB ] ,
2012-12-14 12:58:55 +04:00
EXYNOS_CIOCRSA ( buf_id ) ) ;
} else {
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , buf_info - > base [ EXYNOS_DRM_PLANAR_CB ] ,
2012-12-14 12:58:55 +04:00
EXYNOS_CIOCBSA ( buf_id ) ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , buf_info - > base [ EXYNOS_DRM_PLANAR_CR ] ,
2012-12-14 12:58:55 +04:00
EXYNOS_CIOCRSA ( buf_id ) ) ;
}
break ;
case IPP_BUF_DEQUEUE :
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , 0x0 , EXYNOS_CIOYSA ( buf_id ) ) ;
fimc_write ( ctx , 0x0 , EXYNOS_CIOCBSA ( buf_id ) ) ;
fimc_write ( ctx , 0x0 , EXYNOS_CIOCRSA ( buf_id ) ) ;
2012-12-14 12:58:55 +04:00
break ;
default :
/* bypass */
break ;
}
2014-08-28 13:07:38 +04:00
fimc_dst_set_buf_seq ( ctx , buf_id , buf_type ) ;
return 0 ;
2012-12-14 12:58:55 +04:00
}
static struct exynos_drm_ipp_ops fimc_dst_ops = {
. set_fmt = fimc_dst_set_fmt ,
. set_transf = fimc_dst_set_transf ,
. set_size = fimc_dst_set_size ,
. set_addr = fimc_dst_set_addr ,
} ;
static int fimc_clk_ctrl ( struct fimc_context * ctx , bool enable )
{
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " enable[%d] \n " , enable ) ;
2012-12-14 12:58:55 +04:00
if ( enable ) {
2013-04-23 15:34:37 +04:00
clk_prepare_enable ( ctx - > clocks [ FIMC_CLK_GATE ] ) ;
clk_prepare_enable ( ctx - > clocks [ FIMC_CLK_WB_A ] ) ;
2012-12-14 12:58:55 +04:00
ctx - > suspended = false ;
} else {
2013-04-23 15:34:37 +04:00
clk_disable_unprepare ( ctx - > clocks [ FIMC_CLK_GATE ] ) ;
clk_disable_unprepare ( ctx - > clocks [ FIMC_CLK_WB_A ] ) ;
2012-12-14 12:58:55 +04:00
ctx - > suspended = true ;
}
return 0 ;
}
static irqreturn_t fimc_irq_handler ( int irq , void * dev_id )
{
struct fimc_context * ctx = dev_id ;
struct exynos_drm_ippdrv * ippdrv = & ctx - > ippdrv ;
2012-12-22 12:49:22 +04:00
struct drm_exynos_ipp_cmd_node * c_node = ippdrv - > c_node ;
2012-12-14 12:58:55 +04:00
struct drm_exynos_ipp_event_work * event_work =
c_node - > event_work ;
int buf_id ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " fimc id[%d] \n " , ctx - > id ) ;
2012-12-14 12:58:55 +04:00
fimc_clear_irq ( ctx ) ;
if ( fimc_check_ovf ( ctx ) )
return IRQ_NONE ;
if ( ! fimc_check_frame_end ( ctx ) )
return IRQ_NONE ;
buf_id = fimc_get_buf_id ( ctx ) ;
if ( buf_id < 0 )
return IRQ_HANDLED ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " buf_id[%d] \n " , buf_id ) ;
2012-12-14 12:58:55 +04:00
2014-08-28 13:07:38 +04:00
fimc_dst_set_buf_seq ( ctx , buf_id , IPP_BUF_DEQUEUE ) ;
2012-12-14 12:58:55 +04:00
event_work - > ippdrv = ippdrv ;
event_work - > buf_id [ EXYNOS_DRM_OPS_DST ] = buf_id ;
2014-08-28 13:07:33 +04:00
queue_work ( ippdrv - > event_workq , & event_work - > work ) ;
2012-12-14 12:58:55 +04:00
return IRQ_HANDLED ;
}
static int fimc_init_prop_list ( struct exynos_drm_ippdrv * ippdrv )
{
2014-05-19 14:54:05 +04:00
struct drm_exynos_ipp_prop_list * prop_list = & ippdrv - > prop_list ;
2012-12-14 12:58:55 +04:00
prop_list - > version = 1 ;
prop_list - > writeback = 1 ;
prop_list - > refresh_min = FIMC_REFRESH_MIN ;
prop_list - > refresh_max = FIMC_REFRESH_MAX ;
prop_list - > flip = ( 1 < < EXYNOS_DRM_FLIP_NONE ) |
( 1 < < EXYNOS_DRM_FLIP_VERTICAL ) |
( 1 < < EXYNOS_DRM_FLIP_HORIZONTAL ) ;
prop_list - > degree = ( 1 < < EXYNOS_DRM_DEGREE_0 ) |
( 1 < < EXYNOS_DRM_DEGREE_90 ) |
( 1 < < EXYNOS_DRM_DEGREE_180 ) |
( 1 < < EXYNOS_DRM_DEGREE_270 ) ;
prop_list - > csc = 1 ;
prop_list - > crop = 1 ;
prop_list - > crop_max . hsize = FIMC_CROP_MAX ;
prop_list - > crop_max . vsize = FIMC_CROP_MAX ;
prop_list - > crop_min . hsize = FIMC_CROP_MIN ;
prop_list - > crop_min . vsize = FIMC_CROP_MIN ;
prop_list - > scale = 1 ;
prop_list - > scale_max . hsize = FIMC_SCALE_MAX ;
prop_list - > scale_max . vsize = FIMC_SCALE_MAX ;
prop_list - > scale_min . hsize = FIMC_SCALE_MIN ;
prop_list - > scale_min . vsize = FIMC_SCALE_MIN ;
return 0 ;
}
static inline bool fimc_check_drm_flip ( enum drm_exynos_flip flip )
{
switch ( flip ) {
case EXYNOS_DRM_FLIP_NONE :
case EXYNOS_DRM_FLIP_VERTICAL :
case EXYNOS_DRM_FLIP_HORIZONTAL :
2012-12-22 12:49:24 +04:00
case EXYNOS_DRM_FLIP_BOTH :
2012-12-14 12:58:55 +04:00
return true ;
default :
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " invalid flip \n " ) ;
2012-12-14 12:58:55 +04:00
return false ;
}
}
static int fimc_ippdrv_check_property ( struct device * dev ,
struct drm_exynos_ipp_property * property )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
struct exynos_drm_ippdrv * ippdrv = & ctx - > ippdrv ;
2014-05-19 14:54:05 +04:00
struct drm_exynos_ipp_prop_list * pp = & ippdrv - > prop_list ;
2012-12-14 12:58:55 +04:00
struct drm_exynos_ipp_config * config ;
struct drm_exynos_pos * pos ;
struct drm_exynos_sz * sz ;
bool swap ;
int i ;
for_each_ipp_ops ( i ) {
if ( ( i = = EXYNOS_DRM_OPS_SRC ) & &
( property - > cmd = = IPP_CMD_WB ) )
continue ;
config = & property - > config [ i ] ;
pos = & config - > pos ;
sz = & config - > sz ;
/* check for flip */
if ( ! fimc_check_drm_flip ( config - > flip ) ) {
DRM_ERROR ( " invalid flip. \n " ) ;
goto err_property ;
}
/* check for degree */
switch ( config - > degree ) {
case EXYNOS_DRM_DEGREE_90 :
case EXYNOS_DRM_DEGREE_270 :
swap = true ;
break ;
case EXYNOS_DRM_DEGREE_0 :
case EXYNOS_DRM_DEGREE_180 :
swap = false ;
break ;
default :
DRM_ERROR ( " invalid degree. \n " ) ;
goto err_property ;
}
/* check for buffer bound */
if ( ( pos - > x + pos - > w > sz - > hsize ) | |
( pos - > y + pos - > h > sz - > vsize ) ) {
DRM_ERROR ( " out of buf bound. \n " ) ;
goto err_property ;
}
/* check for crop */
if ( ( i = = EXYNOS_DRM_OPS_SRC ) & & ( pp - > crop ) ) {
if ( swap ) {
if ( ( pos - > h < pp - > crop_min . hsize ) | |
( sz - > vsize > pp - > crop_max . hsize ) | |
( pos - > w < pp - > crop_min . vsize ) | |
( sz - > hsize > pp - > crop_max . vsize ) ) {
DRM_ERROR ( " out of crop size. \n " ) ;
goto err_property ;
}
} else {
if ( ( pos - > w < pp - > crop_min . hsize ) | |
( sz - > hsize > pp - > crop_max . hsize ) | |
( pos - > h < pp - > crop_min . vsize ) | |
( sz - > vsize > pp - > crop_max . vsize ) ) {
DRM_ERROR ( " out of crop size. \n " ) ;
goto err_property ;
}
}
}
/* check for scale */
if ( ( i = = EXYNOS_DRM_OPS_DST ) & & ( pp - > scale ) ) {
if ( swap ) {
if ( ( pos - > h < pp - > scale_min . hsize ) | |
( sz - > vsize > pp - > scale_max . hsize ) | |
( pos - > w < pp - > scale_min . vsize ) | |
( sz - > hsize > pp - > scale_max . vsize ) ) {
DRM_ERROR ( " out of scale size. \n " ) ;
goto err_property ;
}
} else {
if ( ( pos - > w < pp - > scale_min . hsize ) | |
( sz - > hsize > pp - > scale_max . hsize ) | |
( pos - > h < pp - > scale_min . vsize ) | |
( sz - > vsize > pp - > scale_max . vsize ) ) {
DRM_ERROR ( " out of scale size. \n " ) ;
goto err_property ;
}
}
}
}
return 0 ;
err_property :
for_each_ipp_ops ( i ) {
if ( ( i = = EXYNOS_DRM_OPS_SRC ) & &
( property - > cmd = = IPP_CMD_WB ) )
continue ;
config = & property - > config [ i ] ;
pos = & config - > pos ;
sz = & config - > sz ;
DRM_ERROR ( " [%s]f[%d]r[%d]pos[%d %d %d %d]sz[%d %d] \n " ,
i ? " dst " : " src " , config - > flip , config - > degree ,
pos - > x , pos - > y , pos - > w , pos - > h ,
sz - > hsize , sz - > vsize ) ;
}
return - EINVAL ;
}
static void fimc_clear_addr ( struct fimc_context * ctx )
{
int i ;
for ( i = 0 ; i < FIMC_MAX_SRC ; i + + ) {
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , 0 , EXYNOS_CIIYSA ( i ) ) ;
fimc_write ( ctx , 0 , EXYNOS_CIICBSA ( i ) ) ;
fimc_write ( ctx , 0 , EXYNOS_CIICRSA ( i ) ) ;
2012-12-14 12:58:55 +04:00
}
for ( i = 0 ; i < FIMC_MAX_DST ; i + + ) {
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , 0 , EXYNOS_CIOYSA ( i ) ) ;
fimc_write ( ctx , 0 , EXYNOS_CIOCBSA ( i ) ) ;
fimc_write ( ctx , 0 , EXYNOS_CIOCRSA ( i ) ) ;
2012-12-14 12:58:55 +04:00
}
}
static int fimc_ippdrv_reset ( struct device * dev )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
/* reset h/w block */
2012-12-22 12:49:27 +04:00
fimc_sw_reset ( ctx ) ;
2012-12-14 12:58:55 +04:00
/* reset scaler capability */
memset ( & ctx - > sc , 0x0 , sizeof ( ctx - > sc ) ) ;
fimc_clear_addr ( ctx ) ;
return 0 ;
}
static int fimc_ippdrv_start ( struct device * dev , enum drm_exynos_ipp_cmd cmd )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
struct exynos_drm_ippdrv * ippdrv = & ctx - > ippdrv ;
2012-12-22 12:49:22 +04:00
struct drm_exynos_ipp_cmd_node * c_node = ippdrv - > c_node ;
2012-12-14 12:58:55 +04:00
struct drm_exynos_ipp_property * property ;
struct drm_exynos_ipp_config * config ;
struct drm_exynos_pos img_pos [ EXYNOS_DRM_OPS_MAX ] ;
struct drm_exynos_ipp_set_wb set_wb ;
int ret , i ;
u32 cfg0 , cfg1 ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " cmd[%d] \n " , cmd ) ;
2012-12-14 12:58:55 +04:00
if ( ! c_node ) {
DRM_ERROR ( " failed to get c_node. \n " ) ;
return - EINVAL ;
}
property = & c_node - > property ;
2014-05-19 14:54:07 +04:00
fimc_mask_irq ( ctx , true ) ;
2012-12-14 12:58:55 +04:00
for_each_ipp_ops ( i ) {
config = & property - > config [ i ] ;
img_pos [ i ] = config - > pos ;
}
ret = fimc_set_prescaler ( ctx , & ctx - > sc ,
& img_pos [ EXYNOS_DRM_OPS_SRC ] ,
& img_pos [ EXYNOS_DRM_OPS_DST ] ) ;
if ( ret ) {
dev_err ( dev , " failed to set precalser. \n " ) ;
return ret ;
}
/* If set ture, we can save jpeg about screen */
fimc_handle_jpeg ( ctx , false ) ;
fimc_set_scaler ( ctx , & ctx - > sc ) ;
fimc_set_polarity ( ctx , & ctx - > pol ) ;
switch ( cmd ) {
case IPP_CMD_M2M :
fimc_set_type_ctrl ( ctx , FIMC_WB_NONE ) ;
fimc_handle_lastend ( ctx , false ) ;
/* setup dma */
2014-05-19 14:54:08 +04:00
cfg0 = fimc_read ( ctx , EXYNOS_MSCTRL ) ;
2012-12-14 12:58:55 +04:00
cfg0 & = ~ EXYNOS_MSCTRL_INPUT_MASK ;
cfg0 | = EXYNOS_MSCTRL_INPUT_MEMORY ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg0 , EXYNOS_MSCTRL ) ;
2012-12-14 12:58:55 +04:00
break ;
case IPP_CMD_WB :
fimc_set_type_ctrl ( ctx , FIMC_WB_A ) ;
fimc_handle_lastend ( ctx , true ) ;
/* setup FIMD */
2013-04-23 15:34:38 +04:00
ret = fimc_set_camblk_fimd0_wb ( ctx ) ;
if ( ret < 0 ) {
dev_err ( dev , " camblk setup failed. \n " ) ;
return ret ;
}
2012-12-14 12:58:55 +04:00
set_wb . enable = 1 ;
set_wb . refresh = property - > refresh_rate ;
exynos_drm_ippnb_send_event ( IPP_SET_WRITEBACK , ( void * ) & set_wb ) ;
break ;
case IPP_CMD_OUTPUT :
default :
ret = - EINVAL ;
dev_err ( dev , " invalid operations. \n " ) ;
return ret ;
}
/* Reset status */
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , 0x0 , EXYNOS_CISTATUS ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
cfg0 = fimc_read ( ctx , EXYNOS_CIIMGCPT ) ;
2012-12-14 12:58:55 +04:00
cfg0 & = ~ EXYNOS_CIIMGCPT_IMGCPTEN_SC ;
cfg0 | = EXYNOS_CIIMGCPT_IMGCPTEN_SC ;
/* Scaler */
2014-05-19 14:54:08 +04:00
cfg1 = fimc_read ( ctx , EXYNOS_CISCCTRL ) ;
2012-12-14 12:58:55 +04:00
cfg1 & = ~ EXYNOS_CISCCTRL_SCAN_MASK ;
cfg1 | = ( EXYNOS_CISCCTRL_PROGRESSIVE |
EXYNOS_CISCCTRL_SCALERSTART ) ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg1 , EXYNOS_CISCCTRL ) ;
2012-12-14 12:58:55 +04:00
/* Enable image capture*/
cfg0 | = EXYNOS_CIIMGCPT_IMGCPTEN ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg0 , EXYNOS_CIIMGCPT ) ;
2012-12-14 12:58:55 +04:00
/* Disable frame end irq */
2014-05-19 14:54:08 +04:00
fimc_clear_bits ( ctx , EXYNOS_CIGCTRL , EXYNOS_CIGCTRL_IRQ_END_DISABLE ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:08 +04:00
fimc_clear_bits ( ctx , EXYNOS_CIOCTRL , EXYNOS_CIOCTRL_WEAVE_MASK ) ;
2012-12-14 12:58:55 +04:00
2014-08-28 13:07:37 +04:00
if ( cmd = = IPP_CMD_M2M )
2014-05-19 14:54:08 +04:00
fimc_set_bits ( ctx , EXYNOS_MSCTRL , EXYNOS_MSCTRL_ENVID ) ;
2012-12-14 12:58:55 +04:00
return 0 ;
}
static void fimc_ippdrv_stop ( struct device * dev , enum drm_exynos_ipp_cmd cmd )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
struct drm_exynos_ipp_set_wb set_wb = { 0 , 0 } ;
u32 cfg ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " cmd[%d] \n " , cmd ) ;
2012-12-14 12:58:55 +04:00
switch ( cmd ) {
case IPP_CMD_M2M :
/* Source clear */
2014-05-19 14:54:08 +04:00
cfg = fimc_read ( ctx , EXYNOS_MSCTRL ) ;
2012-12-14 12:58:55 +04:00
cfg & = ~ EXYNOS_MSCTRL_INPUT_MASK ;
cfg & = ~ EXYNOS_MSCTRL_ENVID ;
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , cfg , EXYNOS_MSCTRL ) ;
2012-12-14 12:58:55 +04:00
break ;
case IPP_CMD_WB :
exynos_drm_ippnb_send_event ( IPP_SET_WRITEBACK , ( void * ) & set_wb ) ;
break ;
case IPP_CMD_OUTPUT :
default :
dev_err ( dev , " invalid operations. \n " ) ;
break ;
}
2014-05-19 14:54:07 +04:00
fimc_mask_irq ( ctx , false ) ;
2012-12-14 12:58:55 +04:00
/* reset sequence */
2014-05-19 14:54:08 +04:00
fimc_write ( ctx , 0x0 , EXYNOS_CIFCNTSEQ ) ;
2012-12-14 12:58:55 +04:00
/* Scaler disable */
2014-05-19 14:54:08 +04:00
fimc_clear_bits ( ctx , EXYNOS_CISCCTRL , EXYNOS_CISCCTRL_SCALERSTART ) ;
2012-12-14 12:58:55 +04:00
/* Disable image capture */
2014-05-19 14:54:08 +04:00
fimc_clear_bits ( ctx , EXYNOS_CIIMGCPT ,
EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN ) ;
2012-12-14 12:58:55 +04:00
/* Enable frame end irq */
2014-05-19 14:54:08 +04:00
fimc_set_bits ( ctx , EXYNOS_CIGCTRL , EXYNOS_CIGCTRL_IRQ_END_DISABLE ) ;
2012-12-14 12:58:55 +04:00
}
2013-04-23 15:34:37 +04:00
static void fimc_put_clocks ( struct fimc_context * ctx )
{
int i ;
for ( i = 0 ; i < FIMC_CLKS_MAX ; i + + ) {
if ( IS_ERR ( ctx - > clocks [ i ] ) )
continue ;
clk_put ( ctx - > clocks [ i ] ) ;
ctx - > clocks [ i ] = ERR_PTR ( - EINVAL ) ;
}
}
static int fimc_setup_clocks ( struct fimc_context * ctx )
{
struct device * fimc_dev = ctx - > ippdrv . dev ;
struct device * dev ;
int ret , i ;
for ( i = 0 ; i < FIMC_CLKS_MAX ; i + + )
ctx - > clocks [ i ] = ERR_PTR ( - EINVAL ) ;
for ( i = 0 ; i < FIMC_CLKS_MAX ; i + + ) {
if ( i = = FIMC_CLK_WB_A | | i = = FIMC_CLK_WB_B )
dev = fimc_dev - > parent ;
else
dev = fimc_dev ;
ctx - > clocks [ i ] = clk_get ( dev , fimc_clock_names [ i ] ) ;
if ( IS_ERR ( ctx - > clocks [ i ] ) ) {
if ( i > = FIMC_CLK_MUX )
break ;
ret = PTR_ERR ( ctx - > clocks [ i ] ) ;
dev_err ( fimc_dev , " failed to get clock: %s \n " ,
fimc_clock_names [ i ] ) ;
goto e_clk_free ;
}
}
/* Optional FIMC LCLK parent clock setting */
if ( ! IS_ERR ( ctx - > clocks [ FIMC_CLK_PARENT ] ) ) {
ret = clk_set_parent ( ctx - > clocks [ FIMC_CLK_MUX ] ,
ctx - > clocks [ FIMC_CLK_PARENT ] ) ;
if ( ret < 0 ) {
dev_err ( fimc_dev , " failed to set parent. \n " ) ;
goto e_clk_free ;
}
}
ret = clk_set_rate ( ctx - > clocks [ FIMC_CLK_LCLK ] , ctx - > clk_frequency ) ;
if ( ret < 0 )
goto e_clk_free ;
ret = clk_prepare_enable ( ctx - > clocks [ FIMC_CLK_LCLK ] ) ;
if ( ! ret )
return ret ;
e_clk_free :
fimc_put_clocks ( ctx ) ;
return ret ;
}
2013-04-23 15:34:38 +04:00
static int fimc_parse_dt ( struct fimc_context * ctx )
{
struct device_node * node = ctx - > ippdrv . dev - > of_node ;
/* Handle only devices that support the LCD Writeback data path */
if ( ! of_property_read_bool ( node , " samsung,lcd-wb " ) )
return - ENODEV ;
if ( of_property_read_u32 ( node , " clock-frequency " ,
& ctx - > clk_frequency ) )
ctx - > clk_frequency = FIMC_DEFAULT_LCLK_FREQUENCY ;
ctx - > id = of_alias_get_id ( node , " fimc " ) ;
if ( ctx - > id < 0 ) {
dev_err ( ctx - > ippdrv . dev , " failed to get node alias id. \n " ) ;
return - EINVAL ;
}
return 0 ;
}
2012-12-22 03:09:25 +04:00
static int fimc_probe ( struct platform_device * pdev )
2012-12-14 12:58:55 +04:00
{
struct device * dev = & pdev - > dev ;
struct fimc_context * ctx ;
struct resource * res ;
struct exynos_drm_ippdrv * ippdrv ;
int ret ;
2013-04-23 15:34:38 +04:00
if ( ! dev - > of_node ) {
dev_err ( dev , " device tree node not found. \n " ) ;
return - ENODEV ;
2012-12-14 12:58:55 +04:00
}
ctx = devm_kzalloc ( dev , sizeof ( * ctx ) , GFP_KERNEL ) ;
if ( ! ctx )
return - ENOMEM ;
2013-04-23 15:34:38 +04:00
ctx - > ippdrv . dev = dev ;
ret = fimc_parse_dt ( ctx ) ;
if ( ret < 0 )
return ret ;
ctx - > sysreg = syscon_regmap_lookup_by_phandle ( dev - > of_node ,
" samsung,sysreg " ) ;
if ( IS_ERR ( ctx - > sysreg ) ) {
dev_err ( dev , " syscon regmap lookup failed. \n " ) ;
return PTR_ERR ( ctx - > sysreg ) ;
}
2012-12-14 12:58:55 +04:00
/* resource memory */
ctx - > regs_res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-01-21 14:09:02 +04:00
ctx - > regs = devm_ioremap_resource ( dev , ctx - > regs_res ) ;
if ( IS_ERR ( ctx - > regs ) )
return PTR_ERR ( ctx - > regs ) ;
2012-12-14 12:58:55 +04:00
/* resource irq */
res = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
if ( ! res ) {
dev_err ( dev , " failed to request irq resource. \n " ) ;
2012-12-28 14:26:18 +04:00
return - ENOENT ;
2012-12-14 12:58:55 +04:00
}
ctx - > irq = res - > start ;
2013-05-22 16:14:17 +04:00
ret = devm_request_threaded_irq ( dev , ctx - > irq , NULL , fimc_irq_handler ,
2012-12-14 12:58:55 +04:00
IRQF_ONESHOT , " drm_fimc " , ctx ) ;
if ( ret < 0 ) {
dev_err ( dev , " failed to request irq. \n " ) ;
2012-12-28 14:26:18 +04:00
return ret ;
2012-12-14 12:58:55 +04:00
}
2013-04-23 15:34:37 +04:00
ret = fimc_setup_clocks ( ctx ) ;
if ( ret < 0 )
2013-05-22 16:14:17 +04:00
return ret ;
2012-12-14 12:58:55 +04:00
ippdrv = & ctx - > ippdrv ;
ippdrv - > ops [ EXYNOS_DRM_OPS_SRC ] = & fimc_src_ops ;
ippdrv - > ops [ EXYNOS_DRM_OPS_DST ] = & fimc_dst_ops ;
ippdrv - > check_property = fimc_ippdrv_check_property ;
ippdrv - > reset = fimc_ippdrv_reset ;
ippdrv - > start = fimc_ippdrv_start ;
ippdrv - > stop = fimc_ippdrv_stop ;
ret = fimc_init_prop_list ( ippdrv ) ;
if ( ret < 0 ) {
dev_err ( dev , " failed to init property list. \n " ) ;
2013-04-23 15:34:37 +04:00
goto err_put_clk ;
2012-12-14 12:58:55 +04:00
}
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " id[%d]ippdrv[0x%x] \n " , ctx - > id , ( int ) ippdrv ) ;
2012-12-14 12:58:55 +04:00
2014-05-19 14:54:09 +04:00
spin_lock_init ( & ctx - > lock ) ;
2012-12-14 12:58:55 +04:00
platform_set_drvdata ( pdev , ctx ) ;
pm_runtime_set_active ( dev ) ;
pm_runtime_enable ( dev ) ;
ret = exynos_drm_ippdrv_register ( ippdrv ) ;
if ( ret < 0 ) {
dev_err ( dev , " failed to register drm fimc device. \n " ) ;
2013-04-23 15:34:37 +04:00
goto err_pm_dis ;
2012-12-14 12:58:55 +04:00
}
2013-05-22 16:14:14 +04:00
dev_info ( dev , " drm fimc registered successfully. \n " ) ;
2012-12-14 12:58:55 +04:00
return 0 ;
2013-04-23 15:34:37 +04:00
err_pm_dis :
2012-12-14 12:58:55 +04:00
pm_runtime_disable ( dev ) ;
2013-04-23 15:34:37 +04:00
err_put_clk :
fimc_put_clocks ( ctx ) ;
2012-12-24 12:33:43 +04:00
2012-12-14 12:58:55 +04:00
return ret ;
}
2012-12-22 03:09:25 +04:00
static int fimc_remove ( struct platform_device * pdev )
2012-12-14 12:58:55 +04:00
{
struct device * dev = & pdev - > dev ;
struct fimc_context * ctx = get_fimc_context ( dev ) ;
struct exynos_drm_ippdrv * ippdrv = & ctx - > ippdrv ;
exynos_drm_ippdrv_unregister ( ippdrv ) ;
2013-04-23 15:34:37 +04:00
fimc_put_clocks ( ctx ) ;
2012-12-14 12:58:55 +04:00
pm_runtime_set_suspended ( dev ) ;
pm_runtime_disable ( dev ) ;
return 0 ;
}
# ifdef CONFIG_PM_SLEEP
static int fimc_suspend ( struct device * dev )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " id[%d] \n " , ctx - > id ) ;
2012-12-14 12:58:55 +04:00
if ( pm_runtime_suspended ( dev ) )
return 0 ;
return fimc_clk_ctrl ( ctx , false ) ;
}
static int fimc_resume ( struct device * dev )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " id[%d] \n " , ctx - > id ) ;
2012-12-14 12:58:55 +04:00
if ( ! pm_runtime_suspended ( dev ) )
return fimc_clk_ctrl ( ctx , true ) ;
return 0 ;
}
# endif
2014-12-04 03:04:55 +03:00
# ifdef CONFIG_PM
2012-12-14 12:58:55 +04:00
static int fimc_runtime_suspend ( struct device * dev )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " id[%d] \n " , ctx - > id ) ;
2012-12-14 12:58:55 +04:00
return fimc_clk_ctrl ( ctx , false ) ;
}
static int fimc_runtime_resume ( struct device * dev )
{
struct fimc_context * ctx = get_fimc_context ( dev ) ;
2013-06-12 05:44:40 +04:00
DRM_DEBUG_KMS ( " id[%d] \n " , ctx - > id ) ;
2012-12-14 12:58:55 +04:00
return fimc_clk_ctrl ( ctx , true ) ;
}
# endif
static const struct dev_pm_ops fimc_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( fimc_suspend , fimc_resume )
SET_RUNTIME_PM_OPS ( fimc_runtime_suspend , fimc_runtime_resume , NULL )
} ;
2013-04-23 15:34:38 +04:00
static const struct of_device_id fimc_of_match [ ] = {
{ . compatible = " samsung,exynos4210-fimc " } ,
{ . compatible = " samsung,exynos4212-fimc " } ,
{ } ,
} ;
2014-07-19 00:36:41 +04:00
MODULE_DEVICE_TABLE ( of , fimc_of_match ) ;
2013-04-23 15:34:38 +04:00
2012-12-14 12:58:55 +04:00
struct platform_driver fimc_driver = {
. probe = fimc_probe ,
2012-12-22 03:09:25 +04:00
. remove = fimc_remove ,
2012-12-14 12:58:55 +04:00
. driver = {
2013-04-23 15:34:38 +04:00
. of_match_table = fimc_of_match ,
2012-12-14 12:58:55 +04:00
. name = " exynos-drm-fimc " ,
. owner = THIS_MODULE ,
. pm = & fimc_pm_ops ,
} ,
} ;