xref: /haiku/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp (revision 9f3bdf3d039430b5172c424def20ce5d9f7367d4)
1 /*
2  * Copyright 2006-2018, 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  *		Alexander von Gluck IV, kallisti5@unixzen.com
8  *		Adrien Destugues, pulkomandy@pulkomandy.tk
9  */
10 
11 
12 #include "intel_extreme.h"
13 
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <errno.h>
18 
19 #include <AreaKeeper.h>
20 #include <boot_item.h>
21 #include <driver_settings.h>
22 #include <util/kernel_cpp.h>
23 
24 #include <vesa_info.h>
25 
26 #include "driver.h"
27 #include "power.h"
28 #include "utility.h"
29 
30 
31 #define TRACE_INTELEXTREME
32 #ifdef TRACE_INTELEXTREME
33 #	define TRACE(x...) dprintf("intel_extreme: " x)
34 #else
35 #	define TRACE(x) ;
36 #endif
37 
38 #define ERROR(x...) dprintf("intel_extreme: " x)
39 #define CALLED(x...) TRACE("intel_extreme: CALLED %s\n", __PRETTY_FUNCTION__)
40 
41 
42 static void
43 init_overlay_registers(overlay_registers* _registers)
44 {
45 	user_memset(_registers, 0, B_PAGE_SIZE);
46 
47 	overlay_registers registers;
48 	memset(&registers, 0, sizeof(registers));
49 	registers.contrast_correction = 0x48;
50 	registers.saturation_cos_correction = 0x9a;
51 		// this by-passes contrast and saturation correction
52 
53 	user_memcpy(_registers, &registers, sizeof(overlay_registers));
54 }
55 
56 
57 static void
58 read_settings(bool &hardwareCursor)
59 {
60 	hardwareCursor = false;
61 
62 	void* settings = load_driver_settings("intel_extreme");
63 	if (settings != NULL) {
64 		hardwareCursor = get_driver_boolean_parameter(settings,
65 			"hardware_cursor", true, true);
66 
67 		unload_driver_settings(settings);
68 	}
69 }
70 
71 
72 static int32
73 release_vblank_sem(intel_info &info)
74 {
75 	int32 count;
76 	if (get_sem_count(info.shared_info->vblank_sem, &count) == B_OK
77 		&& count < 0) {
78 		release_sem_etc(info.shared_info->vblank_sem, -count,
79 			B_DO_NOT_RESCHEDULE);
80 		return B_INVOKE_SCHEDULER;
81 	}
82 
83 	return B_HANDLED_INTERRUPT;
84 }
85 
86 
87 static void
88 gen8_enable_interrupts(intel_info& info, pipe_index pipe, bool enable)
89 {
90 	ASSERT(pipe != INTEL_PIPE_ANY);
91 	ASSERT(info.device_type.Generation() >= 12 || pipe != INTEL_PIPE_D);
92 
93 	const uint32 regMask = PCH_INTERRUPT_PIPE_MASK_BDW(pipe);
94 	const uint32 regEnabled = PCH_INTERRUPT_PIPE_ENABLED_BDW(pipe);
95 	const uint32 regIdentity = PCH_INTERRUPT_PIPE_IDENTITY_BDW(pipe);
96 	const uint32 value = enable ? PCH_INTERRUPT_VBLANK_BDW : 0;
97 	write32(info, regIdentity, ~0);
98 	write32(info, regEnabled, value);
99 	write32(info, regMask, ~value);
100 }
101 
102 
103 static uint32
104 gen11_enable_global_interrupts(intel_info& info, bool enable)
105 {
106 	write32(info, GEN11_GFX_MSTR_IRQ, enable ? GEN11_MASTER_IRQ : 0);
107 	return enable ? 0 : read32(info, GEN11_GFX_MSTR_IRQ);
108 }
109 
110 
111 static uint32
112 gen8_enable_global_interrupts(intel_info& info, bool enable)
113 {
114 	write32(info, PCH_MASTER_INT_CTL_BDW, enable ? PCH_MASTER_INT_CTL_GLOBAL_BDW : 0);
115 	return enable ? 0 : read32(info, PCH_MASTER_INT_CTL_BDW);
116 }
117 
118 
119 /*!
120 	Checks interrupt status with provided master interrupt control register.
121 	For Gen8 to Gen11.
122 */
123 static int32
124 gen8_handle_interrupts(intel_info& info, uint32 interrupt)
125 {
126 	int32 handled = B_HANDLED_INTERRUPT;
127 	if ((interrupt & PCH_MASTER_INT_CTL_PIPE_PENDING_BDW(INTEL_PIPE_A)) != 0) {
128 		const uint32 regIdentity = PCH_INTERRUPT_PIPE_IDENTITY_BDW(INTEL_PIPE_A);
129 		uint32 identity = read32(info, regIdentity);
130 		if ((identity & PCH_INTERRUPT_VBLANK_BDW) != 0) {
131 			handled = release_vblank_sem(info);
132 			write32(info, regIdentity, identity | PCH_INTERRUPT_VBLANK_BDW);
133 		} else {
134 			dprintf("gen8_handle_interrupts unhandled interrupt on pipe A\n");
135 		}
136 		interrupt &= ~PCH_MASTER_INT_CTL_PIPE_PENDING_BDW(INTEL_PIPE_A);
137 	}
138 	if ((interrupt & PCH_MASTER_INT_CTL_PIPE_PENDING_BDW(INTEL_PIPE_B)) != 0) {
139 		const uint32 regIdentity = PCH_INTERRUPT_PIPE_IDENTITY_BDW(INTEL_PIPE_B);
140 		uint32 identity = read32(info, regIdentity);
141 		if ((identity & PCH_INTERRUPT_VBLANK_BDW) != 0) {
142 			handled = release_vblank_sem(info);
143 			write32(info, regIdentity, identity | PCH_INTERRUPT_VBLANK_BDW);
144 		} else {
145 			dprintf("gen8_handle_interrupts unhandled interrupt on pipe B\n");
146 		}
147 		interrupt &= ~PCH_MASTER_INT_CTL_PIPE_PENDING_BDW(INTEL_PIPE_B);
148 	}
149 	if ((interrupt & PCH_MASTER_INT_CTL_PIPE_PENDING_BDW(INTEL_PIPE_C)) != 0) {
150 		const uint32 regIdentity = PCH_INTERRUPT_PIPE_IDENTITY_BDW(INTEL_PIPE_C);
151 		uint32 identity = read32(info, regIdentity);
152 		if ((identity & PCH_INTERRUPT_VBLANK_BDW) != 0) {
153 			handled = release_vblank_sem(info);
154 			write32(info, regIdentity, identity | PCH_INTERRUPT_VBLANK_BDW);
155 		} else {
156 			dprintf("gen8_handle_interrupts unhandled interrupt on pipe C\n");
157 		}
158 		interrupt &= ~PCH_MASTER_INT_CTL_PIPE_PENDING_BDW(INTEL_PIPE_C);
159 	}
160 
161 	if ((interrupt & GEN8_DE_PORT_IRQ) != 0) {
162 		uint32 iir = read32(info, GEN8_DE_PORT_IIR);
163 		if (iir != 0) {
164 			write32(info, GEN8_DE_PORT_IIR, iir);
165 		}
166 		interrupt &= ~GEN8_DE_PORT_IRQ;
167 	}
168 
169 	if (info.device_type.Generation() >= 11 && (interrupt & GEN11_DE_HPD_IRQ) != 0) {
170 		dprintf("gen8_handle_interrupts HPD\n");
171 		uint32 iir = read32(info, GEN11_DE_HPD_IIR);
172 		if (iir != 0) {
173 			dprintf("gen8_handle_interrupts HPD_IIR %" B_PRIx32 "\n", iir);
174 			write32(info, GEN11_DE_HPD_IIR, iir);
175 		}
176 		interrupt &= ~GEN11_DE_HPD_IRQ;
177 	}
178 
179 	if ((interrupt & GEN8_DE_PCH_IRQ) != 0) {
180 		dprintf("gen8_handle_interrupts PCH\n");
181 		uint32 iir = read32(info, SDEIIR);
182 		if (iir != 0) {
183 			dprintf("gen8_handle_interrupts PCH_IIR %" B_PRIx32 "\n", iir);
184 			write32(info, SDEIIR, iir);
185 			if (info.shared_info->pch_info >= INTEL_PCH_ICP) {
186 				uint32 ddiHotplug = read32(info, SHOTPLUG_CTL_DDI);
187 				write32(info, SHOTPLUG_CTL_DDI, ddiHotplug);
188 				dprintf("gen8_handle_interrupts PCH_IIR ddiHotplug %" B_PRIx32 "\n", ddiHotplug);
189 
190 				uint32 tcHotplug = read32(info, SHOTPLUG_CTL_TC);
191 				write32(info, SHOTPLUG_CTL_TC, tcHotplug);
192 				dprintf("gen8_handle_interrupts PCH_IIR tcHotplug %" B_PRIx32 "\n", tcHotplug);
193 			}
194 		}
195 		interrupt &= ~GEN8_DE_PCH_IRQ;
196 	}
197 
198 	interrupt &= ~PCH_MASTER_INT_CTL_GLOBAL_BDW;
199 	if (interrupt != 0)
200 		dprintf("gen8_handle_interrupts unhandled %" B_PRIx32 "\n", interrupt);
201 	return handled;
202 }
203 
204 
205 
206 /** Get the appropriate interrupt mask for enabling or testing interrupts on
207  * the given pipe.
208  *
209  * The bits to test or set are different depending on the hardware generation.
210  *
211  * \param info Intel_extreme driver information
212  * \param pipe pipe to use
213  * \param enable true to get the mask for enabling the interrupts, false to get
214  *               the mask for testing them.
215  */
216 static uint32
217 intel_get_interrupt_mask(intel_info& info, pipe_index pipe, bool enable)
218 {
219 	uint32 mask = 0;
220 	bool hasPCH = info.pch_info != INTEL_PCH_NONE;
221 
222 	// Intel changed the PCH register mapping between Sandy Bridge and the
223 	// later generations (Ivy Bridge and up).
224 	// The PCH register itself does not exist in pre-PCH platforms, and the
225 	// previous interrupt register of course also had a different mapping.
226 
227 	if (pipe == INTEL_PIPE_A) {
228 		if (info.device_type.InGroup(INTEL_GROUP_SNB)
229 				|| info.device_type.InGroup(INTEL_GROUP_ILK))
230 			mask |= PCH_INTERRUPT_VBLANK_PIPEA_SNB;
231 		else if (hasPCH)
232 			mask |= PCH_INTERRUPT_VBLANK_PIPEA;
233 		else
234 			mask |= INTERRUPT_VBLANK_PIPEA;
235 	}
236 
237 	if (pipe == INTEL_PIPE_B) {
238 		if (info.device_type.InGroup(INTEL_GROUP_SNB)
239 				|| info.device_type.InGroup(INTEL_GROUP_ILK))
240 			mask |= PCH_INTERRUPT_VBLANK_PIPEB_SNB;
241 		else if (hasPCH)
242 			mask |= PCH_INTERRUPT_VBLANK_PIPEB;
243 		else
244 			mask |= INTERRUPT_VBLANK_PIPEB;
245 	}
246 
247 #if 0 // FIXME enable when we support the 3rd pipe
248 	if (pipe == INTEL_PIPE_C) {
249 		// Older generations only had two pipes
250 		if (hasPCH && info.device_type.Generation() > 6)
251 			mask |= PCH_INTERRUPT_VBLANK_PIPEC;
252 	}
253 #endif
254 
255 	// On SandyBridge, there is an extra "global enable" flag, which must also
256 	// be set when enabling the interrupts (but not when testing for them).
257 	if (enable && info.device_type.InFamily(INTEL_FAMILY_SER5))
258 		mask |= PCH_INTERRUPT_GLOBAL_SNB;
259 
260 	return mask;
261 }
262 
263 
264 static void
265 intel_enable_interrupts(intel_info& info, pipes which, bool enable)
266 {
267 	uint32 finalMask = 0;
268 	const uint32 pipeAMask = intel_get_interrupt_mask(info, INTEL_PIPE_A, true);
269 	const uint32 pipeBMask = intel_get_interrupt_mask(info, INTEL_PIPE_B, true);
270 	if (which.HasPipe(INTEL_PIPE_A))
271 		finalMask |= pipeAMask;
272 	if (which.HasPipe(INTEL_PIPE_B))
273 		finalMask |= pipeBMask;
274 
275 	const uint32 value = enable ? finalMask : 0;
276 
277 	// Clear all the interrupts
278 	write32(info, find_reg(info, INTEL_INTERRUPT_IDENTITY), ~0);
279 
280 	// enable interrupts - we only want VBLANK interrupts
281 	write32(info, find_reg(info, INTEL_INTERRUPT_ENABLED), value);
282 	write32(info, find_reg(info, INTEL_INTERRUPT_MASK), ~value);
283 }
284 
285 
286 static bool
287 intel_check_interrupt(intel_info& info, pipes& which)
288 {
289 	which.ClearPipe(INTEL_PIPE_ANY);
290 	const uint32 pipeAMask = intel_get_interrupt_mask(info, INTEL_PIPE_A, false);
291 	const uint32 pipeBMask = intel_get_interrupt_mask(info, INTEL_PIPE_B, false);
292 	const uint32 regIdentity = find_reg(info, INTEL_INTERRUPT_IDENTITY);
293 	const uint32 interrupt = read32(info, regIdentity);
294 	if ((interrupt & pipeAMask) != 0)
295 		which.SetPipe(INTEL_PIPE_A);
296 	if ((interrupt & pipeBMask) != 0)
297 		which.SetPipe(INTEL_PIPE_B);
298 	return which.HasPipe(INTEL_PIPE_ANY);
299 }
300 
301 
302 static void
303 g35_clear_interrupt_status(intel_info& info, pipe_index pipe)
304 {
305 	// These registers do not exist on later GPUs.
306 	if (info.device_type.Generation() > 4)
307 		return;
308 
309 	const uint32 value = DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED;
310 	switch (pipe) {
311 		case INTEL_PIPE_A:
312 			write32(info, INTEL_DISPLAY_A_PIPE_STATUS, value);
313 			break;
314 		case INTEL_PIPE_B:
315 			write32(info, INTEL_DISPLAY_B_PIPE_STATUS, value);
316 			break;
317 		default:
318 			break;
319 	}
320 }
321 
322 
323 static void
324 intel_clear_pipe_interrupt(intel_info& info, pipe_index pipe)
325 {
326 	// On G35/G45, prior to clearing Display Pipe interrupt in IIR
327 	// the corresponding interrupt status must first be cleared.
328 	g35_clear_interrupt_status(info, pipe);
329 
330 	const uint32 regIdentity = find_reg(info, INTEL_INTERRUPT_IDENTITY);
331 	const uint32 bit = intel_get_interrupt_mask(info, pipe, false);
332 	const uint32 identity = read32(info, regIdentity);
333 	write32(info, regIdentity, identity | bit);
334 }
335 
336 
337 /*!
338 	Interrupt routine for Gen8 and Gen9.
339 	See Gen12 Display Engine: Interrupt Service Routine chapter.
340 */
341 static int32
342 gen8_interrupt_handler(void* data)
343 {
344 	intel_info& info = *(intel_info*)data;
345 
346 	uint32 interrupt = gen8_enable_global_interrupts(info, false);
347 	if (interrupt == 0) {
348 		gen8_enable_global_interrupts(info, true);
349 		return B_UNHANDLED_INTERRUPT;
350 	}
351 
352 	int32 handled = gen8_handle_interrupts(info, interrupt);
353 
354 	gen8_enable_global_interrupts(info, true);
355 	return handled;
356 }
357 
358 
359 /*!
360 	Interrupt routine for Gen11.
361 	See Gen12 Display Engine: Interrupt Service Routine chapter.
362 */
363 static int32
364 gen11_interrupt_handler(void* data)
365 {
366 	intel_info& info = *(intel_info*)data;
367 
368 	uint32 interrupt = gen11_enable_global_interrupts(info, false);
369 
370 	if (interrupt == 0) {
371 		gen11_enable_global_interrupts(info, true);
372 		return B_UNHANDLED_INTERRUPT;
373 	}
374 
375 	int32 handled = B_HANDLED_INTERRUPT;
376 	if ((interrupt & GEN11_DISPLAY_IRQ) != 0)
377 		handled = gen8_handle_interrupts(info, read32(info, GEN11_DISPLAY_INT_CTL));
378 
379 	gen11_enable_global_interrupts(info, true);
380 	return handled;
381 }
382 
383 
384 static int32
385 intel_interrupt_handler(void* data)
386 {
387 	intel_info &info = *(intel_info*)data;
388 
389 	pipes which;
390 	bool shouldHandle = intel_check_interrupt(info, which);
391 
392 	if (!shouldHandle)
393 		return B_UNHANDLED_INTERRUPT;
394 
395 	int32 handled = B_HANDLED_INTERRUPT;
396 
397 	while (shouldHandle) {
398 		if (which.HasPipe(INTEL_PIPE_A)) {
399 			handled = release_vblank_sem(info);
400 
401 			intel_clear_pipe_interrupt(info, INTEL_PIPE_A);
402 		}
403 
404 		if (which.HasPipe(INTEL_PIPE_B)) {
405 			handled = release_vblank_sem(info);
406 
407 			intel_clear_pipe_interrupt(info, INTEL_PIPE_B);
408 		}
409 
410 #if 0
411 		// FIXME we don't have support for the 3rd pipe yet
412 		if (which.HasPipe(INTEL_PIPE_C)) {
413 			handled = release_vblank_sem(info);
414 
415 			intel_clear_pipe_interrupt(info, INTEL_PIPE_C);
416 		}
417 #endif
418 
419 		shouldHandle = intel_check_interrupt(info, which);
420 	}
421 
422 	return handled;
423 }
424 
425 
426 static void
427 init_interrupt_handler(intel_info &info)
428 {
429 	info.shared_info->vblank_sem = create_sem(0, "intel extreme vblank");
430 	if (info.shared_info->vblank_sem < B_OK)
431 		return;
432 
433 	status_t status = B_OK;
434 
435 	// We need to change the owner of the sem to the calling team (usually the
436 	// app_server), because userland apps cannot acquire kernel semaphores
437 	thread_id thread = find_thread(NULL);
438 	thread_info threadInfo;
439 	if (get_thread_info(thread, &threadInfo) != B_OK
440 		|| set_sem_owner(info.shared_info->vblank_sem, threadInfo.team)
441 			!= B_OK) {
442 		status = B_ERROR;
443 	}
444 
445 	// Find the right interrupt vector, using MSIs if available.
446 	info.irq = 0;
447 	info.use_msi = false;
448 	if (info.pci->u.h0.interrupt_pin != 0x00) {
449 		info.irq = info.pci->u.h0.interrupt_line;
450 		if (info.irq == 0xff)
451 			info.irq = 0;
452 	}
453 	if (gPCI->get_msi_count(info.pci->bus,
454 			info.pci->device, info.pci->function) >= 1) {
455 		uint32 msiVector = 0;
456 		if (gPCI->configure_msi(info.pci->bus, info.pci->device,
457 				info.pci->function, 1, &msiVector) == B_OK
458 			&& gPCI->enable_msi(info.pci->bus, info.pci->device,
459 				info.pci->function) == B_OK) {
460 			TRACE("using message signaled interrupts\n");
461 			info.irq = msiVector;
462 			info.use_msi = true;
463 		}
464 	}
465 
466 	if (status == B_OK && info.irq != 0) {
467 		// we've gotten an interrupt line for us to use
468 
469 		info.fake_interrupts = false;
470 
471 		if (info.device_type.Generation() >= 8) {
472 			interrupt_handler handler = &gen8_interrupt_handler;
473 			if (info.device_type.Generation() >= 11)
474 				handler = &gen11_interrupt_handler;
475 			status = install_io_interrupt_handler(info.irq,
476 				handler, (void*)&info, 0);
477 			if (status == B_OK) {
478 				gen8_enable_interrupts(info, INTEL_PIPE_A, true);
479 				gen8_enable_interrupts(info, INTEL_PIPE_B, true);
480 				if (info.device_type.Generation() >= 11)
481 					gen8_enable_interrupts(info, INTEL_PIPE_C, true);
482 				gen8_enable_global_interrupts(info, true);
483 
484 				if (info.device_type.Generation() >= 11) {
485 					if (info.shared_info->pch_info >= INTEL_PCH_ICP) {
486 						read32(info, SDEIIR);
487 						write32(info, SDEIER, 0xffffffff);
488 						write32(info, SDEIMR, ~SDE_GMBUS_ICP);
489 						read32(info, SDEIMR);
490 					}
491 
492 					uint32 mask = GEN8_AUX_CHANNEL_A;
493 					mask |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C | GEN9_AUX_CHANNEL_D;
494 					mask |= CNL_AUX_CHANNEL_F;
495 					mask |= ICL_AUX_CHANNEL_E;
496 					read32(info, GEN8_DE_PORT_IIR);
497 					write32(info, GEN8_DE_PORT_IER, mask);
498 					write32(info, GEN8_DE_PORT_IMR, ~mask);
499 					read32(info, GEN8_DE_PORT_IMR);
500 
501 					read32(info, GEN8_DE_MISC_IIR);
502 					write32(info, GEN8_DE_MISC_IER, GEN8_DE_EDP_PSR);
503 					write32(info, GEN8_DE_MISC_IMR, ~GEN8_DE_EDP_PSR);
504 					read32(info, GEN8_DE_MISC_IMR);
505 
506 					read32(info, GEN11_GU_MISC_IIR);
507 					write32(info, GEN11_GU_MISC_IER, GEN11_GU_MISC_GSE);
508 					write32(info, GEN11_GU_MISC_IMR, ~GEN11_GU_MISC_GSE);
509 					read32(info, GEN11_GU_MISC_IMR);
510 
511 					read32(info, GEN11_DE_HPD_IIR);
512 					write32(info, GEN11_DE_HPD_IER,
513 						GEN11_DE_TC_HOTPLUG_MASK | GEN11_DE_TBT_HOTPLUG_MASK);
514 					write32(info, GEN11_DE_HPD_IMR, 0xffffffff);
515 					read32(info, GEN11_DE_HPD_IMR);
516 
517 					write32(info, GEN11_TC_HOTPLUG_CTL, 0);
518 					write32(info, GEN11_TBT_HOTPLUG_CTL, 0);
519 
520 					if (info.shared_info->pch_info >= INTEL_PCH_ICP) {
521 						if (info.shared_info->pch_info <= INTEL_PCH_TGP)
522 							write32(info, SHPD_FILTER_CNT, SHPD_FILTER_CNT_500_ADJ);
523 						read32(info, SDEIMR);
524 						write32(info, SDEIMR, 0x3f023f07);
525 						read32(info, SDEIMR);
526 
527 						uint32 ctl = read32(info, SHOTPLUG_CTL_DDI);
528 						// we enable everything, should come from the VBT
529 						ctl |= SHOTPLUG_CTL_DDI_HPD_ENABLE(HPD_PORT_A)
530 							| SHOTPLUG_CTL_DDI_HPD_ENABLE(HPD_PORT_B)
531 							| SHOTPLUG_CTL_DDI_HPD_ENABLE(HPD_PORT_C)
532 							| SHOTPLUG_CTL_DDI_HPD_ENABLE(HPD_PORT_D);
533 						write32(info, SHOTPLUG_CTL_DDI, ctl);
534 						ctl = read32(info, SHOTPLUG_CTL_TC);
535 						// we enable everything, should come from the VBT
536 						ctl |= SHOTPLUG_CTL_TC_HPD_ENABLE(HPD_PORT_TC1)
537 							| SHOTPLUG_CTL_TC_HPD_ENABLE(HPD_PORT_TC2)
538 							| SHOTPLUG_CTL_TC_HPD_ENABLE(HPD_PORT_TC3)
539 							| SHOTPLUG_CTL_TC_HPD_ENABLE(HPD_PORT_TC4)
540 							| SHOTPLUG_CTL_TC_HPD_ENABLE(HPD_PORT_TC5)
541 							| SHOTPLUG_CTL_TC_HPD_ENABLE(HPD_PORT_TC6);
542 						write32(info, SHOTPLUG_CTL_TC, ctl);
543 					}
544 
545 					gen11_enable_global_interrupts(info, true);
546 				}
547 			}
548 		} else {
549 			status = install_io_interrupt_handler(info.irq,
550 				&intel_interrupt_handler, (void*)&info, 0);
551 			if (status == B_OK) {
552 				g35_clear_interrupt_status(info, INTEL_PIPE_A);
553 				g35_clear_interrupt_status(info, INTEL_PIPE_B);
554 
555 				pipes which;
556 				which.SetPipe(INTEL_PIPE_A);
557 				which.SetPipe(INTEL_PIPE_B);
558 				intel_enable_interrupts(info, which, true);
559 			}
560 		}
561 	}
562 	if (status < B_OK) {
563 		// There is no interrupt reserved for us, or we couldn't install our
564 		// interrupt handler, let's fake the vblank interrupt for our clients
565 		// using a timer interrupt
566 		info.fake_interrupts = true;
567 
568 		// TODO: fake interrupts!
569 		ERROR("Fake interrupt mode (no PCI interrupt line assigned\n");
570 		status = B_ERROR;
571 	}
572 
573 	if (status < B_OK) {
574 		delete_sem(info.shared_info->vblank_sem);
575 		info.shared_info->vblank_sem = B_ERROR;
576 	}
577 }
578 
579 
580 //	#pragma mark -
581 
582 
583 status_t
584 intel_free_memory(intel_info &info, addr_t base)
585 {
586 	return gGART->free_memory(info.aperture, base);
587 }
588 
589 
590 status_t
591 intel_allocate_memory(intel_info &info, size_t size, size_t alignment,
592 	uint32 flags, addr_t* _base, phys_addr_t* _physicalBase)
593 {
594 	return gGART->allocate_memory(info.aperture, size, alignment,
595 		flags, _base, _physicalBase);
596 }
597 
598 
599 status_t
600 intel_extreme_init(intel_info &info)
601 {
602 	CALLED();
603 	info.aperture = gGART->map_aperture(info.pci->bus, info.pci->device,
604 		info.pci->function, 0, &info.aperture_base);
605 	if (info.aperture < B_OK) {
606 		ERROR("error: could not map GART aperture! (%s)\n",
607 			strerror(info.aperture));
608 		return info.aperture;
609 	}
610 
611 	AreaKeeper sharedCreator;
612 	info.shared_area = sharedCreator.Create("intel extreme shared info",
613 		(void**)&info.shared_info, B_ANY_KERNEL_ADDRESS,
614 		ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info)) + 3 * B_PAGE_SIZE,
615 		B_FULL_LOCK,
616 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA);
617 	if (info.shared_area < B_OK) {
618 		ERROR("error: could not create shared area!\n");
619 		gGART->unmap_aperture(info.aperture);
620 		return info.shared_area;
621 	}
622 
623 	// enable power
624 	gPCI->set_powerstate(info.pci->bus, info.pci->device, info.pci->function,
625 		PCI_pm_state_d0);
626 
627 	memset((void*)info.shared_info, 0, sizeof(intel_shared_info));
628 
629 	int mmioIndex = 1;
630 	if (info.device_type.Generation() >= 3) {
631 		// For some reason Intel saw the need to change the order of the
632 		// mappings with the introduction of the i9xx family
633 		mmioIndex = 0;
634 	}
635 
636 	// evaluate driver settings, if any
637 
638 	bool hardwareCursor;
639 	read_settings(hardwareCursor);
640 
641 	// memory mapped I/O
642 
643 	// TODO: registers are mapped twice (by us and intel_gart), maybe we
644 	// can share it between the drivers
645 
646 	phys_addr_t addr = info.pci->u.h0.base_registers[mmioIndex];
647 	uint64 barSize = info.pci->u.h0.base_register_sizes[mmioIndex];
648 	if ((info.pci->u.h0.base_register_flags[mmioIndex] & PCI_address_type) == PCI_address_type_64) {
649 		addr |= (uint64)info.pci->u.h0.base_registers[mmioIndex + 1] << 32;
650 		barSize |= (uint64)info.pci->u.h0.base_register_sizes[mmioIndex + 1] << 32;
651 	}
652 	AreaKeeper mmioMapper;
653 	info.registers_area = mmioMapper.Map("intel extreme mmio", addr, barSize,
654 		B_ANY_KERNEL_ADDRESS,
655 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA,
656 		(void**)&info.registers);
657 	if (mmioMapper.InitCheck() < B_OK) {
658 		ERROR("error: could not map memory I/O!\n");
659 		gGART->unmap_aperture(info.aperture);
660 		return info.registers_area;
661 	}
662 
663 	bool hasPCH = (info.pch_info != INTEL_PCH_NONE);
664 
665 	ERROR("Init Intel generation %d GPU %s PCH split.\n",
666 		info.device_type.Generation(), hasPCH ? "with" : "without");
667 
668 	uint32* blocks = info.shared_info->register_blocks;
669 	blocks[REGISTER_BLOCK(REGS_FLAT)] = 0;
670 
671 	// setup the register blocks for the different architectures
672 	if (hasPCH) {
673 		// PCH based platforms (IronLake through ultra-low-power Broadwells)
674 		blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)]
675 			= PCH_NORTH_SHARED_REGISTER_BASE;
676 		blocks[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT)]
677 			= PCH_NORTH_PIPE_AND_PORT_REGISTER_BASE;
678 		blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)]
679 			= PCH_NORTH_PLANE_CONTROL_REGISTER_BASE;
680 		blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)]
681 			= PCH_SOUTH_SHARED_REGISTER_BASE;
682 		blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)]
683 			= PCH_SOUTH_TRANSCODER_AND_PORT_REGISTER_BASE;
684 	} else {
685 		// (G)MCH/ICH based platforms
686 		blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)]
687 			= MCH_SHARED_REGISTER_BASE;
688 		blocks[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT)]
689 			= MCH_PIPE_AND_PORT_REGISTER_BASE;
690 		blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)]
691 			= MCH_PLANE_CONTROL_REGISTER_BASE;
692 		blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)]
693 			= ICH_SHARED_REGISTER_BASE;
694 		blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)]
695 			= ICH_PORT_REGISTER_BASE;
696 	}
697 
698 	// Everything in the display PRM gets +0x180000
699 	if (info.device_type.InGroup(INTEL_GROUP_VLV)) {
700 		// "I nearly got violent with the hw guys when they told me..."
701 		blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)] += VLV_DISPLAY_BASE;
702 		blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)] += VLV_DISPLAY_BASE;
703 	}
704 
705 	TRACE("REGS_NORTH_SHARED: 0x%" B_PRIx32 "\n",
706 		blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)]);
707 	TRACE("REGS_NORTH_PIPE_AND_PORT: 0x%" B_PRIx32 "\n",
708 		blocks[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT)]);
709 	TRACE("REGS_NORTH_PLANE_CONTROL: 0x%" B_PRIx32 "\n",
710 		blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)]);
711 	TRACE("REGS_SOUTH_SHARED: 0x%" B_PRIx32 "\n",
712 		blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)]);
713 	TRACE("REGS_SOUTH_TRANSCODER_PORT: 0x%" B_PRIx32 "\n",
714 		blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)]);
715 
716 	// make sure bus master, memory-mapped I/O, and frame buffer is enabled
717 	set_pci_config(info.pci, PCI_command, 2, get_pci_config(info.pci,
718 		PCI_command, 2) | PCI_command_io | PCI_command_memory
719 		| PCI_command_master);
720 
721 	// reserve ring buffer memory (currently, this memory is placed in
722 	// the graphics memory), but this could bring us problems with
723 	// write combining...
724 
725 	ring_buffer &primary = info.shared_info->primary_ring_buffer;
726 	if (intel_allocate_memory(info, 16 * B_PAGE_SIZE, 0, 0,
727 			(addr_t*)&primary.base) == B_OK) {
728 		primary.register_base = INTEL_PRIMARY_RING_BUFFER;
729 		primary.size = 16 * B_PAGE_SIZE;
730 		primary.offset = (addr_t)primary.base - info.aperture_base;
731 	}
732 
733 	// Enable clock gating
734 	intel_en_gating(info);
735 
736 	// Enable automatic gpu downclocking if we can to save power
737 	intel_en_downclock(info);
738 
739 	// no errors, so keep areas and mappings
740 	sharedCreator.Detach();
741 	mmioMapper.Detach();
742 
743 	aperture_info apertureInfo;
744 	gGART->get_aperture_info(info.aperture, &apertureInfo);
745 
746 	info.shared_info->registers_area = info.registers_area;
747 	info.shared_info->graphics_memory = (uint8*)info.aperture_base;
748 	info.shared_info->physical_graphics_memory = apertureInfo.physical_base;
749 	info.shared_info->graphics_memory_size = apertureInfo.size;
750 	info.shared_info->frame_buffer = 0;
751 	info.shared_info->dpms_mode = B_DPMS_ON;
752 	info.shared_info->min_brightness = 2;
753 	info.shared_info->internal_crt_support = true;
754 	info.shared_info->pch_info = info.pch_info;
755 	info.shared_info->device_type = info.device_type;
756 
757 	// Pull VBIOS info for later use
758 	info.shared_info->got_vbt = parse_vbt_from_bios(info.shared_info);
759 
760 	/* at least 855gm can't drive more than one head at time */
761 	if (info.device_type.InFamily(INTEL_FAMILY_8xx))
762 		info.shared_info->single_head_locked = 1;
763 
764 	if (info.device_type.InFamily(INTEL_FAMILY_SER5)) {
765 		info.shared_info->pll_info.reference_frequency = 120000;// 120 MHz
766 		info.shared_info->pll_info.max_frequency = 350000;
767 			// 350 MHz RAM DAC speed
768 		info.shared_info->pll_info.min_frequency = 20000;		// 20 MHz
769 	} else if (info.device_type.InFamily(INTEL_FAMILY_9xx)) {
770 		info.shared_info->pll_info.reference_frequency = 96000;	// 96 MHz
771 		info.shared_info->pll_info.max_frequency = 400000;
772 			// 400 MHz RAM DAC speed
773 		info.shared_info->pll_info.min_frequency = 20000;		// 20 MHz
774 	} else if (info.device_type.HasDDI() && (info.device_type.Generation() <= 8)) {
775 		info.shared_info->pll_info.reference_frequency = 135000;// 135 MHz
776 		info.shared_info->pll_info.max_frequency = 350000;
777 			// 350 MHz RAM DAC speed
778 		info.shared_info->pll_info.min_frequency = 25000;		// 25 MHz
779 	} else if ((info.device_type.Generation() >= 9) &&
780 				info.device_type.InGroup(INTEL_GROUP_SKY)) {
781 		info.shared_info->pll_info.reference_frequency = 24000;	// 24 MHz
782 		info.shared_info->pll_info.max_frequency = 350000;
783 			// 350 MHz RAM DAC speed
784 		info.shared_info->pll_info.min_frequency = 25000;		// 25 MHz
785 	} else if (info.device_type.Generation() >= 9) {
786 		uint32 refInfo =
787 			(read32(info, ICL_DSSM) & ICL_DSSM_REF_FREQ_MASK) >> ICL_DSSM_REF_FREQ_SHIFT;
788 		switch (refInfo) {
789 			case ICL_DSSM_24000:
790 				info.shared_info->pll_info.reference_frequency = 24000;	// 24 MHz
791 				break;
792 			case ICL_DSSM_19200:
793 				info.shared_info->pll_info.reference_frequency = 19200;	// 19.2 MHz
794 				break;
795 			case ICL_DSSM_38400:
796 				info.shared_info->pll_info.reference_frequency = 38400;	// 38.4 MHz
797 				break;
798 			default:
799 				ERROR("error: unknown ref. freq. strap, using 24Mhz! %" B_PRIx32 "\n", refInfo);
800 				info.shared_info->pll_info.reference_frequency = 24000;	// 24 MHz
801 				break;
802 		}
803 		info.shared_info->pll_info.max_frequency = 350000;
804 			// 350 MHz RAM DAC speed
805 		info.shared_info->pll_info.min_frequency = 25000;		// 25 MHz
806 	} else {
807 		info.shared_info->pll_info.reference_frequency = 48000;	// 48 MHz
808 		info.shared_info->pll_info.max_frequency = 350000;
809 			// 350 MHz RAM DAC speed
810 		info.shared_info->pll_info.min_frequency = 25000;		// 25 MHz
811 	}
812 
813 	info.shared_info->pll_info.divisor_register = INTEL_DISPLAY_A_PLL_DIVISOR_0;
814 
815 #ifdef __HAIKU__
816 	strlcpy(info.shared_info->device_identifier, info.device_identifier,
817 		sizeof(info.shared_info->device_identifier));
818 #else
819 	strcpy(info.shared_info->device_identifier, info.device_identifier);
820 #endif
821 
822 	// setup overlay registers
823 
824 	status_t status = intel_allocate_memory(info, B_PAGE_SIZE, 0,
825 		intel_uses_physical_overlay(*info.shared_info)
826 				? B_APERTURE_NEED_PHYSICAL : 0,
827 		(addr_t*)&info.overlay_registers,
828 		&info.shared_info->physical_overlay_registers);
829 	if (status == B_OK) {
830 		info.shared_info->overlay_offset = (addr_t)info.overlay_registers
831 			- info.aperture_base;
832 		TRACE("Overlay registers mapped at 0x%" B_PRIx32 " = %p - %"
833 			B_PRIxADDR " (%" B_PRIxPHYSADDR ")\n",
834 			info.shared_info->overlay_offset, info.overlay_registers,
835 			info.aperture_base, info.shared_info->physical_overlay_registers);
836 		init_overlay_registers(info.overlay_registers);
837 	} else {
838 		ERROR("error: could not allocate overlay memory! %s\n",
839 			strerror(status));
840 	}
841 
842 	// Allocate hardware status page and the cursor memory
843 	TRACE("Allocating hardware status page");
844 
845 	if (intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL,
846 			(addr_t*)info.shared_info->status_page,
847 			&info.shared_info->physical_status_page) == B_OK) {
848 		// TODO: set status page
849 	}
850 	if (hardwareCursor) {
851 		intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL,
852 			(addr_t*)&info.shared_info->cursor_memory,
853 			&info.shared_info->physical_cursor_memory);
854 	}
855 
856 	edid1_info* edidInfo = (edid1_info*)get_boot_item(VESA_EDID_BOOT_INFO,
857 		NULL);
858 	if (edidInfo != NULL) {
859 		info.shared_info->has_vesa_edid_info = true;
860 		memcpy(&info.shared_info->vesa_edid_info, edidInfo, sizeof(edid1_info));
861 	}
862 
863 	init_interrupt_handler(info);
864 
865 	if (hasPCH) {
866 		if (info.device_type.Generation() == 5) {
867 			info.shared_info->fdi_link_frequency = (read32(info, FDI_PLL_BIOS_0)
868 				& FDI_PLL_FB_CLOCK_MASK) + 2;
869 			info.shared_info->fdi_link_frequency *= 100;
870 		} else {
871 			info.shared_info->fdi_link_frequency = 2700;
872 		}
873 		if (info.shared_info->pch_info >= INTEL_PCH_CNP) {
874 			// TODO read/write info.shared_info->hraw_clock
875 		} else {
876 			info.shared_info->hraw_clock = (read32(info, PCH_RAWCLK_FREQ)
877 				& RAWCLK_FREQ_MASK) * 1000;
878 			TRACE("%s: rawclk rate: %" B_PRIu32 " kHz\n", __func__, info.shared_info->hraw_clock);
879 		}
880 	} else {
881 		// TODO read info.shared_info->hraw_clock
882 		info.shared_info->fdi_link_frequency = 0;
883 	}
884 
885 	if (info.device_type.InGroup(INTEL_GROUP_BDW)) {
886 		uint32 lcpll = read32(info, LCPLL_CTL);
887 		if ((lcpll & LCPLL_CD_SOURCE_FCLK) != 0)
888 			info.shared_info->hw_cdclk = 800000;
889 		else if ((read32(info, FUSE_STRAP) & HSW_CDCLK_LIMIT) != 0)
890 			info.shared_info->hw_cdclk = 450000;
891 		else if ((lcpll & LCPLL_CLK_FREQ_MASK) == LCPLL_CLK_FREQ_450)
892 			info.shared_info->hw_cdclk = 450000;
893 		else if ((lcpll & LCPLL_CLK_FREQ_MASK) == LCPLL_CLK_FREQ_54O_BDW)
894 			info.shared_info->hw_cdclk = 540000;
895 		else if ((lcpll & LCPLL_CLK_FREQ_MASK) == LCPLL_CLK_FREQ_337_5_BDW)
896 			info.shared_info->hw_cdclk = 337500;
897 		else
898 			info.shared_info->hw_cdclk = 675000;
899 	} else if (info.device_type.InGroup(INTEL_GROUP_HAS)) {
900 		uint32 lcpll = read32(info, LCPLL_CTL);
901 		if ((lcpll & LCPLL_CD_SOURCE_FCLK) != 0)
902 			info.shared_info->hw_cdclk = 800000;
903 		else if ((read32(info, FUSE_STRAP) & HSW_CDCLK_LIMIT) != 0)
904 			info.shared_info->hw_cdclk = 450000;
905 		else if ((lcpll & LCPLL_CLK_FREQ_MASK) == LCPLL_CLK_FREQ_450)
906 			info.shared_info->hw_cdclk = 450000;
907 		/* ULT type is missing
908 		else if (IS_ULT)
909 			info.shared_info->hw_cdclk = 337500;
910 		*/
911 		else
912 			info.shared_info->hw_cdclk = 540000;
913 	} else if (info.device_type.InGroup(INTEL_GROUP_SNB)
914 		|| info.device_type.InGroup(INTEL_GROUP_IVB)) {
915 		info.shared_info->hw_cdclk = 400000;
916 	} else if (info.device_type.InGroup(INTEL_GROUP_ILK)) {
917 		info.shared_info->hw_cdclk = 450000;
918 	}
919 	TRACE("%s: hw_cdclk: %" B_PRIu32 " kHz\n", __func__, info.shared_info->hw_cdclk);
920 
921 	TRACE("%s: completed successfully!\n", __func__);
922 	return B_OK;
923 }
924 
925 
926 void
927 intel_extreme_uninit(intel_info &info)
928 {
929 	CALLED();
930 
931 	if (!info.fake_interrupts && info.shared_info->vblank_sem > 0) {
932 		// disable interrupt generation
933 		if (info.device_type.Generation() >= 8) {
934 			if (info.device_type.Generation() >= 11) {
935 				gen11_enable_global_interrupts(info, false);
936 			}
937 			gen8_enable_global_interrupts(info, false);
938 			interrupt_handler handler = &gen8_interrupt_handler;
939 			if (info.device_type.Generation() >= 11)
940 				handler = &gen11_interrupt_handler;
941 			remove_io_interrupt_handler(info.irq, handler, &info);
942 		} else {
943 			write32(info, find_reg(info, INTEL_INTERRUPT_ENABLED), 0);
944 			write32(info, find_reg(info, INTEL_INTERRUPT_MASK), ~0);
945 			remove_io_interrupt_handler(info.irq, intel_interrupt_handler, &info);
946 		}
947 
948 		if (info.use_msi) {
949 			gPCI->disable_msi(info.pci->bus,
950 				info.pci->device, info.pci->function);
951 			gPCI->unconfigure_msi(info.pci->bus,
952 				info.pci->device, info.pci->function);
953 		}
954 	}
955 
956 	gGART->unmap_aperture(info.aperture);
957 
958 	delete_area(info.registers_area);
959 	delete_area(info.shared_area);
960 }
961 
962