2010-08-03 09:50:29 -03:00
/*
* S5P camera interface ( video postprocessor ) driver
*
2010-10-07 10:06:16 -03:00
* Copyright ( c ) 2010 Samsung Electronics Co . , Ltd
2010-08-03 09:50:29 -03:00
*
* Sylwester Nawrocki , < s . nawrocki @ 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/module.h>
# include <linux/kernel.h>
# include <linux/version.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>
# 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"
2010-12-27 15:34:43 -03:00
static char * fimc_clocks [ MAX_FIMC_CLOCKS ] = {
" sclk_fimc " , " fimc " , " sclk_cam "
} ;
2010-08-03 09:50:29 -03:00
static struct fimc_fmt fimc_formats [ ] = {
{
2010-12-08 14:05:08 -03:00
. name = " RGB565 " ,
. fourcc = V4L2_PIX_FMT_RGB565X ,
. depth = { 16 } ,
. color = S5P_FIMC_RGB565 ,
. memplanes = 1 ,
. colplanes = 1 ,
. mbus_code = V4L2_MBUS_FMT_RGB565_2X8_BE ,
. 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
} , {
2010-12-08 14:05:08 -03:00
. name = " XRGB-8-8-8-8, 32 bpp " ,
. fourcc = V4L2_PIX_FMT_RGB32 ,
. depth = { 32 } ,
. color = S5P_FIMC_RGB888 ,
. 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 = " 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 ,
2010-10-07 10:06:16 -03:00
} ,
2010-10-08 05:01:14 -03:00
} ;
2010-08-03 09:50:29 -03:00
static struct v4l2_queryctrl fimc_ctrls [ ] = {
{
. id = V4L2_CID_HFLIP ,
. type = V4L2_CTRL_TYPE_BOOLEAN ,
. name = " Horizontal flip " ,
. minimum = 0 ,
. maximum = 1 ,
. default_value = 0 ,
2010-10-08 05:01:14 -03:00
} , {
2010-08-03 09:50:29 -03:00
. id = V4L2_CID_VFLIP ,
. type = V4L2_CTRL_TYPE_BOOLEAN ,
. name = " Vertical flip " ,
. minimum = 0 ,
. maximum = 1 ,
. default_value = 0 ,
2010-10-08 05:01:14 -03:00
} , {
2010-08-03 09:50:29 -03:00
. id = V4L2_CID_ROTATE ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " Rotation (CCW) " ,
. minimum = 0 ,
. maximum = 270 ,
. step = 90 ,
. default_value = 0 ,
} ,
} ;
static struct v4l2_queryctrl * get_ctrl ( int id )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( fimc_ctrls ) ; + + i )
if ( id = = fimc_ctrls [ i ] . id )
return & fimc_ctrls [ i ] ;
return NULL ;
}
2010-12-28 22:12:43 -03:00
int fimc_check_scaler_ratio ( int sw , int sh , int dw , int dh , int rot )
2010-08-03 09:50:29 -03:00
{
2010-12-28 22:12:43 -03:00
int tx , ty ;
2010-08-03 09:50:29 -03:00
2010-12-28 22:12:43 -03:00
if ( rot = = 90 | | rot = = 270 ) {
ty = dw ;
tx = dh ;
2010-08-03 09:50:29 -03:00
} else {
2010-12-28 22:12:43 -03:00
tx = dw ;
ty = dh ;
2010-08-03 09:50:29 -03:00
}
2010-12-28 22:12:43 -03:00
if ( ( sw > = SCALER_MAX_HRATIO * tx ) | | ( sh > = SCALER_MAX_VRATIO * ty ) )
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 ;
dbg ( " s: %d, t: %d, shift: %d, ratio: %d " ,
src , tar , * shift , * ratio ) ;
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
{
struct fimc_scaler * sc = & ctx - > scaler ;
struct fimc_frame * s_frame = & ctx - > s_frame ;
struct fimc_frame * d_frame = & ctx - > d_frame ;
2010-12-28 11:27:13 -03:00
struct samsung_fimc_variant * variant = ctx - > fimc_dev - > variant ;
2010-08-03 09:50:29 -03:00
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 ) {
v4l2_err ( & ctx - > fimc_dev - > m2m . v4l2_dev ,
" invalid target size: %d x %d " , tx , ty ) ;
return - EINVAL ;
}
sx = s_frame - > width ;
sy = s_frame - > height ;
if ( sx < = 0 | | sy < = 0 ) {
err ( " invalid source size: %d x %d " , sx , sy ) ;
return - EINVAL ;
}
sc - > real_width = sx ;
sc - > real_height = sy ;
dbg ( " sx= %d, sy= %d, tx= %d, ty= %d " , sx , sy , tx , ty ) ;
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 ;
}
2010-10-07 10:06:16 -03:00
static void fimc_capture_handler ( struct fimc_dev * fimc )
{
struct fimc_vid_cap * cap = & fimc - > vid_cap ;
struct fimc_vid_buffer * v_buf = NULL ;
if ( ! list_empty ( & cap - > active_buf_q ) ) {
v_buf = active_queue_pop ( cap ) ;
2010-12-01 10:14:59 -03:00
vb2_buffer_done ( & v_buf - > vb , VB2_BUF_STATE_DONE ) ;
2010-10-07 10:06:16 -03:00
}
if ( test_and_clear_bit ( ST_CAPT_SHUT , & fimc - > state ) ) {
wake_up ( & fimc - > irq_queue ) ;
return ;
}
if ( ! list_empty ( & cap - > pending_buf_q ) ) {
v_buf = pending_queue_pop ( cap ) ;
fimc_hw_set_output_addr ( fimc , & v_buf - > paddr , cap - > buf_index ) ;
v_buf - > index = cap - > buf_index ;
dbg ( " hw ptr: %d, sw ptr: %d " ,
fimc_hw_get_frame_index ( fimc ) , cap - > buf_index ) ;
/* Move the buffer to the capture active queue */
active_queue_add ( cap , v_buf ) ;
dbg ( " next frame: %d, done frame: %d " ,
fimc_hw_get_frame_index ( fimc ) , v_buf - > index ) ;
if ( + + cap - > buf_index > = FIMC_MAX_OUT_BUFS )
cap - > buf_index = 0 ;
} else if ( test_and_clear_bit ( ST_CAPT_STREAM , & fimc - > state ) & &
cap - > active_buf_cnt < = 1 ) {
fimc_deactivate_capture ( fimc ) ;
}
dbg ( " frame: %d, active_buf_cnt= %d " ,
fimc_hw_get_frame_index ( fimc ) , cap - > active_buf_cnt ) ;
}
2010-08-03 09:50:29 -03:00
static irqreturn_t fimc_isr ( int irq , void * priv )
{
2010-10-08 05:01:14 -03:00
struct fimc_dev * fimc = priv ;
2010-08-03 09:50:29 -03:00
BUG_ON ( ! fimc ) ;
fimc_hw_clear_irq ( fimc ) ;
spin_lock ( & fimc - > slock ) ;
if ( test_and_clear_bit ( ST_M2M_PEND , & fimc - > state ) ) {
2010-12-01 10:14:59 -03:00
struct vb2_buffer * src_vb , * dst_vb ;
struct fimc_ctx * ctx = v4l2_m2m_get_curr_priv ( fimc - > m2m . m2m_dev ) ;
2010-08-03 09:50:29 -03:00
if ( ! ctx | | ! ctx - > m2m_ctx )
goto isr_unlock ;
2010-12-01 10:14:59 -03:00
src_vb = v4l2_m2m_src_buf_remove ( ctx - > m2m_ctx ) ;
dst_vb = v4l2_m2m_dst_buf_remove ( ctx - > m2m_ctx ) ;
if ( src_vb & & dst_vb ) {
v4l2_m2m_buf_done ( src_vb , VB2_BUF_STATE_DONE ) ;
v4l2_m2m_buf_done ( dst_vb , VB2_BUF_STATE_DONE ) ;
2010-08-03 09:50:29 -03:00
v4l2_m2m_job_finish ( fimc - > m2m . m2m_dev , ctx - > m2m_ctx ) ;
}
2010-10-07 10:06:16 -03:00
goto isr_unlock ;
}
if ( test_bit ( ST_CAPT_RUN , & fimc - > state ) )
fimc_capture_handler ( fimc ) ;
if ( test_and_clear_bit ( ST_CAPT_PEND , & fimc - > state ) ) {
set_bit ( ST_CAPT_RUN , & fimc - > state ) ;
wake_up ( & fimc - > irq_queue ) ;
2010-08-03 09:50:29 -03:00
}
isr_unlock :
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 ) ;
paddr - > y = vb2_dma_contig_plane_paddr ( 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 )
paddr - > cb = vb2_dma_contig_plane_paddr ( vb , 1 ) ;
if ( frame - > fmt - > memplanes = = 3 )
paddr - > cr = vb2_dma_contig_plane_paddr ( 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. */
static void fimc_set_yuv_order ( struct fimc_ctx * ctx )
{
/* 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 ) ;
}
2010-09-06 03:53:44 -03:00
static void fimc_prepare_dma_offset ( struct fimc_ctx * ctx , struct fimc_frame * f )
{
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 ) ;
}
2010-08-03 09:50:29 -03:00
/**
* fimc_prepare_config - check dimensions , operation and color mode
* and pre - calculate offset and the scaling coefficients .
*
* @ ctx : hardware context information
* @ flags : flags indicating which parameters to check / update
*
* Return : 0 if dimensions are valid or non zero otherwise .
*/
2010-10-07 10:06:16 -03:00
int fimc_prepare_config ( struct fimc_ctx * ctx , u32 flags )
2010-08-03 09:50:29 -03:00
{
struct fimc_frame * s_frame , * d_frame ;
2010-12-01 10:14:59 -03:00
struct vb2_buffer * vb = NULL ;
2010-08-03 09:50:29 -03:00
int ret = 0 ;
s_frame = & ctx - > s_frame ;
d_frame = & ctx - > d_frame ;
if ( flags & FIMC_PARAMS ) {
2010-09-06 03:53:44 -03:00
/* Prepare the DMA offset ratios for scaler. */
fimc_prepare_dma_offset ( ctx , & ctx - > s_frame ) ;
fimc_prepare_dma_offset ( ctx , & ctx - > d_frame ) ;
2010-08-03 09:50:29 -03:00
if ( s_frame - > height > ( SCALER_MAX_VRATIO * d_frame - > height ) | |
s_frame - > width > ( SCALER_MAX_HRATIO * d_frame - > width ) ) {
err ( " out of scaler range " ) ;
return - EINVAL ;
}
2010-09-06 03:53:44 -03:00
fimc_set_yuv_order ( ctx ) ;
2010-08-03 09:50:29 -03:00
}
/* Input DMA mode is not allowed when the scaler is disabled. */
ctx - > scaler . enabled = 1 ;
if ( flags & FIMC_SRC_ADDR ) {
2010-12-01 10:14:59 -03:00
vb = v4l2_m2m_next_src_buf ( ctx - > m2m_ctx ) ;
ret = fimc_prepare_addr ( ctx , vb , s_frame , & s_frame - > paddr ) ;
2010-08-03 09:50:29 -03:00
if ( ret )
return ret ;
}
if ( flags & FIMC_DST_ADDR ) {
2010-12-01 10:14:59 -03:00
vb = v4l2_m2m_next_dst_buf ( ctx - > m2m_ctx ) ;
ret = fimc_prepare_addr ( ctx , vb , d_frame , & d_frame - > paddr ) ;
2010-08-03 09:50:29 -03:00
}
return ret ;
}
static void fimc_dma_run ( void * priv )
{
struct fimc_ctx * ctx = priv ;
struct fimc_dev * fimc ;
unsigned long flags ;
u32 ret ;
2010-10-30 18:08:31 -03:00
if ( WARN ( ! ctx , " null hardware context \n " ) )
2010-08-03 09:50:29 -03:00
return ;
fimc = ctx - > fimc_dev ;
spin_lock_irqsave ( & ctx - > slock , flags ) ;
set_bit ( ST_M2M_PEND , & fimc - > state ) ;
ctx - > state | = ( FIMC_SRC_ADDR | FIMC_DST_ADDR ) ;
ret = fimc_prepare_config ( ctx , ctx - > state ) ;
if ( ret ) {
2010-10-08 05:01:14 -03:00
err ( " Wrong parameters " ) ;
2010-08-03 09:50:29 -03:00
goto dma_unlock ;
}
2010-10-08 05:01:14 -03:00
/* Reconfigure hardware if the context has changed. */
if ( fimc - > m2m . ctx ! = ctx ) {
2010-08-03 09:50:29 -03:00
ctx - > state | = FIMC_PARAMS ;
2010-10-08 05:01:14 -03:00
fimc - > m2m . ctx = ctx ;
}
2010-08-03 09:50:29 -03:00
fimc_hw_set_input_addr ( fimc , & ctx - > s_frame . paddr ) ;
if ( ctx - > state & FIMC_PARAMS ) {
fimc_hw_set_input_path ( ctx ) ;
fimc_hw_set_in_dma ( ctx ) ;
if ( fimc_set_scaler_info ( ctx ) ) {
2010-10-08 05:01:14 -03:00
err ( " Scaler setup error " ) ;
2010-08-03 09:50:29 -03:00
goto dma_unlock ;
}
2010-12-28 11:27:13 -03:00
fimc_hw_set_prescaler ( ctx ) ;
2010-12-28 11:37:55 -03:00
fimc_hw_set_mainscaler ( ctx ) ;
2010-08-03 09:50:29 -03:00
fimc_hw_set_target_format ( ctx ) ;
fimc_hw_set_rotation ( ctx ) ;
fimc_hw_set_effect ( ctx ) ;
}
fimc_hw_set_output_path ( ctx ) ;
if ( ctx - > state & ( FIMC_DST_ADDR | FIMC_PARAMS ) )
2010-10-08 05:01:14 -03:00
fimc_hw_set_output_addr ( fimc , & ctx - > d_frame . paddr , - 1 ) ;
2010-08-03 09:50:29 -03:00
if ( ctx - > state & FIMC_PARAMS )
fimc_hw_set_out_dma ( ctx ) ;
2010-10-08 05:01:14 -03:00
fimc_activate_capture ( ctx ) ;
2010-08-03 09:50:29 -03:00
2010-12-01 10:14:59 -03:00
ctx - > state & = ( FIMC_CTX_M2M | FIMC_CTX_CAP |
FIMC_SRC_FMT | FIMC_DST_FMT ) ;
2010-10-08 05:01:14 -03:00
fimc_hw_activate_input_dma ( fimc , true ) ;
2010-08-03 09:50:29 -03:00
dma_unlock :
spin_unlock_irqrestore ( & ctx - > slock , flags ) ;
}
2010-08-06 10:50:46 -03:00
static void fimc_job_abort ( void * priv )
{
/* Nothing done in job_abort. */
}
2010-08-03 09:50:29 -03:00
2010-12-01 10:14:59 -03:00
static int fimc_queue_setup ( struct vb2_queue * vq , unsigned int * num_buffers ,
2010-12-08 14:05:08 -03:00
unsigned int * num_planes , unsigned long sizes [ ] ,
void * allocators [ ] )
2010-08-03 09:50:29 -03:00
{
2010-12-01 10:14:59 -03:00
struct fimc_ctx * ctx = vb2_get_drv_priv ( vq ) ;
2010-12-08 14:05:08 -03:00
struct fimc_frame * f ;
int i ;
f = ctx_get_frame ( ctx , vq - > type ) ;
if ( IS_ERR ( f ) )
return PTR_ERR ( f ) ;
2010-08-03 09:50:29 -03:00
2010-12-08 14:05:08 -03:00
/*
* Return number of non - contigous planes ( plane buffers )
* depending on the configured color format .
*/
if ( f - > fmt )
* num_planes = f - > fmt - > memplanes ;
2010-08-03 09:50:29 -03:00
2010-12-08 14:05:08 -03:00
for ( i = 0 ; i < f - > fmt - > memplanes ; i + + ) {
sizes [ i ] = ( f - > width * f - > height * f - > fmt - > depth [ i ] ) > > 3 ;
allocators [ i ] = ctx - > fimc_dev - > alloc_ctx ;
}
2010-12-01 10:14:59 -03:00
2010-12-08 14:05:08 -03:00
if ( * num_buffers = = 0 )
* num_buffers = 1 ;
2010-08-03 09:50:29 -03:00
return 0 ;
}
2010-12-01 10:14:59 -03:00
static int fimc_buf_prepare ( struct vb2_buffer * vb )
2010-08-03 09:50:29 -03:00
{
2010-12-01 10:14:59 -03:00
struct fimc_ctx * ctx = vb2_get_drv_priv ( vb - > vb2_queue ) ;
2010-08-03 09:50:29 -03:00
struct fimc_frame * frame ;
2010-12-08 14:05:08 -03:00
int i ;
2010-08-03 09:50:29 -03:00
2010-12-01 10:14:59 -03:00
frame = ctx_get_frame ( ctx , vb - > vb2_queue - > type ) ;
2010-08-06 10:50:46 -03:00
if ( IS_ERR ( frame ) )
return PTR_ERR ( frame ) ;
2010-08-03 09:50:29 -03:00
2010-12-08 14:05:08 -03:00
for ( i = 0 ; i < frame - > fmt - > memplanes ; i + + )
vb2_set_plane_payload ( vb , i , frame - > payload [ i ] ) ;
2010-08-03 09:50:29 -03:00
return 0 ;
}
2010-12-01 10:14:59 -03:00
static void fimc_buf_queue ( struct vb2_buffer * vb )
2010-08-03 09:50:29 -03:00
{
2010-12-01 10:14:59 -03:00
struct fimc_ctx * ctx = vb2_get_drv_priv ( vb - > vb2_queue ) ;
2010-10-07 10:06:16 -03:00
dbg ( " ctx: %p, ctx->state: 0x%x " , ctx , ctx - > state ) ;
2010-12-01 10:14:59 -03:00
if ( ctx - > m2m_ctx )
v4l2_m2m_buf_queue ( ctx - > m2m_ctx , vb ) ;
}
2010-10-07 10:06:16 -03:00
2010-12-01 10:14:59 -03:00
static void fimc_lock ( struct vb2_queue * vq )
{
struct fimc_ctx * ctx = vb2_get_drv_priv ( vq ) ;
mutex_lock ( & ctx - > fimc_dev - > lock ) ;
}
2010-10-07 10:06:16 -03:00
2010-12-01 10:14:59 -03:00
static void fimc_unlock ( struct vb2_queue * vq )
{
struct fimc_ctx * ctx = vb2_get_drv_priv ( vq ) ;
mutex_unlock ( & ctx - > fimc_dev - > lock ) ;
2010-08-03 09:50:29 -03:00
}
2010-12-01 10:14:59 -03:00
struct vb2_ops fimc_qops = {
. queue_setup = fimc_queue_setup ,
. buf_prepare = fimc_buf_prepare ,
. buf_queue = fimc_buf_queue ,
. wait_prepare = fimc_unlock ,
. wait_finish = fimc_lock ,
2010-08-03 09:50:29 -03:00
} ;
static int fimc_m2m_querycap ( struct file * file , void * priv ,
struct v4l2_capability * cap )
{
struct fimc_ctx * ctx = file - > private_data ;
struct fimc_dev * fimc = ctx - > fimc_dev ;
strncpy ( cap - > driver , fimc - > pdev - > name , sizeof ( cap - > driver ) - 1 ) ;
strncpy ( cap - > card , fimc - > pdev - > name , sizeof ( cap - > card ) - 1 ) ;
cap - > bus_info [ 0 ] = 0 ;
cap - > version = KERNEL_VERSION ( 1 , 0 , 0 ) ;
cap - > capabilities = V4L2_CAP_STREAMING |
2010-12-08 14:05:08 -03:00
V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE ;
2010-08-03 09:50:29 -03:00
return 0 ;
}
2010-12-08 14:05:08 -03:00
int fimc_vidioc_enum_fmt_mplane ( struct file * file , void * priv ,
2010-08-03 09:50:29 -03:00
struct v4l2_fmtdesc * f )
{
struct fimc_fmt * fmt ;
if ( f - > index > = ARRAY_SIZE ( fimc_formats ) )
return - EINVAL ;
fmt = & fimc_formats [ f - > index ] ;
strncpy ( f - > description , fmt - > name , sizeof ( f - > description ) - 1 ) ;
f - > pixelformat = fmt - > fourcc ;
2010-10-07 10:06:16 -03:00
2010-08-03 09:50:29 -03:00
return 0 ;
}
2010-12-08 14:05:08 -03:00
int fimc_vidioc_g_fmt_mplane ( struct file * file , void * priv ,
struct v4l2_format * f )
2010-08-03 09:50:29 -03:00
{
struct fimc_ctx * ctx = priv ;
struct fimc_frame * frame ;
2010-10-08 05:01:14 -03:00
frame = ctx_get_frame ( ctx , f - > type ) ;
2010-08-06 10:50:46 -03:00
if ( IS_ERR ( frame ) )
return PTR_ERR ( frame ) ;
2010-08-03 09:50:29 -03:00
f - > fmt . pix . width = frame - > width ;
f - > fmt . pix . height = frame - > height ;
f - > fmt . pix . field = V4L2_FIELD_NONE ;
f - > fmt . pix . pixelformat = frame - > fmt - > fourcc ;
return 0 ;
}
2010-10-07 10:06:16 -03:00
struct fimc_fmt * find_format ( struct v4l2_format * f , unsigned int mask )
2010-08-03 09:50:29 -03:00
{
struct fimc_fmt * fmt ;
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( fimc_formats ) ; + + i ) {
fmt = & fimc_formats [ i ] ;
2010-10-07 10:06:16 -03:00
if ( fmt - > fourcc = = f - > fmt . pix . pixelformat & &
( fmt - > flags & mask ) )
2010-08-03 09:50:29 -03:00
break ;
}
2010-10-07 10:06:16 -03:00
return ( i = = ARRAY_SIZE ( fimc_formats ) ) ? NULL : fmt ;
2010-08-03 09:50:29 -03:00
}
2010-10-07 10:06:16 -03:00
struct fimc_fmt * find_mbus_format ( struct v4l2_mbus_framefmt * f ,
unsigned int mask )
2010-08-03 09:50:29 -03:00
{
struct fimc_fmt * fmt ;
2010-10-07 10:06:16 -03:00
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( fimc_formats ) ; + + i ) {
fmt = & fimc_formats [ i ] ;
if ( fmt - > mbus_code = = f - > code & & ( fmt - > flags & mask ) )
break ;
}
return ( i = = ARRAY_SIZE ( fimc_formats ) ) ? NULL : fmt ;
}
2010-12-08 14:05:08 -03:00
int fimc_vidioc_try_fmt_mplane ( struct file * file , void * priv ,
struct v4l2_format * f )
2010-10-07 10:06:16 -03:00
{
2010-08-03 09:50:29 -03:00
struct fimc_ctx * ctx = priv ;
struct fimc_dev * fimc = ctx - > fimc_dev ;
struct samsung_fimc_variant * variant = fimc - > variant ;
2010-12-08 14:05:08 -03:00
struct v4l2_pix_format_mplane * pix = & f - > fmt . pix_mp ;
2010-10-07 10:06:16 -03:00
struct fimc_fmt * fmt ;
u32 max_width , mod_x , mod_y , mask ;
2010-12-01 10:25:18 -03:00
int i , is_output = 0 ;
2010-08-03 09:50:29 -03:00
2010-12-08 14:05:08 -03:00
if ( f - > type = = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) {
2010-10-07 10:06:16 -03:00
if ( ctx - > state & FIMC_CTX_CAP )
return - EINVAL ;
is_output = 1 ;
2010-12-08 14:05:08 -03:00
} else if ( f - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ) {
2010-08-03 09:50:29 -03:00
return - EINVAL ;
}
2010-12-08 14:05:08 -03:00
dbg ( " w: %d, h: %d " , pix - > width , pix - > height ) ;
2010-10-07 10:06:16 -03:00
mask = is_output ? FMT_FLAGS_M2M : FMT_FLAGS_M2M | FMT_FLAGS_CAM ;
fmt = find_format ( f , mask ) ;
if ( ! fmt ) {
v4l2_err ( & fimc - > m2m . v4l2_dev , " Fourcc format (0x%X) invalid. \n " ,
pix - > pixelformat ) ;
2010-12-01 10:25:18 -03:00
return - EINVAL ;
2010-10-07 10:06:16 -03:00
}
2010-08-03 09:50:29 -03:00
if ( pix - > field = = V4L2_FIELD_ANY )
pix - > field = V4L2_FIELD_NONE ;
else if ( V4L2_FIELD_NONE ! = pix - > field )
2010-12-01 10:25:18 -03:00
return - EINVAL ;
2010-08-03 09:50:29 -03:00
2010-10-07 10:06:16 -03:00
if ( is_output ) {
2010-10-11 13:19:27 -03:00
max_width = variant - > pix_limit - > scaler_dis_w ;
2010-10-07 10:06:16 -03:00
mod_x = ffs ( variant - > min_inp_pixsize ) - 1 ;
2010-08-03 09:50:29 -03:00
} else {
2010-10-11 13:19:27 -03:00
max_width = variant - > pix_limit - > out_rot_dis_w ;
2010-10-07 10:06:16 -03:00
mod_x = ffs ( variant - > min_out_pixsize ) - 1 ;
2010-08-03 09:50:29 -03:00
}
if ( tiled_fmt ( fmt ) ) {
2010-10-07 10:06:16 -03:00
mod_x = 6 ; /* 64 x 32 pixels tile */
mod_y = 5 ;
} else {
2010-12-08 14:05:08 -03:00
if ( fimc - > id = = 1 & & variant - > pix_hoff )
2010-10-07 10:06:16 -03:00
mod_y = fimc_fmt_is_rgb ( fmt - > color ) ? 0 : 1 ;
else
mod_y = mod_x ;
2010-08-03 09:50:29 -03:00
}
2010-10-07 10:06:16 -03:00
dbg ( " mod_x: %d, mod_y: %d, max_w: %d " , mod_x , mod_y , max_width ) ;
2010-08-03 09:50:29 -03:00
2010-10-07 10:06:16 -03:00
v4l_bound_align_image ( & pix - > width , 16 , max_width , mod_x ,
2010-10-11 13:19:27 -03:00
& pix - > height , 8 , variant - > pix_limit - > scaler_dis_w , mod_y , 0 ) ;
2010-08-03 09:50:29 -03:00
2010-12-08 14:05:08 -03:00
pix - > num_planes = fmt - > memplanes ;
for ( i = 0 ; i < pix - > num_planes ; + + i ) {
int bpl = pix - > plane_fmt [ i ] . bytesperline ;
dbg ( " [%d] bpl: %d, depth: %d, w: %d, h: %d " ,
i , bpl , fmt - > depth [ i ] , pix - > width , pix - > height ) ;
if ( ! bpl | | ( bpl * 8 / fmt - > depth [ i ] ) > pix - > width )
bpl = ( pix - > width * fmt - > depth [ 0 ] ) > > 3 ;
2010-08-03 09:50:29 -03:00
2010-12-08 14:05:08 -03:00
if ( ! pix - > plane_fmt [ i ] . sizeimage )
pix - > plane_fmt [ i ] . sizeimage = pix - > height * bpl ;
2010-08-03 09:50:29 -03:00
2010-12-08 14:05:08 -03:00
pix - > plane_fmt [ i ] . bytesperline = bpl ;
dbg ( " [%d]: bpl: %d, sizeimage: %d " ,
i , pix - > plane_fmt [ i ] . bytesperline ,
pix - > plane_fmt [ i ] . sizeimage ) ;
}
2010-08-03 09:50:29 -03:00
2010-12-01 10:25:18 -03:00
return 0 ;
2010-10-07 10:06:16 -03:00
}
2010-08-03 09:50:29 -03:00
2010-12-08 14:05:08 -03:00
static int fimc_m2m_s_fmt_mplane ( struct file * file , void * priv ,
struct v4l2_format * f )
2010-08-03 09:50:29 -03:00
{
struct fimc_ctx * ctx = priv ;
2010-09-28 14:19:26 -03:00
struct fimc_dev * fimc = ctx - > fimc_dev ;
2010-12-01 10:14:59 -03:00
struct vb2_queue * vq ;
2010-08-03 09:50:29 -03:00
struct fimc_frame * frame ;
2010-12-08 14:05:08 -03:00
struct v4l2_pix_format_mplane * pix ;
2010-08-03 09:50:29 -03:00
unsigned long flags ;
2010-12-08 14:05:08 -03:00
int i , ret = 0 ;
2010-12-01 10:25:18 -03:00
u32 tmp ;
2010-08-03 09:50:29 -03:00
2010-12-08 14:05:08 -03:00
ret = fimc_vidioc_try_fmt_mplane ( file , priv , f ) ;
2010-08-03 09:50:29 -03:00
if ( ret )
return ret ;
2010-09-28 14:19:26 -03:00
vq = v4l2_m2m_get_vq ( ctx - > m2m_ctx , f - > type ) ;
2010-08-03 09:50:29 -03:00
2010-12-01 10:14:59 -03:00
if ( vb2_is_streaming ( vq ) ) {
2010-12-01 10:25:18 -03:00
v4l2_err ( & fimc - > m2m . v4l2_dev , " queue (%d) busy \n " , f - > type ) ;
return - EBUSY ;
2010-09-28 14:19:26 -03:00
}
2010-08-03 09:50:29 -03:00
2010-12-08 14:05:08 -03:00
if ( f - > type = = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) {
2010-08-03 09:50:29 -03:00
frame = & ctx - > s_frame ;
2010-12-08 14:05:08 -03:00
} else if ( f - > type = = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ) {
2010-08-03 09:50:29 -03:00
frame = & ctx - > d_frame ;
} else {
2010-12-08 14:05:08 -03:00
v4l2_err ( & fimc - > m2m . v4l2_dev ,
2010-08-03 09:50:29 -03:00
" Wrong buffer/video queue type (%d) \n " , f - > type ) ;
2010-12-01 10:25:18 -03:00
return - EINVAL ;
2010-08-03 09:50:29 -03:00
}
2010-12-08 14:05:08 -03:00
pix = & f - > fmt . pix_mp ;
2010-10-07 10:06:16 -03:00
frame - > fmt = find_format ( f , FMT_FLAGS_M2M ) ;
2010-12-01 10:25:18 -03:00
if ( ! frame - > fmt )
return - EINVAL ;
2010-08-03 09:50:29 -03:00
2010-12-08 14:05:08 -03:00
for ( i = 0 ; i < frame - > fmt - > colplanes ; i + + )
frame - > payload [ i ] = pix - > plane_fmt [ i ] . bytesperline * pix - > height ;
frame - > f_width = pix - > plane_fmt [ 0 ] . bytesperline * 8 /
frame - > fmt - > depth [ 0 ] ;
2010-09-28 14:19:26 -03:00
frame - > f_height = pix - > height ;
frame - > width = pix - > width ;
frame - > height = pix - > height ;
frame - > o_width = pix - > width ;
2010-08-03 09:50:29 -03:00
frame - > o_height = pix - > height ;
2010-09-28 14:19:26 -03:00
frame - > offs_h = 0 ;
frame - > offs_v = 0 ;
2010-08-03 09:50:29 -03:00
spin_lock_irqsave ( & ctx - > slock , flags ) ;
2010-12-01 10:25:18 -03:00
tmp = ( frame = = & ctx - > d_frame ) ? FIMC_DST_FMT : FIMC_SRC_FMT ;
ctx - > state | = FIMC_PARAMS | tmp ;
2010-08-03 09:50:29 -03:00
spin_unlock_irqrestore ( & ctx - > slock , flags ) ;
2010-09-28 14:19:26 -03:00
dbg ( " f_w: %d, f_h: %d " , frame - > f_width , frame - > f_height ) ;
2010-08-03 09:50:29 -03:00
2010-12-01 10:25:18 -03:00
return 0 ;
2010-08-03 09:50:29 -03:00
}
static int fimc_m2m_reqbufs ( struct file * file , void * priv ,
struct v4l2_requestbuffers * reqbufs )
{
struct fimc_ctx * ctx = priv ;
return v4l2_m2m_reqbufs ( file , ctx - > m2m_ctx , reqbufs ) ;
}
static int fimc_m2m_querybuf ( struct file * file , void * priv ,
struct v4l2_buffer * buf )
{
struct fimc_ctx * ctx = priv ;
return v4l2_m2m_querybuf ( file , ctx - > m2m_ctx , buf ) ;
}
static int fimc_m2m_qbuf ( struct file * file , void * priv ,
struct v4l2_buffer * buf )
{
struct fimc_ctx * ctx = priv ;
return v4l2_m2m_qbuf ( file , ctx - > m2m_ctx , buf ) ;
}
static int fimc_m2m_dqbuf ( struct file * file , void * priv ,
struct v4l2_buffer * buf )
{
struct fimc_ctx * ctx = priv ;
return v4l2_m2m_dqbuf ( file , ctx - > m2m_ctx , buf ) ;
}
static int fimc_m2m_streamon ( struct file * file , void * priv ,
enum v4l2_buf_type type )
{
struct fimc_ctx * ctx = priv ;
2010-12-01 10:14:59 -03:00
/* The source and target color format need to be set */
2010-12-08 14:05:08 -03:00
if ( V4L2_TYPE_IS_OUTPUT ( type ) ) {
if ( ~ ctx - > state & FIMC_SRC_FMT )
return - EINVAL ;
} else if ( ~ ctx - > state & FIMC_DST_FMT ) {
2010-12-01 10:14:59 -03:00
return - EINVAL ;
2010-12-08 14:05:08 -03:00
}
2010-12-01 10:14:59 -03:00
2010-08-03 09:50:29 -03:00
return v4l2_m2m_streamon ( file , ctx - > m2m_ctx , type ) ;
}
static int fimc_m2m_streamoff ( struct file * file , void * priv ,
enum v4l2_buf_type type )
{
struct fimc_ctx * ctx = priv ;
return v4l2_m2m_streamoff ( file , ctx - > m2m_ctx , type ) ;
}
2010-10-07 10:06:16 -03:00
int fimc_vidioc_queryctrl ( struct file * file , void * priv ,
2010-08-03 09:50:29 -03:00
struct v4l2_queryctrl * qc )
{
2010-10-07 10:06:16 -03:00
struct fimc_ctx * ctx = priv ;
2010-08-03 09:50:29 -03:00
struct v4l2_queryctrl * c ;
2010-11-18 07:36:34 -03:00
int ret = - EINVAL ;
2010-10-07 10:06:16 -03:00
2010-08-03 09:50:29 -03:00
c = get_ctrl ( qc - > id ) ;
2010-10-07 10:06:16 -03:00
if ( c ) {
* qc = * c ;
return 0 ;
}
2010-11-18 07:36:34 -03:00
if ( ctx - > state & FIMC_CTX_CAP ) {
2010-12-01 10:25:18 -03:00
return v4l2_subdev_call ( ctx - > fimc_dev - > vid_cap . sd ,
2010-10-07 10:06:16 -03:00
core , queryctrl , qc ) ;
2010-11-18 07:36:34 -03:00
}
return ret ;
2010-08-03 09:50:29 -03:00
}
2010-10-07 10:06:16 -03:00
int fimc_vidioc_g_ctrl ( struct file * file , void * priv ,
2010-08-03 09:50:29 -03:00
struct v4l2_control * ctrl )
{
struct fimc_ctx * ctx = priv ;
2010-10-07 10:06:16 -03:00
struct fimc_dev * fimc = ctx - > fimc_dev ;
2010-08-03 09:50:29 -03:00
switch ( ctrl - > id ) {
case V4L2_CID_HFLIP :
ctrl - > value = ( FLIP_X_AXIS & ctx - > flip ) ? 1 : 0 ;
break ;
case V4L2_CID_VFLIP :
ctrl - > value = ( FLIP_Y_AXIS & ctx - > flip ) ? 1 : 0 ;
break ;
case V4L2_CID_ROTATE :
ctrl - > value = ctx - > rotation ;
break ;
default :
2010-10-07 10:06:16 -03:00
if ( ctx - > state & FIMC_CTX_CAP ) {
2010-12-01 10:25:18 -03:00
return v4l2_subdev_call ( fimc - > vid_cap . sd , core ,
g_ctrl , ctrl ) ;
2010-10-07 10:06:16 -03:00
} else {
v4l2_err ( & fimc - > m2m . v4l2_dev ,
" Invalid control \n " ) ;
2010-12-01 10:25:18 -03:00
return - EINVAL ;
2010-10-07 10:06:16 -03:00
}
2010-08-03 09:50:29 -03:00
}
dbg ( " ctrl->value= %d " , ctrl - > value ) ;
2010-10-07 10:06:16 -03:00
2010-12-01 10:25:18 -03:00
return 0 ;
2010-08-03 09:50:29 -03:00
}
2010-10-07 10:06:16 -03:00
int check_ctrl_val ( struct fimc_ctx * ctx , struct v4l2_control * ctrl )
2010-08-03 09:50:29 -03:00
{
struct v4l2_queryctrl * c ;
c = get_ctrl ( ctrl - > id ) ;
if ( ! c )
return - EINVAL ;
if ( ctrl - > value < c - > minimum | | ctrl - > value > c - > maximum
| | ( c - > step ! = 0 & & ctrl - > value % c - > step ! = 0 ) ) {
v4l2_err ( & ctx - > fimc_dev - > m2m . v4l2_dev ,
" Invalid control value \n " ) ;
return - ERANGE ;
}
return 0 ;
}
2010-10-07 10:06:16 -03:00
int fimc_s_ctrl ( struct fimc_ctx * ctx , struct v4l2_control * ctrl )
2010-08-03 09:50:29 -03:00
{
struct samsung_fimc_variant * variant = ctx - > fimc_dev - > variant ;
2010-10-07 10:06:16 -03:00
struct fimc_dev * fimc = ctx - > fimc_dev ;
2010-08-03 09:50:29 -03:00
unsigned long flags ;
2010-12-28 22:12:43 -03:00
int ret = 0 ;
2010-08-03 09:50:29 -03:00
2010-10-07 10:06:16 -03:00
spin_lock_irqsave ( & ctx - > slock , flags ) ;
2010-08-03 09:50:29 -03:00
switch ( ctrl - > id ) {
case V4L2_CID_HFLIP :
if ( ctrl - > value )
ctx - > flip | = FLIP_X_AXIS ;
else
ctx - > flip & = ~ FLIP_X_AXIS ;
break ;
case V4L2_CID_VFLIP :
if ( ctrl - > value )
ctx - > flip | = FLIP_Y_AXIS ;
else
ctx - > flip & = ~ FLIP_Y_AXIS ;
break ;
case V4L2_CID_ROTATE :
2010-12-28 22:12:43 -03:00
if ( ! ( ~ ctx - > state & ( FIMC_DST_FMT | FIMC_SRC_FMT ) ) ) {
ret = fimc_check_scaler_ratio ( ctx - > s_frame . width ,
ctx - > s_frame . height ,
ctx - > d_frame . width ,
ctx - > d_frame . height ,
ctrl - > value ) ;
if ( ret ) {
v4l2_err ( & fimc - > m2m . v4l2_dev ,
" Out of scaler range " ) ;
spin_unlock_irqrestore ( & ctx - > slock , flags ) ;
return - EINVAL ;
}
}
2010-10-07 10:06:16 -03:00
/* Check for the output rotator availability */
if ( ( ctrl - > value = = 90 | | ctrl - > value = = 270 ) & &
( ctx - > in_path = = FIMC_DMA & & ! variant - > has_out_rot ) ) {
spin_unlock_irqrestore ( & ctx - > slock , flags ) ;
return - EINVAL ;
} else {
ctx - > rotation = ctrl - > value ;
2010-08-03 09:50:29 -03:00
}
break ;
default :
2010-10-07 10:06:16 -03:00
spin_unlock_irqrestore ( & ctx - > slock , flags ) ;
v4l2_err ( & fimc - > m2m . v4l2_dev , " Invalid control \n " ) ;
2010-08-03 09:50:29 -03:00
return - EINVAL ;
}
ctx - > state | = FIMC_PARAMS ;
spin_unlock_irqrestore ( & ctx - > slock , flags ) ;
2010-10-07 10:06:16 -03:00
2010-08-03 09:50:29 -03:00
return 0 ;
}
2010-10-07 10:06:16 -03:00
static int fimc_m2m_s_ctrl ( struct file * file , void * priv ,
2010-12-27 14:47:32 -03:00
struct v4l2_control * ctrl )
2010-10-07 10:06:16 -03:00
{
struct fimc_ctx * ctx = priv ;
int ret = 0 ;
ret = check_ctrl_val ( ctx , ctrl ) ;
if ( ret )
return ret ;
ret = fimc_s_ctrl ( ctx , ctrl ) ;
return 0 ;
}
2010-08-03 09:50:29 -03:00
2010-11-25 11:01:51 -03:00
static int fimc_m2m_cropcap ( struct file * file , void * fh ,
2010-10-07 10:06:16 -03:00
struct v4l2_cropcap * cr )
2010-08-03 09:50:29 -03:00
{
struct fimc_frame * frame ;
struct fimc_ctx * ctx = fh ;
2010-10-08 05:01:14 -03:00
frame = ctx_get_frame ( ctx , cr - > type ) ;
2010-08-06 10:50:46 -03:00
if ( IS_ERR ( frame ) )
return PTR_ERR ( frame ) ;
2010-08-03 09:50:29 -03:00
2010-10-07 10:06:16 -03:00
cr - > bounds . left = 0 ;
cr - > bounds . top = 0 ;
cr - > bounds . width = frame - > f_width ;
cr - > bounds . height = frame - > f_height ;
cr - > defrect = cr - > bounds ;
2010-08-03 09:50:29 -03:00
return 0 ;
}
2010-11-25 11:01:51 -03:00
static int fimc_m2m_g_crop ( struct file * file , void * fh , struct v4l2_crop * cr )
2010-08-03 09:50:29 -03:00
{
struct fimc_frame * frame ;
struct fimc_ctx * ctx = file - > private_data ;
2010-10-08 05:01:14 -03:00
frame = ctx_get_frame ( ctx , cr - > type ) ;
2010-08-06 10:50:46 -03:00
if ( IS_ERR ( frame ) )
return PTR_ERR ( frame ) ;
2010-08-03 09:50:29 -03:00
cr - > c . left = frame - > offs_h ;
cr - > c . top = frame - > offs_v ;
cr - > c . width = frame - > width ;
cr - > c . height = frame - > height ;
return 0 ;
}
2010-10-07 10:06:16 -03:00
int fimc_try_crop ( struct fimc_ctx * ctx , struct v4l2_crop * cr )
2010-08-03 09:50:29 -03:00
{
struct fimc_dev * fimc = ctx - > fimc_dev ;
struct fimc_frame * f ;
2010-12-08 14:05:08 -03:00
u32 min_size , halign , depth = 0 ;
int i ;
2010-10-07 10:06:16 -03:00
2010-08-03 09:50:29 -03:00
if ( cr - > c . top < 0 | | cr - > c . left < 0 ) {
v4l2_err ( & fimc - > m2m . v4l2_dev ,
" doesn't support negative values for top & left \n " ) ;
return - EINVAL ;
}
2010-12-08 14:05:08 -03:00
if ( cr - > type = = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE )
2010-11-25 11:01:51 -03:00
f = ( ctx - > state & FIMC_CTX_CAP ) ? & ctx - > s_frame : & ctx - > d_frame ;
2010-12-08 14:05:08 -03:00
else if ( cr - > type = = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE & &
2010-11-25 11:01:51 -03:00
ctx - > state & FIMC_CTX_M2M )
f = & ctx - > s_frame ;
else
return - EINVAL ;
2010-08-03 09:50:29 -03:00
2010-11-25 11:01:51 -03:00
min_size = ( f = = & ctx - > s_frame ) ?
fimc - > variant - > min_inp_pixsize : fimc - > variant - > min_out_pixsize ;
2010-08-03 09:50:29 -03:00
2010-10-07 10:06:16 -03:00
if ( ctx - > state & FIMC_CTX_M2M ) {
if ( fimc - > id = = 1 & & fimc - > variant - > pix_hoff )
halign = fimc_fmt_is_rgb ( f - > fmt - > color ) ? 0 : 1 ;
else
halign = ffs ( min_size ) - 1 ;
/* there are more strict aligment requirements at camera interface */
} else {
min_size = 16 ;
halign = 4 ;
2010-08-03 09:50:29 -03:00
}
2010-12-08 14:05:08 -03:00
for ( i = 0 ; i < f - > fmt - > colplanes ; i + + )
depth + = f - > fmt - > depth [ i ] ;
2010-10-07 10:06:16 -03:00
v4l_bound_align_image ( & cr - > c . width , min_size , f - > o_width ,
ffs ( min_size ) - 1 ,
& cr - > c . height , min_size , f - > o_height ,
2010-12-08 14:05:08 -03:00
halign , 64 / ( ALIGN ( depth , 8 ) ) ) ;
2010-10-07 10:06:16 -03:00
/* adjust left/top if cropping rectangle is out of bounds */
if ( cr - > c . left + cr - > c . width > f - > o_width )
cr - > c . left = f - > o_width - cr - > c . width ;
if ( cr - > c . top + cr - > c . height > f - > o_height )
cr - > c . top = f - > o_height - cr - > c . height ;
cr - > c . left = round_down ( cr - > c . left , min_size ) ;
cr - > c . top = round_down ( cr - > c . top ,
ctx - > state & FIMC_CTX_M2M ? 8 : 16 ) ;
dbg ( " l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d " ,
cr - > c . left , cr - > c . top , cr - > c . width , cr - > c . height ,
f - > f_width , f - > f_height ) ;
return 0 ;
}
static int fimc_m2m_s_crop ( struct file * file , void * fh , struct v4l2_crop * cr )
{
struct fimc_ctx * ctx = file - > private_data ;
struct fimc_dev * fimc = ctx - > fimc_dev ;
unsigned long flags ;
struct fimc_frame * f ;
int ret ;
ret = fimc_try_crop ( ctx , cr ) ;
if ( ret )
return ret ;
2010-12-08 14:05:08 -03:00
f = ( cr - > type = = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) ?
2010-10-07 10:06:16 -03:00
& ctx - > s_frame : & ctx - > d_frame ;
2010-08-03 09:50:29 -03:00
spin_lock_irqsave ( & ctx - > slock , flags ) ;
2010-12-28 22:12:43 -03:00
/* Check to see if scaling ratio is within supported range */
if ( ! ( ~ ctx - > state & ( FIMC_DST_FMT | FIMC_SRC_FMT ) ) ) {
if ( cr - > type = = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) {
ret = fimc_check_scaler_ratio ( cr - > c . width , cr - > c . height ,
ctx - > d_frame . width ,
ctx - > d_frame . height ,
ctx - > rotation ) ;
} else {
ret = fimc_check_scaler_ratio ( ctx - > s_frame . width ,
ctx - > s_frame . height ,
cr - > c . width , cr - > c . height ,
ctx - > rotation ) ;
}
2010-08-03 09:50:29 -03:00
if ( ret ) {
2010-10-07 10:06:16 -03:00
v4l2_err ( & fimc - > m2m . v4l2_dev , " Out of scaler range " ) ;
2010-12-01 10:25:18 -03:00
spin_unlock_irqrestore ( & ctx - > slock , flags ) ;
return - EINVAL ;
2010-08-03 09:50:29 -03:00
}
}
2010-12-28 22:12:43 -03:00
2010-08-03 09:50:29 -03:00
ctx - > state | = FIMC_PARAMS ;
f - > offs_h = cr - > c . left ;
f - > offs_v = cr - > c . top ;
2010-10-07 10:06:16 -03:00
f - > width = cr - > c . width ;
2010-08-03 09:50:29 -03:00
f - > height = cr - > c . height ;
2010-10-07 10:06:16 -03:00
spin_unlock_irqrestore ( & ctx - > slock , flags ) ;
2010-08-03 09:50:29 -03:00
return 0 ;
}
static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
. vidioc_querycap = fimc_m2m_querycap ,
2010-12-08 14:05:08 -03:00
. vidioc_enum_fmt_vid_cap_mplane = fimc_vidioc_enum_fmt_mplane ,
. vidioc_enum_fmt_vid_out_mplane = fimc_vidioc_enum_fmt_mplane ,
2010-08-03 09:50:29 -03:00
2010-12-08 14:05:08 -03:00
. vidioc_g_fmt_vid_cap_mplane = fimc_vidioc_g_fmt_mplane ,
. vidioc_g_fmt_vid_out_mplane = fimc_vidioc_g_fmt_mplane ,
2010-08-03 09:50:29 -03:00
2010-12-08 14:05:08 -03:00
. vidioc_try_fmt_vid_cap_mplane = fimc_vidioc_try_fmt_mplane ,
. vidioc_try_fmt_vid_out_mplane = fimc_vidioc_try_fmt_mplane ,
2010-08-03 09:50:29 -03:00
2010-12-08 14:05:08 -03:00
. vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane ,
. vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane ,
2010-08-03 09:50:29 -03:00
. vidioc_reqbufs = fimc_m2m_reqbufs ,
. vidioc_querybuf = fimc_m2m_querybuf ,
. vidioc_qbuf = fimc_m2m_qbuf ,
. vidioc_dqbuf = fimc_m2m_dqbuf ,
. vidioc_streamon = fimc_m2m_streamon ,
. vidioc_streamoff = fimc_m2m_streamoff ,
2010-10-07 10:06:16 -03:00
. vidioc_queryctrl = fimc_vidioc_queryctrl ,
. vidioc_g_ctrl = fimc_vidioc_g_ctrl ,
2010-08-03 09:50:29 -03:00
. vidioc_s_ctrl = fimc_m2m_s_ctrl ,
2010-11-25 11:01:51 -03:00
. vidioc_g_crop = fimc_m2m_g_crop ,
2010-08-03 09:50:29 -03:00
. vidioc_s_crop = fimc_m2m_s_crop ,
2010-11-25 11:01:51 -03:00
. vidioc_cropcap = fimc_m2m_cropcap
2010-08-03 09:50:29 -03:00
} ;
2010-12-08 14:05:08 -03:00
static int queue_init ( void * priv , struct vb2_queue * src_vq ,
struct vb2_queue * dst_vq )
2010-08-03 09:50:29 -03:00
{
struct fimc_ctx * ctx = priv ;
2010-12-01 10:14:59 -03:00
int ret ;
memset ( src_vq , 0 , sizeof ( * src_vq ) ) ;
src_vq - > type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ;
src_vq - > io_modes = VB2_MMAP | VB2_USERPTR ;
src_vq - > drv_priv = ctx ;
src_vq - > ops = & fimc_qops ;
src_vq - > mem_ops = & vb2_dma_contig_memops ;
src_vq - > buf_struct_size = sizeof ( struct v4l2_m2m_buffer ) ;
2010-08-03 09:50:29 -03:00
2010-12-01 10:14:59 -03:00
ret = vb2_queue_init ( src_vq ) ;
if ( ret )
return ret ;
memset ( dst_vq , 0 , sizeof ( * dst_vq ) ) ;
dst_vq - > type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ;
dst_vq - > io_modes = VB2_MMAP | VB2_USERPTR ;
dst_vq - > drv_priv = ctx ;
dst_vq - > ops = & fimc_qops ;
dst_vq - > mem_ops = & vb2_dma_contig_memops ;
dst_vq - > buf_struct_size = sizeof ( struct v4l2_m2m_buffer ) ;
return vb2_queue_init ( dst_vq ) ;
2010-08-03 09:50:29 -03:00
}
static int fimc_m2m_open ( struct file * file )
{
struct fimc_dev * fimc = video_drvdata ( file ) ;
struct fimc_ctx * ctx = NULL ;
2010-10-07 10:06:16 -03:00
dbg ( " pid: %d, state: 0x%lx, refcnt: %d " ,
task_pid_nr ( current ) , fimc - > state , fimc - > vid_cap . refcnt ) ;
/*
* Return if the corresponding video capture node
* is already opened .
*/
2010-12-01 10:25:18 -03:00
if ( fimc - > vid_cap . refcnt > 0 )
return - EBUSY ;
2010-10-07 10:06:16 -03:00
2010-08-03 09:50:29 -03:00
fimc - > m2m . refcnt + + ;
set_bit ( ST_OUTDMA_RUN , & fimc - > state ) ;
ctx = kzalloc ( sizeof * ctx , GFP_KERNEL ) ;
2010-12-01 10:25:18 -03:00
if ( ! ctx )
return - ENOMEM ;
2010-08-03 09:50:29 -03:00
file - > private_data = ctx ;
ctx - > fimc_dev = fimc ;
2010-10-08 05:01:14 -03:00
/* Default color format */
2010-08-03 09:50:29 -03:00
ctx - > s_frame . fmt = & fimc_formats [ 0 ] ;
ctx - > d_frame . fmt = & fimc_formats [ 0 ] ;
2010-10-07 10:06:16 -03:00
/* Setup the device context for mem2mem mode. */
ctx - > state = FIMC_CTX_M2M ;
2010-08-03 09:50:29 -03:00
ctx - > flags = 0 ;
ctx - > in_path = FIMC_DMA ;
ctx - > out_path = FIMC_DMA ;
spin_lock_init ( & ctx - > slock ) ;
2010-12-01 10:14:59 -03:00
ctx - > m2m_ctx = v4l2_m2m_ctx_init ( fimc - > m2m . m2m_dev , ctx , queue_init ) ;
2010-08-03 09:50:29 -03:00
if ( IS_ERR ( ctx - > m2m_ctx ) ) {
2010-12-01 10:25:18 -03:00
int err = PTR_ERR ( ctx - > m2m_ctx ) ;
2010-08-03 09:50:29 -03:00
kfree ( ctx ) ;
2010-12-01 10:25:18 -03:00
return err ;
2010-08-03 09:50:29 -03:00
}
2010-10-07 10:06:16 -03:00
2010-12-01 10:25:18 -03:00
return 0 ;
2010-08-03 09:50:29 -03:00
}
static int fimc_m2m_release ( struct file * file )
{
struct fimc_ctx * ctx = file - > private_data ;
struct fimc_dev * fimc = ctx - > fimc_dev ;
2010-10-07 10:06:16 -03:00
dbg ( " pid: %d, state: 0x%lx, refcnt= %d " ,
task_pid_nr ( current ) , fimc - > state , fimc - > m2m . refcnt ) ;
2010-08-03 09:50:29 -03:00
v4l2_m2m_ctx_release ( ctx - > m2m_ctx ) ;
kfree ( ctx ) ;
if ( - - fimc - > m2m . refcnt < = 0 )
clear_bit ( ST_OUTDMA_RUN , & fimc - > state ) ;
2010-10-07 10:06:16 -03:00
2010-08-03 09:50:29 -03:00
return 0 ;
}
static unsigned int fimc_m2m_poll ( struct file * file ,
struct poll_table_struct * wait )
{
struct fimc_ctx * ctx = file - > private_data ;
2010-10-07 10:06:16 -03:00
2010-08-03 09:50:29 -03:00
return v4l2_m2m_poll ( file , ctx - > m2m_ctx , wait ) ;
}
static int fimc_m2m_mmap ( struct file * file , struct vm_area_struct * vma )
{
struct fimc_ctx * ctx = file - > private_data ;
2010-10-07 10:06:16 -03:00
2010-08-03 09:50:29 -03:00
return v4l2_m2m_mmap ( file , ctx - > m2m_ctx , vma ) ;
}
static const struct v4l2_file_operations fimc_m2m_fops = {
. owner = THIS_MODULE ,
. open = fimc_m2m_open ,
. release = fimc_m2m_release ,
. poll = fimc_m2m_poll ,
2010-11-18 07:36:34 -03:00
. unlocked_ioctl = video_ioctl2 ,
2010-08-03 09:50:29 -03:00
. mmap = fimc_m2m_mmap ,
} ;
static struct v4l2_m2m_ops m2m_ops = {
. device_run = fimc_dma_run ,
. job_abort = fimc_job_abort ,
} ;
static int fimc_register_m2m_device ( struct fimc_dev * fimc )
{
struct video_device * vfd ;
struct platform_device * pdev ;
struct v4l2_device * v4l2_dev ;
int ret = 0 ;
if ( ! fimc )
return - ENODEV ;
pdev = fimc - > pdev ;
v4l2_dev = & fimc - > m2m . v4l2_dev ;
/* set name if it is empty */
if ( ! v4l2_dev - > name [ 0 ] )
snprintf ( v4l2_dev - > name , sizeof ( v4l2_dev - > name ) ,
" %s.m2m " , dev_name ( & pdev - > dev ) ) ;
ret = v4l2_device_register ( & pdev - > dev , v4l2_dev ) ;
if ( ret )
2010-10-08 05:01:14 -03:00
goto err_m2m_r1 ;
2010-08-03 09:50:29 -03:00
vfd = video_device_alloc ( ) ;
if ( ! vfd ) {
v4l2_err ( v4l2_dev , " Failed to allocate video device \n " ) ;
goto err_m2m_r1 ;
}
vfd - > fops = & fimc_m2m_fops ;
vfd - > ioctl_ops = & fimc_m2m_ioctl_ops ;
vfd - > minor = - 1 ;
vfd - > release = video_device_release ;
2010-12-01 10:25:18 -03:00
vfd - > lock = & fimc - > lock ;
2010-08-03 09:50:29 -03:00
snprintf ( vfd - > name , sizeof ( vfd - > name ) , " %s:m2m " , dev_name ( & pdev - > dev ) ) ;
video_set_drvdata ( vfd , fimc ) ;
platform_set_drvdata ( pdev , fimc ) ;
fimc - > m2m . vfd = vfd ;
fimc - > m2m . m2m_dev = v4l2_m2m_init ( & m2m_ops ) ;
if ( IS_ERR ( fimc - > m2m . m2m_dev ) ) {
v4l2_err ( v4l2_dev , " failed to initialize v4l2-m2m device \n " ) ;
ret = PTR_ERR ( fimc - > m2m . m2m_dev ) ;
goto err_m2m_r2 ;
}
ret = video_register_device ( vfd , VFL_TYPE_GRABBER , - 1 ) ;
if ( ret ) {
v4l2_err ( v4l2_dev ,
" %s(): failed to register video device \n " , __func__ ) ;
goto err_m2m_r3 ;
}
v4l2_info ( v4l2_dev ,
" FIMC m2m driver registered as /dev/video%d \n " , vfd - > num ) ;
return 0 ;
err_m2m_r3 :
v4l2_m2m_release ( fimc - > m2m . m2m_dev ) ;
err_m2m_r2 :
video_device_release ( fimc - > m2m . vfd ) ;
err_m2m_r1 :
v4l2_device_unregister ( v4l2_dev ) ;
return ret ;
}
static void fimc_unregister_m2m_device ( struct fimc_dev * fimc )
{
if ( fimc ) {
v4l2_m2m_release ( fimc - > m2m . m2m_dev ) ;
video_unregister_device ( fimc - > m2m . vfd ) ;
2010-10-08 05:01:14 -03:00
2010-08-03 09:50:29 -03:00
v4l2_device_unregister ( & fimc - > m2m . v4l2_dev ) ;
}
}
static void fimc_clk_release ( struct fimc_dev * fimc )
{
int i ;
2010-12-27 15:34:43 -03:00
for ( i = 0 ; i < fimc - > num_clocks ; i + + ) {
2010-08-03 09:50:29 -03:00
if ( fimc - > clock [ i ] ) {
clk_disable ( fimc - > clock [ i ] ) ;
clk_put ( fimc - > clock [ i ] ) ;
}
}
}
static int fimc_clk_get ( struct fimc_dev * fimc )
{
int i ;
2010-12-27 15:34:43 -03:00
for ( i = 0 ; i < fimc - > num_clocks ; i + + ) {
fimc - > clock [ i ] = clk_get ( & fimc - > pdev - > dev , fimc_clocks [ i ] ) ;
if ( ! IS_ERR_OR_NULL ( fimc - > clock [ i ] ) ) {
clk_enable ( fimc - > clock [ i ] ) ;
continue ;
2010-08-03 09:50:29 -03:00
}
2010-12-27 15:34:43 -03:00
dev_err ( & fimc - > pdev - > dev , " failed to get fimc clock: %s \n " ,
fimc_clocks [ i ] ) ;
return - ENXIO ;
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 ;
int ret = 0 ;
2010-12-27 15:34:43 -03:00
int cap_input_index = - 1 ;
2010-08-03 09:50:29 -03:00
dev_dbg ( & pdev - > dev , " %s(): \n " , __func__ ) ;
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 ;
}
fimc = kzalloc ( sizeof ( struct fimc_dev ) , GFP_KERNEL ) ;
if ( ! fimc )
return - ENOMEM ;
fimc - > id = pdev - > id ;
fimc - > variant = drv_data - > variant [ fimc - > id ] ;
fimc - > pdev = pdev ;
2010-10-07 10:06:16 -03:00
fimc - > pdata = pdev - > dev . platform_data ;
2010-08-03 09:50:29 -03:00
fimc - > state = ST_IDLE ;
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 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " failed to find the registers \n " ) ;
ret = - ENOENT ;
goto err_info ;
}
fimc - > regs_res = request_mem_region ( res - > start , resource_size ( res ) ,
dev_name ( & pdev - > dev ) ) ;
if ( ! fimc - > regs_res ) {
dev_err ( & pdev - > dev , " failed to obtain register region \n " ) ;
ret = - ENOENT ;
goto err_info ;
}
fimc - > regs = ioremap ( res - > start , resource_size ( res ) ) ;
if ( ! fimc - > regs ) {
dev_err ( & pdev - > dev , " failed to map registers \n " ) ;
ret = - ENXIO ;
goto err_req_region ;
}
2010-12-27 15:34:43 -03:00
fimc - > num_clocks = MAX_FIMC_CLOCKS - 1 ;
/*
* Check if vide capture node needs to be registered for this device
* instance .
*/
if ( fimc - > pdata ) {
int i ;
for ( i = 0 ; i < FIMC_MAX_CAMIF_CLIENTS ; + + i )
if ( fimc - > pdata - > isp_info [ i ] )
break ;
if ( i < FIMC_MAX_CAMIF_CLIENTS ) {
cap_input_index = i ;
fimc - > num_clocks + + ;
}
}
2010-08-03 09:50:29 -03:00
ret = fimc_clk_get ( fimc ) ;
if ( ret )
goto err_regs_unmap ;
2010-12-27 15:34:43 -03:00
clk_set_rate ( fimc - > clock [ CLK_BUS ] , drv_data - > lclk_frequency ) ;
2010-08-03 09:50:29 -03:00
res = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " failed to get IRQ resource \n " ) ;
ret = - ENXIO ;
goto err_clk ;
}
fimc - > irq = res - > start ;
fimc_hw_reset ( fimc ) ;
ret = request_irq ( fimc - > irq , fimc_isr , 0 , pdev - > name , fimc ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to install irq (%d) \n " , ret ) ;
goto err_clk ;
}
2010-12-01 10:14:59 -03:00
/* Initialize contiguous memory allocator */
fimc - > alloc_ctx = vb2_dma_contig_init_ctx ( & fimc - > pdev - > dev ) ;
if ( IS_ERR ( fimc - > alloc_ctx ) ) {
ret = PTR_ERR ( fimc - > alloc_ctx ) ;
goto err_irq ;
}
2010-08-03 09:50:29 -03:00
ret = fimc_register_m2m_device ( fimc ) ;
if ( ret )
2010-10-08 05:01:14 -03:00
goto err_irq ;
2010-08-03 09:50:29 -03:00
2010-10-07 10:06:16 -03:00
/* At least one camera sensor is required to register capture node */
2010-12-27 15:34:43 -03:00
if ( cap_input_index > = 0 ) {
ret = fimc_register_capture_device ( fimc ) ;
if ( ret )
goto err_m2m ;
clk_disable ( fimc - > clock [ CLK_CAM ] ) ;
2010-10-07 10:06:16 -03:00
}
2010-10-11 13:19:27 -03:00
/*
* Exclude the additional output DMA address registers by masking
* them out on HW revisions that provide extended capabilites .
*/
if ( fimc - > variant - > out_buf_count > 4 )
fimc_hw_set_dma_seq ( fimc , 0xF ) ;
2010-08-03 09:50:29 -03:00
dev_dbg ( & pdev - > dev , " %s(): fimc-%d registered successfully \n " ,
__func__ , fimc - > id ) ;
return 0 ;
2010-10-07 10:06:16 -03:00
err_m2m :
fimc_unregister_m2m_device ( fimc ) ;
2010-08-03 09:50:29 -03:00
err_irq :
free_irq ( fimc - > irq , fimc ) ;
err_clk :
fimc_clk_release ( fimc ) ;
err_regs_unmap :
iounmap ( fimc - > regs ) ;
err_req_region :
release_resource ( fimc - > regs_res ) ;
kfree ( fimc - > regs_res ) ;
err_info :
kfree ( fimc ) ;
2010-10-08 05:01:14 -03:00
2010-08-03 09:50:29 -03:00
return ret ;
}
static int __devexit fimc_remove ( struct platform_device * pdev )
{
struct fimc_dev * fimc =
( struct fimc_dev * ) platform_get_drvdata ( pdev ) ;
free_irq ( fimc - > irq , fimc ) ;
fimc_hw_reset ( fimc ) ;
fimc_unregister_m2m_device ( fimc ) ;
2010-10-07 10:06:16 -03:00
fimc_unregister_capture_device ( fimc ) ;
2010-08-03 09:50:29 -03:00
fimc_clk_release ( fimc ) ;
2010-12-01 10:14:59 -03:00
vb2_dma_contig_cleanup_ctx ( fimc - > alloc_ctx ) ;
2010-08-03 09:50:29 -03:00
iounmap ( fimc - > regs ) ;
release_resource ( fimc - > regs_res ) ;
kfree ( fimc - > regs_res ) ;
kfree ( fimc ) ;
2010-10-08 05:01:14 -03:00
dev_info ( & pdev - > dev , " %s driver unloaded \n " , pdev - > name ) ;
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. */
static struct fimc_pix_limit s5p_pix_limit [ 3 ] = {
[ 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 ,
} ,
} ;
static struct samsung_fimc_variant fimc0_variant_s5p = {
. has_inp_rot = 1 ,
. has_out_rot = 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 ,
. 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 = {
. min_inp_pixsize = 16 ,
. min_out_pixsize = 16 ,
2010-10-11 13:19:27 -03:00
. hor_offs_align = 8 ,
. 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 ,
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 ,
. 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 ,
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 ,
. 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 = {
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 ,
. out_buf_count = 4 ,
. pix_limit = & s5p_pix_limit [ 2 ] ,
} ;
2010-08-03 09:50:29 -03:00
2010-10-11 13:19:27 -03:00
static struct samsung_fimc_variant fimc0_variant_s5pv310 = {
. pix_hoff = 1 ,
. has_inp_rot = 1 ,
. has_out_rot = 1 ,
2010-11-25 10:49:21 -03:00
. has_cistatus2 = 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 ,
. out_buf_count = 32 ,
. pix_limit = & s5p_pix_limit [ 1 ] ,
} ;
static struct samsung_fimc_variant fimc2_variant_s5pv310 = {
. pix_hoff = 1 ,
2010-11-25 10:49:21 -03:00
. has_cistatus2 = 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 ,
. out_buf_count = 32 ,
. pix_limit = & s5p_pix_limit [ 2 ] ,
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 */
static struct samsung_fimc_driverdata fimc_drvdata_s5pv310 = {
. variant = {
[ 0 ] = & fimc0_variant_s5pv310 ,
[ 1 ] = & fimc0_variant_s5pv310 ,
[ 2 ] = & fimc0_variant_s5pv310 ,
[ 3 ] = & fimc2_variant_s5pv310 ,
} ,
. 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
} , {
. name = " s5pv310-fimc " ,
. driver_data = ( unsigned long ) & fimc_drvdata_s5pv310 ,
2010-08-03 09:50:29 -03:00
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( platform , fimc_driver_ids ) ;
static struct platform_driver fimc_driver = {
. probe = fimc_probe ,
. remove = __devexit_p ( fimc_remove ) ,
. id_table = fimc_driver_ids ,
. driver = {
. name = MODULE_NAME ,
. owner = THIS_MODULE ,
}
} ;
static int __init fimc_init ( void )
{
2010-10-08 05:01:14 -03:00
int ret = platform_driver_register ( & fimc_driver ) ;
if ( ret )
err ( " platform_driver_register failed: %d \n " , ret ) ;
return ret ;
2010-08-03 09:50:29 -03:00
}
static void __exit fimc_exit ( void )
{
platform_driver_unregister ( & fimc_driver ) ;
}
module_init ( fimc_init ) ;
module_exit ( fimc_exit ) ;
2010-10-07 10:06:16 -03:00
MODULE_AUTHOR ( " Sylwester Nawrocki <s.nawrocki@samsung.com> " ) ;
MODULE_DESCRIPTION ( " S5P FIMC camera host interface/video postprocessor driver " ) ;
2010-08-03 09:50:29 -03:00
MODULE_LICENSE ( " GPL " ) ;