s3c2410 lcd驱动分析(1)
2011-12-26 20:24
477 查看
s3c2410fb.c是平台通用的lcd驱动
platform_driver_register(&s3c2410fb_driver);用平台资源注册
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,
},
};
-------------------------------------------------------------
static int __devinit s3c24xxfb_probe(struct platform_device *pdev,enum s3c_drv_type drv_type)
mach_info = pdev->dev.platform_data;//获得mach_info结构体s3c2410fb_mach_info结构体的资源在mach-smdk2410中添加,通过
函数注册(void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd))
if (mach_info->default_display >= mach_info->num_displays)//判断设置的display值
display = mach_info->displays + mach_info->default_display;//不明白为什么这里的偏移有什么鬼用
irq = platform_get_irq(pdev, 0);/*获得irq资源*/其实就是调用platform_get_resource,封装成找irq资源
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);/*分配空间*/
platform_set_drvdata(pdev, fbinfo);/*把fbinfo放到pdev下面的私有数据
info = fbinfo->par;//将fb_info的私有数据指向info结构体,在函数中有用
info->dev = &pdev->dev;//就是将设备的device结构体放入s3c2410fb_info结构体中
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*获得寄存器的地址资源*/
size = (res->end - res->start) + 1;/*计算资源大小*/
info->mem = request_mem_region(res->start, size, pdev->name);/*申请i/o资源的空间,i/o资源在使用前要申请,再映射才能访问,
据别人说这样的好处是访问资源时只要加偏移量
info->io = ioremap(res->start, size);/*映射寄存器的地址*/
//计算irq的基地址
info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
lcdcon1 = readl(info->io + S3C2410_LCDCON1);//读取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;这几行设置一些看不懂的参数,一般照抄就行
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);//申请中断
static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)//不明白中断怎么产生,一般都没有用到中断
info->clk = clk_get(NULL, "lcd");
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;
}计算最大的frame buffer 长度
ret = s3c2410fb_map_video_memory(fbinfo);/*设置LCD的buffer,包括虚拟地址,物理地址*/
info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,&map_dma, GFP_KERNEL);//分配一段内存用来做frame
//buffer,在这里有一个cache数据一致性的问题,可以看另外的一篇文章
info->screen_base : 内存的虚拟起始地址,在内核要用此地址来操作所分配的内存
fbi->dev : 可以平台初始化里指定,主要是用到dma_mask之类参数,可参考framebuffer
map_size : 实际分配大小,传入dma_map_size即可
&map_dma: 返回的内存物理地址,dma就可以用。
info->fix.smem_start = map_dma;//设置物理地址
fbinfo->var.xres = display->xres;
fbinfo->var.yres = display->yres;
fbinfo->var.bits_per_pixel = display->bpp;//这里设置可变的参数,宽,高,bpp(每像素多少位)
s3c2410fb_init_registers(fbinfo);//初始化gpio端口
struct s3c2410fb_info *fbi = info->par;//在前面已经将他们的地址复制了
struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;//前面讲info->dev = &pdev->dev中赋值
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);设置gpio管脚
writel(mach_info->lpcsel, lpcsel);//往LPCSEL写值
writel(0x00, tpal);//往TPAL寄存器写值
s3c2410fb_check_var(&fbinfo->var, fbinfo);//用来设置参数,在ioctl中也会调用到这个函数
struct s3c2410fb_display *default_display = mach_info->displays +mach_info->default_display;//默认的display结构体
int type = default_display->type;//lcd的类型
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->
4000
displays[i].xres &&
var->bits_per_pixel == mach_info->displays[i].bpp) {
display = mach_info->displays + i;//使用设定的
break;
}
//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;//lcdcon5寄存器的值
fbi->regs.lcdcon1 = display->type;//设置lcdcon1的值,就是设置lcd的类型
switch (var->bits_per_pixel)//这个switch里面就是更具bits_per_pixel的值设定红,绿,蓝的位长和偏移
ret = s3c2410fb_cpufreq_register(info);//这里没用,没有定义
ret = register_framebuffer(fbinfo);//在fb这个驱动架构中注册设备的函数,就是将fb_info结构体添加到fb_info的数组中,并设定了次设备号
ret = device_create_file(&pdev->dev, &dev_attr_debug);//创建一个设备文件
在probe函数里面就是这些初始化的内容,对于lcd的驱动就是设置fb_info结构体的参数,包括:fb_var_screeninfo,fb_fix_screeninfo,设置寄存器的值,fb_ops几个方面的值。
在fb_ops有三个是固定的,主要要实现两个函数。
platform_driver_register(&s3c2410fb_driver);用平台资源注册
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,
},
};
-------------------------------------------------------------
static int __devinit s3c24xxfb_probe(struct platform_device *pdev,enum s3c_drv_type drv_type)
mach_info = pdev->dev.platform_data;//获得mach_info结构体s3c2410fb_mach_info结构体的资源在mach-smdk2410中添加,通过
函数注册(void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd))
if (mach_info->default_display >= mach_info->num_displays)//判断设置的display值
display = mach_info->displays + mach_info->default_display;//不明白为什么这里的偏移有什么鬼用
irq = platform_get_irq(pdev, 0);/*获得irq资源*/其实就是调用platform_get_resource,封装成找irq资源
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);/*分配空间*/
platform_set_drvdata(pdev, fbinfo);/*把fbinfo放到pdev下面的私有数据
info = fbinfo->par;//将fb_info的私有数据指向info结构体,在函数中有用
info->dev = &pdev->dev;//就是将设备的device结构体放入s3c2410fb_info结构体中
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*获得寄存器的地址资源*/
size = (res->end - res->start) + 1;/*计算资源大小*/
info->mem = request_mem_region(res->start, size, pdev->name);/*申请i/o资源的空间,i/o资源在使用前要申请,再映射才能访问,
据别人说这样的好处是访问资源时只要加偏移量
info->io = ioremap(res->start, size);/*映射寄存器的地址*/
//计算irq的基地址
info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
lcdcon1 = readl(info->io + S3C2410_LCDCON1);//读取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;这几行设置一些看不懂的参数,一般照抄就行
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);//申请中断
static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)//不明白中断怎么产生,一般都没有用到中断
info->clk = clk_get(NULL, "lcd");
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;
}计算最大的frame buffer 长度
ret = s3c2410fb_map_video_memory(fbinfo);/*设置LCD的buffer,包括虚拟地址,物理地址*/
info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,&map_dma, GFP_KERNEL);//分配一段内存用来做frame
//buffer,在这里有一个cache数据一致性的问题,可以看另外的一篇文章
info->screen_base : 内存的虚拟起始地址,在内核要用此地址来操作所分配的内存
fbi->dev : 可以平台初始化里指定,主要是用到dma_mask之类参数,可参考framebuffer
map_size : 实际分配大小,传入dma_map_size即可
&map_dma: 返回的内存物理地址,dma就可以用。
info->fix.smem_start = map_dma;//设置物理地址
fbinfo->var.xres = display->xres;
fbinfo->var.yres = display->yres;
fbinfo->var.bits_per_pixel = display->bpp;//这里设置可变的参数,宽,高,bpp(每像素多少位)
s3c2410fb_init_registers(fbinfo);//初始化gpio端口
struct s3c2410fb_info *fbi = info->par;//在前面已经将他们的地址复制了
struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;//前面讲info->dev = &pdev->dev中赋值
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);设置gpio管脚
writel(mach_info->lpcsel, lpcsel);//往LPCSEL写值
writel(0x00, tpal);//往TPAL寄存器写值
s3c2410fb_check_var(&fbinfo->var, fbinfo);//用来设置参数,在ioctl中也会调用到这个函数
struct s3c2410fb_display *default_display = mach_info->displays +mach_info->default_display;//默认的display结构体
int type = default_display->type;//lcd的类型
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->
4000
displays[i].xres &&
var->bits_per_pixel == mach_info->displays[i].bpp) {
display = mach_info->displays + i;//使用设定的
break;
}
//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;//lcdcon5寄存器的值
fbi->regs.lcdcon1 = display->type;//设置lcdcon1的值,就是设置lcd的类型
switch (var->bits_per_pixel)//这个switch里面就是更具bits_per_pixel的值设定红,绿,蓝的位长和偏移
ret = s3c2410fb_cpufreq_register(info);//这里没用,没有定义
ret = register_framebuffer(fbinfo);//在fb这个驱动架构中注册设备的函数,就是将fb_info结构体添加到fb_info的数组中,并设定了次设备号
ret = device_create_file(&pdev->dev, &dev_attr_debug);//创建一个设备文件
在probe函数里面就是这些初始化的内容,对于lcd的驱动就是设置fb_info结构体的参数,包括:fb_var_screeninfo,fb_fix_screeninfo,设置寄存器的值,fb_ops几个方面的值。
在fb_ops有三个是固定的,主要要实现两个函数。
相关文章推荐
- s3c2410_lcd & frame buffer 驱动分析 int __init s3c2410fb_probe(struct device *dev) { struct s3c2410f
- s3c2410_lcd & frame buffer 驱动分析
- 基于s3c2410的YL-LCD35液晶驱动的分析
- S3C2410驱动分析之LCD驱动
- s3c2410_lcd & frame buffer 驱动分析 (转)
- s3c2410 LCD驱动的结构分析
- s3c2410_lcd & frame buffer 驱动分析
- S3C2410驱动分析的LCD驱动
- Linux-2.6.20的LCD驱动分析(一)[转]
- 高通 android平台LCD驱动分析
- Linux设备驱动工程师之路——触摸屏驱动s3c2410_ts.c分析
- S3C2440裸机学习 - LCD驱动原理及代码分析[一]
- mini2440驱动分析之LCD
- s3c2410 framebuffer驱动中的mmap分析备忘
- s3c2410触摸屏驱动(2.6内核)分析 -中断下半部
- LCD驱动分析(三): framebuffer驱动的注册
- S3C2440裸机学习[2] - LCD驱动原理及代码分析[二]
- Linux-2.6.20的LCD驱动分析(二)[转]
- mini2440驱动分析之LCD
- lcd驱动分析(读书笔记)