S3C2440 Linux驱动移植——LCD

来源:互联网 发布:深圳多迪网络靠谱吗 编辑:程序博客网 时间:2024/05/19 13:14

转自 http://blog.csdn.net/yj4231/article/details/7878762

PC主机:Ubuntu 10.4 和redhat 9.0

目标板:TQ2440开发板 Linux内核:2.6.30

屏幕型号:WXCAT35-TG3#001F 分辨率: 320X240


本文将介绍如何移植LCD设备。

在移植前,先配置下内核,将LCD设备编译进内核。


1.移植

移植LCD设置只须修改位于arch/arm/mach-s3c2440/mach-smdk2440.c中的两个结构体的数据。

1.1 s3c2410fb_display结构

修改后的内容如下:

[cpp] view plaincopy
  1. /* LCD driver info */  
  2.   
  3. static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {  
  4.   
  5.     .lcdcon5    = S3C2410_LCDCON5_FRM565 |  
  6.               S3C2410_LCDCON5_INVVLINE |  
  7.               S3C2410_LCDCON5_INVVFRAME |  
  8.               S3C2410_LCDCON5_PWREN |  
  9.               S3C2410_LCDCON5_HWSWP,  
  10.   
  11.     .type       = S3C2410_LCDCON1_TFT,  
  12.   
  13.     .width      = 320,//240,  
  14.     .height     = 240,//320,  
  15.   
  16.     .pixclock   = 156250,//166667, /* HCLK 60 MHz, divisor 10 */  
  17.     .xres       = 320,//240,  
  18.     .yres       = 240,//320,  
  19.     .bpp        = 16,  
  20.     .left_margin    = 20,  
  21.     .right_margin   = 38,//8,  
  22.     .hsync_len  = 30,//4,  
  23.     .upper_margin   = 15,//8,  
  24.     .lower_margin   = 12,//7,  
  25.     .vsync_len  = 3,//4,  
  26. };  

上面的参数是如何修改的呢?我们来看下。

type表示显示模式,这里为TFT模式。

width和height表示屏幕的分辨率,我的分辨率是320X240。

xres和yres分别等于width和height。

bpp表示所每个像素点位数,这里使用16位。

left_margin,right_margin,hsync_len,upper_margin,lower_margin,vsync_len这六个参数的值由LCD的手册给出。下图为LCD中的参数:


在这里,我给出上面6个参数和LCD手册中数据的对应关系:

       .left_margin    =            Hsync front porch        =    20

       .right_margin  =            Hsync back porch        =     38

       .hsync_len      =           Hsync pulse width        =     30

       .upper_margin       =      Vsync  back porch       =    15

       .lower_margin       =      Vsync front porch        =     12

       .vsync_len      =           Vsync pulse width        =     3

 

pixclock的值是用来计算CLKVAL的。在S3C2440的datasheet中,CLKVAL的计算公式为:

CLKVAL = HCLK / VCLK / 2 -1,而VCLK即为上面图中的Dclk,值为6.4MHz。

接着我们看下驱动程序是如何计算CLKVAL的。

[cpp] view plaincopy
  1. /* s3c2410fb_activate_var 
  2.  * 
  3.  * activate (set) the controller from the given framebuffer 
  4.  * information 
  5.  */  
  6. static void s3c2410fb_activate_var(struct fb_info *info)  
  7. {  
  8.     struct s3c2410fb_info *fbi = info->par;  
  9.     void __iomem *regs = fbi->io;  
  10.     int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/  
  11.     struct fb_var_screeninfo *var = &info->var;  
  12.     int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;  
  13.   
  14.     dprintk("%s: var->xres  = %d\n", __func__, var->xres);  
  15.     dprintk("%s: var->yres  = %d\n", __func__, var->yres);  
  16.     dprintk("%s: var->bpp   = %d\n", __func__, var->bits_per_pixel);  
  17.   
  18.     if (type == S3C2410_LCDCON1_TFT) {  
  19.         s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/  
  20.         --clkdiv;  
  21.         if (clkdiv < 0)  
  22.             clkdiv = 0;  
  23.     } else {  
  24.         s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);  
  25.         if (clkdiv < 2)  
  26.             clkdiv = 2;  
  27.     }  
  28.   
  29.     fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/  
  30.   
  31.     /* write new registers */  
  32.   
  33.     dprintk("new register set:\n");  
  34.     dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);  
  35.     dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);  
  36.     dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);  
  37.     dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);  
  38.     dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);  
  39.     /*把计算好的值填入LCD控制器中*/  
  40.     writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,     
  41.         regs + S3C2410_LCDCON1);                        /*仍然禁止LCD*/  
  42.     writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);  
  43.     writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);  
  44.     writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);  
  45.     writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);  
  46.   
  47.     /* set lcd address pointers */  
  48.     s3c2410fb_set_lcdaddr(info);                        /*设置LCD帧缓冲起始地址*/  
  49.   
  50.     fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,  
  51.     writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);   /*使能LCD*/  
  52. }  
  53. static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,  
  54.                       unsigned long pixclk)  
  55. {  
  56.     unsigned long clk = clk_get_rate(fbi->clk);         /*获取当前时钟频率(Hz)*/  
  57.     unsigned long long div;  
  58.   
  59.     /* pixclk is in picoseconds, our clock is in Hz 
  60.      * 
  61.      * Hz -> picoseconds is / 10^-12 
  62.      */  
  63.   
  64.     div = (unsigned long long)clk * pixclk;  
  65.     div >>= 12;            /* div / 2^12 */  
  66.     do_div(div, 625 * 625UL * 625); /* div / 5^12 */  
  67.   
  68.     dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);  
  69.     return div;  
  70. }  
首先pixclock作为参数传递给了s3c2410fb_calc_pixclk函数,当该函数执行完以后clkdiv  = clk x pixclk  / 10^12 / 2。

随后由于是采用TFT模式,将clkdiv-1。最后得:

clkdiv  = clk X pixclk  / 10^12 / 2 - 1,这里的clk即为HCLK,LCD模块使用HCLK作为时钟源。

为方便观察,将前面datasheet中的计算公式复制在此:CLKVAL = HCLK / VCLK / 2 -1。

我们可以看出1/VCLK = pixclk / 10^12,也就是说pixclk = 10^12 / VCLK。 我们已经知道VCLK=Dclk =6.4MHz

因此,pixclk=156250。

其实在内核的参考文档中有这样一段话:

The speed at which the electron beam paints the pixels is determined by the
dotclock in the graphics board. For a dotclock of e.g. 28.37516 MHz (millions
of cycles per second), each pixel is 35242 ps (picoseconds) long:

    1/(28.37516E6 Hz) = 35.242E-9 s

也就是说VCLK的倒数,再乘10^12即为pixclk。picoseconds单位表示微微秒,即10^12。

最后一个参数lcdcon5,它是寄存器LCDCON5的配置信息。

S3C2410_LCDCON5_FRM565表示使用565格式。

S3C2410_LCDCON5_PWREN表示PWREN管脚输出信号使能。

S3C2410_LCDCON5_INVVLINE 和S3C2410_LCDCON5_INVVFRAME 表示反转VLINE/HSYNC和VFRAME/VSYNC两个信号线,这个可以通过对比LCD手册和S3C2440的datasheet来得出。

S3C2410_LCDCON5_HWSWP:由于使用小端模式,需设置半字交换。

1.2 s3c2410fb_mach_info结构

修改后的内容如下:

[cpp] view plaincopy
  1. static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {  
  2.     .displays   = &smdk2440_lcd_cfg,  
  3.     .num_displays   = 1,  
  4.     .default_display = 0,  
  5.   
  6. #if 0  
  7.     /* currently setup by downloader */  
  8.     .gpccon     = 0xaa940659,  
  9.     .gpccon_mask    = 0xffffffff,  
  10.     .gpcup      = 0x0000ffff,  
  11.     .gpcup_mask = 0xffffffff,  
  12.     .gpdcon     = 0xaa84aaa0,  
  13.     .gpdcon_mask    = 0xffffffff,  
  14.     .gpdup      = 0x0000faff,  
  15.     .gpdup_mask = 0xffffffff,  
  16. #endif  
  17. //no  
  18. //  .lpcsel     = ((0xCE6) & ~7) | 1<<4,  
  19. };  

做出的唯一修改就是:禁用lpsel,因为如果不是使用三星LPC3600/LCC3600 LCD,必须禁止LPC3600/LCC3600模式(写入0到TCONSEL)。

num_displays 表示有几个LCD设备。

default_display表示这是第几个LCD设备。


2.  测试

在修改过上述两个数据结构之后,直接编译内核,然后下载到开发板上。

可以执行命令fbset,输出如下:

mode "320x240-58"

        # D: 6.400 MHz, H: 15.686 kHz, V: 58.097 Hz
        geometry 320 240 320 240 16
        timings 156250 20 38 15 12 30 3
        accel false
        rgba 5/11,6/5,5/0,0/0
endmode

上述中的数值都是我们设置的参数。

另外,可以通过下列测试程序进行测试:

[cpp] view plaincopy
  1. #include <linux/fb.h>  
  2. #include <unistd.h>  
  3. #include <sys/mman.h>  
  4. #include <stdio.h>  
  5. #include <fcntl.h>  
  6. //#include <syswait.h>  
  7.   
  8. int main()  
  9. {  
  10.     int fd, retval, i;  
  11.     struct fb_var_screeninfo var;  
  12.     struct fb_fix_screeninfo fix;  
  13.     unsigned short *memstart;  
  14.   
  15.     fd = open("/dev/fb0", O_RDWR);  
  16.     if(fd < 0){  
  17.         printf("open /dev/fb0 failed\n");  
  18.         return -1;  
  19.     }  
  20.       
  21.     retval = ioctl(fd, FBIOGET_FSCREENINFO, &fix);  
  22.     if(retval < 0){  
  23.         printf("ioctl failed\n");  
  24.         return -1;  
  25.     }  
  26.   
  27.     printf("seme len= %d\n", fix.smem_len);  
  28.   
  29.     memstart = mmap(NULL, 240*320*2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);  
  30.     if (memstart == MAP_FAILED){  
  31.         printf("mmap wrong\n");  
  32.     }  
  33.       
  34.     for(; ;){  
  35.     sleep(1);  
  36.     for(i = 0; i < (153600>>1); i++)  
  37.         *(memstart + i) = 0xf800;  
  38.     sleep(1);  
  39.     for(i = 0; i < (153600>>1); i++)  
  40.         *(memstart + i) = 0x0c00;  
  41.     }  
  42. }  
该测试程序是网上的,我只是稍微修改了下。

2012.11.05 添加屏幕型号
0 0
原创粉丝点击