Linux ov2640移植

一、设备树的从设备地址的确定

先看摄像头的数据手册:
在这里插入图片描述

而在i2c协议中,从设备地址有(7/10位)一般都是7位(最后一位为读写位),而0x60是8位的,所以整体右移一位变为0x30!

二、设备树gpio节点名称配错

原本是:

        ov2640: camera@0x30 {
                compatible = "ovti,ov2640";
                reg = <0x30>;
                status = "okay";

                pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_csi1 &pinctrl_pwn_rst>;
                resetb = <&gpio1 2 GPIO_ACTIVE_LOW>;
                pwdn = <&gpio1 4 GPIO_ACTIVE_HIGH>;
                clocks = <&clks IMX6UL_CLK_CSI>;
                clock-names = "xvclk";
                port {
                        camera_ep: endpoint {
                                remote-endpoint = <&csi_ep>;
                                bus-width = <8>;
                        };
                };
        };    

于是uboot启动时报错:

ov2640 1-0030: Product ID error fb:fb

说明读取i2c的设备信息出了问题,这不关摄像头部分,于是我们分析ov2640.c就行了。

进入:drivers\media\i2c\soc_camera\ov2640.c

  • probe

    static int ov2640_probe(struct i2c_client *client,
    			const struct i2c_device_id *did)
    {
    	struct ov2640_priv	*priv;
    	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
    	struct i2c_adapter	*adapter = to_i2c_adapter(client->dev.parent);
    	int			ret;
    
    	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
    		dev_err(&adapter->dev,
    			"OV2640: I2C-Adapter doesn't support SMBUS\n");
    		return -EIO;
    	}
    
    	priv = devm_kzalloc(&client->dev, sizeof(struct ov2640_priv), GFP_KERNEL);
    	if (!priv) {
    		dev_err(&adapter->dev,
    			"Failed to allocate memory for private data!\n");
    		return -ENOMEM;
    	}
    
    	priv->clk = v4l2_clk_get(&client->dev, "xvclk");
    	if (IS_ERR(priv->clk))
    		return -EPROBE_DEFER;
    
    	if (!ssdd && !client->dev.of_node) {
    		dev_err(&client->dev, "Missing platform_data for driver\n");
    		ret = -EINVAL;
    		goto err_clk;
    	}
    
    	if (!ssdd) {
    		ret = ov2640_probe_dt(client, priv);//这里面会设置priv以及soc_camera_subdev_desc的两个函数
    		if (ret)
    			goto err_clk;
    	}
    
    	v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops);
    	v4l2_ctrl_handler_init(&priv->hdl, 2);
    	v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
    			V4L2_CID_VFLIP, 0, 1, 1, 0);
    	v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
    			V4L2_CID_HFLIP, 0, 1, 1, 0);
    	priv->subdev.ctrl_handler = &priv->hdl;
    	if (priv->hdl.error) {
    		ret = priv->hdl.error;
    		goto err_clk;
    	}
    
    	ret = ov2640_video_probe(client);//报错在这里面
    	if (ret < 0)
    		goto err_videoprobe;
    
    	ret = v4l2_async_register_subdev(&priv->subdev);
    	if (ret < 0)
    		goto err_videoprobe;
    
    	dev_info(&adapter->dev, "OV2640 Probed\n");
    
    	return 0;
    
    err_videoprobe:
    	v4l2_ctrl_handler_free(&priv->hdl);
    err_clk:
    	v4l2_clk_put(priv->clk);
    	return ret;
    }
    
  • static int ov2640_video_probe(struct i2c_client *client)
    {
    	struct ov2640_priv *priv = to_ov2640(client);
    	u8 pid, ver, midh, midl;
    	const char *devname;
    	int ret;
    
    	ret = ov2640_s_power(&priv->subdev, 1);//上电
    	if (ret < 0)
    		return ret;
    
    	/*
    	 * check and show product ID and manufacturer ID
    	 */
    	i2c_smbus_write_byte_data(client, BANK_SEL, BANK_SEL_SENS);
    	pid  = i2c_smbus_read_byte_data(client, PID);
    	ver  = i2c_smbus_read_byte_data(client, VER);
    	midh = i2c_smbus_read_byte_data(client, MIDH);
    	midl = i2c_smbus_read_byte_data(client, MIDL);
    
    	switch (VERSION(pid, ver)) {
    	case PID_OV2640:
    		devname     = "ov2640";
    		break;
    	default:
    		dev_err(&client->dev,
    			"Product ID error %x:%x\n", pid, ver);
    		ret = -ENODEV;
    		goto done;
    	}
    
    	dev_info(&client->dev,
    		 "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
    		 devname, pid, ver, midh, midl);
    
    	ret = v4l2_ctrl_handler_setup(&priv->hdl);
    
    done:
    	ov2640_s_power(&priv->subdev, 0);
    	return ret;
    }
    

    于是我在想,会不会是ov2640_s_power上电没成功导致,读不出来?

  • static int ov2640_s_power(struct v4l2_subdev *sd, int on)
    {
    	struct i2c_client *client = v4l2_get_subdevdata(sd);
    	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
    	struct ov2640_priv *priv = to_ov2640(client);
    
    	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
    }
    
  • static inline int soc_camera_set_power(struct device *dev,
    		struct soc_camera_subdev_desc *ssdd, struct v4l2_clk *clk, bool on)
    {
    	return on ? soc_camera_power_on(dev, ssdd, clk)
    		  : soc_camera_power_off(dev, ssdd, clk);
    }
    
  • int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
    			struct v4l2_clk *clk)
    {
    	int ret;
    	bool clock_toggle;
    
    	if (clk && (!ssdd->unbalanced_power ||
    		    !test_and_set_bit(0, &ssdd->clock_state))) {
    		ret = v4l2_clk_enable(clk);
    		if (ret < 0) {
    			dev_err(dev, "Cannot enable clock: %d\n", ret);
    			return ret;
    		}
    		clock_toggle = true;
    	} else {
    		clock_toggle = false;
    	}
    
    	ret = regulator_bulk_enable(ssdd->sd_pdata.num_regulators,
    				    ssdd->sd_pdata.regulators);
    	if (ret < 0) {
    		dev_err(dev, "Cannot enable regulators\n");
    		goto eregenable;
    	}
    
    	if (ssdd->power) {
    		ret = ssdd->power(dev, 1);//调用前面ssdd注册的power
    		if (ret < 0) {
    			dev_err(dev,
    				"Platform failed to power-on the camera.\n");
    			goto epwron;
    		}
    	}
    
    	return 0;
    
    epwron:
    	regulator_bulk_disable(ssdd->sd_pdata.num_regulators,
    			       ssdd->sd_pdata.regulators);
    eregenable:
    	if (clock_toggle)
    		v4l2_clk_disable(clk);
    
    	return ret;
    }
    
  • /* OF probe functions */
    static int ov2640_hw_power(struct device *dev, int on)
    {
    	struct i2c_client *client = to_i2c_client(dev);
    	struct ov2640_priv *priv = to_ov2640(client);
    
    	dev_dbg(&client->dev, "%s: %s the camera\n",
    			__func__, on ? "ENABLE" : "DISABLE");
    
    	if (priv->pwdn_gpio)//这个没进来所以没有上电成功
    		gpiod_direction_output(priv->pwdn_gpio, !on);
    
    	return 0;
    }
    

    而这个ov2640_hw_power又是在ov2640_probe_dt中设置的

  • ov2640_probe_dt

    static int ov2640_probe_dt(struct i2c_client *client,
    		struct ov2640_priv *priv)
    {
    	/* Request the reset GPIO deasserted */
    	priv->resetb_gpio = devm_gpiod_get_optional(&client->dev, "resetb",
    			GPIOD_OUT_LOW);
    	if (!priv->resetb_gpio){
            dev_dbg(&client->dev, "resetb gpio is not assigned!\n");
            printk("---------------resetb_gpio get failed--------------");
        }
    		dev_dbg(&client->dev, "resetb gpio is not assigned!\n");
    	else if (IS_ERR(priv->resetb_gpio))
    		return PTR_ERR(priv->resetb_gpio);
    
    	/* Request the power down GPIO asserted */
    	priv->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "pwdn",
    			GPIOD_OUT_HIGH);//这个竟然没有获取成功
    	if (!priv->pwdn_gpio){
            dev_dbg(&client->dev, "pwdn gpio is not assigned!\n");
            printk("---------------pwdn_gpio get failed--------------");
        }
    		dev_dbg(&client->dev, "pwdn gpio is not assigned!\n");
    	else if (IS_ERR(priv->pwdn_gpio))
    		return PTR_ERR(priv->pwdn_gpio);
    
    	/* Initialize the soc_camera_subdev_desc */
    	priv->ssdd_dt.power = ov2640_hw_power;
    	priv->ssdd_dt.reset = ov2640_hw_reset;
    	client->dev.platform_data = &priv->ssdd_dt;
    
    	return 0;
    }
    

    dev_dbg是不会打印出来的,所以我们看不到结果,于是我们用printk。

  • 重新编译ov2640.c,加载内核

    ---------------resetb_gpio get failed--------------
    ---------------pwdn_gpio get failed--------------
    

    说明,确实是priv->resetb_gpio与priv->pwdn_gpio这两个节点获取失败造成的ov2640 1-0030: Product ID error fb:fb。

于是我们把这两个节点改一下名字:(其实gpio子系统的节点后缀都要加-gpios)

        ov2640: camera@0x30 {
                compatible = "ovti,ov2640";
                reg = <0x30>;
                status = "okay";

                pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_csi1 &pinctrl_pwn_rst>;
                resetb-gpios = <&gpio1 2 GPIO_ACTIVE_LOW>;
                pwdn-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
                clocks = <&clks IMX6UL_CLK_CSI>;
                clock-names = "xvclk";
                port {
                        camera_ep: endpoint {
                                remote-endpoint = <&csi_ep>;
                                bus-width = <8>;
                        };
                };
        };    

于是匹配成功:

ov2640 1-0030: ov2640 Product ID 26:42 Manufacturer ID 7f:a2

三、没有出现videox节点

在v4l2架构中,如果是uvc摄像头驱动,那么它由于只有一个subdev,所以它的video_device一定是在这个subdev的驱动注册的,而对应coms摄像头驱动,至少有两个subdev,它们之间通过v4l2_device连接起来,而一般是数据传输的subdev比如mipi/csi子设备的subdev驱动方注册的video_device,而i2c控制subdev则只是注册subdev进入v4l2中用于日后的控制(mipi的subdev中注册的video_device有效ioctl是调用到i2c subdev的ioctl)。

所以,没有出现videox节点的原因应该就是另一个csi的subdev驱动没有编译进内核。

通过寻找:我们找到drivers\media\platform\mxc\subdev\mx6s_capture.c

它的of_match_table:

static const struct of_device_id mx6s_csi_dt_ids[] = {
	{ .compatible = "fsl,imx6s-csi", },
	{ /* sentinel */ }
};

在我们设备树中也有:

                        csi: csi@021c4000 {
                                compatible = "fsl,imx6ul-csi", "fsl,imx6s-csi";
                                reg = <0x021c4000 0x4000>;
                                interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
                                clocks = <&clks IMX6UL_CLK_DUMMY>,
                                        <&clks IMX6UL_CLK_CSI>,
                                        <&clks IMX6UL_CLK_DUMMY>;
                                clock-names = "disp-axi", "csi_mclk", "disp_dcic";
                                status = "disabled";
                        };

于是确定,是这个驱动没错。

platform目录对应的Kconfig

config VIDEO_MXC_CAPTURE
	tristate "MXC Video For Linux Video Capture"
	depends on VIDEO_V4L2
	---help---
	  This is the video4linux2 capture driver based on i.MX video-in module.

打开mx6s_capture.c对应内核目录的Kconfig

if VIDEO_MXC_CAPTURE

config VIDEO_MXC_CSI_CAMERA
	tristate "CSI camera support"
	depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
	---help---
	  This is the video4linux2 capture driver based on CSI module.

config MXC_CAMERA_OV5640
        tristate "OmniVision ov5640 camera support"
        depends on VIDEO_MXC_CAPTURE && I2C
        ---help---
          If you plan to use the ov5640 Camera with your MXC system, say Y here.

config MXC_VADC
	tristate "mxc VADC support"
	depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
	---help---
		If you plan to use the VADC with your MXC system, say Y here.

config MXC_MIPI_CSI
	tristate "mxc mipi csi driver"
	depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
	---help---
	  This is a V4L2 driver for i.MX7D SoC MIPI-CSI2 receiver devices.

config MXC_CAMERA_OV5640_MIPI
	tristate "OmniVision ov5640 camera support using mipi"
	depends on MXC_MIPI_CSI && I2C
	---help---
	  If you plan to use the ov5640 Camera with mipi interface in your MXC system, say Y here.

config MXC_CAMERA_OV5647_MIPI
	tristate "OmniVision ov5647 camera support using mipi"
	depends on MXC_MIPI_CSI && I2C
	---help---
	  If you plan to use the ov5647 Camera with mipi interface in your MXC system, say Y here.
endif

所以我们应该配置VIDEO_MXC_CAPTURE与VIDEO_MXC_CSI_CAMERA

到内核的make menuconfig中去寻找:

在这里插入图片描述

于是配置好,这两个就行了。

之后加载内核成功看见video1。

四、去掉video0

之前我我一直很奇怪,为什么还没接任何摄像头就会有一个video0,于是这里打算搞清楚来源!

我们都知道,video的注册是在video_register_device里面

int __video_register_device(struct video_device *vdev, int type, int nr,
		int warn_if_nr_in_use, struct module *owner)
{
	.......
	.......
	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);
	video_device[vdev->minor] = vdev;//这里将video_device放入video_device数组里是很有必要的,因为后面的file要获取video_device的数据那么就可以通过inode获得次设备号来从数组中获得了
	mutex_unlock(&videodev_lock);

	if (vdev->ioctl_ops)
		determine_valid_ioctls(vdev);

	/* 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);//原来这个cdev是video_device的,之前以为是v4l2的,只不过它的ops使用的是v4l2的罢了
	if (ret < 0) {
		printk(KERN_ERR "%s: cdev_add failed\n", __func__);
		kfree(vdev->cdev);
		vdev->cdev = NULL;
		goto cleanup;
	}
	
	printk("--------------video_name = %s%d-------------\n",name_base, vdev->num);
	dump_stack();
	/* Part 4: register the device with sysfs */
	vdev->dev.class = &video_class;//video_class是在v4l2_dev.c中videodev_init创建的,这个函数是内核启动时调用的
	vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
	vdev->dev.parent = vdev->dev_parent;
	dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);//确定设备节点名字是videox
	ret = device_register(&vdev->dev);//注册设备节点
	.....
	.....
}

于是我加了:printk(“--------------video_name = %s%d-------------\n”,name_base, vdev->num);dump_stack();

内核打印:

----------------------mx6s_csi_probe--------------------
----------------------__video_register_device-----------------------
--------------video_name = video0-------------
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.1.15 #36
Hardware name: Freescale i.MX6 Ultralite (Device Tree)
[<80015dbc>] (unwind_backtrace) from [<80012748>] (show_stack+0x10/0x14)
[<80012748>] (show_stack) from [<808b875c>] (dump_stack+0x84/0xc4)
[<808b875c>] (dump_stack) from [<805b8d74>] (__video_register_device+0x1028/0x1130)
[<805b8d74>] (__video_register_device) from [<805f072c>] (mx6s_csi_probe+0x200/0x4ac)
[<805f072c>] (mx6s_csi_probe) from [<80384b18>] (platform_drv_probe+0x44/0xa4)
[<80384b18>] (platform_drv_probe) from [<803833d8>] (driver_probe_device+0x174/0x2b4)
[<803833d8>] (driver_probe_device) from [<803835e8>] (__driver_attach+0x8c/0x90)
[<803835e8>] (__driver_attach) from [<8038193c>] (bus_for_each_dev+0x68/0x9c)
[<8038193c>] (bus_for_each_dev) from [<80382b74>] (bus_add_driver+0x148/0x1f0)
[<80382b74>] (bus_add_driver) from [<80383be4>] (driver_register+0x78/0xf8)
[<80383be4>] (driver_register) from [<80009730>] (do_one_initcall+0x8c/0x1d8)
[<80009730>] (do_one_initcall) from [<80c01dac>] (kernel_init_freeable+0x144/0x1e4)
[<80c01dac>] (kernel_init_freeable) from [<808b4790>] (kernel_init+0xc/0xe8)
[<808b4790>] (kernel_init) from [<8000f528>] (ret_from_fork+0x14/0x2c)
CSI: Registered sensor subdevice: ov2640 1-0030
-------mxc_pxp_v4l2.c----------pxp_probe-----------------
----------------------__video_register_device-----------------------
--------------video_name = video1-------------
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.1.15 #36
Hardware name: Freescale i.MX6 Ultralite (Device Tree)
[<80015dbc>] (unwind_backtrace) from [<80012748>] (show_stack+0x10/0x14)
[<80012748>] (show_stack) from [<808b875c>] (dump_stack+0x84/0xc4)
[<808b875c>] (dump_stack) from [<805b8d74>] (__video_register_device+0x1028/0x1130)
[<805b8d74>] (__video_register_device) from [<805fc284>] (pxp_probe+0x114/0x1b4)
[<805fc284>] (pxp_probe) from [<80384b18>] (platform_drv_probe+0x44/0xa4)
[<80384b18>] (platform_drv_probe) from [<803833d8>] (driver_probe_device+0x174/0x2b4)
[<803833d8>] (driver_probe_device) from [<803835e8>] (__driver_attach+0x8c/0x90)
[<803835e8>] (__driver_attach) from [<8038193c>] (bus_for_each_dev+0x68/0x9c)
[<8038193c>] (bus_for_each_dev) from [<80382b74>] (bus_add_driver+0x148/0x1f0)
[<80382b74>] (bus_add_driver) from [<80383be4>] (driver_register+0x78/0xf8)
[<80383be4>] (driver_register) from [<80009730>] (do_one_initcall+0x8c/0x1d8)
[<80009730>] (do_one_initcall) from [<80c01dac>] (kernel_init_freeable+0x144/0x1e4)
[<80c01dac>] (kernel_init_freeable) from [<808b4790>] (kernel_init+0xc/0xe8)
[<808b4790>] (kernel_init) from [<8000f528>] (ret_from_fork+0x14/0x2c)

可以看到,我们的csi摄像头匹配到了video0,而这个video1可以看到是pxp_probe注册到的。

在内核根目录下搜索:pxp_probe路径为:drivers/media/platform/mxc/output/mxc_pxp_v4l2.c

static int pxp_probe(struct platform_device *pdev)
{
	struct pxps *pxp;
	struct v4l2_device *v4l2_dev;
	int err = 0;

	pxp = kzalloc(sizeof(*pxp), GFP_KERNEL);
	if (!pxp) {
		dev_err(&pdev->dev, "failed to allocate control object\n");
		err = -ENOMEM;
		goto exit;
	}

	dev_set_drvdata(&pdev->dev, pxp);

	v4l2_dev = kzalloc(sizeof(*v4l2_dev), GFP_KERNEL);
	if (!v4l2_dev) {
		dev_err(&pdev->dev, "failed to allocate v4l2_dev structure\n");
		err = -ENOMEM;
		goto freeirq;
	}

	err = v4l2_device_register(&pdev->dev, v4l2_dev);
	if (err) {
		dev_err(&pdev->dev, "register v4l2 device failed\n");
		goto freev4l2;
	}

	INIT_LIST_HEAD(&pxp->outq);
	spin_lock_init(&pxp->lock);
	mutex_init(&pxp->mutex);

	pxp->pdev = pdev;

	pxp->vdev = video_device_alloc();
	if (!pxp->vdev) {
		dev_err(&pdev->dev, "video_device_alloc() failed\n");
		err = -ENOMEM;
		goto relv4l2;
	}

	memcpy(pxp->vdev, &pxp_template, sizeof(pxp_template));
	pxp->vdev->v4l2_dev = v4l2_dev;
	video_set_drvdata(pxp->vdev, pxp);

	err = video_register_device(pxp->vdev, VFL_TYPE_GRABBER, video_nr);//这里也注册video_device????
	if (err) {
		dev_err(&pdev->dev, "failed to register video device\n");
		goto freevdev;
	}

	dev_info(&pdev->dev, "initialized\n");

exit:
	return err;

freevdev:
	video_device_release(pxp->vdev);
relv4l2:
	v4l2_device_unregister(v4l2_dev);
freev4l2:
	kfree(v4l2_dev);
freeirq:
	kfree(pxp);

	return err;
}
static const struct of_device_id imx_pxpv4l2_dt_ids[] = {
	{ .compatible = "fsl,imx6sl-pxp-v4l2", },
	{ /* sentinel */ }
};

我们不需要注册它,于是在设备树中找到fsl,imx6sl-pxp-v4l2

 68         pxp_v4l2 {
 69               compatible = "fsl,imx6ul-pxp-v4l2", "fsl,imx6sx-pxp-v4l2", "fsl,imx6sl-pxp-v4l2";
 70               status = "enable";
 71               this node must set diabled or it works
 72         };

这竟然不是.dtsi文件中的,这个节点信息那么少,应该这个去掉也没什么实际作用。

我们把整个节点注释掉,注意:如果它不是在根设备树中的那么把它改为disable它还是能匹配上,只有把整个节点注释才彻底消失

最后,终于发现只剩下我们的video0,也就是ov2640的节点了!

五、使用opencv3.4发现ov2640驱动缺少某个ioctl的实现!

其实在opencv3.1版本并不会出现这样的情况。

一旦打开摄像头:不断报错…

VIDIOC_QUERYCTRL: Inappropriate ioctl for device

应该是内核中缺少VIDIOC_QUERYCTRL这个ioctl了。

于是在内核中添加上,(由于这个VIDIOC_QUERYCTRL的作用是用于获取摄像头的属性,而不是设置,所以不是太重要,我们可以去其它的摄像头驱动中拿过来使用)

static const struct v4l2_ioctl_ops mx6s_csi_ioctl_ops = {
	.vidioc_querycap          = mx6s_vidioc_querycap,
	.vidioc_enum_fmt_vid_cap  = mx6s_vidioc_enum_fmt_vid_cap,
	.vidioc_try_fmt_vid_cap   = mx6s_vidioc_try_fmt_vid_cap,
	.vidioc_g_fmt_vid_cap     = mx6s_vidioc_g_fmt_vid_cap,
	.vidioc_s_fmt_vid_cap     = mx6s_vidioc_s_fmt_vid_cap,
	.vidioc_cropcap       = mx6s_vidioc_cropcap,
	.vidioc_s_crop        = mx6s_vidioc_s_crop,
	.vidioc_g_crop        = mx6s_vidioc_g_crop,
	.vidioc_reqbufs       = mx6s_vidioc_reqbufs,
	.vidioc_querybuf      = mx6s_vidioc_querybuf,
	.vidioc_qbuf          = mx6s_vidioc_qbuf,
	.vidioc_dqbuf         = mx6s_vidioc_dqbuf,
	.vidioc_g_std         = mx6s_vidioc_g_std,
	.vidioc_s_std         = mx6s_vidioc_s_std,
	.vidioc_querystd      = mx6s_vidioc_querystd,
	.vidioc_enum_input    = mx6s_vidioc_enum_input,
	.vidioc_g_input       = mx6s_vidioc_g_input,
	.vidioc_s_input       = mx6s_vidioc_s_input,
	.vidioc_streamon      = mx6s_vidioc_streamon,
	.vidioc_streamoff     = mx6s_vidioc_streamoff,
	.vidioc_g_parm        = mx6s_vidioc_g_parm,
	.vidioc_s_parm        = mx6s_vidioc_s_parm,
	.vidioc_enum_framesizes = mx6s_vidioc_enum_framesizes,
	.vidioc_enum_frameintervals = mx6s_vidioc_enum_frameintervals,

	.vidioc_queryctrl = mx6s_vidioc_queryctrl,
	//为什么要加这个呢?因为opencv3.4版本需要有这个ioctrl(3.1版本好像不需要),但是这个query的ioctrl很随意,
	//可以直接搬移其它驱动文件的,我这里般的是mxc\output\mxc_pxp_v4l2.c
};
struct v4l2_queryctrl ov2640_qctrl[] = {
	{
		.id 		= V4L2_CID_HFLIP,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
		.name		= "Horizontal Flip",
		.minimum	= 0,
		.maximum	= 1,
		.step		= 1,
		.default_value	= 0,
		.flags		= 0,
	}, {
		.id 	= V4L2_CID_VFLIP,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
		.name		= "Vertical Flip",
		.minimum	= 0,
		.maximum	= 1,
		.step		= 1,
		.default_value	= 0,
		.flags		= 0,
	}, {
		.id 	= V4L2_CID_PRIVATE_BASE,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.name		= "Rotation",
		.minimum	= 0,
		.maximum	= 270,
		.step		= 90,
		.default_value	= 0,
		.flags		= 0,
	}, {
		.id 	= V4L2_CID_PRIVATE_BASE + 1,
		.name		= "Background Color",
		.minimum	= 0,
		.maximum	= 0xFFFFFF,
		.step		= 1,
		.default_value	= 0,
		.flags		= 0,
		.type		= V4L2_CTRL_TYPE_INTEGER,
	}, {
		.id 	= V4L2_CID_PRIVATE_BASE + 2,
		.name		= "Set S0 Chromakey",
		.minimum	= -1,
		.maximum	= 0xFFFFFF,
		.step		= 1,
		.default_value	= -1,
		.flags		= 0,
		.type		= V4L2_CTRL_TYPE_INTEGER,
	}, {
		.id 	= V4L2_CID_PRIVATE_BASE + 3,
		.name		= "YUV Colorspace",
		.minimum	= 0,
		.maximum	= 1,
		.step		= 1,
		.default_value	= 0,
		.flags		= 0,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
	},
};

static int mx6s_vidioc_queryctrl(struct file *file, void *priv,
			 struct v4l2_queryctrl *qc)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(ov2640_qctrl); i++)
		if (qc->id && qc->id == ov2640_qctrl[i].id) {
			memcpy(qc, &(ov2640_qctrl[i]), sizeof(*qc));
			return 0;
		}

	return -EINVAL;
}

加上之后能够正常使用opencv3.4打开摄像头了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yrbzNCDZ-1689577134771)(C:\Users\cww\AppData\Roaming\Typora\typora-user-images\image-20230717141253077.png)]

.name = “Set S0 Chromakey”,
.minimum = -1,
.maximum = 0xFFFFFF,
.step = 1,
.default_value = -1,
.flags = 0,
.type = V4L2_CTRL_TYPE_INTEGER,
}, {
.id = V4L2_CID_PRIVATE_BASE + 3,
.name = “YUV Colorspace”,
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0,
.flags = 0,
.type = V4L2_CTRL_TYPE_BOOLEAN,
},
};

static int mx6s_vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qc)
{
int i;

for (i = 0; i < ARRAY_SIZE(ov2640_qctrl); i++)
	if (qc->id && qc->id == ov2640_qctrl[i].id) {
		memcpy(qc, &(ov2640_qctrl[i]), sizeof(*qc));
		return 0;
	}

return -EINVAL;

}


加上之后能够正常使用opencv3.4打开摄像头了。
![在这里插入图片描述](https://img-blog.csdnimg.cn/02937af5f9d54439aa3521e98725df5a.png)


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值