xref: /haiku/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp (revision 29f8805f6c70f1c819eb58ac2220647d8e40d6e7)
1 /*
2  * Copyright 2006-2010, 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 <unistd.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <driver_settings.h>
18 #include <util/kernel_cpp.h>
19 #include "utility.h"
20 
21 #include "driver.h"
22 #include "power.h"
23 
24 
25 #define TRACE_INTELEXTREME
26 #ifdef TRACE_INTELEXTREME
27 #	define TRACE(x...) dprintf("intel_extreme: " x)
28 #else
29 #	define TRACE(x) ;
30 #endif
31 
32 #define ERROR(x...) dprintf("intel_extreme: " x)
33 #define CALLED(x...) TRACE("intel_extreme: CALLED %s\n", __PRETTY_FUNCTION__)
34 
35 
36 static void
37 init_overlay_registers(overlay_registers* registers)
38 {
39 	memset(registers, 0, B_PAGE_SIZE);
40 
41 	registers->contrast_correction = 0x48;
42 	registers->saturation_cos_correction = 0x9a;
43 		// this by-passes contrast and saturation correction
44 }
45 
46 
47 static void
48 read_settings(bool &hardwareCursor)
49 {
50 	hardwareCursor = false;
51 
52 	void* settings = load_driver_settings("intel_extreme");
53 	if (settings != NULL) {
54 		hardwareCursor = get_driver_boolean_parameter(settings,
55 			"hardware_cursor", true, true);
56 
57 		unload_driver_settings(settings);
58 	}
59 }
60 
61 
62 static int32
63 release_vblank_sem(intel_info &info)
64 {
65 	int32 count;
66 	if (get_sem_count(info.shared_info->vblank_sem, &count) == B_OK
67 		&& count < 0) {
68 		release_sem_etc(info.shared_info->vblank_sem, -count,
69 			B_DO_NOT_RESCHEDULE);
70 		return B_INVOKE_SCHEDULER;
71 	}
72 
73 	return B_HANDLED_INTERRUPT;
74 }
75 
76 
77 static int32
78 intel_interrupt_handler(void* data)
79 {
80 	intel_info &info = *(intel_info*)data;
81 
82 	uint16 identity = read16(info, find_reg(info, INTEL_INTERRUPT_IDENTITY));
83 	if (identity == 0)
84 		return B_UNHANDLED_INTERRUPT;
85 
86 	int32 handled = B_HANDLED_INTERRUPT;
87 
88 	// TODO: verify that these aren't actually the same
89 	bool hasPCH = info.device_type.HasPlatformControlHub();
90 	uint16 mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEA : INTERRUPT_VBLANK_PIPEA;
91 	if ((identity & mask) != 0) {
92 		handled = release_vblank_sem(info);
93 
94 		// make sure we'll get another one of those
95 		write32(info, INTEL_DISPLAY_A_PIPE_STATUS,
96 			DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED);
97 	}
98 
99 	mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEB : INTERRUPT_VBLANK_PIPEB;
100 	if ((identity & mask) != 0) {
101 		handled = release_vblank_sem(info);
102 
103 		// make sure we'll get another one of those
104 		write32(info, INTEL_DISPLAY_B_PIPE_STATUS,
105 			DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED);
106 	}
107 
108 	// setting the bit clears it!
109 	write16(info, find_reg(info, INTEL_INTERRUPT_IDENTITY), identity);
110 
111 	return handled;
112 }
113 
114 
115 static void
116 init_interrupt_handler(intel_info &info)
117 {
118 	info.shared_info->vblank_sem = create_sem(0, "intel extreme vblank");
119 	if (info.shared_info->vblank_sem < B_OK)
120 		return;
121 
122 	status_t status = B_OK;
123 
124 	// We need to change the owner of the sem to the calling team (usually the
125 	// app_server), because userland apps cannot acquire kernel semaphores
126 	thread_id thread = find_thread(NULL);
127 	thread_info threadInfo;
128 	if (get_thread_info(thread, &threadInfo) != B_OK
129 		|| set_sem_owner(info.shared_info->vblank_sem, threadInfo.team)
130 			!= B_OK) {
131 		status = B_ERROR;
132 	}
133 
134 	if (status == B_OK && info.pci->u.h0.interrupt_pin != 0x00
135 		&& info.pci->u.h0.interrupt_line != 0xff) {
136 		// we've gotten an interrupt line for us to use
137 
138 		info.fake_interrupts = false;
139 
140 		status = install_io_interrupt_handler(info.pci->u.h0.interrupt_line,
141 			&intel_interrupt_handler, (void*)&info, 0);
142 		if (status == B_OK) {
143 			write32(info, INTEL_DISPLAY_A_PIPE_STATUS,
144 				DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED);
145 			write32(info, INTEL_DISPLAY_B_PIPE_STATUS,
146 				DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED);
147 
148 			write16(info, find_reg(info, INTEL_INTERRUPT_IDENTITY), ~0);
149 
150 			// enable interrupts - we only want VBLANK interrupts
151 			bool hasPCH = info.device_type.HasPlatformControlHub();
152 			uint16 enable = hasPCH
153 				? (PCH_INTERRUPT_VBLANK_PIPEA | PCH_INTERRUPT_VBLANK_PIPEB)
154 				: (INTERRUPT_VBLANK_PIPEA | INTERRUPT_VBLANK_PIPEB);
155 
156 			write16(info, find_reg(info, INTEL_INTERRUPT_ENABLED), enable);
157 			write16(info, find_reg(info, INTEL_INTERRUPT_MASK), ~enable);
158 		}
159 	}
160 	if (status < B_OK) {
161 		// There is no interrupt reserved for us, or we couldn't install our
162 		// interrupt handler, let's fake the vblank interrupt for our clients
163 		// using a timer interrupt
164 		info.fake_interrupts = true;
165 
166 		// TODO: fake interrupts!
167 		TRACE("Fake interrupt mode (no PCI interrupt line assigned\n");
168 		status = B_ERROR;
169 	}
170 
171 	if (status < B_OK) {
172 		delete_sem(info.shared_info->vblank_sem);
173 		info.shared_info->vblank_sem = B_ERROR;
174 	}
175 }
176 
177 
178 //	#pragma mark -
179 
180 
181 status_t
182 intel_free_memory(intel_info &info, addr_t base)
183 {
184 	return gGART->free_memory(info.aperture, base);
185 }
186 
187 
188 status_t
189 intel_allocate_memory(intel_info &info, size_t size, size_t alignment,
190 	uint32 flags, addr_t* _base, phys_addr_t* _physicalBase)
191 {
192 	return gGART->allocate_memory(info.aperture, size, alignment,
193 		flags, _base, _physicalBase);
194 }
195 
196 
197 status_t
198 intel_extreme_init(intel_info &info)
199 {
200 	CALLED();
201 	info.aperture = gGART->map_aperture(info.pci->bus, info.pci->device,
202 		info.pci->function, 0, &info.aperture_base);
203 	if (info.aperture < B_OK) {
204 		ERROR("error: could not map GART aperture!\n");
205 		return info.aperture;
206 	}
207 
208 	AreaKeeper sharedCreator;
209 	info.shared_area = sharedCreator.Create("intel extreme shared info",
210 		(void**)&info.shared_info, B_ANY_KERNEL_ADDRESS,
211 		ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info)) + 3 * B_PAGE_SIZE,
212 		B_FULL_LOCK, 0);
213 	if (info.shared_area < B_OK) {
214 		ERROR("error: could not create shared area!\n");
215 		gGART->unmap_aperture(info.aperture);
216 		return info.shared_area;
217 	}
218 
219 	memset((void*)info.shared_info, 0, sizeof(intel_shared_info));
220 
221 	int fbIndex = 0;
222 	int mmioIndex = 1;
223 	if (info.device_type.InFamily(INTEL_TYPE_9xx)) {
224 		// For some reason Intel saw the need to change the order of the
225 		// mappings with the introduction of the i9xx family
226 		mmioIndex = 0;
227 		fbIndex = 2;
228 	}
229 
230 	// evaluate driver settings, if any
231 
232 	bool hardwareCursor;
233 	read_settings(hardwareCursor);
234 
235 	// memory mapped I/O
236 
237 	// TODO: registers are mapped twice (by us and intel_gart), maybe we
238 	// can share it between the drivers
239 
240 	AreaKeeper mmioMapper;
241 	info.registers_area = mmioMapper.Map("intel extreme mmio",
242 		info.pci->u.h0.base_registers[mmioIndex],
243 		info.pci->u.h0.base_register_sizes[mmioIndex],
244 		B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
245 		(void**)&info.registers);
246 	if (mmioMapper.InitCheck() < B_OK) {
247 		ERROR("error: could not map memory I/O!\n");
248 		gGART->unmap_aperture(info.aperture);
249 		return info.registers_area;
250 	}
251 
252 	uint32* blocks = info.shared_info->register_blocks;
253 	blocks[REGISTER_BLOCK(REGS_FLAT)] = 0;
254 
255 	// setup the register blocks for the different architectures
256 	if (info.device_type.HasPlatformControlHub()) {
257 		// PCH based platforms (IronLake and up)
258 		blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)]
259 			= PCH_NORTH_SHARED_REGISTER_BASE;
260 		blocks[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT)]
261 			= PCH_NORTH_PIPE_AND_PORT_REGISTER_BASE;
262 		blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)]
263 			= PCH_NORTH_PLANE_CONTROL_REGISTER_BASE;
264 		blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)]
265 			= PCH_SOUTH_SHARED_REGISTER_BASE;
266 		blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)]
267 			= PCH_SOUTH_TRANSCODER_AND_PORT_REGISTER_BASE;
268 	} else {
269 		// (G)MCH/ICH based platforms
270 		blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)]
271 			= MCH_SHARED_REGISTER_BASE;
272 		blocks[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT)]
273 			= MCH_PIPE_AND_PORT_REGISTER_BASE;
274 		blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)]
275 			= MCH_PLANE_CONTROL_REGISTER_BASE;
276 		blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)]
277 			= ICH_SHARED_REGISTER_BASE;
278 		blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)]
279 			= ICH_PORT_REGISTER_BASE;
280 	}
281 
282 	// make sure bus master, memory-mapped I/O, and frame buffer is enabled
283 	set_pci_config(info.pci, PCI_command, 2, get_pci_config(info.pci,
284 		PCI_command, 2) | PCI_command_io | PCI_command_memory
285 		| PCI_command_master);
286 
287 	// reserve ring buffer memory (currently, this memory is placed in
288 	// the graphics memory), but this could bring us problems with
289 	// write combining...
290 
291 	ring_buffer &primary = info.shared_info->primary_ring_buffer;
292 	if (intel_allocate_memory(info, 16 * B_PAGE_SIZE, 0, 0,
293 			(addr_t*)&primary.base) == B_OK) {
294 		primary.register_base = INTEL_PRIMARY_RING_BUFFER;
295 		primary.size = 16 * B_PAGE_SIZE;
296 		primary.offset = (addr_t)primary.base - info.aperture_base;
297 	}
298 
299 	// Enable clock gating
300 	intel_en_gating(info);
301 
302 	// Enable automatic gpu downclocking if we can to save power
303 	intel_en_downclock(info);
304 
305 	// no errors, so keep areas and mappings
306 	sharedCreator.Detach();
307 	mmioMapper.Detach();
308 
309 	aperture_info apertureInfo;
310 	gGART->get_aperture_info(info.aperture, &apertureInfo);
311 
312 	info.shared_info->registers_area = info.registers_area;
313 	info.shared_info->graphics_memory = (uint8*)info.aperture_base;
314 	info.shared_info->physical_graphics_memory = apertureInfo.physical_base;
315 	info.shared_info->graphics_memory_size = apertureInfo.size;
316 	info.shared_info->frame_buffer = 0;
317 	info.shared_info->dpms_mode = B_DPMS_ON;
318 
319 	if (info.device_type.InFamily(INTEL_TYPE_9xx)) {
320 		info.shared_info->pll_info.reference_frequency = 96000;	// 96 kHz
321 		info.shared_info->pll_info.max_frequency = 400000;
322 			// 400 MHz RAM DAC speed
323 		info.shared_info->pll_info.min_frequency = 20000;		// 20 MHz
324 	} else {
325 		info.shared_info->pll_info.reference_frequency = 48000;	// 48 kHz
326 		info.shared_info->pll_info.max_frequency = 350000;
327 			// 350 MHz RAM DAC speed
328 		info.shared_info->pll_info.min_frequency = 25000;		// 25 MHz
329 	}
330 
331 	info.shared_info->pll_info.divisor_register = INTEL_DISPLAY_A_PLL_DIVISOR_0;
332 
333 	info.shared_info->device_type = info.device_type;
334 #ifdef __HAIKU__
335 	strlcpy(info.shared_info->device_identifier, info.device_identifier,
336 		sizeof(info.shared_info->device_identifier));
337 #else
338 	strcpy(info.shared_info->device_identifier, info.device_identifier);
339 #endif
340 
341 	// setup overlay registers
342 
343 	if (intel_allocate_memory(info, B_PAGE_SIZE, 0,
344 			intel_uses_physical_overlay(*info.shared_info)
345 				? B_APERTURE_NEED_PHYSICAL : 0,
346 			(addr_t*)&info.overlay_registers,
347 			&info.shared_info->physical_overlay_registers) == B_OK) {
348 		info.shared_info->overlay_offset = (addr_t)info.overlay_registers
349 			- info.aperture_base;
350 	}
351 
352 	init_overlay_registers(info.overlay_registers);
353 
354 	// Allocate hardware status page and the cursor memory
355 
356 	if (intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL,
357 			(addr_t*)info.shared_info->status_page,
358 			&info.shared_info->physical_status_page) == B_OK) {
359 		// TODO: set status page
360 	}
361 	if (hardwareCursor) {
362 		intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL,
363 			(addr_t*)&info.shared_info->cursor_memory,
364 			&info.shared_info->physical_cursor_memory);
365 	}
366 
367 	init_interrupt_handler(info);
368 
369 	TRACE("%s: completed successfully!\n", __func__);
370 	return B_OK;
371 }
372 
373 
374 void
375 intel_extreme_uninit(intel_info &info)
376 {
377 	CALLED();
378 
379 	if (!info.fake_interrupts && info.shared_info->vblank_sem > 0) {
380 		// disable interrupt generation
381 		write16(info, find_reg(info, INTEL_INTERRUPT_ENABLED), 0);
382 		write16(info, find_reg(info, INTEL_INTERRUPT_MASK), ~0);
383 
384 		remove_io_interrupt_handler(info.pci->u.h0.interrupt_line,
385 			intel_interrupt_handler, &info);
386 	}
387 
388 	gGART->unmap_aperture(info.aperture);
389 
390 	delete_area(info.registers_area);
391 	delete_area(info.shared_area);
392 }
393 
394