* Copyright 2006-2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Support for i915 chipset and up based on the X driver,
* Copyright 2006-2007 Intel Corporation.
*
* Authors:
* Axel Dörfler, axeld@pinc-software.de
* Alexander von Gluck, kallisti5@unixzen.com
*/
#include "mode.h"
#include <create_display_modes.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "accelerant.h"
#include "accelerant_protos.h"
#include "bios.h"
#include "connector.h"
#include "display.h"
#include "displayport.h"
#include "encoder.h"
#include "pll.h"
#include "utility.h"
#define TRACE_MODE
#ifdef TRACE_MODE
extern "C" void _sPrintf(const char* format, ...);
# define TRACE(x...) _sPrintf("radeon_hd: " x)
#else
# define TRACE(x...) ;
#endif
#define ERROR(x...) _sPrintf("radeon_hd: " x)
status_t
create_mode_list(void)
{
uint8 crtcID = 0;
const color_space kRadeonHDSpaces[] = {B_RGB32_LITTLE, B_RGB24_LITTLE,
B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8};
gInfo->mode_list_area = create_display_modes("radeon HD modes",
&gDisplay[crtcID]->edidData, NULL, 0, kRadeonHDSpaces,
B_COUNT_OF(kRadeonHDSpaces), is_mode_supported, &gInfo->mode_list,
&gInfo->shared_info->mode_count);
if (gInfo->mode_list_area < B_OK)
return gInfo->mode_list_area;
gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
return B_OK;
}
uint32
radeon_accelerant_mode_count(void)
{
TRACE("%s\n", __func__);
return gInfo->shared_info->mode_count;
}
status_t
radeon_get_mode_list(display_mode* modeList)
{
TRACE("%s\n", __func__);
memcpy(modeList, gInfo->mode_list,
gInfo->shared_info->mode_count * sizeof(display_mode));
return B_OK;
}
status_t
radeon_get_preferred_mode(display_mode* preferredMode)
{
TRACE("%s\n", __func__);
uint8_t crtc = 0;
if (gDisplay[crtc]->preferredMode.virtual_width > 0
&& gDisplay[crtc]->preferredMode.virtual_height > 0) {
TRACE("%s: preferred mode was found for display %" B_PRIu8 "\n",
__func__, crtc);
memcpy(preferredMode, &gDisplay[crtc]->preferredMode,
sizeof(gDisplay[crtc]->preferredMode));
return B_OK;
}
return B_ERROR;
}
status_t
radeon_get_edid_info(void* info, size_t size, uint32* edid_version)
{
uint8 crtcID = 0;
TRACE("%s\n", __func__);
if (!gInfo->shared_info->has_edid)
return B_ERROR;
if (size < sizeof(struct edid1_info))
return B_BUFFER_OVERFLOW;
memcpy(info, &gDisplay[crtcID]->edidData, sizeof(struct edid1_info));
*edid_version = EDID_VERSION_1;
return B_OK;
}
uint32
radeon_dpms_capabilities(void)
{
return B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF;
}
uint32
radeon_dpms_mode(void)
{
return gInfo->dpms_mode;
}
void
radeon_dpms_set(uint8 id, int mode)
{
if (mode == B_DPMS_ON) {
display_crtc_dpms(id, mode);
encoder_dpms_set(id, mode);
} else {
encoder_dpms_set(id, mode);
display_crtc_dpms(id, mode);
}
gInfo->dpms_mode = mode;
}
void
radeon_dpms_set_hook(int mode)
{
uint8 crtcID = 0;
if (gDisplay[crtcID]->attached)
radeon_dpms_set(crtcID, mode);
}
status_t
radeon_set_display_mode(display_mode* mode)
{
TRACE("%s\n", __func__);
TRACE(" mode->space: %#" B_PRIx32 "\n", mode->space);
TRACE(" mode->virtual_width: %" B_PRIu16 "\n", mode->virtual_width);
TRACE(" mode->virtual_height: %" B_PRIu16 "\n", mode->virtual_height);
TRACE(" mode->h_display_start: %" B_PRIu16 "\n", mode->h_display_start);
TRACE(" mode->v_display_start: %" B_PRIu16 "\n", mode->v_display_start);
TRACE(" mode->flags: %#" B_PRIx32 "\n", mode->flags);
uint8 crtcID = 0;
if (gDisplay[crtcID]->attached == false)
return B_ERROR;
memcpy(&gDisplay[crtcID]->currentMode, mode, sizeof(display_mode));
uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
if (connector_is_dp(connectorIndex)) {
dp_info *dpInfo = &gConnector[connectorIndex]->dpInfo;
dpInfo->laneCount = dp_get_lane_count(connectorIndex, mode);
dpInfo->linkRate = dp_get_link_rate(connectorIndex, mode);
}
encoder_output_lock(true);
display_crtc_lock(crtcID, ATOM_ENABLE);
radeon_dpms_set(crtcID, B_DPMS_OFF);
encoder_assign_crtc(crtcID);
pll_pick(connectorIndex);
pll_info* pll = &gConnector[connectorIndex]->encoder.pll;
TRACE("%s: pll %d selected for connector %" B_PRIu32 "\n", __func__,
pll->id, connectorIndex);
pll_set(mode, crtcID);
display_crtc_set_dtd(crtcID, mode);
display_crtc_fb_set(crtcID, mode);
display_crtc_scale(crtcID, mode);
encoder_mode_set(crtcID);
radeon_dpms_set(crtcID, B_DPMS_ON);
display_crtc_lock(crtcID, ATOM_DISABLE);
encoder_output_lock(false);
#ifdef TRACE_MODE
debug_dp_info();
TRACE("D1CRTC_STATUS Value: 0x%X\n",
Read32(CRT, AVIVO_D1CRTC_STATUS));
TRACE("D2CRTC_STATUS Value: 0x%X\n",
Read32(CRT, AVIVO_D2CRTC_STATUS));
TRACE("D1CRTC_CONTROL Value: 0x%X\n",
Read32(CRT, AVIVO_D1CRTC_CONTROL));
TRACE("D2CRTC_CONTROL Value: 0x%X\n",
Read32(CRT, AVIVO_D2CRTC_CONTROL));
TRACE("D1GRPH_ENABLE Value: 0x%X\n",
Read32(CRT, AVIVO_D1GRPH_ENABLE));
TRACE("D2GRPH_ENABLE Value: 0x%X\n",
Read32(CRT, AVIVO_D2GRPH_ENABLE));
TRACE("D1SCL_ENABLE Value: 0x%X\n",
Read32(CRT, AVIVO_D1SCL_SCALER_ENABLE));
TRACE("D2SCL_ENABLE Value: 0x%X\n",
Read32(CRT, AVIVO_D2SCL_SCALER_ENABLE));
TRACE("D1CRTC_BLANK_CONTROL Value: 0x%X\n",
Read32(CRT, AVIVO_D1CRTC_BLANK_CONTROL));
TRACE("D2CRTC_BLANK_CONTROL Value: 0x%X\n",
Read32(CRT, AVIVO_D1CRTC_BLANK_CONTROL));
#endif
return B_OK;
}
status_t
radeon_get_display_mode(display_mode* _currentMode)
{
TRACE("%s\n", __func__);
*_currentMode = gInfo->shared_info->current_mode;
return B_OK;
}
status_t
radeon_get_frame_buffer_config(frame_buffer_config* config)
{
TRACE("%s\n", __func__);
config->frame_buffer = gInfo->shared_info->frame_buffer;
config->frame_buffer_dma = (uint8*)gInfo->shared_info->frame_buffer_phys;
config->bytes_per_row = gInfo->shared_info->bytes_per_row;
TRACE(" config->frame_buffer: %#" B_PRIxADDR "\n",
(phys_addr_t)config->frame_buffer);
TRACE(" config->frame_buffer_dma: %#" B_PRIxADDR "\n",
(phys_addr_t)config->frame_buffer_dma);
TRACE(" config->bytes_per_row: %" B_PRIu32 "\n", config->bytes_per_row);
return B_OK;
}
status_t
radeon_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
{
TRACE("%s\n", __func__);
if (_low != NULL) {
uint32 totalClocks = (uint32)mode->timing.h_total
* (uint32)mode->timing.v_total;
uint32 low = (totalClocks * 48L) / 1000L;
if (low < PLL_MIN_DEFAULT)
low = PLL_MIN_DEFAULT;
else if (low > PLL_MAX_DEFAULT)
return B_ERROR;
*_low = low;
}
if (_high != NULL)
*_high = PLL_MAX_DEFAULT;
return B_OK;
}
bool
is_mode_supported(display_mode* mode)
{
bool sane = true;
if (is_mode_sane(mode) != B_OK)
sane = false;
uint32 crtid = 0;
if (gInfo->shared_info->has_edid
&& gDisplay[crtid]->foundRanges) {
uint32 hfreq = mode->timing.pixel_clock / mode->timing.h_total;
if (hfreq > gDisplay[crtid]->hfreqMax + 1
|| hfreq < gDisplay[crtid]->hfreqMin - 1) {
sane = false;
}
uint32 vfreq = mode->timing.pixel_clock / ((mode->timing.v_total
* mode->timing.h_total) / 1000);
if (vfreq > gDisplay[crtid]->vfreqMax + 1
|| vfreq < gDisplay[crtid]->vfreqMin - 1) {
sane = false;
}
}
#if 0
TRACE("MODE: %d ; %d %d %d %d ; %d %d %d %d is %s\n",
mode->timing.pixel_clock, mode->timing.h_display,
mode->timing.h_sync_start, mode->timing.h_sync_end,
mode->timing.h_total, mode->timing.v_display,
mode->timing.v_sync_start, mode->timing.v_sync_end,
mode->timing.v_total,
sane ? "OK." : "BAD, out of range!");
#endif
return sane;
}
* A quick sanity check of the provided display_mode
*/
status_t
is_mode_sane(display_mode* mode)
{
if (mode->timing.h_sync_start > mode->timing.h_sync_end) {
TRACE("%s: ERROR: (%dx%d) "
"received h_sync_start greater then h_sync_end!\n",
__func__, mode->timing.h_display, mode->timing.v_display);
return B_ERROR;
}
if (mode->timing.h_total < mode->timing.h_display) {
TRACE("%s: ERROR: (%dx%d) "
"received h_total greater then h_display!\n",
__func__, mode->timing.h_display, mode->timing.v_display);
return B_ERROR;
}
if (mode->timing.v_sync_start > mode->timing.v_sync_end) {
TRACE("%s: ERROR: (%dx%d) "
"received v_sync_start greater then v_sync_end!\n",
__func__, mode->timing.h_display, mode->timing.v_display);
return B_ERROR;
}
if (mode->timing.v_total < mode->timing.v_display) {
TRACE("%s: ERROR: (%dx%d) "
"received v_total greater then v_display!\n",
__func__, mode->timing.h_display, mode->timing.v_display);
return B_ERROR;
}
int refresh = mode->timing.pixel_clock * 1000
/ (mode->timing.h_total * mode->timing.v_total);
if (refresh < 30 || refresh > 250) {
TRACE("%s: ERROR: (%dx%d) "
"refresh rate of %dHz is unlikely for any kind of monitor!\n",
__func__, mode->timing.h_display, mode->timing.v_display, refresh);
return B_ERROR;
}
return B_OK;
}
uint32
get_mode_bpp(display_mode* mode)
{
switch (mode->space) {
case B_CMAP8:
return 8;
case B_RGB15_LITTLE:
return 15;
case B_RGB16_LITTLE:
return 16;
case B_RGB24_LITTLE:
case B_RGB32_LITTLE:
return 32;
}
ERROR("%s: Unknown colorspace for mode, guessing 32 bits per pixel\n",
__func__);
return 32;
}
static uint32_t
radeon_get_backlight_register()
{
if (gInfo->shared_info->chipsetID >= RADEON_R600)
return 0x172c;
return 0x0018;
}
status_t
radeon_set_brightness(float brightness)
{
TRACE("%s (%f)\n", __func__, brightness);
if (brightness < 0 || brightness > 1)
return B_BAD_VALUE;
uint32_t backlightReg = radeon_get_backlight_register();
uint8_t brightnessRaw = (uint8_t)ceilf(brightness * 255);
uint32_t level = Read32(OUT, backlightReg);
TRACE("brightness level = %lx\n", level);
level &= ~ATOM_S2_CURRENT_BL_LEVEL_MASK;
level |= (( brightnessRaw << ATOM_S2_CURRENT_BL_LEVEL_SHIFT )
& ATOM_S2_CURRENT_BL_LEVEL_MASK);
TRACE("new brightness level = %lx\n", level);
Write32(OUT, backlightReg, level);
uint8 crtcID = 0;
uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
connector_info* connector = gConnector[connectorIndex];
pll_info* pll = &connector->encoder.pll;
transmitter_dig_setup(connectorIndex, pll->pixelClock,
0, 0, ATOM_TRANSMITTER_ACTION_BL_BRIGHTNESS_CONTROL);
transmitter_dig_setup(connectorIndex, pll->pixelClock,
0, 0, ATOM_TRANSMITTER_ACTION_LCD_BLON);
return B_OK;
}
status_t
radeon_get_brightness(float* brightness)
{
TRACE("%s\n", __func__);
if (brightness == NULL)
return B_BAD_VALUE;
uint32_t backlightReg = Read32(OUT, radeon_get_backlight_register());
uint8_t brightnessRaw = ((backlightReg & ATOM_S2_CURRENT_BL_LEVEL_MASK) >>
ATOM_S2_CURRENT_BL_LEVEL_SHIFT);
*brightness = (float)brightnessRaw / 255;
return B_OK;
}