1/******************************************************************************
2/
3/	File:			Tuner.cpp
4/
5/	Description:	Philips Desktop TV Tuners interface.
6/
7/	Copyright 2001, Carlos Hasan
8/
9*******************************************************************************/
10
11#include <Debug.h>
12#include "Tuner.h"
13
14enum tuner_control_bits {
15    CHANGE_PUMP             = 0x40,     /* change pump settings */
16    CHANGE_PUMP_FAST        = 0x00,     /*  for fast tuning */
17    CHANGE_PUMP_SLOW        = 0x40,     /*  for moderate speed tuning */
18
19    TEST_MODE               = 0x38,     /* test mode settings */
20    TEST_MODE_NORMAL        = 0x08,     /*  for normal operation */
21
22    RATIO_SELECT            = 0x06,     /* ratio select */
23    RATIO_SELECT_FAST       = 0x00,     /*  fast picture search */
24    RATIO_SELECT_SLOW       = 0x02,     /*  slow picture search */
25    RATIO_SELECT_NORMAL     = 0x06,     /*  normal picture search */
26
27    OS_MODE                 = 0x01,     /* PLL disabling */
28    OS_MODE_NORMAL          = 0x00,     /*  for normal operation */
29    OS_MODE_SWITCH          = 0x01      /*  change pump to high impedance */
30};
31
32enum tuner_status_bits {
33    STB_POR                 = 0x80,     /* power on reset */
34    STB_FL                  = 0x40,     /* in-lock flag */
35    STB_INF                 = 0x38,     /* digital information */
36    STB_ADC                 = 0x07      /* A/D converter */
37};
38
39/*****************************************************************************
40/
41/   Tuner Parameters and Frequencies for TV Antenna and Cable Channels
42/
43******************************************************************************/
44
45static const int kBandsPerTuner   = 5;
46
47static const struct {
48    tuner_type brand;
49    const char* name;
50    struct {
51        int pll;
52        float minFrequency, maxFrequency;
53    } bands[kBandsPerTuner];
54} kTunerTable[] = {
55    /* Philips FI1236 NTSC M/N */
56    { C_TUNER_FI1236, "FI1236 NTSC",
57      { { 0xa2,  54.00, 157.25 },
58        { 0x94, 162.00, 451.25 },
59        { 0x34, 451.25, 463.25 },
60        { 0x31, 463.25, 801.25 },
61        { 0x00,   0.00,   0.00 } } },
62
63    /* Philips FI1236J NTSC Japan */
64    { C_TUNER_FI1236J, "FI1236J NTSC Japan",
65      { { 0xa2,  54.00, 157.25 },
66        { 0x94, 162.00, 451.25 },
67        { 0x34, 451.25, 463.25 },
68        { 0x31, 463.25, 801.25 },
69        { 0x00,   0.00,   0.00 } } },
70
71    /* Philips FI1236MK2 NTSC M/N */
72    { C_TUNER_FI1236MK2, "FI1236MK2 NTSC",
73      { { 0xa0,  55.25, 160.00 },
74        { 0x90, 160.00, 454.00 },
75        { 0x30, 454.00, 855.25 },
76        { 0x00,   0.00,   0.00 },
77        { 0x00,   0.00,   0.00 } } },
78
79    /* Philips FI1216 PAL B/G */
80    { C_TUNER_FI1216, "FI1216 PAL",
81      { { 0xa2,  45.00, 140.25 },
82        { 0xa4, 140.25, 168.25 },
83        { 0x94, 168.25, 447.25 },
84        { 0x34, 447.25, 463.25 },
85        { 0x31, 463.25, 855.25 } } },
86
87    /* Philips FI1216MK2 PAL B/G */
88    { C_TUNER_FI1216MK2, "FI1216MK2 PAL",
89      { { 0xa0,  48.25, 170.00 },
90        { 0x90, 170.00, 450.00 },
91        { 0x30, 450.00, 855.25 },
92        { 0x00,   0.00,   0.00 },
93        { 0x00,   0.00,   0.00 } } },
94
95    /* Philips FI1216MF PAL B/G, SECAM L/L' */
96    { C_TUNER_FI1216MF, "FI1216MF PAL/SECAM",
97      { { 0xa1,  45.00, 168.25 },
98        { 0x91, 168.25, 447.25 },
99        { 0x31, 447.25, 855.25 },
100        { 0x00,   0.00,   0.00 },
101        { 0x00,   0.00,   0.00 } } },
102
103    /* Philips FI1246 PAL I */
104    { C_TUNER_FI1246, "FI1246 PAL I",
105      { { 0xa2,  45.00, 140.25 },
106        { 0xa4, 140.25, 168.25 },
107        { 0x94, 168.25, 447.25 },
108        { 0x34, 447.25, 463.25 },
109        { 0x31, 463.25, 855.25 } } },
110
111    /* Philips FI1256 SECAM D/K */
112    { C_TUNER_FI1256, "FI1256 SECAM",
113      { { 0xa0,  48.25, 168.25 },
114        { 0x90, 175.25, 455.25 },
115        { 0x30, 463.25, 863.25 },
116        { 0x00,   0.00,   0.00 },
117        { 0x00,   0.00,   0.00 } } },
118
119    /* Temic FN5AL PAL I/B/G/DK, SECAM DK */
120    { C_TUNER_TEMIC_FN5AL_PAL, "Temic FN5AL PAL",
121      { { 0xa2,  45.00, 140.25 },
122        { 0xa4, 140.25, 168.25 },
123        { 0x94, 168.25, 447.25 },
124        { 0x31, 447.25, 855.25 },
125        { 0x00,   0.00,   0.00 } } },
126
127    /* Temic FN5AL PAL I/B/G/DK, SECAM DK */
128    { C_TUNER_TEMIC_FN5AL_SECAM, "Temic FN5AL SECAM",
129      { { 0xa0,  48.25, 168.25 },
130        { 0x90, 175.25, 455.25 },
131        { 0x30, 463.25, 863.25 },
132        { 0x00,   0.00,   0.00 },
133        { 0x00,   0.00,   0.00 } } } };
134
135static const int kNumTuners = sizeof(kTunerTable) / sizeof(kTunerTable[0]);
136
137
138CTuner::CTuner(CI2CPort & port)
139	:	fPort(port),
140		fType(C_TUNER_NONE),
141		fAddress(0xC6),
142		fDivider(0)
143{
144	PRINT(("CTuner::CTuner()\n"));
145
146	if( fPort.InitCheck() == B_OK ) {
147		radeon_video_tuner tuner;
148		radeon_video_decoder video;
149		radeon_video_clock clock;
150		int dummy;
151
152		fPort.Radeon().GetMMParameters(tuner, video, clock, dummy, dummy, dummy);
153		switch (tuner) {
154		case C_RADEON_NO_TUNER:
155			fType = C_TUNER_NONE;
156			break;
157		case C_RADEON_FI1236_MK1_NTSC:
158			fType = C_TUNER_FI1236;
159			break;
160		case C_RADEON_FI1236_MK2_NTSC_JAPAN:
161			fType = C_TUNER_FI1236J;
162			break;
163		case C_RADEON_FI1216_MK2_PAL_BG:
164			fType = C_TUNER_FI1216;
165			break;
166		case C_RADEON_FI1246_MK2_PAL_I:
167			fType = C_TUNER_FI1246;
168			break;
169		case C_RADEON_FI1216_MF_MK2_PAL_BG_SECAM_L:
170			fType = C_TUNER_FI1216MF;
171			break;
172		case C_RADEON_FI1236_MK2_NTSC:
173			fType = C_TUNER_FI1236MK2;
174			break;
175		case C_RADEON_FI1256_MK2_SECAM_DK:
176			fType = C_TUNER_FI1256;
177			break;
178		case C_RADEON_FI1216_MK2_PAL_BG_SECAM_L:
179			fType = C_TUNER_FI1216MK2;
180			break;
181		case C_RADEON_TEMIC_FN5AL_PAL_IBGDK_SECAM_DK:
182			fType = C_TUNER_TEMIC_FN5AL_PAL;
183			break;
184		default:
185			fType = C_TUNER_FI1236;
186			break;
187		}
188
189	    for (fAddress = 0xc0; fAddress <= 0xc6; fAddress += 0x02) {
190	        if (fPort.Probe(fAddress)) {
191	        	PRINT(("CTuner::CTuner() - TV Tuner found at I2C port 0x%02x\n", fAddress));
192				break;
193			}
194	    }
195	}
196	if( InitCheck() != B_OK )
197		PRINT(("CTuner::CTuner() - TV Tuner not found!\n"));
198}
199
200CTuner::~CTuner()
201{
202	PRINT(("CTuner::~CTuner()\n"));
203}
204
205status_t CTuner::InitCheck() const
206{
207    return (fType != C_TUNER_NONE && fAddress >= 0xc0 && fAddress <= 0xc6) ?
208    	B_OK : B_ERROR;
209}
210
211bool CTuner::SetFrequency(float frequency, float picture)
212{
213	//PRINT(("CTuner::SetFrequency(%g, %g)\n", frequency, picture));
214
215    for (int index = 0; index < kNumTuners; index++) {
216        if (kTunerTable[index].brand == fType) {
217            for (int band = 0; band < kBandsPerTuner; band++) {
218                if (frequency >= kTunerTable[index].bands[band].minFrequency &&
219                    frequency <= kTunerTable[index].bands[band].maxFrequency) {
220                    SetParameters(
221                        (int) (16.0 * (frequency + picture)),
222                        CHANGE_PUMP_FAST | RATIO_SELECT_NORMAL |
223                        TEST_MODE_NORMAL | OS_MODE_NORMAL,
224                        kTunerTable[index].bands[band].pll |
225                        ((Status() & STB_INF) >> 3));
226                    return true;
227                }
228            }
229        }
230    }
231    return false;
232}
233
234bool CTuner::SweepFrequency(float frequency, float picture)
235{
236	PRINT(("CTuner::SweepFrequency(%g, %g)\n", frequency, picture));
237
238    float centerFrequency = frequency;
239
240    /* sweeping down */
241    do {
242        SetFrequency(frequency, picture);
243        for (int timeout = 0; timeout < 10; timeout++) {
244            snooze(20000);
245            if (IsLocked())
246                break;
247	    }
248    } while (ADC() == 0  &&
249             (frequency -= 0.5000) > centerFrequency - 3.000);
250
251    /* sweeping up */
252    do {
253        SetFrequency(frequency, picture);
254        for (int timeout = 0; timeout < 10; timeout++) {
255            snooze(20000);
256            if (IsLocked())
257                break;
258        }
259    } while (ADC() != 0 &&
260             (frequency += 0.0625) < centerFrequency + 0.500);
261
262    return (ADC() == 0);
263}
264
265const char * CTuner::Name() const
266{
267    for (int index = 0; index < kNumTuners; index++) {
268        if (kTunerTable[index].brand == fType)
269            return kTunerTable[index].name;
270    }
271    return 0;
272}
273
274tuner_type CTuner::Type() const
275{
276	return fType;
277}
278
279bool CTuner::HasSignal(void)
280{
281    return (IsLocked() && ADC() <= 2);
282}
283
284int CTuner::Status(void)
285{
286    char buffer[1];
287    fPort.Read(fAddress, buffer, sizeof(buffer));
288    return buffer[0] & 0xff;
289}
290
291int CTuner::ADC(void)
292{
293    return Status() & STB_ADC;
294}
295
296bool CTuner::IsLocked(void)
297{
298    return (Status() & STB_FL) != 0;
299}
300
301void CTuner::SetParameters(int divider, int control, int band)
302{
303    char buffer[4];
304
305    if (divider > fDivider) {
306        buffer[0] = (divider >> 8) & 0x7f;
307        buffer[1] = (divider >> 0) & 0xff;
308
309        buffer[2] = (control | 0x80);
310        buffer[3] = (band & 0xff);
311    }
312    else {
313        buffer[0] = (control | 0x80);
314        buffer[1] = (band & 0xff);
315
316        buffer[2] = (divider >> 8) & 0x7f;
317        buffer[3] = (divider >> 0) & 0xff;
318    }
319
320    fDivider = divider;
321
322    fPort.Write(fAddress, buffer, sizeof(buffer));
323}
324