xref: /haiku/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp (revision fce4895d1884da5ae6fb299d23c735c598e690b1)
1 /*
2  * Copyright 2006-2014, 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  */
9 
10 
11 #include "intel_extreme.h"
12 
13 #include "AreaKeeper.h"
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <errno.h>
18 
19 #include <boot_item.h>
20 #include <driver_settings.h>
21 #include <util/kernel_cpp.h>
22 
23 #include <vesa_info.h>
24 
25 #include "driver.h"
26 #include "power.h"
27 #include "utility.h"
28 
29 
30 #define TRACE_INTELEXTREME
31 #ifdef TRACE_INTELEXTREME
32 #	define TRACE(x...) dprintf("intel_extreme: " x)
33 #else
34 #	define TRACE(x) ;
35 #endif
36 
37 #define ERROR(x...) dprintf("intel_extreme: " x)
38 #define CALLED(x...) TRACE("intel_extreme: CALLED %s\n", __PRETTY_FUNCTION__)
39 
40 
41 static void
42 init_overlay_registers(overlay_registers* registers)
43 {
44 	memset(registers, 0, B_PAGE_SIZE);
45 
46 	registers->contrast_correction = 0x48;
47 	registers->saturation_cos_correction = 0x9a;
48 		// this by-passes contrast and saturation correction
49 }
50 
51 
52 static void
53 read_settings(bool &hardwareCursor)
54 {
55 	hardwareCursor = false;
56 
57 	void* settings = load_driver_settings("intel_extreme");
58 	if (settings != NULL) {
59 		hardwareCursor = get_driver_boolean_parameter(settings,
60 			"hardware_cursor", true, true);
61 
62 		unload_driver_settings(settings);
63 	}
64 }
65 
66 
67 static int32
68 release_vblank_sem(intel_info &info)
69 {
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;
76 	}
77 
78 	return B_HANDLED_INTERRUPT;
79 }
80 
81 
82 static int32
83 intel_interrupt_handler(void* data)
84 {
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;
90 
91 	int32 handled = B_HANDLED_INTERRUPT;
92 
93 	while (identity != 0) {
94 
95 		// TODO: verify that these aren't actually the same
96 		bool hasPCH = info.device_type.HasPlatformControlHub();
97 		uint16 mask;
98 
99 		// Intel changed the PCH register mapping between Sandy Bridge and the
100 		// later generations (Ivy Bridge and up).
101 		if (info.device_type.InGroup(INTEL_GROUP_SNB)) {
102 			mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEA_SNB
103 				: INTERRUPT_VBLANK_PIPEA;
104 			if ((identity & mask) != 0) {
105 				handled = release_vblank_sem(info);
106 
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);
110 			}
111 
112 			mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEB_SNB
113 				: INTERRUPT_VBLANK_PIPEB;
114 			if ((identity & mask) != 0) {
115 				handled = release_vblank_sem(info);
116 
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);
120 			}
121 		} else {
122 			mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEA
123 				: INTERRUPT_VBLANK_PIPEA;
124 			if ((identity & mask) != 0) {
125 				handled = release_vblank_sem(info);
126 
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);
130 			}
131 
132 			mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEB
133 				: INTERRUPT_VBLANK_PIPEB;
134 			if ((identity & mask) != 0) {
135 				handled = release_vblank_sem(info);
136 
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);
140 			}
141 
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);
148 
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);
152 			}
153 #endif
154 		}
155 
156 		// setting the bit clears it!
157 		write16(info, reg, identity);
158 		identity = read16(info, reg);
159 	}
160 
161 	return handled;
162 }
163 
164 
165 static void
166 init_interrupt_handler(intel_info &info)
167 {
168 	info.shared_info->vblank_sem = create_sem(0, "intel extreme vblank");
169 	if (info.shared_info->vblank_sem < B_OK)
170 		return;
171 
172 	status_t status = B_OK;
173 
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;
182 	}
183 
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;
199 		}
200 	}
201 
202 	if (status == B_OK && info.irq != 0xff) {
203 		// we've gotten an interrupt line for us to use
204 
205 		info.fake_interrupts = false;
206 
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);
214 
215 			write16(info, find_reg(info, INTEL_INTERRUPT_IDENTITY), ~0);
216 
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);
222 
223 			write16(info, find_reg(info, INTEL_INTERRUPT_ENABLED), enable);
224 			write16(info, find_reg(info, INTEL_INTERRUPT_MASK), ~enable);
225 		}
226 	}
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;
232 
233 		// TODO: fake interrupts!
234 		TRACE("Fake interrupt mode (no PCI interrupt line assigned\n");
235 		status = B_ERROR;
236 	}
237 
238 	if (status < B_OK) {
239 		delete_sem(info.shared_info->vblank_sem);
240 		info.shared_info->vblank_sem = B_ERROR;
241 	}
242 }
243 
244 
245 //	#pragma mark -
246 
247 
248 status_t
249 intel_free_memory(intel_info &info, addr_t base)
250 {
251 	return gGART->free_memory(info.aperture, base);
252 }
253 
254 
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)
258 {
259 	return gGART->allocate_memory(info.aperture, size, alignment,
260 		flags, _base, _physicalBase);
261 }
262 
263 
264 status_t
265 intel_extreme_init(intel_info &info)
266 {
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;
273 	}
274 
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;
284 	}
285 
286 	memset((void*)info.shared_info, 0, sizeof(intel_shared_info));
287 
288 	int fbIndex = 0;
289 	int mmioIndex = 1;
290 	if (info.device_type.Generation() >= 3) {
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;
295 	}
296 
297 	// evaluate driver settings, if any
298 
299 	bool hardwareCursor;
300 	read_settings(hardwareCursor);
301 
302 	// memory mapped I/O
303 
304 	// TODO: registers are mapped twice (by us and intel_gart), maybe we
305 	// can share it between the drivers
306 
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;
317 	}
318 
319 	ERROR("Init Intel generation %" B_PRId32 " GPU %s PCH split.\n",
320 		info.device_type.Generation(),
321 		info.device_type.HasPlatformControlHub() ? "with" : "without");
322 
323 	uint32* blocks = info.shared_info->register_blocks;
324 	blocks[REGISTER_BLOCK(REGS_FLAT)] = 0;
325 
326 	// setup the register blocks for the different architectures
327 	if (info.device_type.HasPlatformControlHub()) {
328 		// PCH based platforms (IronLake through ultra-low-power Broadwells)
329 		blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)]
330 			= PCH_NORTH_SHARED_REGISTER_BASE;
331 		blocks[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT)]
332 			= PCH_NORTH_PIPE_AND_PORT_REGISTER_BASE;
333 		blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)]
334 			= PCH_NORTH_PLANE_CONTROL_REGISTER_BASE;
335 		blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)]
336 			= PCH_SOUTH_SHARED_REGISTER_BASE;
337 		blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)]
338 			= PCH_SOUTH_TRANSCODER_AND_PORT_REGISTER_BASE;
339 	} else {
340 		// (G)MCH/ICH based platforms
341 		blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)]
342 			= MCH_SHARED_REGISTER_BASE;
343 		blocks[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT)]
344 			= MCH_PIPE_AND_PORT_REGISTER_BASE;
345 		blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)]
346 			= MCH_PLANE_CONTROL_REGISTER_BASE;
347 		blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)]
348 			= ICH_SHARED_REGISTER_BASE;
349 		blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)]
350 			= ICH_PORT_REGISTER_BASE;
351 	}
352 
353 	// Everything in the display PRM gets +0x180000
354 	if (info.device_type.InGroup(INTEL_GROUP_VLV)) {
355 		// "I nearly got violent with the hw guys when they told me..."
356 		blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)] += VLV_DISPLAY_BASE;
357 		blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)] += VLV_DISPLAY_BASE;
358 	}
359 
360 	TRACE("REGS_NORTH_SHARED: 0x%X\n",
361 		blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)]);
362 	TRACE("REGS_NORTH_PIPE_AND_PORT: 0x%X\n",
363 		blocks[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT)]);
364 	TRACE("REGS_NORTH_PLANE_CONTROL: 0x%X\n",
365 		blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)]);
366 	TRACE("REGS_SOUTH_SHARED: 0x%X\n",
367 		blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)]);
368 	TRACE("REGS_SOUTH_TRANSCODER_PORT: 0x%X\n",
369 		blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)]);
370 
371 	// make sure bus master, memory-mapped I/O, and frame buffer is enabled
372 	set_pci_config(info.pci, PCI_command, 2, get_pci_config(info.pci,
373 		PCI_command, 2) | PCI_command_io | PCI_command_memory
374 		| PCI_command_master);
375 
376 	// reserve ring buffer memory (currently, this memory is placed in
377 	// the graphics memory), but this could bring us problems with
378 	// write combining...
379 
380 	ring_buffer &primary = info.shared_info->primary_ring_buffer;
381 	if (intel_allocate_memory(info, 16 * B_PAGE_SIZE, 0, 0,
382 			(addr_t*)&primary.base) == B_OK) {
383 		primary.register_base = INTEL_PRIMARY_RING_BUFFER;
384 		primary.size = 16 * B_PAGE_SIZE;
385 		primary.offset = (addr_t)primary.base - info.aperture_base;
386 	}
387 
388 	// Enable clock gating
389 	intel_en_gating(info);
390 
391 	// Enable automatic gpu downclocking if we can to save power
392 	intel_en_downclock(info);
393 
394 	// no errors, so keep areas and mappings
395 	sharedCreator.Detach();
396 	mmioMapper.Detach();
397 
398 	aperture_info apertureInfo;
399 	gGART->get_aperture_info(info.aperture, &apertureInfo);
400 
401 	info.shared_info->registers_area = info.registers_area;
402 	info.shared_info->graphics_memory = (uint8*)info.aperture_base;
403 	info.shared_info->physical_graphics_memory = apertureInfo.physical_base;
404 	info.shared_info->graphics_memory_size = apertureInfo.size;
405 	info.shared_info->frame_buffer = 0;
406 	info.shared_info->dpms_mode = B_DPMS_ON;
407 
408 	// Pull VBIOS panel mode for later use
409 	info.shared_info->got_vbt = get_lvds_mode_from_bios(
410 		&info.shared_info->panel_mode);
411 
412 	/* at least 855gm can't drive more than one head at time */
413 	if (info.device_type.InFamily(INTEL_FAMILY_8xx))
414 		info.shared_info->single_head_locked = 1;
415 
416 	if (info.device_type.InFamily(INTEL_FAMILY_9xx)
417 		| info.device_type.InFamily(INTEL_FAMILY_SER5)) {
418 		info.shared_info->pll_info.reference_frequency = 96000;	// 96 kHz
419 		info.shared_info->pll_info.max_frequency = 400000;
420 			// 400 MHz RAM DAC speed
421 		info.shared_info->pll_info.min_frequency = 20000;		// 20 MHz
422 	} else {
423 		info.shared_info->pll_info.reference_frequency = 48000;	// 48 kHz
424 		info.shared_info->pll_info.max_frequency = 350000;
425 			// 350 MHz RAM DAC speed
426 		info.shared_info->pll_info.min_frequency = 25000;		// 25 MHz
427 	}
428 
429 	info.shared_info->pll_info.divisor_register = INTEL_DISPLAY_A_PLL_DIVISOR_0;
430 
431 	info.shared_info->device_type = info.device_type;
432 #ifdef __HAIKU__
433 	strlcpy(info.shared_info->device_identifier, info.device_identifier,
434 		sizeof(info.shared_info->device_identifier));
435 #else
436 	strcpy(info.shared_info->device_identifier, info.device_identifier);
437 #endif
438 
439 	// setup overlay registers
440 
441 	status_t status = intel_allocate_memory(info, B_PAGE_SIZE, 0,
442 		intel_uses_physical_overlay(*info.shared_info)
443 				? B_APERTURE_NEED_PHYSICAL : 0,
444 		(addr_t*)&info.overlay_registers,
445 		&info.shared_info->physical_overlay_registers);
446 	if (status == B_OK) {
447 		info.shared_info->overlay_offset = (addr_t)info.overlay_registers
448 			- info.aperture_base;
449 		init_overlay_registers(info.overlay_registers);
450 	} else {
451 		ERROR("error: could not allocate overlay memory! %s\n",
452 			strerror(status));
453 	}
454 
455 	// Allocate hardware status page and the cursor memory
456 
457 	if (intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL,
458 			(addr_t*)info.shared_info->status_page,
459 			&info.shared_info->physical_status_page) == B_OK) {
460 		// TODO: set status page
461 	}
462 	if (hardwareCursor) {
463 		intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL,
464 			(addr_t*)&info.shared_info->cursor_memory,
465 			&info.shared_info->physical_cursor_memory);
466 	}
467 
468 	edid1_info* edidInfo = (edid1_info*)get_boot_item(VESA_EDID_BOOT_INFO,
469 		NULL);
470 	if (edidInfo != NULL) {
471 		info.shared_info->has_vesa_edid_info = true;
472 		memcpy(&info.shared_info->vesa_edid_info, edidInfo, sizeof(edid1_info));
473 	}
474 
475 	init_interrupt_handler(info);
476 
477 	if (info.device_type.HasPlatformControlHub()) {
478 		if (info.device_type.Generation() == 5) {
479 			info.shared_info->fdi_link_frequency = (read32(info, FDI_PLL_BIOS_0)
480 				& FDI_PLL_FB_CLOCK_MASK) + 2;
481 			info.shared_info->fdi_link_frequency *= 100;
482 		} else {
483 			info.shared_info->fdi_link_frequency = 2700;
484 		}
485 	} else {
486 		info.shared_info->fdi_link_frequency = 0;
487 	}
488 
489 	TRACE("%s: completed successfully!\n", __func__);
490 	return B_OK;
491 }
492 
493 
494 void
495 intel_extreme_uninit(intel_info &info)
496 {
497 	CALLED();
498 
499 	if (!info.fake_interrupts && info.shared_info->vblank_sem > 0) {
500 		// disable interrupt generation
501 		write16(info, find_reg(info, INTEL_INTERRUPT_ENABLED), 0);
502 		write16(info, find_reg(info, INTEL_INTERRUPT_MASK), ~0);
503 
504 		remove_io_interrupt_handler(info.irq, intel_interrupt_handler, &info);
505 
506 		if (info.use_msi && gPCIx86Module != NULL) {
507 			gPCIx86Module->disable_msi(info.pci->bus,
508 				info.pci->device, info.pci->function);
509 			gPCIx86Module->unconfigure_msi(info.pci->bus,
510 				info.pci->device, info.pci->function);
511 		}
512 	}
513 
514 	gGART->unmap_aperture(info.aperture);
515 
516 	delete_area(info.registers_area);
517 	delete_area(info.shared_area);
518 }
519 
520