itop4412 LCD设备驱动详解(四)之PROBE再深入
2016-11-05 21:47
961 查看
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);
该函数的作用在第二节中提到:
函数原型如下:
现在来看看s3cfb_init_fbinfo函数
dma burst 参数参考
exynos 所能支持的bpp:由寄存器决定
这里是WINCON0 的参数:
接下来看:
2 probe 之 s3cfb_register_framebuffer(fbdev[i])) ./* register fb_info */
代码如下 终于向内核提交fb_info 了
好了返回probe
接下来:这两个都是硬件相关函数:
3.probe 之s3cfb_set_clock(fbdev[i]);
probe 函数之 s3cfb_enable_window
SHADOWCON 使能
Base Address = 0x11C0_0000
Address = Base Address + 0x0034, Reset Value = 0x0000_0000
WINCON0 使能窗口数据手册
1probe 函数之
开启设备VIDCON0
至此probe函数就介绍的差不多了,主要功能就这些,其他就略过。
剩下就是要介绍下面的操作函数 (还有win的尺寸,buf地址,坐标,画图等都还没 介绍)
——-未完待续
在第二节中我们详解介绍了 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地址,坐标,画图等都还没 介绍)
——-未完待续
相关文章推荐
- itop4412 LCD设备驱动详解(三)之PROBE
- itop4412 LCD设备驱动详解(二)之DRIVER
- itop4412 LCD设备驱动详解(一)之DEVICE
- 迅为4412开发板Linux驱动教程——总线_设备_驱动注册流程详解
- Writing Linux LCD drivers—深入分析framebuffer设备驱动的结构
- Writing Linux LCD drivers—深入分析framebuffer设备驱动的结构
- Writing Linux LCD drivers—深入分析framebuffer设备驱动的结构
- Itop4412开发板emmc驱动移植(设备树)
- Writing Linux LCD drivers—深入分析framebuffer设备驱动的结构
- iTOP-4412开发板驱动lcd显卡以及linux开机log的修改方法
- 迅为4412开发板Linux驱动教程——总线_设备_驱动注册流程详解
- Writing Linux LCD drivers—深入分析framebuffer设备驱动的结构
- 4412驱动实验之05 总线_设备_驱动注册流程详解
- 迅为4412开发板Linux驱动教程——总线_设备_驱动注册流程详解
- [迅为开发板资料分享]iTOP-4412开发板LCD的屏幕驱动
- Writing Linux LCD drivers—深入分析framebuffer设备驱动的结构
- 转:linux下 s3c2440 lcd驱动移植详解
- linux 设备驱动开发详解 code (1)
- linux 设备驱动开发详解 code (5)
- 从USB设备插上到驱动probe调用流程分析