您的位置:首页 > 运维架构 > Linux

linux下的cmos摄像头驱动设计1-video驱动的注册

2014-11-30 21:23 302 查看
linux下的cmos驱动设计分析:

cmos摄像头驱动设计主要分为几部分:1.ARM处理器摄像头接口部分,2.通过i2c接口控制的摄像头芯片部分,被称为senor部分,3.就是上述两部分直接的联系部分,写在板级文件mach-boardname.c文件中。

其实这个摄像头驱动不是太难,摄像头驱动设计的目标就是,ARM处理器控制摄像头芯片,使其能够采集图像然后处理器把图像读出来。

围绕着这个目的,linux系统提供了v4l2架构,架构的目的是让我们程序员开发的时候更加方便,有个标准的规范,我们程序员在开发驱动的时候,就按着它给的标准的规范,把我们对硬件的操作写到框架中,这样在使用标准的应用程序操作这个设备的时候,,,就通过一层一层,内核中框架的调用对硬件进行了操作。

对于摄像头驱动来说,

我就写一写驱动的注册过程和应用程序对应的驱动程序中的操作吧

驱动的注册:

在我现在看的linux 3.0.12内核源码中,注册的关键代码主要在drivers/media/video/samsung/fimc/fimc_dev.c 文件的fimc_probe(struct platform_device *pdev)函数中

在这个函数中,我看关键部分代码主要是以下几个函数

1 . ctrl = fimc_register_controller(pdev);

2. ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);

3.ret = fimc_init_global(pdev);

4.ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);

接下来,一一分析这几个函数,我想,注册过程就大体知道了。

1.ctrl = fimc_register_controller(pdev);

ctrl是fimc_probe刚开始定义的一个结构体指针struct fimc_control *ctrl;

形参pdev是什么呢?是fimc的平台设备struct platform_device *pdev,这个在哪定义?

struct platform_device s3c_device_fimc0 = {

.name = "s3c-fimc",

.id = 0,

.num_resources
= ARRAY_SIZE(s3c_fimc0_resource),

.resource
= s3c_fimc0_resource,

};

还有一个很重要的函数就是在板级配置文件smdk4x12_machine_init函数中,s3c_fimc0_set_platdata(&fimc_plat);这句话的作用为

s3c_device_fimc0.dev.platform_data=fimc_plat;

fimc_plat为什么样的结构体呢?其定义为

static struct s3c_platform_fimc fimc_plat = {

.default_cam = CAMERA_PAR_A,

.camera = &ov5640,

.hw_ver = 0x51,

} 它的作用主要是描述了fimc结构的设置,比如读取哪种格式的数据,读取时采用哪种形式的时序,fimc读取时的时钟频率等,

其中ov5640这个结构展开为

static struct s3c_platform_camera ov5640 = {

.id = CAMERA_PAR_A,

.clk_name
= "sclk_cam0",

.i2c_busnum = 7,

.cam_power
= smdk4x12_cam1_reset,

.type = CAM_TYPE_ITU,

.fmt = ITU_601_YCBCR422_8BIT,

.order422
= CAM_ORDER422_8BIT_CBYCRY,

.info = &ov5640_i2c_info,

.pixelformat
= V4L2_PIX_FMT_UYVY, //modify by cym V4L2_PIX_FMT_UYVY,

.srclk_name = "xusbxti",

.clk_rate
= 24000000,

.line_length
= 1920,

.width
= 640,

.height = 480,

.window = {

.left = 0,

.top = 0,

.width
= 640,

.height = 480,

},

/* Polarity */

.inv_pclk
= 0,

.inv_vsync
= 1,

.inv_href
= 0,

.inv_hsync
= 0,

.reset_camera
= 1,

.initialized
= 0,

.layout_rotate = 0 //for shuping, //180,

};

上面是为我们分析函数进行了准备,把会用到的相关的结构体都进行了罗列,下面开始分析 ctrl = fimc_register_controller(pdev);

static struct fimc_control *fimc_register_controller(struct platform_device *pdev)
{
struct s3c_platform_fimc *pdata;
struct fimc_control *ctrl;
struct resource *res;
int id, err;
struct cma_info mem_info;
struct clk *sclk_fimc_lclk = NULL;
struct clk *fimc_src_clk = NULL;

id = pdev->id;
pdata = to_fimc_plat(&pdev->dev);

ctrl = get_fimc_ctrl(id);
ctrl->id = id;
ctrl->dev = &pdev->dev;
ctrl->vd = &fimc_video_device[id];
ctrl->vd->minor = id;
ctrl->log = FIMC_LOG_DEFAULT;
ctrl->power_status = FIMC_POWER_OFF;

/* CMA */
sprintf(ctrl->cma_name, "%s%d", FIMC_CMA_NAME, ctrl->id);
err = cma_info(&mem_info, ctrl->dev, 0);
fimc_info1("%s : [cma_info] start_addr : 0x%x, end_addr : 0x%x, "
"total_size : 0x%x, free_size : 0x%x\n",
__func__, mem_info.lower_bound, mem_info.upper_bound,
mem_info.total_size, mem_info.free_size);
if (err) {
fimc_err("%s: get cma info failed\n", __func__);
ctrl->mem.size = 0;
ctrl->mem.base = 0;
} else {
ctrl->mem.size = mem_info.total_size;
ctrl->mem.base = (dma_addr_t)cma_alloc
(ctrl->dev, ctrl->cma_name, (size_t)ctrl->mem.size, 0);
}
printk(KERN_DEBUG "ctrl->mem.size = 0x%x\n", ctrl->mem.size);
printk(KERN_DEBUG "ctrl->mem.base = 0x%x\n", ctrl->mem.base);
ctrl->mem.curr = ctrl->mem.base;
ctrl->status = FIMC_STREAMOFF;

switch (pdata->hw_ver) {
case 0x40:
ctrl->limit = &fimc40_limits[id];
break;
case 0x43:
case 0x45:
ctrl->limit = &fimc43_limits[id];
break;
case 0x50:
ctrl->limit = &fimc50_limits[id];
break;
case 0x51:
ctrl->limit = &fimc51_limits[id];
break;
default:
ctrl->limit = &fimc51_limits[id];
fimc_err("%s: failed to get HW version\n", __func__);
break;
}

sprintf(ctrl->name, "%s%d", FIMC_NAME, id);
strcpy(ctrl->vd->name, ctrl->name);

atomic_set(&ctrl->in_use, 0);
mutex_init(&ctrl->lock);
mutex_init(&ctrl->v4l2_lock);
spin_lock_init(&ctrl->outq_lock);
init_waitqueue_head(&ctrl->wq);

/* get resource for io memory */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
fimc_err("%s: failed to get io memory region\n", __func__);
return NULL;
}

/* request mem region */
res = request_mem_region(res->start, res->end - res->start + 1,
pdev->name);
if (!res) {
fimc_err("%s: failed to request io memory region\n", __func__);
return NULL;
}

/* ioremap for register block */
ctrl->regs = ioremap(res->start, res->end - res->start + 1);
if (!ctrl->regs) {
fimc_err("%s: failed to remap io region\n", __func__);
return NULL;
}

/* irq */
ctrl->irq = platform_get_irq(pdev, 0);
if (request_irq(ctrl->irq, fimc_irq, IRQF_DISABLED, ctrl->name, ctrl))
fimc_err("%s: request_irq failed\n", __func__);

if (soc_is_exynos4210())
fimc_src_clk = clk_get(&pdev->dev, "mout_mpll");
else
fimc_src_clk = clk_get(&pdev->dev, "mout_mpll_user");

if (IS_ERR(fimc_src_clk)) {
dev_err(&pdev->dev, "failed to get parent clock\n");
iounmap(ctrl->regs);
return NULL;
}

sclk_fimc_lclk = clk_get(&pdev->dev, FIMC_CORE_CLK);
if (IS_ERR(sclk_fimc_lclk)) {
dev_err(&pdev->dev, "failed to get sclk_fimc_lclk\n");
iounmap(ctrl->regs);
clk_put(fimc_src_clk);
return NULL;
}

if (clk_set_parent(sclk_fimc_lclk, fimc_src_clk)) {
dev_err(&pdev->dev, "unable to set parent %s of clock %s.\n",
fimc_src_clk->name, sclk_fimc_lclk->name);
iounmap(ctrl->regs);
clk_put(sclk_fimc_lclk);
clk_put(fimc_src_clk);
return NULL;
}
clk_set_rate(sclk_fimc_lclk, FIMC_CLK_RATE);
clk_put(sclk_fimc_lclk);
clk_put(fimc_src_clk);

#if (!defined(CONFIG_EXYNOS_DEV_PD) || !defined(CONFIG_PM_RUNTIME))
fimc_hwset_reset(ctrl);
#endif

return ctrl;
}
展开发现,上面代码主要做的了:

1,填充了struct fimc_control *ctrl;这个结构,这个结构是用来描述fimc控制器的,

2,映射了FIMC的的寄存器

3.申请设置了FImc控制器的时钟

*****************************************

接下来分析第二个函数 ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);

这里的形参&pdev->dev,&ctrl,还是我们开始分析时提到的那些结构,

展开函数

int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
{
if (v4l2_dev == NULL)
return -EINVAL;

INIT_LIST_HEAD(&v4l2_dev->subdevs);
spin_lock_init(&v4l2_dev->lock);
mutex_init(&v4l2_dev->ioctl_lock);
v4l2_prio_init(&v4l2_dev->prio);
kref_init(&v4l2_dev->ref);
v4l2_dev->dev = dev;
if (dev == NULL) {
/* If dev == NULL, then name must be filled in by the caller */
WARN_ON(!v4l2_dev->name[0]);
return 0;
}

/* Set name to driver name + device name if it is empty. */
if (!v4l2_dev->name[0])
snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
dev->driver->name, dev_name(dev));
if (!dev_get_drvdata(dev))
dev_set_drvdata(dev, v4l2_dev);
return 0;
}
这个函数主要干了几件事:

1.初始化了一个链表

2.稍微对&v4l2_dev进行了设置

3.v4l2_dev->dev = dev;把形参平台设备结构中的dev关联到v4l2_dev->dev,以后有用

4.if (!dev_get_drvdata(dev)) dev_set_drvdata(dev, v4l2_dev); 使得 平台设备结构中的dev和这个v4l2_dev,实现相互关联

接下来看第3个关键函数,ret = fimc_init_global(pdev);

static int fimc_init_global(struct platform_device *pdev)
{
struct fimc_control *ctrl;
struct s3c_platform_fimc *pdata;
struct s3c_platform_camera *cam;
struct clk *srclk;
int id, i;

pdata = to_fimc_plat(&pdev->dev);
id = pdev->id;
ctrl = get_fimc_ctrl(id);

/* Registering external camera modules. re-arrange order to be sure */
for (i = 0; i < FIMC_MAXCAMS; i++) {
cam = pdata->camera[i];
if (!cam)
break;
/* WriteBack doesn't need clock setting */
if ((cam->id == CAMERA_WB) || (cam->id == CAMERA_WB_B)) {
fimc_dev->camera[i] = cam;
fimc_dev->camera_isvalid[i] = 1;
fimc_dev->camera[i]->initialized = 0;
continue;
}

/* source clk for MCLK*/
srclk = clk_get(&pdev->dev, cam->srclk_name);
if (IS_ERR(srclk)) {
fimc_err("%s: failed to get srclk source\n", __func__);
return -EINVAL;
}

/* mclk */
cam->clk = clk_get(&pdev->dev, cam->clk_name);
if (IS_ERR(cam->clk)) {
fimc_err("%s: failed to get mclk source\n", __func__);
return -EINVAL;
}

if (clk_set_parent(cam->clk, srclk)) {
dev_err(&pdev->dev, "unable to set parent %s of clock %s.\n",
srclk->name, cam->clk->name);
clk_put(srclk);
clk_put(cam->clk);
return -EINVAL;
}

/* Assign camera device to fimc */
fimc_dev->camera[i] = cam;
fimc_dev->camera_isvalid[i] = 1;
fimc_dev->camera[i]->initialized = 0;
}

fimc_dev->mclk_status = CAM_MCLK_OFF;
fimc_dev->active_camera = -1;
fimc_dev->initialized = 1;

return 0;
}


这个函数主要做的是:

根据平台设备设置的信息,初始化fimc_dev这个结构体,这是一个全局变量,在fimc_dev.c中有定义,struct fimc_global *fimc_dev;

/**
* struct fimc_dev - abstraction for FIMC entity
* @slock:	the spinlock protecting this data structure
* @lock:	the mutex protecting this data structure
* @pdev:	pointer to the FIMC platform device
* @pdata:	pointer to the device platform data
* @variant:	the IP variant information
* @id:		FIMC device index (0..FIMC_MAX_DEVS)
* @num_clocks: the number of clocks managed by this device instance
* @clock:	clocks required for FIMC operation
* @regs:	the mapped hardware registers
* @regs_res:	the resource claimed for IO registers
* @irq:	FIMC interrupt number
* @irq_queue:	interrupt handler waitqueue
* @m2m:	memory-to-memory V4L2 device information
* @vid_cap:	camera capture device information
* @state:	flags used to synchronize m2m and capture mode operation
* @alloc_ctx:	videobuf2 memory allocator context
*/
struct fimc_dev {
spinlock_t			slock;
struct mutex			lock;
struct platform_device		*pdev;
struct s5p_platform_fimc	*pdata;
struct samsung_fimc_variant	*variant;
u16				id;
u16				num_clocks;
struct clk			*clock[MAX_FIMC_CLOCKS];
void __iomem			*regs;
struct resource			*regs_res;
int				irq;
wait_queue_head_t		irq_queue;
struct work_struct		work_struct;
struct workqueue_struct		*irq_workqueue;
struct fimc_m2m_device		m2m;
struct fimc_vid_cap		vid_cap;
unsigned long			state;
struct vb2_alloc_ctx		*alloc_ctx;
struct fimc_addr		paddr[FIMC_MAX_OUT_BUFS];
#ifdef CONFIG_VIDEOBUF2_SDVMM
enum vcm_dev_id			vcm_id;
#endif
const struct fimc_vb2		*vb2;
};


接下来,看最后一个关键函数:4.ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);

形参ctrl->vd为上面介绍的第一个函数中赋值的,为ctrl->vd = &fimc_video_device[id];ctrl->id代码是FIMC几,0,1,2或者3

/* Register video devices. Note that if video_register_device fails,
the release() callback of the video_device structure is *not* called, so
the caller is responsible for freeing any data. Usually that means that
you call video_device_release() on failure. */
static inline int __must_check video_register_device(struct video_device *vdev,
int type, int nr)
{
return __video_register_device(vdev, type, nr, 1, vdev->fops->owner);
}
注册video devices ,

展开看看

/**
*	__video_register_device - register video4linux devices
*	@vdev: video device structure we want to register
*	@type: type of device to register
*	@nr:   which device node number (0 == /dev/video0, 1 == /dev/video1, ...
*             -1 == first free)
*	@warn_if_nr_in_use: warn if the desired device node number
*	       was already in use and another number was chosen instead.
*	@owner: module that owns the video device node
*
*	The registration code assigns minor numbers and device node numbers
*	based on the requested type and registers the new device node with
*	the kernel.
*
*	This function assumes that struct video_device was zeroed when it
*	was allocated and does not contain any stale date.
*
*	An error is returned if no free minor or device node number could be
*	found, or if the registration of the device node failed.
*
*	Zero is returned on success.
*
*	Valid types are
*
*	%VFL_TYPE_GRABBER - A frame grabber
*
*	%VFL_TYPE_VBI - Vertical blank data (undecoded)
*
*	%VFL_TYPE_RADIO - A radio card
*
*	%VFL_TYPE_SUBDEV - A subdevice
*/
int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, struct module *owner)
{
int i = 0;
int ret;
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;

/* A minor value of -1 marks this video device as never
having been registered */
vdev->minor = -1;

/* the release callback MUST be present */
WARN_ON(!vdev->release);
if (!vdev->release)
return -EINVAL;

/* v4l2_fh support */
spin_lock_init(&vdev->fh_lock);
INIT_LIST_HEAD(&vdev->fh_list);

/* Part 1: check device type */
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video";
break;
case VFL_TYPE_VBI:
name_base = "vbi";
break;
case VFL_TYPE_RADIO:
name_base = "radio";
break;
case VFL_TYPE_SUBDEV:
name_base = "v4l-subdev";
break;
default:
printk(KERN_ERR "%s called with unknown type: %d\n",
__func__, type);
return -EINVAL;
}

vdev->vfl_type = type;
vdev->cdev = NULL;
if (vdev->v4l2_dev) {
if (vdev->v4l2_dev->dev)
vdev->parent = vdev->v4l2_dev->dev;
if (vdev->ctrl_handler == NULL)
vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
/* If the prio state pointer is NULL, then use the v4l2_device
prio state. */
if (vdev->prio == NULL)
vdev->prio = &vdev->v4l2_dev->prio;
}

/* Part 2: find a free minor, device node number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* Keep the ranges for the first four types for historical
* reasons.
* Newer devices (not yet in place) should use the range
* of 128-191 and just pick the first free minor there
* (new style). */
switch (type) {
case VFL_TYPE_GRABBER:
minor_offset = 0;
minor_cnt = 64;
break;
case VFL_TYPE_RADIO:
minor_offset = 64;
minor_cnt = 64;
break;
case VFL_TYPE_VBI:
minor_offset = 224;
minor_cnt = 32;
break;
default:
minor_offset = 128;
minor_cnt = 64;
break;
}
#endif

/* Pick a device node number */
mutex_lock(&videodev_lock);
nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
if (nr == minor_cnt)
nr = devnode_find(vdev, 0, minor_cnt);
if (nr == minor_cnt) {
printk(KERN_ERR "could not get a free device node number\n");
mutex_unlock(&videodev_lock);
return -ENFILE;
}
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* 1-on-1 mapping of device node number to minor number */
i = nr;
#else
/* The device node number and minor numbers are independent, so
we just find the first free minor number. */
for (i = 0; i < VIDEO_NUM_DEVICES; i++)
if (video_device[i] == NULL)
break;
if (i == VIDEO_NUM_DEVICES) {
mutex_unlock(&videodev_lock);
printk(KERN_ERR "could not get a free minor\n");
return -ENFILE;
}
#endif
vdev->minor = i + minor_offset;
vdev->num = nr;
devnode_set(vdev);

/* Should not happen since we thought this minor was free */
WARN_ON(video_device[vdev->minor] != NULL);
vdev->index = get_index(vdev);
mutex_unlock(&videodev_lock);

/* Part 3: Initialize the character device */
vdev->cdev = cdev_alloc();
if (vdev->cdev == NULL) {
ret = -ENOMEM;
goto cleanup;
}
vdev->cdev->ops = &v4l2_fops;
vdev->cdev->owner = owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
if (ret < 0) {
printk(KERN_ERR "%s: cdev_add failed\n", __func__);
kfree(vdev->cdev);
vdev->cdev = NULL;
goto cleanup;
}

/* Part 4: register the device with sysfs */
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
if (vdev->parent)
vdev->dev.parent = vdev->parent;
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
ret = device_register(&vdev->dev);
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
goto cleanup;
}
/* Register the release callback that will be called when the last
reference to the device goes away. */
vdev->dev.release = v4l2_device_release;

if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
name_base, nr, video_device_node_name(vdev));

/* Increase v4l2_device refcount */
if (vdev->v4l2_dev)
v4l2_device_get(vdev->v4l2_dev);

#if defined(CONFIG_MEDIA_CONTROLLER)
/* Part 5: Register the entity. */
if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
vdev->vfl_type != VFL_TYPE_SUBDEV) {
vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
vdev->entity.name = vdev->name;
vdev->entity.v4l.major = VIDEO_MAJOR;
vdev->entity.v4l.minor = vdev->minor;
ret = media_device_register_entity(vdev->v4l2_dev->mdev,
&vdev->entity);
if (ret < 0)
printk(KERN_WARNING
"%s: media_device_register_entity failed\n",
__func__);
}
#endif
/* Part 6: Activate this minor. The char device can now be used. */
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
mutex_lock(&videodev_lock);
video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);

return 0;

cleanup:
mutex_lock(&videodev_lock);
if (vdev->cdev)
cdev_del(vdev->cdev);
devnode_clear(vdev);
mutex_unlock(&videodev_lock);
/* Mark this video device as never having been registered. */
vdev->minor = -1;
return ret;
}


这个函数做的主要是

1.确定要注册的设备类型,与传入的那个形参有关,一般为video类型

2.查找相同类型设备的设备节点号,比如这次注册了VIDEO0,那下次执行这个函数时,要检查出来,0这个号已经被注册,注册的就是VIDEO1

3.注册设备,注册的是一个字符设备,所以某种意思上说,video设备也属于一个字符设备
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: