#include <stdlib.h>
#include <string.h>
#include "radeon_accelerant.h"
#include "mmio.h"
#include "crtc_regs.h"
#include "dac_regs.h"
#include "pll_regs.h"
#include "tv_out_regs.h"
#include "config_regs.h"
#include "ddc_regs.h"
#include "gpiopad_regs.h"
#include "fp_regs.h"
#include "pll_access.h"
#include "theatre_regs.h"
#include "set_mode.h"
#include "ddc.h"
typedef struct {
accelerator_info *ai;
uint32 port;
} ddc_port_info;
static status_t
get_signals(void *cookie, int *clk, int *data)
{
ddc_port_info *info = (ddc_port_info *)cookie;
vuint8 *regs = info->ai->regs;
uint32 value;
value = INREG(regs, info->port);
*clk = (value >> RADEON_GPIO_Y_SHIFT_1) & 1;
*data = (value >> RADEON_GPIO_Y_SHIFT_0) & 1;
return B_OK;
}
static status_t
set_signals(void *cookie, int clk, int data)
{
ddc_port_info *info = (ddc_port_info *)cookie;
vuint8 *regs = info->ai->regs;
uint32 value;
value = INREG(regs, info->port);
value &= ~(RADEON_GPIO_A_1 | RADEON_GPIO_A_0);
value &= ~(RADEON_GPIO_EN_0 | RADEON_GPIO_EN_1);
value |= ((1-clk) << RADEON_GPIO_EN_SHIFT_1)
| ((1-data) << RADEON_GPIO_EN_SHIFT_0);
OUTREG(regs, info->port, value);
return B_OK;
}
bool
Radeon_ReadEDID(accelerator_info *ai, uint32 ddcPort, edid1_info *edid)
{
i2c_bus bus;
ddc_port_info info;
void *vdif;
size_t vdifLength;
info.ai = ai;
info.port = ddcPort;
ddc2_init_timing(&bus);
bus.cookie = &info;
bus.set_signals = &set_signals;
bus.get_signals = &get_signals;
if (ddc2_read_edid1(&bus, edid, &vdif, &vdifLength) != B_OK)
return false;
SHOW_FLOW(2, "Found DDC-capable monitor @0x%04x", ddcPort);
free(vdif);
return true;
}
static bool
Radeon_DetectCRTInt(accelerator_info *ai, bool colour)
{
vuint8 *regs = ai->regs;
uint32 old_crtc_ext_cntl, old_dac_ext_cntl, old_dac_cntl, value;
bool found;
old_crtc_ext_cntl = INREG(regs, RADEON_CRTC_EXT_CNTL);
value = old_crtc_ext_cntl | RADEON_CRTC_CRT_ON;
OUTREG(regs, RADEON_CRTC_EXT_CNTL, value);
old_dac_ext_cntl = INREG(regs, RADEON_DAC_EXT_CNTL);
value = RADEON_DAC_FORCE_BLANK_OFF_EN | RADEON_DAC_FORCE_DATA_EN
| (colour ? RADEON_DAC_FORCE_DATA_SEL_RGB : RADEON_DAC_FORCE_DATA_SEL_G)
| (0x1b6 << RADEON_DAC_FORCE_DATA_SHIFT);
OUTREG(regs, RADEON_DAC_EXT_CNTL, value);
old_dac_cntl = INREG(regs, RADEON_DAC_CNTL);
value = old_dac_cntl & ~(RADEON_DAC_RANGE_CNTL_MASK | RADEON_DAC_PDWN);
value |= RADEON_DAC_RANGE_CNTL_PS2 | RADEON_DAC_CMP_EN;
OUTREG(regs, RADEON_DAC_CNTL, value);
snooze(2000);
found = (INREG(regs, RADEON_DAC_CNTL) & RADEON_DAC_CMP_OUTPUT) != 0;
if (found) {
SHOW_INFO(2, "Found %s CRT connected to CRT-DAC",
colour ? "colour" : "b/w");
}
OUTREG(regs, RADEON_DAC_CNTL, old_dac_cntl);
OUTREG(regs, RADEON_DAC_EXT_CNTL, old_dac_ext_cntl);
OUTREG(regs, RADEON_CRTC_EXT_CNTL, old_crtc_ext_cntl);
return found;
}
static bool
Radeon_DetectCRT(accelerator_info *ai)
{
vuint32 old_vclk_ecp_cntl, value;
bool found;
old_vclk_ecp_cntl = Radeon_INPLL(ai->regs, ai->si->asic,
RADEON_VCLK_ECP_CNTL);
value = old_vclk_ecp_cntl
& ~(RADEON_PIXCLK_ALWAYS_ONb | RADEON_PIXCLK_DAC_ALWAYS_ONb);
Radeon_OUTPLL(ai->regs, ai->si->asic, RADEON_VCLK_ECP_CNTL, value);
found = Radeon_DetectCRTInt(ai, true) || Radeon_DetectCRTInt(ai, false);
Radeon_OUTPLL(ai->regs, ai->si->asic, RADEON_VCLK_ECP_CNTL,
old_vclk_ecp_cntl);
return found;
}
static bool
Radeon_DetectTVCRT_RV200(accelerator_info *ai)
{
vuint8 *regs = ai->regs;
uint32 old_crtc2_gen_cntl, old_tv_dac_cntl, old_dac_cntl2, value;
bool found;
old_crtc2_gen_cntl = INREG(regs, RADEON_CRTC2_GEN_CNTL);
value = old_crtc2_gen_cntl & ~RADEON_CRTC2_PIX_WIDTH_MASK;
value |= RADEON_CRTC2_CRT2_ON | (2 << RADEON_CRTC2_PIX_WIDTH_SHIFT);
OUTREG(regs, RADEON_CRTC2_GEN_CNTL, value);
old_tv_dac_cntl = INREG(regs, RADEON_TV_DAC_CNTL);
value = RADEON_TV_DAC_CNTL_NBLANK | RADEON_TV_DAC_CNTL_NHOLD
| RADEON_TV_DAC_CNTL_DETECT | RADEON_TV_DAC_CNTL_STD_PS2;
OUTREG(regs, RADEON_TV_DAC_CNTL, value);
value = RADEON_DAC2_FORCE_BLANK_OFF_EN | RADEON_DAC2_FORCE_DATA_EN
| RADEON_DAC_FORCE_DATA_SEL_RGB
| (0x180 << RADEON_DAC_FORCE_DATA_SHIFT);
OUTREG(regs, RADEON_DAC_EXT_CNTL, value);
old_dac_cntl2 = INREG(regs, RADEON_DAC_CNTL2);
value = old_dac_cntl2 | RADEON_DAC2_CLK_SEL_CRT | RADEON_DAC2_CMP_EN;
OUTREG(regs, RADEON_DAC_CNTL2, value);
snooze(10000);
found = (INREG(regs, RADEON_DAC_CNTL2) & RADEON_DAC2_CMP_OUTPUT) != 0;
if (found)
SHOW_INFO0(2, "Found CRT connected to TV-DAC, i.e. DVI port");
OUTREG(regs, RADEON_DAC_CNTL2, old_dac_cntl2);
OUTREG(regs, RADEON_DAC_EXT_CNTL, 0);
OUTREG(regs, RADEON_TV_DAC_CNTL, old_tv_dac_cntl);
OUTREG(regs, RADEON_CRTC2_GEN_CNTL, old_crtc2_gen_cntl);
return found;
}
static bool
Radeon_DetectTVCRT_R300(accelerator_info *ai)
{
vuint8 *regs = ai->regs;
uint32 old_crtc2_gen_cntl, old_tv_dac_cntl, old_dac_cntl2, value;
uint32 old_radeon_gpiopad_a;
bool found;
old_radeon_gpiopad_a = INREG(regs, RADEON_GPIOPAD_A);
OUTREGP(regs, RADEON_GPIOPAD_EN, 1, ~1);
OUTREGP(regs, RADEON_GPIOPAD_MASK, 1, ~1);
OUTREGP(regs, RADEON_GPIOPAD_A, 1, ~1);
old_crtc2_gen_cntl = INREG(regs, RADEON_CRTC2_GEN_CNTL);
value = old_crtc2_gen_cntl;
value &= ~RADEON_CRTC2_PIX_WIDTH_MASK;
value |= (2 << RADEON_CRTC2_PIX_WIDTH_SHIFT) | RADEON_CRTC2_CRT2_ON
| RADEON_CRTC2_VSYNC_TRISTAT;
OUTREG(regs, RADEON_CRTC2_GEN_CNTL, value);
old_tv_dac_cntl = INREG(regs, RADEON_TV_DAC_CNTL);
OUTREG(regs, RADEON_TV_DAC_CNTL, RADEON_TV_DAC_CNTL_NBLANK
| RADEON_TV_DAC_CNTL_NHOLD | RADEON_TV_DAC_CNTL_DETECT
| RADEON_TV_DAC_CNTL_STD_PS2);
OUTREG(regs, RADEON_DAC_EXT_CNTL, RADEON_DAC2_FORCE_BLANK_OFF_EN
| RADEON_DAC2_FORCE_DATA_EN | RADEON_DAC_FORCE_DATA_SEL_RGB
| (0x1b6 << RADEON_DAC_FORCE_DATA_SHIFT));
old_dac_cntl2 = INREG(regs, RADEON_DAC_CNTL2);
OUTREG(regs, RADEON_DAC_CNTL2, old_dac_cntl2 | RADEON_DAC2_CLK_SEL_CRT
| RADEON_DAC2_CMP_EN);
snooze(10000);
found = (INREG(regs, RADEON_DAC_CNTL2) & RADEON_DAC2_CMP_OUT_B) != 0;
OUTREG(regs, RADEON_DAC_CNTL2, old_dac_cntl2);
OUTREG(regs, RADEON_DAC_EXT_CNTL, 0);
OUTREG(regs, RADEON_TV_DAC_CNTL, old_tv_dac_cntl);
OUTREG(regs, RADEON_CRTC2_GEN_CNTL, old_crtc2_gen_cntl);
OUTREGP(regs, RADEON_GPIOPAD_A, old_radeon_gpiopad_a, ~1);
return found;
}
static bool
Radeon_DetectTVCRT(accelerator_info *ai)
{
if (ai->si->is_mobility)
return dd_none;
switch (ai->si->asic) {
case rt_r100:
return dd_none;
case rt_rv100:
case rt_rv200:
case rt_rv250:
case rt_rv280:
case rt_rs100:
case rt_rs200:
case rt_rs300:
return Radeon_DetectTVCRT_RV200(ai);
case rt_r300:
case rt_r350:
case rt_rv350:
case rt_rv380:
case rt_r420:
return Radeon_DetectTVCRT_R300(ai);
case rt_r200:
return dd_none;
}
return dd_none;
}
static display_device_e
Radeon_DetectTV_RV200(accelerator_info *ai, bool tv_crt_found)
{
vuint8 *regs = ai->regs;
uint32 value, old_dac_cntl2, old_crtc_ext_cntl, old_crtc2_gen_cntl;
uint32 old_tv_master_cntl, old_tv_dac_cntl, old_pre_dac_mux_cntl;
uint32 config_cntl;
display_device_e displays = dd_none;
if (tv_crt_found)
return dd_none;
old_dac_cntl2 = INREG(regs, RADEON_DAC_CNTL2);
value = old_dac_cntl2 & ~RADEON_DAC2_CLK_SEL_CRT;
OUTREG(regs, RADEON_DAC_CNTL2, value);
old_crtc_ext_cntl = INREG(regs, RADEON_CRTC_EXT_CNTL);
old_crtc2_gen_cntl = INREG(regs, RADEON_CRTC2_GEN_CNTL);
old_tv_master_cntl = INREG(regs, RADEON_TV_MASTER_CNTL);
value = old_tv_master_cntl | RADEON_TV_MASTER_CNTL_TV_ON;
value &= ~(
RADEON_TV_MASTER_CNTL_TV_ASYNC_RST |
RADEON_TV_MASTER_CNTL_RESTART_PHASE_FIX |
RADEON_TV_MASTER_CNTL_CRT_FIFO_CE_EN |
RADEON_TV_MASTER_CNTL_TV_FIFO_CE_EN |
RADEON_TV_MASTER_CNTL_RE_SYNC_NOW_SEL_MASK);
value |=
RADEON_TV_MASTER_CNTL_TV_FIFO_ASYNC_RST |
RADEON_TV_MASTER_CNTL_CRT_ASYNC_RST;
OUTREG(regs, RADEON_TV_MASTER_CNTL, value);
old_tv_dac_cntl = INREG(regs, RADEON_TV_DAC_CNTL);
config_cntl = INREG(regs, RADEON_CONFIG_CNTL);
value =
RADEON_TV_DAC_CNTL_NBLANK | RADEON_TV_DAC_CNTL_NHOLD |
RADEON_TV_DAC_CNTL_DETECT | RADEON_TV_DAC_CNTL_STD_NTSC |
(8 << RADEON_TV_DAC_CNTL_BGADJ_SHIFT) |
((((config_cntl & RADEON_CFG_ATI_REV_ID_MASK) == 0) ? 8 : 4) << RADEON_TV_DAC_CNTL_DACADJ_SHIFT);
OUTREG(regs, RADEON_TV_DAC_CNTL, value);
old_pre_dac_mux_cntl = INREG(regs, RADEON_TV_PRE_DAC_MUX_CNTL);
value =
RADEON_TV_PRE_DAC_MUX_CNTL_C_GRN_EN | RADEON_TV_PRE_DAC_MUX_CNTL_CMP_BLU_EN |
(RADEON_TV_MUX_FORCE_DAC_DATA << RADEON_TV_PRE_DAC_MUX_CNTL_RED_MX_SHIFT) |
(RADEON_TV_MUX_FORCE_DAC_DATA << RADEON_TV_PRE_DAC_MUX_CNTL_GRN_MX_SHIFT) |
(RADEON_TV_MUX_FORCE_DAC_DATA << RADEON_TV_PRE_DAC_MUX_CNTL_BLU_MX_SHIFT) |
(0x109 << RADEON_TV_PRE_DAC_MUX_CNTL_FORCE_DAC_DATA_SHIFT);
OUTREG(regs, RADEON_TV_PRE_DAC_MUX_CNTL, value);
snooze(3000);
value = INREG(regs, RADEON_TV_DAC_CNTL);
if ((value & RADEON_TV_DAC_CNTL_GDACDET) != 0) {
displays |= dd_stv;
SHOW_INFO0(2, "S-Video TV-Out is connected");
}
if ((value & RADEON_TV_DAC_CNTL_BDACDET) != 0) {
displays |= dd_ctv;
SHOW_INFO0(2, "Composite TV-Out is connected");
}
OUTREG(regs, RADEON_TV_PRE_DAC_MUX_CNTL, old_pre_dac_mux_cntl);
OUTREG(regs, RADEON_TV_DAC_CNTL, old_tv_dac_cntl);
OUTREG(regs, RADEON_TV_MASTER_CNTL, old_tv_master_cntl);
OUTREG(regs, RADEON_CRTC2_GEN_CNTL, old_crtc2_gen_cntl);
OUTREG(regs, RADEON_CRTC_EXT_CNTL, old_crtc_ext_cntl);
OUTREG(regs, RADEON_DAC_CNTL2, old_dac_cntl2);
return displays;
}
static display_device_e Radeon_DetectTV_R300(accelerator_info *ai)
{
vuint8 *regs = ai->regs;
display_device_e displays = dd_none;
uint32 tmp, old_dac_cntl2, old_crtc2_gen_cntl, old_dac_ext_cntl, old_tv_dac_cntl;
uint32 old_radeon_gpiopad_a;
old_radeon_gpiopad_a = INREG(regs, RADEON_GPIOPAD_A);
OUTREGP(regs, RADEON_GPIOPAD_EN, 1, ~1);
OUTREGP(regs, RADEON_GPIOPAD_MASK, 1, ~1);
OUTREGP(regs, RADEON_GPIOPAD_A, 0, ~1);
old_dac_cntl2 = INREG(regs, RADEON_DAC_CNTL2);
OUTREG(regs, RADEON_DAC_CNTL2, RADEON_DAC2_CLK_SEL_CRT);
old_crtc2_gen_cntl = INREG(regs, RADEON_CRTC2_GEN_CNTL);
OUTREG(regs, RADEON_CRTC2_GEN_CNTL,
RADEON_CRTC2_CRT2_ON | RADEON_CRTC2_VSYNC_TRISTAT);
old_dac_ext_cntl = INREG(regs, RADEON_DAC_EXT_CNTL);
OUTREG(regs, RADEON_DAC_EXT_CNTL,
RADEON_DAC2_FORCE_BLANK_OFF_EN | RADEON_DAC2_FORCE_DATA_EN |
RADEON_DAC_FORCE_DATA_SEL_RGB |
(0xec << RADEON_DAC_FORCE_DATA_SHIFT));
old_tv_dac_cntl = INREG(regs, RADEON_TV_DAC_CNTL);
OUTREG(regs, RADEON_TV_DAC_CNTL,
RADEON_TV_DAC_CNTL_STD_NTSC |
(8 << RADEON_TV_DAC_CNTL_BGADJ_SHIFT) |
(6 << RADEON_TV_DAC_CNTL_DACADJ_SHIFT));
(void)INREG(regs, RADEON_TV_DAC_CNTL);
snooze(4000);
OUTREG(regs, RADEON_TV_DAC_CNTL,
RADEON_TV_DAC_CNTL_NBLANK | RADEON_TV_DAC_CNTL_NHOLD |
RADEON_TV_DAC_CNTL_DETECT |
RADEON_TV_DAC_CNTL_STD_NTSC |
(8 << RADEON_TV_DAC_CNTL_BGADJ_SHIFT) |
(6 << RADEON_TV_DAC_CNTL_DACADJ_SHIFT));
(void)INREG(regs, RADEON_TV_DAC_CNTL);
snooze(6000);
tmp = INREG(regs, RADEON_TV_DAC_CNTL);
if ((tmp & RADEON_TV_DAC_CNTL_GDACDET) != 0) {
displays |= dd_stv;
SHOW_INFO0(2, "S-Video TV-Out is connected");
}
if ((tmp & RADEON_TV_DAC_CNTL_BDACDET) != 0) {
displays |= dd_ctv;
SHOW_INFO0(2, "Composite TV-Out is connected");
}
OUTREG(regs, RADEON_TV_DAC_CNTL, old_tv_dac_cntl);
OUTREG(regs, RADEON_DAC_EXT_CNTL, old_dac_ext_cntl);
OUTREG(regs, RADEON_CRTC2_GEN_CNTL, old_crtc2_gen_cntl);
OUTREG(regs, RADEON_DAC_CNTL2, old_dac_cntl2);
OUTREGP(regs, RADEON_GPIOPAD_A, old_radeon_gpiopad_a, ~1);
return displays;
}
static bool readTVDetect(accelerator_info *ai)
{
uint32 tmp;
int i;
bigtime_t start_time;
bool detect;
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL,
RADEON_TV_DAC_CNTL_STD_NTSC | RADEON_TV_DAC_CNTL_DETECT | RADEON_TV_DAC_CNTL_NBLANK);
Radeon_VIPRead(ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL, &tmp);
detect = (tmp & RADEON_TV_DAC_CNTL_CMPOUT) != 0;
start_time = system_time();
do {
for (i = 0; i < 5; ++i) {
bool cur_detect;
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL,
RADEON_TV_DAC_CNTL_STD_NTSC | RADEON_TV_DAC_CNTL_DETECT | RADEON_TV_DAC_CNTL_NBLANK |
RADEON_TV_DAC_CNTL_NHOLD);
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL,
RADEON_TV_DAC_CNTL_STD_NTSC | RADEON_TV_DAC_CNTL_DETECT | RADEON_TV_DAC_CNTL_NBLANK);
Radeon_VIPRead(ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL, &tmp);
cur_detect = (tmp & RADEON_TV_DAC_CNTL_CMPOUT) != 0;
if (cur_detect != detect)
break;
detect = cur_detect;
}
if (i == 5) {
return detect;
}
} while (system_time() - start_time < 1000000);
SHOW_FLOW0(2, "timeout");
return false;
}
static display_device_e
Radeon_DetectTV_Theatre(accelerator_info *ai)
{
uint32 old_tv_dac_cntl, old_pre_dac_mux_cntl, old_modulator_cntl1;
uint32 old_master_cntl;
uint32 uv_adr, old_last_fifo_entry, old_mid_fifo_entry, last_fifo_addr;
display_device_e displays = dd_none;
if (ai->si->tv_chip != tc_external_rt1)
return dd_none;
Radeon_VIPRead(ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL,
&old_tv_dac_cntl);
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL,
RADEON_TV_DAC_CNTL_STD_NTSC | RADEON_TV_DAC_CNTL_DETECT
| RADEON_TV_DAC_CNTL_NHOLD | RADEON_TV_DAC_CNTL_NBLANK);
Radeon_VIPRead(ai, ai->si->theatre_channel, THEATRE_VIP_PRE_DAC_MUX_CNTL,
&old_pre_dac_mux_cntl);
Radeon_VIPRead(ai, ai->si->theatre_channel, THEATRE_VIP_MODULATOR_CNTL1,
&old_modulator_cntl1);
Radeon_VIPRead(ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL,
&old_master_cntl);
Radeon_VIPRead(ai, ai->si->theatre_channel, THEATRE_VIP_UV_ADR, &uv_adr);
last_fifo_addr = (uv_adr & RADEON_TV_UV_ADR_MAX_UV_ADR_MASK) * 2 + 1;
old_last_fifo_entry = Radeon_TheatreReadFIFO(ai, last_fifo_addr);
old_mid_fifo_entry = Radeon_TheatreReadFIFO(ai, 0x18f);
Radeon_TheatreWriteFIFO(ai, last_fifo_addr, 0x20208);
Radeon_TheatreWriteFIFO(ai, 0x18f, 0x3ff2608);
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL,
RADEON_TV_MASTER_CNTL_TV_FIFO_ASYNC_RST
| RADEON_TV_MASTER_CNTL_TV_ASYNC_RST);
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_MODULATOR_CNTL1,
(0x2c << RADEON_TV_MODULATOR_CNTL1_SET_UP_LEVEL_SHIFT)
| (0x2c << RADEON_TV_MODULATOR_CNTL1_BLANK_LEVEL_SHIFT));
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL,
RADEON_TV_MASTER_CNTL_TV_ASYNC_RST);
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL, 0);
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_PRE_DAC_MUX_CNTL,
RADEON_TV_PRE_DAC_MUX_CNTL_CMP_BLU_EN
| RADEON_TV_PRE_DAC_MUX_CNTL_DAC_DITHER_EN
| (9 << RADEON_TV_PRE_DAC_MUX_CNTL_BLU_MX_SHIFT)
| (0xa8 << RADEON_TV_PRE_DAC_MUX_CNTL_FORCE_DAC_DATA_SHIFT));
if (readTVDetect(ai)) {
SHOW_FLOW0(2, "Composite-Out of Rage Theatre is connected");
displays |= dd_ctv;
}
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL,
RADEON_TV_DAC_CNTL_STD_NTSC | RADEON_TV_DAC_CNTL_DETECT
| RADEON_TV_DAC_CNTL_NBLANK | RADEON_TV_DAC_CNTL_NHOLD);
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_PRE_DAC_MUX_CNTL,
RADEON_TV_PRE_DAC_MUX_CNTL_Y_RED_EN
| RADEON_TV_PRE_DAC_MUX_CNTL_DAC_DITHER_EN
| (9 << RADEON_TV_PRE_DAC_MUX_CNTL_RED_MX_SHIFT)
| (0xa8 << RADEON_TV_PRE_DAC_MUX_CNTL_FORCE_DAC_DATA_SHIFT));
if (readTVDetect(ai)) {
SHOW_FLOW0(2, "S-Video-Out of Rage Theatre is connected");
displays |= dd_stv;
}
Radeon_TheatreWriteFIFO(ai, last_fifo_addr, old_last_fifo_entry);
Radeon_TheatreWriteFIFO(ai, 0x18f, old_mid_fifo_entry);
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL,
old_master_cntl);
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_MODULATOR_CNTL1,
old_modulator_cntl1);
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_PRE_DAC_MUX_CNTL,
old_pre_dac_mux_cntl);
Radeon_VIPWrite(ai, ai->si->theatre_channel, THEATRE_VIP_TV_DAC_CNTL,
old_tv_dac_cntl);
return displays;
}
static display_device_e
Radeon_DetectTV(accelerator_info *ai, bool tv_crt_found)
{
switch (ai->si->asic) {
case rt_r100:
case rt_r200:
return Radeon_DetectTV_Theatre(ai);
case rt_rv100:
case rt_rv200:
case rt_rv250:
case rt_rv280:
case rt_rs100:
case rt_rs200:
case rt_rs300:
return Radeon_DetectTV_RV200(ai, tv_crt_found);
case rt_r300:
case rt_r350:
case rt_rv350:
case rt_rv380:
case rt_r420:
return Radeon_DetectTV_R300(ai);
}
return dd_none;
}
static void
Radeon_FindFPTiming_DetailedMonitorDesc(const edid1_info *edid, fp_info *fp,
uint32 *max_hsize, uint32 *max_vsize)
{
int i;
for (i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; ++i) {
if (edid->detailed_monitor[i].monitor_desc_type == EDID1_IS_DETAILED_TIMING) {
const edid1_detailed_timing *timing = &edid->detailed_monitor[i].data.detailed_timing;
SHOW_FLOW(2, "Found detailed timing for mode %dx%d in DDC data",
(int)timing->h_active, (int)timing->v_active);
if (timing->h_active > *max_hsize && timing->v_active > *max_vsize) {
*max_hsize = timing->h_active;
*max_vsize = timing->v_active;
fp->panel_xres = timing->h_active;
fp->h_blank = timing->h_blank;
fp->h_over_plus = timing->h_sync_off;
fp->h_sync_width = timing->h_sync_width;
fp->panel_yres = timing->v_active;
fp->v_blank = timing->v_blank;
fp->v_over_plus = timing->v_sync_off;
fp->v_sync_width = timing->v_sync_width;
fp->dot_clock = timing->pixel_clock * 10;
}
}
}
}
static void
Radeon_FindFPTiming_StandardTiming(const edid1_info *edid, fp_info *fp,
uint32 *max_hsize, uint32 *max_vsize)
{
int i;
for (i = 0; i < EDID1_NUM_STD_TIMING; ++i) {
const edid1_std_timing *std_timing = &edid->std_timing[i];
int best_fit = -1;
int best_refresh_deviation = 10000;
int j;
if (std_timing->h_size <= 256)
continue;
for (j = 0; j < (int)vesa_mode_list_count; ++j) {
int refresh_rate, cur_refresh_deviation;
if (vesa_mode_list[j].h_display != std_timing->h_size
|| vesa_mode_list[j].v_display != std_timing->v_size)
continue;
refresh_rate = (int64)vesa_mode_list[j].pixel_clock * 1000*1000
/ (vesa_mode_list[j].h_total * vesa_mode_list[j].v_total);
cur_refresh_deviation = (100 * (refresh_rate - std_timing->refresh
* 1000)) / refresh_rate;
if (cur_refresh_deviation < 0)
cur_refresh_deviation = -cur_refresh_deviation;
if (cur_refresh_deviation < 1
&& cur_refresh_deviation < best_refresh_deviation) {
best_fit = j;
best_refresh_deviation = cur_refresh_deviation;
}
}
if (best_fit < 0) {
SHOW_FLOW(2, "Unsupported standard mode %dx%d@%dHz (not VESA)",
std_timing->h_size, std_timing->v_size, std_timing->refresh);
continue;
}
if (std_timing->h_size > *max_hsize && std_timing->h_size > *max_vsize) {
const display_timing *timing = &vesa_mode_list[best_fit];
SHOW_FLOW(2, "Found DDC data for standard mode %dx%d",
(int)timing->h_display, (int)timing->v_display);
*max_hsize = timing->h_display;
*max_vsize = timing->h_display;
fp->panel_xres = timing->h_display;
fp->h_blank = timing->h_total - timing->h_display;
fp->h_over_plus = timing->h_sync_start - timing->h_display;
fp->h_sync_width = timing->h_sync_end - timing->h_sync_start;
fp->panel_yres = timing->v_display;
fp->v_blank = timing->v_total - timing->v_display;
fp->v_over_plus = timing->v_sync_start - timing->v_display;
fp->v_sync_width = timing->v_sync_end - timing->v_sync_start;
fp->dot_clock = timing->pixel_clock;
}
}
}
static status_t
Radeon_StoreFPEDID(accelerator_info *ai, int port, const edid1_info *edid)
{
fp_info *fp = &ai->si->flatpanels[port];
uint32 max_hsize, max_vsize;
max_hsize = max_vsize = 0;
Radeon_FindFPTiming_DetailedMonitorDesc(edid, fp, &max_hsize, &max_vsize);
if (max_hsize == 0) {
SHOW_FLOW0(2, "Timing is not explicitely defined in DDC - checking standard modes");
Radeon_FindFPTiming_StandardTiming(edid, fp, &max_hsize, &max_vsize);
if (max_hsize == 0) {
SHOW_FLOW0(2, "Still found no valid native mode, disabling DVI");
return B_ERROR;
}
}
SHOW_INFO(2, "h_disp=%d, h_blank=%d, h_over_plus=%d, h_sync_width=%d",
fp->panel_xres, fp->h_blank, fp->h_over_plus, fp->h_sync_width);
SHOW_INFO(2, "v_disp=%d, v_blank=%d, v_over_plus=%d, v_sync_width=%d",
fp->panel_yres, fp->v_blank, fp->v_over_plus, fp->v_sync_width);
SHOW_INFO(2, "pixel_clock=%d kHz", fp->dot_clock);
return B_OK;
}
static void
Radeon_ConnectorInfo(accelerator_info *ai, int port, disp_entity* ptr_entity)
{
const char* mon;
const char* ddc = ptr_entity->port_info[port].ddc_type == ddc_none_detected
? "None" : ptr_entity->port_info[port].ddc_type == ddc_monid
? "Mon ID" : ptr_entity->port_info[port].ddc_type == ddc_dvi
? "DVI DDC" : ptr_entity->port_info[port].ddc_type == ddc_vga
? "VGA DDC" : ptr_entity->port_info[port].ddc_type == ddc_crt2
? "CRT2 DDC" : "Error";
const char* tmds = ptr_entity->port_info[port].tmds_type == tmds_unknown
? "None" : ptr_entity->port_info[port].tmds_type == tmds_int
? "Internal" : ptr_entity->port_info[port].tmds_type == tmds_ext
? "External" : "??? ";
const char* dac = ptr_entity->port_info[port].dac_type == dac_unknown
? "Unknown" : ptr_entity->port_info[port].dac_type == dac_primary
? "Primary" : ptr_entity->port_info[port].dac_type == dac_tvdac
? "TV / External" : "Error";
const char* con;
if (ai->si->is_atombios) {
con = ptr_entity->port_info[port].connector_type == connector_none_atom
? "None" : ptr_entity->port_info[port].connector_type == connector_vga_atom
? "VGA" : ptr_entity->port_info[port].connector_type == connector_dvi_i_atom
? "DVI-I" : ptr_entity->port_info[port].connector_type == connector_dvi_d_atom
? "DVI-D" : ptr_entity->port_info[port].connector_type == connector_dvi_a_atom
? "DVI-A" : ptr_entity->port_info[port].connector_type == connector_stv_atom
? "S-Video TV" : ptr_entity->port_info[port].connector_type == connector_ctv_atom
? "Composite TV" : ptr_entity->port_info[port].connector_type == connector_lvds_atom
? "LVDS" : ptr_entity->port_info[port].connector_type == connector_digital_atom
? "Digital" : ptr_entity->port_info[port].connector_type == connector_unsupported_atom
? "N/A " : "Err ";
} else {
con = ptr_entity->port_info[port].connector_type == connector_none
? "None" : ptr_entity->port_info[port].connector_type == connector_crt
? "VGA" : ptr_entity->port_info[port].connector_type == connector_dvi_i
? "DVI-I" : ptr_entity->port_info[port].connector_type == connector_dvi_d
? "DVI-D" : ptr_entity->port_info[port].connector_type == connector_proprietary
? "Proprietary" : ptr_entity->port_info[port].connector_type == connector_stv
? "S-Video TV" : ptr_entity->port_info[port].connector_type == connector_ctv
? "Composite TV" : ptr_entity->port_info[port].connector_type == connector_unsupported
? "N/A" : "Err";
}
mon = ptr_entity->port_info[port].mon_type == mt_unknown ? "???"
: ptr_entity->port_info[port].mon_type == mt_none ? "None"
: ptr_entity->port_info[port].mon_type == mt_crt ? "CRT "
: ptr_entity->port_info[port].mon_type == mt_lcd ? "LCD "
: ptr_entity->port_info[port].mon_type == mt_dfp ? "DVI "
: ptr_entity->port_info[port].mon_type == mt_ctv ? "Composite TV"
: ptr_entity->port_info[port].mon_type == mt_stv ? "S-Video TV"
: "Err ?";
SHOW_INFO(2, "Port %d:- \nMonitor: %s\nConn Type: %s\nDDC Port: %s\nTMDS Type: %s\nDAC Type: %s",
port, mon, con, ddc, tmds, dac);
}
void
Radeon_DetectDisplays(accelerator_info *ai)
{
shared_info *si = ai->si;
disp_entity* routes = &si->routing;
display_device_e displays = 0;
display_device_e controlled_displays = ai->vc->controlled_displays;
int i;
uint32 edid_regs[] = {
0,
RADEON_GPIO_MONID,
RADEON_GPIO_DVI_DDC,
RADEON_GPIO_VGA_DDC,
RADEON_GPIO_CRT2_DDC
};
Radeon_WaitForIdle(ai, true);
if (routes->port_info[1].tmds_type == tmds_int) {
radeon_connector swap_entity;
swap_entity = routes->port_info[0];
routes->port_info[0] = routes->port_info[1];
routes->port_info[1] = swap_entity;
SHOW_FLOW0(2, "Swapping TMDS_INT to first port");
} else if (routes->port_info[0].tmds_type != tmds_int
&& routes->port_info[1].tmds_type != tmds_int) {
if (routes->port_info[1].dac_type == dac_primary
&& routes->port_info[0].dac_type != dac_primary) {
radeon_connector swap_entity;
swap_entity = routes->port_info[0];
routes->port_info[0] = routes->port_info[1];
routes->port_info[1] = swap_entity;
SHOW_FLOW0(2, "Swapping Primary Dac to front");
}
}
if (si->asic == rt_rs300) {
if (routes->port_info[0].connector_type == connector_crt) {
routes->port_info[0].dac_type = dac_tvdac;
routes->port_info[1].dac_type = dac_primary;
} else {
routes->port_info[1].dac_type = dac_primary;
routes->port_info[0].dac_type = dac_tvdac;
}
} else if (si->num_crtc == 1) {
routes->port_info[0].dac_type = dac_primary;
}
for (i = 0; i < 2; i++) {
memset(&routes->port_info[i].edid , 0, sizeof(edid1_info));
switch (routes->port_info[i].ddc_type) {
case ddc_monid:
case ddc_dvi:
case ddc_vga:
case ddc_crt2:
if (Radeon_ReadEDID(ai,
edid_regs[routes->port_info[i].ddc_type],
&routes->port_info[i].edid)) {
routes->port_info[i].edid_valid = true;
SHOW_FLOW(2, "Edid Data for CRTC %d on line %d", i, routes->port_info[i].ddc_type);
edid_dump(&routes->port_info[i].edid);
} else {
routes->port_info[i].mon_type = mt_none;
}
break;
default:
SHOW_FLOW(2, "No Edid Pin Assigned to CRTC %d ", i);
routes->port_info[i].mon_type = mt_none;
}
if (routes->port_info[i].edid_valid) {
if (routes->port_info[i].edid.display.input_type == 1) {
SHOW_FLOW0(2, "Must be a DVI monitor");
if (routes->port_info[i].tmds_type == tmds_ext){
if (Radeon_StoreFPEDID(ai, i, &routes->port_info[i].edid) == B_OK) {
SHOW_INFO0(2, "Found Ext Laptop DVI");
routes->port_info[i].mon_type = mt_dfp;
ai->si->flatpanels[i].is_fp2 = true;
displays |= dd_dvi_ext;
} else {
SHOW_ERROR0(2, "Disabled Ext DVI - invalid EDID");
}
} else {
if (INREG(ai->regs, RADEON_FP_GEN_CNTL) & (1 << 7) || (!si->is_mobility)) {
if (Radeon_StoreFPEDID(ai, i, &routes->port_info[i].edid) == B_OK) {
SHOW_INFO0(2, "Found DVI");
routes->port_info[i].mon_type = mt_dfp;
displays |= dd_dvi;
} else {
SHOW_ERROR0(2, "Disabled DVI - invalid EDID");
}
} else {
SHOW_INFO0(2, "Laptop Panel Found");
routes->port_info[i].mon_type = mt_lcd;
displays |= dd_lvds;
}
ai->si->flatpanels[1].is_fp2 = FALSE;
}
} else {
if (si->num_crtc > 1) {
SHOW_FLOW0(2, "Must be an analog monitor on DVI port");
routes->port_info[i].mon_type = mt_crt;
displays |= dd_tv_crt;
} else {
SHOW_FLOW0(2, "Seems to be a CRT on VGA port!?");
routes->port_info[i].mon_type = mt_crt;
displays |= dd_crt;
}
}
}
}
if (!routes->port_info[0].edid_valid) {
SHOW_INFO0(2, "Searching port 0");
if (si->is_mobility && (INREG(ai->regs, RADEON_BIOS_4_SCRATCH) & 4)) {
SHOW_INFO0(2, "Found Laptop Panel");
routes->port_info[0].mon_type = mt_lcd;
displays |= dd_lvds;
}
}
if (!routes->port_info[1].edid_valid) {
if (si->is_mobility && (INREG(ai->regs, RADEON_FP2_GEN_CNTL) & RADEON_FP2_FPON)) {
SHOW_INFO0(2, "Found Ext Laptop DVI");
routes->port_info[1].mon_type = mt_dfp;
displays |= dd_dvi_ext;
}
}
if (routes->port_info[0].mon_type == mt_none) {
if (routes->port_info[1].mon_type == mt_none) {
routes->port_info[0].mon_type = mt_crt;
} else {
radeon_connector portSwapEntity;
fp_info panelInfoSwapEntity;
portSwapEntity = routes->port_info[0];
routes->port_info[0] = routes->port_info[1];
routes->port_info[1] = portSwapEntity;
panelInfoSwapEntity = ai->si->flatpanels[0];
ai->si->flatpanels[0] = ai->si->flatpanels[1];
ai->si->flatpanels[1] = panelInfoSwapEntity;
SHOW_ERROR0(2, "swapping active port 2 to free port 1");
}
}
routes->reversed_DAC = false;
if (routes->port_info[1].dac_type == dac_tvdac) {
SHOW_ERROR0(2, "Reversed dac detected (not impl. yet)");
routes->reversed_DAC = true;
}
if ((displays & dd_crt) == 0 && (controlled_displays & dd_crt) != 0
&& Radeon_DetectCRT(ai))
displays |= dd_crt;
if ((displays & dd_tv_crt) == 0
&& (controlled_displays & dd_tv_crt) != 0
&& Radeon_DetectTVCRT(ai))
displays |= dd_tv_crt;
if ((controlled_displays & (dd_ctv | dd_stv)) != 0)
displays |= Radeon_DetectTV(ai, (displays & dd_tv_crt) != 0);
SHOW_INFO(0, "Detected monitors: 0x%x", displays);
displays &= controlled_displays;
if (displays == 0)
displays = dd_crt;
Radeon_ConnectorInfo(ai, 0, routes);
Radeon_ConnectorInfo(ai, 1, routes);
ai->vc->connected_displays = displays;
RELEASE_BEN(si->cp.lock);
}