s3c6410_LCD & frame buffer 驱动分析
代码很多啊,好多东西都是不明白,这里做个笔记,特别是对LCD寄存器初始化,真的是看得。。。。
看来得找个时间玩玩裸奔啊。。。。。。。。。。。
struct fb_ops s3cfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = s3cfb_check_var,
.fb_set_par = s3cfb_set_par,
.fb_blank = s3cfb_blank,
.fb_pan_display = s3cfb_pan_display,
.fb_setcolreg = s3cfb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_cursor = soft_cursor,
.fb_ioctl = s3cfb_ioctl,
};
static struct platform_driver s3cfb_driver = {
.probe = s3cfb_probe,{
/**模块加载函数实现平台驱动的注册**/
EXPORT_SYMBOL(lcdsize);
/**LCD尺寸选择
MODULE_LICENSE("GPL");
/******************s3cfb_probe主要实现LCD控制寄存器的初始化和平台驱动资源的获取,时钟获取,初始化各种结构体等工作,注册framebuffer等
static int __init s3cfb_probe(struct platform_device *pdev)
{
struct resource *res; /*用来保存从LCD平台设备中获取的LCD资源*/
struct fb_info *fbinfo;/*FrameBuffer驱动所对应的fb_info结构体*/
s3cfb_info_t *info;/**该结构其实就是对驱动中所用到的各种变量以及结构的一种封装*/
char driver_name[] = "s3cfb";/*FrameBuffer设备名称*/
int index = 0, ret, size;
/**分配fb_info结构体**/
fbinfo = framebuffer_alloc(sizeof(s3cfb_info_t), &pdev->dev);
if (!fbinfo)
return -ENOMEM;
/*将fbinfo传递给内核中总线设备结构所对应的驱动数据信息*/
platform_set_drvdata(pdev, fbinfo);
/**fb_info中似有成员赋值给s3cfb_info_t**/
info = fbinfo->par;
info->dev = &pdev->dev;
/**获取LCD平台设备所使用的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;
}
size = (res->end - res->start) + 1;
/*为上面LCDIO端口申请IO内存*/
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;
}
/**将LCD的此段IO地址空间映射成虚拟地址空间,这样才能操作**/
info->io = ioremap(res->start, size);
if (info->io == NULL) {
dev_err(&pdev->dev, "ioremap() of registers failed\n");
ret = -ENXIO;
goto release_mem;
}
/*初始化LCD视频中断控制寄存器0**/
s3cfb_pre_init();
/**TFT背光设置**/
s3cfb_set_backlight_power(1);
/**电源设置**/
s3cfb_set_lcd_power(1);
s3cfb_set_backlight_level(S3CFB_DEFAULT_BACKLIGHT_LEVEL);
/**获取LCD时钟**/
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)));
/**0帧数据**/
s3cfb_fimd.vsync_info.count = 0;
/**初始化帧同步信号的等待队列头**/
init_waitqueue_head(&s3cfb_fimd.vsync_info.wait_queue);
/**获取LCD平台设备所使用的中断号*/
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;
}
/**申请LCD中断服务**/
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);
/**6410支持多种屏幕**/
for (index = 0; index < S3CFB_NUM; index++) {
/**这个s3cfb_info就是一个s3cfb_info_t类型的数组,每一款LCD对应着一个索引*/
/**上面获取了IO内存,映射了IO,获取到了时钟等,这里用他们来填充这个s3cfb_info结构**/
s3cfb_info[index].mem = info->mem;
s3cfb_info[index].io = info->io;
s3cfb_info[index].clk = info->clk;
/**填充各种结构体中的参数,比如填充fb_info中的可变参数结构的成员和不可变参数结构的成员等**/
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;
}
/*初始化完fb_info后,开始对LCD各寄存器进行初始化,这个寄存器的配置还真没怎么搞懂啊。感觉好难。。*/
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;
}
/**注册帧缓冲设备(FrameBuffer)fb_info到系统当中*/
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");
printk("jkq debug VIDCON0 is %x\n",readl(S3C_VIDCON0));
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;
}
/**********接下来我想对prob函数中所调用的部分函数分析分析,这里面所调用的函数有些还真没弄清楚。。******/
/*初始化LCD视频中断控制寄存器0**/
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;
/**详情请看S3C6410数据手册第14章482页。。初始化视频中断以VSYNS开始并使能该寄存器*/
writel(s3cfb_fimd.vidintcon0, S3C_VIDINTCON0);
}
/******下面这三个是控制LCD电源以及背光控制的**/
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);
}
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);
}
static void s3cfb_set_backlight_level(int to)
{
s3cfb_fimd.backlight_level = to;
if (s3cfb_fimd.set_brightness)
(s3cfb_fimd.set_brightness)(to);
}
/*********************************************************************************************/
/***该函数在probe中被调用就是用来填充各种结构中的成员变量的它们的具体实现在s3cfb_fimd4x.c中**/
static void s3cfb_init_fbinfo(s3cfb_info_t *finfo, char *drv_name, int index)
{
int i = 0;
if (index == 0)
{
if(lcdsize == 0)
s3cfb_init_hw_35();
else if(lcdsize == 1)
s3cfb_init_hw_43();
else if(lcdsize == 2)
s3cfb_init_hw_56();
else if(lcdsize == 3)
s3cfb_init_hw_70();
else if(lcdsize == 4)
s3cfb_init_hw_vga800();
else//这个是我们的LCD尺寸
s3cfb_init_hw_43();//初始化4.3寸液晶显示器
}
/**如果index != 0则将传进来的驱动的名字kaob到不可变参数数组中的ID数组中*/
strcpy(finfo->fb.fix.id, drv_name);//初始化fb_fix_screeninfo->id
finfo->win_id = index;//将窗口索引传入,一共是5个窗口.
finfo->fb.fix.type = FB_TYPE_PACKED_PIXELS;//填充fb_fix_screeninfo->type,使用默认类型,其余还有VGA
finfo->fb.fix.type_aux = 0;//填充fb_fix_screeninfo->type_aux
finfo->fb.fix.xpanstep = 0;//填充fb_fix_screeninfo->xpanstep
finfo->fb.fix.ypanstep = 1;//填充fb_fix_screeninfo->ypanstep
finfo->fb.fix.ywrapstep = 0;//填充fb_fix_screeninfo->ywrapstep
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;/**16位颜色表**/
/**给可变参数赋值**/
finfo->fb.var.nonstd = 0;//像素格式,0代表标准像素格式,!0代表非标准像素格式
finfo->fb.var.activate = FB_ACTIVATE_NOW;//该宏在fb.h中有定义。
finfo->fb.var.accel_flags = 0;/* (OBSOLETE) see fb_info.flags ,源码注释是这样的也就是根据fb.flags
的取值觉得这个了*/
finfo->fb.var.vmode = FB_VMODE_NONINTERLACED;
finfo->fb.var.xoffset = s3cfb_fimd.xoffset; //0
finfo->fb.var.yoffset = s3cfb_fimd.yoffset; //0
/* 下面就是设置LCD分辨率了,具体的取值情况还要结合数据手册,不过这些默认值在s3cfb_fimd4x.c文件中已经有了,当然了这个文件从官方下载下来的源代码是没有的,这三星提供的吧*/
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;//与之对应的虚拟分辨率-垂直
/********这里用文字表达一下:fb_var_screeninfo中的成员变量xres和yres是指在显示屏上真是显示的频率。而xres_virtual和yres_virtual是虚拟分辨率,它们是指显存的分辨率。举个例子假如LCD垂直分辨率是400,而虚拟分辨率是800,这就是说在显存中是存储着800行要显示的,但是实际上显示器每次只能显示400行,至于具体需要显示那些行还需要fb_var_screeninfo中的另一个变量yoffset,若yoffset=0则从0行开始显示400行,n=20则从显存21行开始显示400行。**********/
} 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;/**像素时钟、单位(ps)**/
finfo->fb.var.hsync_len = s3cfb_fimd.hsync_len;/**行同步信号宽度,用VCLK计算对应时序图中HSPW*/
finfo->fb.var.left_margin = s3cfb_fimd.left_margin;/*行切换:同步到绘图之间的延迟对应HBPD**/
finfo->fb.var.right_margin = s3cfb_fimd.right_margin;/**行切换:绘图到同步之间的延迟对应HFPD*/
finfo->fb.var.vsync_len = s3cfb_fimd.vsync_len;/**帧同步信号长度对应VSPW*/
finfo->fb.var.upper_margin = s3cfb_fimd.upper_margin;/**帧切换:同步到绘图之间的延迟对应VBPD*/
finfo->fb.var.lower_margin = s3cfb_fimd.lower_margin;/**帧切换:绘图到同步之间的延迟对应VFPD*/
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; /*fb缓冲内存的起始地址(物理地址)**/
finfo->fb.fix.line_length = finfo->fb.var.width * s3cfb_fimd.bytes_per_pixel;/*一行的字节数*/
#if !defined(CONFIG_FB_S3C_EXT_VIRTUAL_SCREEN)
#if defined(CONFIG_FB_S3C_EXT_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.
*
* !WARN: smem_len*5 may break the probe for 1024*768
*/
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;
}
/*********上面这些的具体实现偶看不下去。。。。没有认真去研究在s3cfb_fimd4x.c中都实现了*****
在probe函数中调用了该函数为显存分配内存
static int __init s3cfb_map_video_memory(s3cfb_info_t *fbi)
{
DPRINTK("map_video_memory(fbi=%p)\n", fbi);
/**获取framebuffer缓存的大小**/
fbi->map_size_f1 = PAGE_ALIGN(fbi->fb.fix.smem_len);
/**因为系统采用DMA来搬移显示数据,所以使用dma_alloc_writecombine来分配一段writecombine区域**/
fbi->map_cpu_f1 = dma_alloc_writecombine(fbi->dev, fbi->map_size_f1, &fbi->map_dma_f1, GFP_KERNEL);/*分配内存,分配可供DMA使用的地址,fbi->map_cpu_f1为虚拟地址,fbi->map_dma_f1为物理地址*/
fbi->map_size_f1 = fbi->fb.fix.smem_len; //恢复真实的大小
if (fbi->map_cpu_f1) {
/* prevent initial garbage on screen */
printk("Window[%d] - FB1: map_video_memory: clear %p:%08x\n",
fbi->win_id, fbi->map_cpu_f1, fbi->map_size_f1);
/**使用之前先初始化,以防止缓存中的一些无效数据信息显示到LCD上**/
memset(fbi->map_cpu_f1, 0xf0, fbi->map_size_f1);
/**********DMA搬迁数据*****************/
fbi->screen_dma_f1 = fbi->map_dma_f1;//物理地址
fbi->fb.screen_base = fbi->map_cpu_f1;//虚拟地址
fbi->fb.fix.smem_start = fbi->screen_dma_f1;//物理地址
printk(" FB1: map_video_memory: dma=%08x cpu=%p size=%08x\n",
fbi->map_dma_f1, fbi->map_cpu_f1, fbi->fb.fix.smem_len);
}
if (!fbi->map_cpu_f1)
return -ENOMEM;
#if defined(CONFIG_FB_S3C_EXT_DOUBLE_BUFFERING)
if (fbi->win_id < 2 && fbi->map_cpu_f1) {
fbi->map_size_f2 = (fbi->fb.fix.smem_len / 2);
fbi->map_cpu_f2 = fbi->map_cpu_f1 + fbi->map_size_f2;
fbi->map_dma_f2 = fbi->map_dma_f1 + fbi->map_size_f2;
/* prevent initial garbage on screen */
printk("Window[%d] - FB2: map_video_memory: clear %p:%08x\n",
fbi->win_id, fbi->map_cpu_f2, fbi->map_size_f2);
fbi->screen_dma_f2 = fbi->map_dma_f2;
printk(" FB2: map_video_memory: dma=%08x cpu=%p size=%08x\n",
fbi->map_dma_f2, fbi->map_cpu_f2, fbi->map_size_f2);
}
#endif
if (s3cfb_fimd.map_video_memory)
(s3cfb_fimd.map_video_memory)(fbi);
return 0;
}
/*
* 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.
* 检查fb_info中的可变参数,具体的分析还需要S3C6410数据手册,没啥可说的很明了
*/
static int s3cfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
s3cfb_info_t *fbi = (s3cfb_info_t *) info;
DPRINTK("check_var(var=%p, info=%p)\n", var, info);
switch (var->bits_per_pixel) {
case 8://每像素8BPP具体继续往下看
var->red = s3cfb_rgb_8.red;
var->green = s3cfb_rgb_8.green;
var->blue = s3cfb_rgb_8.blue;
var->transp = s3cfb_rgb_8.transp;
s3cfb_fimd.bytes_per_pixel = 1;
break;
case 16: //16BPP
var->red = s3cfb_rgb_16.red;
var->green = s3cfb_rgb_16.green;
var->blue = s3cfb_rgb_16.blue;
var->transp = s3cfb_rgb_16.transp;
s3cfb_fimd.bytes_per_pixel = 2;
break;
case 24://24BPP
var->red = s3cfb_rgb_24.red;
var->green = s3cfb_rgb_24.green;
var->blue = s3cfb_rgb_24.blue;
var->transp = s3cfb_rgb_24.transp;
s3cfb_fimd.bytes_per_pixel = 4;
break;
case 28://28BPP
var->red = s3cfb_rgb_28.red;
var->green = s3cfb_rgb_28.green;
var->blue = s3cfb_rgb_28.blue;
var->transp = s3cfb_rgb_28.transp;
s3cfb_fimd.bytes_per_pixel = 4;
break;
case 32://32bPP
var->red = s3cfb_rgb_32.red;
var->green = s3cfb_rgb_32.green;
var->blue = s3cfb_rgb_32.blue;
var->transp = s3cfb_rgb_32.transp;
s3cfb_fimd.bytes_per_pixel = 4;
break;
}
/* WIN0 cannot support alpha channel. */
if( (fbi->win_id == 0) && (var->bits_per_pixel == 28) ){
var->transp.length = 0;
}
return 0;
}
/*****为了说明上面这个检查可变参数函数下面这个结构定义在s3cfb.h中三星提供的Z官方源码中木有。。。。
typedef struct {
struct fb_bitfield red;//红
struct fb_bitfield green; //绿
struct fb_bitfield blue;//蓝
struct fb_bitfield transp;//透明度
} s3cfb_rgb_t;
//8BPP显示模式,颜色表长度为8位
const static s3cfb_rgb_t s3cfb_rgb_8 = {
.red = {.offset = 0, .length = 8,},
.green = {.offset = 0, .length = 8,},
.blue = {.offset = 0, .length = 8,},
.transp = {.offset = 0, .length = 0,},
};
//16BPP(RGB565显示模式)
const static s3cfb_rgb_t s3cfb_rgb_16 = {
.red = {.offset = 11, .length = 5,},
.green = {.offset = 5, .length = 6,},
.blue = {.offset = 0, .length = 5,},
.transp = {.offset = 0, .length = 0,},
};
//24BPP(A:8:8:8)显示模式
const static s3cfb_rgb_t s3cfb_rgb_24 = {
.red = {.offset = 16, .length = 8,},
.green = {.offset = 8, .length = 8,},
.blue = {.offset = 0, .length = 8,},
.transp = {.offset = 0, .length = 0,},
};
//28Bpp(A:8:8:8)显示模式
const static s3cfb_rgb_t s3cfb_rgb_28 = {
.red = {.offset = 16, .length = 8,},
.green = {.offset = 8, .length = 8,},
.blue = {.offset = 0, .length = 8,},
.transp = {.offset = 24, .length = 4,},
};
//32Bpp(A:8:8:8)显示模式
const static s3cfb_rgb_t s3cfb_rgb_32 = {
.red = {.offset = 16, .length = 8,},
.green = {.offset = 8, .length = 8,},
.blue = {.offset = 0, .length = 8,},
.transp = {.offset = 24, .length = 8,},
};
/**上面这些我是这样理解的不知道有没有错啊,比如说对应//32Bpp(A:8:8:8)显示模式,颜色表长度为32,也就是32bit,每一个像素占一个字节8bit,对应的RGB888,对应的blue长度为8,偏移为0,对应的green长度为8偏移8,对应的red长度为8,偏移刚好是16,剩下的transp偏移为24,长度为8,这不刚好32位长度么?*/
/***对于 s3cfb_init_registers(配置LCD控制器中的各寄存器)还没研究,稍微看了一下。好难。。。。。。。。。