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