在ARMlinux中,每一个.dts文件都对应一个ARM的machine,这些文件都放在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>;
};
};
这个文件中包含1个root结点"/";root结点下面含一系列子结点,本例中为"cpus";结点”cpus”下又有一个子结点”cpu0“,结点"cpu0"下又含有一系列子结点,为"cpu0","cpu1",”cpu2”和”cpu3”;
各结点都有一系列属性。这些属性可能为空,可能为字符串,可能为字符串数组,也可能为Cells(由u32整数组成)。
注意cpus和cpus的2个cpu子结点的命名,它们遵循的组织形式为:
下面来看cpus结点的信息:
#address-cells = <1>;
#size-cells = <0>;
首先前面的#代表是数字,对于这个cells的概念必须理解:一个cell代表一个u32整数,而这个address-cells代表子结点的address所包含的cells数目(即是由几个u32所组成的)。同样,size-cells代表子结点的size所包含的cells数目(即是由几个u32所组成的)。这两个概念与子结点的reg信息息息相关,也就是说,如果子结点中包含了reg信息的话,这个reg的表示方法是通过父结点的address-cells和size-cells来指定的。
(一)可寻址的设备使用如下信息来在DeviceTree中编码地址信息:
reg
#address-cells
#size-cells
其中reg的组织形式为reg=
以上面的信息为例:
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>,决定了4个cpu子结点的address为1,而length为空,于是形成了4个cpu的reg= <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子结点的address为1,length为1。
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子结点的address为2,length为1。同时需要注意的是,在这里数字的表示都是大端模式,如果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又是什么含义,一般由驱动的实现决定,而且也会在DeviceTree的binding文档中说明。
对于其它的有关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_0和v4l2_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_id和csi_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_id和csi_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>;
};
可以看出来,在这个0v564x的dts类文件中,并没有指定这个设备所对应的ipu_id,只是指定了csi_id为0.
如果有匹配的驱动,就会调用到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();函数,在这个函数中,master和slave设备就会互相匹配。成功的话,就会调用到master里面的attach函数。
下面来看看开机信息:
首先是注册两个ipu,这两个ipu的地址分别是:0x02400000和0x02800000。
这是第一个ipu的probe信息:
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.
这是第二个ipu的probe信息:
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_0和v4l2_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_id为0,csi_id为1;而ov5640设备的csi_id为0,所以它们俩并不匹配。所以最终会打印出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_id为0,csi_id为0;ov5640设备的csi_id为0,所以它们俩匹配。打印出下面的信息,表明匹配成功。
从这里可以看出来,对于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) {