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 DH |
27 | |
28 | void hvr950q_cs5340_audio(void *priv, int enable) | |
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, | |
16af6f5a | 49 | .i2c_clk_divider = AU0828_I2C_CLK_30KHZ, |
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 */ | |
80 | .i2c_clk_divider = AU0828_I2C_CLK_30KHZ, | |
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, | |
16af6f5a | 111 | .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, |
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 DH |
164 | case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ |
165 | case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ | |
166 | case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ | |
167 | case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ | |
168 | case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ | |
169 | case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ | |
170 | case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */ | |
104fe9a2 | 171 | case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ |
28930fa9 ST |
172 | break; |
173 | default: | |
f07e8e4b MK |
174 | printk(KERN_WARNING "%s: warning: " |
175 | "unknown hauppauge model #%d\n", __func__, tv.model); | |
28930fa9 ST |
176 | break; |
177 | } | |
178 | ||
f07e8e4b MK |
179 | printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n", |
180 | __func__, tv.model); | |
28930fa9 ST |
181 | } |
182 | ||
28930fa9 ST |
183 | void au0828_card_setup(struct au0828_dev *dev) |
184 | { | |
28930fa9 | 185 | static u8 eeprom[256]; |
f1add5b5 | 186 | struct tuner_setup tun_setup; |
b14667f3 | 187 | struct v4l2_subdev *sd; |
f1add5b5 DH |
188 | unsigned int mode_mask = T_ANALOG_TV | |
189 | T_DIGITAL_TV; | |
28930fa9 | 190 | |
f07e8e4b | 191 | dprintk(1, "%s()\n", __func__); |
bc3c613c | 192 | |
f1add5b5 DH |
193 | memcpy(&dev->board, &au0828_boards[dev->boardnr], sizeof(dev->board)); |
194 | ||
28930fa9 ST |
195 | if (dev->i2c_rc == 0) { |
196 | dev->i2c_client.addr = 0xa0 >> 1; | |
197 | tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom)); | |
198 | } | |
199 | ||
f1add5b5 | 200 | switch (dev->boardnr) { |
28930fa9 ST |
201 | case AU0828_BOARD_HAUPPAUGE_HVR850: |
202 | case AU0828_BOARD_HAUPPAUGE_HVR950Q: | |
59d27521 | 203 | case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: |
8e8bd229 | 204 | case AU0828_BOARD_HAUPPAUGE_WOODBURY: |
28930fa9 ST |
205 | if (dev->i2c_rc == 0) |
206 | hauppauge_eeprom(dev, eeprom+0xa0); | |
207 | break; | |
208 | } | |
f1add5b5 | 209 | |
220be77c | 210 | if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) { |
f1add5b5 DH |
211 | /* Load the analog demodulator driver (note this would need to |
212 | be abstracted out if we ever need to support a different | |
213 | demod) */ | |
e6574f2f HV |
214 | sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, |
215 | "au8522", "au8522", 0x8e >> 1); | |
62899a28 DH |
216 | if (sd == NULL) |
217 | printk(KERN_ERR "analog subdev registration failed\n"); | |
f1add5b5 DH |
218 | } |
219 | ||
220 | /* Setup tuners */ | |
221 | if (dev->board.tuner_type != TUNER_ABSENT) { | |
222 | /* Load the tuner module, which does the attach */ | |
e6574f2f HV |
223 | sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, |
224 | "tuner", "tuner", dev->board.tuner_addr); | |
62899a28 DH |
225 | if (sd == NULL) |
226 | printk(KERN_ERR "tuner subdev registration fail\n"); | |
f1add5b5 DH |
227 | |
228 | tun_setup.mode_mask = mode_mask; | |
229 | tun_setup.type = dev->board.tuner_type; | |
230 | tun_setup.addr = dev->board.tuner_addr; | |
231 | tun_setup.tuner_callback = au0828_tuner_callback; | |
2689d3dc DH |
232 | v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, |
233 | &tun_setup); | |
f1add5b5 | 234 | } |
28930fa9 ST |
235 | } |
236 | ||
265a6510 ST |
237 | /* |
238 | * The bridge has between 8 and 12 gpios. | |
239 | * Regs 1 and 0 deal with output enables. | |
a9c36aad | 240 | * Regs 3 and 2 deal with direction. |
265a6510 ST |
241 | */ |
242 | void au0828_gpio_setup(struct au0828_dev *dev) | |
243 | { | |
f07e8e4b | 244 | dprintk(1, "%s()\n", __func__); |
bc3c613c | 245 | |
f1add5b5 | 246 | switch (dev->boardnr) { |
265a6510 ST |
247 | case AU0828_BOARD_HAUPPAUGE_HVR850: |
248 | case AU0828_BOARD_HAUPPAUGE_HVR950Q: | |
59d27521 | 249 | case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: |
8e8bd229 | 250 | case AU0828_BOARD_HAUPPAUGE_WOODBURY: |
265a6510 ST |
251 | /* GPIO's |
252 | * 4 - CS5340 | |
253 | * 5 - AU8522 Demodulator | |
254 | * 6 - eeprom W/P | |
8b2f0795 | 255 | * 7 - power supply |
265a6510 ST |
256 | * 9 - XC5000 Tuner |
257 | */ | |
258 | ||
259 | /* Into reset */ | |
260 | au0828_write(dev, REG_003, 0x02); | |
8b2f0795 | 261 | au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10); |
265a6510 ST |
262 | au0828_write(dev, REG_001, 0x0); |
263 | au0828_write(dev, REG_000, 0x0); | |
264 | msleep(100); | |
265 | ||
8b2f0795 | 266 | /* Out of reset (leave the cs5340 in reset until needed) */ |
265a6510 ST |
267 | au0828_write(dev, REG_003, 0x02); |
268 | au0828_write(dev, REG_001, 0x02); | |
8b2f0795 DH |
269 | au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10); |
270 | au0828_write(dev, REG_000, 0x80 | 0x40 | 0x20); | |
271 | ||
265a6510 ST |
272 | msleep(250); |
273 | break; | |
274 | case AU0828_BOARD_DVICO_FUSIONHDTV7: | |
275 | /* GPIO's | |
276 | * 6 - ? | |
277 | * 8 - AU8522 Demodulator | |
278 | * 9 - XC5000 Tuner | |
279 | */ | |
280 | ||
281 | /* Into reset */ | |
282 | au0828_write(dev, REG_003, 0x02); | |
283 | au0828_write(dev, REG_002, 0xa0); | |
284 | au0828_write(dev, REG_001, 0x0); | |
285 | au0828_write(dev, REG_000, 0x0); | |
286 | msleep(100); | |
287 | ||
288 | /* Out of reset */ | |
289 | au0828_write(dev, REG_003, 0x02); | |
290 | au0828_write(dev, REG_002, 0xa0); | |
291 | au0828_write(dev, REG_001, 0x02); | |
292 | au0828_write(dev, REG_000, 0xa0); | |
293 | msleep(250); | |
294 | break; | |
295 | } | |
296 | } | |
297 | ||
298 | /* table of devices that work with this driver */ | |
a8eb912c | 299 | struct usb_device_id au0828_usb_id_table[] = { |
265a6510 ST |
300 | { USB_DEVICE(0x2040, 0x7200), |
301 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
302 | { USB_DEVICE(0x2040, 0x7240), | |
303 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 }, | |
304 | { USB_DEVICE(0x0fe9, 0xd620), | |
305 | .driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 }, | |
104fe9a2 MK |
306 | { USB_DEVICE(0x2040, 0x7210), |
307 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
308 | { USB_DEVICE(0x2040, 0x7217), | |
309 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
310 | { USB_DEVICE(0x2040, 0x721b), | |
311 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
a636da6b MK |
312 | { USB_DEVICE(0x2040, 0x721e), |
313 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
104fe9a2 MK |
314 | { USB_DEVICE(0x2040, 0x721f), |
315 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
316 | { USB_DEVICE(0x2040, 0x7280), | |
317 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
318 | { USB_DEVICE(0x0fd9, 0x0008), | |
319 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, | |
59d27521 MK |
320 | { USB_DEVICE(0x2040, 0x7201), |
321 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, | |
322 | { USB_DEVICE(0x2040, 0x7211), | |
323 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, | |
324 | { USB_DEVICE(0x2040, 0x7281), | |
325 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, | |
8e8bd229 MK |
326 | { USB_DEVICE(0x2040, 0x8200), |
327 | .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY }, | |
265a6510 ST |
328 | { }, |
329 | }; | |
330 | ||
331 | MODULE_DEVICE_TABLE(usb, au0828_usb_id_table); |