嵌入式linux驱动-lcd驱动笔记

一、开发环境

1、内核:Linux 2.6.22.6;

2、JZ2440v3

3、ubuntu 9.10

二、二期视频讲的是3.5寸tft屏的JZ2440v3用的4.3寸tft屏,参数有所不同。

程序

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 
#include 
#include 
#include 

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");





你可能感兴趣的:(嵌入式linux驱动)