一、LCD驱动探测(probe)和(remove)函数
分析一个总线驱动一般都从探测函数开始,现在看下探测函数,在LCD驱动第一部分末尾讲述了LCD驱动定义的结构体,本文在多处将使用这些定义的结构体。点击(此处)折叠或打开
- static int __devinit xxxxfb_probe(struct platform_device *pdev)
- {
- int ret = 0;
- struct clk *lcd_clk;
- struct fb_info *fb;
- struct resource *mem;
- unsigned long rate = 0;
- struct xxxxfb *xxxxfb;
- struct xxxx_fb_platform_data *pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "Missing platform data\n");
- return -ENXIO;
- }
- printk(KERN_INFO "######xxxx_lcdfb_probe start######\n");
-
- #ifdef CONFIG_FB_xxxx_SVGA
- *(volatile unsigned int *)addr = 0x4; //addr为系统寄存器中设置LCD分频寄存器
- #else
- *(volatile unsigned int *)addr = 0x5;
- #endif
- lcd_clk = clk_get(NULL, "lcd");
- if (IS_ERR(lcd_clk)) {
- DBG("failed to find watchdog clock source\n");
- ret = PTR_ERR(lcd_clk);
- return -ENXIO;
- }
- rate = clk_get_rate(lcd_clk);
- DBG("lcd clk rate is %ld\n", rate);
- clk_enable(lcd_clk);
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "Failed to get register memory resource\n");
- ret = -ENXIO;
- goto err_put_lpclk;
- }
- mem = request_mem_region(mem->start, resource_size(mem), pdev->name);
- if (!mem) {
- dev_err(&pdev->dev, "Failed to request register memory region\n");
- ret = -EBUSY;
- goto err_put_lpclk;
- }
- fb = framebuffer_alloc(sizeof(struct xxxxfb), &pdev->dev);
- if (!fb) {
- dev_err(&pdev->dev, "Failed to allocate framebuffer device\n");
- ret = -ENOMEM;
- goto err_release_mem_region;
- }
- fb->fbops = &xxxx_ops; //fb支持的操作函数集,在(二)中讲述
- fb->flags = FBINFO_DEFAULT;
- gsc3280fb = fb->par;
- gsc3280fb->pdev = pdev;
- gsc3280fb->pdata = pdata;
- gsc3280fb->mem = mem;
- gsc3280fb->ldclk = lcd_clk;
- gsc3280fb->base = ioremap(mem->start, resource_size(mem)); //寄存器映射
- DBG("xxxxfb->base = %p\n", xxxxfb->base);
- if (!xxxxfb->base) {
- dev_err(&pdev->dev, "Failed to ioremap register memory region\n");
- ret = -EBUSY;
- goto err_framebuffer_alloc;
- }
- //调色板io地址映射
- xxxxfb->pseudo_palette = ioremap(XXXX_LCDC_CMAP_REG, XXXX_LCDC_CMAP_LEN);
- if (!xxxxfb->pseudo_palette ) {
- dev_err(&pdev->dev, "Failed to ioremap cmap register memory region\n");
- ret = -EBUSY;
- if (xxxxfb->base ) {
- iounmap(gsc3280fb->base);
- }
- goto err_framebuffer_alloc;
- }
- platform_set_drvdata(pdev, xxxxfb);
- mutex_init(&xxxxfb->lock);
- fb_videomode_to_modelist(pdata->modes, pdata->num_modes, &fb->modelist);
- fb_videomode_to_var(&fb->var, pdata->modes); //从pdata->modes赋值到fb->var,完全的赋值函数
- fb->var.bits_per_pixel = pdata->bpp; //赋值bpp
- xxxxfb_check_var(&fb->var, fb); //校验参数,在(二)中讲述
- ret = xxxxfb_alloc_devmem(gsc3280fb); //申请DMA内存,接下来讲述
- if (ret) {
- dev_err(&pdev->dev, "Failed to allocate video memory\n");
- goto err_iounmap;
- }
- fb->fix = xxxxfb_fix; //fix结构体,内容见后面
- fb->fix.line_length = fb->var.bits_per_pixel * fb->var.xres / 8;
- fb->fix.mmio_start = mem->start;
- fb->fix.mmio_len = resource_size(mem);
- fb->fix.smem_start = gsc3280fb->vidmem_phys;
- fb->fix.smem_len = fb->fix.line_length * fb->var.yres;
- fb->screen_base = gsc3280fb->vidmem;
- fb->pseudo_palette = gsc3280fb->pseudo_palette;
- fb_alloc_cmap(&fb->cmap, 256, 0);
- gsc3280fb->is_enabled = 1;
- writel(gsc3280fb->vidmem_phys, gsc3280fb->base + XXXX_REG_LCD_VBAR); //写DMA基地址到LCD寄存器
- fb->mode = NULL;
- xxxxfb_set_par(fb); //设置参数,在(二)中讲述
- ret = register_framebuffer(fb); //注册framebuffer
- if (ret) {
- dev_err(&pdev->dev, "Failed to register framebuffer: %d\n", ret);
- goto err_free_devmem;
- }
- xxxxfb->fb = fb;
- my_vga_mode = 1;
- #ifdef CONFIG_FB_XXXX_VGA
- //VGA的参数
- DBG("init vga mode");
- if (my_vga_mode == 0) {
- writel(0x879f03ff, xxxxfb->base + XXXX_REG_LCD_HTIM);
- writel(0x1c0502ff, xxxxfb->base + XXXX_REG_LCD_VTIM);
- writel(0x05400326, xxxxfb->base + XXXX_REG_LCD_HVLEN);
- }
- else if (my_vga_mode == 1) {
- writel(0xd78704ff, xxxxfb->base + XXXX_REG_LCD_HTIM);
- writel(0x1d0203bf, xxxxfb->base + XXXX_REG_LCD_VTIM);
- writel(0x06b003e2, xxxxfb->base + XXXX_REG_LCD_HVLEN);
- }
- else {
- ;
- }
- #else
- DBG("init lcd mode");
- writel(0x2700031f, gsc3280fb->base + XXXX_REG_LCD_HTIM);
- writel(0x031301f3, gsc3280fb->base + XXXX_REG_LCD_VTIM);
- writel(0x0420020d, gsc3280fb->base + XXXX_REG_LCD_HVLEN);
- #endif
- xxxxfb_enable(xxxx fb); //使能LCD,接下来讲述
- printk(KERN_INFO "######xxxx_lcdfb_probe success######\n");
- return 0;
- err_free_devmem:
- fb_dealloc_cmap(&fb->cmap);
- xxxxfb_free_devmem(xxxxfb);
- err_iounmap:
- iounmap(xxxxfb->base);
- iounmap(xxxxfb->pseudo_palette);
- err_framebuffer_alloc:
- framebuffer_release(fb);
- err_release_mem_region:
- release_mem_region(mem->start, resource_size(mem));
- err_put_lpclk:
- clk_put(lcd_clk);
- printk(KERN_INFO "######xxxx_lcdfb_probe error######\n");
- return ret;
- }
点击(此处)折叠或打开
- //宏定义见fb.h
- static const struct fb_fix_screeninfo xxxxfb_fix __devinitdata = {
- .id = "XXXXFB",
- .type = FB_TYPE_PACKED_PIXELS,
- .visual = FB_VISUAL_TRUECOLOR,
- .xpanstep = 0,
- .ypanstep = 0,
- .ywrapstep = 0,
- .accel = FB_ACCEL_NONE,
- };
点击(此处)折叠或打开
- static int xxxxfb_alloc_devmem(struct xxxxfb *xxxxfb)
- {
- int i = 0, max_videosize = 0;
- struct fb_videomode *mode = xxxxfb->pdata->modes;
- for (i = 0; i < xxxxfb->pdata->num_modes; ++mode, ++i) {
- if (max_videosize < mode->xres * mode->yres)
- max_videosize = mode->xres * mode->yres;
- }
- max_videosize *= xxxxfb_get_controller_bpp(xxxxfb) >> 3;
- xxxxfb->framedesc = dma_alloc_coherent(&xxxxfb->pdev->dev,
- sizeof(*xxxxfb->framedesc),
- &xxxxfb->framedesc_phys, GFP_KERNEL);
- if (!xxxxfb->framedesc)
- return -ENOMEM;
- xxxxfb->vidmem_size = PAGE_ALIGN(max_videosize); //DMA内存大小
- xxxxfb->vidmem = dma_alloc_coherent(&xxxxfb->pdev->dev,
- xxxx0fb->vidmem_size,
- &xxxxfb->vidmem_phys, GFP_KERNEL); //申请DMA内存
- if (!xxxxfb->vidmem)
- goto err_free_framedesc;
- xxxxfb->framedesc->next = xxxxfb->framedesc_phys;
- xxxxfb->framedesc->addr = xxxxfb->vidmem_phys;
- xxxxfb->framedesc->id = 0xdeafbead;
- xxxxfb->framedesc->cmd = 0;
- xxxxfb->framedesc->cmd |= max_videosize / 4;
- return 0;
- err_free_framedesc:
- dma_free_coherent(&xxxx0fb->pdev->dev, sizeof(*xxxxfb->framedesc),
- xxxxfb->framedesc, xxxxfb->framedesc_phys);
- return -ENOMEM;
- }
使能LCD函数xxxxfb_enable:
点击(此处)折叠或打开
- static void gsc3280fb_enable(struct xxxxfb *xxxxfb)
- {
- writel(0x101c, xxxxfb->base + XXXX_REG_LCD_CTRL); //设置控制寄存器
- writel(0x1, xxxxfb->base + XXXX_REG_LCD_ENABLE); //使能
- clk_enable(xxxxfb->ldclk); //使能时钟
- }
二、fb支持的函数集xxxx_ops
xxxx_ops具体内容如下:
点击(此处)折叠或打开
- /*Framebuffer底层硬件操作各接口函数*/
- static struct fb_ops xxxxfb_ops = {
- .owner = THIS_MODULE,
- .fb_check_var = xxxxfb_check_var,
- .fb_set_par = xxxxfb_set_par, /*设置fb_info中的参数,主要是LCD的显示模式*/
- .fb_blank = xxxxfb_blank, /*显示空白(即:LCD开关控制)*/
- .fb_cursor = xxxxfb_cursor,
- .fb_setcolreg = xxxxfb_setcolreg, /*设置颜色表*/
- /*以下三个函数是可选的,主要是提供fb_console的支持,在内核中已经实现,这里直接调用即可*/
- .fb_fillrect = sys_fillrect, /*定义在drivers/video/cfbfillrect.c中*/
- .fb_copyarea = sys_copyarea, /*定义在drivers/video/cfbcopyarea.c中*/
- .fb_imageblit = sys_imageblit, /*定义在drivers/video/cfbimgblt.c中*/
- };
1、xxxxfb_check_var
点击(此处)折叠或打开
- static int xxxxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb)
- {
- struct xxxxfb *xxxxfb = fb->par;
- struct fb_videomode *mode;
- if (var->bits_per_pixel != xxxxfb_get_controller_bpp(xxxxfb) &&
- var->bits_per_pixel != xxxxfb->pdata->bpp) //获取bpp,接下来讲述
- return -EINVAL;
- mode = xxxxfb_get_mode(xxxxfb, var); //获取模式,内容接下来讲述
- if (mode == NULL)
- return -EINVAL;
- /* 设置时钟像素,行、帧切换值,水平同步、垂直同步长度值 */
- fb_videomode_to_var(var, mode); //从mode赋值到var
- /*根据色位模式(BPP)来设置可变参数中R、G、B的颜色位域。*/
- switch (xxxxfb->pdata->bpp) {
- case 8:
- break;
- case 15:
- var->red.offset = 10;
- var->red.length = 5;
- var->green.offset = 6;
- var->green.length = 5;
- var->blue.offset = 0;
- var->blue.length = 5;
- break;
- case 16:
- var->red.offset = 11;
- var->red.length = 5;
- var->green.offset = 5;
- var->green.length = 6;
- var->blue.offset = 0;
- var->blue.length = 5;
- break;
- default:
- break;
- }
- return 0;
- }
点击(此处)折叠或打开
- static int xxxxfb_get_controller_bpp(struct xxxxfb *xxxxfb)
- {
- switch (xxxxfb->pdata->bpp) {
- case 8:
- return 8;
- case 15:
- case 16:
- return 16;
- default:
- return gsc3280fb->pdata->bpp;
- }
- }
点击(此处)折叠或打开
- static struct fb_videomode *xxxxfb_get_mode(struct xxxxfb *xxxxfb,
- struct fb_var_screeninfo *var)
- {
- size_t i;
- struct fb_videomode *mode = xxxxfb->pdata->modes;
-
- for (i = 0; i < xxxxfb->pdata->num_modes && mode != NULL; ++i, ++mode) { //逐一比较
- if ((mode->xres == var->xres) && (mode->yres == var->yres)) { //比较判断
- return mode;
- }
- }
- return NULL;
- }
2、设置参数函数xxxxfb_set_par
点击(此处)折叠或打开
- static int xxxxfb_set_par(struct fb_info *info)
- {
- struct xxxxfb *xxxxfb = info->par;
- struct gsc3280_fb_platform_data *pdata = xxxxfb->pdata;
- struct fb_var_screeninfo *var = &info->var;
- struct fb_videomode *mode;
- uint32_t ctrl;
- uint32_t vtim, htim, hvlen, vt, ht;
- //unsigned long rate;
- mode = xxxxfb_get_mode(xxxxfb, var); //取得模式,上面讲述
- if (mode == NULL)
- return -EINVAL;
- if (mode == info->mode)
- return 0;
- info->mode = mode;
- //根据寄存器的定义组装各个值
- htim = (mode->hsync_len << 24) | (mode->left_margin << 16) | ((mode->xres-1) & 0xffff ) ;
- vtim = (mode->vsync_len << 24) | (mode->upper_margin << 16) | ((mode->yres -1) & 0xffff ) ;
- ht = mode->hsync_len + mode->left_margin + mode->right_margin + mode->xres -1;
- vt = mode->vsync_len + mode->upper_margin + mode->lower_margin + mode->yres- 1;
- hvlen = ht << 16 | (vt & 0xffff);
- ctrl = 0;
- switch (pdata->bpp) {
- case 16:
- ctrl |= GSC3280_LCD_CTRL_BPP_16;
- break;
- case 8:
- ctrl |= GSC3280_LCD_CTRL_BPP_8;
- break;
- default:
- break;
- }
- ctrl |= pdata->lcd_type & 0xff;
- if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
- ctrl |= XXXX_LCD_CTRL_HSYNC_ACTIVE_LOW;
- if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
- ctrl |= XXXX_LCD_CTRL_VSYNC_ACTIVE_LOW;
- if (pdata->lcd_type == XXXX_LCD_TYPE_16BIT_TFT)
- ctrl |= 0x1;
- writel(htim, xxxxfb->base + XXXX_REG_LCD_HTIM);
- writel(vtim, xxxxfb->base + XXXX_REG_LCD_VTIM);
- writel(hvlen, xxxxfb->base + XXXX_REG_LCD_HVLEN);
- //writel(ctrl, xxxxfb->base + XXXX_REG_LCD_CTRL);
- if (!xxxxfb->is_enabled)
- ;
- //clk_disable(xxxxfb->ldclk);
- mutex_unlock(&xxxxfb->lock);
- //clk_set_rate(xxxxfb->lpclk, rate);
- //clk_set_rate(xxxxfb->ldclk, rate * 3);
- return 0;
- }
3、清空函数xxxxfb_blank
点击(此处)折叠或打开
- static int xxxxfb_blank(int blank_mode, struct fb_info *info)
- {
- struct gsc3280fb *xxxxfb = info->par;
- switch (blank_mode) {
- case FB_BLANK_UNBLANK:
- mutex_lock(&xxxxfb->lock);
- if (xxxxfb->is_enabled) {
- mutex_unlock(&xxxxfb->lock);
- return 0; //已经使能了,不用再使能
- }
- xxxxfb_enable(gsc3280fb);
- xxxxfb->is_enabled = 1;
- mutex_unlock(&xxxxfb->lock);
- break;
- default:
- mutex_lock(&xxxxfb->lock);
- if (!xxxxfb->is_enabled) {
- mutex_unlock(&xxxxfb->lock);
- return 0; //已经停止了,不用再停止了
- }
- xxxxfb_disable(xxxxfb); //停用LCD,接下来讲述
- xxxxfb->is_enabled = 0;
- mutex_unlock(&xxxxfb->lock);
- break;
- }
- return 0;
- }
点击(此处)折叠或打开
- static void xxxxfb_disable(struct xxxxfb *xxxxfb)
- {
- uint32_t ctrl;
- ctrl = readl(xxxxfb->base + XXXX_REG_LCD_ENABLE);
- ctrl &= ~XXXX_LCD_CTRL_DISABLE; //停用
- writel(ctrl, xxxxfb->base + XXXX_REG_LCD_ENABLE);
- do {
- ctrl = readl(xxxxfb->base + XXXX_REG_LCD_ENABLE);
- } while ((ctrl & XXXX_LCD_STATE_DISABLED)); //等待停止成功
- clk_disable(xxxxfb->ldclk); //停用时钟
- }
A = dma_alloc_writecombine(B,C,D,GFP_KERNEL);
含义:
A: 内存的虚拟起始地址,在内核要用此地址来操作所分配的内存
B: struct device指针,可以平台初始化里指定,主要是dma_mask之类,可参考framebuffer
C: 实际分配大小,传入dma_map_size即可
D: 返回的内存物理地址,dma就可以用。
所以,A和D是一一对应的,只不过,A是虚拟地址,而D是物理地址。对任意一个操作都将改变缓冲区内容。
我对此函数的理解是,调用此函数将会分配一段内存,D将返回这段内存的实际物理地址供DMA来使用,A将是D对应的
虚拟地址供操作系统调用,对A和D的的任意一个进行操作,都会改变这段内存缓冲区的内容。