Commit | Line | Data |
---|---|---|
265a6510 ST |
1 | /* |
2 | * Driver for the Auvitek USB bridge | |
3 | * | |
6d897616 | 4 | * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> |
265a6510 ST |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
20 | */ | |
21 | ||
22 | #include "au0828.h" | |
23 | #include "au0828-cards.h" | |
8b2f0795 | 24 | #include "au8522.h" |
f1add5b5 DH |
25 | #include "media/tuner.h" |
26 | #include "media/v4l2-common.h" | |
8b2f0795 | 27 | |
a094ca46 | 28 | static void hvr950q_cs5340_audio(void *priv, int enable) |
8b2f0795 DH |
29 | { |
30 | /* Because the HVR-950q shares an i2s bus between the cs5340 and the | |
31 | au8522, we need to hold cs5340 in reset when using the au8522 */ | |
32 | struct au0828_dev *dev = priv; | |
33 | if (enable == 1) | |
34 | au0828_set(dev, REG_000, 0x10); | |
35 | else | |
36 | au0828_clear(dev, REG_000, 0x10); | |
37 | } | |
265a6510 | 38 | |
265a6510 ST |
39 | struct au0828_board au0828_boards[] = { |
40 | [AU0828_BOARD_UNKNOWN] = { | |
41 | .name = "Unknown board", | |
f1add5b5 DH |
42 | .tuner_type = UNSET, |
43 | .tuner_addr = ADDR_UNSET, | |
265a6510 ST |
44 | }, |
45 | [AU0828_BOARD_HAUPPAUGE_HVR850] = { | |
46 | .name = "Hauppauge HVR850", | |
f1add5b5 DH |
47 | .tuner_type = TUNER_XC5000, |
48 | .tuner_addr = 0x61, | |
21dc61d3 | 49 | .i2c_clk_divider = AU0828_I2C_CLK_20KHZ, |
7fdd7c72 DH |
50 | .input = { |
51 | { | |
52 | .type = AU0828_VMUX_TELEVISION, | |
53 | .vmux = AU8522_COMPOSITE_CH4_SIF, | |
54 | .amux = AU8522_AUDIO_SIF, | |
55 | }, | |
56 | { | |
57 | .type = AU0828_VMUX_COMPOSITE, | |
58 | .vmux = AU8522_COMPOSITE_CH1, | |
59 | .amux = AU8522_AUDIO_NONE, | |
60 | .audio_setup = hvr950q_cs5340_audio, | |
61 | }, | |
62 | { | |
63 | .type = AU0828_VMUX_SVIDEO, | |
64 | .vmux = AU8522_SVIDEO_CH13, | |
65 | .amux = AU8522_AUDIO_NONE, | |
66 | .audio_setup = hvr950q_cs5340_audio, | |
67 | }, | |
68 | }, | |
265a6510 ST |
69 | }, |
70 | [AU0828_BOARD_HAUPPAUGE_HVR950Q] = { | |
71 | .name = "Hauppauge HVR950Q", | |
f1add5b5 DH |
72 | .tuner_type = TUNER_XC5000, |
73 | .tuner_addr = 0x61, | |
16af6f5a DH |
74 | /* The au0828 hardware i2c implementation does not properly |
75 | support the xc5000's i2c clock stretching. So we need to | |
76 | lower the clock frequency enough where the 15us clock | |
77 | stretch fits inside of a normal clock cycle, or else the | |
78 | au0828 fails to set the STOP bit. A 30 KHz clock puts the | |
79 | clock pulse width at 18us */ | |
21dc61d3 | 80 | .i2c_clk_divider = AU0828_I2C_CLK_20KHZ, |
8b2f0795 DH |
81 | .input = { |
82 | { | |
83 | .type = AU0828_VMUX_TELEVISION, | |
84 | .vmux = AU8522_COMPOSITE_CH4_SIF, | |
85 | .amux = AU8522_AUDIO_SIF, | |
86 | }, | |
87 | { | |
88 | .type = AU0828_VMUX_COMPOSITE, | |
89 | .vmux = AU8522_COMPOSITE_CH1, | |
90 | .amux = AU8522_AUDIO_NONE, | |
91 | .audio_setup = hvr950q_cs5340_audio, | |
92 | }, | |
93 | { | |
94 | .type = AU0828_VMUX_SVIDEO, | |
95 | .vmux = AU8522_SVIDEO_CH13, | |
96 | .amux = AU8522_AUDIO_NONE, | |
97 | .audio_setup = hvr950q_cs5340_audio, | |
98 | }, | |
99 | }, | |
265a6510 | 100 | }, |
59d27521 MK |
101 | [AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL] = { |
102 | .name = "Hauppauge HVR950Q rev xxF8", | |
f1add5b5 DH |
103 | .tuner_type = UNSET, |
104 | .tuner_addr = ADDR_UNSET, | |
16af6f5a | 105 | .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, |
59d27521 | 106 | }, |
265a6510 ST |
107 | [AU0828_BOARD_DVICO_FUSIONHDTV7] = { |
108 | .name = "DViCO FusionHDTV USB", | |
f1add5b5 DH |
109 | .tuner_type = UNSET, |
110 | .tuner_addr = ADDR_UNSET, | |
7a1dd50b | 111 | .i2c_clk_divider = AU0828_I2C_CLK_20KHZ, |
265a6510 | 112 | }, |
8e8bd229 MK |
113 | [AU0828_BOARD_HAUPPAUGE_WOODBURY] = { |
114 | .name = "Hauppauge Woodbury", | |
f1add5b5 DH |
115 | .tuner_type = UNSET, |
116 | .tuner_addr = ADDR_UNSET, | |
16af6f5a | 117 | .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, |
8e8bd229 | 118 | }, |
265a6510 | 119 | }; |
265a6510 ST |
120 | |
121 | /* Tuner callback function for au0828 boards. Currently only needed | |
122 | * for HVR1500Q, which has an xc5000 tuner. | |
123 | */ | |
d7cba043 | 124 | int au0828_tuner_callback(void *priv, int component, int command, int arg) |
265a6510 ST |
125 | { |
126 | struct au0828_dev *dev = priv; | |
127 | ||
f07e8e4b | 128 | dprintk(1, "%s()\n", __func__); |
bc3c613c | 129 | |
f1add5b5 | 130 | switch (dev->boardnr) { |
265a6510 ST |
131 | case AU0828_BOARD_HAUPPAUGE_HVR850: |
132 | case AU0828_BOARD_HAUPPAUGE_HVR950Q: | |
59d27521 | 133 | case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: |
265a6510 | 134 | case AU0828_BOARD_DVICO_FUSIONHDTV7: |
a9c36aad | 135 | if (command == 0) { |
265a6510 ST |
136 | /* Tuner Reset Command from xc5000 */ |
137 | /* Drive the tuner into reset and out */ | |
138 | au0828_clear(dev, REG_001, 2); | |
b25ed9c5 | 139 | mdelay(10); |
265a6510 | 140 | au0828_set(dev, REG_001, 2); |
b25ed9c5 | 141 | mdelay(10); |
265a6510 | 142 | return 0; |
18d73c58 | 143 | } else { |
265a6510 | 144 | printk(KERN_ERR |
f07e8e4b | 145 | "%s(): Unknown command.\n", __func__); |
265a6510 ST |
146 | return -EINVAL; |
147 | } | |
148 | break; | |
149 | } | |
150 | ||
151 | return 0; /* Should never be here */ | |
152 | } | |
153 | ||
28930fa9 ST |
154 | static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) |
155 | { | |
156 | struct tveeprom tv; | |
157 | ||
158 | tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data); | |
f1add5b5 | 159 | dev->board.tuner_type = tv.tuner_type; |
28930fa9 ST |
160 | |
161 | /* Make sure we support the board model */ | |
a9c36aad | 162 | switch (tv.model) { |
104fe9a2 | 163 | case 72000: /* WinTV-HVR950q (Retail, IR, ATSC/QAM */ |
62899a28 | 164 | case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ |
6b536a6c MK |
165 | case 72101: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ |
166 | case 72201: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ | |
62899a28 DH |
167 | case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ |
168 | case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ | |
169 | case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ | |
170 | case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ | |
171 | case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ | |
c70ffd59 MK |
172 | case 72261: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ |
173 | case 72271: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ | |
174 | case 72281: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ | |
62899a28 | 175 | case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */ |
104fe9a2 | 176 | case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ |
28930fa9 ST |
177 | break; |
178 | default: | |
f07e8e4b MK |
179 | printk(KERN_WARNING "%s: warning: " |
180 | "unknown hauppauge model #%d\n", __func__, tv.model); | |
28930fa9 ST |
181 | break; |
182 | } | |
183 | ||
f07e8e4b MK |
184 | printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n", |
185 | __func__, tv.model); | |
28930fa9 ST |
186 | } |
187 | ||
5b7d8de7 MK |
188 | void au0828_card_analog_fe_setup(struct au0828_dev *dev); |
189 | ||
28930fa9 ST |
190 | void au0828_card_setup(struct au0828_dev *dev) |
191 | { | |
28930fa9 ST |
192 | static u8 eeprom[256]; |
193 | ||
f07e8e4b | 194 | dprintk(1, "%s()\n", __func__); |
bc3c613c | 195 | |
f01e0ffd | 196 | dev->board = au0828_boards[dev->boardnr]; |
f1add5b5 | 197 | |
28930fa9 ST |
198 | if (dev->i2c_rc == 0) { |
199 | dev->i2c_client.addr = 0xa0 >> 1; | |
200 | tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom)); | |
201 | } | |
202 | ||
f1add5b5 | 203 | switch (dev->boardnr) { |
28930fa9 ST |
204 | case AU0828_BOARD_HAUPPAUGE_HVR850: |
205 | case AU0828_BOARD_HAUPPAUGE_HVR950Q: | |
59d27521 | 206 | case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: |
8e8bd229 | 207 | case AU0828_BOARD_HAUPPAUGE_WOODBURY: |
28930fa9 ST |
208 | if (dev->i2c_rc == 0) |
209 | hauppauge_eeprom(dev, eeprom+0xa0); | |
210 | break; | |
211 | } | |
f1add5b5 | 212 | |
5b7d8de7 MK |
213 | au0828_card_analog_fe_setup(dev); |
214 | } | |
215 | ||
216 | void au0828_card_analog_fe_setup(struct au0828_dev *dev) | |
217 | { | |
8a4e7866 | 218 | #ifdef CONFIG_VIDEO_AU0828_V4L2 |
5b7d8de7 MK |
219 | struct tuner_setup tun_setup; |
220 | struct v4l2_subdev *sd; | |
221 | unsigned int mode_mask = T_ANALOG_TV; | |
222 | ||
220be77c | 223 | if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) { |
f1add5b5 DH |
224 | /* Load the analog demodulator driver (note this would need to |
225 | be abstracted out if we ever need to support a different | |
226 | demod) */ | |
e6574f2f | 227 | sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, |
9a1f8b34 | 228 | "au8522", 0x8e >> 1, NULL); |
62899a28 DH |
229 | if (sd == NULL) |
230 | printk(KERN_ERR "analog subdev registration failed\n"); | |
f1add5b5 DH |
231 | } |
232 | ||
233 | /* Setup tuners */ | |
234 | if (dev->board.tuner_type != TUNER_ABSENT) { | |
235 | /* Load the tuner module, which does the attach */ | |
e6574f2f | 236 | sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, |
9a1f8b34 | 237 | "tuner", dev->board.tuner_addr, NULL); |
62899a28 DH |
238 | if (sd == NULL) |
239 | printk(KERN_ERR "tuner subdev registration fail\n"); | |
f1add5b5 DH |
240 | |
241 | tun_setup.mode_mask = mode_mask; | |
242 | tun_setup.type = dev->board.tuner_type; | |
243 | tun_setup.addr = dev->board.tuner_addr; | |
244 | tun_setup.tuner_callback = au0828_tuner_callback; | |
2689d3dc DH |
245 | v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, |
246 | &tun_setup); | |
f1add5b5 | 247 | } |
8a4e7866 | 248 | #endif |
28930fa9 ST |
249 | } |
250 | ||
265a6510 ST |
251 | /* |
252 | * The bridge has between 8 and 12 gpios. | |
253 | * Regs 1 and 0 deal with output enables. | |
a9c36aad | 254 | * Regs 3 and 2 deal with direction. |
265a6510 ST |
255 | */ |
256 | void au0828_gpio_setup(struct au0828_dev *dev) | |
257 | { | |
f07e8e4b | 258 | dprintk(1, "%s()\n", __func__); |
bc3c613c | 259 | |
f1add5b5 | 260 | switch (dev->boardnr) { |
265a6510 ST |
261 | case AU0828_BOARD_HAUPPAUGE_HVR850: |
262 | case AU0828_BOARD_HAUPPAUGE_HVR950Q: | |
59d27521 | 263 | case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: |
8e8bd229 | 264 | case AU0828_BOARD_HAUPPAUGE_WOODBURY: |
265a6510 ST |
265 | /* GPIO's |
266 | * 4 - CS5340 | |
267 | * 5 - AU8522 Demodulator | |
268 | * 6 - eeprom W/P | |
8b2f0795 | 269 | * 7 - power supply |
265a6510 ST |
270 | * 9 - XC5000 Tuner |
271 | */ | |
272 | ||
a06b429d DH |
273 | /* Set relevant GPIOs as outputs (leave the EEPROM W/P |
274 | as an input since we will never touch it and it has | |
275 | a pullup) */ | |
265a6510 | 276 | au0828_write(dev, REG_003, 0x02); |
8b2f0795 | 277 | au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10); |
a06b429d DH |
278 | |
279 | /* Into reset */ | |
265a6510 ST |
280 | au0828_write(dev, REG_001, 0x0); |
281 | au0828_write(dev, REG_000, 0x0); | |
a06b429d | 282 | msleep(50); |
265a6510 | 283 | |
a06b429d DH |
284 | /* Bring power supply out of reset */ |
285 | au0828_write(dev, REG_000, 0x80); | |
286 | msleep(50); | |
287 | ||
288 | /* Bring xc5000 and au8522 out of reset (leave the | |
289 | cs5340 in reset until needed) */ | |
290 | au0828_write(dev, REG_001, 0x02); /* xc5000 */ | |
291 | au0828_write(dev, REG_000, 0x80 | 0x20); /* PS + au8522 */ | |
8b2f0795 | 292 | |
265a6510 ST |
293 | msleep(250); |
294 | break; | |
295 | case AU0828_BOARD_DVICO_FUSIONHDTV7: | |
296 | /* GPIO's | |
297 | * 6 - ? | |
298 | * 8 - AU8522 Demodulator | |
299 | * 9 - XC5000 Tuner | |
300 | */ | |
301 | ||
302 | /* Into reset */ | |
303 | au0828_write(dev, REG_003, 0x02); | |
304 | au0828_write(dev, REG_002, 0xa0); | |
305 | au0828_write(dev, REG_001, 0x0); | |
306 | au0828_write(dev, REG_000, 0x0); | |
307 | msleep(100); | |
308 | ||
309 | /* Out of reset */ | |
310 | au0828_write(dev, REG_003, 0x02); | |
311 | au0828_write(dev, REG_002, 0xa0); | |
312 | au0828_write(dev, REG_001, 0x02); | |
313 | au0828_write(dev, REG_000, 0xa0); | |
314 | msleep(250); | |
315 | break; | |
316 | } | |
317 | } | |
318 | ||
319 | /* table of devices that work with this driver */ | |
a8eb912c | 320 | struct usb_device_id au0828_usb_id_table[] = { |
265a6510 ST |
321 | { USB_DEVICE(0x2040, 0x7200), |
322 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
323 | { USB_DEVICE(0x2040, 0x7240), | |
324 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 }, | |
325 | { USB_DEVICE(0x0fe9, 0xd620), | |
326 | .driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 }, | |
104fe9a2 MK |
327 | { USB_DEVICE(0x2040, 0x7210), |
328 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
329 | { USB_DEVICE(0x2040, 0x7217), | |
330 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
331 | { USB_DEVICE(0x2040, 0x721b), | |
332 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
a636da6b MK |
333 | { USB_DEVICE(0x2040, 0x721e), |
334 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
104fe9a2 MK |
335 | { USB_DEVICE(0x2040, 0x721f), |
336 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
337 | { USB_DEVICE(0x2040, 0x7280), | |
338 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
339 | { USB_DEVICE(0x0fd9, 0x0008), | |
340 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
59d27521 MK |
341 | { USB_DEVICE(0x2040, 0x7201), |
342 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, | |
343 | { USB_DEVICE(0x2040, 0x7211), | |
344 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, | |
345 | { USB_DEVICE(0x2040, 0x7281), | |
346 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, | |
e2b710bf IL |
347 | { USB_DEVICE(0x05e1, 0x0480), |
348 | .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY }, | |
8e8bd229 MK |
349 | { USB_DEVICE(0x2040, 0x8200), |
350 | .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY }, | |
23bbba34 MK |
351 | { USB_DEVICE(0x2040, 0x7260), |
352 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
64a6b6cb MK |
353 | { USB_DEVICE(0x2040, 0x7213), |
354 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
ffc80988 MK |
355 | { USB_DEVICE(0x2040, 0x7270), |
356 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
265a6510 ST |
357 | { }, |
358 | }; | |
359 | ||
360 | MODULE_DEVICE_TABLE(usb, au0828_usb_id_table); |