一、帧缓冲设备驱动在Linux子系统中的结构:
二、帧缓冲相关的重要数据结构
从帧缓冲设备驱动程序结构 看,该驱动主要跟fb_info结构体有关,该结构体记录了帧缓冲设备的全部信息,包括设备的设置参数、状态以及对底层硬件操作的函数指针。在Linux 中,帧缓冲设备最关键的一个数据结构体是fb_info,fb_info中包括了关于帧缓冲设备属性和操作的完整描述。每一个帧缓冲设备都必须对应一个fb_info,fb_info在/linux/fb.h中的定义如下:(只列出重要的一些)
2.1 fb_info结构体
struct fb_info {
int node;
int flags;
struct mutex lock;
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
struct fb_monspecs monspecs;
struct work_struct queue;
struct fb_pixmap pixmap;
struct fb_pixmap sprite;
struct fb_cmap cmap;
struct fb_videomode *mode;
#ifdef CONFIG_FB_BACKLIGHT
struct backlight_device *bl_dev;
struct mutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif
#ifdef CONFIG_FB_DEFERRED_IO
struct delayed_work deferred_work;
struct fb_deferred_io *fbdefio;
#endif
struct fb_ops *fbops;
struct device *device;
struct device *dev;
int class_flag;
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops;
#endif
char __iomem *screen_base;
unsigned long screen_size;
void *pseudo_palette;
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state;
void *fbcon_par;
void *par;
};
其中,比较重要的成员有struct fb_var_screeninfo var、struct fb_fix_screeninfo fix和struct fb_ops *fbops,他们也都是结构体,下面我们一个一个的来看。
2.2 struct fb_var_screeninfo 结构体
fb_var_screeninfo结构体主要记录用户可以修改的控制器的参数,比如屏幕的分辨率和每个像素的比特数等。例如:fb_var_screeninfo中的xres定义屏幕一行有多少个点,yres定义一屏幕一列有多少个点,bits_per_pixel定义每个点用多少个自己表示。该结构体定义如下:
struct fb_var_screeninfo {
__u32 xres;
__u32 yres;
__u32 xres_virtual;
__u32 yres_virtual;
__u32 xoffset;
__u32 yoffset;
__u32 bits_per_pixel;
__u32 grayscale;
struct fb_bitfield red;
struct fb_bitfield green;
struct fb_bitfield blue;
struct fb_bitfield transp;
__u32 nonstd;
__u32 activate;
__u32 height;
__u32 width;
__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;
__u32 vmode;
__u32 rotate;
__u32 reserved[5];
};
2.3 struct fb_fix_screeninfo结构体
fb_fix_screeninfo结构体又主要记录用户不可以修改的控制器的参数,比如屏幕缓冲区的物理地址和长度等。当帧缓冲设备进行映射操作的时候,就是从fb_fix_screeninfo中取得缓冲区物理地址。该结构体的定义如下:
struct fb_fix_screeninfo {
char id[16];
unsigned long smem_start;
__u32 smem_len;
__u32 type;
__u32 type_aux;
__u32 visual;
__u16 xpanstep;
__u16 ypanstep;
__u16 ywrapstep;
__u32 line_length;
unsigned long mmio_start;
__u32 mmio_len;
__u32 accel;
__u16 reserved[3];
};
2.4 fb_ops结构体
fb_ops结构体是对底层硬件操作的函数指针,该结构体中定义了对硬件的操作有:(这里只列出了常用的操作)
struct fb_ops {
struct module *owner;
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
int (*fb_set_par)(struct fb_info *info);
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp, struct fb_info *info);
int (*fb_blank)(int blank, struct fb_info *info);
void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
};
其中操作函数fb_info-> fbops 结构体写法如下:
static struct fb_ops s3c_lcdfb_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = my_lcdfb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
三、帧缓冲相关的重要函数
3.1 申请一个fb_info结构体
函数原型:struct fb_info *framebuffer_alloc(size_t size, struct device *dev);
参数说明 :size 额外的内存
*dev 指针, 这里填0,表示这个申请的结构体里没有内容
3.2 fb_info的注册与注销
函数原型:int register_framebuffer(struct fb_info *fb_info)
参数说明 : fb_info 待注册的fb_info对象
向内核中注册fb_info结构体,若内存不够,注册失败会返回负数
函数原型:int unregister_framebuffer(struct fb_info *fb_info)
说明 :注销内核中fb_info结构体
3.3 分配DMA缓存区给显存
分配一段DMA缓存区,分配出来的内存会禁止cache缓存(因为DMA传输不需要CPU);
它和 dma_alloc_coherent ()函数相似,不过 dma_alloc_coherent ()函数是分配出来的内存会禁止cache缓存以及禁止写入缓冲区。
函数原型:void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t handle, gfp_t gfp)
参数说明 :dev 指针,这里填0,表示这个申请的缓冲区里没有内容
size 分配的地址大小(字节单位)
*handle 申请到的物理起始地址
gfp 分配出来的内存参数,标志定义在<linux/gfp.h>,常用标志如下:
返回值: 申请到的DMA缓冲区的虚拟地址,若为NULL,表示分配失败,则需要使用dma_free_writecombine()释放内存,避免内存泄漏
四、驱动实现
在驱动init入口函数中:
1)分配一个fb_info结构体
2)设置fb_info
2.1)设置固定的参数fb_info-> fix
2.2) 设置可变的参数fb_info-> var
2.3) 设置操作函数fb_info-> fbops
2.4) 设置fb_info 其它的成员
3)设置硬件相关的操作
3.1)配置LCD引脚
3.2)根据LCD手册设置LCD控制器
3.3)分配显存(framebuffer),把地址告诉LCD控制器和fb_info
4)开启LCD,并注册fb_info: register_framebuffer()
4.1) 直接在init函数中开启LCD(后面讲到电源管理,再来优化)
控制LCDCON5允许PWREN信号,
然后控制LCDCON1输出PWREN信号,
输出GPB0高电平来开背光,
4.2) 注册fb_info
在驱动exit出口函数中:
1)卸载内核中的fb_info
控制LCDCON1关闭PWREN信号,关背光,iounmap注销地址
3)释放DMA缓存地址dma_free_writecombine()
4)释放注册的fb_info
代码实现如下:
#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>
#define LCD_xres 480
#define LCD_yres 272
static unsigned long *GPBcon;
static unsigned long *GPCcon;
static unsigned long *GPDcon;
static unsigned long *GPGcon;
static unsigned long *GPBdat;
struct lcd_reg{
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 tconsel;
};
static struct lcd_reg *lcd_reg;
static struct fb_info *my_lcd;
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 my_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;
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;
return 0;
}
static struct fb_ops my_lcdfb_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = my_lcdfb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
static int lcd_init(void)
{
my_lcd= framebuffer_alloc(0,0);
strcpy(my_lcd->fix.id, "mylcd");
my_lcd->fix.smem_len =LCD_xres*LCD_yres*2;
my_lcd->fix.type =FB_TYPE_PACKED_PIXELS;
my_lcd->fix.visual =FB_VISUAL_TRUECOLOR;
my_lcd->fix.line_length =LCD_xres*2;
my_lcd->var.xres =LCD_xres;
my_lcd->var.yres =LCD_yres;
my_lcd->var.xres_virtual =LCD_xres;
my_lcd->var.yres_virtual =LCD_yres;
my_lcd->var.xoffset = 0;
my_lcd->var.yoffset =0;
my_lcd->var.bits_per_pixel=16;
my_lcd->var.grayscale = 0;
my_lcd->var.red.offset = 11;
my_lcd->var.red.length = 5;
my_lcd->var.green.offset = 5;
my_lcd->var.green.length = 6;
my_lcd->var.blue.offset = 0;
my_lcd->var.blue.length = 5;
my_lcd->fbops = &my_lcdfb_ops;
my_lcd->pseudo_palette =pseudo_palette;
my_lcd->screen_size =LCD_xres * LCD_yres *2;
GPBcon = ioremap(0x56000010, 8);
GPBdat = GPBcon+1;
GPCcon = ioremap(0x56000020, 4);
GPDcon = ioremap(0x56000030, 4);
GPGcon = ioremap(0x56000060, 4);
*GPBcon &=~(0x03<<(0*2));
*GPBcon |= (0x01<<(0*2));
*GPBdat &=~(0X1<<0);
*GPCcon =0xaaaaaaaa;
*GPDcon =0xaaaaaaaa;
*GPGcon |=(0x03<<(4*2));
lcd_reg=ioremap(0X4D000000, sizeof( lcd_reg) );
lcd_reg->lcdcon1 = (4<<8) | (0X3<<5) | (0x0C<<1) ;
lcd_reg->lcdcon2 = ((3)<<24) | (271<<14) | ((1)<<6) |((0)<<0);
lcd_reg->lcdcon3 = ((16)<<19) | (479<<8) | ((10));
lcd_reg->lcdcon4 = (4);
lcd_reg->lcdcon5 = (1<<11) | (1<<9) | (1<<8) |(1<<0);
lcd_reg->lcdcon1 &=~(1<<0);
lcd_reg->lcdcon5 &=~(1<<3);
my_lcd->screen_base=dma_alloc_writecombine(0,my_lcd->fix.smem_len, &my_lcd->fix.smem_start, GFP_KERNEL);
lcd_reg->lcdsaddr1 =(my_lcd->fix.smem_start>>1)&0X3FFFFFFF;
lcd_reg->lcdsaddr2 =((my_lcd->fix.smem_start+my_lcd->screen_size)>>1)&0X1FFFFF;
lcd_reg->lcdsaddr3 =LCD_xres& 0x3ff;
lcd_reg->lcdcon1 |=1<<0;
lcd_reg->lcdcon5 |=1<<3;
*GPBdat |=(0X1<<0);
register_framebuffer(my_lcd);
return 0;
}
static int lcd_exit(void)
{
unregister_framebuffer(my_lcd);
lcd_reg->lcdcon1 &=~(1<<0);
lcd_reg->lcdcon5 &=~(1<<3);
*GPBdat &=~(0X1<<4);
iounmap(GPBcon);
iounmap(GPCcon);
iounmap(GPDcon);
iounmap(GPGcon);
dma_free_writecombine(0,my_lcd->screen_size,my_lcd->screen_base,my_lcd->fix.smem_start);
framebuffer_release(my_lcd);
return 0;
}
module_init(lcd_init);
module_exit(lcd_exit);
MODULE_LICENSE("GPL");
五、测试
5.1重新编译内核,去掉默认的LCD
make menuconfig ,进入menu菜单重新设置内核参数:
-> Device Drivers
-> Graphics support:
<M> S3C2410 LCD framebuffer support
然后make uImage 编译内核
make modules 编译模块
为什么要编译模块?
因为LCD驱动相关的文件也没有编进内核,而fb_ops里的成员fb_fillrect(), fb_copyarea(), fb_imageblit()用的都是drivers/video下面的3个文件,所以需要这3个的.ko模块,如下图所示:
5.2挂载驱动
将编译好的LCD驱动模块 和drivers/video里的3个.ko模块 放入nfs文件系统目录中
然后烧写内核, 先装载3个/drivers/video下编译好的模块,再来装载LCD驱动模块
挂载LCD驱动后, 如下图,可以通过 ls -l /dev/fb* 命令查看已挂载的LCD设备节点:
$ ls /dev/fb*
$ /dev/fb0
5.3 测试运行
echo hello > /dev/tty1 // LCD上便显示hello字段
cat Makefile > /dev/tty1 // LCD上便显示Makeflie文件的内容(花屏)
转载链接:https://www.jianshu.com/p/785962e6c357
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)