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