xref: /haiku/src/add-ons/accelerants/intel_extreme/accelerant.cpp (revision 87f4776937505e3014251c9c3434be78ae29d7d0)
1 /*
2  * Copyright 2006-2016, 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 <new>
23 
24 #include <AGP.h>
25 #include <AutoDeleterOS.h>
26 
27 
28 #undef TRACE
29 #define TRACE_ACCELERANT
30 #ifdef TRACE_ACCELERANT
31 #	define TRACE(x...) _sPrintf("intel_extreme: " x)
32 #else
33 #	define TRACE(x...)
34 #endif
35 
36 #define ERROR(x...) _sPrintf("intel_extreme: " x)
37 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
38 
39 
40 struct accelerant_info* gInfo;
41 uint32 gDumpCount;
42 
43 
44 //	#pragma mark -
45 
46 
47 // intel_reg --mmio=ie-0001.bin --devid=27a2 dump
48 void
49 dump_registers()
50 {
51 	char filename[255];
52 
53 	sprintf(filename, "/boot/system/cache/tmp/ie-%04" B_PRId32 ".bin",
54 		gDumpCount);
55 
56 	ERROR("%s: Taking register dump #%" B_PRId32 "\n", __func__, gDumpCount);
57 
58 	int fd = open(filename, O_CREAT | O_WRONLY, 0644);
59 	uint32 data = 0;
60 	if (fd >= 0) {
61 		for (int32 i = 0; i < 0x80000; i += sizeof(data)) {
62 			//char line[512];
63 			//int length = sprintf(line, "%05" B_PRIx32 ": "
64 			//	"%08" B_PRIx32 " %08" B_PRIx32 " %08" B_PRIx32 " %08" B_PRIx32 "\n",
65 			//	i, read32(i), read32(i + 4), read32(i + 8), read32(i + 12));
66 			data = read32(i);
67 			write(fd, &data, sizeof(data));
68 		}
69 		close(fd);
70 		sync();
71 	}
72 
73 	gDumpCount++;
74 }
75 
76 
77 /*! This is the common accelerant_info initializer. It is called by
78 	both, the first accelerant and all clones.
79 */
80 static status_t
81 init_common(int device, bool isClone)
82 {
83 	// initialize global accelerant info structure
84 
85 	// Number of register dumps we have... taken.
86 	gDumpCount = 0;
87 
88 	gInfo = (accelerant_info*)malloc(sizeof(accelerant_info));
89 	if (gInfo == NULL)
90 		return B_NO_MEMORY;
91 	MemoryDeleter infoDeleter(gInfo);
92 
93 	memset(gInfo, 0, sizeof(accelerant_info));
94 
95 	gInfo->is_clone = isClone;
96 	gInfo->device = device;
97 
98 	// get basic info from driver
99 
100 	intel_get_private_data data;
101 	data.magic = INTEL_PRIVATE_DATA_MAGIC;
102 
103 	if (ioctl(device, INTEL_GET_PRIVATE_DATA, &data,
104 			sizeof(intel_get_private_data)) != 0)
105 		return B_ERROR;
106 
107 	AreaDeleter sharedDeleter(clone_area("intel extreme shared info",
108 		(void**)&gInfo->shared_info, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA,
109 		data.shared_info_area));
110 	status_t status = gInfo->shared_info_area = sharedDeleter.Get();
111 	if (status < B_OK)
112 		return status;
113 
114 	AreaDeleter regsDeleter(clone_area("intel extreme regs",
115 		(void**)&gInfo->registers, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA,
116 		gInfo->shared_info->registers_area));
117 	status = gInfo->regs_area = regsDeleter.Get();
118 	if (status < B_OK)
119 		return status;
120 
121 	infoDeleter.Detach();
122 	sharedDeleter.Detach();
123 	regsDeleter.Detach();
124 
125 	// The overlay registers, hardware status, and cursor memory share
126 	// a single area with the shared_info
127 
128 	if (gInfo->shared_info->overlay_offset != 0) {
129 		gInfo->overlay_registers = (struct overlay_registers*)
130 			(gInfo->shared_info->graphics_memory
131 			+ gInfo->shared_info->overlay_offset);
132 	}
133 
134 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) {
135 		// allocate some extra memory for the 3D context
136 		if (intel_allocate_memory(INTEL_i965_3D_CONTEXT_SIZE,
137 				B_APERTURE_NON_RESERVED, gInfo->context_base) == B_OK) {
138 			gInfo->context_offset = gInfo->context_base
139 				- (addr_t)gInfo->shared_info->graphics_memory;
140 		}
141 	}
142 
143 	gInfo->pipe_count = 0;
144 
145 	// Allocate all of our pipes
146 	for (int i = 0; i < MAX_PIPES; i++) {
147 		switch (i) {
148 			case 0:
149 				gInfo->pipes[i] = new(std::nothrow) Pipe(INTEL_PIPE_A);
150 				break;
151 			case 1:
152 				gInfo->pipes[i] = new(std::nothrow) Pipe(INTEL_PIPE_B);
153 				break;
154 			default:
155 				ERROR("%s: Unknown pipe %d\n", __func__, i);
156 		}
157 		if (gInfo->pipes[i] == NULL)
158 			ERROR("%s: Error allocating pipe %d\n", __func__, i);
159 		else
160 			gInfo->pipe_count++;
161 	}
162 
163 	return B_OK;
164 }
165 
166 
167 /*! Clean up data common to both primary and cloned accelerant */
168 static void
169 uninit_common(void)
170 {
171 	intel_free_memory(gInfo->context_base);
172 
173 	delete_area(gInfo->regs_area);
174 	delete_area(gInfo->shared_info_area);
175 
176 	gInfo->regs_area = gInfo->shared_info_area = -1;
177 
178 	// close the file handle ONLY if we're the clone
179 	if (gInfo->is_clone)
180 		close(gInfo->device);
181 
182 	free(gInfo);
183 }
184 
185 
186 static void
187 dump_ports()
188 {
189 	if (gInfo->port_count == 0) {
190 		TRACE("%s: No ports connected\n", __func__);
191 		return;
192 	}
193 
194 	TRACE("%s: Connected ports: (port_count: %" B_PRIu32 ")\n", __func__,
195 		gInfo->port_count);
196 
197 	for (uint32 i = 0; i < gInfo->port_count; i++) {
198 		Port* port = gInfo->ports[i];
199 		if (!port) {
200 			TRACE("port %" B_PRIu32 ":: INVALID ALLOC!\n", i);
201 			continue;
202 		}
203 		TRACE("port %" B_PRIu32 ": %s %s\n", i, port->PortName(),
204 			port->IsConnected() ? "connected" : "disconnected");
205 	}
206 }
207 
208 
209 static bool
210 has_connected_port(port_index portIndex, uint32 type)
211 {
212 	for (uint32 i = 0; i < gInfo->port_count; i++) {
213 		Port* port = gInfo->ports[i];
214 		if (type != INTEL_PORT_TYPE_ANY && port->Type() != type)
215 			continue;
216 		if (portIndex != INTEL_PORT_ANY && port->PortIndex() != portIndex)
217 			continue;
218 
219 		return true;
220 	}
221 
222 	return false;
223 }
224 
225 
226 static status_t
227 probe_ports()
228 {
229 	// Try to determine what ports to use. We use the following heuristic:
230 	// * Check for DisplayPort, these can be more or less detected reliably.
231 	// * Check for HDMI, it'll fail on devices not having HDMI for us to fall
232 	//   back to DVI.
233 	// * Assume DVI B if no HDMI and no DisplayPort is present, confirmed by
234 	//   reading EDID in the IsConnected() call.
235 	// * Check for analog if possible (there's a detection bit on PCH),
236 	//   otherwise the assumed presence is confirmed by reading EDID in
237 	//   IsConnected().
238 
239 	TRACE("adpa: %08" B_PRIx32 "\n", read32(INTEL_ANALOG_PORT));
240 	TRACE("dova: %08" B_PRIx32 ", dovb: %08" B_PRIx32
241 		", dovc: %08" B_PRIx32 "\n", read32(INTEL_DIGITAL_PORT_A),
242 		read32(INTEL_DIGITAL_PORT_B), read32(INTEL_DIGITAL_PORT_C));
243 	TRACE("lvds: %08" B_PRIx32 "\n", read32(INTEL_DIGITAL_LVDS_PORT));
244 
245 	bool foundLVDS = false;
246 
247 	gInfo->port_count = 0;
248 	for (int i = INTEL_PORT_A; i <= INTEL_PORT_D; i++) {
249 		TRACE("Probing DisplayPort %d\n", i);
250 		Port* displayPort = new(std::nothrow) DisplayPort((port_index)i);
251 		if (displayPort == NULL)
252 			return B_NO_MEMORY;
253 
254 		if (displayPort->IsConnected())
255 			gInfo->ports[gInfo->port_count++] = displayPort;
256 		else
257 			delete displayPort;
258 	}
259 
260 	// Digital Display Interface
261 	if (gInfo->shared_info->device_type.HasDDI()) {
262 		for (int i = INTEL_PORT_A; i <= INTEL_PORT_E; i++) {
263 			TRACE("Probing DDI %d\n", i);
264 
265 			Port* ddiPort
266 				= new(std::nothrow) DigitalDisplayInterface((port_index)i);
267 
268 			if (ddiPort == NULL)
269 				return B_NO_MEMORY;
270 
271 			if (ddiPort->IsConnected())
272 				gInfo->ports[gInfo->port_count++] = ddiPort;
273 			else
274 				delete ddiPort;
275 		}
276 	}
277 
278 	// Ensure DP_A isn't already taken (or DDI)
279 	TRACE("Probing eDP\n");
280 	if (!has_connected_port((port_index)INTEL_PORT_A, INTEL_PORT_TYPE_ANY)) {
281 		// also always try eDP, it'll also just fail if not applicable
282 		Port* eDPPort = new(std::nothrow) EmbeddedDisplayPort();
283 		if (eDPPort == NULL)
284 			return B_NO_MEMORY;
285 		if (eDPPort->IsConnected())
286 			gInfo->ports[gInfo->port_count++] = eDPPort;
287 		else
288 			delete eDPPort;
289 	}
290 
291 	for (int i = INTEL_PORT_B; i <= INTEL_PORT_D; i++) {
292 		TRACE("Probing HDMI %d\n", i);
293 		if (has_connected_port((port_index)i, INTEL_PORT_TYPE_ANY)) {
294 			// Ensure port not already claimed by something like DDI
295 			continue;
296 		}
297 
298 		Port* hdmiPort = new(std::nothrow) HDMIPort((port_index)i);
299 		if (hdmiPort == NULL)
300 			return B_NO_MEMORY;
301 
302 		if (hdmiPort->IsConnected())
303 			gInfo->ports[gInfo->port_count++] = hdmiPort;
304 		else
305 			delete hdmiPort;
306 	}
307 
308 	// always try the LVDS port, it'll simply fail if not applicable
309 	TRACE("Probing LVDS\n");
310 	Port* lvdsPort = new(std::nothrow) LVDSPort();
311 	if (lvdsPort == NULL)
312 		return B_NO_MEMORY;
313 	if (lvdsPort->IsConnected()) {
314 		foundLVDS = true;
315 		gInfo->ports[gInfo->port_count++] = lvdsPort;
316 		gInfo->head_mode |= HEAD_MODE_LVDS_PANEL;
317 		gInfo->head_mode |= HEAD_MODE_B_DIGITAL;
318 	} else
319 		delete lvdsPort;
320 
321 	TRACE("Probing DVI\n");
322 	if (!has_connected_port(INTEL_PORT_ANY, INTEL_PORT_TYPE_ANY)) {
323 		// there's neither DisplayPort nor HDMI so far, assume DVI B
324 		for (port_index index = INTEL_PORT_B; index <= INTEL_PORT_C;
325 				index = (port_index)(index + 1)) {
326 			Port* dviPort = new(std::nothrow) DigitalPort(index, "DVI");
327 			if (dviPort == NULL)
328 				return B_NO_MEMORY;
329 
330 			if (dviPort->IsConnected()) {
331 				gInfo->ports[gInfo->port_count++] = dviPort;
332 				gInfo->head_mode |= HEAD_MODE_B_DIGITAL;
333 			} else
334 				delete dviPort;
335 		}
336 	}
337 
338 	// then finally always try the analog port
339 	TRACE("Probing Analog\n");
340 	Port* analogPort = new(std::nothrow) AnalogPort();
341 	if (analogPort == NULL)
342 		return B_NO_MEMORY;
343 	if (analogPort->IsConnected()) {
344 		gInfo->ports[gInfo->port_count++] = analogPort;
345 		gInfo->head_mode |= HEAD_MODE_A_ANALOG;
346 	} else
347 		delete analogPort;
348 
349 	if (gInfo->port_count == 0)
350 		return B_ERROR;
351 
352 	// Activate reference clocks if needed
353 	if (gInfo->shared_info->pch_info == INTEL_PCH_IBX
354 		|| gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
355 		TRACE("Activating clocks\n");
356 		// XXX: Is LVDS the same as Panel?
357 		refclk_activate_ilk(foundLVDS);
358 	}
359 	/*
360 	} else if (gInfo->shared_info->pch_info == INTEL_PCH_LPT) {
361 		// TODO: Some kind of stepped bend thing?
362 		// only needed for vga
363 		refclk_activate_lpt(foundLVDS);
364 	}
365 	*/
366 
367 	TRACE("Probing complete.\n");
368 	return B_OK;
369 }
370 
371 
372 static status_t
373 assign_pipes()
374 {
375 	// TODO: At some point we should "group" ports to pipes with the same mode.
376 	// You can drive multiple ports from a single pipe as long as the mode is
377 	// the same. For the moment we could get displays with the wrong pipes
378 	// assigned when the count is > 1;
379 
380 	uint32 current = 0;
381 
382 	bool assigned[gInfo->pipe_count];
383 	memset(assigned, 0, gInfo->pipe_count);
384 
385 	// Some ports need to be assigned to a fixed pipe on old hardware (or due
386 	// to limitations in the current driver on current hardware). Assign those
387 	// first
388 	for (uint32 i = 0; i < gInfo->port_count; i++) {
389 		if (gInfo->ports[i] == NULL)
390 			continue;
391 
392 		pipe_index preference = gInfo->ports[i]->PipePreference();
393 		if (preference != INTEL_PIPE_ANY) {
394 			int index = (preference == INTEL_PIPE_B) ? 1 : 0;
395 			if (assigned[index]) {
396 				TRACE("Pipe %d is already assigned, it will drive multiple "
397 					"displays\n", index);
398 			}
399 			gInfo->ports[i]->SetPipe(gInfo->pipes[index]);
400 			assigned[index] = true;
401 			continue;
402 		}
403 	}
404 
405 	// In a second pass, assign the remaining ports to the remaining pipes
406 	for (uint32 i = 0; i < gInfo->port_count; i++) {
407 		if (gInfo->ports[i]->IsConnected()) {
408 			while (current < gInfo->pipe_count && assigned[current])
409 				current++;
410 
411 			if (current >= gInfo->pipe_count) {
412 				ERROR("%s: No pipes left to assign to port %s!\n", __func__,
413 					gInfo->ports[i]->PortName());
414 				continue;
415 			}
416 
417 			gInfo->ports[i]->SetPipe(gInfo->pipes[current]);
418 		}
419 	}
420 
421 	return B_OK;
422 }
423 
424 
425 //	#pragma mark - public accelerant functions
426 
427 
428 /*! Init primary accelerant */
429 status_t
430 intel_init_accelerant(int device)
431 {
432 	CALLED();
433 
434 	status_t status = init_common(device, false);
435 	if (status != B_OK)
436 		return status;
437 
438 	intel_shared_info &info = *gInfo->shared_info;
439 
440 	init_lock(&info.accelerant_lock, "intel extreme accelerant");
441 	init_lock(&info.engine_lock, "intel extreme engine");
442 
443 	setup_ring_buffer(info.primary_ring_buffer, "intel primary ring buffer");
444 
445 	// Probe all ports
446 	status = probe_ports();
447 
448 	// On TRACE, dump ports and states
449 	dump_ports();
450 
451 	if (status != B_OK)
452 		ERROR("Warning: zero active displays were found!\n");
453 
454 	status = assign_pipes();
455 
456 	if (status != B_OK)
457 		ERROR("Warning: error while assigning pipes!\n");
458 
459 	status = create_mode_list();
460 	if (status != B_OK) {
461 		uninit_common();
462 		return status;
463 	}
464 
465 	return B_OK;
466 }
467 
468 
469 ssize_t
470 intel_accelerant_clone_info_size(void)
471 {
472 	CALLED();
473 	// clone info is device name, so return its maximum size
474 	return B_PATH_NAME_LENGTH;
475 }
476 
477 
478 void
479 intel_get_accelerant_clone_info(void* info)
480 {
481 	CALLED();
482 	ioctl(gInfo->device, INTEL_GET_DEVICE_NAME, info, B_PATH_NAME_LENGTH);
483 }
484 
485 
486 status_t
487 intel_clone_accelerant(void* info)
488 {
489 	CALLED();
490 
491 	// create full device name
492 	char path[B_PATH_NAME_LENGTH];
493 	strcpy(path, "/dev/");
494 #ifdef __HAIKU__
495 	strlcat(path, (const char*)info, sizeof(path));
496 #else
497 	strcat(path, (const char*)info);
498 #endif
499 
500 	int fd = open(path, B_READ_WRITE);
501 	if (fd < 0)
502 		return errno;
503 
504 	status_t status = init_common(fd, true);
505 	if (status != B_OK)
506 		goto err1;
507 
508 	// get read-only clone of supported display modes
509 	status = gInfo->mode_list_area = clone_area(
510 		"intel extreme cloned modes", (void**)&gInfo->mode_list,
511 		B_ANY_ADDRESS, B_READ_AREA, gInfo->shared_info->mode_list_area);
512 	if (status < B_OK)
513 		goto err2;
514 
515 	return B_OK;
516 
517 err2:
518 	uninit_common();
519 err1:
520 	close(fd);
521 	return status;
522 }
523 
524 
525 /*! This function is called for both, the primary accelerant and all of
526 	its clones.
527 */
528 void
529 intel_uninit_accelerant(void)
530 {
531 	CALLED();
532 
533 	// delete accelerant instance data
534 	delete_area(gInfo->mode_list_area);
535 	gInfo->mode_list = NULL;
536 
537 	intel_shared_info &info = *gInfo->shared_info;
538 
539 	uninit_lock(&info.accelerant_lock);
540 	uninit_lock(&info.engine_lock);
541 
542 	uninit_ring_buffer(info.primary_ring_buffer);
543 
544 	uninit_common();
545 }
546 
547 
548 status_t
549 intel_get_accelerant_device_info(accelerant_device_info* info)
550 {
551 	CALLED();
552 
553 	info->version = B_ACCELERANT_VERSION;
554 
555 	DeviceType* type = &gInfo->shared_info->device_type;
556 
557 	if (type->InFamily(INTEL_FAMILY_8xx))
558 		strcpy(info->name, "Intel Extreme");
559 	else if (type->InFamily(INTEL_FAMILY_9xx))
560 		strcpy(info->name, "Intel GMA");
561 	else if (type->InFamily(INTEL_FAMILY_SOC0))
562 		strcpy(info->name, "Intel Atom");
563 	else if (type->InFamily(INTEL_FAMILY_SER5))
564 		strcpy(info->name, "Intel HD/Iris");
565 	else
566 		strcpy(info->name, "Intel");
567 
568 	strcpy(info->chipset, gInfo->shared_info->device_identifier);
569 	strcpy(info->serial_no, "None");
570 
571 	info->memory = gInfo->shared_info->graphics_memory_size;
572 	info->dac_speed = gInfo->shared_info->pll_info.max_frequency;
573 
574 	return B_OK;
575 }
576 
577 
578 sem_id
579 intel_accelerant_retrace_semaphore()
580 {
581 	CALLED();
582 	return gInfo->shared_info->vblank_sem;
583 }
584