xref: /haiku/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp (revision e39da397f5ff79f2db9f9a3ddf1852b6710578af)
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 = 0x40;
90 	registers->saturation_cos_correction = 0x40;
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_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_85x) {
156 		switch (memoryConfig & STOLEN_MEMORY_MASK) {
157 			case i855_STOLEN_MEMORY_4M:
158 				memorySize *= 4;
159 				break;
160 			case i855_STOLEN_MEMORY_8M:
161 				memorySize *= 8;
162 				break;
163 			case i855_STOLEN_MEMORY_16M:
164 				memorySize *= 16;
165 				break;
166 			case i855_STOLEN_MEMORY_32M:
167 				memorySize *= 32;
168 				break;
169 			case i855_STOLEN_MEMORY_48M:
170 				memorySize *= 48;
171 				break;
172 			case i855_STOLEN_MEMORY_64M:
173 				memorySize *= 64;
174 				break;
175 		}
176 	}
177 
178 	return memorySize;
179 }
180 
181 
182 static void
183 set_gtt_entry(intel_info &info, uint32 offset, uint8 *physicalAddress)
184 {
185 	write32(info.registers + INTEL_GTT_BASE + (offset >> 10),
186 		(uint32)physicalAddress | GTT_ENTRY_VALID);
187 }
188 
189 
190 static int32
191 release_vblank_sem(intel_info &info)
192 {
193 	int32 count;
194 	if (get_sem_count(info.shared_info->vblank_sem, &count) == B_OK
195 		&& count < 0) {
196 		release_sem_etc(info.shared_info->vblank_sem, -count, B_DO_NOT_RESCHEDULE);
197 		return B_INVOKE_SCHEDULER;
198 	}
199 
200 	return B_HANDLED_INTERRUPT;
201 }
202 
203 
204 static int32
205 intel_interrupt_handler(void *data)
206 {
207 	intel_info &info = *(intel_info *)data;
208 
209 	uint32 identity = read16(info.registers + INTEL_INTERRUPT_IDENTITY);
210 	if (identity == 0)
211 		return B_UNHANDLED_INTERRUPT;
212 
213 	int32 handled = B_HANDLED_INTERRUPT;
214 
215 	if ((identity & INTERRUPT_VBLANK) != 0) {
216 		handled = release_vblank_sem(info);
217 
218 		// make sure we'll get another one of those
219 		write32(info.registers + INTEL_DISPLAY_A_PIPE_STATUS, DISPLAY_PIPE_VBLANK_STATUS);
220 	}
221 
222 	// setting the bit clears it!
223 	write16(info.registers + INTEL_INTERRUPT_IDENTITY, identity);
224 
225 	return handled;
226 }
227 
228 
229 static void
230 init_interrupt_handler(intel_info &info)
231 {
232 	info.shared_info->vblank_sem = create_sem(0, "intel extreme vblank");
233 	if (info.shared_info->vblank_sem < B_OK)
234 		return;
235 
236 	status_t status = B_OK;
237 
238 	// We need to change the owner of the sem to the calling team (usually the
239 	// app_server), because userland apps cannot acquire kernel semaphores
240 	thread_id thread = find_thread(NULL);
241 	thread_info threadInfo;
242 	if (get_thread_info(thread, &threadInfo) != B_OK
243 		|| set_sem_owner(info.shared_info->vblank_sem, threadInfo.team) != B_OK) {
244 		status = B_ERROR;
245 	}
246 
247 	if (status == B_OK
248 		&& info.pci->u.h0.interrupt_pin != 0x00 && info.pci->u.h0.interrupt_line != 0xff) {
249 		// we've gotten an interrupt line for us to use
250 
251 		info.fake_interrupts = false;
252 
253 		status = install_io_interrupt_handler(info.pci->u.h0.interrupt_line,
254 			&intel_interrupt_handler, (void *)&info, 0);
255 		if (status == B_OK) {
256 			// enable interrupts - we only want VBLANK interrupts
257 			write16(info.registers + INTEL_INTERRUPT_ENABLED,
258 				read16(info.registers + INTEL_INTERRUPT_ENABLED) | INTERRUPT_VBLANK);
259 			write16(info.registers + INTEL_INTERRUPT_MASK, ~INTERRUPT_VBLANK);
260 
261 			write32(info.registers + INTEL_DISPLAY_A_PIPE_STATUS,
262 				DISPLAY_PIPE_VBLANK_STATUS);
263 			write16(info.registers + INTEL_INTERRUPT_IDENTITY, ~0);
264 		}
265 	}
266 	if (status < B_OK) {
267 		// there is no interrupt reserved for us, or we couldn't install our interrupt
268 		// handler, let's fake the vblank interrupt for our clients using a timer
269 		// interrupt
270 		info.fake_interrupts = true;
271 
272 		// TODO: fake interrupts!
273 		status = B_ERROR;
274 	}
275 
276 	if (status < B_OK) {
277 		delete_sem(info.shared_info->vblank_sem);
278 		info.shared_info->vblank_sem = B_ERROR;
279 	}
280 }
281 
282 
283 //	#pragma mark -
284 
285 
286 status_t
287 intel_extreme_init(intel_info &info)
288 {
289 	AreaKeeper sharedCreator;
290 	info.shared_area = sharedCreator.Create("intel extreme shared info",
291 		(void **)&info.shared_info, B_ANY_KERNEL_ADDRESS,
292 		ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info)) + 3 * B_PAGE_SIZE,
293 		B_FULL_LOCK, 0);
294 	if (info.shared_area < B_OK)
295 		return info.shared_area;
296 
297 	memset((void *)info.shared_info, 0, sizeof(intel_shared_info));
298 
299 	// evaluate driver settings, if any
300 
301 	bool ignoreBIOSAllocated;
302 	bool hardwareCursor;
303 	size_t totalSize;
304 	read_settings(totalSize, hardwareCursor, ignoreBIOSAllocated);
305 
306 	// Determine the amount of "stolen" (ie. reserved by the BIOS) graphics memory
307 	// and see if we need to allocate some more.
308 	// TODO: make it allocate the memory on demand!
309 
310 	size_t stolenSize = ignoreBIOSAllocated ? 0 : determine_stolen_memory_size(info);
311 	totalSize = max_c(totalSize, stolenSize);
312 
313 	dprintf(DEVICE_NAME ": detected %ld MB of stolen memory, reserving %ld MB total\n",
314 		stolenSize >> 20, totalSize >> 20);
315 
316 	AreaKeeper additionalMemoryCreator;
317 	uint8 *additionalMemory;
318 
319 	if (stolenSize < totalSize) {
320 		// Every device should have at least 8 MB - we could also allocate them
321 		// on demand only, but we're lazy here...
322 		// TODO: overlay seems to have problem when the memory pages are too
323 		//	far spreaded - that's why we're using B_CONTIGUOUS for now.
324 		info.additional_memory_area = additionalMemoryCreator.Create("intel additional memory",
325 			(void **)&additionalMemory, B_ANY_KERNEL_ADDRESS,
326 			totalSize - stolenSize, B_CONTIGUOUS /*B_FULL_LOCK*/, 0);
327 		if (info.additional_memory_area < B_OK)
328 			return info.additional_memory_area;
329 	} else
330 		info.additional_memory_area = B_ERROR;
331 
332 	// map frame buffer, try to map it write combined
333 
334 	AreaKeeper graphicsMapper;
335 	info.graphics_memory_area = graphicsMapper.Map("intel extreme graphics memory",
336 		(void *)info.pci->u.h0.base_registers[0],
337 		totalSize, B_ANY_KERNEL_BLOCK_ADDRESS | B_MTR_WC,
338 		B_READ_AREA | B_WRITE_AREA, (void **)&info.graphics_memory);
339 	if (graphicsMapper.InitCheck() < B_OK) {
340 		// try again without write combining
341 		dprintf(DEVICE_NAME ": enabling write combined mode failed.\n");
342 
343 		info.graphics_memory_area = graphicsMapper.Map("intel extreme graphics memory",
344 			(void *)info.pci->u.h0.base_registers[0],
345 			totalSize/*info.pci->u.h0.base_register_sizes[0]*/, B_ANY_KERNEL_BLOCK_ADDRESS,
346 			B_READ_AREA | B_WRITE_AREA, (void **)&info.graphics_memory);
347 	}
348 	if (graphicsMapper.InitCheck() < B_OK) {
349 		dprintf(DEVICE_NAME ": could not map frame buffer!\n");
350 		return info.graphics_memory_area;
351 	}
352 
353 	// memory mapped I/O
354 
355 	AreaKeeper mmioMapper;
356 	info.registers_area = mmioMapper.Map("intel extreme mmio",
357 		(void *)info.pci->u.h0.base_registers[1],
358 		info.pci->u.h0.base_register_sizes[1],
359 		B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA /*0*/, (void **)&info.registers);
360 	if (mmioMapper.InitCheck() < B_OK) {
361 		dprintf(DEVICE_NAME ": could not map memory I/O!\n");
362 		return info.registers_area;
363 	}
364 
365 	// init graphics memory manager
366 
367 	info.memory_manager = mem_init("intel extreme memory manager", 0, totalSize, 1024,
368 		min_c(totalSize / 1024, 512));
369 	if (info.memory_manager == NULL)
370 		return B_NO_MEMORY;
371 
372 	// reserve ring buffer memory (currently, this memory is placed in
373 	// the graphics memory), but this could bring us problems with
374 	// write combining...
375 
376 	ring_buffer &primary = info.shared_info->primary_ring_buffer;
377 	if (mem_alloc(info.memory_manager, 4 * B_PAGE_SIZE, &info,
378 			&primary.handle, &primary.offset) == B_OK) {
379 		primary.register_base = INTEL_PRIMARY_RING_BUFFER;
380 		primary.size = 4 * B_PAGE_SIZE;
381 		primary.base = info.graphics_memory + primary.offset;
382 	}
383 
384 	ring_buffer &secondary = info.shared_info->secondary_ring_buffer;
385 	if (mem_alloc(info.memory_manager, B_PAGE_SIZE, &info,
386 			&secondary.handle, &secondary.offset) == B_OK) {
387 		secondary.register_base = INTEL_SECONDARY_RING_BUFFER_0;
388 		secondary.size = B_PAGE_SIZE;
389 		secondary.base = info.graphics_memory + secondary.offset;
390 	}
391 
392 	// no errors, so keep areas and mappings
393 	sharedCreator.Detach();
394 	additionalMemoryCreator.Detach();
395 	graphicsMapper.Detach();
396 	mmioMapper.Detach();
397 
398 	info.shared_info->graphics_memory_area = info.graphics_memory_area;
399 	info.shared_info->registers_area = info.registers_area;
400 	info.shared_info->graphics_memory = info.graphics_memory;
401 	info.shared_info->physical_graphics_memory = (uint8 *)info.pci->u.h0.base_registers[0];
402 
403 	info.shared_info->graphics_memory_size = totalSize;
404 	info.shared_info->frame_buffer_offset = 0;
405 	info.shared_info->dpms_mode = B_DPMS_ON;
406 	info.shared_info->pll_info.reference_frequency = 48000;	// 48 kHz
407 	info.shared_info->pll_info.min_frequency = 25000;		// 25 MHz (not tested)
408 	info.shared_info->pll_info.max_frequency = 350000;		// 350 MHz RAM DAC speed
409 	info.shared_info->pll_info.divisor_register = INTEL_DISPLAY_A_PLL_DIVISOR_0;
410 
411 	info.shared_info->device_type = info.device_type;
412 #ifdef __HAIKU__
413 	strlcpy(info.shared_info->device_identifier, info.device_identifier,
414 		sizeof(info.shared_info->device_identifier));
415 #else
416 	strcpy(info.shared_info->device_identifier, info.device_identifier);
417 #endif
418 
419 	// setup overlay registers
420 
421 	info.overlay_registers = (overlay_registers *)((uint8 *)info.shared_info
422 		+ ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info)));
423 	init_overlay_registers(info.overlay_registers);
424 
425 	physical_entry physicalEntry;
426 	get_memory_map(info.overlay_registers, sizeof(overlay_registers), &physicalEntry, 1);
427 	info.shared_info->physical_overlay_registers = (uint8 *)physicalEntry.address;
428 
429 	// The hardware status page and the cursor memory share one area with
430 	// the overlay registers and the shared info
431 
432 	get_memory_map((uint8 *)info.overlay_registers + B_PAGE_SIZE,
433 		B_PAGE_SIZE, &physicalEntry, 1);
434 	info.shared_info->physical_status_page = (uint8 *)physicalEntry.address;
435 
436 	get_memory_map((uint8 *)info.overlay_registers + 2 * B_PAGE_SIZE,
437 		B_PAGE_SIZE, &physicalEntry, 1);
438 	info.shared_info->physical_cursor_memory = (uint8 *)physicalEntry.address;
439 
440 	// setup the GTT to point to include the additional memory
441 
442 	if (stolenSize < totalSize) {
443 		for (size_t offset = stolenSize; offset < totalSize;) {
444 			physical_entry physicalEntry;
445 			get_memory_map(additionalMemory + offset - stolenSize,
446 				totalSize - offset, &physicalEntry, 1);
447 
448 			for (size_t i = 0; i < physicalEntry.size; i += B_PAGE_SIZE) {
449 				set_gtt_entry(info, offset + i, (uint8 *)physicalEntry.address + i);
450 			}
451 
452 			offset += physicalEntry.size;
453 		}
454 	}
455 
456 	// We also need to map the cursor memory into the GTT
457 
458 	info.shared_info->hardware_cursor_enabled = hardwareCursor;
459 	if (hardwareCursor) {
460 		// This could also be part of the usual graphics memory, but since we
461 		// need to make sure it's not in the graphics local memory (and I don't
462 		// even know yet how to determine that a chip has local memory...), we
463 		// keep the current strategy and put it into the shared area.
464 		// Unfortunately, the GTT is not readable until it has been written into
465 		// the double buffered register set; we cannot get its original contents.
466 
467 		set_gtt_entry(info, totalSize, info.shared_info->physical_cursor_memory);
468 		info.shared_info->cursor_buffer_offset = totalSize;
469 	}
470 
471 	init_interrupt_handler(info);
472 
473 	info.cookie_magic = INTEL_COOKIE_MAGIC;
474 		// this makes the cookie valid to be used
475 
476 	dprintf(DEVICE_NAME "intel_extreme_init() completed successfully!\n");
477 
478 	return B_OK;
479 }
480 
481 
482 void
483 intel_extreme_uninit(intel_info &info)
484 {
485 	dprintf(DEVICE_NAME": intel_extreme_uninit()\n");
486 
487 	if (!info.fake_interrupts && info.shared_info->vblank_sem > 0) {
488 		// disable interrupt generation
489 		write16(info.registers + INTEL_INTERRUPT_ENABLED, 0);
490 		write16(info.registers + INTEL_INTERRUPT_MASK, ~0);
491 
492 		remove_io_interrupt_handler(info.pci->u.h0.interrupt_line,
493 			intel_interrupt_handler, &info);
494 	}
495 
496 	mem_destroy(info.memory_manager);
497 
498 	delete_area(info.graphics_memory_area);
499 	delete_area(info.registers_area);
500 	delete_area(info.shared_area);
501 
502 	// we may or may not have allocated additional graphics memory
503 	if (info.additional_memory_area >= B_OK)
504 		delete_area(info.additional_memory_area);
505 }
506 
507