1 | /* $NetBSD: lg3303.c,v 1.9 2015/03/07 14:16:51 jmcneill 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.9 2015/03/07 14:16:51 jmcneill 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 * |
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 | if (lg == NULL) |
91 | return NULL; |
92 | lg->parent = parent; |
93 | lg->i2c = i2c; |
94 | lg->i2c_addr = addr; |
95 | lg->current_modulation = -1; |
96 | lg->flags = flags; |
97 | |
98 | if (lg3303_init(lg) != 0) { |
99 | kmem_free(lg, sizeof(*lg)); |
100 | return NULL; |
101 | } |
102 | |
103 | device_printf(lg->parent, "lg3303: found @ 0x%02x\n" , addr); |
104 | |
105 | return lg; |
106 | } |
107 | |
108 | void |
109 | lg3303_close(struct lg3303 *lg) |
110 | { |
111 | kmem_free(lg, sizeof(*lg)); |
112 | } |
113 | |
114 | static int |
115 | lg3303_write(struct lg3303 *lg, uint8_t *buf, size_t len) |
116 | { |
117 | unsigned int i; |
118 | uint8_t *p = buf; |
119 | int error; |
120 | |
121 | for (i = 0; i < len - 1; i += 2) { |
122 | error = iic_exec(lg->i2c, I2C_OP_WRITE_WITH_STOP, lg->i2c_addr, |
123 | p, 2, NULL, 0, 0); |
124 | if (error) |
125 | return error; |
126 | p += 2; |
127 | } |
128 | |
129 | return 0; |
130 | } |
131 | |
132 | static int |
133 | lg3303_read(struct lg3303 *lg, uint8_t reg, uint8_t *buf, size_t len) |
134 | { |
135 | int error; |
136 | |
137 | error = iic_exec(lg->i2c, I2C_OP_WRITE, lg->i2c_addr, |
138 | ®, sizeof(reg), NULL, 0, 0); |
139 | if (error) |
140 | return error; |
141 | return iic_exec(lg->i2c, I2C_OP_READ, lg->i2c_addr, |
142 | NULL, 0, buf, len, 0); |
143 | } |
144 | |
145 | static int |
146 | lg3303_reset(struct lg3303 *lg) |
147 | { |
148 | uint8_t buffer[] = {REG_IRQ_STATUS, 0x00}; |
149 | int error = lg3303_write(lg, buffer, 2); |
150 | if (error == 0) { |
151 | buffer[1] = 0x01; |
152 | error = lg3303_write(lg, buffer, 2); |
153 | } |
154 | return error; |
155 | } |
156 | |
157 | static int |
158 | lg3303_init(struct lg3303 *lg) |
159 | { |
160 | //static uint8_t init_data[] = {0x4c, 0x14, 0x87, 0xf3}; |
161 | static uint8_t init_data[] = {0x4c, 0x14}; |
162 | size_t len; |
163 | int error; |
164 | |
165 | #if notyet |
166 | if (clock_polarity == DVB_IFC_POS_POL) |
167 | len = 4; |
168 | else |
169 | #endif |
170 | len = 2; |
171 | |
172 | error = lg3303_write(lg, init_data, len); |
173 | if (error == 0) |
174 | lg3303_reset(lg); |
175 | |
176 | return error; |
177 | } |
178 | |
179 | int |
180 | lg3303_set_modulation(struct lg3303 *lg, fe_modulation_t modulation) |
181 | { |
182 | int error; |
183 | static uint8_t vsb_data[] = { |
184 | 0x04, 0x00, |
185 | 0x0d, 0x40, |
186 | 0x0e, 0x87, |
187 | 0x0f, 0x8e, |
188 | 0x10, 0x01, |
189 | 0x47, 0x8b |
190 | }; |
191 | static uint8_t qam_data[] = { |
192 | 0x04, 0x00, |
193 | 0x0d, 0x00, |
194 | 0x0e, 0x00, |
195 | 0x0f, 0x00, |
196 | 0x10, 0x00, |
197 | 0x51, 0x63, |
198 | 0x47, 0x66, |
199 | 0x48, 0x66, |
200 | 0x4d, 0x1a, |
201 | 0x49, 0x08, |
202 | 0x4a, 0x9b |
203 | }; |
204 | uint8_t top_ctrl[] = {REG_TOP_CONTROL, 0x00}; |
205 | |
206 | error = lg3303_reset(lg); |
207 | if (error) |
208 | return error; |
209 | |
210 | if (lg->flags & LG3303_CFG_SERIAL_INPUT) |
211 | top_ctrl[1] = 0x40; |
212 | |
213 | switch (modulation) { |
214 | case VSB_8: |
215 | top_ctrl[1] |= 0x03; |
216 | error = lg3303_write(lg, vsb_data, sizeof(vsb_data)); |
217 | if (error) |
218 | return error; |
219 | break; |
220 | case QAM_256: |
221 | top_ctrl[1] |= 0x01; |
222 | /* FALLTHROUGH */ |
223 | case QAM_64: |
224 | error = lg3303_write(lg, qam_data, sizeof(qam_data)); |
225 | if (error) |
226 | return error; |
227 | break; |
228 | default: |
229 | device_printf(lg->parent, |
230 | "lg3303: unsupported modulation type (%d)\n" , |
231 | modulation); |
232 | return EINVAL; |
233 | } |
234 | error = lg3303_write(lg, top_ctrl, sizeof(top_ctrl)); |
235 | if (error) |
236 | return error; |
237 | lg->current_modulation = modulation; |
238 | lg3303_reset(lg); |
239 | |
240 | return error; |
241 | } |
242 | |
243 | fe_status_t |
244 | lg3303_get_dtv_status(struct lg3303 *lg) |
245 | { |
246 | uint8_t reg = 0, value = 0x00; |
247 | fe_status_t festatus = 0; |
248 | int error = 0; |
249 | |
250 | error = lg3303_read(lg, 0x58, &value, sizeof(value)); |
251 | if (error) |
252 | return 0; |
253 | |
254 | if (value & 0x01) |
255 | festatus |= FE_HAS_SIGNAL; |
256 | |
257 | error = lg3303_read(lg, REG_CARRIER_LOCK, &value, sizeof(value)); |
258 | if (error) |
259 | return 0; |
260 | |
261 | switch (lg->current_modulation) { |
262 | case VSB_8: |
263 | if (value & 0x80) |
264 | festatus |= FE_HAS_CARRIER; |
265 | reg = 0x38; |
266 | break; |
267 | case QAM_64: |
268 | case QAM_256: |
269 | if ((value & 0x07) == 0x07) |
270 | festatus |= FE_HAS_CARRIER; |
271 | reg = 0x8a; |
272 | break; |
273 | default: |
274 | device_printf(lg->parent, |
275 | "lg3303: unsupported modulation type (%d)\n" , |
276 | lg->current_modulation); |
277 | return 0; |
278 | } |
279 | |
280 | if ((festatus & FE_HAS_CARRIER) == 0) |
281 | return festatus; |
282 | |
283 | error = lg3303_read(lg, reg, &value, sizeof(value)); |
284 | if (!error && (value & 0x01)) |
285 | festatus |= FE_HAS_LOCK; |
286 | |
287 | if (festatus & FE_HAS_LOCK) |
288 | festatus |= (FE_HAS_SYNC | FE_HAS_VITERBI); |
289 | |
290 | return festatus; |
291 | } |
292 | |
293 | uint16_t |
294 | lg3303_get_snr(struct lg3303 *lg) |
295 | { |
296 | int64_t noise, snr_const; |
297 | uint8_t buffer[5]; |
298 | int64_t snr; |
299 | int error; |
300 | |
301 | switch (lg->current_modulation) { |
302 | case VSB_8: |
303 | error = lg3303_read(lg, REG_EQPH_ERR0, buffer, sizeof(buffer)); |
304 | if (error) |
305 | return 0; |
306 | noise = ((buffer[0] & 7) << 16) | (buffer[3] << 8) | buffer[4]; |
307 | snr_const = 73957994; /* log10(2560) * pow(2,24) */ |
308 | break; |
309 | case QAM_64: |
310 | case QAM_256: |
311 | error = lg3303_read(lg, REG_CARRIER_MSEQAM1, buffer, 2); |
312 | if (error) |
313 | return 0; |
314 | noise = (buffer[0] << 8) | buffer[1]; |
315 | if (lg->current_modulation == QAM_64) |
316 | snr_const = 97939837; /* log10(688128) * pow(2,24) */ |
317 | else |
318 | snr_const = 98026066; /* log10(696320) * pow(2,24) */ |
319 | break; |
320 | default: |
321 | device_printf(lg->parent, |
322 | "lg3303: unsupported modulation type (%d)\n" , |
323 | lg->current_modulation); |
324 | return 0; |
325 | } |
326 | |
327 | if (noise == 0) |
328 | return 0; |
329 | snr = dtv_intlog10(noise); |
330 | if (snr > snr_const) |
331 | return 0; |
332 | return (10 * (snr_const - snr)) >> 16; |
333 | } |
334 | |
335 | uint16_t |
336 | lg3303_get_signal_strength(struct lg3303 *lg) |
337 | { |
338 | return ((uint32_t)lg3303_get_snr(lg) << 16) / 8960; |
339 | } |
340 | |
341 | uint32_t |
342 | lg3303_get_ucblocks(struct lg3303 *lg) |
343 | { |
344 | uint8_t buffer[2]; |
345 | int error; |
346 | |
347 | error = lg3303_read(lg, REG_PACKET_ERR_COUNTER1, buffer, sizeof(buffer)); |
348 | if (error) |
349 | return 0; |
350 | |
351 | return (buffer[0] << 8) | buffer[1]; |
352 | } |
353 | |
354 | MODULE(MODULE_CLASS_DRIVER, lg3303, "i2cexec,dtv_math" ); |
355 | |
356 | static int |
357 | lg3303_modcmd(modcmd_t cmd, void *opaque) |
358 | { |
359 | if (cmd == MODULE_CMD_INIT || cmd == MODULE_CMD_FINI) |
360 | return 0; |
361 | return ENOTTY; |
362 | } |
363 | |