Other Parts Discussed in Thread: DSI-TUNER
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");