在Windows CE下显示驱动是一个比较复杂的驱动,不仅仅设计到硬件的操作,还有上层驱动的GDI接口支持,有时候还需要支持DirectDraw等绘图接口。如果所有的编码工作都重新做一遍的话,难度还是挺大的,庆幸的是微软已经把大部分的接口都提供好了,DDI中包含的20个接口函数(以函数指针的方式表示),需要我们实现的也仅仅只有GPEEnableDriver。闲话不多说,我们先来看看S3C2440A_LCD_REG结构体吧,如下所示;
typedef struct {
UINT32 LCDCON1; // 0x00
UINT32 LCDCON2; // 0x04
UINT32 LCDCON3; // 0x08
UINT32 LCDCON4; // 0x0C
UINT32 LCDCON5; // 0x10
UINT32 LCDSADDR1; // 0x14
UINT32 LCDSADDR2; // 0x18
UINT32 LCDSADDR3; // 0x1C
UINT32 REDLUT; // 0x20
UINT32 GREENLUT; // 0x24
UINT32 BLUELUT; // 0x28
UINT32 PAD[8]; // 0x2C - 0x48
UINT32 DITHMODE; // 0x4C
UINT32 TPAL; // 0x50
UINT32 LCDINTPND; // 0x54
UINT32 LCDSRCPND; // 0x58
UINT32 LCDINTMSK; // 0x5C
UINT32 TCONSEL; // 0x60
} S3C2440A_LCD_REG, *PS3C2440A_LCD_REG;
其中这里面的成员分别对应着LCD控制器中的相关的寄存器,一旦映射成功就可以对这些寄存器进行直接性质的操作了。先来看看Eboot是如何对其进行初始化操作的,如下代码;
static void InitDisplay(void)
{
volatile S3C2440A_IOPORT_REG *s2440IOP = (S3C2440A_IOPORT_REG *)OALPAtoVA(S3C2440A_BASE_REG_PA_IOPORT, FALSE);
volatile S3C2440A_LCD_REG *s2440LCD = (S3C2440A_LCD_REG *)OALPAtoVA(S3C2440A_BASE_REG_PA_LCD, FALSE);
// Set up the LCD controller registers to display a power-on bitmap image.
s2440IOP->GPCUP = 0xFFFFFFFF;
s2440IOP->GPCCON = 0xAAAAAAAA;
s2440IOP->GPDUP = 0xFFFFFFFF;
s2440IOP->GPDCON = 0xAAAAAAAA;
s2440IOP->GPGCON &= ~(3 << 8); // Set LCD_PWREN as output
s2440IOP->GPGCON |= (1 << 8);
s2440IOP->GPGDAT |= (1 << 4); // LCD3V3 ON
//clkval_calc = (WORD)((float)(S3C2440A_HCLK)/(2.0*5000000)+0.5)-1;
s2440LCD->LCDCON1 = (CLKVAL_TFT << 8) |/* VCLK = HCLK /((CLKVAL + 1) * 2) -> About7Mhz */
(LCD_MVAL_USED << 7) | /* 0 : Each Frame */
(3 << 5) | /* TFT LCD Pannel */
(12 << 1) | /* 16bpp Mode */
(0 << 0) ; /* Disable LCD Output */
s2440LCD->LCDCON2 = (LCD_VBPD << 24) | /* VBPD : 1 */
(LCD_LINEVAL_TFT << 14) | /* Vertical Size : 320 - 1 */
(LCD_VFPD << 6) | /* VFPD : 2 */
(LCD_VSPW << 0) ; /* VSPW : 1 */
s2440LCD->LCDCON3 = (LCD_HBPD << 19) | /* HBPD : 6 */
(LCD_HOZVAL_TFT << 8) | /* HOZVAL_TFT : 240 - 1 */
(LCD_HFPD << 0) ; /* HFPD : 2 */
s2440LCD->LCDCON4 = (LCD_MVAL << 8) | /* MVAL : 13 */
(LCD_HSPW << 0) ; /* HSPW : 4 */
s2440LCD->LCDCON5 = (0 << 12) | /* BPP24BL : LSB valid */
(1 << 11) | /* FRM565 MODE : 5:6:5 Format */
(0 << 10) | /* INVVCLK : VCLK Falling Edge */
(1 << 9) | /* INVVLINE:Inverted Polarity */
(1 << 8) | /* INVVFRAME : Inverted Polarity */
(0 << 7) | /* INVVD : Normal */
(0 << 6) | /* INVVDEN : Normal */
(0 << 5) | /* INVPWREN : Normal */
(0 << 4) | /* INVENDLINE : Normal */
(1 << 3) | /* PWREN : Disable PWREN */
(0 << 2) | /* ENLEND: Disable LEND signal */
(0 << 1) | /* BSWP : Swap Disable */
(1 << 0) ; /* HWSWP : Swap Enable */
s2440LCD->LCDSADDR1 = ((IMAGE_FRAMEBUFFER_DMA_BASE_eboot >> 22) << 21) |
((M5D(IMAGE_FRAMEBUFFER_DMA_BASE_eboot >> 1)) << 0);
s2440LCD->LCDSADDR2 = M5D((IMAGE_FRAMEBUFFER_DMA_BASE_eboot + (LCD_XSIZE_TFT * LCD_YSIZE_TFT * 2)) >> 1);
s2440LCD->LCDSADDR3 = (((LCD_XSIZE_TFT - LCD_XSIZE_TFT) / 1) << 11) | (LCD_XSIZE_TFT / 1);
//s2440LCD->TCONSEL |= 0x3;
s2440LCD->TCONSEL &= (~7);
//s2440LCD->TCONSEL |= (0x1<<4);
s2440LCD->TPAL = 0x0;
s2440LCD->LCDCON1 |= 1;
// Display a bitmap image on the LCD...
//memcpy((void *)IMAGE_FRAMEBUFFER_UA_BASE, ScreenBitmap, LCD_ARRAY_SIZE_TFT_16BIT);
memset((void *)IMAGE_FRAMEBUFFER_UA_BASE_eboot, 0xef, LCD_ARRAY_SIZE_TFT_16BIT);
}
上面的代码已经注释的很清楚,其中IMAGE_FRAMEBUFFER_UA_BASE_eboot指向的是显示屏幕的内存区域,其中主要可以装载RGB256 4MB的图片数据;OALPAtoVA函数的作用是将物理地址转化为虚拟地址,物理地址和虚拟地址的映射表可以在oemaddrtab_cfg.inc文件中可以看到,对应的数组为g_oalAddressTable二维数组。
然而驱动模式下的内存映射API和Eboot完全不同,正如前几节中介绍,驱动的内存映射方式常采用VirtualAlloc、VirtualCopy等函数,如下代码;
v_s2440CLKPWR = (volatile S3C2440A_CLKPWR_REG *)VirtualAlloc(0, sizeof(S3C2440A_CLKPWR_REG), MEM_RESERVE, PAGE_NOACCESS);
VirtualCopy((PVOID)v_s2440CLKPWR, (PVOID)(S3C2440A_BASE_REG_PA_CLOCK_POWER>>8), sizeof(S3C2440A_CLKPWR_REG), PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL ))
vm_pIOPreg = (volatile S3C2440A_IOPORT_REG *)VirtualAlloc(0, sizeof(S3C2440A_IOPORT_REG), MEM_RESERVE, PAGE_NOACCESS);
VirtualCopy((PVOID)vm_pIOPreg, (PVOID)(S3C2440A_BASE_REG_PA_IOPORT >> 8), sizeof(S3C2440A_IOPORT_REG), PAGE_PHYSICAL | PAGE_READWRITE | PAGE_NOCACHE))
显示的操作对于上层应用来说,通过微软的封装GDI接口可以实现各种各样的图形绘制,包括简单的线、点、矩形等操作,然而对于系统来说就比较简单了,其中只需要实现最基本的点操作,线操作即可,当然需要复杂的操作还可以扩展。如下介绍CursorOn的操作,这个函数主要实现对鼠标类型的各个操作绘制,相反的还有CursorOff函数操作,如下代码;
if (!m_CursorForcedOff && !m_CursorDisabled && !m_CursorVisible)
{
RECTL cursorRectSave = m_CursorRect;
int iRotate;
RotateRectl(&m_CursorRect);
for (y = m_CursorRect.top; y < m_CursorRect.bottom; y++){
if (y < 0){
continue;
}
if (y >= m_nScreenHeightSave){
break;
}
ptrLine = &ptrScreen[y * m_pPrimarySurface->Stride()];
cbsLine = &m_CursorBackingStore[(y - m_CursorRect.top) * (m_CursorSize.x * (m_colorDepth >> 3))];
for (x = m_CursorRect.left; x < m_CursorRect.right; x++){
if (x < 0){
continue;
}
if (x >= m_nScreenWidthSave){
break;
}
// x' = x - m_CursorRect.left; y' = y - m_CursorRect.top;
// Width = m_CursorSize.x; Height = m_CursorSize.y;
switch (m_iRotate){
case DMDO_0:
iRotate = (y - m_CursorRect.top)*m_CursorSize.x + x - m_CursorRect.left;
break;
case DMDO_90:
iRotate = (x - m_CursorRect.left)*m_CursorSize.x + m_CursorSize.y - 1 - (y - m_CursorRect.top);
break;
case DMDO_180:
iRotate = (m_CursorSize.y - 1 - (y - m_CursorRect.top))*m_CursorSize.x + m_CursorSize.x - 1 - (x - m_CursorRect.left);
break;
case DMDO_270:
iRotate = (m_CursorSize.x -1 - (x - m_CursorRect.left))*m_CursorSize.x + y - m_CursorRect.top;
break;
default:
iRotate = (y - m_CursorRect.top)*m_CursorSize.x + x - m_CursorRect.left;
break;
}
cbsLine[(x - m_CursorRect.left) * (m_colorDepth >> 3)] = ptrLine[x * (m_colorDepth >> 3)];
ptrLine[x * (m_colorDepth >> 3)] &= m_CursorAndShape[iRotate];
ptrLine[x * (m_colorDepth >> 3)] ^= m_CursorXorShape[iRotate];
if (m_colorDepth > 8)
{
cbsLine[(x - m_CursorRect.left) * (m_colorDepth >> 3) + 1] = ptrLine[x * (m_colorDepth >> 3) + 1];
ptrLine[x * (m_colorDepth >> 3) + 1] &= m_CursorAndShape[iRotate];
ptrLine[x * (m_colorDepth >> 3) + 1] ^= m_CursorXorShape[iRotate];
if (m_colorDepth > 16)
{
cbsLine[(x - m_CursorRect.left) * (m_colorDepth >> 3) + 2] = ptrLine[x * (m_colorDepth >> 3) + 2];
ptrLine[x * (m_colorDepth >> 3) + 2] &= m_CursorAndShape[iRotate];
ptrLine[x * (m_colorDepth >> 3) + 2] ^= m_CursorXorShape[iRotate];
}
}
}
}
m_CursorRect = cursorRectSave;
m_CursorVisible = TRUE;
}
其他还有如电源操作(需要初始化话屏幕,其原理有点像Windows XP的驱动开发模式),绘制各种图形操作等,显示的编码与硬件操作的这块不是很难,只需要继承DDGPE,实现相应的虚拟函数即可,可以说这也是C++高妙之处。随后几节将介绍DDGPE的实现;