1 /*****************************************************************************\
2 * Tseng Labs ET6000, ET6100 and ET6300 graphics driver for BeOS 5.
3 * Copyright (c) 2003-2004, Evgeniy Vladimirovich Bobkov.
4 \*****************************************************************************/
6 /* standard kernel driver stuff */
7 #include <KernelExport.h>
12 /* this is for the standardized portion of the driver API */
13 /* currently only one operation is defined: B_GET_ACCELERANT_SIGNATURE */
14 #include <graphic_driver.h>
16 /* this is for sprintf() */
19 /* this is for string compares */
22 /* The private interface between the accelerant and the kernel driver. */
23 #include "DriverInterface.h"
29 /*****************************************************************************/
31 #define ddprintf(a) dprintf a
38 /* Tell the kernel what revision of the driver API we support */
39 int32 api_version
= 2;
41 /*****************************************************************************/
42 /* This structure is private to the kernel driver */
44 uint32 isOpen
; /* a count of how many times the devices has been opened */
45 area_id sharedArea
; /* the area shared between the driver and all of the accelerants */
46 ET6000SharedInfo
*si
; /* a pointer to the shared area, for convenience */
48 uint32 interrupt_count
; /* if we're debugging, a count of how many times
49 the interrupt handler has been called for this device */
51 pci_info pcii
; /* a convenience copy of the pci info for this device */
52 char name
[B_OS_NAME_LENGTH
]; /* where we keep the name of the device for publishing and comparing */
54 /*****************************************************************************/
57 uint32 total_interrupts
; /* total number of interrupts seen by our handler */
59 uint32 count
; /* number of devices actually found */
60 benaphore kernel
; /* for serializing opens/closes */
61 char *deviceNames
[MAX_DEVICES
+1]; /* device name pointer storage */
62 ET6000DeviceInfo di
[MAX_DEVICES
]; /* device specific stuff */
64 /*****************************************************************************/
65 static DeviceData
*pd
;
66 /*****************************************************************************/
67 /* prototypes for our private functions */
68 static status_t
et6000OpenHook(const char* name
, uint32 flags
, void** cookie
);
69 static status_t
et6000CloseHook(void* dev
);
70 static status_t
et6000FreeHook(void* dev
);
71 static status_t
et6000ReadHook(void* dev
, off_t pos
, void* buf
, size_t* len
);
72 static status_t
et6000WriteHook(void* dev
, off_t pos
, const void* buf
, size_t* len
);
73 static status_t
et6000ControlHook(void* dev
, uint32 msg
, void *buf
, size_t len
);
74 static status_t
et6000MapDevice(ET6000DeviceInfo
*di
);
75 static void et6000UnmapDevice(ET6000DeviceInfo
*di
);
76 static void et6000ProbeDevices(void);
77 static int32
et6000Interrupt(void *data
);
80 static int et6000dump(int argc
, char **argv
);
82 /*****************************************************************************/
83 static pci_module_info
*pci_bus
;
85 #define get_pci(o, s) (*pci_bus->read_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s))
87 #define set_pci(o, s, v) (*pci_bus->write_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s), (v))
88 /*****************************************************************************/
89 static device_hooks et6000DeviceHooks
= {
101 /*****************************************************************************/
102 #define TSENG_VENDOR_ID 0x100C /* Tseng Labs Inc */
104 static uint16 et6000DeviceList
[] = {
105 0x3208, /* ET6000/ET6100 */
113 } supportedDevices
[] = {
114 {TSENG_VENDOR_ID
, et6000DeviceList
},
117 /*****************************************************************************/
119 * Returns B_OK if one is found, otherwise returns
120 * B_ERROR so the driver will be unloaded.
122 status_t
init_hardware(void) {
125 bool foundOne
= FALSE
;
127 /* choke if we can't find the PCI bus */
128 if (get_module(B_PCI_MODULE_NAME
, (module_info
**)&pci_bus
) != B_OK
)
131 /* while there are more pci devices */
132 while ((*pci_bus
->get_nth_pci_info
)(pciIndex
, &pcii
) == B_NO_ERROR
) {
135 ddprintf(("ET6000 init_hardware(): checking pci index %ld, device 0x%04x/0x%04x\n", pciIndex
, pcii
.vendor_id
, pcii
.device_id
));
136 /* if we match a supported vendor */
137 while (supportedDevices
[vendor
].vendor
) {
138 if (supportedDevices
[vendor
].vendor
== pcii
.vendor_id
) {
139 uint16
*devices
= supportedDevices
[vendor
].devices
;
140 /* while there are more supported devices */
142 /* if we match a supported device */
143 if (*devices
== pcii
.device_id
) {
144 ddprintf(("ET6000: we support this device\n"));
148 /* next supported device */
154 /* next pci_info struct, please */
157 ddprintf(("ET6000: init_hardware - no supported devices\n"));
160 /* put away the module manager */
161 put_module(B_PCI_MODULE_NAME
);
162 return (foundOne
? B_OK
: B_ERROR
);
164 /*****************************************************************************/
165 static void et6000ProbeDevices(void) {
168 ET6000DeviceInfo
*di
= pd
->di
;
170 /* while there are more pci devices */
171 while ((count
< MAX_DEVICES
) &&
172 ((*pci_bus
->get_nth_pci_info
)(pciIndex
, &(di
->pcii
)) == B_NO_ERROR
))
176 ddprintf(("ET6000: checking pci index %ld, device 0x%04x/0x%04x\n", pciIndex
, di
->pcii
.vendor_id
, di
->pcii
.device_id
));
177 /* if we match a supported vendor */
178 while (supportedDevices
[vendor
].vendor
) {
179 if (supportedDevices
[vendor
].vendor
== di
->pcii
.vendor_id
) {
180 uint16
*devices
= supportedDevices
[vendor
].devices
;
181 /* while there are more supported devices */
183 /* if we match a supported device */
184 if (*devices
== di
->pcii
.device_id
) {
185 /* publish the device name */
186 sprintf(di
->name
, "graphics/%04X_%04X_%02X%02X%02X",
187 di
->pcii
.vendor_id
, di
->pcii
.device_id
,
188 di
->pcii
.bus
, di
->pcii
.device
, di
->pcii
.function
);
189 ddprintf(("ET6000: making /dev/%s\n", di
->name
));
190 /* remember the name */
191 pd
->deviceNames
[count
] = di
->name
;
192 /* mark the driver as available for R/W open */
194 /* mark areas as not yet created */
196 /* mark pointer to shared data as invalid */
198 /* inc pointer to device info */
202 /* break out of these while loops */
205 /* next supported device */
212 /* next pci_info struct, please */
215 /* propagate count */
217 /* terminate list of device names with a null pointer */
218 pd
->deviceNames
[pd
->count
] = NULL
;
219 ddprintf(("SKD et6000ProbeDevices: %ld supported devices\n", pd
->count
));
221 /*****************************************************************************/
222 status_t
init_driver(void) {
223 /* get a handle for the pci bus */
224 if (get_module(B_PCI_MODULE_NAME
, (module_info
**)&pci_bus
) != B_OK
)
227 /* driver private data */
228 pd
= (DeviceData
*)calloc(1, sizeof(DeviceData
));
230 put_module(B_PCI_MODULE_NAME
);
233 /* initialize the benaphore */
234 INIT_BEN(pd
->kernel
);
236 /* find all of our supported devices */
237 et6000ProbeDevices();
240 add_debugger_command("et6000dump", et6000dump
, "dump ET6000 kernel driver persistant data");
245 /*****************************************************************************/
246 const char **publish_devices(void) {
247 /* return the list of supported devices */
248 return (const char **)pd
->deviceNames
;
250 /*****************************************************************************/
251 device_hooks
*find_device(const char *name
) {
253 while (pd
->deviceNames
[index
]) {
254 if (strcmp(name
, pd
->deviceNames
[index
]) == 0)
255 return &et6000DeviceHooks
;
260 /*****************************************************************************/
261 void uninit_driver(void) {
264 remove_debugger_command("et6000dump", et6000dump
);
267 /* free the driver data */
268 DELETE_BEN(pd
->kernel
);
272 /* put the pci module away */
273 put_module(B_PCI_MODULE_NAME
);
275 /*****************************************************************************/
276 static int32
et6000Interrupt(void *data
) {
277 int32 handled
= B_UNHANDLED_INTERRUPT
;
278 ET6000DeviceInfo
*di
= (ET6000DeviceInfo
*)data
;
279 ET6000SharedInfo
*si
= di
->si
;
280 int32
*flags
= &(si
->flags
);
283 pd
->total_interrupts
++;
286 /* is someone already handling an interrupt for this device? */
287 if (atomic_or(flags
, ET6000_HANDLER_INSTALLED
) & ET6000_HANDLER_INSTALLED
) {
289 kprintf("ET6000: Already in handler!\n");
294 switch (et6000aclInterruptCause(si
->mmRegs
)) {
295 case ET6000_ACL_INT_CAUSE_NONE
:
296 handled
= B_UNHANDLED_INTERRUPT
;
298 case ET6000_ACL_INT_CAUSE_READ
:
299 et6000aclReadInterruptClear(si
->mmRegs
);
300 handled
= B_HANDLED_INTERRUPT
;
302 case ET6000_ACL_INT_CAUSE_WRITE
:
303 et6000aclWriteInterruptClear(si
->mmRegs
);
304 handled
= B_HANDLED_INTERRUPT
;
306 case ET6000_ACL_INT_CAUSE_BOTH
: /* Can it be at all? */
307 et6000aclReadInterruptClear(si
->mmRegs
);
308 et6000aclWriteInterruptClear(si
->mmRegs
);
309 handled
= B_HANDLED_INTERRUPT
;
314 /* increment the counter for this device */
315 if (handled
== B_HANDLED_INTERRUPT
)
316 di
->interrupt_count
++;
319 /* note that we're not in the handler any more */
320 atomic_and(flags
, ~ET6000_HANDLER_INSTALLED
);
325 /*****************************************************************************/
326 static uint32
et6000GetOnboardMemorySize(uint16 pciConfigSpace
,
327 volatile void *memory
)
331 ioSet8(0x3d8, 0x00, 0xa0); /* Set the KEY for color modes */
332 ioSet8(0x3b8, 0x00, 0xa0); /* Set the KEY for monochrome modes */
334 switch (ioGet8(0x3C2) & 0x03) {
335 case 0x00: /* onboard memory is of DRAM type */
336 memSize
= 1024*1024 * ((ioGet8(pciConfigSpace
+ 0x45) & 0x03) + 1);
338 case 0x03: /* onboard memory is of MDRAM type */
339 memSize
= /* number*8 of 32kb banks per channel */
340 ((ioGet8(pciConfigSpace
+ 0x47) & 0x07) + 1) * 8 * 32*1024;
341 if (ioGet8(pciConfigSpace
+ 0x45) & 0x04) /* If 2 channels */
344 default: /* onboard memory is of unknown type */
345 memSize
= 4196*1024; /* Let it be of maximum possible size */
349 * This algorithm would fail to recongize 2.25Mb of onboard
350 * memory - it would detect 2.5Mb instead. It needs to be fixed.
352 if (memSize
== 2621440) { /* If 2.5Mb detected */
353 uint8 pci40
= ioGet8(pciConfigSpace
+0x40);
354 et6000EnableLinearMemoryMapping(pciConfigSpace
);
356 /* Check whether the memory beyond 2.25Mb really exists */
357 *(volatile uint32
*)((uint32
)memory
+ 2359296) = 0xaa55aa55;
358 if (*(volatile uint32
*)((uint32
)memory
+ 2359296) != 0xaa55aa55)
359 memSize
= 2359296; /* It's 2.25Mb */
361 ioSet8(pciConfigSpace
+0x40, 0x00, pci40
); /* Restore */
366 /*****************************************************************************/
367 static status_t
et6000MapDevice(ET6000DeviceInfo
*di
) {
368 char buffer
[B_OS_NAME_LENGTH
];
369 ET6000SharedInfo
*si
= di
->si
;
371 pci_info
*pcii
= &(di
->pcii
);
373 /* Enable memory space access and I/O space access */
374 tmpUlong
= get_pci(PCI_command
, 4);
375 tmpUlong
|= 0x00000003;
376 set_pci(PCI_command
, 4, tmpUlong
);
378 /* Enable ROM decoding */
379 tmpUlong
= get_pci(PCI_rom_base
, 4);
380 tmpUlong
|= 0x00000001;
381 set_pci(PCI_rom_base
, 4, tmpUlong
);
383 /* PCI header base address in I/O space */
384 si
->pciConfigSpace
= (uint16
)di
->pcii
.u
.h0
.base_registers
[1];
386 sprintf(buffer
, "%04X_%04X_%02X%02X%02X videomemory",
387 di
->pcii
.vendor_id
, di
->pcii
.device_id
,
388 di
->pcii
.bus
, di
->pcii
.device
, di
->pcii
.function
);
391 * We map the whole graphics card memory area (which consist of RAM memory
392 * and memory mapped registers) at once. Memory mapped registers must not
393 * be cacheble, so the whole area is mapped with B_MTR_UC (unable caching).
394 * We certainly could map separately the RAM memory with write combining
395 * (B_MTR_WC) and the memory mapped registers with B_MTR_UC.
397 si
->memoryArea
= map_physical_memory(buffer
,
398 di
->pcii
.u
.h0
.base_registers
[0],
399 di
->pcii
.u
.h0
.base_register_sizes
[0],
400 B_ANY_KERNEL_BLOCK_ADDRESS
| B_MTR_UC
,
401 B_READ_AREA
+ B_WRITE_AREA
,
404 si
->framebuffer
= si
->memory
;
405 si
->mmRegs
= (void *)((uint32
)si
->memory
+ 0x003fff00);
406 si
->emRegs
= (void *)((uint32
)si
->memory
+ 0x003fe000);
408 /* remember the physical addresses */
409 si
->physMemory
= si
->physFramebuffer
=
410 (void *) di
->pcii
.u
.h0
.base_registers_pci
[0];
412 si
->memSize
= et6000GetOnboardMemorySize(si
->pciConfigSpace
, si
->memory
);
414 /* in any case, return the result */
415 return si
->memoryArea
;
417 /*****************************************************************************/
418 static void et6000UnmapDevice(ET6000DeviceInfo
*di
) {
419 ET6000SharedInfo
*si
= di
->si
;
421 ddprintf(("et6000UnmapDevice(%08lx) begins...\n", (uint32
)di
));
422 ddprintf((" memoryArea: %ld\n", si
->memoryArea
));
424 if (si
->memoryArea
>= 0)
425 delete_area(si
->memoryArea
);
427 si
->framebuffer
= NULL
;
428 si
->physFramebuffer
= NULL
;
430 si
->physMemory
= NULL
;
432 ddprintf(("et6000UnmapDevice() ends.\n"));
434 /*****************************************************************************/
435 static status_t
et6000OpenHook(const char* name
, uint32 flags
, void** cookie
) {
437 ET6000DeviceInfo
*di
;
438 ET6000SharedInfo
*si
;
439 status_t result
= B_OK
;
440 char shared_name
[B_OS_NAME_LENGTH
];
442 ddprintf(("SKD et6000OpenHook(%s, %ld, 0x%08lx)\n", name
, flags
, (uint32
)cookie
));
444 /* find the device name in the list of devices */
445 /* we're never passed a name we didn't publish */
446 while(pd
->deviceNames
[index
] &&
447 (strcmp(name
, pd
->deviceNames
[index
]) != 0))
452 /* for convienience */
453 di
= &(pd
->di
[index
]);
455 /* make sure no one else has write access to the common data */
456 AQUIRE_BEN(pd
->kernel
);
458 /* if it's already open for writing */
460 /* mark it open another time */
463 /* create the shared area */
464 sprintf(shared_name
, "%04X_%04X_%02X%02X%02X shared",
465 di
->pcii
.vendor_id
, di
->pcii
.device_id
,
466 di
->pcii
.bus
, di
->pcii
.device
, di
->pcii
.function
);
467 /* create this area with NO user-space read or write permissions, to prevent accidental dammage */
468 di
->sharedArea
= create_area(shared_name
, (void **)&(di
->si
), B_ANY_KERNEL_ADDRESS
, ((sizeof(ET6000SharedInfo
) + (B_PAGE_SIZE
- 1)) & ~(B_PAGE_SIZE
- 1)), B_FULL_LOCK
, 0);
469 if (di
->sharedArea
< 0) {
470 /* return the error */
471 result
= di
->sharedArea
;
475 /* save a few dereferences */
478 /* save the vendor and device IDs */
479 si
->vendor_id
= di
->pcii
.vendor_id
;
480 si
->device_id
= di
->pcii
.device_id
;
481 si
->revision
= di
->pcii
.revision
;
483 si
->pixelClockMax16
= 135000;
484 si
->pixelClockMax24
= 135000;
485 if (si
->vendor_id
== 0x100C) { /* Tseng Labs, Inc. */
486 switch (si
->device_id
) {
487 case 0x3208:/* ET6000/ET6100 */
488 if (si
->revision
< 0x70) { /* ET6000 */
489 si
->pixelClockMax16
= 135000;
490 si
->pixelClockMax24
= 135000;
493 si
->pixelClockMax16
= 175000;
494 si
->pixelClockMax24
= 175000;
497 case 0x4702: /* ET6300 */
498 si
->pixelClockMax16
= 220000;
499 si
->pixelClockMax24
= 220000;
505 result
= et6000MapDevice(di
);
511 * Clear any pending interrupts and disable interrupts. Driver
512 * currently does not use interrupts and unlikely will in future.
514 et6000aclReadInterruptClear(si
->mmRegs
);
515 et6000aclWriteInterruptClear(si
->mmRegs
);
516 et6000aclMasterInterruptDisable(si
->mmRegs
);
518 /* Install the interrupt handler */
519 result
= install_io_interrupt_handler(di
->pcii
.u
.h0
.interrupt_line
,
520 et6000Interrupt
, (void *)di
, 0);
521 /* bail if we couldn't install the handler */
526 /* mark the device open */
529 /* send the cookie to the opener */
535 et6000UnmapDevice(di
);
538 /* clean up our shared area */
539 delete_area(di
->sharedArea
);
544 /* end of critical section */
545 RELEASE_BEN(pd
->kernel
);
547 /* all done, return the status */
548 ddprintf(("et6000OpenHook returning 0x%08lx\n", result
));
552 /*****************************************************************************/
554 * et6000ReadHook - does nothing, gracefully
556 static status_t
et6000ReadHook(void* dev
, off_t pos
, void* buf
, size_t* len
)
559 return B_NOT_ALLOWED
;
562 /*****************************************************************************/
564 * et6000WriteHook - does nothing, gracefully
566 static status_t
et6000WriteHook(void* dev
, off_t pos
, const void* buf
, size_t* len
)
569 return B_NOT_ALLOWED
;
571 /*****************************************************************************/
573 * et6000CloseHook - does nothing, gracefully
575 static status_t
et6000CloseHook(void* dev
)
577 ddprintf(("SKD et6000CloseHook(%08lx)\n", (uint32
)dev
));
578 /* we don't do anything on close: there might be dup'd fd */
581 /*****************************************************************************/
583 * et6000FreeHook - close down the device
585 static status_t
et6000FreeHook(void* dev
) {
586 ET6000DeviceInfo
*di
= (ET6000DeviceInfo
*)dev
;
587 ET6000SharedInfo
*si
= di
->si
;
589 ddprintf(("SKD et6000FreeHook() begins...\n"));
590 /* lock the driver */
591 AQUIRE_BEN(pd
->kernel
);
593 /* if opened multiple times, decrement the open count and exit */
595 goto unlock_and_exit
;
597 /* Clear any pending interrupts and disable interrupts. */
598 et6000aclReadInterruptClear(si
->mmRegs
);
599 et6000aclWriteInterruptClear(si
->mmRegs
);
600 et6000aclMasterInterruptDisable(si
->mmRegs
);
602 /* Remove the interrupt handler */
603 remove_io_interrupt_handler(di
->pcii
.u
.h0
.interrupt_line
, et6000Interrupt
, di
);
605 /* free framebuffer area */
606 et6000UnmapDevice(di
);
608 /* clean up our shared area */
609 delete_area(di
->sharedArea
);
614 /* mark the device available */
616 /* unlock the driver */
617 RELEASE_BEN(pd
->kernel
);
618 ddprintf(("SKD et6000FreeHook() ends.\n"));
622 /*****************************************************************************/
624 * et6000ControlHook - where the real work is done
626 static status_t
et6000ControlHook(void* dev
, uint32 msg
, void *buf
, size_t len
) {
627 ET6000DeviceInfo
*di
= (ET6000DeviceInfo
*)dev
;
628 status_t result
= B_DEV_INVALID_IOCTL
;
630 /* ddprintf(("ioctl: %d, buf: 0x%08x, len: %d\n", msg, buf, len)); */
632 /* the only PUBLIC ioctl */
633 case B_GET_ACCELERANT_SIGNATURE
: {
634 char *sig
= (char *)buf
;
635 strcpy(sig
, "et6000.accelerant");
639 /* PRIVATE ioctl from here on */
640 case ET6000_GET_PRIVATE_DATA
: {
641 ET6000GetPrivateData
*gpd
= (ET6000GetPrivateData
*)buf
;
642 if (gpd
->magic
== ET6000_PRIVATE_DATA_MAGIC
) {
643 gpd
->sharedInfoArea
= di
->sharedArea
;
648 case ET6000_GET_PCI
: {
649 ET6000GetSetPCI
*gsp
= (ET6000GetSetPCI
*)buf
;
650 if (gsp
->magic
== ET6000_PRIVATE_DATA_MAGIC
) {
651 pci_info
*pcii
= &(di
->pcii
);
652 gsp
->value
= get_pci(gsp
->offset
, gsp
->size
);
657 case ET6000_SET_PCI
: {
658 ET6000GetSetPCI
*gsp
= (ET6000GetSetPCI
*)buf
;
659 if (gsp
->magic
== ET6000_PRIVATE_DATA_MAGIC
) {
660 pci_info
*pcii
= &(di
->pcii
);
661 set_pci(gsp
->offset
, gsp
->size
, gsp
->value
);
666 case ET6000_DEVICE_NAME
: { /* Needed for cloning */
667 ET6000DeviceName
*dn
= (ET6000DeviceName
*)buf
;
668 if(dn
->magic
== ET6000_PRIVATE_DATA_MAGIC
) {
669 strncpy(dn
->name
, di
->name
, B_OS_NAME_LENGTH
);
674 case ET6000_PROPOSE_DISPLAY_MODE
: {
675 ET6000DisplayMode
*dm
= (ET6000DisplayMode
*)buf
;
676 if(dm
->magic
== ET6000_PRIVATE_DATA_MAGIC
) {
677 result
= et6000ProposeMode(&dm
->mode
, dm
->memSize
);
681 case ET6000_SET_DISPLAY_MODE
: {
682 ET6000DisplayMode
*dm
= (ET6000DisplayMode
*)buf
;
683 if(dm
->magic
== ET6000_PRIVATE_DATA_MAGIC
) {
684 result
= et6000SetMode(&dm
->mode
, dm
->pciConfigSpace
);
690 /*****************************************************************************/
692 static int et6000dump(int argc
, char **argv
) {
695 kprintf("ET6000 Kernel Driver Persistant Data\n\nThere are %ld card(s)\n", pd
->count
);
696 kprintf("Driver wide benahpore: %ld/%ld\n", pd
->kernel
.ben
, pd
->kernel
.sem
);
698 kprintf("Total seen interrupts: %ld\n", pd
->total_interrupts
);
699 for (i
= 0; i
< pd
->count
; i
++) {
700 ET6000DeviceInfo
*di
= &(pd
->di
[i
]);
701 uint16 device_id
= di
->pcii
.device_id
;
702 ET6000SharedInfo
*si
= di
->si
;
703 kprintf(" device_id: 0x%04x\n", device_id
);
704 kprintf(" interrupt count: %ld\n", di
->interrupt_count
);
710 return 1; /* the magic number for success */
713 /*****************************************************************************/