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
36static void
37init_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
47static void
48read_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
62static int32
63release_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
77static int32
78intel_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
115static void
116init_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
181status_t
182intel_free_memory(intel_info &info, addr_t base)
183{
184	return gGART->free_memory(info.aperture, base);
185}
186
187
188status_t
189intel_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
197status_t
198intel_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		(void*)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
374void
375intel_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