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