xref: /OK3568_Linux_fs/kernel/drivers/media/tuners/tda9887.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun #include <linux/module.h>
3*4882a593Smuzhiyun #include <linux/kernel.h>
4*4882a593Smuzhiyun #include <linux/i2c.h>
5*4882a593Smuzhiyun #include <linux/types.h>
6*4882a593Smuzhiyun #include <linux/init.h>
7*4882a593Smuzhiyun #include <linux/errno.h>
8*4882a593Smuzhiyun #include <linux/delay.h>
9*4882a593Smuzhiyun #include <linux/videodev2.h>
10*4882a593Smuzhiyun #include <media/v4l2-common.h>
11*4882a593Smuzhiyun #include <media/tuner.h>
12*4882a593Smuzhiyun #include "tuner-i2c.h"
13*4882a593Smuzhiyun #include "tda9887.h"
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun /* Chips:
17*4882a593Smuzhiyun    TDA9885 (PAL, NTSC)
18*4882a593Smuzhiyun    TDA9886 (PAL, SECAM, NTSC)
19*4882a593Smuzhiyun    TDA9887 (PAL, SECAM, NTSC, FM Radio)
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun    Used as part of several tuners
22*4882a593Smuzhiyun */
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun static int debug;
25*4882a593Smuzhiyun module_param(debug, int, 0644);
26*4882a593Smuzhiyun MODULE_PARM_DESC(debug, "enable verbose debug messages");
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun static DEFINE_MUTEX(tda9887_list_mutex);
29*4882a593Smuzhiyun static LIST_HEAD(hybrid_tuner_instance_list);
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun struct tda9887_priv {
32*4882a593Smuzhiyun 	struct tuner_i2c_props i2c_props;
33*4882a593Smuzhiyun 	struct list_head hybrid_tuner_instance_list;
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun 	unsigned char	   data[4];
36*4882a593Smuzhiyun 	unsigned int       config;
37*4882a593Smuzhiyun 	unsigned int       mode;
38*4882a593Smuzhiyun 	unsigned int       audmode;
39*4882a593Smuzhiyun 	v4l2_std_id        std;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	bool               standby;
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun /* ---------------------------------------------------------------------- */
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun #define UNSET       (-1U)
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun struct tvnorm {
49*4882a593Smuzhiyun 	v4l2_std_id       std;
50*4882a593Smuzhiyun 	char              *name;
51*4882a593Smuzhiyun 	unsigned char     b;
52*4882a593Smuzhiyun 	unsigned char     c;
53*4882a593Smuzhiyun 	unsigned char     e;
54*4882a593Smuzhiyun };
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun /* ---------------------------------------------------------------------- */
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun //
59*4882a593Smuzhiyun // TDA defines
60*4882a593Smuzhiyun //
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun //// first reg (b)
63*4882a593Smuzhiyun #define cVideoTrapBypassOFF     0x00    // bit b0
64*4882a593Smuzhiyun #define cVideoTrapBypassON      0x01    // bit b0
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun #define cAutoMuteFmInactive     0x00    // bit b1
67*4882a593Smuzhiyun #define cAutoMuteFmActive       0x02    // bit b1
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun #define cIntercarrier           0x00    // bit b2
70*4882a593Smuzhiyun #define cQSS                    0x04    // bit b2
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun #define cPositiveAmTV           0x00    // bit b3:4
73*4882a593Smuzhiyun #define cFmRadio                0x08    // bit b3:4
74*4882a593Smuzhiyun #define cNegativeFmTV           0x10    // bit b3:4
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun #define cForcedMuteAudioON      0x20    // bit b5
78*4882a593Smuzhiyun #define cForcedMuteAudioOFF     0x00    // bit b5
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun #define cOutputPort1Active      0x00    // bit b6
81*4882a593Smuzhiyun #define cOutputPort1Inactive    0x40    // bit b6
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun #define cOutputPort2Active      0x00    // bit b7
84*4882a593Smuzhiyun #define cOutputPort2Inactive    0x80    // bit b7
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun //// second reg (c)
88*4882a593Smuzhiyun #define cDeemphasisOFF          0x00    // bit c5
89*4882a593Smuzhiyun #define cDeemphasisON           0x20    // bit c5
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun #define cDeemphasis75           0x00    // bit c6
92*4882a593Smuzhiyun #define cDeemphasis50           0x40    // bit c6
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun #define cAudioGain0             0x00    // bit c7
95*4882a593Smuzhiyun #define cAudioGain6             0x80    // bit c7
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun #define cTopMask                0x1f    // bit c0:4
98*4882a593Smuzhiyun #define cTopDefault		0x10	// bit c0:4
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun //// third reg (e)
101*4882a593Smuzhiyun #define cAudioIF_4_5             0x00    // bit e0:1
102*4882a593Smuzhiyun #define cAudioIF_5_5             0x01    // bit e0:1
103*4882a593Smuzhiyun #define cAudioIF_6_0             0x02    // bit e0:1
104*4882a593Smuzhiyun #define cAudioIF_6_5             0x03    // bit e0:1
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun #define cVideoIFMask		0x1c	// bit e2:4
108*4882a593Smuzhiyun /* Video IF selection in TV Mode (bit B3=0) */
109*4882a593Smuzhiyun #define cVideoIF_58_75           0x00    // bit e2:4
110*4882a593Smuzhiyun #define cVideoIF_45_75           0x04    // bit e2:4
111*4882a593Smuzhiyun #define cVideoIF_38_90           0x08    // bit e2:4
112*4882a593Smuzhiyun #define cVideoIF_38_00           0x0C    // bit e2:4
113*4882a593Smuzhiyun #define cVideoIF_33_90           0x10    // bit e2:4
114*4882a593Smuzhiyun #define cVideoIF_33_40           0x14    // bit e2:4
115*4882a593Smuzhiyun #define cRadioIF_45_75           0x18    // bit e2:4
116*4882a593Smuzhiyun #define cRadioIF_38_90           0x1C    // bit e2:4
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun /* IF1 selection in Radio Mode (bit B3=1) */
119*4882a593Smuzhiyun #define cRadioIF_33_30		0x00	// bit e2,4 (also 0x10,0x14)
120*4882a593Smuzhiyun #define cRadioIF_41_30		0x04	// bit e2,4
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun /* Output of AFC pin in radio mode when bit E7=1 */
123*4882a593Smuzhiyun #define cRadioAGC_SIF		0x00	// bit e3
124*4882a593Smuzhiyun #define cRadioAGC_FM		0x08	// bit e3
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun #define cTunerGainNormal         0x00    // bit e5
127*4882a593Smuzhiyun #define cTunerGainLow            0x20    // bit e5
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun #define cGating_18               0x00    // bit e6
130*4882a593Smuzhiyun #define cGating_36               0x40    // bit e6
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun #define cAgcOutON                0x80    // bit e7
133*4882a593Smuzhiyun #define cAgcOutOFF               0x00    // bit e7
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun /* ---------------------------------------------------------------------- */
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun static struct tvnorm tvnorms[] = {
138*4882a593Smuzhiyun 	{
139*4882a593Smuzhiyun 		.std   = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N,
140*4882a593Smuzhiyun 		.name  = "PAL-BGHN",
141*4882a593Smuzhiyun 		.b     = ( cNegativeFmTV  |
142*4882a593Smuzhiyun 			   cQSS           ),
143*4882a593Smuzhiyun 		.c     = ( cDeemphasisON  |
144*4882a593Smuzhiyun 			   cDeemphasis50  |
145*4882a593Smuzhiyun 			   cTopDefault),
146*4882a593Smuzhiyun 		.e     = ( cGating_36     |
147*4882a593Smuzhiyun 			   cAudioIF_5_5   |
148*4882a593Smuzhiyun 			   cVideoIF_38_90 ),
149*4882a593Smuzhiyun 	},{
150*4882a593Smuzhiyun 		.std   = V4L2_STD_PAL_I,
151*4882a593Smuzhiyun 		.name  = "PAL-I",
152*4882a593Smuzhiyun 		.b     = ( cNegativeFmTV  |
153*4882a593Smuzhiyun 			   cQSS           ),
154*4882a593Smuzhiyun 		.c     = ( cDeemphasisON  |
155*4882a593Smuzhiyun 			   cDeemphasis50  |
156*4882a593Smuzhiyun 			   cTopDefault),
157*4882a593Smuzhiyun 		.e     = ( cGating_36     |
158*4882a593Smuzhiyun 			   cAudioIF_6_0   |
159*4882a593Smuzhiyun 			   cVideoIF_38_90 ),
160*4882a593Smuzhiyun 	},{
161*4882a593Smuzhiyun 		.std   = V4L2_STD_PAL_DK,
162*4882a593Smuzhiyun 		.name  = "PAL-DK",
163*4882a593Smuzhiyun 		.b     = ( cNegativeFmTV  |
164*4882a593Smuzhiyun 			   cQSS           ),
165*4882a593Smuzhiyun 		.c     = ( cDeemphasisON  |
166*4882a593Smuzhiyun 			   cDeemphasis50  |
167*4882a593Smuzhiyun 			   cTopDefault),
168*4882a593Smuzhiyun 		.e     = ( cGating_36     |
169*4882a593Smuzhiyun 			   cAudioIF_6_5   |
170*4882a593Smuzhiyun 			   cVideoIF_38_90 ),
171*4882a593Smuzhiyun 	},{
172*4882a593Smuzhiyun 		.std   = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc,
173*4882a593Smuzhiyun 		.name  = "PAL-M/Nc",
174*4882a593Smuzhiyun 		.b     = ( cNegativeFmTV  |
175*4882a593Smuzhiyun 			   cQSS           ),
176*4882a593Smuzhiyun 		.c     = ( cDeemphasisON  |
177*4882a593Smuzhiyun 			   cDeemphasis75  |
178*4882a593Smuzhiyun 			   cTopDefault),
179*4882a593Smuzhiyun 		.e     = ( cGating_36     |
180*4882a593Smuzhiyun 			   cAudioIF_4_5   |
181*4882a593Smuzhiyun 			   cVideoIF_45_75 ),
182*4882a593Smuzhiyun 	},{
183*4882a593Smuzhiyun 		.std   = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H,
184*4882a593Smuzhiyun 		.name  = "SECAM-BGH",
185*4882a593Smuzhiyun 		.b     = ( cNegativeFmTV  |
186*4882a593Smuzhiyun 			   cQSS           ),
187*4882a593Smuzhiyun 		.c     = ( cTopDefault),
188*4882a593Smuzhiyun 		.e     = ( cAudioIF_5_5   |
189*4882a593Smuzhiyun 			   cVideoIF_38_90 ),
190*4882a593Smuzhiyun 	},{
191*4882a593Smuzhiyun 		.std   = V4L2_STD_SECAM_L,
192*4882a593Smuzhiyun 		.name  = "SECAM-L",
193*4882a593Smuzhiyun 		.b     = ( cPositiveAmTV  |
194*4882a593Smuzhiyun 			   cQSS           ),
195*4882a593Smuzhiyun 		.c     = ( cTopDefault),
196*4882a593Smuzhiyun 		.e     = ( cGating_36	  |
197*4882a593Smuzhiyun 			   cAudioIF_6_5   |
198*4882a593Smuzhiyun 			   cVideoIF_38_90 ),
199*4882a593Smuzhiyun 	},{
200*4882a593Smuzhiyun 		.std   = V4L2_STD_SECAM_LC,
201*4882a593Smuzhiyun 		.name  = "SECAM-L'",
202*4882a593Smuzhiyun 		.b     = ( cOutputPort2Inactive |
203*4882a593Smuzhiyun 			   cPositiveAmTV  |
204*4882a593Smuzhiyun 			   cQSS           ),
205*4882a593Smuzhiyun 		.c     = ( cTopDefault),
206*4882a593Smuzhiyun 		.e     = ( cGating_36	  |
207*4882a593Smuzhiyun 			   cAudioIF_6_5   |
208*4882a593Smuzhiyun 			   cVideoIF_33_90 ),
209*4882a593Smuzhiyun 	},{
210*4882a593Smuzhiyun 		.std   = V4L2_STD_SECAM_DK,
211*4882a593Smuzhiyun 		.name  = "SECAM-DK",
212*4882a593Smuzhiyun 		.b     = ( cNegativeFmTV  |
213*4882a593Smuzhiyun 			   cQSS           ),
214*4882a593Smuzhiyun 		.c     = ( cDeemphasisON  |
215*4882a593Smuzhiyun 			   cDeemphasis50  |
216*4882a593Smuzhiyun 			   cTopDefault),
217*4882a593Smuzhiyun 		.e     = ( cGating_36     |
218*4882a593Smuzhiyun 			   cAudioIF_6_5   |
219*4882a593Smuzhiyun 			   cVideoIF_38_90 ),
220*4882a593Smuzhiyun 	},{
221*4882a593Smuzhiyun 		.std   = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
222*4882a593Smuzhiyun 		.name  = "NTSC-M",
223*4882a593Smuzhiyun 		.b     = ( cNegativeFmTV  |
224*4882a593Smuzhiyun 			   cQSS           ),
225*4882a593Smuzhiyun 		.c     = ( cDeemphasisON  |
226*4882a593Smuzhiyun 			   cDeemphasis75  |
227*4882a593Smuzhiyun 			   cTopDefault),
228*4882a593Smuzhiyun 		.e     = ( cGating_36     |
229*4882a593Smuzhiyun 			   cAudioIF_4_5   |
230*4882a593Smuzhiyun 			   cVideoIF_45_75 ),
231*4882a593Smuzhiyun 	},{
232*4882a593Smuzhiyun 		.std   = V4L2_STD_NTSC_M_JP,
233*4882a593Smuzhiyun 		.name  = "NTSC-M-JP",
234*4882a593Smuzhiyun 		.b     = ( cNegativeFmTV  |
235*4882a593Smuzhiyun 			   cQSS           ),
236*4882a593Smuzhiyun 		.c     = ( cDeemphasisON  |
237*4882a593Smuzhiyun 			   cDeemphasis50  |
238*4882a593Smuzhiyun 			   cTopDefault),
239*4882a593Smuzhiyun 		.e     = ( cGating_36     |
240*4882a593Smuzhiyun 			   cAudioIF_4_5   |
241*4882a593Smuzhiyun 			   cVideoIF_58_75 ),
242*4882a593Smuzhiyun 	}
243*4882a593Smuzhiyun };
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun static struct tvnorm radio_stereo = {
246*4882a593Smuzhiyun 	.name = "Radio Stereo",
247*4882a593Smuzhiyun 	.b    = ( cFmRadio       |
248*4882a593Smuzhiyun 		  cQSS           ),
249*4882a593Smuzhiyun 	.c    = ( cDeemphasisOFF |
250*4882a593Smuzhiyun 		  cAudioGain6    |
251*4882a593Smuzhiyun 		  cTopDefault),
252*4882a593Smuzhiyun 	.e    = ( cTunerGainLow  |
253*4882a593Smuzhiyun 		  cAudioIF_5_5   |
254*4882a593Smuzhiyun 		  cRadioIF_38_90 ),
255*4882a593Smuzhiyun };
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun static struct tvnorm radio_mono = {
258*4882a593Smuzhiyun 	.name = "Radio Mono",
259*4882a593Smuzhiyun 	.b    = ( cFmRadio       |
260*4882a593Smuzhiyun 		  cQSS           ),
261*4882a593Smuzhiyun 	.c    = ( cDeemphasisON  |
262*4882a593Smuzhiyun 		  cDeemphasis75  |
263*4882a593Smuzhiyun 		  cTopDefault),
264*4882a593Smuzhiyun 	.e    = ( cTunerGainLow  |
265*4882a593Smuzhiyun 		  cAudioIF_5_5   |
266*4882a593Smuzhiyun 		  cRadioIF_38_90 ),
267*4882a593Smuzhiyun };
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun /* ---------------------------------------------------------------------- */
270*4882a593Smuzhiyun 
dump_read_message(struct dvb_frontend * fe,unsigned char * buf)271*4882a593Smuzhiyun static void dump_read_message(struct dvb_frontend *fe, unsigned char *buf)
272*4882a593Smuzhiyun {
273*4882a593Smuzhiyun 	struct tda9887_priv *priv = fe->analog_demod_priv;
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	static char *afc[16] = {
276*4882a593Smuzhiyun 		"- 12.5 kHz",
277*4882a593Smuzhiyun 		"- 37.5 kHz",
278*4882a593Smuzhiyun 		"- 62.5 kHz",
279*4882a593Smuzhiyun 		"- 87.5 kHz",
280*4882a593Smuzhiyun 		"-112.5 kHz",
281*4882a593Smuzhiyun 		"-137.5 kHz",
282*4882a593Smuzhiyun 		"-162.5 kHz",
283*4882a593Smuzhiyun 		"-187.5 kHz [min]",
284*4882a593Smuzhiyun 		"+187.5 kHz [max]",
285*4882a593Smuzhiyun 		"+162.5 kHz",
286*4882a593Smuzhiyun 		"+137.5 kHz",
287*4882a593Smuzhiyun 		"+112.5 kHz",
288*4882a593Smuzhiyun 		"+ 87.5 kHz",
289*4882a593Smuzhiyun 		"+ 62.5 kHz",
290*4882a593Smuzhiyun 		"+ 37.5 kHz",
291*4882a593Smuzhiyun 		"+ 12.5 kHz",
292*4882a593Smuzhiyun 	};
293*4882a593Smuzhiyun 	tuner_info("read: 0x%2x\n", buf[0]);
294*4882a593Smuzhiyun 	tuner_info("  after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no");
295*4882a593Smuzhiyun 	tuner_info("  afc            : %s\n", afc[(buf[0] >> 1) & 0x0f]);
296*4882a593Smuzhiyun 	tuner_info("  fmif level     : %s\n", (buf[0] & 0x20) ? "high" : "low");
297*4882a593Smuzhiyun 	tuner_info("  afc window     : %s\n", (buf[0] & 0x40) ? "in" : "out");
298*4882a593Smuzhiyun 	tuner_info("  vfi level      : %s\n", (buf[0] & 0x80) ? "high" : "low");
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun 
dump_write_message(struct dvb_frontend * fe,unsigned char * buf)301*4882a593Smuzhiyun static void dump_write_message(struct dvb_frontend *fe, unsigned char *buf)
302*4882a593Smuzhiyun {
303*4882a593Smuzhiyun 	struct tda9887_priv *priv = fe->analog_demod_priv;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	static char *sound[4] = {
306*4882a593Smuzhiyun 		"AM/TV",
307*4882a593Smuzhiyun 		"FM/radio",
308*4882a593Smuzhiyun 		"FM/TV",
309*4882a593Smuzhiyun 		"FM/radio"
310*4882a593Smuzhiyun 	};
311*4882a593Smuzhiyun 	static char *adjust[32] = {
312*4882a593Smuzhiyun 		"-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9",
313*4882a593Smuzhiyun 		"-8",  "-7",  "-6",  "-5",  "-4",  "-3",  "-2",  "-1",
314*4882a593Smuzhiyun 		"0",   "+1",  "+2",  "+3",  "+4",  "+5",  "+6",  "+7",
315*4882a593Smuzhiyun 		"+8",  "+9",  "+10", "+11", "+12", "+13", "+14", "+15"
316*4882a593Smuzhiyun 	};
317*4882a593Smuzhiyun 	static char *deemph[4] = {
318*4882a593Smuzhiyun 		"no", "no", "75", "50"
319*4882a593Smuzhiyun 	};
320*4882a593Smuzhiyun 	static char *carrier[4] = {
321*4882a593Smuzhiyun 		"4.5 MHz",
322*4882a593Smuzhiyun 		"5.5 MHz",
323*4882a593Smuzhiyun 		"6.0 MHz",
324*4882a593Smuzhiyun 		"6.5 MHz / AM"
325*4882a593Smuzhiyun 	};
326*4882a593Smuzhiyun 	static char *vif[8] = {
327*4882a593Smuzhiyun 		"58.75 MHz",
328*4882a593Smuzhiyun 		"45.75 MHz",
329*4882a593Smuzhiyun 		"38.9 MHz",
330*4882a593Smuzhiyun 		"38.0 MHz",
331*4882a593Smuzhiyun 		"33.9 MHz",
332*4882a593Smuzhiyun 		"33.4 MHz",
333*4882a593Smuzhiyun 		"45.75 MHz + pin13",
334*4882a593Smuzhiyun 		"38.9 MHz + pin13",
335*4882a593Smuzhiyun 	};
336*4882a593Smuzhiyun 	static char *rif[4] = {
337*4882a593Smuzhiyun 		"44 MHz",
338*4882a593Smuzhiyun 		"52 MHz",
339*4882a593Smuzhiyun 		"52 MHz",
340*4882a593Smuzhiyun 		"44 MHz",
341*4882a593Smuzhiyun 	};
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 	tuner_info("write: byte B 0x%02x\n", buf[1]);
344*4882a593Smuzhiyun 	tuner_info("  B0   video mode      : %s\n",
345*4882a593Smuzhiyun 		   (buf[1] & 0x01) ? "video trap" : "sound trap");
346*4882a593Smuzhiyun 	tuner_info("  B1   auto mute fm    : %s\n",
347*4882a593Smuzhiyun 		   (buf[1] & 0x02) ? "yes" : "no");
348*4882a593Smuzhiyun 	tuner_info("  B2   carrier mode    : %s\n",
349*4882a593Smuzhiyun 		   (buf[1] & 0x04) ? "QSS" : "Intercarrier");
350*4882a593Smuzhiyun 	tuner_info("  B3-4 tv sound/radio  : %s\n",
351*4882a593Smuzhiyun 		   sound[(buf[1] & 0x18) >> 3]);
352*4882a593Smuzhiyun 	tuner_info("  B5   force mute audio: %s\n",
353*4882a593Smuzhiyun 		   (buf[1] & 0x20) ? "yes" : "no");
354*4882a593Smuzhiyun 	tuner_info("  B6   output port 1   : %s\n",
355*4882a593Smuzhiyun 		   (buf[1] & 0x40) ? "high (inactive)" : "low (active)");
356*4882a593Smuzhiyun 	tuner_info("  B7   output port 2   : %s\n",
357*4882a593Smuzhiyun 		   (buf[1] & 0x80) ? "high (inactive)" : "low (active)");
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	tuner_info("write: byte C 0x%02x\n", buf[2]);
360*4882a593Smuzhiyun 	tuner_info("  C0-4 top adjustment  : %s dB\n",
361*4882a593Smuzhiyun 		   adjust[buf[2] & 0x1f]);
362*4882a593Smuzhiyun 	tuner_info("  C5-6 de-emphasis     : %s\n",
363*4882a593Smuzhiyun 		   deemph[(buf[2] & 0x60) >> 5]);
364*4882a593Smuzhiyun 	tuner_info("  C7   audio gain      : %s\n",
365*4882a593Smuzhiyun 		   (buf[2] & 0x80) ? "-6" : "0");
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	tuner_info("write: byte E 0x%02x\n", buf[3]);
368*4882a593Smuzhiyun 	tuner_info("  E0-1 sound carrier   : %s\n",
369*4882a593Smuzhiyun 		   carrier[(buf[3] & 0x03)]);
370*4882a593Smuzhiyun 	tuner_info("  E6   l pll gating   : %s\n",
371*4882a593Smuzhiyun 		   (buf[3] & 0x40) ? "36" : "13");
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	if (buf[1] & 0x08) {
374*4882a593Smuzhiyun 		/* radio */
375*4882a593Smuzhiyun 		tuner_info("  E2-4 video if        : %s\n",
376*4882a593Smuzhiyun 			   rif[(buf[3] & 0x0c) >> 2]);
377*4882a593Smuzhiyun 		tuner_info("  E7   vif agc output  : %s\n",
378*4882a593Smuzhiyun 			   (buf[3] & 0x80)
379*4882a593Smuzhiyun 			   ? ((buf[3] & 0x10) ? "fm-agc radio" :
380*4882a593Smuzhiyun 						"sif-agc radio")
381*4882a593Smuzhiyun 			   : "fm radio carrier afc");
382*4882a593Smuzhiyun 	} else {
383*4882a593Smuzhiyun 		/* video */
384*4882a593Smuzhiyun 		tuner_info("  E2-4 video if        : %s\n",
385*4882a593Smuzhiyun 			   vif[(buf[3] & 0x1c) >> 2]);
386*4882a593Smuzhiyun 		tuner_info("  E5   tuner gain      : %s\n",
387*4882a593Smuzhiyun 			   (buf[3] & 0x80)
388*4882a593Smuzhiyun 			   ? ((buf[3] & 0x20) ? "external" : "normal")
389*4882a593Smuzhiyun 			   : ((buf[3] & 0x20) ? "minimum"  : "normal"));
390*4882a593Smuzhiyun 		tuner_info("  E7   vif agc output  : %s\n",
391*4882a593Smuzhiyun 			   (buf[3] & 0x80) ? ((buf[3] & 0x20)
392*4882a593Smuzhiyun 				? "pin3 port, pin22 vif agc out"
393*4882a593Smuzhiyun 				: "pin22 port, pin3 vif acg ext in")
394*4882a593Smuzhiyun 				: "pin3+pin22 port");
395*4882a593Smuzhiyun 	}
396*4882a593Smuzhiyun 	tuner_info("--\n");
397*4882a593Smuzhiyun }
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun /* ---------------------------------------------------------------------- */
400*4882a593Smuzhiyun 
tda9887_set_tvnorm(struct dvb_frontend * fe)401*4882a593Smuzhiyun static int tda9887_set_tvnorm(struct dvb_frontend *fe)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun 	struct tda9887_priv *priv = fe->analog_demod_priv;
404*4882a593Smuzhiyun 	struct tvnorm *norm = NULL;
405*4882a593Smuzhiyun 	char *buf = priv->data;
406*4882a593Smuzhiyun 	int i;
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	if (priv->mode == V4L2_TUNER_RADIO) {
409*4882a593Smuzhiyun 		if (priv->audmode == V4L2_TUNER_MODE_MONO)
410*4882a593Smuzhiyun 			norm = &radio_mono;
411*4882a593Smuzhiyun 		else
412*4882a593Smuzhiyun 			norm = &radio_stereo;
413*4882a593Smuzhiyun 	} else {
414*4882a593Smuzhiyun 		for (i = 0; i < ARRAY_SIZE(tvnorms); i++) {
415*4882a593Smuzhiyun 			if (tvnorms[i].std & priv->std) {
416*4882a593Smuzhiyun 				norm = tvnorms+i;
417*4882a593Smuzhiyun 				break;
418*4882a593Smuzhiyun 			}
419*4882a593Smuzhiyun 		}
420*4882a593Smuzhiyun 	}
421*4882a593Smuzhiyun 	if (NULL == norm) {
422*4882a593Smuzhiyun 		tuner_dbg("Unsupported tvnorm entry - audio muted\n");
423*4882a593Smuzhiyun 		return -1;
424*4882a593Smuzhiyun 	}
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 	tuner_dbg("configure for: %s\n", norm->name);
427*4882a593Smuzhiyun 	buf[1] = norm->b;
428*4882a593Smuzhiyun 	buf[2] = norm->c;
429*4882a593Smuzhiyun 	buf[3] = norm->e;
430*4882a593Smuzhiyun 	return 0;
431*4882a593Smuzhiyun }
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun static unsigned int port1  = UNSET;
434*4882a593Smuzhiyun static unsigned int port2  = UNSET;
435*4882a593Smuzhiyun static unsigned int qss    = UNSET;
436*4882a593Smuzhiyun static unsigned int adjust = UNSET;
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun module_param(port1, int, 0644);
439*4882a593Smuzhiyun module_param(port2, int, 0644);
440*4882a593Smuzhiyun module_param(qss, int, 0644);
441*4882a593Smuzhiyun module_param(adjust, int, 0644);
442*4882a593Smuzhiyun 
tda9887_set_insmod(struct dvb_frontend * fe)443*4882a593Smuzhiyun static int tda9887_set_insmod(struct dvb_frontend *fe)
444*4882a593Smuzhiyun {
445*4882a593Smuzhiyun 	struct tda9887_priv *priv = fe->analog_demod_priv;
446*4882a593Smuzhiyun 	char *buf = priv->data;
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	if (UNSET != port1) {
449*4882a593Smuzhiyun 		if (port1)
450*4882a593Smuzhiyun 			buf[1] |= cOutputPort1Inactive;
451*4882a593Smuzhiyun 		else
452*4882a593Smuzhiyun 			buf[1] &= ~cOutputPort1Inactive;
453*4882a593Smuzhiyun 	}
454*4882a593Smuzhiyun 	if (UNSET != port2) {
455*4882a593Smuzhiyun 		if (port2)
456*4882a593Smuzhiyun 			buf[1] |= cOutputPort2Inactive;
457*4882a593Smuzhiyun 		else
458*4882a593Smuzhiyun 			buf[1] &= ~cOutputPort2Inactive;
459*4882a593Smuzhiyun 	}
460*4882a593Smuzhiyun 
461*4882a593Smuzhiyun 	if (UNSET != qss) {
462*4882a593Smuzhiyun 		if (qss)
463*4882a593Smuzhiyun 			buf[1] |= cQSS;
464*4882a593Smuzhiyun 		else
465*4882a593Smuzhiyun 			buf[1] &= ~cQSS;
466*4882a593Smuzhiyun 	}
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 	if (adjust < 0x20) {
469*4882a593Smuzhiyun 		buf[2] &= ~cTopMask;
470*4882a593Smuzhiyun 		buf[2] |= adjust;
471*4882a593Smuzhiyun 	}
472*4882a593Smuzhiyun 	return 0;
473*4882a593Smuzhiyun }
474*4882a593Smuzhiyun 
tda9887_do_config(struct dvb_frontend * fe)475*4882a593Smuzhiyun static int tda9887_do_config(struct dvb_frontend *fe)
476*4882a593Smuzhiyun {
477*4882a593Smuzhiyun 	struct tda9887_priv *priv = fe->analog_demod_priv;
478*4882a593Smuzhiyun 	char *buf = priv->data;
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 	if (priv->config & TDA9887_PORT1_ACTIVE)
481*4882a593Smuzhiyun 		buf[1] &= ~cOutputPort1Inactive;
482*4882a593Smuzhiyun 	if (priv->config & TDA9887_PORT1_INACTIVE)
483*4882a593Smuzhiyun 		buf[1] |= cOutputPort1Inactive;
484*4882a593Smuzhiyun 	if (priv->config & TDA9887_PORT2_ACTIVE)
485*4882a593Smuzhiyun 		buf[1] &= ~cOutputPort2Inactive;
486*4882a593Smuzhiyun 	if (priv->config & TDA9887_PORT2_INACTIVE)
487*4882a593Smuzhiyun 		buf[1] |= cOutputPort2Inactive;
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun 	if (priv->config & TDA9887_QSS)
490*4882a593Smuzhiyun 		buf[1] |= cQSS;
491*4882a593Smuzhiyun 	if (priv->config & TDA9887_INTERCARRIER)
492*4882a593Smuzhiyun 		buf[1] &= ~cQSS;
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	if (priv->config & TDA9887_AUTOMUTE)
495*4882a593Smuzhiyun 		buf[1] |= cAutoMuteFmActive;
496*4882a593Smuzhiyun 	if (priv->config & TDA9887_DEEMPHASIS_MASK) {
497*4882a593Smuzhiyun 		buf[2] &= ~0x60;
498*4882a593Smuzhiyun 		switch (priv->config & TDA9887_DEEMPHASIS_MASK) {
499*4882a593Smuzhiyun 		case TDA9887_DEEMPHASIS_NONE:
500*4882a593Smuzhiyun 			buf[2] |= cDeemphasisOFF;
501*4882a593Smuzhiyun 			break;
502*4882a593Smuzhiyun 		case TDA9887_DEEMPHASIS_50:
503*4882a593Smuzhiyun 			buf[2] |= cDeemphasisON | cDeemphasis50;
504*4882a593Smuzhiyun 			break;
505*4882a593Smuzhiyun 		case TDA9887_DEEMPHASIS_75:
506*4882a593Smuzhiyun 			buf[2] |= cDeemphasisON | cDeemphasis75;
507*4882a593Smuzhiyun 			break;
508*4882a593Smuzhiyun 		}
509*4882a593Smuzhiyun 	}
510*4882a593Smuzhiyun 	if (priv->config & TDA9887_TOP_SET) {
511*4882a593Smuzhiyun 		buf[2] &= ~cTopMask;
512*4882a593Smuzhiyun 		buf[2] |= (priv->config >> 8) & cTopMask;
513*4882a593Smuzhiyun 	}
514*4882a593Smuzhiyun 	if ((priv->config & TDA9887_INTERCARRIER_NTSC) &&
515*4882a593Smuzhiyun 	    (priv->std & V4L2_STD_NTSC))
516*4882a593Smuzhiyun 		buf[1] &= ~cQSS;
517*4882a593Smuzhiyun 	if (priv->config & TDA9887_GATING_18)
518*4882a593Smuzhiyun 		buf[3] &= ~cGating_36;
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 	if (priv->mode == V4L2_TUNER_RADIO) {
521*4882a593Smuzhiyun 		if (priv->config & TDA9887_RIF_41_3) {
522*4882a593Smuzhiyun 			buf[3] &= ~cVideoIFMask;
523*4882a593Smuzhiyun 			buf[3] |= cRadioIF_41_30;
524*4882a593Smuzhiyun 		}
525*4882a593Smuzhiyun 		if (priv->config & TDA9887_GAIN_NORMAL)
526*4882a593Smuzhiyun 			buf[3] &= ~cTunerGainLow;
527*4882a593Smuzhiyun 	}
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	return 0;
530*4882a593Smuzhiyun }
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun /* ---------------------------------------------------------------------- */
533*4882a593Smuzhiyun 
tda9887_status(struct dvb_frontend * fe)534*4882a593Smuzhiyun static int tda9887_status(struct dvb_frontend *fe)
535*4882a593Smuzhiyun {
536*4882a593Smuzhiyun 	struct tda9887_priv *priv = fe->analog_demod_priv;
537*4882a593Smuzhiyun 	unsigned char buf[1];
538*4882a593Smuzhiyun 	int rc;
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 	rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, 1);
541*4882a593Smuzhiyun 	if (rc != 1)
542*4882a593Smuzhiyun 		tuner_info("i2c i/o error: rc == %d (should be 1)\n", rc);
543*4882a593Smuzhiyun 	dump_read_message(fe, buf);
544*4882a593Smuzhiyun 	return 0;
545*4882a593Smuzhiyun }
546*4882a593Smuzhiyun 
tda9887_configure(struct dvb_frontend * fe)547*4882a593Smuzhiyun static void tda9887_configure(struct dvb_frontend *fe)
548*4882a593Smuzhiyun {
549*4882a593Smuzhiyun 	struct tda9887_priv *priv = fe->analog_demod_priv;
550*4882a593Smuzhiyun 	int rc;
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun 	memset(priv->data,0,sizeof(priv->data));
553*4882a593Smuzhiyun 	tda9887_set_tvnorm(fe);
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	/* A note on the port settings:
556*4882a593Smuzhiyun 	   These settings tend to depend on the specifics of the board.
557*4882a593Smuzhiyun 	   By default they are set to inactive (bit value 1) by this driver,
558*4882a593Smuzhiyun 	   overwriting any changes made by the tvnorm. This means that it
559*4882a593Smuzhiyun 	   is the responsibility of the module using the tda9887 to set
560*4882a593Smuzhiyun 	   these values in case of changes in the tvnorm.
561*4882a593Smuzhiyun 	   In many cases port 2 should be made active (0) when selecting
562*4882a593Smuzhiyun 	   SECAM-L, and port 2 should remain inactive (1) for SECAM-L'.
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	   For the other standards the tda9887 application note says that
565*4882a593Smuzhiyun 	   the ports should be set to active (0), but, again, that may
566*4882a593Smuzhiyun 	   differ depending on the precise hardware configuration.
567*4882a593Smuzhiyun 	 */
568*4882a593Smuzhiyun 	priv->data[1] |= cOutputPort1Inactive;
569*4882a593Smuzhiyun 	priv->data[1] |= cOutputPort2Inactive;
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun 	tda9887_do_config(fe);
572*4882a593Smuzhiyun 	tda9887_set_insmod(fe);
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 	if (priv->standby)
575*4882a593Smuzhiyun 		priv->data[1] |= cForcedMuteAudioON;
576*4882a593Smuzhiyun 
577*4882a593Smuzhiyun 	tuner_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",
578*4882a593Smuzhiyun 		  priv->data[1], priv->data[2], priv->data[3]);
579*4882a593Smuzhiyun 	if (debug > 1)
580*4882a593Smuzhiyun 		dump_write_message(fe, priv->data);
581*4882a593Smuzhiyun 
582*4882a593Smuzhiyun 	if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,priv->data,4)))
583*4882a593Smuzhiyun 		tuner_info("i2c i/o error: rc == %d (should be 4)\n", rc);
584*4882a593Smuzhiyun 
585*4882a593Smuzhiyun 	if (debug > 2) {
586*4882a593Smuzhiyun 		msleep_interruptible(1000);
587*4882a593Smuzhiyun 		tda9887_status(fe);
588*4882a593Smuzhiyun 	}
589*4882a593Smuzhiyun }
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun /* ---------------------------------------------------------------------- */
592*4882a593Smuzhiyun 
tda9887_tuner_status(struct dvb_frontend * fe)593*4882a593Smuzhiyun static void tda9887_tuner_status(struct dvb_frontend *fe)
594*4882a593Smuzhiyun {
595*4882a593Smuzhiyun 	struct tda9887_priv *priv = fe->analog_demod_priv;
596*4882a593Smuzhiyun 	tuner_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n",
597*4882a593Smuzhiyun 		   priv->data[1], priv->data[2], priv->data[3]);
598*4882a593Smuzhiyun }
599*4882a593Smuzhiyun 
tda9887_get_afc(struct dvb_frontend * fe,s32 * afc)600*4882a593Smuzhiyun static int tda9887_get_afc(struct dvb_frontend *fe, s32 *afc)
601*4882a593Smuzhiyun {
602*4882a593Smuzhiyun 	struct tda9887_priv *priv = fe->analog_demod_priv;
603*4882a593Smuzhiyun 	static const int AFC_BITS_2_kHz[] = {
604*4882a593Smuzhiyun 		-12500,  -37500,  -62500,  -97500,
605*4882a593Smuzhiyun 		-112500, -137500, -162500, -187500,
606*4882a593Smuzhiyun 		187500,  162500,  137500,  112500,
607*4882a593Smuzhiyun 		97500 ,  62500,   37500 ,  12500
608*4882a593Smuzhiyun 	};
609*4882a593Smuzhiyun 	__u8 reg = 0;
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun 	if (priv->mode != V4L2_TUNER_RADIO)
612*4882a593Smuzhiyun 		return 0;
613*4882a593Smuzhiyun 	if (1 == tuner_i2c_xfer_recv(&priv->i2c_props, &reg, 1))
614*4882a593Smuzhiyun 		*afc = AFC_BITS_2_kHz[(reg >> 1) & 0x0f];
615*4882a593Smuzhiyun 	return 0;
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun 
tda9887_standby(struct dvb_frontend * fe)618*4882a593Smuzhiyun static void tda9887_standby(struct dvb_frontend *fe)
619*4882a593Smuzhiyun {
620*4882a593Smuzhiyun 	struct tda9887_priv *priv = fe->analog_demod_priv;
621*4882a593Smuzhiyun 
622*4882a593Smuzhiyun 	priv->standby = true;
623*4882a593Smuzhiyun 
624*4882a593Smuzhiyun 	tda9887_configure(fe);
625*4882a593Smuzhiyun }
626*4882a593Smuzhiyun 
tda9887_set_params(struct dvb_frontend * fe,struct analog_parameters * params)627*4882a593Smuzhiyun static void tda9887_set_params(struct dvb_frontend *fe,
628*4882a593Smuzhiyun 			       struct analog_parameters *params)
629*4882a593Smuzhiyun {
630*4882a593Smuzhiyun 	struct tda9887_priv *priv = fe->analog_demod_priv;
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 	priv->standby = false;
633*4882a593Smuzhiyun 	priv->mode    = params->mode;
634*4882a593Smuzhiyun 	priv->audmode = params->audmode;
635*4882a593Smuzhiyun 	priv->std     = params->std;
636*4882a593Smuzhiyun 	tda9887_configure(fe);
637*4882a593Smuzhiyun }
638*4882a593Smuzhiyun 
tda9887_set_config(struct dvb_frontend * fe,void * priv_cfg)639*4882a593Smuzhiyun static int tda9887_set_config(struct dvb_frontend *fe, void *priv_cfg)
640*4882a593Smuzhiyun {
641*4882a593Smuzhiyun 	struct tda9887_priv *priv = fe->analog_demod_priv;
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun 	priv->config = *(unsigned int *)priv_cfg;
644*4882a593Smuzhiyun 	tda9887_configure(fe);
645*4882a593Smuzhiyun 
646*4882a593Smuzhiyun 	return 0;
647*4882a593Smuzhiyun }
648*4882a593Smuzhiyun 
tda9887_release(struct dvb_frontend * fe)649*4882a593Smuzhiyun static void tda9887_release(struct dvb_frontend *fe)
650*4882a593Smuzhiyun {
651*4882a593Smuzhiyun 	struct tda9887_priv *priv = fe->analog_demod_priv;
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	mutex_lock(&tda9887_list_mutex);
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun 	if (priv)
656*4882a593Smuzhiyun 		hybrid_tuner_release_state(priv);
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun 	mutex_unlock(&tda9887_list_mutex);
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun 	fe->analog_demod_priv = NULL;
661*4882a593Smuzhiyun }
662*4882a593Smuzhiyun 
663*4882a593Smuzhiyun static const struct analog_demod_ops tda9887_ops = {
664*4882a593Smuzhiyun 	.info		= {
665*4882a593Smuzhiyun 		.name	= "tda9887",
666*4882a593Smuzhiyun 	},
667*4882a593Smuzhiyun 	.set_params     = tda9887_set_params,
668*4882a593Smuzhiyun 	.standby        = tda9887_standby,
669*4882a593Smuzhiyun 	.tuner_status   = tda9887_tuner_status,
670*4882a593Smuzhiyun 	.get_afc        = tda9887_get_afc,
671*4882a593Smuzhiyun 	.release        = tda9887_release,
672*4882a593Smuzhiyun 	.set_config     = tda9887_set_config,
673*4882a593Smuzhiyun };
674*4882a593Smuzhiyun 
tda9887_attach(struct dvb_frontend * fe,struct i2c_adapter * i2c_adap,u8 i2c_addr)675*4882a593Smuzhiyun struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
676*4882a593Smuzhiyun 				    struct i2c_adapter *i2c_adap,
677*4882a593Smuzhiyun 				    u8 i2c_addr)
678*4882a593Smuzhiyun {
679*4882a593Smuzhiyun 	struct tda9887_priv *priv = NULL;
680*4882a593Smuzhiyun 	int instance;
681*4882a593Smuzhiyun 
682*4882a593Smuzhiyun 	mutex_lock(&tda9887_list_mutex);
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun 	instance = hybrid_tuner_request_state(struct tda9887_priv, priv,
685*4882a593Smuzhiyun 					      hybrid_tuner_instance_list,
686*4882a593Smuzhiyun 					      i2c_adap, i2c_addr, "tda9887");
687*4882a593Smuzhiyun 	switch (instance) {
688*4882a593Smuzhiyun 	case 0:
689*4882a593Smuzhiyun 		mutex_unlock(&tda9887_list_mutex);
690*4882a593Smuzhiyun 		return NULL;
691*4882a593Smuzhiyun 	case 1:
692*4882a593Smuzhiyun 		fe->analog_demod_priv = priv;
693*4882a593Smuzhiyun 		priv->standby = true;
694*4882a593Smuzhiyun 		tuner_info("tda988[5/6/7] found\n");
695*4882a593Smuzhiyun 		break;
696*4882a593Smuzhiyun 	default:
697*4882a593Smuzhiyun 		fe->analog_demod_priv = priv;
698*4882a593Smuzhiyun 		break;
699*4882a593Smuzhiyun 	}
700*4882a593Smuzhiyun 
701*4882a593Smuzhiyun 	mutex_unlock(&tda9887_list_mutex);
702*4882a593Smuzhiyun 
703*4882a593Smuzhiyun 	memcpy(&fe->ops.analog_ops, &tda9887_ops,
704*4882a593Smuzhiyun 	       sizeof(struct analog_demod_ops));
705*4882a593Smuzhiyun 
706*4882a593Smuzhiyun 	return fe;
707*4882a593Smuzhiyun }
708*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tda9887_attach);
709*4882a593Smuzhiyun 
710*4882a593Smuzhiyun MODULE_LICENSE("GPL");
711