1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for the Auvitek USB bridge |
4 | * |
5 | * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> |
6 | */ |
7 | |
8 | #include "au0828.h" |
9 | #include "au0828-cards.h" |
10 | #include "au8522.h" |
11 | #include "media/tuner.h" |
12 | #include "media/v4l2-common.h" |
13 | |
14 | static void hvr950q_cs5340_audio(void *priv, int enable) |
15 | { |
16 | /* Because the HVR-950q shares an i2s bus between the cs5340 and the |
17 | au8522, we need to hold cs5340 in reset when using the au8522 */ |
18 | struct au0828_dev *dev = priv; |
19 | if (enable == 1) |
20 | au0828_set(dev, REG_000, 0x10); |
21 | else |
22 | au0828_clear(dev, REG_000, 0x10); |
23 | } |
24 | |
25 | /* |
26 | * WARNING: There's a quirks table at sound/usb/quirks-table.h |
27 | * that should also be updated every time a new device with V4L2 support |
28 | * is added here. |
29 | */ |
30 | struct au0828_board au0828_boards[] = { |
31 | [AU0828_BOARD_UNKNOWN] = { |
32 | .name = "Unknown board" , |
33 | .tuner_type = -1U, |
34 | .tuner_addr = ADDR_UNSET, |
35 | }, |
36 | [AU0828_BOARD_HAUPPAUGE_HVR850] = { |
37 | .name = "Hauppauge HVR850" , |
38 | .tuner_type = TUNER_XC5000, |
39 | .tuner_addr = 0x61, |
40 | .has_ir_i2c = 1, |
41 | .has_analog = 1, |
42 | .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, |
43 | .input = { |
44 | { |
45 | .type = AU0828_VMUX_TELEVISION, |
46 | .vmux = AU8522_COMPOSITE_CH4_SIF, |
47 | .amux = AU8522_AUDIO_SIF, |
48 | }, |
49 | { |
50 | .type = AU0828_VMUX_COMPOSITE, |
51 | .vmux = AU8522_COMPOSITE_CH1, |
52 | .amux = AU8522_AUDIO_NONE, |
53 | .audio_setup = hvr950q_cs5340_audio, |
54 | }, |
55 | { |
56 | .type = AU0828_VMUX_SVIDEO, |
57 | .vmux = AU8522_SVIDEO_CH13, |
58 | .amux = AU8522_AUDIO_NONE, |
59 | .audio_setup = hvr950q_cs5340_audio, |
60 | }, |
61 | }, |
62 | }, |
63 | [AU0828_BOARD_HAUPPAUGE_HVR950Q] = { |
64 | .name = "Hauppauge HVR950Q" , |
65 | .tuner_type = TUNER_XC5000, |
66 | .tuner_addr = 0x61, |
67 | .has_ir_i2c = 1, |
68 | .has_analog = 1, |
69 | .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, |
70 | .input = { |
71 | { |
72 | .type = AU0828_VMUX_TELEVISION, |
73 | .vmux = AU8522_COMPOSITE_CH4_SIF, |
74 | .amux = AU8522_AUDIO_SIF, |
75 | }, |
76 | { |
77 | .type = AU0828_VMUX_COMPOSITE, |
78 | .vmux = AU8522_COMPOSITE_CH1, |
79 | .amux = AU8522_AUDIO_NONE, |
80 | .audio_setup = hvr950q_cs5340_audio, |
81 | }, |
82 | { |
83 | .type = AU0828_VMUX_SVIDEO, |
84 | .vmux = AU8522_SVIDEO_CH13, |
85 | .amux = AU8522_AUDIO_NONE, |
86 | .audio_setup = hvr950q_cs5340_audio, |
87 | }, |
88 | }, |
89 | }, |
90 | [AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL] = { |
91 | .name = "Hauppauge HVR950Q rev xxF8" , |
92 | .tuner_type = TUNER_XC5000, |
93 | .tuner_addr = 0x61, |
94 | .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, |
95 | }, |
96 | [AU0828_BOARD_DVICO_FUSIONHDTV7] = { |
97 | .name = "DViCO FusionHDTV USB" , |
98 | .tuner_type = TUNER_XC5000, |
99 | .tuner_addr = 0x61, |
100 | .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, |
101 | }, |
102 | [AU0828_BOARD_HAUPPAUGE_WOODBURY] = { |
103 | .name = "Hauppauge Woodbury" , |
104 | .tuner_type = TUNER_NXP_TDA18271, |
105 | .tuner_addr = 0x60, |
106 | .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, |
107 | }, |
108 | }; |
109 | |
110 | /* Tuner callback function for au0828 boards. Currently only needed |
111 | * for HVR1500Q, which has an xc5000 tuner. |
112 | */ |
113 | int au0828_tuner_callback(void *priv, int component, int command, int arg) |
114 | { |
115 | struct au0828_dev *dev = priv; |
116 | |
117 | dprintk(1, "%s()\n" , __func__); |
118 | |
119 | switch (dev->boardnr) { |
120 | case AU0828_BOARD_HAUPPAUGE_HVR850: |
121 | case AU0828_BOARD_HAUPPAUGE_HVR950Q: |
122 | case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: |
123 | case AU0828_BOARD_DVICO_FUSIONHDTV7: |
124 | if (command == 0) { |
125 | /* Tuner Reset Command from xc5000 */ |
126 | /* Drive the tuner into reset and out */ |
127 | au0828_clear(dev, REG_001, 2); |
128 | mdelay(10); |
129 | au0828_set(dev, REG_001, 2); |
130 | mdelay(10); |
131 | return 0; |
132 | } else { |
133 | pr_err("%s(): Unknown command.\n" , __func__); |
134 | return -EINVAL; |
135 | } |
136 | break; |
137 | } |
138 | |
139 | return 0; /* Should never be here */ |
140 | } |
141 | |
142 | static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) |
143 | { |
144 | struct tveeprom tv; |
145 | |
146 | tveeprom_hauppauge_analog(tvee: &tv, eeprom_data); |
147 | dev->board.tuner_type = tv.tuner_type; |
148 | |
149 | /* Make sure we support the board model */ |
150 | switch (tv.model) { |
151 | case 72000: /* WinTV-HVR950q (Retail, IR, ATSC/QAM */ |
152 | case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ |
153 | case 72101: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ |
154 | case 72201: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ |
155 | case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ |
156 | case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ |
157 | case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ |
158 | case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ |
159 | case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ |
160 | case 72261: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ |
161 | case 72271: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ |
162 | case 72281: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ |
163 | case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */ |
164 | case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ |
165 | break; |
166 | default: |
167 | pr_warn("%s: warning: unknown hauppauge model #%d\n" , |
168 | __func__, tv.model); |
169 | break; |
170 | } |
171 | |
172 | pr_info("%s: hauppauge eeprom: model=%d\n" , |
173 | __func__, tv.model); |
174 | } |
175 | |
176 | void au0828_card_analog_fe_setup(struct au0828_dev *dev); |
177 | |
178 | void au0828_card_setup(struct au0828_dev *dev) |
179 | { |
180 | static u8 eeprom[256]; |
181 | |
182 | dprintk(1, "%s()\n" , __func__); |
183 | |
184 | if (dev->i2c_rc == 0) { |
185 | dev->i2c_client.addr = 0xa0 >> 1; |
186 | tveeprom_read(c: &dev->i2c_client, eedata: eeprom, len: sizeof(eeprom)); |
187 | } |
188 | |
189 | switch (dev->boardnr) { |
190 | case AU0828_BOARD_HAUPPAUGE_HVR850: |
191 | case AU0828_BOARD_HAUPPAUGE_HVR950Q: |
192 | case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: |
193 | case AU0828_BOARD_HAUPPAUGE_WOODBURY: |
194 | if (dev->i2c_rc == 0) |
195 | hauppauge_eeprom(dev, eeprom_data: eeprom+0xa0); |
196 | break; |
197 | } |
198 | |
199 | au0828_card_analog_fe_setup(dev); |
200 | } |
201 | |
202 | void au0828_card_analog_fe_setup(struct au0828_dev *dev) |
203 | { |
204 | #ifdef CONFIG_VIDEO_AU0828_V4L2 |
205 | struct tuner_setup tun_setup; |
206 | struct v4l2_subdev *sd; |
207 | unsigned int mode_mask = T_ANALOG_TV; |
208 | |
209 | if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) { |
210 | /* Load the analog demodulator driver (note this would need to |
211 | be abstracted out if we ever need to support a different |
212 | demod) */ |
213 | sd = v4l2_i2c_new_subdev(v4l2_dev: &dev->v4l2_dev, adapter: &dev->i2c_adap, |
214 | client_type: "au8522" , addr: 0x8e >> 1, NULL); |
215 | if (sd == NULL) |
216 | pr_err("analog subdev registration failed\n" ); |
217 | } |
218 | |
219 | /* Setup tuners */ |
220 | if (dev->board.tuner_type != TUNER_ABSENT && dev->board.has_analog) { |
221 | /* Load the tuner module, which does the attach */ |
222 | sd = v4l2_i2c_new_subdev(v4l2_dev: &dev->v4l2_dev, adapter: &dev->i2c_adap, |
223 | client_type: "tuner" , addr: dev->board.tuner_addr, NULL); |
224 | if (sd == NULL) |
225 | pr_err("tuner subdev registration fail\n" ); |
226 | |
227 | tun_setup.mode_mask = mode_mask; |
228 | tun_setup.type = dev->board.tuner_type; |
229 | tun_setup.addr = dev->board.tuner_addr; |
230 | tun_setup.tuner_callback = au0828_tuner_callback; |
231 | v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, |
232 | &tun_setup); |
233 | } |
234 | #endif |
235 | } |
236 | |
237 | /* |
238 | * The bridge has between 8 and 12 gpios. |
239 | * Regs 1 and 0 deal with output enables. |
240 | * Regs 3 and 2 deal with direction. |
241 | */ |
242 | void au0828_gpio_setup(struct au0828_dev *dev) |
243 | { |
244 | dprintk(1, "%s()\n" , __func__); |
245 | |
246 | switch (dev->boardnr) { |
247 | case AU0828_BOARD_HAUPPAUGE_HVR850: |
248 | case AU0828_BOARD_HAUPPAUGE_HVR950Q: |
249 | case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: |
250 | case AU0828_BOARD_HAUPPAUGE_WOODBURY: |
251 | /* GPIO's |
252 | * 4 - CS5340 |
253 | * 5 - AU8522 Demodulator |
254 | * 6 - eeprom W/P |
255 | * 7 - power supply |
256 | * 9 - XC5000 Tuner |
257 | */ |
258 | |
259 | /* Set relevant GPIOs as outputs (leave the EEPROM W/P |
260 | as an input since we will never touch it and it has |
261 | a pullup) */ |
262 | au0828_write(dev, REG_003, 0x02); |
263 | au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10); |
264 | |
265 | /* Into reset */ |
266 | au0828_write(dev, REG_001, 0x0); |
267 | au0828_write(dev, REG_000, 0x0); |
268 | msleep(msecs: 50); |
269 | |
270 | /* Bring power supply out of reset */ |
271 | au0828_write(dev, REG_000, 0x80); |
272 | msleep(msecs: 50); |
273 | |
274 | /* Bring xc5000 and au8522 out of reset (leave the |
275 | cs5340 in reset until needed) */ |
276 | au0828_write(dev, REG_001, 0x02); /* xc5000 */ |
277 | au0828_write(dev, REG_000, 0x80 | 0x20); /* PS + au8522 */ |
278 | |
279 | msleep(msecs: 250); |
280 | break; |
281 | case AU0828_BOARD_DVICO_FUSIONHDTV7: |
282 | /* GPIO's |
283 | * 6 - ? |
284 | * 8 - AU8522 Demodulator |
285 | * 9 - XC5000 Tuner |
286 | */ |
287 | |
288 | /* Into reset */ |
289 | au0828_write(dev, REG_003, 0x02); |
290 | au0828_write(dev, REG_002, 0xa0); |
291 | au0828_write(dev, REG_001, 0x0); |
292 | au0828_write(dev, REG_000, 0x0); |
293 | msleep(msecs: 100); |
294 | |
295 | /* Out of reset */ |
296 | au0828_write(dev, REG_003, 0x02); |
297 | au0828_write(dev, REG_002, 0xa0); |
298 | au0828_write(dev, REG_001, 0x02); |
299 | au0828_write(dev, REG_000, 0xa0); |
300 | msleep(msecs: 250); |
301 | break; |
302 | } |
303 | } |
304 | |
305 | /* table of devices that work with this driver */ |
306 | struct usb_device_id au0828_usb_id_table[] = { |
307 | { USB_DEVICE(0x2040, 0x7200), |
308 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, |
309 | { USB_DEVICE(0x2040, 0x7240), |
310 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 }, |
311 | { USB_DEVICE(0x0fe9, 0xd620), |
312 | .driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 }, |
313 | { USB_DEVICE(0x2040, 0x7210), |
314 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, |
315 | { USB_DEVICE(0x2040, 0x7217), |
316 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, |
317 | { USB_DEVICE(0x2040, 0x721b), |
318 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, |
319 | { USB_DEVICE(0x2040, 0x721e), |
320 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, |
321 | { USB_DEVICE(0x2040, 0x721f), |
322 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, |
323 | { USB_DEVICE(0x2040, 0x7280), |
324 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, |
325 | { USB_DEVICE(0x0fd9, 0x0008), |
326 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, |
327 | { USB_DEVICE(0x2040, 0x7201), |
328 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, |
329 | { USB_DEVICE(0x2040, 0x7211), |
330 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, |
331 | { USB_DEVICE(0x2040, 0x7281), |
332 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, |
333 | { USB_DEVICE(0x05e1, 0x0480), |
334 | .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY }, |
335 | { USB_DEVICE(0x2040, 0x8200), |
336 | .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY }, |
337 | { USB_DEVICE(0x2040, 0x7260), |
338 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, |
339 | { USB_DEVICE(0x2040, 0x7213), |
340 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, |
341 | { USB_DEVICE(0x2040, 0x7270), |
342 | .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, |
343 | { }, |
344 | }; |
345 | |
346 | MODULE_DEVICE_TABLE(usb, au0828_usb_id_table); |
347 | |