新的framebuff驅動核心思想是:直接操作顯示區域,需要自己寫的framebuff驅動里沒有畫點、畫圓、顯示字符、顯示漢字等的具體操作。這些操作在framebuff驅動框架里已經實現,無需自己編寫。下面記錄下framebuff驅動的編寫過程,lcd硬件部分僅保留修改lcd顯示區的IO映射和數據寫入即可。
手上這款lcd自帶控制器,只能通過讀寫其提供寄存器和他交互數據,不能直接映射他的顯示區域。所以我在驅動里申請了2個和lcd顯示緩沖區一樣大小的內存,一個用于模擬framebuff驅動需要的共享內存區域,另一個用來保存這個模擬共享區域的快照,用于比對共享區域的變化。當檢測到共享內存區域的變化后,將這個變化通過lcd的寄存器寫給lcd,這樣就能實現共享區域的變化能被同步反映到lcd設備上。
在內核的drivers/video/目錄下有很多fb設備的驅動,我找了個簡單的dnfb.c作為參考,以他為藍本實現我的驅動。首先修改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
我們在zydz_fb.c中來寫驅動代碼,首先要完成顯示區域的變化如何寫入到設備,這個雖不是framebuff驅動本身特有的,但其作為最基本的一環,必須先實現。原系統平臺的相關驅動可以借鑒。原來的驅動代碼是先定位到lcd顯示緩沖的行首,然后一個字節一個字節的寫,直到寫完一行的數據,其中位置光標自動右移。但在我這,一行點位根本顯示不全,我們用的是RA9935A,我懷疑它在控制自動移位方面可能存在問題。后來我改變寫數據的方式:自己控制位置光標,然后寫一個字節!這樣能正常顯示了。
接先來就是和MiniGUI聯調,邊調邊修改我的驅動。MinGUI得使用shadow引擎才能支持8bpp以下的。重新編譯minigui,configure 時加上--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
經過n次的測試,主要方法是在MinGUI中增加打印信息,根據輸出信息判斷出錯的位置,然后修改驅動。最后跟到了src/newgal/video.c的int GAL_VideoModeOK (int width, int height, int bpp, Uint32 flags)函數,
里面有段注釋和代碼看了,讓人心涼了一大節!
/* Currently 1 and 4 bpp are not supported */
if ( bpp 《 8 || bpp 》 32 ) {
return(0);
}
看來MinGUI1.6.10是不支持位深小于8的屏了。我嘗試著注釋掉了這段代碼,以便讓MinGUI能完成初始化的工作。接著出現下面的錯誤:
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
這個使用到了framebuff驅動的mmap調用,再查看drivers/video/Fbmem.c默認的fb_mmap函數:
/* frame buffer memory */
start = info-》fix.smem_start;
len = PAGE_ALIGN((start & ~PAGE_MASK) + info-》fix.smem_len);
他會將info-》fix.smem_start這個物理地址進行映射。好了,framebuff驅動里面我們可以使用virt_to_phys獲取共享內存區域的物理地址!
自此,edit例程總算運行起來了!顯示效果見下圖:
顯示效果不理想,MiniGUI還是用在8bpp以上屏上合適!,下面貼上主要的代碼:
* linux/drivers/video/zyd***b.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 */
// zyd***b_blank控制屏幕開關
static int zyd***b_blank(int blank, struct fb_info *info);
static struct fb_ops zyd***b_ops = {
.owner = THIS_MODULE,
//.fb_blank = zyd***b_blank,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
struct fb_var_screeninfo zyd***b_var __devinitdata = {
.xres = 320,//實際x軸分辨率
.yres = 240,//實際y軸分辨率
.xres_virtual = 320,//虛擬x軸分辨率
.yres_virtual = 240,//虛擬y軸分辨率
.bits_per_pixel= 1, //定義每個點用多少位表示
.height = -1,
.width = -1,
//.vmode = FB_VMODE_NONINTERLACED,
};
static struct fb_fix_screeninfo zyd***b_fix __devinitdata = {
.id = “zyd***b”,//設備名稱
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_MONO01 ,/* Monochr. 1=Black 0=White */
.line_length = DISPRAMBUFLSZ,
};
/*
* Initialization
*/
static int __devinit zyd***b_probe(struct platform_device *dev)
{
struct fb_info *info;
int err = 0;
info = framebuffer_alloc(0, &dev-》dev);
if (!info)
return -ENOMEM;
info-》fbops = &zyd***b_ops;
info-》fix = zyd***b_fix;
info-》fix.smem_start = virt_to_phys(rambuf_cur);
info-》fix.smem_len = DISPRAMBUFSIZE;
info-》var = zyd***b_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 ! ”);
retu
return err;
}
void disp_init( )
{
static int inited=0;
if(inited)return;
io_data=ioremap_nocache(lcd_DATA_PORT,32);
io_cmd=ioremap_nocache(lcd_CTRL_PORT,32);
io_ctrl=ioremap_nocache(lcd_PERH_PORT,32);
rambuf_org = kmalloc(DISPRAMBUFSIZE,GFP_KERNEL);
rambuf_cur = kmalloc(DISPRAMBUFSIZE,GFP_KERNEL);
lcd_reset();
inited=1;
}
unsigned char re_uc(unsigned char x)
{
x = (x&0x0f)《《4 |(x&0xf0)》》4;
x = (x&0x33)《《2 |(x&0xcc)》》2;
x = (x&0x55)《《1 |(x&0xaa)》》1;
return x;
}
static struct timer_list timer_key;
void fb_timer_func(unsigned long pa)
{
int i;
for(i=0;i
{
if(rambuf_org[i]!=rambuf_cur[i])
{
WritelcdCmd(SRCSET_CMD);
WritelcdData(i%0x100);
WritelcdData(i/0x100);
WritelcdCmd(MEMWRITE_CMD);
WritelcdData(re_uc(rambuf_cur[i]));
rambuf_org[i]=rambuf_cur[i];
}
}
mod_timer(&timer_key,jiffies+20);
}
static struct platform_driver zyd***b_driver = {
.probe = zyd***b_probe,
.driver = {
.name = “zyd***b”,
},
};
static struct platform_device zyd***b_device = {
.name = “zyd***b”,
};
int __init zyd***b_init(void)
{
int ret,i,j;
disp_init( );
ret = platform_driver_register(&zyd***b_driver);
if (!ret) {
ret = platform_device_register(&zyd***b_device);
if (ret)
platform_driver_unregister(&zyd***b_driver);
}
init_timer(&timer_key);
timer_key.function=&fb_timer_func;
timer_key.expires=jiffies+10;
timer_key.data = 0;
add_timer(&timer_key);
return ret;
}
static void __exit zyd***b_exit(void)
{
del_timer(&timer_key);
platform_device_unregister(&zyd***b_device);
platform_driver_unregister(&zyd***b_driver);
if (rambuf_org)
kfree(rambuf_org);
if (rambuf_cur)
kfree(rambuf_cur);
}
module_init(zyd***b_init);
module_exit(zyd***b_exit);
MODULE_LICENSE(“GPL”);
評論
查看更多