android camera(一):camera模組CMM介紹node
android camera(二):攝像頭工做原理、s5PV310 攝像頭接口(CAMIF)android
android camera(三):camera V4L2 FIMCapi
android camera(四):camera 驅動 GT2005數組
下載:經常使用攝像頭規格書(個別有android驅動程序) :bf3703 30W、gc0308 30W、ov7670、gt2005 200W、gt2015 200W、NT99250 200W、s5k5ba 200W、s5k4ba
前面兩篇說的有點多了,不過多瞭解點東西也挺好的,遇到問題時能夠有更多的思路,真正驅動是從這一塊開始。通常BSP的camera都是無缺的,咱們只用關心驅動這些就能夠了。數據結構
1. V4L2框架
1)簡介ide
在Linux中,攝像頭方面的標準化程度比較高,這個標準就是V4L2驅動程序,這也是業界比較公認的方式。函數
V4L全稱是Video for Linux,是Linux內核中標準的關於視頻驅動程序,目前使用比較多的版本是Video for Linux 2,簡稱V4L2。它爲Linux下的視頻驅動提供了統一的接口,使得應用程序可使用統一的API操做不一樣的視頻設備。從內核空間到用戶空間,主要的數據流和控制類均由V4L2驅動程序的框架來定義。atom
V4L2驅動程序通常只提供Video數據的得到,而如何實現視頻預覽,如何向上層發送數據,如何把純視頻流和取景器、視頻錄製等實際業務組織起來,都是camera的硬件抽象層須要負責的工做。debug
V4L2驅動核心實現爲以下文件:drivers/media/video/v4l2-dev.c。
V4l2-dev.h中定義的video_device是V4L2驅動程序的核心數據結構,它爲具體的攝像頭sensor驅動提供了接口調用。
V4l2的採集過程(應用程序):
1) 打開設備,得到文件描述符;
2) 設置圖片格式;
3) 分配緩衝區;
4) 啓動採集過程,讀取數據;
5) 中止採集,關閉設備。
2)數據結構
V4L2的主要數據結構是video_device,定義在v4l2_dev.h中:
struct video_device
{
/* device ops */
const struct v4l2_file_operations *fops; /*接口函數指針*/
/* sysfs */
struct device dev; /* v4l 設備結構 */
struct cdev *cdev; /* 字符設備結構*/
/* Set either parent or v4l2_dev if your driver uses v4l2_device */
struct device *parent; /* 設備父指針 */
struct v4l2_device *v4l2_dev; /* v4l2設備指針*/
/* device info */
char name[32]; /*設備名稱*/
int vfl_type;
/* 'minor' is set to -1 if the registration failed */
int minor; /*次設備號*/
u16 num;
/* use bitops to set/clear/test flags */
unsigned long flags;
/* attribute to differentiate multiple indices on one physical device */
int index;
/* V4L2 file handles */
spinlock_t fh_lock; /* Lock for all v4l2_fhs */
struct list_head fh_list; /* List of struct v4l2_fh */
int debug; /* debug 級別*/
/* Video 標準變量 */
v4l2_std_id tvnorms; /* Supported tv norms */
v4l2_std_id current_norm; /* Current tvnorm */
/* 回調函數 */
void (*release)(struct video_device *vdev);
/* ioctl 回調函數 */
const struct v4l2_ioctl_ops *ioctl_ops;
};
主要接口函數有:
intvideo_register_device(struct video_device *vdev, int type, int nr);
static intv4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
2. FIMC
1)簡介
FIMC這個模塊不單單是一個攝像頭的控制接口,它還承擔着V4L2的output功能和overlay的功能。
FIMC的驅動在內核中的位置:drivers/media/video/samsung/fimc
它包含下邊的文件:
fimc_regs.c
fimc_capture.c
fimc_dev.c
fimc_output.c
fimc_overlay.c
fimc_v4l2.c
它們的組織關係以下:
能夠看到,FIMC的驅動實現了v4l2全部的接口,能夠分爲v4l2-input設備接口,v4l2-output設備接口以及v4l2-overlay設備接口。這裏咱們主要關注v4l2-input設備接口,由於攝像頭屬於視頻輸入設備。
fimc_v4l2.c裏面註冊了不少的回調函數,都是用於實現v4l2的標準接口的,可是這些回調函數基本上都不是在fimc_v4l2.c裏面實現的,而是有相應的.c分別去實現。好比:
v4l2-input設備的操做實現:fimc_capture.c
v4l2-output設備的操做實現: fimc_output.c
v4l2-overlay設備的操做實現: fimc_overlay.c
這些代碼其實都是和具體硬件操做無關的,這個驅動把全部操做硬件寄存器的代碼都寫到一個文件裏面了,就是fimc40_regs.c。這樣把硬件相關的代碼和硬件無關的代碼分開來實現是很是好的方式,能夠最大限度的實現代碼複用。
2) 數據結構
FIMC的主要數據結構fimc_control,定義在fimc.h中:
struct fimc_control {
int id; /* 控制器 id */
char name[16];
atomic_t in_use;
void __iomem *regs; /* 寄存器 i/o */
struct clk *clk; /* interface clock */
struct regulator *regulator; /* pd regulator */
struct fimc_meminfo mem; /* for reserved mem */
/* kernel helpers */
struct mutex lock; /* controller lock */
struct mutex alloc_lock;
struct mutex v4l2_lock;
wait_queue_head_t wq;
struct device *dev;
int irq;
/* v4l2 related */
struct video_device *vd;
struct v4l2_device v4l2_dev;
/* fimc specific */
struct fimc_limit *limit; /* H/W limitation */
struct s3c_platform_camera *cam; /* activated camera */
struct fimc_capinfo *cap; /* capture dev info */
struct fimc_outinfo *out; /* output dev info */
struct fimc_fbinfo fb; /* fimd info */
struct fimc_scaler sc; /* scaler info */
struct fimc_effect fe; /* fimc effect info */
enum fimc_status status;
enum fimc_log log;
u32 ctx_busy[FIMC_MAX_CTXS];
};
由於FIMC一共有三套同樣的控制器(fimc0, fimc1, fimc2),因此驅動裏使用了一個數組來描述:
struct video_device fimc_video_device[FIMC_DEVICES] = {
[0] = {
.fops = &fimc_fops,
.ioctl_ops = &fimc_v4l2_ops,
.release = fimc_vdev_release,
},
[1] = {
.fops = &fimc_fops,
.ioctl_ops = &fimc_v4l2_ops,
.release = fimc_vdev_release,
},
[2] = {
.fops = &fimc_fops,
.ioctl_ops = &fimc_v4l2_ops,
.release = fimc_vdev_release,
},
};
fb_ops結構體是針對v4l2設備的基本操做,定義以下:
static const struct v4l2_file_operations fimc_fops = {
.owner = THIS_MODULE,
.open = fimc_open,
.release = fimc_release,
.ioctl = video_ioctl2,
.read = fimc_read,
.write = fimc_write,
.mmap = fimc_mmap,
.poll = fimc_poll,
};
3)FIMC初始設置
在S5PV210中,FIMC初始設置代碼在 /drivers/ arch/arm/mach-s5pv210/mach-smdkv310.c中:
static struct s3c_platform_fimc fimc_plat = {
.srclk_name = "mout_mpll",
.clk_name = "sclk_fimc",
.lclk_name = "sclk_fimc_lclk",
.clk_rate = 166750000,
.default_cam = CAMERA_CSI_C,
.camera = {
&mt9p111,//5M back cam
&s5k6aafx,///1.3M front cam
},
.hw_ver = 0x43,
};
對於GPIO的配置代碼在 /drivers/ arch/arm/mach-s5pv210/setup-fimc0.c中:
oid s3c_fimc0_cfg_gpio(struct platform_device *pdev)
{
int i = 0;
/* CAM A port(b0010) : PCLK, VSYNC, HREF, DATA[0-4] */
for (i = 0; i < 8; i++) {
s3c_gpio_cfgpin(S5PV210_GPE0(i), S3C_GPIO_SFN(2));
s3c_gpio_setpull(S5PV210_GPE0(i), S3C_GPIO_PULL_NONE);
}
/* CAM A port(b0010) : DATA[5-7], CLKOUT(MIPI CAM also), FIELD */
for (i = 0; i < 5; i++) {
s3c_gpio_cfgpin(S5PV210_GPE1(i), S3C_GPIO_SFN(2));
s3c_gpio_setpull(S5PV210_GPE1(i), S3C_GPIO_PULL_NONE);
}
/* CAM B port(b0011) : DATA[0-7] */
for (i = 0; i < 8; i++) {
s3c_gpio_cfgpin(S5PV210_GPJ0(i), S3C_GPIO_SFN(3));
s3c_gpio_setpull(S5PV210_GPJ0(i), S3C_GPIO_PULL_NONE);
}
/* CAM B port(b0011) : PCLK, VSYNC, HREF, FIELD, CLCKOUT */
for (i = 0; i < 5; i++) {
s3c_gpio_cfgpin(S5PV210_GPJ1(i), S3C_GPIO_SFN(3));
s3c_gpio_setpull(S5PV210_GPJ1(i), S3C_GPIO_PULL_NONE);
}
}
4)接口函數
FIMC的主要回調函數以下,實如今fimc_v4l2.c中:
onst struct v4l2_ioctl_ops fimc_v4l2_ops = {
.vidioc_querycap = fimc_querycap,
.vidioc_reqbufs = fimc_reqbufs,
.vidioc_querybuf = fimc_querybuf,
.vidioc_g_ctrl = fimc_g_ctrl,
.vidioc_s_ctrl = fimc_s_ctrl,
.vidioc_s_ext_ctrls = fimc_s_ext_ctrls,
.vidioc_cropcap = fimc_cropcap,
.vidioc_g_crop = fimc_g_crop,
.vidioc_s_crop = fimc_s_crop,
.vidioc_streamon = fimc_streamon,
.vidioc_streamoff = fimc_streamoff,
.vidioc_qbuf = fimc_qbuf,
.vidioc_dqbuf = fimc_dqbuf,
.vidioc_enum_fmt_vid_cap = fimc_enum_fmt_vid_capture,
.vidioc_g_fmt_vid_cap = fimc_g_fmt_vid_capture,
.vidioc_s_fmt_vid_cap = fimc_s_fmt_vid_capture,
.vidioc_try_fmt_vid_cap = fimc_try_fmt_vid_capture,
.vidioc_enum_input = fimc_enum_input,
.vidioc_g_input = fimc_g_input,
.vidioc_s_input = fimc_s_input,
.vidioc_g_parm = fimc_g_parm,
.vidioc_s_parm = fimc_s_parm,
.vidioc_queryctrl = fimc_queryctrl,
.vidioc_querymenu = fimc_querymenu,
.vidioc_g_fmt_vid_out = fimc_g_fmt_vid_out,
.vidioc_s_fmt_vid_out = fimc_s_fmt_vid_out,
.vidioc_try_fmt_vid_out = fimc_try_fmt_vid_out,
.vidioc_g_fbuf = fimc_g_fbuf,
.vidioc_s_fbuf = fimc_s_fbuf,
.vidioc_try_fmt_vid_overlay = fimc_try_fmt_overlay,
.vidioc_g_fmt_vid_overlay = fimc_g_fmt_vid_overlay,
.vidioc_s_fmt_vid_overlay = fimc_s_fmt_vid_overlay,
};
對於寄存器的操做,實現都在fimc_regs.c文件中,如
int fimc_hwset_camera_source(struct fimc_control *ctrl) { struct s3c_platform_camera *cam = ctrl->cam; u32 cfg = 0; cfg |= S3C_CISRCFMT_ITU601_8BIT; cfg |= cam->order422; if (cam->type == CAM_TYPE_ITU) cfg |= cam->fmt; cfg |= S3C_CISRCFMT_SOURCEHSIZE(cam->width); cfg |= S3C_CISRCFMT_SOURCEVSIZE(cam->height); writel(cfg, ctrl->regs + S3C_CISRCFMT); return 0; } int fimc_hwset_enable_irq(struct fimc_control *ctrl, int overflow, int level) { u32 cfg = readl(ctrl->regs + S3C_CIGCTRL); cfg &= ~(S3C_CIGCTRL_IRQ_OVFEN | S3C_CIGCTRL_IRQ_LEVEL); cfg |= S3C_CIGCTRL_IRQ_ENABLE; if (overflow) cfg |= S3C_CIGCTRL_IRQ_OVFEN; if (level) cfg |= S3C_CIGCTRL_IRQ_LEVEL; writel(cfg, ctrl->regs + S3C_CIGCTRL); return 0; }