S5PV210 LCD DRIVER
硬件平台:SAMSUNG S5PV210
内核版本:LINUX-2.6.35.7
系统版本:ANDROID 2.3.1
一、platform device
S5PV210 LCD platform_device 的实例 s3c_device_fb 定义在 arch/arm/plat-s5p/devs.c
struct platform_device s3c_device_fb = {
.name = "s3cfb", // 设备名,与platform_driver 中的保持一致
.id = -1,// 设备编号,是做什么用的?
.num_resources = ARRAY_SIZE(s3cfb_resource), // 资源的数量
.resource = s3cfb_resource,// 设备所使用的资源
.dev = {
.dma_mask = &fb_dma_mask, // 设备DMA能访问的内存范围 (0xffffffffUL)
.coherent_dma_mask = 0xffffffffUL // 作用于申请一致性DMA缓冲区
}
};
资源信息定义如下:
static struct resource s3cfb_resource[] = {
[0] = {
.start = S5P_PA_LCD, // 设备占据内存的开始地址 (0xF8000000)
.end = S5P_PA_LCD + S5P_SZ_LCD - 1, // 设备占据内存的结束地址,大小为1M
.flags = IORESOURCE_MEM, // 表示为MEM
},
[1] = {
.start = IRQ_LCD1, // 中断号的开始值
.end = IRQ_LCD1, // 中断号的结束值
.flags = IORESOURCE_IRQ, // 标识为中断
},
[2] = {
.start = IRQ_LCD0,
.end = IRQ_LCD0,
.flags = IORESOURCE_IRQ,
},
};
在 arch/arm/mach-s5pv210/mach-s5pv210.c 中定义了 platform data :ek070tn93_fb_data
static struct s3c_platform_fb ek070tn93_fb_data __initdata = {
.hw_ver= 0x62, // 难道是 hardware version ?怎么来的?
.nr_wins = 5, // number window? s5pv210 有5个window,是不是这个意思?
.default_win = CONFIG_FB_S3C_DEFAULT_WINDOW, // 默认的window = 2
.swap = FB_SWAP_WORD | FB_SWAP_HWORD,
.lcd = &ek070tn93, // 记录LCD的硬件参数信息
.cfg_gpio= ek070tn93_cfg_gpio, // 配置 GPIO 寄存器
.backlight_on= ek070tn93_backlight_on, // 开启背光
.backlight_onoff = ek070tn93_backlight_off, // 关闭背光
.reset_lcd= ek070tn93_reset_lcd, // reset
};
配置 LCD 硬件参数的结构体如下:
static struct s3cfb_lcd ek070tn93 = {
.width = S5PV210_LCD_WIDTH, // lcd 宽度 800
.height = S5PV210_LCD_HEIGHT, // lcd 高度 480
.bpp = 32, // 像素位数
.freq = 60, // 帧率,这个是如何得来的?
// 时序
.timing = {
.h_fp = 210, // 行数据传输完成后到下一个行同步信号到来的延迟 HFPD+1
.h_bp = 38, // 行同步信号完成后到行数据传输前的延迟 HFPD+1
.h_sw = 10, // 行同步信号的脉宽 HSPW +1
.v_fp = 22, // 帧数据传输完成后到下一个帧同步信号到来的延迟 VFPD+1
.v_fpe = 1, // vertical front porch for even field, ?
.v_bp = 18, // 帧同步信号完成后到帧数据传输前的延迟 VBPD+1
.v_bpe = 1, // vertical back porch for even field, ?
.v_sw = 7, // 帧同步信号的脉宽 VSPW +1
},
//极性
.polarity = {
.rise_vclk = 0,
.inv_hsync = 1,
.inv_vsync = 1,
.inv_vden = 0,
},
};
对于 timing 中的相关参数参考LCD数据手册设置即可,手册内一般会有参考值,如下:
GPIO 寄存器配置函数如下:
static void ek070tn93_cfg_gpio(struct platform_device *pdev)
{
int i;
/* 将相应的IO引脚设置为LCD 相关功能 ,禁用上拉、下拉功能 */
for (i = 0; i < 8; i++) {
s3c_gpio_cfgpin(S5PV210_GPF0(i), S3C_GPIO_SFN(2));
s3c_gpio_setpull(S5PV210_GPF0(i), S3C_GPIO_PULL_NONE);
}
for (i = 0; i < 8; i++) {
s3c_gpio_cfgpin(S5PV210_GPF1(i), S3C_GPIO_SFN(2));
s3c_gpio_setpull(S5PV210_GPF1(i), S3C_GPIO_PULL_NONE);
}
for (i = 0; i < 8; i++) {
s3c_gpio_cfgpin(S5PV210_GPF2(i), S3C_GPIO_SFN(2));
s3c_gpio_setpull(S5PV210_GPF2(i), S3C_GPIO_PULL_NONE);
}
for (i = 0; i < 4; i++) {
s3c_gpio_cfgpin(S5PV210_GPF3(i), S3C_GPIO_SFN(2));
s3c_gpio_setpull(S5PV210_GPF3(i), S3C_GPIO_PULL_NONE);
}
/* mDNIe SEL: why we shall write 0x2 ? */
writel(0x2, S5P_MDNIE_SEL); // 这是做什么用的?注释掉也没有产生影响
/* 赋予最大的驱动能力 */
writel(0xffffffff, S5PV210_GPF0_BASE + 0xc);
writel(0xffffffff, S5PV210_GPF1_BASE + 0xc);
writel(0xffffffff, S5PV210_GPF2_BASE + 0xc);
writel(0x000000ff, S5PV210_GPF3_BASE + 0xc);
}
背光开启与关闭函数如下:
static int ek070tn93_backlight_on(struct platform_device *pdev)
{
s3c_gpio_cfgpin(S5PV210_GPD0(0), S3C_GPIO_OUTPUT); // 设置GPD0_0引脚为输出状态
s3c_gpio_setpull(S5PV210_GPD0(0), S3C_GPIO_PULL_UP); // 使能GPD0_0引脚上拉电阻
gpio_set_value(S5PV210_GPD0(0), 1); // 将GPD0_0设置成高电平
s3c_gpio_cfgpin(S5PV210_GPD0(0), S3C_GPIO_SFN(2)); // 将GPD0_0设置为TOUT_0功能
return 0;
}
static int ek070tn93_backlight_off(struct platform_device *pdev, int onoff)
{
s3c_gpio_cfgpin(S5PV210_GPD0(0), S3C_GPIO_OUTPUT);
s3c_gpio_setpull(S5PV210_GPD0(0), S3C_GPIO_PULL_DOWN);
gpio_set_value(S5PV210_GPD0(0), 0);
return 0;
}
lcd背光部分电路图如下:
在 arch/arm/mach-s5pv210/mach-s5pv210.c 中的 smdkv210_devices[] 数组中添加了 s3c_device_fb :
static struct platform_device *smdkv210_devices[] __initdata = {
..
&s3c_device_fb,
...
}
而在 smdkv210_machine_init 函数中将 smdkv210_devices 加入了platform ,并将ek070tn93设置为s3c_device_fb的platform data:
static void __init smdkc110_machine_init(void)
{
...
platform_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices));
...
s3cfb_set_platdata(&ek070tn93_fb_data);
...
}
二、platform driver
S5PV210 LCD platform_driver 的实例 s3cfb_driver 定义在 drivers/video/samsung/s3cfb.c中static struct platform_driver s3cfb_driver = {
.probe = s3cfb_probe,
.remove = __devexit_p(s3cfb_remove),
.driver = {
.name = S3CFB_NAME,// "s3cfb"
.owner = THIS_MODULE,
},
};
并在模块加载时注册,模块卸载时注销,如下:
static int __init s3cfb_register(void)
{
platform_driver_register(&s3cfb_driver);
return 0;
}
static void __exit s3cfb_unregister(void)
{
platform_driver_unregister(&s3cfb_driver);
}
module_init(s3cfb_register);
module_exit(s3cfb_unregister);
探测函数 s3cfb_probe如下:
static int __devinit s3cfb_probe(struct platform_device *pdev)
{
struct s3c_platform_fb *pdata;
struct s3cfb_global *fbdev;
struct resource *res;
int i, j, ret = 0;
fbdev = kzalloc(sizeof(struct s3cfb_global), GFP_KERNEL);
if (!fbdev) {
dev_err(&pdev->dev, "failed to allocate for " "global fb structure\n");
ret = -ENOMEM;
goto err_global;
}
fbdev->dev = &pdev->dev;// 获取平台设备s3c_device_fb
fbdev->regulator = regulator_get(&pdev->dev, "pd");
if (!fbdev->regulator) {
dev_err(fbdev->dev, "failed to get regulator\n");ret = -EINVAL;
goto err_regulator;
}
ret = regulator_enable(fbdev->regulator);
if (ret < 0) {dev_err(fbdev->dev, "failed to enable regulator\n");
ret = -EINVAL;
goto err_regulator;
}
if (!pdata) {
dev_err(fbdev->dev, "failed to get platform data\n");
ret = -EINVAL;
goto err_pdata;
}
fbdev->lcd = (struct s3cfb_lcd *)pdata->lcd;// 得到 ek070tn93
if (pdata->cfg_gpio)
pdata->cfg_gpio(pdev);// 即执行ek070tn93_fb_data 的 ek070tn93_cfg_gpio函数配置相关寄存器
if (pdata->clk_on)
pdata->clk_on(pdev, &fbdev->clock);// 即arch/arm/mach-s5pv210/devs.c 中s3cfb_clk_on
/* 获取平台设备的资源s3cfb_resource,这个IORESOURCE_MEM要与平台设备中定义的一致 */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(fbdev->dev, "failed to get io memory region\n");
ret = -EINVAL;
goto err_io;
}
/* 申请IO空间 */
res = request_mem_region(res->start, res->end - res->start + 1, pdev->name);
if (!res) {
dev_err(fbdev->dev, "failed to request io memory region\n");
ret = -EINVAL;
goto err_io;
}
/* 映射IO空间 */
fbdev->regs = ioremap(res->start, res->end - res->start + 1);
if (!fbdev->regs) {
dev_err(fbdev->dev, "failed to remap io region\n");
ret = -EINVAL;
goto err_mem;
}
s3cfb_set_vsync_interrupt(fbdev, 1);
s3cfb_set_global_interrupt(fbdev, 1);
/* 初始化lcd参数,包括lcd大小、时序、极性等。 */
s3cfb_init_global(fbdev);
/* 申请 framebuffer 显示缓冲区 */
if (s3cfb_alloc_framebuffer(fbdev)) {
ret = -ENOMEM;
goto err_alloc;
}
/* framebuffer 设备注册 */
if (s3cfb_register_framebuffer(fbdev)) {
ret = -EINVAL;
goto err_register;
}
s3cfb_set_clock(fbdev);
s3cfb_set_window(fbdev, pdata->default_win, 1);
s3cfb_display_on(fbdev);
/* 获取设备中断 */
fbdev->irq = platform_get_irq(pdev, 0);
if (request_irq(fbdev->irq, s3cfb_irq_frame, IRQF_SHARED,pdev->name, fbdev)) {
dev_err(fbdev->dev, "request_irq failed\n");
ret = -EINVAL;
goto err_irq;
}
#ifdef CONFIG_FB_S3C_LCD_INIT
if (pdata->backlight_on)
pdata->backlight_on(pdev);
if (!bootloaderfb && pdata->reset_lcd)
pdata->reset_lcd(pdev);
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
fbdev->early_suspend.suspend = s3cfb_early_suspend;
fbdev->early_suspend.resume = s3cfb_late_resume;
fbdev->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
register_early_suspend(&fbdev->early_suspend);
#endif
/* 添加到sysfs文件系统 */
ret = device_create_file(&(pdev->dev), &dev_attr_win_power);
if (ret < 0)
dev_err(fbdev->dev, "failed to add sysfs entries\n");
dev_info(fbdev->dev, "registered successfully\n");
#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
if (fb_prepare_logo( fbdev->fb[pdata->default_win], FB_ROTATE_UR)) {
printk("Start display and show logo\n");
/* Start display and show logo on boot */
fb_set_cmap(&fbdev->fb[pdata->default_win]->cmap, fbdev->fb[pdata->default_win]);
fb_show_logo(fbdev->fb[pdata->default_win], FB_ROTATE_UR);
}
#endif
mdelay(200);
if (pdata->backlight_on)
pdata->backlight_on(pdev);// 开启背光 对应ek070tn93_fb_data的 ek070tn93_backlight_on
return 0;
err_irq:
s3cfb_display_off(fbdev);s3cfb_set_window(fbdev, pdata->default_win, 0);
for (i = pdata->default_win;i < pdata->nr_wins + pdata->default_win; i++) {
j = i % pdata->nr_wins;
unregister_framebuffer(fbdev->fb[j]);
}
err_register:
for (i = 0; i < pdata->nr_wins; i++) {
if (i == pdata->default_win)
s3cfb_unmap_default_video_memory(fbdev->fb[i]);
framebuffer_release(fbdev->fb[i]);
}
kfree(fbdev->fb);
err_alloc:
iounmap(fbdev->regs);
err_mem:
release_mem_region(res->start, res->end - res->start + 1);
err_io:
pdata->clk_off(pdev, &fbdev->clock);
err_pdata:
regulator_disable(fbdev->regulator);
err_regulator:
kfree(fbdev);
err_global:
return ret;
}
以上是个人对S5PV210 LCD 的浅显认识,其中有好多不理解之处,还望高手指点。文中如有错误之处,还望批评指正。