写到后面的驱动的,不是所有驱动,都要自已去写,去写也是不现实的,以后工作中只需要移植修改就可以了。所以,学习驱动的框架,以及硬件的操作方式,在这部分驱动学习中应该着重强调,即要明确学习方法。
帧缓冲设备为标准的字符型设备,在Linux中主设备号29,定义在/include/linux/major.h中的FB_MAJOR,次设备号定义帧缓冲的个数,最大允许有32个FrameBuffer,定义在/include/linux/fb.h中的FB_MAX,对应于文件系统下/dev /fb%d设备文件。
1. 帧缓冲设备驱动在Linux子系统中的结构如下:
我们从上面这幅图看,帧缓冲设备在Linux中也可以看做是一个完整的子系统,大体由fbmem.c和xxxfb.c组成。向上给应用程序提供完善的设备文件操作接口(即对FrameBuffer设备进行read、write、ioctl等操作),接口在Linux提供的fbmem.c文件中实现;向下提供了硬件操作的接口,只是这些接口Linux并没有提供实现,因为这要根据具体的LCD控制器硬件进行设置,所以这就是我们要做的事情了(即xxxfb.c
部分的实现)。
从这上面可以看出,所有嵌入式linux中帧缓冲设备都是这种模式,其结构层次相当重要,如上图可见。其中,fbmem.c是内核早已实现好的与上层应用程序的接口程序,我们不需要去管,大概内容就是注册29号帧缓冲设备,以及一些file_operation的实现,这些函数接口都是与应用程序相对应的,其配制的硬件操作还要调用下层的,xxxfb.c。好,可见,我们要写驱动或者说,到时项目中移植LCD驱动时,只需要实现xxxfb.c部分。而这部分里的重点,主要是platform_driver的注册,当platform_bustype利用match匹配后,调用probe函数,也正是在这个函数中实现了,本部分程序的重要部分,fb_info等数据结构的初始化,相关寄存器的初始化,缓冲buffer的内存分配与映射,以及register_framebuffer的注册。
其中,要我们实现的主要部分就是probe函数,fb_info结构体,调用register_framebuffer函数,以及以下结构的实现
static
struct fb_ops s3c2410fb_ops = {
.owner =
THIS_MODULE,
.fb_check_var =
s3c2410fb_check_var,
.fb_set_par =
s3c2410fb_set_par,/*设置fb_info中的参数,主要是LCD的显示模式*/ 这些部分的函数,会被fbmem.c上层调用
.fb_blank =
s3c2410fb_blank,//显示空白
.fb_setcolreg =
s3c2410fb_setcolreg,/
.fb_fillrect =
cfb_fillrect,
.fb_copyarea =
cfb_copyarea,
.fb_imageblit =
cfb_imageblit,
};
这里面主要是前四个函数的实现,后面的内核已帮我们实现好了。
这里先看一下platform_device的注册过程:
static struct resource s3c_lcd_resource[] = {//LCD设备及资源定义
文件在arch/arm/plat-s3c24xx/devs.c中
[0] = {
.start =
S3C24XX_PA_LCD,
.end = S3C24XX_PA_LCD +
S3C24XX_SZ_LCD - 1,
.flags =
IORESOURCE_MEM,
},
[1] = {
.start =
IRQ_LCD,
.end = IRQ_LCD,
.flags =
IORESOURCE_IRQ,
}
};
static u64 s3c_device_lcd_dmamask = 0xffffffffUL;
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 =
&s3c_device_lcd_dmamask,
.coherent_dma_mask =
0xffffffffUL
}
};
接着是LCD屏幕配置信息
//;NEC 3.5鈥滾CD 鐨勯厤缃拰鍙傛暟璁剧疆
#if
defined(CONFIG_FB_S3C2410_N240320)
//arch/arm/mach-s3c2440/mach-mini2440.c
#define LCD_WIDTH 240
#define
LCD_HEIGHT 320
#define LCD_PIXCLOCK 100000
#define LCD_RIGHT_MARGIN
36
#define LCD_LEFT_MARGIN 19
#define LCD_HSYNC_LEN 5
#define
LCD_UPPER_MARGIN 1
#define LCD_LOWER_MARGIN 5
#define LCD_VSYNC_LEN
1
//;澶忔櫘8鈥滾CD 鐨勯厤缃拰鍙傛暟璁剧疆
#elif
defined(CONFIG_FB_S3C2410_TFT640480)
#define LCD_WIDTH 640
#define
LCD_HEIGHT 480
#define LCD_PIXCLOCK 80000
#define LCD_RIGHT_MARGIN
67
#define LCD_LEFT_MARGIN 40
#define LCD_HSYNC_LEN 31
#define
LCD_UPPER_MARGIN 25
#define LCD_LOWER_MARGIN 5
#define LCD_VSYNC_LEN
1
//Sony 3.5鈥滾CD 鐨勯厤缃拰鍙傛暟璁剧疆
#elif
defined(CONFIG_FB_S3C2410_X240320)
#define LCD_WIDTH 240
#define
LCD_HEIGHT 320
#define LCD_PIXCLOCK 170000
#define LCD_RIGHT_MARGIN
25
#define LCD_LEFT_MARGIN 0
#define LCD_HSYNC_LEN 4
#define
LCD_UPPER_MARGIN 0
#define LCD_LOWER_MARGIN 4
#define LCD_VSYNC_LEN
9
#define LCD_CON5 (S3C2410_LCDCON5_FRM565 | S3C2410_LCDCON5_INVVDEN |
S3C2410_LCDCON5_INVVFRAME | S3C2410_LCDCON5_INVVLINE | S3C2410_LCDCON5_INVVCLK |
S3C2410_LCDCON5_HWSWP )
//;缁熷疂3.5鈥滾CD 鐨勯厤缃拰鍙傛暟璁剧疆
#elif
defined(CONFIG_FB_S3C2410_T240320)
#define LCD_WIDTH 240
#define
LCD_HEIGHT 320
#define LCD_PIXCLOCK 146250//146250
#define
LCD_RIGHT_MARGIN 25
#define LCD_LEFT_MARGIN 0
#define LCD_HSYNC_LEN
4
#define LCD_UPPER_MARGIN 1//1
#define LCD_LOWER_MARGIN 4
#define
LCD_VSYNC_LEN 1//1
//;缇ゅ垱7鈥滾CD 鐨勯厤缃拰鍙傛暟璁剧疆
#elif
defined(CONFIG_FB_S3C2410_TFT800480)
#define LCD_WIDTH 800
#define
LCD_HEIGHT 480
#define LCD_PIXCLOCK 11463//40000
#define LCD_RIGHT_MARGIN
67
#define LCD_LEFT_MARGIN 40
#define LCD_HSYNC_LEN 31
#define
LCD_UPPER_MARGIN 25
#define LCD_LOWER_MARGIN 5
#define LCD_VSYNC_LEN
1
//;LCD2VGA(鍒嗚鲸鐜囦负1024x768)妯″潡鐨勯厤缃拰鍙傛暟璁剧疆
#elif
defined(CONFIG_FB_S3C2410_VGA1024768)
#define LCD_WIDTH 1024
#define
LCD_HEIGHT 768
#define LCD_PIXCLOCK 80000
#define LCD_RIGHT_MARGIN
15
#define LCD_LEFT_MARGIN 199
#define LCD_HSYNC_LEN 15
#define
LCD_UPPER_MARGIN 1
#define LCD_LOWER_MARGIN 1
#define LCD_VSYNC_LEN
1
#define LCD_CON5 (S3C2410_LCDCON5_FRM565 |
S3C2410_LCDCON5_HWSWP)
#endif
#if defined (LCD_WIDTH)
static struct
s3c2410fb_display mini2440_lcd_cfg __initdata = {
#if !defined
(LCD_CON5)
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
S3C2410_LCDCON5_INVVLINE
|
S3C2410_LCDCON5_INVVFRAME |
S3C2410_LCDCON5_PWREN
|
S3C2410_LCDCON5_HWSWP,
#else
.lcdcon5 = LCD_CON5,
#endif
.type
= S3C2410_LCDCON1_TFT,
.width = LCD_WIDTH,
.height =
LCD_HEIGHT,
.pixclock = LCD_PIXCLOCK,
.xres = LCD_WIDTH,
.yres =
LCD_HEIGHT,
.bpp = 16,
.left_margin = LCD_LEFT_MARGIN +
1,
.right_margin = LCD_RIGHT_MARGIN + 1,
.hsync_len = LCD_HSYNC_LEN +
1,
.upper_margin = LCD_UPPER_MARGIN + 1,
.lower_margin = LCD_LOWER_MARGIN
+ 1,
.vsync_len = LCD_VSYNC_LEN + 1,
};
static struct
s3c2410fb_mach_info mini2440_fb_info __initdata = {
.displays =
&mini2440_lcd_cfg,
.num_displays = 1,
.default_display = 0,
.gpccon
= 0xaa955699,
.gpccon_mask = 0xffc003cc,
.gpcup =
0x0000ffff,
.gpcup_mask = 0xffffffff,
.gpdcon =
0xaa95aaa1,
.gpdcon_mask = 0xffc0fff0,
.gpdup = 0x0000faff,
.gpdup_mask
= 0xffffffff,
.lpcsel = 0xf82,
};
#endif
接着把要注册的所有平台设备放到平台设备数组中去:
static struct platform_device *mini2440_devices[] __initdata =
{
&s3c_device_usb,
&s3c_device_rtc,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_nand,
//;鎶妌and flash 璁惧娣诲姞鍒板紑鍙戞澘鐨勮澶囧垪琛ㄧ粨鏋?
&mini2440_device_eth,
//;鎶婄綉鍗″钩鍙拌澶囨坊鍔犲埌寮?鍙戞澘鐨勮澶囧垪琛ㄧ粨鏋?
&s3c_device_sdi, //鎶奡D
鍗$粨鏋勮澶囨坊鍔犲埌鐩爣骞冲彴璁惧闆嗕腑
&s3c24xx_uda134x, //;娉ㄥ唽UDA1341
璁惧骞冲彴鍒板唴鏍镐腑
};
最后就是平台设备的注册了:
static void __init mini2440_machine_init(void)
{
#if defined
(LCD_WIDTH)
s3c24xx_fb_set_platdata(&mini2440_fb_info);//这里是将以上的屏幕信息添加到platform_data中去,见下
#endif
s3c_device_sdi.dev.platform_data
=
&mini2440_mmc_cfg;
s3c_i2c0_set_platdata(NULL);
s3c_device_nand.dev.platform_data
= &mini2440_nand_info;
platform_add_devices(mini2440_devices,
ARRAY_SIZE(mini2440_devices));
//smdk_machine_init();
}
void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info
*pd)
{
struct s3c2410fb_mach_info *npd;
npd = kmalloc(sizeof(*npd), GFP_KERNEL);
if (npd)
{
memcpy(npd, pd,
sizeof(*npd));
s3c_device_lcd.dev.platform_data = npd;
}
else {
printk(KERN_ERR "no memory for LCD platform
data\n");
}
}
以下是驱动解析:
/* linux/drivers/video/s3c2410fb.c
* Copyright
(c) 2004,2005 Arnaud Patard
* Copyright
(c) 2004-2008 Ben Dooks
*
* S3C2410 LCD Framebuffer Driver
*
* This file is subject to
the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of
this archive for
* more
details.
*
*
Driver based on skeletonfb.c, sa1100fb.c and others.
*/
#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-lcd.h>
#include<mach/regs-gpio.h>
#include<mach/fb.h>
#ifdef CONFIG_PM
#include<linux/pm.h>
#endif
#include"s3c2410fb.h"
/* Debugging stuff */
#ifdef
CONFIG_FB_S3C2410_DEBUG
staticint debug =1;
#else
staticint debug =0;
#endif
#define dprintk(msg...) if(debug){ printk(KERN_DEBUG "s3c2410fb: " msg);}
/* useful functions */
staticint
is_s3c2412(struct s3c2410fb_info *fbi)
{
return(fbi->drv_type
== DRV_S3C2412);
}
/* s3c2410fb_set_lcdaddr
*
* initialise lcd
controller address pointers
*/
staticvoid
s3c2410fb_set_lcdaddr(struct fb_info *info)
{
unsignedlong saddr1, saddr2, saddr3;
struct s3c2410fb_info *fbi =
info->par;
void __iomem *regs = fbi->io;
saddr1 = info->fix.smem_start
>>1;
saddr2 =
info->fix.smem_start;
saddr2 += info->fix.line_length * info->var.yres;
saddr2 >>=1;
saddr3 =
S3C2410_OFFSIZE(0)|
S3C2410_PAGEWIDTH((info->fix.line_length
/2)&0x3ff);
dprintk("LCDSADDR1
= 0x%08lx\n",
saddr1);
dprintk("LCDSADDR2 = 0x%08lx\n",
saddr2);
dprintk("LCDSADDR3 = 0x%08lx\n",
saddr3);
writel(saddr1, regs + S3C2410_LCDSADDR1);//写入三个地址寄存器
writel(saddr2, regs
+
S3C2410_LCDSADDR2);
writel(saddr3, regs
+
S3C2410_LCDSADDR3);
}
/* s3c2410fb_calc_pixclk()
*
* calculate divisor for
clk->pixclk
*/
staticunsignedint s3c2410fb_calc_pixclk(struct
s3c2410fb_info *fbi,
unsignedlong
pixclk)
{
unsignedlong clk = fbi->clk_rate;
unsignedlonglong div;
/* pixclk is in picoseconds,
our clock is in Hz
*
* Hz -> picoseconds is / 10^-12
*/
div =(unsignedlonglong)clk
* pixclk;
div >>=12; /* div / 2^12
*/
do_div(div,625*625UL*625);/* div / 5^12
*/
dprintk("pixclk
%ld, divisor is %ld\n",
pixclk,(long)div);
return div;
}
/*
* s3c2410fb_check_var():
* Get the video params out of ‘var‘. If a value doesn‘t
fit, round it up,
* if it‘s too big,
return -EINVAL.
*
*/
staticint s3c2410fb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct
s3c2410fb_info *fbi
= info->par;
struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
struct s3c2410fb_display *display = NULL;
struct s3c2410fb_display *default_display = mach_info->displays +
mach_info->default_display;
int type = default_display->type;
unsigned i;
dprintk("check_var(var=%p, info=%p)\n",var, info);
/* validate x/y resolution
*/
/* choose default
mode if possible */
if(var->yres == default_display->yres &&
var->xres
== default_display->xres &&
var->bits_per_pixel
== default_display->bpp)
display = default_display;
else
for(i =0; i < mach_info->num_displays; i++)
if(type
== mach_info->displays[i].type &&
var->yres
== mach_info->displays[i].yres &&
var->xres
== mach_info->displays[i].xres &&
var->bits_per_pixel ==
mach_info->displays[i].bpp){
display = mach_info->displays + i;
break;
}
if(!display){
dprintk("wrong
resolution or depth %dx%d at %d bpp\n",
var->xres,var->yres,var->bits_per_pixel);
return-EINVAL;
}
/* it is always the size as
the display */
var->xres_virtual =
display->xres;
var->yres_virtual =
display->yres;
var->height
= display->height;
var->width = display->width;
/* copy lcd settings
*/
var->pixclock = display->pixclock;
var->left_margin =
display->left_margin;
var->right_margin = display->right_margin;
var->upper_margin =
display->upper_margin;
var->lower_margin = display->lower_margin;
var->vsync_len
= display->vsync_len;
var->hsync_len = display->hsync_len;
fbi->regs.lcdcon5 = display->lcdcon5;
/* set
display type */
fbi->regs.lcdcon1 = display->type;
var->transp.offset =0;
var->transp.length
=0;
/* set
r/g/b positions */
switch(var->bits_per_pixel){
case1:
case2:
case4:
var->red.offset =0;
var->red.length =var->bits_per_pixel;
var->green =var->red;
var->blue =var->red;
break;
case8:
if(display->type != S3C2410_LCDCON1_TFT){
/*
8 bpp 332 */
var->red.length =3;
var->red.offset =5;
var->green.length =3;
var->green.offset =2;
var->blue.length =2;
var->blue.offset =0;
}else{
var->red.offset =0;
var->red.length =8;
var->green =var->red;
var->blue =var->red;
}
break;
case12:
/* 12 bpp 444 */
var->red.length =4;
var->red.offset =8;
var->green.length =4;
var->green.offset =4;
var->blue.length =4;
var->blue.offset =0;
break;
default:
case16:
if(display->lcdcon5 & S3C2410_LCDCON5_FRM565){
/* 16 bpp, 565 format
*/
var->red.offset =11;
var->green.offset =5;
var->blue.offset =0;
var->red.length =5;
var->green.length =6;
var->blue.length =5;
}else{
/* 16 bpp, 5551 format
*/
var->red.offset =11;
var->green.offset =6;
var->blue.offset =1;
var->red.length =5;
var->green.length =5;
var->blue.length =5;
}
break;
case32:
/* 24 bpp 888 and 8 dummy */
var->red.length =8;
var->red.offset =16;
var->green.length =8;
var->green.offset =8;
var->blue.length =8;
var->blue.offset =0;
break;
}
return0;
}
/* s3c2410fb_calculate_stn_lcd_regs
*
* calculate register values from var settings
*/
staticvoid
s3c2410fb_calculate_stn_lcd_regs(conststruct fb_info *info,
struct s3c2410fb_hw *regs)
{
conststruct
s3c2410fb_info *fbi
= info->par;
conststruct
fb_var_screeninfo *var=&info->var;
int type = regs->lcdcon1 &~S3C2410_LCDCON1_TFT;
int hs =var->xres >>2;
unsigned wdly =(var->left_margin >>4)-1;
unsigned wlh =(var->hsync_len >>4)-1;
if(type != S3C2410_LCDCON1_STN4)
hs >>=1;
switch(var->bits_per_pixel){
case1:
regs->lcdcon1 |=
S3C2410_LCDCON1_STN1BPP;
break;
case2:
regs->lcdcon1 |=
S3C2410_LCDCON1_STN2GREY;
break;
case4:
regs->lcdcon1 |=
S3C2410_LCDCON1_STN4GREY;
break;
case8:
regs->lcdcon1 |=
S3C2410_LCDCON1_STN8BPP;
hs *=3;
break;
case12:
regs->lcdcon1 |=
S3C2410_LCDCON1_STN12BPP;
hs *=3;
break;
default:
/* invalid pixel depth
*/
dev_err(fbi->dev,"invalid bpp %d\n",
var->bits_per_pixel);
}
/*
update X/Y info */
dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",
var->left_margin,var->right_margin,var->hsync_len);
regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres -1);
if(wdly >3)
wdly =3;
if(wlh >3)
wlh =3;
regs->lcdcon3
= S3C2410_LCDCON3_WDLY(wdly)|
S3C2410_LCDCON3_LINEBLANK(var->right_margin /8)|
S3C2410_LCDCON3_HOZVAL(hs -1);
regs->lcdcon4
=
S3C2410_LCDCON4_WLH(wlh);
}
/* s3c2410fb_calculate_tft_lcd_regs
*
* calculate register values from var settings
*/
staticvoid
s3c2410fb_calculate_tft_lcd_regs(conststruct fb_info *info,
struct s3c2410fb_hw *regs)
{
conststruct
s3c2410fb_info *fbi
= info->par;
conststruct
fb_var_screeninfo *var=&info->var;
switch(var->bits_per_pixel){
case1:
regs->lcdcon1 |=
S3C2410_LCDCON1_TFT1BPP;
break;
case2:
regs->lcdcon1 |=
S3C2410_LCDCON1_TFT2BPP;
break;
case4:
regs->lcdcon1 |=
S3C2410_LCDCON1_TFT4BPP;
break;
case8:
regs->lcdcon1 |=
S3C2410_LCDCON1_TFT8BPP;
regs->lcdcon5
|= S3C2410_LCDCON5_BSWP
|
S3C2410_LCDCON5_FRM565;
regs->lcdcon5
&=~S3C2410_LCDCON5_HWSWP;
break;
case16:
regs->lcdcon1
|=
S3C2410_LCDCON1_TFT16BPP;//16色模式
regs->lcdcon5 &=~S3C2410_LCDCON5_BSWP;//关闭字节交换
regs->lcdcon5
|=
S3C2410_LCDCON5_HWSWP;//开启半字交换
break;
case32:
regs->lcdcon1 |=
S3C2410_LCDCON1_TFT24BPP;
regs->lcdcon5
&=~(S3C2410_LCDCON5_BSWP
|
S3C2410_LCDCON5_HWSWP
|
S3C2410_LCDCON5_BPP24BL);
break;
default:
/* invalid pixel depth
*/
dev_err(fbi->dev,"invalid bpp %d\n",
var->bits_per_pixel);
}
/*
update X/Y info */
dprintk("setting vert: up=%d, low=%d, sync=%d\n",
var->upper_margin,var->lower_margin,var->vsync_len);
dprintk("setting
horz: lft=%d, rt=%d, sync=%d\n",
var->left_margin,var->right_margin,var->hsync_len);
regs->lcdcon2
=
S3C2410_LCDCON2_LINEVAL(var->yres
-1)|
S3C2410_LCDCON2_VBPD(var->upper_margin -1)|
S3C2410_LCDCON2_VFPD(var->lower_margin -1)|
S3C2410_LCDCON2_VSPW(var->vsync_len -1);
regs->lcdcon3
=
S3C2410_LCDCON3_HBPD(var->right_margin -1)|
S3C2410_LCDCON3_HFPD(var->left_margin -1)|
S3C2410_LCDCON3_HOZVAL(var->xres -1);
regs->lcdcon4
=
S3C2410_LCDCON4_HSPW(var->hsync_len
-1);
}
/* s3c2410fb_activate_var
*
* activate (set) the
controller from the given framebuffer
*
information
*/
staticvoid
s3c2410fb_activate_var(struct fb_info *info)
{
struct
s3c2410fb_info *fbi
= info->par;
void __iomem *regs = fbi->io;
int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
struct fb_var_screeninfo *var=&info->var;
int clkdiv;
clkdiv = DIV_ROUND_UP(s3c2410fb_calc_pixclk(fbi,var->pixclock),2);
dprintk("%s:
var->xres = %d\n",
__func__,var->xres);
dprintk("%s: var->yres =
%d\n", __func__,var->yres);
dprintk("%s: var->bpp =
%d\n", __func__,var->bits_per_pixel);
if(type == S3C2410_LCDCON1_TFT){
s3c2410fb_calculate_tft_lcd_regs(info,&fbi->regs);
--clkdiv;
if(clkdiv
<0)
clkdiv =0;
}else{
s3c2410fb_calculate_stn_lcd_regs(info,&fbi->regs);
if(clkdiv
<2)
clkdiv =2;
}
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);
/* write new registers */
dprintk("new
register set:\n");
dprintk("lcdcon[1] = 0x%08lx\n",
fbi->regs.lcdcon1);
dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
dprintk("lcdcon[3] = 0x%08lx\n",
fbi->regs.lcdcon3);
dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
dprintk("lcdcon[5] = 0x%08lx\n",
fbi->regs.lcdcon5);
writel(fbi->regs.lcdcon1 &~S3C2410_LCDCON1_ENVID,
regs +
S3C2410_LCDCON1);
writel(fbi->regs.lcdcon2, regs
+ S3C2410_LCDCON2);//把设置的数据,真正写入物理寄存器
writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
/* set lcd address pointers
*/
s3c2410fb_set_lcdaddr(info);//地址寄存器设置
fbi->regs.lcdcon1 |=
S3C2410_LCDCON1_ENVID,
writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);//开启视频功能
}
/*
* s3c2410fb_set_par - Alters the
hardware state.
* @info: frame buffer structure
that represents a single frame buffer
*
*/
staticint s3c2410fb_set_par(struct fb_info *info)
{
struct
fb_var_screeninfo *var=&info->var;
switch(var->bits_per_pixel){
case32:
case16:
case12:
info->fix.visual
=
FB_VISUAL_TRUECOLOR;//设置为16位真彩色
break;
case1:
info->fix.visual = FB_VISUAL_MONO01;
break;
default:
info->fix.visual
=
FB_VISUAL_PSEUDOCOLOR;
break;
}
info->fix.line_length =(var->xres_virtual *var->bits_per_pixel)/8;
/* activate this new configuration */
s3c2410fb_activate_var(info);
return0;
}
staticvoid schedule_palette_update(struct s3c2410fb_info *fbi,
unsignedint
regno,unsignedint val)
{
unsignedlong flags;
unsignedlong
irqen;
void __iomem *irq_base = fbi->irq_base;
local_irq_save(flags);
fbi->palette_buffer[regno]= val;
if(!fbi->palette_ready){
fbi->palette_ready
=1;
/* enable IRQ
*/
irqen = readl(irq_base + S3C24XX_LCDINTMSK);
irqen &=~S3C2410_LCDINT_FRSYNC;
writel(irqen, irq_base
+
S3C24XX_LCDINTMSK);
}
local_irq_restore(flags);
}
/* from pxafb.c */
staticinlineunsignedint
chan_to_field(unsignedint
chan,
struct fb_bitfield *bf)
{
chan &=0xffff;
chan >>=16- bf->length;
return chan << bf->offset;
}
staticint
s3c2410fb_setcolreg(unsigned regno,
unsigned red,unsigned
green,unsigned blue,
unsigned transp,struct fb_info
*info)
{
struct
s3c2410fb_info *fbi
= info->par;
void __iomem *regs = fbi->io;
unsignedint
val;
/* dprintk("setcol:
regno=%d, rgb=%d,%d,%d\n",
regno, red, green, blue); */
switch(info->fix.visual){
case FB_VISUAL_TRUECOLOR:
/* true-colour, use
pseudo-palette */
if(regno <16){
u32 *pal = info->pseudo_palette;
val = chan_to_field(red, &info->var.red);
val |=
chan_to_field(green,&info->var.green);
val |= chan_to_field(blue, &info->var.blue);
pal[regno]= val;//假调色板
}
break;
case FB_VISUAL_PSEUDOCOLOR:
if(regno <256){
/* currently assume RGB
5-6-5 mode */
val =(red
>> 0)&0xf800;
val |=(green
>> 5)&0x07e0;
val |=(blue
>>11)&0x001f;
writel(val, regs +
S3C2410_TFTPAL(regno));//真正用到调色板
schedule_palette_update(fbi, regno,
val);
}
break;
default:
return1; /* unknown type */
}
return0;
}
/* s3c2410fb_lcd_enable
*
* shutdown the lcd
controller
*/
staticvoid
s3c2410fb_lcd_enable(struct s3c2410fb_info *fbi,int enable)
{
unsignedlong flags;
local_irq_save(flags);
if(enable)//开关LCD显示
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID;
else
fbi->regs.lcdcon1 &=~S3C2410_LCDCON1_ENVID;
writel(fbi->regs.lcdcon1, fbi->io + S3C2410_LCDCON1);
local_irq_restore(flags);
}
/*
*
s3c2410fb_blank
* @blank_mode: the blank
mode we want.
* @info: frame buffer
structure that represents a single frame buffer
*
* Blank the screen if
blank_mode != 0, else unblank. Return 0 if
* blanking succeeded, != 0 if un-/blanking failed due to
e.g. a
* video mode which doesn‘t support
it. Implements VESA suspend
* and
powerdown modes on hardware that supports disabling hsync/vsync:
*
* Returns negative
errno on error, or zero on success.
*
*/
staticint s3c2410fb_blank(int
blank_mode,struct fb_info *info)
{
struct s3c2410fb_info *fbi =
info->par;
void __iomem *tpal_reg = fbi->io;
dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);
tpal_reg += is_s3c2412(fbi)? S3C2412_TPAL : S3C2410_TPAL;
if(blank_mode == FB_BLANK_POWERDOWN){
s3c2410fb_lcd_enable(fbi,0);
}else{
s3c2410fb_lcd_enable(fbi,1);
}
if(blank_mode == FB_BLANK_UNBLANK)
writel(0x0,
tpal_reg);//禁止临时调色板
else{
dprintk("setting
TPAL to output 0x000000\n");
writel(S3C2410_TPAL_EN, tpal_reg);//使能
}
return0;
}
staticint
s3c2410fb_debug_show(struct device *dev,
struct device_attribute *attr,char*buf)
{
return snprintf(buf, PAGE_SIZE,"%s\n", debug
?"on":"off");
}
staticint
s3c2410fb_debug_store(struct device *dev,
struct device_attribute *attr,
constchar*buf,size_t len)
{
if(len <1)
return-EINVAL;
if(strnicmp(buf,"on",2)==0||
strnicmp(buf,"1",1)==0){
debug =1;
printk(KERN_DEBUG "s3c2410fb: Debug
On");
}elseif(strnicmp(buf,"off",3)==0||
strnicmp(buf,"0",1)==0){
debug =0;
printk(KERN_DEBUG "s3c2410fb: Debug
Off");
}else{
return-EINVAL;
}
return
len;
}
static DEVICE_ATTR(debug,0666, s3c2410fb_debug_show, s3c2410fb_debug_store);
staticstruct
fb_ops s3c2410fb_ops ={
.owner = THIS_MODULE,
.fb_check_var = s3c2410fb_check_var,
.fb_set_par =
s3c2410fb_set_par,/*设置fb_info中的参数,主要是LCD的显示模式*/
.fb_blank =
s3c2410fb_blank,//显示空白
.fb_setcolreg = s3c2410fb_setcolreg,
.fb_fillrect =
cfb_fillrect,
.fb_copyarea =
cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
/*
*
s3c2410fb_map_video_memory():
* Allocates
the DRAM memory for the frame buffer. This buffer is
* remapped into a non-cached, non-buffered, memory region
to
* allow palette and pixel writes to
occur without flushing the
* cache.
Once this area is remapped, all virtual memory
* access to the video memory should occur at the new
region.
*/
staticint __init
s3c2410fb_map_video_memory(struct fb_info *info)
{
struct
s3c2410fb_info *fbi
= info->par;
dma_addr_t map_dma;//虚拟地址起始地址
unsigned
map_size =
PAGE_ALIGN(info->fix.smem_len);/* to align the pointer to the (next)
page boundary */
dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);
//DMA内存分配,这里为非cache式的,即非缓冲的,保证了数据的一致和稳定
info->screen_base =
dma_alloc_writecombine(fbi->dev,
map_size,
&map_dma,
GFP_KERNEL);
if(info->screen_base){
/* prevent initial garbage on
screen */
dprintk("map_video_memory: clear %p:%08x\n",
info->screen_base, map_size);
//将分配的地址清零
memset(info->screen_base,0x00,
map_size);
//将虚拟地址起始地址放于固定参数中
info->fix.smem_start = map_dma;
dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",
info->fix.smem_start,
info->screen_base,
map_size);
}
return
info->screen_base
?0:-ENOMEM;
}
staticinlinevoid s3c2410fb_unmap_video_memory(struct fb_info *info)
{
struct
s3c2410fb_info *fbi
= info->par;
dma_free_writecombine(fbi->dev, PAGE_ALIGN(info->fix.smem_len),
info->screen_base, info->fix.smem_start);
}
staticinlinevoid modify_gpio(void __iomem *reg,
unsignedlongset,unsignedlong
mask)
{
unsignedlong tmp;
tmp =
readl(reg)&~mask;
writel(tmp
|set, reg);
}
/*
*
s3c2410fb_init_registers - Initialise all LCD-related registers
*/
staticint s3c2410fb_init_registers(struct fb_info
*info)
{
struct
s3c2410fb_info *fbi
= info->par;//获得s3c24xxfb_probe中struct s3c2410fb_info
*info的值
struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
unsignedlong
flags;
void __iomem *regs = fbi->io;
void __iomem *tpal;
void __iomem *lpcsel;
if(is_s3c2412(fbi)){
tpal = regs +
S3C2412_TPAL;
lpcsel =
regs +
S3C2412_TCONSEL;
}else{
tpal =
regs +
S3C2410_TPAL;
lpcsel =
regs +
S3C2410_LPCSEL;
}
/* Initialise LCD with values from haret */
local_irq_save(flags); //关闭cpu所有中断
/* modify the gpio(s) with interrupts set (bjd)
*/
//设置多功能引脚,选为lcd模式
modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask);
modify_gpio(S3C2410_GPCCON,
mach_info->gpccon,
mach_info->gpccon_mask);
modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask);
modify_gpio(S3C2410_GPDCON,
mach_info->gpdcon,
mach_info->gpdcon_mask);
local_irq_restore(flags); //恢复cpu所有中断
dprintk("LPCSEL = 0x%08lx\n",
mach_info->lpcsel);
writel(mach_info->lpcsel, lpcsel);
dprintk("replacing TPAL %08x\n", readl(tpal));
/* ensure temporary palette
disabled */
writel(0x00, tpal);//先关闭临时调色板
return 0;
}
staticvoid s3c2410fb_write_palette(struct s3c2410fb_info *fbi)
{
unsignedint
i;
void __iomem *regs = fbi->io;
fbi->palette_ready =0;
for(i =0; i <256; i++){
unsignedlong ent
= fbi->palette_buffer[i];
if(ent
==
PALETTE_BUFF_CLEAR)
continue;
writel(ent, regs + S3C2410_TFTPAL(i)); //写入调色板RAM
/* it seems the only way to know
exactly
*if the palette
wrote ok,is
to check
* to see if the value verifies ok
*/
if (readw(regs + S3C2410_TFTPAL(i)) ==
ent)
fbi->palette_buffer[i] =
PALETTE_BUFF_CLEAR;
else
fbi->palette_ready = 1;
/*retry*/
}
}
static irqreturn_t s3c2410fb_irq(int irq, void
*dev_id)
{
struct
s3c2410fb_info *fbi = dev_id;
void __iomem
*irq_base = fbi->irq_base;
unsigned long
lcdirq = readl(irq_base + S3C24XX_LCDINTPND);
if (lcdirq & S3C2410_LCDINT_FRSYNC)
{//帧同步中断
if(fbi->palette_ready)//如果用的真彩色模式,感觉中断没被用到
s3c2410fb_write_palette(fbi);
writel(S3C2410_LCDINT_FRSYNC,
irq_base +
S3C24XX_LCDINTPND);//清LCD总挂起寄存器
writel(S3C2410_LCDINT_FRSYNC,
irq_base +
S3C24XX_LCDSRCPND);//清LCD子挂起寄存器
}
return
IRQ_HANDLED;
}
#ifdef CONFIG_CPU_FREQ
static int s3c2410fb_cpufreq_transition(struct notifier_block *nb,
unsignedlong
val,void*data)
{
struct cpufreq_freqs *freqs =
data;
struct s3c2410fb_info *info;
struct
fb_info *fbinfo;
long delta_f;
info =
container_of(nb,struct
s3c2410fb_info,
freq_transition);
fbinfo =
platform_get_drvdata(to_platform_device(info->dev));
/* work out change, <0
for speed-up */
delta_f = info->clk_rate
- clk_get_rate(info->clk);
if((val == CPUFREQ_POSTCHANGE && delta_f >0)||
(val == CPUFREQ_PRECHANGE && delta_f <0)){
info->clk_rate =
clk_get_rate(info->clk);
s3c2410fb_activate_var(fbinfo);
}
return0;
}
staticinlineint s3c2410fb_cpufreq_register(struct s3c2410fb_info *info)
{
info->freq_transition.notifier_call =
s3c2410fb_cpufreq_transition;
return
cpufreq_register_notifier(&info->freq_transition,
CPUFREQ_TRANSITION_NOTIFIER);
}
staticinlinevoid s3c2410fb_cpufreq_deregister(struct s3c2410fb_info *info)
{
cpufreq_unregister_notifier(&info->freq_transition,
CPUFREQ_TRANSITION_NOTIFIER);
}
#else
staticinlineint s3c2410fb_cpufreq_register(struct
s3c2410fb_info *info)
{
return0;
}
staticinlinevoid s3c2410fb_cpufreq_deregister(struct s3c2410fb_info *info)
{
}
#endif
staticchar
driver_name[]="s3c2410fb";
staticint
__init 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;//取出设置好的mach_info指针
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;
}
//获取lcd屏幕参数
display = mach_info->displays + mach_info->default_display;
//获取irq中断资源,其它此函数也是调用platform_get_resource
irq
= platform_get_irq(pdev,0);
if(irq <0){
dev_err(&pdev->dev,"no irq for
device\n");
return-ENOENT;
}
//分配fbinfo结构体
fbinfo = framebuffer_alloc(sizeof(struct
s3c2410fb_info),&pdev->dev);
if(!fbinfo)
return-ENOMEM;
//设置platform_device->device->device_private->(void
*driver_data)=fbinfo
//空类型可存任何数据结构,这里存在这的意思是后面通过私有数据结构可用到其它地方
platform_set_drvdata(pdev,
fbinfo);
//初始info相关部分
info =
fbinfo->par;//这里很重要,后面的参数设置要用
info->dev =&pdev->dev;
info->drv_type =
drv_type;
//获取IO资源
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;
}
//IO内存操作:申请->映射->访问->释放
//操作函数:request_mem_region->ioremap->writel
etc.->iounmap+release_mem_region
//有时release_mem_region可以这样用release_resource+kfree这点,可以参看
//release_mem_region代码实现可知
size
=(res->end-
res->start)+1;
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);//映射
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->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;
//操作函数集,这里才是上层fbmem.c中read/write的最终实现函数
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, IRQF_DISABLED, 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(!info->clk || IS_ERR(info->clk)){
printk(KERN_ERR "failed to get lcd clock
source\n");
ret
=-ENOENT;
goto
release_irq;
}
clk_enable(info->clk);
dprintk("got
and enabled clock\n");
msleep(1);
//获取时钟频率,这里=HCLK=100M
info->clk_rate =
clk_get_rate(info->clk);
/* find maximum required
memory size for display */
for(i =0; i < mach_info->num_displays; i++){
unsignedlong
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;
//Initialise all LCD-related
registers
s3c2410fb_init_registers(fbinfo);
s3c2410fb_check_var(&fbinfo->var, fbinfo);//检查并设置可变参数
ret = s3c2410fb_cpufreq_register(info);
//在s3c2440中没定义,因为CONFIG_CPU_FREQ没定义
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);
return0;
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_resource(info->mem);
kfree(info->mem);
dealloc_fb:
platform_set_drvdata(pdev, NULL);
framebuffer_release(fbinfo);
return
ret;
}
staticint
__init s3c2410fb_probe(struct platform_device *pdev)
{
return s3c24xxfb_probe(pdev,
DRV_S3C2410);
}
staticint
__init s3c2412fb_probe(struct platform_device *pdev)
{
return s3c24xxfb_probe(pdev,
DRV_S3C2412);
}
/*
*
Cleanup
*/
staticint
s3c2410fb_remove(struct platform_device *pdev)
{
struct fb_info *fbinfo = platform_get_drvdata(pdev);
struct
s3c2410fb_info *info
= fbinfo->par;
int
irq;
unregister_framebuffer(fbinfo);
s3c2410fb_cpufreq_deregister(info);
s3c2410fb_lcd_enable(info,0);
msleep(1);
s3c2410fb_unmap_video_memory(fbinfo);
if(info->clk){
clk_disable(info->clk);
clk_put(info->clk);
info->clk =
NULL;
}
irq =
platform_get_irq(pdev,0);
free_irq(irq,
info);
iounmap(info->io);
release_resource(info->mem);
kfree(info->mem);
platform_set_drvdata(pdev, NULL);
framebuffer_release(fbinfo);
return0;
}
#ifdef CONFIG_PM
/* suspend and resume support for the lcd controller
*/
staticint s3c2410fb_suspend(struct platform_device *dev,pm_message_t state)
{
struct
fb_info *fbinfo =
platform_get_drvdata(dev);
struct
s3c2410fb_info *info
= fbinfo->par;
s3c2410fb_lcd_enable(info,0);
/* sleep before disabling
the clock, we need to ensure
* the LCD DMA
engine is not going to get back on the bus
*
before the clock goes off again (bjd) */
msleep(1);
clk_disable(info->clk);
return0;
}
staticint
s3c2410fb_resume(struct platform_device *dev)
{
struct fb_info
*fbinfo = platform_get_drvdata(dev);
struct s3c2410fb_info *info =
fbinfo->par;
clk_enable(info->clk);
msleep(1);
s3c2410fb_init_registers(fbinfo);
/* re-activate our display
after resume */
s3c2410fb_activate_var(fbinfo);
s3c2410fb_blank(FB_BLANK_UNBLANK, fbinfo);
return0;
}
#else
#define s3c2410fb_suspend NULL
#define s3c2410fb_resume
NULL
#endif
static struct platform_driver s3c2410fb_driver =
{
.probe =
s3c2410fb_probe,
.remove = s3c2410fb_remove,
.suspend = s3c2410fb_suspend,
.resume = s3c2410fb_resume,
.driver ={
.name ="s3c2410-lcd",
.owner =
THIS_MODULE,
},
};
staticstruct
platform_driver s3c2412fb_driver ={
.probe =
s3c2412fb_probe,
.remove = s3c2410fb_remove,
.suspend = s3c2410fb_suspend,
.resume = s3c2410fb_resume,
.driver ={
.name ="s3c2412-lcd",
.owner =
THIS_MODULE,
},
};
int __init
s3c2410fb_init(void)
{
int ret = platform_driver_register(&s3c2410fb_driver);
if(ret ==0)
ret = platform_driver_register(&s3c2412fb_driver);
return
ret;
}
staticvoid __exit s3c2410fb_cleanup(void)
{
platform_driver_unregister(&s3c2410fb_driver);
platform_driver_unregister(&s3c2412fb_driver);
}
module_init(s3c2410fb_init);
module_exit(s3c2410fb_cleanup);
MODULE_AUTHOR("Arnaud
Patard <arnaud.patard@rtp-net.org>, "
"Ben
Dooks <ben-linux@fluff.org>");
MODULE_DESCRIPTION("Framebuffer driver for the s3c2410");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:s3c2410-lcd");
MODULE_ALIAS("platform:s3c2412-lcd");
测试应用程序:
#include <unistd.h>
#include <stdio.h>
#include
<fcntl.h>
#include <linux/fb.h>
#include
<sys/mman.h>
int main () {
int fp=0;
struct
fb_var_screeninfo vinfo;
struct fb_fix_screeninfo
finfo;
long screensize=0;
char *fbp
= 0;
int x = 0, y = 0;
long location
= 0;
fp = open ("/dev/fb0",O_RDWR);
if (fp <
0){
printf("Error : Can not open
framebuffer device\n");
exit(1);
}
if
(ioctl(fp,FBIOGET_FSCREENINFO,&finfo)){
printf("Error reading fixed
information\n");
exit(2);
}
if
(ioctl(fp,FBIOGET_VSCREENINFO,&vinfo)){
printf("Error reading variable
information\n");
exit(3);
}
screensize = vinfo.xres * vinfo.yres *
vinfo.bits_per_pixel / 8; //单帧画面空间
/*这就是把fp所指的文件中从开始到screensize大小的内容给映射出来,得到一个指向这块空间的指针*/
fbp
=(char *) mmap (0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,
fp,0);
if ((int) fbp == -1)
{
printf ("Error: failed to
map framebuffer device to
memory.\n");
exit
(4);
}
/*这是你想画的点的位置坐标,(0,0)点在屏幕左上角*/
for(x=100;x<150;x++)
{
for(y=100;y<150;y++)
{
location = x * (vinfo.bits_per_pixel / 8) + y *
finfo.line_length;
*(fbp + location) = 255; /* 蓝色的色深 */
/*直接赋值来改变屏幕上某点的颜色*/
*(fbp + location + 1) = 0; /* 绿色的色深*/
/*注明:这几个赋值是针对每像素四字节来设置的,如果针对每像素2字节,*/
*(fbp + location + 2) = 0; /* 红色的色深*/
/*比如RGB565,则需要进行转化*/
*(fbp + location + 3) = 0; /*
是否透明*/
}
}
munmap (fbp, screensize);
/*解除映射*/
close (fp);
/*关闭文件*/
return 0;
}
通过mmap函数映射后,可直接操作设备缓冲区。
相对应的fbmem.c中的iotcl和mmap函数很重要。
static int
fb_mmap(struct file *file, struct vm_area_struct *
vma)
{
int fbidx =
iminor(file->f_path.dentry->d_inode);
struct fb_info *info =
registered_fb[fbidx];
struct fb_ops *fb =
info->fbops;
unsigned long off;
unsigned long
start;
u32 len;
if (vma->vm_pgoff > (~0UL >>
PAGE_SHIFT))
return -EINVAL;
off = vma->vm_pgoff
<< PAGE_SHIFT;
if (!fb)
return
-ENODEV;
mutex_lock(&info->mm_lock);
if
(fb->fb_mmap) {
int res;
res =
fb->fb_mmap(info,
vma);
mutex_unlock(&info->mm_lock);
return
res;
}
/* frame buffer memory */
start =
info->fix.smem_start;
len = PAGE_ALIGN((start & ~PAGE_MASK) +
info->fix.smem_len);
if (off >= len) {
/* memory
mapped io */
off -= len;
if
(info->var.accel_flags)
{
mutex_unlock(&info->mm_lock);
return
-EINVAL;
}
start =
info->fix.mmio_start;
len = PAGE_ALIGN((start &
~PAGE_MASK) +
info->fix.mmio_len);
}
mutex_unlock(&info->mm_lock);
start
&= PAGE_MASK;
if ((vma->vm_end - vma->vm_start + off) >
len)
return -EINVAL;
off +=
start;
vma->vm_pgoff = off >> PAGE_SHIFT;
/* This is
an IO map - tell maydump to skip this VMA */
vma->vm_flags |= VM_IO
| VM_RESERVED;
fb_pgprotect(file, vma, off);
if
(io_remap_pfn_range(vma, vma->vm_start, off >>
PAGE_SHIFT,
vma->vm_end -
vma->vm_start, vma->vm_page_prot))//核心部分,建立页表映射
return
-EAGAIN;
return 0;
}