2 * Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Support for i915 chipset and up based on the X driver,
6 * Copyright 2006-2007 Intel Corporation.
9 * Axel Dörfler, axeld@pinc-software.de
19 #include <create_display_modes.h>
22 #include <validate_display_mode.h>
24 #include "accelerant_protos.h"
25 #include "accelerant.h"
34 # define TRACE(x...) _sPrintf("intel_extreme: " x)
39 #define ERROR(x...) _sPrintf("intel_extreme: " x)
40 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
44 // This hack needs to die. Leaving in for a little while
45 // incase we *really* need it.
47 retrieve_current_mode(display_mode
& mode
, uint32 pllRegister
)
49 uint32 pll
= read32(pllRegister
);
51 uint32 hTotalRegister
;
52 uint32 vTotalRegister
;
55 uint32 imageSizeRegister
;
56 uint32 controlRegister
;
58 if (pllRegister
== INTEL_DISPLAY_A_PLL
) {
59 pllDivisor
= read32((pll
& DISPLAY_PLL_DIVISOR_1
) != 0
60 ? INTEL_DISPLAY_A_PLL_DIVISOR_1
: INTEL_DISPLAY_A_PLL_DIVISOR_0
);
62 hTotalRegister
= INTEL_DISPLAY_A_HTOTAL
;
63 vTotalRegister
= INTEL_DISPLAY_A_VTOTAL
;
64 hSyncRegister
= INTEL_DISPLAY_A_HSYNC
;
65 vSyncRegister
= INTEL_DISPLAY_A_VSYNC
;
66 imageSizeRegister
= INTEL_DISPLAY_A_IMAGE_SIZE
;
67 controlRegister
= INTEL_DISPLAY_A_CONTROL
;
68 } else if (pllRegister
== INTEL_DISPLAY_B_PLL
) {
69 pllDivisor
= read32((pll
& DISPLAY_PLL_DIVISOR_1
) != 0
70 ? INTEL_DISPLAY_B_PLL_DIVISOR_1
: INTEL_DISPLAY_B_PLL_DIVISOR_0
);
72 hTotalRegister
= INTEL_DISPLAY_B_HTOTAL
;
73 vTotalRegister
= INTEL_DISPLAY_B_VTOTAL
;
74 hSyncRegister
= INTEL_DISPLAY_B_HSYNC
;
75 vSyncRegister
= INTEL_DISPLAY_B_VSYNC
;
76 imageSizeRegister
= INTEL_DISPLAY_B_IMAGE_SIZE
;
77 controlRegister
= INTEL_DISPLAY_B_CONTROL
;
79 ERROR("%s: PLL not supported\n", __func__
);
83 pll_divisors divisors
;
84 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_PIN
)) {
86 divisors
.m2
= (pllDivisor
& DISPLAY_PLL_IGD_M2_DIVISOR_MASK
)
87 >> DISPLAY_PLL_M2_DIVISOR_SHIFT
;
88 divisors
.n
= ((pllDivisor
& DISPLAY_PLL_IGD_N_DIVISOR_MASK
)
89 >> DISPLAY_PLL_N_DIVISOR_SHIFT
) - 1;
91 divisors
.m1
= (pllDivisor
& DISPLAY_PLL_M1_DIVISOR_MASK
)
92 >> DISPLAY_PLL_M1_DIVISOR_SHIFT
;
93 divisors
.m2
= (pllDivisor
& DISPLAY_PLL_M2_DIVISOR_MASK
)
94 >> DISPLAY_PLL_M2_DIVISOR_SHIFT
;
95 divisors
.n
= (pllDivisor
& DISPLAY_PLL_N_DIVISOR_MASK
)
96 >> DISPLAY_PLL_N_DIVISOR_SHIFT
;
100 get_pll_limits(&limits
, false);
101 // TODO: Detect LVDS connector vs assume no
103 if (gInfo
->shared_info
->device_type
.Generation() >= 3) {
105 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_PIN
)) {
106 divisors
.post1
= (pll
& DISPLAY_PLL_IGD_POST1_DIVISOR_MASK
)
107 >> DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT
;
109 divisors
.post1
= (pll
& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK
)
110 >> DISPLAY_PLL_POST1_DIVISOR_SHIFT
;
113 if (pllRegister
== INTEL_DISPLAY_B_PLL
114 && !gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_96x
)) {
115 // TODO: Fix this? Need to support dual channel LVDS.
116 divisors
.post2
= LVDS_POST2_RATE_SLOW
;
118 if ((pll
& DISPLAY_PLL_DIVIDE_HIGH
) != 0)
119 divisors
.post2
= limits
.max
.post2
;
121 divisors
.post2
= limits
.min
.post2
;
125 divisors
.post1
= (pll
& DISPLAY_PLL_POST1_DIVISOR_MASK
)
126 >> DISPLAY_PLL_POST1_DIVISOR_SHIFT
;
128 if ((pll
& DISPLAY_PLL_DIVIDE_4X
) != 0)
129 divisors
.post2
= limits
.max
.post2
;
131 divisors
.post2
= limits
.min
.post2
;
134 divisors
.m
= 5 * divisors
.m1
+ divisors
.m2
;
135 divisors
.post
= divisors
.post1
* divisors
.post2
;
138 = gInfo
->shared_info
->pll_info
.reference_frequency
/ 1000.0f
;
140 = ((referenceClock
* divisors
.m
) / divisors
.n
) / divisors
.post
;
144 mode
.timing
.pixel_clock
= uint32(pixelClock
* 1000);
145 mode
.timing
.flags
= 0;
147 uint32 value
= read32(hTotalRegister
);
148 mode
.timing
.h_total
= (value
>> 16) + 1;
149 mode
.timing
.h_display
= (value
& 0xffff) + 1;
151 value
= read32(hSyncRegister
);
152 mode
.timing
.h_sync_end
= (value
>> 16) + 1;
153 mode
.timing
.h_sync_start
= (value
& 0xffff) + 1;
155 value
= read32(vTotalRegister
);
156 mode
.timing
.v_total
= (value
>> 16) + 1;
157 mode
.timing
.v_display
= (value
& 0xffff) + 1;
159 value
= read32(vSyncRegister
);
160 mode
.timing
.v_sync_end
= (value
>> 16) + 1;
161 mode
.timing
.v_sync_start
= (value
& 0xffff) + 1;
163 // image size and color space
165 value
= read32(imageSizeRegister
);
166 mode
.virtual_width
= (value
>> 16) + 1;
167 mode
.virtual_height
= (value
& 0xffff) + 1;
169 // using virtual size based on image size is the 'proper' way to do it,
170 // however the bios appears to be suggesting scaling or somesuch, so ignore
171 // the proper virtual dimension for now if they'd suggest a smaller size.
172 if (mode
.virtual_width
< mode
.timing
.h_display
)
173 mode
.virtual_width
= mode
.timing
.h_display
;
174 if (mode
.virtual_height
< mode
.timing
.v_display
)
175 mode
.virtual_height
= mode
.timing
.v_display
;
177 value
= read32(controlRegister
);
178 switch (value
& DISPLAY_CONTROL_COLOR_MASK
) {
179 case DISPLAY_CONTROL_RGB32
:
181 mode
.space
= B_RGB32
;
183 case DISPLAY_CONTROL_RGB16
:
184 mode
.space
= B_RGB16
;
186 case DISPLAY_CONTROL_RGB15
:
187 mode
.space
= B_RGB15
;
189 case DISPLAY_CONTROL_CMAP8
:
190 mode
.space
= B_CMAP8
;
194 mode
.h_display_start
= 0;
195 mode
.v_display_start
= 0;
196 mode
.flags
= B_8_BIT_DAC
| B_HARDWARE_CURSOR
| B_PARALLEL_ACCESS
197 | B_DPMS
| B_SUPPORTS_OVERLAYS
;
203 get_color_space_format(const display_mode
&mode
, uint32
&colorMode
,
204 uint32
&bytesPerRow
, uint32
&bitsPerPixel
)
206 uint32 bytesPerPixel
;
208 switch (mode
.space
) {
210 colorMode
= DISPLAY_CONTROL_RGB32
;
215 colorMode
= DISPLAY_CONTROL_RGB16
;
220 colorMode
= DISPLAY_CONTROL_RGB15
;
226 colorMode
= DISPLAY_CONTROL_CMAP8
;
232 bytesPerRow
= mode
.virtual_width
* bytesPerPixel
;
234 // Make sure bytesPerRow is a multiple of 64
235 // TODO: check if the older chips have the same restriction!
236 if ((bytesPerRow
& 63) != 0)
237 bytesPerRow
= (bytesPerRow
+ 63) & ~63;
242 sanitize_display_mode(display_mode
& mode
)
244 // Some cards only support even pixel counts, while others require an odd
246 uint16 pixelCount
= 1;
247 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_Gxx
)
248 || gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_96x
)
249 || gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_94x
)
250 || gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_91x
)
251 || gInfo
->shared_info
->device_type
.InFamily(INTEL_FAMILY_8xx
)
252 || gInfo
->shared_info
->device_type
.InFamily(INTEL_FAMILY_7xx
)) {
256 // TODO: verify constraints - these are more or less taken from the
258 display_constraints constraints
= {
260 320, 8192, 200, 4096,
262 gInfo
->shared_info
->pll_info
.min_frequency
,
263 gInfo
->shared_info
->pll_info
.max_frequency
,
265 {pixelCount
, 0, 8160, 32, 8192, 0, 8192},
266 {1, 1, 4092, 2, 63, 1, 4096}
269 return sanitize_display_mode(mode
, constraints
,
270 gInfo
->has_edid
? &gInfo
->edid_info
: NULL
);
278 set_frame_buffer_registers(uint32 baseRegister
, uint32 surfaceRegister
)
280 intel_shared_info
&sharedInfo
= *gInfo
->shared_info
;
281 display_mode
&mode
= gInfo
->current_mode
;
283 if (sharedInfo
.device_type
.InGroup(INTEL_GROUP_96x
)
284 || sharedInfo
.device_type
.InGroup(INTEL_GROUP_G4x
)
285 || sharedInfo
.device_type
.InGroup(INTEL_GROUP_ILK
)
286 || sharedInfo
.device_type
.InFamily(INTEL_FAMILY_SER5
)
287 || sharedInfo
.device_type
.InFamily(INTEL_FAMILY_SOC0
)) {
288 write32(baseRegister
, mode
.v_display_start
* sharedInfo
.bytes_per_row
289 + mode
.h_display_start
* (sharedInfo
.bits_per_pixel
+ 7) / 8);
290 read32(baseRegister
);
291 write32(surfaceRegister
, sharedInfo
.frame_buffer_offset
);
292 read32(surfaceRegister
);
294 write32(baseRegister
, sharedInfo
.frame_buffer_offset
295 + mode
.v_display_start
* sharedInfo
.bytes_per_row
296 + mode
.h_display_start
* (sharedInfo
.bits_per_pixel
+ 7) / 8);
297 read32(baseRegister
);
303 set_frame_buffer_base()
305 // TODO we always set both displays to the same address. When we support
306 // multiple framebuffers, they should get different addresses here.
307 set_frame_buffer_registers(INTEL_DISPLAY_A_BASE
, INTEL_DISPLAY_A_SURFACE
);
308 set_frame_buffer_registers(INTEL_DISPLAY_B_BASE
, INTEL_DISPLAY_B_SURFACE
);
312 /*! Creates the initial mode list of the primary accelerant.
313 It's called from intel_init_accelerant().
316 create_mode_list(void)
318 for (uint32 i
= 0; i
< gInfo
->port_count
; i
++) {
319 if (gInfo
->ports
[i
] == NULL
)
322 status_t status
= gInfo
->ports
[i
]->GetEDID(&gInfo
->edid_info
);
324 gInfo
->has_edid
= true;
327 // If no EDID, but have vbt from driver, use that mode
328 if (!gInfo
->has_edid
&& gInfo
->shared_info
->got_vbt
) {
329 // We could not read any EDID info. Fallback to creating a list with
330 // only the mode set up by the BIOS.
332 // TODO: support lower modes via scaling and windowing
333 size_t size
= (sizeof(display_mode
) + B_PAGE_SIZE
- 1)
334 & ~(B_PAGE_SIZE
- 1);
337 area_id area
= create_area("intel extreme modes",
338 (void**)&list
, B_ANY_ADDRESS
, size
, B_NO_LOCK
,
339 B_READ_AREA
| B_WRITE_AREA
);
343 memcpy(list
, &gInfo
->shared_info
->panel_mode
, sizeof(display_mode
));
345 gInfo
->mode_list_area
= area
;
346 gInfo
->mode_list
= list
;
347 gInfo
->shared_info
->mode_list_area
= gInfo
->mode_list_area
;
348 gInfo
->shared_info
->mode_count
= 1;
352 // Otherwise return the 'real' list of modes
355 gInfo
->mode_list_area
= create_display_modes("intel extreme modes",
356 gInfo
->has_edid
? &gInfo
->edid_info
: NULL
, NULL
, 0, NULL
, 0, NULL
,
358 if (gInfo
->mode_list_area
< B_OK
)
359 return gInfo
->mode_list_area
;
361 gInfo
->mode_list
= list
;
362 gInfo
->shared_info
->mode_list_area
= gInfo
->mode_list_area
;
363 gInfo
->shared_info
->mode_count
= count
;
370 wait_for_vblank(void)
372 acquire_sem_etc(gInfo
->shared_info
->vblank_sem
, 1, B_RELATIVE_TIMEOUT
,
374 // With the output turned off via DPMS, we might not get any interrupts
375 // anymore that's why we don't wait forever for it.
383 intel_accelerant_mode_count(void)
386 return gInfo
->shared_info
->mode_count
;
391 intel_get_mode_list(display_mode
* modeList
)
394 memcpy(modeList
, gInfo
->mode_list
,
395 gInfo
->shared_info
->mode_count
* sizeof(display_mode
));
401 intel_propose_display_mode(display_mode
* target
, const display_mode
* low
,
402 const display_mode
* high
)
406 // first search for the specified mode in the list, if no mode is found
407 // try to fix the target mode in sanitize_display_mode
408 // TODO: Only sanitize_display_mode should be used. However, at the moments
409 // the mode constraints are not optimal and do not work for all
411 for (uint32 i
= 0; i
< gInfo
->shared_info
->mode_count
; i
++) {
412 display_mode
*mode
= &gInfo
->mode_list
[i
];
414 // TODO: improve this, ie. adapt pixel clock to allowed values!!!
416 if (target
->virtual_width
!= mode
->virtual_width
417 || target
->virtual_height
!= mode
->virtual_height
418 || target
->space
!= mode
->space
) {
426 sanitize_display_mode(*target
);
428 return is_display_mode_within_bounds(*target
, *low
, *high
)
429 ? B_OK
: B_BAD_VALUE
;
434 intel_set_display_mode(display_mode
* mode
)
436 TRACE("%s(%" B_PRIu16
"x%" B_PRIu16
")\n", __func__
,
437 mode
->virtual_width
, mode
->virtual_height
);
442 display_mode target
= *mode
;
444 // TODO: it may be acceptable to continue when using panel fitting or
445 // centering, since the data from propose_display_mode will not actually be
446 // used as is in this case.
447 if (sanitize_display_mode(target
)) {
448 TRACE("Video mode was adjusted by sanitize_display_mode\n");
449 TRACE("Initial mode: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n",
450 mode
->timing
.h_display
, mode
->timing
.h_sync_start
,
451 mode
->timing
.h_sync_end
, mode
->timing
.h_total
,
452 mode
->timing
.v_display
, mode
->timing
.v_sync_start
,
453 mode
->timing
.v_sync_end
, mode
->timing
.v_total
);
454 TRACE("Sanitized: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n",
455 target
.timing
.h_display
, target
.timing
.h_sync_start
,
456 target
.timing
.h_sync_end
, target
.timing
.h_total
,
457 target
.timing
.v_display
, target
.timing
.v_sync_start
,
458 target
.timing
.v_sync_end
, target
.timing
.v_total
);
461 uint32 colorMode
, bytesPerRow
, bitsPerPixel
;
462 get_color_space_format(target
, colorMode
, bytesPerRow
, bitsPerPixel
);
464 // TODO: do not go further if the mode is identical to the current one.
465 // This would avoid the screen being off when switching workspaces when they
466 // have the same resolution.
468 intel_shared_info
&sharedInfo
= *gInfo
->shared_info
;
469 Autolock
locker(sharedInfo
.accelerant_lock
);
471 // First register dump
474 // TODO: This may not be neccesary
475 set_display_power_mode(B_DPMS_OFF
);
477 // free old and allocate new frame buffer in graphics memory
479 intel_free_memory(sharedInfo
.frame_buffer
);
482 if (intel_allocate_memory(bytesPerRow
* target
.virtual_height
, 0,
484 // oh, how did that happen? Unfortunately, there is no really good way
486 if (intel_allocate_memory(gInfo
->current_mode
.virtual_height
487 * sharedInfo
.bytes_per_row
, 0, base
) == B_OK
) {
488 sharedInfo
.frame_buffer
= base
;
489 sharedInfo
.frame_buffer_offset
= base
490 - (addr_t
)sharedInfo
.graphics_memory
;
491 set_frame_buffer_base();
494 TRACE("%s: Failed to allocate framebuffer !\n", __func__
);
498 // clear frame buffer before using it
499 memset((uint8
*)base
, 0, bytesPerRow
* target
.virtual_height
);
500 sharedInfo
.frame_buffer
= base
;
501 sharedInfo
.frame_buffer_offset
= base
- (addr_t
)sharedInfo
.graphics_memory
;
504 if ((gInfo
->head_mode
& HEAD_MODE_TESTING
) != 0) {
505 // 1. Enable panel power as needed to retrieve panel configuration
506 // (use AUX VDD enable bit)
507 // skip, did detection already, might need that before that though
509 // 2. Enable PCH clock reference source and PCH SSC modulator,
510 // wait for warmup (Can be done anytime before enabling port)
511 // skip, most certainly already set up by bios to use other ports,
512 // will need for coldstart though
514 // 3. If enabling CPU embedded DisplayPort A: (Can be done anytime
515 // before enabling CPU pipe or port)
516 // a. Enable PCH 120MHz clock source output to CPU, wait for DMI
518 // b. Configure and enable CPU DisplayPort PLL in the DisplayPort A
519 // register, wait for warmup
520 // skip, not doing eDP right now, should go into
521 // EmbeddedDisplayPort class though
523 // 4. If enabling port on PCH: (Must be done before enabling CPU pipe
525 // a. Enable PCH FDI Receiver PLL, wait for warmup plus DMI latency
526 // b. Switch from Rawclk to PCDclk in FDI Receiver (FDI A OR FDI B)
527 // c. [DevSNB] Enable CPU FDI Transmitter PLL, wait for warmup
528 // d. [DevILK] CPU FDI PLL is always on and does not need to be
530 FDILink
* link
= pipe
->FDILink();
532 link
->Receiver().EnablePLL();
533 link
->Receiver().SwitchClock(true);
534 link
->Transmitter().EnablePLL();
537 // 5. Enable CPU panel fitter if needed for hires, required for VGA
538 // (Can be done anytime before enabling CPU pipe)
539 PanelFitter
* fitter
= pipe
->PanelFitter();
541 fitter
->Enable(mode
);
543 // 6. Configure CPU pipe timings, M/N/TU, and other pipe settings
544 // (Can be done anytime before enabling CPU pipe)
545 pll_divisors divisors
;
546 compute_pll_divisors(target
, divisors
, false);
547 pipe
->ConfigureTimings(divisors
);
549 // 7. Enable CPU pipe
552 8. Configure
and enable CPU
planes (VGA
or hires
)
553 9. If enabling port on PCH
:
554 // a. Program PCH FDI Receiver TU size same as Transmitter TU size for TU error checking
556 // i. Set pre-emphasis and voltage (iterate if training steps fail)
557 ii
. Enable CPU FDI Transmitter
and PCH FDI Receiver with Training Pattern
1 enabled
.
558 iii
. Wait
for FDI training pattern
1 time
559 iv
. Read PCH FDI Receiver
ISR ([DevIBX
-B
+] IIR
) for bit lock in bit
8 (retry at least once
if no lock
)
560 v
. Enable training pattern
2 on CPU FDI Transmitter
and PCH FDI Receiver
561 vi
. Wait
for FDI training pattern
2 time
562 vii
. Read PCH FDI Receiver
ISR ([DevIBX
-B
+] IIR
) for symbol lock in bit
9 (retry at least once
if no
564 viii
. Enable normal pixel output on CPU FDI Transmitter
and PCH FDI Receiver
565 ix
. Wait
for FDI idle pattern time
for link to become active
566 c
. Configure
and enable PCH DPLL
, wait
for PCH DPLL
warmup (Can be done anytime before enabling
568 d
. [DevCPT
] Configure DPLL SEL to set the DPLL to transcoder mapping
and enable DPLL to the
570 e
. [DevCPT
] Configure DPLL_CTL DPLL_HDMI_multipler
.
571 f
. Configure PCH transcoder timings
, M
/N
/TU
, and other transcoder
settings (should match CPU settings
).
572 g
. [DevCPT
] Configure
and enable Transcoder DisplayPort Control
if DisplayPort will be used
573 h
. Enable PCH transcoder
574 10. Enable
ports (DisplayPort must enable in training pattern
1)
575 11. Enable panel power through panel power sequencing
576 12. Wait
for panel power sequencing to reach enabled steady state
577 13. Disable panel power override
578 14. If DisplayPort
, complete link training
579 15. Enable panel backlight
583 // make sure VGA display is disabled
584 write32(INTEL_VGA_DISPLAY_CONTROL
, VGA_DISPLAY_DISABLED
);
585 read32(INTEL_VGA_DISPLAY_CONTROL
);
587 // Go over each port and set the display mode
588 for (uint32 i
= 0; i
< gInfo
->port_count
; i
++) {
589 if (gInfo
->ports
[i
] == NULL
)
591 if (!gInfo
->ports
[i
]->IsConnected())
594 status_t status
= gInfo
->ports
[i
]->SetDisplayMode(&target
, colorMode
);
596 ERROR("%s: Unable to set display mode!\n", __func__
);
599 TRACE("%s: Port configuration completed successfully!\n", __func__
);
601 // We set the same color mode across all pipes
602 program_pipe_color_modes(colorMode
);
604 // TODO: This may not be neccesary (see DPMS OFF at top)
605 set_display_power_mode(sharedInfo
.dpms_mode
);
607 // Changing bytes per row seems to be ignored if the plane/pipe is turned
610 // Always set both pipes, just in case
611 // TODO rework this when we get multiple head support with different
613 write32(INTEL_DISPLAY_A_BYTES_PER_ROW
, bytesPerRow
);
614 write32(INTEL_DISPLAY_B_BYTES_PER_ROW
, bytesPerRow
);
616 // update shared info
617 gInfo
->current_mode
= target
;
619 // TODO: move to gInfo
620 sharedInfo
.bytes_per_row
= bytesPerRow
;
621 sharedInfo
.bits_per_pixel
= bitsPerPixel
;
623 set_frame_buffer_base();
624 // triggers writing back double-buffered registers
626 // Second register dump
634 intel_get_display_mode(display_mode
* _currentMode
)
638 *_currentMode
= gInfo
->current_mode
;
640 // This seems unreliable. We should always know the current_mode
641 //retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL);
647 intel_get_edid_info(void* info
, size_t size
, uint32
* _version
)
651 if (!gInfo
->has_edid
)
653 if (size
< sizeof(struct edid1_info
))
654 return B_BUFFER_OVERFLOW
;
656 memcpy(info
, &gInfo
->edid_info
, sizeof(struct edid1_info
));
657 *_version
= EDID_VERSION_1
;
663 intel_get_backlight_register(bool read
)
665 if (gInfo
->shared_info
->pch_info
== INTEL_PCH_NONE
)
666 return MCH_BLC_PWM_CTL
;
669 return PCH_SBLC_PWM_CTL2
;
671 return PCH_BLC_PWM_CTL
;
676 intel_set_brightness(float brightness
)
680 if (brightness
< 0 || brightness
> 1)
683 uint32_t period
= read32(intel_get_backlight_register(true)) >> 16;
684 uint32_t duty
= (uint32_t)(period
* brightness
) & 0xfffe;
685 /* Setting the low bit seems to give strange results on some Atom machines */
686 write32(intel_get_backlight_register(false), duty
| (period
<< 16));
693 intel_get_brightness(float* brightness
)
697 if (brightness
== NULL
)
700 uint16_t period
= read32(intel_get_backlight_register(true)) >> 16;
701 uint16_t duty
= read32(intel_get_backlight_register(false)) & 0xffff;
702 *brightness
= (float)duty
/ period
;
709 intel_get_frame_buffer_config(frame_buffer_config
* config
)
713 uint32 offset
= gInfo
->shared_info
->frame_buffer_offset
;
715 config
->frame_buffer
= gInfo
->shared_info
->graphics_memory
+ offset
;
716 config
->frame_buffer_dma
717 = (uint8
*)gInfo
->shared_info
->physical_graphics_memory
+ offset
;
718 config
->bytes_per_row
= gInfo
->shared_info
->bytes_per_row
;
725 intel_get_pixel_clock_limits(display_mode
* mode
, uint32
* _low
, uint32
* _high
)
730 // lower limit of about 48Hz vertical refresh
731 uint32 totalClocks
= (uint32
)mode
->timing
.h_total
732 * (uint32
)mode
->timing
.v_total
;
733 uint32 low
= (totalClocks
* 48L) / 1000L;
734 if (low
< gInfo
->shared_info
->pll_info
.min_frequency
)
735 low
= gInfo
->shared_info
->pll_info
.min_frequency
;
736 else if (low
> gInfo
->shared_info
->pll_info
.max_frequency
)
743 *_high
= gInfo
->shared_info
->pll_info
.max_frequency
;
750 intel_move_display(uint16 horizontalStart
, uint16 verticalStart
)
754 intel_shared_info
&sharedInfo
= *gInfo
->shared_info
;
755 Autolock
locker(sharedInfo
.accelerant_lock
);
757 display_mode
&mode
= gInfo
->current_mode
;
759 if (horizontalStart
+ mode
.timing
.h_display
> mode
.virtual_width
760 || verticalStart
+ mode
.timing
.v_display
> mode
.virtual_height
)
763 mode
.h_display_start
= horizontalStart
;
764 mode
.v_display_start
= verticalStart
;
766 set_frame_buffer_base();
773 intel_get_timing_constraints(display_timing_constraints
* constraints
)
781 intel_set_indexed_colors(uint count
, uint8 first
, uint8
* colors
, uint32 flags
)
783 TRACE("%s(colors = %p, first = %u)\n", __func__
, colors
, first
);
788 Autolock
locker(gInfo
->shared_info
->accelerant_lock
);
790 for (; count
-- > 0; first
++) {
791 uint32 color
= colors
[0] << 16 | colors
[1] << 8 | colors
[2];
794 write32(INTEL_DISPLAY_A_PALETTE
+ first
* sizeof(uint32
), color
);
795 write32(INTEL_DISPLAY_B_PALETTE
+ first
* sizeof(uint32
), color
);