1/* $NetBSD: xc3028.c,v 1.7 2015/03/07 14:16:51 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.7 2015/03/07 14:16:51 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, fwlen);
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, "i2cexec");
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