1.LCD内核框架
所谓的驱动程序就是调用LINUX内核提供的函数来操作硬件,如LCD,IIC 设备
内核自带的LCD驱动程序drivers/video/samsung/s3cfb.c
这个驱动程序通过提供最底层的操作来被核心层调用,这个核心层提供了应用程序所需open/close ,read/write
这个核心层到底是啥呢?
drivers/video/fbmem.c , 它提供了对LCD的所有抽象操作
2.硬件接线及原理
VDD_LCD:LCD电源引脚
VD0-VD23: video data引脚,lcd控制器发的数据在这几根线上
LVDEN: video data 使能引脚
LHSYNC: 能发出HSYNC信号,用于切换到下一行扫描
L
VSYNC: 能发出VSYNC信号,用于返回到最开始行的扫描
LVCLK:控制器发给lcd的时钟引脚,根据lcd手册设置
3.几个重要的结构体
/include/uapi/linux/fb.h中的
-
struct fb_var_screeninfo {
-
__u32 xres; // X物理坐标
-
__u32 yres; // Y物理坐标
-
__u32 xres_virtual; // X虚拟坐标
-
__u32 yres_virtual; // Y虚拟坐标
-
__u32 xoffset; // X虚拟坐标与物理坐标差距
-
__u32 yoffset; // Y虚拟坐标与物理坐标差距
-
__u32 bits_per_pixel; // 每一个像素占几位
-
__u32 grayscale; //灰度模式
-
struct fb_bitfield red; //绿色位域
-
struct fb_bitfield green; //绿色位域
-
struct fb_bitfield blue; //蓝色位域
-
struct fb_bitfield transp; //透明色
-
__u32 nonstd; // = 1 非标准,= 0 标准
-
__u32 activate; // 活动模式
-
__u32 height; // 高,物理尺寸/mm
-
__u32 width; // 宽,物理尺寸/mm
-
__u32 accel_flags; // 加速标志
-
__u32 pixclock; // 像素时钟
-
__u32 left_margin; //左边框,驱动程序一般不设置
-
__u32 right_margin; //右边框,驱动程序一般不设置
-
__u32 upper_margin; // 上边框,驱动程序一般不设置
-
__u32 lower_margin;//底边框,驱动程序一般不设置
-
__u32 hsync_len; // 行同步信号长度
-
__u32 vsync_len; // 垂直同步信号长度
-
__u32 sync; /*see FB_SYNC_ */
-
__u32 vmode; /* see FB_VMODE_ */
-
__u32 reserved[6];// 预留字节
-
};
-
struct fb_bitfield {
-
_u32 offset; // 位域偏移
-
__u32 length; // 位域长度
-
__u32 msb_right; //=1,最重要一位在右边
-
//=0,最重要一位在左边
-
};
4.驱动的编写步骤
a.分配核心结构体fb_info
使用函数s3c_lcd = framebuffer_alloc(0, NULL);
每一个lcd驱动程序都要分配一个fb_info结构体,framebuffer_alloc的用法可以参考其他lcd驱动,第一个参数0表示只需要分配本结构体的长度,第二个NULL,表示无device结构
返回值为fb_info结构体
b.设置结构体成员
设置
s3c_lcd内的各个结构体成员的值,如:
s3c_lcd->fix.smem_len = 480 *272*32/8;
s3c_lcd->fix.type = FB_TYPE_PACKED_PIXELS;
s3c_lcd->fix.visual = FB_VISUAL_TRUECOLOR;
s3c_lcd->fix.line_length = 480 *4;
c.硬件相关的操作
配置GPIO用于LCD
根据LCD手册设置LCD控制器,比如VCLK的频率等
分配显存(framebuffer),并把地址告诉LCD控制器
d.注册
使用函数register_framebuffer(s3c_lcd);
5.LCD驱动的代码如下
-
#include <linux/module.h>
-
#include <linux/kernel.h>
-
#include <linux/errno.h>
-
#include <linux/string.h>
-
#include <linux/mm.h>
-
#include <linux/tty.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 <mach/regs-gpio.h>
-
#include <mach/regs-fb.h>
-
#include <mach/regs-lcd.h>
-
-
static struct fb_info *s3c_lcd;
-
static volatile struct lcd_regs* lcd_regs;
-
static volatile unsigned long *mifpcon;
-
static volatile unsigned long *spcon;
-
static volatile unsigned long *gpfcon;
-
static volatile unsigned long *gpfdat;
-
static volatile unsigned long *gpicon;
-
static volatile unsigned long *gpjcon;
-
static u32 pseudo_palette[16];
-
-
struct lcd_regs{
-
unsigned long vidcon0;
-
unsigned long vidcon1;
-
unsigned long vidcon2;
-
unsigned long reserver1;
-
unsigned long vidtcon0;
-
unsigned long vidtcon1;
-
unsigned long vidtcon2;
-
unsigned long reserver2;
-
unsigned long wincon0;
-
unsigned long wincon1;
-
unsigned long wincon2;
-
unsigned long wincon3;
-
unsigned long wincon4;
-
unsigned long reserver3[3];
-
unsigned long vidosd0a;
-
unsigned long vidosd0b;
-
unsigned long vidosd0c;
-
unsigned long reserver4;
-
unsigned long vidosd1a;
-
unsigned long vidosd1b;
-
unsigned long vidosd1c;
-
unsigned long vidosd1d;
-
unsigned long vidosd2a;
-
unsigned long vidosd2b;
-
unsigned long vidosd2c;
-
unsigned long vidosd2d;
-
unsigned long vidosd3a;
-
unsigned long vidosd3b;
-
unsigned long vidosd3c;
-
unsigned long reserver5;
-
unsigned long vidosd4a;
-
unsigned long vidosd4b;
-
unsigned long vidosd4c;
-
unsigned long reserver6[5];
-
unsigned long vidw00add0b0;
-
unsigned long vidw00add0b1;
-
unsigned long vidw01add0b0;
-
unsigned long vidw01add0b1;
-
unsigned long vidw02add0;
-
unsigned long reserver7;
-
unsigned long vidw03add0;
-
unsigned long reserver8;
-
unsigned long vidw04add0;
-
unsigned long reserver9[3];
-
unsigned long vidw00add1b0;
-
unsigned long vidw00add1b1;
-
};
-
-
/*调色板*/
-
static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, unsigned int transp, struct fb_info *info)
-
{
-
u32 color = 0;
-
u32 *p;
-
-
red = (red >> (16 - info->var.red.length)) << info->var.red.offset; // 0xAA0000
-
green = (green >> (16 - info->var.green.length)) << info->var.green.offset; // 0xCC00
-
blue = (blue >> (16 - info->var.blue.length)) << info->var.blue.offset; // 0xEE
-
-
color = red | green | blue; // 0xAACCEE
-
p = info->pseudo_palette;
-
p[regno] = color;
-
-
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 lcd_init(void)
-
{
-
struct clk *clk;
-
int hclk;
-
int clkval;
-
-
/*1.分配一个fb_info结构体*/
-
s3c_lcd = framebuffer_alloc(0, NULL);
-
-
/*2.设置*/
-
//2.1设置固定的参数
-
strcpy(s3c_lcd->fix.id, "mylcd");
-
s3c_lcd->fix.smem_len = 480*272*32/8;//看lcd手册,4位
-
s3c_lcd->fix.type = FB_TYPE_PACKED_PIXELS;//在fb.h里的FB_TYPE里第一个
-
s3c_lcd->fix.visual = FB_VISUAL_TRUECOLOR;//TFT屏是真彩色
-
s3c_lcd->fix.line_length = 480*32/8 ;//一行480个像素,一个像素4个字节(32bit)
-
//2.2设置可变的参数
-
s3c_lcd->var.xres = 480;
-
s3c_lcd->var.yres = 272;
-
s3c_lcd->var.xres_virtual = 480;
-
s3c_lcd->var.yres_virtual = 272;
-
s3c_lcd->var.bits_per_pixel = 32;//每个像素用多少位
-
/*RGB:888*/
-
s3c_lcd->var.red.offset = 16;//从16位开始,长度为8
-
s3c_lcd->var.red.length = 8;
-
s3c_lcd->var.green.offset = 8;
-
s3c_lcd->var.green.length = 8;
-
s3c_lcd->var.blue.offset = 0;
-
s3c_lcd->var.blue.length = 8;
-
-
s3c_lcd->var.activate = FB_ACTIVATE_NOW;// 看不懂就用默认值为0 的
-
-
-
//2.3设置操作函数
-
s3c_lcd->fbops = &s3c_lcdfb_ops;
-
-
//2.4其他设置
-
s3c_lcd->pseudo_palette = pseudo_palette;/*调色板*/
-
//3c_lcd->screen_base = ;/*显存的虚拟地址*/
-
s3c_lcd->screen_size = 480*272*32/8;
-
-
-
/*3.硬件相关操作*/
-
//3.1配置GPIO用于LCD
-
gpicon = ioremap(0x7F008100, 4);
-
*gpicon = 0xAAAAAAAA;
-
gpjcon = ioremap(0x7F008120, 4);
-
*gpjcon = 0xAAAAAA;
-
-
gpfcon = ioremap(0x7F0080A0, 4);//把GPF14设置为CLKOUT功能
-
gpfdat = gpfcon + 1;
-
*gpfcon &= ~(3<<28);
-
*gpfcon |= (1<<28);
-
-
mifpcon = ioremap(0x7410800C, 4);//参考6410芯片手册,When LCD Bypass mode is set (MIFPCON[3]=1) in sleep mode, GPISLPCON and GPIPUDSLP cannot control I port.
-
*mifpcon &= ~(1<<3);
-
spcon = ioremap(0x7F0081A0, 4);//参考6410芯片手册,SPCON: LCD_SEL[1:0] value @ 0x7F0081A0 must be set as ‘00’ to use Host I/F Style or as ‘01’ to use RGB I/F Style
-
*spcon &= ~(3);
-
*spcon |= 1;
-
-
//3.2根据LCD手册设置LCD控制器,比如VCLK的频率等
-
lcd_regs = ioremap(0x77100000, sizeof(struct lcd_regs));
-
-
clk = clk_get(NULL, "lcd");//struct clk *clk_get(struct device *dev, const char *id)
-
clk_enable(clk);
-
-
clk = clk_get(NULL, "hclk");
-
hclk = clk_get_rate(clk);
-
clkval = hclk/9000000 - 1;
-
printk("hclk = %d, clkval = %d\n", hclk, clkval);
-
-
lcd_regs->vidcon0 = (0<<29) | (0<<26) | (3<<23) | (3<<20) | (0<<17) | (clkval<<6) | (1<<4);
-
lcd_regs->vidcon1 = (0<<7) | (1<<6) | (1<<5) | (0<<4);/* 在vclk的下降沿获取数据, HSYNC高电平有效, VSYNC高电平有效 */
-
lcd_regs->vidtcon0 = (1<<16) | (1<<8) | (9<<0);//垂直
-
lcd_regs->vidtcon1 = (1<<16) | (1<<8) | (40<<0);//水平
-
lcd_regs->vidtcon2 = (271<<11) | (479<<0);
-
//配置窗口0
-
lcd_regs->wincon0 &= ~(0xf << 2);
-
lcd_regs->wincon0 |= (0xb<<2);
-
lcd_regs->vidosd0a = (0<<11) | (0<<0);
-
lcd_regs->vidosd0b = (479<<11) | (271<< 0);
-
lcd_regs->vidosd0c = 480*272;//窗口尺寸
-
-
-
//3.3分配显存(framebuffer),并把地址告诉LCD控制器
-
s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->screen_size, (dma_addr_t *)&s3c_lcd->fix.smem_start, GFP_KERNEL);
-
lcd_regs->vidw00add0b0 = s3c_lcd->fix.smem_start;
-
lcd_regs->vidw00add1b0 = (s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len) & 0xffffff;
-
-
//s3c_lcd->fix.smem_start = ;/*显存的物理地址*/
-
/*启动/使能LCD*/
-
*gpfdat |= (1<<14);
-
lcd_regs->vidcon0 |= (3);
-
lcd_regs->wincon0 |= (1<<0);
-
-
/*4.注册*/
-
register_framebuffer(s3c_lcd);
-
return 0;
-
}
-
-
static void lcd_exit(void)
-
{
-
struct clk *clk;
-
-
unregister_framebuffer(s3c_lcd);
-
*gpfdat &= ~(1<<14);
-
lcd_regs->vidcon0 &= ~(1<<1);
-
lcd_regs->wincon0 &= ~(1<<0);
-
-
clk = clk_get(NULL, "lcd");
-
clk_disable(clk);
-
iounmap(lcd_regs);
-
iounmap(gpfcon);
-
iounmap(gpicon);
-
iounmap(gpjcon);
-
iounmap(mifpcon);
-
iounmap(spcon);
-
dma_free_writecombine(NULL, s3c_lcd->screen_size, s3c_lcd->screen_base, s3c_lcd->fix.smem_start);
-
framebuffer_release(s3c_lcd);
-
-
}
-
-
module_init(lcd_init);
-
module_exit(lcd_exit);
-
-
MODULE_LICENSE("GPL");
6.测试
首先要配置内核,去除内核中的LCD驱动,步骤如下
Device Drivers --->
Graphics support --->
< > S3C Framebuffer Support (eXtended)
<*> Support for frame buffer devices --->
Samsung S3C Framebuffer Support
重新make uImage,并make modules,把新内核烧到开发板。
然后把/drivers/video中的cfbcopyarea.ko 、cfbfillrect.ko 、cfbimgblt.ko 加载到开发板
测试1:
重启开发板,然后执行以下命令
insmod cfbcopyarea.ko
insmod cfbfillrect.ko
insmod cfbimgblt.ko
insmod lcd.ko
echo hello > /dev/tty1
可以看到屏幕打印了文字hello
cat lcd.ko > /dev/fb0
可以看到花屏了
测试2:
在开发板的文件系统的/etc/inittab文件中添加一行tty1::askfirst:-/bin/sh
重启开发板,然后执行以下命令
insmod cfbcopyarea.ko
insmod cfbfillrect.ko
insmod cfbimgblt.ko
insmod lcd.ko
可以看到有文字在屏幕上显示,再装载我们之前做的输入子系统的按键驱动
insmod button_input.ko
就可以在开发板屏幕上看到shell命令行了
阅读(1165) | 评论(0) | 转发(0) |