2010-10-07 17:06:16 +04:00
/*
2011-04-28 16:06:19 +04:00
* Samsung S5P / EXYNOS4 SoC series camera interface ( camera capture ) driver
2010-10-07 17:06:16 +04:00
*
2011-04-28 16:06:19 +04:00
* Copyright ( C ) 2010 - 2011 Samsung Electronics Co . , Ltd .
2010-10-07 17:06:16 +04:00
* Author : 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 version 2 as
* published by the Free Software Foundation .
*/
# 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>
2011-09-02 13:25:32 +04:00
# include <linux/pm_runtime.h>
2010-10-07 17:06:16 +04:00
# include <linux/list.h>
# include <linux/slab.h>
# include <linux/videodev2.h>
# include <media/v4l2-device.h>
# include <media/v4l2-ioctl.h>
# include <media/v4l2-mem2mem.h>
2010-12-01 16:14:59 +03:00
# include <media/videobuf2-core.h>
# include <media/videobuf2-dma-contig.h>
2010-10-07 17:06:16 +04:00
2011-08-25 02:25:10 +04:00
# include "fimc-mdevice.h"
2010-10-07 17:06:16 +04:00
# include "fimc-core.h"
2011-06-10 22:36:53 +04:00
static int fimc_init_capture ( struct fimc_dev * fimc )
{
struct fimc_ctx * ctx = fimc - > vid_cap . ctx ;
struct fimc_sensor_info * sensor ;
unsigned long flags ;
int ret = 0 ;
if ( fimc - > pipeline . sensor = = NULL | | ctx = = NULL )
return - ENXIO ;
if ( ctx - > s_frame . fmt = = NULL )
return - EINVAL ;
sensor = v4l2_get_subdev_hostdata ( fimc - > pipeline . sensor ) ;
spin_lock_irqsave ( & fimc - > slock , flags ) ;
fimc_prepare_dma_offset ( ctx , & ctx - > d_frame ) ;
fimc_set_yuv_order ( ctx ) ;
fimc_hw_set_camera_polarity ( fimc , sensor - > pdata ) ;
fimc_hw_set_camera_type ( fimc , sensor - > pdata ) ;
fimc_hw_set_camera_source ( fimc , sensor - > pdata ) ;
fimc_hw_set_camera_offset ( fimc , & ctx - > s_frame ) ;
ret = fimc_set_scaler_info ( ctx ) ;
if ( ! ret ) {
fimc_hw_set_input_path ( ctx ) ;
fimc_hw_set_prescaler ( ctx ) ;
fimc_hw_set_mainscaler ( ctx ) ;
fimc_hw_set_target_format ( ctx ) ;
fimc_hw_set_rotation ( ctx ) ;
fimc_hw_set_effect ( ctx ) ;
fimc_hw_set_output_path ( ctx ) ;
fimc_hw_set_out_dma ( ctx ) ;
}
spin_unlock_irqrestore ( & fimc - > slock , flags ) ;
return ret ;
}
2011-08-29 15:51:49 +04:00
static void fimc_capture_state_cleanup ( struct fimc_dev * fimc )
2010-10-07 17:06:16 +04:00
{
2011-08-29 15:51:49 +04:00
struct fimc_vid_cap * cap = & fimc - > vid_cap ;
2010-12-01 16:14:59 +03:00
struct fimc_vid_buffer * buf ;
2011-08-29 15:51:49 +04:00
unsigned long flags ;
2010-10-07 17:06:16 +04:00
spin_lock_irqsave ( & fimc - > slock , flags ) ;
fimc - > state & = ~ ( 1 < < ST_CAPT_RUN | 1 < < ST_CAPT_PEND |
2011-02-07 21:59:46 +03:00
1 < < ST_CAPT_SHUT | 1 < < ST_CAPT_STREAM ) ;
2010-10-07 17:06:16 +04:00
fimc - > vid_cap . active_buf_cnt = 0 ;
2010-12-01 16:14:59 +03:00
/* Release buffers that were enqueued in the driver by videobuf2. */
while ( ! list_empty ( & cap - > pending_buf_q ) ) {
buf = pending_queue_pop ( cap ) ;
vb2_buffer_done ( & buf - > vb , VB2_BUF_STATE_ERROR ) ;
}
while ( ! list_empty ( & cap - > active_buf_q ) ) {
buf = active_queue_pop ( cap ) ;
vb2_buffer_done ( & buf - > vb , VB2_BUF_STATE_ERROR ) ;
}
2010-10-07 17:06:16 +04:00
spin_unlock_irqrestore ( & fimc - > slock , flags ) ;
2011-08-29 15:51:49 +04:00
}
static int fimc_stop_capture ( struct fimc_dev * fimc )
{
struct fimc_vid_cap * cap = & fimc - > vid_cap ;
unsigned long flags ;
if ( ! fimc_capture_active ( fimc ) )
return 0 ;
spin_lock_irqsave ( & fimc - > slock , flags ) ;
set_bit ( ST_CAPT_SHUT , & fimc - > state ) ;
fimc_deactivate_capture ( fimc ) ;
spin_unlock_irqrestore ( & fimc - > slock , flags ) ;
wait_event_timeout ( fimc - > irq_queue ,
! test_bit ( ST_CAPT_SHUT , & fimc - > state ) ,
FIMC_SHUTDOWN_TIMEOUT ) ;
2010-10-07 17:06:16 +04:00
2011-08-29 15:51:49 +04:00
v4l2_subdev_call ( cap - > sd , video , s_stream , 0 ) ;
fimc_capture_state_cleanup ( fimc ) ;
2010-10-07 17:06:16 +04:00
dbg ( " state: 0x%lx " , fimc - > state ) ;
return 0 ;
}
2011-08-29 15:51:49 +04:00
static int start_streaming ( struct vb2_queue * q , unsigned int count )
2010-12-01 16:14:59 +03:00
{
struct fimc_ctx * ctx = q - > drv_priv ;
struct fimc_dev * fimc = ctx - > fimc_dev ;
2011-06-10 22:36:53 +04:00
struct fimc_vid_cap * vid_cap = & fimc - > vid_cap ;
2011-08-29 15:51:49 +04:00
int min_bufs ;
2010-12-01 16:14:59 +03:00
int ret ;
2011-02-07 21:59:46 +03:00
fimc_hw_reset ( fimc ) ;
2011-06-10 22:36:53 +04:00
vid_cap - > frame_count = 0 ;
2011-02-07 21:59:46 +03:00
2011-06-10 22:36:53 +04:00
ret = fimc_init_capture ( fimc ) ;
2010-12-01 16:14:59 +03:00
if ( ret )
2011-08-29 15:51:49 +04:00
goto error ;
2010-12-01 16:14:59 +03:00
set_bit ( ST_CAPT_PEND , & fimc - > state ) ;
2011-08-29 15:51:49 +04:00
min_bufs = fimc - > vid_cap . reqbufs_count > 1 ? 2 : 1 ;
if ( fimc - > vid_cap . active_buf_cnt > = min_bufs )
fimc_activate_capture ( ctx ) ;
2010-12-01 16:14:59 +03:00
return 0 ;
2011-08-29 15:51:49 +04:00
error :
fimc_capture_state_cleanup ( fimc ) ;
return ret ;
2010-12-01 16:14:59 +03:00
}
static int stop_streaming ( struct vb2_queue * q )
{
struct fimc_ctx * ctx = q - > drv_priv ;
struct fimc_dev * fimc = ctx - > fimc_dev ;
2011-02-23 14:24:33 +03:00
if ( ! fimc_capture_active ( fimc ) )
2010-12-01 16:14:59 +03:00
return - EINVAL ;
return fimc_stop_capture ( fimc ) ;
}
2011-09-02 13:25:32 +04:00
int fimc_capture_suspend ( struct fimc_dev * fimc )
{
return - EBUSY ;
}
int fimc_capture_resume ( struct fimc_dev * fimc )
{
return 0 ;
}
2010-12-08 20:05:08 +03:00
static unsigned int get_plane_size ( struct fimc_frame * fr , unsigned int plane )
2010-12-01 16:14:59 +03:00
{
2010-12-08 20:05:08 +03:00
if ( ! fr | | plane > = fr - > fmt - > memplanes )
2010-12-01 16:14:59 +03:00
return 0 ;
2010-12-08 20:05:08 +03:00
return fr - > f_width * fr - > f_height * fr - > fmt - > depth [ plane ] / 8 ;
2010-12-01 16:14:59 +03:00
}
static int queue_setup ( struct vb2_queue * vq , unsigned int * num_buffers ,
2011-08-24 13:43:36 +04:00
unsigned int * num_planes , unsigned int sizes [ ] ,
2010-12-01 16:14:59 +03:00
void * allocators [ ] )
{
struct fimc_ctx * ctx = vq - > drv_priv ;
2010-12-08 20:05:08 +03:00
struct fimc_fmt * fmt = ctx - > d_frame . fmt ;
int i ;
2010-12-01 16:14:59 +03:00
if ( ! fmt )
return - EINVAL ;
2010-12-08 20:05:08 +03:00
* num_planes = fmt - > memplanes ;
2010-12-01 16:14:59 +03:00
2010-12-08 20:05:08 +03:00
for ( i = 0 ; i < fmt - > memplanes ; i + + ) {
sizes [ i ] = get_plane_size ( & ctx - > d_frame , i ) ;
allocators [ i ] = ctx - > fimc_dev - > alloc_ctx ;
}
2010-12-01 16:14:59 +03:00
2010-12-08 20:05:08 +03:00
return 0 ;
2010-12-01 16:14:59 +03:00
}
static int buffer_prepare ( struct vb2_buffer * vb )
{
struct vb2_queue * vq = vb - > vb2_queue ;
struct fimc_ctx * ctx = vq - > drv_priv ;
int i ;
2010-12-08 20:05:08 +03:00
if ( ! ctx - > d_frame . fmt | | vq - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE )
return - EINVAL ;
2010-12-01 16:14:59 +03:00
2010-12-08 20:05:08 +03:00
for ( i = 0 ; i < ctx - > d_frame . fmt - > memplanes ; i + + ) {
unsigned long size = get_plane_size ( & ctx - > d_frame , i ) ;
2010-12-01 16:14:59 +03:00
if ( vb2_plane_size ( vb , i ) < size ) {
2011-06-10 22:36:48 +04:00
v4l2_err ( ctx - > fimc_dev - > vid_cap . vfd ,
" User buffer too small (%ld < %ld) \n " ,
2010-12-01 16:14:59 +03:00
vb2_plane_size ( vb , i ) , size ) ;
return - EINVAL ;
}
vb2_set_plane_payload ( vb , i , size ) ;
}
return 0 ;
}
static void buffer_queue ( struct vb2_buffer * vb )
{
struct fimc_ctx * ctx = vb2_get_drv_priv ( vb - > vb2_queue ) ;
struct fimc_dev * fimc = ctx - > fimc_dev ;
struct fimc_vid_buffer * buf
= container_of ( vb , struct fimc_vid_buffer , vb ) ;
struct fimc_vid_cap * vid_cap = & fimc - > vid_cap ;
unsigned long flags ;
2011-02-07 21:59:46 +03:00
int min_bufs ;
2010-12-01 16:14:59 +03:00
spin_lock_irqsave ( & fimc - > slock , flags ) ;
2011-02-07 21:59:46 +03:00
fimc_prepare_addr ( ctx , & buf - > vb , & ctx - > d_frame , & buf - > paddr ) ;
if ( ! test_bit ( ST_CAPT_STREAM , & fimc - > state )
& & vid_cap - > active_buf_cnt < FIMC_MAX_OUT_BUFS ) {
/* Setup the buffer directly for processing. */
int buf_id = ( vid_cap - > reqbufs_count = = 1 ) ? - 1 :
vid_cap - > buf_index ;
2010-12-01 16:14:59 +03:00
2011-02-07 21:59:46 +03:00
fimc_hw_set_output_addr ( fimc , & buf - > paddr , buf_id ) ;
buf - > index = vid_cap - > buf_index ;
active_queue_add ( vid_cap , buf ) ;
2010-12-01 16:14:59 +03:00
2011-02-07 21:59:46 +03:00
if ( + + vid_cap - > buf_index > = FIMC_MAX_OUT_BUFS )
vid_cap - > buf_index = 0 ;
} else {
fimc_pending_queue_add ( vid_cap , buf ) ;
2010-12-01 16:14:59 +03:00
}
2011-02-07 21:59:46 +03:00
min_bufs = vid_cap - > reqbufs_count > 1 ? 2 : 1 ;
2011-08-29 15:51:49 +04:00
if ( vb2_is_streaming ( & vid_cap - > vbq ) & &
vid_cap - > active_buf_cnt > = min_bufs & &
2011-02-07 21:59:46 +03:00
! test_and_set_bit ( ST_CAPT_STREAM , & fimc - > state ) )
fimc_activate_capture ( ctx ) ;
2010-12-01 16:14:59 +03:00
spin_unlock_irqrestore ( & fimc - > slock , flags ) ;
}
static void fimc_lock ( struct vb2_queue * vq )
{
struct fimc_ctx * ctx = vb2_get_drv_priv ( vq ) ;
mutex_lock ( & ctx - > fimc_dev - > lock ) ;
}
static void fimc_unlock ( struct vb2_queue * vq )
{
struct fimc_ctx * ctx = vb2_get_drv_priv ( vq ) ;
mutex_unlock ( & ctx - > fimc_dev - > lock ) ;
}
static struct vb2_ops fimc_capture_qops = {
. queue_setup = queue_setup ,
. buf_prepare = buffer_prepare ,
. buf_queue = buffer_queue ,
. wait_prepare = fimc_unlock ,
. wait_finish = fimc_lock ,
. start_streaming = start_streaming ,
. stop_streaming = stop_streaming ,
} ;
2011-08-25 02:25:10 +04:00
/**
* fimc_capture_ctrls_create - initialize the control handler
* Initialize the capture video node control handler and fill it
* with the FIMC controls . Inherit any sensor ' s controls if the
* ' user_subdev_api ' flag is false ( default behaviour ) .
* This function need to be called with the graph mutex held .
*/
int fimc_capture_ctrls_create ( struct fimc_dev * fimc )
{
struct fimc_vid_cap * vid_cap = & fimc - > vid_cap ;
int ret ;
if ( WARN_ON ( vid_cap - > ctx = = NULL ) )
return - ENXIO ;
if ( vid_cap - > ctx - > ctrls_rdy )
return 0 ;
ret = fimc_ctrls_create ( vid_cap - > ctx ) ;
if ( ret | | vid_cap - > user_subdev_api )
return ret ;
return v4l2_ctrl_add_handler ( & vid_cap - > ctx - > ctrl_handler ,
fimc - > pipeline . sensor - > ctrl_handler ) ;
}
2010-10-07 17:06:16 +04:00
static int fimc_capture_open ( struct file * file )
{
struct fimc_dev * fimc = video_drvdata ( file ) ;
2011-06-10 22:36:50 +04:00
int ret = v4l2_fh_open ( file ) ;
if ( ret )
return ret ;
2010-10-07 17:06:16 +04:00
dbg ( " pid: %d, state: 0x%lx " , task_pid_nr ( current ) , fimc - > state ) ;
/* Return if the corresponding video mem2mem node is already opened. */
if ( fimc_m2m_active ( fimc ) )
return - EBUSY ;
2011-09-02 13:25:32 +04:00
ret = pm_runtime_get_sync ( & fimc - > pdev - > dev ) ;
2011-06-10 22:36:50 +04:00
if ( ret < 0 ) {
v4l2_fh_release ( file ) ;
2011-09-02 13:25:32 +04:00
return ret ;
2011-06-10 22:36:50 +04:00
}
2011-09-02 13:25:32 +04:00
2011-08-25 02:25:10 +04:00
if ( + + fimc - > vid_cap . refcnt = = 1 )
ret = fimc_capture_ctrls_create ( fimc ) ;
2010-10-07 17:06:16 +04:00
2011-08-25 02:25:10 +04:00
return ret ;
2010-10-07 17:06:16 +04:00
}
static int fimc_capture_close ( struct file * file )
{
struct fimc_dev * fimc = video_drvdata ( file ) ;
dbg ( " pid: %d, state: 0x%lx " , task_pid_nr ( current ) , fimc - > state ) ;
if ( - - fimc - > vid_cap . refcnt = = 0 ) {
fimc_stop_capture ( fimc ) ;
2011-08-25 02:25:10 +04:00
fimc_ctrls_delete ( fimc - > vid_cap . ctx ) ;
2010-12-01 16:14:59 +03:00
vb2_queue_release ( & fimc - > vid_cap . vbq ) ;
2010-10-07 17:06:16 +04:00
}
2011-09-02 13:25:32 +04:00
pm_runtime_put ( & fimc - > pdev - > dev ) ;
2011-06-10 22:36:50 +04:00
return v4l2_fh_release ( file ) ;
2010-10-07 17:06:16 +04:00
}
static unsigned int fimc_capture_poll ( struct file * file ,
struct poll_table_struct * wait )
{
2011-06-10 22:36:50 +04:00
struct fimc_dev * fimc = video_drvdata ( file ) ;
2010-10-07 17:06:16 +04:00
2010-12-01 16:25:18 +03:00
return vb2_poll ( & fimc - > vid_cap . vbq , file , wait ) ;
2010-10-07 17:06:16 +04:00
}
static int fimc_capture_mmap ( struct file * file , struct vm_area_struct * vma )
{
2011-06-10 22:36:50 +04:00
struct fimc_dev * fimc = video_drvdata ( file ) ;
2010-10-07 17:06:16 +04:00
2010-12-01 16:25:18 +03:00
return vb2_mmap ( & fimc - > vid_cap . vbq , vma ) ;
2010-10-07 17:06:16 +04:00
}
/* video device file operations */
static const struct v4l2_file_operations fimc_capture_fops = {
. owner = THIS_MODULE ,
. open = fimc_capture_open ,
. release = fimc_capture_close ,
. poll = fimc_capture_poll ,
. unlocked_ioctl = video_ioctl2 ,
. mmap = fimc_capture_mmap ,
} ;
static int fimc_vidioc_querycap_capture ( struct file * file , void * priv ,
struct v4l2_capability * cap )
{
2011-06-10 22:36:50 +04:00
struct fimc_dev * fimc = video_drvdata ( file ) ;
2010-10-07 17:06:16 +04:00
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 ;
2010-12-08 20:05:08 +03:00
cap - > capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_VIDEO_CAPTURE_MPLANE ;
2010-10-07 17:06:16 +04:00
return 0 ;
}
/* Synchronize formats of the camera interface input and attached sensor. */
static int sync_capture_fmt ( struct fimc_ctx * ctx )
{
struct fimc_frame * frame = & ctx - > s_frame ;
struct fimc_dev * fimc = ctx - > fimc_dev ;
struct v4l2_mbus_framefmt * fmt = & fimc - > vid_cap . fmt ;
int ret ;
fmt - > width = ctx - > d_frame . o_width ;
fmt - > height = ctx - > d_frame . o_height ;
ret = v4l2_subdev_call ( fimc - > vid_cap . sd , video , s_mbus_fmt , fmt ) ;
if ( ret = = - ENOIOCTLCMD ) {
err ( " s_mbus_fmt failed " ) ;
return ret ;
}
dbg ( " w: %d, h: %d, code= %d " , fmt - > width , fmt - > height , fmt - > code ) ;
frame - > fmt = find_mbus_format ( fmt , FMT_FLAGS_CAM ) ;
if ( ! frame - > fmt ) {
err ( " fimc source format not found \n " ) ;
return - EINVAL ;
}
frame - > f_width = fmt - > width ;
frame - > f_height = fmt - > height ;
frame - > width = fmt - > width ;
frame - > height = fmt - > height ;
frame - > o_width = fmt - > width ;
frame - > o_height = fmt - > height ;
frame - > offs_h = 0 ;
frame - > offs_v = 0 ;
return 0 ;
}
2011-06-10 22:36:50 +04:00
static int fimc_cap_g_fmt_mplane ( struct file * file , void * fh ,
struct v4l2_format * f )
{
struct fimc_dev * fimc = video_drvdata ( file ) ;
struct fimc_ctx * ctx = fimc - > vid_cap . ctx ;
if ( f - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE )
return - EINVAL ;
return fimc_fill_format ( & ctx - > d_frame , f ) ;
}
static int fimc_cap_try_fmt_mplane ( struct file * file , void * fh ,
struct v4l2_format * f )
{
struct fimc_dev * fimc = video_drvdata ( file ) ;
struct fimc_ctx * ctx = fimc - > vid_cap . ctx ;
return fimc_try_fmt_mplane ( ctx , f ) ;
}
2010-12-08 20:05:08 +03:00
static int fimc_cap_s_fmt_mplane ( struct file * file , void * priv ,
struct v4l2_format * f )
2010-10-07 17:06:16 +04:00
{
2011-06-10 22:36:50 +04:00
struct fimc_dev * fimc = video_drvdata ( file ) ;
struct fimc_ctx * ctx = fimc - > vid_cap . ctx ;
2010-12-08 20:05:08 +03:00
struct v4l2_pix_format_mplane * pix ;
2011-06-10 22:36:50 +04:00
struct fimc_frame * frame ;
2010-10-07 17:06:16 +04:00
int ret ;
2010-12-08 20:05:08 +03:00
int i ;
2010-10-07 17:06:16 +04:00
2010-12-08 20:05:08 +03:00
if ( f - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE )
2010-10-07 17:06:16 +04:00
return - EINVAL ;
2011-06-10 22:36:50 +04:00
ret = fimc_try_fmt_mplane ( ctx , f ) ;
2010-10-07 17:06:16 +04:00
if ( ret )
return ret ;
2011-03-15 20:50:52 +03:00
if ( vb2_is_busy ( & fimc - > vid_cap . vbq ) | | fimc_capture_active ( fimc ) )
2010-12-08 20:05:08 +03:00
return - EBUSY ;
2010-10-07 17:06:16 +04:00
frame = & ctx - > d_frame ;
2010-12-08 20:05:08 +03:00
pix = & f - > fmt . pix_mp ;
2010-10-07 17:06:16 +04:00
frame - > fmt = find_format ( f , FMT_FLAGS_M2M | FMT_FLAGS_CAM ) ;
if ( ! frame - > fmt ) {
2011-06-10 22:36:48 +04:00
v4l2_err ( fimc - > vid_cap . vfd ,
" Not supported capture (FIMC target) color format \n " ) ;
2010-12-01 16:25:18 +03:00
return - EINVAL ;
2010-10-07 17:06:16 +04:00
}
2011-04-08 16:11:43 +04:00
for ( i = 0 ; i < frame - > fmt - > colplanes ; i + + ) {
frame - > payload [ i ] =
( pix - > width * pix - > height * frame - > fmt - > depth [ i ] ) > > 3 ;
}
2010-12-08 20:05:08 +03:00
2010-10-07 17:06:16 +04:00
/* Output DMA frame pixel size and offsets. */
2010-12-08 20:05:08 +03:00
frame - > f_width = pix - > plane_fmt [ 0 ] . bytesperline * 8
/ frame - > fmt - > depth [ 0 ] ;
2010-10-07 17:06:16 +04:00
frame - > f_height = pix - > height ;
frame - > width = pix - > width ;
frame - > height = pix - > height ;
frame - > o_width = pix - > width ;
frame - > o_height = pix - > height ;
frame - > offs_h = 0 ;
frame - > offs_v = 0 ;
ctx - > state | = ( FIMC_PARAMS | FIMC_DST_FMT ) ;
2010-12-01 16:25:18 +03:00
ret = sync_capture_fmt ( ctx ) ;
2010-10-07 17:06:16 +04:00
return ret ;
}
static int fimc_cap_enum_input ( struct file * file , void * priv ,
2011-07-27 01:27:59 +04:00
struct v4l2_input * i )
2010-10-07 17:06:16 +04:00
{
2011-06-10 22:36:50 +04:00
struct fimc_dev * fimc = video_drvdata ( file ) ;
2010-10-07 17:06:16 +04:00
2011-07-27 01:27:59 +04:00
if ( i - > index ! = 0 )
2010-10-07 17:06:16 +04:00
return - EINVAL ;
i - > type = V4L2_INPUT_TYPE_CAMERA ;
return 0 ;
}
2011-07-27 01:27:59 +04:00
static int fimc_cap_s_input ( struct file * file , void * priv , unsigned int i )
2010-10-07 17:06:16 +04:00
{
2011-07-27 01:27:59 +04:00
return i = = 0 ? i : - EINVAL ;
2010-10-07 17:06:16 +04:00
}
2011-07-27 01:27:59 +04:00
static int fimc_cap_g_input ( struct file * file , void * priv , unsigned int * i )
2010-10-07 17:06:16 +04:00
{
2011-07-27 01:27:59 +04:00
* i = 0 ;
2010-10-07 17:06:16 +04:00
return 0 ;
}
static int fimc_cap_streamon ( struct file * file , void * priv ,
2010-12-01 16:14:59 +03:00
enum v4l2_buf_type type )
2010-10-07 17:06:16 +04:00
{
2011-06-10 22:36:50 +04:00
struct fimc_dev * fimc = video_drvdata ( file ) ;
struct fimc_ctx * ctx = fimc - > vid_cap . ctx ;
2010-10-07 17:06:16 +04:00
if ( fimc_capture_active ( fimc ) | | ! fimc - > vid_cap . sd )
2010-12-01 16:25:18 +03:00
return - EBUSY ;
2010-10-07 17:06:16 +04:00
if ( ! ( ctx - > state & FIMC_DST_FMT ) ) {
2011-06-10 22:36:48 +04:00
v4l2_err ( fimc - > vid_cap . vfd , " Format is not set \n " ) ;
2010-12-01 16:25:18 +03:00
return - EINVAL ;
2010-10-07 17:06:16 +04:00
}
2010-12-01 16:25:18 +03:00
return vb2_streamon ( & fimc - > vid_cap . vbq , type ) ;
2010-10-07 17:06:16 +04:00
}
static int fimc_cap_streamoff ( struct file * file , void * priv ,
2010-12-01 16:25:18 +03:00
enum v4l2_buf_type type )
2010-10-07 17:06:16 +04:00
{
2011-06-10 22:36:50 +04:00
struct fimc_dev * fimc = video_drvdata ( file ) ;
2010-10-07 17:06:16 +04:00
2010-12-01 16:25:18 +03:00
return vb2_streamoff ( & fimc - > vid_cap . vbq , type ) ;
2010-10-07 17:06:16 +04:00
}
static int fimc_cap_reqbufs ( struct file * file , void * priv ,
2010-12-08 20:05:08 +03:00
struct v4l2_requestbuffers * reqbufs )
2010-10-07 17:06:16 +04:00
{
2011-06-10 22:36:50 +04:00
struct fimc_dev * fimc = video_drvdata ( file ) ;
int ret = vb2_reqbufs ( & fimc - > vid_cap . vbq , reqbufs ) ;
2010-10-07 17:06:16 +04:00
if ( ! ret )
2011-06-10 22:36:50 +04:00
fimc - > vid_cap . reqbufs_count = reqbufs - > count ;
2010-10-07 17:06:16 +04:00
return ret ;
}
static int fimc_cap_querybuf ( struct file * file , void * priv ,
struct v4l2_buffer * buf )
{
2011-06-10 22:36:50 +04:00
struct fimc_dev * fimc = video_drvdata ( file ) ;
2010-10-07 17:06:16 +04:00
2011-06-10 22:36:50 +04:00
return vb2_querybuf ( & fimc - > vid_cap . vbq , buf ) ;
2010-10-07 17:06:16 +04:00
}
static int fimc_cap_qbuf ( struct file * file , void * priv ,
struct v4l2_buffer * buf )
{
2011-06-10 22:36:50 +04:00
struct fimc_dev * fimc = video_drvdata ( file ) ;
return vb2_qbuf ( & fimc - > vid_cap . vbq , buf ) ;
2010-10-07 17:06:16 +04:00
}
static int fimc_cap_dqbuf ( struct file * file , void * priv ,
struct v4l2_buffer * buf )
{
2011-06-10 22:36:50 +04:00
struct fimc_dev * fimc = video_drvdata ( file ) ;
return vb2_dqbuf ( & fimc - > vid_cap . vbq , buf , file - > f_flags & O_NONBLOCK ) ;
2010-10-07 17:06:16 +04:00
}
2010-11-25 17:01:51 +03:00
static int fimc_cap_cropcap ( struct file * file , void * fh ,
struct v4l2_cropcap * cr )
{
2011-06-10 22:36:50 +04:00
struct fimc_dev * fimc = video_drvdata ( file ) ;
struct fimc_frame * f = & fimc - > vid_cap . ctx - > s_frame ;
2010-11-25 17:01:51 +03:00
2010-12-08 20:05:08 +03:00
if ( cr - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE )
2010-11-25 17:01:51 +03:00
return - EINVAL ;
cr - > bounds . left = 0 ;
cr - > bounds . top = 0 ;
cr - > bounds . width = f - > o_width ;
cr - > bounds . height = f - > o_height ;
cr - > defrect = cr - > bounds ;
return 0 ;
}
static int fimc_cap_g_crop ( struct file * file , void * fh , struct v4l2_crop * cr )
{
2011-06-10 22:36:50 +04:00
struct fimc_dev * fimc = video_drvdata ( file ) ;
struct fimc_frame * f = & fimc - > vid_cap . ctx - > s_frame ;
2010-12-01 16:25:18 +03:00
2010-11-25 17:01:51 +03:00
cr - > c . left = f - > offs_h ;
cr - > c . top = f - > offs_v ;
cr - > c . width = f - > width ;
cr - > c . height = f - > height ;
return 0 ;
}
2011-06-10 22:36:50 +04:00
static int fimc_cap_s_crop ( struct file * file , void * fh , struct v4l2_crop * cr )
2010-10-07 17:06:16 +04:00
{
2011-06-10 22:36:50 +04:00
struct fimc_dev * fimc = video_drvdata ( file ) ;
struct fimc_ctx * ctx = fimc - > vid_cap . ctx ;
2010-10-07 17:06:16 +04:00
struct fimc_frame * f ;
int ret = - EINVAL ;
if ( fimc_capture_active ( fimc ) )
return - EBUSY ;
ret = fimc_try_crop ( ctx , cr ) ;
if ( ret )
return ret ;
if ( ! ( ctx - > state & FIMC_DST_FMT ) ) {
2011-06-10 22:36:48 +04:00
v4l2_err ( fimc - > vid_cap . vfd , " Capture format is not set \n " ) ;
return - EINVAL ;
2010-10-07 17:06:16 +04:00
}
f = & ctx - > s_frame ;
/* Check for the pixel scaling ratio when cropping input image. */
2010-12-29 04:12:43 +03:00
ret = fimc_check_scaler_ratio ( cr - > c . width , cr - > c . height ,
ctx - > d_frame . width , ctx - > d_frame . height ,
ctx - > rotation ) ;
2010-10-07 17:06:16 +04:00
if ( ret ) {
2011-06-10 22:36:48 +04:00
v4l2_err ( fimc - > vid_cap . vfd , " Out of the scaler range \n " ) ;
2010-12-01 16:25:18 +03:00
return ret ;
2010-10-07 17:06:16 +04:00
}
2010-12-01 16:25:18 +03:00
f - > offs_h = cr - > c . left ;
f - > offs_v = cr - > c . top ;
f - > width = cr - > c . width ;
f - > height = cr - > c . height ;
return 0 ;
2010-10-07 17:06:16 +04:00
}
static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
. vidioc_querycap = fimc_vidioc_querycap_capture ,
2010-12-08 20:05:08 +03:00
. vidioc_enum_fmt_vid_cap_mplane = fimc_vidioc_enum_fmt_mplane ,
2011-06-10 22:36:50 +04:00
. vidioc_try_fmt_vid_cap_mplane = fimc_cap_try_fmt_mplane ,
2010-12-08 20:05:08 +03:00
. vidioc_s_fmt_vid_cap_mplane = fimc_cap_s_fmt_mplane ,
2011-06-10 22:36:50 +04:00
. vidioc_g_fmt_vid_cap_mplane = fimc_cap_g_fmt_mplane ,
2010-10-07 17:06:16 +04:00
. vidioc_reqbufs = fimc_cap_reqbufs ,
. vidioc_querybuf = fimc_cap_querybuf ,
. vidioc_qbuf = fimc_cap_qbuf ,
. vidioc_dqbuf = fimc_cap_dqbuf ,
. vidioc_streamon = fimc_cap_streamon ,
. vidioc_streamoff = fimc_cap_streamoff ,
2010-11-25 17:01:51 +03:00
. vidioc_g_crop = fimc_cap_g_crop ,
2010-10-07 17:06:16 +04:00
. vidioc_s_crop = fimc_cap_s_crop ,
2010-11-25 17:01:51 +03:00
. vidioc_cropcap = fimc_cap_cropcap ,
2010-10-07 17:06:16 +04:00
. vidioc_enum_input = fimc_cap_enum_input ,
. vidioc_s_input = fimc_cap_s_input ,
. vidioc_g_input = fimc_cap_g_input ,
} ;
2011-08-25 02:28:18 +04:00
/* Media operations */
static int fimc_link_setup ( struct media_entity * entity ,
const struct media_pad * local ,
const struct media_pad * remote , u32 flags )
{
struct video_device * vd = media_entity_to_video_device ( entity ) ;
struct v4l2_subdev * sd = media_entity_to_v4l2_subdev ( remote - > entity ) ;
struct fimc_dev * fimc = video_get_drvdata ( vd ) ;
if ( WARN_ON ( fimc = = NULL ) )
return 0 ;
dbg ( " %s --> %s, flags: 0x%x. input: 0x%x " ,
local - > entity - > name , remote - > entity - > name , flags ,
fimc - > vid_cap . input ) ;
if ( flags & MEDIA_LNK_FL_ENABLED ) {
if ( fimc - > vid_cap . input ! = 0 )
return - EBUSY ;
fimc - > vid_cap . input = sd - > grp_id ;
return 0 ;
}
fimc - > vid_cap . input = 0 ;
return 0 ;
}
static const struct media_entity_operations fimc_media_ops = {
. link_setup = fimc_link_setup ,
} ;
2010-12-08 20:05:08 +03:00
/* fimc->lock must be already initialized */
2011-06-10 22:36:48 +04:00
int fimc_register_capture_device ( struct fimc_dev * fimc ,
struct v4l2_device * v4l2_dev )
2010-10-07 17:06:16 +04:00
{
struct video_device * vfd ;
struct fimc_vid_cap * vid_cap ;
struct fimc_ctx * ctx ;
struct v4l2_format f ;
2010-12-08 20:05:08 +03:00
struct fimc_frame * fr ;
2010-12-01 16:14:59 +03:00
struct vb2_queue * q ;
2011-06-10 22:36:48 +04:00
int ret = - ENOMEM ;
2010-10-07 17:06:16 +04:00
ctx = kzalloc ( sizeof * ctx , GFP_KERNEL ) ;
if ( ! ctx )
return - ENOMEM ;
ctx - > fimc_dev = fimc ;
ctx - > in_path = FIMC_CAMERA ;
ctx - > out_path = FIMC_DMA ;
ctx - > state = FIMC_CTX_CAP ;
2010-12-08 20:05:08 +03:00
/* Default format of the output frames */
f . fmt . pix . pixelformat = V4L2_PIX_FMT_RGB32 ;
fr = & ctx - > d_frame ;
fr - > fmt = find_format ( & f , FMT_FLAGS_M2M ) ;
fr - > width = fr - > f_width = fr - > o_width = 640 ;
fr - > height = fr - > f_height = fr - > o_height = 480 ;
2010-10-07 17:06:16 +04:00
vfd = video_device_alloc ( ) ;
if ( ! vfd ) {
v4l2_err ( v4l2_dev , " Failed to allocate video device \n " ) ;
2011-06-10 22:36:48 +04:00
goto err_vd_alloc ;
2010-10-07 17:06:16 +04:00
}
2011-06-10 22:36:48 +04:00
snprintf ( vfd - > name , sizeof ( vfd - > name ) , " %s.capture " ,
dev_name ( & fimc - > pdev - > dev ) ) ;
2010-10-07 17:06:16 +04:00
vfd - > fops = & fimc_capture_fops ;
vfd - > ioctl_ops = & fimc_capture_ioctl_ops ;
2011-07-27 01:08:21 +04:00
vfd - > v4l2_dev = v4l2_dev ;
2010-10-07 17:06:16 +04:00
vfd - > minor = - 1 ;
vfd - > release = video_device_release ;
2010-12-01 16:25:18 +03:00
vfd - > lock = & fimc - > lock ;
2010-10-07 17:06:16 +04:00
video_set_drvdata ( vfd , fimc ) ;
vid_cap = & fimc - > vid_cap ;
vid_cap - > vfd = vfd ;
vid_cap - > active_buf_cnt = 0 ;
vid_cap - > reqbufs_count = 0 ;
vid_cap - > refcnt = 0 ;
2010-12-08 20:05:08 +03:00
/* Default color format for image sensor */
2010-10-07 17:06:16 +04:00
vid_cap - > fmt . code = V4L2_MBUS_FMT_YUYV8_2X8 ;
INIT_LIST_HEAD ( & vid_cap - > pending_buf_q ) ;
INIT_LIST_HEAD ( & vid_cap - > active_buf_q ) ;
spin_lock_init ( & ctx - > slock ) ;
vid_cap - > ctx = ctx ;
2010-12-01 16:14:59 +03:00
q = & fimc - > vid_cap . vbq ;
memset ( q , 0 , sizeof ( * q ) ) ;
2010-12-08 20:05:08 +03:00
q - > type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ;
2010-12-01 16:14:59 +03:00
q - > io_modes = VB2_MMAP | VB2_USERPTR ;
q - > drv_priv = fimc - > vid_cap . ctx ;
q - > ops = & fimc_capture_qops ;
q - > mem_ops = & vb2_dma_contig_memops ;
q - > buf_struct_size = sizeof ( struct fimc_vid_buffer ) ;
vb2_queue_init ( q ) ;
2010-10-07 17:06:16 +04:00
2011-07-27 01:08:21 +04:00
fimc - > vid_cap . vd_pad . flags = MEDIA_PAD_FL_SINK ;
ret = media_entity_init ( & vfd - > entity , 1 , & fimc - > vid_cap . vd_pad , 0 ) ;
if ( ret )
goto err_ent ;
2011-08-25 02:28:18 +04:00
vfd - > entity . ops = & fimc_media_ops ;
2011-08-25 02:25:10 +04:00
vfd - > ctrl_handler = & ctx - > ctrl_handler ;
2010-10-07 17:06:16 +04:00
return 0 ;
2011-07-27 01:08:21 +04:00
err_ent :
2010-10-07 17:06:16 +04:00
video_device_release ( vfd ) ;
2011-06-10 22:36:48 +04:00
err_vd_alloc :
2011-06-02 13:18:34 +04:00
kfree ( ctx ) ;
2010-10-07 17:06:16 +04:00
return ret ;
}
void fimc_unregister_capture_device ( struct fimc_dev * fimc )
{
2011-07-27 01:08:21 +04:00
struct video_device * vfd = fimc - > vid_cap . vfd ;
2010-10-07 17:06:16 +04:00
2011-07-27 01:08:21 +04:00
if ( vfd ) {
media_entity_cleanup ( & vfd - > entity ) ;
2011-08-26 22:40:36 +04:00
/* Can also be called if video device was
not registered */
2011-07-27 01:08:21 +04:00
video_unregister_device ( vfd ) ;
}
kfree ( fimc - > vid_cap . ctx ) ;
2011-08-26 22:40:36 +04:00
fimc - > vid_cap . ctx = NULL ;
2010-10-07 17:06:16 +04:00
}