1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Driver for the MaxLinear MxL69x family of combo tuners/demods |
4 | * |
5 | * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> |
6 | * |
7 | * based on code: |
8 | * Copyright (c) 2016 MaxLinear, Inc. All rights reserved |
9 | * which was released under GPL V2 |
10 | */ |
11 | |
12 | #include <linux/mutex.h> |
13 | #include <linux/i2c-mux.h> |
14 | #include <linux/string.h> |
15 | #include <linux/firmware.h> |
16 | |
17 | #include "mxl692.h" |
18 | #include "mxl692_defs.h" |
19 | |
20 | static const struct dvb_frontend_ops mxl692_ops; |
21 | |
22 | struct mxl692_dev { |
23 | struct dvb_frontend fe; |
24 | struct i2c_client *i2c_client; |
25 | struct mutex i2c_lock; /* i2c command mutex */ |
26 | enum MXL_EAGLE_DEMOD_TYPE_E demod_type; |
27 | enum MXL_EAGLE_POWER_MODE_E power_mode; |
28 | u32 current_frequency; |
29 | int device_type; |
30 | int seqnum; |
31 | int init_done; |
32 | }; |
33 | |
34 | static int mxl692_i2c_write(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) |
35 | { |
36 | int ret = 0; |
37 | struct i2c_msg msg = { |
38 | .addr = dev->i2c_client->addr, |
39 | .flags = 0, |
40 | .buf = buffer, |
41 | .len = buf_len |
42 | }; |
43 | |
44 | ret = i2c_transfer(adap: dev->i2c_client->adapter, msgs: &msg, num: 1); |
45 | if (ret != 1) |
46 | dev_dbg(&dev->i2c_client->dev, "i2c write error!\n" ); |
47 | |
48 | return ret; |
49 | } |
50 | |
51 | static int mxl692_i2c_read(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) |
52 | { |
53 | int ret = 0; |
54 | struct i2c_msg msg = { |
55 | .addr = dev->i2c_client->addr, |
56 | .flags = I2C_M_RD, |
57 | .buf = buffer, |
58 | .len = buf_len |
59 | }; |
60 | |
61 | ret = i2c_transfer(adap: dev->i2c_client->adapter, msgs: &msg, num: 1); |
62 | if (ret != 1) |
63 | dev_dbg(&dev->i2c_client->dev, "i2c read error!\n" ); |
64 | |
65 | return ret; |
66 | } |
67 | |
68 | static int convert_endian(u32 size, u8 *d) |
69 | { |
70 | u32 i; |
71 | |
72 | for (i = 0; i < (size & ~3); i += 4) { |
73 | d[i + 0] ^= d[i + 3]; |
74 | d[i + 3] ^= d[i + 0]; |
75 | d[i + 0] ^= d[i + 3]; |
76 | |
77 | d[i + 1] ^= d[i + 2]; |
78 | d[i + 2] ^= d[i + 1]; |
79 | d[i + 1] ^= d[i + 2]; |
80 | } |
81 | |
82 | switch (size & 3) { |
83 | case 0: |
84 | case 1: |
85 | /* do nothing */ |
86 | break; |
87 | case 2: |
88 | d[i + 0] ^= d[i + 1]; |
89 | d[i + 1] ^= d[i + 0]; |
90 | d[i + 0] ^= d[i + 1]; |
91 | break; |
92 | |
93 | case 3: |
94 | d[i + 0] ^= d[i + 2]; |
95 | d[i + 2] ^= d[i + 0]; |
96 | d[i + 0] ^= d[i + 2]; |
97 | break; |
98 | } |
99 | return size; |
100 | } |
101 | |
102 | static int convert_endian_n(int n, u32 size, u8 *d) |
103 | { |
104 | int i, count = 0; |
105 | |
106 | for (i = 0; i < n; i += size) |
107 | count += convert_endian(size, d: d + i); |
108 | return count; |
109 | } |
110 | |
111 | static void mxl692_tx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) |
112 | { |
113 | #ifdef __BIG_ENDIAN |
114 | return; |
115 | #endif |
116 | buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ |
117 | |
118 | switch (opcode) { |
119 | case MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET: |
120 | case MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET: |
121 | case MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET: |
122 | buffer += convert_endian(size: sizeof(u32), d: buffer); |
123 | break; |
124 | case MXL_EAGLE_OPCODE_QAM_PARAMS_SET: |
125 | buffer += 5; |
126 | buffer += convert_endian(size: 2 * sizeof(u32), d: buffer); |
127 | break; |
128 | default: |
129 | /* no swapping - all get opcodes */ |
130 | /* ATSC/OOB no swapping */ |
131 | break; |
132 | } |
133 | } |
134 | |
135 | static void mxl692_rx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) |
136 | { |
137 | #ifdef __BIG_ENDIAN |
138 | return; |
139 | #endif |
140 | buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ |
141 | |
142 | switch (opcode) { |
143 | case MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET: |
144 | buffer++; |
145 | buffer += convert_endian(size: 2 * sizeof(u16), d: buffer); |
146 | break; |
147 | case MXL_EAGLE_OPCODE_ATSC_STATUS_GET: |
148 | buffer += convert_endian_n(n: 2, size: sizeof(u16), d: buffer); |
149 | buffer += convert_endian(size: sizeof(u32), d: buffer); |
150 | break; |
151 | case MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET: |
152 | buffer += convert_endian(size: 3 * sizeof(u32), d: buffer); |
153 | break; |
154 | case MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET: |
155 | buffer += convert_endian_n(n: 24, size: sizeof(u16), d: buffer); |
156 | break; |
157 | case MXL_EAGLE_OPCODE_QAM_STATUS_GET: |
158 | buffer += 8; |
159 | buffer += convert_endian_n(n: 2, size: sizeof(u16), d: buffer); |
160 | buffer += convert_endian(size: sizeof(u32), d: buffer); |
161 | break; |
162 | case MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET: |
163 | buffer += convert_endian(size: 7 * sizeof(u32), d: buffer); |
164 | break; |
165 | case MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET: |
166 | case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET: |
167 | case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET: |
168 | case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET: |
169 | case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET: |
170 | buffer += convert_endian_n(n: 24, size: sizeof(u16), d: buffer); |
171 | break; |
172 | case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET: |
173 | buffer += convert_endian_n(n: 8, size: sizeof(u16), d: buffer); |
174 | break; |
175 | case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET: |
176 | buffer += convert_endian_n(n: 17, size: sizeof(u16), d: buffer); |
177 | break; |
178 | case MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET: |
179 | buffer += convert_endian(size: 3 * sizeof(u32), d: buffer); |
180 | break; |
181 | case MXL_EAGLE_OPCODE_OOB_STATUS_GET: |
182 | buffer += convert_endian_n(n: 2, size: sizeof(u16), d: buffer); |
183 | buffer += convert_endian(size: sizeof(u32), d: buffer); |
184 | break; |
185 | case MXL_EAGLE_OPCODE_SMA_RECEIVE_GET: |
186 | buffer += convert_endian(size: sizeof(u32), d: buffer); |
187 | break; |
188 | default: |
189 | /* no swapping - all set opcodes */ |
190 | break; |
191 | } |
192 | } |
193 | |
194 | static u32 mxl692_checksum(u8 *buffer, u32 size) |
195 | { |
196 | u32 ix, div_size; |
197 | u32 cur_cksum = 0; |
198 | __be32 *buf; |
199 | |
200 | div_size = DIV_ROUND_UP(size, 4); |
201 | |
202 | buf = (__be32 *)buffer; |
203 | for (ix = 0; ix < div_size; ix++) |
204 | cur_cksum += be32_to_cpu(buf[ix]); |
205 | |
206 | cur_cksum ^= 0xDEADBEEF; |
207 | |
208 | return cur_cksum; |
209 | } |
210 | |
211 | static int (struct mxl692_dev *dev, |
212 | const u8 *buffer, u32 buf_len) |
213 | { |
214 | int status = 0; |
215 | u32 ix, temp; |
216 | __be32 *local_buf = NULL; |
217 | u8 temp_cksum = 0; |
218 | static const u8 fw_hdr[] = { |
219 | 0x4D, 0x31, 0x10, 0x02, 0x40, 0x00, 0x00, 0x80 |
220 | }; |
221 | |
222 | if (memcmp(p: buffer, q: fw_hdr, size: 8) != 0) { |
223 | status = -EINVAL; |
224 | goto err_finish; |
225 | } |
226 | |
227 | local_buf = (__be32 *)(buffer + 8); |
228 | temp = be32_to_cpu(*local_buf); |
229 | |
230 | if ((buf_len - 16) != temp >> 8) { |
231 | status = -EINVAL; |
232 | goto err_finish; |
233 | } |
234 | |
235 | for (ix = 16; ix < buf_len; ix++) |
236 | temp_cksum += buffer[ix]; |
237 | |
238 | if (temp_cksum != buffer[11]) |
239 | status = -EINVAL; |
240 | |
241 | err_finish: |
242 | if (status) |
243 | dev_dbg(&dev->i2c_client->dev, "failed\n" ); |
244 | return status; |
245 | } |
246 | |
247 | static int mxl692_write_fw_block(struct mxl692_dev *dev, const u8 *buffer, |
248 | u32 buf_len, u32 *index) |
249 | { |
250 | int status = 0; |
251 | u32 ix = 0, total_len = 0, addr = 0, chunk_len = 0, prevchunk_len = 0; |
252 | u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; |
253 | int payload_max = MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE; |
254 | |
255 | ix = *index; |
256 | |
257 | if (buffer[ix] == 0x53) { |
258 | total_len = buffer[ix + 1] << 16 | buffer[ix + 2] << 8 | buffer[ix + 3]; |
259 | total_len = (total_len + 3) & ~3; |
260 | addr = buffer[ix + 4] << 24 | buffer[ix + 5] << 16 | |
261 | buffer[ix + 6] << 8 | buffer[ix + 7]; |
262 | ix += MXL_EAGLE_FW_SEGMENT_HEADER_SIZE; |
263 | |
264 | while ((total_len > 0) && (status == 0)) { |
265 | plocal_buf = local_buf; |
266 | chunk_len = (total_len < payload_max) ? total_len : payload_max; |
267 | |
268 | *plocal_buf++ = 0xFC; |
269 | *plocal_buf++ = chunk_len + sizeof(u32); |
270 | |
271 | *(u32 *)plocal_buf = addr + prevchunk_len; |
272 | #ifdef __BIG_ENDIAN |
273 | convert_endian(sizeof(u32), plocal_buf); |
274 | #endif |
275 | plocal_buf += sizeof(u32); |
276 | |
277 | memcpy(plocal_buf, &buffer[ix], chunk_len); |
278 | convert_endian(size: chunk_len, d: plocal_buf); |
279 | if (mxl692_i2c_write(dev, buffer: local_buf, |
280 | buf_len: (chunk_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { |
281 | status = -EREMOTEIO; |
282 | break; |
283 | } |
284 | |
285 | prevchunk_len += chunk_len; |
286 | total_len -= chunk_len; |
287 | ix += chunk_len; |
288 | } |
289 | *index = ix; |
290 | } else { |
291 | status = -EINVAL; |
292 | } |
293 | |
294 | if (status) |
295 | dev_dbg(&dev->i2c_client->dev, "err %d\n" , status); |
296 | |
297 | return status; |
298 | } |
299 | |
300 | static int mxl692_memwrite(struct mxl692_dev *dev, u32 addr, |
301 | u8 *buffer, u32 size) |
302 | { |
303 | int status = 0, total_len = 0; |
304 | u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; |
305 | |
306 | total_len = size; |
307 | total_len = (total_len + 3) & ~3; /* 4 byte alignment */ |
308 | |
309 | if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE)) |
310 | dev_dbg(&dev->i2c_client->dev, "hrmph?\n" ); |
311 | |
312 | plocal_buf = local_buf; |
313 | |
314 | *plocal_buf++ = 0xFC; |
315 | *plocal_buf++ = total_len + sizeof(u32); |
316 | |
317 | *(u32 *)plocal_buf = addr; |
318 | plocal_buf += sizeof(u32); |
319 | |
320 | memcpy(plocal_buf, buffer, total_len); |
321 | #ifdef __BIG_ENDIAN |
322 | convert_endian(sizeof(u32) + total_len, local_buf + 2); |
323 | #endif |
324 | if (mxl692_i2c_write(dev, buffer: local_buf, |
325 | buf_len: (total_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { |
326 | status = -EREMOTEIO; |
327 | goto err_finish; |
328 | } |
329 | |
330 | return status; |
331 | err_finish: |
332 | dev_dbg(&dev->i2c_client->dev, "err %d\n" , status); |
333 | return status; |
334 | } |
335 | |
336 | static int mxl692_memread(struct mxl692_dev *dev, u32 addr, |
337 | u8 *buffer, u32 size) |
338 | { |
339 | int status = 0; |
340 | u8 local_buf[MXL_EAGLE_I2C_MHEADER_SIZE] = {}, *plocal_buf = NULL; |
341 | |
342 | plocal_buf = local_buf; |
343 | |
344 | *plocal_buf++ = 0xFB; |
345 | *plocal_buf++ = sizeof(u32); |
346 | *(u32 *)plocal_buf = addr; |
347 | #ifdef __BIG_ENDIAN |
348 | convert_endian(sizeof(u32), plocal_buf); |
349 | #endif |
350 | mutex_lock(&dev->i2c_lock); |
351 | |
352 | if (mxl692_i2c_write(dev, buffer: local_buf, MXL_EAGLE_I2C_MHEADER_SIZE) > 0) { |
353 | size = (size + 3) & ~3; /* 4 byte alignment */ |
354 | status = mxl692_i2c_read(dev, buffer, buf_len: (u16)size) < 0 ? -EREMOTEIO : 0; |
355 | #ifdef __BIG_ENDIAN |
356 | if (status == 0) |
357 | convert_endian(size, buffer); |
358 | #endif |
359 | } else { |
360 | status = -EREMOTEIO; |
361 | } |
362 | |
363 | mutex_unlock(lock: &dev->i2c_lock); |
364 | |
365 | if (status) |
366 | dev_dbg(&dev->i2c_client->dev, "err %d\n" , status); |
367 | |
368 | return status; |
369 | } |
370 | |
371 | static const char *mxl692_opcode_string(u8 opcode) |
372 | { |
373 | if (opcode <= MXL_EAGLE_OPCODE_INTERNAL) |
374 | return MXL_EAGLE_OPCODE_STRING[opcode]; |
375 | |
376 | return "invalid opcode" ; |
377 | } |
378 | |
379 | static int mxl692_opwrite(struct mxl692_dev *dev, u8 *buffer, |
380 | u32 size) |
381 | { |
382 | int status = 0, total_len = 0; |
383 | u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; |
384 | struct MXL_EAGLE_HOST_MSG_HEADER_T *tx_hdr = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)buffer; |
385 | |
386 | total_len = size; |
387 | total_len = (total_len + 3) & ~3; /* 4 byte alignment */ |
388 | |
389 | if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) |
390 | dev_dbg(&dev->i2c_client->dev, "hrmph?\n" ); |
391 | |
392 | plocal_buf = local_buf; |
393 | |
394 | *plocal_buf++ = 0xFE; |
395 | *plocal_buf++ = (u8)total_len; |
396 | |
397 | memcpy(plocal_buf, buffer, total_len); |
398 | convert_endian(size: total_len, d: plocal_buf); |
399 | |
400 | if (mxl692_i2c_write(dev, buffer: local_buf, |
401 | buf_len: (total_len + MXL_EAGLE_I2C_PHEADER_SIZE)) < 0) { |
402 | status = -EREMOTEIO; |
403 | goto err_finish; |
404 | } |
405 | err_finish: |
406 | if (status) |
407 | dev_dbg(&dev->i2c_client->dev, "opcode %s err %d\n" , |
408 | mxl692_opcode_string(tx_hdr->opcode), status); |
409 | return status; |
410 | } |
411 | |
412 | static int mxl692_opread(struct mxl692_dev *dev, u8 *buffer, |
413 | u32 size) |
414 | { |
415 | int status = 0; |
416 | u32 ix = 0; |
417 | u8 local_buf[MXL_EAGLE_I2C_PHEADER_SIZE] = {}; |
418 | |
419 | local_buf[0] = 0xFD; |
420 | local_buf[1] = 0; |
421 | |
422 | if (mxl692_i2c_write(dev, buffer: local_buf, MXL_EAGLE_I2C_PHEADER_SIZE) > 0) { |
423 | size = (size + 3) & ~3; /* 4 byte alignment */ |
424 | |
425 | /* Read in 4 byte chunks */ |
426 | for (ix = 0; ix < size; ix += 4) { |
427 | if (mxl692_i2c_read(dev, buffer: buffer + ix, buf_len: 4) < 0) { |
428 | dev_dbg(&dev->i2c_client->dev, "ix=%d size=%d\n" , ix, size); |
429 | status = -EREMOTEIO; |
430 | goto err_finish; |
431 | } |
432 | } |
433 | convert_endian(size, d: buffer); |
434 | } else { |
435 | status = -EREMOTEIO; |
436 | } |
437 | err_finish: |
438 | if (status) |
439 | dev_dbg(&dev->i2c_client->dev, "err %d\n" , status); |
440 | return status; |
441 | } |
442 | |
443 | static int mxl692_i2c_writeread(struct mxl692_dev *dev, |
444 | u8 opcode, |
445 | u8 *tx_payload, |
446 | u8 tx_payload_size, |
447 | u8 *rx_payload, |
448 | u8 rx_payload_expected) |
449 | { |
450 | int status = 0, timeout = 40; |
451 | u8 tx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; |
452 | u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; |
453 | u32 resp_checksum = 0, resp_checksum_tmp = 0; |
454 | struct MXL_EAGLE_HOST_MSG_HEADER_T *; |
455 | struct MXL_EAGLE_HOST_MSG_HEADER_T *; |
456 | |
457 | mutex_lock(&dev->i2c_lock); |
458 | |
459 | if ((tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE) > |
460 | (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) { |
461 | status = -EINVAL; |
462 | goto err_finish; |
463 | } |
464 | |
465 | tx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)tx_buf; |
466 | tx_header->opcode = opcode; |
467 | tx_header->seqnum = dev->seqnum++; |
468 | tx_header->payload_size = tx_payload_size; |
469 | tx_header->checksum = 0; |
470 | |
471 | if (dev->seqnum == 0) |
472 | dev->seqnum = 1; |
473 | |
474 | if (tx_payload && tx_payload_size > 0) |
475 | memcpy(&tx_buf[MXL_EAGLE_HOST_MSG_HEADER_SIZE], tx_payload, tx_payload_size); |
476 | |
477 | mxl692_tx_swap(opcode, buffer: tx_buf); |
478 | |
479 | tx_header->checksum = 0; |
480 | tx_header->checksum = mxl692_checksum(buffer: tx_buf, |
481 | MXL_EAGLE_HOST_MSG_HEADER_SIZE + tx_payload_size); |
482 | #ifdef __LITTLE_ENDIAN |
483 | convert_endian(size: 4, d: (u8 *)&tx_header->checksum); /* cksum is big endian */ |
484 | #endif |
485 | /* send Tx message */ |
486 | status = mxl692_opwrite(dev, buffer: tx_buf, |
487 | size: tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE); |
488 | if (status) { |
489 | status = -EREMOTEIO; |
490 | goto err_finish; |
491 | } |
492 | |
493 | /* receive Rx message (polling) */ |
494 | rx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)rx_buf; |
495 | |
496 | do { |
497 | status = mxl692_opread(dev, buffer: rx_buf, |
498 | size: rx_payload_expected + MXL_EAGLE_HOST_MSG_HEADER_SIZE); |
499 | usleep_range(min: 1000, max: 2000); |
500 | timeout--; |
501 | } while ((timeout > 0) && (status == 0) && |
502 | (rx_header->seqnum == 0) && |
503 | (rx_header->checksum == 0)); |
504 | |
505 | if (timeout == 0 || status) { |
506 | dev_dbg(&dev->i2c_client->dev, "timeout=%d status=%d\n" , |
507 | timeout, status); |
508 | status = -ETIMEDOUT; |
509 | goto err_finish; |
510 | } |
511 | |
512 | if (rx_header->status) { |
513 | dev_dbg(&dev->i2c_client->dev, "rx header status code: %d\n" , rx_header->status); |
514 | status = -EREMOTEIO; |
515 | goto err_finish; |
516 | } |
517 | |
518 | if (rx_header->seqnum != tx_header->seqnum || |
519 | rx_header->opcode != tx_header->opcode || |
520 | rx_header->payload_size != rx_payload_expected) { |
521 | dev_dbg(&dev->i2c_client->dev, "Something failed seq=%s opcode=%s pSize=%s\n" , |
522 | rx_header->seqnum != tx_header->seqnum ? "X" : "0" , |
523 | rx_header->opcode != tx_header->opcode ? "X" : "0" , |
524 | rx_header->payload_size != rx_payload_expected ? "X" : "0" ); |
525 | if (rx_header->payload_size != rx_payload_expected) |
526 | dev_dbg(&dev->i2c_client->dev, |
527 | "rx_header->payloadSize=%d rx_payload_expected=%d\n" , |
528 | rx_header->payload_size, rx_payload_expected); |
529 | status = -EREMOTEIO; |
530 | goto err_finish; |
531 | } |
532 | |
533 | resp_checksum = rx_header->checksum; |
534 | rx_header->checksum = 0; |
535 | |
536 | resp_checksum_tmp = mxl692_checksum(buffer: rx_buf, |
537 | MXL_EAGLE_HOST_MSG_HEADER_SIZE + rx_header->payload_size); |
538 | #ifdef __LITTLE_ENDIAN |
539 | convert_endian(size: 4, d: (u8 *)&resp_checksum_tmp); /* cksum is big endian */ |
540 | #endif |
541 | if (resp_checksum != resp_checksum_tmp) { |
542 | dev_dbg(&dev->i2c_client->dev, "rx checksum failure\n" ); |
543 | status = -EREMOTEIO; |
544 | goto err_finish; |
545 | } |
546 | |
547 | mxl692_rx_swap(opcode: rx_header->opcode, buffer: rx_buf); |
548 | |
549 | if (rx_header->payload_size > 0) { |
550 | if (!rx_payload) { |
551 | dev_dbg(&dev->i2c_client->dev, "no rx payload?!?\n" ); |
552 | status = -EREMOTEIO; |
553 | goto err_finish; |
554 | } |
555 | memcpy(rx_payload, rx_buf + MXL_EAGLE_HOST_MSG_HEADER_SIZE, |
556 | rx_header->payload_size); |
557 | } |
558 | err_finish: |
559 | if (status) |
560 | dev_dbg(&dev->i2c_client->dev, "err %d\n" , status); |
561 | |
562 | mutex_unlock(lock: &dev->i2c_lock); |
563 | return status; |
564 | } |
565 | |
566 | static int mxl692_fwdownload(struct mxl692_dev *dev, |
567 | const u8 *firmware_buf, u32 buf_len) |
568 | { |
569 | int status = 0; |
570 | u32 ix, reg_val = 0x1; |
571 | u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; |
572 | struct MXL_EAGLE_DEV_STATUS_T *dev_status; |
573 | |
574 | if (buf_len < MXL_EAGLE_FW_HEADER_SIZE || |
575 | buf_len > MXL_EAGLE_FW_MAX_SIZE_IN_KB * 1000) |
576 | return -EINVAL; |
577 | |
578 | mutex_lock(&dev->i2c_lock); |
579 | |
580 | dev_dbg(&dev->i2c_client->dev, "\n" ); |
581 | |
582 | status = mxl692_validate_fw_header(dev, buffer: firmware_buf, buf_len); |
583 | if (status) |
584 | goto err_finish; |
585 | |
586 | ix = 16; |
587 | status = mxl692_write_fw_block(dev, buffer: firmware_buf, buf_len, index: &ix); /* DRAM */ |
588 | if (status) |
589 | goto err_finish; |
590 | |
591 | status = mxl692_write_fw_block(dev, buffer: firmware_buf, buf_len, index: &ix); /* IRAM */ |
592 | if (status) |
593 | goto err_finish; |
594 | |
595 | /* release CPU from reset */ |
596 | status = mxl692_memwrite(dev, addr: 0x70000018, buffer: (u8 *)®_val, size: sizeof(u32)); |
597 | if (status) |
598 | goto err_finish; |
599 | |
600 | mutex_unlock(lock: &dev->i2c_lock); |
601 | |
602 | if (status == 0) { |
603 | /* verify FW is alive */ |
604 | usleep_range(MXL_EAGLE_FW_LOAD_TIME * 1000, max: (MXL_EAGLE_FW_LOAD_TIME + 5) * 1000); |
605 | dev_status = (struct MXL_EAGLE_DEV_STATUS_T *)&rx_buf; |
606 | status = mxl692_i2c_writeread(dev, |
607 | opcode: MXL_EAGLE_OPCODE_DEVICE_STATUS_GET, |
608 | NULL, |
609 | tx_payload_size: 0, |
610 | rx_payload: (u8 *)dev_status, |
611 | rx_payload_expected: sizeof(struct MXL_EAGLE_DEV_STATUS_T)); |
612 | } |
613 | |
614 | return status; |
615 | err_finish: |
616 | mutex_unlock(lock: &dev->i2c_lock); |
617 | if (status) |
618 | dev_dbg(&dev->i2c_client->dev, "err %d\n" , status); |
619 | return status; |
620 | } |
621 | |
622 | static int mxl692_get_versions(struct mxl692_dev *dev) |
623 | { |
624 | int status = 0; |
625 | struct MXL_EAGLE_DEV_VER_T dev_ver = {}; |
626 | static const char * const chip_id[] = {"N/A" , "691" , "248" , "692" }; |
627 | |
628 | status = mxl692_i2c_writeread(dev, opcode: MXL_EAGLE_OPCODE_DEVICE_VERSION_GET, |
629 | NULL, |
630 | tx_payload_size: 0, |
631 | rx_payload: (u8 *)&dev_ver, |
632 | rx_payload_expected: sizeof(struct MXL_EAGLE_DEV_VER_T)); |
633 | if (status) |
634 | return status; |
635 | |
636 | dev_info(&dev->i2c_client->dev, "MxL692_DEMOD Chip ID: %s\n" , |
637 | chip_id[dev_ver.chip_id]); |
638 | |
639 | dev_info(&dev->i2c_client->dev, |
640 | "MxL692_DEMOD FW Version: %d.%d.%d.%d_RC%d\n" , |
641 | dev_ver.firmware_ver[0], |
642 | dev_ver.firmware_ver[1], |
643 | dev_ver.firmware_ver[2], |
644 | dev_ver.firmware_ver[3], |
645 | dev_ver.firmware_ver[4]); |
646 | |
647 | return status; |
648 | } |
649 | |
650 | static int mxl692_reset(struct mxl692_dev *dev) |
651 | { |
652 | int status = 0; |
653 | u32 dev_type = MXL_EAGLE_DEVICE_MAX, reg_val = 0x2; |
654 | |
655 | dev_dbg(&dev->i2c_client->dev, "\n" ); |
656 | |
657 | /* legacy i2c override */ |
658 | status = mxl692_memwrite(dev, addr: 0x80000100, buffer: (u8 *)®_val, size: sizeof(u32)); |
659 | if (status) |
660 | goto err_finish; |
661 | |
662 | /* verify sku */ |
663 | status = mxl692_memread(dev, addr: 0x70000188, buffer: (u8 *)&dev_type, size: sizeof(u32)); |
664 | if (status) |
665 | goto err_finish; |
666 | |
667 | if (dev_type != dev->device_type) |
668 | goto err_finish; |
669 | |
670 | err_finish: |
671 | if (status) |
672 | dev_dbg(&dev->i2c_client->dev, "err %d\n" , status); |
673 | return status; |
674 | } |
675 | |
676 | static int mxl692_config_regulators(struct mxl692_dev *dev, |
677 | enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E power_supply) |
678 | { |
679 | int status = 0; |
680 | u32 reg_val; |
681 | |
682 | dev_dbg(&dev->i2c_client->dev, "\n" ); |
683 | |
684 | /* configure main regulator according to the power supply source */ |
685 | status = mxl692_memread(dev, addr: 0x90000000, buffer: (u8 *)®_val, size: sizeof(u32)); |
686 | if (status) |
687 | goto err_finish; |
688 | |
689 | reg_val &= 0x00FFFFFF; |
690 | reg_val |= (power_supply == MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE) ? |
691 | 0x14000000 : 0x10000000; |
692 | |
693 | status = mxl692_memwrite(dev, addr: 0x90000000, buffer: (u8 *)®_val, size: sizeof(u32)); |
694 | if (status) |
695 | goto err_finish; |
696 | |
697 | /* configure digital regulator to high current mode */ |
698 | status = mxl692_memread(dev, addr: 0x90000018, buffer: (u8 *)®_val, size: sizeof(u32)); |
699 | if (status) |
700 | goto err_finish; |
701 | |
702 | reg_val |= 0x800; |
703 | |
704 | status = mxl692_memwrite(dev, addr: 0x90000018, buffer: (u8 *)®_val, size: sizeof(u32)); |
705 | |
706 | err_finish: |
707 | if (status) |
708 | dev_dbg(&dev->i2c_client->dev, "err %d\n" , status); |
709 | return status; |
710 | } |
711 | |
712 | static int mxl692_config_xtal(struct mxl692_dev *dev, |
713 | struct MXL_EAGLE_DEV_XTAL_T *dev_xtal) |
714 | { |
715 | int status = 0; |
716 | u32 reg_val, reg_val1; |
717 | |
718 | dev_dbg(&dev->i2c_client->dev, "\n" ); |
719 | |
720 | status = mxl692_memread(dev, addr: 0x90000000, buffer: (u8 *)®_val, size: sizeof(u32)); |
721 | if (status) |
722 | goto err_finish; |
723 | |
724 | /* set XTAL capacitance */ |
725 | reg_val &= 0xFFFFFFE0; |
726 | reg_val |= dev_xtal->xtal_cap; |
727 | |
728 | /* set CLK OUT */ |
729 | reg_val = dev_xtal->clk_out_enable ? (reg_val | 0x0100) : (reg_val & 0xFFFFFEFF); |
730 | |
731 | status = mxl692_memwrite(dev, addr: 0x90000000, buffer: (u8 *)®_val, size: sizeof(u32)); |
732 | if (status) |
733 | goto err_finish; |
734 | |
735 | /* set CLK OUT divider */ |
736 | reg_val = dev_xtal->clk_out_div_enable ? (reg_val | 0x0200) : (reg_val & 0xFFFFFDFF); |
737 | |
738 | status = mxl692_memwrite(dev, addr: 0x90000000, buffer: (u8 *)®_val, size: sizeof(u32)); |
739 | if (status) |
740 | goto err_finish; |
741 | |
742 | /* set XTAL sharing */ |
743 | reg_val = dev_xtal->xtal_sharing_enable ? (reg_val | 0x010400) : (reg_val & 0xFFFEFBFF); |
744 | |
745 | status = mxl692_memwrite(dev, addr: 0x90000000, buffer: (u8 *)®_val, size: sizeof(u32)); |
746 | if (status) |
747 | goto err_finish; |
748 | |
749 | /* enable/disable XTAL calibration, based on master/slave device */ |
750 | status = mxl692_memread(dev, addr: 0x90000030, buffer: (u8 *)®_val1, size: sizeof(u32)); |
751 | if (status) |
752 | goto err_finish; |
753 | |
754 | if (dev_xtal->xtal_calibration_enable) { |
755 | /* enable XTAL calibration and set XTAL amplitude to a higher value */ |
756 | reg_val1 &= 0xFFFFFFFD; |
757 | reg_val1 |= 0x30; |
758 | |
759 | status = mxl692_memwrite(dev, addr: 0x90000030, buffer: (u8 *)®_val1, size: sizeof(u32)); |
760 | if (status) |
761 | goto err_finish; |
762 | } else { |
763 | /* disable XTAL calibration */ |
764 | reg_val1 |= 0x2; |
765 | |
766 | status = mxl692_memwrite(dev, addr: 0x90000030, buffer: (u8 *)®_val1, size: sizeof(u32)); |
767 | if (status) |
768 | goto err_finish; |
769 | |
770 | /* set XTAL bias value */ |
771 | status = mxl692_memread(dev, addr: 0x9000002c, buffer: (u8 *)®_val, size: sizeof(u32)); |
772 | if (status) |
773 | goto err_finish; |
774 | |
775 | reg_val &= 0xC0FFFFFF; |
776 | reg_val |= 0xA000000; |
777 | |
778 | status = mxl692_memwrite(dev, addr: 0x9000002c, buffer: (u8 *)®_val, size: sizeof(u32)); |
779 | if (status) |
780 | goto err_finish; |
781 | } |
782 | |
783 | /* start XTAL calibration */ |
784 | status = mxl692_memread(dev, addr: 0x70000010, buffer: (u8 *)®_val, size: sizeof(u32)); |
785 | if (status) |
786 | goto err_finish; |
787 | |
788 | reg_val |= 0x8; |
789 | |
790 | status = mxl692_memwrite(dev, addr: 0x70000010, buffer: (u8 *)®_val, size: sizeof(u32)); |
791 | if (status) |
792 | goto err_finish; |
793 | |
794 | status = mxl692_memread(dev, addr: 0x70000018, buffer: (u8 *)®_val, size: sizeof(u32)); |
795 | if (status) |
796 | goto err_finish; |
797 | |
798 | reg_val |= 0x10; |
799 | |
800 | status = mxl692_memwrite(dev, addr: 0x70000018, buffer: (u8 *)®_val, size: sizeof(u32)); |
801 | if (status) |
802 | goto err_finish; |
803 | |
804 | status = mxl692_memread(dev, addr: 0x9001014c, buffer: (u8 *)®_val, size: sizeof(u32)); |
805 | if (status) |
806 | goto err_finish; |
807 | |
808 | reg_val &= 0xFFFFEFFF; |
809 | |
810 | status = mxl692_memwrite(dev, addr: 0x9001014c, buffer: (u8 *)®_val, size: sizeof(u32)); |
811 | if (status) |
812 | goto err_finish; |
813 | |
814 | reg_val |= 0x1000; |
815 | |
816 | status = mxl692_memwrite(dev, addr: 0x9001014c, buffer: (u8 *)®_val, size: sizeof(u32)); |
817 | if (status) |
818 | goto err_finish; |
819 | |
820 | usleep_range(min: 45000, max: 55000); |
821 | |
822 | err_finish: |
823 | if (status) |
824 | dev_dbg(&dev->i2c_client->dev, "err %d\n" , status); |
825 | return status; |
826 | } |
827 | |
828 | static int mxl692_powermode(struct mxl692_dev *dev, |
829 | enum MXL_EAGLE_POWER_MODE_E power_mode) |
830 | { |
831 | int status = 0; |
832 | u8 mode = power_mode; |
833 | |
834 | dev_dbg(&dev->i2c_client->dev, "%s\n" , |
835 | power_mode == MXL_EAGLE_POWER_MODE_SLEEP ? "sleep" : "active" ); |
836 | |
837 | status = mxl692_i2c_writeread(dev, |
838 | opcode: MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET, |
839 | tx_payload: &mode, |
840 | tx_payload_size: sizeof(u8), |
841 | NULL, |
842 | rx_payload_expected: 0); |
843 | if (status) { |
844 | dev_dbg(&dev->i2c_client->dev, "err %d\n" , status); |
845 | return status; |
846 | } |
847 | |
848 | dev->power_mode = power_mode; |
849 | |
850 | return status; |
851 | } |
852 | |
853 | static int mxl692_init(struct dvb_frontend *fe) |
854 | { |
855 | struct mxl692_dev *dev = fe->demodulator_priv; |
856 | struct i2c_client *client = dev->i2c_client; |
857 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
858 | int status = 0; |
859 | const struct firmware *firmware; |
860 | struct MXL_EAGLE_DEV_XTAL_T xtal_config = {}; |
861 | |
862 | dev_dbg(&dev->i2c_client->dev, "\n" ); |
863 | |
864 | if (dev->init_done) |
865 | goto warm; |
866 | |
867 | dev->seqnum = 1; |
868 | |
869 | status = mxl692_reset(dev); |
870 | if (status) |
871 | goto err; |
872 | |
873 | usleep_range(min: 50 * 1000, max: 60 * 1000); /* was 1000! */ |
874 | |
875 | status = mxl692_config_regulators(dev, power_supply: MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL); |
876 | if (status) |
877 | goto err; |
878 | |
879 | xtal_config.xtal_cap = 26; |
880 | xtal_config.clk_out_div_enable = 0; |
881 | xtal_config.clk_out_enable = 0; |
882 | xtal_config.xtal_calibration_enable = 0; |
883 | xtal_config.xtal_sharing_enable = 1; |
884 | status = mxl692_config_xtal(dev, dev_xtal: &xtal_config); |
885 | if (status) |
886 | goto err; |
887 | |
888 | status = request_firmware(fw: &firmware, MXL692_FIRMWARE, device: &client->dev); |
889 | if (status) { |
890 | dev_dbg(&dev->i2c_client->dev, "firmware missing? %s\n" , |
891 | MXL692_FIRMWARE); |
892 | goto err; |
893 | } |
894 | |
895 | status = mxl692_fwdownload(dev, firmware_buf: firmware->data, buf_len: firmware->size); |
896 | if (status) |
897 | goto err_release_firmware; |
898 | |
899 | release_firmware(fw: firmware); |
900 | |
901 | status = mxl692_get_versions(dev); |
902 | if (status) |
903 | goto err; |
904 | |
905 | dev->power_mode = MXL_EAGLE_POWER_MODE_SLEEP; |
906 | warm: |
907 | /* Config Device Power Mode */ |
908 | if (dev->power_mode != MXL_EAGLE_POWER_MODE_ACTIVE) { |
909 | status = mxl692_powermode(dev, power_mode: MXL_EAGLE_POWER_MODE_ACTIVE); |
910 | if (status) |
911 | goto err; |
912 | |
913 | usleep_range(min: 50 * 1000, max: 60 * 1000); /* was 500! */ |
914 | } |
915 | |
916 | /* Init stats here to indicate which stats are supported */ |
917 | c->cnr.len = 1; |
918 | c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
919 | c->post_bit_error.len = 1; |
920 | c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
921 | c->post_bit_count.len = 1; |
922 | c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
923 | c->block_error.len = 1; |
924 | c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
925 | |
926 | dev->init_done = 1; |
927 | return 0; |
928 | err_release_firmware: |
929 | release_firmware(fw: firmware); |
930 | err: |
931 | dev_dbg(&dev->i2c_client->dev, "err %d\n" , status); |
932 | return status; |
933 | } |
934 | |
935 | static int mxl692_sleep(struct dvb_frontend *fe) |
936 | { |
937 | struct mxl692_dev *dev = fe->demodulator_priv; |
938 | |
939 | if (dev->power_mode != MXL_EAGLE_POWER_MODE_SLEEP) |
940 | mxl692_powermode(dev, power_mode: MXL_EAGLE_POWER_MODE_SLEEP); |
941 | |
942 | return 0; |
943 | } |
944 | |
945 | static int mxl692_set_frontend(struct dvb_frontend *fe) |
946 | { |
947 | struct dtv_frontend_properties *p = &fe->dtv_property_cache; |
948 | struct mxl692_dev *dev = fe->demodulator_priv; |
949 | |
950 | int status = 0; |
951 | enum MXL_EAGLE_DEMOD_TYPE_E demod_type; |
952 | struct MXL_EAGLE_MPEGOUT_PARAMS_T mpeg_params = {}; |
953 | enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E qam_annex = MXL_EAGLE_QAM_DEMOD_ANNEX_B; |
954 | struct MXL_EAGLE_QAM_DEMOD_PARAMS_T qam_params = {}; |
955 | struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T tuner_params = {}; |
956 | u8 op_param = 0; |
957 | |
958 | dev_dbg(&dev->i2c_client->dev, "\n" ); |
959 | |
960 | switch (p->modulation) { |
961 | case VSB_8: |
962 | demod_type = MXL_EAGLE_DEMOD_TYPE_ATSC; |
963 | break; |
964 | case QAM_AUTO: |
965 | case QAM_64: |
966 | case QAM_128: |
967 | case QAM_256: |
968 | demod_type = MXL_EAGLE_DEMOD_TYPE_QAM; |
969 | break; |
970 | default: |
971 | return -EINVAL; |
972 | } |
973 | |
974 | if (dev->current_frequency == p->frequency && dev->demod_type == demod_type) { |
975 | dev_dbg(&dev->i2c_client->dev, "already set up\n" ); |
976 | return 0; |
977 | } |
978 | |
979 | dev->current_frequency = -1; |
980 | dev->demod_type = -1; |
981 | |
982 | op_param = demod_type; |
983 | status = mxl692_i2c_writeread(dev, |
984 | opcode: MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET, |
985 | tx_payload: &op_param, |
986 | tx_payload_size: sizeof(u8), |
987 | NULL, |
988 | rx_payload_expected: 0); |
989 | if (status) { |
990 | dev_dbg(&dev->i2c_client->dev, |
991 | "DEVICE_DEMODULATOR_TYPE_SET...FAIL err 0x%x\n" , status); |
992 | goto err; |
993 | } |
994 | |
995 | usleep_range(min: 20 * 1000, max: 30 * 1000); /* was 500! */ |
996 | |
997 | mpeg_params.mpeg_parallel = 0; |
998 | mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_MSB_1ST; |
999 | mpeg_params.mpeg_sync_pulse_width = MXL_EAGLE_DATA_SYNC_WIDTH_BIT; |
1000 | mpeg_params.mpeg_valid_pol = MXL_EAGLE_CLOCK_POSITIVE; |
1001 | mpeg_params.mpeg_sync_pol = MXL_EAGLE_CLOCK_POSITIVE; |
1002 | mpeg_params.mpeg_clk_pol = MXL_EAGLE_CLOCK_NEGATIVE; |
1003 | mpeg_params.mpeg3wire_mode_enable = 0; |
1004 | mpeg_params.mpeg_clk_freq = MXL_EAGLE_MPEG_CLOCK_27MHZ; |
1005 | |
1006 | switch (demod_type) { |
1007 | case MXL_EAGLE_DEMOD_TYPE_ATSC: |
1008 | status = mxl692_i2c_writeread(dev, |
1009 | opcode: MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, |
1010 | tx_payload: (u8 *)&mpeg_params, |
1011 | tx_payload_size: sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), |
1012 | NULL, |
1013 | rx_payload_expected: 0); |
1014 | if (status) |
1015 | goto err; |
1016 | break; |
1017 | case MXL_EAGLE_DEMOD_TYPE_QAM: |
1018 | if (qam_annex == MXL_EAGLE_QAM_DEMOD_ANNEX_A) |
1019 | mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_LSB_1ST; |
1020 | status = mxl692_i2c_writeread(dev, |
1021 | opcode: MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, |
1022 | tx_payload: (u8 *)&mpeg_params, |
1023 | tx_payload_size: sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), |
1024 | NULL, |
1025 | rx_payload_expected: 0); |
1026 | if (status) |
1027 | goto err; |
1028 | |
1029 | qam_params.annex_type = qam_annex; |
1030 | qam_params.qam_type = MXL_EAGLE_QAM_DEMOD_AUTO; |
1031 | qam_params.iq_flip = MXL_EAGLE_DEMOD_IQ_AUTO; |
1032 | if (p->modulation == QAM_64) |
1033 | qam_params.symbol_rate_hz = 5057000; |
1034 | else |
1035 | qam_params.symbol_rate_hz = 5361000; |
1036 | |
1037 | qam_params.symbol_rate_256qam_hz = 5361000; |
1038 | |
1039 | status = mxl692_i2c_writeread(dev, |
1040 | opcode: MXL_EAGLE_OPCODE_QAM_PARAMS_SET, |
1041 | tx_payload: (u8 *)&qam_params, |
1042 | tx_payload_size: sizeof(struct MXL_EAGLE_QAM_DEMOD_PARAMS_T), |
1043 | NULL, rx_payload_expected: 0); |
1044 | if (status) |
1045 | goto err; |
1046 | |
1047 | break; |
1048 | default: |
1049 | break; |
1050 | } |
1051 | |
1052 | usleep_range(min: 20 * 1000, max: 30 * 1000); /* was 500! */ |
1053 | |
1054 | tuner_params.freq_hz = p->frequency; |
1055 | tuner_params.bandwidth = MXL_EAGLE_TUNER_BW_6MHZ; |
1056 | tuner_params.tune_mode = MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW; |
1057 | |
1058 | dev_dbg(&dev->i2c_client->dev, " Tuning Freq: %d %s\n" , tuner_params.freq_hz, |
1059 | demod_type == MXL_EAGLE_DEMOD_TYPE_ATSC ? "ATSC" : "QAM" ); |
1060 | |
1061 | status = mxl692_i2c_writeread(dev, |
1062 | opcode: MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET, |
1063 | tx_payload: (u8 *)&tuner_params, |
1064 | tx_payload_size: sizeof(struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T), |
1065 | NULL, |
1066 | rx_payload_expected: 0); |
1067 | if (status) |
1068 | goto err; |
1069 | |
1070 | usleep_range(min: 20 * 1000, max: 30 * 1000); /* was 500! */ |
1071 | |
1072 | switch (demod_type) { |
1073 | case MXL_EAGLE_DEMOD_TYPE_ATSC: |
1074 | status = mxl692_i2c_writeread(dev, |
1075 | opcode: MXL_EAGLE_OPCODE_ATSC_INIT_SET, |
1076 | NULL, tx_payload_size: 0, NULL, rx_payload_expected: 0); |
1077 | if (status) |
1078 | goto err; |
1079 | break; |
1080 | case MXL_EAGLE_DEMOD_TYPE_QAM: |
1081 | status = mxl692_i2c_writeread(dev, |
1082 | opcode: MXL_EAGLE_OPCODE_QAM_RESTART_SET, |
1083 | NULL, tx_payload_size: 0, NULL, rx_payload_expected: 0); |
1084 | if (status) |
1085 | goto err; |
1086 | break; |
1087 | default: |
1088 | break; |
1089 | } |
1090 | |
1091 | dev->demod_type = demod_type; |
1092 | dev->current_frequency = p->frequency; |
1093 | |
1094 | return 0; |
1095 | err: |
1096 | dev_dbg(&dev->i2c_client->dev, "err %d\n" , status); |
1097 | return status; |
1098 | } |
1099 | |
1100 | static int mxl692_get_frontend(struct dvb_frontend *fe, |
1101 | struct dtv_frontend_properties *p) |
1102 | { |
1103 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
1104 | |
1105 | p->modulation = c->modulation; |
1106 | p->frequency = c->frequency; |
1107 | |
1108 | return 0; |
1109 | } |
1110 | |
1111 | static int mxl692_read_snr(struct dvb_frontend *fe, u16 *snr) |
1112 | { |
1113 | struct mxl692_dev *dev = fe->demodulator_priv; |
1114 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
1115 | u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; |
1116 | struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; |
1117 | struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; |
1118 | enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; |
1119 | int mxl_status = 0; |
1120 | |
1121 | *snr = 0; |
1122 | |
1123 | dev_dbg(&dev->i2c_client->dev, "\n" ); |
1124 | |
1125 | atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; |
1126 | qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; |
1127 | |
1128 | switch (demod_type) { |
1129 | case MXL_EAGLE_DEMOD_TYPE_ATSC: |
1130 | mxl_status = mxl692_i2c_writeread(dev, |
1131 | opcode: MXL_EAGLE_OPCODE_ATSC_STATUS_GET, |
1132 | NULL, |
1133 | tx_payload_size: 0, |
1134 | rx_payload: rx_buf, |
1135 | rx_payload_expected: sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); |
1136 | if (!mxl_status) { |
1137 | *snr = (u16)(atsc_status->snr_db_tenths / 10); |
1138 | c->cnr.stat[0].scale = FE_SCALE_DECIBEL; |
1139 | c->cnr.stat[0].svalue = *snr; |
1140 | } |
1141 | break; |
1142 | case MXL_EAGLE_DEMOD_TYPE_QAM: |
1143 | mxl_status = mxl692_i2c_writeread(dev, |
1144 | opcode: MXL_EAGLE_OPCODE_QAM_STATUS_GET, |
1145 | NULL, |
1146 | tx_payload_size: 0, |
1147 | rx_payload: rx_buf, |
1148 | rx_payload_expected: sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); |
1149 | if (!mxl_status) |
1150 | *snr = (u16)(qam_status->snr_db_tenths / 10); |
1151 | break; |
1152 | case MXL_EAGLE_DEMOD_TYPE_OOB: |
1153 | default: |
1154 | break; |
1155 | } |
1156 | |
1157 | if (mxl_status) |
1158 | dev_dbg(&dev->i2c_client->dev, "err %d\n" , mxl_status); |
1159 | return mxl_status; |
1160 | } |
1161 | |
1162 | static int mxl692_read_ber_ucb(struct dvb_frontend *fe) |
1163 | { |
1164 | struct mxl692_dev *dev = fe->demodulator_priv; |
1165 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
1166 | u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; |
1167 | struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *atsc_errors; |
1168 | enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; |
1169 | int mxl_status = 0; |
1170 | u32 utmp; |
1171 | |
1172 | dev_dbg(&dev->i2c_client->dev, "\n" ); |
1173 | |
1174 | atsc_errors = (struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *)&rx_buf; |
1175 | |
1176 | switch (demod_type) { |
1177 | case MXL_EAGLE_DEMOD_TYPE_ATSC: |
1178 | mxl_status = mxl692_i2c_writeread(dev, |
1179 | opcode: MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, |
1180 | NULL, |
1181 | tx_payload_size: 0, |
1182 | rx_payload: rx_buf, |
1183 | rx_payload_expected: sizeof(struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T)); |
1184 | if (!mxl_status) { |
1185 | if (atsc_errors->error_packets == 0) |
1186 | utmp = 0; |
1187 | else |
1188 | utmp = ((atsc_errors->error_bytes / atsc_errors->error_packets) * |
1189 | atsc_errors->total_packets); |
1190 | /* ber */ |
1191 | c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; |
1192 | c->post_bit_error.stat[0].uvalue += atsc_errors->error_bytes; |
1193 | c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; |
1194 | c->post_bit_count.stat[0].uvalue += utmp; |
1195 | /* ucb */ |
1196 | c->block_error.stat[0].scale = FE_SCALE_COUNTER; |
1197 | c->block_error.stat[0].uvalue += atsc_errors->error_packets; |
1198 | |
1199 | dev_dbg(&dev->i2c_client->dev, "%llu %llu\n" , |
1200 | c->post_bit_count.stat[0].uvalue, c->block_error.stat[0].uvalue); |
1201 | } |
1202 | break; |
1203 | case MXL_EAGLE_DEMOD_TYPE_QAM: |
1204 | case MXL_EAGLE_DEMOD_TYPE_OOB: |
1205 | default: |
1206 | break; |
1207 | } |
1208 | |
1209 | if (mxl_status) |
1210 | dev_dbg(&dev->i2c_client->dev, "err %d\n" , mxl_status); |
1211 | |
1212 | return mxl_status; |
1213 | } |
1214 | |
1215 | static int mxl692_read_status(struct dvb_frontend *fe, |
1216 | enum fe_status *status) |
1217 | { |
1218 | struct mxl692_dev *dev = fe->demodulator_priv; |
1219 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
1220 | u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; |
1221 | struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; |
1222 | struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; |
1223 | enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; |
1224 | int mxl_status = 0; |
1225 | *status = 0; |
1226 | |
1227 | dev_dbg(&dev->i2c_client->dev, "\n" ); |
1228 | |
1229 | atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; |
1230 | qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; |
1231 | |
1232 | switch (demod_type) { |
1233 | case MXL_EAGLE_DEMOD_TYPE_ATSC: |
1234 | mxl_status = mxl692_i2c_writeread(dev, |
1235 | opcode: MXL_EAGLE_OPCODE_ATSC_STATUS_GET, |
1236 | NULL, |
1237 | tx_payload_size: 0, |
1238 | rx_payload: rx_buf, |
1239 | rx_payload_expected: sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); |
1240 | if (!mxl_status && atsc_status->atsc_lock) { |
1241 | *status |= FE_HAS_SIGNAL; |
1242 | *status |= FE_HAS_CARRIER; |
1243 | *status |= FE_HAS_VITERBI; |
1244 | *status |= FE_HAS_SYNC; |
1245 | *status |= FE_HAS_LOCK; |
1246 | |
1247 | c->cnr.stat[0].scale = FE_SCALE_DECIBEL; |
1248 | c->cnr.stat[0].svalue = atsc_status->snr_db_tenths / 10; |
1249 | } |
1250 | break; |
1251 | case MXL_EAGLE_DEMOD_TYPE_QAM: |
1252 | mxl_status = mxl692_i2c_writeread(dev, |
1253 | opcode: MXL_EAGLE_OPCODE_QAM_STATUS_GET, |
1254 | NULL, |
1255 | tx_payload_size: 0, |
1256 | rx_payload: rx_buf, |
1257 | rx_payload_expected: sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); |
1258 | if (!mxl_status && qam_status->qam_locked) { |
1259 | *status |= FE_HAS_SIGNAL; |
1260 | *status |= FE_HAS_CARRIER; |
1261 | *status |= FE_HAS_VITERBI; |
1262 | *status |= FE_HAS_SYNC; |
1263 | *status |= FE_HAS_LOCK; |
1264 | |
1265 | c->cnr.stat[0].scale = FE_SCALE_DECIBEL; |
1266 | c->cnr.stat[0].svalue = qam_status->snr_db_tenths / 10; |
1267 | } |
1268 | break; |
1269 | case MXL_EAGLE_DEMOD_TYPE_OOB: |
1270 | default: |
1271 | break; |
1272 | } |
1273 | |
1274 | if ((*status & FE_HAS_LOCK) == 0) { |
1275 | /* No lock, reset all statistics */ |
1276 | c->cnr.len = 1; |
1277 | c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
1278 | c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
1279 | c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
1280 | c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
1281 | return 0; |
1282 | } |
1283 | |
1284 | if (mxl_status) |
1285 | dev_dbg(&dev->i2c_client->dev, "err %d\n" , mxl_status); |
1286 | else |
1287 | mxl_status = mxl692_read_ber_ucb(fe); |
1288 | |
1289 | return mxl_status; |
1290 | } |
1291 | |
1292 | static const struct dvb_frontend_ops mxl692_ops = { |
1293 | .delsys = { SYS_ATSC }, |
1294 | .info = { |
1295 | .name = "MaxLinear MxL692 VSB tuner-demodulator" , |
1296 | .frequency_min_hz = 54000000, |
1297 | .frequency_max_hz = 858000000, |
1298 | .frequency_stepsize_hz = 62500, |
1299 | .caps = FE_CAN_8VSB |
1300 | }, |
1301 | |
1302 | .init = mxl692_init, |
1303 | .sleep = mxl692_sleep, |
1304 | .set_frontend = mxl692_set_frontend, |
1305 | .get_frontend = mxl692_get_frontend, |
1306 | |
1307 | .read_status = mxl692_read_status, |
1308 | .read_snr = mxl692_read_snr, |
1309 | }; |
1310 | |
1311 | static int mxl692_probe(struct i2c_client *client) |
1312 | { |
1313 | struct mxl692_config *config = client->dev.platform_data; |
1314 | struct mxl692_dev *dev; |
1315 | int ret = 0; |
1316 | |
1317 | dev = kzalloc(size: sizeof(*dev), GFP_KERNEL); |
1318 | if (!dev) { |
1319 | ret = -ENOMEM; |
1320 | dev_dbg(&client->dev, "kzalloc() failed\n" ); |
1321 | goto err; |
1322 | } |
1323 | |
1324 | memcpy(&dev->fe.ops, &mxl692_ops, sizeof(struct dvb_frontend_ops)); |
1325 | dev->fe.demodulator_priv = dev; |
1326 | dev->i2c_client = client; |
1327 | *config->fe = &dev->fe; |
1328 | mutex_init(&dev->i2c_lock); |
1329 | i2c_set_clientdata(client, data: dev); |
1330 | |
1331 | dev_info(&client->dev, "MaxLinear mxl692 successfully attached\n" ); |
1332 | |
1333 | return 0; |
1334 | err: |
1335 | dev_dbg(&client->dev, "failed %d\n" , ret); |
1336 | return -ENODEV; |
1337 | } |
1338 | |
1339 | static void mxl692_remove(struct i2c_client *client) |
1340 | { |
1341 | struct mxl692_dev *dev = i2c_get_clientdata(client); |
1342 | |
1343 | dev->fe.demodulator_priv = NULL; |
1344 | i2c_set_clientdata(client, NULL); |
1345 | kfree(objp: dev); |
1346 | } |
1347 | |
1348 | static const struct i2c_device_id mxl692_id_table[] = { |
1349 | {"mxl692" , 0}, |
1350 | {} |
1351 | }; |
1352 | MODULE_DEVICE_TABLE(i2c, mxl692_id_table); |
1353 | |
1354 | static struct i2c_driver mxl692_driver = { |
1355 | .driver = { |
1356 | .name = "mxl692" , |
1357 | }, |
1358 | .probe = mxl692_probe, |
1359 | .remove = mxl692_remove, |
1360 | .id_table = mxl692_id_table, |
1361 | }; |
1362 | |
1363 | module_i2c_driver(mxl692_driver); |
1364 | |
1365 | MODULE_AUTHOR("Brad Love <brad@nextdimension.cc>" ); |
1366 | MODULE_DESCRIPTION("MaxLinear MxL692 demodulator/tuner driver" ); |
1367 | MODULE_FIRMWARE(MXL692_FIRMWARE); |
1368 | MODULE_LICENSE("GPL" ); |
1369 | |