blob: a8f773274fe3f8188f0e7131f86f27fa74d0a24d [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>
Michael Krufky8218b0b2007-06-26 13:12:08 -030014#include "tuner-driver.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
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -030025#define tda9887_info(fmt, arg...) do {\
Hans Verkuil343f92c2006-06-25 11:02:02 -030026 printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -030027 i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
28#define tda9887_dbg(fmt, arg...) do {\
29 if (tuner_debug) \
Hans Verkuil343f92c2006-06-25 11:02:02 -030030 printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -030031 i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
Michael Krufkyb2083192007-05-29 22:54:06 -030033struct tda9887_priv {
34 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
100#define cVideoIF_58_75 0x00 // bit e2:4
101#define cVideoIF_45_75 0x04 // bit e2:4
102#define cVideoIF_38_90 0x08 // bit e2:4
103#define cVideoIF_38_00 0x0C // bit e2:4
104#define cVideoIF_33_90 0x10 // bit e2:4
105#define cVideoIF_33_40 0x14 // bit e2:4
106#define cRadioIF_45_75 0x18 // bit e2:4
107#define cRadioIF_38_90 0x1C // bit e2:4
108
109
110#define cTunerGainNormal 0x00 // bit e5
111#define cTunerGainLow 0x20 // bit e5
112
113#define cGating_18 0x00 // bit e6
114#define cGating_36 0x40 // bit e6
115
116#define cAgcOutON 0x80 // bit e7
117#define cAgcOutOFF 0x00 // bit e7
118
119/* ---------------------------------------------------------------------- */
120
121static struct tvnorm tvnorms[] = {
122 {
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200123 .std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N,
124 .name = "PAL-BGHN",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 .b = ( cNegativeFmTV |
126 cQSS ),
127 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200128 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300129 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200130 .e = ( cGating_36 |
131 cAudioIF_5_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 cVideoIF_38_90 ),
133 },{
134 .std = V4L2_STD_PAL_I,
135 .name = "PAL-I",
136 .b = ( cNegativeFmTV |
137 cQSS ),
138 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200139 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300140 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200141 .e = ( cGating_36 |
142 cAudioIF_6_0 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 cVideoIF_38_90 ),
144 },{
145 .std = V4L2_STD_PAL_DK,
146 .name = "PAL-DK",
147 .b = ( cNegativeFmTV |
148 cQSS ),
149 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200150 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300151 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200152 .e = ( cGating_36 |
153 cAudioIF_6_5 |
154 cVideoIF_38_90 ),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 },{
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200156 .std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc,
157 .name = "PAL-M/Nc",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 .b = ( cNegativeFmTV |
159 cQSS ),
160 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200161 cDeemphasis75 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300162 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200163 .e = ( cGating_36 |
164 cAudioIF_4_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 cVideoIF_45_75 ),
166 },{
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200167 .std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H,
168 .name = "SECAM-BGH",
169 .b = ( cPositiveAmTV |
170 cQSS ),
Hans Verkuilf5b01422006-06-25 15:37:29 -0300171 .c = ( cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200172 .e = ( cGating_36 |
173 cAudioIF_5_5 |
174 cVideoIF_38_90 ),
175 },{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 .std = V4L2_STD_SECAM_L,
177 .name = "SECAM-L",
178 .b = ( cPositiveAmTV |
179 cQSS ),
Hans Verkuilf5b01422006-06-25 15:37:29 -0300180 .c = ( cTopDefault),
Nickolay V. Shmyrev3375c392005-11-08 21:37:05 -0800181 .e = ( cGating_36 |
Nickolay V. Shmyrev5f7591c2005-11-08 21:37:04 -0800182 cAudioIF_6_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 cVideoIF_38_90 ),
184 },{
Mauro Carvalho Chehabf3c59872006-01-09 15:25:00 -0200185 .std = V4L2_STD_SECAM_LC,
186 .name = "SECAM-L'",
187 .b = ( cOutputPort2Inactive |
188 cPositiveAmTV |
189 cQSS ),
Hans Verkuilf5b01422006-06-25 15:37:29 -0300190 .c = ( cTopDefault),
Mauro Carvalho Chehabf3c59872006-01-09 15:25:00 -0200191 .e = ( cGating_36 |
192 cAudioIF_6_5 |
193 cVideoIF_33_90 ),
194 },{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 .std = V4L2_STD_SECAM_DK,
196 .name = "SECAM-DK",
197 .b = ( cNegativeFmTV |
198 cQSS ),
199 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200200 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300201 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200202 .e = ( cGating_36 |
203 cAudioIF_6_5 |
204 cVideoIF_38_90 ),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 },{
Hans Verkuil0dfd8122006-02-07 06:45:34 -0200206 .std = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 .name = "NTSC-M",
208 .b = ( cNegativeFmTV |
209 cQSS ),
210 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200211 cDeemphasis75 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300212 cTopDefault),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 .e = ( cGating_36 |
214 cAudioIF_4_5 |
215 cVideoIF_45_75 ),
216 },{
217 .std = V4L2_STD_NTSC_M_JP,
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200218 .name = "NTSC-M-JP",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 .b = ( cNegativeFmTV |
220 cQSS ),
221 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200222 cDeemphasis50 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300223 cTopDefault),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 .e = ( cGating_36 |
225 cAudioIF_4_5 |
226 cVideoIF_58_75 ),
227 }
228};
229
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700230static struct tvnorm radio_stereo = {
231 .name = "Radio Stereo",
232 .b = ( cFmRadio |
233 cQSS ),
234 .c = ( cDeemphasisOFF |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200235 cAudioGain6 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300236 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200237 .e = ( cTunerGainLow |
238 cAudioIF_5_5 |
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700239 cRadioIF_38_90 ),
240};
241
242static struct tvnorm radio_mono = {
243 .name = "Radio Mono",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 .b = ( cFmRadio |
245 cQSS ),
246 .c = ( cDeemphasisON |
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200247 cDeemphasis75 |
Hans Verkuilf5b01422006-06-25 15:37:29 -0300248 cTopDefault),
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200249 .e = ( cTunerGainLow |
250 cAudioIF_5_5 |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 cRadioIF_38_90 ),
252};
253
254/* ---------------------------------------------------------------------- */
255
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300256static void dump_read_message(struct tuner *t, unsigned char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257{
258 static char *afc[16] = {
259 "- 12.5 kHz",
260 "- 37.5 kHz",
261 "- 62.5 kHz",
262 "- 87.5 kHz",
263 "-112.5 kHz",
264 "-137.5 kHz",
265 "-162.5 kHz",
266 "-187.5 kHz [min]",
267 "+187.5 kHz [max]",
268 "+162.5 kHz",
269 "+137.5 kHz",
270 "+112.5 kHz",
271 "+ 87.5 kHz",
272 "+ 62.5 kHz",
273 "+ 37.5 kHz",
274 "+ 12.5 kHz",
275 };
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800276 tda9887_info("read: 0x%2x\n", buf[0]);
277 tda9887_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no");
278 tda9887_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]);
279 tda9887_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low");
280 tda9887_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out");
281 tda9887_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282}
283
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300284static void dump_write_message(struct tuner *t, unsigned char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285{
286 static char *sound[4] = {
287 "AM/TV",
288 "FM/radio",
289 "FM/TV",
290 "FM/radio"
291 };
292 static char *adjust[32] = {
293 "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9",
294 "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1",
295 "0", "+1", "+2", "+3", "+4", "+5", "+6", "+7",
296 "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15"
297 };
298 static char *deemph[4] = {
299 "no", "no", "75", "50"
300 };
301 static char *carrier[4] = {
302 "4.5 MHz",
303 "5.5 MHz",
304 "6.0 MHz",
305 "6.5 MHz / AM"
306 };
307 static char *vif[8] = {
308 "58.75 MHz",
309 "45.75 MHz",
310 "38.9 MHz",
311 "38.0 MHz",
312 "33.9 MHz",
313 "33.4 MHz",
314 "45.75 MHz + pin13",
315 "38.9 MHz + pin13",
316 };
317 static char *rif[4] = {
318 "44 MHz",
319 "52 MHz",
320 "52 MHz",
321 "44 MHz",
322 };
323
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800324 tda9887_info("write: byte B 0x%02x\n",buf[1]);
325 tda9887_info(" B0 video mode : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 (buf[1] & 0x01) ? "video trap" : "sound trap");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800327 tda9887_info(" B1 auto mute fm : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 (buf[1] & 0x02) ? "yes" : "no");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800329 tda9887_info(" B2 carrier mode : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 (buf[1] & 0x04) ? "QSS" : "Intercarrier");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800331 tda9887_info(" B3-4 tv sound/radio : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 sound[(buf[1] & 0x18) >> 3]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800333 tda9887_info(" B5 force mute audio: %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 (buf[1] & 0x20) ? "yes" : "no");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800335 tda9887_info(" B6 output port 1 : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 (buf[1] & 0x40) ? "high (inactive)" : "low (active)");
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800337 tda9887_info(" B7 output port 2 : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 (buf[1] & 0x80) ? "high (inactive)" : "low (active)");
339
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800340 tda9887_info("write: byte C 0x%02x\n",buf[2]);
341 tda9887_info(" C0-4 top adjustment : %s dB\n", adjust[buf[2] & 0x1f]);
342 tda9887_info(" C5-6 de-emphasis : %s\n", deemph[(buf[2] & 0x60) >> 5]);
343 tda9887_info(" C7 audio gain : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 (buf[2] & 0x80) ? "-6" : "0");
345
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800346 tda9887_info("write: byte E 0x%02x\n",buf[3]);
347 tda9887_info(" E0-1 sound carrier : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 carrier[(buf[3] & 0x03)]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800349 tda9887_info(" E6 l pll gating : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 (buf[3] & 0x40) ? "36" : "13");
351
352 if (buf[1] & 0x08) {
353 /* radio */
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800354 tda9887_info(" E2-4 video if : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 rif[(buf[3] & 0x0c) >> 2]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800356 tda9887_info(" E7 vif agc output : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 (buf[3] & 0x80)
358 ? ((buf[3] & 0x10) ? "fm-agc radio" : "sif-agc radio")
359 : "fm radio carrier afc");
360 } else {
361 /* video */
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800362 tda9887_info(" E2-4 video if : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 vif[(buf[3] & 0x1c) >> 2]);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800364 tda9887_info(" E5 tuner gain : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 (buf[3] & 0x80)
366 ? ((buf[3] & 0x20) ? "external" : "normal")
367 : ((buf[3] & 0x20) ? "minimum" : "normal"));
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800368 tda9887_info(" E7 vif agc output : %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 (buf[3] & 0x80)
370 ? ((buf[3] & 0x20)
371 ? "pin3 port, pin22 vif agc out"
372 : "pin22 port, pin3 vif acg ext in")
373 : "pin3+pin22 port");
374 }
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800375 tda9887_info("--\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376}
377
378/* ---------------------------------------------------------------------- */
379
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300380static int tda9887_set_tvnorm(struct tuner *t, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381{
382 struct tvnorm *norm = NULL;
383 int i;
384
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300385 if (t->mode == V4L2_TUNER_RADIO) {
386 if (t->audmode == V4L2_TUNER_MODE_MONO)
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700387 norm = &radio_mono;
388 else
Mauro Carvalho Chehab586b0ca2005-06-28 20:45:21 -0700389 norm = &radio_stereo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 } else {
391 for (i = 0; i < ARRAY_SIZE(tvnorms); i++) {
392 if (tvnorms[i].std & t->std) {
393 norm = tvnorms+i;
394 break;
395 }
396 }
397 }
398 if (NULL == norm) {
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800399 tda9887_dbg("Unsupported tvnorm entry - audio muted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 return -1;
401 }
402
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800403 tda9887_dbg("configure for: %s\n",norm->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 buf[1] = norm->b;
405 buf[2] = norm->c;
406 buf[3] = norm->e;
407 return 0;
408}
409
410static unsigned int port1 = UNSET;
411static unsigned int port2 = UNSET;
412static unsigned int qss = UNSET;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200413static unsigned int adjust = UNSET;
414
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415module_param(port1, int, 0644);
416module_param(port2, int, 0644);
417module_param(qss, int, 0644);
418module_param(adjust, int, 0644);
419
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300420static int tda9887_set_insmod(struct tuner *t, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421{
422 if (UNSET != port1) {
423 if (port1)
424 buf[1] |= cOutputPort1Inactive;
425 else
426 buf[1] &= ~cOutputPort1Inactive;
427 }
428 if (UNSET != port2) {
429 if (port2)
430 buf[1] |= cOutputPort2Inactive;
431 else
432 buf[1] &= ~cOutputPort2Inactive;
433 }
434
435 if (UNSET != qss) {
436 if (qss)
437 buf[1] |= cQSS;
438 else
439 buf[1] &= ~cQSS;
440 }
441
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200442 if (adjust >= 0x00 && adjust < 0x20) {
443 buf[2] &= ~cTopMask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 buf[2] |= adjust;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200445 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 return 0;
447}
448
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300449static int tda9887_set_config(struct tuner *t, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450{
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300451 if (t->tda9887_config & TDA9887_PORT1_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 buf[1] &= ~cOutputPort1Inactive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300453 if (t->tda9887_config & TDA9887_PORT1_INACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 buf[1] |= cOutputPort1Inactive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300455 if (t->tda9887_config & TDA9887_PORT2_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 buf[1] &= ~cOutputPort2Inactive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300457 if (t->tda9887_config & TDA9887_PORT2_INACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 buf[1] |= cOutputPort2Inactive;
459
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300460 if (t->tda9887_config & TDA9887_QSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 buf[1] |= cQSS;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300462 if (t->tda9887_config & TDA9887_INTERCARRIER)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 buf[1] &= ~cQSS;
464
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300465 if (t->tda9887_config & TDA9887_AUTOMUTE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 buf[1] |= cAutoMuteFmActive;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300467 if (t->tda9887_config & TDA9887_DEEMPHASIS_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 buf[2] &= ~0x60;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300469 switch (t->tda9887_config & TDA9887_DEEMPHASIS_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 case TDA9887_DEEMPHASIS_NONE:
471 buf[2] |= cDeemphasisOFF;
472 break;
473 case TDA9887_DEEMPHASIS_50:
474 buf[2] |= cDeemphasisON | cDeemphasis50;
475 break;
476 case TDA9887_DEEMPHASIS_75:
477 buf[2] |= cDeemphasisON | cDeemphasis75;
478 break;
479 }
480 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300481 if (t->tda9887_config & TDA9887_TOP_SET) {
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200482 buf[2] &= ~cTopMask;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300483 buf[2] |= (t->tda9887_config >> 8) & cTopMask;
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200484 }
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300485 if ((t->tda9887_config & TDA9887_INTERCARRIER_NTSC) && (t->std & V4L2_STD_NTSC))
Nickolay V. Shmyrev3ae1adc2005-11-08 21:37:39 -0800486 buf[1] &= ~cQSS;
Trent Piephod7304de2006-08-24 22:43:45 -0300487 if (t->tda9887_config & TDA9887_GATING_18)
488 buf[3] &= ~cGating_36;
Mauro Carvalho Chehabcefccc82006-12-04 08:31:35 -0300489
490 if (t->tda9887_config & TDA9887_GAIN_NORMAL) {
491 radio_stereo.e &= ~cTunerGainLow;
492 radio_mono.e &= ~cTunerGainLow;
493 }
494
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 return 0;
496}
497
498/* ---------------------------------------------------------------------- */
499
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300500static int tda9887_status(struct tuner *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501{
502 unsigned char buf[1];
503 int rc;
504
505 memset(buf,0,sizeof(buf));
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300506 if (1 != (rc = i2c_master_recv(&t->i2c,buf,1)))
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800507 tda9887_info("i2c i/o error: rc == %d (should be 1)\n",rc);
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800508 dump_read_message(t, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 return 0;
510}
511
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300512static void tda9887_configure(struct i2c_client *client)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513{
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300514 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkyb2083192007-05-29 22:54:06 -0300515 struct tda9887_priv *priv = t->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 int rc;
517
Michael Krufkyb2083192007-05-29 22:54:06 -0300518 memset(priv->data,0,sizeof(priv->data));
519 tda9887_set_tvnorm(t,priv->data);
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700520
Hans Verkuilf98c55e2006-01-09 15:25:18 -0200521 /* A note on the port settings:
522 These settings tend to depend on the specifics of the board.
523 By default they are set to inactive (bit value 1) by this driver,
524 overwriting any changes made by the tvnorm. This means that it
525 is the responsibility of the module using the tda9887 to set
526 these values in case of changes in the tvnorm.
527 In many cases port 2 should be made active (0) when selecting
528 SECAM-L, and port 2 should remain inactive (1) for SECAM-L'.
529
530 For the other standards the tda9887 application note says that
531 the ports should be set to active (0), but, again, that may
532 differ depending on the precise hardware configuration.
533 */
Michael Krufkyb2083192007-05-29 22:54:06 -0300534 priv->data[1] |= cOutputPort1Inactive;
535 priv->data[1] |= cOutputPort2Inactive;
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700536
Michael Krufkyb2083192007-05-29 22:54:06 -0300537 tda9887_set_config(t,priv->data);
538 tda9887_set_insmod(t,priv->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700540 if (t->mode == T_STANDBY) {
Michael Krufkyb2083192007-05-29 22:54:06 -0300541 priv->data[1] |= cForcedMuteAudioON;
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700542 }
543
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800544 tda9887_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",
Michael Krufkyb2083192007-05-29 22:54:06 -0300545 priv->data[1],priv->data[2],priv->data[3]);
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300546 if (tuner_debug > 1)
Michael Krufkyb2083192007-05-29 22:54:06 -0300547 dump_write_message(t, priv->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548
Michael Krufkyb2083192007-05-29 22:54:06 -0300549 if (4 != (rc = i2c_master_send(&t->i2c,priv->data,4)))
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800550 tda9887_info("i2c i/o error: rc == %d (should be 4)\n",rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300552 if (tuner_debug > 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 msleep_interruptible(1000);
554 tda9887_status(t);
555 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556}
557
558/* ---------------------------------------------------------------------- */
559
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300560static void tda9887_tuner_status(struct i2c_client *client)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561{
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300562 struct tuner *t = i2c_get_clientdata(client);
Michael Krufkyb2083192007-05-29 22:54:06 -0300563 struct tda9887_priv *priv = t->priv;
564 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 -0300565}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300567static int tda9887_get_afc(struct i2c_client *client)
568{
569 struct tuner *t = i2c_get_clientdata(client);
570 static int AFC_BITS_2_kHz[] = {
571 -12500, -37500, -62500, -97500,
572 -112500, -137500, -162500, -187500,
573 187500, 162500, 137500, 112500,
574 97500 , 62500, 37500 , 12500
575 };
576 int afc=0;
577 __u8 reg = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300579 if (1 == i2c_master_recv(&t->i2c,&reg,1))
580 afc = AFC_BITS_2_kHz[(reg>>1)&0x0f];
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700581
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300582 return afc;
583}
Mauro Carvalho Chehab56fc08c2005-06-23 22:05:07 -0700584
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300585static void tda9887_standby(struct i2c_client *client)
586{
587 tda9887_configure(client);
588}
Hans Verkuil4eb0c142005-11-08 21:37:18 -0800589
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300590static void tda9887_set_freq(struct i2c_client *client, unsigned int freq)
591{
592 tda9887_configure(client);
593}
594
Michael Krufky024cf532007-06-04 15:20:11 -0300595static void tda9887_release(struct i2c_client *c)
596{
597 struct tuner *t = i2c_get_clientdata(c);
598
599 kfree(t->priv);
600 t->priv = NULL;
601}
602
Michael Krufky9af596e2007-06-06 16:15:48 -0300603static struct tuner_operations tda9887_tuner_ops = {
604 .set_tv_freq = tda9887_set_freq,
605 .set_radio_freq = tda9887_set_freq,
606 .standby = tda9887_standby,
607 .tuner_status = tda9887_tuner_status,
608 .get_afc = tda9887_get_afc,
609 .release = tda9887_release,
610};
611
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300612int tda9887_tuner_init(struct i2c_client *c)
613{
Michael Krufkyb2083192007-05-29 22:54:06 -0300614 struct tda9887_priv *priv = NULL;
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300615 struct tuner *t = i2c_get_clientdata(c);
616
Michael Krufkyb2083192007-05-29 22:54:06 -0300617 priv = kzalloc(sizeof(struct tda9887_priv), GFP_KERNEL);
618 if (priv == NULL)
619 return -ENOMEM;
620 t->priv = priv;
621
Mauro Carvalho Chehab15396232006-06-23 16:13:56 -0300622 strlcpy(c->name, "tda9887", sizeof(c->name));
623
624 tda9887_info("tda988[5/6/7] found @ 0x%x (%s)\n", t->i2c.addr,
625 t->i2c.driver->driver.name);
626
Michael Krufky9af596e2007-06-06 16:15:48 -0300627 memcpy(&t->ops, &tda9887_tuner_ops, sizeof(struct tuner_operations));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
629 return 0;
630}
631
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632/*
633 * Overrides for Emacs so that we follow Linus's tabbing style.
634 * ---------------------------------------------------------------------------
635 * Local variables:
636 * c-basic-offset: 8
637 * End:
638 */