本文重点分析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)》
基本同《Tiny4412 LCD驱动(DRM)》1.1~1.5小节,主要差异在于平台数据。
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,在内核日志中将会发现:
上述过程将会创建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
和/dev/fb0~fb4对应的sysfs目录如下:
可进入上述目录查看LCD的modes(分辨率-刷新率)、bits_per_pixel(BPP)等参数。
(1)modes
(2)virtual_size
(3)bits_per_pixel
(4)stride
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
由《MINI2440看门狗驱动》3.1小节可知,平台设备和驱动的匹配规则有3条,《SMDK2440 LCD驱动(FB)》、《SMDK6410 LCD驱动(FB)》都是通过驱动和设备的名称来进行匹配,而本驱动则采用.id_table来匹配,匹配过程会遍历s3c_fb_driver_ids数组只要其中一个.name和平台设备的.name一致,就算匹配成了,"exynos4-fb"就是匹配成功的名称。
Android源码提供了一个很好的测试工具system/extras/tests/framebuffer/fb_test.c,将其交叉编译放进开发板,运行效果如下: