Merge branch 'master' of git://git.ucc.asn.au/tpg/acess2
[tpg/acess2.git] / Modules / Display / PL110 / main.c
1 /**\r
2  * Acess2 ARM PrimeCell Colour LCD Controller (PL110) Driver\r
3  * - By John Hodge (thePowersGang)\r
4  *\r
5  * main.c\r
6  * - Driver core\r
7  *\r
8  *\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
11  */\r
12 #define DEBUG   0\r
13 #define VERSION ((0<<8)|10)\r
14 #include <acess.h>\r
15 #include <errno.h>\r
16 #include <modules.h>\r
17 #include <vfs.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
23 \r
24 #define ABS(a)  ((a)>0?(a):-(a))\r
25 \r
26 // === TYPEDEFS ===\r
27 typedef struct sPL110   tPL110;\r
28 \r
29 struct sPL110\r
30 {\r
31         Uint32  LCDTiming0;\r
32         Uint32  LCDTiming1;\r
33         Uint32  LCDTiming2;\r
34         Uint32  LCDTiming3;\r
35         \r
36         Uint32  LCDUPBase;\r
37         Uint32  LCDLPBase;\r
38         Uint32  LCDIMSC;\r
39         Uint32  LCDControl;\r
40         Uint32  LCDRIS;\r
41         Uint32  LCDMIS;\r
42         Uint32  LCDICR;\r
43         Uint32  LCDUPCurr;\r
44         Uint32  LCDLPCurr;\r
45 };\r
46 \r
47 #ifndef PL110_BASE\r
48 #define PL110_BASE      0x10020000      // Integrator\r
49 #endif\r
50 \r
51 // === CONSTANTS ===\r
52 const struct {\r
53         short W, H;\r
54 }       caPL110_Modes[] = {\r
55         {640,480},\r
56         {800,600},\r
57         {1024,768}      // MAX\r
58 };\r
59 const int       ciPL110_ModeCount = sizeof(caPL110_Modes)/sizeof(caPL110_Modes[0]);\r
60 \r
61 // === PROTOTYPES ===\r
62 // Driver\r
63  int    PL110_Install(char **Arguments);\r
64 void    PL110_Uninstall();\r
65 // Internal\r
66 // Filesystem\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, const void *buffer);\r
69  int    PL110_IOCtl(tVFS_Node *node, int id, void *data);\r
70 // -- Internals\r
71  int    PL110_int_SetResolution(int W, int H);\r
72 \r
73 // === GLOBALS ===\r
74 MODULE_DEFINE(0, VERSION, PL110, PL110_Install, NULL, NULL);\r
75 tVFS_NodeType   gPL110_DevNodeType = {\r
76         .Read = PL110_Read,\r
77         .Write = PL110_Write,\r
78         .IOCtl = PL110_IOCtl\r
79         };\r
80 tDevFS_Driver   gPL110_DriverStruct = {\r
81         NULL, "PL110",\r
82         {.Type = &gPL110_DevNodeType}\r
83 };\r
84 // -- Options\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
89         NULL,\r
90         {\r
91                 {"Base", "P", &gPL110_PhysBase},\r
92                 {"IsVersatile", "i", &gbPL110_IsVersatile},\r
93                 {NULL, NULL, NULL}\r
94         }\r
95 };\r
96 // -- Driver state\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
105 // -- Misc\r
106 tDrvUtil_Video_BufInfo  gPL110_DrvUtil_BufInfo;\r
107 tVideo_IOCtl_Pos        gPL110_CursorPos;\r
108 \r
109 // === CODE ===\r
110 /**\r
111  */\r
112 int PL110_Install(char **Arguments)\r
113 {\r
114 //      KeyVal_Parse(&gPL110_KeyValueParser, Arguments);\r
115         \r
116         gpPL110_IOMem = (void*)MM_MapHWPages(gPL110_PhysBase, 1);\r
117 \r
118         PL110_int_SetResolution(caPL110_Modes[0].W, caPL110_Modes[0].H);\r
119 \r
120         DevFS_AddDevice( &gPL110_DriverStruct );\r
121 \r
122         return 0;\r
123 }\r
124 \r
125 /**\r
126  * \brief Clean up resources for driver unloading\r
127  */\r
128 void PL110_Uninstall()\r
129 {\r
130 }\r
131 \r
132 /**\r
133  * \brief Read from the framebuffer\r
134  */\r
135 Uint64 PL110_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
136 {\r
137         return 0;\r
138 }\r
139 \r
140 /**\r
141  * \brief Write to the framebuffer\r
142  */\r
143 Uint64 PL110_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)\r
144 {\r
145         gPL110_DrvUtil_BufInfo.BufferFormat = giPL110_BufferMode;\r
146         return DrvUtil_Video_WriteLFB(&gPL110_DrvUtil_BufInfo, Offset, Length, Buffer);\r
147 }\r
148 \r
149 const char *csaPL110_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};\r
150 \r
151 /**\r
152  * \brief Handle messages to the device\r
153  */\r
154 int PL110_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
155 {\r
156          int    ret = -2;\r
157         ENTER("pNode iID pData", Node, ID, Data);\r
158         \r
159         switch(ID)\r
160         {\r
161         BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaPL110_IOCtls);\r
162 \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
169                 break;\r
170         \r
171         case VIDEO_IOCTL_GETSETMODE:\r
172                 if(Data)\r
173                 {\r
174                          int    newMode;\r
175                         \r
176                         if( !CheckMem(Data, sizeof(int)) )\r
177                                 LEAVE_RET('i', -1);\r
178                         \r
179                         newMode = *(int*)Data;\r
180                         \r
181                         if(newMode < 0 || newMode >= ciPL110_ModeCount)\r
182                                 LEAVE_RET('i', -1);\r
183 \r
184                         if(newMode != giPL110_CurrentMode)\r
185                         {\r
186                                 giPL110_CurrentMode = newMode;\r
187                                 PL110_int_SetResolution( caPL110_Modes[newMode].W, caPL110_Modes[newMode].H );\r
188                         }\r
189                 }\r
190                 ret = giPL110_CurrentMode;\r
191                 break;\r
192         \r
193         case VIDEO_IOCTL_FINDMODE:\r
194                 {\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
200                         LEAVE_RET('i', 0);\r
201                 if( mode->flags != 0 )\r
202                         LEAVE_RET('i', 0);\r
203 \r
204                 ret = 0;\r
205 \r
206                 for( int i = 0; i < ciPL110_ModeCount; i ++ )\r
207                 {\r
208                          int    area;\r
209                         if(mode->width == caPL110_Modes[i].W && mode->height == caPL110_Modes[i].H) {\r
210                                 mode->id = i;\r
211                                 ret = 1;\r
212                                 break;\r
213                         }\r
214                         \r
215                         area = caPL110_Modes[i].W * caPL110_Modes[i].H;\r
216                         if(!reqArea) {\r
217                                 reqArea = mode->width * mode->height;\r
218                                 closest = i;\r
219                                 closestArea = area;\r
220                         }\r
221                         else if( ABS(area - reqArea) < ABS(closestArea - reqArea) ) {\r
222                                 closest = i;\r
223                                 closestArea = area;\r
224                         }\r
225                 }\r
226                 \r
227                 if( ret == 0 )\r
228                 {\r
229                         mode->id = closest;\r
230                         ret = 1;\r
231                 }\r
232                 mode->width = caPL110_Modes[mode->id].W;\r
233                 mode->height = caPL110_Modes[mode->id].H;\r
234                 break;\r
235                 }\r
236         \r
237         case VIDEO_IOCTL_MODEINFO:\r
238                 {\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
243                         LEAVE_RET('i', 0);\r
244                 \r
245 \r
246                 mode->bpp = 32;\r
247                 mode->flags = 0;\r
248                 mode->width = caPL110_Modes[mode->id].W;\r
249                 mode->height = caPL110_Modes[mode->id].H;\r
250 \r
251                 ret = 1;\r
252                 break;\r
253                 }\r
254         \r
255         case VIDEO_IOCTL_SETCURSOR:\r
256                 if( !Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Pos)) )\r
257                         LEAVE_RET('i', -1);\r
258 \r
259                 DrvUtil_Video_RemoveCursor( &gPL110_DrvUtil_BufInfo );\r
260                 \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
267                                 );\r
268                 else\r
269                         DrvUtil_Video_DrawCursor(\r
270                                 &gPL110_DrvUtil_BufInfo,\r
271                                 gPL110_CursorPos.x,\r
272                                 gPL110_CursorPos.y\r
273                                 );\r
274                 break;\r
275         \r
276         default:\r
277                 LEAVE('i', -2);\r
278                 return -2;\r
279         }\r
280         \r
281         LEAVE('i', ret);\r
282         return ret;\r
283 }\r
284 \r
285 //\r
286 //\r
287 //\r
288 \r
289 /**\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
294  */\r
295 int PL110_int_SetResolution(int W, int H)\r
296 {\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
300                 return 1;\r
301         }\r
302         if(W > 1024)    W = 1024;\r
303         if(H > 768)     H = 768;\r
304 \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
309 \r
310         if( gpPL110_Framebuffer ) {\r
311                 MM_UnmapHWPages((tVAddr)gpPL110_Framebuffer, (giPL110_FramebufferSize+0xFFF)>>12);\r
312         }\r
313         giPL110_FramebufferSize = W*H*4;\r
314 \r
315         gpPL110_Framebuffer = (void*)MM_AllocDMA( (giPL110_FramebufferSize+0xFFF)>>12, 32, &gPL110_FramebufferPhys );\r
316         gpPL110_IOMem->LCDUPBase = gPL110_FramebufferPhys;\r
317         gpPL110_IOMem->LCDLPBase = 0;\r
318 \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
322         // way around\r
323         if( gbPL110_IsVersatile )\r
324         {\r
325                 gpPL110_IOMem->LCDIMSC = controlWord;   // Actually LCDControl\r
326                 gpPL110_IOMem->LCDControl = 0;  // Actually LCDIMSC\r
327         }\r
328         else\r
329         {\r
330                 gpPL110_IOMem->LCDIMSC = 0;\r
331                 gpPL110_IOMem->LCDControl = controlWord;\r
332         }\r
333 \r
334         giPL110_Width = W;\r
335         giPL110_Height = H;\r
336 \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
343         \r
344         return 0;\r
345 }\r

UCC git Repository :: git.ucc.asn.au