iMX6开发板移植Linux系统之LVDS显示屏驱动程序分析之LVDS参数的匹配过程分析

学习交流加

  • 个人qq:
    1126137994
  • 个人微信:
    liu1126137994
  • 学习交流资源分享qq群:
    962535112

上一篇分析LVDS驱动程序移植过程的文章(文章链接为:移植Linux系统到iMX6开发板之LVDS显示屏驱动程序的框架分析与移植)中最后于有一点需要分析LVDS参数的匹配过程的,由于篇幅太长,所以另写一篇文章来记录。

核心函数fb_find_mode(),在分析之前先了解下几个参数。

重要参数说明:
一. ldb.c中的 ldb_modedb
在i.mx6中,关于lvds液晶屏的这个结构体参数(系统lvds接口支持的lcd时序参数都在此了)所属文件为:driver/video/mxc/ldb.c

static struct fb_videomode ldb_modedb[] = {{"LDB-WXGA", 60, 1280, 800, 14065,40, 40,10, 3,80, 10,0,FB_VMODE_NONINTERLACED,FB_MODE_IS_DETAILED,},{"LDB-XGA", 60, 1024, 768, 15385,220, 40,21, 7,60, 10,0,FB_VMODE_NONINTERLACED,FB_MODE_IS_DETAILED,},{"LDB-WSVGA", 60, 1024, 600, 19528,140, 160,20, 12,20, 3,0,FB_VMODE_NONINTERLACED,FB_MODE_IS_DETAILED,},{"LDB-WSVGA480", 60, 1024, 480, 23000,140, 160,20, 12,20, 3,0,FB_VMODE_NONINTERLACED,FB_MODE_IS_DETAILED,},{"LDB-1080P60", 60, 1920, 1080, 7692,100, 40,30, 3,10, 2,0,FB_VMODE_NONINTERLACED,FB_MODE_IS_DETAILED,},{"LDB-QXGA", 30, 2048, 1536, 9746,5, 150,9, 3,5, 1,0,FB_VMODE_NONINTERLACED,FB_MODE_IS_DETAILED,},};

这些结构体参数的意义:

/* include/linux/fb.h */                 struct fb_videomode {  
2.     const char *name;  “LDB-WSVGA”    /* 名字 */  
3.     u32 refresh;        60    /* 刷新频率 */  
4.     u32 xres;           1024  //行像素  
5.     u32 yres;           768   //列像素  
6.     u32 pixclock;       19528(14065) //时钟频率,单位ps,14430  
7.     u32 left_margin;    140   // HBPD(horizontal back porch):80  
8.     u32 right_margin;   160   // HFPD(horizontal front porth):48  
9.     u32 upper_margin;   20    // VBPD(vertical back porch),15  
10.    u32 lower_margin;   12    // VFBD(vertical front porch),2  
11.    u32 hsync_len;      20    // HSPW(horizontal sync pulse width):32  
12.    u32 vsync_len;      3     // VSPW(vertical sync pulse width):47  
13.    u32 sync;           0 
14.    u32 vmode;          
15.    u32 flag;  
16.};

我们项目中用的屏幕参数如下:
整屏刷新频率 60M
屏幕分辨率 1024*768
时钟频率 14065
left_margin 40(单位像素)
right_margin 40(单位像素)
upper_margin 10(单位像素)
lower_margin 3(单位像素)
行扫描脉宽 hsync_len 80(单位像素时间)
场扫描脉宽vsync_len 10(单位像素时间)

二. arch\arm\mach-mx6\Board-mx6q_sabresd.c 中的 ipuv3_fb_platform_data结构。
我们的是:

static struct ipuv3_fb_platform_data sabresd_fb_data[] = {{ /*fb0*/.disp_dev = "ldb",.interface_pix_fmt = IPU_PIX_FMT_RGB666,.mode_str = "LDB-XGA",.default_bpp = 16,.int_clk = false,.late_init = false,}, {.disp_dev = "ldb",.interface_pix_fmt = IPU_PIX_FMT_RGB666,.mode_str = "LDB-XGA",.default_bpp = 16,.int_clk = false,}, {.disp_dev = "lcd",.interface_pix_fmt = IPU_PIX_FMT_RGB565,.mode_str = "CLAA-WVGA",.default_bpp = 16,.int_clk = false,.late_init = false,}, {.disp_dev = "ldb",.interface_pix_fmt = IPU_PIX_FMT_RGB666,.mode_str = "LDB-VGA",.default_bpp = 16,.int_clk = false,.late_init = false,},
};

fb_find_mode()函数就是匹配上面结构的参数mode_str 的值,然后再去ldb_modedb结构体看看有没有LVDS需要的时序参数。

此mode_str其实就是后面会提到的mode_options, 格式如下:

 <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or<name>[-<bpp>][@<refresh>]

所以有两种类型:

  1. 字符规则形, 如 “LDB-WXVGA”
  2. 数字规则形,如"1920*1080"
    具体各个参数意义可参照fb_find_mode()函数注释。

三 .环境变量的设置(uboot环境变量cmdline的设置:)
我们拿到的源码,是由厂家直接提供的源码,可以直接通过uboot环境变量向内核代码覆盖一些参数:
如下:
单通道模式:
setenv bootargs_mmc 'setenv bootargs ${bootargs} ip=off root=/dev/mmcblk0p1 rootwait rw
video=mxcfb0:dev=ldb,LDB-WSVGA,if=RGB24,bpp=32
video=mxcfb1:off video=mxcfb2:off ldb=sin0 fbmem=28M fb0base=0x27b00000 ’

将上面参数通过uboot启动,输入进去,保存后重新启动就可以。

它会覆盖sabresd_fb_data[]的值,覆盖的规则根据mxcfb后面的值,比如 mxcfb0 覆盖sabresd_fb_data[0]
里的值,以此类推。了解了参数的意义后,下面就好理解了:

ldb.c中的ldb_disp_init函数有如下调用:

static int ldb_disp_init(struct mxc_dispdrv_handle *disp,  struct mxc_dispdrv_setting *setting) {  ......  ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str,  ldb_modedb, ldb_modedb_sz, NULL, setting->default_bpp);  
...... }  

我们fb_find_mode的参数为:
setting->dft_mode_str为: “LDB-WSVGA”
setting->default_bpp为: 32

fb_find_mode执行源代码为:

int fb_find_mode(struct fb_var_screeninfo *var,struct fb_info *info, const char *mode_option,const struct fb_videomode *db, unsigned int dbsize,const struct fb_videomode *default_mode,unsigned int default_bpp)
{int i;/* Set up defaults *//*如果db参数没有给,则使用modedb*/if (!db) {db = modedb;dbsize = ARRAY_SIZE(modedb);}/*如果没有设置则使用db[0]的值,我们本身就是使用db[0](只不过我们是通过设置环境变量的值覆盖了它)的值*/if (!default_mode)default_mode = &db[0];/*没有设置bpp则默认使用8bpp,本例是32*/  if (!default_bpp)default_bpp = 8;/* Did the user specify a video mode? */if (!mode_option)mode_option = fb_mode_option;/*本例是“LDB-WSVGA”*/  if (mode_option) {const char *name = mode_option;unsigned int namelen = strlen(name);int res_specified = 0, bpp_specified = 0, refresh_specified = 0;unsigned int xres = 0, yres = 0, bpp = default_bpp, refresh = 0;int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;u32 best, diff, tdiff;/*数字格式规则形才会跑下面的循环*/for (i = namelen-1; i >= 0; i--) {switch (name[i]) {/*@后面的是刷新频率*/ case '@':namelen = i;if (!refresh_specified && !bpp_specified &&!yres_specified) {refresh = simple_strtol(&name[i+1], NULL, 10);refresh_specified = 1;if (cvt || rb)cvt = 0;} elsegoto done;break;
/*后面是bpp*/case '-':namelen = i;if (!bpp_specified && !yres_specified) {bpp = simple_strtol(&name[i+1], NULL, 10);bpp_specified = 1;if (cvt || rb)cvt = 0;} elsegoto done;break;
/*获取yres*/  case 'x':if (!yres_specified) {yres = simple_strtol(&name[i+1], NULL, 10);yres_specified = 1;} elsegoto done;break;case '0' ... '9':break;case 'M':if (!yres_specified)cvt = 1;break;case 'R':if (!cvt)rb = 1;break;case 'm':if (!cvt)margins = 1;break;case 'i':if (!cvt)interlace = 1;break;default:goto done;}}
/*如果yres有值,那么也获取xres.*/ if (i < 0 && yres_specified) {xres = simple_strtol(name, NULL, 10);res_specified = 1;}
done:/*不会跑这里*/  if (cvt) {struct fb_videomode cvt_mode;int ret;DPRINTK("CVT mode %dx%d@%dHz%s%s%s\n", xres, yres,(refresh) ? refresh : 60, (rb) ? " reduced blanking" :"", (margins) ? " with margins" : "", (interlace) ?" interlaced" : "");memset(&cvt_mode, 0, sizeof(cvt_mode));cvt_mode.xres = xres;cvt_mode.yres = yres;cvt_mode.refresh = (refresh) ? refresh : 60;if (interlace)cvt_mode.vmode |= FB_VMODE_INTERLACED;elsecvt_mode.vmode &= ~FB_VMODE_INTERLACED;ret = fb_find_mode_cvt(&cvt_mode, margins, rb);if (!ret && !fb_try_mode(var, info, &cvt_mode, bpp)) {DPRINTK("modedb CVT: CVT mode ok\n");return 1;}DPRINTK("CVT mode invalid, getting mode from database\n");}DPRINTK("Trying specified video mode%s %ix%i\n",refresh_specified ? "" : " (ignoring refresh rate)", xres, yres);/*如果刷新率没指定*/ if (!refresh_specified) {/** If the caller has provided a custom mode database and a* valid monspecs structure, we look for the mode with the* highest refresh rate.  Otherwise we play it safe it and* try to find a mode with a refresh rate closest to the* standard 60 Hz.*/if (db != modedb &&info->monspecs.vfmin && info->monspecs.vfmax &&info->monspecs.hfmin && info->monspecs.hfmax &&info->monspecs.dclkmax) {refresh = 1000;} else {/*默认使用60HZ*/  refresh = 60;}}diff = -1;best = -1;/*根据名字或者分辨率来匹配。*/  for (i = 0; i < dbsize; i++) {if ((name_matches(db[i], name, namelen) ||(res_specified && res_matches(db[i], xres, yres))) &&!fb_try_mode(var, info, &db[i], bpp)) {/*刷新率也匹配的时候就认准你了!*/ if (refresh_specified && db[i].refresh == refresh) {return 1;} else {/*刷新率不一样就找差得最少的*/ if (abs(db[i].refresh - refresh) < diff) {diff = abs(db[i].refresh - refresh);best = i;}}}}/*得到刷新率差得最少的db,然后返回*/ if (best != -1) {fb_try_mode(var, info, &db[best], bpp);return (refresh_specified) ? 2 : 1;}/*跑到这里说明名字和分辨率都不匹配。*/ diff = 2 * (xres + yres);best = -1;DPRINTK("Trying best-fit modes\n");/*找到分辨率最小的那组数据。*/  for (i = 0; i < dbsize; i++) {DPRINTK("Trying %ix%i\n", db[i].xres, db[i].yres);if (!fb_try_mode(var, info, &db[i], bpp)) {tdiff = abs(db[i].xres - xres) +abs(db[i].yres - yres);/** Penalize modes with resolutions smaller* than requested.*/if (xres > db[i].xres || yres > db[i].yres)tdiff += xres + yres;/*差值大的会被保留,说白了,最终就是找到分辨率最小的那组参数。*/  if (diff > tdiff) {diff = tdiff;best = i;}}}/*获取best对应的var参数。*/  if (best != -1) {fb_try_mode(var, info, &db[best], bpp);return 5;}}/*运行到这里有两种情况, 1. 字母规则型(如LDB-WXVGA),那就是名字不匹配,并且参数检查失败,。 2. 数字规则型(如1920x1080), 那就是名字不匹配 && 分辨率比ldb_modedb中的小上两倍以上(比如1920x1080 和 320x240)。 */ DPRINTK("Trying default video mode\n");if (!fb_try_mode(var, info, default_mode, default_bpp))return 3;/*默认的还失败那只能随便找一个了。*/ DPRINTK("Trying all modes\n");for (i = 0; i < dbsize; i++)if (!fb_try_mode(var, info, &db[i], default_bpp))return 4;DPRINTK("No valid mode found\n");return 0;
}

本例中fb_try_mode返回的都是0,看代码,这里的作用基本上看成是得到当前对应的db值然后放再var中供后面的framebuffer driver使用。

\drivers\video\modedb.cstatic int fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info,const struct fb_videomode *mode, unsigned int bpp)
{int err = 0;DPRINTK("Trying mode %s %dx%d-%d@%d\n", mode->name ? mode->name : "noname",mode->xres, mode->yres, bpp, mode->refresh);var->xres = mode->xres;var->yres = mode->yres;var->xres_virtual = mode->xres;var->yres_virtual = mode->yres;var->xoffset = 0;var->yoffset = 0;var->bits_per_pixel = bpp;var->activate |= FB_ACTIVATE_TEST;var->pixclock = mode->pixclock;var->left_margin = mode->left_margin;var->right_margin = mode->right_margin;var->upper_margin = mode->upper_margin;var->lower_margin = mode->lower_margin;var->hsync_len = mode->hsync_len;var->vsync_len = mode->vsync_len;var->sync = mode->sync;var->vmode = mode->vmode;if (info->fbops->fb_check_var)err = info->fbops->fb_check_var(var, info);var->activate &= ~FB_ACTIVATE_TEST;return err;
}

drivers\video\mxc\mxc_ipuv3_fb.c中的 mxcfb_check_var函数


static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{u32 vtotal;u32 htotal;struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;if (var->xres == 0 || var->yres == 0)return 0;/* fg should not bigger than bg */if (mxc_fbi->ipu_ch == MEM_FG_SYNC) {struct fb_info *fbi_tmp;int bg_xres = 0, bg_yres = 0;int16_t pos_x, pos_y;bg_xres = var->xres;bg_yres = var->yres;fbi_tmp = found_registered_fb(MEM_BG_SYNC, mxc_fbi->ipu_id);if (fbi_tmp) {bg_xres = fbi_tmp->var.xres;bg_yres = fbi_tmp->var.yres;}ipu_disp_get_window_pos(mxc_fbi->ipu, mxc_fbi->ipu_ch, &pos_x, &pos_y);if ((var->xres + pos_x) > bg_xres)var->xres = bg_xres - pos_x;if ((var->yres + pos_y) > bg_yres)var->yres = bg_yres - pos_y;}if (var->rotate > IPU_ROTATE_VERT_FLIP)var->rotate = IPU_ROTATE_NONE;if (var->xres_virtual < var->xres)var->xres_virtual = var->xres;if (var->yres_virtual < var->yres)var->yres_virtual = var->yres * 3;if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&(var->bits_per_pixel != 16) && (var->bits_per_pixel != 12) &&(var->bits_per_pixel != 8))var->bits_per_pixel = 16;if (check_var_pixfmt(var))/* Fall back to default */bpp_to_var(var->bits_per_pixel, var);if (var->pixclock < 1000) {htotal = var->xres + var->right_margin + var->hsync_len +var->left_margin;vtotal = var->yres + var->lower_margin + var->vsync_len +var->upper_margin;var->pixclock = (vtotal * htotal * 6UL) / 100UL;var->pixclock = KHZ2PICOS(var->pixclock);dev_dbg(info->device,"pixclock set for 60Hz refresh = %u ps\n",var->pixclock);}var->height = -1;var->width = -1;var->grayscale = 0;return 0;
}

想一起探讨以及获得各种学习资源加我(有我博客中写的代码的原稿):
qq:1126137994
微信:liu1126137994
可以共同交流关于嵌入式,操作系统,C++语言,C语言,数据结构等技术问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/423328.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

日常spoken英语学习

今天遇到一个老外&#xff0c;说&#xff1a;can you speak engilsh dou you know coffee in here 我是想了半天&#xff0c;第一有点蒙&#xff0c;第二确实忘了&#xff0c;咖啡馆在哪了&#xff0c;回了一句&#xff1a;Iam think 感觉不知道如何组织语言了&#xff0c;口…

【C++深度剖析教程9】初探C++标准库

在这之前&#xff0c;我写的C程序不能叫做标准的C程序&#xff0c;因为里面写的大多数还带有C语言的影子。今天我们来学习C标准库。 首先看一下例子&#xff1a;操作符<<的原生意义是按位左移。那么我们重载这个操作符&#xff0c;将变量或者常量&#xff0c;左移到一个…

Quartus II常见问题集锦

1、 【问题】Pin Planner 的使用问题&#xff1a;在QuartusII 7.2 &#xff0c;时序仿真都通过&#xff0c;但是&#xff0c;一旦使用Pin Planner设定引脚后&#xff0c;时序仿真就发生变化&#xff0c;与功能仿真结果不一致&#xff0c;不是理想的结果。使用Pin Planner时要注…

员工考核UI网页界面(PS大屏文件资料)

现分享人员管理可视化数据统计网页UI、员工考核数据可视化UI网页界面模版的UI源文件&#xff0c;供UI设计师们快速获取PSD源文件完成工作。 若需更多 大屏组件&#xff0c;请移步小7的另一篇文章&#xff1a;数据可视化大屏组件&#xff0c;大屏PSD设计源文件(大屏UI设计规范)…

【C++深度剖析教程10】C++中的字符串类

首先我们看看C语言&#xff1a; C语言不支持真正意义上的字符串C语言用字符数组和一组函数实现字符串操作语言不支持自定义类型&#xff0c;一次无法获得字符串类型 从C到C中的进化过程中引入了自定义类型 在C中&#xff0c;可以通过类完成字符串类型的定义 C标准库提供了st…

docker 使用中遇到的问题

1.执行docker run hello 命令建立镜像过程中报错 开始以为是加速器的问题&#xff0c;将/etc/docker/daemon.json 文件删除还是不行 后来执行了这两条命令&#xff1a;就不报错了如下图&#xff1a; systemctl daemon-reloadsystemctl restart docker.service 参考&#xff1…

广州技术沙龙第 2、3 期参会者twitter、blog、兴趣大合集

随着组织者越来越有经验&#xff0c;我们在第 2、3 期报名的时候&#xff0c;让参会者填上自己的 twitter、blog和兴趣方向&#xff0c;为的就是让大家方便地找到与自己志同道合的朋友&#xff0c;今天推出第一个合集&#xff0c;祝大家找到新朋友。 注&#xff1a;twitter 账号…

maven 解决冲突

1.Maven之jar包冲突解决&#xff08;理解maven 产生冲突的原因&#xff09; 导致jar包冲突的原因 1、mvn的传递依赖特性&#xff1a;mvn编译打包除了会引入直接申明的依赖&#xff0c;还会引入间接申明的依赖 2、mvn的依赖仲裁规则&#xff1a; 1&#xff09;. 按照项目总控P…

jz2440开发板修改UBOOT支持NAND FLASH

很多天没有看嵌入式的东西了&#xff0c;今天来看一下&#xff0c;继续之前移植uboot到jz2440开发板。今天我们来实现Uboot支持NAND FLASH。 在之前的文章里&#xff08;点击连接查看之前的记录&#xff09;&#xff0c;我们为了编译通过把NAND FLASH 给屏蔽掉了&#xff0c;现…

i.MX6网卡驱动程序fec.c的分析(AR8035网卡驱动程序的详细分析)之一

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 因为做的项目需要用到ethercat主站&#xff0c;而用ethercat主站&#xff0c;标准的网卡网络协议栈性能就无法达到要求&#xff0c;需要根据ethercat官…

TOAD常用快捷键

现在在企业中&#xff0c;操作oracle数据库的客户端&#xff0c;除了PL/SQL外&#xff0c;使用的较多的就是TOAD了&#xff01; 为此&#xff0c;我在网上搜索了下&#xff0c;整理了些简单TOAD的使用技巧&#xff0c;现分享给大家。 常用快捷键&#xff1a; F8 调出以前执行的…

i.MX6网卡驱动程序fec.c的分析(AR8035网卡驱动程序的详细分析)之二

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 今天接着分析上次没有分析完的i.MX6网卡驱动程序。上一篇分析了iMX6网卡驱动程序的driver与device的加载过程&#xff08;点击可以查看上一篇文章&…

文档视图

IntelliFMEA是从IntelliQMS项目的子项目&#xff0c;可单独发布。在IntelliQMS中的APQP插件设计了一个更为完整的项目管理。在IntelliFMEA中的项目管理只是对FMEA有关的文档进行管理的一种方式。IntelliFMEA文档视图的工作方式是:1. 浏览和级联显示IntelliFMEA当前项目的文档清…

阅读ethercat官方文档关于ethercat网卡驱动程序的一些内容

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 改造iMX6&#xff08;fec&#xff09;网卡驱动程序前期工作之&#xff1a;阅读ethercat-1.5.2.pdf文档的第四章内容。 ethercat-1.5.2.pdf文档链接&a…

事务和锁

事务和锁 事务的定义 事务&#xff08;Transaction&#xff09;&#xff0c;一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。 事务…

数据库表的字段中含空格怎么办?

数据库建表&#xff1a; create table t1203 ("ha ha" varchar(100));查询语句&#xff1a;select "ha ha" from t1203;插入数据&#xff1a;insert into t1203("ha ha") values(hello world);其他类似.... 转载于:https://www.cnblogs.com/s…

jz2440开发板移植U-boot之修改代码支持DM9000网卡

今天我们来移植U-boot到jz2440开发板&#xff0c;修改代码支持DM9000网卡。查看之前写的移植记录请点击链接&#xff1a;点击查看之前的移植记录 现在大多数开发板都支持DM9000网卡。我们的U-boot源码里面也是有DM9000网卡的驱动程序的。文件为Dm9000x.c&#xff08;drivers\n…

利用Lombok编写优雅的spring依赖注入代码,去掉繁人的@Autowired

1.引入Lombok 视频教程 https://www.projectlombok.org 2.Lombok jar 下载地址 https://plugins.jetbrains.com/plugin/6317-lombok-plugin 3.大家平时使用spring依赖注入&#xff0c;都是怎么写的&#xff1f; Service public class OrderService { Autowired private Us…

99%与100%

一个表格可以正常的显示&#xff0c;也就是有所有的边框&#xff0c;可是打印的时候却没有右边框。相对来说这个表格比较复杂&#xff0c;首先他有headgroup footgroup也就是在打印的时候分页打印并显示多行表头用的&#xff0c;所以在css上下了一些功夫。可是上边说的问题怎么…

移植U-BOOT之裁剪和修改默认参数(易用性)启动内核,以及对uboot进行分区

今天我们来裁剪U-BOOT&#xff0c;使其更加易用&#xff0c;修改默认参数&#xff0c;以及制作最终修改好得补丁文件方便以后的快速移植。 那么如果想看之前的关于网卡以及flash等的移植&#xff0c;请点击链接查看&#xff1a;点击链接查看 在裁剪修改之前呢&#xff0c;我们…