2 * Acess2 ARM PrimeCell Colour LCD Controller (PL110) Driver
\r
3 * - By John Hodge (thePowersGang)
\r
9 * NOTE: The PL110 is set to 24bpp, but these are stored as 32-bit words.
\r
10 * This corresponds to the Acess 32bpp mode, as the Acess 24bpp is packed
\r
13 #define VERSION ((0<<8)|10)
\r
16 #include <modules.h>
\r
18 #include <fs_devfs.h>
\r
19 #include <drv_pci.h>
\r
20 #include <api_drv_video.h>
\r
21 #include <lib/keyvalue.h>
\r
22 #include <options.h> // ARM Arch
\r
24 #define ABS(a) ((a)>0?(a):-(a))
\r
27 typedef struct sPL110 tPL110;
\r
48 #define PL110_BASE 0x10020000 // Integrator
\r
51 // === CONSTANTS ===
\r
54 } caPL110_Modes[] = {
\r
59 const int ciPL110_ModeCount = sizeof(caPL110_Modes)/sizeof(caPL110_Modes[0]);
\r
61 // === PROTOTYPES ===
\r
63 int PL110_Install(char **Arguments);
\r
64 void PL110_Uninstall();
\r
67 Uint64 PL110_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);
\r
68 Uint64 PL110_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);
\r
69 int PL110_IOCtl(tVFS_Node *node, int id, void *data);
\r
71 int PL110_int_SetResolution(int W, int H);
\r
74 MODULE_DEFINE(0, VERSION, PL110, PL110_Install, NULL, NULL);
\r
75 tDevFS_Driver gPL110_DriverStruct = {
\r
79 .Write = PL110_Write,
\r
80 .IOCtl = PL110_IOCtl
\r
84 tPAddr gPL110_PhysBase = PL110_BASE;
\r
85 int gbPL110_IsVersatile = 1;
\r
86 // -- KeyVal parse rules
\r
87 const tKeyVal_ParseRules gPL110_KeyValueParser = {
\r
90 {"Base", "P", &gPL110_PhysBase},
\r
91 {"IsVersatile", "i", &gbPL110_IsVersatile},
\r
96 int giPL110_CurrentMode = 0;
\r
97 int giPL110_BufferMode;
\r
98 int giPL110_Width = 640;
\r
99 int giPL110_Height = 480;
\r
100 size_t giPL110_FramebufferSize;
\r
101 tPL110 *gpPL110_IOMem;
\r
102 tPAddr gPL110_FramebufferPhys;
\r
103 void *gpPL110_Framebuffer;
\r
105 tDrvUtil_Video_BufInfo gPL110_DrvUtil_BufInfo;
\r
106 tVideo_IOCtl_Pos gPL110_CursorPos;
\r
111 int PL110_Install(char **Arguments)
\r
113 // KeyVal_Parse(&gPL110_KeyValueParser, Arguments);
\r
115 gpPL110_IOMem = (void*)MM_MapHWPages(gPL110_PhysBase, 1);
\r
117 PL110_int_SetResolution(caPL110_Modes[0].W, caPL110_Modes[0].H);
\r
119 DevFS_AddDevice( &gPL110_DriverStruct );
\r
125 * \brief Clean up resources for driver unloading
\r
127 void PL110_Uninstall()
\r
132 * \brief Read from the framebuffer
\r
134 Uint64 PL110_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)
\r
140 * \brief Write to the framebuffer
\r
142 Uint64 PL110_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
\r
144 gPL110_DrvUtil_BufInfo.BufferFormat = giPL110_BufferMode;
\r
145 return DrvUtil_Video_WriteLFB(&gPL110_DrvUtil_BufInfo, Offset, Length, Buffer);
\r
148 const char *csaPL110_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};
\r
151 * \brief Handle messages to the device
\r
153 int PL110_IOCtl(tVFS_Node *Node, int ID, void *Data)
\r
156 ENTER("pNode iID pData", Node, ID, Data);
\r
160 BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaPL110_IOCtls);
\r
162 case VIDEO_IOCTL_SETBUFFORMAT:
\r
163 DrvUtil_Video_RemoveCursor( &gPL110_DrvUtil_BufInfo );
\r
164 ret = giPL110_BufferMode;
\r
165 if(Data) giPL110_BufferMode = *(int*)Data;
\r
166 if(gPL110_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)
\r
167 DrvUtil_Video_SetCursor( &gPL110_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor );
\r
170 case VIDEO_IOCTL_GETSETMODE:
\r
175 if( !CheckMem(Data, sizeof(int)) )
\r
176 LEAVE_RET('i', -1);
\r
178 newMode = *(int*)Data;
\r
180 if(newMode < 0 || newMode >= ciPL110_ModeCount)
\r
181 LEAVE_RET('i', -1);
\r
183 if(newMode != giPL110_CurrentMode)
\r
185 giPL110_CurrentMode = newMode;
\r
186 PL110_int_SetResolution( caPL110_Modes[newMode].W, caPL110_Modes[newMode].H );
\r
189 ret = giPL110_CurrentMode;
\r
192 case VIDEO_IOCTL_FINDMODE:
\r
194 tVideo_IOCtl_Mode *mode = Data;
\r
195 int closest, closestArea, reqArea = 0;
\r
196 if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))
\r
197 LEAVE_RET('i', -1);
\r
198 if( mode->bpp != 32 )
\r
200 if( mode->flags != 0 )
\r
205 for( int i = 0; i < ciPL110_ModeCount; i ++ )
\r
208 if(mode->width == caPL110_Modes[i].W && mode->height == caPL110_Modes[i].H) {
\r
214 area = caPL110_Modes[i].W * caPL110_Modes[i].H;
\r
216 reqArea = mode->width * mode->height;
\r
218 closestArea = area;
\r
220 else if( ABS(area - reqArea) < ABS(closestArea - reqArea) ) {
\r
222 closestArea = area;
\r
228 mode->id = closest;
\r
231 mode->width = caPL110_Modes[mode->id].W;
\r
232 mode->height = caPL110_Modes[mode->id].H;
\r
236 case VIDEO_IOCTL_MODEINFO:
\r
238 tVideo_IOCtl_Mode *mode = Data;
\r
239 if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))
\r
240 LEAVE_RET('i', -1);
\r
241 if(mode->id < 0 || mode->id >= ciPL110_ModeCount)
\r
247 mode->width = caPL110_Modes[mode->id].W;
\r
248 mode->height = caPL110_Modes[mode->id].H;
\r
254 case VIDEO_IOCTL_SETCURSOR:
\r
255 if( !Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Pos)) )
\r
256 LEAVE_RET('i', -1);
\r
258 DrvUtil_Video_RemoveCursor( &gPL110_DrvUtil_BufInfo );
\r
260 gPL110_CursorPos = *(tVideo_IOCtl_Pos*)Data;
\r
261 if(gPL110_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)
\r
262 DrvUtil_Video_DrawCursor(
\r
263 &gPL110_DrvUtil_BufInfo,
\r
264 gPL110_CursorPos.x*giVT_CharWidth,
\r
265 gPL110_CursorPos.y*giVT_CharHeight
\r
268 DrvUtil_Video_DrawCursor(
\r
269 &gPL110_DrvUtil_BufInfo,
\r
270 gPL110_CursorPos.x,
\r
289 * \brief Set the LCD controller resolution
\r
290 * \param W Width (aligned to 16 pixels, cipped to 1024)
\r
291 * \param H Height (clipped to 768)
\r
292 * \return Boolean failure
\r
294 int PL110_int_SetResolution(int W, int H)
\r
296 W = (W + 15) & ~0xF;
\r
297 if(W <= 0 || H <= 0) {
\r
298 Log_Warning("PL110", "Attempted to set invalid resolution (%ix%i)", W, H);
\r
301 if(W > 1024) W = 1024;
\r
302 if(H > 768) H = 768;
\r
304 gpPL110_IOMem->LCDTiming0 = ((W/16)-1) << 2;
\r
305 gpPL110_IOMem->LCDTiming1 = H-1;
\r
306 gpPL110_IOMem->LCDTiming2 = (14 << 27);
\r
307 gpPL110_IOMem->LCDTiming3 = 0;
\r
309 if( gpPL110_Framebuffer ) {
\r
310 MM_UnmapHWPages((tVAddr)gpPL110_Framebuffer, (giPL110_FramebufferSize+0xFFF)>>12);
\r
312 giPL110_FramebufferSize = W*H*4;
\r
314 gpPL110_Framebuffer = (void*)MM_AllocDMA( (giPL110_FramebufferSize+0xFFF)>>12, 32, &gPL110_FramebufferPhys );
\r
315 gpPL110_IOMem->LCDUPBase = gPL110_FramebufferPhys;
\r
316 gpPL110_IOMem->LCDLPBase = 0;
\r
318 // Power on, BGR mode, ???, ???, enabled
\r
319 Uint32 controlWord = (1 << 11)|(1 << 8)|(1 << 5)|(5 << 1)|1;
\r
320 // According to qemu, the Versatile version has these two the wrong
\r
322 if( gbPL110_IsVersatile )
\r
324 gpPL110_IOMem->LCDIMSC = controlWord; // Actually LCDControl
\r
325 gpPL110_IOMem->LCDControl = 0; // Actually LCDIMSC
\r
329 gpPL110_IOMem->LCDIMSC = 0;
\r
330 gpPL110_IOMem->LCDControl = controlWord;
\r
334 giPL110_Height = H;
\r
336 // Update the DrvUtil buffer info
\r
337 gPL110_DrvUtil_BufInfo.Framebuffer = gpPL110_Framebuffer;
\r
338 gPL110_DrvUtil_BufInfo.Pitch = W * 4;
\r
339 gPL110_DrvUtil_BufInfo.Width = W;
\r
340 gPL110_DrvUtil_BufInfo.Height = H;
\r
341 gPL110_DrvUtil_BufInfo.Depth = 32;
\r