/* * Copyright 2006-2009, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Axel Dörfler, axeld@pinc-software.de */ #include "intel_extreme.h" #include "AreaKeeper.h" #include "driver.h" #include "utility.h" #include #include #include #include #include #include #define TRACE_DEVICE #ifdef TRACE_DEVICE # define TRACE(x) dprintf x #else # define TRACE(x) ; #endif static void init_overlay_registers(overlay_registers *registers) { memset(registers, 0, B_PAGE_SIZE); registers->contrast_correction = 0x48; registers->saturation_cos_correction = 0x9a; // this by-passes contrast and saturation correction } static void read_settings(bool &hardwareCursor) { hardwareCursor = false; void *settings = load_driver_settings("intel_extreme"); if (settings != NULL) { hardwareCursor = get_driver_boolean_parameter(settings, "hardware_cursor", true, true); unload_driver_settings(settings); } } static int32 release_vblank_sem(intel_info &info) { int32 count; if (get_sem_count(info.shared_info->vblank_sem, &count) == B_OK && count < 0) { release_sem_etc(info.shared_info->vblank_sem, -count, B_DO_NOT_RESCHEDULE); return B_INVOKE_SCHEDULER; } return B_HANDLED_INTERRUPT; } static int32 intel_interrupt_handler(void *data) { intel_info &info = *(intel_info *)data; uint32 identity = read16(info.registers + INTEL_INTERRUPT_IDENTITY); if (identity == 0) return B_UNHANDLED_INTERRUPT; int32 handled = B_HANDLED_INTERRUPT; if ((identity & INTERRUPT_VBLANK) != 0) { handled = release_vblank_sem(info); // make sure we'll get another one of those write32(info.registers + INTEL_DISPLAY_A_PIPE_STATUS, DISPLAY_PIPE_VBLANK_STATUS); } // setting the bit clears it! write16(info.registers + INTEL_INTERRUPT_IDENTITY, identity); return handled; } static void init_interrupt_handler(intel_info &info) { info.shared_info->vblank_sem = create_sem(0, "intel extreme vblank"); if (info.shared_info->vblank_sem < B_OK) return; status_t status = B_OK; // We need to change the owner of the sem to the calling team (usually the // app_server), because userland apps cannot acquire kernel semaphores thread_id thread = find_thread(NULL); thread_info threadInfo; if (get_thread_info(thread, &threadInfo) != B_OK || set_sem_owner(info.shared_info->vblank_sem, threadInfo.team) != B_OK) { status = B_ERROR; } if (status == B_OK && info.pci->u.h0.interrupt_pin != 0x00 && info.pci->u.h0.interrupt_line != 0xff) { // we've gotten an interrupt line for us to use info.fake_interrupts = false; status = install_io_interrupt_handler(info.pci->u.h0.interrupt_line, &intel_interrupt_handler, (void *)&info, 0); if (status == B_OK) { // enable interrupts - we only want VBLANK interrupts write16(info.registers + INTEL_INTERRUPT_ENABLED, read16(info.registers + INTEL_INTERRUPT_ENABLED) | INTERRUPT_VBLANK); write16(info.registers + INTEL_INTERRUPT_MASK, ~INTERRUPT_VBLANK); write32(info.registers + INTEL_DISPLAY_A_PIPE_STATUS, DISPLAY_PIPE_VBLANK_STATUS); write16(info.registers + INTEL_INTERRUPT_IDENTITY, ~0); } } if (status < B_OK) { // There is no interrupt reserved for us, or we couldn't install our // interrupt handler, let's fake the vblank interrupt for our clients // using a timer interrupt info.fake_interrupts = true; // TODO: fake interrupts! status = B_ERROR; } if (status < B_OK) { delete_sem(info.shared_info->vblank_sem); info.shared_info->vblank_sem = B_ERROR; } } // #pragma mark - status_t intel_free_memory(intel_info &info, addr_t base) { return gGART->free_memory(info.aperture, base); } status_t intel_allocate_memory(intel_info &info, size_t size, size_t alignment, uint32 flags, addr_t *_base, addr_t *_physicalBase) { return gGART->allocate_memory(info.aperture, size, alignment, flags, _base, _physicalBase); } status_t intel_extreme_init(intel_info &info) { info.aperture = gGART->map_aperture(info.pci->bus, info.pci->device, info.pci->function, 0, &info.aperture_base); if (info.aperture < B_OK) return info.aperture; AreaKeeper sharedCreator; info.shared_area = sharedCreator.Create("intel extreme shared info", (void **)&info.shared_info, B_ANY_KERNEL_ADDRESS, ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info)) + 3 * B_PAGE_SIZE, B_FULL_LOCK, 0); if (info.shared_area < B_OK) { gGART->unmap_aperture(info.aperture); return info.shared_area; } memset((void *)info.shared_info, 0, sizeof(intel_shared_info)); int fbIndex = 0; int mmioIndex = 1; if (info.device_type.InFamily(INTEL_TYPE_9xx)) { // For some reason Intel saw the need to change the order of the // mappings with the introduction of the i9xx family mmioIndex = 0; fbIndex = 2; } // evaluate driver settings, if any bool hardwareCursor; read_settings(hardwareCursor); // memory mapped I/O // TODO: registers are mapped twice (by us and intel_gart), maybe we // can share it between the drivers AreaKeeper mmioMapper; info.registers_area = mmioMapper.Map("intel extreme mmio", (void *)info.pci->u.h0.base_registers[mmioIndex], info.pci->u.h0.base_register_sizes[mmioIndex], B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void **)&info.registers); if (mmioMapper.InitCheck() < B_OK) { dprintf(DEVICE_NAME ": could not map memory I/O!\n"); gGART->unmap_aperture(info.aperture); return info.registers_area; } // make sure bus master, memory-mapped I/O, and frame buffer is enabled set_pci_config(info.pci, PCI_command, 2, get_pci_config(info.pci, PCI_command, 2) | PCI_command_io | PCI_command_memory | PCI_command_master); // reserve ring buffer memory (currently, this memory is placed in // the graphics memory), but this could bring us problems with // write combining... ring_buffer &primary = info.shared_info->primary_ring_buffer; if (intel_allocate_memory(info, 16 * B_PAGE_SIZE, 0, 0, (addr_t *)&primary.base) == B_OK) { primary.register_base = INTEL_PRIMARY_RING_BUFFER; primary.size = 16 * B_PAGE_SIZE; primary.offset = (addr_t)primary.base - info.aperture_base; } // Clock gating // Fix some problems on certain chips (taken from X driver) // TODO: clean this up if (info.pci->device_id == 0x2a02 || info.pci->device_id == 0x2a12) { dprintf("i965GM/i965GME quirk\n"); write32(info.registers + 0x6204, (1L << 29)); } else if (info.device_type.InGroup(INTEL_TYPE_G4x)) { dprintf("G4x clock gating\n"); write32(info.registers + 0x6204, 0); write32(info.registers + 0x6208, (1L << 9) | (1L << 7) | (1L << 6)); write32(info.registers + 0x6210, 0); uint32 dspclk_gate_val = (1L << 28) | (1L << 3) | (1L << 2); if ((info.device_type.type & INTEL_TYPE_MOBILE) == INTEL_TYPE_MOBILE) { dprintf("G4x mobile clock gating\n"); dspclk_gate_val |= 1L << 18; } write32(info.registers + 0x6200, dspclk_gate_val) ; } else { dprintf("i965 quirk\n"); write32(info.registers + 0x6204, (1L << 29) | (1L << 23)); } write32(info.registers + 0x7408, 0x10); // no errors, so keep areas and mappings sharedCreator.Detach(); mmioMapper.Detach(); aperture_info apertureInfo; gGART->get_aperture_info(info.aperture, &apertureInfo); info.shared_info->registers_area = info.registers_area; info.shared_info->graphics_memory = (uint8 *)info.aperture_base; info.shared_info->physical_graphics_memory = apertureInfo.physical_base; info.shared_info->graphics_memory_size = apertureInfo.size; info.shared_info->frame_buffer = 0; info.shared_info->dpms_mode = B_DPMS_ON; if (info.device_type.InFamily(INTEL_TYPE_9xx)) { info.shared_info->pll_info.reference_frequency = 96000; // 96 kHz info.shared_info->pll_info.max_frequency = 400000; // 400 MHz RAM DAC speed info.shared_info->pll_info.min_frequency = 20000; // 20 MHz } else { info.shared_info->pll_info.reference_frequency = 48000; // 48 kHz info.shared_info->pll_info.max_frequency = 350000; // 350 MHz RAM DAC speed info.shared_info->pll_info.min_frequency = 25000; // 25 MHz } info.shared_info->pll_info.divisor_register = INTEL_DISPLAY_A_PLL_DIVISOR_0; info.shared_info->device_type = info.device_type; #ifdef __HAIKU__ strlcpy(info.shared_info->device_identifier, info.device_identifier, sizeof(info.shared_info->device_identifier)); #else strcpy(info.shared_info->device_identifier, info.device_identifier); #endif // setup overlay registers if (intel_allocate_memory(info, B_PAGE_SIZE, 0, intel_uses_physical_overlay(*info.shared_info) ? B_APERTURE_NEED_PHYSICAL : 0, (addr_t *)&info.overlay_registers, &info.shared_info->physical_overlay_registers) == B_OK) { info.shared_info->overlay_offset = (addr_t)info.overlay_registers - info.aperture_base; } init_overlay_registers(info.overlay_registers); // Allocate hardware status page and the cursor memory if (intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL, (addr_t *)info.shared_info->status_page, &info.shared_info->physical_status_page) == B_OK) { // TODO: set status page } if (hardwareCursor) { intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL, (addr_t *)&info.shared_info->cursor_memory, &info.shared_info->physical_cursor_memory); } init_interrupt_handler(info); TRACE((DEVICE_NAME "intel_extreme_init() completed successfully!\n")); return B_OK; } void intel_extreme_uninit(intel_info &info) { TRACE((DEVICE_NAME": intel_extreme_uninit()\n")); if (!info.fake_interrupts && info.shared_info->vblank_sem > 0) { // disable interrupt generation write16(info.registers + INTEL_INTERRUPT_ENABLED, 0); write16(info.registers + INTEL_INTERRUPT_MASK, ~0); remove_io_interrupt_handler(info.pci->u.h0.interrupt_line, intel_interrupt_handler, &info); } gGART->unmap_aperture(info.aperture); delete_area(info.registers_area); delete_area(info.shared_area); }