1/* $NetBSD$ */
2
3/*-
4 * Copyright (c) 2008, 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#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD$");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/device.h>
35#include <sys/conf.h>
36
37#include <dev/usb/usb.h>
38#include <dev/usb/usbdi.h>
39#include <dev/usb/usbdi_util.h>
40#include <dev/usb/usbdevs.h>
41
42#include <dev/i2c/i2cvar.h>
43
44#include <dev/usb/emdtvvar.h>
45#include <dev/usb/emdtvreg.h>
46
47static void		emdtv_dtv_get_devinfo(void *,
48			    struct dvb_frontend_info *);
49static int		emdtv_dtv_open(void *, int);
50static void		emdtv_dtv_close(void *);
51static int		emdtv_dtv_set_tuner(void *,
52			    const struct dvb_frontend_parameters *);
53static fe_status_t	emdtv_dtv_get_status(void *);
54static uint16_t		emdtv_dtv_get_signal_strength(void *);
55static uint16_t		emdtv_dtv_get_snr(void *);
56static int		emdtv_dtv_start_transfer(void *,
57			    void (*)(void *, const struct dtv_payload *),
58			    void *);
59static int		emdtv_dtv_stop_transfer(void *);
60
61static int		emdtv_dtv_tuner_reset(void *);
62
63static void		emdtv_dtv_isoc_startall(struct emdtv_softc *);
64static int		emdtv_dtv_isoc_start(struct emdtv_softc *,
65			    struct emdtv_isoc_xfer *);
66static void		emdtv_dtv_isoc(usbd_xfer_handle, usbd_private_handle,
67			    usbd_status);
68
69static const struct dtv_hw_if emdtv_dtv_if = {
70	.get_devinfo = emdtv_dtv_get_devinfo,
71	.open = emdtv_dtv_open,
72	.close = emdtv_dtv_close,
73	.set_tuner = emdtv_dtv_set_tuner,
74	.get_status = emdtv_dtv_get_status,
75	.get_signal_strength = emdtv_dtv_get_signal_strength,
76	.get_snr = emdtv_dtv_get_snr,
77	.start_transfer = emdtv_dtv_start_transfer,
78	.stop_transfer = emdtv_dtv_stop_transfer,
79};
80
81void
82emdtv_dtv_attach(struct emdtv_softc *sc)
83{
84	usb_endpoint_descriptor_t *ed;
85	usbd_status status;
86	int i;
87
88	for (i = 0; i < EMDTV_NXFERS; i++) {
89		sc->sc_ix[i].ix_altix = (i & 1) ?
90		    &sc->sc_ix[i - 1] : &sc->sc_ix[i + 1];
91		sc->sc_ix[i].ix_sc = sc;
92	}
93
94	ed = usbd_interface2endpoint_descriptor(sc->sc_iface, 3);
95	if (ed == NULL) {
96		aprint_error_dev(sc->sc_dev, "couldn't find endpoint 3\n");
97		return;
98	}
99	sc->sc_isoc_maxpacketsize = UGETW(ed->wMaxPacketSize);
100	sc->sc_isoc_buflen = sc->sc_isoc_maxpacketsize * EMDTV_NFRAMES;
101
102	aprint_debug_dev(sc->sc_dev, "calling usbd_open_pipe, ep 0x%02x\n",
103	    ed->bEndpointAddress);
104	status = usbd_open_pipe(sc->sc_iface,
105	    ed->bEndpointAddress, USBD_EXCLUSIVE_USE,
106	    &sc->sc_isoc_pipe);
107	if (status != USBD_NORMAL_COMPLETION) {
108		aprint_error_dev(sc->sc_dev, "couldn't open isoc pipe\n");
109		usbd_set_interface(sc->sc_iface, 0);
110		return;
111	}
112
113	emdtv_write_1(sc, UR_GET_STATUS, 0x48, 0x00);
114	emdtv_write_1(sc, UR_GET_STATUS, 0x12, 0x77);
115	usbd_delay_ms(sc->sc_udev, 6);
116
117	emdtv_gpio_ctl(sc, EMDTV_GPIO_ANALOG_ON, false);
118	emdtv_gpio_ctl(sc, EMDTV_GPIO_TS1_ON, true);
119	emdtv_gpio_ctl(sc, EMDTV_GPIO_TUNER1_ON, true);
120	emdtv_gpio_ctl(sc, EMDTV_GPIO_DEMOD1_RESET, true);
121	usbd_delay_ms(sc->sc_udev, 100);
122
123	emdtv_dtv_rescan(sc, NULL, NULL);
124}
125
126void
127emdtv_dtv_detach(struct emdtv_softc *sc, int flags)
128{
129	sc->sc_streaming = false;
130
131	if (sc->sc_dtvdev != NULL) {
132		config_detach(sc->sc_dtvdev, flags);
133		sc->sc_dtvdev = NULL;
134	}
135
136	if (sc->sc_xc3028)
137		xc3028_close(sc->sc_xc3028);
138	if (sc->sc_lg3303)
139		lg3303_close(sc->sc_lg3303);
140
141	if (sc->sc_isoc_pipe) {
142		usbd_abort_pipe(sc->sc_isoc_pipe);
143		usbd_close_pipe(sc->sc_isoc_pipe);
144		sc->sc_isoc_pipe = NULL;
145	}
146}
147
148void
149emdtv_dtv_rescan(struct emdtv_softc *sc, const char *ifattr, const int *locs)
150{
151	struct dtv_attach_args daa;
152
153	daa.hw = &emdtv_dtv_if;
154	daa.priv = sc;
155
156	if (ifattr_match(ifattr, "dtvbus") && sc->sc_dtvdev == NULL)
157		sc->sc_dtvdev = config_found_ia(sc->sc_dev, "dtvbus",
158		    &daa, dtv_print);
159}
160
161static void
162emdtv_dtv_get_devinfo(void *priv, struct dvb_frontend_info *info)
163{
164	struct emdtv_softc *sc = priv;
165
166	memset(info, 0, sizeof(*info));
167	strlcpy(info->name, sc->sc_board->eb_name, sizeof(info->name));
168	info->type = FE_ATSC;
169	info->frequency_min = 54000000;
170	info->frequency_max = 858000000;
171	info->frequency_stepsize = 62500;
172	info->caps = FE_CAN_8VSB;
173}
174
175static int
176emdtv_dtv_open(void *priv, int flags)
177{
178	struct emdtv_softc *sc = priv;
179
180	if (sc->sc_dying)
181		return ENXIO;
182
183	switch (sc->sc_board->eb_tuner) {
184	case EMDTV_TUNER_XC3028:
185		if (sc->sc_xc3028 == NULL) {
186			sc->sc_xc3028 = xc3028_open(sc->sc_dev,
187			    &sc->sc_i2c, 0x61 << 1, emdtv_dtv_tuner_reset, sc,
188			    XC3028);
189		}
190		if (sc->sc_xc3028 == NULL) {
191			aprint_error_dev(sc->sc_dev, "couldn't open xc3028\n");
192			return ENXIO;
193		}
194		break;
195	case EMDTV_TUNER_XC3028L:
196		if (sc->sc_xc3028 == NULL) {
197			sc->sc_xc3028 = xc3028_open(sc->sc_dev,
198			    &sc->sc_i2c, 0x61 << 1, emdtv_dtv_tuner_reset, sc,
199			    XC3028L);
200		}
201		if (sc->sc_xc3028 == NULL) {
202			aprint_error_dev(sc->sc_dev, "couldn't open xc3028l\n");
203			return ENXIO;
204		}
205		break;
206	default:
207		aprint_error_dev(sc->sc_dev, "unsupported tuner (%d)\n",
208		    sc->sc_board->eb_tuner);
209		return EIO;
210	}
211
212	switch (sc->sc_board->eb_demod) {
213	case EMDTV_DEMOD_LG3303:
214		if (sc->sc_lg3303 == NULL) {
215			sc->sc_lg3303 = lg3303_open(sc->sc_dev,
216			    &sc->sc_i2c, 0x1c, 0);
217		}
218		if (sc->sc_lg3303 == NULL) {
219			aprint_error_dev(sc->sc_dev, "couldn't open lg3303\n");
220			return ENXIO;
221		}
222		break;
223	default:
224		aprint_error_dev(sc->sc_dev, "unsupported demod (%d)\n",
225		    sc->sc_board->eb_demod);
226		return EIO;
227	}
228
229	return 0;
230}
231
232static void
233emdtv_dtv_close(void *priv)
234{
235	return;
236}
237
238static int
239emdtv_dtv_set_tuner(void *priv, const struct dvb_frontend_parameters *params)
240{
241	struct emdtv_softc *sc = priv;
242	int error;
243
244	/* Setup demod */
245	error = ENXIO;
246	if (sc->sc_lg3303)
247		error = lg3303_set_modulation(sc->sc_lg3303,
248		    params->u.vsb.modulation);
249	if (error)
250		return error;
251
252	/* Setup tuner */
253	error = ENXIO;
254	if (sc->sc_xc3028)
255		error = xc3028_tune_dtv(sc->sc_xc3028, params);
256
257	return error;
258}
259
260static fe_status_t
261emdtv_dtv_get_status(void *priv)
262{
263	struct emdtv_softc *sc = priv;
264
265	if (sc->sc_lg3303)
266		return lg3303_get_dtv_status(sc->sc_lg3303);
267
268	return 0;
269}
270
271uint16_t
272emdtv_dtv_get_signal_strength(void *priv)
273{
274	struct emdtv_softc *sc = priv;
275
276	if (sc->sc_lg3303)
277		return lg3303_get_signal_strength(sc->sc_lg3303);
278
279	return 0;
280}
281
282uint16_t
283emdtv_dtv_get_snr(void *priv)
284{
285	struct emdtv_softc *sc = priv;
286
287	if (sc->sc_lg3303)
288		return lg3303_get_snr(sc->sc_lg3303);
289
290	return 0;
291}
292
293static int
294emdtv_dtv_start_transfer(void *priv,
295    void (*cb)(void *, const struct dtv_payload *), void *arg)
296{
297	struct emdtv_softc *sc = priv;
298	int i, s;
299
300	s = splusb();
301
302	sc->sc_streaming = true;
303	sc->sc_dtvsubmitcb = cb;
304	sc->sc_dtvsubmitarg = arg;
305
306	aprint_debug_dev(sc->sc_dev, "allocating isoc xfers (pktsz %d)\n",
307	    sc->sc_isoc_maxpacketsize);
308
309	KERNEL_LOCK(1, curlwp);
310	for (i = 0; i < EMDTV_NXFERS; i++) {
311		sc->sc_ix[i].ix_xfer = usbd_alloc_xfer(sc->sc_udev);
312		sc->sc_ix[i].ix_buf = usbd_alloc_buffer(sc->sc_ix[i].ix_xfer,
313		    sc->sc_isoc_buflen);
314		aprint_debug_dev(sc->sc_dev, "  ix[%d] xfer %p buf %p\n",
315		    i, sc->sc_ix[i].ix_xfer, sc->sc_ix[i].ix_buf);
316	}
317	KERNEL_UNLOCK_ONE(curlwp);
318
319	aprint_debug_dev(sc->sc_dev, "starting isoc transactions\n");
320
321	emdtv_dtv_isoc_startall(sc);
322	splx(s);
323
324	return 0;
325}
326
327static int
328emdtv_dtv_stop_transfer(void *priv)
329{
330	struct emdtv_softc *sc = priv;
331	int i;
332
333	aprint_debug_dev(sc->sc_dev, "stopping stream\n");
334
335	sc->sc_streaming = false;
336
337	KERNEL_LOCK(1, curlwp);
338	if (sc->sc_isoc_pipe != NULL)
339		usbd_abort_pipe(sc->sc_isoc_pipe);
340
341	for (i = 0; i < EMDTV_NXFERS; i++)
342		if (sc->sc_ix[i].ix_xfer) {
343			usbd_free_xfer(sc->sc_ix[i].ix_xfer);
344			sc->sc_ix[i].ix_xfer = NULL;
345			sc->sc_ix[i].ix_buf = NULL;
346		}
347	KERNEL_UNLOCK_ONE(curlwp);
348
349	sc->sc_dtvsubmitcb = NULL;
350	sc->sc_dtvsubmitarg = NULL;
351
352	return 0;
353}
354
355static void
356emdtv_dtv_isoc_startall(struct emdtv_softc *sc)
357{
358	int i;
359
360	if (sc->sc_streaming == false || sc->sc_dying == true)
361		return;
362
363	for (i = 0; i < EMDTV_NXFERS; i += 2)
364		emdtv_dtv_isoc_start(sc, &sc->sc_ix[i]);
365}
366
367static int
368emdtv_dtv_isoc_start(struct emdtv_softc *sc, struct emdtv_isoc_xfer *ix)
369{
370	int i;
371
372	if (sc->sc_isoc_pipe == NULL)
373		return EIO;
374
375	for (i = 0; i < EMDTV_NFRAMES; i++)
376		ix->ix_frlengths[i] = sc->sc_isoc_maxpacketsize;
377
378	usbd_setup_isoc_xfer(ix->ix_xfer,
379			     sc->sc_isoc_pipe,
380			     ix,
381			     ix->ix_frlengths,
382			     EMDTV_NFRAMES,
383			     USBD_NO_COPY | USBD_SHORT_XFER_OK,
384			     emdtv_dtv_isoc);
385
386	KERNEL_LOCK(1, curlwp);
387	usbd_transfer(ix->ix_xfer);
388	KERNEL_UNLOCK_ONE(curlwp);
389
390	return 0;
391}
392
393static void
394emdtv_dtv_isoc(usbd_xfer_handle xfer, usbd_private_handle priv,
395    usbd_status err)
396{
397	struct emdtv_isoc_xfer *ix = priv;
398	struct emdtv_softc *sc = ix->ix_sc;
399	struct dtv_payload payload;
400	usbd_pipe_handle isoc = sc->sc_isoc_pipe;
401	uint32_t len;
402	uint8_t *buf;
403	int i;
404
405	KASSERT(xfer == ix->ix_xfer);
406
407	if (sc->sc_dying || sc->sc_dtvsubmitcb == NULL)
408		return;
409
410	if (err) {
411		if (err == USBD_STALLED) {
412			usbd_clear_endpoint_stall_async(isoc);
413			goto resched;
414		}
415		return;
416	}
417
418	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
419
420	if (len == 0)
421		goto resched;
422
423	buf = usbd_get_buffer(xfer);
424	if (buf == NULL)
425		goto resched;
426
427	for (i = 0; i < EMDTV_NFRAMES; i++, buf += sc->sc_isoc_maxpacketsize) {
428		if (ix->ix_frlengths[i] == 0)
429			continue;
430		payload.data = buf;
431		payload.size = ix->ix_frlengths[i];
432		sc->sc_dtvsubmitcb(sc->sc_dtvsubmitarg, &payload);
433	}
434
435resched:
436	emdtv_dtv_isoc_start(sc, ix->ix_altix);
437}
438
439static int
440emdtv_dtv_tuner_reset(void *opaque)
441{
442	struct emdtv_softc *sc = opaque;
443	emdtv_gpio_ctl(sc, EMDTV_GPIO_TUNER1_RESET, true);
444	return 0;
445}
446