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