xref: /haiku/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp (revision 020cbad9d40235a2c50a81a42d69912a5ff8fbc4)
1 /*
2  * Copyright 2006-2008, 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  */
8 
9 
10 #include "intel_extreme.h"
11 
12 #include "AreaKeeper.h"
13 #include "driver.h"
14 #include "utility.h"
15 
16 #include <unistd.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <errno.h>
20 
21 #include <driver_settings.h>
22 #include <util/kernel_cpp.h>
23 
24 
25 #define TRACE_DEVICE
26 #ifdef TRACE_DEVICE
27 #	define TRACE(x) dprintf x
28 #else
29 #	define TRACE(x) ;
30 #endif
31 
32 
33 static void
34 init_overlay_registers(overlay_registers *registers)
35 {
36 	memset(registers, 0, B_PAGE_SIZE);
37 
38 	registers->contrast_correction = 0x48;
39 	registers->saturation_cos_correction = 0x9a;
40 		// this by-passes contrast and saturation correction
41 }
42 
43 
44 static void
45 read_settings(size_t &memorySize, bool &hardwareCursor, bool &ignoreAllocated)
46 {
47 	size_t size = 8; // 8 MB
48 	hardwareCursor = false;
49 	ignoreAllocated = false;
50 
51 	void *settings = load_driver_settings("intel_extreme");
52 	if (settings != NULL) {
53 		size_t specified = 0;
54 		const char *string = get_driver_parameter(settings,
55 			"graphics_memory_size", "0", "0");
56 		if (string != NULL)
57 			specified = atoi(string);
58 
59 		hardwareCursor = get_driver_boolean_parameter(settings,
60 			"hardware_cursor", true, true);
61 
62 		ignoreAllocated = get_driver_boolean_parameter(settings,
63 			"ignore_bios_allocated_memory", false, false);
64 
65 		unload_driver_settings(settings);
66 
67 		if (specified != 0) {
68 			// take the next power of two
69 			size_t desired = 1;
70 			while (desired < specified) {
71 				desired *= 2;
72 			}
73 
74 			if (desired < 128)
75 				size = desired;
76 		}
77 	}
78 
79 	memorySize = size << 20;
80 }
81 
82 
83 static size_t
84 determine_stolen_memory_size(intel_info &info)
85 {
86 	// read stolen memory from the PCI configuration of the PCI bridge
87 	uint16 memoryConfig = gPCI->read_pci_config(0, 0, 0, INTEL_GRAPHICS_MEMORY_CONTROL, 2);
88 	size_t memorySize = 1 << 20; // 1 MB
89 	size_t gttSize = 0;
90 
91 	if (info.device_type == (INTEL_TYPE_9xx | INTEL_TYPE_965)) {
92 		switch (memoryConfig & i965_GTT_MASK) {
93 			case i965_GTT_128K:
94 				gttSize = 128 << 10;
95 				break;
96 			case i965_GTT_256K:
97 				gttSize = 256 << 10;
98 				break;
99 			case i965_GTT_512K:
100 				gttSize = 512 << 10;
101 				break;
102 		}
103 	} else if (info.device_type == (INTEL_TYPE_9xx | INTEL_TYPE_G33)) {
104 		switch (memoryConfig & G33_GTT_MASK) {
105 			case G33_GTT_1M:
106 				gttSize = 1 << 20;
107 				break;
108 			case G33_GTT_2M:
109 				gttSize = 2 << 20;
110 				break;
111 		}
112 	} else {
113 		// older models have the GTT as large as their frame buffer mapping
114 		// TODO: check if the i9xx version works with the i8xx chips as well
115 		size_t frameBufferSize = 0;
116 		if ((info.device_type & INTEL_TYPE_FAMILY_MASK) == INTEL_TYPE_8xx) {
117 			if ((info.device_type & INTEL_TYPE_83x) != 0
118 				&& (memoryConfig & MEMORY_MASK) == i830_FRAME_BUFFER_64M)
119 				frameBufferSize = 64 << 20;
120 			else
121 				frameBufferSize = 128 << 20;
122 		} else if ((info.device_type & INTEL_TYPE_FAMILY_MASK) == INTEL_TYPE_9xx)
123 			frameBufferSize = info.pci->u.h0.base_register_sizes[2];
124 
125 		gttSize = frameBufferSize / 1024;
126 	}
127 
128 	// TODO: test with different models!
129 
130 	if (info.device_type == (INTEL_TYPE_8xx | INTEL_TYPE_83x)) {
131 		switch (memoryConfig & STOLEN_MEMORY_MASK) {
132 			case i830_LOCAL_MEMORY_ONLY:
133 				// TODO: determine its size!
134 				break;
135 			case i830_STOLEN_512K:
136 				memorySize >>= 1;
137 				break;
138 			case i830_STOLEN_8M:
139 				memorySize *= 8;
140 				break;
141 		}
142 	} else if (info.device_type == (INTEL_TYPE_8xx | INTEL_TYPE_85x)
143 		|| (info.device_type & INTEL_TYPE_FAMILY_MASK) == INTEL_TYPE_9xx) {
144 		switch (memoryConfig & STOLEN_MEMORY_MASK) {
145 			case i855_STOLEN_MEMORY_4M:
146 				memorySize *= 4;
147 				break;
148 			case i855_STOLEN_MEMORY_8M:
149 				memorySize *= 8;
150 				break;
151 			case i855_STOLEN_MEMORY_16M:
152 				memorySize *= 16;
153 				break;
154 			case i855_STOLEN_MEMORY_32M:
155 				memorySize *= 32;
156 				break;
157 			case i855_STOLEN_MEMORY_48M:
158 				memorySize *= 48;
159 				break;
160 			case i855_STOLEN_MEMORY_64M:
161 				memorySize *= 64;
162 				break;
163 			case i855_STOLEN_MEMORY_128M:
164 				memorySize *= 128;
165 				break;
166 			case i855_STOLEN_MEMORY_256M:
167 				memorySize *= 256;
168 				break;
169 		}
170 	}
171 
172 	return memorySize - gttSize - 4096;
173 }
174 
175 
176 static void
177 set_gtt_entry(intel_info &info, uint32 offset, uint8 *physicalAddress)
178 {
179 	write32(info.gtt_base + (offset >> GTT_PAGE_SHIFT),
180 		(uint32)physicalAddress | GTT_ENTRY_VALID);
181 }
182 
183 
184 static int32
185 release_vblank_sem(intel_info &info)
186 {
187 	int32 count;
188 	if (get_sem_count(info.shared_info->vblank_sem, &count) == B_OK
189 		&& count < 0) {
190 		release_sem_etc(info.shared_info->vblank_sem, -count,
191 			B_DO_NOT_RESCHEDULE);
192 		return B_INVOKE_SCHEDULER;
193 	}
194 
195 	return B_HANDLED_INTERRUPT;
196 }
197 
198 
199 static int32
200 intel_interrupt_handler(void *data)
201 {
202 	intel_info &info = *(intel_info *)data;
203 
204 	uint32 identity = read16(info.registers + INTEL_INTERRUPT_IDENTITY);
205 	if (identity == 0)
206 		return B_UNHANDLED_INTERRUPT;
207 
208 	int32 handled = B_HANDLED_INTERRUPT;
209 
210 	if ((identity & INTERRUPT_VBLANK) != 0) {
211 		handled = release_vblank_sem(info);
212 
213 		// make sure we'll get another one of those
214 		write32(info.registers + INTEL_DISPLAY_A_PIPE_STATUS, DISPLAY_PIPE_VBLANK_STATUS);
215 	}
216 
217 	// setting the bit clears it!
218 	write16(info.registers + INTEL_INTERRUPT_IDENTITY, identity);
219 
220 	return handled;
221 }
222 
223 
224 static void
225 init_interrupt_handler(intel_info &info)
226 {
227 	info.shared_info->vblank_sem = create_sem(0, "intel extreme vblank");
228 	if (info.shared_info->vblank_sem < B_OK)
229 		return;
230 
231 	status_t status = B_OK;
232 
233 	// We need to change the owner of the sem to the calling team (usually the
234 	// app_server), because userland apps cannot acquire kernel semaphores
235 	thread_id thread = find_thread(NULL);
236 	thread_info threadInfo;
237 	if (get_thread_info(thread, &threadInfo) != B_OK
238 		|| set_sem_owner(info.shared_info->vblank_sem, threadInfo.team) != B_OK) {
239 		status = B_ERROR;
240 	}
241 
242 	if (status == B_OK
243 		&& info.pci->u.h0.interrupt_pin != 0x00 && info.pci->u.h0.interrupt_line != 0xff) {
244 		// we've gotten an interrupt line for us to use
245 
246 		info.fake_interrupts = false;
247 
248 		status = install_io_interrupt_handler(info.pci->u.h0.interrupt_line,
249 			&intel_interrupt_handler, (void *)&info, 0);
250 		if (status == B_OK) {
251 			// enable interrupts - we only want VBLANK interrupts
252 			write16(info.registers + INTEL_INTERRUPT_ENABLED,
253 				read16(info.registers + INTEL_INTERRUPT_ENABLED) | INTERRUPT_VBLANK);
254 			write16(info.registers + INTEL_INTERRUPT_MASK, ~INTERRUPT_VBLANK);
255 
256 			write32(info.registers + INTEL_DISPLAY_A_PIPE_STATUS,
257 				DISPLAY_PIPE_VBLANK_STATUS);
258 			write16(info.registers + INTEL_INTERRUPT_IDENTITY, ~0);
259 		}
260 	}
261 	if (status < B_OK) {
262 		// there is no interrupt reserved for us, or we couldn't install our interrupt
263 		// handler, let's fake the vblank interrupt for our clients using a timer
264 		// interrupt
265 		info.fake_interrupts = true;
266 
267 		// TODO: fake interrupts!
268 		status = B_ERROR;
269 	}
270 
271 	if (status < B_OK) {
272 		delete_sem(info.shared_info->vblank_sem);
273 		info.shared_info->vblank_sem = B_ERROR;
274 	}
275 }
276 
277 
278 //	#pragma mark -
279 
280 
281 status_t
282 intel_extreme_init(intel_info &info)
283 {
284 	AreaKeeper sharedCreator;
285 	info.shared_area = sharedCreator.Create("intel extreme shared info",
286 		(void **)&info.shared_info, B_ANY_KERNEL_ADDRESS,
287 		ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info)) + 3 * B_PAGE_SIZE,
288 		B_FULL_LOCK, 0);
289 	if (info.shared_area < B_OK)
290 		return info.shared_area;
291 
292 	memset((void *)info.shared_info, 0, sizeof(intel_shared_info));
293 
294 	int fbIndex = 0;
295 	int mmioIndex = 1;
296 	if ((info.device_type & INTEL_TYPE_FAMILY_MASK) == INTEL_TYPE_9xx) {
297 		// for some reason Intel saw the need to change the order of the mappings
298 		// with the introduction of the i9xx family
299 		mmioIndex = 0;
300 		fbIndex = 2;
301 	}
302 
303 	// evaluate driver settings, if any
304 
305 	bool ignoreBIOSAllocated;
306 	bool hardwareCursor;
307 	size_t totalSize;
308 	read_settings(totalSize, hardwareCursor, ignoreBIOSAllocated);
309 
310 	// Determine the amount of "stolen" (ie. reserved by the BIOS) graphics memory
311 	// and see if we need to allocate some more.
312 	// TODO: make it allocate the memory on demand!
313 
314 	size_t stolenSize = ignoreBIOSAllocated ? 0 : determine_stolen_memory_size(info);
315 	totalSize = max_c(totalSize, stolenSize);
316 
317 	dprintf(DEVICE_NAME ": detected %ld MB of stolen memory, reserving %ld MB total\n",
318 		stolenSize >> 20, totalSize >> 20);
319 
320 	AreaKeeper additionalMemoryCreator;
321 	uint8 *additionalMemory;
322 
323 	if (stolenSize < totalSize) {
324 		// Every device should have at least 8 MB - we could also allocate them
325 		// on demand only, but we're lazy here...
326 		// TODO: overlay seems to have problem when the memory pages are too
327 		//	far spreaded - that's why we're using B_CONTIGUOUS for now.
328 		info.additional_memory_area = additionalMemoryCreator.Create("intel additional memory",
329 			(void **)&additionalMemory, B_ANY_KERNEL_ADDRESS,
330 			totalSize - stolenSize, B_CONTIGUOUS /*B_FULL_LOCK*/, 0);
331 		if (info.additional_memory_area < B_OK)
332 			return info.additional_memory_area;
333 	} else
334 		info.additional_memory_area = B_ERROR;
335 
336 	// map frame buffer, try to map it write combined
337 
338 	AreaKeeper graphicsMapper;
339 	info.graphics_memory_area = graphicsMapper.Map("intel extreme graphics memory",
340 		(void *)info.pci->u.h0.base_registers[fbIndex],
341 		totalSize, B_ANY_KERNEL_BLOCK_ADDRESS /*| B_MTR_WC*/,
342 		B_READ_AREA | B_WRITE_AREA, (void **)&info.graphics_memory);
343 	if (graphicsMapper.InitCheck() < B_OK) {
344 		// try again without write combining
345 		dprintf(DEVICE_NAME ": enabling write combined mode failed.\n");
346 
347 		info.graphics_memory_area = graphicsMapper.Map("intel extreme graphics memory",
348 			(void *)info.pci->u.h0.base_registers[fbIndex],
349 			totalSize/*info.pci->u.h0.base_register_sizes[0]*/, B_ANY_KERNEL_BLOCK_ADDRESS,
350 			B_READ_AREA | B_WRITE_AREA, (void **)&info.graphics_memory);
351 	}
352 	if (graphicsMapper.InitCheck() < B_OK) {
353 		dprintf(DEVICE_NAME ": could not map frame buffer!\n");
354 		return info.graphics_memory_area;
355 	}
356 
357 	// memory mapped I/O
358 
359 	AreaKeeper mmioMapper;
360 	info.registers_area = mmioMapper.Map("intel extreme mmio",
361 		(void *)info.pci->u.h0.base_registers[mmioIndex],
362 		info.pci->u.h0.base_register_sizes[mmioIndex],
363 		B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA /*0*/, (void **)&info.registers);
364 	if (mmioMapper.InitCheck() < B_OK) {
365 		dprintf(DEVICE_NAME ": could not map memory I/O!\n");
366 		return info.registers_area;
367 	}
368 
369 	// make sure bus master, memory-mapped I/O, and frame buffer is enabled
370 	set_pci_config(info.pci, PCI_command, 2, get_pci_config(info.pci, PCI_command, 2)
371 		| PCI_command_io | PCI_command_memory | PCI_command_master);
372 
373 	// init graphics memory manager
374 
375 	AreaKeeper gttMapper;
376 	info.gtt_area = -1;
377 
378 	if ((info.device_type & INTEL_TYPE_9xx) != 0) {
379 		if ((info.device_type & INTEL_TYPE_GROUP_MASK) == INTEL_TYPE_965) {
380 			info.gtt_base = info.registers + i965_GTT_BASE;
381 			info.gtt_size = i965_GTT_SIZE;
382 		} else {
383 			info.gtt_area = gttMapper.Map("intel extreme gtt",
384 				(void *)info.pci->u.h0.base_registers[3], totalSize / 1024,
385 				B_ANY_KERNEL_ADDRESS, 0, (void **)&info.gtt_base);
386 			if (gttMapper.InitCheck() < B_OK) {
387 				dprintf(DEVICE_NAME ": could not map GTT area!\n");
388 				return info.gtt_area;
389 			}
390 			info.gtt_size = totalSize / 1024;
391 		}
392 	} else {
393 		info.gtt_base = info.registers + i830_GTT_BASE;
394 		info.gtt_size = i830_GTT_SIZE;
395 	}
396 
397 	info.memory_manager = mem_init("intel extreme memory manager", 0, totalSize, 1024,
398 		min_c(totalSize / 1024, 512));
399 	if (info.memory_manager == NULL)
400 		return B_NO_MEMORY;
401 
402 	// reserve ring buffer memory (currently, this memory is placed in
403 	// the graphics memory), but this could bring us problems with
404 	// write combining...
405 
406 	ring_buffer &primary = info.shared_info->primary_ring_buffer;
407 	if (mem_alloc(info.memory_manager, 4 * B_PAGE_SIZE, &info,
408 			&primary.handle, &primary.offset) == B_OK) {
409 		primary.register_base = INTEL_PRIMARY_RING_BUFFER;
410 		primary.size = 4 * B_PAGE_SIZE;
411 		primary.base = info.graphics_memory + primary.offset;
412 	}
413 
414 	ring_buffer &secondary = info.shared_info->secondary_ring_buffer;
415 	if (mem_alloc(info.memory_manager, B_PAGE_SIZE, &info,
416 			&secondary.handle, &secondary.offset) == B_OK) {
417 		secondary.register_base = INTEL_SECONDARY_RING_BUFFER_0;
418 		secondary.size = B_PAGE_SIZE;
419 		secondary.base = info.graphics_memory + secondary.offset;
420 	}
421 
422 	// Fix some problems on certain chips (taken from X driver)
423 	// TODO: clean this up
424 	if (info.pci->device_id == 0x2a02 || info.pci->device_id == 0x2a12) {
425 		dprintf("i965GM/i965GME quirk\n");
426 		write32(info.registers + 0x6204, (1L << 29));
427 	} else {
428 		dprintf("i965 quirk\n");
429 		write32(info.registers + 0x6204, (1L << 29) | (1L << 23));
430 	}
431 	write32(info.registers + 0x7408, 0x10);
432 
433 	// no errors, so keep areas and mappings
434 	sharedCreator.Detach();
435 	additionalMemoryCreator.Detach();
436 	graphicsMapper.Detach();
437 	mmioMapper.Detach();
438 	gttMapper.Detach();
439 
440 	info.shared_info->graphics_memory_area = info.graphics_memory_area;
441 	info.shared_info->registers_area = info.registers_area;
442 	info.shared_info->graphics_memory = info.graphics_memory;
443 	info.shared_info->physical_graphics_memory = (uint8 *)info.pci->u.h0.base_registers[0];
444 
445 	info.shared_info->graphics_memory_size = totalSize;
446 	info.shared_info->frame_buffer_offset = 0;
447 	info.shared_info->dpms_mode = B_DPMS_ON;
448 
449 	if ((info.device_type & INTEL_TYPE_9xx) != 0) {
450 		info.shared_info->pll_info.reference_frequency = 96000;	// 96 kHz
451 		info.shared_info->pll_info.max_frequency = 400000;		// 400 MHz RAM DAC speed
452 		info.shared_info->pll_info.min_frequency = 20000;		// 20 MHz
453 	} else {
454 		info.shared_info->pll_info.reference_frequency = 48000;	// 48 kHz
455 		info.shared_info->pll_info.max_frequency = 350000;		// 350 MHz RAM DAC speed
456 		info.shared_info->pll_info.min_frequency = 25000;		// 25 MHz
457 	}
458 
459 	info.shared_info->pll_info.divisor_register = INTEL_DISPLAY_A_PLL_DIVISOR_0;
460 
461 	info.shared_info->device_type = info.device_type;
462 #ifdef __HAIKU__
463 	strlcpy(info.shared_info->device_identifier, info.device_identifier,
464 		sizeof(info.shared_info->device_identifier));
465 #else
466 	strcpy(info.shared_info->device_identifier, info.device_identifier);
467 #endif
468 
469 	// setup overlay registers
470 
471 	if (info.device_type == (INTEL_TYPE_9xx | INTEL_TYPE_G33)) {
472 		if (mem_alloc(info.memory_manager, B_PAGE_SIZE, &info,
473 				&info.overlay_handle, &info.overlay_offset) == B_OK) {
474 			info.overlay_registers = (overlay_registers *)(info.graphics_memory
475 				+ info.overlay_offset);
476 			info.shared_info->overlay_offset = info.overlay_offset;
477 		}
478 	} else {
479 		info.overlay_registers = (overlay_registers *)((uint8 *)info.shared_info
480 			+ ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info)));
481 	}
482 	init_overlay_registers(info.overlay_registers);
483 
484 	physical_entry physicalEntry;
485 	get_memory_map(info.overlay_registers, sizeof(overlay_registers), &physicalEntry, 1);
486 	info.shared_info->physical_overlay_registers = (uint8 *)physicalEntry.address;
487 
488 	// The hardware status page and the cursor memory share one area with
489 	// the overlay registers and the shared info
490 
491 	uint8 *base = (uint8 *)info.shared_info + ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info));
492 	get_memory_map(base + B_PAGE_SIZE, B_PAGE_SIZE, &physicalEntry, 1);
493 	info.shared_info->physical_status_page = (uint8 *)physicalEntry.address;
494 
495 	get_memory_map(base + 2 * B_PAGE_SIZE, B_PAGE_SIZE, &physicalEntry, 1);
496 	info.shared_info->physical_cursor_memory = (uint8 *)physicalEntry.address;
497 
498 	// setup the GTT to point to include the additional memory
499 
500 	if (stolenSize < totalSize) {
501 		for (size_t offset = stolenSize; offset < totalSize;) {
502 			physical_entry physicalEntry;
503 			get_memory_map(additionalMemory + offset - stolenSize,
504 				totalSize - offset, &physicalEntry, 1);
505 
506 			for (size_t i = 0; i < physicalEntry.size; i += B_PAGE_SIZE) {
507 				set_gtt_entry(info, offset + i, (uint8 *)physicalEntry.address + i);
508 			}
509 
510 			offset += physicalEntry.size;
511 		}
512 	}
513 
514 	// We also need to map the cursor memory into the GTT
515 
516 	info.shared_info->hardware_cursor_enabled = hardwareCursor;
517 	if (hardwareCursor) {
518 		// This could also be part of the usual graphics memory, but since we
519 		// need to make sure it's not in the graphics local memory (and I don't
520 		// even know yet how to determine that a chip has local memory...), we
521 		// keep the current strategy and put it into the shared area.
522 		// Unfortunately, the GTT is not readable until it has been written into
523 		// the double buffered register set; we cannot get its original contents.
524 
525 		set_gtt_entry(info, totalSize, info.shared_info->physical_cursor_memory);
526 		info.shared_info->cursor_buffer_offset = totalSize;
527 	}
528 
529 	init_interrupt_handler(info);
530 
531 	TRACE((DEVICE_NAME "intel_extreme_init() completed successfully!\n"));
532 	return B_OK;
533 }
534 
535 
536 void
537 intel_extreme_uninit(intel_info &info)
538 {
539 	TRACE((DEVICE_NAME": intel_extreme_uninit()\n"));
540 
541 	if (!info.fake_interrupts && info.shared_info->vblank_sem > 0) {
542 		// disable interrupt generation
543 		write16(info.registers + INTEL_INTERRUPT_ENABLED, 0);
544 		write16(info.registers + INTEL_INTERRUPT_MASK, ~0);
545 
546 		remove_io_interrupt_handler(info.pci->u.h0.interrupt_line,
547 			intel_interrupt_handler, &info);
548 	}
549 
550 	mem_destroy(info.memory_manager);
551 
552 	delete_area(info.graphics_memory_area);
553 	delete_area(info.gtt_area);
554 	delete_area(info.registers_area);
555 	delete_area(info.shared_area);
556 
557 	// we may or may not have allocated additional graphics memory
558 	if (info.additional_memory_area >= B_OK)
559 		delete_area(info.additional_memory_area);
560 }
561 
562