1/* $NetBSD: nxt2k.c,v 1.7 2019/12/31 14:27:50 thorpej Exp $ */
2
3/*
4 * Copyright (c) 2008, 2011 Jonathan A. Kollasch
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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: nxt2k.c,v 1.7 2019/12/31 14:27:50 thorpej Exp $");
31
32#include <sys/param.h>
33#include <sys/kernel.h>
34#include <sys/systm.h>
35#include <sys/device.h>
36#include <sys/kmem.h>
37#include <sys/condvar.h>
38#include <sys/mutex.h>
39#include <sys/module.h>
40
41#include <dev/firmload.h>
42
43#include <dev/i2c/nxt2kvar.h>
44
45struct nxt2k {
46	device_t        parent;
47	i2c_tag_t       tag;
48	i2c_addr_t      addr;
49	kcondvar_t      cv;
50	kmutex_t        mtx;
51	bool            loaded; /* firmware is loaded? */
52};
53
54static int nxt2k_init(struct nxt2k *);
55
56static int nxt2k_writedata(struct nxt2k *, uint8_t, uint8_t *, size_t);
57static int nxt2k_readdata(struct nxt2k *, uint8_t, uint8_t *, size_t);
58static int nxt2k_writereg(struct nxt2k *, uint8_t, uint8_t *, size_t);
59static int nxt2k_readreg(struct nxt2k*, uint8_t, uint8_t *, size_t);
60
61static int nxt2k4_init(struct nxt2k *);
62static bool nxt2k4_load_firmware(struct nxt2k *);
63static void nxt2k4_mc_init(struct nxt2k *);
64static void nxt2k_mc_start(struct nxt2k *);
65static void nxt2k_mc_stop(struct nxt2k *);
66static void nxt2k_agc_reset(struct nxt2k *);
67static uint16_t nxt2k_crc_ccit(uint16_t, uint8_t);
68
69static int
70nxt2k_writedata(struct nxt2k *nxt, uint8_t reg, uint8_t *data, size_t len)
71{
72	uint8_t buffer[384];
73	int error;
74
75	KASSERT((len + 1) <= 384);
76
77	if ((error = iic_acquire_bus(nxt->tag, 0)) != 0)
78		return error;
79
80	buffer[0] = reg;
81	memcpy(&buffer[1], data, len);
82
83	error = iic_exec(nxt->tag, I2C_OP_WRITE_WITH_STOP, nxt->addr,
84			 buffer, len + 1, NULL, 0, 0);
85
86	iic_release_bus(nxt->tag, 0);
87
88	return error;
89}
90
91static int
92nxt2k_readdata(struct nxt2k *nxt, uint8_t reg, uint8_t *data, size_t len)
93{
94	int error;
95
96	if ((error = iic_acquire_bus(nxt->tag, 0)) != 0)
97		return error;
98
99	error = iic_exec(nxt->tag, I2C_OP_READ_WITH_STOP, nxt->addr,
100			 &reg, 1, data, len, 0);
101
102	iic_release_bus(nxt->tag, 0);
103
104	return error;
105}
106
107static int
108nxt2k_writereg(struct nxt2k *nxt, uint8_t reg, uint8_t *data, size_t len)
109{
110	uint8_t attr, len2, buf;
111
112	nxt2k_writedata(nxt, 0x35, &reg, 1);
113
114	nxt2k_writedata(nxt, 0x36, data, len);
115
116	attr = 0x02;
117	if (reg & 0x80) {
118		attr = attr << 1;
119		if (reg & 0x04)
120			attr = attr >> 1;
121	}
122	len2 = ((attr << 4) | 0x10) | len;
123	buf = 0x80;
124
125	nxt2k_writedata(nxt, 0x34, &len2, 1);
126
127	nxt2k_writedata(nxt, 0x21, &buf, 1);
128
129	nxt2k_readdata(nxt, 0x21, &buf, 1);
130
131	if (buf == 0)
132		return 0;
133
134	return -1;
135}
136
137static int
138nxt2k_readreg(struct nxt2k *nxt, uint8_t reg, uint8_t *data, size_t len)
139{
140	uint8_t buf, len2, attr;
141	unsigned int i;
142
143	nxt2k_writedata(nxt, 0x35, &reg, 1);
144
145	attr = 0x02;
146	if (reg & 0x80) {
147		attr = attr << 1;
148		if (reg & 0x04)
149			attr = attr >> 1;
150	}
151
152	len2 = (attr << 4) | len;
153	nxt2k_writedata(nxt, 0x34, &len2, 1);
154
155	buf = 0x80;
156	nxt2k_writedata(nxt, 0x21, &buf, 1);
157
158	for(i = 0; i < len; i++) {
159		nxt2k_readdata(nxt, 0x36+i, &data[i], 1);
160	}
161
162	return 0;
163}
164
165static void
166nxt2k_agc_reset(struct nxt2k *nxt)
167{
168	uint8_t byte;
169	nxt2k_readreg(nxt, 0x08, &byte, 1);
170	byte = 0x08;
171	nxt2k_writereg(nxt, 0x08, &byte, 1);
172	byte = 0x00;
173	nxt2k_writereg(nxt, 0x08, &byte, 1);
174	return;
175}
176
177static void
178nxt2k_mc_stop(struct nxt2k *nxt)
179{
180	int counter;
181	uint8_t stopval, buf;
182
183	/* 2k4 */
184	stopval = 0x10;
185
186	buf = 0x80;
187	nxt2k_writedata(nxt, 0x22, &buf, 1);
188
189	for(counter = 0; counter < 20; counter++) {
190		nxt2k_readdata(nxt, 0x31, &buf, 1);
191		if (buf & stopval)
192			return;
193		mutex_enter(&nxt->mtx);
194		cv_timedwait(&nxt->cv, &nxt->mtx, mstohz(10));
195		mutex_exit(&nxt->mtx);
196	}
197
198	printf("%s timeout\n", __func__);
199
200	return;
201}
202
203static void
204nxt2k_mc_start(struct nxt2k *nxt)
205{
206	uint8_t buf;
207
208	buf = 0x00;
209	nxt2k_writedata(nxt, 0x22, &buf, 1);
210}
211
212static void
213nxt2k4_mc_init(struct nxt2k *nxt)
214{
215	uint8_t byte;
216	uint8_t data[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xC0};
217	int counter;
218
219	byte = 0x00;
220	nxt2k_writedata(nxt, 0x2b, &byte, 1);
221	byte = 0x70;
222	nxt2k_writedata(nxt, 0x34, &byte, 1);
223	byte = 0x04;
224	nxt2k_writedata(nxt, 0x35, &byte, 1);
225
226	nxt2k_writedata(nxt, 0x36, data, 9);
227
228	byte = 0x80;
229	nxt2k_writedata(nxt, 0x21, &byte, 1);
230
231	for(counter = 0; counter < 20; counter++) {
232		nxt2k_readdata(nxt, 0x21, &byte, 1);
233		if ( byte == 0 )
234			return;
235		mutex_enter(&nxt->mtx);
236		cv_timedwait(&nxt->cv, &nxt->mtx, mstohz(25));
237		mutex_exit(&nxt->mtx);
238	}
239
240	printf("%s timeout\n", __func__);
241
242	return;
243}
244
245/* CRC-CCIT */
246static uint16_t
247nxt2k_crc_ccit(uint16_t crc, uint8_t byte)
248{
249	int i;
250	uint16_t input;
251
252	input = byte << 8;
253
254	for(i = 0; i < 8; i++) {
255		if ((crc ^ input) & 0x8000)
256			crc = (crc << 1) ^ 0x1021;
257		else
258			crc = (crc << 1);
259		input = input << 1;
260	}
261	return crc;
262}
263
264static bool
265nxt2k4_load_firmware(struct nxt2k *nxt)
266{
267	firmware_handle_t fh;
268	uint8_t *blob;
269	size_t fwsize;
270	size_t position;
271	int error;
272	uint16_t crc;
273
274	error = firmware_open("nxt2k", "dvb-fe-nxt2004.fw", &fh);
275	if (error != 0) {
276		printf("nxt2k firmware_open fail %d\n", error);
277		return 0;
278	}
279
280	fwsize = firmware_get_size(fh);
281	printf("fwsize %zd\n", fwsize);
282	blob = firmware_malloc(fwsize);
283	if ( blob == NULL ) {
284		printf("nxt2k firmware_malloc fail\n");
285		firmware_close(fh);
286		return -1;
287	}
288
289	error = firmware_read(fh, 0, blob, fwsize);
290	if (error != 0) {
291		printf("nxt2k firmware_read fail %d\n", error);
292		firmware_free(blob, fwsize);
293		firmware_close(fh);
294		return -1;
295	}
296
297	/* calculate CRC */
298	crc = 0;
299	for(position = 0; position < fwsize; position++) {
300		crc = nxt2k_crc_ccit(crc, blob[position]);
301	}
302	printf("nxt2k firmware crc is %02x\n", crc);
303
304	uint16_t rambase;
305	uint8_t buf[3];
306
307	rambase = 0x1000;
308
309	/* hold the micro in reset while loading firmware */
310	buf[0] = 0x80;
311	nxt2k_writedata(nxt, 0x2b, buf, 1);
312
313	buf[0] = rambase >> 8;
314	buf[1] = rambase & 0xFF;
315	buf[2] = 0x81;
316	/* write starting address */
317	nxt2k_writedata(nxt, 0x29, buf, 3);
318
319	position = 0;
320
321	size_t xfercnt;
322
323	while ( position < fwsize ) {
324		xfercnt = fwsize - position > 255 ? 255 : fwsize - position;
325		nxt2k_writedata(nxt, 0x2c, &blob[position], xfercnt);
326		position += xfercnt;
327	}
328
329	/* write crc */
330        buf[0] = crc >> 8;
331        buf[1] = crc & 0xFF;
332	nxt2k_writedata(nxt, 0x2c, buf, 2);
333
334	/* do a read to stop things */
335	nxt2k_readdata(nxt, 0x2c, buf, 1);
336
337	/* set transfer mode to complete */
338	buf[0] = 0x80;
339	nxt2k_writedata(nxt, 0x2b, buf, 1);
340
341	firmware_free(blob, fwsize);
342	firmware_close(fh);
343
344	return 1;
345}
346
347static int
348nxt2k4_init(struct nxt2k *nxt)
349{
350	int success;
351	uint8_t buf[3];
352
353	buf[0] = 0x00;
354	nxt2k_writedata(nxt, 0x1e, buf, 1);
355
356	/* try to load firmware */
357	nxt->loaded = nxt2k4_load_firmware(nxt);
358	if (nxt->loaded == false)
359		return ECANCELED;
360
361	/* ensure transfer is complete */
362	buf[0] = 0x01;
363	nxt2k_writedata(nxt, 0x19, buf, 1);
364
365	nxt2k4_mc_init(nxt);
366	nxt2k_mc_stop(nxt);
367	nxt2k_mc_stop(nxt);
368	nxt2k4_mc_init(nxt);
369	nxt2k_mc_stop(nxt);
370
371	buf[0] = 0xff;
372	nxt2k_writereg(nxt, 0x08, buf, 1);
373	buf[0] = 0x00;
374	nxt2k_writereg(nxt, 0x08, buf, 1);
375
376	buf[0] = 0xD7;
377	nxt2k_writedata(nxt, 0xd7, buf, 1);
378
379	buf[0] = 0x07;
380	buf[1] = 0xfe;
381	nxt2k_writedata(nxt, 0x35, buf, 2);
382	buf[0] = 0x12;
383	nxt2k_writedata(nxt, 0x34, buf, 1);
384	buf[0] = 0x80;
385	nxt2k_writedata(nxt, 0x21, buf, 1);
386
387	buf[0] = 0x21;
388	nxt2k_writedata(nxt, 0x0a, buf, 1);
389
390	buf[0] = 0x01;
391	nxt2k_writereg(nxt, 0x80, buf, 1);
392
393	/* fec mpeg mode */
394	buf[0] = 0x7E;
395	buf[1] = 0x00;
396	nxt2k_writedata(nxt, 0xe9, buf, 2);
397
398	/* mux selection */
399	buf[0] = 0x00;
400	nxt2k_writedata(nxt, 0xcc, buf, 1);
401
402	/* */
403	nxt2k_readreg(nxt, 0x80, buf, 1);
404	buf[0] = 0x00;
405	nxt2k_writereg(nxt, 0x80, buf, 1);
406
407	/* soft reset? */
408	nxt2k_readreg(nxt, 0x08, buf, 1);
409	buf[0] = 0x10;
410	nxt2k_writereg(nxt, 0x08, buf, 1);
411	nxt2k_readreg(nxt, 0x08, buf, 1);
412	buf[0] = 0x00;
413	nxt2k_writereg(nxt, 0x08, buf, 1);
414
415	/* */
416	nxt2k_readreg(nxt, 0x80, buf, 1);
417	buf[0] = 0x01;
418	nxt2k_writereg(nxt, 0x80, buf, 1);
419	buf[0] = 0x70;
420	nxt2k_writereg(nxt, 0x81, buf, 1);
421	buf[0] = 0x31; buf[1] = 0x5E; buf[2] = 0x66;
422	nxt2k_writereg(nxt, 0x82, buf, 3);
423
424	nxt2k_readreg(nxt, 0x88, buf, 1);
425	buf[0] = 0x11;
426	nxt2k_writereg(nxt, 0x88, buf, 1);
427	nxt2k_readreg(nxt, 0x80, buf, 1);
428	buf[0] = 0x40;
429	nxt2k_writereg(nxt, 0x80, buf, 1);
430
431	nxt2k_readdata(nxt, 0x10, buf, 1);
432	buf[0] = 0x10;
433	nxt2k_writedata(nxt, 0x10, buf, 1);
434	nxt2k_readdata(nxt, 0x0a, buf, 1);
435	buf[0] = 0x21;
436	nxt2k_writedata(nxt, 0x0a, buf, 1);
437
438	nxt2k4_mc_init(nxt);
439
440	buf[0] = 0x21;
441	nxt2k_writedata(nxt, 0x0a, buf, 1);
442	buf[0] = 0x7e;
443	nxt2k_writedata(nxt, 0xe9, buf, 1);
444	buf[0] = 0x00;
445	nxt2k_writedata(nxt, 0xea, buf, 1);
446
447	nxt2k_readreg(nxt, 0x80, buf, 1);
448	buf[0] = 0x00;
449	nxt2k_writereg(nxt, 0x80, buf, 1);
450	nxt2k_readreg(nxt, 0x80, buf, 1);
451	buf[0] = 0x00;
452	nxt2k_writereg(nxt, 0x80, buf, 1);
453
454	nxt2k_readreg(nxt, 0x08, buf, 1);
455	buf[0] = 0x10;
456	nxt2k_writereg(nxt, 0x08, buf, 1);
457	nxt2k_readreg(nxt, 0x08, buf, 1);
458	buf[0] = 0x00;
459	nxt2k_writereg(nxt, 0x08, buf, 1);
460
461	nxt2k_readreg(nxt, 0x80, buf, 1);
462	buf[0] = 0x04;
463	nxt2k_writereg(nxt, 0x80, buf, 1);
464	buf[0] = 0x00;
465	nxt2k_writereg(nxt, 0x81, buf, 1);
466	buf[0] = 0x80; buf[1] = 0x00; buf[2] = 0x00;
467	nxt2k_writereg(nxt, 0x82, buf, 3);
468
469	nxt2k_readreg(nxt, 0x88, buf, 1);
470	buf[0] = 0x11;
471	nxt2k_writereg(nxt, 0x88, buf, 1);
472
473	nxt2k_readreg(nxt, 0x80, buf, 1);
474	buf[0] = 0x44;
475	nxt2k_writereg(nxt, 0x80, buf, 1);
476
477	/* init tuner */
478	nxt2k_readdata(nxt, 0x10, buf, 1);
479	buf[0] = 0x12;
480	nxt2k_writedata(nxt, 0x10, buf,1);
481	buf[0] = 0x04;
482	nxt2k_writedata(nxt, 0x13, buf,1);
483	buf[0] = 0x00;
484	nxt2k_writedata(nxt, 0x16, buf,1);
485	buf[0] = 0x04;
486	nxt2k_writedata(nxt, 0x14, buf,1);
487	buf[0] = 0x00;
488	nxt2k_writedata(nxt, 0x14, buf,1);
489	nxt2k_writedata(nxt, 0x17, buf,1);
490	nxt2k_writedata(nxt, 0x14, buf,1);
491	nxt2k_writedata(nxt, 0x17, buf,1);
492
493	success = 1;
494	return success;
495}
496
497uint16_t
498nxt2k_get_signal(struct nxt2k *nxt)
499{
500	uint16_t temp;
501	uint8_t b[2];
502
503	b[0] = 0x00;
504	nxt2k_writedata(nxt, 0xa1, b, 1);
505
506	nxt2k_readreg(nxt, 0xa6, b, 2);
507
508	temp = (b[0] << 8) | b[1];
509
510	printf("a6: %04hx\n", temp);
511
512	return 0x7fff - temp * 16;
513}
514
515uint16_t
516nxt2k_get_snr(struct nxt2k *nxt)
517{
518	uint32_t tsnr;
519	uint16_t temp, temp2;
520	uint8_t b[2];
521
522	b[0] = 0x00;
523	nxt2k_writedata(nxt, 0xa1, b, 1);
524
525	nxt2k_readreg(nxt, 0xa6, b, 2);
526
527	temp = (b[0] << 8) | b[1];
528
529	temp2 = 0x7fff - temp;
530
531	printf("snr temp2: %04hx\n", temp2);
532
533	if (temp2 > 0x7f00)
534		tsnr = 1000*24+(1000*(30-24)*(temp2-0x7f00)/(0x7fff-0x7f00));
535	else if ( temp2 > 0x7ec0)
536		tsnr = 1000*18+(1000*(24-18)*(temp2-0x7ec0)/(0x7f00-0x7ec0));
537	else if ( temp2 > 0x7c00)
538		tsnr = 1000*12+(1000*(18-12)*(temp2-0x7c00)/(0x7ec0-0x7c00));
539	else
540		tsnr = 1000*0+(1000*(12-0)*(temp2-0)/(0x7c00-0));
541
542	printf("snr tsnr: %08x\n", tsnr);
543
544	return ((tsnr * 0xffff)/32000);
545}
546
547fe_status_t
548nxt2k_get_dtv_status(struct nxt2k *nxt)
549{
550	uint8_t reg;
551	fe_status_t status = 0;
552
553	nxt2k_readdata(nxt, 0x31, &reg, 1);
554	if (reg & 0x20) {
555		status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
556		    FE_HAS_SYNC | FE_HAS_LOCK;
557	}
558
559	return status;
560}
561
562#if notyet
563int
564nxt2k_fe_read_ucblocks(struct nxt2k *nxt, uint32_t *ucblk)
565{
566	uint8_t reg[3];
567
568	nxt2k_readreg(nxt, 0xe6, reg, 3);
569	*ucblk = reg[2];
570
571	return 0;
572}
573
574int
575nxt2k_fe_read_ber(struct nxt2k *nxt, uint32_t *ber)
576{
577	uint8_t reg[3];
578
579	nxt2k_readreg(nxt, 0xe6, reg, 3);
580
581	*ber = ((reg[0] << 8) + reg[1]) * 8;
582
583	return 0;
584}
585#endif
586
587static int
588nxt2k_fe_set_frontend(struct nxt2k *nxt, fe_modulation_t modulation)
589{
590	uint8_t buf[5];
591
592	if (nxt->loaded != true)
593		nxt2k4_init(nxt);
594
595	nxt2k_mc_stop(nxt);
596
597	{ /* 2k4 */
598	/* make sure demod is set to digital */
599	buf[0] = 0x04;
600	nxt2k_writedata(nxt, 0x14, buf, 1);
601	buf[0] = 0x00;
602	nxt2k_writedata(nxt, 0x17, buf, 1);
603	}
604
605	/* QAM/VSB punctured/non-punctured goes here */
606
607	/* tune in */
608	/* maybe ensure tuner managed to tune in? */
609
610	/* tuning done, reset agc */
611	nxt2k_agc_reset(nxt);
612
613	/* set target power level */
614	switch (modulation) {
615	case VSB_8:
616		buf[0] = 0x70;
617		break;
618	case QAM_256:
619	case QAM_64:
620		buf[0] = 0x74;
621		break;
622	default:
623		return EINVAL;
624		/* NOTREACHED */
625	}
626	nxt2k_writedata(nxt, 0x42, buf, 1);
627
628	/* configure sdm */
629	buf[0] = 0x07; /* 2k4 */
630	nxt2k_writedata(nxt, 0x57, buf, 1);
631
632	/* write sdm1 input */
633        buf[0] = 0x10;
634        buf[1] = 0x00;
635	nxt2k_writedata(nxt, 0x58, buf, 2); /* 2k4 */
636
637	/* write sdmx input */
638	switch (modulation) {
639	case VSB_8:
640		buf[0] = 0x60;
641		break;
642	case QAM_256:
643		buf[0] = 0x64;
644		break;
645	case QAM_64:
646		buf[0] = 0x68;
647		break;
648	default:
649		return EINVAL;
650		/* NOTREACHED */
651	}
652        buf[1] = 0x00;
653	nxt2k_writedata(nxt, 0x5c, buf, 2); /* 2k4 */
654
655	/* write adc power lpf fc */
656	buf[0] = 0x05;
657	nxt2k_writedata(nxt, 0x43, buf, 1);
658
659	{ /* 2k4 */
660	buf[0] = 0x00;
661	buf[1] = 0x00;
662	nxt2k_writedata(nxt, 0x46, buf, 2);
663	}
664
665	/* write accumulator2 input */
666	buf[0] = 0x80;
667	buf[1] = 0x00;
668	nxt2k_writedata(nxt, 0x4b, buf, 2); /* 2k4 */
669
670	/* write kg1 */
671	buf[0] = 0x00;
672	nxt2k_writedata(nxt, 0x4d, buf, 1);
673
674	/* write sdm12 lpf fc */
675	buf[0] = 0x44;
676	nxt2k_writedata(nxt, 0x55, buf, 1);
677
678	/* write agc control reg */
679	buf[0] = 0x04;
680	nxt2k_writedata(nxt, 0x41, buf, 1);
681
682	{ /* 2k4 */
683	nxt2k_readreg(nxt, 0x80, buf, 1);
684	buf[0] = 0x24;
685	nxt2k_writereg(nxt, 0x80, buf, 1);
686
687	/* soft reset? */
688	nxt2k_readreg(nxt, 0x08, buf, 1);
689	buf[0] = 0x10;
690	nxt2k_writereg(nxt, 0x08, buf, 1);
691	nxt2k_readreg(nxt, 0x08, buf, 1);
692	buf[0] = 0x00;
693	nxt2k_writereg(nxt, 0x08, buf, 1);
694
695	nxt2k_readreg(nxt, 0x80, buf, 1);
696	buf[0] = 0x04;
697	nxt2k_writereg(nxt, 0x80, buf, 1);
698
699	buf[0] = 0x00;
700	nxt2k_writereg(nxt, 0x81, buf, 1);
701
702	buf[0] = 0x80; buf[1] = 0x00; buf[2] = 0x00;
703	nxt2k_writereg(nxt, 0x82, buf, 3);
704
705	nxt2k_readreg(nxt, 0x88, buf, 1);
706	buf[0] = 0x11;
707	nxt2k_writereg(nxt, 0x88, buf, 1);
708
709	nxt2k_readreg(nxt, 0x80, buf, 1);
710	buf[0] = 0x44;
711	nxt2k_writereg(nxt, 0x80, buf, 1);
712	}
713
714	/* write agc ucgp0 */
715	switch (modulation) {
716	case VSB_8:
717		buf[0] = 0x00;
718		break;
719	case QAM_64:
720		buf[0] = 0x02;
721		break;
722	case QAM_256:
723		buf[0] = 0x03;
724		break;
725	default:
726		return EINVAL;
727		/* NOTREACHED */
728	}
729	nxt2k_writedata(nxt, 0x30, buf, 1);
730
731	/* write agc control reg */
732	buf[0] = 0x00;
733	nxt2k_writedata(nxt, 0x41, buf, 1);
734
735	/* write accumulator2 input */
736	buf[0] = 0x80;
737	buf[1] = 0x00;
738	{ /* 2k4 */
739	nxt2k_writedata(nxt, 0x49, buf, 2);
740	nxt2k_writedata(nxt, 0x4b, buf, 2);
741	}
742
743	/* write agc control reg */
744	buf[0] = 0x04;
745	nxt2k_writedata(nxt, 0x41, buf, 1);
746
747	nxt2k_mc_start(nxt);
748
749	{ /* 2k4 */
750	nxt2k4_mc_init(nxt);
751	buf[0] = 0xf0;
752	buf[1] = 0x00;
753	nxt2k_writedata(nxt, 0x5c, buf, 2);
754	}
755
756	/* "adjacent channel detection" code would go here */
757
758	return 0;
759}
760
761static int
762nxt2k_init(struct nxt2k *nxt)
763{
764	int ret = 0;
765
766	printf("%s\n", __func__);
767
768	if (nxt->loaded != 1)
769		ret = nxt2k4_init(nxt);
770
771	return ret;
772}
773
774
775struct nxt2k *
776nxt2k_open(device_t parent, i2c_tag_t tag, i2c_addr_t addr, unsigned int if_freq)
777{
778	struct nxt2k *nxt;
779	int e;
780	uint8_t b[5];
781
782	nxt = kmem_alloc(sizeof(*nxt), KM_SLEEP);
783	nxt->parent = parent;
784	nxt->tag = tag;
785	nxt->addr = addr;
786
787	/* read chip ids */
788	e = nxt2k_readdata(nxt, 0x00, b, 5);
789
790	if (e) {
791		printf("%s read failed %d\n", __func__, e);
792		kmem_free(nxt, sizeof(*nxt));
793		return NULL;
794	}
795
796	if (b[0] != 0x05) {
797		printf("%s unsupported %02x %02x %02x %02x %02x\n",
798		    __func__, b[0], b[1], b[2], b[3], b[4]);
799		kmem_free(nxt, sizeof(*nxt));
800		return NULL;
801	}
802
803	mutex_init(&nxt->mtx, MUTEX_DEFAULT, IPL_NONE);
804	cv_init(&nxt->cv, "nxtpl");
805
806	nxt->loaded = false;
807
808	return nxt;
809}
810
811void
812nxt2k_close(struct nxt2k *nxt)
813{
814	kmem_free(nxt, sizeof(*nxt));
815}
816
817void
818nxt2k_enable(struct nxt2k *nxt, bool enable)
819{
820	if (enable == true)
821		nxt2k_init(nxt);
822}
823
824int
825nxt2k_set_modulation(struct nxt2k *nxt, fe_modulation_t modulation)
826{
827	return nxt2k_fe_set_frontend(nxt, modulation);
828}
829
830MODULE(MODULE_CLASS_DRIVER, nxt2k, "i2cexec");
831
832static int
833nxt2k_modcmd(modcmd_t cmd, void *opaque)
834{
835	if (cmd == MODULE_CMD_INIT || cmd == MODULE_CMD_FINI)
836		return 0;
837	return ENOTTY;
838}
839