xref: /haiku/src/add-ons/accelerants/intel_extreme/accelerant.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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 
26 
27 #undef TRACE
28 #define TRACE_ACCELERANT
29 #ifdef TRACE_ACCELERANT
30 #	define TRACE(x...) _sPrintf("intel_extreme: " x)
31 #else
32 #	define TRACE(x...)
33 #endif
34 
35 #define ERROR(x...) _sPrintf("intel_extreme: " x)
36 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
37 
38 
39 struct accelerant_info* gInfo;
40 uint32 gDumpCount;
41 
42 
43 class AreaCloner {
44 public:
45 							AreaCloner();
46 							~AreaCloner();
47 
48 			area_id			Clone(const char* name, void** _address,
49 								uint32 spec, uint32 protection,
50 								area_id sourceArea);
51 			status_t		InitCheck()
52 								{ return fArea < 0 ? (status_t)fArea : B_OK; }
53 			void			Keep();
54 
55 private:
56 			area_id			fArea;
57 };
58 
59 
60 AreaCloner::AreaCloner()
61 	:
62 	fArea(-1)
63 {
64 }
65 
66 
67 AreaCloner::~AreaCloner()
68 {
69 	if (fArea >= 0)
70 		delete_area(fArea);
71 }
72 
73 
74 area_id
75 AreaCloner::Clone(const char* name, void** _address, uint32 spec,
76 	uint32 protection, area_id sourceArea)
77 {
78 	fArea = clone_area(name, _address, spec, protection, sourceArea);
79 	return fArea;
80 }
81 
82 
83 void
84 AreaCloner::Keep()
85 {
86 	fArea = -1;
87 }
88 
89 
90 //	#pragma mark -
91 
92 
93 // intel_reg --mmio=ie-0001.bin --devid=27a2 dump
94 void
95 dump_registers()
96 {
97 	char filename[255];
98 
99 	sprintf(filename, "/boot/system/cache/tmp/ie-%04" B_PRId32 ".bin",
100 		gDumpCount);
101 
102 	ERROR("%s: Taking register dump #%" B_PRId32 "\n", __func__, gDumpCount);
103 
104 	int fd = open(filename, O_CREAT | O_WRONLY, 0644);
105 	uint32 data = 0;
106 	if (fd >= 0) {
107 		for (int32 i = 0; i < 0x80000; i += sizeof(data)) {
108 			//char line[512];
109 			//int length = sprintf(line, "%05" B_PRIx32 ": "
110 			//	"%08" B_PRIx32 " %08" B_PRIx32 " %08" B_PRIx32 " %08" B_PRIx32 "\n",
111 			//	i, read32(i), read32(i + 4), read32(i + 8), read32(i + 12));
112 			data = read32(i);
113 			write(fd, &data, sizeof(data));
114 		}
115 		close(fd);
116 		sync();
117 	}
118 
119 	gDumpCount++;
120 }
121 
122 
123 /*! This is the common accelerant_info initializer. It is called by
124 	both, the first accelerant and all clones.
125 */
126 static status_t
127 init_common(int device, bool isClone)
128 {
129 	// initialize global accelerant info structure
130 
131 	// Number of register dumps we have... taken.
132 	gDumpCount = 0;
133 
134 	gInfo = (accelerant_info*)malloc(sizeof(accelerant_info));
135 	if (gInfo == NULL)
136 		return B_NO_MEMORY;
137 
138 	memset(gInfo, 0, sizeof(accelerant_info));
139 
140 	gInfo->is_clone = isClone;
141 	gInfo->device = device;
142 
143 	// get basic info from driver
144 
145 	intel_get_private_data data;
146 	data.magic = INTEL_PRIVATE_DATA_MAGIC;
147 
148 	if (ioctl(device, INTEL_GET_PRIVATE_DATA, &data,
149 			sizeof(intel_get_private_data)) != 0) {
150 		free(gInfo);
151 		return B_ERROR;
152 	}
153 
154 	AreaCloner sharedCloner;
155 	gInfo->shared_info_area = sharedCloner.Clone("intel extreme shared info",
156 		(void**)&gInfo->shared_info, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA,
157 		data.shared_info_area);
158 	status_t status = sharedCloner.InitCheck();
159 	if (status < B_OK) {
160 		free(gInfo);
161 		return status;
162 	}
163 
164 	AreaCloner regsCloner;
165 	gInfo->regs_area = regsCloner.Clone("intel extreme regs",
166 		(void**)&gInfo->registers, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA,
167 		gInfo->shared_info->registers_area);
168 	status = regsCloner.InitCheck();
169 	if (status < B_OK) {
170 		free(gInfo);
171 		return status;
172 	}
173 
174 	sharedCloner.Keep();
175 	regsCloner.Keep();
176 
177 	// The overlay registers, hardware status, and cursor memory share
178 	// a single area with the shared_info
179 
180 	if (gInfo->shared_info->overlay_offset != 0) {
181 		gInfo->overlay_registers = (struct overlay_registers*)
182 			(gInfo->shared_info->graphics_memory
183 			+ gInfo->shared_info->overlay_offset);
184 	}
185 
186 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) {
187 		// allocate some extra memory for the 3D context
188 		if (intel_allocate_memory(INTEL_i965_3D_CONTEXT_SIZE,
189 				B_APERTURE_NON_RESERVED, gInfo->context_base) == B_OK) {
190 			gInfo->context_offset = gInfo->context_base
191 				- (addr_t)gInfo->shared_info->graphics_memory;
192 		}
193 	}
194 
195 	gInfo->pipe_count = 0;
196 
197 	// Allocate all of our pipes
198 	for (int i = 0; i < MAX_PIPES; i++) {
199 		switch (i) {
200 			case 0:
201 				gInfo->pipes[i] = new(std::nothrow) Pipe(INTEL_PIPE_A);
202 				break;
203 			case 1:
204 				gInfo->pipes[i] = new(std::nothrow) Pipe(INTEL_PIPE_B);
205 				break;
206 			default:
207 				ERROR("%s: Unknown pipe %d\n", __func__, i);
208 		}
209 		if (gInfo->pipes[i] == NULL)
210 			ERROR("%s: Error allocating pipe %d\n", __func__, i);
211 		else
212 			gInfo->pipe_count++;
213 	}
214 
215 	return B_OK;
216 }
217 
218 
219 /*! Clean up data common to both primary and cloned accelerant */
220 static void
221 uninit_common(void)
222 {
223 	intel_free_memory(gInfo->context_base);
224 
225 	delete_area(gInfo->regs_area);
226 	delete_area(gInfo->shared_info_area);
227 
228 	gInfo->regs_area = gInfo->shared_info_area = -1;
229 
230 	// close the file handle ONLY if we're the clone
231 	if (gInfo->is_clone)
232 		close(gInfo->device);
233 
234 	free(gInfo);
235 }
236 
237 
238 static void
239 dump_ports()
240 {
241 	if (gInfo->port_count == 0) {
242 		TRACE("%s: No ports connected\n", __func__);
243 		return;
244 	}
245 
246 	TRACE("%s: Connected ports: (port_count: %" B_PRIu32 ")\n", __func__,
247 		gInfo->port_count);
248 
249 	for (uint32 i = 0; i < gInfo->port_count; i++) {
250 		Port* port = gInfo->ports[i];
251 		if (!port) {
252 			TRACE("port %" B_PRIu32 ":: INVALID ALLOC!\n", i);
253 			continue;
254 		}
255 		TRACE("port %" B_PRIu32 ": %s %s\n", i, port->PortName(),
256 			port->IsConnected() ? "connected" : "disconnected");
257 	}
258 }
259 
260 
261 static bool
262 has_connected_port(port_index portIndex, uint32 type)
263 {
264 	for (uint32 i = 0; i < gInfo->port_count; i++) {
265 		Port* port = gInfo->ports[i];
266 		if (type != INTEL_PORT_TYPE_ANY && port->Type() != type)
267 			continue;
268 		if (portIndex != INTEL_PORT_ANY && port->PortIndex() != portIndex)
269 			continue;
270 
271 		return true;
272 	}
273 
274 	return false;
275 }
276 
277 
278 static status_t
279 probe_ports()
280 {
281 	// Try to determine what ports to use. We use the following heuristic:
282 	// * Check for DisplayPort, these can be more or less detected reliably.
283 	// * Check for HDMI, it'll fail on devices not having HDMI for us to fall
284 	//   back to DVI.
285 	// * Assume DVI B if no HDMI and no DisplayPort is present, confirmed by
286 	//   reading EDID in the IsConnected() call.
287 	// * Check for analog if possible (there's a detection bit on PCH),
288 	//   otherwise the assumed presence is confirmed by reading EDID in
289 	//   IsConnected().
290 
291 	TRACE("adpa: %08" B_PRIx32 "\n", read32(INTEL_ANALOG_PORT));
292 	TRACE("dova: %08" B_PRIx32 ", dovb: %08" B_PRIx32
293 		", dovc: %08" B_PRIx32 "\n", read32(INTEL_DIGITAL_PORT_A),
294 		read32(INTEL_DIGITAL_PORT_B), read32(INTEL_DIGITAL_PORT_C));
295 	TRACE("lvds: %08" B_PRIx32 "\n", read32(INTEL_DIGITAL_LVDS_PORT));
296 
297 	bool foundLVDS = false;
298 
299 	gInfo->port_count = 0;
300 	for (int i = INTEL_PORT_A; i <= INTEL_PORT_D; i++) {
301 		TRACE("Probing DisplayPort %d\n", i);
302 		Port* displayPort = new(std::nothrow) DisplayPort((port_index)i);
303 		if (displayPort == NULL)
304 			return B_NO_MEMORY;
305 
306 		if (displayPort->IsConnected())
307 			gInfo->ports[gInfo->port_count++] = displayPort;
308 		else
309 			delete displayPort;
310 	}
311 
312 	// Digital Display Interface
313 	if (gInfo->shared_info->device_type.HasDDI()) {
314 		for (int i = INTEL_PORT_A; i <= INTEL_PORT_E; i++) {
315 			TRACE("Probing DDI %d\n", i);
316 
317 			Port* ddiPort
318 				= new(std::nothrow) DigitalDisplayInterface((port_index)i);
319 
320 			if (ddiPort == NULL)
321 				return B_NO_MEMORY;
322 
323 			if (ddiPort->IsConnected())
324 				gInfo->ports[gInfo->port_count++] = ddiPort;
325 			else
326 				delete ddiPort;
327 		}
328 	}
329 
330 	// Ensure DP_A isn't already taken (or DDI)
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 	for (int i = INTEL_PORT_B; i <= INTEL_PORT_D; i++) {
344 		TRACE("Probing HDMI %d\n", i);
345 		if (has_connected_port((port_index)i, INTEL_PORT_TYPE_ANY)) {
346 			// Ensure port not already claimed by something like DDI
347 			continue;
348 		}
349 
350 		Port* hdmiPort = new(std::nothrow) HDMIPort((port_index)i);
351 		if (hdmiPort == NULL)
352 			return B_NO_MEMORY;
353 
354 		if (hdmiPort->IsConnected())
355 			gInfo->ports[gInfo->port_count++] = hdmiPort;
356 		else
357 			delete hdmiPort;
358 	}
359 
360 	// always try the LVDS port, it'll simply fail if not applicable
361 	TRACE("Probing LVDS\n");
362 	Port* lvdsPort = new(std::nothrow) LVDSPort();
363 	if (lvdsPort == NULL)
364 		return B_NO_MEMORY;
365 	if (lvdsPort->IsConnected()) {
366 		foundLVDS = true;
367 		gInfo->ports[gInfo->port_count++] = lvdsPort;
368 		gInfo->head_mode |= HEAD_MODE_LVDS_PANEL;
369 		gInfo->head_mode |= HEAD_MODE_B_DIGITAL;
370 	} else
371 		delete lvdsPort;
372 
373 	TRACE("Probing DVI\n");
374 	if (!has_connected_port(INTEL_PORT_ANY, INTEL_PORT_TYPE_ANY)) {
375 		// there's neither DisplayPort nor HDMI so far, assume DVI B
376 		for (port_index index = INTEL_PORT_B; index <= INTEL_PORT_C;
377 				index = (port_index)(index + 1)) {
378 			Port* dviPort = new(std::nothrow) DigitalPort(index, "DVI");
379 			if (dviPort == NULL)
380 				return B_NO_MEMORY;
381 
382 			if (dviPort->IsConnected()) {
383 				gInfo->ports[gInfo->port_count++] = dviPort;
384 				gInfo->head_mode |= HEAD_MODE_B_DIGITAL;
385 			} else
386 				delete dviPort;
387 		}
388 	}
389 
390 	// then finally always try the analog port
391 	TRACE("Probing Analog\n");
392 	Port* analogPort = new(std::nothrow) AnalogPort();
393 	if (analogPort == NULL)
394 		return B_NO_MEMORY;
395 	if (analogPort->IsConnected()) {
396 		gInfo->ports[gInfo->port_count++] = analogPort;
397 		gInfo->head_mode |= HEAD_MODE_A_ANALOG;
398 	} else
399 		delete analogPort;
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 		// XXX: Is LVDS the same as Panel?
409 		refclk_activate_ilk(foundLVDS);
410 	}
411 	/*
412 	} else if (gInfo->shared_info->pch_info == INTEL_PCH_LPT) {
413 		// TODO: Some kind of stepped bend thing?
414 		// only needed for vga
415 		refclk_activate_lpt(foundLVDS);
416 	}
417 	*/
418 
419 	TRACE("Probing complete.\n");
420 	return B_OK;
421 }
422 
423 
424 static status_t
425 assign_pipes()
426 {
427 	// TODO: At some point we should "group" ports to pipes with the same mode.
428 	// You can drive multiple ports from a single pipe as long as the mode is
429 	// the same. For the moment we could get displays with the wrong pipes
430 	// assigned when the count is > 1;
431 
432 	uint32 current = 0;
433 
434 	bool assigned[gInfo->pipe_count];
435 	memset(assigned, 0, gInfo->pipe_count);
436 
437 	// Some ports need to be assigned to a fixed pipe on old hardware (or due
438 	// to limitations in the current driver on current hardware). Assign those
439 	// first
440 	for (uint32 i = 0; i < gInfo->port_count; i++) {
441 		if (gInfo->ports[i] == NULL)
442 			continue;
443 
444 		pipe_index preference = gInfo->ports[i]->PipePreference();
445 		if (preference != INTEL_PIPE_ANY) {
446 			int index = (preference == INTEL_PIPE_B) ? 1 : 0;
447 			if (assigned[index]) {
448 				TRACE("Pipe %d is already assigned, it will drive multiple "
449 					"displays\n", index);
450 			}
451 			gInfo->ports[i]->SetPipe(gInfo->pipes[index]);
452 			assigned[index] = true;
453 			continue;
454 		}
455 	}
456 
457 	// In a second pass, assign the remaining ports to the remaining pipes
458 	for (uint32 i = 0; i < gInfo->port_count; i++) {
459 		if (gInfo->ports[i]->IsConnected()) {
460 			while (current < gInfo->pipe_count && assigned[current])
461 				current++;
462 
463 			if (current >= gInfo->pipe_count) {
464 				ERROR("%s: No pipes left to assign to port %s!\n", __func__,
465 					gInfo->ports[i]->PortName());
466 				continue;
467 			}
468 
469 			gInfo->ports[i]->SetPipe(gInfo->pipes[current]);
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 	intel_shared_info &info = *gInfo->shared_info;
590 
591 	uninit_lock(&info.accelerant_lock);
592 	uninit_lock(&info.engine_lock);
593 
594 	uninit_ring_buffer(info.primary_ring_buffer);
595 
596 	uninit_common();
597 }
598 
599 
600 status_t
601 intel_get_accelerant_device_info(accelerant_device_info* info)
602 {
603 	CALLED();
604 
605 	info->version = B_ACCELERANT_VERSION;
606 
607 	DeviceType* type = &gInfo->shared_info->device_type;
608 
609 	if (type->InFamily(INTEL_FAMILY_8xx))
610 		strcpy(info->name, "Intel Extreme");
611 	else if (type->InFamily(INTEL_FAMILY_9xx))
612 		strcpy(info->name, "Intel GMA");
613 	else if (type->InFamily(INTEL_FAMILY_SOC0))
614 		strcpy(info->name, "Intel Atom");
615 	else if (type->InFamily(INTEL_FAMILY_SER5))
616 		strcpy(info->name, "Intel HD/Iris");
617 	else
618 		strcpy(info->name, "Intel");
619 
620 	strcpy(info->chipset, gInfo->shared_info->device_identifier);
621 	strcpy(info->serial_no, "None");
622 
623 	info->memory = gInfo->shared_info->graphics_memory_size;
624 	info->dac_speed = gInfo->shared_info->pll_info.max_frequency;
625 
626 	return B_OK;
627 }
628 
629 
630 sem_id
631 intel_accelerant_retrace_semaphore()
632 {
633 	CALLED();
634 	return gInfo->shared_info->vblank_sem;
635 }
636