This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
i am using our SN65DSI84 for the MIPI to LVDS display, but there are no video output issue after the normal initialization.I can detect the mipi data single,mipi clock single and lvds clock single.but the lvds data only have high level.i dump all the register data and they are the same as which i write . the error register 0xE5 read back is always 0x01 after writing 0xFF or 0x00. Do you know what's the problem in here?
Attached is my initialization code and schematic.
/* * Copyright (c) 2014 MediaTek Inc. * Author: Jitao Shi <jitao.shi@mediatek.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include <linux/delay.h> #include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/of_graph.h> #include "../../../gpio/gpiolib.h" #include "drm_crtc.h" #include "drm_crtc_helper.h" #include "drm_atomic_helper.h" #include "drmP.h" #include "drm_mipi_dsi.h" #define REGFLAG_DELAY 0xAB struct dsi84_bridge { struct drm_connector connector; struct i2c_client *client; struct i2c_client *dptx_i2c; struct i2c_client *mipirx_i2c; struct drm_bridge bridge; struct mipi_dsi_device dsi; struct gpio_desc *gpio_en; u32 resolution_index; bool enabled; }; typedef struct { unsigned char cmd; unsigned char data; } dsi84_setting_table; static dsi84_setting_table dsi84_init_table[] = { {0x09,0x00}, {0x0A,0x05}, {0x0B,0x28}, {0x0D,0x00}, {0x10,0x26}, {0x11,0x00}, {0x12,0x52}, {0x13,0x00}, {0x18,0x6c}, {0x19,0x00}, {0x1A,0x03}, {0x1B,0x00}, {0x20,0x80}, {0x21,0x07}, {0x22,0x00}, {0x23,0x00}, {0x24,0x00}, {0x25,0x00}, {0x26,0x00}, {0x27,0x00}, {0x28,0x20}, {0x29,0x00}, {0x2A,0x00}, {0x2B,0x00}, {0x2C,0x04}, {0x2D,0x00}, {0x2E,0x00}, {0x2F,0x00}, {0x30,0x01}, {0x31,0x00}, {0x32,0x00}, {0x33,0x00}, {0x34,0x58}, {0x35,0x00}, {0x36,0x00}, {0x37,0x00}, {0x38,0x00}, {0x39,0x00}, {0x3A,0x00}, {0x3B,0x00}, {0x3C,0x00}, {0x3D,0x00}, {0x3E,0x00}, {0x0D , 0x01}, {REGFLAG_DELAY,10},//delay 5ms {0x09 , 0x01},//soft reset {0xFF,0x00},//ended flag }; static inline struct dsi84_bridge * bridge_to_dsi84(struct drm_bridge *bridge) { return container_of(bridge, struct dsi84_bridge, bridge); } static int dsi84_write_byte(struct dsi84_bridge *ti_bridge, char addr, char val) { int ret; char buf[2]; printk("[jxq debug] %s,%d\n",__func__,__LINE__); buf[0] = addr; buf[1] = val; ret = i2c_master_send(ti_bridge->client, buf, 2); if (ret <= 0) { DRM_ERROR("Failed to send i2c command, ret=%d\n", ret); return ret; } return 0; } static int sn65dsi84_i2c_read(struct i2c_client *client, char *writebuf, int writelen, char *readbuf, int readlen) { int ret; if (writelen > 0) { struct i2c_msg msgs[] = { { .addr = client->addr, .flags = 0, .len = writelen, .buf = writebuf, }, { .addr = client->addr, .flags = I2C_M_RD, .len = readlen, .buf = readbuf, }, }; ret = i2c_transfer(client->adapter, msgs, 2); if (ret < 0) dev_err(&client->dev, "%s: i2c read error.\n", __func__); } else { struct i2c_msg msgs[] = { { .addr = client->addr, .flags = I2C_M_RD, .len = readlen, .buf = readbuf, }, }; ret = i2c_transfer(client->adapter, msgs, 1); if (ret < 0) dev_err(&client->dev, "%s:i2c read error.\n", __func__); } return ret; } /* static int dsi84_read_byte(struct dsi84_bridge *ti_bridge, char addr, u8 *buf, int len) { int ret; ret = i2c_master_send(ti_bridge->client, &addr, len); if (ret <= 0) { DRM_ERROR("Failed to send i2c command, ret=%d\n", ret); return ret; } ret = i2c_master_recv(ti_bridge->client, buf, len); if (ret <= 0) { DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret); return ret; } return 0; } */ static const struct drm_display_mode default_mode[5] = { { .clock = 137728, .hdisplay = 1920, .hsync_start = 1920 + 92,//加左边界 .hsync_end = 1920 + 92 + 4,//加左边界加水平同步 .htotal = 1920 + 92 + 4 + 88,//加左边界加水平同步加右边界 .vdisplay = 1080, .vsync_start = 1080 + 6, .vsync_end = 1080 + 6 + 1, .vtotal = 1080 + 6 + 1 + 4, .vrefresh = 60, }, { .clock = 74250*2, .hdisplay = 1920, .hsync_start = 1920 + 23,//加左边界 .hsync_end = 1920 + 23 + 44,//加左边界加水平同步 .htotal = 1920 + 280,//加左边界加水平同步加右边界 .vdisplay = 1080, .vsync_start = 1080 + 4, .vsync_end = 1080 + 4 + 5, .vtotal = 1080 + 20, .vrefresh = 60, }, { .clock = 34906, .hdisplay = 852, .hsync_start = 852 + 47, .hsync_end = 852 + 47 + 44, .htotal = 852 + 47 + 44 + 149, .vdisplay = 480, .vsync_start = 480 + 4, .vsync_end = 480 + 4 + 5, .vtotal = 480 + 4 + 5 + 36, .vrefresh = 60, }, { .clock = 61658, .hdisplay = 1024, .hsync_start = 1024 + 47, .hsync_end = 1024 + 47 + 42, .htotal = 1024 + 47 + 42 + 149, .vdisplay = 768, .vsync_start = 768 + 4, .vsync_end = 768 + 4 + 5, .vtotal = 768 + 4 + 5 + 36, .vrefresh = 60, }, { .clock = 25000, .hdisplay = 640, .hsync_start = 640 + 48, .hsync_end = 640 + 48 + 45, .htotal = 640 + 48 + 45 + 137, .vdisplay = 480, .vsync_start = 480 + 4, .vsync_end = 480 + 4 + 5, .vtotal = 480 + 4 + 5 + 36, .vrefresh = 60, } }; static int dsi84_get_modes(struct drm_connector *connector) { struct drm_display_mode *mode; printk("[jxq debug] %s,%d\n",__func__,__LINE__); mode = drm_mode_duplicate(connector->dev, &default_mode[0]); if (!mode) { DRM_ERROR("failed to add mode %ux%ux@%u\n", default_mode[0].hdisplay, default_mode[0].vdisplay, default_mode[0].vrefresh); return -ENOMEM; } drm_mode_set_name(mode); drm_mode_probed_add(connector, mode); return 1; } static const struct drm_connector_helper_funcs dsi84_connector_helper_funcs = { .get_modes = dsi84_get_modes, }; static enum drm_connector_status dsi84_detect(struct drm_connector *connector, bool force) { return connector_status_connected; } static const struct drm_connector_funcs dsi84_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .detect = dsi84_detect, .destroy = drm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; static void dsi84_dump_reg(struct dsi84_bridge *ite_bridge, dsi84_setting_table *table, unsigned int count){ unsigned int i; unsigned char data; for(i = 0; i < count; i++) { unsigned char cmd; cmd = table[i].cmd; switch (cmd) { case REGFLAG_DELAY : msleep(table[i].data); break; case 0xFF: break; default: sn65dsi84_i2c_read(ite_bridge->client, &cmd, 1, &data, 1); printk("dsi84_dump_reg dump cmd=0x%x data=0x%x \n",cmd,data); } } } static void dsi84_enable(struct drm_bridge *bridge) { struct dsi84_bridge *ite_bridge = bridge_to_dsi84(bridge); int ret; char reg; char data; //这个里面对sn65dsi84做初始化 printk("[jxq debug] %s,%d\n",__func__,__LINE__); //WARN_ON(1); ret = dsi84_write_byte(ite_bridge, 0x09, 0x00); if(ret < 0){ printk("[jxq debug] soft_reset fail\n"); } dsi84_write_byte(ite_bridge, 0x0A, 0x05); dsi84_write_byte(ite_bridge, 0x0B, 0x28);//分频0010 1000,dsi clk/6 dsi84_write_byte(ite_bridge, 0x0D, 0x00); msleep(10); dsi84_write_byte(ite_bridge, 0x10, 0x26);// dsi84_write_byte(ite_bridge, 0x11, 0x00); dsi84_write_byte(ite_bridge, 0x12, 0x52);//DSI CLK 411MHz dsi84_write_byte(ite_bridge, 0x13, 0x00); dsi84_write_byte(ite_bridge, 0x18, 0x6C); dsi84_write_byte(ite_bridge, 0x19, 0x00);//0F dsi84_write_byte(ite_bridge, 0x1A, 0x03);// dsi84_write_byte(ite_bridge, 0x1B, 0x00);// dsi84_write_byte(ite_bridge, 0x20, 0x80); dsi84_write_byte(ite_bridge, 0x21, 0x07);//1920 dsi84_write_byte(ite_bridge, 0x22, 0x00); dsi84_write_byte(ite_bridge, 0x23, 0x00); dsi84_write_byte(ite_bridge, 0x24, 0x00); dsi84_write_byte(ite_bridge, 0x25, 0x00); dsi84_write_byte(ite_bridge, 0x26, 0x00); dsi84_write_byte(ite_bridge, 0x27, 0x00); dsi84_write_byte(ite_bridge, 0x28, 0x20);//0xe1 dsi84_write_byte(ite_bridge, 0x29, 0x00);// dsi84_write_byte(ite_bridge, 0x2A, 0x00);// dsi84_write_byte(ite_bridge, 0x2B, 0x00); dsi84_write_byte(ite_bridge, 0x2C, 0x04); dsi84_write_byte(ite_bridge, 0x2D, 0x00);//水平同步4 dsi84_write_byte(ite_bridge, 0x2E, 0x00); dsi84_write_byte(ite_bridge, 0x2F, 0x00); dsi84_write_byte(ite_bridge, 0x30, 0x01); dsi84_write_byte(ite_bridge, 0x31, 0x00);//垂直同步1 dsi84_write_byte(ite_bridge, 0x32, 0x00); dsi84_write_byte(ite_bridge, 0x33, 0x00); dsi84_write_byte(ite_bridge, 0x34, 0x58);//hbp88 dsi84_write_byte(ite_bridge, 0x35, 0x00); dsi84_write_byte(ite_bridge, 0x36, 0x00);//vbp6 dsi84_write_byte(ite_bridge, 0x37, 0x00); dsi84_write_byte(ite_bridge, 0x38, 0x00);//hfp92 0x5c dsi84_write_byte(ite_bridge, 0x39, 0x00); dsi84_write_byte(ite_bridge, 0x3A, 0x00);//vfp4 dsi84_write_byte(ite_bridge, 0x3B, 0x00); dsi84_write_byte(ite_bridge, 0x3C, 0x00); dsi84_write_byte(ite_bridge, 0x3D, 0x00); dsi84_write_byte(ite_bridge, 0x3E, 0x00); dsi84_write_byte(ite_bridge, 0xE5, 0xFF); dsi84_write_byte(ite_bridge, 0x0D, 0x01); msleep(20); dsi84_write_byte(ite_bridge, 0x09, 0x01); msleep(20); reg = 0xe5; sn65dsi84_i2c_read(ite_bridge->client, ®, 1, &data, 1); printk("[jxq debug] 0xe5 = 0x%2x\n",data); msleep(20); reg = 0xe5; sn65dsi84_i2c_read(ite_bridge->client, ®, 1, &data, 1); printk("[jxq debug] 0xe5 = 0x%2x\n",data); msleep(20); reg = 0xe5; sn65dsi84_i2c_read(ite_bridge->client, ®, 1, &data, 1); printk("[jxq debug] 0xe5 = 0x%2x\n",data); msleep(20); reg = 0xe5; sn65dsi84_i2c_read(ite_bridge->client, ®, 1, &data, 1); printk("[jxq debug] 0xe5 = 0x%2x\n",data); dsi84_dump_reg(ite_bridge, dsi84_init_table, (unsigned int)(sizeof(dsi84_init_table) / sizeof(dsi84_setting_table))); } static void dsi84_pre_enable(struct drm_bridge *bridge) { //struct dsi84_bridge *ite_bridge = bridge_to_dsi84(bridge); printk("[jxq debug] %s,%d\n",__func__,__LINE__); //WARN_ON(1); //gpiod_set_value(ite_bridge->gpio_en, 0); //msleep(100); //gpiod_set_value(ite_bridge->gpio_en, 1); //msleep(100); //ite_bridge->enabled = true; } static void dsi84_disable(struct drm_bridge *bridge) { struct dsi84_bridge *ite_bridge = bridge_to_dsi84(bridge); //WARN_ON(1); if (!ite_bridge->enabled) return; ite_bridge->enabled = false; gpiod_set_value(ite_bridge->gpio_en, 0); } static int dsi84_bridge_attach(struct drm_bridge *bridge) { struct dsi84_bridge *ite_bridge = bridge_to_dsi84(bridge); struct device *dev = &ite_bridge->client->dev; struct device_node *np = dev->of_node; struct device_node *port, *in_ep; struct device_node *dsi_node = NULL; struct mipi_dsi_host *host = ite_bridge->dsi.host; int ret; printk("[jxq debug] %s,%d\n",__func__,__LINE__); //WARN_ON(1); if (!bridge->encoder) { DRM_ERROR("Parent encoder object not found"); return -ENODEV; } ret = drm_connector_init(bridge->dev, &ite_bridge->connector, &dsi84_connector_funcs, DRM_MODE_CONNECTOR_DSI); if (ret) { DRM_ERROR("Failed to initialize connector with drm\n"); return ret; } drm_connector_helper_add(&ite_bridge->connector, &dsi84_connector_helper_funcs); ite_bridge->connector.dpms = DRM_MODE_DPMS_ON; drm_mode_connector_attach_encoder(&ite_bridge->connector, bridge->encoder); port = of_graph_get_port_by_id(np, 0); if (port) { in_ep = of_get_child_by_name(port, "endpoint"); of_node_put(port); if (in_ep) { dsi_node = of_graph_get_remote_port_parent(in_ep); of_node_put(in_ep); } } if (dsi_node) { host = of_find_mipi_dsi_host_by_node(dsi_node); of_node_put(dsi_node); if (!host) { ret = -ENODEV; goto err; } } ite_bridge->dsi.host = host; ite_bridge->dsi.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; ite_bridge->dsi.format = MIPI_DSI_FMT_RGB888; ite_bridge->dsi.lanes = 4; ret = mipi_dsi_attach(&ite_bridge->dsi); if (ret) goto err; printk("%s Exit ! ",__FUNCTION__); return 0; err: drm_connector_cleanup(&ite_bridge->connector); return ret; } static const struct drm_bridge_funcs dsi84_bridge_funcs = { .pre_enable = dsi84_pre_enable, .enable = dsi84_enable, .disable = dsi84_disable, .attach = dsi84_bridge_attach, }; static int dsi84_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; struct dsi84_bridge *ite_bridge; int i,ret,err; int chipid[]={0x35, 0x38, 0x49, 0x53, 0x44, 0x20, 0x20, 0x20, 0x01}; char address,value; printk("[jxq debug] %s,%d\n",__func__,__LINE__); ite_bridge = devm_kzalloc(dev, sizeof(*ite_bridge), GFP_KERNEL); if (!ite_bridge) return -ENOMEM; ite_bridge->client = client; //en接gpio33 ite_bridge->gpio_en = devm_gpiod_get(&client->dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(ite_bridge->gpio_en)) { ret = PTR_ERR(ite_bridge->gpio_en); DRM_ERROR("cannot get gpio_en %d\n", ret); return ret; } printk("[jxq debug] %ld,%s,%s\n",ite_bridge->gpio_en->flags,ite_bridge->gpio_en->label,ite_bridge->gpio_en->name); gpiod_set_value(ite_bridge->gpio_en, 0); msleep(500); gpiod_set_value(ite_bridge->gpio_en, 1); msleep(100); //ret = dsi84_write_byte(ite_bridge, 0x09, 0x01); //if(ret < 0){ // printk("[jxq debug] soft_reset fail\n"); //} //dsi84_write_byte(ite_bridge, 0x0D, 0x00); for(i=0;i<sizeof(chipid)/sizeof(int);i++) { address=(char)i; //err=dsi84_read_byte(ite_bridge, address, &value, 1); err=sn65dsi84_i2c_read(ite_bridge->client, &address, 1, &value, 1); if (err < 0) { dev_err(&ite_bridge->client->dev, "fail to read chip id\n"); return err; } printk("[jxq debug] value=0x%x\n",value); if(value!=chipid[i]) { dev_err(&ite_bridge->client->dev, "chip id is not correct\n"); return err; } } ite_bridge->bridge.funcs = &dsi84_bridge_funcs; ite_bridge->bridge.of_node = dev->of_node; ret = drm_bridge_add(&ite_bridge->bridge); if (ret) { DRM_ERROR("Failed to add bridge\n"); return ret; } i2c_set_clientdata(client, ite_bridge); return 0; } static int dsi84_remove(struct i2c_client *client) { struct dsi84_bridge *ite_bridge = i2c_get_clientdata(client); drm_bridge_remove(&ite_bridge->bridge); return 0; } static const struct i2c_device_id dsi84_i2c_table[] = { {"dsi84", 0}, {}, }; MODULE_DEVICE_TABLE(i2c, dsi84_i2c_table); static const struct of_device_id dsi84_match[] = { { .compatible = "ti,sn65dsi84" }, {}, }; MODULE_DEVICE_TABLE(of, dsi84_match); static struct i2c_driver dsi84_driver = { .id_table = dsi84_i2c_table, .probe = dsi84_probe, .remove = dsi84_remove, .driver = { .name = "ti,dsi84", .of_match_table = dsi84_match, }, }; module_i2c_driver(dsi84_driver); MODULE_DESCRIPTION("TI DS90UB947 driver"); MODULE_LICENSE("GPL v2");
Please reference this FAQ: https://e2e.ti.com/support/interface/f/138/t/852871?-FAQ-SN65DSI84-No-display-output-with-SN65DSI83-SN65DSI84-SN65DSI85
Regards,
I.K.
thanks for your reply.
there are two questions about the initialization sequence.
1 Does the dsi-clk have to come out earlier than the sn65dsi84 EN or CSR-init?
2 Is the LP-11 State necessary in the DSI-DATA?
Hi Xiaoqiong,
The datasheet lists the steps and order for the initialization sequence. Every step in the initialization sequence is required in the order that it's listed, except for steps 9 through 11.
Regards,
I.K.
Hi I.K.
I am following the init sequence listed in the datasheet .But the PLL is still unlocked.Compared to your init sequence showed on the oscilloscope,i find the LP11 state time of our mediatek 2712 platform is only 1-2us. i have no idea why the PLL is unlocked .please help je check this question.the attach is my .dsi output file from the DSI-Tuner .dsi84-72.6M.dsi.7z
Hi Xiaoqiong,
Looking at your .dsi output file, you did not configure the device correctly.
1. Your configuration is single DSI to dual LVDS. This means that the horizontal timing parameters (HPW, HBP, HFP, HActive) on the DSI side need to be exactly double that of the LVDS side. The vertical parameters can remain the same.
2. The DSI CLK should be calculated using equation 2 of this document: http://www.ti.com/lit/an/slla356/slla356.pdf Since the configuration is single DSI to dual LVDS, the equation should be multiplied by 2. Your DSI CLK should be 435.6 MHz exactly to support dual 72.6 MHz LVDS clocks.
Regards,
I.K.
Hi I.K.
I make the the horizontal timing parameters (HPW, HBP, HFP, HActive) on the DSI side doulbe that of the LVDS side and correct the DSI CLK ,but there are also no lvds video output.The attach is my new .dsi output file.
According to the 2.5 chapterof the document:http://www.ti.com/lit/an/slla356/slla356.pdf ,I think my init sequence and configure are correct.please help me check again.thands a lot.
Hi Xiaoqiong,
Please share another screenshot of your initialization sequence, and label it similar to the one here: https://e2e.ti.com/support/interface/f/138/t/852871?-FAQ-SN65DSI84-No-display-output-with-SN65DSI83-SN65DSI84-SN65DSI85
In addition, are you making sure to enable the PLL by setting bit 0 in register 0x0D to 1 as shown in the initialization sequence?
Also, now that you have fixed the timing parameters and clock frequency, have you also ensured that your DSI source is actually outputting these new values?
Regards,
I.K.
HI I.K.
thands for your reply.
1 My initialization sequence is similar to the one in the link. Below is a screenshot on my oscilloscope:
2 I am sure I enable the PLL by setting bit 0 in register 0x0D to 1 .
3 The DSI output match the timing parameters value I fixed.
There is also no lvds output . LVDS clk can be detect using the oscilloscope and the frequence is right .
The test pattern mode is ok.
Hi Xiaoqiong,
Sorry for the delay.
I don't see the DSI CLK in your screenshot. Can you please capture the initialization sequence again in a format similar to this: https://e2e.ti.com/support/interface/f/138/t/852871?-FAQ-SN65DSI84-No-display-output-with-SN65DSI83-SN65DSI84-SN65DSI85
Regards,
I.K.
Hi Xiaoqiong,
Can you provide a register dump of the DSI84?
Regards,
I.K.
Hi I.K.
thanks for your reply.
The attach is a register dump.Please help me check it.
0x09 0x00 0x0A 0x05 0x0B 0x28 0x0D 0x01 0x10 0x26 0x11 0x00 0x12 0x57 0x13 0x00 0x18 0x6c 0x19 0x00 0x1A 0x03 0x1B 0x00 0x20 0x80 0x21 0x07 0x22 0x00 0x23 0x00 0x24 0x00 0x25 0x00 0x26 0x00 0x27 0x00 0x28 0x20 0x29 0x00 0x2A 0x00 0x2B 0x00 0x2C 0x50 0x2D 0x00 0x2E 0x00 0x2F 0x00 0x30 0x07 0x31 0x00 0x32 0x00 0x33 0x00 0x34 0x64 0x35 0x00 0x36 0x00 0x37 0x00 0x38 0x00 0x39 0x00 0x3A 0x00 0x3B 0x00 0x3C 0x00 0x3D 0x00 0x3E 0x00 0x0D 0x00 0x09 0x01
Hi Xiaoqiong,
The dump looks okay. Have you tried enabling the test pattern to see if that can display correctly (you can enable the test pattern by checking the "Test Pattern" box in the DSI-Tuner and regenerating the settings)?
Regards,
I.K.
Hi Xiaoqiong,
If the test pattern works then the issue is likely with your DSI source. Can you check #4 on this FAQ: https://e2e.ti.com/support/interface/f/138/t/852871
Regards,
I.K.