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文件。首先我們引入頭文件,並定義一些常亮和全局變量等:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>

/*  LCD T35參數設定 */
#define     LCD_WIDTH   240             /* LCD面板的行寬 */
#define     LCD_HEIGHT  320             /* LCD面板的列寬 */

#define     VSPW        1                /* 通過計算無效行數垂直同步脈沖寬度決定VSYNC脈沖的高電平寬度 */
#define     VBPD        1                /* 垂直同步周期后的無效行數 */
#define     LINEVAL     (LCD_HEIGHT-1)  /* LCD的垂直寬度-1 */
#define     VFPD        1                /* 垂直同步周期前的的無效行數 */

#define     CLKVAL      7              /* VCLK = HCLK / [(CLKVAL  + 1)  × 2] */

#define     HSPW        9                /* 通過計算VCLK的數水平同步脈沖寬度決定HSYNC脈沖的高電平寬度 */
#define     HBPD        19               /* 描述水平后沿為HSYNC的下降沿與有效數據的開始之間的VCLK周期數 */
#define     HOZVAL      (LCD_WIDTH-1)    /* LCD的水平寬度-1 */
#define     HFPD        9                /* 水平后沿為有效數據的結束與HSYNC的上升沿之間的VCLK周期數 */

/* 定義fb_info */
static struct fb_info *s3c_lcd;
/* 調色板數組,被fb_info->pseudo_palette調用 */
static u32 pseudo_palette[16];
/* 時鍾 */
static struct clk *lcd_clk;

/* GPIO相關寄存器 */
static volatile unsigned long *gpcup;
static volatile unsigned long *gpccon;
static volatile unsigned long *gpdup;
static volatile unsigned long *gpdcon;
static volatile unsigned long *gpgcon;
static volatile unsigned long *gpgdat;
static volatile unsigned long *gpgup;

/* lcd相關寄存器 */
struct lcd_regs{
    unsigned long   lcdcon1;
    unsigned long    lcdcon2;
    unsigned long    lcdcon3;
    unsigned long    lcdcon4;
    unsigned long    lcdcon5;
    unsigned long    lcdsaddr1;
    unsigned long    lcdsaddr2;
    unsigned long    lcdsaddr3;
    unsigned long    redlut;
    unsigned long    greenlut;
    unsigned long    bluelut;
    unsigned long    reserved[9];
    unsigned long    dithmode;
    unsigned long    tpal;
    unsigned long    lcdintpnd;
    unsigned long    lcdsrcpnd;
    unsigned long    lcdintmsk;
    unsigned long    lpcsel;
};

static volatile struct lcd_regs* lcd_regs;

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緩沖區的大小

    // 時鍾相關,獲取lcd時鍾
    lcd_clk = clk_get(NULL, "lcd");
    if (IS_ERR(lcd_clk)) {
        printk("failed to get lcd clock source\n");
        return PTR_ERR(lcd_clk);
    }

    clk_prepare_enable(lcd_clk);       // 時鍾使能
    printk("got and enabled clock\n");

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);

3.11 lcd_dev.c完整代碼

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>

/*  LCD T35參數設定 */
#define     LCD_WIDTH   240             /* LCD面板的行寬 */
#define     LCD_HEIGHT  320             /* LCD面板的列寬 */

#define     VSPW        1                /* 通過計算無效行數垂直同步脈沖寬度決定VSYNC脈沖的高電平寬度 */
#define     VBPD        1                /* 垂直同步周期后的無效行數 */
#define     LINEVAL     (LCD_HEIGHT-1)  /* LCD的垂直寬度-1 */
#define     VFPD        1                /* 垂直同步周期前的的無效行數 */

#define     CLKVAL      7              /* VCLK = HCLK / [(CLKVAL  + 1)  × 2] */

#define     HSPW        9                /* 通過計算VCLK的數水平同步脈沖寬度決定HSYNC脈沖的高電平寬度 */
#define     HBPD        19               /* 描述水平后沿為HSYNC的下降沿與有效數據的開始之間的VCLK周期數 */
#define     HOZVAL      (LCD_WIDTH-1)    /* LCD的水平寬度-1 */
#define     HFPD        9                /* 水平后沿為有效數據的結束與HSYNC的上升沿之間的VCLK周期數 */

/* 定義fb_info */
static struct fb_info *s3c_lcd;
/* 調色板數組,被fb_info->pseudo_palette調用 */
static u32 pseudo_palette[16];
/* 時鍾 */
static struct clk *lcd_clk;

/* GPIO相關寄存器 */
static volatile unsigned long *gpcup;
static volatile unsigned long *gpccon;
static volatile unsigned long *gpdup;
static volatile unsigned long *gpdcon;
static volatile unsigned long *gpgcon;
static volatile unsigned long *gpgdat;
static volatile unsigned long *gpgup;

/* lcd相關寄存器 */
struct lcd_regs{
    unsigned long   lcdcon1;
    unsigned long    lcdcon2;
    unsigned long    lcdcon3;
    unsigned long    lcdcon4;
    unsigned long    lcdcon5;
    unsigned long    lcdsaddr1;
    unsigned long    lcdsaddr2;
    unsigned long    lcdsaddr3;
    unsigned long    redlut;
    unsigned long    greenlut;
    unsigned long    bluelut;
    unsigned long    reserved[9];
    unsigned long    dithmode;
    unsigned long    tpal;
    unsigned long    lcdintpnd;
    unsigned long    lcdsrcpnd;
    unsigned long    lcdintmsk;
    unsigned long    lpcsel;
};

static volatile struct lcd_regs* lcd_regs;

/*
 * 將內核單色使用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;
    //調色板數組不能大於15
    if (regno >= 16)
         return 1;

    /* 用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;
}


#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;
}


/*
 * 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中定義
};


/*
 * lcd驅動模塊入口
 */
static int lcd_init(void)
{
   int ret;
   printk("lcd device registered\n");

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

       printk("s3c_lcd=%px\n", s3c_lcd);

   /* 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一行所占字節數

    /* 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;

     /* 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緩沖區的大小

    // 時鍾相關,獲取lcd時鍾
    lcd_clk = clk_get(NULL, "lcd");
    if (IS_ERR(lcd_clk)) {
        printk("failed to get lcd clock source\n");
        return PTR_ERR(lcd_clk);
    }

    clk_prepare_enable(lcd_clk);       // 時鍾使能
    printk("got and enabled clock\n");

    /* 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.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.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;

    /* 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);

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

    return 0;
}

/*
 * lcd驅動模塊出口
 */
static void __exit lcd_exit(void)
{
    printk("lcd device unregistered\n");

    unregister_framebuffer(s3c_lcd);
     /* 禁止LCD使能 */
    lcd_regs->lcdcon1 &= ~(1<<0);
    /* 關閉背光 */
    *gpgdat &= ~(1<<4);
    dma_free_wc(&s3c_device_lcd.dev, s3c_lcd->fix.smem_len, s3c_lcd->screen_base, s3c_lcd->fix.smem_start);
    iounmap(lcd_regs);
    iounmap(gpcup);
    iounmap(gpccon);
    iounmap(gpdup);
    iounmap(gpdcon);
    iounmap(gpgcon);
    framebuffer_release(s3c_lcd);
}

module_init(lcd_init);
module_exit(lcd_exit);
MODULE_LICENSE("GPL");
View Code

四、測試

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驅動


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM