blob: e8597c10aa4c4e1a9eff7e3d487e5721e5ebb3b7 [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006#include <linux/init.h>
7#include <linux/errno.h>
8#include <linux/slab.h>
9#include <linux/delay.h>
Michael Krufkyffbb8072007-08-21 01:14:12 -030010#include <linux/videodev.h>
Michael Krufky5e453dc2006-01-09 15:32:31 -020011#include <media/v4l2-common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <media/tuner.h>
Michael Krufky8218b0b2007-06-26 13:12:08 -030013#include "tuner-driver.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
Trent Piepho5e082f12007-08-03 18:32:38 -030099#define cVideoIFMask 0x1c // bit e2:4
100/* Video IF selection in TV Mode (bit B3=0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101#define cVideoIF_58_75 0x00 // bit e2:4
102#define cVideoIF_45_75 0x04 // bit e2:4
103#define cVideoIF_38_90 0x08 // bit e2:4
104#define cVideoIF_38_00 0x0C // bit e2:4
105#define cVideoIF_33_90 0x10 // bit e2:4
106#define cVideoIF_33_40 0x14 // bit e2:4
107#define cRadioIF_45_75 0x18 // bit e2:4
108#define cRadioIF_38_90 0x1C // bit e2:4
109
Trent Piepho5e082f12007-08-03 18:32:38 -0300110/* IF1 selection in Radio Mode (bit B3=1) */
111#define cRadioIF_33_30 0x00 // bit e2,4 (also 0x10,0x14)
112#define cRadioIF_41_30 0x04 // bit e2,4
113
114/* Output of AFC pin in radio mode when bit E7=1 */
115#define cRadioAGC_SIF 0x00 // bit e3
116#define cRadioAGC_FM 0x08 // bit e3
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
118#define cTunerGainNormal 0x00 // bit e5
119#define cTunerGainLow 0x20 // bit e5
120
121#define cGating_18 0x00 // bit e6
122#define cGating_36 0x40 // bit e6
123
124#define cAgcOutON 0x80 // bit e7
125#define cAgcOutOFF 0x00 // bit e7
126
127/* ---------------------------------------------------------------------- */
128
129static struct tvnorm tvnorms[] = {
130 {
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200131 .std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N,
132 .name = "PAL-BGHN",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 .b = ( cNegativeFmTV |
134 cQSS ),
135 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200136 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300137 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200138 .e = ( cGating_36 |
139 cAudioIF_5_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 cVideoIF_38_90 ),
141 },{
142 .std = V4L2_STD_PAL_I,
143 .name = "PAL-I",
144 .b = ( cNegativeFmTV |
145 cQSS ),
146 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200147 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300148 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200149 .e = ( cGating_36 |
150 cAudioIF_6_0 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 cVideoIF_38_90 ),
152 },{
153 .std = V4L2_STD_PAL_DK,
154 .name = "PAL-DK",
155 .b = ( cNegativeFmTV |
156 cQSS ),
157 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200158 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300159 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200160 .e = ( cGating_36 |
161 cAudioIF_6_5 |
162 cVideoIF_38_90 ),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 },{
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200164 .std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc,
165 .name = "PAL-M/Nc",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 .b = ( cNegativeFmTV |
167 cQSS ),
168 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200169 cDeemphasis75 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300170 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200171 .e = ( cGating_36 |
172 cAudioIF_4_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 cVideoIF_45_75 ),
174 },{
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200175 .std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H,
176 .name = "SECAM-BGH",
177 .b = ( cPositiveAmTV |
178 cQSS ),
Hans Verkuilf5b01422006-06-25 15:37:29 -0300179 .c = ( cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200180 .e = ( cGating_36 |
181 cAudioIF_5_5 |
182 cVideoIF_38_90 ),
183 },{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 .std = V4L2_STD_SECAM_L,
185 .name = "SECAM-L",
186 .b = ( cPositiveAmTV |
187 cQSS ),
Hans Verkuilf5b01422006-06-25 15:37:29 -0300188 .c = ( cTopDefault),
Nickolay V. Shmyrev3375c392005-11-08 21:37:05 -0800189 .e = ( cGating_36 |
Nickolay V. Shmyrev5f7591c2005-11-08 21:37:04 -0800190 cAudioIF_6_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 cVideoIF_38_90 ),
192 },{
Mauro Carvalho Chehabf3c59872006-01-09 15:25:00 -0200193 .std = V4L2_STD_SECAM_LC,
194 .name = "SECAM-L'",
195 .b = ( cOutputPort2Inactive |
196 cPositiveAmTV |
197 cQSS ),
Hans Verkuilf5b01422006-06-25 15:37:29 -0300198 .c = ( cTopDefault),
Mauro Carvalho Chehabf3c59872006-01-09 15:25:00 -0200199 .e = ( cGating_36 |
200 cAudioIF_6_5 |
201 cVideoIF_33_90 ),
202 },{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 .std = V4L2_STD_SECAM_DK,
204 .name = "SECAM-DK",
205 .b = ( cNegativeFmTV |
206 cQSS ),
207 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200208 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300209 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200210 .e = ( cGating_36 |
211 cAudioIF_6_5 |
212 cVideoIF_38_90 ),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 },{
Hans Verkuil0dfd8122006-02-07 06:45:34 -0200214 .std = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 .name = "NTSC-M",
216 .b = ( cNegativeFmTV |
217 cQSS ),
218 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200219 cDeemphasis75 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300220 cTopDefault),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 .e = ( cGating_36 |
222 cAudioIF_4_5 |
223 cVideoIF_45_75 ),
224 },{
225 .std = V4L2_STD_NTSC_M_JP,
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200226 .name = "NTSC-M-JP",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 .b = ( cNegativeFmTV |
228 cQSS ),
229 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200230 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300231 cTopDefault),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 .e = ( cGating_36 |
233 cAudioIF_4_5 |
234 cVideoIF_58_75 ),
235 }
236};
237
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700238static struct tvnorm radio_stereo = {
239 .name = "Radio Stereo",
240 .b = ( cFmRadio |
241 cQSS ),
242 .c = ( cDeemphasisOFF |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200243 cAudioGain6 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300244 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200245 .e = ( cTunerGainLow |
246 cAudioIF_5_5 |
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700247 cRadioIF_38_90 ),
248};
249
250static struct tvnorm radio_mono = {
251 .name = "Radio Mono",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 .b = ( cFmRadio |
253 cQSS ),
254 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200255 cDeemphasis75 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300256 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200257 .e = ( cTunerGainLow |
258 cAudioIF_5_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 cRadioIF_38_90 ),
260};
261
262/* ---------------------------------------------------------------------- */
263
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300264static void dump_read_message(struct tuner *t, unsigned char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265{
266 static char *afc[16] = {
267 "- 12.5 kHz",
268 "- 37.5 kHz",
269 "- 62.5 kHz",
270 "- 87.5 kHz",
271 "-112.5 kHz",
272 "-137.5 kHz",
273 "-162.5 kHz",
274 "-187.5 kHz [min]",
275 "+187.5 kHz [max]",
276 "+162.5 kHz",
277 "+137.5 kHz",
278 "+112.5 kHz",
279 "+ 87.5 kHz",
280 "+ 62.5 kHz",
281 "+ 37.5 kHz",
282 "+ 12.5 kHz",
283 };
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800284 tda9887_info("read: 0x%2x\n", buf[0]);
285 tda9887_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no");
286 tda9887_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]);
287 tda9887_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low");
288 tda9887_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out");
289 tda9887_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290}
291
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300292static void dump_write_message(struct tuner *t, unsigned char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293{
294 static char *sound[4] = {
295 "AM/TV",
296 "FM/radio",
297 "FM/TV",
298 "FM/radio"
299 };
300 static char *adjust[32] = {
301 "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9",
302 "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1",
303 "0", "+1", "+2", "+3", "+4", "+5", "+6", "+7",
304 "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15"
305 };
306 static char *deemph[4] = {
307 "no", "no", "75", "50"
308 };
309 static char *carrier[4] = {
310 "4.5 MHz",
311 "5.5 MHz",
312 "6.0 MHz",
313 "6.5 MHz / AM"
314 };
315 static char *vif[8] = {
316 "58.75 MHz",
317 "45.75 MHz",
318 "38.9 MHz",
319 "38.0 MHz",
320 "33.9 MHz",
321 "33.4 MHz",
322 "45.75 MHz + pin13",
323 "38.9 MHz + pin13",
324 };
325 static char *rif[4] = {
326 "44 MHz",
327 "52 MHz",
328 "52 MHz",
329 "44 MHz",
330 };
331
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800332 tda9887_info("write: byte B 0x%02x\n",buf[1]);
333 tda9887_info(" B0 video mode : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 (buf[1] & 0x01) ? "video trap" : "sound trap");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800335 tda9887_info(" B1 auto mute fm : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 (buf[1] & 0x02) ? "yes" : "no");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800337 tda9887_info(" B2 carrier mode : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 (buf[1] & 0x04) ? "QSS" : "Intercarrier");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800339 tda9887_info(" B3-4 tv sound/radio : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 sound[(buf[1] & 0x18) >> 3]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800341 tda9887_info(" B5 force mute audio: %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 (buf[1] & 0x20) ? "yes" : "no");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800343 tda9887_info(" B6 output port 1 : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 (buf[1] & 0x40) ? "high (inactive)" : "low (active)");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800345 tda9887_info(" B7 output port 2 : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 (buf[1] & 0x80) ? "high (inactive)" : "low (active)");
347
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800348 tda9887_info("write: byte C 0x%02x\n",buf[2]);
349 tda9887_info(" C0-4 top adjustment : %s dB\n", adjust[buf[2] & 0x1f]);
350 tda9887_info(" C5-6 de-emphasis : %s\n", deemph[(buf[2] & 0x60) >> 5]);
351 tda9887_info(" C7 audio gain : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 (buf[2] & 0x80) ? "-6" : "0");
353
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800354 tda9887_info("write: byte E 0x%02x\n",buf[3]);
355 tda9887_info(" E0-1 sound carrier : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 carrier[(buf[3] & 0x03)]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800357 tda9887_info(" E6 l pll gating : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 (buf[3] & 0x40) ? "36" : "13");
359
360 if (buf[1] & 0x08) {
361 /* radio */
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800362 tda9887_info(" E2-4 video if : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 rif[(buf[3] & 0x0c) >> 2]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800364 tda9887_info(" E7 vif agc output : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 (buf[3] & 0x80)
366 ? ((buf[3] & 0x10) ? "fm-agc radio" : "sif-agc radio")
367 : "fm radio carrier afc");
368 } else {
369 /* video */
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800370 tda9887_info(" E2-4 video if : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 vif[(buf[3] & 0x1c) >> 2]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800372 tda9887_info(" E5 tuner gain : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 (buf[3] & 0x80)
374 ? ((buf[3] & 0x20) ? "external" : "normal")
375 : ((buf[3] & 0x20) ? "minimum" : "normal"));
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800376 tda9887_info(" E7 vif agc output : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 (buf[3] & 0x80)
378 ? ((buf[3] & 0x20)
379 ? "pin3 port, pin22 vif agc out"
380 : "pin22 port, pin3 vif acg ext in")
381 : "pin3+pin22 port");
382 }
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800383 tda9887_info("--\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384}
385
386/* ---------------------------------------------------------------------- */
387
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300388static int tda9887_set_tvnorm(struct tuner *t, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389{
390 struct tvnorm *norm = NULL;
391 int i;
392
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300393 if (t->mode == V4L2_TUNER_RADIO) {
394 if (t->audmode == V4L2_TUNER_MODE_MONO)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700395 norm = &radio_mono;
396 else
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700397 norm = &radio_stereo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 } else {
399 for (i = 0; i < ARRAY_SIZE(tvnorms); i++) {
400 if (tvnorms[i].std & t->std) {
401 norm = tvnorms+i;
402 break;
403 }
404 }
405 }
406 if (NULL == norm) {
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800407 tda9887_dbg("Unsupported tvnorm entry - audio muted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 return -1;
409 }
410
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800411 tda9887_dbg("configure for: %s\n",norm->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 buf[1] = norm->b;
413 buf[2] = norm->c;
414 buf[3] = norm->e;
415 return 0;
416}
417
418static unsigned int port1 = UNSET;
419static unsigned int port2 = UNSET;
420static unsigned int qss = UNSET;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200421static unsigned int adjust = UNSET;
422
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423module_param(port1, int, 0644);
424module_param(port2, int, 0644);
425module_param(qss, int, 0644);
426module_param(adjust, int, 0644);
427
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300428static int tda9887_set_insmod(struct tuner *t, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429{
430 if (UNSET != port1) {
431 if (port1)
432 buf[1] |= cOutputPort1Inactive;
433 else
434 buf[1] &= ~cOutputPort1Inactive;
435 }
436 if (UNSET != port2) {
437 if (port2)
438 buf[1] |= cOutputPort2Inactive;
439 else
440 buf[1] &= ~cOutputPort2Inactive;
441 }
442
443 if (UNSET != qss) {
444 if (qss)
445 buf[1] |= cQSS;
446 else
447 buf[1] &= ~cQSS;
448 }
449
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200450 if (adjust >= 0x00 && adjust < 0x20) {
451 buf[2] &= ~cTopMask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 buf[2] |= adjust;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200453 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 return 0;
455}
456
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300457static int tda9887_set_config(struct tuner *t, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458{
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300459 if (t->tda9887_config & TDA9887_PORT1_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 buf[1] &= ~cOutputPort1Inactive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300461 if (t->tda9887_config & TDA9887_PORT1_INACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 buf[1] |= cOutputPort1Inactive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300463 if (t->tda9887_config & TDA9887_PORT2_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 buf[1] &= ~cOutputPort2Inactive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300465 if (t->tda9887_config & TDA9887_PORT2_INACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 buf[1] |= cOutputPort2Inactive;
467
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300468 if (t->tda9887_config & TDA9887_QSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 buf[1] |= cQSS;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300470 if (t->tda9887_config & TDA9887_INTERCARRIER)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 buf[1] &= ~cQSS;
472
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300473 if (t->tda9887_config & TDA9887_AUTOMUTE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 buf[1] |= cAutoMuteFmActive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300475 if (t->tda9887_config & TDA9887_DEEMPHASIS_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 buf[2] &= ~0x60;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300477 switch (t->tda9887_config & TDA9887_DEEMPHASIS_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 case TDA9887_DEEMPHASIS_NONE:
479 buf[2] |= cDeemphasisOFF;
480 break;
481 case TDA9887_DEEMPHASIS_50:
482 buf[2] |= cDeemphasisON | cDeemphasis50;
483 break;
484 case TDA9887_DEEMPHASIS_75:
485 buf[2] |= cDeemphasisON | cDeemphasis75;
486 break;
487 }
488 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300489 if (t->tda9887_config & TDA9887_TOP_SET) {
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200490 buf[2] &= ~cTopMask;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300491 buf[2] |= (t->tda9887_config >> 8) & cTopMask;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200492 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300493 if ((t->tda9887_config & TDA9887_INTERCARRIER_NTSC) && (t->std & V4L2_STD_NTSC))
Nickolay V. Shmyrev3ae1adc2005-11-08 21:37:39 -0800494 buf[1] &= ~cQSS;
Trent Piephod7304de2006-08-24 22:43:45 -0300495 if (t->tda9887_config & TDA9887_GATING_18)
496 buf[3] &= ~cGating_36;
Mauro Carvalho Chehabcefccc82006-12-04 08:31:35 -0300497
Trent Piepho5e082f12007-08-03 18:32:38 -0300498 if (t->mode == V4L2_TUNER_RADIO) {
499 if (t->tda9887_config & TDA9887_RIF_41_3) {
500 buf[3] &= ~cVideoIFMask;
501 buf[3] |= cRadioIF_41_30;
502 }
503 if (t->tda9887_config & TDA9887_GAIN_NORMAL)
504 buf[3] &= ~cTunerGainLow;
Mauro Carvalho Chehabcefccc82006-12-04 08:31:35 -0300505 }
506
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 return 0;
508}
509
510/* ---------------------------------------------------------------------- */
511
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300512static int tda9887_status(struct tuner *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513{
514 unsigned char buf[1];
515 int rc;
516
517 memset(buf,0,sizeof(buf));
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300518 if (1 != (rc = i2c_master_recv(&t->i2c,buf,1)))
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800519 tda9887_info("i2c i/o error: rc == %d (should be 1)\n",rc);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800520 dump_read_message(t, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 return 0;
522}
523
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300524static void tda9887_configure(struct i2c_client *client)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525{
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300526 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkyb2083192007-05-29 22:54:06 -0300527 struct tda9887_priv *priv = t->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 int rc;
529
Michael Krufkyb2083192007-05-29 22:54:06 -0300530 memset(priv->data,0,sizeof(priv->data));
531 tda9887_set_tvnorm(t,priv->data);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700532
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200533 /* A note on the port settings:
534 These settings tend to depend on the specifics of the board.
535 By default they are set to inactive (bit value 1) by this driver,
536 overwriting any changes made by the tvnorm. This means that it
537 is the responsibility of the module using the tda9887 to set
538 these values in case of changes in the tvnorm.
539 In many cases port 2 should be made active (0) when selecting
540 SECAM-L, and port 2 should remain inactive (1) for SECAM-L'.
541
542 For the other standards the tda9887 application note says that
543 the ports should be set to active (0), but, again, that may
544 differ depending on the precise hardware configuration.
545 */
Michael Krufkyb2083192007-05-29 22:54:06 -0300546 priv->data[1] |= cOutputPort1Inactive;
547 priv->data[1] |= cOutputPort2Inactive;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700548
Michael Krufkyb2083192007-05-29 22:54:06 -0300549 tda9887_set_config(t,priv->data);
550 tda9887_set_insmod(t,priv->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700552 if (t->mode == T_STANDBY) {
Michael Krufkyb2083192007-05-29 22:54:06 -0300553 priv->data[1] |= cForcedMuteAudioON;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700554 }
555
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800556 tda9887_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",
Michael Krufkyb2083192007-05-29 22:54:06 -0300557 priv->data[1],priv->data[2],priv->data[3]);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300558 if (tuner_debug > 1)
Michael Krufkyb2083192007-05-29 22:54:06 -0300559 dump_write_message(t, priv->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560
Michael Krufkyb2083192007-05-29 22:54:06 -0300561 if (4 != (rc = i2c_master_send(&t->i2c,priv->data,4)))
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800562 tda9887_info("i2c i/o error: rc == %d (should be 4)\n",rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300564 if (tuner_debug > 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 msleep_interruptible(1000);
566 tda9887_status(t);
567 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568}
569
570/* ---------------------------------------------------------------------- */
571
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300572static void tda9887_tuner_status(struct i2c_client *client)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573{
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300574 struct tuner *t = i2c_get_clientdata(client);
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
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300579static int tda9887_get_afc(struct i2c_client *client)
580{
581 struct tuner *t = i2c_get_clientdata(client);
582 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
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300591 if (1 == i2c_master_recv(&t->i2c,&reg,1))
592 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
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300597static void tda9887_standby(struct i2c_client *client)
598{
599 tda9887_configure(client);
600}
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800601
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300602static void tda9887_set_freq(struct i2c_client *client, unsigned int freq)
603{
604 tda9887_configure(client);
605}
606
Michael Krufky024cf532007-06-04 15:20:11 -0300607static void tda9887_release(struct i2c_client *c)
608{
609 struct tuner *t = i2c_get_clientdata(c);
610
611 kfree(t->priv);
612 t->priv = NULL;
613}
614
Michael Krufky9af596e2007-06-06 16:15:48 -0300615static struct tuner_operations tda9887_tuner_ops = {
616 .set_tv_freq = tda9887_set_freq,
617 .set_radio_freq = tda9887_set_freq,
618 .standby = tda9887_standby,
619 .tuner_status = tda9887_tuner_status,
620 .get_afc = tda9887_get_afc,
621 .release = tda9887_release,
622};
623
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300624int tda9887_tuner_init(struct i2c_client *c)
625{
Michael Krufkyb2083192007-05-29 22:54:06 -0300626 struct tda9887_priv *priv = NULL;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300627 struct tuner *t = i2c_get_clientdata(c);
628
Michael Krufkyb2083192007-05-29 22:54:06 -0300629 priv = kzalloc(sizeof(struct tda9887_priv), GFP_KERNEL);
630 if (priv == NULL)
631 return -ENOMEM;
632 t->priv = priv;
633
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300634 strlcpy(c->name, "tda9887", sizeof(c->name));
635
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 */