《Linux驱动》iTop4412开发板LCD驱动 详细分析 (三)

接下来我们来详解介绍probe中的函数:

第一个函数: 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 —–设置大小参数

时间: 2024-10-25 08:57:55

《Linux驱动》iTop4412开发板LCD驱动 详细分析 (三)的相关文章

[转载]嵌入式开发板-iTOP-4412开发板LCD的屏幕驱动

平台:iTOP-4412开发板 大家好今天我们来讲一下 iTOP-4412 开发板 LCD 的屏幕驱动, iTOP-4412 开发板支持 4.3 寸, 7 寸, 9.7寸的 lcd 显示屏.其中 4.3 寸屏是用的 cpu 直接出来的 RGB 信号,7 寸屏和 9.7 寸屏是用的 LVDS 信号,硬件 上 使 用 了 一 个 RGB 转 LVDS 的 芯 片 实 现 的 . 我 们 来 看 下 显 示 驱 动 , 显 示 驱 动 在 内 核 的"drivers/video/samsung&quo

[迅为开发板资料分享]iTOP-4412开发板LCD的屏幕驱动

大家好今天我们来讲一下 iTOP-4412 开发板 LCD 的屏幕驱动, iTOP-4412 开发板支持 4.3 寸, 7 寸, 9.7寸的 lcd 显示屏.其中 4.3 寸屏是用的 cpu 直接出来的 RGB 信号,7 寸屏和 9.7 寸屏是用的 LVDS 信号,硬件 上 使 用 了 一 个 RGB 转 LVDS 的 芯 片 实 现 的 . 我 们 来 看 下 显 示 驱 动 , 显 示 驱 动 在 内 核 的"drivers/video/samsung" 目录下面, 这个驱动是三星

学习嵌入式Linux-选择iTOP-4412开发板

部分视频观看地址: [视频教程]iTOP-4412开发板之学习方法--致初学者 http://v.youku.com/v_show/id_XNzQ5MDA4NzM2.html [视频教程]三星Exynos 4412处理器开发要点 http://v.youku.com/v_show/id_XNjIwODA0MTY4.html [视频教程]iTOP-4412开发板之如何扩展不同型号LCD屏 http://v.youku.com/v_show/id_XNjM4NjY2NDQ4.html [视频教程]i

学习嵌入式linux开发板之iTOP-4412 开发板如何操作GPIO

本文转自迅为论坛:http://www.topeetboard.com 平台:迅为嵌入式linux开发板 Exynos4412 所有的 GPIO 都有固定的地址,为了方便操作这些 GPIO,Linux 内核 在 gpio-exynos4.h 里面定义了一些 GPIO 的宏,例如: #define EXYNOS4_GPA0(_nr)  (EXYNOS4_GPIO_A0_START + (_nr)) #define EXYNOS4_GPA1(_nr)  (EXYNOS4_GPIO_A1_START 

Beaglebone Black开发板安装驱动

Beaglebone Black开发板安装驱动 Beaglebone Black开发板安装驱动,在使用Beaglebone Black开发板子做任何事情之前首先需要安装驱动.下面的内容就了展示在Windows.Mac OS X以及Linux操作系统下Beaglebone Black开发板驱动的安装方式.使用USB连接的Beaglebone Black在电脑中显示为一个存储设备,如图1.2所示.   图1.2  Beaglebone Black作为可移动存储设备 这其中包括了你要开始使用Beagl

迅为iTOP-4412开发板Linux系统下如何配置wifi模块

精英版在 linux 下使用 wifi,需要进行下面的配置: 首先用户在拿到光盘资料以后查看下光盘里面的"linux"->"root_xxxxxxxx.tar.gz"文件,其中的 "xxxxxxxx" 代表linux系统的版本日期, 迅为提供的支持linux下wifi的系统版本是 "root_20150422.tar.gz" , 也就是 2015-04-22 以后的版本(包括 20150422)是支持 wifi 模块的.

iTOP-4412开发板-嵌入式平台开机测试

iTOP-4412平台硬件的连接1.核心板和底板的连接 迅为iTOP-4412开发板采用工业级进口板对板连接器,拔插方便稳定可靠,如下图所示. 另外迅为独家提供两种封装的核心板,接口定义完全兼容,如下图所示. iTOP-4412开发平台,核心板和底板可分离.拆分核心板的时候,使用塑料的薄片在核心和底板任意一边轻轻撬动,听到清脆的响声,表明撬动的一边的连接器已经分离,接着依次分离其它三边. 安装核心板的时候,核心板和底板的箭头要指向同一方向,将核心板和底板对齐,然后按压核心板,听到四次清脆的响声,

iTOP-4412开发板网盘资料介绍

iTOP-4412开发板网盘视频资料内容如下: 01-烧写.编译以及基础知识视频 02-嵌入式Linux 视频 03-iTOP-4412 开发板硬件设计指导视频 04-Android 应用程序视频 05-Android 应用教程2015 06-裸机程序实验文档以及工具文件 07-Linux-x86-Qt5.3.2 以及QtE4.7.1 应用视频 08-Linux 驱动教程 09-迅为电子Java 教程 10-uboot教程 01-烧写.编译以及基础知识视频: 02-嵌入式Linux 视频: 03

【分享】iTOP4412开发板-Bluetooth移植文档

最近须要把Bluetooth移植到iTOP-4412 开发平台.查阅了相关资料,经过一段时间的研究.调试,最终成功的将蓝牙功能移植到了开发板上面.这里笔者记录移植过程及注意事项,方便以后工作须要. iTOP-4412开发板的Bletooth模块与板卡之间的连接採用UART接口.Bletooth硬件模块使用的是MTK的MT6620芯片,MTK提供了Android4.0及Android4.4的driver, Porting Guid,有了这些就为我们的移植工作做了整体性的指导. 可是唯独MTK提供的