一、开发环境
1、内核:Linux 2.6.22.6;
2、JZ2440v3
3、ubuntu 9.10
二、二期视频讲的是3.5寸tft屏的JZ2440v3用的4.3寸tft屏,参数有所不同。程序
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/div64.h>
#include <asm/mach/map.h>
#include <asm/arch/regs-lcd.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/fb.h>
static volatile unsigned long *gpbcon;
static volatile unsigned long *gpbdat;
static volatile unsigned long *gpccon;
static volatile unsigned long *gpcdat;
static volatile unsigned long *gpdcon;
static volatile unsigned long *gpddat;
static volatile unsigned long *gpgcon;
static volatile unsigned long *gpgdat;
static struct fb_info *mylcdinfo_fb;
static u32 pseudo_palette[16];
static struct lcd_regs {
unsigned long lcdcon1;
unsigned long lcdcon2;
unsigned long lcdcon3;
unsigned long lcdcon4;
unsigned long lcdcon5;
unsigned long lcdsaddr1;
unsigned long lcdsaddr2;
unsigned long lcdsaddr3;
unsigned long redlut;
unsigned long greenlut;
unsigned long bluelut;
unsigned long reserved[9];
unsigned long dithmode;
unsigned long tpal;
unsigned long lcdintpnd;
unsigned long lcdsrcpnd;
unsigned long lcdintmsk;
unsigned long tconsel;
};
static volatile struct lcd_regs * lcd_regs;
static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
{
chan &= 0xffff;
chan >>= 16 - bf->length;
return chan << bf->offset;
}
static int mylcd_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info)
{
unsigned int val;
/* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n", regno, red, green, blue); */
/* true-colour, use pseuo-palette */
if (regno > 16)
return 1; /* unknown type */
val = chan_to_field(red, &info->var.red);
val |= chan_to_field(green, &info->var.green);
val |= chan_to_field(blue, &info->var.blue);
pseudo_palette[regno] = val;
return 0;
}
static struct fb_ops mylcd_ops = {
.owner = THIS_MODULE,
//.fb_check_var = s3c2410fb_check_var,
//.fb_set_par = s3c2410fb_set_par,
//.fb_blank = s3c2410fb_blank,
.fb_setcolreg = mylcd_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
static int mylcd_init(void)
{
//1、分配fb_info结构体
mylcdinfo_fb = framebuffer_alloc(0, NULL);
//2、设置
//固定参数
strcpy(mylcdinfo_fb->fix.id, "mylcd");
mylcdinfo_fb->fix.smem_len=480*272*16/8;
mylcdinfo_fb->fix.type=FB_TYPE_PACKED_PIXELS;
mylcdinfo_fb->fix.visual=FB_VISUAL_TRUECOLOR;
mylcdinfo_fb->fix.line_length=480*2;
//可变参数
mylcdinfo_fb->var.xres=480;
mylcdinfo_fb->var.yres=272;
mylcdinfo_fb->var.xres_virtual=480;
mylcdinfo_fb->var.yres_virtual=272;
mylcdinfo_fb->var.bits_per_pixel=16;
mylcdinfo_fb->var.red.offset=11;
mylcdinfo_fb->var.red.length=5;
mylcdinfo_fb->var.green.offset=5;
mylcdinfo_fb->var.green.length=6;
mylcdinfo_fb->var.blue.offset=0;
mylcdinfo_fb->var.blue.length=5;
mylcdinfo_fb->var.activate=FB_ACTIVATE_NOW;
//操作函数
mylcdinfo_fb->fbops=&mylcd_ops;
mylcdinfo_fb->pseudo_palette=pseudo_palette;
//mylcdinfo_fb.screen_base=
mylcdinfo_fb->screen_size=480*272*16/8;
//其他
//3、硬件相关操作
//配置LCD GPIO
gpbcon=ioremap(0x56000010, 8);
gpbdat=gpbcon+1;
gpccon=ioremap(0x56000020, 4);
//gpcdat=gpccon+1;
gpdcon=ioremap(0x56000030, 4);
gpgcon=ioremap(0x56000060, 4);
//gpgdat=gpgcon+1;
*gpccon=0xaaaaaaaa;
*gpdcon=0xaaaaaaaa;
*gpbcon &= ~(3);//背光
*gpbcon |= 1;
*gpbdat&= ~1;
*gpgcon |= (3<<8);
//设置lcd控制器
lcd_regs=ioremap(0X4D000000, sizeof(struct lcd_regs));
/*lcdcon1
* bit[17:8]: VCLK = HCLK / [(CLKVAL+1) x 2], LCD手册P14
* 10MHz(100ns) = 100MHz / [(CLKVAL+1) x 2]
* CLKVAL = 4
* bit[6:5]: 0b11, TFT LCD
* bit[4:1]: 0b1100, 16 bpp for TFT
* bit[0] : 0 = Disable the video output and the LCD control signal.
*/
lcd_regs->lcdcon1=(4<<8)|(3<<5)|(0x0c<<1);
/* 垂直方向的时间参数
* bit[31:24]: VBPD, VSYNC之后再过多长时间才能发出第1行数据
* LCD手册 T0-T2-T1=4
* VBPD=3
* bit[23:14]: 多少行, 320, 所以LINEVAL=320-1=319
* bit[13:6] : VFPD, 发出最后一行数据之后,再过多长时间才发出VSYNC
* LCD手册T2-T5=322-320=2, 所以VFPD=2-1=1
* bit[5:0] : VSPW, VSYNC信号的脉冲宽度, LCD手册T1=1, 所以VSPW=1-1=0
*/
lcd_regs->lcdcon2=(1<<24)|(271<<14)|(1<<6)|(9<<0);
/* 水平方向的时间参数
* bit[25:19]: HBPD, VSYNC之后再过多长时间才能发出第1行数据
* LCD手册 T6-T7-T8=17
* HBPD=16
* bit[18:8]: 多少列, 240, 所以HOZVAL=240-1=239
* bit[7:0] : HFPD, 发出最后一行里最后一个象素数据之后,再过多长时间才发出HSYNC
* LCD手册T8-T11=251-240=11, 所以HFPD=11-1=10
*/
lcd_regs->lcdcon3=(1<<19)|(479<<8)|(1<<0);
/* 水平方向的同步信号
* bit[7:0] : HSPW, HSYNC信号的脉冲宽度, LCD手册T7=5, 所以HSPW=5-1=4
*/
lcd_regs->lcdcon4=(40<<0);
/* 信号的极性
* bit[11]: 1=565 format
* bit[10]: 0 = The video data is fetched at VCLK falling edge
* bit[9] : 1 = HSYNC信号要反转,即低电平有效
* bit[8] : 1 = VSYNC信号要反转,即低电平有效
* bit[6] : 0 = VDEN不用反转
* bit[3] : 0 = PWREN输出0
* bit[1] : 0 = BSWP
* bit[0] : 1 = HWSWP 2440手册P413
*/
lcd_regs->lcdcon5=(1<<11)|(1<<9)|(1<<8)|(1<<0);
//显存分配,并告诉lcd控制器地址
//mylcdinfo_fb.fix.smem_start
mylcdinfo_fb->screen_base=dma_alloc_writecombine(NULL,mylcdinfo_fb->fix.smem_len,&mylcdinfo_fb->fix.smem_start,GFP_KERNEL);
lcd_regs->lcdsaddr1=(mylcdinfo_fb->fix.smem_start >> 1)&~(3<<30);
lcd_regs->lcdsaddr2=((mylcdinfo_fb->fix.smem_start+mylcdinfo_fb->fix.smem_len) >>1 )&0x1fffff;
lcd_regs->lcdsaddr3=(480*16/16);
//启动lcd
lcd_regs->lcdcon1 |=1;
lcd_regs->lcdcon5 |=(1<<3);
*gpbdat|= 1;
//4、注册
register_framebuffer(mylcdinfo_fb);
return 0;
}
static void mylcd_exit(void)
{
unregister_framebuffer(&mylcdinfo_fb);
lcd_regs->lcdcon1 &=~1;
*gpbdat&=~1;
dma_free_writecombine(NULL, mylcdinfo_fb->fix.smem_len,mylcdinfo_fb->screen_base, mylcdinfo_fb->fix.smem_start);
iounmap(lcd_regs);
iounmap(gpbcon);
iounmap(gpccon);
iounmap(gpdcon);
iounmap(gpgcon);
framebuffer_release(mylcdinfo_fb);
}
module_init(mylcd_init);
module_exit(mylcd_exit);
MODULE_LICENSE("GPL");