camera驅動框架分析(中)


camera host的驅動

下面開始分析camera host吧,如果僅僅是想知道camera sensor驅動怎么寫,而不想知道內部具體怎么個調用流程,怎么個架構設計,那可以跳過該部分,直接去看i2c camera sensor的驅動了。前面說了我們選擇at91平台,那對應的camera host 文件就是drivers/media/platform/soc_camera/atmel-isi.c了。

static struct platform_driver atmel_isi_driver = {
	.remove		= atmel_isi_remove,
	.driver		= {
		.name = "atmel_isi",
		.owner = THIS_MODULE,
	},
};

module_platform_driver_probe(atmel_isi_driver, atmel_isi_probe);

直接看atmel_isi_probe吧,它怎么和它對應的平台設備匹配的過程就不再描述了,和上面說的類似,具體實現在文件arch/arm/mach-at91/at91sam9g45_devices.c里。

atmel_isi_probe里面有一部分會設計到該soc isi(camera host)的硬件初始化,我這里不跟蹤進去,只是將和camera sensor驅動相關的代碼摘取出來:

static int atmel_isi_probe(struct platform_device *pdev)
{
	unsigned int irq;
	struct atmel_isi *isi;
	struct resource *regs;
	int ret, i;
	struct device *dev = &pdev->dev;
	struct soc_camera_host *soc_host;
	struct isi_platform_data *pdata;

	.....
	.....
	.....

	soc_host		= &isi->soc_host;
	soc_host->drv_name	= "isi-camera";
	soc_host->ops		= &isi_soc_camera_host_ops;
	soc_host->priv		= isi;
	soc_host->v4l2_dev.dev	= &pdev->dev;
	soc_host->nr		= pdev->id;

	ret = soc_camera_host_register(soc_host);
	if (ret) {
		dev_err(&pdev->dev, "Unable to register soc camera host\n");
		goto err_register_soc_camera_host;
	}
	return 0;

	.....
	.....
	.....

	return ret;
}

這里我們會開始接觸第三個重要的數據結構soc_camera_host,他在內核里代表的就是一個camera host設備。另外,第四個重要的數據結構soc_camera_host_ops,它是soc_camera架構為camera host定義的,用來實現所有host相關的操作回調。soc_camera在適當的時候,會調用里面的接口。camera host驅動主要工作的一部分就是實現該數據結構了。里面有一句soc_host->v4l2_dev.dev = &pdev->dev;表示soc_camera_host內嵌的v4l2框架定義的父對象(子對象v4l2_subdev,后面會出現的)對應的設備就是該camera host平台設備。將soc_camera_host進行適當的初始化后,再調用soc_camera架構定義的api soc_camera_host_register來實現camera host的注冊。也就是在這里,會處理上面soc_camera_device_register注冊進系統的camera sensor了。它里面核心的動作主要有兩個

第一個,ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev),這是v4l2框架給出的api,camera host在v4l2架構里面應該算是一個v4l2_device,camera里面的sensor控制部分,即i2c設備對應就應該是v4l2_subdev了。

第二個,scan_add_host(ici),就是這個函數完成了對soc_camera_device_register注冊進系統的camera的掃描及處理。

static void scan_add_host(struct soc_camera_host *ici)
{
	struct soc_camera_device *icd;

	mutex_lock(&list_lock);

	list_for_each_entry(icd, &devices, list)
		if (icd->iface == ici->nr) {
			struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
			struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc;

			/* The camera could have been already on, try to reset */
			if (ssdd->reset)
				ssdd->reset(icd->pdev);

			icd->parent = ici->v4l2_dev.dev;

			/* Ignore errors */
			soc_camera_probe(ici, icd);
		}

	mutex_unlock(&list_lock);
}

它主要就是掃描devices全局鏈表,找里面icd->iface和自己的nr相同的。這里的icd->iface其實就是在soc_camera_link里面的bus_id,而nr對應着camera host平台設備描述信息里面的id。這里找到匹配的設備后,就調用soc_camera_probe(ici, icd)來處理掛載在該總線上的設備了。傳入的兩個參數ici,是我們camera host驅動分配並初始化的數據結構,由camera host負責初始化它里面的v4l2_dev以及ops等等,而icd是通用驅動soc_camera分配並根據我們在扳級相關的文件里面用soc_camera_link描述的信息初始化的對象。另外,還一個需要注意的地方,icd->parent = ici->v4l2_dev.dev;這里指定icd,也就是soc camera的parent為camera host,ici->v4l2_dev.dev就代表着camera host platform device對應的device,不信請回到atmel_isi_probe,里面有一句soc_host->v4l2_dev.dev = &pdev->dev;可以證實啦。這其實很好理解啦,camera host 當然就應該是camera的parent,對吧!

到這里,host driver就開始正式的訪問那些之前就注冊到全局鏈表devices里面camera了(icd)。分析之前,可以想象一下,它會做什么事情呢?我覺得怎么也得將扳級相關的文件里面用soc_camera_linkboard_info對應的i2c控制部分給處理了吧,也就是找到對應用i2c控制的sensor driver。還有就是將該icd加入到host管理數據結構里啦,然后創建一個導出給應用層的設備節點,讓對該設備節點的操作能夠最終轉到host或者sensor driver提供的ops里。下面正式開始分析吧,驗證下對不對。這里直接將說明插入到代碼中:

/* Called during host-driver probe */
static int soc_camera_probe(struct soc_camera_host *ici,
			    struct soc_camera_device *icd)
{
	struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);//拿到扳級相關的文件里面用soc_camera_link信息,和我們預想一樣  果然是要開始處理它了
	struct soc_camera_host_desc *shd = &sdesc->host_desc;//同上
	struct device *control = NULL;
	int ret;

	dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev));

	/*
	 * Currently the subdev with the largest number of controls (13) is
	 * ov6550. So let's pick 16 as a hint for the control handler. Note
	 * that this is a hint only: too large and you waste some memory, too
	 * small and there is a (very) small performance hit when looking up
	 * controls in the internal hash.
	 */
	ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16);//v4l2_ctrl是v4l2框架提供的一種ioctl處理機制,我們只需要知道它這里主要是服務於sensor驅動,讓sensor驅動里的一些ioctl控制能夠注入到導出給應用層ioctl的里面,這樣,應用層調用相關的ioctl,最終會有一部分進入到sensor驅動里面
	if (ret < 0)
		return ret;

	/* Must have icd->vdev before registering the device */
	ret = video_dev_create(icd);//這也是v4l2框架提供的api,用於創建一個導出給應用層操作的設備文件(這里僅僅是初始化對應的數據結構),一個camera設備對應一個設備文件導出,這個很容易理解吧
	if (ret < 0)
		goto evdc;

	/*
	 * ..._video_start() will create a device node, video_register_device()
	 * itself is protected against concurrent open() calls, but we also have
	 * to protect our data also during client probing.
	 */

	/* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */
	if (shd->board_info) {//我只分析這種情況,后面會詳細分析soc_camera_i2c_init
		ret = soc_camera_i2c_init(icd, sdesc);
		if (ret < 0 && ret != -EPROBE_DEFER)
			goto eadd;
	} else if (!shd->add_device || !shd->del_device) {
		ret = -EINVAL;
		goto eadd;
	} else {
		mutex_lock(&ici->clk_lock);
		ret = ici->ops->clock_start(ici);
		mutex_unlock(&ici->clk_lock);
		if (ret < 0)
			goto eadd;

		if (shd->module_name)
			ret = request_module(shd->module_name);

		ret = shd->add_device(icd);
		if (ret < 0)
			goto eadddev;

		/*
		 * FIXME: this is racy, have to use driver-binding notification,
		 * when it is available
		 */
		control = to_soc_camera_control(icd);
		if (!control || !control->driver || !dev_get_drvdata(control) ||
		    !try_module_get(control->driver->owner)) {
			shd->del_device(icd);
			ret = -ENODEV;
			goto enodrv;
		}
	}

	mutex_lock(&ici->host_lock);
	ret = soc_camera_probe_finish(icd);//一切都准備好后,就可以正式添加video_device到系統來導出給應用層使用了,后面也會對該函數進一步分析
	mutex_unlock(&ici->host_lock);
	if (ret < 0)
		goto efinish;

	return 0;

efinish:
	if (shd->board_info) {
		soc_camera_i2c_free(icd);
	} else {
		shd->del_device(icd);
		module_put(control->driver->owner);
enodrv:
eadddev:
		mutex_lock(&ici->clk_lock);
		ici->ops->clock_stop(ici);
		mutex_unlock(&ici->clk_lock);
	}
eadd:
	video_device_release(icd->vdev);
	icd->vdev = NULL;
	if (icd->vdev) {
		video_device_release(icd->vdev);
		icd->vdev = NULL;
	}
evdc:
	v4l2_ctrl_handler_free(&icd->ctrl_handler);
	return ret;
}

下面重點分析soc_camera_i2c_init,它才是處理board_info的函數。還是直接將說明插入到代碼中:

static int soc_camera_i2c_init(struct soc_camera_device *icd,
			       struct soc_camera_desc *sdesc)
{
	struct soc_camera_subdev_desc *ssdd;
	struct i2c_client *client;
	struct soc_camera_host *ici;
	struct soc_camera_host_desc *shd = &sdesc->host_desc;
	struct i2c_adapter *adap;
	struct v4l2_subdev *subdev;
	char clk_name[V4L2_SUBDEV_NAME_SIZE];
	int ret;

	/* First find out how we link the main client */
	if (icd->sasc) {
		/* Async non-OF probing handled by the subdevice list */
		return -EPROBE_DEFER;
	}

	ici = to_soc_camera_host(icd->parent);//前面說了,icd的parent當然就是host對應的device,而該device里面的driver data部分早就被設置為v4l2_device了(詳情請看v4l2_device_register),前面也說了v4l2_device在v4l2框架里就代表着老大,i2c sensor對應的就是v4l2_subdev,代表着小弟。to_soc_camera_host里面通過container_of從v4l2_device找到ici
	adap = i2c_get_adapter(shd->i2c_adapter_id);//通過扳級相關文件里指定的i2c_adapter_id尋找該i2c host對應的數據結構,i2c驅動框架就不多說了,原理也差不多
	if (!adap) {
		dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n",
			shd->i2c_adapter_id);
		return -ENODEV;
	}

	ssdd = kzalloc(sizeof(*ssdd), GFP_KERNEL);
	if (!ssdd) {
		ret = -ENOMEM;
		goto ealloc;
	}

	memcpy(ssdd, &sdesc->subdev_desc, sizeof(*ssdd));
	/*
	 * In synchronous case we request regulators ourselves in
	 * soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try
	 * to allocate them again.
	 */
	ssdd->sd_pdata.num_regulators = 0;
	ssdd->sd_pdata.regulators = NULL;
	shd->board_info->platform_data = ssdd;

	snprintf(clk_name, sizeof(clk_name), "%d-%04x",
		 shd->i2c_adapter_id, shd->board_info->addr);

	icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd);//注冊一個v4l2_clk,i2c sensor驅動里會請求這個clk,操作集里的函數最終將定向到ici的操作集合里的函數中,也就是camera host實現的操作集中去
	if (IS_ERR(icd->clk)) {
		ret = PTR_ERR(icd->clk);
		goto eclkreg;
	}

	subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
				shd->board_info, NULL);//這里面就是處理i2c sensor了,后面會詳細分析
	if (!subdev) {
		ret = -ENODEV;
		goto ei2cnd;
	}

	client = v4l2_get_subdevdata(subdev);

	/* Use to_i2c_client(dev) to recover the i2c client */
	icd->control = &client->dev;

	return 0;
ei2cnd:
	v4l2_clk_unregister(icd->clk);
	icd->clk = NULL;
eclkreg:
	kfree(ssdd);
ealloc:
	i2c_put_adapter(adap);
	return ret;
}

未完,待續!
2015年6月


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM