怎样写 Linux LCD Linux驱动程序运行在

液晶显示屏Linux系统驱动程序开发液晶显示屏Linux系统驱动程序开发优奕视界显示屏百家号随着高性能嵌入式处理器的普及和硬件成本的不断降低,尤其是ARM 系列处理器的推出,嵌入式系统的功能越来越强。单色LCD液晶屏也因为色彩单调,存储信息小,处理速度慢而不能符合人们的需求。在多媒体应用的推动下,彩色LCD液晶屏越来越多地应用到了嵌入式系统中 如新一代手机和掌上电脑多采用TFT显示器件,该显示器件支持彩色图形界面和视频媒体播放。Linux作为开放源代码的操作系统也在市场中占据了一席之地。由于Linux成本低廉,源代码开放,因此成为国内外厂商极力发展的操作系统。在应用需求的推动下,Linux下也出现了许多图形界面软件包,如MiniGUI、Trolletech公司的Embedded QT等,其图形界面及开发工具与Windows CE不相上下。在图形软件包的开发和移植工作中都牵涉到底层LCD的驱动问题。笔者有幸参与了Linux操作系统下LCD部分的开发,其主要功能是点亮液晶屏,将在摄像头上采集的BMP图片在液晶屏上显示并将BMP 格式压缩成JPEG格式,使得存储量减少。因此笔者就在开发过程中遇到的问题一一阐述。背景知识在切入正题之前,先来了解在做驱动过程中需要预先知道的知识。1.硬件平台MC9328MX1(以下简称MX1)是Motorola 公司基于ARM核心的第一款MCU,主要面向高端嵌入式应用。内部采用ARM920T内核,并集成了SDRAM/Flash、LCD,USB、蓝牙.多媒体闪存卡(MMC/SD、Memory Stick)和CMOS摄像头等控制器。LCD控制器的功能是产生显示驱动信号,驱动LCD显示屏。用户只需要通过读写一系列的寄存器,完成配制和显示控制。MX1中的LCD控制器可支持单色/彩色LCD 显示器。支持彩色TFT时,可提供4/8/12/16位颜色模式,其中16位颜色模式下可以显示64k种颜色。配置LCD控制器重要的一步是指定显示缓冲区,显示的内容就是从缓冲区中读出的,其大小由屏幕分辨率和显示颜色数决定。在本例中,采用KYocera 公司的KCS057QV1AJ液晶屏,在240×320分辨率下可提供8位彩色显示,即最大256色位图。2.Linux下的设备驱动在Linux操作系统下有两类主要的设备文件类型,一种是字符设备,另一种是块设备。字符设备和块设备的主要区别是在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求读/写时,它首先察看缓冲区的内容,如果缓冲区的数据能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。Linux的设备管理是和文件系统解密结合的,各种设备都以文件的形式存放在/dev目录下,称为设备文件。应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样。为了管理这些设备,系统为设备编了号,每个设备号又分为主设备号和次设备号。主设备号用来区分不同种类的设备,而次设备号标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分它们。设备文件的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序。帧缓冲设备为标准字符设备,主设备号为29,次设备号则从0到31。3.Linux的帧缓冲设备帧缓冲区是出现在Linux 2.2.xx及以后版本内核当中的一种驱动程序接口,这种接口将显示设备抽象为帧缓冲区设备区。它允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。这种操作是抽象的、统一的,用户不必关心物理显存的位置、换页机制等具体细节。这些都由Framebufer设备驱动来完成。帧缓冲设备对应的设备文件为/dev/fb*,如果系统有多个显示卡,Linux下还可支持多个帧缓冲设备,最多可达32个,分别为/dev/fb0到/dev/fb31,而/dev/fb则为当前缺省的帧缓冲设备,通常指向/dev/fb0。当然在嵌入式系统中支持一个显示设备就够了。在使用Framebufer时,Linux是将显卡置于图形模式下的.在应用程序中,一般通过将Frame-Buffer设备映射到进程地址空间的方式使用,对于帧缓冲来说,可以把它看成是一段内存,用于读写内存的函数均可对这段地址进行读写,只不过这段内存被专门用于放置要在LCD上显示的内容,其目的就是通过配置LCDC寄存器在一段指定内存与LCD 之间建立一个自动传输的通道。这样,任何程序只要修改这段内存中的数据,就可以改变LCD 上的显示内容。关于优奕视界:优奕视界//www.chinasunyee.com是一家集工业级显示屏和触摸屏研发、设计、生产、销售和服务为一体的国家高新技术企业,服务手持终端、三防手机、医疗设备、物联网设备等行业终端品牌客户! 公司十年专注于工业级显示屏、工业级触摸屏、光学贴合产品研发制造,提供一体化显示触控解决方案,并提供高端光电性能的技术配套服务。在工业级显示屏领域,优奕视界笑傲群雄,以匠心精神为客户提供一体化显示屏触控解决方案,赋予“看见”新的高度。强大的设计研发团队,有超过十年的显示模组行业经验,为客户提供从方案设计到产品量产技术的一条龙服务; “高可靠性方案”、“阳光下可视方案”和“宽视角方案”是优奕视界研发团队不断技术创新的成果,充分适应了市场的需求。   优奕视界拥有专业的产品检测设备,工业级的可靠性标准,为客户产品质量保驾护航。同时积累了一大批诸如新大陆、新华医疗、盖瑞医疗、远大、凡卓、柏斯、艾尔康、北斗星通、智美达、瑞沃德等实力客户群体,成为他们信赖的长期供应商,屹立于行业领先地位。本文由百家号作者上传并发布,百家号仅提供信息发布平台。文章仅代表作者个人观点,不代表百度立场。未经作者许可,不得转载。优奕视界显示屏百家号最近更新:简介:专注工业级显示屏、工业级触摸屏作者最新文章相关文章购买商品:
商品价格:
价格读取中
支付方式:
请扫码进行支付
请扫码进行支付
适合人群所有人已参加学习2056
会员立省3.00元,
会员立省3.00元,
总结出怎么写一个lcd的驱动程序的步骤1.分配一个fb_info的结构体2.设置fb_info结构体3.向内核注册register_framebuffer4.硬件相关的初始化
技术总监/研发总监
2003 年毕业于中国科学技术大学,电子专业、软件专业双学位。近10年嵌入式开发经验,曾ZTE公司负责Linux底层系统开发。
近5年作为特聘讲师在若干个有名的培训机构讲课。 擅长ARM系列方面的硬件原理图设计, 精通ARM等架构处理器的Linux底层系统开发。
著有《嵌入式LINUX应用开发完全手册》一书,自2008年来一直是Linux系统类畅销书。
领取优惠券
正在努力加载中~~随笔 - 20&
文章 - 0&评论 - 2&trackbacks - 0
一、S3C6410 LCD驱动裸机代码LCD控制器初始化:
1 unsigned long VideoBuffer[LCD_LOW][LCD_COL] = {0};
2 void lcd_init(void)
/* 1.初始化IO端口为LCD端口 */
/* GPIO configure */
GPICON = 0xAAAAAAAA;
GPJCON = 0x00AAAAAA;
/* 2.使能LCD时钟 */
//HCLK_GATE |= (1 && 3);
//默认打开
MIFPCON &=~(1 && 3);
/* 3.设置I/F类型 */
SPCON &=~(3 && 0);
SPCON |= (1 && 0);
VIDCON0 = (CLK_DIV && 6) | (1 && 4) | (1 && 1) | (1 && 0);
VIDCON1 |= (1 && 6) | (1 && 5);
/* 4.LCD控制器相关寄存器配置 */
VIDTCON0 = (VBPD && 16) | (VFPD && 8) | (VSPW && 0);
VIDTCON1 = (HBPD && 16) | (HFPD && 8) | (HSPW && 0);
VIDTCON2 = (LINEVAL && 11) | (HOZVAL && 0);
WINCON0 |= (0xB && 2) | (1 && 0);
VIDOSD0A = (0 && 11) | (0 && 0);
VIDOSD0B = (HOZVAL && 11) | (LINEVAL && 0);
VIDOSD0C = LCD_LOW * LCD_COL;
VIDW00ADD0B0 = ((unsigned long)VideoBuffer);
VIDW00ADD1B0 = ((unsigned long)VideoBuffer + LCD_LOW * LCD_COL * 4) & 0xFFFFFF;
LCD控制器驱动程序有几个重要步骤:1、初始化IO端口为LCD端口,通过GPICON和GPJCON将相应的为设置成LCD VD[n]模式。2、使能LCD时钟,HCLK_GATE寄存器默认是打开的,所以不需要设置。3、设置I/F类型,根据芯片手册上的描述,需要设置SPCON寄存器把I/F模式设置成RGB I/F style。4、LCD控制器相关寄存器配置,这一步是根据LCD规格书设置时序相关的时间参数,包括水平时间参数和垂直时间参数,下面是我所使用的LCD时间参数:根据这张图可以确定VSPW的值即为VS pulse width的值,这里取个中间值10。VFPD的值即为VS Front Porch的值,取典型值22。VBPD的值没有明确给出,但是通过下图可以知道VBPD = tvb - tvpw,从上图中可以知道tvb就是VS Blanking等于23,tvpw为VSPW = 10,所以可以确定VBPD = 23 - 10。水平方向这三个方向的分析和垂直方向是一样的,最终得出的数据如下:
1 #define VBPD
(23 - 10 - 1)
//tvb - tvpw = tvb - VSPW
2 #define VFPD
3 #define VSPW
4 #define HBPD
(46 - 20 - 1)
//thb - thpw = thb - HSPW
5 #define HFPD
6 #define HSPW
7 #define LINEVAL
8 #define HOZVAL
LINEVAL为屏幕垂直方向的像素,HOZVAL为屏幕水平方向的像素。在芯片手册上可以看到下面的描述,所以上面的参数都要进行减一操作。最后VIDW00ADD0B0和VIDW00ADD1B0寄存器指定了LCD缓冲区。
二、Linux内核LCD设备驱动Linux为LCD显示设备提供了一个帧缓冲接口,所谓的帧缓冲其实就是想裸机代码一样在内存中分配一段空间,这段空间就描述了整个屏幕像素点的信息,往缓冲区中与现实点对应的位置写入颜色值,对应的颜色就会在LCD上显示。帧缓冲设备实质上是一个字符设备,主设备号29,对应于/dev/fbn设备文件。上面简单概括了LCD控制器裸机程序相关寄存器的操作,下面看看Linux内核里面是怎样实现LCD设备驱动的。Linux内核LCD设备驱动程序在s3cfb.c文件里面,从s3cfb.c里面的module_init()函数入手,找到平台驱动结构体里面的s3cfb_probe()函数,在之前的博客中已经提到,分析平台设备驱动首先要分析的函数就是probe函数。进入s3cfb_probe()函数中,找到了一个s3cfb_init_fbinfo()函数,这个函数又调用了s3cfb_init_hw()函数,在s3cfb_init_hw()函数中调用了s3cfb_set_fimd_info()函数和s3cfb_set_gpio()函数,s3cfb_set_fimd_info()函数中初始化了一个s3cfb_fimd_info_t结构体,里面存储的数据就包括上面裸机程序里面时序相关的时间参数,这个结构体里面包含着所有的LCD控制器相关的参数。s3cfb_set_gpio()函数部分代码如下:
1 int s3cfb_set_gpio(void)
/* 2.使能LCD时钟 */
/* enable clock to LCD */
val = readl(S3C_HCLK_GATE);
val |= S3C_CLKCON_HCLK_LCD;
writel(val, S3C_HCLK_GATE);
/* 3.设置I/F类型 */
/* select TFT LCD type (RGB I/F) */
val = readl(S3C64XX_SPCON);
val &= ~0x3;
val |= (1 && 0);
writel(val, S3C64XX_SPCON);
/* 1.初始化IO端口为LCD端口 */
for (i = 0; i & 16; i++)
s3c_gpio_cfgpin(S3C64XX_GPI(i), S3C_GPIO_SFN(2));
for (i = 0; i & 12; i++)
s3c_gpio_cfgpin(S3C64XX_GPJ(i), S3C_GPIO_SFN(2));
在s3cfb_set_gpio()函数中实现了LCD控制器裸机操作中的第一、第二和第三个步骤。退回到s3cfb_probe()函数中,调用了s3cfb_map_video_memory()函数,在这个函数中调用了dma_alloc_writecombine()函数完成帧缓冲设备显示缓冲区的分配。为什么是dma_alloc_writecombine()函数?因为系统对数据的搬移采用的是DMA方式。再退回到s3cfb_probe()函数,调用了s3cfb_init_registers()函数:
1 /* 4.LCD控制器相关寄存器配置 */
2 int s3cfb_init_registers(s3cfb_info_t *fbi)
if (win_num == 0) {
s3cfb_fimd.vidcon0 = s3cfb_fimd.vidcon0 & ~(S3C_VIDCON0_ENVID_ENABLE | S3C_VIDCON0_ENVID_F_ENABLE);
writel(s3cfb_fimd.vidcon0, S3C_VIDCON0);
lcd_clock = clk_get(NULL, "lcd");
s3cfb_fimd.vidcon0 |= S3C_VIDCON0_CLKVAL_F((int) (s3cfb_fimd.pixclock));
writel(video_phy_temp_f1, S3C_VIDW00ADD0B0 + (0x08 * win_num));
writel(S3C_VIDWxxADD1_VBASEL_F((unsigned long) video_phy_temp_f1 + (page_width + offset) * (var-&yres)), S3C_VIDW00ADD1B0 + (0x08 * win_num));
writel(S3C_VIDWxxADD2_OFFSIZE_F(offset) | (S3C_VIDWxxADD2_PAGEWIDTH_F(page_width)), S3C_VIDW00ADD2 + (0x04 * win_num));
if (win_num & 2) {
writel(video_phy_temp_f2, S3C_VIDW00ADD0B1 + (0x08 * win_num));
writel(S3C_VIDWxxADD1_VBASEL_F((unsigned long) video_phy_temp_f2 + (page_width + offset) * (var-&yres)), S3C_VIDW00ADD1B1 + (0x08 * win_num));
switch (win_num) {
writel(s3cfb_fimd.wincon0, S3C_WINCON0);
writel(s3cfb_fimd.vidcon0, S3C_VIDCON0);
writel(s3cfb_fimd.vidcon1, S3C_VIDCON1);
writel(s3cfb_fimd.vidtcon0, S3C_VIDTCON0);
writel(s3cfb_fimd.vidtcon1, S3C_VIDTCON1);
writel(s3cfb_fimd.vidtcon2, S3C_VIDTCON2);
writel(s3cfb_fimd.dithmode, S3C_DITHMODE);
writel(s3cfb_fimd.vidintcon0, S3C_VIDINTCON0);
writel(s3cfb_fimd.vidintcon1, S3C_VIDINTCON1);
writel(s3cfb_fimd.vidosd0a, S3C_VIDOSD0A);
writel(s3cfb_fimd.vidosd0b, S3C_VIDOSD0B);
writel(s3cfb_fimd.vidosd0c, S3C_VIDOSD0C);
writel(s3cfb_fimd.wpalcon, S3C_WPALCON);
s3cfb_onoff_win(fbi, ON);
local_irq_restore(flags);
在s3cfb_init_registers()函数里面进行了裸机操作的第四步操作,s3cfb_fimd结构体里面的数据就来源于上面所说的s3cfb_set_fimd_info()函数。最后调用了register_framebuffer()函数注册了一个帧缓冲设备,剩下的事情就交给内核来完成了。
阅读(...) 评论()linux内核如何驱动lcd显示系统界面
[问题点数:40分]
本版专家分:0
结帖率 83.33%
CSDN今日推荐
本版专家分:22786
2011年9月 Linux/Unix社区大版内专家分月排行榜第一2008年11月 C/C++大版内专家分月排行榜第一
2012年11月 Linux/Unix社区大版内专家分月排行榜第二2011年8月 Linux/Unix社区大版内专家分月排行榜第二2008年10月 C/C++大版内专家分月排行榜第二
2012年8月 Linux/Unix社区大版内专家分月排行榜第三
本版专家分:100
本版专家分:0
本版专家分:84
匿名用户不能发表回复!
其他相关推荐博客访问: 144685
博文数量: 27
博客积分: 465
博客等级: 下士
技术积分: 1340
注册时间:
分类: LINUX 21:25:07
& & 首先我们来回归下怎么写一个字符设备驱动程序,下面以linux设备中的混杂设备为例
static int __init misc_init(void)
&&&&int err;
#ifdef CONFIG_PROC_FS
&&&&proc_create("misc", 0, NULL, &misc_proc_fops);
&&&&misc_class = class_create(THIS_MODULE, "misc");
&&&&err = PTR_ERR(misc_class);
&&&&if (IS_ERR(misc_class))
&&&&&&&&goto fail_remove;
&&&&err = -EIO;
&&&&if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
&&&&&&&&goto fail_printk;
&&&&misc_class->devnode = misc_devnode;
&&&&return 0;
fail_printk:
&&&&printk("unable to get major %d for misc devices\n", MISC_MAJOR);
&&&&class_destroy(misc_class);
fail_remove:
&&&&remove_proc_entry("misc", NULL);
&&&&return err;
}上面的主要步骤为申请一个主次设备号,构建一个file_opertiaon,并用register_chrdev注册,构建类,并可以在类下创建设备节点。现在来看看内核是怎么实现lcd驱动程序呢?
static int __init
fbmem_init(void)
&&&&proc_create("fb", 0, NULL, &fb_proc_fops);//与proc文件系统有关
&&&&if (register_chrdev(FB_MAJOR,"fb",&fb_fops))//注册字符设备
&&&&&&&&printk("unable to get major %d for fb devs\n", FB_MAJOR);
&&&&fb_class = class_create(THIS_MODULE, "graphics");//创建类,为以后创建设备节点做准备
&&&&if (IS_ERR(fb_class)) {
&&&&&&&&printk(KERN_WARNING "Unabl errno = %ld\n", PTR_ERR(fb_class));
&&&&&&&&fb_class = NULL;
&&&&return 0;
}从上面也可以看出,内核对于lcd驱动程序也是满足一个主设备号为29的字符设备,那么用户空间访问通过fb_fops结构体的成员函数会操作到LCD控制器硬件寄存器。下面来看看linux帧缓冲设备驱动的主要结构图
framebuffer机制模仿显卡的功能,将显卡硬件结构抽象为一系列的数据结构,可以通过framebuffer的读写直接对显存进行操作。framebuffer一个字符设备,主设备号,对应于/dev/fb0...32的设备文件。对于用户程序而言,与字符设备并没有什么区别,用户具有把其看成一块内存,既可以读也可以写。那么下面来看看
在这个图中占主导地位的fb_info结构体
struct fb_info {
&&&&atomic_t count;
&&&&int node; & & & & & & //子设备号
&&&&int flags;
&&&&struct mutex lock;& & & & //互斥锁
&&&&struct mutex mm_lock;&&&&&&&&/* Lock for fb_mmap and smem_* fields */
&&&&struct fb_var_screeninfo var;& & //当前缓冲区的可变参数
&&&&struct fb_fix_screeninfo fix;& & //固定参数
&&&&struct fb_monspecs monspecs;& & //当前显示器标志
&&&&struct work_struct queue;& & //帧缓冲事件队列
&&&&struct fb_pixmap pixmap;& & //图像硬件mapper
&&&&struct fb_pixmap sprite;&&&&//光标硬件mapper
&&&&struct fb_cmap cmap;&&&&&&&&//当前的调色板
& & struct list_head modelist; /* mode list */
&&&&struct fb_videomode *mode;& & //当前的视频模式
#ifdef CONFIG_FB_BACKLIGHT & & &
&&&&/* assigned backlight device *//支持lcd背光的设置
&&&&/* set before framebuffer registration,
&&&& remove after unregister */
&&&&struct backlight_device *bl_dev; &
&&&&/* Backlight level curve */
&&&&struct mutex bl_curve_mutex;&&&&
&&&&u8 bl_curve[FB_BACKLIGHT_LEVELS];
#ifdef CONFIG_FB_DEFERRED_IO
&&&&struct delayed_work deferred_work;
&&&&struct fb_deferred_io *fbdefio;
&&&&struct fb_ops *fbops; & & //帧缓冲操作函数集
&&&&struct device *device;&&&&&&&&//父设备
& & struct device *dev;&&&&&&&&/* This is this fb device */
&&&&int class_flag; /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
&&&&struct fb_tile_ops *tileops; /* Tile Blitting */
&&&&char __iomem *screen_base;&&&&//虚拟基地址
&&&&unsigned long screen_size;&&&&/* Amount of ioremapped VRAM or 0 */
&&&&void *pseudo_palette;&&&&&&&&/* Fake palette of 16 colors */
#define FBINFO_STATE_RUNNING&&&&0
#define FBINFO_STATE_SUSPENDED&&&&1
&&&&u32 state;&&&&&&&&&&&&/* Hardware state i.e suspend */
&&&&void *fbcon_par; /* fbcon use-only private area */
&&&&/* From here on everything is device dependent */
&&&&void *par;
&&&&/* we need the PCI or similar aperture base/size not
&&&& smem_start/size as smem_start may just be an object
&&&& allocated inside the aperture so may not actually overlap */
&&&&struct apertures_struct {
&&&&&&&&unsigned int count;
&&&&&&&&struct aperture {
&&&&&&&&&&&&resource_size_t base;
&&&&&&&&&&&&resource_size_t size;
&&&&&&&&} ranges[0];
&&&&} *apertures;
fb_ops结构体用来实现对帧缓冲的操作,这些函数需要我们自行编写的。
struct fb_ops {
&&&&/* open/release and usage marking */
&&&&struct module *owner;
&&&&int (*fb_open)(struct fb_info *info, int user);
&&&&int (*fb_release)(struct fb_info *info, int user);//打开和释放
&&&&/* For framebuffers with strange non linear layouts or that do not
&&&& * work with normal memory mapped access
&&&&ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
&&&&&&&&&&&& size_t count, loff_t *ppos);
&&&&ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
&&&&&&&&&&&& size_t count, loff_t *ppos);
&&&&/* checks var and eventually tweaks it to something supported,
&&&& * DO NOT MODIFY PAR *///检测可变参数,并调整到支持的值
&&&&int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
&&&&/* set the video mode according to info->var */
&&&&int (*fb_set_par)(struct fb_info *info);//视频设置模式
&&&&/* set color register */
&&&&int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
&&&&&&&&&&&& unsigned blue, unsigned transp, struct fb_info *info);//设置color寄存器
&&&&/* set color registers in batch */
&&&&int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);//批量设置color寄存器,设备颜色表
&&&&/* blank display */
&&&&int (*fb_blank)(int blank, struct fb_info *info);//显示空白
&&&&/* pan display */
&&&&int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
&&&&/* Draws a rectangle */
&&&&void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);//填充矩形
&&&&/* Copy data from area to another */
&&&&void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);//数据复制
&&&&/* Draws a image to the display */
&&&&void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);//图形填充
&&&&/* Draws cursor */
&&&&int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);//绘制光标
&&&&/* Rotates the display */
&&&&void (*fb_rotate)(struct fb_info *info, int angle);//旋转显示
&&&&/* wait for blit idle, optional */
&&&&int (*fb_sync)(struct fb_info *info);//等待blit空闲
&&&&/* perform fb specific ioctl (optional) */
&&&&int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
&&&&&&&&&&&&unsigned long arg);
&&&&/* Handle 32bit compat ioctl (optional) */
&&&&int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
&&&&&&&&&&&&unsigned long arg);
&&&&/* perform fb specific mmap */
&&&&int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
&&&&/* get capability given var */
&&&&void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
&&&&&&&&&&&& struct fb_var_screeninfo *var);
&&&&/* teardown any resources to do with this framebuffer */
&&&&void (*fb_destroy)(struct fb_info *info);
&&&&/* called at KDB enter and leave time to prepare the console */
&&&&int (*fb_debug_enter)(struct fb_info *info);
&&&&int (*fb_debug_leave)(struct fb_info *info);
};内核中定义了一个struct fb_info *registered_fb[FB_MAX]来存放屏的相关信息,下面来看看帧缓冲设备fb_info结构体的注册。
register_framebuffer(struct fb_info *fb_info)
&&&&int ret;
&&&&mutex_lock(&registration_lock);
&&&&ret = do_register_framebuffer(fb_info);
&&&&mutex_unlock(&registration_lock);
&&&&return ret;
static int do_register_framebuffer(struct fb_info *fb_info)
&&&&int i;
&&&&struct fb_event event;
&&&&struct fb_videomode mode;
&&&&if (fb_check_foreignness(fb_info))
&&&&&&&&return -ENOSYS;
&&&&do_remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
&&&&&&&&&&&&&&&&&&&& fb_is_primary_device(fb_info));
&&&&if (num_registered_fb == FB_MAX)
&&&&&&&&return -ENXIO;
&&&&num_registered_fb++;
&&&&for (i = 0 ; i < FB_MAX; i++)
&&&&&&&&if (!registered_fb[i])
&&&&&&&&&&&&break;
&&&&fb_info->node = i;
&&&&atomic_set(&fb_info->count, 1);
&&&&mutex_init(&fb_info->lock);
&&&&mutex_init(&fb_info->mm_lock);
&&&&fb_info->dev = device_create(fb_class, fb_info->device,
&&&&&&&&&&&&&&&& MKDEV(FB_MAJOR, i), NULL, "fb%d", i);//创建设备节点
&&&&if (IS_ERR(fb_info->dev)) {
&&&&&&&&/* Not fatal */
&&&&&&&&printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
&&&&&&&&fb_info->dev = NULL;
&&&&} else
&&&&&&&&fb_init_device(fb_info);//初始化fb的属性文件
&&&&if (fb_info->pixmap.addr == NULL) {
&&&&&&&&fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
&&&&&&&&if (fb_info->pixmap.addr) {
&&&&&&&&&&&&fb_info->pixmap.size = FBPIXMAPSIZE;
&&&&&&&&&&&&fb_info->pixmap.buf_align = 1;
&&&&&&&&&&&&fb_info->pixmap.scan_align = 1;
&&&&&&&&&&&&fb_info->pixmap.access_align = 32;
&&&&&&&&&&&&fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
&&&&fb_info->pixmap.offset = 0;
&&&&if (!fb_info->pixmap.blit_x)
&&&&&&&&fb_info->pixmap.blit_x = ~(u32)0;
&&&&if (!fb_info->pixmap.blit_y)
&&&&&&&&fb_info->pixmap.blit_y = ~(u32)0;
&&&&if (!fb_info->modelist.prev || !fb_info->modelist.next)
&&&&&&&&INIT_LIST_HEAD(&fb_info->modelist);
&&&&fb_var_to_videomode(&mode, &fb_info->var);
&&&&fb_add_videomode(&mode, &fb_info->modelist);
&&&&registered_fb[i] = fb_info;
&&&&event.info = fb_info;
&&&&if (!lock_fb_info(fb_info))
&&&&&&&&return -ENODEV;
&&&&fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
&&&&unlock_fb_info(fb_info);
&&&&return 0;
}这个函数主要是创建设备节点,初始化fb的属性文件等一些初始化,下面来看看帧缓冲区驱动核心层的层次结构图:
那么从上面的图可以总结出怎么写一个lcd的驱动程序的步骤1.分配一个fb_info的结构体2.设置3.注册register_framebuffer4.硬件相关的初始化上面将了一些framebuffer帧缓冲的有关的,那么我们具体看看内核怎么去实现lcd的驱动。int __init s3c2410fb_init(void){&&&&int ret = platform_driver_register(&s3c2410fb_driver);&&&&if (ret == 0)&&&&&&&&ret = platform_driver_register(&s3c2412fb_driver);&&&&return ret;}static void __exit s3c2410fb_cleanup(void){&&&&platform_driver_unregister(&s3c2410fb_driver);&&&&platform_driver_unregister(&s3c2412fb_driver);}module_init(s3c2410fb_init);module_exit(s3c2410fb_cleanup);lcd驱动挂在platform总线上,注册了一个s3c2410fb_driver的驱动,由总线驱动设备模型中,当注册一个设备或者驱动的时候,会调用总线的match函数,而platform的match函数是匹配设备和驱动的名字static struct platform_driver s3c2410fb_driver = {&&&&.probe&&&&&&&&= s3c2410fb_probe,&&&&.remove&&&&&&&&= __devexit_p(s3c2410fb_remove),&&&&.suspend&&&&= s3c2410fb_suspend,&&&&.resume&&&&&&&&= s3c2410fb_resume,&&&&.driver&&&&&&&&= {&&&&&&&&.name&&&&= "s3c2410-lcd",&&&&&&&&.owner&&&&= THIS_MODULE,&&&&},};struct platform_device s3c_device_lcd = {&&&&.name&&&&&&&&= "s3c2410-lcd",&&&&.id&&&&&&&&= -1,&&&&.num_resources&&&&= ARRAY_SIZE(s3c_lcd_resource),&&&&.resource&&&&= s3c_lcd_resource,&&&&.dev&&&&&&&&= {&&&&&&&&.dma_mask&&&&&&&&= &samsung_device_dma_mask,&&&&&&&&.coherent_dma_mask&&&&= DMA_BIT_MASK(32),&&&&}};static struct resource s3c_lcd_resource[] = {&&&&[0] = DEFINE_RES_MEM(S3C24XX_PA_LCD, S3C24XX_SZ_LCD),//内存&&&&[1] = DEFINE_RES_IRQ(IRQ_LCD),//中断号};驱动与设备匹配会,会调用probe函数static int __devinit s3c24xxfb_probe(struct platform_device *pdev,&&&&&&&&&&&&&&&&
enum s3c_drv_type drv_type){&&&&struct s3c2410fb_info *info;&&&&struct s3c2410fb_display *display;&&&&struct fb_info *fbinfo;&&&&struct s3c2410fb_mach_info *mach_info;&&&&struct resource *res;&&&&int ret;&&&&int irq;&&&&int i;&&&&int size;&&&&u32 lcdcon1;&&&&mach_info = pdev->dev.platform_data;&&&&if (mach_info == NULL) {&&&&&&&&dev_err(&pdev->dev,&&&&&&&&&&&&"no platform data for lcd, cannot attach\n");&&&&&&&&return -EINVAL;&&&&}&&&&if (mach_info->default_display >= mach_info->num_displays) {&&&&&&&&dev_err(&pdev->dev, "default is %d but only %d displays\n",&&&&&&&&&&&&mach_info->default_display, mach_info->num_displays);&&&&&&&&return -EINVAL;&&&&}&&&&display = mach_info->displays + mach_info->default_display;&&&&irq = platform_get_irq(pdev, 0);//获取中断号&&&&if (irq < 0) {&&&&&&&&dev_err(&pdev->dev, "no irq for device\n");&&&&&&&&return -ENOENT;&&&&}&&&&fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);//分配一个fb_info结构体&&&&if (!fbinfo)&&&&&&&&return -ENOMEM;&&&&platform_set_drvdata(pdev, fbinfo);&&&&info = fbinfo->par;&&&&info->dev = &pdev->dev;&&&&info->drv_type = drv_type;&&&&res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获取内存地址&&&&if (res == NULL) {&&&&&&&&dev_err(&pdev->dev, "failed to get memory registers\n");&&&&&&&&ret = -ENXIO;&&&&&&&&goto dealloc_fb;&&&&}&&&&size = resource_size(res);&&&&info->mem = request_mem_region(res->start, size, pdev->name);&&&&if (info->mem == NULL) {&&&&&&&&dev_err(&pdev->dev, "failed to get memory region\n");&&&&&&&&ret = -ENOENT;&&&&&&&&goto dealloc_fb;&&&&}&&&&info->io = ioremap(res->start, size);//io map&&&&if (info->io == NULL) {&&&&&&&&dev_err(&pdev->dev, "ioremap() of registers failed\n");&&&&&&&&ret = -ENXIO;&&&&&&&&goto release_mem;&&&&}&&&&info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);&&&&dprintk("devinit\n");&&&&strcpy(fbinfo->fix.id, driver_name);&&&&/* Stop the video */&&&&lcdcon1 = readl(info->io + S3C2410_LCDCON1);&&&&writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);//设置fbinfo结构体&&&&fbinfo->fix.type&&&&
= FB_TYPE_PACKED_PIXELS;&&&&fbinfo->fix.type_aux&&&&
= 0;&&&&fbinfo->fix.xpanstep&&&&
= 0;&&&&fbinfo->fix.ypanstep&&&&
= 0;&&&&fbinfo->fix.ywrapstep&&&&
= 0;&&&&fbinfo->fix.accel&&&&
= FB_ACCEL_NONE;&&&&fbinfo->var.nonstd&&&&
= 0;&&&&fbinfo->var.activate&&&&
= FB_ACTIVATE_NOW;&&&&fbinfo->var.accel_flags
= 0;&&&&fbinfo->var.vmode&&&&
= FB_VMODE_NONINTERLACED;&&&&fbinfo->fbops&&&&&&&&
= &s3c2410fb_ops;&&&&fbinfo->flags&&&&&&&&
= FBINFO_FLAG_DEFAULT;&&&&fbinfo->pseudo_palette
= &info->pseudo_pal;&&&&for (i = 0; i < 256; i++)&&&&&&&&info->palette_buffer[i] = PALETTE_BUFF_CLEAR;&&&&ret = request_irq(irq, s3c2410fb_irq, 0, pdev->name, info);&&&&if (ret) {&&&&&&&&dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);&&&&&&&&ret = -EBUSY;&&&&&&&&goto release_regs;&&&&}&&&&info->clk = clk_get(NULL, "lcd");&&&&if (IS_ERR(info->clk)) {&&&&&&&&printk(KERN_ERR "failed to get lcd clock source\n");&&&&&&&&ret = PTR_ERR(info->clk);&&&&&&&&goto release_irq;&&&&}&&&&clk_enable(info->clk);&&&&dprintk("got and enabled clock\n");&&&&msleep(1);&&&&info->clk_rate = clk_get_rate(info->clk);&&&&/* find maximum required memory size for display */&&&&for (i = 0; i < mach_info->num_displays; i++) {&&&&&&&&unsigned long smem_len = mach_info->displays[i].xres;&&&&&&&&smem_len *= mach_info->displays[i].yres;&&&&&&&&smem_len *= mach_info->displays[i].bpp;&&&&&&&&smem_len >>= 3;&&&&&&&&if (fbinfo->fix.smem_len < smem_len)&&&&&&&&&&&&fbinfo->fix.smem_len = smem_len;&&&&}&&&&/* Initialize video memory */&&&&ret = s3c2410fb_map_video_memory(fbinfo);&&&&if (ret) {&&&&&&&&printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);&&&&&&&&ret = -ENOMEM;&&&&&&&&goto release_clock;&&&&}&&&&dprintk("got video memory\n");&&&&fbinfo->var.xres = display->xres;&&&&fbinfo->var.yres = display->yres;&&&&fbinfo->var.bits_per_pixel = display->bpp;&&&&s3c2410fb_init_registers(fbinfo);&&&&s3c2410fb_check_var(&fbinfo->var, fbinfo);&&&&ret = s3c2410fb_cpufreq_register(info);&&&&if (ret < 0) {&&&&&&&&dev_err(&pdev->dev, "Failed to register cpufreq\n");&&&&&&&&goto free_video_memory;&&&&}&&&&ret = register_framebuffer(fbinfo);&&&&if (ret < 0) {&&&&&&&&printk(KERN_ERR "Failed to register framebuffer device: %d\n",&&&&&&&&&&&&ret);&&&&&&&&goto free_cpufreq;&&&&}&&&&/* create device files */&&&&ret = device_create_file(&pdev->dev, &dev_attr_debug);&&&&if (ret) {&&&&&&&&printk(KERN_ERR "failed to add debug attribute\n");&&&&}&&&&printk(KERN_INFO "fb%d: %s frame buffer device\n",&&&&&&&&fbinfo->node, fbinfo->fix.id);&&&&return 0;&free_cpufreq:&&&&s3c2410fb_cpufreq_deregister(info);free_video_memory:&&&&s3c2410fb_unmap_video_memory(fbinfo);release_clock:&&&&clk_disable(info->clk);&&&&clk_put(info->clk);release_irq:&&&&free_irq(irq, info);release_regs:&&&&iounmap(info->io);release_mem:&&&&release_mem_region(res->start, size);dealloc_fb:&&&&platform_set_drvdata(pdev, NULL);&&&&framebuffer_release(fbinfo);&&&&return ret;}上面的一些步骤基本与总结的一个lcd的驱动程序的步骤相同,下面来看看用户空间怎么去访问app:read()-->kernel调用fbmem.c中的fb_read(),其他的接口类似。static ssize_tfb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){&&&&unsigned long p = *ppos;&&&&struct fb_info *info = file_fb_info(file);//获取次设备号,根据次设备号从registered_fb数组中拿&&&&u8 *buffer, *dst; & & & & & & & & & & & & //到对应的fb_info&&&&u8 __iomem *src;&&&&int c, cnt = 0, err = 0;&&&&unsigned long total_size;&&&&if (!info || ! info->screen_base)&&&&&&&&return -ENODEV;&&&&if (info->state != FBINFO_STATE_RUNNING)&&&&&&&&return -EPERM;&&&&if (info->fbops->fb_read)//调用read函数&&&&&&&&return info->fbops->fb_read(info, buf, count, ppos);&&&&&&&&total_size = info->screen_size;&&&&if (total_size == 0)&&&&&&&&total_size = info->fix.smem_len;&&&&if (p >= total_size)&&&&&&&&return 0;&&&&if (count >= total_size)&&&&&&&&count = total_size;&&&&if (count + p > total_size)&&&&&&&&count = total_size - p;&&&&buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,&&&&&&&&&&&& GFP_KERNEL);&&&&if (!buffer)&&&&&&&&return -ENOMEM;&&&&src = (u8 __iomem *) (info->screen_base + p);//获得帧缓冲的起始地址+偏移量&&&&if (info->fbops->fb_sync)&&&&&&&&info->fbops->fb_sync(info);& & &&&&&&while (count) { & & & & & & & & & & & & & //拷贝数据&&&&&&&&c
= (count > PAGE_SIZE) ? PAGE_SIZE : count;&&&&&&&&dst = buffer;&&&&&&&&fb_memcpy_fromfb(dst, src, c);&&&&&&&&dst += c;&&&&&&&&src += c;&&&&&&&&if (copy_to_user(buf, buffer, c)) {//提交给应用层。&&&&&&&&&&&&err = -EFAULT;&&&&&&&&&&&&break;&&&&&&&&}&&&&&&&&*ppos += c;&&&&&&&&buf += c;&&&&&&&&cnt += c;&&&&&&&&count -= c;&&&&}&&&&kfree(buffer);&&&&return (err) ? err : cnt;}
阅读(12711) | 评论(0) | 转发(7) |
给主人留下些什么吧!~~
请登录后评论。

我要回帖

更多关于 Linux需要安装驱动程序吗 的文章

 

随机推荐