1 // Hooks for via vgabios calls into main bios.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 //
5 // This file may be distributed under the terms of the GNU LGPLv3 license.
6 
7 #include "biosvar.h" // GET_GLOBAL
8 #include "bregs.h" // set_code_invalid
9 #include "config.h" // CONFIG_*
10 #include "hw/pci.h" // pci_config_readb
11 #include "hw/pcidevice.h" // pci_find_device
12 #include "hw/pci_ids.h" // PCI_VENDOR_ID_VIA
13 #include "hw/pci_regs.h" // PCI_VENDOR_ID
14 #include "output.h" // dprintf
15 #include "string.h" // strcmp
16 #include "util.h" // handle_155f, handle_157f
17 
18 #define VH_VIA 1
19 #define VH_INTEL 2
20 #define VH_SMI 3
21 
22 int VGAHookHandlerType VARFSEG;
23 
24 static void
handle_155fXX(struct bregs * regs)25 handle_155fXX(struct bregs *regs)
26 {
27     set_code_unimplemented(regs, RET_EUNSUPPORTED);
28 }
29 
30 static void
handle_157fXX(struct bregs * regs)31 handle_157fXX(struct bregs *regs)
32 {
33     set_code_unimplemented(regs, RET_EUNSUPPORTED);
34 }
35 
36 /****************************************************************
37  * Via hooks
38  ****************************************************************/
39 
40 int ViaFBsize VARFSEG, ViaRamSpeed VARFSEG;
41 
42 static void
via_155f01(struct bregs * regs)43 via_155f01(struct bregs *regs)
44 {
45     regs->eax = 0x5f;
46     regs->cl = 2; // panel type =  2 = 1024 * 768
47     set_success(regs);
48     dprintf(1, "Warning: VGA panel type is hardcoded\n");
49 }
50 
51 static void
via_155f02(struct bregs * regs)52 via_155f02(struct bregs *regs)
53 {
54     regs->eax = 0x5f;
55     regs->bx = 2;
56     regs->cx = 0x401;  // PAL + crt only
57     regs->dx = 0;  // TV Layout - default
58     set_success(regs);
59     dprintf(1, "Warning: VGA TV/CRT output type is hardcoded\n");
60 }
61 
62 static void
via_155f18(struct bregs * regs)63 via_155f18(struct bregs *regs)
64 {
65     int fbsize = GET_GLOBAL(ViaFBsize), ramspeed = GET_GLOBAL(ViaRamSpeed);
66     if (fbsize < 0 || ramspeed < 0) {
67         set_code_invalid(regs, RET_EUNSUPPORTED);
68         return;
69     }
70     regs->eax = 0x5f;
71     regs->ebx = 0x500 | (ramspeed << 4) | fbsize;
72     regs->ecx = 0x060;
73     set_success(regs);
74 }
75 
76 static void
via_155f19(struct bregs * regs)77 via_155f19(struct bregs *regs)
78 {
79     set_invalid_silent(regs);
80 }
81 
82 static void
via_155f(struct bregs * regs)83 via_155f(struct bregs *regs)
84 {
85     switch (regs->al) {
86     case 0x01: via_155f01(regs); break;
87     case 0x02: via_155f02(regs); break;
88     case 0x18: via_155f18(regs); break;
89     case 0x19: via_155f19(regs); break;
90     default:   handle_155fXX(regs); break;
91     }
92 }
93 
94 static int
getFBSize(struct pci_device * pci)95 getFBSize(struct pci_device *pci)
96 {
97     /* FB config */
98     u8 reg = pci_config_readb(pci->bdf, 0xa1);
99 
100     /* GFX disabled ? */
101     if (!(reg & 0x80))
102         return -1;
103 
104     static u8 mem_power[] = {0, 3, 4, 5, 6, 7, 8, 9};
105     return mem_power[(reg >> 4) & 0x7];
106 }
107 
108 static int
getViaRamSpeed(struct pci_device * pci)109 getViaRamSpeed(struct pci_device *pci)
110 {
111     return (pci_config_readb(pci->bdf, 0x90) & 0x07) + 3;
112 }
113 
114 static int
getAMDRamSpeed(void)115 getAMDRamSpeed(void)
116 {
117     struct pci_device *pci = pci_find_device(PCI_VENDOR_ID_AMD
118                                              , PCI_DEVICE_ID_AMD_K8_NB_MEMCTL);
119     if (!pci)
120         return -1;
121 
122     /* mem clk 0 = DDR2 400 */
123     return (pci_config_readb(pci->bdf, 0x94) & 0x7) + 6;
124 }
125 
126 /* int 0x15 - 5f18
127 
128    ECX = unknown/don't care
129    EBX[3..0] Frame Buffer Size 2^N MiB
130    EBX[7..4] Memory speed:
131        0: SDR  66Mhz
132        1: SDR 100Mhz
133        2: SDR 133Mhz
134        3: DDR 100Mhz (PC1600 or DDR200)
135        4: DDR 133Mhz (PC2100 or DDR266)
136        5: DDR 166Mhz (PC2700 or DDR333)
137        6: DDR 200Mhz (PC3200 or DDR400)
138        7: DDR2 133Mhz (DDR2 533)
139        8: DDR2 166Mhz (DDR2 667)
140        9: DDR2 200Mhz (DDR2 800)
141        A: DDR2 233Mhz (DDR2 1066)
142        B: and above: Unknown
143    EBX[?..8] Total memory size?
144    EAX = 0x5f for success
145 */
146 
147 #define PCI_DEVICE_ID_VIA_K8M890CE_3    0x3336
148 #define PCI_DEVICE_ID_VIA_VX855_MEMCTRL 0x3409
149 
150 static void
via_setup(struct pci_device * pci)151 via_setup(struct pci_device *pci)
152 {
153     VGAHookHandlerType = VH_VIA;
154 
155     struct pci_device *d = pci_find_device(PCI_VENDOR_ID_VIA
156                                            , PCI_DEVICE_ID_VIA_K8M890CE_3);
157     if (d) {
158         ViaFBsize = getFBSize(d);
159         ViaRamSpeed = getAMDRamSpeed();
160         return;
161     }
162     d = pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855_MEMCTRL);
163     if (d) {
164         ViaFBsize = getFBSize(d);
165         ViaRamSpeed = getViaRamSpeed(d);
166         return;
167     }
168 
169     dprintf(1, "Warning: VGA memory size and speed is hardcoded\n");
170     ViaFBsize = 5; // 32M frame buffer
171     ViaRamSpeed = 4; // MCLK = DDR266
172 }
173 
174 
175 /****************************************************************
176  * Intel VGA hooks
177  ****************************************************************/
178 
179 u8 IntelDisplayType VARFSEG, IntelDisplayId VARFSEG;
180 
181 static void
intel_155f35(struct bregs * regs)182 intel_155f35(struct bregs *regs)
183 {
184     regs->ax = 0x005f;
185     regs->cl = GET_GLOBAL(IntelDisplayType);
186     set_success(regs);
187 }
188 
189 static void
intel_155f40(struct bregs * regs)190 intel_155f40(struct bregs *regs)
191 {
192     regs->ax = 0x005f;
193     regs->cl = GET_GLOBAL(IntelDisplayId);
194     set_success(regs);
195 }
196 
197 static void
intel_155f50(struct bregs * regs)198 intel_155f50(struct bregs *regs)
199 {
200     /* Mandatory hook on some Dell laptops */
201     regs->ax = 0x005f;
202     set_success(regs);
203 }
204 
205 static void
intel_155f(struct bregs * regs)206 intel_155f(struct bregs *regs)
207 {
208     switch (regs->al) {
209     case 0x35: intel_155f35(regs); break;
210     case 0x40: intel_155f40(regs); break;
211     case 0x50: intel_155f50(regs); break;
212     default:   handle_155fXX(regs); break;
213     }
214 }
215 
216 #define BOOT_DISPLAY_DEFAULT    (0)
217 #define BOOT_DISPLAY_CRT        (1 << 0)
218 #define BOOT_DISPLAY_TV         (1 << 1)
219 #define BOOT_DISPLAY_EFP        (1 << 2)
220 #define BOOT_DISPLAY_LCD        (1 << 3)
221 #define BOOT_DISPLAY_CRT2       (1 << 4)
222 #define BOOT_DISPLAY_TV2        (1 << 5)
223 #define BOOT_DISPLAY_EFP2       (1 << 6)
224 #define BOOT_DISPLAY_LCD2       (1 << 7)
225 
226 static void
intel_setup(struct pci_device * pci)227 intel_setup(struct pci_device *pci)
228 {
229     VGAHookHandlerType = VH_INTEL;
230 
231     IntelDisplayType = BOOT_DISPLAY_DEFAULT;
232     IntelDisplayId = 3;
233 }
234 
235 static void
roda_setup(struct pci_device * pci)236 roda_setup(struct pci_device *pci)
237 {
238     VGAHookHandlerType = VH_INTEL;
239     // IntelDisplayType = BOOT_DISPLAY_DEFAULT;
240     IntelDisplayType = BOOT_DISPLAY_LCD;
241     // IntelDisplayId = inb(0x60f) & 0x0f; // Correct according to Crete
242     IntelDisplayId = 3; // Correct according to empirical studies
243 }
244 
245 static void
kontron_setup(struct pci_device * pci)246 kontron_setup(struct pci_device *pci)
247 {
248     VGAHookHandlerType = VH_INTEL;
249     IntelDisplayType = BOOT_DISPLAY_CRT;
250     IntelDisplayId = 3;
251 }
252 
253 static void
getac_setup(struct pci_device * pci)254 getac_setup(struct pci_device *pci)
255 {
256 }
257 
258 /****************************************************************
259  * Silicon Motion hooks
260  ****************************************************************/
261 
262 u8 SmiBootDisplay VARFSEG; // 1: LCD, 2: CRT, 3: Both */
263 
264 static void
smi_157f02(struct bregs * regs)265 smi_157f02(struct bregs *regs)
266 {
267     /* Boot Display Device Override */
268     regs->ax = 0x007f;
269     regs->bl = GET_GLOBAL(SmiBootDisplay);
270     set_success(regs);
271 }
272 
273 static void
smi_157f14(struct bregs * regs)274 smi_157f14(struct bregs *regs)
275 {
276     /* ReduceOn support default status */
277     regs->ax = 0x007f;
278     regs->bl = 0x00;
279     set_success(regs);
280 }
281 
282 static void
smi_157f(struct bregs * regs)283 smi_157f(struct bregs *regs)
284 {
285     switch (regs->al) {
286     case 0x02: smi_157f02(regs); break;
287     case 0x14: smi_157f14(regs); break;
288     default:   handle_157fXX(regs); break;
289     }
290 }
291 
292 static void
winent_mb6047_setup(struct pci_device * pci)293 winent_mb6047_setup(struct pci_device *pci)
294 {
295     VGAHookHandlerType = VH_SMI;
296     SmiBootDisplay = 0x02;
297 }
298 
299 /****************************************************************
300  * Entry and setup
301  ****************************************************************/
302 
303 // Main 16bit entry point
304 void
handle_155f(struct bregs * regs)305 handle_155f(struct bregs *regs)
306 {
307     if (!CONFIG_VGAHOOKS) {
308         handle_155fXX(regs);
309         return;
310     }
311 
312     int htype = GET_GLOBAL(VGAHookHandlerType);
313     switch (htype) {
314     case VH_VIA:   via_155f(regs); break;
315     case VH_INTEL: intel_155f(regs); break;
316     default:       handle_155fXX(regs); break;
317     }
318 }
319 
320 // Main 16bit entry point
321 void
handle_157f(struct bregs * regs)322 handle_157f(struct bregs *regs)
323 {
324     if (!CONFIG_VGAHOOKS) {
325         handle_157fXX(regs);
326         return;
327     }
328 
329     int htype = GET_GLOBAL(VGAHookHandlerType);
330     switch (htype) {
331     case VH_SMI:   smi_157f(regs); break;
332     default:       handle_157fXX(regs); break;
333     }
334 }
335 
336 // Setup
337 void
vgahook_setup(struct pci_device * pci)338 vgahook_setup(struct pci_device *pci)
339 {
340     if (!CONFIG_VGAHOOKS)
341         return;
342 
343     if (strcmp(CBvendor, "KONTRON") == 0 && strcmp(CBpart, "986LCD-M") == 0)
344         kontron_setup(pci);
345     else if (strcmp(CBvendor, "GETAC") == 0 && strcmp(CBpart, "P470") == 0)
346         getac_setup(pci);
347     else if (strcmp(CBvendor, "RODA") == 0 && strcmp(CBpart, "RK886EX") == 0)
348         roda_setup(pci);
349     else if (strcmp(CBvendor, "Win Enterprise") == 0 && strcmp(CBpart, "MB6047") == 0)
350         winent_mb6047_setup(pci);
351     else if (pci->vendor == PCI_VENDOR_ID_VIA)
352         via_setup(pci);
353     else if (pci->vendor == PCI_VENDOR_ID_INTEL)
354         intel_setup(pci);
355 }
356