linux驅動移植-LCD驅動案例 Mini2440裸機開發之LCD程式設計(GB2312、ASCII字型檔製作)16.Linux-LCD驅動(詳解)
由於我使用的Mini2440開發板採用的LCD為TFT屏,型號為LCD-P35(LQ035Q1DG04和ZQ3506_V0手冊通用) 。這一節,我們將參考s3c2410fb.c編寫LCD驅動程式。
一、LCD驅動編寫基礎函式
1.1 dma_alloc_wc
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引腳
(3.2) 根據LCD手冊設定LCD控制器
(3.3) 分配視訊記憶體(framebuffer),把視訊記憶體地址高速LCD控制器和fb_info
(4) 開啟LCD,並註冊fb_info
(4.1) 開啟LCD
-
- 控制LCDCON5允許PWREN訊號
- 控制LCDCON1輸出PWREN訊號
- 輸出GPB0高電平來開啟背光
(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.1設定固定引數 */ strcpy(s3c_lcd->fix.id, "mylcd"); s3c_lcd->fix.smem_len = LCD_xres * LCD_yres * 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_xres * 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_xres; // 行解析度 s3c_lcd->var.yres = LCD_yres; // 列解析度 s3c_lcd->var.xres_virtual = LCD_xres; // 行虛擬解析度 s3c_lcd->var.yres_virtual = LCD_yres; // 列虛擬解析度 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_xres * LCD_yres * 2; // framebuffer緩衝區的大小
3.6 設定硬體相關的操作(配置GPIO用於LCD)
參考之前介紹的Mini2440裸機開發之LCD程式設計(GB2312、ASCII字型檔製作)初始化GPIO,主要是GPCCON、GPDCON暫存器:
/* 3.硬體相關操作 */ /* 3.1 配置GPIO口用於LCD */ gpccon = ioremap(0x56000020,4); gpdcon = ioremap(0x56000030,4); gpgcon = ioremap(0x56000030,4); // GPIO管腳用於VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND *gpccon = 0xaaaaaaaa; // GPIO管腳用於VD[23:8] *gpdcon = 0xaaaaaaaa; // 設定GPG4引腳為LCD_PWREN模式 *gpgcon |= (3 << 8);
3.7 設定硬體相關的操作(根據LCD手冊設定LCD控制器)
3.8 設定硬體相關的操作(分配視訊記憶體)
四、測試
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 --->
-
Support for frame buffer devices --->
- <M> S3C2410 LCD framebuffer support
- <M> Samsung S3C framebuffer support
-
Support for frame buffer devices --->
-
Frame buffer Device --->
儲存檔案,輸入檔名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/
編譯模組:
make modules
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. Erasing at 0x80000 -- 6% complete. Erasing at 0xa0000 -- 9% complete. Erasing at 0xc0000 -- 12% complete. Erasing at 0xe0000 -- 15% complete. Erasing at 0x100000 -- 18% complete. Erasing at 0x120000 -- 21% complete. Erasing at 0x140000 -- 25% complete. Erasing at 0x160000 -- 28% complete. Erasing at 0x180000 -- 31% complete. Erasing at 0x1a0000 -- 34% complete. Erasing at 0x1c0000 -- 37% complete. Erasing at 0x1e0000 -- 40% complete. Erasing at 0x200000 -- 43% complete. Erasing at 0x220000 -- 46% complete. Erasing at 0x240000 -- 50% complete. Erasing at 0x260000 -- 53% complete. Erasing at 0x280000 -- 56% complete. Erasing at 0x2a0000 -- 59% complete. Erasing at 0x2c0000 -- 62% complete. Erasing at 0x2e0000 -- 65% complete. Erasing at 0x300000 -- 68% complete. Erasing at 0x320000 -- 71% complete. Erasing at 0x340000 -- 75% complete. Erasing at 0x360000 -- 78% complete. Erasing at 0x380000 -- 81% complete. Erasing at 0x3a0000 -- 84% complete. Erasing at 0x3c0000 -- 87% complete. Erasing at 0x3e0000 -- 90% complete. Erasing at 0x400000 -- 93% complete. Erasing at 0x420000 -- 96% complete. Erasing at 0x440000 -- 100% 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 安裝驅動
重啟開發板,執行如下命令:
insmod lcd_dev.ko
掛載LCD驅動後, 如下圖,可以通過如下命令檢視已掛載的LCD裝置節點::
ls -l /dev/fb*
4.6 測試執行
比如向LCD螢幕輸出hello字串:
echo hello> /dev/tty1
參考文章