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 "accelerant_protos.h"
11#include "accelerant.h"
12
13#include "utility.h"
14
15#include <Debug.h>
16#include <errno.h>
17#include <stdlib.h>
18#include <string.h>
19#include <unistd.h>
20#include <syslog.h>
21
22#include <AGP.h>
23
24
25#undef TRACE
26#define TRACE_ACCELERANT
27#ifdef TRACE_ACCELERANT
28#	define TRACE(x...) _sPrintf("intel_extreme accelerant:" x)
29#else
30#	define TRACE(x...)
31#endif
32
33#define ERROR(x...) _sPrintf("intel_extreme accelerant: " x)
34#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
35
36
37struct accelerant_info* gInfo;
38
39
40class AreaCloner {
41public:
42							AreaCloner();
43							~AreaCloner();
44
45			area_id			Clone(const char* name, void** _address,
46								uint32 spec, uint32 protection,
47								area_id sourceArea);
48			status_t		InitCheck()
49								{ return fArea < 0 ? (status_t)fArea : B_OK; }
50			void			Keep();
51
52private:
53			area_id			fArea;
54};
55
56
57AreaCloner::AreaCloner()
58	:
59	fArea(-1)
60{
61}
62
63
64AreaCloner::~AreaCloner()
65{
66	if (fArea >= 0)
67		delete_area(fArea);
68}
69
70
71area_id
72AreaCloner::Clone(const char* name, void** _address, uint32 spec,
73	uint32 protection, area_id sourceArea)
74{
75	fArea = clone_area(name, _address, spec, protection, sourceArea);
76	return fArea;
77}
78
79
80void
81AreaCloner::Keep()
82{
83	fArea = -1;
84}
85
86
87//	#pragma mark -
88
89
90/*! This is the common accelerant_info initializer. It is called by
91	both, the first accelerant and all clones.
92*/
93static status_t
94init_common(int device, bool isClone)
95{
96	// initialize global accelerant info structure
97
98	gInfo = (accelerant_info*)malloc(sizeof(accelerant_info));
99	if (gInfo == NULL)
100		return B_NO_MEMORY;
101
102	memset(gInfo, 0, sizeof(accelerant_info));
103
104	gInfo->is_clone = isClone;
105	gInfo->device = device;
106
107	// get basic info from driver
108
109	intel_get_private_data data;
110	data.magic = INTEL_PRIVATE_DATA_MAGIC;
111
112	if (ioctl(device, INTEL_GET_PRIVATE_DATA, &data,
113			sizeof(intel_get_private_data)) != 0) {
114		free(gInfo);
115		return B_ERROR;
116	}
117
118	AreaCloner sharedCloner;
119	gInfo->shared_info_area = sharedCloner.Clone("intel extreme shared info",
120		(void**)&gInfo->shared_info, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA,
121		data.shared_info_area);
122	status_t status = sharedCloner.InitCheck();
123	if (status < B_OK) {
124		free(gInfo);
125		return status;
126	}
127
128	AreaCloner regsCloner;
129	gInfo->regs_area = regsCloner.Clone("intel extreme regs",
130		(void**)&gInfo->registers, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA,
131		gInfo->shared_info->registers_area);
132	status = regsCloner.InitCheck();
133	if (status < B_OK) {
134		free(gInfo);
135		return status;
136	}
137
138	sharedCloner.Keep();
139	regsCloner.Keep();
140
141	// The overlay registers, hardware status, and cursor memory share
142	// a single area with the shared_info
143
144	gInfo->overlay_registers = (struct overlay_registers*)
145		(gInfo->shared_info->graphics_memory
146		+ gInfo->shared_info->overlay_offset);
147
148	if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) {
149		// allocate some extra memory for the 3D context
150		if (intel_allocate_memory(INTEL_i965_3D_CONTEXT_SIZE,
151				B_APERTURE_NON_RESERVED, gInfo->context_base) == B_OK) {
152			gInfo->context_offset = gInfo->context_base
153				- (addr_t)gInfo->shared_info->graphics_memory;
154		}
155	}
156
157	return B_OK;
158}
159
160
161/*! Clean up data common to both primary and cloned accelerant */
162static void
163uninit_common(void)
164{
165	intel_free_memory(gInfo->context_base);
166
167	delete_area(gInfo->regs_area);
168	delete_area(gInfo->shared_info_area);
169
170	gInfo->regs_area = gInfo->shared_info_area = -1;
171
172	// close the file handle ONLY if we're the clone
173	if (gInfo->is_clone)
174		close(gInfo->device);
175
176	free(gInfo);
177}
178
179
180//	#pragma mark - public accelerant functions
181
182
183/*! Init primary accelerant */
184status_t
185intel_init_accelerant(int device)
186{
187	CALLED();
188
189	status_t status = init_common(device, false);
190	if (status != B_OK)
191		return status;
192
193	intel_shared_info &info = *gInfo->shared_info;
194
195	init_lock(&info.accelerant_lock, "intel extreme accelerant");
196	init_lock(&info.engine_lock, "intel extreme engine");
197
198	setup_ring_buffer(info.primary_ring_buffer, "intel primary ring buffer");
199
200	// determine head depending on what's already enabled from the BIOS
201	// TODO: it would be nicer to retrieve this data via DDC - else the
202	//	display is gone for good if the BIOS decides to only show the
203	//	picture on the connected analog monitor!
204	gInfo->head_mode = 0;
205	if (read32(INTEL_DISPLAY_B_PIPE_CONTROL) & DISPLAY_PIPE_ENABLED)
206		gInfo->head_mode |= HEAD_MODE_B_DIGITAL;
207	if (read32(INTEL_DISPLAY_A_PIPE_CONTROL) & DISPLAY_PIPE_ENABLED)
208		gInfo->head_mode |= HEAD_MODE_A_ANALOG;
209
210	uint32 lvds = read32(INTEL_DISPLAY_LVDS_PORT);
211
212	// If we have an enabled display pipe we save the passed information and
213	// assume it is the valid panel size..
214	// Later we query for proper EDID info if it exists, or figure something
215	// else out. (Default modes, etc.)
216	bool hasPCH = gInfo->shared_info->device_type.HasPlatformControlHub();
217	if ((hasPCH && (lvds & PCH_LVDS_DETECTED) != 0)
218		|| (!hasPCH && (lvds & DISPLAY_PIPE_ENABLED) != 0)) {
219		save_lvds_mode();
220		gInfo->head_mode |= HEAD_MODE_LVDS_PANEL;
221	}
222
223	TRACE("head detected: %#x\n", gInfo->head_mode);
224	TRACE("adpa: %08lx, dova: %08lx, dovb: %08lx, lvds: %08lx\n",
225		read32(INTEL_DISPLAY_A_ANALOG_PORT),
226		read32(INTEL_DISPLAY_A_DIGITAL_PORT),
227		read32(INTEL_DISPLAY_B_DIGITAL_PORT),
228		read32(INTEL_DISPLAY_LVDS_PORT));
229
230	status = create_mode_list();
231	if (status != B_OK) {
232		uninit_common();
233		return status;
234	}
235
236	return B_OK;
237}
238
239
240ssize_t
241intel_accelerant_clone_info_size(void)
242{
243	CALLED();
244	// clone info is device name, so return its maximum size
245	return B_PATH_NAME_LENGTH;
246}
247
248
249void
250intel_get_accelerant_clone_info(void* info)
251{
252	CALLED();
253	ioctl(gInfo->device, INTEL_GET_DEVICE_NAME, info, B_PATH_NAME_LENGTH);
254}
255
256
257status_t
258intel_clone_accelerant(void* info)
259{
260	CALLED();
261
262	// create full device name
263	char path[B_PATH_NAME_LENGTH];
264	strcpy(path, "/dev/");
265#ifdef __HAIKU__
266	strlcat(path, (const char*)info, sizeof(path));
267#else
268	strcat(path, (const char*)info);
269#endif
270
271	int fd = open(path, B_READ_WRITE);
272	if (fd < 0)
273		return errno;
274
275	status_t status = init_common(fd, true);
276	if (status != B_OK)
277		goto err1;
278
279	// get read-only clone of supported display modes
280	status = gInfo->mode_list_area = clone_area(
281		"intel extreme cloned modes", (void**)&gInfo->mode_list,
282		B_ANY_ADDRESS, B_READ_AREA, gInfo->shared_info->mode_list_area);
283	if (status < B_OK)
284		goto err2;
285
286	return B_OK;
287
288err2:
289	uninit_common();
290err1:
291	close(fd);
292	return status;
293}
294
295
296/*! This function is called for both, the primary accelerant and all of
297	its clones.
298*/
299void
300intel_uninit_accelerant(void)
301{
302	CALLED();
303
304	// delete accelerant instance data
305	delete_area(gInfo->mode_list_area);
306	gInfo->mode_list = NULL;
307
308	intel_shared_info &info = *gInfo->shared_info;
309
310	uninit_lock(&info.accelerant_lock);
311	uninit_lock(&info.engine_lock);
312
313	uninit_ring_buffer(info.primary_ring_buffer);
314
315	uninit_common();
316}
317
318
319status_t
320intel_get_accelerant_device_info(accelerant_device_info* info)
321{
322	CALLED();
323
324	info->version = B_ACCELERANT_VERSION;
325	strcpy(info->name, gInfo->shared_info->device_type.InFamily(INTEL_TYPE_7xx)
326		? "Intel Extreme Graphics 1" : "Intel Extreme Graphics 2");
327	strcpy(info->chipset, gInfo->shared_info->device_identifier);
328	strcpy(info->serial_no, "None");
329
330	info->memory = gInfo->shared_info->graphics_memory_size;
331	info->dac_speed = gInfo->shared_info->pll_info.max_frequency;
332
333	return B_OK;
334}
335
336
337sem_id
338intel_accelerant_retrace_semaphore()
339{
340	CALLED();
341	return gInfo->shared_info->vblank_sem;
342}
343
344