需求:项目需求需要同一个固件兼容两款不同IC的mipi屏。以便有一个IC停产之后使用不受影响。由于是同一个模组厂商,所以设计初就要求硬件接口一样或者兼容。
方法:一开始的想法很简单和网上以及同行提供的方法一样在uboot阶段去读屏的ID,然后再把对应屏ID放到cmdline里面去传到kernel.在kernel里面做判断屏ID,来使用不同的panel-init-sequence 和timing。这种方法也可行。把屏的ID放到cmdline的方法是我也没找到,有大佬解答一下吗?
然后询问RK,RK提供了另一种方案不依靠cmdline传参。dts中dsi节点下面添加不同的panel节点。开机uboot阶段,通过mipi dsi读取每个屏的id寄存器与dts中设置的目标寄存器值进行比较。如果不是完全一致,就会将对应的panel节点的status设置成“disabled”。
最后会留下来唯一一个是status是“okay”的panel。kernel在加载panel的时候找到这个status是okay的panel,其他的不会加载。okay我们按照这个做法来实现我们的需求。
首先对DTB做一个深入了解。要不然怎么理解在uboot阶段把dts里面的status掉呢。在系统启动过程中,由bootloader将dtb文件加载到内存,并告知linux内核dtb在内存中的首地址(中途可能会对某些节点进行创建或者修改)。内核在start_kernel中会对dtb文件进行解析,并形成对应的device_node和property结构体;至此,dtb文件便完成任务了大佬做的详细解答可点
dtb的组成
其中structure block区域描述了设备树本身的结构和内容,strings block包含了设备树中所有属性的名称。以上都是参照链接上知乎大佬arch的文章内容。那么我理解就是dtb本身就是一个二进制文件。里面包含了我们的dts的文件内容包括节点以及属性。既然都以二进制存在。而且dtb的起始地址已知。我们只要找到节点的偏移地址以及status属性就可以把okay -> disabled.欧克解决; 这个下面就开始分析代码。
首先我们要咨询屏的IC厂屏的ID在那个寄存器里面。然后编写dts。如下:
&dsi0 {
status = "okay";
//天钰IC 8553
panel@0 {
status = "okay";
compatible = "simple-panel-dsi";
reg = <0>;
backlight = <&backlight>;
iovcc-gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_HIGH>;
//avdd-gpios = <&gpio4 RK_PC7 GPIO_ACTIVE_HIGH>;
avdd-gpios = <&gpio3 RK_PC3 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio3 RK_PC1 GPIO_ACTIVE_LOW>;
init-delay-ms = <60>;
reset-delay-ms = <60>;
enable-delay-ms = <60>;
prepare-delay-ms = <60>;
unprepare-delay-ms = <60>;
disable-delay-ms = <60>;
dsi,flags = <(MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET)>;
dsi,format = <MIPI_DSI_FMT_RGB888>;
dsi,lanes = <4>;
num = <0>; //panel的唯一标识,必须是大于等于0的整数
id = [95]; //读取id寄存器的返回值数组,十六进制
id-reg = <0x04>; //id寄存器地址,1个字节,需要加“0x”表示十六进制,
panel-read-id-sequence= [05 78 01 11
-------------
];
panel-exit-sequence = [
05 0A 01 28
05 78 01 10
];
disp_timings0: display-timings {
native-mode = <&dsi0_timing0>;
dsi0_timing0: timing0 {
clock-frequency = <160000000>;
hactive = <1080>;
vactive = <2340>;
hfront-porch = <60>;
hsync-len = <2>;
hback-porch = <60>;
vfront-porch = <20>;
vsync-len = <2>;
vback-porch = <8>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <0>;
pixelclk-active = <1>;
};
};
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
panel0_in_dsi0: endpoint {
remote-endpoint = <&dsi0_out_panel0>;
};
};
};
};
//奇景 83112a
panel@1 {
status = "okay";
compatible = "simple-panel-dsi";
reg = <0>;
backlight = <&backlight>;
iovcc-gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_HIGH>;
//avdd-gpios = <&gpio4 RK_PC7 GPIO_ACTIVE_HIGH>;
avdd-gpios = <&gpio3 RK_PC3 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio3 RK_PC1 GPIO_ACTIVE_LOW>;
init-delay-ms = <60>;
reset-delay-ms = <60>;
enable-delay-ms = <60>;
prepare-delay-ms = <60>;
unprepare-delay-ms = <60>;
disable-delay-ms = <60>;
dsi,flags = <(MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET)>;
dsi,format = <MIPI_DSI_FMT_RGB888>;
dsi,lanes = <4>;
num = <1>; //panel的唯一标识,必须是大于等于0的整数
id = [83]; //读取id寄存器的返回值数组,十六进制
id-reg = <0xB9>; //id寄存器地址,1个字节,需要加“0x”表示十六进制,
panel-read-id-sequence= [05 78 01 11
05 32 01 29 ]; //如果屏读寄存器前需要发命令,添写在这个数组里面,否则就不要配置这个数组
panel-init-sequence = [
--------------
15 78 02 11 00
15 0A 02 29 00
];
panel-exit-sequence = [
05 0A 01 28
05 78 01 10
];
display-timings {
native-mode = <&dsi0_timing1>;
dsi0_timing1: timing1 {
clock-frequency = <155000000>;//mast Keep FPS 53
hactive = <1080>;
vactive = <2340>;
hfront-porch = <48>;
hsync-len = <44>;
hback-porch = <48>;
vfront-porch = <42>;
vsync-len = <5>;
vback-porch = <7>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <0>;
pixelclk-active = <1>;
};
};
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
panel1_in_dsi0: endpoint {
remote-endpoint = <&dsi0_out_panel1>;
};
};
};
};
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
dsi0_out_panel0: endpoint {
remote-endpoint = <&panel0_in_dsi0>;
};
};
port@1 {
reg = <1>;
dsi0_out_panel1: endpoint {
remote-endpoint = <&panel1_in_dsi0>;
};
};
};
};
这里省略初始话代码。主要添加了:
reg = <0>;//dsi的vic number,必须是0
num = <0>;//panel的唯一标识,必须是大于等于0的整数
id = [04 05]; //读取id寄存器的返回值数组,十六进制
id-reg = <0x0a>;//id寄存器地址,1个字节,需要加“0x”表示十六进制,
panel-read-id-sequence= [39 00 04 FF 98 81 01 ];//如果屏读寄存器前需要发命令,添写在这个数组里面,否则就不要配置这个数组。
然后我们在uboot里面获取id 和id_reg。然后是最主要的read屏的寄存器读出ID来和id比较。然后再看是okay改节点还是disabled掉改节点
1.从dts上找到panle节点。根据上面的dts我们有俩个panel节点分别是panel@0 和panel@1 并利用device_node *np记录节点
+++ b/video/drm/dw_mipi_dsi.c
static int dw_mipi_dsi_connector_fixup_dts(struct display_state *state, void *blob)
{
struct connector_state *conn_state = &state->conn_state;
struct panel_state *panel_state = &state->panel_state;
int num = panel_state->panel->num;
const struct device_node *np;
const char *path;
int panel_node,connector;
np = ofnode_to_np(conn_state->node);
path = np->full_name;
connector = fdt_path_offset(blob, path);
for (panel_node = fdt_subnode_offset(blob, connector, "panel");
panel_node >= 0;
panel_node = fdt_next_subnode(blob, panel_node)){
dbg("fixup panel(num=%d) target =%d\n",fdtdec_get_int(blob, panel_node, "num", 0),num);
if(fdtdec_get_int(blob, panel_node, "num", 0) != num){
fdt_status_disabled(blob, panel_node);
}else{
fdt_status_okay(blob, panel_node);
}
}
return 0;
}
利用 fdt_subnode_offset 函数找到名称为panel的节点偏移。
2.找到节点中的id以及id_reg等属性的值并给到一个定义好的结构体内填充
static int rockchip_panel_ofdata_to_platdata(struct udevice *dev)
{
struct rockchip_panel_plat *plat = dev_get_platdata(dev);
const void *data;
int len = 0;
int ret;
plat->power_invert = dev_read_bool(dev, "power-invert");
plat->delay.prepare = dev_read_u32_default(dev, "prepare-delay-ms", 0);
plat->delay.unprepare = dev_read_u32_default(dev, "unprepare-delay-ms", 0);
plat->delay.enable = dev_read_u32_default(dev, "enable-delay-ms", 0);
plat->delay.disable = dev_read_u32_default(dev, "disable-delay-ms", 0);
plat->delay.init = dev_read_u32_default(dev, "init-delay-ms", 0);
plat->delay.reset = dev_read_u32_default(dev, "reset-delay-ms", 0);
plat->bus_format = dev_read_u32_default(dev, "bus-format",
MEDIA_BUS_FMT_RBG888_1X24);
plat->bpc = dev_read_u32_default(dev, "bpc", 8);
plat->id_reg = dev_read_u32_default(dev, "id-reg", 0);
plat->num = dev_read_u32_default(dev, "num", 0);
data = dev_read_prop(dev, "id", &len);
dbg("wzf:-------%s------\n",__func__);
if(data){
plat->id = malloc(sizeof(*plat->id));
if (!plat->id)
return -ENOMEM;
plat->id->buf = malloc(sizeof(char) * len);
memcpy(plat->id->buf, data, len);
plat->id->len = len;
dbg("wzf:----plat dtb reg = %x id = %s len = %d",plat->id_reg,plat->id->buf,len);
}
dbg("mingming-->dts id reg = 0x%x\n",plat->id_reg);
for(len =0;len < plat->id->len;len++){
dbg("dts id[%d] = %x\n", len, *(plat->id->buf + len) );
}
data = dev_read_prop(dev, "panel-init-sequence", &len);
if (data) {
plat->on_cmds = calloc(1, sizeof(*plat->on_cmds));
if (!plat->on_cmds)
return -ENOMEM;
ret = rockchip_panel_parse_cmds(data, len, plat->on_cmds);
if (ret) {
dbg("failed to parse panel init sequence\n");
goto free_on_cmds;
}
}
data = dev_read_prop(dev, "panel-read-id-sequence", &len);
if (data) {
plat->read_id_cmds = calloc(1, sizeof(*plat->read_id_cmds));
if (!plat->read_id_cmds)
return -ENOMEM;
ret = rockchip_panel_parse_cmds(data, len, plat->read_id_cmds);
if (ret) {
dbg("failed to parse panel read id sequence\n");
goto free_read_id_cmds;
}
}else{
plat->read_id_cmds = NULL;
}
data = dev_read_prop(dev, "panel-exit-sequence", &len);
if (data) {
plat->off_cmds = calloc(1, sizeof(*plat->off_cmds));
if (!plat->off_cmds) {
ret = -ENOMEM;
goto free_on_cmds;
}
ret = rockchip_panel_parse_cmds(data, len, plat->off_cmds);
if (ret) {
dbg("failed to parse panel exit sequence\n");
goto free_cmds;
}
}
return 0;
利用 dev_read_u32_default 函数获取dts中我们填充的对比的id给到buf以及我们读的寄存器地址,以及地址长度 plat->id_reg,plat->id->buf,len(这里原本俩块屏的id是 JD9525 以及 HI83211a但是 我们比较一个就可以比较出来我就只填了 95 83)
JD9525 : plat->id-> buf = 95 plat->id_reg = 0x04 len =1;
HI83211a: plat->id-> buf = 83 plat->id_reg = 0x89 len =1;
3.拿到DTS中要比对的数据那么我们就要从寄存器中读取数据
static int rockchip_dsi_panel_getId(struct rockchip_panel *panel)
{
u8 *buf;
int i,len,ret = -1;
int count = 3;
struct rockchip_panel_plat *plat = dev_get_platdata(panel->dev);
struct rockchip_panel_priv *priv = dev_get_priv(panel->dev);
struct mipi_dsi_device *dsi = dev_get_parent_platdata(panel->dev);
dbg("%s(%d) start\n", __func__, __LINE__);
len = plat->id->len;
printf("mingming-->%s(%d) start len:%d\n", __func__, __LINE__, len);
buf = malloc(sizeof(char) * len);
if (priv->power_supply)
regulator_set_enable(priv->power_supply, !plat->power_invert);
//这段要根据自己的上电时序修改
if (dm_gpio_is_valid(&priv->enable_gpio))
dm_gpio_set_value(&priv->enable_gpio, 1);
if (plat->delay.prepare)
mdelay(plat->delay.prepare);
if (dm_gpio_is_valid(&priv->reset_gpio))
dm_gpio_set_value(&priv->reset_gpio, 1);
if (plat->delay.reset)
mdelay(plat->delay.reset);
if (dm_gpio_is_valid(&priv->reset_gpio))
dm_gpio_set_value(&priv->reset_gpio, 0);
if (plat->delay.init)
mdelay(plat->delay.init);
priv->prepared = true;
if (plat->read_id_cmds) {
printf("send read id cmds\n");
rockchip_panel_send_dsi_cmds(dsi, plat->read_id_cmds);
free(plat->read_id_cmds);
plat->read_id_cmds = NULL;
}else{
if (plat->on_cmds) {
printf("send on cmds\n");
ret = rockchip_panel_send_dsi_cmds(dsi, plat->on_cmds);
if (ret)
printf("failed to send on cmds: %d\n", ret); }
}
ret = -1;
while((ret < 0) && count > 0){
if(len > 1){
ret = mipi_dsi_set_maximum_return_packet_size(dsi, len); //设置要读取的值的长度由于我们dts上只有一个字节 len 为一所以他只 会读一个字节出来
}
ret = mipi_dsi_dcs_read(dsi, plat->id_reg, buf,len); //getId核心代码
count --;
}
// printf("panel_simple_prepare buf = %d \n",buf[0]);
if(ret == len)
for(i = 0; i < len; i++){
printf("read buf id[%d] = %x,panel target id[%d] = %x\n", i, *(buf + i), i, *(plat->id->buf + i));
if(*(buf + i) != *(plat->id->buf + i)){ //和第二步中的plat->id->buf比对
plat->num = -1;
return -1;
}
}
else{
dbg("read panel(num = %d) timeout or no response\n",plat->num);
plat->num = -1;
return -1;
}
panel->num = plat->num;
return 0;
}
这个利用函数mipi_dsi_dcs_read(dsi, plat->id_reg, buf,len)plat->id_reg从寄存器中读取值给到buf。
那么只要buf拿到值就可以和第二步中的plat->id->buf比对if(*(buf + i) != *(plat->id->buf + i))由于我们只有一个字节 i = 0;如果多个字节i自增就可以对比多个字节。 如果匹配getId就会返回0 否则就返回-1
4getId通过判断dts的id和寄存器的ID匹对来判断屏的dts和屏是否匹配。然后就要okay 该panel节点 或者disabled该节点。
static int panel_matching(struct display_state *state)
{
struct connector_state *conn_state = &state->conn_state;
struct panel_state *panel_state = &state->panel_state;
const struct rockchip_connector *conn = conn_state->connector;
const struct rockchip_connector_funcs *conn_funcs = conn->funcs;
ofnode panel_node;
struct udevice *panel_dev;
int ret;
if (conn_funcs->init) {
ret = conn_funcs->init(state);
if (ret){
printf("failed to init connector\n");
goto deinit;
}
}
if (conn_state->phy)
rockchip_phy_init(conn_state->phy);
ofnode_for_each_subnode(panel_node, conn_state->node) {
if (ofnode_valid(panel_node) && ofnode_is_available(panel_node)) {
ret = uclass_get_device_by_ofnode(UCLASS_PANEL, panel_node,
&panel_dev);
if (ret){
dbg("this sub node is not a panel node\n");
continue;
}
}
struct rockchip_panel *panel = (struct rockchip_panel *)dev_get_driver_data(panel_dev);
if (panel){
panel->state = state;
panel_state->panel = panel;
}
if (!conn_funcs) {
printf("failed to find connector functions\n");
return -ENXIO;
}
if (panel_state->panel)
rockchip_panel_init(panel_state->panel);
if (conn_funcs->prepare)
conn_funcs->prepare(state);
printf("%s(%d) getid\n", __func__, __LINE__);
if(rockchip_panel_getId(panel_state->panel) != -1){ //调用getId函数 如果)=-1 就是屏和dts匹配直接break;如果不等于执行下面的rockchip_panel_disable函数 把该节点disabled掉
dbg("dsi find panel id\n");
rockchip_panel_unprepare(panel_state->panel);
break;
}
rockchip_panel_unprepare(panel_state->panel);
rockchip_panel_disable(panel_state->panel);
}
if (conn_funcs->disable)
conn_funcs->disable(state);
if (conn_funcs->unprepare)
conn_funcs->unprepare(state);
return 0;
deinit:
if (conn_funcs->deinit)
conn_funcs->deinit(state);
return ret;
}
通过 ofnode_for_each_subnode遍历dsi下所有子节点。然后调用getId函数 如 != - 1 就是屏和dts匹配直接break;如果不等于执行下面的rockchip_panel_disable函数 把该节点disabled掉。因此我们的所有panel节点都应设置为okay。然后等待disabeld掉不适用的节点。
补充说明:
把节点disabled掉的并不是rockchip_panel_disable函数。而是fdt_setprope等fdt函数。
然后我们把板子里面的dtb文件导出来查看是否正常被disabled掉status参考链接
把板子里面的/sys/firmware/fdt文件导出来。
再放到我们的编译服务器使用dtc命令 把fdt转化成dts文件
dtc -I dtb -O dts fdt
导出来的dts文件:
dsi@fe060000 {
compatible = "rockchip,rk3568-mipi-dsi";
reg = <0x00 0xfe060000 0x00 0x10000>;
interrupts = <0x00 0x44 0x04>;
clocks = <0x1f 0xe8 0x1f 0xda 0x2e>;
clock-names = "pclk\0hclk\0hs_clk";
resets = <0x1f 0x110>;
reset-names = "apb";
phys = <0x2e>;
phy-names = "mipi_dphy";
power-domains = <0x21 0x09>;
rockchip,grf = <0x32>;
#address-cells = <0x01>;
#size-cells = <0x00>;
status = "okay";
phandle = <0x190>;
ports {
#address-cells = <0x01>;
#size-cells = <0x00>;
port@0 {
reg = <0x00>;
#address-cells = <0x01>;
#size-cells = <0x00>;
phandle = <0x191>;
endpoint@0 {
reg = <0x00>;
remote-endpoint = <0x14>;
status = "okay";
phandle = <0x89>;
};
endpoint@1 {
reg = <0x01>;
remote-endpoint = <0x94>;
status = "disabled";
phandle = <0x8d>;
};
endpoint {
remote-endpoint = <0x95>;
phandle = <0x9a>;
};
};
port@1 {
reg = <0x01>;
endpoint {
remote-endpoint = <0x96>;
phandle = <0x9c>;
};
};
};
panel@0 {
status = "okay";
compatible = "simple-panel-dsi";
reg = <0x00>;
backlight = <0x97>;
iovcc-gpios = <0x36 0x17 0x00>;
avdd-gpios = <0x98 0x13 0x00>;
reset-gpios = <0x98 0x11 0x01>;
init-delay-ms = <0x3c>;
reset-delay-ms = <0x3c>;
enable-delay-ms = <0x3c>;
prepare-delay-ms = <0x3c>;
unprepare-delay-ms = <0x3c>;
disable-delay-ms = <0x3c>;
dsi,flags = <0xa03>;
dsi,format = <0x00>;
dsi,lanes = <0x04>;
num = <0x00>;
id = [95];
id-reg = <0x04>;
panel-read-id-sequence = <0x5780111 0x5320129>;
panel-init-sequence = [39 00 04 df 95 25 ba 15 00 02 de 00 39 00 04 b9 7c 43 85 39 00 08 bb 00 11 43 1f 1f 11 11 39 00 03 bd 00 22 39 00 04 bc 2e 30 73 39 00 05 bf 2c 2c 3f c3 39 00 06 c0 00 f7 01 f7 01 39 00 04 c1 04 11 40 39 01 41 c3 3b 01 06 05 03 03 03 01 01 01 01 01 01 01 01 01 06 2b 30 54 58 7d 01 80 ff ff 00 05 03 7f 00 04 00 04 00 04 02 13 ff ff 02 13 03 00 03 02 03 03 00 03 ff ff 00 07 ff ff 03 0d 03 0d 03 0d 03 0d 39 01 40 c4 01 06 05 03 03 03 01 01 01 01 01 01 01 01 01 06 2b 30 54 58 7d 01 80 03 0d 00 05 03 0d 00 04 00 04 00 04 02 13 02 13 02 13 03 03 03 03 03 03 00 03 ff ff 00 07 03 0d 03 0d 03 0d 03 0d 03 0d 39 01 18 c6 01 49 00 36 00 0a 05 82 01 00 00 01 00 01 00 00 00 00 00 35 00 00 01 15 00 02 c7 40 39 00 08 c8 45 92 87 00 00 00 00 39 00 17 cd 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 3f 3f 39 00 1d ce 00 00 00 00 00 00 00 00 00 00 00 0c 0c 0c 0c 0c 0c fc fc ff fc fc 0c 0c 0c 00 00 00 39 01 35 cf 00 00 00 00 00 00 00 00 00 33 00 3f 33 33 3f 3f 3f 33 33 33 33 33 33 33 33 00 00 00 00 00 00 00 00 00 00 ff 00 00 00 00 00 00 00 00 00 00 00 ff 00 00 00 00 39 01 20 d0 00 1f 1f 1f 1f 1f 1f 1f 1f 1e 1f df df df f6 f5 f4 c5 c7 d5 de de de df c1 ff ff ff f0 00 00 39 01 20 d1 00 1f 1f 1f 1f 1f 1f 1f 1f 1e 1f df df df f6 f5 f4 c4 c6 d5 de de de df c0 ff ff ff f0 00 00 39 01 1d d2 00 1f 1f 1f 1f 1f 1f 1f 1f 1e 1f 1f 1f 1f 36 35 34 c6 c4 d5 de de 1f 1e 00 00 00 00 39 01 1d d3 00 1f 1f 1f 1f 1f 1f 1f 1f 1e 1f 1f 1f 1f 36 35 34 c7 c5 d5 de de 1f 1e 01 00 00 00 39 01 3e d4 10 10 00 08 00 05 00 00 00 00 00 01 02 00 11 e0 01 00 04 01 01 11 e0 03 00 05 01 01 00 00 02 04 0a 09 38 00 0f 00 00 00 00 00 00 00 00 03 00 26 30 00 00 00 00 00 00 00 00 01 00 00 00 39 01 5d d5 01 10 01 00 00 00 00 00 00 00 00 e8 00 01 00 07 32 5a 10 40 05 00 01 00 30 30 00 01 14 00 71 00 04 10 04 06 00 00 00 00 00 09 00 00 00 1b 00 00 01 00 01 00 01 00 01 00 06 00 2b 00 30 00 54 00 59 00 7d 00 00 00 ff ff ff 3f 3f 00 00 1f ff 00 00 00 1f ff 00 ff ff ff ff ff ff 00 39 01 14 d7 00 02 84 00 00 00 00 00 00 00 00 00 00 02 84 00 00 00 00 15 00 02 de 01 15 00 02 b5 69 39 01 30 c2 00 00 00 01 55 55 6a aa ff f0 00 00 0e 21 33 42 52 5c 68 7d 91 a0 ac e1 05 22 3e 57 70 70 8e ab cd f6 25 65 91 ab cb dd f4 0e 28 49 78 bb f8 39 01 30 c1 00 00 00 01 55 55 6a aa ff f0 04 05 16 29 3b 4a 5a 64 70 85 99 a8 b4 e9 0d 2a 46 5f 78 78 96 b3 d5 fe 2d 6d 99 b3 d3 e5 fc 16 30 51 80 c3 ff 39 01 30 c3 00 00 00 01 55 55 6a aa ff f0 00 00 0e 21 33 42 52 5c 68 7d 91 a0 ac e1 05 22 3e 57 70 70 8e ab cd f6 25 65 91 ab cb dd f4 0e 28 49 78 bb f8 39 01 30 c4 00 00 00 01 55 55 6a aa ff f0 04 05 16 29 3b 4a 5a 64 70 85 99 a8 b4 e9 0d 2a 46 5f 78 78 96 b3 d5 fe 2d 6d 99 b3 d3 e5 fc 16 30 51 80 c3 ff 39 01 30 c6 00 00 00 01 55 55 6a aa ff f0 00 00 0e 21 33 42 52 5c 68 7d 91 a0 ac e1 05 22 3e 57 70 70 8e ab cd f6 25 65 91 ab cb dd f4 0e 28 49 78 bb f8 39 01 30 c5 00 00 00 01 55 55 6a aa ff f0 04 05 16 29 3b 4a 5a 64 70 85 99 a8 b4 e9 0d 2a 46 5f 78 78 96 b3 d5 fe 2d 6d 99 b3 d3 e5 fc 16 30 51 80 c3 ff 39 01 0c c7 15 14 07 07 09 07 87 63 62 63 04 15 00 02 de 02 15 00 02 bf 04 15 00 02 c1 20 39 00 0a c2 02 42 d0 02 00 c0 41 72 fb 15 00 02 c5 0e 39 00 08 c4 00 11 0f 80 1c 01 08 39 00 17 e5 10 da d8 02 2b c0 2b c0 09 04 40 40 00 20 00 00 00 00 28 00 00 00 39 00 05 ec 77 00 5f 02 15 00 02 de 03 39 00 03 d0 00 65 15 00 02 de 00 05 1e 01 35 05 78 01 11 05 32 01 29];
panel-exit-sequence = <0x50a0128 0x5780110>;
display-timings {
native-mode = <0x99>;
phandle = <0x192>;
timing0 {
clock-frequency = <0x9896800>;
hactive = <0x438>;
vactive = <0x924>;
hfront-porch = <0x3c>;
hsync-len = <0x02>;
hback-porch = <0x3c>;
vfront-porch = <0x14>;
vsync-len = <0x02>;
vback-porch = <0x08>;
hsync-active = <0x00>;
vsync-active = <0x00>;
de-active = <0x00>;
pixelclk-active = <0x01>;
phandle = <0x99>;
};
};
ports {
#address-cells = <0x01>;
#size-cells = <0x00>;
port@0 {
reg = <0x00>;
endpoint {
remote-endpoint = <0x9a>;
phandle = <0x95>;
};
};
};
};
panel@1 {
status = "disabled";
compatible = "simple-panel-dsi";
reg = <0x00>;
backlight = <0x97>;
iovcc-gpios = <0x36 0x17 0x00>;
avdd-gpios = <0x98 0x13 0x00>;
reset-gpios = <0x98 0x11 0x01>;
init-delay-ms = <0x3c>;
reset-delay-ms = <0x3c>;
enable-delay-ms = <0x3c>;
prepare-delay-ms = <0x3c>;
unprepare-delay-ms = <0x3c>;
disable-delay-ms = <0x3c>;
dsi,flags = <0xa03>;
dsi,format = <0x00>;
dsi,lanes = <0x04>;
num = <0x01>;
id = [83];
id-reg = <0xb9>;
panel-read-id-sequence = <0x5780111 0x5320129>;
panel-init-sequence = [39 00 04 b9 83 11 2a 39 00 09 b1 08 25 25 80 80 4a 4f aa 39 00 0f b2 00 02 00 90 24 00 0a 28 ea 11 00 01 15 a0 39 00 03 d2 2b 2b 39 00 1c b4 02 ba 02 ba 02 ba 04 bc 00 00 04 bc 00 ff 00 ff 00 00 0c 12 00 2a 06 06 0f 00 86 39 00 04 b6 8c 8c e3 15 00 02 cc 08 39 05 30 d3 40 00 00 00 00 01 00 14 14 03 01 11 08 03 03 03 03 32 10 00 00 00 32 10 0a 00 0a 32 10 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ea 1c 39 05 31 d5 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 c0 c0 18 18 40 40 18 18 18 18 31 31 30 30 2f 2f 01 00 03 02 20 20 c0 c0 c0 c0 19 19 40 40 25 24 39 05 31 d6 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 40 40 18 18 40 40 18 18 18 18 31 31 30 30 2f 2f 00 01 02 03 20 20 40 40 40 40 40 40 19 19 24 25 39 00 0d d8 aa aa aa aa ff ea aa aa aa aa ff ea 15 00 02 bd 01 39 00 0d d8 aa aa af ff ff ff aa aa af ff ff ff 15 00 02 bd 02 39 00 0d d8 aa aa af ff ff ff aa aa af ff ff ff 15 00 02 bd 03 39 00 19 d8 aa aa ae bf ab ea aa aa ae bf ab ea aa aa af ff ff ff aa aa af ff ff ff 15 00 02 bd 00 15 00 02 c1 01 15 00 02 bd 01 39 00 3a c1 ff fa f6 f2 ee ea e6 dd d9 d5 d1 cd c8 c4 c0 bc b7 b3 ae a6 9d 95 8c 84 7c 74 6c 64 5c 55 4c 46 3f 37 2f 28 20 19 12 0a 07 05 03 01 00 28 03 e9 95 8c 8e f9 c8 18 e7 12 00 15 00 02 bd 02 39 00 3a c1 ff fa f6 f2 ee ea e6 dd d9 d5 d1 cd c8 c4 c0 bc b7 b3 ae a6 9d 95 8c 84 7c 74 6c 64 5c 55 4c 46 3f 37 2f 28 20 19 12 0a 07 05 03 01 00 28 03 e9 95 8c 8e f9 c8 18 e7 12 00 15 00 02 bd 03 39 00 3a c1 ff fa f6 f2 ee ea e6 dd d9 d5 d1 cd c8 c4 c0 bc b7 b3 ae a6 9d 95 8c 84 7c 74 6c 64 5c 55 4c 46 3f 37 2f 28 20 19 12 0a 07 05 03 01 00 28 03 e9 95 8c 8e f9 c8 18 e7 12 00 15 00 02 bd 00 39 00 1a e7 0f 0f 1e 68 1e 65 00 50 01 14 00 00 02 02 02 05 14 14 32 b9 23 b9 08 13 69 15 00 02 bd 01 39 00 09 e7 02 00 ba 01 a4 0e 20 0f 15 00 02 bd 02 39 00 1e e7 00 00 08 40 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 70 01 70 02 00 15 00 02 bd 00 39 00 03 c0 22 22 15 00 02 bd 02 39 00 0a b4 00 92 12 11 88 12 12 00 53 15 00 02 bd 01 39 00 04 b1 64 01 09 15 00 02 bd 00 39 00 07 c7 00 00 04 e0 33 00 15 00 02 e9 c3 39 00 05 cb 91 d2 00 02 15 00 02 e9 3f 15 78 02 11 00 15 0a 02 29 00];
panel-exit-sequence = <0x50a0128 0x5780110>;
display-timings {
native-mode = <0x9b>;
timing1 {
clock-frequency = <0x93d1cc0>;
hactive = <0x438>;
vactive = <0x924>;
hfront-porch = <0x30>;
hsync-len = <0x2c>;
hback-porch = <0x30>;
vfront-porch = <0x2a>;
vsync-len = <0x05>;
vback-porch = <0x07>;
hsync-active = <0x00>;
vsync-active = <0x00>;
de-active = <0x00>;
pixelclk-active = <0x01>;
phandle = <0x9b>;
};
};
ports {
#address-cells = <0x01>;
#size-cells = <0x00>;
port@0 {
reg = <0x00>;
endpoint {
remote-endpoint = <0x9c>;
phandle = <0x96>;
};
};
};
};
};
可以看到panel@1已经被disabled掉了 。原本我们的dts都写的是okay。表示我们功能已经实现。
ok流程代码分析结束。我们再追一下源码看rockchip_panel_disable是怎么做的
static void panel_simple_enable(struct rockchip_panel *panel)
{
struct rockchip_panel_plat *plat = dev_get_platdata(panel->dev);
struct rockchip_panel_priv *priv = dev_get_priv(panel->dev);
if (priv->enabled)
return;
if (plat->delay.enable)
mdelay(plat->delay.enable);
if (priv->backlight)
backlight_enable(priv->backlight);
priv->enabled = true;
}
static void panel_simple_disable(struct rockchip_panel *panel)
{
struct rockchip_panel_plat *plat = dev_get_platdata(panel->dev);
struct rockchip_panel_priv *priv = dev_get_priv(panel->dev);
if (!priv->enabled)
return;
if (priv->backlight)
backlight_disable(priv->backlight);
if (plat->delay.disable)
mdelay(plat->delay.disable);
priv->enabled = false;
}
看不懂。好了我们在来看一下mipi_dsi的函数
ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
size_t len)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.type = MIPI_DSI_DCS_READ,
.tx_buf = &cmd,
.tx_len = 1,
.rx_buf = data,
.rx_len = len
};
return mipi_dsi_device_transfer(dsi, &msg);
}
好了它就是填充了一个消息结构体,然后跟i2c一样transfer。读到数据。具体mipi帧格式我也不知道。
uboot patch
diff --git a/video/drm/dw_mipi_dsi.c b/video/drm/dw_mipi_dsi.c
old mode 100644
new mode 100755
index bf43669..7cd2f29
--- a/video/drm/dw_mipi_dsi.c
+++ b/video/drm/dw_mipi_dsi.c
@@ -18,6 +18,7 @@
#include <syscon.h>
#include <asm/arch-rockchip/clock.h>
#include <linux/iopoll.h>
+#include <fdt_support.h>
#include "rockchip_display.h"
#include "rockchip_crtc.h"
@@ -655,7 +656,7 @@ static int dw_mipi_dsi_read_from_fifo(struct dw_mipi_dsi *dsi,
int ret;
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
- val, !(val & GEN_RD_CMD_BUSY), 5000);
+ val, !(val & GEN_RD_CMD_BUSY), 50000);
if (ret) {
printf("entire response isn't stored in the FIFO\n");
return ret;
@@ -718,7 +719,6 @@ static ssize_t dw_mipi_dsi_transfer(struct dw_mipi_dsi *dsi,
struct mipi_dsi_packet packet;
int ret;
int val;
-
if (msg->flags & MIPI_DSI_MSG_USE_LPM) {
dsi_update_bits(dsi, DSI_VID_MODE_CFG, LP_CMD_EN, LP_CMD_EN);
dsi_update_bits(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS, 0);
@@ -1237,6 +1237,7 @@ static int dw_mipi_dsi_connector_prepare(struct display_state *state)
}
lane_rate = dw_mipi_dsi_get_lane_rate(dsi);
+ if(lane_rate == 0) lane_rate = 1000000000UL;
if (dsi->dphy.phy)
dw_mipi_dsi_set_hs_clk(dsi, lane_rate);
else
@@ -1279,12 +1280,43 @@ static int dw_mipi_dsi_connector_disable(struct display_state *state)
return 0;
}
+static int dw_mipi_dsi_connector_fixup_dts(struct display_state *state, void *blob)
+{
+ struct connector_state *conn_state = &state->conn_state;
+ struct panel_state *panel_state = &state->panel_state;
+ int num = panel_state->panel->num;
+ const struct device_node *np;
+ const char *path;
+ int panel_node,connector;
+
+ np = ofnode_to_np(conn_state->node);
+ path = np->full_name;
+ connector = fdt_path_offset(blob, path);
+
+ for (panel_node = fdt_subnode_offset(blob, connector, "panel");
+ panel_node >= 0;
+ panel_node = fdt_next_subnode(blob, panel_node)){
+
+ dbg("fixup panel(num=%d) target =%d\n",fdtdec_get_int(blob, panel_node, "num", 0),num);
+
+ if(fdtdec_get_int(blob, panel_node, "num", 0) != num){
+
+ fdt_status_disabled(blob, panel_node);
+ }else{
+ fdt_status_okay(blob, panel_node);
+ }
+ }
+
+ return 0;
+}
+
static const struct rockchip_connector_funcs dw_mipi_dsi_connector_funcs = {
.init = dw_mipi_dsi_connector_init,
.prepare = dw_mipi_dsi_connector_prepare,
.unprepare = dw_mipi_dsi_connector_unprepare,
.enable = dw_mipi_dsi_connector_enable,
.disable = dw_mipi_dsi_connector_disable,
+ .fixup_dts = dw_mipi_dsi_connector_fixup_dts,
};
static int dw_mipi_dsi_probe(struct udevice *dev)
diff --git a/video/drm/rockchip_display.c b/video/drm/rockchip_display.c
old mode 100644
new mode 100755
index 139a338..2636fde
--- a/video/drm/rockchip_display.c
+++ b/video/drm/rockchip_display.c
@@ -236,6 +236,77 @@ static int connector_panel_init(struct display_state *state)
return 0;
}
+static int panel_matching(struct display_state *state)
+{
+ struct connector_state *conn_state = &state->conn_state;
+ struct panel_state *panel_state = &state->panel_state;
+ const struct rockchip_connector *conn = conn_state->connector;
+ const struct rockchip_connector_funcs *conn_funcs = conn->funcs;
+ ofnode panel_node;
+ struct udevice *panel_dev;
+ int ret;
+
+
+ if (conn_funcs->init) {
+ ret = conn_funcs->init(state);
+ if (ret){
+ printf("failed to init connector\n");
+ goto deinit;
+ }
+ }
+
+ if (conn_state->phy)
+ rockchip_phy_init(conn_state->phy);
+
+ ofnode_for_each_subnode(panel_node, conn_state->node) {
+
+ if (ofnode_valid(panel_node) && ofnode_is_available(panel_node)) {
+ ret = uclass_get_device_by_ofnode(UCLASS_PANEL, panel_node,
+ &panel_dev);
+ if (ret){
+ dbg("this sub node is not a panel node\n");
+ continue;
+ }
+
+ }
+ struct rockchip_panel *panel = (struct rockchip_panel *)dev_get_driver_data(panel_dev);
+ if (panel){
+
+ panel->state = state;
+ panel_state->panel = panel;
+ }
+ if (!conn_funcs) {
+ printf("failed to find connector functions\n");
+ return -ENXIO;
+ }
+
+ if (panel_state->panel)
+ rockchip_panel_init(panel_state->panel);
+
+ if (conn_funcs->prepare)
+ conn_funcs->prepare(state);
+
+ printf("%s(%d) getid\n", __func__, __LINE__);
+ if(rockchip_panel_getId(panel_state->panel) != -1){
+ dbg("dsi find panel id\n");
+ rockchip_panel_unprepare(panel_state->panel);
+ break;
+ }
+ rockchip_panel_unprepare(panel_state->panel);
+ rockchip_panel_disable(panel_state->panel);
+ }
+ if (conn_funcs->disable)
+ conn_funcs->disable(state);
+
+ if (conn_funcs->unprepare)
+ conn_funcs->unprepare(state);
+ return 0;
+ deinit:
+ if (conn_funcs->deinit)
+ conn_funcs->deinit(state);
+ return ret;
+}
+
int drm_mode_vrefresh(const struct drm_display_mode *mode)
{
int refresh = 0;
@@ -544,6 +615,20 @@ static int display_init(struct display_state *state)
return -ENXIO;
}
+ ofnode_for_each_subnode(node, state->conn_state.node) {
+ const struct device_node *np;
+ const char *path;
+ np = ofnode_to_np(node);
+ path = np->full_name;
+ if(strstr(path,"panel"))
+ i++;
+ }
+
+ if (i > 1 && panel_matching(state)) {
+ printf("Warn: Failed to match panel drivers\n");
+ }
+
+
if (crtc_state->crtc->active &&
memcmp(&crtc_state->crtc->active_mode, &conn_state->mode,
sizeof(struct drm_display_mode))) {
diff --git a/video/drm/rockchip_display.h b/video/drm/rockchip_display.h
old mode 100644
new mode 100755
index 5e89157..3cd0075
--- a/video/drm/rockchip_display.h
+++ b/video/drm/rockchip_display.h
@@ -53,6 +53,12 @@ enum rockchip_mcu_cmd {
/* for use special outface */
#define ROCKCHIP_OUT_MODE_AAAA 15
+#if 1
+#define dbg(format, arg...) printf("DSI-READ: " format "\n", ## arg)
+#else
+#define dbg(format, arg...) do {} while(0)
+#endif
+
struct rockchip_mcu_timing {
int mcu_pix_total;
int mcu_cs_pst;
@@ -95,7 +101,7 @@ struct crtc_state {
struct panel_state {
struct rockchip_panel *panel;
-
+ int num;
ofnode dsp_lut_node;
};
diff --git a/video/drm/rockchip_panel.c b/video/drm/rockchip_panel.c
old mode 100644
new mode 100755
index 6e27364..c949139
--- a/video/drm/rockchip_panel.c
+++ b/video/drm/rockchip_panel.c
@@ -41,6 +41,11 @@ struct rockchip_panel_cmds {
int cmd_cnt;
};
+struct dsi_panel_id {
+ u8 *buf;
+ int len;
+};
+
struct rockchip_panel_plat {
bool power_invert;
u32 bus_format;
@@ -57,6 +62,10 @@ struct rockchip_panel_plat {
struct rockchip_panel_cmds *on_cmds;
struct rockchip_panel_cmds *off_cmds;
+ struct rockchip_panel_cmds *read_id_cmds;
+ unsigned int id_reg;
+ struct dsi_panel_id *id;
+ unsigned int num;
};
struct rockchip_panel_priv {
@@ -299,6 +308,7 @@ static void panel_simple_prepare(struct rockchip_panel *panel)
}
priv->prepared = true;
+
}
static void panel_simple_unprepare(struct rockchip_panel *panel)
@@ -361,6 +371,11 @@ static void panel_simple_disable(struct rockchip_panel *panel)
struct rockchip_panel_plat *plat = dev_get_platdata(panel->dev);
struct rockchip_panel_priv *priv = dev_get_priv(panel->dev);
+ if (dm_gpio_is_valid(&priv->reset_gpio))
+ dm_gpio_free(panel->dev, &priv->reset_gpio);
+ if (dm_gpio_is_valid(&priv->enable_gpio))
+ dm_gpio_free(panel->dev, &priv->enable_gpio);
+
if (!priv->enabled)
return;
@@ -381,12 +396,93 @@ static void panel_simple_init(struct rockchip_panel *panel)
conn_state->bus_format = panel->bus_format;
}
+static int rockchip_dsi_panel_getId(struct rockchip_panel *panel)
+{
+ u8 *buf;
+ int i,len,ret = -1;
+ int count = 3;
+
+ struct rockchip_panel_plat *plat = dev_get_platdata(panel->dev);
+ struct rockchip_panel_priv *priv = dev_get_priv(panel->dev);
+ struct mipi_dsi_device *dsi = dev_get_parent_platdata(panel->dev);
+ dbg("%s(%d) start\n", __func__, __LINE__);
+
+ len = plat->id->len;
+ printf("mingming-->%s(%d) start len:%d\n", __func__, __LINE__, len);
+ buf = malloc(sizeof(char) * len);
+
+ if (priv->power_supply)
+ regulator_set_enable(priv->power_supply, !plat->power_invert);
+
+ if (dm_gpio_is_valid(&priv->enable_gpio))
+ dm_gpio_set_value(&priv->enable_gpio, 1);
+
+ if (plat->delay.prepare)
+ mdelay(plat->delay.prepare);
+
+ if (dm_gpio_is_valid(&priv->reset_gpio))
+ dm_gpio_set_value(&priv->reset_gpio, 1);
+
+ if (plat->delay.reset)
+ mdelay(plat->delay.reset);
+
+ if (dm_gpio_is_valid(&priv->reset_gpio))
+ dm_gpio_set_value(&priv->reset_gpio, 0);
+
+ if (plat->delay.init)
+ mdelay(plat->delay.init);
+ priv->prepared = true;
+
+
+ if (plat->read_id_cmds) {
+ printf("send read id cmds\n");
+ rockchip_panel_send_dsi_cmds(dsi, plat->read_id_cmds);
+ free(plat->read_id_cmds);
+ plat->read_id_cmds = NULL;
+ }else{
+ if (plat->on_cmds) {
+ printf("send on cmds\n");
+ ret = rockchip_panel_send_dsi_cmds(dsi, plat->on_cmds);
+ if (ret)
+ printf("failed to send on cmds: %d\n", ret);
+ }
+
+ }
+
+ ret = -1;
+ while((ret < 0) && count > 0){
+ if(len > 1){
+ ret = mipi_dsi_set_maximum_return_packet_size(dsi, len);
+ }
+ ret = mipi_dsi_dcs_read(dsi, plat->id_reg, buf,len);
+ count --;
+ }
+
+ // printf("panel_simple_prepare buf = %d \n",buf[0]);
+ if(ret == len)
+ for(i = 0; i < len; i++){
+ printf("read buf id[%d] = %x,panel target id[%d] = %x\n", i, *(buf + i), i, *(plat->id->buf + i));
+ if(*(buf + i) != *(plat->id->buf + i)){
+ plat->num = -1;
+ return -1;
+ }
+ }
+ else{
+ dbg("read panel(num = %d) timeout or no response\n",plat->num);
+ plat->num = -1;
+ return -1;
+ }
+ panel->num = plat->num;
+ return 0;
+}
+
static const struct rockchip_panel_funcs rockchip_panel_funcs = {
.init = panel_simple_init,
.prepare = panel_simple_prepare,
.unprepare = panel_simple_unprepare,
.enable = panel_simple_enable,
.disable = panel_simple_disable,
+ .getId = rockchip_dsi_panel_getId,
};
static int rockchip_panel_ofdata_to_platdata(struct udevice *dev)
@@ -408,7 +504,23 @@ static int rockchip_panel_ofdata_to_platdata(struct udevice *dev)
plat->bus_format = dev_read_u32_default(dev, "bus-format",
MEDIA_BUS_FMT_RBG888_1X24);
plat->bpc = dev_read_u32_default(dev, "bpc", 8);
+ plat->id_reg = dev_read_u32_default(dev, "id-reg", 0);
+ plat->num = dev_read_u32_default(dev, "num", 0);
+ data = dev_read_prop(dev, "id", &len);
+ if(data){
+ plat->id = malloc(sizeof(*plat->id));
+ if (!plat->id)
+ return -ENOMEM;
+ plat->id->buf = malloc(sizeof(char) * len);
+ memcpy(plat->id->buf, data, len);
+ plat->id->len = len;
+ }
+
+ dbg("mingming-->dts id reg = 0x%x\n",plat->id_reg);
+ for(len =0;len < plat->id->len;len++){
+ dbg("dts id[%d] = %x\n", len, *(plat->id->buf + len) );
+ }
data = dev_read_prop(dev, "panel-init-sequence", &len);
if (data) {
plat->on_cmds = calloc(1, sizeof(*plat->on_cmds));
@@ -421,7 +533,20 @@ static int rockchip_panel_ofdata_to_platdata(struct udevice *dev)
goto free_on_cmds;
}
}
+ data = dev_read_prop(dev, "panel-read-id-sequence", &len);
+ if (data) {
+ plat->read_id_cmds = calloc(1, sizeof(*plat->read_id_cmds));
+ if (!plat->read_id_cmds)
+ return -ENOMEM;
+ ret = rockchip_panel_parse_cmds(data, len, plat->read_id_cmds);
+ if (ret) {
+ printf("failed to parse panel read id sequence\n");
+ goto free_read_id_cmds;
+ }
+ }else{
+ plat->read_id_cmds = NULL;
+ }
data = dev_read_prop(dev, "panel-exit-sequence", &len);
if (data) {
plat->off_cmds = calloc(1, sizeof(*plat->off_cmds));
@@ -443,6 +568,9 @@ free_cmds:
free(plat->off_cmds);
free_on_cmds:
free(plat->on_cmds);
+free_read_id_cmds:
+ free(plat->read_id_cmds);
+
return ret;
}
diff --git a/video/drm/rockchip_panel.h b/video/drm/rockchip_panel.h
old mode 100644
new mode 100755
index 3a7587f..8ab9764
--- a/video/drm/rockchip_panel.h
+++ b/video/drm/rockchip_panel.h
@@ -16,6 +16,7 @@ struct rockchip_panel_funcs {
void (*unprepare)(struct rockchip_panel *panel);
void (*enable)(struct rockchip_panel *panel);
void (*disable)(struct rockchip_panel *panel);
+ int (*getId)(struct rockchip_panel *panel);
};
struct rockchip_panel {
@@ -24,7 +25,7 @@ struct rockchip_panel {
unsigned int bpc;
const struct rockchip_panel_funcs *funcs;
const void *data;
-
+ int num;
struct display_state *state;
};
@@ -37,6 +38,17 @@ static inline void rockchip_panel_init(struct rockchip_panel *panel)
panel->funcs->init(panel);
}
+static inline int rockchip_panel_getId(struct rockchip_panel *panel)
+{
+ if (!panel)
+ return -1;
+
+ if (panel->funcs && panel->funcs->getId)
+ return panel->funcs->getId(panel);
+
+ return -1;
+}
+
static inline void rockchip_panel_prepare(struct rockchip_panel *panel)
{
if (!panel)