root/src/add-ons/media/media-add-ons/radeon/Theater100.cpp
/******************************************************************************
/
/       File:                   Theater.cpp
/
/       Description:    ATI Rage Theater Video Decoder interface.
/
/       Copyright 2001, Carlos Hasan
/
*******************************************************************************/

#include <Debug.h>
#include "Theater100.h"
#include "Theater.h"
#include "TheatreReg.h"
#include "lendian_bitfield.h"

CTheater100::CTheater100(CRadeon & radeon, int device)
:CTheater(radeon, device)
{
        PRINT(("CTheater100::CTheater100()\n"));
        
        if( fPort.InitCheck() == B_OK ) {
                radeon_video_tuner tuner;
                radeon_video_decoder video;
                
                radeon.GetMMParameters(tuner, video, fClock,
                        fTunerPort, fCompositePort, fSVideoPort);
                
                if (fClock != C_RADEON_VIDEO_CLOCK_29_49892_MHZ &&
                        fClock != C_RADEON_VIDEO_CLOCK_27_00000_MHZ)
                        PRINT(("CTheater100::CTheater100() - Unsupported crystal clock!\n"));

                //fDevice = fPort.FindVIPDevice( C_THEATER100_VIP_DEVICE_ID );
        
        }
        
        if( InitCheck() != B_OK )
                PRINT(("CTheater100::CTheater100() - Rage Theater not found!\n"));
}

CTheater100::~CTheater100()
{
        PRINT(("CTheater100::~CTheater100()\n"));
        
        if( InitCheck() == B_OK )
                SetEnable(false, false);
}

status_t CTheater100::InitCheck() const
{
        status_t res;
        
        res = fPort.InitCheck();
        if( res != B_OK )
                return res;
                
        return (fDevice >= C_VIP_PORT_DEVICE_0 && fDevice <= C_VIP_PORT_DEVICE_3) ? B_OK : B_ERROR;
}

void CTheater100::Reset()
{
        PRINT(("CTheater100::Reset()\n"));
        
        SetHue(0);
        SetBrightness(0);
        SetSaturation(0);
        SetContrast(0);
        SetSharpness(false);
}

// disable/enable capturing
void CTheater100::SetEnable(bool enable, bool vbi)
{
        PRINT(("CTheater100::SetEnable(%d, %d)\n", enable, vbi));

#if 0
        //@ reset ADC?
        SetRegister(VIP_ADC_CNTL, ADC_CPRESET, ADC_CPRESET);
        snooze(1000);
        SetRegister(VIP_ADC_CNTL, ADC_CPRESET, 0);
        snooze(1000);
        SetRegister(VIP_ADC_CNTL, ADC_PDWN, ADC_PDWN_DOWN);
#endif

        
        WaitVSYNC();

        /* Disable the Video In, Scaler and DVS port */
        SetRegister(VIP_MASTER_CNTL, VIN_ASYNC_RST, VIN_ASYNC_RST);
        SetRegister(VIP_MASTER_CNTL, DVS_ASYNC_RST, DVS_ASYNC_RST);

        /* select the reference clock for the Video In */
        SetRegister(VIP_CLOCK_SEL_CNTL, VIN_CLK_SEL, VIN_CLK_SEL_REF_CLK);

        /* reset the VIN/L54 PLL clocks */
        SetRegister(VIP_PLL_CNTL1, VINRST, VINRST);
        SetRegister(VIP_PLL_CNTL1, L54RST, L54RST);

        /* power down the ADC block */
        SetRegister(VIP_ADC_CNTL, ADC_PDWN, ADC_PDWN);

        /* set DVS port to input mode */
        SetRegister(VIP_DVS_PORT_CTRL, DVS_DIRECTION, DVS_DIRECTION_INPUT);

        /* select DVS clock to 8xFsc and disable continuous mode */
        SetRegister(VIP_DVS_PORT_CTRL, DVS_CLK_SELECT, DVS_CLK_SELECT_8X);
        SetRegister(VIP_DVS_PORT_CTRL, CONTINUOUS_STREAM, 0);

        if (enable) {
                WaitVSYNC();

                SetClock(fStandard, fClock);
                SetADC(fStandard, fSource);
                SetLuminanceProcessor(fStandard);
                SetChromaProcessor(fStandard);
                SetVSYNC(fStandard);
                SetClipWindow(fStandard, vbi);
                SetCombFilter(fStandard, fSource);
                SetHSYNC(fStandard);
                SetSyncGenerator(fStandard);
                SetScaler(fStandard, fHActive, fVActive, fDeinterlace);
                
                /* Enable ADC block */
                SetRegister(VIP_ADC_CNTL, ADC_PDWN, ADC_PDWN_UP);

                WaitVSYNC();

                /* Enable the Video In, Scaler and DVS port */
                SetRegister(VIP_MASTER_CNTL, VIN_ASYNC_RST, 0);
                SetRegister(VIP_MASTER_CNTL, DVS_ASYNC_RST, 0);

                /* set DVS port to output mode */
                SetRegister(VIP_DVS_PORT_CTRL, DVS_DIRECTION, DVS_DIRECTION_OUTPUT);

                //WaitHSYNC();

                /* restore luminance and chroma settings */
                SetLuminanceLevels(fStandard, fBrightness, fContrast);
                SetChromaLevels(fStandard, fSaturation, fHue);
        }
}

void CTheater100::SetStandard(theater_standard standard, theater_source source)
{
        PRINT(("CTheater100::SetStandard(%s, %s)\n",
                "NTSC\0\0\0\0\0\0NTSC-J\0\0\0\0NTSC-443\0\0PAL-M\0\0\0\0\0"
                "PAL-N\0\0\0\0\0PAL-NC\0\0\0\0PAL-BDGHI\0PAL-60\0\0\0\0"
                "SECAM\0\0\0\0\0"+10*standard,
                "TUNER\0COMP\0\0SVIDEO"+6*source));
                
        fStandard = standard;
        fSource = source;
}

void CTheater100::SetSize(int hactive, int vactive)
{
        PRINT(("CTheater100::SetSize(%d, %d)\n", hactive, vactive));
        
        fHActive = hactive;
        fVActive = vactive;
}

void CTheater100::SetDeinterlace(bool deinterlace)
{
        PRINT(("CTheater100::SetDeinterlace(%d)\n", deinterlace));
        
        fDeinterlace = deinterlace;
}

void CTheater100::SetSharpness(int sharpness)
{
        PRINT(("CTheater100::SetSharpness(%d)\n", sharpness));
        
        SetRegister(VIP_H_SCALER_CONTROL, H_SHARPNESS, sharpness << 25);
}

void CTheater100::SetBrightness(int brightness)
{
        PRINT(("CTheater100::SetBrightness(%d)\n", brightness));
        
        fBrightness = brightness;
        SetLuminanceLevels(fStandard, fBrightness, fContrast);
}

void CTheater100::SetContrast(int contrast)
{
        PRINT(("CTheater100::SetContrast(%d)\n", contrast));

        fContrast = contrast;
        SetLuminanceLevels(fStandard, fBrightness, fContrast);
}

void CTheater100::SetSaturation(int saturation)
{
        PRINT(("CTheater100::SetSaturation(%d)\n", saturation));

        fSaturation = saturation;
        SetChromaLevels(fStandard, fSaturation, fHue);
}

void CTheater100::SetHue(int hue)
{
        PRINT(("CTheater100::SetHue(%d)\n", hue));

        fHue = hue;
        SetChromaLevels(fStandard, fSaturation, fHue);
}


// set pixel clock
void CTheater100::SetClock(theater_standard standard, radeon_video_clock clock)
{
        // set VIN PLL clock dividers
        int referenceDivider, feedbackDivider, postDivider;
        
        switch (standard) {
        case C_THEATER_NTSC:
        case C_THEATER_NTSC_JAPAN:
                if (clock == C_RADEON_VIDEO_CLOCK_29_49892_MHZ) {
                        referenceDivider = 0x39;
                        feedbackDivider = 0x14c;
                        postDivider = 0x6;
                }
                else {
                        referenceDivider = 0x0b;
                        feedbackDivider = 0x46;
                        postDivider = 0x6;
                }
                break;
        case C_THEATER_NTSC_443:
                if (clock == C_RADEON_VIDEO_CLOCK_29_49892_MHZ) {
                        referenceDivider = 0x23;
                        feedbackDivider = 0x88;
                        postDivider = 0x7;
                }
                else {
                        referenceDivider = 0x2c;
                        feedbackDivider = 0x121;
                        postDivider = 0x5;
                }
                break;
        case C_THEATER_PAL_M:
                if (clock == C_RADEON_VIDEO_CLOCK_29_49892_MHZ) {
                        referenceDivider = 0x2c;
                        feedbackDivider = 0x12b;
                        postDivider = 0x7;
                }
                else {
                        referenceDivider = 0x0b;
                        feedbackDivider = 0x46;
                        postDivider = 0x6;
                }
                break;
        case C_THEATER_PAL_BDGHI:
        case C_THEATER_PAL_N:
        case C_THEATER_PAL_60:
        case C_THEATER_SECAM:
                if (clock == C_RADEON_VIDEO_CLOCK_29_49892_MHZ) {
                        referenceDivider = 0x0e;
                        feedbackDivider = 0x65;
                        postDivider = 0x6;
                }
                else {
                        referenceDivider = 0x2c;
                        feedbackDivider = 0x121;
                        postDivider = 0x5;
                }
                break;
        case C_THEATER_PAL_NC:
                if (clock == C_RADEON_VIDEO_CLOCK_29_49892_MHZ) {
                        referenceDivider = 0x23;
                        feedbackDivider = 0x88;
                        postDivider = 0x7;
                }
                else {
                        referenceDivider = 0x37;
                        feedbackDivider = 0x1d3;
                        postDivider = 0x8;
                }
                break;
        default:
                PRINT(("CTheater100::SetClock() - Bad standard\n"));
                return;
        }

        // reset VIN PLL and select the reference clock
        SetRegister(VIP_CLOCK_SEL_CNTL, VIN_CLK_SEL, VIN_CLK_SEL_REF_CLK);
        SetRegister(VIP_PLL_CNTL1, VINRST, VINRST);
        SetRegister(VIP_PLL_CNTL1, L54RST, L54RST);
        
        // set up the VIN PLL clock control
        SetRegister(VIP_VIN_PLL_CNTL, VIN_M0, referenceDivider << 0);
        SetRegister(VIP_VIN_PLL_CNTL, VIN_N0, feedbackDivider << 11);
        SetRegister(VIP_VIN_PLL_CNTL, VIN_P, postDivider << 24);

        // active the VIN/L54 PLL and attach the VIN PLL to the VIN clock
        SetRegister(VIP_PLL_CNTL1, VINRST, 0);
        SetRegister(VIP_PLL_CNTL1, L54RST, 0);
        SetRegister(VIP_CLOCK_SEL_CNTL, VIN_CLK_SEL, VIN_CLK_SEL_VIPLL_CLK);

        PRINT(("CTheater100::SetClock(Fsamp=%g, Fref=%g)\n",
                ((fClock == C_RADEON_VIDEO_CLOCK_29_49892_MHZ ? 29.49892 : 27.0) * feedbackDivider) / (referenceDivider * postDivider),
                (fClock == C_RADEON_VIDEO_CLOCK_29_49892_MHZ ? 29.49892 : 27.0)));
}


// setup analog-digital converter
void CTheater100::SetADC(theater_standard standard, theater_source source)
{
        PRINT(("CTheater100::SetADC(%c, %c)\n", "NJ4MNCB6S"[standard], "TCS"[source]));
        
        // set HW_DEBUG before setting the standard
        SetRegister(VIP_HW_DEBUG, 0x0000f000);
        
        // select the video standard
        switch (standard) {
        case C_THEATER_NTSC:
        case C_THEATER_NTSC_JAPAN:
        case C_THEATER_NTSC_443:
        case C_THEATER_PAL_M:
                SetRegister(VIP_STANDARD_SELECT, STANDARD_SEL, STANDARD_NTSC);
                break;
        case C_THEATER_PAL_BDGHI:
        case C_THEATER_PAL_N:
        case C_THEATER_PAL_60:
        case C_THEATER_PAL_NC:
                SetRegister(VIP_STANDARD_SELECT, STANDARD_SEL, STANDARD_PAL);
                break;
        case C_THEATER_SECAM:
                SetRegister(VIP_STANDARD_SELECT, STANDARD_SEL, STANDARD_SECAM);
                break;
        default:
                PRINT(("CTheater100::SetADC() - Bad standard\n"));
                return;
        }

        // select input connector and Y/C mode
        switch (source) {
        case C_THEATER_TUNER:
                SetRegister(VIP_ADC_CNTL, INPUT_SELECT, fTunerPort);
                SetRegister(VIP_STANDARD_SELECT, YC_MODE, YC_MODE_COMPOSITE);
                break;
        case C_THEATER_COMPOSITE:
                SetRegister(VIP_ADC_CNTL, INPUT_SELECT, fCompositePort);
                SetRegister(VIP_STANDARD_SELECT, YC_MODE, YC_MODE_COMPOSITE);
                break;
        case C_THEATER_SVIDEO:
                SetRegister(VIP_ADC_CNTL, INPUT_SELECT, fSVideoPort);
                SetRegister(VIP_STANDARD_SELECT, YC_MODE, YC_MODE_SVIDEO);
                break;
        default:
                PRINT(("CTheater100::SetADC() - Bad source\n"));
                return;
        }
        
        SetRegister(VIP_ADC_CNTL, I_CLAMP_SEL, I_CLAMP_SEL_22);
        SetRegister(VIP_ADC_CNTL, I_AGC_SEL, I_AGC_SEL_7);

        SetRegister(VIP_ADC_CNTL, EXT_CLAMP_CAP, EXT_CLAMP_CAP_EXTERNAL);
        SetRegister(VIP_ADC_CNTL, EXT_AGC_CAP, EXT_AGC_CAP_EXTERNAL);
        SetRegister(VIP_ADC_CNTL, ADC_DECI_BYPASS, ADC_DECI_WITH_FILTER);
        SetRegister(VIP_ADC_CNTL, VBI_DECI_BYPASS, VBI_DECI_WITH_FILTER);
        SetRegister(VIP_ADC_CNTL, DECI_DITHER_EN, 0 << 12);
        SetRegister(VIP_ADC_CNTL, ADC_CLK_SEL, ADC_CLK_SEL_8X);
        SetRegister(VIP_ADC_CNTL, ADC_BYPASS, ADC_BYPASS_INTERNAL);
        switch (standard) {
        case C_THEATER_NTSC:
        case C_THEATER_NTSC_JAPAN:
        case C_THEATER_NTSC_443:
        case C_THEATER_PAL_M:
                SetRegister(VIP_ADC_CNTL, ADC_CH_GAIN_SEL, ADC_CH_GAIN_SEL_NTSC);
                break;
        case C_THEATER_PAL_BDGHI:
        case C_THEATER_PAL_N:
        case C_THEATER_PAL_60:
        case C_THEATER_PAL_NC:
        case C_THEATER_SECAM:
                SetRegister(VIP_ADC_CNTL, ADC_CH_GAIN_SEL, ADC_CH_GAIN_SEL_PAL);
                break;
        }
        SetRegister(VIP_ADC_CNTL, ADC_PAICM, 1 << 18);

        SetRegister(VIP_ADC_CNTL, ADC_PDCBIAS, 2 << 20);
        SetRegister(VIP_ADC_CNTL, ADC_PREFHI, ADC_PREFHI_2_7);
        SetRegister(VIP_ADC_CNTL, ADC_PREFLO, ADC_PREFLO_1_5);

        SetRegister(VIP_ADC_CNTL, ADC_IMUXOFF, 0 << 26);
        SetRegister(VIP_ADC_CNTL, ADC_CPRESET, 0 << 27);
}


// setup horizontal sync PLL
void CTheater100::SetHSYNC(theater_standard standard)
{
        static const uint16 hs_line_total[] = {
                0x38E,  0x38E,  0x46F,  0x38D,  0x46F,  0x395,  0x46F,  0x467,  0x46F };
                
        static const uint32 hs_dto_inc[] = {
                0x40000,        0x40000,        0x40000,        0x40000,        0x40000,        0x40000,        0x40000,        0x40000,        0x3E7A2 };

        // TK: completely different in gatos
        static const uint8 hs_pll_sgain[] = {
                2,              2,              2,              2,              2,              2,              2,              2,              2 };
        static const uint8 hs_pll_fgain[] = {
                8,              8,              8,              8,              8,              8,              8,              8,              8 };

        static const uint8 gen_lock_delay[] = {
                0x10,   0x10,   0x10,   0x10,   0x10,   0x10,   0x10,   0x10,   0x10 };
                
        static const uint8 min_pulse_width[] = {
                0x21,   0x21,   0x29,   0x21,   0x29,   0x21,   0x29,   0x29,   0x29 };
        static const uint8 max_pulse_width[] = {
                0x64,   0x64,   0x7D,   0x64,   0x7D,   0x65,   0x7D,   0x7D,   0x7D };
                
        static const uint16 win_close_limit[] = {
                0x0A0,  0x0A0,  0x0C7,  0x0A0,  0x0C7,  0x0A0,  0x0C7,  0x0C7,  0x0C7 };
        static const uint16 win_open_limit[] = {
                0x1B7,  0x1B7,  0x228,  0x1B7,  0x228,  0x1BB,  0x228,  0x224,  0x228 };


        // set number of samples per line
        SetRegister(VIP_HS_PLINE, HS_LINE_TOTAL, hs_line_total[standard]);
        
        SetRegister(VIP_HS_DTOINC, HS_DTO_INC, hs_dto_inc[standard]);
        
        SetRegister(VIP_HS_PLLGAIN, HS_PLL_SGAIN, hs_pll_sgain[standard] << 0);
        SetRegister(VIP_HS_PLLGAIN, HS_PLL_FGAIN, (uint32)hs_pll_fgain[standard] << 4);
        
        SetRegister(VIP_HS_GENLOCKDELAY, GEN_LOCK_DELAY, gen_lock_delay[standard]);

        // set min/max pulse width in samples   
        SetRegister(VIP_HS_MINMAXWIDTH, MIN_PULSE_WIDTH, min_pulse_width[standard] << 0);
        SetRegister(VIP_HS_MINMAXWIDTH, MAX_PULSE_WIDTH, (uint32)max_pulse_width[standard] << 8);

        SetRegister(VIP_HS_WINDOW_LIMIT, WIN_CLOSE_LIMIT, win_close_limit[standard] << 0);
        SetRegister(VIP_HS_WINDOW_LIMIT, WIN_OPEN_LIMIT, (uint32)win_open_limit[standard] << 16);
        

        PRINT(("CTheater100::SetHSYNC(total=%d, pulse=%d/%d, window=%d/%d)\n",
                Register(VIP_HS_PLINE, HS_LINE_TOTAL),
                Register(VIP_HS_MINMAXWIDTH, MIN_PULSE_WIDTH) >> 0,
                Register(VIP_HS_MINMAXWIDTH, MAX_PULSE_WIDTH) >> 8,
                Register(VIP_HS_WINDOW_LIMIT, WIN_CLOSE_LIMIT) >> 0,
                Register(VIP_HS_WINDOW_LIMIT, WIN_OPEN_LIMIT) >> 16));
}


// wait until horizontal scaler is locked
void CTheater100::WaitHSYNC()
{
        for (int timeout = 0; timeout < 1000; timeout++) {
                if (Register(VIP_HS_PULSE_WIDTH, HS_GENLOCKED) != 0)
                        return;
                snooze(20);
        }
        PRINT(("CTheater100::WaitHSYNC() - wait for HSync locking time out!\n"));
}


// setup vertical sync and field detector
void CTheater100::SetVSYNC(theater_standard standard)
{
        static const uint16 vsync_int_trigger[] = {
                0x2AA,  0x2AA,  0x353,  0x2AA,  0x353,  0x2B0,  0x353,  0x34D,  0x353 };
        static const uint16 vsync_int_hold[] = {
                0x017,  0x017,  0x01C,  0x017,  0x01C,  0x017,  0x01C,  0x01C,  0x01C };
        // PAL value changed from 26b to 26d - else, odd/even field detection fails sometimes;
        // did the same for PAL N, PAL NC and SECAM
        static const uint16 vs_field_blank_start[] = {
                0x206,  0x206,  0x206,  0x206,  0x26d,  0x26d,  0x26d,  0x206,  0x26d };
        static const uint8 vs_field_blank_end[] = {
                0x00a,  0x00a,  0x00a,  0x00a,  0x02a,  0x02a,  0x02a,  0x00a,  0x02a };
        // NTSC value changed from 1 to 105 - else, odd/even fields were always swapped;
        // did the same for NTSC Japan, NTSC 443, PAL M and PAL 60
        static const uint16 vs_field_id_location[] = {
                0x105,  0x105,  0x105,  0x105,  0x1,    0x1,    0x1,    0x105,  0x1 };
        static const uint16 vs_frame_total[] = {
                0x217,  0x217,  0x217,  0x217,  0x27B,  0x27B,  0x27B,  0x217,  0x27B };

        SetRegister(VIP_VS_DETECTOR_CNTL, VSYNC_INT_TRIGGER, vsync_int_trigger[standard] << 0);
        SetRegister(VIP_VS_DETECTOR_CNTL, VSYNC_INT_HOLD, (uint32)vsync_int_hold[standard] << 16);

        SetRegister(VIP_VS_BLANKING_CNTL, VS_FIELD_BLANK_START, vs_field_blank_start[standard] << 0);
        SetRegister(VIP_VS_BLANKING_CNTL, VS_FIELD_BLANK_END, (uint32)vs_field_blank_end[standard] << 16);
        SetRegister(VIP_VS_FRAME_TOTAL, VS_FRAME_TOTAL, vs_frame_total[standard]);

        SetRegister(VIP_VS_FIELD_ID_CNTL, VS_FIELD_ID_LOCATION, vs_field_id_location[standard] << 0);

        // auto-detect fields
        SetRegister(VIP_VS_COUNTER_CNTL, FIELD_DETECT_MODE, FIELD_DETECT_DETECTED);
        
        // don't flip fields
        SetRegister(VIP_VS_COUNTER_CNTL, FIELD_FLIP_EN, 0 );

        PRINT(("CTheater100::SetVSYNC(total=%d)\n",
                Register(VIP_VS_FRAME_TOTAL, VS_FRAME_TOTAL)));
}

// wait until a visible line is viewed
void CTheater100::WaitVSYNC()
{
        for (int timeout = 0; timeout < 1000; timeout++) {
                int lineCount = Register(VIP_VS_LINE_COUNT, VS_LINE_COUNT);
                if (lineCount > 1 && lineCount < 20)
                        return;
                snooze(20);
        }
        PRINT(("CTheater100::WaitVSYNC() - wait for VBI timed out!\n"));
}


// setup timing generator
void CTheater100::SetSyncGenerator(theater_standard standard)
{
        static const uint16 blank_int_start[] = {
                0x031,  0x031,  0x046,  0x031,  0x046,  0x046,  0x046,  0x031,  0x046 };
        static const uint8 blank_int_length[] = {
                0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F };
                
        static const uint16 sync_tip_start[] = {
                0x0372, 0x0372, 0x0453, 0x0371, 0x0453, 0x0379, 0x0453, 0x044B, 0x0453 };
        static const uint8 sync_tip_length[] = {
                0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F };

        static const uint8 uv_int_start[] = {
                0x03B,  0x03B,  0x052,  0x03B,  0x052,  0x03B,  0x052,  0x03C,  0x068 };
        static const uint8 u_int_length[] = {
                0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F };
        static const uint8 v_int_length[] = {
                0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F,   0x0F };

        // set blank interrupt position
        SetRegister(VIP_SG_BLACK_GATE, BLANK_INT_START, blank_int_start[standard] );
        SetRegister(VIP_SG_BLACK_GATE, BLANK_INT_LENGTH, (uint32)blank_int_length[standard] << 8);

        SetRegister(VIP_SG_SYNCTIP_GATE, SYNC_TIP_START, sync_tip_start[standard]);
        SetRegister(VIP_SG_SYNCTIP_GATE, SYNC_TIP_LENGTH, (uint32)sync_tip_length[standard] << 12);

        SetRegister(VIP_SG_UVGATE_GATE, UV_INT_START, uv_int_start[standard] << 0);
        
        SetRegister(VIP_SG_UVGATE_GATE, U_INT_LENGTH, (uint32)u_int_length[standard] << 8);
        SetRegister(VIP_SG_UVGATE_GATE, V_INT_LENGTH, (uint32)v_int_length[standard] << 12);

        PRINT(("CTheater100::SetSyncGenerator(black=%d/%d, synctip=%d/%d, uvgate=%d/%d-%d)\n",
                Register(VIP_SG_BLACK_GATE, BLANK_INT_START) >> 0,
                Register(VIP_SG_BLACK_GATE, BLANK_INT_LENGTH) >> 8,
                Register(VIP_SG_SYNCTIP_GATE, SYNC_TIP_START),
                Register(VIP_SG_SYNCTIP_GATE, SYNC_TIP_LENGTH) >> 12,
                Register(VIP_SG_UVGATE_GATE, UV_INT_START),
                Register(VIP_SG_UVGATE_GATE, U_INT_LENGTH) >> 8,
                Register(VIP_SG_UVGATE_GATE, V_INT_LENGTH) >> 12));
}


// setup input comb filter.
// this is really ugly but I cannot find a scheme
void CTheater100::SetCombFilter(theater_standard standard, theater_source source)
{
        enum {
                _3Tap_2D_adaptive_Comb = 1,     // composite
                _2Tap_C_combed_Y_Sub = 2,
                _2Tap_C_combed_Y_combed = 3,
                _3Tap_C_combed_Y_Sub = 4,
                _3Tap_C_combed_Y_combed = 5,
                YC_mode_Comb_filter_off = 6,    // S-Video
                YC_mode_2Tap_YV_filter = 7,
                YC_mode_3Tap_YV_filter = 8
        };

        // make sure to keep bitfield in sync with register definition!
        // we could define each component as an uint8, but this would waste space
        // and would require an extra register-composition 
        typedef struct {
                LBITFIELD32_12 (
                        comb_hck                        : 8, 
                        comb_vck                        : 8, 
                        comb_filter_en          : 1, 
                        comb_adaptiv_en         : 1,
                        comb_bpfmuxsel          : 3,
                        comb_coutsel            : 2,
                        comb_sumdiff0sel        : 1, 
                        comb_sumdiff1sel        : 2,
                        comb_yvlpfsel           : 1, 
                        comb_dlylinesel         : 2, 
                        comb_ydlyinsel          : 2, 
                        comb_ysubbw                     : 1
                );
        } comb_cntl0;
        
        typedef struct {
                LBITFIELD32_7 (
                        comb_ydlyoutsel         : 2,
                        comb_coresize           : 2, 
                        comb_ysuben                     : 1, 
                        comb_youtsel            : 1,
                        comb_syncpfsel          : 2, 
                        comb_synclpfrst         : 1, 
                        comb_debug                      : 1
                );
        } comb_cntl1;

        typedef struct {
                LBITFIELD32_4 (
                        comb_hyk0                       : 8, 
                        comb_vyk0                       : 8, 
                        comb_hyk1                       : 8, 
                        comb_vyk1                       : 8
                );
        } comb_cntl2;

        typedef struct {
                LBITFIELD32_2 (
                        comb_tap0length         : 16,
                        comb_tap1length         : 12
                );
        } comb_line_length;

        typedef struct {
                const uint8                             *types;
                const comb_cntl0                *cntl0;
                const comb_cntl1                *cntl1;
                const comb_cntl2                *cntl2;
                const comb_line_length  *line_length;
        } comb_settings;
        
        static const uint8 comb_types_ntsc_m[] = {
                _3Tap_2D_adaptive_Comb,
                _2Tap_C_combed_Y_Sub,
                _2Tap_C_combed_Y_combed,        
                _3Tap_C_combed_Y_Sub,
                _3Tap_C_combed_Y_combed,        
                YC_mode_Comb_filter_off,
                YC_mode_2Tap_YV_filter, 
                YC_mode_3Tap_YV_filter,
                0
        };

        static const comb_cntl0 comb_cntl0_ntsc_m[] = {
                {       0x90,   0x80,   1,      1,      0,      2,      0,      1,      0,      1,      0,      0 },
                {       0,      0,      1,      0,      3,      2,      0,      0,      0,      1,      0,      0 },
                {       0,      0,      1,      0,      3,      2,      0,      0,      0,      1,      1,      0 },
                {       0,      0,      1,      0,      1,      2,      0,      1,      0,      1,      0,      0 },
                {       0,      0,      1,      0,      1,      2,      0,      1,      1,      1,      0,      0 },
                {       0,      0,      0,      0,      5,      2,      0,      0,      0,      1,      2,      0 },
                {       0,      0,      0,      0,      5,      2,      0,      0,      0,      1,      1,      0 },
                {       0,      0,      0,      0,      5,      2,      0,      0,      1,      1,      0,      0 }
        };
        
        static const comb_cntl1 comb_cntl1_ntsc_m[] = {
                {       0,      0,      1,      0,      0,      0,      0 }, 
                {       2,      0,      1,      0,      0,      0,      0 }, 
                {       3,      0,      0,      0,      0,      0,      0 }, 
                {       0,      0,      1,      0,      1,      0,      0 }, 
                {       3,      0,      0,      0,      1,      0,      0 }, 
                {       1,      0,      0,      0,      2,      0,      0 }, 
                {       3,      0,      0,      0,      0,      0,      0 }, 
                {       3,      0,      0,      0,      1,      0,      0 }
        };
                
        static const comb_cntl2 comb_cntl2_ntsc_m[] = {
                {       0x10,   0x10,   0x16,   0x16 },
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF }
        };
                
        static const comb_line_length comb_line_length_ntsc_m[] = {
                {       0x38A,  0x718 }, 
                {       0x38A,  0x718 }, 
                {       0x38A,  0x718 }, 
                {       0x38A,  0x718 }, 
                {       0x38A,  0x718 }, 
                {       0,              0 }, 
                {       0x38A,  0 }, 
                {       0x38A,  0x718 }
        };


        static const uint8 comb_types_ntsc_433[] = {
                _2Tap_C_combed_Y_Sub,
                _2Tap_C_combed_Y_combed,        
                _3Tap_C_combed_Y_Sub,
                _3Tap_C_combed_Y_combed,        
                YC_mode_Comb_filter_off,
                YC_mode_2Tap_YV_filter, 
                YC_mode_3Tap_YV_filter,
                0
        };
        
        static const comb_cntl0 comb_cntl0_ntsc_433[] = {
                {       0,      0,      1,      0,      3,      2,      0,      0,      0,      1,      0,      0 },
                {       0,      0,      1,      0,      3,      2,      0,      0,      0,      1,      1,      0 },
                {       0,      0,      1,      0,      1,      2,      0,      1,      0,      1,      0,      0 },
                {       0,      0,      1,      0,      1,      2,      0,      1,      1,      1,      0,      0 },
                {       0,      0,      0,      0,      5,      2,      0,      0,      0,      1,      2,      0 },
                {       0,      0,      0,      0,      5,      2,      0,      0,      0,      1,      1,      0 },
                {       0,      0,      0,      0,      5,      2,      0,      0,      1,      1,      0,      0 }
        };
        
        static const comb_cntl1 comb_cntl1_ntsc_433[] = {
                {       2,      0,      1,      0,      0,      0,      0 },
                {       3,      0,      0,      0,      0,      0,      0 },
                {       0,      0,      1,      0,      1,      0,      0 },
                {       3,      0,      0,      0,      1,      0,      0 },
                {       1,      0,      0,      0,      2,      0,      0 },
                {       3,      0,      0,      0,      0,      0,      0 },
                {       3,      0,      0,      0,      1,      0,      0 }
        };
        
        static const comb_cntl2 comb_cntl2_ntsc_433[] = {
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF }
        };
        
        static const comb_line_length comb_line_length_ntsc_433[] = {
                {       0x462,  0x8C9 },
                {       0x462,  0x8C9 },
                {       0x462,  0x8C9 },
                {       0x462,  0x8C9 },
                {       0,              0 },
                {       0x462,  0x8C9 },
                {       0x462,  0x8C9 }
        };
        

        static const uint8 comb_types_pal_m[] = {
                _2Tap_C_combed_Y_Sub,
                YC_mode_2Tap_YV_filter, 
                0
        };
        
        static const comb_cntl0 comb_cntl0_pal_m[] = {
                {       0,      0,      1,      0,      4,      0,      1,      2,      0,      0,      2,      0 },
                {       0,      0,      1,      0,      5,      0,      1,      2,      0,      0,      2,      0 }
        };

        static const comb_cntl1 comb_cntl1_pal_m[] = {
                {       1,      0,      1,      1,      2,      0,      0 },
                {       1,      0,      0,      1,      2,      0,      0 }
        };
        
        static const comb_cntl2 comb_cntl2_pal_m[] = {
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF }
        };

        static const comb_line_length comb_line_length_pal_m[] = {
                {       0x389,  0 },
                {       0x389,  0 }
        };


        static const uint8 comb_types_pal_n[] = {
                _3Tap_2D_adaptive_Comb,
                _2Tap_C_combed_Y_Sub,
                YC_mode_2Tap_YV_filter,
                0
        };

        static const comb_cntl0 comb_cntl0_pal_n[] = {
                {       0x90,   0x80,   1,      1,      0,      2,      0,      1,      0,      1,      0,      0 },
                {       0,      0,      1,      0,      4,      0,      1,      2,      0,      0,      2,      0 },
                {       0,      0,      1,      0,      5,      0,      1,      2,      0,      0,      2,      0 }
        };

        static const comb_cntl1 comb_cntl1_pal_n[] = {
                {       0,      0,      1,      0,      0,      0,      0 },
                {       1,      0,      1,      1,      2,      0,      0 },
                {       1,      0,      0,      1,      2,      0,      0 }
        };
        
        static const comb_cntl2 comb_cntl2_pal_n[] = {
                {       0x10,   0x10,   0x16,   0x16 },
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF }
        };

        static const comb_line_length comb_line_length_pal_n[] = {
                {       0x46B,  0x8DA },
                {       0x46C,  0 },
                {       0x46C,  0 }
        };


        static const uint8 comb_types_pal_nc[] = {
                _3Tap_2D_adaptive_Comb,
                _2Tap_C_combed_Y_Sub,
                YC_mode_2Tap_YV_filter,
                0
        };

        // used to represent an N/A for easier copy'n'paste     
#define X 0

        static const comb_cntl0 comb_cntl0_pal_nc[] = {
                {       0x90,   0x80,   1,      1,      0,      2,      0,      1,      0,      1,      0,      0 },
                {       X,      X,      1,      0,      4,      0,      1,      2,      0,      0,      2,      0 },
                {       X,      X,      1,      0,      5,      0,      1,      2,      X,      0,      2,      0 }
        };
        
        static const comb_cntl1 comb_cntl1_pal_nc[] = {
                {       0,      0,      1,      0,      0,      0,      0 },
                {       1,      0,      1,      1,      2,      0,      0 },
                {       1,      0,      0,      1,      2,      0,      0 }
        };

        static const comb_cntl2 comb_cntl2_pal_nc[] = {
                {       0x10,   0x10,   0x16,   0x16 },
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF }
        };

        static const comb_line_length comb_line_length_pal_nc[] = {
                {       0x391,  0x726 },
                {       0x394,  X },
                {       0x394,  X }
        };
        
        
        static const uint8 comb_types_pal[] = {
                _3Tap_2D_adaptive_Comb,
                _2Tap_C_combed_Y_Sub,
                YC_mode_2Tap_YV_filter,
                0
        };

        static const comb_cntl0 comb_cntl0_pal[] = {
                {       0x90,   0x80,   1,      1,      0,      2,      0,      1,      0,      1,      0,      0 },
                {       0,      0,      1,      0,      4,      0,      1,      2,      0,      0,      2,      0 },
                {       0,      0,      1,      0,      5,      0,      1,      2,      X,      0,      2,      0 }
        };
        
        static const comb_cntl1 comb_cntl1_pal[] = {
                {       0,      0,      1,      0,      0,      0,      0 },
                {       1,      0,      1,      1,      2,      0,      0 },
                {       1,      0,      0,      1,      2,      0,      0 }
        };

        static const comb_cntl2 comb_cntl2_pal[] = {    
                {       2,      1,      8,      6 },
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF }
        };

        static const comb_line_length comb_line_length_pal[] = {
                {       0x46B,  0x8DA },
                {       0x46C,  X },
                {       0x46C,  X }
        };


        static const uint8 comb_types_pal_60[] = {
                _2Tap_C_combed_Y_Sub,
                YC_mode_2Tap_YV_filter,
                0
        };

        static const comb_cntl0 comb_cntl0_pal_60[] = {
                {       0,      0,      1,      0,      4,      0,      1,      2,      0,      0,      2,      0 },
                {       0,      0,      1,      0,      5,      0,      1,      2,      0,      0,      2,      0 }
        };
        
        static const comb_cntl1 comb_cntl1_pal_60[] = {
                {       1,      0,      1,      1,      2,      0,      0 },
                {       1,      0,      0,      1,      2,      0,      0 }
        };

        static const comb_cntl2 comb_cntl2_pal_60[] = { 
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF }
        };

        static const comb_line_length comb_line_length_pal_60[] = {
                {       0x463,  0 },
                {       0x463,  0 }
        };

                
        static const uint8 comb_types_secam[] = {
                _2Tap_C_combed_Y_Sub,           // could be another type, spec is unclear here
                YC_mode_2Tap_YV_filter,
                0,
        };

        static const comb_cntl0 comb_cntl0_secam[] = {
                {       X,      X,      0,      0,      4,      X,      X,      X,      X,      2,      2,      1 },
                {       X,      X,      0,      0,      5,      X,      X,      X,      X,      2,      2,      X }
        };
        
        static const comb_cntl1 comb_cntl1_secam[] = {
                {       1,      0,      1,      0,      2,      0,      0 },
                {       1,      X,      0,      0,      2,      0,      0 }
        };
        
        static const comb_cntl2 comb_cntl2_secam[] = {  
                {       0xFF,   0xFF,   0xFF,   0xFF },
                {       0xFF,   0xFF,   0xFF,   0xFF }
        };
        
        static const comb_line_length comb_line_length_secam[] = {
                {       0x46A,  0 },
                {       0x46A,  0 }
        };

#undef X                
                
        static const comb_settings comb_settings_list[] = {
                { comb_types_ntsc_m,    comb_cntl0_ntsc_m,              comb_cntl1_ntsc_m,              comb_cntl2_ntsc_m,              comb_line_length_ntsc_m },
                { comb_types_ntsc_m,    comb_cntl0_ntsc_m,              comb_cntl1_ntsc_m,              comb_cntl2_ntsc_m,              comb_line_length_ntsc_m },
                { comb_types_ntsc_433,  comb_cntl0_ntsc_433,    comb_cntl1_ntsc_433,    comb_cntl2_ntsc_433,    comb_line_length_ntsc_433 },
                { comb_types_pal_m,             comb_cntl0_pal_m,               comb_cntl1_pal_m,               comb_cntl2_pal_m,               comb_line_length_pal_m },
                { comb_types_pal_n,             comb_cntl0_pal_n,               comb_cntl1_pal_n,               comb_cntl2_pal_n,               comb_line_length_pal_n },
                { comb_types_pal_nc,    comb_cntl0_pal_nc,              comb_cntl1_pal_nc,              comb_cntl2_pal_nc,              comb_line_length_pal_nc },
                { comb_types_pal,               comb_cntl0_pal,                 comb_cntl1_pal,                 comb_cntl2_pal,                 comb_line_length_pal },
                { comb_types_pal_60,    comb_cntl0_pal_60,              comb_cntl1_pal_60,              comb_cntl2_pal_60,              comb_line_length_pal_60 },
                { comb_types_secam,             comb_cntl0_secam,               comb_cntl1_secam,               comb_cntl2_secam,               comb_line_length_secam }
        };

        int min_type, max_type, type;
        const comb_settings *settings;
        int i = 0;

        PRINT(("CTheater100::SetCombFilter(%c, %c)\n", "NJ4MNCB6S"[standard], "TCS"[source]));

        // I don't really understand what the different types mean;
        // what is particularly strange is that many types are defined for few standards only
        if( source == C_THEATER_TUNER || source == C_THEATER_COMPOSITE ) {
                min_type = _3Tap_2D_adaptive_Comb;
                max_type = _3Tap_C_combed_Y_combed;
        } else {
                min_type = YC_mode_Comb_filter_off;
                max_type = YC_mode_3Tap_YV_filter;
        }
        
        settings = &comb_settings_list[standard];
        
        for( type = min_type; type <= max_type; ++type ) {
                for( i = 0; settings->types[i]; ++i ) {
                        if( settings->types[i] == type )
                                break;
                }
                
                if( settings->types[i] != 0 )
                        break;
        }
        
        if( type > max_type ) {
                PRINT(("CTheater100::SetCombFilter() - No settings for this standard and input type combination!!!\n"));
                return;
        }

        SetRegister(VIP_COMB_CNTL0, *(const int32 *)(settings->cntl0 + i));
        SetRegister(VIP_COMB_CNTL1, *(const int32 *)(settings->cntl1 + i));
        SetRegister(VIP_COMB_CNTL2, *(const int32 *)(settings->cntl2 + i));
        SetRegister(VIP_COMB_LINE_LENGTH, *(const int32 *)(settings->line_length + i));
        

        // reset the comb filter
        SetRegister(VIP_COMB_CNTL1, Register(VIP_COMB_CNTL1) ^ COMB_SYNCLPFRST);
        SetRegister(VIP_COMB_CNTL1, Register(VIP_COMB_CNTL1) ^ COMB_SYNCLPFRST);
}


// setup luma processor
void CTheater100::SetLuminanceProcessor(theater_standard standard)
{       
        static const uint16 synctip_ref0[] = {
                0x037,  0x037,  0x037,  0x037,  0x037,  0x037,  0x037,  0x037,  0x037 };
        static const uint16 synctip_ref1[] = {
                0x029,  0x029,  0x029,  0x029,  0x029,  0x026,  0x026,  0x026,  0x026 };
        static const uint16 clamp_ref[] = {
                0x03B,  0x03B,  0x03B,  0x03B,  0x03B,  0x03B,  0x03B,  0x03B,  0x03B };
        static const uint16 agc_peakwhite[] = {
                0x0FF,  0x0FF,  0x0FF,  0x0FF,  0x0FF,  0x0FF,  0x0FF,  0x0FF,  0x0FF };
        static const uint16 vbi_peakwhite[] = {
                0x0D2,  0x0D2,  0xD2,   0x0D2,  0x0D2,  0x0C6,  0x0C6,  0x0C6,  0x0C6 };
                
        static const uint16 wpa_threshold[] = {
                0x406,  0x406,  0x4FC,  0x406,  0x59C,  0x488,  0x59C,  0x59C,  0x57A };
        static const uint16 wpa_trigger_lo[] = {
                0x0B3,  0x0B3,  0x0B3,  0x0B3,  0x096,  0x096,  0x096,  0x0B3,  0x096 };
        static const uint16 wpa_trigger_hi[] = {
                0x21B,  0x21B,  0x21B,  0x21B,  0x1C2,  0x1C2,  0x1C2,  0x21B,  0x1C2 };
        static const uint16 lp_lockout_start[] = {
                0x206,  0x206,  0x206,  0x206,  0x263,  0x263,  0x263,  0x206,  0x263 };
        // PAL: changed 0x2c to 0x0c; NTSC: changed 0x21 to 0x0b
        static const uint16 lp_lockout_end[] = {
                0x00B,  0x00B,  0x00B,  0x00B,  0x00C,  0x00C,  0x00C,  0x00B,  0x00C };

        PRINT(("CTheater100::SetLuminanceProcessor(%c)\n", "NJ4MNCB6S"[standard]));

        SetRegister(VIP_LP_AGC_CLAMP_CNTL0, SYNCTIP_REF0, synctip_ref0[standard] << 0);
        SetRegister(VIP_LP_AGC_CLAMP_CNTL0, SYNCTIP_REF1, (uint32)synctip_ref1[standard] << 8);
        SetRegister(VIP_LP_AGC_CLAMP_CNTL0, CLAMP_REF, (uint32)clamp_ref[standard] << 16);
        SetRegister(VIP_LP_AGC_CLAMP_CNTL0, AGC_PEAKWHITE, (uint32)agc_peakwhite[standard] << 24);
        SetRegister(VIP_LP_AGC_CLAMP_CNTL1, VBI_PEAKWHITE, (uint32)vbi_peakwhite[standard] << 0);
        
        SetRegister(VIP_LP_WPA_CNTL0, WPA_THRESHOLD, wpa_threshold[standard] << 0);
        SetRegister(VIP_LP_WPA_CNTL1, WPA_TRIGGER_LO, wpa_trigger_lo[standard] << 0);
        SetRegister(VIP_LP_WPA_CNTL1, WPA_TRIGGER_HI, (uint32)wpa_trigger_hi[standard] << 16);
        SetRegister(VIP_LP_VERT_LOCKOUT, LP_LOCKOUT_START, lp_lockout_start[standard] << 0);
        SetRegister(VIP_LP_VERT_LOCKOUT, LP_LOCKOUT_END, (uint32)lp_lockout_end[standard] << 16);
}


// setup brightness and contrast
void CTheater100::SetLuminanceLevels(theater_standard standard, int brightness, int contrast)
{
        double ref0, setup, gain;

        ref0 = Register(VIP_LP_AGC_CLAMP_CNTL0, SYNCTIP_REF0);

        switch (standard) {
        case C_THEATER_NTSC:
        case C_THEATER_PAL_M:
        case C_THEATER_NTSC_443:
                setup = 7.5 * ref0 / 40.0;
                gain = 219.0 / (92.5 * ref0 / 40.0);
                break;

        case C_THEATER_NTSC_JAPAN:
                setup = 0.0;
                gain = 219.0 / (100.0 * ref0 / 40.0);
                break;

        case C_THEATER_PAL_BDGHI:
        case C_THEATER_PAL_N:
        case C_THEATER_SECAM:
        case C_THEATER_PAL_60:
        case C_THEATER_PAL_NC:
                setup = 0.0;
                gain = 219.0 / (100.0 * ref0 / 43.0);
                break;
                
        default:
                setup = 0.0;
                gain = 0.0;
                break;
        }

        if (contrast <= -100)
                contrast = -99;

        /* set luminance processor constrast (7:0) */
        SetRegister(VIP_LP_CONTRAST, CONTRAST,
                int(64.0 * ((contrast + 100) / 100.0) * gain) << 0);

        /* set luminance processor brightness (13:0) */
        SetRegister(VIP_LP_BRIGHTNESS, BRIGHTNESS,
                int(16.0 * ((brightness - setup) + 16.0 / ((contrast + 100) * gain / 100.0))) & BRIGHTNESS);
}


// setup chroma demodulator
void CTheater100::SetChromaProcessor(theater_standard standard)
{
        PRINT(("CTheater100::SetChromaProcessor(%c)\n", "NJ4MNCB6S"[standard]));
        
        static const uint32 ch_dto_inc[] = {
                0x400000,       0x400000,       0x400000,       0x400000,       0x400000,       0x400000,       0x400000,       0x400000,       0x3E7A28 };
        static const uint8 ch_pll_sgain[] = {
                1,              1,              1,              1,              1,              1,              1,              1,              5 };
        static const uint8 ch_pll_fgain[] = {
                2,              2,              2,              2,              2,              2,              2,              2,              6 };
                
        static const uint8 ch_height[] = {
                0xCD,   0xCD,   0xCD,   0x91,   0x91,   0x9C,   0x9C,   0x9C,   0x66 };
        static const uint8 ch_kill_level[] = {
                0x0C0,  0xC0,   0xC0,   0x8C,   0x8C,   0x90,   0x90,   0x90,   0x60 };
        static const uint8 ch_agc_error_lim[] = {
                2,              2,              2,              2,              2,              2,              2,              2,              3 };
        static const uint8 ch_agc_filter_en[] = {
                0,              0,              0,              0,              0,              0,              1,              0,              0 };
        static const uint8 ch_agc_loop_speed[] = {
                0,              0,              0,              0,              0,              0,              0,              0,              0 };
                
        static const uint16 cr_burst_gain[] = {
                0x7A,   0x71,   0x7A,   0x7A,   0x7A,   0x7A,   0x7A,   0x7A,   0x1FF };
        static const uint16 cb_burst_gain[] = {
                0xAC,   0x9F,   0xAC,   0xAC,   0xAC,   0xAB,   0xAB,   0xAB,   0x1FF };
        static const uint16 crdr_active_gain[] = {
                0x7A,   0x71,   0x7A,   0x7A,   0x7A,   0x7A,   0x7A,   0x7A,   0x11C };
        static const uint16 cbdb_active_gain[] = {
                0xAC,   0x9F,   0xAC,   0xAC,   0xAC,   0xAB,   0xAB,   0xAB,   0x15A };
        static const uint16 cp_vert_lockout_start[] = {
                0x207,  0x207,  0x207,  0x207,  0x269,  0x269,  0x269,  0x207,  0x269 };
        static const uint8 cp_vert_lockout_end[] = {
                0x00E,  0x00E,  0x00E,  0x00E,  0x00E,  0x012,  0x012,  0x00E,  0x012 };

        SetRegister(VIP_CP_PLL_CNTL0, CH_DTO_INC, ch_dto_inc[standard] << 0);
        SetRegister(VIP_CP_PLL_CNTL0, CH_PLL_SGAIN, (uint32)ch_pll_sgain[standard] << 24);
        SetRegister(VIP_CP_PLL_CNTL0, CH_PLL_FGAIN, (uint32)ch_pll_fgain[standard] << 28);

        SetRegister(VIP_CP_AGC_CNTL, CH_HEIGHT, ch_height[standard] << 0);      
        SetRegister(VIP_CP_AGC_CNTL, CH_KILL_LEVEL, (uint32)ch_kill_level[standard] << 8);
        SetRegister(VIP_CP_AGC_CNTL, CH_AGC_ERROR_LIM, (uint32)ch_agc_error_lim[standard] << 16);
        SetRegister(VIP_CP_AGC_CNTL, CH_AGC_FILTER_EN, (uint32)ch_agc_filter_en[standard] << 18);
        SetRegister(VIP_CP_AGC_CNTL, CH_AGC_LOOP_SPEED, (uint32)ch_agc_loop_speed[standard] << 19);

        SetRegister(VIP_CP_BURST_GAIN, CR_BURST_GAIN, cr_burst_gain[standard] << 0);
        SetRegister(VIP_CP_BURST_GAIN, CB_BURST_GAIN, (uint32)cb_burst_gain[standard] << 16);
        
        SetRegister(VIP_CP_ACTIVE_GAIN, CRDR_ACTIVE_GAIN, crdr_active_gain[standard] << 0);
        SetRegister(VIP_CP_ACTIVE_GAIN, CBDB_ACTIVE_GAIN, (uint32)cbdb_active_gain[standard] << 16);

        SetRegister(VIP_CP_VERT_LOCKOUT, CP_LOCKOUT_START, cp_vert_lockout_start[standard] << 0);
        SetRegister(VIP_CP_VERT_LOCKOUT, CP_LOCKOUT_END, (uint32)cp_vert_lockout_end[standard] << 16);
}


// set colour saturation and hue.
// hue makes sense for NTSC only and seems to act as saturation for PAL
void CTheater100::SetChromaLevels(theater_standard standard, int saturation, int hue)
{
        int ref0;
        double gain, CRgain, CBgain;

        /* compute Cr/Cb gains */
        ref0 = Register(VIP_LP_AGC_CLAMP_CNTL0, SYNCTIP_REF0);

        switch (standard) {
        case C_THEATER_NTSC:
        case C_THEATER_NTSC_443:
        case C_THEATER_PAL_M:
                CRgain = (40.0 / ref0) * (100.0 / 92.5) * (1.0 / 0.877) * (112.0 / 70.1) / 1.5;
                CBgain = (40.0 / ref0) * (100.0 / 92.5) * (1.0 / 0.492) * (112.0 / 88.6) / 1.5;
                break;

        case C_THEATER_NTSC_JAPAN:
                CRgain = (40.0 / ref0) * (100.0 / 100.0) * (1.0 / 0.877) * (112.0 / 70.1) / 1.5;
                CBgain = (40.0 / ref0) * (100.0 / 100.0) * (1.0 / 0.492) * (112.0 / 88.6) / 1.5;
                break;

        case C_THEATER_PAL_BDGHI:
        case C_THEATER_PAL_60:
        case C_THEATER_PAL_NC:
        case C_THEATER_PAL_N:
                CRgain = (43.0 / ref0) * (100.0 / 92.5) * (1.0 / 0.877) * (112.0 / 70.1) / 1.5;
                CBgain = (43.0 / ref0) * (100.0 / 92.5) * (1.0 / 0.492) * (112.0 / 88.6) / 1.5;
                break;

        case C_THEATER_SECAM:
                CRgain = 32.0 * 32768.0 / 280000.0 / (33554432.0 / 35.46985) * (1.597 / 1.902) / 1.5;
                CBgain = 32.0 * 32768.0 / 230000.0 / (33554432.0 / 35.46985) * (1.267 / 1.505) / 1.5;
                break;
        
        default:
                PRINT(("CTheater100::SetChromaLevels() - Bad standard\n"));
                CRgain = 0.0;
                CBgain = 0.0;
                break;
        }
        
        if (saturation >= 0)
                gain = 1.0 + 4.9 * saturation / 100.0;
        else
                gain = 1.0 + saturation / 100.0;
                
        SetRegister(VIP_CP_ACTIVE_GAIN, CRDR_ACTIVE_GAIN, int(128 * CRgain * gain) << 0);
        SetRegister(VIP_CP_ACTIVE_GAIN, CBDB_ACTIVE_GAIN, int(128 * CBgain * gain) << 16);

        if (hue >= 0)
                hue = (256 * hue) / 360;
        else
                hue = (256 * (hue + 360)) / 360;

        SetRegister(VIP_CP_HUE_CNTL, HUE_ADJ, hue << 0);
}


// these values are used by scaler as well
static const uint16 h_active_start[] = {
        0x06b,  0x06B,  0x07E,  0x067,  0x09A,  0x07D,  0x09A,  0x084,  0x095 };
static const uint16 h_active_end[] = {
        0x363,  0x363,  0x42A,  0x363,  0x439,  0x439,  0x439,  0x363,  0x439 };
static const uint16 v_active_start[] = {
        0x025,  0x025,  0x025,  0x025,  0x02E,  0x02E,  0x02E,  0x025,  0x02E };
// PAL height is too small (572 instead of 576 lines), but changing 0x269 to 0x26d
// leads to trouble, and the last 2 lines seem to be used for VBI data 
// (read: garbage) anyway
static const uint16 v_active_end[] = {
        0x204,  0x204,  0x204,  0x204,  0x269,  0x269,  0x269,  0x204,  0x269 };
static const uint16 h_vbi_wind_start[] = {
        0x064,  0x064,  0x064,  0x064,  0x084,  0x084,  0x084,  0x064,  0x084 };
static const uint16 h_vbi_wind_end[] = {
        0x366,  0x366,  0x366,  0x366,  0x41F,  0x41F,  0x41F,  0x366,  0x41F };
static const uint16 v_vbi_wind_start[] = {
        0x00b,  0x00b,  0x00b,  0x00b,  0x008,  0x008,  0x008,  0x00b,  0x008 };
static const uint16 v_vbi_wind_end[] = {
        0x024,  0x024,  0x024,  0x024,  0x02d,  0x02d,  0x02d,  0x024,  0x02d };
        
void CTheater100::getActiveRange( theater_standard standard, CRadeonRect &rect )
{
        rect.SetTo( 
                h_active_start[standard], v_active_start[standard],
                h_active_end[standard], v_active_end[standard] );
}

void CTheater100::getVBIRange( theater_standard standard, CRadeonRect &rect )
{
        rect.SetTo( 
                h_vbi_wind_start[standard], v_vbi_wind_start[standard],
                h_vbi_wind_end[standard], v_vbi_wind_end[standard] );
}

// program clipping engine
void CTheater100::SetClipWindow(theater_standard standard, bool vbi)
{               
        // set horizontal active window
        SetRegister(VIP_H_ACTIVE_WINDOW, H_ACTIVE_START, h_active_start[standard] << 0);
        SetRegister(VIP_H_ACTIVE_WINDOW, H_ACTIVE_END, (uint32)h_active_end[standard] << 16);
        
        // set vertical active window
        SetRegister(VIP_V_ACTIVE_WINDOW, V_ACTIVE_START, v_active_start[standard] << 0);
        SetRegister(VIP_V_ACTIVE_WINDOW, V_ACTIVE_END, (uint32)v_active_end[standard] << 16);
        
        // set horizontal VBI window
        SetRegister(VIP_H_VBI_WINDOW, H_VBI_WIND_START, h_vbi_wind_start[standard] << 0);
        SetRegister(VIP_H_VBI_WINDOW, H_VBI_WIND_END, (uint32)h_vbi_wind_end[standard] << 16);
        
        // set vertical VBI window
        SetRegister(VIP_V_VBI_WINDOW, V_VBI_WIND_START, v_vbi_wind_start[standard] << 0);
        SetRegister(VIP_V_VBI_WINDOW, V_VBI_WIND_END, (uint32)v_vbi_wind_end[standard]  << 16);

        // set VBI scaler control
        SetRegister(VIP_VBI_SCALER_CONTROL, (1 << 16) & VBI_SCALING_RATIO);
        
        // enable/disable VBI capture
        SetRegister(VIP_VBI_CONTROL, VBI_CAPTURE_ENABLE,
                vbi ? VBI_CAPTURE_EN : VBI_CAPTURE_DIS);
        
        PRINT(("CTheater100::SetClipWindow(active=%d/%d/%d/%d, vbi=%d/%d/%d/%d)\n",
                Register(VIP_H_ACTIVE_WINDOW, H_ACTIVE_START) >> 0,
                Register(VIP_H_ACTIVE_WINDOW, H_ACTIVE_END) >> 16,
                Register(VIP_V_ACTIVE_WINDOW, V_ACTIVE_START) >> 0,
                Register(VIP_V_ACTIVE_WINDOW, V_ACTIVE_END) >> 16,
                Register(VIP_H_VBI_WINDOW, H_VBI_WIND_START) >> 0,
                Register(VIP_H_VBI_WINDOW, H_VBI_WIND_END) >> 16,
                Register(VIP_V_VBI_WINDOW, V_VBI_WIND_START) >> 0,
                Register(VIP_V_VBI_WINDOW, V_VBI_WIND_END) >> 16));
                
}


// setup capture scaler.
void CTheater100::SetScaler(theater_standard standard, int hactive, int vactive, bool deinterlace)
{
        int oddOffset, evenOffset;
        uint16 h_active_width, v_active_height;
        
//      ASSERT(vactive <= 511);

        // TK: Gatos uses different values here
        h_active_width = h_active_end[standard] - h_active_start[standard] + 1;
        v_active_height = v_active_end[standard] - v_active_start[standard] + 1;
        
        // for PAL, we have 572 lines only, but need 576 lines;
        // my attempts to find those missing lines all failed, so if the application requests
        // 576 lines, we had to upscale the video which is not supported by hardware;
        // solution: restrict to 572 lines - the scaler will fill out the missing lines with black
        if( vactive > v_active_height )
                vactive = v_active_height;
        
        if (deinterlace) {
                // progressive scan
                evenOffset = oddOffset = 512 - (int) ((512 * vactive) / v_active_height);
        }
        else {
                // interlaced
                evenOffset = (int) ((512 * vactive) / v_active_height);
                oddOffset = 2048 - evenOffset;
        }

        // set scale input window
        SetRegister(VIP_SCALER_IN_WINDOW, H_IN_WIND_START, h_active_start[standard] << 0);
        SetRegister(VIP_SCALER_IN_WINDOW, V_IN_WIND_START, (uint32)v_active_start[standard] << 16);

        SetRegister(VIP_SCALER_OUT_WINDOW, H_OUT_WIND_WIDTH, hactive << 0);
        SetRegister(VIP_SCALER_OUT_WINDOW, V_OUT_WIND_HEIGHT, (vactive / 2) << 16);

        SetRegister(VIP_H_SCALER_CONTROL, H_SCALE_RATIO, (((uint32)h_active_width << 16) / hactive) << 0);
        SetRegister(VIP_V_SCALER_CONTROL, V_SCALE_RATIO, ((vactive << 11) / v_active_height) << 0);

        // enable horizontal and vertical scaler
        SetRegister(VIP_H_SCALER_CONTROL, H_BYPASS, 
                h_active_width == hactive ? H_BYPASS : 0);
        SetRegister(VIP_V_SCALER_CONTROL, V_BYPASS, 
                v_active_height == vactive ? V_BYPASS : 0);

        // set deinterlace control
        SetRegister(VIP_V_SCALER_CONTROL, V_DEINTERLACE_ON, deinterlace ? V_DEINTERLACE_ON : 0);
        SetRegister(VIP_V_DEINTERLACE_CONTROL, EVENF_OFFSET, evenOffset << 0);
        SetRegister(VIP_V_DEINTERLACE_CONTROL, ODDF_OFFSET, oddOffset << 11);

        SetRegister(VIP_V_SCALER_CONTROL, V_DEINTERLACE_ON, deinterlace ? V_DEINTERLACE_ON : 0);
        
        PRINT(("CTheater100::SetScaler(active=%d/%d/%d/%d, scale=%d/%d)\n",
                Register(VIP_SCALER_IN_WINDOW, H_IN_WIND_START) >> 0,
                Register(VIP_SCALER_IN_WINDOW, V_IN_WIND_START) >> 16,
                hactive, vactive,
                Register(VIP_H_SCALER_CONTROL, H_SCALE_RATIO),
                Register(VIP_V_SCALER_CONTROL, V_SCALE_RATIO)));
}

int CTheater100::CurrentLine()
{
        return Register(VIP_VS_LINE_COUNT) & VS_LINE_COUNT;
}

void CTheater100::PrintToStream()
{
        PRINT(("<<< Rage Theater Registers >>>\n"));
        for (int index = 0x0400; index <= 0x06ff; index += 4) {
                int value = Register(index);
                value = value; // unused var if debug is off
                PRINT(("REG_0x%04x = 0x%08x\n", index, value));
        }       
}