2010-08-03 09:50:29 -03:00
/*
2012-05-08 15:51:24 -03:00
* Samsung S5P / EXYNOS4 SoC series FIMC ( CAMIF ) driver
2010-08-03 09:50:29 -03:00
*
2011-04-28 09:06:19 -03:00
* Copyright ( C ) 2010 - 2011 Samsung Electronics Co . , Ltd .
* Contact : Sylwester Nawrocki , < s . nawrocki @ samsung . com >
2010-08-03 09:50:29 -03:00
*
* 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/module.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/bug.h>
# include <linux/interrupt.h>
# include <linux/device.h>
# include <linux/platform_device.h>
2011-09-02 06:25:32 -03:00
# include <linux/pm_runtime.h>
2010-08-03 09:50:29 -03:00
# include <linux/list.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <linux/clk.h>
# include <media/v4l2-ioctl.h>
2010-12-01 10:14:59 -03:00
# include <media/videobuf2-core.h>
# include <media/videobuf2-dma-contig.h>
2010-08-03 09:50:29 -03:00
# include "fimc-core.h"
2011-09-01 06:01:08 -03:00
# include "fimc-mdevice.h"
2010-08-03 09:50:29 -03:00
2010-12-27 15:34:43 -03:00
static char * fimc_clocks [ MAX_FIMC_CLOCKS ] = {
2011-06-10 15:36:45 -03:00
" sclk_fimc " , " fimc "
2010-12-27 15:34:43 -03:00
} ;
2010-08-03 09:50:29 -03:00
static struct fimc_fmt fimc_formats [ ] = {
{
2010-12-08 14:05:08 -03:00
. name = " RGB565 " ,
2011-11-04 10:07:06 -03:00
. fourcc = V4L2_PIX_FMT_RGB565 ,
2010-12-08 14:05:08 -03:00
. depth = { 16 } ,
. color = S5P_FIMC_RGB565 ,
. memplanes = 1 ,
. colplanes = 1 ,
. flags = FMT_FLAGS_M2M ,
2010-08-03 09:50:29 -03:00
} , {
2010-12-08 14:05:08 -03:00
. name = " BGR666 " ,
. fourcc = V4L2_PIX_FMT_BGR666 ,
. depth = { 32 } ,
. color = S5P_FIMC_RGB666 ,
. memplanes = 1 ,
. colplanes = 1 ,
. flags = FMT_FLAGS_M2M ,
2010-08-03 09:50:29 -03:00
} , {
2011-12-01 14:02:24 -03:00
. name = " ARGB8888, 32 bpp " ,
2010-12-08 14:05:08 -03:00
. fourcc = V4L2_PIX_FMT_RGB32 ,
. depth = { 32 } ,
. color = S5P_FIMC_RGB888 ,
. memplanes = 1 ,
. colplanes = 1 ,
2011-12-01 14:02:24 -03:00
. flags = FMT_FLAGS_M2M | FMT_HAS_ALPHA ,
} , {
. name = " ARGB1555 " ,
. fourcc = V4L2_PIX_FMT_RGB555 ,
. depth = { 16 } ,
. color = S5P_FIMC_RGB555 ,
. memplanes = 1 ,
. colplanes = 1 ,
. flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA ,
} , {
. name = " ARGB4444 " ,
. fourcc = V4L2_PIX_FMT_RGB444 ,
. depth = { 16 } ,
. color = S5P_FIMC_RGB444 ,
. memplanes = 1 ,
. colplanes = 1 ,
. flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA ,
2010-08-03 09:50:29 -03:00
} , {
2010-12-08 14:05:08 -03:00
. name = " YUV 4:2:2 packed, YCbYCr " ,
. fourcc = V4L2_PIX_FMT_YUYV ,
. depth = { 16 } ,
. color = S5P_FIMC_YCBYCR422 ,
. memplanes = 1 ,
. colplanes = 1 ,
. mbus_code = V4L2_MBUS_FMT_YUYV8_2X8 ,
. flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM ,
2010-10-07 10:06:16 -03:00
} , {
2010-12-08 14:05:08 -03:00
. name = " YUV 4:2:2 packed, CbYCrY " ,
. fourcc = V4L2_PIX_FMT_UYVY ,
. depth = { 16 } ,
. color = S5P_FIMC_CBYCRY422 ,
. memplanes = 1 ,
. colplanes = 1 ,
. mbus_code = V4L2_MBUS_FMT_UYVY8_2X8 ,
. flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM ,
2010-08-03 09:50:29 -03:00
} , {
2010-12-08 14:05:08 -03:00
. name = " YUV 4:2:2 packed, CrYCbY " ,
. fourcc = V4L2_PIX_FMT_VYUY ,
. depth = { 16 } ,
. color = S5P_FIMC_CRYCBY422 ,
. memplanes = 1 ,
. colplanes = 1 ,
. mbus_code = V4L2_MBUS_FMT_VYUY8_2X8 ,
. flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM ,
2010-08-03 09:50:29 -03:00
} , {
2010-12-08 14:05:08 -03:00
. name = " YUV 4:2:2 packed, YCrYCb " ,
. fourcc = V4L2_PIX_FMT_YVYU ,
. depth = { 16 } ,
. color = S5P_FIMC_YCRYCB422 ,
. memplanes = 1 ,
. colplanes = 1 ,
. mbus_code = V4L2_MBUS_FMT_YVYU8_2X8 ,
. flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM ,
2010-08-03 09:50:29 -03:00
} , {
2010-12-08 14:05:08 -03:00
. name = " YUV 4:2:2 planar, Y/Cb/Cr " ,
. fourcc = V4L2_PIX_FMT_YUV422P ,
. depth = { 12 } ,
. color = S5P_FIMC_YCBYCR422 ,
. memplanes = 1 ,
. colplanes = 3 ,
. flags = FMT_FLAGS_M2M ,
2010-08-03 09:50:29 -03:00
} , {
2010-12-08 14:05:08 -03:00
. name = " YUV 4:2:2 planar, Y/CbCr " ,
. fourcc = V4L2_PIX_FMT_NV16 ,
. depth = { 16 } ,
. color = S5P_FIMC_YCBYCR422 ,
. memplanes = 1 ,
. colplanes = 2 ,
. flags = FMT_FLAGS_M2M ,
2010-08-03 09:50:29 -03:00
} , {
2010-12-08 14:05:08 -03:00
. name = " YUV 4:2:2 planar, Y/CrCb " ,
. fourcc = V4L2_PIX_FMT_NV61 ,
. depth = { 16 } ,
. color = S5P_FIMC_YCRYCB422 ,
. memplanes = 1 ,
. colplanes = 2 ,
. flags = FMT_FLAGS_M2M ,
2010-08-03 09:50:29 -03:00
} , {
2010-12-08 14:05:08 -03:00
. name = " YUV 4:2:0 planar, YCbCr " ,
. fourcc = V4L2_PIX_FMT_YUV420 ,
. depth = { 12 } ,
. color = S5P_FIMC_YCBCR420 ,
. memplanes = 1 ,
. colplanes = 3 ,
. flags = FMT_FLAGS_M2M ,
2010-08-03 09:50:29 -03:00
} , {
2010-12-08 14:05:08 -03:00
. name = " YUV 4:2:0 planar, Y/CbCr " ,
. fourcc = V4L2_PIX_FMT_NV12 ,
. depth = { 12 } ,
. color = S5P_FIMC_YCBCR420 ,
. memplanes = 1 ,
. colplanes = 2 ,
. flags = FMT_FLAGS_M2M ,
} , {
. name = " YUV 4:2:0 non-contiguous 2-planar, Y/CbCr " ,
. fourcc = V4L2_PIX_FMT_NV12M ,
. color = S5P_FIMC_YCBCR420 ,
. depth = { 8 , 4 } ,
. memplanes = 2 ,
. colplanes = 2 ,
. flags = FMT_FLAGS_M2M ,
} , {
. name = " YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr " ,
. fourcc = V4L2_PIX_FMT_YUV420M ,
. color = S5P_FIMC_YCBCR420 ,
. depth = { 8 , 2 , 2 } ,
. memplanes = 3 ,
. colplanes = 3 ,
. flags = FMT_FLAGS_M2M ,
} , {
. name = " YUV 4:2:0 non-contiguous 2-planar, Y/CbCr, tiled " ,
. fourcc = V4L2_PIX_FMT_NV12MT ,
. color = S5P_FIMC_YCBCR420 ,
. depth = { 8 , 4 } ,
. memplanes = 2 ,
. colplanes = 2 ,
. flags = FMT_FLAGS_M2M ,
2011-08-26 14:57:06 -03:00
} , {
. name = " JPEG encoded data " ,
. fourcc = V4L2_PIX_FMT_JPEG ,
. color = S5P_FIMC_JPEG ,
. depth = { 8 } ,
. memplanes = 1 ,
. colplanes = 1 ,
. mbus_code = V4L2_MBUS_FMT_JPEG_1X8 ,
. flags = FMT_FLAGS_CAM ,
2010-10-07 10:06:16 -03:00
} ,
2010-10-08 05:01:14 -03:00
} ;
2010-08-03 09:50:29 -03:00
2012-05-08 15:51:24 -03:00
struct fimc_fmt * fimc_get_format ( unsigned int index )
2011-12-01 14:02:24 -03:00
{
2012-05-08 15:51:24 -03:00
if ( index > = ARRAY_SIZE ( fimc_formats ) )
return NULL ;
return & fimc_formats [ index ] ;
2011-12-01 14:02:24 -03:00
}
2011-08-26 14:57:06 -03:00
int fimc_check_scaler_ratio ( struct fimc_ctx * ctx , int sw , int sh ,
int dw , int dh , int rotation )
2010-08-03 09:50:29 -03:00
{
2011-08-26 14:57:06 -03:00
if ( rotation = = 90 | | rotation = = 270 )
swap ( dw , dh ) ;
2010-08-03 09:50:29 -03:00
2011-08-26 14:57:06 -03:00
if ( ! ctx - > scaler . enabled )
return ( sw = = dw & & sh = = dh ) ? 0 : - EINVAL ;
2010-08-03 09:50:29 -03:00
2011-08-26 14:57:06 -03:00
if ( ( sw > = SCALER_MAX_HRATIO * dw ) | | ( sh > = SCALER_MAX_VRATIO * dh ) )
2010-12-28 22:12:43 -03:00
return - EINVAL ;
2010-08-03 09:50:29 -03:00
return 0 ;
}
static int fimc_get_scaler_factor ( u32 src , u32 tar , u32 * ratio , u32 * shift )
{
2010-10-08 05:01:14 -03:00
u32 sh = 6 ;
if ( src > = 64 * tar )
2010-08-03 09:50:29 -03:00
return - EINVAL ;
2010-10-08 05:01:14 -03:00
while ( sh - - ) {
u32 tmp = 1 < < sh ;
if ( src > = tar * tmp ) {
* shift = sh , * ratio = tmp ;
return 0 ;
}
2010-08-03 09:50:29 -03:00
}
2010-10-08 05:01:14 -03:00
* shift = 0 , * ratio = 1 ;
2010-08-03 09:50:29 -03:00
return 0 ;
}
2010-10-07 10:06:16 -03:00
int fimc_set_scaler_info ( struct fimc_ctx * ctx )
2010-08-03 09:50:29 -03:00
{
2011-06-10 15:36:48 -03:00
struct samsung_fimc_variant * variant = ctx - > fimc_dev - > variant ;
struct device * dev = & ctx - > fimc_dev - > pdev - > dev ;
2010-08-03 09:50:29 -03:00
struct fimc_scaler * sc = & ctx - > scaler ;
struct fimc_frame * s_frame = & ctx - > s_frame ;
struct fimc_frame * d_frame = & ctx - > d_frame ;
int tx , ty , sx , sy ;
int ret ;
2010-10-08 05:01:22 -03:00
if ( ctx - > rotation = = 90 | | ctx - > rotation = = 270 ) {
ty = d_frame - > width ;
tx = d_frame - > height ;
} else {
tx = d_frame - > width ;
ty = d_frame - > height ;
}
2010-08-03 09:50:29 -03:00
if ( tx < = 0 | | ty < = 0 ) {
2011-06-10 15:36:48 -03:00
dev_err ( dev , " Invalid target size: %dx%d " , tx , ty ) ;
2010-08-03 09:50:29 -03:00
return - EINVAL ;
}
sx = s_frame - > width ;
sy = s_frame - > height ;
if ( sx < = 0 | | sy < = 0 ) {
2011-06-10 15:36:48 -03:00
dev_err ( dev , " Invalid source size: %dx%d " , sx , sy ) ;
2010-08-03 09:50:29 -03:00
return - EINVAL ;
}
sc - > real_width = sx ;
sc - > real_height = sy ;
ret = fimc_get_scaler_factor ( sx , tx , & sc - > pre_hratio , & sc - > hfactor ) ;
if ( ret )
return ret ;
ret = fimc_get_scaler_factor ( sy , ty , & sc - > pre_vratio , & sc - > vfactor ) ;
if ( ret )
return ret ;
sc - > pre_dst_width = sx / sc - > pre_hratio ;
sc - > pre_dst_height = sy / sc - > pre_vratio ;
2010-12-28 11:27:13 -03:00
if ( variant - > has_mainscaler_ext ) {
sc - > main_hratio = ( sx < < 14 ) / ( tx < < sc - > hfactor ) ;
sc - > main_vratio = ( sy < < 14 ) / ( ty < < sc - > vfactor ) ;
} else {
sc - > main_hratio = ( sx < < 8 ) / ( tx < < sc - > hfactor ) ;
sc - > main_vratio = ( sy < < 8 ) / ( ty < < sc - > vfactor ) ;
}
2010-08-03 09:50:29 -03:00
sc - > scaleup_h = ( tx > = sx ) ? 1 : 0 ;
sc - > scaleup_v = ( ty > = sy ) ? 1 : 0 ;
/* check to see if input and output size/format differ */
if ( s_frame - > fmt - > color = = d_frame - > fmt - > color
& & s_frame - > width = = d_frame - > width
& & s_frame - > height = = d_frame - > height )
sc - > copy_mode = 1 ;
else
sc - > copy_mode = 0 ;
return 0 ;
}
2011-09-02 06:25:32 -03:00
static irqreturn_t fimc_irq_handler ( int irq , void * priv )
2010-08-03 09:50:29 -03:00
{
2010-10-08 05:01:14 -03:00
struct fimc_dev * fimc = priv ;
2011-02-23 08:24:33 -03:00
struct fimc_ctx * ctx ;
2010-08-03 09:50:29 -03:00
fimc_hw_clear_irq ( fimc ) ;
2011-09-02 06:25:32 -03:00
spin_lock ( & fimc - > slock ) ;
2011-02-23 08:24:33 -03:00
if ( test_and_clear_bit ( ST_M2M_PEND , & fimc - > state ) ) {
2011-09-02 06:25:32 -03:00
if ( test_and_clear_bit ( ST_M2M_SUSPENDING , & fimc - > state ) ) {
set_bit ( ST_M2M_SUSPENDED , & fimc - > state ) ;
wake_up ( & fimc - > irq_queue ) ;
goto out ;
}
2011-02-23 08:24:33 -03:00
ctx = v4l2_m2m_get_curr_priv ( fimc - > m2m . m2m_dev ) ;
if ( ctx ! = NULL ) {
2011-09-02 06:25:32 -03:00
spin_unlock ( & fimc - > slock ) ;
2011-02-23 08:24:33 -03:00
fimc_m2m_job_finish ( ctx , VB2_BUF_STATE_DONE ) ;
2010-08-03 09:50:29 -03:00
2011-02-23 08:24:33 -03:00
if ( ctx - > state & FIMC_CTX_SHUT ) {
ctx - > state & = ~ FIMC_CTX_SHUT ;
wake_up ( & fimc - > irq_queue ) ;
}
2012-03-19 13:11:40 -03:00
return IRQ_HANDLED ;
2010-08-03 09:50:29 -03:00
}
2011-08-26 14:57:06 -03:00
} else if ( test_bit ( ST_CAPT_PEND , & fimc - > state ) ) {
2012-05-08 15:51:24 -03:00
int last_buf = test_bit ( ST_CAPT_JPEG , & fimc - > state ) & &
fimc - > vid_cap . reqbufs_count = = 1 ;
fimc_capture_irq_handler ( fimc , ! last_buf ) ;
2010-08-03 09:50:29 -03:00
}
2011-09-02 06:25:32 -03:00
out :
2010-08-03 09:50:29 -03:00
spin_unlock ( & fimc - > slock ) ;
return IRQ_HANDLED ;
}
2010-12-08 14:05:08 -03:00
/* The color format (colplanes, memplanes) must be already configured. */
2010-12-01 10:14:59 -03:00
int fimc_prepare_addr ( struct fimc_ctx * ctx , struct vb2_buffer * vb ,
2010-10-08 05:01:14 -03:00
struct fimc_frame * frame , struct fimc_addr * paddr )
2010-08-03 09:50:29 -03:00
{
int ret = 0 ;
2010-10-08 05:01:14 -03:00
u32 pix_size ;
2010-08-03 09:50:29 -03:00
2010-12-01 10:14:59 -03:00
if ( vb = = NULL | | frame = = NULL )
2010-08-03 09:50:29 -03:00
return - EINVAL ;
pix_size = frame - > width * frame - > height ;
2010-12-08 14:05:08 -03:00
dbg ( " memplanes= %d, colplanes= %d, pix_size= %d " ,
frame - > fmt - > memplanes , frame - > fmt - > colplanes , pix_size ) ;
2011-08-29 03:20:56 -03:00
paddr - > y = vb2_dma_contig_plane_dma_addr ( vb , 0 ) ;
2010-08-03 09:50:29 -03:00
2010-12-08 14:05:08 -03:00
if ( frame - > fmt - > memplanes = = 1 ) {
switch ( frame - > fmt - > colplanes ) {
2010-08-03 09:50:29 -03:00
case 1 :
paddr - > cb = 0 ;
paddr - > cr = 0 ;
break ;
case 2 :
/* decompose Y into Y/Cb */
paddr - > cb = ( u32 ) ( paddr - > y + pix_size ) ;
paddr - > cr = 0 ;
break ;
case 3 :
paddr - > cb = ( u32 ) ( paddr - > y + pix_size ) ;
/* decompose Y into Y/Cb/Cr */
if ( S5P_FIMC_YCBCR420 = = frame - > fmt - > color )
paddr - > cr = ( u32 ) ( paddr - > cb
+ ( pix_size > > 2 ) ) ;
else /* 422 */
paddr - > cr = ( u32 ) ( paddr - > cb
+ ( pix_size > > 1 ) ) ;
break ;
default :
return - EINVAL ;
}
2010-12-08 14:05:08 -03:00
} else {
if ( frame - > fmt - > memplanes > = 2 )
2011-08-29 03:20:56 -03:00
paddr - > cb = vb2_dma_contig_plane_dma_addr ( vb , 1 ) ;
2010-12-08 14:05:08 -03:00
if ( frame - > fmt - > memplanes = = 3 )
2011-08-29 03:20:56 -03:00
paddr - > cr = vb2_dma_contig_plane_dma_addr ( vb , 2 ) ;
2010-08-03 09:50:29 -03:00
}
2010-10-08 05:01:14 -03:00
dbg ( " PHYS_ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d " ,
paddr - > y , paddr - > cb , paddr - > cr , ret ) ;
2010-08-03 09:50:29 -03:00
return ret ;
}
/* Set order for 1 and 2 plane YCBCR 4:2:2 formats. */
2011-06-10 15:36:53 -03:00
void fimc_set_yuv_order ( struct fimc_ctx * ctx )
2010-08-03 09:50:29 -03:00
{
/* The one only mode supported in SoC. */
ctx - > in_order_2p = S5P_FIMC_LSB_CRCB ;
ctx - > out_order_2p = S5P_FIMC_LSB_CRCB ;
/* Set order for 1 plane input formats. */
switch ( ctx - > s_frame . fmt - > color ) {
case S5P_FIMC_YCRYCB422 :
2010-12-22 00:56:05 -03:00
ctx - > in_order_1p = S5P_MSCTRL_ORDER422_CBYCRY ;
2010-08-03 09:50:29 -03:00
break ;
case S5P_FIMC_CBYCRY422 :
2010-12-22 00:56:05 -03:00
ctx - > in_order_1p = S5P_MSCTRL_ORDER422_YCRYCB ;
2010-08-03 09:50:29 -03:00
break ;
case S5P_FIMC_CRYCBY422 :
2010-12-22 00:56:05 -03:00
ctx - > in_order_1p = S5P_MSCTRL_ORDER422_YCBYCR ;
2010-08-03 09:50:29 -03:00
break ;
case S5P_FIMC_YCBYCR422 :
default :
2010-12-22 00:56:05 -03:00
ctx - > in_order_1p = S5P_MSCTRL_ORDER422_CRYCBY ;
2010-08-03 09:50:29 -03:00
break ;
}
dbg ( " ctx->in_order_1p= %d " , ctx - > in_order_1p ) ;
switch ( ctx - > d_frame . fmt - > color ) {
case S5P_FIMC_YCRYCB422 :
2010-12-22 00:56:05 -03:00
ctx - > out_order_1p = S5P_CIOCTRL_ORDER422_CBYCRY ;
2010-08-03 09:50:29 -03:00
break ;
case S5P_FIMC_CBYCRY422 :
2010-12-22 00:56:05 -03:00
ctx - > out_order_1p = S5P_CIOCTRL_ORDER422_YCRYCB ;
2010-08-03 09:50:29 -03:00
break ;
case S5P_FIMC_CRYCBY422 :
2010-12-22 00:56:05 -03:00
ctx - > out_order_1p = S5P_CIOCTRL_ORDER422_YCBYCR ;
2010-08-03 09:50:29 -03:00
break ;
case S5P_FIMC_YCBYCR422 :
default :
2010-12-22 00:56:05 -03:00
ctx - > out_order_1p = S5P_CIOCTRL_ORDER422_CRYCBY ;
2010-08-03 09:50:29 -03:00
break ;
}
dbg ( " ctx->out_order_1p= %d " , ctx - > out_order_1p ) ;
}
2011-06-10 15:36:53 -03:00
void fimc_prepare_dma_offset ( struct fimc_ctx * ctx , struct fimc_frame * f )
2010-09-06 03:53:44 -03:00
{
struct samsung_fimc_variant * variant = ctx - > fimc_dev - > variant ;
2010-12-08 14:05:08 -03:00
u32 i , depth = 0 ;
for ( i = 0 ; i < f - > fmt - > colplanes ; i + + )
depth + = f - > fmt - > depth [ i ] ;
2010-09-06 03:53:44 -03:00
f - > dma_offset . y_h = f - > offs_h ;
if ( ! variant - > pix_hoff )
2010-12-08 14:05:08 -03:00
f - > dma_offset . y_h * = ( depth > > 3 ) ;
2010-09-06 03:53:44 -03:00
f - > dma_offset . y_v = f - > offs_v ;
f - > dma_offset . cb_h = f - > offs_h ;
f - > dma_offset . cb_v = f - > offs_v ;
f - > dma_offset . cr_h = f - > offs_h ;
f - > dma_offset . cr_v = f - > offs_v ;
if ( ! variant - > pix_hoff ) {
2010-12-08 14:05:08 -03:00
if ( f - > fmt - > colplanes = = 3 ) {
2010-09-06 03:53:44 -03:00
f - > dma_offset . cb_h > > = 1 ;
f - > dma_offset . cr_h > > = 1 ;
}
if ( f - > fmt - > color = = S5P_FIMC_YCBCR420 ) {
f - > dma_offset . cb_v > > = 1 ;
f - > dma_offset . cr_v > > = 1 ;
}
}
dbg ( " in_offset: color= %d, y_h= %d, y_v= %d " ,
f - > fmt - > color , f - > dma_offset . y_h , f - > dma_offset . y_v ) ;
}
2011-08-24 19:25:10 -03:00
/*
* V4L2 controls handling
*/
# define ctrl_to_ctx(__ctrl) \
container_of ( ( __ctrl ) - > handler , struct fimc_ctx , ctrl_handler )
2011-12-01 14:02:24 -03:00
static int __fimc_s_ctrl ( struct fimc_ctx * ctx , struct v4l2_ctrl * ctrl )
2011-08-24 19:25:10 -03:00
{
struct fimc_dev * fimc = ctx - > fimc_dev ;
struct samsung_fimc_variant * variant = fimc - > variant ;
2011-12-01 14:02:24 -03:00
unsigned int flags = FIMC_DST_FMT | FIMC_SRC_FMT ;
2011-08-24 19:25:10 -03:00
int ret = 0 ;
if ( ctrl - > flags & V4L2_CTRL_FLAG_INACTIVE )
return 0 ;
switch ( ctrl - > id ) {
case V4L2_CID_HFLIP :
ctx - > hflip = ctrl - > val ;
break ;
case V4L2_CID_VFLIP :
ctx - > vflip = ctrl - > val ;
break ;
case V4L2_CID_ROTATE :
if ( fimc_capture_pending ( fimc ) | |
2011-12-01 14:02:24 -03:00
( ctx - > state & flags ) = = flags ) {
2011-08-26 14:57:06 -03:00
ret = fimc_check_scaler_ratio ( ctx , ctx - > s_frame . width ,
2011-08-24 19:25:10 -03:00
ctx - > s_frame . height , ctx - > d_frame . width ,
ctx - > d_frame . height , ctrl - > val ) ;
2011-12-01 14:02:24 -03:00
if ( ret )
return - EINVAL ;
2011-08-24 19:25:10 -03:00
}
if ( ( ctrl - > val = = 90 | | ctrl - > val = = 270 ) & &
! variant - > has_out_rot )
return - EINVAL ;
2011-12-01 14:02:24 -03:00
2011-08-24 19:25:10 -03:00
ctx - > rotation = ctrl - > val ;
break ;
2011-12-01 14:02:24 -03:00
case V4L2_CID_ALPHA_COMPONENT :
ctx - > d_frame . alpha = ctrl - > val ;
break ;
2011-08-24 19:25:10 -03:00
}
ctx - > state | = FIMC_PARAMS ;
set_bit ( ST_CAPT_APPLY_CFG , & fimc - > state ) ;
return 0 ;
}
2011-12-01 14:02:24 -03:00
static int fimc_s_ctrl ( struct v4l2_ctrl * ctrl )
{
struct fimc_ctx * ctx = ctrl_to_ctx ( ctrl ) ;
unsigned long flags ;
int ret ;
2012-03-19 13:11:40 -03:00
spin_lock_irqsave ( & ctx - > fimc_dev - > slock , flags ) ;
2011-12-01 14:02:24 -03:00
ret = __fimc_s_ctrl ( ctx , ctrl ) ;
2012-03-19 13:11:40 -03:00
spin_unlock_irqrestore ( & ctx - > fimc_dev - > slock , flags ) ;
2011-12-01 14:02:24 -03:00
return ret ;
}
2011-08-24 19:25:10 -03:00
static const struct v4l2_ctrl_ops fimc_ctrl_ops = {
. s_ctrl = fimc_s_ctrl ,
} ;
int fimc_ctrls_create ( struct fimc_ctx * ctx )
{
2011-12-01 14:02:24 -03:00
struct samsung_fimc_variant * variant = ctx - > fimc_dev - > variant ;
unsigned int max_alpha = fimc_get_alpha_mask ( ctx - > d_frame . fmt ) ;
2011-08-24 19:25:10 -03:00
if ( ctx - > ctrls_rdy )
return 0 ;
2011-12-01 14:02:24 -03:00
v4l2_ctrl_handler_init ( & ctx - > ctrl_handler , 4 ) ;
2011-08-24 19:25:10 -03:00
ctx - > ctrl_rotate = v4l2_ctrl_new_std ( & ctx - > ctrl_handler , & fimc_ctrl_ops ,
2012-01-10 05:46:57 -03:00
V4L2_CID_ROTATE , 0 , 270 , 90 , 0 ) ;
2011-08-24 19:25:10 -03:00
ctx - > ctrl_hflip = v4l2_ctrl_new_std ( & ctx - > ctrl_handler , & fimc_ctrl_ops ,
2012-01-10 05:46:57 -03:00
V4L2_CID_HFLIP , 0 , 1 , 1 , 0 ) ;
2011-08-24 19:25:10 -03:00
ctx - > ctrl_vflip = v4l2_ctrl_new_std ( & ctx - > ctrl_handler , & fimc_ctrl_ops ,
2012-01-10 05:46:57 -03:00
V4L2_CID_VFLIP , 0 , 1 , 1 , 0 ) ;
2011-12-01 14:02:24 -03:00
if ( variant - > has_alpha )
ctx - > ctrl_alpha = v4l2_ctrl_new_std ( & ctx - > ctrl_handler ,
& fimc_ctrl_ops , V4L2_CID_ALPHA_COMPONENT ,
0 , max_alpha , 1 , 0 ) ;
else
ctx - > ctrl_alpha = NULL ;
2011-08-24 19:25:10 -03:00
ctx - > ctrls_rdy = ctx - > ctrl_handler . error = = 0 ;
return ctx - > ctrl_handler . error ;
}
void fimc_ctrls_delete ( struct fimc_ctx * ctx )
{
if ( ctx - > ctrls_rdy ) {
v4l2_ctrl_handler_free ( & ctx - > ctrl_handler ) ;
ctx - > ctrls_rdy = false ;
2011-12-01 14:02:24 -03:00
ctx - > ctrl_alpha = NULL ;
2011-08-24 19:25:10 -03:00
}
}
void fimc_ctrls_activate ( struct fimc_ctx * ctx , bool active )
{
2011-12-01 14:02:24 -03:00
unsigned int has_alpha = ctx - > d_frame . fmt - > flags & FMT_HAS_ALPHA ;
2011-08-24 19:25:10 -03:00
if ( ! ctx - > ctrls_rdy )
return ;
mutex_lock ( & ctx - > ctrl_handler . lock ) ;
v4l2_ctrl_activate ( ctx - > ctrl_rotate , active ) ;
v4l2_ctrl_activate ( ctx - > ctrl_hflip , active ) ;
v4l2_ctrl_activate ( ctx - > ctrl_vflip , active ) ;
2011-12-01 14:02:24 -03:00
if ( ctx - > ctrl_alpha )
v4l2_ctrl_activate ( ctx - > ctrl_alpha , active & & has_alpha ) ;
2011-08-24 19:25:10 -03:00
if ( active ) {
ctx - > rotation = ctx - > ctrl_rotate - > val ;
ctx - > hflip = ctx - > ctrl_hflip - > val ;
ctx - > vflip = ctx - > ctrl_vflip - > val ;
} else {
ctx - > rotation = 0 ;
ctx - > hflip = 0 ;
ctx - > vflip = 0 ;
}
mutex_unlock ( & ctx - > ctrl_handler . lock ) ;
}
2011-12-01 14:02:24 -03:00
/* Update maximum value of the alpha color control */
void fimc_alpha_ctrl_update ( struct fimc_ctx * ctx )
{
struct fimc_dev * fimc = ctx - > fimc_dev ;
struct v4l2_ctrl * ctrl = ctx - > ctrl_alpha ;
if ( ctrl = = NULL | | ! fimc - > variant - > has_alpha )
return ;
v4l2_ctrl_lock ( ctrl ) ;
ctrl - > maximum = fimc_get_alpha_mask ( ctx - > d_frame . fmt ) ;
if ( ctrl - > cur . val > ctrl - > maximum )
ctrl - > cur . val = ctrl - > maximum ;
v4l2_ctrl_unlock ( ctrl ) ;
}
2011-06-10 15:36:50 -03:00
int fimc_fill_format ( struct fimc_frame * frame , struct v4l2_format * f )
2010-08-03 09:50:29 -03:00
{
2011-06-10 15:36:50 -03:00
struct v4l2_pix_format_mplane * pixm = & f - > fmt . pix_mp ;
2011-01-15 01:17:42 -03:00
int i ;
2010-08-03 09:50:29 -03:00
2011-06-10 15:36:50 -03:00
pixm - > width = frame - > o_width ;
pixm - > height = frame - > o_height ;
pixm - > field = V4L2_FIELD_NONE ;
pixm - > pixelformat = frame - > fmt - > fourcc ;
pixm - > colorspace = V4L2_COLORSPACE_JPEG ;
pixm - > num_planes = frame - > fmt - > memplanes ;
2011-01-15 01:17:42 -03:00
for ( i = 0 ; i < pixm - > num_planes ; + + i ) {
2011-06-10 15:36:50 -03:00
int bpl = frame - > f_width ;
2011-01-15 01:17:42 -03:00
if ( frame - > fmt - > colplanes = = 1 ) /* packed formats */
bpl = ( bpl * frame - > fmt - > depth [ 0 ] ) / 8 ;
pixm - > plane_fmt [ i ] . bytesperline = bpl ;
pixm - > plane_fmt [ i ] . sizeimage = ( frame - > o_width *
frame - > o_height * frame - > fmt - > depth [ i ] ) / 8 ;
}
2010-08-03 09:50:29 -03:00
return 0 ;
}
2011-08-26 14:51:00 -03:00
void fimc_fill_frame ( struct fimc_frame * frame , struct v4l2_format * f )
{
struct v4l2_pix_format_mplane * pixm = & f - > fmt . pix_mp ;
frame - > f_width = pixm - > plane_fmt [ 0 ] . bytesperline ;
if ( frame - > fmt - > colplanes = = 1 )
frame - > f_width = ( frame - > f_width * 8 ) / frame - > fmt - > depth [ 0 ] ;
frame - > f_height = pixm - > height ;
frame - > width = pixm - > width ;
frame - > height = pixm - > height ;
frame - > o_width = pixm - > width ;
frame - > o_height = pixm - > height ;
frame - > offs_h = 0 ;
frame - > offs_v = 0 ;
}
/**
* fimc_adjust_mplane_format - adjust bytesperline / sizeimage for each plane
* @ fmt : fimc pixel format description ( input )
* @ width : requested pixel width
* @ height : requested pixel height
* @ pix : multi - plane format to adjust
*/
void fimc_adjust_mplane_format ( struct fimc_fmt * fmt , u32 width , u32 height ,
struct v4l2_pix_format_mplane * pix )
{
u32 bytesperline = 0 ;
int i ;
pix - > colorspace = V4L2_COLORSPACE_JPEG ;
pix - > field = V4L2_FIELD_NONE ;
pix - > num_planes = fmt - > memplanes ;
2011-12-01 14:02:24 -03:00
pix - > pixelformat = fmt - > fourcc ;
2011-08-26 14:51:00 -03:00
pix - > height = height ;
pix - > width = width ;
for ( i = 0 ; i < pix - > num_planes ; + + i ) {
u32 bpl = pix - > plane_fmt [ i ] . bytesperline ;
u32 * sizeimage = & pix - > plane_fmt [ i ] . sizeimage ;
if ( fmt - > colplanes > 1 & & ( bpl = = 0 | | bpl < pix - > width ) )
bpl = pix - > width ; /* Planar */
if ( fmt - > colplanes = = 1 & & /* Packed */
( bpl = = 0 | | ( ( bpl * 8 ) / fmt - > depth [ i ] ) < pix - > width ) )
bpl = ( pix - > width * fmt - > depth [ 0 ] ) / 8 ;
if ( i = = 0 ) /* Same bytesperline for each plane. */
bytesperline = bpl ;
pix - > plane_fmt [ i ] . bytesperline = bytesperline ;
* sizeimage = ( pix - > width * pix - > height * fmt - > depth [ i ] ) / 8 ;
}
}
2011-06-13 11:09:40 -03:00
/**
* fimc_find_format - lookup fimc color format by fourcc or media bus format
* @ pixelformat : fourcc to match , ignored if null
* @ mbus_code : media bus code to match , ignored if null
* @ mask : the color flags to match
* @ index : offset in the fimc_formats array , ignored if negative
*/
2012-04-22 17:07:09 -03:00
struct fimc_fmt * fimc_find_format ( const u32 * pixelformat , const u32 * mbus_code ,
2011-06-13 11:09:40 -03:00
unsigned int mask , int index )
2010-08-03 09:50:29 -03:00
{
2011-06-13 11:09:40 -03:00
struct fimc_fmt * fmt , * def_fmt = NULL ;
2010-08-03 09:50:29 -03:00
unsigned int i ;
2011-06-13 11:09:40 -03:00
int id = 0 ;
2010-08-03 09:50:29 -03:00
2012-04-22 17:07:09 -03:00
if ( index > = ( int ) ARRAY_SIZE ( fimc_formats ) )
2011-06-13 11:09:40 -03:00
return NULL ;
2010-10-07 10:06:16 -03:00
for ( i = 0 ; i < ARRAY_SIZE ( fimc_formats ) ; + + i ) {
fmt = & fimc_formats [ i ] ;
2011-06-13 11:09:40 -03:00
if ( ! ( fmt - > flags & mask ) )
continue ;
if ( pixelformat & & fmt - > fourcc = = * pixelformat )
return fmt ;
if ( mbus_code & & fmt - > mbus_code = = * mbus_code )
return fmt ;
if ( index = = id )
def_fmt = fmt ;
id + + ;
2010-10-07 10:06:16 -03:00
}
2011-06-13 11:09:40 -03:00
return def_fmt ;
2010-10-07 10:06:16 -03:00
}
2011-09-02 06:25:32 -03:00
static void fimc_clk_put ( struct fimc_dev * fimc )
2010-08-03 09:50:29 -03:00
{
int i ;
2012-03-17 18:31:33 -03:00
for ( i = 0 ; i < MAX_FIMC_CLOCKS ; i + + ) {
2012-01-30 11:39:30 -03:00
if ( IS_ERR_OR_NULL ( fimc - > clock [ i ] ) )
continue ;
clk_unprepare ( fimc - > clock [ i ] ) ;
clk_put ( fimc - > clock [ i ] ) ;
fimc - > clock [ i ] = NULL ;
2010-08-03 09:50:29 -03:00
}
}
static int fimc_clk_get ( struct fimc_dev * fimc )
{
2012-01-30 11:39:30 -03:00
int i , ret ;
2012-03-17 18:31:33 -03:00
for ( i = 0 ; i < MAX_FIMC_CLOCKS ; i + + ) {
2010-12-27 15:34:43 -03:00
fimc - > clock [ i ] = clk_get ( & fimc - > pdev - > dev , fimc_clocks [ i ] ) ;
2012-01-30 11:39:30 -03:00
if ( IS_ERR ( fimc - > clock [ i ] ) )
goto err ;
ret = clk_prepare ( fimc - > clock [ i ] ) ;
if ( ret < 0 ) {
clk_put ( fimc - > clock [ i ] ) ;
fimc - > clock [ i ] = NULL ;
goto err ;
}
2010-08-03 09:50:29 -03:00
}
2011-09-02 06:25:32 -03:00
return 0 ;
2012-01-30 11:39:30 -03:00
err :
fimc_clk_put ( fimc ) ;
dev_err ( & fimc - > pdev - > dev , " failed to get clock: %s \n " ,
fimc_clocks [ i ] ) ;
return - ENXIO ;
2011-09-02 06:25:32 -03:00
}
static int fimc_m2m_suspend ( struct fimc_dev * fimc )
{
unsigned long flags ;
int timeout ;
spin_lock_irqsave ( & fimc - > slock , flags ) ;
if ( ! fimc_m2m_pending ( fimc ) ) {
spin_unlock_irqrestore ( & fimc - > slock , flags ) ;
return 0 ;
}
clear_bit ( ST_M2M_SUSPENDED , & fimc - > state ) ;
set_bit ( ST_M2M_SUSPENDING , & fimc - > state ) ;
spin_unlock_irqrestore ( & fimc - > slock , flags ) ;
timeout = wait_event_timeout ( fimc - > irq_queue ,
test_bit ( ST_M2M_SUSPENDED , & fimc - > state ) ,
FIMC_SHUTDOWN_TIMEOUT ) ;
clear_bit ( ST_M2M_SUSPENDING , & fimc - > state ) ;
return timeout = = 0 ? - EAGAIN : 0 ;
}
static int fimc_m2m_resume ( struct fimc_dev * fimc )
{
unsigned long flags ;
spin_lock_irqsave ( & fimc - > slock , flags ) ;
/* Clear for full H/W setup in first run after resume */
fimc - > m2m . ctx = NULL ;
spin_unlock_irqrestore ( & fimc - > slock , flags ) ;
if ( test_and_clear_bit ( ST_M2M_SUSPENDED , & fimc - > state ) )
fimc_m2m_job_finish ( fimc - > m2m . ctx ,
VB2_BUF_STATE_ERROR ) ;
2010-08-03 09:50:29 -03:00
return 0 ;
}
static int fimc_probe ( struct platform_device * pdev )
{
struct fimc_dev * fimc ;
struct resource * res ;
struct samsung_fimc_driverdata * drv_data ;
2011-02-28 11:12:19 -03:00
struct s5p_platform_fimc * pdata ;
2010-08-03 09:50:29 -03:00
int ret = 0 ;
drv_data = ( struct samsung_fimc_driverdata * )
platform_get_device_id ( pdev ) - > driver_data ;
2010-10-11 13:19:27 -03:00
if ( pdev - > id > = drv_data - > num_entities ) {
2010-08-03 09:50:29 -03:00
dev_err ( & pdev - > dev , " Invalid platform device id: %d \n " ,
pdev - > id ) ;
return - EINVAL ;
}
2012-01-30 11:37:59 -03:00
fimc = devm_kzalloc ( & pdev - > dev , sizeof ( * fimc ) , GFP_KERNEL ) ;
2010-08-03 09:50:29 -03:00
if ( ! fimc )
return - ENOMEM ;
fimc - > id = pdev - > id ;
2011-09-02 06:25:32 -03:00
2010-08-03 09:50:29 -03:00
fimc - > variant = drv_data - > variant [ fimc - > id ] ;
fimc - > pdev = pdev ;
2011-02-28 11:12:19 -03:00
pdata = pdev - > dev . platform_data ;
fimc - > pdata = pdata ;
2011-09-02 06:25:32 -03:00
2010-10-07 10:06:16 -03:00
init_waitqueue_head ( & fimc - > irq_queue ) ;
2010-08-03 09:50:29 -03:00
spin_lock_init ( & fimc - > slock ) ;
mutex_init ( & fimc - > lock ) ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2012-01-30 11:37:59 -03:00
fimc - > regs = devm_request_and_ioremap ( & pdev - > dev , res ) ;
if ( fimc - > regs = = NULL ) {
dev_err ( & pdev - > dev , " Failed to obtain io memory \n " ) ;
return - ENOENT ;
2010-08-03 09:50:29 -03:00
}
res = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
2012-01-30 11:37:59 -03:00
if ( res = = NULL ) {
dev_err ( & pdev - > dev , " Failed to get IRQ resource \n " ) ;
return - ENXIO ;
2010-08-03 09:50:29 -03:00
}
2011-09-02 06:25:32 -03:00
ret = fimc_clk_get ( fimc ) ;
if ( ret )
2012-01-30 11:37:59 -03:00
return ret ;
2011-09-02 06:25:32 -03:00
clk_set_rate ( fimc - > clock [ CLK_BUS ] , drv_data - > lclk_frequency ) ;
clk_enable ( fimc - > clock [ CLK_BUS ] ) ;
2012-03-17 18:31:33 -03:00
ret = devm_request_irq ( & pdev - > dev , res - > start , fimc_irq_handler ,
2012-01-30 11:37:59 -03:00
0 , pdev - > name , fimc ) ;
2010-08-03 09:50:29 -03:00
if ( ret ) {
dev_err ( & pdev - > dev , " failed to install irq (%d) \n " , ret ) ;
goto err_clk ;
}
2012-04-20 18:57:25 -03:00
ret = fimc_initialize_capture_subdev ( fimc ) ;
if ( ret )
goto err_clk ;
platform_set_drvdata ( pdev , fimc ) ;
2011-09-02 06:25:32 -03:00
pm_runtime_enable ( & pdev - > dev ) ;
ret = pm_runtime_get_sync ( & pdev - > dev ) ;
if ( ret < 0 )
2012-04-20 18:57:25 -03:00
goto err_sd ;
2010-12-01 10:14:59 -03:00
/* Initialize contiguous memory allocator */
2011-09-02 06:25:32 -03:00
fimc - > alloc_ctx = vb2_dma_contig_init_ctx ( & pdev - > dev ) ;
2010-12-01 10:14:59 -03:00
if ( IS_ERR ( fimc - > alloc_ctx ) ) {
ret = PTR_ERR ( fimc - > alloc_ctx ) ;
2011-09-02 06:25:32 -03:00
goto err_pm ;
2010-12-01 10:14:59 -03:00
}
2011-08-26 15:40:36 -03:00
dev_dbg ( & pdev - > dev , " FIMC.%d registered successfully \n " , fimc - > id ) ;
2010-08-03 09:50:29 -03:00
2011-09-02 06:25:32 -03:00
pm_runtime_put ( & pdev - > dev ) ;
2010-08-03 09:50:29 -03:00
return 0 ;
2011-09-02 06:25:32 -03:00
err_pm :
pm_runtime_put ( & pdev - > dev ) ;
2012-04-20 18:57:25 -03:00
err_sd :
fimc_unregister_capture_subdev ( fimc ) ;
2010-08-03 09:50:29 -03:00
err_clk :
2011-09-02 06:25:32 -03:00
fimc_clk_put ( fimc ) ;
2010-08-03 09:50:29 -03:00
return ret ;
}
2011-09-02 06:25:32 -03:00
static int fimc_runtime_resume ( struct device * dev )
2010-08-03 09:50:29 -03:00
{
2011-09-02 06:25:32 -03:00
struct fimc_dev * fimc = dev_get_drvdata ( dev ) ;
2010-08-03 09:50:29 -03:00
2011-09-02 06:25:32 -03:00
dbg ( " fimc%d: state: 0x%lx " , fimc - > id , fimc - > state ) ;
/* Enable clocks and perform basic initalization */
clk_enable ( fimc - > clock [ CLK_GATE ] ) ;
2010-08-03 09:50:29 -03:00
fimc_hw_reset ( fimc ) ;
2011-09-02 06:25:32 -03:00
/* Resume the capture or mem-to-mem device */
if ( fimc_capture_busy ( fimc ) )
return fimc_capture_resume ( fimc ) ;
2011-11-17 06:23:21 -03:00
return fimc_m2m_resume ( fimc ) ;
2011-09-02 06:25:32 -03:00
}
static int fimc_runtime_suspend ( struct device * dev )
{
struct fimc_dev * fimc = dev_get_drvdata ( dev ) ;
int ret = 0 ;
if ( fimc_capture_busy ( fimc ) )
ret = fimc_capture_suspend ( fimc ) ;
else
ret = fimc_m2m_suspend ( fimc ) ;
if ( ! ret )
clk_disable ( fimc - > clock [ CLK_GATE ] ) ;
dbg ( " fimc%d: state: 0x%lx " , fimc - > id , fimc - > state ) ;
return ret ;
}
# ifdef CONFIG_PM_SLEEP
static int fimc_resume ( struct device * dev )
{
struct fimc_dev * fimc = dev_get_drvdata ( dev ) ;
unsigned long flags ;
dbg ( " fimc%d: state: 0x%lx " , fimc - > id , fimc - > state ) ;
/* Do not resume if the device was idle before system suspend */
spin_lock_irqsave ( & fimc - > slock , flags ) ;
if ( ! test_and_clear_bit ( ST_LPM , & fimc - > state ) | |
( ! fimc_m2m_active ( fimc ) & & ! fimc_capture_busy ( fimc ) ) ) {
spin_unlock_irqrestore ( & fimc - > slock , flags ) ;
return 0 ;
}
fimc_hw_reset ( fimc ) ;
spin_unlock_irqrestore ( & fimc - > slock , flags ) ;
if ( fimc_capture_busy ( fimc ) )
return fimc_capture_resume ( fimc ) ;
return fimc_m2m_resume ( fimc ) ;
}
static int fimc_suspend ( struct device * dev )
{
struct fimc_dev * fimc = dev_get_drvdata ( dev ) ;
dbg ( " fimc%d: state: 0x%lx " , fimc - > id , fimc - > state ) ;
if ( test_and_set_bit ( ST_LPM , & fimc - > state ) )
return 0 ;
if ( fimc_capture_busy ( fimc ) )
return fimc_capture_suspend ( fimc ) ;
return fimc_m2m_suspend ( fimc ) ;
}
# endif /* CONFIG_PM_SLEEP */
static int __devexit fimc_remove ( struct platform_device * pdev )
{
struct fimc_dev * fimc = platform_get_drvdata ( pdev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
pm_runtime_set_suspended ( & pdev - > dev ) ;
2010-08-03 09:50:29 -03:00
2012-04-20 18:57:25 -03:00
fimc_unregister_capture_subdev ( fimc ) ;
2010-12-01 10:14:59 -03:00
vb2_dma_contig_cleanup_ctx ( fimc - > alloc_ctx ) ;
2011-09-02 06:25:32 -03:00
clk_disable ( fimc - > clock [ CLK_BUS ] ) ;
fimc_clk_put ( fimc ) ;
2010-10-08 05:01:14 -03:00
2011-09-02 06:25:32 -03:00
dev_info ( & pdev - > dev , " driver unloaded \n " ) ;
2010-08-03 09:50:29 -03:00
return 0 ;
}
2010-10-11 13:19:27 -03:00
/* Image pixel limits, similar across several FIMC HW revisions. */
2011-04-08 09:08:52 -03:00
static struct fimc_pix_limit s5p_pix_limit [ 4 ] = {
2010-10-11 13:19:27 -03:00
[ 0 ] = {
. scaler_en_w = 3264 ,
. scaler_dis_w = 8192 ,
. in_rot_en_h = 1920 ,
. in_rot_dis_w = 8192 ,
. out_rot_en_w = 1920 ,
. out_rot_dis_w = 4224 ,
} ,
[ 1 ] = {
. scaler_en_w = 4224 ,
. scaler_dis_w = 8192 ,
. in_rot_en_h = 1920 ,
. in_rot_dis_w = 8192 ,
. out_rot_en_w = 1920 ,
. out_rot_dis_w = 4224 ,
} ,
[ 2 ] = {
. scaler_en_w = 1920 ,
. scaler_dis_w = 8192 ,
. in_rot_en_h = 1280 ,
. in_rot_dis_w = 8192 ,
. out_rot_en_w = 1280 ,
. out_rot_dis_w = 1920 ,
} ,
2011-04-08 09:08:52 -03:00
[ 3 ] = {
. scaler_en_w = 1920 ,
. scaler_dis_w = 8192 ,
. in_rot_en_h = 1366 ,
. in_rot_dis_w = 8192 ,
. out_rot_en_w = 1366 ,
. out_rot_dis_w = 1920 ,
} ,
2010-10-11 13:19:27 -03:00
} ;
static struct samsung_fimc_variant fimc0_variant_s5p = {
. has_inp_rot = 1 ,
. has_out_rot = 1 ,
2011-09-01 06:01:08 -03:00
. has_cam_if = 1 ,
2010-08-03 09:50:29 -03:00
. min_inp_pixsize = 16 ,
. min_out_pixsize = 16 ,
2010-10-11 13:19:27 -03:00
. hor_offs_align = 8 ,
2011-05-27 13:12:23 -03:00
. min_vsize_align = 16 ,
2010-10-11 13:19:27 -03:00
. out_buf_count = 4 ,
. pix_limit = & s5p_pix_limit [ 0 ] ,
2010-08-03 09:50:29 -03:00
} ;
static struct samsung_fimc_variant fimc2_variant_s5p = {
2011-09-01 06:01:08 -03:00
. has_cam_if = 1 ,
2010-08-03 09:50:29 -03:00
. min_inp_pixsize = 16 ,
. min_out_pixsize = 16 ,
2010-10-11 13:19:27 -03:00
. hor_offs_align = 8 ,
2011-05-27 13:12:23 -03:00
. min_vsize_align = 16 ,
2010-10-11 13:19:27 -03:00
. out_buf_count = 4 ,
. pix_limit = & s5p_pix_limit [ 1 ] ,
2010-08-03 09:50:29 -03:00
} ;
2010-10-11 13:19:27 -03:00
static struct samsung_fimc_variant fimc0_variant_s5pv210 = {
. pix_hoff = 1 ,
. has_inp_rot = 1 ,
. has_out_rot = 1 ,
2011-09-01 06:01:08 -03:00
. has_cam_if = 1 ,
2010-08-03 09:50:29 -03:00
. min_inp_pixsize = 16 ,
2010-10-08 05:01:14 -03:00
. min_out_pixsize = 16 ,
2010-10-11 13:19:27 -03:00
. hor_offs_align = 8 ,
2011-05-27 13:12:23 -03:00
. min_vsize_align = 16 ,
2010-10-11 13:19:27 -03:00
. out_buf_count = 4 ,
. pix_limit = & s5p_pix_limit [ 1 ] ,
} ;
2010-08-03 09:50:29 -03:00
2010-10-11 13:19:27 -03:00
static struct samsung_fimc_variant fimc1_variant_s5pv210 = {
. pix_hoff = 1 ,
. has_inp_rot = 1 ,
. has_out_rot = 1 ,
2011-09-01 06:01:08 -03:00
. has_cam_if = 1 ,
2010-12-28 11:27:13 -03:00
. has_mainscaler_ext = 1 ,
2010-10-11 13:19:27 -03:00
. min_inp_pixsize = 16 ,
. min_out_pixsize = 16 ,
. hor_offs_align = 1 ,
2011-05-27 13:12:23 -03:00
. min_vsize_align = 1 ,
2010-10-11 13:19:27 -03:00
. out_buf_count = 4 ,
. pix_limit = & s5p_pix_limit [ 2 ] ,
2010-08-03 09:50:29 -03:00
} ;
static struct samsung_fimc_variant fimc2_variant_s5pv210 = {
2011-09-01 06:01:08 -03:00
. has_cam_if = 1 ,
2010-09-06 03:53:44 -03:00
. pix_hoff = 1 ,
2010-08-03 09:50:29 -03:00
. min_inp_pixsize = 16 ,
2010-10-08 05:01:14 -03:00
. min_out_pixsize = 16 ,
2010-10-11 13:19:27 -03:00
. hor_offs_align = 8 ,
2011-05-27 13:12:23 -03:00
. min_vsize_align = 16 ,
2010-10-11 13:19:27 -03:00
. out_buf_count = 4 ,
. pix_limit = & s5p_pix_limit [ 2 ] ,
} ;
2010-08-03 09:50:29 -03:00
2011-04-08 09:08:52 -03:00
static struct samsung_fimc_variant fimc0_variant_exynos4 = {
2010-10-11 13:19:27 -03:00
. pix_hoff = 1 ,
. has_inp_rot = 1 ,
. has_out_rot = 1 ,
2011-09-01 06:01:08 -03:00
. has_cam_if = 1 ,
2010-11-25 10:49:21 -03:00
. has_cistatus2 = 1 ,
2010-12-28 11:27:13 -03:00
. has_mainscaler_ext = 1 ,
2011-12-01 14:02:24 -03:00
. has_alpha = 1 ,
2010-10-11 13:19:27 -03:00
. min_inp_pixsize = 16 ,
. min_out_pixsize = 16 ,
2011-06-07 11:19:33 -03:00
. hor_offs_align = 2 ,
2011-05-27 13:12:23 -03:00
. min_vsize_align = 1 ,
2010-10-11 13:19:27 -03:00
. out_buf_count = 32 ,
. pix_limit = & s5p_pix_limit [ 1 ] ,
} ;
2011-09-01 06:01:08 -03:00
static struct samsung_fimc_variant fimc3_variant_exynos4 = {
2010-10-11 13:19:27 -03:00
. pix_hoff = 1 ,
2011-09-01 06:01:08 -03:00
. has_cam_if = 1 ,
2010-11-25 10:49:21 -03:00
. has_cistatus2 = 1 ,
2010-12-28 11:27:13 -03:00
. has_mainscaler_ext = 1 ,
2011-12-01 14:02:24 -03:00
. has_alpha = 1 ,
2010-10-11 13:19:27 -03:00
. min_inp_pixsize = 16 ,
. min_out_pixsize = 16 ,
2011-06-07 11:19:33 -03:00
. hor_offs_align = 2 ,
2011-05-27 13:12:23 -03:00
. min_vsize_align = 1 ,
2010-10-11 13:19:27 -03:00
. out_buf_count = 32 ,
2011-04-08 09:08:52 -03:00
. pix_limit = & s5p_pix_limit [ 3 ] ,
2010-08-03 09:50:29 -03:00
} ;
2010-10-11 13:19:27 -03:00
/* S5PC100 */
2010-08-03 09:50:29 -03:00
static struct samsung_fimc_driverdata fimc_drvdata_s5p = {
. variant = {
2010-10-11 13:19:27 -03:00
[ 0 ] = & fimc0_variant_s5p ,
[ 1 ] = & fimc0_variant_s5p ,
2010-08-03 09:50:29 -03:00
[ 2 ] = & fimc2_variant_s5p ,
} ,
2010-10-11 13:19:27 -03:00
. num_entities = 3 ,
. lclk_frequency = 133000000UL ,
2010-08-03 09:50:29 -03:00
} ;
2010-10-11 13:19:27 -03:00
/* S5PV210, S5PC110 */
2010-08-03 09:50:29 -03:00
static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = {
. variant = {
2010-10-11 13:19:27 -03:00
[ 0 ] = & fimc0_variant_s5pv210 ,
[ 1 ] = & fimc1_variant_s5pv210 ,
2010-08-03 09:50:29 -03:00
[ 2 ] = & fimc2_variant_s5pv210 ,
} ,
2010-10-11 13:19:27 -03:00
. num_entities = 3 ,
. lclk_frequency = 166000000UL ,
} ;
/* S5PV310, S5PC210 */
2011-04-08 09:08:52 -03:00
static struct samsung_fimc_driverdata fimc_drvdata_exynos4 = {
2010-10-11 13:19:27 -03:00
. variant = {
2011-04-08 09:08:52 -03:00
[ 0 ] = & fimc0_variant_exynos4 ,
[ 1 ] = & fimc0_variant_exynos4 ,
[ 2 ] = & fimc0_variant_exynos4 ,
2011-09-01 06:01:08 -03:00
[ 3 ] = & fimc3_variant_exynos4 ,
2010-10-11 13:19:27 -03:00
} ,
. num_entities = 4 ,
. lclk_frequency = 166000000UL ,
2010-08-03 09:50:29 -03:00
} ;
static struct platform_device_id fimc_driver_ids [ ] = {
{
. name = " s5p-fimc " ,
. driver_data = ( unsigned long ) & fimc_drvdata_s5p ,
} , {
. name = " s5pv210-fimc " ,
. driver_data = ( unsigned long ) & fimc_drvdata_s5pv210 ,
2010-10-11 13:19:27 -03:00
} , {
2011-04-08 09:08:52 -03:00
. name = " exynos4-fimc " ,
. driver_data = ( unsigned long ) & fimc_drvdata_exynos4 ,
2010-08-03 09:50:29 -03:00
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( platform , fimc_driver_ids ) ;
2011-09-02 06:25:32 -03:00
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 )
} ;
2010-08-03 09:50:29 -03:00
static struct platform_driver fimc_driver = {
. probe = fimc_probe ,
2011-09-01 06:01:08 -03:00
. remove = __devexit_p ( fimc_remove ) ,
2010-08-03 09:50:29 -03:00
. id_table = fimc_driver_ids ,
. driver = {
2011-09-01 06:01:08 -03:00
. name = FIMC_MODULE_NAME ,
2010-08-03 09:50:29 -03:00
. owner = THIS_MODULE ,
2011-09-02 06:25:32 -03:00
. pm = & fimc_pm_ops ,
2010-08-03 09:50:29 -03:00
}
} ;
2011-09-01 06:01:08 -03:00
int __init fimc_register_driver ( void )
2010-08-03 09:50:29 -03:00
{
2012-03-21 09:58:09 -03:00
return platform_driver_register ( & fimc_driver ) ;
2010-08-03 09:50:29 -03:00
}
2011-09-01 06:01:08 -03:00
void __exit fimc_unregister_driver ( void )
2010-08-03 09:50:29 -03:00
{
platform_driver_unregister ( & fimc_driver ) ;
}