最近在V3s平台上调试LCD驱动时遇到了一个棘手的问题:Uboot阶段显示正常,但进入Linux内核后屏幕出现闪烁条纹。这个问题在7寸液晶屏上特别明显,无论是1024×600还是800×480分辨率的屏幕都会出现。使用逻辑分析仪测量后发现,Uboot阶段的LCD_CLK时钟频率是稳定的25MHz,但进入内核后时钟频率却变成了33.3MHz。
这种时钟频率突变会导致什么问题呢?简单来说,LCD控制器需要按照固定的时序向屏幕发送数据,时钟频率的变化会打乱这个时序。就像音乐会上的指挥突然改变节奏,乐手们就会跟不上节拍一样。具体到我们的案例中,时钟频率升高会导致像素数据传输过快,屏幕来不及处理,最终表现为显示异常。
要解决这个问题,首先需要理解V3s的显示子系统架构。V3s的显示引擎主要由以下几个部分组成:
通过查阅全志SOC的技术文档和哇酷开发者社区的讨论,发现这个问题与TCON的时钟配置有关。具体来说,在linux-5.2.y内核的sun4i_tcon.c文件中,dclk_min_div参数的默认设置会导致时钟分频计算出现偏差。
使用逻辑分析仪捕获的信号显示,内核阶段的时钟频率确实从Uboot的25MHz跳变到了33.3MHz。这个变化不是随机的,而是由于内核驱动中的时钟分频计算方式与硬件预期不符导致的。在显示子系统中,时钟频率的计算公式大致为:
code复制实际频率 = 基础时钟频率 / (分频系数 + 1)
当分频系数设置不当时,就会得到不符合预期的时钟频率。
找到问题根源后,我们需要修改内核驱动来修正时钟配置。具体要修改的文件是:
code复制linux-5.2.y/drivers/gpu/drm/sun4i/sun4i_tcon.c
在这个文件的第488行左右,找到sun4i_tcon0_mode_set_rgb函数,将dclk_min_div的值从默认的6改为1:
c复制static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon,
const struct drm_encoder *encoder,
const struct drm_display_mode *mode)
{
// ...其他代码...
tcon->dclk_min_div = 1; // 原值为6
tcon->dclk_max_div = 127;
// ...其他代码...
}
这个修改的意义在于放宽时钟分频的最小限制。原来的dclk_min_div=6限制了分频系数的选择范围,在某些情况下会导致计算得到的时钟频率偏离预期值。将其改为1后,时钟分频器有更大的调节空间,能够计算出更接近目标值的频率。
修改完成后需要重新编译内核。编译时要注意使用正确的交叉编译工具链和配置文件。建议使用如下命令:
bash复制make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- sun8i-v3s_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4
仅仅修改内核驱动还不够,因为Uboot和内核之间的显示参数需要保持一致。对于已经量产的产品,如果只有网口没有调试串口,修改Uboot环境变量就需要特殊方法。
Uboot提供了fw_printenv工具来操作环境变量。首先需要在Uboot源码目录下编译这个工具:
bash复制make env
编译完成后会生成fw_printenv可执行文件。要使用这个工具,还需要创建配置文件/etc/fw_env.config,内容如下:
code复制/dev/mmcblk0 0x88000 0x20000
这个配置告诉工具环境变量存储在MMC设备的哪个位置。对于TF卡启动的系统,通常使用上述配置。如果是NAND启动,则需要根据实际情况调整。
修改LCD参数的具体命令示例:
bash复制fw_setenv lcd_width 800
fw_setenv lcd_height 480
fw_setenv lcd_dclk_freq 33000000
这些变量名需要根据具体的Uboot版本和板级支持包进行调整。修改完成后,重启设备使新参数生效。
为了确保系统各阶段的显示参数一致,还需要修改Linux设备树文件。对于V3s平台,设备树文件通常位于:
code复制arch/arm/boot/dts/sun8i-v3s-licheepi-zero-with-800x480-lcd.dts
在这个文件中,需要确认LCD面板的配置是否正确。关键是要匹配面板的compatible字符串:
dts复制&panel {
compatible = "urt,umsh-8596md-t", "simple-panel";
};
这个字符串必须与内核驱动中的定义一致。内核中LCD面板的定义可以在以下文件中找到:
code复制drivers/gpu/drm/panel/panel-simple.c
这个文件中包含了大量预定义的面板参数,我们需要确保设备树中使用的compatible字符串能在这里找到对应的配置。例如:
c复制static const struct of_device_id platform_of_match[] = {
{
.compatible = "urt,umsh-8596md-t",
.data = &urt_umsh_8596md_t_params,
},
// ...其他面板定义...
};
如果使用的面板不在预定义列表中,就需要自行添加对应的参数结构体。这包括设置合适的分辨率、时序参数和电源控制序列等。
完成上述修改后,需要进行全系统的调试验证。建议按照以下步骤进行:
bash复制printenv lcd_*
setenv lcd_width 800
setenv lcd_height 480
saveenv
bash复制dmesg | grep -i tcon
dmesg | grep -i mixer
使用逻辑分析仪或示波器测量实际的LCD_CLK频率,确保其与预期值一致。
如果仍有问题,可以尝试调整内核驱动中的其他参数,如:
bash复制echo 0xff > /sys/module/drm/parameters/debug
这会将DRM子系统的调试信息输出到内核日志。
在实际项目中,可能会遇到各种与LCD驱动相关的问题。以下是一些常见问题及其解决方法:
调试显示问题时,逻辑分析仪是最有用的工具之一。建议重点监测以下信号:
要彻底解决显示问题,有必要深入了解V3s的显示子系统架构。V3s的显示流水线大致如下:
时钟系统是这个流水线的关键。V3s使用PLL_VIDEO作为显示时钟源,经过多个分频器后供给各个模块。时钟配置不当会导致:
在设备树中,时钟配置通常如下所示:
dts复制&tcon0 {
clocks = <&ccu CLK_BUS_TCON0>, <&ccu CLK_TCON0>;
clock-names = "ahb", "tcon-ch0";
};
理解这些时钟之间的关系对于调试显示问题至关重要。例如,"ahb"时钟控制寄存器访问,"tcon-ch0"时钟则直接影响像素时钟频率。
对于复杂的显示问题,可能需要更深入的调试手段:
c复制dev_info(tcon->dev, "dclk rate: %lu, div: %d\n",
clk_get_rate(tcon->dclk), div);
bash复制# 查看TCON寄存器
devmem 0x01c0c000
bash复制perf stat -e cycles,instructions -a sleep 10
bash复制cat /sys/kernel/debug/pm_genpd/summary
bash复制memtester 100M 1
来测试内存性能。
调试显示问题时,保持耐心和系统性很重要。建议每次只修改一个参数,并记录修改前后的效果。这样可以帮助快速定位问题根源。