分类: 嵌入式
2014-04-23 10:34:40
LCD 调试总结
数据流程:
Framebuffer -> DC -> Dsi -> Dsi83 -> LCD
DC 通过DMA从framebuffer 取数据 ,然后通过并口传递给Dsi , Dsi 输出mipi 信号给Dsi83 , Dsi83 将MIPI 信号转成LVDS 信号给LCD 。
初始化流程:
首先得给各个器件上电,然后执行各个控制器的初始化代码,这个过程里面要确保各个信号的稳定性以及时序符合芯片要求,这是调试驱动的第一步,这一步不能确保后面不管怎么搞都是瞎搞。
遇到的问题:
一、不知道pclk 是什么意思,也不了解pclk 与mipi clk 的换算关系:
1,Pclk 是一秒钟要送出的总的像素点,实际上就是 一行的像素点*总行数*帧率
2,mipi clk 的算式就是mipi clk = pclk * bpp/num(lane)*2
文字解释,pclk*bpp 就是一秒要送出的总的bit 位也就是比特率,这是每一秒由DC 送给Dsi 的数据,因为它在芯片内部是以并口的形式送出的,而Dsi 拿到数据后是以串口的形式送出的,所以这里计算mipi的clk 要除以lane的总数,又因为mipi是上升沿下降沿都取数据,所以这里要除以2。
二、这次主要是遇到LCD 不断闪屏,为了debug做过的实验有:
1, 测试模式下用 mipi clk ,仍然闪屏
2,测试模式下用外部 clk ,不闪屏
实际上从这里就有理由怀疑mipi 的输出有问题,从原理上来看接下来也应该去check dsi的输出信号,但是因为mipi 没有测试点接出来,所以一开始居然避开了这一项检测,转而去狂填参数,现在想起来大概是因为填参数对软件而言比较容易,但实在是没什么道理,我也对一开始没有坚持自己的推理让硬件去测信号感到不满。
解决问题:
最终吹掉dsi83 IC ,量mipi 信号,发现时钟的确输出不连贯,而对比使用该IC 的能正常显示的板子,其clk 是连贯的。那么我们首先要保证mipi 信号输出的正确性。
最后改掉一个控制clk 的 参数TEGRA_DSI_VIDEO_CLOCK_CONTINUOUS 即可。
总结: 遇到问题,应该跳出来,找人喝茶聊天,根据原理寻找思路,而不是匆匆忙忙冒冒失失的一头扎进去,以至深陷其中,不能自拔。
最后附上一张图:
方向不对,越努力越尴尬。
========================================华丽丽的分界线================代码======================
代码介绍:
英伟达平台:
对于linux 内核显示相关代码在 Dc.c (drivers\video\tegra\dc) 目录下。
这是display control的驱动文件,在这个文件中首先看driver 结构体:
struct platform_driver tegra_dc_driver = {
.driver = {
.name = "tegradc",// 根据这个名字找到对应设备资源
.owner = THIS_MODULE,
},
.probe = tegra_dc_probe,
.remove = tegra_dc_remove,
#ifdef CONFIG_PM
.suspend = tegra_dc_suspend,
.resume = tegra_dc_resume,
#endif
.shutdown = tegra_dc_shutdown,
};
设备资源在文件Board-macallan-panel.c (arch\arm\mach-tegra) 中
static struct platform_device macallan_disp2_device = {
.name = "tegradc",
.id = 1,
.resource = macallan_disp2_resources,
.num_resources = ARRAY_SIZE(macallan_disp2_resources),
.dev = {
.platform_data = &macallan_disp2_pdata,
},
};
static struct platform_device macallan_disp1_device = {
.name = "tegradc",
.id = 0,
.resource = macallan_disp1_resources,
.num_resources = ARRAY_SIZE(macallan_disp1_resources),
.dev = {
.platform_data = &macallan_disp1_pdata,
},
};
这里tegra4 有两个控制器,一个给Dsi 用,另一个给HDMI 用。这些板级资源的具体功能需要对照tegra4 的spec 来看,主要是一些寄存器的配置。
然后在看DC 和 DSI 的控制器初始化代码前得看panel 的代码,因为DC 和DSI 输出要匹配panel的接受,两者不匹配就会出问题,匹配了,就OK!
DC 的输出分HDMI 和 DSI ,他们的配置是如下函数设置的:
static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out)
{
struct tegra_dc_mode *mode;
dc->out = out;
printk("liucong tegra_dc_set_out\n");
mode = tegra_dc_get_override_mode(dc);
if (mode)
tegra_dc_set_mode(dc, mode);
else if (out->n_modes > 0)
tegra_dc_set_mode(dc, &dc->out->modes[0]);
switch (out->type) {
case TEGRA_DC_OUT_RGB:
dc->out_ops = &tegra_dc_rgb_ops;
break;
case TEGRA_DC_OUT_HDMI:
dc->out_ops = &tegra_dc_hdmi_ops;
break;
case TEGRA_DC_OUT_DSI:
dc->out_ops = &tegra_dc_dsi_ops;// 通用显示串行输出接口,这里是mipi 控制器的操作接口
break;
default:
dc->out_ops = NULL;
break;
}
if (dc->out_ops && dc->out_ops->init)
dc->out_ops->init(dc);
}
望文生义,tegra_dc_set_out就是设置DC 的输出,这里是以结构体的方式赋值的,这个结构体包含了具体输出的各种接口,比如DSI 的tegra_dc_dsi_ops :
struct tegra_dc_out_ops tegra_dc_dsi_ops = {
.init = tegra_dc_dsi_init,
.destroy = tegra_dc_dsi_destroy,
.enable = tegra_dc_dsi_enable,
.postpoweron = tegra_dc_dsi_postpoweron,
.disable = tegra_dc_dsi_disable,
.postpoweroff = tegra_dc_dsi_postpoweroff,
.hold = tegra_dc_dsi_hold_host,
.release = tegra_dc_dsi_release_host,
#ifdef CONFIG_PM
.suspend = tegra_dc_dsi_suspend,
.resume = tegra_dc_dsi_resume,
#endif
.setup_clk = tegra_dc_dsi_setup_clk,
};
Panel 的代码在Panel-p-wuxga-10-1.c (arch\arm\mach-tegra)
这里主要做三件事,1,背光数据的初始化,2,LCD相关上电时序的控制,3,DSI 输出信号的配置(需要匹配panel)
背光初始化和上电时序就不说了,这里说下第三点:
static struct tegra_dc_mode dsi_p_wuxga_10_1_modes[] = {
{
.pclk = 68900000,
.h_ref_to_sync = 10,// 4
.v_ref_to_sync = 1,// 1
.h_sync_width = 32,// 16
.v_sync_width = 6,// 2
.h_back_porch = 80,// 32
.v_back_porch = 14,// 16
.h_active = 1280,// 1920
.v_active = 800,// 1200
.h_front_porch = 48,// 120
.v_front_porch = 3,// 17
},
};
望文生义,看这个结构体的名字就知道tegra_dc_mode 是配置DC 输出要求的,这里有PCLK HBP HFP HWP VBP VFP VWP 等,他们的具体含义请自行百度了。
另外还有一个比较重要的结构体:
static struct tegra_dsi_out dsi_p_wuxga_10_1_pdata = {
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
.n_data_lanes = 2,
.controller_vs = DSI_VS_0,
#else
.controller_vs = DSI_VS_1,
#endif
.n_data_lanes = 2,
.video_burst_mode = TEGRA_DSI_VIDEO_NONE_BURST_MODE_WITH_SYNC_END,
.pixel_format = TEGRA_DSI_PIXEL_FORMAT_24BIT_P,
.refresh_rate = 60,
.virtual_channel = TEGRA_DSI_VIRTUAL_CHANNEL_0,
.dsi_instance = DSI_INSTANCE_0,
.dsi2lvds_bridge_enable = 1,
.panel_reset = DSI_PANEL_RESET,
.power_saving_suspend = true,
.video_data_type = TEGRA_DSI_VIDEO_TYPE_VIDEO_MODE,
.video_clock_mode = TEGRA_DSI_VIDEO_CLOCK_CONTINUOUS,// 这次卡这里了
.dsi_init_cmd = dsi_p_1280x800_8_0_init_cmd,
.n_init_cmd = ARRAY_SIZE(dsi_p_1280x800_8_0_init_cmd),
};
同样望文生义 tegra_dsi_out 是配置dsi输出信号的,这些都很重要,具体含义也不细说了,看注释或百度即可。
一般而言,上面的信息填好后panel就可以工作了,但我们这里还有个转接芯片,转接芯片的代码在Dsi2lvds.c (drivers\video\tegra\dc) ,这里面就是做了dsi83 芯片的初始化,通过I2C 接口。
另外我们还要注意 的是,这个Dsi 是用的mipi 协议,要注意MIPI 协议的一些规范,以及内核提供给我们的mipi 协议的接口,比如进入LP 模式的接口tegra_dsi_set_to_lp_mode等。代码在Dsi.c (drivers\video\tegra\dc)
=============================初始化流程========================
Init 流程:
[ 2.012377] [
[ 2.012821] [
[ 2.013300] [
[ 2.013733] [
[ 2.014165] [
[ 2.014455] [
[ 2.014881] [
[ 2.015345] [
[ 2.015780] [
[ 2.016200] [
[ 2.016670] [
[ 2.016933] [
[ 2.017405] [
[ 2.017832] [
[ 2.018294] [
[ 2.018721] [
[ 2.018976] [
Enable 流程:
[ 2.121421] [
[ 2.121856] [
[ 2.122339] [
[ 2.122779] [
[ 2.123211] [
[ 2.123687] [
[ 2.124110] [
[ 2.124396] [
[ 2.124821] [
[ 2.125255] [
[ 2.125724] [
[ 2.126146] [
[ 2.126462] [
[ 2.126721] [
[ 2.127154] [