#include <Debug.h>
#include "Tuner.h"
enum tuner_control_bits {
CHANGE_PUMP = 0x40,
CHANGE_PUMP_FAST = 0x00,
CHANGE_PUMP_SLOW = 0x40,
TEST_MODE = 0x38,
TEST_MODE_NORMAL = 0x08,
RATIO_SELECT = 0x06,
RATIO_SELECT_FAST = 0x00,
RATIO_SELECT_SLOW = 0x02,
RATIO_SELECT_NORMAL = 0x06,
OS_MODE = 0x01,
OS_MODE_NORMAL = 0x00,
OS_MODE_SWITCH = 0x01
};
enum tuner_status_bits {
STB_POR = 0x80,
STB_FL = 0x40,
STB_INF = 0x38,
STB_ADC = 0x07
};
static const int kBandsPerTuner = 5;
static const struct {
tuner_type brand;
const char* name;
struct {
int pll;
float minFrequency, maxFrequency;
} bands[kBandsPerTuner];
} kTunerTable[] = {
{ 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 } } },
{ 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 } } },
{ 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 } } },
{ 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 } } },
{ 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 } } },
{ 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 } } },
{ 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 } } },
{ 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 } } },
{ 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 } } },
{ 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)
{
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;
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);
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));
}