📄 lpc32xx_lcd.cpp
字号:
//*********************************************************************
//* Software that is described herein is for illustrative purposes only
//* which provides customers with programming information regarding the
//* products. This software is supplied "AS IS" without any warranties.
//* NXP Semiconductors assumes no responsibility or liability for the
//* use of the software, conveys no license or title under any patent,
//* copyright, or mask work right to the product. NXP Semiconductors
//* reserves the right to make changes in the software without
//* notification. NXP Semiconductors also make no representation or
//* warranty that such application will be suitable for the specified
//* use without further testing or modification.
//*
//* Copyright NXP Semiconductors
//*********************************************************************
//
// lpc32xx_lcd.cpp
//
// This file implements basic flat display driver. Informations about
// supported display modes and switching between them are offloaded to kernel
// via KernelIoControl calls.
//
#define DDI 1
#include "precomp.h"
#include <windows.h>
#include <types.h>
#include <winddi.h>
#include <gpe.h>
#include <emul.h>
#include <ceddk.h>
#include <dispperf.h>
#include <pkfuncs.h>
#include <ddhal.h>
#include "lpc32xx_lcd.h"
#include "lcd_panel_types.h"
#include "lpc32xx_gpio.h"
#include "bsp.h"
#define dim(x) (sizeof(x) / sizeof(x[0]))
//------------------------------------------------------------------------------
// Debug Zones (only for retail)
//#ifndef SHIP_BUILD
INSTANTIATE_GPE_ZONES(0x03, "MGDI Driver", "error", "warn")
#undef ZONE_ERROR
#undef ZONE_WARN
#undef ZONE_FUNCTION
#undef ZONE_INIT
#undef ZONE_INFO
#define ZONE_ERROR DEBUGZONE(0)
#define ZONE_WARN DEBUGZONE(1)
#define ZONE_FUNCTION DEBUGZONE(2)
#define ZONE_INIT DEBUGZONE(3)
#define ZONE_INFO DEBUGZONE(4)
//#endif
//------------------------------------------------------------------------------
// Static variables
static GPE *g_pGPE = (GPE *) NULL;
static TCHAR gszBaseInstance[256] = _T("Drivers\\Display\\LPC32XX\\CONFIG");
static DWORD PanelType = 0;
//------------------------------------------------------------------------------
// External functions
BOOL APIENTRY GPEEnableDriver(
ULONG version, ULONG cj, DRVENABLEDATA *pData, ENGCALLBACKS *pCallbacks
);
//------------------------------------------------------------------------------
//
// Function: DisplayInit
//
// The GWES will invoke this routine once prior to making any other calls
// into the driver. This routine needs to save its instance path information
// and return TRUE. If it returns FALSE, GWES will abort the display
// initialization.
//
BOOL DisplayInit(LPCTSTR pszInstance, DWORD monitors)
{
// Save copy of instance base
if(pszInstance != NULL) {
_tcsncpy(gszBaseInstance, pszInstance, dim(gszBaseInstance));
}
g_pGPE = new LPC32XXLCD;
return TRUE;
}
//------------------------------------------------------------------------------
//
// Function: DrvEnableDriver
//
// This function must be exported from display driver. As long as we use
// GPE class implementation we don't need do anything else than call
// GPEEnableDriver library function.
//
BOOL DrvEnableDriver(
ULONG version, ULONG cj, DRVENABLEDATA *pData, ENGCALLBACKS *pCallbacks
) {
BOOL rc;
rc = GPEEnableDriver(version, cj, pData, pCallbacks);
return rc;
}
//------------------------------------------------------------------------------
//
// Function: DrvGetMasks
//
ULONG *APIENTRY DrvGetMasks(DHPDEV dhpdev)
{
return LCDPanelList[PanelType].m_BitMasks;
}
//------------------------------------------------------------------------------
//
// Function: RegisterDDHALAPI
//
VOID RegisterDDHALAPI()
{
DEBUGMSG(ZONE_FUNCTION, (L"+RegisterDDHALAPI\r\n"));
}
//------------------------------------------------------------------------------
//
// Function: GetGPE
//
// This function is called from GPE class library to get pointer to class
// deriver from GPE.
//
GPE* GetGPE()
{
return g_pGPE;
}
//------------------------------------------------------------------------------
//
// Function: lcd_update_clock
//
// Sets the LCD clock.
//
void LPC32XXLCD::lcd_update_clock(UINT32 desired_clock)
{
UINT32 pixel_div, tmp;
DWORD clk, bytesret;
// Get base clock for LCD (HCLK)
if (KernelIoControl(IOCTL_LPC32XX_GETHCLK, NULL, 0, &clk,
sizeof (clk), &bytesret) == FALSE)
{
// Cannot get clock
RETAILMSG(1, (_T("lcd_update_clock: lcd_update_clock: Error getting LCD base clock\r\n")));
RETAILMSG(1, (_T("lcd_update_clock: Using 104MHz for the base clock\r\n")));
clk = 104000000;
}
// Find closest clock divider to get clock rate
pixel_div = 1;
while (((clk / pixel_div) > desired_clock) && (pixel_div < 0x3F)) {
pixel_div++;
}
DEBUGMSG(ZONE_INFO, (_T("LCD: lcd_update_clock: Base clock %d, divider %d, LCD clock %d\r\n"),
clk, pixel_div, clk/pixel_div));
tmp = pLCDRegs->lcdpol;
if (pixel_div <= 1)
{
/* Pixel clock divider is 1, skip divider logic */
tmp |= CLCDC_LCDTIMING2_BCD;
}
else
{
// Add in new divider
tmp &= ~(CLCDC_LCDTIMING2_BCD | CLCDC_LCDTIMING2_PCD(0x1F) | 0xF8000000);
tmp |= (pixel_div & 0x1F);
tmp |= (((pixel_div >> 5) & 0x1F) << 27);
}
pLCDRegs->lcdpol = tmp;
}
//------------------------------------------------------------------------------
//
// Constructor
//
void LPC32XXLCD::lpc32xx_hw_init(void)
{
HKEY hkDisplay = NULL;
DWORD dwStatus, dwType, dwSize, tmp, bsize, UseIRAM, phya;
PHYSICAL_ADDRESS pa;
GPIO_REGS_T *pGPIOREGs;
// Open the registry key and read our configuration
dwStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, gszBaseInstance, 0, 0, &hkDisplay);
if (dwStatus != ERROR_SUCCESS) {
RETAILMSG(1, (_T("LCD: lpc32xx_hw_init: Error opening registry!\r\n")));
return;
}
else
{
// Get panel type from the register, the panel type matches the panels
// index specified in the panels.cpp list.
PanelType = 0xffff;
dwSize = sizeof(DWORD);
dwType = REG_DWORD;
dwStatus = RegQueryValueEx(hkDisplay, _T("LCDPanelType"), NULL, &dwType,
(LPBYTE) &PanelType, &dwSize);
// Sanity checks
if (dwStatus != ERROR_SUCCESS) {
DEBUGMSG(ZONE_WARN,
(_T("LCD: lpc32xx_hw_init: No panel type found, using 0!\r\n")));
PanelType = 0;
}
if (PanelType > (LCD_MAX_PANELS - 1)) {
RETAILMSG(1,
(_T("LCD: lpc32xx_hw_init: Invalid panel type, use 0!\r\n")));
PanelType = 0;
}
// Get IRAM use flag
// Get panel type from the register, the panel type matches the panels
// index specified in the panels.cpp list.
UseIRAM = 1;
dwSize = sizeof(DWORD);
dwType = REG_DWORD;
dwStatus = RegQueryValueEx(hkDisplay, _T("UseIRAM"), NULL, &dwType,
(LPBYTE) &UseIRAM, &dwSize);
if (dwStatus != ERROR_SUCCESS) {
DEBUGMSG(ZONE_WARN,
(_T("LCD: lpc32xx_hw_init: Using IRAM for LCD buffer\r\n")));
UseIRAM = 1;
}
}
// Close the registry key
if (hkDisplay != NULL) {
RegCloseKey(hkDisplay);
}
// Only TFT panels on 16bpp and 24bpp are supported
if (LCDPanelList[PanelType].lcd_panel_type != TFT) {
RETAILMSG(1, (_T("LCD: lpc32xx_hw_init: Not a TFT panel!\r\n")));
return;
}
// Get pointer to registers used in this driver
pa.QuadPart = LCD_BASE;
pLCDRegs = (CLCDC_REGS_T *) MmMapIoSpace(pa, sizeof (CLCDC_REGS_T), FALSE);
pa.QuadPart = CLK_PM_BASE;
pCLKPWRRegs = (CLKPWR_REGS_T *)
MmMapIoSpace(pa, sizeof (CLKPWR_REGS_T), FALSE);
if ((pLCDRegs == NULL) || (pCLKPWRRegs == NULL))
{
RETAILMSG(1, (_T("LCD: lpc32xx_hw_init: Critcal error: cannot map registers!\r\n")));
if (pLCDRegs != NULL)
{
MmUnmapIoSpace(pLCDRegs, sizeof(CLCDC_REGS_T));
}
if (pCLKPWRRegs != NULL)
{
MmUnmapIoSpace(pCLKPWRRegs, sizeof(CLKPWR_REGS_T));
}
return;
}
if (LCDPanelList[PanelType].bits_per_pixel == 16) {
tmp = CLKPWR_LCDCTRL_LCDTYPE_TFT16;
bsize = 2;
}
else if (LCDPanelList[PanelType].bits_per_pixel == 24) {
tmp = CLKPWR_LCDCTRL_LCDTYPE_TFT24;
bsize = 4;
}
else {
RETAILMSG(1, (_T("LCD: lpc32xx_hw_init: Unsupported color depth!\r\n")));
return;
}
// Enough memory for frame buffer?
if ((LCDPanelList[PanelType].pixels_per_line *
LCDPanelList[PanelType].lines_per_panel * bsize) >
IMAGE_WINCE_LCD_BUFFERS_SIZE) {
RETAILMSG(1, (_T("LCD: lpc32xx_hw_init: Frame buffer not big enough!\r\n")));
return;
}
// Enable LCD clocks and setup prescaler to 1
pCLKPWRRegs->clkpwr_lcdclk_ctrl = (tmp | CLKPWR_LCDCTRL_CLK_EN);
// Configure LCD based on selected panel type
pLCDRegs->lcdctrl &= ~CLCDC_LCDCTRL_ENABLE;
// Generate the timing 0 word
tmp = (CLCDC_LCDTIMING0_PPL(LCDPanelList[PanelType].pixels_per_line) |
CLCDC_LCDTIMING0_HSW(LCDPanelList[PanelType].h_sync_pulse_width) |
CLCDC_LCDTIMING0_HFP(LCDPanelList[PanelType].h_front_porch) |
CLCDC_LCDTIMING0_HBP(LCDPanelList[PanelType].h_back_porch));
pLCDRegs->lcdtimh = tmp;
// Generate the timing 1 word
tmp = (CLCDC_LCDTIMING1_LPP(LCDPanelList[PanelType].lines_per_panel) |
CLCDC_LCDTIMING1_VSW(LCDPanelList[PanelType].v_sync_pulse_width) |
CLCDC_LCDTIMING1_VFP(LCDPanelList[PanelType].v_front_porch) |
CLCDC_LCDTIMING1_VBP(LCDPanelList[PanelType].v_back_porch));
pLCDRegs->lcdtimv = tmp;
// Generate the timing 2 word
tmp = CLCDC_LCDTIMING2_ACB(LCDPanelList[PanelType].ac_bias_frequency);
if (LCDPanelList[PanelType].invert_output_enable != 0)
{
tmp |= CLCDC_LCDTIMING2_IOE;
}
if (LCDPanelList[PanelType].invert_panel_clock != 0)
{
tmp |= CLCDC_LCDTIMING2_IPC;
}
if (LCDPanelList[PanelType].invert_hsync != 0)
{
tmp |= CLCDC_LCDTIMING2_IHS;
}
if (LCDPanelList[PanelType].invert_vsync != 0)
{
tmp |= CLCDC_LCDTIMING2_IVS;
}
// Clocks per line and pixels per line are the same
tmp = tmp | CLCDC_LCDTIMING2_CPL(
LCDPanelList[PanelType].pixels_per_line - 1);
pLCDRegs->lcdpol = tmp;
// Update timing 2 word with correct clock data
lcd_update_clock(LCDPanelList[PanelType].optimal_clock);
// Skip timing 3 word - just set to 0x0
pLCDRegs->lcdle = 0x00000000;
// Default with all interrupts off
pLCDRegs->lcdintrenable = 0x00000000;
#if 0 // SDRAM only version
// Default configuration is 16 bits per pixel with blue and
// green not swapped
if (LCDPanelList[PanelType].bits_per_pixel == 16) {
tmp = CLCDC_LCDCTRL_BPP16_565;
}
else
{
tmp = CLCDC_LCDCTRL_BPP24;
}
pLCDRegs->lcdctrl = (tmp | CLCDC_LCDCTRL_RGB | CLCDC_LCDCTRL_TFT);
// Setup frame buffer
pLCDRegs->lcdupbase = IMAGE_WINCE_LCD_BUFFERS_PA;
// Enable LCD
pLCDRegs->lcdctrl |= (CLCDC_LCDCTRL_ENABLE | CLCDC_LCDCTRL_PWR);
// Enable LCD and backlight power
pa.QuadPart = GPIO_BASE;
pGPIOREGs = (GPIO_REGS_T *) MmMapIoSpace(pa, sizeof (GPIO_REGS_T), FALSE);
pGPIOREGs->pio_outp_set = _BIT(0);
#if BSP_BOARD_PHY3250_QVGA_LCD1307 > 0
pGPIOREGs->pio_outp_set = _BIT(4);
#else
pGPIOREGs->pio_outp_clr = _BIT(4);
#endif
MmUnmapIoSpace((PVOID) pGPIOREGs, 0);
// Setup up display mode related constants
m_nScreenWidth = LCDPanelList[PanelType].pixels_per_line;
m_nScreenHeight = LCDPanelList[PanelType].lines_per_panel;
m_colorDepth = LCDPanelList[PanelType].bits_per_pixel;
m_cbScanLineLength = LCDPanelList[PanelType].pixels_per_line * bsize;
m_FrameBufferSize = m_nScreenHeight * m_cbScanLineLength;
// Allocate virtual memory for frame buffer
m_VirtualFrameBuffer = (DWORD) VirtualAlloc(NULL, m_FrameBufferSize,
MEM_RESERVE, (PAGE_READWRITE | PAGE_NOCACHE));
if (!m_VirtualFrameBuffer)
{
RETAILMSG(1, (TEXT("m_VirtualFrameBuffer is not allocated\n\r")));
return;
}
if (!VirtualCopy((PVOID)m_VirtualFrameBuffer, (PVOID) (IMAGE_WINCE_LCD_BUFFERS_PA >> 8),
m_FrameBufferSize, (PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL)))
{
RETAILMSG(1, (TEXT("m_VirtualFrameBuffer is not mapped\n\r")));
VirtualFree((PVOID)m_VirtualFrameBuffer, 0, MEM_RELEASE);
return;
}
DEBUGMSG(ZONE_FUNCTION, (TEXT("m_VirtualFrameBuffer is mapped at %x(PHY : %x)\n\r"),
m_VirtualFrameBuffer, IMAGE_WINCE_LCD_BUFFERS_PA));
#else // SDRAM and IRAM version
// Default configuration is 16 bits per pixel with blue and
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -