Chinaunix首页 | 论坛 | 博客
  • 博客访问: 853706
  • 博文数量: 286
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1980
  • 用 户 组: 普通用户
  • 注册时间: 2014-05-04 16:41
文章分类

全部博文(286)

文章存档

2020年(2)

2018年(5)

2017年(95)

2016年(69)

2015年(15)

2014年(100)

我的朋友

分类: 嵌入式

2014-09-10 21:31:20

 Linux移植开发指南学习笔记(六) LCD背光驱动和显示驱动 (Fedora9)

1、添加LCD背光驱动
    到目前为止,我们一直都在命令行下移植,查看结果,LCD 屏幕上似乎总是如伸手不见五指的黑夜(但我的是亮的,成功添加背光驱动后可以来通过echo 0 > /dev/backlight 来开关背光),神秘而又无可奈何,从这一小节开始,我们就要打开这道神秘之门了。 
    在mini2440/micro2440开发板中,LCD 背光是通过CPU 的LCD_PWR 引脚来控制的,从原理图中可以看出,它对应于GPG4,如图 

    当LCD_PWR  输出为高电平“1”时,将打开背光;当输出为低电平“0”时,将关闭背光( 注意:里只是打开和关闭背光,而并没有背光亮度的调节作用)。

    现在,我们需要增加一个简单的背光驱动,以便能够通过软件便可简单的控制背光的开关。我们要达到的目的是:在命令终端通过向背光设备发送偶数比如“0”便可关闭背光,发送奇数比如“1”便可打开背光,这样使用起来就方便多了,而不需要专门的应用程序控制它,正如在用户手册中所描述的方法(2.5.10  控制LCD 的背光) : 
    提示:LCD 背光设备文件:/dev/backlight  
    在命令行种输入:echo 0 > /dev/backlight 可以关闭 LCD背光。 
    在命令行种输入:echo 1 > /dev/backlight 可以打开 LCD背光。 
    为了实现这点,我们在linux-2.6.32.2/drivers/video 目录增加一个mini2440_backlight.c文件,内容如下: 

; 以下头文件可能并不是每一个都必须的,但多余的并不会影响驱动程序的内容 
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
 
#include  
#include  
#include  
#include  
#include  
   
#include  
#include  
 
#undef DEBUG 
//#define DEBUG 
#ifdef DEBUG
#define DPRINTK(x...) {printk(__FUNCTI ON__"(%d): ",__LINE__);printk(##x);} 
#else 
#define DPRINTK(x...) (void)(0) 
#endif 
 
; 定义背光驱动的名称为backligh,将会出现在/dev/backlight 
#define DEVICE_NAME  "backlight" 
 
; 定义背光变量bl_state,以记录背光的开关状态 
static unsigned int bl_state; 
 
; 设置背光开关的函数,主要是翻转背光变量bl_state 
static inline void set_bl(int state) 

  bl_state = !!state;  // 翻转 bl_state变量 
 s3c2410_gpio_setpin(S3C2410_GPG(4), bl_state); // 把结果写入背光所用的寄存器 GPG4 

 
; 获取背光状态 
static inline unsigned int get_bl(void) 

 return bl_state; 

 
; 从应用程序读取参数,并传递到内核中 
static ssize_t dev_write(struct file *file, const char *buffer, size_t count, loff_t * ppos) 

  unsigned char ch; 
 int ret; 
  if (count == 0) { 
  return count; 
 } 
    ; 使用 copy_from_user 函数从用户层/ 应用层读取参数 
  ret = copy_from_user(&ch, buffe r, sizeof ch) ? -EFAULT : 0; 
  if (ret) { 
  return ret; 
 } 
 
    ch &= 0x01; // 判断奇数还是偶数 
set_bl(ch); // 设置背光状态 
   
 return count; 

 
; 把内核参数传递给用户层/ 应用层的读函数 
static ssize_t dev_read(struct file *filp, char *buffer, size_t count, loff_t *ppos) 

 int ret; 
  unsigned char str[] = {'0', '1' }; 
 
  if (count == 0) { 
  return 0; 
 } 
 
     ; 使用 copy_to_user 函数把内核参数传递到用户层/ 应用层 
  ret = copy_to_user(buffer, str + get_bl(), sizeof(unsigned char) ) ? -EFAULT : 0; 
  if (ret) { 
  return ret; 
 } 
 
 return sizeof(unsigned char); 

 
; 设备操作集 
static struct file_operations dev_fops = { 
 owner: THIS_MODULE, 
 read:dev_read,  
 write: dev_write, 
}; 
 
static struct miscdevice misc = { 
  .minor = MISC_DYNAMIC_MINOR, 
  .name = DEVICE_NAME, 
  .fops = &dev_fops, 
}; 
 
; 设备初始化,内核启动时就有效 
static int __init dev_init(void) 

 int ret; 
 
  ret = misc_register(&misc); 
 
 printk (DEVICE_NAME"\tinitialized\n"); 
 
     ; 初始化背光所用的端口 GPG4 为输出 
 s3c2410_gpio_cfgpin(S3C2410_GPG(4), S3C2410_GPIO_OUTPUT); 
     ; 启动内核时打开背光 
 set_bl(1); 
 return ret; 

 
 
static void __exit dev_exit(void) 

 misc_deregister(&misc); 

 
module_init(dev_init); // 注册背光驱动模块 
module_exit(dev_exit); // 卸载背光驱动模块 
MODULE_LICENSE("GPL"); 
MODULE_AUTHOR("FriendlyARM Inc."); 

然后把背光配置选项加入内核配置菜单,打开 linux-2.6.32.2/drivers/video/Kconfig ,在如图位置加入:

config FB_S3C2410_DEBUG 
        bool "S3C2410 lcd debug messages" 
        depends on FB_S3C2410 
        help 
                    Turn on debugging messages. Note that you can set/unset at run time 
          through sysfs 
 
# 在里加入MINI2440的背光驱动配置 
config BACKLIGHT_MINI2440 
                tristate "Backlight support for mini2440 from FriendlyARM" 
        depends on MACH_MINI2440 && FB_S3C2410 
        help 
          backlight driver for MINI2440 from FriendlyARM 
config FB_SM501 
        tristate "Silicon Motion SM501 framebuffer support" 
        depends on FB && MFD_SM501 
        select FB_CFB_FILLRECT 
        select FB_CFB_COPYAREA 
        select FB_CFB_IMAGEBLIT 

再打开linux-2.6.32.2/drivers/video/Makefile ,根据配置定义加入驱动目标文件,如图:

# the test framebuffer is last 
obj-$(CONFIG_FB_VIRTUAL)          += vfb.o 
 
#video output switch sysfs driver 
obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o 
 
obj-$(CONFIG_BACKLIGHT_MINI2440) += mini2440_backlight.o  

    这样,我们就在内核中移植好了mini2440 的背光驱动,在内核源代码根目录执行:
make menuconfig,依次选择如下子菜单:
Device Drivers  ---> 
   Graphics support  ---> 
      <*> Support for frame buffer devices  ---> 

    在这里,按空格选中我们刚刚加入的mini2440 配置项,然后退出保存内核配置菜单,
    在命令行执行:make zImage 
    将生成arch/arm/boot/zImage ,使用 supervivi 的“k”功能把它烧写到开发板中,通过指令来打开或关闭LCD,这说明我们已经点亮了背光,只不过LCD 驱动还有些问题,接着我们将会详细的介绍如何移植LCD驱动。
    同时在ls  /dev/back* 可以看到 /dev/backlight 


2、移植LCD显示驱动 

    Linux-2.6.32.2 内核已经支持S3C2440 的LCD 控制器驱动,但在此我们先介绍一下关于2440 LCD控制器以及驱动相关的LCD 的一些基础知识。 
    注意:在此我们只讨论TFT LCD,也就是真彩屏。 
    LCD 驱动中最关键的就是时钟频率( Clock frequency) 的设置,时钟频率设置不对,LCD的显示就会闪,或者根本没有显示。一般LCD的Datasheet 上会写有一个推荐的频率,比如mini2440 所用的统宝3.5”LCD ,在它的数据手册第13页,有这样一个表格: 
    可以看到,这里推荐的时钟频率是 6.39MHz ,近似于 6.4MHz,范围,是 5M-6.85MHz 。S3C2440 之LCD 控制器与此相关的设置为CLKVAL ,通过设置它,就可以在LCD 接口的VCLK引脚上产生LCD 所需要的时钟频率,那么CLKVAL 和VCLK 有何种关系呢?在2440手册(411页) 中,有这样一段描述: 
    The rate of VCLK signal depends on the CLKVAL field  in the LCDCON1 register.  Table 15-3 defines the relationship of VCLK and CLKVAL. The minimum value of CLKVAL is 0

    接下来,手册中提供了它们的数学关系公式: 
    VCLK(Hz) = HCLK/[(CLKVAL+1)x2] 
   

    那么HCLK 是多少呢?
    
我们的开发板运行于400Mhz,这个可以在bootloader 的源代
码头文件中看到,如图

   

    可见,FCLK:HCLK:PCLK = 1:4:8,因此得出HCLK=100Mhz,再根据上述公式得出
    CLKVAL 应为: 
    CLKVAL=HCLK/(VCLK*2) -1 
    CLKVAL = 100000000 / (6400000 * 2) - 1 = 6.8 
    选择最接近的整数值7,并把它写入LCDCON1:17-8(注意:我们实际使用的数值是8) ,由此产生的VCLK 频率实测为5.63Mhz 左右,它也是在5-6.85Mhz 之间的数值,如图:

新内核中的pixclock 参数 

    在以前较老的Linux 内核中,对于LCD 寄存器的设置都是这样直接填写CLKVAL 的,但Linux-2.6.32.2 内核却不再使用这样简单直观的方式,而是通过一个称为“pixclock”的参数进行调节,它的计算变的复杂和难以理解,我们不清楚Linux 内核中关于2440部分的移植为何改变成这样的方式,这有可能是为了和X86 体系中的设置保持一致的风格,下面我们根据实际的代码进行一些推导和说明,但推导结果和我们的实际设置是并不一致的,会有一些误差。
    提示:我们实际提供的pixclock 参数并不是按照以下的方式推导计算出的,而是先确定好CLKVAL 的数值,再反复尝试、猜测得到.
推算见手册

在内核中添加各种LCD类型的支持 

打开arch/arm/mach-s3c2440/mach-mini2440.c ,先删除之前的LCD设备平台代码,如下: 

/* LCD driver info */  
 
static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = { 
 
        .lcdcon5        = S3C2410_LCDCON5_FRM565 | 
 
 
                          S3C2410_LCDCON5_INVVLINE |  
                          S3C2410_LCDCON5_INVVFRAME |  
                          S3C2410_LCDCON5_PWREN | 
                          S3C2410_LCDCON5_HWSWP, 
 
        .type           = S3C2410_LC DCON1_TFT,  
 
        .width          = 240, 
        .height         = 320,  
 
        .pixclock       = 166667, /* HCLK 60 MHz, divisor 10 */ 
        .xres           = 240,  
        .yres           = 320,  
        .bpp            = 16,  
        .left_margin    = 20,  
        .right_margin   = 8,  
        .hsync_len      = 4,  
        .upper_margin   = 8,  
        .lower_margin   = 7,  
        .vsync_len      = 4,  
};  
static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {  
        .displays       = &smdk2440_l cd_cfg,  
        .num_displays   = 1,  
        .default_display = 0, 
 
#if 0  
        /* currently setup by downloader */ 
        .gpccon         = 0xaa940659,  
        .gpccon_mask    = 0xffffffff, 
        .gpcup          = 0x0000ffff, 
        .gpcup_mask      = 0xffffffff,  
        .gpdcon         = 0xaa84aaa0, 
        .gpdcon_mask    = 0xffffffff, 
        .gpdup          = 0x0000faff, 
        .gpdup_mask     = 0xffffffff, 
#endif  
 
        .lpcsel         = ((0xCE6) & ~7) | 1<<4, 
 
 
};  


再把友善之臂已经移植好的代码加入,如下: 
/* LCD driver info */ 
 
;NEC 3.5”LCD 的配置和参数设置 
#if defined(CONFIG_FB_S3C2410_N240320) 
 
#define LCD_WIDTH 240 
#define LCD_HEIGHT 320 
#define LCD_PIXCLOCK 100000 
 
#define LCD_RIGHT_MARGIN 36 
#define LCD_LEFT_MARGIN 19 
#define LCD_HSYNC_LEN 5 
 
#define LCD_UPPER_MARGIN 1 
#define LCD_LOWER_MARGIN 5 
#define LCD_VSYNC_LEN 1 
 
; 夏普8”LCD 的配置和参数设置 
#elif defined(CONFIG_FB_S3C2410_TFT640480) 
#define LCD_WIDTH 640 
#define LCD_HEIGHT 480 
#define LCD_PIXCLOCK 80000 
 
#define LCD_RIGHT_MARGIN 67   
#define LCD_LEFT_MARGIN 40 
#define LCD_HSYNC_LEN 31 
 
#define LCD_UPPER_MARGIN 25 
#define LCD_LOWER_MARGIN 5 
#define LCD_VSYNC_LEN 1 
 
; 统宝3.5”LCD 的配置和参数设置 
#elif defined(CONFIG_FB_S3C2410_T240320) 
#define LCD_WIDTH 240 
#define LCD_HEIGHT 320 
#define LCD_PIXCLOCK 146250//170000 
  
#define LCD_RIGHT_MARGIN 25 
#define LCD_LEFT_MARGIN 0 
#define LCD_HSYNC_LEN 4 
 
#define LCD_UPPER_MARGIN 1 
#define LCD_LOWER_MARGIN 4 
#define LCD_VSYNC_LEN 1 
 
; 群创7”LCD 的配置和参数设置 
#elif defined(CONFIG_FB_S3C2410_TFT800480) 
#define LCD_WIDTH 800 
#define LCD_HEIGHT 480 
#define LCD_PIXCLOCK 11463//40000 
 
#define LCD_RIGHT_MARGIN 67 
#define LCD_LEFT_MARGIN 40 
#define LCD_HSYNC_LEN 31 
 
#define LCD_UPPER_MARGIN 25 
#define LCD_LOWER_MARGIN 5 
#define LCD_VSYNC_LEN 1 
 
;LCD2VGA(分辨率为1024x768)模块的配置和参数设置 
#elif defined(CONFIG_FB_S3C2410_VGA1024768) 
#define LCD_WIDTH 1024 
#define LCD_HEIGHT 768 
#define LCD_PIXCLOCK 80000 
 
#define LCD_RIGHT_MARGIN 15 
#define LCD_LEFT_MARGIN 199 
#define LCD_HSYNC_LEN 15 
 
#define LCD_UPPER_MARGIN 1 
#define LCD_LOWER_MARGIN 1 
#define LCD_VSYNC_LEN 1 
#define LCD_CON5 (S3C2410_LCDCON5_FRM565 | S3C2410_LCDCON5_HWSWP) 
 
#endif 
 
  
#if defined (LCD_WIDTH) 
 
static struct s3c2410fb_display mini2440_lcd_cfg __initdata = { 
 
#if !defined (LCD_CON5) 
        .lcdcon5        = S3C2410_LCDCON5_FRM565 | 
                          S3C2410_LCDCON5_INVVLINE | 
                          S3C2410_LCDCON5_INVVFRAME | 
                          S3C2410_LCDCON5_PWREN | 
                          S3C2410_LCDCON5_HWSWP, 
#else 
        .lcdcon5        = LCD_CON5, 
#endif 
 
        .type           = S3C2410_LCDCON1_TFT, 
 
        .width          = LCD_WIDTH, 
        .height         = LCD_HEIGHT, 
 
        .pixclock       = LCD_PIXCLOCK, 
        .xres           = LCD_WIDTH, 
        .yres           = LCD_HEIGHT, 
        .bpp            = 16, 
        .left_margin    = LCD_LEFT_MARGIN + 1, 
        .right_margin   = LCD_RIGHT_MARGIN + 1, 
        .hsync_len      = LCD_HSYNC_LEN + 1, 
        .upper_margin   = LCD_UPPER_MARGIN + 1, 
        .lower_margin   = LCD_LOWER_MARGIN + 1, 
        .vsync_len      = LCD_VSYNC_LEN + 1, 
}; 
 
static struct s3c2410fb_mach_info mini2440_fb_info __initdata = { 
        .displays       = &mini2440_lcd_cfg, 
        .num_displays   = 1, 
        .default_display = 0, 
 
        .gpccon =       0xaa955699, 
        .gpccon_mask =  0xffc003cc, 
        .gpcup =        0x0000ffff, 
 
 
        .gpcup_mask =   0xffffffff, 
 
        .gpdcon =       0xaa95aaa1, 
        .gpdcon_mask =  0xffc0fff0, 
        .gpdup =        0x0000faff, 
        .gpdup_mask =   0xffffffff, 
 
 
        .lpcsel         = 0xf82, 
}; 
 
#endif 

    然后打开drivers/video/Kconfig ,在大概1935行加入以下配置信息: 
config FB_S3C2410_DEBUG 
  bool "S3C2410 lcd debug messages" 
  depends on FB_S3C2410 
 help 
      Turn on debugging messages. Note that you can set/unset at run time 
    through sysfs 
 
choice 
  prompt "LCD select" 
  depends on FB_S3C2410 
 help 
     S3C24x0 LCD size select 
 
config FB_S3C2410_T240320 
  boolean "3.5 inch 240X320 Toppoly LCD" 
  depends on FB_S3C2410 
 help 
      3.5 inch 240X320 Toppoly LCD 
 
config FB_S3C2410_N240320 
  boolean "3.5 inch 240X320 NEC LCD" 
  depends on FB_S3C2410 
 help 
        3.5 inch 240x320 NEC LCD 
 
config FB_S3C2410_TFT640480 
 
  boolean "8 inch 640X480 L80 LCD" 
  depends on FB_S3C2410 
  help 
     8 inch 640X480 LCD 
 
config FB_S3C2410_TFT800480 
  boolean "7 inch 800x480 TFT LCD" 
  depends on FB_S3C2410 
   help 
     7 inch 800x480 TFT LCD 
 
config FB_S3C2410_VGA1024768 
  boolean "VGA 1024x768" 
  depends on FB_S3C2410 
   help 
     VGA 1024x768 
 
endchoice 
 
config BACKLIGHT_MINI2440 
  tristate "Backlight support for mini2440 from FriendlyARM" 
  depends on MACH_MINI2440 && FB_S3C2410 
  help 
      backlight driver for MINI2440 from FriendlyARM 
 
config FB_SM501 
  tristate "Silicon Motion SM501 framebuffer support" 
  depends on FB && MFD_SM501 
 select FB_CFB_FILLRECT 
 select FB_CFB_COPYAREA 
 select FB_CFB_IMAGEBLIT 
    这样,我们就完成了LCD 驱动的移植,如果你需要加入其他型号的LCD驱动,也可以参照上面的方式复制即可,一般小尺寸的 pixclock 参数可以参考统宝 3.5” 的,超过 640x480分辨率的参数可以参考8”LCD 的,特别要注意你使用的LCD 的长宽也要修改。

配置内核并下载到开发板测试 

现在,我们在命令行输入:make menuconfig 进入内核配置,依次按下面的子菜单项选择: 
 
Device Drivers  ---> 
  Graphics support  ---> 
<*> Support for frame buffer devices    ---> 
  LCD select (3.5 inch  240X320 Toppoly LCD)  ---> 
会出现如图所示LCD 型号配置选项:

    按空格或者回车键选择我们需要的LCD 型号,然后退出保存内核配置。 
    在命令行执行: 
#make zImage 
    将会生成arch/arm/boot/zImage ,把它烧写到开发板中,一个小企鹅出现,屏幕由两半合为一个。但是触摸时没有反应。
阅读(1054) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~