From ffec4cb14d0b87745137bcb115e949f1fbc895b9 Mon Sep 17 00:00:00 2001 From: shadow303 Date: Fri, 16 Jul 2004 00:49:57 +0000 Subject: [PATCH] Add video in support included with version 4.1 of the radeon driver. Feel free to relocate if I have guessed the wrong destination for this stuff. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@8407 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/media/media-add-ons/Jamfile | 1 + src/add-ons/media/media-add-ons/radeon/CC.cpp | 828 +++++++ src/add-ons/media/media-add-ons/radeon/CC.h | 67 + .../media/media-add-ons/radeon/Capture.cpp | 346 +++ .../media/media-add-ons/radeon/Capture.h | 89 + .../media/media-add-ons/radeon/I2CPort.cpp | 234 ++ .../media/media-add-ons/radeon/I2CPort.h | 63 + .../media/media-add-ons/radeon/MSP3430.cpp | 878 ++++++++ .../media/media-add-ons/radeon/MSP3430.h | 40 + .../media/media-add-ons/radeon/Radeon.cpp | 899 ++++++++ .../media/media-add-ons/radeon/Radeon.h | 405 ++++ .../media-add-ons/radeon/RadeonAddOn.cpp | 432 ++++ .../media/media-add-ons/radeon/RadeonAddOn.h | 99 + .../media-add-ons/radeon/RadeonProducer.cpp | 1964 +++++++++++++++++ .../media-add-ons/radeon/RadeonProducer.h | 241 ++ .../media/media-add-ons/radeon/Theater.cpp | 1363 ++++++++++++ .../media/media-add-ons/radeon/Theater.h | 135 ++ .../media/media-add-ons/radeon/TheatreReg.h | 1235 +++++++++++ .../media/media-add-ons/radeon/Tuner.cpp | 323 +++ .../media/media-add-ons/radeon/Tuner.h | 70 + .../media/media-add-ons/radeon/VIPPort.cpp | 33 + .../media/media-add-ons/radeon/VIPPort.h | 69 + .../media/media-add-ons/radeon/VideoIn.cpp | 453 ++++ .../media/media-add-ons/radeon/VideoIn.h | 144 ++ .../media-add-ons/radeon/yuv_converter.h | 177 ++ 25 files changed, 10588 insertions(+) create mode 100644 src/add-ons/media/media-add-ons/radeon/CC.cpp create mode 100644 src/add-ons/media/media-add-ons/radeon/CC.h create mode 100644 src/add-ons/media/media-add-ons/radeon/Capture.cpp create mode 100644 src/add-ons/media/media-add-ons/radeon/Capture.h create mode 100644 src/add-ons/media/media-add-ons/radeon/I2CPort.cpp create mode 100644 src/add-ons/media/media-add-ons/radeon/I2CPort.h create mode 100644 src/add-ons/media/media-add-ons/radeon/MSP3430.cpp create mode 100644 src/add-ons/media/media-add-ons/radeon/MSP3430.h create mode 100644 src/add-ons/media/media-add-ons/radeon/Radeon.cpp create mode 100644 src/add-ons/media/media-add-ons/radeon/Radeon.h create mode 100644 src/add-ons/media/media-add-ons/radeon/RadeonAddOn.cpp create mode 100644 src/add-ons/media/media-add-ons/radeon/RadeonAddOn.h create mode 100644 src/add-ons/media/media-add-ons/radeon/RadeonProducer.cpp create mode 100644 src/add-ons/media/media-add-ons/radeon/RadeonProducer.h create mode 100644 src/add-ons/media/media-add-ons/radeon/Theater.cpp create mode 100644 src/add-ons/media/media-add-ons/radeon/Theater.h create mode 100644 src/add-ons/media/media-add-ons/radeon/TheatreReg.h create mode 100644 src/add-ons/media/media-add-ons/radeon/Tuner.cpp create mode 100644 src/add-ons/media/media-add-ons/radeon/Tuner.h create mode 100644 src/add-ons/media/media-add-ons/radeon/VIPPort.cpp create mode 100644 src/add-ons/media/media-add-ons/radeon/VIPPort.h create mode 100644 src/add-ons/media/media-add-ons/radeon/VideoIn.cpp create mode 100644 src/add-ons/media/media-add-ons/radeon/VideoIn.h create mode 100644 src/add-ons/media/media-add-ons/radeon/yuv_converter.h diff --git a/src/add-ons/media/media-add-ons/Jamfile b/src/add-ons/media/media-add-ons/Jamfile index b2ec150838..741d4bb715 100644 --- a/src/add-ons/media/media-add-ons/Jamfile +++ b/src/add-ons/media/media-add-ons/Jamfile @@ -6,6 +6,7 @@ SubDir OBOS_TOP src add-ons media media-add-ons ; SubInclude OBOS_TOP src add-ons media media-add-ons legacy ; SubInclude OBOS_TOP src add-ons media media-add-ons mixer ; SubInclude OBOS_TOP src add-ons media media-add-ons multi_audio ; +SubInclude OBOS_TOP src add-ons media media-add-ons radeon ; SubInclude OBOS_TOP src add-ons media media-add-ons tone_producer_demo ; SubInclude OBOS_TOP src add-ons media media-add-ons video_producer_demo ; diff --git a/src/add-ons/media/media-add-ons/radeon/CC.cpp b/src/add-ons/media/media-add-ons/radeon/CC.cpp new file mode 100644 index 0000000000..4a4f5d0366 --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/CC.cpp @@ -0,0 +1,828 @@ +/****************************************************************************** +/ +/ File: CC.cpp +/ +/ Description: Closed caption module. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#include +#include +#include "CC.h" + +CCaption::CCaption(CCapture & capture) + : fCapture(capture), + fChannel(C_RADEON_CC1), + fRow(0), + fColumn(0), + fColor(0), + fLastControlCode(0) +{ + for (int row = 0; row < C_RADEON_CC_ROWS; row++) { + for (int column = 0; column < C_RADEON_CC_COLUMNS; column++) { + fText[row][column] = fDisplayText[row][column] = 0x0000; + } + } +} + +CCaption::~CCaption() +{ +} + +status_t CCaption::InitCheck() const +{ + return fCapture.InitCheck(); +} + +void CCaption::SetMode(cc_mode mode) +{ + fChannel = mode; + fLastControlCode = 0; +} + +bool CCaption::DecodeBits(const unsigned char * buffer, int & data) +{ + // assume the buffer is long enough (at least VBI line width bytes) + if (buffer == NULL) + return false; + + // compute low/high levels + int low = 0xff, high = 0x00; + for (int offset = C_RADEON_CC_BLANK_START; offset < C_RADEON_CC_BLANK_START + 4 * C_RADEON_CC_BIT_DURATION; offset++) { + if (low > buffer[offset]) + low = buffer[offset]; + if (high < buffer[offset]) + high = buffer[offset]; + } + if (low + C_RADEON_CC_LEVEL_THRESHOLD >= high) + return false; + + const int middle = (low + high) >> 1; + + // find position of the first pulse + int start = C_RADEON_CC_BLANK_START + C_RADEON_CC_BIT_DURATION; + while (start <= C_RADEON_CC_BLANK_START + 4 * C_RADEON_CC_BIT_DURATION) { + if (buffer[start + 0] < middle && buffer[start + 1] < middle && + buffer[start + 3] > middle && buffer[start + 4] > middle) + break; + start++; + } + if (start >= C_RADEON_CC_BLANK_START + 4 * C_RADEON_CC_BIT_DURATION) + return false; + + // compute position of the last pulse + int end = start + 17 * C_RADEON_CC_BIT_DURATION; + + // start in middle of first pulse + int bit = 0; + bool one = true; + for (int offset = start + C_RADEON_CC_BIT_DURATION / 2; offset < end; offset++) { + int width = 1; + + // search the next pulse front + while (offset < end) { + if (one) { + if (buffer[offset + 0] > middle && buffer[offset + 1] > middle && + buffer[offset + 3] < middle && buffer[offset + 4] < middle) + break; + } + else { + if (buffer[offset + 0] < middle && buffer[offset + 1] < middle && + buffer[offset + 3] > middle && buffer[offset + 4] > middle) + break; + } + offset++; + width++; + } + + // compute pulse width in bits + const int nbits = (width + (bit == 0 ? 0 : bit == 15 ? C_RADEON_CC_BIT_DURATION : + C_RADEON_CC_BIT_DURATION / 2)) / C_RADEON_CC_BIT_DURATION; + data >>= nbits; + if (one) + data |= (0xffff << (16 - nbits)) & 0xffff; + + if ((bit += nbits) >= C_RADEON_CC_BITS_PER_FIELD) + break; + + one = !one; + } + + if (bit != C_RADEON_CC_BITS_PER_FIELD) + return false; + + return true; +} + +bool CCaption::Decode(const unsigned char * buffer0, + const unsigned char * buffer1) +{ + enum caption_code { + /* channel */ + C_CC1 = 0x0000, + C_CC2 = 0x0800, + + /* address */ + C_ROW_1 = 0x0100, + C_ROW_2 = 0x0120, + C_ROW_3 = 0x0200, + C_ROW_4 = 0x0220, + C_ROW_5 = 0x0500, + C_ROW_6 = 0x0520, + C_ROW_7 = 0x0600, + C_ROW_8 = 0x0620, + C_ROW_9 = 0x0700, + C_ROW_10 = 0x0720, + C_ROW_11 = 0x0000, + C_ROW_12 = 0x0300, + C_ROW_13 = 0x0320, + C_ROW_14 = 0x0400, + C_ROW_15 = 0x0420, + + /* color */ + C_WHITE = 0x0000, + C_GREEN = 0x0002, + C_BLUE = 0x0004, + C_CYAN = 0x0006, + C_RED = 0x0008, + C_YELLOW = 0x000a, + C_MAGENTA = 0x000c, + C_ITALICS = 0x000e, + + /* underline */ + C_NORMAL = 0x0000, + C_UNDERLINE = 0x0001, + + /* control */ + C_RCL = 0x0000, // resume caption loading + C_BS = 0x0001, // backspace + C_AOF = 0x0002, // alarm off + C_AON = 0x0003, // alarm on + C_DER = 0x0004, // delete to end of row + C_RU2 = 0x0005, // roll-up captions (2 rows) + C_RU3 = 0x0006, // roll-up captions (3 rows) + C_RU4 = 0x0007, // roll-up captions (4 rows) + C_FON = 0x0008, // flash on + C_RDC = 0x0009, // resume direct captioning + C_TR = 0x000a, // text restart + C_RTD = 0x000b, // resume text display + C_EDM = 0x000c, // erase displayed memory + C_CR = 0x000d, // carriage return + C_ENM = 0x000e, // erase non-displayed memory + C_EOC = 0x000f, // end of caption + + /* special character */ + C_reg = 0x0000, // registered + C_deg = 0x0001, // degree + C_frac12 = 0x0002, // fraction 1/2 + C_iquest = 0x0003, // invert question + C_trademark = 0x0004, // trademark + C_cent = 0x0005, // cent + C_pound = 0x0006, // pound + C_music = 0x0007, // music note + C_agrave = 0x0008, // agrave + C_tspace = 0x0009, // transparent space + C_egrave = 0x000a, // egrave + C_acirc = 0x000b, // a circ + C_ecirc = 0x000c, // e circ + C_icirc = 0x000d, // i circ + C_ocirc = 0x000e, // o circ + C_ucirc = 0x000f, // u circ + + /* standard character (ASCII) */ + C_aacute = 0x002a, // a acute accent + C_eacute = 0x005c, // e acute accent + C_iacute = 0x005e, // i acute accent + C_oacute = 0x005f, // o acute accent + C_uacute = 0x0060, // u acute accent + C_ccedil = 0x007b, // c cedilla + C_division = 0x007c, // division sign + C_Ntilde = 0x007d, // N tilde + C_ntilde = 0x007e, // n tilde + C_block = 0x007f // solid block + }; + + int code, channel, character; + + /* decode next pair of bytes from the odd or even field buffer */ + if (!DecodeBits(fChannel == C_RADEON_CC1 || fChannel == C_RADEON_CC2 ? buffer0 : buffer1, code)) + return false; + + /* swap decoded bytes */ + code = ((code << 8) & 0xff00) | ((code >> 8) & 0x00ff); + + /* check parity */ + for (int bit = 0; bit < 7; bit++) { + if ((code & (0x0001 << bit)) != 0) + code ^= 0x0080; + if ((code & (0x0100 << bit)) != 0) + code ^= 0x8000; + } + if ((code & 0x8080) != 0x8080) { + PRINT(("CCaption::Decode() - parity error (%04X)\n", code)); + return false; + } + + /* check channel number */ + channel = (code & 0x0800) >> 12; + if (channel == 0) { + if (fChannel != C_RADEON_CC1 && fChannel != C_RADEON_CC3) { + PRINT(("CCaption::Decode() - ignore channel (%02X)\n", code & 0x7f7f)); + return false; + } + } + else { + if (fChannel != C_RADEON_CC2 && fChannel != C_RADEON_CC4) { + PRINT(("CCaption::Decode() - ignore channel (%02X)\n", code & 0x7f7f)); + return false; + } + } + + if ((code & 0x7000) == 0x0000) { + /* one-byte standard character (0?XX) */ + character = code & 0x007f; + + if (character >= 0x20) { + + PRINT(("%c", character)); + + fText[fRow][fColumn] = (fColor << 8) + character; + if (fColumn < C_RADEON_CC_COLUMNS - 1) + fColumn++; + } + else if (character != 0x00) { + PRINT(("<%04X>", code & 0x7f7f)); + } + } + else if ((code & 0x7770) == 0x1120) { + /* middle row code (112X) */ + fColor = code & 0x000f; + + fText[fRow][fColumn] = (fColor << 8) + ' '; + if (fColumn < C_RADEON_CC_COLUMNS - 1) + fColumn++; + + switch (fColor & 0x000e) { + case C_WHITE: + PRINT(("")); + else + PRINT((">")); + } + else if ((code & 0x7770) == 0x1130) { + /* two-byte special character (113X) */ + character = (code & 0x000f); + + fText[fRow][fColumn] = (fColor << 8) + character + 0x0080; + if (fColumn < C_RADEON_CC_COLUMNS - 1) + fColumn++; + + switch (character) { + case C_reg: + PRINT(("®")); + break; + case C_deg: + PRINT(("°")); + break; + case C_frac12: + PRINT(("½")); + break; + case C_iquest: + PRINT(("¿")); + break; + case C_trademark: + PRINT(("&trademark;")); + break; + case C_cent: + PRINT(("¢")); + break; + case C_pound: + PRINT(("£")); + break; + case C_music: + PRINT(("&music;")); + break; + case C_agrave: + PRINT(("à")); + break; + case C_tspace: + PRINT(("&tspace;")); + break; + case C_egrave: + PRINT(("è")); + break; + case C_acirc: + PRINT(("â")); + break; + case C_ecirc: + PRINT(("ê")); + break; + case C_icirc: + PRINT(("î")); + break; + case C_ocirc: + PRINT(("ô")); + break; + case C_ucirc: + PRINT(("û")); + break; + default: + PRINT(("", code & 0x7f7f)); + break; + } + } + else if ((code & 0x7770) == 0x1420) { + + if (code == fLastControlCode) + return false; + fLastControlCode = code; + + /* miscellaneous control code (142X) */ + switch (code & 0x000f) { + case C_RCL: + // resume caption loading + PRINT(("")); + break; + + case C_BS: + // backspace + PRINT(("")); + if (fColumn > 0) + fText[fRow][--fColumn] = 0x0000; + break; + + case C_AOF: + // alarm off + PRINT(("")); + break; + + case C_AON: + // alarm on + PRINT(("")); + break; + + case C_DER: + // delete to end of row + PRINT(("")); + for (int column = fColumn; column < C_RADEON_CC_COLUMNS; column++) + fText[fRow][column] = 0x0000; + break; + + case C_RU2: + // rollup captions (2 rows) + PRINT(("")); + for (int row = 0; row < C_RADEON_CC_ROWS - 2; row++) { + for (int column = 0; column < C_RADEON_CC_COLUMNS; column++) + fText[row][column] = fText[row + 2][column]; + } + for (int row = C_RADEON_CC_ROWS - 2; row < C_RADEON_CC_ROWS; row++) { + for (int column = 0; column < C_RADEON_CC_COLUMNS; column++) + fText[row][column] = 0x0000; + } + break; + + case C_RU3: + // rollup captions (3 rows) + PRINT(("")); + for (int row = 0; row < C_RADEON_CC_ROWS - 3; row++) { + for (int column = 0; column < C_RADEON_CC_COLUMNS; column++) + fText[row][column] = fText[row + 2][column]; + } + for (int row = C_RADEON_CC_ROWS - 3; row < C_RADEON_CC_ROWS; row++) { + for (int column = 0; column < C_RADEON_CC_COLUMNS; column++) + fText[row][column] = 0x0000; + } + break; + + case C_RU4: + // rollup captions (4 rows) + PRINT(("")); + for (int row = 0; row < C_RADEON_CC_ROWS - 4; row++) { + for (int column = 0; column < C_RADEON_CC_COLUMNS; column++) + fText[row][column] = fText[row + 2][column]; + } + for (int row = C_RADEON_CC_ROWS - 4; row < C_RADEON_CC_ROWS; row++) { + for (int column = 0; column < C_RADEON_CC_COLUMNS; column++) + fText[row][column] = 0x0000; + } + break; + + case C_FON: + // flash on + PRINT(("")); + break; + + case C_RDC: + // resume direct captioning + PRINT(("")); + break; + + case C_TR: + // text restart + PRINT(("")); + + fRow = fColumn = 0; + break; + + case C_RTD: + // resume text display + PRINT(("")); + + fRow = fColumn = 0; + break; + + case C_EDM: + // erase displayed memory + PRINT(("\n")); + for (int row = 0; row < C_RADEON_CC_ROWS; row++) { + for (int column = 0; column < C_RADEON_CC_COLUMNS; column++) + fDisplayText[row][column] = 0x0000; + } + DisplayCaptions(); + break; + + case C_CR: + // carriage return + PRINT(("")); + + /* has no effect in caption loading mode */ + break; + + case C_ENM: + // erase non-displayed memory + PRINT(("")); + for (int row = 0; row < C_RADEON_CC_ROWS; row++) { + for (int column = 0; column < C_RADEON_CC_COLUMNS; column++) + fText[row][column] = 0x0000; + } + break; + + case C_EOC: + // end of caption + PRINT(("\n")); + for (int row = 0; row < C_RADEON_CC_ROWS; row++) { + for (int column = 0; column < C_RADEON_CC_COLUMNS; column++) { + const int code = fText[row][column]; + fText[row][column] = fDisplayText[row][column]; + fDisplayText[row][column] = code; + } + } + + DisplayCaptions(); + break; + } + } + else if ((code & 0x7770) == 0x1720) { + /* tab offset (172X) */ + const int offset = code & 0x000f; + + if (offset >= 1 && offset <= 3) { + PRINT(("", offset)); + + if ((fColumn += offset) >= C_RADEON_CC_COLUMNS - 1) + fColumn = C_RADEON_CC_COLUMNS - 1; + } + else { + PRINT(("", code & 0x7f7f)); + } + } + else if ((code & 0x7040) == 0x1040) { + /* preamble address code (1Y4X, 1Y6X) */ + + switch (code & 0x0720) { + case C_ROW_1: + PRINT(("\n", code & 0x7f7f)); + return false; + } + + if ((code & 0x0010) == 0x0000) { + /* change color */ + fColor = (code & 0x000f); + fColumn = 0; + + switch (fColor & 0x000e) { + case C_WHITE: + PRINT((",white")); + break; + case C_GREEN: + PRINT((",green")); + break; + case C_BLUE: + PRINT((",blue")); + break; + case C_CYAN: + PRINT((",cyan")); + break; + case C_RED: + PRINT((",red")); + break; + case C_YELLOW: + PRINT((",yellow")); + break; + case C_MAGENTA: + PRINT((",magenta")); + break; + case C_ITALICS: + PRINT((",italics")); + break; + } + if ((fColor & 0x0001) != 0) + PRINT((",underline>")); + else + PRINT((">")); + + } + else { + /* indent, white */ + fColor = C_WHITE | (code & 0x0001); + fColumn = (code & 0x000e) << 1; + PRINT((",col%d>", fColumn)); + } + } + else { + /* two one-byte standard characters */ + character = (code >> 8) & 0x7f; + if (character >= 0x20) { + + PRINT(("%c", character)); + + fText[fRow][fColumn] = (fColor << 8) + character; + if (fColumn < C_RADEON_CC_COLUMNS - 1) + fColumn++; + } + else if (character != 0x00) { + PRINT(("<%02X>", character)); + } + + character = (code >> 0) & 0x7f; + if (character >= 0x20) { + + PRINT(("%c", character)); + + fText[fRow][fColumn] = (fColor << 8) + character; + if (fColumn < C_RADEON_CC_COLUMNS - 1) + fColumn++; + } + else if (character != 0x00) { + PRINT(("<%02X>", character)); + } + } + + return true; +} + +void CCaption::DisplayCaptions() const +{ + printf("\x1b[H\x1b[J"); + + for (int row = 0; row < C_RADEON_CC_ROWS; row++) { + for (int column = 0; column < C_RADEON_CC_COLUMNS; column++) { + if (fDisplayText[row][column] == 0x0000) { + printf("\x1b[0;47;37m "); + continue; + } + + const int code = (fDisplayText[row][column] >> 0) & 0xff; + const int color = (fDisplayText[row][column] >> 8) & 0xff; + + switch (color & 0x000e) { + case 0x0000: // WHITE + printf("\x1b[0;%d;40;37m", (color & 1) << 2); + break; + case 0x0002: // GREEN + printf("\x1b[0;%d;40;32m", (color & 1) << 2); + break; + case 0x0004: // BLUE + printf("\x1b[0;%d;40;34m", (color & 1) << 2); + break; + case 0x0006: // CYAN + printf("\x1b[0;%d;40;35m", (color & 1) << 2); + break; + case 0x0008: // RED + printf("\x1b[0;%d;40;31m", (color & 1) << 2); + break; + case 0x000a: // YELLOW + printf("\x1b[0;%d;40;33m", (color & 1) << 2); + break; + case 0x000c: // MAGENTA + printf("\x1b[0;%d;40;36m", (color & 1) << 2); + break; + case 0x000e: // WHITE ITALICS + if ((color & 1) == 0) + printf("\x1b[1;40;37m"); + else + printf("\x1b[1;4;40;37m"); + break; + } + + if (code >= 0x20 && code <= 0x7f) { + switch (code) { + case 0x002a: + // aacute + printf("\xc3\xa1"); + break; + case 0x005c: + // eacute + printf("\xc3\xa9"); + break; + case 0x005e: + // iacute + printf("\xc3\xad"); + break; + case 0x005f: + // oacute + printf("\xc3\xb3"); + break; + case 0x0060: + // uacute + printf("\xc3\xba"); + break; + case 0x007b: + // ccedil + printf("\xc3\xa7"); + break; + case 0x007c: + // division + printf("\xc3\xb7"); + break; + case 0x007d: + // Ntilde + printf("\xc3\x91"); + break; + case 0x007e: + // ntilde + printf("\xc3\xb1"); + break; + case 0x007f: + // block + printf("\xc1\xbf"); + break; + default: + // ASCII character + printf("%c", code); + break; + } + } + else { + switch (code) { + case 0x0080: + // reg + printf("\xc2\xae"); + break; + case 0x0081: + // deg + printf("\xc2\xb0"); + break; + case 0x0082: + // frac12 + printf("\xc2\xbd"); + break; + case 0x0083: + // iquest + printf("\xc2\xbf"); + break; + case 0x0084: + // trademark + printf("\xe2\x84"); + break; + case 0x0085: + // cent + printf("\xc2\xa2"); + break; + case 0x0086: + // pound + printf("\xc2\xa3"); + break; + case 0x0087: + // music + printf("\xc2\xa7"); + break; + case 0x0088: + // agrave + printf("\xc3\xa0"); + break; + case 0x0089: + // tspace + printf("\x1b[0;47;37m "); + break; + case 0x008a: + // egrave + printf("\xc3\xa8"); + break; + case 0x008b: + // acirc + printf("\xc3\xa2"); + break; + case 0x008c: + // ecirc + printf("\xc3\xaa"); + break; + case 0x008d: + // icirc + printf("\xc3\xae"); + break; + case 0x008e: + // ocirc + printf("\xc3\xb4"); + break; + case 0x008f: + // ucirc + printf("\xc3\xbb"); + break; + default: + // buggy code + printf("<%02X>", code); + break; + } + } + } + printf("\n"); + } + printf("\x1b[0;30;47m"); +} diff --git a/src/add-ons/media/media-add-ons/radeon/CC.h b/src/add-ons/media/media-add-ons/radeon/CC.h new file mode 100644 index 0000000000..b011b69ec4 --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/CC.h @@ -0,0 +1,67 @@ +/****************************************************************************** +/ +/ File: CC.h +/ +/ Description: Closed caption module. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#ifndef __CC_H__ +#define __CC_H__ + +#include "Capture.h" + +enum cc_mode { + C_RADEON_CC1 = 0, + C_RADEON_CC2 = 1, + C_RADEON_CC3 = 2, + C_RADEON_CC4 = 3 +}; + +enum cc_window { + C_RADEON_CC_VBI_LINE_WIDTH = 2048, + C_RADEON_CC_BLANK_START = 4*112, + C_RADEON_CC_VBI_LINE_NUMBER = 19, + C_RADEON_CC_BIT_DURATION = 56, + C_RADEON_CC_BITS_PER_FIELD = 16, + C_RADEON_CC_LEVEL_THRESHOLD = 32, + + C_RADEON_CC_LINE = 21, + + C_RADEON_CC_CHANNELS = 4, + C_RADEON_CC_ROWS = 15, + C_RADEON_CC_COLUMNS = 32 +}; + +class CCaption { +public: + CCaption(CCapture & capture); + + ~CCaption(); + + status_t InitCheck() const; + + void SetMode(cc_mode mode); + + bool Decode(const unsigned char * buffer0, + const unsigned char * buffer1); + +private: + bool DecodeBits(const unsigned char * buffer, int & data); + + void DisplayCaptions() const; + +private: + CCapture & fCapture; + cc_mode fChannel; + int fRow; + int fColumn; + int fColor; + int fLastControlCode; + short fText[C_RADEON_CC_ROWS][C_RADEON_CC_COLUMNS]; + short fDisplayText[C_RADEON_CC_ROWS][C_RADEON_CC_COLUMNS]; +}; + +#endif diff --git a/src/add-ons/media/media-add-ons/radeon/Capture.cpp b/src/add-ons/media/media-add-ons/radeon/Capture.cpp new file mode 100644 index 0000000000..5179f2a34f --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/Capture.cpp @@ -0,0 +1,346 @@ +/****************************************************************************** +/ +/ File: Capture.cpp +/ +/ Description: ATI Radeon Capture Unit interface. +/ +/ Copyright 2001, Carlos Hasan +/ +/ TK: something about synchronization: I removed all the FIFO wait +/ functions as they aren't thread-safe and AFAIK not needed as +/ only 2D/3D register accesses are buffered +/ +*******************************************************************************/ + +#include +#include "Capture.h" + +CCapture::CCapture(CRadeon & radeon) + : fRadeon(radeon), + fMode(C_RADEON_CAPTURE_FIELD_SINGLE), + fFormat(C_RADEON_CAPTURE_CCIR656), + fOffset0(0), + fOffset1(0), + fVBIOffset0(0), + fVBIOffset1(0), + fSize(0), + fVBISize(0), + fPitch(0), + fClip(), + fVBIClip() +{ + PRINT(("CCapture::CCapture()\n")); +} + +CCapture::~CCapture() +{ + PRINT(("CCapture::~CCapture()\n")); +} + +status_t CCapture::InitCheck() const +{ + return fRadeon.InitCheck(); +} + +void CCapture::SetBuffer(capture_stream_format format, capture_buffer_mode mode, + int offset0, int offset1, int size, int pitch) +{ + PRINT(("CCapture::SetBuffer(%s, %s, 0x%08x, 0x%08x, 0x%08x, %d)\n", + "BROOKTREE\0CCIR656\0\0\0ZVIDEO\0\0\0\0VIP"+10*format, + "FIELD-SINGLE\0FIELD-DOUBLE\0BOB-SINGLE\0\0\0" + "BOB-DOUBLE\0\0\0WEAVE-SINGLE\0WEAVE-DOUBLE"+13*mode, + offset0, offset1, size, pitch)); + + fMode = mode; + fFormat = format; + fOffset0 = offset0 + fRadeon.VirtualMemoryBase(); + fOffset1 = offset1 + fRadeon.VirtualMemoryBase(); + fSize = size; + fPitch = pitch; +} + +void CCapture::SetClip(int left, int top, int right, int bottom) +{ + PRINT(("CCapture::SetClip(%d, %d, %d, %d)\n", + left, top, right, bottom)); + + fClip.SetTo(left, top, right, bottom); +} + +void CCapture::SetVBIBuffer(int offset0, int offset1, int size) +{ + PRINT(("CCapture::SetVBIBuffer(0x%08x, 0x%08x, %d)\n", offset0, offset1, size)); + + fVBIOffset0 = offset0 + fRadeon.VirtualMemoryBase(); + fVBIOffset1 = offset1 + fRadeon.VirtualMemoryBase(); + fVBISize = size; +} + +void CCapture::SetVBIClip(int left, int top, int right, int bottom) +{ + PRINT(("CCapture::SetVBIClip(%d, %d, %d, %d)\n", + left, top, right, bottom)); + + fVBIClip.SetTo(left, top, right, bottom); +} + +void CCapture::Start(bool vbi) +{ + PRINT(("CCapture::Start(%d)\n", vbi)); + + // initially the capture unit is disabled + //fRadeon.WaitForFifo(2); + SetRegister(C_RADEON_CAP0_TRIG_CNTL, C_RADEON_CAP0_TRIGGER_W_NO_ACTION); + + // select buffer offset and pitch + //fRadeon.WaitForFifo(5); + + switch (fMode) { + case C_RADEON_CAPTURE_FIELD_SINGLE: + /* capture single field, single buffer */ + SetRegister(C_RADEON_CAP0_BUF0_OFFSET, fOffset0); + SetRegister(C_RADEON_CAP0_BUF_PITCH, fPitch << 1); + break; + + case C_RADEON_CAPTURE_FIELD_DOUBLE: + /* capture single field, double buffer */ + SetRegister(C_RADEON_CAP0_BUF0_OFFSET, fOffset0); + SetRegister(C_RADEON_CAP0_BUF1_OFFSET, fOffset1); + SetRegister(C_RADEON_CAP0_BUF_PITCH, fPitch << 1); + break; + + case C_RADEON_CAPTURE_BOB_SINGLE: + /* capture interlaced frame, single buffer */ + SetRegister(C_RADEON_CAP0_BUF0_OFFSET, fOffset0); + SetRegister(C_RADEON_CAP0_BUF0_EVEN_OFFSET, fOffset0 + fSize); + SetRegister(C_RADEON_CAP0_BUF_PITCH, fPitch << 1); + break; + + case C_RADEON_CAPTURE_BOB_DOUBLE: + /* capture interlaced frame, double buffer */ + SetRegister(C_RADEON_CAP0_BUF0_OFFSET, fOffset0); + SetRegister(C_RADEON_CAP0_BUF0_EVEN_OFFSET, fOffset0 + fSize); + SetRegister(C_RADEON_CAP0_BUF1_OFFSET, fOffset1); + SetRegister(C_RADEON_CAP0_BUF1_EVEN_OFFSET, fOffset1 + fSize); + SetRegister(C_RADEON_CAP0_BUF_PITCH, fPitch << 1); + break; + + case C_RADEON_CAPTURE_WEAVE_SINGLE: + /* capture deinterlaced frame, single buffer */ + SetRegister(C_RADEON_CAP0_BUF0_OFFSET, fOffset0); + SetRegister(C_RADEON_CAP0_BUF0_EVEN_OFFSET, fOffset0 + (fPitch << 1)); + SetRegister(C_RADEON_CAP0_BUF_PITCH, fPitch << 2); + break; + + case C_RADEON_CAPTURE_WEAVE_DOUBLE: + /* capture deinterlaced frame, double buffer */ + SetRegister(C_RADEON_CAP0_BUF0_OFFSET, fOffset0); + SetRegister(C_RADEON_CAP0_BUF0_EVEN_OFFSET, fOffset0 + (fPitch << 1)); + SetRegister(C_RADEON_CAP0_BUF1_OFFSET, fOffset1); + SetRegister(C_RADEON_CAP0_BUF1_EVEN_OFFSET, fOffset1 + (fPitch << 1)); + SetRegister(C_RADEON_CAP0_BUF_PITCH, fPitch << 2); + break; + } + + // select VBI buffer offset + //fRadeon.WaitForFifo(4); + + // FIXME: change according to the buffering mode? + SetRegister(C_RADEON_CAP0_VBI0_OFFSET, fVBIOffset0); + SetRegister(C_RADEON_CAP0_VBI1_OFFSET, fVBIOffset0 + fVBISize); + + SetRegister(C_RADEON_CAP0_VBI2_OFFSET, fVBIOffset1); + SetRegister(C_RADEON_CAP0_VBI3_OFFSET, fVBIOffset1 + fVBISize); + + // select capture clipping window + //fRadeon.WaitForFifo(2); + + SetRegister(C_RADEON_CAP0_H_WINDOW, + ((fClip.Left() << 1) & C_RADEON_CAP0_H_START) | + ((fClip.Width() << 17) & C_RADEON_CAP0_H_WIDTH)); + + SetRegister(C_RADEON_CAP0_V_WINDOW, + ((fClip.Top() << 0) & C_RADEON_CAP0_V_START) | + ((fClip.Bottom() << 16) & C_RADEON_CAP0_V_END)); + + // select VBI clipping window + //fRadeon.WaitForFifo(2); + + SetRegister(C_RADEON_CAP0_VBI_H_WINDOW, + ((fVBIClip.Left() << 0) & C_RADEON_CAP0_VBI_H_START) | + ((fVBIClip.Width() << 16) & C_RADEON_CAP0_VBI_H_WIDTH)); + + SetRegister(C_RADEON_CAP0_VBI_V_WINDOW, + ((fVBIClip.Top() << 0) & C_RADEON_CAP0_VBI_V_START) | + ((fVBIClip.Bottom() << 16) & C_RADEON_CAP0_VBI_V_END)); + + // select buffer type, input mode, video format and buffering mode + //fRadeon.WaitForFifo(10); + + switch (fMode) { + case C_RADEON_CAPTURE_FIELD_SINGLE: + case C_RADEON_CAPTURE_FIELD_DOUBLE: + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_BUF_TYPE, C_RADEON_CAP0_BUF_TYPE_FIELD); + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_ONESHOT_MODE, C_RADEON_CAP0_ONESHOT_MODE_FIELD); + break; + + case C_RADEON_CAPTURE_BOB_SINGLE: + case C_RADEON_CAPTURE_BOB_DOUBLE: + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_BUF_TYPE, C_RADEON_CAP0_BUF_TYPE_ALTERNATING); + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_ONESHOT_MODE, C_RADEON_CAP0_ONESHOT_MODE_FIELD); + break; + + case C_RADEON_CAPTURE_WEAVE_SINGLE: + case C_RADEON_CAPTURE_WEAVE_DOUBLE: + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_BUF_TYPE, C_RADEON_CAP0_BUF_TYPE_FRAME); + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_ONESHOT_MODE, C_RADEON_CAP0_ONESHOT_MODE_FRAME); + break; + } + + switch (fMode) { + case C_RADEON_CAPTURE_FIELD_SINGLE: + case C_RADEON_CAPTURE_BOB_SINGLE: + case C_RADEON_CAPTURE_WEAVE_SINGLE: + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_BUF_MODE, C_RADEON_CAP0_BUF_MODE_SINGLE); + break; + + case C_RADEON_CAPTURE_FIELD_DOUBLE: + case C_RADEON_CAPTURE_BOB_DOUBLE: + case C_RADEON_CAPTURE_WEAVE_DOUBLE: + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_BUF_MODE, C_RADEON_CAP0_BUF_MODE_DOUBLE); + break; + } + + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_INPUT_MODE, C_RADEON_CAP0_INPUT_MODE_CONTINUOUS); + SetRegister(C_RADEON_CAP0_CONFIG, + C_RADEON_CAP0_VIDEO_IN_FORMAT | C_RADEON_CAP0_VIDEO_SIGNED_UV, C_RADEON_CAP0_VIDEO_IN_VYUY422); + + + // select stream format and port mode + //fRadeon.WaitForFifo(4); + + switch (fFormat) { + case C_RADEON_CAPTURE_BROOKTREE: + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_STREAM_FORMAT, C_RADEON_CAP0_STREAM_BROOKTREE); + SetRegister(C_RADEON_CAP0_PORT_MODE_CNTL, C_RADEON_CAP0_PORT_WIDTH_8_BITS | C_RADEON_CAP0_PORT_LOWER_BYTE_USED); + break; + case C_RADEON_CAPTURE_CCIR656: + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_STREAM_FORMAT, C_RADEON_CAP0_STREAM_CCIR656); + SetRegister(C_RADEON_CAP0_PORT_MODE_CNTL, C_RADEON_CAP0_PORT_WIDTH_8_BITS | C_RADEON_CAP0_PORT_LOWER_BYTE_USED); + break; + case C_RADEON_CAPTURE_ZOOMVIDEO: + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_STREAM_FORMAT, C_RADEON_CAP0_STREAM_ZV); + SetRegister(C_RADEON_CAP0_PORT_MODE_CNTL, C_RADEON_CAP0_PORT_WIDTH_16_BITS | C_RADEON_CAP0_PORT_LOWER_BYTE_USED); + break; + case C_RADEON_CAPTURE_VIP: + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_STREAM_FORMAT, C_RADEON_CAP0_STREAM_VIP); + SetRegister(C_RADEON_CAP0_PORT_MODE_CNTL, C_RADEON_CAP0_PORT_WIDTH_16_BITS | C_RADEON_CAP0_PORT_LOWER_BYTE_USED); + break; + } + + // set capture mirror mode, field sense, downscaler/decimator, enable 3:4 pull down + //fRadeon.WaitForFifo(16); + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_MIRROR_EN, 0); + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_ONESHOT_MIRROR_EN, 0); + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_START_FIELD, C_RADEON_CAP0_START_ODD_FIELD); + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_HORZ_DOWN, C_RADEON_CAP0_HORZ_DOWN_1X); + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_VERT_DOWN, C_RADEON_CAP0_VERT_DOWN_1X); + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_VBI_HORZ_DOWN, C_RADEON_CAP0_VBI_HORZ_DOWN_1X); + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_HDWNS_DEC, C_RADEON_CAP0_DECIMATOR); + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_SOFT_PULL_DOWN_EN, C_RADEON_CAP0_SOFT_PULL_DOWN_EN); + + // prepare to enable capture + //fRadeon.WaitForFifo(14); + + // disable test and debug modes + SetRegister(C_RADEON_TEST_DEBUG_CNTL, 0); + SetRegister(C_RADEON_CAP0_VIDEO_SYNC_TEST, 0); + SetRegister(C_RADEON_CAP0_DEBUG, C_RADEON_CAP0_V_SYNC); + + // connect capture engine to AMC connector + SetRegister(C_RADEON_VIDEOMUX_CNTL, 1, 1); + + // select capture engine clock source to PCLK + SetRegister(C_RADEON_FCP_CNTL, C_RADEON_FCP0_SRC_PCLK); + + // enable capture unit + SetRegister(C_RADEON_CAP0_TRIG_CNTL, + C_RADEON_CAP0_TRIGGER_W_CAPTURE | C_RADEON_CAP0_EN); + + // enable VBI capture + SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_VBI_EN, + (vbi ? C_RADEON_CAP0_VBI_EN : 0)); +} + +void CCapture::Stop() +{ + PRINT(("CCapture::Stop()\n")); + + // disable capture unit + //fRadeon.WaitForFifo(4); + + // disable capture unit + SetRegister(C_RADEON_CAP0_TRIG_CNTL, C_RADEON_CAP0_TRIGGER_W_NO_ACTION); + + // disable the capture engine clock (set to ground) + fRadeon.SetRegister(C_RADEON_FCP_CNTL, C_RADEON_FCP0_SRC_GND); +} + +void CCapture::SetInterrupts(bool enable) +{ + PRINT(("CCapture::SetInterrupts(%d)\n", enable)); + + fRadeon.SetRegister(C_RADEON_CAP_INT_CNTL, + C_RADEON_CAP0_BUF0_INT_EN | C_RADEON_CAP0_BUF0_EVEN_INT_EN | + C_RADEON_CAP0_BUF1_INT_EN | C_RADEON_CAP0_BUF1_EVEN_INT_EN | + C_RADEON_CAP0_VBI0_INT_EN | C_RADEON_CAP0_VBI1_INT_EN, + (enable ? C_RADEON_CAP0_BUF0_INT_EN | C_RADEON_CAP0_BUF0_EVEN_INT_EN | + C_RADEON_CAP0_BUF1_INT_EN | C_RADEON_CAP0_BUF1_EVEN_INT_EN | + C_RADEON_CAP0_VBI0_INT_EN | C_RADEON_CAP0_VBI1_INT_EN : 0)); + + // clear any stick interrupt + fRadeon.SetRegister(C_RADEON_CAP_INT_STATUS, + C_RADEON_CAP0_BUF0_INT_AK | C_RADEON_CAP0_BUF0_EVEN_INT_AK | + C_RADEON_CAP0_BUF1_INT_AK | C_RADEON_CAP0_BUF1_EVEN_INT_AK | + C_RADEON_CAP0_VBI0_INT_AK | C_RADEON_CAP0_VBI1_INT_AK); +} + +int CCapture::WaitInterrupts(int * sequence, bigtime_t * when, bigtime_t timeout) +{ + int mask; + + if (fRadeon.WaitInterrupt(&mask, sequence, when, timeout) == B_OK) { + /* + int mask = fRadeon.Register(C_RADEON_CAP_INT_STATUS); + + fRadeon.SetRegister(C_RADEON_CAP_INT_STATUS, + C_RADEON_CAP0_BUF0_INT_AK | C_RADEON_CAP0_BUF0_EVEN_INT_AK | + C_RADEON_CAP0_BUF1_INT_AK | C_RADEON_CAP0_BUF1_EVEN_INT_AK | + C_RADEON_CAP0_VBI0_INT_AK | C_RADEON_CAP0_VBI1_INT_AK); + */ + + return + ((mask & C_RADEON_CAP0_BUF0_INT) != 0 ? C_RADEON_CAPTURE_BUF0_INT : 0) | + ((mask & C_RADEON_CAP0_BUF1_INT) != 0 ? C_RADEON_CAPTURE_BUF1_INT : 0) | + ((mask & C_RADEON_CAP0_BUF0_EVEN_INT) != 0 ? C_RADEON_CAPTURE_BUF0_EVEN_INT : 0) | + ((mask & C_RADEON_CAP0_BUF1_EVEN_INT) != 0 ? C_RADEON_CAPTURE_BUF1_EVEN_INT : 0) | + ((mask & C_RADEON_CAP0_VBI0_INT) != 0 ? C_RADEON_CAPTURE_VBI0_INT : 0) | + ((mask & C_RADEON_CAP0_VBI1_INT) != 0 ? C_RADEON_CAPTURE_VBI1_INT : 0); + } + return 0; +} + +int CCapture::Register(radeon_register index, int mask) +{ + return fRadeon.Register(index, mask); +} + +void CCapture::SetRegister(radeon_register index, int value) +{ + fRadeon.SetRegister(index, value); +} + +void CCapture::SetRegister(radeon_register index, int mask, int value) +{ + fRadeon.SetRegister(index, mask, value); +} diff --git a/src/add-ons/media/media-add-ons/radeon/Capture.h b/src/add-ons/media/media-add-ons/radeon/Capture.h new file mode 100644 index 0000000000..e737eab31a --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/Capture.h @@ -0,0 +1,89 @@ +/****************************************************************************** +/ +/ File: Capture.h +/ +/ Description: ATI Radeon Capture Unit interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#ifndef __CAPTURE_H__ +#define __CAPTURE_H__ + +#include "Radeon.h" + +enum capture_buffer_mode { + C_RADEON_CAPTURE_FIELD_SINGLE = 0, + C_RADEON_CAPTURE_FIELD_DOUBLE = 1, + C_RADEON_CAPTURE_BOB_SINGLE = 2, + C_RADEON_CAPTURE_BOB_DOUBLE = 3, + C_RADEON_CAPTURE_WEAVE_SINGLE = 4, + C_RADEON_CAPTURE_WEAVE_DOUBLE = 5 +}; + +enum capture_stream_format { + C_RADEON_CAPTURE_BROOKTREE = 0, + C_RADEON_CAPTURE_CCIR656 = 1, + C_RADEON_CAPTURE_ZOOMVIDEO = 2, + C_RADEON_CAPTURE_VIP = 3 +}; + +enum capture_interrupt_mask { + C_RADEON_CAPTURE_BUF0_INT = 1 << 0, + C_RADEON_CAPTURE_BUF0_EVEN_INT = 1 << 1, + C_RADEON_CAPTURE_BUF1_INT = 1 << 2, + C_RADEON_CAPTURE_BUF1_EVEN_INT = 1 << 3, + C_RADEON_CAPTURE_VBI0_INT = 1 << 4, + C_RADEON_CAPTURE_VBI1_INT = 1 << 5 +}; + +class CCapture { +public: + CCapture(CRadeon & radeon); + + ~CCapture(); + + status_t InitCheck() const; + + void SetBuffer(capture_stream_format format, capture_buffer_mode mode, + int offset0, int offset1, int size, int pitch); + + void SetClip(int left, int top, int right, int bottom); + + void SetVBIBuffer(int offset0, int offset1, int size); + + void SetVBIClip(int left, int top, int right, int bottom); + + void Start(bool vbi = false); + + void Stop(); + +public: + void SetInterrupts(bool enable); + + int WaitInterrupts(int * sequence, bigtime_t * when, bigtime_t timeout); + +private: + int Register(radeon_register index, int mask); + + void SetRegister(radeon_register index, int value); + + void SetRegister(radeon_register index, int mask, int value); + +private: + CRadeon & fRadeon; + capture_buffer_mode fMode; + capture_stream_format fFormat; + int fOffset0; + int fOffset1; + int fVBIOffset0; + int fVBIOffset1; + int fSize; + int fVBISize; + int fPitch; + CRadeonRect fClip; + CRadeonRect fVBIClip; +}; + +#endif diff --git a/src/add-ons/media/media-add-ons/radeon/I2CPort.cpp b/src/add-ons/media/media-add-ons/radeon/I2CPort.cpp new file mode 100644 index 0000000000..d9e2cd6c7e --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/I2CPort.cpp @@ -0,0 +1,234 @@ +/****************************************************************************** +/ +/ File: I2C.cpp +/ +/ Description: ATI Radeon I2C Serial Bus interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#include +#include "I2CPort.h" + +CI2CPort::CI2CPort(CRadeon & radeon, int rate) + : fRadeon(radeon), + fNfactor(0), + fMfactor(0), + fTimeLimit(0) +{ + PRINT(("CI2CPort::CI2CPort()\n")); + + if( fRadeon.InitCheck() == B_OK ) { + int refFreq, refDiv, minFreq, maxFreq, xclock; + fRadeon.GetPLLParameters(refFreq, refDiv, minFreq, maxFreq, xclock); + + double n = (xclock * 10000.0) / (4.0 * rate); + for (fNfactor = 1; fNfactor < 255; fNfactor++) { + if (fNfactor * (fNfactor - 1) > n) + break; + } + fMfactor = fNfactor - 1; + fTimeLimit = 2 * fNfactor; + + // enable I2C bus + fRadeon.SetRegister(C_RADEON_I2C_CNTL_1, 0x00ff0000, + C_RADEON_I2C_SEL | C_RADEON_I2C_EN); + + fRadeon.SetRegister(C_RADEON_I2C_CNTL_0, 0x000000ff, + C_RADEON_I2C_DONE | C_RADEON_I2C_NACK | + C_RADEON_I2C_HALT | C_RADEON_I2C_SOFT_RST | + C_RADEON_I2C_DRIVE_EN | C_RADEON_I2C_DRIVE_SEL); + +#if DEBUG + PRINT(("CI2CPort::CI2CPort() - I2C devices found at ports: ")); + for (int address = 0x80; address <= 0xff; address += 0x02) { + if (Probe(address)) + PRINT(("0x%02x ", address)); + } + PRINT(("\n")); +#endif + } +} + +CI2CPort::~CI2CPort() +{ + PRINT(("CI2CPort::~CI2CPort()\n")); + if( fRadeon.InitCheck() == B_OK ) { + // disable I2C bus + fRadeon.SetRegister(C_RADEON_I2C_CNTL_1, 0); + } +} + +status_t CI2CPort::InitCheck() const +{ + return fRadeon.InitCheck(); +} + +CRadeon & CI2CPort::Radeon() const +{ + return fRadeon; +} + +bool CI2CPort::Probe(int address) +{ + char buffer[1]; + + return Read(address, buffer, sizeof(buffer)); +} + +bool CI2CPort::Write(int address, const char * buffer, int length) +{ + if (Send(address, buffer, length, true, true) == length) + return true; + return false; +} + +bool CI2CPort::Read(int address, char * buffer, int length) +{ + if (Receive(address, buffer, length, true, true) == length) + return true; + return false; +} + +bool CI2CPort::Write(int address, const char * buffer, int length, char * result, int reslen) +{ + if (Send(address, buffer, length, true, false) == length) + if (Receive(address, result, reslen, true, true) == reslen) + return true; + return false; +} + +int CI2CPort::Register(int address, int index) +{ + char value = index; + + if (Send(address, &value, sizeof(value), true, false) == sizeof(value)) { + if (Receive(address, &value, sizeof(value), true, true) == sizeof(value)) + return value & 0xff; + } + PRINT(("CI2CPort::Register() - error\n")); + return -1; +} + +void CI2CPort::SetRegister(int address, int index, int value) +{ + char buffer[2]; + + buffer[0] = index; + buffer[1] = value; + + if (Send(address, buffer, sizeof(buffer), true, true) != sizeof(buffer)) + PRINT(("CI2CPort::SetRegister() - error\n")); +} + +int CI2CPort::Send(int address, const char * buffer, int length, bool start, bool stop) +{ + //fRadeon.WaitForFifo(4 + length); + + // clear status bit + fRadeon.SetRegister(C_RADEON_I2C_CNTL_0, + C_RADEON_I2C_DONE | C_RADEON_I2C_NACK | + C_RADEON_I2C_HALT | C_RADEON_I2C_SOFT_RST); + + // write address + fRadeon.SetRegister(C_RADEON_I2C_DATA, address & 0xfffffffe); + + // write data + for (int offset = 0; offset < length; offset++) + fRadeon.SetRegister(C_RADEON_I2C_DATA, buffer[offset]); + + // + fRadeon.SetRegister(C_RADEON_I2C_CNTL_1, + (fTimeLimit << 24) | length | + C_RADEON_I2C_EN | C_RADEON_I2C_SEL | 0x100); + + fRadeon.SetRegister(C_RADEON_I2C_CNTL_0, + (fNfactor << 24) | (fMfactor << 16) | + C_RADEON_I2C_GO | C_RADEON_I2C_DRIVE_EN | + (start ? C_RADEON_I2C_START : 0) | (stop ? C_RADEON_I2C_STOP : 0)); + + for (int wait = 0; wait < 100; wait++) { + if ((fRadeon.Register(C_RADEON_I2C_CNTL_0) & C_RADEON_I2C_GO) == 0) + break; + } + + if (WaitAck() != C_RADEON_I2C_DONE) { + Stop(); + return 0; + } + + return length; +} + +int CI2CPort::Receive(int address, char * buffer, int length, bool start, bool stop) +{ + //fRadeon.WaitForFifo(length + 4); + + fRadeon.SetRegister(C_RADEON_I2C_CNTL_0, + C_RADEON_I2C_DONE | C_RADEON_I2C_NACK | + C_RADEON_I2C_HALT | C_RADEON_I2C_SOFT_RST); + + fRadeon.SetRegister(C_RADEON_I2C_DATA, address | 0x00000001); + + fRadeon.SetRegister(C_RADEON_I2C_CNTL_1, + (fTimeLimit << 24) | C_RADEON_I2C_EN | //C_RADEON_I2C_SEL | + length | 0x100); + + fRadeon.SetRegister(C_RADEON_I2C_CNTL_0, + (fNfactor << 24) | (fMfactor << 16) | C_RADEON_I2C_GO | + (start ? C_RADEON_I2C_START : 0) | (stop ? C_RADEON_I2C_STOP : 0) | + C_RADEON_I2C_DRIVE_EN | C_RADEON_I2C_RECEIVE); + + + for (int wait = 0; wait < 100; wait++) { + if ((fRadeon.Register(C_RADEON_I2C_CNTL_0) & C_RADEON_I2C_GO) == 0) + break; + } + + if (WaitAck() != C_RADEON_I2C_DONE) + return 0; + + snooze(1000); + + for (int offset = 0; offset < length; offset++) { + //fRadeon.WaitForFifo(1); + buffer[offset] = fRadeon.Register(C_RADEON_I2C_DATA) & 0xff; + } + + return length; +} + +int CI2CPort::WaitAck() +{ + for (int wait = 0; wait < 100; wait++) { + int control = fRadeon.Register(C_RADEON_I2C_CNTL_0); + if ((control & C_RADEON_I2C_HALT) != 0) + return C_RADEON_I2C_HALT; + if ((control & C_RADEON_I2C_NACK) != 0) + return C_RADEON_I2C_NACK; + if ((control & C_RADEON_I2C_DONE) != 0) + return C_RADEON_I2C_DONE; + snooze(1000); + } + PRINT(("CI2CPort::WaitAck() - Time out!\n")); + return C_RADEON_I2C_HALT; +} + +void CI2CPort::Stop() +{ + // reset status flags + fRadeon.SetRegister(C_RADEON_I2C_CNTL_0, + C_RADEON_I2C_DONE | C_RADEON_I2C_NACK | C_RADEON_I2C_HALT, 0); + + // issue abort call + fRadeon.SetRegister(C_RADEON_I2C_CNTL_0, + C_RADEON_I2C_ABORT | C_RADEON_I2C_GO, C_RADEON_I2C_ABORT | C_RADEON_I2C_GO); + + // wait GO bit to go low + for (int wait = 0; wait < 100; wait++) { + if ((fRadeon.Register(C_RADEON_I2C_CNTL_0) & C_RADEON_I2C_GO) == 0) + snooze(1000); + } + PRINT(("CI2CPort::Stop() - Time out!\n")); +} diff --git a/src/add-ons/media/media-add-ons/radeon/I2CPort.h b/src/add-ons/media/media-add-ons/radeon/I2CPort.h new file mode 100644 index 0000000000..ea4c08c453 --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/I2CPort.h @@ -0,0 +1,63 @@ +/****************************************************************************** +/ +/ File: I2C.h +/ +/ Description: ATI Radeon I2C Serial Bus interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#ifndef __I2C_PORT_H__ +#define __I2C_PORT_H__ + +#include "Radeon.h" + +enum i2c_port_clock_rate { + C_RADEON_I2C_MIN_CLOCK_RATE = 7500, + C_RADEON_I2C_MAX_CLOCK_RATE = 100000, + C_RADEON_I2C_DEFAULT_CLOCK_RATE = 80000 +}; + +class CI2CPort { +public: + CI2CPort(CRadeon & radeon, int rate = C_RADEON_I2C_DEFAULT_CLOCK_RATE); + + ~CI2CPort(); + + status_t InitCheck() const; + + CRadeon & Radeon() const; + + bool Probe(int address); + +public: + bool Write(int address, const char * buffer, int length); + + bool Read(int address, char * buffer, int length); + + bool Write(int address, const char * buffer, int length, char * output, int outlen); + +public: + int Register(int address, int index); + + void SetRegister(int address, int index, int value); + +private: + int Send(int address, const char * buffer, int length, bool start, bool stop); + + int Receive(int address, char * buffer, int length, bool start, bool stop); + + int WaitAck(); + + void Stop(); + +private: + CRadeon & fRadeon; + int fNfactor; + int fMfactor; + int fTimeLimit; +}; + + +#endif diff --git a/src/add-ons/media/media-add-ons/radeon/MSP3430.cpp b/src/add-ons/media/media-add-ons/radeon/MSP3430.cpp new file mode 100644 index 0000000000..aa7a739e97 --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/MSP3430.cpp @@ -0,0 +1,878 @@ +/****************************************************************************** +/ +/ File: MSP3430.cpp +/ +/ Description: Micronas Multistandard Sound Processor (MSP) interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#include +#include "MSP3430.h" + +enum MSP3430_register { + MSP3430_CONTROL = 0x00, // R/W control register + MSP3430_WR_DEM = 0x10, // Write demodulator register + MSP3430_RD_DEM = 0x11, // Read demodulator register + MSP3430_WR_DSP = 0x12, // Read DSP register + MSP3430_RD_DSP = 0x13 // Write DSP register +}; + +enum MSP3430_control_register { + MSP3430_CONTROL_NORMAL = 0x0000, // normal operation mode + MSP3430_CONTROL_RESET = 0x8000 // reset mode +}; + +enum MSP3430_wr_dem_register { + MSP3430_DEM_STANDARD_SEL = 0x0020, // standard selection + MSP3430_DEM_MODUS = 0x0030, // demodulator options + MSP3430_DEM_I2S_CONFIG = 0x0040 // I2S configuration +}; + +enum MSP3430_rd_dem_register { + MSP3430_DEM_STANDARD_RES = 0x007e, // standard detection result + MSP3430_DEM_STATUS = 0x0200 // status register +}; + +enum MSP3430_wr_dsp_register { + // preprocessing + MSP3430_DSP_FM_PRESCALE = 0x000e, // FM/AM analog signal prescale + MSP3430_DSP_PRE_FM = BITS(15:8), + MSP3430_DSP_FM_MATRIX = BITS(7:0), + MSP3430_DSP_NICAM_PRESCALE = 0x0010, // NICAM digital signal prescale + MSP34300_DSP_PRE_NICAM = BITS(15:8), + MSP3430_DSP_PRE_I2S2 = 0x0012, // I2S digital signal prescale + MSP3430_DSP_PRE_I2S1 = 0x0016, + MSP3430_DSP_PRE_SCART = 0x000d, // SCART prescale + + // source select and output channel matrix + MSP3430_DSP_SRC_MAT_MAIN = 0x0008, // loudspeaker source and matrix + MSP3430_DSP_SRC_MAIN = BITS(15:8), + MSP3430_DSP_MAT_MAIN = BITS(7:0), + MSP3430_DSP_SRC_MAT_AUX = 0x0009, // headphone source and matrix + MSP3430_DSP_SRC_AUX = BITS(15:8), + MSP3430_DSP_MAT_AUX = BITS(7:0), + MSP3430_DSP_SRC_MAT_SCART1 = 0x000a, // SCART1 source and matrix + MSP3430_DSP_SRC_SCART1 = BITS(15:8), + MSP3430_DSP_MAT_SCART1 = BITS(7:0), + MSP3430_DSP_SRC_MAT_SCART2 = 0x0041, // SCART2 source and matrix + MSP3430_DSP_SRC_SCART2 = BITS(15:8), + MSP3430_DSP_MAT_SCART2 = BITS(7:0), + MSP3430_DSP_SRC_MAT_I2S = 0x000b, // I2S source and matrix + MSP3430_DSP_SRC_I2S = BITS(15:8), + MSP3430_DSP_MAT_I2S = BITS(7:0), + MSP3430_DSP_SRC_MAT_QPEAK = 0x000c, // QuasiPeak detector source and matrix + MSP3430_DSP_SRC_QPEAK = BITS(15:8), + MSP3430_DSP_MAT_QPEAK = BITS(7:0), + + // loudspeaker and headphone processing + MSP3430_DSP_VOL_MAIN = 0x0000, // loudspeaker volume + MSP3430_DSP_VOL_AUX = 0x0006, // headphone volume + MSP3430_DSP_AVC = 0x0029, // automatic volume correction + MSP3430_DSP_BAL_MAIN = 0x0001, // loudspeaker balance + MSP3430_DSP_BAL_AUX = 0x0030, // headphone balance + MSP3430_DSP_TONE_MODE = 0x0020, // select bass/treble or equalizer + MSP3430_DSP_BASS_MAIN = 0x0002, // loudspeaker bass + MSP3430_DSP_BASS_AUX = 0x0031, // headphone bass + MSP3430_DSP_TREB_MAIN = 0x0003, // loudspeaker treble + MSP3430_DSP_TREB_AUX = 0x0032, // headphone treble + MSP3430_DSP_EQUAL_BAND1 = 0x0021, // equalizer coefficients + MSP3430_DSP_EQUAL_BAND2 = 0x0022, + MSP3430_DSP_EQUAL_BAND3 = 0x0023, + MSP3430_DSP_EQUAL_BAND4 = 0x0024, + MSP3430_DSP_EQUAL_BAND5 = 0x0025, + MSP3430_DSP_LOUD_MAIN = 0x0004, // loudspeaker loudness + MSP3430_DSP_LOUD_AUX = 0x0033, // headphone loudness + MSP3430_DSP_SPAT_MAIN = 0x0005, // spatial effects + + // subwoofer output channel + MSP3430_DSP_SUBW_LEVEL = 0x002c, // subwoofer level + MSP3430_DSP_SUBW_FREQ = 0x002d, // subwoofer frequency + + // micronas dynamic bass + MSP3430_DSP_MDB_STR = 0x0068, // MDB strength + MSP3430_DSP_MDB_LIM = 0x0069, // MDB limit + MSP3430_DSP_MDB_HMC = 0x006a, // MDB harmonic content + MSP3430_DSP_MDB_LP = 0x006b, // MDB low frequency + MSP3430_DSP_MDB_HP = 0x006c, // MDB high frequency + + // SCART output channel + MSP3430_DSP_VOL_SCART1 = 0x0007, // SCART1 volume + MSP3430_DSP_VOL_SCART2 = 0x0040, // SCART2 volume + + // SCART switches and digital I/O pins + MSP3430_DSP_ACB_REG = 0x0013, // SCART switches + MSP3430_DSP_BEEPER = 0x0014 // Beeper volume and frequency +}; + +enum MSP3430_rd_dsp_register { + // quasi-peak detector readout + MSP3430_DSP_QPEAK_L = 0x0019, // Quasipeak detector left and right + MSP3430_DSP_QPEAK_R = 0x001a, + + // MSP 34x0G version readout + MSP3430_DSP_MSP_HARD_REVISION = 0x001e, // MSP hardware and revision + MSP3430_DSP_MSP_HARD = BITS(15:8), + MSP3430_DSP_MSP_REVISION = BITS(7:0), + MSP3430_DSP_MSP_PRODUCT_ROM = 0x001f, // MSP product and ROM version + MSP3430_DSP_MSP_PRODUCT = BITS(15:8), + MSP3430_DSP_MSP_ROM = BITS(7:0) +}; + +enum MSP3430_sound_standard { + C_MSP3430_AUTOMATIC = 0x0001, // Automatic Detection (*) + C_MSP3430_M_FM_STEREO = 0x0002, // NTSC M/N (*) + C_MSP3430_BG_FM_STEREO = 0x0003, // PAL B/G (*) + C_MSP3430_DK1_FM_STEREO = 0x0004, // (*) + C_MSP3430_DK2_FM_STEREO = 0x0005, // + C_MSP3430_DK_FM_MONO = 0x0006, // + C_MSP3430_DK3_FM_STEREO = 0x0007, // + C_MSP3430_BG_NICAM_FM = 0x0008, // PAL B/G (*) + C_MSP3430_L_NICAM_AM = 0x0009, // (*) + C_MSP3430_I_NICAM_FM = 0x000A, // PAL I (*) + C_MSP3430_DK_NICAM_FM = 0x000B, // (*) + C_MSP3430_DK_NICAM_FM_HDEV2 = 0x000C, + C_MSP3430_DK_NICAM_FM_HDEV3 = 0x000D, + C_MSP3430_BTSC_STEREO = 0x0020, // BTSC Stereo (*) + C_MSP3430_BTSC_MONO_SAP = 0x0021, + C_MSP3430_M_JAPAN_STEREO = 0x0030, // NTSC Japan (*) + C_MSP3430_FM_RADIO = 0x0040, // FM Radio (*) + C_MSP3430_SAT_MONO = 0x0050, // Satellite Mono + C_MSP3430_SAT_STEREO = 0x0051, // Satellite Stereo + C_MSP3430_SAT_ASTRA_RADIO = 0x0060 // Astra Digital Radio +}; + +enum MSP3430_channel_source { + C_MSP3430_SOURCE_FM = 0x00, // FM/AM Mono + C_MSP3430_SOURCE_STEREO = 0x01, // Stereo or A/B (NICAM) + C_MSP3430_SOURCE_SCART = 0x02, // SCART Input + C_MSP3430_SOURCE_LANG_A = 0x03, // Stereo/Language A + C_MSP3430_SOURCE_LANG_B = 0x04, // Stereo/Language B + C_MSP3430_SOURCE_I2S1 = 0x05, // I2S1 Input + C_MSP3430_SOURCE_I2S2 = 0x06 // I2S2 Input +}; + +enum MSP3430_channel_matrix { + C_MSP3430_MATRIX_SOUND_A = 0x00, // Sound A Mono + C_MSP3430_MATRIX_SOUND_B = 0x10, // Sound B Mono + C_MSP3430_MATRIX_STEREO = 0x20, // Stereo + C_MSP3430_MATRIX_MONO = 0x30 // Mono +}; + + + +/* + +------------------------------------------------------------------- +System Sound Sound Color + Carrier (MHz) Modulation System +------------------------------------------------------------------- +Satellite 6.5/5.85 FM-Mono/NICAM PAL + 6.5 FM-Mono + 7.02/7.2 FM-Stereo PAL + 7.38/7.56 ASTRA Digital Radio + etc. + 4.5/4.724212 FM-Stereo (A2) NTSC +------------------------------------------------------------------- +NTSC M/N 4.5 FM-FM (EIA-J) NTSC +------------------------------------------------------------------- + 4.5 BTSC Stereo + SAP NTSC, PAL +FM-Radio 10.7 FM-Stereo Radio . +------------------------------------------------------------------- + + + + + +*/ + +CMSP3430::CMSP3430(CI2CPort & port) + : fPort(port), + fAddress(0) +{ + PRINT(("CMSP3430::CMSP3430()\n")); + + if( fPort.InitCheck() == B_OK ) { + for (fAddress = 0x80; fAddress <= 0x80; fAddress += 0x02) { + if (fPort.Probe(fAddress)) { + PRINT(("CMSP3430::CMSP3430() - Sound Processor found at I2C port 0x%02x\n", fAddress)); + break; + } + } + } + if( InitCheck() != B_OK ) + PRINT(("CMSP3430::CMSP3430() - Sound processor not found!\n")); +} + +CMSP3430::~CMSP3430() +{ + PRINT(("CMSP3430::~CMSP3430()\n")); +} + +status_t CMSP3430::InitCheck() const +{ + status_t res; + + res = fPort.InitCheck(); + if( res != B_OK ) + return res; + + return (fAddress >= 0x80 && fAddress <= 0x80) ? B_OK : B_ERROR; +} + +void CMSP3430::SetEnable(bool enable) +{ + PRINT(("CMSP3430::SetEnable(%d)\n", enable)); + + SetControlRegister(MSP3430_CONTROL_RESET); + SetControlRegister(MSP3430_CONTROL_NORMAL); + + if (enable) { + SetRegister(MSP3430_WR_DEM, MSP3430_DEM_MODUS, 0x2003); + SetRegister(MSP3430_WR_DEM, MSP3430_DEM_STANDARD_SEL, C_MSP3430_BTSC_STEREO); // 0x20 + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_FM_PRESCALE, 0x2403); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_MAIN, 0x0320); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_VOL_MAIN, 0x7300); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_MAIN, 0x0320); + } + else { + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_VOL_MAIN, 0x0000); + } +} + +#if 0 +#pragma mark - + +void CMSP3430::SetI2S(bool fast) +{ + // select 2 x 16 bit (1024 MHz) or 2 x 32 bit (2048) frequency + SetRegister(MSP3430_WR_DEM, I2S_CONFIG, fast ? 0x0001 : 0x0000); +} + +bool CMSP3430::IsSAP() +{ + // bilingual sound mode or SAP present? + if ((Register(MSP3430_RD_DEM, STATUS) & 0x0100) != 0x0000) + return true; + return false; +} + +bool CMSP3430::IsMonoNICAM() +{ + // independent mono sound (only NICAM)? + if ((Register(MSP3430_RD_DEM, STATUS) & 0x0080) != 0x0000) + return true; + return false; +} + +bool CMSP3430::IsStereo() +{ + // mono/stereo indication + if ((Register(MSP3430_RD_DEM, STATUS) & 0x0040) != 0x0000) + return true; + return false; +} + +bool CMSP3430::IsFM() +{ + // is analog sound standard (FM or AM) active? + if ((Register(MSP3430_RD_DEM, STATUS) & 0x0120) == 0x0000) + return true; + return false; +} + +bool CMSP3430::IsNICAM() +{ + // digital sound (NICAM) available? + if ((Register(MSP3430_RD_DEM, STATUS) & 0x0100) == 0x0100) + return true; + return false; +} + +bool CMSP3430::HasSecondaryCarrier() +{ + // detected secondary carrier (2nd A2 or SAP sub-carrier) + if ((Register(MSP3430_RD_DEM, STATUS) & 0x0004) != 0x0000) + return true; + return false; +} + +bool CMSP3430::HasPrimaryCarrier() +{ + // detected primary carrier (Mono or MPX carrier) + if ((Register(MSP3430_RD_DEM, STATUS) & 0x0002) != 0x0000) + return true; + return false; +} + +void CMSP3430::SetStandard(MSP3430_standard standard) +{ + fStandard = standard; + + // set sound mode, FM/AM matrix, source channels + switch (standard) { + C_MSP3430_STANDARD_BG_FM: + C_MSP3430_STANDARD_DK_FM: + // Sound Mode FM Matrix FM/AM Source Stereo A/B Source + // --------------------------------------------------------------- + // Mono Sound A Mono Mono Mono + C_MSP3430_STANDARD_M_KOREA: + C_MSP3430_STANDARD_M_JAPAN: + // Stereo Korean Stereo Stereo Stereo + // Stereo German Stereo Stereo Stereo + // Lang A & B No Matrix Left=A, Right=B Left=A, Right=B + + C_MSP3430_STANDARD_BG_NICAM: + + + } + + SetDemodulator(); +} + +void CMSP3430::SetFMPrescale(int gain, MSP3430_fm_matrix matrix, bool mute) +{ + // set FM/AM prescale and FM matrix mode + fFMPrescale = (mute ? 0x00 : Clamp(gain, 0x00, 0x7f)) << 8; + /// see table 6--17, Appendix B. + switch (matrix) { + case C_MSP3430_MATRIX_NONE: + fFMPrescale |= 0x0000; + break; + case C_MSP3430_MATRIX_GERMAN_STEREO_BG: + fFMPrescale |= 0x0001; + break; + case C_MSP3430_MATRIX_KOREAN_STEREO: + fFMPrescale |= 0x0002; + break; + case C_MSP3430_MATRIX_SOUND_A_MONO: + fFMPrescale |= 0x0003; + break; + case C_MSP3430_MATROX_SOUND_B_MONO: + fFMPrescale |= 0x0004; + break; + } + + SetDemodulator(); +} + +void CMSP3430::SetNICAMPrescale(int gain, bool mute) +{ + // set NICAM prescale to 0..12 dB + fNICAMPrescale = (mute ? 0x00 : Clamp(0x20 + (0x5f * gain) / 12, 0x20, 0x7f)) << 8; + + SetDemodulator(); +} + +void CMSP3430::SetI2SPrescale(int gain, bool mute) +{ + // set digital I2S input prescale 0..18 dB + fI2SPrescale = (mute ? 0x00 : Clamp(0x10 + (0x7f * gain) / 18, 0x10, 0x7f) << 8; + SetSCARTInput(); +} + +void CMSP3430::SetSCARTPrescale(int gain, bool mute) +{ + // set SCART input signal prescale 0..14 dB + fSCARTPrescale = (mute ? 0x00 : Clamp(0x19 + (0x66 * gain) / 14, 0x19, 0x7f) << 8; + SetSCARTInput(); +} + +void CMSP3430::SetSource(MSP3430_source_channel speaker, + MSP3430_source_channel headphone, + MSP3430_source_channel scart1, + MSP3430_source_channel scart2, + MSP3430_source_channel i2s, + MSP3430_source_channel qpeak) +{ + // set source and matrix.. + // select FM/AM, Stereo A/B, Stereo A, Stereo B, SCART, I2S1 or I2S2 source channels + // and select Sound A Mono, Sound B Mono, Stereo or Mono mode + fSrcMain = loudspeaker; + fSrcAux = headphone; + fSrcSCART1 = scart1; + fSrcSCART2 = scart2; + fSrcI2S = i2s; + fSrcQPeak = qpeak; + SetOutputChannels(); +} + + +void CMSP3430::SetVolume(int level, bool mute, MSP3430_clipping_mode mode, + MSP3430_automatic_volume automatic) +{ + // set volume between -114 dB and 12 dB with 1 dB step size + fVolume = (mute ? 0x00 : Clamp(level + 0x73, 0x01, 0x7f)) << 8; + switch (mode) { + case C_MSP3430_CLIP_REDUCE_VOLUME: + fVolume |= 0x00; + break; + case C_MSP3430_CLIP_REDUCE_TONE: + fVolume |= 0x01; + break; + case C_MSP3430_CLIP_COMPROMISE: + fVolume |= 0x02; + break; + case C_MSP3430_CLIP_DYNAMIC: + fVolume |= 0x03; + break; + } + + // enable/disable automatic volume correction + switch (automatic) { + case C_MSP3430_AUTOMATIC_VOLUME_OFF: + fAVC = 0x0000; + break; + case C_MSP3430_AUTOMATIC_VOLUME_20_MS: + fAVC = 0x8100; + break; + case C_MSP3430_AUTOMATIC_VOLUME_2_SEC: + fAVC = 0x8200; + break; + case C_MSP3430_AUTOMATIC_VOLUME_4_SEC: + fAVC = 0x8400; + break; + case C_MSP3430_AUTOMATIC_VOLUME_8_SEC: + fAVC = 0x8800; + break; + } + SetOutputChannels(); +} + +void CMSP3430::SetBalance(int balance, MSP3430_balance_mode mode) +{ + switch (mode) { + case C_MSP3430_BALANCE_LINEAR: + // set balance between -100% (right) and 100% (left) + fBalance = (((0x7f * Clamp(balance, -100, 100)) / 100) << 8) + 0x0000; + break; + + case C_MSP3430_BALANCE_LOGARITHMIC: + // set balance between -128 dB (right) and 127 dB (left) + fBalance = (((0x7f * Clamp(balance, -128, 127)) / 127) << 8) + 0x0001; + break; + } + SetOutputChannels(); +} + + +void CMSP3430::SetEqualizer(int bass, int treble) +{ + // set bass to -12..12 dB or 14..20 dB + if (bass <= 12) + fBass = Clamp(0x00 + 8 * bass, -0x60, 0x60) << 8; + else + fBass = Clamp(0x68 + 4 * (bass - 14), 0x68, 0x7f) << 8; + + // set treble to -12..15 dB + fTreble = Clamp(0x00 + 8 * treble, -0x60, 0x78) << 8; + + // enable bass/treble and disable equalizer + fToneControl = 0x00; + fEqualizer[0] = fEqualizer[1] = fEqualizer[2] = + fEqualizer[3] = fEqualizer[4] = fEqualizer[5] = 0x00; + + SetOutputChannels(); +} + +void CMSP3430::SetEqualizer(int gain[5]) +{ + // set five band equalizer (120 Hz, 500 Hz, 1500 Hz, 5000 Hz, 10000 Hz) + for (int index = 0; index < 5; index++) + fEqualizer[index] = Clamp(0x00 + 8 * gain[index], -0x60, 0x60) << 8; + + // disable bass/treble and enable equalizer + fToneControl = 0xff; + fBass = fTreble = 0x00; + + SetOutputChannels(); +} + +void CMSP3430::SetLoudness(int loudness, MSP3430_loudness_mode mode) +{ + // set loudness gain between 0 dB and 17 dB + fLoudness = Clamp(0x00 + 4 * loudness, 0x00, 0x44) << 8; + switch (mode) { + case C_MSP3430_LOUDNESS_NORMAL: + fLoudness |= 0x0000; + break; + case C_MSP3430_LOUDNESS_SUPER_BASS: + fLoudness |= 0x0004; + break; + } + + SetOutputChannels(); +} + +void CMSP3430::SetSpatial(int strength, MSP3430_spatial_mode mode, MSP3430_spatial_highpass pass) +{ + // enlarge or reduce spatial effects -100% to +100% + fSpatial = Clamp(0x00 + (0x7f * strength) / 100, -0x80, 0x7f) << 8; + switch (mode) { + case C_MSP3430_SPATIAL_MODE_A: + fSpatial |= 0x0000; + break; + case C_MSP3430_SPATIAL_MODE_B: + fSpatial |= 0x0020; + break; + } + switch (pass) { + case C_MSP3430_SPATIAL_MAX_HIGH_PASS: + fSpatial |= 0x0000; + break; + case C_MSP3430_SPATIAL_2_3_HIGH_PASS: + fSpatial |= 0x0002; + break; + case C_MSP3430_SPATIAL_1_3_HIGH_PASS: + fSpatial |= 0x0004; + break; + case C_MSP3430_SPATIAL_MIN_HIGH_PASS: + fSpatial |= 0x0006; + break; + case C_MSP3430_SPATIAL_AUTO_HIGH_PASS: + fSpatial |= 0x0008; + break; + } + SetOutputChannels(); +} + +void CMSP3430::SetSubwoofer(int level, int frequency, bool mute, MSP3430_subwoofer_mode mode) +{ + // set subwoofer level between -128 dB and 12 dB + fSubwooferLevel = (mute ? 0x80 : Clamp(0x00 + level, -128, 12)) << 8; + + // set subwoofer corner frequency between 50..400 Hz + fSubwooferFrequency = Clamp(frequency / 10, 5, 40) << 8; + switch (mode) { + case C_MSP3430_SUBWOOFER_UNFILTERED: + fSubwooferFrequency |= 0x0000; + break; + case C_MSP3430_SUBWOOFER_HIGH_PASS: + fSubwooferFrequency |= 0x0001; + break; + case C_MSP3430_SUBWOOFER_MDB_MAIN: + fSubwooferFrequency |= 0x0002; + break; + } + + // disable MDB? + fMDBStrength = 0x0000; + fMDBLimit = 0x0000; + fMDBHarmonic = 0x0000; + fMDBHighPass = 0x0a00; + fMDBLowPass = 0x0a00; + + SetSubwooferAndMDBOutputChannels(); +} + +void CMSP3430::SetMDB(int strength, int limit, int harmonic, + int minfrequency, int maxfrequency, bool mute) +{ + // set MDB effect strength 0..127 + fMDBStrength = (mute ? 0x00 : Clamp(strength, 0x00, 0x7f)) << 7; + + // set MDB amplitude limit between -32..0 dBFS + fMDBLimit = Clamp(limit, -32, 0) << 8; + + // set MDB harmonic content 0..100% + fMDBHarmonic = Clamp((0x7f * harmonic) / 100, 0x00, 0x7f) << 8; + + // set MDB low pass corner frequency 50..300 Hz + fMDBLowPass = Clamp(minFrequency / 10, 5, 30) << 8; + + // set MDB high pass corner frequency 20..300 Hz + fMDBHighPass = Clamp(maxFrequency / 10, 2, 30) << 8; + + // disable subwoofer level adjustments + fSubwooferLevel = 0x0000; + fSubwooferFrequency = 0x0002; + + // for MDB, use dynamic clipping mode + fVolume = (fVolume & ~0x000f) | 0x0003; + + SetSubwooferAndMDBOutputChannels(); +} + +void CMSP3430::SetSCART(int level1, int level2, bool mute, + MSP3430_scart_input input, + MSP3430_scart_output output1, + MSP3430_scart_output output2) +{ + // set volume SCART1/2 output channel -114..12 dB + fSCART1Volume = (mute ? 0x00 : Clamp(0x73 + (0x7f * level1) / 12, 0x00, 0x7f)) << 8; + fSCART2Volume = (mute ? 0x00 : Clamp(0x73 + (0x7f * level2) / 12, 0x00, 0x7f)) << 8; + + fSCART1Volume |= 0x0001; + fSCART2Volume |= 0x0001; + + // SCART DSP input select + fACB = 0x0000; + switch (input) { + case C_MSP3430_SCART_INPUT_SCART1: + fACB = 0x0000; + break; + case C_MSP3430_SCART_INPUT_MONO: + fACB = 0x0100; + break; + case C_MSP3430_SCART_INPUT_SCART2: + fACB = 0x0200; + break; + case C_MSP3430_SCART_INPUT_SCART3: + fACB = 0x0300; + break; + case C_MSP3430_SCART_INPUT_SCART4: + fACB = 0x0020; + break; + case C_MSP3430_SCART_INPUT_MUTE: + fACB = 0x0320; + break; + } + + // SCART1 output select + switch (output1) { + case C_MSP3430_SCART_OUTPUT_SCART3: + fACB |= 0x0000; + break; + case C_MSP3430_SCART_OUTPUT_SCART2: + fACB |= 0x0400; + break; + case C_MSP3430_SCART_OUTPUT_MONO: + fACB |= 0x0800; + break; + case C_MSP3430_SCART_OUTPUT_SCART1_DA: + fACB |= 0x0c00; + break; + case C_MSP3430_SCART_OUTPUT_SCART2_DA: + fACB |= 0x0080; + break; + case C_MSP3430_SCART_OUTPUT_SCART1_INPUT: + fACB |= 0x0480; + break; + case C_MSP3430_SCART_OUTPUT_SCART4_INPUT: + fACB |= 0x0880; + break; + case C_MSP3430_SCART_OUTPUT_MUTE: + fACB |= 0x0c80; + break; + } + + // SCART2 output select + switch (output2) { + case C_MSP3430_SCART_OUTPUT_SCART3: + fACB |= 0x0000; + break; + case C_MSP3430_SCART_OUTPUT_SCART2: + fACB |= 0x1000; + break; + case C_MSP3430_SCART_OUTPUT_MONO: + fACB |= 0x2000; + break; + case C_MSP3430_SCART_OUTPUT_SCART1_DA: + fACB |= 0x3000; + break; + case C_MSP3430_SCART_OUTPUT_SCART2_DA: + fACB |= 0x0200; + break; + case C_MSP3430_SCART_OUTPUT_SCART1_INPUT: + fACB |= 0x1200; + break; + case C_MSP3430_SCART_OUTPUT_SCART4_INPUT: + fACB |= 0x2200; + break; + case C_MSP3430_SCART_OUTPUT_MUTE: + fACB |= 0x3200; + break; + } + + SetSCARTSignalPath(); + SetOutputChannels(); +} + +void CMSP3430::SetBeeper(int volume, MSP3430_beeper_frequency frequency, bool mute) +{ + // set beeper volume 0..127 + int beeper = (mute ? 0x00 : Clamp(volume, 0x00, 0x7f)) << 8; + + // set beeper frequency 16, 1000 or 4000 Hz + switch (frequency) { + case C_MSP3430_BEEPER_16_HZ: + beeper |= 0x0001; + break; + case C_MSP3430_BEEPER_1_KHZ: + beeper |= 0x0040; + break; + case C_MSP3430_BEEPER_4_KHZ: + beeper |= 0x00ff; + break; + } + SetRegister(MSP3430_WR_DSP, BEEPER, beeper); +} + +void CMSP3430::SetQuasiPeak(int left, int right) +{ + // set quasipeak detector readout -32768..32767 + fQPeakLeft = left; + fQPeakRight = right; + + SetOutputChannels(); +} + +void CMSP3430::GetVersion(char * version) +{ + int revision = Register(MSP3430_RD_DSP, MSP_HARD_REVISION); + int product = Register(MSP3430_RD_DSP, MSP_PRODUCT_ROM); + + sprintf(version, "MSP34%02d%c-%c%d", + ((product & MSP_PRODUCT) >> 8), + ((revision & MSP_REVISION) >> 0) + 0x40, + ((revision & MSP_HARD) >> 8) + 0x40, + ((product & MSP_ROM) >> 0)); +} + +#pragma mark - + +void CMSP3430::Reset() +{ + // software reset + SetControlRegister(MSP3430_CONTROL_RESET); + SetControlRegister(MSP3430_CONTROL_NORMAL); +} + +void CMSP3430::SetSCARTSignalPath() +{ + // select SCART DSP input and output + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_ACB_REG,fACB); +} + +void CMSP3430::SetDemodulator() +{ + // set preferred mode and sound IF input + // (automatic, enable STATUS change, detect 4.5 MHz carrier as BTSC) + SetRegister(MSP3430_WR_DEM, MODUS, 0x2003); + + // set preferred prescale (FM and NICAM) + SetRegister(MSP3430_WR_DSP, PRE_FM, fFMPrescale); + SetRegister(MSP3430_WR_DSP, PRE_NICAM, fNICAMPrescale); + + // automatic sound standard selection + SetRegister(MSP3430_WR_DEM, MSP3430_DEM_STANDARD_SEL, 0x0001); + + // readback the detected TV sound or FM-radio standard + while ((fStandard = Register(MSP3430_RD_DEM, STANDARD_RES)) >= 0x0800) + snooze(100000); +} + +void CMSP3430::SetSCARTInput() +{ + // select preferred prescale for SCART and I2C + SetRegister(MSP3430_WR_DSP, PRE_SCART, fSCARTPrescale); + SetRegister(MSP3430_WR_DSP, PRE_I2S1, fI2SPrescale); + SetRegister(MSP3430_WR_DSP, PRE_I2S2, fI2SPrescale); +} + +void CMSP3430::SetOutputChannels() +{ + // select source channel and matrix for each output + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_MAIN, fMainMatrix); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_AUX, fAuxMatrix); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_SCART1, fSCART1Matrix); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_SCART2, fSCART2Matrix); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_I2S, fI2SMatrix); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SRC_MAT_QPEAK, fQPeakMatrix); + + // set audio baseband processing + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_TONE_MODE, fToneControl); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_BASS_MAIN, fBass); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_BASS_AUX, fBass); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_TREB_MAIN, fTreble); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_TREB_AUX, fTreble); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_EQUAL_BAND1, fEqualizer[0]); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_EQUAL_BAND2, fEqualizer[1]); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_EQUAL_BAND3, fEqualizer[2]); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_EQUAL_BAND4, fEqualizer[3]); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_EQUAL_BAND5, fEqualizer[4]); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_LOUD_MAIN, fLoudness); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_LOUD_AUX, fLoudness); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_SPAT_MAIN, fSpatial); + + // select volume for each output channel + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_VOL_MAIN, fVolume); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_VOL_AUX, fVolume); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_AVC, fAVC); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_BAL_MAIN, fBalance); + SetRegister(MSP3430_WR_DSP, MSP3430_DSP_BAL_AUX, fBalance); + + SetRegister(MSP3430_WR_DSP, VOL_SCART1, fSCART1Volume); + SetRegister(MSP3430_WR_DSP, VOL_SCART2, fSCART2Volume); + + // set quasipeak detector readout + SetRegister(MSP3430_WR_DSP, QPEAK_L, fQPeakLeft); + SetRegister(MSP3430_WR_DSP, QPEAK_R, fQPeakRight); +} + +void CMSP3430::SetSubwooferAndMDBOutputChannels() +{ + // set subwoofer output channel + SetRegister(MSP3430_WR_DSP, SUBW_LEVEL, fSubwooferLevel); + SetRegister(MSP3430_WR_DSP, SUBW_FREQ, fSubwooferFrequency); + + // set MDB control + SetRegister(MSP3430_WR_DSP, MDB_STR, fMDBStrength); + SetRegister(MSP3430_WR_DSP, MDB_LIM, fMDBLimit); + SetRegister(MSP3430_WR_DSP, MDB_HMC, fMDBHarmonic); + SetRegister(MSP3430_WR_DSP, MDB_LP, fMDBLowPass); + SetRegister(MSP3430_WR_DSP, MDB_HP, fMDBHighPass); +} + +#pragma mark - +#endif + +int CMSP3430::ControlRegister() +{ + char message[1], result[2]; + + message[0] = MSP3430_CONTROL; + if (fPort.Write(fAddress, message, sizeof(message), result, sizeof(result))) + return ((result[0] << 8) & 0xff00) + ((result[1] << 0) & 0x00ff); + return 0; +} + +void CMSP3430::SetControlRegister(int value) +{ + char message[3]; + + message[0] = MSP3430_CONTROL; + message[1] = value >> 8; + message[2] = value >> 0; + + if (!fPort.Write(fAddress, message, sizeof(message))) + PRINT(("CMSP3430::SetControl() - error\n")); +} + +int CMSP3430::Register(int address, int subaddress) +{ + char message[3], response[2]; + + message[0] = address; + message[1] = subaddress >> 8; + message[2] = subaddress >> 0; + + if (fPort.Write(fAddress, message, sizeof(message), response, sizeof(response))) + return ((response[0] << 8) & 0xff00) + ((response[1] << 0) & 0x00ff); + return 0; +} + +void CMSP3430::SetRegister(int address, int subaddress, int value) +{ + char message[5]; + + message[0] = address; + message[1] = subaddress >> 8; + message[2] = subaddress >> 0; + message[3] = value >> 8; + message[4] = value >> 0; + + if (!fPort.Write(fAddress, message, sizeof(message))) + PRINT(("CMSP3430::SetRegister() - error\n")); +} diff --git a/src/add-ons/media/media-add-ons/radeon/MSP3430.h b/src/add-ons/media/media-add-ons/radeon/MSP3430.h new file mode 100644 index 0000000000..5700a5940b --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/MSP3430.h @@ -0,0 +1,40 @@ +/****************************************************************************** +/ +/ File: MSP3430.h +/ +/ Description: Micronas Multistandard Sound Processor (MSP) interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#ifndef __MSP3430_H__ +#define __MSP3430_H__ + +#include "I2CPort.h" + +class CMSP3430 { +public: + CMSP3430(CI2CPort & port); + + ~CMSP3430(); + + status_t InitCheck() const; + + void SetEnable(bool enable); + +private: + int ControlRegister(); + + void SetControlRegister(int value); + + int Register(int address, int subaddress); + + void SetRegister(int address, int subaddress, int value); + +private: + CI2CPort & fPort; + int fAddress; +}; + +#endif diff --git a/src/add-ons/media/media-add-ons/radeon/Radeon.cpp b/src/add-ons/media/media-add-ons/radeon/Radeon.cpp new file mode 100644 index 0000000000..488605a6d0 --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/Radeon.cpp @@ -0,0 +1,899 @@ +/****************************************************************************** +/ +/ File: Radeon.cpp +/ +/ Description: ATI Radeon Graphics Chip interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#include +#include +#include +#include +//#include "Driver.h" +#include "Radeon.h" + +static const char * const C_RADEON_REGISTER_AREA_NAME = "RadeonRegisters"; +static const char * const C_RADEON_MEMORY_AREA_NAME = "RadeonMemory"; +static const char * const C_RADEON_ROM_AREA_NAME = "RadeonROM"; + +// CRadeonRect +CRadeonRect::CRadeonRect() + : fLeft(0), + fTop(0), + fRight(0), + fBottom(0) +{ +} + +CRadeonRect::CRadeonRect(int left, int top, int right, int bottom) + : fLeft(left), + fTop(top), + fRight(right), + fBottom(bottom) +{ +} + +int CRadeonRect::Left() const +{ + return fLeft; +} + +int CRadeonRect::Top() const +{ + return fTop; +} + +int CRadeonRect::Right() const +{ + return fRight; +} + +int CRadeonRect::Bottom() const +{ + return fBottom; +} + +int CRadeonRect::Width() const +{ + return fRight - fLeft + 1; +} + +int CRadeonRect::Height() const +{ + return fBottom - fTop + 1; +} + +void CRadeonRect::SetLeft(int value) +{ + fLeft = value; +} + +void CRadeonRect::SetTop(int value) +{ + fTop = value; +} + +void CRadeonRect::SetRight(int value) +{ + fRight = value; +} + +void CRadeonRect::SetBottom(int value) +{ + fBottom = value; +} + +void CRadeonRect::SetTo(int left, int top, int right, int bottom) +{ + fLeft = left; + fTop = top; + fRight = right; + fBottom = bottom; +} + +void CRadeonRect::MoveTo(int left, int top) +{ + fRight += left - fLeft; + fBottom += top - fTop; + fLeft += left - fLeft; + fTop += top - fTop; +} + +void CRadeonRect::ResizeTo(int width, int height) +{ + fRight = fLeft + width - 1; + fBottom = fTop + height - 1; +} + + +// CRadeon +CRadeon::CRadeon( const char *dev_name ) + : fHandle(0), + fRegister(NULL), + fROM(NULL), + fVirtualCard(NULL), + fSharedInfo(NULL), + fRegisterArea(0), + fROMArea(0), + fVirtualCardArea(0), + fSharedInfoArea(0) +{ + PRINT(("CRadeon::CRadeon()\n")); + + if ((fHandle = open(dev_name, O_RDWR | O_CLOEXEC)) < 0) { + PRINT(("CRadeon::CRadeon() - Can't open kernel driver\n")); + return; + } + + radeon_get_private_data gpd; + + if (GetDeviceInformation(gpd) < B_OK) { + PRINT(("CRadeon::CRadeon() - Can't get device information\n")); + return; + } + + CloneArea( + "Radeon virtual card", gpd.virtual_card_area, + &fVirtualCardArea, (void **)&fVirtualCard ); + CloneArea( + "Radeon shared info", gpd.shared_info_area, + &fSharedInfoArea, (void **)&fSharedInfo ); + + if( fSharedInfo != NULL ) { + CloneArea( + "Radeon regs", fSharedInfo->regs_area, + &fRegisterArea, (void **)&fRegister ); + CloneArea( + "Radeon ROM", fSharedInfo->ROM_area, + &fROMArea, (void **)&fROM ); + } + + if (fVirtualCard == NULL || fSharedInfo == NULL || + fROM == NULL || fRegister == NULL) + { + PRINT(("CRadeon::CRadeon() - Can't map memory apertures\n")); + return; + } + + PRINT(("CRadeon::CRadeon() - ATI Radeon found\n")); +} + +CRadeon::~CRadeon() +{ + PRINT(("CRadeon::~CRadeon()\n")); + + if( fVirtualCard != NULL ) + delete_area( fVirtualCardArea ); + + if( fSharedInfo != NULL ) + delete_area( fSharedInfoArea ); + + if (fRegister != NULL) + delete_area( fRegisterArea ); + + if (fROM != NULL) + delete_area( fROMArea ); + + if (fHandle >= 0) + close(fHandle); +} + +status_t CRadeon::InitCheck() const +{ + return + (fHandle >= 0 && + fRegister != NULL && fROM != NULL && + fVirtualCard != NULL && fSharedInfo != NULL) ? B_OK : B_ERROR; +} + +uint32 CRadeon::VirtualMemoryBase() const +{ + return fSharedInfo->memory[mt_local].virtual_addr_start; +} + +int CRadeon::Register(radeon_register index) const +{ + return fRegister[index >> 2]; +} + +void CRadeon::SetRegister(radeon_register index, int value) +{ + fRegister[index >> 2] = value; +} + +int CRadeon::Register(radeon_register index, int mask) const +{ + return fRegister[index >> 2] & mask; +} + +void CRadeon::SetRegister(radeon_register index, int mask, int value) +{ +#ifdef DEBUG + if ((value & ~mask) != 0) + PRINT(("CRadeon::SetRegister(0x%04x, 0x%08x, 0x%08x)\n", index, mask, value)); +#endif + + fRegister[index >> 2] = (fRegister[index >> 2] & ~mask) | (value & mask); +} + +int CRadeon::VIPRegister(int device, int address) +{ + radeon_vip_read vr; + status_t res; + + vr.magic = RADEON_PRIVATE_DATA_MAGIC; + vr.channel = device; + vr.address = address; + + res = ioctl( fHandle, RADEON_VIPREAD, &vr, sizeof( vr )); + + if( res == B_OK ) + return vr.data; + else + return -1; +} + +void CRadeon::SetVIPRegister(int device, int address, int value) +{ + radeon_vip_write vw; + + vw.magic = RADEON_PRIVATE_DATA_MAGIC; + vw.channel = device; + vw.address = address; + vw.data = value; + + ioctl( fHandle, RADEON_VIPWRITE, &vw, sizeof( vw )); +} + +int CRadeon::FindVIPDevice( uint32 device_id ) +{ + radeon_find_vip_device fvd; + status_t res; + + fvd.magic = RADEON_PRIVATE_DATA_MAGIC; + fvd.device_id = device_id; + + res = ioctl( fHandle, RADEON_FINDVIPDEVICE, &fvd, sizeof( fvd )); + + if( res == B_OK ) + return fvd.channel; + else + return -1; +} + +void CRadeon::GetPLLParameters(int & refFreq, int & refDiv, int & minFreq, int & maxFreq, int & xclock) +{ + refFreq = fSharedInfo->pll.ref_freq; + refDiv = fSharedInfo->pll.ref_div; + minFreq = fSharedInfo->pll.min_pll_freq; + maxFreq = fSharedInfo->pll.max_pll_freq; + xclock = fSharedInfo->pll.xclk; +} + +void CRadeon::GetMMParameters(radeon_video_tuner & tuner, + radeon_video_decoder & video, + radeon_video_clock & clock, + int & tunerPort, + int & compositePort, + int & svideoPort) +{ + unsigned char *fVideoBIOS = fROM + fROM[0x48] + (fROM[0x49] << 8); + unsigned char * fMMTable = fROM + fVideoBIOS[0x38] + (fVideoBIOS[0x39] << 8); + + switch (fMMTable[0] & 0x1f) { + case 0x00: + tuner = C_RADEON_NO_TUNER; + break; + case 0x01: + tuner = C_RADEON_FI1236_MK1_NTSC; + break; + case 0x02: + tuner = C_RADEON_FI1236_MK2_NTSC_JAPAN; + break; + case 0x03: + tuner = C_RADEON_FI1216_MK2_PAL_BG; + break; + case 0x04: + tuner = C_RADEON_FI1246_MK2_PAL_I; + break; + case 0x05: + tuner = C_RADEON_FI1216_MF_MK2_PAL_BG_SECAM_L; + break; + case 0x06: + tuner = C_RADEON_FI1236_MK2_NTSC; + break; + case 0x07: + tuner = C_RADEON_FI1256_MK2_SECAM_DK; + break; + case 0x08: + tuner = C_RADEON_FI1236_MK2_NTSC; + break; + case 0x09: + tuner = C_RADEON_FI1216_MK2_PAL_BG; + break; + case 0x0a: + tuner = C_RADEON_FI1246_MK2_PAL_I; + break; + case 0x0b: + tuner = C_RADEON_FI1216_MK2_PAL_BG_SECAM_L; + break; + case 0x0c: + tuner = C_RADEON_FI1236_MK2_NTSC; + break; + case 0x0d: + tuner = C_RADEON_TEMIC_FN5AL_PAL_IBGDK_SECAM_DK; + break; + default: + tuner = C_RADEON_NO_TUNER; + break; + } + + switch (fMMTable[5] & 0x0f) { + case 0x00: + video = C_RADEON_NO_VIDEO; + break; + case 0x01: + video = C_RADEON_BT819; + break; + case 0x02: + video = C_RADEON_BT829; + break; + case 0x03: + video = C_RADEON_BT829A; + break; + case 0x04: + video = C_RADEON_SA7111; + break; + case 0x05: + video = C_RADEON_SA7112; + break; + case 0x06: + video = C_RADEON_RAGE_THEATER; + break; + default: + video = C_RADEON_NO_VIDEO; + break; + } + + switch (fMMTable[5] & 0xf0) { + case 0x00: + case 0x10: + case 0x20: + case 0x30: + clock = C_RADEON_NO_VIDEO_CLOCK; + break; + case 0x40: + clock = C_RADEON_VIDEO_CLOCK_28_63636_MHZ; + break; + case 0x50: + clock = C_RADEON_VIDEO_CLOCK_29_49892_MHZ; + break; + case 0x60: + clock = C_RADEON_VIDEO_CLOCK_27_00000_MHZ; + break; + case 0x70: + clock = C_RADEON_VIDEO_CLOCK_14_31818_MHZ; + break; + default: + clock = C_RADEON_NO_VIDEO_CLOCK; + break; + } + + for (int port = 0; port < 4; port++) { + switch (fMMTable[7 + port] & 0x03) { + case 0x00: + // Unused or Invalid + break; + case 0x01: + // Tuner Input + tunerPort = 0; + break; + case 0x02: + // Front/Rear Composite Input + compositePort = (fMMTable[7 + port] & 0x04 ? 2 : 1); + break; + case 0x03: + // Front/Rear SVideo Input + svideoPort = (fMMTable[7 + port] & 0x04 ? 6 : 5); + break; + } + } +} + +status_t CRadeon::AllocateGraphicsMemory( + memory_type_e memory_type, int32 size, + int32 *offset, int32 *handle ) +{ + radeon_alloc_mem am; + status_t res; + + am.magic = RADEON_PRIVATE_DATA_MAGIC; + am.size = size; + am.memory_type = mt_local; + am.global = false; + + res = ioctl( fHandle, RADEON_ALLOC_MEM, &am ); + + if( res != B_OK ) + return res; + + *handle = am.handle; + *offset = am.offset; + return B_OK; +} + +void CRadeon::FreeGraphicsMemory( + memory_type_e memory_type, int32 handle ) +{ + radeon_free_mem fm; + + fm.magic = RADEON_PRIVATE_DATA_MAGIC; + fm.memory_type = memory_type; + fm.global = false; + fm.handle = handle; + + ioctl( fHandle, RADEON_FREE_MEM, &fm ); +} + +status_t CRadeon::DMACopy( + uint32 src, void *target, size_t size, bool lock_mem, bool contiguous ) +{ + radeon_dma_copy dc; + + dc.magic = RADEON_PRIVATE_DATA_MAGIC; + dc.src = src; + dc.target = target; + dc.size = size; + dc.lock_mem = lock_mem; + dc.contiguous = contiguous; + + return ioctl( fHandle, RADEON_DMACOPY, &dc ); +} + +status_t CRadeon::GetDeviceInformation(radeon_get_private_data & info) +{ + info.magic = RADEON_PRIVATE_DATA_MAGIC; + + return ioctl( fHandle, RADEON_GET_PRIVATE_DATA, &info, sizeof( info )); +} + +status_t CRadeon::CloneArea(const char * name, area_id src_area, + area_id *cloned_area, void ** map) +{ + int res = clone_area( name, map, B_ANY_ADDRESS, + B_READ_AREA | B_WRITE_AREA, src_area ); + + if( res < 0 ) { + return res; + } else { + *cloned_area = res; + return B_OK; + } +} + +status_t CRadeon::WaitInterrupt(int * mask, int * sequence, bigtime_t * time, bigtime_t timeout) +{ + radeon_wait_for_cap_irq wvc; + status_t status; + + wvc.magic = RADEON_PRIVATE_DATA_MAGIC; + wvc.timeout = timeout; + + status = ioctl( fHandle, RADEON_WAIT_FOR_CAP_IRQ, &wvc ); + + if( status == B_OK ) { + *mask = wvc.int_status; + *sequence = wvc.counter; + *time = wvc.timestamp; + } + + return status; +} + +#if 0 +void CRadeon::PrintToStream() +{ + // ATI ROM Signature + if (ROM(0) == 0x55 && ROM(1) == 0xAA) { + for (int offset = 0; offset < 128 - 9; offset++) { + if (ROM(offset + 0) == '7' && + ROM(offset + 1) == '6' && + ROM(offset + 2) == '1' && + ROM(offset + 3) == '2' && + ROM(offset + 4) == '9' && + ROM(offset + 5) == '5' && + ROM(offset + 6) == '5' && + ROM(offset + 7) == '2' && + ROM(offset + 8) == '0') + break; + } + } + + // Video BIOS + unsigned char *fVideoBIOS = fROM + fROM[0x48] + (fROM[0x49] << 8); + + PRINT(( + "----------------------------------------------------------------------\n" + "ATI RADEON VIDEO BIOS\n" + "\n" + "BIOS Revision: %03d.%03d.%03d%03d.%s\n" + "PCI Bus/Device/Function Code: 0x%04x\n" + "BIOS Runtime Segment Address: 0x%04x\n" + "I/O Base Address: 0x%04x\n" + "Subsystem Vendor ID: 0x%04x\n" + "Subsystem ID: 0x%04x\n" + "Post Vendor ID: 0x%04x\n" + "\n", + + // OEM Revision (ID1.ID2.REVISION.CONFIG_FILE) + fVideoBIOS[2], fVideoBIOS[3], + fVideoBIOS[4], fVideoBIOS[5], + fROM + fVideoBIOS[0x10] + (fVideoBIOS[0x11] << 8), + + // PCI bus, device, function code + fVideoBIOS[0x16] + (fVideoBIOS[0x17] << 8), + + // ROM BIOS segment + fVideoBIOS[0x18] + (fVideoBIOS[0x19] << 8), + + // I/O base address + fVideoBIOS[0x1a] + (fVideoBIOS[0x1b] << 8), + + // Subsystem Vendor ID, Subsystem ID, Post Vendor ID + fVideoBIOS[0x1c] + (fVideoBIOS[0x1d] << 8), + fVideoBIOS[0x1e] + (fVideoBIOS[0x1f] << 8), + fVideoBIOS[0x20] + (fVideoBIOS[0x21] << 8) + )); + + + // PLL Information + unsigned char *fPLL = fROM + fVideoBIOS[0x30] + (fVideoBIOS[0x31] << 8); + + PRINT(( + "----------------------------------------------------------------------\n" + "ATI RADEON PLL INFORMATION TABLE\n" + "\n" + "External Clock: %g MHz\n" + "Reference Frequency: %g MHz\n" + "Reference Divisor: %d\n" + "Min PLL Frequency: %g MHz\n" + "Max PLL Frequency: %g MHz\n" + "\n", + (fPLL[0x08] + (fPLL[0x09] << 8)) / 1000.0, + (fPLL[0x0e] + (fPLL[0x0f] << 8)) / 100.0, + fPLL[0x10] + (fPLL[0x11] << 8), + (fPLL[0x12] + (fPLL[0x13] << 8) + (fPLL[0x14] << 16) + (fPLL[0x15] << 24)) / 1000.0, + (fPLL[0x12] + (fPLL[0x16] << 8) + (fPLL[0x17] << 16) + (fPLL[0x18] << 24)) / 1000.0)); + + + // TV Table + unsigned char * fTVTable = fROM + fVideoBIOS[0x32] + (fVideoBIOS[0x33] << 8); + + PRINT(( + "----------------------------------------------------------------------\n" + "ATI RADEON TV INFORMATION TABLE\n" + "\n" + "Table Signature: %c%c%c\n" + "Table Version: %d\n" + "Table Size: %d bytes\n" + "\n" + "TVOut Support: %s\n" + "BIOS built-in TV standard: %s\n" + "TVOut information: %s, %s MHz\n" + "\n" + "Run time supported TV standard:%s%s%s%s%s%s\n" + "Initialization time supported TV standard:%s%s%s%s%s%s\n" + "\n", + + // Table signature, version and size + fTVTable[0x00],fTVTable[0x01], fTVTable[0x02], + fTVTable[0x03], + fTVTable[0x04] + ((int) fTVTable[0x05] << 8), + + // TVOut support + fTVTable[0x06] == 'N' ? "TVOut chip not found" : "TVOut chip on board", + + // BIOS built-in initialization TV standard + (fTVTable[0x07] & 0x0f) == 0x01 ? "NTSC" : + (fTVTable[0x07] & 0x0f) == 0x02 ? "PAL" : + (fTVTable[0x07] & 0x0f) == 0x03 ? "PAL-M" : + (fTVTable[0x07] & 0x0f) == 0x04 ? "PAL-60" : + (fTVTable[0x07] & 0x0f) == 0x05 ? "NTSC-J" : + (fTVTable[0x07] & 0x0f) == 0x06 ? "SCART-PAL" : "Reserved", + + // TV Out information + (fTVTable[0x09] & 0x03) == 0x00 ? "Invalid" : + (fTVTable[0x09] & 0x03) == 0x01 ? "TV off, CRT on" : + (fTVTable[0x09] & 0x03) == 0x02 ? "TV on, CRT off" : "TV on, CRT on", + + (fTVTable[0x09] & 0x0c) == 0x00 ? "29.498928713" : + (fTVTable[0x09] & 0x0c) == 0x04 ? "28.63636" : + (fTVTable[0x09] & 0x0c) == 0x08 ? "14.31818" : "27.0", + + // Runtime supported TV standard + (fTVTable[0x0a] & 0x01) != 0 ? " NTSC" : "", + (fTVTable[0x0a] & 0x02) != 0 ? " PAL" : "", + (fTVTable[0x0a] & 0x04) != 0 ? " PAL-M" : "", + (fTVTable[0x0a] & 0x08) != 0 ? " PAL-60" : "", + (fTVTable[0x0a] & 0x10) != 0 ? " NTSC-J" : "", + (fTVTable[0x0a] & 0x20) != 0 ? " SCART-PAL" : "", + + // Initialization time supported TV standard + (fTVTable[0x0b] & 0x01) != 0 ? " NTSC" : "", + (fTVTable[0x0b] & 0x02) != 0 ? " PAL" : "", + (fTVTable[0x0b] & 0x04) != 0 ? " PAL-M" : "", + (fTVTable[0x0b] & 0x08) != 0 ? " PAL-60" : "", + (fTVTable[0x0b] & 0x10) != 0 ? " NTSC-J" : "", + (fTVTable[0x0b] & 0x20) != 0 ? " SCART-PAL" : "" + )); + + // Hardware Configuration Table + unsigned char * fHWTable = fROM + fVideoBIOS[0x36] + (fVideoBIOS[0x37] << 8); + + PRINT(( + "----------------------------------------------------------------------\n" + "ATI RADEON HARDWARE CONFIGURATION TABLE\n" + "\n" + "Table Signature: %c%c%c%c\n" + "Table Revision: %d\n" + "Table Size: %d\n" + "\n" + "I2C Type: %s\n" + "TVOut Support: %s\n" + "Video Out Crystal: %s\n" + "ImpactTV Data Port: %s\n" + "\n" + "Video Port Capability:\n" + " AMC/DVS0 Video Port: %s\n" + " Zoom Video Port: %s\n" + " AMC/DVS1 Video Port: %s\n" + " VIP 16-bit Video Port: %s\n" + "\n" + "Host Port Configuration: %s\n" + "\n", + + // Table Signature, Revision, Size + fHWTable[0x00], fHWTable[0x01], fHWTable[0x02], fHWTable[0x03], + fHWTable[0x04], fHWTable[0x05], + + // I2C type + (fHWTable[0x06] & 0x0f) == 0x00 ? "Normal GP I/O (data=GP_IO2, clock=GP_IO1)" : + (fHWTable[0x06] & 0x0f) == 0x01 ? "ImpacTV GP I/O" : + (fHWTable[0x06] & 0x0f) == 0x02 ? "Dedicated I2C Pin" : + (fHWTable[0x06] & 0x0f) == 0x03 ? "GP I/O (data=GP_IO12, clock=GP_IO13)" : + (fHWTable[0x06] & 0x0f) == 0x04 ? "GP I/O (data=GPIO12, clock=GPIO10)" : + (fHWTable[0x06] & 0x0f) == 0x05 ? "RAGE THEATER I2C Master" : + (fHWTable[0x06] & 0x0f) == 0x06 ? "Rage128 MPP2 Pin" : + (fHWTable[0x06] & 0x0f) == 0x0f ? "No I2C Configuration" : "Reserved", + + // TVOut support + (fHWTable[0x07] & 0x0f) == 0x00 ? "No TVOut supported" : + (fHWTable[0x07] & 0x0f) == 0x01 ? "ImpactTV1 supported" : + (fHWTable[0x07] & 0x0f) == 0x02 ? "ImpactTV2 supported" : + (fHWTable[0x07] & 0x0f) == 0x03 ? "Improved ImpactTV2 supported" : + (fHWTable[0x07] & 0x0f) == 0x04 ? "RAGE THEATER supported" : "Reserved", + + // Video Out Crystal + (fHWTable[0x07] & 0x70) == 0x00 ? "TVOut not installed" : + (fHWTable[0x07] & 0x70) == 0x10 ? "28.63636 MHz" : + (fHWTable[0x07] & 0x70) == 0x20 ? "29.49892713 MHz" : + (fHWTable[0x07] & 0x70) == 0x30 ? "27.0 MHz" : + (fHWTable[0x07] & 0x70) == 0x40 ? "14.31818 MHz" : "Reserved", + + // ImpactTV data port + (fHWTable[0x07] & 0x80) == 0x00 ? "MPP1" : "MPP2", + + // Video Port Capability + (fHWTable[0x08] & 0x01) == 0x00 ? "Not Supported" : "Supported", + (fHWTable[0x08] & 0x02) == 0x00 ? "Not Supported" : "Supported", + (fHWTable[0x08] & 0x04) == 0x00 ? "Not Supported" : "Supported", + (fHWTable[0x08] & 0x08) == 0x00 ? "Not Supported" : "Supported", + + // Host Port Configuration + (fHWTable[0x09] & 0x0f) == 0x00 ? "No Host Port" : + (fHWTable[0x09] & 0x0f) == 0x01 ? "MPP Host Port" : + (fHWTable[0x09] & 0x0f) == 0x02 ? "2 bit VIP Host Port" : + (fHWTable[0x09] & 0x0f) == 0x03 ? "4 bit VIP Host Port" : + (fHWTable[0x09] & 0x0f) == 0x04 ? "8 bit VIP Host Port" : "Reserved" + )); + + // Multimedia Table + unsigned char * fMMTable = fROM + fVideoBIOS[0x38] + (fVideoBIOS[0x39] << 8); + + PRINT(( + "----------------------------------------------------------------------\n" + "ATI RADEON MULTIMEDIA TABLE\n" + "\n" + "Table Revision: %d\n" + "Table Size: %d bytes\n" + "\n" + "Tuner Chip: %s\n" + "Tuner Input: %s\n" + "Tuner Voltage Regulator: %s\n" + "\n" + "Audio Chip: %s\n" + "FM Audio Decoder: %s\n" + "Audio Scrambling: %s\n" + "\n" + "Product Type: %s, Revision %d\n" + "Product ID: %s\n" + "\n" + "I2S Input Configuration: %s\n" + "I2S Output Configuration: %s\n" + "I2S Audio Chip: %s\n" + "S/PDIF Output Configuration: %s\n" + "\n" + "Video Decoder: %s\n" + "Video Standard/Crystal: %s\n" + "Video Decoder Host Config: %s\n" + "Hardware Teletext: %s\n" + "\n" + "Video Input:\n" + " 0: %s, %s (ID %d)\n" + " 1: %s, %s (ID %d)\n" + " 2: %s, %s (ID %d)\n" + " 3: %s, %s (ID %d)\n" + " 4: %s, %s (ID %d)\n" + "\n", + + /* Hardware Table */ + fMMTable[-2], fMMTable[-1], + + /* Tuner Type */ + (fMMTable[0] & 0x1f) == 0x00 ? "No Tuner" : + (fMMTable[0] & 0x1f) == 0x01 ? "Philips FI1236 MK1 NTSC M/N North America" : + (fMMTable[0] & 0x1f) == 0x02 ? "Philips FI1236 MK2 NTSC M/N Japan" : + + (fMMTable[0] & 0x1f) == 0x03 ? "Philips FI1216 MK2 PAL B/G" : + (fMMTable[0] & 0x1f) == 0x04 ? "Philips FI1246 MK2 PAL I" : + (fMMTable[0] & 0x1f) == 0x05 ? "Philips FI1216 MF MK2 PAL B/G, SECAM L/L'" : + (fMMTable[0] & 0x1f) == 0x06 ? "Philips FI1236 MK2 NTSC M/N North America" : + (fMMTable[0] & 0x1f) == 0x07 ? "Philips FI1256 MK2 SECAM D/K" : + (fMMTable[0] & 0x1f) == 0x08 ? "Philips FM1236 MK2 NTSC M/N North America" : + + (fMMTable[0] & 0x1f) == 0x09 ? "Philips FI1216 MK2 PAL B/G - External Tuner POD" : + (fMMTable[0] & 0x1f) == 0x0a ? "Philips FI1246 MK2 PAL I - External Tuner POD" : + (fMMTable[0] & 0x1f) == 0x0b ? "Philips FI1216 MF MK2 PAL B/G, SECAM L/L' - External Tuner POD" : + (fMMTable[0] & 0x1f) == 0x0c ? "Philips FI1236 MK2 NTSC M/N North America - External Tuner POD" : + + (fMMTable[0] & 0x1f) == 0x0d ? "Temic FN5AL RF3X7595 PAL I/B/G/DK & SECAM DK" : + (fMMTable[0] & 0x1f) == 0x10 ? "Alps TSBH5 NTSC M/N North America" : + (fMMTable[0] & 0x1f) == 0x11 ? "Alps TSC?? NTSC M/N North America" : + (fMMTable[0] & 0x1f) == 0x12 ? "Alps TSCH5 NTSC M/N North America" : + + (fMMTable[0] & 0x1f) == 0x1f ? "Unknown Tuner Type" : "Reserved", + + /* Video Input for Tuner */ + (fMMTable[0] & 0xe0) == 0x00 ? "Video Input 0" : + (fMMTable[0] & 0xe0) == 0x20 ? "Video Input 1" : + (fMMTable[0] & 0xe0) == 0x40 ? "Video Input 2" : + (fMMTable[0] & 0xe0) == 0x60 ? "Video Input 3" : + (fMMTable[0] & 0xe0) == 0x80 ? "Video Input 4" : "Reserved", + + /* Tuner Voltage */ + (fMMTable[3] & 0x03) == 0x00 ? "No Tuner Power down feature" : + (fMMTable[3] & 0x03) == 0x01 ? "Tuner Power down feature" : "Reserved", + + /* Audio Chip Type */ + (fMMTable[1] & 0x0f) == 0x00 ? "Philips TEA5582 NTSC Stereo, no dbx, no volume" : + (fMMTable[1] & 0x0f) == 0x01 ? "Mono with audio mux" : + (fMMTable[1] & 0x0f) == 0x02 ? "Philips TDA9850 NTSC N.A. Stereo, dbx, mux, no volume" : + (fMMTable[1] & 0x0f) == 0x03 ? "Sony CXA2020S Japan NTSC Stereo, mux, no volume" : + (fMMTable[1] & 0x0f) == 0x04 ? "ITT MSP3410D Europe Stereo, volume, internal mux" : + (fMMTable[1] & 0x0f) == 0x05 ? "Crystal CS4236B" : + (fMMTable[1] & 0x0f) == 0x06 ? "Philips TDA9851 NTSC Stereo, volume, no dbx, no mux" : + (fMMTable[1] & 0x0f) == 0x07 ? "ITT MSP3415 (Europe)" : + (fMMTable[1] & 0x0f) == 0x08 ? "ITT MSP3430 (N.A.)" : + (fMMTable[1] & 0x0f) == 0x0f ? "No Audio Chip Installed" : "Reserved", + + /* FM Audio Decoder */ + (fMMTable[3] & 0x30) == 0x00 ? "No FM Audio Decoder" : + (fMMTable[3] & 0x30) == 0x10 ? "FM Audio Decoder (Rohm BA1332F)" : "Reserved", + + /* Audio Scrambling */ + (fMMTable[3] & 0x80) == 0x00 ? "Not Supported" : "Supported", + + /* Product Type */ + (fMMTable[1] & 0x10) == 0x00 ? "OEM Product" : "ATI Product", + + /* OEM Revision */ + (fMMTable[1] & 0xe0) >> 5, + + /* Product ID */ + (fMMTable[1] & 0x10) == 0x00 ? "" : + ( + fMMTable[2] == 0x00 ? "ATI Prototype Board" : + fMMTable[2] == 0x01 ? "ATI All in Wonder" : + fMMTable[2] == 0x02 ? "ATI All in Wonder Pro, no MPEG/DVD decoder" : + fMMTable[2] == 0x03 ? "ATI All in Wonder Pro, CD11 or similar MPEG/DVD decoder on MPP" : + fMMTable[2] == 0x04 ? "ATI All in Wonder Plus" : + fMMTable[2] == 0x05 ? "ATI Kitchener Board" : + fMMTable[2] == 0x06 ? "ATI Toronto Board (analog audio)" : + fMMTable[2] == 0x07 ? "ATI TV-Wonder" : + fMMTable[2] == 0x08 ? "ATI Victoria Board (Rage XL plus RAGE THEATER)" : "Reserved" + ), + + /* I2S Input/Output Configuration */ + (fMMTable[4] & 0x01) == 0x00 ? "Not Supported" : "Supported", + (fMMTable[4] & 0x02) == 0x00 ? "Not Supported" : "Supported", + + /* I2S Audio Chip */ + (fMMTable[4] & 0x1c) == 0x00 ? "TDA1309_32Strap" : + (fMMTable[4] & 0x1c) == 0x04 ? "TDA1309_64Strap" : + (fMMTable[4] & 0x1c) == 0x08 ? "ITT MSP3430" : + (fMMTable[4] & 0x1c) == 0x0c ? "ITT MSP3415" : "Reserved", + + /* S/PDIF Output Config */ + (fMMTable[4] & 0x20) == 0x00 ? "Not Supported" : "Supported", + + /* Video Decoder Type */ + (fMMTable[5] & 0x0f) == 0x00 ? "No Video Decoder" : + (fMMTable[5] & 0x0f) == 0x01 ? "Bt819" : + (fMMTable[5] & 0x0f) == 0x02 ? "Bt829" : + (fMMTable[5] & 0x0f) == 0x03 ? "Bt829A" : + (fMMTable[5] & 0x0f) == 0x04 ? "Philips SA7111" : + (fMMTable[5] & 0x0f) == 0x05 ? "Philips SA7112 or SA7112A" : + (fMMTable[5] & 0x0f) == 0x06 ? "RAGE THEATER" : "Reserved", + + /* Video-In Standard/Crystal */ + (fMMTable[5] & 0xf0) == 0x00 ? "NTSC and PAL Crystals installed" : + (fMMTable[5] & 0xf0) == 0x10 ? "NTSC Crystal only" : + (fMMTable[5] & 0xf0) == 0x20 ? "PAL Crystal only" : + (fMMTable[5] & 0xf0) == 0x30 ? "NTSC, PAL, SECAM Ssingle crystal for Bt829 and Bt879" : + (fMMTable[5] & 0xf0) == 0x40 ? "28.63636 MHz Crystal" : + (fMMTable[5] & 0xf0) == 0x50 ? "29.49892713 MHz Crystal" : + (fMMTable[5] & 0xf0) == 0x60 ? "27.0 MHz Crystal" : + (fMMTable[5] & 0xf0) == 0x70 ? "14.31818 MHz Crystal" : "Reserved", + + /* Video Decoder Host Config */ + (fMMTable[6] & 0x07) == 0x00 ? "I2C Device" : + (fMMTable[6] & 0x07) == 0x01 ? "MPP Device" : + (fMMTable[6] & 0x07) == 0x02 ? "2 bits VIP Device" : + (fMMTable[6] & 0x07) == 0x03 ? "4 bits VIP Device" : + (fMMTable[6] & 0x07) == 0x04 ? "8 bits VIP Device" : + (fMMTable[6] & 0x07) == 0x07 ? "PCI Device" : "Reserved", + + /* Hardware Teletext */ + (fMMTable[3] & 0x0c) == 0x00 ? "No Hardware Teletext" : + (fMMTable[3] & 0x0c) == 0x04 ? "Philips SAA5281" : "Reserved", + + /* Video Input 0 */ + (fMMTable[7] & 0x03) == 0x00 ? "Unused/Invalid" : + (fMMTable[7] & 0x03) == 0x01 ? "Tuner Input" : + (fMMTable[7] & 0x03) == 0x02 ? "Composite Input" : "S-Video Input", + (fMMTable[7] & 0x04) == 0x00 ? "Front Connector" : "Rear Connector", + (fMMTable[7] & 0x38) >> 3, + + /* Video Input 1 */ + (fMMTable[8] & 0x03) == 0x00 ? "Unused/Invalid" : + (fMMTable[8] & 0x03) == 0x01 ? "Tuner Input" : + (fMMTable[8] & 0x03) == 0x02 ? "Composite Input" : "S-Video Input", + (fMMTable[8] & 0x04) == 0x00 ? "Front Connector" : "Rear Connector", + (fMMTable[8] & 0x38) >> 3, + + /* Video Input 2 */ + (fMMTable[9] & 0x03) == 0x00 ? "Unused/Invalid" : + (fMMTable[9] & 0x03) == 0x01 ? "Tuner Input" : + (fMMTable[9] & 0x03) == 0x02 ? "Composite Input" : "S-Video Input", + (fMMTable[9] & 0x04) == 0x00 ? "Front Connector" : "Rear Connector", + (fMMTable[9] & 0x38) >> 3, + + /* Video Input 3 */ + (fMMTable[10] & 0x03) == 0x00 ? "Unused/Invalid" : + (fMMTable[10] & 0x03) == 0x01 ? "Tuner Input" : + (fMMTable[10] & 0x03) == 0x02 ? "Composite Input" : "S-Video Input", + (fMMTable[10] & 0x04) == 0x00 ? "Front Connector" : "Rear Connector", + (fMMTable[10] & 0x38) >> 3, + + /* Video Input 4 */ + (fMMTable[11] & 0x03) == 0x00 ? "Unused/Invalid" : + (fMMTable[11] & 0x03) == 0x01 ? "Tuner Input" : + (fMMTable[11] & 0x03) == 0x02 ? "Composite Input" : "S-Video Input", + (fMMTable[11] & 0x04) == 0x00 ? "Front Connector" : "Rear Connector", + (fMMTable[11] & 0x38) >> 3 + )); +} +#endif diff --git a/src/add-ons/media/media-add-ons/radeon/Radeon.h b/src/add-ons/media/media-add-ons/radeon/Radeon.h new file mode 100644 index 0000000000..b21baea9a7 --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/Radeon.h @@ -0,0 +1,405 @@ +/****************************************************************************** +/ +/ File: Radeon.h +/ +/ Description: ATI Radeon Graphics Chip interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#ifndef __RADEON_H__ +#define __RADEON_H__ + +#include "radeon_interface.h" + +#define BITS(a) int((0xffffffffU>>(31-(1?a)))&(0xffffffffU<<(0?a))) + +enum radeon_video_tuner { + C_RADEON_NO_TUNER = 0, + C_RADEON_FI1236_MK1_NTSC = 1, + C_RADEON_FI1236_MK2_NTSC_JAPAN = 2, + C_RADEON_FI1216_MK2_PAL_BG = 3, + C_RADEON_FI1246_MK2_PAL_I = 4, + C_RADEON_FI1216_MF_MK2_PAL_BG_SECAM_L = 5, + C_RADEON_FI1236_MK2_NTSC = 6, + C_RADEON_FI1256_MK2_SECAM_DK = 7, + C_RADEON_FI1216_MK2_PAL_BG_SECAM_L = 8, + C_RADEON_TEMIC_FN5AL_PAL_IBGDK_SECAM_DK = 9 +}; + +enum radeon_video_clock { + C_RADEON_NO_VIDEO_CLOCK = 0, + C_RADEON_VIDEO_CLOCK_28_63636_MHZ = 1, + C_RADEON_VIDEO_CLOCK_29_49892_MHZ = 2, + C_RADEON_VIDEO_CLOCK_27_00000_MHZ = 3, + C_RADEON_VIDEO_CLOCK_14_31818_MHZ = 4 +}; + +enum radeon_video_decoder { + C_RADEON_NO_VIDEO = 0, + C_RADEON_BT819 = 1, + C_RADEON_BT829 = 2, + C_RADEON_BT829A = 3, + C_RADEON_SA7111 = 4, + C_RADEON_SA7112 = 5, + C_RADEON_RAGE_THEATER = 6 +}; + + +enum radeon_register { + C_RADEON_VIDEOMUX_CNTL = 0x0190, + C_RADEON_VIPH_INT_SEL = BITS(0:0), + C_RADEON_ROM_CLK_DIVIDE = BITS(20:16), + C_RADEON_STR_ROMCLK = BITS(21:21), + C_RADEON_VIP_INTERNAL_DEBUG_SEL = BITS(24:22), + + // I2C + C_RADEON_I2C_CNTL_0 = 0x0090, + C_RADEON_I2C_DONE = BITS(0:0), + C_RADEON_I2C_NACK = BITS(1:1), + C_RADEON_I2C_HALT = BITS(2:2), + C_RADEON_I2C_SOFT_RST = BITS(5:5), + C_RADEON_I2C_DRIVE_EN = BITS(6:6), + C_RADEON_I2C_DRIVE_SEL = BITS(7:7), + C_RADEON_I2C_DRIVE_SEL_10_MCLKS = 0 << 7, + C_RADEON_I2C_DRIVE_SEL_20_MCLKS = 1 << 7, + C_RADEON_I2C_START = BITS(8:8), + C_RADEON_I2C_STOP = BITS(9:9), + C_RADEON_I2C_RECEIVE = BITS(10:10), + C_RADEON_I2C_ABORT = BITS(11:11), + C_RADEON_I2C_GO = BITS(12:12), + C_RADEON_I2C_PRESCALE = BITS(31:16), + + C_RADEON_I2C_CNTL_1 = 0x0094, + C_RADEON_I2C_DATA_COUNT = BITS(3:0), + C_RADEON_I2C_ADDR_COUNT = BITS(10:8), + C_RADEON_I2C_SEL = BITS(16:16), + C_RADEON_I2C_EN = BITS(17:17), + C_RADEON_I2C_TIME_LIMIT = BITS(31:24), + + C_RADEON_I2C_DATA = 0x0098, + C_RADEON_I2C_DATA_MASK = BITS(7:0), + + // Capture + C_RADEON_CAP_INT_CNTL = 0x0908, + C_RADEON_CAP0_BUF0_INT_EN = BITS(0:0), + C_RADEON_CAP0_BUF0_EVEN_INT_EN = BITS(1:1), + C_RADEON_CAP0_BUF1_INT_EN = BITS(2:2), + C_RADEON_CAP0_BUF1_EVEN_INT_EN = BITS(3:3), + C_RADEON_CAP0_VBI0_INT_EN = BITS(4:4), + C_RADEON_CAP0_VBI1_INT_EN = BITS(5:5), + C_RADEON_CAP0_ONESHOT_INT_EN = BITS(6:6), + C_RADEON_CAP0_ANC0_INT_EN = BITS(7:7), + C_RADEON_CAP0_ANC1_INT_EN = BITS(8:8), + C_RADEON_CAP0_VBI2_INT_EN = BITS(9:9), + C_RADEON_CAP0_VBI3_INT_EN = BITS(10:10), + C_RADEON_CAP0_ANC2_INT_EN = BITS(11:11), + C_RADEON_CAP0_ANC3_INT_EN = BITS(12:12), + + C_RADEON_CAP_INT_STATUS = 0x090C, + C_RADEON_CAP0_BUF0_INT = BITS(0:0), + C_RADEON_CAP0_BUF0_INT_AK = BITS(0:0), + C_RADEON_CAP0_BUF0_EVEN_INT = BITS(1:1), + C_RADEON_CAP0_BUF0_EVEN_INT_AK = BITS(1:1), + C_RADEON_CAP0_BUF1_INT = BITS(2:2), + C_RADEON_CAP0_BUF1_INT_AK = BITS(2:2), + C_RADEON_CAP0_BUF1_EVEN_INT = BITS(3:3), + C_RADEON_CAP0_BUF1_EVEN_INT_AK = BITS(3:3), + C_RADEON_CAP0_VBI0_INT = BITS(4:4), + C_RADEON_CAP0_VBI0_INT_AK = BITS(4:4), + C_RADEON_CAP0_VBI1_INT = BITS(5:5), + C_RADEON_CAP0_VBI1_INT_AK = BITS(5:5), + C_RADEON_CAP0_ONESHOT_INT = BITS(6:6), + C_RADEON_CAP0_ONESHOT_INT_AK = BITS(6:6), + C_RADEON_CAP0_ANC0_INT = BITS(7:7), + C_RADEON_CAP0_ANC0_INT_AK = BITS(7:7), + C_RADEON_CAP0_ANC1_INT = BITS(8:8), + C_RADEON_CAP0_ANC1_INT_AK = BITS(8:8), + C_RADEON_CAP0_VBI2_INT = BITS(9:9), + C_RADEON_CAP0_VBI2_INT_AK = BITS(9:9), + C_RADEON_CAP0_VBI3_INT = BITS(10:10), + C_RADEON_CAP0_VBI3_INT_AK = BITS(10:10), + C_RADEON_CAP0_ANC2_INT = BITS(11:11), + C_RADEON_CAP0_ANC2_INT_AK = BITS(11:11), + C_RADEON_CAP0_ANC3_INT = BITS(12:12), + C_RADEON_CAP0_ANC3_INT_AK = BITS(12:12), + + C_RADEON_FCP_CNTL = 0x0910, + C_RADEON_FCP0_SRC_SEL = BITS(2:0), + C_RADEON_FCP0_SRC_PCICLK = 0 << 0, + C_RADEON_FCP0_SRC_PCLK = 1 << 0, + C_RADEON_FCP0_SRC_PCLKb = 2 << 0, + C_RADEON_FCP0_SRC_HREF = 3 << 0, + C_RADEON_FCP0_SRC_GND = 4 << 0, + C_RADEON_FCP0_SRC_HREFb = 5 << 0, + + C_RADEON_CAP0_BUF0_OFFSET = 0x0920, + C_RADEON_CAP0_BUF1_OFFSET = 0x0924, + C_RADEON_CAP0_BUF0_EVEN_OFFSET = 0x0928, + C_RADEON_CAP0_BUF1_EVEN_OFFSET = 0x092C, + + C_RADEON_CAP0_BUF_PITCH = 0x0930, + C_RADEON_CAP0_BUF_PITCH_MASK = BITS(11:0), + + C_RADEON_CAP0_V_WINDOW = 0x0934, + C_RADEON_CAP0_V_START = BITS(11:0), + C_RADEON_CAP0_V_END = BITS(27:16), + + C_RADEON_CAP0_H_WINDOW = 0x0938, + C_RADEON_CAP0_H_START = BITS(11:0), + C_RADEON_CAP0_H_WIDTH = BITS(27:16), + + C_RADEON_CAP0_VBI0_OFFSET = 0x093C, + C_RADEON_CAP0_VBI1_OFFSET = 0x0940, + + C_RADEON_CAP0_VBI_V_WINDOW = 0x0944, + C_RADEON_CAP0_VBI_V_START = BITS(11:0), + C_RADEON_CAP0_VBI_V_END = BITS(27:16), + + C_RADEON_CAP0_VBI_H_WINDOW = 0x0948, + C_RADEON_CAP0_VBI_H_START = BITS(11:0), + C_RADEON_CAP0_VBI_H_WIDTH = BITS(27:16), + + C_RADEON_CAP0_PORT_MODE_CNTL = 0x094C, + C_RADEON_CAP0_PORT_WIDTH = BITS(1:1), + C_RADEON_CAP0_PORT_WIDTH_8_BITS = 0 << 1, + C_RADEON_CAP0_PORT_WIDTH_16_BITS = 1 << 1, + C_RADEON_CAP0_PORT_BYTE_USED = BITS(2:2), + C_RADEON_CAP0_PORT_LOWER_BYTE_USED = 0 << 2, + C_RADEON_CAP0_PORT_UPPER_BYTE_USED = 1 << 2, + + C_RADEON_CAP0_TRIG_CNTL = 0x0950, + C_RADEON_CAP0_TRIGGER_R = BITS(1:0), + C_RADEON_CAP0_TRIGGER_R_COMPLETE = 0 << 0, + C_RADEON_CAP0_TRIGGER_R_PENDING = 1 << 0, + C_RADEON_CAP0_TRIGGER_R_IN_PROGRESS = 2 << 0, + C_RADEON_CAP0_TRIGGER_W = BITS(0:0), + C_RADEON_CAP0_TRIGGER_W_NO_ACTION = 0 << 0, + C_RADEON_CAP0_TRIGGER_W_CAPTURE = 1 << 0, + C_RADEON_CAP0_EN = BITS(4:4), + C_RADEON_CAP0_VSYNC_CNT_R = BITS(15:8), + C_RADEON_CAP0_VSYNC_CLR = BITS(16:16), + + C_RADEON_CAP0_DEBUG = 0x0954, + C_RADEON_CAP0_H_STATUS = BITS(11:0), + C_RADEON_CAP0_V_STATUS = BITS(27:16), + C_RADEON_CAP0_V_SYNC = BITS(28:28), + + C_RADEON_CAP0_CONFIG = 0x0958, + C_RADEON_CAP0_INPUT_MODE = BITS(0:0), + C_RADEON_CAP0_INPUT_MODE_ONESHOT = 0 << 0, + C_RADEON_CAP0_INPUT_MODE_CONTINUOUS = 1 << 0, + C_RADEON_CAP0_START_FIELD = BITS(1:1), + C_RADEON_CAP0_START_ODD_FIELD = 0 << 1, + C_RADEON_CAP0_START_EVEN_FIELD = 1 << 1, + C_RADEON_CAP0_START_BUF_R = BITS(2:2), + C_RADEON_CAP0_START_BUF_W = BITS(3:3), + C_RADEON_CAP0_BUF_TYPE = BITS(5:4), + C_RADEON_CAP0_BUF_TYPE_FIELD = 0 << 4, + C_RADEON_CAP0_BUF_TYPE_ALTERNATING = 1 << 4, + C_RADEON_CAP0_BUF_TYPE_FRAME = 2 << 4, + C_RADEON_CAP0_ONESHOT_MODE = BITS(6:6), + C_RADEON_CAP0_ONESHOT_MODE_FIELD = 0 << 6, + C_RADEON_CAP0_ONESHOT_MODE_FRAME = 1 << 6, + C_RADEON_CAP0_BUF_MODE = BITS(8:7), + C_RADEON_CAP0_BUF_MODE_SINGLE = 0 << 7, + C_RADEON_CAP0_BUF_MODE_DOUBLE = 1 << 7, + C_RADEON_CAP0_BUF_MODE_TRIPLE = 2 << 7, + C_RADEON_CAP0_MIRROR_EN = BITS(9:9), + C_RADEON_CAP0_ONESHOT_MIRROR_EN = BITS(10:10), + C_RADEON_CAP0_VIDEO_SIGNED_UV = BITS(11:11), + C_RADEON_CAP0_ANC_DECODE_EN = BITS(12:12), + C_RADEON_CAP0_VBI_EN = BITS(13:13), + C_RADEON_CAP0_SOFT_PULL_DOWN_EN = BITS(14:14), + C_RADEON_CAP0_VIP_EXTEND_FLAG_EN = BITS(15:15), + C_RADEON_CAP0_FAKE_FIELD_EN = BITS(16:16), + C_RADEON_CAP0_FIELD_START_LINE_DIFF = BITS(18:17), + C_RADEON_CAP0_HORZ_DOWN = BITS(20:19), + C_RADEON_CAP0_HORZ_DOWN_1X = 0 << 19, + C_RADEON_CAP0_HORZ_DOWN_2X = 1 << 19, + C_RADEON_CAP0_HORZ_DOWN_3X = 2 << 19, + C_RADEON_CAP0_VERT_DOWN = BITS(22:21), + C_RADEON_CAP0_VERT_DOWN_1X = 0 << 21, + C_RADEON_CAP0_VERT_DOWN_2X = 1 << 21, + C_RADEON_CAP0_VERT_DOWN_3X = 2 << 21, + C_RADEON_CAP0_STREAM_FORMAT = BITS(25:23), + C_RADEON_CAP0_STREAM_BROOKTREE = 0 << 23, + C_RADEON_CAP0_STREAM_CCIR656 = 1 << 23, + C_RADEON_CAP0_STREAM_ZV = 2 << 23, + C_RADEON_CAP0_STREAM_VIP = 3 << 23, + C_RADEON_CAP0_STREAM_TRANSPORT = 4 << 23, + C_RADEON_CAP0_HDWNS_DEC = BITS(26:26), + C_RADEON_CAP0_DOWNSCALER = 0 << 26, + C_RADEON_CAP0_DECIMATOR = 1 << 26, + C_RADEON_CAP0_VIDEO_IN_FORMAT = BITS(29:29), + C_RADEON_CAP0_VIDEO_IN_YVYU422 = 0 << 29, + C_RADEON_CAP0_VIDEO_IN_VYUY422 = 1 << 29, + C_RADEON_CAP0_VBI_HORZ_DOWN = BITS(31:30), + C_RADEON_CAP0_VBI_HORZ_DOWN_1X = 0 << 30, + C_RADEON_CAP0_VBI_HORZ_DOWN_2X = 1 << 30, + C_RADEON_CAP0_VBI_HORZ_DOWN_4X = 2 << 30, + + C_RADEON_CAP0_ANC0_OFFSET = 0x095C, + C_RADEON_CAP0_ANC1_OFFSET = 0x0960, + + C_RADEON_CAP0_ANC_H_WINDOW = 0x0964, + C_RADEON_CAP0_ANC_WIDTH = BITS(11:0), + + C_RADEON_CAP0_VIDEO_SYNC_TEST = 0x0968, + C_RADEON_CAP0_TEST_VID_SOF = BITS(0:0), + C_RADEON_CAP0_TEST_VID_EOF = BITS(1:1), + C_RADEON_CAP0_TEST_VID_EOL = BITS(2:2), + C_RADEON_CAP0_TEST_VID_FIELD = BITS(3:3), + C_RADEON_CAP0_TEST_SYNC_EN = BITS(5:5), + + C_RADEON_CAP0_ONESHOT_BUF_OFFSET = 0x096C, + + C_RADEON_CAP0_BUF_STATUS = 0x0970, + C_RADEON_CAP0_PRE_VID_BUF = BITS(1:0), + C_RADEON_CAP0_CUR_VID_BUF = BITS(3:2), + C_RADEON_CAP0_PRE_FIELD = BITS(4:4), + C_RADEON_CAP0_CUR_FIELD = BITS(5:5), + C_RADEON_CAP0_PRE_VBI_BUF = BITS(7:6), + C_RADEON_CAP0_CUR_VBI_BUF = BITS(9:8), + C_RADEON_CAP0_VBI_BUF_STATUS = BITS(10:10), + C_RADEON_CAP0_PRE_ANC_BUF = BITS(12:11), + C_RADEON_CAP0_CUR_ANC_BUF = BITS(14:13), + C_RADEON_CAP0_ANC_BUF_STATUS = BITS(16:16), + C_RADEON_CAP0_ANC_PRE_BUF_CNT = BITS(27:16), + C_RADEON_CAP0_VIP_INC = BITS(28:28), + C_RADEON_CAP0_VIP_PRE_REPEAT_FIELD = BITS(29:29), + C_RADEON_CAP0_CAP_BUF_STATUS = BITS(30:30), + + C_RADEON_CAP0_VBI2_OFFSET = 0x0980, + C_RADEON_CAP0_VBI3_OFFSET = 0x0984, + C_RADEON_CAP0_ANC2_OFFSET = 0x0988, + C_RADEON_CAP0_ANC3_OFFSET = 0x098C, + + C_RADEON_VBI_BUFFER_CONTROL = 0x0900, + C_RADEON_CAP0_BUFFER_WATER_MARK = BITS(4:0), + C_RADEON_FULL_BUFFER_EN = BITS(16:16), + C_RADEON_CAP0_ANC_VBI_QUAD_BUF = BITS(17:17), + C_RADEON_VID_BUFFER_RESET = BITS(20:20), + C_RADEON_CAP_SWAP = BITS(22:21), + C_RADEON_CAP0_BUFFER_EMPTY_R = BITS(24:24), + + // Test and Debug control + C_RADEON_TEST_DEBUG_CNTL = 0x0120, + C_RADEON_TEST_DEBUG_OUT_EN = 0x00000001 +}; + + +class CRadeonRect { +public: + CRadeonRect(); + + CRadeonRect(int left, int top, int right, int bottom); + + int Left() const; + + int Top() const; + + int Right() const; + + int Bottom() const; + + int Width() const; + + int Height() const; + + void SetLeft(int value); + + void SetTop(int value); + + void SetRight(int value); + + void SetBottom(int value); + + void SetTo(int left, int top, int right, int bottom); + + void MoveTo(int left, int top); + + void ResizeTo(int width, int height); + +private: + int fLeft; + int fTop; + int fRight; + int fBottom; +}; + +class CRadeon { +public: + CRadeon( const char *dev_name ); + + ~CRadeon(); + + status_t InitCheck() const; + + uint32 VirtualMemoryBase() const; + +public: + int Register(radeon_register index) const; + + void SetRegister(radeon_register index, int value); + + int Register(radeon_register index, int mask) const; + + void SetRegister(radeon_register index, int mask, int value); + + int VIPRegister(int device, int address); + + void SetVIPRegister(int device, int address, int value); + + int FindVIPDevice( uint32 device_id ); + +public: + void GetPLLParameters(int & refFreq, int & refDiv, int & minFreq, int & maxFreq, int & xclock); + + void GetMMParameters(radeon_video_tuner & tuner, + radeon_video_decoder & video, + radeon_video_clock & clock, + int & tunerPort, + int & compositePort, + int & svideoPort); + +public: + status_t AllocateGraphicsMemory( + memory_type_e memory_type, int32 size, + int32 *offset, int32 *handle ); + + void FreeGraphicsMemory( + memory_type_e memory_type, int32 handle ); + + status_t DMACopy( + uint32 src, void *target, size_t size, bool lock_mem, bool contiguous ); + +public: + status_t GetDeviceInformation(radeon_get_private_data & info); + + status_t WaitInterrupt(int * mask, int * sequence, bigtime_t * time, bigtime_t timeout); + + status_t CloneArea(const char * name, area_id src_area, + area_id *cloned_area, void ** map); + +private: + int fHandle; + unsigned int * fRegister; + unsigned char * fROM; + virtual_card *fVirtualCard; + shared_info *fSharedInfo; + + area_id fRegisterArea; + area_id fROMArea; + area_id fVirtualCardArea; + area_id fSharedInfoArea; +}; + +template +inline T Clamp(T value, T min, T max) +{ + return (value < min ? min : value > max ? max : value); +} + +#endif diff --git a/src/add-ons/media/media-add-ons/radeon/RadeonAddOn.cpp b/src/add-ons/media/media-add-ons/radeon/RadeonAddOn.cpp new file mode 100644 index 0000000000..c20346166c --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/RadeonAddOn.cpp @@ -0,0 +1,432 @@ +/****************************************************************************** +/ +/ File: RadeonAddOn.cpp +/ +/ Description: ATI Radeon Video Capture Media AddOn for BeOS. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "RadeonAddOn.h" +#include "RadeonProducer.h" + +#define DPRINT(args) { PRINT(("\x1b[0;30;35m")); PRINT(args); PRINT(("\x1b[0;30;47m")); } + +CRadeonPlug::CRadeonPlug( CRadeonAddOn *aaddon, const BPath &adev_path, int aid ) + : addon( aaddon ), dev_path( adev_path ), id( aid ), node( NULL ) +{ + fFlavorInfo.name = const_cast("Radeon In"); + fFlavorInfo.info = const_cast("Radeon Video In Media Node"); + fFlavorInfo.kinds = B_BUFFER_PRODUCER | B_CONTROLLABLE | B_PHYSICAL_INPUT; + fFlavorInfo.flavor_flags = 0; + fFlavorInfo.internal_id = aid; + fFlavorInfo.possible_count = 1; + + fFlavorInfo.in_format_count = 0; + fFlavorInfo.in_format_flags = 0; + fFlavorInfo.in_formats = NULL; + + fFlavorInfo.out_format_count = 4; + //fFlavorInfo.out_format_count = 1; + fFlavorInfo.out_format_flags = 0; + + fMediaFormat[0].type = B_MEDIA_RAW_VIDEO; + fMediaFormat[0].u.raw_video = media_raw_video_format::wildcard; + fMediaFormat[0].u.raw_video.interlace = 1; + fMediaFormat[0].u.raw_video.display.format = B_RGB32; + + fMediaFormat[1].type = B_MEDIA_RAW_VIDEO; + fMediaFormat[1].u.raw_video = media_raw_video_format::wildcard; + fMediaFormat[1].u.raw_video.interlace = 1; + fMediaFormat[1].u.raw_video.display.format = B_RGB16; + + fMediaFormat[2].type = B_MEDIA_RAW_VIDEO; + fMediaFormat[2].u.raw_video = media_raw_video_format::wildcard; + fMediaFormat[2].u.raw_video.interlace = 1; + fMediaFormat[2].u.raw_video.display.format = B_RGB15; + + fMediaFormat[3].type = B_MEDIA_RAW_VIDEO; + fMediaFormat[3].u.raw_video = media_raw_video_format::wildcard; + fMediaFormat[3].u.raw_video.interlace = 1; + fMediaFormat[3].u.raw_video.display.format = B_YCbCr422; + + /*fMediaFormat[0].type = B_MEDIA_RAW_VIDEO; + fMediaFormat[0].u.raw_video = media_raw_video_format::wildcard; + fMediaFormat[0].u.raw_video.interlace = 1; + fMediaFormat[0].u.raw_video.display.format = B_YCbCr422;*/ + + fFlavorInfo.out_formats = fMediaFormat; + + readSettings(); +} + +BPath +CRadeonPlug::getSettingsPath() +{ + BPath path; + + if( find_directory( B_USER_CONFIG_DIRECTORY, &path ) != B_OK ) + return BPath(); + + path.Append( "settings/Media/RadeonIn" ); + + create_directory( path.Path(), 755 ); + + BString id_string; + + id_string << "settings" << id; + + path.Append( id_string.String() ); + + return path; +} + +void +CRadeonPlug::writeSettings( BMessage *new_settings ) +{ + BMessage cur_settings; + + // if new_settings are provided, use them; else, ask node for settings + // (needed during shutdown where node cannot reply anymore) + if( new_settings != NULL ) { + cur_settings = *new_settings; + + } else { + if( node == NULL ) + return; + + if( addon->GetConfigurationFor( node, &cur_settings ) != B_OK ) + return; + } + + BMallocIO old_settings_flat, new_settings_flat; + + settings.Flatten( &old_settings_flat ); + cur_settings.Flatten( &new_settings_flat ); + + if( old_settings_flat.BufferLength() == new_settings_flat.BufferLength() && + memcmp( old_settings_flat.Buffer(), new_settings_flat.Buffer(), old_settings_flat.BufferLength() ) == 0 ) + return; + + settings = cur_settings; + + BPath settings_path = getSettingsPath(); + + BFile file( settings_path.Path(), B_WRITE_ONLY | B_CREATE_FILE ); + + settings.Flatten( &file ); +} + +void +CRadeonPlug::readSettings() +{ + BPath settings_path = getSettingsPath(); + + BFile file( settings_path.Path(), B_READ_ONLY ); + + settings.Unflatten( &file ); +} + + +CRadeonAddOn::CRadeonAddOn(image_id imid) + : BMediaAddOn(imid), + settings_thread( -1 ), settings_thread_sem( -1 ) +{ + DPRINT(("CRadeonAddOn::CRadeonAddOn()\n")); + + fInitStatus = B_NO_INIT; + + if( RecursiveScan( "/dev/video/radeon" ) != B_OK ) + return; + + if( (settings_thread_sem = create_sem( 0, "Radeon In settings" )) < 0 ) + return; + + if( INIT_BEN( plug_lock, "Radeon device list" ) < 0 ) + return; + + if( (settings_thread = spawn_thread( + settings_writer, "Radeon In settings", B_LOW_PRIORITY, this )) < 0 ) + return; + + resume_thread( settings_thread ); + + fInitStatus = B_OK; +} + +CRadeonAddOn::~CRadeonAddOn() +{ + status_t dummy; + + DPRINT(("CRadeonAddOn::~CRadeonAddOn()\n")); + + release_sem( settings_thread_sem ); + wait_for_thread( settings_thread, &dummy ); + + delete_sem( settings_thread_sem ); + + for( int32 i = 0; i < fDevices.CountItems(); ++i ) { + CRadeonPlug *plug = (CRadeonPlug *)fDevices.ItemAt(i); + + delete plug; + } + + DELETE_BEN( plug_lock ); +} + + +status_t +CRadeonAddOn::InitCheck(const char **out_failure_text) +{ + DPRINT(("CRadeonAddOn::InitCheck()\n")); + + if (fInitStatus < B_OK) { + *out_failure_text = "Unknown error"; + return fInitStatus; + } + + return B_OK; +} + +int32 +CRadeonAddOn::settings_writer( void *param ) +{ + ((CRadeonAddOn *)param)->settingsWriter(); + return B_OK; +} + +void +CRadeonAddOn::writeSettings() +{ + ACQUIRE_BEN( plug_lock ); + + for( int32 i = 0; i < fDevices.CountItems(); ++i ) { + CRadeonPlug *plug = (CRadeonPlug *)fDevices.ItemAt(i); + + plug->writeSettings( NULL ); + } + + RELEASE_BEN( plug_lock ); +} + +void +CRadeonAddOn::settingsWriter() +{ + while( acquire_sem_etc( settings_thread_sem, 1, B_RELATIVE_TIMEOUT, 10000000 ) == B_TIMED_OUT ) { + writeSettings(); + } +} + +void +CRadeonAddOn::UnregisterNode( BMediaNode *node, BMessage *settings ) +{ + ACQUIRE_BEN( plug_lock ); + + for( int32 i = 0; i < fDevices.CountItems(); ++i ) { + CRadeonPlug *plug = (CRadeonPlug *)fDevices.ItemAt(i); + + if( plug->getNode() == node ) { + // write last settings, so they don't get lost + plug->writeSettings( settings ); + plug->setNode( NULL ); + break; + } + } + + RELEASE_BEN( plug_lock ); +} + +int32 +CRadeonAddOn::CountFlavors() +{ + DPRINT(("CRadeonAddOn::CountFlavors()\n")); + + if (fInitStatus < B_OK) + return fInitStatus; + + return fDevices.CountItems(); +} + +/* + * The pointer to the flavor received only needs to be valid between + * successive calls to BCRadeonAddOn::GetFlavorAt(). + */ +status_t +CRadeonAddOn::GetFlavorAt(int32 n, const flavor_info **out_info) +{ + DPRINT(("CRadeonAddOn::GetFlavorAt()\n")); + + if (fInitStatus < B_OK) + return fInitStatus; + + if (n < 0 || n >= fDevices.CountItems() ) + return B_BAD_INDEX; + + /* Return the flavor defined in the constructor */ + *out_info = ((CRadeonPlug *)fDevices.ItemAt(n))->getFlavorInfo(); + return B_OK; +} + +BMediaNode * +CRadeonAddOn::InstantiateNodeFor( + const flavor_info *info, BMessage *config, status_t *out_error) +{ + DPRINT(("CRadeonAddOn::InstantiateNodeFor()\n")); + + CRadeonProducer *node; + + if (fInitStatus < B_OK) + return NULL; + + if (info->internal_id < 0 || info->internal_id >= fDevices.CountItems()) + return NULL; + + CRadeonPlug *plug = (CRadeonPlug *)fDevices.ItemAt( info->internal_id ); + + ACQUIRE_BEN( plug_lock ); + + if( plug->getNode() != NULL ) { + *out_error = B_BUSY; + node = NULL; + + } else { + BMessage single_settings; + + // under R5, configuration is always an empty message, so we need to + // get our own configuration + if( config == NULL || 1 ) + config = plug->getSettings(); + + node = new CRadeonProducer( this, plug->getName(), + plug->getDeviceName(), info->internal_id, config ); + + if (node && (node->InitCheck() < B_OK)) { + *out_error = node->InitCheck(); + delete node; + node = NULL; + } + } + + plug->setNode( node ); + + RELEASE_BEN( plug_lock ); + + return node; +} + +status_t CRadeonAddOn::GetConfigurationFor( + BMediaNode *your_node, + BMessage *into_message ) +{ + port_id reply_port; + CRadeonProducer::configuration_msg msg; + status_t res; + + reply_port = create_port( 1, "GetConfiguration Reply" ); + if( reply_port < 0 ) + return reply_port; + + msg.reply_port = reply_port; + + res = write_port_etc( your_node->ControlPort(), CRadeonProducer::C_GET_CONFIGURATION, + &msg, sizeof( msg ), B_TIMEOUT, 10000000 ); + if( res == B_OK ) { + ssize_t reply_size; + CRadeonProducer::configuration_msg_reply *reply; + int32 code; + + reply_size = port_buffer_size_etc( reply_port, B_TIMEOUT, 10000000 ); + if( reply_size < 0 ) + res = reply_size; + else { + reply = (CRadeonProducer::configuration_msg_reply *)malloc( reply_size ); + + res = read_port( reply_port, &code, reply, reply_size ); + if( res == reply_size ) { + if( code != CRadeonProducer::C_GET_CONFIGURATION_REPLY ) + res = B_ERROR; + else { + res = reply->res; + + if( res == B_OK ) + res = into_message->Unflatten( &reply->config ); + } + } + + free( reply ); + } + } + + delete_port( reply_port ); + + return res; +} + +status_t +CRadeonAddOn::RecursiveScan(const char* rootPath, BEntry *rootEntry = NULL) +{ + BDirectory root; + + if( rootEntry != NULL ) + root.SetTo( rootEntry ); + else if( rootPath != NULL ) { + root.SetTo( rootPath ); + } else { + PRINT(("Error in MultiAudioAddOn::RecursiveScan null params\n")); + return B_ERROR; + } + + BEntry entry; + int cur_id = 0; + + while( root.GetNextEntry( &entry ) > B_ERROR ) { + if(entry.IsDirectory()) { + RecursiveScan( rootPath, &entry ); + + } else { + BPath path; + + entry.GetPath(&path); + + CRadeon device( path.Path() ); + + if( device.InitCheck() != B_OK) + continue; + + CVIPPort vip_port( device ); + + // if there is a Rage Theatre, then there should be Video-In + if( vip_port.InitCheck() == B_OK && + vip_port.FindVIPDevice( + (C_THEATER_VIP_VENDOR_ID << 0) | + (C_THEATER_VIP_DEVICE_ID << 16)) >= 0 ) + { + fDevices.AddItem( new CRadeonPlug( this, path, cur_id++ )); + } + } + } + + return B_OK; +} + + +BMediaAddOn * +make_media_addon(image_id imid) +{ + return new CRadeonAddOn(imid); +} diff --git a/src/add-ons/media/media-add-ons/radeon/RadeonAddOn.h b/src/add-ons/media/media-add-ons/radeon/RadeonAddOn.h new file mode 100644 index 0000000000..bb5a18591c --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/RadeonAddOn.h @@ -0,0 +1,99 @@ +/****************************************************************************** +/ +/ File: RadeonAddOn.h +/ +/ Description: ATI Radeon Video Capture Media AddOn for BeOS. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#ifndef __RADEON_ADDON_H__ +#define __RADEON_ADDON_H__ + +#include +#include +#include "benaphore.h" + +#define TOUCH(x) ((void)(x)) + +extern "C" _EXPORT BMediaAddOn *make_media_addon(image_id you); + +class CRadeonAddOn; + +// descriptor of a possible Radeon node +class CRadeonPlug { +public: + CRadeonPlug( CRadeonAddOn *addon, const BPath &dev_path, int id ); + ~CRadeonPlug() {} + + const char *getName() { return fFlavorInfo.name; } + const char *getDeviceName() { return dev_path.Path(); } + const char *getDeviceShortName() { return dev_path.Leaf(); } + flavor_info *getFlavorInfo() { return &fFlavorInfo; } + BMediaNode *getNode() { return node; } + void setNode( BMediaNode *anode ) { node = anode; } + BMessage *getSettings() { return &settings; } + + void writeSettings( BMessage *new_settings ); + +private: + CRadeonAddOn *addon; // (global) addon (only needed for GetConfiguration()) + BPath dev_path; // path of device driver + int id; // id of plug + flavor_info fFlavorInfo; // flavor of plug (contains unique id) + BMediaNode *node; // active node (or NULL) + BMessage settings; // settings for this node + media_format fMediaFormat[4]; + + void readSettings(); + BPath getSettingsPath(); +}; + +class CRadeonAddOn : public BMediaAddOn +{ +public: + CRadeonAddOn(image_id imid); + virtual ~CRadeonAddOn(); + + virtual status_t InitCheck(const char **out_failure_text); + + virtual int32 CountFlavors(); + virtual status_t GetFlavorAt(int32 n, const flavor_info ** out_info); + virtual BMediaNode *InstantiateNodeFor( + const flavor_info * info, + BMessage * config, + status_t * out_error); + + virtual status_t SaveConfigInfo(BMediaNode *node, BMessage *message) + { TOUCH(node); TOUCH(message); return B_OK; } + + virtual bool WantsAutoStart() { return false; } + virtual status_t AutoStart(int in_count, BMediaNode **out_node, + int32 *out_internal_id, bool *out_has_more) + { TOUCH(in_count); TOUCH(out_node); + TOUCH(out_internal_id); TOUCH(out_has_more); + return B_ERROR; } + + virtual status_t GetConfigurationFor( + BMediaNode *your_node, + BMessage *into_message ); + +// private methods + void UnregisterNode( BMediaNode *node, BMessage *settings ); + +private: + status_t fInitStatus; + BList fDevices; // list of BPath of found devices + thread_id settings_thread; + sem_id settings_thread_sem; + benaphore plug_lock; + BMessage settings; + + status_t RecursiveScan( const char* rootPath, BEntry *rootEntry = NULL ); + static int32 settings_writer( void *param ); + void settingsWriter(); + void writeSettings(); +}; + +#endif diff --git a/src/add-ons/media/media-add-ons/radeon/RadeonProducer.cpp b/src/add-ons/media/media-add-ons/radeon/RadeonProducer.cpp new file mode 100644 index 0000000000..d2c33e3b9e --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/RadeonProducer.cpp @@ -0,0 +1,1964 @@ +/****************************************************************************** +/ +/ File: RadeonProducer.cpp +/ +/ Description: ATI Radeon Video Producer media node. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include "RadeonAddOn.h" + +#define DPRINT(args) { PRINT(("\x1b[0;30;35m")); PRINT(args); PRINT(("\x1b[0;30;47m")); } + +#define TOUCH(x) ((void)(x)) + +#define PRINTF(a,b) \ + do { \ + if (a < 2) { \ + printf("CRadeonProducer::"); \ + printf b; \ + } \ + } while (0) + +#include "RadeonProducer.h" + +// convert Be video standard to video-in standard; +// Be forgot some standards and define code 7 and 8 to be MPEG1/2, i.e. +// didn't leave any space for enhancements, so I chose to use 101 and up; +// this way, we get a scattered list of video standards, needing special +// functions to convert to scattered Be-code to the compact video-in-code +video_in_standard BeToVideoInStandard( int32 be_standard ) +{ + switch( be_standard ) { + case 1: return C_VIDEO_IN_NTSC; + case 2: return C_VIDEO_IN_NTSC_JAPAN; + case 3: return C_VIDEO_IN_PAL_BDGHI; + case 4: return C_VIDEO_IN_PAL_M; + case 5: return C_VIDEO_IN_PAL_N; + case 6: return C_VIDEO_IN_SECAM; + case 101: return C_VIDEO_IN_NTSC_443; + case 102: return C_VIDEO_IN_PAL_60; + case 103: return C_VIDEO_IN_PAL_NC; + default: return C_VIDEO_IN_NTSC; + } +} + +int32 VideoInStandardToBe( video_in_standard standard ) +{ + int32 be_standard[] = { + 1, 2, 101, 4, 3, 5, 102, 103, 6 + }; + + if( (uint)standard < sizeof( be_standard ) / sizeof( be_standard[0] ) ) + return be_standard[(int)standard]; + else + return 1; +} + +status_t CRadeonProducer::FindInt32( + BMessage *config, EOptions option, int32 min_value, int32 max_value, + int32 default_value, int32 *value ) +{ + char name[5]; + status_t res; + + *value = default_value; + + *(int32 *)name = option; + name[4] = 0; + + res = config->FindInt32( name, value ); + if( res == B_NAME_NOT_FOUND ) + return B_OK; + + if( res != B_OK ) + return res; + + *value = MAX( *value, min_value ); + *value = MIN( *value, max_value ); + return B_OK; +} + +CRadeonProducer::CRadeonProducer( + CRadeonAddOn *addon, const char *name, const char *device_name, int32 internal_id, + BMessage *config ) + : BMediaNode(name), + BMediaEventLooper(), + BBufferProducer(B_MEDIA_RAW_VIDEO), + BControllable(), + fVideoIn( device_name ) +{ + DPRINT(("CRadeonProducer::CRadeonProducer()\n")); + + fInitStatus = B_NO_INIT; + + fInternalID = internal_id; + fAddOn = addon; + + fBufferGroup = NULL; + //fUsedBufferGroup = NULL; + + fProcessingLatency = 0LL; + + //fConnected = false; + fEnabled = true; + + AddNodeKind(B_PHYSICAL_INPUT); + + if( fVideoIn.InitCheck() == B_OK ) + fInitStatus = B_OK; + + fSource = ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) != 0 ? C_VIDEO_IN_TUNER : C_VIDEO_IN_COMPOSITE); + fStandard = 1; + fMode = C_VIDEO_IN_WEAVE; + fFormat = B_RGB32; + fResolution = 4; + fTuner = 25; + fBrightness = 0; + fContrast = 0; + fSaturation = 0; + fHue = 0; + fSharpness = 0; + + if( config != NULL ) { + status_t res; + int32 standard; + + if( (res = FindInt32( config, P_SOURCE, 0, C_VIDEO_IN_SOURCE_MAX, + (fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) != 0 ? C_VIDEO_IN_TUNER : C_VIDEO_IN_COMPOSITE, + &fSource )) != B_OK || + (res = FindInt32( config, P_STANDARD, 0, C_VIDEO_IN_STANDARD_MAX, + C_VIDEO_IN_NTSC, &standard )) != B_OK || + (res = FindInt32( config, P_MODE, 0, C_VIDEO_IN_CAPTURE_MODE_MAX, + C_VIDEO_IN_FIELD, &fMode )) != B_OK || + (res = FindInt32( config, P_FORMAT, -2147483647L-1, 2147483647L, + B_RGB16, &fFormat )) != B_OK || + (res = FindInt32( config, P_RESOLUTION, 0, C_RESOLUTION_MAX, + 4, &fResolution )) != B_OK || + (res = FindInt32( config, P_TUNER, 0, C_CHANNEL_MAX, + 25, &fTuner )) != B_OK || + (res = FindInt32( config, P_BRIGHTNESS, -100, +100, + 0, &fBrightness )) != B_OK || + (res = FindInt32( config, P_CONTRAST, 0, 100, + 0, &fContrast )) != B_OK || + (res = FindInt32( config, P_SATURATION, -100, +100, + 0, &fSaturation )) != B_OK || + (res = FindInt32( config, P_HUE, -90, +90, + 0, &fHue )) != B_OK || + (res = FindInt32( config, P_SHARPNESS, 0, 15, + 0, &fSharpness )) != B_OK ) + { + DPRINT(("Corrupted settings (%s)\n", strerror( res ))); + } + + // standard is stored as internal code (which has no "holes" in its numbering); + // time to convert it + fStandard = VideoInStandardToBe( (video_in_standard)standard ); + + // if there is no tuner, force composite input + if( (fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) == 0 ) + fSource = C_VIDEO_IN_COMPOSITE; + + // format ids are scattered, so we must verify them manually + switch( fFormat ) { + case B_YCbCr422: + case B_GRAY8: + case B_RGB15: + case B_RGB16: + case B_RGB32: + break; + default: + fFormat = B_RGB16; + } + } + + fSourceLastChange = + fStandardLastChange = + fModeLastChange = + fFormatLastChange = + fResolutionLastChange = + fTunerLastChange = + fBrightnessLastChange = + fContrastLastChange = + fSaturationLastChange = + fHueLastChange = + fSharpnessLastChange = system_time(); + + fOutput.destination = media_destination::null; + strcpy(fOutput.name, Name()); + + // we provide interlaced raw video in any format + fOutput.format.type = B_MEDIA_RAW_VIDEO; + fOutput.format.u.raw_video = media_raw_video_format::wildcard; +} + + +void CRadeonProducer::setupWeb() +{ + /////////// + /* Set up the parameter web */ + + // in "kind" value of parameters is "stampTV-compatible", i.e. + // if not defined, we use the name used in stampTV + BParameterWeb *web = new BParameterWeb(); + BParameterGroup *controls = web->MakeGroup("Controls"); + BParameterGroup *options = web->MakeGroup("Video"); + /*BParameterGroup *audio = web->MakeGroup("Audio");*/ + + BParameterGroup *controls1 = controls->MakeGroup("Controls1"); + BParameterGroup *controls2 = controls->MakeGroup("Controls2"); + BParameterGroup *controls3 = controls->MakeGroup("Controls3"); + + BParameterGroup *options1 = options->MakeGroup("Options1"); + BParameterGroup *options2 = options->MakeGroup("Options2"); + + /*BParameterGroup *audio1 = audio->MakeGroup("Audio1"); + BParameterGroup *audio2 = audio->MakeGroup("Audio2");*/ + + + // Controls + if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) != 0) { + // Controls.Channel + BDiscreteParameter *tuner = controls1->MakeDiscreteParameter( + P_TUNER, B_MEDIA_NO_TYPE, "Channel:", B_TUNER_CHANNEL); + + for (int channel = 0; channel <= 125; channel++) { + char buffer[32]; + sprintf(buffer, "%d", channel); + tuner->AddItem(channel, buffer); + } + } + + // Controls.Source + BDiscreteParameter *source = controls1->MakeDiscreteParameter( + P_SOURCE, B_MEDIA_RAW_VIDEO, "Video Input:", "Video Input:"); + + if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) != 0) + source->AddItem(C_VIDEO_IN_TUNER, "Tuner"); + if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_COMPOSITE) != 0) + source->AddItem(C_VIDEO_IN_COMPOSITE, "Composite"); + if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_SVIDEO) != 0) + source->AddItem(C_VIDEO_IN_SVIDEO, "SVideo"); + + // TODO: + BDiscreteParameter *source2 = controls1->MakeDiscreteParameter( + P_AUDIO_SOURCE, B_MEDIA_RAW_VIDEO, "Audio Input:", "Audio Input:"); + if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) != 0) + source2->AddItem(C_VIDEO_IN_TUNER, "Tuner"); + if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_COMPOSITE) != 0) + source2->AddItem(C_VIDEO_IN_COMPOSITE, "Composite"); + if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_SVIDEO) != 0) + source2->AddItem(C_VIDEO_IN_SVIDEO, "SVideo"); + + + // Controls.Brightness/Contrast/Saturation/Hue + controls2->MakeContinuousParameter(P_BRIGHTNESS, B_MEDIA_RAW_VIDEO,"Brightness", "BRIGHTNESS", "", -100, 100, 1); + controls2->MakeContinuousParameter(P_CONTRAST, B_MEDIA_RAW_VIDEO, "Contrast", "CONTRAST", "", 0, 100, 1); + controls2->MakeContinuousParameter(P_SHARPNESS, B_MEDIA_RAW_VIDEO, "Sharpness", B_LEVEL, "dB", 0, 15, 1); + + controls3->MakeContinuousParameter(P_SATURATION, B_MEDIA_RAW_VIDEO, "Saturation", "SATURATION", "", -100, 100, 1); + controls3->MakeContinuousParameter(P_HUE, B_MEDIA_RAW_VIDEO, "Hue", B_LEVEL, "°", -90, 90, 1); + + + // Options.Resolution + BDiscreteParameter *resolution = options1->MakeDiscreteParameter( + P_RESOLUTION, B_MEDIA_RAW_VIDEO, "Default Image Size:", B_RESOLUTION); + + resolution->AddItem(6, "768x576"); + resolution->AddItem(5, "720x576"); + resolution->AddItem(4, "720x480"); + resolution->AddItem(0, "640x480"); + resolution->AddItem(3, "480x360"); + resolution->AddItem(1, "320x240"); + resolution->AddItem(2, "160x120"); + + // Options.Format + BDiscreteParameter *format = options1->MakeDiscreteParameter( + P_FORMAT, B_MEDIA_RAW_VIDEO, "Default Colors:", B_COLOR_SPACE); + + format->AddItem(B_YCbCr422, "YCbCr422 (fastest)"); + format->AddItem(B_GRAY8, "8 Bits/Pixel (gray)"); + format->AddItem(B_RGB15, "15 Bits/Pixel"); + format->AddItem(B_RGB16, "16 Bits/Pixel"); + format->AddItem(B_RGB32, "32 Bits/Pixel"); + + // Options.Standard + BDiscreteParameter *standard = options2->MakeDiscreteParameter( + P_STANDARD, B_MEDIA_RAW_VIDEO, "Video Format:", B_VIDEO_FORMAT); + + standard->AddItem(1, "NTSC"); + standard->AddItem(2, "NTSC Japan"); + standard->AddItem(101, "NTSC 443"); + standard->AddItem(4, "PAL M"); + standard->AddItem(3, "PAL BDGHI"); + standard->AddItem(5, "PAL N"); + standard->AddItem(102, "PAL 60"); + standard->AddItem(103, "PAL NC"); + standard->AddItem(6, "SECAM"); + + // Options.Mode + BDiscreteParameter *mode = options2->MakeDiscreteParameter( + P_MODE, B_MEDIA_RAW_VIDEO, "Video Interlace:", B_GENERIC); + + mode->AddItem(C_VIDEO_IN_FIELD, "Field"); + mode->AddItem(C_VIDEO_IN_BOB, "Bob"); + mode->AddItem(C_VIDEO_IN_WEAVE, "Weave"); + + + // TODO: + /* + BDiscreteParameter *standard2 = audio1->MakeDiscreteParameter( + P_AUDIO_FORMAT, B_MEDIA_RAW_VIDEO, "Audio Format:", B_VIDEO_FORMAT); + standard2->AddItem(0, "Stereo"); + standard2->AddItem(1, "Mono"); + standard2->AddItem(2, "NICAM"); + + BDiscreteParameter *audioSource = audio1->MakeDiscreteParameter( + P_AUDIO_FORMAT, B_MEDIA_RAW_VIDEO, "Audio Source:", B_VIDEO_FORMAT); + audioSource->AddItem(0, "FM"); + audioSource->AddItem(1, "Stereo"); + audioSource->AddItem(2, "SCART"); + audioSource->AddItem(3, "Language A"); + audioSource->AddItem(4, "Language B"); + + BDiscreteParameter *matrix= audio2->MakeDiscreteParameter( + P_AUDIO_FORMAT, B_MEDIA_RAW_VIDEO, "Audio Matrix:", B_VIDEO_FORMAT); + matrix->AddItem(0, "Sound A"); + matrix->AddItem(1, "Sound B"); + matrix->AddItem(2, "Stereo"); + matrix->AddItem(3, "Mono");*/ + + /* After this call, the BControllable owns the BParameterWeb object and + * will delete it for you */ + SetParameterWeb(web); + ///////// +} + +CRadeonProducer::~CRadeonProducer() +{ + DPRINT(("CRadeonProducer::~CRadeonProducer()\n")); + + if (fInitStatus == B_OK) { + /* Clean up after ourselves, in case the application didn't make us + * do so. */ + /*if (fConnected) + Disconnect(fOutput.source, fOutput.destination);*/ + + HandleStop(); + } + + delete fBufferGroup; + fBufferGroup = NULL; + + BMessage settings; + + GetConfiguration( &settings ); + + fAddOn->UnregisterNode( this, &settings ); + + Quit(); +} + +/* BMediaNode */ + +port_id +CRadeonProducer::ControlPort() const +{ + return BMediaNode::ControlPort(); +} + +BMediaAddOn * +CRadeonProducer::AddOn(int32 *internal_id) const +{ + if (internal_id) + *internal_id = fInternalID; + return fAddOn; +} + +status_t +CRadeonProducer::HandleMessage(int32 message, const void *data, size_t size) +{ + //DPRINT(("CRadeonProducer::HandleMessage()\n")); + + switch( message ) { + case C_GET_CONFIGURATION: { + const configuration_msg *request = (const configuration_msg *)data; + BMessage msg; + configuration_msg_reply *reply; + size_t reply_size, config_size; + status_t res; + + if( size < sizeof( configuration_msg )) + return B_ERROR; + + res = GetConfiguration( &msg ); + + config_size = msg.FlattenedSize(); + reply_size = sizeof( *reply ) + config_size; + reply = (configuration_msg_reply *)malloc( reply_size ); + if( reply == NULL ) + return B_NO_MEMORY; + + reply->res = res; + reply->config_size = config_size; + msg.Flatten( &reply->config, config_size ); + + write_port_etc( request->reply_port, C_GET_CONFIGURATION_REPLY, + reply, reply_size, B_TIMEOUT, 0 ); + + free( reply ); + return B_OK; + } + default: + return B_ERROR; + // return BControllable::HandleMessage(message, data, size); + } +} + +void +CRadeonProducer::Preroll() +{ + /* This hook may be called before the node is started to give the hardware + * a chance to start. */ + DPRINT(("CRadeonProducer::Preroll()\n")); +} + +void +CRadeonProducer::SetTimeSource(BTimeSource *time_source) +{ + DPRINT(("CRadeonProducer::SetTimeSource()\n")); + + /* Tell frame generation thread to recalculate delay value */ + //release_sem(fFrameSync); +} + +status_t +CRadeonProducer::RequestCompleted(const media_request_info &info) +{ + DPRINT(("CRadeonProducer::RequestCompleted()\n")); + + return BMediaNode::RequestCompleted(info); +} + +/* BMediaEventLooper */ + +void +CRadeonProducer::NodeRegistered() +{ + DPRINT(("CRadeonProducer::NodeRegistered()\n")); + + if (fInitStatus != B_OK) { + ReportError(B_NODE_IN_DISTRESS); + return; + } + + setupWeb(); +//!!// + + fOutput.node = Node(); + fOutput.source.port = ControlPort(); + fOutput.source.id = 0; + + /* Tailor these for the output of your device */ + /******** + fOutput.format.type = B_MEDIA_RAW_VIDEO; + fOutput.format.u.raw_video = media_raw_video_format::wildcard; +// fOutput.format.u.raw_video.interlace = 1; + fOutput.format.u.raw_video.display.format = B_RGB32; + ********/ + + // up to 60 frames (NTSC); jitter less then half a frame; processing time + // depends on whether colour space conversion is required, let's say half + // a frame in worst case + SetPriority( suggest_thread_priority( B_VIDEO_RECORDING, 60, 8000, 8000 )); + + /* Start the BMediaEventLooper control loop running */ + Run(); +} + +void +CRadeonProducer::Start(bigtime_t performance_time) +{ + DPRINT(("CRadeonProducer::Start()\n")); + + BMediaEventLooper::Start(performance_time); +} + +void +CRadeonProducer::Stop(bigtime_t performance_time, bool immediate) +{ + DPRINT(("CRadeonProducer::Stop()\n")); + + BMediaEventLooper::Stop(performance_time, immediate); +} + +void +CRadeonProducer::Seek(bigtime_t media_time, bigtime_t performance_time) +{ + DPRINT(("CRadeonProducer::Seek()\n")); + + BMediaEventLooper::Seek(media_time, performance_time); +} + +void +CRadeonProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time) +{ + DPRINT(("CRadeonProducer::TimeWarp()\n")); + + BMediaEventLooper::TimeWarp(at_real_time, to_performance_time); +} + +status_t +CRadeonProducer::AddTimer(bigtime_t at_performance_time, int32 cookie) +{ + DPRINT(("CRadeonProducer::AddTimer()\n")); + + return BMediaEventLooper::AddTimer(at_performance_time, cookie); +} + +void +CRadeonProducer::SetRunMode(run_mode mode) +{ + DPRINT(("CRadeonProducer::SetRunMode()\n")); + + BMediaEventLooper::SetRunMode(mode); +} + +void +CRadeonProducer::HandleEvent(const media_timed_event *event, + bigtime_t lateness, bool realTimeEvent) +{ + //DPRINT(("CRadeonProducer::HandleEvent()\n")); + + TOUCH(lateness); TOUCH(realTimeEvent); + + switch(event->type) + { + case BTimedEventQueue::B_START: + HandleStart(event->event_time); + break; + case BTimedEventQueue::B_STOP: + HandleStop(); + break; + case BTimedEventQueue::B_WARP: + HandleTimeWarp(event->bigdata); + break; + case BTimedEventQueue::B_SEEK: + HandleSeek(event->bigdata); + break; + case BTimedEventQueue::B_HANDLE_BUFFER: + case BTimedEventQueue::B_DATA_STATUS: + case BTimedEventQueue::B_PARAMETER: + default: + PRINTF(-1, ("HandleEvent: Unhandled event -- %lx\n", event->type)); + break; + case BTimedEventQueue::B_HARDWARE: + HandleHardware(); + break; + } + + //DPRINT(("CRadeonProducer::HandleEvent() done\n")); +} + +void +CRadeonProducer::CleanUpEvent(const media_timed_event *event) +{ + DPRINT(("CRadeonProducer::CleanUpEvent()\n")); + + BMediaEventLooper::CleanUpEvent(event); +} + +bigtime_t +CRadeonProducer::OfflineTime() +{ + return BMediaEventLooper::OfflineTime(); +} + +void +CRadeonProducer::ControlLoop() +{ + BMediaEventLooper::ControlLoop(); +} + +status_t +CRadeonProducer::DeleteHook(BMediaNode * node) +{ + DPRINT(("CRadeonProducer::DeleteHook()\n")); + + return BMediaEventLooper::DeleteHook(node); +} + + + +/* BBufferProducer */ + +// choose capture mode according to format and update format according to that +status_t +CRadeonProducer::verifySetMode( media_format *format ) +{ + float frame_rate = fVideoIn.getFrameRate( + BeToVideoInStandard( fStandard )) / 1000.0f; + + if( format->u.raw_video.interlace == media_raw_video_format::wildcard.interlace ) { + if( format->u.raw_video.field_rate == media_raw_video_format::wildcard.field_rate ) { + format->u.raw_video.interlace = fMode == C_VIDEO_IN_BOB ? 2 : 1; + format->u.raw_video.field_rate = frame_rate * format->u.raw_video.interlace; + } else { + if( format->u.raw_video.field_rate == frame_rate ) + format->u.raw_video.interlace = 1; + else if( format->u.raw_video.field_rate == frame_rate * 2 ) + format->u.raw_video.interlace = 2; + else { + DPRINT(( "Unsupported field rate for active TV standard (%f)\n", + format->u.raw_video.field_rate )); + return B_MEDIA_BAD_FORMAT; + } + } + + } else if( format->u.raw_video.interlace == 1 ) { + if( format->u.raw_video.field_rate == media_raw_video_format::wildcard.field_rate ) + format->u.raw_video.field_rate = frame_rate; + else { + // don't compare directly - there are rounding errors + if( fabs(format->u.raw_video.field_rate - frame_rate) > 0.001 ) { + DPRINT(( "Wrong field rate for active TV standard (%f) in progressive mode (expected %f)\n", + format->u.raw_video.field_rate - 29.976, + frame_rate - 29.976 )); + return B_MEDIA_BAD_FORMAT; + } + } + + } else if( format->u.raw_video.interlace == 2 ) { + if( format->u.raw_video.field_rate == media_raw_video_format::wildcard.field_rate ) + format->u.raw_video.field_rate = frame_rate * 2; + else { + if( fabs(format->u.raw_video.field_rate - frame_rate * 2) > 0.001 ) { + DPRINT(( "Wrong field rate for active TV standard (%f) in interlace mode\n", + format->u.raw_video.field_rate )); + return B_MEDIA_BAD_FORMAT; + } + } + + } else { + DPRINT(( "Invalid interlace mode (%d)\n", format->u.raw_video.interlace )); + return B_MEDIA_BAD_FORMAT; + } + + return B_OK; +} + +/* + Map BeOS capture mode to internal capture mode. +*/ +int32 +CRadeonProducer::extractCaptureMode( const media_format *format ) +{ + // if application requests interlace, it always gets BOB; + // if is requests non-interlace, it may get WEAVE or FIELD - + // if the user selected one of them, we are fine; else, + // we always choose WEAVE (could choose FIELD as well, make + // it dependant on resolution, but the more magic the more problems) + if( format->u.raw_video.interlace == 2 ) + return C_VIDEO_IN_BOB; + else if( fMode == C_VIDEO_IN_BOB ) + return C_VIDEO_IN_WEAVE; + else + return fMode; +} + +// check pixel aspect of format and set it if it's wildcarded +status_t +CRadeonProducer::verifySetPixelAspect( media_format *format ) +{ + // for simplicity, we always assume 1:1 aspect + if( format->u.raw_video.pixel_width_aspect != media_raw_video_format::wildcard.pixel_width_aspect || + format->u.raw_video.pixel_height_aspect != media_raw_video_format::wildcard.pixel_height_aspect ) + { + if( format->u.raw_video.pixel_width_aspect != + format->u.raw_video.pixel_height_aspect ) + { + DPRINT(( "Unsupported pixel aspect (%d:%d)\n", + format->u.raw_video.pixel_width_aspect, + format->u.raw_video.pixel_height_aspect )); + return B_MEDIA_BAD_FORMAT; + } + } else { + format->u.raw_video.pixel_width_aspect = 1; + format->u.raw_video.pixel_height_aspect = 1; + } + + return B_OK; + +#if 0 + // we assume 1:2 for interlaced and 1:1 for deinterlaced video + // (this is not really true as it depends on TV standard and + // resolution, but it should be enough for start) + if( format->u.raw_video.pixel_width_aspect != media_raw_video_format::wildcard.pixel_width_aspect || + format->u.raw_video.pixel_height_aspect != media_raw_video_format::wildcard.pixel_height_aspect ) + { + double ratio = mode == C_VIDEO_IN_WEAVE ? 1 : 0.5; + + if( (float)format->u.raw_video.pixel_width_aspect / + format->u.raw_video.pixel_height_aspect != ratio ) + { + DPRINT(( "Unsupported pixel aspect (%d:%d)\n", + format->u.raw_video.pixel_width_aspect, + format->u.raw_video.pixel_height_aspect )); + return B_MEDIA_BAD_FORMAT; + } + } else { + format->u.raw_video.pixel_width_aspect = 1; + format->u.raw_video.pixel_height_aspect = + mode == C_VIDEO_IN_WEAVE ? 1 : 2; + } + + return B_OK; +#endif +} + +// verify active range defined as format +status_t +CRadeonProducer::verifyActiveRange( media_format *format ) +{ + CRadeonRect active_rect; + + fVideoIn.getActiveRange( BeToVideoInStandard( fStandard ), active_rect ); + + if( format->u.raw_video.first_active != media_raw_video_format::wildcard.first_active ) { + if( (int32)format->u.raw_video.first_active < 0 ) { + DPRINT(( "Unsupported first_active (%d)\n", format->u.raw_video.first_active )); + return B_MEDIA_BAD_FORMAT; + } + } + + // don't care about last_active much - some programs set it to number of + // captured lines, which is really something different + // (I have the feeling, noone really knows how to use this value properly) + if( format->u.raw_video.last_active != media_raw_video_format::wildcard.last_active ) { + if( format->u.raw_video.last_active >= (uint32)active_rect.Height() ) { + DPRINT(( "Unsupported last_active (%d)\n", format->u.raw_video.last_active )); + return B_MEDIA_BAD_FORMAT; + } + } + + return B_OK; +} + + +// set active range in format if yet undefined +void +CRadeonProducer::setActiveRange( media_format *format ) +{ + CRadeonRect active_rect; + + fVideoIn.getActiveRange( BeToVideoInStandard( fStandard ), active_rect ); + + if( format->u.raw_video.first_active == media_raw_video_format::wildcard.first_active ) + format->u.raw_video.first_active = 0; + + if( format->u.raw_video.last_active == media_raw_video_format::wildcard.last_active ) + format->u.raw_video.last_active = (uint32)active_rect.Height() - 1; +} + + +// verify requested orientation +status_t +CRadeonProducer::verifyOrientation( media_format *format ) +{ + if( format->u.raw_video.orientation != media_raw_video_format::wildcard.orientation ) { + if( format->u.raw_video.orientation != B_VIDEO_TOP_LEFT_RIGHT ) { + DPRINT(( "Unsupported orientation (%d)\n", format->u.raw_video.orientation )); + return B_MEDIA_BAD_FORMAT; + } + } + + return B_OK; +} + + +// set image orientation if yet undefined +void +CRadeonProducer::setOrientation( media_format *format ) +{ + if( format->u.raw_video.orientation == media_raw_video_format::wildcard.orientation ) + format->u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT; +} + + +// verify requested pixel format +status_t +CRadeonProducer::verifyPixelFormat( media_format *format ) +{ + if( format->u.raw_video.display.format != + media_raw_video_format::wildcard.display.format ) + { + switch( format->u.raw_video.display.format ) { + case B_RGB32: + case B_RGB16: + case B_RGB15: + case B_YCbCr422: + case B_GRAY8: + break; + + default: + DPRINT(("Unsupported colour space (%x)\n", + format->u.raw_video.display.format )); + return B_MEDIA_BAD_FORMAT; + } + } + + return B_OK; +} + + +// set pixel format to user-defined default if not set yet +void +CRadeonProducer::setPixelFormat( media_format *format ) +{ + if( format->u.raw_video.display.format == + media_raw_video_format::wildcard.display.format ) + format->u.raw_video.display.format = (color_space)fFormat; +} + +/* + Verify video size and set it if undefined. +*/ +status_t +CRadeonProducer::verifySetSize( + media_format *format, int32 mode, bool set_bytes_per_row ) +{ + CRadeonRect active_rect; + + fVideoIn.getActiveRange( BeToVideoInStandard( fStandard ), active_rect ); + + // width and height must both be defined, else we define it ourself, + // i.e. if the application leaves one of them wildcarded, we + // set both + if( format->u.raw_video.display.line_width != + media_raw_video_format::wildcard.display.line_width && + format->u.raw_video.display.line_count != + media_raw_video_format::wildcard.display.line_count ) + { + uint32 max_height = active_rect.Height(); + + if( mode != C_VIDEO_IN_WEAVE ) + max_height /= 2; + + if( format->u.raw_video.display.line_width > (uint32)active_rect.Width() || + format->u.raw_video.display.line_count > max_height ) + { + DPRINT(("Requested image size is too high (%dx%d)\n", + format->u.raw_video.display.line_width, + format->u.raw_video.display.line_count)); + return B_MEDIA_BAD_FORMAT; + } + + // our format converters do up to 8 pixels at a time (grey8); + // to be absolutely sure we don't get trouble there, refuse + // any width that is not a multiple of 8 + if( (format->u.raw_video.display.line_width & 7) != 0 ) { + DPRINT(( "Request image width is not multiple of 8 (%d)\n", + format->u.raw_video.display.line_width )); + return B_MEDIA_BAD_FORMAT; + } + } else { + switch (fResolution) { + case 0: + format->u.raw_video.display.line_width = 640; + format->u.raw_video.display.line_count = 480; + break; + case 3: + format->u.raw_video.display.line_width = 480; + format->u.raw_video.display.line_count = 360; + break; + case 4: + format->u.raw_video.display.line_width = 720; + format->u.raw_video.display.line_count = 480; + break; + case 5: + format->u.raw_video.display.line_width = 720; + format->u.raw_video.display.line_count = 576; + break; + case 6: + format->u.raw_video.display.line_width = 768; + format->u.raw_video.display.line_count = 576; + break; + case 1: + format->u.raw_video.display.line_width = 320; + format->u.raw_video.display.line_count = 240; + break; + case 2: + format->u.raw_video.display.line_width = 160; + format->u.raw_video.display.line_count = 120; + break; + } + + if( format->u.raw_video.display.line_width > (uint32)active_rect.Width() ) + format->u.raw_video.display.line_width = (uint32)active_rect.Width() & ~7; + + if( format->u.raw_video.display.line_count > (uint32)active_rect.Height() ) + format->u.raw_video.display.line_count = (uint32)active_rect.Height(); + + // BOB and FIELD modes provide only field, which has half height + if( mode != C_VIDEO_IN_WEAVE ) { + if( format->u.raw_video.display.line_count > (uint32)active_rect.Height() / 2 ) + format->u.raw_video.display.line_count = (uint32)active_rect.Height() / 2; + } + } + + if( format->u.raw_video.display.format != media_raw_video_format::wildcard.display.format ) { + uint32 bytes_per_row; + + switch( format->u.raw_video.display.format ) { + case B_RGB32: + bytes_per_row = + format->u.raw_video.display.line_width * 4; + break; + + case B_RGB15: + case B_RGB16: + case B_YCbCr422: + bytes_per_row = + format->u.raw_video.display.line_width * 2; + break; + + default: + bytes_per_row = + format->u.raw_video.display.line_width; + } + + if( format->u.raw_video.display.bytes_per_row != + media_raw_video_format::wildcard.display.bytes_per_row ) + { + if( format->u.raw_video.display.bytes_per_row < bytes_per_row ) { + DPRINT(( "Requested bytes per row are too small", + format->u.raw_video.display.bytes_per_row )); + return B_MEDIA_BAD_FORMAT; + } + } else if( set_bytes_per_row ) + format->u.raw_video.display.bytes_per_row = bytes_per_row; + } + + + return B_OK; +} + + +// verify "offset" parameters of format +status_t +CRadeonProducer::verifyFormatOffsets( media_format *format ) +{ + if( format->u.raw_video.display.pixel_offset != + media_raw_video_format::wildcard.display.pixel_offset && + format->u.raw_video.display.pixel_offset != 0 ) + { + DPRINT(( "Requested pixel offset is not zero" )); + return B_MEDIA_BAD_FORMAT; + } + + if( format->u.raw_video.display.line_offset != + media_raw_video_format::wildcard.display.line_offset && + format->u.raw_video.display.line_offset != 0 ) + { + DPRINT(( "Requested line offset is not zero" )); + return B_MEDIA_BAD_FORMAT; + } + + return B_OK; +} + + +// set "offset" parameters of format if not set yet +void +CRadeonProducer::setFormatOffsets( media_format *format ) +{ + if( format->u.raw_video.display.pixel_offset == + media_raw_video_format::wildcard.display.pixel_offset ) + format->u.raw_video.display.pixel_offset = 0; + + if( format->u.raw_video.display.line_offset == + media_raw_video_format::wildcard.display.line_offset ) + format->u.raw_video.display.line_offset = 0; +} + + +// verify "flags" parameter of format +status_t +CRadeonProducer::verifyFormatFlags( media_format *format ) +{ + if( format->u.raw_video.display.flags != + media_raw_video_format::wildcard.display.flags && + format->u.raw_video.display.flags != 0 ) + { + DPRINT(( "Requested display flags are not zero" )); + return B_MEDIA_BAD_FORMAT; + } + + return B_OK; +} + + +// set "flags" parameter of format if not set yet +void +CRadeonProducer::setFormatFlags( media_format *format ) +{ + if( format->u.raw_video.display.flags == + media_raw_video_format::wildcard.display.flags ) + format->u.raw_video.display.flags = 0; +} + + +/* + * Fill out all wildcards in a format descriptor. + */ +status_t +CRadeonProducer::finalizeFormat( media_format *format ) +{ + if (format->type != B_MEDIA_RAW_VIDEO) + return B_MEDIA_BAD_FORMAT; + + status_t res; + + res = verifySetMode( format ); + if( res != B_OK ) + return res; + + int32 mode = extractCaptureMode( format ); + + res = verifyActiveRange( format ); + if( res != B_OK ) + return res; + + setActiveRange( format ); + + res = verifyOrientation( format ); + if( res != B_OK ) + return res; + + res = verifySetPixelAspect( format ); + if( res != B_OK ) + return res; + + res = verifyPixelFormat( format ); + if( res != B_OK ) + return res; + + setPixelFormat( format); + + res = verifySetSize( format, mode, true ); + if( res != B_OK ) + return res; + + res = verifyFormatOffsets( format ); + if( res != B_OK ) + return res; + + setFormatOffsets( format ); + + res = verifyFormatFlags( format ); + if( res != B_OK ) + return res; + + setFormatFlags( format ); +} + + +/* + * Someone has no idea what format we usually provide and asks us. + * + * It's not really clear whether we are allowed to return wildcards. + */ +status_t +CRadeonProducer::FormatSuggestionRequested( + media_type type, int32 quality, media_format *format) +{ + DPRINT(("CRadeonProducer::FormatSuggestionRequested()\n")); + + if (type != B_MEDIA_RAW_VIDEO) + return B_MEDIA_BAD_FORMAT; + + TOUCH(quality); + + format->type = B_MEDIA_RAW_VIDEO; + + format->u.raw_video = media_raw_video_format::wildcard; + + finalizeFormat( format ); + + return B_OK; +} + + +/* + Initial format proposal as part of a connection establishment. + + First, the application defines a format with many wildcards in it; + this format is passed to us, so we can restrict it if necessary; + we should leave as many wildcards as possible, because in the next + step the consumer is asked, and he will not be happy if he has to choise left . +*/ +status_t +CRadeonProducer::FormatProposal(const media_source &output, media_format *format) +{ + char buffer[256]; + + DPRINT(("CRadeonProducer::FormatProposal()\n")); + + if( format == NULL ) + return B_BAD_VALUE; + + if( output != fOutput.source ) + return B_MEDIA_BAD_SOURCE; + + string_for_format(*format, buffer, sizeof(buffer)); + + DPRINT(("CRadeonProducer::FormatProposal() - in=%s\n", buffer)); + + if( format->type == B_MEDIA_NO_TYPE ) { + // if there is not even a type, set raw video + format->type = B_MEDIA_RAW_VIDEO; + format->u.raw_video = media_raw_video_format::wildcard; + } + + if (format->type != B_MEDIA_RAW_VIDEO) + return B_MEDIA_BAD_FORMAT; + + status_t res; + + // first, choose capture mode, so we know the maximum video size + res = verifySetMode( format ); + if( res != B_OK ) + return res; + + int32 mode = extractCaptureMode( format ); + + res = verifyActiveRange( format ); + if( res != B_OK ) + return res; + + res = verifyOrientation( format ); + if( res != B_OK ) + return res; + + setOrientation( format ); + + // simple aspect calculation: we always use 1:1, so setting this is easy + res = verifySetPixelAspect( format ); + if( res != B_OK ) + return res; + + res = verifyPixelFormat( format ); + if( res != B_OK ) + return res; + + // if we don't set it, the consumer usually chooses a stupid format + setPixelFormat( format ); + + // verify size and set if if undefined; + // do that now, else the consumer will set it (making the defaults + // set via preferences useless) + // leave bytes_per_lines untouched, though + // (we don't really care but the consumer may have some alignment restrictions) + res = verifySetSize( format, mode, false ); + if( res != B_OK ) + return res; + + res = verifyFormatOffsets( format ); + if( res != B_OK ) + return res; + + res = verifyFormatFlags( format ); + if( res != B_OK ) + return res; + + string_for_format(*format, buffer, sizeof(buffer)); + + DPRINT(("CRadeonProducer::FormatProposal() - out=%s\n", buffer)); + + return B_OK; +} + +status_t +CRadeonProducer::FormatChangeRequested(const media_source &source, + const media_destination &destination, media_format *io_format, + int32 *_deprecated_) +{ + DPRINT(("CRadeonProducer::FormatChangeRequested()\n")); + + TOUCH(destination); TOUCH(io_format); TOUCH(_deprecated_); + if (source != fOutput.source) + return B_MEDIA_BAD_SOURCE; + + return B_ERROR; +} + +status_t +CRadeonProducer::GetNextOutput(int32 *cookie, media_output *out_output) +{ + DPRINT(("CRadeonProducer::GetNextOutput()\n")); + + if (!out_output) + return B_BAD_VALUE; + + if ((*cookie) != 0) + return B_BAD_INDEX; + + *out_output = fOutput; + (*cookie)++; + return B_OK; +} + +status_t +CRadeonProducer::DisposeOutputCookie(int32 cookie) +{ + DPRINT(("CRadeonProducer::DisposeOutputCookie()\n")); + + TOUCH(cookie); + + return B_OK; +} + +status_t +CRadeonProducer::SetBufferGroup(const media_source &for_source, + BBufferGroup *group) +{ + DPRINT(("CRadeonProducer::SetBufferGroup()\n")); + + if( for_source != fOutput.source ) + return B_MEDIA_BAD_SOURCE; + + if( group != NULL ) { + delete fBufferGroup; + fBufferGroup = group; + } + + return B_OK; +} + +status_t +CRadeonProducer::VideoClippingChanged(const media_source &for_source, + int16 num_shorts, int16 *clip_data, + const media_video_display_info &display, int32 *_deprecated_) +{ + DPRINT(("CRadeonProducer::VideoClippingChanged()\n")); + + TOUCH(for_source); TOUCH(num_shorts); TOUCH(clip_data); + TOUCH(display); TOUCH(_deprecated_); + + return B_ERROR; +} + +status_t +CRadeonProducer::GetLatency(bigtime_t *out_latency) +{ + DPRINT(("CRadeonProducer::GetLatency()\n")); + + // delay is one frame for capturing, a scheduling latency, the + // DMA copying, the format conversion and the output nodes latency; + // scheduling, DMA copying and format conversion is summed up in + // fProcessingLatency + bigtime_t capture_latency = (bigtime_t)(1000000.0 / fOutput.format.u.raw_video.field_rate); + + // HACK: (see HandleHardware()) + // to be compatible to existing software, we write the ending time of + // capture instead of the beginning time into buffers, thus we + // have no capture delay + capture_latency = 0; + + bigtime_t buffer_latency = fProcessingLatency; + BBufferProducer::GetLatency( &buffer_latency ); + + *out_latency = SchedulingLatency() + capture_latency + fProcessingLatency + + buffer_latency; + + DPRINT(("latency=%lld\n", *out_latency)); + + return B_OK; +} + + +status_t +CRadeonProducer::PrepareToConnect(const media_source &source, + const media_destination &destination, media_format *format, + media_source *out_source, char *out_name) +{ + DPRINT(("CRadeonProducer::PrepareToConnect()\n")); + + PRINTF(1, ("PrepareToConnect() %ldx%ld\n", \ + format->u.raw_video.display.line_width, \ + format->u.raw_video.display.line_count)); + + if (source != fOutput.source) { + DPRINT(("bad source\n")); + return B_MEDIA_BAD_SOURCE; + } + + if (fOutput.destination != media_destination::null) { + DPRINT(("already connected\n")); + return B_MEDIA_ALREADY_CONNECTED; + } + + char buffer[256]; + string_for_format(*format, buffer, sizeof(buffer)); + DPRINT(("CRadeonProducer::PrepareToConnect() - in=%s\n", buffer)); + + status_t res = finalizeFormat( format ); + if( res != B_OK ) + return res; + + *out_source = fOutput.source; + strcpy(out_name, fOutput.name); + + string_for_format(*format, buffer, sizeof(buffer)); + DPRINT(("CRadeonProducer::PrepareToConnect() - out=%s\n", buffer)); + + // reserve connection + fOutput.destination = destination; + + DPRINT(("finished\n")); + + return B_OK; +} + +void +CRadeonProducer::setDefaultBufferGroup() +{ + DPRINT(("CRadeonProducer::setDefaultBufferGroup()\n")); + /*delete fBufferGroup; + fBufferGroup = NULL;*/ + if( fBufferGroup != NULL ) { + DPRINT(("Buffer already set\n")); + return; + } + + fBufferGroup = new BBufferGroup( + fOutput.format.u.raw_video.display.bytes_per_row * + fOutput.format.u.raw_video.display.line_count, + 3, B_ANY_ADDRESS, B_FULL_LOCK ); + + if (fBufferGroup->InitCheck() < B_OK) { + delete fBufferGroup; + fBufferGroup = NULL; + return; + } +} + +void +CRadeonProducer::startCapturing() +{ + if( RunState() != BMediaEventLooper::B_STARTED || + fOutput.destination == media_destination::null ) + return; + + fVideoIn.SetChannel(fTuner, C_VIDEO_IN_NTSC); + fVideoIn.SetBrightness(fBrightness); + fVideoIn.SetContrast(fContrast); + fVideoIn.SetSaturation(fSaturation); + fVideoIn.SetHue(fHue); + fVideoIn.SetSharpness(fSharpness); + + fVideoIn.Start(video_in_source(fSource), BeToVideoInStandard( fStandard ), + video_in_capture_mode(fCurMode), + fOutput.format.u.raw_video.display.line_width, + fOutput.format.u.raw_video.display.line_count); + + char *tmp_buffer; + tmp_buffer = (char *)malloc( + fOutput.format.u.raw_video.display.bytes_per_row * + fOutput.format.u.raw_video.display.line_count ); + + int field_sequence; + short field_number; + bigtime_t capture_time; + + // do a real capturing to prime everything + fVideoIn.Capture( + tmp_buffer != NULL ? fOutput.format.u.raw_video.display.format : B_NO_COLOR_SPACE, + tmp_buffer, + fOutput.format.u.raw_video.display.bytes_per_row * fOutput.format.u.raw_video.display.line_count, + fOutput.format.u.raw_video.display.bytes_per_row, + &field_sequence, &field_number, &capture_time ); + + // capture some frames to be sure there are no pending buffers; + // discard captured data to safe time (we want to catch up and not fall behind) + for( int i = 0; i < 3; ++i ) { + fVideoIn.Capture( + B_NO_COLOR_SPACE, + NULL, + fOutput.format.u.raw_video.display.bytes_per_row * fOutput.format.u.raw_video.display.line_count, + fOutput.format.u.raw_video.display.bytes_per_row, + &field_sequence, &field_number, &capture_time ); + + DPRINT(("Captured: %lld, current: %lld\n", capture_time, system_time() )); + } + + // do a real capturing to see how long it takes until the + // buffer is ready, i.e. including DMA and colour conversion + fVideoIn.Capture( + tmp_buffer != NULL ? fOutput.format.u.raw_video.display.format : B_NO_COLOR_SPACE, + tmp_buffer, + fOutput.format.u.raw_video.display.bytes_per_row * fOutput.format.u.raw_video.display.line_count, + fOutput.format.u.raw_video.display.bytes_per_row, + &field_sequence, &field_number, &capture_time ); + + DPRINT(("Captured: %lld, current: %lld\n", capture_time, system_time() )); + + // now we know our internal latency + fProcessingLatency = system_time() - capture_time; + + DPRINT(("Processing latency: %dµs\n", fProcessingLatency )); + + // store field sequence to start with zero + // (capture-internal field sequence always counts up) + fFieldSequenceBase = field_sequence; + + if( tmp_buffer != NULL ) + free(tmp_buffer); + + // tell latence MediaEventLooper so it can schedule events properly ahead + bigtime_t total_latency; + + GetLatency( &total_latency ); + SetEventLatency( total_latency ); + + // Create the buffer group + setDefaultBufferGroup(); + + //fUsedBufferGroup = fBufferGroup; + + // schedule a capture event after one field's time + RealTimeQueue()->FlushEvents( 0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HARDWARE ); + + media_timed_event event( + capture_time + 1000000 / fOutput.format.u.raw_video.field_rate, + BTimedEventQueue::B_HARDWARE); + + RealTimeQueue()->AddEvent(event); +} + +void +CRadeonProducer::Connect(status_t error, const media_source &source, + const media_destination &destination, const media_format &format, + char *io_name) +{ + // we even get called if consumer reported error in AcceptFormat; + // in this case, we must release the source already reserved by + // PrepareToConnect + if( error != B_OK ) { + DPRINT(( "Connect: Consumer returned error (%s) - releasing source", + strerror( error ))); + fOutput.destination = media_destination::null; + return; + } + + if( source != fOutput.source ) { + DPRINT(( "Connect: Wrong source specified\n")); + return; + } + + fOutput.destination = destination; + fOutput.format = format; + fCurMode = extractCaptureMode( &format ); + + char buffer[256]; + string_for_format(format, buffer, sizeof(buffer)); + + DPRINT(("CRadeonProducer::Connect() - %s\n", buffer)); + + strcpy(io_name, fOutput.name); + + startCapturing(); + + DPRINT(("CRadeonProducer::Connect() done\n")); +} + +void +CRadeonProducer::Disconnect(const media_source &source, + const media_destination &destination) +{ + DPRINT(("Disconnect()\n")); + + if( source != fOutput.source || destination != fOutput.destination ) { + DPRINT(("Disconnect: Bad source and/or destination\n")); + return; + } + + fOutput.destination = media_destination::null; + + delete fBufferGroup; + fBufferGroup = NULL; + + fVideoIn.Stop(); + + RealTimeQueue()->FlushEvents( 0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HARDWARE ); + + // reset format to get rid of any connection left-overs + fOutput.format.type = B_MEDIA_RAW_VIDEO; + fOutput.format.u.raw_video = media_raw_video_format::wildcard; + + DPRINT(("Disconnect() done\n")); +} + +void +CRadeonProducer::LateNoticeReceived(const media_source &source, + bigtime_t how_much, bigtime_t performance_time) +{ + DPRINT(("CRadeonProducer::LateNoticeReceived()\n")); + + TOUCH(source); TOUCH(how_much); TOUCH(performance_time); +} + +void +CRadeonProducer::EnableOutput(const media_source &source, bool enabled, + int32 *_deprecated_) +{ + DPRINT(("CRadeonProducer::EnableOutput()\n")); + + TOUCH(_deprecated_); + + if (source != fOutput.source) + return; + + fEnabled = enabled; +} + +status_t +CRadeonProducer::SetPlayRate(int32 numer, int32 denom) +{ + DPRINT(("CRadeonProducer::SetPlayRate()\n")); + + TOUCH(numer); TOUCH(denom); + + return B_ERROR; +} + +void +CRadeonProducer::AdditionalBufferRequested(const media_source &source, + media_buffer_id prev_buffer, bigtime_t prev_time, + const media_seek_tag *prev_tag) +{ + DPRINT(("CRadeonProducer::AdditionalBufferRequested()\n")); + + TOUCH(source); TOUCH(prev_buffer); TOUCH(prev_time); TOUCH(prev_tag); +} + +void +CRadeonProducer::LatencyChanged(const media_source &source, + const media_destination &destination, bigtime_t new_latency, + uint32 flags) +{ + DPRINT(("CRadeonProducer::LatencyChanged()\n")); + BBufferProducer::LatencyChanged( source, destination, new_latency, flags ); +} + + + +/* BControllable */ + +status_t +CRadeonProducer::GetParameterValue( + int32 id, bigtime_t *last_change, void *value, size_t *size) +{ + DPRINT(("CRadeonProducer::GetParameterValue(%d)\n", id)); + + switch (id) { + case P_SOURCE: + *last_change = fSourceLastChange; + *size = sizeof(fSource); + *((uint32 *) value) = fSource; + break; + case P_STANDARD: + *last_change = fStandardLastChange; + *size = sizeof(fStandard); + *((uint32 *) value) = fStandard; + break; + case P_MODE: + *last_change = fModeLastChange; + *size = sizeof(fMode); + *((uint32 *) value) = fMode; + break; + case P_FORMAT: + *last_change = fFormatLastChange; + *size = sizeof(fFormat); + *((uint32 *) value) = fFormat; + break; + case P_RESOLUTION: + *last_change = fResolutionLastChange; + *size = sizeof(fResolution); + *((uint32 *) value) = fResolution; + break; + case P_TUNER: + *last_change = fTunerLastChange; + *size = sizeof(fTuner); + *((uint32 *) value) = fTuner; + break; + case P_BRIGHTNESS: + *last_change = fBrightnessLastChange; + *size = sizeof(fBrightness); + *((float *) value) = fBrightness; + break; + case P_CONTRAST: + *last_change = fContrastLastChange; + *size = sizeof(fContrast); + *((float *) value) = fContrast; + break; + case P_SATURATION: + *last_change = fSaturationLastChange; + *size = sizeof(fSaturation); + *((float *) value) = fSaturation; + break; + case P_HUE: + *last_change = fHueLastChange; + *size = sizeof(fHue); + *((float *) value) = fHue; + break; + case P_SHARPNESS: + *last_change = fSharpnessLastChange; + *size = sizeof(fSharpness); + *((float *) value) = fSharpness; + break; + default: + DPRINT(("Unknown parameter\n")); + return B_BAD_VALUE; + } + + return B_OK; +} + + +/* + * Change video format instantly. + * + * Used when user changes a settings that affect the video format. + * The new format must be the current format with some values + * replaced with wildcards. Don't put too many wildcards: + * some settings (like video size) are normally chosen by the + * application, so don't use this function as a secret override. + */ +void +CRadeonProducer::instantFormatChange( media_format *new_format ) +{ + if( fOutput.destination == media_destination::null ) + return; + + if( finalizeFormat( new_format ) != B_OK ) { + DPRINT(("Current format does not allow to change interlace mode on-the-fly\n")); + return; + } + + if( ChangeFormat( fOutput.source, fOutput.destination, new_format ) != B_OK ) { + DPRINT(("Consumer does not allow to change interlace mode instantly\n")); + return; + } + + fOutput.format = *new_format; + fCurMode = extractCaptureMode( new_format ); + + fVideoIn.Stop(); + startCapturing(); +} + +void +CRadeonProducer::SetParameterValue( + int32 id, bigtime_t when, const void *value, size_t size) +{ + DPRINT(("CRadeonProducer::SetParameterValue()\n")); + + if (!value || size != sizeof(uint32)) + return; + + switch (id) { + case P_SOURCE: + if (*((const int32 *) value) == fSource) + return; + fSource = *((const uint32 *) value); + fSourceLastChange = when; + break; + case P_STANDARD: { + if (*((const int32 *) value) == fStandard) + return; + fStandard = *((const uint32 *) value); + fStandardLastChange = when; + + media_format new_format = fOutput.format; + + new_format.u.raw_video.field_rate = media_raw_video_format::wildcard.field_rate; + new_format.u.raw_video.interlace = media_raw_video_format::wildcard.interlace; + new_format.u.raw_video.pixel_width_aspect = media_raw_video_format::wildcard.pixel_width_aspect; + new_format.u.raw_video.pixel_height_aspect = media_raw_video_format::wildcard.pixel_height_aspect; + new_format.u.raw_video.first_active = media_raw_video_format::wildcard.first_active; + new_format.u.raw_video.last_active = media_raw_video_format::wildcard.last_active; + + instantFormatChange( &new_format ); + break; } + case P_MODE: { + if (*((const int32 *) value) == fMode) + return; + + fMode = *((const uint32 *) value); + fModeLastChange = when; + + media_format new_format = fOutput.format; + + new_format.u.raw_video.field_rate = media_raw_video_format::wildcard.field_rate; + new_format.u.raw_video.interlace = media_raw_video_format::wildcard.interlace; + new_format.u.raw_video.pixel_width_aspect = media_raw_video_format::wildcard.pixel_width_aspect; + new_format.u.raw_video.pixel_height_aspect = media_raw_video_format::wildcard.pixel_height_aspect; + + instantFormatChange( &new_format ); + + break; } + case P_FORMAT: { + if (*((const int32 *) value) == fFormat) + return; + fFormat = *((const uint32 *) value); + fFormatLastChange = when; + + media_format new_format = fOutput.format; + + new_format.u.raw_video.display.format = media_raw_video_format::wildcard.display.format; + new_format.u.raw_video.display.bytes_per_row = media_raw_video_format::wildcard.display.bytes_per_row; + + instantFormatChange( &new_format ); + break; } + case P_RESOLUTION: + if (*((const int32 *) value) == fResolution) + return; + fResolution = *((const uint32 *) value); + fResolutionLastChange = when; + // no live update - see instantFormatChange() + break; + case P_TUNER: + if (*((const int32 *) value) == fTuner) + return; + fTuner = *((const uint32 *) value); + fTunerLastChange = when; + fVideoIn.SetChannel(fTuner, C_VIDEO_IN_NTSC); + break; + case P_BRIGHTNESS: + if (*((const float *) value) == fBrightness) + return; + fBrightness = (int32)*((const float *) value); + fBrightnessLastChange = when; + fVideoIn.SetBrightness(fBrightness); + break; + case P_CONTRAST: + if (*((const float *) value) == fContrast) + return; + fContrast = (int32)*((const float *) value); + fContrastLastChange = when; + fVideoIn.SetContrast(fContrast); + break; + case P_SATURATION: + if (*((const float *) value) == fSaturation) + return; + fSaturation = (int32)*((const float *) value); + fSaturationLastChange = when; + fVideoIn.SetSaturation(fSaturation); + break; + case P_HUE: + if (*((const float *) value) == fHue) + return; + fHue = (int32)*((const float *) value); + fHueLastChange = when; + fVideoIn.SetHue(fHue); + break; + case P_SHARPNESS: + if (*((const float *) value) == fSharpness) + return; + fSharpness = (int32)*((const float *) value); + fSharpnessLastChange = when; + fVideoIn.SetSharpness(fSharpness); + break; + default: + return; + } + + BroadcastNewParameterValue(when, id, const_cast(value), sizeof(uint32)); +} + +status_t +CRadeonProducer::StartControlPanel(BMessenger *out_messenger) +{ + return BControllable::StartControlPanel(out_messenger); +} + +status_t CRadeonProducer::AddInt32( + BMessage *msg, EOptions option, int32 value ) +{ + char name[5]; + + *(int32 *)name = option; + name[4] = 0; + + return msg->AddInt32( name, value ); +} + +status_t +CRadeonProducer::GetConfiguration( BMessage *out ) +{ + status_t res; + + if( (res = AddInt32( out, P_SOURCE, fSource )) != B_OK || + (res = AddInt32( out, P_STANDARD, BeToVideoInStandard( fStandard ))) != B_OK || + (res = AddInt32( out, P_MODE, fMode )) != B_OK || + (res = AddInt32( out, P_FORMAT, fFormat )) != B_OK || + (res = AddInt32( out, P_RESOLUTION, fResolution )) != B_OK || + (res = AddInt32( out, P_TUNER, fTuner )) != B_OK || + (res = AddInt32( out, P_BRIGHTNESS, fBrightness )) != B_OK || + (res = AddInt32( out, P_CONTRAST, fContrast )) != B_OK || + (res = AddInt32( out, P_SATURATION, fSaturation )) != B_OK || + (res = AddInt32( out, P_HUE, fHue )) != B_OK || + (res = AddInt32( out, P_SHARPNESS, fSharpness )) != B_OK ) + return res; + + return B_OK; +} + + +/* VideoProducer */ + +void +CRadeonProducer::HandleStart(bigtime_t performance_time) +{ + /* Start producing frames, even if the output hasn't been connected yet. */ + DPRINT(("CRadeonProducer::HandleStart()\n")); + + if( RunState() != BMediaEventLooper::B_STOPPED ) { + DPRINT(("already running\n")); + return; + } + + SetRunState( BMediaEventLooper::B_STARTED ); + + startCapturing(); +} + +void +CRadeonProducer::HandleStop(void) +{ + DPRINT(("CRadeonProducer::HandleStop()\n")); + + fVideoIn.Stop(); + + // discard pending capture event + RealTimeQueue()->FlushEvents( 0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HARDWARE ); +} + +void +CRadeonProducer::HandleTimeWarp(bigtime_t performance_time) +{ + DPRINT(("CRadeonProducer::HandleTimeWarp()\n")); +} + +void +CRadeonProducer::HandleSeek(bigtime_t performance_time) +{ + DPRINT(("CRadeonProducer::HandleSeek()\n")); +} + +void +CRadeonProducer::captureField( bigtime_t *capture_time ) +{ + *capture_time = system_time(); + + // don't capture if output is disabled + if (!fEnabled) + return; + + BBuffer *buffer = fBufferGroup->RequestBuffer( + fOutput.format.u.raw_video.display.bytes_per_row * + fOutput.format.u.raw_video.display.line_count, 0LL); + + if (!buffer) { + DPRINT(( "No buffer\n" )); + return; + } + + media_header *h = buffer->Header(); + h->type = B_MEDIA_RAW_VIDEO; + h->time_source = TimeSource()->ID(); + h->size_used = fOutput.format.u.raw_video.display.bytes_per_row * + fOutput.format.u.raw_video.display.line_count; + h->file_pos = 0; + h->orig_size = 0; + h->data_offset = 0; + h->u.raw_video.field_gamma = 1.0; + h->u.raw_video.pulldown_number = 0; + h->u.raw_video.first_active_line = fOutput.format.u.raw_video.first_active; + h->u.raw_video.line_count = fOutput.format.u.raw_video.display.line_count; + + int field_sequence; + short field_number; + + int dropped=fVideoIn.Capture( + fOutput.format.u.raw_video.display.format, + buffer->Data(), + fOutput.format.u.raw_video.display.bytes_per_row * fOutput.format.u.raw_video.display.line_count, + fOutput.format.u.raw_video.display.bytes_per_row, + &field_sequence, + &field_number, + capture_time); + + // HACK: store end instead of start time + // obviously, the _start_ time of a frame is always in the past by one + // frame; unfortunately, programs like stamptv expect the start time to + // be in the present, else they drop the frame; therefore, we write the + // _end_ time into the start time field, and everyone is happy (though + // the time is wrong) + // this leads to many tweaks in the code! + h->start_time = TimeSource()->PerformanceTimeFor( *capture_time ); + + h->u.raw_video.field_sequence = field_sequence - fFieldSequenceBase; + h->u.raw_video.field_number = field_number; + + if (dropped > 1) { + PRINT(("%d frames dropped\n", dropped-1)); + } + + if (SendBuffer(buffer, fOutput.destination) < B_OK) { + PRINTF(-1, ("FrameGenerator: Error sending buffer\n")); + buffer->Recycle(); + } +} + +void +CRadeonProducer::HandleHardware() +{ + bigtime_t capture_time; + + //DPRINT(("Hi\n")); + + if( RunState() != BMediaEventLooper::B_STARTED ) + return; + + captureField( &capture_time ); + + // generate next event after next field + media_timed_event event( + capture_time + 1000000 / fOutput.format.u.raw_video.field_rate, + BTimedEventQueue::B_HARDWARE); + + RealTimeQueue()->AddEvent(event); +} diff --git a/src/add-ons/media/media-add-ons/radeon/RadeonProducer.h b/src/add-ons/media/media-add-ons/radeon/RadeonProducer.h new file mode 100644 index 0000000000..07f65879a5 --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/RadeonProducer.h @@ -0,0 +1,241 @@ +/****************************************************************************** +/ +/ File: RadeonProducer.h +/ +/ Description: ATI Radeon Video Producer media node. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#ifndef __RADEON_PRODUCER_H__ +#define __RADEON_PRODUCER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include "VideoIn.h" + +class CRadeonAddOn; + +class CRadeonProducer : + public virtual BMediaEventLooper, + public virtual BBufferProducer, + public virtual BControllable +{ +public: + CRadeonProducer( + CRadeonAddOn *addon, const char *name, const char *device_name, + int32 internal_id, BMessage *config ); +virtual ~CRadeonProducer(); + +void setupWeb(); +virtual status_t InitCheck() const { return fInitStatus; } + +/* BMediaNode */ +public: +virtual port_id ControlPort() const; +virtual BMediaAddOn *AddOn(int32 * internal_id) const; +virtual status_t HandleMessage(int32 message, const void *data, + size_t size); +protected: +virtual void Preroll(); +virtual void SetTimeSource(BTimeSource * time_source); +virtual status_t RequestCompleted(const media_request_info & info); + +/* BMediaEventLooper */ +protected: +virtual void NodeRegistered(); +virtual void Start(bigtime_t performance_time); +virtual void Stop(bigtime_t performance_time, bool immediate); +virtual void Seek(bigtime_t media_time, bigtime_t performance_time); +virtual void TimeWarp(bigtime_t at_real_time, + bigtime_t to_performance_time); +virtual status_t AddTimer(bigtime_t at_performance_time, int32 cookie); +virtual void SetRunMode(run_mode mode); +virtual void HandleEvent(const media_timed_event *event, + bigtime_t lateness, bool realTimeEvent = false); +virtual void CleanUpEvent(const media_timed_event *event); +virtual bigtime_t OfflineTime(); +virtual void ControlLoop(); +virtual status_t DeleteHook(BMediaNode * node); + +/* BBufferProducer */ +protected: +virtual status_t FormatSuggestionRequested(media_type type, int32 quality, + media_format * format); +virtual status_t FormatProposal(const media_source &output, + media_format *format); +virtual status_t FormatChangeRequested(const media_source &source, + const media_destination &destination, + media_format *io_format, int32 *_deprecated_); +virtual status_t GetNextOutput(int32 * cookie, media_output * out_output); +virtual status_t DisposeOutputCookie(int32 cookie); +virtual status_t SetBufferGroup(const media_source &for_source, + BBufferGroup * group); +virtual status_t VideoClippingChanged(const media_source &for_source, + int16 num_shorts, int16 *clip_data, + const media_video_display_info &display, + int32 * _deprecated_); +virtual status_t GetLatency(bigtime_t * out_latency); +virtual status_t PrepareToConnect(const media_source &what, + const media_destination &where, + media_format *format, + media_source *out_source, char *out_name); +virtual void Connect(status_t error, const media_source &source, + const media_destination &destination, + const media_format & format, char *io_name); +virtual void Disconnect(const media_source & what, + const media_destination & where); +virtual void LateNoticeReceived(const media_source & what, + bigtime_t how_much, bigtime_t performance_time); +virtual void EnableOutput(const media_source & what, bool enabled, + int32 * _deprecated_); +virtual status_t SetPlayRate(int32 numer,int32 denom); +virtual void AdditionalBufferRequested(const media_source & source, + media_buffer_id prev_buffer, bigtime_t prev_time, + const media_seek_tag * prev_tag); +virtual void LatencyChanged(const media_source & source, + const media_destination & destination, + bigtime_t new_latency, uint32 flags); + +/* BControllable */ +protected: +virtual status_t GetParameterValue(int32 id, bigtime_t *last_change, + void *value, size_t *size); +virtual void SetParameterValue(int32 id, bigtime_t when, + const void *value, size_t size); +virtual status_t StartControlPanel(BMessenger *out_messenger); + +public: + enum { + C_GET_CONFIGURATION = BTimedEventQueue::B_USER_EVENT, + C_GET_CONFIGURATION_REPLY + }; + + struct configuration_msg { + port_id reply_port; + }; + + struct configuration_msg_reply { + status_t res; + size_t config_size; + char config; + }; + +/* state */ +private: + void HandleStart(bigtime_t performance_time); + void HandleStop(); + void HandleTimeWarp(bigtime_t performance_time); + void HandleSeek(bigtime_t performance_time); + void HandleHardware(); + + // home-brewed extension + status_t GetConfiguration( BMessage *out ); + + CVideoIn fVideoIn; + + status_t fInitStatus; + + int32 fInternalID; + CRadeonAddOn *fAddOn; + + BBufferGroup *fBufferGroup; + //BBufferGroup *fUsedBufferGroup; + +static int32 _frame_generator_(void *data); + int32 FrameGenerator(); + + /* The remaining variables should be declared volatile, but they + * are not here to improve the legibility of the sample code. */ + //uint32 fFrame; + uint32 fFieldSequenceBase; + //bigtime_t fPerformanceTimeBase; + bigtime_t fProcessingLatency; + media_output fOutput; + //media_raw_video_format fConnectedFormat; + //bool fConnected; + bool fEnabled; + + // use fixed names as they are used in settings file + enum EOptions { + P_SOURCE = 'VSRC', + P_AUDIO_SOURCE = 'ASRC', + P_AUDIO_FORMAT = 'AFMT', + P_VOLUME = 'VOL ', + P_STANDARD = 'TVST', + P_MODE = 'CMOD', + P_FORMAT = 'VFMT', + P_RESOLUTION = 'RES ', + P_TUNER = 'TUNR', + P_BRIGHTNESS = 'BRGT', + P_CONTRAST = 'CONT', + P_SATURATION = 'SATU', + P_HUE = 'HUE ', + P_SHARPNESS = 'SHRP' + }; + + enum { C_RESOLUTION_MAX = 6 }; + enum { C_CHANNEL_MAX = 125 }; + + int32 fSource; + int32 fStandard; + int32 fMode; + int32 fCurMode; // mode, overwritten by application + int32 fFormat; + int32 fResolution; + int32 fTuner; + int32 fBrightness; + int32 fContrast; + int32 fSaturation; + int32 fHue; + int32 fSharpness; + bigtime_t fSourceLastChange; + bigtime_t fStandardLastChange; + bigtime_t fModeLastChange; + bigtime_t fFormatLastChange; + bigtime_t fResolutionLastChange; + bigtime_t fTunerLastChange; + bigtime_t fBrightnessLastChange; + bigtime_t fContrastLastChange; + bigtime_t fSaturationLastChange; + bigtime_t fHueLastChange; + bigtime_t fSharpnessLastChange; + + status_t AddInt32( + BMessage *msg, EOptions option, int32 value ); + + status_t FindInt32( + BMessage *config, EOptions option, int32 min_value, int32 max_value, + int32 default_value, int32 *value ); + + // format negotiation helpers + status_t verifySetMode( media_format *format ); + int32 extractCaptureMode( const media_format *format ); + status_t verifySetPixelAspect( media_format *format ); + status_t verifyActiveRange( media_format *format ); + void setActiveRange( media_format *format ); + status_t verifyOrientation( media_format *format ); + void setOrientation( media_format *format ); + status_t verifyPixelFormat( media_format *format ); + void setPixelFormat( media_format *format ); + status_t verifySetSize( media_format *format, int32 mode, bool set_bytes_per_row ); + status_t verifyFormatOffsets( media_format *format ); + void setFormatOffsets( media_format *format ); + status_t verifyFormatFlags( media_format *format ); + void setFormatFlags( media_format *format ); + + status_t finalizeFormat( media_format *format ); + + void startCapturing(); + void captureField( bigtime_t *capture_time ); + void setDefaultBufferGroup(); + void instantFormatChange( media_format *new_format ); +}; + +#endif diff --git a/src/add-ons/media/media-add-ons/radeon/Theater.cpp b/src/add-ons/media/media-add-ons/radeon/Theater.cpp new file mode 100644 index 0000000000..96655d290c --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/Theater.cpp @@ -0,0 +1,1363 @@ +/****************************************************************************** +/ +/ File: Theater.cpp +/ +/ Description: ATI Rage Theater Video Decoder interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#include +#include "Theater.h" +#include "TheatreReg.h" +#include "lendian_bitfield.h" + + +CTheater::CTheater(CRadeon & radeon) + : fPort(radeon), + fDevice(0), + fClock(C_RADEON_NO_VIDEO_CLOCK), + fTunerPort(0), + fCompositePort(0), + fSVideoPort(0), + fStandard(C_THEATER_NTSC), + fSource(C_THEATER_TUNER), + fBrightness(0), + fContrast(0), + fSaturation(0), + fHue(0), + fDeinterlace(true) +{ + PRINT(("CTheater::CTheater()\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(("CTheater::CTheater() - Unsupported crystal clock!\n")); + + fDevice = fPort.FindVIPDevice( + (C_THEATER_VIP_VENDOR_ID << 0) | + (C_THEATER_VIP_DEVICE_ID << 16)); + } + if( InitCheck() != B_OK ) + PRINT(("CTheater::CTheater() - Rage Theater not found!\n")); +} + +CTheater::~CTheater() +{ + PRINT(("CTheater::~CTheater()\n")); + + if( InitCheck() == B_OK ) + SetEnable(false, false); +} + +status_t CTheater::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 CTheater::Reset() +{ + PRINT(("CTheater::Reset()\n")); + + SetHue(0); + SetBrightness(0); + SetSaturation(0); + SetContrast(0); + SetSharpness(false); +} + +// disable/enable capturing +void CTheater::SetEnable(bool enable, bool vbi) +{ + PRINT(("CTheater::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 CTheater::SetStandard(theater_standard standard, theater_source source) +{ + PRINT(("CTheater::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 CTheater::SetSize(int hactive, int vactive) +{ + PRINT(("CTheater::SetSize(%d, %d)\n", hactive, vactive)); + + fHActive = hactive; + fVActive = vactive; +} + +void CTheater::SetDeinterlace(bool deinterlace) +{ + PRINT(("CTheater::SetDeinterlace(%d)\n", deinterlace)); + + fDeinterlace = deinterlace; +} + +void CTheater::SetSharpness(int sharpness) +{ + PRINT(("CTheater::SetSharpness(%d)\n", sharpness)); + + SetRegister(VIP_H_SCALER_CONTROL, H_SHARPNESS, sharpness << 25); +} + +void CTheater::SetBrightness(int brightness) +{ + PRINT(("CTheater::SetBrightness(%d)\n", brightness)); + + fBrightness = brightness; + SetLuminanceLevels(fStandard, fBrightness, fContrast); +} + +void CTheater::SetContrast(int contrast) +{ + PRINT(("CTheater::SetContrast(%d)\n", contrast)); + + fContrast = contrast; + SetLuminanceLevels(fStandard, fBrightness, fContrast); +} + +void CTheater::SetSaturation(int saturation) +{ + PRINT(("CTheater::SetSaturation(%d)\n", saturation)); + + fSaturation = saturation; + SetChromaLevels(fStandard, fSaturation, fHue); +} + +void CTheater::SetHue(int hue) +{ + PRINT(("CTheater::SetHue(%d)\n", hue)); + + fHue = hue; + SetChromaLevels(fStandard, fSaturation, fHue); +} + + +// set pixel clock +void CTheater::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(("CTheater::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(("CTheater::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 CTheater::SetADC(theater_standard standard, theater_source source) +{ + PRINT(("CTheater::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(("CTheater::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(("CTheater::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 CTheater::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(("CTheater::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 CTheater::WaitHSYNC() +{ + for (int timeout = 0; timeout < 1000; timeout++) { + if (Register(VIP_HS_PULSE_WIDTH, HS_GENLOCKED) != 0) + return; + snooze(20); + } + PRINT(("CTheater::WaitHSYNC() - wait for HSync locking time out!\n")); +} + + +// setup vertical sync and field detector +void CTheater::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(("CTheater::SetVSYNC(total=%d)\n", + Register(VIP_VS_FRAME_TOTAL, VS_FRAME_TOTAL))); +} + +// wait until a visible line is viewed +void CTheater::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(("CTheater::WaitVSYNC() - wait for VBI timed out!\n")); +} + + +// setup timing generator +void CTheater::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(("CTheater::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 CTheater::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(("CTheater::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(("CTheater::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 CTheater::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(("CTheater::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 CTheater::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 CTheater::SetChromaProcessor(theater_standard standard) +{ + PRINT(("CTheater::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 CTheater::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(("CTheater::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 CTheater::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 CTheater::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 CTheater::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(("CTheater::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 CTheater::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(("CTheater::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 CTheater::Register(int index) +{ + return fPort.Register(fDevice, index); +} + +int CTheater::Register(int index, int mask) +{ + return fPort.Register(fDevice, index) & mask; +} + +void CTheater::SetRegister(int index, int value) +{ + fPort.SetRegister(fDevice, index, value); +} + +void CTheater::SetRegister(int index, int mask, int value) +{ + if ((value & ~mask) != 0) + PRINT(("WARNING: CTheater::SetRegister(0x%04x, 0x%08x, 0x%08x)\n", index, mask, value)); + + fPort.SetRegister(fDevice, index, + (fPort.Register(fDevice, index) & ~mask) | (value & mask)); +} + +int CTheater::CurrentLine() +{ + return Register(VIP_VS_LINE_COUNT) & VS_LINE_COUNT; +} + +void CTheater::PrintToStream() +{ + PRINT(("<<< Rage Theater Registers >>>\n")); + for (int index = 0x0400; index <= 0x06ff; index += 4) { + int value = Register(index); + PRINT(("REG_0x%04x = 0x%08x\n", index, value)); + } +} diff --git a/src/add-ons/media/media-add-ons/radeon/Theater.h b/src/add-ons/media/media-add-ons/radeon/Theater.h new file mode 100644 index 0000000000..f4502c958e --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/Theater.h @@ -0,0 +1,135 @@ +/****************************************************************************** +/ +/ File: Theater.h +/ +/ Description: ATI Rage Theater Video Decoder interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#ifndef __THEATER_H__ +#define __THEATER_H__ + +#include "Radeon.h" +#include "VIPPort.h" + +enum theater_identifier { + C_THEATER_VIP_VENDOR_ID = 0x1002, + C_THEATER_VIP_DEVICE_ID = 0x4d54 +}; + + +enum theater_standard { + // TK: rearranged to match spec order + C_THEATER_NTSC = 0, + C_THEATER_NTSC_JAPAN = 1, + C_THEATER_NTSC_443 = 2, + C_THEATER_PAL_M = 3, + C_THEATER_PAL_N = 4, + C_THEATER_PAL_NC = 5, + C_THEATER_PAL_BDGHI = 6, + C_THEATER_PAL_60 = 7, + C_THEATER_SECAM = 8 +}; + +enum theater_source { + C_THEATER_TUNER = 0, + C_THEATER_COMPOSITE = 1, + C_THEATER_SVIDEO = 2 +}; + + +class CTheater { +public: + CTheater(CRadeon & radeon); + + ~CTheater(); + + status_t InitCheck() const; + + void Reset(); + + void SetEnable(bool enable, bool vbi); + + void SetStandard(theater_standard standard, theater_source source); + + void SetSize(int hactive, int vactive); + + void SetDeinterlace(bool deinterlace); + + void SetSharpness(int sharpness); + + void SetBrightness(int brightness); + + void SetContrast(int contrast); + + void SetSaturation(int saturation); + + void SetHue(int hue); + + int CurrentLine(); + + void getActiveRange( theater_standard standard, CRadeonRect &rect ); + + void getVBIRange( theater_standard standard, CRadeonRect &rect ); + + void PrintToStream(); + +private: + void SetClock(theater_standard standard, radeon_video_clock clock); + + void SetADC(theater_standard standard, theater_source source); + + void SetHSYNC(theater_standard standard); + + void WaitHSYNC(); + + void SetVSYNC(theater_standard standard); + + void WaitVSYNC(); + + void SetSyncGenerator(theater_standard standard); + + void SetCombFilter(theater_standard standard, theater_source source); + + void SetLuminanceProcessor(theater_standard standard); + + void SetLuminanceLevels(theater_standard standard, int brightness, int contrast); + + void SetChromaProcessor(theater_standard standard); + + void SetChromaLevels(theater_standard standard, int saturation, int hue); + + void SetClipWindow(theater_standard standard, bool vbi); + + void SetScaler(theater_standard standard, int hactive, int vactive, bool deinterlace); + +public: + int Register(int index); + + int Register(int index, int mask); + + void SetRegister(int index, int value); + + void SetRegister(int index, int mask, int value); + +private: + CVIPPort fPort; + int fDevice; + radeon_video_clock fClock; + int fTunerPort; + int fCompositePort; + int fSVideoPort; + theater_standard fStandard; + theater_source fSource; + int fBrightness; + int fContrast; + int fSaturation; + int fHue; + int fHActive; + int fVActive; + bool fDeinterlace; +}; + +#endif diff --git a/src/add-ons/media/media-add-ons/radeon/TheatreReg.h b/src/add-ons/media/media-add-ons/radeon/TheatreReg.h new file mode 100644 index 0000000000..7736bf9c8b --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/TheatreReg.h @@ -0,0 +1,1235 @@ +/****************************************************************************** +/ +/ File: Theater.cpp +/ +/ Description: ATI Rage Theater Video Decoder interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#ifndef _THEATRE_REG_H +#define _THEATRE_REG_H + +enum theater_register { + // TVOut Front End Datapath + VIP_RGB_CNTL = 0x0048, + RGB_IS_888_PACK = BITS(0:0), // Select 24/16 bit (888/565) RGB mode + UV_DITHER_EN = BITS(3:2), // Select dithering mode for U/V data + SWITCH_TO_BLUE = BITS(4:4), // Replace input video with blue screen + + VIP_TVO_SYNC_PAT_ACCUM = 0x0108, + H_PATTERN_ACCUM = BITS(10:0), + SCAN_REF_OFFSET = BITS(15:11), + BYTE0 = BITS(23:16), + BYTE1 = BITS(31:24), + + VIP_TV0_SYNC_THRESHOLD = 0x010c, + MAX_H_PATTERNS = BITS(10:0), + MIN_H_PATTERNS = BITS(27:16), + + VIP_TVO_SYNC_PAT_EXPECT = 0x0110, + SYNC_PAT_EXPECT_W0 = BITS(15:0), + SYNC_PAT_EXPECT_W1 = BITS(23:16), + + VIP_DELAY_ONE_MAP_A = 0x0114, + DELAY_ONE_MAP_W0 = BITS(15:0), + DELAY_ONE_MAP_W1 = BITS(31:16), + + VIP_DELAY_ONE_MAP_B = 0x0118, + DELAY_ONE_MAP_W2 = BITS(15:0), + + VIP_DELAY_ZERO_MAP_A = 0x011c, + DELAY_ZERO_MAP_W0 = BITS(15:0), + DELAY_ZERO_MAP_W1 = BITS(31:16), + + VIP_DELAY_ZERO_MAP_B = 0x0120, + DELAY_ZERO_MAP_W2 = BITS(15:0), + + VIP_TVO_DATA_DELAY_A = 0x0140, + TVO_DATA0_DELAY = BITS(5:0), + TVO_DATA1_DELAY = BITS(13:8), + TVO_DATA2_DELAY = BITS(21:16), + TVO_DATA3_DELAY = BITS(29:24), + + VIP_TVO_DATA_DELAY_B = 0x0144, + TVO_DATA4_DELAY = BITS(5:0), + TVO_DATA5_DELAY = BITS(13:8), + TVO_DATA6_DELAY = BITS(21:16), + TVO_DATA7_DELAY = BITS(29:24), + + VIP_VSCALER_CNTL1 = 0x01c0, + UV_INC = BITS(15:0), // Vertical scaling of CRTC UV data + UV_THINNER = BITS(22:16), // Number of lines in the UV data that + // are not blended to create a line on TV + Y_W_EN = BITS(24:24), + Y_DEL_W_SIG = BITS(27:26), + + VIP_VSCALER_CNTL2 = 0x01c8, + DITHER_MODE = BITS(0:0), // Select dithering mode + Y_OUTPUT_DITHER_EN = BITS(1:1), + UV_OUTPUT_DITHER_EN = BITS(2:2), + UV_TO_BUF_DITHER_EN = BITS(3:3), + UV_ACCUM_INIT = BITS(31:24), + + VIP_Y_FALL_CNTL = 0x01cc, + Y_FALL_ACCUM_INIT = BITS(15:0), + Y_FALL_PING_PONG = BITS(16:16), + Y_COEF_EN = BITS(17:17), + Y_COEF_VALUE = BITS(31:24), + + VIP_Y_RISE_CNTL = 0x01d0, + Y_RISE_ACCUM_INIT = BITS(15:0), + Y_RISE_PING_PONG = BITS(16:16), + + VIP_Y_SAW_TOOTH_CNTL = 0x01d4, + Y_SAW_TOOTH_MAP = BITS(15:0), + Y_SAW_TOOTH_SLOPE = BITS(31:16), + + // TVOut Shadow CRTC + VIP_HTOTAL = 0x0080, + D_HTOTAL = BITS(10:0), // Number of clocks per line (-1) + + VIP_HDISP = 0x0084, + D_HDISP = BITS(9:0), // Number of active pixels per line (-1) + + VIP_HSIZE = 0x0088, + D_HSIZE = BITS(9:0), // Unused in Rage Theater A21 and later + + VIP_HSTART = 0x008c, + D_HSTART = BITS(10:0), + + VIP_HCOUNT = 0x0090, // Current horizontal CRT pixel within + D_HCOUNT = BITS(10:0), // a line being processed + + VIP_VTOTAL = 0x0094, // Number of lines per frame (-1) + D_VTOTAL = BITS(9:0), + + VIP_VDISP = 0x0098, // Number of active lines (-1) + D_VDISP = BITS(9:0), + + VIP_VCOUNT = 0x009c, // Current vertical CRT line being processed + D_VCOUNT = BITS(9:0), + + VIP_VFTOTAL = 0x00a0, // Number of CRT frames that occur during + D_FTOTAL = BITS(3:0), // one complete cycle of the TV timing (-1) + + VIP_DFCOUNT = 0x00a4, // Current CRT frame count, equivalent to the + D_FCOUNT = BITS(3:0), // field number in a complete TV cycle + + VIP_DFRESTART = 0x00a8, // The frame/field during which a restart + D_FRESTART = BITS(3:0), // will be generated when TV_MASTER is 0 + + VIP_DHRESTART = 0x00ac, // Horizontal pixel during which a restart + D_HRESTART = BITS(10:0), // will be generated when TV_MASTER is 0 + + VIP_DVRESTART = 0x00b0, // Vertical line during which a restart + D_VRESTART = BITS(9:0), // will be generated when TV_MASTER is 0 + + VIP_SYNC_SIZE = 0x00b4, // Number of pixels expected in the + D_SYNC_SIZE = BITS(9:0), // synchronization line + + VIP_FRAME_LOCK_CNTL = 0x0100, + MAX_LOCK_STR = BITS(3:0), + FIELD_SYNC_EN = BITS(4:4), + FIELD_SYNC_TRIGGER = BITS(5:5), + ACTUAL_LOCK_STR = BITS(11:8), + AVG_MISSED_SYNC = BITS(15:12), + + VIP_SYNC_LOCK_CNTL = 0x0104, + PRI_TVO_DATA_LINE_SEL = BITS(2:0), + SEC_SYNC_CHECK_DEL = BITS(13:8), + MPP_ACTIVE_AS_MASK = BITS(14:14), + MPP_ACTIVE_DS_MASK = BITS(15:15), + SYNC_COMMAND = BITS(18:16), + SYNC_LOCK_TRIGGER = BITS(21:21), + DETECT_EN = BITS(24:24), + SELF_LOCK_EN = BITS(25:25), + MAN_LOCK_EN = BITS(26:26), + MAX_REF_OFFSET = BITS(31:27), + + // TVOut Up Sampling Filter + VIP_UPSAMP_COEFF0_0 = 0x0340, + COEFF0_0 = BITS(5:0), + COEFF0_1 = BITS(14:8), + COEFF0_2 = BITS(22:16), + COEFF0_3 = BITS(29:24), + + VIP_UPSAMP_COEFF0_1 = 0x0344, + COEFF0_4 = BITS(7:0), + COEFF0_5 = BITS(15:8), + COEFF0_6 = BITS(21:16), + COEFF0_7 = BITS(30:24), + + VIP_UPSAMP_COEFF0_2 = 0x0348, + COEFF0_8 = BITS(6:0), + COEFF0_9 = BITS(13:8), + + VIP_UPSAMP_COEFF1_0 = 0x034c, + COEFF1_0 = BITS(5:0), + COEFF1_1 = BITS(14:8), + COEFF1_2 = BITS(22:16), + COEFF1_3 = BITS(29:24), + + VIP_UPSAMP_COEFF1_1 = 0x0350, + COEFF1_4 = BITS(7:0), + COEFF1_5 = BITS(15:8), + COEFF1_6 = BITS(21:16), + COEFF1_7 = BITS(30:24), + + VIP_UPSAMP_COEFF1_2 = 0x0354, + COEFF1_8 = BITS(6:0), + COEFF1_9 = BITS(13:8), + + VIP_UPSAMP_COEFF2_0 = 0x0358, + COEFF2_0 = BITS(5:0), + COEFF2_1 = BITS(14:8), + COEFF2_2 = BITS(22:16), + COEFF2_3 = BITS(29:24), + + VIP_UPSAMP_COEFF2_1 = 0x035c, + COEFF2_4 = BITS(7:0), + COEFF2_5 = BITS(15:8), + COEFF2_6 = BITS(21:16), + COEFF2_7 = BITS(30:24), + + VIP_UPSAMP_COEFF2_2 = 0x0360, + COEFF2_8 = BITS(6:0), + COEFF2_9 = BITS(13:8), + + VIP_UPSAMP_COEFF3_0 = 0x0364, + COEFF3_0 = BITS(5:0), + COEFF3_1 = BITS(14:8), + COEFF3_2 = BITS(22:16), + COEFF3_3 = BITS(29:24), + + VIP_UPSAMP_COEFF3_1 = 0x0368, + COEFF3_4 = BITS(7:0), + COEFF3_5 = BITS(15:8), + COEFF3_6 = BITS(21:16), + COEFF3_7 = BITS(30:24), + + VIP_UPSAMP_COEFF3_2 = 0x036c, + COEFF3_8 = BITS(6:0), + COEFF3_9 = BITS(13:8), + + VIP_UPSAMP_COEFF4_0 = 0x0370, + COEFF4_0 = BITS(5:0), + COEFF4_1 = BITS(14:8), + COEFF4_2 = BITS(22:16), + COEFF4_3 = BITS(29:24), + + VIP_UPSAMP_COEFF4_1 = 0x0374, + COEFF4_4 = BITS(7:0), + COEFF4_5 = BITS(15:8), + COEFF4_6 = BITS(21:16), + COEFF4_7 = BITS(30:24), + + VIP_UPSAMP_COEFF4_2 = 0x0378, + COEFF4_8 = BITS(6:0), + COEFF4_9 = BITS(13:8), + + // TVOut Encoder + VIP_SYNC_CNTL = 0x0050, + SYNC_OE = BITS(0:0), // Sync output enable + SYNC_OUT = BITS(1:1), // Sync output data + SYNC_IN = BITS(2:2), // Sync input data + SYNC_PUB = BITS(3:3), // Sync pull-up enable + SYNC_PD = BITS(4:4), // Sync pull-down enable + SYNC_DRV = BITS(5:5), // Sync drive select + SYNC_MX = BITS(11:8), // Sync mux + + VIP_HOST_READ_DATA = 0x0180, + HOST_RD_DATA_W0 = BITS(15:0), + HOST_RD_DATA_W1 = BITS(27:16), + + VIP_HOST_WRITE_DATA = 0x0184, + HOST_WR_DATA_W0 = BITS(15:0), + HOST_WR_DATA_W1 = BITS(27:16), + + VIP_HOST_RD_WT_CNTL = 0x0188, + HOST_ADR = BITS(8:0), + HOST_FIFO_RD = BITS(12:12), + HOST_FIFO_RD_ACK = BITS(13:13), + HOST_FIFO_WT = BITS(14:14), + HOST_FIFO_WT_ACK = BITS(15:15), + + VIP_TIMING_CNTL = 0x01c4, + H_INC = BITS(11:0), // Horizontal scaling of the TV image + REQ_DELAY = BITS(18:16), + REQ_Y_FIRST = BITS(19:19), + FORCE_BURST_ALWAYS = BITS(21:21), + UV_POST_SCALE_BYPASS = BITS(23:23), + UV_OUTPUT_POST_SCALE = BITS(31:24), + + VIP_UPSAMP_AND_GAIN_CNTL = 0x01e0, + YUPSAMP_EN = BITS(0:0), // Enable Y upsampling filter + YUPSAMP_FLAT = BITS(1:1), // Force Y upsampling to use centre tap + UVUPSAMP_EN = BITS(2:2), // Enable U/V upsampling filters + UVUPSAMP_FLAT = BITS(3:3), // Force U/V upsampling to use centre tap + Y_BREAK_EN = BITS(8:8), // Enable Y break point + UV_BREAK_EN = BITS(10:10), // Enable U/V break point + + VIP_GAIN_LIMIT_SETTINGS = 0x01e4, + Y_GAIN_LIMIT = BITS(10:0), // Gain limit for the luminance (Y) + UV_GAIN_LIMIT = BITS(24:16), // Gain limit for the chrominance (U/V) + + VIP_LINEAR_GAIN_SETTINGS = 0x01e8, + Y_GAIN = BITS(8:0), // Gain for the luminance (1.8 fixed point) + UV_GAIN = BITS(24:16), // Gain for the chrominance (1.8 fixed point) + + VIP_MODULATOR_CNTL1 = 0x0200, + YFLT_EN = BITS(2:2), // Enable Composite/SVideo Y filter + UVFLT_EN = BITS(3:3), // Enable U/V filters + ALT_PHASE_EN = BITS(6:6), // Phase alternating line (0=NTSC, 1=PAL) + SYNC_TIP_LEVEL = BITS(7:7), // Composite Y sync tip level + SET_UP_LEVEL = BITS(14:8), // Video setup level + BLANK_LEVEL = BITS(22:16), // Video blank level + SLEW_RATE_LIMIT = BITS(23:23), + FORCE_BLACK_WHITE = BITS(24:24), // Force B&W video + Y_FILT_BLEND = BITS(31:28), // Sharpness of Y filters + + VIP_MODULATOR_CNTL2 = 0x0204, + U_BURST_LEVEL = BITS(8:0), + V_BUST_LEVEL = BITS(24:16), + + VIP_PRE_DAC_MUX_CNTL = 0x0240, + Y_RED_EN = BITS(0:0), + C_GRN_EN = BITS(1:1), + CMP_BLU_EN = BITS(2:2), + DAC_DITHER_EN = BITS(3:3), + RED_MX = BITS(7:4), + GRN_MX = BITS(11:8), + BLU_MX = BITS(15:12), + FORCE_DAC_DATA = BITS(25:16), + YUPFILT_DISABLE = BITS(26:26), + CVUPFILT_DISABLE = BITS(27:27), + UUPFILT_DISABLE = BITS(28:28), + + VIP_TV_DAC_CNTL = 0x0280, + NBLANK = BITS(0:0), + NHOLD = BITS(1:1), + PEDESTAL = BITS(2:2), + DASLEEP = BITS(3:3), + DETECT = BITS(4:4), + CMPOUT = BITS(5:5), + BGSLEEP = BITS(6:6), + STD = BITS(9:8), + MON = BITS(15:12), + + VIP_CRC_CNTL = 0x02c0, + V_COMP_DATA_EN = BITS(1:0), + V_COMP_GATE = BITS(2:2), + V_COMP_EN = BITS(3:3), + RST_SUBC_ONRSTRT = BITS(4:4), + CRC_TV_RSTRT_SEL = BITS(5:5), + + VIP_VIDEO_PORT_SIG = 0x02c4, + CRC_SIG = BITS(29:0), + + VIP_UV_ADR = 0x0300, + MAX_UV_ADDR = BITS(7:0), + TABLE1_BOT_ADR = BITS(15:8), + TABLE3_TOP_ADR = BITS(23:16), + HCODE_TABLE_SEL = BITS(26:25), + VCODE_TABLE_SEL = BITS(28:27), + SWITCH_TABLE_REQ = BITS(31:31), + + // TVOut VBI Control + VIP_VBI_CC_CNTL = 0x02c8, + VBI_CC_DATA = BITS(15:0), // VBI data for CC + VBI_CC_WT = BITS(24:24), // Initiates a write cycle using VBI_CC_DATA + VBI_CC_WT_ACK = BITS(25:25), + VBI_CC_HOLD = BITS(26:26), + VBI_DECODE_EN = BITS(31:31), + + VIP_VBI_EDS_CNTL = 0x02cc, + VBI_EDS_DATA = BITS(15:0), + VBI_EDS_WT = BITS(24:24), + VBI_EDS_WT_ACK = BITS(25:25), + VBI_EDS_HOLD = BITS(26:26), + + VIP_VBI_20BIT_CNTL = 0x02d0, + VBI_20BIT_DATA0 = BITS(15:0), + VBI_20BIT_DATA1 = BITS(19:16), + VBI_20BIT_WT = BITS(24:24), + VBI_20BIT_WT_ACK = BITS(25:25), + VBI_20BIT_HOLD = BITS(26:26), + + VIP_VBI_DTO_CNTL = 0x02d4, + VBI_CC_DTO_P = BITS(15:0), + VBI_20BIT_DTO_P = BITS(31:16), + + VIP_VBI_LEVEL_CNTL = 0x02d8, + VBI_CC_LEVEL = BITS(6:0), + VBI_20BIT_LEVEL = BITS(14:8), + VBI_CLK_RUNIN_GAIN = BITS(24:16), + + // Video Decoder Horizontal Sync PLL Control + VIP_HS_PLINE = 0x0480, // Pixels per line (910) + HS_LINE_TOTAL = BITS(10:0), + + VIP_HS_DTOINC = 0x0484, // ??? + HS_DTO_INC = BITS(19:0), + + VIP_HS_PLLGAIN = 0x0488, + HS_PLL_SGAIN = BITS(3:0), + HS_PLL_FGAIN = BITS(7:4), + + VIP_HS_MINMAXWIDTH = 0x048c, + MIN_PULSE_WIDTH = BITS(7:0), + MAX_PULSE_WIDTH = BITS(15:8), + + VIP_HS_GENLOCKDELAY = 0x0490, + GEN_LOCK_DELAY = BITS(7:0), + + VIP_HS_WINDOW_LIMIT = 0x0494, + WIN_CLOSE_LIMIT = BITS(10:0), + WIN_OPEN_LIMIT = BITS(26:16), + + VIP_HS_WINDOW_OC_SPEED = 0x0498, + WIN_CLOSE_SPEED = BITS(3:0), + WIN_OPEN_SPEED = BITS(7:4), + + VIP_HS_PULSE_WIDTH = 0x049c, + H_SYNC_PULSE_WIDTH = BITS(7:0), + HS_GENLOCKED = BITS(8:8), // HPLL is locked? + HS_SYNC_IN_WIN = BITS(9:9), // Sync in Hwindow? + + VIP_HS_PLL_ERROR = 0x04a0, + HS_PLL_ERROR = BITS(14:0), + + VIP_HS_PLL_FS_PATH = 0x04a4, + HS_PLL_FAST_PATH = BITS(14:0), + HS_PLL_SLOW_PATH = BITS(30:16), + + // Video Decoder Comb Filter + VIP_COMB_CNTL0 = 0x0440, + COMB_HCK = BITS(7:0), + COMB_VCK = BITS(15:8), + COMB_FILTER_EN = BITS(16:16), // 0=fast AGC, CLAMP, and Chroma AGC loops (A41 ASIC only) + COMB_ADAPTIVE_EN = BITS(17:17), + COMB_BPFMUXSEL = BITS(20:18), + COMB_COUTSEL = BITS(22:21), + COMB_SUMDIFF0SEL = BITS(23:23), + COMB_SUMDIFF1SEL = BITS(25:24), + COMB_YVLPFSEL = BITS(26:26), + COMB_DLYLINESEL = BITS(28:27), + COMB_YDLYINSEL = BITS(30:29), + COMB_YSUBBW = BITS(31:31), + + VIP_COMB_CNTL1 = 0x0444, + COMB_YDLYOUTSEL = BITS(1:0), + COMB_CORESIZE = BITS(3:2), + COMB_YSUBEN = BITS(4:4), + COMB_YOUTSEL = BITS(5:5), + COMB_SYNCLPFSEL = BITS(7:6), + COMB_SYNCLPFRST = BITS(8:8), + COMB_DEBUG = BITS(9:9), + + VIP_COMB_CNTL2 = 0x0448, + COMB_HYK0 = BITS(7:0), + COMB_VYK0 = BITS(15:8), + COMB_HYK1 = BITS(23:16), + COMB_VYK1 = BITS(31:24), + + VIP_COMB_LINE_LENGTH = 0x044c, + COMB_TAP0LENGTH = BITS(10:0), + COMB_TAP1LENGTH = BITS(27:16), + + VIP_NOISE_CNTL0 = 0x0450, + NR_EN = BITS(0:0), + NR_GAIN_CNTL = BITS(3:1), + NR_BW_TRESH = BITS(9:4), + NR_GC_TRESH = BITS(14:10), + NR_COEF_DESPEC_IMODE = BITS(15:15), + + // Video Decoder ADC Control + VIP_ADC_CNTL = 0x0400, + INPUT_SELECT = BITS(2:0), // Video input mux select + INPUT_SELECT_COMP0 = 0 << 0, // Tuner + INPUT_SELECT_COMP1 = 1 << 0, // Front Comp1 + INPUT_SELECT_COMP2 = 2 << 0, // Rear Comp1 + INPUT_SELECT_YF_COMP3 = 3 << 0, // Front Comp2 + INPUT_SELECT_YR_COMP4 = 4 << 0, // Rear Comp2 + INPUT_SELECT_YCF_COMP3 = 5 << 0, // Front YC + INPUT_SELECT_YCR_COMP4 = 6 << 0, // Rear YC + I_CLAMP_SEL = BITS(4:3), // Clamp charge-pump current select + I_CLAMP_SEL_0_3 = 0 << 3, // 0.3 uA + I_CLAMP_SEL_7 = 1 << 3, // 7.0 uA + I_CLAMP_SEL_15 = 2 << 3, // 15.0 uA + I_CLAMP_SEL_22 = 3 << 3, // 22.0 uA + I_AGC_SEL = BITS(6:5), // AGC charge-pump current select + I_AGC_SEL_0_3 = 0 << 5, // 0.3 uA + I_AGC_SEL_7 = 1 << 5, // 7.0 uA + I_AGC_SEL_15 = 2 << 5, // 15.0 uA + I_AGC_SEL_22 = 3 << 5, // 22.0 uA + ADC_PDWN = BITS(7:7), // AGC power-down select + ADC_PDWN_UP = 0 << 7, // Power up (for capture mode) + ADC_PDWN_DOWN = 1 << 7, // Power down + EXT_CLAMP_CAP = BITS(8:8), // Clamp charge cap select + EXT_CLAMP_CAP_INTERNAL = 0 << 8, // Use internal Clamp Cap. + EXT_CLAMP_CAP_EXTERNAL = 1 << 8, // Use external Clamp Cap. + EXT_AGC_CAP = BITS(9:9), // AGC charge Cap. select + EXT_AGC_CAP_INTERNAL = 0 << 9, // Use internal AGC Cap. + EXT_AGC_CAP_EXTERNAL = 1 << 9, // Use external AGC Cap. + ADC_DECI_BYPASS = BITS(10:10), // ADC video data decimation filter select + ADC_DECI_WITH_FILTER = 0 << 10, // Decimate ADC data with filtering + ADC_DECI_WITHOUT_FILTER = 1 << 10, // Decimate ADC data with no filtering + VBI_DECI_BYPASS = BITS(11:11), // ADC VBI data decimation filter select + VBI_DECI_WITH_FILTER = 0 << 11, // Decimate VBI data from ADC with filtering + VBI_DECI_WITHOUT_FILTER = 1 << 11, // Decimate VBI data from ADC with no filtering + DECI_DITHER_EN = BITS(12:12), // Decimation filter output dither enable + ADC_CLK_SEL = BITS(13:13), // ADC clock select + ADC_CLK_SEL_4X = 0 << 13, // Run ADC at 4x Fsc + ADC_CLK_SEL_8X = 1 << 13, // Run ADC at 8x Fsc + ADC_BYPASS = BITS(15:14), // ADC data path select + ADC_BYPASS_INTERNAL = 0 << 14, // Use internal ADC + ADC_BYPASS_EXTERNAL = 1 << 14, // Use external ADC + ADC_BYPASS_SINGLE = 2 << 14, // Use single step data + ADC_CH_GAIN_SEL = BITS(17:16), // Analog Chroma gain select + ADC_CH_GAIN_SEL_NTSC = 0 << 16, // Set chroma gain for NTSC + ADC_CH_GAIN_SEL_PAL = 1 << 16, // Set chroma gain for PAL + ADC_PAICM = BITS(19:18), // AMP common mode voltage select + ADC_PDCBIAS = BITS(21:20), // DC 1.5V bias programmable select + ADC_PREFHI = BITS(23:22), // ADC voltage reference high + ADC_PREFHI_2_7 = 0 << 22, // 2.7V (recommended) + ADC_PREFHI_2_6 = 1 << 22, // 2.6V + ADC_PREFHI_2_5 = 2 << 22, // 2.5V + ADC_PREFHI_2_4 = 3 << 22, // 2.4V + ADC_PREFLO = BITS(25:24), // ADC voltage reference low + ADC_PREFLO_1_8 = 0 << 24, // 1.8V + ADC_PREFLO_1_7 = 1 << 24, // 1.7V + ADC_PREFLO_1_6 = 2 << 24, // 1.6V + ADC_PREFLO_1_5 = 3 << 24, // 1.5V (recommended) + ADC_IMUXOFF = BITS(26:26), + ADC_CPRESET = BITS(27:27), // AGC charge pump reset + + VIP_ADC_DEBUG = 0x0404, + ADC_PTST = BITS(0:0), // AGC test mode enable + ADC_PYPDN = BITS(1:1), + ADC_PCPDN = BITS(2:2), // Chroma AGC path power down mode + ADC_PTSTA0 = BITS(3:3), // AGC test mux A select bit 0 + ADC_PTSTA1 = BITS(4:4), // AGC test mux A select bit 1 + ADC_PTSTB0 = BITS(5:5), // AGC test mux B select bit 0 + ADC_PTSTB1 = BITS(6:6), // AGC test mux B select bit 1 + ADC_TSTADC = BITS(7:7), // Luma & Chroma ADC test mode + ADC_TSTPROBEY = BITS(8:8), // Luma AGC/ADC test mode + ADC_TSTPROBEC = BITS(9:9), // Chroma AGC/ADC test mode + ADC_TSTPROBEADC = BITS(10:10), // Chroma ADC test structure probe mode + ADC_TSTADCBIAS = BITS(11:11), // Chroma ADC bias node probe mode + ADC_TSTADCREFM = BITS(12:12), // Middle reference point for Luma & Chroma ADC probe + ADC_TSTADCFBP = BITS(13:13), // Chroma ADC folding block positive output probe mode + ADC_TSTADCFBN = BITS(14:14), // Chroma ADC folding block negative output probe mode + ADC_TSTADCCMP1 = BITS(15:15), // Chroma ADC comparator #1 output probe mode + ADC_TSTADCCMP9 = BITS(16:16), // Chroma ADC comparator #9 output probe mode + ADC_TSTADCCMP17 = BITS(17:17), // Chroma ADC comparator #19 output probe mode + ADC_TSTADCLATCH = BITS(18:18), // Dummy latch test mode + ADC_TSTADCCOMP = BITS(19:19), // Dummy comparator test mode + + VIP_THERMO2BIN_STATUS = 0x040c, + YOVERFLOW = BITS(0:0), + YUNDERFLOW = BITS(1:1), + YMSB_LOW_BY_ONE = BITS(2:2), + YMSB_HI_BY_ONE = BITS(3:3), + COVERFLOW = BITS(4:4), + CUNDERFLOW = BITS(5:5), + CMSB_LOW_BY_ONE = BITS(6:6), + CMSB_HI_BY_ONE = BITS(7:7), + + // Video Decoder Sync Generator + VIP_SG_BLACK_GATE = 0x04c0, // horizontal blank + BLANK_INT_START = BITS(7:0), // start of horizontal blank (49) + BLANK_INT_LENGTH = BITS(11:8), + + VIP_SG_SYNCTIP_GATE = 0x04c4, // synctip pulse + SYNC_TIP_START = BITS(10:0), // start of sync pulse (882) + SYNC_TIP_LENGTH = BITS(15:12), + + VIP_SG_UVGATE_GATE = 0x04c8, // chroma burst + UV_INT_START = BITS(7:0), // start of chroma burst (59) + U_INT_LENGTH = BITS(11:8), + V_INT_LENGTH = BITS(15:12), + + // Video Decoder Luminance Processor + VIP_LP_AGC_CLAMP_CNTL0 = 0x0500, // Luma AGC Clamp control + SYNCTIP_REF0 = BITS(7:0), // 40 IRE reference + SYNCTIP_REF1 = BITS(15:8), + CLAMP_REF = BITS(23:16), + AGC_PEAKWHITE = BITS(31:24), + + VIP_LP_AGC_CLAMP_CNTL1 = 0x0504, // Luma AGC Clamp control + VBI_PEAKWHITE = BITS(7:0), // + CLAMPLOOP_EN = BITS(24:24), // Run Clamp loop + CLAMPLOOP_INV = BITS(25:25), // Negative Clamp Loop + AGCLOOP_EN = BITS(26:26), // Run AGC loop + AGCLOOP_INV = BITS(27:27), // Negative AGC loop + + VIP_LP_BRIGHTNESS = 0x0508, // Luma Brightness control + BRIGHTNESS = BITS(13:0), // Brightness level + LUMAFLT_SEL = BITS(15:15), // Select flat filter + + VIP_LP_CONTRAST = 0x050c, // Luma Contrast level + CONTRAST = BITS(7:0), // Contrast level + DITHER_SEL = BITS(9:8), // Dither selection + DITHER_SEL_TRUNC = 0 << 8, // Truncation + DITHER_SEL_ROUND = 1 << 8, // Round + DITHER_SEL_4BIT = 2 << 8, // 4 bit error + DITHER_SEL_9BIT = 3 << 9, // 9 bit error + + VIP_LP_SLICE_LIMIT = 0x0510, + SLICE_LIMIT_HI = BITS(7:0), + SLICE_LIMIT_LO = BITS(15:8), + SLICE_LIMIT = BITS(23:16), + + VIP_LP_WPA_CNTL0 = 0x0514, + WPA_THRESHOLD = BITS(10:0), + + VIP_LP_WPA_CNTL1 = 0x0518, + WPA_TRIGGER_LO = BITS(9:0), + WPA_TRIGGER_HI = BITS(25:16), + + VIP_LP_BLACK_LEVEL = 0x051c, + BLACK_LEVEL = BITS(12:0), + + VIP_LP_SLICE_LEVEL = 0x0520, + SLICE_LEVEL = BITS(7:0), + + VIP_LP_SYNCTIP_LEVEL = 0x0524, + SYNCTIP_LEVEL = BITS(12:0), + + VIP_LP_VERT_LOCKOUT = 0x0528, + LP_LOCKOUT_START = BITS(9:0), + LP_LOCKOUT_END = BITS(25:16), + + // Video Decoder Vertical Sync Detector/Counter + VIP_VS_DETECTOR_CNTL = 0x0540, + VSYNC_INT_TRIGGER = BITS(10:0), + VSYNC_INT_HOLD = BITS(26:16), + + VIP_VS_BLANKING_CNTL = 0x0544, + VS_FIELD_BLANK_START = BITS(9:0), + VS_FIELD_BLANK_END = BITS(25:16), + + VIP_VS_FIELD_ID_CNTL = 0x0548, + VS_FIELD_ID_LOCATION = BITS(8:0), + + VIP_VS_COUNTER_CNTL = 0x054c, // Vertical Sync Counter control + FIELD_DETECT_MODE = BITS(1:0), // Field detection mode + FIELD_DETECT_ARTIFICIAL = 0 << 0, // Use artificial field + FIELD_DETECT_DETECTED = 1 << 0, // Use detected field + FIELD_DETECT_AUTO = 2 << 0, // Auto switch to Artificial if interlace is lost + FIELD_DETECT_FORCE = 3 << 0, // Use field force bit + FIELD_FLIP_EN = BITS(2:2), // Flip the fields + FIELD_FORCE_EN = BITS(3:3), // Force field number + VSYNC_WINDOW_EN = BITS(4:4), // Enable VSYNC window + + VIP_VS_FRAME_TOTAL = 0x0550, + VS_FRAME_TOTAL = BITS(9:0), // number of lines per frame + + VIP_VS_LINE_COUNT = 0x0554, + VS_LINE_COUNT = BITS(9:0), // current line counter + VS_ITU656_VB = BITS(13:13), + VS_ITU656_FID = BITS(14:14), + VS_INTERLACE_DETECTED = BITS(15:15), + VS_DETECTED_LINES = BITS(25:16), // detected number of lines per frame + CURRENT_FIELD = BITS(27:27), // current field number (odd or even) + PREVIOUS_FIELD = BITS(28:28), // previous field number (odd or even) + ARTIFICIAL_FIELD = BITS(29:29), + VS_WINDOW_COUNT = BITS(31:30), + + // Video Decoder Chroma Processor + VIP_CP_PLL_CNTL0 = 0x0580, + CH_DTO_INC = BITS(23:0), + CH_PLL_SGAIN = BITS(27:24), + CH_PLL_FGAIN = BITS(31:28), + + VIP_CP_PLL_CNTL1 = 0x0584, + VFIR = BITS(0:0), // 0=disable, 1=enable phase filter FIR + PFLIP = BITS(1:1), // 0=Use PAL/SECAM Vswitch as detected + // 1=Flip detected PAL/SECAM Vswitch + PFILT = BITS(2:2), // 0=Use sign bit of phase error for PAL Vswitch + // 1=Use PAL Vswitch filter + + VIP_CP_HUE_CNTL = 0x0588, + HUE_ADJ = BITS(7:0), // Hue adjustment + + VIP_CP_BURST_GAIN = 0x058c, + CR_BURST_GAIN = BITS(8:0), + CB_BURST_GAIN = BITS(24:16), + + VIP_CP_AGC_CNTL = 0x0590, + CH_HEIGHT = BITS(7:0), + CH_KILL_LEVEL = BITS(15:8), + CH_AGC_ERROR_LIM = BITS(17:16), // Force error to 0, 1, 2 or 3 + CH_AGC_FILTER_EN = BITS(18:18), // 0=disable, 1=enable filter + CH_AGC_LOOP_SPEED = BITS(19:19), // 0=slow, 1=fast + + VIP_CP_ACTIVE_GAIN = 0x0594, + CRDR_ACTIVE_GAIN = BITS(9:0), // Saturation adjustment + CBDB_ACTIVE_GAIN = BITS(25:16), + + VIP_CP_PLL_STATUS0 = 0x0598, + CH_GAIN_ACC0 = BITS(13:0), + CH_GAIN_ACC1 = BITS(29:16), + + VIP_CP_PLL_STATUS1 = 0x059c, + CH_VINT_OUT = BITS(18:0), + + VIP_CP_PLL_STATUS2 = 0x05a0, + CH_UINT_OUT = BITS(12:0), + CH_VSWITCH = BITS(16:16), + CH_SECAM_SWITCH = BITS(17:17), + CH_PAL_SWITCH = BITS(18:18), + CH_PAL_FLT_STAT = BITS(21:19), + CH_COLOR_KILL = BITS(22:22), + + VIP_CP_PLL_STATUS3 = 0x05a4, + CH_ERROR_INT0 = BITS(20:0), + + VIP_CP_PLL_STATUS4 = 0x05a8, + CH_ERROR_INT1 = BITS(20:0), + + VIP_CP_PLL_STATUS5 = 0x05ac, + CH_FAST_PATH = BITS(24:0), + + VIP_CP_PLL_STATUS6 = 0x05b0, + CH_SLOW_PATH = BITS(24:0), + + VIP_CP_PLL_STATUS7 = 0x05b4, + FIELD_BPHASE_COUNT = BITS(5:0), + BPHASE_BURST_COUNT = BITS(13:8), + + VIP_CP_DEBUG_FORCE = 0x05b8, + GAIN_FORCE_DATA = BITS(11:0), + GAIN_FORCE_EN = BITS(12:12), // 0=disable, 1==enable force chroma gain + + VIP_CP_VERT_LOCKOUT = 0x05bc, + CP_LOCKOUT_START = BITS(9:0), + CP_LOCKOUT_END = BITS(25:16), + + // Video Decoder Clip Engine and VBI Control + VIP_H_ACTIVE_WINDOW = 0x05c0, + H_ACTIVE_START = BITS(10:0), // Horizotal active window + H_ACTIVE_END = BITS(26:16), + + VIP_V_ACTIVE_WINDOW = 0x05c4, + V_ACTIVE_START = BITS(9:0), // Vertical active window + V_ACTIVE_END = BITS(25:16), + + VIP_H_VBI_WINDOW = 0x05c8, + H_VBI_WIND_START = BITS(10:0), // Horizontal VBI window + H_VBI_WIND_END = BITS(26:16), + + VIP_V_VBI_WINDOW = 0x05cc, + V_VBI_WIND_START = BITS(9:0), // Vertical VBI window + V_VBI_WIND_END = BITS(25:16), + + VIP_VBI_CONTROL = 0x05d0, + VBI_CAPTURE_ENABLE = BITS(1:0), // Select VBI capture + VBI_CAPTURE_DIS = 0 << 0, // Disable VBI capture + VBI_CAPTURE_EN = 1 << 0, // Enable VBI capture + VBI_CAPTURE_RAW = 2 << 0, // Enable Raw Video capture + + + // Video Decoder Standard + VIP_STANDARD_SELECT = 0x0408, + STANDARD_SEL = BITS(1:0), // Select video standard + STANDARD_NTSC = 0 << 0, // NTSC + STANDARD_PAL = 1 << 0, // PAL + STANDARD_SECAM = 2 << 0, // SECAM + YC_MODE = BITS(2:2), // Select YC video mode + YC_MODE_COMPOSITE = 0 << 2, // Composite + YC_MODE_SVIDEO = 1 << 2, // SVideo + + // Video In Scaler and DVS Port + VIP_SCALER_IN_WINDOW = 0x0618, // Scaler In Window + H_IN_WIND_START = BITS(10:0), // Horizontal start + V_IN_WIND_START = BITS(25:16), // Vertical start + + VIP_SCALER_OUT_WINDOW = 0x061c, // Scaler Out Window + H_OUT_WIND_WIDTH = BITS(9:0), // Horizontal output window width + V_OUT_WIND_HEIGHT = BITS(24:16), // Vertical output window height + + VIP_H_SCALER_CONTROL = 0x0600, // Horizontal Scaler control + H_SCALE_RATIO = BITS(20:0), // Horizontal scale ratio (5.16 fixed point) + H_SHARPNESS = BITS(28:25), // Sharpness control (15=6dB high frequency boost) + H_BYPASS = BITS(30:30), // Horizontal bypass enable + + VIP_V_SCALER_CONTROL = 0x0604, // Vertical Scaler control + V_SCALE_RATIO = BITS(11:0), // Vertical scaling ratio (1.11 fixed point) + V_DEINTERLACE_ON = BITS(12:12), // Enable deinterlacing + V_FIELD_FLIP = BITS(13:13), // Invert field flag + V_BYPASS = BITS(14:14), // Enable vertical bypass + V_DITHER_ON = BITS(15:15), // Vertical path dither enable + + VIP_V_DEINTERLACE_CONTROL = 0x0608, // Deinterlace control + EVENF_OFFSET = BITS(10:0), // Even Field offset + ODDF_OFFSET = BITS(21:11), // Odd Field offset + + VIP_VBI_SCALER_CONTROL = 0x060c, // VBI Scaler control + VBI_SCALING_RATIO = BITS(16:0), // Scaling ratio for VBI data (1.16 fixed point) + VBI_ALIGNER_ENABLE = BITS(17:17), // VBI/Raw data aligner enable + + VIP_DVS_PORT_CTRL = 0x0610, // DVS Port control + DVS_DIRECTION = BITS(0:0), // DVS direction + DVS_DIRECTION_INPUT = 0 << 0, // Input mode + DVS_DIRECTION_OUTPUT = 1 << 0, // Output mode + DVS_VBI_BYTE_SWAP = BITS(1:1), // Output video stream type + DVS_VBI_BYTE_SEQUENTIAL = 0 << 1, // Sequential + DVS_VBI_BYTE_SWAPPED = 1 << 1, // Byte swapped + DVS_CLK_SELECT = BITS(2:2), // DVS output clock select + DVS_CLK_SELECT_8X = 0 << 2, // 8x Fsc + DVS_CLK_SELECT_27MHz = 1 << 2, // 27 MHz + CONTINUOUS_STREAM = BITS(3:3), // Enable continuous stream mode + DVSOUT_CLK_DRV = BITS(4:4), // 0=high, 1=low DVS port output clock buffer drive strength + DVSOUT_DATA_DRV = BITS(5:5), // 0=high, 1=low DVS port output data buffers driver strength + + VIP_DVS_PORT_READBACK = 0x0614, // DVS Port readback + DVS_OUTPUT_READBACK = BITS(7:0), // Data from DVS port fifo + + // Clock and Reset Control + VIP_CLKOUT_GPIO_CNTL = 0x0038, + CLKOUT0_SEL = BITS(2:0), // Select output to CLKOUT0_GPIO0 pin + CLKOUT0_SEL_REF_CLK = 0 << 0, // Reference Clock + CLKOUT0_SEL_L54_CLK = 1 << 0, // Lockable 54 MHz Clock + CLKOUT0_SEL_AUD_CLK = 2 << 0, // Audio Source Clock + CLKOUT0_SEL_DIV_AUD_CLK = 3 << 0, // Divided Audio Source Clock + CLKOUT0_SEL_BYTE_CLK = 4 << 0, // Byte Clock + CLKOUT0_SEL_PIXEL_CLK = 5 << 0, // Pixel Clock + CLKOUT0_SEL_TEST_MUX = 6 << 0, // Clock Test Mux Output + CLKOUT0_SEL_GPIO0_OUT = 7 << 0, // GPIO0_OUT + CLKOUT0_DRV = BITS(3:3), // Set drive strength for CLKOUT0_GPIO0 pin + CLKOUT0_DRV_8mA = 0 << 3, // 8 mA + CLKOUT0_DRV_4mA = 1 << 3, // 4 mA + CLKOUT1_SEL = BITS(6:4), // Select output to CLKOUT1_GPIO1 pin + CLKOUT1_SEL_REF_CLK = 0 << 4, // Reference Clock + CLKOUT1_SEL_L54_CLK = 1 << 4, // Lockable 54 MHz Clock + CLKOUT1_SEL_AUD_CLK = 2 << 4, // Audio Source Clock + CLKOUT1_SEL_DIV_AUD_CLK = 3 << 4, // Divided Audio Source Clock + CLKOUT1_SEL_PIXEL_CLK = 4 << 4, // Pixel Clock + CLKOUT1_SEL_SPDIF_CLK = 5 << 4, // SPDIF Clock + CLKOUT1_SEL_REG_CLK = 6 << 4, // Register Clock + CLKOUT1_SEL_GPIO1_OUT = 7 << 4, // GPIO1_OUT + CLKOUT1_DRV = BITS(7:7), // Set drive strength for CLKOUT1_GPIO1 pin + CLKOUT1_DRV_8mA = 0 << 7, // 8 mA + CLKOUT1_DRV_4mA = 1 << 7, // 4 mA + CLKOUT2_SEL = BITS(10:8), // Select output to CLKOUT2_GPIO2 pin + CLKOUT2_SEL_REF_CLK = 0 << 0, // Reference Clock + CLKOUT2_SEL_L54_CLK = 1 << 0, // Lockable 54 MHz Clock + CLKOUT2_SEL_AUD_CLK = 2 << 0, // Audio Source Clock + CLKOUT2_SEL_DIV_AUD_CLK = 3 << 0, // Divided Audio Source Clock + CLKOUT2_SEL_VIN_CLK = 4 << 0, // Video In Clock + CLKOUT2_SEL_VIN_SC_CLK = 5 << 0, // Video In Scaler Clock + CLKOUT2_SEL_TV_CLK = 6 << 0, // TV Clock + CLKOUT2_SEL_GPIO2_OUT = 7 << 0, // GPIO2_OUT + CLKOUT2_DRV = BITS(11:11), // Set drive strength for CLKOUT2_GPIO2 pin + CLKOUT2_DRV_8mA = 0 << 11, // 8 mA + CLKOUT2_DRV_4mA = 1 << 11, // 4 mA + CLKOUT1_DIV = BITS(23:16), // Postdivider for CLKOUT1 + CLKOUT2_DIV = BITS(31:24), // Postdivider for CLKOUT2 + + VIP_MASTER_CNTL = 0x0040, // Master control + TV_ASYNC_RST = BITS(0:0), // Reset several blocks that use the TV Clock + CRT_ASYNC_RST = BITS(1:1), // Reset several blocks that use the CRT Clock + RESTART_PHASE_FIX = BITS(3:3), // + TV_FIFO_ASYNC_RST = BITS(4:4), // + VIN_ASYNC_RST = BITS(5:5), // Reset several blocks that use the VIN, ADC or SIN Clock + AUD_ASYNC_RST = BITS(6:6), // Reset several blocks that use the SPDIF or I2S Clock + DVS_ASYNC_RST = BITS(7:7), // Reset several blocks that use the DVSOUT Clock + CLKOUT_CLK_SEL = BITS(8:8), // External BYTE clock + CRT_FIFO_CE_EN = BITS(9:9), // + TV_FIFO_CE_EN = BITS(10:10), + + VIP_CLKOUT_CNTL = 0x004c, + CLKOUT_OE = BITS(0:0), + CLKOUT_PUB = BITS(3:3), + CLKOUT_PD = BITS(4:4), + CLKOUT_DRV = BITS(5:5), + + VIP_TV_PLL_CNTL = 0x00c0, + TV_M0_LO = BITS(7:0), + TV_N0_LO = BITS(16:8), + TV_M0_HI = BITS(20:18), + TV_N0_HI = BITS(22:21), + TV_SLIP_EN = BITS(23:23), + TV_P = BITS(27:24), + TV_DTO_EN = BITS(28:28), + TV_DTO_TYPE = BITS(29:29), + TV_REF_CLK_SEL = BITS(31:30), + + VIP_CRT_PLL_CNTL = 0x00c4, + CRT_M0_LO = BITS(7:0), + CRT_N0_LO = BITS(16:8), + CRT_M0_HI = BITS(20:18), + CRT_N0_HI = BITS(22:21), + CRTCLK_USE_CLKBY2 = BITS(25:25), + CRT_MNFLIP_EN = BITS(26:26), + CRT_SLIP_EN = BITS(27:27), + CRT_DTO_EN = BITS(28:28), + CRT_DTO_TYPE = BITS(29:29), + CRT_REF_CLK_SEL = BITS(31:30), + + VIP_PLL_CNTL0 = 0x00c8, + TVRST = BITS(1:1), + CRTRST = BITS(2:2), + TVSLEEPB = BITS(3:3), + CRTSLEEPB = BITS(4:4), + TVPCP = BITS(10:8), + TVPVG = BITS(12:11), + TVPDC = BITS(15:13), + CRTPCP = BITS(18:16), + CRTPVG = BITS(20:19), + CRTPDC = BITS(23:21), + CKMONEN = BITS(24:24), + + VIP_PLL_TEST_CNTL = 0x00cc, + PLL_TEST = BITS(0:0), + PLL_TST_RST = BITS(1:1), + PLL_TST_DIV = BITS(2:2), + PLL_TST_CNT_RST = BITS(3:3), + STOP_REF_CLK = BITS(7:7), + PLL_TST_SEL = BITS(13:8), + PLL_TEST_COUNT = BITS(31:16), + + VIP_CLOCK_SEL_CNTL = 0x00d0, + TV_CLK_SEL = BITS(0:0), + CRT_CLK_SEL = BITS(1:1), + BYT_CLK_SEL = BITS(3:2), + PIX_CLK_SEL = BITS(4:4), + REG_CLK_SEL = BITS(5:5), + TST_CLK_SEL = BITS(6:6), + VIN_CLK_SEL = BITS(7:7), // Select VIN clock + VIN_CLK_SEL_REF_CLK = 0 << 7, // Select reference clock + VIN_CLK_SEL_VIPLL_CLK = 1 << 7, // Select VIN PLL clock + BYT_CLK_DEL = BITS(10:8), + AUD_CLK_SEL = BITS(11:11), + L54_CLK_SEL = BITS(12:12), + MV_ZONE_1_PHASE = BITS(13:13), + MV_ZONE_2_PHASE = BITS(14:14), + MV_ZONE_3_PHASE = BITS(15:15), + + VIP_VIN_PLL_CNTL = 0x00d4, // VIN PLL control + VIN_M0 = BITS(10:0), // Reference divider + VIN_N0 = BITS(21:11), // Feedback divider + VIN_MNFLIP_EN = BITS(22:22), // M/N flip enable + VIN_P = BITS(27:24), // Post divider + VIN_REF_CLK_SEL = BITS(31:30), // VIN reference source select + VIN_REF_CLK = 0 << 30, // Reference clock + VIN_SEC_REF_CLK = 1 << 30, // Secondary Reference Clock + VIN_L54_CLK = 2 << 30, // L54 PLL Clock + VIN_SLIP_L54_CLK = 3 << 30, // Slippable L54 PLL Clock + + VIP_VIN_PLL_FINE_CNTL = 0x00d8, + VIN_M1 = BITS(10:0), + VIN_N1 = BITS(21:11), + VIN_DIVIDER_SEL = BITS(22:22), + VIN_MNFLIP_REQ = BITS(23:23), + VIN_MNFLIP_DONE = BITS(24:24), + TV_LOCK_TO_VIN = BITS(27:27), + TV_P_FOR_VINCLK = BITS(31:28), + + VIP_AUD_PLL_CNTL = 0x00e0, + AUD_M0 = BITS(10:0), + AUD_N0 = BITS(21:11), + AUD_MNFLIP_EN = BITS(22:22), + AUD_SLIP_EN = BITS(23:23), + AUD_P = BITS(27:24), + AUD_DTO_EN = BITS(28:28), + AUD_DTO_TYPE = BITS(29:29), + AUD_REF_CLK_SEL = BITS(31:30), + + VIP_AUD_PLL_FINE_CNTL = 0x00e4, + AUD_M1 = BITS(10:0), + AUD_N1 = BITS(21:11), + AUD_DIVIDER_SEL = BITS(22:22), + AUD_MNFLIP_REQ = BITS(23:23), + AUD_MNFLIP_DONE = BITS(24:24), + AUD_SLIP_REQ = BITS(25:25), + AID_SLIP_DONE = BITS(26:26), + AUD_SLIP_COUNT = BITS(31:28), + + VIP_AUD_CLK_DIVIDERS = 0x00e8, + SPDIF_P = BITS(3:0), + I2S_P = BITS(7:4), + DIV_AUD_P = BITS(11:8), + + VIP_AUD_DTO_INCREMENTS = 0x00ec, + AUD_DTO_INC0 = BITS(15:0), + AUD_DTO_INC1 = BITS(31:16), + + VIP_L54_PLL_CNTL = 0x00f0, + L54_M0 = BITS(7:0), + L54_N0 = BITS(21:11), + L54_MNFLIP_EN = BITS(22:22), + L54_SLIP_EN = BITS(23:23), + L54_P = BITS(27:24), + L54_DTO_EN = BITS(28:28), + L54_DTO_TYPE = BITS(29:29), + L54_REF_CLK_SEL = BITS(30:30), + + VIP_L54_PLL_FINE_CNTL = 0x00f4, + L54_M1 = BITS(7:0), + L54_N1 = BITS(21:11), + L54_DIVIDER_SEL = BITS(22:22), + L54_MNFLIP_REQ = BITS(23:23), + L54_MNFLIP_DONE = BITS(24:24), + L54_SLIP_REQ = BITS(25:25), + L54_SLIP_DONE = BITS(26:26), + L54_SLIP_COUNT = BITS(31:28), + + VIP_L54_DTO_INCREMENTS = 0x00f8, + L54_DTO_INC0 = BITS(15:0), + L54_DTO_INC1 = BITS(31:16), + + VIP_PLL_CNTL1 = 0x00fc, + VINRST = BITS(1:1), // 0=active, 1=reset + AUDRST = BITS(2:2), + L54RST = BITS(3:3), + VINSLEEPB = BITS(4:4), + AUDSLEEPB = BITS(5:5), + L54SLEEPB = BITS(6:6), + VINPCP = BITS(10:8), + VINPVG = BITS(12:11), + VINPDC = BITS(15:13), + AUDPCP = BITS(18:16), + AUDPVG = BITS(20:19), + L54PCP = BITS(26:24), + L54PVG = BITS(28:27), + L54PDC = BITS(31:29), + + // Audio Interfaces + VIP_FIFOA_CONFIG = 0x0800, + ENT_FIFOA = BITS(8:0), + START_FIFOA = BITS(17:16), + START_FIFOA_ADDR_0 = 0 << 16, + START_FIFOA_ADDR_64 = 1 << 16, + START_FIFOA_ADDR_128 = 2 << 16, + START_FIFOA_ADDR_192 = 3 << 16, + END_FIFOA = BITS(19:18), + END_FIFOA_ADDR_63 = 0 << 18, + END_FIFOA_ADDR_127 = 1 << 18, + END_FIFOA_ADDR_191 = 2 << 18, + END_FIFOA_ADDR_255 = 3 << 18, + TEST_EN_FIFOA = BITS(20:20), + RST_FIFOA = BITS(21:21), + WT_FIFOA_FULL = BITS(22:22), + EMPTY_FIFOA = BITS(23:23), + + VIP_FIFOB_CONFIG = 0x0804, + ENT_FIFOB = BITS(8:0), + START_FIFOB = BITS(17:16), + END_FIFOB = BITS(19:18), + TEST_EN_FIFOB = BITS(20:20), + RST_FIFOB = BITS(21:21), + WT_FIFOB_FULL = BITS(22:22), + EMPTY_FIFOB = BITS(23:23), + + VIP_FIFOC_CONFIG = 0x0808, + ENT_FIFOC = BITS(8:0), + START_FIFOC = BITS(17:16), + END_FIFOC = BITS(19:18), + TEST_EN_FIFOC = BITS(20:20), + RST_FIFOC = BITS(21:21), + WT_FIFOC_FULL = BITS(22:22), + EMPTY_FIFOC = BITS(23:23), + + VIP_SPDIF_PORT_CNTL = 0x080c, + SPDIF_PORT_EN = BITS(0:0), + AC3_BURST_TRIGGER = BITS(1:1), + AC3_BURST_ACTIVE = BITS(2:2), + AC3_STREAM_MODE = BITS(3:3), + SWAP_AC3_ORDER = BITS(4:4), + TX_ON_NOT_EMPTY = BITS(5:5), + SPDIF_UNDERFLOW_CLEAR = BITS(6:6), + SPDIF_UNDERFLOW = BITS(7:7), + SPDIF_UNDERFLOW_CNT = BITS(15:8), + SPDIF_OE = BITS(16:16), + SPDIF_DRV = BITS(19:19), + PREAMBLE_AND_IDLE_SW = BITS(24:24), + + VIP_SPDIF_CHANNEL_STAT = 0x0810, + SPDIF_STATUS_BLOCK = BITS(0:0), + SPDIF_DATA_TYPE = BITS(1:1), + SPDIF_DIGITAL_COPY = BITS(2:2), + SPDIF_PREEMPHASIS = BITS(5:3), + SPDIF_MODE = BITS(7:6), + SPDIF_CATEGORY = BITS(15:8), + SPDIF_SRC_NUM = BITS(19:16), + SPDIF_NUM_CHANNELS = BITS(23:20), + SPDIF_SAMP_FREQ = BITS(27:24), + SPDIF_CLOCK_ACC = BITS(29:28), + + VIP_SPDIF_AC3_PREAMBLE = 0x0814, + AC3_DATA_TYPE = BITS(4:0), + AC3_ERR_FLAG = BITS(7:7), + AC3_DATA_DEPEN = BITS(12:8), + AC3_STREAM_NUM = BITS(15:13), + AC3_LENGTH_CODE = BITS(31:16), + + VIP_I2S_TRANSMIT_CNTL = 0x0818, + IISTX_PORT_EN = BITS(0:0), + IISTX_UNDERFLOW_CLEAR = BITS(6:6), + IISTX_UNDERFLOW = BITS(7:7), + IISTX_UNDERFLOW_FRAMES = BITS(15:8), + IIS_BITS_PER_CHAN = BITS(21:16), + IIS_SLAVE_EN = BITS(24:24), + IIS_LOOPBACK_EN = BITS(25:25), + ADO_OE = BITS(26:26), + ADIO_OE = BITS(29:29), + WS_OE = BITS(30:30), + BITCLK_OE = BITS(31:31), + + VIP_I2S_RECEIVE_CNTL = 0x081c, + IISRX_PORT_EN = BITS(0:0), + LOOPBACK_NO_UNDERFLOW = BITS(5:5), + IISRX_OVERFLOW_CLEAR = BITS(6:6), + IISRX_OVERFLOW = BITS(7:7), + IISRX_OVERFLOW_FRAMES = BITS(15:8), + + VIP_SPDIF_TX_CNT_REG = 0x0820, + SPDIF_TX_CNT = BITS(23:0), + SPDIF_TX_CNT_CLR = BITS(24:24), + + VIP_IIS_TX_CNT_REG = 0x0824, + IIS_TX_CNT = BITS(23:0), + IIS_TX_CNT_CLR = BITS(24:24), + + // Miscellaneous Registers + VIP_HW_DEBUG = 0x0010, + HW_DEBUG_TBD = BITS(15:0), + + VIP_SW_SCRATCH = 0x0014, + SW_SCRATCH_TBD = BITS(15:0), + + VIP_I2C_CNTL_0 = 0x0020, + I2C_DONE = BITS(0:0), + I2C_NACK = BITS(1:1), + I2C_HALT = BITS(2:2), + I2C_SOFT_RST = BITS(5:5), + SDA_DRIVE_EN = BITS(6:6), + I2C_DRIVE_SEL = BITS(7:7), + I2C_START = BITS(8:8), + I2C_STOP = BITS(9:9), + I2C_RECEIVE = BITS(10:10), + I2C_ABORT = BITS(11:11), + I2C_GO = BITS(12:12), + I2C_PRESCALE = BITS(31:16), + + VIP_I2C_CNTL_1 = 0x0024, + I2C_DATA_COUNT = BITS(3:0), + I2C_ADDR_COUNT = BITS(10:8), + SCL_DRIVE_EN = BITS(16:16), + I2C_SEL = BITS(17:17), + I2C_TIME_LIMIT = BITS(31:24), + + VIP_I2C_DATA = 0x0028, + I2C_DATA = BITS(7:0), + + VIP_INT_CNTL = 0x002c, + I2C_INT_EN = BITS(0:0), + SPDIF_UF_INT_EN = BITS(1:1), + IISTX_UF_INT_EN = BITS(2:2), + IISTX_OF_INT_EN = BITS(3:3), + VIN_VSYNC_INT_EN = BITS(4:4), + VIN_VACTIVE_END_INT_EN = BITS(5:5), + VSYNC_DIFF_OVER_LIMIT_INT_EN= BITS(6:6), + I2C_INT_AK = BITS(16:16), + I2C_INT = BITS(16:16), + SPDIF_UF_INT_AK = BITS(17:17), + SPDIF_UF_INT = BITS(17:17), + IISTX_UF_INT_AK = BITS(18:18), + IISTX_UF_INT = BITS(18:18), + IISRX_OF_INT_AK = BITS(19:19), + IISRX_OF_INT = BITS(19:19), + VIN_VSYNC_INT_AK = BITS(20:20), + VIN_VSYNC_INT = BITS(20:20), + VIN_VACTIVE_END_INT_AK = BITS(21:21), + VIN_VACTIVE_END_INT = BITS(21:21), + VSYNC_DIFF_OVER_LIMIT_INT_AK= BITS(22:22), + VSYNC_DIFF_OVER_LIMIT_INT = BITS(22:22), + + VIP_GPIO_INOUT = 0x0030, + CLKOUT0_GPIO0_OUT = BITS(0:0), + CLKOUT0_GPIO1_OUT = BITS(1:1), + CLKOUT0_GPIO2_OUT = BITS(2:2), + GPIO_6TO3_OUT = BITS(6:3), + SPDIF_GPIO_OUT = BITS(7:7), + ADO_GPIO_OUT = BITS(8:8), + ADIO_GPIO_OUT = BITS(9:9), + WS_GPIO_OUT = BITS(10:10), + BITCLK_GPIO_OUT = BITS(11:11), + HAD_GPIO_OUT = BITS(13:12), + CLKOUT0_GPIO0_IN = BITS(16:16), + CLKOUT0_GPIO1_IN = BITS(17:17), + CLKOUT0_GPIO2_IN = BITS(18:18), + GPIO_6TO3_IN = BITS(22:19), + SPDIF_GPIO_IN = BITS(23:23), + ADO_GPIO_IN = BITS(24:24), + ADIO_GPIO_IN = BITS(25:25), + WS_GPIO_IN = BITS(26:26), + BITCLK_GPIO_IN = BITS(27:27), + HAD_GPIO_IN = BITS(29:28), + + VIP_GPIO_CNTL = 0x0034, + CLKOUT0_GPIO0_OE = BITS(0:0), + CLKOUT1_GPIO1_OE = BITS(1:1), + CLKOUT2_GPIO2_OE = BITS(2:2), + GPIO_6TO3_OE = BITS(6:3), + SPDIF_GPIO_OE = BITS(7:7), + ADO_GPIO_OE = BITS(8:8), + ADIO_GPIO_OE = BITS(9:9), + WS_GPIO_OE = BITS(10:10), + BITCLK_GPIO_OE = BITS(11:11), + HAD_GPIO_OE = BITS(13:12), + GPIO_6TO1_STRAPS = BITS(22:17), + + VIP_RIPINTF_PORT_CNTL = 0x003c, + MPP_DATA_DRV = BITS(2:2), + HAD_DRV = BITS(3:3), + HCTL_DRV = BITS(4:4), + SRDY_IRQb_DRV = BITS(5:5), + SUB_SYS_ID_EN = BITS(16:16), + + VIP_DECODER_DEBUG_CNTL = 0x05d4, + CHIP_DEBUG_SEL = BITS(7:0), + CHIP_DEBUG_EN = BITS(8:8), + DECODER_DEBUG_SEL = BITS(15:12), + + VIP_SINGLE_STEP_DATA = 0x05d8, + SS_C = BITS(7:0), + SS_Y = BITS(15:8), + + VIP_I2C_CNTL = 0x0054, + I2C_CLK_OE = BITS(0:0), + I2C_CLK_OUT = BITS(1:1), + I2C_CLK_IN = BITS(2:2), + I2C_DAT_OE = BITS(4:4), + I2C_DAT_OUT = BITS(5:5), + I2C_DAT_IN = BITS(6:6), + I2C_CLK_PUB = BITS(8:8), + I2C_CLK_PD = BITS(9:9), + I2C_CLK_DRV = BITS(10:10), + I2C_DAT_PUB = BITS(12:12), + I2C_DAT_PD = BITS(13:13), + I2C_DAT_DRV = BITS(14:14), + I2C_CLK_MX = BITS(19:16), + I2C_DAT_MX = BITS(23:20), + DELAY_TEST_MODE = BITS(25:24), + + // Undocumented Registers + VIP_TV_PLL_FINE_CNTL = 0x00b8, + VIP_CRT_PLL_FINE_CNTL = 0x00bc, + VIP_MV_MODE_CNTL = 0x0208, + VIP_MV_STRIPE_CNTL = 0x020c, + VIP_MV_LEVEL_CNTL1 = 0x0210, + VIP_MV_LEVEL_CNTL2 = 0x0214, + VIP_MV_STATUS = 0x0330, + VIP_TV_DTO_INCREMENTS = 0x0390, + VIP_CRT_DTO_INCREMENTS = 0x0394, + VIP_VSYNC_DIFF_CNTL = 0x03a0, + VIP_VSYNC_DIFF_LIMITS = 0x03a4, + VIP_VSYNC_DIFF_RD_DATA = 0x03a8 +}; + +#endif diff --git a/src/add-ons/media/media-add-ons/radeon/Tuner.cpp b/src/add-ons/media/media-add-ons/radeon/Tuner.cpp new file mode 100644 index 0000000000..bfcf23ed25 --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/Tuner.cpp @@ -0,0 +1,323 @@ +/****************************************************************************** +/ +/ File: Tuner.cpp +/ +/ Description: Philips Desktop TV Tuners interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#include +#include "Tuner.h" + +enum tuner_control_bits { + CHANGE_PUMP = 0x40, /* change pump settings */ + CHANGE_PUMP_FAST = 0x00, /* for fast tuning */ + CHANGE_PUMP_SLOW = 0x40, /* for moderate speed tuning */ + + TEST_MODE = 0x38, /* test mode settings */ + TEST_MODE_NORMAL = 0x08, /* for normal operation */ + + RATIO_SELECT = 0x06, /* ratio select */ + RATIO_SELECT_FAST = 0x00, /* fast picture search */ + RATIO_SELECT_SLOW = 0x02, /* slow picture search */ + RATIO_SELECT_NORMAL = 0x06, /* normal picture search */ + + OS_MODE = 0x01, /* PLL disabling */ + OS_MODE_NORMAL = 0x00, /* for normal operation */ + OS_MODE_SWITCH = 0x01 /* change pump to high impedance */ +}; + +enum tuner_status_bits { + STB_POR = 0x80, /* power on reset */ + STB_FL = 0x40, /* in-lock flag */ + STB_INF = 0x38, /* digital information */ + STB_ADC = 0x07 /* A/D converter */ +}; + +/***************************************************************************** +/ +/ Tuner Parameters and Frequencies for TV Antenna and Cable Channels +/ +******************************************************************************/ + +static const int kBandsPerTuner = 5; + +static const struct { + tuner_type brand; + const char* name; + struct { + int pll; + float minFrequency, maxFrequency; + } bands[kBandsPerTuner]; +} kTunerTable[] = { + /* Philips FI1236 NTSC M/N */ + { C_TUNER_FI1236, "FI1236 NTSC", + { { 0xa2, 54.00, 157.25 }, + { 0x94, 162.00, 451.25 }, + { 0x34, 451.25, 463.25 }, + { 0x31, 463.25, 801.25 }, + { 0x00, 0.00, 0.00 } } }, + + /* Philips FI1236J NTSC Japan */ + { C_TUNER_FI1236J, "FI1236J NTSC Japan", + { { 0xa2, 54.00, 157.25 }, + { 0x94, 162.00, 451.25 }, + { 0x34, 451.25, 463.25 }, + { 0x31, 463.25, 801.25 }, + { 0x00, 0.00, 0.00 } } }, + + /* Philips FI1236MK2 NTSC M/N */ + { C_TUNER_FI1236MK2, "FI1236MK2 NTSC", + { { 0xa0, 55.25, 160.00 }, + { 0x90, 160.00, 454.00 }, + { 0x30, 454.00, 855.25 }, + { 0x00, 0.00, 0.00 }, + { 0x00, 0.00, 0.00 } } }, + + /* Philips FI1216 PAL B/G */ + { C_TUNER_FI1216, "FI1216 PAL", + { { 0xa2, 45.00, 140.25 }, + { 0xa4, 140.25, 168.25 }, + { 0x94, 168.25, 447.25 }, + { 0x34, 447.25, 463.25 }, + { 0x31, 463.25, 855.25 } } }, + + /* Philips FI1216MK2 PAL B/G */ + { C_TUNER_FI1216MK2, "FI1216MK2 PAL", + { { 0xa0, 48.25, 170.00 }, + { 0x90, 170.00, 450.00 }, + { 0x30, 450.00, 855.25 }, + { 0x00, 0.00, 0.00 }, + { 0x00, 0.00, 0.00 } } }, + + /* Philips FI1216MF PAL B/G, SECAM L/L' */ + { C_TUNER_FI1216MF, "FI1216MF PAL/SECAM", + { { 0xa1, 45.00, 168.25 }, + { 0x91, 168.25, 447.25 }, + { 0x31, 447.25, 855.25 }, + { 0x00, 0.00, 0.00 }, + { 0x00, 0.00, 0.00 } } }, + + /* Philips FI1246 PAL I */ + { C_TUNER_FI1246, "FI1246 PAL I", + { { 0xa2, 45.00, 140.25 }, + { 0xa4, 140.25, 168.25 }, + { 0x94, 168.25, 447.25 }, + { 0x34, 447.25, 463.25 }, + { 0x31, 463.25, 855.25 } } }, + + /* Philips FI1256 SECAM D/K */ + { C_TUNER_FI1256, "FI1256 SECAM", + { { 0xa0, 48.25, 168.25 }, + { 0x90, 175.25, 455.25 }, + { 0x30, 463.25, 863.25 }, + { 0x00, 0.00, 0.00 }, + { 0x00, 0.00, 0.00 } } }, + + /* Temic FN5AL PAL I/B/G/DK, SECAM DK */ + { C_TUNER_TEMIC_FN5AL_PAL, "Temic FN5AL PAL", + { { 0xa2, 45.00, 140.25 }, + { 0xa4, 140.25, 168.25 }, + { 0x94, 168.25, 447.25 }, + { 0x31, 447.25, 855.25 }, + { 0x00, 0.00, 0.00 } } }, + + /* Temic FN5AL PAL I/B/G/DK, SECAM DK */ + { C_TUNER_TEMIC_FN5AL_SECAM, "Temic FN5AL SECAM", + { { 0xa0, 48.25, 168.25 }, + { 0x90, 175.25, 455.25 }, + { 0x30, 463.25, 863.25 }, + { 0x00, 0.00, 0.00 }, + { 0x00, 0.00, 0.00 } } } }; + +static const int kNumTuners = sizeof(kTunerTable) / sizeof(kTunerTable[0]); + + +CTuner::CTuner(CI2CPort & port) + : fPort(port), + fType(C_TUNER_NONE), + fAddress(0xC6), + fDivider(0) +{ + PRINT(("CTuner::CTuner()\n")); + + if( fPort.InitCheck() == B_OK ) { + radeon_video_tuner tuner; + radeon_video_decoder video; + radeon_video_clock clock; + int dummy; + + fPort.Radeon().GetMMParameters(tuner, video, clock, dummy, dummy, dummy); + switch (tuner) { + case C_RADEON_NO_TUNER: + fType = C_TUNER_NONE; + break; + case C_RADEON_FI1236_MK1_NTSC: + fType = C_TUNER_FI1236; + break; + case C_RADEON_FI1236_MK2_NTSC_JAPAN: + fType = C_TUNER_FI1236J; + break; + case C_RADEON_FI1216_MK2_PAL_BG: + fType = C_TUNER_FI1216; + break; + case C_RADEON_FI1246_MK2_PAL_I: + fType = C_TUNER_FI1246; + break; + case C_RADEON_FI1216_MF_MK2_PAL_BG_SECAM_L: + fType = C_TUNER_FI1216MF; + break; + case C_RADEON_FI1236_MK2_NTSC: + fType = C_TUNER_FI1236MK2; + break; + case C_RADEON_FI1256_MK2_SECAM_DK: + fType = C_TUNER_FI1256; + break; + case C_RADEON_FI1216_MK2_PAL_BG_SECAM_L: + fType = C_TUNER_FI1216MK2; + break; + case C_RADEON_TEMIC_FN5AL_PAL_IBGDK_SECAM_DK: + fType = C_TUNER_TEMIC_FN5AL_PAL; + break; + default: + fType = C_TUNER_FI1236; + break; + } + + for (fAddress = 0xc0; fAddress <= 0xc6; fAddress += 0x02) { + if (fPort.Probe(fAddress)) { + PRINT(("CTuner::CTuner() - TV Tuner found at I2C port 0x%02x\n", fAddress)); + break; + } + } + } + if( InitCheck() != B_OK ) + PRINT(("CTuner::CTuner() - TV Tuner not found!\n")); +} + +CTuner::~CTuner() +{ + PRINT(("CTuner::~CTuner()\n")); +} + +status_t CTuner::InitCheck() const +{ + return (fType != C_TUNER_NONE && fAddress >= 0xc0 && fAddress <= 0xc6) ? + B_OK : B_ERROR; +} + +bool CTuner::SetFrequency(float frequency, float picture) +{ + //PRINT(("CTuner::SetFrequency(%g, %g)\n", frequency, picture)); + + for (int index = 0; index < kNumTuners; index++) { + if (kTunerTable[index].brand == fType) { + for (int band = 0; band < kBandsPerTuner; band++) { + if (frequency >= kTunerTable[index].bands[band].minFrequency && + frequency <= kTunerTable[index].bands[band].maxFrequency) { + SetParameters( + (int) (16.0 * (frequency + picture)), + CHANGE_PUMP_FAST | RATIO_SELECT_NORMAL | + TEST_MODE_NORMAL | OS_MODE_NORMAL, + kTunerTable[index].bands[band].pll | + ((Status() & STB_INF) >> 3)); + return true; + } + } + } + } + return false; +} + +bool CTuner::SweepFrequency(float frequency, float picture) +{ + PRINT(("CTuner::SweepFrequency(%g, %g)\n", frequency, picture)); + + float centerFrequency = frequency; + + /* sweeping down */ + do { + SetFrequency(frequency, picture); + for (int timeout = 0; timeout < 10; timeout++) { + snooze(20000); + if (IsLocked()) + break; + } + } while (ADC() == 0 && + (frequency -= 0.5000) > centerFrequency - 3.000); + + /* sweeping up */ + do { + SetFrequency(frequency, picture); + for (int timeout = 0; timeout < 10; timeout++) { + snooze(20000); + if (IsLocked()) + break; + } + } while (ADC() != 0 && + (frequency += 0.0625) < centerFrequency + 0.500); + + return (ADC() == 0); +} + +const char * CTuner::Name() const +{ + for (int index = 0; index < kNumTuners; index++) { + if (kTunerTable[index].brand == fType) + return kTunerTable[index].name; + } + return 0; +} + +tuner_type CTuner::Type() const +{ + return fType; +} + +bool CTuner::HasSignal(void) +{ + return (IsLocked() && ADC() <= 2); +} + +int CTuner::Status(void) +{ + char buffer[1]; + fPort.Read(fAddress, buffer, sizeof(buffer)); + return buffer[0] & 0xff; +} + +int CTuner::ADC(void) +{ + return Status() & STB_ADC; +} + +bool CTuner::IsLocked(void) +{ + return (Status() & STB_FL) != 0; +} + +void CTuner::SetParameters(int divider, int control, int band) +{ + char buffer[4]; + + if (divider > fDivider) { + buffer[0] = (divider >> 8) & 0x7f; + buffer[1] = (divider >> 0) & 0xff; + + buffer[2] = (control | 0x80); + buffer[3] = (band & 0xff); + } + else { + buffer[0] = (control | 0x80); + buffer[1] = (band & 0xff); + + buffer[2] = (divider >> 8) & 0x7f; + buffer[3] = (divider >> 0) & 0xff; + } + + fDivider = divider; + + fPort.Write(fAddress, buffer, sizeof(buffer)); +} diff --git a/src/add-ons/media/media-add-ons/radeon/Tuner.h b/src/add-ons/media/media-add-ons/radeon/Tuner.h new file mode 100644 index 0000000000..cb948255e5 --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/Tuner.h @@ -0,0 +1,70 @@ +/****************************************************************************** +/ +/ File: Tuner.h +/ +/ Description: Philips Desktop TV Tuners interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#ifndef __TUNER_H__ +#define __TUNER_H__ + +#include "I2CPort.h" + +enum tuner_type { + C_TUNER_NONE = 0x1400, /* Unknown */ + C_TUNER_FI1236 = 0x1401, /* NTSC M/N */ + C_TUNER_FI1236J = 0x1402, /* NTSC Japan */ + C_TUNER_FI1236MK2 = 0x1403, /* NTSC M/N */ + C_TUNER_FI1216 = 0x1404, /* PAL B/G */ + C_TUNER_FI1216MK2 = 0x1405, /* PAL B/G */ + C_TUNER_FI1216MF = 0x1406, /* PAL B/G, SECAM L/L' */ + C_TUNER_FI1246 = 0x1407, /* PAL I */ + C_TUNER_FI1256 = 0x1408, /* SECAM D/K */ + C_TUNER_TEMIC_FN5AL_PAL = 0x1409, /* PAL I/B/G/DK */ + C_TUNER_TEMIC_FN5AL_SECAM = 0x140a /* SECAM DK */ +}; + +enum tuner_picture_carrier { + C_TUNER_NTSC_PICTURE_CARRIER = 4575, + C_TUNER_PAL_PICTURE_CARRIER = 3890, + C_TUNER_SECAM_PICTURE_CARRIER = 3890 +}; + +class CTuner { +public: + CTuner(CI2CPort & port); + + ~CTuner(); + + status_t InitCheck() const; + + const char * Name() const; + + tuner_type Type() const; + + bool SetFrequency(float frequency, float picture); + + bool SweepFrequency(float frequency, float picture); + + bool HasSignal(void); + + int Status(); + + bool IsLocked(); + + int ADC(); + +private: + void SetParameters(int divider, int control, int band); + +private: + CI2CPort & fPort; + tuner_type fType; + int fAddress; + int fDivider; +}; + +#endif diff --git a/src/add-ons/media/media-add-ons/radeon/VIPPort.cpp b/src/add-ons/media/media-add-ons/radeon/VIPPort.cpp new file mode 100644 index 0000000000..8ca89a76f5 --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/VIPPort.cpp @@ -0,0 +1,33 @@ +/****************************************************************************** +/ +/ File: VIP.cpp +/ +/ Description: ATI Radeon Video Input Port (VIP) interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#include +#include "VIPPort.h" + +CVIPPort::CVIPPort(CRadeon & radeon) + : fRadeon(radeon) +{ + PRINT(("CVIPPort::CVIPPort()\n")); +} + +CVIPPort::~CVIPPort() +{ + PRINT(("CVIPPort::~CVIPPort()\n")); +} + +status_t CVIPPort::InitCheck() const +{ + return fRadeon.InitCheck(); +} + +CRadeon & CVIPPort::Radeon() +{ + return fRadeon; +} diff --git a/src/add-ons/media/media-add-ons/radeon/VIPPort.h b/src/add-ons/media/media-add-ons/radeon/VIPPort.h new file mode 100644 index 0000000000..7f9ee8b3ea --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/VIPPort.h @@ -0,0 +1,69 @@ +/****************************************************************************** +/ +/ File: VIP.h +/ +/ Description: ATI Radeon Video Input Port (VIP) interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#ifndef __VIP_PORT_H__ +#define __VIP_PORT_H__ + +#include "Radeon.h" + +enum vip_port_device { + C_VIP_PORT_DEVICE_0 = 0, + C_VIP_PORT_DEVICE_1 = 1, + C_VIP_PORT_DEVICE_2 = 2, + C_VIP_PORT_DEVICE_3 = 3 +}; + +enum vip_port_register { + C_VIP_VENDOR_DEVICE_ID = 0x0000, + C_VIP_VENDOR_ID = BITS(15:0), + C_VIP_DEVICE_ID = BITS(31:16), + + C_VIP_SUB_VENDOR_DEVICE_ID = 0x0004, + C_VIP_SUB_VENDOR_ID = BITS(15:0), + C_VIP_SUB_DEVICE_ID = BITS(31:16), + + C_VIP_COMMAND_STATUS = 0x0008, + C_VIP_POWER_ST = BITS(1:0), + C_VIP_XHOST_ON = BITS(2:2), + C_VIP_P1_SUPPORT = BITS(16:16), + C_VIP_P2_SUPPORT = BITS(17:17), + C_VIP_HOST_CAP = BITS(19:18), + + C_VIP_REVISION_ID = 0x000c, + C_VIP_REVISION_ID_MASK = BITS(15:0) +}; + +class CVIPPort { +public: + CVIPPort(CRadeon & radeon); + + ~CVIPPort(); + + status_t InitCheck() const; + + CRadeon & Radeon(); + + int Register(int device, int address) { + return fRadeon.VIPRegister( device, address ); + } + + void SetRegister(int device, int address, int value) { + fRadeon.SetVIPRegister( device, address, value ); + } + + int FindVIPDevice( uint32 device_id ) { + return fRadeon.FindVIPDevice( device_id ); + } + +private: + CRadeon & fRadeon; +}; + +#endif diff --git a/src/add-ons/media/media-add-ons/radeon/VideoIn.cpp b/src/add-ons/media/media-add-ons/radeon/VideoIn.cpp new file mode 100644 index 0000000000..46fcefbb05 --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/VideoIn.cpp @@ -0,0 +1,453 @@ +/****************************************************************************** +/ +/ File: VideoIn.cpp +/ +/ Description: High-Level ATI Radeon Video Capture Interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#include +#include "VideoIn.h" +#include + +static const theater_standard kStandard[] = { + C_THEATER_NTSC, + C_THEATER_NTSC_JAPAN, + C_THEATER_NTSC_443, + C_THEATER_PAL_M, + C_THEATER_PAL_BDGHI, + C_THEATER_PAL_N, + C_THEATER_PAL_60, + C_THEATER_PAL_NC, + C_THEATER_SECAM, +// C_THEATER_NTSC_RAW +}; + +static const theater_source kSource[] = { + C_THEATER_TUNER, + C_THEATER_COMPOSITE, + C_THEATER_SVIDEO +}; + +static const capture_buffer_mode kMode[] = { + C_RADEON_CAPTURE_FIELD_DOUBLE, + C_RADEON_CAPTURE_BOB_DOUBLE, + C_RADEON_CAPTURE_WEAVE_DOUBLE +}; + +static const struct { + struct { + int width, height; + } total; + struct { + int left, top; + int width, height; + } active; + struct { + int left, top; + int width, height; + } vbi; +} kTiming[] = { + { { 910, 525 }, { 112, 37, 755, 480 }, { 73, 13, 798, 20 } }, // NTSC-M + { { 910, 525 }, { 112, 37, 755, 480 }, { 73, 13, 798, 24 } }, // NTSC-Japan + { { 1042, 525 }, { 126, 37, 940, 480 }, { 73, 13, 798, 24 } }, // NTSC-443 + { { 910, 525 }, { 103, 37, 764, 480 }, { 73, 13, 798, 24 } }, // PAL-M + + { { 1135, 625 }, { 154, 35, 928, 576 }, { 132, 11, 924, 24 } }, // PAL-BDGHI + { { 1135, 625 }, { 154, 35, 928, 576 }, { 132, 11, 924, 24 } }, // PAL-N + { { 1125, 625 }, { 132, 37, 736, 480 }, { 100, 13, 770, 26 } }, // PAL-60 + { { 910, 625 }, { 125, 35, 957, 576 }, { 132, 11, 924, 24 } }, // PAL-NC + { { 1135, 625 }, { 149, 35, 933, 576 }, { 132, 11, 924, 24 } }, // SECAM + + { { 910, 525 }, { 112, 37, 755, 480 }, { 73, 13, 798, 22 } } // NTSC-Raw +}; + + + +CVideoIn::CVideoIn( const char *dev_name ) + : fRadeon( dev_name ), + fCapture(fRadeon), + fI2CPort(fRadeon), + fTheater(fRadeon), + fTuner(fI2CPort), + fSound(fI2CPort), + fBuffer0(0), + fBuffer1(0), + fBuffer0Handle(0), + fBuffer1Handle(0), + convert_buffer( NULL ), + fBufferLength(0), + fBufferBytesPerRow(0), + fBufferSequence(0), + fBufferPeriod(0), + started( false ) +{ + Trace("CVideoIn::CVideoIn()"); +} + +CVideoIn::~CVideoIn() +{ + Trace("CVideoIn::~CVideoIn()"); + + FreeBuffers(); +} + +status_t CVideoIn::InitCheck() const +{ + status_t res; + + Trace("CVideoIn::InitCheck()"); + + if( (res = fRadeon.InitCheck()) != B_OK ) + return res; + + if( (res = fCapture.InitCheck()) != B_OK ) + return res; + + if( (res = fI2CPort.InitCheck()) != B_OK ) + return res; + + return fTheater.InitCheck(); +} + +int CVideoIn::Capabilities() const +{ + return (fTuner.InitCheck() == B_OK ? C_VIDEO_IN_HAS_TUNER + C_VIDEO_IN_HAS_COMPOSITE + C_VIDEO_IN_HAS_SVIDEO : 0) + + (fSound.InitCheck() == B_OK ? C_VIDEO_IN_HAS_SOUND : 0); +} + +void CVideoIn::Start(video_in_source source, video_in_standard standard, + video_in_capture_mode mode, int width, int height) +{ + char buffer[256]; + sprintf(buffer, "CVideoIn::Start(%s, %d, %d)", + mode == C_VIDEO_IN_FIELD ? "C_VIDEO_IN_FIELD" : + mode == C_VIDEO_IN_BOB ? "C_VIDEO_IN_BOB" : "C_VIDEO_IN_WEAVE", width, height); + Trace(buffer); + + if( started ) + Stop(); + + switch (mode) { + case C_VIDEO_IN_FIELD: + case C_VIDEO_IN_BOB: + width = Clamp(width, 0, kTiming[standard].active.width); + height = Clamp(height, 0, kTiming[standard].active.height / 2); + break; + case C_VIDEO_IN_WEAVE: + width = Clamp(width, 0, kTiming[standard].active.width); + height = Clamp(height, 0, kTiming[standard].active.height); + break; + } + + fBufferBytesPerRow = (2 * width + 15) & ~15; + fBufferLength = (fBufferBytesPerRow * height + 15) & ~15; + + FreeBuffers(); + + // TBD: + // no error handling !!!! + fRadeon.AllocateGraphicsMemory( + mt_local, + mode == C_VIDEO_IN_BOB ? 4 * fBufferLength : 2 * fBufferLength, + &fBuffer0, &fBuffer0Handle ); + + fRadeon.AllocateGraphicsMemory( + mt_local, + mode == C_VIDEO_IN_BOB ? 2 * fBufferLength : 1 * fBufferLength, + &fBuffer1, &fBuffer1Handle ); + + convert_buffer = malloc( mode == C_VIDEO_IN_BOB ? 4 * fBufferLength : 2 * fBufferLength ); + + fBufferPeriod = 1000000000LL / getFrameRate( standard ); + + if( mode == C_VIDEO_IN_BOB ) + fBufferPeriod >>= 1; + + fTheater.SetStandard(kStandard[standard], kSource[source]); + fTheater.SetSize(width, (mode != C_VIDEO_IN_WEAVE ? 2 * height : height)); + + fCapture.SetBuffer(C_RADEON_CAPTURE_CCIR656, kMode[mode], fBuffer0, fBuffer1, fBufferLength, fBufferBytesPerRow >> 1); + fCapture.SetClip(0, kTiming[standard].vbi.height, width - 1, kTiming[standard].vbi.height + (mode != C_VIDEO_IN_WEAVE ? height : height >> 1) - 1); + + fTheater.SetEnable(true, false); + if( fSound.InitCheck() == B_OK ) + fSound.SetEnable(true); + fCapture.SetInterrupts(true); + fCapture.Start(); +} + +void CVideoIn::Stop() +{ + Trace("CVideoIn::Stop()"); + + if( !started ) + return; + + fCapture.Stop(); + fCapture.SetInterrupts(false); + if( fSound.InitCheck() == B_OK ) + fSound.SetEnable(false); + fTheater.SetEnable(false, false); + + FreeBuffers(); +} + +void CVideoIn::FreeBuffers() +{ + if( fBuffer0Handle > 0 ) { + fRadeon.FreeGraphicsMemory( mt_local, fBuffer0Handle ); + fBuffer0Handle = 0; + } + + if( fBuffer1Handle > 0 ) { + fRadeon.FreeGraphicsMemory( mt_local, fBuffer1Handle ); + fBuffer1Handle = 0; + } + + if( convert_buffer != NULL ) { + free( convert_buffer ); + convert_buffer = NULL; + } +} + +void CVideoIn::SetBrightness(int brightness) +{ + Trace("CVideoIn::SetBrightness()"); + + fTheater.SetBrightness(brightness); +} + +void CVideoIn::SetContrast(int contrast) +{ + Trace("CVideoIn::SetContrast()"); + + fTheater.SetContrast(contrast); +} + +void CVideoIn::SetSaturation(int saturation) +{ + Trace("CVideoIn::SetSaturation()"); + + fTheater.SetSaturation(saturation); +} + +void CVideoIn::SetHue(int hue) +{ + Trace("CVideoIn::SetHue()"); + + fTheater.SetHue(hue); +} + +void CVideoIn::SetSharpness(int sharpness) +{ + Trace("CVideoIn::SetSharpness()"); + + fTheater.SetSharpness(sharpness); +} + +void CVideoIn::SetFrequency(float frequency, float picture) +{ + Trace("CVideoIn::SetFrequency()"); + + if (fTuner.Type() != C_TUNER_NONE) + fTuner.SweepFrequency(frequency, picture); +} + +float CVideoIn::FrequencyForChannel(int channel, video_in_standard standard) +{ + float frequency = 0; + + Trace("CVideoIn::FrequencyForChannel()"); + + if (fTuner.Type() != C_TUNER_NONE) { + switch (standard) { + case C_VIDEO_IN_NTSC: + case C_VIDEO_IN_NTSC_RAW: + // NTSC Cable + if (channel >= 2 && channel <= 6) { + frequency = 55.25 + 6.00 * (channel - 2); + } + else if (channel >= 7 && channel <= 13) { + frequency = 175.25 + 6.00 * (channel - 7); + } + else if (channel >= 14 && channel <= 22) { + frequency = 121.25 + 6.00 * (channel - 14); + } + else if (channel >= 23 && channel <= 36) { + frequency = 217.25 + 6.00 * (channel - 23); + } + else if (channel >= 37 && channel <= 62) { + frequency = 301.25 + 6.00 * (channel - 37); + } + else if (channel >= 63 && channel <= 94) { + frequency = 457.25 + 6.00 * (channel - 63); + } + else if (channel >= 95 && channel <= 99) { + frequency = 91.25 + 6.00 * (channel - 95); + } + else if (channel >= 100 && channel <= 125) { + frequency = 649.25 + 6.00 * (channel - 100); + } + else { + frequency = 0; + } + break; + case C_VIDEO_IN_NTSC_JAPAN: + case C_VIDEO_IN_NTSC_443: + case C_VIDEO_IN_PAL_M: + case C_VIDEO_IN_PAL_BDGHI: + case C_VIDEO_IN_PAL_N: + case C_VIDEO_IN_PAL_60: + case C_VIDEO_IN_PAL_NC: + case C_VIDEO_IN_SECAM: + break; + } + } + return frequency; +} + +bool CVideoIn::SetChannel(int channel, video_in_standard standard) +{ + Trace("CVideoIn::SetChannel()"); + + if (fTuner.Type() == C_TUNER_NONE) + return true; + + const float frequency = FrequencyForChannel(channel, standard); + + switch (standard) { + case C_VIDEO_IN_NTSC: + case C_VIDEO_IN_NTSC_RAW: + return fTuner.SweepFrequency(frequency, C_TUNER_NTSC_PICTURE_CARRIER / 100.0f); + break; + case C_VIDEO_IN_NTSC_JAPAN: + case C_VIDEO_IN_NTSC_443: + case C_VIDEO_IN_PAL_M: + case C_VIDEO_IN_PAL_BDGHI: + case C_VIDEO_IN_PAL_N: + case C_VIDEO_IN_PAL_60: + case C_VIDEO_IN_PAL_NC: + case C_VIDEO_IN_SECAM: + return fTuner.SweepFrequency(frequency, C_TUNER_PAL_PICTURE_CARRIER / 100.0f); + } + return false; +} + +int CVideoIn::Capture(color_space colorSpace, void * bits, int bitsLength, + int bytesPerRow, int * sequence, short * number, bigtime_t * when) +{ +// Trace("CVideoIn::Capture()"); + + int mask, counter; + + if ((mask = fCapture.WaitInterrupts(sequence, when, fBufferPeriod)) == 0) + return 0; + + *number = ((mask & (C_RADEON_CAPTURE_BUF0_INT | C_RADEON_CAPTURE_BUF1_INT)) != 0 ? 0 : 1); + + int32 captured_buffer = + ((mask & C_RADEON_CAPTURE_BUF0_INT) != 0 ? fBuffer0 : + (mask & C_RADEON_CAPTURE_BUF1_INT) != 0 ? fBuffer1 : + (mask & C_RADEON_CAPTURE_BUF0_EVEN_INT) != 0 ? fBuffer0 + fBufferLength : + (mask & C_RADEON_CAPTURE_BUF1_EVEN_INT) != 0 ? fBuffer1 + fBufferLength : 0); + + /*PRINT(("colorSpace:%x, bitsLength: %d, fBufferLength: %d, bytesPerRow: %d, fBufferBytesPerRow: %d\n", + colorSpace, bitsLength, fBufferLength, bytesPerRow, fBufferBytesPerRow ));*/ + + // always copy into main memory first, even if it must be converted by CPU - + // reading from graphics mem is incredibly slow + if (colorSpace == B_YCbCr422 && bitsLength <= fBufferLength && bytesPerRow == fBufferBytesPerRow) { + PRINT(("%d, %p\n", captured_buffer, bits)); + + fRadeon.DMACopy( captured_buffer, bits, bitsLength, true, false ); + } + else if (colorSpace == B_RGB32 && bitsLength <= 2 * fBufferLength && bytesPerRow == 2 * fBufferBytesPerRow) { + + fRadeon.DMACopy( captured_buffer, convert_buffer, fBufferLength, true, false ); + +#define RGB32 +#include "yuv_converter.h" +#undef RGB32 + + } + else if (colorSpace == B_RGB16 && bitsLength <= fBufferLength && bytesPerRow == fBufferBytesPerRow) { + fRadeon.DMACopy( captured_buffer, convert_buffer, fBufferLength, true, false ); + +#define RGB16 +#include "yuv_converter.h" +#undef RGB16 + + } + else if (colorSpace == B_RGB15 && bitsLength <= fBufferLength && bytesPerRow == fBufferBytesPerRow) { + fRadeon.DMACopy( captured_buffer, convert_buffer, fBufferLength, true, false ); + +#define RGB15 +#include "yuv_converter.h" +#undef RGB15 + + } + else if (colorSpace == B_GRAY8 && 2 * bitsLength <= fBufferLength && 2 * bytesPerRow == fBufferBytesPerRow) { + fRadeon.DMACopy( captured_buffer, convert_buffer, fBufferLength, true, false ); + + static const unsigned short mask[] = { + 0x00ff, 0x00ff, 0x00ff, 0x00ff }; + + asm volatile( + "1:\n" + "movq 0x00(%0),%%mm0\n" // mm0 = Cr2' Y3 Cb2' Y2 Cr0' Y1 Cb0' Y0 + "movq 0x08(%0),%%mm1\n" // mm1 = Cr6' Y7 Cb6' Y6 Cr4' Y5 Cb4' Y4 + "pand %3,%%mm0\n" // mm0 = 00 Y3 00 Y2 00 Y1 00 Y0 + "pand %3,%%mm1\n" // mm1 = 00 Y7 00 Y6 00 Y5 00 Y4 + "packuswb %%mm1,%%mm0\n" // mm0 = Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 + "movq %%mm0,(%1)\n" // destination[0] = mm0 + "addl $0x10,%0\n" // source += 16 + "addl $0x08,%1\n" // destination += 8 + "subl $0x08,%2\n" + "jg 1b\n" + "emms\n" + : + : "r" (convert_buffer), "r" (bits), "r" (bitsLength), "m" (mask)); + } + else if( colorSpace == B_NO_COLOR_SPACE ) { + // special case: only wait for image but don't copy it + ; + } + else { + PRINT(("CVideoIn::Capture() - Bad buffer format\n")); + } + + counter = *sequence - fBufferSequence; + fBufferSequence = *sequence; + return counter; +} + +void CVideoIn::Trace(const char * message) const +{ + PRINT(("\x1b[0;30;34m%s\x1b[0;30;47m\n", message)); +} + +int32 CVideoIn::getFrameRate( video_in_standard standard ) +{ + // TODO: I'm not really sure about these values + static const int32 frame_rate[] = { + 29976, 29976, 29976, 25000, 25000, 25000, 29976, 25000, 25000, 29976 + }; + + return frame_rate[standard]; +} + +void CVideoIn::getActiveRange( video_in_standard standard, CRadeonRect &rect ) +{ + // in theory, we would ask fTheatre; + // in practice, values retrieved from there don't work; + // e.g. for PAL, according to Theatre, VBI height is 38, but you must set up the + // clipping unit to height 24! I have no clue what goes wrong there + rect.SetTo( + kTiming[standard].active.left, + kTiming[standard].active.top, + kTiming[standard].active.left + kTiming[standard].active.width - 1, + kTiming[standard].active.top + kTiming[standard].active.height - 1 ); +} diff --git a/src/add-ons/media/media-add-ons/radeon/VideoIn.h b/src/add-ons/media/media-add-ons/radeon/VideoIn.h new file mode 100644 index 0000000000..72085f11da --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/VideoIn.h @@ -0,0 +1,144 @@ +/****************************************************************************** +/ +/ File: VideoIn.h +/ +/ Description: High-Level ATI Radeon Video Capture Interface. +/ +/ Copyright 2001, Carlos Hasan +/ +*******************************************************************************/ + +#ifndef __VIDEO_IN_H__ +#define __VIDEO_IN_H__ + +#include "Radeon.h" +#include "Capture.h" +//#include "Overlay.h" +#include "I2CPort.h" +#include "VIPPort.h" +#include "Tuner.h" +#include "MSP3430.h" +#include "Theater.h" + +enum video_in_source { + C_VIDEO_IN_TUNER, + C_VIDEO_IN_COMPOSITE, + C_VIDEO_IN_SVIDEO, +}; + +enum { C_VIDEO_IN_SOURCE_MAX = 2 }; + +enum video_in_standard { + C_VIDEO_IN_NTSC, + C_VIDEO_IN_NTSC_JAPAN, + C_VIDEO_IN_NTSC_443, + C_VIDEO_IN_PAL_M, + C_VIDEO_IN_PAL_BDGHI, + C_VIDEO_IN_PAL_N, + C_VIDEO_IN_PAL_60, + C_VIDEO_IN_PAL_NC, + C_VIDEO_IN_SECAM, + C_VIDEO_IN_NTSC_RAW +}; + +enum { C_VIDEO_IN_STANDARD_MAX = 8 }; + +enum video_in_capture_mode { + C_VIDEO_IN_FIELD, + C_VIDEO_IN_BOB, + C_VIDEO_IN_WEAVE +}; + +enum { C_VIDEO_IN_CAPTURE_MODE_MAX = 2 }; + +enum video_in_resolution { + C_VIDEO_IN_NTSC_SQ_WIDTH = 640, + C_VIDEO_IN_NTSC_SQ_HEIGHT = 480, + + C_VIDEO_IN_NTSC_CCIR_WIDTH = 720, + C_VIDEO_IN_NTSC_CCIR_HEIGHT = 480, + + C_VIDEO_IN_PAL_SQ_WIDTH = 768, + C_VIDEO_IN_PAL_SQ_HEIGHT = 576, + + C_VIDEO_IN_PAL_CCIR_WIDTH = 720, + C_VIDEO_IN_PAL_CCIR_HEIGHT = 576 +}; + +/*enum video_in_frame_rate { + C_VIDEO_IN_NTSC_FRAME_RATE = 29976, + C_VIDEO_IN_PAL_FRAME_RATE = 25000 +};*/ + +enum video_in_capabilities { + C_VIDEO_IN_HAS_SOUND = 1 << 0, + C_VIDEO_IN_HAS_TUNER = 1 << 1, + C_VIDEO_IN_HAS_COMPOSITE = 1 << 2, + C_VIDEO_IN_HAS_SVIDEO = 1 << 3 +}; + +class CVideoIn { +public: + CVideoIn( const char *dev_name ); + + ~CVideoIn(); + + status_t InitCheck() const; + + int Capabilities() const; + +public: + void Start(video_in_source source, video_in_standard standard, + video_in_capture_mode mode, int width, int height); + + void Stop(); + + int Capture(color_space colorSpace, void * bits, int bitsLength, + int bytesPerRow, int * sequence, short * number, bigtime_t * when); + +public: + void SetBrightness(int brightness); + + void SetContrast(int contrast); + + void SetSaturation(int saturation); + + void SetHue(int hue); + + void SetSharpness(int sharpness); + + void SetFrequency(float frequency, float picture); + + float FrequencyForChannel(int channel, video_in_standard standard); + + bool SetChannel(int channel, video_in_standard standard); + + int32 getFrameRate( video_in_standard standard ); + + void getActiveRange( video_in_standard standard, CRadeonRect &rect ); + +private: + void FreeBuffers(); + + void Trace(const char * message) const; + +private: + CRadeon fRadeon; + CCapture fCapture; + CI2CPort fI2CPort; + CTheater fTheater; + CTuner fTuner; + CMSP3430 fSound; + int32 fBuffer0; + int32 fBuffer1; + int32 fBuffer0Handle; + int32 fBuffer1Handle; + void *convert_buffer; + int fBufferLength; + int fBufferBytesPerRow; + int fBufferSequence; + bigtime_t fBufferPeriod; + bool started; +}; + +#endif diff --git a/src/add-ons/media/media-add-ons/radeon/yuv_converter.h b/src/add-ons/media/media-add-ons/radeon/yuv_converter.h new file mode 100644 index 0000000000..f35163ec29 --- /dev/null +++ b/src/add-ons/media/media-add-ons/radeon/yuv_converter.h @@ -0,0 +1,177 @@ +/* + Copyright (c) 2004, Thomas Kurschel + + + Part of Radeon In add-on + + YUV converter + + The Rage Theatre always provides YUV422 data and starting with Radeon, ATI + moved colour converter from 2D to 3D unit, so if you need another format you + must convert it manually, unless you get 3D working (which is, starting with r300, + hopeless anyway as there is no spec). + + Colour temperature is according to BT.601; for YCbCr format see also GraphicsDefs.h + + This header is included from VideoIn.cpp, with various defines to convert to + the wished format (RGB15, RGB16 or RGB32). + + Things to improve: + - colour components should be interpolated for odd pixels +*/ + + static const int8 c_offs[8] = + { 128, 128, 128, 128, 128, 128, 128, 128 }; + + static const int16 y_offs[4] = + { 16*128, 16*128, 16*128, 16*128 }; + + static const uint16 masks[2][4] = { + // high byte mask + { 0xff00, 0xff00, 0xff00, 0xff00 }, + // low byte mask + { 0x00ff, 0x00ff, 0x00ff, 0x00ff }, + }; + + static const int16 scale[5][4] = { + // Y pre-scale + { (int16)(1.1678 * 512), (int16)(1.1678 * 512), (int16)(1.1678 * 512), (int16)(1.1678 * 512) }, + // CbG CrG CbG CrG + { (int16)(-0.3929 * 256), (int16)(-0.8154 * 256), (int16)(-0.3929 * 256), (int16)(-0.8154 * 256) }, + // CbB CrR CbB CrR + { (int16)(2.0232 * 256), (int16)(1.6007 * 256), (int16)(2.0232 * 256), (int16)(1.6007 * 256) }, + }; + + static const int8 masks_8bit[2][8] = { + // r/b 16 bit mask and r/g/b 15 bit mask + { 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8 }, + // g 16 bit mask + { 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc }, + }; + + asm volatile( + "2:\n" + "pxor %%mm7,%%mm7\n" + + "1:\n" + "movq (%0),%%mm0\n" // mm0 = Cr2'Y3' Cb2'Y2' Cr0'Y1' Cb0'Y0' + "movq %%mm0,%%mm1\n" // mm1 = Cr2'Y3' Cb2'Y2' Cr0'Y1' Cb0'Y0' + + // Y is in 16..235 + // we need to substract 16 and scale to full range + // as standard MMX has a _signed_ integer multiply only, the highest bit must + // be zero before scaling, i.e. we use signed format 9.7 + "pand 8+%4,%%mm0\n" // mm2 = Y3' Y2' Y1' Y0' + "psllw $7,%%mm0\n" + "psubusw %3,%%mm0\n" + "pmulhw %5,%%mm0\n" // mm0 = Y3 Y2 Y1 Y0 + + // Cb and Cr is biased; compensate that + "psubb %2,%%mm1\n" // mm1 = Cr2 xxx Cb2 xxx Cr0 xxx Cb0 xxx + "pand %4,%%mm1\n" // mm1 = Cr2 Cb2 Cr0 Cb0 + + // transform Cb and Cr to green component + "movq %%mm1,%%mm2\n" + "pmaddwd 8+%5,%%mm1\n" // mm1 = CbCrG2 xxxxxxx CbCrG0 xxxxxxx + "psrad $16,%%mm1\n" // mm1 = CbCrG2 CbCrG0 + "packssdw %%mm1,%%mm1\n" // mm1 = CbCrG2 CbCrG0 CbCrG2 CbCrG0 + "punpcklwd %%mm1,%%mm1\n" // mm1 = CbCrG2 CbCrG2 CbCrG0 CbCrG0 + + // transform Cb to blue and Cr to red component + "pmulhw 16+%5,%%mm2\n" // mm2 = CrR2 CbB2 CrR0 CbB0 + + // nasty shuffling to separate and duplicate components + "movq %%mm2,%%mm3\n" + "punpcklwd %%mm3,%%mm3\n" // mm3 = CrR0 CrR0 CbB0 CbB0 + "punpckhwd %%mm2,%%mm2\n" // mm2 = CrR2 CrR2 CbB2 CbB2 + + "movq %%mm3,%%mm4\n" + "punpckldq %%mm2,%%mm3\n" // mm3 = CbB2 CbB2 CbB0 CbB0 + "punpckhdq %%mm2,%%mm4\n" // mm4 = CrR2 CrR2 CrR0 CrR0 + + // add Y to get final RGB + "paddsw %%mm0,%%mm1\n" // mm1 = G3 G2 G1 G0 + "paddsw %%mm0,%%mm3\n" // mm3 = B3 B2 B1 B0 + "paddsw %%mm0,%%mm4\n" // mm4 = R3 R2 R1 R0 + + // now, RBG can be converted to 8 bits each + "packuswb %%mm0,%%mm1\n" // mm1 = Y3 Y2 Y1 Y0 G3 G2 G1 G0 + "packuswb %%mm4,%%mm3\n" // mm3 = R3 R2 R1 R0 B3 B2 B1 B0 + +#ifdef RGB32 + // convertion to RGB32 + "movq %%mm3,%%mm2\n" + "punpckhbw %%mm1,%%mm3\n" // mm3 = Y3 R3 Y2 R2 Y1 R1 Y0 R0 + "punpcklbw %%mm1,%%mm2\n" // mm2 = G3 B3 G2 B2 G1 B1 G0 B0 + + "movq %%mm2,%%mm1\n" + "punpcklwd %%mm3,%%mm2\n" + "movq %%mm2,0x00(%1)\n" // dst = Y1 R1 G1 B1 Y0 R0 G0 B0 + + "punpckhwd %%mm3,%%mm1\n" + "movq %%mm1,0x08(%1)\n" // dst = Y3 R3 G3 B3 Y2 R2 G2 B2 + + "addl $0x08,%0\n" // source += 8 + "addl $0x10,%1\n" // destination += 16 + "subl $0x10,%7\n" // next pixels +#endif + +#ifdef RGB16 + // convertion to RGB16 + // things would be much easier if Intel had added a RGB32->RGB16 instruction + "pand %6,%%mm3\n" // mm3 - R3 R2 R1 R0 B3 B2 B1 B0 (masked) + "pand 8+%6,%%mm1\n" // mm1 - Y3 Y2 Y1 Y0 G3 G2 G1 G0 (masked) + + "punpcklbw %%mm7,%%mm1\n" // mm1 - G3 G2 G1 G0 + "movq %%mm7,%%mm2\n" + "punpckhbw %%mm3,%%mm2\n" // mm2 - R3 R2 R1 R0 + "punpcklbw %%mm7,%%mm3\n" // mm3 - B3 B2 B1 B0 + + "psllw $3,%%mm1\n" // mm1 - G3 G2 G1 G0 + "psrlw $3,%%mm3\n" // mm3 - B3 B2 B1 B0 + + "por %%mm2,%%mm1\n" + "por %%mm3,%%mm1\n" + "movq %%mm1,(%1)\n" + + "addl $0x08,%0\n" // source += 8 + "addl $0x08,%1\n" // destination += 8 + "subl $0x08,%7\n" // next pixels +#endif + +#ifdef RGB15 + // convertion to RGB15 + // same problem as before + "pand %6,%%mm3\n" // mm3 - R3 R2 R1 R0 B3 B2 B1 B0 (masked) + "pand %6,%%mm1\n" // mm1 - Y3 Y2 Y1 Y0 G3 G2 G1 G0 (masked) + + "punpcklbw %%mm7,%%mm1\n" // mm1 - G3 G2 G1 G0 + "movq %%mm7,%%mm2\n" + "punpckhbw %%mm3,%%mm2\n" // mm2 - R3 R2 R1 R0 + "punpcklbw %%mm7,%%mm3\n" // mm3 - B3 B2 B1 B0 + + "psllw $2,%%mm1\n" // mm1 - G3 G2 G1 G0 + "psrlw $1,%%mm2\n" // mm2 - R3 R2 R1 R0 + "psrlw $3,%%mm3\n" // mm3 - B3 B2 B1 B0 + + "por %%mm2,%%mm1\n" + "por %%mm3,%%mm1\n" + "movq %%mm1,(%1)\n" + + "addl $0x08,%0\n" // source += 8 + "addl $0x08,%1\n" // destination += 8 + "subl $0x08,%7\n" // next pixels +#endif + + // next + "jg 1b\n" + + "movl %9,%7\n" + "subl %7,%8\n" + + "jg 2b\n" + "emms\n" + : + : "r" (convert_buffer), "r" (bits), "m" (c_offs), "m" (y_offs), + "m" (masks), "m" (scale), "m" (masks_8bit), + "r" (bytesPerRow), "m" (bitsLength), "m" (bytesPerRow));