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
40struct accelerant_info* gInfo;
41uint32 gDumpCount;
42
43
44//	#pragma mark -
45
46
47// intel_reg --mmio=ie-0001.bin --devid=27a2 dump
48void
49dump_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*/
83static status_t
84init_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 */
183static void
184uninit_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
201static void
202dump_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
224static bool
225has_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
241static status_t
242probe_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
435static status_t
436assign_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 */
493status_t
494intel_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
533ssize_t
534intel_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
542void
543intel_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
550status_t
551intel_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
581err2:
582	uninit_common();
583err1:
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*/
592void
593intel_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
611status_t
612intel_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
641sem_id
642intel_accelerant_retrace_semaphore()
643{
644	CALLED();
645	return gInfo->shared_info->vblank_sem;
646}
647