上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构。
上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/18188259
上一节讲到probe函数就没继续往下深究了,这一节里,我们来详细分析s3c24xxfb_probe函数,整体分析如下:
static int __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; /* 包含s3c2410fb_display */
struct resource *res;
int ret;
int irq;
int i;
int size;
u32 lcdcon1;
/* s3c24xx_fb_set_platdata()里会设置platform_data
* tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);
* 所以这里传入来的platform_data就是tq2440_fb_info结构体实例
*/
mach_info = pdev->dev.platform_data;
/* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL */
if (mach_info == NULL) {
dev_err(&pdev->dev,
"no platform data for lcd, cannot attach\n");
return -EINVAL; /* 表示无效的参数 */
}
/* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */
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指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */
display = mach_info->displays + mach_info->default_display;
/* 通过平台设备platform_device获得IRQ
* platform_get_irq其实是调用platform_get_resource(dev, IORESOURCE_IRQ, num)
*/
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq for device\n");
return -ENOENT;
}
/* 分配一个fb_info结构体,第一参数不为0表示,额外多申请的空间
* 用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据
* 比如:clk,resource,io,irq_base,drv_type等额外信息
*/
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
if (!fbinfo)
return -ENOMEM; /* 返回NULL表示失败 */
/* 相当于pdev->dev->driver_data = fbinfo */
platform_set_drvdata(pdev, fbinfo);
/* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */
info = fbinfo->par; /* 将私有数据赋给info指针变量 */
info->dev = &pdev->dev; /* 指定struct s3c2410fb_info中dev为平台设备中的dev */
info->drv_type = drv_type; /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */
/* 通过平台设备platform_device获得资源(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; /* 资源的大小 */
/* 申请以res->start地址开始大小为size的I/O内存 */
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;
}
/* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */
info->io = ioremap(res->start, size);
if (info->io == NULL) {
dev_err(&pdev->dev, &#