You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1474 lines
36 KiB

/*********************************************************************
* tv output of radeon 7000.
* some code borrowed from gatos.sourceforge.net
* Copyright 2007, Fuxin Zhang, Lemote Corp.
* History:
*
* First version. 2007.1.13
*********************************************************************/
#include "theater_out.h"
//#define RADEON_DEBUG 1
#if RADEON_DEBUG
#define RTTRACE(x) { prom_printf x; }
#else
#define RTTRACE(x)
#endif
/**********************************************************************
*
* MASK_N_BIT
*
**********************************************************************/
#define MASK_N_BIT(n) (1UL << (n))
/**********************************************************************
*
* Constants
*
**********************************************************************/
/*
* Reference frequency
* FIXME: This should be extracted from BIOS data
*/
#define REF_FREQUENCY 27000
#define TV_PLL_FINE_INI 0X10000000
/*
* VIP_TV_PLL_CNTL
*/
#define VIP_TV_PLL_CNTL_M_SHIFT 0
#define VIP_TV_PLL_CNTL_NLO 0x1ff
#define VIP_TV_PLL_CNTL_NLO_SHIFT 8
#define VIP_TV_PLL_CNTL_NHI 0x600
#define VIP_TV_PLL_CNTL_NHI_SHIFT (21-9)
#define VIP_TV_PLL_CNTL_P_SHIFT 24
/*
* VIP_CRT_PLL_CNTL
*/
#define VIP_CRT_PLL_CNTL_M 0xff
#define VIP_CRT_PLL_CNTL_M_SHIFT 0
#define VIP_CRT_PLL_CNTL_NLO 0x1ff
#define VIP_CRT_PLL_CNTL_NLO_SHIFT 8
#define VIP_CRT_PLL_CNTL_NHI 0x600
#define VIP_CRT_PLL_CNTL_NHI_SHIFT (21-9)
#define VIP_CRT_PLL_CNTL_CLKBY2 MASK_N_BIT(25)
/*
* Value for VIP_PLL_CNTL0
*/
#define VIP_PLL_CNTL0_INI 0x00acac18
#define VIP_PLL_CNTL0_TVSLEEPB MASK_N_BIT(3)
#define VIP_PLL_CNTL0_CRTSLEEPB MASK_N_BIT(4)
/*
* Value for VIP_PLL_TEST_CNTL
*/
#define VIP_PLL_TEST_CNTL_INI 0
/*
* VIP_CLOCK_SEL_CNTL
*/
#define VIP_CLOCK_SEL_CNTL_INI 0x33
#define VIP_CLOCK_SEL_CNTL_BYTCLK_SHIFT 2
#define VIP_CLOCK_SEL_CNTL_BYTCLK 0xc
#define VIP_CLOCK_SEL_CNTL_REGCLK MASK_N_BIT(5)
#define VIP_CLOCK_SEL_CNTL_BYTCLKD_SHIFT 8
/*
* Value for VIP_CLKOUT_CNTL
*/
#define VIP_CLKOUT_CNTL_INI 0x29
/*
* Value for VIP_SYNC_LOCK_CNTL
*/
#define VIP_SYNC_LOCK_CNTL_INI 0x01000000
/*
* Value for VIP_TVO_SYNC_PAT_EXPECT
*/
#define VIP_TVO_SYNC_PAT_EXPECT_INI 0x00000001
/*
* VIP_RGB_CNTL
*/
#define VIP_RGB_CNTL_RGB_IS_888_PACK MASK_N_BIT(0)
/*
* Value for VIP_VSCALER_CNTL2
*/
#define VIP_VSCALER_CNTL2_INI 0x10000000
/*
* Value for VIP_Y_FALL_CNTL
*/
/* #define VIP_Y_FALL_CNTL_INI 0x00010200 */
#define VIP_Y_FALL_CNTL_INI 0x80030400
/*
* VIP_UV_ADR
*/
#define VIP_UV_ADR_INI 0xc8
#define VIP_UV_ADR_HCODE_TABLE_SEL 0x06000000
#define VIP_UV_ADR_HCODE_TABLE_SEL_SHIFT 25
#define VIP_UV_ADR_VCODE_TABLE_SEL 0x18000000
#define VIP_UV_ADR_VCODE_TABLE_SEL_SHIFT 27
#define VIP_UV_ADR_MAX_UV_ADR 0x000000ff
#define VIP_UV_ADR_MAX_UV_ADR_SHIFT 0
#define VIP_UV_ADR_TABLE1_BOT_ADR 0x0000ff00
#define VIP_UV_ADR_TABLE1_BOT_ADR_SHIFT 8
#define VIP_UV_ADR_TABLE3_TOP_ADR 0x00ff0000
#define VIP_UV_ADR_TABLE3_TOP_ADR_SHIFT 16
#define MAX_FIFO_ADDR_RT 0x1a7
#define MAX_FIFO_ADDR_ERT 0x1ff
/*
* VIP_HOST_RD_WT_CNTL
*/
#define VIP_HOST_RD_WT_CNTL_RD MASK_N_BIT(12)
#define VIP_HOST_RD_WT_CNTL_RD_ACK MASK_N_BIT(13)
#define VIP_HOST_RD_WT_CNTL_WT MASK_N_BIT(14)
#define VIP_HOST_RD_WT_CNTL_WT_ACK MASK_N_BIT(15)
/*
* Value for VIP_SYNC_CNTL
*/
#define VIP_SYNC_CNTL_INI 0x28
/*
* VIP_VSCALER_CNTL1
*/
#define VIP_VSCALER_CNTL1_UV_INC 0xffff
#define VIP_VSCALER_CNTL1_UV_INC_SHIFT 0
/*
* VIP_TIMING_CNTL
*/
#define VIP_TIMING_CNTL_UV_OUT_POST_SCALE_SHIFT 24
#define VIP_TIMING_CNTL_INI 0x000b0000
#define VIP_TIMING_CNTL_H_INC_SHIFT 0
#define VIP_TIMING_CNTL_H_INC 0xfff
/*
* Value for VIP_PRE_DAC_MUX_CNTL
*/
#define VIP_PRE_DAC_MUX_CNTL_INI 0x0000000f
/*
* VIP_TV_DAC_CNTL
*/
#define VIP_TV_DAC_CNTL_NBLANK MASK_N_BIT(0)
#define VIP_TV_DAC_CNTL_DASLEEP MASK_N_BIT(3)
#define VIP_TV_DAC_CNTL_BGSLEEP MASK_N_BIT(6)
/*
* Value for VIP_FRAME_LOCK_CNTL
*/
#define VIP_FRAME_LOCK_CNTL_INI 0x0000000f
/*
* Value for VIP_HW_DEBUG
*/
#define VIP_HW_DEBUG_INI 0x00000200
/*
* VIP_MASTER_CNTL
*/
#define VIP_MASTER_CNTL_TV_ASYNC_RST MASK_N_BIT(0)
#define VIP_MASTER_CNTL_CRT_ASYNC_RST MASK_N_BIT(1)
#define VIP_MASTER_CNTL_RESTART_PHASE_FIX MASK_N_BIT(3)
#define VIP_MASTER_CNTL_TV_FIFO_ASYNC_RST MASK_N_BIT(4)
#define VIP_MASTER_CNTL_VIN_ASYNC_RST MASK_N_BIT(5)
#define VIP_MASTER_CNTL_AUD_ASYNC_RST MASK_N_BIT(6)
#define VIP_MASTER_CNTL_DVS_ASYNC_RST MASK_N_BIT(7)
#define VIP_MASTER_CNTL_CRT_FIFO_CE_EN MASK_N_BIT(9)
#define VIP_MASTER_CNTL_TV_FIFO_CE_EN MASK_N_BIT(10)
#define VIP_MASTER_CNTL_ON_INI (VIP_MASTER_CNTL_RESTART_PHASE_FIX | \
VIP_MASTER_CNTL_VIN_ASYNC_RST | \
VIP_MASTER_CNTL_AUD_ASYNC_RST | \
VIP_MASTER_CNTL_DVS_ASYNC_RST | \
VIP_MASTER_CNTL_CRT_FIFO_CE_EN | \
VIP_MASTER_CNTL_TV_FIFO_CE_EN)
#define VIP_MASTER_CNTL_OFF_INI (VIP_MASTER_CNTL_TV_ASYNC_RST | \
VIP_MASTER_CNTL_CRT_ASYNC_RST | \
VIP_MASTER_CNTL_RESTART_PHASE_FIX | \
VIP_MASTER_CNTL_TV_FIFO_ASYNC_RST | \
VIP_MASTER_CNTL_VIN_ASYNC_RST | \
VIP_MASTER_CNTL_AUD_ASYNC_RST | \
VIP_MASTER_CNTL_DVS_ASYNC_RST | \
VIP_MASTER_CNTL_CRT_FIFO_CE_EN | \
VIP_MASTER_CNTL_TV_FIFO_CE_EN)
/*
* Value for VIP_LINEAR_GAIN_SETTINGS
*/
#define VIP_LINEAR_GAIN_SETTINGS_INI 0x01000100
/*
* Value for VIP_GAIN_LIMIT_SETTINGS_INI
*/
#define VIP_GAIN_LIMIT_SETTINGS_INI 0x017f05ff
/*
* Value for VIP_UPSAMP_AND_GAIN_CNTL
*/
#define VIP_UPSAMP_AND_GAIN_CNTL_INI 0x00000005
/*
* RADEON_VCLK_ECP_CNTL
*/
#define RADEON_VCLK_ECP_CNTL_BYTECLK_POSTDIV 0x00030000
#define RADEON_VCLK_ECP_CNTL_BYTECLK_NODIV 0x00000000
/*
* RADEON_PLL_TEST_CNTL
*/
#define RADEON_PLL_TEST_CNTL_PLL_MASK_READ_B MASK_N_BIT(9)
/*
* RADEON_DAC_CNTL
*/
#define RADEON_DAC_CNTL_DAC_TVO_EN MASK_N_BIT(10)
#define RADEON_PPLL_POST3_DIV_BY_2 0x10000
#define RADEON_PPLL_POST3_DIV_BY_3 0x40000
#define RADEON_PPLL_FB3_DIV_SHIFT 0
#define RADEON_PPLL_POST3_DIV_SHIFT 16
/*
* RADEON_DISP_MERGE_CNTL
*/
#define RADEON_DISP_MERGE_CNTL_INI 0xffff0000
/*
* RADEON_HTOTAL_CNTL
*/
#define RADEON_HTOTAL_CNTL_HTOT_PIX_SLIP_SHIFT 0
#define RADEON_HTOTAL_CNTL_HTOT_CNTL_VGA_EN MASK_N_BIT(28)
/*
* RADEON_DISP_OUTPUT_CNTL
*/
#define RADEON_DISP_TV_SOURCE MASK_N_BIT(16)
#define RADEON_DISP_TV_MODE_MASK (3 << 17)
#define RADEON_DISP_TV_MODE_888 (0 << 17)
#define RADEON_DISP_TV_MODE_565 (1 << 17)
#define RADEON_DISP_TV_YG_DITH_EN MASK_N_BIT(19)
#define RADEON_DISP_TV_CBB_CRR_DITH_EN MASK_N_BIT(20)
#define RADEON_DISP_TV_BIT_WIDTH MASK_N_BIT(21)
#define RADEON_DISP_TV_SYNC_MODE_MASK (3 << 22)
#define RADEON_DISP_TV_SYNC_COLOR_MASK (3 << 25)
/*
* ERT registers
*/
#define TV_MASTER_CNTL 0x0800
#define TV_MASTER_CNTL_TVCLK_ALWAYS_ON MASK_N_BIT(30)
#define TV_MASTER_CNTL_TV_ON MASK_N_BIT(31)
#define TV_MASTER_CNTL_ON_INI (VIP_MASTER_CNTL_VIN_ASYNC_RST | \
VIP_MASTER_CNTL_CRT_FIFO_CE_EN | \
VIP_MASTER_CNTL_TV_FIFO_CE_EN | \
TV_MASTER_CNTL_TVCLK_ALWAYS_ON | \
TV_MASTER_CNTL_TV_ON)
#define TV_MASTER_CNTL_OFF_INI (VIP_MASTER_CNTL_TV_ASYNC_RST | \
VIP_MASTER_CNTL_CRT_ASYNC_RST | \
VIP_MASTER_CNTL_TV_FIFO_ASYNC_RST | \
VIP_MASTER_CNTL_CRT_FIFO_CE_EN | \
VIP_MASTER_CNTL_TV_FIFO_CE_EN | \
TV_MASTER_CNTL_TVCLK_ALWAYS_ON)
#define TV_RGB_CNTL 0x0804
#define TV_RGB_CNTL_INI 0x007b0004
#define TV_SYNC_CNTL 0x0808
#define TV_HTOTAL 0x080c
#define TV_HDISP 0x0810
#define TV_HSTART 0x0818
#define TV_HCOUNT 0x081c
#define TV_VTOTAL 0x0820
#define TV_VDISP 0x0824
#define TV_VCOUNT 0x0828
#define TV_FTOTAL 0x082c
#define TV_FCOUNT 0x0830
#define TV_FRESTART 0x0834
#define TV_HRESTART 0x0838
#define TV_VRESTART 0x083c
#define TV_HOST_READ_DATA 0x0840
#define TV_HOST_WRITE_DATA 0x0844
#define TV_HOST_RD_WT_CNTL 0x0848
#define TV_VSCALER_CNTL1 0x084c
#define TV_VSCALER_CNTL1_RESTART_FIELD MASK_N_BIT(29)
#define TV_TIMING_CNTL 0x0850
#define TV_VSCALER_CNTL2 0x0854
#define TV_Y_FALL_CNTL 0x0858
#define TV_Y_RISE_CNTL 0x085c
#define TV_Y_SAWTOOTH_CNTL 0x0860
#define TV_UPSAMP_AND_GAIN_CNTL 0x0864
#define TV_GAIN_LIMIT_SETTINGS 0x0868
#define TV_LINEAR_GAIN_SETTINGS 0x086c
#define TV_MODULATOR_CNTL1 0x0870
#define TV_MODULATOR_CNTL2 0x0874
#define TV_PRE_DAC_MUX_CNTL 0x0888
#define TV_DAC_CNTL 0x088c
#define TV_DAC_CNTL_NBLANK MASK_N_BIT(0)
#define TV_DAC_CNTL_NHOLD MASK_N_BIT(1)
#define TV_DAC_CNTL_BGSLEEP MASK_N_BIT(6)
#define TV_DAC_CNTL_RDACPD MASK_N_BIT(24)
#define TV_DAC_CNTL_GDACPD MASK_N_BIT(25)
#define TV_DAC_CNTL_BDACPD MASK_N_BIT(26)
#define TV_CRC_CNTL 0x0890
#define TV_UV_ADR 0x08ac
/*
* ERT PLL registers
*/
#define TV_PLL_CNTL 0x21
#define TV_PLL_CNTL1 0x22
#define TV_PLL_CNTL1_TVPLL_RESET MASK_N_BIT(1)
#define TV_PLL_CNTL1_TVPLL_SLEEP MASK_N_BIT(3)
#define TV_PLL_CNTL1_TVPDC_SHIFT 14
#define TV_PLL_CNTL1_TVPDC_MASK (3 << 14)
#define TV_PLL_CNTL1_TVCLK_SRC_SEL MASK_N_BIT(30)
/*
* Constant upsampler coefficients
*/
static
const
unsigned int upsamplerCoeffs[] = {
0x3f010000,
0x7b008002,
0x00003f01,
0x341b7405,
0x7f3a7617,
0x00003d04,
0x2d296c0a,
0x0e316c2c,
0x00003e7d,
0x2d1f7503,
0x2927643b,
0x0000056f,
0x29257205,
0x25295050,
0x00000572
};
#define N_UPSAMPLER_COEFFS (sizeof(upsamplerCoeffs) / sizeof(upsamplerCoeffs[ 0 ]))
/*
* Maximum length of horizontal/vertical code timing tables for state storage
*/
#define MAX_H_CODE_TIMING_LEN 32
#define MAX_V_CODE_TIMING_LEN 32
/*
* Type of VIP bus
*/
#define VIP_TYPE "ATI VIP BUS"
/*
* Limits of h/v positions (hPos & vPos in TheaterOutRec)
*/
#define MAX_H_POSITION 5 /* Range: [-5..5], negative is on the lef 0 is default, positive is on the right */
#define MAX_V_POSITION 5 /* Range: [-5..5], negative is up, 0 is defaul positive is down */
/*
* Unit for hPos (in TV clock periods)
*/
#define H_POS_UNIT 10
/*
* Indexes in h. code timing table for horizontal line position adjustment
*/
#define H_TABLE_POS1 6
#define H_TABLE_POS2 8
/*
* Limits of hor. size (hSize in TheaterOutRec)
*/
#define MAX_H_SIZE 5 /* Range: [-5..5], negative is smaller, positive is larger */
/**********************************************************************
*
* TimingTableEl
*
* Elements of H/V code timing tables
*
**********************************************************************/
typedef unsigned short TimingTableEl; /* Bits 0 to 13 only are actually used */
/**********************************************************************
*
* ModeConstants
*
* Storage of constants related to a single video mode
*
**********************************************************************/
typedef struct {
unsigned short horResolution;
unsigned short verResolution;
TVStd standard;
unsigned short horTotal;
unsigned short verTotal;
unsigned short horStart;
unsigned short horSyncStart;
unsigned short verSyncStart;
unsigned defRestart;
unsigned int vScalerCntl1;
unsigned int yRiseCntl;
unsigned int ySawtoothCntl;
unsigned short crtcPLL_N;
unsigned char crtcPLL_M;
Bool crtcPLL_divBy2;
unsigned char crtcPLL_byteClkDiv;
unsigned char crtcPLL_postDiv;
Bool use888RGB; /* False: RGB data is 565 packed (2 bytes/pixel) */
/* True : RGB data is 888 packed (3 bytes/pixel) */
unsigned pixToTV;
unsigned char byteClkDelay;
unsigned int tvoDataDelayA;
unsigned int tvoDataDelayB;
const TimingTableEl *horTimingTable;
const TimingTableEl *verTimingTable;
} ModeConstants;
/**********************************************************************
*
* TheaterState
*
* Storage of RT state
*
**********************************************************************/
typedef struct {
unsigned int clkout_cntl;
unsigned int clock_sel_cntl;
unsigned int crc_cntl;
unsigned int crt_pll_cntl;
unsigned int dfrestart;
unsigned int dhrestart;
unsigned int dvrestart;
unsigned int frame_lock_cntl;
unsigned int gain_limit_settings;
unsigned int hdisp;
unsigned int hstart;
unsigned int htotal;
unsigned int hw_debug;
unsigned int linear_gain_settings;
unsigned int master_cntl;
unsigned int modulator_cntl1;
unsigned int modulator_cntl2;
unsigned int pll_cntl0;
unsigned int pll_test_cntl;
unsigned int pre_dac_mux_cntl;
unsigned int rgb_cntl;
unsigned int sync_cntl;
unsigned int sync_lock_cntl;
unsigned int sync_size;
unsigned int timing_cntl;
unsigned int tvo_data_delay_a;
unsigned int tvo_data_delay_b;
unsigned int tvo_sync_pat_expect;
unsigned int tvo_sync_threshold;
unsigned int tv_dac_cntl;
unsigned int tv_pll_cntl;
unsigned int tv_pll_fine_cntl;
unsigned int upsamp_and_gain_cntl;
unsigned int upsamp_coeffs[N_UPSAMPLER_COEFFS];
unsigned int uv_adr;
unsigned int vdisp;
unsigned int vftotal;
unsigned int vscaler_cntl1;
unsigned int vscaler_cntl2;
unsigned int vtotal;
unsigned int y_fall_cntl;
unsigned int y_rise_cntl;
unsigned int y_saw_tooth_cntl;
unsigned int disp_merge_cntl;
TimingTableEl h_code_timing[MAX_H_CODE_TIMING_LEN];
TimingTableEl v_code_timing[MAX_V_CODE_TIMING_LEN];
} TheaterState, *TheaterStatePtr;
/**********************************************************************
*
* TVConstants
*
* Constants that depend on tv standard only
*
**********************************************************************/
typedef struct {
unsigned char tvPLL_M;
unsigned short tvPLL_N;
unsigned char tvPLL_postDiv;
unsigned int tvClockT; /* Period of TV clock (unit = 100 psec) */
unsigned int modulatorCntl1;
unsigned int modulatorCntl2;
unsigned int vip_tvDAC_Cntl;
unsigned int ert_tvDAC_Cntl;
unsigned int vftotal;
unsigned linesFrame;
unsigned zeroHSize; /* Length of the picture part of a hor. line for hSize = 0 (unit = 100 psec) */
unsigned hSizeUnit; /* Value of hSize = 1 (unit = 100 psec) */
} TVConstants;
/**********************************************************************
*
* tvStdConsts
*
* Table of constants for tv standards (index is a TVStd)
*
**********************************************************************/
static
const
TVConstants tvStdConsts[] = {
/*
* NTSC
*/
{
22, /* tvPLL_M */
175, /* tvPLL_N */
5, /* tvPLL_postDiv */
233, /* tvClockT */
0x60bb468c, /* modulatorCntl1 */
0x00000191, /* modulatorCntl2 */
0x00000113, /* vip_tvDAC_Cntl */
0x00680113, /* ert_tvDAC_Cntl */
1, /* vftotal */
525, /* linesFrame */
479166, /* zeroHSize */
9478 /* hSizeUnit */
},
/*
* PAL
*/
{
113, /* tvPLL_M */
668, /* tvPLL_N */
3, /* tvPLL_postDiv */
188, /* tvClockT */
0x60bb3bcc, /* modulatorCntl1 */
0x003e01b2, /* modulatorCntl2 */
0x00000013, /* vip_tvDAC_Cntl */
0x00680013, /* ert_tvDAC_Cntl */
3, /* vftotal */
625, /* linesFrame */
473200, /* zeroHSize */
9360 /* hSizeUnit */
}
};
/**********************************************************************
*
* availableModes
*
* Table of all allowed modes for tv output
*
**********************************************************************/
static
const
TimingTableEl horTimingNTSC_BIOS[] = {
0x0007,
0x003f,
0x0263,
0x0a24,
0x2a6b,
0x0a36,
0x126d, /* H_TABLE_POS1 */
0x1bfe,
0x1a8f, /* H_TABLE_POS2 */
0x1ec7,
0x3863,
0x1bfe,
0x1bfe,
0x1a2a,
0x1e95,
0x0e31,
0x201b,
0
};
static
const
TimingTableEl verTimingNTSC_BIOS[] = {
0x2001,
0x200d,
0x1006,
0x0c06,
0x1006,
0x1818,
0x21e3,
0x1006,
0x0c06,
0x1006,
0x1817,
0x21d4,
0x0002,
0
};
static
const
TimingTableEl horTimingPAL_BIOS[] = {
0x0007,
0x0058,
0x027c,
0x0a31,
0x2a77,
0x0a95,
0x124f, /* H_TABLE_POS1 */
0x1bfe,
0x1b22, /* H_TABLE_POS2 */
0x1ef9,
0x387c,
0x1bfe,
0x1bfe,
0x1b31,
0x1eb5,
0x0e43,
0x201b,
0
};
static
const
TimingTableEl verTimingPAL_BIOS[] = {
0x2001,
0x200c,
0x1005,
0x0c05,
0x1005,
0x1401,
0x1821,
0x2240,
0x1005,
0x0c05,
0x1005,
0x1401,
0x1822,
0x2230,
0x0002,
0
};
static
const
ModeConstants availableModes[] = {
{
800, /* horResolution */
600, /* verResolution */
TV_STD_NTSC, /* standard */
990, /* horTotal */
740, /* verTotal */
813, /* horStart */
824, /* horSyncStart */
632, /* verSyncStart */
625592, /* defRestart */
0x0900b46b, /* vScalerCntl1 */
0x00012c00, /* yRiseCntl */
0x10002d1a, /* ySawtoothCntl */
592, /* crtcPLL_N */
91, /* crtcPLL_M */
1, /* crtcPLL_divBy2 */
0, /* crtcPLL_byteClkDiv */
4, /* crtcPLL_postDiv */
0, /* use888RGB */
1022, /* pixToTV */
1, /* byteClkDelay */
0x0a0b0907, /* tvoDataDelayA */
0x060a090a, /* tvoDataDelayB */
horTimingNTSC_BIOS, /* horTimingTable */
verTimingNTSC_BIOS /* verTimingTable */
},
{
800, /* horResolution */
600, /* verResolution */
TV_STD_PAL, /* standard */
1144, /* horTotal */
706, /* verTotal */
812, /* horStart */
824, /* horSyncStart */
669, /* verSyncStart */
696700, /* defRestart */
0x09009097, /* vScalerCntl1 */
0x000007da, /* yRiseCntl */
0x10002426, /* ySawtoothCntl */
1382, /* crtcPLL_N */
231, /* crtcPLL_M */
1, /* crtcPLL_divBy2 */
0, /* crtcPLL_byteClkDiv */
4, /* crtcPLL_postDiv */
0, /* use888RGB */
759, /* pixToTV */
1, /* byteClkDelay */
0x0a0b0907, /* tvoDataDelayA */
0x060a090a, /* tvoDataDelayB */
horTimingPAL_BIOS, /* horTimingTable */
verTimingPAL_BIOS /* verTimingTable */
}
};
#define N_AVAILABLE_MODES (sizeof(availableModes) / sizeof(availableModes[ 0 ]))
TheaterState st;
/**********************************************************************
*
* ert_read
*
* Read from an ERT register
*
**********************************************************************/
static
void ert_read(unsigned int reg, unsigned int *data)
{
*data = MMINL(reg);
RTTRACE(("ert_read : %x = %x\n", reg, *data));
}
/**********************************************************************
*
* ert_write
*
* Write to an ERT register
*
**********************************************************************/
static
void ert_write(unsigned int reg, unsigned int data)
{
RTTRACE(("ert_write: %x = %x\n", reg, data));
MMOUTL(reg, data);
}
/**********************************************************************
*
* waitPLL_lock
*
* Wait for PLLs to lock
*
**********************************************************************/
static
void waitPLL_lock(unsigned nTests, unsigned nWaitLoops, unsigned cntThreshold)
{
unsigned int savePLLTest;
unsigned i;
unsigned j;
MMOUTL(RADEON_TEST_DEBUG_MUX,
(MMINL(RADEON_TEST_DEBUG_MUX) & 0xffff60ff) | 0x100);
savePLLTest = INPLL(RADEON_PLL_TEST_CNTL);
OUTPLL(RADEON_PLL_TEST_CNTL,
savePLLTest & ~RADEON_PLL_TEST_CNTL_PLL_MASK_READ_B);
MMOUTB(RADEON_CLOCK_CNTL_INDEX, RADEON_PLL_TEST_CNTL);
for (i = 0; i < nTests; i++) {
MMOUTB(RADEON_CLOCK_CNTL_DATA + 3, 0);
for (j = 0; j < nWaitLoops; j++)
if (MMINB(RADEON_CLOCK_CNTL_DATA + 3) >= cntThreshold)
break;
}
OUTPLL(RADEON_PLL_TEST_CNTL, savePLLTest);
MMOUTL(RADEON_TEST_DEBUG_MUX,
MMINL(RADEON_TEST_DEBUG_MUX) & 0xffffe0ff);
}
/**********************************************************************
*
* writeFIFO
*
* Write to RT FIFO RAM
*
**********************************************************************/
static
void writeFIFO(unsigned short addr, unsigned int value)
{
unsigned int tmp;
ert_write(TV_HOST_WRITE_DATA, value);
ert_write(TV_HOST_RD_WT_CNTL, addr | VIP_HOST_RD_WT_CNTL_WT);
do {
ert_read(TV_HOST_RD_WT_CNTL, &tmp);
}
while ((tmp & VIP_HOST_RD_WT_CNTL_WT_ACK) == 0);
ert_write(TV_HOST_RD_WT_CNTL, 0);
}
/**********************************************************************
*
* readFIFO
*
* Read from RT FIFO RAM
*
**********************************************************************/
static
void readFIFO(unsigned short addr, unsigned int *value)
{
unsigned int tmp;
ert_write(TV_HOST_RD_WT_CNTL, addr | VIP_HOST_RD_WT_CNTL_RD);
do {
ert_read(TV_HOST_RD_WT_CNTL, &tmp);
}
while ((tmp & VIP_HOST_RD_WT_CNTL_RD_ACK) == 0);
ert_write(TV_HOST_RD_WT_CNTL, 0);
ert_read(TV_HOST_READ_DATA, value);
}
/**********************************************************************
*
* getTimingTablesAddr
*
* Get FIFO addresses of horizontal & vertical code timing tables from
* settings of uv_adr register.
*
**********************************************************************/
static
void
getTimingTablesAddr(unsigned int uv_adr,
Bool isERT, unsigned short *hTable, unsigned short *vTable)
{
switch ((uv_adr & VIP_UV_ADR_HCODE_TABLE_SEL) >>
VIP_UV_ADR_HCODE_TABLE_SEL_SHIFT) {
case 0:
*hTable = isERT ? MAX_FIFO_ADDR_ERT : MAX_FIFO_ADDR_RT;
break;
case 1:
*hTable =
((uv_adr & VIP_UV_ADR_TABLE1_BOT_ADR) >>
VIP_UV_ADR_TABLE1_BOT_ADR_SHIFT) * 2;
break;
case 2:
*hTable =
((uv_adr & VIP_UV_ADR_TABLE3_TOP_ADR) >>
VIP_UV_ADR_TABLE3_TOP_ADR_SHIFT) * 2;
break;
default:
/*
* Of course, this should never happen
*/
*hTable = 0;
break;
}
switch ((uv_adr & VIP_UV_ADR_VCODE_TABLE_SEL) >>
VIP_UV_ADR_VCODE_TABLE_SEL_SHIFT) {
case 0:
*vTable =
((uv_adr & VIP_UV_ADR_MAX_UV_ADR) >>
VIP_UV_ADR_MAX_UV_ADR_SHIFT) * 2 + 1;
break;
case 1:
*vTable =
((uv_adr & VIP_UV_ADR_TABLE1_BOT_ADR) >>
VIP_UV_ADR_TABLE1_BOT_ADR_SHIFT) * 2 + 1;
break;
case 2:
*vTable =
((uv_adr & VIP_UV_ADR_TABLE3_TOP_ADR) >>
VIP_UV_ADR_TABLE3_TOP_ADR_SHIFT) * 2 + 1;
break;
default:
/*
* Of course, this should never happen
*/
*vTable = 0;
break;
}
}
/**********************************************************************
*
* saveTimingTables
*
* Save horizontal/vertical timing code tables
*
**********************************************************************/
static
void saveTimingTables(TheaterStatePtr save)
{
unsigned short hTable;
unsigned short vTable;
unsigned int tmp;
unsigned i;
ert_read(TV_UV_ADR, &save->uv_adr);
getTimingTablesAddr(save->uv_adr, 1, &hTable, &vTable);
/*
* Reset FIFO arbiter in order to be able to access FIFO RAM
*/
ert_write(TV_MASTER_CNTL, save->master_cntl | TV_MASTER_CNTL_TV_ON);
RTTRACE(("saveTimingTables: reading timing tables\n"));
for (i = 0; i < MAX_H_CODE_TIMING_LEN; i += 2) {
readFIFO(hTable--, &tmp);
save->h_code_timing[i] = (unsigned short)((tmp >> 14) & 0x3fff);
save->h_code_timing[i + 1] = (unsigned short)(tmp & 0x3fff);
if (save->h_code_timing[i] == 0
|| save->h_code_timing[i + 1] == 0)
break;
}
for (i = 0; i < MAX_V_CODE_TIMING_LEN; i += 2) {
readFIFO(vTable++, &tmp);
save->v_code_timing[i] = (unsigned short)(tmp & 0x3fff);
save->v_code_timing[i + 1] =
(unsigned short)((tmp >> 14) & 0x3fff);
if (save->v_code_timing[i] == 0
|| save->v_code_timing[i + 1] == 0)
break;
}
}
/**********************************************************************
*
* restoreTimingTables
*
* Load horizontal/vertical timing code tables
*
**********************************************************************/
static
void restoreTimingTables(TheaterStatePtr restore)
{
unsigned short hTable;
unsigned short vTable;
unsigned int tmp;
unsigned i;
ert_write(TV_UV_ADR, restore->uv_adr);
getTimingTablesAddr(restore->uv_adr, 1, &hTable, &vTable);
for (i = 0; i < MAX_H_CODE_TIMING_LEN; i += 2, hTable--) {
tmp =
((unsigned int)restore->
h_code_timing[i] << 14) | ((unsigned int)restore->
h_code_timing[i + 1]);
writeFIFO(hTable, tmp);
if (restore->h_code_timing[i] == 0
|| restore->h_code_timing[i + 1] == 0)
break;
}
for (i = 0; i < MAX_V_CODE_TIMING_LEN; i += 2, vTable++) {
tmp =
((unsigned int)restore->
v_code_timing[i +
1] << 14) | ((unsigned int)restore->
v_code_timing[i]);
writeFIFO(vTable, tmp);
if (restore->v_code_timing[i] == 0
|| restore->v_code_timing[i + 1] == 0)
break;
}
}
/**********************************************************************
*
* ERT_RestorePLL
*
* Set ERT PLLs
*
**********************************************************************/
static
void ERT_RestorePLL(TheaterStatePtr restore)
{
OUTPLLP(TV_PLL_CNTL1, 0, ~TV_PLL_CNTL1_TVCLK_SRC_SEL);
OUTPLL(TV_PLL_CNTL, restore->tv_pll_cntl);
OUTPLLP(TV_PLL_CNTL1, TV_PLL_CNTL1_TVPLL_RESET,
~TV_PLL_CNTL1_TVPLL_RESET);
waitPLL_lock(200, 800, 135);
OUTPLLP(TV_PLL_CNTL1, 0, ~TV_PLL_CNTL1_TVPLL_RESET);
waitPLL_lock(300, 160, 27);
waitPLL_lock(200, 800, 135);
OUTPLLP(TV_PLL_CNTL1, 0, ~0xf);
OUTPLLP(TV_PLL_CNTL1, TV_PLL_CNTL1_TVCLK_SRC_SEL,
~TV_PLL_CNTL1_TVCLK_SRC_SEL);
OUTPLLP(TV_PLL_CNTL1, (1 << TV_PLL_CNTL1_TVPDC_SHIFT),
~TV_PLL_CNTL1_TVPDC_MASK);
OUTPLLP(TV_PLL_CNTL1, 0, ~TV_PLL_CNTL1_TVPLL_SLEEP);
}
/**********************************************************************
*
* ERT_RestoreHV
*
* Set ERT horizontal/vertical settings
*
**********************************************************************/
static
void ERT_RestoreHV(TheaterStatePtr restore)
{
ert_write(TV_RGB_CNTL, restore->rgb_cntl);
ert_write(TV_HTOTAL, restore->htotal);
ert_write(TV_HDISP, restore->hdisp);
ert_write(TV_HSTART, restore->hstart);
ert_write(TV_VTOTAL, restore->vtotal);
ert_write(TV_VDISP, restore->vdisp);
ert_write(TV_FTOTAL, restore->vftotal);
ert_write(TV_VSCALER_CNTL1, restore->vscaler_cntl1);
ert_write(TV_VSCALER_CNTL2, restore->vscaler_cntl2);
ert_write(TV_Y_FALL_CNTL, restore->y_fall_cntl);
ert_write(TV_Y_RISE_CNTL, restore->y_rise_cntl);
ert_write(TV_Y_SAWTOOTH_CNTL, restore->y_saw_tooth_cntl);
}
/**********************************************************************
*
* ERT_RestoreRestarts
*
* Set ERT TV_*RESTART registers
*
**********************************************************************/
static
void ERT_RestoreRestarts(TheaterStatePtr restore)
{
ert_write(TV_FRESTART, restore->dfrestart);
ert_write(TV_HRESTART, restore->dhrestart);
ert_write(TV_VRESTART, restore->dvrestart);
}
/**********************************************************************
*
* ERT_RestoreOutputStd
*
* Set tv standard & output muxes
*
**********************************************************************/
static
void ERT_RestoreOutputStd(TheaterStatePtr restore)
{
ert_write(TV_SYNC_CNTL, restore->sync_cntl);
ert_write(TV_TIMING_CNTL, restore->timing_cntl);
ert_write(TV_MODULATOR_CNTL1, restore->modulator_cntl1);
ert_write(TV_MODULATOR_CNTL2, restore->modulator_cntl2);
ert_write(TV_PRE_DAC_MUX_CNTL, restore->pre_dac_mux_cntl);
ert_write(TV_CRC_CNTL, restore->crc_cntl);
}
/**********************************************************************
*
* ERT_IsOn
*
* Test if tv output would be enabled with a given value in TV_DAC_CNTL
*
**********************************************************************/
static Bool ERT_IsOn(unsigned int tv_dac_cntl)
{
if (tv_dac_cntl & TV_DAC_CNTL_BGSLEEP)
return 0;
else if ((tv_dac_cntl &
(TV_DAC_CNTL_RDACPD | TV_DAC_CNTL_GDACPD |
TV_DAC_CNTL_BDACPD)) ==
(TV_DAC_CNTL_RDACPD | TV_DAC_CNTL_GDACPD | TV_DAC_CNTL_BDACPD))
return 0;
else
return 1;
}
/**********************************************************************
*
* ERT_Restore
*
* Restore state of ERT
*
**********************************************************************/
static
void ERT_Restore(TheaterStatePtr restore)
{
RTTRACE(("Entering ERT_Restore\n"));
ert_write(TV_MASTER_CNTL, restore->master_cntl | TV_MASTER_CNTL_TV_ON);
ert_write(TV_MASTER_CNTL,
restore->master_cntl |
VIP_MASTER_CNTL_TV_ASYNC_RST |
VIP_MASTER_CNTL_CRT_ASYNC_RST |
VIP_MASTER_CNTL_RESTART_PHASE_FIX |
VIP_MASTER_CNTL_TV_FIFO_ASYNC_RST);
/*
* Temporarily turn the TV DAC off
*/
ert_write(TV_DAC_CNTL,
(restore->tv_dac_cntl & ~TV_DAC_CNTL_NBLANK) |
TV_DAC_CNTL_BGSLEEP |
TV_DAC_CNTL_RDACPD | TV_DAC_CNTL_GDACPD | TV_DAC_CNTL_BDACPD);
RTTRACE(("ERT_Restore: checkpoint 1\n"));
ERT_RestorePLL(restore);
RTTRACE(("ERT_Restore: checkpoint 2\n"));
ERT_RestoreHV(restore);
ert_write(TV_MASTER_CNTL,
restore->master_cntl |
VIP_MASTER_CNTL_TV_ASYNC_RST |
VIP_MASTER_CNTL_CRT_ASYNC_RST |
VIP_MASTER_CNTL_RESTART_PHASE_FIX);
RTTRACE(("ERT_Restore: checkpoint 3\n"));
ERT_RestoreRestarts(restore);
RTTRACE(("ERT_Restore: checkpoint 4\n"));
/*
* Timing tables are only restored when tv output is active
*/
if (ERT_IsOn(restore->tv_dac_cntl))
restoreTimingTables(restore);
ert_write(TV_MASTER_CNTL,
restore->master_cntl |
VIP_MASTER_CNTL_TV_ASYNC_RST |
VIP_MASTER_CNTL_RESTART_PHASE_FIX);
RTTRACE(("ERT_Restore: checkpoint 5\n"));
ERT_RestoreOutputStd(restore);
ert_write(TV_MASTER_CNTL, restore->master_cntl);
ert_write(RADEON_DISP_MERGE_CNTL, restore->disp_merge_cntl);
ert_write(TV_GAIN_LIMIT_SETTINGS, restore->gain_limit_settings);
ert_write(TV_LINEAR_GAIN_SETTINGS, restore->linear_gain_settings);
ert_write(TV_DAC_CNTL, restore->tv_dac_cntl);
RTTRACE(("Leaving ERT_Restore\n"));
}
/**********************************************************************
*
* computeRestarts
*
* Compute F,V,H restarts from default restart position and
* hPos & vPos
* Return 1 when code timing table was changed
*
**********************************************************************/
static
Bool
computeRestarts(const ModeConstants * constPtr,
TVStd tvStd,
int hPos, int vPos, int hSize, TheaterStatePtr save)
{
int restart;
const TVConstants *pTvStd = &tvStdConsts[tvStd];
unsigned hTotal;
unsigned vTotal;
unsigned fTotal;
int vOffset;
int hOffset;
TimingTableEl p1;
TimingTableEl p2;
Bool hChanged;
unsigned short hInc;
hTotal = constPtr->horTotal;
vTotal = constPtr->verTotal;
fTotal = pTvStd->vftotal + 1;
/*
* Adjust positions 1&2 in hor. code timing table
*/
hOffset = hPos * H_POS_UNIT;
p1 = constPtr->horTimingTable[H_TABLE_POS1];
p2 = constPtr->horTimingTable[H_TABLE_POS2];
p1 = (TimingTableEl) ((int)p1 + hOffset);
p2 = (TimingTableEl) ((int)p2 - hOffset);
hChanged = (p1 != save->h_code_timing[H_TABLE_POS1] ||
p2 != save->h_code_timing[H_TABLE_POS2]);
save->h_code_timing[H_TABLE_POS1] = p1;
save->h_code_timing[H_TABLE_POS2] = p2;
/*
* Convert hOffset from n. of TV clock periods to n. of CRTC clock periods (CRTC pixels)
*/
hOffset = (hOffset * (int)(constPtr->pixToTV)) / 1000;
/*
* Adjust restart
*/
restart = constPtr->defRestart;
/*
* Convert vPos TV lines to n. of CRTC pixels
* Be verrrrry careful when mixing signed & unsigned values in C..
*/
vOffset =
((int)(vTotal * hTotal) * 2 * vPos) / (int)(pTvStd->linesFrame);
restart -= vOffset + hOffset;
RTTRACE(("computeRestarts: def = %u, h = %d , v = %d , p1=%04x , p2=%04x , restart = %d\n", constPtr->defRestart, hPos, vPos, p1, p2, restart));
save->dhrestart = restart % hTotal;
restart /= hTotal;
save->dvrestart = restart % vTotal;
restart /= vTotal;
save->dfrestart = restart % fTotal;
RTTRACE(("computeRestarts: F/H/V=%u,%u,%u\n", save->dfrestart,
save->dvrestart, save->dhrestart));
/*
* Compute H_INC from hSize
*/
hInc =
(unsigned
short)((int)(constPtr->horResolution * 4096 * pTvStd->tvClockT) /
(hSize * (int)(pTvStd->hSizeUnit) +
(int)(pTvStd->zeroHSize)));
save->timing_cntl =
(save->
timing_cntl & ~VIP_TIMING_CNTL_H_INC) | ((unsigned int)hInc <<
VIP_TIMING_CNTL_H_INC_SHIFT);
RTTRACE(("computeRestarts: hSize=%d,hInc=%u\n", hSize, hInc));
return hChanged;
}
/**********************************************************************
*
* RT_Init
*
* Define RT state for a given standard/resolution combination
*
**********************************************************************/
static
void
RT_Init(const ModeConstants * constPtr,
TVStd tvStd,
Bool enable, int hPos, int vPos, int hSize, TheaterStatePtr save)
{
unsigned i;
unsigned int tmp;
const TVConstants *pTvStd = &tvStdConsts[tvStd];
save->clkout_cntl = VIP_CLKOUT_CNTL_INI;
save->clock_sel_cntl = VIP_CLOCK_SEL_CNTL_INI |
(constPtr->crtcPLL_byteClkDiv << VIP_CLOCK_SEL_CNTL_BYTCLK_SHIFT) |
(constPtr->byteClkDelay << VIP_CLOCK_SEL_CNTL_BYTCLKD_SHIFT);
save->crc_cntl = 0;
tmp = ((unsigned int)constPtr->crtcPLL_M << VIP_CRT_PLL_CNTL_M_SHIFT) |
(((unsigned int)constPtr->
crtcPLL_N & VIP_CRT_PLL_CNTL_NLO) << VIP_CRT_PLL_CNTL_NLO_SHIFT) |
(((unsigned int)constPtr->
crtcPLL_N & VIP_CRT_PLL_CNTL_NHI) << VIP_CRT_PLL_CNTL_NHI_SHIFT);
if (constPtr->crtcPLL_divBy2)
tmp |= VIP_CRT_PLL_CNTL_CLKBY2;
save->crt_pll_cntl = tmp;
save->frame_lock_cntl = VIP_FRAME_LOCK_CNTL_INI;
save->gain_limit_settings = VIP_GAIN_LIMIT_SETTINGS_INI;
save->hdisp = constPtr->horResolution - 1;
save->hstart = constPtr->horStart;
save->htotal = constPtr->horTotal - 1;
save->hw_debug = VIP_HW_DEBUG_INI;
save->linear_gain_settings = VIP_LINEAR_GAIN_SETTINGS_INI;
/*
* TEST TEST TEST TEST TEST TEST TEST TEST TEST
*/
save->master_cntl =
enable ? TV_MASTER_CNTL_ON_INI : TV_MASTER_CNTL_OFF_INI;
save->modulator_cntl1 = pTvStd->modulatorCntl1;
save->modulator_cntl2 = pTvStd->modulatorCntl2;
save->pll_cntl0 = VIP_PLL_CNTL0_INI;
save->pll_test_cntl = VIP_PLL_TEST_CNTL_INI;
save->pre_dac_mux_cntl = VIP_PRE_DAC_MUX_CNTL_INI;
save->rgb_cntl = TV_RGB_CNTL_INI;
save->sync_cntl = VIP_SYNC_CNTL_INI;
save->sync_lock_cntl = VIP_SYNC_LOCK_CNTL_INI;
save->sync_size = constPtr->horResolution + 8;
tmp =
(constPtr->
vScalerCntl1 >> VIP_VSCALER_CNTL1_UV_INC_SHIFT) &
VIP_VSCALER_CNTL1_UV_INC;
tmp = ((16384 * 256 * 10) / tmp + 5) / 10;
tmp = (tmp << VIP_TIMING_CNTL_UV_OUT_POST_SCALE_SHIFT) |
VIP_TIMING_CNTL_INI;
save->timing_cntl = tmp;
save->tvo_data_delay_a = constPtr->tvoDataDelayA;
save->tvo_data_delay_b = constPtr->tvoDataDelayB;
save->tvo_sync_pat_expect = VIP_TVO_SYNC_PAT_EXPECT_INI;
if (constPtr->use888RGB)
save->tvo_sync_threshold =
constPtr->horResolution + constPtr->horResolution / 2;
else
save->tvo_sync_threshold = constPtr->horResolution;
if (enable)
save->tv_dac_cntl = pTvStd->ert_tvDAC_Cntl;
else
save->tv_dac_cntl =
(pTvStd->
ert_tvDAC_Cntl & ~(TV_DAC_CNTL_NBLANK | TV_DAC_CNTL_NHOLD))
| (TV_DAC_CNTL_BGSLEEP | TV_DAC_CNTL_RDACPD |
TV_DAC_CNTL_GDACPD | TV_DAC_CNTL_BDACPD);
tmp = ((unsigned int)(pTvStd->tvPLL_M) << VIP_TV_PLL_CNTL_M_SHIFT) |
(((unsigned int)(pTvStd->
tvPLL_N) & VIP_TV_PLL_CNTL_NLO) <<
VIP_TV_PLL_CNTL_NLO_SHIFT) | (((unsigned int)(pTvStd->tvPLL_N) &
VIP_TV_PLL_CNTL_NHI) <<
VIP_TV_PLL_CNTL_NHI_SHIFT) |
((unsigned int)(pTvStd->tvPLL_postDiv) << VIP_TV_PLL_CNTL_P_SHIFT);
save->tv_pll_cntl = tmp;
save->tv_pll_fine_cntl = TV_PLL_FINE_INI;
save->upsamp_and_gain_cntl = VIP_UPSAMP_AND_GAIN_CNTL_INI;
memcpy(&save->upsamp_coeffs[0], upsamplerCoeffs,
sizeof(save->upsamp_coeffs));
save->uv_adr = VIP_UV_ADR_INI;
save->vdisp = constPtr->verResolution - 1;
save->vftotal = pTvStd->vftotal;
save->vscaler_cntl1 = constPtr->vScalerCntl1;
save->vscaler_cntl1 |= TV_VSCALER_CNTL1_RESTART_FIELD;
save->vscaler_cntl2 = VIP_VSCALER_CNTL2_INI;
save->vtotal = constPtr->verTotal - 1;
save->y_fall_cntl = VIP_Y_FALL_CNTL_INI;
save->y_rise_cntl = constPtr->yRiseCntl;
save->y_saw_tooth_cntl = constPtr->ySawtoothCntl;
save->disp_merge_cntl = RADEON_DISP_MERGE_CNTL_INI;
for (i = 0; i < MAX_H_CODE_TIMING_LEN; i++) {
if ((save->h_code_timing[i] = constPtr->horTimingTable[i]) == 0)
break;
}
for (i = 0; i < MAX_V_CODE_TIMING_LEN; i++) {
if ((save->v_code_timing[i] = constPtr->verTimingTable[i]) == 0)
break;
}
/*
* This must be called AFTER loading timing tables as they are modified by this function
*/
computeRestarts(constPtr, tvStd, hPos, vPos, hSize, save);
}
/**********************************************************************
*
* ERTAutoDetect
*
**********************************************************************/
static
Bool ERTAutoDetect(void)
{
unsigned int saveReg = MMINL(TV_LINEAR_GAIN_SETTINGS);
int detected = 0;
/*
* Ultra-dumb way of detecting an ERT: check that a register is present
* @ TV_LINEAR_GAIN_SETTINGS (this is probably one of the most harmless
* register to touch)
*/
MMOUTL(TV_LINEAR_GAIN_SETTINGS, 0x15500aa);
if (MMINL(TV_LINEAR_GAIN_SETTINGS) == 0x15500aa) {
MMOUTL(TV_LINEAR_GAIN_SETTINGS, 0x0aa0155);
if (MMINL(TV_LINEAR_GAIN_SETTINGS) == 0x0aa0155) {
detected = 1;
RTTRACE(("tv out module found!\n"));
}
}
MMOUTL(TV_LINEAR_GAIN_SETTINGS, saveReg);
return detected;
}
void radeon_tvout_init(int tvmode)
{
RTTRACE(("tv out module initing...\n"));
if (!ERTAutoDetect())
return;
RT_Init(&availableModes[tvmode], tvmode, 1, -3, 0, 0, &st);
ERT_Restore(&st);
RTTRACE(("done!\n"));
}