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

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