xref: /haiku/src/add-ons/kernel/drivers/graphics/ati/driver.cpp (revision 1a76488fc88584bf66b9751d7fb9b6527ac20d87)
1 /*
2 	Copyright 2007-2011 Haiku, Inc.  All rights reserved.
3 	Distributed under the terms of the MIT license.
4 
5 	Authors:
6 	Gerald Zajac
7 */
8 
9 #include <KernelExport.h>
10 #include <PCI.h>
11 #include <drivers/bios.h>
12 #include <malloc.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <graphic_driver.h>
16 #include <boot_item.h>
17 
18 #include "DriverInterface.h"
19 
20 
21 #undef TRACE
22 
23 #ifdef ENABLE_DEBUG_TRACE
24 #	define TRACE(x...) dprintf("ati: " x)
25 #else
26 #	define TRACE(x...) ;
27 #endif
28 
29 
30 #define ATI_ACCELERANT_NAME    "ati.accelerant"
31 
32 #define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
33 
34 #define VESA_MODES_BOOT_INFO "vesa_modes/v1"
35 
36 #define SKD_HANDLER_INSTALLED 0x80000000
37 #define MAX_DEVICES		4
38 #define DEVICE_FORMAT	"%04X_%04X_%02X%02X%02X"
39 
40 #define M64_BIOS_SIZE		0x10000		// 64KB
41 #define R128_BIOS_SIZE		0x10000		// 64KB
42 
43 int32 api_version = B_CUR_DRIVER_API_VERSION;	// revision of driver API used
44 
45 #define VENDOR_ID 0x1002	// ATI vendor ID
46 
47 // Mach64 register definitions.
48 #define M64_CLOCK_INTERNAL	4
49 #define M64_CONFIG_CHIP_ID	0x0CE0		// offset in register area
50 #define M64_CFG_CHIP_TYPE	0x0000FFFF
51 
52 
53 struct ChipInfo {
54 	uint16		chipID;			// PCI device id of the chip
55 	ChipType	chipType;		// assigned chip type identifier
56 	const char*	chipName;		// user recognizable name for chip
57 								//   (must be < 32 chars)
58 };
59 
60 
61 // Names for chip types.
62 
63 static char sRage128_GL[]		 = "RAGE 128 GL";
64 static char sRage128_VR[]		 = "RAGE 128 VR";
65 static char sRage128_Pro_GL[]	 = "RAGE 128 PRO GL";
66 static char sRage128_Pro_VR[]	 = "RAGE 128 PRO VR";
67 static char sRage128_Pro_Ultra[] = "RAGE 128 PRO Ultra";
68 
69 // This table maps a PCI device ID to a chip type identifier and the chip name.
70 // The table is split into two groups of chips, the Mach64 and Rage128 chips,
71 // with each group ordered by the chip ID.
72 
73 static const ChipInfo chipTable[] = {
74 	{ 0x4742, MACH64_264GTPRO,		"3D RAGE PRO, AGP"		},		// GB
75 	{ 0x4744, MACH64_264GTPRO,		"3D RAGE PRO, AGP"		},		// GD
76 	{ 0x4749, MACH64_264GTPRO,		"3D RAGE PRO, PCI"		},		// GI
77 	{ 0x474C, MACH64_264XL,			"3D RAGE XC, PCI"		},		// GL
78 	{ 0x474D, MACH64_264XL,			"3D RAGE XL, AGP"		},		// GM
79 	{ 0x474E, MACH64_264XL,			"3D RAGE XC, AGP"		},		// GN
80 	{ 0x474F, MACH64_264XL,			"3D RAGE XL, PCI"		},		// GO
81 	{ 0x4750, MACH64_264GTPRO,		"3D RAGE PRO, PCI"		},		// GP
82 	{ 0x4751, MACH64_264GTPRO,		"3D RAGE PRO, PCI"		},		// GQ
83 	{ 0x4752, MACH64_264XL,			"3D RAGE XL, PCI"		},		// GR
84 	{ 0x4753, MACH64_264XL,			"3D RAGE XC, PCI"		},		// GS
85 	{ 0x4754, MACH64_264GT,			"3D RAGE II"			},		// GT
86 	{ 0x4755, MACH64_264GTDVD,		"3D RAGE II+"			},		// GU
87 	{ 0x4756, MACH64_264GT2C,		"3D RAGE IIC, PCI"		},		// GV
88 	{ 0x4757, MACH64_264GT2C,		"3D RAGE IIC, AGP"		},		// GW
89 	{ 0x4759, MACH64_264GT2C,		"3D RAGE IIC, PCI"		},		// GY
90 	{ 0x475A, MACH64_264GT2C,		"3D RAGE IIC, AGP"		},		// GZ
91 	{ 0x4C42, MACH64_264LTPRO,		"3D RAGE LT PRO, AGP"	},		// LB
92 	{ 0x4C44, MACH64_264LTPRO,		"3D RAGE LT PRO, AGP"	},		// LD
93 	{ 0x4C47, MACH64_264LT,			"3D RAGE LT"			},		// LG
94 	{ 0x4C49, MACH64_264LTPRO,		"3D RAGE LT PRO, PCI"	},		// LI
95 	{ 0x4C4D, MACH64_MOBILITY,		"3D RAGE Mobility, AGP"	},		// LM
96 	{ 0x4C4E, MACH64_MOBILITY,		"3D RAGE Mobility, AGP"	},		// LN
97 	{ 0x4C50, MACH64_264LTPRO,		"3D RAGE LT PRO, PCI"	},		// LP
98 	{ 0x4C51, MACH64_264LTPRO,		"3D RAGE LT PRO, PCI"	},		// LQ
99 	{ 0x4C52, MACH64_MOBILITY,		"3D RAGE Mobility, PCI"	},		// LR
100 	{ 0x4C53, MACH64_MOBILITY,		"3D RAGE Mobility, PCI"	},		// LS
101 	{ 0x5654, MACH64_264VT,			"264VT2"				},		// VT
102 	{ 0x5655, MACH64_264VT3,		"264VT3"				},		// VU
103 	{ 0x5656, MACH64_264VT4,		"264VT4"				},		// VV
104 
105 	{ 0x4C45, RAGE128_MOBILITY,		"RAGE 128 Mobility 3"	},		// LE
106 	{ 0x4C46, RAGE128_MOBILITY,		"RAGE 128 Mobility 3"	},		// LF
107 	{ 0x4D46, RAGE128_MOBILITY,		"RAGE 128 Mobility 4"	},		// MF
108 	{ 0x4D4C, RAGE128_MOBILITY,		"RAGE 128 Mobility 4"	},		// ML
109 	{ 0x5041, RAGE128_PRO_GL,		sRage128_Pro_GL			},		// PA
110 	{ 0x5042, RAGE128_PRO_GL,		sRage128_Pro_GL			},		// PB
111 	{ 0x5043, RAGE128_PRO_GL,		sRage128_Pro_GL			},		// PC
112 	{ 0x5044, RAGE128_PRO_GL,		sRage128_Pro_GL			},		// PD
113 	{ 0x5045, RAGE128_PRO_GL,		sRage128_Pro_GL			},		// PE
114 	{ 0x5046, RAGE128_PRO_GL,		sRage128_Pro_GL			},		// PF
115 	{ 0x5047, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PG
116 	{ 0x5048, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PH
117 	{ 0x5049, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PI
118 	{ 0x504A, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PJ
119 	{ 0x504B, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PK
120 	{ 0x504C, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PL
121 	{ 0x504D, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PM
122 	{ 0x504E, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PN
123 	{ 0x504F, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PO
124 	{ 0x5050, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PP
125 	{ 0x5051, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PQ
126 	{ 0x5052, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PR
127 	{ 0x5053, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PS
128 	{ 0x5054, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PT
129 	{ 0x5055, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PU
130 	{ 0x5056, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PV
131 	{ 0x5057, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PW
132 	{ 0x5058, RAGE128_PRO_VR,		sRage128_Pro_VR			},		// PX
133 	{ 0x5245, RAGE128_GL,			sRage128_GL				},		// RE
134 	{ 0x5246, RAGE128_GL,			sRage128_GL				},		// RF
135 	{ 0x5247, RAGE128_GL,			sRage128_GL				},		// RG
136 	{ 0x524B, RAGE128_VR,			sRage128_VR				},		// RK
137 	{ 0x524C, RAGE128_VR,			sRage128_VR				},		// RL
138 	{ 0x5345, RAGE128_VR,			sRage128_VR				},		// SE
139 	{ 0x5346, RAGE128_VR,			sRage128_VR				},		// SF
140 	{ 0x5347, RAGE128_VR,			sRage128_VR				},		// SG
141 	{ 0x5348, RAGE128_VR,			sRage128_VR				},		// SH
142 	{ 0x534B, RAGE128_GL,			sRage128_GL				},		// SK
143 	{ 0x534C, RAGE128_GL,			sRage128_GL				},		// SL
144 	{ 0x534D, RAGE128_GL,			sRage128_GL				},		// SM
145 	{ 0x534E, RAGE128_GL,			sRage128_GL				},		// SN
146 	{ 0x5446, RAGE128_PRO_ULTRA,	sRage128_Pro_Ultra		},		// TF
147 	{ 0x544C, RAGE128_PRO_ULTRA,	sRage128_Pro_Ultra		},		// TL
148 	{ 0x5452, RAGE128_PRO_ULTRA,	sRage128_Pro_Ultra		},		// TR
149 	{ 0x5453, RAGE128_PRO_ULTRA,	sRage128_Pro_Ultra		},		// TS
150 	{ 0x5454, RAGE128_PRO_ULTRA,	sRage128_Pro_Ultra		},		// TT
151 	{ 0x5455, RAGE128_PRO_ULTRA,	sRage128_Pro_Ultra		},		// TU
152 	{ 0,	  ATI_NONE,				NULL }
153 };
154 
155 
156 struct DeviceInfo {
157 	uint32			openCount;		// count of how many times device has been opened
158 	int32			flags;
159 	area_id 		sharedArea;		// area shared between driver and all accelerants
160 	SharedInfo* 	sharedInfo;		// pointer to shared info area memory
161 	vuint8*	 		regs;			// pointer to memory mapped registers
162 	const ChipInfo*	pChipInfo;		// info about the selected chip
163 	pci_info		pciInfo;		// copy of pci info for this device
164 	char			name[B_OS_NAME_LENGTH]; // name of device
165 };
166 
167 
168 static Benaphore		gLock;
169 static DeviceInfo		gDeviceInfo[MAX_DEVICES];
170 static char*			gDeviceNames[MAX_DEVICES + 1];
171 static pci_module_info*	gPCI;
172 
173 
174 // Prototypes for device hook functions.
175 
176 static status_t device_open(const char* name, uint32 flags, void** cookie);
177 static status_t device_close(void* dev);
178 static status_t device_free(void* dev);
179 static status_t device_read(void* dev, off_t pos, void* buf, size_t* len);
180 static status_t device_write(void* dev, off_t pos, const void* buf, size_t* len);
181 static status_t device_ioctl(void* dev, uint32 msg, void* buf, size_t len);
182 
183 static device_hooks gDeviceHooks =
184 {
185 	device_open,
186 	device_close,
187 	device_free,
188 	device_ioctl,
189 	device_read,
190 	device_write,
191 	NULL,
192 	NULL,
193 	NULL,
194 	NULL
195 };
196 
197 
198 
199 static inline uint32
200 GetPCI(pci_info& info, uint8 offset, uint8 size)
201 {
202 	return gPCI->read_pci_config(info.bus, info.device, info.function, offset,
203 		size);
204 }
205 
206 
207 static inline void
208 SetPCI(pci_info& info, uint8 offset, uint8 size, uint32 value)
209 {
210 	gPCI->write_pci_config(info.bus, info.device, info.function, offset, size,
211 		value);
212 }
213 
214 
215 // Functions for dealing with Vertical Blanking Interrupts.  Currently, I do
216 // not know the commands to handle these operations;  thus, these functions
217 // currently do nothing.
218 
219 static bool
220 InterruptIsVBI()
221 {
222 	// return true only if a vertical blanking interrupt has occured
223 	return false;
224 }
225 
226 
227 static void
228 ClearVBI()
229 {
230 }
231 
232 static void
233 EnableVBI()
234 {
235 }
236 
237 static void
238 DisableVBI()
239 {
240 }
241 
242 
243 
244 static status_t
245 GetEdidFromBIOS(edid1_raw& edidRaw)
246 {
247 	// Get the EDID info from the video BIOS, and return B_OK if successful.
248 
249 #define ADDRESS_SEGMENT(address) ((addr_t)(address) >> 4)
250 #define ADDRESS_OFFSET(address) ((addr_t)(address) & 0xf)
251 
252 	bios_module_info* biosModule;
253 	status_t status = get_module(B_BIOS_MODULE_NAME, (module_info**)&biosModule);
254 	if (status != B_OK) {
255 		TRACE("GetEdidFromBIOS(): failed to get BIOS module: 0x%" B_PRIx32 "\n",
256 			status);
257 		return status;
258 	}
259 
260 	bios_state* state;
261 	status = biosModule->prepare(&state);
262 	if (status != B_OK) {
263 		TRACE("GetEdidFromBIOS(): bios_prepare() failed: 0x%" B_PRIx32 "\n",
264 			status);
265 		put_module(B_BIOS_MODULE_NAME);
266 		return status;
267 	}
268 
269 	bios_regs regs = {};
270 	regs.eax = 0x4f15;
271 	regs.ebx = 0;			// 0 = report DDC service
272 	regs.ecx = 0;
273 	regs.es = 0;
274 	regs.edi = 0;
275 
276 	status = biosModule->interrupt(state, 0x10, &regs);
277 	if (status == B_OK) {
278 		// AH contains the error code, and AL determines whether or not the
279 		// function is supported.
280 		if (regs.eax != 0x4f)
281 			status = B_NOT_SUPPORTED;
282 
283 		// Test if DDC is supported by the monitor.
284 		if ((regs.ebx & 3) == 0)
285 			status = B_NOT_SUPPORTED;
286 	}
287 
288 	if (status == B_OK) {
289 		edid1_raw* edid = (edid1_raw*)biosModule->allocate_mem(state,
290 			sizeof(edid1_raw));
291 		if (edid == NULL) {
292 			status = B_NO_MEMORY;
293 			goto out;
294 		}
295 
296 		regs.eax = 0x4f15;
297 		regs.ebx = 1;		// 1 = read EDID
298 		regs.ecx = 0;
299 		regs.edx = 0;
300 		regs.es  = ADDRESS_SEGMENT(edid);
301 		regs.edi = ADDRESS_OFFSET(edid);
302 
303 		status = biosModule->interrupt(state, 0x10, &regs);
304 		if (status == B_OK) {
305 			if (regs.eax != 0x4f) {
306 				status = B_NOT_SUPPORTED;
307 			} else {
308 				// Copy the EDID info to the caller's location, and compute the
309 				// checksum of the EDID info while copying.
310 
311 				uint8 sum = 0;
312 				uint8 allOr = 0;
313 				uint8* dest = (uint8*)&edidRaw;
314 				uint8* src = (uint8*)edid;
315 
316 				for (uint32 j = 0; j < sizeof(edidRaw); j++) {
317 					sum += *src;
318 					allOr |= *src;
319 					*dest++ = *src++;
320 				}
321 
322 				if (allOr == 0) {
323 					TRACE("GetEdidFromBIOS(); EDID info contains only zeros\n");
324 					status = B_ERROR;
325 				} else if (sum != 0) {
326 					TRACE("GetEdidFromBIOS(); Checksum error in EDID info\n");
327 					status = B_ERROR;
328 				}
329 			}
330 		}
331 	}
332 
333 out:
334 	biosModule->finish(state);
335 	put_module(B_BIOS_MODULE_NAME);
336 	return status;
337 }
338 
339 
340 static status_t
341 SetVesaDisplayMode(uint16 mode)
342 {
343 	// Set the VESA display mode, and return B_OK if successful.
344 
345 #define SET_MODE_MASK				0x01ff
346 #define SET_MODE_LINEAR_BUFFER		(1 << 14)
347 
348 	bios_module_info* biosModule;
349 	status_t status = get_module(B_BIOS_MODULE_NAME, (module_info**)&biosModule);
350 	if (status != B_OK) {
351 		TRACE("SetVesaDisplayMode(0x%x): failed to get BIOS module: 0x%" B_PRIx32
352 			"\n", mode, status);
353 		return status;
354 	}
355 
356 	bios_state* state;
357 	status = biosModule->prepare(&state);
358 	if (status != B_OK) {
359 		TRACE("SetVesaDisplayMode(0x%x): bios_prepare() failed: 0x%" B_PRIx32
360 			"\n", mode, status);
361 		put_module(B_BIOS_MODULE_NAME);
362 		return status;
363 	}
364 
365 	bios_regs regs = {};
366 	regs.eax = 0x4f02;
367 	regs.ebx = (mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER;
368 
369 	status = biosModule->interrupt(state, 0x10, &regs);
370 	if (status != B_OK) {
371 		TRACE("SetVesaDisplayMode(0x%x): BIOS interrupt failed\n", mode);
372 	}
373 
374 	if (status == B_OK && (regs.eax & 0xffff) != 0x4f) {
375 		TRACE("SetVesaDisplayMode(0x%x): BIOS returned 0x%04" B_PRIx32 "\n",
376 			mode, regs.eax & 0xffff);
377 		status = B_ERROR;
378 	}
379 
380 	biosModule->finish(state);
381 	put_module(B_BIOS_MODULE_NAME);
382 	return status;
383 }
384 
385 
386 
387 // Macros for accessing BIOS info.
388 
389 #define BIOS8(v)  (romAddr[v])
390 #define BIOS16(v) (romAddr[v] | \
391 				  (romAddr[(v) + 1] << 8))
392 #define BIOS32(v) (romAddr[v] | \
393 				  (romAddr[(v) + 1] << 8) | \
394 				  (romAddr[(v) + 2] << 16) | \
395 				  (romAddr[(v) + 3] << 24))
396 
397 
398 static status_t
399 Mach64_GetBiosParameters(DeviceInfo& di, uint8& clockType)
400 {
401 	// Get some clock parameters from the video BIOS, and if Mobility chip,
402 	// also get the LCD panel width & height.
403 
404 	// In case mapping the ROM area fails or other error occurs, set default
405 	// values for the parameters which will be obtained from the BIOS ROM.
406 
407 	clockType = M64_CLOCK_INTERNAL;
408 
409 	SharedInfo& si = *(di.sharedInfo);
410 	M64_Params& params = si.m64Params;
411 	params.clockNumberToProgram = 3;
412 
413 	si.panelX = 0;
414 	si.panelY = 0;
415 
416 	// Map the ROM area.  The Mach64 chips do not assign a ROM address in the
417 	// PCI info;  thus, access the ROM via the ISA legacy memory map.
418 
419 	uint8* romAddr;
420 	area_id romArea = map_physical_memory("ATI Mach64 ROM",
421 #if defined(__x86__) || defined(__x86_64__)
422 		0x000c0000,
423 #else
424 		di.pciInfo.u.h0.rom_base,
425 #endif
426 		M64_BIOS_SIZE,
427 		B_ANY_KERNEL_ADDRESS,
428 		B_KERNEL_READ_AREA,
429 		(void**)&(romAddr));
430 
431 	if (romArea < 0) {
432 		TRACE("Mach64_GetBiosParameters(), ROM mapping error: %" B_PRId32 "\n",
433 			romArea);
434 		return romArea;		// ROM mapping failed; return error code
435 	}
436 
437 	// Check if we have the BIOS signature (might fail on laptops..).
438 
439 	if (BIOS8(0) != 0x55 || BIOS8(1) != 0xaa) {
440 		TRACE("Mach64_GetBiosParameters(), ROM does not contain BIOS signature\n");
441 		delete_area(romArea);
442 		return B_ERROR;
443 	}
444 
445 	// Get clock info from BIOS.
446 
447 	uint32 romTable = BIOS16(0x48);
448 	uint32 clockTable = BIOS16(romTable + 16);
449 	clockType = BIOS8(clockTable);
450 	params.clockNumberToProgram = BIOS8(clockTable + 6);
451 	params.maxPixelClock = BIOS16(clockTable + 4) * 10;
452 	params.refFreq = BIOS16(clockTable + 8);
453 	params.refDivider = BIOS16(clockTable + 10);
454 
455 	// If Mobility chip, get the LCD panel width & height.
456 
457 	if (si.chipType == MACH64_MOBILITY) {
458 		uint32 lcdTable = BIOS16(0x78);
459 		if (BIOS32(lcdTable) == 0x544d5224) {	// is LCD table signature correct?
460 			uint32 lcdPanelInfo = BIOS16(lcdTable + 10);
461 			si.panelX = BIOS16(lcdPanelInfo + 25);
462 			si.panelY = BIOS16(lcdPanelInfo + 27);
463 			TRACE("Mobility LCD Panel size: %dx%d\n", si.panelX, si.panelY);
464 		} else {
465 			TRACE("Mobility LCD table signature 0x%x in BIOS is incorrect\n",
466 				 BIOS32(lcdTable));
467 		}
468 	}
469 
470 	delete_area(romArea);
471 
472 	return B_OK;
473 }
474 
475 
476 
477 static status_t
478 Rage128_GetBiosParameters(DeviceInfo& di)
479 {
480 	// Get the PLL parameters from the video BIOS, and if Mobility chips, also
481 	// get the LCD panel width & height and a few other related parameters.
482 
483 	// In case mapping the ROM area fails or other error occurs, set default
484 	// values for the parameters which will be obtained from the BIOS ROM.
485 	// The default PLL parameters values probably will not work for all chips.
486 	// For example, reference freq can be 29.50MHz, 28.63MHz, or 14.32MHz.
487 
488 	SharedInfo& si = *(di.sharedInfo);
489 	R128_PLLParams& pll = si.r128PLLParams;
490 	pll.reference_freq = 2950;
491 	pll.reference_div = 65;
492 	pll.min_pll_freq = 12500;
493 	pll.max_pll_freq = 25000;
494 	pll.xclk = 10300;
495 
496 	si.panelX = 0;
497 	si.panelY = 0;
498 	si.panelPowerDelay = 1;
499 
500 	// Map the ROM area.  The Rage128 chips do not assign a ROM address in the
501 	// PCI info;  thus, access the ROM via the ISA legacy memory map.
502 
503 	uint8* romAddr;
504 	area_id romArea = map_physical_memory("ATI Rage128 ROM",
505 #if defined(__x86__) || defined(__x86_64__)
506 		0x000c0000,
507 #else
508 		di.pciInfo.u.h0.rom_base,
509 #endif
510 		R128_BIOS_SIZE,
511 		B_ANY_KERNEL_ADDRESS,
512 		B_KERNEL_READ_AREA,
513 		(void**)&(romAddr));
514 
515 	if (romArea < 0) {
516 		TRACE("Rage128_GetBiosParameters(), ROM mapping error: %" B_PRId32
517 			"\n", romArea);
518 		return romArea;		// ROM mapping failed; return error code
519 	}
520 
521 	// Check if we got the BIOS signature (might fail on laptops..).
522 
523 	if (BIOS8(0) != 0x55 || BIOS8(1) != 0xaa) {
524 		TRACE("Rage128_GetBiosParameters(), ROM does not contain BIOS signature\n");
525 		delete_area(romArea);
526 		return B_ERROR;
527 	}
528 
529 	// Get the PLL values from the mapped ROM area.
530 
531 	uint16 biosHeader = BIOS16(0x48);
532 	uint16 pllInfoBlock = BIOS16(biosHeader + 0x30);
533 
534 	pll.reference_freq = BIOS16(pllInfoBlock + 0x0e);
535 	pll.reference_div = BIOS16(pllInfoBlock + 0x10);
536 	pll.min_pll_freq = BIOS32(pllInfoBlock + 0x12);
537 	pll.max_pll_freq = BIOS32(pllInfoBlock + 0x16);
538 	pll.xclk = BIOS16(pllInfoBlock + 0x08);
539 
540 	TRACE("PLL parameters: rf=%d rd=%d min=%" B_PRId32 " max=%" B_PRId32
541 		"; xclk=%d\n",
542 		pll.reference_freq, pll.reference_div, pll.min_pll_freq,
543 		pll.max_pll_freq, pll.xclk);
544 
545 	// If Mobility chip, get the LCD panel width & height and a few other
546 	// related parameters.
547 
548 	if (si.chipType == RAGE128_MOBILITY) {
549 		// There should be direct access to the start of the FP info table, but
550 		// until we find out where that offset is stored, we must search for
551 		// the ATI signature string: "M3      ".
552 
553 		int i;
554 		for (i = 4; i < R128_BIOS_SIZE - 8; i++) {
555 			if (BIOS8(i) == 'M' &&
556 					BIOS8(i + 1) == '3' &&
557 					BIOS8(i + 2) == ' ' &&
558 					BIOS8(i + 3) == ' ' &&
559 					BIOS8(i + 4) == ' ' &&
560 					BIOS8(i + 5) == ' ' &&
561 					BIOS8(i + 6) == ' ' &&
562 					BIOS8(i + 7) == ' ') {
563 				int fpHeader = i - 2;
564 
565 				// Assume that only one panel is attached and supported.
566 
567 				for (i = fpHeader + 20; i < fpHeader + 84; i += 2) {
568 					if (BIOS16(i) != 0) {
569 						int fpStart = BIOS16(i);
570 						si.panelX = BIOS16(fpStart + 25);
571 						si.panelY = BIOS16(fpStart + 27);
572 						si.panelPowerDelay = BIOS8(fpStart + 56);
573 						TRACE("LCD Panel size: %dx%d  Panel type: 0x%x   power delay: %d\n",
574 							si.panelX, si.panelY, BIOS16(fpStart + 29),
575 							si.panelPowerDelay);
576 						break;
577 					}
578 				}
579 
580 				break;
581 			}
582 		}
583 	}
584 
585 	delete_area(romArea);
586 
587 	return B_OK;
588 }
589 
590 
591 static status_t
592 MapDevice(DeviceInfo& di)
593 {
594 	SharedInfo& si = *(di.sharedInfo);
595 	pci_info& pciInfo = di.pciInfo;
596 
597 	// Enable memory mapped IO and bus master.
598 
599 	SetPCI(pciInfo, PCI_command, 2, GetPCI(pciInfo, PCI_command, 2)
600 		| PCI_command_io | PCI_command_memory | PCI_command_master);
601 
602 	// Enable ROM decoding
603 
604 	if (di.pciInfo.u.h0.rom_size > 0) {
605 		SetPCI(pciInfo, PCI_rom_base, 4,
606 			GetPCI(pciInfo, PCI_rom_base, 4) | 0x00000001);
607 	}
608 
609 	// Map the video memory.
610 
611 	phys_addr_t videoRamAddr = pciInfo.u.h0.base_registers[0];
612 	uint32 videoRamSize = pciInfo.u.h0.base_register_sizes[0];
613 	si.videoMemPCI = videoRamAddr;
614 	char frameBufferAreaName[] = "ATI frame buffer";
615 
616 	si.videoMemArea = map_physical_memory(
617 		frameBufferAreaName,
618 		videoRamAddr,
619 		videoRamSize,
620 		B_ANY_KERNEL_BLOCK_ADDRESS | B_MTR_WC,
621 		B_READ_AREA + B_WRITE_AREA,
622 		(void**)&(si.videoMemAddr));
623 
624 	if (si.videoMemArea < 0) {
625 		// Try to map this time without write combining.
626 		si.videoMemArea = map_physical_memory(
627 			frameBufferAreaName,
628 			videoRamAddr,
629 			videoRamSize,
630 			B_ANY_KERNEL_BLOCK_ADDRESS,
631 			B_READ_AREA + B_WRITE_AREA,
632 			(void**)&(si.videoMemAddr));
633 	}
634 
635 	if (si.videoMemArea < 0)
636 		return si.videoMemArea;
637 
638 	// Map the MMIO register area.
639 
640 	phys_addr_t regsBase = pciInfo.u.h0.base_registers[2];
641 	uint32 regAreaSize = pciInfo.u.h0.base_register_sizes[2];
642 
643 	// If the register area address or size is not in the PCI info, it should
644 	// be at the end of the video memory.  Check if it is there.
645 
646 	if (MACH64_FAMILY(si.chipType) && (regsBase == 0 || regAreaSize == 0)) {
647 		uint32 regsOffset = 0x7ff000;	// offset to regs area in video memory
648 		addr_t regs = addr_t(si.videoMemAddr) + regsOffset;
649 		uint32 chipInfo = *((vuint32*)(regs + M64_CONFIG_CHIP_ID));
650 
651 		if (si.deviceID != (chipInfo & M64_CFG_CHIP_TYPE)) {
652 			// Register area not found;  delete any other areas that were
653 			// created.
654 			delete_area(si.videoMemArea);
655 			si.videoMemArea = -1;
656 			TRACE("Mach64 register area not found\n");
657 			return B_ERROR;
658 		}
659 
660 		// Adjust params for creating register area below.
661 
662 		regsBase = videoRamAddr + regsOffset;
663 		regAreaSize = 0x1000;
664 		TRACE("Register address is at end of frame buffer memory at 0x%"
665 			B_PRIxPHYSADDR "\n", regsBase);
666 	}
667 
668 	si.regsArea = map_physical_memory("ATI mmio registers",
669 		regsBase,
670 		regAreaSize,
671 		B_ANY_KERNEL_ADDRESS,
672 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA,
673 		(void**)&di.regs);
674 
675 	// If there was an error, delete other areas.
676 	if (si.regsArea < 0) {
677 		delete_area(si.videoMemArea);
678 		si.videoMemArea = -1;
679 	}
680 
681 	return si.regsArea;
682 }
683 
684 
685 static void
686 UnmapDevice(DeviceInfo& di)
687 {
688 	SharedInfo& si = *(di.sharedInfo);
689 
690 	if (si.regsArea >= 0)
691 		delete_area(si.regsArea);
692 	if (si.videoMemArea >= 0)
693 		delete_area(si.videoMemArea);
694 
695 	si.regsArea = si.videoMemArea = -1;
696 	si.videoMemAddr = (addr_t)NULL;
697 	di.regs = NULL;
698 }
699 
700 
701 static int32
702 InterruptHandler(void* data)
703 {
704 	int32 handled = B_UNHANDLED_INTERRUPT;
705 	DeviceInfo& di = *((DeviceInfo*)data);
706 	int32* flags = &(di.flags);
707 
708 	// Is someone already handling an interrupt for this device?
709 	if (atomic_or(flags, SKD_HANDLER_INSTALLED) & SKD_HANDLER_INSTALLED)
710 		return B_UNHANDLED_INTERRUPT;
711 
712 	if (InterruptIsVBI()) {	// was interrupt a VBI?
713 		ClearVBI();			// clear interrupt
714 
715 		handled = B_HANDLED_INTERRUPT;
716 
717 		// Release vertical blanking semaphore.
718 		sem_id& sem = di.sharedInfo->vertBlankSem;
719 
720 		if (sem >= 0) {
721 			int32 blocked;
722 			if ((get_sem_count(sem, &blocked) == B_OK) && (blocked < 0)) {
723 				release_sem_etc(sem, -blocked, B_DO_NOT_RESCHEDULE);
724 				handled = B_INVOKE_SCHEDULER;
725 			}
726 		}
727 	}
728 
729 	atomic_and(flags, ~SKD_HANDLER_INSTALLED);	// note we're not in handler anymore
730 
731 	return handled;
732 }
733 
734 
735 static void
736 InitInterruptHandler(DeviceInfo& di)
737 {
738 	SharedInfo& si = *(di.sharedInfo);
739 
740 	DisableVBI();					// disable & clear any pending interrupts
741 	si.bInterruptAssigned = false;	// indicate interrupt not assigned yet
742 
743 	// Create a semaphore for vertical blank management.
744 	si.vertBlankSem = create_sem(0, di.name);
745 	if (si.vertBlankSem < 0)
746 		return;
747 
748 	// Change the owner of the semaphores to the calling team (usually the
749 	// app_server).  This is required because apps can't aquire kernel
750 	// semaphores.
751 
752 	thread_id threadID = find_thread(NULL);
753 	thread_info threadInfo;
754 	status_t status = get_thread_info(threadID, &threadInfo);
755 	if (status == B_OK)
756 		status = set_sem_owner(si.vertBlankSem, threadInfo.team);
757 
758 	// If there is a valid interrupt assigned, set up interrupts.
759 
760 	if (status == B_OK && di.pciInfo.u.h0.interrupt_pin != 0x00
761 		&& di.pciInfo.u.h0.interrupt_line != 0xff) {
762 		// We have a interrupt line to use.
763 
764 		status = install_io_interrupt_handler(di.pciInfo.u.h0.interrupt_line,
765 			InterruptHandler, (void*)(&di), 0);
766 
767 		if (status == B_OK)
768 			si.bInterruptAssigned = true;	// we can use interrupt related functions
769 	}
770 
771 	if (status != B_OK) {
772 		// Interrupt does not exist; thus delete semaphore as it won't be used.
773 		delete_sem(si.vertBlankSem);
774 		si.vertBlankSem = -1;
775 	}
776 }
777 
778 
779 static status_t
780 InitDevice(DeviceInfo& di)
781 {
782 	// Perform initialization and mapping of the device, and return B_OK if
783 	// sucessful;  else, return error code.
784 
785 	// Get the table of VESA modes that the chip supports.  Note that we will
786 	// need this table only for chips that are currently connected to a laptop
787 	// display or a monitor connected via a DVI interface.
788 
789 	size_t vesaModeTableSize = 0;
790 	VesaMode* vesaModes = (VesaMode*)get_boot_item(VESA_MODES_BOOT_INFO,
791 		&vesaModeTableSize);
792 
793 	size_t sharedSize = (sizeof(SharedInfo) + 7) & ~7;
794 
795 	// Create the area for shared info with NO user-space read or write
796 	// permissions, to prevent accidental damage.
797 
798 	di.sharedArea = create_area("ATI shared info",
799 		(void**) &(di.sharedInfo),
800 		B_ANY_KERNEL_ADDRESS,
801 		ROUND_TO_PAGE_SIZE(sharedSize + vesaModeTableSize),
802 		B_FULL_LOCK,
803 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA);
804 	if (di.sharedArea < 0)
805 		return di.sharedArea;	// return error code
806 
807 	SharedInfo& si = *(di.sharedInfo);
808 
809 	memset(&si, 0, sharedSize);
810 
811 	if (vesaModes != NULL) {
812 		si.vesaModeTableOffset = sharedSize;
813 		si.vesaModeCount = vesaModeTableSize / sizeof(VesaMode);
814 
815 		memcpy((uint8*)&si + si.vesaModeTableOffset, vesaModes,
816 			vesaModeTableSize);
817 	}
818 
819 	pci_info& pciInfo = di.pciInfo;
820 
821 	si.vendorID = pciInfo.vendor_id;
822 	si.deviceID = pciInfo.device_id;
823 	si.revision = pciInfo.revision;
824 	si.chipType = di.pChipInfo->chipType;
825 	strcpy(si.chipName, di.pChipInfo->chipName);
826 
827 	TRACE("Chip revision: 0x%x\n", si.revision);
828 
829 	// 264GT has two chip versions.  If version is non-zero, chip is 264GTB.
830 
831 	if (si.chipType == MACH64_264GT && si.revision & 0x7)
832 		si.chipType = MACH64_264GTB;
833 
834 	// 264VT has two chip versions.  If version is non-zero, chip is 264VTB.
835 
836 	if (si.chipType == MACH64_264VT && si.revision & 0x7)
837 		si.chipType = MACH64_264VTB;
838 
839 	status_t status = MapDevice(di);
840 
841 	// If device mapped without any error, get the bios parameters from the
842 	// chip's BIOS ROM.
843 
844 	if (status >= 0) {
845 		if (MACH64_FAMILY(si.chipType)) {
846 			uint8 clockType;
847 			Mach64_GetBiosParameters(di, clockType);
848 
849 			// All chips supported by this driver should have an internal clock.
850 			// If the clock is not an internal clock, the video chip is not
851 			// supported.
852 
853 			if (clockType != M64_CLOCK_INTERNAL) {
854 				TRACE("Video chip clock type %d not supported\n", clockType);
855 				status = B_UNSUPPORTED;
856 			}
857 		}
858 		else if (RAGE128_FAMILY(si.chipType))
859 			Rage128_GetBiosParameters(di);
860 	}
861 
862 	if (status < 0) {
863 		delete_area(di.sharedArea);
864 		di.sharedArea = -1;
865 		di.sharedInfo = NULL;
866 		return status;		// return error code
867 	}
868 
869 	InitInterruptHandler(di);
870 
871 	TRACE("Interrupt assigned:  %s\n", si.bInterruptAssigned ? "yes" : "no");
872 	return B_OK;
873 }
874 
875 
876 static const ChipInfo*
877 GetNextSupportedDevice(uint32& pciIndex, pci_info& pciInfo)
878 {
879 	// Search the PCI devices for a device that is supported by this driver.
880 	// The search starts at the device specified by argument pciIndex, and
881 	// continues until a supported device is found or there are no more devices
882 	// to examine.  Argument pciIndex is incremented after each device is
883 	// examined.
884 
885 	// If a supported device is found, return a pointer to the struct containing
886 	// the chip info; else return NULL.
887 
888 	while (gPCI->get_nth_pci_info(pciIndex, &pciInfo) == B_OK) {
889 
890 		if (pciInfo.vendor_id == VENDOR_ID) {
891 
892 			// Search the table of supported devices to find a chip/device that
893 			// matches device ID of the current PCI device.
894 
895 			const ChipInfo* pDevice = chipTable;
896 
897 			while (pDevice->chipID != 0) {	// end of table?
898 				if (pDevice->chipID == pciInfo.device_id) {
899 					// Matching device/chip found.  If chip is 264VT, reject it
900 					// if its version is zero since the mode can not be set on
901 					// that chip.
902 
903 					if (pDevice->chipType == MACH64_264VT
904 							&& (pciInfo.revision & 0x7) == 0)
905 						break;
906 
907 					return pDevice;		// matching device/chip found
908 				}
909 
910 				pDevice++;
911 			}
912 		}
913 
914 		pciIndex++;
915 	}
916 
917 	return NULL;		// no supported device found
918 }
919 
920 
921 
922 //	#pragma mark - Kernel Interface
923 
924 
925 status_t
926 init_hardware(void)
927 {
928 	// Return B_OK if a device supported by this driver is found; otherwise,
929 	// return B_ERROR so the driver will be unloaded.
930 
931 	if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK)
932 		return B_ERROR;		// unable to access PCI bus
933 
934 	// Check pci devices for a device supported by this driver.
935 
936 	uint32 pciIndex = 0;
937 	pci_info pciInfo;
938 	const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, pciInfo);
939 
940 	TRACE("init_hardware() - %s\n",
941 		pDevice == NULL ? "no supported devices" : "device supported");
942 
943 	put_module(B_PCI_MODULE_NAME);		// put away the module manager
944 
945 	return (pDevice == NULL ? B_ERROR : B_OK);
946 }
947 
948 
949 status_t
950 init_driver(void)
951 {
952 	// Get handle for the pci bus.
953 
954 	if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK)
955 		return B_ERROR;
956 
957 	status_t status = gLock.Init("ATI driver lock");
958 	if (status < B_OK)
959 		return status;
960 
961 	// Get info about all the devices supported by this driver.
962 
963 	uint32 pciIndex = 0;
964 	uint32 count = 0;
965 
966 	while (count < MAX_DEVICES) {
967 		DeviceInfo& di = gDeviceInfo[count];
968 
969 		const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, di.pciInfo);
970 		if (pDevice == NULL)
971 			break;			// all supported devices have been obtained
972 
973 		// Compose device name.
974 		sprintf(di.name, "graphics/" DEVICE_FORMAT,
975 				  di.pciInfo.vendor_id, di.pciInfo.device_id,
976 				  di.pciInfo.bus, di.pciInfo.device, di.pciInfo.function);
977 		TRACE("init_driver() match found; name: %s\n", di.name);
978 
979 		gDeviceNames[count] = di.name;
980 		di.openCount = 0;		// mark driver as available for R/W open
981 		di.sharedArea = -1;		// indicate shared area not yet created
982 		di.sharedInfo = NULL;
983 		di.pChipInfo = pDevice;
984 		count++;
985 		pciIndex++;
986 	}
987 
988 	gDeviceNames[count] = NULL;	// terminate list with null pointer
989 
990 	TRACE("init_driver() %" B_PRIu32 " supported devices\n", count);
991 
992 	return B_OK;
993 }
994 
995 
996 void
997 uninit_driver(void)
998 {
999 	// Free the driver data.
1000 
1001 	gLock.Delete();
1002 	put_module(B_PCI_MODULE_NAME);	// put the pci module away
1003 }
1004 
1005 
1006 const char**
1007 publish_devices(void)
1008 {
1009 	return (const char**)gDeviceNames;	// return list of supported devices
1010 }
1011 
1012 
1013 device_hooks*
1014 find_device(const char* name)
1015 {
1016 	int i = 0;
1017 	while (gDeviceNames[i] != NULL) {
1018 		if (strcmp(name, gDeviceNames[i]) == 0)
1019 			return &gDeviceHooks;
1020 		i++;
1021 	}
1022 
1023 	return NULL;
1024 }
1025 
1026 
1027 
1028 //	#pragma mark - Device Hooks
1029 
1030 
1031 static status_t
1032 device_open(const char* name, uint32 /*flags*/, void** cookie)
1033 {
1034 	status_t status = B_OK;
1035 
1036 	TRACE("device_open() - name: %s\n", name);
1037 
1038 	// Find the device name in the list of devices.
1039 
1040 	int32 i = 0;
1041 	while (gDeviceNames[i] != NULL && (strcmp(name, gDeviceNames[i]) != 0))
1042 		i++;
1043 
1044 	if (gDeviceNames[i] == NULL)
1045 		return B_BAD_VALUE;		// device name not found in list of devices
1046 
1047 	DeviceInfo& di = gDeviceInfo[i];
1048 
1049 	gLock.Acquire();	// make sure no one else has write access to common data
1050 
1051 	if (di.openCount == 0)
1052 		status = InitDevice(di);
1053 
1054 	gLock.Release();
1055 
1056 	if (status == B_OK) {
1057 		di.openCount++;		// mark device open
1058 		*cookie = &di;		// send cookie to opener
1059 	}
1060 
1061 	TRACE("device_open() returning 0x%" B_PRIx32 ",  open count: %" B_PRIu32 "\n", status,
1062 		di.openCount);
1063 	return status;
1064 }
1065 
1066 
1067 static status_t
1068 device_read(void* dev, off_t pos, void* buf, size_t* len)
1069 {
1070 	// Following 3 lines of code are here to eliminate "unused parameter"
1071 	// warnings.
1072 	(void)dev;
1073 	(void)pos;
1074 	(void)buf;
1075 
1076 	*len = 0;
1077 	return B_NOT_ALLOWED;
1078 }
1079 
1080 
1081 static status_t
1082 device_write(void* dev, off_t pos, const void* buf, size_t* len)
1083 {
1084 	// Following 3 lines of code are here to eliminate "unused parameter"
1085 	// warnings.
1086 	(void)dev;
1087 	(void)pos;
1088 	(void)buf;
1089 
1090 	*len = 0;
1091 	return B_NOT_ALLOWED;
1092 }
1093 
1094 
1095 static status_t
1096 device_close(void* dev)
1097 {
1098 	(void)dev;		// avoid compiler warning for unused arg
1099 
1100 	return B_NO_ERROR;
1101 }
1102 
1103 
1104 static status_t
1105 device_free(void* dev)
1106 {
1107 	DeviceInfo& di = *((DeviceInfo*)dev);
1108 	SharedInfo& si = *(di.sharedInfo);
1109 	pci_info& pciInfo = di.pciInfo;
1110 
1111 	TRACE("enter device_free()\n");
1112 
1113 	gLock.Acquire();		// lock driver
1114 
1115 	// If opened multiple times, merely decrement the open count and exit.
1116 
1117 	if (di.openCount <= 1) {
1118 		DisableVBI();		// disable & clear any pending interrupts
1119 
1120 		if (si.bInterruptAssigned) {
1121 			remove_io_interrupt_handler(pciInfo.u.h0.interrupt_line,
1122 				InterruptHandler, &di);
1123 		}
1124 
1125 		// Delete the semaphores, ignoring any errors because the owning team
1126 		// may have died.
1127 		if (si.vertBlankSem >= 0)
1128 			delete_sem(si.vertBlankSem);
1129 		si.vertBlankSem = -1;
1130 
1131 		UnmapDevice(di);	// free regs and frame buffer areas
1132 
1133 		delete_area(di.sharedArea);
1134 		di.sharedArea = -1;
1135 		di.sharedInfo = NULL;
1136 	}
1137 
1138 	if (di.openCount > 0)
1139 		di.openCount--;		// mark device available
1140 
1141 	gLock.Release();	// unlock driver
1142 
1143 	TRACE("exit device_free() openCount: %" B_PRIu32 "\n", di.openCount);
1144 	return B_OK;
1145 }
1146 
1147 
1148 static status_t
1149 device_ioctl(void* dev, uint32 msg, void* buffer, size_t bufferLength)
1150 {
1151 	DeviceInfo& di = *((DeviceInfo*)dev);
1152 
1153 	TRACE("device_ioctl(); ioctl: %" B_PRIu32 ", buffer: %#08" B_PRIxADDR
1154 		", bufLen: %" B_PRIuSIZE "\n", msg, (addr_t)buffer, bufferLength);
1155 
1156 	switch (msg) {
1157 		case B_GET_ACCELERANT_SIGNATURE:
1158 		{
1159 			status_t status = user_strlcpy((char*)buffer, ATI_ACCELERANT_NAME,
1160 				bufferLength);
1161 			if (status < B_OK)
1162 				return status;
1163 
1164 			return B_OK;
1165 		}
1166 
1167 		case ATI_DEVICE_NAME:
1168 		{
1169 			status_t status = user_strlcpy((char*)buffer, di.name,
1170 				B_OS_NAME_LENGTH);
1171 			if (status < B_OK)
1172 				return status;
1173 
1174 			return B_OK;
1175 		}
1176 
1177 		case ATI_GET_SHARED_DATA:
1178 			if (bufferLength != sizeof(area_id))
1179 				return B_BAD_DATA;
1180 
1181 			return user_memcpy(buffer, &di.sharedArea, sizeof(area_id));
1182 
1183 		case ATI_GET_EDID:
1184 		{
1185 			if (bufferLength != sizeof(edid1_raw))
1186 				return B_BAD_DATA;
1187 
1188 			edid1_raw rawEdid;
1189 			status_t status = GetEdidFromBIOS(rawEdid);
1190 			if (status != B_OK)
1191 				return status;
1192 
1193 			return user_memcpy((edid1_raw*)buffer, &rawEdid, sizeof(rawEdid));
1194 		}
1195 
1196 		case ATI_SET_VESA_DISPLAY_MODE:
1197 		{
1198 			if (bufferLength != sizeof(uint16))
1199 				return B_BAD_DATA;
1200 
1201 			uint16 value;
1202 			status_t status = user_memcpy(&value, buffer, sizeof(uint16));
1203 			if (status < B_OK)
1204 				return status;
1205 
1206 			return SetVesaDisplayMode(value);
1207 		}
1208 
1209 		case ATI_RUN_INTERRUPTS:
1210 		{
1211 			if (bufferLength != sizeof(bool))
1212 				return B_BAD_DATA;
1213 
1214 			bool value;
1215 			status_t res = user_memcpy(&value, buffer, sizeof(bool));
1216 			if (res < B_OK)
1217 				return res;
1218 
1219 			if (value)
1220 				EnableVBI();
1221 			else
1222 				DisableVBI();
1223 
1224 			return B_OK;
1225 		}
1226 	}
1227 
1228 	return B_DEV_INVALID_IOCTL;
1229 }
1230