1/* $NetBSD: xc3028.c,v 1.4 2011/08/09 10:54:22 jmcneill 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 * Xceive XC3028L 31 */ 32 33#include <sys/cdefs.h> 34__KERNEL_RCSID(0, "$NetBSD: xc3028.c,v 1.4 2011/08/09 10:54:22 jmcneill 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/xc3028reg.h> 49#include <dev/i2c/xc3028var.h> 50 51#define XC3028_FIRMWARE_DRVNAME "xc3028" 52 53#define XC3028_FREQ_MIN 1000000 54#define XC3028_FREQ_MAX 1023000000 55 56#define XC3028_FW_BASE (1 << 0) 57#define XC3028_FW_D2633 (1 << 4) 58#define XC3028_FW_DTV6 (1 << 5) 59#define XC3028_FW_QAM (1 << 6) 60#define XC3028_FW_ATSC (1 << 16) 61#define XC3028_FW_LG60 (1 << 18) 62#define XC3028_FW_F6MHZ (1 << 27) 63#define XC3028_FW_SCODE (1 << 29) 64#define XC3028_FW_HAS_IF (1 << 30) 65 66#define XC3028_FW_DEFAULT (XC3028_FW_ATSC|XC3028_FW_D2633|XC3028_FW_DTV6) 67 68static kmutex_t xc3028_firmware_lock; 69 70static int xc3028_reset(struct xc3028 *); 71static int xc3028_read_2(struct xc3028 *, uint16_t, uint16_t *); 72static int xc3028_write_buffer(struct xc3028 *, const uint8_t *, size_t); 73static int xc3028_firmware_open(struct xc3028 *); 74static int xc3028_firmware_parse(struct xc3028 *, const uint8_t *, size_t); 75static int xc3028_firmware_upload(struct xc3028 *, struct xc3028_fw *); 76static int xc3028_scode_upload(struct xc3028 *, struct xc3028_fw *); 77static void xc3028_dump_fw(struct xc3028 *, struct xc3028_fw *, 78 const char *); 79 80static const char * 81xc3028_name(struct xc3028 *xc) 82{ 83 if (xc->type == XC3028L) 84 return "xc3028l"; 85 else 86 return "xc3028"; 87} 88 89static const char * 90xc3028_firmware_name(struct xc3028 *xc) 91{ 92 if (xc->type == XC3028L) 93 return "xc3028L-v36.fw"; 94 else 95 return "xc3028-v27.fw"; 96} 97 98static int 99xc3028_reset(struct xc3028 *xc) 100{ 101 int error = 0; 102 103 if (xc->reset) 104 error = xc->reset(xc->reset_priv); 105 106 return error; 107} 108 109static struct xc3028_fw * 110xc3028_get_basefw(struct xc3028 *xc) 111{ 112 struct xc3028_fw *fw; 113 unsigned int i; 114 115 for (i = 0; i < xc->nfw; i++) { 116 fw = &xc->fw[i]; 117 if (fw->type == XC3028_FW_BASE) 118 return fw; 119 } 120 121 return NULL; 122} 123 124static struct xc3028_fw * 125xc3028_get_stdfw(struct xc3028 *xc) 126{ 127 struct xc3028_fw *fw; 128 unsigned int i; 129 130 for (i = 0; i < xc->nfw; i++) { 131 fw = &xc->fw[i]; 132 if (fw->type == (XC3028_FW_D2633|XC3028_FW_DTV6|XC3028_FW_ATSC)) 133 return fw; 134 } 135 136 return NULL; 137} 138 139static struct xc3028_fw * 140xc3028_get_scode(struct xc3028 *xc) 141{ 142 struct xc3028_fw *fw; 143 unsigned int i; 144 145 for (i = 0; i < xc->nfw; i++) { 146 fw = &xc->fw[i]; 147 if (fw->type == 148 (XC3028_FW_DTV6|XC3028_FW_QAM|XC3028_FW_ATSC|XC3028_FW_LG60| 149 XC3028_FW_F6MHZ|XC3028_FW_SCODE|XC3028_FW_HAS_IF) && 150 fw->int_freq == 6200) 151 return fw; 152 } 153 154 return NULL; 155} 156 157static int 158xc3028_firmware_open(struct xc3028 *xc) 159{ 160 firmware_handle_t fwh; 161 struct xc3028_fw *basefw, *stdfw, *scode; 162 uint8_t *fw = NULL; 163 uint16_t xcversion = 0; 164 size_t fwlen; 165 int error; 166 167 mutex_enter(&xc3028_firmware_lock); 168 169 error = firmware_open(XC3028_FIRMWARE_DRVNAME, 170 xc3028_firmware_name(xc), &fwh); 171 if (error) 172 goto done; 173 fwlen = firmware_get_size(fwh); 174 fw = firmware_malloc(fwlen); 175 if (fw == NULL) { 176 firmware_close(fwh); 177 error = ENOMEM; 178 goto done; 179 } 180 error = firmware_read(fwh, 0, fw, fwlen); 181 firmware_close(fwh); 182 if (error) 183 goto done; 184 185 device_printf(xc->parent, "%s: loading firmware '%s/%s'\n", 186 xc3028_name(xc), XC3028_FIRMWARE_DRVNAME, xc3028_firmware_name(xc)); 187 error = xc3028_firmware_parse(xc, fw, fwlen); 188 if (!error) { 189 basefw = xc3028_get_basefw(xc); 190 stdfw = xc3028_get_stdfw(xc); 191 scode = xc3028_get_scode(xc); 192 if (basefw && stdfw) { 193 xc3028_reset(xc); 194 xc3028_dump_fw(xc, basefw, "base"); 195 error = xc3028_firmware_upload(xc, basefw); 196 if (error) 197 return error; 198 xc3028_dump_fw(xc, stdfw, "std"); 199 error = xc3028_firmware_upload(xc, stdfw); 200 if (error) 201 return error; 202 if (scode) { 203 xc3028_dump_fw(xc, scode, "scode"); 204 error = xc3028_scode_upload(xc, scode); 205 if (error) 206 return error; 207 } 208 } else 209 error = ENODEV; 210 } 211 if (!error) { 212 xc3028_read_2(xc, XC3028_REG_VERSION, &xcversion); 213 214 device_printf(xc->parent, "%s: hw %d.%d, fw %d.%d\n", 215 xc3028_name(xc), 216 (xcversion >> 12) & 0xf, (xcversion >> 8) & 0xf, 217 (xcversion >> 4) & 0xf, (xcversion >> 0) & 0xf); 218 } 219 220done: 221 if (fw) 222 firmware_free(fw, 0); 223 mutex_exit(&xc3028_firmware_lock); 224 225 if (error) 226 aprint_error_dev(xc->parent, 227 "%s: couldn't open firmware '%s/%s' (error=%d)\n", 228 xc3028_name(xc), XC3028_FIRMWARE_DRVNAME, 229 xc3028_firmware_name(xc), error); 230 231 return error; 232} 233 234static const char *xc3028_fw_types[] = { 235 "BASE", 236 "F8MHZ", 237 "MTS", 238 "D2620", 239 "D2633", 240 "DTV6", 241 "QAM", 242 "DTV7", 243 "DTV78", 244 "DTV8", 245 "FM", 246 "INPUT1", 247 "LCD", 248 "NOGD", 249 "INIT1", 250 "MONO", 251 "ATSC", 252 "IF", 253 "LG60", 254 "ATI638", 255 "OREN538", 256 "OREN36", 257 "TOYOTA388", 258 "TOYOTA794", 259 "DIBCOM52", 260 "ZARLINK456", 261 "CHINA", 262 "F6MHZ", 263 "INPUT2", 264 "SCODE", 265 "HAS_IF", 266}; 267 268static void 269xc3028_dump_fw(struct xc3028 *xc, struct xc3028_fw *xcfw, const char *type) 270{ 271 unsigned int i; 272 273 device_printf(xc->parent, "%s: %s:", xc3028_name(xc), type); 274 if (xcfw == NULL) { 275 printf(" <none>\n"); 276 return; 277 } 278 for (i = 0; i < __arraycount(xc3028_fw_types); i++) { 279 if (xcfw->type & (1 << i)) 280 printf(" %s", xc3028_fw_types[i]); 281 } 282 if (xcfw->type & (1 << 30)) 283 printf("_%d", xcfw->int_freq); 284 if (xcfw->id) 285 printf(" id=%" PRIx64, xcfw->id); 286 printf(" size=%u\n", xcfw->data_size); 287} 288 289static int 290xc3028_firmware_parse(struct xc3028 *xc, const uint8_t *fw, size_t fwlen) 291{ 292 const uint8_t *p = fw, *endp = p + fwlen; 293 char fwname[32 + 1]; 294 uint16_t fwver, narr; 295 unsigned int index; 296 struct xc3028_fw *xcfw; 297 298 if (fwlen < 36) 299 return EINVAL; 300 301 /* first 32 bytes are the firmware name string */ 302 memset(fwname, 0, sizeof(fwname)); 303 memcpy(fwname, p, sizeof(fwname) - 1); 304 p += (sizeof(fwname) - 1); 305 306 fwver = le16dec(p); 307 p += sizeof(fwver); 308 narr = le16dec(p); 309 p += sizeof(narr); 310 311 aprint_debug_dev(xc->parent, "%s: fw type %s, ver %d.%d, %d images\n", 312 xc3028_name(xc), fwname, fwver >> 8, fwver & 0xff, narr); 313 314 xc->fw = kmem_zalloc(sizeof(*xc->fw) * narr, KM_SLEEP); 315 if (xc->fw == NULL) 316 return ENOMEM; 317 xc->nfw = narr; 318 319 for (index = 0; index < xc->nfw && p < endp; index++) { 320 xcfw = &xc->fw[index]; 321 322 if (endp - p < 16) 323 goto corrupt; 324 325 xcfw->type = le32dec(p); 326 p += sizeof(xcfw->type); 327 328 xcfw->id = le64dec(p); 329 p += sizeof(xcfw->id); 330 331 if (xcfw->type & XC3028_FW_HAS_IF) { 332 xcfw->int_freq = le16dec(p); 333 p += sizeof(xcfw->int_freq); 334 if ((uint32_t)(endp - p) < sizeof(xcfw->data_size)) 335 goto corrupt; 336 } 337 338 xcfw->data_size = le32dec(p); 339 p += sizeof(xcfw->data_size); 340 341 if (xcfw->data_size == 0 || 342 xcfw->data_size > (uint32_t)(endp - p)) 343 goto corrupt; 344 xcfw->data = kmem_alloc(xcfw->data_size, KM_SLEEP); 345 if (xcfw->data == NULL) 346 goto corrupt; 347 memcpy(xcfw->data, p, xcfw->data_size); 348 p += xcfw->data_size; 349 } 350 351 return 0; 352 353corrupt: 354 aprint_error_dev(xc->parent, "%s: fw image corrupt\n", xc3028_name(xc)); 355 for (index = 0; index < xc->nfw; index++) { 356 if (xc->fw[index].data) 357 kmem_free(xc->fw[index].data, xc->fw[index].data_size); 358 } 359 kmem_free(xc->fw, sizeof(*xc->fw) * xc->nfw); 360 xc->nfw = 0; 361 362 return ENXIO; 363} 364 365static int 366xc3028_firmware_upload(struct xc3028 *xc, struct xc3028_fw *xcfw) 367{ 368 const uint8_t *fw = xcfw->data, *p; 369 uint32_t fwlen = xcfw->data_size; 370 uint8_t cmd[64]; 371 unsigned int i; 372 uint16_t len, rem; 373 size_t wrlen; 374 int error; 375 376 for (i = 0; i < fwlen - 2;) { 377 len = le16dec(&fw[i]); 378 i += 2; 379 if (len == 0xffff) 380 break; 381 382 /* reset command */ 383 if (len == 0x0000) { 384 error = xc3028_reset(xc); 385 if (error) 386 return error; 387 continue; 388 } 389 /* reset clk command */ 390 if (len == 0xff00) { 391 continue; 392 } 393 /* delay command */ 394 if (len & 0x8000) { 395 delay((len & 0x7fff) * 1000); 396 continue; 397 } 398 399 if (i + len > fwlen) { 400 printf("weird len, i=%u len=%u fwlen=%u'\n", i, len, fwlen); 401 return EINVAL; 402 } 403 404 cmd[0] = fw[i]; 405 p = &fw[i + 1]; 406 rem = len - 1; 407 while (rem > 0) { 408 wrlen = min(rem, __arraycount(cmd) - 1); 409 memcpy(&cmd[1], p, wrlen); 410 error = xc3028_write_buffer(xc, cmd, wrlen + 1); 411 if (error) 412 return error; 413 p += wrlen; 414 rem -= wrlen; 415 } 416 i += len; 417 } 418 419 return 0; 420} 421 422static int 423xc3028_scode_upload(struct xc3028 *xc, struct xc3028_fw *xcfw) 424{ 425 static uint8_t scode_init[] = { 0xa0, 0x00, 0x00, 0x00 }; 426 static uint8_t scode_fini[] = { 0x00, 0x8c }; 427 int error; 428 429 if (xcfw->data_size < 12) 430 return EINVAL; 431 error = xc3028_write_buffer(xc, scode_init, sizeof(scode_init)); 432 if (error) 433 return error; 434 error = xc3028_write_buffer(xc, xcfw->data, 12); 435 if (error) 436 return error; 437 error = xc3028_write_buffer(xc, scode_fini, sizeof(scode_fini)); 438 if (error) 439 return error; 440 441 return 0; 442} 443 444static int 445xc3028_read_2(struct xc3028 *xc, uint16_t reg, uint16_t *val) 446{ 447 uint8_t cmd[2], resp[2]; 448 int error; 449 450 cmd[0] = reg >> 8; 451 cmd[1] = reg & 0xff; 452 error = iic_exec(xc->i2c, I2C_OP_WRITE, xc->i2c_addr, 453 cmd, sizeof(cmd), NULL, 0, 0); 454 if (error) 455 return error; 456 resp[0] = resp[1] = 0; 457 error = iic_exec(xc->i2c, I2C_OP_READ, xc->i2c_addr, 458 NULL, 0, resp, sizeof(resp), 0); 459 if (error) 460 return error; 461 462 *val = (resp[0] << 8) | resp[1]; 463 464 return 0; 465} 466 467static int 468xc3028_write_buffer(struct xc3028 *xc, const uint8_t *data, size_t datalen) 469{ 470 return iic_exec(xc->i2c, I2C_OP_WRITE_WITH_STOP, xc->i2c_addr, 471 data, datalen, NULL, 0, 0); 472} 473 474#if notyet 475static int 476xc3028_write_2(struct xc3028 *xc, uint16_t reg, uint16_t val) 477{ 478 uint8_t data[4]; 479 480 data[0] = reg >> 8; 481 data[1] = reg & 0xff; 482 data[2] = val >> 8; 483 data[3] = val & 0xff; 484 485 return xc3028_write_buffer(xc, data, sizeof(data)); 486} 487#endif 488 489struct xc3028 * 490xc3028_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr, 491 xc3028_reset_cb reset, void *reset_priv, enum xc3028_type type) 492{ 493 struct xc3028 *xc; 494 495 xc = kmem_alloc(sizeof(*xc), KM_SLEEP); 496 if (xc == NULL) 497 return NULL; 498 xc->parent = parent; 499 xc->i2c = i2c; 500 xc->i2c_addr = addr; 501 xc->reset = reset; 502 xc->reset_priv = reset_priv; 503 xc->type = type; 504 505 if (xc3028_firmware_open(xc)) { 506 aprint_error_dev(parent, "%s: fw open failed\n", 507 xc3028_name(xc)); 508 goto failed; 509 } 510 511 return xc; 512 513failed: 514 kmem_free(xc, sizeof(*xc)); 515 return NULL; 516} 517 518void 519xc3028_close(struct xc3028 *xc) 520{ 521 unsigned int index; 522 523 if (xc->fw) { 524 for (index = 0; index < xc->nfw; index++) { 525 if (xc->fw[index].data) 526 kmem_free(xc->fw[index].data, 527 xc->fw[index].data_size); 528 } 529 kmem_free(xc->fw, sizeof(*xc->fw) * xc->nfw); 530 } 531 kmem_free(xc, sizeof(*xc)); 532} 533 534int 535xc3028_tune_dtv(struct xc3028 *xc, const struct dvb_frontend_parameters *params) 536{ 537 static uint8_t freq_init[] = { 0x80, 0x02, 0x00, 0x00 }; 538 uint8_t freq_buf[4]; 539 uint32_t div, offset = 0; 540 int error; 541 542 if (params->u.vsb.modulation == VSB_8) { 543 offset = 1750000; 544 } else { 545 return EINVAL; 546 } 547 548 div = (params->frequency - offset + 15625 / 2) / 15625; 549 550 error = xc3028_write_buffer(xc, freq_init, sizeof(freq_init)); 551 if (error) 552 return error; 553 delay(10000); 554 555 freq_buf[0] = (div >> 24) & 0xff; 556 freq_buf[1] = (div >> 16) & 0xff; 557 freq_buf[2] = (div >> 8) & 0xff; 558 freq_buf[3] = (div >> 0) & 0xff; 559 error = xc3028_write_buffer(xc, freq_buf, sizeof(freq_buf)); 560 if (error) 561 return error; 562 delay(100000); 563 564 return 0; 565} 566 567MODULE(MODULE_CLASS_DRIVER, xc3028, "iic"); 568 569static int 570xc3028_modcmd(modcmd_t cmd, void *opaque) 571{ 572 switch (cmd) { 573 case MODULE_CMD_INIT: 574 mutex_init(&xc3028_firmware_lock, MUTEX_DEFAULT, IPL_NONE); 575 return 0; 576 case MODULE_CMD_FINI: 577 mutex_destroy(&xc3028_firmware_lock); 578 return 0; 579 default: 580 return ENOTTY; 581 } 582} 583