自己写coms_ov7740驱动

设备文件


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>

/* 第一个参数名字用于匹配,第二个参数是摄像头地址 */
static struct i2c_board_info cmos_ov7740_info = {	
	/* 由芯片手册可知,写 -- 0x42(01000010),读 -- 0x43(01000011)
	 * 将读地址或者写地址,向右移动一位即可。
	 */
	I2C_BOARD_INFO("cmos_ov7740", 0x21),
};

static struct i2c_client *cmos_ov7740_client;

static int cmos_ov7740_dev_init(void)
{
	struct i2c_adapter *i2c_adap;
	/* 获得适配器,由2440原理图可知,2440只有一个iic控制器,所以iic控制器为适配器0,
	 * 也就是0号iic总线上 
	 */
	i2c_adap = i2c_get_adapter(0);
	cmos_ov7740_client = i2c_new_device(i2c_adap, &cmos_ov7740_info);
	/* 适配器使用完后要释放他 */
	i2c_put_adapter(i2c_adap);

	return 0;
}

static void cmos_ov7740_dev_exit(void)
{
	i2c_unregister_device(cmos_ov7740_client);
}

module_init(cmos_ov7740_dev_init);
module_exit(cmos_ov7740_dev_exit);

MODULE_LICENSE("GPL");

 

 


驱动文件


/* 该驱动函数是基于linux3.4.2
 * 该驱动程序是参考自己写的uvc驱动
 *
 */


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <linux/mm.h>
#include <asm/atomic.h>
#include <asm/unaligned.h>

#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf-core.h>

#include <linux/clk.h>
#include <asm/io.h>

#define OV7740_INIT_REGS_SIZE (sizeof(ov7740_setting_30fps_VGA_640_480)/sizeof(ov7740_setting_30fps_VGA_640_480[0]))

#define CAM_SRC_HSIZE	(640)
#define CAM_SRC_VSIZE	(480)

#define CAM_ORDER_YCbYCr (0)
#define CAM_ORDER_YCrYCb (1)
#define CAM_ORDER_CbYCrY (2)
#define CAM_ORDER_CrYCbY (3)

#define WinHorOfst		(0)
#define WinVerOfst		(0)

struct cmos_ov7740_scaler {
	unsigned int PreHorRatio;//水平变比
	unsigned int PreVerRatio;//垂直变比
	unsigned int H_Shift;//水平比
	unsigned int V_Shift;//垂直比
	unsigned int PreDstWidth;//目标宽度
	unsigned int PreDstHeight;//目标高度
	unsigned int MainHorRatio;//预览主缩放的水平比
	unsigned int MainVerRatio;//预览主缩放的垂直比
	unsigned int SHfactor;//缩放变比
	unsigned int ScaleUpDown;//放大缩小标志
};

static struct cmos_ov7740_scaler sc;

typedef struct cmos_ov7740_i2c_value {
	unsigned char regaddr;
	unsigned char value;
}ov7740_t;

/* init: 640x480,30fps的,YUV422输出格式 */
ov7740_t ov7740_setting_30fps_VGA_640_480[] =
{
	{0x12, 0x80},
	{0x47, 0x02},
	{0x17, 0x27},
	{0x04, 0x40},
	{0x1B, 0x81},
	{0x29, 0x17},
	{0x5F, 0x03},
	{0x3A, 0x09},
	{0x33, 0x44},
	{0x68, 0x1A},

	{0x14, 0x38},
	{0x5F, 0x04},
	{0x64, 0x00},
	{0x67, 0x90},
	{0x27, 0x80},
	{0x45, 0x41},
	{0x4B, 0x40},
	{0x36, 0x2f},
	{0x11, 0x01},
	{0x36, 0x3f},
	{0x0c, 0x12},

	{0x12, 0x00},
	{0x17, 0x25},
	{0x18, 0xa0},
	{0x1a, 0xf0},
	{0x31, 0xa0},
	{0x32, 0xf0},

	{0x85, 0x08},
	{0x86, 0x02},
	{0x87, 0x01},
	{0xd5, 0x10},
	{0x0d, 0x34},
	{0x19, 0x03},
	{0x2b, 0xf8},
	{0x2c, 0x01},

	{0x53, 0x00},
	{0x89, 0x30},
	{0x8d, 0x30},
	{0x8f, 0x85},
	{0x93, 0x30},
	{0x95, 0x85},
	{0x99, 0x30},
	{0x9b, 0x85},

	{0xac, 0x6E},
	{0xbe, 0xff},
	{0xbf, 0x00},
	{0x38, 0x14},
	{0xe9, 0x00},
	{0x3D, 0x08},
	{0x3E, 0x80},
	{0x3F, 0x40},
	{0x40, 0x7F},
	{0x41, 0x6A},
	{0x42, 0x29},
	{0x49, 0x64},
	{0x4A, 0xA1},
	{0x4E, 0x13},
	{0x4D, 0x50},
	{0x44, 0x58},
	{0x4C, 0x1A},
	{0x4E, 0x14},
	{0x38, 0x11},
	{0x84, 0x70}
};

struct cmos_ov7740_fmt {
	char  *name;
	u32   fourcc;          /* v4l2 format id */
	int   depth;
};

/* 这两个格式仅仅是预览通道支持的格式,由控制器的芯片手册可知 */
static struct cmos_ov7740_fmt formats[] = {
	{
		.name     = "RGB565",
		.fourcc   = V4L2_PIX_FMT_RGB565,
		.depth    = 16,
	},
	{
		.name     = "PACKED_RGB_888",
		.fourcc   = V4L2_PIX_FMT_RGB24,
		.depth    = 24,
	},
};

struct camif_buffer
{
	unsigned int order;
	unsigned long virt_base;
	unsigned long phy_base;	
};

struct camif_buffer img_buff[] =
{
	{
		.order = 0,
		.virt_base = (unsigned long)NULL,
		.phy_base = (unsigned long)NULL		
	},
	{
		.order = 0,
		.virt_base = (unsigned long)NULL,
		.phy_base = (unsigned long)NULL		
	},
	{
		.order = 0,
		.virt_base = (unsigned long)NULL,
		.phy_base = (unsigned long)NULL		
	},
	{
		.order = 0,
		.virt_base = (unsigned long)NULL,
		.phy_base = (unsigned long)NULL		
	}
};

static struct i2c_client *cmos_ov7740_client;

// CAMIF GPIO
static unsigned long *GPJCON;
static unsigned long *GPJDAT;
static unsigned long *GPJUP;

// CAMIF
static unsigned long *CISRCFMT;
static unsigned long *CIWDOFST;
static unsigned long *CIGCTRL;
static unsigned long *CIPRCLRSA1;
static unsigned long *CIPRCLRSA2;
static unsigned long *CIPRCLRSA3;
static unsigned long *CIPRCLRSA4;
static unsigned long *CIPRTRGFMT;
static unsigned long *CIPRCTRL;
static unsigned long *CIPRSCPRERATIO;
static unsigned long *CIPRSCPREDST;
static unsigned long *CIPRSCCTRL;
static unsigned long *CIPRTAREA;
static unsigned long *CIIMGCPT;

// IRQ
static unsigned long *SRCPND;
static unsigned long *INTPND;
static unsigned long *SUBSRCPND;

static unsigned int SRC_Width, SRC_Height;
static unsigned int TargetHsize_Pr, TargetVsize_Pr;
static unsigned long buf_size;
static unsigned int bytesperline;

static DECLARE_WAIT_QUEUE_HEAD(cam_wait_queue);
/* 中断标志 */
static volatile int ev_cam = 0;

static irqreturn_t cmos_ov7740_camif_irq_c(int irq, void *dev_id) 
{
	return IRQ_HANDLED;
}

static irqreturn_t cmos_ov7740_camif_irq_p(int irq, void *dev_id) 
{
	/* 清中断,因为不是使用上升沿或者下降沿触发,所以我们要清楚中断标志位
	 * 确保下次能继续产生中断。由中断控制器的手册394页可知,向相应的寄存器写入1
	 * 就是清除相应的中断标识,写入0表示保持原来的标志不变。
	 */
	*SRCPND = 1<<6;
	*INTPND = 1<<6;
	*SUBSRCPND = 1<<12;

	ev_cam = 1;
	wake_up_interruptible(&cam_wait_queue);

	return IRQ_HANDLED;
}

/* A2 参考 uvc_v4l2_do_ioctl */
static int cmos_ov7740_vidioc_querycap(struct file *file, void  *priv,
					struct v4l2_capability *cap)
{
	memset(cap, 0, sizeof *cap);
	strcpy(cap->driver, "cmos_ov7740");
	strcpy(cap->card, "cmos_ov7740");
	cap->version = 2;
	/* V4L2_CAP_READWRITE表示获取摄像头数据使用过读写操作,不是通过流操作
	 * 所谓流操作就是通过qbuf,dqbuf,mmap,poll等函数来获得数据的
	 */
	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;

	return 0;
}

/* A3 列举支持哪种格式
 * 参考: uvc_fmts 数组
 */
static int cmos_ov7740_vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
					struct v4l2_fmtdesc *f)
{
	struct cmos_ov7740_fmt *fmt;

	if (f->index >= ARRAY_SIZE(formats))
		return -EINVAL;

	fmt = &formats[f->index];

	strlcpy(f->description, fmt->name, sizeof(f->description));
	f->pixelformat = fmt->fourcc;

	return 0;
}

/* A4 返回当前所使用的格式 */
static int cmos_ov7740_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
					struct v4l2_format *f)
{
	return 0;
}

/* A5 测试驱动程序是否支持某种格式, 强制设置该格式 
 * 参考: uvc_v4l2_try_format
 *       myvivi_vidioc_try_fmt_vid_cap
 */
static int cmos_ov7740_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
			struct v4l2_format *f)
{
	/* 判断是否为摄像头设备 */
	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
	{
		return -EINVAL;
	}

	/* 在coms中的CICOSCCTRL寄存器中可知,在预览通道只支持RGB16/RGB24 */
	if ((f->fmt.pix.pixelformat != V4L2_PIX_FMT_RGB565) && (f->fmt.pix.pixelformat != V4L2_PIX_FMT_RGB24))
		return -EINVAL;

	return 0;
}

/* A6 参考 myvivi_vidioc_s_fmt_vid_cap 
 * 设置摄像头数据格式
 */
static int cmos_ov7740_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
					struct v4l2_format *f)
{
	int ret = cmos_ov7740_vidioc_try_fmt_vid_cap(file, NULL, f);
	if (ret < 0)
		return ret;
	/* 目标图像的水平像素大小 */
	TargetHsize_Pr = f->fmt.pix.width;
	/* 目标图像的垂直像素大小 */
	TargetVsize_Pr = f->fmt.pix.height;

	if(f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565)
	{
		/* bit[30]: 设置图像输出格式是RGB16、RGB24 */
		*CIPRSCCTRL &= ~(1<<30);
	
		f->fmt.pix.bytesperline = (f->fmt.pix.width * 16) >> 3;
		f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
		buf_size = f->fmt.pix.sizeimage;
		bytesperline = f->fmt.pix.bytesperline;
	}
	else if(f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24)
	{
		/* bit[30]: 设置图像输出格式是RGB16、RGB24 */
		*CIPRSCCTRL |= (1<<30);
		
		/* 对于RGB24格式数据来说,每个像素用32位来表示,在s3c2440用户手册的515页可知 */
		f->fmt.pix.bytesperline = (f->fmt.pix.width * 32) >> 3;
		f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
		buf_size = f->fmt.pix.sizeimage;
		bytesperline = f->fmt.pix.bytesperline;
	}
	


	/*
	CIPRTRGFMT:
		bit[28:16] -- 表示目标图片的水平像素大小(TargetHsize_Pr)
		bit[15:14] -- 是否旋转,我们这个驱动就不选择了
		bit[12:0]	 -- 表示目标图片的垂直像素大小(TargetVsize_Pr)
	*/
	*CIPRTRGFMT = (TargetHsize_Pr<<16)|(0x0<<14)|(TargetVsize_Pr<<0);

	return 0;
}

/* 分配四个缓存,因为有四个CIPRCLRSA1寄存器用于存放视频的缓存寄存器 */
static int cmos_ov7740_vidioc_reqbufs(struct file *file, void *priv,
			  struct v4l2_requestbuffers *p)
{
	unsigned int order;
	/* 参考其他驱动如何使用__get_free_pages函数,就会涉及到get_order
	 * 表示大小
	 */
	order = get_order(buf_size);

	img_buff[0].order = order;
	/* 使用__get_free_pages函数最大可以分配4M的连续物理内存,不适用kmalloc函数是因为
	 * 其最大只能分配128K的连续物理内存。
	 */
	img_buff[0].virt_base = __get_free_pages(GFP_KERNEL|__GFP_DMA, img_buff[0].order);
	if(img_buff[0].virt_base == (unsigned long)NULL)
	{
		printk("error0\n");
		goto error0;
	}
	/* 将虚拟地址转换为物理地址。因为CIPRCLRSA1寄存器就是要赋值为物理地址 */
	img_buff[0].phy_base = __virt_to_phys(img_buff[0].virt_base);

	img_buff[1].order = order;
	img_buff[1].virt_base = __get_free_pages(GFP_KERNEL|__GFP_DMA, img_buff[1].order);
	if(img_buff[1].virt_base == (unsigned long)NULL)
	{
		printk("error1\n");
		goto error1;
	}
	img_buff[1].phy_base = __virt_to_phys(img_buff[1].virt_base);

	img_buff[2].order = order;
	img_buff[2].virt_base = __get_free_pages(GFP_KERNEL|__GFP_DMA, img_buff[2].order);
	if(img_buff[2].virt_base == (unsigned long)NULL)
	{
		printk("error2\n");
		goto error2;
	}
	img_buff[2].phy_base = __virt_to_phys(img_buff[2].virt_base);

	img_buff[3].order = order;
	img_buff[3].virt_base = __get_free_pages(GFP_KERNEL|__GFP_DMA, img_buff[3].order);
	if(img_buff[3].virt_base == (unsigned long)NULL)
	{
		printk("error3\n");
		goto error3;
	}
	img_buff[3].phy_base = __virt_to_phys(img_buff[3].virt_base);

	/* CIPRCLRSA1寄存器需要赋值为物理地址 
	 * 这个地址其实就是给dma用的,当摄像头采集完数据之后将数据放到CIPRCLRSA1
	 * 寄存器指定的地址,然后在开始采集下一帧数据时触发一个中断。这个过程有点类似dma
	 * 的操作,只不过dma拷贝完指定长度的数据到指定的地址后会触发一个中断。
	 */
	*CIPRCLRSA1 = img_buff[0].phy_base;
	*CIPRCLRSA2 = img_buff[1].phy_base;
	*CIPRCLRSA3 = img_buff[2].phy_base;
	*CIPRCLRSA4 = img_buff[3].phy_base;

	return 0;
error3:
	free_pages(img_buff[2].virt_base, order);
	img_buff[2].phy_base = (unsigned long)NULL;		
error2:
	free_pages(img_buff[1].virt_base, order);
	img_buff[1].phy_base = (unsigned long)NULL;	
error1:
	free_pages(img_buff[0].virt_base, order);
	img_buff[0].phy_base = (unsigned long)NULL;
error0:	
	return -ENOMEM;
}

/* 突发长度的计算在2440用户手册的532页 */
static void CalculateBurstSize(unsigned int hSize, unsigned int *mainBusrtSize, unsigned int *remainedBustSize)
{
	unsigned int tmp;
	/* 由芯片手册可知,所有的突发长度只能为2,4,8,16,所以我们就从最快的16开始测试 
	 * 由芯片手册可知,每一行的数据必须字为单位。
	 */
	tmp = (hSize/4)%16;
	switch(tmp)
	{
		case 0:
			*mainBusrtSize = 16;
			*remainedBustSize = 16;
			break;
		case 4:
			*mainBusrtSize = 16;
			*remainedBustSize = 4;
			break;
		case 8:
			*mainBusrtSize = 16;
			*remainedBustSize = 8;
			break;
		/* 主突发长度不能为16,所以开始尝试主突发长度为8 */
		default:
			tmp = (hSize/4)%8;
			switch(tmp)
			{
				case 0:
					*mainBusrtSize = 8;
					*remainedBustSize = 8;
					break;
				case 4:
					*mainBusrtSize = 8;
					*remainedBustSize = 4;
					break;
				/* 主突发长度不能为8,开始尝试主突发长度为4 */
				default:
					*mainBusrtSize = 4;
					tmp = (hSize/4)%4;
					*remainedBustSize = (tmp)?tmp:4;
					break;
			}
			break;
	}
}

/* 照抄jz2440用户手册的527页代码 */
static void camif_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
{
	if(src >= 64*tar) {return;}
	else if(src >= 32*tar) {*ratio = 32; *shift = 5;}
	else if(src >= 16*tar) {*ratio = 16; *shift = 4;}
	else if(src >= 8*tar) {*ratio = 8; *shift = 3;}
	else if(src >= 4*tar) {*ratio = 4; *shift = 2;}
	else if(src >= 2*tar) {*ratio = 2; *shift = 1;}
	else {*ratio = 1; *shift = 0;}
}

/* 计算公式在2440用户手册的527页 */
static void cmos_ov7740_calculate_scaler_info(void)
{
	unsigned int sx, sy, tx, ty;

	/* 这里的源宽度和源高度是经过窗口剪切的图像数据 
	 * 这里的目标宽度和高度是应用程序在cmos_ov7740_vidioc_s_fmt_vid_cap函数
	 * 设置数据格式时传入的,表示应用程序想要获得的图像分辨率
	 */
	sx = SRC_Width;
	sy = SRC_Height;
	tx = TargetHsize_Pr;
	ty = TargetVsize_Pr;

	printk("%s: SRC_in(%d, %d), Target_out(%d, %d)\n", __func__, sx, sy, tx, ty);

	camif_get_scaler_factor(sx, tx, &sc.PreHorRatio, &sc.H_Shift);
	camif_get_scaler_factor(sy, ty, &sc.PreVerRatio, &sc.V_Shift);

	sc.PreDstWidth = sx / sc.PreHorRatio;
	sc.PreDstHeight = sy / sc.PreVerRatio;
	
	sc.MainHorRatio = (sx << 8) / (tx << sc.H_Shift);
	sc.MainVerRatio = (sy << 8) / (ty << sc.V_Shift);

	sc.SHfactor = 10 - (sc.H_Shift + sc.V_Shift);

	sc.ScaleUpDown = (tx>=sx)?1:0;
}

/* A11 启动传输 
 * 参考: uvc_video_enable(video, 1):
 *           uvc_commit_video
 *           uvc_init_video
 */
static int cmos_ov7740_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
	unsigned int Main_burst, Remained_burst;

	/*
	CISRCFMT:
		bit[31]	-- 选择传输方式为BT601或者BT656
		bit[30]	-- 设置偏移值(0 = +0 (正常情况下) - for YCbCr)
		bit[29]	-- 保留位,必须设置为0
		bit[28:16]	-- 设置源图片的水平像素值(640)
		bit[15:14]	-- 设置源图片的颜色顺序(0x0c --> 0x2),由摄像头地址为0xc的寄存器
		可知,输出的顺序为UYVYUYVY------>CbYCrYCbYCrY,
		bit[12:0]		-- 设置源图片的垂直像素值(480)
		
		水平和垂直的像素值是摄像头模块输入的,查看摄像头模块输出水平和垂直的像素值即可
	*/
	*CISRCFMT |= (0<<30)|(0<<29)|(CAM_SRC_HSIZE<<16)|(CAM_ORDER_CbYCrY<<14)|(CAM_SRC_VSIZE<<0);

	/*
	CIWDOFST:
		bit[31]		-- 1 = 使能窗口功能、0 = 不使用窗口功能
		bit[30、15:12]-- 清除溢出标志位
		bit[26:16]	-- 水平方向的裁剪的大小
		bit[10:0]		-- 垂直方向的裁剪的大小
	*/
	*CIWDOFST |=(1<<30)|(0xf<<12);
	*CIWDOFST |= (1<<31)|(WinHorOfst<<16)|(WinVerOfst<<0);
	/* 裁剪后的图像宽度 = 原始图像宽度 - 2*水平窗口宽度 */
	SRC_Width = CAM_SRC_HSIZE - 2*WinHorOfst;
	/* 裁剪后的图像高度 = 原始图像高度 - 2*水平窗口高度 */
	SRC_Height = CAM_SRC_VSIZE - 2*WinVerOfst;

	/*
	CIGCTRL:
		bit[31]		-- 软件复位CAMIF控制器
		bit[30]		-- 用于复位外部摄像头模块
		bit[29]		-- 保留位,必须设置为1
		bit[28:27]	-- 用于选择信号源(00 = 输入源来自摄像头模块)
		bit[26]		-- 设置像素时钟的极性(猜0)
		bit[25]		-- 设置VSYNC的极性(0)
		bit[24]		-- 设置HREF的极性(0),行同步信号
	*/
	*CIGCTRL |= (1<<29)|(0<<27)|(0<<26)|(0<<25)|(0<<24);

	/*
	CIPRCTRL:
		bit[23:19] -- 主突发长度(Main_burst)
		bit[18:14] -- 剩余突发长度(Remained_burst)
		bit[2]	  -- 是否使能LastIRQ功能(不使能)
	*/
	CalculateBurstSize(bytesperline, &Main_burst, &Remained_burst);
	*CIPRCTRL = (Main_burst<<19)|(Remained_burst<<14)|(0<<2);

	/*
	CIPRSCPRERATIO:
		bit[31:28]: 预览缩放的变化系数(SHfactor_Pr)
		bit[22:16]: 预览缩放的水平比(PreHorRatio_Pr)
		bit[6:0]: 预览缩放的垂直比(PreVerRatio_Pr)

	CIPRSCPREDST:
		bit[27:16]: 预览缩放的目标宽度(PreDstWidth_Pr)
		bit[11:0]: 预览缩放的目标高度(PreDstHeight_Pr)

	CIPRSCCTRL:
		bit[29:28]: 告诉摄像头控制器(图片是缩小、放大)(ScaleUpDown_Pr)
		bit[24:16]: 预览主缩放的水平比(MainHorRatio_Pr)
		bit[8:0]: 预览主缩放的垂直比(MainVerRatio_Pr)

		bit[31]: 必须固定设置为1
		bit[30]: 设置图像输出格式是RGB16、RGB24
		bit[15]: 预览缩放开始

		//bit[30]在cmos_ov7740_vidioc_s_fmt_vid_cap函数中设置
		
	*/
	cmos_ov7740_calculate_scaler_info();
	*CIPRSCPRERATIO = (sc.SHfactor<<28)|(sc.PreHorRatio<<16)|(sc.PreVerRatio<<0);
	*CIPRSCPREDST = (sc.PreDstWidth<<16)|(sc.PreDstHeight<<0);
	*CIPRSCCTRL |= (1<<31)|(sc.ScaleUpDown<<28)|(sc.MainHorRatio<<16)|(sc.MainVerRatio<<0);

	/*
	CIPRTAREA:
		表示预览通道的目标区域
	*/
	*CIPRTAREA = TargetHsize_Pr * TargetVsize_Pr;

	/*
	CIIMGCPT:
		bit[31]: 用来使能摄像头控制器
		bit[30]: 使能编码通道
		bit[29]: 使能预览通道
	*/
	*CIIMGCPT = (1<<31)|(1<<29);
	*CIPRSCCTRL |= (1<<15);

	return 0;
}

/* A17 停止 
 * 参考 : uvc_video_enable(video, 0)
 */
static int cmos_ov7740_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type t)
{
	*CIPRSCCTRL &= ~(1<<15);
	*CIIMGCPT &= ~((1<<31)|(1<<29));

	return 0;
}

static const struct v4l2_ioctl_ops cmos_ov7740_ioctl_ops = {
        // 表示它是一个摄像头设备
        .vidioc_querycap      = cmos_ov7740_vidioc_querycap,

        /* 用于列举、获得、测试、设置摄像头的数据的格式 */
        .vidioc_enum_fmt_vid_cap  = cmos_ov7740_vidioc_enum_fmt_vid_cap,
        .vidioc_g_fmt_vid_cap     = cmos_ov7740_vidioc_g_fmt_vid_cap,
        .vidioc_try_fmt_vid_cap   = cmos_ov7740_vidioc_try_fmt_vid_cap,
        .vidioc_s_fmt_vid_cap     = cmos_ov7740_vidioc_s_fmt_vid_cap,
        
        /* 缓冲区操作: 申请/查询/放入队列/取出队列 */
        .vidioc_reqbufs       = cmos_ov7740_vidioc_reqbufs,

	/* 说明: 因为我们是通过读的方式来获得摄像头数据,因此查询/放入队列/取出队列这些操作函数将不在需要 */
#if 0
        .vidioc_querybuf      = myuvc_vidioc_querybuf,
        .vidioc_qbuf          = myuvc_vidioc_qbuf,
        .vidioc_dqbuf         = myuvc_vidioc_dqbuf,
#endif

        // 启动/停止
        .vidioc_streamon      = cmos_ov7740_vidioc_streamon,
        .vidioc_streamoff     = cmos_ov7740_vidioc_streamoff,   
};

/* A1 */
static int cmos_ov7740_open(struct file *file)
{
	return 0;
}

/* A18 关闭 */
static int cmos_ov7740_close(struct file *file)
{
    
	return 0;
}

/* 应用程序通过读的方式读取摄像头的数据 */
static ssize_t cmos_ov7740_read(struct file *filep, char __user *buf, size_t count, loff_t *pos)
{
	size_t end;
	int i;
	/* 取一帧数据的大小和用户想要读取数据的大小的最小值 */
	end = min_t(size_t, buf_size, count);
	wait_event_interruptible(cam_wait_queue, ev_cam);

	for(i=0; i<4; i++)
	{
		if(copy_to_user(buf, (void *)img_buff[i].virt_base, end))
			return -EFAULT;
	}

	ev_cam = 0;

	return end;
}

/* 应用程序使用read函数来获取摄像头数据,所以不再需要mmap,poll函数。 */
static const struct v4l2_file_operations cmos_ov7740_fops = {
	.owner			= THIS_MODULE,
	.open       		= cmos_ov7740_open,
	.release    		= cmos_ov7740_close,
	/* video_ioctl2是内核提供的函数,只是起到中转的作用,最后会调用video_device中的ioctl_ops */
	.unlocked_ioctl      	= video_ioctl2,
	.read			= cmos_ov7740_read,
};

/*
	注意:
		该函数是必须的,否则在insmod的时候,会出错
*/
static void cmos_ov7740_release(struct video_device *vdev)
{
	unsigned int order;

	order = get_order(buf_size);

	free_pages(img_buff[0].virt_base, order);
	img_buff[0].phy_base = (unsigned long)NULL;
	free_pages(img_buff[1].virt_base, order);
	img_buff[1].phy_base = (unsigned long)NULL;	
	free_pages(img_buff[2].virt_base, order);
	img_buff[2].phy_base = (unsigned long)NULL;		
	free_pages(img_buff[3].virt_base, order);
	img_buff[3].phy_base = (unsigned long)NULL;	
}

/* 2.1. 分配、设置一个video_device结构体 
 * 这里采用的是静态分配结构体,使用video_device_alloc()是动态分配摄像头结构体
 */
static struct video_device cmos_ov7740_vdev = {
	.fops		= &cmos_ov7740_fops,
	.ioctl_ops		= &cmos_ov7740_ioctl_ops,
	.release		= cmos_ov7740_release,
	.name		= "cmos_ov7740",
};

static void cmos_ov7740_gpio_cfg(void)
{
	/* 设置相应的GPIO用于CAMIF */
	*GPJCON = 0x2aaaaaa;
	/* 将数据位清0 */
	*GPJDAT = 0;

	/* 为了让信号稳定,使能上拉电阻 */
	*GPJUP = 0;
}

static void cmos_ov7740_camif_reset(void)
{
	/* 传输方式为BT601 */
	*CISRCFMT |= (1<<31);

	/* 复位CAMIF控制器 */
	*CIGCTRL |= (1<<31);
	/* 稍微延迟一下 */
	mdelay(10);
	/* 使摄像头恢复正常状态 */
	*CIGCTRL &= ~(1<<31);
	mdelay(10);	
}

static void cmos_ov7740_clk_cfg(void)
{
	struct clk *camif_clk;
	struct clk *camif_upll_clk;

	/* 使能CAMIF的时钟源(摄像头控制器) 
	 * clk_get是三星公司为自己的soc写好的代码,我们可以直接使用,但是我们也可以自己设置
	 * 相应的寄存器来设置时钟。在source insight中搜索”camif”,找到s3c2440相关的文件,
	 * 查看其实能函数是如何配置其寄存器的
	 */
	camif_clk = clk_get(NULL, "camif");
	if(!camif_clk || IS_ERR(camif_clk))
	{
		printk(KERN_INFO "failed to get CAMIF clock source\n");
	}
	clk_enable(camif_clk);

	/* 使能并设置CAMCLK = 24MHz,设置外部摄像头的时钟 
	 * camif-upll是来自usb总线的时钟,
	 * 由芯片手册可知外部摄像头时钟也是是通过soc内部的时钟树来配置,由芯片手册可知外部摄像头
	 * 时钟来自usb总线的时钟,这个时钟也是配置soc中的时钟树来设置频率的。也是三星公司写好的。
	 */
	camif_upll_clk = clk_get(NULL, "camif-upll");
	clk_set_rate(camif_upll_clk, 24000000);
	mdelay(100);
}

/*
	注意:
		1.S3C2440提供的复位时序(CAMRST)为:0->1->0(0:表示正常工作的电平、1:表示复位电平)
		  但是,实验证明,该复位时序与我们的OV7740需要的复位时序(1->0->1)不符合。
		2.因此,我们就应该结合OV7740的具体复位时序,来设置相应的寄存器。
*/
static void cmos_ov7740_reset(void)
{
	*CIGCTRL |= (1<<30);
	mdelay(30);
	*CIGCTRL &= ~(1<<30);
	mdelay(30);
	*CIGCTRL |= (1<<30);
	mdelay(30);	
}

static void cmos_ov7740_init(void)
{
	unsigned int mid;
	int i;

	/* 读 
	 * 0xa是摄像头模块的厂家id的高8位,0xb是厂家id的低8位
	 * i2c_smbus_read_byte_data函数是内核提供的函数,也可是使用i2c_transfer函数
	 */
	mid = i2c_smbus_read_byte_data(cmos_ov7740_client, 0x0a)<<8;
	mid |= i2c_smbus_read_byte_data(cmos_ov7740_client, 0x0b);
	printk("manufacture ID = 0x%4x\n", mid);

	/* 写 
	 * 初始化摄像头模块
	 */
	for(i = 0; i < OV7740_INIT_REGS_SIZE; i++)
	{
		i2c_smbus_write_byte_data(cmos_ov7740_client, ov7740_setting_30fps_VGA_640_480[i].regaddr, ov7740_setting_30fps_VGA_640_480[i].value);
		mdelay(2);
	}
}

static int __devinit cmos_ov7740_probe(struct i2c_client *client,
				  const struct i2c_device_id *id)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	/* 2.3 硬件相关 */
	/* 2.3.1 映射相应的寄存器 */
	GPJCON = ioremap(0x560000d0, 4);
	GPJDAT = ioremap(0x560000d4, 4);
	/* 上拉电阻相关的寄存器 */ 
	GPJUP = ioremap(0x560000d8, 4);

	/* 以下是摄像头控制器的寄存器 */

	CISRCFMT = ioremap(0x4F000000, 4);
	CIWDOFST = ioremap(0x4F000004, 4);
	CIGCTRL = ioremap(0x4F000008, 4);
	CIPRCLRSA1 = ioremap(0x4F00006C, 4);
	CIPRCLRSA2 = ioremap(0x4F000070, 4);
	CIPRCLRSA3 = ioremap(0x4F000074, 4);
	CIPRCLRSA4 = ioremap(0x4F000078, 4);
	CIPRTRGFMT = ioremap(0x4F00007C, 4);
	CIPRCTRL = ioremap(0x4F000080, 4);
	CIPRSCPRERATIO = ioremap(0x4F000084, 4);
	CIPRSCPREDST = ioremap(0x4F000088, 4);
	CIPRSCCTRL = ioremap(0x4F00008C, 4);
	CIPRTAREA = ioremap(0x4F000090, 4);
	CIIMGCPT = ioremap(0x4F0000A0, 4);

	SRCPND = ioremap(0X4A000000, 4);
	INTPND = ioremap(0X4A000010, 4);
	SUBSRCPND = ioremap(0X4A000018, 4);

	/* 2.3.2 设置相应的GPIO用于CAMIF */
	cmos_ov7740_gpio_cfg();

	/* 2.3.3 复位一下CAMIF控制器
	 * 接下来要操作摄像头控制器,因此要先复位一下控制器
	 */
	cmos_ov7740_camif_reset();

	/* 2.3.4 设置、使能时钟(使能HCLK用于摄像头控制器、使能并设置CAMCLK = 24MHz用于外部摄像头) */
	cmos_ov7740_clk_cfg();

	/* 2.3.5 复位一下摄像头模块 */
	cmos_ov7740_reset();

	/* 2.3.6 通过IIC总线,初始化摄像头模块 */
	cmos_ov7740_client = client;
	cmos_ov7740_init();

	/* 2.3.7 注册中断 
	 * IRQF_DISABLED - keep irqs disabled when calling the action handler
	 */
	if (request_irq(IRQ_S3C2440_CAM_C, cmos_ov7740_camif_irq_c, IRQF_DISABLED , "CAM_C", NULL))
		printk("%s:request_irq failed\n", __func__);

	if (request_irq(IRQ_S3C2440_CAM_P, cmos_ov7740_camif_irq_p, IRQF_DISABLED , "CAM_P", NULL))
		printk("%s:request_irq failed\n", __func__);
	
	
	/* 3. 注册 ,以下信息是通过追踪源代码得到的结论
 	 * 参数-1表示分配第一个可用的号
	 * which device node number (0 == /dev/video0, 1 == /dev/video1, ...
	 *            -1 == first free) 
	 */
    if(video_register_device(&cmos_ov7740_vdev, VFL_TYPE_GRABBER, -1))
	{
		printk("unable to register video device\n");
	}

	return 0;
}

static int __devexit cmos_ov7740_remove(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	iounmap(GPJCON);
	iounmap(GPJDAT);
	iounmap(GPJUP);

	iounmap(CISRCFMT);
	iounmap(CIWDOFST);
	iounmap(CIGCTRL);
	iounmap(CIPRCLRSA1);
	iounmap(CIPRCLRSA2);
	iounmap(CIPRCLRSA3);
	iounmap(CIPRCLRSA4);
	iounmap(CIPRTRGFMT);
	iounmap(CIPRCTRL);
	iounmap(CIPRSCPRERATIO);
	iounmap(CIPRSCPREDST);
	iounmap(CIPRSCCTRL);
	iounmap(CIPRTAREA);
	iounmap(CIIMGCPT);
	
	iounmap(SRCPND);
	iounmap(INTPND);
	iounmap(SUBSRCPND);

	free_irq(IRQ_S3C2440_CAM_C, NULL);
	free_irq(IRQ_S3C2440_CAM_P, NULL);
	video_unregister_device(&cmos_ov7740_vdev);
	return 0;
}

static const struct i2c_device_id cmos_ov7740_id_table[] = {
	{ "cmos_ov7740", 0 },
	{}
};

/* 1.1. 分配、设置一个i2c_driver */
static struct i2c_driver cmos_ov7740_driver = {
	.driver	= {
		/* 用于和iic设备进行匹配,必须和iic_dev中的名字一样,否则匹配不成功 */
		.name	= "cmos_ov7740",
		.owner	= THIS_MODULE,
	},
	.probe		= cmos_ov7740_probe,
	.remove		= __devexit_p(cmos_ov7740_remove),
	.id_table	= cmos_ov7740_id_table,
};

static int cmos_ov7740_drv_init(void)
{
	/* 1.2.注册 */
	i2c_add_driver(&cmos_ov7740_driver);

	return 0;
}

static void cmos_ov7740_drv_exit(void)
{
	i2c_del_driver(&cmos_ov7740_driver);
}

module_init(cmos_ov7740_drv_init);
module_exit(cmos_ov7740_drv_exit);

MODULE_LICENSE("GPL");

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值