S5PV210 LCD 驱动


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数据手册设置即可,手册内一般会有参考值,如下:

        S5PV210 LCD 驱动_第1张图片


对于 polarity 中的参数,要对比s5pv210芯片手册中LCD控制器的时序与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背光部分电路图如下:

               


VLED+ 接高电平,那么当VLED-为高电平时,LCD背光开,VLED-为低电平时LCD背光关。而VLED-接到了GPD0_0引脚,所以通过控制GPD0_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;

        }


        pdata = to_fb_plat(&pdev->dev);// 获取平台设备的platform data:ek070tn93_fb_data
        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 的浅显认识,其中有好多不理解之处,还望高手指点。文中如有错误之处,还望批评指正。

你可能感兴趣的:(Samsung)