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

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