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 "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_PIPEA) != 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 | DISPLAY_PIPE_VBLANK_ENABLED); 91 } 92 93 if ((identity & INTERRUPT_VBLANK_PIPEB) != 0) { 94 handled = release_vblank_sem(info); 95 96 // make sure we'll get another one of those 97 write32(info.registers + INTEL_DISPLAY_B_PIPE_STATUS, 98 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED); 99 } 100 101 // setting the bit clears it! 102 write16(info.registers + INTEL_INTERRUPT_IDENTITY, identity); 103 104 return handled; 105 } 106 107 108 static void 109 init_interrupt_handler(intel_info &info) 110 { 111 info.shared_info->vblank_sem = create_sem(0, "intel extreme vblank"); 112 if (info.shared_info->vblank_sem < B_OK) 113 return; 114 115 status_t status = B_OK; 116 117 // We need to change the owner of the sem to the calling team (usually the 118 // app_server), because userland apps cannot acquire kernel semaphores 119 thread_id thread = find_thread(NULL); 120 thread_info threadInfo; 121 if (get_thread_info(thread, &threadInfo) != B_OK 122 || set_sem_owner(info.shared_info->vblank_sem, threadInfo.team) 123 != B_OK) { 124 status = B_ERROR; 125 } 126 127 if (status == B_OK && info.pci->u.h0.interrupt_pin != 0x00 128 && info.pci->u.h0.interrupt_line != 0xff) { 129 // we've gotten an interrupt line for us to use 130 131 info.fake_interrupts = false; 132 133 status = install_io_interrupt_handler(info.pci->u.h0.interrupt_line, 134 &intel_interrupt_handler, (void *)&info, 0); 135 if (status == B_OK) { 136 write32(info.registers + INTEL_DISPLAY_A_PIPE_STATUS, 137 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED); 138 write32(info.registers + INTEL_DISPLAY_B_PIPE_STATUS, 139 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED); 140 write16(info.registers + INTEL_INTERRUPT_IDENTITY, ~0); 141 142 // enable interrupts - we only want VBLANK interrupts 143 write16(info.registers + INTEL_INTERRUPT_ENABLED, 144 read16(info.registers + INTEL_INTERRUPT_ENABLED) 145 | INTERRUPT_VBLANK_PIPEA | INTERRUPT_VBLANK_PIPEB); 146 write16(info.registers + INTEL_INTERRUPT_MASK, 147 ~(INTERRUPT_VBLANK_PIPEA | INTERRUPT_VBLANK_PIPEB)); 148 } 149 } 150 if (status < B_OK) { 151 // There is no interrupt reserved for us, or we couldn't install our 152 // interrupt handler, let's fake the vblank interrupt for our clients 153 // using a timer interrupt 154 info.fake_interrupts = true; 155 156 // TODO: fake interrupts! 157 TRACE((DEVICE_NAME "Fake interrupt mode (no PCI interrupt line assigned)")); 158 status = B_ERROR; 159 } 160 161 if (status < B_OK) { 162 delete_sem(info.shared_info->vblank_sem); 163 info.shared_info->vblank_sem = B_ERROR; 164 } 165 } 166 167 168 // #pragma mark - 169 170 171 status_t 172 intel_free_memory(intel_info &info, addr_t base) 173 { 174 return gGART->free_memory(info.aperture, base); 175 } 176 177 178 status_t 179 intel_allocate_memory(intel_info &info, size_t size, size_t alignment, 180 uint32 flags, addr_t *_base, phys_addr_t *_physicalBase) 181 { 182 return gGART->allocate_memory(info.aperture, size, alignment, 183 flags, _base, _physicalBase); 184 } 185 186 187 status_t 188 intel_extreme_init(intel_info &info) 189 { 190 info.aperture = gGART->map_aperture(info.pci->bus, info.pci->device, 191 info.pci->function, 0, &info.aperture_base); 192 if (info.aperture < B_OK) 193 return info.aperture; 194 195 AreaKeeper sharedCreator; 196 info.shared_area = sharedCreator.Create("intel extreme shared info", 197 (void **)&info.shared_info, B_ANY_KERNEL_ADDRESS, 198 ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info)) + 3 * B_PAGE_SIZE, 199 B_FULL_LOCK, 0); 200 if (info.shared_area < B_OK) { 201 gGART->unmap_aperture(info.aperture); 202 return info.shared_area; 203 } 204 205 memset((void *)info.shared_info, 0, sizeof(intel_shared_info)); 206 207 int fbIndex = 0; 208 int mmioIndex = 1; 209 if (info.device_type.InFamily(INTEL_TYPE_9xx)) { 210 // For some reason Intel saw the need to change the order of the 211 // mappings with the introduction of the i9xx family 212 mmioIndex = 0; 213 fbIndex = 2; 214 } 215 216 // evaluate driver settings, if any 217 218 bool hardwareCursor; 219 read_settings(hardwareCursor); 220 221 // memory mapped I/O 222 223 // TODO: registers are mapped twice (by us and intel_gart), maybe we 224 // can share it between the drivers 225 226 AreaKeeper mmioMapper; 227 info.registers_area = mmioMapper.Map("intel extreme mmio", 228 (void *)info.pci->u.h0.base_registers[mmioIndex], 229 info.pci->u.h0.base_register_sizes[mmioIndex], 230 B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, 231 (void **)&info.registers); 232 if (mmioMapper.InitCheck() < B_OK) { 233 dprintf(DEVICE_NAME ": could not map memory I/O!\n"); 234 gGART->unmap_aperture(info.aperture); 235 return info.registers_area; 236 } 237 238 // make sure bus master, memory-mapped I/O, and frame buffer is enabled 239 set_pci_config(info.pci, PCI_command, 2, get_pci_config(info.pci, PCI_command, 2) 240 | PCI_command_io | PCI_command_memory | PCI_command_master); 241 242 // reserve ring buffer memory (currently, this memory is placed in 243 // the graphics memory), but this could bring us problems with 244 // write combining... 245 246 ring_buffer &primary = info.shared_info->primary_ring_buffer; 247 if (intel_allocate_memory(info, 16 * B_PAGE_SIZE, 0, 0, 248 (addr_t *)&primary.base) == B_OK) { 249 primary.register_base = INTEL_PRIMARY_RING_BUFFER; 250 primary.size = 16 * B_PAGE_SIZE; 251 primary.offset = (addr_t)primary.base - info.aperture_base; 252 } 253 254 // Clock gating 255 // Fix some problems on certain chips (taken from X driver) 256 // TODO: clean this up 257 if (info.pci->device_id == 0x2a02 || info.pci->device_id == 0x2a12) { 258 dprintf("i965GM/i965GME quirk\n"); 259 write32(info.registers + 0x6204, (1L << 29)); 260 } else if (info.device_type.InGroup(INTEL_TYPE_G4x)) { 261 dprintf("G4x clock gating\n"); 262 write32(info.registers + 0x6204, 0); 263 write32(info.registers + 0x6208, (1L << 9) | (1L << 7) | (1L << 6)); 264 write32(info.registers + 0x6210, 0); 265 266 uint32 gateValue = (1L << 28) | (1L << 3) | (1L << 2); 267 if ((info.device_type.type & INTEL_TYPE_MOBILE) == INTEL_TYPE_MOBILE) { 268 dprintf("G4x mobile clock gating\n"); 269 gateValue |= 1L << 18; 270 } 271 write32(info.registers + 0x6200, gateValue); 272 } else { 273 dprintf("i965 quirk\n"); 274 write32(info.registers + 0x6204, (1L << 29) | (1L << 23)); 275 } 276 write32(info.registers + 0x7408, 0x10); 277 278 // no errors, so keep areas and mappings 279 sharedCreator.Detach(); 280 mmioMapper.Detach(); 281 282 aperture_info apertureInfo; 283 gGART->get_aperture_info(info.aperture, &apertureInfo); 284 285 info.shared_info->registers_area = info.registers_area; 286 info.shared_info->graphics_memory = (uint8 *)info.aperture_base; 287 info.shared_info->physical_graphics_memory = apertureInfo.physical_base; 288 info.shared_info->graphics_memory_size = apertureInfo.size; 289 info.shared_info->frame_buffer = 0; 290 info.shared_info->dpms_mode = B_DPMS_ON; 291 292 if (info.device_type.InFamily(INTEL_TYPE_9xx)) { 293 info.shared_info->pll_info.reference_frequency = 96000; // 96 kHz 294 info.shared_info->pll_info.max_frequency = 400000; 295 // 400 MHz RAM DAC speed 296 info.shared_info->pll_info.min_frequency = 20000; // 20 MHz 297 } else { 298 info.shared_info->pll_info.reference_frequency = 48000; // 48 kHz 299 info.shared_info->pll_info.max_frequency = 350000; 300 // 350 MHz RAM DAC speed 301 info.shared_info->pll_info.min_frequency = 25000; // 25 MHz 302 } 303 304 info.shared_info->pll_info.divisor_register = INTEL_DISPLAY_A_PLL_DIVISOR_0; 305 306 info.shared_info->device_type = info.device_type; 307 #ifdef __HAIKU__ 308 strlcpy(info.shared_info->device_identifier, info.device_identifier, 309 sizeof(info.shared_info->device_identifier)); 310 #else 311 strcpy(info.shared_info->device_identifier, info.device_identifier); 312 #endif 313 314 // setup overlay registers 315 316 if (intel_allocate_memory(info, B_PAGE_SIZE, 0, 317 intel_uses_physical_overlay(*info.shared_info) 318 ? B_APERTURE_NEED_PHYSICAL : 0, 319 (addr_t *)&info.overlay_registers, 320 &info.shared_info->physical_overlay_registers) == B_OK) { 321 info.shared_info->overlay_offset = (addr_t)info.overlay_registers 322 - info.aperture_base; 323 } 324 325 init_overlay_registers(info.overlay_registers); 326 327 // Allocate hardware status page and the cursor memory 328 329 if (intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL, 330 (addr_t *)info.shared_info->status_page, 331 &info.shared_info->physical_status_page) == B_OK) { 332 // TODO: set status page 333 } 334 if (hardwareCursor) { 335 intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL, 336 (addr_t *)&info.shared_info->cursor_memory, 337 &info.shared_info->physical_cursor_memory); 338 } 339 340 init_interrupt_handler(info); 341 342 TRACE((DEVICE_NAME "_init() completed successfully!\n")); 343 return B_OK; 344 } 345 346 347 void 348 intel_extreme_uninit(intel_info &info) 349 { 350 TRACE((DEVICE_NAME": intel_extreme_uninit()\n")); 351 352 if (!info.fake_interrupts && info.shared_info->vblank_sem > 0) { 353 // disable interrupt generation 354 write16(info.registers + INTEL_INTERRUPT_ENABLED, 0); 355 write16(info.registers + INTEL_INTERRUPT_MASK, ~0); 356 357 remove_io_interrupt_handler(info.pci->u.h0.interrupt_line, 358 intel_interrupt_handler, &info); 359 } 360 361 gGART->unmap_aperture(info.aperture); 362 363 delete_area(info.registers_area); 364 delete_area(info.shared_area); 365 } 366 367