分类: LINUX
2013-09-07 18:35:32
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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_info *s3clcdfb_info;
static volatile unsigned long *gpbcon;
static volatile unsigned long *gpbdat;
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];
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_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 int __init lcd_init(void)
{
s3clcdfb_info= framebuffer_alloc(0,NULL); // 1. 分配一个fb_info,分配的内存大小在该函数内部会自动指定
/* 2. 设置 */
/* 2.1 设置固定的参数 */
strcpy(s3clcdfb_info->fix.id,"lcd"); //字符串形式的标识符
// s3clcdfb_info->fix.smem_start= //fb缓冲内存的开始地址(物理地址)
s3clcdfb_info->fix.smem_len = 480*272*32/8; //fb缓冲的长度max_xres *max_yres*max_bpp/8 , TQ2440的LCD位宽是24,但是2440里会分配4字节即32位(浪费1字节)
s3clcdfb_info->fix.type =FB_TYPE_PACKED_PIXELS; //查看宏 FB_TYPE_ FB_TYPE_PACKED_PIXELS=0
s3clcdfb_info->fix.type_aux =0;
s3clcdfb_info->fix.visual =FB_VISUAL_TRUECOLOR; //查看宏FB_VISUAL_,用于记录屏幕使用的色彩模式,一般是FB_VISUAL_TRUECOLOR(真彩色)
s3clcdfb_info->fix.xpanstep =0; //如果没有硬件 panning,=0
s3clcdfb_info->fix.ypanstep =0; //如果没有硬件 panning,=0
s3clcdfb_info->fix.ywrapstep =0; //如果没有硬件 panning,=0
s3clcdfb_info->fix.line_length =480*32/8; //1行的字节数,TQ2440的LCD位宽是24,但是2440里会分配4字节即32位(浪费1字节)
s3clcdfb_info->fix.accel = FB_ACCEL_NONE;
/* 2.2 设置可变的参数 */
s3clcdfb_info->var.xres =480; //定义屏幕一行有多少个点
s3clcdfb_info->var.yres =272; //定义屏幕一列由多少个点
s3clcdfb_info->var.xres_virtual =480; //虚拟屏幕一行有多少个点
s3clcdfb_info->var.yres_virtual =272; //虚拟屏幕一列由多少个点
s3clcdfb_info->var.xoffset =0; //虚拟到可见(实际)之间的行方向偏移
s3clcdfb_info->var.yoffset =0; //虚拟到可见(实际)之间的列方向偏移
s3clcdfb_info->var.bits_per_pixel =16;
s3clcdfb_info->var.grayscale =0;
/* RGB:565 */
s3clcdfb_info->var.red.offset = 16; //位域偏移
s3clcdfb_info->var.red.length = 8; //位域长度
s3clcdfb_info->var.green.offset = 8; //位域偏移
s3clcdfb_info->var.green.length = 8; //位域长度
s3clcdfb_info->var.blue.offset = 0; //位域偏移
s3clcdfb_info->var.blue.length = 8; //位域长度
/* 2.3 设置操作函数 */
s3clcdfb_info->fbops =&s3c_lcdfb_ops; //设置操作函数
s3clcdfb_info->flags =FBINFO_FLAG_DEFAULT;
s3clcdfb_info->pseudo_palette =pseudo_palette; //伪16色调色板
s3clcdfb_info->screen_base = dma_alloc_writecombine(NULL, s3clcdfb_info->fix.smem_len, &s3clcdfb_info->fix.smem_start, GFP_KERNEL); //虚拟基地址
s3clcdfb_info->screen_size = 480*272*32/8; //ioremapped 的虚拟内存大小
/* 2.4 其他的设置 */
s3clcdfb_info->var.nonstd =0; //标准像素格式
s3clcdfb_info->var.activate =FB_ACTIVATE_NOW;
s3clcdfb_info->var.accel_flags =0;
s3clcdfb_info->var.pixclock =100000; //像素时钟(皮秒),pixclock=1/Dclk=
s3clcdfb_info->var.left_margin =2; //行切换,从同步到绘图之间的延迟, Hsyn front-porch,查看LCD数据手册
s3clcdfb_info->var.right_margin =2; //行切换,从绘图到同步之间的延迟 Hsyn back-porch
s3clcdfb_info->var.upper_margin =2; //帧切换,从同步到绘图之间的延迟 Vsyn front-porch
s3clcdfb_info->var.lower_margin =2; //帧切换,从绘图到同步之间的延迟 Vsyn back-porch
s3clcdfb_info->var.hsync_len =41; //水平同步的长度 Hsyn pulse Width
s3clcdfb_info->var.vsync_len =10; //垂直同步的长度 Vsyn pulse Width
s3clcdfb_info->var.vmode = FB_VMODE_NONINTERLACED;
/* 3. 硬件相关的操作 */
/* 3.1 配置GPIO用于LCD */
gpbcon = ioremap(0x56000010, 8);
gpbdat = gpbcon+1;
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] */
*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手册P5 (Dclk=9MHz~15MHz)
* 10MHz(100ns) = 100MHz / [(CLKVAL+1) x 2]
* CLKVAL = 4
* bit[6:5]: 0b11, TFT LCD
* bit[4:1]: 0b1101, 24 bpp for TFT
* bit[0] : 0 = 禁止视频输出LCD控制信号
*/
lcd_regs->lcdcon1 = (4<<8) | (3<<5) | (0x0d<<1);
/* 垂直方向的时间参数
* bit[31:24]: VBPD, VSYNC之后再过多长时间才能发出第1行数据
* LCD手册 tvb=2
* VBPD=2-1=1
* bit[23:14]: 多少行, 272, 所以LINEVAL=272-1=271
* bit[13:6] : VFPD, 发出最后一行数据之后,再过多长时间才发出VSYNC
* LCD手册tvf=2, 所以VFPD=2-1=1
* bit[5:0] : VSPW, VSYNC信号的脉冲宽度, LCD手册tvp=10, 所以VSPW=10-1=9
*/
lcd_regs->lcdcon2 = (1<<24) | (271<<14) | (1<<6) | (9<<0);
/* 水平方向的时间参数
* bit[25:19]: HBPD, VSYNC之后再过多长时间才能发出第1行数据
* LCD手册 thb=2
* HBPD=1
* bit[18:8]: 多少列, 480, 所以HOZVAL=480-1=479
* bit[7:0] : HFPD, 发出最后一行里最后一个象素数据之后,再过多长时间才发出HSYNC
* LCD手册thf=2, 所以HFPD=2-1=1
*/
lcd_regs->lcdcon3 = (1<<19) | (479<<8) | (1<<0);
/* 水平方向的同步信号
* bit[7:0] : HSPW, HSYNC信号的脉冲宽度, LCD手册Thp=41, 所以HSPW=41-1=40
*/
lcd_regs->lcdcon4 = 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 = (0<<10) | (1<<9) | (1<<8) | (0<<12) | (0<<1) | (0<<0);
lcd_regs->lcdsaddr1 = (s3clcdfb_info->fix.smem_start >> 1) & ~(3<<30);
lcd_regs->lcdsaddr2 = ((s3clcdfb_info->fix.smem_start +s3clcdfb_info->fix.smem_len ) >> 1) & 0x1fffff;
lcd_regs->lcdsaddr3 = (480*32/16); // 一行的长度(单位: 2字节)
lcd_regs->lcdcon1 |= (1<<0); //使能LCD控制器
lcd_regs->lcdcon5 |= (1<<3); //使能LCD本身: LCD_PWREN
register_framebuffer(s3clcdfb_info); // 4. 注册
return 0;
}
static void __exit lcd_exit(void)
{
unregister_framebuffer(s3clcdfb_info); //释放fb_info
lcd_regs->lcdcon1 &= ~(1<<0); /* 关闭LCD控制器 */
lcd_regs->lcdcon1 &= ~(1<<3); /* 关闭LCD本身 */
dma_free_writecombine(NULL, s3clcdfb_info->fix.smem_len, s3clcdfb_info->screen_base, s3clcdfb_info->fix.smem_start); //释放缓冲区
iounmap(lcd_regs); //取消映射
iounmap(gpbcon);
iounmap(gpccon);
iounmap(gpdcon);
iounmap(gpgcon);
framebuffer_release(s3clcdfb_info); //注销
}
module_init(lcd_init);
module_exit(lcd_exit);
MODULE_AUTHOR("shenchaoping"); //描述模块作者
MODULE_LICENSE("Dual BSD/GPL");//指定代码使用双重许可证
MODULE_VERSION("v1.0"); //模块版本
MODULE_DESCRIPTION("A lcd operation module"); //说明模块用途
MODULE_ALIAS("lcd"); //模块别名