linux3.4.2 之LCD驱动

 

驱动源码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fb.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>    //包含了s3c2410_gpio_cfgpin等io操作函数
#include <mach/regs-lcd.h>
#include <mach/regs-gpio.h>
#include <mach/fb.h>

#define LCD_LENGTH 480
#define LCD_WIDTH  272
#define BIT_WIDTH  32

static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
			     unsigned int green, unsigned int blue,
			     unsigned int transp, struct fb_info *info);
			     

static struct fb_info *lcd;
static struct fb_ops  lcd_ops = {
	.owner = THIS_MODULE,
	.fb_setcolreg = s3c_lcdfb_setcolreg,
	.fb_fillrect = cfb_fillrect,
	.fb_copyarea = cfb_copyarea,
	.fb_imageblit = cfb_imageblit,
};

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;
};

static volatile unsigned long *gpccon;
static volatile unsigned long *gpdcon;
static volatile unsigned long *gpgcon;
static volatile struct lcd_regs* lcd_regs;
static u32 pseudo_palette[16];


/* from pxafb.c */
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 int lcd_init(void)
{
 	//1.分配一个fb_info结构体
	lcd = framebuffer_alloc(0, NULL);

	//2.设置
	//2.1设置固定的参数
	strcpy(lcd->fix.id, "mylcd");
	lcd->fix.smem_len = LCD_LENGTH*LCD_WIDTH*BIT_WIDTH/8; /* TQ2440的LCD位宽是24,但是2440里会分配4字节即32位(浪费1字节) */
	lcd->fix.type     = FB_TYPE_PACKED_PIXELS;
	lcd->fix.visual   = FB_VISUAL_TRUECOLOR;
	lcd->fix.line_length = LCD_LENGTH*BIT_WIDTH/8;
	
	//2.2设置可变的参数
	lcd->var.xres           = LCD_LENGTH;
	lcd->var.yres           = LCD_WIDTH;
	lcd->var.xres_virtual   = LCD_LENGTH;
	lcd->var.yres_virtual   = LCD_WIDTH;
	lcd->var.bits_per_pixel = BIT_WIDTH;//每个像素用多少位
	
	lcd->var.red.offset     = 16;
	lcd->var.red.length     = 8;
	lcd->var.green.offset   = 8;
	lcd->var.green.length   = 8;
	lcd->var.blue.offset    = 0;
	lcd->var.blue.length    = 8;

	lcd->var.activate       = FB_ACTIVATE_NOW;
	
	//2.3设置操作函数
	lcd->fbops = &lcd_ops;
	
	//2.4其他
	lcd->pseudo_palette = pseudo_palette;//调色板
	lcd->screen_size = LCD_LENGTH*LCD_WIDTH*BIT_WIDTH/8;
	
	//3.硬件相关的设置
	//3.1配置gpio用于lcd
	gpccon = ioremap(0x56000020, 4);
	gpdcon = ioremap(0x56000030, 4);
	gpgcon = ioremap(0x56000060, 4);

	*gpccon = 0xaaaaaaaa;/* GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */
	*gpdcon = 0xaaaaaaaa;/* GPIO管脚用于VD[23:8] */

	s3c2410_gpio_cfgpin(S3C2410_GPB(0),S3C2410_GPIO_OUTPUT);//(背光驱动使能引脚)gpb0设置为输出
	s3c2410_gpio_setpin(S3C2410_GPB(0),0);//输出低电平

	*gpgcon |= S3C2410_GPG4_LCDPWREN;//gpg4用作lcd电源使能引脚

	lcd_regs = ioremap(S3C2410_PA_LCD, sizeof(struct lcd_regs));

	//3.2根据lcd手册设置lcd控制器
	/*bit[17:8]: VCLK = HCLK / [(CLKVAL+1) * 2];
	*	  (10MHz)VCLK = 100MHz/[(CLKVAL+1) * 2];
	*   => CLKVAL = 4
	* bit[6:5]:display mode = 0b11;
	* bit[4:1]:Select the BPP (Bits Per Pixel) mode.  24 bpp for TFT
	* bit[0]  :LCD video output and the logic enable/disable.*/  
	lcd_regs->lcdcon1  = S3C2410_LCDCON1_CLKVAL(4) | 
	                     S3C2410_LCDCON1_TFT | 
						 S3C2410_LCDCON1_TFT24BPP;

	/*垂直方向的时间参数
	* bit[31:24]:VBPD = 1;//VSYNC之后再过多长时间才能发出第一行数据
	* bit[23:14]:LINEVAL = 319;//多少行
	* bit[13: 6]:VFPD = 1;//发出最后一行数据之后,再过多长时间发出VSYNC信号
	* bit[ 5: 0]:VSPW = 9;//VSYNC信号的脉冲宽度   */
	lcd_regs->lcdcon2 = S3C2410_LCDCON2_VBPD(1) | 
	                    S3C2410_LCDCON2_LINEVAL(271) | 
						S3C2410_LCDCON2_VFPD(1) |
						S3C2410_LCDCON2_VSPW(9);
	/* 水平方向的时间参数
	 * bit[25:19]: HBPD, VSYNC之后再过多长时间才能发出第1行数据
	 *             LCD手册 thb=2
	 *             HBPD=1
	 * bit[18:8]: 多少列, LCD_LENGTH, 所以HOZVAL=LCD_LENGTH-1=479
	 * bit[7:0] : HFPD, 发出最后一行里最后一个象素数据之后,再过多长时间才发出HSYNC
	 *             LCD手册thf=2, 所以HFPD=2-1=1*/
	lcd_regs->lcdcon3 = S3C2410_LCDCON3_HBPD(1) | 
	                    S3C2410_LCDCON3_HOZVAL(479) | 
						S3C2410_LCDCON3_HFPD(1);

	/* 水平方向的同步信号
	* bit[7:0] : HSPW, HSYNC信号的脉冲宽度, LCD手册Thp=41, 所以HSPW=41-1=40*/ 
	lcd_regs->lcdcon4 = S3C2410_LCDCON4_HSPW(40);

	/* 信号的极性 
	 * bit[11]: 1=565 format, 对于24bpp这个不用设
	 * bit[10]: 0 = 在VCLK的下降沿取数据
	 * bit[9] : 1 = HSYNC信号要反转,即低电平有效 
	 * bit[8] : 1 = VSYNC信号要反转,即低电平有效 
	 * bit[6] : 0 = VDEN不用反转
	 * bit[3] : 0 = PWREN输出0
	 * BSWP = 0, HWSWP = 0, BPP24BL = 0 : 当bpp=24时,2440会给每一个象素分配32位即4字节,哪一个字节是不使用的? 看2440手册P412
         * bit[12]: 0, LSB valid, 即最高字节不使用
	 * bit[1] : 0 = BSWP
	 * bit[0] : 0 = HWSWP*/
	lcd_regs->lcdcon5 = S3C2410_LCDCON5_INVVLINE | 
						S3C2410_LCDCON5_INVVFRAME;
	
	//3.3分配显存(framebuffer),并把地址告诉LCD控制器
	//lcd->fix.smem_start :显存的物理地址
	//lcd->screen_base    :显存虚拟地址
	lcd->screen_base = dma_alloc_writecombine(NULL, lcd->screen_size, &lcd->fix.smem_start, GFP_KERNEL);//返回显存虚拟地址

	lcd_regs->lcdsaddr1 = (lcd->fix.smem_start>>1) & ~(3<<30);
	lcd_regs->lcdsaddr2 = ((lcd->fix.smem_start+lcd->fix.smem_len) >> 1) & 0x1fffff ;//结束地址
	lcd_regs->lcdsaddr3 = (LCD_LENGTH*BIT_WIDTH/16);//虚拟屏一行的长度(单位:半字)

	//启动lcd
	lcd_regs->lcdcon1 |= S3C2410_LCDCON1_ENVID; /* 使能LCD控制器 */
	lcd_regs->lcdcon5 |= S3C2410_LCDCON5_PWREN; /* 使能LCD本身: LCD_PWREN */
	s3c2410_gpio_setpin(S3C2410_GPB(0),1);;     /* 输出高电平, 使能背光, TQ2440的背光电路也是通过LCD_PWREN来控制的 */
	
	//4.注册
	register_framebuffer(lcd);

	return 0;
}

static void lcd_exit(void)
{
	unregister_framebuffer(lcd);
	lcd_regs->lcdcon1 &= ~(1<<0); /* 关闭LCD控制器 */
	lcd_regs->lcdcon1 &= ~(1<<3); /* 关闭LCD本身 */
	s3c2410_gpio_setpin(S3C2410_GPB(0),0);;     /* 关闭背光 */
	dma_free_writecombine(NULL, lcd->fix.smem_len, lcd->screen_base, lcd->fix.smem_start);
	iounmap(lcd_regs);
	iounmap(gpccon);
	iounmap(gpdcon);
	iounmap(gpgcon);
	framebuffer_release(lcd);
}

module_init(lcd_init);
module_exit(lcd_exit);
MODULE_LICENSE("GPL");

驱动测试

1.将原生LCD驱动配置为模块:

 -> Device Drivers
      -> Graphics support
          -> Support for frame buffer devices 
                 <M> S3C2410 LCD framebuffer support

2.make uImage 编译内核

3. 编译生成lcd_drv.o ,Makefile文件如下

KERN_DIR = /home/ningjw/linux-3.4.2

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= lcd_drv.o

4. insmod  lcd_drv.ko 装载模块(linux3.4.2不需要额外加载cfbcopyarea.ko cfbfillrect.ko cfbimgblt.ko)

5. 测试驱动是否正常

ls -l /dev/fb*            //命令查看已挂载的LCD设备节点
echo hello> /dev/tty1     // LCD上便显示hello字段
cat Makefile>/dev/tty1    // LCD上便显示Makeflie文件的内容

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值