itop exynos4412 lcd驱动 详细分析 (四)

来源:互联网 发布:Linux下重启apache 编辑:程序博客网 时间:2024/06/02 00:34

(若转载,请注明出处,若有错误请指正,谢谢)
(以下分析皆基于:itop4412精英板设备和代码资源)
(内核为:iTop4412_Kernel_3.0提供)
(看客需要一定的linux平台驱动基础,和lcd操作基础)

lcd的工作,在kernel 中有device 和driver两个描述,这也是必然

在第二节中我们详解介绍了 s3cfb_main.c ——-probe函数的框架。
回顾一下probe函数的作用:
1. 获取平台设备 device中的资源
2. 对设备做了一下相应的初始化
3. 申请了fb_info ,根据要求进行了填充
4. 向内核提交了fb_info
5. 使能设备等
6. 创建属性文件

在上一节中,我们对probe函数中的部分接口进行了阐述,现在接着来看剩下的。
(请与第二节probe函数框架对应分析)

看这一节之前请务必 先了解该三个结构体:
struct fb_info ;
struct fb_fix_screeninfo ;
struct fb_var_screeninfo ;

1 .probe 之 s3cfb_alloc_framebuffer(fbdev[i], i);

 该函数的作用在第二节中提到:  /* alloc fb_info */        /* 这个函数在s3cfb_ops.c 文件中   -------fb 操作集合*/        /*这个函数的作用是申请struct fb_info 结构体,初始化fb_info 结构体的信息*/        /*fb_info 结构体 上与内核接口耦合的关系,我们写lcd驱动就是除了初始化相关硬件以外*/        /*就是把fb_info 结构体初始化后 注册到内核,供 fb_mem 调用,上层才能跟底层结合起来*/        /*该函数具体内容将在接下来介绍*/

函数原型如下:

int s3cfb_alloc_framebuffer(struct s3cfb_global *fbdev, int fimd_id){    struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev);    int ret = 0;    int i;    // 根据wins 个数开辟fb_info 空间的指针,这个在platform device中指定了    //这里并没有申请fb_info空间,数据结构体框图请参见第二节的介绍    /*        static struct s3c_platform_fb default_fb_data __initdata = {        #if defined(CONFIG_ARCH_EXYNOS4)            .hw_ver = 0x70,        #else            .hw_ver = 0x62,        #endif            .nr_wins    = 5,  //就是它        #if defined(CONFIG_FB_S5P_DEFAULT_WINDOW)            .default_win    = CONFIG_FB_S5P_DEFAULT_WINDOW,        #else            .default_win    = 0,        #endif            .swap       = FB_SWAP_WORD | FB_SWAP_HWORD,        };            */    fbdev->fb = kmalloc(pdata->nr_wins *                sizeof(struct fb_info *), GFP_KERNEL);    if (!fbdev->fb) {        dev_err(fbdev->dev, "not enough memory\n");        ret = -ENOMEM;        goto err_alloc;    }    //接下来就是为每个fb_info 申请空间,进行初始化了    for (i = 0; i < pdata->nr_wins; i++) {        /*这个函数的功能是:开辟 fb_info s3cfb_windows 空间    并且把 fb_info 中的dev 指向 fbdev->dev     把 fb_info->par 指向 s3cfb_windows,可以参考下面的图*/         fbdev->fb[i] = framebuffer_alloc(sizeof(struct s3cfb_window),                        fbdev->dev);        if (!fbdev->fb[i]) {            dev_err(fbdev->dev, "not enough memory\n");            ret = -ENOMEM;            goto err_alloc_fb;        }        /*主要是初始化fb_info 结构体,对var ,fix进行填充等....*/        ret = s3cfb_init_fbinfo(fbdev, i);        if (ret) {            dev_err(fbdev->dev,                "failed to allocate memory for fb%d\n", i);            ret = -ENOMEM;            goto err_alloc_fb;        }#ifdef CONFIG_FB_S5P_SOFTBUTTON_UI        if (i == pdata->default_win || i == 4)#else        if (i == pdata->default_win)#endif                  /*主要是为窗体分配存放RGB数据的空间。(该分配一般用DMA)*/            if (s3cfb_map_default_video_memory(fbdev,                        fbdev->fb[i], fimd_id)) {                dev_err(fbdev->dev,                "failed to map video memory "                "for default window (%d)\n", i);            ret = -ENOMEM;            goto err_alloc_fb;        }    }    return 0;err_alloc_fb:    for (i = 0; i < pdata->nr_wins; i++) {        if (fbdev->fb[i])            framebuffer_release(fbdev->fb[i]);    }    kfree(fbdev->fb);err_alloc:    return ret;}
    fbdev->fb[i] = framebuffer_alloc(sizeof(struct s3cfb_window),    fbdev->dev);函数的作用开辟 fb_info s3cfb_windows 空间并且把 fb_info 中的dev 指向 fbdev->dev 把 fb_info->par 指向 s3cfb_windows

这里写图片描述

现在来看看s3cfb_init_fbinfo函数

int s3cfb_init_fbinfo(struct s3cfb_global *fbdev, int id){    /*        对刚刚开辟的fb_info 空间中的相应地址 进行提取,方便接下来的填充                (对fb_info 中相关成员的信息,请查阅相关资料,这里不做阐述)                (所这里需要读者具有一定的功底)    */    struct fb_info *fb = fbdev->fb[id];    struct fb_fix_screeninfo *fix = &fb->fix;    struct fb_var_screeninfo *var = &fb->var;    struct s3cfb_window *win = fb->par;    struct s3cfb_alpha *alpha = &win->alpha;    struct s3cfb_lcd *lcd = fbdev->lcd;    struct s3cfb_lcd_timing *timing = &lcd->timing;    /*对窗体进行清空,由此可见 fb->par 的作用。设置fix ->id   */    memset(win, 0, sizeof(struct s3cfb_window));    platform_set_drvdata(to_platform_device(fbdev->dev), fb);    strcpy(fix->id, S3CFB_NAME);    /*    指定窗体id,窗体数据路径,dma burst ,win的win->power_state 状态,设置透明度方式*/    /* fimd specific */    win->id = id;    /*选择数据来源*/    /*enum s3cfb_data_path_t {    DATA_PATH_FIFO = 0,    DATA_PATH_DMA = 1,    DATA_PATH_IPC = 2,    };*/    win->path = DATA_PATH_DMA;    /*设置dma_burst ,大小范围可以根据数据数据手册决定,请看下图*/    win->dma_burst = 16;    s3cfb_update_power_state(fbdev, win->id, FB_BLANK_POWERDOWN);    alpha->mode = PLANE_BLENDING;    /* fbinfo */    /*   设置fb->fbops = &s3cfb_ops;  s3cfb_ops 是一个全局变量在,s3cfb_main 中指定:*/    fb->fbops = &s3cfb_ops;    fb->flags = FBINFO_FLAG_DEFAULT;//  然后是设置FBINFO    fb->pseudo_palette = &win->pseudo_pal;//设置虚拟的调色板地址#if (CONFIG_FB_S5P_NR_BUFFERS != 1) // 设置 偏移: 一般为0    fix->xpanstep = 2;    fix->ypanstep = 1;#else    fix->xpanstep = 0;    fix->ypanstep = 0;#endif    /*  设置type --- FB_TYPE_PACKED_PIXELS   -----像素与内存对应,TFT就是基于这个管理内存。根据设备需求不同选择*/     fix->type = FB_TYPE_PACKED_PIXELS;    fix->accel = FB_ACCEL_NONE;//无此设备    /*-----设置显示格式真彩,当然还有黑白*/    /*索引等显示方式,请查阅相关资料*/    fix->visual = FB_VISUAL_TRUECOLOR;    /*设置可变参数宽高,lcd 是一个指针,指向了wa101 结构体,(还记得否?)*/    var->xres = lcd->width;    var->yres = lcd->height;     /*设置虚拟分辨率,嵌入式设备一般不该分辨率*/     /*所以通常设置成  xres和yres 一样*/     /*这里是为了驱动兼容*/#if defined(CONFIG_FB_S5P_VIRTUAL)    var->xres_virtual = CONFIG_FB_S5P_X_VRES;    var->yres_virtual = CONFIG_FB_S5P_Y_VRES * CONFIG_FB_S5P_NR_BUFFERS;#else    var->xres_virtual = var->xres;    var->yres_virtual = var->yres * CONFIG_FB_S5P_NR_BUFFERS;#endif    /* 设置成 32 bpp 的分辨率,这里啰嗦一句: 分辨率由 WINCONx 寄存器中BBPMODE决定,你可以看到这里支持的格式有很多。而代码写死了(可惜,也可能是跟其他硬件兼容,也可能是为了一口气开辟最大的空间,低bpp可以不用重新申请空间,bpp向下兼容(猜测)    */    var->bits_per_pixel = 32;    /*设置xoffset ,yoffset 偏移 都为0*/    /*不为0 的话:var->xoffset = var->xres_virtual - var->xres - 1*/    var->xoffset = 0;    /*不为0的话:var->yoffset = var->yres_virtual - var->yres - 1;*/    var->yoffset = 0;    var->width = 0;    var->height = 0;    var->transp.length = 0;    fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;    fix->smem_len = fix->line_length * var->yres_virtual;    var->nonstd = 0;//----标准格式 ,!=0则为非标准格式    var->activate = FB_ACTIVATE_NOW;//----完全应用    var->vmode = FB_VMODE_NONINTERLACED;//-----正常扫描    // 以下是设置timing    var->hsync_len = timing->h_sw;    var->vsync_len = timing->v_sw;    var->left_margin = timing->h_bp;    var->right_margin = timing->h_fp;    var->upper_margin = timing->v_bp;    var->lower_margin = timing->v_fp;    //根据相应参数计算pixclock的值,    var->pixclock = (lcd->freq *        (var->left_margin + var->right_margin        + var->hsync_len + var->xres) *        (var->upper_margin + var->lower_margin        + var->vsync_len + var->yres));    var->pixclock = KHZ2PICOS(var->pixclock/1000);    //设置fb的R/G/B位域,-----也就是RGB的格式,这里非常简单,不做描述    s3cfb_set_bitfield(var);    /*    设置透明度 模式    设置channel---0 通道    设置value -------最大值不透明    */    s3cfb_set_alpha_info(var, win);    return 0;}

dma burst 参数参考
这里写图片描述

exynos 所能支持的bpp:由寄存器决定
这里是WINCON0 的参数:
这里写图片描述
这里写图片描述

接下来看:

    if (s3cfb_map_default_video_memory(fbdev,                        fbdev->fb[i], fimd_id)) {                dev_err(fbdev->dev,                "failed to map video memory "                "for default window (%d)\n", i);            ret = -ENOMEM;            goto err_alloc_fb;        }
    s3cfb_map_default_video_memory 主要是为窗体分配存放RGB数据的空间。(该分配一般用DMA)    函数太长,功能单一,这里就简单的阐述一下就可以了    并且让 fix->smem_start ------指向开辟空间的物理空间      (空间长度有上面fix->mem_len 指定了)                fb->screen_base ------指向开辟空间的虚拟地址

2 probe 之 s3cfb_register_framebuffer(fbdev[i])) ./* register fb_info */
代码如下 终于向内核提交fb_info 了

int s3cfb_register_framebuffer(struct s3cfb_global *fbdev){    struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev);    int ret, i, j;    /* on registering framebuffer, framebuffer of default window is registered at first. */    for (i = pdata->default_win; i < pdata->nr_wins + pdata->default_win; i++) {        j = i % pdata->nr_wins;        ret = register_framebuffer(fbdev->fb[j]); //向内核注册lcd设备,成功过后就                //在/dev/fbx的节点  (创建不是drive做的,有兴趣的可以分析该函数)        if (ret) {            dev_err(fbdev->dev, "failed to register \                framebuffer device\n");            return -EINVAL;        }#ifdef CONFIG_FB_S5P_SOFTBUTTON_UI /* Add Menu UI Window 4 */        if(j==4){            dev_info(fbdev->dev, " set parameters for win4");            s3cfb_check_var_window(fbdev, &fbdev->fb[j]->var, fbdev->fb[j]);            s3cfb_set_par_window(fbdev, fbdev->fb[j]);        }#endif#ifndef CONFIG_FRAMEBUFFER_CONSOLE        //lcd 控制台        if (j == pdata->default_win) {            //fops  /* 检查参数和设置参数将在后面涉及中&s3cfb_ops;讲到  */            // 检查参数            s3cfb_check_var_window(fbdev, &fbdev->fb[j]->var,                    fbdev->fb[j]);            // 设置参数            s3cfb_set_par_window(fbdev, fbdev->fb[j]);            // 显示logo            s3cfb_draw_logo(fbdev->fb[j]);        }#endif    }    return 0;}

好了返回probe
接下来:这两个都是硬件相关函数:

3.probe 之s3cfb_set_clock(fbdev[i]);

/*     该函数的主要作用是获得时钟,计算clk,向VIDCON0 配置相应的时钟参数*/int s3cfb_set_clock(struct s3cfb_global *ctrl){    struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev);    u32 cfg, maxclk, src_clk, vclk, div;    /* spec is under 100MHz */    maxclk = 100 * 1000000;    cfg = readl(ctrl->regs + S3C_VIDCON0);    if (pdata->hw_ver == 0x70) {        cfg &= ~(S3C_VIDCON0_CLKVALUP_MASK |            S3C_VIDCON0_VCLKEN_MASK);        cfg |= (S3C_VIDCON0_CLKVALUP_ALWAYS |            S3C_VIDCON0_VCLKEN_NORMAL);        src_clk = clk_get_rate(ctrl->clock);        printk(KERN_DEBUG "FIMD src sclk = %d\n", src_clk);    } else {        cfg &= ~(S3C_VIDCON0_CLKSEL_MASK |            S3C_VIDCON0_CLKVALUP_MASK |            S3C_VIDCON0_VCLKEN_MASK |            S3C_VIDCON0_CLKDIR_MASK);        cfg |= (S3C_VIDCON0_CLKVALUP_ALWAYS |            S3C_VIDCON0_VCLKEN_NORMAL |            S3C_VIDCON0_CLKDIR_DIVIDED);        if (strcmp(pdata->clk_name, "sclk_fimd") == 0) {            cfg |= S3C_VIDCON0_CLKSEL_SCLK;            src_clk = clk_get_rate(ctrl->clock);            printk(KERN_DEBUG "FIMD src sclk = %d\n", src_clk);        } else {            cfg |= S3C_VIDCON0_CLKSEL_HCLK;            src_clk = ctrl->clock->parent->rate;            printk(KERN_DEBUG "FIMD src hclk = %d\n", src_clk);        }    }    vclk = PICOS2KHZ(ctrl->fb[pdata->default_win]->var.pixclock) * 1000;    if (vclk > maxclk) {        dev_info(ctrl->dev, "vclk(%d) should be smaller than %d\n",            vclk, maxclk);        /* vclk = maxclk; */    }    div = src_clk / vclk;    if (src_clk % vclk) {        if ((src_clk % vclk) > (vclk/2))            div++;    }    if ((src_clk/div) > maxclk)        dev_info(ctrl->dev, "vclk(%d) should be smaller than %d Hz\n",            src_clk/div, maxclk);    cfg &= ~S3C_VIDCON0_CLKVAL_F(0xff);    cfg |= S3C_VIDCON0_CLKVAL_F(div - 1);    writel(cfg, ctrl->regs + S3C_VIDCON0);    //dev_dbg(ctrl->dev,     printk("parent clock: %d, vclk: %d, vclk div: %d\n",            src_clk, vclk, div);    return 0;}

probe 函数之 s3cfb_enable_window

int s3cfb_enable_window(struct s3cfb_global *fbdev, int id){    struct s3cfb_window *win = fbdev->fb[id]->par;    if (!win->enabled)        atomic_inc(&fbdev->enabled_win);    if (s3cfb_window_on(fbdev, id)) { //使能窗口,具体操作如下        win->enabled = 0;        return -EFAULT;    } else {        win->enabled = 1;        return 0;    }}int s3cfb_window_on(struct s3cfb_global *ctrl, int id){    struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev);    u32 cfg;    if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) {        cfg = readl(ctrl->regs + S3C_WINSHMAP);        cfg |= S3C_WINSHMAP_CH_ENABLE(id);  //选择通道        writel(cfg, ctrl->regs + S3C_WINSHMAP);    }    cfg = readl(ctrl->regs + S3C_WINCON(id));    cfg |= S3C_WINCON_ENWIN_ENABLE;  //使能数据,可以查WINCONx ,第0位    writel(cfg, ctrl->regs + S3C_WINCON(id));    dev_dbg(ctrl->dev, "[fb%d] turn on\n", id);    return 0;}

SHADOWCON 使能
 Base Address = 0x11C0_0000
 Address = Base Address + 0x0034, Reset Value = 0x0000_0000
这里写图片描述

WINCON0 使能窗口数据手册
这里写图片描述

1probe 函数之

        //设置窗口状态,由程序员标记        s3cfb_update_power_state(fbdev[i], pdata->default_win,                    FB_BLANK_UNBLANK);        //着重看这儿,真正开启了lcd设备                  s3cfb_display_on(fbdev[i]);    int s3cfb_display_on(struct s3cfb_global *ctrl){    u32 cfg;    cfg = readl(ctrl->regs + S3C_VIDCON0);    cfg |= (S3C_VIDCON0_ENVID_ENABLE | S3C_VIDCON0_ENVID_F_ENABLE);    writel(cfg, ctrl->regs + S3C_VIDCON0);    dev_dbg(ctrl->dev, "global display is on\n");    return 0;}

开启设备VIDCON0
这里写图片描述

至此probe函数就介绍的差不多了,主要功能就这些,其他就略过。
剩下就是要介绍下面的操作函数 (还有win的尺寸,buf地址,坐标,画图等都还没 介绍)
这里写图片描述

——未完待续

0 0
原创粉丝点击