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");
#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");
相关文章推荐
- 运维知识:RAID磁盘阵列配置和调优小结
- 1. Two Sum
- Spring基础—Bean容器
- OpenERP|ODOO高德地图应用
- 网络配置与挂载
- 1004 Financial Management
- left join中on和where的区别是什么
- Define your own binary operators
- Javascript 异步加载详解
- Hibernate中的一级缓存、二级缓存和懒加载
- 二叉排序树(三序输出)
- 查找代码错误.java
- 动态计算tablviewcell高度
- java创建邮件模板
- UI Components的理解
- 挂载分区
- 3.Android群英传读书笔记-控件架构与自定义控件
- Android中数据文件解析(Json解析)
- 剑指offer(五十七)之二叉树的下一个结点
- C#操作 word代码