1 /* $NetBSD: xc5k.c,v 1.9 2018/09/03 16:29:31 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 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 * Xceive XC5000 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: xc5k.c,v 1.9 2018/09/03 16:29:31 riastradh 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/bus.h> 41 #include <sys/kmem.h> 42 #include <sys/mutex.h> 43 #include <sys/module.h> 44 45 #include <dev/firmload.h> 46 #include <dev/i2c/i2cvar.h> 47 48 #include <dev/i2c/xc5kreg.h> 49 #include <dev/i2c/xc5kvar.h> 50 51 #define XC5K_FIRMWARE_DRVNAME "xc5k" 52 #define XC5K_FIRMWARE_IMGNAME "dvb-fe-xc5000-1.6.114.fw" 53 54 #define XC5K_FREQ_MIN 1000000 55 #define XC5K_FREQ_MAX 1023000000 56 57 static kmutex_t xc5k_firmware_lock; 58 59 static int xc5k_reset(struct xc5k *); 60 static int xc5k_read_2(struct xc5k *, uint16_t, uint16_t *); 61 static int xc5k_write_buffer(struct xc5k *, const uint8_t *, size_t); 62 static int xc5k_firmware_open(struct xc5k *); 63 static int xc5k_firmware_upload(struct xc5k *, const uint8_t *, size_t); 64 65 static int 66 xc5k_reset(struct xc5k *xc) 67 { 68 int error = 0; 69 70 if (xc->reset) 71 error = xc->reset(xc->reset_priv); 72 73 return error; 74 } 75 76 static int 77 xc5k_firmware_upload(struct xc5k *xc, const uint8_t *fw, size_t fwlen) 78 { 79 const uint8_t *p; 80 uint8_t cmd[64]; 81 unsigned int i; 82 uint16_t len, rem; 83 size_t wrlen; 84 int error; 85 86 for (i = 0; i < fwlen - 1;) { 87 len = (fw[i] << 8) | fw[i + 1]; 88 i += 2; 89 if (len == 0xffff) 90 break; 91 92 /* reset command */ 93 if (len == 0x0000) { 94 error = xc5k_reset(xc); 95 if (error) 96 return error; 97 continue; 98 } 99 100 /* delay command */ 101 if (len & 0x8000) { 102 delay((len & 0x7fff) * 1000); 103 continue; 104 } 105 106 if (i + len >= fwlen) 107 break; 108 109 cmd[0] = fw[i]; 110 cmd[1] = fw[i + 1]; 111 p = &fw[i + 2]; 112 rem = len - 2; 113 while (rem > 0) { 114 wrlen = uimin(rem, __arraycount(cmd) - 2); 115 memcpy(&cmd[2], p, wrlen); 116 error = xc5k_write_buffer(xc, cmd, wrlen + 2); 117 if (error) 118 return error; 119 p += wrlen; 120 rem -= wrlen; 121 } 122 i += len; 123 } 124 125 return 0; 126 } 127 128 static int 129 xc5k_firmware_open(struct xc5k *xc) 130 { 131 firmware_handle_t fwh; 132 uint16_t product_id, xcversion, xcbuild; 133 uint8_t *fw = NULL; 134 size_t fwlen; 135 int error; 136 137 mutex_enter(&xc5k_firmware_lock); 138 139 error = xc5k_read_2(xc, XC5K_REG_PRODUCT_ID, &product_id); 140 if (error || product_id != XC5K_PRODUCT_ID_NOFW) 141 goto done; 142 143 error = firmware_open(XC5K_FIRMWARE_DRVNAME, 144 XC5K_FIRMWARE_IMGNAME, &fwh); 145 if (error) 146 goto done; 147 fwlen = firmware_get_size(fwh); 148 fw = firmware_malloc(fwlen); 149 if (fw == NULL) { 150 firmware_close(fwh); 151 error = ENOMEM; 152 goto done; 153 } 154 error = firmware_read(fwh, 0, fw, fwlen); 155 firmware_close(fwh); 156 if (error) 157 goto done; 158 159 aprint_normal_dev(xc->parent, "xc5k: loading firmware '%s/%s'\n", 160 XC5K_FIRMWARE_DRVNAME, XC5K_FIRMWARE_IMGNAME); 161 162 error = xc5k_firmware_upload(xc, fw, fwlen); 163 if (error) 164 goto done; 165 166 error = xc5k_read_2(xc, XC5K_REG_VERSION, &xcversion); 167 if (error) { 168 error = 0; 169 goto done; 170 } 171 172 error = xc5k_read_2(xc, XC5K_REG_BUILD, &xcbuild); 173 if (error) { 174 error = 0; 175 xcbuild = 0; 176 } 177 178 aprint_normal_dev(xc->parent, "xc5k: hw %d.%d, fw %d.%d.%d\n", 179 (xcversion >> 12) & 0xf, (xcversion >> 8) & 0xf, 180 (xcversion >> 4) & 0xf, xcversion & 0xf, xcbuild); 181 182 done: 183 if (fw) 184 firmware_free(fw, fwlen); 185 mutex_exit(&xc5k_firmware_lock); 186 187 if (error) 188 aprint_error_dev(xc->parent, 189 "xc5k: couldn't open firmware '%s/%s' (error=%d)\n", 190 XC5K_FIRMWARE_DRVNAME, XC5K_FIRMWARE_IMGNAME, error); 191 192 return error; 193 } 194 195 static int 196 xc5k_read_2(struct xc5k *xc, uint16_t reg, uint16_t *val) 197 { 198 uint8_t cmd[2], resp[2]; 199 int error; 200 201 cmd[0] = reg >> 8; 202 cmd[1] = reg & 0xff; 203 error = iic_exec(xc->i2c, I2C_OP_WRITE, xc->i2c_addr, 204 cmd, sizeof(cmd), NULL, 0, 0); 205 if (error) 206 return error; 207 resp[0] = resp[1] = 0; 208 error = iic_exec(xc->i2c, I2C_OP_READ, xc->i2c_addr, 209 NULL, 0, resp, sizeof(resp), 0); 210 if (error) 211 return error; 212 213 *val = (resp[0] << 8) | resp[1]; 214 215 return 0; 216 } 217 218 static int 219 xc5k_write_buffer(struct xc5k *xc, const uint8_t *data, size_t datalen) 220 { 221 return iic_exec(xc->i2c, I2C_OP_WRITE, xc->i2c_addr, 222 data, datalen, NULL, 0, 0); 223 } 224 225 static int 226 xc5k_write_2(struct xc5k *xc, uint16_t reg, uint16_t val) 227 { 228 uint8_t data[4]; 229 uint16_t busy; 230 int error, retry; 231 232 data[0] = reg >> 8; 233 data[1] = reg & 0xff; 234 data[2] = val >> 8; 235 data[3] = val & 0xff; 236 error = xc5k_write_buffer(xc, data, sizeof(data)); 237 if (error) 238 return error; 239 240 retry = 1000; 241 while (--retry > 0) { 242 error = xc5k_read_2(xc, XC5K_REG_BUSY, &busy); 243 if (error || !busy) 244 break; 245 delay(5000); 246 } 247 248 return error; 249 } 250 251 struct xc5k * 252 xc5k_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr, 253 xc5k_reset_cb reset, void *reset_priv, unsigned int if_freq, 254 fe_type_t fe_type) 255 { 256 struct xc5k *xc; 257 uint16_t product_id; 258 259 xc = kmem_alloc(sizeof(*xc), KM_SLEEP); 260 xc->parent = parent; 261 xc->i2c = i2c; 262 xc->i2c_addr = addr; 263 xc->reset = reset; 264 xc->reset_priv = reset_priv; 265 xc->if_freq = if_freq; 266 xc->fe_type = fe_type; 267 268 if (xc5k_read_2(xc, XC5K_REG_PRODUCT_ID, &product_id)) 269 goto failed; 270 271 aprint_debug_dev(parent, "xc5k: product=0x%04x\n", product_id); 272 273 if (product_id != XC5K_PRODUCT_ID_NOFW && product_id != XC5K_PRODUCT_ID) 274 goto failed; 275 276 if (xc5k_firmware_open(xc)) 277 goto failed; 278 if (xc5k_write_2(xc, XC5K_REG_INIT, 0)) 279 goto failed; 280 delay(100000); 281 282 if (xc5k_read_2(xc, XC5K_REG_PRODUCT_ID, &product_id)) 283 goto failed; 284 285 aprint_debug_dev(parent, "xc5k: product=0x%04x\n", product_id); 286 287 return xc; 288 289 failed: 290 kmem_free(xc, sizeof(*xc)); 291 return NULL; 292 } 293 294 void 295 xc5k_close(struct xc5k *xc) 296 { 297 kmem_free(xc, sizeof(*xc)); 298 } 299 300 int 301 xc5k_tune_video(struct xc5k *xc, struct xc5k_params *params) 302 { 303 uint16_t amode, vmode; 304 uint16_t lock, freq; 305 int retry; 306 307 switch (params->standard) { 308 case VIDEO_STANDARD_NTSC_M: 309 case VIDEO_STANDARD_NTSC_M_JP: 310 case VIDEO_STANDARD_NTSC_M_KR: 311 amode = XC5K_AUDIO_MODE_BTSC; 312 vmode = XC5K_VIDEO_MODE_BTSC; 313 break; 314 default: 315 return EINVAL; 316 } 317 318 if (xc5k_write_2(xc, XC5K_REG_SIGNAL_SOURCE, params->signal_source)) 319 return EIO; 320 if (xc5k_write_2(xc, XC5K_REG_VIDEO_MODE, vmode)) 321 return EIO; 322 if (xc5k_write_2(xc, XC5K_REG_AUDIO_MODE, amode)) 323 return EIO; 324 if (xc5k_write_2(xc, XC5K_REG_OUTAMP, XC5K_OUTAMP_ANALOG)) 325 return EIO; 326 freq = (params->frequency * 62500) / 15625; 327 #ifdef XC5K_DEBUG 328 printf("xc5k_tune_video: frequency=%u (%u Hz)\n", params->frequency, 329 params->frequency * 62500); 330 printf(" freq=%u\n", freq); 331 #endif 332 if (xc5k_write_2(xc, XC5K_REG_FINER_FREQ, freq)) 333 return EIO; 334 335 retry = 100; 336 while (--retry > 0) { 337 if (xc5k_read_2(xc, XC5K_REG_LOCK, &lock)) 338 return EIO; 339 #ifdef XC5K_DEBUG 340 printf("xc5k_tune_video: lock=0x%04x\n", lock); 341 #endif 342 if (lock == 1) 343 break; 344 delay(5000); 345 } 346 347 return 0; 348 } 349 350 int 351 xc5k_tune_dtv(struct xc5k *xc, const struct dvb_frontend_parameters *params) 352 { 353 uint16_t amode, vmode; 354 uint32_t freq, ifout; 355 int signal_source; 356 fe_modulation_t modulation; 357 358 if (xc->fe_type == FE_ATSC) 359 modulation = params->u.vsb.modulation; 360 else if (xc->fe_type == FE_QAM) 361 modulation = params->u.qam.modulation; 362 else 363 return EINVAL; 364 365 switch (modulation) { 366 case VSB_8: 367 case VSB_16: 368 signal_source = XC5K_SIGNAL_SOURCE_AIR; 369 switch (xc->fe_type) { 370 case FE_ATSC: 371 amode = XC5K_AUDIO_MODE_DTV6; 372 vmode = XC5K_VIDEO_MODE_DTV6; 373 freq = params->frequency - 1750000; 374 break; 375 default: 376 return EINVAL; 377 } 378 break; 379 case QAM_16: 380 case QAM_32: 381 case QAM_64: 382 case QAM_128: 383 case QAM_256: 384 signal_source = XC5K_SIGNAL_SOURCE_CABLE; 385 switch (xc->fe_type) { 386 case FE_ATSC: 387 amode = XC5K_AUDIO_MODE_DTV6; 388 vmode = XC5K_VIDEO_MODE_DTV6; 389 freq = params->frequency - 1750000; 390 break; 391 case FE_QAM: 392 amode = XC5K_AUDIO_MODE_DTV78; 393 vmode = XC5K_VIDEO_MODE_DTV78; 394 freq = params->frequency - 2750000; 395 break; 396 default: 397 return EINVAL; 398 } 399 break; 400 default: 401 return EINVAL; 402 } 403 404 if (freq > XC5K_FREQ_MAX || freq < XC5K_FREQ_MIN) 405 return ERANGE; 406 407 if (xc5k_write_2(xc, XC5K_REG_SIGNAL_SOURCE, signal_source)) 408 return EIO; 409 if (xc5k_write_2(xc, XC5K_REG_VIDEO_MODE, vmode)) 410 return EIO; 411 if (xc5k_write_2(xc, XC5K_REG_AUDIO_MODE, amode)) 412 return EIO; 413 ifout = ((xc->if_freq / 1000) * 1024) / 1000; 414 if (xc5k_write_2(xc, XC5K_REG_IF_OUT, ifout)) 415 return EIO; 416 if (xc5k_write_2(xc, XC5K_REG_OUTAMP, XC5K_OUTAMP_DIGITAL)) 417 return EIO; 418 freq = (uint16_t)(freq / 15625); 419 if (xc5k_write_2(xc, XC5K_REG_FINER_FREQ, freq)) 420 return EIO; 421 422 return 0; 423 } 424 425 fe_status_t 426 xc5k_get_status(struct xc5k *xc) 427 { 428 uint16_t lock_status; 429 fe_status_t festatus = 0; 430 431 if (xc5k_read_2(xc, XC5K_REG_LOCK, &lock_status)) 432 return 0; 433 if (lock_status & XC5K_LOCK_LOCKED) { 434 festatus |= FE_HAS_LOCK; 435 if ((lock_status & XC5K_LOCK_NOSIGNAL) == 0) 436 festatus |= FE_HAS_SIGNAL; 437 } 438 439 return festatus; 440 } 441 442 MODULE(MODULE_CLASS_DRIVER, xc5k, "i2cexec"); 443 444 static int 445 xc5k_modcmd(modcmd_t cmd, void *opaque) 446 { 447 switch (cmd) { 448 case MODULE_CMD_INIT: 449 mutex_init(&xc5k_firmware_lock, MUTEX_DEFAULT, IPL_NONE); 450 return 0; 451 case MODULE_CMD_FINI: 452 mutex_destroy(&xc5k_firmware_lock); 453 return 0; 454 default: 455 return ENOTTY; 456 } 457 } 458