android camera(三):camera V4L2 FIMC

關鍵詞:android  camera CMM 模組 camera參數  CAMIF   V4L2  
平臺信息:
內核:
linux
系統:android

平臺:S5PV310(samsung exynos 4210) 

作者:xubin341719(歡迎轉載,請註明作者)

android camera(一):camera模組CMM介紹

android camera(二):攝像頭工作原理、s5PV310 攝像頭接口(CAMIF)

android camera(三):camera V4L2 FIMC

android camera(四):camera 驅動 GT2005

下載:常用攝像頭規格書(個別有android驅動程序)  :bf3703 30W、gc0308 30W、ov7670、gt2005 200W、gt2015 200W、NT99250 200W、s5k5ba 200W、s5k4ba

          前面兩篇說的有點多了,不過多瞭解點東西也挺好的,遇到問題時可以有更多的思路,真正驅動是從這一塊開始。一般BSP的camera都是完好的,我們只用關心驅動這些就可以了。

1. V4L2

1)簡介

        在Linux中,攝像頭方面的標準化程度比較高,這個標準就是V4L2驅動程序,這也是業界比較公認的方式。

        V4L全稱是Video for Linux,是Linux內核中標準的關於視頻驅動程序,目前使用比較多的版本是Video for Linux 2,簡稱V4L2。它爲Linux下的視頻驅動提供了統一的接口,使得應用程序可以使用統一的API操作不同的視頻設備。從內核空間到用戶空間,主要的數據流和控制類均由V4L2驅動程序的框架來定義。

V4L2驅動程序一般只提供Video數據的獲得,而如何實現視頻預覽,如何向上層發送數據,如何把純視頻流和取景器、視頻錄製等實際業務組織起來,都是camera的硬件抽象層需要負責的工作。

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;
}



發佈了68 篇原創文章 · 獲贊 135 · 訪問量 168萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章