blob: 0b5dc37fea514bd25dbbf750cf39512b4e21d49b [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07002#include <linux/kernel.h>
3#include <linux/i2c.h>
4#include <linux/types.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07005#include <linux/init.h>
6#include <linux/errno.h>
7#include <linux/slab.h>
8#include <linux/delay.h>
Michael Krufkyffbb8072007-08-21 01:14:12 -03009#include <linux/videodev.h>
Michael Krufky5e453dc2006-01-09 15:32:31 -020010#include <media/v4l2-common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include <media/tuner.h>
Michael Krufky8ca40832007-12-16 20:11:46 -030012#include "tuner-driver.h"
Michael Krufkyab166052007-12-09 02:26:48 -030013#include "tuner-i2c.h"
Michael Krufky31c95842007-10-21 20:48:48 -030014#include "tda9887.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070015
Mauro Carvalho Chehab674434c2005-12-12 00:37:28 -080016
Linus Torvalds1da177e2005-04-16 15:20:36 -070017/* Chips:
18 TDA9885 (PAL, NTSC)
19 TDA9886 (PAL, SECAM, NTSC)
20 TDA9887 (PAL, SECAM, NTSC, FM Radio)
21
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -030022 Used as part of several tuners
Linus Torvalds1da177e2005-04-16 15:20:36 -070023*/
24
Michael Krufky31c95842007-10-21 20:48:48 -030025static int tda9887_debug;
26module_param_named(debug, tda9887_debug, int, 0644);
27
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -030028#define tda9887_info(fmt, arg...) do {\
Michael Krufkydaae5892007-12-16 19:14:31 -030029 printk(KERN_INFO "tda9887 %d-%04x: " fmt, \
30 i2c_adapter_id(priv->i2c_props.adap), \
31 priv->i2c_props.addr, ##arg); } while (0)
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -030032#define tda9887_dbg(fmt, arg...) do {\
Michael Krufky31c95842007-10-21 20:48:48 -030033 if (tda9887_debug) \
Michael Krufkydaae5892007-12-16 19:14:31 -030034 printk(KERN_INFO "tda9887 %d-%04x: " fmt, \
35 i2c_adapter_id(priv->i2c_props.adap), \
36 priv->i2c_props.addr, ##arg); } while (0)
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
Michael Krufkyb2083192007-05-29 22:54:06 -030038struct tda9887_priv {
Michael Krufkydb8a6952007-08-21 01:24:42 -030039 struct tuner_i2c_props i2c_props;
40
Michael Krufkyb2083192007-05-29 22:54:06 -030041 unsigned char data[4];
Michael Krufky710401b2007-12-16 19:53:32 -030042 unsigned int config;
Michael Krufky91c9d4a2007-12-16 20:05:00 -030043 unsigned int mode;
44 unsigned int audmode;
45 v4l2_std_id std;
Michael Krufkyb2083192007-05-29 22:54:06 -030046};
Linus Torvalds1da177e2005-04-16 15:20:36 -070047
48/* ---------------------------------------------------------------------- */
49
50#define UNSET (-1U)
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52struct tvnorm {
53 v4l2_std_id std;
54 char *name;
55 unsigned char b;
56 unsigned char c;
57 unsigned char e;
58};
59
Linus Torvalds1da177e2005-04-16 15:20:36 -070060/* ---------------------------------------------------------------------- */
61
62//
63// TDA defines
64//
65
66//// first reg (b)
67#define cVideoTrapBypassOFF 0x00 // bit b0
68#define cVideoTrapBypassON 0x01 // bit b0
69
70#define cAutoMuteFmInactive 0x00 // bit b1
71#define cAutoMuteFmActive 0x02 // bit b1
72
73#define cIntercarrier 0x00 // bit b2
74#define cQSS 0x04 // bit b2
75
76#define cPositiveAmTV 0x00 // bit b3:4
77#define cFmRadio 0x08 // bit b3:4
78#define cNegativeFmTV 0x10 // bit b3:4
79
80
81#define cForcedMuteAudioON 0x20 // bit b5
82#define cForcedMuteAudioOFF 0x00 // bit b5
83
84#define cOutputPort1Active 0x00 // bit b6
85#define cOutputPort1Inactive 0x40 // bit b6
86
87#define cOutputPort2Active 0x00 // bit b7
88#define cOutputPort2Inactive 0x80 // bit b7
89
90
91//// second reg (c)
92#define cDeemphasisOFF 0x00 // bit c5
93#define cDeemphasisON 0x20 // bit c5
94
95#define cDeemphasis75 0x00 // bit c6
96#define cDeemphasis50 0x40 // bit c6
97
98#define cAudioGain0 0x00 // bit c7
99#define cAudioGain6 0x80 // bit c7
100
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200101#define cTopMask 0x1f // bit c0:4
Hans Verkuilf5b01422006-06-25 15:37:29 -0300102#define cTopDefault 0x10 // bit c0:4
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103
104//// third reg (e)
105#define cAudioIF_4_5 0x00 // bit e0:1
106#define cAudioIF_5_5 0x01 // bit e0:1
107#define cAudioIF_6_0 0x02 // bit e0:1
108#define cAudioIF_6_5 0x03 // bit e0:1
109
110
Trent Piepho5e082f12007-08-03 18:32:38 -0300111#define cVideoIFMask 0x1c // bit e2:4
112/* Video IF selection in TV Mode (bit B3=0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113#define cVideoIF_58_75 0x00 // bit e2:4
114#define cVideoIF_45_75 0x04 // bit e2:4
115#define cVideoIF_38_90 0x08 // bit e2:4
116#define cVideoIF_38_00 0x0C // bit e2:4
117#define cVideoIF_33_90 0x10 // bit e2:4
118#define cVideoIF_33_40 0x14 // bit e2:4
119#define cRadioIF_45_75 0x18 // bit e2:4
120#define cRadioIF_38_90 0x1C // bit e2:4
121
Trent Piepho5e082f12007-08-03 18:32:38 -0300122/* IF1 selection in Radio Mode (bit B3=1) */
123#define cRadioIF_33_30 0x00 // bit e2,4 (also 0x10,0x14)
124#define cRadioIF_41_30 0x04 // bit e2,4
125
126/* Output of AFC pin in radio mode when bit E7=1 */
127#define cRadioAGC_SIF 0x00 // bit e3
128#define cRadioAGC_FM 0x08 // bit e3
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129
130#define cTunerGainNormal 0x00 // bit e5
131#define cTunerGainLow 0x20 // bit e5
132
133#define cGating_18 0x00 // bit e6
134#define cGating_36 0x40 // bit e6
135
136#define cAgcOutON 0x80 // bit e7
137#define cAgcOutOFF 0x00 // bit e7
138
139/* ---------------------------------------------------------------------- */
140
141static struct tvnorm tvnorms[] = {
142 {
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200143 .std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N,
144 .name = "PAL-BGHN",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 .b = ( cNegativeFmTV |
146 cQSS ),
147 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200148 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300149 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200150 .e = ( cGating_36 |
151 cAudioIF_5_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 cVideoIF_38_90 ),
153 },{
154 .std = V4L2_STD_PAL_I,
155 .name = "PAL-I",
156 .b = ( cNegativeFmTV |
157 cQSS ),
158 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200159 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300160 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200161 .e = ( cGating_36 |
162 cAudioIF_6_0 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 cVideoIF_38_90 ),
164 },{
165 .std = V4L2_STD_PAL_DK,
166 .name = "PAL-DK",
167 .b = ( cNegativeFmTV |
168 cQSS ),
169 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200170 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300171 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200172 .e = ( cGating_36 |
173 cAudioIF_6_5 |
174 cVideoIF_38_90 ),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 },{
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200176 .std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc,
177 .name = "PAL-M/Nc",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 .b = ( cNegativeFmTV |
179 cQSS ),
180 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200181 cDeemphasis75 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300182 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200183 .e = ( cGating_36 |
184 cAudioIF_4_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 cVideoIF_45_75 ),
186 },{
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200187 .std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H,
188 .name = "SECAM-BGH",
189 .b = ( cPositiveAmTV |
190 cQSS ),
Hans Verkuilf5b01422006-06-25 15:37:29 -0300191 .c = ( cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200192 .e = ( cGating_36 |
193 cAudioIF_5_5 |
194 cVideoIF_38_90 ),
195 },{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 .std = V4L2_STD_SECAM_L,
197 .name = "SECAM-L",
198 .b = ( cPositiveAmTV |
199 cQSS ),
Hans Verkuilf5b01422006-06-25 15:37:29 -0300200 .c = ( cTopDefault),
Nickolay V. Shmyrev3375c392005-11-08 21:37:05 -0800201 .e = ( cGating_36 |
Nickolay V. Shmyrev5f7591c2005-11-08 21:37:04 -0800202 cAudioIF_6_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 cVideoIF_38_90 ),
204 },{
Mauro Carvalho Chehabf3c59872006-01-09 15:25:00 -0200205 .std = V4L2_STD_SECAM_LC,
206 .name = "SECAM-L'",
207 .b = ( cOutputPort2Inactive |
208 cPositiveAmTV |
209 cQSS ),
Hans Verkuilf5b01422006-06-25 15:37:29 -0300210 .c = ( cTopDefault),
Mauro Carvalho Chehabf3c59872006-01-09 15:25:00 -0200211 .e = ( cGating_36 |
212 cAudioIF_6_5 |
213 cVideoIF_33_90 ),
214 },{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 .std = V4L2_STD_SECAM_DK,
216 .name = "SECAM-DK",
217 .b = ( cNegativeFmTV |
218 cQSS ),
219 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200220 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300221 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200222 .e = ( cGating_36 |
223 cAudioIF_6_5 |
224 cVideoIF_38_90 ),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 },{
Hans Verkuil0dfd8122006-02-07 06:45:34 -0200226 .std = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 .name = "NTSC-M",
228 .b = ( cNegativeFmTV |
229 cQSS ),
230 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200231 cDeemphasis75 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300232 cTopDefault),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 .e = ( cGating_36 |
234 cAudioIF_4_5 |
235 cVideoIF_45_75 ),
236 },{
237 .std = V4L2_STD_NTSC_M_JP,
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200238 .name = "NTSC-M-JP",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 .b = ( cNegativeFmTV |
240 cQSS ),
241 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200242 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300243 cTopDefault),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 .e = ( cGating_36 |
245 cAudioIF_4_5 |
246 cVideoIF_58_75 ),
247 }
248};
249
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700250static struct tvnorm radio_stereo = {
251 .name = "Radio Stereo",
252 .b = ( cFmRadio |
253 cQSS ),
254 .c = ( cDeemphasisOFF |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200255 cAudioGain6 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300256 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200257 .e = ( cTunerGainLow |
258 cAudioIF_5_5 |
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700259 cRadioIF_38_90 ),
260};
261
262static struct tvnorm radio_mono = {
263 .name = "Radio Mono",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 .b = ( cFmRadio |
265 cQSS ),
266 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200267 cDeemphasis75 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300268 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200269 .e = ( cTunerGainLow |
270 cAudioIF_5_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 cRadioIF_38_90 ),
272};
273
274/* ---------------------------------------------------------------------- */
275
Michael Krufky4e9154b2007-10-21 19:39:50 -0300276static void dump_read_message(struct dvb_frontend *fe, unsigned char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300278 struct tda9887_priv *priv = fe->analog_demod_priv;
279
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 static char *afc[16] = {
281 "- 12.5 kHz",
282 "- 37.5 kHz",
283 "- 62.5 kHz",
284 "- 87.5 kHz",
285 "-112.5 kHz",
286 "-137.5 kHz",
287 "-162.5 kHz",
288 "-187.5 kHz [min]",
289 "+187.5 kHz [max]",
290 "+162.5 kHz",
291 "+137.5 kHz",
292 "+112.5 kHz",
293 "+ 87.5 kHz",
294 "+ 62.5 kHz",
295 "+ 37.5 kHz",
296 "+ 12.5 kHz",
297 };
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800298 tda9887_info("read: 0x%2x\n", buf[0]);
299 tda9887_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no");
300 tda9887_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]);
301 tda9887_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low");
302 tda9887_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out");
303 tda9887_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304}
305
Michael Krufky4e9154b2007-10-21 19:39:50 -0300306static void dump_write_message(struct dvb_frontend *fe, unsigned char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300308 struct tda9887_priv *priv = fe->analog_demod_priv;
309
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 static char *sound[4] = {
311 "AM/TV",
312 "FM/radio",
313 "FM/TV",
314 "FM/radio"
315 };
316 static char *adjust[32] = {
317 "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9",
318 "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1",
319 "0", "+1", "+2", "+3", "+4", "+5", "+6", "+7",
320 "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15"
321 };
322 static char *deemph[4] = {
323 "no", "no", "75", "50"
324 };
325 static char *carrier[4] = {
326 "4.5 MHz",
327 "5.5 MHz",
328 "6.0 MHz",
329 "6.5 MHz / AM"
330 };
331 static char *vif[8] = {
332 "58.75 MHz",
333 "45.75 MHz",
334 "38.9 MHz",
335 "38.0 MHz",
336 "33.9 MHz",
337 "33.4 MHz",
338 "45.75 MHz + pin13",
339 "38.9 MHz + pin13",
340 };
341 static char *rif[4] = {
342 "44 MHz",
343 "52 MHz",
344 "52 MHz",
345 "44 MHz",
346 };
347
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800348 tda9887_info("write: byte B 0x%02x\n",buf[1]);
349 tda9887_info(" B0 video mode : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 (buf[1] & 0x01) ? "video trap" : "sound trap");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800351 tda9887_info(" B1 auto mute fm : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 (buf[1] & 0x02) ? "yes" : "no");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800353 tda9887_info(" B2 carrier mode : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 (buf[1] & 0x04) ? "QSS" : "Intercarrier");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800355 tda9887_info(" B3-4 tv sound/radio : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 sound[(buf[1] & 0x18) >> 3]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800357 tda9887_info(" B5 force mute audio: %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 (buf[1] & 0x20) ? "yes" : "no");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800359 tda9887_info(" B6 output port 1 : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 (buf[1] & 0x40) ? "high (inactive)" : "low (active)");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800361 tda9887_info(" B7 output port 2 : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 (buf[1] & 0x80) ? "high (inactive)" : "low (active)");
363
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800364 tda9887_info("write: byte C 0x%02x\n",buf[2]);
365 tda9887_info(" C0-4 top adjustment : %s dB\n", adjust[buf[2] & 0x1f]);
366 tda9887_info(" C5-6 de-emphasis : %s\n", deemph[(buf[2] & 0x60) >> 5]);
367 tda9887_info(" C7 audio gain : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 (buf[2] & 0x80) ? "-6" : "0");
369
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800370 tda9887_info("write: byte E 0x%02x\n",buf[3]);
371 tda9887_info(" E0-1 sound carrier : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 carrier[(buf[3] & 0x03)]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800373 tda9887_info(" E6 l pll gating : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 (buf[3] & 0x40) ? "36" : "13");
375
376 if (buf[1] & 0x08) {
377 /* radio */
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800378 tda9887_info(" E2-4 video if : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 rif[(buf[3] & 0x0c) >> 2]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800380 tda9887_info(" E7 vif agc output : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 (buf[3] & 0x80)
382 ? ((buf[3] & 0x10) ? "fm-agc radio" : "sif-agc radio")
383 : "fm radio carrier afc");
384 } else {
385 /* video */
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800386 tda9887_info(" E2-4 video if : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 vif[(buf[3] & 0x1c) >> 2]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800388 tda9887_info(" E5 tuner gain : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 (buf[3] & 0x80)
390 ? ((buf[3] & 0x20) ? "external" : "normal")
391 : ((buf[3] & 0x20) ? "minimum" : "normal"));
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800392 tda9887_info(" E7 vif agc output : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 (buf[3] & 0x80)
394 ? ((buf[3] & 0x20)
395 ? "pin3 port, pin22 vif agc out"
396 : "pin22 port, pin3 vif acg ext in")
397 : "pin3+pin22 port");
398 }
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800399 tda9887_info("--\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400}
401
402/* ---------------------------------------------------------------------- */
403
Michael Krufky4e9154b2007-10-21 19:39:50 -0300404static int tda9887_set_tvnorm(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300406 struct tda9887_priv *priv = fe->analog_demod_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 struct tvnorm *norm = NULL;
Michael Krufky4e9154b2007-10-21 19:39:50 -0300408 char *buf = priv->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 int i;
410
Michael Krufky91c9d4a2007-12-16 20:05:00 -0300411 if (priv->mode == V4L2_TUNER_RADIO) {
412 if (priv->audmode == V4L2_TUNER_MODE_MONO)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700413 norm = &radio_mono;
414 else
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700415 norm = &radio_stereo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 } else {
417 for (i = 0; i < ARRAY_SIZE(tvnorms); i++) {
Michael Krufky91c9d4a2007-12-16 20:05:00 -0300418 if (tvnorms[i].std & priv->std) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 norm = tvnorms+i;
420 break;
421 }
422 }
423 }
424 if (NULL == norm) {
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800425 tda9887_dbg("Unsupported tvnorm entry - audio muted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 return -1;
427 }
428
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800429 tda9887_dbg("configure for: %s\n",norm->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 buf[1] = norm->b;
431 buf[2] = norm->c;
432 buf[3] = norm->e;
433 return 0;
434}
435
436static unsigned int port1 = UNSET;
437static unsigned int port2 = UNSET;
438static unsigned int qss = UNSET;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200439static unsigned int adjust = UNSET;
440
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441module_param(port1, int, 0644);
442module_param(port2, int, 0644);
443module_param(qss, int, 0644);
444module_param(adjust, int, 0644);
445
Michael Krufky4e9154b2007-10-21 19:39:50 -0300446static int tda9887_set_insmod(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300448 struct tda9887_priv *priv = fe->analog_demod_priv;
449 char *buf = priv->data;
450
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 if (UNSET != port1) {
452 if (port1)
453 buf[1] |= cOutputPort1Inactive;
454 else
455 buf[1] &= ~cOutputPort1Inactive;
456 }
457 if (UNSET != port2) {
458 if (port2)
459 buf[1] |= cOutputPort2Inactive;
460 else
461 buf[1] &= ~cOutputPort2Inactive;
462 }
463
464 if (UNSET != qss) {
465 if (qss)
466 buf[1] |= cQSS;
467 else
468 buf[1] &= ~cQSS;
469 }
470
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200471 if (adjust >= 0x00 && adjust < 0x20) {
472 buf[2] &= ~cTopMask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 buf[2] |= adjust;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200474 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 return 0;
476}
477
Michael Krufky710401b2007-12-16 19:53:32 -0300478static int tda9887_do_config(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300480 struct tda9887_priv *priv = fe->analog_demod_priv;
Michael Krufky4e9154b2007-10-21 19:39:50 -0300481 char *buf = priv->data;
482
Michael Krufky710401b2007-12-16 19:53:32 -0300483 if (priv->config & TDA9887_PORT1_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 buf[1] &= ~cOutputPort1Inactive;
Michael Krufky710401b2007-12-16 19:53:32 -0300485 if (priv->config & TDA9887_PORT1_INACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 buf[1] |= cOutputPort1Inactive;
Michael Krufky710401b2007-12-16 19:53:32 -0300487 if (priv->config & TDA9887_PORT2_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 buf[1] &= ~cOutputPort2Inactive;
Michael Krufky710401b2007-12-16 19:53:32 -0300489 if (priv->config & TDA9887_PORT2_INACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 buf[1] |= cOutputPort2Inactive;
491
Michael Krufky710401b2007-12-16 19:53:32 -0300492 if (priv->config & TDA9887_QSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 buf[1] |= cQSS;
Michael Krufky710401b2007-12-16 19:53:32 -0300494 if (priv->config & TDA9887_INTERCARRIER)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 buf[1] &= ~cQSS;
496
Michael Krufky710401b2007-12-16 19:53:32 -0300497 if (priv->config & TDA9887_AUTOMUTE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 buf[1] |= cAutoMuteFmActive;
Michael Krufky710401b2007-12-16 19:53:32 -0300499 if (priv->config & TDA9887_DEEMPHASIS_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 buf[2] &= ~0x60;
Michael Krufky710401b2007-12-16 19:53:32 -0300501 switch (priv->config & TDA9887_DEEMPHASIS_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 case TDA9887_DEEMPHASIS_NONE:
503 buf[2] |= cDeemphasisOFF;
504 break;
505 case TDA9887_DEEMPHASIS_50:
506 buf[2] |= cDeemphasisON | cDeemphasis50;
507 break;
508 case TDA9887_DEEMPHASIS_75:
509 buf[2] |= cDeemphasisON | cDeemphasis75;
510 break;
511 }
512 }
Michael Krufky710401b2007-12-16 19:53:32 -0300513 if (priv->config & TDA9887_TOP_SET) {
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200514 buf[2] &= ~cTopMask;
Michael Krufky710401b2007-12-16 19:53:32 -0300515 buf[2] |= (priv->config >> 8) & cTopMask;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200516 }
Michael Krufky710401b2007-12-16 19:53:32 -0300517 if ((priv->config & TDA9887_INTERCARRIER_NTSC) &&
Michael Krufky91c9d4a2007-12-16 20:05:00 -0300518 (priv->std & V4L2_STD_NTSC))
Nickolay V. Shmyrev3ae1adc2005-11-08 21:37:39 -0800519 buf[1] &= ~cQSS;
Michael Krufky710401b2007-12-16 19:53:32 -0300520 if (priv->config & TDA9887_GATING_18)
Trent Piephod7304de2006-08-24 22:43:45 -0300521 buf[3] &= ~cGating_36;
Mauro Carvalho Chehabcefccc82006-12-04 08:31:35 -0300522
Michael Krufky91c9d4a2007-12-16 20:05:00 -0300523 if (priv->mode == V4L2_TUNER_RADIO) {
Michael Krufky710401b2007-12-16 19:53:32 -0300524 if (priv->config & TDA9887_RIF_41_3) {
Trent Piepho5e082f12007-08-03 18:32:38 -0300525 buf[3] &= ~cVideoIFMask;
526 buf[3] |= cRadioIF_41_30;
527 }
Michael Krufky710401b2007-12-16 19:53:32 -0300528 if (priv->config & TDA9887_GAIN_NORMAL)
Trent Piepho5e082f12007-08-03 18:32:38 -0300529 buf[3] &= ~cTunerGainLow;
Mauro Carvalho Chehabcefccc82006-12-04 08:31:35 -0300530 }
531
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 return 0;
533}
534
535/* ---------------------------------------------------------------------- */
536
Michael Krufky4e9154b2007-10-21 19:39:50 -0300537static int tda9887_status(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300539 struct tda9887_priv *priv = fe->analog_demod_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 unsigned char buf[1];
541 int rc;
542
543 memset(buf,0,sizeof(buf));
Michael Krufkydb8a6952007-08-21 01:24:42 -0300544 if (1 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props,buf,1)))
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800545 tda9887_info("i2c i/o error: rc == %d (should be 1)\n",rc);
Michael Krufky4e9154b2007-10-21 19:39:50 -0300546 dump_read_message(fe, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 return 0;
548}
549
Michael Krufky4e9154b2007-10-21 19:39:50 -0300550static void tda9887_configure(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300552 struct tda9887_priv *priv = fe->analog_demod_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 int rc;
554
Michael Krufkyb2083192007-05-29 22:54:06 -0300555 memset(priv->data,0,sizeof(priv->data));
Michael Krufky4e9154b2007-10-21 19:39:50 -0300556 tda9887_set_tvnorm(fe);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700557
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200558 /* A note on the port settings:
559 These settings tend to depend on the specifics of the board.
560 By default they are set to inactive (bit value 1) by this driver,
561 overwriting any changes made by the tvnorm. This means that it
562 is the responsibility of the module using the tda9887 to set
563 these values in case of changes in the tvnorm.
564 In many cases port 2 should be made active (0) when selecting
565 SECAM-L, and port 2 should remain inactive (1) for SECAM-L'.
566
567 For the other standards the tda9887 application note says that
568 the ports should be set to active (0), but, again, that may
569 differ depending on the precise hardware configuration.
570 */
Michael Krufkyb2083192007-05-29 22:54:06 -0300571 priv->data[1] |= cOutputPort1Inactive;
572 priv->data[1] |= cOutputPort2Inactive;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700573
Michael Krufky710401b2007-12-16 19:53:32 -0300574 tda9887_do_config(fe);
Michael Krufky4e9154b2007-10-21 19:39:50 -0300575 tda9887_set_insmod(fe);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
Michael Krufky91c9d4a2007-12-16 20:05:00 -0300577 if (priv->mode == T_STANDBY)
Michael Krufkyb2083192007-05-29 22:54:06 -0300578 priv->data[1] |= cForcedMuteAudioON;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700579
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800580 tda9887_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",
Michael Krufkyb2083192007-05-29 22:54:06 -0300581 priv->data[1],priv->data[2],priv->data[3]);
Michael Krufky31c95842007-10-21 20:48:48 -0300582 if (tda9887_debug > 1)
Michael Krufky4e9154b2007-10-21 19:39:50 -0300583 dump_write_message(fe, priv->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
Michael Krufkydb8a6952007-08-21 01:24:42 -0300585 if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,priv->data,4)))
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800586 tda9887_info("i2c i/o error: rc == %d (should be 4)\n",rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
Michael Krufky31c95842007-10-21 20:48:48 -0300588 if (tda9887_debug > 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 msleep_interruptible(1000);
Michael Krufky4e9154b2007-10-21 19:39:50 -0300590 tda9887_status(fe);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592}
593
594/* ---------------------------------------------------------------------- */
595
Michael Krufky4e9154b2007-10-21 19:39:50 -0300596static void tda9887_tuner_status(struct dvb_frontend *fe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300598 struct tda9887_priv *priv = fe->analog_demod_priv;
Michael Krufky31c95842007-10-21 20:48:48 -0300599 tda9887_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n",
600 priv->data[1], priv->data[2], priv->data[3]);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300601}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
Michael Krufky4e9154b2007-10-21 19:39:50 -0300603static int tda9887_get_afc(struct dvb_frontend *fe)
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300604{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300605 struct tda9887_priv *priv = fe->analog_demod_priv;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300606 static int AFC_BITS_2_kHz[] = {
607 -12500, -37500, -62500, -97500,
608 -112500, -137500, -162500, -187500,
609 187500, 162500, 137500, 112500,
610 97500 , 62500, 37500 , 12500
611 };
612 int afc=0;
613 __u8 reg = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614
Michael Krufkydb8a6952007-08-21 01:24:42 -0300615 if (1 == tuner_i2c_xfer_recv(&priv->i2c_props,&reg,1))
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300616 afc = AFC_BITS_2_kHz[(reg>>1)&0x0f];
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700617
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300618 return afc;
619}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700620
Michael Krufky4e9154b2007-10-21 19:39:50 -0300621static void tda9887_standby(struct dvb_frontend *fe)
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300622{
Michael Krufky91c9d4a2007-12-16 20:05:00 -0300623 struct tda9887_priv *priv = fe->analog_demod_priv;
624
625 priv->mode = T_STANDBY;
626
Michael Krufky4e9154b2007-10-21 19:39:50 -0300627 tda9887_configure(fe);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300628}
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800629
Michael Krufkyc7919d52007-12-08 17:06:30 -0300630static void tda9887_set_params(struct dvb_frontend *fe,
631 struct analog_parameters *params)
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300632{
Michael Krufky91c9d4a2007-12-16 20:05:00 -0300633 struct tda9887_priv *priv = fe->analog_demod_priv;
634
635 priv->mode = params->mode;
636 priv->audmode = params->audmode;
637 priv->std = params->std;
Michael Krufky4e9154b2007-10-21 19:39:50 -0300638 tda9887_configure(fe);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300639}
640
Michael Krufky710401b2007-12-16 19:53:32 -0300641static int tda9887_set_config(struct dvb_frontend *fe, void *priv_cfg)
642{
643 struct tda9887_priv *priv = fe->analog_demod_priv;
644
645 priv->config = *(unsigned int *)priv_cfg;
646 tda9887_configure(fe);
647
648 return 0;
649}
650
Michael Krufky4e9154b2007-10-21 19:39:50 -0300651static void tda9887_release(struct dvb_frontend *fe)
Michael Krufky024cf532007-06-04 15:20:11 -0300652{
Michael Krufky4e9154b2007-10-21 19:39:50 -0300653 kfree(fe->analog_demod_priv);
654 fe->analog_demod_priv = NULL;
Michael Krufky024cf532007-06-04 15:20:11 -0300655}
656
Michael Krufky1dde7a42007-10-21 13:40:56 -0300657static struct analog_tuner_ops tda9887_tuner_ops = {
Michael Krufkya55db8c2007-12-09 13:52:51 -0300658 .info = {
659 .name = "TDA9887",
660 },
Michael Krufkyc7919d52007-12-08 17:06:30 -0300661 .set_params = tda9887_set_params,
Michael Krufky9af596e2007-06-06 16:15:48 -0300662 .standby = tda9887_standby,
663 .tuner_status = tda9887_tuner_status,
664 .get_afc = tda9887_get_afc,
665 .release = tda9887_release,
Michael Krufky710401b2007-12-16 19:53:32 -0300666 .set_config = tda9887_set_config,
Michael Krufky9af596e2007-06-06 16:15:48 -0300667};
668
Michael Krufky8ca40832007-12-16 20:11:46 -0300669struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
670 struct i2c_adapter *i2c_adap,
671 u8 i2c_addr)
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300672{
Michael Krufkyb2083192007-05-29 22:54:06 -0300673 struct tda9887_priv *priv = NULL;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300674
Michael Krufkyb2083192007-05-29 22:54:06 -0300675 priv = kzalloc(sizeof(struct tda9887_priv), GFP_KERNEL);
676 if (priv == NULL)
Michael Krufky8ca40832007-12-16 20:11:46 -0300677 return NULL;
678 fe->analog_demod_priv = priv;
Michael Krufkyb2083192007-05-29 22:54:06 -0300679
Michael Krufky8ca40832007-12-16 20:11:46 -0300680 priv->i2c_props.addr = i2c_addr;
681 priv->i2c_props.adap = i2c_adap;
Michael Krufkydb8a6952007-08-21 01:24:42 -0300682
Michael Krufky8ca40832007-12-16 20:11:46 -0300683 tda9887_info("tda988[5/6/7] found\n");
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300684
Michael Krufky8ca40832007-12-16 20:11:46 -0300685 fe->ops.analog_demod_ops = &tda9887_tuner_ops;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300686
Michael Krufky8ca40832007-12-16 20:11:46 -0300687 return fe;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688}
Michael Krufky31c95842007-10-21 20:48:48 -0300689EXPORT_SYMBOL_GPL(tda9887_attach);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
Michael Krufky5ef47302007-10-27 02:17:19 -0300691MODULE_LICENSE("GPL");
692
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693/*
694 * Overrides for Emacs so that we follow Linus's tabbing style.
695 * ---------------------------------------------------------------------------
696 * Local variables:
697 * c-basic-offset: 8
698 * End:
699 */