zoukankan      html  css  js  c++  java
  • 驱动程序实例(五):LCD驱动程序分析(Samsung LCD)

    /************************************************************************************

    *本文为个人学习记录,如有错误,欢迎指正。

    *本文参考资料: 

    *        朱有鹏嵌入式课程

    *        https://blog.csdn.net/ultraman_hs/article/details/54987874

    ************************************************************************************/

    结合之前对Linux的framebuffer驱动框架的分析(详见Linux字符设备驱动框架(五):Linux内核的framebuffer驱动框架),本文对LCD的驱动程序进行了分析。

    本文基于九鼎科技的x210开发板的BSP进行分析,涉及到以下文件: 

    (1)drivers/video/samsung/s3cfb.c:驱动主体 ;
    (2)drivers/video/samsung/s3cfb_fimd6x.c:其中包含很多LCD硬件操作的函数;
    (2)arch/arm/mach-s5pv210/mach-x210.c:负责提供platform_device的 ;
    (3)arch/arm/plat-s5p/devs.c:为platform_device提供一些硬件描述信息的。

    Samsung编写的LCD设备驱动程序是通过Linux内核的platform总线驱动框架实现的,本文将分两大部分device和driver对其进行分析。

    1. device部分分析

    (1) struct platform_device s3c_device_lcd

    LCD的设备信息被嵌入在struct platform_device *smdkc110_devices[]数组中,在内核初始化时,内核将调用platform_add_devices()函数将smdkc110_devices[]数组中的所有设备注册至内核。

    //所在文件:/kernel/arch/arm/mach-s5pv210/mach-x210.c
    
    //smdkc110_devices[]数组中包含了开发板的所有设备的设备信息
    static struct platform_device *smdkc110_devices[] __initdata = 
    {
        ... ...
        &s3c_device_lcd,  //LCD设备信息
        ... ...
    }
    
    //开发板初始化函数
    static void __init smdkc110_machine_init(void)
    {
        ... ...
        platform_add_devices(smdkc110_devices, ARRAY_SIZE(smdkc110_devices)); //向内核注册smdkc110_devices[]中的所有设备
        ... ...
    #ifdef CONFIG_FB_S3C_LTE480WV
        s3cfb_set_platdata(&lte480wv_fb_data); //设置platform_device s3c_device_lcd的platform_data
    #endif  
    
    #ifdef CONFIG_FB_S3C_EK070TN93
        smdkv210_backlight_off();
        s3cfb_set_platdata(&ek070tn93_fb_data); //设置platform_device s3c_device_lcd的platform_data
    #endif
    }

    LCD的设备信息如下:

    //所在文件:/kernel/arch/arm/plat-s3c24xx/devs.c
    
    //LCD控制器的资源信息
    static struct resource s3c_lcd_resource[] = {
        [0] = {
            .start = S3C24XX_PA_LCD,                     //控制器IO端口开始地址(0xf8000000)
            .end   = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,//控制器IO端口结束地址(1M)
            .flags = IORESOURCE_MEM,
        },
        [1] = {
            .start = IRQ_LCD,//LCD中断
            .end   = IRQ_LCD,
            .flags = IORESOURCE_IRQ,
        }
    
    };
    
    struct platform_device s3c_device_lcd = 
    {
        .name          = "s3c2410-lcd",    //设备名称
        .id            = -1,
        .num_resources = ARRAY_SIZE(s3c_lcd_resource),
        .resource      = s3c_lcd_resource, //LCD设备需要的资源
        .dev           = {
            .dma_mask  = &s3c_device_lcd_dmamask,
            .coherent_dma_mask    = 0xffffffffUL
        }
    };

    (2)struct s3c_platform_fb与s3cfb_set_platdata

    在struct platform_device s3c_device_fb里并没有设置platdata的内容,而是通过s3cfb_set_platdata这个函数来单独设置platdata。因为内核中实现了多个LCD设备的platdata,用户可通过相应的宏定义来选择设置。它在启动时通过smdkc110_machine_init函数加载,保证platdata在内核启动时就已经设置好。

    在移植LCD驱动过程中,LCD的配置信息在其platdata中进行修改。

    通过内核源码及.config文件的分析,此处LCD的platdata如下:

    //所在文件:/kernel/arch/arm/mach-s5pv210/mach-x210.c
    //LCD的参数信息
    static struct s3cfb_lcd ek070tn93 = {
        .width = S5PV210_LCD_WIDTH,  //LCD分辨率参数,可修改LCD分辨率
        .height = S5PV210_LCD_HEIGHT,
        .bpp = 32,
        .freq = 60,
    
        .timing = {
            .h_fp    = 210,
            .h_bp    = 38,
            .h_sw    = 10,
            .v_fp    = 22,
            .v_fpe    = 1,
            .v_bp    = 18,
            .v_bpe    = 1,
            .v_sw    = 7,
        },
        .polarity = {
            .rise_vclk = 0,
            .inv_hsync = 1,
            .inv_vsync = 1,
            .inv_vden = 0,
        },
    };
    
    //LCD的platdata
    static struct s3c_platform_fb ek070tn93_fb_data __initdata = {
        .hw_ver    = 0x62,
        .nr_wins = 5,
        .default_win = CONFIG_FB_S3C_DEFAULT_WINDOW,
        .swap = FB_SWAP_WORD | FB_SWAP_HWORD,
    
        .lcd = &ek070tn93,                            //LCD参数信息
        .cfg_gpio    = ek070tn93_cfg_gpio,            //LCD的GPIO初始化函数
        .backlight_on    = ek070tn93_backlight_on,    //背光点亮函数
        .backlight_onoff    = ek070tn93_backlight_off,//背光熄灭函数
        .reset_lcd    = ek070tn93_reset_lcd,          //LCD复位函数
    };

    2. driver部分分析

    (1)驱动注册

    //所在文件:/kernel/drivers/video/samsung/s3cfb.c
    static struct platform_driver s3cfb_driver = {
        .probe = s3cfb_probe,
        .remove = __devexit_p(s3cfb_remove),
        .driver = {
               .name = S3CFB_NAME,
               .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);

    (2)probe函数分析

        static int __devinit s3cfb_probe(struct platform_device *pdev)
        {
            struct s3c_platform_fb *pdata;/*LCD的platdata,LCD屏配置信息结构体*/
            struct s3cfb_global *fbdev;   /*驱动程序全局变量结构体,主要作用是在驱动部分的2个文件(s3cfb.c和s3cfb_fimd6x.c)的函数中做数据传递用的*/
            struct resource *res;         /*用来保存从LCD平台设备中获取的LCD资源*/
            int i, j, ret = 0;
    
            fbdev = kzalloc(sizeof(struct s3cfb_global), GFP_KERNEL);
            
            fbdev->dev = &pdev->dev;
         ... ...
          /*LCD电源功耗管理*/
            fbdev->regulator = regulator_get(&pdev->dev, "pd");
            ret = regulator_enable(fbdev->regulator);
            
            /*1)获取LCD配置信息*/
         pdata = to_fb_plat(&pdev->dev);             //获取LCD的platdata
            fbdev->lcd = (struct s3cfb_lcd *)pdata->lcd;//获取LCD参数信息
    
         ... ...
            /*配置GPIO端口*/
            if (pdata->cfg_gpio)
                pdata->cfg_gpio(pdev);
    
            /*设置时钟参数*/
            if (pdata->clk_on)
                pdata->clk_on(pdev, &fbdev->clock);
    
            /*获取LCD平台设备所使用的IO端口资源,注意这个IORESOURCE_MEM标志和LCD平台设备定义中的一致*/
            res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    
            /*申请LCD IO端口所占用的IO空间(注意理解IO空间和内存空间的区别),request_mem_region定义在ioport.h中*/
            res = request_mem_region(res->start, res->end - res->start + 1, pdev->name);
          
    
            /*将LCD的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中
             * 注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作*/
            fbdev->regs = ioremap(res->start, res->end - res->start + 1);
            
        #ifdef CONFIG_FB_S3C_LTE480WV
            /*设置寄存器初始状态*/
            s3cfb_pre_init_para(fbdev);
        #endif
    
            /*设置gamma 值*/
            s3cfb_set_gamma(fbdev);
            /*设置VSYNC中断*/
            s3cfb_set_vsync_interrupt(fbdev, 1);
            /*设置全局中断*/
            s3cfb_set_global_interrupt(fbdev, 1);
            /*fb设备参数信息初始化*/
            s3cfb_init_global(fbdev);
    
            /*为framebuffer分配空间,进行内存映射,填充fb_info*/
            if (s3cfb_alloc_framebuffer(fbdev)) {
                ret = -ENOMEM;
                goto err_alloc;
            }
    
            /*注册fb设备到系统中*/
            if (s3cfb_register_framebuffer(fbdev)) //创建framebuffer设备
         {
                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
    ");
                ret = -EINVAL;
                goto err_irq;
            }
    
        #ifdef CONFIG_FB_S3C_LCD_INIT
            if (pdata->backlight_on)  //打开LCD的背光
                pdata->backlight_on(pdev);
    
            if (!bootloaderfb && pdata->reset_lcd)
                pdata->reset_lcd(pdev);
    
            if (pdata->lcd_on)
                pdata->lcd_on(pdev);
        #endif
    
    
            /*对设备文件系统的支持,创建fb设备文件*/
            ret = device_create_file(&(pdev->dev), &dev_attr_win_power);
            if (ret < 0)
                dev_err(fbdev->dev, "failed to add sysfs entries
    ");
    
            dev_info(fbdev->dev, "registered successfully
    ");
    
            /*显示开机logo*/
        #if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
            if (fb_prepare_logo( fbdev->fb[pdata->default_win], FB_ROTATE_UR)) //准备logo
         {
                printk("Start display and show logo
    ");
                /* 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);//显示logo
            }
        #endif
    
      ... ...
            return 0;
        }

    1)s3cfb_register_framebuffer

    s3cfb_register_framebuffer()函数中调用register_framebuffer()注册多个framebuffer设备,对应/dev/fb*,从而应用层可以设置多个虚拟屏幕达到更好的显示效果。

    2)logo显示

    Linux内核启动成功后,会在LCD中显示logo图标。Linux内核中提供了多个logo文件(/kernel/kernel/drivers/video/logo),内核调用fb_prepare_logo()函数来确定需要显示的logo,再调用fb_show_logo()函数显示logo。用户可通过宏定义来设置显示相应的logo图标。

    int fb_prepare_logo(struct fb_info *info, int rotate)
    {
        ... ...
        fb_logo.logo = fb_find_logo(depth);//查找需要显示的logo
        ... ... 
    }
    
    const struct linux_logo * __init_refok fb_find_logo(int depth)
    {
        const struct linux_logo *logo = NULL;
    
        if (nologo)
            return NULL;
    
        if (depth >= 1) {
    #ifdef CONFIG_LOGO_LINUX_MONO
            /* Generic Linux logo */
            logo = &logo_linux_mono;
    #endif
    #ifdef CONFIG_LOGO_SUPERH_MONO
            /* SuperH Linux logo */
            logo = &logo_superh_mono;
    #endif
        }
        
        if (depth >= 4) {
    #ifdef CONFIG_LOGO_LINUX_VGA16
            /* Generic Linux logo */
            logo = &logo_linux_vga16;
    #endif
        ... ...
        return logo;
    }
  • 相关阅读:
    第三周java学习总结
    第一周Java学习总结
    关闭窗体
    乱七八糟
    网页游戏资料
    timer控件的使用
    spread 签套循环改变颜色编号为颜色
    限制输入‘号的代码
    SQlcharindex命令
    限制输入类型
  • 原文地址:https://www.cnblogs.com/linfeng-learning/p/9486662.html
Copyright © 2011-2022 走看看