【驅動】第4課、LCD驅動之學習筆記
開發環境
主 機:VMWare--Ubuntu-16.04.2-x64-100ask
開發板:Mini2440--256M NandFlash, 2M NorFlash, 64M SDRAM, LCD-TD35;
bootlorder:u-boot1.16, Kernel:2.6.22.6;
編譯器:arm-linux-gcc-3.4.5
目錄
7、問題:在bootloader階段、核心空間、使用者空間對指標指向的空間是如何定義和引用的
7.2 核心空間指標和指標變數的引用的方法?或者說對核心空間記憶體單元的操作方法
1、驅動程式分析,一般從入口函式(xx_init()函式)或裝置的 file_operations 結構體入手,一路走下去,
重點分析物件: 1>賦值語句; 2>函式呼叫; 3>
不特別在意的東西: 1>引數的判斷的語句; 2>看不懂的語句;
2、本LCD驅動程式的缺陷:對於LCD的引數的使用是直接檢視LCD手冊,然後填寫到驅動的幀緩衝器結構體、硬體暫存器的配置、
對於不同的LCD操作不具有可移植性,
3、本LCD驅動程式與裸機LCD程式框架的異同:
相同:
不同: 1)本LCD驅動多了幀緩衝器資訊結構體struct fb_info *s3c_lcd 的初始化和配置;
驅動程式中,根據s3c_lcd = framebuffer_alloc(0, NULL);分配幀緩衝器的各項資訊,
I: 內容上類同於裸板LCD結構體的定義,但是本質和功能完全不同,該驅動是幀緩衝器資訊結構體s3c_lcd,裸板是某款LCD的各項時間引數結構體lcd_3_5_params等;
幀緩衝器資訊結構體s3c_lcd作用:定義可供核心呼叫某款LCD的使用的LCD幀緩衝器;
LCD引數結構體lcd_3_5_params作用:給LCD控制器的初始化配置提供資料;
II:FB_BASE視訊記憶體地址的分配方式不同:
驅動上是>> /* 3.3 分配視訊記憶體(framebuffer), 並把地址告訴LCD控制器 */
s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len, &s3c_lcd->fix.smem_start, GFP_KERNEL);
裸板上是>> #define LCD_3_5_FB_BASE 0x33b00000 //lcd_3.5.c檔案;
2)本LCD驅動少了裸板對於LCD結構體的定義、具體某一款LCD的定義初始化;
4、LCD驅動程式設計擴充套件猜想:可否把裸板的分離分層程式和該章的框架結合起來?甚至把前兩章學習的分離、分層結合起來?
編寫一個具備良好移植相容性的LCD驅動模組。
5、筆記:
1)對於暫存器或者對映記憶體,需要遵循先定義後使用的原則:
gpbcon = ioremap(0x56000010, 8);
lcdcon1 = (4<<8) | (3<<5) | (0x0c<<1);
2)硬體暫存器的設定上,本驅動和我的mini2440的暫存器配置的資料有什麼不同?
I:GPIO口——
II:LCD控制器——
3)LCD背光燈、LCD本身、觸控式螢幕是三個獨立的系統,只不過整合到一個LCD螢幕上工作而已。
4)裸板LCD的主要工作是配置LCD及其控制器,並實現分離分層,然後是關於測試LCD的應用程式:在幀緩衝器中填充資料以完成不同的圖案或字元的輸出;
驅動LCD的主要工作是配置幀緩衝器結構體struct fb_info *s3c_lcd ,然後是LCD相關暫存器的配置。
5)驅動中調色盤(假調色盤)終究沒有用到?只是在fb_ops結構體中定義好元素.fb_setcolreg(即對應LCD的調色盤配置函式s3c_lcdfb_setcolreg),
以相容以往的程式?
6、LCD驅動學習難點是以下三個函式的使用:
I. s3c_lcdfb = framebuffer_alloc(0, NULL);
功能:分配一個幀緩衝器,即分配一段連續的實體記憶體並 set to zero 給幀緩衝器結構體指標 s3c_lcdfb使用。
問題1:以前分配一個結構體時,直接定義然後初始化結構體內的元素即可。這次的xx_fb結構體,定義時(或之後),
需要在初始化之前使用專用的函式分配一段記憶體給結構體xx_fb。是因為這個結構體太大了嗎?
以前裸機全域性變數是直接在.bss段分配記憶體給其使用,由於.bss段在程式有效資料的最後面,只要總的記憶體還夠,就可以直接
順序分配記憶體空間給全域性變數。如此,既有問題2:
問題2:整個linux系統(bootlorder, linux核心, fs根檔案系統)中,關於記憶體是如何管理、使用和分配的?具體到驅動程式的全域性變數和應用程式
的全域性變數的記憶體又是怎樣分配的?
答:百度查到的博文有說,記憶體管理系統可以分為兩部分,分別是核心空間記憶體管理和使用者空間記憶體管理。具體以後再看。
問題3:framebuffer_alloc()函式的使用是因為在其之前只定義了幀緩衝器結構體指標 s3c_lcdfb的原因還是 因為結構體太大需要用專用函式
分配一段記憶體給該結構體使用?對於結構體的定義是怎麼樣的?
補充:以前裸機的全域性變數,若是一般全域性變數分配到.data段,const全域性變數(常變數)分配到.rodata段(只讀資料段),初值為0/無初值的全域性變數
分配到.bss段。這些段在記憶體中的地址分配如下:
SECTIONS{
. = 0x30000000; /*SDRAM在CPU統一編址中的基地址*/
__code_start = .;
. = ALIGN(4);
.text : {*(.text);}
. = ALIGN(4);
.rodata : {*(.rodata);}
. = ALIGN(4);
.data : {*(.data);}
. = ALIGN(4);
.bss : {
_bss_start = . ;
*(.bss) *(.COMMON);
}
. = ALIGN(4);
_end = . ;
}
注意:kzalloc申請過程做了初始化工作並在其後的過程中給指標的成員device做了賦值工作。如果暫時不要給device成員賦值,引數可以寫NULL。
II. s3c_lcdfb->screen_base = dma_alloc_writecombine(NULL, s3c_lcdfb->fix.smem_len, s3c_lcdfb->fix.smem_start, GFP_KERNEL); /* 3.3 分配視訊記憶體,並把視訊記憶體實體地址告訴LCD控制器 */
III. register_framebuffer(s3c_lcdfb); /* 4.註冊幀緩衝器裝置 */
--------------------------------------------------------------------------------------
7、問題:在bootloader階段、核心空間、使用者空間對指標指向的空間是如何定義和引用的?
答:我的困惑源於對各層級指標操作仍存在錯誤的模糊認識!關於bootloader空間、核心空間、使用者空間指標的用法如下。
7.0 <1>舊問題:核心中,對於結構體變數的是如何定義和分配記憶體的?
<2>定義結構體指標變數時,有沒有同時分配儲存空間啊?
<3>看到結構體的陣列定義好以後就直接可以用了。但是結構體指標在連結串列中還要malloc()申請空間。這是為什麼啊?
答:<1>兩種方法,一種是靜態分配記憶體(即直接定義):struct Student stu1; 第二種是通過該結構體指標動態分配一段記憶體空間:struct Student *pst2=malloc(sizeof(struct Student));
<2>定義結構體指標變數 p,並不代表定義了結構體指標指向的變數 *p,系統分配的記憶體是給指標變數p用的,對於指標p指向的空間未定義不分配記憶體空間;
<3>結構體陣列struct Student pstu3[]={{...}, {...}};即同時定義了兩個結構體變數pstu3[0], pstu3[1], 只不過他們是作為陣列pstu3的元素而存在的。
連結串列一般是動態分配記憶體空間,可隨時插入和刪除節點,作為指標域的指標,需要動態分配一個節點的記憶體空間作為其指向的插入的節點。
7.1 在使用者空間中指標變數的引用的方法,詳解如下:
其一:郝斌C語言課程關於指標常見錯誤的筆記:
<0>指標就是地址,地址就是指標; 指標不是指標變數,指標變數不是指標。
<1>int *p; #不代表定義了一個叫做*p的變數!
<2>所有的變數、記憶體單元都遵循先定義再使用的規則,否則就會編譯報錯或程式崩潰!
例:int main(void){
int *p;
int i = 5;
// *p = i; //不遮蔽則程式執行崩潰!
printf("p = %#x\n", p); //列印:p = 0xcccccccc
// printf("*p = %d\n", *p); //不遮蔽則程式執行崩潰!
return 0;
}
/*在VC++6.0中的執行結果:
p = 0xcccccccc
Press any key to continue
*/
語句*p = i; (//不遮蔽則程式執行崩潰!)的詳解:
p須先有指向,然後才能有一個值(地址)賦給它; 若p無指向,則p為空/不可知的垃圾值,因為不知道p的值是多少,所以不知*p
到底代表的是哪一個變數。而*p=i=5,就是把5賦給了一個不知道地址的單元。歸根到底是因為使用者程式只能訪問屬於本程式的記憶體單元,
對於不屬於本程式的記憶體單元,指標p無權訪問。
<3>為指標變數指向的記憶體單元分配記憶體空間的方法(目前只有這兩種方法,其他alloc與malloc類同):
a.靜態分配指標p指向的記憶體空間:int *p; int i; p=&i; *p=5;
b.動態分配指標p指向的記憶體空間:int *p = (int *)malloc(sizeof(int)); *p=5; //其中(int *)可以省略,系統會自動轉換到需要的指標型別;
說明:記憶體的動態分配主要應用於建立程式中的動態資料結構(如連結串列)中。
<4>給指標變數賦值的方法只有一種: int *p; int i; p=&i; ?應該還有另外一種,如上malloc。
其二:譚浩強《C程式設計》第八章_善於利用指標
8.1 指標是什麼
為了說清楚什麼是指標,必須先弄清楚資料在記憶體中是如何儲存的,又是如何讀取的。
如果在程式中定義了一個變數,在對程式進行編譯時,系統就會給這個變數分配記憶體單元。編譯系統根據程式中定義的變數型別,分配一定長度的空間。
由於通過地址能找到所需的變數單元,可以說,地址指向該變數單元。
在程式中一般是通過變數名來引用變數的值,例如:printf("%d\n", i);
上面實際上,是通過變數名i找到儲存單元的地址,從而對儲存單元進行存取操作的。程式經過編譯以後已經將變數名轉換為變數的地址,對變數值的存取都是
通過地址進行的。 (對變數操作的本質>>>>>>>>>對記憶體操作)。
將數值3送到變數i中,有兩種表達方法:
<1>直接訪問:將3直接送到變數i所標誌的單元中,例如“i=3;”; “scanf("%d", &i);”。
<2>間接訪問:將3送到變數pi所指向的單元(即變數i的儲存單元),例如“*pi=3;”, 其中*pi表pi指向的物件。
8.2 指標變數
存放地址的變數是指標變數,它用來指向另一個物件(如變數、陣列、函式等)。
8.2.1 定義指標變數
型別名 * 指標變數名;
8.2.1 引用指標變數
<1>給指標變數賦值。如:p=&a; //把a的地址賦給指標變數p;
<2>引用指標變數指向的變數。如果已經執行"p=&a;",即指標變數p指向了整型變數a,則:printf("%d",*p);其作用是以整數形式輸出指標變數p所指向的變數的值,即變數a的值。如果有賦值語句:*p=1;表示把整數1賦給p當前所指向的變數。
<3>引用指標變數的值。如:printf("%#x", p);
注意:要熟練掌握兩個有關的運算子:<1>& 取地址運算子。<2>* 指標運算子。
8.8 動態記憶體分配與指向它的指標變數
8.8.1 什麼是記憶體的動態分配
除了靜態記憶體分配(一般變數),C語言還允許建立記憶體動態分配區域,以存放一些臨時用的資料,這些資料不必在程式的宣告部分定義,也不必等到函式結束
時才釋放,而是需要時隨時開闢,不需要時隨時釋放。由於未在宣告部分定義它們為變數或陣列,因此不能通過變數名或者陣列名去引用這些資料,只能通過指標來引用。
8.8.2 怎樣建立記憶體的動態分配
對記憶體的動態分配是通過系統提供的庫函式來實現的,主要有malloc, calloc, free, realloc等函式。
記憶體的動態分配主要應用於建立程式中的動態資料結構(如連結串列)中。
【知識歸納】
指標的定義:一個從0開始的非負整數的記憶體單元編號。
指標變數的使用:先定義(int i;int *p;),然後賦初值(p=&i),再然後才可以對其指向的記憶體單元進行讀/寫等操作。
7.2 核心空間指標和指標變數的引用的方法?或者說對核心空間記憶體單元的操作方法?
分兩類:一類是對硬體地址的直接引用(*(volatile unsigned int *)(0x48000000)) ,但只有讀/寫兩種操作方式,這是在bottloader(和核心?)階段使用的,
使用者空間所不具備的應用方式。使用者空間屬於應用層,通過核心方可訪問硬體(包括各種裝置的地址)。
這個對硬體地址的直接引用的層級沒有系統概念,因為系統是我們自己定義編寫的。
二類是與使用者空間的方法一樣:定義指標變數靜態分配記憶體,然後賦初值再引用,p=&a;*p=3; 或動態分配記憶體int *p=malloc(sizeof(int));*p=5;可以有讀/寫/算數加減(包括自加減)等運算操作。
【總結】
造成這種模糊認知是因為arm學習中,CPU對GPIO介面、協議類介面、記憶體類介面控制器(不包括Nandflash)進行統一編址,
對暫存器的操作(包括讀/寫資料)是直接用暫存器的地址(即指標)的指標運算子* Addr(例如:GPACON)作為物件進行讀/寫操作,而非藉助
某一物件,或者說這個物件已經迴歸到某一具體(已知地址的)裝置或記憶體單元。
#define __REG(x) (*(volatile unsigned int *)(x))
#define BWSCON __REG(0x48000000) //Memory Controllers:Bus width & wait status control
#define GPACON __REG(0x56000000) //I/O port:Port A control
在裸機和核心中,對於沒有賦初值的變數一般自動寫0,指標變數寫NULL。這時,對指標變數 p 操作:*p=3;是不現實的。
----------------------------------------------------------------------------------------------------------------
8、暫存器位運算詳解
<1>lcdsaddr1的設定如下,哪種方法正確?
LCDSADDR1位定義:
LCDBANK [29:21]
這些位表明系統儲存器中視訊緩衝器的 bank 位置的 A[30:22]。即使當
移動視口時也不能改變 LCDBANK 的值。LCD 幀緩衝器應該在 4MB
連續區域內,以保證當移動視口時不會改變 LCDBANK 的值。因此應
該謹慎使用 malloc()函式。
預設值:0x00
LCDBASEU [20:0]
對於雙掃描 LCD:這些位表明遞增地址計數器的開始地址的 A[21:1],
它是用於雙掃描 LCD 的遞增幀儲存器或單掃描 LCD 的幀儲存器。
對於單掃描 LCD:這些位表明 LCD 幀緩衝器的開始地址的 A[21:1]。
預設值:0x000000
LCD裸板程式該暫存器的配置:
addr = plcdparams->fb_base<<1;
addr >>= 2;
LCDSADDR1 = addr; //幀緩衝器的開始地址;
方法1:plcd_regs->lcdsaddr1 = ((s3c_lcdfb->fix.smem_start<<1)>>2);
方法2:lcd_regs->lcdsaddr1 = (s3c_lcd->fix.smem_start >> 1) & ~(3<<30);
問題:哪種可以達到正確配置要求?
答:方法2是經過LCD驅動上機驗證可行的,對於方法1是裸板LCD程式使用的方法,但是對於long型資料的直接多次位操作並賦予
暫存器尚待商榷。
驗證程式碼:
#include <stdio.h>
int main(void)
{ unsigned int addr1 = 0xffffffff;
unsigned int lcdsaddr1 = ((addr1<<1)>>2);
unsigned int lcdsaddr2 = ((0xffffffff<<1)>>2);
printf("lcdsaddr1 = %#x, lcdsaddr2 = %#x\n", lcdsaddr1, lcdsaddr2);
return 0;
}
/*在VC++6.0中的執行結果:
lcdsaddr1 = 0x3fffffff, lcdsaddr2 = 0x3fffffff
Press any key to continue
*/
VC++6.0和gcc編譯器對於這類位操作的規則是否一致?為什麼不在直接在arm平臺測試該應用程式呢?##########
若一致,即可說明方法2也是可行的。本次LCD驅動測試使用方法2,觀察LCD執行是否正常。
現象:可使得LCD正常工作!以上兩種方法均可執行!
9.測試
目的:配置一個沒有LCD模組的核心,用新核心去掛接到開發板0x30000000(即SDRAM)進行測試:
# nfs 30000000 192.168.1.105:/work/nfs_root/uImage_mlcd
掛接(伺服器上的)網路根檔案系統到(開發板根檔案系統的)/mnt,從flash上啟動根檔案系統:
# mount -t nfs -o nolock,vers=2 192.168.1.105:/work/nfs_root/fs_second /mnt
說明:經在單板測試,若單板上的根檔案系統原本就是掛接的伺服器上的根檔案系統(例如:fs_second),則不需再用mount命令掛接網路根檔案
系統到開發板了!可直接進行驅動模組載入等試,執行命令:
# echo hello > /dev/tty1
均可在單板LCD上打印出字串“hello”!
操作步驟:
9.1 編譯一個新的沒有LCD模組的核心--uImage_nolcd
$ cd /home/book/workbook/mini2440/systems/linux-2.6.22.6/
$ make menuconfig //配置核心,去掉原來的LCD驅動程式;
-> Device Drivers
-> Graphics support
<M> S3C2410 LCD framebuffer support
$ make uImage //編譯生成新核心;
$ cp arch/arm/boot/uImage /work/nfs_root/uImage_nolcd
$ make modules //編譯模組,是為了把fb_ops結構體的3個cfb_xx函式對應的cfb_xx.c原始檔編譯成.ko檔案(模組),供稍後測試時使用。
9.2 用新核心啟動開發板,在倒數計時結束前按下“空格”鍵,進入uboot選單>
OpenJTAG> print
...(列印的核心資訊..)
ipaddr=192.168.7.17
...(列印的核心資訊..)
OpenJTAG> set ipaddr 192.168.1.17
OpenJTAG> save
OpenJTAG> nfs 30000000 192.168.1.105:/work/nfs_root/uImage_mlcd //uImage_mlcd是核心選單配置時,LCD_fb配置為<M>,即模組,可以事後載入使用;
OpenJTAG> bootm 30000000 //啟動新核心uImage_mlcd;
<啟動核心...>
# mount -t nfs -o nolock,vers=2 192.168.1.105:/work/nfs_root/fs_second /mnt
# cd /mnt
# ls
bin driver_test lib mnt sbin usr
dev etc linuxrc proc sys
9.3 重新配置單板根檔案系統的/etc/inittab檔案,使得單板上的Linux系統擁有串列埠0終端和單板按鍵-LCD兩個控制檯
修改inittab檔案:
# vi /etc/inittab
#/etc/inittab
#console::askfirst:-/bin/sh
::sysinit:/etc/init.d/rcS
s3c2410_serial0::askfirst:-/bin/sh
tty1::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
檢視:
# cat /etc/inittab
#/etc/inittab
#console::askfirst:-/bin/sh
::sysinit:/etc/init.d/rcS
s3c2410_serial0::askfirst:-/bin/sh
tty1::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
# reboot //重啟系統;
OpenJTAG> nfs 30000000 192.168.1.105:/work/nfs_root/uImage_mlcd
OpenJTAG> bootm 30000000 //啟動新核心uImage_mlcd;
<啟動核心...>
# mount -t nfs -o nolock,vers=2 192.168.1.105:/work/nfs_root/fs_second /mnt
# cd /mnt
# insmod cfbcopyarea.ko
# insmod cfbimgblt.ko
# insmod cfbfillrect.ko
# insmod lcd_6.ko
Console: switching to colour frame buffer device 30x40
# insmod input_keys2.ko
input: Unspecified device as /class/input/input1
<此時,LCD螢幕顯示提示資訊...以下都是單板按鍵--LCD螢幕控制檯的LCD螢幕的顯示資訊...>
Please press Enter to activate
starting pid 768, tty '/dev/tty1': '/bin/sh'
# ls
bin dev etc driver_test ...
問題:剛載入好了LCD模組,LCD可以正常使用,但是過一會不用就黑屏了,不能再顯示寫入的字串了,為什麼?
答:Linux下的LCD驅動預設在無操作之後10分鐘後會自動關閉螢幕。
問題:怎麼喚醒LCD螢幕呢?
答:若載入了單板的輸入子系統,可直接敲擊單板上的指令“按鍵”,即可自動喚醒LCD螢幕進行指令操作。
在中斷執行命令# echo wakakak > /dev/tty1 雖然仍然可以傳送字元到單板LCD螢幕,但是不能喚醒LCD螢幕,需要用單板按鍵喚醒!
10、原始碼
1 /* 2 2018-12-14 3 File: lcd_2.c 4 */ 5 #include <linux/module.h> 6 #include <linux/kernel.h> 7 #include <linux/errno.h> 8 #include <linux/string.h> 9 #include <linux/mm.h> 10 #include <linux/slab.h> 11 #include <linux/delay.h> 12 #include <linux/fb.h> 13 #include <linux/init.h> 14 #include <linux/dma-mapping.h> 15 #include <linux/interrupt.h> 16 #include <linux/workqueue.h> 17 #include <linux/wait.h> 18 #include <linux/platform_device.h> 19 #include <linux/clk.h> 20 21 #include <asm/io.h> 22 #include <asm/uaccess.h> 23 #include <asm/div64.h> 24 25 #include <asm/mach/map.h> 26 #include <asm/arch/regs-lcd.h> 27 #include <asm/arch/regs-gpio.h> 28 #include <asm/arch/fb.h> 29 MODULE_LICENSE("GPL"); 30 31 /* 函式宣告 */ 32 static int s3c_lcdfb_setcolreg(unsigned regno, unsigned red, unsigned green, 33 unsigned blue, unsigned transp, struct fb_info *info); 34 35 struct lcd_regs { 36 unsigned long lcdcon1; 37 unsigned long lcdcon2; 38 unsigned long lcdcon3; 39 unsigned long lcdcon4; 40 unsigned long lcdcon5; 41 unsigned long lcdsaddr1; 42 unsigned long lcdsaddr2; 43 unsigned long lcdsaddr3; 44 unsigned long redlut; 45 unsigned long greenlut; 46 unsigned long bluelut; 47 unsigned long reserved[8]; 48 unsigned long dithmode; 49 unsigned long tpal; 50 unsigned long lcdintpnd; 51 unsigned long lcdsrcpnd; 52 unsigned long lcdintmsk; 53 unsigned long tconsel; 54 }; 55 56 static struct fb_ops s3c_lcdfb_ops = { 57 .owner = THIS_MODULE, 58 /* set color register */ 59 .fb_setcolreg = s3c_lcdfb_setcolreg, 60 /* Draws a rectangle */ 61 .fb_fillrect = cfb_fillrect, /* void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect); */ 62 /* Copy data from area to another*/ 63 .fb_copyarea = cfb_copyarea, /* void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);*/ 64 /* Draws a image to the display */ 65 .fb_imageblit = cfb_imageblit, /*void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);*/ 66 }; 67 static struct fb_info *s3c_lcdfb; /* 定義一個幀緩衝器結構體 */ 68 static volatile struct lcd_regs *plcd_regs; 69 static volatile unsigned long *gpbcon; 70 static volatile unsigned long *gpbdat; 71 static volatile unsigned long *gpccon; 72 static volatile unsigned long *gpdcon; 73 static volatile unsigned long *gpgcon; 74 static u32 pseudo_palette[16]; 75 76 /* 畫素格式轉換 */ 77 static inline unsigned int chan_to_field(u_int chan, struct fb_bitfield * bf) 78 { 79 chan &= 0xffff; /* 保留該color的低16位 */ 80 chan >>= 16 - bf->length; /* 裁剪到設定畫素資料格式的該color長度 */ 81 return chan<<bf->offset; 82 } 83 /* 設定調色盤 */ 84 static int s3c_lcdfb_setcolreg(unsigned regno, unsigned red, unsigned green, 85 unsigned blue, unsigned transp, struct fb_info *info) 86 { 87 unsigned int val; 88 if(regno > 16) 89 return 1; /* unknown tybe */ 90 val = chan_to_field(red, &info->var.red); 91 val |= chan_to_field(green, &info->var.green); 92 val |= chan_to_field(blue, &info->var.blue); 93 pseudo_palette[regno] = val; 94 return 0; 95 } 96 97 static int lcd_init(void) 98 { 99 int ret = 0; 100 101 /* 1.分配一個幀緩衝器 */ 102 s3c_lcdfb = framebuffer_alloc(0, NULL); /* sucess: return info; err: return NULL; */ 103 if(!s3c_lcdfb) 104 { 105 printk(KERN_ERR "Unable to alloc s3c_lcdfb!\n"); 106 goto err_fail1; 107 } 108 /* 2.設定幀緩衝器結構體 */ 109 /* 2.1設定固定引數 */ 110 strcpy(s3c_lcdfb->fix.id, "mylcd"); 111 //s3c_lcdfb->fix.smem_start = ; /* 幀緩衝器的起始地址(實體地址) */ 112 s3c_lcdfb->fix.smem_len = 320*240*16/8; /* 幀緩衝器的長度 */ 113 s3c_lcdfb->fix.type = FB_TYPE_PACKED_PIXELS; 114 s3c_lcdfb->fix.visual = FB_VISUAL_TRUECOLOR; /* TFT真彩 */ 115 s3c_lcdfb->fix.line_length = 240*16/8; /* length of a line in bytes */ 116 /* 2.2設定可變引數 */ 117 s3c_lcdfb->var.xres = 240; /* 可見解析度 */ 118 s3c_lcdfb->var.yres = 320; 119 s3c_lcdfb->var.xres_virtual = 240; /* 虛擬解析度 */ 120 s3c_lcdfb->var.yres_virtual = 320; 121 s3c_lcdfb->var.bits_per_pixel = 16; /* 16bpp */ 122 /* RGB = 565 */ 123 s3c_lcdfb->var.red.offset = 11; /* 開始位域偏移量 */ 124 s3c_lcdfb->var.red.length = 5; /* 位域長度 */ 125 s3c_lcdfb->var.green.offset = 5; 126 s3c_lcdfb->var.green.length = 6; 127 s3c_lcdfb->var.blue.offset = 0; 128 s3c_lcdfb->var.blue.length = 5; 129 s3c_lcdfb->var.activate = FB_ACTIVATE_NOW; 130 131 /* 2.3設定操作函式 */ 132 s3c_lcdfb->fbops = &s3c_lcdfb_ops; 133 /* 2.4設定其餘引數 */ 134 //s3c_lcdfb->screen_base = ; /* 螢幕開始的虛擬地址 */ 135 s3c_lcdfb->screen_size = 320*240*16/8; /* Amount of ioremapped VRAM or 0 */ 136 s3c_lcdfb->pseudo_palette = pseudo_palette; 137 /* 3.設定硬體(LCD相關暫存器) */ 138 /* 3.1設定用於LCD的GPIO引腳 */ 139 /* 對映暫存器地址到虛擬記憶體 */ 140 gpbcon = ioremap(0x56000010, 8); 141 gpbdat = gpbcon + 1; 142 gpccon = ioremap(0x56000020, 4); 143 gpdcon = ioremap(0x56000030, 4); 144 gpgcon = ioremap(0x56000060, 4); 145 /* LCD背光燈LED+-, 硬體自動連線到電源正負極,不管? 146 * GPB1: 配置為輸出引腳,高電平:背光供電; 低電平:不供電 147 * GPBCON &= ~(3<<2); 148 * GPBCON |= (1<<2); 149 */ 150 *gpbcon &= ~(3<<2); 151 *gpbcon |= (1<<2); 152 *gpbdat &= ~(1<<1); /* 低電平,先不提供背光 */ 153 /* S3C2440-LCD連線引腳狀態控制 */ 154 *gpccon = 0xaaaaaaaa; /* GPIO管腳用於VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */ 155 *gpdcon = 0xaaaaaaaa; /* GPIO管腳用於VD[23:8] */ 156 /* GPG4: 配置為 LCD_PWREN 功能引腳 */ 157 *gpgcon |= (3<<8); 158 159 /* 3.2設定LCD控制器 */ 160 plcd_regs = ioremap(0X4D000000, sizeof(struct lcd_regs)); 161 /* LCDCON1位定義: 162 * CLKVAL [17:8] 決定 VCLK 的頻率和 CLKVAL[9:0]。TFT:VCLK = HCLK / [(CLKVAL + 1) × 2] (CLKVAL≥0); 163 * PNRMODE [6:5] 11 = TFT LCD 面板; 164 * BPPMODE [4:1] 選擇 BPP(位每畫素)模式: 1100 = TFT 的 16 bpp; 165 * ENVID [0] 0 = 禁止視訊輸出和 LCD 控制訊號; 1 = 允許視訊輸出和 LCD 控制訊號; 166 * 167 * //int CLKVAL = (double)HCLK/plcdparams->time_seq.vclk/2 - 1 + 0.5; 168 * //int CLKVAL = 5; // jz2440 的4.3寸TFT螢幕; 169 * int CLKVAL = 7; // mini2440的3.5寸TFT螢幕; 170 * int bppmode = plcdparams->bpp == 8? 0xb :\ 171 plcdparams->bpp == 16? 0xc :\ 172 0xd; //0xd: 32/24bpp; 173 * LCDCON1 = (CLKVAL<<8) | (3<<5) | (bppmode<<1); 174 */ 175 plcd_regs->lcdcon1 = (7<<8) | (3<<5) | (0xc<<1); 176 /* LCDCON2位定義: 177 * VBPD [31:24]TFT:tvb-1, 垂直後沿為幀開始時,垂直同步週期後的的無效行數。0x00 178 * LINEVAL [23:14] xres-1 ,TFT/STN:此位決定了 LCD 面板的垂直尺寸。 0000000000 179 * VFPD [13:6]TFT: tvf-1, 垂直前沿為幀結束時,垂直同步週期前的的無效行數。00000000 180 * VSPW [5:0]TFT: tvp-1, 通過計算無效行數垂直同步脈衝寬度決定 VSYNC 脈衝的高電平寬度。000000 181 182 * LCDCON2 = (plcdparams->time_seq.tvb - 1<<24) |\ 183 (plcdparams->time_seq.tvf - 1<<6) |\ 184 (plcdparams->time_seq.tvp - 1<<0) |\ 185 (plcdparams->yres - 1<<14); 186 */ 187 plcd_regs->lcdcon2 = (1<<24) | (319<<14) |(8<<6) | (1<<0); 188 /* LCDCON3位定義: 189 * HBPD(TFT)[25:19] TFT:thb-1, 水平後沿為 HSYNC 的下降沿與有效資料的開始之間的 VCLK 週期數。0000000 190 * HOZVAL [18:8] TFT/STN:yres-1, 此位決定了 LCD 面板的水平尺寸。必須決定 HOZVAL 來滿足 1 行的總位元組為 4n 位元組。 191 * 如果單色模式中 LCD 的 x 尺寸為 120個點,但不能支援 x=120,因為 1 行是由 16 位元組(2n)所組成。 192 * LCD面板驅動器將捨棄額外的 8 個點。00000000000 193 * HFPD(TFT)[7:0] TFT:thf-1, 水平後沿為有效資料的結束與 HSYNC 的上升沿之間的 VCLK 週期數。0X00 194 195 * LCDCON3 = (plcdparams->time_seq.thb - 1<<19) |\ 196 (plcdparams->xres- 1<<8) |\ 197 (plcdparams->time_seq.thf - 1<<0); 198 */ 199 plcd_regs->lcdcon3 = (34<<19) | (239<<8) | (39<<0); 200 /* 201 * LCDCON4位定義: 202 * HSPW(TFT)[7:0]TFT:通過計算 VCLK 的數水平同步脈衝寬度決定 HSYNC 脈衝的高電平寬度 0X00 203 204 * LCDCON4 = (plcdparams->time_seq.thp - 1<<0); 205 */ 206 plcd_regs->lcdcon4 = (4<<0); 207 /* 208 * LCDCON5位定義: 209 * FRM565 [11]TFT:此位選擇 16 bpp 輸出視訊資料的格式, 0 = 5:5:5:1 格式 1 = 5:6:5 格式; 0 210 * INVVCLK [10]STN/TFT:此位控制 VCLK 有效沿的極性, 0 = VCLK 下降沿取視訊資料 1 = VCLK 上升沿取視訊資料; 0 211 * INVVLINE [9]STN/TFT:此位表明 VLINE/HSYNC 脈衝極性, 0 = 正常 1 = 反轉; 0, 212 * INVVFRAME [8]STN/TFT:此位表明 VFRAME/VSYNC 脈衝極性, 0 = 正常 1 = 反轉; 0, 213 * INVVD [7]STN/TFT:此位表明 VD(視訊資料)脈衝極性, 0 = 正常 1 = 反轉 VD; 0 214 * INVVDEN [6]TFT:此位表明 VDEN 訊號極性, 0 = 正常 1 = 反轉; 0 215 * INVPWREN [5]STN/TFT:此位表明 PWREN 訊號極性, 0 = 正常 1 = 反轉; 0 216 * INVLEND [4]TFT:此位表明 LEND 訊號極性, 0 = 正常 1 = 反轉; 0 217 * PWREN [3]STN/TFT:LCD_PWREN 輸出訊號使能/禁止, 0 = 禁止 PWREN 訊號 1 = 允許 PWREN 訊號; 0 218 * ENLEND [2]TFT:LEND 輸出訊號使能/禁止, 0 = 禁止 LEND 訊號 1 = 允許 LEND 訊號; 0 219 * 220 * BPP24BL [12]TFT:此位決定 24 bpp 視訊儲存器的順序, 0 = LSB 有效 1 = MSB 有效; 0 221 * BSWP [1]STN/TFT:位元組交換控制位, 0 = 交換禁止 1 = 交換使能; 0 222 * HWSWP [0]STN/TFT:半位元組交換控制位, 0 = 交換禁止 1 = 交換使能, 0 223 * 224 * 儲存器資料格式(TFT) 225 * pixels_data_format = plcdparams->bpp == 32 ? (0<<0) : \ 226 plcdparams->bpp == 16 ? (1<<0) : \ 227 (1<<1); //8bpp; 228 LCDCON5 = (plcdparams->pin_pol.vclk<<10) |\ 229 (plcdparams->pin_pol.hsync<<9) |\ 230 (plcdparams->pin_pol.vsync<<8) |\ 231 (plcdparams->pin_pol.rgb<<7) |\ 232 (plcdparams->pin_pol.de<<6) |\ 233 (plcdparams->pin_pol.pwren<<5) |\ 234 (1<<11) | pixels_data_format; 235 */ 236 plcd_regs->lcdcon5 = (1<<11) | (0<<10) |(1<<9) | (1<<8) | (0<<7) |\ 237 (0<<6) | (0<<5) |(0<<1) | (1<<0); 238 239 /* 3.3 配置視訊記憶體 */ 240 s3c_lcdfb->screen_base = dma_alloc_writecombine(NULL, s3c_lcdfb->fix.smem_len, (dma_addr_t *)&s3c_lcdfb->fix.smem_start, GFP_KERNEL); /* 螢幕開始的虛擬地址 */ 241 /* sucess: return info; err: return NULL; */ 242 if(!s3c_lcdfb->screen_base) 243 { 244 printk(KERN_ERR "Unable to alloc the framebuffer of s3c_lcdfb->screen_base!\n"); 245 goto err_fail2; 246 } 247 /* 248 * LCDSADDR1位定義: 249 LCDBANK [29:21] 250 這些位表明系統儲存器中視訊緩衝器的 bank 位置的 A[30:22]。即使當 251 移動視口時也不能改變 LCDBANK 的值。LCD 幀緩衝器應該在 4MB 252 連續區域內,以保證當移動視口時不會改變 LCDBANK 的值。因此應 253 該謹慎使用 malloc()函式。 254 0x00 255 LCDBASEU [20:0] 256 對於雙掃描 LCD:這些位表明遞增地址計數器的開始地址的 A[21:1], 257 它是用於雙掃描 LCD 的遞增幀儲存器或單掃描 LCD 的幀儲存器。 258 對於單掃描 LCD:這些位表明 LCD 幀緩衝器的開始地址的 A[21:1]。 259 0x000000 260 261 addr = plcdparams->fb_base<<1; 262 addr >>= 2; 263 LCDSADDR1 = addr; //幀緩衝器的開始地址; 264 */ 265 plcd_regs->lcdsaddr1 = ((s3c_lcdfb->fix.smem_start<<1)>>2); 266 /* 267 LCDSADDR2 位 描述 初始狀態 268 LCDBASEL [20:0] 269 對於單掃描 LCD:這些位表明 LCD 幀緩衝器的結束地址的 A[21:1]。 270 LCDBASEL = ((幀結束地址) >> 1) + 1 271 = LCDBASEU + (PAGEWIDTH+OFFSIZE) × (LINEVAL+1); 272 0x0000 273 274 //addr = plcdparams->fb_base + plcdparams->xres*plcdparams->yres*plcdparams->bpp/8; 275 //LCDSADDR2 = (addr>>1) + 1; //幀緩衝器的結束地址; 276 277 addr = plcdparams->fb_base + plcdparams->xres*plcdparams->yres*plcdparams->bpp/8; 278 addr >>= 1; 279 //addr &= 0x1fffff; //有沒有都一樣! 280 LCDSADDR2 = addr; 281 */ 282 plcd_regs->lcdsaddr2 = ((s3c_lcdfb->fix.smem_start + s3c_lcdfb->fix.smem_len)>>1) & 0x1fffff; 283 plcd_regs->lcdsaddr3 = 240*16/16; /* 虛擬屏的頁寬度(單位: 半位元組) */ 284 /* 3.4啟動LCD */ 285 /* GPB0: 輸出高電平,給LCD提供背光燈LED+- 286 * GPBDAT |= (1<<1); 287 */ 288 *gpbdat |= (1<<1); 289 /* LCDCON1位定義: 290 * ENVID [0] 使能LCD訊號輸出: 0 = 禁止視訊輸出和 LCD 控制訊號 1 = 允許視訊輸出和 LCD 控制訊號 291 * LCDCON1 |= (1<<0); 292 */ 293 plcd_regs->lcdcon1 |= (1<<0); 294 /* LCDCON5位定義: 295 * PWREN [3] STN/TFT:LCD_PWREN 輸出訊號使能/禁止, 0 = 禁止 PWREN 訊號 1 = 允許 PWREN 訊號; 0 296 * LCDCON5 |= (1<<3); 297 */ 298 plcd_regs->lcdcon5 |= (1<<3); 299 300 /* 4.註冊幀緩衝器 */ 301 ret = register_framebuffer(s3c_lcdfb); 302 if(ret < 0) 303 { 304 printk(KERN_ERR "Unable to register the s3c_lcdfb framebuffer device!\n"); 305 goto err_fail3; 306 } 307 308 return 0; 309 err_fail3: 310 unregister_framebuffer(s3c_lcdfb); 311 *gpbdat &= ~(1<<1); 312 plcd_regs->lcdcon1 &= ~(1<<0); 313 plcd_regs->lcdcon5 &= ~(1<<3); 314 err_fail2: 315 dma_free_writecombine(NULL, s3c_lcdfb->fix.smem_len, s3c_lcdfb->screen_base, s3c_lcdfb->fix.smem_start); 316 iounmap(plcd_regs); 317 iounmap(gpbcon); 318 iounmap(gpccon); 319 iounmap(gpdcon); 320 iounmap(gpgcon); 321 err_fail1: 322 framebuffer_release(s3c_lcdfb); 323 return ret; 324 } 325 326 static void lcd_exit(void) 327 { 328 /* 1.登出幀緩衝器 */ 329 unregister_framebuffer(s3c_lcdfb); 330 /* 2.禁止LCD使能 */ 331 /* GPB0: 輸出高電平,給LCD提供背光燈LED+- 332 * GPBDAT &= ~(1<<1); 333 */ 334 *gpbdat &= ~(1<<1); 335 /* LCDCON1位定義: 336 * ENVID [0] 使能LCD訊號輸出: 0 = 禁止視訊輸出和 LCD 控制訊號 1 = 允許視訊輸出和 LCD 控制訊號 337 * LCDCON1 &= ~(1<<0); 338 */ 339 plcd_regs->lcdcon1 &= ~(1<<0); 340 /* LCDCON5位定義: 341 * PWREN [3] STN/TFT:LCD_PWREN 輸出訊號使能/禁止, 0 = 禁止 PWREN 訊號 1 = 允許 PWREN 訊號; 0 342 * LCDCON5 &= ~(1<<3); 343 */ 344 plcd_regs->lcdcon5 &= ~(1<<3); 345 346 /* 3.釋放視訊記憶體 */ 347 dma_free_writecombine(NULL, s3c_lcdfb->fix.smem_len, s3c_lcdfb->screen_base, s3c_lcdfb->fix.smem_start); 348 /* 4.釋放暫存器對映的記憶體 */ 349 iounmap(plcd_regs); 350 iounmap(gpbcon); 351 iounmap(gpccon); 352 iounmap(gpdcon); 353 iounmap(gpgcon); 354 /* 5.收回分配給幀緩衝器的空間 */ 355 framebuffer_release(s3c_lcdfb); 356 } 357 358 module_init(lcd_init); 359 module_exit(lcd_exit);
Makefile
1 ifneq ($(KERNELRELEASE),) 2 obj-m := lcd_6.o 3 else 4 KERN_DIR ?= /home/book/workbook/mini2440/systems/linux-2.6.22.6 5 6 PWD = $(shell pwd) 7 all: 8 $(MAKE) -C $(KERN_DIR) M=$(PWD) modules 9 clean: 10 $(MAKE) -C $(KERN_DIR) M=$(PWD) modules clean 11 rm -rf modules.order 12 13 endif