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