blob: be5387f11afb3de8d3c71da946b65d9ce5f8cd0c [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 Krufky8218b0b2007-06-26 13:12:08 -030012#include "tuner-driver.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070013
Mauro Carvalho Chehab674434c2005-12-12 00:37:28 -080014
Linus Torvalds1da177e2005-04-16 15:20:36 -070015/* Chips:
16 TDA9885 (PAL, NTSC)
17 TDA9886 (PAL, SECAM, NTSC)
18 TDA9887 (PAL, SECAM, NTSC, FM Radio)
19
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -030020 Used as part of several tuners
Linus Torvalds1da177e2005-04-16 15:20:36 -070021*/
22
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -030023#define tda9887_info(fmt, arg...) do {\
Hans Verkuil343f92c2006-06-25 11:02:02 -030024 printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -030025 i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
26#define tda9887_dbg(fmt, arg...) do {\
27 if (tuner_debug) \
Hans Verkuil343f92c2006-06-25 11:02:02 -030028 printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -030029 i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
Michael Krufkyb2083192007-05-29 22:54:06 -030031struct tda9887_priv {
Michael Krufkydb8a6952007-08-21 01:24:42 -030032 struct tuner_i2c_props i2c_props;
33
Michael Krufkyb2083192007-05-29 22:54:06 -030034 unsigned char data[4];
35};
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
37/* ---------------------------------------------------------------------- */
38
39#define UNSET (-1U)
Linus Torvalds1da177e2005-04-16 15:20:36 -070040
41struct tvnorm {
42 v4l2_std_id std;
43 char *name;
44 unsigned char b;
45 unsigned char c;
46 unsigned char e;
47};
48
Linus Torvalds1da177e2005-04-16 15:20:36 -070049/* ---------------------------------------------------------------------- */
50
51//
52// TDA defines
53//
54
55//// first reg (b)
56#define cVideoTrapBypassOFF 0x00 // bit b0
57#define cVideoTrapBypassON 0x01 // bit b0
58
59#define cAutoMuteFmInactive 0x00 // bit b1
60#define cAutoMuteFmActive 0x02 // bit b1
61
62#define cIntercarrier 0x00 // bit b2
63#define cQSS 0x04 // bit b2
64
65#define cPositiveAmTV 0x00 // bit b3:4
66#define cFmRadio 0x08 // bit b3:4
67#define cNegativeFmTV 0x10 // bit b3:4
68
69
70#define cForcedMuteAudioON 0x20 // bit b5
71#define cForcedMuteAudioOFF 0x00 // bit b5
72
73#define cOutputPort1Active 0x00 // bit b6
74#define cOutputPort1Inactive 0x40 // bit b6
75
76#define cOutputPort2Active 0x00 // bit b7
77#define cOutputPort2Inactive 0x80 // bit b7
78
79
80//// second reg (c)
81#define cDeemphasisOFF 0x00 // bit c5
82#define cDeemphasisON 0x20 // bit c5
83
84#define cDeemphasis75 0x00 // bit c6
85#define cDeemphasis50 0x40 // bit c6
86
87#define cAudioGain0 0x00 // bit c7
88#define cAudioGain6 0x80 // bit c7
89
Hans Verkuilf98c55e2006-01-09 15:25:18 -020090#define cTopMask 0x1f // bit c0:4
Hans Verkuilf5b01422006-06-25 15:37:29 -030091#define cTopDefault 0x10 // bit c0:4
Linus Torvalds1da177e2005-04-16 15:20:36 -070092
93//// third reg (e)
94#define cAudioIF_4_5 0x00 // bit e0:1
95#define cAudioIF_5_5 0x01 // bit e0:1
96#define cAudioIF_6_0 0x02 // bit e0:1
97#define cAudioIF_6_5 0x03 // bit e0:1
98
99
Trent Piepho5e082f12007-08-03 18:32:38 -0300100#define cVideoIFMask 0x1c // bit e2:4
101/* Video IF selection in TV Mode (bit B3=0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102#define cVideoIF_58_75 0x00 // bit e2:4
103#define cVideoIF_45_75 0x04 // bit e2:4
104#define cVideoIF_38_90 0x08 // bit e2:4
105#define cVideoIF_38_00 0x0C // bit e2:4
106#define cVideoIF_33_90 0x10 // bit e2:4
107#define cVideoIF_33_40 0x14 // bit e2:4
108#define cRadioIF_45_75 0x18 // bit e2:4
109#define cRadioIF_38_90 0x1C // bit e2:4
110
Trent Piepho5e082f12007-08-03 18:32:38 -0300111/* IF1 selection in Radio Mode (bit B3=1) */
112#define cRadioIF_33_30 0x00 // bit e2,4 (also 0x10,0x14)
113#define cRadioIF_41_30 0x04 // bit e2,4
114
115/* Output of AFC pin in radio mode when bit E7=1 */
116#define cRadioAGC_SIF 0x00 // bit e3
117#define cRadioAGC_FM 0x08 // bit e3
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
119#define cTunerGainNormal 0x00 // bit e5
120#define cTunerGainLow 0x20 // bit e5
121
122#define cGating_18 0x00 // bit e6
123#define cGating_36 0x40 // bit e6
124
125#define cAgcOutON 0x80 // bit e7
126#define cAgcOutOFF 0x00 // bit e7
127
128/* ---------------------------------------------------------------------- */
129
130static struct tvnorm tvnorms[] = {
131 {
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200132 .std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N,
133 .name = "PAL-BGHN",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 .b = ( cNegativeFmTV |
135 cQSS ),
136 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200137 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300138 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200139 .e = ( cGating_36 |
140 cAudioIF_5_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 cVideoIF_38_90 ),
142 },{
143 .std = V4L2_STD_PAL_I,
144 .name = "PAL-I",
145 .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_6_0 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 cVideoIF_38_90 ),
153 },{
154 .std = V4L2_STD_PAL_DK,
155 .name = "PAL-DK",
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_5 |
163 cVideoIF_38_90 ),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 },{
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200165 .std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc,
166 .name = "PAL-M/Nc",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 .b = ( cNegativeFmTV |
168 cQSS ),
169 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200170 cDeemphasis75 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300171 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200172 .e = ( cGating_36 |
173 cAudioIF_4_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 cVideoIF_45_75 ),
175 },{
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200176 .std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H,
177 .name = "SECAM-BGH",
178 .b = ( cPositiveAmTV |
179 cQSS ),
Hans Verkuilf5b01422006-06-25 15:37:29 -0300180 .c = ( cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200181 .e = ( cGating_36 |
182 cAudioIF_5_5 |
183 cVideoIF_38_90 ),
184 },{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 .std = V4L2_STD_SECAM_L,
186 .name = "SECAM-L",
187 .b = ( cPositiveAmTV |
188 cQSS ),
Hans Verkuilf5b01422006-06-25 15:37:29 -0300189 .c = ( cTopDefault),
Nickolay V. Shmyrev3375c392005-11-08 21:37:05 -0800190 .e = ( cGating_36 |
Nickolay V. Shmyrev5f7591c2005-11-08 21:37:04 -0800191 cAudioIF_6_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 cVideoIF_38_90 ),
193 },{
Mauro Carvalho Chehabf3c59872006-01-09 15:25:00 -0200194 .std = V4L2_STD_SECAM_LC,
195 .name = "SECAM-L'",
196 .b = ( cOutputPort2Inactive |
197 cPositiveAmTV |
198 cQSS ),
Hans Verkuilf5b01422006-06-25 15:37:29 -0300199 .c = ( cTopDefault),
Mauro Carvalho Chehabf3c59872006-01-09 15:25:00 -0200200 .e = ( cGating_36 |
201 cAudioIF_6_5 |
202 cVideoIF_33_90 ),
203 },{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 .std = V4L2_STD_SECAM_DK,
205 .name = "SECAM-DK",
206 .b = ( cNegativeFmTV |
207 cQSS ),
208 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200209 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300210 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200211 .e = ( cGating_36 |
212 cAudioIF_6_5 |
213 cVideoIF_38_90 ),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 },{
Hans Verkuil0dfd8122006-02-07 06:45:34 -0200215 .std = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 .name = "NTSC-M",
217 .b = ( cNegativeFmTV |
218 cQSS ),
219 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200220 cDeemphasis75 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300221 cTopDefault),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 .e = ( cGating_36 |
223 cAudioIF_4_5 |
224 cVideoIF_45_75 ),
225 },{
226 .std = V4L2_STD_NTSC_M_JP,
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200227 .name = "NTSC-M-JP",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 .b = ( cNegativeFmTV |
229 cQSS ),
230 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200231 cDeemphasis50 |
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_58_75 ),
236 }
237};
238
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700239static struct tvnorm radio_stereo = {
240 .name = "Radio Stereo",
241 .b = ( cFmRadio |
242 cQSS ),
243 .c = ( cDeemphasisOFF |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200244 cAudioGain6 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300245 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200246 .e = ( cTunerGainLow |
247 cAudioIF_5_5 |
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700248 cRadioIF_38_90 ),
249};
250
251static struct tvnorm radio_mono = {
252 .name = "Radio Mono",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 .b = ( cFmRadio |
254 cQSS ),
255 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200256 cDeemphasis75 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300257 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200258 .e = ( cTunerGainLow |
259 cAudioIF_5_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 cRadioIF_38_90 ),
261};
262
263/* ---------------------------------------------------------------------- */
264
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300265static void dump_read_message(struct tuner *t, unsigned char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266{
267 static char *afc[16] = {
268 "- 12.5 kHz",
269 "- 37.5 kHz",
270 "- 62.5 kHz",
271 "- 87.5 kHz",
272 "-112.5 kHz",
273 "-137.5 kHz",
274 "-162.5 kHz",
275 "-187.5 kHz [min]",
276 "+187.5 kHz [max]",
277 "+162.5 kHz",
278 "+137.5 kHz",
279 "+112.5 kHz",
280 "+ 87.5 kHz",
281 "+ 62.5 kHz",
282 "+ 37.5 kHz",
283 "+ 12.5 kHz",
284 };
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800285 tda9887_info("read: 0x%2x\n", buf[0]);
286 tda9887_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no");
287 tda9887_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]);
288 tda9887_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low");
289 tda9887_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out");
290 tda9887_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291}
292
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300293static void dump_write_message(struct tuner *t, unsigned char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294{
295 static char *sound[4] = {
296 "AM/TV",
297 "FM/radio",
298 "FM/TV",
299 "FM/radio"
300 };
301 static char *adjust[32] = {
302 "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9",
303 "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1",
304 "0", "+1", "+2", "+3", "+4", "+5", "+6", "+7",
305 "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15"
306 };
307 static char *deemph[4] = {
308 "no", "no", "75", "50"
309 };
310 static char *carrier[4] = {
311 "4.5 MHz",
312 "5.5 MHz",
313 "6.0 MHz",
314 "6.5 MHz / AM"
315 };
316 static char *vif[8] = {
317 "58.75 MHz",
318 "45.75 MHz",
319 "38.9 MHz",
320 "38.0 MHz",
321 "33.9 MHz",
322 "33.4 MHz",
323 "45.75 MHz + pin13",
324 "38.9 MHz + pin13",
325 };
326 static char *rif[4] = {
327 "44 MHz",
328 "52 MHz",
329 "52 MHz",
330 "44 MHz",
331 };
332
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800333 tda9887_info("write: byte B 0x%02x\n",buf[1]);
334 tda9887_info(" B0 video mode : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 (buf[1] & 0x01) ? "video trap" : "sound trap");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800336 tda9887_info(" B1 auto mute fm : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 (buf[1] & 0x02) ? "yes" : "no");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800338 tda9887_info(" B2 carrier mode : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 (buf[1] & 0x04) ? "QSS" : "Intercarrier");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800340 tda9887_info(" B3-4 tv sound/radio : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 sound[(buf[1] & 0x18) >> 3]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800342 tda9887_info(" B5 force mute audio: %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 (buf[1] & 0x20) ? "yes" : "no");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800344 tda9887_info(" B6 output port 1 : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 (buf[1] & 0x40) ? "high (inactive)" : "low (active)");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800346 tda9887_info(" B7 output port 2 : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 (buf[1] & 0x80) ? "high (inactive)" : "low (active)");
348
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800349 tda9887_info("write: byte C 0x%02x\n",buf[2]);
350 tda9887_info(" C0-4 top adjustment : %s dB\n", adjust[buf[2] & 0x1f]);
351 tda9887_info(" C5-6 de-emphasis : %s\n", deemph[(buf[2] & 0x60) >> 5]);
352 tda9887_info(" C7 audio gain : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 (buf[2] & 0x80) ? "-6" : "0");
354
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800355 tda9887_info("write: byte E 0x%02x\n",buf[3]);
356 tda9887_info(" E0-1 sound carrier : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 carrier[(buf[3] & 0x03)]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800358 tda9887_info(" E6 l pll gating : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 (buf[3] & 0x40) ? "36" : "13");
360
361 if (buf[1] & 0x08) {
362 /* radio */
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800363 tda9887_info(" E2-4 video if : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 rif[(buf[3] & 0x0c) >> 2]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800365 tda9887_info(" E7 vif agc output : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 (buf[3] & 0x80)
367 ? ((buf[3] & 0x10) ? "fm-agc radio" : "sif-agc radio")
368 : "fm radio carrier afc");
369 } else {
370 /* video */
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800371 tda9887_info(" E2-4 video if : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 vif[(buf[3] & 0x1c) >> 2]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800373 tda9887_info(" E5 tuner gain : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 (buf[3] & 0x80)
375 ? ((buf[3] & 0x20) ? "external" : "normal")
376 : ((buf[3] & 0x20) ? "minimum" : "normal"));
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800377 tda9887_info(" E7 vif agc output : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 (buf[3] & 0x80)
379 ? ((buf[3] & 0x20)
380 ? "pin3 port, pin22 vif agc out"
381 : "pin22 port, pin3 vif acg ext in")
382 : "pin3+pin22 port");
383 }
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800384 tda9887_info("--\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385}
386
387/* ---------------------------------------------------------------------- */
388
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300389static int tda9887_set_tvnorm(struct tuner *t, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390{
391 struct tvnorm *norm = NULL;
392 int i;
393
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300394 if (t->mode == V4L2_TUNER_RADIO) {
395 if (t->audmode == V4L2_TUNER_MODE_MONO)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700396 norm = &radio_mono;
397 else
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700398 norm = &radio_stereo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 } else {
400 for (i = 0; i < ARRAY_SIZE(tvnorms); i++) {
401 if (tvnorms[i].std & t->std) {
402 norm = tvnorms+i;
403 break;
404 }
405 }
406 }
407 if (NULL == norm) {
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800408 tda9887_dbg("Unsupported tvnorm entry - audio muted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 return -1;
410 }
411
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800412 tda9887_dbg("configure for: %s\n",norm->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 buf[1] = norm->b;
414 buf[2] = norm->c;
415 buf[3] = norm->e;
416 return 0;
417}
418
419static unsigned int port1 = UNSET;
420static unsigned int port2 = UNSET;
421static unsigned int qss = UNSET;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200422static unsigned int adjust = UNSET;
423
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424module_param(port1, int, 0644);
425module_param(port2, int, 0644);
426module_param(qss, int, 0644);
427module_param(adjust, int, 0644);
428
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300429static int tda9887_set_insmod(struct tuner *t, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430{
431 if (UNSET != port1) {
432 if (port1)
433 buf[1] |= cOutputPort1Inactive;
434 else
435 buf[1] &= ~cOutputPort1Inactive;
436 }
437 if (UNSET != port2) {
438 if (port2)
439 buf[1] |= cOutputPort2Inactive;
440 else
441 buf[1] &= ~cOutputPort2Inactive;
442 }
443
444 if (UNSET != qss) {
445 if (qss)
446 buf[1] |= cQSS;
447 else
448 buf[1] &= ~cQSS;
449 }
450
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200451 if (adjust >= 0x00 && adjust < 0x20) {
452 buf[2] &= ~cTopMask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 buf[2] |= adjust;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200454 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 return 0;
456}
457
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300458static int tda9887_set_config(struct tuner *t, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459{
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300460 if (t->tda9887_config & TDA9887_PORT1_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 buf[1] &= ~cOutputPort1Inactive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300462 if (t->tda9887_config & TDA9887_PORT1_INACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 buf[1] |= cOutputPort1Inactive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300464 if (t->tda9887_config & TDA9887_PORT2_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 buf[1] &= ~cOutputPort2Inactive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300466 if (t->tda9887_config & TDA9887_PORT2_INACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 buf[1] |= cOutputPort2Inactive;
468
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300469 if (t->tda9887_config & TDA9887_QSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 buf[1] |= cQSS;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300471 if (t->tda9887_config & TDA9887_INTERCARRIER)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 buf[1] &= ~cQSS;
473
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300474 if (t->tda9887_config & TDA9887_AUTOMUTE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 buf[1] |= cAutoMuteFmActive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300476 if (t->tda9887_config & TDA9887_DEEMPHASIS_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 buf[2] &= ~0x60;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300478 switch (t->tda9887_config & TDA9887_DEEMPHASIS_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 case TDA9887_DEEMPHASIS_NONE:
480 buf[2] |= cDeemphasisOFF;
481 break;
482 case TDA9887_DEEMPHASIS_50:
483 buf[2] |= cDeemphasisON | cDeemphasis50;
484 break;
485 case TDA9887_DEEMPHASIS_75:
486 buf[2] |= cDeemphasisON | cDeemphasis75;
487 break;
488 }
489 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300490 if (t->tda9887_config & TDA9887_TOP_SET) {
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200491 buf[2] &= ~cTopMask;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300492 buf[2] |= (t->tda9887_config >> 8) & cTopMask;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200493 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300494 if ((t->tda9887_config & TDA9887_INTERCARRIER_NTSC) && (t->std & V4L2_STD_NTSC))
Nickolay V. Shmyrev3ae1adc2005-11-08 21:37:39 -0800495 buf[1] &= ~cQSS;
Trent Piephod7304de2006-08-24 22:43:45 -0300496 if (t->tda9887_config & TDA9887_GATING_18)
497 buf[3] &= ~cGating_36;
Mauro Carvalho Chehabcefccc82006-12-04 08:31:35 -0300498
Trent Piepho5e082f12007-08-03 18:32:38 -0300499 if (t->mode == V4L2_TUNER_RADIO) {
500 if (t->tda9887_config & TDA9887_RIF_41_3) {
501 buf[3] &= ~cVideoIFMask;
502 buf[3] |= cRadioIF_41_30;
503 }
504 if (t->tda9887_config & TDA9887_GAIN_NORMAL)
505 buf[3] &= ~cTunerGainLow;
Mauro Carvalho Chehabcefccc82006-12-04 08:31:35 -0300506 }
507
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 return 0;
509}
510
511/* ---------------------------------------------------------------------- */
512
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300513static int tda9887_status(struct tuner *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514{
Michael Krufkydb8a6952007-08-21 01:24:42 -0300515 struct tda9887_priv *priv = t->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 unsigned char buf[1];
517 int rc;
518
519 memset(buf,0,sizeof(buf));
Michael Krufkydb8a6952007-08-21 01:24:42 -0300520 if (1 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props,buf,1)))
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800521 tda9887_info("i2c i/o error: rc == %d (should be 1)\n",rc);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800522 dump_read_message(t, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 return 0;
524}
525
Michael Krufkydb8a6952007-08-21 01:24:42 -0300526static void tda9887_configure(struct tuner *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527{
Michael Krufkyb2083192007-05-29 22:54:06 -0300528 struct tda9887_priv *priv = t->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 int rc;
530
Michael Krufkyb2083192007-05-29 22:54:06 -0300531 memset(priv->data,0,sizeof(priv->data));
532 tda9887_set_tvnorm(t,priv->data);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700533
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200534 /* A note on the port settings:
535 These settings tend to depend on the specifics of the board.
536 By default they are set to inactive (bit value 1) by this driver,
537 overwriting any changes made by the tvnorm. This means that it
538 is the responsibility of the module using the tda9887 to set
539 these values in case of changes in the tvnorm.
540 In many cases port 2 should be made active (0) when selecting
541 SECAM-L, and port 2 should remain inactive (1) for SECAM-L'.
542
543 For the other standards the tda9887 application note says that
544 the ports should be set to active (0), but, again, that may
545 differ depending on the precise hardware configuration.
546 */
Michael Krufkyb2083192007-05-29 22:54:06 -0300547 priv->data[1] |= cOutputPort1Inactive;
548 priv->data[1] |= cOutputPort2Inactive;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700549
Michael Krufkyb2083192007-05-29 22:54:06 -0300550 tda9887_set_config(t,priv->data);
551 tda9887_set_insmod(t,priv->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700553 if (t->mode == T_STANDBY) {
Michael Krufkyb2083192007-05-29 22:54:06 -0300554 priv->data[1] |= cForcedMuteAudioON;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700555 }
556
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800557 tda9887_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",
Michael Krufkyb2083192007-05-29 22:54:06 -0300558 priv->data[1],priv->data[2],priv->data[3]);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300559 if (tuner_debug > 1)
Michael Krufkyb2083192007-05-29 22:54:06 -0300560 dump_write_message(t, priv->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
Michael Krufkydb8a6952007-08-21 01:24:42 -0300562 if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,priv->data,4)))
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800563 tda9887_info("i2c i/o error: rc == %d (should be 4)\n",rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300565 if (tuner_debug > 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 msleep_interruptible(1000);
567 tda9887_status(t);
568 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569}
570
571/* ---------------------------------------------------------------------- */
572
Michael Krufkydb8a6952007-08-21 01:24:42 -0300573static void tda9887_tuner_status(struct tuner *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574{
Michael Krufkyb2083192007-05-29 22:54:06 -0300575 struct tda9887_priv *priv = t->priv;
576 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 -0300577}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578
Michael Krufkydb8a6952007-08-21 01:24:42 -0300579static int tda9887_get_afc(struct tuner *t)
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300580{
Michael Krufkydb8a6952007-08-21 01:24:42 -0300581 struct tda9887_priv *priv = t->priv;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300582 static int AFC_BITS_2_kHz[] = {
583 -12500, -37500, -62500, -97500,
584 -112500, -137500, -162500, -187500,
585 187500, 162500, 137500, 112500,
586 97500 , 62500, 37500 , 12500
587 };
588 int afc=0;
589 __u8 reg = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590
Michael Krufkydb8a6952007-08-21 01:24:42 -0300591 if (1 == tuner_i2c_xfer_recv(&priv->i2c_props,&reg,1))
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300592 afc = AFC_BITS_2_kHz[(reg>>1)&0x0f];
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700593
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300594 return afc;
595}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700596
Michael Krufkydb8a6952007-08-21 01:24:42 -0300597static void tda9887_standby(struct tuner *t)
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300598{
Michael Krufkydb8a6952007-08-21 01:24:42 -0300599 tda9887_configure(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300600}
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800601
Michael Krufkydb8a6952007-08-21 01:24:42 -0300602static void tda9887_set_freq(struct tuner *t, unsigned int freq)
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300603{
Michael Krufkydb8a6952007-08-21 01:24:42 -0300604 tda9887_configure(t);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300605}
606
Michael Krufkydb8a6952007-08-21 01:24:42 -0300607static void tda9887_release(struct tuner *t)
Michael Krufky024cf532007-06-04 15:20:11 -0300608{
Michael Krufky024cf532007-06-04 15:20:11 -0300609 kfree(t->priv);
610 t->priv = NULL;
611}
612
Michael Krufky9af596e2007-06-06 16:15:48 -0300613static struct tuner_operations tda9887_tuner_ops = {
614 .set_tv_freq = tda9887_set_freq,
615 .set_radio_freq = tda9887_set_freq,
616 .standby = tda9887_standby,
617 .tuner_status = tda9887_tuner_status,
618 .get_afc = tda9887_get_afc,
619 .release = tda9887_release,
620};
621
Michael Krufkydb8a6952007-08-21 01:24:42 -0300622int tda9887_tuner_init(struct tuner *t)
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300623{
Michael Krufkyb2083192007-05-29 22:54:06 -0300624 struct tda9887_priv *priv = NULL;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300625
Michael Krufkyb2083192007-05-29 22:54:06 -0300626 priv = kzalloc(sizeof(struct tda9887_priv), GFP_KERNEL);
627 if (priv == NULL)
628 return -ENOMEM;
629 t->priv = priv;
630
Michael Krufkydb8a6952007-08-21 01:24:42 -0300631 priv->i2c_props.addr = t->i2c.addr;
632 priv->i2c_props.adap = t->i2c.adapter;
633
634 strlcpy(t->i2c.name, "tda9887", sizeof(t->i2c.name));
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300635
636 tda9887_info("tda988[5/6/7] found @ 0x%x (%s)\n", t->i2c.addr,
637 t->i2c.driver->driver.name);
638
Michael Krufky9af596e2007-06-06 16:15:48 -0300639 memcpy(&t->ops, &tda9887_tuner_ops, sizeof(struct tuner_operations));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640
641 return 0;
642}
643
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644/*
645 * Overrides for Emacs so that we follow Linus's tabbing style.
646 * ---------------------------------------------------------------------------
647 * Local variables:
648 * c-basic-offset: 8
649 * End:
650 */