概述
Table of Contents
一、应用程序访问驱动原理
二、LCD驱动编写步骤
2.1、.分配fb_info
2.2、初始化fb_info(var fix)
2.3、硬件寄存器操作
2.4、显存设置
2.5、注册
三、LCD驱动代码
一、应用程序访问驱动原理
LCD驱动程序
假设
app: open("/dev/fb0", ...) 主设备号: 29, 次设备号: 0
--------------------------------------------------------------
kernel:
fb_open
int fbidx = iminor(inode);
struct fb_info *info = = registered_fb[0];app: read()
---------------------------------------------------------------
kernel:
fb_read
int fbidx = iminor(inode);
struct fb_info *info = registered_fb[fbidx];
if (info->fbops->fb_read)
return info->fbops->fb_read(info, buf, count, ppos);
src = (u32 __iomem *) (info->screen_base + p);
dst = buffer;
*dst++ = fb_readl(src++);
copy_to_user(buf, buffer, c)
问1. registered_fb在哪里被设置?
答1. register_framebuffer
二、LCD驱动编写步骤
怎么写LCD驱动程序? linux 自带LCD驱动框架(三)
1. 分配一个fb_info结构体: framebuffer_alloc2. 初始化填充fb_info 成员 重要结构体解析
3. 硬件相关的操作 LCD数据手册解析
4. 注册: register_framebuffer
2.1、.分配fb_info
s5pv210_lcd = framebuffer_alloc(0, NULL);
2.2、初始化fb_info(var fix)
// 3.初始化fb_info
// 3.1初始化屏幕的固定参数信息
strcpy(s5pv210_lcd->fix.id, "s5pv210_lcd");
s5pv210_lcd->fix.smem_len = 800*480*4; //480*272*2
s5pv210_lcd->fix.type = FB_TYPE_PACKED_PIXELS;
s5pv210_lcd->fix.visual = FB_VISUAL_TRUECOLOR;
s5pv210_lcd->fix.line_length = 800*4; //480*2// 3.2初始化屏幕的可变参数信息
s5pv210_lcd->var.xres = 800; //480 开发板的屏幕是800*480 注释部分为480*272的屏幕
s5pv210_lcd->var.yres = 480; //272
s5pv210_lcd->var.xres_virtual = 800; //480
s5pv210_lcd->var.yres_virtual = 480; //272
s5pv210_lcd->var.bits_per_pixel = 32; //16
s5pv210_lcd->var.red.offset = 16; //11
s5pv210_lcd->var.red.length = 8;//5
s5pv210_lcd->var.green.offset = 8; //5
s5pv210_lcd->var.green.length = 8; //6
s5pv210_lcd->var.blue.offset = 0; //0
s5pv210_lcd->var.blue.length = 8; //5
s5pv210_lcd->var.activate = FB_ACTIVATE_NOW;
2.3、硬件寄存器操作
// 4.硬件初始化
// 4.1GPIO的复用处理
gpf0con = ioremap(0xE0200120,4);
gpf1con = ioremap(0xE0200140,4);
gpf2con = ioremap(0xE0200160,4);
gpf3con = ioremap(0xE0200180,4);
*gpf0con = 0x22222222; // GPF0[7:0]
*gpf1con = 0x22222222; // GPF1[7:0]
*gpf2con = 0x22222222; // GPF2[7:0]
*gpf3con = 0x22222222; // GPF3[7:0]// 4.2初始化LCD控制器相关的寄存器
vidcon0 = ioremap(0xF8000000,4);
vidcon1 = ioremap(0xF8000004,4);
wincon0 = ioremap(0xF8000020,4);
vidosd0a = ioremap(0xF8000040,4);
vidosd0b = ioremap(0xF8000044,4);
vidosd0c = ioremap(0xF8000048,4);
vidw00add0b0 = ioremap(0xF80000A0,4);
vidw00add1b0 = ioremap(0xF80000D0,4);
vidw00add2 = ioremap(0xF8000100,4);
vidtcon0 = ioremap(0xF8000010,4);
vidtcon1 = ioremap(0xF8000014,4);
vidtcon2 = ioremap(0xF8000018,4);
wpalcon = ioremap(0xF80001A0,4);
shadowcon = ioremap(0xF8000034,4);
*vidcon0 &= ~((3<<26) | (1<<18) | (0xff<<6) | (1<<2)); /* RGB I/F, RGB Parallel format, */
*vidcon0 |= ((4<<6) | (1<<4) ); /* Divided by CLKVAL_F,vclk== HCLK / (CLKVAL+1) = 166.75/5 = 33.35MHz */
*vidcon1 &= ~(1<<7);
*vidcon1 |= ((1<<6) | (1<<5));// 这个硬件时序初始化一定要会换算!
// 一旦屏幕发生变化,这些参数也要重新换算
*vidtcon0 = (VBPD << 16) | (VFPD << 8) | (VSPW << 0);
*vidtcon1 = (HBPD << 16) | (HFPD << 8) | (HSPW << 0);*vidtcon2 = (LINEVAL << 11) | (HOZVAL << 0);
*wincon0 &= ~(0xf << 2);
*wincon0 |= (0xB<<2)/*|(1<<15)*/;
*vidosd0a = (LeftTopX<<11) | (LeftTopY << 0);
*vidosd0b = (RightBotX<<11) | (RightBotY << 0);
*vidosd0c = (LINEVAL + 1) * (HOZVAL + 1);
2.4、显存设置
// 3.5 让内核帮你分配显存的起始物理地址和对应的内核虚拟地址
//内核分配好以后,把显存的物理地址保存在fix.smem_start
//对应的显存的内核虚拟地址保存在screen_base
s5pv210_lcd->screen_base = dma_alloc_writecombine(NULL,
s5pv210_lcd->fix.smem_len,
(dma_addr_t *)&s5pv210_lcd->fix.smem_start,
GFP_KERNEL);//告诉CPU显存的起始物理地址和结束物理地址
*vidw00add0b0 = s5pv210_lcd->fix.smem_start;
*vidw00add1b0 = s5pv210_lcd->fix.smem_start
+ s5pv210_lcd->fix.smem_len;
2.5、注册
//向核心层注册分配初始化好的fb_info
register_framebuffer(s5pv210_lcd);
三、LCD驱动代码
#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/platform_device.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <asm/io.h>
#include <asm/div64.h>
#include <asm/mach/map.h>
#include <mach/regs-gpio.h>
#define MHZ (1000*1000)
#define PRINT_MHZ(m)
((m) / MHZ), ((m / 1000) % 1000)
#define VSPW
9
#define VBPD
13
#define LINEVAL
479
#define VFPD
21
#define HSPW
19
#define HBPD
25
#define HOZVAL
799
#define HFPD
209
#define LeftTopX
0
#define LeftTopY
0
#define RightBotX
799
#define RightBotY
479
static unsigned long *vidcon0; /* video control 0 */
static unsigned long *vidcon1; /* video control 1 */
//static unsigned long *vidcon2; /* video control 2 */
static unsigned long *vidtcon0; /* video time control 0 */
static unsigned long *vidtcon1; /* video time control 1 */
static unsigned long *vidtcon2; /* video time control 2 */
static unsigned long *wincon0; /* window control 0 */
static unsigned long *vidosd0a; /* video window 0 position control */
static unsigned long *vidosd0b; /* video window 0 position control1 */
static unsigned long *vidosd0c; /* video window 0 position control */
static unsigned long *vidw00add0b0;
/* window 0 buffer start address, buffer 0 */
static unsigned long *vidw00add1b0;
/* window 0 buffer end address, buffer 0 */
static unsigned long *vidw00add2;
/* window 0 buffer size */
static unsigned long *wpalcon;
static unsigned long *shadowcon;
static unsigned long *gpf0con;
static unsigned long *gpf1con;
static unsigned long *gpf2con;
static unsigned long *gpf3con;
//static unsigned long *gpd0con;
//static unsigned long *gpd0dat;
//static unsigned long *clk_gate_block;
//static unsigned long *display_control;
static struct fb_info *s5pv210_lcd;
static struct fb_ops s5pv210_lcdfb_ops = {
.owner
= THIS_MODULE,
.fb_fillrect = cfb_fillrect, //填充矩形
.fb_copyarea = cfb_copyarea, //显存区域的拷贝
.fb_imageblit = cfb_imageblit, //处理图像
};
static int s5pv210_lcd_init(void)
{
// 1.获取LCD控制器的时钟,并且启动LCD控制器时钟
struct clk
*s5pv210_clk;
s5pv210_clk = clk_get(NULL, "lcd");
clk_enable(s5pv210_clk);
// 2.分配fb_info
s5pv210_lcd = framebuffer_alloc(0, NULL);
// 3.初始化fb_info
// 3.1初始化屏幕的固定参数信息
strcpy(s5pv210_lcd->fix.id, "s5pv210_lcd");
s5pv210_lcd->fix.smem_len = 800*480*4; //480*272*2
s5pv210_lcd->fix.type = FB_TYPE_PACKED_PIXELS;
s5pv210_lcd->fix.visual = FB_VISUAL_TRUECOLOR;
s5pv210_lcd->fix.line_length = 800*4; //480*2
// 3.2初始化屏幕的可变参数信息
s5pv210_lcd->var.xres
= 800; //480
s5pv210_lcd->var.yres
= 480; //272
s5pv210_lcd->var.xres_virtual
= 800;
//480
s5pv210_lcd->var.yres_virtual
= 480;
//272
s5pv210_lcd->var.bits_per_pixel = 32; //16
s5pv210_lcd->var.red.offset
= 16; //11
s5pv210_lcd->var.red.length
= 8;//5
s5pv210_lcd->var.green.offset
= 8; //5
s5pv210_lcd->var.green.length
= 8; //6
s5pv210_lcd->var.blue.offset
= 0; //0
s5pv210_lcd->var.blue.length
= 8; //5
s5pv210_lcd->var.activate
= FB_ACTIVATE_NOW;
// 3.3提供操作显存的接口
s5pv210_lcd->fbops = &s5pv210_lcdfb_ops;
// 3.4 初始化屏幕的大小
s5pv210_lcd->screen_size = 800*480*4;
// 4.硬件初始化
// 4.1GPIO的复用处理
gpf0con = ioremap(0xE0200120,4);
gpf1con = ioremap(0xE0200140,4);
gpf2con = ioremap(0xE0200160,4);
gpf3con = ioremap(0xE0200180,4);
*gpf0con = 0x22222222;
// GPF0[7:0]
*gpf1con = 0x22222222;
// GPF1[7:0]
*gpf2con = 0x22222222;
// GPF2[7:0]
*gpf3con = 0x22222222;
// GPF3[7:0]
// 4.2初始化LCD控制器相关的寄存器
vidcon0 = ioremap(0xF8000000,4);
vidcon1 = ioremap(0xF8000004,4);
wincon0 = ioremap(0xF8000020,4);
vidosd0a = ioremap(0xF8000040,4);
vidosd0b = ioremap(0xF8000044,4);
vidosd0c = ioremap(0xF8000048,4);
vidw00add0b0
= ioremap(0xF80000A0,4);
vidw00add1b0
= ioremap(0xF80000D0,4);
vidw00add2
= ioremap(0xF8000100,4);
vidtcon0
= ioremap(0xF8000010,4);
vidtcon1
= ioremap(0xF8000014,4);
vidtcon2
= ioremap(0xF8000018,4);
wpalcon
= ioremap(0xF80001A0,4);
shadowcon
= ioremap(0xF8000034,4);
*vidcon0 &= ~((3<<26) | (1<<18) | (0xff<<6)
| (1<<2));
/* RGB I/F, RGB Parallel format,
*/
*vidcon0 |= ((4<<6) | (1<<4) );
/* Divided by CLKVAL_F,vclk== HCLK / (CLKVAL+1) = 166.75/5 = 33.35MHz */
*vidcon1 &= ~(1<<7);
*vidcon1 |= ((1<<6) | (1<<5));
// 这个硬件时序初始化一定要会换算!
// 一旦屏幕发生变化,这些参数也要重新换算
*vidtcon0 = (VBPD << 16) | (VFPD << 8) | (VSPW << 0);
*vidtcon1 = (HBPD << 16) | (HFPD << 8) | (HSPW << 0);
*vidtcon2 = (LINEVAL << 11) | (HOZVAL << 0);
*wincon0 &= ~(0xf << 2);
*wincon0 |= (0xB<<2)/*|(1<<15)*/;
*vidosd0a = (LeftTopX<<11) | (LeftTopY << 0);
*vidosd0b = (RightBotX<<11) | (RightBotY << 0);
*vidosd0c = (LINEVAL + 1) * (HOZVAL + 1);
// 3.5 让内核帮你分配显存的起始物理地址和对应的内核虚拟地址
//内核分配好以后,把显存的物理地址保存在fix.smem_start
//对应的显存的内核虚拟地址保存在screen_base
s5pv210_lcd->screen_base = dma_alloc_writecombine(NULL,
s5pv210_lcd->fix.smem_len,
(dma_addr_t *)&s5pv210_lcd->fix.smem_start,
GFP_KERNEL);
//告诉CPU显存的起始物理地址和结束物理地址
*vidw00add0b0 = s5pv210_lcd->fix.smem_start;
*vidw00add1b0 = s5pv210_lcd->fix.smem_start
+ s5pv210_lcd->fix.smem_len;
*shadowcon = 0x1;
*vidcon0 |= 0x3;
*wincon0 |= 1;
//向核心层注册分配初始化好的fb_info
register_framebuffer(s5pv210_lcd);
return 0;
}
static void s5pv210_lcd_exit(void)
{
unregister_framebuffer(s5pv210_lcd);
dma_free_writecombine(NULL,
s5pv210_lcd->fix.smem_len,
s5pv210_lcd->screen_base,
s5pv210_lcd->fix.smem_start);
iounmap(gpf0con);
iounmap(gpf1con);
iounmap(gpf2con);
iounmap(gpf3con);
//iounmap(gpd0con);
//iounmap(gpd0dat);
//iounmap(display_control);
iounmap(vidcon0);
iounmap(vidcon1);
iounmap(vidtcon2);
iounmap(wincon0);
iounmap(vidosd0a);
iounmap(vidosd0b);
iounmap(vidosd0c);
iounmap(vidw00add0b0);
iounmap(vidw00add1b0);
iounmap(vidw00add2);
iounmap(vidtcon0);
iounmap(vidtcon1);
iounmap(shadowcon);
framebuffer_release(s5pv210_lcd);
}
module_init(s5pv210_lcd_init);
module_exit(s5pv210_lcd_exit);
MODULE_LICENSE("GPL");
最后
以上就是喜悦野狼为你收集整理的linux LCD 驱动编写(四)一、应用程序访问驱动原理二、LCD驱动编写步骤三、LCD驱动代码的全部内容,希望文章能够帮你解决linux LCD 驱动编写(四)一、应用程序访问驱动原理二、LCD驱动编写步骤三、LCD驱动代码所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复