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