2006-11-04 15:25:53 +03:00
/*
* A driver for the CMOS camera controller in the Marvell 88 ALP01 " cafe "
* multifunction chip . Currently works with the Omnivision OV7670
* sensor .
*
2007-10-24 00:31:36 +04:00
* The data sheet for this device can be found at :
* http : //www.marvell.com/products/pcconn/88ALP01.jsp
*
2006-11-04 15:25:53 +03:00
* Copyright 2006 One Laptop Per Child Association , Inc .
2007-03-23 01:44:17 +03:00
* Copyright 2006 - 7 Jonathan Corbet < corbet @ lwn . net >
2006-11-04 15:25:53 +03:00
*
* Written by Jonathan Corbet , corbet @ lwn . net .
*
* This file may be distributed under the terms of the GNU General
* Public License , version 2.
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/fs.h>
# include <linux/pci.h>
# include <linux/i2c.h>
# include <linux/interrupt.h>
# include <linux/spinlock.h>
# include <linux/videodev2.h>
# include <media/v4l2-common.h>
2007-04-27 19:31:08 +04:00
# include <media/v4l2-chip-ident.h>
2006-11-04 15:25:53 +03:00
# include <linux/device.h>
# include <linux/wait.h>
# include <linux/list.h>
# include <linux/dma-mapping.h>
# include <linux/delay.h>
# include <linux/debugfs.h>
# include <linux/jiffies.h>
# include <linux/vmalloc.h>
# include <asm/uaccess.h>
# include <asm/io.h>
# include "cafe_ccic-regs.h"
2007-03-25 18:36:28 +04:00
# define CAFE_VERSION 0x000002
2006-11-04 15:25:53 +03:00
/*
* Parameters .
*/
MODULE_AUTHOR ( " Jonathan Corbet <corbet@lwn.net> " ) ;
MODULE_DESCRIPTION ( " Marvell 88ALP01 CMOS Camera Controller driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_SUPPORTED_DEVICE ( " Video " ) ;
/*
* Internal DMA buffer management . Since the controller cannot do S / G I / O ,
* we must have physically contiguous buffers to bring frames into .
* These parameters control how many buffers we use , whether we
* allocate them at load time ( better chance of success , but nails down
* memory ) or when somebody tries to use the camera ( riskier ) , and ,
* for load - time allocation , how big they should be .
*
* The controller can cycle through three buffers . We could use
* more by flipping pointers around , but it probably makes little
* sense .
*/
# define MAX_DMA_BUFS 3
2008-04-22 21:41:48 +04:00
static int alloc_bufs_at_read ;
2007-09-19 09:44:18 +04:00
module_param ( alloc_bufs_at_read , bool , 0444 ) ;
MODULE_PARM_DESC ( alloc_bufs_at_read ,
" Non-zero value causes DMA buffers to be allocated when the "
" video capture device is read, rather than at module load "
" time. This saves memory, but decreases the chances of "
" successfully getting those buffers. " ) ;
2006-11-04 15:25:53 +03:00
static int n_dma_bufs = 3 ;
module_param ( n_dma_bufs , uint , 0644 ) ;
MODULE_PARM_DESC ( n_dma_bufs ,
" The number of DMA buffers to allocate. Can be either two "
" (saves memory, makes timing tighter) or three. " ) ;
static int dma_buf_size = VGA_WIDTH * VGA_HEIGHT * 2 ; /* Worst case */
module_param ( dma_buf_size , uint , 0444 ) ;
MODULE_PARM_DESC ( dma_buf_size ,
" The size of the allocated DMA buffers. If actual operating "
" parameters require larger buffers, an attempt to reallocate "
" will be made. " ) ;
static int min_buffers = 1 ;
module_param ( min_buffers , uint , 0644 ) ;
MODULE_PARM_DESC ( min_buffers ,
" The minimum number of streaming I/O buffers we are willing "
" to work with. " ) ;
static int max_buffers = 10 ;
module_param ( max_buffers , uint , 0644 ) ;
MODULE_PARM_DESC ( max_buffers ,
" The maximum number of streaming I/O buffers an application "
" will be allowed to allocate. These buffers are big and live "
" in vmalloc space. " ) ;
2008-04-22 21:41:48 +04:00
static int flip ;
2006-11-04 15:25:53 +03:00
module_param ( flip , bool , 0444 ) ;
MODULE_PARM_DESC ( flip ,
" If set, the sensor will be instructed to flip the image "
" vertically. " ) ;
enum cafe_state {
S_NOTREADY , /* Not yet initialized */
S_IDLE , /* Just hanging around */
S_FLAKED , /* Some sort of problem */
S_SINGLEREAD , /* In read() */
S_SPECREAD , /* Speculative read (for future read()) */
S_STREAMING /* Streaming data */
} ;
/*
* Tracking of streaming I / O buffers .
*/
struct cafe_sio_buffer {
struct list_head list ;
struct v4l2_buffer v4lbuf ;
char * buffer ; /* Where it lives in kernel space */
int mapcount ;
struct cafe_camera * cam ;
} ;
/*
* A description of one of our devices .
* Locking : controlled by s_mutex . Certain fields , however , require
* the dev_lock spinlock ; they are marked as such by comments .
* dev_lock is also required for access to device registers .
*/
struct cafe_camera
{
enum cafe_state state ;
unsigned long flags ; /* Buffer status, mainly (dev_lock) */
int users ; /* How many open FDs */
struct file * owner ; /* Who has data access (v4l2) */
/*
* Subsystem structures .
*/
struct pci_dev * pdev ;
struct video_device v4ldev ;
struct i2c_adapter i2c_adapter ;
struct i2c_client * sensor ;
unsigned char __iomem * regs ;
struct list_head dev_list ; /* link to other devices */
/* DMA buffers */
unsigned int nbufs ; /* How many are alloc'd */
int next_buf ; /* Next to consume (dev_lock) */
unsigned int dma_buf_size ; /* allocated size */
void * dma_bufs [ MAX_DMA_BUFS ] ; /* Internal buffer addresses */
dma_addr_t dma_handles [ MAX_DMA_BUFS ] ; /* Buffer bus addresses */
unsigned int specframes ; /* Unconsumed spec frames (dev_lock) */
unsigned int sequence ; /* Frame sequence number */
unsigned int buf_seq [ MAX_DMA_BUFS ] ; /* Sequence for individual buffers */
/* Streaming buffers */
unsigned int n_sbufs ; /* How many we have */
struct cafe_sio_buffer * sb_bufs ; /* The array of housekeeping structs */
struct list_head sb_avail ; /* Available for data (we own) (dev_lock) */
struct list_head sb_full ; /* With data (user space owns) (dev_lock) */
struct tasklet_struct s_tasklet ;
/* Current operating parameters */
2007-04-27 19:31:08 +04:00
u32 sensor_type ; /* Currently ov7670 only */
2006-11-04 15:25:53 +03:00
struct v4l2_pix_format pix_format ;
/* Locks */
struct mutex s_mutex ; /* Access to this structure */
spinlock_t dev_lock ; /* Access to device */
/* Misc */
wait_queue_head_t smbus_wait ; /* Waiting on i2c events */
wait_queue_head_t iowait ; /* Waiting on frame data */
# ifdef CONFIG_VIDEO_ADV_DEBUG
struct dentry * dfs_regs ;
struct dentry * dfs_cam_regs ;
# endif
} ;
/*
* Status flags . Always manipulated with bit operations .
*/
# define CF_BUF0_VALID 0 /* Buffers valid - first three */
# define CF_BUF1_VALID 1
# define CF_BUF2_VALID 2
# define CF_DMA_ACTIVE 3 /* A frame is incoming */
# define CF_CONFIG_NEEDED 4 /* Must configure hardware */
/*
* Start over with DMA buffers - dev_lock needed .
*/
static void cafe_reset_buffers ( struct cafe_camera * cam )
{
int i ;
cam - > next_buf = - 1 ;
for ( i = 0 ; i < cam - > nbufs ; i + + )
clear_bit ( i , & cam - > flags ) ;
cam - > specframes = 0 ;
}
static inline int cafe_needs_config ( struct cafe_camera * cam )
{
return test_bit ( CF_CONFIG_NEEDED , & cam - > flags ) ;
}
static void cafe_set_config_needed ( struct cafe_camera * cam , int needed )
{
if ( needed )
set_bit ( CF_CONFIG_NEEDED , & cam - > flags ) ;
else
clear_bit ( CF_CONFIG_NEEDED , & cam - > flags ) ;
}
/*
* Debugging and related .
*/
# define cam_err(cam, fmt, arg...) \
dev_err ( & ( cam ) - > pdev - > dev , fmt , # # arg ) ;
# define cam_warn(cam, fmt, arg...) \
dev_warn ( & ( cam ) - > pdev - > dev , fmt , # # arg ) ;
# define cam_dbg(cam, fmt, arg...) \
dev_dbg ( & ( cam ) - > pdev - > dev , fmt , # # arg ) ;
/* ---------------------------------------------------------------------*/
/*
* We keep a simple list of known devices to search at open time .
*/
static LIST_HEAD ( cafe_dev_list ) ;
static DEFINE_MUTEX ( cafe_dev_list_lock ) ;
static void cafe_add_dev ( struct cafe_camera * cam )
{
mutex_lock ( & cafe_dev_list_lock ) ;
list_add_tail ( & cam - > dev_list , & cafe_dev_list ) ;
mutex_unlock ( & cafe_dev_list_lock ) ;
}
static void cafe_remove_dev ( struct cafe_camera * cam )
{
mutex_lock ( & cafe_dev_list_lock ) ;
list_del ( & cam - > dev_list ) ;
mutex_unlock ( & cafe_dev_list_lock ) ;
}
static struct cafe_camera * cafe_find_dev ( int minor )
{
struct cafe_camera * cam ;
mutex_lock ( & cafe_dev_list_lock ) ;
list_for_each_entry ( cam , & cafe_dev_list , dev_list ) {
if ( cam - > v4ldev . minor = = minor )
goto done ;
}
cam = NULL ;
done :
mutex_unlock ( & cafe_dev_list_lock ) ;
return cam ;
}
static struct cafe_camera * cafe_find_by_pdev ( struct pci_dev * pdev )
{
struct cafe_camera * cam ;
mutex_lock ( & cafe_dev_list_lock ) ;
list_for_each_entry ( cam , & cafe_dev_list , dev_list ) {
if ( cam - > pdev = = pdev )
goto done ;
}
cam = NULL ;
done :
mutex_unlock ( & cafe_dev_list_lock ) ;
return cam ;
}
/* ------------------------------------------------------------------------ */
/*
* Device register I / O
*/
static inline void cafe_reg_write ( struct cafe_camera * cam , unsigned int reg ,
unsigned int val )
{
iowrite32 ( val , cam - > regs + reg ) ;
}
static inline unsigned int cafe_reg_read ( struct cafe_camera * cam ,
unsigned int reg )
{
return ioread32 ( cam - > regs + reg ) ;
}
static inline void cafe_reg_write_mask ( struct cafe_camera * cam , unsigned int reg ,
unsigned int val , unsigned int mask )
{
unsigned int v = cafe_reg_read ( cam , reg ) ;
v = ( v & ~ mask ) | ( val & mask ) ;
cafe_reg_write ( cam , reg , v ) ;
}
static inline void cafe_reg_clear_bit ( struct cafe_camera * cam ,
unsigned int reg , unsigned int val )
{
cafe_reg_write_mask ( cam , reg , 0 , val ) ;
}
static inline void cafe_reg_set_bit ( struct cafe_camera * cam ,
unsigned int reg , unsigned int val )
{
cafe_reg_write_mask ( cam , reg , val , val ) ;
}
/* -------------------------------------------------------------------- */
/*
* The I2C / SMBUS interface to the camera itself starts here . The
* controller handles SMBUS itself , presenting a relatively simple register
* interface ; all we have to do is to tell it where to route the data .
*/
# define CAFE_SMBUS_TIMEOUT (HZ) /* generous */
static int cafe_smbus_write_done ( struct cafe_camera * cam )
{
unsigned long flags ;
int c1 ;
/*
* We must delay after the interrupt , or the controller gets confused
* and never does give us good status . Fortunately , we don ' t do this
* often .
*/
udelay ( 20 ) ;
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
c1 = cafe_reg_read ( cam , REG_TWSIC1 ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
return ( c1 & ( TWSIC1_WSTAT | TWSIC1_ERROR ) ) ! = TWSIC1_WSTAT ;
}
static int cafe_smbus_write_data ( struct cafe_camera * cam ,
u16 addr , u8 command , u8 value )
{
unsigned int rval ;
unsigned long flags ;
2007-08-17 08:02:33 +04:00
DEFINE_WAIT ( the_wait ) ;
2006-11-04 15:25:53 +03:00
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
rval = TWSIC0_EN | ( ( addr < < TWSIC0_SID_SHIFT ) & TWSIC0_SID ) ;
rval | = TWSIC0_OVMAGIC ; /* Make OV sensors work */
/*
* Marvell sez set clkdiv to all 1 ' s for now .
*/
rval | = TWSIC0_CLKDIV ;
cafe_reg_write ( cam , REG_TWSIC0 , rval ) ;
( void ) cafe_reg_read ( cam , REG_TWSIC1 ) ; /* force write */
rval = value | ( ( command < < TWSIC1_ADDR_SHIFT ) & TWSIC1_ADDR ) ;
cafe_reg_write ( cam , REG_TWSIC1 , rval ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
2007-08-17 08:02:33 +04:00
/*
* Time to wait for the write to complete . THIS IS A RACY
* WAY TO DO IT , but the sad fact is that reading the TWSIC1
* register too quickly after starting the operation sends
* the device into a place that may be kinder and better , but
* which is absolutely useless for controlling the sensor . In
* practice we have plenty of time to get into our sleep state
* before the interrupt hits , and the worst case is that we
* time out and then see that things completed , so this seems
* the best way for now .
*/
do {
prepare_to_wait ( & cam - > smbus_wait , & the_wait ,
TASK_UNINTERRUPTIBLE ) ;
schedule_timeout ( 1 ) ; /* even 1 jiffy is too long */
finish_wait ( & cam - > smbus_wait , & the_wait ) ;
} while ( ! cafe_smbus_write_done ( cam ) ) ;
# ifdef IF_THE_CAFE_HARDWARE_WORKED_RIGHT
2006-11-04 15:25:53 +03:00
wait_event_timeout ( cam - > smbus_wait , cafe_smbus_write_done ( cam ) ,
CAFE_SMBUS_TIMEOUT ) ;
2007-08-17 08:02:33 +04:00
# endif
2006-11-04 15:25:53 +03:00
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
rval = cafe_reg_read ( cam , REG_TWSIC1 ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
if ( rval & TWSIC1_WSTAT ) {
cam_err ( cam , " SMBUS write (%02x/%02x/%02x) timed out \n " , addr ,
command , value ) ;
return - EIO ;
}
if ( rval & TWSIC1_ERROR ) {
cam_err ( cam , " SMBUS write (%02x/%02x/%02x) error \n " , addr ,
command , value ) ;
return - EIO ;
}
return 0 ;
}
static int cafe_smbus_read_done ( struct cafe_camera * cam )
{
unsigned long flags ;
int c1 ;
/*
* We must delay after the interrupt , or the controller gets confused
* and never does give us good status . Fortunately , we don ' t do this
* often .
*/
udelay ( 20 ) ;
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
c1 = cafe_reg_read ( cam , REG_TWSIC1 ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
return c1 & ( TWSIC1_RVALID | TWSIC1_ERROR ) ;
}
static int cafe_smbus_read_data ( struct cafe_camera * cam ,
u16 addr , u8 command , u8 * value )
{
unsigned int rval ;
unsigned long flags ;
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
rval = TWSIC0_EN | ( ( addr < < TWSIC0_SID_SHIFT ) & TWSIC0_SID ) ;
rval | = TWSIC0_OVMAGIC ; /* Make OV sensors work */
/*
* Marvel sez set clkdiv to all 1 ' s for now .
*/
rval | = TWSIC0_CLKDIV ;
cafe_reg_write ( cam , REG_TWSIC0 , rval ) ;
( void ) cafe_reg_read ( cam , REG_TWSIC1 ) ; /* force write */
rval = TWSIC1_READ | ( ( command < < TWSIC1_ADDR_SHIFT ) & TWSIC1_ADDR ) ;
cafe_reg_write ( cam , REG_TWSIC1 , rval ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
wait_event_timeout ( cam - > smbus_wait ,
cafe_smbus_read_done ( cam ) , CAFE_SMBUS_TIMEOUT ) ;
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
rval = cafe_reg_read ( cam , REG_TWSIC1 ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
if ( rval & TWSIC1_ERROR ) {
cam_err ( cam , " SMBUS read (%02x/%02x) error \n " , addr , command ) ;
return - EIO ;
}
if ( ! ( rval & TWSIC1_RVALID ) ) {
cam_err ( cam , " SMBUS read (%02x/%02x) timed out \n " , addr ,
command ) ;
return - EIO ;
}
* value = rval & 0xff ;
return 0 ;
}
/*
* Perform a transfer over SMBUS . This thing is called under
* the i2c bus lock , so we shouldn ' t race with ourselves . . .
*/
static int cafe_smbus_xfer ( struct i2c_adapter * adapter , u16 addr ,
unsigned short flags , char rw , u8 command ,
int size , union i2c_smbus_data * data )
{
struct cafe_camera * cam = i2c_get_adapdata ( adapter ) ;
int ret = - EINVAL ;
/*
* Refuse to talk to anything but OV cam chips . We should
* never even see an attempt to do so , but one never knows .
*/
if ( cam - > sensor & & addr ! = cam - > sensor - > addr ) {
cam_err ( cam , " funky smbus addr %d \n " , addr ) ;
return - EINVAL ;
}
/*
* This interface would appear to only do byte data ops . OK
* it can do word too , but the cam chip has no use for that .
*/
if ( size ! = I2C_SMBUS_BYTE_DATA ) {
cam_err ( cam , " funky xfer size %d \n " , size ) ;
return - EINVAL ;
}
if ( rw = = I2C_SMBUS_WRITE )
ret = cafe_smbus_write_data ( cam , addr , command , data - > byte ) ;
else if ( rw = = I2C_SMBUS_READ )
ret = cafe_smbus_read_data ( cam , addr , command , & data - > byte ) ;
return ret ;
}
static void cafe_smbus_enable_irq ( struct cafe_camera * cam )
{
unsigned long flags ;
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
cafe_reg_set_bit ( cam , REG_IRQMASK , TWSIIRQS ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
}
static u32 cafe_smbus_func ( struct i2c_adapter * adapter )
{
return I2C_FUNC_SMBUS_READ_BYTE_DATA |
I2C_FUNC_SMBUS_WRITE_BYTE_DATA ;
}
static struct i2c_algorithm cafe_smbus_algo = {
. smbus_xfer = cafe_smbus_xfer ,
. functionality = cafe_smbus_func
} ;
/* Somebody is on the bus */
static int cafe_cam_init ( struct cafe_camera * cam ) ;
2006-11-20 01:04:55 +03:00
static void cafe_ctlr_stop_dma ( struct cafe_camera * cam ) ;
static void cafe_ctlr_power_down ( struct cafe_camera * cam ) ;
2006-11-04 15:25:53 +03:00
static int cafe_smbus_attach ( struct i2c_client * client )
{
struct cafe_camera * cam = i2c_get_adapdata ( client - > adapter ) ;
/*
* Don ' t talk to chips we don ' t recognize .
*/
if ( client - > driver - > id = = I2C_DRIVERID_OV7670 ) {
cam - > sensor = client ;
return cafe_cam_init ( cam ) ;
}
return - EINVAL ;
}
static int cafe_smbus_detach ( struct i2c_client * client )
{
struct cafe_camera * cam = i2c_get_adapdata ( client - > adapter ) ;
2006-11-20 01:04:55 +03:00
if ( cam - > sensor = = client ) {
cafe_ctlr_stop_dma ( cam ) ;
cafe_ctlr_power_down ( cam ) ;
cam_err ( cam , " lost the sensor! \n " ) ;
2006-11-04 15:25:53 +03:00
cam - > sensor = NULL ; /* Bummer, no camera */
2006-11-20 01:04:55 +03:00
cam - > state = S_NOTREADY ;
}
2006-11-04 15:25:53 +03:00
return 0 ;
}
static int cafe_smbus_setup ( struct cafe_camera * cam )
{
struct i2c_adapter * adap = & cam - > i2c_adapter ;
int ret ;
cafe_smbus_enable_irq ( cam ) ;
adap - > id = I2C_HW_SMBUS_CAFE ;
adap - > class = I2C_CLASS_CAM_DIGITAL ;
adap - > owner = THIS_MODULE ;
adap - > client_register = cafe_smbus_attach ;
adap - > client_unregister = cafe_smbus_detach ;
adap - > algo = & cafe_smbus_algo ;
strcpy ( adap - > name , " cafe_ccic " ) ;
2007-02-14 00:09:03 +03:00
adap - > dev . parent = & cam - > pdev - > dev ;
2006-11-04 15:25:53 +03:00
i2c_set_adapdata ( adap , cam ) ;
ret = i2c_add_adapter ( adap ) ;
if ( ret )
printk ( KERN_ERR " Unable to register cafe i2c adapter \n " ) ;
return ret ;
}
static void cafe_smbus_shutdown ( struct cafe_camera * cam )
{
i2c_del_adapter ( & cam - > i2c_adapter ) ;
}
/* ------------------------------------------------------------------- */
/*
* Deal with the controller .
*/
/*
* Do everything we think we need to have the interface operating
* according to the desired format .
*/
static void cafe_ctlr_dma ( struct cafe_camera * cam )
{
/*
* Store the first two Y buffers ( we aren ' t supporting
* planar formats for now , so no UV bufs ) . Then either
* set the third if it exists , or tell the controller
* to just use two .
*/
cafe_reg_write ( cam , REG_Y0BAR , cam - > dma_handles [ 0 ] ) ;
cafe_reg_write ( cam , REG_Y1BAR , cam - > dma_handles [ 1 ] ) ;
if ( cam - > nbufs > 2 ) {
cafe_reg_write ( cam , REG_Y2BAR , cam - > dma_handles [ 2 ] ) ;
cafe_reg_clear_bit ( cam , REG_CTRL1 , C1_TWOBUFS ) ;
}
else
cafe_reg_set_bit ( cam , REG_CTRL1 , C1_TWOBUFS ) ;
cafe_reg_write ( cam , REG_UBAR , 0 ) ; /* 32 bits only for now */
}
static void cafe_ctlr_image ( struct cafe_camera * cam )
{
int imgsz ;
struct v4l2_pix_format * fmt = & cam - > pix_format ;
imgsz = ( ( fmt - > height < < IMGSZ_V_SHIFT ) & IMGSZ_V_MASK ) |
( fmt - > bytesperline & IMGSZ_H_MASK ) ;
cafe_reg_write ( cam , REG_IMGSIZE , imgsz ) ;
cafe_reg_write ( cam , REG_IMGOFFSET , 0 ) ;
/* YPITCH just drops the last two bits */
cafe_reg_write_mask ( cam , REG_IMGPITCH , fmt - > bytesperline ,
IMGP_YP_MASK ) ;
/*
* Tell the controller about the image format we are using .
*/
switch ( cam - > pix_format . pixelformat ) {
case V4L2_PIX_FMT_YUYV :
cafe_reg_write_mask ( cam , REG_CTRL0 ,
C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_YUYV ,
C0_DF_MASK ) ;
break ;
case V4L2_PIX_FMT_RGB444 :
cafe_reg_write_mask ( cam , REG_CTRL0 ,
C0_DF_RGB | C0_RGBF_444 | C0_RGB4_XRGB ,
C0_DF_MASK ) ;
/* Alpha value? */
break ;
case V4L2_PIX_FMT_RGB565 :
cafe_reg_write_mask ( cam , REG_CTRL0 ,
C0_DF_RGB | C0_RGBF_565 | C0_RGB5_BGGR ,
C0_DF_MASK ) ;
break ;
default :
cam_err ( cam , " Unknown format %x \n " , cam - > pix_format . pixelformat ) ;
break ;
}
/*
* Make sure it knows we want to use hsync / vsync .
*/
cafe_reg_write_mask ( cam , REG_CTRL0 , C0_SIF_HVSYNC ,
C0_SIFM_MASK ) ;
}
/*
* Configure the controller for operation ; caller holds the
* device mutex .
*/
static int cafe_ctlr_configure ( struct cafe_camera * cam )
{
unsigned long flags ;
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
cafe_ctlr_dma ( cam ) ;
cafe_ctlr_image ( cam ) ;
cafe_set_config_needed ( cam , 0 ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
return 0 ;
}
static void cafe_ctlr_irq_enable ( struct cafe_camera * cam )
{
/*
* Clear any pending interrupts , since we do not
* expect to have I / O active prior to enabling .
*/
cafe_reg_write ( cam , REG_IRQSTAT , FRAMEIRQS ) ;
cafe_reg_set_bit ( cam , REG_IRQMASK , FRAMEIRQS ) ;
}
static void cafe_ctlr_irq_disable ( struct cafe_camera * cam )
{
cafe_reg_clear_bit ( cam , REG_IRQMASK , FRAMEIRQS ) ;
}
/*
* Make the controller start grabbing images . Everything must
* be set up before doing this .
*/
static void cafe_ctlr_start ( struct cafe_camera * cam )
{
/* set_bit performs a read, so no other barrier should be
needed here */
cafe_reg_set_bit ( cam , REG_CTRL0 , C0_ENABLE ) ;
}
static void cafe_ctlr_stop ( struct cafe_camera * cam )
{
cafe_reg_clear_bit ( cam , REG_CTRL0 , C0_ENABLE ) ;
}
static void cafe_ctlr_init ( struct cafe_camera * cam )
{
unsigned long flags ;
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
/*
* Added magic to bring up the hardware on the B - Test board
*/
cafe_reg_write ( cam , 0x3038 , 0x8 ) ;
cafe_reg_write ( cam , 0x315c , 0x80008 ) ;
/*
* Go through the dance needed to wake the device up .
* Note that these registers are global and shared
* with the NAND and SD devices . Interaction between the
* three still needs to be examined .
*/
cafe_reg_write ( cam , REG_GL_CSR , GCSR_SRS | GCSR_MRS ) ; /* Needed? */
cafe_reg_write ( cam , REG_GL_CSR , GCSR_SRC | GCSR_MRC ) ;
cafe_reg_write ( cam , REG_GL_CSR , GCSR_SRC | GCSR_MRS ) ;
2007-04-27 19:32:28 +04:00
/*
* Here we must wait a bit for the controller to come around .
*/
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
2007-08-17 08:03:22 +04:00
msleep ( 5 ) ;
2007-04-27 19:32:28 +04:00
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
2006-11-04 15:25:53 +03:00
cafe_reg_write ( cam , REG_GL_CSR , GCSR_CCIC_EN | GCSR_SRC | GCSR_MRC ) ;
cafe_reg_set_bit ( cam , REG_GL_IMASK , GIMSK_CCIC_EN ) ;
/*
* Make sure it ' s not powered down .
*/
cafe_reg_clear_bit ( cam , REG_CTRL1 , C1_PWRDWN ) ;
/*
* Turn off the enable bit . It sure should be off anyway ,
* but it ' s good to be sure .
*/
cafe_reg_clear_bit ( cam , REG_CTRL0 , C0_ENABLE ) ;
/*
* Mask all interrupts .
*/
cafe_reg_write ( cam , REG_IRQMASK , 0 ) ;
/*
* Clock the sensor appropriately . Controller clock should
* be 48 MHz , sensor " typical " value is half that .
*/
cafe_reg_write_mask ( cam , REG_CLKCTRL , 2 , CLK_DIV_MASK ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
}
/*
* Stop the controller , and don ' t return until we ' re really sure that no
* further DMA is going on .
*/
static void cafe_ctlr_stop_dma ( struct cafe_camera * cam )
{
unsigned long flags ;
/*
* Theory : stop the camera controller ( whether it is operating
* or not ) . Delay briefly just in case we race with the SOF
* interrupt , then wait until no DMA is active .
*/
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
cafe_ctlr_stop ( cam ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
mdelay ( 1 ) ;
wait_event_timeout ( cam - > iowait ,
! test_bit ( CF_DMA_ACTIVE , & cam - > flags ) , HZ ) ;
if ( test_bit ( CF_DMA_ACTIVE , & cam - > flags ) )
cam_err ( cam , " Timeout waiting for DMA to end \n " ) ;
/* This would be bad news - what now? */
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
cam - > state = S_IDLE ;
cafe_ctlr_irq_disable ( cam ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
}
/*
* Power up and down .
*/
static void cafe_ctlr_power_up ( struct cafe_camera * cam )
{
unsigned long flags ;
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
cafe_reg_clear_bit ( cam , REG_CTRL1 , C1_PWRDWN ) ;
2007-05-22 07:37:58 +04:00
/*
* Part one of the sensor dance : turn the global
* GPIO signal on .
*/
cafe_reg_write ( cam , REG_GL_FCR , GFCR_GPIO_ON ) ;
cafe_reg_write ( cam , REG_GL_GPIOR , GGPIO_OUT | GGPIO_VAL ) ;
2006-11-04 15:25:53 +03:00
/*
* Put the sensor into operational mode ( assumes OLPC - style
* wiring ) . Control 0 is reset - set to 1 to operate .
* Control 1 is power down , set to 0 to operate .
*/
2006-11-20 01:04:55 +03:00
cafe_reg_write ( cam , REG_GPR , GPR_C1EN | GPR_C0EN ) ; /* pwr up, reset */
2007-04-27 19:32:28 +04:00
// mdelay(1); /* Marvell says 1ms will do it */
2006-11-04 15:25:53 +03:00
cafe_reg_write ( cam , REG_GPR , GPR_C1EN | GPR_C0EN | GPR_C0 ) ;
2007-04-27 19:32:28 +04:00
// mdelay(1); /* Enough? */
2006-11-04 15:25:53 +03:00
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
2007-05-22 07:37:58 +04:00
msleep ( 5 ) ; /* Just to be sure */
2006-11-04 15:25:53 +03:00
}
static void cafe_ctlr_power_down ( struct cafe_camera * cam )
{
unsigned long flags ;
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
cafe_reg_write ( cam , REG_GPR , GPR_C1EN | GPR_C0EN | GPR_C1 ) ;
2007-05-22 07:37:58 +04:00
cafe_reg_write ( cam , REG_GL_FCR , GFCR_GPIO_ON ) ;
cafe_reg_write ( cam , REG_GL_GPIOR , GGPIO_OUT ) ;
2006-11-04 15:25:53 +03:00
cafe_reg_set_bit ( cam , REG_CTRL1 , C1_PWRDWN ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
}
/* -------------------------------------------------------------------- */
/*
* Communications with the sensor .
*/
static int __cafe_cam_cmd ( struct cafe_camera * cam , int cmd , void * arg )
{
struct i2c_client * sc = cam - > sensor ;
int ret ;
if ( sc = = NULL | | sc - > driver = = NULL | | sc - > driver - > command = = NULL )
return - EINVAL ;
ret = sc - > driver - > command ( sc , cmd , arg ) ;
if ( ret = = - EPERM ) /* Unsupported command */
return 0 ;
return ret ;
}
static int __cafe_cam_reset ( struct cafe_camera * cam )
{
int zero = 0 ;
return __cafe_cam_cmd ( cam , VIDIOC_INT_RESET , & zero ) ;
}
/*
* We have found the sensor on the i2c . Let ' s try to have a
* conversation .
*/
static int cafe_cam_init ( struct cafe_camera * cam )
{
2007-04-27 19:31:08 +04:00
struct v4l2_chip_ident chip = { V4L2_CHIP_MATCH_I2C_ADDR , 0 , 0 , 0 } ;
2006-11-04 15:25:53 +03:00
int ret ;
mutex_lock ( & cam - > s_mutex ) ;
if ( cam - > state ! = S_NOTREADY )
cam_warn ( cam , " Cam init with device in funky state %d " ,
cam - > state ) ;
ret = __cafe_cam_reset ( cam ) ;
if ( ret )
goto out ;
2007-04-27 19:31:08 +04:00
chip . match_chip = cam - > sensor - > addr ;
ret = __cafe_cam_cmd ( cam , VIDIOC_G_CHIP_IDENT , & chip ) ;
2006-11-04 15:25:53 +03:00
if ( ret )
goto out ;
2007-04-27 19:31:08 +04:00
cam - > sensor_type = chip . ident ;
2006-11-04 15:25:53 +03:00
// if (cam->sensor->addr != OV7xx0_SID) {
if ( cam - > sensor_type ! = V4L2_IDENT_OV7670 ) {
cam_err ( cam , " Unsupported sensor type %d " , cam - > sensor - > addr ) ;
ret = - EINVAL ;
goto out ;
}
/* Get/set parameters? */
ret = 0 ;
cam - > state = S_IDLE ;
out :
2007-05-22 07:37:58 +04:00
cafe_ctlr_power_down ( cam ) ;
2006-11-04 15:25:53 +03:00
mutex_unlock ( & cam - > s_mutex ) ;
return ret ;
}
/*
* Configure the sensor to match the parameters we have . Caller should
* hold s_mutex
*/
static int cafe_cam_set_flip ( struct cafe_camera * cam )
{
struct v4l2_control ctrl ;
memset ( & ctrl , 0 , sizeof ( ctrl ) ) ;
ctrl . id = V4L2_CID_VFLIP ;
ctrl . value = flip ;
return __cafe_cam_cmd ( cam , VIDIOC_S_CTRL , & ctrl ) ;
}
static int cafe_cam_configure ( struct cafe_camera * cam )
{
struct v4l2_format fmt ;
int ret , zero = 0 ;
if ( cam - > state ! = S_IDLE )
return - EINVAL ;
fmt . fmt . pix = cam - > pix_format ;
ret = __cafe_cam_cmd ( cam , VIDIOC_INT_INIT , & zero ) ;
if ( ret = = 0 )
ret = __cafe_cam_cmd ( cam , VIDIOC_S_FMT , & fmt ) ;
/*
* OV7670 does weird things if flip is set * before * format . . .
*/
ret + = cafe_cam_set_flip ( cam ) ;
return ret ;
}
/* -------------------------------------------------------------------- */
/*
* DMA buffer management . These functions need s_mutex held .
*/
/* FIXME: this is inefficient as hell, since dma_alloc_coherent just
* does a get_free_pages ( ) call , and we waste a good chunk of an orderN
* allocation . Should try to allocate the whole set in one chunk .
*/
static int cafe_alloc_dma_bufs ( struct cafe_camera * cam , int loadtime )
{
int i ;
cafe_set_config_needed ( cam , 1 ) ;
if ( loadtime )
cam - > dma_buf_size = dma_buf_size ;
2006-12-01 21:37:49 +03:00
else
2006-11-04 15:25:53 +03:00
cam - > dma_buf_size = cam - > pix_format . sizeimage ;
if ( n_dma_bufs > 3 )
n_dma_bufs = 3 ;
cam - > nbufs = 0 ;
for ( i = 0 ; i < n_dma_bufs ; i + + ) {
cam - > dma_bufs [ i ] = dma_alloc_coherent ( & cam - > pdev - > dev ,
cam - > dma_buf_size , cam - > dma_handles + i ,
GFP_KERNEL ) ;
if ( cam - > dma_bufs [ i ] = = NULL ) {
cam_warn ( cam , " Failed to allocate DMA buffer \n " ) ;
break ;
}
/* For debug, remove eventually */
memset ( cam - > dma_bufs [ i ] , 0xcc , cam - > dma_buf_size ) ;
( cam - > nbufs ) + + ;
}
switch ( cam - > nbufs ) {
case 1 :
dma_free_coherent ( & cam - > pdev - > dev , cam - > dma_buf_size ,
cam - > dma_bufs [ 0 ] , cam - > dma_handles [ 0 ] ) ;
cam - > nbufs = 0 ;
case 0 :
cam_err ( cam , " Insufficient DMA buffers, cannot operate \n " ) ;
return - ENOMEM ;
case 2 :
if ( n_dma_bufs > 2 )
cam_warn ( cam , " Will limp along with only 2 buffers \n " ) ;
break ;
}
return 0 ;
}
static void cafe_free_dma_bufs ( struct cafe_camera * cam )
{
int i ;
for ( i = 0 ; i < cam - > nbufs ; i + + ) {
dma_free_coherent ( & cam - > pdev - > dev , cam - > dma_buf_size ,
cam - > dma_bufs [ i ] , cam - > dma_handles [ i ] ) ;
cam - > dma_bufs [ i ] = NULL ;
}
cam - > nbufs = 0 ;
}
/* ----------------------------------------------------------------------- */
/*
* Here starts the V4L2 interface code .
*/
/*
* Read an image from the device .
*/
static ssize_t cafe_deliver_buffer ( struct cafe_camera * cam ,
char __user * buffer , size_t len , loff_t * pos )
{
int bufno ;
unsigned long flags ;
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
if ( cam - > next_buf < 0 ) {
cam_err ( cam , " deliver_buffer: No next buffer \n " ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
return - EIO ;
}
bufno = cam - > next_buf ;
clear_bit ( bufno , & cam - > flags ) ;
if ( + + ( cam - > next_buf ) > = cam - > nbufs )
cam - > next_buf = 0 ;
if ( ! test_bit ( cam - > next_buf , & cam - > flags ) )
cam - > next_buf = - 1 ;
cam - > specframes = 0 ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
if ( len > cam - > pix_format . sizeimage )
len = cam - > pix_format . sizeimage ;
if ( copy_to_user ( buffer , cam - > dma_bufs [ bufno ] , len ) )
return - EFAULT ;
( * pos ) + = len ;
return len ;
}
/*
* Get everything ready , and start grabbing frames .
*/
static int cafe_read_setup ( struct cafe_camera * cam , enum cafe_state state )
{
int ret ;
unsigned long flags ;
/*
* Configuration . If we still don ' t have DMA buffers ,
* make one last , desperate attempt .
*/
if ( cam - > nbufs = = 0 )
if ( cafe_alloc_dma_bufs ( cam , 0 ) )
return - ENOMEM ;
if ( cafe_needs_config ( cam ) ) {
cafe_cam_configure ( cam ) ;
ret = cafe_ctlr_configure ( cam ) ;
if ( ret )
return ret ;
}
/*
* Turn it loose .
*/
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
cafe_reset_buffers ( cam ) ;
cafe_ctlr_irq_enable ( cam ) ;
cam - > state = state ;
cafe_ctlr_start ( cam ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
return 0 ;
}
static ssize_t cafe_v4l_read ( struct file * filp ,
char __user * buffer , size_t len , loff_t * pos )
{
struct cafe_camera * cam = filp - > private_data ;
2007-02-14 01:31:45 +03:00
int ret = 0 ;
2006-11-04 15:25:53 +03:00
/*
* Perhaps we ' re in speculative read mode and already
* have data ?
*/
mutex_lock ( & cam - > s_mutex ) ;
if ( cam - > state = = S_SPECREAD ) {
if ( cam - > next_buf > = 0 ) {
ret = cafe_deliver_buffer ( cam , buffer , len , pos ) ;
if ( ret ! = 0 )
goto out_unlock ;
}
} else if ( cam - > state = = S_FLAKED | | cam - > state = = S_NOTREADY ) {
ret = - EIO ;
goto out_unlock ;
} else if ( cam - > state ! = S_IDLE ) {
ret = - EBUSY ;
goto out_unlock ;
}
/*
* v4l2 : multiple processes can open the device , but only
* one gets to grab data from it .
*/
if ( cam - > owner & & cam - > owner ! = filp ) {
ret = - EBUSY ;
goto out_unlock ;
}
cam - > owner = filp ;
/*
* Do setup if need be .
*/
if ( cam - > state ! = S_SPECREAD ) {
ret = cafe_read_setup ( cam , S_SINGLEREAD ) ;
if ( ret )
goto out_unlock ;
}
/*
* Wait for something to happen . This should probably
* be interruptible ( FIXME ) .
*/
wait_event_timeout ( cam - > iowait , cam - > next_buf > = 0 , HZ ) ;
if ( cam - > next_buf < 0 ) {
cam_err ( cam , " read() operation timed out \n " ) ;
cafe_ctlr_stop_dma ( cam ) ;
ret = - EIO ;
goto out_unlock ;
}
/*
* Give them their data and we should be done .
*/
ret = cafe_deliver_buffer ( cam , buffer , len , pos ) ;
out_unlock :
mutex_unlock ( & cam - > s_mutex ) ;
return ret ;
}
/*
* Streaming I / O support .
*/
static int cafe_vidioc_streamon ( struct file * filp , void * priv ,
enum v4l2_buf_type type )
{
struct cafe_camera * cam = filp - > private_data ;
int ret = - EINVAL ;
if ( type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE )
goto out ;
mutex_lock ( & cam - > s_mutex ) ;
if ( cam - > state ! = S_IDLE | | cam - > n_sbufs = = 0 )
goto out_unlock ;
cam - > sequence = 0 ;
ret = cafe_read_setup ( cam , S_STREAMING ) ;
out_unlock :
mutex_unlock ( & cam - > s_mutex ) ;
out :
return ret ;
}
static int cafe_vidioc_streamoff ( struct file * filp , void * priv ,
enum v4l2_buf_type type )
{
struct cafe_camera * cam = filp - > private_data ;
int ret = - EINVAL ;
if ( type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE )
goto out ;
mutex_lock ( & cam - > s_mutex ) ;
if ( cam - > state ! = S_STREAMING )
goto out_unlock ;
cafe_ctlr_stop_dma ( cam ) ;
ret = 0 ;
out_unlock :
mutex_unlock ( & cam - > s_mutex ) ;
out :
return ret ;
}
static int cafe_setup_siobuf ( struct cafe_camera * cam , int index )
{
struct cafe_sio_buffer * buf = cam - > sb_bufs + index ;
INIT_LIST_HEAD ( & buf - > list ) ;
buf - > v4lbuf . length = PAGE_ALIGN ( cam - > pix_format . sizeimage ) ;
buf - > buffer = vmalloc_user ( buf - > v4lbuf . length ) ;
if ( buf - > buffer = = NULL )
return - ENOMEM ;
buf - > mapcount = 0 ;
buf - > cam = cam ;
buf - > v4lbuf . index = index ;
buf - > v4lbuf . type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
buf - > v4lbuf . field = V4L2_FIELD_NONE ;
buf - > v4lbuf . memory = V4L2_MEMORY_MMAP ;
/*
2007-08-23 23:37:49 +04:00
* Offset : must be 32 - bit even on a 64 - bit system . videobuf - dma - sg
2006-11-04 15:25:53 +03:00
* just uses the length times the index , but the spec warns
* against doing just that - vma merging problems . So we
* leave a gap between each pair of buffers .
*/
buf - > v4lbuf . m . offset = 2 * index * buf - > v4lbuf . length ;
return 0 ;
}
static int cafe_free_sio_buffers ( struct cafe_camera * cam )
{
int i ;
/*
* If any buffers are mapped , we cannot free them at all .
*/
for ( i = 0 ; i < cam - > n_sbufs ; i + + )
if ( cam - > sb_bufs [ i ] . mapcount > 0 )
return - EBUSY ;
/*
* OK , let ' s do it .
*/
for ( i = 0 ; i < cam - > n_sbufs ; i + + )
vfree ( cam - > sb_bufs [ i ] . buffer ) ;
cam - > n_sbufs = 0 ;
kfree ( cam - > sb_bufs ) ;
cam - > sb_bufs = NULL ;
INIT_LIST_HEAD ( & cam - > sb_avail ) ;
INIT_LIST_HEAD ( & cam - > sb_full ) ;
return 0 ;
}
static int cafe_vidioc_reqbufs ( struct file * filp , void * priv ,
struct v4l2_requestbuffers * req )
{
struct cafe_camera * cam = filp - > private_data ;
2007-02-07 03:52:36 +03:00
int ret = 0 ; /* Silence warning */
2006-11-04 15:25:53 +03:00
/*
* Make sure it ' s something we can do . User pointers could be
* implemented without great pain , but that ' s not been done yet .
*/
if ( req - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE )
return - EINVAL ;
if ( req - > memory ! = V4L2_MEMORY_MMAP )
return - EINVAL ;
/*
* If they ask for zero buffers , they really want us to stop streaming
* ( if it ' s happening ) and free everything . Should we check owner ?
*/
mutex_lock ( & cam - > s_mutex ) ;
if ( req - > count = = 0 ) {
if ( cam - > state = = S_STREAMING )
cafe_ctlr_stop_dma ( cam ) ;
ret = cafe_free_sio_buffers ( cam ) ;
goto out ;
}
/*
* Device needs to be idle and working . We * could * try to do the
* right thing in S_SPECREAD by shutting things down , but it
* probably doesn ' t matter .
*/
if ( cam - > state ! = S_IDLE | | ( cam - > owner & & cam - > owner ! = filp ) ) {
ret = - EBUSY ;
goto out ;
}
cam - > owner = filp ;
if ( req - > count < min_buffers )
req - > count = min_buffers ;
else if ( req - > count > max_buffers )
req - > count = max_buffers ;
if ( cam - > n_sbufs > 0 ) {
ret = cafe_free_sio_buffers ( cam ) ;
if ( ret )
goto out ;
}
cam - > sb_bufs = kzalloc ( req - > count * sizeof ( struct cafe_sio_buffer ) ,
GFP_KERNEL ) ;
if ( cam - > sb_bufs = = NULL ) {
ret = - ENOMEM ;
goto out ;
}
for ( cam - > n_sbufs = 0 ; cam - > n_sbufs < req - > count ; ( cam - > n_sbufs + + ) ) {
ret = cafe_setup_siobuf ( cam , cam - > n_sbufs ) ;
if ( ret )
break ;
}
if ( cam - > n_sbufs = = 0 ) /* no luck at all - ret already set */
kfree ( cam - > sb_bufs ) ;
req - > count = cam - > n_sbufs ; /* In case of partial success */
out :
mutex_unlock ( & cam - > s_mutex ) ;
return ret ;
}
static int cafe_vidioc_querybuf ( struct file * filp , void * priv ,
struct v4l2_buffer * buf )
{
struct cafe_camera * cam = filp - > private_data ;
int ret = - EINVAL ;
mutex_lock ( & cam - > s_mutex ) ;
if ( buf - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE )
goto out ;
if ( buf - > index < 0 | | buf - > index > = cam - > n_sbufs )
goto out ;
* buf = cam - > sb_bufs [ buf - > index ] . v4lbuf ;
ret = 0 ;
out :
mutex_unlock ( & cam - > s_mutex ) ;
return ret ;
}
static int cafe_vidioc_qbuf ( struct file * filp , void * priv ,
struct v4l2_buffer * buf )
{
struct cafe_camera * cam = filp - > private_data ;
struct cafe_sio_buffer * sbuf ;
int ret = - EINVAL ;
unsigned long flags ;
mutex_lock ( & cam - > s_mutex ) ;
if ( buf - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE )
goto out ;
if ( buf - > index < 0 | | buf - > index > = cam - > n_sbufs )
goto out ;
sbuf = cam - > sb_bufs + buf - > index ;
if ( sbuf - > v4lbuf . flags & V4L2_BUF_FLAG_QUEUED ) {
ret = 0 ; /* Already queued?? */
goto out ;
}
if ( sbuf - > v4lbuf . flags & V4L2_BUF_FLAG_DONE ) {
/* Spec doesn't say anything, seems appropriate tho */
ret = - EBUSY ;
goto out ;
}
sbuf - > v4lbuf . flags | = V4L2_BUF_FLAG_QUEUED ;
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
list_add ( & sbuf - > list , & cam - > sb_avail ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
ret = 0 ;
out :
mutex_unlock ( & cam - > s_mutex ) ;
return ret ;
}
static int cafe_vidioc_dqbuf ( struct file * filp , void * priv ,
struct v4l2_buffer * buf )
{
struct cafe_camera * cam = filp - > private_data ;
struct cafe_sio_buffer * sbuf ;
int ret = - EINVAL ;
unsigned long flags ;
mutex_lock ( & cam - > s_mutex ) ;
if ( buf - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE )
goto out_unlock ;
if ( cam - > state ! = S_STREAMING )
goto out_unlock ;
if ( list_empty ( & cam - > sb_full ) & & filp - > f_flags & O_NONBLOCK ) {
ret = - EAGAIN ;
goto out_unlock ;
}
while ( list_empty ( & cam - > sb_full ) & & cam - > state = = S_STREAMING ) {
mutex_unlock ( & cam - > s_mutex ) ;
if ( wait_event_interruptible ( cam - > iowait ,
! list_empty ( & cam - > sb_full ) ) ) {
ret = - ERESTARTSYS ;
goto out ;
}
mutex_lock ( & cam - > s_mutex ) ;
}
if ( cam - > state ! = S_STREAMING )
ret = - EINTR ;
else {
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
/* Should probably recheck !list_empty() here */
sbuf = list_entry ( cam - > sb_full . next ,
struct cafe_sio_buffer , list ) ;
list_del_init ( & sbuf - > list ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
sbuf - > v4lbuf . flags & = ~ V4L2_BUF_FLAG_DONE ;
* buf = sbuf - > v4lbuf ;
ret = 0 ;
}
out_unlock :
mutex_unlock ( & cam - > s_mutex ) ;
out :
return ret ;
}
static void cafe_v4l_vm_open ( struct vm_area_struct * vma )
{
struct cafe_sio_buffer * sbuf = vma - > vm_private_data ;
/*
* Locking : done under mmap_sem , so we don ' t need to
* go back to the camera lock here .
*/
sbuf - > mapcount + + ;
}
static void cafe_v4l_vm_close ( struct vm_area_struct * vma )
{
struct cafe_sio_buffer * sbuf = vma - > vm_private_data ;
mutex_lock ( & sbuf - > cam - > s_mutex ) ;
sbuf - > mapcount - - ;
/* Docs say we should stop I/O too... */
if ( sbuf - > mapcount = = 0 )
sbuf - > v4lbuf . flags & = ~ V4L2_BUF_FLAG_MAPPED ;
mutex_unlock ( & sbuf - > cam - > s_mutex ) ;
}
static struct vm_operations_struct cafe_v4l_vm_ops = {
. open = cafe_v4l_vm_open ,
. close = cafe_v4l_vm_close
} ;
static int cafe_v4l_mmap ( struct file * filp , struct vm_area_struct * vma )
{
struct cafe_camera * cam = filp - > private_data ;
unsigned long offset = vma - > vm_pgoff < < PAGE_SHIFT ;
int ret = - EINVAL ;
int i ;
struct cafe_sio_buffer * sbuf = NULL ;
if ( ! ( vma - > vm_flags & VM_WRITE ) | | ! ( vma - > vm_flags & VM_SHARED ) )
return - EINVAL ;
/*
* Find the buffer they are looking for .
*/
mutex_lock ( & cam - > s_mutex ) ;
for ( i = 0 ; i < cam - > n_sbufs ; i + + )
if ( cam - > sb_bufs [ i ] . v4lbuf . m . offset = = offset ) {
sbuf = cam - > sb_bufs + i ;
break ;
}
if ( sbuf = = NULL )
goto out ;
ret = remap_vmalloc_range ( vma , sbuf - > buffer , 0 ) ;
if ( ret )
goto out ;
vma - > vm_flags | = VM_DONTEXPAND ;
vma - > vm_private_data = sbuf ;
vma - > vm_ops = & cafe_v4l_vm_ops ;
sbuf - > v4lbuf . flags | = V4L2_BUF_FLAG_MAPPED ;
cafe_v4l_vm_open ( vma ) ;
ret = 0 ;
out :
mutex_unlock ( & cam - > s_mutex ) ;
return ret ;
}
static int cafe_v4l_open ( struct inode * inode , struct file * filp )
{
struct cafe_camera * cam ;
cam = cafe_find_dev ( iminor ( inode ) ) ;
if ( cam = = NULL )
return - ENODEV ;
filp - > private_data = cam ;
mutex_lock ( & cam - > s_mutex ) ;
if ( cam - > users = = 0 ) {
cafe_ctlr_power_up ( cam ) ;
__cafe_cam_reset ( cam ) ;
cafe_set_config_needed ( cam , 1 ) ;
/* FIXME make sure this is complete */
}
( cam - > users ) + + ;
mutex_unlock ( & cam - > s_mutex ) ;
return 0 ;
}
static int cafe_v4l_release ( struct inode * inode , struct file * filp )
{
struct cafe_camera * cam = filp - > private_data ;
mutex_lock ( & cam - > s_mutex ) ;
( cam - > users ) - - ;
if ( filp = = cam - > owner ) {
cafe_ctlr_stop_dma ( cam ) ;
cafe_free_sio_buffers ( cam ) ;
cam - > owner = NULL ;
}
2006-11-20 01:04:55 +03:00
if ( cam - > users = = 0 ) {
2006-11-04 15:25:53 +03:00
cafe_ctlr_power_down ( cam ) ;
2007-09-19 09:44:18 +04:00
if ( alloc_bufs_at_read )
2006-11-20 01:04:55 +03:00
cafe_free_dma_bufs ( cam ) ;
}
2006-11-04 15:25:53 +03:00
mutex_unlock ( & cam - > s_mutex ) ;
return 0 ;
}
static unsigned int cafe_v4l_poll ( struct file * filp ,
struct poll_table_struct * pt )
{
struct cafe_camera * cam = filp - > private_data ;
poll_wait ( filp , & cam - > iowait , pt ) ;
if ( cam - > next_buf > = 0 )
return POLLIN | POLLRDNORM ;
return 0 ;
}
static int cafe_vidioc_queryctrl ( struct file * filp , void * priv ,
struct v4l2_queryctrl * qc )
{
struct cafe_camera * cam = filp - > private_data ;
int ret ;
mutex_lock ( & cam - > s_mutex ) ;
ret = __cafe_cam_cmd ( cam , VIDIOC_QUERYCTRL , qc ) ;
mutex_unlock ( & cam - > s_mutex ) ;
return ret ;
}
static int cafe_vidioc_g_ctrl ( struct file * filp , void * priv ,
struct v4l2_control * ctrl )
{
struct cafe_camera * cam = filp - > private_data ;
int ret ;
mutex_lock ( & cam - > s_mutex ) ;
ret = __cafe_cam_cmd ( cam , VIDIOC_G_CTRL , ctrl ) ;
mutex_unlock ( & cam - > s_mutex ) ;
return ret ;
}
static int cafe_vidioc_s_ctrl ( struct file * filp , void * priv ,
struct v4l2_control * ctrl )
{
struct cafe_camera * cam = filp - > private_data ;
int ret ;
mutex_lock ( & cam - > s_mutex ) ;
ret = __cafe_cam_cmd ( cam , VIDIOC_S_CTRL , ctrl ) ;
mutex_unlock ( & cam - > s_mutex ) ;
return ret ;
}
static int cafe_vidioc_querycap ( struct file * file , void * priv ,
struct v4l2_capability * cap )
{
strcpy ( cap - > driver , " cafe_ccic " ) ;
strcpy ( cap - > card , " cafe_ccic " ) ;
cap - > version = CAFE_VERSION ;
cap - > capabilities = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE | V4L2_CAP_STREAMING ;
return 0 ;
}
/*
* The default format we use until somebody says otherwise .
*/
static struct v4l2_pix_format cafe_def_pix_format = {
. width = VGA_WIDTH ,
. height = VGA_HEIGHT ,
. pixelformat = V4L2_PIX_FMT_YUYV ,
. field = V4L2_FIELD_NONE ,
. bytesperline = VGA_WIDTH * 2 ,
. sizeimage = VGA_WIDTH * VGA_HEIGHT * 2 ,
} ;
static int cafe_vidioc_enum_fmt_cap ( struct file * filp ,
void * priv , struct v4l2_fmtdesc * fmt )
{
struct cafe_camera * cam = priv ;
int ret ;
if ( fmt - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE )
return - EINVAL ;
mutex_lock ( & cam - > s_mutex ) ;
ret = __cafe_cam_cmd ( cam , VIDIOC_ENUM_FMT , fmt ) ;
mutex_unlock ( & cam - > s_mutex ) ;
return ret ;
}
static int cafe_vidioc_try_fmt_cap ( struct file * filp , void * priv ,
struct v4l2_format * fmt )
{
struct cafe_camera * cam = priv ;
int ret ;
mutex_lock ( & cam - > s_mutex ) ;
ret = __cafe_cam_cmd ( cam , VIDIOC_TRY_FMT , fmt ) ;
mutex_unlock ( & cam - > s_mutex ) ;
return ret ;
}
static int cafe_vidioc_s_fmt_cap ( struct file * filp , void * priv ,
struct v4l2_format * fmt )
{
struct cafe_camera * cam = priv ;
int ret ;
/*
* Can ' t do anything if the device is not idle
* Also can ' t if there are streaming buffers in place .
*/
if ( cam - > state ! = S_IDLE | | cam - > n_sbufs > 0 )
return - EBUSY ;
/*
* See if the formatting works in principle .
*/
ret = cafe_vidioc_try_fmt_cap ( filp , priv , fmt ) ;
if ( ret )
return ret ;
/*
* Now we start to change things for real , so let ' s do it
* under lock .
*/
mutex_lock ( & cam - > s_mutex ) ;
cam - > pix_format = fmt - > fmt . pix ;
/*
* Make sure we have appropriate DMA buffers .
*/
ret = - ENOMEM ;
if ( cam - > nbufs > 0 & & cam - > dma_buf_size < cam - > pix_format . sizeimage )
cafe_free_dma_bufs ( cam ) ;
if ( cam - > nbufs = = 0 ) {
if ( cafe_alloc_dma_bufs ( cam , 0 ) )
goto out ;
}
/*
* It looks like this might work , so let ' s program the sensor .
*/
ret = cafe_cam_configure ( cam ) ;
if ( ! ret )
ret = cafe_ctlr_configure ( cam ) ;
out :
mutex_unlock ( & cam - > s_mutex ) ;
return ret ;
}
/*
* Return our stored notion of how the camera is / should be configured .
* The V4l2 spec wants us to be smarter , and actually get this from
* the camera ( and not mess with it at open time ) . Someday .
*/
static int cafe_vidioc_g_fmt_cap ( struct file * filp , void * priv ,
struct v4l2_format * f )
{
struct cafe_camera * cam = priv ;
f - > fmt . pix = cam - > pix_format ;
return 0 ;
}
/*
* We only have one input - the sensor - so minimize the nonsense here .
*/
static int cafe_vidioc_enum_input ( struct file * filp , void * priv ,
struct v4l2_input * input )
{
if ( input - > index ! = 0 )
return - EINVAL ;
input - > type = V4L2_INPUT_TYPE_CAMERA ;
input - > std = V4L2_STD_ALL ; /* Not sure what should go here */
strcpy ( input - > name , " Camera " ) ;
return 0 ;
}
static int cafe_vidioc_g_input ( struct file * filp , void * priv , unsigned int * i )
{
* i = 0 ;
return 0 ;
}
static int cafe_vidioc_s_input ( struct file * filp , void * priv , unsigned int i )
{
if ( i ! = 0 )
return - EINVAL ;
return 0 ;
}
/* from vivi.c */
2006-11-20 19:19:20 +03:00
static int cafe_vidioc_s_std ( struct file * filp , void * priv , v4l2_std_id * a )
2006-11-04 15:25:53 +03:00
{
return 0 ;
}
2006-12-01 21:50:59 +03:00
/*
* G / S_PARM . Most of this is done by the sensor , but we are
* the level which controls the number of read buffers .
*/
static int cafe_vidioc_g_parm ( struct file * filp , void * priv ,
struct v4l2_streamparm * parms )
{
struct cafe_camera * cam = priv ;
int ret ;
mutex_lock ( & cam - > s_mutex ) ;
ret = __cafe_cam_cmd ( cam , VIDIOC_G_PARM , parms ) ;
mutex_unlock ( & cam - > s_mutex ) ;
parms - > parm . capture . readbuffers = n_dma_bufs ;
return ret ;
}
static int cafe_vidioc_s_parm ( struct file * filp , void * priv ,
struct v4l2_streamparm * parms )
{
struct cafe_camera * cam = priv ;
int ret ;
mutex_lock ( & cam - > s_mutex ) ;
ret = __cafe_cam_cmd ( cam , VIDIOC_S_PARM , parms ) ;
mutex_unlock ( & cam - > s_mutex ) ;
parms - > parm . capture . readbuffers = n_dma_bufs ;
return ret ;
}
2006-11-17 17:59:22 +03:00
static void cafe_v4l_dev_release ( struct video_device * vd )
2006-11-04 15:25:53 +03:00
{
struct cafe_camera * cam = container_of ( vd , struct cafe_camera , v4ldev ) ;
kfree ( cam ) ;
}
/*
* This template device holds all of those v4l2 methods ; we
* clone it for specific real devices .
*/
2007-02-12 11:55:33 +03:00
static const struct file_operations cafe_v4l_fops = {
2006-11-04 15:25:53 +03:00
. owner = THIS_MODULE ,
. open = cafe_v4l_open ,
. release = cafe_v4l_release ,
. read = cafe_v4l_read ,
. poll = cafe_v4l_poll ,
. mmap = cafe_v4l_mmap ,
. ioctl = video_ioctl2 ,
. llseek = no_llseek ,
} ;
static struct video_device cafe_v4l_template = {
. name = " cafe " ,
. type = VFL_TYPE_GRABBER ,
. type2 = VID_TYPE_CAPTURE ,
. minor = - 1 , /* Get one dynamically */
2006-11-20 19:19:20 +03:00
. tvnorms = V4L2_STD_NTSC_M ,
2006-11-04 15:25:53 +03:00
. current_norm = V4L2_STD_NTSC_M , /* make mplayer happy */
. fops = & cafe_v4l_fops ,
. release = cafe_v4l_dev_release ,
. vidioc_querycap = cafe_vidioc_querycap ,
. vidioc_enum_fmt_cap = cafe_vidioc_enum_fmt_cap ,
. vidioc_try_fmt_cap = cafe_vidioc_try_fmt_cap ,
. vidioc_s_fmt_cap = cafe_vidioc_s_fmt_cap ,
. vidioc_g_fmt_cap = cafe_vidioc_g_fmt_cap ,
. vidioc_enum_input = cafe_vidioc_enum_input ,
. vidioc_g_input = cafe_vidioc_g_input ,
. vidioc_s_input = cafe_vidioc_s_input ,
. vidioc_s_std = cafe_vidioc_s_std ,
. vidioc_reqbufs = cafe_vidioc_reqbufs ,
. vidioc_querybuf = cafe_vidioc_querybuf ,
. vidioc_qbuf = cafe_vidioc_qbuf ,
. vidioc_dqbuf = cafe_vidioc_dqbuf ,
. vidioc_streamon = cafe_vidioc_streamon ,
. vidioc_streamoff = cafe_vidioc_streamoff ,
. vidioc_queryctrl = cafe_vidioc_queryctrl ,
. vidioc_g_ctrl = cafe_vidioc_g_ctrl ,
. vidioc_s_ctrl = cafe_vidioc_s_ctrl ,
2006-12-01 21:50:59 +03:00
. vidioc_g_parm = cafe_vidioc_g_parm ,
. vidioc_s_parm = cafe_vidioc_s_parm ,
2006-11-04 15:25:53 +03:00
} ;
/* ---------------------------------------------------------------------- */
/*
* Interrupt handler stuff
*/
static void cafe_frame_tasklet ( unsigned long data )
{
struct cafe_camera * cam = ( struct cafe_camera * ) data ;
int i ;
unsigned long flags ;
struct cafe_sio_buffer * sbuf ;
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
for ( i = 0 ; i < cam - > nbufs ; i + + ) {
int bufno = cam - > next_buf ;
if ( bufno < 0 ) { /* "will never happen" */
cam_err ( cam , " No valid bufs in tasklet! \n " ) ;
break ;
}
if ( + + ( cam - > next_buf ) > = cam - > nbufs )
cam - > next_buf = 0 ;
if ( ! test_bit ( bufno , & cam - > flags ) )
continue ;
if ( list_empty ( & cam - > sb_avail ) )
break ; /* Leave it valid, hope for better later */
clear_bit ( bufno , & cam - > flags ) ;
sbuf = list_entry ( cam - > sb_avail . next ,
struct cafe_sio_buffer , list ) ;
2007-04-27 19:32:28 +04:00
/*
* Drop the lock during the big copy . This * should * be safe . . .
*/
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
2006-12-01 21:37:49 +03:00
memcpy ( sbuf - > buffer , cam - > dma_bufs [ bufno ] ,
cam - > pix_format . sizeimage ) ;
2006-11-04 15:25:53 +03:00
sbuf - > v4lbuf . bytesused = cam - > pix_format . sizeimage ;
sbuf - > v4lbuf . sequence = cam - > buf_seq [ bufno ] ;
sbuf - > v4lbuf . flags & = ~ V4L2_BUF_FLAG_QUEUED ;
sbuf - > v4lbuf . flags | = V4L2_BUF_FLAG_DONE ;
2007-04-27 19:32:28 +04:00
spin_lock_irqsave ( & cam - > dev_lock , flags ) ;
2006-11-04 15:25:53 +03:00
list_move_tail ( & sbuf - > list , & cam - > sb_full ) ;
}
if ( ! list_empty ( & cam - > sb_full ) )
wake_up ( & cam - > iowait ) ;
spin_unlock_irqrestore ( & cam - > dev_lock , flags ) ;
}
static void cafe_frame_complete ( struct cafe_camera * cam , int frame )
{
/*
* Basic frame housekeeping .
*/
if ( test_bit ( frame , & cam - > flags ) & & printk_ratelimit ( ) )
cam_err ( cam , " Frame overrun on %d, frames lost \n " , frame ) ;
set_bit ( frame , & cam - > flags ) ;
clear_bit ( CF_DMA_ACTIVE , & cam - > flags ) ;
if ( cam - > next_buf < 0 )
cam - > next_buf = frame ;
cam - > buf_seq [ frame ] = + + ( cam - > sequence ) ;
switch ( cam - > state ) {
/*
* If in single read mode , try going speculative .
*/
case S_SINGLEREAD :
cam - > state = S_SPECREAD ;
cam - > specframes = 0 ;
wake_up ( & cam - > iowait ) ;
break ;
/*
* If we are already doing speculative reads , and nobody is
* reading them , just stop .
*/
case S_SPECREAD :
if ( + + ( cam - > specframes ) > = cam - > nbufs ) {
cafe_ctlr_stop ( cam ) ;
cafe_ctlr_irq_disable ( cam ) ;
cam - > state = S_IDLE ;
}
wake_up ( & cam - > iowait ) ;
break ;
/*
* For the streaming case , we defer the real work to the
* camera tasklet .
*
* FIXME : if the application is not consuming the buffers ,
* we should eventually put things on hold and restart in
* vidioc_dqbuf ( ) .
*/
case S_STREAMING :
tasklet_schedule ( & cam - > s_tasklet ) ;
break ;
default :
cam_err ( cam , " Frame interrupt in non-operational state \n " ) ;
break ;
}
}
static void cafe_frame_irq ( struct cafe_camera * cam , unsigned int irqs )
{
unsigned int frame ;
cafe_reg_write ( cam , REG_IRQSTAT , FRAMEIRQS ) ; /* Clear'em all */
/*
* Handle any frame completions . There really should
* not be more than one of these , or we have fallen
* far behind .
*/
for ( frame = 0 ; frame < cam - > nbufs ; frame + + )
if ( irqs & ( IRQ_EOF0 < < frame ) )
cafe_frame_complete ( cam , frame ) ;
/*
* If a frame starts , note that we have DMA active . This
* code assumes that we won ' t get multiple frame interrupts
* at once ; may want to rethink that .
*/
if ( irqs & ( IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2 ) )
set_bit ( CF_DMA_ACTIVE , & cam - > flags ) ;
}
static irqreturn_t cafe_irq ( int irq , void * data )
{
struct cafe_camera * cam = data ;
unsigned int irqs ;
spin_lock ( & cam - > dev_lock ) ;
irqs = cafe_reg_read ( cam , REG_IRQSTAT ) ;
if ( ( irqs & ALLIRQS ) = = 0 ) {
spin_unlock ( & cam - > dev_lock ) ;
return IRQ_NONE ;
}
if ( irqs & FRAMEIRQS )
cafe_frame_irq ( cam , irqs ) ;
if ( irqs & TWSIIRQS ) {
cafe_reg_write ( cam , REG_IRQSTAT , TWSIIRQS ) ;
wake_up ( & cam - > smbus_wait ) ;
}
spin_unlock ( & cam - > dev_lock ) ;
return IRQ_HANDLED ;
}
/* -------------------------------------------------------------------------- */
# ifdef CONFIG_VIDEO_ADV_DEBUG
/*
* Debugfs stuff .
*/
static char cafe_debug_buf [ 1024 ] ;
static struct dentry * cafe_dfs_root ;
static void cafe_dfs_setup ( void )
{
cafe_dfs_root = debugfs_create_dir ( " cafe_ccic " , NULL ) ;
if ( IS_ERR ( cafe_dfs_root ) ) {
cafe_dfs_root = NULL ; /* Never mind */
printk ( KERN_NOTICE " cafe_ccic unable to set up debugfs \n " ) ;
}
}
static void cafe_dfs_shutdown ( void )
{
if ( cafe_dfs_root )
debugfs_remove ( cafe_dfs_root ) ;
}
static int cafe_dfs_open ( struct inode * inode , struct file * file )
{
file - > private_data = inode - > i_private ;
return 0 ;
}
static ssize_t cafe_dfs_read_regs ( struct file * file ,
char __user * buf , size_t count , loff_t * ppos )
{
struct cafe_camera * cam = file - > private_data ;
char * s = cafe_debug_buf ;
int offset ;
for ( offset = 0 ; offset < 0x44 ; offset + = 4 )
s + = sprintf ( s , " %02x: %08x \n " , offset ,
cafe_reg_read ( cam , offset ) ) ;
for ( offset = 0x88 ; offset < = 0x90 ; offset + = 4 )
s + = sprintf ( s , " %02x: %08x \n " , offset ,
cafe_reg_read ( cam , offset ) ) ;
for ( offset = 0xb4 ; offset < = 0xbc ; offset + = 4 )
s + = sprintf ( s , " %02x: %08x \n " , offset ,
cafe_reg_read ( cam , offset ) ) ;
for ( offset = 0x3000 ; offset < = 0x300c ; offset + = 4 )
s + = sprintf ( s , " %04x: %08x \n " , offset ,
cafe_reg_read ( cam , offset ) ) ;
return simple_read_from_buffer ( buf , count , ppos , cafe_debug_buf ,
s - cafe_debug_buf ) ;
}
2007-02-12 11:55:33 +03:00
static const struct file_operations cafe_dfs_reg_ops = {
2006-11-04 15:25:53 +03:00
. owner = THIS_MODULE ,
. read = cafe_dfs_read_regs ,
. open = cafe_dfs_open
} ;
static ssize_t cafe_dfs_read_cam ( struct file * file ,
char __user * buf , size_t count , loff_t * ppos )
{
struct cafe_camera * cam = file - > private_data ;
char * s = cafe_debug_buf ;
int offset ;
if ( ! cam - > sensor )
return - EINVAL ;
for ( offset = 0x0 ; offset < 0x8a ; offset + + )
{
u8 v ;
cafe_smbus_read_data ( cam , cam - > sensor - > addr , offset , & v ) ;
s + = sprintf ( s , " %02x: %02x \n " , offset , v ) ;
}
return simple_read_from_buffer ( buf , count , ppos , cafe_debug_buf ,
s - cafe_debug_buf ) ;
}
2007-02-12 11:55:33 +03:00
static const struct file_operations cafe_dfs_cam_ops = {
2006-11-04 15:25:53 +03:00
. owner = THIS_MODULE ,
. read = cafe_dfs_read_cam ,
. open = cafe_dfs_open
} ;
static void cafe_dfs_cam_setup ( struct cafe_camera * cam )
{
char fname [ 40 ] ;
if ( ! cafe_dfs_root )
return ;
sprintf ( fname , " regs-%d " , cam - > v4ldev . minor ) ;
cam - > dfs_regs = debugfs_create_file ( fname , 0444 , cafe_dfs_root ,
cam , & cafe_dfs_reg_ops ) ;
sprintf ( fname , " cam-%d " , cam - > v4ldev . minor ) ;
cam - > dfs_cam_regs = debugfs_create_file ( fname , 0444 , cafe_dfs_root ,
cam , & cafe_dfs_cam_ops ) ;
}
static void cafe_dfs_cam_shutdown ( struct cafe_camera * cam )
{
if ( ! IS_ERR ( cam - > dfs_regs ) )
debugfs_remove ( cam - > dfs_regs ) ;
if ( ! IS_ERR ( cam - > dfs_cam_regs ) )
debugfs_remove ( cam - > dfs_cam_regs ) ;
}
# else
# define cafe_dfs_setup()
# define cafe_dfs_shutdown()
# define cafe_dfs_cam_setup(cam)
# define cafe_dfs_cam_shutdown(cam)
# endif /* CONFIG_VIDEO_ADV_DEBUG */
/* ------------------------------------------------------------------------*/
/*
* PCI interface stuff .
*/
static int cafe_pci_probe ( struct pci_dev * pdev ,
const struct pci_device_id * id )
{
int ret ;
u16 classword ;
struct cafe_camera * cam ;
/*
* Make sure we have a camera here - we ' ll get calls for
* the other cafe devices as well .
*/
pci_read_config_word ( pdev , PCI_CLASS_DEVICE , & classword ) ;
if ( classword ! = PCI_CLASS_MULTIMEDIA_VIDEO )
return - ENODEV ;
/*
* Start putting together one of our big camera structures .
*/
ret = - ENOMEM ;
cam = kzalloc ( sizeof ( struct cafe_camera ) , GFP_KERNEL ) ;
if ( cam = = NULL )
goto out ;
mutex_init ( & cam - > s_mutex ) ;
mutex_lock ( & cam - > s_mutex ) ;
spin_lock_init ( & cam - > dev_lock ) ;
cam - > state = S_NOTREADY ;
cafe_set_config_needed ( cam , 1 ) ;
init_waitqueue_head ( & cam - > smbus_wait ) ;
init_waitqueue_head ( & cam - > iowait ) ;
cam - > pdev = pdev ;
cam - > pix_format = cafe_def_pix_format ;
INIT_LIST_HEAD ( & cam - > dev_list ) ;
INIT_LIST_HEAD ( & cam - > sb_avail ) ;
INIT_LIST_HEAD ( & cam - > sb_full ) ;
tasklet_init ( & cam - > s_tasklet , cafe_frame_tasklet , ( unsigned long ) cam ) ;
/*
* Get set up on the PCI bus .
*/
ret = pci_enable_device ( pdev ) ;
if ( ret )
goto out_free ;
pci_set_master ( pdev ) ;
ret = - EIO ;
cam - > regs = pci_iomap ( pdev , 0 , 0 ) ;
if ( ! cam - > regs ) {
printk ( KERN_ERR " Unable to ioremap cafe-ccic regs \n " ) ;
goto out_free ;
}
ret = request_irq ( pdev - > irq , cafe_irq , IRQF_SHARED , " cafe-ccic " , cam ) ;
if ( ret )
goto out_iounmap ;
2007-05-22 07:37:58 +04:00
/*
* Initialize the controller and leave it powered up . It will
* stay that way until the sensor driver shows up .
*/
2006-11-04 15:25:53 +03:00
cafe_ctlr_init ( cam ) ;
cafe_ctlr_power_up ( cam ) ;
/*
2007-05-22 07:37:58 +04:00
* Set up I2C / SMBUS communications . We have to drop the mutex here
* because the sensor could attach in this call chain , leading to
* unsightly deadlocks .
2006-11-04 15:25:53 +03:00
*/
mutex_unlock ( & cam - > s_mutex ) ; /* attach can deadlock */
ret = cafe_smbus_setup ( cam ) ;
if ( ret )
goto out_freeirq ;
/*
* Get the v4l2 setup done .
*/
mutex_lock ( & cam - > s_mutex ) ;
cam - > v4ldev = cafe_v4l_template ;
cam - > v4ldev . debug = 0 ;
// cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG;
2007-03-25 18:35:56 +04:00
cam - > v4ldev . dev = & pdev - > dev ;
2006-11-04 15:25:53 +03:00
ret = video_register_device ( & cam - > v4ldev , VFL_TYPE_GRABBER , - 1 ) ;
if ( ret )
goto out_smbus ;
/*
* If so requested , try to get our DMA buffers now .
*/
2007-09-19 09:44:18 +04:00
if ( ! alloc_bufs_at_read ) {
2006-11-04 15:25:53 +03:00
if ( cafe_alloc_dma_bufs ( cam , 1 ) )
cam_warn ( cam , " Unable to alloc DMA buffers at load "
" will try again later. " ) ;
}
cafe_dfs_cam_setup ( cam ) ;
mutex_unlock ( & cam - > s_mutex ) ;
cafe_add_dev ( cam ) ;
return 0 ;
out_smbus :
cafe_smbus_shutdown ( cam ) ;
out_freeirq :
cafe_ctlr_power_down ( cam ) ;
free_irq ( pdev - > irq , cam ) ;
out_iounmap :
pci_iounmap ( pdev , cam - > regs ) ;
out_free :
kfree ( cam ) ;
out :
return ret ;
}
/*
* Shut down an initialized device
*/
static void cafe_shutdown ( struct cafe_camera * cam )
{
/* FIXME: Make sure we take care of everything here */
cafe_dfs_cam_shutdown ( cam ) ;
if ( cam - > n_sbufs > 0 )
/* What if they are still mapped? Shouldn't be, but... */
cafe_free_sio_buffers ( cam ) ;
cafe_remove_dev ( cam ) ;
cafe_ctlr_stop_dma ( cam ) ;
cafe_ctlr_power_down ( cam ) ;
cafe_smbus_shutdown ( cam ) ;
cafe_free_dma_bufs ( cam ) ;
free_irq ( cam - > pdev - > irq , cam ) ;
pci_iounmap ( cam - > pdev , cam - > regs ) ;
video_unregister_device ( & cam - > v4ldev ) ;
/* kfree(cam); done in v4l_release () */
}
static void cafe_pci_remove ( struct pci_dev * pdev )
{
struct cafe_camera * cam = cafe_find_by_pdev ( pdev ) ;
if ( cam = = NULL ) {
2006-12-20 15:34:32 +03:00
printk ( KERN_WARNING " pci_remove on unknown pdev %p \n " , pdev ) ;
2006-11-04 15:25:53 +03:00
return ;
}
mutex_lock ( & cam - > s_mutex ) ;
if ( cam - > users > 0 )
cam_warn ( cam , " Removing a device with users! \n " ) ;
cafe_shutdown ( cam ) ;
/* No unlock - it no longer exists */
}
2007-03-25 18:36:28 +04:00
# ifdef CONFIG_PM
/*
* Basic power management .
*/
static int cafe_pci_suspend ( struct pci_dev * pdev , pm_message_t state )
{
struct cafe_camera * cam = cafe_find_by_pdev ( pdev ) ;
int ret ;
2007-10-24 00:30:27 +04:00
enum cafe_state cstate ;
2007-03-25 18:36:28 +04:00
ret = pci_save_state ( pdev ) ;
if ( ret )
return ret ;
2007-10-24 00:30:27 +04:00
cstate = cam - > state ; /* HACK - stop_dma sets to idle */
2007-03-25 18:36:28 +04:00
cafe_ctlr_stop_dma ( cam ) ;
cafe_ctlr_power_down ( cam ) ;
pci_disable_device ( pdev ) ;
2007-10-24 00:30:27 +04:00
cam - > state = cstate ;
2007-03-25 18:36:28 +04:00
return 0 ;
}
static int cafe_pci_resume ( struct pci_dev * pdev )
{
struct cafe_camera * cam = cafe_find_by_pdev ( pdev ) ;
int ret = 0 ;
ret = pci_restore_state ( pdev ) ;
if ( ret )
return ret ;
2007-04-25 07:20:13 +04:00
ret = pci_enable_device ( pdev ) ;
2007-08-17 08:01:33 +04:00
2007-04-25 07:20:13 +04:00
if ( ret ) {
cam_warn ( cam , " Unable to re-enable device on resume! \n " ) ;
return ret ;
}
2007-03-25 18:36:28 +04:00
cafe_ctlr_init ( cam ) ;
2007-08-17 08:01:33 +04:00
cafe_ctlr_power_down ( cam ) ;
mutex_lock ( & cam - > s_mutex ) ;
if ( cam - > users > 0 ) {
cafe_ctlr_power_up ( cam ) ;
__cafe_cam_reset ( cam ) ;
}
mutex_unlock ( & cam - > s_mutex ) ;
2007-03-25 18:36:28 +04:00
set_bit ( CF_CONFIG_NEEDED , & cam - > flags ) ;
if ( cam - > state = = S_SPECREAD )
cam - > state = S_IDLE ; /* Don't bother restarting */
else if ( cam - > state = = S_SINGLEREAD | | cam - > state = = S_STREAMING )
ret = cafe_read_setup ( cam , cam - > state ) ;
return ret ;
}
# endif /* CONFIG_PM */
2006-11-04 15:25:53 +03:00
static struct pci_device_id cafe_ids [ ] = {
{ PCI_DEVICE ( 0x11ab , 0x4100 ) } , /* Eventual real ID */
{ PCI_DEVICE ( 0x11ab , 0x4102 ) } , /* Really eventual real ID */
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , cafe_ids ) ;
static struct pci_driver cafe_pci_driver = {
. name = " cafe1000-ccic " ,
. id_table = cafe_ids ,
. probe = cafe_pci_probe ,
. remove = cafe_pci_remove ,
2007-03-25 18:36:28 +04:00
# ifdef CONFIG_PM
. suspend = cafe_pci_suspend ,
. resume = cafe_pci_resume ,
# endif
2006-11-04 15:25:53 +03:00
} ;
static int __init cafe_init ( void )
{
int ret ;
printk ( KERN_NOTICE " Marvell M88ALP01 'CAFE' Camera Controller version %d \n " ,
CAFE_VERSION ) ;
cafe_dfs_setup ( ) ;
ret = pci_register_driver ( & cafe_pci_driver ) ;
if ( ret ) {
printk ( KERN_ERR " Unable to register cafe_ccic driver \n " ) ;
goto out ;
}
request_module ( " ov7670 " ) ; /* FIXME want something more general */
ret = 0 ;
out :
return ret ;
}
static void __exit cafe_exit ( void )
{
pci_unregister_driver ( & cafe_pci_driver ) ;
cafe_dfs_shutdown ( ) ;
}
module_init ( cafe_init ) ;
module_exit ( cafe_exit ) ;