2 * Copyright 2007-2012 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT license.
11 #include <KernelExport.h>
13 #include <drivers/bios.h>
17 #include <graphic_driver.h>
18 #include <boot_item.h>
20 #include "DriverInterface.h"
25 #ifdef ENABLE_DEBUG_TRACE
26 # define TRACE(x...) dprintf("i810: " x)
28 # define TRACE(x...) ;
32 #define ACCELERANT_NAME "intel_810.accelerant"
34 #define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
37 #define DEVICE_FORMAT "%04X_%04X_%02X%02X%02X"
39 #define VENDOR_ID 0x8086 // Intel vendor ID
43 uint16 chipID
; // PCI device id of the chip
44 const char* chipName
; // user recognizable name (must be < 32 chars)
48 // This table maps a PCI device ID to a chip type identifier and the chip name.
50 static const ChipInfo chipTable
[] = {
52 { 0x7123, "i810-dc100" },
60 uint32 openCount
; // how many times device has been opened
62 area_id sharedArea
; // shared between driver and accelerants
63 SharedInfo
* sharedInfo
; // pointer to shared info area memory
64 vuint8
* regs
; // pointer to memory mapped registers
65 const ChipInfo
* pChipInfo
; // info about the selected chip
66 pci_info pciInfo
; // copy of pci info for this device
67 area_id gttArea
; // area used for GTT
68 addr_t gttAddr
; // virtual address of GTT
69 char name
[B_OS_NAME_LENGTH
]; // name of device
73 static Benaphore gLock
;
74 static DeviceInfo gDeviceInfo
[MAX_DEVICES
];
75 static char* gDeviceNames
[MAX_DEVICES
+ 1];
76 static pci_module_info
* gPCI
;
79 // Prototypes for device hook functions.
80 static status_t
device_open(const char* name
, uint32 flags
, void** cookie
);
81 static status_t
device_close(void* dev
);
82 static status_t
device_free(void* dev
);
83 static status_t
device_read(void* dev
, off_t pos
, void* buf
, size_t* len
);
84 static status_t
device_write(void* dev
, off_t pos
, const void* buf
,
86 static status_t
device_ioctl(void* dev
, uint32 msg
, void* buf
, size_t len
);
88 static device_hooks gDeviceHooks
=
103 // Video chip register definitions.
104 // =================================
106 #define INTERRUPT_ENABLED 0x020a0
107 #define INTERRUPT_MASK 0x020a8
109 // Graphics address translation table.
110 #define PAGE_TABLE_CONTROL 0x02020
111 #define PAGE_TABLE_ENABLED 0x01
113 #define PTE_BASE 0x10000
114 #define PTE_VALID 0x01
117 // Macros for memory mapped I/O.
118 // ==============================
120 #define INREG16(addr) (*((vuint16*)(di.regs + (addr))))
121 #define INREG32(addr) (*((vuint32*)(di.regs + (addr))))
123 #define OUTREG16(addr, val) (*((vuint16*)(di.regs + (addr))) = (val))
124 #define OUTREG32(addr, val) (*((vuint32*)(di.regs + (addr))) = (val))
128 GetPCI(pci_info
& info
, uint8 offset
, uint8 size
)
130 return gPCI
->read_pci_config(info
.bus
, info
.device
, info
.function
, offset
,
136 SetPCI(pci_info
& info
, uint8 offset
, uint8 size
, uint32 value
)
138 gPCI
->write_pci_config(info
.bus
, info
.device
, info
.function
, offset
, size
,
144 GetEdidFromBIOS(edid1_raw
& edidRaw
)
146 // Get the EDID info from the video BIOS, and return B_OK if successful.
148 #define ADDRESS_SEGMENT(address) ((addr_t)(address) >> 4)
149 #define ADDRESS_OFFSET(address) ((addr_t)(address) & 0xf)
151 bios_module_info
* biosModule
;
152 status_t status
= get_module(B_BIOS_MODULE_NAME
, (module_info
**)&biosModule
);
153 if (status
!= B_OK
) {
154 TRACE("GetEdidFromBIOS(): failed to get BIOS module: 0x%" B_PRIx32
"\n",
160 status
= biosModule
->prepare(&state
);
161 if (status
!= B_OK
) {
162 TRACE("GetEdidFromBIOS(): bios_prepare() failed: 0x%" B_PRIx32
"\n",
164 put_module(B_BIOS_MODULE_NAME
);
170 regs
.ebx
= 0; // 0 = report DDC service
175 status
= biosModule
->interrupt(state
, 0x10, ®s
);
176 if (status
== B_OK
) {
177 // AH contains the error code, and AL determines whether or not the
178 // function is supported.
179 if (regs
.eax
!= 0x4f)
180 status
= B_NOT_SUPPORTED
;
182 // Test if DDC is supported by the monitor.
183 if ((regs
.ebx
& 3) == 0)
184 status
= B_NOT_SUPPORTED
;
187 if (status
== B_OK
) {
188 edid1_raw
* edid
= (edid1_raw
*)biosModule
->allocate_mem(state
,
191 status
= B_NO_MEMORY
;
196 regs
.ebx
= 1; // 1 = read EDID
199 regs
.es
= ADDRESS_SEGMENT(edid
);
200 regs
.edi
= ADDRESS_OFFSET(edid
);
202 status
= biosModule
->interrupt(state
, 0x10, ®s
);
203 if (status
== B_OK
) {
204 if (regs
.eax
!= 0x4f) {
205 status
= B_NOT_SUPPORTED
;
207 // Copy the EDID info to the caller's location, and compute the
208 // checksum of the EDID info while copying.
212 uint8
* dest
= (uint8
*)&edidRaw
;
213 uint8
* src
= (uint8
*)edid
;
215 for (uint32 j
= 0; j
< sizeof(edidRaw
); j
++) {
222 TRACE("GetEdidFromBIOS(); EDID info contains only zeros\n");
224 } else if (sum
!= 0) {
225 TRACE("GetEdidFromBIOS(); Checksum error in EDID info\n");
233 biosModule
->finish(state
);
234 put_module(B_BIOS_MODULE_NAME
);
236 TRACE("GetEdidFromBIOS() status: 0x%" B_PRIx32
"\n", status
);
242 InitDevice(DeviceInfo
& di
)
244 // Perform initialization and mapping of the device, and return B_OK if
245 // sucessful; else, return error code.
247 TRACE("enter InitDevice()\n");
249 // Create the area for shared info with NO user-space read or write
250 // permissions, to prevent accidental damage.
252 size_t sharedSize
= (sizeof(SharedInfo
) + 7) & ~7;
254 di
.sharedArea
= create_area("i810 shared info",
255 (void**) &(di
.sharedInfo
),
256 B_ANY_KERNEL_ADDRESS
,
257 ROUND_TO_PAGE_SIZE(sharedSize
),
259 if (di
.sharedArea
< 0)
260 return di
.sharedArea
; // return error code
262 SharedInfo
& si
= *(di
.sharedInfo
);
263 memset(&si
, 0, sharedSize
);
264 si
.regsArea
= -1; // indicate area has not yet been created
265 si
.videoMemArea
= -1;
267 pci_info
& pciInfo
= di
.pciInfo
;
269 si
.vendorID
= pciInfo
.vendor_id
;
270 si
.deviceID
= pciInfo
.device_id
;
271 si
.revision
= pciInfo
.revision
;
272 strcpy(si
.chipName
, di
.pChipInfo
->chipName
);
274 // Enable memory mapped IO and bus master.
276 SetPCI(pciInfo
, PCI_command
, 2, GetPCI(pciInfo
, PCI_command
, 2)
277 | PCI_command_io
| PCI_command_memory
| PCI_command_master
);
279 // Map the MMIO register area.
281 phys_addr_t regsBase
= pciInfo
.u
.h0
.base_registers
[1];
282 uint32 regAreaSize
= pciInfo
.u
.h0
.base_register_sizes
[1];
284 si
.regsArea
= map_physical_memory("i810 mmio registers",
287 B_ANY_KERNEL_ADDRESS
,
288 B_KERNEL_READ_AREA
| B_KERNEL_WRITE_AREA
,
291 if (si
.regsArea
< 0) {
292 TRACE("Unable to map MMIO, error: 0x%lx\n", si
.regsArea
);
296 // Allocate memory for the GTT which must be 64K for the 810/815 chips.
298 uint32 gttSize
= 64 * 1024;
299 di
.gttArea
= create_area("GTT memory", (void**) &(di
.gttAddr
),
300 B_ANY_KERNEL_ADDRESS
, gttSize
, B_FULL_LOCK
| B_CONTIGUOUS
,
301 B_READ_AREA
| B_WRITE_AREA
);
303 if (di
.gttArea
< B_OK
) {
304 TRACE("Unable to create GTT, error: 0x%lx\n", di
.gttArea
);
308 memset((void*)(di
.gttAddr
), 0, gttSize
);
310 // Get the physical address of the GTT, and set GTT address in the chip.
312 physical_entry entry
;
313 status_t status
= get_memory_map((void *)(di
.gttAddr
),
314 B_PAGE_SIZE
, &entry
, 1);
316 TRACE("Unable to get physical address of GTT, error: 0x%lx\n", status
);
320 OUTREG32(PAGE_TABLE_CONTROL
, entry
.address
| PAGE_TABLE_ENABLED
);
321 INREG32(PAGE_TABLE_CONTROL
);
323 // Allocate video memory to be used for the frame buffer.
325 si
.videoMemSize
= 4 * 1024 * 1024;
326 si
.videoMemArea
= create_area("video memory", (void**)&(si
.videoMemAddr
),
327 B_ANY_ADDRESS
, si
.videoMemSize
, B_FULL_LOCK
,
328 B_READ_AREA
| B_WRITE_AREA
);
329 if (si
.videoMemArea
< B_OK
) {
330 TRACE("Unable to create video memory, error: 0x%lx\n", si
.videoMemArea
);
334 // Get the physical address of each page of the video memory, and put
335 // the physical address of each page into the GTT table.
337 for (uint32 offset
= 0; offset
< si
.videoMemSize
; offset
+= B_PAGE_SIZE
) {
338 status
= get_memory_map((void *)(si
.videoMemAddr
+ offset
),
339 B_PAGE_SIZE
, &entry
, 1);
341 TRACE("Unable to get physical address of video memory page, error:"
342 " 0x%lx offset: %ld\n", status
, offset
);
347 si
.videoMemPCI
= entry
.address
;
349 OUTREG32(PTE_BASE
+ ((offset
/ B_PAGE_SIZE
) * 4),
350 entry
.address
| PTE_VALID
);
353 TRACE("InitDevice() exit OK\n");
359 DeleteAreas(DeviceInfo
& di
)
361 // Delete all areas that were created.
363 if (di
.sharedArea
>= 0 && di
.sharedInfo
!= NULL
) {
364 SharedInfo
& si
= *(di
.sharedInfo
);
365 if (si
.regsArea
>= 0)
366 delete_area(si
.regsArea
);
367 if (si
.videoMemArea
>= 0)
368 delete_area(si
.videoMemArea
);
372 delete_area(di
.gttArea
);
374 di
.gttAddr
= (addr_t
)NULL
;
376 if (di
.sharedArea
>= 0)
377 delete_area(di
.sharedArea
);
379 di
.sharedInfo
= NULL
;
383 static const ChipInfo
*
384 GetNextSupportedDevice(uint32
& pciIndex
, pci_info
& pciInfo
)
386 // Search the PCI devices for a device that is supported by this driver.
387 // The search starts at the device specified by argument pciIndex, and
388 // continues until a supported device is found or there are no more devices
389 // to examine. Argument pciIndex is incremented after each device is
392 // If a supported device is found, return a pointer to the struct containing
393 // the chip info; else return NULL.
395 while (gPCI
->get_nth_pci_info(pciIndex
, &pciInfo
) == B_OK
) {
397 if (pciInfo
.vendor_id
== VENDOR_ID
) {
399 // Search the table of supported devices to find a chip/device that
400 // matches device ID of the current PCI device.
402 const ChipInfo
* pDevice
= chipTable
;
404 while (pDevice
->chipID
!= 0) { // end of table?
405 if (pDevice
->chipID
== pciInfo
.device_id
)
406 return pDevice
; // matching device/chip found
415 return NULL
; // no supported device found
419 // #pragma mark - Kernel Interface
425 // Return B_OK if a device supported by this driver is found; otherwise,
426 // return B_ERROR so the driver will be unloaded.
428 status_t status
= get_module(B_PCI_MODULE_NAME
, (module_info
**)&gPCI
);
429 if (status
!= B_OK
) {
430 TRACE("PCI module unavailable, error 0x%lx\n", status
);
434 // Check pci devices for a device supported by this driver.
438 const ChipInfo
* pDevice
= GetNextSupportedDevice(pciIndex
, pciInfo
);
440 TRACE("init_hardware() - %s\n",
441 pDevice
== NULL
? "no supported devices" : "device supported");
443 put_module(B_PCI_MODULE_NAME
); // put away the module manager
445 return (pDevice
== NULL
? B_ERROR
: B_OK
);
452 // Get handle for the pci bus.
454 status_t status
= get_module(B_PCI_MODULE_NAME
, (module_info
**)&gPCI
);
455 if (status
!= B_OK
) {
456 TRACE("PCI module unavailable, error 0x%lx\n", status
);
460 status
= gLock
.Init("i810 driver lock");
462 put_module(B_AGP_GART_MODULE_NAME
);
463 put_module(B_PCI_MODULE_NAME
);
467 // Get info about all the devices supported by this driver.
472 while (count
< MAX_DEVICES
) {
473 DeviceInfo
& di
= gDeviceInfo
[count
];
475 const ChipInfo
* pDevice
= GetNextSupportedDevice(pciIndex
, di
.pciInfo
);
477 break; // all supported devices have been obtained
479 // Compose device name.
480 sprintf(di
.name
, "graphics/" DEVICE_FORMAT
,
481 di
.pciInfo
.vendor_id
, di
.pciInfo
.device_id
,
482 di
.pciInfo
.bus
, di
.pciInfo
.device
, di
.pciInfo
.function
);
483 TRACE("init_driver() match found; name: %s\n", di
.name
);
485 gDeviceNames
[count
] = di
.name
;
486 di
.openCount
= 0; // mark driver as available for R/W open
487 di
.sharedArea
= -1; // indicate shared area not yet created
488 di
.sharedInfo
= NULL
;
489 di
.gttArea
= -1; // indicate GTT area not yet created
490 di
.gttAddr
= (addr_t
)NULL
;
491 di
.pChipInfo
= pDevice
;
496 gDeviceNames
[count
] = NULL
; // terminate list with null pointer
498 TRACE("init_driver() %ld supported devices\n", count
);
507 // Free the driver data.
510 put_module(B_AGP_GART_MODULE_NAME
);
511 put_module(B_PCI_MODULE_NAME
); // put the pci module away
516 publish_devices(void)
518 return (const char**)gDeviceNames
; // return list of supported devices
523 find_device(const char* name
)
526 while (gDeviceNames
[i
] != NULL
) {
527 if (strcmp(name
, gDeviceNames
[i
]) == 0)
528 return &gDeviceHooks
;
536 // #pragma mark - Device Hooks
540 device_open(const char* name
, uint32
/*flags*/, void** cookie
)
542 status_t status
= B_OK
;
544 TRACE("device_open() - name: %s, cookie: 0x%" B_PRIXADDR
"\n", name
,
547 // Find the device name in the list of devices.
550 while (gDeviceNames
[i
] != NULL
&& (strcmp(name
, gDeviceNames
[i
]) != 0))
553 if (gDeviceNames
[i
] == NULL
)
554 return B_BAD_VALUE
; // device name not found in list of devices
556 DeviceInfo
& di
= gDeviceInfo
[i
];
558 gLock
.Acquire(); // make sure no one else has write access to common data
560 if (di
.openCount
== 0) {
561 status
= InitDevice(di
);
563 DeleteAreas(di
); // error occurred; delete any areas created
568 if (status
== B_OK
) {
569 di
.openCount
++; // mark device open
570 *cookie
= &di
; // send cookie to opener
573 TRACE("device_open() returning 0x%lx, open count: %ld\n", status
,
580 device_read(void* dev
, off_t pos
, void* buf
, size_t* len
)
582 // Following 3 lines of code are here to eliminate "unused parameter"
589 return B_NOT_ALLOWED
;
594 device_write(void* dev
, off_t pos
, const void* buf
, size_t* len
)
596 // Following 3 lines of code are here to eliminate "unused parameter"
603 return B_NOT_ALLOWED
;
608 device_close(void* dev
)
610 (void)dev
; // avoid compiler warning for unused arg
612 TRACE("device_close()\n");
618 device_free(void* dev
)
620 DeviceInfo
& di
= *((DeviceInfo
*)dev
);
622 TRACE("enter device_free()\n");
624 gLock
.Acquire(); // lock driver
626 // If opened multiple times, merely decrement the open count and exit.
628 if (di
.openCount
<= 1)
631 if (di
.openCount
> 0)
632 di
.openCount
--; // mark device available
634 gLock
.Release(); // unlock driver
636 TRACE("exit device_free() openCount: %ld\n", di
.openCount
);
642 device_ioctl(void* dev
, uint32 msg
, void* buffer
, size_t bufferLength
)
644 DeviceInfo
& di
= *((DeviceInfo
*)dev
);
646 TRACE("device_ioctl(); ioctl: %lu, buffer: 0x%" B_PRIXADDR
", "
647 "bufLen: %lu\n", msg
, (addr_t
)buffer
, bufferLength
);
650 case B_GET_ACCELERANT_SIGNATURE
:
651 strcpy((char*)buffer
, ACCELERANT_NAME
);
652 TRACE("Intel 810 accelerant: %s\n", ACCELERANT_NAME
);
655 case INTEL_DEVICE_NAME
:
656 strncpy((char*)buffer
, di
.name
, B_OS_NAME_LENGTH
);
657 ((char*)buffer
)[B_OS_NAME_LENGTH
-1] = '\0';
660 case INTEL_GET_SHARED_DATA
:
661 if (bufferLength
!= sizeof(area_id
))
664 *((area_id
*)buffer
) = di
.sharedArea
;
669 if (bufferLength
!= sizeof(edid1_raw
))
673 status_t status
= GetEdidFromBIOS(rawEdid
);
675 user_memcpy((edid1_raw
*)buffer
, &rawEdid
, sizeof(rawEdid
));
680 return B_DEV_INVALID_IOCTL
;