Linux的LCD驱动分析及移植
测试平台宿主机平台:Ubuntu 12.04.4 LTS 目标机:Easy-ARM IMX283 目标机内核:Linux 2.6.35.3 ? LCD驱动分析LCD屏的驱动总体上分成两块,一块是GUI显示输出驱动;一块是触摸驱动(该部分单独一节另外描述)。 LCD驱动概念LCD是Liquid Crystal Display的简称,也就是经常所说的液晶显示器。LCD能够支持彩色图像的显示和视频的播放,是一种非常重要的输出设备。如果我们的系统要用GUI(图形界面接口),比如minigui,MicroWindows。这时LCD设备驱动程序就应该编写成frambuffer接口,而不是编写成仅仅操作底层的LCD控制器接口。 framebuffer是Linux系统为显示设备提供的一个接口,它将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行操作。framebuffer又叫帧缓冲,是Linux为操作显示设备提供的一个用户接口。用户应用程序可以通过framebuffer透明地访问不同类型的显示设备。从这个方面来说,framebuffer是硬件设备显示缓冲区的抽象。Linux抽象出framebuffer这个帧缓冲区可以供用户应用程序直接读写,通过更改framebuffer中的内容,就可以立刻显示在LCD显示屏上。 framebuffer是一个标准的字符设备,主设备号是29,次设备号根据缓冲区的数目而定。framebuffer对应/dev/fb%d设备文件。根据显卡的多少,设备文件可能是/dev/fb0、/dev/fb1等。缓冲区设备也是一种普通的内存设备,可以直接对其进行读写。对用户程序而言,它和/dev下面的其他设备没有什么区别,用户可以把frameBuffer看成一块内存,既可以写,又可以读。显示器将根据内存数据显示对应的图像界面。这一切都由LCD控制器和响应的驱动程序来完成。 ? LCD驱动框架分析总体上是一个平台设备驱动与字符驱动的组合; 先从LCD的设备 dev/fb* 是怎么实现的来进行追溯; 1.开发板启动,进行设备注册; 在? arch/arm/mach-mx28/device.c??设备注册文件中,注册LCD的fd平台设备 “mxs-fb” // mxs-fb平台设备资源定义 2.接下来是?platform_driver? ?mxsfb_driver 的注册,匹配之后触发?mxsfb_probe 函数执行以下操作: 进行相关硬件初始化和?framebuffer 设置;
register_framebuffer()??注册 LCD 屏的 fd 设备;
?
LCD的驱动包含:
drivers/video/mxs/lcd_43wvf1g.c //
drivers/video/mxs/lcdif.c ? //
drivers/video/mxs/mxsfb.c ? //
平台设备驱动?platform_driver 注册??drivers/video/mxs/mxsfb.c? static struct platform_driver mxsfb_driver = { .probe = mxsfb_probe,.remove = mxsfb_remove,.suspend = mxsfb_suspend,.resume = mxsfb_resume,.driver = { .name = "mxs-fb", // 与启动时的平台设备注册的 platform_device 名称相同 .owner = THIS_MODULE,}; static int __init mxsfb_init(void) { return platform_driver_register(&mxsfb_driver); } 驱动安装时与平台设备匹配之后触发?mxsfb_probe 函数,这个时核心。 static int __devinit mxsfb_probe(struct platform_device *pdev) { int ret = 0; struct mxs_fb_data *data; struct resource *res; struct fb_info *info; struct mxs_platform_fb_data *pdata = pdev->dev.platform_data; struct mxs_platform_fb_entry *pentry = NULL; mydbg("n"); if (pdata == NULL) { ret = -ENODEV; goto out; } if (default_panel_name) { mydbg("default_panel_name=%sn",default_panel_name); pentry = (void *)mxs_lcd_iterate_pdata(pdata,get_matching_pentry_by_name,default_panel_name); if (pentry) { mxs_lcd_move_pentry_up(pentry,pdata); pdata->cur = pentry; } } if (!default_panel_name || !pentry) { mydbg("n"); pentry = pdata->cur; } if (!pentry || !pentry->init_panel || !pentry->run_panel || !pentry->release_panel) { mydbg("n"); ret = -EINVAL; goto out; } data = (struct mxs_fb_data *)framebuffer_alloc(sizeof(struct mxs_fb_data) + sizeof(u32) * 256 - sizeof(struct fb_info),&pdev->dev); if (data == NULL) { ret = -ENOMEM; goto out; } cdata = data; data->dev = &pdev->dev; data->pdata = pdata; platform_set_drvdata(pdev,data); info = &data->info; dev_dbg(&pdev->dev,"resolution %dx%d,bpp %dn",pentry->x_res,pentry->y_res,pentry->bpp); mxs_lcd_iterate_pdata(pdata,get_max_memsize,data); data->map_size = PAGE_ALIGN(data->mem_size) * NUM_SCREENS; dev_dbg(&pdev->dev,"memory to allocate: %dn",data->map_size); data->virt_start = dma_alloc_writecombine(&pdev->dev,data->map_size,&data->phys_start,GFP_KERNEL); if (data->virt_start == NULL) { ret = -ENOMEM; goto out_dma; } dev_dbg(&pdev->dev,"allocated at %p:0x%xn",data->virt_start,data->phys_start); mutex_init(&data->blank_mutex); INIT_WORK(&data->work,mxsfb_task); data->state = F_ENABLE; mxsfb_default.bits_per_pixel = pentry->bpp; /* NB: rotated */ mxsfb_default.xres = pentry->y_res; mxsfb_default.yres = pentry->x_res; mxsfb_default.xres_virtual = pentry->y_res; mxsfb_default.yres_virtual = data->map_size / (pentry->y_res * pentry->bpp / 8); if (mxsfb_default.yres_virtual >= mxsfb_default.yres * 2) mxsfb_default.yres_virtual = mxsfb_default.yres * 2; else mxsfb_default.yres_virtual = mxsfb_default.yres; mxsfb_fix.smem_start = data->phys_start; mxsfb_fix.smem_len = pentry->y_res * pentry->x_res * pentry->bpp / 8; mxsfb_fix.ypanstep = 1; switch (pentry->bpp) { case 32: case 24: mxsfb_default.red.offset = 16; mxsfb_default.red.length = 8; mxsfb_default.green.offset = 8; mxsfb_default.green.length = 8; mxsfb_default.blue.offset = 0; mxsfb_default.blue.length = 8; break; case 16: #if 0 mxsfb_default.red.offset = 11; mxsfb_default.red.length = 5; mxsfb_default.green.offset = 5; mxsfb_default.green.length = 6; mxsfb_default.blue.offset = 0; mxsfb_default.blue.length = 5; break; #else mxsfb_default.red.offset = 0 ; mxsfb_default.red.length = 5; mxsfb_default.green.offset = 5; mxsfb_default.green.length = 6; mxsfb_default.blue.offset = 11; mxsfb_default.blue.length = 5; break; #endif default: dev_err(&pdev->dev,"unsupported bitwidth %dn",pentry->bpp); ret = -EINVAL; goto out_dma; } info->screen_base = data->virt_start; info->fbops = &mxsfb_ops; info->var = mxsfb_default; info->fix = mxsfb_fix; info->pseudo_palette = &data->par; data->par = NULL; info->flags = FBINFO_FLAG_DEFAULT; init_waitqueue_head(&data->vsync_wait_q); data->vsync_count = 0; res = platform_get_resource(pdev,IORESOURCE_MEM,0); if (res == NULL) { dev_err(&pdev->dev,"cannot get IRQ resourcen"); ret = -ENODEV; goto out_dma; } data->regbase = (unsigned long)IO_ADDRESS(res->start); res = platform_get_resource(pdev,IORESOURCE_IRQ,"cannot get IRQ resourcen"); ret = -ENODEV; goto out_dma; } data->irq = res->start; mxsfb_check_var(&info->var,info); ret = fb_alloc_cmap(&info->cmap,256,0); if (ret) goto out_cmap; mxsfb_set_par(info); mxs_init_lcdif(); ret = pentry->init_panel(data->dev,data->phys_start,mxsfb_fix.smem_len,pentry); if (ret) { dev_err(&pdev->dev,"cannot initialize LCD paneln"); goto out_panel; } dev_dbg(&pdev->dev,"LCD panel initializedn"); init_timings(data); // not effect dotclk mode ret = request_irq(data->irq,lcd_irq_handler,0,"fb_irq",data); if (ret) { dev_err(&pdev->dev,"request_irq (%d) failed with error %dn",data->irq,ret); goto out_panel; } ret = register_framebuffer(info); // 注册fb_info if (ret) goto out_irq; pentry->run_panel(); /* REVISIT: temporary workaround for MX23EVK */ mxsfb_disable_controller(data); mxsfb_enable_controller(data); data->cur_phys = data->phys_start; dev_dbg(&pdev->dev,"LCD running nown"); #ifdef CONFIG_CPU_FREQ mxsfb_nb.fb_data = data; cpufreq_register_notifier(&mxsfb_nb.nb,CPUFREQ_TRANSITION_NOTIFIER); #endif /* CONFIG_CPU_FREQ */ goto out; out_irq: free_irq(data->irq,data); out_panel: fb_dealloc_cmap(&info->cmap); out_cmap: dma_free_writecombine(&pdev->dev,data->map_size,data->phys_start); out_dma: kfree(data); out: return ret; } ? 待续..... (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |