1. 框架
1.1 硬件协议简介
1.2 驱动框架
1.3 bus-drv-dev模型及写程序
a. 设备的4种构建方法
a.1 定义一个i2c_board_info, 里面有:名字, 设备地址
然后i2c_register_board_info(busnum, ...) (把它们放入__i2c_board_list链表)
list_add_tail(&devinfo->list, &__i2c_board_list);
链表何时使用:
i2c_register_adapter > i2c_scan_static_board_info > i2c_new_device
使用限制:必须在 i2c_register_adapter 之前 i2c_register_board_info
所以:不适合我们动态加载insmod
a.2 直接i2c_new_device, i2c_new_probed_device
a.2.1 i2c_new_device : 认为设备肯定存在
a.2.2 i2c_new_probed_device :对于"已经识别出来的设备"(probed_device),才会创建("new")
i2c_new_probed_device
probe(adap, addr_list[i]) /* 确定设备是否真实存在 */
info->addr = addr_list[i];
i2c_new_device(adap, info);
a.3 从用户空间创建设备
创建设备
echo at24c08 0x50 > /sys/class/i2c-adapter/i2c-0/new_device
导致i2c_new_device被调用
删除设备
echo 0x50 > /sys/class/i2c-adapter/i2c-0/delete_device
导致i2c_unregister_device
a.4 前面的3种方法都要事先确定适配器(I2C总线,I2C控制器)
如果我事先并不知道这个I2C设备在哪个适配器上,怎么办?去class表示的所有的适配器上查找
有上一些I2C设备的地址是一样,怎么继续分配它是哪一款?用detect函数
static struct i2c_driver at24cxx_driver = {
.class = I2C_CLASS_HWMON, /* 表示去哪些适配器上找设备 */
.driver = {
.name = "100ask",
.owner = THIS_MODULE,
},
.probe = at24cxx_probe,
.remove = __devexit_p(at24cxx_remove),
.id_table = at24cxx_id_table,
.detect = at24cxx_detect, /* 用这个函数来检测设备确实存在 */
.address_list = addr_list, /* 这些设备的地址 */
};
去"class表示的这一类"I2C适配器,用"detect函数"来确定能否找到"address_list里的设备",
如果能找到就调用i2c_new_device来注册i2c_client, 这会和i2c_driver的id_table比较,
如果匹配,调用probe
i2c_add_driver
i2c_register_driver
a. at24cxx_driver放入i2c_bus_type的drv链表
并且从dev链表里取出能匹配的i2c_client并调用probe
driver_register
b. 对于每一个适配器,调用__process_new_driver
对于每一个适配器,调用它的函数确定address_list里的设备是否存在
如果存在,再调用detect进一步确定、设置,然后i2c_new_device
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver);
__process_new_driver
i2c_do_add_adapter
/* Detect supported devices on that bus, and instantiate them */
i2c_detect(adap, driver);
for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
err = i2c_detect_address(temp_client, driver);
/* 判断这个设备是否存在:简单的发出S信号确定有ACK */
if (!i2c_default_probe(adapter, addr))
return 0;
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = addr;
// 设置info.type
err = driver->detect(temp_client, &info);
i2c_new_device.
下面示例一份cmos摄像头ov7740的i2c驱动:
//设备注册部分:cmos_ov7740_dev.c
#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 = {
I2C_BOARD_INFO("cmos_ov7740", 0x21), //0x21:i2c设备地址
}; static struct i2c_client *cmos_ov7740_client; static int cmos_ov7740_dev_init(void)
{
struct i2c_adapter *i2c_adap; i2c_adap = i2c_get_adapter(); //获得当前单板的适配器号
cmos_ov7740_client = i2c_new_device(i2c_adap, &cmos_ov7740_info); //生成一个i2c设备
i2c_put_adapter(i2c_adap); //挂到到适配器0之下 return ;
} 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");
//驱动注册部分:cmos_ov7740_drv.c
#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 = ,
},
{
.name = "PACKED_RGB_888",
.fourcc = V4L2_PIX_FMT_RGB24,
.depth = ,
},
}; struct camif_buffer
{
unsigned int order;
unsigned long virt_base;
unsigned long phy_base;
}; struct camif_buffer img_buff[] =
{
{
.order = ,
.virt_base = (unsigned long)NULL,
.phy_base = (unsigned long)NULL
},
{
.order = ,
.virt_base = (unsigned long)NULL,
.phy_base = (unsigned long)NULL
},
{
.order = ,
.virt_base = (unsigned long)NULL,
.phy_base = (unsigned long)NULL
},
{
.order = ,
.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 = ; 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)
{
/* 清中断 */
*SRCPND = <<;
*INTPND = <<;
*SUBSRCPND = <<; ev_cam = ;
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, , sizeof *cap);
strcpy(cap->driver, "cmos_ov7740");
strcpy(cap->card, "cmos_ov7740");
cap->version = ; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; return ;
} /* 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 ;
} /* A4 返回当前所使用的格式 */
static int cmos_ov7740_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
return ;
} /* 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;
} if ((f->fmt.pix.pixelformat != V4L2_PIX_FMT_RGB565) && (f->fmt.pix.pixelformat != V4L2_PIX_FMT_RGB24))
return -EINVAL; return ;
} /* 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 < )
return ret; TargetHsize_Pr = f->fmt.pix.width;
TargetVsize_Pr = f->fmt.pix.height; if(f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565)
{
*CIPRSCCTRL &= ~(<<); f->fmt.pix.bytesperline = (f->fmt.pix.width * ) >> ;
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)
{
*CIPRSCCTRL |= (<<); f->fmt.pix.bytesperline = (f->fmt.pix.width * ) >> ;
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<<)|(0x0<<)|(TargetVsize_Pr<<); return ;
} static int cmos_ov7740_vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
unsigned int order; order = get_order(buf_size); img_buff[].order = order;
img_buff[].virt_base = __get_free_pages(GFP_KERNEL|__GFP_DMA, img_buff[].order);
if(img_buff[].virt_base == (unsigned long)NULL)
{
printk("error0\n");
goto error0;
}
img_buff[].phy_base = __virt_to_phys(img_buff[].virt_base); img_buff[].order = order;
img_buff[].virt_base = __get_free_pages(GFP_KERNEL|__GFP_DMA, img_buff[].order);
if(img_buff[].virt_base == (unsigned long)NULL)
{
printk("error1\n");
goto error1;
}
img_buff[].phy_base = __virt_to_phys(img_buff[].virt_base); img_buff[].order = order;
img_buff[].virt_base = __get_free_pages(GFP_KERNEL|__GFP_DMA, img_buff[].order);
if(img_buff[].virt_base == (unsigned long)NULL)
{
printk("error2\n");
goto error2;
}
img_buff[].phy_base = __virt_to_phys(img_buff[].virt_base); img_buff[].order = order;
img_buff[].virt_base = __get_free_pages(GFP_KERNEL|__GFP_DMA, img_buff[].order);
if(img_buff[].virt_base == (unsigned long)NULL)
{
printk("error3\n");
goto error3;
}
img_buff[].phy_base = __virt_to_phys(img_buff[].virt_base); *CIPRCLRSA1 = img_buff[].phy_base;
*CIPRCLRSA2 = img_buff[].phy_base;
*CIPRCLRSA3 = img_buff[].phy_base;
*CIPRCLRSA4 = img_buff[].phy_base; return ;
error3:
free_pages(img_buff[].virt_base, order);
img_buff[].phy_base = (unsigned long)NULL;
error2:
free_pages(img_buff[].virt_base, order);
img_buff[].phy_base = (unsigned long)NULL;
error1:
free_pages(img_buff[].virt_base, order);
img_buff[].phy_base = (unsigned long)NULL;
error0:
return -ENOMEM;
} static void CalculateBurstSize(unsigned int hSize, unsigned int *mainBusrtSize, unsigned int *remainedBustSize)
{
unsigned int tmp; tmp = (hSize/)%;
switch(tmp)
{
case :
*mainBusrtSize = ;
*remainedBustSize = ;
break;
case :
*mainBusrtSize = ;
*remainedBustSize = ;
break;
case :
*mainBusrtSize = ;
*remainedBustSize = ;
break;
default:
tmp = (hSize/)%;
switch(tmp)
{
case :
*mainBusrtSize = ;
*remainedBustSize = ;
break;
case :
*mainBusrtSize = ;
*remainedBustSize = ;
break;
default:
*mainBusrtSize = ;
tmp = (hSize/)%;
*remainedBustSize = (tmp)?tmp:;
break;
}
break;
}
} static void camif_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
{
if(src >= *tar) {return;}
else if(src >= *tar) {*ratio = ; *shift = ;}
else if(src >= *tar) {*ratio = ; *shift = ;}
else if(src >= *tar) {*ratio = ; *shift = ;}
else if(src >= *tar) {*ratio = ; *shift = ;}
else if(src >= *tar) {*ratio = ; *shift = ;}
else {*ratio = ; *shift = ;}
} static void cmos_ov7740_calculate_scaler_info(void)
{
unsigned int sx, sy, tx, ty; 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 << ) / (tx << sc.H_Shift);
sc.MainVerRatio = (sy << ) / (ty << sc.V_Shift); sc.SHfactor = - (sc.H_Shift + sc.V_Shift); sc.ScaleUpDown = (tx>=sx)?:;
} /* 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)
bit[12:0] -- 设置源图片的垂直像素值(480)
*/
*CISRCFMT |= (<<)|(<<)|(CAM_SRC_HSIZE<<)|(CAM_ORDER_CbYCrY<<)|(CAM_SRC_VSIZE<<); /*
CIWDOFST:
bit[31] -- 1 = 使能窗口功能、0 = 不使用窗口功能
bit[30、15:12]-- 清除溢出标志位
bit[26:16] -- 水平方向的裁剪的大小
bit[10:0] -- 垂直方向的裁剪的大小
*/
*CIWDOFST |=(<<)|(0xf<<);
*CIWDOFST |= (<<)|(WinHorOfst<<)|(WinVerOfst<<);
SRC_Width = CAM_SRC_HSIZE - *WinHorOfst;
SRC_Height = CAM_SRC_VSIZE - *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 |= (<<)|(<<)|(<<)|(<<)|(<<); /*
CIPRCTRL:
bit[23:19] -- 主突发长度(Main_burst)
bit[18:14] -- 剩余突发长度(Remained_burst)
bit[2] -- 是否使能LastIRQ功能(不使能)
*/
CalculateBurstSize(bytesperline, &Main_burst, &Remained_burst);
*CIPRCTRL = (Main_burst<<)|(Remained_burst<<)|(<<); /*
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]: 预览缩放开始
*/
cmos_ov7740_calculate_scaler_info();
*CIPRSCPRERATIO = (sc.SHfactor<<)|(sc.PreHorRatio<<)|(sc.PreVerRatio<<);
*CIPRSCPREDST = (sc.PreDstWidth<<)|(sc.PreDstHeight<<);
*CIPRSCCTRL |= (<<)|(sc.ScaleUpDown<<)|(sc.MainHorRatio<<)|(sc.MainVerRatio<<); /*
CIPRTAREA:
表示预览通道的目标区域
*/
*CIPRTAREA = TargetHsize_Pr * TargetVsize_Pr; /*
CIIMGCPT:
bit[31]: 用来使能摄像头控制器
bit[30]: 使能编码通道
bit[29]: 使能预览通道
*/
*CIIMGCPT = (<<)|(<<);
*CIPRSCCTRL |= (<<); return ;
} /* A17 停止
* 参考 : uvc_video_enable(video, 0)
*/
static int cmos_ov7740_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type t)
{
*CIPRSCCTRL &= ~(<<);
*CIIMGCPT &= ~((<<)|(<<)); return ;
} 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 ;
} /* A18 关闭 */
static int cmos_ov7740_close(struct file *file)
{ return ;
} /* 应用程序通过读的方式读取摄像头的数据 */
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=; i<; i++)
{
if(copy_to_user(buf, (void *)img_buff[i].virt_base, end))
return -EFAULT;
} ev_cam = ; return end;
} static const struct v4l2_file_operations cmos_ov7740_fops = {
.owner = THIS_MODULE,
.open = cmos_ov7740_open,
.release = cmos_ov7740_close,
.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[].virt_base, order);
img_buff[].phy_base = (unsigned long)NULL;
free_pages(img_buff[].virt_base, order);
img_buff[].phy_base = (unsigned long)NULL;
free_pages(img_buff[].virt_base, order);
img_buff[].phy_base = (unsigned long)NULL;
free_pages(img_buff[].virt_base, order);
img_buff[].phy_base = (unsigned long)NULL;
} /* 2.1. 分配、设置一个video_device结构体 */
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;
*GPJDAT = ; /* 使能上拉电阻 */
*GPJUP = ;
} static void cmos_ov7740_camif_reset(void)
{
/* 传输方式为BT601 */
*CISRCFMT |= (<<); /* 复位CAMIF控制器 */
*CIGCTRL |= (<<);
mdelay();
*CIGCTRL &= ~(<<);
mdelay();
} static void cmos_ov7740_clk_cfg(void)
{
struct clk *camif_clk;
struct clk *camif_upll_clk; /* 使能CAMIF的时钟源 */
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_clk = clk_get(NULL, "camif-upll");
clk_set_rate(camif_upll_clk, );
mdelay();
} /*
注意:
1.S3C2440提供的复位时序(CAMRST)为:0->1->0(0:表示正常工作的电平、1:表示复位电平)
但是,实验证明,该复位时序与我们的OV7740需要的复位时序(1->0->1)不符合。
2.因此,我们就应该结合OV7740的具体复位时序,来设置相应的寄存器。
*/
static void cmos_ov7740_reset(void)
{
*CIGCTRL |= (<<);
mdelay();
*CIGCTRL &= ~(<<);
mdelay();
*CIGCTRL |= (<<);
mdelay();
} static void cmos_ov7740_init(void)
{
unsigned int mid;
int i; /* 读 */
mid = i2c_smbus_read_byte_data(cmos_ov7740_client, 0x0a)<<;
mid |= i2c_smbus_read_byte_data(cmos_ov7740_client, 0x0b);
printk("manufacture ID = 0x%4x\n", mid); /* 写 */
for(i = ; 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();
}
} 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, );
GPJDAT = ioremap(0x560000d4, );
GPJUP = ioremap(0x560000d8, ); CISRCFMT = ioremap(0x4F000000, );
CIWDOFST = ioremap(0x4F000004, );
CIGCTRL = ioremap(0x4F000008, );
CIPRCLRSA1 = ioremap(0x4F00006C, );
CIPRCLRSA2 = ioremap(0x4F000070, );
CIPRCLRSA3 = ioremap(0x4F000074, );
CIPRCLRSA4 = ioremap(0x4F000078, );
CIPRTRGFMT = ioremap(0x4F00007C, );
CIPRCTRL = ioremap(0x4F000080, );
CIPRSCPRERATIO = ioremap(0x4F000084, );
CIPRSCPREDST = ioremap(0x4F000088, );
CIPRSCCTRL = ioremap(0x4F00008C, );
CIPRTAREA = ioremap(0x4F000090, );
CIIMGCPT = ioremap(0x4F0000A0, ); SRCPND = ioremap(0X4A000000, );
INTPND = ioremap(0X4A000010, );
SUBSRCPND = ioremap(0X4A000018, ); /* 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 注册中断 */
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__); /* 2.2.注册 */
if(video_register_device(&cmos_ov7740_vdev, VFL_TYPE_GRABBER, -))
{
printk("unable to register video device\n");
} return ;
} 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 ;
} static const struct i2c_device_id cmos_ov7740_id_table[] = {
{ "cmos_ov7740", },
{}
}; /* 1.1. 分配、设置一个i2c_driver */
static struct i2c_driver cmos_ov7740_driver = {
.driver = {
.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 ;
} 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");
再附一段测试程序,对i2c设备的寄存器进行读写测试:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> /* i2c_test r addr
* i2c_test w addr val
*/ void print_usage(char *file)
{
printf("%s r addr\n", file);
printf("%s w addr val\n", file);
} int main(int argc, char **argv)
{
int fd;
unsigned char buf[]; if ((argc != ) && (argc != ))
{
print_usage(argv[]);
return -;
} fd = open("/dev/at24cxx", O_RDWR);
if (fd < )
{
printf("can't open /dev/at24cxx\n");
return -;
} if (strcmp(argv[], "r") == )
{
buf[] = strtoul(argv[], NULL, );
read(fd, buf, );
printf("data: %c, %d, 0x%2x\n", buf[], buf[], buf[]);
}
else if (strcmp(argv[], "w") == )
{
buf[] = strtoul(argv[], NULL, );
buf[] = strtoul(argv[], NULL, );
write(fd, buf, );
}
else
{
print_usage(argv[]);
return -;
} return ;
}