您的位置:首页 > 其它

s3c2440 LCD驱动编写

2016-06-11 11:43 411 查看
#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>

/*LCD控制寄存器的描述*/

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

};

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 volatile unsigned long *gpbcon;

static volatile unsigned long *gpbdat;

static volatile unsigned long *gpccon;

static volatile unsigned long *gpdcon;

static volatile unsigned long *gpddat;

static struct fb_info *s3c_lcd;//这个结构一体中的默认值都是0

static u32 pseudo_palette[16];

/* 结构体名和结构体指针名相同,以后要使用结构体的话则lcd_regs

 * 前面必须加上struct,否则会认为是指针名

 *

 */

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 volatile struct lcd_regs* lcd_regs;

static int lcd_init(void)

{
/*1.分配fb_info*/
/*framebuffer_alloc(size_t size,struct device * dev)中的size是为结构体分配的私有空间*/
s3c_lcd = framebuffer_alloc(0,NULL);
/*2.设置*/
/*2.1 设置固定的参数*/
strcpy(s3c_lcd->fix->id,"mylcd");//名字
s3c_lcd->fix->smem_len = 240*320*16/8;//显存长度
/*FB_TYPE_PACKED_PIXELS是默认的类型,支持大部分LCD */
s3c_lcd->fix->type     = FB_TYPE_PACKED_PIXELS;
s3c_lcd->fix.visual    = FB_VISUAL_TRUECOLOR;//设置颜色为真彩色  TFT是真彩色
s3c_lcd->fix.line_length = 240*2;
/*2.2设置可变的参数*/
s3c_lcd->var.xres     = 240;//x方向的分辨率
s3c_lcd->var.yres     = 320;
s3c_lcd->var.xres_virtual = 240;//x方向虚拟分辨率 
s3c_lcd->var.yres_virtual = 320;
s3c_lcd->var.bits_per_pixel = 16;

/*RGB:565*/
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;//使设置的值立即生效
/*2.3设置操作函数*/
s3c_lcd->fbops              = &s3c_lcdfb_ops;
/*2.4其他的设置*/
/*pseudo_palette
*假的调色板,为了兼容以前的程序
*/
s3c_lcd->pseudo_palette    = pseudo_palette;

// s3c_lcd->screen_base       = ;//显存的虚拟地址
s3c_lcd->screen_size       = 240*320*16/8;/*显存的大小*/
/*3.硬件相关的设置*/
/*3.1配置GPIO用于LCD*/
 
gpbcon = ioremap(0x56000010,8);/*8 个字节*/
gpbdat = gpbcon + 1;//指针+1   相当于加4个字节

gpccon = ioremap(0x56000020,4);//4 
gpdcon = ioremap(0x56000030,4);//4 
gpgcon = ioremap(0x56000040,4);//4 

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

*gpbcon &= ~(3);/*设置bit0,bit1为0*/
*gpbcon |= (1); /*设置GPB0为输出引脚*/
*gpbdat &= ~(1);/*输出低电平*/

*gpgcon |= (3<<8);/* GPG4用作LCD_PWREN */

/*3.2根据LCD手册设置LCD控制器,比如VCLK的频率等*/
 
lcd_regs = ioremap(0x4D000000,sizeof(struct lcd_regs));
/* bit[17:8]: VCLK = HCLK / [(CLKVAL + 1) x 2],LCD手册P14
*   10MHZ(100ns) = 100MHZ / [(CLKVAL + 1) x 2]    HCLK可以从内核读出
*   CLKVAL = 4
* bit[6:5] : 0b11, TFT LCD
* bit[4:1] : 0b1100,16 bpp for TFT,16bpp表示每个像素由16位表示颜色
* 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 = (3<<24)|(319<<14)|(1<<6)|(0<<0);

/* 水平方向的时间参数
* bit[25:19]: HBPD,VSYNC之后在经过多长时间才能发出第一行数据
* 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 = (16<<19) | (239<<8) | (10<<0);

/* 水平方向的同步信号
* bit[7:0]: HSPW,HSYNC信号的脉冲宽度,LCD手册T7=5,所以HSPW=5-1=4 
*/
lcd_regs->lcdcon4 = (4<<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);

/*3.3分配显存(framebuffer),并把地址告诉LCD控制器*/

/*
* dma_alloc_writecombine参数分别为设备,显存大小,显存的物理地址,标志
* 该函数的返回值是这块内存的虚拟地址

* A = dma_alloc_writecombine(struct device *dev, size_t size,dma_addr_t *handle, gfp_t gfp);
*
* 含义:
* A          : 内存的虚拟起始地址,在内核要用此地址来操作所分配的内存
* dev      : 可以平台初始化里指定,主要是用到dma_mask之类参数,可参考framebuffer
* size      : 实际分配大小,传入dma_map_size即可
* handle: 返回的内存物理地址,dma就可以用。
* A和hanle是一一对应的,A是虚拟地址,而handle
* 是总线地址。对任意一个操作都将改变写缓冲区内容。
*/

s3c_lcd->screen_base = dma_alloc_writecombine(NULL,s3c_lcd->fix->smem_len,s3c_lcd->fix.smem_start,GFP_KERNEL);

/* LCDSADDR1   对显存的物理地址起始地址s3c_lcd->fix.smem_start操作
* bit[29:21]:对应显存的物理地址起始地址A[30:22]
* bit[20:0] :对应显存的物理地址起始地址A[21:1]
*
*/
lcd_regs->lcdsaddr1 = (s3c_lcd->fix.smem_start>>1) & ~(3<<30);

/* LCDSADDR2   对显存的物理地址结束地址 s3c_lcd->fix.smem_start + s3c_lcd->fix->smem_len操作
* bit[20:0] :对应显存的物理地址结束地址A[21:1]
*
*/
lcd_regs->lcdsaddr2 = ((s3c_lcd->fix.smem_start + s3c_lcd->fix->smem_len)>>1) & 0x1fffff;

/* LCDSADDR3   
* bit[10:0] :PAGEWIDTH,一行的长度(单位:半字)
* PAGEWIDTH = 240*16/16
*
*/
lcd_regs->lcdsaddr3 = 240*16/16;

// s3c_lcd->fix.smem_start     = ;/*显存的物理地址*/

/*启动LCD*/
lcd_regs->lcdcon1 |= (1<<0);/*启动LCD本身*/ 
*gpbdat |= (1);/*输出高电平,使能背光灯*/

/*4.注册*/
register_framebuffer(s3c_lcd);
return 0;

}

static void lcd_exit(void)

{
unregister_framebuffer(s3c_lcd);
lcd_regs->lcdcon1 &= ~(1<<0); /* 关闭LCD本身 */
*gpbdat &= ~1;     /* 关闭背光 */
dma_free_writecombine(NULL, s3c_lcd->fix.smem_len, s3c_lcd->screen_base, s3c_lcd->fix.smem_start);
iounmap(lcd_regs);
iounmap(gpbcon);
iounmap(gpccon);
iounmap(gpdcon);
iounmap(gpgcon);
framebuffer_release(s3c_lcd);

}

module_init(lcd_init);

module_exit(lcd_exit);

MODULE_LICENSE("GPL");
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: