xref: /netbsd-src/sys/dev/i2c/xc3028.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
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 
68 static kmutex_t	xc3028_firmware_lock;
69 
70 static int	xc3028_reset(struct xc3028 *);
71 static int	xc3028_read_2(struct xc3028 *, uint16_t, uint16_t *);
72 static int	xc3028_write_buffer(struct xc3028 *, const uint8_t *, size_t);
73 static int	xc3028_firmware_open(struct xc3028 *);
74 static int	xc3028_firmware_parse(struct xc3028 *, const uint8_t *, size_t);
75 static int	xc3028_firmware_upload(struct xc3028 *, struct xc3028_fw *);
76 static int	xc3028_scode_upload(struct xc3028 *, struct xc3028_fw *);
77 static void	xc3028_dump_fw(struct xc3028 *, struct xc3028_fw *,
78 		    const char *);
79 
80 static const char *
81 xc3028_name(struct xc3028 *xc)
82 {
83 	if (xc->type == XC3028L)
84 		return "xc3028l";
85 	else
86 		return "xc3028";
87 }
88 
89 static const char *
90 xc3028_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 
98 static int
99 xc3028_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 
109 static struct xc3028_fw *
110 xc3028_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 
124 static struct xc3028_fw *
125 xc3028_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 
139 static struct xc3028_fw *
140 xc3028_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 
157 static int
158 xc3028_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 
220 done:
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 
234 static 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 
268 static void
269 xc3028_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 
289 static int
290 xc3028_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 
353 corrupt:
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 
365 static int
366 xc3028_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 
422 static int
423 xc3028_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 
444 static int
445 xc3028_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 
467 static int
468 xc3028_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
475 static int
476 xc3028_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 
489 struct xc3028 *
490 xc3028_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 
513 failed:
514 	kmem_free(xc, sizeof(*xc));
515 	return NULL;
516 }
517 
518 void
519 xc3028_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 
534 int
535 xc3028_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 
567 MODULE(MODULE_CLASS_DRIVER, xc3028, "i2cexec");
568 
569 static int
570 xc3028_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