1. S3C2440 LCD驱动架构解析

S3C2440作为经典的ARM9处理器,其LCD控制器采用硬件加速设计,通过DMA通道直接操作显存。内核驱动代码主要分布在drivers/video/fbdev/s3c2410fb.c中,这个文件实现了帧缓冲设备的核心操作。

驱动采用标准的platform_driver架构,与设备树中的节点匹配后触发probe函数。关键数据结构包括:

struct s3c2410fb_info {
    struct device      *dev;
    struct clk        *clk;
    void __iomem      *io;
    struct s3c2410fb_hw regs;
    u32               palette_buffer[256];
    u32               pseudo_pal[16];
};

注意:不同内核版本中寄存器定义可能变化,4.19内核后新增了alpha混合支持

2. 硬件接口配置详解

2.1 时序参数计算

LCD驱动需要配置的关键时序参数包括:

  • VCLK:像素时钟频率 = (HCLK / (CLKVAL +1)) × 2
  • VBPD:垂直后沿 = (tvb - 1) / (tv + 1)
  • VFPD:垂直前沿 = tvf - 1
  • VSPW:垂直同步脉宽 = tvp - 1

以800x480 LCD为例,典型配置如下:

static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
    .timing = {
        .h_fp    = 8,
        .h_bp    = 4,
        .h_sw    = 8,
        .v_fp    = 2,
        .v_fpe   = 1,
        .v_bp    = 2,
        .v_bpe   = 1,
        .v_sw    = 1,
    },
    .default_display = 0,
};

2.2 显存管理

驱动通过dma_alloc_writecombine()申请DMA内存:

fbi->screen_base = dma_alloc_writecombine(dev, 
                    map_size, &map_dma, GFP_KERNEL);

显存布局采用双缓冲设计:

+-------------------+
| 主缓冲 (800x480x2) |
+-------------------+
| 副缓冲 (800x480x2) |
+-------------------+
| 调色板 (256x4)    |
+-------------------+

3. 关键函数流程分析

3.1 初始化流程

  1. s3c2410fb_probe()

    • 获取平台资源(I/O映射、中断等)
    • 配置GPIO为LCD功能
    • 初始化时钟和PLL
    • 注册framebuffer设备
  2. s3c2410fb_set_par()

    • 计算并设置时序参数
    • 配置DMA缓冲区地址
    • 更新调色板

3.2 中断处理

VSYNC中断服务程序完成:

static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)
{
    struct s3c2410fb_info *fbi = dev_id;
    u32 irq_sts = readl(fbi->irq_base + S3C2410_LCDINTPND);
    
    if (irq_sts & S3C2410_LCDINT_VSYNC) {
        wake_up_interruptible(&fbi->vsync_wait);
        writel(S3C2410_LCDINT_VSYNC, 
              fbi->irq_base + S3C2410_LCDINTPND);
    }
    return IRQ_HANDLED;
}

4. 性能优化实践

4.1 DMA传输优化

通过调整BURST_SIZE提升吞吐量:

#define S3C2410_LCDFIFO_BURST_4    (0x3 << 4)
#define S3C2410_LCDFIFO_BURST_8    (0x2 << 4) 
#define S3C2410_LCDFIFO_BURST_16   (0x1 << 4)

4.2 双缓冲实现

在fb_ops中实现页面切换:

static int s3c2410fb_pan_display(struct fb_var_screeninfo *var,
                                struct fb_info *info)
{
    struct s3c2410fb_info *fbi = info->par;
    dma_addr_t dma_addr = fbi->screen_dma + var->yoffset * fbi->fix.line_length;
    
    writel(dma_addr, fbi->regs + S3C2410_LCDSADDR1);
    return 0;
}

5. 常见问题排查

5.1 花屏问题处理

  1. 检查时序参数是否匹配LCD规格书
  2. 确认HCLK频率是否稳定
  3. 测量VCLK信号质量
  4. 排查显存地址对齐问题

5.2 性能调优记录

通过ftrace测得各阶段耗时:

| 阶段                | 4.9内核(ms) | 5.10内核(ms) |
|---------------------|------------|-------------|
| DMA配置             | 1.2        | 0.8         |
| 调色板更新          | 3.5        | 1.2         |
| 垂直同步等待        | 16.7       | 8.9         |

6. 设备树配置实例

典型S3C2440 LCD节点配置:

lcd-controller@4D000000 {
    compatible = "samsung,s3c2440-lcd";
    reg = <0x4D000000 0x20>;
    interrupts = <0 0 16>;
    clocks = <&lcd CLK_LCD>;
    clock-names = "lcd";
    pinctrl-names = "default";
    pinctrl-0 = <&lcd_pins>;
    status = "okay";

    display-timings {
        native-mode = <&timing0>;
        timing0: 800x480 {
            clock-frequency = <33300000>;
            hactive = <800>;
            vactive = <480>;
            hfront-porch = <40>;
            hback-porch = <88>;
            hsync-len = <48>;
            vfront-porch = <13>;
            vback-porch = <32>;
            vsync-len = <3>;
            hsync-active = <0>;
            vsync-active = <0>;
        };
    };
};

7. 调试技巧汇编

  1. 寄存器检查命令:
adb shell cat /sys/kernel/debug/regmap/4d000000.lcd/registers
  1. 帧率测量方法:
static void measure_fps(struct fb_info *info)
{
    struct timespec ts1, ts2;
    getnstimeofday(&ts1);
    for (int i = 0; i < 60; i++) {
        fb_pan_display(info, &info->var);
    }
    getnstimeofday(&ts2);
    pr_info("FPS: %ld\n", 60 * NSEC_PER_SEC / (ts2.tv_nsec - ts1.tv_nsec));
}
  1. 内存泄漏检测:
echo scan > /sys/kernel/debug/kmemleak
cat /sys/kernel/debug/kmemleak

更多推荐