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
39int32 api_version = B_CUR_DRIVER_API_VERSION;	// revision of driver API used
40
41#define VENDOR_ID	0x121A		// 3DFX vendor ID
42
43
44struct 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
54static 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
62struct 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
74static Benaphore		gLock;
75static DeviceInfo		gDeviceInfo[MAX_DEVICES];
76static char*			gDeviceNames[MAX_DEVICES + 1];
77static pci_module_info*	gPCI;
78
79
80// Prototypes for device hook functions.
81
82static status_t device_open(const char* name, uint32 flags, void** cookie);
83static status_t device_close(void* dev);
84static status_t device_free(void* dev);
85static status_t device_read(void* dev, off_t pos, void* buf, size_t* len);
86static status_t device_write(void* dev, off_t pos, const void* buf,
87					size_t* len);
88static status_t device_ioctl(void* dev, uint32 msg, void* buf, size_t len);
89
90static 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
106static inline uint32
107GetPCI(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
114static inline void
115SetPCI(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
122static status_t
123MapDevice(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_MTR_WC,
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
190static void
191UnmapDevice(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
206static status_t
207InitDevice(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
252static const ChipInfo*
253GetNextSupportedDevice(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
292status_t
293init_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
316status_t
317init_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
363void
364uninit_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
373const char**
374publish_devices(void)
375{
376	return (const char**)gDeviceNames;	// return list of supported devices
377}
378
379
380device_hooks*
381find_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
398static status_t
399device_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
434static status_t
435device_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
447static status_t
448device_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
460static status_t
461device_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
470static status_t
471device_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
499static status_t
500device_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