查看: 2727|回复: 1

[资料] 飞凌分享:s3c6410 LCD在uboot下的驱动

[复制链接]
  • TA的每日心情

    2014-4-10 13:56
  • 签到天数: 5 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    发表于 2014-1-24 08:51:03 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 forlinx2013 于 2014-1-24 09:08 编辑

    欢迎大家来到飞凌爱板网专区,对嵌入式技术感兴趣的朋友不妨多多关注一下,我们提供了公司所有开发板的所有资料,也会更新大量技术文章,欢迎大家一块学习提高!!!

    s3c6410 LCD在uboot下的驱动
    今天移植屏驱动涉及到了一些关于uboot的内容,所以就uboot的驱动进行了一些修改,下面我就和大家分享下我的方法。
    uboot下有现成的LCD驱动模板,原来是用于MPC823和PXA250的,我们需要在s3c6410这个ARM11的uboot中增加LCD的驱动,可以在这个基础上修改。具体分以下几步:
    1. 修改配置头文件,是整个工程支持LCD
    在修改include/configs/smdk6410.h 增加一行
    #define CONFIG_LCD
    这行会把common/lcd.c加入编译,同时影响lib_arm/board.c,lib_arm/armlinux.c。在文件cpu/s3c64xx/cpu.c中,还有进入linux内核前的关于lcd控制的准备工作lcd_disable(),lcd_panel_disable()
    2. 修改common/lcd.c
    lcd.c中准备函数calc_fbsize(),这个函数会在armlinux.c中调用,用于计算lcd显示缓冲需要的内存。注意,如果我们使用的现实缓冲比较大,就不能使用这个方法来分配。ubootmalloc函数最大分配的内存,不可能超过uboot使用的总内存512K。如果显示分辨率只有320x240,这个占用内存只有153K,这个方法还是可行的。
    先定义一个LCD的显示控制结构,用于各种函数的计算。
    typedef struct vidinfo {
    ushort vl_col; /*每行点数,如320*/
    ushort vl_row; /*整屏行数,如340 */
    ushort vl_width; /* 虚拟屏宽度,我们简化,不用虚拟屏*/
    ushort vl_height; /* 虚拟屏高度*/
    u_char vl_bpix; /*每点位数,如16*/
    } vidinfo_t;
    我们使用宏结构定义各种参数:
    //1376 //1178
    #define S3CFB_HFP 52//40 /*前沿点数*/
    #define S3CFB_HSW 50//48 /* hsync脉冲宽度 */
    #define S3CFB_HBP 200//240//40 /* 后沿点数 */
    //805 //807
    #define S3CFB_VFP 13//13 /* 前沿行数 */
    #define S3CFB_VSW 1//3 /* vsync脉冲宽度 */
    #define S3CFB_VBP 25//29 /* 后沿行数 */

    #define S3CFB_HRES 1024 /* x方向分辨率*/
    #define S3CFB_VRES 768 /* y方向分辨率*/
    #define S3CFB_VFRAME_FREQ 60 /* 帧频率*/
    #define PIXELBITS 16 /*每点使用位数*/

    #define S3CFB_IVCLK CFG_LOW /*VCLK极性控制*/
    #define S3CFB_IHSYNC CFG_HIGH /*HSYNC极性控制*/
    #define S3CFB_IVSYNC CFG_HIGH /*VSYNC极性控制*/
    #define S3CFB_IVDEN CFG_LOW /*DE 数据有效极性控制*/
    #define S3CFB_PIXEL_CLOCK (S3CFB_VFRAME_FREQ * (S3CFB_HFP + S3CFB_HSW + S3CFB_HBP + S3CFB_HRES) * (S3CFB_VFP + S3CFB_VSW + S3CFB_VBP + S3CFB_VRES))
    我们使用的LCD比较大,我们直接指定显示缓冲区。
    #define LCD_FRAMEBUFFER (TEXT_BASE - 0x300000)
    准备参数结构
    vidinfo_t panel_info = {
    S3CFB_HRES,
    S3CFB_VRES,
    0,
    0,
    PIXELBITS,
    };
    需要在uboot中,通过计算分配显示缓冲,需要加入下面的宏:
    DECLARE_GLOBAL_DATA_PTR;
    现在我们完成calc_fbsize函数
    ulong calc_fbsize (void)
    {
    ulong size;

    int line_length = (panel_info.vl_col * panel_info.vl_bpix) / 8;
    size = line_length * panel_info.vl_row;
    #ifdef LCD_FRAMEBUFFER
    size = 4096; //直接给一个固定值
    #endif
    return size;
    }
    3. 继续修改lcd.c,准备函数lcd_setmem,给board.c调用
    ulong lcd_setmem (ulong addr)
    {

    ulong size;
    int line_length = (panel_info.vl_col * panel_info.vl_bpix) / 8;

    debug ("LCD panel info: %d x %d, %d bit/pix\n",
    panel_info.vl_col, panel_info.vl_row, NBITS (panel_info.vl_bpix) );

    size = line_length * panel_info.vl_row;
    #ifdef LCD_FRAMEBUFFER
    size = 4096; //直接给一个固定值
    #endif
    /* Round up to nearest full page */
    size = (size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);

    /* Allocate pages for the frame buffer. */
    addr -= size;

    debug ("Reserving %ldk for LCD Framebuffer at: %08lx\n", size>>10, addr);

    return (addr);
    }
    4. 为了简化我们的工作,我们使用linux内核中的头文件,来替换我们重新定义寄存器。
    linux2.6.28文件包中,提取arch/arm/plat-s3c/include/plat/regs-lcd.h
    开头部分用下面替换
    #include <common.h>
    #include <regs.h>

    /***************************************************************************/
    /* LCD Registers for S3C2443/2450/S3C6400/6410 */

    #define S3C_LCDREG(x) __REG((x) + ELFIN_LCD_BASE)

    /* LCD control registers */
    #define S3C_VIDCON0 S3C_LCDREG(0x00) /* Video control 0 register */
    #define S3C_VIDCON1 S3C_LCDREG(0x04) /* Video control 1 register */
    增加MIFPCON_REG SPCON_REG寄存器的定义
    #define MIFPCON_REG __REG(0x7410800C)
    #define SEL_BYPASS_MASK 0x00000008
    #define SPCON_REG __REG(ELFIN_GPIO_BASE + SPCON_OFFSET)
    #define LCD_SEL_MASK 0x00000003
    #define RGB_IF_STYLE_MASK 0x00000001
    5. 增加函数lcd_disablelcd_panel_disable用于进入linux内核的准备工作
    void lcd_disable (void)
    {
    S3C_VIDCON0 &= (~(S3C_VIDCON0_ENVID_ENABLE | S3C_VIDCON0_ENVID_F_ENABLE));
    }

    void lcd_Enable (void)
    {
    S3C_VIDCON0 |= (S3C_VIDCON0_ENVID_ENABLE | S3C_VIDCON0_ENVID_F_ENABLE);
    }


    void lcd_panel_disable(void)
    {
    MIFPCON_REG |= SEL_BYPASS_MASK;
    }
    6. 准备驱动函数
    /************************************************************************/
    /* ** GENERIC Initialization Routines */
    /************************************************************************/

    int drv_lcd_init (void)
    {
    device_t lcddev;
    int rc;

    lcd_base = (void *)(gd->fb_base);
    #ifdef LCD_FRAMEBUFFER
    lcd_base = (void*)LCD_FRAMEBUFFER;
    #endif
    lcd_line_length = (panel_info.vl_col * panel_info.vl_bpix) / 8;
    lcd_color_fg = 0xffff;
    lcd_color_bg = 0x0000;

    lcd_init (lcd_base); /* LCD initialization */
    //画线,测试一下显示效果
    lcd_vline(0,0,S3CFB_HRES,0x001f);
    lcd_vline(0,S3CFB_VRES-1,S3CFB_HRES,0x07e0);
    lcd_hline(0,0,S3CFB_VRES,0xf800);
    lcd_hline(S3CFB_HRES-1,0,S3CFB_VRES,0xffff);


    #ifdef CONFIG_NO_VIDEO_CONSOLE
    return 0;
    #endif

    /* Device initialization */
    memset (&lcddev, 0, sizeof (lcddev));

    strcpy (lcddev.name, "lcd");
    lcddev.ext = 0; /* No extensions */
    lcddev.flags = DEV_FLAGS_OUTPUT; /* Output only */
    lcddev.putc = lcd_putc; /* 'putc' function */
    lcddev.puts = lcd_puts; /* 'puts' function */

    rc = device_register (&lcddev); //如果需要uboot的打印信息使用LCD,需要登记设备。

    return (rc == 0) ? 1 : rc;
    }
    7. 准备lcd_init 初始化屏显位置信息,图片等(为简化略过显示图片)
    static int lcd_init (void *lcdbase)
    {
    /* Initialize the lcd controller */
    debug ("[LCD] Initializing LCD frambuffer at %p\n", lcdbase);

    lcd_ctrl_init (lcdbase);

    //lcd_clear (NULL, 1, 1, NULL); /* dummy args */
    //lcd_enable ();

    /* Initialize the console */
    console_col = 0;
    #ifdef CONFIG_LCD_INFO_BELOW_LOGO
    console_row = 7 + BMP_LOGO_HEIGHT / VIDEO_FONT_HEIGHT;
    #else
    console_row = 1; /* leave 1 blank line below logo */
    #endif
    lcd_is_enabled = 1;

    return 0;
    }
    8 . 寄存器相关的初始化
    static int lcd_ctrl_init(void *lcdbase)
    {
    ulong freq_lcdclk;
    ulong freq_Hclk;
    ulong fb_size;
    unsigned char nn;
    unsigned short *pp;
    int i;
    //初始化管脚分配
    GPICON_REG = 0xaaaaaaaa;
    GPIPUD_REG = 0xaaaaaaaa;
    GPJCON_REG = 0xaaaaaaaa;
    GPJPUD_REG = 0xaaaaaaaa;

    lcd_disable();
    S3C_WINCON0 &= (~(S3C_WINCONx_ENWIN_F_ENABLE));

    按照手册初始化寄存器
    // (1)MOFPCON:SEL_BYPASS[3] value@0x7410800C 必须设置为0
    MIFPCON_REG &= (~SEL_BYPASS_MASK);
    //(2)SPCONCD_SEL[1:0]value@0x74F0081A0 选择RGB I/F类型
    SPCON_REG &= (~LCD_SEL_MASK);
    SPCON_REG |= (RGB_IF_STYLE_MASK);
    //(3)VIDCON0:配置视频输出格式
    freq_lcdclk = S3CFB_PIXEL_CLOCK;
    freq_Hclk = get_HCLK();
    nn = (unsigned char)(freq_Hclk / freq_lcdclk) - 1;
    if(freq_lcdclk < freq_Hclk/2)
    {
    S3C_VIDCON0 = S3C_VIDCON0_INTERLACE_F_PROGRESSIVE + S3C_VIDCON0_VIDOUT_RGB_IF + \
    S3C_VIDCON0_PNRMODE_RGB_P + S3C_VIDCON0_CLKVALUP_ST_FRM + S3C_VIDCON0_CLKVAL_F(nn) + \
    S3C_VIDCON0_CLKDIR_DIVIDED + S3C_VIDCON0_CLKSEL_F_HCLK;
    }else
    {
    S3C_VIDCON0 = S3C_VIDCON0_INTERLACE_F_PROGRESSIVE + S3C_VIDCON0_VIDOUT_RGB_IF + \
    S3C_VIDCON0_PNRMODE_RGB_P + S3C_VIDCON0_CLKVALUP_ST_FRM + S3C_VIDCON0_CLKVAL_F(0) + \
    S3C_VIDCON0_CLKDIR_DIRECTED + S3C_VIDCON0_CLKSEL_F_HCLK;
    }
    //(4)VIDCON1:RGB I/F 控制信号
    nn = 0;
    if(S3CFB_IVCLK)
    {
    nn += S3C_VIDCON1_IVCLK_RISE_EDGE;
    }
    if(S3CFB_IHSYNC)
    {
    nn += S3C_VIDCON1_IHSYNC_INVERT;
    }
    if(S3CFB_IVSYNC)
    {
    nn += S3C_VIDCON1_IVSYNC_INVERT;
    }
    if(S3CFB_IVDEN)
    {
    nn += S3C_VIDCON1_IVDEN_INVERT;
    }
    S3C_VIDCON1 = (unsigned int)nn;
    S3C_VIDCON2 = 0;
    //(5)I80IFCONx:i80系统I/F信号控制
    //(6)ITUIFCON0:ITU(BT.601)接口控制
    //(7)VIDTCONx:视频输出时序和显示尺寸
    S3C_VIDTCON0 = S3C_VIDTCON0_VBPD(S3CFB_VBP - 1) | S3C_VIDTCON0_VFPD(S3CFB_VFP - 1) | S3C_VIDTCON0_VSPW(S3CFB_VSW - 1);
    S3C_VIDTCON1 = S3C_VIDTCON1_HBPD(S3CFB_HBP - 1) | S3C_VIDTCON1_HFPD(S3CFB_HFP -1) | S3C_VIDTCON1_HSPW(S3CFB_HSW - 1);
    S3C_VIDTCON2 = S3C_VIDTCON2_LINEVAL(S3CFB_VRES - 1) | S3C_VIDTCON2_HOZVAL(S3CFB_HRES - 1);
    //(8)WINCONx:窗口格式控制,为简化只使用窗口0,不使用双缓冲
    S3C_WINCON0 = S3C_WINCONx_BPPMODE_F_16BPP_565;
    S3C_WINCON1 = 0;
    S3C_WINCON2 = 0;
    S3C_WINCON3 = 0;
    S3C_WINCON4 = 0;

    //(9)VIDOSDxA ,VIDOSDxB:窗口位置控制
    S3C_VIDOSD0A = S3C_VIDOSDxA_OSD_LTX_F(0) + S3C_VIDOSDxA_OSD_LTY_F(0);
    S3C_VIDOSD0B = S3C_VIDOSDxB_OSD_RBX_F(S3CFB_HRES - 1) | S3C_VIDOSDxB_OSD_RBY_F(S3CFB_VRES - 1);
    S3C_VIDOSD0C = S3C_VIDOSD0C_OSDSIZE(S3CFB_HRES*S3CFB_VRES);

    S3C_VIDOSD1A = 0;
    S3C_VIDOSD1B = 0;
    S3C_VIDOSD1C = 0;
    S3C_VIDOSD1D = 0;
    S3C_VIDOSD2A = 0;
    S3C_VIDOSD2B = 0;
    S3C_VIDOSD2C = 0;
    S3C_VIDOSD2D = 0;
    S3C_VIDOSD3A = 0;
    S3C_VIDOSD3B = 0;
    S3C_VIDOSD3C = 0;
    S3C_VIDOSD4A = 0;
    S3C_VIDOSD4B = 0;
    S3C_VIDOSD4C = 0;

    //(10)VIDOSDxC:alpha 值设置

    //(11)VIDWxxADDx:源图像地址设置

    fb_size = calc_fbsize();
    #ifdef LCD_FRAMEBUFFER
    fb_size = (panel_info.vl_col * panel_info.vl_bpix) / 8 * panel_info.vl_row;
    #endif
    // S3C_VIDW00ADD0B0 = (unsigned int)(lcdbase) | S3C_VIDWxxADD0_VBANK_F((unsigned int)lcdbase);
    S3C_VIDW00ADD0B0 = virt_to_phys(lcdbase);
    S3C_VIDW00ADD0B1 = 0;
    S3C_VIDW01ADD0B0 = 0;
    S3C_VIDW01ADD0B1 = 0;
    S3C_VIDW02ADD0 = 0;
    S3C_VIDW03ADD0 = 0;
    S3C_VIDW04ADD0 = 0;

    // S3C_VIDW00ADD1B0 = S3C_VIDWxxADD1_VBASEL_F((unsigned int)(lcdbase) + fb_size);
    S3C_VIDW00ADD1B0 = virt_to_phys((unsigned int)(lcdbase) + fb_size);
    S3C_VIDW00ADD1B1 = 0;
    S3C_VIDW01ADD1B0 = 0;
    S3C_VIDW01ADD1B1 = 0;
    S3C_VIDW02ADD1 = 0;
    S3C_VIDW03ADD1 = 0;
    S3C_VIDW04ADD1 = 0;

    S3C_VIDW00ADD2 = S3C_VIDWxxADD2_OFFSIZE_F(0) | (S3C_VIDWxxADD2_PAGEWIDTH_F(panel_info.vl_col*panel_info.vl_bpix/8));
    S3C_VIDW01ADD2 = 0;
    S3C_VIDW02ADD2 = 0;
    S3C_VIDW03ADD2 = 0;
    S3C_VIDW04ADD2 = 0;
    //(12)WxKEYCONx:色键寄存器
    //(13)WINxMAP:窗口颜色控制
    //(14)WPALCON:调色板控制
    //(15)WxPDATAxx:调色板数据
    #if 1
    memset(lcdbase,0x55,fb_size);//增加一个底色
    #else
    pp = lcdbase;
    for(i=0;i< S3CFB_HRES * S3CFB_VRES;i++)
    {
    *pp = 0xf100;
    pp++;
    }
    #endif
    lcd_Enable();
    S3C_WINCON0 |= S3C_WINCONx_ENWIN_F_ENABLE;
    return (0);
    }
    这样屏上应该有显示了,下面仔细根据屏的类型调整宏机构就可以了。(完)


    回复

    使用道具 举报

  • TA的每日心情

    2014-4-10 13:56
  • 签到天数: 5 天

    连续签到: 1 天

    [LV.2]偶尔看看I

     楼主| 发表于 2014-2-11 17:05:04 | 显示全部楼层
    怎么没有好评呢
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-11-22 05:37 , Processed in 0.123068 second(s), 17 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.