xref: /haiku/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp (revision bea66afaeb8d038d8918106a430a56b6e9fb3109)
1 /*
2  * Copyright 2006-2009, 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(bool &hardwareCursor)
46 {
47 	hardwareCursor = false;
48 
49 	void *settings = load_driver_settings("intel_extreme");
50 	if (settings != NULL) {
51 		hardwareCursor = get_driver_boolean_parameter(settings,
52 			"hardware_cursor", true, true);
53 
54 		unload_driver_settings(settings);
55 	}
56 }
57 
58 
59 static int32
60 release_vblank_sem(intel_info &info)
61 {
62 	int32 count;
63 	if (get_sem_count(info.shared_info->vblank_sem, &count) == B_OK
64 		&& count < 0) {
65 		release_sem_etc(info.shared_info->vblank_sem, -count,
66 			B_DO_NOT_RESCHEDULE);
67 		return B_INVOKE_SCHEDULER;
68 	}
69 
70 	return B_HANDLED_INTERRUPT;
71 }
72 
73 
74 static int32
75 intel_interrupt_handler(void *data)
76 {
77 	intel_info &info = *(intel_info *)data;
78 
79 	uint32 identity = read16(info.registers + INTEL_INTERRUPT_IDENTITY);
80 	if (identity == 0)
81 		return B_UNHANDLED_INTERRUPT;
82 
83 	int32 handled = B_HANDLED_INTERRUPT;
84 
85 	if ((identity & INTERRUPT_VBLANK) != 0) {
86 		handled = release_vblank_sem(info);
87 
88 		// make sure we'll get another one of those
89 		write32(info.registers + INTEL_DISPLAY_A_PIPE_STATUS,
90 			DISPLAY_PIPE_VBLANK_STATUS);
91 	}
92 
93 	// setting the bit clears it!
94 	write16(info.registers + INTEL_INTERRUPT_IDENTITY, identity);
95 
96 	return handled;
97 }
98 
99 
100 static void
101 init_interrupt_handler(intel_info &info)
102 {
103 	info.shared_info->vblank_sem = create_sem(0, "intel extreme vblank");
104 	if (info.shared_info->vblank_sem < B_OK)
105 		return;
106 
107 	status_t status = B_OK;
108 
109 	// We need to change the owner of the sem to the calling team (usually the
110 	// app_server), because userland apps cannot acquire kernel semaphores
111 	thread_id thread = find_thread(NULL);
112 	thread_info threadInfo;
113 	if (get_thread_info(thread, &threadInfo) != B_OK
114 		|| set_sem_owner(info.shared_info->vblank_sem, threadInfo.team) != B_OK) {
115 		status = B_ERROR;
116 	}
117 
118 	if (status == B_OK && info.pci->u.h0.interrupt_pin != 0x00
119 		&& info.pci->u.h0.interrupt_line != 0xff) {
120 		// we've gotten an interrupt line for us to use
121 
122 		info.fake_interrupts = false;
123 
124 		status = install_io_interrupt_handler(info.pci->u.h0.interrupt_line,
125 			&intel_interrupt_handler, (void *)&info, 0);
126 		if (status == B_OK) {
127 			// enable interrupts - we only want VBLANK interrupts
128 			write16(info.registers + INTEL_INTERRUPT_ENABLED,
129 				read16(info.registers + INTEL_INTERRUPT_ENABLED)
130 				| INTERRUPT_VBLANK);
131 			write16(info.registers + INTEL_INTERRUPT_MASK, ~INTERRUPT_VBLANK);
132 
133 			write32(info.registers + INTEL_DISPLAY_A_PIPE_STATUS,
134 				DISPLAY_PIPE_VBLANK_STATUS);
135 			write16(info.registers + INTEL_INTERRUPT_IDENTITY, ~0);
136 		}
137 	}
138 	if (status < B_OK) {
139 		// There is no interrupt reserved for us, or we couldn't install our
140 		// interrupt handler, let's fake the vblank interrupt for our clients
141 		// using a timer interrupt
142 		info.fake_interrupts = true;
143 
144 		// TODO: fake interrupts!
145 		status = B_ERROR;
146 	}
147 
148 	if (status < B_OK) {
149 		delete_sem(info.shared_info->vblank_sem);
150 		info.shared_info->vblank_sem = B_ERROR;
151 	}
152 }
153 
154 
155 //	#pragma mark -
156 
157 
158 status_t
159 intel_free_memory(intel_info &info, addr_t base)
160 {
161 	return gGART->free_memory(info.aperture, base);
162 }
163 
164 
165 status_t
166 intel_allocate_memory(intel_info &info, size_t size, size_t alignment,
167 	uint32 flags, addr_t *_base, addr_t *_physicalBase)
168 {
169 	return gGART->allocate_memory(info.aperture, size, alignment,
170 		flags, _base, _physicalBase);
171 }
172 
173 
174 status_t
175 intel_extreme_init(intel_info &info)
176 {
177 	info.aperture = gGART->map_aperture(info.pci->bus, info.pci->device,
178 		info.pci->function, 0, &info.aperture_base);
179 	if (info.aperture < B_OK)
180 		return info.aperture;
181 
182 	AreaKeeper sharedCreator;
183 	info.shared_area = sharedCreator.Create("intel extreme shared info",
184 		(void **)&info.shared_info, B_ANY_KERNEL_ADDRESS,
185 		ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info)) + 3 * B_PAGE_SIZE,
186 		B_FULL_LOCK, 0);
187 	if (info.shared_area < B_OK) {
188 		gGART->unmap_aperture(info.aperture);
189 		return info.shared_area;
190 	}
191 
192 	memset((void *)info.shared_info, 0, sizeof(intel_shared_info));
193 
194 	int fbIndex = 0;
195 	int mmioIndex = 1;
196 	if (info.device_type.InFamily(INTEL_TYPE_9xx)) {
197 		// For some reason Intel saw the need to change the order of the
198 		// mappings with the introduction of the i9xx family
199 		mmioIndex = 0;
200 		fbIndex = 2;
201 	}
202 
203 	// evaluate driver settings, if any
204 
205 	bool hardwareCursor;
206 	read_settings(hardwareCursor);
207 
208 	// memory mapped I/O
209 
210 	// TODO: registers are mapped twice (by us and intel_gart), maybe we
211 	// can share it between the drivers
212 
213 	AreaKeeper mmioMapper;
214 	info.registers_area = mmioMapper.Map("intel extreme mmio",
215 		(void *)info.pci->u.h0.base_registers[mmioIndex],
216 		info.pci->u.h0.base_register_sizes[mmioIndex],
217 		B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
218 		(void **)&info.registers);
219 	if (mmioMapper.InitCheck() < B_OK) {
220 		dprintf(DEVICE_NAME ": could not map memory I/O!\n");
221 		gGART->unmap_aperture(info.aperture);
222 		return info.registers_area;
223 	}
224 
225 	// make sure bus master, memory-mapped I/O, and frame buffer is enabled
226 	set_pci_config(info.pci, PCI_command, 2, get_pci_config(info.pci, PCI_command, 2)
227 		| PCI_command_io | PCI_command_memory | PCI_command_master);
228 
229 	// reserve ring buffer memory (currently, this memory is placed in
230 	// the graphics memory), but this could bring us problems with
231 	// write combining...
232 
233 	ring_buffer &primary = info.shared_info->primary_ring_buffer;
234 	if (intel_allocate_memory(info, 16 * B_PAGE_SIZE, 0, 0,
235 			(addr_t *)&primary.base) == B_OK) {
236 		primary.register_base = INTEL_PRIMARY_RING_BUFFER;
237 		primary.size = 16 * B_PAGE_SIZE;
238 		primary.offset = (addr_t)primary.base - info.aperture_base;
239 	}
240 
241 	// Clock gating
242 	// Fix some problems on certain chips (taken from X driver)
243 	// TODO: clean this up
244 	if (info.pci->device_id == 0x2a02 || info.pci->device_id == 0x2a12) {
245 		dprintf("i965GM/i965GME quirk\n");
246 		write32(info.registers + 0x6204, (1L << 29));
247 	} else if (info.device_type.InGroup(INTEL_TYPE_G4x)) {
248 		dprintf("G4x clock gating\n");
249 		write32(info.registers + 0x6204, 0);
250 		write32(info.registers + 0x6208, (1L << 9) | (1L << 7) | (1L << 6));
251 		write32(info.registers + 0x6210, 0);
252 
253 		uint32 dspclk_gate_val = (1L << 28) | (1L << 3) | (1L << 2);
254 		if ((info.device_type.type & INTEL_TYPE_MOBILE) == INTEL_TYPE_MOBILE) {
255 			dprintf("G4x mobile clock gating\n");
256 		    dspclk_gate_val |= 1L << 18;
257 		}
258 		write32(info.registers + 0x6200, dspclk_gate_val)	;
259 
260 	} else {
261 		dprintf("i965 quirk\n");
262 		write32(info.registers + 0x6204, (1L << 29) | (1L << 23));
263 	}
264 	write32(info.registers + 0x7408, 0x10);
265 
266 	// no errors, so keep areas and mappings
267 	sharedCreator.Detach();
268 	mmioMapper.Detach();
269 
270 	aperture_info apertureInfo;
271 	gGART->get_aperture_info(info.aperture, &apertureInfo);
272 
273 	info.shared_info->registers_area = info.registers_area;
274 	info.shared_info->graphics_memory = (uint8 *)info.aperture_base;
275 	info.shared_info->physical_graphics_memory = apertureInfo.physical_base;
276 	info.shared_info->graphics_memory_size = apertureInfo.size;
277 	info.shared_info->frame_buffer = 0;
278 	info.shared_info->dpms_mode = B_DPMS_ON;
279 
280 	if (info.device_type.InFamily(INTEL_TYPE_9xx)) {
281 		info.shared_info->pll_info.reference_frequency = 96000;	// 96 kHz
282 		info.shared_info->pll_info.max_frequency = 400000;
283 			// 400 MHz RAM DAC speed
284 		info.shared_info->pll_info.min_frequency = 20000;		// 20 MHz
285 	} else {
286 		info.shared_info->pll_info.reference_frequency = 48000;	// 48 kHz
287 		info.shared_info->pll_info.max_frequency = 350000;
288 			// 350 MHz RAM DAC speed
289 		info.shared_info->pll_info.min_frequency = 25000;		// 25 MHz
290 	}
291 
292 	info.shared_info->pll_info.divisor_register = INTEL_DISPLAY_A_PLL_DIVISOR_0;
293 
294 	info.shared_info->device_type = info.device_type;
295 #ifdef __HAIKU__
296 	strlcpy(info.shared_info->device_identifier, info.device_identifier,
297 		sizeof(info.shared_info->device_identifier));
298 #else
299 	strcpy(info.shared_info->device_identifier, info.device_identifier);
300 #endif
301 
302 	// setup overlay registers
303 
304 	if (intel_allocate_memory(info, B_PAGE_SIZE, 0,
305 			intel_uses_physical_overlay(*info.shared_info)
306 				? B_APERTURE_NEED_PHYSICAL : 0,
307 			(addr_t *)&info.overlay_registers,
308 			&info.shared_info->physical_overlay_registers) == B_OK) {
309 		info.shared_info->overlay_offset = (addr_t)info.overlay_registers
310 			- info.aperture_base;
311 	}
312 
313 	init_overlay_registers(info.overlay_registers);
314 
315 	// Allocate hardware status page and the cursor memory
316 
317 	if (intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL,
318 			(addr_t *)info.shared_info->status_page,
319 			&info.shared_info->physical_status_page) == B_OK) {
320 		// TODO: set status page
321 	}
322 	if (hardwareCursor) {
323 		intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL,
324 			(addr_t *)&info.shared_info->cursor_memory,
325 			&info.shared_info->physical_cursor_memory);
326 	}
327 
328 	init_interrupt_handler(info);
329 
330 	TRACE((DEVICE_NAME "intel_extreme_init() completed successfully!\n"));
331 	return B_OK;
332 }
333 
334 
335 void
336 intel_extreme_uninit(intel_info &info)
337 {
338 	TRACE((DEVICE_NAME": intel_extreme_uninit()\n"));
339 
340 	if (!info.fake_interrupts && info.shared_info->vblank_sem > 0) {
341 		// disable interrupt generation
342 		write16(info.registers + INTEL_INTERRUPT_ENABLED, 0);
343 		write16(info.registers + INTEL_INTERRUPT_MASK, ~0);
344 
345 		remove_io_interrupt_handler(info.pci->u.h0.interrupt_line,
346 			intel_interrupt_handler, &info);
347 	}
348 
349 	gGART->unmap_aperture(info.aperture);
350 
351 	delete_area(info.registers_area);
352 	delete_area(info.shared_area);
353 }
354 
355