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 size_t PL110_Read(tVFS_Node *node, off_t Offset, size_t len, void *buffer, Uint flags);
\r
68 size_t PL110_Write(tVFS_Node *node, off_t offset, size_t len, const void *buffer, Uint flags);
\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 tVFS_NodeType gPL110_DevNodeType = {
\r
77 .Write = PL110_Write,
\r
78 .IOCtl = PL110_IOCtl
\r
80 tDevFS_Driver gPL110_DriverStruct = {
\r
82 {.Type = &gPL110_DevNodeType}
\r
85 tPAddr gPL110_PhysBase = PL110_BASE;
\r
86 int gbPL110_IsVersatile = 1;
\r
87 // -- KeyVal parse rules
\r
88 const tKeyVal_ParseRules gPL110_KeyValueParser = {
\r
91 {"Base", "P", &gPL110_PhysBase},
\r
92 {"IsVersatile", "i", &gbPL110_IsVersatile},
\r
97 int giPL110_CurrentMode = 0;
\r
98 int giPL110_BufferMode;
\r
99 int giPL110_Width = 640;
\r
100 int giPL110_Height = 480;
\r
101 size_t giPL110_FramebufferSize;
\r
102 tPL110 *gpPL110_IOMem;
\r
103 tPAddr gPL110_FramebufferPhys;
\r
104 void *gpPL110_Framebuffer;
\r
106 tDrvUtil_Video_BufInfo gPL110_DrvUtil_BufInfo;
\r
107 tVideo_IOCtl_Pos gPL110_CursorPos;
\r
112 int PL110_Install(char **Arguments)
\r
114 // KeyVal_Parse(&gPL110_KeyValueParser, Arguments);
\r
116 gpPL110_IOMem = (void*)MM_MapHWPages(gPL110_PhysBase, 1);
\r
118 PL110_int_SetResolution(caPL110_Modes[0].W, caPL110_Modes[0].H);
\r
120 DevFS_AddDevice( &gPL110_DriverStruct );
\r
126 * \brief Clean up resources for driver unloading
\r
128 void PL110_Uninstall()
\r
133 * \brief Read from the framebuffer
\r
135 size_t PL110_Read(tVFS_Node *node, off_t off, size_t len, void *buffer, Uint flags)
\r
141 * \brief Write to the framebuffer
\r
143 size_t PL110_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint flags)
\r
145 gPL110_DrvUtil_BufInfo.BufferFormat = giPL110_BufferMode;
\r
146 return DrvUtil_Video_WriteLFB(&gPL110_DrvUtil_BufInfo, Offset, Length, Buffer);
\r
149 const char *csaPL110_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};
\r
152 * \brief Handle messages to the device
\r
154 int PL110_IOCtl(tVFS_Node *Node, int ID, void *Data)
\r
157 ENTER("pNode iID pData", Node, ID, Data);
\r
161 BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaPL110_IOCtls);
\r
163 case VIDEO_IOCTL_SETBUFFORMAT:
\r
164 DrvUtil_Video_RemoveCursor( &gPL110_DrvUtil_BufInfo );
\r
165 ret = giPL110_BufferMode;
\r
166 if(Data) giPL110_BufferMode = *(int*)Data;
\r
167 if(gPL110_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)
\r
168 DrvUtil_Video_SetCursor( &gPL110_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor );
\r
171 case VIDEO_IOCTL_GETSETMODE:
\r
176 if( !CheckMem(Data, sizeof(int)) )
\r
177 LEAVE_RET('i', -1);
\r
179 newMode = *(int*)Data;
\r
181 if(newMode < 0 || newMode >= ciPL110_ModeCount)
\r
182 LEAVE_RET('i', -1);
\r
184 if(newMode != giPL110_CurrentMode)
\r
186 giPL110_CurrentMode = newMode;
\r
187 PL110_int_SetResolution( caPL110_Modes[newMode].W, caPL110_Modes[newMode].H );
\r
190 ret = giPL110_CurrentMode;
\r
193 case VIDEO_IOCTL_FINDMODE:
\r
195 tVideo_IOCtl_Mode *mode = Data;
\r
196 int closest, closestArea, reqArea = 0;
\r
197 if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))
\r
198 LEAVE_RET('i', -1);
\r
199 if( mode->bpp != 32 )
\r
201 if( mode->flags != 0 )
\r
206 for( int i = 0; i < ciPL110_ModeCount; i ++ )
\r
209 if(mode->width == caPL110_Modes[i].W && mode->height == caPL110_Modes[i].H) {
\r
215 area = caPL110_Modes[i].W * caPL110_Modes[i].H;
\r
217 reqArea = mode->width * mode->height;
\r
219 closestArea = area;
\r
221 else if( ABS(area - reqArea) < ABS(closestArea - reqArea) ) {
\r
223 closestArea = area;
\r
229 mode->id = closest;
\r
232 mode->width = caPL110_Modes[mode->id].W;
\r
233 mode->height = caPL110_Modes[mode->id].H;
\r
237 case VIDEO_IOCTL_MODEINFO:
\r
239 tVideo_IOCtl_Mode *mode = Data;
\r
240 if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))
\r
241 LEAVE_RET('i', -1);
\r
242 if(mode->id < 0 || mode->id >= ciPL110_ModeCount)
\r
248 mode->width = caPL110_Modes[mode->id].W;
\r
249 mode->height = caPL110_Modes[mode->id].H;
\r
255 case VIDEO_IOCTL_SETCURSOR:
\r
256 if( !Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Pos)) )
\r
257 LEAVE_RET('i', -1);
\r
259 DrvUtil_Video_RemoveCursor( &gPL110_DrvUtil_BufInfo );
\r
261 gPL110_CursorPos = *(tVideo_IOCtl_Pos*)Data;
\r
262 if(gPL110_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)
\r
263 DrvUtil_Video_DrawCursor(
\r
264 &gPL110_DrvUtil_BufInfo,
\r
265 gPL110_CursorPos.x*giVT_CharWidth,
\r
266 gPL110_CursorPos.y*giVT_CharHeight
\r
269 DrvUtil_Video_DrawCursor(
\r
270 &gPL110_DrvUtil_BufInfo,
\r
271 gPL110_CursorPos.x,
\r
290 * \brief Set the LCD controller resolution
\r
291 * \param W Width (aligned to 16 pixels, cipped to 1024)
\r
292 * \param H Height (clipped to 768)
\r
293 * \return Boolean failure
\r
295 int PL110_int_SetResolution(int W, int H)
\r
297 W = (W + 15) & ~0xF;
\r
298 if(W <= 0 || H <= 0) {
\r
299 Log_Warning("PL110", "Attempted to set invalid resolution (%ix%i)", W, H);
\r
302 if(W > 1024) W = 1024;
\r
303 if(H > 768) H = 768;
\r
305 gpPL110_IOMem->LCDTiming0 = ((W/16)-1) << 2;
\r
306 gpPL110_IOMem->LCDTiming1 = H-1;
\r
307 gpPL110_IOMem->LCDTiming2 = (14 << 27);
\r
308 gpPL110_IOMem->LCDTiming3 = 0;
\r
310 if( gpPL110_Framebuffer ) {
\r
311 MM_UnmapHWPages(gpPL110_Framebuffer, (giPL110_FramebufferSize+0xFFF)>>12);
\r
313 giPL110_FramebufferSize = W*H*4;
\r
315 gpPL110_Framebuffer = MM_AllocDMA( (giPL110_FramebufferSize+0xFFF)>>12, 32, &gPL110_FramebufferPhys );
\r
316 gpPL110_IOMem->LCDUPBase = gPL110_FramebufferPhys;
\r
317 gpPL110_IOMem->LCDLPBase = 0;
\r
319 // Power on, BGR mode, ???, ???, enabled
\r
320 Uint32 controlWord = (1 << 11)|(1 << 8)|(1 << 5)|(5 << 1)|1;
\r
321 // According to qemu, the Versatile version has these two the wrong
\r
323 if( gbPL110_IsVersatile )
\r
325 gpPL110_IOMem->LCDIMSC = controlWord; // Actually LCDControl
\r
326 gpPL110_IOMem->LCDControl = 0; // Actually LCDIMSC
\r
330 gpPL110_IOMem->LCDIMSC = 0;
\r
331 gpPL110_IOMem->LCDControl = controlWord;
\r
335 giPL110_Height = H;
\r
337 // Update the DrvUtil buffer info
\r
338 gPL110_DrvUtil_BufInfo.Framebuffer = gpPL110_Framebuffer;
\r
339 gPL110_DrvUtil_BufInfo.Pitch = W * 4;
\r
340 gPL110_DrvUtil_BufInfo.Width = W;
\r
341 gPL110_DrvUtil_BufInfo.Height = H;
\r
342 gPL110_DrvUtil_BufInfo.Depth = 32;
\r