一些重要的结构体: struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; struct resource *parent, *sibling, *child; };
struct s3c2410fb_hw { unsigned longlcdcon1; unsigned longlcdcon2; unsigned longlcdcon3; unsigned longlcdcon4; unsigned longlcdcon5; };
struct s3c2410fb_display { /* LCD type */ unsigned type;
/* Screen size */ unsigned short width; unsigned short height;
/* Screen info */ unsigned short xres; unsigned short yres; unsigned short bpp;
unsigned pixclock;/* pixclock in picoseconds */ unsigned short left_margin; /* value in pixels (TFT) or HCLKs (STN) */ unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */ unsigned short hsync_len; /* value in pixels (TFT) or HCLKs (STN) */ unsigned short upper_margin;/* value in lines (TFT) or 0 (STN) */ unsigned short lower_margin;/* value in lines (TFT) or 0 (STN) */ unsigned short vsync_len;/* value in lines (TFT) or 0 (STN) */
/* lcd configuration registers */ unsigned longlcdcon5; };
struct s3c2410fb_mach_info { struct s3c2410fb_display *displays;/* attached diplays info */ unsigned num_displays;/* number of defined displays */ unsigned default_display;
/* GPIOs */ unsigned longgpcup; unsigned longgpcup_mask; unsigned longgpccon; unsigned longgpccon_mask; unsigned longgpdup; unsigned longgpdup_mask; unsigned longgpdcon; unsigned longgpdcon_mask;
/* lpc3600 control register */ unsigned longlpcsel; };
#define LCD_WIDTH 320 #define LCD_HEIGHT 240 #define LCD_PIXCLOCK 170000
#define LCD_RIGHT_MARGIN 0x44 #define LCD_LEFT_MARGIN 0x04 #define LCD_HSYNC_LEN 0x01
#define LCD_UPPER_MARGIN 10 #define LCD_LOWER_MARGIN 4 #define LCD_VSYNC_LEN 1 #define LCD_CON5 (S3C2410_LCDCON5_FRM565 | S3C2410_LCDCON5_INVVFRAME | \ S3C2410_LCDCON5_INVVLINE | S3C2410_LCDCON5_HWSWP )
static struct s3c2410fb_display mini2440_lcd_cfg = { #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 = { .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, };
/* 定义lcd 平台设备 */ 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 } };
/* mini2440 所有platform设备数组*/ 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_dm9k, &net_device_cs8900, &s3c24xx_uda134x, };
/* 内核启动的时候会调用该函数 */ static void __init mini2440_machine_init(void) { #if defined (LCD_WIDTH) s3c24xx_fb_set_platdata(&mini2440_fb_info); #endif s3c_i2c0_set_platdata(NULL);
/* 以下两个函数会完成platform的注册等初始化 */ platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices)); friendly_arm_machine_init(); }
void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd) { struct s3c2410fb_mach_info *npd;
/* 分配一个3c2410fb_mach_info结构体*/ npd = kmalloc(sizeof(*npd), GFP_KERNEL); if (npd) { memcpy(npd, pd, sizeof(*npd)); /* s3c_device_lcd.dev.platform_data所指内容就为mini2440_fb_info * 结构体内容的一个拷贝 */ s3c_device_lcd.dev.platform_data = npd; } else { printk(KERN_ERR "no memory for LCD platform data\n"); } }
/*s3c2410fb_init会注册该驱动,当driver和device匹配成功,调用s3c2412fb_probe*/ static struct 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; }
static struct fb_ops s3c2410fb_ops = { .owner= THIS_MODULE, .fb_check_var= s3c2410fb_check_var, .fb_set_par= s3c2410fb_set_par, .fb_blank= s3c2410fb_blank, .fb_setcolreg= s3c2410fb_setcolreg, .fb_fillrect= cfb_fillrect, .fb_copyarea= cfb_copyarea, .fb_imageblit= cfb_imageblit, };
/* 该函数很长,只列出关键部分 */ 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; struct resource *res; int irq; u32 lcdcon1;
/* 现在mach_info指向有lcd引脚设置的结构体 */ mach_info = pdev->dev.platform_data;
/* dispaly指向mini2440_lcd_cfg,该结构体有LCD一些设置信息 *如:长度,宽度等。 */ display = mach_info->displays + mach_info->default_display;
/* 获得中断号 */ irq = platform_get_irq(pdev, 0);
/* 该函数分配 sizeof(struct fb_info) + sizeof(struct s3c2410fb_info)大小的 *的内存,fb_info指向sizeof(struct fb_info)分配的内存,fb_info->par指向 *sizeof(struct s3c2410fb_info)分配的内存。fbinfo->device = &pdev->dev */ fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
/*该函数执行一下赋值pdev->dev->private_data = fb_info;*/ platform_set_drvdata(pdev, fbinfo);
info = fbinfo->par; /*struct s3c2410fb_info *info;*/ info->dev = &pdev->dev; info->drv_type = drv_type;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/ * 需映射内存大小 */ size = (res->end - res->start) + 1;
info->mem = request_mem_region(res->start, size, pdev->name);
/* 物理地址到虚拟地址的映射 */ info->io = ioremap(res->start, size);
info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE); /* Stop the video */ lcdcon1 = readl(info->io + S3C2410_LCDCON1); writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
/* 设置名称 static char driver_name[] = "s3c2410fb"; */ strcpy(fbinfo->fix.id, driver_name);
fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
fbinfo->var.nonstd = 0;
fbinfo->fbops = &s3c2410fb_ops; fbinfo->flags = FBINFO_FLAG_DEFAULT; fbinfo->pseudo_palette = &info->pseudo_pal;
/* frame_buffer内存到物理地址的映射,该函数会调用 *info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,&map_dma, GFP_KERNEL); *该函数将分配一块可以dma的内存区域,并且返回两个值,info->screen_base为虚拟地址 *map_dma的值为虚拟地址对应的物理地址,该值将被填至s3c2440的LCD控制器中。 *该函数还会执行一些赋值:info->fix.smem_start = map_dma; */ ret = s3c2410fb_map_video_memory(fbinfo);
/* 用 mini2440_fb_info结构体的成员值初始化LCD的引脚*/ s3c2410fb_init_registers(fbinfo);
/*做参数检查,并赋值*/ s3c2410fb_check_var(&fbinfo->var, fbinfo);
/* 注册fbinfo */ ret = register_framebuffer(fbinfo);
/* 创建属性文件 */ ret = device_create_file(&pdev->dev, &dev_attr_debug);
}
LCD控制器的初始化主要在s3c2410fb_activate_var(info);函数中,该函数被s3c2410fb_set_par函数调用,为fb_ops中的一个成员。 但是s3c24xxfb_probe函数并没有调用该函数,那是什么时候初始化的LCD控制器呢? 通过在s3c2410fb_set_pa设置dump_stack()函数,一下为返回的函数调用信息: do_exit--->kernel_init--->__exception_text_end-->platform_driver_register--->driver_register--->bus_add_driver--->driver_attach--->bus_for_each_dev--->_driver_attach--->driver_probe_device--->platform_drv_probe--->s3c2410fb_probe--->s3c24xxfb_probe --->register_framebuffer--->fb_notifier_call_chain--->blocking_notifier_call_chain--->_blocking_notifier_call_chain--->notifier_call_chain--->fbcon_event_notify--->fbcon_takeover--->take_over_console--->visual_init--->fbcon_init--->s3c2410fb_set_par 我们看到在注册register_framebuffer时它会一路调用直到调用s3c2410fb_set_par函数执行LCD控制器的初始化。
|