dts文件分析---以ov5640为例,修改dts文件使ov5640使用第二个IPU

ARM 190浏览

ARMlinux中,每一个.dts文件都对应一个ARMmachine,这些文件都放在arch/arm/boot/dts文件夹中。同时,对于每一个SoC可能对应多个machine,这些dts文件中会包含许多共同的部分,所以就有了.dtsi文件。这个.dtsi文件类似C语言中的头文件,在其他的.dts文件中可以通过#include”xxxx.dtsi“
来包含这些头文件。

因为我们的开发板是imx6q-sabersd,所以首先查看对应的imx6q-sabresd.dts文件,有如下内容:

#include "imx6q.dtsi"
#include "imx6qdl-sabresd.dtsi"
/ {
	model = "Freescale i.MX6 Quad SABRE Smart Device Board";
	compatible = "fsl,imx6q-sabresd", "fsl,imx6q";
}; 

.dts文件的每个设备,都有一个compatible属性,compatible属性用于用户驱动和设备的绑定。compatible属性是一个字符串的列表,列表中的第一个字符串表征了结点代表的确切设备,形式为"<manufacturer>,<model>",其后的字符串表征可兼容的其他设备。可以说前面的是特指,后面的则涵盖更广的范围。

上述.dts文件中,root结点"/"compatible属性compatible=
"fsl,imx6q-sabresd","fsl,imx6q";
定义了系统的名称。Linux内核透过root结点"/"compatible属性即可判断它启动的是什么machine


但是在这个文件中,只有少量的设备,对于其他cpu等设备的描述信息都没有,肯定不是全部的设备信息,这时候就能看到这个文件中包含的其他文件,cpu等信息肯定是在其他地方描述的。

每个文件中都会有一个根结点/“,在编译设备文件的时候,编译器会将这些文件都放在同一个”/”结点下面,这样就不会担心各个文件中的根结点会重复,同时,编译器也会对不同文件中相同结点下的信息进行合并处理,在后面再具体分析。由于每个文件中都有一个根结点,这样就更方便我们书写这个设备树文件。

继续查看imx6q.dtsi文件:

/ {
	cpus {
		#address-cells = <1>;
		#size-cells = <0>;

		cpu0: cpu@0 {
			compatible = "arm,cortex-a9";
			device_type = "cpu";
			reg = <0>;
			next-level-cache = <&L2>;
			operating-points = <
				/* kHz    uV */
				1200000 1275000
				996000  1250000
				852000  1250000
				792000  1175000
				396000  975000
			>;
			fsl,soc-operating-points = <
				/* ARM kHz  SOC-PU uV */
				1200000 1275000
				996000	1250000
				852000	1250000
				792000	1175000
				396000	1175000
			>;
			clock-latency = <61036>; /* two CLK32 periods */
			clocks = <&clks IMX6QDL_CLK_ARM>,
				 <&clks IMX6QDL_CLK_PLL2_PFD2_396M>,
				 <&clks IMX6QDL_CLK_STEP>,
				 <&clks IMX6QDL_CLK_PLL1_SW>,
				 <&clks IMX6QDL_CLK_PLL1_SYS>,
				 <&clks IMX6QDL_PLL1_BYPASS>,
				 <&clks IMX6QDL_CLK_PLL1>,
				 <&clks IMX6QDL_PLL1_BYPASS_SRC> ;
			clock-names = "arm", "pll2_pfd2_396m", "step",
				      "pll1_sw", "pll1_sys", "pll1_bypass", "pll1", "pll1_bypass_src";
			arm-supply = <&reg_arm>;
			pu-supply = <&reg_pu>;
			soc-supply = <&reg_soc>;
		};

		cpu@1 {
			compatible = "arm,cortex-a9";
			device_type = "cpu";
			reg = <1>;
			next-level-cache = <&L2>;
		};

		cpu@2 {
			compatible = "arm,cortex-a9";
			device_type = "cpu";
			reg = <2>;
			next-level-cache = <&L2>;
		};

		cpu@3 {
			compatible = "arm,cortex-a9";
			device_type = "cpu";
			reg = <3>;
			next-level-cache = <&L2>;
		};
	};

这个文件中包含1root结点"/"root结点下面含一系列子结点,本例中为"cpus";结点”cpus”下又有一个子结点”cpu0“,结点"cpu0"下又含有一系列子结点,为"cpu0","cpu1",”cpu2”和”cpu3”
各结点都有一系列属性。这些属性可能为空,可能为字符串,可能为字符串数组,也可能为Cells(由u32整数组成)。

注意cpuscpus2cpu子结点的命名,它们遵循的组织形式为:<name>[@<unit-address>]<>中的内容是必选项,[]中的则为可选项。name是一个ASCII字符串,用于描述结点对应的设备类型,如3comEthernet适配器对应的结点name宜为ethernet,而不是3com509。如果一个结点描述的设备有地址,则应该给出@unit-address。多个相同类型设备结点的name可以一样,只要unit-address不同即可,如本例中含有cpu@0cpu@1以及serial@101f0000serial@101f2000这样的同名结点。设备的unit-address地址也经常在其对应结点的reg属性中给出。ePAPR标准给出了结点命名的规范。

下面来看cpus结点的信息:

		#address-cells = <1>;
		#size-cells = <0>;

首先前面的#代表是数字,对于这个cells的概念必须理解:一个cell代表一个u32整数,而这个address-cells代表子结点的address所包含的cells数目(即是由几个u32所组成的)。同样,size-cells代表子结点的size所包含的cells数目(即是由几个u32所组成的)。这两个概念与子结点的reg信息息息相关,也就是说,如果子结点中包含了reg信息的话,这个reg的表示方法是通过父结点的address-cellssize-cells来指定的。

(一)可寻址的设备使用如下信息来在DeviceTree中编码地址信息:

  • reg

  • #address-cells

  • #size-cells

其中reg的组织形式为reg=
<address1 length1 [address2 length2] [address3 length3] ...>
,其中的每一组addresslength表明了设备使用的一个地址范围。address1个或多个32位的整型(即cell),而length则为cell的列表或者为空(若#size-cells=
0
)。addresslength字段是可变长的,父结点的#address-cells#size-cells分别决定了子结点的reg属性的addresslength字段的长度。

以上面的信息为例:

cpus {
		#address-cells = <1>;
		#size-cells = <0>;

		cpu@1 {
			compatible = "arm,cortex-a9";
			device_type = "cpu";
			reg = <1>;
			next-level-cache = <&L2>;
		};

		cpu@2 {
			compatible = "arm,cortex-a9";
			device_type = "cpu";
			reg = <2>;
			next-level-cache = <&L2>;
		};
	};

cpus结点中指定了#address-cells=
<1>
#size-cells=
<0>,
决定了4cpu子结点的address1,而length为空,于是形成了4cpureg=
<0>; reg = <1>; reg = <
2>;
reg=
<
3>;

再来看两个例子

soc {
		#address-cells = <1>;
		#size-cells = <1>;

	timer@00a00600 {
			compatible = "arm,cortex-a9-twd-timer";
			reg = <0x00a00600 0x20>;
		};

	ipu2: ipu@02800000 {
			compatible = "fsl,imx6q-ipu";
			reg = <0x02800000 0x400000>;
		};
};

soc结点中指定了#address-cells=
<1>
#size-cells=
<1>,
决定了下面timer子结点的address1length1

External-bus {
#address-cells = <2>
#size-cells = <1>;

ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
interrupts = < 5 2 >;
};

flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};

external-bus结点中指定了#address-cells=
<2>
#size-cells=<1>,决定了下面ethernet子结点和flash子结点的address2length1。同时需要注意的是,在这里数字的表示都是大端模式,如果address用两个cells来表示,那么想要知道这个外设的地址,就直接将两个cells里面的地址连接起来即可。

(二)DeviceTree中还可以表示中断连接信息,对于中断控制器而言,它提供如下属性:
interrupt-controller---
这个属性为空,中断控制器应该加上此属性表明自己的身份;
#interrupt-cells
#address-cells#size-cells相似,它表明连接此中断控制器的设备的interrupts属性的cell大小。

intc: interrupt-controller@00a01000 {
		compatible = "arm,cortex-a9-gic";
		#interrupt-cells = <3>;
		#address-cells = <1>;
		#size-cells = <1>;
		interrupt-controller;
		reg = <0x00a01000 0x1000>,
		      <0x00a00100 0x100>;
	};

soc {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "simple-bus";
		interrupt-parent = <&intc>;

	ipu1: ipu@02400000 {
			interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>,
				     <0 5 IRQ_TYPE_LEVEL_HIGH>;
		};
};

在整个DeviceTree中,与中断相关的属性还包括:
interrupt-parent---
设备结点透过它来指定它所依附的中断控制器的phandle,当结点没有指定interrupt-parent时,则从父结点继承。对于本例而言,soc结点指定了interrupt-parent=
<&intc>;
其对应于intc:interrupt-controller@00a01000,而soc结点的子结点ipu1并未指定interrupt-parent,因此它们都继承了intc,即位于0x00a01000的中断控制器。
interrupts---
用到了中断的设备结点通过它指定中断号、触发方法等,具体这个属性含有多少个cell,由它依附的中断控制器结点的#interrupt-cells属性决定。而具体每个cell又是什么含义,一般由驱动的实现决定,而且也会在DeviceTreebinding文档中说明。

对于其它的有关dts文件的信息,查看ePAPR标准

下面是关于imx6q-sabresd开发板有关ipu信息的描述:

imx6qdl.dtsi文件中:

mipi_csi: mipi_csi@021dc000 { /* MIPI-CSI */
				compatible = "fsl,imx6q-mipi-csi2";
				reg = <0x021dc000 0x4000>;
				interrupts = <0 100 0x04>, <0 101 0x04>;
				clocks = <&clks IMX6QDL_CLK_HSI_TX>,
					 <&clks IMX6QDL_CLK_EMI_SEL>,
					 <&clks IMX6QDL_CLK_VIDEO_27M>;
				/* Note: clks 138 is hsi_tx, however, the dphy_c
				 * hsi_tx and pll_refclk use the same clk gate.
				 * In current clk driver, open/close clk gate do
				 * use hsi_tx for a temporary debug purpose.
				 */
				clock-names = "dphy_clk", "pixel_clk", "cfg_clk";
				status = "disabled";
			};

ipu1: ipu@02400000 {
			compatible = "fsl,imx6q-ipu";
			reg = <0x02400000 0x400000>;
			interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>,
				     <0 5 IRQ_TYPE_LEVEL_HIGH>;
			clocks = <&clks IMX6QDL_CLK_IPU1>,
				 <&clks IMX6QDL_CLK_IPU1_DI0>, <&clks IMX6QDL_CLK_IPU1_DI1>,
				 <&clks IMX6QDL_CLK_IPU1_DI0_SEL>, <&clks IMX6QDL_CLK_IPU1_DI1_SEL>,
				 <&clks IMX6QDL_CLK_LDB_DI0>, <&clks IMX6QDL_CLK_LDB_DI1>;
			clock-names = "bus",
				      "di0", "di1",
				      "di0_sel", "di1_sel",
				      "ldb_di0", "ldb_di1";
			resets = <&src 2>;
			bypass_reset = <0>;
		};

imx6q.dtsi文件中:

ipu2: ipu@02800000 {
			compatible = "fsl,imx6q-ipu";
			reg = <0x02800000 0x400000>;
			interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>,
				     <0 7 IRQ_TYPE_LEVEL_HIGH>;
			clocks = <&clks IMX6QDL_CLK_IPU2>,
				 <&clks IMX6QDL_CLK_IPU2_DI0>, <&clks IMX6QDL_CLK_IPU2_DI1>,
				 <&clks IMX6QDL_CLK_IPU2_DI0_SEL>, <&clks IMX6QDL_CLK_IPU2_DI1_SEL>,
				 <&clks IMX6QDL_CLK_LDB_DI0>, <&clks IMX6QDL_CLK_LDB_DI1>;
			clock-names = "bus",
				      "di0", "di1",
				      "di0_sel", "di1_sel",
				      "ldb_di0", "ldb_di1";
			resets = <&src 4>;
			bypass_reset = <0>;
		};

imx6qdl-sabresd.dtsi文件中:

	v4l2_cap_0 {
		compatible = "fsl,imx6q-v4l2-capture";
		ipu_id = <0>;
		csi_id = <0>;
		mclk_source = <0>;
		status = "okay";
	};

	v4l2_cap_1 {
		compatible = "fsl,imx6q-v4l2-capture";
		ipu_id = <0>;
		csi_id = <1>;
		mclk_source = <0>;
		status = "okay";
	};

&i2c1 {
	clock-frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c1>;
	status = "okay";

	ov564x: ov564x@3c {
		compatible = "ovti,ov564x";
		reg = <0x3c>;
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_ipu1_2>;
		clocks = <&clks IMX6QDL_CLK_CKO>;
		clock-names = "csi_mclk";
		DOVDD-supply = <&vgen4_reg>; /* 1.8v */
		AVDD-supply = <&vgen3_reg>;  /* 2.8v, on rev C board is VGEN3,
						on rev B board is VGEN5 */
		DVDD-supply = <&vgen2_reg>;  /* 1.5v*/
		pwn-gpios = <&gpio1 16 1>;   /* active low: SD1_DAT0 */
		rst-gpios = <&gpio1 17 0>;   /* active high: SD1_DAT1 */
		csi_id = <0>;
		mclk = <24000000>;
		mclk_source = <0>;
	};
};

&i2c2 {
	clock-frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c2>;
	status = "okay";

	ov564x_mipi: ov564x_mipi@3c { /* i2c2 driver */
		compatible = "ovti,ov564x_mipi";
		reg = <0x3c>;
		clocks = <&clks 201>;
		clock-names = "csi_mclk";
		DOVDD-supply = <&vgen4_reg>; /* 1.8v */
		AVDD-supply = <&vgen3_reg>;  /* 2.8v, rev C board is VGEN3
						rev B board is VGEN5 */
		DVDD-supply = <&vgen2_reg>;  /* 1.5v*/
		pwn-gpios = <&gpio1 19 1>;   /* active low: SD1_CLK */
		rst-gpios = <&gpio1 20 0>;   /* active high: SD1_DAT2 */
		csi_id = <1>;
		mclk = <24000000>;
		mclk_source = <0>;
	};
};

&mipi_csi {
	status = "okay";
	ipu_id = <0>;
	csi_id = <1>;
	v_channel = <0>;
	lanes = <2>;
};

mxc_v4l2_capture.c文件中,

static struct platform_driver mxc_v4l2_driver = {
	.driver = {
		   .name = "mxc_v4l2_capture",
		   .owner = THIS_MODULE,
		 <span style="color:#FF0000;">  .of_match_table = mxc_v4l2_dt_ids, </span>
		   },
	.id_table = imx_v4l2_devtype,
	.probe = mxc_v4l2_probe,
	.remove = mxc_v4l2_remove,
	.suspend = mxc_v4l2_suspend,
	.resume = mxc_v4l2_resume,
	.shutdown = NULL,
};
static const struct of_device_id <span style="color:#FF0000;">mxc_v4l2_dt_ids[]</span> = {
	{
		.compatible = "fsl,imx6q-v4l2-capture",
		.data = &imx_v4l2_devtype[IMX6_V4L2],
	}, {
		/* sentinel */
	}
};
MODULE_DEVICE_TABLE(of, mxc_v4l2_dt_ids);

当内核中有匹配的驱动与设备的时候,就会调用mxc_v4l2_probe函数。是否匹配,在内核中是通过match函数的:

xxxxxxxxxxxxxxx

这个match函数中首先匹配of设备树类型的:匹配的就是这个compatible变量。向上搜索这个"fsl,imx6q-v4l2-capture",可以发现,在dts类文件中,匹配的是v4l2_cap_0v4l2_cap_1。当这两个设备注册到内核中的时候,就会调用两次mxc_v4l2_probe函数。而在这个mxc_v4l2_probe函数中,会通过ret=
of_property_read_u32(np, "ipu_id", &ipu_id);
ret=
of_property_read_u32(np, "csi_id", &csi_id);
两个函数分别从dts类文件中读取相应的ipu_idcsi_id号。

分别执行两次mxc_v4l2_probe函数,就会生成两个cam_data结构体,每个结构体里面都包含了一个void*ipu,会通过找到的ipu_id来对应将ipu_array数组中的ipu结构体取出来,赋给cam->ipu。通过cam->ipu=
ipu_get_soc(ipu_id);
这条语句。
同时,会通过cam->ipu_id=
ipu_id;
cam->csi=
csi_id;
对应将从dts类文件中获取到这两个号设置到cam_data结构体中。

获取到的ipu_idcsi_id分别从下面可以看出来:

/ {
	v4l2_cap_0 {
		compatible = "fsl,imx6q-v4l2-capture";
		ipu_id = <0>;
		csi_id = <0>;
		mclk_source = <0>;
		status = "okay";
	};

	v4l2_cap_1 {
		compatible = "fsl,imx6q-v4l2-capture";
		ipu_id = <0>;
		csi_id = <1>;
		mclk_source = <0>;
		status = "okay";
	};

同时注意,在mxc-v4l2_capture.c中同样设置了:

	cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL);
	cam->self->module = THIS_MODULE;
	sprintf(cam->self->name, "mxc_v4l2_cap%d", cam->csi);
	cam->self->type = v4l2_int_type_master;
	cam->self->u.master = &mxc_v4l2_master;

所以v4l2_cap_0所对应的cam_data结构体中的self->name"mxc_v4l2_cap0"v4l2_cap_1所对应的cam_data结构体中的self->name"mxc_v4l2_cap1"

同时需要注意的是,这两个设备就是所谓的master设备,即cam->self结构体就是master所对应的结构体实体。

那么再来看看slave设备(还是以ov5640为例):

static struct i2c_driver ov5640_i2c_driver = {
	.driver = {
		  .owner = THIS_MODULE,
		  .name  = "ov564x",
		  },
	.probe  = ov5640_probe,
	.remove = ov5640_remove,
	.id_table = ov5640_id,
};

最终在match函数中,这个结构体里面没有对应的dts类型的匹配项,最终就会通过名字来匹配:

&i2c1 {
	ov564x: ov564x@3c {
		compatible = "ovti,ov564x";
		reg = <0x3c>;
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_ipu1_2>;
		clocks = <&clks IMX6QDL_CLK_CKO>;
		clock-names = "csi_mclk";
		DOVDD-supply = <&vgen4_reg>; /* 1.8v */
		AVDD-supply = <&vgen3_reg>;  /* 2.8v, on rev C board is VGEN3,
						on rev B board is VGEN5 */
		DVDD-supply = <&vgen2_reg>;  /* 1.5v*/
		pwn-gpios = <&gpio1 16 1>;   /* active low: SD1_DAT0 */
		rst-gpios = <&gpio1 17 0>;   /* active high: SD1_DAT1 */
		csi_id = <0>;
		mclk = <24000000>;
		mclk_source = <0>;
	}; 

可以看出来,在这个0v564xdts类文件中,并没有指定这个设备所对应的ipu_id,只是指定了csi_id0.

如果有匹配的驱动,就会调用到ov5640_probe函数,在这个函数中,通过retval=
of_property_read_u32(dev->of_node, "csi_id",&(ov5640_data.csi));
来将从dts里面获取的csi_id号填充到ov5640_data中。之后将ov5640_int_device.priv指向ov5640_data

ov5640_int_device.priv= &ov5640_data;

retval= v4l2_int_device_register(&ov5640_int_device);

通过v4l2_int_device_register函数以后,就会调用到v4l2_int_device_try_attach_all();函数,在这个函数中,masterslave设备就会互相匹配。成功的话,就会调用到master里面的attach函数。

下面来看看开机信息:

首先是注册两个ipu,这两个ipu的地址分别是:0x024000000x02800000

这是第一个ipuprobe信息:

imx-ipuv32400000.ipu: <ipu_probe>

imx-ipuv32400000.ipu: revision is IPUv3H

imx-ipuv32400000.ipu: IPU CM Regs = c0816000

imx-ipuv32400000.ipu: IPU IC Regs = c081e000

imx-ipuv32400000.ipu: IPU IDMAC Regs = c0826000

imx-ipuv32400000.ipu: IPU DP Regs = c082e000

imx-ipuv32400000.ipu: IPU DC Regs = c0836000

imx-ipuv32400000.ipu: IPU DMFC Regs = c083e000

imx-ipuv32400000.ipu: IPU DI0 Regs = c0846000

imx-ipuv32400000.ipu: IPU DI1 Regs = c084e000

imx-ipuv32400000.ipu: IPU SMFC Regs = c0856000

imx-ipuv32400000.ipu: IPU CSI0 Regs = c085e000

imx-ipuv32400000.ipu: IPU CSI1 Regs = c0866000

imx-ipuv32400000.ipu: IPU CPMem = c0900000

imx-ipuv32400000.ipu: IPU TPMem = c08e0000

imx-ipuv32400000.ipu: IPU DC Template Mem = c0940000

imx-ipuv32400000.ipu: IPU VDI Regs = c086c000

imx-ipuv32400000.ipu: IPU DMFC NORMAL mode: 1(0~1), 5B(4,5), 5F(6,7)

imx-ipuv32400000.ipu: ipu_clk = 264000000

ipu_task_thread:sched_setaffinity cpu:0.

ipu_task_thread:sched_setaffinity cpu:0.

这是第二个ipuprobe信息:

imx-ipuv32800000.ipu: <ipu_probe>

imx-ipuv32800000.ipu: revision is IPUv3H

imx-ipuv32800000.ipu: IPU CM Regs = c086e000

imx-ipuv32800000.ipu: IPU IC Regs = c087a000

imx-ipuv32800000.ipu: IPU IDMAC Regs = c087c000

imx-ipuv32800000.ipu: IPU DP Regs = c087e000

imx-ipuv32800000.ipu: IPU DC Regs = c08bc000

imx-ipuv32800000.ipu: IPU DMFC Regs = c08be000

imx-ipuv32800000.ipu: IPU DI0 Regs = c08c6000

imx-ipuv32800000.ipu: IPU DI1 Regs = c08ce000

imx-ipuv32800000.ipu: IPU SMFC Regs = c08d6000

imx-ipuv32800000.ipu: IPU CSI0 Regs = c08d8000

imx-ipuv32800000.ipu: IPU CSI1 Regs = c08da000

imx-ipuv32800000.ipu: IPU CPMem = c0980000

imx-ipuv32800000.ipu: IPU TPMem = c09c0000

imx-ipuv32800000.ipu: IPU DC Template Mem = c0a00000

imx-ipuv32800000.ipu: IPU VDI Regs = c08dc000

imx-ipuv32800000.ipu: IPU DMFC NORMAL mode: 1(0~1), 5B(4,5), 5F(6,7)

imx-ipuv32800000.ipu: ipu_clk = 264000000

ipu_task_thread:sched_setaffinity cpu:0.

ipu_task_thread:sched_setaffinity cpu:0.

之后就是mxc_v4l2_capture的注册,在这个过程中,会注册v4l2_cap_0v4l2_cap_1两个设备,会分别调用两次mxc_v4l2_probe函数,开机信息如下:

第一次probe信息:

InMVC:camera_init

**********************mxc_v4l2_probe**********************

InMVC: init_camera_struct

Videodevice registered: Mxc Camera #0

第二次probe信息:

**********************mxc_v4l2_probe**********************

InMVC: init_camera_struct

Videodevice registered: Mxc Camera #1

这两个master设备注册以后,如果这时候ov5640设备已经注册的话,就会调用到mxc_v4l2_master_attach函数,如果ov5640设备没有注册的话,就会等待它注册,然后同样会调用到mxc_v4l2_master_attach函数。在这个函数中

pr_debug("InMVC: mxc_v4l2_master_attachn");

pr_debug(" slave.name = %sn", slave->name);

pr_debug(" master.name = %sn", slave->u.slave->master->name);

所以看看开机信息中:

InMVC: mxc_v4l2_master_attach

slave.name= ov564x

master.name= mxc_v4l2_cap1

mxc_v4l2_master_attach:csi doesn't match

首先匹配的是mxc_v4l2_cap1,即v4l2_cap_1设备,这个设备的ipu_id0,csi_id1;而ov5640设备的csi_id0,所以它们俩并不匹配。所以最终会打印出csidoesn't
match
那句话。

InMVC: mxc_v4l2_master_attach

slave.name= ov564x

master.name= mxc_v4l2_cap0

Endof mxc_v4l2_master_attach: v2f pix widthxheight 288 x 352

Endof mxc_v4l2_master_attach: crop_bounds widthxheight 640 x 480

Endof mxc_v4l2_master_attach: crop_defrect widthxheight 640 x 480

Endof mxc_v4l2_master_attach: crop_current widthxheight 640 x 480

cameraov5640 is found

之后匹配的是mxc_v4l2_cap0,即v4l2_cap_0设备,这个设备的ipu_id0,csi_id0ov5640设备的csi_id0,所以它们俩匹配。打印出下面的信息,表明匹配成功。

从这里可以看出来,对于OV5640这个设备来说,在这里只是匹配了csi_id,并没有为它指定ipu_id,所以想要将两个ipu都使用起来的话,肯定要在这个地方进行修改。

通过上面的分析,我们可以看出来,对于ov5640设备来说,它只使用了csi_id,为此,为这个结构体添加上ipu_id这个选项,用来选择使用哪一个ipu,同时修改master设备的ipu_id,将v4l2_cap_1修改为使用ipu_id=
1
,同时,在ov5640_probe函数中,使用retval=
of_property_read_u32(dev->of_node, "ipu_id",&(ov5640_data.ipu_id));
来读取ipu_id号。

这个修改的目的是直接让ov5640使用ipu1

在做mxc_v4l2_capture.c这个实验的时候,可以看到打印信息如下:

imx-ipuv32400000.ipu: ********** In ipu_csi_set_window_sizefunction!***********

imx-ipuv32400000.ipu: *********************Before : CSI_ACT_FRM_SIZE =0x011F015F

imx-ipuv32400000.ipu: *********************After : CSI_ACT_FRM_SIZE =0x01DF027F

imx-ipuv32400000.ipu: ********** In ipu_csi_set_window_posfunction!***********

imx-ipuv32400000.ipu: *********************Before : CSI_OUT_FRM_CTRL =0x00000000

imx-ipuv32400000.ipu: *********************After : CSI_OUT_FRM_CTRL =0x00000000

在这些打印信息中,首先打印出来了ipu0的首地址,都为2400000,是第一个ipu的地址,通过这些修改以后,所有的打印信息应该都变为ipu1的首地址2800000.


修改如下所示:

---a/arch/arm/boot/dts/imx6q-sabresd.dts

+++b/arch/arm/boot/dts/imx6q-sabresd.dts

@@-18,6 +18,10 @@

/{

model= "Freescale i.MX6 Quad SABRE Smart Device Board";

compatible= "fsl,imx6q-sabresd", "fsl,imx6q";

+

+ v4l2_cap_1 {

+ ipu_id = <1>;

+ };

};

&battery{

diff--git a/arch/arm/boot/dts/imx6qdl-sabresd.dtsib/arch/arm/boot/dts/imx6qdl-sabresd.dtsi

index2d2e483..bad0f1a 100644

---a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi

+++b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi

@@-233,7 +233,7 @@

v4l2_cap_0{

compatible= "fsl,imx6q-v4l2-capture";

- ipu_id = <0>;

+ ipu_id = <1>;

csi_id= <0>;

mclk_source= <0>;

status= "okay";

@@-354,6 +354,7 @@

DVDD-supply= <&vgen2_reg>; /* 1.5v*/

pwn-gpios= <&gpio1 16 1>; /* active low: SD1_DAT0 */

rst-gpios= <&gpio1 17 0>; /* active high: SD1_DAT1 */

+ ipu_id = <1>;

csi_id= <0>;

mclk= <24000000>;

mclk_source= <0>;

diff--git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.cb/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c

index9a42daf..4e87379 100644

---a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c

+++b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c

@@-2744,7 +2751,10 @@ static int init_camera_struct(cam_data
*cam,struct platform_device *pdev)

cam->self= kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL);

cam->self->module= THIS_MODULE;

- sprintf(cam->self->name, "mxc_v4l2_cap%d",cam->csi);

+ if (cam->ipu_id == 0)

+ sprintf(cam->self->name, "mxc_v4l2_cap_%d",cam->csi);

+ else

+ sprintf(cam->self->name, "mxc_v4l2_cap%d",cam->csi +
2);

cam->self->type= v4l2_int_type_master;

cam->self->u.master= &mxc_v4l2_master;

@@-3010,12 +3020,19 @@ static int mxc_v4l2_master_attach(structv4l2_int_device
*slave)

pr_debug("InMVC: mxc_v4l2_master_attachn");

pr_debug(" slave.name = %sn", slave->name);

pr_debug(" master.name = %sn", slave->u.slave->master->name);

+ pr_debug(" slave.ipu_id = %dn", sdata->ipu_id);

+ pr_debug(" master.ipu_id = %dn", cam->ipu_id);

if(slave == NULL) {

pr_err("ERROR:v4l2 capture: slave parameter not valid.n");

return-1;

}

+ if (sdata->ipu_id != cam->ipu_id){

+ pr_debug("%s: ipu_id doesn't matchn",__func__);

+ return -1;

+ }

+

if(sdata->csi != cam->csi) {

pr_debug("%s:csi doesn't matchn", __func__);

return-1;

diff--git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.hb/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h

indexf671775..c57b7a7 100644

---a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h

+++b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h

@@-253,6 +253,7 @@ struct sensor_data {

u32mclk;

u8mclk_source;

structclk *sensor_clk;

+ int ipu_id;

intcsi;

void(*io_init)(void);

diff--git a/drivers/media/platform/mxc/capture/ov5640.cb/drivers/media/platform/mxc/capture/ov5640.c

indexec8b809..d5a41a1 100644

---a/drivers/media/platform/mxc/capture/ov5640.c

+++b/drivers/media/platform/mxc/capture/ov5640.c

@@-1869,6 +1869,13 @@ static int ov5640_probe(struct i2c_client*client,

returnretval;

}

+ retval = of_property_read_u32(dev->of_node, "ipu_id",

+ &(ov5640_data.ipu_id));

+ if (retval)

+ {

+ ov5640_data.ipu_id = 0;

+ }

+

retval= of_property_read_u32(dev->of_node, "csi_id",

&(ov5640_data.csi));

if(retval) {