1 /*
2 * Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Support for i915 chipset and up based on the X driver,
6 * Copyright 2006-2007 Intel Corporation.
7 *
8 * Authors:
9 * Axel Dörfler, axeld@pinc-software.de
10 */
11
12
13 #include <algorithm>
14 #include <math.h>
15 #include <stdio.h>
16 #include <string.h>
17
18 #include <Debug.h>
19
20 #include <create_display_modes.h>
21 #include <ddc.h>
22 #include <edid.h>
23 #include <validate_display_mode.h>
24
25 #include "accelerant_protos.h"
26 #include "accelerant.h"
27 #include "pll.h"
28 #include "Ports.h"
29 #include "utility.h"
30
31
32 #undef TRACE
33 #define TRACE_MODE
34 #ifdef TRACE_MODE
35 # define TRACE(x...) _sPrintf("intel_extreme: " x)
36 #else
37 # define TRACE(x...)
38 #endif
39
40 #define ERROR(x...) _sPrintf("intel_extreme: " x)
41 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
42
43
44 static void
get_color_space_format(const display_mode & mode,uint32 & colorMode,uint32 & bytesPerRow,uint32 & bitsPerPixel)45 get_color_space_format(const display_mode &mode, uint32 &colorMode,
46 uint32 &bytesPerRow, uint32 &bitsPerPixel)
47 {
48 uint32 bytesPerPixel;
49
50 switch (mode.space) {
51 case B_RGB32_LITTLE:
52 if (gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_LAKE)) {
53 colorMode = DISPLAY_CONTROL_RGB32_SKY;
54 } else {
55 colorMode = DISPLAY_CONTROL_RGB32;
56 }
57 bytesPerPixel = 4;
58 bitsPerPixel = 32;
59 break;
60 case B_RGB16_LITTLE:
61 if (gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_LAKE)) {
62 colorMode = DISPLAY_CONTROL_RGB16_SKY;
63 } else {
64 colorMode = DISPLAY_CONTROL_RGB16;
65 }
66 bytesPerPixel = 2;
67 bitsPerPixel = 16;
68 break;
69 case B_RGB15_LITTLE:
70 if (gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_LAKE)) {
71 colorMode = DISPLAY_CONTROL_RGB15_SKY;
72 } else {
73 colorMode = DISPLAY_CONTROL_RGB15;
74 }
75 bytesPerPixel = 2;
76 bitsPerPixel = 15;
77 break;
78 case B_CMAP8:
79 default:
80 if (gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_LAKE)) {
81 colorMode = DISPLAY_CONTROL_CMAP8_SKY;
82 } else {
83 colorMode = DISPLAY_CONTROL_CMAP8;
84 }
85 bytesPerPixel = 1;
86 bitsPerPixel = 8;
87 break;
88 }
89
90 bytesPerRow = mode.virtual_width * bytesPerPixel;
91
92 // Make sure bytesPerRow is a multiple of 64
93 if ((bytesPerRow & 63) != 0)
94 bytesPerRow = (bytesPerRow + 63) & ~63;
95 }
96
97
98 static bool
sanitize_display_mode(display_mode & mode)99 sanitize_display_mode(display_mode& mode)
100 {
101 uint16 pixelCount = 1;
102 // Older cards require pixel count to be even
103 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_Gxx)
104 || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)
105 || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_94x)
106 || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_91x)
107 || gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_8xx)) {
108 pixelCount = 2;
109 }
110
111 display_constraints constraints = {
112 // resolution
113 320, 4096, 200, 4096,
114 // pixel clock
115 gInfo->shared_info->pll_info.min_frequency,
116 gInfo->shared_info->pll_info.max_frequency,
117 // horizontal
118 {pixelCount, 0, 8160, 32, 8192, 0, 8192},
119 {1, 1, 8190, 2, 8192, 1, 8192}
120 };
121
122 return sanitize_display_mode(mode, constraints,
123 gInfo->has_edid ? &gInfo->edid_info : NULL);
124 }
125
126
127 // #pragma mark -
128
129
130 static void
set_frame_buffer_registers(uint32 offset)131 set_frame_buffer_registers(uint32 offset)
132 {
133 intel_shared_info &sharedInfo = *gInfo->shared_info;
134 display_mode &mode = sharedInfo.current_mode;
135 uint32 bytes_per_pixel = (sharedInfo.bits_per_pixel + 7) / 8;
136
137 if (sharedInfo.device_type.InGroup(INTEL_GROUP_96x)
138 || sharedInfo.device_type.InGroup(INTEL_GROUP_G4x)
139 || sharedInfo.device_type.InGroup(INTEL_GROUP_ILK)
140 || sharedInfo.device_type.InFamily(INTEL_FAMILY_SER5)
141 || sharedInfo.device_type.InFamily(INTEL_FAMILY_LAKE)
142 || sharedInfo.device_type.InFamily(INTEL_FAMILY_SOC0)) {
143 if (sharedInfo.device_type.InGroup(INTEL_GROUP_HAS)) {
144 // || sharedInfo.device_type.InGroup(INTEL_GROUP_SKY)) {
145 write32(INTEL_DISPLAY_A_OFFSET_HAS + offset,
146 ((uint32)mode.v_display_start << 16)
147 | (uint32)mode.h_display_start);
148 read32(INTEL_DISPLAY_A_OFFSET_HAS + offset);
149 } else {
150 write32(INTEL_DISPLAY_A_BASE + offset,
151 mode.v_display_start * sharedInfo.bytes_per_row
152 + mode.h_display_start * bytes_per_pixel);
153 read32(INTEL_DISPLAY_A_BASE + offset);
154 }
155 write32(INTEL_DISPLAY_A_SURFACE + offset, sharedInfo.frame_buffer_offset);
156 read32(INTEL_DISPLAY_A_SURFACE + offset);
157 } else {
158 write32(INTEL_DISPLAY_A_BASE + offset, sharedInfo.frame_buffer_offset
159 + mode.v_display_start * sharedInfo.bytes_per_row
160 + mode.h_display_start * bytes_per_pixel);
161 read32(INTEL_DISPLAY_A_BASE + offset);
162 }
163 }
164
165
166 void
set_frame_buffer_base()167 set_frame_buffer_base()
168 {
169 // TODO we always set both displays to the same address. When we support
170 // multiple framebuffers, they should get different addresses here.
171 set_frame_buffer_registers(0);
172 set_frame_buffer_registers(INTEL_DISPLAY_OFFSET);
173 }
174
175
176 static bool
limit_modes_for_gen3_lvds(display_mode * mode)177 limit_modes_for_gen3_lvds(display_mode* mode)
178 {
179 // Filter out modes with resolution higher than the internal LCD can
180 // display.
181 // FIXME do this only for that display. The whole display mode logic
182 // needs to be adjusted to know which display we're talking about.
183 if (gInfo->shared_info->panel_timing.h_display < mode->timing.h_display)
184 return false;
185 if (gInfo->shared_info->panel_timing.v_display < mode->timing.v_display)
186 return false;
187
188 return true;
189 }
190
191 /*! Creates the initial mode list of the primary accelerant.
192 It's called from intel_init_accelerant().
193 */
194 status_t
create_mode_list(void)195 create_mode_list(void)
196 {
197 CALLED();
198
199 for (uint32 i = 0; i < gInfo->port_count; i++) {
200 if (gInfo->ports[i] == NULL)
201 continue;
202
203 status_t status = gInfo->ports[i]->GetEDID(&gInfo->edid_info);
204 if (status == B_OK) {
205 gInfo->has_edid = true;
206 break;
207 }
208 }
209 // use EDID found at boot time if there since we don't have any ourselves
210 if (!gInfo->has_edid && gInfo->shared_info->has_vesa_edid_info) {
211 TRACE("%s: Using VESA edid info\n", __func__);
212 memcpy(&gInfo->edid_info, &gInfo->shared_info->vesa_edid_info,
213 sizeof(edid1_info));
214 // show in log what we got
215 edid_dump(&gInfo->edid_info);
216 gInfo->has_edid = true;
217 }
218
219 display_mode* list;
220 uint32 count = 0;
221
222 const color_space kSupportedSpaces[] = {B_RGB32_LITTLE, B_RGB16_LITTLE,
223 B_CMAP8};
224 const color_space* supportedSpaces;
225 int colorSpaceCount;
226
227 if (gInfo->shared_info->device_type.Generation() >= 4) {
228 // No B_RGB15, use our custom colorspace list
229 supportedSpaces = kSupportedSpaces;
230 colorSpaceCount = B_COUNT_OF(kSupportedSpaces);
231 } else {
232 supportedSpaces = NULL;
233 colorSpaceCount = 0;
234 }
235
236 // If no EDID, but have vbt from driver, use that mode
237 if (!gInfo->has_edid && gInfo->shared_info->got_vbt) {
238 // We could not read any EDID info. Fallback to creating a list with
239 // only the mode set up by the BIOS.
240
241 check_display_mode_hook limitModes = NULL;
242 if (gInfo->shared_info->device_type.Generation() < 4)
243 limitModes = limit_modes_for_gen3_lvds;
244
245 display_mode mode;
246 mode.timing = gInfo->shared_info->panel_timing;
247 mode.space = B_RGB32;
248 mode.virtual_width = mode.timing.h_display;
249 mode.virtual_height = mode.timing.v_display;
250 mode.h_display_start = 0;
251 mode.v_display_start = 0;
252 mode.flags = 0;
253
254 // TODO: support lower modes via scaling and windowing
255 gInfo->mode_list_area = create_display_modes("intel extreme modes", NULL, &mode, 1,
256 supportedSpaces, colorSpaceCount, limitModes, &list, &count);
257 } else {
258 // Otherwise return the 'real' list of modes
259 gInfo->mode_list_area = create_display_modes("intel extreme modes",
260 gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0,
261 supportedSpaces, colorSpaceCount, NULL, &list, &count);
262 }
263
264 if (gInfo->mode_list_area < B_OK)
265 return gInfo->mode_list_area;
266
267 gInfo->mode_list = list;
268 gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
269 gInfo->shared_info->mode_count = count;
270
271 return B_OK;
272 }
273
274
275 void
wait_for_vblank(void)276 wait_for_vblank(void)
277 {
278 acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT,
279 21000);
280 // With the output turned off via DPMS, we might not get any interrupts
281 // anymore that's why we don't wait forever for it. At 50Hz, we're sure
282 // to get a vblank in at most 20ms, so there is no need to wait longer
283 // than that.
284 }
285
286
287 // #pragma mark -
288
289
290 uint32
intel_accelerant_mode_count(void)291 intel_accelerant_mode_count(void)
292 {
293 CALLED();
294 return gInfo->shared_info->mode_count;
295 }
296
297
298 status_t
intel_get_mode_list(display_mode * modeList)299 intel_get_mode_list(display_mode* modeList)
300 {
301 CALLED();
302 memcpy(modeList, gInfo->mode_list,
303 gInfo->shared_info->mode_count * sizeof(display_mode));
304 return B_OK;
305 }
306
307
308 status_t
intel_propose_display_mode(display_mode * target,const display_mode * low,const display_mode * high)309 intel_propose_display_mode(display_mode* target, const display_mode* low,
310 const display_mode* high)
311 {
312 CALLED();
313
314 display_mode mode = *target;
315
316 if (sanitize_display_mode(*target)) {
317 TRACE("Video mode was adjusted by sanitize_display_mode\n");
318 TRACE("Initial mode: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n",
319 mode.timing.h_display, mode.timing.h_sync_start,
320 mode.timing.h_sync_end, mode.timing.h_total,
321 mode.timing.v_display, mode.timing.v_sync_start,
322 mode.timing.v_sync_end, mode.timing.v_total);
323 TRACE("Sanitized: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n",
324 target->timing.h_display, target->timing.h_sync_start,
325 target->timing.h_sync_end, target->timing.h_total,
326 target->timing.v_display, target->timing.v_sync_start,
327 target->timing.v_sync_end, target->timing.v_total);
328 }
329 // (most) modeflags are outputs from us (the driver). So we should
330 // set them depending on the mode and the current hardware config
331 target->flags |= B_SCROLL;
332
333 return is_display_mode_within_bounds(*target, *low, *high)
334 ? B_OK : B_BAD_VALUE;
335 }
336
337
338 status_t
intel_set_display_mode(display_mode * mode)339 intel_set_display_mode(display_mode* mode)
340 {
341 if (mode == NULL)
342 return B_BAD_VALUE;
343
344 TRACE("%s(%" B_PRIu16 "x%" B_PRIu16 ", virtual: %" B_PRIu16 "x%" B_PRIu16 ")\n", __func__,
345 mode->timing.h_display, mode->timing.v_display, mode->virtual_width, mode->virtual_height);
346
347 display_mode target = *mode;
348
349 if (intel_propose_display_mode(&target, &target, &target) != B_OK)
350 return B_BAD_VALUE;
351
352 uint32 colorMode, bytesPerRow, bitsPerPixel;
353 get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel);
354
355 // TODO: do not go further if the mode is identical to the current one.
356 // This would avoid the screen being off when switching workspaces when they
357 // have the same resolution.
358
359 intel_shared_info &sharedInfo = *gInfo->shared_info;
360 Autolock locker(sharedInfo.accelerant_lock);
361
362 // First register dump
363 //dump_registers();
364
365 // TODO: This may not be neccesary
366 set_display_power_mode(B_DPMS_OFF);
367
368 // free old and allocate new frame buffer in graphics memory
369
370 intel_free_memory(sharedInfo.frame_buffer);
371
372 addr_t base;
373 if (intel_allocate_memory(bytesPerRow * target.virtual_height, 0,
374 base) < B_OK) {
375 // oh, how did that happen? Unfortunately, there is no really good way
376 // back. Try to restore a framebuffer for the previous mode, at least.
377 if (intel_allocate_memory(sharedInfo.current_mode.virtual_height
378 * sharedInfo.bytes_per_row, 0, base) == B_OK) {
379 sharedInfo.frame_buffer = base;
380 sharedInfo.frame_buffer_offset = base
381 - (addr_t)sharedInfo.graphics_memory;
382 set_frame_buffer_base();
383 }
384
385 ERROR("%s: Failed to allocate framebuffer !\n", __func__);
386 return B_NO_MEMORY;
387 }
388
389 // clear frame buffer before using it
390 memset((uint8*)base, 0, bytesPerRow * target.virtual_height);
391 sharedInfo.frame_buffer = base;
392 sharedInfo.frame_buffer_offset = base - (addr_t)sharedInfo.graphics_memory;
393
394 #if 0
395 if ((gInfo->head_mode & HEAD_MODE_TESTING) != 0) {
396 // 1. Enable panel power as needed to retrieve panel configuration
397 // (use AUX VDD enable bit)
398 // skip, did detection already, might need that before that though
399
400 // 2. Enable PCH clock reference source and PCH SSC modulator,
401 // wait for warmup (Can be done anytime before enabling port)
402 // skip, most certainly already set up by bios to use other ports,
403 // will need for coldstart though
404
405 // 3. If enabling CPU embedded DisplayPort A: (Can be done anytime
406 // before enabling CPU pipe or port)
407 // a. Enable PCH 120MHz clock source output to CPU, wait for DMI
408 // latency
409 // b. Configure and enable CPU DisplayPort PLL in the DisplayPort A
410 // register, wait for warmup
411 // skip, not doing eDP right now, should go into
412 // EmbeddedDisplayPort class though
413
414 // 4. If enabling port on PCH: (Must be done before enabling CPU pipe
415 // or FDI)
416 // a. Enable PCH FDI Receiver PLL, wait for warmup plus DMI latency
417 // b. Switch from Rawclk to PCDclk in FDI Receiver (FDI A OR FDI B)
418 // c. [DevSNB] Enable CPU FDI Transmitter PLL, wait for warmup
419 // d. [DevILK] CPU FDI PLL is always on and does not need to be
420 // enabled
421 FDILink* link = pipe->FDILink();
422 if (link != NULL) {
423 link->Receiver().EnablePLL();
424 link->Receiver().SwitchClock(true);
425 link->Transmitter().EnablePLL();
426 }
427
428 // 5. Enable CPU panel fitter if needed for hires, required for VGA
429 // (Can be done anytime before enabling CPU pipe)
430 PanelFitter* fitter = pipe->PanelFitter();
431 if (fitter != NULL)
432 fitter->Enable(mode);
433
434 // 6. Configure CPU pipe timings, M/N/TU, and other pipe settings
435 // (Can be done anytime before enabling CPU pipe)
436 pll_divisors divisors;
437 compute_pll_divisors(target, divisors, false);
438 pipe->ConfigureTimings(divisors);
439
440 // 7. Enable CPU pipe
441 pipe->Enable();
442
443 8. Configure and enable CPU planes (VGA or hires)
444 9. If enabling port on PCH:
445 // a. Program PCH FDI Receiver TU size same as Transmitter TU size for TU error checking
446 // b. Train FDI
447 // i. Set pre-emphasis and voltage (iterate if training steps fail)
448 ii. Enable CPU FDI Transmitter and PCH FDI Receiver with Training Pattern 1 enabled.
449 iii. Wait for FDI training pattern 1 time
450 iv. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for bit lock in bit 8 (retry at least once if no lock)
451 v. Enable training pattern 2 on CPU FDI Transmitter and PCH FDI Receiver
452 vi. Wait for FDI training pattern 2 time
453 vii. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for symbol lock in bit 9 (retry at least once if no
454 lock)
455 viii. Enable normal pixel output on CPU FDI Transmitter and PCH FDI Receiver
456 ix. Wait for FDI idle pattern time for link to become active
457 c. Configure and enable PCH DPLL, wait for PCH DPLL warmup (Can be done anytime before enabling
458 PCH transcoder)
459 d. [DevCPT] Configure DPLL SEL to set the DPLL to transcoder mapping and enable DPLL to the
460 transcoder.
461 e. [DevCPT] Configure DPLL_CTL DPLL_HDMI_multipler.
462 f. Configure PCH transcoder timings, M/N/TU, and other transcoder settings (should match CPU settings).
463 g. [DevCPT] Configure and enable Transcoder DisplayPort Control if DisplayPort will be used
464 h. Enable PCH transcoder
465 10. Enable ports (DisplayPort must enable in training pattern 1)
466 11. Enable panel power through panel power sequencing
467 12. Wait for panel power sequencing to reach enabled steady state
468 13. Disable panel power override
469 14. If DisplayPort, complete link training
470 15. Enable panel backlight
471 }
472 #endif
473
474 // make sure VGA display is disabled
475 write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED);
476 read32(INTEL_VGA_DISPLAY_CONTROL);
477
478 // Go over each port and set the display mode
479 for (uint32 i = 0; i < gInfo->port_count; i++) {
480 if (gInfo->ports[i] == NULL)
481 continue;
482 if (!gInfo->ports[i]->IsConnected())
483 continue;
484
485 status_t status = gInfo->ports[i]->SetDisplayMode(&target, colorMode);
486 if (status != B_OK)
487 ERROR("%s: Unable to set display mode!\n", __func__);
488 }
489
490 TRACE("%s: Port configuration completed successfully!\n", __func__);
491
492 // We set the same color mode across all pipes
493 program_pipe_color_modes(colorMode);
494
495 // TODO: This may not be neccesary (see DPMS OFF at top)
496 set_display_power_mode(sharedInfo.dpms_mode);
497
498 // Changing bytes per row seems to be ignored if the plane/pipe is turned
499 // off
500
501 // Always set both pipes, just in case
502 // TODO rework this when we get multiple head support with different
503 // resolutions
504 if (sharedInfo.device_type.InFamily(INTEL_FAMILY_LAKE)) {
505 write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow >> 6);
506 write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow >> 6);
507 } else {
508 write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow);
509 write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow);
510 }
511
512 // update shared info
513 sharedInfo.current_mode = target;
514 sharedInfo.bytes_per_row = bytesPerRow;
515 sharedInfo.bits_per_pixel = bitsPerPixel;
516
517 set_frame_buffer_base();
518 // triggers writing back double-buffered registers
519 // which is INTEL_DISPLAY_X_BYTES_PER_ROW only apparantly
520
521 // Second register dump
522 //dump_registers();
523
524 return B_OK;
525 }
526
527
528 status_t
intel_get_display_mode(display_mode * _currentMode)529 intel_get_display_mode(display_mode* _currentMode)
530 {
531 CALLED();
532
533 *_currentMode = gInfo->shared_info->current_mode;
534
535 // This seems unreliable. We should always know the current_mode
536 //retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL);
537 return B_OK;
538 }
539
540
541 status_t
intel_get_preferred_mode(display_mode * preferredMode)542 intel_get_preferred_mode(display_mode* preferredMode)
543 {
544 TRACE("%s\n", __func__);
545 display_mode mode;
546
547 if (gInfo->has_edid || !gInfo->shared_info->got_vbt
548 || !gInfo->shared_info->device_type.IsMobile()) {
549 return B_ERROR;
550 }
551
552 mode.timing = gInfo->shared_info->panel_timing;
553 mode.space = B_RGB32;
554 mode.virtual_width = mode.timing.h_display;
555 mode.virtual_height = mode.timing.v_display;
556 mode.h_display_start = 0;
557 mode.v_display_start = 0;
558 mode.flags = 0;
559 memcpy(preferredMode, &mode, sizeof(mode));
560 return B_OK;
561 }
562
563
564 status_t
intel_get_edid_info(void * info,size_t size,uint32 * _version)565 intel_get_edid_info(void* info, size_t size, uint32* _version)
566 {
567 if (!gInfo->has_edid)
568 return B_ERROR;
569 if (size < sizeof(struct edid1_info))
570 return B_BUFFER_OVERFLOW;
571
572 memcpy(info, &gInfo->edid_info, sizeof(struct edid1_info));
573 *_version = EDID_VERSION_1;
574 return B_OK;
575 }
576
577
578 // Get the backlight registers. We need the backlight frequency (we never write it, but we ned to
579 // know it's value as the duty cycle/brihtness level is proportional to it), and the duty cycle
580 // register (read to get the current backlight value, written to set it). On older generations,
581 // the two values are in the same register (16 bits each), on newer ones there are two separate
582 // registers.
583 static int32_t
intel_get_backlight_register(bool period)584 intel_get_backlight_register(bool period)
585 {
586 if (gInfo->shared_info->pch_info >= INTEL_PCH_CNP) {
587 if (period)
588 return PCH_SOUTH_BLC_PWM_PERIOD;
589 else
590 return PCH_SOUTH_BLC_PWM_DUTY_CYCLE;
591 } else if (gInfo->shared_info->pch_info >= INTEL_PCH_SPT)
592 return BLC_PWM_PCH_CTL2;
593
594 if (gInfo->shared_info->pch_info == INTEL_PCH_NONE)
595 return MCH_BLC_PWM_CTL;
596
597 // FIXME this mixup of south and north registers seems very strange; it should either be
598 // a single register with both period and duty in it, or two separate registers.
599 if (period)
600 return PCH_SOUTH_BLC_PWM_PERIOD;
601 else
602 return PCH_BLC_PWM_CTL;
603 }
604
605
606 status_t
intel_set_brightness(float brightness)607 intel_set_brightness(float brightness)
608 {
609 CALLED();
610
611 if (brightness < 0 || brightness > 1)
612 return B_BAD_VALUE;
613
614 // The "duty cycle" is a proportion of the period (0 = backlight off,
615 // period = maximum brightness).
616 // Additionally we don't want it to be completely 0 here, because then
617 // it becomes hard to turn the display on again (at least until we get
618 // working ACPI keyboard shortcuts for this). So always keep the backlight
619 // at least a little bit on for now.
620
621 if (gInfo->shared_info->pch_info >= INTEL_PCH_CNP) {
622 uint32_t period = read32(intel_get_backlight_register(true));
623
624 uint32_t duty = (uint32_t)(period * brightness);
625 duty = std::max(duty, (uint32_t)gInfo->shared_info->min_brightness);
626
627 write32(intel_get_backlight_register(false), duty);
628 } else if (gInfo->shared_info->pch_info >= INTEL_PCH_SPT) {
629 uint32_t period = read32(intel_get_backlight_register(true)) >> 16;
630
631 uint32_t duty = (uint32_t)(period * brightness) & 0xffff;
632 duty = std::max(duty, (uint32_t)gInfo->shared_info->min_brightness);
633
634 write32(intel_get_backlight_register(false), duty | (period << 16));
635 } else {
636 // On older devices there is a single register with both period and duty cycle
637 uint32 tmp = read32(intel_get_backlight_register(true));
638 bool legacyMode = false;
639 if (gInfo->shared_info->device_type.Generation() == 2
640 || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_915M)
641 || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_945M)) {
642 legacyMode = (tmp & BLM_LEGACY_MODE) != 0;
643 }
644
645 uint32_t period = tmp >> 16;
646
647 uint32_t mask = 0xffff;
648 uint32_t shift = 0;
649 if (gInfo->shared_info->device_type.Generation() < 4) {
650 // The low bit must be masked out because
651 // it is apparently used for something else on some Atom machines (no
652 // reference to that in the documentation that I know of).
653 mask = 0xfffe;
654 shift = 1;
655 period = tmp >> 17;
656 }
657 if (legacyMode)
658 period *= 0xfe;
659 uint32_t duty = (uint32_t)(period * brightness);
660 if (legacyMode) {
661 uint8 lpc = duty / 0xff + 1;
662 duty /= lpc;
663
664 // set pci config reg with lpc
665 intel_brightness_legacy brightnessLegacy;
666 brightnessLegacy.magic = INTEL_PRIVATE_DATA_MAGIC;
667 brightnessLegacy.lpc = lpc;
668 ioctl(gInfo->device, INTEL_SET_BRIGHTNESS_LEGACY, &brightnessLegacy,
669 sizeof(brightnessLegacy));
670 }
671
672 duty = std::max(duty, (uint32_t)gInfo->shared_info->min_brightness);
673 duty <<= shift;
674
675 write32(intel_get_backlight_register(false), (duty & mask) | (tmp & ~mask));
676 }
677
678 return B_OK;
679 }
680
681
682 status_t
intel_get_brightness(float * brightness)683 intel_get_brightness(float* brightness)
684 {
685 CALLED();
686
687 if (brightness == NULL)
688 return B_BAD_VALUE;
689
690 uint32_t duty;
691 uint32_t period;
692
693 if (gInfo->shared_info->pch_info >= INTEL_PCH_CNP) {
694 period = read32(intel_get_backlight_register(true));
695 duty = read32(intel_get_backlight_register(false));
696 } else {
697 uint32 tmp = read32(intel_get_backlight_register(true));
698 bool legacyMode = false;
699 if (gInfo->shared_info->device_type.Generation() == 2
700 || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_915M)
701 || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_945M)) {
702 legacyMode = (tmp & BLM_LEGACY_MODE) != 0;
703 }
704 period = tmp >> 16;
705 duty = read32(intel_get_backlight_register(false)) & 0xffff;
706 if (legacyMode) {
707 period *= 0xff;
708
709 // get lpc from pci config reg
710 intel_brightness_legacy brightnessLegacy;
711 brightnessLegacy.magic = INTEL_PRIVATE_DATA_MAGIC;
712 ioctl(gInfo->device, INTEL_GET_BRIGHTNESS_LEGACY, &brightnessLegacy,
713 sizeof(brightnessLegacy));
714 duty *= brightnessLegacy.lpc;
715 }
716 if (gInfo->shared_info->device_type.Generation() < 4) {
717 period >>= 1;
718 duty >>= 1;
719 }
720 }
721 *brightness = (float)duty / period;
722
723 return B_OK;
724 }
725
726
727 status_t
intel_get_frame_buffer_config(frame_buffer_config * config)728 intel_get_frame_buffer_config(frame_buffer_config* config)
729 {
730 CALLED();
731
732 uint32 offset = gInfo->shared_info->frame_buffer_offset;
733
734 config->frame_buffer = gInfo->shared_info->graphics_memory + offset;
735 config->frame_buffer_dma
736 = (uint8*)gInfo->shared_info->physical_graphics_memory + offset;
737 config->bytes_per_row = gInfo->shared_info->bytes_per_row;
738
739 return B_OK;
740 }
741
742
743 status_t
intel_get_pixel_clock_limits(display_mode * mode,uint32 * _low,uint32 * _high)744 intel_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
745 {
746 CALLED();
747
748 if (_low != NULL) {
749 // lower limit of about 48Hz vertical refresh
750 uint32 totalClocks = (uint32)mode->timing.h_total
751 * (uint32)mode->timing.v_total;
752 uint32 low = (totalClocks * 48L) / 1000L;
753 if (low < gInfo->shared_info->pll_info.min_frequency)
754 low = gInfo->shared_info->pll_info.min_frequency;
755 else if (low > gInfo->shared_info->pll_info.max_frequency)
756 return B_ERROR;
757
758 *_low = low;
759 }
760
761 if (_high != NULL)
762 *_high = gInfo->shared_info->pll_info.max_frequency;
763
764 return B_OK;
765 }
766
767
768 status_t
intel_move_display(uint16 horizontalStart,uint16 verticalStart)769 intel_move_display(uint16 horizontalStart, uint16 verticalStart)
770 {
771 intel_shared_info &sharedInfo = *gInfo->shared_info;
772 Autolock locker(sharedInfo.accelerant_lock);
773
774 display_mode &mode = sharedInfo.current_mode;
775
776 if (horizontalStart + mode.timing.h_display > mode.virtual_width
777 || verticalStart + mode.timing.v_display > mode.virtual_height)
778 return B_BAD_VALUE;
779
780 mode.h_display_start = horizontalStart;
781 mode.v_display_start = verticalStart;
782
783 set_frame_buffer_base();
784
785 return B_OK;
786 }
787
788
789 status_t
intel_get_timing_constraints(display_timing_constraints * constraints)790 intel_get_timing_constraints(display_timing_constraints* constraints)
791 {
792 CALLED();
793 return B_ERROR;
794 }
795
796
797 void
intel_set_indexed_colors(uint count,uint8 first,uint8 * colors,uint32 flags)798 intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags)
799 {
800 TRACE("%s(colors = %p, first = %u)\n", __func__, colors, first);
801
802 if (colors == NULL)
803 return;
804
805 Autolock locker(gInfo->shared_info->accelerant_lock);
806
807 for (; count-- > 0; first++) {
808 uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2];
809 colors += 3;
810
811 write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color);
812 write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color);
813 }
814 }
815