emdtv.c revision 1.18
1/* $NetBSD: emdtv.c,v 1.18 2022/06/26 22:49:09 riastradh Exp $ */
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: emdtv.c,v 1.18 2022/06/26 22:49:09 riastradh Exp $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/device.h>
35#include <sys/lwp.h>
36#include <sys/module.h>
37#include <sys/conf.h>
38
39#include <dev/usb/usb.h>
40#include <dev/usb/usbdi.h>
41#include <dev/usb/usbdi_util.h>
42#include <dev/usb/usbdivar.h>
43#include <dev/usb/usbdevs.h>
44
45#include <dev/usb/emdtvvar.h>
46#include <dev/usb/emdtvreg.h>
47
48static int	emdtv_match(device_t, cfdata_t, void *);
49static void	emdtv_attach(device_t, device_t, void *);
50static int	emdtv_detach(device_t, int);
51static int	emdtv_rescan(device_t, const char *, const int *);
52static void	emdtv_childdet(device_t, device_t);
53static int	emdtv_activate(device_t, enum devact);
54
55static bool	emdtv_read_eeprom(struct emdtv_softc *);
56static void	emdtv_board_setup(struct emdtv_softc *);
57
58static void	emdtv_default_board_init(struct emdtv_softc *);
59
60CFATTACH_DECL2_NEW(emdtv, sizeof(struct emdtv_softc),
61    emdtv_match, emdtv_attach, emdtv_detach, emdtv_activate,
62    emdtv_rescan, emdtv_childdet);
63
64static const struct usb_devno emdtv_devices[] = {
65	{ USB_VENDOR_AMD,	USB_PRODUCT_AMD_TV_WONDER_600_USB },
66	{ USB_VENDOR_PINNACLE,	USB_PRODUCT_PINNACLE_PCTV800E },
67};
68
69int emdtv_debug_regs = 0;
70
71static int
72emdtv_match(device_t parent, cfdata_t match, void *opaque)
73{
74	struct usb_attach_arg *uaa = opaque;
75
76	return usb_lookup(emdtv_devices, uaa->uaa_vendor, uaa->uaa_product) != NULL ?
77	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
78}
79
80static void
81emdtv_attach(device_t parent, device_t self, void *opaque)
82{
83	struct emdtv_softc *sc = device_private(self);
84	struct usb_attach_arg *uaa = opaque;
85	struct usbd_device *dev = uaa->uaa_device;
86	usbd_status status;
87	char *devinfo;
88
89	devinfo = usbd_devinfo_alloc(dev, 0);
90	aprint_naive("\n");
91	aprint_normal(": %s\n", devinfo);
92	usbd_devinfo_free(devinfo);
93
94	sc->sc_dev = self;
95	sc->sc_udev = dev;
96
97	sc->sc_vendor = uaa->uaa_vendor;
98	sc->sc_product = uaa->uaa_product;
99
100	emdtv_i2c_attach(sc);
101
102	emdtv_read_eeprom(sc);
103
104	sc->sc_board = emdtv_board_lookup(sc->sc_vendor, sc->sc_product);
105	if (sc->sc_board == NULL) {
106		aprint_error_dev(sc->sc_dev,
107		    "unsupported board 0x%04x:0x%04x\n",
108		    sc->sc_vendor, sc->sc_product);
109		sc->sc_dying = true;
110		return;
111	}
112
113	emdtv_write_1(sc, 0x02, 0xa0, 0x23);
114	if (emdtv_read_1(sc, UR_GET_STATUS, 0x05) != 0) {
115		(void)emdtv_read_1(sc, 0x02, 0xa0);
116		if (emdtv_read_1(sc, 0x02, 0xa0) & 0x08)
117			aprint_debug_dev(sc->sc_dev,
118			    "board requires manual gpio configuration\n");
119	}
120
121	emdtv_board_setup(sc);
122
123	emdtv_gpio_ctl(sc, EMDTV_GPIO_ANALOG_ON, false);
124	emdtv_gpio_ctl(sc, EMDTV_GPIO_TS1_ON, false);
125	usbd_delay_ms(sc->sc_udev, 100);
126	emdtv_gpio_ctl(sc, EMDTV_GPIO_ANALOG_ON, true);
127	emdtv_gpio_ctl(sc, EMDTV_GPIO_TUNER1_ON, true);
128	usbd_delay_ms(sc->sc_udev, 100);
129
130	status = usbd_set_config_no(sc->sc_udev, 1, 1);
131        if (status != USBD_NORMAL_COMPLETION) {
132		aprint_error_dev(sc->sc_dev, "failed to set configuration"
133		    ", err=%s\n", usbd_errstr(status));
134		return;
135	}
136
137	status = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface);
138	if (status != USBD_NORMAL_COMPLETION) {
139		aprint_error_dev(sc->sc_dev, "couldn't find iface handle\n");
140		return;
141	}
142
143	status = usbd_set_interface(sc->sc_iface, 1);
144	if (status != USBD_NORMAL_COMPLETION) {
145		aprint_error_dev(sc->sc_dev, "couldn't set interface\n");
146		return;
147	}
148
149	emdtv_dtv_attach(sc);
150	emdtv_ir_attach(sc);
151	sc->sc_subdevs_attached = true;
152}
153
154static int
155emdtv_detach(device_t self, int flags)
156{
157	struct emdtv_softc *sc = device_private(self);
158	usbd_status status;
159	int error;
160
161	sc->sc_dying = true;
162
163	error = config_detach_children(self, flags);
164	if (error)
165		return error;
166
167	if (sc->sc_subdevs_attached) {
168		emdtv_ir_detach(sc, flags);
169		emdtv_dtv_detach(sc, flags);
170	}
171
172	if (sc->sc_iface != NULL) {
173		status = usbd_set_interface(sc->sc_iface, 0);
174		if (status != USBD_NORMAL_COMPLETION)
175			aprint_error_dev(sc->sc_dev,
176			    "couldn't stop stream: %s\n", usbd_errstr(status));
177	}
178
179	emdtv_i2c_detach(sc, flags);
180
181	return 0;
182}
183
184int
185emdtv_activate(device_t self, enum devact act)
186{
187	struct emdtv_softc *sc = device_private(self);
188
189	switch (act) {
190	case DVACT_DEACTIVATE:
191		sc->sc_dying = true;
192		break;
193	}
194
195	return 0;
196}
197
198static int
199emdtv_rescan(device_t self, const char *ifattr, const int *locs)
200{
201	struct emdtv_softc *sc = device_private(self);
202
203	emdtv_dtv_rescan(sc, ifattr, locs);
204
205	return 0;
206}
207
208static void
209emdtv_childdet(device_t self, device_t child)
210{
211	struct emdtv_softc *sc = device_private(self);
212
213	if (child == sc->sc_cirdev)
214		sc->sc_cirdev = NULL;
215	if (child == sc->sc_dtvdev)
216		sc->sc_dtvdev = NULL;
217}
218
219static bool
220emdtv_read_eeprom(struct emdtv_softc *sc)
221{
222	i2c_addr_t ee = EM28XX_I2C_ADDR_EEPROM;
223	uint8_t buf, *p = sc->sc_eeprom;
224	struct emdtv_eeprom *eeprom = (struct emdtv_eeprom *)sc->sc_eeprom;
225	int block, size = sizeof(sc->sc_eeprom);
226
227	if (iic_exec(&sc->sc_i2c, I2C_OP_READ, ee, NULL, 0, NULL, 0, 0))
228		return false;
229	buf = 0;
230	if (iic_exec(&sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, ee, &buf, 1,
231	    NULL, 0, 0))
232		return false;
233	while (size > 0) {
234		block = uimin(size, 16);
235		if (iic_exec(&sc->sc_i2c, I2C_OP_READ, ee, NULL, 0,
236		    p, block, 0))
237			return false;
238		size -= block;
239		p += block;
240	}
241
242	aprint_normal_dev(sc->sc_dev,
243	    "id 0x%08x vendor 0x%04x product 0x%04x\n",
244	    eeprom->id, eeprom->vendor, eeprom->product);
245
246	sc->sc_vendor = eeprom->vendor;
247	sc->sc_product = eeprom->product;
248
249	return true;
250}
251
252static void
253emdtv_board_setup(struct emdtv_softc *sc)
254{
255	switch (sc->sc_vendor) {
256	case USB_VENDOR_EMPIA:
257		switch (sc->sc_product) {
258		case USB_PRODUCT_EMPIA_EM2883:
259			emdtv_write_1(sc, UR_GET_STATUS, EM28XX_XCLK_REG, 0x97);
260			emdtv_write_1(sc, UR_GET_STATUS, EM28XX_I2C_CLK_REG,
261			    0x40);
262			delay(10000);
263			emdtv_write_1(sc, UR_GET_STATUS, 0x08, 0x2d);
264			delay(10000);
265			break;
266		default:
267			aprint_normal_dev(sc->sc_dev,
268			    "unknown EMPIA board 0x%04x/0x%04x\n",
269			    sc->sc_vendor, sc->sc_product);
270			break;
271		}
272		break;
273	case USB_VENDOR_AMD:
274		switch (sc->sc_product) {
275		case USB_PRODUCT_AMD_TV_WONDER_600_USB:
276			emdtv_default_board_init(sc);
277			break;
278		default:
279			aprint_normal_dev(sc->sc_dev,
280			    "unknown AMD board 0x%04x/0x%04x\n",
281			    sc->sc_vendor, sc->sc_product);
282		}
283		break;
284	case USB_VENDOR_PINNACLE:
285		switch (sc->sc_product) {
286		case USB_PRODUCT_PINNACLE_PCTV800E:
287			emdtv_default_board_init(sc);
288			break;
289		default:
290			aprint_normal_dev(sc->sc_dev,
291			    "unknown Pinnacle board 0x%04x/0x%04x\n",
292			    sc->sc_vendor, sc->sc_product);
293		}
294		break;
295	default:
296		aprint_normal_dev(sc->sc_dev,
297		    "unknown board 0x%04x:0x%04x\n",
298		    sc->sc_vendor, sc->sc_product);
299		break;
300	}
301}
302
303/*
304 * Register read/write
305 */
306uint8_t
307emdtv_read_1(struct emdtv_softc *sc, uint8_t req, uint16_t index)
308{
309	uint8_t val;
310	emdtv_read_multi_1(sc, req, index, &val, 1);
311	return val;
312}
313
314void
315emdtv_write_1(struct emdtv_softc *sc, uint8_t req, uint16_t index, uint8_t val)
316{
317	emdtv_write_multi_1(sc, req, index, &val, 1);
318}
319
320void
321emdtv_read_multi_1(struct emdtv_softc *sc, uint8_t req, uint16_t index,
322    uint8_t *datap, uint16_t count)
323{
324	usb_device_request_t request;
325	usbd_status status;
326
327	request.bmRequestType = UT_READ_VENDOR_DEVICE;
328	request.bRequest = req;
329	USETW(request.wValue, 0x0000);
330	USETW(request.wIndex, index);
331	USETW(request.wLength, count);
332
333	KERNEL_LOCK(1, curlwp);
334	status = usbd_do_request(sc->sc_udev, &request, datap);
335	KERNEL_UNLOCK_ONE(curlwp);
336
337	if (status != USBD_NORMAL_COMPLETION) {
338		aprint_error_dev(sc->sc_dev, "couldn't read %x/%x: %s\n",
339		    req, index, usbd_errstr(status));
340		memset(datap, 0, count);
341	}
342
343	if (emdtv_debug_regs) {
344		int i;
345		printf("%s [%s] c0 %02x 00 00 %02x 00 01 00 <<<",
346		    __func__, status == 0 ? " OK" : "NOK", req, index);
347		for (i = 0; status == 0 && i < count; i++)
348			printf(" %02x", datap[i]);
349		printf("\n");
350	}
351}
352
353void
354emdtv_write_multi_1(struct emdtv_softc *sc, uint8_t req, uint16_t index,
355    const uint8_t *datap, uint16_t count)
356{
357	usb_device_request_t request;
358	usbd_status status;
359
360	request.bmRequestType = UT_WRITE_VENDOR_DEVICE;
361	request.bRequest = req;
362	USETW(request.wValue, 0x0000);
363	USETW(request.wIndex, index);
364	USETW(request.wLength, count);
365
366	KERNEL_LOCK(1, curlwp);
367	status = usbd_do_request(sc->sc_udev, &request, __UNCONST(datap));
368	KERNEL_UNLOCK_ONE(curlwp);
369
370	if (status != USBD_NORMAL_COMPLETION)
371		aprint_error_dev(sc->sc_dev, "couldn't read %x/%x: %s\n",
372		    req, index, usbd_errstr(status));
373
374	if (emdtv_debug_regs) {
375		int i;
376		printf("%s [%s] 40 %02x 00 00 %02x 00 %02x 00 >>>",
377		    __func__, status == 0 ? " OK" : "NOK",
378		    req, index, count);
379		for (i = 0; i < count; ++i)
380			printf(" %02x", datap[i]);
381		printf("\n");
382	}
383}
384
385bool
386emdtv_gpio_ctl(struct emdtv_softc *sc, emdtv_gpio_reg_t gpioreg, bool onoff)
387{
388	const struct emdtv_board *eb = sc->sc_board;
389	uint16_t gpio_value, reg;
390	uint8_t gpio;
391	uint8_t eeprom_offset = 0x3c;
392	uint8_t val;
393
394	if (sc->sc_board->eb_manual_gpio == false) {
395		val = eeprom_offset + gpioreg;
396		emdtv_write_1(sc, 0x03, 0xa0, val);
397		gpio_value = emdtv_read_1(sc, 0x02, 0xa0);
398	} else {
399		const struct emdtv_gpio_regs *r = &eb->eb_gpio_regs;
400		switch (gpioreg) {
401		case EMDTV_GPIO_TS1_ON:
402			gpio_value = r->ts1_on;
403			break;
404		case EMDTV_GPIO_ANALOG_ON:
405			gpio_value = r->a_on;
406			break;
407		case EMDTV_GPIO_TUNER1_ON:
408			gpio_value = r->t1_on;
409			break;
410		case EMDTV_GPIO_TUNER1_RESET:
411			gpio_value = r->t1_reset;
412				break;
413		case EMDTV_GPIO_DEMOD1_RESET:
414			gpio_value = r->d1_reset;
415			break;
416		default:
417			aprint_error_dev(sc->sc_dev,
418			    "unknown gpio reg %d\n", gpioreg);
419			return false;
420		}
421	}
422
423	if ((gpio_value & 0x80) == 0) {
424		aprint_error_dev(sc->sc_dev,
425		    "gpio reg %d not enabled\n", gpioreg);
426		return false;
427	}
428
429	reg = gpio_value & 0x10 ? 0x04 : 0x08;
430	gpio = emdtv_read_1(sc, UR_GET_STATUS, reg);
431	if ((gpio_value & 0x40) == 0) {
432		gpio &= ~((uint8_t)(1 << (gpio_value & 7)));
433
434		if (onoff)
435			gpio |= ((gpio_value >> 5) & 1) << (gpio_value & 7);
436		else
437			gpio |= (((gpio_value >> 5) & 1) ^ 1) <<
438			    (gpio_value & 7);
439		emdtv_write_1(sc, UR_GET_STATUS, reg, gpio);
440	} else {
441		gpio &= ~((uint8_t)(1 << (gpio_value & 0xf)));
442
443		gpio |= ((gpio_value >> 5) & 1) << (gpio_value & 7);
444		emdtv_write_1(sc, UR_GET_STATUS, reg, gpio);
445		usbd_delay_ms(sc->sc_udev, 100);
446
447		gpio &= ~((uint8_t)(1 << (gpio_value & 0xf)));
448		gpio |= (((gpio_value >> 5) & 1) ^ 1) << (gpio_value & 7);
449		emdtv_write_1(sc, UR_GET_STATUS, reg, gpio);
450		usbd_delay_ms(sc->sc_udev, 100);
451	}
452
453	return true;
454}
455
456static void
457emdtv_default_board_init(struct emdtv_softc *sc)
458{
459	emdtv_write_1(sc, UR_GET_STATUS, EM28XX_XCLK_REG, 0x27);
460	emdtv_write_1(sc, UR_GET_STATUS, EM28XX_I2C_CLK_REG, 0x40);
461	emdtv_write_1(sc, UR_GET_STATUS, 0x08, 0xff);
462	emdtv_write_1(sc, UR_GET_STATUS, 0x04, 0x00);
463	usbd_delay_ms(sc->sc_udev, 100);
464	emdtv_write_1(sc, UR_GET_STATUS, 0x04, 0x08);
465	usbd_delay_ms(sc->sc_udev, 100);
466	emdtv_write_1(sc, UR_GET_STATUS, 0x08, 0xff);
467	usbd_delay_ms(sc->sc_udev, 50);
468	emdtv_write_1(sc, UR_GET_STATUS, 0x08, 0x2d);
469	usbd_delay_ms(sc->sc_udev, 50);
470	emdtv_write_1(sc, UR_GET_STATUS, 0x08, 0x3d);
471	//emdtv_write_1(sc, UR_GET_STATUS, 0x0f, 0xa7);
472	usbd_delay_ms(sc->sc_udev, 10);
473}
474
475MODULE(MODULE_CLASS_DRIVER, emdtv, "cir,lg3303,xc3028");
476
477#ifdef _MODULE
478#include "ioconf.c"
479#endif
480
481static int
482emdtv_modcmd(modcmd_t cmd, void *opaque)
483{
484	switch (cmd) {
485	case MODULE_CMD_INIT:
486#ifdef _MODULE
487		return config_init_component(cfdriver_ioconf_emdtv,
488		    cfattach_ioconf_emdtv, cfdata_ioconf_emdtv);
489#else
490		return 0;
491#endif
492	case MODULE_CMD_FINI:
493#ifdef _MODULE
494		return config_fini_component(cfdriver_ioconf_emdtv,
495		    cfattach_ioconf_emdtv, cfdata_ioconf_emdtv);
496#else
497		return 0;
498#endif
499	default:
500		return ENOTTY;
501	}
502}
503