AT91SAM9260添加Framebuff驅(qū)動(dòng)
掃描二維碼
隨時(shí)隨地手機(jī)看文章
公司使用的sam9260平臺(tái),LCD自帶控制器,單色。MinGUI的文檔說支持單色LCD,所以打算根據(jù)現(xiàn)有LCD操作方法結(jié)合framebuff驅(qū)動(dòng)格式編寫一個(gè)支持framebuff的新驅(qū)動(dòng)。
原有的LCD操作方法實(shí)現(xiàn)了畫矩形、ASCII字符、漢字。最終是根據(jù)字符或漢字的點(diǎn)陣信息在屏幕上打點(diǎn)!如此而已。原LCD驅(qū)動(dòng)做了個(gè)雙緩沖顯示區(qū),根據(jù)緩沖區(qū)的變化改寫LCD設(shè)備的顯示區(qū),新的framebuff驅(qū)動(dòng)核心思想是:直接操作顯示區(qū)域,需要自己寫的framebuff驅(qū)動(dòng)里沒有畫點(diǎn)、畫圓、顯示字符、顯示漢字等的具體操作。這些操作在framebuff驅(qū)動(dòng)框架里已經(jīng)實(shí)現(xiàn),無需自己編寫。下面記錄下framebuff驅(qū)動(dòng)的編寫過程,LCD硬件部分僅保留修改LCD顯示區(qū)的IO映射和數(shù)據(jù)寫入即可。
手上這款LCD自帶控制器,只能通過讀寫其提供寄存器和他交互數(shù)據(jù),不能直接映射他的顯示區(qū)域。所以我在驅(qū)動(dòng)里申請(qǐng)了2個(gè)和LCD顯示緩沖區(qū)一樣大小的內(nèi)存,一個(gè)用于模擬framebuff驅(qū)動(dòng)需要的共享內(nèi)存區(qū)域,另一個(gè)用來保存這個(gè)模擬共享區(qū)域的快照,用于比對(duì)共享區(qū)域的變化。當(dāng)檢測(cè)到共享內(nèi)存區(qū)域的變化后,將這個(gè)變化通過LCD的寄存器寫給LCD,這樣就能實(shí)現(xiàn)共享區(qū)域的變化能被同步反映到LCD設(shè)備上。
在內(nèi)核的drivers/video/目錄下有很多fb設(shè)備的驅(qū)動(dòng),我找了個(gè)簡(jiǎn)單的dnfb.c作為參考,以他為藍(lán)本實(shí)現(xiàn)我的驅(qū)動(dòng)。首先修改drivers/video下Kconfig,添加:
config FB_DISPLAY
tristate"WHZYDZ LCD support"
depends on FB && ARM
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
接著修改Makefile,添加:
obj-$(CONFIG_FB_DISPLAY) += zydz_fb.o
我們?cè)趜ydz_fb.c中來寫驅(qū)動(dòng)代碼,首先要完成顯示區(qū)域的變化如何寫入到設(shè)備,這個(gè)雖不是framebuff驅(qū)動(dòng)本身特有的,但其作為最基本的一環(huán),必須先實(shí)現(xiàn)。原系統(tǒng)平臺(tái)的相關(guān)驅(qū)動(dòng)可以借鑒。原來的驅(qū)動(dòng)代碼是先定位到LCD顯示緩沖的行首,然后一個(gè)字節(jié)一個(gè)字節(jié)的寫,直到寫完一行的數(shù)據(jù),其中位置光標(biāo)自動(dòng)右移。但在我這,一行點(diǎn)位根本顯示不全,我們用的是RA9935A,我懷疑它在控制自動(dòng)移位方面可能存在問題。后來我改變寫數(shù)據(jù)的方式:自己控制位置光標(biāo),然后寫一個(gè)字節(jié)!這樣能正常顯示了。
接先來就是和MiniGUI聯(lián)調(diào),邊調(diào)邊修改我的驅(qū)動(dòng)。MinGUI得使用shadow引擎才能支持8bpp以下的。重新編譯minigui,configure 時(shí)加上--enable-newgal
--enable-videoshadow
--with-targetname=fbcon
MiniGUI.cfg配置文件修改如下:
[system]
# GAL engine and default options
gal_engine=shadow
defaultmode=320x240-1bpp
[shadow]
real_engine=fbcon
經(jīng)過n次的測(cè)試,主要方法是在MinGUI中增加打印信息,根據(jù)輸出信息判斷出錯(cuò)的位置,然后修改驅(qū)動(dòng)。最后跟到了src/newgal/video.c的int GAL_VideoModeOK (int width, int height, int bpp, Uint32 flags)函數(shù),
里面有段注釋和代碼看了,讓人心涼了一大節(jié)!
/* Currently 1 and 4 bpp are not supported */
if ( bpp < 8 || bpp > 32 ) {
return(0);
}
看來MinGUI1.6.10是不支持位深小于8的屏了。我嘗試著注釋掉了這段代碼,以便讓MinGUI能完成初始化的工作。接著出現(xiàn)下面的錯(cuò)誤:
Linux_fbcon fb_fix.line_length=40
Linux_fbcon fbcon_info.yres=240
Linux_fbcon fbcon_info.fb_size=12288
Linux_fbcon fbcon_info.fb=40021000
Linux_fbcon fbcon_info.bpp=1
GAL_GetVideoMode 1
width=320
height=240
bpp=1
Unhandled fault: external abort on non-linefetch (0x008) at 0x40021000
Bus error
查看linux_fbcon.c:
fbcon_info.fb =
#ifdef _FXRM9200_IAL /* workaround for Fuxu RM9200 */
mmap (NULL, fbcon_info.fb_size, PROT_READ | PROT_WRITE, MAP_SHARED,
fbcon_info.fd_fb, 0);
#elif defined (__uClinux__)
mmap (NULL, fbcon_info.fb_size, PROT_READ | PROT_WRITE, 0,
fbcon_info.fd_fb, 0);
#else
mmap (NULL, fbcon_info.fb_size, PROT_READ | PROT_WRITE, MAP_SHARED,
fbcon_info.fd_fb, 0);
#endif
這個(gè)使用到了framebuff驅(qū)動(dòng)的mmap調(diào)用,再查看drivers/video/Fbmem.c默認(rèn)的fb_mmap函數(shù):
/* frame buffer memory */
start = info->fix.smem_start;
len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
他會(huì)將info->fix.smem_start這個(gè)物理地址進(jìn)行映射。好了,framebuff驅(qū)動(dòng)里面我們可以使用virt_to_phys獲取共享內(nèi)存區(qū)域的物理地址!
自此,edit例程總算運(yùn)行起來了!顯示效果見下圖:
顯示效果不理想,MiniGUI還是用在8bpp以上屏上合適!,下面貼上主要的代碼:
* linux/drivers/video/zydzfb.c -- ZYDZ graphics adaptor frame buffer device
*
* Created 16 Sep2011 by hongchang.yu(yu_hongchang@163.com)
* Based on dnfb.c
*
* History:
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LCD_WIDTH 320
#define LCD_HEIGHT 240
#define DISPRAMBUFLSZ (LCD_WIDTH/8)
#define DISPRAMBUFSIZE (DISPRAMBUFLSZ*LCD_HEIGHT)
/* display_ video definitions */
static void __iomem *io_data=NULL;
static void __iomem *io_cmd=NULL;
static void __iomem *io_ctrl=NULL;
static unsigned long ioo_data=0;
static unsigned char *rambuf_org = NULL;
static unsigned char *rambuf_cur = NULL;
/* frame buffer operations */
// zydzfb_blank控制屏幕開關(guān)
static int zydzfb_blank(int blank, struct fb_info *info);
static struct fb_ops zydzfb_ops = {
.owner = THIS_MODULE,
//.fb_blank = zydzfb_blank,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
struct fb_var_screeninfo zydzfb_var __devinitdata = {
.xres = 320,//實(shí)際x軸分辨率
.yres = 240,//實(shí)際y軸分辨率
.xres_virtual = 320,//虛擬x軸分辨率
.yres_virtual = 240,//虛擬y軸分辨率
.bits_per_pixel= 1, //定義每個(gè)點(diǎn)用多少位表示
.height = -1,
.width = -1,
//.vmode = FB_VMODE_NONINTERLACED,
};
static struct fb_fix_screeninfo zydzfb_fix __devinitdata = {
.id = "zydzfb",//設(shè)備名稱
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_MONO01 ,/* Monochr. 1=Black 0=White */
.line_length = DISPRAMBUFLSZ,
};
/*
* Initialization
*/
static int __devinit zydzfb_probe(struct platform_device *dev)
{
struct fb_info *info;
int err = 0;
info = framebuffer_alloc(0, &dev->dev);
if (!info)
return -ENOMEM;
info->fbops = &zydzfb_ops;
info->fix = zydzfb_fix;
info->fix.smem_start = virt_to_phys(rambuf_cur);
info->fix.smem_len = DISPRAMBUFSIZE;
info->var = zydzfb_var;
/* Virtual address */
info->screen_base = rambuf_cur;
info->screen_size = DISPRAMBUFSIZE;
err = fb_alloc_cmap(&info->cmap, 2, 0);
if (err < 0) {
framebuffer_release(info);
return err;
}
err = register_framebuffer(info);
if (err < 0) {
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
return err;
}
platform_set_drvdata(dev, info);
/* now we have registered we can safely setup the hardware */
printk("display_ frame buffer alive and kicking !n");
retu