auvitek.c revision 1.3.6.2 1 1.3.6.2 rmind /* $NetBSD: auvitek.c,v 1.3.6.2 2011/03/05 20:54:10 rmind Exp $ */
2 1.3.6.2 rmind
3 1.3.6.2 rmind /*-
4 1.3.6.2 rmind * Copyright (c) 2010 Jared D. McNeill <jmcneill (at) invisible.ca>
5 1.3.6.2 rmind * All rights reserved.
6 1.3.6.2 rmind *
7 1.3.6.2 rmind * Redistribution and use in source and binary forms, with or without
8 1.3.6.2 rmind * modification, are permitted provided that the following conditions
9 1.3.6.2 rmind * are met:
10 1.3.6.2 rmind * 1. Redistributions of source code must retain the above copyright
11 1.3.6.2 rmind * notice, this list of conditions and the following disclaimer.
12 1.3.6.2 rmind * 2. Redistributions in binary form must reproduce the above copyright
13 1.3.6.2 rmind * notice, this list of conditions and the following disclaimer in the
14 1.3.6.2 rmind * documentation and/or other materials provided with the distribution.
15 1.3.6.2 rmind *
16 1.3.6.2 rmind * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.3.6.2 rmind * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.3.6.2 rmind * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.3.6.2 rmind * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.3.6.2 rmind * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.3.6.2 rmind * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.3.6.2 rmind * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.3.6.2 rmind * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.3.6.2 rmind * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.3.6.2 rmind * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.3.6.2 rmind * POSSIBILITY OF SUCH DAMAGE.
27 1.3.6.2 rmind */
28 1.3.6.2 rmind
29 1.3.6.2 rmind /*
30 1.3.6.2 rmind * Auvitek AU0828 USB controller
31 1.3.6.2 rmind */
32 1.3.6.2 rmind
33 1.3.6.2 rmind #include <sys/cdefs.h>
34 1.3.6.2 rmind __KERNEL_RCSID(0, "$NetBSD: auvitek.c,v 1.3.6.2 2011/03/05 20:54:10 rmind Exp $");
35 1.3.6.2 rmind
36 1.3.6.2 rmind #include <sys/types.h>
37 1.3.6.2 rmind #include <sys/param.h>
38 1.3.6.2 rmind #include <sys/systm.h>
39 1.3.6.2 rmind #include <sys/device.h>
40 1.3.6.2 rmind #include <sys/conf.h>
41 1.3.6.2 rmind #include <sys/bus.h>
42 1.3.6.2 rmind #include <sys/module.h>
43 1.3.6.2 rmind
44 1.3.6.2 rmind #include <dev/usb/usb.h>
45 1.3.6.2 rmind #include <dev/usb/usbdi.h>
46 1.3.6.2 rmind #include <dev/usb/usbdi_util.h>
47 1.3.6.2 rmind #include <dev/usb/usbdevs.h>
48 1.3.6.2 rmind
49 1.3.6.2 rmind #include <dev/usb/auvitekreg.h>
50 1.3.6.2 rmind #include <dev/usb/auvitekvar.h>
51 1.3.6.2 rmind
52 1.3.6.2 rmind static int auvitek_match(device_t, cfdata_t, void *);
53 1.3.6.2 rmind static void auvitek_attach(device_t, device_t, void *);
54 1.3.6.2 rmind static int auvitek_detach(device_t, int);
55 1.3.6.2 rmind static void auvitek_childdet(device_t, device_t);
56 1.3.6.2 rmind static int auvitek_activate(device_t, enum devact);
57 1.3.6.2 rmind
58 1.3.6.2 rmind CFATTACH_DECL2_NEW(auvitek, sizeof(struct auvitek_softc),
59 1.3.6.2 rmind auvitek_match, auvitek_attach, auvitek_detach, auvitek_activate,
60 1.3.6.2 rmind NULL, auvitek_childdet);
61 1.3.6.2 rmind
62 1.3.6.2 rmind static const struct {
63 1.3.6.2 rmind uint16_t vendor;
64 1.3.6.2 rmind uint16_t product;
65 1.3.6.2 rmind const char * name;
66 1.3.6.2 rmind enum auvitek_board board;
67 1.3.6.2 rmind } auvitek_devices[] = {
68 1.3.6.2 rmind { 0x2040, 0x7200,
69 1.3.6.2 rmind "WinTV HVR-950Q", AUVITEK_BOARD_HVR_950Q },
70 1.3.6.2 rmind { 0x2040, 0x7240,
71 1.3.6.2 rmind "WinTV HVR-850", AUVITEK_BOARD_HVR_850 },
72 1.3.6.2 rmind };
73 1.3.6.2 rmind
74 1.3.6.2 rmind static int
75 1.3.6.2 rmind auvitek_match(device_t parent, cfdata_t match, void *opaque)
76 1.3.6.2 rmind {
77 1.3.6.2 rmind struct usb_attach_arg *uaa = opaque;
78 1.3.6.2 rmind unsigned int i;
79 1.3.6.2 rmind
80 1.3.6.2 rmind for (i = 0; i < __arraycount(auvitek_devices); i++) {
81 1.3.6.2 rmind if (auvitek_devices[i].vendor == uaa->vendor &&
82 1.3.6.2 rmind auvitek_devices[i].product == uaa->product)
83 1.3.6.2 rmind return UMATCH_VENDOR_PRODUCT;
84 1.3.6.2 rmind }
85 1.3.6.2 rmind
86 1.3.6.2 rmind return UMATCH_NONE;
87 1.3.6.2 rmind }
88 1.3.6.2 rmind
89 1.3.6.2 rmind static void
90 1.3.6.2 rmind auvitek_attach(device_t parent, device_t self, void *opaque)
91 1.3.6.2 rmind {
92 1.3.6.2 rmind struct auvitek_softc *sc = device_private(self);
93 1.3.6.2 rmind struct usb_attach_arg *uaa = opaque;
94 1.3.6.2 rmind usbd_device_handle dev = uaa->device;
95 1.3.6.2 rmind usb_endpoint_descriptor_t *ed;
96 1.3.6.2 rmind usbd_status err;
97 1.3.6.2 rmind unsigned int i;
98 1.3.6.2 rmind uint8_t nep;
99 1.3.6.2 rmind
100 1.3.6.2 rmind aprint_naive("\n");
101 1.3.6.2 rmind aprint_normal(": AU0828\n");
102 1.3.6.2 rmind
103 1.3.6.2 rmind sc->sc_dev = self;
104 1.3.6.2 rmind sc->sc_udev = dev;
105 1.3.6.2 rmind sc->sc_uport = uaa->port;
106 1.3.6.2 rmind
107 1.3.6.2 rmind for (i = 0; i < __arraycount(auvitek_devices); i++) {
108 1.3.6.2 rmind if (auvitek_devices[i].vendor == uaa->vendor &&
109 1.3.6.2 rmind auvitek_devices[i].product == uaa->product)
110 1.3.6.2 rmind break;
111 1.3.6.2 rmind }
112 1.3.6.2 rmind KASSERT(i != __arraycount(auvitek_devices));
113 1.3.6.2 rmind sc->sc_descr = auvitek_devices[i].name;
114 1.3.6.2 rmind sc->sc_board = auvitek_devices[i].board;
115 1.3.6.2 rmind
116 1.3.6.2 rmind sc->sc_dying = sc->sc_running = 0;
117 1.3.6.2 rmind
118 1.3.6.2 rmind mutex_init(&sc->sc_subdev_lock, MUTEX_DEFAULT, IPL_VM);
119 1.3.6.2 rmind
120 1.3.6.2 rmind err = usbd_set_config_index(dev, 0, 1);
121 1.3.6.2 rmind if (err) {
122 1.3.6.2 rmind aprint_error_dev(self, "couldn't set config index: %s\n",
123 1.3.6.2 rmind usbd_errstr(err));
124 1.3.6.2 rmind return;
125 1.3.6.2 rmind }
126 1.3.6.2 rmind err = usbd_device2interface_handle(dev, 0, &sc->sc_iface);
127 1.3.6.2 rmind if (err) {
128 1.3.6.2 rmind aprint_error_dev(self, "couldn't get interface handle: %s\n",
129 1.3.6.2 rmind usbd_errstr(err));
130 1.3.6.2 rmind return;
131 1.3.6.2 rmind }
132 1.3.6.2 rmind
133 1.3.6.2 rmind err = usbd_set_interface(sc->sc_iface, AUVITEK_XFER_ALTNO);
134 1.3.6.2 rmind if (err) {
135 1.3.6.2 rmind aprint_error_dev(self, "couldn't set interface: %s\n",
136 1.3.6.2 rmind usbd_errstr(err));
137 1.3.6.2 rmind return;
138 1.3.6.2 rmind }
139 1.3.6.2 rmind
140 1.3.6.2 rmind nep = 0;
141 1.3.6.2 rmind usbd_endpoint_count(sc->sc_iface, &nep);
142 1.3.6.2 rmind sc->sc_ax.ax_sc = sc;
143 1.3.6.2 rmind sc->sc_ax.ax_endpt = -1;
144 1.3.6.2 rmind for (i = 0; i < nep; i++) {
145 1.3.6.2 rmind int dir, type;
146 1.3.6.2 rmind
147 1.3.6.2 rmind ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
148 1.3.6.2 rmind if (ed == NULL) {
149 1.3.6.2 rmind aprint_error_dev(self,
150 1.3.6.2 rmind "couldn't read endpoint descriptor %d\n", i);
151 1.3.6.2 rmind continue;
152 1.3.6.2 rmind }
153 1.3.6.2 rmind
154 1.3.6.2 rmind dir = UE_GET_DIR(ed->bEndpointAddress);
155 1.3.6.2 rmind type = UE_GET_XFERTYPE(ed->bmAttributes);
156 1.3.6.2 rmind
157 1.3.6.2 rmind if (dir == UE_DIR_IN && type == UE_ISOCHRONOUS) {
158 1.3.6.2 rmind sc->sc_ax.ax_endpt = ed->bEndpointAddress;
159 1.3.6.2 rmind sc->sc_ax.ax_maxpktlen =
160 1.3.6.2 rmind UE_GET_SIZE(UGETW(ed->wMaxPacketSize)) *
161 1.3.6.2 rmind (UE_GET_TRANS(UGETW(ed->wMaxPacketSize)) + 1);
162 1.3.6.2 rmind break;
163 1.3.6.2 rmind }
164 1.3.6.2 rmind }
165 1.3.6.2 rmind err = usbd_set_interface(sc->sc_iface, 0);
166 1.3.6.2 rmind if (err) {
167 1.3.6.2 rmind aprint_error_dev(self, "couldn't set interface: %s\n",
168 1.3.6.2 rmind usbd_errstr(err));
169 1.3.6.2 rmind sc->sc_dying = 1;
170 1.3.6.2 rmind return;
171 1.3.6.2 rmind }
172 1.3.6.2 rmind if (sc->sc_ax.ax_endpt == -1) {
173 1.3.6.2 rmind aprint_error_dev(self, "couldn't find isoc endpoint\n");
174 1.3.6.2 rmind sc->sc_dying = 1;
175 1.3.6.2 rmind return;
176 1.3.6.2 rmind }
177 1.3.6.2 rmind if (sc->sc_ax.ax_maxpktlen == 0) {
178 1.3.6.2 rmind aprint_error_dev(self, "couldn't determine packet length\n");
179 1.3.6.2 rmind sc->sc_dying = 1;
180 1.3.6.2 rmind return;
181 1.3.6.2 rmind }
182 1.3.6.2 rmind
183 1.3.6.2 rmind aprint_debug_dev(self, "isoc endpoint 0x%02x size %d\n",
184 1.3.6.2 rmind sc->sc_ax.ax_endpt, sc->sc_ax.ax_maxpktlen);
185 1.3.6.2 rmind
186 1.3.6.2 rmind auvitek_board_init(sc);
187 1.3.6.2 rmind
188 1.3.6.2 rmind auvitek_i2c_attach(sc);
189 1.3.6.2 rmind
190 1.3.6.2 rmind sc->sc_au8522 = au8522_open(self, &sc->sc_i2c, 0x8e >> 1);
191 1.3.6.2 rmind if (sc->sc_au8522 == NULL) {
192 1.3.6.2 rmind aprint_error_dev(sc->sc_dev, "couldn't initialize decoder\n");
193 1.3.6.2 rmind sc->sc_dying = 1;
194 1.3.6.2 rmind return;
195 1.3.6.2 rmind }
196 1.3.6.2 rmind
197 1.3.6.2 rmind auvitek_video_attach(sc);
198 1.3.6.2 rmind auvitek_audio_attach(sc);
199 1.3.6.2 rmind }
200 1.3.6.2 rmind
201 1.3.6.2 rmind static int
202 1.3.6.2 rmind auvitek_detach(device_t self, int flags)
203 1.3.6.2 rmind {
204 1.3.6.2 rmind struct auvitek_softc *sc = device_private(self);
205 1.3.6.2 rmind
206 1.3.6.2 rmind sc->sc_dying = 1;
207 1.3.6.2 rmind
208 1.3.6.2 rmind pmf_device_deregister(self);
209 1.3.6.2 rmind
210 1.3.6.2 rmind auvitek_audio_detach(sc, flags);
211 1.3.6.2 rmind auvitek_video_detach(sc, flags);
212 1.3.6.2 rmind
213 1.3.6.2 rmind if (sc->sc_xc5k)
214 1.3.6.2 rmind xc5k_close(sc->sc_xc5k);
215 1.3.6.2 rmind if (sc->sc_au8522)
216 1.3.6.2 rmind au8522_close(sc->sc_au8522);
217 1.3.6.2 rmind
218 1.3.6.2 rmind auvitek_i2c_detach(sc, flags);
219 1.3.6.2 rmind
220 1.3.6.2 rmind mutex_destroy(&sc->sc_subdev_lock);
221 1.3.6.2 rmind
222 1.3.6.2 rmind return 0;
223 1.3.6.2 rmind }
224 1.3.6.2 rmind
225 1.3.6.2 rmind int
226 1.3.6.2 rmind auvitek_activate(device_t self, enum devact act)
227 1.3.6.2 rmind {
228 1.3.6.2 rmind struct auvitek_softc *sc = device_private(self);
229 1.3.6.2 rmind
230 1.3.6.2 rmind switch (act) {
231 1.3.6.2 rmind case DVACT_DEACTIVATE:
232 1.3.6.2 rmind sc->sc_dying = 1;
233 1.3.6.2 rmind return 0;
234 1.3.6.2 rmind default:
235 1.3.6.2 rmind return 0;
236 1.3.6.2 rmind }
237 1.3.6.2 rmind }
238 1.3.6.2 rmind
239 1.3.6.2 rmind static void
240 1.3.6.2 rmind auvitek_childdet(device_t self, device_t child)
241 1.3.6.2 rmind {
242 1.3.6.2 rmind struct auvitek_softc *sc = device_private(self);
243 1.3.6.2 rmind
244 1.3.6.2 rmind auvitek_video_childdet(sc, child);
245 1.3.6.2 rmind }
246 1.3.6.2 rmind
247 1.3.6.2 rmind uint8_t
248 1.3.6.2 rmind auvitek_read_1(struct auvitek_softc *sc, uint16_t reg)
249 1.3.6.2 rmind {
250 1.3.6.2 rmind usb_device_request_t req;
251 1.3.6.2 rmind usbd_status err;
252 1.3.6.2 rmind int actlen;
253 1.3.6.2 rmind uint8_t data;
254 1.3.6.2 rmind
255 1.3.6.2 rmind req.bmRequestType = UT_READ_VENDOR_DEVICE;
256 1.3.6.2 rmind req.bRequest = AU0828_CMD_REQUEST_IN;
257 1.3.6.2 rmind USETW(req.wValue, 0);
258 1.3.6.2 rmind USETW(req.wIndex, reg);
259 1.3.6.2 rmind USETW(req.wLength, sizeof(data));
260 1.3.6.2 rmind
261 1.3.6.2 rmind err = usbd_do_request_flags(sc->sc_udev, &req, &data, 0,
262 1.3.6.2 rmind &actlen, USBD_DEFAULT_TIMEOUT);
263 1.3.6.2 rmind if (err)
264 1.3.6.2 rmind printf("%s: read failed: %s\n", device_xname(sc->sc_dev),
265 1.3.6.2 rmind usbd_errstr(err));
266 1.3.6.2 rmind
267 1.3.6.2 rmind return data;
268 1.3.6.2 rmind }
269 1.3.6.2 rmind
270 1.3.6.2 rmind void
271 1.3.6.2 rmind auvitek_write_1(struct auvitek_softc *sc, uint16_t reg, uint8_t data)
272 1.3.6.2 rmind {
273 1.3.6.2 rmind usb_device_request_t req;
274 1.3.6.2 rmind usbd_status err;
275 1.3.6.2 rmind int actlen;
276 1.3.6.2 rmind
277 1.3.6.2 rmind req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
278 1.3.6.2 rmind req.bRequest = AU0828_CMD_REQUEST_OUT;
279 1.3.6.2 rmind USETW(req.wValue, data);
280 1.3.6.2 rmind USETW(req.wIndex, reg);
281 1.3.6.2 rmind USETW(req.wLength, 0);
282 1.3.6.2 rmind
283 1.3.6.2 rmind err = usbd_do_request_flags(sc->sc_udev, &req, NULL, 0,
284 1.3.6.2 rmind &actlen, USBD_DEFAULT_TIMEOUT);
285 1.3.6.2 rmind if (err)
286 1.3.6.2 rmind printf("%s: write failed: %s\n", device_xname(sc->sc_dev),
287 1.3.6.2 rmind usbd_errstr(err));
288 1.3.6.2 rmind }
289 1.3.6.2 rmind
290 1.3.6.2 rmind MODULE(MODULE_CLASS_DRIVER, auvitek, "au8522,xc5k");
291 1.3.6.2 rmind
292 1.3.6.2 rmind #ifdef _MODULE
293 1.3.6.2 rmind #include "ioconf.c"
294 1.3.6.2 rmind #endif
295 1.3.6.2 rmind
296 1.3.6.2 rmind static int
297 1.3.6.2 rmind auvitek_modcmd(modcmd_t cmd, void *opaque)
298 1.3.6.2 rmind {
299 1.3.6.2 rmind switch (cmd) {
300 1.3.6.2 rmind case MODULE_CMD_INIT:
301 1.3.6.2 rmind #ifdef _MODULE
302 1.3.6.2 rmind return config_init_component(cfdriver_ioconf_auvitek,
303 1.3.6.2 rmind cfattach_ioconf_auvitek, cfdata_ioconf_auvitek);
304 1.3.6.2 rmind #else
305 1.3.6.2 rmind return 0;
306 1.3.6.2 rmind #endif
307 1.3.6.2 rmind case MODULE_CMD_FINI:
308 1.3.6.2 rmind #ifdef _MODULE
309 1.3.6.2 rmind return config_fini_component(cfdriver_ioconf_auvitek,
310 1.3.6.2 rmind cfattach_ioconf_auvitek, cfdata_ioconf_auvitek);
311 1.3.6.2 rmind #else
312 1.3.6.2 rmind return 0;
313 1.3.6.2 rmind #endif
314 1.3.6.2 rmind default:
315 1.3.6.2 rmind return ENOTTY;
316 1.3.6.2 rmind }
317 1.3.6.2 rmind }
318