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

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属性是一个字符串的列表,列表中的第一个字符串表征了结点代表的确切设备,形式为",",其后的字符串表征可兼容的其他设备。可以说前面的是特指,后面的则涵盖更广的范围。


上述.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 = <®_arm>; 
			pu-supply = <®_pu>; 
			soc-supply = <®_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是一个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= ,其中的每一组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, 
		   .of_match_table = mxc_v4l2_dt_ids, 
		   }, 
	.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 mxc_v4l2_dt_ids[] = { 
	{ 
		.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:

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:

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_attach\n");

pr_debug(" slave.name = %s\n", slave->name);

pr_debug(" master.name = %s\n", 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_attach\n");

pr_debug(" slave.name = %s\n", slave->name);

pr_debug(" master.name = %s\n", slave->u.slave->master->name);

+ pr_debug(" slave.ipu_id = %d\n", sdata->ipu_id);

+ pr_debug(" master.ipu_id = %d\n", 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 match\n",__func__);

+ return -1;

+ }

+

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

pr_debug("%s:csi doesn't match\n", __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) {




你可能感兴趣的:(i.MX6-IPU子系统)