xref: /netbsd/sys/dev/i2c/lg3303.c (revision d65a6a67)
1 /* $NetBSD: lg3303.c,v 1.10 2017/06/01 02:45:10 chs Exp $ */
2 
3 /*-
4  * Copyright 2007 Jason Harmening
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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 #include <sys/param.h>
31 __KERNEL_RCSID(0, "$NetBSD: lg3303.c,v 1.10 2017/06/01 02:45:10 chs Exp $");
32 
33 #include <sys/types.h>
34 #include <sys/kmem.h>
35 #include <sys/module.h>
36 #include <sys/bitops.h>
37 
38 #include <dev/i2c/i2cvar.h>
39 #include <dev/i2c/lg3303var.h>
40 #include <dev/dtv/dtvif.h>
41 #include <dev/dtv/dtv_math.h>
42 
43 #define REG_TOP_CONTROL         0x00
44 #define REG_IRQ_MASK            0x01
45 #define REG_IRQ_STATUS          0x02
46 #define REG_VSB_CARRIER_FREQ0   0x16
47 #define REG_VSB_CARRIER_FREQ1   0x17
48 #define REG_VSB_CARRIER_FREQ2   0x18
49 #define REG_VSB_CARRIER_FREQ3   0x19
50 #define REG_CARRIER_MSEQAM1     0x1a
51 #define REG_CARRIER_MSEQAM2     0x1b
52 #define REG_CARRIER_LOCK        0x1c
53 #define REG_TIMING_RECOVERY     0x1d
54 #define REG_AGC_DELAY0          0x2a
55 #define REG_AGC_DELAY1          0x2b
56 #define REG_AGC_DELAY2          0x2c
57 #define REG_AGC_RF_BANDWIDTH0   0x2d
58 #define REG_AGC_RF_BANDWIDTH1   0x2e
59 #define REG_AGC_RF_BANDWIDTH2   0x2f
60 #define REG_AGC_LOOP_BANDWIDTH0 0x30
61 #define REG_AGC_LOOP_BANDWIDTH1 0x31
62 #define REG_AGC_FUNC_CTRL1      0x32
63 #define REG_AGC_FUNC_CTRL2      0x33
64 #define REG_AGC_FUNC_CTRL3      0x34
65 #define REG_AGC_RFIF_ACC0       0x39
66 #define REG_AGC_RFIF_ACC1       0x3a
67 #define REG_AGC_RFIF_ACC2       0x3b
68 #define REG_AGC_STATUS          0x3f
69 #define REG_SYNC_STATUS_VSB     0x43
70 #define REG_DEMUX_CONTROL       0x66
71 #define REG_EQPH_ERR0           0x6e
72 #define REG_EQ_ERR1             0x6f
73 #define REG_EQ_ERR2             0x70
74 #define REG_PH_ERR1             0x71
75 #define REG_PH_ERR2             0x72
76 #define REG_PACKET_ERR_COUNTER1 0x8b
77 #define REG_PACKET_ERR_COUNTER2 0x8c
78 
79 #define LG3303_DEFAULT_DELAY 250000
80 
81 static int	lg3303_reset(struct lg3303 *);
82 static int	lg3303_init(struct lg3303 *);
83 
84 struct lg3303 *
lg3303_open(device_t parent,i2c_tag_t i2c,i2c_addr_t addr,int flags)85 lg3303_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr, int flags)
86 {
87 	struct lg3303 *lg;
88 
89 	lg = kmem_alloc(sizeof(*lg), KM_SLEEP);
90 	lg->parent = parent;
91 	lg->i2c = i2c;
92 	lg->i2c_addr = addr;
93 	lg->current_modulation = -1;
94 	lg->flags = flags;
95 
96 	if (lg3303_init(lg) != 0) {
97 		kmem_free(lg, sizeof(*lg));
98 		return NULL;
99 	}
100 
101 	device_printf(lg->parent, "lg3303: found @ 0x%02x\n", addr);
102 
103 	return lg;
104 }
105 
106 void
lg3303_close(struct lg3303 * lg)107 lg3303_close(struct lg3303 *lg)
108 {
109 	kmem_free(lg, sizeof(*lg));
110 }
111 
112 static int
lg3303_write(struct lg3303 * lg,uint8_t * buf,size_t len)113 lg3303_write(struct lg3303 *lg, uint8_t *buf, size_t len)
114 {
115 	unsigned int i;
116 	uint8_t *p = buf;
117 	int error;
118 
119 	for (i = 0; i < len - 1; i += 2) {
120 		error = iic_exec(lg->i2c, I2C_OP_WRITE_WITH_STOP, lg->i2c_addr,
121 		    p, 2, NULL, 0, 0);
122 		if (error)
123 			return error;
124 		p += 2;
125 	}
126 
127 	return 0;
128 }
129 
130 static int
lg3303_read(struct lg3303 * lg,uint8_t reg,uint8_t * buf,size_t len)131 lg3303_read(struct lg3303 *lg, uint8_t reg, uint8_t *buf, size_t len)
132 {
133 	int error;
134 
135 	error = iic_exec(lg->i2c, I2C_OP_WRITE, lg->i2c_addr,
136 	    &reg, sizeof(reg), NULL, 0, 0);
137 	if (error)
138 		return error;
139 	return iic_exec(lg->i2c, I2C_OP_READ, lg->i2c_addr,
140 	    NULL, 0, buf, len, 0);
141 }
142 
143 static int
lg3303_reset(struct lg3303 * lg)144 lg3303_reset(struct lg3303 *lg)
145 {
146 	uint8_t buffer[] = {REG_IRQ_STATUS, 0x00};
147 	int error = lg3303_write(lg, buffer, 2);
148 	if (error == 0) {
149 		buffer[1] = 0x01;
150 		error = lg3303_write(lg, buffer, 2);
151 	}
152 	return error;
153 }
154 
155 static int
lg3303_init(struct lg3303 * lg)156 lg3303_init(struct lg3303 *lg)
157 {
158 	//static uint8_t init_data[] = {0x4c, 0x14, 0x87, 0xf3};
159 	static uint8_t init_data[] = {0x4c, 0x14};
160 	size_t len;
161 	int error;
162 
163 #if notyet
164 	if (clock_polarity == DVB_IFC_POS_POL)
165 		len = 4;
166 	else
167 #endif
168 	len = 2;
169 
170 	error = lg3303_write(lg, init_data, len);
171 	if (error == 0)
172       		lg3303_reset(lg);
173 
174 	return error;
175 }
176 
177 int
lg3303_set_modulation(struct lg3303 * lg,fe_modulation_t modulation)178 lg3303_set_modulation(struct lg3303 *lg, fe_modulation_t modulation)
179 {
180 	int error;
181 	static uint8_t vsb_data[] = {
182 		0x04, 0x00,
183 		0x0d, 0x40,
184 		0x0e, 0x87,
185 		0x0f, 0x8e,
186 		0x10, 0x01,
187 		0x47, 0x8b
188 	};
189 	static uint8_t qam_data[] = {
190 		0x04, 0x00,
191 		0x0d, 0x00,
192 		0x0e, 0x00,
193 		0x0f, 0x00,
194 		0x10, 0x00,
195 		0x51, 0x63,
196 		0x47, 0x66,
197 		0x48, 0x66,
198 		0x4d, 0x1a,
199 		0x49, 0x08,
200 		0x4a, 0x9b
201 	};
202 	uint8_t top_ctrl[] = {REG_TOP_CONTROL, 0x00};
203 
204 	error = lg3303_reset(lg);
205 	if (error)
206 		return error;
207 
208 	if (lg->flags & LG3303_CFG_SERIAL_INPUT)
209 		top_ctrl[1] = 0x40;
210 
211 	switch (modulation) {
212 	case VSB_8:
213 		top_ctrl[1] |= 0x03;
214 		error = lg3303_write(lg, vsb_data, sizeof(vsb_data));
215 		if (error)
216 			return error;
217 		break;
218 	case QAM_256:
219 		top_ctrl[1] |= 0x01;
220 		/* FALLTHROUGH */
221 	case QAM_64:
222 		error = lg3303_write(lg, qam_data, sizeof(qam_data));
223 		if (error)
224 			return error;
225 		break;
226 	default:
227 		device_printf(lg->parent,
228 		    "lg3303: unsupported modulation type (%d)\n",
229 		    modulation);
230 		return EINVAL;
231 	}
232 	error = lg3303_write(lg, top_ctrl, sizeof(top_ctrl));
233 	if (error)
234 		return error;
235 	lg->current_modulation = modulation;
236 	lg3303_reset(lg);
237 
238 	return error;
239 }
240 
241 fe_status_t
lg3303_get_dtv_status(struct lg3303 * lg)242 lg3303_get_dtv_status(struct lg3303 *lg)
243 {
244 	uint8_t reg = 0, value = 0x00;
245 	fe_status_t festatus = 0;
246 	int error = 0;
247 
248 	error = lg3303_read(lg, 0x58, &value, sizeof(value));
249 	if (error)
250 		return 0;
251 
252 	if (value & 0x01)
253 		festatus |= FE_HAS_SIGNAL;
254 
255 	error = lg3303_read(lg, REG_CARRIER_LOCK, &value, sizeof(value));
256 	if (error)
257 		return 0;
258 
259 	switch (lg->current_modulation) {
260 	case VSB_8:
261 		if (value & 0x80)
262 			festatus |= FE_HAS_CARRIER;
263 		reg = 0x38;
264 		break;
265 	case QAM_64:
266 	case QAM_256:
267 		if ((value & 0x07) == 0x07)
268 			festatus |= FE_HAS_CARRIER;
269 		reg = 0x8a;
270 		break;
271 	default:
272 		device_printf(lg->parent,
273 		    "lg3303: unsupported modulation type (%d)\n",
274 		    lg->current_modulation);
275 		return 0;
276 	}
277 
278 	if ((festatus & FE_HAS_CARRIER) == 0)
279 		return festatus;
280 
281 	error = lg3303_read(lg, reg, &value, sizeof(value));
282 	if (!error && (value & 0x01))
283 		festatus |= FE_HAS_LOCK;
284 
285 	if (festatus & FE_HAS_LOCK)
286 		festatus |= (FE_HAS_SYNC | FE_HAS_VITERBI);
287 
288 	return festatus;
289 }
290 
291 uint16_t
lg3303_get_snr(struct lg3303 * lg)292 lg3303_get_snr(struct lg3303 *lg)
293 {
294 	int64_t noise, snr_const;
295 	uint8_t buffer[5];
296 	int64_t snr;
297 	int error;
298 
299 	switch (lg->current_modulation) {
300 	case VSB_8:
301 		error = lg3303_read(lg, REG_EQPH_ERR0, buffer, sizeof(buffer));
302 		if (error)
303 			return 0;
304 		noise = ((buffer[0] & 7) << 16) | (buffer[3] << 8) | buffer[4];
305 		snr_const = 73957994;	/* log10(2560) * pow(2,24) */
306 		break;
307 	case QAM_64:
308 	case QAM_256:
309 		error = lg3303_read(lg, REG_CARRIER_MSEQAM1, buffer, 2);
310 		if (error)
311 			return 0;
312 		noise = (buffer[0] << 8) | buffer[1];
313 		if (lg->current_modulation == QAM_64)
314 			snr_const = 97939837;	/* log10(688128) * pow(2,24) */
315 		else
316 			snr_const = 98026066;	/* log10(696320) * pow(2,24) */
317 		break;
318 	default:
319 		device_printf(lg->parent,
320 		    "lg3303: unsupported modulation type (%d)\n",
321 		    lg->current_modulation);
322 		return 0;
323 	}
324 
325 	if (noise == 0)
326 		return 0;
327 	snr = dtv_intlog10(noise);
328 	if (snr > snr_const)
329 		return 0;
330 	return (10 * (snr_const - snr)) >> 16;
331 }
332 
333 uint16_t
lg3303_get_signal_strength(struct lg3303 * lg)334 lg3303_get_signal_strength(struct lg3303 *lg)
335 {
336 	return ((uint32_t)lg3303_get_snr(lg) << 16) / 8960;
337 }
338 
339 uint32_t
lg3303_get_ucblocks(struct lg3303 * lg)340 lg3303_get_ucblocks(struct lg3303 *lg)
341 {
342 	uint8_t buffer[2];
343 	int error;
344 
345 	error = lg3303_read(lg, REG_PACKET_ERR_COUNTER1, buffer, sizeof(buffer));
346 	if (error)
347 		return 0;
348 
349 	return (buffer[0] << 8) | buffer[1];
350 }
351 
352 MODULE(MODULE_CLASS_DRIVER, lg3303, "i2cexec,dtv_math");
353 
354 static int
lg3303_modcmd(modcmd_t cmd,void * opaque)355 lg3303_modcmd(modcmd_t cmd, void *opaque)
356 {
357 	if (cmd == MODULE_CMD_INIT || cmd == MODULE_CMD_FINI)
358 		return 0;
359 	return ENOTTY;
360 }
361