mirror of
				https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
				synced 2025-10-31 09:36:25 +00:00 
			
		
		
		
	 e6574f2fbe
			
		
	
	
		e6574f2fbe
		
	
	
	
	
		
			
			The functions v4l2_i2c_new_subdev and v4l2_i2c_new_probed_subdev relied on i2c_get_adapdata to return the v4l2_device. However, this is not always possible on embedded platforms. So modify the API to pass the v4l2_device pointer explicitly. Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
		
			
				
	
	
		
			2152 lines
		
	
	
		
			52 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2152 lines
		
	
	
		
			52 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * A driver for the CMOS camera controller in the Marvell 88ALP01 "cafe"
 | |
|  * multifunction chip.  Currently works with the Omnivision OV7670
 | |
|  * sensor.
 | |
|  *
 | |
|  * The data sheet for this device can be found at:
 | |
|  *    http://www.marvell.com/products/pcconn/88ALP01.jsp
 | |
|  *
 | |
|  * Copyright 2006 One Laptop Per Child Association, Inc.
 | |
|  * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
 | |
|  *
 | |
|  * Written by Jonathan Corbet, corbet@lwn.net.
 | |
|  *
 | |
|  * v4l2_device/v4l2_subdev conversion by:
 | |
|  * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
 | |
|  *
 | |
|  * Note: this conversion is untested! Please contact the linux-media
 | |
|  * mailinglist if you can test this, together with the test results.
 | |
|  *
 | |
|  * 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/mm.h>
 | |
| #include <linux/pci.h>
 | |
| #include <linux/i2c.h>
 | |
| #include <linux/interrupt.h>
 | |
| #include <linux/spinlock.h>
 | |
| #include <linux/videodev2.h>
 | |
| #include <media/v4l2-device.h>
 | |
| #include <media/v4l2-ioctl.h>
 | |
| #include <media/v4l2-chip-ident.h>
 | |
| #include <linux/device.h>
 | |
| #include <linux/wait.h>
 | |
| #include <linux/list.h>
 | |
| #include <linux/dma-mapping.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/jiffies.h>
 | |
| #include <linux/vmalloc.h>
 | |
| 
 | |
| #include <asm/uaccess.h>
 | |
| #include <asm/io.h>
 | |
| 
 | |
| #include "cafe_ccic-regs.h"
 | |
| 
 | |
| #define CAFE_VERSION 0x000002
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * 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
 | |
| static int alloc_bufs_at_read;
 | |
| 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.");
 | |
| 
 | |
| 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.");
 | |
| 
 | |
| static int flip;
 | |
| 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
 | |
| {
 | |
| 	struct v4l2_device v4l2_dev;
 | |
| 	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 vdev;
 | |
| 	struct i2c_adapter i2c_adapter;
 | |
| 	struct v4l2_subdev *sensor;
 | |
| 	unsigned short sensor_addr;
 | |
| 
 | |
| 	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 */
 | |
| 	u32 sensor_type;		/* Currently ov7670 only */
 | |
| 	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 */
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * 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 */
 | |
| 
 | |
| #define sensor_call(cam, o, f, args...) \
 | |
| 	v4l2_subdev_call(cam->sensor, o, f, ##args)
 | |
| 
 | |
| static inline struct cafe_camera *to_cam(struct v4l2_device *dev)
 | |
| {
 | |
| 	return container_of(dev, struct cafe_camera, v4l2_dev);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * 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);
 | |
| 
 | |
| 
 | |
| /* ---------------------------------------------------------------------*/
 | |
| 
 | |
| /*
 | |
|  * 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;
 | |
| 	DEFINE_WAIT(the_wait);
 | |
| 
 | |
| 	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);
 | |
| 
 | |
| 	/*
 | |
| 	 * 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
 | |
| 	wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(cam),
 | |
| 			CAFE_SMBUS_TIMEOUT);
 | |
| #endif
 | |
| 	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 v4l2_device *v4l2_dev = i2c_get_adapdata(adapter);
 | |
| 	struct cafe_camera *cam = to_cam(v4l2_dev);
 | |
| 	int ret = -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 void cafe_ctlr_stop_dma(struct cafe_camera *cam);
 | |
| static void cafe_ctlr_power_down(struct cafe_camera *cam);
 | |
| 
 | |
| 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->owner = THIS_MODULE;
 | |
| 	adap->algo = &cafe_smbus_algo;
 | |
| 	strcpy(adap->name, "cafe_ccic");
 | |
| 	adap->dev.parent = &cam->pdev->dev;
 | |
| 	i2c_set_adapdata(adap, &cam->v4l2_dev);
 | |
| 	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);
 | |
| 	/*
 | |
| 	 * Here we must wait a bit for the controller to come around.
 | |
| 	 */
 | |
| 	spin_unlock_irqrestore(&cam->dev_lock, flags);
 | |
| 	msleep(5);
 | |
| 	spin_lock_irqsave(&cam->dev_lock, flags);
 | |
| 
 | |
| 	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 48MHz, 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);
 | |
| 	/*
 | |
| 	 * 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);
 | |
| 	/*
 | |
| 	 * 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.
 | |
| 	 */
 | |
| 	cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
 | |
| /*	mdelay(1); */ /* Marvell says 1ms will do it */
 | |
| 	cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
 | |
| /*	mdelay(1); */ /* Enough? */
 | |
| 	spin_unlock_irqrestore(&cam->dev_lock, flags);
 | |
| 	msleep(5); /* Just to be sure */
 | |
| }
 | |
| 
 | |
| 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);
 | |
| 	cafe_reg_write(cam, REG_GL_FCR, GFCR_GPIO_ON);
 | |
| 	cafe_reg_write(cam, REG_GL_GPIOR, GGPIO_OUT);
 | |
| 	cafe_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN);
 | |
| 	spin_unlock_irqrestore(&cam->dev_lock, flags);
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /*
 | |
|  * Communications with the sensor.
 | |
|  */
 | |
| 
 | |
| static int __cafe_cam_reset(struct cafe_camera *cam)
 | |
| {
 | |
| 	return sensor_call(cam, core, reset, 0);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * We have found the sensor on the i2c.  Let's try to have a
 | |
|  * conversation.
 | |
|  */
 | |
| static int cafe_cam_init(struct cafe_camera *cam)
 | |
| {
 | |
| 	struct v4l2_dbg_chip_ident chip;
 | |
| 	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;
 | |
| 	chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR;
 | |
| 	chip.match.addr = cam->sensor_addr;
 | |
| 	ret = sensor_call(cam, core, g_chip_ident, &chip);
 | |
| 	if (ret)
 | |
| 		goto out;
 | |
| 	cam->sensor_type = chip.ident;
 | |
| 	if (cam->sensor_type != V4L2_IDENT_OV7670) {
 | |
| 		cam_err(cam, "Unsupported sensor type 0x%x", cam->sensor_type);
 | |
| 		ret = -EINVAL;
 | |
| 		goto out;
 | |
| 	}
 | |
| /* Get/set parameters? */
 | |
| 	ret = 0;
 | |
| 	cam->state = S_IDLE;
 | |
|   out:
 | |
| 	cafe_ctlr_power_down(cam);
 | |
| 	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 sensor_call(cam, core, s_ctrl, &ctrl);
 | |
| }
 | |
| 
 | |
| 
 | |
| static int cafe_cam_configure(struct cafe_camera *cam)
 | |
| {
 | |
| 	struct v4l2_format fmt;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (cam->state != S_IDLE)
 | |
| 		return -EINVAL;
 | |
| 	fmt.fmt.pix = cam->pix_format;
 | |
| 	ret = sensor_call(cam, core, init, 0);
 | |
| 	if (ret == 0)
 | |
| 		ret = sensor_call(cam, video, 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;
 | |
| 	else
 | |
| 		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;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	/*
 | |
| 	 * 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;
 | |
| 	/*
 | |
| 	 * Offset: must be 32-bit even on a 64-bit system.  videobuf-dma-sg
 | |
| 	 * 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;
 | |
| 	int ret = 0;  /* Silence warning */
 | |
| 
 | |
| 	/*
 | |
| 	 * 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->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->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->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 (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 file *filp)
 | |
| {
 | |
| 	struct cafe_camera *cam = video_drvdata(filp);
 | |
| 
 | |
| 	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 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;
 | |
| 	}
 | |
| 	if (cam->users == 0) {
 | |
| 		cafe_ctlr_power_down(cam);
 | |
| 		if (alloc_bufs_at_read)
 | |
| 			cafe_free_dma_bufs(cam);
 | |
| 	}
 | |
| 	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 = priv;
 | |
| 	int ret;
 | |
| 
 | |
| 	mutex_lock(&cam->s_mutex);
 | |
| 	ret = sensor_call(cam, core, 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 = priv;
 | |
| 	int ret;
 | |
| 
 | |
| 	mutex_lock(&cam->s_mutex);
 | |
| 	ret = sensor_call(cam, core, 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 = priv;
 | |
| 	int ret;
 | |
| 
 | |
| 	mutex_lock(&cam->s_mutex);
 | |
| 	ret = sensor_call(cam, core, 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_vid_cap(struct file *filp,
 | |
| 		void *priv, struct v4l2_fmtdesc *fmt)
 | |
| {
 | |
| 	struct cafe_camera *cam = priv;
 | |
| 	int ret;
 | |
| 
 | |
| 	mutex_lock(&cam->s_mutex);
 | |
| 	ret = sensor_call(cam, video, enum_fmt, fmt);
 | |
| 	mutex_unlock(&cam->s_mutex);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int cafe_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
 | |
| 		struct v4l2_format *fmt)
 | |
| {
 | |
| 	struct cafe_camera *cam = priv;
 | |
| 	int ret;
 | |
| 
 | |
| 	mutex_lock(&cam->s_mutex);
 | |
| 	ret = sensor_call(cam, video, try_fmt, fmt);
 | |
| 	mutex_unlock(&cam->s_mutex);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int cafe_vidioc_s_fmt_vid_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_vid_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_vid_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 */
 | |
| static int cafe_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 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 = sensor_call(cam, video, 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 = sensor_call(cam, video, s_parm, parms);
 | |
| 	mutex_unlock(&cam->s_mutex);
 | |
| 	parms->parm.capture.readbuffers = n_dma_bufs;
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int cafe_vidioc_g_chip_ident(struct file *file, void *priv,
 | |
| 		struct v4l2_dbg_chip_ident *chip)
 | |
| {
 | |
| 	struct cafe_camera *cam = priv;
 | |
| 
 | |
| 	chip->ident = V4L2_IDENT_NONE;
 | |
| 	chip->revision = 0;
 | |
| 	if (v4l2_chip_match_host(&chip->match)) {
 | |
| 		chip->ident = V4L2_IDENT_CAFE;
 | |
| 		return 0;
 | |
| 	}
 | |
| 	return sensor_call(cam, core, g_chip_ident, chip);
 | |
| }
 | |
| 
 | |
| #ifdef CONFIG_VIDEO_ADV_DEBUG
 | |
| static int cafe_vidioc_g_register(struct file *file, void *priv,
 | |
| 		struct v4l2_dbg_register *reg)
 | |
| {
 | |
| 	struct cafe_camera *cam = priv;
 | |
| 
 | |
| 	if (v4l2_chip_match_host(®->match)) {
 | |
| 		reg->val = cafe_reg_read(cam, reg->reg);
 | |
| 		reg->size = 4;
 | |
| 		return 0;
 | |
| 	}
 | |
| 	return sensor_call(cam, core, g_register, reg);
 | |
| }
 | |
| 
 | |
| static int cafe_vidioc_s_register(struct file *file, void *priv,
 | |
| 		struct v4l2_dbg_register *reg)
 | |
| {
 | |
| 	struct cafe_camera *cam = priv;
 | |
| 
 | |
| 	if (v4l2_chip_match_host(®->match)) {
 | |
| 		cafe_reg_write(cam, reg->reg, reg->val);
 | |
| 		return 0;
 | |
| 	}
 | |
| 	return sensor_call(cam, core, s_register, reg);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * This template device holds all of those v4l2 methods; we
 | |
|  * clone it for specific real devices.
 | |
|  */
 | |
| 
 | |
| static const struct v4l2_file_operations cafe_v4l_fops = {
 | |
| 	.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,
 | |
| };
 | |
| 
 | |
| static const struct v4l2_ioctl_ops cafe_v4l_ioctl_ops = {
 | |
| 	.vidioc_querycap 	= cafe_vidioc_querycap,
 | |
| 	.vidioc_enum_fmt_vid_cap = cafe_vidioc_enum_fmt_vid_cap,
 | |
| 	.vidioc_try_fmt_vid_cap	= cafe_vidioc_try_fmt_vid_cap,
 | |
| 	.vidioc_s_fmt_vid_cap	= cafe_vidioc_s_fmt_vid_cap,
 | |
| 	.vidioc_g_fmt_vid_cap	= cafe_vidioc_g_fmt_vid_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,
 | |
| 	.vidioc_g_parm		= cafe_vidioc_g_parm,
 | |
| 	.vidioc_s_parm		= cafe_vidioc_s_parm,
 | |
| 	.vidioc_g_chip_ident    = cafe_vidioc_g_chip_ident,
 | |
| #ifdef CONFIG_VIDEO_ADV_DEBUG
 | |
| 	.vidioc_g_register 	= cafe_vidioc_g_register,
 | |
| 	.vidioc_s_register 	= cafe_vidioc_s_register,
 | |
| #endif
 | |
| };
 | |
| 
 | |
| static struct video_device cafe_v4l_template = {
 | |
| 	.name = "cafe",
 | |
| 	.minor = -1, /* Get one dynamically */
 | |
| 	.tvnorms = V4L2_STD_NTSC_M,
 | |
| 	.current_norm = V4L2_STD_NTSC_M,  /* make mplayer happy */
 | |
| 
 | |
| 	.fops = &cafe_v4l_fops,
 | |
| 	.ioctl_ops = &cafe_v4l_ioctl_ops,
 | |
| 	.release = video_device_release_empty,
 | |
| };
 | |
| 
 | |
| 
 | |
| /* ---------------------------------------------------------------------- */
 | |
| /*
 | |
|  * 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);
 | |
| 		/*
 | |
| 		 * Drop the lock during the big copy.  This *should* be safe...
 | |
| 		 */
 | |
| 		spin_unlock_irqrestore(&cam->dev_lock, flags);
 | |
| 		memcpy(sbuf->buffer, cam->dma_bufs[bufno],
 | |
| 				cam->pix_format.sizeimage);
 | |
| 		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;
 | |
| 		spin_lock_irqsave(&cam->dev_lock, flags);
 | |
| 		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;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* -------------------------------------------------------------------------- */
 | |
| /*
 | |
|  * PCI interface stuff.
 | |
|  */
 | |
| 
 | |
| static int cafe_pci_probe(struct pci_dev *pdev,
 | |
| 		const struct pci_device_id *id)
 | |
| {
 | |
| 	int ret;
 | |
| 	struct cafe_camera *cam;
 | |
| 
 | |
| 	/*
 | |
| 	 * Start putting together one of our big camera structures.
 | |
| 	 */
 | |
| 	ret = -ENOMEM;
 | |
| 	cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL);
 | |
| 	if (cam == NULL)
 | |
| 		goto out;
 | |
| 	ret = v4l2_device_register(&pdev->dev, &cam->v4l2_dev);
 | |
| 	if (ret)
 | |
| 		goto out_free;
 | |
| 
 | |
| 	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_unreg;
 | |
| 	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_unreg;
 | |
| 	}
 | |
| 	ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam);
 | |
| 	if (ret)
 | |
| 		goto out_iounmap;
 | |
| 	/*
 | |
| 	 * Initialize the controller and leave it powered up.  It will
 | |
| 	 * stay that way until the sensor driver shows up.
 | |
| 	 */
 | |
| 	cafe_ctlr_init(cam);
 | |
| 	cafe_ctlr_power_up(cam);
 | |
| 	/*
 | |
| 	 * 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.
 | |
| 	 */
 | |
| 	mutex_unlock(&cam->s_mutex);  /* attach can deadlock */
 | |
| 	ret = cafe_smbus_setup(cam);
 | |
| 	if (ret)
 | |
| 		goto out_freeirq;
 | |
| 
 | |
| 	cam->sensor_addr = 0x42;
 | |
| 	cam->sensor = v4l2_i2c_new_subdev(&cam->v4l2_dev, &cam->i2c_adapter,
 | |
| 			"ov7670", "ov7670", cam->sensor_addr);
 | |
| 	if (cam->sensor == NULL) {
 | |
| 		ret = -ENODEV;
 | |
| 		goto out_smbus;
 | |
| 	}
 | |
| 	ret = cafe_cam_init(cam);
 | |
| 	if (ret)
 | |
| 		goto out_smbus;
 | |
| 
 | |
| 	/*
 | |
| 	 * Get the v4l2 setup done.
 | |
| 	 */
 | |
| 	mutex_lock(&cam->s_mutex);
 | |
| 	cam->vdev = cafe_v4l_template;
 | |
| 	cam->vdev.debug = 0;
 | |
| /*	cam->vdev.debug = V4L2_DEBUG_IOCTL_ARG;*/
 | |
| 	cam->vdev.v4l2_dev = &cam->v4l2_dev;
 | |
| 	ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
 | |
| 	if (ret)
 | |
| 		goto out_smbus;
 | |
| 	video_set_drvdata(&cam->vdev, cam);
 | |
| 
 | |
| 	/*
 | |
| 	 * If so requested, try to get our DMA buffers now.
 | |
| 	 */
 | |
| 	if (!alloc_bufs_at_read) {
 | |
| 		if (cafe_alloc_dma_bufs(cam, 1))
 | |
| 			cam_warn(cam, "Unable to alloc DMA buffers at load"
 | |
| 					" will try again later.");
 | |
| 	}
 | |
| 
 | |
| 	mutex_unlock(&cam->s_mutex);
 | |
| 	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:
 | |
| 	v4l2_device_unregister(&cam->v4l2_dev);
 | |
| out_unreg:
 | |
| 	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 */
 | |
| 	if (cam->n_sbufs > 0)
 | |
| 		/* What if they are still mapped?  Shouldn't be, but... */
 | |
| 		cafe_free_sio_buffers(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->vdev);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void cafe_pci_remove(struct pci_dev *pdev)
 | |
| {
 | |
| 	struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
 | |
| 	struct cafe_camera *cam = to_cam(v4l2_dev);
 | |
| 
 | |
| 	if (cam == NULL) {
 | |
| 		printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev);
 | |
| 		return;
 | |
| 	}
 | |
| 	mutex_lock(&cam->s_mutex);
 | |
| 	if (cam->users > 0)
 | |
| 		cam_warn(cam, "Removing a device with users!\n");
 | |
| 	cafe_shutdown(cam);
 | |
| 	v4l2_device_unregister(&cam->v4l2_dev);
 | |
| 	kfree(cam);
 | |
| /* No unlock - it no longer exists */
 | |
| }
 | |
| 
 | |
| 
 | |
| #ifdef CONFIG_PM
 | |
| /*
 | |
|  * Basic power management.
 | |
|  */
 | |
| static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state)
 | |
| {
 | |
| 	struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
 | |
| 	struct cafe_camera *cam = to_cam(v4l2_dev);
 | |
| 	int ret;
 | |
| 	enum cafe_state cstate;
 | |
| 
 | |
| 	ret = pci_save_state(pdev);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	cstate = cam->state; /* HACK - stop_dma sets to idle */
 | |
| 	cafe_ctlr_stop_dma(cam);
 | |
| 	cafe_ctlr_power_down(cam);
 | |
| 	pci_disable_device(pdev);
 | |
| 	cam->state = cstate;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int cafe_pci_resume(struct pci_dev *pdev)
 | |
| {
 | |
| 	struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
 | |
| 	struct cafe_camera *cam = to_cam(v4l2_dev);
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	ret = pci_restore_state(pdev);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = pci_enable_device(pdev);
 | |
| 
 | |
| 	if (ret) {
 | |
| 		cam_warn(cam, "Unable to re-enable device on resume!\n");
 | |
| 		return ret;
 | |
| 	}
 | |
| 	cafe_ctlr_init(cam);
 | |
| 	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);
 | |
| 
 | |
| 	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 */
 | |
| 
 | |
| 
 | |
| static struct pci_device_id cafe_ids[] = {
 | |
| 	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL,
 | |
| 		     PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) },
 | |
| 	{ 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,
 | |
| #ifdef CONFIG_PM
 | |
| 	.suspend = cafe_pci_suspend,
 | |
| 	.resume = cafe_pci_resume,
 | |
| #endif
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| static int __init cafe_init(void)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n",
 | |
| 			CAFE_VERSION);
 | |
| 	ret = pci_register_driver(&cafe_pci_driver);
 | |
| 	if (ret) {
 | |
| 		printk(KERN_ERR "Unable to register cafe_ccic driver\n");
 | |
| 		goto out;
 | |
| 	}
 | |
| 	ret = 0;
 | |
| 
 | |
|   out:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void __exit cafe_exit(void)
 | |
| {
 | |
| 	pci_unregister_driver(&cafe_pci_driver);
 | |
| }
 | |
| 
 | |
| module_init(cafe_init);
 | |
| module_exit(cafe_exit);
 |