《Linux驱动》iTop4412开发板LCD驱动 详细分析 (三)
2016-05-20 14:34
477 查看
接下来我们来详解介绍probe中的函数:
在介绍 这个函数之前,我们先来看看wa101是什么
好先看 s3cfb_setup_lcd();的作用
好来看看type如何获得:get_lcd_type 在board文件中
由此可以看出type 由硬件 gpc0(3) gpx0(6) 两个硬件决定,来我们看看原理图:
外围板
这就是拨码开关自适应屏的原理。这里我用的 4.3 小屏 type 为0x2
指定了输出的格式(这里设置输出的是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模式
模式参考数据手册:
来看看格式设置函数:s3cfb_set_output(fbdev); //设置输出格式
2) .s3cfb_set_display_mode(fbdev);//设置模式
函数原型如下:
3) s3cfb_set_polarity(fbdev);//设置引脚极性
源码如下:
主要是对 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—下降沿触发 —-相应位设置成0
hsync—是低脉冲触发—-反转
vsyns —是低脉冲触发—反转
这个函数主要供能是设置各种时序和脉冲宽度。
VIDTCON0
屏幕手册如下:
至此: global init 到此结束:
该函数完成主要设置:
output—–指定输出格式为RGB
display —-指定输出的rgb格式为normal ,且是并行输出
polartiy —-设置触发极性
timing —– 设置时间参数
lcd_size —–设置大小参数
第一个函数: s3cfb_set_lcd_info(fbdev[i]);
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;//设备的bpp int 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 1 int 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) 两个硬件决定,来我们看看原理图:
外围板
这就是拨码开关自适应屏的原理。这里我用的 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模式: //好原理图和芯片手册如下: }
第三个函数
//该函数也是在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 中,函数原型如下: //该函数主要使能lcd_clk fimd_sclk 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"); else mout_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; }
第四个函数
对硬件的初始化,里面主要是对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 ;也就是0 writel(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—下降沿触发 —-相应位设置成0
hsync—是低脉冲触发—-反转
vsyns —是低脉冲触发—反转
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
屏幕手册如下:
VSPW -------------(Vertical pulse width); VFPD -----------------(Vertical front porch) VBPD -------------------------(Vertical back porch) VBPDE ------是yuv才用的着,这里可以不用设置 行的参数类似这里不做介绍。由于该值有一定的范围,一般我们是按推荐typ 设置
这里可以看到,在itop4412提供的源码里跟标准的还有是有一些不同。只要在范围内就好了。
5 。s3cfb_set_lcd_size(fbdev);//设置lcd大小
函数原型如下:int s3cfb_set_lcd_size(struct s3cfb_global *ctrl) { u32 cfg = 0; #ifdef CONFIG_FB_S5P_WA101S cfg |= S3C_VIDTCON2_HOZVAL(ctrl->lcd->width - 1); #else cfg |= S3C_VIDTCON2_HOZVAL(ctrl->lcd->width - 1); #endif cfg |= 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 —–设置大小参数
相关文章推荐
- 从源码安装Mysql/Percona 5.5
- Mootools 1.2教程 函数
- autoit InputBox 函数
- 土人系列AS入门教程 -- 对象篇
- 文件遍历排序函数
- 土人系列AS入门教程--语法篇
- Oracle 函数大全[字符串函数,数学函数,日期函数]第1/4页
- ASP木马后门新思路
- ASP下经常用的字符串等函数参考资料
- PostgreSQL教程(五):函数和操作符详解(1)
- C#中struct和class的区别详解
- DOS批处理 函数定义与用法
- asp Chr 函数 数字转字母的方法
- Lua中的函数精讲笔记
- Lua中的闭合函数、非全局函数与函数的尾调用详解
- Lua中调用C++函数示例
- Lua实现split函数
- Lua常用时间函数使用实例
- Lua函数与字符串处理简明总结
- Lua学习笔记之表和函数