Chinaunix首页 | 论坛 | 博客
  • 博客访问: 353286
  • 博文数量: 197
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 303
  • 用 户 组: 普通用户
  • 注册时间: 2013-09-02 14:21
文章分类

全部博文(197)

文章存档

2014年(89)

2013年(108)

我的朋友

分类: 嵌入式

2013-10-31 14:47:53

一、LCD驱动探测(probe)和(remove)函数

        分析一个总线驱动一般都从探测函数开始,现在看下探测函数,在LCD驱动第一部分末尾讲述了LCD驱动定义的结构体,本文在多处将使用这些定义的结构体。

点击(此处)折叠或打开

  1. static int __devinit xxxxfb_probe(struct platform_device *pdev)
  2. {
  3.     int ret = 0;
  4.     struct clk *lcd_clk;
  5.     struct fb_info *fb;
  6.     struct resource *mem;
  7.     unsigned long rate = 0;
  8.     struct xxxxfb *xxxxfb;
  9.     struct xxxx_fb_platform_data *pdata = pdev->dev.platform_data;
  10.     if (!pdata) {
  11.         dev_err(&pdev->dev, "Missing platform data\n");
  12.         return -ENXIO;
  13.     }
  14.     printk(KERN_INFO "######xxxx_lcdfb_probe start######\n");
  15.     
  16. #ifdef CONFIG_FB_xxxx_SVGA
  17.         *(volatile unsigned int *)addr = 0x4;    //addr为系统寄存器中设置LCD分频寄存器
  18. #else
  19.         *(volatile unsigned int *)addr = 0x5;
  20. #endif

  21.     lcd_clk = clk_get(NULL, "lcd");
  22.     if (IS_ERR(lcd_clk)) {
  23.         DBG("failed to find watchdog clock source\n");
  24.         ret = PTR_ERR(lcd_clk);
  25.         return -ENXIO;
  26.     }
  27.     rate = clk_get_rate(lcd_clk);
  28.     DBG("lcd clk rate is %ld\n", rate);
  29.     clk_enable(lcd_clk);
  30.     
  31.     mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  32.     if (!mem) {
  33.         dev_err(&pdev->dev, "Failed to get register memory resource\n");
  34.         ret = -ENXIO;
  35.         goto err_put_lpclk;
  36.     }
  37.     mem = request_mem_region(mem->start, resource_size(mem), pdev->name);
  38.     if (!mem) {
  39.         dev_err(&pdev->dev, "Failed to request register memory region\n");
  40.         ret = -EBUSY;
  41.         goto err_put_lpclk;
  42.     }
  43.     fb = framebuffer_alloc(sizeof(struct xxxxfb), &pdev->dev);
  44.     if (!fb) {
  45.         dev_err(&pdev->dev, "Failed to allocate framebuffer device\n");
  46.         ret = -ENOMEM;
  47.         goto err_release_mem_region;
  48.     }
  49.     fb->fbops = &xxxx_ops;    //fb支持的操作函数集,在(二)中讲述
  50.     fb->flags = FBINFO_DEFAULT;
  51.     gsc3280fb = fb->par;
  52.     gsc3280fb->pdev = pdev;
  53.     gsc3280fb->pdata = pdata;
  54.     gsc3280fb->mem = mem;
  55.     gsc3280fb->ldclk = lcd_clk;
  56.     gsc3280fb->base = ioremap(mem->start, resource_size(mem));    //寄存器映射
  57.     DBG("xxxxfb->base = %p\n", xxxxfb->base);
  58.     if (!xxxxfb->base) {
  59.         dev_err(&pdev->dev, "Failed to ioremap register memory region\n");
  60.         ret = -EBUSY;
  61.         goto err_framebuffer_alloc;
  62.     }
  63.     //调色板io地址映射
  64.     xxxxfb->pseudo_palette = ioremap(XXXX_LCDC_CMAP_REG, XXXX_LCDC_CMAP_LEN);
  65.     if (!xxxxfb->pseudo_palette ) {
  66.         dev_err(&pdev->dev, "Failed to ioremap cmap register memory region\n");
  67.         ret = -EBUSY;
  68.         if (xxxxfb->base ) {
  69.             iounmap(gsc3280fb->base);
  70.         }
  71.         goto err_framebuffer_alloc;
  72.     }
  73.     platform_set_drvdata(pdev, xxxxfb);
  74.     mutex_init(&xxxxfb->lock);
  75.     fb_videomode_to_modelist(pdata->modes, pdata->num_modes, &fb->modelist);
  76.     fb_videomode_to_var(&fb->var, pdata->modes); //从pdata->modes赋值到fb->var,完全的赋值函数
  77.     fb->var.bits_per_pixel = pdata->bpp;    //赋值bpp
  78.     xxxxfb_check_var(&fb->var, fb);     //校验参数在(二)中讲述
  79.     ret = xxxxfb_alloc_devmem(gsc3280fb);     //申请DMA内存,接下来讲述
  80.     if (ret) {
  81.         dev_err(&pdev->dev, "Failed to allocate video memory\n");
  82.         goto err_iounmap;
  83.     }
  84.     fb->fix = xxxxfb_fix;    //fix结构体,内容见后面
  85.     fb->fix.line_length = fb->var.bits_per_pixel * fb->var.xres / 8;
  86.     fb->fix.mmio_start = mem->start;
  87.     fb->fix.mmio_len = resource_size(mem);
  88.     fb->fix.smem_start = gsc3280fb->vidmem_phys;
  89.     fb->fix.smem_len = fb->fix.line_length * fb->var.yres;
  90.     fb->screen_base = gsc3280fb->vidmem;
  91.     fb->pseudo_palette = gsc3280fb->pseudo_palette;
  92.     fb_alloc_cmap(&fb->cmap, 256, 0);
  93.     gsc3280fb->is_enabled = 1;
  94.     writel(gsc3280fb->vidmem_phys, gsc3280fb->base + XXXX_REG_LCD_VBAR);     //写DMA基地址到LCD寄存器
  95.     fb->mode = NULL;
  96.     xxxxfb_set_par(fb);     //设置参数,在(二)中讲述
  97.     ret = register_framebuffer(fb);    //注册framebuffer
  98.     if (ret) {
  99.         dev_err(&pdev->dev, "Failed to register framebuffer: %d\n", ret);
  100.         goto err_free_devmem;
  101.     }
  102.     xxxxfb->fb = fb;
  103.     my_vga_mode = 1;

  104. #ifdef CONFIG_FB_XXXX_VGA
  105.     //VGA的参数
  106.     DBG("init vga mode");
  107.     if (my_vga_mode == 0) {
  108.         writel(0x879f03ff, xxxxfb->base + XXXX_REG_LCD_HTIM);
  109.         writel(0x1c0502ff, xxxxfb->base + XXXX_REG_LCD_VTIM);
  110.         writel(0x05400326, xxxxfb->base + XXXX_REG_LCD_HVLEN);
  111.     }
  112.     else if (my_vga_mode == 1) {
  113.         writel(0xd78704ff, xxxxfb->base + XXXX_REG_LCD_HTIM);
  114.         writel(0x1d0203bf, xxxxfb->base + XXXX_REG_LCD_VTIM);
  115.         writel(0x06b003e2, xxxxfb->base + XXXX_REG_LCD_HVLEN);
  116.     }
  117.     else {
  118.         ;
  119.     }
  120. #else            
  121.     DBG("init lcd mode");
  122.     writel(0x2700031f, gsc3280fb->base + XXXX_REG_LCD_HTIM);
  123.     writel(0x031301f3, gsc3280fb->base + XXXX_REG_LCD_VTIM);
  124.     writel(0x0420020d, gsc3280fb->base + XXXX_REG_LCD_HVLEN);
  125. #endif

  126.     xxxxfb_enable(xxxx fb);    //使能LCD,接下来讲述
  127.     printk(KERN_INFO "######xxxx_lcdfb_probe success######\n");
  128.     return 0;

  129. err_free_devmem:
  130.     fb_dealloc_cmap(&fb->cmap);
  131.     xxxxfb_free_devmem(xxxxfb);
  132. err_iounmap:
  133.     iounmap(xxxxfb->base);
  134.     iounmap(xxxxfb->pseudo_palette);
  135. err_framebuffer_alloc:
  136.     framebuffer_release(fb);
  137. err_release_mem_region:
  138.     release_mem_region(mem->start, resource_size(mem));
  139. err_put_lpclk:
  140.     clk_put(lcd_clk);
  141.     printk(KERN_INFO "######xxxx_lcdfb_probe error######\n");
  142.     return ret;
  143. }
        fix结构体

点击(此处)折叠或打开

  1. //宏定义见fb.h
  2. static const struct fb_fix_screeninfo xxxxfb_fix __devinitdata = {
  3.     .id        = "XXXXFB",
  4.     .type        = FB_TYPE_PACKED_PIXELS,
  5.     .visual        = FB_VISUAL_TRUECOLOR,
  6.     .xpanstep    = 0,
  7.     .ypanstep    = 0,
  8.     .ywrapstep    = 0,
  9.     .accel        = FB_ACCEL_NONE,
  10. };
        现在看下申请DMA内存函数xxxxfb_alloc_devmem(xxxxfb)

点击(此处)折叠或打开

  1. static int xxxxfb_alloc_devmem(struct xxxxfb *xxxxfb)
  2. {
  3.     int i = 0, max_videosize = 0;
  4.     struct fb_videomode *mode = xxxxfb->pdata->modes;
  5.     for (i = 0; i < xxxxfb->pdata->num_modes; ++mode, ++i) {
  6.         if (max_videosize < mode->xres * mode->yres)
  7.             max_videosize = mode->xres * mode->yres;
  8.     }
  9.     max_videosize *= xxxxfb_get_controller_bpp(xxxxfb) >> 3;
  10.     xxxxfb->framedesc = dma_alloc_coherent(&xxxxfb->pdev->dev,
  11.                     sizeof(*xxxxfb->framedesc),
  12.                     &xxxxfb->framedesc_phys, GFP_KERNEL);
  13.     if (!xxxxfb->framedesc)
  14.         return -ENOMEM;
  15.     xxxxfb->vidmem_size = PAGE_ALIGN(max_videosize);    //DMA内存大小
  16.     xxxxfb->vidmem = dma_alloc_coherent(&xxxxfb->pdev->dev,
  17.                     xxxx0fb->vidmem_size,
  18.                     &xxxxfb->vidmem_phys, GFP_KERNEL);    //申请DMA内存
  19.     if (!xxxxfb->vidmem)
  20.         goto err_free_framedesc;
  21.     xxxxfb->framedesc->next = xxxxfb->framedesc_phys;
  22.     xxxxfb->framedesc->addr = xxxxfb->vidmem_phys;
  23.     xxxxfb->framedesc->id = 0xdeafbead;
  24.     xxxxfb->framedesc->cmd = 0;
  25.     xxxxfb->framedesc->cmd |= max_videosize / 4;
  26.     return 0;
  27. err_free_framedesc:
  28.     dma_free_coherent(&xxxx0fb->pdev->dev, sizeof(*xxxxfb->framedesc),
  29.                 xxxxfb->framedesc, xxxxfb->framedesc_phys);
  30.     return -ENOMEM;
  31. }

        使能LCD函数xxxxfb_enable:

点击(此处)折叠或打开

  1. static void gsc3280fb_enable(struct xxxxfb *xxxxfb)
  2. {
  3.     writel(0x101c, xxxxfb->base + XXXX_REG_LCD_CTRL);    //设置控制寄存器
  4.     writel(0x1, xxxxfb->base + XXXX_REG_LCD_ENABLE);    //使能
  5.     clk_enable(xxxxfb->ldclk);     //使能时钟
  6. }


二、fb支持的函数集xxxx_ops

        xxxx_ops具体内容如下:

点击(此处)折叠或打开

  1. /*Framebuffer底层硬件操作各接口函数*/
  2. static struct fb_ops xxxxfb_ops = {
  3.     .owner        = THIS_MODULE,
  4.     .fb_check_var = xxxxfb_check_var,
  5.     .fb_set_par   = xxxxfb_set_par,    /*设置fb_info中的参数,主要是LCD的显示模式*/
  6.     .fb_blank      = xxxxfb_blank,    /*显示空白(即:LCD开关控制)*/
  7.     .fb_cursor      = xxxxfb_cursor,
  8.     .fb_setcolreg = xxxxfb_setcolreg,    /*设置颜色表*/
  9.     /*以下三个函数是可选的,主要是提供fb_console的支持,在内核中已经实现,这里直接调用即可*/
  10.     .fb_fillrect     = sys_fillrect,    /*定义在drivers/video/cfbfillrect.c中*/
  11.     .fb_copyarea     = sys_copyarea,    /*定义在drivers/video/cfbcopyarea.c中*/
  12.     .fb_imageblit    = sys_imageblit,    /*定义在drivers/video/cfbimgblt.c中*/
  13. };
        除了内核中实现的最后三个函数外,现在我们一一讲述各个接口函数。

1、xxxxfb_check_var

点击(此处)折叠或打开

  1. static int xxxxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb)
  2. {
  3.     struct xxxxfb *xxxxfb = fb->par;
  4.     struct fb_videomode *mode;

  5.     if (var->bits_per_pixel != xxxxfb_get_controller_bpp(xxxxfb) &&
  6.         var->bits_per_pixel != xxxxfb->pdata->bpp)    //获取bpp,接下来讲述
  7.         return -EINVAL;
  8.     mode = xxxxfb_get_mode(xxxxfb, var);    //获取模式,内容接下来讲述
  9.     if (mode == NULL)
  10.         return -EINVAL;
  11.      /* 设置时钟像素,行、帧切换值,水平同步、垂直同步长度值 */
  12.     fb_videomode_to_var(var, mode);    //从mode赋值到var
  13.     /*根据色位模式(BPP)来设置可变参数中R、G、B的颜色位域。*/
  14.     switch (xxxxfb->pdata->bpp) {
  15.     case 8:
  16.         break;
  17.     case 15:
  18.         var->red.offset = 10;
  19.         var->red.length = 5;
  20.         var->green.offset = 6;
  21.         var->green.length = 5;
  22.         var->blue.offset = 0;
  23.         var->blue.length = 5;
  24.         break;
  25.     case 16:
  26.         var->red.offset = 11;
  27.         var->red.length = 5;
  28.         var->green.offset = 5;
  29.         var->green.length = 6;
  30.         var->blue.offset = 0;
  31.         var->blue.length = 5;
  32.         break;
  33.     default:
  34.         break;
  35.     }
  36.     return 0;
  37. }
        xxxxfb_get_controller_bpp

点击(此处)折叠或打开

  1. static int xxxxfb_get_controller_bpp(struct xxxxfb *xxxxfb)
  2. {
  3.     switch (xxxxfb->pdata->bpp) {
  4.     case 8:
  5.         return 8;
  6.     case 15:
  7.     case 16:
  8.         return 16;
  9.     default:
  10.         return gsc3280fb->pdata->bpp;
  11.     }
  12. }
       xxxxfb_get_mode:

点击(此处)折叠或打开

  1. static struct fb_videomode *xxxxfb_get_mode(struct xxxxfb *xxxxfb,
  2.     struct fb_var_screeninfo *var)
  3. {
  4.     size_t i;
  5.     struct fb_videomode *mode = xxxxfb->pdata->modes;
  6.     
  7.     for (i = 0; i < xxxxfb->pdata->num_modes && mode != NULL; ++i, ++mode) {    //逐一比较
  8.         if ((mode->xres == var->xres) && (mode->yres == var->yres)) {    //比较判断
  9.             return mode;
  10.         }
  11.     }
  12.     return NULL;
  13. }

2、设置参数函数xxxxfb_set_par

点击(此处)折叠或打开

  1. static int xxxxfb_set_par(struct fb_info *info)
  2. {
  3.     struct xxxxfb *xxxxfb = info->par;
  4.     struct gsc3280_fb_platform_data *pdata = xxxxfb->pdata;
  5.     struct fb_var_screeninfo *var = &info->var;
  6.     struct fb_videomode *mode;
  7.     uint32_t ctrl;
  8.     uint32_t vtim, htim, hvlen, vt, ht;
  9.     //unsigned long rate;
  10.     mode = xxxxfb_get_mode(xxxxfb, var);     //取得模式,上面讲述
  11.     if (mode == NULL)
  12.         return -EINVAL;
  13.     if (mode == info->mode)
  14.         return 0;
  15.     info->mode = mode;
  16.     //根据寄存器的定义组装各个值
  17.     htim = (mode->hsync_len << 24) | (mode->left_margin << 16) | ((mode->xres-1) & 0xffff ) ;
  18.     vtim = (mode->vsync_len << 24) | (mode->upper_margin << 16) | ((mode->yres -1) & 0xffff ) ;
  19.     ht = mode->hsync_len + mode->left_margin + mode->right_margin + mode->xres -1;
  20.     vt = mode->vsync_len + mode->upper_margin + mode->lower_margin + mode->yres- 1;
  21.     hvlen = ht << 16 | (vt & 0xffff);
  22.     ctrl = 0;
  23.     switch (pdata->bpp) {
  24.     case 16:
  25.         ctrl |= GSC3280_LCD_CTRL_BPP_16;
  26.         break;
  27.     case 8:
  28.         ctrl |= GSC3280_LCD_CTRL_BPP_8;
  29.         break;
  30.     default:
  31.         break;
  32.     }
  33.     ctrl |= pdata->lcd_type & 0xff;
  34.     if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
  35.         ctrl |= XXXX_LCD_CTRL_HSYNC_ACTIVE_LOW;
  36.     if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
  37.         ctrl |= XXXX_LCD_CTRL_VSYNC_ACTIVE_LOW;
  38.     if (pdata->lcd_type == XXXX_LCD_TYPE_16BIT_TFT)
  39.         ctrl |= 0x1;
  40.     writel(htim, xxxxfb->base + XXXX_REG_LCD_HTIM);
  41.     writel(vtim, xxxxfb->base + XXXX_REG_LCD_VTIM);
  42.     writel(hvlen, xxxxfb->base + XXXX_REG_LCD_HVLEN);
  43.     //writel(ctrl, xxxxfb->base + XXXX_REG_LCD_CTRL);
  44.     if (!xxxxfb->is_enabled)
  45.         ;
  46.     //clk_disable(xxxxfb->ldclk);
  47.     mutex_unlock(&xxxxfb->lock);
  48.     //clk_set_rate(xxxxfb->lpclk, rate);
  49.     //clk_set_rate(xxxxfb->ldclk, rate * 3);
  50.     return 0;
  51. }

3、清空函数xxxxfb_blank

点击(此处)折叠或打开

  1. static int xxxxfb_blank(int blank_mode, struct fb_info *info)
  2. {
  3.     struct gsc3280fb *xxxxfb = info->par;
  4.     switch (blank_mode) {
  5.     case FB_BLANK_UNBLANK:
  6.         mutex_lock(&xxxxfb->lock);
  7.         if (xxxxfb->is_enabled) {
  8.             mutex_unlock(&xxxxfb->lock);
  9.             return 0;    //已经使能了,不用再使能
  10.         }
  11.         xxxxfb_enable(gsc3280fb);
  12.         xxxxfb->is_enabled = 1;
  13.         mutex_unlock(&xxxxfb->lock);
  14.         break;
  15.     default:
  16.         mutex_lock(&xxxxfb->lock);
  17.         if (!xxxxfb->is_enabled) {
  18.             mutex_unlock(&xxxxfb->lock);
  19.             return 0;    //已经停止了,不用再停止了
  20.         }
  21.         xxxxfb_disable(xxxxfb);    //停用LCD,接下来讲述
  22.         xxxxfb->is_enabled = 0;
  23.         mutex_unlock(&xxxxfb->lock);
  24.         break;
  25.     }
  26.     return 0;
  27. }
        停用LCD函数xxxxfb_disable:

点击(此处)折叠或打开

  1. static void xxxxfb_disable(struct xxxxfb *xxxxfb)
  2. {
  3.     uint32_t ctrl;
  4.     ctrl = readl(xxxxfb->base + XXXX_REG_LCD_ENABLE);
  5.     ctrl &= ~XXXX_LCD_CTRL_DISABLE;     //停用
  6.     writel(ctrl, xxxxfb->base + XXXX_REG_LCD_ENABLE);
  7.     do {
  8.         ctrl = readl(xxxxfb->base + XXXX_REG_LCD_ENABLE);
  9.     } while ((ctrl & XXXX_LCD_STATE_DISABLED));    //等待停止成功
  10.     clk_disable(xxxxfb->ldclk);    //停用时钟
  11. }

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的的任意一个进行操作,都会改变这段内存缓冲区的内容。






阅读(1782) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~