⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cafe_ccic.c

📁 trident tm5600的linux驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * 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. * * 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 "compat.h"#include <media/v4l2-common.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/debugfs.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 3static 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{	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 */	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 */#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;	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 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 0  /* client needs to talk during attach process */	if (! cam->sensor) {		cam_err(cam, "SMBUS xfer with no client\n");		return -EINVAL;	}#endif	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);static void cafe_ctlr_stop_dma(struct cafe_camera *cam);static void cafe_ctlr_power_down(struct cafe_camera *cam);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);	if (cam->sensor == client) {		cafe_ctlr_stop_dma(cam);		cafe_ctlr_power_down(cam);		cam_err(cam, "lost the sensor!\n");		cam->sensor = NULL;  /* Bummer, no camera */		cam->state = S_NOTREADY;	}	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");	adap->dev.parent = &cam->pdev->dev;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -