vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / accelerants / intel_extreme / mode.cpp
blob4bf08e792aec142b806fcddc5643714be3058dde
1 /*
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.
8 * Authors:
9 * Axel Dörfler, axeld@pinc-software.de
13 #include <math.h>
14 #include <stdio.h>
15 #include <string.h>
17 #include <Debug.h>
19 #include <create_display_modes.h>
20 #include <ddc.h>
21 #include <edid.h>
22 #include <validate_display_mode.h>
24 #include "accelerant_protos.h"
25 #include "accelerant.h"
26 #include "pll.h"
27 #include "Ports.h"
28 #include "utility.h"
31 #undef TRACE
32 #define TRACE_MODE
33 #ifdef TRACE_MODE
34 # define TRACE(x...) _sPrintf("intel_extreme: " x)
35 #else
36 # define TRACE(x...)
37 #endif
39 #define ERROR(x...) _sPrintf("intel_extreme: " x)
40 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
43 #if 0
44 // This hack needs to die. Leaving in for a little while
45 // incase we *really* need it.
46 static void
47 retrieve_current_mode(display_mode& mode, uint32 pllRegister)
49 uint32 pll = read32(pllRegister);
50 uint32 pllDivisor;
51 uint32 hTotalRegister;
52 uint32 vTotalRegister;
53 uint32 hSyncRegister;
54 uint32 vSyncRegister;
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;
78 } else {
79 ERROR("%s: PLL not supported\n", __func__);
80 return;
83 pll_divisors divisors;
84 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
85 divisors.m1 = 0;
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;
90 } else {
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;
99 pll_limits limits;
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;
108 } else {
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;
117 } else {
118 if ((pll & DISPLAY_PLL_DIVIDE_HIGH) != 0)
119 divisors.post2 = limits.max.post2;
120 else
121 divisors.post2 = limits.min.post2;
123 } else {
124 // 8xx
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;
130 else
131 divisors.post2 = limits.min.post2;
134 divisors.m = 5 * divisors.m1 + divisors.m2;
135 divisors.post = divisors.post1 * divisors.post2;
137 float referenceClock
138 = gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
139 float pixelClock
140 = ((referenceClock * divisors.m) / divisors.n) / divisors.post;
142 // timing
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:
180 default:
181 mode.space = B_RGB32;
182 break;
183 case DISPLAY_CONTROL_RGB16:
184 mode.space = B_RGB16;
185 break;
186 case DISPLAY_CONTROL_RGB15:
187 mode.space = B_RGB15;
188 break;
189 case DISPLAY_CONTROL_CMAP8:
190 mode.space = B_CMAP8;
191 break;
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;
199 #endif
202 static void
203 get_color_space_format(const display_mode &mode, uint32 &colorMode,
204 uint32 &bytesPerRow, uint32 &bitsPerPixel)
206 uint32 bytesPerPixel;
208 switch (mode.space) {
209 case B_RGB32_LITTLE:
210 colorMode = DISPLAY_CONTROL_RGB32;
211 bytesPerPixel = 4;
212 bitsPerPixel = 32;
213 break;
214 case B_RGB16_LITTLE:
215 colorMode = DISPLAY_CONTROL_RGB16;
216 bytesPerPixel = 2;
217 bitsPerPixel = 16;
218 break;
219 case B_RGB15_LITTLE:
220 colorMode = DISPLAY_CONTROL_RGB15;
221 bytesPerPixel = 2;
222 bitsPerPixel = 15;
223 break;
224 case B_CMAP8:
225 default:
226 colorMode = DISPLAY_CONTROL_CMAP8;
227 bytesPerPixel = 1;
228 bitsPerPixel = 8;
229 break;
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;
241 static bool
242 sanitize_display_mode(display_mode& mode)
244 // Some cards only support even pixel counts, while others require an odd
245 // one.
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)) {
253 pixelCount = 2;
256 // TODO: verify constraints - these are more or less taken from the
257 // radeon driver!
258 display_constraints constraints = {
259 // resolution
260 320, 8192, 200, 4096,
261 // pixel clock
262 gInfo->shared_info->pll_info.min_frequency,
263 gInfo->shared_info->pll_info.max_frequency,
264 // horizontal
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);
274 // #pragma mark -
277 static void
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);
293 } else {
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);
302 void
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().
315 status_t
316 create_mode_list(void)
318 for (uint32 i = 0; i < gInfo->port_count; i++) {
319 if (gInfo->ports[i] == NULL)
320 continue;
322 status_t status = gInfo->ports[i]->GetEDID(&gInfo->edid_info);
323 if (status == B_OK)
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);
336 display_mode* list;
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);
340 if (area < 0)
341 return 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;
349 return B_OK;
352 // Otherwise return the 'real' list of modes
353 display_mode* list;
354 uint32 count = 0;
355 gInfo->mode_list_area = create_display_modes("intel extreme modes",
356 gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0, NULL, 0, NULL,
357 &list, &count);
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;
365 return B_OK;
369 void
370 wait_for_vblank(void)
372 acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT,
373 25000);
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.
379 // #pragma mark -
382 uint32
383 intel_accelerant_mode_count(void)
385 CALLED();
386 return gInfo->shared_info->mode_count;
390 status_t
391 intel_get_mode_list(display_mode* modeList)
393 CALLED();
394 memcpy(modeList, gInfo->mode_list,
395 gInfo->shared_info->mode_count * sizeof(display_mode));
396 return B_OK;
400 status_t
401 intel_propose_display_mode(display_mode* target, const display_mode* low,
402 const display_mode* high)
404 CALLED();
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
410 // configurations.
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) {
419 continue;
422 *target = *mode;
423 return B_OK;
426 sanitize_display_mode(*target);
428 return is_display_mode_within_bounds(*target, *low, *high)
429 ? B_OK : B_BAD_VALUE;
433 status_t
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);
439 if (mode == NULL)
440 return B_BAD_VALUE;
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
472 //dump_registers();
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);
481 addr_t base;
482 if (intel_allocate_memory(bytesPerRow * target.virtual_height, 0,
483 base) < B_OK) {
484 // oh, how did that happen? Unfortunately, there is no really good way
485 // back
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__);
495 return B_NO_MEMORY;
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;
503 #if 0
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
517 // latency
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
524 // or FDI)
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
529 // enabled
530 FDILink* link = pipe->FDILink();
531 if (link != NULL) {
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();
540 if (fitter != NULL)
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
550 pipe->Enable();
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
555 // b. Train FDI
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
563 lock)
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
567 PCH transcoder)
568 d. [DevCPT] Configure DPLL SEL to set the DPLL to transcoder mapping and enable DPLL to the
569 transcoder.
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
581 #endif
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)
590 continue;
591 if (!gInfo->ports[i]->IsConnected())
592 continue;
594 status_t status = gInfo->ports[i]->SetDisplayMode(&target, colorMode);
595 if (status != B_OK)
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
608 // off
610 // Always set both pipes, just in case
611 // TODO rework this when we get multiple head support with different
612 // resolutions
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
627 //dump_registers();
629 return B_OK;
633 status_t
634 intel_get_display_mode(display_mode* _currentMode)
636 CALLED();
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);
642 return B_OK;
646 status_t
647 intel_get_edid_info(void* info, size_t size, uint32* _version)
649 CALLED();
651 if (!gInfo->has_edid)
652 return B_ERROR;
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;
658 return B_OK;
662 static int32_t
663 intel_get_backlight_register(bool read)
665 if (gInfo->shared_info->pch_info == INTEL_PCH_NONE)
666 return MCH_BLC_PWM_CTL;
668 if (read)
669 return PCH_SBLC_PWM_CTL2;
670 else
671 return PCH_BLC_PWM_CTL;
675 status_t
676 intel_set_brightness(float brightness)
678 CALLED();
680 if (brightness < 0 || brightness > 1)
681 return B_BAD_VALUE;
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));
688 return B_OK;
692 status_t
693 intel_get_brightness(float* brightness)
695 CALLED();
697 if (brightness == NULL)
698 return B_BAD_VALUE;
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;
704 return B_OK;
708 status_t
709 intel_get_frame_buffer_config(frame_buffer_config* config)
711 CALLED();
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;
720 return B_OK;
724 status_t
725 intel_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
727 CALLED();
729 if (_low != NULL) {
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)
737 return B_ERROR;
739 *_low = low;
742 if (_high != NULL)
743 *_high = gInfo->shared_info->pll_info.max_frequency;
745 return B_OK;
749 status_t
750 intel_move_display(uint16 horizontalStart, uint16 verticalStart)
752 CALLED();
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)
761 return B_BAD_VALUE;
763 mode.h_display_start = horizontalStart;
764 mode.v_display_start = verticalStart;
766 set_frame_buffer_base();
768 return B_OK;
772 status_t
773 intel_get_timing_constraints(display_timing_constraints* constraints)
775 CALLED();
776 return B_ERROR;
780 void
781 intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags)
783 TRACE("%s(colors = %p, first = %u)\n", __func__, colors, first);
785 if (colors == NULL)
786 return;
788 Autolock locker(gInfo->shared_info->accelerant_lock);
790 for (; count-- > 0; first++) {
791 uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2];
792 colors += 3;
794 write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color);
795 write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color);