usb_ecm: Use the current configuration instead of a fixed one.
[haiku.git] / src / add-ons / kernel / drivers / graphics / intel_810 / driver.cpp
blobfc71602661845ee1f3bc57d3e79d0fa7a9660268
1 /*
2 * Copyright 2007-2012 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT license.
5 * Authors:
6 * Gerald Zajac
7 */
10 #include <AGP.h>
11 #include <KernelExport.h>
12 #include <PCI.h>
13 #include <drivers/bios.h>
14 #include <malloc.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <graphic_driver.h>
18 #include <boot_item.h>
20 #include "DriverInterface.h"
23 #undef TRACE
25 #ifdef ENABLE_DEBUG_TRACE
26 # define TRACE(x...) dprintf("i810: " x)
27 #else
28 # define TRACE(x...) ;
29 #endif
32 #define ACCELERANT_NAME "intel_810.accelerant"
34 #define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
36 #define MAX_DEVICES 4
37 #define DEVICE_FORMAT "%04X_%04X_%02X%02X%02X"
39 #define VENDOR_ID 0x8086 // Intel vendor ID
42 struct ChipInfo {
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[] = {
51 { 0x7121, "i810" },
52 { 0x7123, "i810-dc100" },
53 { 0x7125, "i810e" },
54 { 0x1132, "i815" },
55 { 0, NULL }
59 struct DeviceInfo {
60 uint32 openCount; // how many times device has been opened
61 int32 flags;
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,
85 size_t* len);
86 static status_t device_ioctl(void* dev, uint32 msg, void* buf, size_t len);
88 static device_hooks gDeviceHooks =
90 device_open,
91 device_close,
92 device_free,
93 device_ioctl,
94 device_read,
95 device_write,
96 NULL,
97 NULL,
98 NULL,
99 NULL
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))
127 static inline uint32
128 GetPCI(pci_info& info, uint8 offset, uint8 size)
130 return gPCI->read_pci_config(info.bus, info.device, info.function, offset,
131 size);
135 static inline void
136 SetPCI(pci_info& info, uint8 offset, uint8 size, uint32 value)
138 gPCI->write_pci_config(info.bus, info.device, info.function, offset, size,
139 value);
143 static status_t
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",
155 status);
156 return status;
159 bios_state* state;
160 status = biosModule->prepare(&state);
161 if (status != B_OK) {
162 TRACE("GetEdidFromBIOS(): bios_prepare() failed: 0x%" B_PRIx32 "\n",
163 status);
164 put_module(B_BIOS_MODULE_NAME);
165 return status;
168 bios_regs regs = {};
169 regs.eax = 0x4f15;
170 regs.ebx = 0; // 0 = report DDC service
171 regs.ecx = 0;
172 regs.es = 0;
173 regs.edi = 0;
175 status = biosModule->interrupt(state, 0x10, &regs);
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,
189 sizeof(edid1_raw));
190 if (edid == NULL) {
191 status = B_NO_MEMORY;
192 goto out;
195 regs.eax = 0x4f15;
196 regs.ebx = 1; // 1 = read EDID
197 regs.ecx = 0;
198 regs.edx = 0;
199 regs.es = ADDRESS_SEGMENT(edid);
200 regs.edi = ADDRESS_OFFSET(edid);
202 status = biosModule->interrupt(state, 0x10, &regs);
203 if (status == B_OK) {
204 if (regs.eax != 0x4f) {
205 status = B_NOT_SUPPORTED;
206 } else {
207 // Copy the EDID info to the caller's location, and compute the
208 // checksum of the EDID info while copying.
210 uint8 sum = 0;
211 uint8 allOr = 0;
212 uint8* dest = (uint8*)&edidRaw;
213 uint8* src = (uint8*)edid;
215 for (uint32 j = 0; j < sizeof(edidRaw); j++) {
216 sum += *src;
217 allOr |= *src;
218 *dest++ = *src++;
221 if (allOr == 0) {
222 TRACE("GetEdidFromBIOS(); EDID info contains only zeros\n");
223 status = B_ERROR;
224 } else if (sum != 0) {
225 TRACE("GetEdidFromBIOS(); Checksum error in EDID info\n");
226 status = B_ERROR;
232 out:
233 biosModule->finish(state);
234 put_module(B_BIOS_MODULE_NAME);
236 TRACE("GetEdidFromBIOS() status: 0x%" B_PRIx32 "\n", status);
237 return status;
241 static status_t
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),
258 B_FULL_LOCK, 0);
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",
285 regsBase,
286 regAreaSize,
287 B_ANY_KERNEL_ADDRESS,
288 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
289 (void**)&di.regs);
291 if (si.regsArea < 0) {
292 TRACE("Unable to map MMIO, error: 0x%lx\n", si.regsArea);
293 return 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);
305 return B_NO_MEMORY;
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);
315 if (status < B_OK) {
316 TRACE("Unable to get physical address of GTT, error: 0x%lx\n", status);
317 return 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);
331 return B_NO_MEMORY;
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);
340 if (status < B_OK) {
341 TRACE("Unable to get physical address of video memory page, error:"
342 " 0x%lx offset: %ld\n", status, offset);
343 return status;
346 if (offset == 0)
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");
354 return B_OK;
358 static void
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);
371 if (di.gttArea >= 0)
372 delete_area(di.gttArea);
373 di.gttArea = -1;
374 di.gttAddr = (addr_t)NULL;
376 if (di.sharedArea >= 0)
377 delete_area(di.sharedArea);
378 di.sharedArea = -1;
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
390 // examined.
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
408 pDevice++;
412 pciIndex++;
415 return NULL; // no supported device found
419 // #pragma mark - Kernel Interface
422 status_t
423 init_hardware(void)
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);
431 return status;
434 // Check pci devices for a device supported by this driver.
436 uint32 pciIndex = 0;
437 pci_info pciInfo;
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);
449 status_t
450 init_driver(void)
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);
457 return status;
460 status = gLock.Init("i810 driver lock");
461 if (status < B_OK) {
462 put_module(B_AGP_GART_MODULE_NAME);
463 put_module(B_PCI_MODULE_NAME);
464 return status;
467 // Get info about all the devices supported by this driver.
469 uint32 pciIndex = 0;
470 uint32 count = 0;
472 while (count < MAX_DEVICES) {
473 DeviceInfo& di = gDeviceInfo[count];
475 const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, di.pciInfo);
476 if (pDevice == NULL)
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;
492 count++;
493 pciIndex++;
496 gDeviceNames[count] = NULL; // terminate list with null pointer
498 TRACE("init_driver() %ld supported devices\n", count);
500 return B_OK;
504 void
505 uninit_driver(void)
507 // Free the driver data.
509 gLock.Delete();
510 put_module(B_AGP_GART_MODULE_NAME);
511 put_module(B_PCI_MODULE_NAME); // put the pci module away
515 const char**
516 publish_devices(void)
518 return (const char**)gDeviceNames; // return list of supported devices
522 device_hooks*
523 find_device(const char* name)
525 int i = 0;
526 while (gDeviceNames[i] != NULL) {
527 if (strcmp(name, gDeviceNames[i]) == 0)
528 return &gDeviceHooks;
529 i++;
532 return NULL;
536 // #pragma mark - Device Hooks
539 static status_t
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,
545 (addr_t)cookie);
547 // Find the device name in the list of devices.
549 int32 i = 0;
550 while (gDeviceNames[i] != NULL && (strcmp(name, gDeviceNames[i]) != 0))
551 i++;
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);
562 if (status < B_OK)
563 DeleteAreas(di); // error occurred; delete any areas created
566 gLock.Release();
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,
574 di.openCount);
575 return status;
579 static status_t
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"
583 // warnings.
584 (void)dev;
585 (void)pos;
586 (void)buf;
588 *len = 0;
589 return B_NOT_ALLOWED;
593 static status_t
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"
597 // warnings.
598 (void)dev;
599 (void)pos;
600 (void)buf;
602 *len = 0;
603 return B_NOT_ALLOWED;
607 static status_t
608 device_close(void* dev)
610 (void)dev; // avoid compiler warning for unused arg
612 TRACE("device_close()\n");
613 return B_NO_ERROR;
617 static status_t
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)
629 DeleteAreas(di);
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);
637 return B_OK;
641 static status_t
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);
649 switch (msg) {
650 case B_GET_ACCELERANT_SIGNATURE:
651 strcpy((char*)buffer, ACCELERANT_NAME);
652 TRACE("Intel 810 accelerant: %s\n", ACCELERANT_NAME);
653 return B_OK;
655 case INTEL_DEVICE_NAME:
656 strncpy((char*)buffer, di.name, B_OS_NAME_LENGTH);
657 ((char*)buffer)[B_OS_NAME_LENGTH -1] = '\0';
658 return B_OK;
660 case INTEL_GET_SHARED_DATA:
661 if (bufferLength != sizeof(area_id))
662 return B_BAD_DATA;
664 *((area_id*)buffer) = di.sharedArea;
665 return B_OK;
667 case INTEL_GET_EDID:
669 if (bufferLength != sizeof(edid1_raw))
670 return B_BAD_DATA;
672 edid1_raw rawEdid;
673 status_t status = GetEdidFromBIOS(rawEdid);
674 if (status == B_OK)
675 user_memcpy((edid1_raw*)buffer, &rawEdid, sizeof(rawEdid));
676 return status;
680 return B_DEV_INVALID_IOCTL;