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

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

第一个函数: s3cfb_set_lcd_info(fbdev[i]);

上一节我们是这样说的
//这个函数是获取具体设备的参数的,跟s3cfb_wa101s.c有关
// 在这个函数中我们看到如何通过硬件拨码开关选择lcd硬件
//讲在下面介绍

1.该函数源码如下:

/*该函数在s3cfb_wa101s.c 中*/
/* name should be fixed as 's3cfb_set_lcd_info' */
void s3cfb_set_lcd_info(struct s3cfb_global *ctrl)
{s3cfb_setup_lcd(); //由硬件选择设备,初始化相应参数wa101.init_ldi = NULL;ctrl->lcd = &wa101; //让全局结构体指向该设备
}

在介绍 这个函数之前,我们先来看看wa101是什么

#include "s3cfb.h"
static struct s3cfb_lcd wa101 = {
#if 0//smdk
//  .width  = 1366,.width  = 1360,.height = 768,.bpp    = 24,.freq   = 60,.timing = {.h_fp   = 48,.h_bp   = 80,。。。。。。。。。。
}
//该结构体如下:
struct s3cfb_lcd {int   width;//设备宽int   height;//设备高int   bpp;//设备的bppint   freq;//刷新頻度struct    s3cfb_lcd_timing timing;  //与硬件时序参数struct    s3cfb_lcd_polarity polarity;void  (*init_ldi)(void);void  (*deinit_ldi)(void);};//由此可以看出wa101 就是一个描述lcd硬件设备的结构体。

好先看 s3cfb_setup_lcd();的作用

void s3cfb_setup_lcd()
{
#if 1int type = get_lcd_type();  //获得type来选择什么样的硬件初始化//printk("************** type = %d\n", type);if(0x0 == type)        //9.7{wa101.width = 1024;wa101.height = 768;wa101.bpp       = 24;。。。。。。。。}else if(0x1 == type)   //7.0{。。。。。。。。        }else if(0x2 == type)   //4.3{wa101.width = 480;wa101.height = 272;wa101.bpp       = 24;wa101.freq = 60;。。。。。。。。。。。。。。。}。。。。。。。。。       #endif
}   

好来看看type如何获得:get_lcd_type 在board文件中

int get_lcd_type()
{int value1, value2, type = 0;int flags = 0;value1 = gpio_get_value(EXYNOS4_GPC0(3));value2 = gpio_get_value(EXYNOS4_GPX0(6));type = (value1<<1)|value2;printk("value1 = %d, value2 = %d, type = 0x%x\n", value1, value2, type);return type;
}
EXPORT_SYMBOL(get_lcd_type);

由此可以看出type 由硬件 gpc0(3) gpx0(6) 两个硬件决定,来我们看看原理图:
外围板

核心板 gpx0_6

核心板 gpc0_3

这就是拨码开关自适应屏的原理。这里我用的 4.3 小屏 type 为0x2
第二个函数: pdata->cfg_gpio(pdev);
/* platform_data*/
pdata = to_fb_plat(&pdev->dev);
if (pdata->cfg_gpio)
pdata->cfg_gpio(pdev); /初始化io

//初始化io的函数,在device  s3cfb_set_platdata函数中指定的s3cfb_get_clk_name(npd->clk_name); //获取时钟npd->cfg_gpio = s3cfb_cfg_gpio; //获取引脚操作函数//以上是device描述的//该函数在setup-fb-s5p.c 中void s3cfb_cfg_gpio(struct platform_device *pdev)
{   。。。。。s3cfb_gpio_setup_24bpp(EXYNOS4_GPF0(0), 8, S3C_GPIO_SFN(2), S5P_GPIO_DRVSTR_LV4);s3cfb_gpio_setup_24bpp(EXYNOS4_GPF1(0), 8, S3C_GPIO_SFN(2), S5P_GPIO_DRVSTR_LV4);s3cfb_gpio_setup_24bpp(EXYNOS4_GPF2(0), 8, S3C_GPIO_SFN(2), S5P_GPIO_DRVSTR_LV4);s3cfb_gpio_setup_24bpp(EXYNOS4_GPF3(0), 4, S3C_GPIO_SFN(2), S5P_GPIO_DRVSTR_LV4);。。。。。。。。。。。。。。
}   

设置引脚的函数主要是把GFP0(0-7) GPF1(0-7) GPF2(0-7) GPF3(0-3) 设置成lcd模式:
好原理图和芯片手册如下:


第三个函数: pdata->cfg_gpio(pdev);

 //该函数也是在device中3cfb_get_clk_name(npd->clk_name); //获取时钟npd->lcd_off = s3cfb_lcd_off;//关闭lcd 设备npd->clk_on = s3cfb_clk_on; // 时钟开//时钟的名字有第一节就知道了:sclk_fimd  if (pdata->clk_on)pdata->clk_on(pdev, &fbdev[i]->clock);
 //s3cfb_clk_on  函数在setup-fb-s5p.c 中,函数原型如下:
int s3cfb_clk_on(struct platform_device *pdev, struct clk **s3cfb_clk)
{struct clk *sclk = NULL;struct clk *mout_mpll = NULL;struct clk *lcd_clk = NULL;u32 rate = 0;int ret = 0;lcd_clk = clk_get(&pdev->dev, "lcd");if (IS_ERR(lcd_clk)) {dev_err(&pdev->dev, "failed to get operation clk for fimd\n");goto err_clk0;}ret = clk_enable(lcd_clk);if (ret < 0) {dev_err(&pdev->dev, "failed to clk_enable of lcd clk for fimd\n");goto err_clk0;}clk_put(lcd_clk);sclk = clk_get(&pdev->dev, "sclk_fimd");if (IS_ERR(sclk)) {dev_err(&pdev->dev, "failed to get sclk for fimd\n");goto err_clk1;}if (soc_is_exynos4210())mout_mpll = clk_get(&pdev->dev, "mout_mpll");elsemout_mpll = clk_get(&pdev->dev, "mout_mpll_user");if (IS_ERR(mout_mpll)) {dev_err(&pdev->dev, "failed to get mout_mpll for fimd\n");goto err_clk2;}ret = clk_set_parent(sclk, mout_mpll);if (ret < 0) {dev_err(&pdev->dev, "failed to clk_set_parent for fimd\n");goto err_clk2;}ret = clk_set_rate(sclk, 800000000);if (ret < 0) {dev_err(&pdev->dev, "failed to clk_set_rate of sclk for fimd\n");goto err_clk2;}dev_dbg(&pdev->dev, "set fimd sclk rate to %d\n", rate);clk_put(mout_mpll);ret = clk_enable(sclk);if (ret < 0) {dev_err(&pdev->dev, "failed to clk_enable of sclk for fimd\n");goto err_clk2;}*s3cfb_clk = sclk;return 0;err_clk2:clk_put(mout_mpll);
err_clk1:clk_put(sclk);
err_clk0:clk_put(lcd_clk);return -EINVAL;
}
//由上可知,该函数主要使能lcd_clk  fimd_sclk

第四个函数: pdata->cfg_gpio(pdev);
/故名思议这是对硬件的初始化,里面主要是对exynos4412的寄存器设置所有会调到 s3cfb_fimd6x.c 中的寄存器操作,将在下面详细介绍

int s3cfb_init_global(struct s3cfb_global *fbdev)
{fbdev->output = OUTPUT_RGB; //指定了输出格式fbdev->rgb_mode = MODE_RGB_P;//指定了rgb模式fbdev->wq_count = 0; //等待队列技术清零init_waitqueue_head(&fbdev->wq);//初始化等到队列mutex_init(&fbdev->lock);//初始化锁s3cfb_set_output(fbdev); //设置输出格式s3cfb_set_display_mode(fbdev);//设置模式s3cfb_set_polarity(fbdev);//设置引脚极性s3cfb_set_timing(fbdev);//设置时序s3cfb_set_lcd_size(fbdev);//设置lcd大小return 0;
}

指定了输出的格式(这里设置输出的是RGB数据),并未写入寄存器( 此功能由s3cfb_set_output来完成 ),支持格式如下:
enum s3cfb_output_t {
OUTPUT_RGB,
OUTPUT_ITU,
OUTPUT_I80LDI0,
OUTPUT_I80LDI1,
OUTPUT_WB_RGB,
OUTPUT_WB_I80LDI0,
OUTPUT_WB_I80LDI1,
};
输出格式请参考如下手册:Exynos4412 User Manual (Public) version 1.0

rgb模式
enum s3cfb_rgb_mode_t {
MODE_RGB_P = 0,
MODE_BGR_P = 1,
MODE_RGB_S = 2,
MODE_BGR_S = 3,
};
模式参考数据手册:

来看看格式设置函数:s3cfb_set_output(fbdev); //设置输出格式

int s3cfb_set_output(struct s3cfb_global *ctrl)
{u32 cfg;cfg = readl(ctrl->regs + S3C_VIDCON0);cfg &= ~S3C_VIDCON0_VIDOUT_MASK; //清楚vidcon0寄存器26-28 位数据--也就是上面手册上的//VOUT                              if (ctrl->output == OUTPUT_RGB)cfg |= S3C_VIDCON0_VIDOUT_RGB; //我们的选择 //宏定义如下#define S3C_VIDCON0_VIDOUT_RGB           (0 << 26)else if (ctrl->output == OUTPUT_ITU)cfg |= S3C_VIDCON0_VIDOUT_ITU;else if (ctrl->output == OUTPUT_I80LDI0)cfg |= S3C_VIDCON0_VIDOUT_I80LDI0;else if (ctrl->output == OUTPUT_I80LDI1)cfg |= S3C_VIDCON0_VIDOUT_I80LDI1;else if (ctrl->output == OUTPUT_WB_RGB)cfg |= S3C_VIDCON0_VIDOUT_WB_RGB;else if (ctrl->output == OUTPUT_WB_I80LDI0)cfg |= S3C_VIDCON0_VIDOUT_WB_I80LDI0;else if (ctrl->output == OUTPUT_WB_I80LDI1)cfg |= S3C_VIDCON0_VIDOUT_WB_I80LDI1;else {dev_err(ctrl->dev, "invalid output type: %d\n", ctrl->output);return -EINVAL;}writel(cfg, ctrl->regs + S3C_VIDCON0);//写入的exynos VIDCON0 寄存器cfg = readl(ctrl->regs + S3C_VIDCON2);cfg &= ~(S3C_VIDCON2_WB_MASK | S3C_VIDCON2_TVFORMATSEL_MASK | \S3C_VIDCON2_TVFORMATSEL_YUV_MASK); //清楚掩码if (ctrl->output == OUTPUT_RGB)cfg |= S3C_VIDCON2_WB_DISABLE; //#define S3C_VIDCON2_WB_DISABLE            (0 << 15)//在这里是兼容接口,在这里无用else if (ctrl->output == OUTPUT_ITU)cfg |= S3C_VIDCON2_WB_DISABLE;else if (ctrl->output == OUTPUT_I80LDI0)cfg |= S3C_VIDCON2_WB_DISABLE;else if (ctrl->output == OUTPUT_I80LDI1)cfg |= S3C_VIDCON2_WB_DISABLE;else if (ctrl->output == OUTPUT_WB_RGB)cfg |= (S3C_VIDCON2_WB_ENABLE | S3C_VIDCON2_TVFORMATSEL_SW | \S3C_VIDCON2_TVFORMATSEL_YUV444);else if (ctrl->output == OUTPUT_WB_I80LDI0)cfg |= (S3C_VIDCON2_WB_ENABLE | S3C_VIDCON2_TVFORMATSEL_SW | \S3C_VIDCON2_TVFORMATSEL_YUV444);else if (ctrl->output == OUTPUT_WB_I80LDI1)cfg |= (S3C_VIDCON2_WB_ENABLE | S3C_VIDCON2_TVFORMATSEL_SW | \S3C_VIDCON2_TVFORMATSEL_YUV444);else {dev_err(ctrl->dev, "invalid output type: %d\n", ctrl->output);return -EINVAL;}writel(cfg, ctrl->regs + S3C_VIDCON2);return 0;
}

2 .s3cfb_set_display_mode(fbdev);//设置模式
函数原型如下:

int s3cfb_set_display_mode(struct s3cfb_global *ctrl)
{u32 cfg;cfg = readl(ctrl->regs + S3C_VIDCON0);cfg &= ~S3C_VIDCON0_PNRMODE_MASK; //对VIDCON0 的17,18 清空,可以看到上的寄存器图cfg |= (ctrl->rgb_mode << S3C_VIDCON0_PNRMODE_SHIFT);//前面fbdev->rgb_mode = MODE_RGB_P ;也就是0writel(cfg, ctrl->regs + S3C_VIDCON0);//所以把VIDCON0 的17 ,18 为都设置成了0,也就rgb并行口,正常模式return 0;
}

3 s3cfb_set_polarity(fbdev);//设置引脚极性
源码如下:

int s3cfb_set_polarity(struct s3cfb_global *ctrl)
{struct s3cfb_lcd_polarity *pol;u32 cfg;pol = &ctrl->lcd->polarity;cfg = 0;/* Set VCLK hold scheme */cfg &= S3C_VIDCON1_FIXVCLK_MASK;cfg |= S3C_VIDCON1_FIXVCLK_VCLK_RUN;if (pol->rise_vclk)cfg |= S3C_VIDCON1_IVCLK_RISING_EDGE;if (pol->inv_hsync)cfg |= S3C_VIDCON1_IHSYNC_INVERT;if (pol->inv_vsync)cfg |= S3C_VIDCON1_IVSYNC_INVERT;if (pol->inv_vden)cfg |= S3C_VIDCON1_IVDEN_INVERT;writel(cfg, ctrl->regs + S3C_VIDCON1);return 0;
}

主要是对 vden vsynsc hsync vclk 使能信号,垂直同步信号,行同步信号,vclk触发方式。
要知道这些功能,首先了解 lcd 显示原理,和这几个信号的做用。
要设置的话 必须根据原理图 和两个数据手册:exynos4412 和 lcd控制芯片
Exynos4412 User Manual (Public) version 1.0
WXCAT43-TG6#001_V1.0
exynos 关联位 4-vden 5-vsync 6-hsync 7-vclk

在wa101中 vden vsynsc hsync vclk 都是0 ,这些数据的由来,得分别看exynos 和lcd 的工作时序图:
exynos4412

exynos中 DE 是高电平触发,clk 触发方式(需要指定),hsync 是高脉冲触发 ,vsynsc 是高脉冲触发

WXCAT43:

两个都看了,lcd 硬件中 DE 是高电平触发,clk 是下降沿触发,hsync 是低脉冲触发 ,vsynsc 是低脉冲触发

综合:要驱动lcd设备:exynos 要设置成lcd触发有效的相应模式de----高电平触发--不反转clk---下降沿触发 ----相应位设置成0hsync---是低脉冲触发----反转vsyns ---是低脉冲触发---反转

再看数据和代码:
“`

由此代码 发现vclk 和vden 都是正确的,而hsync 和vsync 并没有反转 (这是神马?????)
(经测试把 wa101.polarity.rise_vclk = 0;//1,
wa101.polarity.inv_hsync = 1;
wa101.polarity.inv_vsync = 1;//1,
wa101.polarity.inv_vden = 0;//0,
设置成这样,设备依然能正常运行,我也是醉了)

4 s3cfb_set_timing(fbdev);//设置时序
代码原型如下:

int s3cfb_set_timing(struct s3cfb_global *ctrl)
{struct s3cfb_lcd_timing *time;u32 cfg;time = &ctrl->lcd->timing;cfg = 0;cfg |= S3C_VIDTCON0_VBPDE(time->v_bpe - 1);cfg |= S3C_VIDTCON0_VBPD(time->v_bp - 1);cfg |= S3C_VIDTCON0_VFPD(time->v_fp - 1);cfg |= S3C_VIDTCON0_VSPW(time->v_sw - 1);writel(cfg, ctrl->regs + S3C_VIDTCON0);cfg = 0;cfg |= S3C_VIDTCON1_VFPDE(time->v_fpe - 1);cfg |= S3C_VIDTCON1_HBPD(time->h_bp - 1);cfg |= S3C_VIDTCON1_HFPD(time->h_fp - 1);cfg |= S3C_VIDTCON1_HSPW(time->h_sw - 1);writel(cfg, ctrl->regs + S3C_VIDTCON1);return 0;
}

这个函数主要供能是设置各种时序和脉冲宽度。
VIDTCON0
VIDTCON1 数据手册如下:

本来这应该看exynos 和WXCAT43 分别对应求得的。
而这里跟经验,小小的偷了一下懒 (此方法一般有效,特殊情况除外)

                    VSPW            -----------------(Vertical pulse width);VFPD            -----------------(Vertical front porch)VBPD            -----------------(Vertical back porch)VBPDE           -----------------是yuv才用的着,这里可以不用设置行的参数类似这里不做介绍。由于该值有一定的范围,一般我们是按推荐typ  设置

这里可以看到,在itop提供的源码里跟标准的还有是有一些不同。只要在范围内就好了。

5、s3cfb_set_lcd_size(fbdev);//设置lcd大小

int s3cfb_set_lcd_size(struct s3cfb_global *ctrl)
{u32 cfg = 0;#ifdef CONFIG_FB_S5P_WA101Scfg |= S3C_VIDTCON2_HOZVAL(ctrl->lcd->width - 1);
#elsecfg |= S3C_VIDTCON2_HOZVAL(ctrl->lcd->width - 1);
#endifcfg |= S3C_VIDTCON2_LINEVAL(ctrl->lcd->height - 1);writel(cfg, ctrl->regs + S3C_VIDTCON2);return 0;
}

宽和高的设置:

至此: global init 到此结束:

该函数完成,回顾一下主要设置:
output—–指定输出格式为RGB
display —-指定输出的rgb格式为normal ,且是并行输出
polartiy —-设置触发极性
timing —– 设置时间参数
lcd_size —–设置大小参数

好现在又可以回到probe函数中了,由于内容过长,剩下的函数将在下一节中介绍,
未完待续。。。。。

itop4412 LCD设备驱动详解(三)之PROBE相关推荐

  1. itop4412 LCD设备驱动详解(四)之PROBE再深入

    LCD的工作,在kernel中有device和driver两个描述,这也是必然 在第二节中我们详解介绍了 s3cfb_main.c ---probe函数的框架. 回顾一下probe函数的作用: 1. ...

  2. Linux LCD设备驱动详解

    本文是基于mini2440开发板Linux版本号是linux-2.6.32.2的学习笔记 一. LCD device硬件信息 1.LCD控制器的寄存器地址从 0X4D000000开始 2.lcd de ...

  3. Linux字符设备驱动详解七(“插件“设备树实现RGB灯驱动)

    文章目录 系列文章目录 前言 正文 Device Tree Overlays:"插件"设备树 传统设备树 "插件"设备树 使用前提 案例说明 设备树:foo.d ...

  4. Linux字符设备驱动详解四(使用自属的xbus驱动总线)

    文章目录 系列文章目录 前言 驱动目录 正文 驱动总线 总线管理 总线注册 设备注册 驱动注册 代码示例 总结 系列文章目录 Linux字符设备驱动详解 Linux字符设备驱动详解二(使用设备驱动模型 ...

  5. Linux usb设备驱动详解

    1.Linux usb设备驱动框架 USB是通用串行总线的总称,Linux内核几乎支持所有的usb设备,包括键盘,鼠标,打印机,modem,扫描仪.Linux的usb驱动分为主机驱动与gadget驱动 ...

  6. Linux设备驱动之usb设备驱动详解

    原文地址:http://blog.csdn.net/chenjin_zhong/article/details/6329316 1.Linux usb设备驱动框架 USB是通用串行总线的总称,Linu ...

  7. 【转】草根老师的 linux字符设备驱动详解

    Linux 驱动 之 模块化编程 Linux 驱动之模块参数和符号导出 Linux 设备驱动之字符设备(一) Linux 设备驱动之字符设备(二) Linux 设备驱动之字符设备(三) 转载于:htt ...

  8. EtherCAT设备协议详解三、EtherCAT CoE

    CoE 是 CANopen on EtherCAT, 在ethercat报文中封装CANopen协议 服务数据对象(SDO)用来访问CANopen对象字典条目的 关于canopen怎么封装到ether ...

  9. 设备、设备节点和设备驱动详解

    Linux下的设备通常分为三类,字符设备,块设备和网络设备. 设备驱动程序也分为对应的三类:字符设备驱动程序.块设备驱动程序和网络设备驱动程序. 常见的字符设备有鼠标.键盘.串口.控制台等. 常见的块 ...

最新文章

  1. python bs4 find_all_BeautifulSoup中的find,find_all
  2. Swift3.0语言教程使用占位符格式创建和初始化字符串
  3. Java中实现多线程的两种方式之间的区别
  4. 在画图软件中,可以画出不同大小或颜色的圆形、矩形等几何图形。几何图形之间有许多共同的特征,如它们可以是用某种颜色画出来的,可以是填充的或者不填充的。此外还有些不同的特征,比如,圆形都有半径,可以根据半
  5. java高级框架应用开发案例教程_Java高级框架应用开发案例教程:struts2+spring+hibernate PDF...
  6. IDEA(2018版)实用快捷键整理
  7. elasticsearch批量数据导入和导出
  8. android恢复微信好友,安卓微信删除好友怎么找回 找回好友详细方法
  9. Android的Gallery3D模块介绍
  10. 机器学习面试必知:学生t分布的神奇之处
  11. 计算机专业工艺流程简述,CNC加工中心编程的工艺流程,新手必读! ! !
  12. 高效添加origin配色盘
  13. python爬大学生就业数据分析_Python 网络爬虫数据分析实战
  14. Hdl_localization全套安装运行问题总结
  15. github 发布静态页面
  16. “舒淇半停工原因”上热搜:人生下半场,拼的是健康
  17. JS(javascript)在自动化测试项目中的应用【软件测试开发入门教程】
  18. 21个数据科学家面试必须知道的问题和答案
  19. MyBatis在Spring中的事务管理
  20. 洛谷P4316 绿豆蛙的归宿

热门文章

  1. Android手机充电器通用吗,安卓手机充电器可以混用么?
  2. netty Recycler(三)WeakOrderQueue unreachable
  3. DLP RGB结构光投影光源 IMSTL_M20_V2
  4. 厦门工学院计算机管理,厦门工学院
  5. 前端与服务器通讯的数据交换格式XML 、JSON
  6. 判断浏览器是手机端还是pc端 以及判断安卓还是iOS
  7. 知微传感Dkam系列3D相机SDK例程篇:配置相机曝光
  8. 普中51单片机-步进电机启停、变速控制级速度显示
  9. #普中51开发板# 52单片机数码管显示5201314
  10. 锤子便签APP 产品使用分析报告