看lcd驅動好幾天了,一直找不到突破點,感覺無從下手。今天看了一篇介紹lcd驅動編寫的文章,寫的很詳細,對理解frambuffer以及lcd驅動很有幫助。但是英文的,多多少少有點不習慣,翻譯下來留着以後複習的時候再看就方便了。
編寫基於linux的lcd驅動
作者:JimSheng
翻譯:窗外雲天 [email protected]
摘要:
本文詳細描述怎樣編寫linux frambuffer LCD 驅動程序
1. LCD 驅動/設備/控制器
2. linux frambuffer 驅動
2.1 爲什麼要用frambuffer?
2.2 什麼是frambuffer設備?
2.3 怎樣編寫frambuffer驅動程序?
3. linux frambuffer 驅動源碼分析
3.1 fb.h
3.2 fbmen.c
4. LCD 控制器驅動程序結構
4.1 爲lcd顯存分配內存
4.2 實現 fb_ops 功能函數
1.LCD 驅動/設備/控制器
除了LCD設備的datasheet,還有兩個關於LCD的非常不錯的書,並且他們都是中文的。一本叫:《液晶顯示技術》,另外一本叫《液晶顯示器件》。這兩本書詳細介紹了LCD的知識,包括對硬件的操作,LCD的底層編程等。這兩本書對設計LCD模塊和底層編程都很有幫助。
2. linux frambuffer 驅動
2.1 爲什麼要用frambuffer?
如果我們的系統要用GUI(圖形界面接口),比如minigui,MicroWindows.這時LCD設備驅動程序就應該編寫成frambuffer接口,而不是編寫成僅僅操作底層的LCD控制器接口。
2.2 什麼是frambuffer設備?
frambuffer設備層是對圖像設備的一種抽象,它代表了視頻硬件的幀緩存,使得應用程序通過定義好的接口就可以訪問硬件。所以應用程序不需要考慮底層的(寄存器級)的操作。應用程序對設備文件的訪問一般在
/dev 目錄,如 /dev/fb*。更多關於frambuffer設備的詳細信息請閱讀內核文檔:linux /Documentation /fb /framebuffer.txt and linux /Documentation /fb /interal.txt
2.3 怎樣編寫frambuffer驅動程序?
關於如何編寫frambuffer設備驅動,有很多參考資料。你可以在/linux-fbdev.sourceforge.net /HOWTO /index.html網站上閱讀“Linux Frame buffer Driver Writing HOWTO”。
但是我認爲這篇文章過於簡短。所以分析內核相關部分的源碼纔是王道。
3.linux frambuffer 驅動源碼分析
linux中frambuffer接口的所有功能實現都包括在下面兩個文件中
1) linux/include/linux/fb.h
2) linux/drivers/video/fbmem.c
下面詳細分析這兩個文件:
3.1 fb.h
所有frambuffer重要的結構體都定義在這個文件中。下面逐個分析他們:
1) fb_var_screeninfo
這個結構用來描述一個顯卡可以被設置的特性,在fb_var_screeninfo結構體裏有我們設備需要的,比如分辨率等信息。
struct fb_var_screeninfo {
__u32 xres; /* 視口水平分辨率 */
__u32 yres;
__u32 xres_virtual; /* 虛擬屏幕水平分辨率 */
__u32 yres_virtual;
__u32 xoffset; /* 偏移視口與虛擬屏幕水平分辨率偏移 */
__u32 yoffset;
__u32 bits_per_pixel; /* 像素的位數 */
__u32 grayscale; /* 灰度標誌,如果爲1代表是灰度 */
struct fb_bitfield red; /* 如果是真彩色,這個是顏色位,如果不是那麼只有結構的大小重要,其他表示的信息無關緊要 */
struct fb_bitfield green;
struct fb_bitfield blue;
struct fb_bitfield transp; /* 透明度 */
__u32 nonstd; /* 非標準顏色表示標誌位 */
__u32 activate; /* 參照 FB_ACTIVATE_* */
__u32 height; /* 在內存地址空間的長度 */
__u32 width; /* 在內存地址空間的寬度 */
__u32 accel_flags; /* (不用了) 參照 fb_info.flags */
/* 時序: 以下所有的值單位都是pixclock, 當然除了pixclock */
__u32 pixclock; /* 每秒像素值 */
__u32 left_margin; /* 從sync信號到顯示真正的像素的時鐘個數 */
__u32 right_margin; /* 從真正顯示像素到sync信號的時鐘個數 */
__u32 upper_margin; /* 上面兩個是針對列像素的,這個針對行的 */
__u32 lower_margin;
__u32 hsync_len; /* 水平sync信號的長度 */
__u32 vsync_len; /* 垂直sync信號的長度 */
__u32 sync; /* 參照 FB_SYNC_* */
__u32 vmode; /* 參照 FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */ 譯者注:這個不知道具體是什麼
__u32 reserved[5]; /* 保留 */
};
2) fb_fix_screeninfon 這個結構體定義了一些顯示設備的特性,這些特性當設備的工作模式確定之後就不能改變了。例如:frambuffer 內存的起始地址。這個地址是根據模式的不同而不同。一旦你用了一種模式,你就不要在想去改變他的起始地址了。在這種情況下,你可以獲得顯示設備的內存區域地址,但是你不能更改他。
struct fb_fix_screeninfo {
char id[16]; /* 身份表示符,例如 "TT Builtin" */
unsigned long smem_start; /* frame buffer內存的開始地址 */
/* (物理地址) */
__u32 smem_len; /* frame buffer內存地址的長度 */
__u32 type; /* 參照 FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
__u32 visual; /* 參照 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; /* 每行的長度,單位字節 */
unsigned long mmio_start; /* I/O 內存的開始地址 */
/* (物理地址) */
__u32 mmio_len; /* I/O內存的長度 */
__u32 accel; /* 對驅動程序的標示:是哪個設備*/
__u16 reserved[3]; /* 保留 */
};
3) fb_cmap 調色板信息,這個結構是設備依賴的。應用程序可以通過ioctls的FBIOGETCMAP和FBIOPUTCMAP命令來獲得和設置這個結構
struct fb_cmap {
__u32 start; /* First entry */
__u32 len; /* Number of entries */
__u16 *red; /* Red values */
__u16 *green;
__u16 *blue;
__u16 *transp; /* transparency, can be NULL */
};
4) fb_info 這個結構代表了一個顯示設備的當前狀態。他僅對內核可見。除了fb_info結構,內核還有一個fb_ops結構,定義了一些驅動必須的操作函數。
struct fb_info {
int node;
int flags;
struct mutex lock; /* Lock for open/release/ioctl funcs */
struct mutex mm_lock; /* Lock for fb_mmap and smem_* fields */
struct fb_var_screeninfo var; /* Current var */
struct fb_fix_screeninfo fix; /* Current fix */
struct fb_monspecs monspecs; /* Current Monitor specs */
struct work_struct queue; /* Framebuffer event queue */
struct fb_pixmap pixmap; /* Image hardware mapper */
struct fb_pixmap sprite; /* Cursor hardware mapper */
struct fb_cmap cmap; /* Current cmap */
struct list_head modelist; /* mode list */
struct fb_videomode *mode; /* current mode */
#ifdef CONFIG_FB_BACKLIGHT
/* assigned backlight device */
/* set before framebuffer registration,
remove after unregister */
struct backlight_device *bl_dev;
/* Backlight level curve */
struct mutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif
#ifdef CONFIG_FB_DEFERRED_IO
struct delayed_work deferred_work;
struct fb_deferred_io *fbdefio;
#endif
struct fb_ops *fbops;
struct device *device; /* This is the parent */
struct device *dev; /* This is this fb device */
int class_flag; /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; /* Tile Blitting */
#endif
char __iomem *screen_base; /* Virtual address */
unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */
void *pseudo_palette; /* Fake palette of 16 colors */
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state; /* Hardware state i.e suspend */
void *fbcon_par; /* fbcon use-only private area */
/* From here on everything is device dependent */
void *par;
/* we need the PCI or similiar aperture base/size not
smem_start/size as smem_start may just be an object
allocated inside the aperture so may not actually overlap */
resource_size_t aperture_base;
resource_size_t aperture_size;
};
5) struct fb_ops應用程序可以通過ioctl()系統調用來操作LCD硬件,這些操作需要fb_ops中的函數的支持。
struct fb_ops {
/* open/release and usage marking */
struct module *owner;
int (*fb_open)(struct fb_info *info, int user);
int (*fb_release)(struct fb_info *info, int user);
/* For framebuffers with strange non linear layouts or that do not
* work with normal memory mapped access
*/
ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
size_t count, loff_t *ppos);
ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
size_t count, loff_t *ppos);
/* checks var and eventually tweaks it to something supported,
* DO NOT MODIFY PAR */
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
/* set the video mode according to info->var */
int (*fb_set_par)(struct fb_info *info);
/* set color register */
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp, struct fb_info *info);
/* set color registers in batch */
int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
/* blank display */
int (*fb_blank)(int blank, struct fb_info *info);
/* pan display */
int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
/* Draws a rectangle */
void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
/* Copy data from area to another */
void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
/* Draws a image to the display */
void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
/* Draws cursor */
int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
/* Rotates the display */
void (*fb_rotate)(struct fb_info *info, int angle);
/* wait for blit idle, optional */
int (*fb_sync)(struct fb_info *info);
/* perform fb specific ioctl (optional) */
int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
unsigned long arg);
/* Handle 32bit compat ioctl (optional) */
int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
unsigned long arg);
/* perform fb specific mmap */
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
/* get capability given var */
void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
struct fb_var_screeninfo *var);
/* teardown any resources to do with this framebuffer */
void (*fb_destroy)(struct fb_info *info);
};
6) 主要結構關係struct fb_info
| | fb_var_screeninfo
| | fb_fix_screeninfo
| | fb_cmap
| | modename[40]
struct fb_ops ---|--->ops on var
| | fb_open
| | fb_release
| | fb_ioctl
| | fb_mmap
struct fbgen_hwswitch -|-> detect
| | encode_fix
| | encode_var
| | decode_fix
| | decode_var
| | get_var
| | set_var
| | getcolreg
| | setcolreg
| | pan_display
| | blank
| | set_disp
struct fbgen_hwswitch 結構是硬件操作的抽象,不是必須的,但有時很重要。 3.2 fbmem.c
fbmem.c 是frambuffer驅動的核心,他向上給應用程序提供了系統調用接口,向下對特定的硬件提供底層的驅動接口。底層驅動可以通過接口向內核註冊自己。fbmem.c提供了frambuffer驅動的所有接口代碼,從而避免重複的工作。
1) 全局變量
struct fb_info *registered_fb[FB_MAX];
int num_registered_fb;
這兩個變量用來標識系統中正在使用的fb_info結構,fb_info代表了顯示設備當前的狀態。所有的fb_info結構體都保存在全局數組中。當一個新的frambuffer註冊到內核,一個新的項就會加入到這個數組中並且 num_registered_fb加一。static struct {
const char *name;
int (*init)(void);
int (*setup)(void);
} fb_drivers[] __initdata= { ....};
如果一個frambuffer 驅動模塊是靜態鏈接到內核的,那麼必須在這個結構中加入新項,如果用動態加載模塊的方法,就不用考慮這個結構了。static struct file_operations fb_ops ={
owner: THIS_MODULE,
read: fb_read,
write: fb_write,
ioctl: fb_ioctl,
mmap: fb_mmap,
open: fb_open,
release: fb_release
};
這是面向應用程序的接口,fbmem.c中實現了這些操作。2) register_framebuffer(struct fb_info *fb_info) 函數 和 unregister_framebuffer(struct fb_info *fb_info)函數
這個函數是frambuffer設備驅動程序的底層接口。驅動程序用這兩個函數向內核註冊和註銷自己。驅動程序所做的所有底層工作就是填充一個fb_info結構體然後註冊自己
4 LCD控制器驅動程序的結構框架
LCD 驅動操作LCD設備。而frambuffer核心(fbmem.c)管理這些驅動。在linux /drivers /fb /skeleton.c中,編寫了一個frambuffer驅動結構的框架。他用一些代碼展示瞭如何編寫frambuffer驅動。因爲這個太簡單了,他除了填充一個frambuffer的fb_info結構體並註冊到內核外什麼都沒做。爲了編寫一個可用的LCD控制器驅動,還必須做其他的一些工作。具體需要做些什麼呢,我們知道設備驅動程序將硬件抽象化並嚮應用程序提供接口。所以根據系統調用接口的需要,我們實現相應的底層硬件操作,那就是我們在3.2小節提到的 file_operations 結構體
4.1 爲設備分配一塊顯示內存
仔細研究fbmem.c後,我們發現 open()和release() 文件操作方法的實現不需要底層硬件的支持。但是 read(),write()和mmap() 需要一個通用的操作函數fb_get_fix(),這個函數必須由驅動程序的作者編寫另外驅動程序還必須爲設備分配一塊內存作爲顯示緩存,因爲大多數LCD控制器都沒有自己的緩存。這塊內存的開始地址和大小將會填充在fb_fix_screeninfo結構的smem_start和smem_len域。而且內存必須是物理連續的。
4.2 fbops 函數的實現
現在還有個文件操作沒有討論的就是Ioctl()了,應用程序使用系統調用ioctl來操作LCD硬件。在fb_ops定義的方法來支持這些操作。
注意: 這裏的fbops結構不是上層的系統調用接口(注:是在fb_info中的)。下面我們討論該實現什麼樣的方法。ioctl()系統調用在fbmem.c中實現,因此我們在這個實現中會發現相關的命令與fb_ops方法
FBIOGET_VSCREENINFO fb_get_var
FBIOPUT_VSCREENINFO fb_set_var
FBIOGET_FSCREENINFO fb_get_fix
FBIOPUTCMAP fb_set_cmap
FBIOGETCMAP fb_get_cmap
FBIOPAN_DISPLAY fb_pan_display
現在我們知道應該實現這些fb_XXX_XXX函數。應用程序的作者可以通過調用FBIOXXXX來操作lcd。我們如何來實現這些操作呢?最好是先參考一下linux/drivers/video/fbgen.c和在the linux/drivers/video目錄下其他的顯示驅動程序。在這些函數中,fb_set_var()最重要。他用來設置顯示模式和實現其他功能的。下面是fb_set_var()實現的通用步驟:
1) 檢查模式設置是否必須
2) 設置模式
3) 設置調色板
4) 根據先前的配置,設置LCD控制器的寄存器
這四步說明了底層操作的實現。好奇的童鞋會問:應用程序的圖像數據是怎樣顯示在屏幕上的。驅動程序分配一塊內存作爲顯存。然後設置顯存的開始地址和長度到對應的LCD控制器的相應寄存器(這個操作一般由fb_set_var())實現。內存中的數據會通過LCD控制器自動顯示到屏幕上(詳情請參照特定的LCD控制器)。另一方面顯存被mmap()到了用戶空間。因此,當用戶將數據拷貝到映射的內存的時候,數據就會顯示到屏幕上了。
參考資料:
1 液晶顯示技術
2 液晶顯示器件
3 linux/Documentation/fb/framebuffer.txt
4 linux/Documentation/fb/interal.txt
5 Linux Framebuffer Driver Writing HOWTO
http:/linux-fbdev.sourceforge.net/HOWTO /index.html
6 linux/include/linux/fb.h
7 linux/drivers/video/fbmem.c
8 linux/drivers/video/skeletonfb.c
9 linux/drivers/video/fbgen.c
10 linux/drivers/video/tgafb.c
11 s3c2410 microprocessor user manual
s3c2410fb.h, s3c2410fb.c, s3c2410fb.c-pre, s3c2410fb.c-mono
A kind of arm-based widely used MCU, with integrated LCD controller.
12 sed1335 datasheet
A kind of widely used LCD controller