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
;
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
);
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();
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
);
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
);
143 // FIXME we don't have supprot for the 3rd pipe yet
144 mask
= hasPCH
? PCH_INTERRUPT_VBLANK_PIPEC
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
);
156 // setting the bit clears it!
157 write16(info
, reg
, identity
);
158 identity
= read16(info
, reg
);
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
)
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
)
184 // Find the right interrupt vector, using MSIs if available.
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) {
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
;
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
);
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");
239 delete_sem(info
.shared_info
->vblank_sem
);
240 info
.shared_info
->vblank_sem
= B_ERROR
;
249 intel_free_memory(intel_info
&info
, addr_t base
)
251 return gGART
->free_memory(info
.aperture
, base
);
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
);
265 intel_extreme_init(intel_info
&info
)
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
,
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
));
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
297 // evaluate driver settings, if any
300 read_settings(hardwareCursor
);
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
;
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();
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
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
;
416 strlcpy(info
.shared_info
->device_identifier
, info
.device_identifier
,
417 sizeof(info
.shared_info
->device_identifier
));
419 strcpy(info
.shared_info
->device_identifier
, info
.device_identifier
);
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
);
434 ERROR("error: could not allocate overlay memory! %s\n",
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
,
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__
);
466 intel_extreme_uninit(intel_info
&info
)
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
);