2 * Copyright 2006-2014, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Axel Dörfler, axeld@pinc-software.de
7 * Alexander von Gluck IV, kallisti5@unixzen.com
11 #include "intel_extreme.h"
13 #include "AreaKeeper.h"
19 #include <boot_item.h>
20 #include <driver_settings.h>
21 #include <util/kernel_cpp.h>
23 #include <vesa_info.h>
30 #define TRACE_INTELEXTREME
31 #ifdef TRACE_INTELEXTREME
32 # define TRACE(x...) dprintf("intel_extreme: " x)
37 #define ERROR(x...) dprintf("intel_extreme: " x)
38 #define CALLED(x...) TRACE("intel_extreme: CALLED %s\n", __PRETTY_FUNCTION__)
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
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
);
68 release_vblank_sem(intel_info
&info
)
71 if (get_sem_count(info
.shared_info
->vblank_sem
, &count
) == B_OK
73 release_sem_etc(info
.shared_info
->vblank_sem
, -count
,
75 return B_INVOKE_SCHEDULER
;
78 return B_HANDLED_INTERRUPT
;
82 /** Get the appropriate interrupt mask for enabling or testing interrupts on
85 * The bits to test or set are different depending on the hardware generation.
87 * \param info Intel_extreme driver information
88 * \param pipes bit mask of the pipes to use
89 * \param enable true to get the mask for enabling the interrupts, false to get
90 * the mask for testing them.
93 intel_get_interrupt_mask(intel_info
& info
, int pipes
, bool enable
)
96 bool hasPCH
= info
.pch_info
!= INTEL_PCH_NONE
;
98 // Intel changed the PCH register mapping between Sandy Bridge and the
99 // later generations (Ivy Bridge and up).
101 if ((pipes
& INTEL_PIPE_A
) != 0) {
102 if (info
.device_type
.InGroup(INTEL_GROUP_SNB
))
103 mask
|= PCH_INTERRUPT_VBLANK_PIPEA_SNB
;
105 mask
|= PCH_INTERRUPT_VBLANK_PIPEA
;
107 mask
|= INTERRUPT_VBLANK_PIPEA
;
110 if ((pipes
& INTEL_PIPE_B
) != 0) {
111 if (info
.device_type
.InGroup(INTEL_GROUP_SNB
))
112 mask
|= PCH_INTERRUPT_VBLANK_PIPEB_SNB
;
114 mask
|= PCH_INTERRUPT_VBLANK_PIPEB
;
116 mask
|= INTERRUPT_VBLANK_PIPEB
;
119 // On SandyBridge, there is an extra "global enable" flag, which must also
120 // be set when enabling the interrupts (but not when testing for them).
121 if (enable
&& info
.device_type
.InGroup(INTEL_GROUP_SNB
))
122 mask
|= PCH_INTERRUPT_GLOBAL_SNB
;
129 intel_interrupt_handler(void* data
)
131 intel_info
&info
= *(intel_info
*)data
;
132 uint32 reg
= find_reg(info
, INTEL_INTERRUPT_IDENTITY
);
133 bool hasPCH
= (info
.pch_info
!= INTEL_PCH_NONE
);
137 identity
= read32(info
, reg
);
139 identity
= read16(info
, reg
);
142 return B_UNHANDLED_INTERRUPT
;
144 int32 handled
= B_HANDLED_INTERRUPT
;
146 while (identity
!= 0) {
148 uint32 mask
= intel_get_interrupt_mask(info
, INTEL_PIPE_A
, false);
150 if ((identity
& mask
) != 0) {
151 handled
= release_vblank_sem(info
);
153 // make sure we'll get another one of those
154 write32(info
, INTEL_DISPLAY_A_PIPE_STATUS
,
155 DISPLAY_PIPE_VBLANK_STATUS
| DISPLAY_PIPE_VBLANK_ENABLED
);
158 mask
= intel_get_interrupt_mask(info
, INTEL_PIPE_B
, false);
159 if ((identity
& mask
) != 0) {
160 handled
= release_vblank_sem(info
);
162 // make sure we'll get another one of those
163 write32(info
, INTEL_DISPLAY_B_PIPE_STATUS
,
164 DISPLAY_PIPE_VBLANK_STATUS
| DISPLAY_PIPE_VBLANK_ENABLED
);
168 // FIXME we don't have supprot for the 3rd pipe yet
169 mask
= hasPCH
? PCH_INTERRUPT_VBLANK_PIPEC
171 if ((identity
& mask
) != 0) {
172 handled
= release_vblank_sem(info
);
174 // make sure we'll get another one of those
175 write32(info
, INTEL_DISPLAY_C_PIPE_STATUS
,
176 DISPLAY_PIPE_VBLANK_STATUS
| DISPLAY_PIPE_VBLANK_ENABLED
);
180 // setting the bit clears it!
182 write32(info
, reg
, identity
);
183 identity
= read32(info
, reg
);
185 write16(info
, reg
, identity
);
186 identity
= read16(info
, reg
);
195 init_interrupt_handler(intel_info
&info
)
197 info
.shared_info
->vblank_sem
= create_sem(0, "intel extreme vblank");
198 if (info
.shared_info
->vblank_sem
< B_OK
)
201 status_t status
= B_OK
;
203 // We need to change the owner of the sem to the calling team (usually the
204 // app_server), because userland apps cannot acquire kernel semaphores
205 thread_id thread
= find_thread(NULL
);
206 thread_info threadInfo
;
207 if (get_thread_info(thread
, &threadInfo
) != B_OK
208 || set_sem_owner(info
.shared_info
->vblank_sem
, threadInfo
.team
)
213 // Find the right interrupt vector, using MSIs if available.
215 info
.use_msi
= false;
216 if (info
.pci
->u
.h0
.interrupt_pin
!= 0x00)
217 info
.irq
= info
.pci
->u
.h0
.interrupt_line
;
218 if (gPCIx86Module
!= NULL
&& gPCIx86Module
->get_msi_count(info
.pci
->bus
,
219 info
.pci
->device
, info
.pci
->function
) >= 1) {
221 if (gPCIx86Module
->configure_msi(info
.pci
->bus
, info
.pci
->device
,
222 info
.pci
->function
, 1, &msiVector
) == B_OK
223 && gPCIx86Module
->enable_msi(info
.pci
->bus
, info
.pci
->device
,
224 info
.pci
->function
) == B_OK
) {
225 ERROR("using message signaled interrupts\n");
226 info
.irq
= msiVector
;
231 if (status
== B_OK
&& info
.irq
!= 0xff) {
232 // we've gotten an interrupt line for us to use
234 info
.fake_interrupts
= false;
236 status
= install_io_interrupt_handler(info
.irq
,
237 &intel_interrupt_handler
, (void*)&info
, 0);
238 if (status
== B_OK
) {
239 write32(info
, INTEL_DISPLAY_A_PIPE_STATUS
,
240 DISPLAY_PIPE_VBLANK_STATUS
| DISPLAY_PIPE_VBLANK_ENABLED
);
241 write32(info
, INTEL_DISPLAY_B_PIPE_STATUS
,
242 DISPLAY_PIPE_VBLANK_STATUS
| DISPLAY_PIPE_VBLANK_ENABLED
);
244 bool hasPCH
= (info
.pch_info
!= INTEL_PCH_NONE
);
246 uint32 enable
= intel_get_interrupt_mask(info
,
247 INTEL_PIPE_A
| INTEL_PIPE_B
, true);
250 // Clear all the interrupts
251 write32(info
, find_reg(info
, INTEL_INTERRUPT_IDENTITY
), ~0);
253 // enable interrupts - we only want VBLANK interrupts
254 write32(info
, find_reg(info
, INTEL_INTERRUPT_ENABLED
), enable
);
255 write32(info
, find_reg(info
, INTEL_INTERRUPT_MASK
), ~enable
);
257 // Clear all the interrupts
258 write16(info
, find_reg(info
, INTEL_INTERRUPT_IDENTITY
), ~0);
260 // enable interrupts - we only want VBLANK interrupts
261 write16(info
, find_reg(info
, INTEL_INTERRUPT_ENABLED
), enable
);
262 write16(info
, find_reg(info
, INTEL_INTERRUPT_MASK
), ~enable
);
268 // There is no interrupt reserved for us, or we couldn't install our
269 // interrupt handler, let's fake the vblank interrupt for our clients
270 // using a timer interrupt
271 info
.fake_interrupts
= true;
273 // TODO: fake interrupts!
274 TRACE("Fake interrupt mode (no PCI interrupt line assigned\n");
279 delete_sem(info
.shared_info
->vblank_sem
);
280 info
.shared_info
->vblank_sem
= B_ERROR
;
289 intel_free_memory(intel_info
&info
, addr_t base
)
291 return gGART
->free_memory(info
.aperture
, base
);
296 intel_allocate_memory(intel_info
&info
, size_t size
, size_t alignment
,
297 uint32 flags
, addr_t
* _base
, phys_addr_t
* _physicalBase
)
299 return gGART
->allocate_memory(info
.aperture
, size
, alignment
,
300 flags
, _base
, _physicalBase
);
305 intel_extreme_init(intel_info
&info
)
308 info
.aperture
= gGART
->map_aperture(info
.pci
->bus
, info
.pci
->device
,
309 info
.pci
->function
, 0, &info
.aperture_base
);
310 if (info
.aperture
< B_OK
) {
311 ERROR("error: could not map GART aperture! (%s)\n", strerror(info
.aperture
));
312 return info
.aperture
;
315 AreaKeeper sharedCreator
;
316 info
.shared_area
= sharedCreator
.Create("intel extreme shared info",
317 (void**)&info
.shared_info
, B_ANY_KERNEL_ADDRESS
,
318 ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info
)) + 3 * B_PAGE_SIZE
,
320 if (info
.shared_area
< B_OK
) {
321 ERROR("error: could not create shared area!\n");
322 gGART
->unmap_aperture(info
.aperture
);
323 return info
.shared_area
;
326 memset((void*)info
.shared_info
, 0, sizeof(intel_shared_info
));
330 if (info
.device_type
.Generation() >= 3) {
331 // For some reason Intel saw the need to change the order of the
332 // mappings with the introduction of the i9xx family
337 // evaluate driver settings, if any
340 read_settings(hardwareCursor
);
344 // TODO: registers are mapped twice (by us and intel_gart), maybe we
345 // can share it between the drivers
347 AreaKeeper mmioMapper
;
348 info
.registers_area
= mmioMapper
.Map("intel extreme mmio",
349 info
.pci
->u
.h0
.base_registers
[mmioIndex
],
350 info
.pci
->u
.h0
.base_register_sizes
[mmioIndex
],
351 B_ANY_KERNEL_ADDRESS
, B_KERNEL_READ_AREA
| B_KERNEL_WRITE_AREA
,
352 (void**)&info
.registers
);
353 if (mmioMapper
.InitCheck() < B_OK
) {
354 ERROR("error: could not map memory I/O!\n");
355 gGART
->unmap_aperture(info
.aperture
);
356 return info
.registers_area
;
359 bool hasPCH
= (info
.pch_info
!= INTEL_PCH_NONE
);
361 ERROR("Init Intel generation %d GPU %s PCH split.\n",
362 info
.device_type
.Generation(), hasPCH
? "with" : "without");
364 uint32
* blocks
= info
.shared_info
->register_blocks
;
365 blocks
[REGISTER_BLOCK(REGS_FLAT
)] = 0;
367 // setup the register blocks for the different architectures
369 // PCH based platforms (IronLake through ultra-low-power Broadwells)
370 blocks
[REGISTER_BLOCK(REGS_NORTH_SHARED
)]
371 = PCH_NORTH_SHARED_REGISTER_BASE
;
372 blocks
[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT
)]
373 = PCH_NORTH_PIPE_AND_PORT_REGISTER_BASE
;
374 blocks
[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL
)]
375 = PCH_NORTH_PLANE_CONTROL_REGISTER_BASE
;
376 blocks
[REGISTER_BLOCK(REGS_SOUTH_SHARED
)]
377 = PCH_SOUTH_SHARED_REGISTER_BASE
;
378 blocks
[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT
)]
379 = PCH_SOUTH_TRANSCODER_AND_PORT_REGISTER_BASE
;
381 // (G)MCH/ICH based platforms
382 blocks
[REGISTER_BLOCK(REGS_NORTH_SHARED
)]
383 = MCH_SHARED_REGISTER_BASE
;
384 blocks
[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT
)]
385 = MCH_PIPE_AND_PORT_REGISTER_BASE
;
386 blocks
[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL
)]
387 = MCH_PLANE_CONTROL_REGISTER_BASE
;
388 blocks
[REGISTER_BLOCK(REGS_SOUTH_SHARED
)]
389 = ICH_SHARED_REGISTER_BASE
;
390 blocks
[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT
)]
391 = ICH_PORT_REGISTER_BASE
;
394 // Everything in the display PRM gets +0x180000
395 if (info
.device_type
.InGroup(INTEL_GROUP_VLV
)) {
396 // "I nearly got violent with the hw guys when they told me..."
397 blocks
[REGISTER_BLOCK(REGS_SOUTH_SHARED
)] += VLV_DISPLAY_BASE
;
398 blocks
[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT
)] += VLV_DISPLAY_BASE
;
401 TRACE("REGS_NORTH_SHARED: 0x%" B_PRIx32
"\n",
402 blocks
[REGISTER_BLOCK(REGS_NORTH_SHARED
)]);
403 TRACE("REGS_NORTH_PIPE_AND_PORT: 0x%" B_PRIx32
"\n",
404 blocks
[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT
)]);
405 TRACE("REGS_NORTH_PLANE_CONTROL: 0x%" B_PRIx32
"\n",
406 blocks
[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL
)]);
407 TRACE("REGS_SOUTH_SHARED: 0x%" B_PRIx32
"\n",
408 blocks
[REGISTER_BLOCK(REGS_SOUTH_SHARED
)]);
409 TRACE("REGS_SOUTH_TRANSCODER_PORT: 0x%" B_PRIx32
"\n",
410 blocks
[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT
)]);
412 // make sure bus master, memory-mapped I/O, and frame buffer is enabled
413 set_pci_config(info
.pci
, PCI_command
, 2, get_pci_config(info
.pci
,
414 PCI_command
, 2) | PCI_command_io
| PCI_command_memory
415 | PCI_command_master
);
417 // reserve ring buffer memory (currently, this memory is placed in
418 // the graphics memory), but this could bring us problems with
419 // write combining...
421 ring_buffer
&primary
= info
.shared_info
->primary_ring_buffer
;
422 if (intel_allocate_memory(info
, 16 * B_PAGE_SIZE
, 0, 0,
423 (addr_t
*)&primary
.base
) == B_OK
) {
424 primary
.register_base
= INTEL_PRIMARY_RING_BUFFER
;
425 primary
.size
= 16 * B_PAGE_SIZE
;
426 primary
.offset
= (addr_t
)primary
.base
- info
.aperture_base
;
429 // Enable clock gating
430 intel_en_gating(info
);
432 // Enable automatic gpu downclocking if we can to save power
433 intel_en_downclock(info
);
435 // no errors, so keep areas and mappings
436 sharedCreator
.Detach();
439 aperture_info apertureInfo
;
440 gGART
->get_aperture_info(info
.aperture
, &apertureInfo
);
442 info
.shared_info
->registers_area
= info
.registers_area
;
443 info
.shared_info
->graphics_memory
= (uint8
*)info
.aperture_base
;
444 info
.shared_info
->physical_graphics_memory
= apertureInfo
.physical_base
;
445 info
.shared_info
->graphics_memory_size
= apertureInfo
.size
;
446 info
.shared_info
->frame_buffer
= 0;
447 info
.shared_info
->dpms_mode
= B_DPMS_ON
;
449 // Pull VBIOS panel mode for later use
450 info
.shared_info
->got_vbt
= get_lvds_mode_from_bios(
451 &info
.shared_info
->panel_mode
);
453 /* at least 855gm can't drive more than one head at time */
454 if (info
.device_type
.InFamily(INTEL_FAMILY_8xx
))
455 info
.shared_info
->single_head_locked
= 1;
457 if (info
.device_type
.InFamily(INTEL_FAMILY_9xx
)
458 | info
.device_type
.InFamily(INTEL_FAMILY_SER5
)) {
459 info
.shared_info
->pll_info
.reference_frequency
= 96000; // 96 kHz
460 info
.shared_info
->pll_info
.max_frequency
= 400000;
461 // 400 MHz RAM DAC speed
462 info
.shared_info
->pll_info
.min_frequency
= 20000; // 20 MHz
464 info
.shared_info
->pll_info
.reference_frequency
= 48000; // 48 kHz
465 info
.shared_info
->pll_info
.max_frequency
= 350000;
466 // 350 MHz RAM DAC speed
467 info
.shared_info
->pll_info
.min_frequency
= 25000; // 25 MHz
470 info
.shared_info
->pll_info
.divisor_register
= INTEL_DISPLAY_A_PLL_DIVISOR_0
;
472 info
.shared_info
->pch_info
= info
.pch_info
;
474 info
.shared_info
->device_type
= info
.device_type
;
476 strlcpy(info
.shared_info
->device_identifier
, info
.device_identifier
,
477 sizeof(info
.shared_info
->device_identifier
));
479 strcpy(info
.shared_info
->device_identifier
, info
.device_identifier
);
482 // setup overlay registers
484 status_t status
= intel_allocate_memory(info
, B_PAGE_SIZE
, 0,
485 intel_uses_physical_overlay(*info
.shared_info
)
486 ? B_APERTURE_NEED_PHYSICAL
: 0,
487 (addr_t
*)&info
.overlay_registers
,
488 &info
.shared_info
->physical_overlay_registers
);
489 if (status
== B_OK
) {
490 info
.shared_info
->overlay_offset
= (addr_t
)info
.overlay_registers
491 - info
.aperture_base
;
492 init_overlay_registers(info
.overlay_registers
);
494 ERROR("error: could not allocate overlay memory! %s\n",
498 // Allocate hardware status page and the cursor memory
500 if (intel_allocate_memory(info
, B_PAGE_SIZE
, 0, B_APERTURE_NEED_PHYSICAL
,
501 (addr_t
*)info
.shared_info
->status_page
,
502 &info
.shared_info
->physical_status_page
) == B_OK
) {
503 // TODO: set status page
505 if (hardwareCursor
) {
506 intel_allocate_memory(info
, B_PAGE_SIZE
, 0, B_APERTURE_NEED_PHYSICAL
,
507 (addr_t
*)&info
.shared_info
->cursor_memory
,
508 &info
.shared_info
->physical_cursor_memory
);
511 edid1_info
* edidInfo
= (edid1_info
*)get_boot_item(VESA_EDID_BOOT_INFO
,
513 if (edidInfo
!= NULL
) {
514 info
.shared_info
->has_vesa_edid_info
= true;
515 memcpy(&info
.shared_info
->vesa_edid_info
, edidInfo
, sizeof(edid1_info
));
518 init_interrupt_handler(info
);
521 if (info
.device_type
.Generation() == 5) {
522 info
.shared_info
->fdi_link_frequency
= (read32(info
, FDI_PLL_BIOS_0
)
523 & FDI_PLL_FB_CLOCK_MASK
) + 2;
524 info
.shared_info
->fdi_link_frequency
*= 100;
526 info
.shared_info
->fdi_link_frequency
= 2700;
529 info
.shared_info
->fdi_link_frequency
= 0;
532 TRACE("%s: completed successfully!\n", __func__
);
538 intel_extreme_uninit(intel_info
&info
)
542 if (!info
.fake_interrupts
&& info
.shared_info
->vblank_sem
> 0) {
543 bool hasPCH
= (info
.pch_info
!= INTEL_PCH_NONE
);
545 // disable interrupt generation
547 write32(info
, find_reg(info
, INTEL_INTERRUPT_ENABLED
), 0);
548 write32(info
, find_reg(info
, INTEL_INTERRUPT_MASK
), ~0);
550 write16(info
, find_reg(info
, INTEL_INTERRUPT_ENABLED
), 0);
551 write16(info
, find_reg(info
, INTEL_INTERRUPT_MASK
), ~0);
554 remove_io_interrupt_handler(info
.irq
, intel_interrupt_handler
, &info
);
556 if (info
.use_msi
&& gPCIx86Module
!= NULL
) {
557 gPCIx86Module
->disable_msi(info
.pci
->bus
,
558 info
.pci
->device
, info
.pci
->function
);
559 gPCIx86Module
->unconfigure_msi(info
.pci
->bus
,
560 info
.pci
->device
, info
.pci
->function
);
564 gGART
->unmap_aperture(info
.aperture
);
566 delete_area(info
.registers_area
);
567 delete_area(info
.shared_area
);