• linux驱动移植LCD驱动案例


    由于我使用的Mini2440开发板采用的LCD为TFT屏,型号为LCD-T35(TD035STEB4)。这一节,我们将参考s3c2410fb.c编写LCD驱动程序。

    一、LCD驱动编写基础函数

    1.1 dma_alloc_wc

    该函数定义在include/linux/dma-mapping.h:

    static inline void *dma_alloc_wc(struct device *dev, size_t size,
                                     dma_addr_t *dma_addr, gfp_t gfp)

    该函数用于申请一段DMA缓冲区,分配出来的内存会禁止cache缓存(因为DMA传输不需要CPU)。

    返回值为:申请到的DMA缓冲区的虚拟地址,若为NULL,表示分配失败,则需要使用dma_free_wc释放内存,避免内存泄漏。

    参数如下: 

    • dev:设备指针;
    • size:分配的地址大小(字节单位);
    • dma_addr:申请到的物理起始地址;
    • gfp:分配出来的内存参数,标志定义在<linux/gfp.h>,常用标志如下:
      • GFP_ATOMIC 用来从中断处理和进程上下文之外的其他代码中分配内存. 从不睡眠;
      • GFP_KERNEL 内核内存的正常分配. 可能睡眠;
      • GFP_USER 用来为用户空间页来分配内存; 它可能睡眠.;

    1.2 dma_free_wc

    static inline void dma_free_wc(struct device *dev, size_t size,
                                   void *cpu_addr, dma_addr_t dma_addr)

    该函数用于释放DMA缓冲区,参数和dma_alloc_wc一样。

    1.3 framebuffer_alloc

    struct fb_info *framebuffer_alloc(size_t size, struct device *dev)

    动态申请一个fb_info结构体,参数如下:

    • size:额外的内存大小;
    • dev:设备指针,用于初始化fb_info->device成员;

    二、LCD驱动编写步骤

    2.1 入口函数

    在驱动入口函数中实现:

    (1) 动态分配fb_info结构体;

    (2) 设置fb_info

    (2.1).设置固定的参数fb_info->fix;

    (2.2) 设置可变的参数fb_info->var;

    (2.3) 设置操作函数fb_info->fbops;

    (2.4) 设置fb_info其它的成员;

    (3) 设置硬件相关的操作

    (3.1) 配置LCD引脚:设置GPIO端口C和GPIO端口D用于LCD;

    (3.2) 根据LCD手册设置LCD控制器时序参数;

    (3.3) 分配显存(framebuffer),把显存地址告诉LCD控制器和fb_info;

    (4) 开启LCD,并注册fb_info

    (4.1) 开启LCD

      • 控制LCDCON5允许PWREN信号,开启背光;
      • 控制LCDCON1使能LCD;

    (4.2) 注册fb_info

    2.2 出口函数

    在驱动出口函数中实现:

    (1) 卸载内核中的fb_info

    (2) 控制LCDCON1关闭PWREN信号,关背光,iounmap注销地址

    (3) 释放DMA缓存地址dma_free_wc

    (4) 释放注册的fb_info

    三、LCD驱动编写 

    首先在/work./sambashare/drivers创建项目文件夹11.lcd_dev,然后我们在11.lcd_dev下创建lcd_dev.c文件。

    3.1 分配一个fb_info结构

       /* 1. 初始化fb_info */
       s3c_lcd = framebuffer_alloc(0,0);
           if (!s3c_lcd)
               return -ENOMEM;

    3.2 设置fb_info(设置固定的参数fb_info->fix)

    首先设置fb_info->fix,成员变量fix类型为struct fb_fix_screeninfo,用于保存LCD屏幕的相关参数,定义在include/uapi/linux/fb.h中:

    struct fb_fix_screeninfo {
            char id[16];                    /* identification string eg "TT Builtin" */
            unsigned long smem_start;       /* Start of frame buffer mem */
                                            /* (physical address) */
            __u32 smem_len;                 /* Length of frame buffer mem */
            __u32 type;                     /* see FB_TYPE_*                */
            __u32 type_aux;                 /* Interleave for interleaved Planes */
            __u32 visual;                   /* see FB_VISUAL_*              */
            __u16 xpanstep;                 /* zero if no hardware panning  */
            __u16 ypanstep;                 /* zero if no hardware panning  */
            __u16 ywrapstep;                /* zero if no hardware ywrap    */
            __u32 line_length;              /* length of a line in bytes    */
            unsigned long mmio_start;       /* Start of Memory Mapped I/O   */
                                            /* (physical address) */
            __u32 mmio_len;                 /* Length of Memory Mapped I/O  */
            __u32 accel;                    /* Indicate to driver which     */
                                            /*  specific chip/card we have  */
            __u16 capabilities;             /* see FB_CAP_*                 */
            __u16 reserved[2];              /* Reserved for future compatibility */
    };

    其中参数含义如下:

    • id:唯一标识符;
    • smem_start:framebuffer缓冲区物理起始位置(一般是显示控制器DMA起始地址);
    • smem_len:framebuffer缓冲区的的长度,单位为字节;
    • type:lcd类型,默认FB_TYPE_PACKED_PIXELS即可,可选参数如下;
    #define FB_TYPE_PACKED_PIXELS           0       /* Packed Pixels        */
    #define FB_TYPE_PLANES                  1       /* Non interleaved planes */
    #define FB_TYPE_INTERLEAVED_PLANES      2       /* Interleaved planes   */
    #define FB_TYPE_TEXT                    3       /* Text/attributes      */
    #define FB_TYPE_VGA_PLANES              4       /* EGA/VGA planes       */
    #define FB_TYPE_FOURCC                  5       /* Type identified by a V4L2 FOURCC */
    • type_aux:附加类型,默认0即可;
    • visual:颜色设置,常用参数如下:
    #define FB_VISUAL_MONO01                0       /* Monochr. 1=Black 0=White  单侧 0白色 1黑色 */
    #define FB_VISUAL_MONO10                1       /* Monochr. 1=White 0=Black  单色 0黑色 1白色 */
    #define FB_VISUAL_TRUECOLOR             2       /* True color   真彩 */
    #define FB_VISUAL_PSEUDOCOLOR           3       /* Pseudo color (like atari) 伪彩 */
    #define FB_VISUAL_DIRECTCOLOR           4       /* Direct color 直彩 */
    #define FB_VISUAL_STATIC_PSEUDOCOLOR    5       /* Pseudo color readonly 只读伪彩 */
    #define FB_VISUAL_FOURCC                6       /* Visual identified by a V4L2 FOURCC */
    • xpanstep:如果没有硬件panning赋值0;
    • ypanstep:如果没有硬件panning赋值0;
    • ywrapstep:若果没有硬件ywarp赋值0;
    • line_length:一行所占的字节数,例:(RGB565)240*320,那么这里就等于240*16/8;
    • mmio_start:内存映射IO的起始地址,用于应用层直接访问寄存器,可以不需要;
    • mmio_len:内存映射IO的长度,可以不需要;
    • accel:指明使用的芯片,用于硬件加速,默认FB_ACCEL_NONE即可,可选参数如下;:
    #define FB_ACCEL_NONE           0       /* no hardware accelerator      */
    #define FB_ACCEL_ATARIBLITT     1       /* Atari Blitter                */
    #define FB_ACCEL_AMIGABLITT     2       /* Amiga Blitter                */
    #define FB_ACCEL_S3_TRIO64      3       /* Cybervision64 (S3 Trio64)    */
    #define FB_ACCEL_NCR_77C32BLT   4       /* RetinaZ3 (NCR 77C32BLT)      */
    ......
    • capabilities:查看FB_CAP_;
    • reserved:为将来的兼容保留位;

    所以:

      /* 2. 设置fb_info */
       /* 2.1设置固定参数 */
           strcpy(s3c_lcd->fix.id, "mylcd");
        s3c_lcd->fix.smem_len        = LCD_WIDTH * LCD_HEIGHT * 2;   // framebuffer缓冲区的大小 每个像素两个字节
           s3c_lcd->fix.type            = FB_TYPE_PACKED_PIXELS;
        s3c_lcd->fix.type_aux        = 0;
        s3c_lcd->fix.xpanstep        = 0;
        s3c_lcd->fix.ypanstep        = 0;
        s3c_lcd->fix.ywrapstep       = 0;
        s3c_lcd->fix.accel           = FB_ACCEL_NONE;
        s3c_lcd->fix.visual          = FB_VISUAL_TRUECOLOR;      //真彩色
        s3c_lcd->fix.line_length     = LCD_WIDTH * 2;             // LCD一行所占字节数

    3.3 设置fb_info(设置可变的参数fb_info->var)

    然后设置fb_info->var,成员变量var类型为struct fb_var_screeninfo,用于保存LCD屏幕的相关参数,定义在include/uapi/linux/fb.h中:

    struct fb_var_screeninfo {
            __u32 xres;                     /* visible resolution           */
            __u32 yres;
            __u32 xres_virtual;             /* virtual resolution           */
            __u32 yres_virtual;
            __u32 xoffset;                  /* offset from virtual to visible */
            __u32 yoffset;                  /* resolution                   */
    
            __u32 bits_per_pixel;           /* guess what                   */
            __u32 grayscale;                /* 0 = color, 1 = grayscale,    */
                                            /* >1 = FOURCC                  */
            struct fb_bitfield red;         /* bitfield in fb mem if true color, */
            struct fb_bitfield green;       /* else only length is significant */
            struct fb_bitfield blue;
            struct fb_bitfield transp;      /* transparency                 */
    
            __u32 nonstd;                   /* != 0 Non standard pixel format */
    
            __u32 activate;                 /* see FB_ACTIVATE_*            */
    
            __u32 height;                   /* height of picture in mm    */
            __u32 width;                    /* width of picture in mm     */
    
            __u32 accel_flags;              /* (OBSOLETE) see fb_info.flags */
    
            /* Timing: All values in pixclocks, except pixclock (of course) */
            __u32 pixclock;                 /* pixel clock in ps (pico seconds) */
            __u32 left_margin;              /* time from sync to picture    */
            __u32 right_margin;             /* time from picture to sync    */
            __u32 upper_margin;             /* time from sync to picture    */
            __u32 lower_margin;
            __u32 hsync_len;                /* length of horizontal sync    */
            __u32 vsync_len;                /* length of vertical sync      */
            __u32 sync;                     /* see FB_SYNC_*                */
            __u32 vmode;                    /* see FB_VMODE_*               */
            __u32 rotate;                   /* angle we rotate counter clockwise */
            __u32 colorspace;               /* colorspace for FOURCC-based modes */
            __u32 reserved[4];              /* Reserved for future compatibility */
    };

    其中参数含义如下:

    • xres:行分辨率
    • yres:列分辨率
    • xres_virtual:行虚拟分辨率,设置和硬件一样即可;
    • yres_virtual:列虚拟分辨率,设置和硬件一样即可;
    • xoffset:行偏移,设置为0即可;
    • yoffset:列偏移,设置为0即可;
    • bits_per_pixel:每个像素用多少位,对于s3c2440不支持18位,只支持16位;
    • grayscale:灰度值,默认即可;
    • red:RGB:565对于R的offset为从最低位(右起)偏移11位,占5bit;
    • green:RGB:565对于G的offset为从最低位(右起)偏移5位,占6bit;
    • blue:RGB:565对于B的offset为从最低位(右起)偏移0位,占5bit;
    • transp:透明度,默认即可;
    • nonstd:0标准像素格式,默认即可;
    • activate:默认即可;
    • height:LCD物理高度,单位mm;
    • width:LCD物理宽度,单位mm;
    • accel_flags:默认即可,过时参数;
    • pixclock:像素时钟,单位皮秒;
    • left_margin:行切换,从同步到绘图之间的延迟;
    • right_margin:行切换,从绘图到同步之间的延迟;
    • upper_margin:帧切换,从同步到绘图之间的延迟;
    • lower_margin:帧切换,从绘图到同步之间的延迟;
    • hsync_len:水平同步的长度;
    • vsync_len:垂直同步的长度;
    • sync:参考FB_SYNC_*;
    • vmode:参考FB_BMODE_*,默认即可;
    • rotate::时针旋转的角度;
    • colorspace:色彩空间;
    • reserved:保留值;

     所以:

        /* 2.2 设置可变参数 */
         s3c_lcd->var.xres            = LCD_WIDTH;         // 行分辨率
         s3c_lcd->var.yres            = LCD_HEIGHT;        // 列分辨率
         s3c_lcd->var.xres_virtual    = LCD_WIDTH;         // 行虚拟分辨率
         s3c_lcd->var.yres_virtual    = LCD_HEIGHT;        // 列虚拟分辨率
         s3c_lcd->var.xoffset         = 0;                 //虚拟到可见屏幕之间的行偏移
         s3c_lcd->var.yoffset         = 0;                 //虚拟到可见屏幕之间的行偏移
         s3c_lcd->var.bits_per_pixel  = 16;                // 每像素使用位数
    
         /* RGB:565 */
         s3c_lcd->var.red.offset      = 11;
         s3c_lcd->var.red.length      = 5;
         s3c_lcd->var.green.offset    = 5;
         s3c_lcd->var.green.length    = 6;
         s3c_lcd->var.blue.offset     = 0;
         s3c_lcd->var.blue.length     = 5;
    
         s3c_lcd->var.nonstd          = 0;
         s3c_lcd->var.activate        = FB_ACTIVATE_NOW;   // 使设置的值立即生效
         s3c_lcd->var.accel_flags     = 0;
         s3c_lcd->var.vmode           = FB_VMODE_NONINTERLACED;

    3.4 设置fb_info(设置操作函数fb_info->fbops)

    /* 调色板数组,被fb_info->pseudo_palette调用 */
    static u32 pseudo_palette[16];
    
    /*
     * 将内核单色使用bf表示
     * @param chan:单色 内核中的单色都是16位
     * @param bf:颜色位信息
     */
    static inline unsigned int chan_to_field(unsigned int chan,
                         struct fb_bitfield *bf)
    {
        chan &= 0xffff;
        chan >>= 16 - bf->length;
        return chan << bf->offset;
    }
    
    /*
     * 调色板操作函数
     *  @param regno: 调色板数组元素索引
     */
    static int s3c2440fb_setcolreg(unsigned regno,
                       unsigned red, unsigned green, unsigned blue,
                       unsigned transp, struct fb_info *info)
    {
        unsigned int val;
        //调色板数组不能大于16
        if (regno > 16)
             return 1;
    
        /* 小于16位,进行转换 */
        u32 *pal = info->pseudo_palette;
    
        /* 用red,green,blue三个颜色值构造出16色数据val */
        val  = chan_to_field(red,   &info->var.red);
        val |= chan_to_field(green, &info->var.green);
        val |= chan_to_field(blue,  &info->var.blue);
    
        /* 放到调色板数组中 */
        pseudo_palette[regno] = val;
        return 0;
    }
    
    
    /*
     * fb_info操作函数fbops
     */
    static struct fb_ops s3c2440fb_ops = {
        .owner        = THIS_MODULE,
        .fb_setcolreg    = s3c2440fb_setcolreg,   // 调色板操作函数 设置调色板fb_info-> pseudo_palette
        .fb_fillrect    = cfb_fillrect,          // 填充矩形  函数在drivers/video/fbdev/core/cfbfillrect.c中定义
        .fb_copyarea    = cfb_copyarea,          // 复制数据  函数在drivers/video/fbdev/core/cfbcopyarea.c中定义
        .fb_imageblit    = cfb_imageblit,         // 绘制图形  函数在drivers/video/fbdev/core/cfbimgblt.c中定义
    };

    3.5 设置fb_info其它的成员

         /* 2.3 设置操作函数 */
         s3c_lcd->fbops               = &s3c2440fb_ops;
    
         /* 2.4 其他设置 */
         s3c_lcd->flags               = FBINFO_FLAG_DEFAULT;
         s3c_lcd->pseudo_palette      = pseudo_palette;             // 保存调色板数组
         s3c_lcd->screen_size         = LCD_WIDTH * LCD_HEIGHT * 2;    // framebuffer缓冲区的大小

    3.6 设置硬件相关的操作(配置GPIO用于LCD)

    参考之前介绍的Mini2440裸机开发之LCD编程(GB2312、ASCII字库制作)初始化GPIO,主要是GPCCON、GPDCON寄存器:

        /* 3.硬件相关操作 */
        /* 3.1 配置GPIO口用于LCD */
        gpcup = ioremap(0x56000028,4);
        gpccon = ioremap(0x56000020,4);
        gpdup = ioremap(0x56000038,4);
        gpdcon = ioremap(0x56000030,4);
        gpgcon = ioremap(0x56000060,12);
        gpgdat = gpgcon + 1;
        gpgup = gpgdat + 1;
    
        // GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND
        *gpcup = 0xffffffff;
        *gpccon = 0xaaaaaaaa;
        // GPIO管脚用于VD[23:8]
        *gpdup = 0xffffffff;
        *gpdcon = 0xaaaaaaaa;
    
        /* Pull - up disable */
        *gpgup |= (1 << 4);
        // 设置GPG4引脚为LCD_PWREN模式
        *gpgcon |= (3 << 8);

    3.7 设置硬件相关的操作(根据LCD手册设置LCD控制器时许参数)

    /* 3.2 根据LCD手册设置LCD控制器, 比如VCLK的频率等 */
        lcd_regs = ioremap(0x4D000000, sizeof(struct lcd_regs));
        printk("lcd_regs=%px map_size %u\n", lcd_regs, sizeof(struct lcd_regs));
    
        /*  [17:8]  CLKVAL
         *  [6:5]   PNRMODE;选择显示模式
         *                  00 = 4 位双扫描显示模式   01 = 4 位单扫描显示模式(STN)
         *                  10 = 8 位单扫描显示模式   11 = TFT LCD 面板
         *  [4:1]  BPPMODE  选择BPP(位每像素)模式    1100 = TFT 的16 BPP
         *  [0]    ENVID    LCD 视频输出和逻辑使能/禁止。
         *                  0 = 禁止视频输出和LCD 控制信号  1 = 允许视频输出和LCD 控制信号
         */
        lcd_regs->lcdcon1   = (CLKVAL<<8)| (3<<5) | (0xC<<1);              /* 16 bpp for TFT */
    
        /*  [31:24]   VBPD:帧同步信号的后肩
         *  [23:14]   LINEVAL:LCD面板的垂直尺寸
         *  [13:6]    VFPD:帧同步信号的前肩
         *  [5:0]     VSPW:同步信号的脉宽
         */
        lcd_regs->lcdcon2 = (VBPD<<24)|(LINEVAL<<14)|(VFPD<<6)|(VSPW);
    
        /* [25:19] HBPD: 行同步信号的后肩
         * [18:8] HOZVAL: LCD面板的水平尺寸
         * [7:0] HFPD: 行同步信号的前肩
         */
        lcd_regs->lcdcon3 = (HBPD<<19)|(HOZVAL<<8)|(HFPD);
        lcd_regs->lcdcon4 = (HSPW);
    
        /* [11] FRM565: 此位选择16 BPP 输出视频数据的格式   0 = 5:5:5:1 格式   1= 5:6:5 格式
         * [10] STN/TFT: 此位控制VCLK 有效沿的极性
         * [9]    INVVLINE: STN/TFT:此位表明VLINE/HSYNC 脉冲极性  0 = 正常 1 = 反转
         * [8]  INVVFRAME: STN/TFT:此位表明VFRAME/VSYNC 脉冲极性 0 = 正常 1 = 反转
         * VLINE/HSYNC 脉冲极性、VFRAME/VSYNC 脉冲极性反转(LCD型号决定)
         * [0]    HWSWP: STN/TFT:半字节交换控制位   0 = 交换禁止 1 = 交换使能
         */
        lcd_regs->lcdcon5 = ((1<<11) | (1<<10) | (1 << 9) | (1 << 8) | (1 << 0));
    
         /*  关闭PWREN信号输出 */
         lcd_regs->lcdcon1 &= ~(1<<0);
         /* 禁止PWREN信号 */
         lcd_regs->lcdcon5  &=~(1<<3);
    
         /* 第一位设置为1 选择输出分片率类型0:320 * 240  1:240*320 */
         lcd_regs->lpcsel = ((0xCE6) & ~7) | 1<<1;

    3.8 设置硬件相关的操作(分配显存)

        /* 3.3 分配显存(framebuffer), 并把地址告诉LCD控制器 */
        ret = s3c2440fb_map_video_memory(s3c_lcd);
        if (ret) {
            printk("failed to allocate video RAM: %d\n", ret);
            // todo 这里应该进行资源释放,我这里就不释放了
            return -ENOMEM;
        }
    
        /* [29:21] LCDBANK:存放帧缓冲起始地址的[30:22]
         * [20:0] LCDBASEU: 存放帧缓冲起始地址的[21:1]
         */
        lcd_regs->lcdsaddr1 = (s3c_lcd->fix.smem_start >> 1) & ~(3<<30);
    
        /* 存放帧结束地址[21:1] */
        lcd_regs->lcdsaddr2 = ((s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len) >> 1) & 0x1fffff;
    
        /* [21:11] OFFSIZE:表示虚拟屏偏移尺寸  即上一行最后像素点到下一行第一个像素点之间间隔多少个像素点
        *  [10:0] PAGEWIDTH:表示行的宽度(半字为单位 16bit)
        */
        lcd_regs->lcdsaddr3 = LCD_WIDTH & 0x3ff;

    其中s3c2440fb_map_video_memory:

    #define samsung_device_dma_mask (*((u64[]) { DMA_BIT_MASK(32) }))
    
    /*platform设备 */
    static struct platform_device s3c_device_lcd = {
            .name           = "s3c2440-lcd",
            .id             = -1,
            .num_resources  = 0,
            .dev            = {
                    .dma_mask               = &samsung_device_dma_mask,
                    .coherent_dma_mask      = DMA_BIT_MASK(32),
            }
    };
    
    /*
     * s3c2440fb_map_video_memory():
     *      Allocates the DRAM memory for the frame buffer.  This buffer is
     *      remapped into a non-cached, non-buffered, memory region to
     *      allow palette and pixel writes to occur without flushing the
     *      cache.  Once this area is remapped, all virtual memory
     *      access to the video memory should occur at the new region.
     */
    static int s3c2440fb_map_video_memory(struct fb_info *fbinfo)
    {
            dma_addr_t map_dma;
            unsigned long map_size = PAGE_ALIGN(fbinfo->fix.smem_len);
    
            printk("map_video_memory(info=%px) map_size %u\n", fbinfo, map_size);
    
            // 第一个参数不能为空 否则会抛异常
            fbinfo->screen_base = dma_alloc_wc(&s3c_device_lcd.dev, map_size, &map_dma,
                                             GFP_KERNEL);
    
            if (fbinfo->screen_base) {
                    /* prevent initial garbage on screen */
                    printk("map_video_memory: clear %px:%08x\n",
                            fbinfo->screen_base, map_size);
                    memset(fbinfo->screen_base, 0x00, map_size);
    
                    fbinfo->fix.smem_start = map_dma;
    
                    printk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",
                            fbinfo->fix.smem_start, fbinfo->screen_base, map_size);
            }
            else
            {
              printk("map_video_memory fail\n");
            }
    
            return fbinfo->screen_base ? 0 : -ENOMEM;
    }

    3.9 开启LCD

        /* 4.1 开启LCD */
        /* 控制LCDCON5允许PWREN信号 */
        lcd_regs->lcdcon5 |= (1 << 3);
        /* 控制LCDCON1 LCD使能 */
        lcd_regs->lcdcon1 |= (1<<0);
        /* 输出高电平, 使能背光 */
        *gpgdat |= 1<<4;
    
        printk("lcdcon[1] = 0x%08lx\n", lcd_regs->lcdcon1);
        printk("lcdcon[2] = 0x%08lx\n", lcd_regs->lcdcon2);
        printk("lcdcon[3] = 0x%08lx\n", lcd_regs->lcdcon3);
        printk("lcdcon[4] = 0x%08lx\n", lcd_regs->lcdcon4);
        printk("lcdcon[5] = 0x%08lx\n", lcd_regs->lcdcon5);
        printk("lcdsaddr[1]= 0x%08lx\n", lcd_regs->lcdsaddr1);
        printk("lcdsaddr[2]= 0x%08lx\n", lcd_regs->lcdsaddr2);
        printk("lcdsaddr[3]= 0x%08lx\n", lcd_regs->lcdsaddr3);

    3.10 注册

        /* 4.2 注册 */
        register_framebuffer(s3c_lcd);

    四、测试

    4.1 配置内核

    我们切换到linux内核目录下,

    cd /wqoroot@zhengyang:~# cd /work/sambashare/linux-5.2.8/

    在linux内核根目录下执行,生成默认配置文件.config:

    make distclean
    make s3c2440_defconfig # 这个是之前我之前配置的

    进行内核配置:

    root@zhengyang:/work/sambashare/linux-5.2.8# make menuconfig

    配置步骤如下:

     Device Drivers  --->

    • Graphics support  --->
      • Frame buffer Device  --->
        • <Y>Support for frame buffer devices
        •  <M> S3C2410 LCD framebuffer support
        •  <M> Samsung S3C framebuffer support

    需要注意的是:

    Support for frame buffer devices:这个设置成Y就行,如果将这个编译进内核,同时会将cfbcopyarea.c、cfbfillrect.c、cfbimgblt.c、font_8x16(lib/fonts路径下)编译进内核,这样就不用单独安装这些了

    具体参考drivers/video/fbdev/Kconfig:

    config FB_CMDLINE
            bool
    
    config FB_NOTIFY
            bool
    
    menuconfig FB
            tristate "Support for frame buffer devices"
            select FB_CMDLINE
            select FB_NOTIFY
            ---help---
              The frame buffer device provides an abstraction for the graphics
              hardware. It represents the frame buffer of some video hardware and
              allows application software to access the graphics hardware through
              a well-defined interface, so the software doesn't need to know
              anything about the low-level (hardware register) stuff.
    
              Frame buffer devices work identically across the different
              architectures supported by Linux and make the implementation of
              application programs easier and more portable; at this point, an X
              server exists which uses the frame buffer device exclusively.
              On several non-X86 architectures, the frame buffer device is the
              only way to use the graphics hardware.
    
              The device is accessed through special device nodes, usually located
              in the /dev directory, i.e. /dev/fb*.
    
             ...
    
    config FB_CFB_FILLRECT
            tristate
            depends on FB
            ---help---
              Include the cfb_fillrect function for generic software rectangle
              filling. This is used by drivers that don't provide their own
              (accelerated) version.
    
    config FB_CFB_COPYAREA
            tristate
            depends on FB
            ---help---
              Include the cfb_copyarea function for generic software area copying.
              This is used by drivers that don't provide their own (accelerated)
              version.
    
    config FB_CFB_IMAGEBLIT
            tristate
            depends on FB
            ---help---
              Include the cfb_imageblit function for generic software image
              blitting. This is used by drivers that don't provide their own
              (accelerated) version.

    修改完配置后,保存文件,输入文件名s3c2440_defconfig,在当前路径下生成s3c2440_defconfig:存档:

    mv s3c2440_defconfig ./arch/arm/configs/

    此时重新执行:

    make s3c2440_defconfig

    查看.config文件可以看到:

    4.2 编译内核和模块

    编译内核:

    make V=1 uImage

    将uImage复制到tftp服务器路径下:

     cp /work/sambashare/linux-5.2.8/arch/arm/boot/uImage /work/tftpboot/

    4.3 烧录内核

    开发板uboot启动完成后,内核启动前,按下任意键,进入uboot,可以通过print查看uboot中已经设置的环境变量。

    设置开发板ip地址,从而可以使用网络服务:

    SMDK2440 # set ipaddr 192.168.0.105
    SMDK2440 # save
    Saving Environment to NAND...
    Erasing NAND...
    
    Erasing at 0x40000 -- 100% complete.
    Writing to NAND... OK
    SMDK2440 # ping 192.168.0.200
    dm9000 i/o: 0x20000000, id: 0x90000a46 
    DM9000: running in 16 bit mode
    MAC: 08:00:3e:26:0a:5b
    operating at unknown: 0 mode
    Using dm9000 device
    host 192.168.0.200 is alive

    设置tftp服务器地址,也就是我们ubuntu服务器地址:

    set serverip 192.168.0.200
    save

    下载内核到内存,并写NAND FLASH:

    tftp 30000000 uImage
    nand erase.part kernel
    nand write 30000000 kernel

    运行结果如下:

    SMDK2440 # tftp 30000000 uImage
    dm9000 i/o: 0x20000000, id: 0x90000a46 
    DM9000: running in 16 bit mode
    MAC: 08:00:3e:26:0a:5b
    operating at unknown: 0 mode
    Using dm9000 device
    TFTP from server 192.168.0.200; our IP address is 192.168.0.188
    Filename 'uImage'.
    Load address: 0x30000000
    Loading: *#################################################################
         #################################################################
         #################################################################
         ##############################################################
         429.7 KiB/s
    done
    Bytes transferred = 3766128 (397770 hex)
    SMDK2440 # nand erase.part kernel
    
    NAND erase.part: device 0 offset 0x60000, size 0x400000
    
    Erasing at 0x60000 --   3% complete.
    ...
    OK
    SMDK2440 # nand write 30000000 kernel
    
    NAND write: device 0 offset 0x60000, size 0x400000
     4194304 bytes written: OK

    4.4 编译LCD驱动

    在11.lcd_dev路径下编译:

    root@zhengyang:/work/sambashare/drivers/11.lcd_dev# cd /work/sambashare/drivers/11.lcd_dev
    root@zhengyang:/work/sambashare/drivers/11.lcd_dev# make

    拷贝驱动文件到nfs文件系统:

    root@zhengyang:/work/sambashare/drivers/11.lcd_dev# cp /work/sambashare/drivers/11.lcd_dev/lcd_dev.ko /work/nfs_root/rootfs/ 

    4.5 安装驱动

    重启开发板,加载lcd驱动,执行如下命令:

    insmod lcd_dev.ko

    加载之后我们发现输出的LCD寄存器信息均是0x00,这里很奇怪,说明我们并没有成功修改LCD寄存器的值,这个问题排查了很久也没有找到原因。

    挂载LCD驱动后, 如下图,可以通过如下命令查看已挂载的LCD设备节点::

    ls -l /dev/fb*  

    五、编译LCD驱动到内核

    5.1 LCD驱动编译进内核

    我们直接将lcd_dev.ko源码拷贝到linux源码下:
    cp lcd_dev.c /work/sambashare/linux-5.2.8/drivers/video/fbdev/

    5.2 修改Kconfig

    cd /work/sambashare/linux-5.2.8/drivers/video/fbdev
    vim Kconfig

    新增如下内容:

    config MY_LCD
        tristate "my_lcd_dev"
        depends on FB && ARCH_S3C2410
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
            default n
            help
              my_lcd driver    

    在源码顶层运行make menuconfig,在Device Drivers -> Graphics support -> Frame buffer Devices可以看到:

    勾选这,并保存。

    5.3 修改Makefile

    cd /work/sambashare/linux-5.2.8/drivers/video/fbdev
    vim Makefile

    新增如下内容:

    obj-$(CONFIG_MY_LCD)             += lcd_dev.o

    5.4 编译内核

    在源码顶层运行:

    make uImage

    复制到nfs文件系统:

     cp /work/sambashare/linux-5.2.8/arch/arm/boot/uImage /work/tftpboot/

    5.5 测试运行

    重新下载内核到开发板:

    tftp 30000000 uImage
    nand erase.part kernel
    nand write 30000000 kernel
    bootm

    启动输出寄存器信息:

    lcd device registered
    s3c_lcd=c39a0c00
    lcd_regs=c48a4000 map_size 104
    map_video_memory(info=c39a0c00) map_size 155648
    map_video_memory: clear c48a6000:00026000
    map_video_memory: dma=339c0000 cpu=(ptrval) size=00026000
    lcdcon[1] = 0x00000779
    lcdcon[2] = 0x014fc041
    lcdcon[3] = 0x0098ef09
    lcdcon[4] = 0x00000009
    lcdcon[5] = 0x00004f09
    lcdsaddr[1]= 0x19ce0000
    lcdsaddr[2]= 0x000f2c00
    lcdsaddr[3]= 0x000000f0

    可以在LCD上看到企鹅logo(向上一节一样配置了Bootup logo)。但是过一会屏幕就什么都没了,针对该问题,目前问题还未排查到原因。

    六、代码下载

    Young / s3c2440_project[drivers]

    参考文章

    [1]16.Linux-LCD驱动(详解)

    [2]十二、Linux驱动之LCD驱动

    [3]S3C2440之LCD驱动

  • 相关阅读:
    ubuntu防火墙设置通过某端口
    pandas入门
    pyplot入门
    numpy教程
    跨域请求 spring boot
    spring boot 启动流程
    代理配置访问
    AOP,拦截器
    spring boot 启动不连接数据库
    Python 3.x 连接数据库(pymysql 方式)
  • 原文地址:https://www.cnblogs.com/zyly/p/16154120.html
Copyright © 2020-2023  润新知