S5PV210 LCD platform_driver 的实例 s3cfb_driver 定义在 drivers/video/samsung/s3cfb.c中
static struct platform_driver s3cfb_driver = { .probe = s3cfb_probe, .remove = __devexit_p(s3cfb_remove), .driver = { .name = "s5pv210-lcd", //要与平台驱动相同 .owner = THIS_MODULE, }, };并在模块加载时注册,模块卸载时注销,如下:
static int __init s3cfb_register(void) { platform_driver_register(&s3cfb_driver); return 0; } static void __exit s3cfb_unregister(void) { platform_driver_unregister(&s3cfb_driver); }探测函数s3cfb_probe如下:
static int __devinit s3cfb_probe(struct platform_device *pdev) { struct s3c_platform_fb *pdata = NULL;//s3c平台资源结构体(配置信息) struct s3cfb_global *fbdev;//lcd driver全局变量结构体指针,是分析驱动的核心指针 struct resource *res = NULL;//资源指针,用来保存冲LCD平台设备中获取的LCD资源 int i, j, ret = 0; fbdev = kzalloc(sizeof(struct s3cfb_global), GFP_KERNEL);//创建设备描述 if (!fbdev) { dev_err(fbdev->dev, "failed to allocate for " "global fb structure\n"); ret = -ENOMEM; goto err_global; } fbdev->dev = &pdev->dev; //获取平台设备s5p_device_lcd fbdev->regulator = regulator_get(&pdev->dev, "pd"); // if (!fbdev->regulator) { if (IS_ERR(fbdev->regulator)) { dev_err(fbdev->dev, "failed to get regulator\n"); ret = -EINVAL; goto err_regulator; } ret = regulator_enable(fbdev->regulator); if (ret < 0) { dev_err(fbdev->dev, "failed to enable regulator\n"); ret = -EINVAL; goto err_regulator; } #if RITESH fbdev->vcc_lcd = regulator_get(&pdev->dev, "vcc_lcd"); if (!fbdev->vcc_lcd) { dev_err(fbdev->dev, "failed to get vcc_lcd\n"); ret = -EINVAL; goto err_vcc_lcd; } ret = regulator_enable(fbdev->vcc_lcd); if (ret < 0) { dev_err(fbdev->dev, "failed to enable vcc_lcd\n"); ret = -EINVAL; goto err_vcc_lcd; } fbdev->vlcd = regulator_get(&pdev->dev, "vlcd"); if (!fbdev->vlcd) { dev_err(fbdev->dev, "failed to get vlcd\n"); ret = -EINVAL; goto err_vlcd; } ret = regulator_enable(fbdev->vlcd); if (ret < 0) { dev_err(fbdev->dev, "failed to enable vlcd\n"); ret = -EINVAL; goto err_vlcd; } #endif pdata = to_fb_plat(&pdev->dev); //获取平台设备的LCD参数信息 Platform data:EmbedSky_LCD_fb_data if (!pdata) { dev_err(fbdev->dev, "failed to get platform data\n"); ret = -EINVAL; goto err_pdata; } fbdev->lcd = (struct s3cfb_lcd *)pdata->lcd; //得到A70_TN92 //配置GPIO端口 if (pdata->cfg_gpio) pdata->cfg_gpio(pdev); //执行EmbedSky_LCD_fb_data的EmbedSky_LCD_cfg_gpio函数配置相关寄存器 //设置时钟参数 if (pdata->clk_on) pdata->clk_on(pdev, &fbdev->clock); //即arch/arm/mach-s5pv210/setup-fb.c 中s3cfb_clk_on /*获取LCD平台设备的资源s3cfb_resource, 这个IORESOURCE_MEM要与平台设备中定义一致*/ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(fbdev->dev, "failed to get io memory region\n"); ret = -EINVAL; goto err_io; } /*申请IO空间*/ //申请LCD IO端口所占用的IO空间(注意理解IO空间和内存空间的区别),request_mem_region定义在ioport.h中 res = request_mem_region(res->start, res->end - res->start + 1, pdev->name); if (!res) { dev_err(fbdev->dev, "failed to request io memory region\n"); ret = -EINVAL; goto err_io; } /*映射IO空间*/ /*将LCD的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中 * 注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作*/ fbdev->regs = ioremap(res->start, res->end - res->start + 1); if (!fbdev->regs) { dev_err(fbdev->dev, "failed to remap io region\n"); ret = -EINVAL; goto err_mem; } //设置VSYNC中断 s3cfb_set_vsync_interrupt(fbdev, 1); //设置全局中断 s3cfb_set_global_interrupt(fbdev, 1); /*初始化LCD参数,包括LCD大小、时序、极性*/ s3cfb_init_global(fbdev); /*申请framebuffer显示缓存区*/ if (s3cfb_alloc_framebuffer(fbdev)) { ret = -ENOMEM; goto err_alloc; } /*framebuffer设备注册*/ if (s3cfb_register_framebuffer(fbdev)) { ret = -EINVAL; goto err_register; } s3cfb_set_clock(fbdev); s3cfb_set_window(fbdev, pdata->default_win, 1); s3cfb_display_on(fbdev); /*获取设备中断*/ fbdev->irq = platform_get_irq(pdev, 0); if (request_irq(fbdev->irq, s3cfb_irq_frame, IRQF_SHARED, pdev->name, fbdev)) { dev_err(fbdev->dev, "request_irq failed\n"); ret = -EINVAL; goto err_irq; } #ifdef CONFIG_FB_S3C_LCD_INIT //#if defined(CONFIG_FB_S3C_TL2796) if (pdata->backlight_on) pdata->backlight_on(pdev); //#endif if (!bootloaderfb && pdata->reset_lcd) pdata->reset_lcd(pdev); #endif #ifdef CONFIG_HAS_EARLYSUSPEND fbdev->early_suspend.suspend = s3cfb_early_suspend; fbdev->early_suspend.resume = s3cfb_late_resume; fbdev->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; register_early_suspend(&fbdev->early_suspend); #endif /*添加到sysfs文件系统,创建fb设备文件*/ ret = device_create_file(&(pdev->dev), &dev_attr_win_power); if (ret < 0) dev_err(fbdev->dev, "failed to add sysfs entries\n"); dev_info(fbdev->dev, "registered successfully\n"); if (fb_prepare_logo( fbdev->fb[pdata->default_win], FB_ROTATE_UR)) { printk("Start display and show logo\n"); /* Start display and show logo on boot */ fb_set_cmap(&fbdev->fb[pdata->default_win]->cmap, fbdev->fb[pdata->default_win]); fb_show_logo(fbdev->fb[pdata->default_win], FB_ROTATE_UR); } //Jerry Gou ++ mdelay(200); if (pdata->backlight_on) pdata->backlight_on(pdev); //开启背光 对应ek070tn93_fb_data的 ek070tn93_backlight_on //++end return 0; err_irq: s3cfb_display_off(fbdev); s3cfb_set_window(fbdev, pdata->default_win, 0); for (i = pdata->default_win; i < pdata->nr_wins + pdata->default_win; i++) { j = i % pdata->nr_wins; unregister_framebuffer(fbdev->fb[j]); } err_register: for (i = 0; i < pdata->nr_wins; i++) { if (i == pdata->default_win) s3cfb_unmap_default_video_memory(fbdev->fb[i]); framebuffer_release(fbdev->fb[i]); } kfree(fbdev->fb); err_alloc: iounmap(fbdev->regs); err_mem: release_mem_region(res->start, res->end - res->start + 1); err_io: pdata->clk_off(pdev, &fbdev->clock); err_pdata: regulator_disable(fbdev->vlcd); err_vlcd: regulator_disable(fbdev->vcc_lcd); err_vcc_lcd: regulator_disable(fbdev->regulator); err_regulator: kfree(fbdev); err_global: return ret; }