xref: /netbsd-src/sys/dev/usb/auvitek_dtv.c (revision cef8759bd76c1b621f8eab8faa6f208faabc2e15)
1 /* $NetBSD: auvitek_dtv.c,v 1.7 2016/04/23 10:15:31 skrll Exp $ */
2 
3 /*-
4  * Copyright (c) 2011 Jared D. McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /*
30  * Auvitek AU0828 USB controller (Digital TV function)
31  */
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: auvitek_dtv.c,v 1.7 2016/04/23 10:15:31 skrll Exp $");
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/device.h>
39 #include <sys/conf.h>
40 #include <sys/kmem.h>
41 #include <sys/bus.h>
42 
43 #include <dev/usb/usb.h>
44 #include <dev/usb/usbdi.h>
45 #include <dev/usb/usbdivar.h>
46 #include <dev/usb/usbdi_util.h>
47 #include <dev/usb/usbdevs.h>
48 
49 #include <dev/dtv/dtvif.h>
50 
51 #include <dev/usb/auvitekreg.h>
52 #include <dev/usb/auvitekvar.h>
53 
54 static void		auvitek_dtv_get_devinfo(void *,
55 			    struct dvb_frontend_info *);
56 static int		auvitek_dtv_open(void *, int);
57 static void		auvitek_dtv_close(void *);
58 static int		auvitek_dtv_set_tuner(void *,
59 			    const struct dvb_frontend_parameters *);
60 static fe_status_t	auvitek_dtv_get_status(void *);
61 static uint16_t		auvitek_dtv_get_signal_strength(void *);
62 static uint16_t		auvitek_dtv_get_snr(void *);
63 static int		auvitek_dtv_start_transfer(void *,
64 			    void (*)(void *, const struct dtv_payload *),
65 			    void *);
66 static int		auvitek_dtv_stop_transfer(void *);
67 
68 static int		auvitek_dtv_init_pipes(struct auvitek_softc *);
69 static int		auvitek_dtv_abort_pipes(struct auvitek_softc *);
70 static int		auvitek_dtv_close_pipes(struct auvitek_softc *);
71 
72 static int		auvitek_dtv_bulk_start(struct auvitek_softc *);
73 static int		auvitek_dtv_bulk_start1(struct auvitek_bulk_xfer *);
74 static void		auvitek_dtv_bulk_cb(struct usbd_xfer *, void *,
75 			    usbd_status);
76 
77 static const struct dtv_hw_if auvitek_dtv_if = {
78 	.get_devinfo = auvitek_dtv_get_devinfo,
79 	.open = auvitek_dtv_open,
80 	.close = auvitek_dtv_close,
81 	.set_tuner = auvitek_dtv_set_tuner,
82 	.get_status = auvitek_dtv_get_status,
83 	.get_signal_strength = auvitek_dtv_get_signal_strength,
84 	.get_snr = auvitek_dtv_get_snr,
85 	.start_transfer = auvitek_dtv_start_transfer,
86 	.stop_transfer = auvitek_dtv_stop_transfer,
87 };
88 
89 int
90 auvitek_dtv_attach(struct auvitek_softc *sc)
91 {
92 
93 	auvitek_dtv_rescan(sc, NULL, NULL);
94 
95 	return sc->sc_dtvdev != NULL;
96 }
97 
98 int
99 auvitek_dtv_detach(struct auvitek_softc *sc, int flags)
100 {
101 	if (sc->sc_dtvdev != NULL) {
102 		config_detach(sc->sc_dtvdev, flags);
103 		sc->sc_dtvdev = NULL;
104 	}
105 
106 	return 0;
107 }
108 
109 void
110 auvitek_dtv_rescan(struct auvitek_softc *sc, const char *ifattr,
111     const int *locs)
112 {
113 	struct dtv_attach_args daa;
114 
115 	daa.hw = &auvitek_dtv_if;
116 	daa.priv = sc;
117 
118 	if (ifattr_match(ifattr, "dtvbus") && sc->sc_dtvdev == NULL)
119 		sc->sc_dtvdev = config_found_ia(sc->sc_dev, "dtvbus",
120 		    &daa, dtv_print);
121 }
122 
123 void
124 auvitek_dtv_childdet(struct auvitek_softc *sc, device_t child)
125 {
126 	if (sc->sc_dtvdev == child)
127 		sc->sc_dtvdev = NULL;
128 }
129 
130 static void
131 auvitek_dtv_get_devinfo(void *priv, struct dvb_frontend_info *info)
132 {
133 	struct auvitek_softc *sc = priv;
134 
135 	memset(info, 0, sizeof(*info));
136 	strlcpy(info->name, sc->sc_descr, sizeof(info->name));
137 	info->type = FE_ATSC;
138 	info->frequency_min = 54000000;
139 	info->frequency_max = 858000000;
140 	info->frequency_stepsize = 62500;
141 	info->caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB;
142 }
143 
144 static int
145 auvitek_dtv_open(void *priv, int flags)
146 {
147 	struct auvitek_softc *sc = priv;
148 
149 	if (sc->sc_dying)
150 		return EIO;
151 
152 	auvitek_attach_tuner(sc->sc_dev);
153 	if (sc->sc_xc5k == NULL)
154 		return ENXIO;
155 
156 	int err = auvitek_dtv_init_pipes(sc);
157 	if (err)
158 		return err;
159 
160 	for (size_t i = 0; i < AUVITEK_NBULK_XFERS; i++) {
161 		sc->sc_ab.ab_bx[i].bx_sc = sc;
162 		err = usbd_create_xfer(sc->sc_ab.ab_pipe,
163 		    AUVITEK_BULK_BUFLEN, 0, 0, &sc->sc_ab.ab_bx[i].bx_xfer);
164 		if (err) {
165 			aprint_error_dev(sc->sc_dev,
166 			    "couldn't allocate xfer\n");
167 			sc->sc_dying = 1;
168 			return err;
169 		}
170 		sc->sc_ab.ab_bx[i].bx_buffer = usbd_get_buffer(
171 		    sc->sc_ab.ab_bx[i].bx_xfer);
172 	}
173 
174 
175 	return 0;
176 }
177 
178 static void
179 auvitek_dtv_close(void *priv)
180 {
181 	struct auvitek_softc *sc = priv;
182 
183 	auvitek_dtv_stop_transfer(sc);
184 	auvitek_dtv_abort_pipes(sc);
185 
186 	for (size_t i = 0; i < AUVITEK_NBULK_XFERS; i++) {
187 		if (sc->sc_ab.ab_bx[i].bx_xfer)
188 			usbd_destroy_xfer(sc->sc_ab.ab_bx[i].bx_xfer);
189 	}
190 
191 	auvitek_dtv_close_pipes(sc);
192 
193 	sc->sc_dtvsubmitcb = NULL;
194 	sc->sc_dtvsubmitarg = NULL;
195 }
196 
197 static int
198 auvitek_dtv_set_tuner(void *priv, const struct dvb_frontend_parameters *params)
199 {
200 	struct auvitek_softc *sc = priv;
201 	int error;
202 
203 	error = au8522_set_modulation(sc->sc_au8522, params->u.vsb.modulation);
204 	if (error)
205 		return error;
206 
207 	delay(100000);
208 
209 	au8522_set_gate(sc->sc_au8522, true);
210 	error = xc5k_tune_dtv(sc->sc_xc5k, params);
211 	au8522_set_gate(sc->sc_au8522, false);
212 
213 	return error;
214 }
215 
216 fe_status_t
217 auvitek_dtv_get_status(void *priv)
218 {
219 	struct auvitek_softc *sc = priv;
220 
221 	return au8522_get_dtv_status(sc->sc_au8522);
222 }
223 
224 uint16_t
225 auvitek_dtv_get_signal_strength(void *priv)
226 {
227 	return auvitek_dtv_get_snr(priv);
228 }
229 
230 uint16_t
231 auvitek_dtv_get_snr(void *priv)
232 {
233 	struct auvitek_softc *sc = priv;
234 
235 	return au8522_get_snr(sc->sc_au8522);
236 }
237 
238 static int
239 auvitek_dtv_start_transfer(void *priv,
240     void (*cb)(void *, const struct dtv_payload *), void *arg)
241 {
242 	struct auvitek_softc *sc = priv;
243 	int s;
244 
245 	if (sc->sc_ab.ab_running) {
246 		return 0;
247 	}
248 
249 	sc->sc_dtvsubmitcb = cb;
250 	sc->sc_dtvsubmitarg = arg;
251 
252 	auvitek_write_1(sc, 0x608, 0x90);
253 	auvitek_write_1(sc, 0x609, 0x72);
254 	auvitek_write_1(sc, 0x60a, 0x71);
255 	auvitek_write_1(sc, 0x60b, 0x01);
256 
257 	sc->sc_ab.ab_running = true;
258 
259 	s = splusb();
260 	auvitek_dtv_bulk_start(sc);
261 	splx(s);
262 
263 	return 0;
264 }
265 
266 static int
267 auvitek_dtv_stop_transfer(void *priv)
268 {
269 	struct auvitek_softc *sc = priv;
270 
271 	sc->sc_ab.ab_running = false;
272 
273 	auvitek_write_1(sc, 0x608, 0x00);
274 	auvitek_write_1(sc, 0x609, 0x00);
275 	auvitek_write_1(sc, 0x60a, 0x00);
276 	auvitek_write_1(sc, 0x60b, 0x00);
277 
278 	return 0;
279 }
280 
281 static int
282 auvitek_dtv_init_pipes(struct auvitek_softc *sc)
283 {
284 	usbd_status err;
285 
286 	KERNEL_LOCK(1, curlwp);
287 	err = usbd_open_pipe(sc->sc_bulk_iface, sc->sc_ab.ab_endpt,
288 	    USBD_EXCLUSIVE_USE|USBD_MPSAFE, &sc->sc_ab.ab_pipe);
289 	KERNEL_UNLOCK_ONE(curlwp);
290 
291 	if (err) {
292 		aprint_error_dev(sc->sc_dev, "couldn't open bulk-in pipe: %s\n",
293 		    usbd_errstr(err));
294 		return ENOMEM;
295 	}
296 
297 	return 0;
298 }
299 
300 static int
301 auvitek_dtv_abort_pipes(struct auvitek_softc *sc)
302 {
303 	if (sc->sc_ab.ab_pipe != NULL) {
304 		KERNEL_LOCK(1, curlwp);
305 		usbd_abort_pipe(sc->sc_ab.ab_pipe);
306 		KERNEL_UNLOCK_ONE(curlwp);
307 	}
308 
309 	return 0;
310 }
311 
312 static int
313 auvitek_dtv_close_pipes(struct auvitek_softc *sc)
314 {
315 	if (sc->sc_ab.ab_pipe != NULL) {
316 		KERNEL_LOCK(1, curlwp);
317 		usbd_close_pipe(sc->sc_ab.ab_pipe);
318 		KERNEL_UNLOCK_ONE(curlwp);
319 		sc->sc_ab.ab_pipe = NULL;
320 	}
321 
322 	return 0;
323 }
324 
325 static void
326 auvitek_dtv_bulk_cb(struct usbd_xfer *xfer, void *priv,
327     usbd_status status)
328 {
329 	struct auvitek_bulk_xfer *bx = priv;
330 	struct auvitek_softc *sc = bx->bx_sc;
331 	struct auvitek_bulk *ab = &sc->sc_ab;
332 	struct dtv_payload payload;
333 	uint32_t xferlen;
334 
335 	if (ab->ab_running == false || sc->sc_dtvsubmitcb == NULL)
336 		return;
337 
338 	usbd_get_xfer_status(xfer, NULL, NULL, &xferlen, NULL);
339 
340 	//printf("%s: status=%d xferlen=%u\n", __func__, status, xferlen);
341 
342 	if (status != USBD_NORMAL_COMPLETION) {
343 		printf("%s: USB error (%s)\n", __func__, usbd_errstr(status));
344 		if (status == USBD_STALLED) {
345 			usbd_clear_endpoint_stall_async(ab->ab_pipe);
346 			goto next;
347 		}
348 		if (status == USBD_SHORT_XFER) {
349 			goto next;
350 		}
351 		return;
352 	}
353 
354 	if (xferlen == 0) {
355 		printf("%s: 0-length xfer\n", __func__);
356 		goto next;
357 	}
358 
359 	payload.data = bx->bx_buffer;
360 	payload.size = xferlen;
361 	sc->sc_dtvsubmitcb(sc->sc_dtvsubmitarg, &payload);
362 
363 next:
364 	auvitek_dtv_bulk_start1(bx);
365 }
366 
367 static int
368 auvitek_dtv_bulk_start(struct auvitek_softc *sc)
369 {
370 	int i, error;
371 
372 	for (i = 0; i < AUVITEK_NBULK_XFERS; i++) {
373 		error = auvitek_dtv_bulk_start1(&sc->sc_ab.ab_bx[i]);
374 		if (error)
375 			return error;
376 	}
377 
378 	return 0;
379 }
380 
381 static int
382 auvitek_dtv_bulk_start1(struct auvitek_bulk_xfer *bx)
383 {
384 	struct auvitek_softc *sc = bx->bx_sc;
385 	usbd_status err;
386 
387 	usbd_setup_xfer(bx->bx_xfer, bx, bx->bx_buffer, AUVITEK_BULK_BUFLEN,
388 	    0 /* USBD_SHORT_XFER_OK */, 100 /* USBD_NO_TIMEOUT */,
389 	    auvitek_dtv_bulk_cb);
390 
391 	KERNEL_LOCK(1, curlwp);
392 	err = usbd_transfer(bx->bx_xfer);
393 	KERNEL_UNLOCK_ONE(curlwp);
394 
395 	if (err != USBD_IN_PROGRESS) {
396 		aprint_error_dev(sc->sc_dev, "USB error: %s\n",
397 		    usbd_errstr(err));
398 		return ENODEV;
399 	}
400 
401 	return 0;
402 }
403