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结构
修改后的内容如下:
- /* 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_LCDCON1_TFT,
- .width = 320,//240,
- .height = 240,//320,
- .pixclock = 156250,//166667, /* HCLK 60 MHz, divisor 10 */
- .xres = 320,//240,
- .yres = 240,//320,
- .bpp = 16,
- .left_margin = 20,
- .right_margin = 38,//8,
- .hsync_len = 30,//4,
- .upper_margin = 15,//8,
- .lower_margin = 12,//7,
- .vsync_len = 3,//4,
- };
上面的参数是如何修改的呢?我们来看下。
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的。
- /* s3c2410fb_activate_var
- *
- * activate (set) the controller from the given framebuffer
- * information
- */
- static void s3c2410fb_activate_var(struct fb_info *info)
- {
- struct s3c2410fb_info *fbi = info->par;
- void __iomem *regs = fbi->io;
- int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/
- struct fb_var_screeninfo *var = &info->var;
- int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
- dprintk("%s: var->xres = %d\n", __func__, var->xres);
- dprintk("%s: var->yres = %d\n", __func__, var->yres);
- dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);
- if (type == S3C2410_LCDCON1_TFT) {
- s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/
- --clkdiv;
- if (clkdiv < 0)
- clkdiv = 0;
- } else {
- s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
- if (clkdiv < 2)
- clkdiv = 2;
- }
- fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/
- /* write new registers */
- dprintk("new register set:\n");
- dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
- dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
- dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
- dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
- dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
- /*把计算好的值填入LCD控制器中*/
- writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
- regs + S3C2410_LCDCON1); /*仍然禁止LCD*/
- writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
- writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
- writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
- writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
- /* set lcd address pointers */
- s3c2410fb_set_lcdaddr(info); /*设置LCD帧缓冲起始地址*/
- fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
- writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1); /*使能LCD*/
- }
- static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
- unsigned long pixclk)
- {
- unsigned long clk = clk_get_rate(fbi->clk); /*获取当前时钟频率(Hz)*/
- unsigned long long div;
- /* pixclk is in picoseconds, our clock is in Hz
- *
- * Hz -> picoseconds is / 10^-12
- */
- div = (unsigned long long)clk * pixclk;
- div >>= 12; /* div / 2^12 */
- do_div(div, 625 * 625UL * 625); /* div / 5^12 */
- dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);
- return div;
- }
随后由于是采用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结构
修改后的内容如下:
- static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
- .displays = &smdk2440_lcd_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
- //no
- // .lpcsel = ((0xCE6) & ~7) | 1<<4,
- };
做出的唯一修改就是:禁用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 Hzgeometry 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
上述中的数值都是我们设置的参数。
另外,可以通过下列测试程序进行测试:
- #include <linux/fb.h>
- #include <unistd.h>
- #include <sys/mman.h>
- #include <stdio.h>
- #include <fcntl.h>
- //#include <syswait.h>
- int main()
- {
- int fd, retval, i;
- struct fb_var_screeninfo var;
- struct fb_fix_screeninfo fix;
- unsigned short *memstart;
- fd = open("/dev/fb0", O_RDWR);
- if(fd < 0){
- printf("open /dev/fb0 failed\n");
- return -1;
- }
- retval = ioctl(fd, FBIOGET_FSCREENINFO, &fix);
- if(retval < 0){
- printf("ioctl failed\n");
- return -1;
- }
- printf("seme len= %d\n", fix.smem_len);
- memstart = mmap(NULL, 240*320*2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (memstart == MAP_FAILED){
- printf("mmap wrong\n");
- }
- for(; ;){
- sleep(1);
- for(i = 0; i < (153600>>1); i++)
- *(memstart + i) = 0xf800;
- sleep(1);
- for(i = 0; i < (153600>>1); i++)
- *(memstart + i) = 0x0c00;
- }
- }
2012.11.05 添加屏幕型号
- S3C2440 Linux驱动移植——LCD
- S3C2440 Linux驱动移植——LCD
- linux下 s3c2440 lcd驱动移植详解
- linux下 s3c2440 lcd驱动移植详解
- linux下 s3c2440 lcd驱动移植详解
- s3c2440 lcd驱动移植
- s3c2440之LCD驱动移植
- S3C2440,Linux,LCD驱动
- S3C2440 Linux驱动移植——SPI
- S3C2440 Linux驱动移植——按键
- S3C2440 Linux驱动移植——SPI
- S3C2440 Linux驱动移植——按键
- S3C2440 Linux驱动移植——SPI
- 转:linux下 s3c2440 lcd驱动移植详解
- S3C2440 Linux驱动移植——NAND驱动
- S3C2440 Linux驱动移植——SD卡驱动
- S3C2440 Linux驱动移植——AT24C02(EEPROM)驱动
- S3C2440 Linux驱动移植——NAND驱动
- c++资源大全
- OpenCV里IplImage的widthStep参数 和width参数
- springmvc restTemplate访问远端工程
- android之调用webservice 实现图片上传
- MemCached Cache Java Client封装优化历程
- S3C2440 Linux驱动移植——LCD
- 摄影比赛 截止日期一览表
- 求解大数据的阶乘的值的显示
- 飞思卡尔kinetis L系列MCU之中断5扇门
- 做acm用到的几个C++库函数
- 微信分享(对比及配置)
- S3C2440 Linux驱动移植——按键
- 程序员必看的十大电影
- CentOS 安装无线网卡驱动,配置无线上网