soc camera子系统之初始化i2c client

我们知道,当我们注册soc_camera host时,会通过搜索链表devices来寻找需要安装到该host上的soc camera device(这就意味着soc camera device的初始化必须要早于soc camera host),当有需要安装到该host上的设备时,我们就注册该soc camera device,根据总线,设备,驱动模型,这时就会调用soc_camera_probe函数,下面再次详细分析一下soc camera probe函数。

static int soc_camera_probe(struct device *dev)
{
	struct soc_camera_device *icd = to_soc_camera_dev(dev);
	struct soc_camera_host *ici = to_soc_camera_host(dev->parent);
	struct soc_camera_link *icl = to_soc_camera_link(icd);
	struct device *control = NULL;
	int ret;

	ret = ici->ops->add(icd);
	if (ret < 0)
		goto eadd;

	ret = soc_camera_power_set(icd, icl, 1);
	if (ret < 0)
		goto epower;

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

	/* Must have icd->vdev before registering the device */
	ret = video_dev_create(icd);
	if (ret < 0)
		goto evdc;

	/* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */
	if (icl->board_info) {
		ret = soc_camera_init_i2c(icd, icl);
		if (ret < 0)
			goto eadddev;
	} else if (!icl->add_device || !icl->del_device) {
		ret = -EINVAL;
		goto eadddev;
	} else {
		if (icl->module_name)
			ret = request_module(icl->module_name);

		ret = icl->add_device(icl, &icd->dev);
		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)) {
			icl->del_device(icl);
			goto enodrv;
		}
	}

	/* At this point client .probe() should have run already */
	ret = soc_camera_init_user_formats(icd);
	if (ret < 0)
		goto eiufmt;

	icd->field = V4L2_FIELD_ANY;

	icd->vdev->lock = &icd->video_lock;

	/*
	 * ..._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.
	 */
	mutex_lock(&icd->video_lock);

	ret = soc_camera_video_start(icd);
	if (ret < 0)
		goto evidstart;

	/* Do we have to sysfs_remove_link() before device_unregister()? */
	if (sysfs_create_link(&icd->dev.kobj, &to_soc_camera_control(icd)->kobj,
			      "control"))
		dev_warn(&icd->dev, "Failed creating the control symlink\n");

	ici->ops->remove(icd);
	soc_camera_power_set(icd, icl, 0);
	mutex_unlock(&icd->video_lock);

	return 0;

evidstart:
	mutex_unlock(&icd->video_lock);
	soc_camera_free_user_formats(icd);
eiufmt:
	if (icl->board_info) {
		soc_camera_free_i2c(icd);
	} else {
		icl->del_device(icl);
		module_put(control->driver->owner);
	}
enodrv:
eadddev:
	video_device_release(icd->vdev);
evdc:
	soc_camera_power_set(icd, icl, 0);
epower:
	ici->ops->remove(icd);
eadd:
	return ret;
}

我们着重分析一下ret =soc_camera_init_i2c(icd, icl);的过程。首先贴出soc_camera_init_i2c的代码。

static int soc_camera_init_i2c(structsoc_camera_device *icd,

                                   struct soc_camera_link *icl)

{

         structi2c_client *client;

         structsoc_camera_host *ici = to_soc_camera_host(icd->dev.parent);

         struct i2c_adapter *adap =i2c_get_adapter(icl->i2c_adapter_id);通过icl传入的adapter_id来获取i2c adapter适配器。

         structv4l2_subdev *subdev;

 

         if(!adap) {

                   dev_err(&icd->dev,"Cannot get I2C adapter #%d. No driver?\n",

                            icl->i2c_adapter_id);

                  goto ei2cga;

         }

 

         icl->board_info->platform_data = icd;//这里很重要哦,是为了建立icdi2c client之间的联系。

 

         subdev =v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,

                                     icl->board_info,NULL);//重点分析该代码

         if(!subdev)

                   gotoei2cnd;

 

         client= v4l2_get_subdevdata(subdev);

 

         /*Use to_i2c_client(dev) to recover the i2c client */

         dev_set_drvdata(&icd->dev,&client->dev);

 

         return0;

ei2cnd:

         i2c_put_adapter(adap);

ei2cga:

         return-ENODEV;

}

我们来贴出 v4l2_i2c_new_subdev_board的源码。

struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
		struct i2c_adapter *adapter, struct i2c_board_info *info,
		const unsigned short *probe_addrs)
{
	struct v4l2_subdev *sd = NULL;
	struct i2c_client *client;

	BUG_ON(!v4l2_dev);

	request_module(I2C_MODULE_PREFIX "%s", info->type);

	/* Create the i2c client */
	......
		client = i2c_new_device(adapter, info); //初始化一个i2c client,我们来分析一下该函数的行为。
【
	struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
	struct i2c_client	*client;
	int			status;

	client = kzalloc(sizeof *client, GFP_KERNEL); //为client申请分配内存。
	if (!client)
		return NULL;

	client->adapter = adap;

	client->dev.platform_data = info->platform_data;//还记得之前icl->board_info->platform_data = icd吗,这里就是将client->dev.platform_data指向icd,那么这里究竟是为了什么这样做呢,是为了在image capture驱动中获取到icd,下面会就具体驱动实例分析。

	if (info->archdata)
		client->dev.archdata = *info->archdata;

	client->flags = info->flags;
	client->addr = info->addr;
	client->irq = info->irq;

	strlcpy(client->name, info->type, sizeof(client->name));

	/* Check for address validity */
	status = i2c_check_client_addr_validity(client);//检查i2c地址是否可用。
	if (status) {
		dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
			client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
		goto out_err_silent;
	}

	/* Check for address business */
	status = i2c_check_addr_busy(adap, client->addr);//检查地址是否已经被占用。
	if (status)
		goto out_err;

	client->dev.parent = &client->adapter->dev;
	client->dev.bus = &i2c_bus_type;
	client->dev.type = &i2c_client_type;
	client->dev.of_node = info->of_node;

	dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
		     client->addr);
	status = device_register(&client->dev); //这里注册i2c client,根据总线,设备,驱动模型,我们可以知道,此时会触发对应的i2c driver(根据name 来match),这时就会调用image capture的驱动探测函数(probe函数。)
	if (status)
		goto out_err;

	dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
		client->name, dev_name(&client->dev));

	return client;

out_err:
	dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
		"(%d)\n", client->name, client->addr, status);
out_err_silent:
	kfree(client);
	return NULL;
】
	......
	sd = i2c_get_clientdata(client);//我们知道在image capture驱动中,已经建立了client和subdev之间的联系,因此可以通过i2c_get_clientdata来获取到subdev。

	/* Register with the v4l2_device which increases the module's
	   use count as well. */
	if (v4l2_device_register_subdev(v4l2_dev, sd))
		sd = NULL;
	/* Decrease the module use count to match the first try_module_get. */
	module_put(client->driver->driver.owner);

error:
	/* If we have a client but no subdev, then something went wrong and
	   we must unregister the client. */
	if (client && sd == NULL)
		i2c_unregister_device(client);
	return sd;
}

下面我就mt9m001.c来分析一下image capture的probe函数

static int mt9m001_probe(struct i2c_client *client,
			 const struct i2c_device_id *did)
{
	struct mt9m001 *mt9m001;
	struct soc_camera_device *icd = client->dev.platform_data; //通过platform_data获取icd。
	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); //通过client->dev.parent获取adapter,这里源于初始化client时(client->dev.parent = &client->adapter->dev;)
	struct soc_camera_link *icl;
	int ret;

	if (!icd) {
		dev_err(&client->dev, "MT9M001: missing soc-camera data!\n");
		return -EINVAL;
	}

	icl = to_soc_camera_link(icd);
	if (!icl) {
		dev_err(&client->dev, "MT9M001 driver needs platform data\n");
		return -EINVAL;
	}

	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
		dev_warn(&adapter->dev,
			 "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
		return -EIO;
	}

	mt9m001 = kzalloc(sizeof(struct mt9m001), GFP_KERNEL);
	if (!mt9m001)
		return -ENOMEM;

	v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops);//这个是个有趣且有伏笔的地方,我们来展开代码分析:
【
	void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
		const struct v4l2_subdev_ops *ops)
{
	v4l2_subdev_init(sd, ops);//初始化subdev,并且将其ops指针指向ops.
	sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
	/* the owner is the same as the i2c_client's driver owner */
	sd->owner = client->driver->driver.owner;
	/* i2c_client and v4l2_subdev point to one another */
	v4l2_set_subdevdata(sd, client); //使得 sd->dev_priv = client
	i2c_set_clientdata(client, sd);//使得client->dev->p->driver_data=sd
	通过以上两行代码,建立了subdev与i2c client直接的联系。
	/* initialize name */
	snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
		client->driver->driver.name, i2c_adapter_id(client->adapter),
		client->addr);
}
】

	/* Second stage probe - when a capture adapter is there */
	icd->ops		= &mt9m001_ops;//icd的回调函数集,由image capture驱动来实现。

.......
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值