LCD驱动的实现

来源:互联网 发布:藏文输入法软件下载 编辑:程序博客网 时间:2024/05/17 06:57

1、LCD设备驱动在linux内核中是作为平台设备存在,所以又要说那些已经说过很多遍的东西。

int __devinit s3cfb_init(void)
{
return platform_driver_register(&s3cfb_driver);
}
static void __exit s3cfb_cleanup(void)
{
platform_driver_unregister(&s3cfb_driver);
}
module_init(s3cfb_init);
module_exit(s3cfb_cleanup);

对应的platform_driver结构体为:
static struct platform_driver s3cfb_driver = {
.probe = s3cfb_probe,
.remove = s3cfb_remove,
.suspend = s3cfb_suspend,
.resume = s3cfb_resume,
        .driver = {
.name = "s3c-lcd",
.owner = THIS_MODULE,
},
};

那么平台设备的资源呢?

/* LCD Controller */

static u64 s3c_device_lcd_dmamask = 0xffffffffUL;


struct platform_device s3c_device_lcd = {
.name  = "s3c-lcd",
.id  = -1,设备编号,-1表示只有这样一个设备
.num_resources = ARRAY_SIZE(s3c_lcd_resource),
.resource  = s3c_lcd_resource,
.dev              = {
.dma_mask = &s3c_device_lcd_dmamask,
.coherent_dma_mask= 0xffffffffUL
}
};


static struct resource s3c_lcd_resource[] = {
[0] = {
.start = S3C64XX_PA_LCD,  //LCD的I/O内存的开始位置
.end   = S3C64XX_PA_LCD + SZ_1M - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_LCD_VSYNC,    //LCD的开始中断号
.end   = IRQ_LCD_SYSTEM,  //LCD的结束中断号
.flags = IORESOURCE_IRQ,
}
};

2、分析其probe函数,源码如下:

/*
 *  Probe
 */
static int __init s3cfb_probe(struct platform_device *pdev)
{
struct resource *res;
struct fb_info *fbinfo;
s3cfb_info_t *info;



char driver_name[] = "s3cfb";
int index = 0, ret, size;


fbinfo = framebuffer_alloc(sizeof(s3cfb_info_t), &pdev->dev);申请一个s3cfb_info_t结构体的空间

if (!fbinfo)
return -ENOMEM;


platform_set_drvdata(pdev, fbinfo);
info = fbinfo->par;
info->dev = &pdev->dev;  填充info结构体变量的相应信息


res = platform_get_resource(pdev, IORESOURCE_MEM, 0);获取LCD平台设备所使用的I/O端口资源。
if (res == NULL) {
dev_err(&pdev->dev, "failed to get memory registers\n");
ret = -ENXIO;
goto dealloc_fb;
}

size = (res->end - res->start) + 1;
info->mem = request_mem_region(res->start, size, pdev->name);申请LCD设备所占的I/O空间
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);将LCD的I/O端口所占用的这段I/O空间映射到内存的虚拟地址。
if (info->io == NULL) {
dev_err(&pdev->dev, "ioremap() of registers failed\n");
ret = -ENXIO;
goto release_mem;
}


s3cfb_pre_init();这个函数的源码在S3cfb_fimd4x.c (linux2.6.28\drivers\video\samsung)文件中,如下所示:

void s3cfb_pre_init(void)
{
/* initialize the fimd specific */
s3cfb_fimd.vidintcon0 &= ~S3C_VIDINTCON0_FRAMESEL0_MASK;
s3cfb_fimd.vidintcon0 |= S3C_VIDINTCON0_FRAMESEL0_VSYNC;
s3cfb_fimd.vidintcon0 |= S3C_VIDINTCON0_INTFRMEN_ENABLE;


writel(s3cfb_fimd.vidintcon0, S3C_VIDINTCON0);
}

这个函数中涉及到一个结构体s3cfb_fimd_info_t s3cfb_fimd,和这个函数在同一个文件中定义,列出部分源码:

s3cfb_fimd_info_t s3cfb_fimd = {
.vidcon0 = S3C_VIDCON0_INTERLACE_F_PROGRESSIVE | S3C_VIDCON0_VIDOUT_RGB_IF | S3C_VIDCON0_L1_DATA16_SUB_16_MODE | \
S3C_VIDCON0_L0_DATA16_MAIN_16_MODE | S3C_VIDCON0_PNRMODE_RGB_P | \
S3C_VIDCON0_CLKVALUP_ALWAYS | S3C_VIDCON0_CLKDIR_DIVIDED | S3C_VIDCON0_CLKSEL_F_HCLK | \
S3C_VIDCON0_ENVID_DISABLE | S3C_VIDCON0_ENVID_F_DISABLE,


.dithmode = (S3C_DITHMODE_RDITHPOS_5BIT | S3C_DITHMODE_GDITHPOS_6BIT | S3C_DITHMODE_BDITHPOS_5BIT ) & S3C_DITHMODE_DITHERING_DISABLE,


#if defined (CONFIG_FB_S3C_BPP_8)
.wincon0 =  S3C_WINCONx_BYTSWP_ENABLE | S3C_WINCONx_BURSTLEN_4WORD | S3C_WINCONx_BPPMODE_F_8BPP_PAL,
.wincon1 =  S3C_WINCONx_HAWSWP_ENABLE | S3C_WINCONx_BURSTLEN_4WORD | S3C_WINCONx_BPPMODE_F_16BPP_565 | S3C_WINCONx_BLD_PIX_PLANE | S3C_WINCONx_ALPHA_SEL_1,
.bpp = S3CFB_PIXEL_BPP_8,
.bytes_per_pixel = 1,
.wpalcon = S3C_WPALCON_W0PAL_16BIT,

..........

..........

.vidosd1c = S3C_VIDOSDxC_ALPHA1_B(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_G(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_R(S3CFB_MAX_ALPHA_LEVEL),
.vidosd2c = S3C_VIDOSDxC_ALPHA1_B(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_G(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_R(S3CFB_MAX_ALPHA_LEVEL),
.vidosd3c = S3C_VIDOSDxC_ALPHA1_B(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_G(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_R(S3CFB_MAX_ALPHA_LEVEL),
.vidosd4c = S3C_VIDOSDxC_ALPHA1_B(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_G(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_R(S3CFB_MAX_ALPHA_LEVEL),


.vidintcon0 = S3C_VIDINTCON0_FRAMESEL0_VSYNC | S3C_VIDINTCON0_FRAMESEL1_NONE | S3C_VIDINTCON0_INTFRMEN_DISABLE | \
S3C_VIDINTCON0_FIFOSEL_WIN0 | S3C_VIDINTCON0_FIFOLEVEL_25 | S3C_VIDINTCON0_INTFIFOEN_DISABLE | S3C_VIDINTCON0_INTEN_ENABLE,
.vidintcon1 = 0,


.xoffset = 0,
.yoffset = 0,


.w1keycon0 = S3C_WxKEYCON0_KEYBLEN_DISABLE | S3C_WxKEYCON0_KEYEN_F_DISABLE | S3C_WxKEYCON0_DIRCON_MATCH_FG_IMAGE | S3C_WxKEYCON0_COMPKEY(0x0),
.............
.w4keycon1 = S3C_WxKEYCON1_COLVAL(0xffffff),


.sync = 0,
.cmap_static = 1,


.vs_offset = S3CFB_DEFAULT_DISPLAY_OFFSET,
.brightness = S3CFB_DEFAULT_BRIGHTNESS,
.backlight_level = S3CFB_DEFAULT_BACKLIGHT_LEVEL,
.backlight_power = 1,
.lcd_power = 1,
};

那么对应的结构体原型在哪呢?

在S3cfb.h (linux2.6.28\drivers\video\samsung)文件中,如下所示:

看那些对应的注释,也应该大致明白这个结构体的作用,存储与显示控制器有关的信息,还有显示屏幕的信息。不知道大家对s3cfb_fimd_info_t这个结构体的命名有何看法?名字往往代表了这个结构体的作用。其实这个结构体的名字给我们的信息是:

fimd—   ——    ——

FIMD: 
                Fully Interactive Mobile Display (完全交互式移动显示设备)

这是第一个概念,另一个概念是 OSD,我查了下资料,有这样的含义:

OSD是on-screen display的简称,即屏幕菜单式调节方式。一般是按Menu键后屏幕弹出的显示器各项调节项目信息的矩形菜单,可通过该菜单对显示器各项工作指标包括色彩、模式、几何形状等进行调整,从而达到最佳的使用状态。

现在对这个结构体的认识是不是深刻多了?

typedef struct {


/* Screen size */
int width;
int height;


/* Screen info */
int xres;
int yres;


/* Virtual Screen info */
int xres_virtual;
int yres_virtual;
int xoffset;
int yoffset;


/* OSD Screen size */
int osd_width;
int osd_height;


/* OSD Screen info */
int osd_xres;
int osd_yres;


/* OSD Screen info */
int osd_xres_virtual;
int osd_yres_virtual;


int bpp;
int bytes_per_pixel;
unsigned long pixclock;


int hsync_len;
int left_margin;
int right_margin;
int vsync_len;
int upper_margin;
int lower_margin;
int sync;


int cmap_grayscale:1;
int cmap_inverse:1;
int cmap_static:1;
int unused:29;


/* backlight info */
int backlight_min;
int backlight_max;
int backlight_default;


int vs_offset;
int brightness;
int palette_win;
int backlight_level;
int backlight_power;
int lcd_power;


s3cfb_vsync_info_t vsync_info;
s3cfb_vs_info_t vs_info;


/* lcd configuration registers */
unsigned long lcdcon1;
unsigned long lcdcon2;


        unsigned long lcdcon3;
unsigned long lcdcon4;
unsigned long lcdcon5;


/* GPIOs */
unsigned long gpcup;
unsigned long gpcup_mask;
unsigned long gpccon;
unsigned long gpccon_mask;
unsigned long gpdup;
unsigned long gpdup_mask;
unsigned long gpdcon;
unsigned long gpdcon_mask;


/* lpc3600 control register */
unsigned long lpcsel;
unsigned long lcdtcon1;
unsigned long lcdtcon2;
unsigned long lcdtcon3;
unsigned long lcdosd1;
unsigned long lcdosd2;
unsigned long lcdosd3;
unsigned long lcdsaddrb1;
unsigned long lcdsaddrb2;
unsigned long lcdsaddrf1;
unsigned long lcdsaddrf2;
unsigned long lcdeaddrb1;
unsigned long lcdeaddrb2;
unsigned long lcdeaddrf1;
unsigned long lcdeaddrf2;
unsigned long lcdvscrb1;
unsigned long lcdvscrb2;
unsigned long lcdvscrf1;
unsigned long lcdvscrf2;
unsigned long lcdintcon;
unsigned long lcdkeycon;
unsigned long lcdkeyval;
unsigned long lcdbgcon;
unsigned long lcdfgcon;
unsigned long lcddithcon;


unsigned long vidcon0;
unsigned long vidcon1;
unsigned long vidtcon0;
unsigned long vidtcon1;
unsigned long vidtcon2;
unsigned long vidtcon3;
unsigned long wincon0;
unsigned long wincon2;
unsigned long wincon1;
unsigned long wincon3;
unsigned long wincon4;


unsigned long vidosd0a;
unsigned long vidosd0b;
unsigned long vidosd0c;
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 vidosd4a;
unsigned long vidosd4b;
unsigned long vidosd4c;


unsigned long vidw00add0b0;
unsigned long vidw00add0b1;
unsigned long vidw01add0;
unsigned long vidw01add0b0;
unsigned long vidw01add0b1;


unsigned long vidw00add1b0;
unsigned long vidw00add1b1;
unsigned long vidw01add1;
unsigned long vidw01add1b0;
unsigned long vidw01add1b1;


unsigned long vidw00add2b0;
unsigned long vidw00add2b1;


unsigned long vidw02add0;
unsigned long vidw03add0;
unsigned long vidw04add0;


unsigned long vidw02add1;
unsigned long vidw03add1;
unsigned long vidw04add1;
unsigned long vidw00add2;
unsigned long vidw01add2;
unsigned long vidw02add2;
unsigned long vidw03add2;
unsigned long vidw04add2;


unsigned long vidintcon;
unsigned long vidintcon0;
unsigned long vidintcon1;
unsigned long w1keycon0;
unsigned long w1keycon1;
unsigned long w2keycon0;
unsigned long w2keycon1;
unsigned long w3keycon0;
unsigned long w3keycon1;
unsigned long w4keycon0;
unsigned long w4keycon1;


unsigned long win0map;
unsigned long win1map;
unsigned long win2map;
unsigned long win3map;
unsigned long win4map;


unsigned long wpalcon;
unsigned long dithmode;
unsigned long intclr0;
unsigned long intclr1;
unsigned long intclr2;


unsigned long win0pal;
unsigned long win1pal;


/* utility functions */
void (*set_backlight_power)(int);
void (*set_lcd_power)(int);
void (*set_brightness)(int);
int (*map_video_memory)(s3cfb_info_t *);
int (*unmap_video_memory)(s3cfb_info_t *);
}s3cfb_fimd_info_t;


下一篇接着从这里分析:
s3cfb_set_backlight_power(1);
s3cfb_set_lcd_power(1);
s3cfb_set_backlight_level(S3CFB_DEFAULT_BACKLIGHT_LEVEL);


info->clk = clk_get(NULL, "lcd");


if (!info->clk || IS_ERR(info->clk)) {
printk(KERN_INFO "failed to get lcd clock source\n");
ret =  -ENOENT;
goto release_io;
}


clk_enable(info->clk);
printk("S3C_LCD clock got enabled :: %ld.%03ld Mhz\n", PRINT_MHZ(clk_get_rate(info->clk)));


s3cfb_fimd.vsync_info.count = 0;
init_waitqueue_head(&s3cfb_fimd.vsync_info.wait_queue);


res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);


if (res == NULL) {
dev_err(&pdev->dev, "failed to get irq\n");
ret = -ENXIO;
goto release_clock;
}


ret = request_irq(res->start, s3cfb_irq, 0, "s3c-lcd", pdev);


if (ret != 0) {
printk("Failed to install irq (%d)\n", ret);
goto release_clock;
}


msleep(5);


for (index = 0; index < S3CFB_NUM; index++) {
s3cfb_info[index].mem = info->mem;
s3cfb_info[index].io = info->io;
s3cfb_info[index].clk = info->clk;


s3cfb_init_fbinfo(&s3cfb_info[index], driver_name, index);


/* Initialize video memory */
ret = s3cfb_map_video_memory(&s3cfb_info[index]);


if (ret) {
printk("Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto release_irq;
}


ret = s3cfb_init_registers(&s3cfb_info[index]);
ret = s3cfb_check_var(&s3cfb_info[index].fb.var, &s3cfb_info[index].fb);


if (index < 2){
if (fb_alloc_cmap(&s3cfb_info[index].fb.cmap, 256, 0) < 0)
goto dealloc_fb;
} else {
if (fb_alloc_cmap(&s3cfb_info[index].fb.cmap, 16, 0) < 0)
goto dealloc_fb;
}


ret = register_framebuffer(&s3cfb_info[index].fb);


if (ret < 0) {
printk(KERN_ERR "Failed to register framebuffer device: %d\n", ret);
goto free_video_memory;
}


printk(KERN_INFO "fb%d: %s frame buffer device\n", s3cfb_info[index].fb.node, s3cfb_info[index].fb.fix.id);
}


/* create device files */
ret = device_create_file(&(pdev->dev), &dev_attr_backlight_power);


if (ret < 0)
printk(KERN_WARNING "s3cfb: failed to add entries\n");


ret = device_create_file(&(pdev->dev), &dev_attr_backlight_level);


if (ret < 0)
printk(KERN_WARNING "s3cfb: failed to add entries\n");


ret = device_create_file(&(pdev->dev), &dev_attr_lcd_power);


if (ret < 0)
printk(KERN_WARNING "s3cfb: failed to add entries\n");


return 0;


free_video_memory:
s3cfb_unmap_video_memory(&s3cfb_info[index]);


release_irq:
free_irq(res->start, &info);


release_clock:
clk_disable(info->clk);
clk_put(info->clk);


release_io:
iounmap(info->io);


release_mem:
release_resource(info->mem);
kfree(info->mem);


dealloc_fb:
framebuffer_release(fbinfo);
return ret;
}

上一篇在说LCD设备驱动对应的probe函数时,没有说完,这一篇接着继续说probe函数。


s3cfb_pre_init();上一次说到了这个函数,而且还讲到了一个结构体s3cfb_fimd_info_t,并说了它的大致作用。
s3cfb_set_backlight_power(1);看名字就只到它的大致作用,与背光灯有关,源码如下:

static void s3cfb_set_backlight_power(int to)
{
s3cfb_fimd.backlight_power = to;


if (s3cfb_fimd.set_backlight_power)
(s3cfb_fimd.set_backlight_power)(to);  函数指针
}

s3cfb_set_lcd_power(1);和上的类似,列出其源码:

static void s3cfb_set_lcd_power(int to)
{
s3cfb_fimd.lcd_power = to;


if (s3cfb_fimd.set_lcd_power)
(s3cfb_fimd.set_lcd_power)(to);也是函数指针
}

s3cfb_set_backlight_level(S3CFB_DEFAULT_BACKLIGHT_LEVEL);也和上面差不多,就不说了。


info->clk = clk_get(NULL, "lcd");得到LCD对应的时钟

if (!info->clk || IS_ERR(info->clk)) {
printk(KERN_INFO "failed to get lcd clock source\n");
ret =  -ENOENT;
goto release_io;
}

clk_enable(info->clk);  使能时钟
printk("S3C_LCD clock got enabled :: %ld.%03ld Mhz\n", PRINT_MHZ(clk_get_rate(info->clk)));


s3cfb_fimd.vsync_info.count = 0;
init_waitqueue_head(&s3cfb_fimd.vsync_info.wait_queue);   //初始化帧同步信号的等待队列头


res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);获取LCD平台设备所使用的中断号

if (res == NULL) {
dev_err(&pdev->dev, "failed to get irq\n");
ret = -ENXIO;
goto release_clock;
}


ret = request_irq(res->start, s3cfb_irq, 0, "s3c-lcd", pdev);  //申请中断,中断处理函数为s3cfb_irq

if (ret != 0) {
printk("Failed to install irq (%d)\n", ret);
goto release_clock;
}


msleep(5);


for (index = 0; index < S3CFB_NUM; index++) {    这一点不是很理解,不明白S3CFB_NUM的含义?查看了2410的代码,发现没有这个循环。我猜测与6410的多层窗口显示有关。我在s3c6410的硬件LCD显示控制器那篇博客中,有过这么一句话:“显示控制器支持5 层图像窗口。”所以我才做这样的猜测。如果你知道了话,请告诉我,谢谢!

#define S3CFB_NUMFB_MIN_NUM(S3CFB_MAX_NUM, CONFIG_FB_S3C_NUM)
#define FB_MIN_NUM(x, y)((x) < (y) ? (x) : (y))  其实就是x,y中的最小值,也就是S3CFB_MAX_NUM和CONFIG_FB_S3C_NUM中的最小值。又有如下定义:
#elif defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410) || defined(CONFIG_CPU_S5PC100)
#define S3CFB_MAX_NUM 5
#define CONFIG_FB_S3C_NUM   4

s3cfb_info[index].mem = info->mem;
s3cfb_info[index].io = info->io;
s3cfb_info[index].clk = info->clk;


s3cfb_init_fbinfo(&s3cfb_info[index], driver_name, index);

填充fb_info中的可变参数结构的成员和不可变参数结构的成员,fb_info结构体中个成员的含义我在第一篇中说过了,你可以去查看。源码如下:

static void s3cfb_init_fbinfo(s3cfb_info_t *finfo, char *drv_name, int index)
{
int i = 0;


if (index == 0)
s3cfb_init_hw();


strcpy(finfo->fb.fix.id, drv_name);


finfo->win_id = index;
finfo->fb.fix.type = FB_TYPE_PACKED_PIXELS;
finfo->fb.fix.type_aux = 0;
finfo->fb.fix.xpanstep = 0;
finfo->fb.fix.ypanstep = 1;
finfo->fb.fix.ywrapstep = 0;
finfo->fb.fix.accel = FB_ACCEL_NONE;


finfo->fb.fbops = &s3cfb_ops;
finfo->fb.flags= FBINFO_FLAG_DEFAULT;


finfo->fb.pseudo_palette = &finfo->pseudo_pal;


finfo->fb.var.nonstd = 0;
finfo->fb.var.activate = FB_ACTIVATE_NOW;
finfo->fb.var.accel_flags = 0;
finfo->fb.var.vmode = FB_VMODE_NONINTERLACED;


finfo->fb.var.xoffset = s3cfb_fimd.xoffset;
finfo->fb.var.yoffset = s3cfb_fimd.yoffset;


if (index == 0) {
finfo->fb.var.height = s3cfb_fimd.height;
finfo->fb.var.width = s3cfb_fimd.width;


finfo->fb.var.xres = s3cfb_fimd.xres;
finfo->fb.var.yres = s3cfb_fimd.yres;


finfo->fb.var.xres_virtual = s3cfb_fimd.xres_virtual;
finfo->fb.var.yres_virtual = s3cfb_fimd.yres_virtual;
} else {
finfo->fb.var.height = s3cfb_fimd.osd_height;
finfo->fb.var.width = s3cfb_fimd.osd_width;


finfo->fb.var.xres = s3cfb_fimd.osd_xres;
finfo->fb.var.yres = s3cfb_fimd.osd_yres;


finfo->fb.var.xres_virtual = s3cfb_fimd.osd_xres_virtual;
finfo->fb.var.yres_virtual = s3cfb_fimd.osd_yres_virtual;
}


finfo->fb.var.bits_per_pixel = s3cfb_fimd.bpp;
        finfo->fb.var.pixclock = s3cfb_fimd.pixclock;
finfo->fb.var.hsync_len = s3cfb_fimd.hsync_len;
finfo->fb.var.left_margin = s3cfb_fimd.left_margin;
finfo->fb.var.right_margin = s3cfb_fimd.right_margin;
finfo->fb.var.vsync_len = s3cfb_fimd.vsync_len;
finfo->fb.var.upper_margin = s3cfb_fimd.upper_margin;
finfo->fb.var.lower_margin = s3cfb_fimd.lower_margin;
finfo->fb.var.sync = s3cfb_fimd.sync;
finfo->fb.var.grayscale = s3cfb_fimd.cmap_grayscale;


finfo->fb.fix.smem_len = finfo->fb.var.xres_virtual * finfo->fb.var.yres_virtual * s3cfb_fimd.bytes_per_pixel;
finfo->fb.fix.line_length = finfo->fb.var.width * s3cfb_fimd.bytes_per_pixel;


#if !defined(CONFIG_FB_S3C_VIRTUAL_SCREEN)


#if defined(CONFIG_FB_S3C_DOUBLE_BUFFERING)
if (index < 2)
finfo->fb.fix.smem_len *= 2;
#else
/*
* Some systems(ex. DirectFB) use FB0 memory as a video memory.
* You can modify the size of multiple.
*/
if (index == 0)
finfo->fb.fix.smem_len *= 5;
#endif


#endif

for (i = 0; i < 256; i++)
finfo->palette_buffer[i] = S3CFB_PALETTE_BUFF_CLEAR;
}



/* Initialize video memory */  帧缓冲设备显示缓冲区的内存分配
ret = s3cfb_map_video_memory(&s3cfb_info[index]);

if (ret) {
printk("Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto release_irq;
}


ret = s3cfb_init_registers(&s3cfb_info[index]);  对LCD各寄存器进行初始化,列出部分源码:

int s3cfb_init_registers(s3cfb_info_t *fbi)
{

               ..........

                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);

              ...........

}
ret = s3cfb_check_var(&s3cfb_info[index].fb.var, &s3cfb_info[index].fb);此函数检查fb的相关参数,如果传递的参数不合法,则进行修改。列出其函数开头的注释:

/*
 * s3cfb_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.
 *
 */


if (index < 2){
if (fb_alloc_cmap(&s3cfb_info[index].fb.cmap, 256, 0) < 0)这个函数和调色板有关,同样列出其注释:

/**
 * fb_alloc_cmap - allocate a colormap
 * @cmap: frame buffer colormap structure
 * @len: length of @cmap
 * @transp: boolean, 1 if there is transparency, 0 otherwise
 *
 * Allocates memory for a colormap @cmap.  @len is the
 * number of entries in the palette.
 *
 * Returns negative errno on error, or zero on success.
 *
 */

goto dealloc_fb;
} else {
if (fb_alloc_cmap(&s3cfb_info[index].fb.cmap, 16, 0) < 0)
goto dealloc_fb;
}


ret = register_framebuffer(&s3cfb_info[index].fb); 注册这个帧缓冲设备(FrameBuffer)fb_info到系统当中,函数源码如下:这个函数的主要作用其实就是把fb_info结构体加入到全局量struct fb_info *registered_fb[FB_MAX]数组中,还有进行一些其他的初始化。

/**
 *register_framebuffer - registers a frame buffer device
 *@fb_info: frame buffer info structure
 *
 *Registers a frame buffer device @fb_info.
 *
 *Returns negative errno on error, or zero for success.
 *
 */


int
register_framebuffer(struct fb_info *fb_info)
{
int i;
struct fb_event event;
s


if (num_registered_fb == FB_MAX)
return -ENXIO;


if (fb_check_foreignness(fb_info))
return -ENOSYS;


num_registered_fb++;
for (i = 0 ; i < FB_MAX; i++)
if (!registered_fb[i])
break;
fb_info->node = i;
mutex_init(&fb_info->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);


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;
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
return 0;
}




if (ret < 0) {
printk(KERN_ERR "Failed to register framebuffer device: %d\n", ret);
goto free_video_memory;
}


printk(KERN_INFO "fb%d: %s frame buffer device\n", s3cfb_info[index].fb.node, s3cfb_info[index].fb.fix.id);
}


/* create device files */下面这些代码是对设备文件系统的支持,创建fb设备文件。
ret = device_create_file(&(pdev->dev), &dev_attr_backlight_power);


if (ret < 0)
printk(KERN_WARNING "s3cfb: failed to add entries\n");


ret = device_create_file(&(pdev->dev), &dev_attr_backlight_level);


if (ret < 0)
printk(KERN_WARNING "s3cfb: failed to add entries\n");


ret = device_create_file(&(pdev->dev), &dev_attr_lcd_power);


if (ret < 0)
printk(KERN_WARNING "s3cfb: failed to add entries\n");


return 0;

下面这些是错误处理函数:

free_video_memory:    
s3cfb_unmap_video_memory(&s3cfb_info[index]);


release_irq:
free_irq(res->start, &info);


release_clock:
clk_disable(info->clk);
clk_put(info->clk);


release_io:
iounmap(info->io);


release_mem:
release_resource(info->mem);
kfree(info->mem);


dealloc_fb:
framebuffer_release(fbinfo);
return ret;
}

probe函数最主要的工作,就是对fb设备进行一定的初始化,申请内存等等,然后注册fb_info到系统中。

2、对应的romeve函数就不说了,就是对应申请的资源的释放,和注销fb_info结构体。



0 0