天天看点

S5PV210 LCD 驱动S5PV210 LCD DRIVER

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 驱动S5PV210 LCD DRIVER

对于 polarity 中的参数,要对比s5pv210芯片手册中LCD控制器的时序与LCD数据手册的时序。

GPIO 寄存器配置函数如下:

static void ek070tn93_cfg_gpio(struct platform_device *pdev)

{

        int i;

        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);

        }

        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背光部分电路图如下:

S5PV210 LCD 驱动S5PV210 LCD DRIVER
S5PV210 LCD 驱动S5PV210 LCD DRIVER

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

        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;

        }

        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;

        }

        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);

        s3cfb_init_global(fbdev);

        if (s3cfb_alloc_framebuffer(fbdev)) {

                ret = -ENOMEM;

                goto err_alloc;

        }

        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

        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");

                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 的浅显认识,其中有好多不理解之处,还望高手指点。文中如有错误之处,还望批评指正。