usb_ecm: Use the current configuration instead of a fixed one.
[haiku.git] / src / add-ons / kernel / drivers / graphics / intel_extreme / intel_extreme.cpp
blob7637ab9da0e94fd772a1d27937dfff3b04529c38
1 /*
2 * Copyright 2006-2014, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 * Alexander von Gluck IV, kallisti5@unixzen.com
8 */
11 #include "intel_extreme.h"
13 #include "AreaKeeper.h"
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <errno.h>
19 #include <boot_item.h>
20 #include <driver_settings.h>
21 #include <util/kernel_cpp.h>
23 #include <vesa_info.h>
25 #include "driver.h"
26 #include "power.h"
27 #include "utility.h"
30 #define TRACE_INTELEXTREME
31 #ifdef TRACE_INTELEXTREME
32 # define TRACE(x...) dprintf("intel_extreme: " x)
33 #else
34 # define TRACE(x) ;
35 #endif
37 #define ERROR(x...) dprintf("intel_extreme: " x)
38 #define CALLED(x...) TRACE("intel_extreme: CALLED %s\n", __PRETTY_FUNCTION__)
41 static void
42 init_overlay_registers(overlay_registers* registers)
44 memset(registers, 0, B_PAGE_SIZE);
46 registers->contrast_correction = 0x48;
47 registers->saturation_cos_correction = 0x9a;
48 // this by-passes contrast and saturation correction
52 static void
53 read_settings(bool &hardwareCursor)
55 hardwareCursor = false;
57 void* settings = load_driver_settings("intel_extreme");
58 if (settings != NULL) {
59 hardwareCursor = get_driver_boolean_parameter(settings,
60 "hardware_cursor", true, true);
62 unload_driver_settings(settings);
67 static int32
68 release_vblank_sem(intel_info &info)
70 int32 count;
71 if (get_sem_count(info.shared_info->vblank_sem, &count) == B_OK
72 && count < 0) {
73 release_sem_etc(info.shared_info->vblank_sem, -count,
74 B_DO_NOT_RESCHEDULE);
75 return B_INVOKE_SCHEDULER;
78 return B_HANDLED_INTERRUPT;
82 static int32
83 intel_interrupt_handler(void* data)
85 intel_info &info = *(intel_info*)data;
86 uint32 reg = find_reg(info, INTEL_INTERRUPT_IDENTITY);
87 uint16 identity = read16(info, reg);
88 if (identity == 0)
89 return B_UNHANDLED_INTERRUPT;
91 int32 handled = B_HANDLED_INTERRUPT;
93 while (identity != 0) {
95 // TODO: verify that these aren't actually the same
96 bool hasPCH = info.device_type.HasPlatformControlHub();
97 uint16 mask;
99 // Intel changed the PCH register mapping between Sandy Bridge and the
100 // later generations (Ivy Bridge and up).
101 if (info.device_type.InFamily(INTEL_TYPE_SNB)) {
102 mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEA_SNB
103 : INTERRUPT_VBLANK_PIPEA;
104 if ((identity & mask) != 0) {
105 handled = release_vblank_sem(info);
107 // make sure we'll get another one of those
108 write32(info, INTEL_DISPLAY_A_PIPE_STATUS,
109 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED);
112 mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEB_SNB
113 : INTERRUPT_VBLANK_PIPEB;
114 if ((identity & mask) != 0) {
115 handled = release_vblank_sem(info);
117 // make sure we'll get another one of those
118 write32(info, INTEL_DISPLAY_B_PIPE_STATUS,
119 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED);
121 } else {
122 mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEA
123 : INTERRUPT_VBLANK_PIPEA;
124 if ((identity & mask) != 0) {
125 handled = release_vblank_sem(info);
127 // make sure we'll get another one of those
128 write32(info, INTEL_DISPLAY_A_PIPE_STATUS,
129 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED);
132 mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEB
133 : INTERRUPT_VBLANK_PIPEB;
134 if ((identity & mask) != 0) {
135 handled = release_vblank_sem(info);
137 // make sure we'll get another one of those
138 write32(info, INTEL_DISPLAY_B_PIPE_STATUS,
139 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED);
142 #if 0
143 // FIXME we don't have supprot for the 3rd pipe yet
144 mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEC
145 : 0;
146 if ((identity & mask) != 0) {
147 handled = release_vblank_sem(info);
149 // make sure we'll get another one of those
150 write32(info, INTEL_DISPLAY_C_PIPE_STATUS,
151 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED);
153 #endif
156 // setting the bit clears it!
157 write16(info, reg, identity);
158 identity = read16(info, reg);
161 return handled;
165 static void
166 init_interrupt_handler(intel_info &info)
168 info.shared_info->vblank_sem = create_sem(0, "intel extreme vblank");
169 if (info.shared_info->vblank_sem < B_OK)
170 return;
172 status_t status = B_OK;
174 // We need to change the owner of the sem to the calling team (usually the
175 // app_server), because userland apps cannot acquire kernel semaphores
176 thread_id thread = find_thread(NULL);
177 thread_info threadInfo;
178 if (get_thread_info(thread, &threadInfo) != B_OK
179 || set_sem_owner(info.shared_info->vblank_sem, threadInfo.team)
180 != B_OK) {
181 status = B_ERROR;
184 // Find the right interrupt vector, using MSIs if available.
185 info.irq = 0xff;
186 info.use_msi = false;
187 if (info.pci->u.h0.interrupt_pin != 0x00)
188 info.irq = info.pci->u.h0.interrupt_line;
189 if (gPCIx86Module != NULL && gPCIx86Module->get_msi_count(info.pci->bus,
190 info.pci->device, info.pci->function) >= 1) {
191 uint8 msiVector = 0;
192 if (gPCIx86Module->configure_msi(info.pci->bus, info.pci->device,
193 info.pci->function, 1, &msiVector) == B_OK
194 && gPCIx86Module->enable_msi(info.pci->bus, info.pci->device,
195 info.pci->function) == B_OK) {
196 ERROR("using message signaled interrupts\n");
197 info.irq = msiVector;
198 info.use_msi = true;
202 if (status == B_OK && info.irq != 0xff) {
203 // we've gotten an interrupt line for us to use
205 info.fake_interrupts = false;
207 status = install_io_interrupt_handler(info.irq,
208 &intel_interrupt_handler, (void*)&info, 0);
209 if (status == B_OK) {
210 write32(info, INTEL_DISPLAY_A_PIPE_STATUS,
211 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED);
212 write32(info, INTEL_DISPLAY_B_PIPE_STATUS,
213 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED);
215 write16(info, find_reg(info, INTEL_INTERRUPT_IDENTITY), ~0);
217 // enable interrupts - we only want VBLANK interrupts
218 bool hasPCH = info.device_type.HasPlatformControlHub();
219 uint16 enable = hasPCH
220 ? (PCH_INTERRUPT_VBLANK_PIPEA | PCH_INTERRUPT_VBLANK_PIPEB)
221 : (INTERRUPT_VBLANK_PIPEA | INTERRUPT_VBLANK_PIPEB);
223 write16(info, find_reg(info, INTEL_INTERRUPT_ENABLED), enable);
224 write16(info, find_reg(info, INTEL_INTERRUPT_MASK), ~enable);
227 if (status < B_OK) {
228 // There is no interrupt reserved for us, or we couldn't install our
229 // interrupt handler, let's fake the vblank interrupt for our clients
230 // using a timer interrupt
231 info.fake_interrupts = true;
233 // TODO: fake interrupts!
234 TRACE("Fake interrupt mode (no PCI interrupt line assigned\n");
235 status = B_ERROR;
238 if (status < B_OK) {
239 delete_sem(info.shared_info->vblank_sem);
240 info.shared_info->vblank_sem = B_ERROR;
245 // #pragma mark -
248 status_t
249 intel_free_memory(intel_info &info, addr_t base)
251 return gGART->free_memory(info.aperture, base);
255 status_t
256 intel_allocate_memory(intel_info &info, size_t size, size_t alignment,
257 uint32 flags, addr_t* _base, phys_addr_t* _physicalBase)
259 return gGART->allocate_memory(info.aperture, size, alignment,
260 flags, _base, _physicalBase);
264 status_t
265 intel_extreme_init(intel_info &info)
267 CALLED();
268 info.aperture = gGART->map_aperture(info.pci->bus, info.pci->device,
269 info.pci->function, 0, &info.aperture_base);
270 if (info.aperture < B_OK) {
271 ERROR("error: could not map GART aperture! (%s)\n", strerror(info.aperture));
272 return info.aperture;
275 AreaKeeper sharedCreator;
276 info.shared_area = sharedCreator.Create("intel extreme shared info",
277 (void**)&info.shared_info, B_ANY_KERNEL_ADDRESS,
278 ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info)) + 3 * B_PAGE_SIZE,
279 B_FULL_LOCK, 0);
280 if (info.shared_area < B_OK) {
281 ERROR("error: could not create shared area!\n");
282 gGART->unmap_aperture(info.aperture);
283 return info.shared_area;
286 memset((void*)info.shared_info, 0, sizeof(intel_shared_info));
288 int fbIndex = 0;
289 int mmioIndex = 1;
290 if (info.device_type.InFamily(INTEL_TYPE_9xx)) {
291 // For some reason Intel saw the need to change the order of the
292 // mappings with the introduction of the i9xx family
293 mmioIndex = 0;
294 fbIndex = 2;
297 // evaluate driver settings, if any
299 bool hardwareCursor;
300 read_settings(hardwareCursor);
302 // memory mapped I/O
304 // TODO: registers are mapped twice (by us and intel_gart), maybe we
305 // can share it between the drivers
307 AreaKeeper mmioMapper;
308 info.registers_area = mmioMapper.Map("intel extreme mmio",
309 info.pci->u.h0.base_registers[mmioIndex],
310 info.pci->u.h0.base_register_sizes[mmioIndex],
311 B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
312 (void**)&info.registers);
313 if (mmioMapper.InitCheck() < B_OK) {
314 ERROR("error: could not map memory I/O!\n");
315 gGART->unmap_aperture(info.aperture);
316 return info.registers_area;
319 uint32* blocks = info.shared_info->register_blocks;
320 blocks[REGISTER_BLOCK(REGS_FLAT)] = 0;
322 // setup the register blocks for the different architectures
323 if (info.device_type.HasPlatformControlHub()) {
324 // PCH based platforms (IronLake and up)
325 blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)]
326 = PCH_NORTH_SHARED_REGISTER_BASE;
327 blocks[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT)]
328 = PCH_NORTH_PIPE_AND_PORT_REGISTER_BASE;
329 blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)]
330 = PCH_NORTH_PLANE_CONTROL_REGISTER_BASE;
331 blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)]
332 = PCH_SOUTH_SHARED_REGISTER_BASE;
333 blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)]
334 = PCH_SOUTH_TRANSCODER_AND_PORT_REGISTER_BASE;
335 } else {
336 // (G)MCH/ICH based platforms
337 blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)]
338 = MCH_SHARED_REGISTER_BASE;
339 blocks[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT)]
340 = MCH_PIPE_AND_PORT_REGISTER_BASE;
341 blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)]
342 = MCH_PLANE_CONTROL_REGISTER_BASE;
343 blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)]
344 = ICH_SHARED_REGISTER_BASE;
345 blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)]
346 = ICH_PORT_REGISTER_BASE;
349 // "I nearly got violent with the hw guys when they told me..."
350 if (info.device_type.InFamily(INTEL_TYPE_VLV)) {
351 TRACE("%s: ValleyView MMIO offset engaged\n", __func__);
352 blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)] += VLV_DISPLAY_BASE;
353 blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)] += VLV_DISPLAY_BASE;
354 blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)] += VLV_DISPLAY_BASE;
357 // make sure bus master, memory-mapped I/O, and frame buffer is enabled
358 set_pci_config(info.pci, PCI_command, 2, get_pci_config(info.pci,
359 PCI_command, 2) | PCI_command_io | PCI_command_memory
360 | PCI_command_master);
362 // reserve ring buffer memory (currently, this memory is placed in
363 // the graphics memory), but this could bring us problems with
364 // write combining...
366 ring_buffer &primary = info.shared_info->primary_ring_buffer;
367 if (intel_allocate_memory(info, 16 * B_PAGE_SIZE, 0, 0,
368 (addr_t*)&primary.base) == B_OK) {
369 primary.register_base = INTEL_PRIMARY_RING_BUFFER;
370 primary.size = 16 * B_PAGE_SIZE;
371 primary.offset = (addr_t)primary.base - info.aperture_base;
374 // Enable clock gating
375 intel_en_gating(info);
377 // Enable automatic gpu downclocking if we can to save power
378 intel_en_downclock(info);
380 // no errors, so keep areas and mappings
381 sharedCreator.Detach();
382 mmioMapper.Detach();
384 aperture_info apertureInfo;
385 gGART->get_aperture_info(info.aperture, &apertureInfo);
387 info.shared_info->registers_area = info.registers_area;
388 info.shared_info->graphics_memory = (uint8*)info.aperture_base;
389 info.shared_info->physical_graphics_memory = apertureInfo.physical_base;
390 info.shared_info->graphics_memory_size = apertureInfo.size;
391 info.shared_info->frame_buffer = 0;
392 info.shared_info->dpms_mode = B_DPMS_ON;
394 info.shared_info->got_vbt = get_lvds_mode_from_bios(
395 &info.shared_info->current_mode);
396 /* at least 855gm can't drive more than one head at time */
397 if (info.device_type.InFamily(INTEL_TYPE_8xx))
398 info.shared_info->single_head_locked = 1;
400 if (info.device_type.InFamily(INTEL_TYPE_9xx)) {
401 info.shared_info->pll_info.reference_frequency = 96000; // 96 kHz
402 info.shared_info->pll_info.max_frequency = 400000;
403 // 400 MHz RAM DAC speed
404 info.shared_info->pll_info.min_frequency = 20000; // 20 MHz
405 } else {
406 info.shared_info->pll_info.reference_frequency = 48000; // 48 kHz
407 info.shared_info->pll_info.max_frequency = 350000;
408 // 350 MHz RAM DAC speed
409 info.shared_info->pll_info.min_frequency = 25000; // 25 MHz
412 info.shared_info->pll_info.divisor_register = INTEL_DISPLAY_A_PLL_DIVISOR_0;
414 info.shared_info->device_type = info.device_type;
415 #ifdef __HAIKU__
416 strlcpy(info.shared_info->device_identifier, info.device_identifier,
417 sizeof(info.shared_info->device_identifier));
418 #else
419 strcpy(info.shared_info->device_identifier, info.device_identifier);
420 #endif
422 // setup overlay registers
424 status_t status = intel_allocate_memory(info, B_PAGE_SIZE, 0,
425 intel_uses_physical_overlay(*info.shared_info)
426 ? B_APERTURE_NEED_PHYSICAL : 0,
427 (addr_t*)&info.overlay_registers,
428 &info.shared_info->physical_overlay_registers);
429 if (status == B_OK) {
430 info.shared_info->overlay_offset = (addr_t)info.overlay_registers
431 - info.aperture_base;
432 init_overlay_registers(info.overlay_registers);
433 } else {
434 ERROR("error: could not allocate overlay memory! %s\n",
435 strerror(status));
438 // Allocate hardware status page and the cursor memory
440 if (intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL,
441 (addr_t*)info.shared_info->status_page,
442 &info.shared_info->physical_status_page) == B_OK) {
443 // TODO: set status page
445 if (hardwareCursor) {
446 intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL,
447 (addr_t*)&info.shared_info->cursor_memory,
448 &info.shared_info->physical_cursor_memory);
451 edid1_info* edidInfo = (edid1_info*)get_boot_item(VESA_EDID_BOOT_INFO,
452 NULL);
453 if (edidInfo != NULL) {
454 info.shared_info->has_vesa_edid_info = true;
455 memcpy(&info.shared_info->vesa_edid_info, edidInfo, sizeof(edid1_info));
458 init_interrupt_handler(info);
460 TRACE("%s: completed successfully!\n", __func__);
461 return B_OK;
465 void
466 intel_extreme_uninit(intel_info &info)
468 CALLED();
470 if (!info.fake_interrupts && info.shared_info->vblank_sem > 0) {
471 // disable interrupt generation
472 write16(info, find_reg(info, INTEL_INTERRUPT_ENABLED), 0);
473 write16(info, find_reg(info, INTEL_INTERRUPT_MASK), ~0);
475 remove_io_interrupt_handler(info.irq, intel_interrupt_handler, &info);
477 if (info.use_msi && gPCIx86Module != NULL) {
478 gPCIx86Module->disable_msi(info.pci->bus,
479 info.pci->device, info.pci->function);
480 gPCIx86Module->unconfigure_msi(info.pci->bus,
481 info.pci->device, info.pci->function);
485 gGART->unmap_aperture(info.aperture);
487 delete_area(info.registers_area);
488 delete_area(info.shared_area);