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
23 #define ABS(a) ((a)>0?(a):-(a))
\r
26 typedef struct sPL110 tPL110;
\r
46 // === CONSTANTS ===
\r
47 #define PL110_BASE 0x10020000 // Integrator
\r
51 } caPL110_Modes[] = {
\r
56 const int ciPL110_ModeCount = sizeof(caPL110_Modes)/sizeof(caPL110_Modes[0]);
\r
58 // === PROTOTYPES ===
\r
60 int PL110_Install(char **Arguments);
\r
61 void PL110_Uninstall();
\r
64 Uint64 PL110_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);
\r
65 Uint64 PL110_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);
\r
66 int PL110_IOCtl(tVFS_Node *node, int id, void *data);
\r
68 int PL110_int_SetResolution(int W, int H);
\r
71 MODULE_DEFINE(0, VERSION, PL110, PL110_Install, NULL, NULL);
\r
72 tDevFS_Driver gPL110_DriverStruct = {
\r
76 .Write = PL110_Write,
\r
77 .IOCtl = PL110_IOCtl
\r
81 tPAddr gPL110_PhysBase = PL110_BASE;
\r
82 int gbPL110_IsVersatile = 1;
\r
83 // -- KeyVal parse rules
\r
84 const tKeyVal_ParseRules gPL110_KeyValueParser = {
\r
87 {"Base", "P", &gPL110_PhysBase},
\r
88 {"IsVersatile", "i", &gbPL110_IsVersatile},
\r
93 int giPL110_CurrentMode = 0;
\r
94 int giPL110_BufferMode;
\r
95 int giPL110_Width = 640;
\r
96 int giPL110_Height = 480;
\r
97 size_t giPL110_FramebufferSize;
\r
98 tPL110 *gpPL110_IOMem;
\r
99 tPAddr gPL110_FramebufferPhys;
\r
100 void *gpPL110_Framebuffer;
\r
102 tDrvUtil_Video_BufInfo gPL110_DrvUtil_BufInfo;
\r
103 tVideo_IOCtl_Pos gPL110_CursorPos;
\r
108 int PL110_Install(char **Arguments)
\r
110 // KeyVal_Parse(&gPL110_KeyValueParser, Arguments);
\r
112 gpPL110_IOMem = (void*)MM_MapHWPages(gPL110_PhysBase, 1);
\r
114 PL110_int_SetResolution(caPL110_Modes[0].W, caPL110_Modes[0].H);
\r
116 DevFS_AddDevice( &gPL110_DriverStruct );
\r
122 * \brief Clean up resources for driver unloading
\r
124 void PL110_Uninstall()
\r
129 * \brief Read from the framebuffer
\r
131 Uint64 PL110_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)
\r
137 * \brief Write to the framebuffer
\r
139 Uint64 PL110_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
\r
141 gPL110_DrvUtil_BufInfo.BufferFormat = giPL110_BufferMode;
\r
142 return DrvUtil_Video_WriteLFB(&gPL110_DrvUtil_BufInfo, Offset, Length, Buffer);
\r
145 const char *csaPL110_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};
\r
148 * \brief Handle messages to the device
\r
150 int PL110_IOCtl(tVFS_Node *Node, int ID, void *Data)
\r
153 ENTER("pNode iID pData", Node, ID, Data);
\r
157 BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaPL110_IOCtls);
\r
159 case VIDEO_IOCTL_SETBUFFORMAT:
\r
160 DrvUtil_Video_RemoveCursor( &gPL110_DrvUtil_BufInfo );
\r
161 ret = giPL110_BufferMode;
\r
162 if(Data) giPL110_BufferMode = *(int*)Data;
\r
163 if(gPL110_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)
\r
164 DrvUtil_Video_SetCursor( &gPL110_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor );
\r
167 case VIDEO_IOCTL_GETSETMODE:
\r
172 if( !CheckMem(Data, sizeof(int)) )
\r
173 LEAVE_RET('i', -1);
\r
175 newMode = *(int*)Data;
\r
177 if(newMode < 0 || newMode >= ciPL110_ModeCount)
\r
178 LEAVE_RET('i', -1);
\r
180 if(newMode != giPL110_CurrentMode)
\r
182 giPL110_CurrentMode = newMode;
\r
183 PL110_int_SetResolution( caPL110_Modes[newMode].W, caPL110_Modes[newMode].H );
\r
186 ret = giPL110_CurrentMode;
\r
189 case VIDEO_IOCTL_FINDMODE:
\r
191 tVideo_IOCtl_Mode *mode = Data;
\r
192 int closest, closestArea, reqArea = 0;
\r
193 if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))
\r
194 LEAVE_RET('i', -1);
\r
195 if( mode->bpp != 32 )
\r
197 if( mode->flags != 0 )
\r
202 for( int i = 0; i < ciPL110_ModeCount; i ++ )
\r
205 if(mode->width == caPL110_Modes[i].W && mode->height == caPL110_Modes[i].H) {
\r
211 area = caPL110_Modes[i].W * caPL110_Modes[i].H;
\r
213 reqArea = mode->width * mode->height;
\r
215 closestArea = area;
\r
217 else if( ABS(area - reqArea) < ABS(closestArea - reqArea) ) {
\r
219 closestArea = area;
\r
225 mode->id = closest;
\r
228 mode->width = caPL110_Modes[mode->id].W;
\r
229 mode->height = caPL110_Modes[mode->id].H;
\r
233 case VIDEO_IOCTL_MODEINFO:
\r
235 tVideo_IOCtl_Mode *mode = Data;
\r
236 if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))
\r
237 LEAVE_RET('i', -1);
\r
238 if(mode->id < 0 || mode->id >= ciPL110_ModeCount)
\r
244 mode->width = caPL110_Modes[mode->id].W;
\r
245 mode->height = caPL110_Modes[mode->id].H;
\r
251 case VIDEO_IOCTL_SETCURSOR:
\r
252 if( !Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Pos)) )
\r
253 LEAVE_RET('i', -1);
\r
255 DrvUtil_Video_RemoveCursor( &gPL110_DrvUtil_BufInfo );
\r
257 gPL110_CursorPos = *(tVideo_IOCtl_Pos*)Data;
\r
258 if(gPL110_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)
\r
259 DrvUtil_Video_DrawCursor(
\r
260 &gPL110_DrvUtil_BufInfo,
\r
261 gPL110_CursorPos.x*giVT_CharWidth,
\r
262 gPL110_CursorPos.y*giVT_CharHeight
\r
265 DrvUtil_Video_DrawCursor(
\r
266 &gPL110_DrvUtil_BufInfo,
\r
267 gPL110_CursorPos.x,
\r
286 * \brief Set the LCD controller resolution
\r
287 * \param W Width (aligned to 16 pixels, cipped to 1024)
\r
288 * \param H Height (clipped to 768)
\r
289 * \return Boolean failure
\r
291 int PL110_int_SetResolution(int W, int H)
\r
293 W = (W + 15) & ~0xF;
\r
294 if(W <= 0 || H <= 0) {
\r
295 Log_Warning("PL110", "Attempted to set invalid resolution (%ix%i)", W, H);
\r
298 if(W > 1024) W = 1024;
\r
299 if(H > 768) H = 768;
\r
301 gpPL110_IOMem->LCDTiming0 = ((W/16)-1) << 2;
\r
302 gpPL110_IOMem->LCDTiming1 = H-1;
\r
303 gpPL110_IOMem->LCDTiming2 = (14 << 27);
\r
304 gpPL110_IOMem->LCDTiming3 = 0;
\r
306 if( gpPL110_Framebuffer ) {
\r
307 MM_UnmapHWPages((tVAddr)gpPL110_Framebuffer, (giPL110_FramebufferSize+0xFFF)>>12);
\r
309 giPL110_FramebufferSize = W*H*4;
\r
311 gpPL110_Framebuffer = (void*)MM_AllocDMA( (giPL110_FramebufferSize+0xFFF)>>12, 32, &gPL110_FramebufferPhys );
\r
312 gpPL110_IOMem->LCDUPBase = gPL110_FramebufferPhys;
\r
313 gpPL110_IOMem->LCDLPBase = 0;
\r
315 // Power on, BGR mode, ???, ???, enabled
\r
316 Uint32 controlWord = (1 << 11)|(1 << 8)|(1 << 5)|(5 << 1)|1;
\r
317 // According to qemu, the Versatile version has these two the wrong
\r
319 if( gbPL110_IsVersatile )
\r
321 gpPL110_IOMem->LCDIMSC = controlWord; // Actually LCDControl
\r
322 gpPL110_IOMem->LCDControl = 0; // Actually LCDIMSC
\r
326 gpPL110_IOMem->LCDIMSC = 0;
\r
327 gpPL110_IOMem->LCDControl = controlWord;
\r
331 giPL110_Height = H;
\r
333 // Update the DrvUtil buffer info
\r
334 gPL110_DrvUtil_BufInfo.Framebuffer = gpPL110_Framebuffer;
\r
335 gPL110_DrvUtil_BufInfo.Pitch = W * 4;
\r
336 gPL110_DrvUtil_BufInfo.Width = W;
\r
337 gPL110_DrvUtil_BufInfo.Height = H;
\r
338 gPL110_DrvUtil_BufInfo.Depth = 32;
\r