驱动程序之_1_字符设备_11_LCD驱动_2_编写

本文深入解析LCD驱动程序的编写过程,从初始化硬件、配置fb_info结构体到注册和注销驱动,详细介绍了如何创建一个简单的LCD驱动,适用于内核fbmem机制。通过具体代码示例,展示了操作函数设置、调色板配置及硬件初始化步骤。

驱动程序之_1_字符设备_11_LCD驱动_2_编写

上一篇文章粗浅地分析了内核的一个LCD驱动代码,由此知道编写LCD驱动的一个方法:编写一个设备,将其融入内核的fbmem即可。
本文仿照上一篇文章所分析的例子,去掉platform框架,编写一个符合fbmem机制的简单LCD驱动程序

在入口函数
1、分配fb_info结构体

	static struct fb_info *s3c_lcd;
	
	s3c_lcd = framebuffer_alloc(0,NULL);	

2、设置fb_info结构体
①、根据硬件要求设置bpp、xres、yres、line_length等基本参数
②、设置调色板
③、设置操作函数结构体
这里只列出操作函数结构体的设置,其中cfb_fillrect、cfb_copyarea、cfb_imageblit这三个操作函数是使用内核自带的程序,使用这个驱动程序的时候需要将对这三个函数所在的文件作相应处理(编进内核或者编译为模块手动加载,在文末测试的时候会采用编译为模块的方法)

static struct fb_ops   s3c_lcdfb_ops = {
	.owner			= THIS_MODULE,
	.fb_setcolreg	= s3c_lcdfb_setcolreg,
	.fb_fillrect	= cfb_fillrect,
	.fb_copyarea	= cfb_copyarea,
	.fb_imageblit	= cfb_imageblit,
};

s3c_lcd->fbops 			= &s3c_lcdfb_ops;

3、注册fb_info结构体

register_framebuffer(s3c_lcd);

4、硬件初始化
根据硬件要求,IO映射、设置IO引脚、LCD控制器、分配物理、虚拟显存等等

2、在出口函数完成与入口函数相反的操作
①、注销fb_info结构体

unregister_framebuffer(s3c_lcd);

②、关闭LCD控制器和相关电源,释放显存、取消IO映射

	*gpbdat 		  &= ~1;   	
	lcd_regs->lcdcon1 &= ~1;	
	lcd_regs->lcdcon5 &= ~(1<<3);

	dma_free_writecombine(NULL,s3c_lcd->screen_size,s3c_lcd->screen_base,s3c_lcd->fix.smem_start);
	
	iounmap(gpbcon);
	iounmap(gpccon);
	iounmap(gpdcon);
	iounmap(gpgcon);
	iounmap(lcd_regs);		

③、销毁fb_info结构体

framebuffer_release(s3c_lcd);

测试方法:
1、进入内核目录,make menuconfig,反选内核的LCD驱动并退出保存,make uImage,编译内核,在arch/arm/boot/中找到内核将其下载到开发板
2、进入内核目录,make modules,编译模块,在drivers/video/中找到cfbimgblt.ko、cfbfillrect.ko、cfbcopyarea.ko,将它们拷贝到开发板上
3、编译LCD驱动,拷贝到开发板上
4、用新的内核启动开发板,加载cfbimgblt.ko、cfbfillrect.ko、cfbcopyarea.ko,再加载LCD驱动
5、输入 echo hello > /dev/tty1 ,将hello输出到lcd上
6、输入 cat 文件名 > /dev/tty1 ,将一个文件的内容输出到lcd上

附上完整代码

#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>

struct s3c_lcd_regs{
	volatile unsigned long lcdcon1;	//0X4D000000
	volatile unsigned long lcdcon2;
	volatile unsigned long lcdcon3;
	volatile unsigned long lcdcon4;	
	volatile unsigned long lcdcon5;
	volatile unsigned long lcdsaddr1;
	volatile unsigned long lcdsaddr2;
	volatile unsigned long lcdsaddr3;
	volatile unsigned long redlut;
	volatile unsigned long greenlut;
	volatile unsigned long bluelut;
	volatile unsigned long reserved[9];
	volatile unsigned long dithmode;	//0X4D00004C
	volatile unsigned long tpal;   	//0X4D000050
	volatile unsigned long lcdintpnd;
	volatile unsigned long lcdsrcpnd;
	volatile unsigned long lcdintmsk;
	volatile unsigned long tconsel;
};


static u32 pseudo_palette[16];

static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
	chan &= 0xffff;
	chan >>= 16 - bf->length;
	return chan << bf->offset;
}


static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
			     unsigned int green, unsigned int blue,
			     unsigned int transp, struct fb_info *info)
{
	unsigned int val;
	
	if (regno > 16)
		return 1;

	/* 用red,green,blue三原色构造出val */
	val  = chan_to_field(red,	&info->var.red);
	val |= chan_to_field(green, &info->var.green);
	val |= chan_to_field(blue,	&info->var.blue);
	
	//((u32 *)(info->pseudo_palette))[regno] = val;
	pseudo_palette[regno] = val;
	return 0;
}


static struct fb_info *s3c_lcd;
static struct fb_ops   s3c_lcdfb_ops = {
	.owner			= THIS_MODULE,
	.fb_setcolreg	= s3c_lcdfb_setcolreg,
	.fb_fillrect	= cfb_fillrect,
	.fb_copyarea	= cfb_copyarea,
	.fb_imageblit	= cfb_imageblit,
};
static struct s3c_lcd_regs *lcd_regs;
static volatile unsigned long *gpbcon,*gpbdat,*gpccon,*gpdcon,*gpgcon;



static int lcd_init(void)
{
	//硬件初始化
	//分配设置注册结构体

	s3c_lcd = framebuffer_alloc(0,NULL);	
	
	
	strcmp(s3c_lcd->fix.id,"s3c_lcd");
	s3c_lcd->fix.smem_len   = 480 * 272 * 2;
	s3c_lcd->fix.type     = FB_TYPE_PACKED_PIXELS;
	s3c_lcd->fix.visual     = FB_VISUAL_TRUECOLOR;
	s3c_lcd->fix.line_length = 480 * 2;
	
	s3c_lcd->var.xres = 480;
	s3c_lcd->var.xres_virtual = 480;
	s3c_lcd->var.yres = 272;
	s3c_lcd->var.yres_virtual = 272;
	s3c_lcd->var.bits_per_pixel = 16;
	
	s3c_lcd->var.red.offset     = 11;
	s3c_lcd->var.red.length     = 5;
	s3c_lcd->var.green.offset   = 5;
	s3c_lcd->var.green.length   = 6;
	s3c_lcd->var.blue.offset    = 0;
	s3c_lcd->var.blue.length    = 5;
	
	s3c_lcd->var.activate       = FB_ACTIVATE_NOW;

	s3c_lcd->fbops 			= &s3c_lcdfb_ops;
	
	s3c_lcd->pseudo_palette = pseudo_palette;
	
	s3c_lcd->screen_size    = 480 * 272 * 2;


	gpbcon = ioremap(0x56000010,8);
	gpbdat = gpbcon + 1;
	gpccon = ioremap(0x56000020,4);
	gpdcon = ioremap(0x56000030,4);
	gpgcon = ioremap(0x56000060,4);

	
	*gpccon  =  0xaaaaaaaa;
	*gpdcon  =  0xaaaaaaaa;
	*gpbcon &= ~(3);	//GPB0	KEYBOARD	//LED电源
	*gpbcon |=  (1);	//GPB0	KEYBOARD	//LED电源	
	*gpbdat &= ~1;
	*gpgcon |=  (3<<8);	//GPG4 LCD_PWREN

	lcd_regs  = ioremap(0X4D000000,sizeof(struct s3c_lcd_regs));

	/*
		lcdcon1
		27:18	(read only)
		17:8	TFT: VCLK = HCLK / [(CLKVAL+1) x 2] ( CLKVAL ≥ 0 )
		   7	Determine the toggle rate of the VM:	0 = Each Frame 1 = The rate defined by the MVAL
		 6:5	11 = TFT LCD panel
		 4:1	1011 = 8 bpp for TFT	1100 = 16 bpp for TFT	1101 = 24 bpp for TFT
		   0	1 = Enable
	*/
	lcd_regs->lcdcon1   =   (4<<8) | 
							(3<<5) | 
							(0x0c<<1);		
	/*
		lcdcon2
		31:24	TFT: Vertical back porch
		23:14	vertical size of LCD panel.
		13:6	Vertical front porch is the number of inactive lines at the end of a frame, before vertical synchronization period.
		 5:0	Vertical sync pulse width determines the VSYNC pulse's high level width by counting the number of inactive lines.
	*/
	//th = VBPD+1
	//tv = VSPW+1
	//272 = LINEVAL+1
	//tvf = VFPD+1
	//thp = HSPW+1
	//thb = HBPD+1
	//480 = HOZVAL+1
	//thf = HFPD+1
	/*
	Note 1: t hd =480CLK, t hf = 2CLK, t hp = 41CLK, t hb = 2CLK
	525CLK=480CLK + 2CLK + 41CLK + 2CLK
	Note 2: t hf + t hp + t hb ﹥44 CLK

	*/
	lcd_regs->lcdcon2   = (1<<24) | (271<<14) | (1<<6) | (9);
	/*
		lcdcon3
		25:19	Horizontal back porch is the number of VCLK periods between the falling edge of HSYNC and the start of active data
		18:8	These bits determine the horizontal size of LCD panel.
		 7:0	 Horizontal front porch is the number of VCLK periods between the end of active data and the rising edge of HSYNC
	*/
	lcd_regs->lcdcon3   = (1<<19) | (479<<8) | (1);
	/*
		lcdcon4
		 7:0	TFT: Horizontal sync pulse width determines the HSYNC pulse's high level width by counting the number of the VCLK.
	*/
	lcd_regs->lcdcon4   = 40;
	/*
		lcdcon5
		12		BPP24BL   TFT: This bit determines the order of 24 bpp video memory. 0 = LSB valid 1 = MSB Valid
		11		FRM565	  0 = 5:5:5:1 Format 1 = 5:6:5 Format
		10		INVVCLK   0 = The video data is fetched at VCLK falling edge
		 9		INVVLINE  This bit indicates the VLINE/HSYNC pulse polarity. 0 = Normal 1 = Inverted
		 8		INVVFRAME This bit indicates the VFRAME/VSYNC pulse polarity.0 = Normal 1 = Inverted
		 7		INVVD	  This bit indicates the VD (video data) pulse polarity.0 = Normal 1 = VD is inverted.
		 6		INVVDEN	  TFT: This bit indicates the VDEN signal polarity.0 = normal 1 = inverted
		 5		INVPWREN  This bit indicates the PWREN signal polarity.0 = normal 1 = inverted
		 4		INVLEND	  This bit indicates the LEND signal polarity.0 = normal 1 = inverted
		 3		PWREN	  LCD_PWREN output signal enable/disable.0 = Disable PWREN signal  1 = Enable PWREN signal
		 2		ENLEND	  LEND output signal enable/disable.	0 = Disable LEND signal  1 = Enable LEND signal
		 1		BSWP
		 0		HWSWP
		 
	*/
	lcd_regs->lcdcon5   = (1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<0);

	s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len, &s3c_lcd->fix.smem_start, GFP_KERNEL);
	
	lcd_regs->lcdsaddr1 = (s3c_lcd->fix.smem_start>>1) & ~(3<<30);
	lcd_regs->lcdsaddr2 = ((s3c_lcd->fix.smem_start+s3c_lcd->fix.smem_len)>>1) & (0x1fffff);
	lcd_regs->lcdsaddr3 = 480 * 16 / 16;
	
	lcd_regs->lcdcon1 |= 1;
	lcd_regs->lcdcon5 |= 1<<3;	
	*gpbdat |= 1;   

	register_framebuffer(s3c_lcd);

	return 0;
}

static void lcd_exit(void)
{
	unregister_framebuffer(s3c_lcd);	

	*gpbdat 		  &= ~1;   	
	lcd_regs->lcdcon1 &= ~1;	
	lcd_regs->lcdcon5 &= ~(1<<3);

	dma_free_writecombine(NULL,s3c_lcd->screen_size,s3c_lcd->screen_base,s3c_lcd->fix.smem_start);
	
	iounmap(gpbcon);
	iounmap(gpccon);
	iounmap(gpdcon);
	iounmap(gpgcon);
	iounmap(lcd_regs);	

	framebuffer_release(s3c_lcd);
}

module_init(lcd_init);
module_exit(lcd_exit);
MODULE_LICENSE("GPL");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值