linux lcd设备驱动剖析四
在“linux lcd设备驱动剖析二”文章中,我们详细分析了s3c24xxfb_probe函数。
文章链接:http://blog.csdn.net/lwj103862095/article/details/18189765
s3c2410fb.c中s3c24xxfb_probe是非常重要的函数之一,但仅仅分析probe函数,貌似感觉有点不够过瘾,貌似缺少分析了一个非常重要的成员。在probe函数中有一句:fbinfo->fbops =
&s3c2410fb_ops;
plain?
- static struct fb_ops s3c2410fb_ops = {
- .owner = THIS_MODULE,
- .fb_check_var = s3c2410fb_check_var, //设置可变参数
- .fb_set_par = s3c2410fb_set_par, //设置固定参数及lcdcon寄存器
- .fb_blank = s3c2410fb_blank, //设置是否使能LCD控制器
- .fb_setcolreg = s3c2410fb_setcolreg, //设置RGB颜色,实现伪颜色表
- .fb_fillrect = cfb_fillrect, //画一个矩形
- .fb_copyarea = cfb_copyarea, //Copy data from area to another
- .fb_imageblit = cfb_imageblit, //Draws a image to the display
- };
一、s3c2410fb_check_var函数主要根据tq2440_lcd_cfg实例来设置fb_info结构体的可变参数
fb_var_screeninfo结构体各个成员,如xres、yres、bits_per_pixel、height、width 等等,具体分析如下:
plain?
- /* 此函数的主要功能是设置可变参数var */
- static int s3c2410fb_check_var(struct fb_var_screeninfo *var,
- struct fb_info *info)
- {
- struct s3c2410fb_info *fbi = info->par;
- /* platform_data就是tq2440_fb_info结构体实例 */
- struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
- struct s3c2410fb_display *display = NULL;
- /* 在tq2440_fb_info实例里,displays = tq2440_lcd_cfg,default_display = 0 */
- struct s3c2410fb_display *default_display = mach_info->displays +
- mach_info->default_display;
- /* 在tq2440_fb_info实例里,type = S3C2410_LCDCON1_TFT */
- int type = default_display->type;
- unsigned i;
- dprintk("check_var(var=%p, info=%p)\n", var, info);
- /* validate x/y resolution */
- /* choose default mode if possible */
- /* 如果参数都等于tq2440_fb_info实例里的参数
- * 那么赋值给display,此时display指向tq2440_fb_info实例
- */
- if (var->yres == default_display->yres &&
- var->xres == default_display->xres &&
- var->bits_per_pixel == default_display->bpp)
- display = default_display;
- /* 否则从tq2440_fb_info结构体实例中循环匹配,num_displays = 1 */
- else
- for (i = 0; i < mach_info->num_displays; i++)
- if (type == mach_info->displays[i].type &&
- var->yres == mach_info->displays[i].yres &&
- var->xres == mach_info->displays[i].xres &&
- var->bits_per_pixel == mach_info->displays[i].bpp) {
- display = mach_info->displays + i;
- break;
- }
- /* 如果匹配不成功,display = NULL 则错误 */
- if (!display) {
- dprintk("wrong resolution or depth %dx%d at %d bpp\n",
- var->xres, var->yres, var->bits_per_pixel);
- return -EINVAL;
- }
- /* it is always the size as the display */
- /* 找到匹配的display后,将实例中的可变参数赋值 */
- var->xres_virtual = display->xres;
- var->yres_virtual = display->yres;
- var->height = display->height;
- var->width = display->width;
- /* copy lcd settings */
- var->pixclock = display->pixclock;
- var->left_margin = display->left_margin;
- var->right_margin = display->right_margin;
- var->upper_margin = display->upper_margin;
- var->lower_margin = display->lower_margin;
- var->vsync_len = display->vsync_len;
- var->hsync_len = display->hsync_len;
- fbi->regs.lcdcon5 = display->lcdcon5;
- /* set display type */
- fbi->regs.lcdcon1 = display->type;
- var->transp.offset = 0;
- var->transp.length = 0;
- /* set r/g/b positions */
- switch (var->bits_per_pixel) {
- case 1:
- case 2:
- case 4:
- var->red.offset = 0;
- var->red.length = var->bits_per_pixel;
- var->green = var->red;
- var->blue = var->red;
- break;
- ......
- /* TQ2440的LCD就是采用这种模式 */
- case 32:
- /* 24 bpp 888 and 8 dummy */
- var->red.length = 8;
- var->red.offset = 16;
- var->green.length = 8;
- var->green.offset = 8;
- var->blue.length = 8;
- var->blue.offset = 0;
- break;
- }
- return 0;
- }
tq2440_lcd_cfg实例在arch/arm/mach-s3c2440/mach-tq2440.c中定义
plain?
- /* LCD driver info */
- /* tq2440_lcd_cfg在tq2440_fb_info中被设置 */
- static struct s3c2410fb_display tq2440_lcd_cfg __initdata = {
- .lcdcon5 = S3C2410_LCDCON5_FRM565 |
- S3C2410_LCDCON5_INVVLINE |
- S3C2410_LCDCON5_INVVFRAME |
- S3C2410_LCDCON5_PWREN |
- S3C2410_LCDCON5_HWSWP,
- .type = S3C2410_LCDCON1_TFT,
- ......
- /* TQ(LCD W4.3)的配置,config_EmbedSky_W43:CONFIG_FB_S3C24X0_TFT480272=y */
- #elif defined(CONFIG_FB_S3C24X0_TFT480272)
- .width = 480,
- .height = 272,
- .pixclock = 40000, /* HCLK 100 MHz, divisor 1 */
- /* VCLK=HCLK/[(CLKVAL+1)x2],HCLK = 100MHz
- * 根据LCD手册"WXCAT43-TG6#001_V1.0.pdf"的第22页可得,VCLK = 10MHz
- * 即10 = 100/[(CLKVAL+1)x2],可得CLKVAL = 4
- */
- .setclkval = 0x4,
- .xres = 480,
- .yres = 272,
- .bpp = 16,
- /* 为什么是这样?参考linux/Documentation/fb/framebuffer.txt */
- .left_margin = 19, /* 左边沿 : for HFPD*/
- .right_margin = 10, /* 右边沿 : for HBPD*/
- .hsync_len = 30, /* 水平同步: for HSPW*/
- .upper_margin = 4, /* 上边沿 : for VFPD*/
- .lower_margin = 2, /* 下边沿 : for VBPD*/
- .vsync_len = 8, /* 垂直同步: for VSPW*/
- ......
- };
二、s3c2410fb_set_par函数先根据var->bits_per_pixel来选择fix.visual,这里bits_per_pixel = 32,
故fix.visual = FB_VISUAL_TRUECOLOR,然后计算一行的字节数,最后调用s3c2410fb_activate_var
函数来激活LCD控制器,即设置各个lcdcon寄存器。
plain?
- static int s3c2410fb_set_par(struct fb_info *info)
- {
- /* 获得刚被s3c2410fb_check_var函数设置过的var */
- struct fb_var_screeninfo *var = &info->var;
- switch (var->bits_per_pixel) {
- case 32:
- case 16:
- case 12:
- info->fix.visual = FB_VISUAL_TRUECOLOR; /* 真彩色 */
- break;
- case 1:
- info->fix.visual = FB_VISUAL_MONO01;
- break;
- default:
- info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
- break;
- }
- /* 一行的字节数 = x*bpp/8 = 480*32/8 = 480*4 */
- info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
- /* activate this new configuration */
- s3c2410fb_activate_var(info);
- return 0;
- }
s3c2410fb_activate_var函数先调用s3c2410fb_calc_pixclk函数来计算LCD时钟频率,然后调用s3c2410fb_calculate_tft_lcd_regs函数来设置lcdcon1~lcdcon5,然后调用writel函数将前面s3c2410fb_calculate_tft_lcd_regs函数设置好的lcdconx写入对应寄存器,接着调用s3c2410fb_set_lcdaddr函数来设置LCDSADDR1、LCDSADDR2、LCDSADDR3寄存器,也就是将之前在probe函数通过s3c2410fb_map_video_memory-->dma_alloc_writecombine函数分配好的“显存”告诉LCD控制器,最后使能LCD控制器。
plain?
- static void s3c2410fb_activate_var(struct fb_info *info)
- {
- /* 在framebuffer_alloc函数里info->par指向了额外多申请
- * 内存空间的首地址,即info->par指向s3c2410fb_info结构体
- */
- struct s3c2410fb_info *fbi = info->par;
- void __iomem *regs = fbi->io; /* IO基地址 */
- /* 设置显示模式为: TFT LCD panel */
- int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
- /* 通过probe函数后platform_data指向tq2440_fb_info结构体实例 */
- struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
- struct s3c2410fb_display *default_display = mach_info->displays +
- mach_info->default_display;
- struct fb_var_screeninfo *var = &info->var;
- /* 计算LCD时钟频率, 在mach_tq2440.c里 pixclock = 40000 */
- int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
- dprintk("%s: var->xres = %d\n", __func__, var->xres);
- dprintk("%s: var->yres = %d\n", __func__, var->yres);
- dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);
- if (type == S3C2410_LCDCON1_TFT) {
- s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);
- --clkdiv;
- if (clkdiv < 0)
- clkdiv = 0;
- } else {
- s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
- if (clkdiv < 2)
- clkdiv = 2;
- }
- // fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);
- fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(default_display->setclkval);
- /* write new registers */
- dprintk("new register set:\n");
- dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
- dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
- dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
- dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
- dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
- /* 禁止视频输出,禁止LCD控制信号 */
- writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
- regs + S3C2410_LCDCON1);
- /* 将前面s3c2410fb_calculate_tft_lcd_regs函数设置好的lcdconx写入对应寄存器 */
- writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
- writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
- writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
- writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
- /* set lcd address pointers */
- s3c2410fb_set_lcdaddr(info);
- /* 最后使能LCD控制器,即使能视频输出 */
- fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
- writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);
- }
s3c2410fb_calculate_tft_lcd_regs函数比较简单这里就不分析了,这里只分析s3c2410fb_set_lcdaddr函数
plain?
- static void s3c2410fb_set_lcdaddr(struct fb_info *info)
- {
- unsigned long saddr1, saddr2, saddr3;
- struct s3c2410fb_info *fbi = info->par;
- void __iomem *regs = fbi->io;
- /* LCDSADDR1 = 帧缓冲区起始地址再右移1位 */
- saddr1 = info->fix.smem_start >> 1;
- /* LCDSADDR2 = 帧缓冲区结束地址再右移1位 */
- saddr2 = info->fix.smem_start;
- saddr2 += info->fix.line_length * info->var.yres; /* 帧缓冲区大小 */
- saddr2 >>= 1;
- /* LCDSADDR3 = 一行的长度,单位为2字节 */
- saddr3 = S3C2410_OFFSIZE(0) |
- S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff);
- dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);
- dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);
- dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);
- writel(saddr1, regs + S3C2410_LCDSADDR1);
- writel(saddr2, regs + S3C2410_LCDSADDR2);
- writel(saddr3, regs + S3C2410_LCDSADDR3);
- }
三、s3c2410fb_setcolreg函数主要通过red,green,blue三原色构造出val,然后再将val写入pseudo_palette假调色板中。
plain?
- static int s3c2410fb_setcolreg(unsigned regno,
- unsigned red, unsigned green, unsigned blue,
- unsigned transp, struct fb_info *info)
- {
- struct s3c2410fb_info *fbi = info->par;
- void __iomem *regs = fbi->io;
- unsigned int val;
- /* TQ2440的LCD是FB_VISUAL_TRUECOLOR,即TFT */
- switch (info->fix.visual) {
- case FB_VISUAL_TRUECOLOR:
- /* true-colour, use pseudo-palette */
- if (regno < 16) {
- u32 *pal = info->pseudo_palette;
- /* 用red,green,blue三原色构造出val */
- val = chan_to_field(red, &info->var.red);
- val |= chan_to_field(green, &info->var.green);
- val |= chan_to_field(blue, &info->var.blue);
- pal[regno] = val;
- }
- break;
- ......
- default:
- return 1; /* unknown type */
- }
- return 0;
- }
chan_to_field 函数如下,将具体的RGB数据代入就比较容易理解这个函数了,相应的var.red、var.green、var.blue在s3c2410fb_check_var函数的最后面有设置。
plain?
- static inline unsigned int chan_to_field(unsigned int chan,
- struct fb_bitfield *bf)
- {
- chan &= 0xffff;
- chan >>= 16 - bf->length;
- return chan << bf->offset;
- }
s3c2410fb_check_var函数设置rgb的部分代码,这里省略了大部分源码,为的是方便参考。
plain?
- static int s3c2410fb_check_var(struct fb_var_screeninfo *var,
- struct fb_info *info)
- {
- .......
- /* set r/g/b positions */
- switch (var->bits_per_pixel) {
- .......
- /* TQ2440的LCD就是采用这种模式 */
- case 32:
- /* 24 bpp 888 and 8 dummy */
- var->red.length = 8;
- var->red.offset = 16;
- var->green.length = 8;
- var->green.offset = 8;
- var->blue.length = 8;
- var->blue.offset = 0;
- break;
- }
- return 0;
- }
而cfb_fillrect、cfb_copyarea、cfb_imageblit是通用的函数,不用驱动工程师去理会,只需要在加载lcd驱动时,将其对应的模块加载,而要加载模块,必须在编译内核后,再执行make modules,这样就可以得到相应的cfb*.ko了。
到这里s3c2410fb.c内核自带的lcd驱动基本剖析完毕,这里总结一下难点:
一、内核自带的lcd驱动是以平台驱动设备模型来编写的,难点不在框架,框架其实很简单,
1、分配一个fb_info结构体;
2、设置 fb_info结构体;
3、注册;
4、硬件相关的设置
二、好啦,难点就是如何设置fb_info结构体,而fb_info结构体成员那么多,是不每个成员都要一一设置呢?当然不是,
主要设置fb_info结构体的固定参数 fb_fix_screeninfo结构体和可变参数fb_var_screeninfo结构体,还有就是硬件相关
的设置,比如lcd时序参数的设置,也就是要设置lcdcon1~lcdcon5,lcdaddr1~lcdaddr3各个寄存器。
linux lcd设备驱动剖析四的更多相关文章
- linux lcd设备驱动剖析一
s3c2440 lcd驱动源码文件是:drivers/video/s3c2410fb.c 看驱动源码首先当然是先看入口函数,这里是s3c2410fb_init函数 [cpp] view plain? ...
- linux lcd设备驱动剖析三
上一节文章中详细地剖析了probe函数,但是从始至终都没有看到打开读写文件接口的操作函数,只看到了下面这个操作结构体 [cpp] view plain? static struct fb_ops s3 ...
- linux lcd设备驱动剖析二
上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构. 上一节文章链接:http://blog.csdn.net/lwj103862095/ ...
- linux 块设备驱动(四)——简单的sbull实例
#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> # ...
- 深入理解Linux字符设备驱动
文章从上层应用访问字符设备驱动开始,一步步地深入分析Linux字符设备的软件层次.组成框架和交互.如何编写驱动.设备文件的创建和mdev原理,对Linux字符设备驱动有全面的讲解.本文整合之前发表的& ...
- Linux字符设备驱动结构(一)--cdev结构体、设备号相关知识机械【转】
本文转载自:http://blog.csdn.net/zqixiao_09/article/details/50839042 一.字符设备基础知识 1.设备驱动分类 linux系统将设备分为3类:字符 ...
- 【VS开发】【DSP开发】浅谈Linux PCI设备驱动(二)
我们在 浅谈Linux PCI设备驱动(一)中(以下简称 浅谈(一) )介绍了PCI的配置寄存器组,而Linux PCI初始化就是使用了这些寄存器来进行的.后面我们会举个例子来说明Linux PCI设 ...
- 【VS开发】【DSP开发】浅谈Linux PCI设备驱动(一)
要弄清楚Linux PCI设备驱动,首先要明白,所谓的Linux PCI设备驱动实际包括Linux PCI设备驱动和设备本身驱动两部分.不知道读者理不理解这句话,本人觉得这句话很重要,对于PCI.US ...
- (57)Linux驱动开发之三Linux字符设备驱动
1.一般情况下,对每一种设备驱动都会定义一个软件模块,这个工程模块包含.h和.c文件,前者定义该设备驱动的数据结构并声明外部函数,后者进行设备驱动的具体实现. 2.典型的无操作系统下的逻辑开发程序是: ...
随机推荐
- 随机森林和adaboost算法(待更新)
Adaboost是一种迭代算法,其核心思想是针对同一个训练集训练不同的分类器(弱分类器),然后把这些弱分类器集合起来,构成一个更强的最终分类器(强分类器).
- VS2015自带v120的Platform Toolset
在VS2015安装组件时,勾选 “Windows 8.1 和 Windows Phone 8.0/8.1”下面的“工具和 Windows SDK”,将会安装Visual Studio 2013 (v1 ...
- LeetCode OJ:Product of Array Except Self(除己之外的元素乘积)
Given an array of n integers where n > 1, nums, return an array output such that output[i] is equ ...
- 剑指offer--25.二叉树的镜像
时间限制:1秒 空间限制:32768K 热度指数:238655 题目描述 操作给定的二叉树,将其变换为源二叉树的镜像. 输入描述: 二叉树的镜像定义:源二叉树 8 / \ 6 10 / \ / \ 5 ...
- [转载]Python注册表信息丢失的解决方案
今天安装Python的模块时,安装失败,提示信息:Python version 2.7 required, which was not found in the registry. 原因在于Pytho ...
- WindowManager实现悬浮可拖动效果
现在360手机卫士有个流量统计的效果,开启流量统计后,在桌面上会出现一个显示流量的窗体,在任何界面都可以自由拖动. 模仿这个功能,做了一个统计手机信号强度的Demo, 界面效果如下: 从上面的截图可以 ...
- 创建Oracle数据库需要注意的几点
规划表和存储空间,防止出现空间不足或者空间的浪费 规划快速恢复区,将快速恢复区定义在与数据文件不同的存储区域,减少IO争用 规划数据库的名称,db_name,db_main,sid 规划spfile内 ...
- HihoCoder1127 二分图三·二分图最小点覆盖和最大独立集
二分图三·二分图最小点覆盖和最大独立集 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 在上次安排完相亲之后又过了挺长时间,大家好像都差不多见过面了.不过相亲这个事不是说 ...
- asp.net core microservices 架构之Task 事务一致性 事件源 详解
一 aspnetcore之task的任务状态-CancellationToken 我有一篇文章讲解了asp.net的线程方面的知识.我们知道.net的针对于多线程的一个亮点就是Task,net clr ...
- Robot Framework接口测试(4)
现在我们已经做好了进行接口测试的必要准备:1.拼接发送的报文:2.发送报文的方法.现在我们实现RF上的接口测试. 我们先对发送的方法进行一下封装: 1.拼接报文方法: #coding : utf-8 ...