1 /* 2 * Copyright 2006-2009, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 */ 8 9 10 #include "intel_extreme.h" 11 12 #include "AreaKeeper.h" 13 #include "driver.h" 14 #include "utility.h" 15 16 #include <unistd.h> 17 #include <stdio.h> 18 #include <string.h> 19 #include <errno.h> 20 21 #include <driver_settings.h> 22 #include <util/kernel_cpp.h> 23 24 25 #define TRACE_DEVICE 26 #ifdef TRACE_DEVICE 27 # define TRACE(x) dprintf x 28 #else 29 # define TRACE(x) ; 30 #endif 31 32 33 static void 34 init_overlay_registers(overlay_registers *registers) 35 { 36 memset(registers, 0, B_PAGE_SIZE); 37 38 registers->contrast_correction = 0x48; 39 registers->saturation_cos_correction = 0x9a; 40 // this by-passes contrast and saturation correction 41 } 42 43 44 static void 45 read_settings(bool &hardwareCursor) 46 { 47 hardwareCursor = false; 48 49 void *settings = load_driver_settings("intel_extreme"); 50 if (settings != NULL) { 51 hardwareCursor = get_driver_boolean_parameter(settings, 52 "hardware_cursor", true, true); 53 54 unload_driver_settings(settings); 55 } 56 } 57 58 59 static int32 60 release_vblank_sem(intel_info &info) 61 { 62 int32 count; 63 if (get_sem_count(info.shared_info->vblank_sem, &count) == B_OK 64 && count < 0) { 65 release_sem_etc(info.shared_info->vblank_sem, -count, 66 B_DO_NOT_RESCHEDULE); 67 return B_INVOKE_SCHEDULER; 68 } 69 70 return B_HANDLED_INTERRUPT; 71 } 72 73 74 static int32 75 intel_interrupt_handler(void *data) 76 { 77 intel_info &info = *(intel_info *)data; 78 79 uint32 identity = read16(info.registers + INTEL_INTERRUPT_IDENTITY); 80 if (identity == 0) 81 return B_UNHANDLED_INTERRUPT; 82 83 int32 handled = B_HANDLED_INTERRUPT; 84 85 if ((identity & INTERRUPT_VBLANK) != 0) { 86 handled = release_vblank_sem(info); 87 88 // make sure we'll get another one of those 89 write32(info.registers + INTEL_DISPLAY_A_PIPE_STATUS, 90 DISPLAY_PIPE_VBLANK_STATUS); 91 } 92 93 // setting the bit clears it! 94 write16(info.registers + INTEL_INTERRUPT_IDENTITY, identity); 95 96 return handled; 97 } 98 99 100 static void 101 init_interrupt_handler(intel_info &info) 102 { 103 info.shared_info->vblank_sem = create_sem(0, "intel extreme vblank"); 104 if (info.shared_info->vblank_sem < B_OK) 105 return; 106 107 status_t status = B_OK; 108 109 // We need to change the owner of the sem to the calling team (usually the 110 // app_server), because userland apps cannot acquire kernel semaphores 111 thread_id thread = find_thread(NULL); 112 thread_info threadInfo; 113 if (get_thread_info(thread, &threadInfo) != B_OK 114 || set_sem_owner(info.shared_info->vblank_sem, threadInfo.team) != B_OK) { 115 status = B_ERROR; 116 } 117 118 if (status == B_OK && info.pci->u.h0.interrupt_pin != 0x00 119 && info.pci->u.h0.interrupt_line != 0xff) { 120 // we've gotten an interrupt line for us to use 121 122 info.fake_interrupts = false; 123 124 status = install_io_interrupt_handler(info.pci->u.h0.interrupt_line, 125 &intel_interrupt_handler, (void *)&info, 0); 126 if (status == B_OK) { 127 // enable interrupts - we only want VBLANK interrupts 128 write16(info.registers + INTEL_INTERRUPT_ENABLED, 129 read16(info.registers + INTEL_INTERRUPT_ENABLED) 130 | INTERRUPT_VBLANK); 131 write16(info.registers + INTEL_INTERRUPT_MASK, ~INTERRUPT_VBLANK); 132 133 write32(info.registers + INTEL_DISPLAY_A_PIPE_STATUS, 134 DISPLAY_PIPE_VBLANK_STATUS); 135 write16(info.registers + INTEL_INTERRUPT_IDENTITY, ~0); 136 } 137 } 138 if (status < B_OK) { 139 // There is no interrupt reserved for us, or we couldn't install our 140 // interrupt handler, let's fake the vblank interrupt for our clients 141 // using a timer interrupt 142 info.fake_interrupts = true; 143 144 // TODO: fake interrupts! 145 status = B_ERROR; 146 } 147 148 if (status < B_OK) { 149 delete_sem(info.shared_info->vblank_sem); 150 info.shared_info->vblank_sem = B_ERROR; 151 } 152 } 153 154 155 // #pragma mark - 156 157 158 status_t 159 intel_free_memory(intel_info &info, addr_t base) 160 { 161 return gGART->free_memory(info.aperture, base); 162 } 163 164 165 status_t 166 intel_allocate_memory(intel_info &info, size_t size, size_t alignment, 167 uint32 flags, addr_t *_base, addr_t *_physicalBase) 168 { 169 return gGART->allocate_memory(info.aperture, size, alignment, 170 flags, _base, _physicalBase); 171 } 172 173 174 status_t 175 intel_extreme_init(intel_info &info) 176 { 177 info.aperture = gGART->map_aperture(info.pci->bus, info.pci->device, 178 info.pci->function, 0, &info.aperture_base); 179 if (info.aperture < B_OK) 180 return info.aperture; 181 182 AreaKeeper sharedCreator; 183 info.shared_area = sharedCreator.Create("intel extreme shared info", 184 (void **)&info.shared_info, B_ANY_KERNEL_ADDRESS, 185 ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info)) + 3 * B_PAGE_SIZE, 186 B_FULL_LOCK, 0); 187 if (info.shared_area < B_OK) { 188 gGART->unmap_aperture(info.aperture); 189 return info.shared_area; 190 } 191 192 memset((void *)info.shared_info, 0, sizeof(intel_shared_info)); 193 194 int fbIndex = 0; 195 int mmioIndex = 1; 196 if (info.device_type.InFamily(INTEL_TYPE_9xx)) { 197 // For some reason Intel saw the need to change the order of the 198 // mappings with the introduction of the i9xx family 199 mmioIndex = 0; 200 fbIndex = 2; 201 } 202 203 // evaluate driver settings, if any 204 205 bool hardwareCursor; 206 read_settings(hardwareCursor); 207 208 // memory mapped I/O 209 210 // TODO: registers are mapped twice (by us and intel_gart), maybe we 211 // can share it between the drivers 212 213 AreaKeeper mmioMapper; 214 info.registers_area = mmioMapper.Map("intel extreme mmio", 215 (void *)info.pci->u.h0.base_registers[mmioIndex], 216 info.pci->u.h0.base_register_sizes[mmioIndex], 217 B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, 218 (void **)&info.registers); 219 if (mmioMapper.InitCheck() < B_OK) { 220 dprintf(DEVICE_NAME ": could not map memory I/O!\n"); 221 gGART->unmap_aperture(info.aperture); 222 return info.registers_area; 223 } 224 225 // make sure bus master, memory-mapped I/O, and frame buffer is enabled 226 set_pci_config(info.pci, PCI_command, 2, get_pci_config(info.pci, PCI_command, 2) 227 | PCI_command_io | PCI_command_memory | PCI_command_master); 228 229 // reserve ring buffer memory (currently, this memory is placed in 230 // the graphics memory), but this could bring us problems with 231 // write combining... 232 233 ring_buffer &primary = info.shared_info->primary_ring_buffer; 234 if (intel_allocate_memory(info, 16 * B_PAGE_SIZE, 0, 0, 235 (addr_t *)&primary.base) == B_OK) { 236 primary.register_base = INTEL_PRIMARY_RING_BUFFER; 237 primary.size = 16 * B_PAGE_SIZE; 238 primary.offset = (addr_t)primary.base - info.aperture_base; 239 } 240 241 // Clock gating 242 // Fix some problems on certain chips (taken from X driver) 243 // TODO: clean this up 244 if (info.pci->device_id == 0x2a02 || info.pci->device_id == 0x2a12) { 245 dprintf("i965GM/i965GME quirk\n"); 246 write32(info.registers + 0x6204, (1L << 29)); 247 } else if (info.device_type.InGroup(INTEL_TYPE_G4x)) { 248 dprintf("G4x clock gating\n"); 249 write32(info.registers + 0x6204, 0); 250 write32(info.registers + 0x6208, (1L << 9) | (1L << 7) | (1L << 6)); 251 write32(info.registers + 0x6210, 0); 252 253 uint32 dspclk_gate_val = (1L << 28) | (1L << 3) | (1L << 2); 254 if ((info.device_type.type & INTEL_TYPE_MOBILE) == INTEL_TYPE_MOBILE) { 255 dprintf("G4x mobile clock gating\n"); 256 dspclk_gate_val |= 1L << 18; 257 } 258 write32(info.registers + 0x6200, dspclk_gate_val) ; 259 260 } else { 261 dprintf("i965 quirk\n"); 262 write32(info.registers + 0x6204, (1L << 29) | (1L << 23)); 263 } 264 write32(info.registers + 0x7408, 0x10); 265 266 // no errors, so keep areas and mappings 267 sharedCreator.Detach(); 268 mmioMapper.Detach(); 269 270 aperture_info apertureInfo; 271 gGART->get_aperture_info(info.aperture, &apertureInfo); 272 273 info.shared_info->registers_area = info.registers_area; 274 info.shared_info->graphics_memory = (uint8 *)info.aperture_base; 275 info.shared_info->physical_graphics_memory = apertureInfo.physical_base; 276 info.shared_info->graphics_memory_size = apertureInfo.size; 277 info.shared_info->frame_buffer = 0; 278 info.shared_info->dpms_mode = B_DPMS_ON; 279 280 if (info.device_type.InFamily(INTEL_TYPE_9xx)) { 281 info.shared_info->pll_info.reference_frequency = 96000; // 96 kHz 282 info.shared_info->pll_info.max_frequency = 400000; 283 // 400 MHz RAM DAC speed 284 info.shared_info->pll_info.min_frequency = 20000; // 20 MHz 285 } else { 286 info.shared_info->pll_info.reference_frequency = 48000; // 48 kHz 287 info.shared_info->pll_info.max_frequency = 350000; 288 // 350 MHz RAM DAC speed 289 info.shared_info->pll_info.min_frequency = 25000; // 25 MHz 290 } 291 292 info.shared_info->pll_info.divisor_register = INTEL_DISPLAY_A_PLL_DIVISOR_0; 293 294 info.shared_info->device_type = info.device_type; 295 #ifdef __HAIKU__ 296 strlcpy(info.shared_info->device_identifier, info.device_identifier, 297 sizeof(info.shared_info->device_identifier)); 298 #else 299 strcpy(info.shared_info->device_identifier, info.device_identifier); 300 #endif 301 302 // setup overlay registers 303 304 if (intel_allocate_memory(info, B_PAGE_SIZE, 0, 305 intel_uses_physical_overlay(*info.shared_info) 306 ? B_APERTURE_NEED_PHYSICAL : 0, 307 (addr_t *)&info.overlay_registers, 308 &info.shared_info->physical_overlay_registers) == B_OK) { 309 info.shared_info->overlay_offset = (addr_t)info.overlay_registers 310 - info.aperture_base; 311 } 312 313 init_overlay_registers(info.overlay_registers); 314 315 // Allocate hardware status page and the cursor memory 316 317 if (intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL, 318 (addr_t *)info.shared_info->status_page, 319 &info.shared_info->physical_status_page) == B_OK) { 320 // TODO: set status page 321 } 322 if (hardwareCursor) { 323 intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL, 324 (addr_t *)&info.shared_info->cursor_memory, 325 &info.shared_info->physical_cursor_memory); 326 } 327 328 init_interrupt_handler(info); 329 330 TRACE((DEVICE_NAME "intel_extreme_init() completed successfully!\n")); 331 return B_OK; 332 } 333 334 335 void 336 intel_extreme_uninit(intel_info &info) 337 { 338 TRACE((DEVICE_NAME": intel_extreme_uninit()\n")); 339 340 if (!info.fake_interrupts && info.shared_info->vblank_sem > 0) { 341 // disable interrupt generation 342 write16(info.registers + INTEL_INTERRUPT_ENABLED, 0); 343 write16(info.registers + INTEL_INTERRUPT_MASK, ~0); 344 345 remove_io_interrupt_handler(info.pci->u.h0.interrupt_line, 346 intel_interrupt_handler, &info); 347 } 348 349 gGART->unmap_aperture(info.aperture); 350 351 delete_area(info.registers_area); 352 delete_area(info.shared_area); 353 } 354 355