xref: /OK3568_Linux_fs/kernel/drivers/media/usb/tm6000/tm6000-cards.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun // tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices
3*4882a593Smuzhiyun //
4*4882a593Smuzhiyun // Copyright (c) 2006-2007 Mauro Carvalho Chehab <mchehab@kernel.org>
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include <linux/init.h>
7*4882a593Smuzhiyun #include <linux/module.h>
8*4882a593Smuzhiyun #include <linux/pci.h>
9*4882a593Smuzhiyun #include <linux/delay.h>
10*4882a593Smuzhiyun #include <linux/i2c.h>
11*4882a593Smuzhiyun #include <linux/usb.h>
12*4882a593Smuzhiyun #include <linux/slab.h>
13*4882a593Smuzhiyun #include <media/v4l2-common.h>
14*4882a593Smuzhiyun #include <media/tuner.h>
15*4882a593Smuzhiyun #include <media/i2c/tvaudio.h>
16*4882a593Smuzhiyun #include <media/rc-map.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include "tm6000.h"
19*4882a593Smuzhiyun #include "tm6000-regs.h"
20*4882a593Smuzhiyun #include "tuner-xc2028.h"
21*4882a593Smuzhiyun #include "xc5000.h"
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #define TM6000_BOARD_UNKNOWN			0
24*4882a593Smuzhiyun #define TM5600_BOARD_GENERIC			1
25*4882a593Smuzhiyun #define TM6000_BOARD_GENERIC			2
26*4882a593Smuzhiyun #define TM6010_BOARD_GENERIC			3
27*4882a593Smuzhiyun #define TM5600_BOARD_10MOONS_UT821		4
28*4882a593Smuzhiyun #define TM5600_BOARD_10MOONS_UT330		5
29*4882a593Smuzhiyun #define TM6000_BOARD_ADSTECH_DUAL_TV		6
30*4882a593Smuzhiyun #define TM6000_BOARD_FREECOM_AND_SIMILAR	7
31*4882a593Smuzhiyun #define TM6000_BOARD_ADSTECH_MINI_DUAL_TV	8
32*4882a593Smuzhiyun #define TM6010_BOARD_HAUPPAUGE_900H		9
33*4882a593Smuzhiyun #define TM6010_BOARD_BEHOLD_WANDER		10
34*4882a593Smuzhiyun #define TM6010_BOARD_BEHOLD_VOYAGER		11
35*4882a593Smuzhiyun #define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE	12
36*4882a593Smuzhiyun #define TM6010_BOARD_TWINHAN_TU501		13
37*4882a593Smuzhiyun #define TM6010_BOARD_BEHOLD_WANDER_LITE		14
38*4882a593Smuzhiyun #define TM6010_BOARD_BEHOLD_VOYAGER_LITE	15
39*4882a593Smuzhiyun #define TM5600_BOARD_TERRATEC_GRABSTER		16
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun #define is_generic(model) ((model == TM6000_BOARD_UNKNOWN) || \
42*4882a593Smuzhiyun 			   (model == TM5600_BOARD_GENERIC) || \
43*4882a593Smuzhiyun 			   (model == TM6000_BOARD_GENERIC) || \
44*4882a593Smuzhiyun 			   (model == TM6010_BOARD_GENERIC))
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun #define TM6000_MAXBOARDS        16
47*4882a593Smuzhiyun static unsigned int card[]     = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET };
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun module_param_array(card,  int, NULL, 0444);
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun static unsigned long tm6000_devused;
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun struct tm6000_board {
55*4882a593Smuzhiyun 	char            *name;
56*4882a593Smuzhiyun 	char		eename[16];		/* EEPROM name */
57*4882a593Smuzhiyun 	unsigned	eename_size;		/* size of EEPROM name */
58*4882a593Smuzhiyun 	unsigned	eename_pos;		/* Position where it appears at ROM */
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	struct tm6000_capabilities caps;
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	enum		tm6000_devtype type;	/* variant of the chipset */
63*4882a593Smuzhiyun 	int             tuner_type;     /* type of the tuner */
64*4882a593Smuzhiyun 	int             tuner_addr;     /* tuner address */
65*4882a593Smuzhiyun 	int             demod_addr;     /* demodulator address */
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	struct tm6000_gpio gpio;
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun 	struct tm6000_input	vinput[3];
70*4882a593Smuzhiyun 	struct tm6000_input	rinput;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	char		*ir_codes;
73*4882a593Smuzhiyun };
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun static struct tm6000_board tm6000_boards[] = {
76*4882a593Smuzhiyun 	[TM6000_BOARD_UNKNOWN] = {
77*4882a593Smuzhiyun 		.name         = "Unknown tm6000 video grabber",
78*4882a593Smuzhiyun 		.caps = {
79*4882a593Smuzhiyun 			.has_tuner	= 1,
80*4882a593Smuzhiyun 			.has_eeprom	= 1,
81*4882a593Smuzhiyun 		},
82*4882a593Smuzhiyun 		.gpio = {
83*4882a593Smuzhiyun 			.tuner_reset	= TM6000_GPIO_1,
84*4882a593Smuzhiyun 		},
85*4882a593Smuzhiyun 		.vinput = { {
86*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
87*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
88*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC1,
89*4882a593Smuzhiyun 			}, {
90*4882a593Smuzhiyun 			.type	= TM6000_INPUT_COMPOSITE1,
91*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_A,
92*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
93*4882a593Smuzhiyun 			}, {
94*4882a593Smuzhiyun 			.type	= TM6000_INPUT_SVIDEO,
95*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_AB,
96*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
97*4882a593Smuzhiyun 			},
98*4882a593Smuzhiyun 		},
99*4882a593Smuzhiyun 	},
100*4882a593Smuzhiyun 	[TM5600_BOARD_GENERIC] = {
101*4882a593Smuzhiyun 		.name         = "Generic tm5600 board",
102*4882a593Smuzhiyun 		.type         = TM5600,
103*4882a593Smuzhiyun 		.tuner_type   = TUNER_XC2028,
104*4882a593Smuzhiyun 		.tuner_addr   = 0xc2 >> 1,
105*4882a593Smuzhiyun 		.caps = {
106*4882a593Smuzhiyun 			.has_tuner	= 1,
107*4882a593Smuzhiyun 			.has_eeprom	= 1,
108*4882a593Smuzhiyun 		},
109*4882a593Smuzhiyun 		.gpio = {
110*4882a593Smuzhiyun 			.tuner_reset	= TM6000_GPIO_1,
111*4882a593Smuzhiyun 		},
112*4882a593Smuzhiyun 		.vinput = { {
113*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
114*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
115*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC1,
116*4882a593Smuzhiyun 			}, {
117*4882a593Smuzhiyun 			.type	= TM6000_INPUT_COMPOSITE1,
118*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_A,
119*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
120*4882a593Smuzhiyun 			}, {
121*4882a593Smuzhiyun 			.type	= TM6000_INPUT_SVIDEO,
122*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_AB,
123*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
124*4882a593Smuzhiyun 			},
125*4882a593Smuzhiyun 		},
126*4882a593Smuzhiyun 	},
127*4882a593Smuzhiyun 	[TM6000_BOARD_GENERIC] = {
128*4882a593Smuzhiyun 		.name         = "Generic tm6000 board",
129*4882a593Smuzhiyun 		.tuner_type   = TUNER_XC2028,
130*4882a593Smuzhiyun 		.tuner_addr   = 0xc2 >> 1,
131*4882a593Smuzhiyun 		.caps = {
132*4882a593Smuzhiyun 			.has_tuner	= 1,
133*4882a593Smuzhiyun 			.has_eeprom	= 1,
134*4882a593Smuzhiyun 		},
135*4882a593Smuzhiyun 		.gpio = {
136*4882a593Smuzhiyun 			.tuner_reset	= TM6000_GPIO_1,
137*4882a593Smuzhiyun 		},
138*4882a593Smuzhiyun 		.vinput = { {
139*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
140*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
141*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC1,
142*4882a593Smuzhiyun 			}, {
143*4882a593Smuzhiyun 			.type	= TM6000_INPUT_COMPOSITE1,
144*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_A,
145*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
146*4882a593Smuzhiyun 			}, {
147*4882a593Smuzhiyun 			.type	= TM6000_INPUT_SVIDEO,
148*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_AB,
149*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
150*4882a593Smuzhiyun 			},
151*4882a593Smuzhiyun 		},
152*4882a593Smuzhiyun 	},
153*4882a593Smuzhiyun 	[TM6010_BOARD_GENERIC] = {
154*4882a593Smuzhiyun 		.name         = "Generic tm6010 board",
155*4882a593Smuzhiyun 		.type         = TM6010,
156*4882a593Smuzhiyun 		.tuner_type   = TUNER_XC2028,
157*4882a593Smuzhiyun 		.tuner_addr   = 0xc2 >> 1,
158*4882a593Smuzhiyun 		.demod_addr   = 0x1e >> 1,
159*4882a593Smuzhiyun 		.caps = {
160*4882a593Smuzhiyun 			.has_tuner	= 1,
161*4882a593Smuzhiyun 			.has_dvb	= 1,
162*4882a593Smuzhiyun 			.has_zl10353	= 1,
163*4882a593Smuzhiyun 			.has_eeprom	= 1,
164*4882a593Smuzhiyun 			.has_remote	= 1,
165*4882a593Smuzhiyun 		},
166*4882a593Smuzhiyun 		.gpio = {
167*4882a593Smuzhiyun 			.tuner_reset	= TM6010_GPIO_2,
168*4882a593Smuzhiyun 			.tuner_on	= TM6010_GPIO_3,
169*4882a593Smuzhiyun 			.demod_reset	= TM6010_GPIO_1,
170*4882a593Smuzhiyun 			.demod_on	= TM6010_GPIO_4,
171*4882a593Smuzhiyun 			.power_led	= TM6010_GPIO_7,
172*4882a593Smuzhiyun 			.dvb_led	= TM6010_GPIO_5,
173*4882a593Smuzhiyun 			.ir		= TM6010_GPIO_0,
174*4882a593Smuzhiyun 		},
175*4882a593Smuzhiyun 		.vinput = { {
176*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
177*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
178*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_SIF1,
179*4882a593Smuzhiyun 			}, {
180*4882a593Smuzhiyun 			.type	= TM6000_INPUT_COMPOSITE1,
181*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_A,
182*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
183*4882a593Smuzhiyun 			}, {
184*4882a593Smuzhiyun 			.type	= TM6000_INPUT_SVIDEO,
185*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_AB,
186*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
187*4882a593Smuzhiyun 			},
188*4882a593Smuzhiyun 		},
189*4882a593Smuzhiyun 	},
190*4882a593Smuzhiyun 	[TM5600_BOARD_10MOONS_UT821] = {
191*4882a593Smuzhiyun 		.name         = "10Moons UT 821",
192*4882a593Smuzhiyun 		.tuner_type   = TUNER_XC2028,
193*4882a593Smuzhiyun 		.eename       = { '1', '0', 'M', 'O', 'O', 'N', 'S', '5', '6', '0', '0', 0xff, 0x45, 0x5b},
194*4882a593Smuzhiyun 		.eename_size  = 14,
195*4882a593Smuzhiyun 		.eename_pos   = 0x14,
196*4882a593Smuzhiyun 		.type         = TM5600,
197*4882a593Smuzhiyun 		.tuner_addr   = 0xc2 >> 1,
198*4882a593Smuzhiyun 		.caps = {
199*4882a593Smuzhiyun 			.has_tuner    = 1,
200*4882a593Smuzhiyun 			.has_eeprom   = 1,
201*4882a593Smuzhiyun 		},
202*4882a593Smuzhiyun 		.gpio = {
203*4882a593Smuzhiyun 			.tuner_reset	= TM6000_GPIO_1,
204*4882a593Smuzhiyun 		},
205*4882a593Smuzhiyun 		.vinput = { {
206*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
207*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
208*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC1,
209*4882a593Smuzhiyun 			}, {
210*4882a593Smuzhiyun 			.type	= TM6000_INPUT_COMPOSITE1,
211*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_A,
212*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
213*4882a593Smuzhiyun 			}, {
214*4882a593Smuzhiyun 			.type	= TM6000_INPUT_SVIDEO,
215*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_AB,
216*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
217*4882a593Smuzhiyun 			},
218*4882a593Smuzhiyun 		},
219*4882a593Smuzhiyun 	},
220*4882a593Smuzhiyun 	[TM5600_BOARD_10MOONS_UT330] = {
221*4882a593Smuzhiyun 		.name         = "10Moons UT 330",
222*4882a593Smuzhiyun 		.tuner_type   = TUNER_PHILIPS_FQ1216AME_MK4,
223*4882a593Smuzhiyun 		.tuner_addr   = 0xc8 >> 1,
224*4882a593Smuzhiyun 		.caps = {
225*4882a593Smuzhiyun 			.has_tuner    = 1,
226*4882a593Smuzhiyun 			.has_dvb      = 0,
227*4882a593Smuzhiyun 			.has_zl10353  = 0,
228*4882a593Smuzhiyun 			.has_eeprom   = 1,
229*4882a593Smuzhiyun 		},
230*4882a593Smuzhiyun 		.vinput = { {
231*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
232*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
233*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC1,
234*4882a593Smuzhiyun 			}, {
235*4882a593Smuzhiyun 			.type	= TM6000_INPUT_COMPOSITE1,
236*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_A,
237*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
238*4882a593Smuzhiyun 			}, {
239*4882a593Smuzhiyun 			.type	= TM6000_INPUT_SVIDEO,
240*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_AB,
241*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
242*4882a593Smuzhiyun 			},
243*4882a593Smuzhiyun 		},
244*4882a593Smuzhiyun 	},
245*4882a593Smuzhiyun 	[TM6000_BOARD_ADSTECH_DUAL_TV] = {
246*4882a593Smuzhiyun 		.name         = "ADSTECH Dual TV USB",
247*4882a593Smuzhiyun 		.tuner_type   = TUNER_XC2028,
248*4882a593Smuzhiyun 		.tuner_addr   = 0xc8 >> 1,
249*4882a593Smuzhiyun 		.caps = {
250*4882a593Smuzhiyun 			.has_tuner    = 1,
251*4882a593Smuzhiyun 			.has_tda9874  = 1,
252*4882a593Smuzhiyun 			.has_dvb      = 1,
253*4882a593Smuzhiyun 			.has_zl10353  = 1,
254*4882a593Smuzhiyun 			.has_eeprom   = 1,
255*4882a593Smuzhiyun 		},
256*4882a593Smuzhiyun 		.vinput = { {
257*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
258*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
259*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC1,
260*4882a593Smuzhiyun 			}, {
261*4882a593Smuzhiyun 			.type	= TM6000_INPUT_COMPOSITE1,
262*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_A,
263*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
264*4882a593Smuzhiyun 			}, {
265*4882a593Smuzhiyun 			.type	= TM6000_INPUT_SVIDEO,
266*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_AB,
267*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
268*4882a593Smuzhiyun 			},
269*4882a593Smuzhiyun 		},
270*4882a593Smuzhiyun 	},
271*4882a593Smuzhiyun 	[TM6000_BOARD_FREECOM_AND_SIMILAR] = {
272*4882a593Smuzhiyun 		.name         = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual",
273*4882a593Smuzhiyun 		.tuner_type   = TUNER_XC2028, /* has a XC3028 */
274*4882a593Smuzhiyun 		.tuner_addr   = 0xc2 >> 1,
275*4882a593Smuzhiyun 		.demod_addr   = 0x1e >> 1,
276*4882a593Smuzhiyun 		.caps = {
277*4882a593Smuzhiyun 			.has_tuner    = 1,
278*4882a593Smuzhiyun 			.has_dvb      = 1,
279*4882a593Smuzhiyun 			.has_zl10353  = 1,
280*4882a593Smuzhiyun 			.has_eeprom   = 0,
281*4882a593Smuzhiyun 			.has_remote   = 1,
282*4882a593Smuzhiyun 		},
283*4882a593Smuzhiyun 		.gpio = {
284*4882a593Smuzhiyun 			.tuner_reset	= TM6000_GPIO_4,
285*4882a593Smuzhiyun 		},
286*4882a593Smuzhiyun 		.vinput = { {
287*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
288*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
289*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC1,
290*4882a593Smuzhiyun 			}, {
291*4882a593Smuzhiyun 			.type	= TM6000_INPUT_COMPOSITE1,
292*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_A,
293*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
294*4882a593Smuzhiyun 			}, {
295*4882a593Smuzhiyun 			.type	= TM6000_INPUT_SVIDEO,
296*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_AB,
297*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
298*4882a593Smuzhiyun 			},
299*4882a593Smuzhiyun 		},
300*4882a593Smuzhiyun 	},
301*4882a593Smuzhiyun 	[TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = {
302*4882a593Smuzhiyun 		.name         = "ADSTECH Mini Dual TV USB",
303*4882a593Smuzhiyun 		.tuner_type   = TUNER_XC2028, /* has a XC3028 */
304*4882a593Smuzhiyun 		.tuner_addr   = 0xc8 >> 1,
305*4882a593Smuzhiyun 		.demod_addr   = 0x1e >> 1,
306*4882a593Smuzhiyun 		.caps = {
307*4882a593Smuzhiyun 			.has_tuner    = 1,
308*4882a593Smuzhiyun 			.has_dvb      = 1,
309*4882a593Smuzhiyun 			.has_zl10353  = 1,
310*4882a593Smuzhiyun 			.has_eeprom   = 0,
311*4882a593Smuzhiyun 		},
312*4882a593Smuzhiyun 		.gpio = {
313*4882a593Smuzhiyun 			.tuner_reset	= TM6000_GPIO_4,
314*4882a593Smuzhiyun 		},
315*4882a593Smuzhiyun 		.vinput = { {
316*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
317*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
318*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC1,
319*4882a593Smuzhiyun 			}, {
320*4882a593Smuzhiyun 			.type	= TM6000_INPUT_COMPOSITE1,
321*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_A,
322*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
323*4882a593Smuzhiyun 			}, {
324*4882a593Smuzhiyun 			.type	= TM6000_INPUT_SVIDEO,
325*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_AB,
326*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
327*4882a593Smuzhiyun 			},
328*4882a593Smuzhiyun 		},
329*4882a593Smuzhiyun 	},
330*4882a593Smuzhiyun 	[TM6010_BOARD_HAUPPAUGE_900H] = {
331*4882a593Smuzhiyun 		.name         = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick",
332*4882a593Smuzhiyun 		.eename       = { 'H', 0, 'V', 0, 'R', 0, '9', 0, '0', 0, '0', 0, 'H', 0 },
333*4882a593Smuzhiyun 		.eename_size  = 14,
334*4882a593Smuzhiyun 		.eename_pos   = 0x42,
335*4882a593Smuzhiyun 		.tuner_type   = TUNER_XC2028, /* has a XC3028 */
336*4882a593Smuzhiyun 		.tuner_addr   = 0xc2 >> 1,
337*4882a593Smuzhiyun 		.demod_addr   = 0x1e >> 1,
338*4882a593Smuzhiyun 		.type         = TM6010,
339*4882a593Smuzhiyun 		.ir_codes = RC_MAP_HAUPPAUGE,
340*4882a593Smuzhiyun 		.caps = {
341*4882a593Smuzhiyun 			.has_tuner    = 1,
342*4882a593Smuzhiyun 			.has_dvb      = 1,
343*4882a593Smuzhiyun 			.has_zl10353  = 1,
344*4882a593Smuzhiyun 			.has_eeprom   = 1,
345*4882a593Smuzhiyun 			.has_remote   = 1,
346*4882a593Smuzhiyun 		},
347*4882a593Smuzhiyun 		.gpio = {
348*4882a593Smuzhiyun 			.tuner_reset	= TM6010_GPIO_2,
349*4882a593Smuzhiyun 			.tuner_on	= TM6010_GPIO_3,
350*4882a593Smuzhiyun 			.demod_reset	= TM6010_GPIO_1,
351*4882a593Smuzhiyun 			.demod_on	= TM6010_GPIO_4,
352*4882a593Smuzhiyun 			.power_led	= TM6010_GPIO_7,
353*4882a593Smuzhiyun 			.dvb_led	= TM6010_GPIO_5,
354*4882a593Smuzhiyun 			.ir		= TM6010_GPIO_0,
355*4882a593Smuzhiyun 		},
356*4882a593Smuzhiyun 		.vinput = { {
357*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
358*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
359*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_SIF1,
360*4882a593Smuzhiyun 			}, {
361*4882a593Smuzhiyun 			.type	= TM6000_INPUT_COMPOSITE1,
362*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_A,
363*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
364*4882a593Smuzhiyun 			}, {
365*4882a593Smuzhiyun 			.type	= TM6000_INPUT_SVIDEO,
366*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_AB,
367*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
368*4882a593Smuzhiyun 			},
369*4882a593Smuzhiyun 		},
370*4882a593Smuzhiyun 	},
371*4882a593Smuzhiyun 	[TM6010_BOARD_BEHOLD_WANDER] = {
372*4882a593Smuzhiyun 		.name         = "Beholder Wander DVB-T/TV/FM USB2.0",
373*4882a593Smuzhiyun 		.tuner_type   = TUNER_XC5000,
374*4882a593Smuzhiyun 		.tuner_addr   = 0xc2 >> 1,
375*4882a593Smuzhiyun 		.demod_addr   = 0x1e >> 1,
376*4882a593Smuzhiyun 		.type         = TM6010,
377*4882a593Smuzhiyun 		.caps = {
378*4882a593Smuzhiyun 			.has_tuner      = 1,
379*4882a593Smuzhiyun 			.has_dvb        = 1,
380*4882a593Smuzhiyun 			.has_zl10353    = 1,
381*4882a593Smuzhiyun 			.has_eeprom     = 1,
382*4882a593Smuzhiyun 			.has_remote     = 1,
383*4882a593Smuzhiyun 			.has_radio	= 1,
384*4882a593Smuzhiyun 		},
385*4882a593Smuzhiyun 		.gpio = {
386*4882a593Smuzhiyun 			.tuner_reset	= TM6010_GPIO_0,
387*4882a593Smuzhiyun 			.demod_reset	= TM6010_GPIO_1,
388*4882a593Smuzhiyun 			.power_led	= TM6010_GPIO_6,
389*4882a593Smuzhiyun 		},
390*4882a593Smuzhiyun 		.vinput = { {
391*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
392*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
393*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_SIF1,
394*4882a593Smuzhiyun 			}, {
395*4882a593Smuzhiyun 			.type	= TM6000_INPUT_COMPOSITE1,
396*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_A,
397*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
398*4882a593Smuzhiyun 			}, {
399*4882a593Smuzhiyun 			.type	= TM6000_INPUT_SVIDEO,
400*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_AB,
401*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
402*4882a593Smuzhiyun 			},
403*4882a593Smuzhiyun 		},
404*4882a593Smuzhiyun 		.rinput = {
405*4882a593Smuzhiyun 			.type	= TM6000_INPUT_RADIO,
406*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC1,
407*4882a593Smuzhiyun 		},
408*4882a593Smuzhiyun 	},
409*4882a593Smuzhiyun 	[TM6010_BOARD_BEHOLD_VOYAGER] = {
410*4882a593Smuzhiyun 		.name         = "Beholder Voyager TV/FM USB2.0",
411*4882a593Smuzhiyun 		.tuner_type   = TUNER_XC5000,
412*4882a593Smuzhiyun 		.tuner_addr   = 0xc2 >> 1,
413*4882a593Smuzhiyun 		.type         = TM6010,
414*4882a593Smuzhiyun 		.caps = {
415*4882a593Smuzhiyun 			.has_tuner      = 1,
416*4882a593Smuzhiyun 			.has_dvb        = 0,
417*4882a593Smuzhiyun 			.has_zl10353    = 0,
418*4882a593Smuzhiyun 			.has_eeprom     = 1,
419*4882a593Smuzhiyun 			.has_remote     = 1,
420*4882a593Smuzhiyun 			.has_radio	= 1,
421*4882a593Smuzhiyun 		},
422*4882a593Smuzhiyun 		.gpio = {
423*4882a593Smuzhiyun 			.tuner_reset	= TM6010_GPIO_0,
424*4882a593Smuzhiyun 			.power_led	= TM6010_GPIO_6,
425*4882a593Smuzhiyun 		},
426*4882a593Smuzhiyun 		.vinput = { {
427*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
428*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
429*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_SIF1,
430*4882a593Smuzhiyun 			}, {
431*4882a593Smuzhiyun 			.type	= TM6000_INPUT_COMPOSITE1,
432*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_A,
433*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
434*4882a593Smuzhiyun 			}, {
435*4882a593Smuzhiyun 			.type	= TM6000_INPUT_SVIDEO,
436*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_AB,
437*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
438*4882a593Smuzhiyun 			},
439*4882a593Smuzhiyun 		},
440*4882a593Smuzhiyun 		.rinput = {
441*4882a593Smuzhiyun 			.type	= TM6000_INPUT_RADIO,
442*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC1,
443*4882a593Smuzhiyun 		},
444*4882a593Smuzhiyun 	},
445*4882a593Smuzhiyun 	[TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = {
446*4882a593Smuzhiyun 		.name         = "Terratec Cinergy Hybrid XE / Cinergy Hybrid-Stick",
447*4882a593Smuzhiyun 		.tuner_type   = TUNER_XC2028, /* has a XC3028 */
448*4882a593Smuzhiyun 		.tuner_addr   = 0xc2 >> 1,
449*4882a593Smuzhiyun 		.demod_addr   = 0x1e >> 1,
450*4882a593Smuzhiyun 		.type         = TM6010,
451*4882a593Smuzhiyun 		.caps = {
452*4882a593Smuzhiyun 			.has_tuner    = 1,
453*4882a593Smuzhiyun 			.has_dvb      = 1,
454*4882a593Smuzhiyun 			.has_zl10353  = 1,
455*4882a593Smuzhiyun 			.has_eeprom   = 1,
456*4882a593Smuzhiyun 			.has_remote   = 1,
457*4882a593Smuzhiyun 			.has_radio    = 1,
458*4882a593Smuzhiyun 		},
459*4882a593Smuzhiyun 		.gpio = {
460*4882a593Smuzhiyun 			.tuner_reset	= TM6010_GPIO_2,
461*4882a593Smuzhiyun 			.tuner_on	= TM6010_GPIO_3,
462*4882a593Smuzhiyun 			.demod_reset	= TM6010_GPIO_1,
463*4882a593Smuzhiyun 			.demod_on	= TM6010_GPIO_4,
464*4882a593Smuzhiyun 			.power_led	= TM6010_GPIO_7,
465*4882a593Smuzhiyun 			.dvb_led	= TM6010_GPIO_5,
466*4882a593Smuzhiyun 			.ir		= TM6010_GPIO_0,
467*4882a593Smuzhiyun 		},
468*4882a593Smuzhiyun 		.ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
469*4882a593Smuzhiyun 		.vinput = { {
470*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
471*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
472*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_SIF1,
473*4882a593Smuzhiyun 			}, {
474*4882a593Smuzhiyun 			.type	= TM6000_INPUT_COMPOSITE1,
475*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_A,
476*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
477*4882a593Smuzhiyun 			}, {
478*4882a593Smuzhiyun 			.type	= TM6000_INPUT_SVIDEO,
479*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_AB,
480*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
481*4882a593Smuzhiyun 			},
482*4882a593Smuzhiyun 		},
483*4882a593Smuzhiyun 		.rinput = {
484*4882a593Smuzhiyun 			.type = TM6000_INPUT_RADIO,
485*4882a593Smuzhiyun 			.amux = TM6000_AMUX_SIF1,
486*4882a593Smuzhiyun 		},
487*4882a593Smuzhiyun 	},
488*4882a593Smuzhiyun 	[TM5600_BOARD_TERRATEC_GRABSTER] = {
489*4882a593Smuzhiyun 		.name         = "Terratec Grabster AV 150/250 MX",
490*4882a593Smuzhiyun 		.type         = TM5600,
491*4882a593Smuzhiyun 		.tuner_type   = TUNER_ABSENT,
492*4882a593Smuzhiyun 		.vinput = { {
493*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
494*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
495*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC1,
496*4882a593Smuzhiyun 			}, {
497*4882a593Smuzhiyun 			.type	= TM6000_INPUT_COMPOSITE1,
498*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_A,
499*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
500*4882a593Smuzhiyun 			}, {
501*4882a593Smuzhiyun 			.type	= TM6000_INPUT_SVIDEO,
502*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_AB,
503*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
504*4882a593Smuzhiyun 			},
505*4882a593Smuzhiyun 		},
506*4882a593Smuzhiyun 	},
507*4882a593Smuzhiyun 	[TM6010_BOARD_TWINHAN_TU501] = {
508*4882a593Smuzhiyun 		.name         = "Twinhan TU501(704D1)",
509*4882a593Smuzhiyun 		.tuner_type   = TUNER_XC2028, /* has a XC3028 */
510*4882a593Smuzhiyun 		.tuner_addr   = 0xc2 >> 1,
511*4882a593Smuzhiyun 		.demod_addr   = 0x1e >> 1,
512*4882a593Smuzhiyun 		.type         = TM6010,
513*4882a593Smuzhiyun 		.caps = {
514*4882a593Smuzhiyun 			.has_tuner    = 1,
515*4882a593Smuzhiyun 			.has_dvb      = 1,
516*4882a593Smuzhiyun 			.has_zl10353  = 1,
517*4882a593Smuzhiyun 			.has_eeprom   = 1,
518*4882a593Smuzhiyun 			.has_remote   = 1,
519*4882a593Smuzhiyun 		},
520*4882a593Smuzhiyun 		.gpio = {
521*4882a593Smuzhiyun 			.tuner_reset	= TM6010_GPIO_2,
522*4882a593Smuzhiyun 			.tuner_on	= TM6010_GPIO_3,
523*4882a593Smuzhiyun 			.demod_reset	= TM6010_GPIO_1,
524*4882a593Smuzhiyun 			.demod_on	= TM6010_GPIO_4,
525*4882a593Smuzhiyun 			.power_led	= TM6010_GPIO_7,
526*4882a593Smuzhiyun 			.dvb_led	= TM6010_GPIO_5,
527*4882a593Smuzhiyun 			.ir		= TM6010_GPIO_0,
528*4882a593Smuzhiyun 		},
529*4882a593Smuzhiyun 		.vinput = { {
530*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
531*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
532*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_SIF1,
533*4882a593Smuzhiyun 			}, {
534*4882a593Smuzhiyun 			.type	= TM6000_INPUT_COMPOSITE1,
535*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_A,
536*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
537*4882a593Smuzhiyun 			}, {
538*4882a593Smuzhiyun 			.type	= TM6000_INPUT_SVIDEO,
539*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_AB,
540*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC2,
541*4882a593Smuzhiyun 			},
542*4882a593Smuzhiyun 		},
543*4882a593Smuzhiyun 	},
544*4882a593Smuzhiyun 	[TM6010_BOARD_BEHOLD_WANDER_LITE] = {
545*4882a593Smuzhiyun 		.name         = "Beholder Wander Lite DVB-T/TV/FM USB2.0",
546*4882a593Smuzhiyun 		.tuner_type   = TUNER_XC5000,
547*4882a593Smuzhiyun 		.tuner_addr   = 0xc2 >> 1,
548*4882a593Smuzhiyun 		.demod_addr   = 0x1e >> 1,
549*4882a593Smuzhiyun 		.type         = TM6010,
550*4882a593Smuzhiyun 		.caps = {
551*4882a593Smuzhiyun 			.has_tuner      = 1,
552*4882a593Smuzhiyun 			.has_dvb        = 1,
553*4882a593Smuzhiyun 			.has_zl10353    = 1,
554*4882a593Smuzhiyun 			.has_eeprom     = 1,
555*4882a593Smuzhiyun 			.has_remote     = 0,
556*4882a593Smuzhiyun 			.has_radio	= 1,
557*4882a593Smuzhiyun 		},
558*4882a593Smuzhiyun 		.gpio = {
559*4882a593Smuzhiyun 			.tuner_reset	= TM6010_GPIO_0,
560*4882a593Smuzhiyun 			.demod_reset	= TM6010_GPIO_1,
561*4882a593Smuzhiyun 			.power_led	= TM6010_GPIO_6,
562*4882a593Smuzhiyun 		},
563*4882a593Smuzhiyun 		.vinput = { {
564*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
565*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
566*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_SIF1,
567*4882a593Smuzhiyun 			},
568*4882a593Smuzhiyun 		},
569*4882a593Smuzhiyun 		.rinput = {
570*4882a593Smuzhiyun 			.type	= TM6000_INPUT_RADIO,
571*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC1,
572*4882a593Smuzhiyun 		},
573*4882a593Smuzhiyun 	},
574*4882a593Smuzhiyun 	[TM6010_BOARD_BEHOLD_VOYAGER_LITE] = {
575*4882a593Smuzhiyun 		.name         = "Beholder Voyager Lite TV/FM USB2.0",
576*4882a593Smuzhiyun 		.tuner_type   = TUNER_XC5000,
577*4882a593Smuzhiyun 		.tuner_addr   = 0xc2 >> 1,
578*4882a593Smuzhiyun 		.type         = TM6010,
579*4882a593Smuzhiyun 		.caps = {
580*4882a593Smuzhiyun 			.has_tuner      = 1,
581*4882a593Smuzhiyun 			.has_dvb        = 0,
582*4882a593Smuzhiyun 			.has_zl10353    = 0,
583*4882a593Smuzhiyun 			.has_eeprom     = 1,
584*4882a593Smuzhiyun 			.has_remote     = 0,
585*4882a593Smuzhiyun 			.has_radio	= 1,
586*4882a593Smuzhiyun 		},
587*4882a593Smuzhiyun 		.gpio = {
588*4882a593Smuzhiyun 			.tuner_reset	= TM6010_GPIO_0,
589*4882a593Smuzhiyun 			.power_led	= TM6010_GPIO_6,
590*4882a593Smuzhiyun 		},
591*4882a593Smuzhiyun 		.vinput = { {
592*4882a593Smuzhiyun 			.type	= TM6000_INPUT_TV,
593*4882a593Smuzhiyun 			.vmux	= TM6000_VMUX_VIDEO_B,
594*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_SIF1,
595*4882a593Smuzhiyun 			},
596*4882a593Smuzhiyun 		},
597*4882a593Smuzhiyun 		.rinput = {
598*4882a593Smuzhiyun 			.type	= TM6000_INPUT_RADIO,
599*4882a593Smuzhiyun 			.amux	= TM6000_AMUX_ADC1,
600*4882a593Smuzhiyun 		},
601*4882a593Smuzhiyun 	},
602*4882a593Smuzhiyun };
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun /* table of devices that work with this driver */
605*4882a593Smuzhiyun static const struct usb_device_id tm6000_id_table[] = {
606*4882a593Smuzhiyun 	{ USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC },
607*4882a593Smuzhiyun 	{ USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC },
608*4882a593Smuzhiyun 	{ USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV },
609*4882a593Smuzhiyun 	{ USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR },
610*4882a593Smuzhiyun 	{ USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV },
611*4882a593Smuzhiyun 	{ USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
612*4882a593Smuzhiyun 	{ USB_DEVICE(0x2040, 0x6601), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
613*4882a593Smuzhiyun 	{ USB_DEVICE(0x2040, 0x6610), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
614*4882a593Smuzhiyun 	{ USB_DEVICE(0x2040, 0x6611), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
615*4882a593Smuzhiyun 	{ USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER },
616*4882a593Smuzhiyun 	{ USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER },
617*4882a593Smuzhiyun 	{ USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
618*4882a593Smuzhiyun 	{ USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
619*4882a593Smuzhiyun 	{ USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER },
620*4882a593Smuzhiyun 	{ USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
621*4882a593Smuzhiyun 	{ USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
622*4882a593Smuzhiyun 	{ USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
623*4882a593Smuzhiyun 	{ USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
624*4882a593Smuzhiyun 	{ USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE },
625*4882a593Smuzhiyun 	{ USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE },
626*4882a593Smuzhiyun 	{ }
627*4882a593Smuzhiyun };
628*4882a593Smuzhiyun MODULE_DEVICE_TABLE(usb, tm6000_id_table);
629*4882a593Smuzhiyun 
630*4882a593Smuzhiyun /* Control power led for show some activity */
tm6000_flash_led(struct tm6000_core * dev,u8 state)631*4882a593Smuzhiyun void tm6000_flash_led(struct tm6000_core *dev, u8 state)
632*4882a593Smuzhiyun {
633*4882a593Smuzhiyun 	/* Power LED unconfigured */
634*4882a593Smuzhiyun 	if (!dev->gpio.power_led)
635*4882a593Smuzhiyun 		return;
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun 	/* ON Power LED */
638*4882a593Smuzhiyun 	if (state) {
639*4882a593Smuzhiyun 		switch (dev->model) {
640*4882a593Smuzhiyun 		case TM6010_BOARD_HAUPPAUGE_900H:
641*4882a593Smuzhiyun 		case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
642*4882a593Smuzhiyun 		case TM6010_BOARD_TWINHAN_TU501:
643*4882a593Smuzhiyun 			tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
644*4882a593Smuzhiyun 				dev->gpio.power_led, 0x00);
645*4882a593Smuzhiyun 			break;
646*4882a593Smuzhiyun 		case TM6010_BOARD_BEHOLD_WANDER:
647*4882a593Smuzhiyun 		case TM6010_BOARD_BEHOLD_VOYAGER:
648*4882a593Smuzhiyun 		case TM6010_BOARD_BEHOLD_WANDER_LITE:
649*4882a593Smuzhiyun 		case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
650*4882a593Smuzhiyun 			tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
651*4882a593Smuzhiyun 				dev->gpio.power_led, 0x01);
652*4882a593Smuzhiyun 			break;
653*4882a593Smuzhiyun 		}
654*4882a593Smuzhiyun 	}
655*4882a593Smuzhiyun 	/* OFF Power LED */
656*4882a593Smuzhiyun 	else {
657*4882a593Smuzhiyun 		switch (dev->model) {
658*4882a593Smuzhiyun 		case TM6010_BOARD_HAUPPAUGE_900H:
659*4882a593Smuzhiyun 		case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
660*4882a593Smuzhiyun 		case TM6010_BOARD_TWINHAN_TU501:
661*4882a593Smuzhiyun 			tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
662*4882a593Smuzhiyun 				dev->gpio.power_led, 0x01);
663*4882a593Smuzhiyun 			break;
664*4882a593Smuzhiyun 		case TM6010_BOARD_BEHOLD_WANDER:
665*4882a593Smuzhiyun 		case TM6010_BOARD_BEHOLD_VOYAGER:
666*4882a593Smuzhiyun 		case TM6010_BOARD_BEHOLD_WANDER_LITE:
667*4882a593Smuzhiyun 		case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
668*4882a593Smuzhiyun 			tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
669*4882a593Smuzhiyun 				dev->gpio.power_led, 0x00);
670*4882a593Smuzhiyun 			break;
671*4882a593Smuzhiyun 		}
672*4882a593Smuzhiyun 	}
673*4882a593Smuzhiyun }
674*4882a593Smuzhiyun 
675*4882a593Smuzhiyun /* Tuner callback to provide the proper gpio changes needed for xc5000 */
tm6000_xc5000_callback(void * ptr,int component,int command,int arg)676*4882a593Smuzhiyun int tm6000_xc5000_callback(void *ptr, int component, int command, int arg)
677*4882a593Smuzhiyun {
678*4882a593Smuzhiyun 	int rc = 0;
679*4882a593Smuzhiyun 	struct tm6000_core *dev = ptr;
680*4882a593Smuzhiyun 
681*4882a593Smuzhiyun 	if (dev->tuner_type != TUNER_XC5000)
682*4882a593Smuzhiyun 		return 0;
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun 	switch (command) {
685*4882a593Smuzhiyun 	case XC5000_TUNER_RESET:
686*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
687*4882a593Smuzhiyun 			       dev->gpio.tuner_reset, 0x01);
688*4882a593Smuzhiyun 		msleep(15);
689*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
690*4882a593Smuzhiyun 			       dev->gpio.tuner_reset, 0x00);
691*4882a593Smuzhiyun 		msleep(15);
692*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
693*4882a593Smuzhiyun 			       dev->gpio.tuner_reset, 0x01);
694*4882a593Smuzhiyun 		break;
695*4882a593Smuzhiyun 	}
696*4882a593Smuzhiyun 	return rc;
697*4882a593Smuzhiyun }
698*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tm6000_xc5000_callback);
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun /* Tuner callback to provide the proper gpio changes needed for xc2028 */
701*4882a593Smuzhiyun 
tm6000_tuner_callback(void * ptr,int component,int command,int arg)702*4882a593Smuzhiyun int tm6000_tuner_callback(void *ptr, int component, int command, int arg)
703*4882a593Smuzhiyun {
704*4882a593Smuzhiyun 	int rc = 0;
705*4882a593Smuzhiyun 	struct tm6000_core *dev = ptr;
706*4882a593Smuzhiyun 
707*4882a593Smuzhiyun 	if (dev->tuner_type != TUNER_XC2028)
708*4882a593Smuzhiyun 		return 0;
709*4882a593Smuzhiyun 
710*4882a593Smuzhiyun 	switch (command) {
711*4882a593Smuzhiyun 	case XC2028_RESET_CLK:
712*4882a593Smuzhiyun 		tm6000_ir_wait(dev, 0);
713*4882a593Smuzhiyun 
714*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
715*4882a593Smuzhiyun 					0x02, arg);
716*4882a593Smuzhiyun 		msleep(10);
717*4882a593Smuzhiyun 		rc = tm6000_i2c_reset(dev, 10);
718*4882a593Smuzhiyun 		break;
719*4882a593Smuzhiyun 	case XC2028_TUNER_RESET:
720*4882a593Smuzhiyun 		/* Reset codes during load firmware */
721*4882a593Smuzhiyun 		switch (arg) {
722*4882a593Smuzhiyun 		case 0:
723*4882a593Smuzhiyun 			/* newer tuner can faster reset */
724*4882a593Smuzhiyun 			switch (dev->model) {
725*4882a593Smuzhiyun 			case TM5600_BOARD_10MOONS_UT821:
726*4882a593Smuzhiyun 				tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
727*4882a593Smuzhiyun 					       dev->gpio.tuner_reset, 0x01);
728*4882a593Smuzhiyun 				tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
729*4882a593Smuzhiyun 					       0x300, 0x01);
730*4882a593Smuzhiyun 				msleep(10);
731*4882a593Smuzhiyun 				tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
732*4882a593Smuzhiyun 					       dev->gpio.tuner_reset, 0x00);
733*4882a593Smuzhiyun 				tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
734*4882a593Smuzhiyun 					       0x300, 0x00);
735*4882a593Smuzhiyun 				msleep(10);
736*4882a593Smuzhiyun 				tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
737*4882a593Smuzhiyun 					       dev->gpio.tuner_reset, 0x01);
738*4882a593Smuzhiyun 				tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
739*4882a593Smuzhiyun 					       0x300, 0x01);
740*4882a593Smuzhiyun 				break;
741*4882a593Smuzhiyun 			case TM6010_BOARD_HAUPPAUGE_900H:
742*4882a593Smuzhiyun 			case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
743*4882a593Smuzhiyun 			case TM6010_BOARD_TWINHAN_TU501:
744*4882a593Smuzhiyun 				tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
745*4882a593Smuzhiyun 					       dev->gpio.tuner_reset, 0x01);
746*4882a593Smuzhiyun 				msleep(60);
747*4882a593Smuzhiyun 				tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
748*4882a593Smuzhiyun 					       dev->gpio.tuner_reset, 0x00);
749*4882a593Smuzhiyun 				msleep(75);
750*4882a593Smuzhiyun 				tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
751*4882a593Smuzhiyun 					       dev->gpio.tuner_reset, 0x01);
752*4882a593Smuzhiyun 				msleep(60);
753*4882a593Smuzhiyun 				break;
754*4882a593Smuzhiyun 			default:
755*4882a593Smuzhiyun 				tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
756*4882a593Smuzhiyun 					       dev->gpio.tuner_reset, 0x00);
757*4882a593Smuzhiyun 				msleep(130);
758*4882a593Smuzhiyun 				tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
759*4882a593Smuzhiyun 					       dev->gpio.tuner_reset, 0x01);
760*4882a593Smuzhiyun 				msleep(130);
761*4882a593Smuzhiyun 				break;
762*4882a593Smuzhiyun 			}
763*4882a593Smuzhiyun 
764*4882a593Smuzhiyun 			tm6000_ir_wait(dev, 1);
765*4882a593Smuzhiyun 			break;
766*4882a593Smuzhiyun 		case 1:
767*4882a593Smuzhiyun 			tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
768*4882a593Smuzhiyun 						0x02, 0x01);
769*4882a593Smuzhiyun 			msleep(10);
770*4882a593Smuzhiyun 			break;
771*4882a593Smuzhiyun 		case 2:
772*4882a593Smuzhiyun 			rc = tm6000_i2c_reset(dev, 100);
773*4882a593Smuzhiyun 			break;
774*4882a593Smuzhiyun 		}
775*4882a593Smuzhiyun 		break;
776*4882a593Smuzhiyun 	case XC2028_I2C_FLUSH:
777*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_50_SET_START, 0, 0);
778*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0);
779*4882a593Smuzhiyun 		break;
780*4882a593Smuzhiyun 	}
781*4882a593Smuzhiyun 	return rc;
782*4882a593Smuzhiyun }
783*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(tm6000_tuner_callback);
784*4882a593Smuzhiyun 
tm6000_cards_setup(struct tm6000_core * dev)785*4882a593Smuzhiyun int tm6000_cards_setup(struct tm6000_core *dev)
786*4882a593Smuzhiyun {
787*4882a593Smuzhiyun 	/*
788*4882a593Smuzhiyun 	 * Board-specific initialization sequence. Handles all GPIO
789*4882a593Smuzhiyun 	 * initialization sequences that are board-specific.
790*4882a593Smuzhiyun 	 * Up to now, all found devices use GPIO1 and GPIO4 at the same way.
791*4882a593Smuzhiyun 	 * Probably, they're all based on some reference device. Due to that,
792*4882a593Smuzhiyun 	 * there's a common routine at the end to handle those GPIO's. Devices
793*4882a593Smuzhiyun 	 * that use different pinups or init sequences can just return at
794*4882a593Smuzhiyun 	 * the board-specific session.
795*4882a593Smuzhiyun 	 */
796*4882a593Smuzhiyun 	switch (dev->model) {
797*4882a593Smuzhiyun 	case TM6010_BOARD_HAUPPAUGE_900H:
798*4882a593Smuzhiyun 	case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
799*4882a593Smuzhiyun 	case TM6010_BOARD_TWINHAN_TU501:
800*4882a593Smuzhiyun 	case TM6010_BOARD_GENERIC:
801*4882a593Smuzhiyun 		/* Turn xceive 3028 on */
802*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01);
803*4882a593Smuzhiyun 		msleep(15);
804*4882a593Smuzhiyun 		/* Turn zarlink zl10353 on */
805*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
806*4882a593Smuzhiyun 		msleep(15);
807*4882a593Smuzhiyun 		/* Reset zarlink zl10353 */
808*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
809*4882a593Smuzhiyun 		msleep(50);
810*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
811*4882a593Smuzhiyun 		msleep(15);
812*4882a593Smuzhiyun 		/* Turn zarlink zl10353 off */
813*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01);
814*4882a593Smuzhiyun 		msleep(15);
815*4882a593Smuzhiyun 		/* ir ? */
816*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01);
817*4882a593Smuzhiyun 		msleep(15);
818*4882a593Smuzhiyun 		/* Power led on (blue) */
819*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00);
820*4882a593Smuzhiyun 		msleep(15);
821*4882a593Smuzhiyun 		/* DVB led off (orange) */
822*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01);
823*4882a593Smuzhiyun 		msleep(15);
824*4882a593Smuzhiyun 		/* Turn zarlink zl10353 on */
825*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
826*4882a593Smuzhiyun 		msleep(15);
827*4882a593Smuzhiyun 		break;
828*4882a593Smuzhiyun 	case TM6010_BOARD_BEHOLD_WANDER:
829*4882a593Smuzhiyun 	case TM6010_BOARD_BEHOLD_WANDER_LITE:
830*4882a593Smuzhiyun 		/* Power led on (blue) */
831*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
832*4882a593Smuzhiyun 		msleep(15);
833*4882a593Smuzhiyun 		/* Reset zarlink zl10353 */
834*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
835*4882a593Smuzhiyun 		msleep(50);
836*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
837*4882a593Smuzhiyun 		msleep(15);
838*4882a593Smuzhiyun 		break;
839*4882a593Smuzhiyun 	case TM6010_BOARD_BEHOLD_VOYAGER:
840*4882a593Smuzhiyun 	case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
841*4882a593Smuzhiyun 		/* Power led on (blue) */
842*4882a593Smuzhiyun 		tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
843*4882a593Smuzhiyun 		msleep(15);
844*4882a593Smuzhiyun 		break;
845*4882a593Smuzhiyun 	default:
846*4882a593Smuzhiyun 		break;
847*4882a593Smuzhiyun 	}
848*4882a593Smuzhiyun 
849*4882a593Smuzhiyun 	/*
850*4882a593Smuzhiyun 	 * Default initialization. Most of the devices seem to use GPIO1
851*4882a593Smuzhiyun 	 * and GPIO4.on the same way, so, this handles the common sequence
852*4882a593Smuzhiyun 	 * used by most devices.
853*4882a593Smuzhiyun 	 * If a device uses a different sequence or different GPIO pins for
854*4882a593Smuzhiyun 	 * reset, just add the code at the board-specific part
855*4882a593Smuzhiyun 	 */
856*4882a593Smuzhiyun 
857*4882a593Smuzhiyun 	if (dev->gpio.tuner_reset) {
858*4882a593Smuzhiyun 		int rc;
859*4882a593Smuzhiyun 		int i;
860*4882a593Smuzhiyun 
861*4882a593Smuzhiyun 		for (i = 0; i < 2; i++) {
862*4882a593Smuzhiyun 			rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
863*4882a593Smuzhiyun 						dev->gpio.tuner_reset, 0x00);
864*4882a593Smuzhiyun 			if (rc < 0) {
865*4882a593Smuzhiyun 				printk(KERN_ERR "Error %i doing tuner reset\n", rc);
866*4882a593Smuzhiyun 				return rc;
867*4882a593Smuzhiyun 			}
868*4882a593Smuzhiyun 
869*4882a593Smuzhiyun 			msleep(10); /* Just to be conservative */
870*4882a593Smuzhiyun 			rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
871*4882a593Smuzhiyun 						dev->gpio.tuner_reset, 0x01);
872*4882a593Smuzhiyun 			if (rc < 0) {
873*4882a593Smuzhiyun 				printk(KERN_ERR "Error %i doing tuner reset\n", rc);
874*4882a593Smuzhiyun 				return rc;
875*4882a593Smuzhiyun 			}
876*4882a593Smuzhiyun 		}
877*4882a593Smuzhiyun 	} else {
878*4882a593Smuzhiyun 		printk(KERN_ERR "Tuner reset is not configured\n");
879*4882a593Smuzhiyun 		return -1;
880*4882a593Smuzhiyun 	}
881*4882a593Smuzhiyun 
882*4882a593Smuzhiyun 	msleep(50);
883*4882a593Smuzhiyun 
884*4882a593Smuzhiyun 	return 0;
885*4882a593Smuzhiyun };
886*4882a593Smuzhiyun 
tm6000_config_tuner(struct tm6000_core * dev)887*4882a593Smuzhiyun static void tm6000_config_tuner(struct tm6000_core *dev)
888*4882a593Smuzhiyun {
889*4882a593Smuzhiyun 	struct tuner_setup tun_setup;
890*4882a593Smuzhiyun 
891*4882a593Smuzhiyun 	/* Load tuner module */
892*4882a593Smuzhiyun 	v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
893*4882a593Smuzhiyun 		"tuner", dev->tuner_addr, NULL);
894*4882a593Smuzhiyun 
895*4882a593Smuzhiyun 	memset(&tun_setup, 0, sizeof(tun_setup));
896*4882a593Smuzhiyun 	tun_setup.type = dev->tuner_type;
897*4882a593Smuzhiyun 	tun_setup.addr = dev->tuner_addr;
898*4882a593Smuzhiyun 
899*4882a593Smuzhiyun 	tun_setup.mode_mask = 0;
900*4882a593Smuzhiyun 	if (dev->caps.has_tuner)
901*4882a593Smuzhiyun 		tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO);
902*4882a593Smuzhiyun 
903*4882a593Smuzhiyun 	switch (dev->tuner_type) {
904*4882a593Smuzhiyun 	case TUNER_XC2028:
905*4882a593Smuzhiyun 		tun_setup.tuner_callback = tm6000_tuner_callback;
906*4882a593Smuzhiyun 		break;
907*4882a593Smuzhiyun 	case TUNER_XC5000:
908*4882a593Smuzhiyun 		tun_setup.tuner_callback = tm6000_xc5000_callback;
909*4882a593Smuzhiyun 		break;
910*4882a593Smuzhiyun 	}
911*4882a593Smuzhiyun 
912*4882a593Smuzhiyun 	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
913*4882a593Smuzhiyun 
914*4882a593Smuzhiyun 	switch (dev->tuner_type) {
915*4882a593Smuzhiyun 	case TUNER_XC2028: {
916*4882a593Smuzhiyun 		struct v4l2_priv_tun_config xc2028_cfg;
917*4882a593Smuzhiyun 		struct xc2028_ctrl ctl;
918*4882a593Smuzhiyun 
919*4882a593Smuzhiyun 		memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
920*4882a593Smuzhiyun 		memset(&ctl, 0, sizeof(ctl));
921*4882a593Smuzhiyun 
922*4882a593Smuzhiyun 		ctl.demod = XC3028_FE_ZARLINK456;
923*4882a593Smuzhiyun 
924*4882a593Smuzhiyun 		xc2028_cfg.tuner = TUNER_XC2028;
925*4882a593Smuzhiyun 		xc2028_cfg.priv  = &ctl;
926*4882a593Smuzhiyun 
927*4882a593Smuzhiyun 		switch (dev->model) {
928*4882a593Smuzhiyun 		case TM6010_BOARD_HAUPPAUGE_900H:
929*4882a593Smuzhiyun 		case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
930*4882a593Smuzhiyun 		case TM6010_BOARD_TWINHAN_TU501:
931*4882a593Smuzhiyun 			ctl.max_len = 80;
932*4882a593Smuzhiyun 			ctl.fname = "xc3028L-v36.fw";
933*4882a593Smuzhiyun 			break;
934*4882a593Smuzhiyun 		default:
935*4882a593Smuzhiyun 			if (dev->dev_type == TM6010)
936*4882a593Smuzhiyun 				ctl.fname = "xc3028-v27.fw";
937*4882a593Smuzhiyun 			else
938*4882a593Smuzhiyun 				ctl.fname = "xc3028-v24.fw";
939*4882a593Smuzhiyun 		}
940*4882a593Smuzhiyun 
941*4882a593Smuzhiyun 		printk(KERN_INFO "Setting firmware parameters for xc2028\n");
942*4882a593Smuzhiyun 		v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
943*4882a593Smuzhiyun 				     &xc2028_cfg);
944*4882a593Smuzhiyun 
945*4882a593Smuzhiyun 		}
946*4882a593Smuzhiyun 		break;
947*4882a593Smuzhiyun 	case TUNER_XC5000:
948*4882a593Smuzhiyun 		{
949*4882a593Smuzhiyun 		struct v4l2_priv_tun_config  xc5000_cfg;
950*4882a593Smuzhiyun 		struct xc5000_config ctl = {
951*4882a593Smuzhiyun 			.i2c_address = dev->tuner_addr,
952*4882a593Smuzhiyun 			.if_khz      = 4570,
953*4882a593Smuzhiyun 			.radio_input = XC5000_RADIO_FM1_MONO,
954*4882a593Smuzhiyun 			};
955*4882a593Smuzhiyun 
956*4882a593Smuzhiyun 		xc5000_cfg.tuner = TUNER_XC5000;
957*4882a593Smuzhiyun 		xc5000_cfg.priv  = &ctl;
958*4882a593Smuzhiyun 
959*4882a593Smuzhiyun 		v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
960*4882a593Smuzhiyun 				     &xc5000_cfg);
961*4882a593Smuzhiyun 		}
962*4882a593Smuzhiyun 		break;
963*4882a593Smuzhiyun 	default:
964*4882a593Smuzhiyun 		printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n");
965*4882a593Smuzhiyun 		break;
966*4882a593Smuzhiyun 	}
967*4882a593Smuzhiyun }
968*4882a593Smuzhiyun 
fill_board_specific_data(struct tm6000_core * dev)969*4882a593Smuzhiyun static int fill_board_specific_data(struct tm6000_core *dev)
970*4882a593Smuzhiyun {
971*4882a593Smuzhiyun 	int rc;
972*4882a593Smuzhiyun 
973*4882a593Smuzhiyun 	dev->dev_type   = tm6000_boards[dev->model].type;
974*4882a593Smuzhiyun 	dev->tuner_type = tm6000_boards[dev->model].tuner_type;
975*4882a593Smuzhiyun 	dev->tuner_addr = tm6000_boards[dev->model].tuner_addr;
976*4882a593Smuzhiyun 
977*4882a593Smuzhiyun 	dev->gpio = tm6000_boards[dev->model].gpio;
978*4882a593Smuzhiyun 
979*4882a593Smuzhiyun 	dev->ir_codes = tm6000_boards[dev->model].ir_codes;
980*4882a593Smuzhiyun 
981*4882a593Smuzhiyun 	dev->demod_addr = tm6000_boards[dev->model].demod_addr;
982*4882a593Smuzhiyun 
983*4882a593Smuzhiyun 	dev->caps = tm6000_boards[dev->model].caps;
984*4882a593Smuzhiyun 
985*4882a593Smuzhiyun 	dev->vinput[0] = tm6000_boards[dev->model].vinput[0];
986*4882a593Smuzhiyun 	dev->vinput[1] = tm6000_boards[dev->model].vinput[1];
987*4882a593Smuzhiyun 	dev->vinput[2] = tm6000_boards[dev->model].vinput[2];
988*4882a593Smuzhiyun 	dev->rinput = tm6000_boards[dev->model].rinput;
989*4882a593Smuzhiyun 
990*4882a593Smuzhiyun 	/* setup per-model quirks */
991*4882a593Smuzhiyun 	switch (dev->model) {
992*4882a593Smuzhiyun 	case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
993*4882a593Smuzhiyun 	case TM6010_BOARD_HAUPPAUGE_900H:
994*4882a593Smuzhiyun 		dev->quirks |= TM6000_QUIRK_NO_USB_DELAY;
995*4882a593Smuzhiyun 		break;
996*4882a593Smuzhiyun 
997*4882a593Smuzhiyun 	default:
998*4882a593Smuzhiyun 		break;
999*4882a593Smuzhiyun 	}
1000*4882a593Smuzhiyun 
1001*4882a593Smuzhiyun 	/* initialize hardware */
1002*4882a593Smuzhiyun 	rc = tm6000_init(dev);
1003*4882a593Smuzhiyun 	if (rc < 0)
1004*4882a593Smuzhiyun 		return rc;
1005*4882a593Smuzhiyun 
1006*4882a593Smuzhiyun 	return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev);
1007*4882a593Smuzhiyun }
1008*4882a593Smuzhiyun 
1009*4882a593Smuzhiyun 
use_alternative_detection_method(struct tm6000_core * dev)1010*4882a593Smuzhiyun static void use_alternative_detection_method(struct tm6000_core *dev)
1011*4882a593Smuzhiyun {
1012*4882a593Smuzhiyun 	int i, model = -1;
1013*4882a593Smuzhiyun 
1014*4882a593Smuzhiyun 	if (!dev->eedata_size)
1015*4882a593Smuzhiyun 		return;
1016*4882a593Smuzhiyun 
1017*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(tm6000_boards); i++) {
1018*4882a593Smuzhiyun 		if (!tm6000_boards[i].eename_size)
1019*4882a593Smuzhiyun 			continue;
1020*4882a593Smuzhiyun 		if (dev->eedata_size < tm6000_boards[i].eename_pos +
1021*4882a593Smuzhiyun 				       tm6000_boards[i].eename_size)
1022*4882a593Smuzhiyun 			continue;
1023*4882a593Smuzhiyun 
1024*4882a593Smuzhiyun 		if (!memcmp(&dev->eedata[tm6000_boards[i].eename_pos],
1025*4882a593Smuzhiyun 			    tm6000_boards[i].eename,
1026*4882a593Smuzhiyun 			    tm6000_boards[i].eename_size)) {
1027*4882a593Smuzhiyun 			model = i;
1028*4882a593Smuzhiyun 			break;
1029*4882a593Smuzhiyun 		}
1030*4882a593Smuzhiyun 	}
1031*4882a593Smuzhiyun 	if (model < 0) {
1032*4882a593Smuzhiyun 		printk(KERN_INFO "Device has eeprom but is currently unknown\n");
1033*4882a593Smuzhiyun 		return;
1034*4882a593Smuzhiyun 	}
1035*4882a593Smuzhiyun 
1036*4882a593Smuzhiyun 	dev->model = model;
1037*4882a593Smuzhiyun 
1038*4882a593Smuzhiyun 	printk(KERN_INFO "Device identified via eeprom as %s (type = %d)\n",
1039*4882a593Smuzhiyun 	       tm6000_boards[model].name, model);
1040*4882a593Smuzhiyun }
1041*4882a593Smuzhiyun 
1042*4882a593Smuzhiyun #if defined(CONFIG_MODULES) && defined(MODULE)
request_module_async(struct work_struct * work)1043*4882a593Smuzhiyun static void request_module_async(struct work_struct *work)
1044*4882a593Smuzhiyun {
1045*4882a593Smuzhiyun 	struct tm6000_core *dev = container_of(work, struct tm6000_core,
1046*4882a593Smuzhiyun 					       request_module_wk);
1047*4882a593Smuzhiyun 
1048*4882a593Smuzhiyun 	request_module("tm6000-alsa");
1049*4882a593Smuzhiyun 
1050*4882a593Smuzhiyun 	if (dev->caps.has_dvb)
1051*4882a593Smuzhiyun 		request_module("tm6000-dvb");
1052*4882a593Smuzhiyun }
1053*4882a593Smuzhiyun 
request_modules(struct tm6000_core * dev)1054*4882a593Smuzhiyun static void request_modules(struct tm6000_core *dev)
1055*4882a593Smuzhiyun {
1056*4882a593Smuzhiyun 	INIT_WORK(&dev->request_module_wk, request_module_async);
1057*4882a593Smuzhiyun 	schedule_work(&dev->request_module_wk);
1058*4882a593Smuzhiyun }
1059*4882a593Smuzhiyun 
flush_request_modules(struct tm6000_core * dev)1060*4882a593Smuzhiyun static void flush_request_modules(struct tm6000_core *dev)
1061*4882a593Smuzhiyun {
1062*4882a593Smuzhiyun 	flush_work(&dev->request_module_wk);
1063*4882a593Smuzhiyun }
1064*4882a593Smuzhiyun #else
1065*4882a593Smuzhiyun #define request_modules(dev)
1066*4882a593Smuzhiyun #define flush_request_modules(dev)
1067*4882a593Smuzhiyun #endif /* CONFIG_MODULES */
1068*4882a593Smuzhiyun 
tm6000_init_dev(struct tm6000_core * dev)1069*4882a593Smuzhiyun static int tm6000_init_dev(struct tm6000_core *dev)
1070*4882a593Smuzhiyun {
1071*4882a593Smuzhiyun 	struct v4l2_frequency f;
1072*4882a593Smuzhiyun 	int rc = 0;
1073*4882a593Smuzhiyun 
1074*4882a593Smuzhiyun 	mutex_init(&dev->lock);
1075*4882a593Smuzhiyun 	mutex_lock(&dev->lock);
1076*4882a593Smuzhiyun 
1077*4882a593Smuzhiyun 	if (!is_generic(dev->model)) {
1078*4882a593Smuzhiyun 		rc = fill_board_specific_data(dev);
1079*4882a593Smuzhiyun 		if (rc < 0)
1080*4882a593Smuzhiyun 			goto err;
1081*4882a593Smuzhiyun 
1082*4882a593Smuzhiyun 		/* register i2c bus */
1083*4882a593Smuzhiyun 		rc = tm6000_i2c_register(dev);
1084*4882a593Smuzhiyun 		if (rc < 0)
1085*4882a593Smuzhiyun 			goto err;
1086*4882a593Smuzhiyun 	} else {
1087*4882a593Smuzhiyun 		/* register i2c bus */
1088*4882a593Smuzhiyun 		rc = tm6000_i2c_register(dev);
1089*4882a593Smuzhiyun 		if (rc < 0)
1090*4882a593Smuzhiyun 			goto err;
1091*4882a593Smuzhiyun 
1092*4882a593Smuzhiyun 		use_alternative_detection_method(dev);
1093*4882a593Smuzhiyun 
1094*4882a593Smuzhiyun 		rc = fill_board_specific_data(dev);
1095*4882a593Smuzhiyun 		if (rc < 0)
1096*4882a593Smuzhiyun 			goto err;
1097*4882a593Smuzhiyun 	}
1098*4882a593Smuzhiyun 
1099*4882a593Smuzhiyun 	/* Default values for STD and resolutions */
1100*4882a593Smuzhiyun 	dev->width = 720;
1101*4882a593Smuzhiyun 	dev->height = 480;
1102*4882a593Smuzhiyun 	dev->norm = V4L2_STD_NTSC_M;
1103*4882a593Smuzhiyun 
1104*4882a593Smuzhiyun 	/* Configure tuner */
1105*4882a593Smuzhiyun 	tm6000_config_tuner(dev);
1106*4882a593Smuzhiyun 
1107*4882a593Smuzhiyun 	/* Set video standard */
1108*4882a593Smuzhiyun 	v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm);
1109*4882a593Smuzhiyun 
1110*4882a593Smuzhiyun 	/* Set tuner frequency - also loads firmware on xc2028/xc3028 */
1111*4882a593Smuzhiyun 	f.tuner = 0;
1112*4882a593Smuzhiyun 	f.type = V4L2_TUNER_ANALOG_TV;
1113*4882a593Smuzhiyun 	f.frequency = 3092;	/* 193.25 MHz */
1114*4882a593Smuzhiyun 	dev->freq = f.frequency;
1115*4882a593Smuzhiyun 	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
1116*4882a593Smuzhiyun 
1117*4882a593Smuzhiyun 	if (dev->caps.has_tda9874)
1118*4882a593Smuzhiyun 		v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
1119*4882a593Smuzhiyun 			"tvaudio", I2C_ADDR_TDA9874, NULL);
1120*4882a593Smuzhiyun 
1121*4882a593Smuzhiyun 	/* register and initialize V4L2 */
1122*4882a593Smuzhiyun 	rc = tm6000_v4l2_register(dev);
1123*4882a593Smuzhiyun 	if (rc < 0)
1124*4882a593Smuzhiyun 		goto err;
1125*4882a593Smuzhiyun 
1126*4882a593Smuzhiyun 	tm6000_add_into_devlist(dev);
1127*4882a593Smuzhiyun 	tm6000_init_extension(dev);
1128*4882a593Smuzhiyun 
1129*4882a593Smuzhiyun 	tm6000_ir_init(dev);
1130*4882a593Smuzhiyun 
1131*4882a593Smuzhiyun 	request_modules(dev);
1132*4882a593Smuzhiyun 
1133*4882a593Smuzhiyun 	mutex_unlock(&dev->lock);
1134*4882a593Smuzhiyun 	return 0;
1135*4882a593Smuzhiyun 
1136*4882a593Smuzhiyun err:
1137*4882a593Smuzhiyun 	mutex_unlock(&dev->lock);
1138*4882a593Smuzhiyun 	return rc;
1139*4882a593Smuzhiyun }
1140*4882a593Smuzhiyun 
1141*4882a593Smuzhiyun /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
1142*4882a593Smuzhiyun #define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
1143*4882a593Smuzhiyun 
get_max_endpoint(struct usb_device * udev,struct usb_host_interface * alt,char * msgtype,struct usb_host_endpoint * curr_e,struct tm6000_endpoint * tm_ep)1144*4882a593Smuzhiyun static void get_max_endpoint(struct usb_device *udev,
1145*4882a593Smuzhiyun 			     struct usb_host_interface *alt,
1146*4882a593Smuzhiyun 			     char *msgtype,
1147*4882a593Smuzhiyun 			     struct usb_host_endpoint *curr_e,
1148*4882a593Smuzhiyun 			     struct tm6000_endpoint *tm_ep)
1149*4882a593Smuzhiyun {
1150*4882a593Smuzhiyun 	u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize);
1151*4882a593Smuzhiyun 	unsigned int size = tmp & 0x7ff;
1152*4882a593Smuzhiyun 
1153*4882a593Smuzhiyun 	if (udev->speed == USB_SPEED_HIGH)
1154*4882a593Smuzhiyun 		size = size * hb_mult(tmp);
1155*4882a593Smuzhiyun 
1156*4882a593Smuzhiyun 	if (size > tm_ep->maxsize) {
1157*4882a593Smuzhiyun 		tm_ep->endp = curr_e;
1158*4882a593Smuzhiyun 		tm_ep->maxsize = size;
1159*4882a593Smuzhiyun 		tm_ep->bInterfaceNumber = alt->desc.bInterfaceNumber;
1160*4882a593Smuzhiyun 		tm_ep->bAlternateSetting = alt->desc.bAlternateSetting;
1161*4882a593Smuzhiyun 
1162*4882a593Smuzhiyun 		printk(KERN_INFO "tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n",
1163*4882a593Smuzhiyun 					msgtype, curr_e->desc.bEndpointAddress,
1164*4882a593Smuzhiyun 					size);
1165*4882a593Smuzhiyun 	}
1166*4882a593Smuzhiyun }
1167*4882a593Smuzhiyun 
1168*4882a593Smuzhiyun /*
1169*4882a593Smuzhiyun  * tm6000_usb_probe()
1170*4882a593Smuzhiyun  * checks for supported devices
1171*4882a593Smuzhiyun  */
tm6000_usb_probe(struct usb_interface * interface,const struct usb_device_id * id)1172*4882a593Smuzhiyun static int tm6000_usb_probe(struct usb_interface *interface,
1173*4882a593Smuzhiyun 			    const struct usb_device_id *id)
1174*4882a593Smuzhiyun {
1175*4882a593Smuzhiyun 	struct usb_device *usbdev;
1176*4882a593Smuzhiyun 	struct tm6000_core *dev;
1177*4882a593Smuzhiyun 	int i, rc;
1178*4882a593Smuzhiyun 	int nr = 0;
1179*4882a593Smuzhiyun 	char *speed;
1180*4882a593Smuzhiyun 
1181*4882a593Smuzhiyun 	usbdev = usb_get_dev(interface_to_usbdev(interface));
1182*4882a593Smuzhiyun 
1183*4882a593Smuzhiyun 	/* Selects the proper interface */
1184*4882a593Smuzhiyun 	rc = usb_set_interface(usbdev, 0, 1);
1185*4882a593Smuzhiyun 	if (rc < 0)
1186*4882a593Smuzhiyun 		goto report_failure;
1187*4882a593Smuzhiyun 
1188*4882a593Smuzhiyun 	/* Check to see next free device and mark as used */
1189*4882a593Smuzhiyun 	nr = find_first_zero_bit(&tm6000_devused, TM6000_MAXBOARDS);
1190*4882a593Smuzhiyun 	if (nr >= TM6000_MAXBOARDS) {
1191*4882a593Smuzhiyun 		printk(KERN_ERR "tm6000: Supports only %i tm60xx boards.\n", TM6000_MAXBOARDS);
1192*4882a593Smuzhiyun 		rc = -ENOMEM;
1193*4882a593Smuzhiyun 		goto put_device;
1194*4882a593Smuzhiyun 	}
1195*4882a593Smuzhiyun 
1196*4882a593Smuzhiyun 	/* Create and initialize dev struct */
1197*4882a593Smuzhiyun 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1198*4882a593Smuzhiyun 	if (!dev) {
1199*4882a593Smuzhiyun 		rc = -ENOMEM;
1200*4882a593Smuzhiyun 		goto put_device;
1201*4882a593Smuzhiyun 	}
1202*4882a593Smuzhiyun 	spin_lock_init(&dev->slock);
1203*4882a593Smuzhiyun 	mutex_init(&dev->usb_lock);
1204*4882a593Smuzhiyun 
1205*4882a593Smuzhiyun 	/* Increment usage count */
1206*4882a593Smuzhiyun 	set_bit(nr, &tm6000_devused);
1207*4882a593Smuzhiyun 	snprintf(dev->name, 29, "tm6000 #%d", nr);
1208*4882a593Smuzhiyun 
1209*4882a593Smuzhiyun 	dev->model = id->driver_info;
1210*4882a593Smuzhiyun 	if (card[nr] < ARRAY_SIZE(tm6000_boards))
1211*4882a593Smuzhiyun 		dev->model = card[nr];
1212*4882a593Smuzhiyun 
1213*4882a593Smuzhiyun 	dev->udev = usbdev;
1214*4882a593Smuzhiyun 	dev->devno = nr;
1215*4882a593Smuzhiyun 
1216*4882a593Smuzhiyun 	switch (usbdev->speed) {
1217*4882a593Smuzhiyun 	case USB_SPEED_LOW:
1218*4882a593Smuzhiyun 		speed = "1.5";
1219*4882a593Smuzhiyun 		break;
1220*4882a593Smuzhiyun 	case USB_SPEED_UNKNOWN:
1221*4882a593Smuzhiyun 	case USB_SPEED_FULL:
1222*4882a593Smuzhiyun 		speed = "12";
1223*4882a593Smuzhiyun 		break;
1224*4882a593Smuzhiyun 	case USB_SPEED_HIGH:
1225*4882a593Smuzhiyun 		speed = "480";
1226*4882a593Smuzhiyun 		break;
1227*4882a593Smuzhiyun 	default:
1228*4882a593Smuzhiyun 		speed = "unknown";
1229*4882a593Smuzhiyun 	}
1230*4882a593Smuzhiyun 
1231*4882a593Smuzhiyun 	/* Get endpoints */
1232*4882a593Smuzhiyun 	for (i = 0; i < interface->num_altsetting; i++) {
1233*4882a593Smuzhiyun 		int ep;
1234*4882a593Smuzhiyun 
1235*4882a593Smuzhiyun 		for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
1236*4882a593Smuzhiyun 			struct usb_host_endpoint	*e;
1237*4882a593Smuzhiyun 			int dir_out;
1238*4882a593Smuzhiyun 
1239*4882a593Smuzhiyun 			e = &interface->altsetting[i].endpoint[ep];
1240*4882a593Smuzhiyun 
1241*4882a593Smuzhiyun 			dir_out = ((e->desc.bEndpointAddress &
1242*4882a593Smuzhiyun 					USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
1243*4882a593Smuzhiyun 
1244*4882a593Smuzhiyun 			printk(KERN_INFO "tm6000: alt %d, interface %i, class %i\n",
1245*4882a593Smuzhiyun 			       i,
1246*4882a593Smuzhiyun 			       interface->altsetting[i].desc.bInterfaceNumber,
1247*4882a593Smuzhiyun 			       interface->altsetting[i].desc.bInterfaceClass);
1248*4882a593Smuzhiyun 
1249*4882a593Smuzhiyun 			switch (e->desc.bmAttributes) {
1250*4882a593Smuzhiyun 			case USB_ENDPOINT_XFER_BULK:
1251*4882a593Smuzhiyun 				if (!dir_out) {
1252*4882a593Smuzhiyun 					get_max_endpoint(usbdev,
1253*4882a593Smuzhiyun 							 &interface->altsetting[i],
1254*4882a593Smuzhiyun 							 "Bulk IN", e,
1255*4882a593Smuzhiyun 							 &dev->bulk_in);
1256*4882a593Smuzhiyun 				} else {
1257*4882a593Smuzhiyun 					get_max_endpoint(usbdev,
1258*4882a593Smuzhiyun 							 &interface->altsetting[i],
1259*4882a593Smuzhiyun 							 "Bulk OUT", e,
1260*4882a593Smuzhiyun 							 &dev->bulk_out);
1261*4882a593Smuzhiyun 				}
1262*4882a593Smuzhiyun 				break;
1263*4882a593Smuzhiyun 			case USB_ENDPOINT_XFER_ISOC:
1264*4882a593Smuzhiyun 				if (!dir_out) {
1265*4882a593Smuzhiyun 					get_max_endpoint(usbdev,
1266*4882a593Smuzhiyun 							 &interface->altsetting[i],
1267*4882a593Smuzhiyun 							 "ISOC IN", e,
1268*4882a593Smuzhiyun 							 &dev->isoc_in);
1269*4882a593Smuzhiyun 				} else {
1270*4882a593Smuzhiyun 					get_max_endpoint(usbdev,
1271*4882a593Smuzhiyun 							 &interface->altsetting[i],
1272*4882a593Smuzhiyun 							 "ISOC OUT", e,
1273*4882a593Smuzhiyun 							 &dev->isoc_out);
1274*4882a593Smuzhiyun 				}
1275*4882a593Smuzhiyun 				break;
1276*4882a593Smuzhiyun 			case USB_ENDPOINT_XFER_INT:
1277*4882a593Smuzhiyun 				if (!dir_out) {
1278*4882a593Smuzhiyun 					get_max_endpoint(usbdev,
1279*4882a593Smuzhiyun 							&interface->altsetting[i],
1280*4882a593Smuzhiyun 							"INT IN", e,
1281*4882a593Smuzhiyun 							&dev->int_in);
1282*4882a593Smuzhiyun 				} else {
1283*4882a593Smuzhiyun 					get_max_endpoint(usbdev,
1284*4882a593Smuzhiyun 							&interface->altsetting[i],
1285*4882a593Smuzhiyun 							"INT OUT", e,
1286*4882a593Smuzhiyun 							&dev->int_out);
1287*4882a593Smuzhiyun 				}
1288*4882a593Smuzhiyun 				break;
1289*4882a593Smuzhiyun 			}
1290*4882a593Smuzhiyun 		}
1291*4882a593Smuzhiyun 	}
1292*4882a593Smuzhiyun 
1293*4882a593Smuzhiyun 
1294*4882a593Smuzhiyun 	printk(KERN_INFO "tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n",
1295*4882a593Smuzhiyun 		speed,
1296*4882a593Smuzhiyun 		le16_to_cpu(dev->udev->descriptor.idVendor),
1297*4882a593Smuzhiyun 		le16_to_cpu(dev->udev->descriptor.idProduct),
1298*4882a593Smuzhiyun 		interface->altsetting->desc.bInterfaceNumber);
1299*4882a593Smuzhiyun 
1300*4882a593Smuzhiyun /* check if the the device has the iso in endpoint at the correct place */
1301*4882a593Smuzhiyun 	if (!dev->isoc_in.endp) {
1302*4882a593Smuzhiyun 		printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n");
1303*4882a593Smuzhiyun 		rc = -ENODEV;
1304*4882a593Smuzhiyun 		goto free_device;
1305*4882a593Smuzhiyun 	}
1306*4882a593Smuzhiyun 
1307*4882a593Smuzhiyun 	/* save our data pointer in this interface device */
1308*4882a593Smuzhiyun 	usb_set_intfdata(interface, dev);
1309*4882a593Smuzhiyun 
1310*4882a593Smuzhiyun 	printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name);
1311*4882a593Smuzhiyun 
1312*4882a593Smuzhiyun 	rc = tm6000_init_dev(dev);
1313*4882a593Smuzhiyun 	if (rc < 0)
1314*4882a593Smuzhiyun 		goto free_device;
1315*4882a593Smuzhiyun 
1316*4882a593Smuzhiyun 	return 0;
1317*4882a593Smuzhiyun 
1318*4882a593Smuzhiyun free_device:
1319*4882a593Smuzhiyun 	kfree(dev);
1320*4882a593Smuzhiyun report_failure:
1321*4882a593Smuzhiyun 	printk(KERN_ERR "tm6000: Error %d while registering\n", rc);
1322*4882a593Smuzhiyun 
1323*4882a593Smuzhiyun 	clear_bit(nr, &tm6000_devused);
1324*4882a593Smuzhiyun put_device:
1325*4882a593Smuzhiyun 	usb_put_dev(usbdev);
1326*4882a593Smuzhiyun 	return rc;
1327*4882a593Smuzhiyun }
1328*4882a593Smuzhiyun 
1329*4882a593Smuzhiyun /*
1330*4882a593Smuzhiyun  * tm6000_usb_disconnect()
1331*4882a593Smuzhiyun  * called when the device gets disconnected
1332*4882a593Smuzhiyun  * video device will be unregistered on v4l2_close in case it is still open
1333*4882a593Smuzhiyun  */
tm6000_usb_disconnect(struct usb_interface * interface)1334*4882a593Smuzhiyun static void tm6000_usb_disconnect(struct usb_interface *interface)
1335*4882a593Smuzhiyun {
1336*4882a593Smuzhiyun 	struct tm6000_core *dev = usb_get_intfdata(interface);
1337*4882a593Smuzhiyun 	usb_set_intfdata(interface, NULL);
1338*4882a593Smuzhiyun 
1339*4882a593Smuzhiyun 	if (!dev)
1340*4882a593Smuzhiyun 		return;
1341*4882a593Smuzhiyun 
1342*4882a593Smuzhiyun 	printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name);
1343*4882a593Smuzhiyun 
1344*4882a593Smuzhiyun 	flush_request_modules(dev);
1345*4882a593Smuzhiyun 
1346*4882a593Smuzhiyun 	tm6000_ir_fini(dev);
1347*4882a593Smuzhiyun 
1348*4882a593Smuzhiyun 	if (dev->gpio.power_led) {
1349*4882a593Smuzhiyun 		switch (dev->model) {
1350*4882a593Smuzhiyun 		case TM6010_BOARD_HAUPPAUGE_900H:
1351*4882a593Smuzhiyun 		case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
1352*4882a593Smuzhiyun 		case TM6010_BOARD_TWINHAN_TU501:
1353*4882a593Smuzhiyun 			/* Power led off */
1354*4882a593Smuzhiyun 			tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
1355*4882a593Smuzhiyun 				dev->gpio.power_led, 0x01);
1356*4882a593Smuzhiyun 			msleep(15);
1357*4882a593Smuzhiyun 			break;
1358*4882a593Smuzhiyun 		case TM6010_BOARD_BEHOLD_WANDER:
1359*4882a593Smuzhiyun 		case TM6010_BOARD_BEHOLD_VOYAGER:
1360*4882a593Smuzhiyun 		case TM6010_BOARD_BEHOLD_WANDER_LITE:
1361*4882a593Smuzhiyun 		case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
1362*4882a593Smuzhiyun 			/* Power led off */
1363*4882a593Smuzhiyun 			tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
1364*4882a593Smuzhiyun 				dev->gpio.power_led, 0x00);
1365*4882a593Smuzhiyun 			msleep(15);
1366*4882a593Smuzhiyun 			break;
1367*4882a593Smuzhiyun 		}
1368*4882a593Smuzhiyun 	}
1369*4882a593Smuzhiyun 	tm6000_v4l2_unregister(dev);
1370*4882a593Smuzhiyun 
1371*4882a593Smuzhiyun 	tm6000_i2c_unregister(dev);
1372*4882a593Smuzhiyun 
1373*4882a593Smuzhiyun 	v4l2_device_unregister(&dev->v4l2_dev);
1374*4882a593Smuzhiyun 
1375*4882a593Smuzhiyun 	dev->state |= DEV_DISCONNECTED;
1376*4882a593Smuzhiyun 
1377*4882a593Smuzhiyun 	usb_put_dev(dev->udev);
1378*4882a593Smuzhiyun 
1379*4882a593Smuzhiyun 	tm6000_close_extension(dev);
1380*4882a593Smuzhiyun 	tm6000_remove_from_devlist(dev);
1381*4882a593Smuzhiyun 
1382*4882a593Smuzhiyun 	clear_bit(dev->devno, &tm6000_devused);
1383*4882a593Smuzhiyun 	kfree(dev);
1384*4882a593Smuzhiyun }
1385*4882a593Smuzhiyun 
1386*4882a593Smuzhiyun static struct usb_driver tm6000_usb_driver = {
1387*4882a593Smuzhiyun 		.name = "tm6000",
1388*4882a593Smuzhiyun 		.probe = tm6000_usb_probe,
1389*4882a593Smuzhiyun 		.disconnect = tm6000_usb_disconnect,
1390*4882a593Smuzhiyun 		.id_table = tm6000_id_table,
1391*4882a593Smuzhiyun };
1392*4882a593Smuzhiyun 
1393*4882a593Smuzhiyun module_usb_driver(tm6000_usb_driver);
1394*4882a593Smuzhiyun 
1395*4882a593Smuzhiyun MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter");
1396*4882a593Smuzhiyun MODULE_AUTHOR("Mauro Carvalho Chehab");
1397*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
1398