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

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设备也属于一个字符设备








你可能感兴趣的:(linux驱动)