Tiny4412 LCD驱动(FB)

0 前言

    本文重点分析Tiny4412官网内核linux-3.5.0中的LCD驱动,其中关键内核配置如下:

CONFIG_FB=y
CONFIG_FB_S3C=y
CONFIG_FB_S3C_NR_BUFFERS=3

相关:《SMDK2440 LCD驱动(FB)》、《SMDK6410 LCD驱动(FB)》、《Tiny4412 LCD驱动(DRM)》

1 平台设备

    基本同《Tiny4412 LCD驱动(DRM)》1.1~1.5小节,主要差异在于平台数据。

1.1 平台数据

    Tiny4412的LCD平台数据定义如下:

static struct s3c_fb_platdata smdk4x12_lcd0_pdata __initdata = {
    .win[0]     = &smdk4x12_fb_win0,
    .win[1]     = &smdk4x12_fb_win1,
    .win[2]     = &smdk4x12_fb_win2,
    .win[3]     = &smdk4x12_fb_win3,
    .win[4]     = &smdk4x12_fb_win4,
    .vtiming    = &smdk4x12_lcd_timing,
    .vidcon0    = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
    .vidcon1    = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
    .setup_gpio = exynos4_fimd0_gpio_setup_24bpp,
};
// @file: arch/arm/mach-exynos/mach-tiny4412.c

    共定义了5个win,在内核日志中将会发现:

Tiny4412 LCD驱动(FB)_第1张图片

    上述过程将会创建5个FB设备文件:

    上述smdk4x12_fb_win0~4的具体参数都是一样的:

static struct s3c_fb_pd_win smdk4x12_fb_win0 = {
    .xres           = 480, // 注:该值并非最终值,会被tiny4412_fb_init_pdata()修改,下同
    .yres           = 800,
    .virtual_x      = 480,
    .virtual_y      = 800 * CONFIG_FB_S3C_NR_BUFFERS,
    .max_bpp        = 32,
    .default_bpp    = 24,
    .width          = 66,
    .height         = 109,
};
// 此处略去smdk4x12_fb_win1~4 ...
// @file: arch/arm/mach-exynos/mach-tiny4412.c

    需要注意的是,由于Tiny4412开发板支持多个型号的LCD,因此上述.xres.yres.virtual_x.virtual_y.width.height以及.vidcon1都需要根据不同的LCD进行调整:

static void __init tiny4412_fb_init_pdata(struct s3c_fb_platdata *pd) {
    struct s3cfb_lcd *lcd;
    ...
    lcd = tiny4412_get_lcd();

    for (i = 0; i < S3C_FB_MAX_WIN; i++) {
        if (pd->win[i] == NULL)
            continue;

        win = pd->win[i];
        win->xres       = lcd->width;
        win->yres       = lcd->height;
        win->default_bpp= lcd->bpp ? : 24;
        win->virtual_x  = win->xres;
        win->virtual_y  = win->yres * CONFIG_FB_S3C_NR_BUFFERS;
        win->width      = lcd->p_width;
        win->height     = lcd->p_height;
    }
    ...
    /* initialize signal polarity of RGB interface */
    if (lcd->polarity.rise_vclk)
        val |= VIDCON1_INV_VCLK;
    if (lcd->polarity.inv_hsync)
        val |= VIDCON1_INV_HSYNC;
    if (lcd->polarity.inv_vsync)
        val |= VIDCON1_INV_VSYNC;
    if (lcd->polarity.inv_vden)
        val |= VIDCON1_INV_VDEN;

    pd->vidcon1 = val;
}
// @file: arch/arm/mach-exynos/mach-tiny4412.c

    获取LCD参数的关键在于下面函数:

struct s3cfb_lcd *tiny4412_get_lcd(void)
{
    return tiny4412_lcd_config[lcd_idx].lcd;
}
// @file: arch/arm/mach-exynos/tiny4412-lcds.c

    可见它主要是从下面列表中选择序号为lcd_idx的配置:

static struct {
    char *name;
    struct s3cfb_lcd *lcd;
    int ctp;
} tiny4412_lcd_config[] = {
    // 此处略去若干行...
    { "S702",   &wvga_s70,   1 },
    // 此处略去若干行...
    { "HDM",    &hdmi_def,   0 },   /* Pls keep it at last */
};
// @file: arch/arm/mach-exynos/tiny4412-lcds.c

    而lcd_idx是由命令行参数“lcd=<屏幕名称>”决定的:

early_param("lcd", tiny4412_setup_lcd);
// @file: arch/arm/mach-exynos/tiny4412-lcds.c

    tiny4412_setup_lcd()使用命令行参数指定的屏幕名称遍历上述tiny4412_lcd_config数组,找出名字匹配的配置,此时lcd_idx就被确定下来了,它最后还会在内核日志中将匹配结果打印出来。例如我购买的屏幕型号为S702,添加U-Boot命令行参数“lcd=S702”,内核日志如下:

    由上述tiny4412_lcd_config数组可知,S702对应的配置为:

static struct s3cfb_lcd wvga_s70 = {
    .width = 800,
    .height = 480,
    .p_width = 155,
    .p_height = 93,
    .bpp = 24,
    .freq = 63,

    .timing = {
        .h_fp = 80,
        .h_bp = 36,
        .h_sw = 10,
        .v_fp = 22,
        .v_fpe = 1,
        .v_bp = 15,
        .v_bpe = 1,
        .v_sw = 8,
    },
    .polarity = {
        .rise_vclk = 1, // 为1则设置VIDCON1_INV_VCLK
        .inv_hsync = 1, // 为1则设置VIDCON1_INV_HSYNC
        .inv_vsync = 1, // 为1则设置VIDCON1_INV_VSYNC
        .inv_vden = 0,  // 为1则设置VIDCON1_INV_VDEN
    },
};
// @file: arch/arm/mach-exynos/tiny4412-lcds.c

1.2 调试

    和/dev/fb0~fb4对应的sysfs目录如下:

      可进入上述目录查看LCD的modes(分辨率-刷新率)、bits_per_pixel(BPP)等参数。  

(1)modes

(2)virtual_size

(3)bits_per_pixel

(4)stride

2 平台驱动

static struct platform_driver s3c_fb_driver = {
    .probe      = s3c_fb_probe,
    .remove     = __devexit_p(s3c_fb_remove),
    .id_table   = s3c_fb_driver_ids,
    .driver     = {
        .name   = "s3c-fb",
        .owner  = THIS_MODULE,
        .pm = &s3cfb_pm_ops,
    },
};
// @file: drivers/video/s3c-fb.c

    其.id_table定义如下:

static struct platform_device_id s3c_fb_driver_ids[] = {
    {
        .name       = "s3c-fb",
        .driver_data    = (unsigned long)&s3c_fb_data_64xx,
    }, {
        .name       = "s5pc100-fb",
        .driver_data    = (unsigned long)&s3c_fb_data_s5pc100,
    }, {
        .name       = "s5pv210-fb",
        .driver_data    = (unsigned long)&s3c_fb_data_s5pv210,
    }, {
        .name       = "exynos4-fb",  // 瞧这里!
        .driver_data    = (unsigned long)&s3c_fb_data_exynos4,
    }, {
        .name       = "exynos5-fb",
        .driver_data    = (unsigned long)&s3c_fb_data_exynos5,
    }, {
        .name       = "s3c2443-fb",
        .driver_data    = (unsigned long)&s3c_fb_data_s3c2443,
    }, {
        .name       = "s5p64x0-fb",
        .driver_data    = (unsigned long)&s3c_fb_data_s5p64x0,
    },
    {},
};
MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids);
// @file: drivers/video/s3c-fb.c

3 匹配过程

    由《MINI2440看门狗驱动》3.1小节可知,平台设备和驱动的匹配规则有3条,《SMDK2440 LCD驱动(FB)》、《SMDK6410 LCD驱动(FB)》都是通过驱动和设备的名称来进行匹配,而本驱动则采用.id_table来匹配,匹配过程会遍历s3c_fb_driver_ids数组只要其中一个.name和平台设备的.name一致,就算匹配成了,"exynos4-fb"就是匹配成功的名称。

4 测试

    Android源码提供了一个很好的测试工具system/extras/tests/framebuffer/fb_test.c,将其交叉编译放进开发板,运行效果如下:

Tiny4412 LCD驱动(FB)_第2张图片

参考资料

你可能感兴趣的:(Tiny4412 LCD驱动(FB))