blob: caca1092790ec61a04f02206415f18c06eaff32d [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001#include <linux/module.h>
2#include <linux/moduleparam.h>
3#include <linux/kernel.h>
4#include <linux/i2c.h>
5#include <linux/types.h>
6#include <linux/videodev.h>
7#include <linux/init.h>
8#include <linux/errno.h>
9#include <linux/slab.h>
10#include <linux/delay.h>
11
Michael Krufky5e453dc2006-01-09 15:32:31 -020012#include <media/v4l2-common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include <media/tuner.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014
Mauro Carvalho Chehab674434c2005-12-12 00:37:28 -080015
Linus Torvalds1da177e2005-04-16 15:20:36 -070016/* Chips:
17 TDA9885 (PAL, NTSC)
18 TDA9886 (PAL, SECAM, NTSC)
19 TDA9887 (PAL, SECAM, NTSC, FM Radio)
20
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -030021 Used as part of several tuners
Linus Torvalds1da177e2005-04-16 15:20:36 -070022*/
23
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -030024#define tda9887_info(fmt, arg...) do {\
Hans Verkuil343f92c2006-06-25 11:02:02 -030025 printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -030026 i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
27#define tda9887_dbg(fmt, arg...) do {\
28 if (tuner_debug) \
Hans Verkuil343f92c2006-06-25 11:02:02 -030029 printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -030030 i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
Michael Krufkyb2083192007-05-29 22:54:06 -030032struct tda9887_priv {
33 unsigned char data[4];
34};
Linus Torvalds1da177e2005-04-16 15:20:36 -070035
36/* ---------------------------------------------------------------------- */
37
38#define UNSET (-1U)
Linus Torvalds1da177e2005-04-16 15:20:36 -070039
40struct tvnorm {
41 v4l2_std_id std;
42 char *name;
43 unsigned char b;
44 unsigned char c;
45 unsigned char e;
46};
47
Linus Torvalds1da177e2005-04-16 15:20:36 -070048/* ---------------------------------------------------------------------- */
49
50//
51// TDA defines
52//
53
54//// first reg (b)
55#define cVideoTrapBypassOFF 0x00 // bit b0
56#define cVideoTrapBypassON 0x01 // bit b0
57
58#define cAutoMuteFmInactive 0x00 // bit b1
59#define cAutoMuteFmActive 0x02 // bit b1
60
61#define cIntercarrier 0x00 // bit b2
62#define cQSS 0x04 // bit b2
63
64#define cPositiveAmTV 0x00 // bit b3:4
65#define cFmRadio 0x08 // bit b3:4
66#define cNegativeFmTV 0x10 // bit b3:4
67
68
69#define cForcedMuteAudioON 0x20 // bit b5
70#define cForcedMuteAudioOFF 0x00 // bit b5
71
72#define cOutputPort1Active 0x00 // bit b6
73#define cOutputPort1Inactive 0x40 // bit b6
74
75#define cOutputPort2Active 0x00 // bit b7
76#define cOutputPort2Inactive 0x80 // bit b7
77
78
79//// second reg (c)
80#define cDeemphasisOFF 0x00 // bit c5
81#define cDeemphasisON 0x20 // bit c5
82
83#define cDeemphasis75 0x00 // bit c6
84#define cDeemphasis50 0x40 // bit c6
85
86#define cAudioGain0 0x00 // bit c7
87#define cAudioGain6 0x80 // bit c7
88
Hans Verkuilf98c55e2006-01-09 15:25:18 -020089#define cTopMask 0x1f // bit c0:4
Hans Verkuilf5b01422006-06-25 15:37:29 -030090#define cTopDefault 0x10 // bit c0:4
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
92//// third reg (e)
93#define cAudioIF_4_5 0x00 // bit e0:1
94#define cAudioIF_5_5 0x01 // bit e0:1
95#define cAudioIF_6_0 0x02 // bit e0:1
96#define cAudioIF_6_5 0x03 // bit e0:1
97
98
99#define cVideoIF_58_75 0x00 // bit e2:4
100#define cVideoIF_45_75 0x04 // bit e2:4
101#define cVideoIF_38_90 0x08 // bit e2:4
102#define cVideoIF_38_00 0x0C // bit e2:4
103#define cVideoIF_33_90 0x10 // bit e2:4
104#define cVideoIF_33_40 0x14 // bit e2:4
105#define cRadioIF_45_75 0x18 // bit e2:4
106#define cRadioIF_38_90 0x1C // bit e2:4
107
108
109#define cTunerGainNormal 0x00 // bit e5
110#define cTunerGainLow 0x20 // bit e5
111
112#define cGating_18 0x00 // bit e6
113#define cGating_36 0x40 // bit e6
114
115#define cAgcOutON 0x80 // bit e7
116#define cAgcOutOFF 0x00 // bit e7
117
118/* ---------------------------------------------------------------------- */
119
120static struct tvnorm tvnorms[] = {
121 {
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200122 .std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N,
123 .name = "PAL-BGHN",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 .b = ( cNegativeFmTV |
125 cQSS ),
126 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200127 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300128 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200129 .e = ( cGating_36 |
130 cAudioIF_5_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 cVideoIF_38_90 ),
132 },{
133 .std = V4L2_STD_PAL_I,
134 .name = "PAL-I",
135 .b = ( cNegativeFmTV |
136 cQSS ),
137 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200138 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300139 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200140 .e = ( cGating_36 |
141 cAudioIF_6_0 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 cVideoIF_38_90 ),
143 },{
144 .std = V4L2_STD_PAL_DK,
145 .name = "PAL-DK",
146 .b = ( cNegativeFmTV |
147 cQSS ),
148 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200149 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300150 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200151 .e = ( cGating_36 |
152 cAudioIF_6_5 |
153 cVideoIF_38_90 ),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 },{
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200155 .std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc,
156 .name = "PAL-M/Nc",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 .b = ( cNegativeFmTV |
158 cQSS ),
159 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200160 cDeemphasis75 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300161 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200162 .e = ( cGating_36 |
163 cAudioIF_4_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 cVideoIF_45_75 ),
165 },{
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200166 .std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H,
167 .name = "SECAM-BGH",
168 .b = ( cPositiveAmTV |
169 cQSS ),
Hans Verkuilf5b01422006-06-25 15:37:29 -0300170 .c = ( cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200171 .e = ( cGating_36 |
172 cAudioIF_5_5 |
173 cVideoIF_38_90 ),
174 },{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 .std = V4L2_STD_SECAM_L,
176 .name = "SECAM-L",
177 .b = ( cPositiveAmTV |
178 cQSS ),
Hans Verkuilf5b01422006-06-25 15:37:29 -0300179 .c = ( cTopDefault),
Nickolay V. Shmyrev3375c392005-11-08 21:37:05 -0800180 .e = ( cGating_36 |
Nickolay V. Shmyrev5f7591c2005-11-08 21:37:04 -0800181 cAudioIF_6_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 cVideoIF_38_90 ),
183 },{
Mauro Carvalho Chehabf3c59872006-01-09 15:25:00 -0200184 .std = V4L2_STD_SECAM_LC,
185 .name = "SECAM-L'",
186 .b = ( cOutputPort2Inactive |
187 cPositiveAmTV |
188 cQSS ),
Hans Verkuilf5b01422006-06-25 15:37:29 -0300189 .c = ( cTopDefault),
Mauro Carvalho Chehabf3c59872006-01-09 15:25:00 -0200190 .e = ( cGating_36 |
191 cAudioIF_6_5 |
192 cVideoIF_33_90 ),
193 },{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 .std = V4L2_STD_SECAM_DK,
195 .name = "SECAM-DK",
196 .b = ( cNegativeFmTV |
197 cQSS ),
198 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200199 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300200 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200201 .e = ( cGating_36 |
202 cAudioIF_6_5 |
203 cVideoIF_38_90 ),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 },{
Hans Verkuil0dfd8122006-02-07 06:45:34 -0200205 .std = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 .name = "NTSC-M",
207 .b = ( cNegativeFmTV |
208 cQSS ),
209 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200210 cDeemphasis75 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300211 cTopDefault),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 .e = ( cGating_36 |
213 cAudioIF_4_5 |
214 cVideoIF_45_75 ),
215 },{
216 .std = V4L2_STD_NTSC_M_JP,
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200217 .name = "NTSC-M-JP",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 .b = ( cNegativeFmTV |
219 cQSS ),
220 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200221 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300222 cTopDefault),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 .e = ( cGating_36 |
224 cAudioIF_4_5 |
225 cVideoIF_58_75 ),
226 }
227};
228
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700229static struct tvnorm radio_stereo = {
230 .name = "Radio Stereo",
231 .b = ( cFmRadio |
232 cQSS ),
233 .c = ( cDeemphasisOFF |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200234 cAudioGain6 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300235 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200236 .e = ( cTunerGainLow |
237 cAudioIF_5_5 |
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700238 cRadioIF_38_90 ),
239};
240
241static struct tvnorm radio_mono = {
242 .name = "Radio Mono",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 .b = ( cFmRadio |
244 cQSS ),
245 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200246 cDeemphasis75 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300247 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200248 .e = ( cTunerGainLow |
249 cAudioIF_5_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 cRadioIF_38_90 ),
251};
252
253/* ---------------------------------------------------------------------- */
254
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300255static void dump_read_message(struct tuner *t, unsigned char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256{
257 static char *afc[16] = {
258 "- 12.5 kHz",
259 "- 37.5 kHz",
260 "- 62.5 kHz",
261 "- 87.5 kHz",
262 "-112.5 kHz",
263 "-137.5 kHz",
264 "-162.5 kHz",
265 "-187.5 kHz [min]",
266 "+187.5 kHz [max]",
267 "+162.5 kHz",
268 "+137.5 kHz",
269 "+112.5 kHz",
270 "+ 87.5 kHz",
271 "+ 62.5 kHz",
272 "+ 37.5 kHz",
273 "+ 12.5 kHz",
274 };
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800275 tda9887_info("read: 0x%2x\n", buf[0]);
276 tda9887_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no");
277 tda9887_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]);
278 tda9887_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low");
279 tda9887_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out");
280 tda9887_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281}
282
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300283static void dump_write_message(struct tuner *t, unsigned char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284{
285 static char *sound[4] = {
286 "AM/TV",
287 "FM/radio",
288 "FM/TV",
289 "FM/radio"
290 };
291 static char *adjust[32] = {
292 "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9",
293 "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1",
294 "0", "+1", "+2", "+3", "+4", "+5", "+6", "+7",
295 "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15"
296 };
297 static char *deemph[4] = {
298 "no", "no", "75", "50"
299 };
300 static char *carrier[4] = {
301 "4.5 MHz",
302 "5.5 MHz",
303 "6.0 MHz",
304 "6.5 MHz / AM"
305 };
306 static char *vif[8] = {
307 "58.75 MHz",
308 "45.75 MHz",
309 "38.9 MHz",
310 "38.0 MHz",
311 "33.9 MHz",
312 "33.4 MHz",
313 "45.75 MHz + pin13",
314 "38.9 MHz + pin13",
315 };
316 static char *rif[4] = {
317 "44 MHz",
318 "52 MHz",
319 "52 MHz",
320 "44 MHz",
321 };
322
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800323 tda9887_info("write: byte B 0x%02x\n",buf[1]);
324 tda9887_info(" B0 video mode : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 (buf[1] & 0x01) ? "video trap" : "sound trap");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800326 tda9887_info(" B1 auto mute fm : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 (buf[1] & 0x02) ? "yes" : "no");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800328 tda9887_info(" B2 carrier mode : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 (buf[1] & 0x04) ? "QSS" : "Intercarrier");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800330 tda9887_info(" B3-4 tv sound/radio : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 sound[(buf[1] & 0x18) >> 3]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800332 tda9887_info(" B5 force mute audio: %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 (buf[1] & 0x20) ? "yes" : "no");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800334 tda9887_info(" B6 output port 1 : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 (buf[1] & 0x40) ? "high (inactive)" : "low (active)");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800336 tda9887_info(" B7 output port 2 : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 (buf[1] & 0x80) ? "high (inactive)" : "low (active)");
338
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800339 tda9887_info("write: byte C 0x%02x\n",buf[2]);
340 tda9887_info(" C0-4 top adjustment : %s dB\n", adjust[buf[2] & 0x1f]);
341 tda9887_info(" C5-6 de-emphasis : %s\n", deemph[(buf[2] & 0x60) >> 5]);
342 tda9887_info(" C7 audio gain : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 (buf[2] & 0x80) ? "-6" : "0");
344
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800345 tda9887_info("write: byte E 0x%02x\n",buf[3]);
346 tda9887_info(" E0-1 sound carrier : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 carrier[(buf[3] & 0x03)]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800348 tda9887_info(" E6 l pll gating : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 (buf[3] & 0x40) ? "36" : "13");
350
351 if (buf[1] & 0x08) {
352 /* radio */
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800353 tda9887_info(" E2-4 video if : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 rif[(buf[3] & 0x0c) >> 2]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800355 tda9887_info(" E7 vif agc output : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 (buf[3] & 0x80)
357 ? ((buf[3] & 0x10) ? "fm-agc radio" : "sif-agc radio")
358 : "fm radio carrier afc");
359 } else {
360 /* video */
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800361 tda9887_info(" E2-4 video if : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 vif[(buf[3] & 0x1c) >> 2]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800363 tda9887_info(" E5 tuner gain : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 (buf[3] & 0x80)
365 ? ((buf[3] & 0x20) ? "external" : "normal")
366 : ((buf[3] & 0x20) ? "minimum" : "normal"));
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800367 tda9887_info(" E7 vif agc output : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 (buf[3] & 0x80)
369 ? ((buf[3] & 0x20)
370 ? "pin3 port, pin22 vif agc out"
371 : "pin22 port, pin3 vif acg ext in")
372 : "pin3+pin22 port");
373 }
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800374 tda9887_info("--\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375}
376
377/* ---------------------------------------------------------------------- */
378
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300379static int tda9887_set_tvnorm(struct tuner *t, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380{
381 struct tvnorm *norm = NULL;
382 int i;
383
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300384 if (t->mode == V4L2_TUNER_RADIO) {
385 if (t->audmode == V4L2_TUNER_MODE_MONO)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700386 norm = &radio_mono;
387 else
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700388 norm = &radio_stereo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 } else {
390 for (i = 0; i < ARRAY_SIZE(tvnorms); i++) {
391 if (tvnorms[i].std & t->std) {
392 norm = tvnorms+i;
393 break;
394 }
395 }
396 }
397 if (NULL == norm) {
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800398 tda9887_dbg("Unsupported tvnorm entry - audio muted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 return -1;
400 }
401
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800402 tda9887_dbg("configure for: %s\n",norm->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 buf[1] = norm->b;
404 buf[2] = norm->c;
405 buf[3] = norm->e;
406 return 0;
407}
408
409static unsigned int port1 = UNSET;
410static unsigned int port2 = UNSET;
411static unsigned int qss = UNSET;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200412static unsigned int adjust = UNSET;
413
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414module_param(port1, int, 0644);
415module_param(port2, int, 0644);
416module_param(qss, int, 0644);
417module_param(adjust, int, 0644);
418
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300419static int tda9887_set_insmod(struct tuner *t, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420{
421 if (UNSET != port1) {
422 if (port1)
423 buf[1] |= cOutputPort1Inactive;
424 else
425 buf[1] &= ~cOutputPort1Inactive;
426 }
427 if (UNSET != port2) {
428 if (port2)
429 buf[1] |= cOutputPort2Inactive;
430 else
431 buf[1] &= ~cOutputPort2Inactive;
432 }
433
434 if (UNSET != qss) {
435 if (qss)
436 buf[1] |= cQSS;
437 else
438 buf[1] &= ~cQSS;
439 }
440
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200441 if (adjust >= 0x00 && adjust < 0x20) {
442 buf[2] &= ~cTopMask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 buf[2] |= adjust;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200444 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 return 0;
446}
447
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300448static int tda9887_set_config(struct tuner *t, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449{
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300450 if (t->tda9887_config & TDA9887_PORT1_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 buf[1] &= ~cOutputPort1Inactive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300452 if (t->tda9887_config & TDA9887_PORT1_INACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 buf[1] |= cOutputPort1Inactive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300454 if (t->tda9887_config & TDA9887_PORT2_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 buf[1] &= ~cOutputPort2Inactive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300456 if (t->tda9887_config & TDA9887_PORT2_INACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 buf[1] |= cOutputPort2Inactive;
458
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300459 if (t->tda9887_config & TDA9887_QSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 buf[1] |= cQSS;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300461 if (t->tda9887_config & TDA9887_INTERCARRIER)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 buf[1] &= ~cQSS;
463
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300464 if (t->tda9887_config & TDA9887_AUTOMUTE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 buf[1] |= cAutoMuteFmActive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300466 if (t->tda9887_config & TDA9887_DEEMPHASIS_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 buf[2] &= ~0x60;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300468 switch (t->tda9887_config & TDA9887_DEEMPHASIS_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 case TDA9887_DEEMPHASIS_NONE:
470 buf[2] |= cDeemphasisOFF;
471 break;
472 case TDA9887_DEEMPHASIS_50:
473 buf[2] |= cDeemphasisON | cDeemphasis50;
474 break;
475 case TDA9887_DEEMPHASIS_75:
476 buf[2] |= cDeemphasisON | cDeemphasis75;
477 break;
478 }
479 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300480 if (t->tda9887_config & TDA9887_TOP_SET) {
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200481 buf[2] &= ~cTopMask;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300482 buf[2] |= (t->tda9887_config >> 8) & cTopMask;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200483 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300484 if ((t->tda9887_config & TDA9887_INTERCARRIER_NTSC) && (t->std & V4L2_STD_NTSC))
Nickolay V. Shmyrev3ae1adc2005-11-08 21:37:39 -0800485 buf[1] &= ~cQSS;
Trent Piephod7304de2006-08-24 22:43:45 -0300486 if (t->tda9887_config & TDA9887_GATING_18)
487 buf[3] &= ~cGating_36;
Mauro Carvalho Chehabcefccc82006-12-04 08:31:35 -0300488
489 if (t->tda9887_config & TDA9887_GAIN_NORMAL) {
490 radio_stereo.e &= ~cTunerGainLow;
491 radio_mono.e &= ~cTunerGainLow;
492 }
493
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 return 0;
495}
496
497/* ---------------------------------------------------------------------- */
498
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300499static int tda9887_status(struct tuner *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500{
501 unsigned char buf[1];
502 int rc;
503
504 memset(buf,0,sizeof(buf));
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300505 if (1 != (rc = i2c_master_recv(&t->i2c,buf,1)))
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800506 tda9887_info("i2c i/o error: rc == %d (should be 1)\n",rc);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800507 dump_read_message(t, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 return 0;
509}
510
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300511static void tda9887_configure(struct i2c_client *client)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512{
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300513 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkyb2083192007-05-29 22:54:06 -0300514 struct tda9887_priv *priv = t->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 int rc;
516
Michael Krufkyb2083192007-05-29 22:54:06 -0300517 memset(priv->data,0,sizeof(priv->data));
518 tda9887_set_tvnorm(t,priv->data);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700519
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200520 /* A note on the port settings:
521 These settings tend to depend on the specifics of the board.
522 By default they are set to inactive (bit value 1) by this driver,
523 overwriting any changes made by the tvnorm. This means that it
524 is the responsibility of the module using the tda9887 to set
525 these values in case of changes in the tvnorm.
526 In many cases port 2 should be made active (0) when selecting
527 SECAM-L, and port 2 should remain inactive (1) for SECAM-L'.
528
529 For the other standards the tda9887 application note says that
530 the ports should be set to active (0), but, again, that may
531 differ depending on the precise hardware configuration.
532 */
Michael Krufkyb2083192007-05-29 22:54:06 -0300533 priv->data[1] |= cOutputPort1Inactive;
534 priv->data[1] |= cOutputPort2Inactive;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700535
Michael Krufkyb2083192007-05-29 22:54:06 -0300536 tda9887_set_config(t,priv->data);
537 tda9887_set_insmod(t,priv->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700539 if (t->mode == T_STANDBY) {
Michael Krufkyb2083192007-05-29 22:54:06 -0300540 priv->data[1] |= cForcedMuteAudioON;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700541 }
542
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800543 tda9887_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",
Michael Krufkyb2083192007-05-29 22:54:06 -0300544 priv->data[1],priv->data[2],priv->data[3]);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300545 if (tuner_debug > 1)
Michael Krufkyb2083192007-05-29 22:54:06 -0300546 dump_write_message(t, priv->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
Michael Krufkyb2083192007-05-29 22:54:06 -0300548 if (4 != (rc = i2c_master_send(&t->i2c,priv->data,4)))
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800549 tda9887_info("i2c i/o error: rc == %d (should be 4)\n",rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300551 if (tuner_debug > 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 msleep_interruptible(1000);
553 tda9887_status(t);
554 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555}
556
557/* ---------------------------------------------------------------------- */
558
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300559static void tda9887_tuner_status(struct i2c_client *client)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560{
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300561 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkyb2083192007-05-29 22:54:06 -0300562 struct tda9887_priv *priv = t->priv;
563 tda9887_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n", priv->data[1], priv->data[2], priv->data[3]);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300564}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300566static int tda9887_get_afc(struct i2c_client *client)
567{
568 struct tuner *t = i2c_get_clientdata(client);
569 static int AFC_BITS_2_kHz[] = {
570 -12500, -37500, -62500, -97500,
571 -112500, -137500, -162500, -187500,
572 187500, 162500, 137500, 112500,
573 97500 , 62500, 37500 , 12500
574 };
575 int afc=0;
576 __u8 reg = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300578 if (1 == i2c_master_recv(&t->i2c,&reg,1))
579 afc = AFC_BITS_2_kHz[(reg>>1)&0x0f];
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700580
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300581 return afc;
582}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700583
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300584static void tda9887_standby(struct i2c_client *client)
585{
586 tda9887_configure(client);
587}
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800588
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300589static void tda9887_set_freq(struct i2c_client *client, unsigned int freq)
590{
591 tda9887_configure(client);
592}
593
Michael Krufky024cf532007-06-04 15:20:11 -0300594static void tda9887_release(struct i2c_client *c)
595{
596 struct tuner *t = i2c_get_clientdata(c);
597
598 kfree(t->priv);
599 t->priv = NULL;
600}
601
Michael Krufky9af596e2007-06-06 16:15:48 -0300602static struct tuner_operations tda9887_tuner_ops = {
603 .set_tv_freq = tda9887_set_freq,
604 .set_radio_freq = tda9887_set_freq,
605 .standby = tda9887_standby,
606 .tuner_status = tda9887_tuner_status,
607 .get_afc = tda9887_get_afc,
608 .release = tda9887_release,
609};
610
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300611int tda9887_tuner_init(struct i2c_client *c)
612{
Michael Krufkyb2083192007-05-29 22:54:06 -0300613 struct tda9887_priv *priv = NULL;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300614 struct tuner *t = i2c_get_clientdata(c);
615
Michael Krufkyb2083192007-05-29 22:54:06 -0300616 priv = kzalloc(sizeof(struct tda9887_priv), GFP_KERNEL);
617 if (priv == NULL)
618 return -ENOMEM;
619 t->priv = priv;
620
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300621 strlcpy(c->name, "tda9887", sizeof(c->name));
622
623 tda9887_info("tda988[5/6/7] found @ 0x%x (%s)\n", t->i2c.addr,
624 t->i2c.driver->driver.name);
625
Michael Krufky9af596e2007-06-06 16:15:48 -0300626 memcpy(&t->ops, &tda9887_tuner_ops, sizeof(struct tuner_operations));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
628 return 0;
629}
630
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631/*
632 * Overrides for Emacs so that we follow Linus's tabbing style.
633 * ---------------------------------------------------------------------------
634 * Local variables:
635 * c-basic-offset: 8
636 * End:
637 */