xref: /haiku/src/add-ons/kernel/drivers/graphics/s3/driver.cpp (revision 22440f4105cafc95cc1d49f9bc65bb395c527d86)
1 /*
2 	Copyright 2007-2008 Haiku, Inc.  All rights reserved.
3 	Distributed under the terms of the MIT license.
4 
5 	Authors:
6 	Gerald Zajac 2007-2008
7 */
8 
9 #include <KernelExport.h>
10 #include <PCI.h>
11 #ifdef __HAIKU__
12 #include <drivers/bios.h>
13 #endif	// __HAIKU__
14 #include <malloc.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <graphic_driver.h>
18 
19 #include "DriverInterface.h"
20 
21 
22 #undef TRACE
23 
24 #ifdef ENABLE_DEBUG_TRACE
25 #	define TRACE(x...) dprintf("S3: " x)
26 #else
27 #	define TRACE(x...) ;
28 #endif
29 
30 
31 
32 #define SKD_HANDLER_INSTALLED 0x80000000
33 #define MAX_DEVICES		4
34 #define DEVICE_FORMAT	"%04X_%04X_%02X%02X%02X"
35 
36 int32 api_version = B_CUR_DRIVER_API_VERSION;	// revision of driver API we support
37 
38 #define VENDOR_ID 0x5333	// S3 vendor ID
39 
40 
41 struct ChipInfo {
42 	uint16		chipID;			// PCI device id of the chip
43 	uint16		chipType;		// assigned chip type identifier
44 	const char*	chipName;		// user recognizable name for chip (must be < 32
45 								// chars)
46 };
47 
48 // This table maps a PCI device ID to a chip type identifier and the chip name.
49 // Note that the Trio64 and Trio64V+ chips have the same ID, but have a different
50 // revision number.  After the revision number is examined, the Trio64V+ will
51 // have a different chip type code and name assigned.
52 
53 static const ChipInfo chipTable[] = {
54 	{ 0x8811, S3_TRIO64,		"Trio64"			},		// see comment above
55 	{ 0x8814, S3_TRIO64_UVP,	"Trio64 UV+"		},
56 	{ 0x8901, S3_TRIO64_V2,		"Trio64 V2/DX/GX"	},
57 
58 	{ 0x5631, S3_VIRGE,			"Virge"				},
59 	{ 0x883D, S3_VIRGE_VX,		"Virge VX"			},
60 	{ 0x8A01, S3_VIRGE_DXGX,	"Virge DX/GX"		},
61 	{ 0x8A10, S3_VIRGE_GX2,		"Virge GX2"			},
62 	{ 0x8C01, S3_VIRGE_MX,		"Virge MX"			},
63 	{ 0x8C03, S3_VIRGE_MXP,		"Virge MX+"			},
64 	{ 0x8904, S3_TRIO_3D,		"Trio 3D"			},
65 	{ 0x8A13, S3_TRIO_3D_2X,	"Trio 3D/2X"		},
66 
67 	{ 0x8a20, S3_SAVAGE_3D,		"Savage3D"				},
68 	{ 0x8a21, S3_SAVAGE_3D,		"Savage3D-MV" 			},
69 	{ 0x8a22, S3_SAVAGE4,		"Savage4"				},
70 	{ 0x8a25, S3_PROSAVAGE,		"ProSavage PM133"		},
71 	{ 0x8a26, S3_PROSAVAGE,		"ProSavage KM133"		},
72 	{ 0x8c10, S3_SAVAGE_MX,		"Savage/MX-MV"			},
73 	{ 0x8c11, S3_SAVAGE_MX,		"Savage/MX"				},
74 	{ 0x8c12, S3_SAVAGE_MX,		"Savage/IX-MV"			},
75 	{ 0x8c13, S3_SAVAGE_MX,		"Savage/IX"				},
76 	{ 0x8c22, S3_SUPERSAVAGE,	"SuperSavage/MX 128"	},
77 	{ 0x8c24, S3_SUPERSAVAGE,	"SuperSavage/MX 64"		},
78 	{ 0x8c26, S3_SUPERSAVAGE,	"SuperSavage/MX 64C"	},
79 	{ 0x8c2a, S3_SUPERSAVAGE,	"SuperSavage/IX 128SDR"	},
80 	{ 0x8c2b, S3_SUPERSAVAGE,	"SuperSavage/IX 128DDR"	},
81 	{ 0x8c2c, S3_SUPERSAVAGE,	"SuperSavage/IX 64SDR"	},
82 	{ 0x8c2d, S3_SUPERSAVAGE,	"SuperSavage/IX 64DDR"	},
83 	{ 0x8c2e, S3_SUPERSAVAGE,	"SuperSavage/IXC 64SDR"	},
84 	{ 0x8c2f, S3_SUPERSAVAGE,	"SuperSavage/IXC 64DDR"	},
85 	{ 0x8d01, S3_TWISTER,		"Twister PN133"			},
86 	{ 0x8d02, S3_TWISTER,		"Twister KN133"			},
87 	{ 0x8d03, S3_PROSAVAGE_DDR,	"ProSavage DDR"			},
88 	{ 0x8d04, S3_PROSAVAGE_DDR,	"ProSavage DDR-K"		},
89 	{ 0x9102, S3_SAVAGE2000,	"Savage2000"			},
90 	{ 0,	  0,				NULL				}
91 };
92 
93 
94 struct DeviceInfo {
95 	uint32			openCount;		// count of how many times device has been opened
96 	int32			flags;
97 	area_id 		sharedArea;		// area shared between driver and all accelerants
98 	SharedInfo* 	sharedInfo;				// pointer to shared info area memory
99 	vuint8*	 		regs;			// pointer to memory mapped registers
100 	const ChipInfo*	pChipInfo;		// info about the selected chip
101 	pci_info		pciInfo;		// copy of pci info for this device
102 	char			name[B_OS_NAME_LENGTH]; // name of device
103 };
104 
105 
106 static Benaphore		gLock;
107 static DeviceInfo		gDeviceInfo[MAX_DEVICES];
108 static char*			gDeviceNames[MAX_DEVICES + 1];
109 static pci_module_info*	gPCI;
110 
111 
112 // Prototypes for device hook functions.
113 
114 static status_t device_open(const char* name, uint32 flags, void** cookie);
115 static status_t device_close(void* dev);
116 static status_t device_free(void* dev);
117 static status_t device_read(void* dev, off_t pos, void* buf, size_t* len);
118 static status_t device_write(void* dev, off_t pos, const void* buf, size_t* len);
119 static status_t device_ioctl(void* dev, uint32 msg, void* buf, size_t len);
120 
121 static device_hooks gDeviceHooks =
122 {
123 	device_open,
124 	device_close,
125 	device_free,
126 	device_ioctl,
127 	device_read,
128 	device_write,
129 	NULL,
130 	NULL,
131 	NULL,
132 	NULL
133 };
134 
135 
136 
137 static inline uint32
138 GetPCI(pci_info& info, uint8 offset, uint8 size)
139 {
140 	return gPCI->read_pci_config(info.bus, info.device, info.function, offset, size);
141 }
142 
143 
144 static inline void
145 SetPCI(pci_info& info, uint8 offset, uint8 size, uint32 value)
146 {
147 	gPCI->write_pci_config(info.bus, info.device, info.function, offset, size, value);
148 }
149 
150 
151 // Functions for dealing with Vertical Blanking Interrupts.  Currently, I do
152 // not know the commands to handle these operations;  thus, these functions
153 // currently do nothing.
154 
155 static bool
156 InterruptIsVBI()
157 {
158 	// return true only if a vertical blanking interrupt has occured
159 	return false;
160 }
161 
162 
163 static void
164 ClearVBI()
165 {
166 }
167 
168 static void
169 EnableVBI()
170 {
171 }
172 
173 static void
174 DisableVBI()
175 {
176 }
177 
178 
179 static status_t
180 MapDevice(DeviceInfo& di)
181 {
182 	char areaName[B_OS_NAME_LENGTH];
183 	SharedInfo& si = *(di.sharedInfo);
184 	pci_info& pciInfo = di.pciInfo;
185 
186 	TRACE("enter MapDevice()\n");
187 
188 	// Enable memory mapped IO and bus master.
189 
190 	SetPCI(pciInfo, PCI_command, 2, GetPCI(pciInfo, PCI_command, 2)
191 		| PCI_command_io | PCI_command_memory | PCI_command_master);
192 
193 	const uint32 SavageMmioRegBaseOld	= 0x1000000;	// 16 MB
194 	const uint32 SavageMmioRegBaseNew	= 0x0000000;
195 	const uint32 SavageMmioRegSize		= 0x0080000;	// 512 KB reg area size
196 
197 	const uint32 VirgeMmioRegBase		= 0x1000000;	// 16 MB
198 	const uint32 VirgeMmioRegSize		= 0x10000;		// 64 KB reg area size
199 
200 	uint32 videoRamAddr = 0;
201 	uint32 videoRamSize = 0;
202 	uint32 regsBase = 0;
203 	uint32 regAreaSize = 0;
204 
205 	// Since we do not know at this point the actual size of the video
206 	// memory, set it to the largest value that the respective chipset
207 	// family can have.
208 
209 	if (S3_SAVAGE_FAMILY(di.pChipInfo->chipType)) {
210 		if (S3_SAVAGE_3D_SERIES(di.pChipInfo->chipType)) {
211 			// Savage 3D & Savage MX chips.
212 
213 			regsBase = pciInfo.u.h0.base_registers[0] + SavageMmioRegBaseOld;
214 			regAreaSize = SavageMmioRegSize;
215 
216 	 		videoRamAddr = pciInfo.u.h0.base_registers[0];
217 			videoRamSize = 16 * 1024 * 1024;	// 16 MB is max for 3D series
218 			si.videoMemPCI = (void *)(pciInfo.u.h0.base_registers_pci[0]);
219 		} else {
220 			// All other Savage chips.
221 
222 			regsBase = pciInfo.u.h0.base_registers[0] + SavageMmioRegBaseNew;
223 			regAreaSize = SavageMmioRegSize;
224 
225 			videoRamAddr = pciInfo.u.h0.base_registers[1];
226 			videoRamSize = pciInfo.u.h0.base_register_sizes[1];
227 			si.videoMemPCI = (void *)(pciInfo.u.h0.base_registers_pci[1]);
228 		}
229 	} else {
230 		// Trio/Virge chips.
231 
232 		regsBase = pciInfo.u.h0.base_registers[0] + VirgeMmioRegBase;
233 		regAreaSize = VirgeMmioRegSize;
234 
235  		videoRamAddr = pciInfo.u.h0.base_registers[0];
236 		videoRamSize = 8 * 1024 * 1024;	// 8 MB is max for Trio/Virge chips
237 		si.videoMemPCI = (void *)(pciInfo.u.h0.base_registers_pci[0]);
238 	}
239 
240 	// Map the MMIO register area.
241 
242 	sprintf(areaName, DEVICE_FORMAT " regs",
243 		pciInfo.vendor_id, pciInfo.device_id,
244 		pciInfo.bus, pciInfo.device, pciInfo.function);
245 
246 	si.regsArea = map_physical_memory(areaName, regsBase, regAreaSize,
247 		B_ANY_KERNEL_ADDRESS,
248 		0,		// neither read nor write, to hide it from user space apps
249 		(void**)(&(di.regs)));
250 
251 	if (si.regsArea < 0)
252 		return si.regsArea;	// return error code
253 
254 	// Map the video memory.
255 
256 	sprintf(areaName, DEVICE_FORMAT " framebuffer",
257 		pciInfo.vendor_id, pciInfo.device_id,
258 		pciInfo.bus, pciInfo.device, pciInfo.function);
259 
260 	si.videoMemArea = map_physical_memory(
261 		areaName,
262 		videoRamAddr,
263 		videoRamSize,
264 		B_ANY_KERNEL_BLOCK_ADDRESS | B_MTR_WC,
265 		B_READ_AREA + B_WRITE_AREA,
266 		&(si.videoMemAddr));
267 
268 	if (si.videoMemArea < 0) {
269 		// Try to map this time without write combining.
270 		si.videoMemArea = map_physical_memory(
271 			areaName,
272 			videoRamAddr,
273 			videoRamSize,
274 			B_ANY_KERNEL_BLOCK_ADDRESS,
275 			B_READ_AREA + B_WRITE_AREA,
276 			&(si.videoMemAddr));
277 	}
278 
279 	TRACE("Video memory, area: %ld,  addr: 0x%" B_PRIXADDR "\n",
280 		si.videoMemArea, (addr_t)(si.videoMemAddr));
281 
282 	// If there was an error, delete other areas.
283 	if (si.videoMemArea < 0) {
284 		delete_area(si.regsArea);
285 		si.regsArea = -1;
286 	}
287 
288 	TRACE("leave MapDevice(); result: %ld\n", si.videoMemArea);
289 	return si.videoMemArea;
290 }
291 
292 
293 static void
294 UnmapDevice(DeviceInfo& di)
295 {
296 	SharedInfo& si = *(di.sharedInfo);
297 
298 	TRACE("enter UnmapDevice()\n");
299 
300 	if (si.regsArea >= 0)
301 		delete_area(si.regsArea);
302 	if (si.videoMemArea >= 0)
303 		delete_area(si.videoMemArea);
304 
305 	si.regsArea = si.videoMemArea = -1;
306 	si.videoMemAddr = NULL;
307 	di.regs = NULL;
308 
309 	TRACE("exit UnmapDevice()\n");
310 }
311 
312 
313 static int32
314 InterruptHandler(void* data)
315 {
316 	int32 handled = B_UNHANDLED_INTERRUPT;
317 	DeviceInfo& di = *((DeviceInfo*)data);
318 	int32* flags = &(di.flags);
319 
320 	// Is someone already handling an interrupt for this device?
321 	if (atomic_or(flags, SKD_HANDLER_INSTALLED) & SKD_HANDLER_INSTALLED)
322 		return B_UNHANDLED_INTERRUPT;
323 
324 	if (InterruptIsVBI()) {	// was interrupt a VBI?
325 		ClearVBI();			// clear interrupt
326 
327 		handled = B_HANDLED_INTERRUPT;
328 
329 		// Release vertical blanking semaphore.
330 		sem_id& sem = di.sharedInfo->vertBlankSem;
331 
332 		if (sem >= 0) {
333 			int32 blocked;
334 			if ((get_sem_count(sem, &blocked) == B_OK) && (blocked < 0)) {
335 				release_sem_etc(sem, -blocked, B_DO_NOT_RESCHEDULE);
336 				handled = B_INVOKE_SCHEDULER;
337 			}
338 		}
339 	}
340 
341 	atomic_and(flags, ~SKD_HANDLER_INSTALLED);	// note we're not in handler anymore
342 
343 	return handled;
344 }
345 
346 
347 static void
348 InitInterruptHandler(DeviceInfo& di)
349 {
350 	SharedInfo& si = *(di.sharedInfo);
351 
352 	TRACE("enter InitInterruptHandler()\n");
353 
354 	DisableVBI();					// disable & clear any pending interrupts
355 	si.bInterruptAssigned = false;	// indicate interrupt not assigned yet
356 
357 	// Create a semaphore for vertical blank management.
358 	si.vertBlankSem = create_sem(0, di.name);
359 	if (si.vertBlankSem < 0)
360 		return;
361 
362 	// Change the owner of the semaphores to the calling team (usually the
363 	// app_server).  This is required because apps can't aquire kernel
364 	// semaphores.
365 
366 	thread_id threadID = find_thread(NULL);
367 	thread_info threadInfo;
368 	status_t status = get_thread_info(threadID, &threadInfo);
369 	if (status == B_OK)
370 		status = set_sem_owner(si.vertBlankSem, threadInfo.team);
371 
372 	// If there is a valid interrupt assigned, set up interrupts.
373 
374 	if (status == B_OK && di.pciInfo.u.h0.interrupt_pin != 0x00
375 		&& di.pciInfo.u.h0.interrupt_line != 0xff) {
376 		// We have a interrupt line to use.
377 
378 		status = install_io_interrupt_handler(di.pciInfo.u.h0.interrupt_line,
379 			InterruptHandler, (void*)(&di), 0);
380 
381 		if (status == B_OK)
382 			si.bInterruptAssigned = true;	// we can use interrupt related functions
383 	}
384 
385 	if (status != B_OK) {
386 		// Interrupt does not exist; thus delete semaphore as it won't be used.
387 		delete_sem(si.vertBlankSem);
388 		si.vertBlankSem = -1;
389 	}
390 }
391 
392 
393 static status_t
394 InitDevice(DeviceInfo& di)
395 {
396 	// Perform initialization and mapping of the device, and return B_OK if
397 	// sucessful;  else, return error code.
398 
399 	// Create the area for shared info with NO user-space read or write
400 	// permissions, to prevent accidental damage.
401 
402 	TRACE("enter InitDevice()\n");
403 
404 	pci_info& pciInfo = di.pciInfo;
405 	char sharedName[B_OS_NAME_LENGTH];
406 
407 	sprintf(sharedName, DEVICE_FORMAT " shared",
408 		pciInfo.vendor_id, pciInfo.device_id,
409 		pciInfo.bus, pciInfo.device, pciInfo.function);
410 
411 	di.sharedArea = create_area(sharedName, (void**) &(di.sharedInfo),
412 		B_ANY_KERNEL_ADDRESS,
413 		((sizeof(SharedInfo) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1)),
414 		B_FULL_LOCK, 0);
415 	if (di.sharedArea < 0)
416 		return di.sharedArea;	// return error code
417 
418 	SharedInfo& si = *(di.sharedInfo);
419 
420 	si.vendorID = pciInfo.vendor_id;
421 	si.deviceID = pciInfo.device_id;
422 	si.revision = pciInfo.revision;
423 	si.chipType = di.pChipInfo->chipType;
424 	strcpy(si.chipName, di.pChipInfo->chipName);
425 
426 	// Trio64 and Trio64V+ chips have the same ID but different revision numbers.
427 	// Since the Trio64V+ supports MMIO, better performance can be obtained
428 	// from it if it is distinguished from the Trio64.
429 
430 	if (si.chipType == S3_TRIO64 && si.revision & 0x40) {
431 		si.chipType = S3_TRIO64_VP;
432 		strcpy(si.chipName, "Trio64 V+");
433 	}
434 
435 	status_t status = MapDevice(di);
436 	if (status < 0) {
437 		delete_area(di.sharedArea);
438 		di.sharedArea = -1;
439 		di.sharedInfo = NULL;
440 		return status;		// return error code
441 	}
442 
443 	InitInterruptHandler(di);
444 
445 	TRACE("Interrupt assigned:  %s\n", si.bInterruptAssigned ? "yes" : "no");
446 	return B_OK;
447 }
448 
449 
450 static const ChipInfo*
451 GetNextSupportedDevice(uint32& pciIndex, pci_info& pciInfo)
452 {
453 	// Search the PCI devices for a device that is supported by this driver.
454 	// The search starts at the device specified by argument pciIndex, and
455 	// continues until a supported device is found or there are no more devices
456 	// to examine.  Argument pciIndex is incremented after each device is
457 	// examined.
458 
459 	// If a supported device is found, return a pointer to the struct containing
460 	// the chip info; else return NULL.
461 
462 	while (gPCI->get_nth_pci_info(pciIndex, &pciInfo) == B_OK) {
463 		if (pciInfo.vendor_id == VENDOR_ID) {
464 			// Search the table of supported devices to find a chip/device that
465 			// matches device ID of the current PCI device.
466 
467 			const ChipInfo* pDevice = chipTable;
468 
469 			while (pDevice->chipID != 0) {		// end of table?
470 				if (pDevice->chipID == pciInfo.device_id) {
471 					pciIndex++;
472 					return pDevice;	// matching device/chip found
473 				}
474 
475 				pDevice++;
476 			}
477 		}
478 
479 		pciIndex++;
480 	}
481 
482 	return NULL;		// no supported device found found
483 }
484 
485 
486 
487 #ifdef __HAIKU__
488 
489 static status_t
490 GetEdidFromBIOS(edid1_raw& edidRaw)
491 {
492 	// Get the EDID info from the video BIOS, and return B_OK if successful.
493 
494 #define ADDRESS_SEGMENT(address) ((addr_t)(address) >> 4)
495 #define ADDRESS_OFFSET(address) ((addr_t)(address) & 0xf)
496 
497 	bios_module_info* biosModule;
498 	status_t status = get_module(B_BIOS_MODULE_NAME, (module_info**)&biosModule);
499 	if (status != B_OK) {
500 		TRACE("GetEdidFromBIOS(): failed to get BIOS module: 0x%" B_PRIx32 "\n",
501 			status);
502 		return status;
503 	}
504 
505 	bios_state* state;
506 	status = biosModule->prepare(&state);
507 	if (status != B_OK) {
508 		TRACE("GetEdidFromBIOS(): bios_prepare() failed: 0x%" B_PRIx32 "\n",
509 			status);
510 		put_module(B_BIOS_MODULE_NAME);
511 		return status;
512 	}
513 
514 	bios_regs regs = {};
515 	regs.eax = 0x4f15;
516 	regs.ebx = 0;			// 0 = report DDC service
517 	regs.ecx = 0;
518 	regs.es = 0;
519 	regs.edi = 0;
520 
521 	status = biosModule->interrupt(state, 0x10, &regs);
522 	if (status == B_OK) {
523 		// AH contains the error code, and AL determines whether or not the
524 		// function is supported.
525 		if (regs.eax != 0x4f)
526 			status = B_NOT_SUPPORTED;
527 
528 		// Test if DDC is supported by the monitor.
529 		if ((regs.ebx & 3) == 0)
530 			status = B_NOT_SUPPORTED;
531 	}
532 
533 	if (status == B_OK) {
534 		edid1_raw* edid = (edid1_raw*)biosModule->allocate_mem(state,
535 			sizeof(edid1_raw));
536 		if (edid == NULL) {
537 			status = B_NO_MEMORY;
538 			goto out;
539 		}
540 
541 		regs.eax = 0x4f15;
542 		regs.ebx = 1;		// 1 = read EDID
543 		regs.ecx = 0;
544 		regs.edx = 0;
545 		regs.es  = ADDRESS_SEGMENT(edid);
546 		regs.edi = ADDRESS_OFFSET(edid);
547 
548 		status = biosModule->interrupt(state, 0x10, &regs);
549 		if (status == B_OK) {
550 			if (regs.eax != 0x4f) {
551 				status = B_NOT_SUPPORTED;
552 			} else {
553 				// Copy the EDID info to the caller's location, and compute the
554 				// checksum of the EDID info while copying.
555 
556 				uint8 sum = 0;
557 				uint8 allOr = 0;
558 				uint8* dest = (uint8*)&edidRaw;
559 				uint8* src = (uint8*)edid;
560 
561 				for (uint32 j = 0; j < sizeof(edidRaw); j++) {
562 					sum += *src;
563 					allOr |= *src;
564 					*dest++ = *src++;
565 				}
566 
567 				if (allOr == 0) {
568 					TRACE("GetEdidFromBIOS(); EDID info contains only zeros\n");
569 					status = B_ERROR;
570 				} else if (sum != 0) {
571 					TRACE("GetEdidFromBIOS(); Checksum error in EDID info\n");
572 					status = B_ERROR;
573 				}
574 			}
575 		}
576 	}
577 
578 out:
579 	biosModule->finish(state);
580 	put_module(B_BIOS_MODULE_NAME);
581 
582 	TRACE("GetEdidFromBIOS() status: 0x%" B_PRIx32 "\n", status);
583 	return status;
584 }
585 
586 #endif	// __HAIKU__
587 
588 
589 
590 //	#pragma mark - Kernel Interface
591 
592 
593 status_t
594 init_hardware(void)
595 {
596 	// Return B_OK if a device supported by this driver is found; otherwise,
597 	// return B_ERROR so the driver will be unloaded.
598 
599 	if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK)
600 		return B_ERROR;		// unable to access PCI bus
601 
602 	// Check pci devices for a device supported by this driver.
603 
604 	uint32 pciIndex = 0;
605 	pci_info pciInfo;
606 	const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, pciInfo);
607 
608 	TRACE("init_hardware() - %s\n", pDevice == NULL ? "no supported devices" : "device supported");
609 
610 	put_module(B_PCI_MODULE_NAME);		// put away the module manager
611 
612 	return (pDevice == NULL ? B_ERROR : B_OK);
613 }
614 
615 
616 status_t  init_driver(void)
617 {
618 	// Get handle for the pci bus.
619 
620 	if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK)
621 		return B_ERROR;
622 
623 	status_t status = gLock.Init("S3 driver lock");
624 	if (status < B_OK)
625 		return status;
626 
627 	// Get info about all the devices supported by this driver.
628 
629 	uint32 pciIndex = 0;
630 	uint32 count = 0;
631 
632 	while (count < MAX_DEVICES) {
633 		DeviceInfo& di = gDeviceInfo[count];
634 
635 		const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, di.pciInfo);
636 		if (pDevice == NULL)
637 			break;			// all supported devices have been obtained
638 
639 		// Compose device name.
640 		sprintf(di.name, "graphics/" DEVICE_FORMAT,
641 				  di.pciInfo.vendor_id, di.pciInfo.device_id,
642 				  di.pciInfo.bus, di.pciInfo.device, di.pciInfo.function);
643 		TRACE("init_driver() match found; name: %s\n", di.name);
644 
645 		gDeviceNames[count] = di.name;
646 		di.openCount = 0;		// mark driver as available for R/W open
647 		di.sharedArea = -1;		// indicate shared area not yet created
648 		di.sharedInfo = NULL;
649 		di.pChipInfo = pDevice;
650 		count++;
651 	}
652 
653 	gDeviceNames[count] = NULL;	// terminate list with null pointer
654 
655 	TRACE("init_driver() %ld supported devices\n", count);
656 
657 	return B_OK;
658 }
659 
660 
661 void
662 uninit_driver(void)
663 {
664 	// Free the driver data.
665 
666 	gLock.Delete();
667 	put_module(B_PCI_MODULE_NAME);	// put the pci module away
668 }
669 
670 
671 const char**
672 publish_devices(void)
673 {
674 	return (const char**)gDeviceNames;	// return list of supported devices
675 }
676 
677 
678 device_hooks*
679 find_device(const char* name)
680 {
681 	int index = 0;
682 	while (gDeviceNames[index] != NULL) {
683 		if (strcmp(name, gDeviceNames[index]) == 0)
684 			return &gDeviceHooks;
685 		index++;
686 	}
687 
688 	return NULL;
689 }
690 
691 
692 
693 //	#pragma mark - Device Hooks
694 
695 
696 static status_t
697 device_open(const char* name, uint32 /*flags*/, void** cookie)
698 {
699 	status_t status = B_OK;
700 
701 	TRACE("device_open() - name: %s, cookie: 0x%" B_PRIXADDR "\n", name,
702 		(addr_t)cookie);
703 
704 	// Find the device name in the list of devices.
705 
706 	int32 index = 0;
707 	while (gDeviceNames[index] != NULL && (strcmp(name, gDeviceNames[index]) != 0))
708 		index++;
709 
710 	if (gDeviceNames[index] == NULL)
711 		return B_BAD_VALUE;		// device name not found in list of devices
712 
713 	DeviceInfo& di = gDeviceInfo[index];
714 
715 	gLock.Acquire();	// make sure no one else has write access to common data
716 
717 	if (di.openCount == 0)
718 		status = InitDevice(di);
719 
720 	gLock.Release();
721 
722 	if (status == B_OK) {
723 		di.openCount++;		// mark device open
724 		*cookie = &di;		// send cookie to opener
725 	}
726 
727 	TRACE("device_open() returning 0x%lx,  open count: %ld\n", status, di.openCount);
728 	return status;
729 }
730 
731 
732 static status_t
733 device_read(void* dev, off_t pos, void* buf, size_t* len)
734 {
735 	// Following 3 lines of code are here to eliminate "unused parameter" warnings.
736 	(void)dev;
737 	(void)pos;
738 	(void)buf;
739 
740 	*len = 0;
741 	return B_NOT_ALLOWED;
742 }
743 
744 
745 static status_t
746 device_write(void* dev, off_t pos, const void* buf, size_t* len)
747 {
748 	// Following 3 lines of code are here to eliminate "unused parameter" warnings.
749 	(void)dev;
750 	(void)pos;
751 	(void)buf;
752 
753 	*len = 0;
754 	return B_NOT_ALLOWED;
755 }
756 
757 
758 static status_t
759 device_close(void* dev)
760 {
761 	(void)dev;		// avoid compiler warning for unused arg
762 
763 	TRACE("device_close()\n");
764 	return B_NO_ERROR;
765 }
766 
767 
768 static status_t
769 device_free(void* dev)
770 {
771 	DeviceInfo& di = *((DeviceInfo*)dev);
772 	SharedInfo& si = *(di.sharedInfo);
773 	pci_info& pciInfo = di.pciInfo;
774 
775 	TRACE("enter device_free()\n");
776 
777 	gLock.Acquire();		// lock driver
778 
779 	// If opened multiple times, merely decrement the open count and exit.
780 
781 	if (di.openCount <= 1) {
782 		DisableVBI();		// disable & clear any pending interrupts
783 
784 		if (si.bInterruptAssigned) {
785 			remove_io_interrupt_handler(pciInfo.u.h0.interrupt_line, InterruptHandler, &di);
786 		}
787 
788 		// Delete the semaphores, ignoring any errors because the owning team may have died.
789 		if (si.vertBlankSem >= 0)
790 			delete_sem(si.vertBlankSem);
791 		si.vertBlankSem = -1;
792 
793 		UnmapDevice(di);	// free regs and frame buffer areas
794 
795 		delete_area(di.sharedArea);
796 		di.sharedArea = -1;
797 		di.sharedInfo = NULL;
798 	}
799 
800 	if (di.openCount > 0)
801 		di.openCount--;		// mark device available
802 
803 	gLock.Release();	// unlock driver
804 
805 	TRACE("exit device_free() openCount: %ld\n", di.openCount);
806 	return B_OK;
807 }
808 
809 
810 static status_t
811 device_ioctl(void* dev, uint32 msg, void* buf, size_t len)
812 {
813 	DeviceInfo& di = *((DeviceInfo*)dev);
814 
815 	(void)len;		// avoid compiler warning for unused arg
816 
817 //	TRACE("device_ioctl(); ioctl: %lu, buf: 0x%08lx, len: %lu\n", msg, (uint32)buf, len);
818 
819 	switch (msg) {
820 		case B_GET_ACCELERANT_SIGNATURE:
821 			strcpy((char*)buf, "s3.accelerant");
822 			return B_OK;
823 
824 		case S3_DEVICE_NAME:
825 			strncpy((char*)buf, di.name, B_OS_NAME_LENGTH);
826 			((char*)buf)[B_OS_NAME_LENGTH -1] = '\0';
827 			return B_OK;
828 
829 		case S3_GET_PRIVATE_DATA:
830 		{
831 			S3GetPrivateData* gpd = (S3GetPrivateData*)buf;
832 			if (gpd->magic == S3_PRIVATE_DATA_MAGIC) {
833 				gpd->sharedInfoArea = di.sharedArea;
834 				return B_OK;
835 			}
836 			break;
837 		}
838 
839 		case S3_GET_EDID:
840 		{
841 #ifdef __HAIKU__
842 			S3GetEDID* ged = (S3GetEDID*)buf;
843 			if (ged->magic == S3_PRIVATE_DATA_MAGIC) {
844 				edid1_raw rawEdid;
845 				status_t status = GetEdidFromBIOS(rawEdid);
846 				if (status == B_OK)
847 					user_memcpy(&ged->rawEdid, &rawEdid, sizeof(rawEdid));
848 				return status;
849 			}
850 #else
851 			return B_UNSUPPORTED;
852 #endif
853 			break;
854 		}
855 
856 		case S3_GET_PIO:
857 		{
858 			S3GetSetPIO* gsp = (S3GetSetPIO*)buf;
859 			if (gsp->magic == S3_PRIVATE_DATA_MAGIC) {
860 				switch (gsp->size) {
861 					case 1:
862 						gsp->value = gPCI->read_io_8(gsp->offset);
863 						break;
864 					case 2:
865 						gsp->value = gPCI->read_io_16(gsp->offset);
866 						break;
867 					case 4:
868 						gsp->value = gPCI->read_io_32(gsp->offset);
869 						break;
870 					default:
871 						TRACE("device_ioctl() S3_GET_PIO invalid size: %ld\n", gsp->size);
872 						return B_ERROR;
873 				}
874 				return B_OK;
875 			}
876 			break;
877 		}
878 
879 		case S3_SET_PIO:
880 		{
881 			S3GetSetPIO* gsp = (S3GetSetPIO*)buf;
882 			if (gsp->magic == S3_PRIVATE_DATA_MAGIC) {
883 				switch (gsp->size) {
884 					case 1:
885 						gPCI->write_io_8(gsp->offset, gsp->value);
886 						break;
887 					case 2:
888 						gPCI->write_io_16(gsp->offset, gsp->value);
889 						break;
890 					case 4:
891 						gPCI->write_io_32(gsp->offset, gsp->value);
892 						break;
893 					default:
894 						TRACE("device_ioctl() S3_SET_PIO invalid size: %ld\n", gsp->size);
895 						return B_ERROR;
896 				}
897 				return B_OK;
898 			}
899 			break;
900 		}
901 
902 		case S3_RUN_INTERRUPTS:
903 		{
904 			S3SetBoolState* ri = (S3SetBoolState*)buf;
905 			if (ri->magic == S3_PRIVATE_DATA_MAGIC) {
906 				if (ri->bEnable)
907 					EnableVBI();
908 				else
909 					DisableVBI();
910 			}
911 			return B_OK;
912 		}
913 	}
914 
915 	return B_DEV_INVALID_IOCTL;
916 }
917