Multiple fixes
[tpg/acess2.git] / Modules / Display / BochsGA / bochsvbe.c
1 /**\r
2  * \file drv_bochsvbe.c\r
3  * \brief BGA (Bochs Graphic Adapter) Driver\r
4  * \note for Acess2\r
5  * \warning This driver does NOT support the Bochs PCI VGA driver\r
6 */\r
7 #define DEBUG   0\r
8 #include <acess.h>\r
9 #include <errno.h>\r
10 #include <modules.h>\r
11 #include <vfs.h>\r
12 #include <fs_devfs.h>\r
13 #include <drv_pci.h>\r
14 #include <tpl_drv_video.h>\r
15 \r
16 #define INT\r
17 \r
18 // === TYPEDEFS ===\r
19 typedef struct {\r
20         Uint16  width;\r
21         Uint16  height;\r
22         Uint16  bpp;\r
23         Uint16  flags;\r
24         Uint32  fbSize;\r
25 } t_bga_mode;\r
26 \r
27 // === CONSTANTS ===\r
28 enum eMode_Flags {\r
29         MODEFLAG_TEXT = 1\r
30 };\r
31 #define BGA_LFB_MAXSIZE (1024*768*4)\r
32 #define VBE_DISPI_BANK_ADDRESS  0xA0000\r
33 #define VBE_DISPI_LFB_PHYSICAL_ADDRESS  0xE0000000\r
34 #define VBE_DISPI_IOPORT_INDEX  0x01CE\r
35 #define VBE_DISPI_IOPORT_DATA   0x01CF\r
36 #define VBE_DISPI_DISABLED      0x00\r
37 #define VBE_DISPI_ENABLED       0x01\r
38 #define VBE_DISPI_LFB_ENABLED   0x40\r
39 #define VBE_DISPI_NOCLEARMEM    0x80\r
40 enum {\r
41         VBE_DISPI_INDEX_ID,\r
42         VBE_DISPI_INDEX_XRES,\r
43         VBE_DISPI_INDEX_YRES,\r
44         VBE_DISPI_INDEX_BPP,\r
45         VBE_DISPI_INDEX_ENABLE,\r
46         VBE_DISPI_INDEX_BANK,\r
47         VBE_DISPI_INDEX_VIRT_WIDTH,\r
48         VBE_DISPI_INDEX_VIRT_HEIGHT,\r
49         VBE_DISPI_INDEX_X_OFFSET,\r
50         VBE_DISPI_INDEX_Y_OFFSET\r
51 };\r
52 \r
53 \r
54 // === PROTOTYPES ===\r
55 // Driver\r
56  int    BGA_Install(char **Arguments);\r
57 void    BGA_Uninstall();\r
58 // Internal\r
59 void    BGA_int_WriteRegister(Uint16 reg, Uint16 value);\r
60 Uint16  BGA_int_ReadRegister(Uint16 reg);\r
61 void    BGA_int_SetBank(Uint16 bank);\r
62 void    BGA_int_SetMode(Uint16 width, Uint16 height);\r
63  int    BGA_int_UpdateMode(int id);\r
64  int    BGA_int_FindMode(tVideo_IOCtl_Mode *info);\r
65  int    BGA_int_ModeInfo(tVideo_IOCtl_Mode *info);\r
66  int    BGA_int_MapFB(void *Dest);\r
67 // Filesystem\r
68 Uint64  BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);\r
69 Uint64  BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);\r
70  int    BGA_Ioctl(tVFS_Node *node, int id, void *data);\r
71 \r
72 // === GLOBALS ===\r
73 MODULE_DEFINE(0, 0x0032, BochsGA, BGA_Install, NULL, "PCI", NULL);\r
74 tDevFS_Driver   gBGA_DriverStruct = {\r
75         NULL, "BochsGA",\r
76         {\r
77         .Read = BGA_Read,\r
78         .Write = BGA_Write,\r
79         .IOCtl = BGA_Ioctl\r
80         }\r
81 };\r
82  int    giBGA_CurrentMode = -1;\r
83 tVideo_IOCtl_Pos        gBGA_CursorPos = {-1,-1};\r
84  int    giBGA_DriverId = -1;\r
85 Uint    *gBGA_Framebuffer;\r
86 t_bga_mode      gBGA_Modes[] = {\r
87         {},\r
88         { 80,25, 12, MODEFLAG_TEXT, 80*25*8},   // 640 x 480\r
89         {100,37, 12, MODEFLAG_TEXT, 100*37*8},  // 800 x 600\r
90         {640,480,8, 0, 640*480},\r
91         {640,480,32, 0, 640*480*4},\r
92         {800,600,8, 0, 800*600},\r
93         {800,600,32, 0, 800*600*4},\r
94 };\r
95 #define BGA_MODE_COUNT  (sizeof(gBGA_Modes)/sizeof(gBGA_Modes[0]))\r
96 \r
97 // === CODE ===\r
98 /**\r
99  * \fn int BGA_Install(char **Arguments)\r
100  */\r
101 int BGA_Install(char **Arguments)\r
102 {\r
103          int    bga_version = 0;\r
104         \r
105         // Check BGA Version\r
106         bga_version = BGA_int_ReadRegister(VBE_DISPI_INDEX_ID);\r
107         // NOTE: This driver was written for 0xB0C4, but they seem to be backwards compatable\r
108         if(bga_version < 0xB0C4 || bga_version > 0xB0C5) {\r
109                 Warning("[BGA ] Bochs Adapter Version is not 0xB0C4 or 0xB0C5, instead 0x%x", bga_version);\r
110                 return MODULE_ERR_NOTNEEDED;\r
111         }\r
112         \r
113         // Install Device\r
114         giBGA_DriverId = DevFS_AddDevice( &gBGA_DriverStruct );\r
115         if(giBGA_DriverId == -1) {\r
116                 Warning("[BGA ] Unable to register with DevFS, maybe already loaded?");\r
117                 return MODULE_ERR_MISC;\r
118         }\r
119         \r
120         // Map Framebuffer to hardware address\r
121         gBGA_Framebuffer = (void *) MM_MapHWPage(VBE_DISPI_LFB_PHYSICAL_ADDRESS, 768);  // 768 pages (3Mb)\r
122         \r
123         return MODULE_ERR_OK;\r
124 }\r
125 \r
126 /**\r
127  * \fn void BGA_Uninstall()\r
128  */\r
129 void BGA_Uninstall()\r
130 {\r
131         //DevFS_DelDevice( giBGA_DriverId );\r
132         MM_UnmapHWPage( VBE_DISPI_LFB_PHYSICAL_ADDRESS, 768 );\r
133 }\r
134 \r
135 /**\r
136  * \fn Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
137  * \brief Read from the framebuffer\r
138  */\r
139 Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
140 {\r
141         // Check Mode\r
142         if(giBGA_CurrentMode == -1)     return -1;\r
143         \r
144         // Check Offset and Length against Framebuffer Size\r
145         if(off+len > gBGA_Modes[giBGA_CurrentMode].fbSize)\r
146                 return -1;\r
147         \r
148         // Copy from Framebuffer\r
149         memcpy(buffer, (void*)((Uint)gBGA_Framebuffer + (Uint)off), len);\r
150         return len;\r
151 }\r
152 \r
153 /**\r
154  * \fn Uint64 BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
155  * \brief Write to the framebuffer\r
156  */\r
157 Uint64 BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *Buffer)\r
158 {       \r
159         ENTER("xoff xlen", off, len);\r
160         \r
161         // Check Mode\r
162         if(giBGA_CurrentMode == -1) {\r
163                 LEAVE('i', -1);\r
164                 return -1;\r
165         }\r
166         \r
167         // Check Input against Frambuffer Size\r
168         if(off + len > gBGA_Modes[giBGA_CurrentMode].fbSize) {\r
169                 LEAVE('i', -1);\r
170                 return -1;\r
171         }\r
172         \r
173         // Text Mode\r
174         if( gBGA_Modes[giBGA_CurrentMode].flags & MODEFLAG_TEXT )\r
175         {\r
176                 tVT_Char        *chars = Buffer;\r
177                  int    pitch = gBGA_Modes[giBGA_CurrentMode].width * giVT_CharWidth;\r
178                  int    x, y;\r
179                 Uint32  *dest;\r
180                 \r
181                 off /= sizeof(tVT_Char);\r
182                 dest = (void*)gBGA_Framebuffer;\r
183                 x = (off % gBGA_Modes[giBGA_CurrentMode].width) * giVT_CharWidth;\r
184                 y = (off / gBGA_Modes[giBGA_CurrentMode].width) * giVT_CharHeight;\r
185                 dest += y * pitch;\r
186                 dest += x * giVT_CharWidth;\r
187                 len /= sizeof(tVT_Char);\r
188                 while(len--)\r
189                 {\r
190                         VT_Font_Render(\r
191                                 chars->Ch,\r
192                                 dest, pitch,\r
193                                 VT_Colour12to24(chars->BGCol),\r
194                                 VT_Colour12to24(chars->FGCol)\r
195                                 );\r
196                         \r
197                         dest += giVT_CharWidth;\r
198                         \r
199                         chars ++;\r
200                         x += giVT_CharWidth;\r
201                         if( x >= pitch ) {\r
202                                 x = 0;\r
203                                 y += giVT_CharHeight;\r
204                                 dest += pitch*(giVT_CharHeight-1);\r
205                         }\r
206                 }\r
207         }\r
208         else\r
209         {\r
210                 Uint8   *destBuf = (Uint8*) ((Uint)gBGA_Framebuffer + (Uint)off);\r
211                 \r
212                 Log("buffer = %p", Buffer);\r
213                 Log("Updating Framebuffer (%p to %p)",\r
214                         destBuf, destBuf + (Uint)len);\r
215                 \r
216                 \r
217                 // Copy to Frambuffer\r
218                 memcpy(destBuf, Buffer, len);\r
219                 \r
220                 Log("BGA Framebuffer updated");\r
221         }\r
222         \r
223         LEAVE('i', len);\r
224         return len;\r
225 }\r
226 \r
227 /**\r
228  * \fn INT int BGA_Ioctl(tVFS_Node *node, int ID, void *Data)\r
229  * \brief Handle messages to the device\r
230  */\r
231 INT int BGA_Ioctl(tVFS_Node *node, int ID, void *Data)\r
232 {\r
233          int    ret = -2;\r
234         ENTER("pNode iId pData", node, ID, Data);\r
235         \r
236         switch(ID)\r
237         {\r
238         case DRV_IOCTL_TYPE:\r
239                 ret = DRV_TYPE_VIDEO;\r
240                 break;\r
241         case DRV_IOCTL_IDENT:\r
242                 memcpy(Data, "BGA1", 4);\r
243                 ret = 1;\r
244                 break;\r
245         case DRV_IOCTL_VERSION:\r
246                 ret = 0x100;\r
247                 break;\r
248         case DRV_IOCTL_LOOKUP:  // TODO: Implement\r
249                 ret = 0;\r
250                 break;\r
251                 \r
252         case VIDEO_IOCTL_GETSETMODE:\r
253                 if( Data )\r
254                         ret = BGA_int_UpdateMode(*(int*)(Data));\r
255                 else\r
256                         ret = giBGA_CurrentMode;\r
257                 break;\r
258         \r
259         case VIDEO_IOCTL_FINDMODE:\r
260                 ret = BGA_int_FindMode((tVideo_IOCtl_Mode*)Data);\r
261                 break;\r
262         \r
263         case VIDEO_IOCTL_MODEINFO:\r
264                 ret = BGA_int_ModeInfo((tVideo_IOCtl_Mode*)Data);\r
265                 break;\r
266         \r
267         // Request Access to LFB\r
268         case VIDEO_IOCTL_REQLFB:\r
269                 ret = BGA_int_MapFB( *(void**)Data );\r
270                 break;\r
271         \r
272         case VIDEO_IOCTL_SETCURSOR:\r
273                 gBGA_CursorPos.x = ((tVideo_IOCtl_Pos*)Data)->x;\r
274                 gBGA_CursorPos.y = ((tVideo_IOCtl_Pos*)Data)->y;\r
275                 break;\r
276         \r
277         default:\r
278                 LEAVE('i', -2);\r
279                 return -2;\r
280         }\r
281         \r
282         LEAVE('i', ret);\r
283         return ret;\r
284 }\r
285 \r
286 //== Internal Functions ==\r
287 /**\r
288  * \fn void BGA_int_WriteRegister(Uint16 reg, Uint16 value)\r
289  * \brief Writes to a BGA register\r
290  */\r
291 void BGA_int_WriteRegister(Uint16 reg, Uint16 value)\r
292 {\r
293         outw(VBE_DISPI_IOPORT_INDEX, reg);\r
294         outw(VBE_DISPI_IOPORT_DATA, value);\r
295 }\r
296 \r
297 INT Uint16 BGA_int_ReadRegister(Uint16 reg)\r
298 {\r
299         outw(VBE_DISPI_IOPORT_INDEX, reg);\r
300         return inw(VBE_DISPI_IOPORT_DATA);\r
301 }\r
302 \r
303 #if 0\r
304 INT void BGA_int_SetBank(Uint16 bank)\r
305 {\r
306         BGA_int_WriteRegister(VBE_DISPI_INDEX_BANK, bank);\r
307 }\r
308 #endif\r
309 \r
310 /**\r
311  * \fn void BGA_int_SetMode(Uint16 width, Uint16 height, Uint16 bpp)\r
312  * \brief Sets the video mode from the dimensions and bpp given\r
313  */\r
314 void BGA_int_SetMode(Uint16 width, Uint16 height)\r
315 {\r
316         ENTER("iwidth iheight ibpp", width, height, bpp);\r
317         BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);\r
318     BGA_int_WriteRegister(VBE_DISPI_INDEX_XRES, width);\r
319     BGA_int_WriteRegister(VBE_DISPI_INDEX_YRES, height);\r
320     BGA_int_WriteRegister(VBE_DISPI_INDEX_BPP,  32);\r
321     BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM | VBE_DISPI_LFB_ENABLED);\r
322     //BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM);\r
323         LEAVE('-');\r
324 }\r
325 \r
326 /**\r
327  * \fn int BGA_int_UpdateMode(int id)\r
328  * \brief Set current vide mode given a mode id\r
329  */\r
330 int BGA_int_UpdateMode(int id)\r
331 {\r
332         // Sanity Check\r
333         if(id < 0 || id >= BGA_MODE_COUNT)      return -1;\r
334         \r
335         // Check if it is a text mode\r
336         if( gBGA_Modes[id].flags & MODEFLAG_TEXT )\r
337                 BGA_int_SetMode(\r
338                         gBGA_Modes[id].width*giVT_CharWidth,\r
339                         gBGA_Modes[id].height*giVT_CharHeight);\r
340         else    // Graphics?\r
341                 BGA_int_SetMode(\r
342                         gBGA_Modes[id].width,\r
343                         gBGA_Modes[id].height);\r
344         \r
345         giBGA_CurrentMode = id;\r
346         return id;\r
347 }\r
348 \r
349 /**\r
350  * \fn int BGA_int_FindMode(tVideo_IOCtl_Mode *info)\r
351  * \brief Find a mode matching the given options\r
352  */\r
353 int BGA_int_FindMode(tVideo_IOCtl_Mode *info)\r
354 {\r
355          int    i;\r
356          int    best = 0, bestFactor = 1000;\r
357          int    factor, tmp;\r
358          int    rqdProduct = info->width * info->height * info->bpp;\r
359         \r
360         ENTER("pinfo", info);\r
361         LOG("info = {width:%i,height:%i,bpp:%i})\n", info->width, info->height, info->bpp);\r
362         \r
363         for(i = 0; i < BGA_MODE_COUNT; i++)\r
364         {\r
365                 #if DEBUG >= 2\r
366                 LogF("Mode %i (%ix%ix%i), ", i, gBGA_Modes[i].width, gBGA_Modes[i].height, gBGA_Modes[i].bpp);\r
367                 #endif\r
368                 \r
369                 // Check if this mode is the same type as what we want\r
370                 if( !(gBGA_Modes[i].flags & MODEFLAG_TEXT) != !(info->flags & VIDEO_FLAG_TEXT) )\r
371                         continue;\r
372                 \r
373                 // Ooh! A perfect match\r
374                 if(gBGA_Modes[i].width == info->width\r
375                 && gBGA_Modes[i].height == info->height\r
376                 && gBGA_Modes[i].bpp == info->bpp)\r
377                 {\r
378                         #if DEBUG >= 2\r
379                         LogF("Perfect!\n");\r
380                         #endif\r
381                         best = i;\r
382                         break;\r
383                 }\r
384                 \r
385                 // If not, how close are we?\r
386                 tmp = gBGA_Modes[i].width * gBGA_Modes[i].height * gBGA_Modes[i].bpp;\r
387                 tmp -= rqdProduct;\r
388                 tmp = tmp < 0 ? -tmp : tmp;     // tmp = ABS(tmp)\r
389                 factor = tmp * 100 / rqdProduct;\r
390                 \r
391                 #if DEBUG >= 2\r
392                 LogF("factor = %i\n", factor);\r
393                 #endif\r
394                 \r
395                 if(factor < bestFactor)\r
396                 {\r
397                         bestFactor = factor;\r
398                         best = i;\r
399                 }\r
400         }\r
401         \r
402         info->id = best;\r
403         info->width = gBGA_Modes[best].width;\r
404         info->height = gBGA_Modes[best].height;\r
405         info->bpp = gBGA_Modes[best].bpp;\r
406         \r
407         info->flags = 0;\r
408         if(gBGA_Modes[best].flags & MODEFLAG_TEXT)\r
409                 info->flags |= VIDEO_FLAG_TEXT;\r
410         \r
411         return best;\r
412 }\r
413 \r
414 /**\r
415  * \fn int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)\r
416  * \brief Get mode information\r
417  */\r
418 int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)\r
419 {\r
420         // Sanity Check\r
421         //if( !MM_IsUser( (Uint)info, sizeof(tVideo_IOCtl_Mode) ) ) {\r
422         //      return -EINVAL;\r
423         //}\r
424         \r
425         if(info->id < 0 || info->id >= BGA_MODE_COUNT)  return -1;\r
426         \r
427         info->width = gBGA_Modes[info->id].width;\r
428         info->height = gBGA_Modes[info->id].height;\r
429         info->bpp = gBGA_Modes[info->id].bpp;\r
430         \r
431         info->flags = 0;\r
432         if(gBGA_Modes[info->id].flags & MODEFLAG_TEXT)\r
433                 info->flags |= VIDEO_FLAG_TEXT;\r
434         \r
435         return 1;\r
436 }\r
437 \r
438 /**\r
439  * \fn int BGA_int_MapFB(void *Dest)\r
440  * \brief Map the framebuffer into a process's space\r
441  * \param Dest  User address to load to\r
442  */\r
443 int BGA_int_MapFB(void *Dest)\r
444 {\r
445         Uint    i;\r
446         Uint    pages;\r
447         \r
448         // Sanity Check\r
449         if((Uint)Dest > 0xC0000000)     return 0;\r
450         if(gBGA_Modes[giBGA_CurrentMode].bpp < 15)      return 0;       // Only non-pallete modes are supported\r
451         \r
452         // Count required pages\r
453         pages = (gBGA_Modes[giBGA_CurrentMode].fbSize + 0xFFF) >> 12;\r
454         \r
455         // Check if there is space\r
456         for( i = 0; i < pages; i++ )\r
457         {\r
458                 if(MM_GetPhysAddr( (Uint)Dest + (i << 12) ))\r
459                         return 0;\r
460         }\r
461         \r
462         // Map\r
463         for( i = 0; i < pages; i++ )\r
464                 MM_Map( (Uint)Dest + (i<<12), VBE_DISPI_LFB_PHYSICAL_ADDRESS + (i<<12) );\r
465         \r
466         return 1;\r
467 }\r

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