Linux内核LCD驱动分析与换屏方法(Tiny4412)
Posted DS小龙哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux内核LCD驱动分析与换屏方法(Tiny4412)相关的知识,希望对你有一定的参考价值。
Linux内核中换屏技术
21.5.1 u-boot中的参数bootargs实现换屏
在uboot中有一个 bootargs环境变量,这个参数就是传递数据给内核的。
对tiny4412提供的内核,可以通过修改 bootargs 实现驱动不同的LCD屏。
21.5.2 分析bootargs中的lcd参数
再启动uboot的时候会有如下的环境参数:
bootargs=noinitrd root=/dev/nfs nfsroot=192.168.10.106:/home/xyd/rootfs/ ip=192.168.10.123:192.168.10.106:192.168.10.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0 coherent_pool=2M lcd=S702 |
每个参数之间都是空格分开。
可以看到上面传递了2,启动内核之后就可以驱动S702屏。
在内核启动阶段,内核会根据lcd=S720这个信息,在注册平台设备/驱动前把LCD平台数据修改了。
21.5.3 分析在注册平台设备/驱动前修改lcd平台数据过程
首先从板级文件Mach-tiny4412.c中开始看。在这个文件中我们会看到一个机器初始化函数。
(1) 机器初始化函数中多平台设备注册代码段
代码段如下,在02299行可以看到:
static void __init smdk4x12_machine_init(void) …… //注册多平台设备 platform_add_devices(smdk4x12_devices, //包含LCD平台设备结构指针 ARRAY_SIZE(smdk4x12_devices));//数组元素个数,即平台设备个数 …… |
注册平台设备函数原型:
int platform_add_devices(struct platform_device **devs, int num) |
LCD平台设备层结构指针就是存放在 smdk4x12_devices的,以上函数注册包含LCD平台设备。所以,我们可以推出修改平台数据的地方应该在 platform_add_devices 函数前。
问题:在哪里修改lcd平台数据?
在platform_add_devices之前,有和lcd相关代码,下面的调用是在有触摸屏的时候才调用。我们只是把它作为例子来找到能识别s702屏的地方。
后边会讲到真正调用识别S702屏的地方。
代码如下:
static void __init smdk4x12_machine_init(void) …… #ifdef CONFIG_TOUCHSCREEN_FT5X0X//配置FT5X0X触摸屏的宏 struct s3cfb_lcd *lcd = tiny4412_get_lcd(); ft5x0x_pdata.screen_max_x = lcd->width; ft5x0x_pdata.screen_max_y = lcd->height; #endif …… platform_add_devices(smdk4x12_devices, ARRAY_SIZE(smdk4x12_devices)); …… |
其实真正的获得lcd参数结构是在tiny4412_fb_init_pdata函数里边调用,后面会有分析。
问题:现在我们分析如何通过bootargc传参获得lcd平台数据?
(2) 机器初始化函数中调用一个和lcd相关的tiny4412_get_lcd函数
SI里边调转,此函数在Tiny4412-lcds.c (linux-3.5\\arch\\arm\\mach-exynos)文件下。
代码如下:
struct s3cfb_lcd *tiny4412_get_lcd(void) return tiny4412_lcd_config[lcd_idx].lcd; |
这个函数只返回一个数组元素,此数组定义和初始化如下:
在文件Tiny4412-lcds.c (linux-3.5\\arch\\arm\\mach-exynos) 中。
代码如下:是一个结构体数组。
static struct char *name; struct s3cfb_lcd *lcd; int ctp; tiny4412_lcd_config[] = "HD700",&wxga_hd700, 1 , "S70",&wvga_s70, 1 , "S702",&wvga_s70, 3 , "W50",&wvga_w50, 0 , "W101",&wsvga_w101, 1 , "A97",&xga_a97, 0 , "LQ150",&xga_lq150, 1 , "L80",&vga_l80, 1 , "HD101",&wxga_hd101, 1 , "BP101",&wxga_bp101, 1 , "HDM",&hdmi_def, 0 ,/* Pls keep it at last */ ; |
所以我们就要关注lcd_idx值,在哪里设置这个值。
SI跳转得到如下:
static int lcd_idx = 0; //在本文件中定义为静态变量 |
(3) 在哪里得到lcd_idx的值
我们采用搜索的方式查找lcd_idx,可以找到以下:
Tiny4412-lcds.c (arch\\arm\\mach-exynos):lcd_idx = i; |
进入查看搜索语句所在代码:
static int __inittiny4412_setup_lcd(char *str) int i; …… /* 这里想通过名字查找对应的lcd配置参数,返回下标号lcd_idx */ for (i = 0; i < ARRAY_SIZE(tiny4412_lcd_config); i++) if (!strcascmp(tiny4412_lcd_config[i].name, str)) lcd_idx = i; break; __ret: printk("TINY4412: %s selected\\n", tiny4412_lcd_config[lcd_idx].name); return 0; |
这个是static静态函数,在本文件中有效。
这里我们可以知道tiny4412_lcd_config是一个数组,它就是存放所有支持的LCD屏信息。 通过比较数组成员name和tiny4412_setup_lcd参数str决定lcd_idx的值。
到这里就有疑问了:tiny4412_setup_lcd在哪里被调用?
(4) 补充知识:字符串比较函数。
int strcasecmp(const char *s1, const char *s2) int c1, c2; do c1 = tolower(*s1++); //转换小写 c2 = tolower(*s2++); //转换小写 while (c1 == c2 && c1 != 0); return c1 - c2; 这个函数是不区分大小写比较 |
21.5.4 几个重要结构
(1) 结构定义即初始化
这个结构体是当前板子所支持的lcd屏列表。
/* 定义lcd屏支持列表 */ static struct char *name; struct s3cfb_lcd *lcd; int ctp; tiny4412_lcd_config[] = "HD700",&wxga_hd700, 1 , "S70",&wvga_s70, 1 ,//我们当前板子上所使用的屏 "W50",&wvga_w50, 0 , "W101",&wsvga_w101, 1 , "A97",&xga_a97, 0 , "HDM",&hdmi_def, 0 ,/* Pls keep it at last */ ; |
里边struct s3cfb_lcd *lcd;结构成员是存放lcd屏的工作时序参数。
(2)结构体定义
如下:这个结构已经包含LCD驱动所需要的平台数据信息。
/* * @width:horizontal resolution---水平分辨率 * @height:vertical resolution---垂直分辨率 * @p_width:width of lcd in mm---屏物理尺寸(宽,mm单位) * @p_height:height of lcd in mm---屏物理尺寸(高,mm单位) * @bpp:bits per pixel * @freq:vframe frequency---刷屏频率 * @timing:timing values--时序参数 * @polarity:polarity settings---极性参数 */ struct s3cfb_lcd intwidth; intheight; intp_width; intp_height; intbpp; intfreq; structs3cfb_lcd_timing timing; structs3cfb_lcd_polarity polarity; ; |
(3) s3cfb_lcd_timing屏的时序参数结构
/* * @h_fp:horizontal front porch * @h_bp:horizontal back porch * @h_sw:horizontal sync width * @v_fp:vertical front porch * @v_fpe:vertical front porch for even field * @v_bp:vertical back porch * @v_bpe:vertical back porch for even field */ struct s3cfb_lcd_timing inth_fp; inth_bp; inth_sw; intv_fp; intv_fpe; intv_bp; intv_bpe; intv_sw; ; |
(4) 屏的时序极性配置
/* * @rise_vclk:if 1, video data is fetched at rising edge * @inv_hsync:if HSYNC polarity is inversed * @inv_vsync:if VSYNC polarity is inversed * @inv_vden:if VDEN polarity is inversed */ struct s3cfb_lcd_polarity intrise_vclk; intinv_hsync; intinv_vsync; intinv_vden; ; |
21.5.5 以bootargc传入lcd=S70为例子填充s3cfb_lcd结构
定义struct s3cfb_lcd结构变量wvga_s70 ,并初始化屏参数;
在文件Tiny4412-lcds.c (arch\\arm\\Mach-exynos)下实现。
//以下信息是根据LCD屏的手册得到的 static struct s3cfb_lcdwvga_s70= .width = 800, .height = 480, .p_width = 155, .p_height = 93, .bpp = 24, .freq = 63,
|