2009-02-23 18:13:24 +03:00
/*
* V4L2 Driver for i . MX3x camera host
*
* Copyright ( C ) 2008
* Guennadi Liakhovetski , DENX Software Engineering , < lg @ denx . de >
*
* 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/init.h>
# include <linux/module.h>
# include <linux/videodev2.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
# include <linux/vmalloc.h>
# include <linux/interrupt.h>
2009-11-09 22:11:34 +03:00
# include <linux/sched.h>
2012-09-14 06:35:54 +04:00
# include <linux/dma/ipu-dma.h>
2009-02-23 18:13:24 +03:00
# include <media/v4l2-common.h>
# include <media/v4l2-dev.h>
2011-02-21 13:14:01 +03:00
# include <media/videobuf2-dma-contig.h>
2009-02-23 18:13:24 +03:00
# include <media/soc_camera.h>
2009-12-11 17:46:49 +03:00
# include <media/soc_mediabus.h>
2009-02-23 18:13:24 +03:00
2012-08-24 17:14:29 +04:00
# include <linux/platform_data/camera-mx3.h>
# include <linux/platform_data/dma-imx.h>
2009-02-23 18:13:24 +03:00
# define MX3_CAM_DRV_NAME "mx3-camera"
/* CMOS Sensor Interface Registers */
# define CSI_REG_START 0x60
# define CSI_SENS_CONF (0x60 - CSI_REG_START)
# define CSI_SENS_FRM_SIZE (0x64 - CSI_REG_START)
# define CSI_ACT_FRM_SIZE (0x68 - CSI_REG_START)
# define CSI_OUT_FRM_CTRL (0x6C - CSI_REG_START)
# define CSI_TST_CTRL (0x70 - CSI_REG_START)
# define CSI_CCIR_CODE_1 (0x74 - CSI_REG_START)
# define CSI_CCIR_CODE_2 (0x78 - CSI_REG_START)
# define CSI_CCIR_CODE_3 (0x7C - CSI_REG_START)
# define CSI_FLASH_STROBE_1 (0x80 - CSI_REG_START)
# define CSI_FLASH_STROBE_2 (0x84 - CSI_REG_START)
# define CSI_SENS_CONF_VSYNC_POL_SHIFT 0
# define CSI_SENS_CONF_HSYNC_POL_SHIFT 1
# define CSI_SENS_CONF_DATA_POL_SHIFT 2
# define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3
# define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4
# define CSI_SENS_CONF_SENS_CLKSRC_SHIFT 7
# define CSI_SENS_CONF_DATA_FMT_SHIFT 8
# define CSI_SENS_CONF_DATA_WIDTH_SHIFT 10
# define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15
# define CSI_SENS_CONF_DIVRATIO_SHIFT 16
# define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
# define CSI_SENS_CONF_DATA_FMT_YUV422 (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
# define CSI_SENS_CONF_DATA_FMT_BAYER (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
# define MAX_VIDEO_MEM 16
struct mx3_camera_buffer {
/* common v4l buffer stuff -- must be first */
2011-02-21 13:14:01 +03:00
struct vb2_buffer vb ;
struct list_head queue ;
2009-02-23 18:13:24 +03:00
/* One descriptot per scatterlist (per frame) */
struct dma_async_tx_descriptor * txd ;
/* We have to "build" a scatterlist ourselves - one element per frame */
struct scatterlist sg ;
} ;
/**
* struct mx3_camera_dev - i . MX3x camera ( CSI ) object
* @ dev : camera device , to which the coherent buffer is attached
* @ icd : currently attached camera sensor
* @ clk : pointer to clock
* @ base : remapped register base address
* @ pdata : platform data
* @ platform_flags : platform flags
* @ mclk : master clock frequency in Hz
* @ capture : list of capture videobuffers
* @ lock : protects video buffer lists
* @ active : active video buffer
* @ idmac_channel : array of pointers to IPU DMAC DMA channels
* @ soc_host : embedded soc_host object
*/
struct mx3_camera_dev {
/*
* i . MX3x is only supposed to handle one camera on its Camera Sensor
* Interface . If anyone ever builds hardware to enable more than one
* camera _simultaneously_ , they will have to modify this driver too
*/
struct clk * clk ;
void __iomem * base ;
struct mx3_camera_pdata * pdata ;
unsigned long platform_flags ;
unsigned long mclk ;
2011-07-27 23:00:29 +04:00
u16 width_flags ; /* max 15 bits */
2009-02-23 18:13:24 +03:00
struct list_head capture ;
spinlock_t lock ; /* Protects video buffer lists */
struct mx3_camera_buffer * active ;
2011-11-03 17:14:00 +04:00
size_t buf_total ;
2011-02-21 13:14:01 +03:00
struct vb2_alloc_ctx * alloc_ctx ;
enum v4l2_field field ;
int sequence ;
2009-02-23 18:13:24 +03:00
/* IDMAC / dmaengine interface */
struct idmac_channel * idmac_channel [ 1 ] ; /* We need one channel */
struct soc_camera_host soc_host ;
} ;
struct dma_chan_request {
struct mx3_camera_dev * mx3_cam ;
enum ipu_channel id ;
} ;
static u32 csi_reg_read ( struct mx3_camera_dev * mx3 , off_t reg )
{
return __raw_readl ( mx3 - > base + reg ) ;
}
static void csi_reg_write ( struct mx3_camera_dev * mx3 , u32 value , off_t reg )
{
__raw_writel ( value , mx3 - > base + reg ) ;
}
2011-02-21 13:14:01 +03:00
static struct mx3_camera_buffer * to_mx3_vb ( struct vb2_buffer * vb )
{
return container_of ( vb , struct mx3_camera_buffer , vb ) ;
}
2009-02-23 18:13:24 +03:00
/* Called from the IPU IDMAC ISR */
static void mx3_cam_dma_done ( void * arg )
{
struct idmac_tx_desc * desc = to_tx_desc ( arg ) ;
struct dma_chan * chan = desc - > txd . chan ;
struct idmac_channel * ichannel = to_idmac_chan ( chan ) ;
struct mx3_camera_dev * mx3_cam = ichannel - > client ;
dev_dbg ( chan - > device - > dev , " callback cookie %d, active DMA 0x%08x \n " ,
desc - > txd . cookie , mx3_cam - > active ? sg_dma_address ( & mx3_cam - > active - > sg ) : 0 ) ;
spin_lock ( & mx3_cam - > lock ) ;
if ( mx3_cam - > active ) {
2011-02-21 13:14:01 +03:00
struct vb2_buffer * vb = & mx3_cam - > active - > vb ;
struct mx3_camera_buffer * buf = to_mx3_vb ( vb ) ;
list_del_init ( & buf - > queue ) ;
2012-09-15 22:14:42 +04:00
v4l2_get_timestamp ( & vb - > v4l2_buf . timestamp ) ;
2011-02-21 13:14:01 +03:00
vb - > v4l2_buf . field = mx3_cam - > field ;
vb - > v4l2_buf . sequence = mx3_cam - > sequence + + ;
vb2_buffer_done ( vb , VB2_BUF_STATE_DONE ) ;
2009-02-23 18:13:24 +03:00
}
if ( list_empty ( & mx3_cam - > capture ) ) {
mx3_cam - > active = NULL ;
spin_unlock ( & mx3_cam - > lock ) ;
/*
* stop capture - without further buffers IPU_CHA_BUF0_RDY will
* not get updated
*/
return ;
}
mx3_cam - > active = list_entry ( mx3_cam - > capture . next ,
2011-02-21 13:14:01 +03:00
struct mx3_camera_buffer , queue ) ;
2009-02-23 18:13:24 +03:00
spin_unlock ( & mx3_cam - > lock ) ;
}
/*
* Videobuf operations
*/
/*
* Calculate the __buffer__ ( not data ) size and number of buffers .
*/
2011-02-21 13:14:01 +03:00
static int mx3_videobuf_setup ( struct vb2_queue * vq ,
2011-08-24 17:30:21 +04:00
const struct v4l2_format * fmt ,
2011-02-21 13:14:01 +03:00
unsigned int * count , unsigned int * num_planes ,
2011-08-24 13:43:36 +04:00
unsigned int sizes [ ] , void * alloc_ctxs [ ] )
2009-02-23 18:13:24 +03:00
{
2011-02-21 13:14:01 +03:00
struct soc_camera_device * icd = soc_camera_from_vb2q ( vq ) ;
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2009-02-23 18:13:24 +03:00
struct mx3_camera_dev * mx3_cam = ici - > priv ;
if ( ! mx3_cam - > idmac_channel [ 0 ] )
return - EINVAL ;
2011-11-03 17:14:00 +04:00
if ( fmt ) {
const struct soc_camera_format_xlate * xlate = soc_camera_xlate_by_fourcc ( icd ,
fmt - > fmt . pix . pixelformat ) ;
2012-03-21 15:03:26 +04:00
unsigned int bytes_per_line ;
int ret ;
2012-03-21 15:03:21 +04:00
2011-11-03 17:14:00 +04:00
if ( ! xlate )
return - EINVAL ;
2012-03-21 15:03:21 +04:00
2012-03-21 15:03:26 +04:00
ret = soc_mbus_bytes_per_line ( fmt - > fmt . pix . width ,
xlate - > host_fmt ) ;
if ( ret < 0 )
return ret ;
2012-03-21 15:03:21 +04:00
2012-03-21 15:03:26 +04:00
bytes_per_line = max_t ( u32 , fmt - > fmt . pix . bytesperline , ret ) ;
ret = soc_mbus_image_size ( xlate - > host_fmt , bytes_per_line ,
fmt - > fmt . pix . height ) ;
if ( ret < 0 )
return ret ;
sizes [ 0 ] = max_t ( u32 , fmt - > fmt . pix . sizeimage , ret ) ;
2011-11-03 17:14:00 +04:00
} else {
/* Called from VIDIOC_REQBUFS or in compatibility mode */
2012-03-21 15:03:21 +04:00
sizes [ 0 ] = icd - > sizeimage ;
2011-11-03 17:14:00 +04:00
}
2009-02-23 18:13:24 +03:00
2011-11-03 17:14:00 +04:00
alloc_ctxs [ 0 ] = mx3_cam - > alloc_ctx ;
2009-02-23 18:13:24 +03:00
2011-11-03 17:14:00 +04:00
if ( ! vq - > num_buffers )
mx3_cam - > sequence = 0 ;
2009-02-23 18:13:24 +03:00
2011-11-03 17:14:00 +04:00
if ( ! * count )
* count = 2 ;
2009-02-23 18:13:24 +03:00
2011-11-03 17:14:00 +04:00
/* If *num_planes != 0, we have already verified *count. */
if ( ! * num_planes & &
sizes [ 0 ] * * count + mx3_cam - > buf_total > MAX_VIDEO_MEM * 1024 * 1024 )
* count = ( MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam - > buf_total ) /
sizes [ 0 ] ;
2009-02-23 18:13:24 +03:00
2011-11-03 17:14:00 +04:00
* num_planes = 1 ;
2009-02-23 18:13:24 +03:00
2011-02-21 13:14:01 +03:00
return 0 ;
2009-02-23 18:13:24 +03:00
}
static enum pixel_fmt fourcc_to_ipu_pix ( __u32 fourcc )
{
/* Add more formats as need arises and test possibilities appear... */
switch ( fourcc ) {
case V4L2_PIX_FMT_RGB24 :
return IPU_PIX_FMT_RGB24 ;
2011-01-17 12:52:10 +03:00
case V4L2_PIX_FMT_UYVY :
case V4L2_PIX_FMT_RGB565 :
2009-02-23 18:13:24 +03:00
default :
return IPU_PIX_FMT_GENERIC ;
}
}
2011-02-21 13:14:01 +03:00
static void mx3_videobuf_queue ( struct vb2_buffer * vb )
2009-02-23 18:13:24 +03:00
{
2011-02-21 13:14:01 +03:00
struct soc_camera_device * icd = soc_camera_from_vb2q ( vb - > vb2_queue ) ;
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2009-02-23 18:13:24 +03:00
struct mx3_camera_dev * mx3_cam = ici - > priv ;
2011-02-21 13:14:01 +03:00
struct mx3_camera_buffer * buf = to_mx3_vb ( vb ) ;
2011-11-03 17:14:00 +04:00
struct scatterlist * sg = & buf - > sg ;
struct dma_async_tx_descriptor * txd ;
struct idmac_channel * ichan = mx3_cam - > idmac_channel [ 0 ] ;
2009-02-23 18:13:24 +03:00
struct idmac_video_param * video = & ichan - > params . video ;
2011-11-03 17:14:00 +04:00
const struct soc_mbus_pixelfmt * host_fmt = icd - > current_fmt - > host_fmt ;
dma_cookie_t cookie ;
size_t new_size ;
2012-03-21 15:03:21 +04:00
new_size = icd - > sizeimage ;
2011-11-03 17:14:00 +04:00
if ( vb2_plane_size ( vb , 0 ) < new_size ) {
dev_err ( icd - > parent , " Buffer #%d too small (%lu < %zu) \n " ,
vb - > v4l2_buf . index , vb2_plane_size ( vb , 0 ) , new_size ) ;
goto error ;
}
2012-08-01 12:05:10 +04:00
if ( ! buf - > txd ) {
2011-11-03 17:14:00 +04:00
sg_dma_address ( sg ) = vb2_dma_contig_plane_dma_addr ( vb , 0 ) ;
sg_dma_len ( sg ) = new_size ;
2012-03-09 01:11:18 +04:00
txd = dmaengine_prep_slave_sg (
2011-11-17 13:24:38 +04:00
& ichan - > dma_chan , sg , 1 , DMA_DEV_TO_MEM ,
2011-11-03 17:14:00 +04:00
DMA_PREP_INTERRUPT ) ;
if ( ! txd )
goto error ;
txd - > callback_param = txd ;
txd - > callback = mx3_cam_dma_done ;
buf - > txd = txd ;
} else {
txd = buf - > txd ;
}
vb2_set_plane_payload ( vb , 0 , new_size ) ;
2009-02-23 18:13:24 +03:00
/* This is the configuration of one sg-element */
2011-11-03 17:14:00 +04:00
video - > out_pixel_fmt = fourcc_to_ipu_pix ( host_fmt - > fourcc ) ;
2011-01-17 12:52:10 +03:00
if ( video - > out_pixel_fmt = = IPU_PIX_FMT_GENERIC ) {
/*
2011-11-03 17:14:00 +04:00
* If the IPU DMA channel is configured to transfer generic
* 8 - bit data , we have to set up the geometry parameters
* correctly , according to the current pixel format . The DMA
* horizontal parameters in this case are expressed in bytes ,
* not in pixels .
2011-01-17 12:52:10 +03:00
*/
2012-03-21 15:03:22 +04:00
video - > out_width = icd - > bytesperline ;
2011-01-17 12:52:10 +03:00
video - > out_height = icd - > user_height ;
2012-03-21 15:03:22 +04:00
video - > out_stride = icd - > bytesperline ;
2011-01-17 12:52:10 +03:00
} else {
/*
* For IPU known formats the pixel unit will be managed
* successfully by the IPU code
*/
video - > out_width = icd - > user_width ;
video - > out_height = icd - > user_height ;
video - > out_stride = icd - > user_width ;
}
2009-02-23 18:13:24 +03:00
# ifdef DEBUG
/* helps to see what DMA actually has written */
2011-02-21 13:14:01 +03:00
if ( vb2_plane_vaddr ( vb , 0 ) )
memset ( vb2_plane_vaddr ( vb , 0 ) , 0xaa , vb2_get_plane_payload ( vb , 0 ) ) ;
2009-02-23 18:13:24 +03:00
# endif
2013-08-29 12:23:36 +04:00
spin_lock_irq ( & mx3_cam - > lock ) ;
2011-02-21 13:14:01 +03:00
list_add_tail ( & buf - > queue , & mx3_cam - > capture ) ;
2009-02-23 18:13:24 +03:00
2011-02-21 13:14:01 +03:00
if ( ! mx3_cam - > active )
2009-02-23 18:13:24 +03:00
mx3_cam - > active = buf ;
2009-08-06 03:06:31 +04:00
spin_unlock_irq ( & mx3_cam - > lock ) ;
2009-02-23 18:13:24 +03:00
cookie = txd - > tx_submit ( txd ) ;
2011-07-16 03:03:38 +04:00
dev_dbg ( icd - > parent , " Submitted cookie %d DMA 0x%08x \n " ,
2009-08-25 18:47:00 +04:00
cookie , sg_dma_address ( & buf - > sg ) ) ;
2009-08-06 03:06:31 +04:00
2009-02-23 18:13:24 +03:00
if ( cookie > = 0 )
return ;
2011-02-21 13:14:01 +03:00
spin_lock_irq ( & mx3_cam - > lock ) ;
2009-02-23 18:13:24 +03:00
2011-02-21 13:14:01 +03:00
/* Submit error */
list_del_init ( & buf - > queue ) ;
2009-02-23 18:13:24 +03:00
if ( mx3_cam - > active = = buf )
mx3_cam - > active = NULL ;
2011-02-21 13:14:01 +03:00
2013-08-29 12:23:36 +04:00
spin_unlock_irq ( & mx3_cam - > lock ) ;
2011-11-03 17:14:00 +04:00
error :
2011-02-21 13:14:01 +03:00
vb2_buffer_done ( vb , VB2_BUF_STATE_ERROR ) ;
2009-02-23 18:13:24 +03:00
}
2011-02-21 13:14:01 +03:00
static void mx3_videobuf_release ( struct vb2_buffer * vb )
2009-02-23 18:13:24 +03:00
{
2011-02-21 13:14:01 +03:00
struct soc_camera_device * icd = soc_camera_from_vb2q ( vb - > vb2_queue ) ;
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2009-02-23 18:13:24 +03:00
struct mx3_camera_dev * mx3_cam = ici - > priv ;
2011-02-21 13:14:01 +03:00
struct mx3_camera_buffer * buf = to_mx3_vb ( vb ) ;
struct dma_async_tx_descriptor * txd = buf - > txd ;
2009-02-23 18:13:24 +03:00
unsigned long flags ;
2011-07-16 03:03:38 +04:00
dev_dbg ( icd - > parent ,
2011-02-21 13:14:01 +03:00
" Release%s DMA 0x%08x, queue %sempty \n " ,
2009-02-23 18:13:24 +03:00
mx3_cam - > active = = buf ? " active " : " " , sg_dma_address ( & buf - > sg ) ,
2011-02-21 13:14:01 +03:00
list_empty ( & buf - > queue ) ? " " : " not " ) ;
2009-02-23 18:13:24 +03:00
spin_lock_irqsave ( & mx3_cam - > lock , flags ) ;
2011-02-21 13:14:01 +03:00
if ( mx3_cam - > active = = buf )
mx3_cam - > active = NULL ;
/* Doesn't hurt also if the list is empty */
list_del_init ( & buf - > queue ) ;
if ( txd ) {
buf - > txd = NULL ;
if ( mx3_cam - > idmac_channel [ 0 ] )
async_tx_ack ( txd ) ;
2009-02-23 18:13:24 +03:00
}
2011-02-21 13:14:01 +03:00
2009-02-23 18:13:24 +03:00
spin_unlock_irqrestore ( & mx3_cam - > lock , flags ) ;
2011-11-03 17:14:00 +04:00
mx3_cam - > buf_total - = vb2_plane_size ( vb , 0 ) ;
2009-02-23 18:13:24 +03:00
}
2011-02-21 13:14:01 +03:00
static int mx3_videobuf_init ( struct vb2_buffer * vb )
{
2011-11-03 17:14:00 +04:00
struct soc_camera_device * icd = soc_camera_from_vb2q ( vb - > vb2_queue ) ;
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
struct mx3_camera_dev * mx3_cam = ici - > priv ;
2011-02-21 13:14:01 +03:00
struct mx3_camera_buffer * buf = to_mx3_vb ( vb ) ;
2011-11-03 17:14:00 +04:00
2012-08-01 12:05:10 +04:00
if ( ! buf - > txd ) {
/* This is for locking debugging only */
INIT_LIST_HEAD ( & buf - > queue ) ;
sg_init_table ( & buf - > sg , 1 ) ;
2011-02-21 13:14:01 +03:00
2012-08-01 12:05:10 +04:00
mx3_cam - > buf_total + = vb2_plane_size ( vb , 0 ) ;
}
2011-02-21 13:14:01 +03:00
return 0 ;
}
2014-04-17 09:47:21 +04:00
static void mx3_stop_streaming ( struct vb2_queue * q )
2011-03-25 11:24:01 +03:00
{
struct soc_camera_device * icd = soc_camera_from_vb2q ( q ) ;
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2011-03-25 11:24:01 +03:00
struct mx3_camera_dev * mx3_cam = ici - > priv ;
struct idmac_channel * ichan = mx3_cam - > idmac_channel [ 0 ] ;
struct mx3_camera_buffer * buf , * tmp ;
unsigned long flags ;
2014-10-11 19:40:31 +04:00
if ( ichan )
dmaengine_pause ( & ichan - > dma_chan ) ;
2011-03-25 11:24:01 +03:00
spin_lock_irqsave ( & mx3_cam - > lock , flags ) ;
mx3_cam - > active = NULL ;
list_for_each_entry_safe ( buf , tmp , & mx3_cam - > capture , queue ) {
list_del_init ( & buf - > queue ) ;
2011-11-03 17:14:00 +04:00
vb2_buffer_done ( & buf - > vb , VB2_BUF_STATE_ERROR ) ;
2011-03-25 11:24:01 +03:00
}
spin_unlock_irqrestore ( & mx3_cam - > lock , flags ) ;
}
2011-02-21 13:14:01 +03:00
static struct vb2_ops mx3_videobuf_ops = {
. queue_setup = mx3_videobuf_setup ,
. buf_queue = mx3_videobuf_queue ,
. buf_cleanup = mx3_videobuf_release ,
. buf_init = mx3_videobuf_init ,
. wait_prepare = soc_camera_unlock ,
. wait_finish = soc_camera_lock ,
2011-03-25 11:24:01 +03:00
. stop_streaming = mx3_stop_streaming ,
2009-02-23 18:13:24 +03:00
} ;
2011-02-21 13:14:01 +03:00
static int mx3_camera_init_videobuf ( struct vb2_queue * q ,
2009-02-23 18:13:24 +03:00
struct soc_camera_device * icd )
{
2011-02-21 13:14:01 +03:00
q - > type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
q - > io_modes = VB2_MMAP | VB2_USERPTR ;
q - > drv_priv = icd ;
q - > ops = & mx3_videobuf_ops ;
q - > mem_ops = & vb2_dma_contig_memops ;
q - > buf_struct_size = sizeof ( struct mx3_camera_buffer ) ;
2014-02-26 02:12:19 +04:00
q - > timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC ;
2011-02-21 13:14:01 +03:00
return vb2_queue_init ( q ) ;
2009-02-23 18:13:24 +03:00
}
/* First part of ipu_csi_init_interface() */
2013-04-04 18:24:43 +04:00
static void mx3_camera_activate ( struct mx3_camera_dev * mx3_cam )
2009-02-23 18:13:24 +03:00
{
u32 conf ;
long rate ;
/* Set default size: ipu_csi_set_window_size() */
csi_reg_write ( mx3_cam , ( 640 - 1 ) | ( ( 480 - 1 ) < < 16 ) , CSI_ACT_FRM_SIZE ) ;
/* ...and position to 0:0: ipu_csi_set_window_pos() */
conf = csi_reg_read ( mx3_cam , CSI_OUT_FRM_CTRL ) & 0xffff0000 ;
csi_reg_write ( mx3_cam , conf , CSI_OUT_FRM_CTRL ) ;
/* We use only gated clock synchronisation mode so far */
conf = 0 < < CSI_SENS_CONF_SENS_PRTCL_SHIFT ;
/* Set generic data, platform-biggest bus-width */
conf | = CSI_SENS_CONF_DATA_FMT_BAYER ;
if ( mx3_cam - > platform_flags & MX3_CAMERA_DATAWIDTH_15 )
conf | = 3 < < CSI_SENS_CONF_DATA_WIDTH_SHIFT ;
else if ( mx3_cam - > platform_flags & MX3_CAMERA_DATAWIDTH_10 )
conf | = 2 < < CSI_SENS_CONF_DATA_WIDTH_SHIFT ;
else if ( mx3_cam - > platform_flags & MX3_CAMERA_DATAWIDTH_8 )
conf | = 1 < < CSI_SENS_CONF_DATA_WIDTH_SHIFT ;
else /* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/
conf | = 0 < < CSI_SENS_CONF_DATA_WIDTH_SHIFT ;
if ( mx3_cam - > platform_flags & MX3_CAMERA_CLK_SRC )
conf | = 1 < < CSI_SENS_CONF_SENS_CLKSRC_SHIFT ;
if ( mx3_cam - > platform_flags & MX3_CAMERA_EXT_VSYNC )
conf | = 1 < < CSI_SENS_CONF_EXT_VSYNC_SHIFT ;
if ( mx3_cam - > platform_flags & MX3_CAMERA_DP )
conf | = 1 < < CSI_SENS_CONF_DATA_POL_SHIFT ;
if ( mx3_cam - > platform_flags & MX3_CAMERA_PCP )
conf | = 1 < < CSI_SENS_CONF_PIX_CLK_POL_SHIFT ;
if ( mx3_cam - > platform_flags & MX3_CAMERA_HSP )
conf | = 1 < < CSI_SENS_CONF_HSYNC_POL_SHIFT ;
if ( mx3_cam - > platform_flags & MX3_CAMERA_VSP )
conf | = 1 < < CSI_SENS_CONF_VSYNC_POL_SHIFT ;
/* ipu_csi_init_interface() */
csi_reg_write ( mx3_cam , conf , CSI_SENS_CONF ) ;
2012-04-03 14:35:16 +04:00
clk_prepare_enable ( mx3_cam - > clk ) ;
2009-02-23 18:13:24 +03:00
rate = clk_round_rate ( mx3_cam - > clk , mx3_cam - > mclk ) ;
2013-04-04 18:24:43 +04:00
dev_dbg ( mx3_cam - > soc_host . v4l2_dev . dev , " Set SENS_CONF to %x, rate %ld \n " , conf , rate ) ;
2009-02-23 18:13:24 +03:00
if ( rate )
clk_set_rate ( mx3_cam - > clk , rate ) ;
}
static int mx3_camera_add_device ( struct soc_camera_device * icd )
{
2013-04-04 18:24:43 +04:00
dev_info ( icd - > parent , " MX3 Camera driver attached to camera %d \n " ,
icd - > devnum ) ;
return 0 ;
}
static void mx3_camera_remove_device ( struct soc_camera_device * icd )
{
dev_info ( icd - > parent , " MX3 Camera driver detached from camera %d \n " ,
icd - > devnum ) ;
}
/* Called with .host_lock held */
static int mx3_camera_clock_start ( struct soc_camera_host * ici )
{
2009-02-23 18:13:24 +03:00
struct mx3_camera_dev * mx3_cam = ici - > priv ;
2013-04-04 18:24:43 +04:00
mx3_camera_activate ( mx3_cam ) ;
2009-02-23 18:13:24 +03:00
2011-11-03 17:14:00 +04:00
mx3_cam - > buf_total = 0 ;
2009-02-23 18:13:24 +03:00
2009-08-25 18:28:22 +04:00
return 0 ;
2009-02-23 18:13:24 +03:00
}
2012-12-24 16:31:33 +04:00
/* Called with .host_lock held */
2013-04-04 18:24:43 +04:00
static void mx3_camera_clock_stop ( struct soc_camera_host * ici )
2009-02-23 18:13:24 +03:00
{
struct mx3_camera_dev * mx3_cam = ici - > priv ;
struct idmac_channel * * ichan = & mx3_cam - > idmac_channel [ 0 ] ;
if ( * ichan ) {
dma_release_channel ( & ( * ichan ) - > dma_chan ) ;
* ichan = NULL ;
}
2012-04-03 14:35:16 +04:00
clk_disable_unprepare ( mx3_cam - > clk ) ;
2009-02-23 18:13:24 +03:00
}
static int test_platform_param ( struct mx3_camera_dev * mx3_cam ,
unsigned char buswidth , unsigned long * flags )
{
2011-07-27 23:00:29 +04:00
/*
* If requested data width is supported by the platform , use it or any
* possible lower value - i . MX31 is smart enough to shift bits
*/
if ( buswidth > fls ( mx3_cam - > width_flags ) )
return - EINVAL ;
2009-02-23 18:13:24 +03:00
/*
* Platform specified synchronization and pixel clock polarities are
* only a recommendation and are only used during probing . MX3x
* camera interface only works in master mode , i . e . , uses HSYNC and
* VSYNC signals from the sensor
*/
2011-07-27 23:00:29 +04:00
* flags = V4L2_MBUS_MASTER |
V4L2_MBUS_HSYNC_ACTIVE_HIGH |
V4L2_MBUS_HSYNC_ACTIVE_LOW |
V4L2_MBUS_VSYNC_ACTIVE_HIGH |
V4L2_MBUS_VSYNC_ACTIVE_LOW |
V4L2_MBUS_PCLK_SAMPLE_RISING |
V4L2_MBUS_PCLK_SAMPLE_FALLING |
V4L2_MBUS_DATA_ACTIVE_HIGH |
V4L2_MBUS_DATA_ACTIVE_LOW ;
2009-02-23 18:13:24 +03:00
return 0 ;
}
static int mx3_camera_try_bus_param ( struct soc_camera_device * icd ,
const unsigned int depth )
{
2011-07-27 23:00:29 +04:00
struct v4l2_subdev * sd = soc_camera_to_subdev ( icd ) ;
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2009-02-23 18:13:24 +03:00
struct mx3_camera_dev * mx3_cam = ici - > priv ;
2011-07-27 23:00:29 +04:00
struct v4l2_mbus_config cfg = { . type = V4L2_MBUS_PARALLEL , } ;
unsigned long bus_flags , common_flags ;
2009-02-23 18:13:24 +03:00
int ret = test_platform_param ( mx3_cam , depth , & bus_flags ) ;
2011-07-16 03:03:38 +04:00
dev_dbg ( icd - > parent , " request bus width %d bit: %d \n " , depth , ret ) ;
2009-02-23 18:13:24 +03:00
if ( ret < 0 )
return ret ;
2011-07-27 23:00:29 +04:00
ret = v4l2_subdev_call ( sd , video , g_mbus_config , & cfg ) ;
if ( ! ret ) {
common_flags = soc_mbus_config_compatible ( & cfg ,
bus_flags ) ;
if ( ! common_flags ) {
dev_warn ( icd - > parent ,
" Flags incompatible: camera 0x%x, host 0x%lx \n " ,
cfg . flags , bus_flags ) ;
return - EINVAL ;
}
} else if ( ret ! = - ENOIOCTLCMD ) {
return ret ;
}
2009-02-23 18:13:24 +03:00
2011-07-27 23:00:29 +04:00
return 0 ;
2009-02-23 18:13:24 +03:00
}
static bool chan_filter ( struct dma_chan * chan , void * arg )
{
struct dma_chan_request * rq = arg ;
struct mx3_camera_pdata * pdata ;
2010-11-05 12:26:03 +03:00
if ( ! imx_dma_is_ipu ( chan ) )
return false ;
2009-02-23 18:13:24 +03:00
if ( ! rq )
return false ;
2009-08-25 18:43:33 +04:00
pdata = rq - > mx3_cam - > soc_host . v4l2_dev . dev - > platform_data ;
2009-02-23 18:13:24 +03:00
return rq - > id = = chan - > chan_id & &
pdata - > dma_dev = = chan - > device - > dev ;
}
2009-12-11 17:46:49 +03:00
static const struct soc_mbus_pixelfmt mx3_camera_formats [ ] = {
2009-02-23 18:13:24 +03:00
{
2009-12-11 17:46:49 +03:00
. fourcc = V4L2_PIX_FMT_SBGGR8 ,
. name = " Bayer BGGR (sRGB) 8 bit " ,
. bits_per_sample = 8 ,
. packing = SOC_MBUS_PACKING_NONE ,
. order = SOC_MBUS_ORDER_LE ,
2012-03-21 15:03:23 +04:00
. layout = SOC_MBUS_LAYOUT_PACKED ,
2009-02-23 18:13:24 +03:00
} , {
2009-12-11 17:46:49 +03:00
. fourcc = V4L2_PIX_FMT_GREY ,
. name = " Monochrome 8 bit " ,
. bits_per_sample = 8 ,
. packing = SOC_MBUS_PACKING_NONE ,
. order = SOC_MBUS_ORDER_LE ,
2012-03-21 15:03:23 +04:00
. layout = SOC_MBUS_LAYOUT_PACKED ,
2009-02-23 18:13:24 +03:00
} ,
} ;
2009-12-11 17:46:49 +03:00
/* This will be corrected as we get more formats */
static bool mx3_camera_packing_supported ( const struct soc_mbus_pixelfmt * fmt )
2009-02-23 18:13:24 +03:00
{
2009-12-11 17:46:49 +03:00
return fmt - > packing = = SOC_MBUS_PACKING_NONE | |
( fmt - > bits_per_sample = = 8 & &
fmt - > packing = = SOC_MBUS_PACKING_2X8_PADHI ) | |
( fmt - > bits_per_sample > 8 & &
fmt - > packing = = SOC_MBUS_PACKING_EXTEND16 ) ;
2009-02-23 18:13:24 +03:00
}
2010-05-09 00:55:00 +04:00
static int mx3_camera_get_formats ( struct soc_camera_device * icd , unsigned int idx ,
2009-02-23 18:13:24 +03:00
struct soc_camera_format_xlate * xlate )
{
2009-12-11 17:46:49 +03:00
struct v4l2_subdev * sd = soc_camera_to_subdev ( icd ) ;
2011-07-16 03:03:38 +04:00
struct device * dev = icd - > parent ;
2009-12-11 17:46:49 +03:00
int formats = 0 , ret ;
2014-11-10 20:28:31 +03:00
u32 code ;
2009-12-11 17:46:49 +03:00
const struct soc_mbus_pixelfmt * fmt ;
2009-02-23 18:13:24 +03:00
2009-12-11 17:46:49 +03:00
ret = v4l2_subdev_call ( sd , video , enum_mbus_fmt , idx , & code ) ;
if ( ret < 0 )
/* No more formats */
return 0 ;
2009-02-23 18:13:24 +03:00
2009-12-11 17:46:49 +03:00
fmt = soc_mbus_get_fmtdesc ( code ) ;
if ( ! fmt ) {
2011-07-16 03:03:38 +04:00
dev_warn ( icd - > parent ,
2013-07-30 09:53:15 +04:00
" Unsupported format code #%u: 0x%x \n " , idx , code ) ;
2009-02-23 18:13:24 +03:00
return 0 ;
2009-12-11 17:46:49 +03:00
}
2009-02-23 18:13:24 +03:00
2009-12-11 17:46:49 +03:00
/* This also checks support for the requested bits-per-sample */
ret = mx3_camera_try_bus_param ( icd , fmt - > bits_per_sample ) ;
2009-02-23 18:13:24 +03:00
if ( ret < 0 )
return 0 ;
2009-12-11 17:46:49 +03:00
switch ( code ) {
2014-11-10 20:28:31 +03:00
case MEDIA_BUS_FMT_SBGGR10_1X10 :
2009-02-23 18:13:24 +03:00
formats + + ;
if ( xlate ) {
2009-12-11 17:46:49 +03:00
xlate - > host_fmt = & mx3_camera_formats [ 0 ] ;
xlate - > code = code ;
2009-02-23 18:13:24 +03:00
xlate + + ;
2013-07-30 09:53:15 +04:00
dev_dbg ( dev , " Providing format %s using code 0x%x \n " ,
2009-12-11 17:46:49 +03:00
mx3_camera_formats [ 0 ] . name , code ) ;
2009-02-23 18:13:24 +03:00
}
2009-12-11 17:46:49 +03:00
break ;
2014-11-10 20:28:31 +03:00
case MEDIA_BUS_FMT_Y10_1X10 :
2009-02-23 18:13:24 +03:00
formats + + ;
if ( xlate ) {
2009-12-11 17:46:49 +03:00
xlate - > host_fmt = & mx3_camera_formats [ 1 ] ;
xlate - > code = code ;
2009-02-23 18:13:24 +03:00
xlate + + ;
2013-07-30 09:53:15 +04:00
dev_dbg ( dev , " Providing format %s using code 0x%x \n " ,
2009-12-11 17:46:49 +03:00
mx3_camera_formats [ 1 ] . name , code ) ;
2009-02-23 18:13:24 +03:00
}
2009-12-11 17:46:49 +03:00
break ;
2009-02-23 18:13:24 +03:00
default :
2009-12-11 17:46:49 +03:00
if ( ! mx3_camera_packing_supported ( fmt ) )
return 0 ;
}
/* Generic pass-through */
formats + + ;
if ( xlate ) {
xlate - > host_fmt = fmt ;
xlate - > code = code ;
2011-01-17 12:52:10 +03:00
dev_dbg ( dev , " Providing format %c%c%c%c in pass-through mode \n " ,
( fmt - > fourcc > > ( 0 * 8 ) ) & 0xFF ,
( fmt - > fourcc > > ( 1 * 8 ) ) & 0xFF ,
( fmt - > fourcc > > ( 2 * 8 ) ) & 0xFF ,
( fmt - > fourcc > > ( 3 * 8 ) ) & 0xFF ) ;
2009-12-11 17:46:49 +03:00
xlate + + ;
2009-02-23 18:13:24 +03:00
}
return formats ;
}
2009-03-13 12:08:20 +03:00
static void configure_geometry ( struct mx3_camera_dev * mx3_cam ,
2011-01-17 12:52:10 +03:00
unsigned int width , unsigned int height ,
2011-05-18 13:49:54 +04:00
const struct soc_mbus_pixelfmt * fmt )
2009-02-23 18:13:24 +03:00
{
u32 ctrl , width_field , height_field ;
2011-01-17 12:52:10 +03:00
if ( fourcc_to_ipu_pix ( fmt - > fourcc ) = = IPU_PIX_FMT_GENERIC ) {
/*
* As the CSI will be configured to output BAYER , here
* the width parameter count the number of samples to
* capture to complete the whole image width .
*/
2011-05-20 11:25:09 +04:00
unsigned int num , den ;
int ret = soc_mbus_samples_per_pixel ( fmt , & num , & den ) ;
BUG_ON ( ret < 0 ) ;
width = width * num / den ;
2011-01-17 12:52:10 +03:00
}
2009-02-23 18:13:24 +03:00
/* Setup frame size - this cannot be changed on-the-fly... */
2009-08-25 18:50:46 +04:00
width_field = width - 1 ;
height_field = height - 1 ;
2009-02-23 18:13:24 +03:00
csi_reg_write ( mx3_cam , width_field | ( height_field < < 16 ) , CSI_SENS_FRM_SIZE ) ;
csi_reg_write ( mx3_cam , width_field < < 16 , CSI_FLASH_STROBE_1 ) ;
csi_reg_write ( mx3_cam , ( height_field < < 16 ) | 0x22 , CSI_FLASH_STROBE_2 ) ;
csi_reg_write ( mx3_cam , width_field | ( height_field < < 16 ) , CSI_ACT_FRM_SIZE ) ;
/* ...and position */
ctrl = csi_reg_read ( mx3_cam , CSI_OUT_FRM_CTRL ) & 0xffff0000 ;
/* Sensor does the cropping */
csi_reg_write ( mx3_cam , ctrl | 0 | ( 0 < < 8 ) , CSI_OUT_FRM_CTRL ) ;
2009-03-13 12:08:20 +03:00
}
static int acquire_dma_channel ( struct mx3_camera_dev * mx3_cam )
{
dma_cap_mask_t mask ;
struct dma_chan * chan ;
struct idmac_channel * * ichan = & mx3_cam - > idmac_channel [ 0 ] ;
/* We have to use IDMAC_IC_7 for Bayer / generic data */
struct dma_chan_request rq = { . mx3_cam = mx3_cam ,
. id = IDMAC_IC_7 } ;
dma_cap_zero ( mask ) ;
dma_cap_set ( DMA_SLAVE , mask ) ;
dma_cap_set ( DMA_PRIVATE , mask ) ;
chan = dma_request_channel ( mask , chan_filter , & rq ) ;
if ( ! chan )
return - EBUSY ;
* ichan = to_idmac_chan ( chan ) ;
( * ichan ) - > client = mx3_cam ;
return 0 ;
}
2009-08-25 18:50:46 +04:00
/*
* FIXME : learn to use stride ! = width , then we can keep stride properly aligned
* and support arbitrary ( even ) widths .
*/
2010-02-24 23:13:29 +03:00
static inline void stride_align ( __u32 * width )
2009-08-25 18:50:46 +04:00
{
2011-05-18 13:49:54 +04:00
if ( ALIGN ( * width , 8 ) < 4096 )
* width = ALIGN ( * width , 8 ) ;
2009-08-25 18:50:46 +04:00
else
* width = * width & ~ 7 ;
}
/*
* As long as we don ' t implement host - side cropping and scaling , we can use
* default g_crop and cropcap from soc_camera . c
*/
2009-03-13 12:08:20 +03:00
static int mx3_camera_set_crop ( struct soc_camera_device * icd ,
2012-09-28 14:39:06 +04:00
const struct v4l2_crop * a )
2009-03-13 12:08:20 +03:00
{
2012-09-28 14:39:06 +04:00
struct v4l2_crop a_writable = * a ;
struct v4l2_rect * rect = & a_writable . c ;
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2009-03-13 12:08:20 +03:00
struct mx3_camera_dev * mx3_cam = ici - > priv ;
2009-08-25 18:46:59 +04:00
struct v4l2_subdev * sd = soc_camera_to_subdev ( icd ) ;
2009-12-11 17:46:49 +03:00
struct v4l2_mbus_framefmt mf ;
2009-08-25 18:50:46 +04:00
int ret ;
2009-03-13 12:08:20 +03:00
2009-08-25 18:50:46 +04:00
soc_camera_limit_side ( & rect - > left , & rect - > width , 0 , 2 , 4096 ) ;
soc_camera_limit_side ( & rect - > top , & rect - > height , 0 , 2 , 4096 ) ;
ret = v4l2_subdev_call ( sd , video , s_crop , a ) ;
if ( ret < 0 )
return ret ;
2011-05-18 13:49:54 +04:00
/* The capture device might have changed its output sizes */
2009-12-11 17:46:49 +03:00
ret = v4l2_subdev_call ( sd , video , g_mbus_fmt , & mf ) ;
2009-08-25 18:50:46 +04:00
if ( ret < 0 )
return ret ;
2011-05-18 13:49:54 +04:00
if ( mf . code ! = icd - > current_fmt - > code )
return - EINVAL ;
2009-12-11 17:46:49 +03:00
if ( mf . width & 7 ) {
2009-08-25 18:50:46 +04:00
/* Ouch! We can only handle 8-byte aligned width... */
2009-12-11 17:46:49 +03:00
stride_align ( & mf . width ) ;
ret = v4l2_subdev_call ( sd , video , s_mbus_fmt , & mf ) ;
2009-03-13 12:08:20 +03:00
if ( ret < 0 )
return ret ;
}
2011-02-21 13:14:01 +03:00
if ( mf . width ! = icd - > user_width | | mf . height ! = icd - > user_height )
2011-05-18 13:49:54 +04:00
configure_geometry ( mx3_cam , mf . width , mf . height ,
icd - > current_fmt - > host_fmt ) ;
2009-08-25 18:50:46 +04:00
2011-07-16 03:03:38 +04:00
dev_dbg ( icd - > parent , " Sensor cropped %dx%d \n " ,
2009-12-11 17:46:49 +03:00
mf . width , mf . height ) ;
2009-08-25 18:50:46 +04:00
2009-12-11 17:46:49 +03:00
icd - > user_width = mf . width ;
icd - > user_height = mf . height ;
2009-08-25 18:50:46 +04:00
return ret ;
2009-03-13 12:08:20 +03:00
}
static int mx3_camera_set_fmt ( struct soc_camera_device * icd ,
struct v4l2_format * f )
{
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2009-03-13 12:08:20 +03:00
struct mx3_camera_dev * mx3_cam = ici - > priv ;
2009-08-25 18:46:59 +04:00
struct v4l2_subdev * sd = soc_camera_to_subdev ( icd ) ;
2009-03-13 12:08:20 +03:00
const struct soc_camera_format_xlate * xlate ;
struct v4l2_pix_format * pix = & f - > fmt . pix ;
2009-12-11 17:46:49 +03:00
struct v4l2_mbus_framefmt mf ;
2009-03-13 12:08:20 +03:00
int ret ;
xlate = soc_camera_xlate_by_fourcc ( icd , pix - > pixelformat ) ;
if ( ! xlate ) {
2011-07-16 03:03:38 +04:00
dev_warn ( icd - > parent , " Format %x not found \n " ,
2009-08-25 18:47:00 +04:00
pix - > pixelformat ) ;
2009-03-13 12:08:20 +03:00
return - EINVAL ;
}
2009-08-25 18:50:46 +04:00
stride_align ( & pix - > width ) ;
2011-07-16 03:03:38 +04:00
dev_dbg ( icd - > parent , " Set format %dx%d \n " , pix - > width , pix - > height ) ;
2009-08-25 18:50:46 +04:00
2009-03-13 12:08:20 +03:00
/*
* Might have to perform a complete interface initialisation like in
* ipu_csi_init_interface ( ) in mxc_v4l2_s_param ( ) . Also consider
* mxc_v4l2_s_fmt ( )
*/
2011-05-18 13:49:54 +04:00
configure_geometry ( mx3_cam , pix - > width , pix - > height , xlate - > host_fmt ) ;
2009-02-23 18:13:24 +03:00
2009-12-11 17:46:49 +03:00
mf . width = pix - > width ;
mf . height = pix - > height ;
mf . field = pix - > field ;
mf . colorspace = pix - > colorspace ;
mf . code = xlate - > code ;
ret = v4l2_subdev_call ( sd , video , s_mbus_fmt , & mf ) ;
if ( ret < 0 )
return ret ;
if ( mf . code ! = xlate - > code )
return - EINVAL ;
2011-02-21 13:14:01 +03:00
if ( ! mx3_cam - > idmac_channel [ 0 ] ) {
ret = acquire_dma_channel ( mx3_cam ) ;
if ( ret < 0 )
return ret ;
}
2009-12-11 17:46:49 +03:00
pix - > width = mf . width ;
pix - > height = mf . height ;
pix - > field = mf . field ;
2011-02-21 13:14:01 +03:00
mx3_cam - > field = mf . field ;
2009-12-11 17:46:49 +03:00
pix - > colorspace = mf . colorspace ;
icd - > current_fmt = xlate ;
2009-02-23 18:13:24 +03:00
2011-07-16 03:03:38 +04:00
dev_dbg ( icd - > parent , " Sensor set %dx%d \n " , pix - > width , pix - > height ) ;
2009-08-25 18:50:46 +04:00
2009-02-23 18:13:24 +03:00
return ret ;
}
static int mx3_camera_try_fmt ( struct soc_camera_device * icd ,
struct v4l2_format * f )
{
2009-08-25 18:46:59 +04:00
struct v4l2_subdev * sd = soc_camera_to_subdev ( icd ) ;
2009-02-23 18:13:24 +03:00
const struct soc_camera_format_xlate * xlate ;
struct v4l2_pix_format * pix = & f - > fmt . pix ;
2009-12-11 17:46:49 +03:00
struct v4l2_mbus_framefmt mf ;
2009-02-23 18:13:24 +03:00
__u32 pixfmt = pix - > pixelformat ;
int ret ;
xlate = soc_camera_xlate_by_fourcc ( icd , pixfmt ) ;
if ( pixfmt & & ! xlate ) {
2011-07-16 03:03:38 +04:00
dev_warn ( icd - > parent , " Format %x not found \n " , pixfmt ) ;
2009-02-23 18:13:24 +03:00
return - EINVAL ;
}
/* limit to MX3 hardware capabilities */
if ( pix - > height > 4096 )
pix - > height = 4096 ;
if ( pix - > width > 4096 )
pix - > width = 4096 ;
/* limit to sensor capabilities */
2009-12-11 17:46:49 +03:00
mf . width = pix - > width ;
mf . height = pix - > height ;
mf . field = pix - > field ;
mf . colorspace = pix - > colorspace ;
mf . code = xlate - > code ;
2009-02-23 18:13:24 +03:00
2009-12-11 17:46:49 +03:00
ret = v4l2_subdev_call ( sd , video , try_mbus_fmt , & mf ) ;
if ( ret < 0 )
return ret ;
2009-02-23 18:13:24 +03:00
2009-12-11 17:46:49 +03:00
pix - > width = mf . width ;
pix - > height = mf . height ;
pix - > colorspace = mf . colorspace ;
switch ( mf . field ) {
case V4L2_FIELD_ANY :
2009-02-23 18:13:24 +03:00
pix - > field = V4L2_FIELD_NONE ;
2009-12-11 17:46:49 +03:00
break ;
case V4L2_FIELD_NONE :
break ;
default :
2011-07-16 03:03:38 +04:00
dev_err ( icd - > parent , " Field type %d unsupported. \n " ,
2009-12-11 17:46:49 +03:00
mf . field ) ;
ret = - EINVAL ;
2009-02-23 18:13:24 +03:00
}
return ret ;
}
2010-08-17 21:29:51 +04:00
static int mx3_camera_reqbufs ( struct soc_camera_device * icd ,
2009-02-23 18:13:24 +03:00
struct v4l2_requestbuffers * p )
{
return 0 ;
}
static unsigned int mx3_camera_poll ( struct file * file , poll_table * pt )
{
2010-08-17 21:29:51 +04:00
struct soc_camera_device * icd = file - > private_data ;
2009-02-23 18:13:24 +03:00
2011-02-21 13:14:01 +03:00
return vb2_poll ( & icd - > vb2_vidq , file , pt ) ;
2009-02-23 18:13:24 +03:00
}
static int mx3_camera_querycap ( struct soc_camera_host * ici ,
struct v4l2_capability * cap )
{
/* cap->name is set by the firendly caller:-> */
strlcpy ( cap - > card , " i.MX3x Camera " , sizeof ( cap - > card ) ) ;
2015-01-18 22:30:11 +03:00
cap - > device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING ;
cap - > capabilities = cap - > device_caps | V4L2_CAP_DEVICE_CAPS ;
2009-02-23 18:13:24 +03:00
return 0 ;
}
2011-09-22 00:52:51 +04:00
static int mx3_camera_set_bus_param ( struct soc_camera_device * icd )
2009-02-23 18:13:24 +03:00
{
2011-07-27 23:00:29 +04:00
struct v4l2_subdev * sd = soc_camera_to_subdev ( icd ) ;
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2009-02-23 18:13:24 +03:00
struct mx3_camera_dev * mx3_cam = ici - > priv ;
2011-07-27 23:00:29 +04:00
struct v4l2_mbus_config cfg = { . type = V4L2_MBUS_PARALLEL , } ;
2011-09-22 00:52:51 +04:00
u32 pixfmt = icd - > current_fmt - > host_fmt - > fourcc ;
2011-07-27 23:00:29 +04:00
unsigned long bus_flags , common_flags ;
2009-02-23 18:13:24 +03:00
u32 dw , sens_conf ;
2009-12-11 17:46:49 +03:00
const struct soc_mbus_pixelfmt * fmt ;
int buswidth ;
int ret ;
2009-02-23 18:13:24 +03:00
const struct soc_camera_format_xlate * xlate ;
2011-07-16 03:03:38 +04:00
struct device * dev = icd - > parent ;
2009-02-23 18:13:24 +03:00
2009-12-11 17:46:49 +03:00
fmt = soc_mbus_get_fmtdesc ( icd - > current_fmt - > code ) ;
if ( ! fmt )
return - EINVAL ;
2009-02-23 18:13:24 +03:00
xlate = soc_camera_xlate_by_fourcc ( icd , pixfmt ) ;
if ( ! xlate ) {
2009-08-25 18:47:00 +04:00
dev_warn ( dev , " Format %x not found \n " , pixfmt ) ;
2009-02-23 18:13:24 +03:00
return - EINVAL ;
}
2011-07-27 23:00:29 +04:00
buswidth = fmt - > bits_per_sample ;
ret = test_platform_param ( mx3_cam , buswidth , & bus_flags ) ;
2009-12-11 17:46:49 +03:00
dev_dbg ( dev , " requested bus width %d bit: %d \n " , buswidth , ret ) ;
2009-02-23 18:13:24 +03:00
if ( ret < 0 )
return ret ;
2011-07-27 23:00:29 +04:00
ret = v4l2_subdev_call ( sd , video , g_mbus_config , & cfg ) ;
if ( ! ret ) {
common_flags = soc_mbus_config_compatible ( & cfg ,
bus_flags ) ;
if ( ! common_flags ) {
dev_warn ( icd - > parent ,
" Flags incompatible: camera 0x%x, host 0x%lx \n " ,
cfg . flags , bus_flags ) ;
return - EINVAL ;
}
} else if ( ret ! = - ENOIOCTLCMD ) {
return ret ;
} else {
common_flags = bus_flags ;
2009-02-23 18:13:24 +03:00
}
2011-07-27 23:00:29 +04:00
dev_dbg ( dev , " Flags cam: 0x%x host: 0x%lx common: 0x%lx \n " ,
cfg . flags , bus_flags , common_flags ) ;
2009-02-23 18:13:24 +03:00
/* Make choices, based on platform preferences */
2011-07-27 23:00:29 +04:00
if ( ( common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH ) & &
( common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ) ) {
2009-02-23 18:13:24 +03:00
if ( mx3_cam - > platform_flags & MX3_CAMERA_HSP )
2011-07-27 23:00:29 +04:00
common_flags & = ~ V4L2_MBUS_HSYNC_ACTIVE_HIGH ;
2009-02-23 18:13:24 +03:00
else
2011-07-27 23:00:29 +04:00
common_flags & = ~ V4L2_MBUS_HSYNC_ACTIVE_LOW ;
2009-02-23 18:13:24 +03:00
}
2011-07-27 23:00:29 +04:00
if ( ( common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH ) & &
( common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ) ) {
2009-02-23 18:13:24 +03:00
if ( mx3_cam - > platform_flags & MX3_CAMERA_VSP )
2011-07-27 23:00:29 +04:00
common_flags & = ~ V4L2_MBUS_VSYNC_ACTIVE_HIGH ;
2009-02-23 18:13:24 +03:00
else
2011-07-27 23:00:29 +04:00
common_flags & = ~ V4L2_MBUS_VSYNC_ACTIVE_LOW ;
2009-02-23 18:13:24 +03:00
}
2011-07-27 23:00:29 +04:00
if ( ( common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH ) & &
( common_flags & V4L2_MBUS_DATA_ACTIVE_LOW ) ) {
2009-02-23 18:13:24 +03:00
if ( mx3_cam - > platform_flags & MX3_CAMERA_DP )
2011-07-27 23:00:29 +04:00
common_flags & = ~ V4L2_MBUS_DATA_ACTIVE_HIGH ;
2009-02-23 18:13:24 +03:00
else
2011-07-27 23:00:29 +04:00
common_flags & = ~ V4L2_MBUS_DATA_ACTIVE_LOW ;
2009-02-23 18:13:24 +03:00
}
2011-07-27 23:00:29 +04:00
if ( ( common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING ) & &
( common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING ) ) {
2009-02-23 18:13:24 +03:00
if ( mx3_cam - > platform_flags & MX3_CAMERA_PCP )
2011-07-27 23:00:29 +04:00
common_flags & = ~ V4L2_MBUS_PCLK_SAMPLE_RISING ;
2009-02-23 18:13:24 +03:00
else
2011-07-27 23:00:29 +04:00
common_flags & = ~ V4L2_MBUS_PCLK_SAMPLE_FALLING ;
2009-02-23 18:13:24 +03:00
}
2011-07-27 23:00:29 +04:00
cfg . flags = common_flags ;
ret = v4l2_subdev_call ( sd , video , s_mbus_config , & cfg ) ;
if ( ret < 0 & & ret ! = - ENOIOCTLCMD ) {
dev_dbg ( dev , " camera s_mbus_config(0x%lx) returned %d \n " ,
2009-08-25 18:28:22 +04:00
common_flags , ret ) ;
2009-02-23 18:13:24 +03:00
return ret ;
2009-08-25 18:28:22 +04:00
}
2009-02-23 18:13:24 +03:00
/*
* So far only gated clock mode is supported . Add a line
* ( 3 < < CSI_SENS_CONF_SENS_PRTCL_SHIFT ) |
* below and select the required mode when supporting other
* synchronisation protocols .
*/
sens_conf = csi_reg_read ( mx3_cam , CSI_SENS_CONF ) &
~ ( ( 1 < < CSI_SENS_CONF_VSYNC_POL_SHIFT ) |
( 1 < < CSI_SENS_CONF_HSYNC_POL_SHIFT ) |
( 1 < < CSI_SENS_CONF_DATA_POL_SHIFT ) |
( 1 < < CSI_SENS_CONF_PIX_CLK_POL_SHIFT ) |
( 3 < < CSI_SENS_CONF_DATA_FMT_SHIFT ) |
( 3 < < CSI_SENS_CONF_DATA_WIDTH_SHIFT ) ) ;
/* TODO: Support RGB and YUV formats */
/* This has been set in mx3_camera_activate(), but we clear it above */
sens_conf | = CSI_SENS_CONF_DATA_FMT_BAYER ;
2011-07-27 23:00:29 +04:00
if ( common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING )
2009-02-23 18:13:24 +03:00
sens_conf | = 1 < < CSI_SENS_CONF_PIX_CLK_POL_SHIFT ;
2011-07-27 23:00:29 +04:00
if ( common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW )
2009-02-23 18:13:24 +03:00
sens_conf | = 1 < < CSI_SENS_CONF_HSYNC_POL_SHIFT ;
2011-07-27 23:00:29 +04:00
if ( common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW )
2009-02-23 18:13:24 +03:00
sens_conf | = 1 < < CSI_SENS_CONF_VSYNC_POL_SHIFT ;
2011-07-27 23:00:29 +04:00
if ( common_flags & V4L2_MBUS_DATA_ACTIVE_LOW )
2009-02-23 18:13:24 +03:00
sens_conf | = 1 < < CSI_SENS_CONF_DATA_POL_SHIFT ;
/* Just do what we're asked to do */
2009-12-11 17:46:49 +03:00
switch ( xlate - > host_fmt - > bits_per_sample ) {
2009-02-23 18:13:24 +03:00
case 4 :
dw = 0 < < CSI_SENS_CONF_DATA_WIDTH_SHIFT ;
break ;
case 8 :
dw = 1 < < CSI_SENS_CONF_DATA_WIDTH_SHIFT ;
break ;
case 10 :
dw = 2 < < CSI_SENS_CONF_DATA_WIDTH_SHIFT ;
break ;
default :
/*
* Actually it can only be 15 now , default is just to silence
* compiler warnings
*/
case 15 :
dw = 3 < < CSI_SENS_CONF_DATA_WIDTH_SHIFT ;
}
csi_reg_write ( mx3_cam , sens_conf | dw , CSI_SENS_CONF ) ;
2009-08-25 18:47:00 +04:00
dev_dbg ( dev , " Set SENS_CONF to %x \n " , sens_conf | dw ) ;
2009-02-23 18:13:24 +03:00
return 0 ;
}
static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
. owner = THIS_MODULE ,
. add = mx3_camera_add_device ,
. remove = mx3_camera_remove_device ,
2013-04-04 18:24:43 +04:00
. clock_start = mx3_camera_clock_start ,
. clock_stop = mx3_camera_clock_stop ,
2009-03-13 12:08:20 +03:00
. set_crop = mx3_camera_set_crop ,
2009-02-23 18:13:24 +03:00
. set_fmt = mx3_camera_set_fmt ,
. try_fmt = mx3_camera_try_fmt ,
. get_formats = mx3_camera_get_formats ,
2011-02-21 13:14:01 +03:00
. init_videobuf2 = mx3_camera_init_videobuf ,
2009-02-23 18:13:24 +03:00
. reqbufs = mx3_camera_reqbufs ,
. poll = mx3_camera_poll ,
. querycap = mx3_camera_querycap ,
. set_bus_param = mx3_camera_set_bus_param ,
} ;
2012-12-22 01:17:53 +04:00
static int mx3_camera_probe ( struct platform_device * pdev )
2009-02-23 18:13:24 +03:00
{
2013-07-30 09:59:49 +04:00
struct mx3_camera_pdata * pdata = pdev - > dev . platform_data ;
2009-02-23 18:13:24 +03:00
struct mx3_camera_dev * mx3_cam ;
struct resource * res ;
void __iomem * base ;
int err = 0 ;
struct soc_camera_host * soc_host ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-07-29 19:18:04 +04:00
base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
2009-02-23 18:13:24 +03:00
2013-07-30 09:59:49 +04:00
if ( ! pdata )
return - EINVAL ;
2013-07-29 19:18:04 +04:00
mx3_cam = devm_kzalloc ( & pdev - > dev , sizeof ( * mx3_cam ) , GFP_KERNEL ) ;
2009-02-23 18:13:24 +03:00
if ( ! mx3_cam ) {
dev_err ( & pdev - > dev , " Could not allocate mx3 camera object \n " ) ;
2013-07-29 19:18:04 +04:00
return - ENOMEM ;
2009-02-23 18:13:24 +03:00
}
2013-07-29 19:18:04 +04:00
mx3_cam - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( mx3_cam - > clk ) )
return PTR_ERR ( mx3_cam - > clk ) ;
2009-02-23 18:13:24 +03:00
2013-07-30 09:59:49 +04:00
mx3_cam - > pdata = pdata ;
mx3_cam - > platform_flags = pdata - > flags ;
2012-08-06 10:02:08 +04:00
if ( ! ( mx3_cam - > platform_flags & MX3_CAMERA_DATAWIDTH_MASK ) ) {
2009-12-11 17:15:05 +03:00
/*
* Platform hasn ' t set available data widths . This is bad .
* Warn and use a default .
*/
2009-02-23 18:13:24 +03:00
dev_warn ( & pdev - > dev , " WARNING! Platform hasn't set available "
" data widths, using default 8 bit \n " ) ;
mx3_cam - > platform_flags | = MX3_CAMERA_DATAWIDTH_8 ;
}
2011-07-27 23:00:29 +04:00
if ( mx3_cam - > platform_flags & MX3_CAMERA_DATAWIDTH_4 )
mx3_cam - > width_flags = 1 < < 3 ;
if ( mx3_cam - > platform_flags & MX3_CAMERA_DATAWIDTH_8 )
mx3_cam - > width_flags | = 1 < < 7 ;
if ( mx3_cam - > platform_flags & MX3_CAMERA_DATAWIDTH_10 )
mx3_cam - > width_flags | = 1 < < 9 ;
if ( mx3_cam - > platform_flags & MX3_CAMERA_DATAWIDTH_15 )
mx3_cam - > width_flags | = 1 < < 14 ;
2009-02-23 18:13:24 +03:00
2013-07-30 09:59:49 +04:00
mx3_cam - > mclk = pdata - > mclk_10khz * 10000 ;
2009-02-23 18:13:24 +03:00
if ( ! mx3_cam - > mclk ) {
dev_warn ( & pdev - > dev ,
" mclk_10khz == 0! Please, fix your platform data. "
" Using default 20MHz \n " ) ;
mx3_cam - > mclk = 20000000 ;
}
/* list of video-buffers */
INIT_LIST_HEAD ( & mx3_cam - > capture ) ;
spin_lock_init ( & mx3_cam - > lock ) ;
mx3_cam - > base = base ;
soc_host = & mx3_cam - > soc_host ;
soc_host - > drv_name = MX3_CAM_DRV_NAME ;
soc_host - > ops = & mx3_soc_camera_host_ops ;
soc_host - > priv = mx3_cam ;
2009-08-25 18:43:33 +04:00
soc_host - > v4l2_dev . dev = & pdev - > dev ;
2009-02-23 18:13:24 +03:00
soc_host - > nr = pdev - > id ;
2009-04-24 19:55:48 +04:00
2011-02-21 13:14:01 +03:00
mx3_cam - > alloc_ctx = vb2_dma_contig_init_ctx ( & pdev - > dev ) ;
2013-07-29 19:18:04 +04:00
if ( IS_ERR ( mx3_cam - > alloc_ctx ) )
return PTR_ERR ( mx3_cam - > alloc_ctx ) ;
2011-02-21 13:14:01 +03:00
2013-07-30 09:59:49 +04:00
if ( pdata - > asd_sizes ) {
soc_host - > asd = pdata - > asd ;
soc_host - > asd_sizes = pdata - > asd_sizes ;
}
2009-02-23 18:13:24 +03:00
err = soc_camera_host_register ( soc_host ) ;
if ( err )
goto ecamhostreg ;
/* IDMAC interface */
dmaengine_get ( ) ;
return 0 ;
ecamhostreg :
2011-02-21 13:14:01 +03:00
vb2_dma_contig_cleanup_ctx ( mx3_cam - > alloc_ctx ) ;
2009-02-23 18:13:24 +03:00
return err ;
}
2012-12-22 01:17:53 +04:00
static int mx3_camera_remove ( struct platform_device * pdev )
2009-02-23 18:13:24 +03:00
{
2009-04-24 19:55:48 +04:00
struct soc_camera_host * soc_host = to_soc_camera_host ( & pdev - > dev ) ;
struct mx3_camera_dev * mx3_cam = container_of ( soc_host ,
struct mx3_camera_dev , soc_host ) ;
2009-02-23 18:13:24 +03:00
2009-04-24 19:55:48 +04:00
soc_camera_host_unregister ( soc_host ) ;
2009-02-23 18:13:24 +03:00
/*
* The channel has either not been allocated ,
* or should have been released
*/
if ( WARN_ON ( mx3_cam - > idmac_channel [ 0 ] ) )
dma_release_channel ( & mx3_cam - > idmac_channel [ 0 ] - > dma_chan ) ;
2011-02-21 13:14:01 +03:00
vb2_dma_contig_cleanup_ctx ( mx3_cam - > alloc_ctx ) ;
2009-02-23 18:13:24 +03:00
dmaengine_put ( ) ;
return 0 ;
}
static struct platform_driver mx3_camera_driver = {
2013-04-03 09:00:37 +04:00
. driver = {
2009-02-23 18:13:24 +03:00
. name = MX3_CAM_DRV_NAME ,
} ,
. probe = mx3_camera_probe ,
2012-12-22 01:17:53 +04:00
. remove = mx3_camera_remove ,
2009-02-23 18:13:24 +03:00
} ;
2012-01-10 10:21:49 +04:00
module_platform_driver ( mx3_camera_driver ) ;
2009-02-23 18:13:24 +03:00
MODULE_DESCRIPTION ( " i.MX3x SoC Camera Host driver " ) ;
MODULE_AUTHOR ( " Guennadi Liakhovetski <lg@denx.de> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
2011-06-25 18:28:37 +04:00
MODULE_VERSION ( " 0.2.3 " ) ;
2009-08-25 18:28:22 +04:00
MODULE_ALIAS ( " platform: " MX3_CAM_DRV_NAME ) ;