1369 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Driver for the MaxLinear MxL69x family of combo tuners/demods4 *5 * Copyright (C) 2020 Brad Love <brad@nextdimension.cc>6 *7 * based on code:8 * Copyright (c) 2016 MaxLinear, Inc. All rights reserved9 * which was released under GPL V210 */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 20static const struct dvb_frontend_ops mxl692_ops;21 22struct 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 34static 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_len42 };43 44 ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1);45 if (ret != 1)46 dev_dbg(&dev->i2c_client->dev, "i2c write error!\n");47 48 return ret;49}50 51static 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_len59 };60 61 ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1);62 if (ret != 1)63 dev_dbg(&dev->i2c_client->dev, "i2c read error!\n");64 65 return ret;66}67 68static 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 102static 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 + i);108 return count;109}110 111static void mxl692_tx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer)112{113#ifdef __BIG_ENDIAN114 return;115#endif116 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(sizeof(u32), buffer);123 break;124 case MXL_EAGLE_OPCODE_QAM_PARAMS_SET:125 buffer += 5;126 buffer += convert_endian(2 * sizeof(u32), buffer);127 break;128 default:129 /* no swapping - all get opcodes */130 /* ATSC/OOB no swapping */131 break;132 }133}134 135static void mxl692_rx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer)136{137#ifdef __BIG_ENDIAN138 return;139#endif140 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(2 * sizeof(u16), buffer);146 break;147 case MXL_EAGLE_OPCODE_ATSC_STATUS_GET:148 buffer += convert_endian_n(2, sizeof(u16), buffer);149 buffer += convert_endian(sizeof(u32), buffer);150 break;151 case MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET:152 buffer += convert_endian(3 * sizeof(u32), buffer);153 break;154 case MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET:155 buffer += convert_endian_n(24, sizeof(u16), buffer);156 break;157 case MXL_EAGLE_OPCODE_QAM_STATUS_GET:158 buffer += 8;159 buffer += convert_endian_n(2, sizeof(u16), buffer);160 buffer += convert_endian(sizeof(u32), buffer);161 break;162 case MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET:163 buffer += convert_endian(7 * sizeof(u32), 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(24, sizeof(u16), buffer);171 break;172 case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET:173 buffer += convert_endian_n(8, sizeof(u16), buffer);174 break;175 case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET:176 buffer += convert_endian_n(17, sizeof(u16), buffer);177 break;178 case MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET:179 buffer += convert_endian(3 * sizeof(u32), buffer);180 break;181 case MXL_EAGLE_OPCODE_OOB_STATUS_GET:182 buffer += convert_endian_n(2, sizeof(u16), buffer);183 buffer += convert_endian(sizeof(u32), buffer);184 break;185 case MXL_EAGLE_OPCODE_SMA_RECEIVE_GET:186 buffer += convert_endian(sizeof(u32), buffer);187 break;188 default:189 /* no swapping - all set opcodes */190 break;191 }192}193 194static 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 211static int mxl692_validate_fw_header(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, 0x80220 };221 222 if (memcmp(buffer, fw_hdr, 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 241err_finish:242 if (status)243 dev_dbg(&dev->i2c_client->dev, "failed\n");244 return status;245}246 247static 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_ENDIAN273 convert_endian(sizeof(u32), plocal_buf);274#endif275 plocal_buf += sizeof(u32);276 277 memcpy(plocal_buf, &buffer[ix], chunk_len);278 convert_endian(chunk_len, plocal_buf);279 if (mxl692_i2c_write(dev, local_buf,280 (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 300static 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_ENDIAN322 convert_endian(sizeof(u32) + total_len, local_buf + 2);323#endif324 if (mxl692_i2c_write(dev, local_buf,325 (total_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) {326 status = -EREMOTEIO;327 goto err_finish;328 }329 330 return status;331err_finish:332 dev_dbg(&dev->i2c_client->dev, "err %d\n", status);333 return status;334}335 336static 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_ENDIAN348 convert_endian(sizeof(u32), plocal_buf);349#endif350 mutex_lock(&dev->i2c_lock);351 352 if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_MHEADER_SIZE) > 0) {353 size = (size + 3) & ~3; /* 4 byte alignment */354 status = mxl692_i2c_read(dev, buffer, (u16)size) < 0 ? -EREMOTEIO : 0;355#ifdef __BIG_ENDIAN356 if (status == 0)357 convert_endian(size, buffer);358#endif359 } else {360 status = -EREMOTEIO;361 }362 363 mutex_unlock(&dev->i2c_lock);364 365 if (status)366 dev_dbg(&dev->i2c_client->dev, "err %d\n", status);367 368 return status;369}370 371static 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 379static 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(total_len, plocal_buf);399 400 if (mxl692_i2c_write(dev, local_buf,401 (total_len + MXL_EAGLE_I2C_PHEADER_SIZE)) < 0) {402 status = -EREMOTEIO;403 goto err_finish;404 }405err_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 412static 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, 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 + ix, 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, buffer);434 } else {435 status = -EREMOTEIO;436 }437err_finish:438 if (status)439 dev_dbg(&dev->i2c_client->dev, "err %d\n", status);440 return status;441}442 443static 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 *tx_header;455 struct MXL_EAGLE_HOST_MSG_HEADER_T *rx_header;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, tx_buf);478 479 tx_header->checksum = 0;480 tx_header->checksum = mxl692_checksum(tx_buf,481 MXL_EAGLE_HOST_MSG_HEADER_SIZE + tx_payload_size);482#ifdef __LITTLE_ENDIAN483 convert_endian(4, (u8 *)&tx_header->checksum); /* cksum is big endian */484#endif485 /* send Tx message */486 status = mxl692_opwrite(dev, tx_buf,487 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, rx_buf,498 rx_payload_expected + MXL_EAGLE_HOST_MSG_HEADER_SIZE);499 usleep_range(1000, 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(rx_buf,537 MXL_EAGLE_HOST_MSG_HEADER_SIZE + rx_header->payload_size);538#ifdef __LITTLE_ENDIAN539 convert_endian(4, (u8 *)&resp_checksum_tmp); /* cksum is big endian */540#endif541 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(rx_header->opcode, 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 }558err_finish:559 if (status)560 dev_dbg(&dev->i2c_client->dev, "err %d\n", status);561 562 mutex_unlock(&dev->i2c_lock);563 return status;564}565 566static 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, firmware_buf, buf_len);583 if (status)584 goto err_finish;585 586 ix = 16;587 status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* DRAM */588 if (status)589 goto err_finish;590 591 status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* IRAM */592 if (status)593 goto err_finish;594 595 /* release CPU from reset */596 status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32));597 if (status)598 goto err_finish;599 600 mutex_unlock(&dev->i2c_lock);601 602 if (status == 0) {603 /* verify FW is alive */604 usleep_range(MXL_EAGLE_FW_LOAD_TIME * 1000, (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 MXL_EAGLE_OPCODE_DEVICE_STATUS_GET,608 NULL,609 0,610 (u8 *)dev_status,611 sizeof(struct MXL_EAGLE_DEV_STATUS_T));612 }613 614 return status;615err_finish:616 mutex_unlock(&dev->i2c_lock);617 if (status)618 dev_dbg(&dev->i2c_client->dev, "err %d\n", status);619 return status;620}621 622static 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, MXL_EAGLE_OPCODE_DEVICE_VERSION_GET,629 NULL,630 0,631 (u8 *)&dev_ver,632 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 650static 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, 0x80000100, (u8 *)®_val, sizeof(u32));659 if (status)660 goto err_finish;661 662 /* verify sku */663 status = mxl692_memread(dev, 0x70000188, (u8 *)&dev_type, sizeof(u32));664 if (status)665 goto err_finish;666 667 if (dev_type != dev->device_type)668 goto err_finish;669 670err_finish:671 if (status)672 dev_dbg(&dev->i2c_client->dev, "err %d\n", status);673 return status;674}675 676static 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, 0x90000000, (u8 *)®_val, 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, 0x90000000, (u8 *)®_val, sizeof(u32));694 if (status)695 goto err_finish;696 697 /* configure digital regulator to high current mode */698 status = mxl692_memread(dev, 0x90000018, (u8 *)®_val, sizeof(u32));699 if (status)700 goto err_finish;701 702 reg_val |= 0x800;703 704 status = mxl692_memwrite(dev, 0x90000018, (u8 *)®_val, sizeof(u32));705 706err_finish:707 if (status)708 dev_dbg(&dev->i2c_client->dev, "err %d\n", status);709 return status;710}711 712static 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, 0x90000000, (u8 *)®_val, 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, 0x90000000, (u8 *)®_val, 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, 0x90000000, (u8 *)®_val, 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, 0x90000000, (u8 *)®_val, 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, 0x90000030, (u8 *)®_val1, 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, 0x90000030, (u8 *)®_val1, 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, 0x90000030, (u8 *)®_val1, sizeof(u32));767 if (status)768 goto err_finish;769 770 /* set XTAL bias value */771 status = mxl692_memread(dev, 0x9000002c, (u8 *)®_val, sizeof(u32));772 if (status)773 goto err_finish;774 775 reg_val &= 0xC0FFFFFF;776 reg_val |= 0xA000000;777 778 status = mxl692_memwrite(dev, 0x9000002c, (u8 *)®_val, sizeof(u32));779 if (status)780 goto err_finish;781 }782 783 /* start XTAL calibration */784 status = mxl692_memread(dev, 0x70000010, (u8 *)®_val, sizeof(u32));785 if (status)786 goto err_finish;787 788 reg_val |= 0x8;789 790 status = mxl692_memwrite(dev, 0x70000010, (u8 *)®_val, sizeof(u32));791 if (status)792 goto err_finish;793 794 status = mxl692_memread(dev, 0x70000018, (u8 *)®_val, sizeof(u32));795 if (status)796 goto err_finish;797 798 reg_val |= 0x10;799 800 status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32));801 if (status)802 goto err_finish;803 804 status = mxl692_memread(dev, 0x9001014c, (u8 *)®_val, sizeof(u32));805 if (status)806 goto err_finish;807 808 reg_val &= 0xFFFFEFFF;809 810 status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32));811 if (status)812 goto err_finish;813 814 reg_val |= 0x1000;815 816 status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32));817 if (status)818 goto err_finish;819 820 usleep_range(45000, 55000);821 822err_finish:823 if (status)824 dev_dbg(&dev->i2c_client->dev, "err %d\n", status);825 return status;826}827 828static 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 MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET,839 &mode,840 sizeof(u8),841 NULL,842 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 853static 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(50 * 1000, 60 * 1000); /* was 1000! */874 875 status = mxl692_config_regulators(dev, 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, &xtal_config);885 if (status)886 goto err;887 888 status = request_firmware(&firmware, MXL692_FIRMWARE, &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->data, firmware->size);896 if (status)897 goto err_release_firmware;898 899 release_firmware(firmware);900 901 status = mxl692_get_versions(dev);902 if (status)903 goto err;904 905 dev->power_mode = MXL_EAGLE_POWER_MODE_SLEEP;906warm:907 /* Config Device Power Mode */908 if (dev->power_mode != MXL_EAGLE_POWER_MODE_ACTIVE) {909 status = mxl692_powermode(dev, MXL_EAGLE_POWER_MODE_ACTIVE);910 if (status)911 goto err;912 913 usleep_range(50 * 1000, 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;928err_release_firmware:929 release_firmware(firmware);930err:931 dev_dbg(&dev->i2c_client->dev, "err %d\n", status);932 return status;933}934 935static 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, MXL_EAGLE_POWER_MODE_SLEEP);941 942 return 0;943}944 945static 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 MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET,985 &op_param,986 sizeof(u8),987 NULL,988 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(20 * 1000, 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 MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET,1010 (u8 *)&mpeg_params,1011 sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T),1012 NULL,1013 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 MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET,1022 (u8 *)&mpeg_params,1023 sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T),1024 NULL,1025 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 else1035 qam_params.symbol_rate_hz = 5361000;1036 1037 qam_params.symbol_rate_256qam_hz = 5361000;1038 1039 status = mxl692_i2c_writeread(dev,1040 MXL_EAGLE_OPCODE_QAM_PARAMS_SET,1041 (u8 *)&qam_params,1042 sizeof(struct MXL_EAGLE_QAM_DEMOD_PARAMS_T),1043 NULL, 0);1044 if (status)1045 goto err;1046 1047 break;1048 default:1049 break;1050 }1051 1052 usleep_range(20 * 1000, 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 MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET,1063 (u8 *)&tuner_params,1064 sizeof(struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T),1065 NULL,1066 0);1067 if (status)1068 goto err;1069 1070 usleep_range(20 * 1000, 30 * 1000); /* was 500! */1071 1072 switch (demod_type) {1073 case MXL_EAGLE_DEMOD_TYPE_ATSC:1074 status = mxl692_i2c_writeread(dev,1075 MXL_EAGLE_OPCODE_ATSC_INIT_SET,1076 NULL, 0, NULL, 0);1077 if (status)1078 goto err;1079 break;1080 case MXL_EAGLE_DEMOD_TYPE_QAM:1081 status = mxl692_i2c_writeread(dev,1082 MXL_EAGLE_OPCODE_QAM_RESTART_SET,1083 NULL, 0, NULL, 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;1095err:1096 dev_dbg(&dev->i2c_client->dev, "err %d\n", status);1097 return status;1098}1099 1100static 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 1111static 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 MXL_EAGLE_OPCODE_ATSC_STATUS_GET,1132 NULL,1133 0,1134 rx_buf,1135 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 MXL_EAGLE_OPCODE_QAM_STATUS_GET,1145 NULL,1146 0,1147 rx_buf,1148 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 1162static 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 MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET,1180 NULL,1181 0,1182 rx_buf,1183 sizeof(struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T));1184 if (!mxl_status) {1185 if (atsc_errors->error_packets == 0)1186 utmp = 0;1187 else1188 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 1215static 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 MXL_EAGLE_OPCODE_ATSC_STATUS_GET,1236 NULL,1237 0,1238 rx_buf,1239 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 MXL_EAGLE_OPCODE_QAM_STATUS_GET,1254 NULL,1255 0,1256 rx_buf,1257 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 else1287 mxl_status = mxl692_read_ber_ucb(fe);1288 1289 return mxl_status;1290}1291 1292static 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_8VSB1300 },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 1311static 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(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, dev);1330 1331 dev_info(&client->dev, "MaxLinear mxl692 successfully attached\n");1332 1333 return 0;1334err:1335 dev_dbg(&client->dev, "failed %d\n", ret);1336 return -ENODEV;1337}1338 1339static 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(dev);1346}1347 1348static const struct i2c_device_id mxl692_id_table[] = {1349 { "mxl692" },1350 {}1351};1352MODULE_DEVICE_TABLE(i2c, mxl692_id_table);1353 1354static 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 1363module_i2c_driver(mxl692_driver);1364 1365MODULE_AUTHOR("Brad Love <brad@nextdimension.cc>");1366MODULE_DESCRIPTION("MaxLinear MxL692 demodulator/tuner driver");1367MODULE_FIRMWARE(MXL692_FIRMWARE);1368MODULE_LICENSE("GPL");1369