xref: /haiku/src/add-ons/accelerants/intel_extreme/accelerant.cpp (revision 52f7c9389475e19fc21487b38064b4390eeb6fea)
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 	int pipeCnt = 2;
147 	if (gInfo->shared_info->device_type.Generation() >= 7)
148 		pipeCnt = 3; // some newer gens have even more..
149 
150 	for (int i = 0; i < pipeCnt; i++) {
151 		switch (i) {
152 			case 0:
153 				gInfo->pipes[i] = new(std::nothrow) Pipe(INTEL_PIPE_A);
154 				break;
155 			case 1:
156 				gInfo->pipes[i] = new(std::nothrow) Pipe(INTEL_PIPE_B);
157 				break;
158 			case 2:
159 				gInfo->pipes[i] = new(std::nothrow) Pipe(INTEL_PIPE_C);
160 				break;
161 			default:
162 				ERROR("%s: Unknown pipe %d\n", __func__, i);
163 		}
164 		if (gInfo->pipes[i] == NULL)
165 			ERROR("%s: Error allocating pipe %d\n", __func__, i);
166 		else
167 			gInfo->pipe_count++;
168 	}
169 
170 	return B_OK;
171 }
172 
173 
174 /*! Clean up data common to both primary and cloned accelerant */
175 static void
176 uninit_common(void)
177 {
178 	intel_free_memory(gInfo->context_base);
179 
180 	delete_area(gInfo->regs_area);
181 	delete_area(gInfo->shared_info_area);
182 
183 	gInfo->regs_area = gInfo->shared_info_area = -1;
184 
185 	// close the file handle ONLY if we're the clone
186 	if (gInfo->is_clone)
187 		close(gInfo->device);
188 
189 	free(gInfo);
190 }
191 
192 
193 static void
194 dump_ports()
195 {
196 	if (gInfo->port_count == 0) {
197 		TRACE("%s: No ports connected\n", __func__);
198 		return;
199 	}
200 
201 	TRACE("%s: Connected ports: (port_count: %" B_PRIu32 ")\n", __func__,
202 		gInfo->port_count);
203 
204 	for (uint32 i = 0; i < gInfo->port_count; i++) {
205 		Port* port = gInfo->ports[i];
206 		if (!port) {
207 			TRACE("port %" B_PRIu32 ":: INVALID ALLOC!\n", i);
208 			continue;
209 		}
210 		TRACE("port %" B_PRIu32 ": %s %s\n", i, port->PortName(),
211 			port->IsConnected() ? "connected" : "disconnected");
212 	}
213 }
214 
215 
216 static bool
217 has_connected_port(port_index portIndex, uint32 type)
218 {
219 	for (uint32 i = 0; i < gInfo->port_count; i++) {
220 		Port* port = gInfo->ports[i];
221 		if (type != INTEL_PORT_TYPE_ANY && port->Type() != type)
222 			continue;
223 		if (portIndex != INTEL_PORT_ANY && port->PortIndex() != portIndex)
224 			continue;
225 
226 		return true;
227 	}
228 
229 	return false;
230 }
231 
232 
233 static status_t
234 probe_ports()
235 {
236 	// Try to determine what ports to use. We use the following heuristic:
237 	// * Check for DisplayPort, these can be more or less detected reliably.
238 	// * Check for HDMI, it'll fail on devices not having HDMI for us to fall
239 	//   back to DVI.
240 	// * Assume DVI B if no HDMI and no DisplayPort is present, confirmed by
241 	//   reading EDID in the IsConnected() call.
242 	// * Check for analog if possible (there's a detection bit on PCH),
243 	//   otherwise the assumed presence is confirmed by reading EDID in
244 	//   IsConnected().
245 
246 	TRACE("adpa: %08" B_PRIx32 "\n", read32(INTEL_ANALOG_PORT));
247 	TRACE("dova: %08" B_PRIx32 ", dovb: %08" B_PRIx32
248 		", dovc: %08" B_PRIx32 "\n", read32(INTEL_DIGITAL_PORT_A),
249 		read32(INTEL_DIGITAL_PORT_B), read32(INTEL_DIGITAL_PORT_C));
250 	TRACE("lvds: %08" B_PRIx32 "\n", read32(INTEL_DIGITAL_LVDS_PORT));
251 
252 	TRACE("dp_a: %08" B_PRIx32 "\n", read32(INTEL_DISPLAY_PORT_A));
253 	TRACE("dp_b: %08" B_PRIx32 "\n", read32(INTEL_DISPLAY_PORT_B));
254 	TRACE("dp_c: %08" B_PRIx32 "\n", read32(INTEL_DISPLAY_PORT_C));
255 	TRACE("dp_d: %08" B_PRIx32 "\n", read32(INTEL_DISPLAY_PORT_D));
256 	TRACE("tra_dp: %08" B_PRIx32 "\n", read32(INTEL_TRANSCODER_A_DP_CTL));
257 	TRACE("trb_dp: %08" B_PRIx32 "\n", read32(INTEL_TRANSCODER_B_DP_CTL));
258 	TRACE("trc_dp: %08" B_PRIx32 "\n", read32(INTEL_TRANSCODER_C_DP_CTL));
259 
260 	bool foundLVDS = false;
261 	bool foundDP = false;
262 	bool foundDDI = false;
263 
264 	gInfo->port_count = 0;
265 #if 0
266 	// make sure I2C hardware controller is off (we use bit-banging)
267 	if (gInfo->shared_info->device_type.Generation() >= 5) {
268 		write32(INTEL_DSPCLK_GATE_D,
269 			read32(INTEL_DSPCLK_GATE_D) | PCH_GMBUSUNIT_CLK_GATE_DIS);
270 		read32(INTEL_DSPCLK_GATE_D);
271 
272 		write32(INTEL_GEN9_CLKGATE_DIS_4,
273 			read32(INTEL_GEN9_CLKGATE_DIS_4) | BXT_GMBUSUNIT_CLK_GATE_DIS);
274 		read32(INTEL_GEN9_CLKGATE_DIS_4);
275 
276 		write32(INTEL_GMBUS0, 0); //reset, idle
277 		write32(INTEL_GMBUS4, 0); //block interrupts
278 	}
279 #endif
280 
281 	// Display Port
282 	if (!gInfo->shared_info->device_type.HasDDI()) {
283 		for (int i = INTEL_PORT_A; i <= INTEL_PORT_D; i++) {
284 			TRACE("Probing DisplayPort %d\n", i);
285 			Port* displayPort = new(std::nothrow) DisplayPort((port_index)i);
286 			if (displayPort == NULL)
287 				return B_NO_MEMORY;
288 
289 			if (displayPort->IsConnected()) {
290 				foundDP = true;
291 				gInfo->ports[gInfo->port_count++] = displayPort;
292 			} else
293 				delete displayPort;
294 		}
295 	}
296 
297 	// Digital Display Interface (for DP, HDMI, DVI and eDP)
298 	if (gInfo->shared_info->device_type.HasDDI()) {
299 		for (int i = INTEL_PORT_B; i <= INTEL_PORT_F; i++) {
300 			TRACE("Probing DDI %d\n", i);
301 
302 			Port* ddiPort
303 				= new(std::nothrow) DigitalDisplayInterface((port_index)i);
304 
305 			if (ddiPort == NULL)
306 				return B_NO_MEMORY;
307 
308 			if (ddiPort->IsConnected()) {
309 				foundDDI = true;
310 				gInfo->ports[gInfo->port_count++] = ddiPort;
311 			} else
312 				delete ddiPort;
313 		}
314 	}
315 
316 #if 0
317 	// never execute this as the 'standard' DisplayPort class called above already handles it.
318 	if (!gInfo->shared_info->device_type.HasDDI()) {
319 		// Ensure DP_A isn't already taken
320 		TRACE("Probing eDP\n");
321 		if (!has_connected_port((port_index)INTEL_PORT_A, INTEL_PORT_TYPE_ANY)) {
322 			// also always try eDP, it'll also just fail if not applicable
323 			Port* eDPPort = new(std::nothrow) EmbeddedDisplayPort();
324 			if (eDPPort == NULL)
325 				return B_NO_MEMORY;
326 			if (eDPPort->IsConnected())
327 				gInfo->ports[gInfo->port_count++] = eDPPort;
328 			else
329 				delete eDPPort;
330 		}
331 	}
332 #endif
333 
334 	if (!gInfo->shared_info->device_type.HasDDI()) {
335 		for (int i = INTEL_PORT_B; i <= INTEL_PORT_D; i++) {
336 			TRACE("Probing HDMI %d\n", i);
337 			if (has_connected_port((port_index)i, INTEL_PORT_TYPE_ANY)) {
338 				// Ensure port not already claimed by something like DDI
339 				TRACE("Port already claimed\n");
340 				continue;
341 			}
342 
343 			Port* hdmiPort = new(std::nothrow) HDMIPort((port_index)i);
344 			if (hdmiPort == NULL)
345 				return B_NO_MEMORY;
346 
347 			if (hdmiPort->IsConnected())
348 				gInfo->ports[gInfo->port_count++] = hdmiPort;
349 			else
350 				delete hdmiPort;
351 		}
352 	}
353 
354 	// always try the LVDS port when chipset supports it, it'll simply fail if not applicable
355 	if (!gInfo->shared_info->device_type.HasDDI()) {
356 		TRACE("Probing LVDS\n");
357 		Port* lvdsPort = new(std::nothrow) LVDSPort();
358 		if (lvdsPort == NULL)
359 			return B_NO_MEMORY;
360 		if (lvdsPort->IsConnected()) {
361 			foundLVDS = true;
362 			gInfo->ports[gInfo->port_count++] = lvdsPort;
363 			gInfo->head_mode |= HEAD_MODE_LVDS_PANEL;
364 			gInfo->head_mode |= HEAD_MODE_B_DIGITAL;
365 		} else
366 			delete lvdsPort;
367 	}
368 
369 	if (!gInfo->shared_info->device_type.HasDDI()) {
370 		if (!has_connected_port(INTEL_PORT_ANY, INTEL_PORT_TYPE_ANY)) {
371 			TRACE("Probing DVI\n");
372 			// there's neither DisplayPort nor HDMI so far, assume DVI B
373 			for (port_index index = INTEL_PORT_B; index <= INTEL_PORT_C;
374 					index = (port_index)(index + 1)) {
375 				Port* dviPort = new(std::nothrow) DigitalPort(index, "DVI");
376 				if (dviPort == NULL)
377 					return B_NO_MEMORY;
378 
379 				if (dviPort->IsConnected()) {
380 					gInfo->ports[gInfo->port_count++] = dviPort;
381 					gInfo->head_mode |= HEAD_MODE_B_DIGITAL;
382 				} else
383 					delete dviPort;
384 			}
385 		}
386 	}
387 
388 	// then finally always try the analog port when chipsets supports it
389 	if (gInfo->shared_info->device_type.Generation() <= 8) {
390 		TRACE("Probing Analog\n");
391 		Port* analogPort = new(std::nothrow) AnalogPort();
392 		if (analogPort == NULL)
393 			return B_NO_MEMORY;
394 		if (analogPort->IsConnected()) {
395 			gInfo->ports[gInfo->port_count++] = analogPort;
396 			gInfo->head_mode |= HEAD_MODE_A_ANALOG;
397 		} else
398 			delete analogPort;
399 	}
400 
401 	if (gInfo->port_count == 0)
402 		return B_ERROR;
403 
404 	// Activate reference clocks if needed
405 	if (gInfo->shared_info->pch_info == INTEL_PCH_IBX
406 		|| gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
407 		TRACE("Activating clocks\n");
408 		refclk_activate_ilk(foundLVDS || foundDP || foundDDI);
409 	}
410 	/*
411 	} else if (gInfo->shared_info->pch_info == INTEL_PCH_LPT) {
412 		// TODO: Some kind of stepped bend thing?
413 		// only needed for vga
414 		refclk_activate_lpt(foundLVDS);
415 	}
416 	*/
417 
418 	TRACE("Probing complete.\n");
419 	return B_OK;
420 }
421 
422 
423 static status_t
424 assign_pipes()
425 {
426 	// TODO: At some point we should "group" ports to pipes with the same mode.
427 	// You can drive multiple ports from a single pipe as long as the mode is
428 	// the same. For the moment we could get displays with the wrong pipes
429 	// assigned when the count is > 1;
430 
431 	uint32 current = 0;
432 
433 	bool assigned[gInfo->pipe_count];
434 	memset(assigned, 0, gInfo->pipe_count);
435 
436 	// Some ports need to be assigned to a fixed pipe on old hardware (or due
437 	// to limitations in the current driver on current hardware). Assign those
438 	// first
439 	for (uint32 i = 0; i < gInfo->port_count; i++) {
440 		if (!gInfo->ports[i]->IsConnected())
441 			continue;
442 
443 		pipe_index preference = gInfo->ports[i]->PipePreference();
444 		if (preference != INTEL_PIPE_ANY) {
445 			int index = (int)preference - 1;
446 			if (assigned[index]) {
447 				TRACE("Pipe %d is already assigned, it will drive multiple "
448 					"displays\n", index);
449 			}
450 			gInfo->ports[i]->SetPipe(gInfo->pipes[index]);
451 			assigned[index] = true;
452 			continue;
453 		}
454 	}
455 
456 	// In a second pass, assign the remaining ports to the remaining pipes
457 	for (uint32 i = 0; i < gInfo->port_count; i++) {
458 		if (gInfo->ports[i]->IsConnected() && gInfo->ports[i]->GetPipe() == NULL) {
459 			while (current < gInfo->pipe_count && assigned[current])
460 				current++;
461 
462 			if (current >= gInfo->pipe_count) {
463 				ERROR("%s: No pipes left to assign to port %s!\n", __func__,
464 					gInfo->ports[i]->PortName());
465 				continue;
466 			}
467 
468 			gInfo->ports[i]->SetPipe(gInfo->pipes[current]);
469 			assigned[current] = true;
470 		}
471 	}
472 
473 	return B_OK;
474 }
475 
476 
477 //	#pragma mark - public accelerant functions
478 
479 
480 /*! Init primary accelerant */
481 status_t
482 intel_init_accelerant(int device)
483 {
484 	CALLED();
485 
486 	status_t status = init_common(device, false);
487 	if (status != B_OK)
488 		return status;
489 
490 	intel_shared_info &info = *gInfo->shared_info;
491 
492 	init_lock(&info.accelerant_lock, "intel extreme accelerant");
493 	init_lock(&info.engine_lock, "intel extreme engine");
494 
495 	setup_ring_buffer(info.primary_ring_buffer, "intel primary ring buffer");
496 
497 	// Probe all ports
498 	status = probe_ports();
499 
500 	// On TRACE, dump ports and states
501 	dump_ports();
502 
503 	if (status != B_OK)
504 		ERROR("Warning: zero active displays were found!\n");
505 
506 	status = assign_pipes();
507 
508 	if (status != B_OK)
509 		ERROR("Warning: error while assigning pipes!\n");
510 
511 	status = create_mode_list();
512 	if (status != B_OK) {
513 		uninit_common();
514 		return status;
515 	}
516 
517 	return B_OK;
518 }
519 
520 
521 ssize_t
522 intel_accelerant_clone_info_size(void)
523 {
524 	CALLED();
525 	// clone info is device name, so return its maximum size
526 	return B_PATH_NAME_LENGTH;
527 }
528 
529 
530 void
531 intel_get_accelerant_clone_info(void* info)
532 {
533 	CALLED();
534 	ioctl(gInfo->device, INTEL_GET_DEVICE_NAME, info, B_PATH_NAME_LENGTH);
535 }
536 
537 
538 status_t
539 intel_clone_accelerant(void* info)
540 {
541 	CALLED();
542 
543 	// create full device name
544 	char path[B_PATH_NAME_LENGTH];
545 	strcpy(path, "/dev/");
546 #ifdef __HAIKU__
547 	strlcat(path, (const char*)info, sizeof(path));
548 #else
549 	strcat(path, (const char*)info);
550 #endif
551 
552 	int fd = open(path, B_READ_WRITE);
553 	if (fd < 0)
554 		return errno;
555 
556 	status_t status = init_common(fd, true);
557 	if (status != B_OK)
558 		goto err1;
559 
560 	// get read-only clone of supported display modes
561 	status = gInfo->mode_list_area = clone_area(
562 		"intel extreme cloned modes", (void**)&gInfo->mode_list,
563 		B_ANY_ADDRESS, B_READ_AREA, gInfo->shared_info->mode_list_area);
564 	if (status < B_OK)
565 		goto err2;
566 
567 	return B_OK;
568 
569 err2:
570 	uninit_common();
571 err1:
572 	close(fd);
573 	return status;
574 }
575 
576 
577 /*! This function is called for both, the primary accelerant and all of
578 	its clones.
579 */
580 void
581 intel_uninit_accelerant(void)
582 {
583 	CALLED();
584 
585 	// delete accelerant instance data
586 	delete_area(gInfo->mode_list_area);
587 	gInfo->mode_list = NULL;
588 
589 	if (!gInfo->is_clone) {
590 		intel_shared_info &info = *gInfo->shared_info;
591 		uninit_lock(&info.accelerant_lock);
592 		uninit_lock(&info.engine_lock);
593 		uninit_ring_buffer(info.primary_ring_buffer);
594 	}
595 	uninit_common();
596 }
597 
598 
599 status_t
600 intel_get_accelerant_device_info(accelerant_device_info* info)
601 {
602 	CALLED();
603 
604 	info->version = B_ACCELERANT_VERSION;
605 
606 	DeviceType* type = &gInfo->shared_info->device_type;
607 
608 	if (type->InFamily(INTEL_FAMILY_8xx))
609 		strcpy(info->name, "Intel Extreme");
610 	else if (type->InFamily(INTEL_FAMILY_9xx))
611 		strcpy(info->name, "Intel GMA");
612 	else if (type->InFamily(INTEL_FAMILY_SOC0))
613 		strcpy(info->name, "Intel Atom");
614 	else if (type->InFamily(INTEL_FAMILY_SER5))
615 		strcpy(info->name, "Intel HD/Iris");
616 	else
617 		strcpy(info->name, "Intel");
618 
619 	strcpy(info->chipset, gInfo->shared_info->device_identifier);
620 	strcpy(info->serial_no, "None");
621 
622 	info->memory = gInfo->shared_info->graphics_memory_size;
623 	info->dac_speed = gInfo->shared_info->pll_info.max_frequency;
624 
625 	return B_OK;
626 }
627 
628 
629 sem_id
630 intel_accelerant_retrace_semaphore()
631 {
632 	CALLED();
633 	return gInfo->shared_info->vblank_sem;
634 }
635