xref: /haiku/src/add-ons/kernel/drivers/graphics/3dfx/driver.cpp (revision fc7456e9b1ec38c941134ed6d01c438cf289381e)
1 /*
2  * Copyright 2007-2010 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 <malloc.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <graphic_driver.h>
15 #ifdef __HAIKU__
16 #include <boot_item.h>
17 #endif	// __HAIKU__
18 
19 #include "DriverInterface.h"
20 
21 
22 #undef TRACE
23 
24 #ifdef ENABLE_DEBUG_TRACE
25 #	define TRACE(x...) dprintf("3dfx: " x)
26 #else
27 #	define TRACE(x...) ;
28 #endif
29 
30 
31 #define ACCELERANT_NAME	 "3dfx.accelerant"
32 
33 #define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
34 
35 #define SKD_HANDLER_INSTALLED 0x80000000
36 #define MAX_DEVICES		4
37 #define DEVICE_FORMAT	"%04X_%04X_%02X%02X%02X"
38 
39 int32 api_version = B_CUR_DRIVER_API_VERSION;	// revision of driver API used
40 
41 #define VENDOR_ID	0x121A		// 3DFX vendor ID
42 
43 
44 struct ChipInfo {
45 	uint16		chipID;			// PCI device id of the chip
46 	ChipType	chipType;		// assigned chip type identifier
47 	const char*	chipName;		// user recognizable name for chip
48 								//   (must be < 32 chars)
49 };
50 
51 
52 // This table maps a PCI device ID to a chip type identifier and the chip name.
53 
54 static const ChipInfo chipTable[] = {
55 	{ 0x03, BANSHEE,	"Banshee"	},
56 	{ 0x05, VOODOO_3,	"Voodoo 3"	},
57 	{ 0x09, VOODOO_5,	"Voodoo 5"	},
58 	{ 0,	TDFX_NONE,	NULL }
59 };
60 
61 
62 struct DeviceInfo {
63 	uint32			openCount;		// count of how many times device has been opened
64 	int32			flags;
65 	area_id 		sharedArea;		// area shared between driver and all accelerants
66 	SharedInfo* 	sharedInfo;		// pointer to shared info area memory
67 	vuint8*	 		regs;			// pointer to memory mapped registers
68 	const ChipInfo*	pChipInfo;		// info about the selected chip
69 	pci_info		pciInfo;		// copy of pci info for this device
70 	char			name[B_OS_NAME_LENGTH]; // name of device
71 };
72 
73 
74 static Benaphore		gLock;
75 static DeviceInfo		gDeviceInfo[MAX_DEVICES];
76 static char*			gDeviceNames[MAX_DEVICES + 1];
77 static pci_module_info*	gPCI;
78 
79 
80 // Prototypes for device hook functions.
81 
82 static status_t device_open(const char* name, uint32 flags, void** cookie);
83 static status_t device_close(void* dev);
84 static status_t device_free(void* dev);
85 static status_t device_read(void* dev, off_t pos, void* buf, size_t* len);
86 static status_t device_write(void* dev, off_t pos, const void* buf,
87 					size_t* len);
88 static status_t device_ioctl(void* dev, uint32 msg, void* buf, size_t len);
89 
90 static device_hooks gDeviceHooks =
91 {
92 	device_open,
93 	device_close,
94 	device_free,
95 	device_ioctl,
96 	device_read,
97 	device_write,
98 	NULL,
99 	NULL,
100 	NULL,
101 	NULL
102 };
103 
104 
105 
106 static inline uint32
107 GetPCI(pci_info& info, uint8 offset, uint8 size)
108 {
109 	return gPCI->read_pci_config(info.bus, info.device, info.function, offset,
110 		size);
111 }
112 
113 
114 static inline void
115 SetPCI(pci_info& info, uint8 offset, uint8 size, uint32 value)
116 {
117 	gPCI->write_pci_config(info.bus, info.device, info.function, offset, size,
118 		value);
119 }
120 
121 
122 static status_t
123 MapDevice(DeviceInfo& di)
124 {
125 	SharedInfo& si = *(di.sharedInfo);
126 	pci_info& pciInfo = di.pciInfo;
127 
128 	TRACE("enter MapDevice()\n");
129 
130 	// Enable memory mapped IO and bus master.
131 
132 	SetPCI(pciInfo, PCI_command, 2, GetPCI(pciInfo, PCI_command, 2)
133 		| PCI_command_io | PCI_command_memory | PCI_command_master);
134 
135 	// Map the video memory.
136 
137 	phys_addr_t videoRamAddr = pciInfo.u.h0.base_registers[1];
138 	uint32 videoRamSize = pciInfo.u.h0.base_register_sizes[1];
139 	si.videoMemPCI = videoRamAddr;
140 	char frameBufferAreaName[] = "3DFX frame buffer";
141 
142 	si.videoMemArea = map_physical_memory(
143 		frameBufferAreaName,
144 		videoRamAddr,
145 		videoRamSize,
146 		B_ANY_KERNEL_BLOCK_ADDRESS | B_WRITE_COMBINING_MEMORY,
147 		B_READ_AREA + B_WRITE_AREA,
148 		(void**)&si.videoMemAddr);
149 
150 	TRACE("Video memory, area: %ld,  addr: 0x%lX, size: %ld\n",
151 		si.videoMemArea, (uint32)(si.videoMemAddr), videoRamSize);
152 
153 	if (si.videoMemArea < 0) {
154 		// Try to map this time without write combining.
155 		si.videoMemArea = map_physical_memory(
156 			frameBufferAreaName,
157 			videoRamAddr,
158 			videoRamSize,
159 			B_ANY_KERNEL_BLOCK_ADDRESS,
160 			B_READ_AREA + B_WRITE_AREA,
161 			(void**)&si.videoMemAddr);
162 	}
163 
164 	if (si.videoMemArea < 0)
165 		return si.videoMemArea;
166 
167 	// Map the MMIO register area.
168 
169 	phys_addr_t regsBase = pciInfo.u.h0.base_registers[0];
170 	uint32 regAreaSize = pciInfo.u.h0.base_register_sizes[0];
171 
172 	si.regsArea = map_physical_memory("3DFX mmio registers",
173 		regsBase,
174 		regAreaSize,
175 		B_ANY_KERNEL_ADDRESS,
176 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA,
177 		(void**)&di.regs);
178 
179 	// If there was an error, delete other areas.
180 	if (si.regsArea < 0) {
181 		delete_area(si.videoMemArea);
182 		si.videoMemArea = -1;
183 	}
184 
185 	TRACE("leave MapDevice(); result: %ld\n", si.regsArea);
186 	return si.regsArea;
187 }
188 
189 
190 static void
191 UnmapDevice(DeviceInfo& di)
192 {
193 	SharedInfo& si = *(di.sharedInfo);
194 
195 	if (si.regsArea >= 0)
196 		delete_area(si.regsArea);
197 	if (si.videoMemArea >= 0)
198 		delete_area(si.videoMemArea);
199 
200 	si.regsArea = si.videoMemArea = -1;
201 	si.videoMemAddr = (addr_t)NULL;
202 	di.regs = NULL;
203 }
204 
205 
206 static status_t
207 InitDevice(DeviceInfo& di)
208 {
209 	// Perform initialization and mapping of the device, and return B_OK if
210 	// sucessful;  else, return error code.
211 
212 	// Create the area for shared info with NO user-space read or write
213 	// permissions, to prevent accidental damage.
214 
215 	TRACE("enter InitDevice()\n");
216 
217 	size_t sharedSize = (sizeof(SharedInfo) + 7) & ~7;
218 
219 	di.sharedArea = create_area("3DFX shared info",
220 		(void**) &(di.sharedInfo),
221 		B_ANY_KERNEL_ADDRESS,
222 		ROUND_TO_PAGE_SIZE(sharedSize),
223 		B_FULL_LOCK,
224 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA);
225 	if (di.sharedArea < 0)
226 		return di.sharedArea;	// return error code
227 
228 	SharedInfo& si = *(di.sharedInfo);
229 
230 	memset(&si, 0, sharedSize);
231 
232 	pci_info& pciInfo = di.pciInfo;
233 
234 	si.vendorID = pciInfo.vendor_id;
235 	si.deviceID = pciInfo.device_id;
236 	si.revision = pciInfo.revision;
237 	si.chipType = di.pChipInfo->chipType;
238 	strcpy(si.chipName, di.pChipInfo->chipName);
239 
240 	status_t status = MapDevice(di);
241 	if (status < 0) {
242 		delete_area(di.sharedArea);
243 		di.sharedArea = -1;
244 		di.sharedInfo = NULL;
245 		return status;		// return error code
246 	}
247 
248 	return B_OK;
249 }
250 
251 
252 static const ChipInfo*
253 GetNextSupportedDevice(uint32& pciIndex, pci_info& pciInfo)
254 {
255 	// Search the PCI devices for a device that is supported by this driver.
256 	// The search starts at the device specified by argument pciIndex, and
257 	// continues until a supported device is found or there are no more devices
258 	// to examine.  Argument pciIndex is incremented after each device is
259 	// examined.
260 
261 	// If a supported device is found, return a pointer to the struct containing
262 	// the chip info; else return NULL.
263 
264 	while (gPCI->get_nth_pci_info(pciIndex, &pciInfo) == B_OK) {
265 
266 		if (pciInfo.vendor_id == VENDOR_ID) {
267 
268 			// Search the table of supported devices to find a chip/device that
269 			// matches device ID of the current PCI device.
270 
271 			const ChipInfo* pDevice = chipTable;
272 
273 			while (pDevice->chipID != 0) {	// end of table?
274 				if (pDevice->chipID == pciInfo.device_id)
275 					return pDevice;		// matching device/chip found
276 
277 				pDevice++;
278 			}
279 		}
280 
281 		pciIndex++;
282 	}
283 
284 	return NULL;		// no supported device found
285 }
286 
287 
288 
289 //	#pragma mark - Kernel Interface
290 
291 
292 status_t
293 init_hardware(void)
294 {
295 	// Return B_OK if a device supported by this driver is found; otherwise,
296 	// return B_ERROR so the driver will be unloaded.
297 
298 	if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK)
299 		return B_ERROR;		// unable to access PCI bus
300 
301 	// Check pci devices for a device supported by this driver.
302 
303 	uint32 pciIndex = 0;
304 	pci_info pciInfo;
305 	const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, pciInfo);
306 
307 	TRACE("init_hardware() - %s\n",
308 		pDevice == NULL ? "no supported devices" : "device supported");
309 
310 	put_module(B_PCI_MODULE_NAME);		// put away the module manager
311 
312 	return (pDevice == NULL ? B_ERROR : B_OK);
313 }
314 
315 
316 status_t
317 init_driver(void)
318 {
319 	// Get handle for the pci bus.
320 
321 	if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK)
322 		return B_ERROR;
323 
324 	status_t status = gLock.Init("3DFX driver lock");
325 	if (status < B_OK)
326 		return status;
327 
328 	// Get info about all the devices supported by this driver.
329 
330 	uint32 pciIndex = 0;
331 	uint32 count = 0;
332 
333 	while (count < MAX_DEVICES) {
334 		DeviceInfo& di = gDeviceInfo[count];
335 
336 		const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, di.pciInfo);
337 		if (pDevice == NULL)
338 			break;			// all supported devices have been obtained
339 
340 		// Compose device name.
341 		sprintf(di.name, "graphics/" DEVICE_FORMAT,
342 				  di.pciInfo.vendor_id, di.pciInfo.device_id,
343 				  di.pciInfo.bus, di.pciInfo.device, di.pciInfo.function);
344 		TRACE("init_driver() match found; name: %s\n", di.name);
345 
346 		gDeviceNames[count] = di.name;
347 		di.openCount = 0;		// mark driver as available for R/W open
348 		di.sharedArea = -1;		// indicate shared area not yet created
349 		di.sharedInfo = NULL;
350 		di.pChipInfo = pDevice;
351 		count++;
352 		pciIndex++;
353 	}
354 
355 	gDeviceNames[count] = NULL;	// terminate list with null pointer
356 
357 	TRACE("init_driver() %ld supported devices\n", count);
358 
359 	return B_OK;
360 }
361 
362 
363 void
364 uninit_driver(void)
365 {
366 	// Free the driver data.
367 
368 	gLock.Delete();
369 	put_module(B_PCI_MODULE_NAME);	// put the pci module away
370 }
371 
372 
373 const char**
374 publish_devices(void)
375 {
376 	return (const char**)gDeviceNames;	// return list of supported devices
377 }
378 
379 
380 device_hooks*
381 find_device(const char* name)
382 {
383 	int i = 0;
384 	while (gDeviceNames[i] != NULL) {
385 		if (strcmp(name, gDeviceNames[i]) == 0)
386 			return &gDeviceHooks;
387 		i++;
388 	}
389 
390 	return NULL;
391 }
392 
393 
394 
395 //	#pragma mark - Device Hooks
396 
397 
398 static status_t
399 device_open(const char* name, uint32 /*flags*/, void** cookie)
400 {
401 	status_t status = B_OK;
402 
403 	TRACE("device_open() - name: %s, cookie: 0x%08lx)\n", name, (uint32)cookie);
404 
405 	// Find the device name in the list of devices.
406 
407 	int32 i = 0;
408 	while (gDeviceNames[i] != NULL && (strcmp(name, gDeviceNames[i]) != 0))
409 		i++;
410 
411 	if (gDeviceNames[i] == NULL)
412 		return B_BAD_VALUE;		// device name not found in list of devices
413 
414 	DeviceInfo& di = gDeviceInfo[i];
415 
416 	gLock.Acquire();	// make sure no one else has write access to common data
417 
418 	if (di.openCount == 0)
419 		status = InitDevice(di);
420 
421 	gLock.Release();
422 
423 	if (status == B_OK) {
424 		di.openCount++;		// mark device open
425 		*cookie = &di;		// send cookie to opener
426 	}
427 
428 	TRACE("device_open() returning 0x%lx,  open count: %ld\n", status,
429 		di.openCount);
430 	return status;
431 }
432 
433 
434 static status_t
435 device_read(void* dev, off_t pos, void* buf, size_t* len)
436 {
437 	// Following 3 lines of code are here to eliminate "unused parameter" warnings.
438 	(void)dev;
439 	(void)pos;
440 	(void)buf;
441 
442 	*len = 0;
443 	return B_NOT_ALLOWED;
444 }
445 
446 
447 static status_t
448 device_write(void* dev, off_t pos, const void* buf, size_t* len)
449 {
450 	// Following 3 lines of code are here to eliminate "unused parameter" warnings.
451 	(void)dev;
452 	(void)pos;
453 	(void)buf;
454 
455 	*len = 0;
456 	return B_NOT_ALLOWED;
457 }
458 
459 
460 static status_t
461 device_close(void* dev)
462 {
463 	(void)dev;		// avoid compiler warning for unused arg
464 
465 	TRACE("device_close()\n");
466 	return B_NO_ERROR;
467 }
468 
469 
470 static status_t
471 device_free(void* dev)
472 {
473 	DeviceInfo& di = *((DeviceInfo*)dev);
474 
475 	TRACE("enter device_free()\n");
476 
477 	gLock.Acquire();		// lock driver
478 
479 	// If opened multiple times, merely decrement the open count and exit.
480 
481 	if (di.openCount <= 1) {
482 		UnmapDevice(di);	// free regs and frame buffer areas
483 
484 		delete_area(di.sharedArea);
485 		di.sharedArea = -1;
486 		di.sharedInfo = NULL;
487 	}
488 
489 	if (di.openCount > 0)
490 		di.openCount--;		// mark device available
491 
492 	gLock.Release();	// unlock driver
493 
494 	TRACE("exit device_free() openCount: %ld\n", di.openCount);
495 	return B_OK;
496 }
497 
498 
499 static status_t
500 device_ioctl(void* dev, uint32 msg, void* buffer, size_t bufferLength)
501 {
502 	DeviceInfo& di = *((DeviceInfo*)dev);
503 
504 #ifndef __HAIKU__
505 	(void)bufferLength;		// avoid compiler warning for unused arg
506 #endif
507 
508 	switch (msg) {
509 		case B_GET_ACCELERANT_SIGNATURE:
510 			strcpy((char*)buffer, ACCELERANT_NAME);
511 			return B_OK;
512 
513 		case TDFX_DEVICE_NAME:
514 			strncpy((char*)buffer, di.name, B_OS_NAME_LENGTH);
515 			((char*)buffer)[B_OS_NAME_LENGTH -1] = '\0';
516 			return B_OK;
517 
518 		case TDFX_GET_SHARED_DATA:
519 #ifdef __HAIKU__
520 			if (bufferLength != sizeof(area_id))
521 				return B_BAD_DATA;
522 #endif
523 
524 			*((area_id*)buffer) = di.sharedArea;
525 			return B_OK;
526 
527 		case TDFX_GET_PIO_REG:
528 		{
529 #ifdef __HAIKU__
530 			if (bufferLength != sizeof(PIORegInfo))
531 				return B_BAD_DATA;
532 #endif
533 
534 			PIORegInfo* regInfo = (PIORegInfo*)buffer;
535 			if (regInfo->magic == TDFX_PRIVATE_DATA_MAGIC) {
536 				int ioAddr = di.pciInfo.u.h0.base_registers[2] + regInfo->offset;
537 				if (regInfo->index >= 0) {
538 					gPCI->write_io_8(ioAddr, regInfo->index);
539 					regInfo->value = gPCI->read_io_8(ioAddr + 1);
540 				} else {
541 					regInfo->value = gPCI->read_io_8(ioAddr);
542 				}
543 				return B_OK;
544 			}
545 			break;
546 		}
547 
548 		case TDFX_SET_PIO_REG:
549 		{
550 #ifdef __HAIKU__
551 			if (bufferLength != sizeof(PIORegInfo))
552 				return B_BAD_DATA;
553 #endif
554 
555 			PIORegInfo* regInfo = (PIORegInfo*)buffer;
556 			if (regInfo->magic == TDFX_PRIVATE_DATA_MAGIC) {
557 				int ioAddr = di.pciInfo.u.h0.base_registers[2] + regInfo->offset;
558 				if (regInfo->index >= 0) {
559 					gPCI->write_io_8(ioAddr, regInfo->index);
560 					gPCI->write_io_8(ioAddr + 1, regInfo->value);
561 				} else {
562 					gPCI->write_io_8(ioAddr, regInfo->value);
563 				}
564 				return B_OK;
565 			}
566 			break;
567 		}
568 	}
569 
570 	return B_DEV_INVALID_IOCTL;
571 }
572