tiny4412 lcd驱动问题记录

来源:互联网 发布:教师网络研修社区定义 编辑:程序博客网 时间:2024/05/16 09:01

fbmem.c文件提供了framebuffer驱动程序的通用文件操作接口,自定义的framebuffer驱动程序可以使用fbmem.c中提供默认的接口。用EXPORT_SYMBOL导出到其他文件中应用

s3c-fb.c是针对的三星开发板的lcd驱动文件接口(s3c_fb_probe等)。

一下都是在友善tiny4412的linux3.5内核中

移植lcd平台资源

1.需要添加lcd的平台设备信息

//初始化相关平台信息

tiny4412_fb_init_pdata(&smdk4x12_lcd0_pdata);

//需要添加lcd的平台设备信息

s5p_fimd0_set_platdata(&smdk4x12_lcd0_pdata);

void __init s5p_fimd0_set_platdata(struct s3c_fb_platdata *pd)
{
s3c_set_platdata(pd, sizeof(struct s3c_fb_platdata),
&s5p_device_fimd0);
}

//lcd 所占用io口及终端资源。

static struct resource s5p_fimd0_resource[] = {
[0] = DEFINE_RES_MEM(S5P_PA_FIMD0, SZ_32K),
[1] = DEFINE_RES_IRQ(IRQ_FIMD0_VSYNC),
[2] = DEFINE_RES_IRQ(IRQ_FIMD0_FIFO),
[3] = DEFINE_RES_IRQ(IRQ_FIMD0_SYSTEM),
};
struct platform_device s5p_device_fimd0 = {
.name = "s5p-fb",
.id = 0,
.num_resources= ARRAY_SIZE(s5p_fimd0_resource),
.resource = s5p_fimd0_resource,
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask= DMA_BIT_MASK(32),
},
};

2.同时把struct platform_device s5p_device_fimd0 = {
.name = "s5p-fb",
.id = 0,
.num_resources= ARRAY_SIZE(s5p_fimd0_resource),
.resource = s5p_fimd0_resource,
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask= DMA_BIT_MASK(32),
},
};

放到struct platform_device *smdk4x12_device【】结构体中,接着调用platform_add_devices将lcd平台设备注册到内核。


lcd驱动分析思路::::

1. 和其它内核代码类似。

     显示驱动的分析都是由 drivers/video/fbmem.c开始,fbmem.c是显示驱动的抽象,实际只是一个框架性的东西。

     fbmem_init 中实现了一个字符设备驱动,并创建了class,但是没有生成设备文件。

    这个字符设备驱动的file_operations里面的函数,实质上都是从struct fb_info *registered_fb[FB_MAX]   这个

    fb_info的结构体数组中去调用 fb_ops 这个结构体中函数指针。数组下标为次设备号。那么这个结构体是如何赋值的

     呢?

    fbmem.c里定义 register_framebuffer这个函数。真正的显示设备都是调用这个函数来给registered_fb这个数组赋值,

   然后再去创建设备文件。


2.  我们搜索register_framebuffer这个文件,有如下几处:

     drivers/gpu/drm/drm_fb_helper.c

     drivers/video/s3c-fb.c


3. 我们先来看看s3c-fb.c

    这个文件注册了一个平台总线设备驱动程序,在其probe函数中调用 register_framebuffer。

    那么这个驱动的probe的函数什么时候调用呢?

    接下来我们看一下内核中实现平台总线驱动代码,注意,平台总线驱动是内核实现的。


4. drivers/base/platform.c

    主要看platform_match这个函数,也就是平台总线设备驱动和平台总线设备是如何匹配的,知道了匹配规则,我们

    就知道如何去需找对应的平台设备了。

    先看这句话

    if (pdrv->id_table)
        return platform_match_id(pdrv->id_table, pdev) != NULL;

   return (strcmp(pdev->name, drv->name) == 0);

    如何平台总线设备驱动中有id_table的话,那么调用platform_match_id这个函数。

   我们再看一下platform_match_id函数做个什么。

   while (id->name[0]) {
        if (strcmp(pdev->name, id->name) == 0) {
            pdev->id_entry = id;
            return id;
        }
        id++;
    }

    很显然,就是拿平台总线设备的name去挨个比较平台总线设备驱动的id_table,匹配成功测返回id。

    如果没有匹配成功,则再去比较平台总线设备的名称和平台总线驱动的名称。也就是

   return (strcmp(pdev->name, drv->name) == 0);  这句。

   一旦匹配成功,那么内核会自动调用平台总线设备驱动的probe函数。


5.  那么接下来,我们就看看s3c-fb.c这个文件里实现的平台总线设备驱动程序的name和id_table

    static struct platform_driver s3c_fb_driver = {
    .probe        = s3c_fb_probe,
    .remove        = s3c_fb_remove,
    .id_table    = s3c_fb_driver_ids,
    .driver        = {
        .name    = "s3c-fb",
        .owner    = THIS_MODULE,
        .pm    = &s3cfb_pm_ops,
    },
};

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,
    },
    {},
};

  有了第4点的分析,我们可以搜索上面红字部分来查找对应的平台总线设备了。


6.  搜索"s3c-fb",找到了

   arch/arm/plat-samsung/devs.c这个文件

    搜索"exynos4-fb",找到了

    arch/arm/mach-exynos/common.c

      其它name,我们应该不用理会,都是其它soc名称。


   在devs.c里定义了

   struct platform_device s3c_device_fb = {
    .name        = "s3c-fb",
    .id        = -1,
    .num_resources    = ARRAY_SIZE(s3c_fb_resource),
    .resource    = s3c_fb_resource,
    .dev        = {
        .dma_mask        = &samsung_device_dma_mask,
        .coherent_dma_mask    = DMA_BIT_MASK(32),
    },
};  

   在common.c里是这句 s5p_fb_setname(0,"exynos4-fb");

   展开实际是这样s5p_device_fimd0.name = name;

   struct platform_device s5p_device_fimd0 = {
    .name        = "s5p-fb",    这块被改为了 "exynos4-fb"
    .id        = 0,
    .num_resources    = ARRAY_SIZE(s5p_fimd0_resource),
    .resource    = s5p_fimd0_resource,
    .dev        = {
        .dma_mask        = &samsung_device_dma_mask,
        .coherent_dma_mask    = DMA_BIT_MASK(32),
    },
};

    从以上分析,实际定义了两个设备,"s3c-fb","exynos4-fb"。


7. 现在,找到了,平台总线的设备和驱动后,我们要做的主要事情就是去修改lcd的各种参数,主要是fb_info结构体

    的fb_var_screeninfo结构体,这里面记录了lcd的主要9个参数。

   行前肩,行后肩,行同步信号脉宽,帧前肩,帧后肩,帧同步信号脉宽,像素时钟频率,x轴像素点,y轴像素点。

    分析了s3c_fb.c文件后,发现是这句来赋值,fb_videomode_to_var(&fbinfo->var, &initmode);

    经过再次分析后,实际数据是来源于平台总线设备中,pd = pdev->dev.platform_data;


8.  那么我们再次回到devs.c这个文件,因为上面两个平台总线设备均定义在次文件中。

    但是查看s3c_device_fb和s5p_device_fimd0这两个平台设备结构体后,没有发现platform_data。那么一定是后面

   专门有赋值的地方。查找后,发现

   void __init s3c_fb_set_platdata(struct s3c_fb_platdata *pd)
{
    s3c_set_platdata(pd, sizeof(struct s3c_fb_platdata),
             &s3c_device_fb);
}

void __init s5p_fimd0_set_platdata(struct s3c_fb_platdata *pd)
{
    s3c_set_platdata(pd, sizeof(struct s3c_fb_platdata),
             &s5p_device_fimd0);
}

   经过搜索发现,s5p_fimd0_set_platdata在 下面文件中调用,

  arch/arm/mach-exynos/mach-nanopc-t1.c

   arch/arm/mach-exynos/mach-smdk4x12.c  这个文件应该没用

   s3c_fb_set_platdata没有被任何地方调用,那么s3c_device_fb这个设备应该没有用处,我认为应该去掉。


9. 接下来我们主要分析s5p_device_fimd0这个设备。

    在mach-nano-t1.c 这句 两句

    nanopc_fb_init_pdata(&nanopc_fb_pdata);

    s5p_fimd0_set_platdata(&nanopc_fb_pdata);

    实际真正的数据是在nanopc_fb_pdata中,并且在nanopc_fb_init_pdata中得到的值。

    再看nanopc_fb_init_pdata这个函数,发现以下几句

    lcd = tiny4412_get_lcd();

     mode->left_margin    = lcd->timing.h_bp;
    mode->right_margin    = lcd->timing.h_fp;
    mode->upper_margin    = lcd->timing.v_bp;
    mode->lower_margin    = lcd->timing.v_fp;
    mode->hsync_len        = lcd->timing.h_sw;
    mode->vsync_len        = lcd->timing.v_sw;
    mode->xres            = lcd->width;
    mode->yres            = lcd->height;

    数据实际来源于tiny4412_get_lcd()。

    tiny4412_get_lcd定义在

    arch/arm/mach-exynos/tiny4412-lcds.c

     到此,我们终于找到各种屏的参数定义,并且还有hdmi参数的定义。

    
   

/home/yangjia/samba/linux-3.5/arch/arm/mach-exynos/mach-tiny4412.c  。_initdata 定义在此文件中。平台驱动都重要在这里注册。平台驱动是匹配名字的。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. static struct platform_device *smdk4x12_devices[] __initdata = {  
  2. #ifdef CONFIG_EXYNOS4_DEV_DWMCI  
  3.     &exynos_device_dwmci,  
  4. #endif  
  5.     &s3c_device_hsmmc2,  
  6.     &s3c_device_hsmmc3,  
  7.     &wm8994_fixed_voltage0,  
  8.     &wm8994_fixed_voltage1,  
  9.     &wm8994_fixed_voltage2,  
  10.     &s3c_device_i2c0,  
  11.     &s3c_device_i2c1,  
  12.     &s3c_device_i2c2,  
  13.     &s3c_device_i2c3,  
  14. #ifdef CONFIG_VIDEO_M5MOLS  
  15.     &s3c_device_i2c4,  
  16. #endif  
  17.     &s3c_device_i2c7,  
  18.     &s3c_device_adc,  
  19.     &s3c_device_rtc,  
  20.     &s3c_device_wdt,  
  21. #ifdef CONFIG_TINY4412_BUZZER  
  22.     &s3c_device_timer[0],  
  23. #endif  
  24. #ifdef CONFIG_VIDEO_EXYNOS_FIMC_LITE  
  25.     &exynos_device_flite0,  
  26.     &exynos_device_flite1,  
  27. #endif  
  28.     &s5p_device_mipi_csis0,  
  29.     &s5p_device_mipi_csis1,  
  30.     &s5p_device_fimc0,  
  31.     &s5p_device_fimc1,  
  32.     &s5p_device_fimc2,  
  33.     &s5p_device_fimc3,  
  34.     &s5p_device_fimc_md,  
  35.     <span style="color:#cc0000;">&s5p_device_fimd0,</span>  

在/home/yangjia/samba/linux-3.5/arch/arm/plat-samsung/devs.c 中定义有 s3c_device_fb 和s5p_device_fimd0 。S5P_PA_FIMD0是VIDCON0的地址,可以查三星的用户手册,地址为 0x11C00000。s5p-fb这个名字没有在后面匹配。


[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #ifdef CONFIG_S5P_DEV_FIMD0  
  2. static struct resource s5p_fimd0_resource[] = {  
  3.     [0] = DEFINE_RES_MEM(S5P_PA_FIMD0, SZ_32K),  
  4.     [1] = DEFINE_RES_IRQ(IRQ_FIMD0_VSYNC),  
  5.     [2] = DEFINE_RES_IRQ(IRQ_FIMD0_FIFO),  
  6.     [3] = DEFINE_RES_IRQ(IRQ_FIMD0_SYSTEM),  
  7. };  
  8.   
  9. struct platform_device s5p_device_fimd0 = {  
  10.     .name       = "s5p-fb",  
  11.     .id     = 0,  
  12.     .num_resources  = ARRAY_SIZE(s5p_fimd0_resource),  
  13.     .resource   = s5p_fimd0_resource,  
  14.     .dev        = {  
  15.         .dma_mask       = &samsung_device_dma_mask,  
  16.         .coherent_dma_mask  = DMA_BIT_MASK(32),  
  17.     },  
  18. };  
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #ifdef CONFIG_S3C_DEV_FB  
  2. static struct resource s3c_fb_resource[] = {  
  3.     [0] = DEFINE_RES_MEM(S3C_PA_FB, SZ_16K),  
  4.     [1] = DEFINE_RES_IRQ(IRQ_LCD_VSYNC),  
  5.     [2] = DEFINE_RES_IRQ(IRQ_LCD_FIFO),  
  6.     [3] = DEFINE_RES_IRQ(IRQ_LCD_SYSTEM),  
  7. };  
  8.   
  9. struct platform_device s3c_device_fb = {  
  10.     .name       = "s3c-fb",  
  11.     .id     = -1,  
  12.     .num_resources  = ARRAY_SIZE(s3c_fb_resource),  
  13.     .resource   = s3c_fb_resource,  
  14.     .dev        = {  
  15.         .dma_mask       = &samsung_device_dma_mask,  
  16.         .coherent_dma_mask  = DMA_BIT_MASK(32),  
  17.     },  
  18. };  
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #ifdef CONFIG_S5P_DEV_FIMD0  
  2. static struct resource s5p_fimd0_resource[] = {  
  3.     [0] = DEFINE_RES_MEM(<span style="color:#cc0000;">S5P_PA_FIMD0</span>, SZ_32K),  
  4.     [1] = DEFINE_RES_IRQ(IRQ_FIMD0_VSYNC),  
  5.     [2] = DEFINE_RES_IRQ(IRQ_FIMD0_FIFO),  
  6.     [3] = DEFINE_RES_IRQ(IRQ_FIMD0_SYSTEM),  
  7. };  

/home/yangjia/samba/linux-3.5/arch/arm/mach-exynos/mach-tiny4412.c  

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. static void __init smdk4x12_machine_init(void)  
  2. {  
  3. #ifdef CONFIG_TOUCHSCREEN_FT5X0X  
  4.     struct s3cfb_lcd *lcd = tiny4412_get_lcd();  
  5.     ft5x0x_pdata.screen_max_x = lcd->width;  
  6.     ft5x0x_pdata.screen_max_y = lcd->height;  

/home/yangjia/samba/linux-3.5/arch/arm/mach-exynos/tiny4412-lcds.c 在这个文件中对lcd的参数进行配置。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. static struct {  
  2.     char *name;  
  3.     struct s3cfb_lcd *lcd;  
  4.     int ctp;  
  5. } tiny4412_lcd_config[] = {  
  6.     { "HD700",  &wxga_hd700, 1 },  
  7.     { "S70",    &wvga_s70,   1 },  
  8.     { "W50",    &wvga_w50,   0 },  
  9.     { "W101",   &wsvga_w101, 1 },  
  10.     { "A97",    &xga_a97,    0 },  
  11.     { "HDM",    &hdmi_def,   0 },   /* Pls keep it at last */  
  12. };  
  13.   
  14. static int lcd_idx = 0;  
  15.   
  16. static int __init tiny4412_setup_lcd(char *str)  
  17. {  
  18.     int i;  
  19.   
  20.     if (!strncasecmp("HDMI", str, 4)) {  
  21.         struct hdmi_config *cfg = &tiny4412_hdmi_config[0];  
  22.         struct s3cfb_lcd *lcd;  
  23.   
  24.         lcd_idx = ARRAY_SIZE(tiny4412_lcd_config) - 1;  
  25.         lcd = tiny4412_lcd_config[lcd_idx].lcd;  
  26.   
  27.         for (i = 0; i < ARRAY_SIZE(tiny4412_hdmi_config); i++, cfg++) {  
  28.             if (!strcasecmp(cfg->name, str)) {  
  29.                 lcd->width = cfg->width;  
  30.                 lcd->height = cfg->height;  
  31.                 goto __ret;  
  32.             }  
  33.         }  
  34.     }  
  35.   
  36.     for (i = 0; i < ARRAY_SIZE(tiny4412_lcd_config); i++) {  
  37.         if (!strcasecmp(tiny4412_lcd_config[i].name, str)) {  
  38.             lcd_idx = i;  
  39.             break;  
  40.         }  
  41.     }  
  42.   
  43. __ret:  
  44.     printk("TINY4412: %s selected\n", tiny4412_lcd_config[lcd_idx].name);  
  45.     return 0;  
  46. }  
  47. early_param("lcd", tiny4412_setup_lcd);  

0 0
原创粉丝点击