* Add LVDS handling for TMDSB

* Fix crash situation if no monitors detected
* Assume TMDSB if no monitors sensed (temporary)


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42508 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Alexander von Gluck IV
2011-07-29 03:32:01 +00:00
parent 87cec635c0
commit e1b9d6e674
7 changed files with 337 additions and 20 deletions
@@ -17,6 +17,7 @@ Addon radeon_hd.accelerant :
dac.cpp
display.cpp
tmds.cpp
lvds.cpp
mode.cpp
bios.cpp
create_display_modes.cpp
@@ -209,16 +209,16 @@ radeon_init_accelerant(int device)
init_lock(&info.engine_lock, "radeon hd engine");
status = detect_displays();
if (status != B_OK)
return status;
//if (status != B_OK)
// return status;
debug_displays();
status = create_mode_list();
if (status != B_OK) {
uninit_common();
return status;
}
//if (status != B_OK) {
// radeon_uninit_accelerant();
// return status;
//}
TRACE("%s done\n", __func__);
return B_OK;
@@ -15,6 +15,7 @@
#include "pll.h"
#include "dac.h"
#include "tmds.h"
#include "lvds.h"
#include <edid.h>
@@ -89,6 +90,7 @@ typedef struct {
uint32 connection_type;
uint8 connection_id;
register_info *regs;
bool found_ranges;
uint32 vfreq_max;
uint32 vfreq_min;
uint32 hfreq_max;
@@ -99,7 +101,7 @@ typedef struct {
// display_info connection_type
#define CONNECTION_DAC 0x0001
#define CONNECTION_TMDS 0x0002
#define CONNECTION_LVDS 0x0003
#define CONNECTION_LVDS 0x0004
// register MMIO modes
#define OUT 0x1 // direct MMIO calls
+18 -3
View File
@@ -216,8 +216,10 @@ status_t
detect_displays()
{
// reset known displays
for (uint32 id = 0; id < MAX_DISPLAY; id++)
for (uint32 id = 0; id < MAX_DISPLAY; id++) {
gDisplay[id]->active = false;
gDisplay[id]->found_ranges = false;
}
uint32 index = 0;
@@ -228,7 +230,9 @@ detect_displays()
gDisplay[index]->connection_type = CONNECTION_DAC;
gDisplay[index]->connection_id = id;
init_registers(gDisplay[index]->regs, index);
detect_crt_ranges(index);
if (detect_crt_ranges(index) == B_OK)
gDisplay[index]->found_ranges = true;
if (index < MAX_DISPLAY)
index++;
else
@@ -243,7 +247,9 @@ detect_displays()
gDisplay[index]->connection_type = CONNECTION_TMDS;
gDisplay[index]->connection_id = id;
init_registers(gDisplay[index]->regs, index);
detect_crt_ranges(index);
if (detect_crt_ranges(index) == B_OK)
gDisplay[index]->found_ranges = true;
if (index < MAX_DISPLAY)
index++;
else
@@ -251,6 +257,15 @@ detect_displays()
}
}
// No monitors? Lets assume LVDS for now
if (index == 0) {
gDisplay[index]->active = true;
gDisplay[index]->connection_type = CONNECTION_LVDS;
gDisplay[index]->connection_id = 1;
// 0 : LVDSA ; 1 : LVDSB / TDMSB
init_registers(gDisplay[index]->regs, index);
}
return B_OK;
}
+260
View File
@@ -0,0 +1,260 @@
/*
* Copyright 2006-2011, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Alexander von Gluck, [email protected]
*/
#include "accelerant_protos.h"
#include "accelerant.h"
#include "utility.h"
#include "lvds.h"
#define TRACE_LVDS
#ifdef TRACE_LVDS
extern "C" void _sPrintf(const char *format, ...);
# define TRACE(x...) _sPrintf("radeon_hd: " x)
#else
# define TRACE(x...) ;
#endif
// Static microvoltage values taken from Xorg driver
static struct R5xxTMDSBMacro {
uint16 device;
uint32 macroSingle;
uint32 macroDual;
} R5xxTMDSBMacro[] = {
/*
* this list isn't complete yet.
* Some more values for dual need to be dug up
*/
{ 0x7104, 0x00F20616, 0x00F20616 }, // R520
{ 0x7142, 0x00F2061C, 0x00F2061C }, // RV515
{ 0x7145, 0x00F1061D, 0x00F2061D },
{ 0x7146, 0x00F1061D, 0x00F1061D }, // RV515
{ 0x7147, 0x0082041D, 0x0082041D }, // RV505
{ 0x7149, 0x00F1061D, 0x00D2061D },
{ 0x7152, 0x00F2061C, 0x00F2061C }, // RV515
{ 0x7183, 0x00B2050C, 0x00B2050C }, // RV530
{ 0x71C0, 0x00F1061F, 0x00f2061D },
{ 0x71C1, 0x0062041D, 0x0062041D }, // RV535
{ 0x71C2, 0x00F1061D, 0x00F2061D }, // RV530
{ 0x71C5, 0x00D1061D, 0x00D2061D },
{ 0x71C6, 0x00F2061D, 0x00F2061D }, // RV530
{ 0x71D2, 0x00F10610, 0x00F20610 }, // RV530: atombios uses 0x00F1061D
{ 0x7249, 0x00F1061D, 0x00F1061D }, // R580
{ 0x724B, 0x00F10610, 0x00F10610 }, // R580: atombios uses 0x00F1061D
{ 0x7280, 0x0042041F, 0x0042041F }, // RV570
{ 0x7288, 0x0042041F, 0x0042041F }, // RV570
{ 0x791E, 0x0001642F, 0x0001642F }, // RS690
{ 0x791F, 0x0001642F, 0x0001642F }, // RS690
{ 0x9400, 0x00020213, 0x00020213 }, // R600
{ 0x9401, 0x00020213, 0x00020213 }, // R600
{ 0x9402, 0x00020213, 0x00020213 }, // R600
{ 0x9403, 0x00020213, 0x00020213 }, // R600
{ 0x9405, 0x00020213, 0x00020213 }, // R600
{ 0x940A, 0x00020213, 0x00020213 }, // R600
{ 0x940B, 0x00020213, 0x00020213 }, // R600
{ 0x940F, 0x00020213, 0x00020213 }, // R600
{ 0, 0, 0 } /* End marker */
};
static struct RV6xxTMDSBMacro {
uint16 device;
uint32 macro;
uint32 tx;
uint32 preEmphasis;
} RV6xxTMDSBMacro[] = {
{ 0x94C1, 0x01030311, 0x10001A00, 0x01801015}, /* RV610 */
{ 0x94C3, 0x01030311, 0x10001A00, 0x01801015}, /* RV610 */
{ 0x9501, 0x0533041A, 0x020010A0, 0x41002045}, /* RV670 */
{ 0x9505, 0x0533041A, 0x020010A0, 0x41002045}, /* RV670 */
{ 0x950F, 0x0533041A, 0x020010A0, 0x41002045}, /* R680 */
{ 0x9587, 0x01030311, 0x10001C00, 0x01C01011}, /* RV630 */
{ 0x9588, 0x01030311, 0x10001C00, 0x01C01011}, /* RV630 */
{ 0x9589, 0x01030311, 0x10001C00, 0x01C01011}, /* RV630 */
{ 0, 0, 0, 0} /* End marker */
};
void
LVDSVoltageControl(uint8 lvdsIndex)
{
bool dualLink = false; // TODO : DualLink
radeon_shared_info &info = *gInfo->shared_info;
// TODO : Special RS690 RS600 IGP oneoffs
if (info.device_chipset < (RADEON_R600 | 0x70))
Write32Mask(OUT, LVTMA_REG_TEST_OUTPUT, 0x00100000, 0x00100000);
// Micromanage voltages
if (info.device_chipset < (RADEON_R600 | 0x10)) {
for (uint32 i = 0; R5xxTMDSBMacro[i].device; i++) {
if (R5xxTMDSBMacro[i].device == info.device_id) {
if (dualLink) {
Write32(OUT, LVTMA_MACRO_CONTROL,
R5xxTMDSBMacro[i].macroDual);
} else {
Write32(OUT, LVTMA_MACRO_CONTROL,
R5xxTMDSBMacro[i].macroSingle);
}
return;
}
}
TRACE("%s : unhandled chipset 0x%X\n", __func__, info.device_id);
} else {
for (uint32 i = 0; RV6xxTMDSBMacro[i].device; i++) {
if (RV6xxTMDSBMacro[i].device == info.device_id) {
Write32(OUT, LVTMA_MACRO_CONTROL, RV6xxTMDSBMacro[i].macro);
Write32(OUT, LVTMA_TRANSMITTER_ADJUST,
RV6xxTMDSBMacro[i].tx);
Write32(OUT, LVTMA_PREEMPHASIS_CONTROL,
RV6xxTMDSBMacro[i].preEmphasis);
return;
}
}
TRACE("%s : unhandled chipset 0x%X\n", __func__, info.device_id);
}
}
void
LVDSPower(uint8 lvdsIndex, int command)
{
bool dualLink = false; // TODO : dualLink
if (lvdsIndex == 0) {
TRACE("LVTMA not yet supported :(\n");
return;
} else {
// Select TMDSB (which is on LVDS)
Write32Mask(OUT, LVTMA_MODE, 0x00000001, 0x00000001);
}
switch (command) {
case RHD_POWER_ON:
TRACE("%s: LVDS %d Power On\n", __func__, lvdsIndex);
Write32Mask(OUT, LVTMA_CNTL, 0x1, 0x00000001);
if (dualLink) {
Write32Mask(OUT, LVTMA_TRANSMITTER_ENABLE,
0x00003E3E, 0x00003E3E);
} else {
Write32Mask(OUT, LVTMA_TRANSMITTER_ENABLE,
0x0000003E, 0x00003E3E);
}
Write32Mask(OUT, LVTMA_TRANSMITTER_CONTROL, 0x00000001, 0x00000001);
snooze(2);
Write32Mask(OUT, LVTMA_TRANSMITTER_CONTROL, 0, 0x00000002);
// TODO : Enable HDMI
return;
case RHD_POWER_RESET:
TRACE("%s: LVDS %d Power Reset\n", __func__, lvdsIndex);
Write32Mask(OUT, LVTMA_TRANSMITTER_ENABLE, 0, 0x00003E3E);
return;
case RHD_POWER_SHUTDOWN:
default:
TRACE("%s: LVDS %d Power Shutdown\n", __func__, lvdsIndex);
Write32Mask(OUT, LVTMA_TRANSMITTER_CONTROL, 0x00000002, 0x00000002);
snooze(2);
Write32Mask(OUT, LVTMA_TRANSMITTER_CONTROL, 0, 0x00000001);
Write32Mask(OUT, LVTMA_TRANSMITTER_ENABLE, 0, 0x00003E3E);
Write32Mask(OUT, LVTMA_CNTL, 0, 0x00000001);
// TODO : Disable HDMI
return;
}
}
status_t
LVDSSet(uint8 lvdsIndex, display_mode *mode)
{
TRACE("%s: LVDS %d Set\n", __func__, lvdsIndex);
uint16 crtid = 0; // TODO : assume CRT0
if (lvdsIndex == 0) {
TRACE("LVTMA not yet supported :(\n");
return B_ERROR;
} else {
// Select TMDSB (which is on LVDS)
Write32Mask(OUT, LVTMA_MODE, 0x00000001, 0x00000001);
}
// Clear HPD events
Write32Mask(OUT, LVTMA_TRANSMITTER_CONTROL, 0, 0x0000000C);
Write32Mask(OUT, LVTMA_TRANSMITTER_ENABLE, 0, 0x00070000);
Write32Mask(OUT, LVTMA_CNTL, 0, 0x00000010);
// Disable LVDS (TMDSB) transmitter
Write32Mask(OUT, LVTMA_TRANSMITTER_ENABLE, 0, 0x00003E3E);
// Reset dither bits
Write32Mask(OUT, LVTMA_BIT_DEPTH_CONTROL, 0, 0x00010101);
Write32Mask(OUT, LVTMA_BIT_DEPTH_CONTROL, LVTMA_DITHER_RESET_BIT,
LVTMA_DITHER_RESET_BIT);
snooze(2);
Write32Mask(OUT, LVTMA_BIT_DEPTH_CONTROL, 0, LVTMA_DITHER_RESET_BIT);
Write32Mask(OUT, LVTMA_BIT_DEPTH_CONTROL, 0, 0xF0000000);
// Undocumented depth control bit from Xorg
Write32Mask(OUT, LVTMA_CNTL, 0x00001000, 0x00011000);
// Reset phase for vsync and use RGB color
Write32Mask(OUT, LVTMA_SOURCE_SELECT, crtid, 0x00010101);
// Assign to CRTC
Write32(OUT, LVTMA_COLOR_FORMAT, 0);
// TODO : Detect DualLink via SynthClock?
Write32Mask(OUT, LVTMA_CNTL, 0, 0x01000000);
// TODO : only > R600 - disable split mode
Write32Mask(OUT, LVTMA_CNTL, 0, 0x20000000);
Write32Mask(OUT, LVTMA_FORCE_OUTPUT_CNTL, 0, 0x00000001);
// Disable force data
Write32Mask(OUT, LVTMA_DCBALANCER_CONTROL, 0x00000001, 0x00000001);
// Enable DC balancer
LVDSVoltageControl(lvdsIndex);
Write32Mask(OUT, LVTMA_TRANSMITTER_CONTROL, 0x00000010, 0x00000010);
// use IDCLK
Write32Mask(OUT, LVTMA_TRANSMITTER_CONTROL, 0x20000000, 0x20000000);
// use clock selected by next write
// TODO : coherent mode?
Write32Mask(OUT, LVTMA_TRANSMITTER_CONTROL, 0, 0x10000000);
Write32Mask(OUT, LVTMA_TRANSMITTER_CONTROL, 0, 0x03FF0000);
// Clear current LVDS clock
// Reset PLL's
Write32Mask(OUT, LVTMA_TRANSMITTER_CONTROL, 0x00000002, 0x00000002);
snooze(2);
Write32Mask(OUT, LVTMA_TRANSMITTER_CONTROL, 0, 0x00000002);
snooze(20);
// Restart LVDS data sync
Write32Mask(OUT, LVTMA_DATA_SYNCHRONIZATION, 0x00000001, 0x00000001);
Write32Mask(OUT, LVTMA_DATA_SYNCHRONIZATION, 0x00000100, 0x00000100);
snooze(20);
Write32Mask(OUT, LVTMA_DATA_SYNCHRONIZATION, 0, 0x00000001);
// TODO : Set HDMI mode
return B_OK;
}
+33
View File
@@ -0,0 +1,33 @@
/*
* Copyright 2006-2011, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Alexander von Gluck, kallisti5@unixzen.com
*/
#ifndef RADEON_HD_LVDS_H
#define RADEON_HD_LVDS_H
#define LVTMA_DATA_SYNCHRONIZATION LVTMA_R600_DATA_SYNCHRONIZATION
#define LVTMA_PWRSEQ_REF_DIV LVTMA_R600_PWRSEQ_REF_DIV
#define LVTMA_PWRSEQ_DELAY1 LVTMA_R600_PWRSEQ_DELAY1
#define LVTMA_PWRSEQ_DELAY2 LVTMA_R600_PWRSEQ_DELAY2
#define LVTMA_PWRSEQ_CNTL LVTMA_R600_PWRSEQ_CNTL
#define LVTMA_PWRSEQ_STATE LVTMA_R600_PWRSEQ_STATE
#define LVTMA_LVDS_DATA_CNTL LVTMA_R600_LVDS_DATA_CNTL
#define LVTMA_MODE LVTMA_R600_MODE
#define LVTMA_TRANSMITTER_ENABLE LVTMA_R600_TRANSMITTER_ENABLE
#define LVTMA_MACRO_CONTROL LVTMA_R600_MACRO_CONTROL
#define LVTMA_TRANSMITTER_CONTROL LVTMA_R600_TRANSMITTER_CONTROL
#define LVTMA_REG_TEST_OUTPUT LVTMA_R600_REG_TEST_OUTPUT
#define LVTMA_BL_MOD_CNTL LVTMA_R600_BL_MOD_CNTL
#define LVTMA_DITHER_RESET_BIT 0x02000000
void LVDSVoltageControl(uint8 lvdsIndex);
void LVDSPower(uint8 lvdsIndex, int command);
status_t LVDSSet(uint8 lvdsIndex, display_mode *mode);
#endif /* RADEON_HD_LVDS_H */
+16 -10
View File
@@ -149,7 +149,6 @@ CardFBSet(uint8 crtid, display_mode *mode)
get_color_space_format(*mode, colorMode, bytesPerRow, bitsPerPixel);
#if 0
// TMDSAllIdle // DVI / HDMI
// LVTMAAllIdle // DVI
@@ -249,9 +248,10 @@ CardModeSet(uint8 crtid, display_mode *mode)
displayTiming.h_total - 1);
// Blanking
uint16 blankStart = displayTiming.h_total
+ displayTiming.h_display - displayTiming.h_sync_start;
uint16 blankEnd = displayTiming.h_total - displayTiming.h_sync_start;
uint16 blankStart = MIN(displayTiming.h_sync_start,
displayTiming.h_display);
uint16 blankEnd = MAX(displayTiming.h_sync_end,
displayTiming.h_total);
Write32(CRT, regs->crtHBlank,
blankStart | (blankEnd << 16));
@@ -267,9 +267,10 @@ CardModeSet(uint8 crtid, display_mode *mode)
Write32(CRT, regs->crtVTotal,
displayTiming.v_total - 1);
blankStart = displayTiming.v_total
+ displayTiming.v_display - displayTiming.v_sync_start;
blankEnd = displayTiming.v_total - displayTiming.v_sync_start;
blankStart = MIN(displayTiming.v_sync_start,
displayTiming.v_display);
blankEnd = MAX(displayTiming.v_sync_end,
displayTiming.v_total);
Write32(CRT, regs->crtVBlank,
blankStart | (blankEnd << 16));
@@ -345,6 +346,9 @@ radeon_set_display_mode(display_mode *mode)
} else if ((gDisplay[display_id]->connection_type & CONNECTION_TMDS) != 0) {
TMDSSet(gDisplay[display_id]->connection_id, mode);
TMDSPower(gDisplay[display_id]->connection_id, RHD_POWER_ON);
} else if ((gDisplay[display_id]->connection_type & CONNECTION_LVDS) != 0) {
LVDSSet(gDisplay[display_id]->connection_id, mode);
LVDSPower(gDisplay[display_id]->connection_id, RHD_POWER_ON);
}
// Ensure screen isn't blanked
@@ -429,7 +433,8 @@ is_mode_supported(display_mode *mode)
uint32 crtid = 0;
// if we have edid info, check frequency adginst crt reported valid ranges
if (gInfo->shared_info->has_edid) {
if (gInfo->shared_info->has_edid
&& gDisplay[crtid]->found_ranges) {
uint32 hfreq = mode->timing.pixel_clock / mode->timing.h_total;
if (hfreq > gDisplay[crtid]->hfreq_max + 1
@@ -453,10 +458,11 @@ is_mode_supported(display_mode *mode)
mode->timing.h_display, mode->timing.v_display, crtid);
return false;
}
TRACE("%dx%d is within CRT %d's valid frequency range\n",
mode->timing.h_display, mode->timing.v_display, crtid);
}
TRACE("%dx%d is within CRT %d's valid frequency range\n",
mode->timing.h_display, mode->timing.v_display, crtid);
return true;
}