* [PATCH 0/3] MaxLinear mxl692 demod-tuner / Hauppauge usb QuadHD @ 2020-06-12 18:39 Brad Love 2020-06-12 18:39 ` [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver Brad Love ` (3 more replies) 0 siblings, 4 replies; 20+ messages in thread From: Brad Love @ 2020-06-12 18:39 UTC (permalink / raw) To: linux-media; +Cc: Brad Love Attached is a new driver for MaxLinear mxl692 ATSC/QAM combo tuner-demodulator. The driver is used by em28xx based Hauppauge USB QuadHD. The Hauppauge USB QuadHD contains four instances of the mxl692, configured as two "dual tuner" em28xx devices. Bus 003 Device 006: ID 2040:846d Hauppauge Bus 003 Device 005: ID 2040:846d Hauppauge The ATSC portion works fully, the QAM portion needs some TLC, and is therefore not listed in the DVB capabilities. The driver is based off vendor provided GPLv2 source code. Note: there are a number of LONG_LINE warnings. I have left them as is, due to recent developments/statements regarding this topic. I do not feel the driver would be made any clearer by arbitrarily splitting up the remaining lines further. This is just a v1, if there's any comments/questions/concerns let me know and I'll sort them out. Brad Love (3): mxl692: MaxLinear 692 ATSC demod-tuner driver em28xx-core: Fix TS2 active led em28xx: Add support for Hauppauge USB QuadHD drivers/media/dvb-frontends/Kconfig | 9 + drivers/media/dvb-frontends/Makefile | 1 + drivers/media/dvb-frontends/mxl692.c | 1363 +++++++++++++++++++++ drivers/media/dvb-frontends/mxl692.h | 38 + drivers/media/dvb-frontends/mxl692_defs.h | 493 ++++++++ drivers/media/usb/em28xx/Kconfig | 1 + drivers/media/usb/em28xx/em28xx-cards.c | 46 + drivers/media/usb/em28xx/em28xx-core.c | 4 +- drivers/media/usb/em28xx/em28xx-dvb.c | 26 + drivers/media/usb/em28xx/em28xx.h | 1 + 10 files changed, 1981 insertions(+), 1 deletion(-) create mode 100644 drivers/media/dvb-frontends/mxl692.c create mode 100644 drivers/media/dvb-frontends/mxl692.h create mode 100644 drivers/media/dvb-frontends/mxl692_defs.h -- 2.23.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver 2020-06-12 18:39 [PATCH 0/3] MaxLinear mxl692 demod-tuner / Hauppauge usb QuadHD Brad Love @ 2020-06-12 18:39 ` Brad Love 2020-06-12 21:49 ` kernel test robot ` (4 more replies) 2020-06-12 18:39 ` [PATCH 2/3] em28xx-core: Fix TS2 active led Brad Love ` (2 subsequent siblings) 3 siblings, 5 replies; 20+ messages in thread From: Brad Love @ 2020-06-12 18:39 UTC (permalink / raw) To: linux-media; +Cc: Brad Love MaxLinear 692 is a combo demod/tuner which has ATSC and QAM capabilities. Only ATSC is currently advertised via DVB properties. QAM still has issues. Signed-off-by: Brad Love <brad@nextdimension.cc> --- drivers/media/dvb-frontends/Kconfig | 9 + drivers/media/dvb-frontends/Makefile | 1 + drivers/media/dvb-frontends/mxl692.c | 1363 +++++++++++++++++++++ drivers/media/dvb-frontends/mxl692.h | 38 + drivers/media/dvb-frontends/mxl692_defs.h | 493 ++++++++ 5 files changed, 1904 insertions(+) create mode 100644 drivers/media/dvb-frontends/mxl692.c create mode 100644 drivers/media/dvb-frontends/mxl692.h create mode 100644 drivers/media/dvb-frontends/mxl692_defs.h diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 643b851a6b60..b1ded2137f0e 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -708,6 +708,15 @@ config DVB_S5H1411 An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. +config DVB_MXL692 + tristate "MaxLinear MXL692 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + MaxLinear MxL692 is a combo tuner-demodulator that + supports ATSC 8VSB and QAM modes. Say Y when you want to + support this frontend. + comment "ISDB-T (terrestrial) frontends" depends on DVB_CORE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index e9179162658c..b9f47d68e14e 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_DVB_S5H1420) += s5h1420.o obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o obj-$(CONFIG_DVB_LGDT3306A) += lgdt3306a.o +obj-$(CONFIG_DVB_MXL692) += mxl692.o obj-$(CONFIG_DVB_LG2160) += lg2160.o obj-$(CONFIG_DVB_CX24123) += cx24123.o obj-$(CONFIG_DVB_LNBH25) += lnbh25.o diff --git a/drivers/media/dvb-frontends/mxl692.c b/drivers/media/dvb-frontends/mxl692.c new file mode 100644 index 000000000000..622b7a7ebab5 --- /dev/null +++ b/drivers/media/dvb-frontends/mxl692.c @@ -0,0 +1,1363 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the MaxLinear MxL69x family of combo tuners/demods + * + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> + * + * based on code: + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved + * which was released under GPL V2 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/mutex.h> +#include <linux/i2c-mux.h> +#include <linux/string.h> +#include <linux/firmware.h> + +#include "mxl692.h" +#include "mxl692_defs.h" + +static int g_big_endian = -1; + +static const struct dvb_frontend_ops mxl692_ops; + +struct mxl692_dev { + struct dvb_frontend fe; + struct i2c_client *i2c_client; + struct mutex i2c_lock; /* i2c command mutex */ + enum MXL_EAGLE_DEMOD_TYPE_E demod_type; + enum MXL_EAGLE_POWER_MODE_E power_mode; + u32 current_frequency; + int device_type; + int seqnum; + int init_done; +}; + +static int mxl692_i2c_write(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) +{ + int ret = 0; + struct i2c_msg msg = { + .addr = dev->i2c_client->addr, + .flags = 0, + .buf = buffer, + .len = buf_len + }; + + ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1); + if (ret != 1) + dev_err(&dev->i2c_client->dev, "i2c write error!\n"); + + return ret; +} + +static int mxl692_i2c_read(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) +{ + int ret = 0; + struct i2c_msg msg = { + .addr = dev->i2c_client->addr, + .flags = I2C_M_RD, + .buf = buffer, + .len = buf_len + }; + + ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1); + if (ret != 1) + dev_err(&dev->i2c_client->dev, "i2c read error!\n"); + + return ret; +} + +static void detect_endianness(void) +{ + u32 temp = 1; + u8 *p_temp = (u8 *)&temp; + + g_big_endian = (*p_temp == 0) ? 1 : 0; + + if (g_big_endian) + pr_debug("%s( BIG )\n", __func__); + else + pr_debug("%s( SMALL )\n", __func__); +} + +static int convert_endian(u32 size, u8 *d) +{ + u32 i; + + for (i = 0; i < (size & ~3); i += 4) { + d[i + 0] ^= d[i + 3]; + d[i + 3] ^= d[i + 0]; + d[i + 0] ^= d[i + 3]; + + d[i + 1] ^= d[i + 2]; + d[i + 2] ^= d[i + 1]; + d[i + 1] ^= d[i + 2]; + } + + switch (size & 3) { + case 0: + case 1: + /* do nothing */ + break; + case 2: + d[i + 0] ^= d[i + 1]; + d[i + 1] ^= d[i + 0]; + d[i + 0] ^= d[i + 1]; + break; + + case 3: + d[i + 0] ^= d[i + 2]; + d[i + 2] ^= d[i + 0]; + d[i + 0] ^= d[i + 2]; + break; + } + return size; +} + +static int convert_endian_n(int n, u32 size, u8 *d) +{ + int i, count = 0; + + for (i = 0; i < n; i += size) + count += convert_endian(size, d + i); + return count; +} + +static void mxl692_tx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) +{ + if (g_big_endian) + return; + + buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ + + switch (opcode) { + case MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET: + case MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET: + case MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET: + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_PARAMS_SET: + buffer += 5; + buffer += convert_endian(2 * sizeof(u32), buffer); + break; + default: + /* no swapping - all get opcodes */ + /* ATSC/OOB no swapping */ + break; + } +} + +static void mxl692_rx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) +{ + if (g_big_endian) + return; + + buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ + + switch (opcode) { + case MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET: + buffer++; + buffer += convert_endian(2 * sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_ATSC_STATUS_GET: + buffer += convert_endian_n(2, sizeof(u16), buffer); + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET: + buffer += convert_endian(3 * sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET: + buffer += convert_endian_n(24, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_STATUS_GET: + buffer += 8; + buffer += convert_endian_n(2, sizeof(u16), buffer); + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET: + buffer += convert_endian(7 * sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET: + buffer += convert_endian_n(24, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET: + buffer += convert_endian_n(8, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET: + buffer += convert_endian_n(17, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET: + buffer += convert_endian(3 * sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_OOB_STATUS_GET: + buffer += convert_endian_n(2, sizeof(u16), buffer); + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_SMA_RECEIVE_GET: + buffer += convert_endian(sizeof(u32), buffer); + break; + default: + /* no swapping - all set opcodes */ + break; + } +} + +static u32 mxl692_checksum(u8 *buffer, u32 size) +{ + u32 ix, remainder = 0, cur_cksum = 0; + + for (ix = 0; ix < size / 4; ix++) + cur_cksum += cpu_to_be32(*(u32 *)(buffer + + (ix * sizeof(u32)))); + remainder = size % 4; + if (remainder > 0) + cur_cksum += cpu_to_be32(*((u32 *)&buffer[size - remainder])); + + cur_cksum ^= 0xDEADBEEF; + + return be32_to_cpu(cur_cksum); +} + +static int mxl692_validate_fw_header(const u8 *buffer, u32 buf_len) +{ + int status = 0; + u32 ix, temp = 0; + u32 *local_buf = NULL; + + if (buffer[0] != 0x4D || buffer[1] != 0x31 || + buffer[2] != 0x10 || buffer[3] != 0x02 || + buffer[4] != 0x40 || buffer[5] != 0x00 || + buffer[6] != 0x00 || buffer[7] != 0x80) { + status = -EINVAL; + goto err_finish; + } + + local_buf = (u32 *)(buffer + 8); + temp = cpu_to_be32(*(u32 *)local_buf); + + if ((buf_len - 16) != (temp >> 8)) { + status = -EINVAL; + goto err_finish; + } + + temp = 0; + for (ix = 16; ix < buf_len; ix++) + temp += buffer[ix]; + + if ((u8)temp != buffer[11]) + status = -EINVAL; +err_finish: + if (status) + pr_err("%s failed! %d\n", __func__, status); + return status; +} + +static int mxl692_write_fw_block(struct mxl692_dev *dev, const u8 *buffer, + u32 buf_len, u32 *index) +{ + int status = 0; + u32 ix = 0, total_len = 0, addr = 0, chunk_len = 0, prevchunk_len = 0; + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; + int payload_max = MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE; + + ix = *index; + + if (buffer[ix] == 0x53) { + total_len = buffer[ix + 1] << 16 | buffer[ix + 2] << 8 | buffer[ix + 3]; + total_len = (total_len + 3) & ~3; + addr = buffer[ix + 4] << 24 | buffer[ix + 5] << 16 | + buffer[ix + 6] << 8 | buffer[ix + 7]; + ix += MXL_EAGLE_FW_SEGMENT_HEADER_SIZE; + + while ((total_len > 0) && (status == 0)) { + plocal_buf = local_buf; + chunk_len = (total_len < payload_max) ? + total_len : payload_max; + + *plocal_buf++ = 0xFC; + *plocal_buf++ = chunk_len + sizeof(u32); + + *(u32 *)plocal_buf = cpu_to_le32(addr + prevchunk_len); + plocal_buf += sizeof(u32); + + memcpy(plocal_buf, &buffer[ix], chunk_len); + convert_endian(chunk_len, plocal_buf); + + if (mxl692_i2c_write(dev, local_buf, + (chunk_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { + status = -EREMOTEIO; + break; + } + + prevchunk_len += chunk_len; + total_len -= chunk_len; + ix += chunk_len; + } + *index = ix; + } else { + status = -EINVAL; + } + + if (status) + dev_err(&dev->i2c_client->dev, "err %d\n", status); + + return status; +} + +static int mxl692_memwrite(struct mxl692_dev *dev, u32 addr, + u8 *buffer, u32 size) +{ + int status = 0, total_len = 0; + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; + + total_len = size; + total_len = (total_len + 3) & ~3; /* 4 byte alignment */ + + if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE)) + dev_dbg(&dev->i2c_client->dev, "hrmph?\n"); + + plocal_buf = local_buf; + + *plocal_buf++ = 0xFC; + *plocal_buf++ = total_len + sizeof(u32); + + *(u32 *)plocal_buf = addr; + plocal_buf += sizeof(u32); + + memcpy(plocal_buf, buffer, total_len); + if (g_big_endian) + convert_endian(sizeof(u32) + total_len, local_buf + 2); + + if (mxl692_i2c_write(dev, local_buf, + (total_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { + status = -EREMOTEIO; + goto err_finish; + } + + return status; +err_finish: + dev_err(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_memread(struct mxl692_dev *dev, u32 addr, + u8 *buffer, u32 size) +{ + int status = 0; + u8 local_buf[MXL_EAGLE_I2C_MHEADER_SIZE] = {}, *plocal_buf = NULL; + + plocal_buf = local_buf; + + *plocal_buf++ = 0xFB; + *plocal_buf++ = sizeof(u32); + *(u32 *)plocal_buf = addr; + + if (g_big_endian) + convert_endian(sizeof(u32), plocal_buf); + + if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_MHEADER_SIZE) > 0) { + size = (size + 3) & ~3; /* 4 byte alignment */ + status = mxl692_i2c_read(dev, buffer, (u16)size) < 0 ? + -EREMOTEIO : 0; + + if (status == 0 && g_big_endian) + convert_endian(size, buffer); + } else { + status = -EREMOTEIO; + } + + if (status) + dev_err(&dev->i2c_client->dev, "err %d\n", status); + + return status; +} + +static int mxl692_opwrite(struct mxl692_dev *dev, u8 *buffer, + u32 size) +{ + int status = 0, total_len = 0; + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; + + total_len = size; + total_len = (total_len + 3) & ~3; /* 4 byte alignment */ + + if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) + dev_dbg(&dev->i2c_client->dev, "hrmph?\n"); + + plocal_buf = local_buf; + + *plocal_buf++ = 0xFE; + *plocal_buf++ = (u8)total_len; + + memcpy(plocal_buf, buffer, total_len); + convert_endian(total_len, plocal_buf); + + if (mxl692_i2c_write(dev, local_buf, + (total_len + MXL_EAGLE_I2C_PHEADER_SIZE)) < 0) { + status = -EREMOTEIO; + goto err_finish; + } +err_finish: + if (status) + dev_err(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_opread(struct mxl692_dev *dev, u8 *buffer, + u32 size) +{ + int status = 0; + u32 ix = 0; + u8 local_buf[MXL_EAGLE_I2C_PHEADER_SIZE] = {}; + + local_buf[0] = 0xFD; + local_buf[1] = 0; + + if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_PHEADER_SIZE) > 0) { + size = (size + 3) & ~3; /* 4 byte alignment */ + + //read in 4 byte chunks + for (ix = 0; ix < size; ix += 4) { + if (mxl692_i2c_read(dev, buffer + ix, 4) < 0) { + dev_dbg(&dev->i2c_client->dev, + "ix=%d size=%d\n", ix, size); + status = -EREMOTEIO; + goto err_finish; + } + } + convert_endian(size, buffer); + } else { + status = -EREMOTEIO; + } +err_finish: + if (status) + dev_err(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_i2c_writeread(struct mxl692_dev *dev, + u8 opcode, + u8 *tx_payload, + u8 tx_payload_size, + u8 *rx_payload, + u8 rx_payload_expected) +{ + int status = 0, timeout = 40; + u8 tx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + u32 resp_checksum = 0, resp_checksum_tmp = 0; + struct MXL_EAGLE_HOST_MSG_HEADER_T *tx_header; + struct MXL_EAGLE_HOST_MSG_HEADER_T *rx_header; + + mutex_lock(&dev->i2c_lock); + + if ((tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE) > + (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) { + status = -EINVAL; + goto err_finish; + } + + tx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)tx_buf; + tx_header->checksum = 0; + tx_header->opcode = opcode; + tx_header->payload_size = tx_payload_size; + tx_header->seqnum = dev->seqnum++; + + if (dev->seqnum == 0) + dev->seqnum = 1; + + if (tx_payload && tx_payload_size > 0) + memcpy(&tx_buf[MXL_EAGLE_HOST_MSG_HEADER_SIZE], + tx_payload, tx_payload_size); + + mxl692_tx_swap(opcode, tx_buf); + + tx_header->checksum = 0; + tx_header->checksum = mxl692_checksum(tx_buf, + MXL_EAGLE_HOST_MSG_HEADER_SIZE + tx_payload_size); + + /* send Tx message */ + status = mxl692_opwrite(dev, tx_buf, + tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE); + if (status) { + status = -EREMOTEIO; + goto err_finish; + } + + /* receive Rx message (polling) */ + rx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)rx_buf; + + do { + status = mxl692_opread(dev, rx_buf, + rx_payload_expected + MXL_EAGLE_HOST_MSG_HEADER_SIZE); + usleep_range(1000, 2000); + timeout--; + } while ((timeout > 0) && (status == 0) && + (rx_header->seqnum == 0) && + (rx_header->checksum == 0)); + + if (timeout == 0 || status) { + dev_dbg(&dev->i2c_client->dev, "timeout=%d status=%d\n", + timeout, status); + status = -ETIMEDOUT; + goto err_finish; + } + + if (rx_header->status) { + status = (int)rx_header->status; + goto err_finish; + } + + if (rx_header->seqnum != tx_header->seqnum || + rx_header->opcode != tx_header->opcode || + rx_header->payload_size != rx_payload_expected) { + dev_dbg(&dev->i2c_client->dev, "Something failed seq=%s opcode=%s pSize=%s\n", + rx_header->seqnum != tx_header->seqnum ? "X" : "0", + rx_header->opcode != tx_header->opcode ? "X" : "0", + rx_header->payload_size != rx_payload_expected ? "X" : "0"); + if (rx_header->payload_size != rx_payload_expected) + dev_dbg(&dev->i2c_client->dev, "rx_header->payloadSize=%d rx_payload_expected=%d\n", + rx_header->payload_size, rx_payload_expected); + status = -EREMOTEIO; + goto err_finish; + } + + resp_checksum = rx_header->checksum; + rx_header->checksum = 0; + resp_checksum_tmp = mxl692_checksum(rx_buf, + MXL_EAGLE_HOST_MSG_HEADER_SIZE + rx_header->payload_size); + + if (resp_checksum != resp_checksum_tmp) { + status = -EREMOTEIO; + goto err_finish; + } + + mxl692_rx_swap(rx_header->opcode, rx_buf); + + if (rx_header->payload_size > 0) { + if (!rx_payload) { + status = -EREMOTEIO; + goto err_finish; + } + memcpy(rx_payload, rx_buf + MXL_EAGLE_HOST_MSG_HEADER_SIZE, + rx_header->payload_size); + } +err_finish: + if (status) + dev_err(&dev->i2c_client->dev, "err %d\n", status); + + mutex_unlock(&dev->i2c_lock); + return status; +} + +static int mxl692_fwdownload(struct mxl692_dev *dev, + const u8 *firmware_buf, u32 buf_len) +{ + int status = 0; + u32 ix, reg_val = 0x1; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_DEV_STATUS_T *dev_status; + + if (buf_len < MXL_EAGLE_FW_HEADER_SIZE || + buf_len > MXL_EAGLE_FW_MAX_SIZE_IN_KB * 1000) + return -EINVAL; + + mutex_lock(&dev->i2c_lock); + + dev_dbg(&dev->i2c_client->dev, "\n"); + + status = mxl692_validate_fw_header(firmware_buf, buf_len); + if (status) + goto err_finish; + + ix = 16; + status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* DRAM */ + if (status) + goto err_finish; + + status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* IRAM */ + if (status) + goto err_finish; + + /* release CPU from reset */ + status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + mutex_unlock(&dev->i2c_lock); + + if (status == 0) { + /* verify FW is alive */ + usleep_range(MXL_EAGLE_FW_LOAD_TIME * 1000, (MXL_EAGLE_FW_LOAD_TIME + 5) * 1000); + dev_status = (struct MXL_EAGLE_DEV_STATUS_T *)&rx_buf; + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_STATUS_GET, + NULL, + 0, + (u8 *)dev_status, + sizeof(struct MXL_EAGLE_DEV_STATUS_T)); + } + + return status; +err_finish: + mutex_unlock(&dev->i2c_lock); + if (status) + dev_err(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_get_versions(struct mxl692_dev *dev) +{ + int status = 0; + struct MXL_EAGLE_DEV_VER_T dev_ver = {}; + static const char * const chip_id[] = {"N/A", "691", "248", "692"}; + + status = mxl692_i2c_writeread(dev, MXL_EAGLE_OPCODE_DEVICE_VERSION_GET, + NULL, + 0, + (u8 *)&dev_ver, + sizeof(struct MXL_EAGLE_DEV_VER_T)); + if (status) + return status; + + dev_info(&dev->i2c_client->dev, "MxL692_DEMOD Chip ID: %s\n", + chip_id[dev_ver.chip_id]); + + dev_info(&dev->i2c_client->dev, + "MxL692_DEMOD FW Version: %d.%d.%d.%d_RC%d\n", + dev_ver.firmware_ver[0], + dev_ver.firmware_ver[1], + dev_ver.firmware_ver[2], + dev_ver.firmware_ver[3], + dev_ver.firmware_ver[4]); + + return status; +} + +static int mxl692_reset(struct mxl692_dev *dev) +{ + int status = 0; + u32 dev_type = MXL_EAGLE_DEVICE_MAX, reg_val = 0x2; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + /* legacy i2c override */ + status = mxl692_memwrite(dev, 0x80000100, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* verify sku */ + status = mxl692_memread(dev, 0x70000188, (u8 *)&dev_type, sizeof(u32)); + if (status) + goto err_finish; + + if (dev_type != dev->device_type) + goto err_finish; + +err_finish: + if (status) + dev_err(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_config_regulators(struct mxl692_dev *dev, + enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E power_supply) +{ + int status = 0; + u32 reg_val; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + /* configure main regulator according to the power supply source */ + status = mxl692_memread(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val &= 0x00FFFFFF; + reg_val |= (power_supply == MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE) ? + 0x14000000 : 0x10000000; + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* configure digital regulator to high current mode */ + status = mxl692_memread(dev, 0x90000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x800; + + status = mxl692_memwrite(dev, 0x90000018, (u8 *)®_val, sizeof(u32)); + +err_finish: + if (status) + dev_err(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_config_xtal(struct mxl692_dev *dev, + struct MXL_EAGLE_DEV_XTAL_T *dev_xtal) +{ + int status = 0; + u32 reg_val, reg_val1; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + status = mxl692_memread(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* set XTAL capacitance */ + reg_val &= 0xFFFFFFE0; + reg_val |= dev_xtal->xtal_cap; + + /* set CLK OUT */ + reg_val = dev_xtal->clk_out_enable ? + (reg_val | 0x0100) : (reg_val & 0xFFFFFEFF); + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* set CLK OUT divider */ + reg_val = dev_xtal->clk_out_div_enable ? + (reg_val | 0x0200) : (reg_val & 0xFFFFFDFF); + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* set XTAL sharing */ + reg_val = dev_xtal->xtal_sharing_enable ? + (reg_val | 0x010400) : (reg_val & 0xFFFEFBFF); + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* enable/disable XTAL calibration, based on master/slave device */ + status = mxl692_memread(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); + if (status) + goto err_finish; + + if (dev_xtal->xtal_calibration_enable) { + /* enable XTAL calibration and set XTAL amplitude to a higher value */ + reg_val1 &= 0xFFFFFFFD; + reg_val1 |= 0x30; + + status = mxl692_memwrite(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); + if (status) + goto err_finish; + } else { + /* disable XTAL calibration */ + reg_val1 |= 0x2; + + status = mxl692_memwrite(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); + if (status) + goto err_finish; + + /* set XTAL bias value */ + status = mxl692_memread(dev, 0x9000002c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val &= 0xC0FFFFFF; + reg_val |= 0xA000000; + + status = mxl692_memwrite(dev, 0x9000002c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + } + + /* start XTAL calibration */ + status = mxl692_memread(dev, 0x70000010, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x8; + + status = mxl692_memwrite(dev, 0x70000010, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + status = mxl692_memread(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x10; + + status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + status = mxl692_memread(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val &= 0xFFFFEFFF; + + status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x1000; + + status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + usleep_range(45000, 55000); + +err_finish: + if (status) + dev_err(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_powermode(struct mxl692_dev *dev, + enum MXL_EAGLE_POWER_MODE_E power_mode) +{ + int status = 0; + + dev_dbg(&dev->i2c_client->dev, "%s\n", + power_mode == MXL_EAGLE_POWER_MODE_SLEEP ? + "sleep" : "active"); + + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET, + (u8 *)&power_mode, + sizeof(u8), + NULL, + 0); + if (status) + dev_err(&dev->i2c_client->dev, "err %d\n", status); + + dev->power_mode = power_mode; + + return status; +} + +static int mxl692_init(struct dvb_frontend *fe) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->i2c_client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int status = 0; + const struct firmware *firmware; + struct MXL_EAGLE_DEV_XTAL_T xtal_config = {}; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + if (dev->init_done) + goto warm; + + dev->seqnum = 1; + + status = mxl692_reset(dev); + if (status) + goto err; + + usleep_range(50 * 1000, 60 * 1000); /* was 1000! */ + + status = mxl692_config_regulators(dev, MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL); + if (status) + goto err; + + xtal_config.xtal_cap = 26; + xtal_config.clk_out_div_enable = 0; + xtal_config.clk_out_enable = 0; + xtal_config.xtal_calibration_enable = 0; + xtal_config.xtal_sharing_enable = 1; + status = mxl692_config_xtal(dev, &xtal_config); + if (status) + goto err; + + status = request_firmware(&firmware, MXL692_FIRMWARE, &client->dev); + if (status) { + dev_dbg(&dev->i2c_client->dev, "firmware missing? %s\n", + MXL692_FIRMWARE); + goto err; + } + + status = mxl692_fwdownload(dev, firmware->data, firmware->size); + if (status) + goto err_release_firmware; + + release_firmware(firmware); + +// usleep_range(500 * 1000, 510 * 1000); /* was 1000! */ + status = mxl692_get_versions(dev); + if (status) + goto err; + + dev->power_mode = MXL_EAGLE_POWER_MODE_SLEEP; + +warm: + /* Init stats here to indicate which stats are supported */ + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + dev->init_done = 1; + return 0; +err_release_firmware: + release_firmware(firmware); +err: + return status; +} + +static int mxl692_sleep(struct dvb_frontend *fe) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + + if (dev->power_mode != MXL_EAGLE_POWER_MODE_SLEEP) + mxl692_powermode(dev, MXL_EAGLE_POWER_MODE_SLEEP); + + return 0; +} + +static int mxl692_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct mxl692_dev *dev = fe->demodulator_priv; + + int status = 0; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type; + enum MXL_EAGLE_POWER_MODE_E power_mode = MXL_EAGLE_POWER_MODE_ACTIVE; + struct MXL_EAGLE_MPEGOUT_PARAMS_T mpeg_params = {}; + enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E qam_annex = MXL_EAGLE_QAM_DEMOD_ANNEX_B; + struct MXL_EAGLE_QAM_DEMOD_PARAMS_T qam_params = {}; + struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T tuner_params = {}; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + switch (p->modulation) { + case VSB_8: + demod_type = MXL_EAGLE_DEMOD_TYPE_ATSC; + break; + case QAM_AUTO: + case QAM_64: + case QAM_128: + case QAM_256: + demod_type = MXL_EAGLE_DEMOD_TYPE_QAM; + break; + default: + return -EINVAL; + } + + if (dev->current_frequency == p->frequency && dev->demod_type == demod_type) + return 0; + + dev->current_frequency = -1; + dev->demod_type = -1; + + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET, + (u8 *)&demod_type, + sizeof(u8), + NULL, + 0); + if (status) { + dev_dbg(&dev->i2c_client->dev, "DEVICE_DEMODULATOR_TYPE_SET...FAIL %d\n", status); + goto err; + } + + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ + + //Config Device Power Mode + if (dev->power_mode != MXL_EAGLE_POWER_MODE_ACTIVE) { + status = mxl692_powermode(dev, power_mode); + if (status) + goto err; + + usleep_range(50 * 1000, 60 * 1000); /* was 500! */ + } + + mpeg_params.mpeg_parallel = 0; + mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_MSB_1ST; + mpeg_params.mpeg_sync_pulse_width = MXL_EAGLE_DATA_SYNC_WIDTH_BIT; + mpeg_params.mpeg_valid_pol = MXL_EAGLE_CLOCK_POSITIVE; + mpeg_params.mpeg_sync_pol = MXL_EAGLE_CLOCK_POSITIVE; + mpeg_params.mpeg_clk_pol = MXL_EAGLE_CLOCK_NEGATIVE; + mpeg_params.mpeg3wire_mode_enable = 0; + mpeg_params.mpeg_clk_freq = MXL_EAGLE_MPEG_CLOCK_27MHZ; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, + (u8 *)&mpeg_params, + sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), + NULL, + 0); + if (status) + goto err; + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + if (qam_annex == MXL_EAGLE_QAM_DEMOD_ANNEX_A) + mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_LSB_1ST; + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, + (u8 *)&mpeg_params, + sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), + NULL, + 0); + if (status) + goto err; + + qam_params.annex_type = qam_annex; + qam_params.qam_type = MXL_EAGLE_QAM_DEMOD_AUTO; + qam_params.iq_flip = MXL_EAGLE_DEMOD_IQ_AUTO; + if (p->modulation == QAM_64) + qam_params.symbol_rate_hz = 5057000; + else + qam_params.symbol_rate_hz = 5361000; + + qam_params.symbol_rate_256qam_hz = 5361000; + + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_PARAMS_SET, + (u8 *)&qam_params, + sizeof(struct MXL_EAGLE_QAM_DEMOD_PARAMS_T), + NULL, 0); + if (status) + goto err; + + break; + default: + break; + } + + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ + + tuner_params.freq_hz = p->frequency; + tuner_params.bandwidth = MXL_EAGLE_TUNER_BW_6MHZ; + tuner_params.tune_mode = MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW; + + dev_dbg(&dev->i2c_client->dev, " Tuning Freq: %d %s\n", tuner_params.freq_hz, + demod_type == MXL_EAGLE_DEMOD_TYPE_ATSC ? "ATSC" : "QAM"); + + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET, + (u8 *)&tuner_params, + sizeof(struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T), + NULL, + 0); + if (status) + goto err; + + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_INIT_SET, + NULL, 0, NULL, 0); + if (status) + goto err; + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_RESTART_SET, + NULL, 0, NULL, 0); + if (status) + goto err; + break; + default: + break; + } + + dev->demod_type = demod_type; + dev->current_frequency = p->frequency; +err: + return 0; +} + +static int mxl692_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + p->modulation = c->modulation; + p->frequency = c->frequency; + + return 0; +} + +static int mxl692_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; + struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; + int mxl_status = 0; + + *snr = 0; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; + qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); + if (!mxl_status) { + *snr = (u16)(atsc_status->snr_db_tenths / 10); + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = *snr; + } + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); + if (!mxl_status) + *snr = (u16)(qam_status->snr_db_tenths / 10); + break; + case MXL_EAGLE_DEMOD_TYPE_OOB: + default: + break; + } + return 0; +} + +static int mxl692_read_ber_ucb(struct dvb_frontend *fe) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T *qam_errors; + struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *atsc_errors; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; + int mxl_status = 0; + u32 utmp; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + qam_errors = (struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T *)&rx_buf; + atsc_errors = (struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *)&rx_buf; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T)); + if (!mxl_status) { + if (atsc_errors->error_packets == 0) + utmp = 0; + else + utmp = ((atsc_errors->error_bytes / atsc_errors->error_packets) * + atsc_errors->total_packets); + /* ber */ + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += atsc_errors->error_bytes; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += utmp; + /* ucb */ + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += atsc_errors->error_packets; + + dev_dbg(&dev->i2c_client->dev, "%llu %llu\n", + c->post_bit_count.stat[0].uvalue, c->block_error.stat[0].uvalue); + } + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + case MXL_EAGLE_DEMOD_TYPE_OOB: + default: + break; + } + return 0; +} + +static int mxl692_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; + struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; + int mxl_status = 0; + *status = 0; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; + qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); + if (!mxl_status && atsc_status->atsc_lock) { + *status |= FE_HAS_SIGNAL; + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_LOCK; + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = atsc_status->snr_db_tenths / 10; + } + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); + if (!mxl_status && qam_status->qam_locked) { + *status |= FE_HAS_SIGNAL; + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_LOCK; + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = qam_status->snr_db_tenths / 10; + } + break; + case MXL_EAGLE_DEMOD_TYPE_OOB: + default: + break; + } + + if ((*status & FE_HAS_LOCK) == 0) { + /* No lock, reset all statistics */ + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return 0; + } + + if (mxl_status) + dev_err(&dev->i2c_client->dev, "err %d\n", mxl_status); + else + mxl_status = mxl692_read_ber_ucb(fe); + + return mxl_status; +} + +static const struct dvb_frontend_ops mxl692_ops = { + .delsys = { SYS_ATSC }, + .info = { + .name = "MaxLinear MxL692 VSB tuner-demodulator", + .frequency_min_hz = 54000000, + .frequency_max_hz = 858000000, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_8VSB + }, + + .init = mxl692_init, + .sleep = mxl692_sleep, + .set_frontend = mxl692_set_frontend, + .get_frontend = mxl692_get_frontend, + + .read_status = mxl692_read_status, + .read_snr = mxl692_read_snr, +}; + +static int mxl692_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mxl692_config *config = client->dev.platform_data; + struct mxl692_dev *dev; + int ret = 0; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + dev_err(&client->dev, "kzalloc() failed\n"); + goto err; + } + + memcpy(&dev->fe.ops, &mxl692_ops, sizeof(struct dvb_frontend_ops)); + dev->fe.demodulator_priv = dev; + dev->i2c_client = client; + *config->fe = &dev->fe; + mutex_init(&dev->i2c_lock); + i2c_set_clientdata(client, dev); + + dev_info(&client->dev, "MaxLinear mxl692 successfully attached\n"); + detect_endianness(); + + return 0; +err: + dev_err(&client->dev, "failed %d\n", ret); + return -ENODEV; +} + +static int mxl692_remove(struct i2c_client *client) +{ + struct mxl692_dev *dev = i2c_get_clientdata(client); + + dev->fe.demodulator_priv = NULL; + i2c_set_clientdata(client, NULL); + kfree(dev); + + return 0; +} + +static const struct i2c_device_id mxl692_id_table[] = { + {"mxl692", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mxl692_id_table); + +static struct i2c_driver mxl692_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "mxl692", + }, + .probe = mxl692_probe, + .remove = mxl692_remove, + .id_table = mxl692_id_table, +}; + +module_i2c_driver(mxl692_driver); + +MODULE_AUTHOR("Brad Love <brad@nextdimension.cc>"); +MODULE_DESCRIPTION("MaxLinear MxL692 demodulator/tuner driver"); +MODULE_FIRMWARE(MXL692_FIRMWARE); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/mxl692.h b/drivers/media/dvb-frontends/mxl692.h new file mode 100644 index 000000000000..45bc48f1f12f --- /dev/null +++ b/drivers/media/dvb-frontends/mxl692.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver for the MaxLinear MxL69x family of tuners/demods + * + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> + * + * based on code: + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved + * which was released under GPL V2 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MXL692_H_ +#define _MXL692_H_ + +#include <media/dvb_frontend.h> + +#define MXL692_FIRMWARE "dvb-demod-mxl692.fw" + +struct mxl692_config { + unsigned char id; + u8 i2c_addr; + /* + * frontend + * returned by driver + */ + struct dvb_frontend **fe; +}; + +#endif /* _MXL692_H_ */ diff --git a/drivers/media/dvb-frontends/mxl692_defs.h b/drivers/media/dvb-frontends/mxl692_defs.h new file mode 100644 index 000000000000..97fd18ae81ff --- /dev/null +++ b/drivers/media/dvb-frontends/mxl692_defs.h @@ -0,0 +1,493 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver for the MaxLinear MxL69x family of combo tuners/demods + * + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> + * + * based on code: + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved + * which was released under GPL V2 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +//#define __packed __attribute__((packed)) + +/***************************************************************************** + * Defines + ***************************************************************************** + */ +#define MXL_EAGLE_HOST_MSG_HEADER_SIZE 8 +#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76 +#define MXL_EAGLE_QAM_FFE_TAPS_LENGTH 16 +#define MXL_EAGLE_QAM_SPUR_TAPS_LENGTH 32 +#define MXL_EAGLE_QAM_DFE_TAPS_LENGTH 72 +#define MXL_EAGLE_ATSC_FFE_TAPS_LENGTH 4096 +#define MXL_EAGLE_ATSC_DFE_TAPS_LENGTH 384 +#define MXL_EAGLE_VERSION_SIZE 5 /* A.B.C.D-RCx */ +#define MXL_EAGLE_FW_LOAD_TIME 50 + +#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76 +#define MXL_EAGLE_FW_HEADER_SIZE 16 +#define MXL_EAGLE_FW_SEGMENT_HEADER_SIZE 8 +#define MXL_EAGLE_MAX_I2C_PACKET_SIZE 58 +#define MXL_EAGLE_I2C_MHEADER_SIZE 6 +#define MXL_EAGLE_I2C_PHEADER_SIZE 2 + +/* Enum of Eagle family devices */ +enum MXL_EAGLE_DEVICE_E { + MXL_EAGLE_DEVICE_691 = 1, /* Device Mxl691 */ + MXL_EAGLE_DEVICE_248 = 2, /* Device Mxl248 */ + MXL_EAGLE_DEVICE_692 = 3, /* Device Mxl692 */ + MXL_EAGLE_DEVICE_MAX, /* No such device */ +}; + +#define VER_A 1 +#define VER_B 1 +#define VER_C 1 +#define VER_D 3 +#define VER_E 6 + +/* Enum of Host to Eagle I2C protocol opcodes */ +enum MXL_EAGLE_OPCODE_E { + /* DEVICE */ + MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET, + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, + MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET, + MXL_EAGLE_OPCODE_DEVICE_GPIO_DIRECTION_SET, + MXL_EAGLE_OPCODE_DEVICE_GPO_LEVEL_SET, + MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET, + MXL_EAGLE_OPCODE_DEVICE_IO_MUX_SET, + MXL_EAGLE_OPCODE_DEVICE_VERSION_GET, + MXL_EAGLE_OPCODE_DEVICE_STATUS_GET, + MXL_EAGLE_OPCODE_DEVICE_GPI_LEVEL_GET, + + /* TUNER */ + MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET, + MXL_EAGLE_OPCODE_TUNER_LOCK_STATUS_GET, + MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET, + + /* ATSC */ + MXL_EAGLE_OPCODE_ATSC_INIT_SET, + MXL_EAGLE_OPCODE_ATSC_ACQUIRE_CARRIER_SET, + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, + MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, + MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_DFE_TAPS_GET, + MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET, + + /* QAM */ + MXL_EAGLE_OPCODE_QAM_PARAMS_SET, + MXL_EAGLE_OPCODE_QAM_RESTART_SET, + MXL_EAGLE_OPCODE_QAM_STATUS_GET, + MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET, + MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_TAPS_NUMBER_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET, + + /* OOB */ + MXL_EAGLE_OPCODE_OOB_PARAMS_SET, + MXL_EAGLE_OPCODE_OOB_RESTART_SET, + MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET, + MXL_EAGLE_OPCODE_OOB_STATUS_GET, + + /* SMA */ + MXL_EAGLE_OPCODE_SMA_INIT_SET, + MXL_EAGLE_OPCODE_SMA_PARAMS_SET, + MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET, + MXL_EAGLE_OPCODE_SMA_RECEIVE_GET, + + /* DEBUG */ + MXL_EAGLE_OPCODE_INTERNAL, + + MXL_EAGLE_OPCODE_MAX = 70, +}; + +/* Enum of Callabck function types */ +enum MXL_EAGLE_CB_TYPE_E { + MXL_EAGLE_CB_FW_DOWNLOAD = 0, +}; + +/* Enum of power supply types */ +enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E { + MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE, /* Single supply of 3.3V */ + MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL, /* Dual supply, 1.8V & 3.3V */ +}; + +/* Enum of I/O pad drive modes */ +enum MXL_EAGLE_IO_MUX_DRIVE_MODE_E { + MXL_EAGLE_IO_MUX_DRIVE_MODE_1X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_2X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_3X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_4X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_5X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_6X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_7X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_8X, +}; + +/* Enum of demodulator types. Used for selection of demodulator + * type in relevant devices, e.g. ATSC vs. QAM in Mxl691 + */ +enum MXL_EAGLE_DEMOD_TYPE_E { + MXL_EAGLE_DEMOD_TYPE_QAM, /* Mxl248 or Mxl692 */ + MXL_EAGLE_DEMOD_TYPE_OOB, /* Mxl248 only */ + MXL_EAGLE_DEMOD_TYPE_ATSC /* Mxl691 or Mxl692 */ +}; + +/* Enum of power modes. Used for initial + * activation, or for activating sleep mode + */ +enum MXL_EAGLE_POWER_MODE_E { + MXL_EAGLE_POWER_MODE_SLEEP, + MXL_EAGLE_POWER_MODE_ACTIVE +}; + +/* Enum of GPIOs, used in device GPIO APIs */ +enum MXL_EAGLE_GPIO_NUMBER_E { + MXL_EAGLE_GPIO_NUMBER_0, + MXL_EAGLE_GPIO_NUMBER_1, + MXL_EAGLE_GPIO_NUMBER_2, + MXL_EAGLE_GPIO_NUMBER_3, + MXL_EAGLE_GPIO_NUMBER_4, + MXL_EAGLE_GPIO_NUMBER_5, + MXL_EAGLE_GPIO_NUMBER_6 +}; + +/* Enum of GPIO directions, used in GPIO direction configuration API */ +enum MXL_EAGLE_GPIO_DIRECTION_E { + MXL_EAGLE_GPIO_DIRECTION_INPUT, + MXL_EAGLE_GPIO_DIRECTION_OUTPUT +}; + +/* Enum of GPIO level, used in device GPIO APIs */ +enum MXL_EAGLE_GPIO_LEVEL_E { + MXL_EAGLE_GPIO_LEVEL_LOW, + MXL_EAGLE_GPIO_LEVEL_HIGH, +}; + +/* Enum of I/O Mux function, used in device I/O mux configuration API */ +enum MXL_EAGLE_IOMUX_FUNCTION_E { + MXL_EAGLE_IOMUX_FUNC_FEC_LOCK, + MXL_EAGLE_IOMUX_FUNC_MERR, +}; + +/* Enum of MPEG Data format, used in MPEG and OOB output configuration */ +enum MXL_EAGLE_MPEG_DATA_FORMAT_E { + MXL_EAGLE_DATA_SERIAL_LSB_1ST = 0, + MXL_EAGLE_DATA_SERIAL_MSB_1ST, + + MXL_EAGLE_DATA_SYNC_WIDTH_BIT = 0, + MXL_EAGLE_DATA_SYNC_WIDTH_BYTE +}; + +/* Enum of MPEG Clock format, used in MPEG and OOB output configuration */ +enum MXL_EAGLE_MPEG_CLOCK_FORMAT_E { + MXL_EAGLE_CLOCK_ACTIVE_HIGH = 0, + MXL_EAGLE_CLOCK_ACTIVE_LOW, + + MXL_EAGLE_CLOCK_POSITIVE = 0, + MXL_EAGLE_CLOCK_NEGATIVE, + + MXL_EAGLE_CLOCK_IN_PHASE = 0, + MXL_EAGLE_CLOCK_INVERTED, +}; + +/* Enum of MPEG Clock speeds, used in MPEG output configuration */ +enum MXL_EAGLE_MPEG_CLOCK_RATE_E { + MXL_EAGLE_MPEG_CLOCK_54MHZ, + MXL_EAGLE_MPEG_CLOCK_40_5MHZ, + MXL_EAGLE_MPEG_CLOCK_27MHZ, + MXL_EAGLE_MPEG_CLOCK_13_5MHZ, +}; + +/* Enum of Interrupt mask bit, used in host interrupt configuration */ +enum MXL_EAGLE_INTR_MASK_BITS_E { + MXL_EAGLE_INTR_MASK_DEMOD = 0, + MXL_EAGLE_INTR_MASK_SMA_RX = 1, + MXL_EAGLE_INTR_MASK_WDOG = 31 +}; + +/* Enum of QAM Demodulator type, used in QAM configuration */ +enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E { + MXL_EAGLE_QAM_DEMOD_ANNEX_B, /* J.83B */ + MXL_EAGLE_QAM_DEMOD_ANNEX_A, /* DVB-C */ +}; + +/* Enum of QAM Demodulator modulation, used in QAM configuration and status */ +enum MXL_EAGLE_QAM_DEMOD_QAM_TYPE_E { + MXL_EAGLE_QAM_DEMOD_QAM16, + MXL_EAGLE_QAM_DEMOD_QAM64, + MXL_EAGLE_QAM_DEMOD_QAM256, + MXL_EAGLE_QAM_DEMOD_QAM1024, + MXL_EAGLE_QAM_DEMOD_QAM32, + MXL_EAGLE_QAM_DEMOD_QAM128, + MXL_EAGLE_QAM_DEMOD_QPSK, + MXL_EAGLE_QAM_DEMOD_AUTO, +}; + +/* Enum of Demodulator IQ setup, used in QAM, OOB configuration and status */ +enum MXL_EAGLE_IQ_FLIP_E { + MXL_EAGLE_DEMOD_IQ_NORMAL, + MXL_EAGLE_DEMOD_IQ_FLIPPED, + MXL_EAGLE_DEMOD_IQ_AUTO, +}; + +/* Enum of OOB Demodulator symbol rates, used in OOB configuration */ +enum MXL_EAGLE_OOB_DEMOD_SYMB_RATE_E { + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_0_772MHZ, /* ANSI/SCTE 55-2 0.772 MHz */ + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_024MHZ, /* ANSI/SCTE 55-1 1.024 MHz */ + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_544MHZ, /* ANSI/SCTE 55-2 1.544 MHz */ +}; + +/* Enum of tuner channel tuning mode */ +enum MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_E { + MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW, /* Normal "view" mode */ + MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_SCAN, /* Fast "scan" mode */ +}; + +/* Enum of tuner bandwidth */ +enum MXL_EAGLE_TUNER_BW_E { + MXL_EAGLE_TUNER_BW_6MHZ, + MXL_EAGLE_TUNER_BW_7MHZ, + MXL_EAGLE_TUNER_BW_8MHZ, +}; + +/* Enum of tuner bandwidth */ +enum MXL_EAGLE_JUNCTION_TEMPERATURE_E { + MXL_EAGLE_JUNCTION_TEMPERATURE_BELOW_0_CELSIUS = 0, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_0_TO_14_CELSIUS = 1, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_14_TO_28_CELSIUS = 3, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_28_TO_42_CELSIUS = 2, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_42_TO_57_CELSIUS = 6, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_57_TO_71_CELSIUS = 7, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_71_TO_85_CELSIUS = 5, + MXL_EAGLE_JUNCTION_TEMPERATURE_ABOVE_85_CELSIUS = 4, +}; + +/* Struct passed in optional callback used during FW download */ +struct MXL_EAGLE_FW_DOWNLOAD_CB_PAYLOAD_T { + u32 total_len; + u32 downloaded_len; +}; + +/* Struct used of I2C protocol between host and Eagle, internal use only */ +struct __packed MXL_EAGLE_HOST_MSG_HEADER_T { + u8 opcode; + u8 seqnum; + u8 payload_size; + u8 status; + u32 checksum; +}; + +/* Device version information struct */ +struct __packed MXL_EAGLE_DEV_VER_T { + u8 chip_id; + u8 firmware_ver[MXL_EAGLE_VERSION_SIZE]; + u8 mxlware_ver[MXL_EAGLE_VERSION_SIZE]; +}; + +/* Xtal configuration struct */ +struct __packed MXL_EAGLE_DEV_XTAL_T { + u8 xtal_cap; /* accepted range is 1..31 pF. Default is 26 */ + u8 clk_out_enable; + u8 clk_out_div_enable; /* clock out freq is xtal freq / 6 */ + u8 xtal_sharing_enable; /* if enabled set xtal_cap to 25 pF */ + u8 xtal_calibration_enable; /* enable for master, disable for slave */ +}; + +/* GPIO direction struct, internally used in GPIO configuration API */ +struct __packed MXL_EAGLE_DEV_GPIO_DIRECTION_T { + u8 gpio_number; + u8 gpio_direction; +}; + +/* GPO level struct, internally used in GPIO configuration API */ +struct __packed MXL_EAGLE_DEV_GPO_LEVEL_T { + u8 gpio_number; + u8 gpo_level; +}; + +/* Device Status struct */ +struct MXL_EAGLE_DEV_STATUS_T { + u8 temperature; + u8 demod_type; + u8 power_mode; + u8 cpu_utilization_percent; +}; + +/* Device interrupt configuration struct */ +struct __packed MXL_EAGLE_DEV_INTR_CFG_T { + u32 intr_mask; + u8 edge_trigger; + u8 positive_trigger; + u8 global_enable_interrupt; +}; + +/* MPEG pad drive parameters, used on MPEG output configuration */ +/* See MXL_EAGLE_IO_MUX_DRIVE_MODE_E */ +struct MXL_EAGLE_MPEG_PAD_DRIVE_T { + u8 pad_drv_mpeg_syn; + u8 pad_drv_mpeg_dat; + u8 pad_drv_mpeg_val; + u8 pad_drv_mpeg_clk; +}; + +/* MPEGOUT parameter struct, used in MPEG output configuration */ +struct MXL_EAGLE_MPEGOUT_PARAMS_T { + u8 mpeg_parallel; + u8 msb_first; + u8 mpeg_sync_pulse_width; /* See MXL_EAGLE_MPEG_DATA_FORMAT_E */ + u8 mpeg_valid_pol; + u8 mpeg_sync_pol; + u8 mpeg_clk_pol; + u8 mpeg3wire_mode_enable; + u8 mpeg_clk_freq; + struct MXL_EAGLE_MPEG_PAD_DRIVE_T mpeg_pad_drv; +}; + +/* QAM Demodulator parameters struct, used in QAM params configuration */ +struct __packed MXL_EAGLE_QAM_DEMOD_PARAMS_T { + u8 annex_type; + u8 qam_type; + u8 iq_flip; + u8 search_range_idx; + u8 spur_canceller_enable; + u32 symbol_rate_hz; + u32 symbol_rate_256qam_hz; +}; + +/* QAM Demodulator status */ +struct MXL_EAGLE_QAM_DEMOD_STATUS_T { + u8 annex_type; + u8 qam_type; + u8 iq_flip; + u8 interleaver_depth_i; + u8 interleaver_depth_j; + u8 qam_locked; + u8 fec_locked; + u8 mpeg_locked; + u16 snr_db_tenths; + s16 timing_offset; + s32 carrier_offset_hz; +}; + +/* QAM Demodulator error counters */ +struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T { + u32 corrected_code_words; + u32 uncorrected_code_words; + u32 total_code_words_received; + u32 corrected_bits; + u32 error_mpeg_frames; + u32 mpeg_frames_received; + u32 erasures; +}; + +/* QAM Demodulator constellation point */ +struct MXL_EAGLE_QAM_DEMOD_CONSTELLATION_VAL_T { + s16 i_value[12]; + s16 q_value[12]; +}; + +/* QAM Demodulator equalizer filter taps */ +struct MXL_EAGLE_QAM_DEMOD_EQU_FILTER_T { + s16 ffe_taps[MXL_EAGLE_QAM_FFE_TAPS_LENGTH]; + s16 spur_taps[MXL_EAGLE_QAM_SPUR_TAPS_LENGTH]; + s16 dfe_taps[MXL_EAGLE_QAM_DFE_TAPS_LENGTH]; + u8 ffe_leading_tap_index; + u8 dfe_taps_number; +}; + +/* OOB Demodulator parameters struct, used in OOB params configuration */ +struct __packed MXL_EAGLE_OOB_DEMOD_PARAMS_T { + u8 symbol_rate; + u8 iq_flip; + u8 clk_pol; +}; + +/* OOB Demodulator error counters */ +struct MXL_EAGLE_OOB_DEMOD_ERROR_COUNTERS_T { + u32 corrected_packets; + u32 uncorrected_packets; + u32 total_packets_received; +}; + +/* OOB status */ +struct __packed MXL_EAGLE_OOB_DEMOD_STATUS_T { + u16 snr_db_tenths; + s16 timing_offset; + s32 carrier_offsetHz; + u8 qam_locked; + u8 fec_locked; + u8 mpeg_locked; + u8 retune_required; + u8 iq_flip; +}; + +/* ATSC Demodulator status */ +struct __packed MXL_EAGLE_ATSC_DEMOD_STATUS_T { + s16 snr_db_tenths; + s16 timing_offset; + s32 carrier_offset_hz; + u8 frame_lock; + u8 atsc_lock; + u8 fec_lock; +}; + +/* ATSC Demodulator error counters */ +struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T { + u32 error_packets; + u32 total_packets; + u32 error_bytes; +}; + +/* ATSC Demodulator equalizers filter taps */ +struct __packed MXL_EAGLE_ATSC_DEMOD_EQU_FILTER_T { + s16 ffe_taps[MXL_EAGLE_ATSC_FFE_TAPS_LENGTH]; + s8 dfe_taps[MXL_EAGLE_ATSC_DFE_TAPS_LENGTH]; +}; + +/* Tuner AGC Status */ +struct __packed MXL_EAGLE_TUNER_AGC_STATUS_T { + u8 locked; + u16 raw_agc_gain; /* AGC gain [dB] = rawAgcGain / 2^6 */ + s16 rx_power_db_hundredths; +}; + +/* Tuner channel tune parameters */ +struct __packed MXL_EAGLE_TUNER_CHANNEL_PARAMS_T { + u32 freq_hz; + u8 tune_mode; + u8 bandwidth; +}; + +/* Tuner channel lock indications */ +struct __packed MXL_EAGLE_TUNER_LOCK_STATUS_T { + u8 rf_pll_locked; + u8 ref_pll_locked; +}; + +/* Smart antenna parameters used in Smart antenna params configuration */ +struct __packed MXL_EAGLE_SMA_PARAMS_T { + u8 full_duplex_enable; + u8 rx_disable; + u8 idle_logic_high; +}; + +/* Smart antenna message format */ +struct __packed MXL_EAGLE_SMA_MESSAGE_T { + u32 payload_bits; + u8 total_num_bits; +}; + -- 2.23.0 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver 2020-06-12 18:39 ` [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver Brad Love @ 2020-06-12 21:49 ` kernel test robot 2020-06-13 8:49 ` kernel test robot ` (3 subsequent siblings) 4 siblings, 0 replies; 20+ messages in thread From: kernel test robot @ 2020-06-12 21:49 UTC (permalink / raw) To: Brad Love, linux-media; +Cc: kbuild-all, Brad Love [-- Attachment #1: Type: text/plain, Size: 6315 bytes --] Hi Brad, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on linuxtv-media/master] [also build test WARNING on v5.7 next-20200612] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982] url: https://github.com/0day-ci/linux/commits/Brad-Love/MaxLinear-mxl692-demod-tuner-Hauppauge-usb-QuadHD/20200613-024056 base: git://linuxtv.org/media_tree.git master config: i386-randconfig-s002-20200612 (attached as .config) compiler: gcc-9 (Debian 9.3.0-13) 9.3.0 reproduce: # apt-get install sparse # sparse version: v0.6.1-250-g42323db3-dirty # save the attached .config to linux build tree make W=1 C=1 ARCH=i386 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> sparse warnings: (new ones prefixed by >>) >> drivers/media/dvb-frontends/mxl692.c:223:27: sparse: sparse: invalid assignment: += >> drivers/media/dvb-frontends/mxl692.c:223:27: sparse: left side has type unsigned int >> drivers/media/dvb-frontends/mxl692.c:223:27: sparse: right side has type restricted __be32 drivers/media/dvb-frontends/mxl692.c:227:27: sparse: sparse: invalid assignment: += drivers/media/dvb-frontends/mxl692.c:227:27: sparse: left side has type unsigned int drivers/media/dvb-frontends/mxl692.c:227:27: sparse: right side has type restricted __be32 >> drivers/media/dvb-frontends/mxl692.c:231:16: sparse: sparse: cast to restricted __be32 >> drivers/media/dvb-frontends/mxl692.c:231:16: sparse: sparse: cast to restricted __be32 >> drivers/media/dvb-frontends/mxl692.c:231:16: sparse: sparse: cast to restricted __be32 >> drivers/media/dvb-frontends/mxl692.c:231:16: sparse: sparse: cast to restricted __be32 >> drivers/media/dvb-frontends/mxl692.c:231:16: sparse: sparse: cast to restricted __be32 >> drivers/media/dvb-frontends/mxl692.c:231:16: sparse: sparse: cast to restricted __be32 >> drivers/media/dvb-frontends/mxl692.c:249:14: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned int [usertype] temp @@ got restricted __be32 [usertype] @@ >> drivers/media/dvb-frontends/mxl692.c:249:14: sparse: expected unsigned int [usertype] temp >> drivers/media/dvb-frontends/mxl692.c:249:14: sparse: got restricted __be32 [usertype] >> drivers/media/dvb-frontends/mxl692.c:293:44: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned int [usertype] @@ got restricted __le32 [usertype] @@ >> drivers/media/dvb-frontends/mxl692.c:293:44: sparse: expected unsigned int [usertype] >> drivers/media/dvb-frontends/mxl692.c:293:44: sparse: got restricted __le32 [usertype] vim +223 drivers/media/dvb-frontends/mxl692.c 217 218 static u32 mxl692_checksum(u8 *buffer, u32 size) 219 { 220 u32 ix, remainder = 0, cur_cksum = 0; 221 222 for (ix = 0; ix < size / 4; ix++) > 223 cur_cksum += cpu_to_be32(*(u32 *)(buffer + 224 (ix * sizeof(u32)))); 225 remainder = size % 4; 226 if (remainder > 0) 227 cur_cksum += cpu_to_be32(*((u32 *)&buffer[size - remainder])); 228 229 cur_cksum ^= 0xDEADBEEF; 230 > 231 return be32_to_cpu(cur_cksum); 232 } 233 234 static int mxl692_validate_fw_header(const u8 *buffer, u32 buf_len) 235 { 236 int status = 0; 237 u32 ix, temp = 0; 238 u32 *local_buf = NULL; 239 240 if (buffer[0] != 0x4D || buffer[1] != 0x31 || 241 buffer[2] != 0x10 || buffer[3] != 0x02 || 242 buffer[4] != 0x40 || buffer[5] != 0x00 || 243 buffer[6] != 0x00 || buffer[7] != 0x80) { 244 status = -EINVAL; 245 goto err_finish; 246 } 247 248 local_buf = (u32 *)(buffer + 8); > 249 temp = cpu_to_be32(*(u32 *)local_buf); 250 251 if ((buf_len - 16) != (temp >> 8)) { 252 status = -EINVAL; 253 goto err_finish; 254 } 255 256 temp = 0; 257 for (ix = 16; ix < buf_len; ix++) 258 temp += buffer[ix]; 259 260 if ((u8)temp != buffer[11]) 261 status = -EINVAL; 262 err_finish: 263 if (status) 264 pr_err("%s failed! %d\n", __func__, status); 265 return status; 266 } 267 268 static int mxl692_write_fw_block(struct mxl692_dev *dev, const u8 *buffer, 269 u32 buf_len, u32 *index) 270 { 271 int status = 0; 272 u32 ix = 0, total_len = 0, addr = 0, chunk_len = 0, prevchunk_len = 0; 273 u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; 274 int payload_max = MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE; 275 276 ix = *index; 277 278 if (buffer[ix] == 0x53) { 279 total_len = buffer[ix + 1] << 16 | buffer[ix + 2] << 8 | buffer[ix + 3]; 280 total_len = (total_len + 3) & ~3; 281 addr = buffer[ix + 4] << 24 | buffer[ix + 5] << 16 | 282 buffer[ix + 6] << 8 | buffer[ix + 7]; 283 ix += MXL_EAGLE_FW_SEGMENT_HEADER_SIZE; 284 285 while ((total_len > 0) && (status == 0)) { 286 plocal_buf = local_buf; 287 chunk_len = (total_len < payload_max) ? 288 total_len : payload_max; 289 290 *plocal_buf++ = 0xFC; 291 *plocal_buf++ = chunk_len + sizeof(u32); 292 > 293 *(u32 *)plocal_buf = cpu_to_le32(addr + prevchunk_len); 294 plocal_buf += sizeof(u32); 295 296 memcpy(plocal_buf, &buffer[ix], chunk_len); 297 convert_endian(chunk_len, plocal_buf); 298 299 if (mxl692_i2c_write(dev, local_buf, 300 (chunk_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { 301 status = -EREMOTEIO; 302 break; 303 } 304 305 prevchunk_len += chunk_len; 306 total_len -= chunk_len; 307 ix += chunk_len; 308 } 309 *index = ix; 310 } else { 311 status = -EINVAL; 312 } 313 314 if (status) 315 dev_err(&dev->i2c_client->dev, "err %d\n", status); 316 317 return status; 318 } 319 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org [-- Attachment #2: .config.gz --] [-- Type: application/gzip, Size: 33680 bytes --] ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver @ 2020-06-12 21:49 ` kernel test robot 0 siblings, 0 replies; 20+ messages in thread From: kernel test robot @ 2020-06-12 21:49 UTC (permalink / raw) To: kbuild-all [-- Attachment #1: Type: text/plain, Size: 6471 bytes --] Hi Brad, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on linuxtv-media/master] [also build test WARNING on v5.7 next-20200612] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982] url: https://github.com/0day-ci/linux/commits/Brad-Love/MaxLinear-mxl692-demod-tuner-Hauppauge-usb-QuadHD/20200613-024056 base: git://linuxtv.org/media_tree.git master config: i386-randconfig-s002-20200612 (attached as .config) compiler: gcc-9 (Debian 9.3.0-13) 9.3.0 reproduce: # apt-get install sparse # sparse version: v0.6.1-250-g42323db3-dirty # save the attached .config to linux build tree make W=1 C=1 ARCH=i386 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> sparse warnings: (new ones prefixed by >>) >> drivers/media/dvb-frontends/mxl692.c:223:27: sparse: sparse: invalid assignment: += >> drivers/media/dvb-frontends/mxl692.c:223:27: sparse: left side has type unsigned int >> drivers/media/dvb-frontends/mxl692.c:223:27: sparse: right side has type restricted __be32 drivers/media/dvb-frontends/mxl692.c:227:27: sparse: sparse: invalid assignment: += drivers/media/dvb-frontends/mxl692.c:227:27: sparse: left side has type unsigned int drivers/media/dvb-frontends/mxl692.c:227:27: sparse: right side has type restricted __be32 >> drivers/media/dvb-frontends/mxl692.c:231:16: sparse: sparse: cast to restricted __be32 >> drivers/media/dvb-frontends/mxl692.c:231:16: sparse: sparse: cast to restricted __be32 >> drivers/media/dvb-frontends/mxl692.c:231:16: sparse: sparse: cast to restricted __be32 >> drivers/media/dvb-frontends/mxl692.c:231:16: sparse: sparse: cast to restricted __be32 >> drivers/media/dvb-frontends/mxl692.c:231:16: sparse: sparse: cast to restricted __be32 >> drivers/media/dvb-frontends/mxl692.c:231:16: sparse: sparse: cast to restricted __be32 >> drivers/media/dvb-frontends/mxl692.c:249:14: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned int [usertype] temp @@ got restricted __be32 [usertype] @@ >> drivers/media/dvb-frontends/mxl692.c:249:14: sparse: expected unsigned int [usertype] temp >> drivers/media/dvb-frontends/mxl692.c:249:14: sparse: got restricted __be32 [usertype] >> drivers/media/dvb-frontends/mxl692.c:293:44: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned int [usertype] @@ got restricted __le32 [usertype] @@ >> drivers/media/dvb-frontends/mxl692.c:293:44: sparse: expected unsigned int [usertype] >> drivers/media/dvb-frontends/mxl692.c:293:44: sparse: got restricted __le32 [usertype] vim +223 drivers/media/dvb-frontends/mxl692.c 217 218 static u32 mxl692_checksum(u8 *buffer, u32 size) 219 { 220 u32 ix, remainder = 0, cur_cksum = 0; 221 222 for (ix = 0; ix < size / 4; ix++) > 223 cur_cksum += cpu_to_be32(*(u32 *)(buffer + 224 (ix * sizeof(u32)))); 225 remainder = size % 4; 226 if (remainder > 0) 227 cur_cksum += cpu_to_be32(*((u32 *)&buffer[size - remainder])); 228 229 cur_cksum ^= 0xDEADBEEF; 230 > 231 return be32_to_cpu(cur_cksum); 232 } 233 234 static int mxl692_validate_fw_header(const u8 *buffer, u32 buf_len) 235 { 236 int status = 0; 237 u32 ix, temp = 0; 238 u32 *local_buf = NULL; 239 240 if (buffer[0] != 0x4D || buffer[1] != 0x31 || 241 buffer[2] != 0x10 || buffer[3] != 0x02 || 242 buffer[4] != 0x40 || buffer[5] != 0x00 || 243 buffer[6] != 0x00 || buffer[7] != 0x80) { 244 status = -EINVAL; 245 goto err_finish; 246 } 247 248 local_buf = (u32 *)(buffer + 8); > 249 temp = cpu_to_be32(*(u32 *)local_buf); 250 251 if ((buf_len - 16) != (temp >> 8)) { 252 status = -EINVAL; 253 goto err_finish; 254 } 255 256 temp = 0; 257 for (ix = 16; ix < buf_len; ix++) 258 temp += buffer[ix]; 259 260 if ((u8)temp != buffer[11]) 261 status = -EINVAL; 262 err_finish: 263 if (status) 264 pr_err("%s failed! %d\n", __func__, status); 265 return status; 266 } 267 268 static int mxl692_write_fw_block(struct mxl692_dev *dev, const u8 *buffer, 269 u32 buf_len, u32 *index) 270 { 271 int status = 0; 272 u32 ix = 0, total_len = 0, addr = 0, chunk_len = 0, prevchunk_len = 0; 273 u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; 274 int payload_max = MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE; 275 276 ix = *index; 277 278 if (buffer[ix] == 0x53) { 279 total_len = buffer[ix + 1] << 16 | buffer[ix + 2] << 8 | buffer[ix + 3]; 280 total_len = (total_len + 3) & ~3; 281 addr = buffer[ix + 4] << 24 | buffer[ix + 5] << 16 | 282 buffer[ix + 6] << 8 | buffer[ix + 7]; 283 ix += MXL_EAGLE_FW_SEGMENT_HEADER_SIZE; 284 285 while ((total_len > 0) && (status == 0)) { 286 plocal_buf = local_buf; 287 chunk_len = (total_len < payload_max) ? 288 total_len : payload_max; 289 290 *plocal_buf++ = 0xFC; 291 *plocal_buf++ = chunk_len + sizeof(u32); 292 > 293 *(u32 *)plocal_buf = cpu_to_le32(addr + prevchunk_len); 294 plocal_buf += sizeof(u32); 295 296 memcpy(plocal_buf, &buffer[ix], chunk_len); 297 convert_endian(chunk_len, plocal_buf); 298 299 if (mxl692_i2c_write(dev, local_buf, 300 (chunk_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { 301 status = -EREMOTEIO; 302 break; 303 } 304 305 prevchunk_len += chunk_len; 306 total_len -= chunk_len; 307 ix += chunk_len; 308 } 309 *index = ix; 310 } else { 311 status = -EINVAL; 312 } 313 314 if (status) 315 dev_err(&dev->i2c_client->dev, "err %d\n", status); 316 317 return status; 318 } 319 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org [-- Attachment #2: config.gz --] [-- Type: application/gzip, Size: 33680 bytes --] ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver 2020-06-12 18:39 ` [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver Brad Love @ 2020-06-13 8:49 ` kernel test robot 2020-06-13 8:49 ` kernel test robot ` (3 subsequent siblings) 4 siblings, 0 replies; 20+ messages in thread From: kernel test robot @ 2020-06-13 8:49 UTC (permalink / raw) To: Brad Love, linux-media; +Cc: kbuild-all, Brad Love [-- Attachment #1: Type: text/plain, Size: 3359 bytes --] Hi Brad, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on linuxtv-media/master] [also build test WARNING on v5.7 next-20200613] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982] url: https://github.com/0day-ci/linux/commits/Brad-Love/MaxLinear-mxl692-demod-tuner-Hauppauge-usb-QuadHD/20200613-024056 base: git://linuxtv.org/media_tree.git master config: alpha-randconfig-c021-20200612 (attached as .config) compiler: alpha-linux-gcc (GCC) 9.3.0 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>, old ones prefixed by <<): drivers/media/dvb-frontends/mxl692.c: In function 'mxl692_read_ber_ucb': >> drivers/media/dvb-frontends/mxl692.c:1159:47: warning: variable 'qam_errors' set but not used [-Wunused-but-set-variable] 1159 | struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T *qam_errors; | ^~~~~~~~~~ vim +/qam_errors +1159 drivers/media/dvb-frontends/mxl692.c 1153 1154 static int mxl692_read_ber_ucb(struct dvb_frontend *fe) 1155 { 1156 struct mxl692_dev *dev = fe->demodulator_priv; 1157 struct dtv_frontend_properties *c = &fe->dtv_property_cache; 1158 u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > 1159 struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T *qam_errors; 1160 struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *atsc_errors; 1161 enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; 1162 int mxl_status = 0; 1163 u32 utmp; 1164 1165 dev_dbg(&dev->i2c_client->dev, "\n"); 1166 1167 qam_errors = (struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T *)&rx_buf; 1168 atsc_errors = (struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *)&rx_buf; 1169 1170 switch (demod_type) { 1171 case MXL_EAGLE_DEMOD_TYPE_ATSC: 1172 mxl_status = mxl692_i2c_writeread(dev, 1173 MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, 1174 NULL, 1175 0, 1176 rx_buf, 1177 sizeof(struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T)); 1178 if (!mxl_status) { 1179 if (atsc_errors->error_packets == 0) 1180 utmp = 0; 1181 else 1182 utmp = ((atsc_errors->error_bytes / atsc_errors->error_packets) * 1183 atsc_errors->total_packets); 1184 /* ber */ 1185 c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; 1186 c->post_bit_error.stat[0].uvalue += atsc_errors->error_bytes; 1187 c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; 1188 c->post_bit_count.stat[0].uvalue += utmp; 1189 /* ucb */ 1190 c->block_error.stat[0].scale = FE_SCALE_COUNTER; 1191 c->block_error.stat[0].uvalue += atsc_errors->error_packets; 1192 1193 dev_dbg(&dev->i2c_client->dev, "%llu %llu\n", 1194 c->post_bit_count.stat[0].uvalue, c->block_error.stat[0].uvalue); 1195 } 1196 break; 1197 case MXL_EAGLE_DEMOD_TYPE_QAM: 1198 case MXL_EAGLE_DEMOD_TYPE_OOB: 1199 default: 1200 break; 1201 } 1202 return 0; 1203 } 1204 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org [-- Attachment #2: .config.gz --] [-- Type: application/gzip, Size: 33557 bytes --] ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver @ 2020-06-13 8:49 ` kernel test robot 0 siblings, 0 replies; 20+ messages in thread From: kernel test robot @ 2020-06-13 8:49 UTC (permalink / raw) To: kbuild-all [-- Attachment #1: Type: text/plain, Size: 3444 bytes --] Hi Brad, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on linuxtv-media/master] [also build test WARNING on v5.7 next-20200613] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982] url: https://github.com/0day-ci/linux/commits/Brad-Love/MaxLinear-mxl692-demod-tuner-Hauppauge-usb-QuadHD/20200613-024056 base: git://linuxtv.org/media_tree.git master config: alpha-randconfig-c021-20200612 (attached as .config) compiler: alpha-linux-gcc (GCC) 9.3.0 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>, old ones prefixed by <<): drivers/media/dvb-frontends/mxl692.c: In function 'mxl692_read_ber_ucb': >> drivers/media/dvb-frontends/mxl692.c:1159:47: warning: variable 'qam_errors' set but not used [-Wunused-but-set-variable] 1159 | struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T *qam_errors; | ^~~~~~~~~~ vim +/qam_errors +1159 drivers/media/dvb-frontends/mxl692.c 1153 1154 static int mxl692_read_ber_ucb(struct dvb_frontend *fe) 1155 { 1156 struct mxl692_dev *dev = fe->demodulator_priv; 1157 struct dtv_frontend_properties *c = &fe->dtv_property_cache; 1158 u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > 1159 struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T *qam_errors; 1160 struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *atsc_errors; 1161 enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; 1162 int mxl_status = 0; 1163 u32 utmp; 1164 1165 dev_dbg(&dev->i2c_client->dev, "\n"); 1166 1167 qam_errors = (struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T *)&rx_buf; 1168 atsc_errors = (struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *)&rx_buf; 1169 1170 switch (demod_type) { 1171 case MXL_EAGLE_DEMOD_TYPE_ATSC: 1172 mxl_status = mxl692_i2c_writeread(dev, 1173 MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, 1174 NULL, 1175 0, 1176 rx_buf, 1177 sizeof(struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T)); 1178 if (!mxl_status) { 1179 if (atsc_errors->error_packets == 0) 1180 utmp = 0; 1181 else 1182 utmp = ((atsc_errors->error_bytes / atsc_errors->error_packets) * 1183 atsc_errors->total_packets); 1184 /* ber */ 1185 c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; 1186 c->post_bit_error.stat[0].uvalue += atsc_errors->error_bytes; 1187 c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; 1188 c->post_bit_count.stat[0].uvalue += utmp; 1189 /* ucb */ 1190 c->block_error.stat[0].scale = FE_SCALE_COUNTER; 1191 c->block_error.stat[0].uvalue += atsc_errors->error_packets; 1192 1193 dev_dbg(&dev->i2c_client->dev, "%llu %llu\n", 1194 c->post_bit_count.stat[0].uvalue, c->block_error.stat[0].uvalue); 1195 } 1196 break; 1197 case MXL_EAGLE_DEMOD_TYPE_QAM: 1198 case MXL_EAGLE_DEMOD_TYPE_OOB: 1199 default: 1200 break; 1201 } 1202 return 0; 1203 } 1204 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org [-- Attachment #2: config.gz --] [-- Type: application/gzip, Size: 33557 bytes --] ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver 2020-06-12 18:39 ` [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver Brad Love @ 2020-06-13 21:07 ` kernel test robot 2020-06-13 8:49 ` kernel test robot ` (3 subsequent siblings) 4 siblings, 0 replies; 20+ messages in thread From: kernel test robot @ 2020-06-13 21:07 UTC (permalink / raw) To: Brad Love, linux-media; +Cc: kbuild-all, Brad Love [-- Attachment #1: Type: text/plain, Size: 1089 bytes --] Hi Brad, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on linuxtv-media/master] [also build test WARNING on v5.7] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982] url: https://github.com/0day-ci/linux/commits/Brad-Love/MaxLinear-mxl692-demod-tuner-Hauppauge-usb-QuadHD/20200613-024056 base: git://linuxtv.org/media_tree.git master config: nds32-randconfig-c022-20200614 (attached as .config) compiler: nds32le-linux-gcc (GCC) 9.3.0 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> coccinelle warnings: (new ones prefixed by >>) >> drivers/media/dvb-frontends/mxl692.c:1350:3-8: No need to set .owner here. The core will do it. Please review and possibly fold the followup patch. --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org [-- Attachment #2: .config.gz --] [-- Type: application/gzip, Size: 24649 bytes --] ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver @ 2020-06-13 21:07 ` kernel test robot 0 siblings, 0 replies; 20+ messages in thread From: kernel test robot @ 2020-06-13 21:07 UTC (permalink / raw) To: kbuild-all [-- Attachment #1: Type: text/plain, Size: 1119 bytes --] Hi Brad, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on linuxtv-media/master] [also build test WARNING on v5.7] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982] url: https://github.com/0day-ci/linux/commits/Brad-Love/MaxLinear-mxl692-demod-tuner-Hauppauge-usb-QuadHD/20200613-024056 base: git://linuxtv.org/media_tree.git master config: nds32-randconfig-c022-20200614 (attached as .config) compiler: nds32le-linux-gcc (GCC) 9.3.0 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> coccinelle warnings: (new ones prefixed by >>) >> drivers/media/dvb-frontends/mxl692.c:1350:3-8: No need to set .owner here. The core will do it. Please review and possibly fold the followup patch. --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org [-- Attachment #2: config.gz --] [-- Type: application/gzip, Size: 24649 bytes --] ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH] mxl692: fix platform_no_drv_owner.cocci warnings 2020-06-12 18:39 ` [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver Brad Love @ 2020-06-13 21:07 ` kernel test robot 2020-06-13 8:49 ` kernel test robot ` (3 subsequent siblings) 4 siblings, 0 replies; 20+ messages in thread From: kernel test robot @ 2020-06-13 21:07 UTC (permalink / raw) To: Brad Love, linux-media; +Cc: kbuild-all, Brad Love From: kernel test robot <lkp@intel.com> drivers/media/dvb-frontends/mxl692.c:1350:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Brad Love <brad@nextdimension.cc> Signed-off-by: kernel test robot <lkp@intel.com> --- url: https://github.com/0day-ci/linux/commits/Brad-Love/MaxLinear-mxl692-demod-tuner-Hauppauge-usb-QuadHD/20200613-024056 base: git://linuxtv.org/media_tree.git master mxl692.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/media/dvb-frontends/mxl692.c +++ b/drivers/media/dvb-frontends/mxl692.c @@ -1347,7 +1347,6 @@ MODULE_DEVICE_TABLE(i2c, mxl692_id_table static struct i2c_driver mxl692_driver = { .driver = { - .owner = THIS_MODULE, .name = "mxl692", }, .probe = mxl692_probe, ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH] mxl692: fix platform_no_drv_owner.cocci warnings @ 2020-06-13 21:07 ` kernel test robot 0 siblings, 0 replies; 20+ messages in thread From: kernel test robot @ 2020-06-13 21:07 UTC (permalink / raw) To: kbuild-all [-- Attachment #1: Type: text/plain, Size: 900 bytes --] From: kernel test robot <lkp@intel.com> drivers/media/dvb-frontends/mxl692.c:1350:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Brad Love <brad@nextdimension.cc> Signed-off-by: kernel test robot <lkp@intel.com> --- url: https://github.com/0day-ci/linux/commits/Brad-Love/MaxLinear-mxl692-demod-tuner-Hauppauge-usb-QuadHD/20200613-024056 base: git://linuxtv.org/media_tree.git master mxl692.c | 1 - 1 file changed, 1 deletion(-) --- a/drivers/media/dvb-frontends/mxl692.c +++ b/drivers/media/dvb-frontends/mxl692.c @@ -1347,7 +1347,6 @@ MODULE_DEVICE_TABLE(i2c, mxl692_id_table static struct i2c_driver mxl692_driver = { .driver = { - .owner = THIS_MODULE, .name = "mxl692", }, .probe = mxl692_probe, ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver 2020-06-12 18:39 ` [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver Brad Love ` (3 preceding siblings ...) 2020-06-13 21:07 ` kernel test robot @ 2020-06-24 9:59 ` Sean Young 2020-06-26 17:41 ` Bradford Love 4 siblings, 1 reply; 20+ messages in thread From: Sean Young @ 2020-06-24 9:59 UTC (permalink / raw) To: Brad Love; +Cc: linux-media Hi Brad, Thank you for the patch, looks great apart from some minor issues. First of all, sparse finds a number of issues, it would be great to have those resolved. On Fri, Jun 12, 2020 at 01:39:35PM -0500, Brad Love wrote: > MaxLinear 692 is a combo demod/tuner which has ATSC and QAM > capabilities. Only ATSC is currently advertised via DVB > properties. QAM still has issues. > > > Signed-off-by: Brad Love <brad@nextdimension.cc> > --- > drivers/media/dvb-frontends/Kconfig | 9 + > drivers/media/dvb-frontends/Makefile | 1 + > drivers/media/dvb-frontends/mxl692.c | 1363 +++++++++++++++++++++ > drivers/media/dvb-frontends/mxl692.h | 38 + > drivers/media/dvb-frontends/mxl692_defs.h | 493 ++++++++ > 5 files changed, 1904 insertions(+) > create mode 100644 drivers/media/dvb-frontends/mxl692.c > create mode 100644 drivers/media/dvb-frontends/mxl692.h > create mode 100644 drivers/media/dvb-frontends/mxl692_defs.h > > diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig > index 643b851a6b60..b1ded2137f0e 100644 > --- a/drivers/media/dvb-frontends/Kconfig > +++ b/drivers/media/dvb-frontends/Kconfig > @@ -708,6 +708,15 @@ config DVB_S5H1411 > An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want > to support this frontend. > > +config DVB_MXL692 > + tristate "MaxLinear MXL692 based" > + depends on DVB_CORE && I2C > + default m if !MEDIA_SUBDRV_AUTOSELECT > + help > + MaxLinear MxL692 is a combo tuner-demodulator that > + supports ATSC 8VSB and QAM modes. Say Y when you want to > + support this frontend. > + > comment "ISDB-T (terrestrial) frontends" > depends on DVB_CORE > > diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile > index e9179162658c..b9f47d68e14e 100644 > --- a/drivers/media/dvb-frontends/Makefile > +++ b/drivers/media/dvb-frontends/Makefile > @@ -55,6 +55,7 @@ obj-$(CONFIG_DVB_S5H1420) += s5h1420.o > obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o > obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o > obj-$(CONFIG_DVB_LGDT3306A) += lgdt3306a.o > +obj-$(CONFIG_DVB_MXL692) += mxl692.o > obj-$(CONFIG_DVB_LG2160) += lg2160.o > obj-$(CONFIG_DVB_CX24123) += cx24123.o > obj-$(CONFIG_DVB_LNBH25) += lnbh25.o > diff --git a/drivers/media/dvb-frontends/mxl692.c b/drivers/media/dvb-frontends/mxl692.c > new file mode 100644 > index 000000000000..622b7a7ebab5 > --- /dev/null > +++ b/drivers/media/dvb-frontends/mxl692.c > @@ -0,0 +1,1363 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Driver for the MaxLinear MxL69x family of combo tuners/demods > + * > + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> > + * > + * based on code: > + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved > + * which was released under GPL V2 > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#include <linux/mutex.h> > +#include <linux/i2c-mux.h> > +#include <linux/string.h> > +#include <linux/firmware.h> > + > +#include "mxl692.h" > +#include "mxl692_defs.h" > + > +static int g_big_endian = -1; g_big_endian is not needed. You can use #ifdef __BIG_ENDIAN ... #endif > + > +static const struct dvb_frontend_ops mxl692_ops; > + > +struct mxl692_dev { > + struct dvb_frontend fe; > + struct i2c_client *i2c_client; > + struct mutex i2c_lock; /* i2c command mutex */ > + enum MXL_EAGLE_DEMOD_TYPE_E demod_type; > + enum MXL_EAGLE_POWER_MODE_E power_mode; > + u32 current_frequency; > + int device_type; > + int seqnum; > + int init_done; > +}; > + > +static int mxl692_i2c_write(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) > +{ > + int ret = 0; > + struct i2c_msg msg = { > + .addr = dev->i2c_client->addr, > + .flags = 0, > + .buf = buffer, > + .len = buf_len > + }; > + > + ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1); > + if (ret != 1) > + dev_err(&dev->i2c_client->dev, "i2c write error!\n"); > + > + return ret; > +} > + > +static int mxl692_i2c_read(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) > +{ > + int ret = 0; > + struct i2c_msg msg = { > + .addr = dev->i2c_client->addr, > + .flags = I2C_M_RD, > + .buf = buffer, > + .len = buf_len > + }; > + > + ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1); > + if (ret != 1) > + dev_err(&dev->i2c_client->dev, "i2c read error!\n"); > + > + return ret; > +} > + > +static void detect_endianness(void) > +{ > + u32 temp = 1; > + u8 *p_temp = (u8 *)&temp; > + > + g_big_endian = (*p_temp == 0) ? 1 : 0; > + > + if (g_big_endian) > + pr_debug("%s( BIG )\n", __func__); > + else > + pr_debug("%s( SMALL )\n", __func__); > +} Function not needed, please remove. > +static int convert_endian(u32 size, u8 *d) > +{ > + u32 i; > + > + for (i = 0; i < (size & ~3); i += 4) { > + d[i + 0] ^= d[i + 3]; > + d[i + 3] ^= d[i + 0]; > + d[i + 0] ^= d[i + 3]; > + > + d[i + 1] ^= d[i + 2]; > + d[i + 2] ^= d[i + 1]; > + d[i + 1] ^= d[i + 2]; If I'm reading this correctly -- and it's hard to parse -- this byte swapping byte 0 and 3, and 1 and 2. convert_endian is only called on little endian. So this is doing le32_to_cpus()? > + } > + > + switch (size & 3) { > + case 0: > + case 1: > + /* do nothing */ > + break; > + case 2: > + d[i + 0] ^= d[i + 1]; > + d[i + 1] ^= d[i + 0]; > + d[i + 0] ^= d[i + 1]; le16_to_cpus()? > + break; > + > + case 3: > + d[i + 0] ^= d[i + 2]; > + d[i + 2] ^= d[i + 0]; > + d[i + 0] ^= d[i + 2]; What is this? le24_to_cpus()? > + break; > + } > + return size; > +} > + > +static int convert_endian_n(int n, u32 size, u8 *d) > +{ > + int i, count = 0; > + > + for (i = 0; i < n; i += size) > + count += convert_endian(size, d + i); > + return count; > +} These functions are really hard to read. It would be much better if the structs like MXL_EAGLE_TUNER_CHANNEL_PARAMS_T have their freq_hz field to be type be32 rather than u32, and do the conversion when it is written/read rather than patching it up to the right endianness. mxl692_tx_swap() and mxl692_rx_swap() both really should be removed, if possible. > + > +static void mxl692_tx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) > +{ > + if (g_big_endian) > + return; > + > + buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ > + > + switch (opcode) { > + case MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET: > + case MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET: > + case MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET: > + buffer += convert_endian(sizeof(u32), buffer); > + break; > + case MXL_EAGLE_OPCODE_QAM_PARAMS_SET: > + buffer += 5; > + buffer += convert_endian(2 * sizeof(u32), buffer); > + break; > + default: > + /* no swapping - all get opcodes */ > + /* ATSC/OOB no swapping */ > + break; > + } > +} > + > +static void mxl692_rx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) > +{ > + if (g_big_endian) > + return; > + > + buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ > + > + switch (opcode) { > + case MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET: > + buffer++; > + buffer += convert_endian(2 * sizeof(u16), buffer); > + break; > + case MXL_EAGLE_OPCODE_ATSC_STATUS_GET: > + buffer += convert_endian_n(2, sizeof(u16), buffer); > + buffer += convert_endian(sizeof(u32), buffer); > + break; > + case MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET: > + buffer += convert_endian(3 * sizeof(u32), buffer); > + break; > + case MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET: > + buffer += convert_endian_n(24, sizeof(u16), buffer); > + break; > + case MXL_EAGLE_OPCODE_QAM_STATUS_GET: > + buffer += 8; > + buffer += convert_endian_n(2, sizeof(u16), buffer); > + buffer += convert_endian(sizeof(u32), buffer); > + break; > + case MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET: > + buffer += convert_endian(7 * sizeof(u32), buffer); > + break; > + case MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET: > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET: > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET: > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET: > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET: > + buffer += convert_endian_n(24, sizeof(u16), buffer); > + break; > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET: > + buffer += convert_endian_n(8, sizeof(u16), buffer); > + break; > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET: > + buffer += convert_endian_n(17, sizeof(u16), buffer); > + break; > + case MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET: > + buffer += convert_endian(3 * sizeof(u32), buffer); > + break; > + case MXL_EAGLE_OPCODE_OOB_STATUS_GET: > + buffer += convert_endian_n(2, sizeof(u16), buffer); > + buffer += convert_endian(sizeof(u32), buffer); > + break; > + case MXL_EAGLE_OPCODE_SMA_RECEIVE_GET: > + buffer += convert_endian(sizeof(u32), buffer); > + break; > + default: > + /* no swapping - all set opcodes */ > + break; > + } > +} > + > +static u32 mxl692_checksum(u8 *buffer, u32 size) > +{ > + u32 ix, remainder = 0, cur_cksum = 0; > + > + for (ix = 0; ix < size / 4; ix++) > + cur_cksum += cpu_to_be32(*(u32 *)(buffer + > + (ix * sizeof(u32)))); This is a bit messy. If buffer is a u32 pointer, you could do buffer[ix] and avoid the cast. > + remainder = size % 4; > + if (remainder > 0) > + cur_cksum += cpu_to_be32(*((u32 *)&buffer[size - remainder])); Ehm. If there is a remainder, then do another cksum += .. ? Can't this be solved by merging this into the above loop? (i.e. one more iteration if size % 4 > 0: for (ix = 0; ix < size; ix += 4) cur_cksum += cpu_to_be32(*(u32 *)(buffer + ix)); Since this is calculating the checksum, I would expect it do X_to_cpu(), not cpu_to_X. > + > + cur_cksum ^= 0xDEADBEEF; > + > + return be32_to_cpu(cur_cksum); > +} > + > +static int mxl692_validate_fw_header(const u8 *buffer, u32 buf_len) > +{ > + int status = 0; > + u32 ix, temp = 0; > + u32 *local_buf = NULL; > + > + if (buffer[0] != 0x4D || buffer[1] != 0x31 || > + buffer[2] != 0x10 || buffer[3] != 0x02 || > + buffer[4] != 0x40 || buffer[5] != 0x00 || > + buffer[6] != 0x00 || buffer[7] != 0x80) { > + status = -EINVAL; > + goto err_finish; > + } memcmp() might be shorter. > + > + local_buf = (u32 *)(buffer + 8); > + temp = cpu_to_be32(*(u32 *)local_buf); > + > + if ((buf_len - 16) != (temp >> 8)) { > + status = -EINVAL; > + goto err_finish; > + } > + > + temp = 0; > + for (ix = 16; ix < buf_len; ix++) > + temp += buffer[ix]; > + > + if ((u8)temp != buffer[11]) By making temp u8 you avoid the cast. > + status = -EINVAL; > +err_finish: > + if (status) > + pr_err("%s failed! %d\n", __func__, status); > + return status; > +} > + > +static int mxl692_write_fw_block(struct mxl692_dev *dev, const u8 *buffer, > + u32 buf_len, u32 *index) > +{ > + int status = 0; > + u32 ix = 0, total_len = 0, addr = 0, chunk_len = 0, prevchunk_len = 0; > + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; > + int payload_max = MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE; > + > + ix = *index; > + > + if (buffer[ix] == 0x53) { > + total_len = buffer[ix + 1] << 16 | buffer[ix + 2] << 8 | buffer[ix + 3]; > + total_len = (total_len + 3) & ~3; > + addr = buffer[ix + 4] << 24 | buffer[ix + 5] << 16 | > + buffer[ix + 6] << 8 | buffer[ix + 7]; > + ix += MXL_EAGLE_FW_SEGMENT_HEADER_SIZE; > + > + while ((total_len > 0) && (status == 0)) { > + plocal_buf = local_buf; > + chunk_len = (total_len < payload_max) ? > + total_len : payload_max; > + > + *plocal_buf++ = 0xFC; > + *plocal_buf++ = chunk_len + sizeof(u32); > + > + *(u32 *)plocal_buf = cpu_to_le32(addr + prevchunk_len); > + plocal_buf += sizeof(u32); > + > + memcpy(plocal_buf, &buffer[ix], chunk_len); > + convert_endian(chunk_len, plocal_buf); > + > + if (mxl692_i2c_write(dev, local_buf, > + (chunk_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { > + status = -EREMOTEIO; > + break; > + } > + > + prevchunk_len += chunk_len; > + total_len -= chunk_len; > + ix += chunk_len; > + } > + *index = ix; > + } else { > + status = -EINVAL; > + } > + > + if (status) > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > + > + return status; > +} > + > +static int mxl692_memwrite(struct mxl692_dev *dev, u32 addr, > + u8 *buffer, u32 size) > +{ > + int status = 0, total_len = 0; > + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; > + > + total_len = size; > + total_len = (total_len + 3) & ~3; /* 4 byte alignment */ > + > + if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE)) > + dev_dbg(&dev->i2c_client->dev, "hrmph?\n"); > + > + plocal_buf = local_buf; > + > + *plocal_buf++ = 0xFC; > + *plocal_buf++ = total_len + sizeof(u32); > + > + *(u32 *)plocal_buf = addr; > + plocal_buf += sizeof(u32); > + > + memcpy(plocal_buf, buffer, total_len); > + if (g_big_endian) > + convert_endian(sizeof(u32) + total_len, local_buf + 2); > + > + if (mxl692_i2c_write(dev, local_buf, > + (total_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { > + status = -EREMOTEIO; > + goto err_finish; > + } > + > + return status; > +err_finish: > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > + return status; > +} > + > +static int mxl692_memread(struct mxl692_dev *dev, u32 addr, > + u8 *buffer, u32 size) > +{ > + int status = 0; > + u8 local_buf[MXL_EAGLE_I2C_MHEADER_SIZE] = {}, *plocal_buf = NULL; > + > + plocal_buf = local_buf; > + > + *plocal_buf++ = 0xFB; > + *plocal_buf++ = sizeof(u32); > + *(u32 *)plocal_buf = addr; > + > + if (g_big_endian) > + convert_endian(sizeof(u32), plocal_buf); > + > + if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_MHEADER_SIZE) > 0) { > + size = (size + 3) & ~3; /* 4 byte alignment */ > + status = mxl692_i2c_read(dev, buffer, (u16)size) < 0 ? > + -EREMOTEIO : 0; > + > + if (status == 0 && g_big_endian) > + convert_endian(size, buffer); > + } else { > + status = -EREMOTEIO; > + } > + > + if (status) > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > + > + return status; > +} > + > +static int mxl692_opwrite(struct mxl692_dev *dev, u8 *buffer, > + u32 size) > +{ > + int status = 0, total_len = 0; > + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; > + > + total_len = size; > + total_len = (total_len + 3) & ~3; /* 4 byte alignment */ > + > + if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) > + dev_dbg(&dev->i2c_client->dev, "hrmph?\n"); > + > + plocal_buf = local_buf; > + > + *plocal_buf++ = 0xFE; > + *plocal_buf++ = (u8)total_len; > + > + memcpy(plocal_buf, buffer, total_len); > + convert_endian(total_len, plocal_buf); > + > + if (mxl692_i2c_write(dev, local_buf, > + (total_len + MXL_EAGLE_I2C_PHEADER_SIZE)) < 0) { > + status = -EREMOTEIO; > + goto err_finish; > + } > +err_finish: > + if (status) > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > + return status; > +} > + > +static int mxl692_opread(struct mxl692_dev *dev, u8 *buffer, > + u32 size) > +{ > + int status = 0; > + u32 ix = 0; > + u8 local_buf[MXL_EAGLE_I2C_PHEADER_SIZE] = {}; > + > + local_buf[0] = 0xFD; > + local_buf[1] = 0; > + > + if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_PHEADER_SIZE) > 0) { > + size = (size + 3) & ~3; /* 4 byte alignment */ > + > + //read in 4 byte chunks > + for (ix = 0; ix < size; ix += 4) { > + if (mxl692_i2c_read(dev, buffer + ix, 4) < 0) { > + dev_dbg(&dev->i2c_client->dev, > + "ix=%d size=%d\n", ix, size); > + status = -EREMOTEIO; > + goto err_finish; > + } > + } > + convert_endian(size, buffer); > + } else { > + status = -EREMOTEIO; > + } > +err_finish: > + if (status) > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > + return status; > +} > + > +static int mxl692_i2c_writeread(struct mxl692_dev *dev, > + u8 opcode, > + u8 *tx_payload, > + u8 tx_payload_size, > + u8 *rx_payload, > + u8 rx_payload_expected) > +{ > + int status = 0, timeout = 40; > + u8 tx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > + u32 resp_checksum = 0, resp_checksum_tmp = 0; > + struct MXL_EAGLE_HOST_MSG_HEADER_T *tx_header; > + struct MXL_EAGLE_HOST_MSG_HEADER_T *rx_header; > + > + mutex_lock(&dev->i2c_lock); > + > + if ((tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE) > > + (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) { > + status = -EINVAL; > + goto err_finish; > + } > + > + tx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)tx_buf; > + tx_header->checksum = 0; > + tx_header->opcode = opcode; > + tx_header->payload_size = tx_payload_size; > + tx_header->seqnum = dev->seqnum++; > + > + if (dev->seqnum == 0) > + dev->seqnum = 1; > + > + if (tx_payload && tx_payload_size > 0) > + memcpy(&tx_buf[MXL_EAGLE_HOST_MSG_HEADER_SIZE], > + tx_payload, tx_payload_size); > + > + mxl692_tx_swap(opcode, tx_buf); > + > + tx_header->checksum = 0; > + tx_header->checksum = mxl692_checksum(tx_buf, > + MXL_EAGLE_HOST_MSG_HEADER_SIZE + tx_payload_size); > + > + /* send Tx message */ > + status = mxl692_opwrite(dev, tx_buf, > + tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE); > + if (status) { > + status = -EREMOTEIO; > + goto err_finish; > + } > + > + /* receive Rx message (polling) */ > + rx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)rx_buf; > + > + do { > + status = mxl692_opread(dev, rx_buf, > + rx_payload_expected + MXL_EAGLE_HOST_MSG_HEADER_SIZE); > + usleep_range(1000, 2000); > + timeout--; > + } while ((timeout > 0) && (status == 0) && > + (rx_header->seqnum == 0) && > + (rx_header->checksum == 0)); > + > + if (timeout == 0 || status) { > + dev_dbg(&dev->i2c_client->dev, "timeout=%d status=%d\n", > + timeout, status); > + status = -ETIMEDOUT; > + goto err_finish; > + } > + > + if (rx_header->status) { > + status = (int)rx_header->status; > + goto err_finish; > + } > + > + if (rx_header->seqnum != tx_header->seqnum || > + rx_header->opcode != tx_header->opcode || > + rx_header->payload_size != rx_payload_expected) { > + dev_dbg(&dev->i2c_client->dev, "Something failed seq=%s opcode=%s pSize=%s\n", > + rx_header->seqnum != tx_header->seqnum ? "X" : "0", > + rx_header->opcode != tx_header->opcode ? "X" : "0", > + rx_header->payload_size != rx_payload_expected ? "X" : "0"); > + if (rx_header->payload_size != rx_payload_expected) > + dev_dbg(&dev->i2c_client->dev, "rx_header->payloadSize=%d rx_payload_expected=%d\n", > + rx_header->payload_size, rx_payload_expected); > + status = -EREMOTEIO; > + goto err_finish; > + } > + > + resp_checksum = rx_header->checksum; > + rx_header->checksum = 0; > + resp_checksum_tmp = mxl692_checksum(rx_buf, > + MXL_EAGLE_HOST_MSG_HEADER_SIZE + rx_header->payload_size); > + > + if (resp_checksum != resp_checksum_tmp) { > + status = -EREMOTEIO; > + goto err_finish; > + } > + > + mxl692_rx_swap(rx_header->opcode, rx_buf); > + > + if (rx_header->payload_size > 0) { > + if (!rx_payload) { > + status = -EREMOTEIO; > + goto err_finish; > + } > + memcpy(rx_payload, rx_buf + MXL_EAGLE_HOST_MSG_HEADER_SIZE, > + rx_header->payload_size); > + } > +err_finish: > + if (status) > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > + > + mutex_unlock(&dev->i2c_lock); > + return status; > +} > + > +static int mxl692_fwdownload(struct mxl692_dev *dev, > + const u8 *firmware_buf, u32 buf_len) > +{ > + int status = 0; > + u32 ix, reg_val = 0x1; > + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > + struct MXL_EAGLE_DEV_STATUS_T *dev_status; > + > + if (buf_len < MXL_EAGLE_FW_HEADER_SIZE || > + buf_len > MXL_EAGLE_FW_MAX_SIZE_IN_KB * 1000) > + return -EINVAL; > + > + mutex_lock(&dev->i2c_lock); > + > + dev_dbg(&dev->i2c_client->dev, "\n"); > + > + status = mxl692_validate_fw_header(firmware_buf, buf_len); > + if (status) > + goto err_finish; > + > + ix = 16; > + status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* DRAM */ > + if (status) > + goto err_finish; > + > + status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* IRAM */ > + if (status) > + goto err_finish; > + > + /* release CPU from reset */ > + status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + mutex_unlock(&dev->i2c_lock); > + > + if (status == 0) { > + /* verify FW is alive */ > + usleep_range(MXL_EAGLE_FW_LOAD_TIME * 1000, (MXL_EAGLE_FW_LOAD_TIME + 5) * 1000); > + dev_status = (struct MXL_EAGLE_DEV_STATUS_T *)&rx_buf; > + status = mxl692_i2c_writeread(dev, > + MXL_EAGLE_OPCODE_DEVICE_STATUS_GET, > + NULL, > + 0, > + (u8 *)dev_status, > + sizeof(struct MXL_EAGLE_DEV_STATUS_T)); > + } > + > + return status; > +err_finish: > + mutex_unlock(&dev->i2c_lock); > + if (status) > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > + return status; > +} > + > +static int mxl692_get_versions(struct mxl692_dev *dev) > +{ > + int status = 0; > + struct MXL_EAGLE_DEV_VER_T dev_ver = {}; > + static const char * const chip_id[] = {"N/A", "691", "248", "692"}; > + > + status = mxl692_i2c_writeread(dev, MXL_EAGLE_OPCODE_DEVICE_VERSION_GET, > + NULL, > + 0, > + (u8 *)&dev_ver, > + sizeof(struct MXL_EAGLE_DEV_VER_T)); > + if (status) > + return status; > + > + dev_info(&dev->i2c_client->dev, "MxL692_DEMOD Chip ID: %s\n", > + chip_id[dev_ver.chip_id]); > + > + dev_info(&dev->i2c_client->dev, > + "MxL692_DEMOD FW Version: %d.%d.%d.%d_RC%d\n", > + dev_ver.firmware_ver[0], > + dev_ver.firmware_ver[1], > + dev_ver.firmware_ver[2], > + dev_ver.firmware_ver[3], > + dev_ver.firmware_ver[4]); > + > + return status; > +} > + > +static int mxl692_reset(struct mxl692_dev *dev) > +{ > + int status = 0; > + u32 dev_type = MXL_EAGLE_DEVICE_MAX, reg_val = 0x2; > + > + dev_dbg(&dev->i2c_client->dev, "\n"); > + > + /* legacy i2c override */ > + status = mxl692_memwrite(dev, 0x80000100, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + /* verify sku */ > + status = mxl692_memread(dev, 0x70000188, (u8 *)&dev_type, sizeof(u32)); > + if (status) > + goto err_finish; > + > + if (dev_type != dev->device_type) > + goto err_finish; > + > +err_finish: > + if (status) > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > + return status; > +} > + > +static int mxl692_config_regulators(struct mxl692_dev *dev, > + enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E power_supply) > +{ > + int status = 0; > + u32 reg_val; > + > + dev_dbg(&dev->i2c_client->dev, "\n"); > + > + /* configure main regulator according to the power supply source */ > + status = mxl692_memread(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + reg_val &= 0x00FFFFFF; > + reg_val |= (power_supply == MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE) ? > + 0x14000000 : 0x10000000; > + > + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + /* configure digital regulator to high current mode */ > + status = mxl692_memread(dev, 0x90000018, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + reg_val |= 0x800; > + > + status = mxl692_memwrite(dev, 0x90000018, (u8 *)®_val, sizeof(u32)); > + > +err_finish: > + if (status) > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > + return status; > +} > + > +static int mxl692_config_xtal(struct mxl692_dev *dev, > + struct MXL_EAGLE_DEV_XTAL_T *dev_xtal) > +{ > + int status = 0; > + u32 reg_val, reg_val1; > + > + dev_dbg(&dev->i2c_client->dev, "\n"); > + > + status = mxl692_memread(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + /* set XTAL capacitance */ > + reg_val &= 0xFFFFFFE0; > + reg_val |= dev_xtal->xtal_cap; > + > + /* set CLK OUT */ > + reg_val = dev_xtal->clk_out_enable ? > + (reg_val | 0x0100) : (reg_val & 0xFFFFFEFF); > + > + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + /* set CLK OUT divider */ > + reg_val = dev_xtal->clk_out_div_enable ? > + (reg_val | 0x0200) : (reg_val & 0xFFFFFDFF); > + > + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + /* set XTAL sharing */ > + reg_val = dev_xtal->xtal_sharing_enable ? > + (reg_val | 0x010400) : (reg_val & 0xFFFEFBFF); > + > + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + /* enable/disable XTAL calibration, based on master/slave device */ > + status = mxl692_memread(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); > + if (status) > + goto err_finish; > + > + if (dev_xtal->xtal_calibration_enable) { > + /* enable XTAL calibration and set XTAL amplitude to a higher value */ > + reg_val1 &= 0xFFFFFFFD; > + reg_val1 |= 0x30; > + > + status = mxl692_memwrite(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); > + if (status) > + goto err_finish; > + } else { > + /* disable XTAL calibration */ > + reg_val1 |= 0x2; > + > + status = mxl692_memwrite(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); > + if (status) > + goto err_finish; > + > + /* set XTAL bias value */ > + status = mxl692_memread(dev, 0x9000002c, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + reg_val &= 0xC0FFFFFF; > + reg_val |= 0xA000000; > + > + status = mxl692_memwrite(dev, 0x9000002c, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + } > + > + /* start XTAL calibration */ > + status = mxl692_memread(dev, 0x70000010, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + reg_val |= 0x8; > + > + status = mxl692_memwrite(dev, 0x70000010, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + status = mxl692_memread(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + reg_val |= 0x10; > + > + status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + status = mxl692_memread(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + reg_val &= 0xFFFFEFFF; > + > + status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + reg_val |= 0x1000; > + > + status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); > + if (status) > + goto err_finish; > + > + usleep_range(45000, 55000); > + > +err_finish: > + if (status) > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > + return status; > +} > + > +static int mxl692_powermode(struct mxl692_dev *dev, > + enum MXL_EAGLE_POWER_MODE_E power_mode) > +{ > + int status = 0; > + > + dev_dbg(&dev->i2c_client->dev, "%s\n", > + power_mode == MXL_EAGLE_POWER_MODE_SLEEP ? > + "sleep" : "active"); > + > + status = mxl692_i2c_writeread(dev, > + MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET, > + (u8 *)&power_mode, > + sizeof(u8), > + NULL, > + 0); > + if (status) > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > + > + dev->power_mode = power_mode; > + > + return status; > +} > + > +static int mxl692_init(struct dvb_frontend *fe) > +{ > + struct mxl692_dev *dev = fe->demodulator_priv; > + struct i2c_client *client = dev->i2c_client; > + struct dtv_frontend_properties *c = &fe->dtv_property_cache; > + int status = 0; > + const struct firmware *firmware; > + struct MXL_EAGLE_DEV_XTAL_T xtal_config = {}; > + > + dev_dbg(&dev->i2c_client->dev, "\n"); > + > + if (dev->init_done) > + goto warm; > + > + dev->seqnum = 1; > + > + status = mxl692_reset(dev); > + if (status) > + goto err; > + > + usleep_range(50 * 1000, 60 * 1000); /* was 1000! */ > + > + status = mxl692_config_regulators(dev, MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL); > + if (status) > + goto err; > + > + xtal_config.xtal_cap = 26; > + xtal_config.clk_out_div_enable = 0; > + xtal_config.clk_out_enable = 0; > + xtal_config.xtal_calibration_enable = 0; > + xtal_config.xtal_sharing_enable = 1; > + status = mxl692_config_xtal(dev, &xtal_config); > + if (status) > + goto err; > + > + status = request_firmware(&firmware, MXL692_FIRMWARE, &client->dev); > + if (status) { > + dev_dbg(&dev->i2c_client->dev, "firmware missing? %s\n", > + MXL692_FIRMWARE); > + goto err; > + } > + > + status = mxl692_fwdownload(dev, firmware->data, firmware->size); > + if (status) > + goto err_release_firmware; > + > + release_firmware(firmware); > + > +// usleep_range(500 * 1000, 510 * 1000); /* was 1000! */ Why is this commented out? This should either be explained or removed. > + status = mxl692_get_versions(dev); > + if (status) > + goto err; > + > + dev->power_mode = MXL_EAGLE_POWER_MODE_SLEEP; > + > +warm: > + /* Init stats here to indicate which stats are supported */ > + c->cnr.len = 1; > + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > + c->post_bit_error.len = 1; > + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > + c->post_bit_count.len = 1; > + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > + c->block_error.len = 1; > + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > + > + dev->init_done = 1; > + return 0; > +err_release_firmware: > + release_firmware(firmware); > +err: > + return status; > +} > + > +static int mxl692_sleep(struct dvb_frontend *fe) > +{ > + struct mxl692_dev *dev = fe->demodulator_priv; > + > + if (dev->power_mode != MXL_EAGLE_POWER_MODE_SLEEP) > + mxl692_powermode(dev, MXL_EAGLE_POWER_MODE_SLEEP); > + > + return 0; > +} > + > +static int mxl692_set_frontend(struct dvb_frontend *fe) > +{ > + struct dtv_frontend_properties *p = &fe->dtv_property_cache; > + struct mxl692_dev *dev = fe->demodulator_priv; > + > + int status = 0; > + enum MXL_EAGLE_DEMOD_TYPE_E demod_type; > + enum MXL_EAGLE_POWER_MODE_E power_mode = MXL_EAGLE_POWER_MODE_ACTIVE; > + struct MXL_EAGLE_MPEGOUT_PARAMS_T mpeg_params = {}; > + enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E qam_annex = MXL_EAGLE_QAM_DEMOD_ANNEX_B; > + struct MXL_EAGLE_QAM_DEMOD_PARAMS_T qam_params = {}; > + struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T tuner_params = {}; > + > + dev_dbg(&dev->i2c_client->dev, "\n"); > + > + switch (p->modulation) { > + case VSB_8: > + demod_type = MXL_EAGLE_DEMOD_TYPE_ATSC; > + break; > + case QAM_AUTO: > + case QAM_64: > + case QAM_128: > + case QAM_256: > + demod_type = MXL_EAGLE_DEMOD_TYPE_QAM; > + break; > + default: > + return -EINVAL; > + } > + > + if (dev->current_frequency == p->frequency && dev->demod_type == demod_type) > + return 0; > + > + dev->current_frequency = -1; > + dev->demod_type = -1; > + > + status = mxl692_i2c_writeread(dev, > + MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET, > + (u8 *)&demod_type, > + sizeof(u8), > + NULL, > + 0); > + if (status) { > + dev_dbg(&dev->i2c_client->dev, "DEVICE_DEMODULATOR_TYPE_SET...FAIL %d\n", status); > + goto err; > + } > + > + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ > + > + //Config Device Power Mode > + if (dev->power_mode != MXL_EAGLE_POWER_MODE_ACTIVE) { > + status = mxl692_powermode(dev, power_mode); > + if (status) > + goto err; > + > + usleep_range(50 * 1000, 60 * 1000); /* was 500! */ > + } > + > + mpeg_params.mpeg_parallel = 0; > + mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_MSB_1ST; > + mpeg_params.mpeg_sync_pulse_width = MXL_EAGLE_DATA_SYNC_WIDTH_BIT; > + mpeg_params.mpeg_valid_pol = MXL_EAGLE_CLOCK_POSITIVE; > + mpeg_params.mpeg_sync_pol = MXL_EAGLE_CLOCK_POSITIVE; > + mpeg_params.mpeg_clk_pol = MXL_EAGLE_CLOCK_NEGATIVE; > + mpeg_params.mpeg3wire_mode_enable = 0; > + mpeg_params.mpeg_clk_freq = MXL_EAGLE_MPEG_CLOCK_27MHZ; > + > + switch (demod_type) { > + case MXL_EAGLE_DEMOD_TYPE_ATSC: > + status = mxl692_i2c_writeread(dev, > + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, > + (u8 *)&mpeg_params, > + sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), > + NULL, > + 0); > + if (status) > + goto err; > + break; > + case MXL_EAGLE_DEMOD_TYPE_QAM: > + if (qam_annex == MXL_EAGLE_QAM_DEMOD_ANNEX_A) > + mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_LSB_1ST; > + status = mxl692_i2c_writeread(dev, > + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, > + (u8 *)&mpeg_params, > + sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), > + NULL, > + 0); > + if (status) > + goto err; > + > + qam_params.annex_type = qam_annex; > + qam_params.qam_type = MXL_EAGLE_QAM_DEMOD_AUTO; > + qam_params.iq_flip = MXL_EAGLE_DEMOD_IQ_AUTO; > + if (p->modulation == QAM_64) > + qam_params.symbol_rate_hz = 5057000; > + else > + qam_params.symbol_rate_hz = 5361000; > + > + qam_params.symbol_rate_256qam_hz = 5361000; > + > + status = mxl692_i2c_writeread(dev, > + MXL_EAGLE_OPCODE_QAM_PARAMS_SET, > + (u8 *)&qam_params, > + sizeof(struct MXL_EAGLE_QAM_DEMOD_PARAMS_T), > + NULL, 0); > + if (status) > + goto err; > + > + break; > + default: > + break; > + } > + > + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ > + > + tuner_params.freq_hz = p->frequency; > + tuner_params.bandwidth = MXL_EAGLE_TUNER_BW_6MHZ; > + tuner_params.tune_mode = MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW; > + > + dev_dbg(&dev->i2c_client->dev, " Tuning Freq: %d %s\n", tuner_params.freq_hz, > + demod_type == MXL_EAGLE_DEMOD_TYPE_ATSC ? "ATSC" : "QAM"); > + > + status = mxl692_i2c_writeread(dev, > + MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET, > + (u8 *)&tuner_params, > + sizeof(struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T), > + NULL, > + 0); > + if (status) > + goto err; > + > + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ > + > + switch (demod_type) { > + case MXL_EAGLE_DEMOD_TYPE_ATSC: > + status = mxl692_i2c_writeread(dev, > + MXL_EAGLE_OPCODE_ATSC_INIT_SET, > + NULL, 0, NULL, 0); > + if (status) > + goto err; > + break; > + case MXL_EAGLE_DEMOD_TYPE_QAM: > + status = mxl692_i2c_writeread(dev, > + MXL_EAGLE_OPCODE_QAM_RESTART_SET, > + NULL, 0, NULL, 0); > + if (status) > + goto err; > + break; > + default: > + break; > + } > + > + dev->demod_type = demod_type; > + dev->current_frequency = p->frequency; > +err: > + return 0; > +} > + > +static int mxl692_get_frontend(struct dvb_frontend *fe, > + struct dtv_frontend_properties *p) > +{ > + struct dtv_frontend_properties *c = &fe->dtv_property_cache; > + > + p->modulation = c->modulation; > + p->frequency = c->frequency; > + > + return 0; > +} > + > +static int mxl692_read_snr(struct dvb_frontend *fe, u16 *snr) > +{ > + struct mxl692_dev *dev = fe->demodulator_priv; > + struct dtv_frontend_properties *c = &fe->dtv_property_cache; > + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > + struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; > + struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; > + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; > + int mxl_status = 0; > + > + *snr = 0; > + > + dev_dbg(&dev->i2c_client->dev, "\n"); > + > + atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; > + qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; It might be nicer to use a union rather casting, for all of these (including below). > + > + switch (demod_type) { > + case MXL_EAGLE_DEMOD_TYPE_ATSC: > + mxl_status = mxl692_i2c_writeread(dev, > + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, > + NULL, > + 0, > + rx_buf, > + sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); > + if (!mxl_status) { > + *snr = (u16)(atsc_status->snr_db_tenths / 10); > + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; > + c->cnr.stat[0].svalue = *snr; > + } > + break; > + case MXL_EAGLE_DEMOD_TYPE_QAM: > + mxl_status = mxl692_i2c_writeread(dev, > + MXL_EAGLE_OPCODE_QAM_STATUS_GET, > + NULL, > + 0, > + rx_buf, > + sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); > + if (!mxl_status) > + *snr = (u16)(qam_status->snr_db_tenths / 10); > + break; > + case MXL_EAGLE_DEMOD_TYPE_OOB: > + default: > + break; > + } > + return 0; > +} > + > +static int mxl692_read_ber_ucb(struct dvb_frontend *fe) > +{ > + struct mxl692_dev *dev = fe->demodulator_priv; > + struct dtv_frontend_properties *c = &fe->dtv_property_cache; > + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > + struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T *qam_errors; > + struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *atsc_errors; > + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; > + int mxl_status = 0; > + u32 utmp; > + > + dev_dbg(&dev->i2c_client->dev, "\n"); > + > + qam_errors = (struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T *)&rx_buf; > + atsc_errors = (struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *)&rx_buf; > + > + switch (demod_type) { > + case MXL_EAGLE_DEMOD_TYPE_ATSC: > + mxl_status = mxl692_i2c_writeread(dev, > + MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, > + NULL, > + 0, > + rx_buf, > + sizeof(struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T)); > + if (!mxl_status) { > + if (atsc_errors->error_packets == 0) > + utmp = 0; > + else > + utmp = ((atsc_errors->error_bytes / atsc_errors->error_packets) * > + atsc_errors->total_packets); > + /* ber */ > + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; > + c->post_bit_error.stat[0].uvalue += atsc_errors->error_bytes; > + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; > + c->post_bit_count.stat[0].uvalue += utmp; > + /* ucb */ > + c->block_error.stat[0].scale = FE_SCALE_COUNTER; > + c->block_error.stat[0].uvalue += atsc_errors->error_packets; > + > + dev_dbg(&dev->i2c_client->dev, "%llu %llu\n", > + c->post_bit_count.stat[0].uvalue, c->block_error.stat[0].uvalue); > + } > + break; > + case MXL_EAGLE_DEMOD_TYPE_QAM: > + case MXL_EAGLE_DEMOD_TYPE_OOB: > + default: > + break; > + } > + return 0; > +} > + > +static int mxl692_read_status(struct dvb_frontend *fe, > + enum fe_status *status) > +{ > + struct mxl692_dev *dev = fe->demodulator_priv; > + struct dtv_frontend_properties *c = &fe->dtv_property_cache; > + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > + struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; > + struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; > + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; > + int mxl_status = 0; > + *status = 0; > + > + dev_dbg(&dev->i2c_client->dev, "\n"); > + > + atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; > + qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; > + > + switch (demod_type) { > + case MXL_EAGLE_DEMOD_TYPE_ATSC: > + mxl_status = mxl692_i2c_writeread(dev, > + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, > + NULL, > + 0, > + rx_buf, > + sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); > + if (!mxl_status && atsc_status->atsc_lock) { > + *status |= FE_HAS_SIGNAL; > + *status |= FE_HAS_CARRIER; > + *status |= FE_HAS_VITERBI; > + *status |= FE_HAS_SYNC; > + *status |= FE_HAS_LOCK; > + > + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; > + c->cnr.stat[0].svalue = atsc_status->snr_db_tenths / 10; > + } > + break; > + case MXL_EAGLE_DEMOD_TYPE_QAM: > + mxl_status = mxl692_i2c_writeread(dev, > + MXL_EAGLE_OPCODE_QAM_STATUS_GET, > + NULL, > + 0, > + rx_buf, > + sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); > + if (!mxl_status && qam_status->qam_locked) { > + *status |= FE_HAS_SIGNAL; > + *status |= FE_HAS_CARRIER; > + *status |= FE_HAS_VITERBI; > + *status |= FE_HAS_SYNC; > + *status |= FE_HAS_LOCK; > + > + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; > + c->cnr.stat[0].svalue = qam_status->snr_db_tenths / 10; > + } > + break; > + case MXL_EAGLE_DEMOD_TYPE_OOB: > + default: > + break; > + } > + > + if ((*status & FE_HAS_LOCK) == 0) { > + /* No lock, reset all statistics */ > + c->cnr.len = 1; > + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > + return 0; > + } > + > + if (mxl_status) > + dev_err(&dev->i2c_client->dev, "err %d\n", mxl_status); > + else > + mxl_status = mxl692_read_ber_ucb(fe); > + > + return mxl_status; > +} > + > +static const struct dvb_frontend_ops mxl692_ops = { > + .delsys = { SYS_ATSC }, > + .info = { > + .name = "MaxLinear MxL692 VSB tuner-demodulator", > + .frequency_min_hz = 54000000, > + .frequency_max_hz = 858000000, > + .frequency_stepsize_hz = 62500, > + .caps = FE_CAN_8VSB > + }, > + > + .init = mxl692_init, > + .sleep = mxl692_sleep, > + .set_frontend = mxl692_set_frontend, > + .get_frontend = mxl692_get_frontend, > + > + .read_status = mxl692_read_status, > + .read_snr = mxl692_read_snr, > +}; > + > +static int mxl692_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct mxl692_config *config = client->dev.platform_data; > + struct mxl692_dev *dev; > + int ret = 0; > + > + dev = kzalloc(sizeof(*dev), GFP_KERNEL); > + if (!dev) { > + ret = -ENOMEM; > + dev_err(&client->dev, "kzalloc() failed\n"); > + goto err; > + } > + > + memcpy(&dev->fe.ops, &mxl692_ops, sizeof(struct dvb_frontend_ops)); > + dev->fe.demodulator_priv = dev; > + dev->i2c_client = client; > + *config->fe = &dev->fe; > + mutex_init(&dev->i2c_lock); > + i2c_set_clientdata(client, dev); > + > + dev_info(&client->dev, "MaxLinear mxl692 successfully attached\n"); > + detect_endianness(); > + > + return 0; > +err: > + dev_err(&client->dev, "failed %d\n", ret); > + return -ENODEV; > +} > + > +static int mxl692_remove(struct i2c_client *client) > +{ > + struct mxl692_dev *dev = i2c_get_clientdata(client); > + > + dev->fe.demodulator_priv = NULL; > + i2c_set_clientdata(client, NULL); > + kfree(dev); > + > + return 0; > +} > + > +static const struct i2c_device_id mxl692_id_table[] = { > + {"mxl692", 0}, > + {} > +}; > +MODULE_DEVICE_TABLE(i2c, mxl692_id_table); > + > +static struct i2c_driver mxl692_driver = { > + .driver = { > + .owner = THIS_MODULE, > + .name = "mxl692", > + }, > + .probe = mxl692_probe, > + .remove = mxl692_remove, > + .id_table = mxl692_id_table, > +}; > + > +module_i2c_driver(mxl692_driver); > + > +MODULE_AUTHOR("Brad Love <brad@nextdimension.cc>"); > +MODULE_DESCRIPTION("MaxLinear MxL692 demodulator/tuner driver"); > +MODULE_FIRMWARE(MXL692_FIRMWARE); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/media/dvb-frontends/mxl692.h b/drivers/media/dvb-frontends/mxl692.h > new file mode 100644 > index 000000000000..45bc48f1f12f > --- /dev/null > +++ b/drivers/media/dvb-frontends/mxl692.h > @@ -0,0 +1,38 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Driver for the MaxLinear MxL69x family of tuners/demods > + * > + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> > + * > + * based on code: > + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved > + * which was released under GPL V2 > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#ifndef _MXL692_H_ > +#define _MXL692_H_ > + > +#include <media/dvb_frontend.h> > + > +#define MXL692_FIRMWARE "dvb-demod-mxl692.fw" > + > +struct mxl692_config { > + unsigned char id; > + u8 i2c_addr; > + /* > + * frontend > + * returned by driver > + */ > + struct dvb_frontend **fe; > +}; > + > +#endif /* _MXL692_H_ */ > diff --git a/drivers/media/dvb-frontends/mxl692_defs.h b/drivers/media/dvb-frontends/mxl692_defs.h > new file mode 100644 > index 000000000000..97fd18ae81ff > --- /dev/null > +++ b/drivers/media/dvb-frontends/mxl692_defs.h > @@ -0,0 +1,493 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Driver for the MaxLinear MxL69x family of combo tuners/demods > + * > + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> > + * > + * based on code: > + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved > + * which was released under GPL V2 > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +//#define __packed __attribute__((packed)) Comment not needed. > + > +/***************************************************************************** > + * Defines > + ***************************************************************************** > + */ > +#define MXL_EAGLE_HOST_MSG_HEADER_SIZE 8 > +#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76 > +#define MXL_EAGLE_QAM_FFE_TAPS_LENGTH 16 > +#define MXL_EAGLE_QAM_SPUR_TAPS_LENGTH 32 > +#define MXL_EAGLE_QAM_DFE_TAPS_LENGTH 72 > +#define MXL_EAGLE_ATSC_FFE_TAPS_LENGTH 4096 > +#define MXL_EAGLE_ATSC_DFE_TAPS_LENGTH 384 > +#define MXL_EAGLE_VERSION_SIZE 5 /* A.B.C.D-RCx */ > +#define MXL_EAGLE_FW_LOAD_TIME 50 > + > +#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76 > +#define MXL_EAGLE_FW_HEADER_SIZE 16 > +#define MXL_EAGLE_FW_SEGMENT_HEADER_SIZE 8 > +#define MXL_EAGLE_MAX_I2C_PACKET_SIZE 58 > +#define MXL_EAGLE_I2C_MHEADER_SIZE 6 > +#define MXL_EAGLE_I2C_PHEADER_SIZE 2 > + > +/* Enum of Eagle family devices */ > +enum MXL_EAGLE_DEVICE_E { > + MXL_EAGLE_DEVICE_691 = 1, /* Device Mxl691 */ > + MXL_EAGLE_DEVICE_248 = 2, /* Device Mxl248 */ > + MXL_EAGLE_DEVICE_692 = 3, /* Device Mxl692 */ > + MXL_EAGLE_DEVICE_MAX, /* No such device */ > +}; > + > +#define VER_A 1 > +#define VER_B 1 > +#define VER_C 1 > +#define VER_D 3 > +#define VER_E 6 > + > +/* Enum of Host to Eagle I2C protocol opcodes */ > +enum MXL_EAGLE_OPCODE_E { > + /* DEVICE */ > + MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET, > + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, > + MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET, > + MXL_EAGLE_OPCODE_DEVICE_GPIO_DIRECTION_SET, > + MXL_EAGLE_OPCODE_DEVICE_GPO_LEVEL_SET, > + MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET, > + MXL_EAGLE_OPCODE_DEVICE_IO_MUX_SET, > + MXL_EAGLE_OPCODE_DEVICE_VERSION_GET, > + MXL_EAGLE_OPCODE_DEVICE_STATUS_GET, > + MXL_EAGLE_OPCODE_DEVICE_GPI_LEVEL_GET, > + > + /* TUNER */ > + MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET, > + MXL_EAGLE_OPCODE_TUNER_LOCK_STATUS_GET, > + MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET, > + > + /* ATSC */ > + MXL_EAGLE_OPCODE_ATSC_INIT_SET, > + MXL_EAGLE_OPCODE_ATSC_ACQUIRE_CARRIER_SET, > + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, > + MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, > + MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_DFE_TAPS_GET, > + MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET, > + > + /* QAM */ > + MXL_EAGLE_OPCODE_QAM_PARAMS_SET, > + MXL_EAGLE_OPCODE_QAM_RESTART_SET, > + MXL_EAGLE_OPCODE_QAM_STATUS_GET, > + MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET, > + MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET, > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET, > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET, > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET, > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_TAPS_NUMBER_GET, > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET, > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET, > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET, > + > + /* OOB */ > + MXL_EAGLE_OPCODE_OOB_PARAMS_SET, > + MXL_EAGLE_OPCODE_OOB_RESTART_SET, > + MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET, > + MXL_EAGLE_OPCODE_OOB_STATUS_GET, > + > + /* SMA */ > + MXL_EAGLE_OPCODE_SMA_INIT_SET, > + MXL_EAGLE_OPCODE_SMA_PARAMS_SET, > + MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET, > + MXL_EAGLE_OPCODE_SMA_RECEIVE_GET, > + > + /* DEBUG */ > + MXL_EAGLE_OPCODE_INTERNAL, > + > + MXL_EAGLE_OPCODE_MAX = 70, > +}; > + > +/* Enum of Callabck function types */ > +enum MXL_EAGLE_CB_TYPE_E { > + MXL_EAGLE_CB_FW_DOWNLOAD = 0, > +}; > + > +/* Enum of power supply types */ > +enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E { > + MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE, /* Single supply of 3.3V */ > + MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL, /* Dual supply, 1.8V & 3.3V */ > +}; > + > +/* Enum of I/O pad drive modes */ > +enum MXL_EAGLE_IO_MUX_DRIVE_MODE_E { > + MXL_EAGLE_IO_MUX_DRIVE_MODE_1X, > + MXL_EAGLE_IO_MUX_DRIVE_MODE_2X, > + MXL_EAGLE_IO_MUX_DRIVE_MODE_3X, > + MXL_EAGLE_IO_MUX_DRIVE_MODE_4X, > + MXL_EAGLE_IO_MUX_DRIVE_MODE_5X, > + MXL_EAGLE_IO_MUX_DRIVE_MODE_6X, > + MXL_EAGLE_IO_MUX_DRIVE_MODE_7X, > + MXL_EAGLE_IO_MUX_DRIVE_MODE_8X, > +}; > + > +/* Enum of demodulator types. Used for selection of demodulator > + * type in relevant devices, e.g. ATSC vs. QAM in Mxl691 > + */ > +enum MXL_EAGLE_DEMOD_TYPE_E { > + MXL_EAGLE_DEMOD_TYPE_QAM, /* Mxl248 or Mxl692 */ > + MXL_EAGLE_DEMOD_TYPE_OOB, /* Mxl248 only */ > + MXL_EAGLE_DEMOD_TYPE_ATSC /* Mxl691 or Mxl692 */ > +}; > + > +/* Enum of power modes. Used for initial > + * activation, or for activating sleep mode > + */ > +enum MXL_EAGLE_POWER_MODE_E { > + MXL_EAGLE_POWER_MODE_SLEEP, > + MXL_EAGLE_POWER_MODE_ACTIVE > +}; > + > +/* Enum of GPIOs, used in device GPIO APIs */ > +enum MXL_EAGLE_GPIO_NUMBER_E { > + MXL_EAGLE_GPIO_NUMBER_0, > + MXL_EAGLE_GPIO_NUMBER_1, > + MXL_EAGLE_GPIO_NUMBER_2, > + MXL_EAGLE_GPIO_NUMBER_3, > + MXL_EAGLE_GPIO_NUMBER_4, > + MXL_EAGLE_GPIO_NUMBER_5, > + MXL_EAGLE_GPIO_NUMBER_6 > +}; > + > +/* Enum of GPIO directions, used in GPIO direction configuration API */ > +enum MXL_EAGLE_GPIO_DIRECTION_E { > + MXL_EAGLE_GPIO_DIRECTION_INPUT, > + MXL_EAGLE_GPIO_DIRECTION_OUTPUT > +}; > + > +/* Enum of GPIO level, used in device GPIO APIs */ > +enum MXL_EAGLE_GPIO_LEVEL_E { > + MXL_EAGLE_GPIO_LEVEL_LOW, > + MXL_EAGLE_GPIO_LEVEL_HIGH, > +}; > + > +/* Enum of I/O Mux function, used in device I/O mux configuration API */ > +enum MXL_EAGLE_IOMUX_FUNCTION_E { > + MXL_EAGLE_IOMUX_FUNC_FEC_LOCK, > + MXL_EAGLE_IOMUX_FUNC_MERR, > +}; > + > +/* Enum of MPEG Data format, used in MPEG and OOB output configuration */ > +enum MXL_EAGLE_MPEG_DATA_FORMAT_E { > + MXL_EAGLE_DATA_SERIAL_LSB_1ST = 0, > + MXL_EAGLE_DATA_SERIAL_MSB_1ST, > + > + MXL_EAGLE_DATA_SYNC_WIDTH_BIT = 0, > + MXL_EAGLE_DATA_SYNC_WIDTH_BYTE > +}; > + > +/* Enum of MPEG Clock format, used in MPEG and OOB output configuration */ > +enum MXL_EAGLE_MPEG_CLOCK_FORMAT_E { > + MXL_EAGLE_CLOCK_ACTIVE_HIGH = 0, > + MXL_EAGLE_CLOCK_ACTIVE_LOW, > + > + MXL_EAGLE_CLOCK_POSITIVE = 0, > + MXL_EAGLE_CLOCK_NEGATIVE, > + > + MXL_EAGLE_CLOCK_IN_PHASE = 0, > + MXL_EAGLE_CLOCK_INVERTED, > +}; > + > +/* Enum of MPEG Clock speeds, used in MPEG output configuration */ > +enum MXL_EAGLE_MPEG_CLOCK_RATE_E { > + MXL_EAGLE_MPEG_CLOCK_54MHZ, > + MXL_EAGLE_MPEG_CLOCK_40_5MHZ, > + MXL_EAGLE_MPEG_CLOCK_27MHZ, > + MXL_EAGLE_MPEG_CLOCK_13_5MHZ, > +}; > + > +/* Enum of Interrupt mask bit, used in host interrupt configuration */ > +enum MXL_EAGLE_INTR_MASK_BITS_E { > + MXL_EAGLE_INTR_MASK_DEMOD = 0, > + MXL_EAGLE_INTR_MASK_SMA_RX = 1, > + MXL_EAGLE_INTR_MASK_WDOG = 31 > +}; > + > +/* Enum of QAM Demodulator type, used in QAM configuration */ > +enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E { > + MXL_EAGLE_QAM_DEMOD_ANNEX_B, /* J.83B */ > + MXL_EAGLE_QAM_DEMOD_ANNEX_A, /* DVB-C */ > +}; > + > +/* Enum of QAM Demodulator modulation, used in QAM configuration and status */ > +enum MXL_EAGLE_QAM_DEMOD_QAM_TYPE_E { > + MXL_EAGLE_QAM_DEMOD_QAM16, > + MXL_EAGLE_QAM_DEMOD_QAM64, > + MXL_EAGLE_QAM_DEMOD_QAM256, > + MXL_EAGLE_QAM_DEMOD_QAM1024, > + MXL_EAGLE_QAM_DEMOD_QAM32, > + MXL_EAGLE_QAM_DEMOD_QAM128, > + MXL_EAGLE_QAM_DEMOD_QPSK, > + MXL_EAGLE_QAM_DEMOD_AUTO, > +}; > + > +/* Enum of Demodulator IQ setup, used in QAM, OOB configuration and status */ > +enum MXL_EAGLE_IQ_FLIP_E { > + MXL_EAGLE_DEMOD_IQ_NORMAL, > + MXL_EAGLE_DEMOD_IQ_FLIPPED, > + MXL_EAGLE_DEMOD_IQ_AUTO, > +}; > + > +/* Enum of OOB Demodulator symbol rates, used in OOB configuration */ > +enum MXL_EAGLE_OOB_DEMOD_SYMB_RATE_E { > + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_0_772MHZ, /* ANSI/SCTE 55-2 0.772 MHz */ > + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_024MHZ, /* ANSI/SCTE 55-1 1.024 MHz */ > + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_544MHZ, /* ANSI/SCTE 55-2 1.544 MHz */ > +}; > + > +/* Enum of tuner channel tuning mode */ > +enum MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_E { > + MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW, /* Normal "view" mode */ > + MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_SCAN, /* Fast "scan" mode */ > +}; > + > +/* Enum of tuner bandwidth */ > +enum MXL_EAGLE_TUNER_BW_E { > + MXL_EAGLE_TUNER_BW_6MHZ, > + MXL_EAGLE_TUNER_BW_7MHZ, > + MXL_EAGLE_TUNER_BW_8MHZ, > +}; > + > +/* Enum of tuner bandwidth */ > +enum MXL_EAGLE_JUNCTION_TEMPERATURE_E { > + MXL_EAGLE_JUNCTION_TEMPERATURE_BELOW_0_CELSIUS = 0, > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_0_TO_14_CELSIUS = 1, > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_14_TO_28_CELSIUS = 3, > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_28_TO_42_CELSIUS = 2, > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_42_TO_57_CELSIUS = 6, > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_57_TO_71_CELSIUS = 7, > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_71_TO_85_CELSIUS = 5, > + MXL_EAGLE_JUNCTION_TEMPERATURE_ABOVE_85_CELSIUS = 4, > +}; > + > +/* Struct passed in optional callback used during FW download */ > +struct MXL_EAGLE_FW_DOWNLOAD_CB_PAYLOAD_T { > + u32 total_len; > + u32 downloaded_len; > +}; > + > +/* Struct used of I2C protocol between host and Eagle, internal use only */ > +struct __packed MXL_EAGLE_HOST_MSG_HEADER_T { > + u8 opcode; > + u8 seqnum; > + u8 payload_size; > + u8 status; > + u32 checksum; > +}; > + > +/* Device version information struct */ > +struct __packed MXL_EAGLE_DEV_VER_T { > + u8 chip_id; > + u8 firmware_ver[MXL_EAGLE_VERSION_SIZE]; > + u8 mxlware_ver[MXL_EAGLE_VERSION_SIZE]; > +}; > + > +/* Xtal configuration struct */ > +struct __packed MXL_EAGLE_DEV_XTAL_T { > + u8 xtal_cap; /* accepted range is 1..31 pF. Default is 26 */ > + u8 clk_out_enable; > + u8 clk_out_div_enable; /* clock out freq is xtal freq / 6 */ > + u8 xtal_sharing_enable; /* if enabled set xtal_cap to 25 pF */ > + u8 xtal_calibration_enable; /* enable for master, disable for slave */ > +}; > + > +/* GPIO direction struct, internally used in GPIO configuration API */ > +struct __packed MXL_EAGLE_DEV_GPIO_DIRECTION_T { > + u8 gpio_number; > + u8 gpio_direction; > +}; > + > +/* GPO level struct, internally used in GPIO configuration API */ > +struct __packed MXL_EAGLE_DEV_GPO_LEVEL_T { > + u8 gpio_number; > + u8 gpo_level; > +}; > + > +/* Device Status struct */ > +struct MXL_EAGLE_DEV_STATUS_T { > + u8 temperature; > + u8 demod_type; > + u8 power_mode; > + u8 cpu_utilization_percent; > +}; > + > +/* Device interrupt configuration struct */ > +struct __packed MXL_EAGLE_DEV_INTR_CFG_T { > + u32 intr_mask; > + u8 edge_trigger; > + u8 positive_trigger; > + u8 global_enable_interrupt; > +}; > + > +/* MPEG pad drive parameters, used on MPEG output configuration */ > +/* See MXL_EAGLE_IO_MUX_DRIVE_MODE_E */ > +struct MXL_EAGLE_MPEG_PAD_DRIVE_T { > + u8 pad_drv_mpeg_syn; > + u8 pad_drv_mpeg_dat; > + u8 pad_drv_mpeg_val; > + u8 pad_drv_mpeg_clk; > +}; > + > +/* MPEGOUT parameter struct, used in MPEG output configuration */ > +struct MXL_EAGLE_MPEGOUT_PARAMS_T { > + u8 mpeg_parallel; > + u8 msb_first; > + u8 mpeg_sync_pulse_width; /* See MXL_EAGLE_MPEG_DATA_FORMAT_E */ > + u8 mpeg_valid_pol; > + u8 mpeg_sync_pol; > + u8 mpeg_clk_pol; > + u8 mpeg3wire_mode_enable; > + u8 mpeg_clk_freq; > + struct MXL_EAGLE_MPEG_PAD_DRIVE_T mpeg_pad_drv; > +}; > + > +/* QAM Demodulator parameters struct, used in QAM params configuration */ > +struct __packed MXL_EAGLE_QAM_DEMOD_PARAMS_T { > + u8 annex_type; > + u8 qam_type; > + u8 iq_flip; > + u8 search_range_idx; > + u8 spur_canceller_enable; > + u32 symbol_rate_hz; > + u32 symbol_rate_256qam_hz; > +}; > + > +/* QAM Demodulator status */ > +struct MXL_EAGLE_QAM_DEMOD_STATUS_T { > + u8 annex_type; > + u8 qam_type; > + u8 iq_flip; > + u8 interleaver_depth_i; > + u8 interleaver_depth_j; > + u8 qam_locked; > + u8 fec_locked; > + u8 mpeg_locked; > + u16 snr_db_tenths; > + s16 timing_offset; > + s32 carrier_offset_hz; > +}; > + > +/* QAM Demodulator error counters */ > +struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T { > + u32 corrected_code_words; > + u32 uncorrected_code_words; > + u32 total_code_words_received; > + u32 corrected_bits; > + u32 error_mpeg_frames; > + u32 mpeg_frames_received; > + u32 erasures; > +}; > + > +/* QAM Demodulator constellation point */ > +struct MXL_EAGLE_QAM_DEMOD_CONSTELLATION_VAL_T { > + s16 i_value[12]; > + s16 q_value[12]; > +}; > + > +/* QAM Demodulator equalizer filter taps */ > +struct MXL_EAGLE_QAM_DEMOD_EQU_FILTER_T { > + s16 ffe_taps[MXL_EAGLE_QAM_FFE_TAPS_LENGTH]; > + s16 spur_taps[MXL_EAGLE_QAM_SPUR_TAPS_LENGTH]; > + s16 dfe_taps[MXL_EAGLE_QAM_DFE_TAPS_LENGTH]; > + u8 ffe_leading_tap_index; > + u8 dfe_taps_number; > +}; > + > +/* OOB Demodulator parameters struct, used in OOB params configuration */ > +struct __packed MXL_EAGLE_OOB_DEMOD_PARAMS_T { > + u8 symbol_rate; > + u8 iq_flip; > + u8 clk_pol; > +}; > + > +/* OOB Demodulator error counters */ > +struct MXL_EAGLE_OOB_DEMOD_ERROR_COUNTERS_T { > + u32 corrected_packets; > + u32 uncorrected_packets; > + u32 total_packets_received; > +}; > + > +/* OOB status */ > +struct __packed MXL_EAGLE_OOB_DEMOD_STATUS_T { > + u16 snr_db_tenths; > + s16 timing_offset; > + s32 carrier_offsetHz; > + u8 qam_locked; > + u8 fec_locked; > + u8 mpeg_locked; > + u8 retune_required; > + u8 iq_flip; > +}; > + > +/* ATSC Demodulator status */ > +struct __packed MXL_EAGLE_ATSC_DEMOD_STATUS_T { > + s16 snr_db_tenths; > + s16 timing_offset; > + s32 carrier_offset_hz; > + u8 frame_lock; > + u8 atsc_lock; > + u8 fec_lock; > +}; > + > +/* ATSC Demodulator error counters */ > +struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T { > + u32 error_packets; > + u32 total_packets; > + u32 error_bytes; > +}; > + > +/* ATSC Demodulator equalizers filter taps */ > +struct __packed MXL_EAGLE_ATSC_DEMOD_EQU_FILTER_T { > + s16 ffe_taps[MXL_EAGLE_ATSC_FFE_TAPS_LENGTH]; > + s8 dfe_taps[MXL_EAGLE_ATSC_DFE_TAPS_LENGTH]; > +}; > + > +/* Tuner AGC Status */ > +struct __packed MXL_EAGLE_TUNER_AGC_STATUS_T { > + u8 locked; > + u16 raw_agc_gain; /* AGC gain [dB] = rawAgcGain / 2^6 */ > + s16 rx_power_db_hundredths; > +}; > + > +/* Tuner channel tune parameters */ > +struct __packed MXL_EAGLE_TUNER_CHANNEL_PARAMS_T { > + u32 freq_hz; > + u8 tune_mode; > + u8 bandwidth; > +}; > + > +/* Tuner channel lock indications */ > +struct __packed MXL_EAGLE_TUNER_LOCK_STATUS_T { > + u8 rf_pll_locked; > + u8 ref_pll_locked; > +}; > + > +/* Smart antenna parameters used in Smart antenna params configuration */ > +struct __packed MXL_EAGLE_SMA_PARAMS_T { > + u8 full_duplex_enable; > + u8 rx_disable; > + u8 idle_logic_high; > +}; > + > +/* Smart antenna message format */ > +struct __packed MXL_EAGLE_SMA_MESSAGE_T { > + u32 payload_bits; > + u8 total_num_bits; > +}; > + > -- > 2.23.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver 2020-06-24 9:59 ` [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver Sean Young @ 2020-06-26 17:41 ` Bradford Love 2020-06-29 17:33 ` Sean Young 0 siblings, 1 reply; 20+ messages in thread From: Bradford Love @ 2020-06-26 17:41 UTC (permalink / raw) To: Sean Young; +Cc: linux-media Hi Sean, On Wed, Jun 24, 2020 at 4:59 AM Sean Young <sean@mess.org> wrote: > > Hi Brad, > > Thank you for the patch, looks great apart from some minor issues. > > First of all, sparse finds a number of issues, it would be great to have > those resolved. Thanks for the feedback. I'm aware of some of the issues that were reported to me by various bots and intend on fixing them. I'll have to utilize sparse to see what else you're talking about. A lot of the oddities you mention below are a symptom of porting the downstream driver I used as reference. I'll work on them when I have time, I just wanted to get this out there first for anyone to test. > > On Fri, Jun 12, 2020 at 01:39:35PM -0500, Brad Love wrote: > > MaxLinear 692 is a combo demod/tuner which has ATSC and QAM > > capabilities. Only ATSC is currently advertised via DVB > > properties. QAM still has issues. > > > > > > Signed-off-by: Brad Love <brad@nextdimension.cc> > > --- > > drivers/media/dvb-frontends/Kconfig | 9 + > > drivers/media/dvb-frontends/Makefile | 1 + > > drivers/media/dvb-frontends/mxl692.c | 1363 +++++++++++++++++++++ > > drivers/media/dvb-frontends/mxl692.h | 38 + > > drivers/media/dvb-frontends/mxl692_defs.h | 493 ++++++++ > > 5 files changed, 1904 insertions(+) > > create mode 100644 drivers/media/dvb-frontends/mxl692.c > > create mode 100644 drivers/media/dvb-frontends/mxl692.h > > create mode 100644 drivers/media/dvb-frontends/mxl692_defs.h > > > > diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig > > index 643b851a6b60..b1ded2137f0e 100644 > > --- a/drivers/media/dvb-frontends/Kconfig > > +++ b/drivers/media/dvb-frontends/Kconfig > > @@ -708,6 +708,15 @@ config DVB_S5H1411 > > An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want > > to support this frontend. > > > > +config DVB_MXL692 > > + tristate "MaxLinear MXL692 based" > > + depends on DVB_CORE && I2C > > + default m if !MEDIA_SUBDRV_AUTOSELECT > > + help > > + MaxLinear MxL692 is a combo tuner-demodulator that > > + supports ATSC 8VSB and QAM modes. Say Y when you want to > > + support this frontend. > > + > > comment "ISDB-T (terrestrial) frontends" > > depends on DVB_CORE > > > > diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile > > index e9179162658c..b9f47d68e14e 100644 > > --- a/drivers/media/dvb-frontends/Makefile > > +++ b/drivers/media/dvb-frontends/Makefile > > @@ -55,6 +55,7 @@ obj-$(CONFIG_DVB_S5H1420) += s5h1420.o > > obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o > > obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o > > obj-$(CONFIG_DVB_LGDT3306A) += lgdt3306a.o > > +obj-$(CONFIG_DVB_MXL692) += mxl692.o > > obj-$(CONFIG_DVB_LG2160) += lg2160.o > > obj-$(CONFIG_DVB_CX24123) += cx24123.o > > obj-$(CONFIG_DVB_LNBH25) += lnbh25.o > > diff --git a/drivers/media/dvb-frontends/mxl692.c b/drivers/media/dvb-frontends/mxl692.c > > new file mode 100644 > > index 000000000000..622b7a7ebab5 > > --- /dev/null > > +++ b/drivers/media/dvb-frontends/mxl692.c > > @@ -0,0 +1,1363 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Driver for the MaxLinear MxL69x family of combo tuners/demods > > + * > > + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> > > + * > > + * based on code: > > + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved > > + * which was released under GPL V2 > > + * > > + * This program is free software; you can redistribute it and/or > > + * modify it under the terms of the GNU General Public License > > + * version 2, as published by the Free Software Foundation. > > + * > > + * This program is distributed in the hope that it will be useful, > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > + * GNU General Public License for more details. > > + */ > > + > > +#include <linux/mutex.h> > > +#include <linux/i2c-mux.h> > > +#include <linux/string.h> > > +#include <linux/firmware.h> > > + > > +#include "mxl692.h" > > +#include "mxl692_defs.h" > > + > > +static int g_big_endian = -1; > > g_big_endian is not needed. You can use > > #ifdef __BIG_ENDIAN > ... > #endif It's on my list to retool the various endian code. > > > + > > +static const struct dvb_frontend_ops mxl692_ops; > > + > > +struct mxl692_dev { > > + struct dvb_frontend fe; > > + struct i2c_client *i2c_client; > > + struct mutex i2c_lock; /* i2c command mutex */ > > + enum MXL_EAGLE_DEMOD_TYPE_E demod_type; > > + enum MXL_EAGLE_POWER_MODE_E power_mode; > > + u32 current_frequency; > > + int device_type; > > + int seqnum; > > + int init_done; > > +}; > > + > > +static int mxl692_i2c_write(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) > > +{ > > + int ret = 0; > > + struct i2c_msg msg = { > > + .addr = dev->i2c_client->addr, > > + .flags = 0, > > + .buf = buffer, > > + .len = buf_len > > + }; > > + > > + ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1); > > + if (ret != 1) > > + dev_err(&dev->i2c_client->dev, "i2c write error!\n"); > > + > > + return ret; > > +} > > + > > +static int mxl692_i2c_read(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) > > +{ > > + int ret = 0; > > + struct i2c_msg msg = { > > + .addr = dev->i2c_client->addr, > > + .flags = I2C_M_RD, > > + .buf = buffer, > > + .len = buf_len > > + }; > > + > > + ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1); > > + if (ret != 1) > > + dev_err(&dev->i2c_client->dev, "i2c read error!\n"); > > + > > + return ret; > > +} > > + > > +static void detect_endianness(void) > > +{ > > + u32 temp = 1; > > + u8 *p_temp = (u8 *)&temp; > > + > > + g_big_endian = (*p_temp == 0) ? 1 : 0; > > + > > + if (g_big_endian) > > + pr_debug("%s( BIG )\n", __func__); > > + else > > + pr_debug("%s( SMALL )\n", __func__); > > +} > > Function not needed, please remove. > > > +static int convert_endian(u32 size, u8 *d) > > +{ > > + u32 i; > > + > > + for (i = 0; i < (size & ~3); i += 4) { > > + d[i + 0] ^= d[i + 3]; > > + d[i + 3] ^= d[i + 0]; > > + d[i + 0] ^= d[i + 3]; > > + > > + d[i + 1] ^= d[i + 2]; > > + d[i + 2] ^= d[i + 1]; > > + d[i + 1] ^= d[i + 2]; > > If I'm reading this correctly -- and it's hard to parse -- this byte > swapping byte 0 and 3, and 1 and 2. convert_endian is only called on > little endian. So this is doing le32_to_cpus()? > This zero copy endian conversion was pulled from the mxl5xx.c driver. These chips expect everything in big endian. I'll look at using the macros you describe here and below. > > > + } > > + > > + switch (size & 3) { > > + case 0: > > + case 1: > > + /* do nothing */ > > + break; > > + case 2: > > + d[i + 0] ^= d[i + 1]; > > + d[i + 1] ^= d[i + 0]; > > + d[i + 0] ^= d[i + 1]; > > le16_to_cpus()? > > > + break; > > + > > + case 3: > > + d[i + 0] ^= d[i + 2]; > > + d[i + 2] ^= d[i + 0]; > > + d[i + 0] ^= d[i + 2]; > > What is this? le24_to_cpus()? > > > + break; > > + } > > + return size; > > +} > > + > > +static int convert_endian_n(int n, u32 size, u8 *d) > > +{ > > + int i, count = 0; > > + > > + for (i = 0; i < n; i += size) > > + count += convert_endian(size, d + i); > > + return count; > > +} > > These functions are really hard to read. It would be much better if > the structs like MXL_EAGLE_TUNER_CHANNEL_PARAMS_T have their freq_hz > field to be type be32 rather than u32, and do the conversion when it > is written/read rather than patching it up to the right endianness. > > mxl692_tx_swap() and mxl692_rx_swap() both really should be removed, > if possible. I'm hesitant to change the structs as they came exactly as they are from MXL. > > > + > > +static void mxl692_tx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) > > +{ > > + if (g_big_endian) > > + return; > > + > > + buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ > > + > > + switch (opcode) { > > + case MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET: > > + case MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET: > > + case MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET: > > + buffer += convert_endian(sizeof(u32), buffer); > > + break; > > + case MXL_EAGLE_OPCODE_QAM_PARAMS_SET: > > + buffer += 5; > > + buffer += convert_endian(2 * sizeof(u32), buffer); > > + break; > > + default: > > + /* no swapping - all get opcodes */ > > + /* ATSC/OOB no swapping */ > > + break; > > + } > > +} > > + > > +static void mxl692_rx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) > > +{ > > + if (g_big_endian) > > + return; > > + > > + buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ > > + > > + switch (opcode) { > > + case MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET: > > + buffer++; > > + buffer += convert_endian(2 * sizeof(u16), buffer); > > + break; > > + case MXL_EAGLE_OPCODE_ATSC_STATUS_GET: > > + buffer += convert_endian_n(2, sizeof(u16), buffer); > > + buffer += convert_endian(sizeof(u32), buffer); > > + break; > > + case MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET: > > + buffer += convert_endian(3 * sizeof(u32), buffer); > > + break; > > + case MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET: > > + buffer += convert_endian_n(24, sizeof(u16), buffer); > > + break; > > + case MXL_EAGLE_OPCODE_QAM_STATUS_GET: > > + buffer += 8; > > + buffer += convert_endian_n(2, sizeof(u16), buffer); > > + buffer += convert_endian(sizeof(u32), buffer); > > + break; > > + case MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET: > > + buffer += convert_endian(7 * sizeof(u32), buffer); > > + break; > > + case MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET: > > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET: > > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET: > > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET: > > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET: > > + buffer += convert_endian_n(24, sizeof(u16), buffer); > > + break; > > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET: > > + buffer += convert_endian_n(8, sizeof(u16), buffer); > > + break; > > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET: > > + buffer += convert_endian_n(17, sizeof(u16), buffer); > > + break; > > + case MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET: > > + buffer += convert_endian(3 * sizeof(u32), buffer); > > + break; > > + case MXL_EAGLE_OPCODE_OOB_STATUS_GET: > > + buffer += convert_endian_n(2, sizeof(u16), buffer); > > + buffer += convert_endian(sizeof(u32), buffer); > > + break; > > + case MXL_EAGLE_OPCODE_SMA_RECEIVE_GET: > > + buffer += convert_endian(sizeof(u32), buffer); > > + break; > > + default: > > + /* no swapping - all set opcodes */ > > + break; > > + } > > +} > > + > > +static u32 mxl692_checksum(u8 *buffer, u32 size) > > +{ > > + u32 ix, remainder = 0, cur_cksum = 0; > > + > > + for (ix = 0; ix < size / 4; ix++) > > + cur_cksum += cpu_to_be32(*(u32 *)(buffer + > > + (ix * sizeof(u32)))); > > This is a bit messy. If buffer is a u32 pointer, you could do buffer[ix] > and avoid the cast. Yeh, I don't like this either, but it's another reference artifact. > > > + remainder = size % 4; > > + if (remainder > 0) > > + cur_cksum += cpu_to_be32(*((u32 *)&buffer[size - remainder])); > > Ehm. If there is a remainder, then do another cksum += .. ? Can't this be > solved by merging this into the above loop? (i.e. one more iteration if > size % 4 > 0: I'll see if that still passes checksum calc and rework this function. A lot of your comments here are already on my list to do as I have time. > > for (ix = 0; ix < size; ix += 4) > cur_cksum += cpu_to_be32(*(u32 *)(buffer + ix)); > > Since this is calculating the checksum, I would expect it do X_to_cpu(), > not cpu_to_X. The checksum is in big endian. Everything "checks out" according to the hardware for all transactions. > > > + > > + cur_cksum ^= 0xDEADBEEF; > > + > > + return be32_to_cpu(cur_cksum); > > +} > > + > > +static int mxl692_validate_fw_header(const u8 *buffer, u32 buf_len) > > +{ > > + int status = 0; > > + u32 ix, temp = 0; > > + u32 *local_buf = NULL; > > + > > + if (buffer[0] != 0x4D || buffer[1] != 0x31 || > > + buffer[2] != 0x10 || buffer[3] != 0x02 || > > + buffer[4] != 0x40 || buffer[5] != 0x00 || > > + buffer[6] != 0x00 || buffer[7] != 0x80) { > > + status = -EINVAL; > > + goto err_finish; > > + } > > memcmp() might be shorter. Agreed. > > > + > > + local_buf = (u32 *)(buffer + 8); > > + temp = cpu_to_be32(*(u32 *)local_buf); > > + > > + if ((buf_len - 16) != (temp >> 8)) { > > + status = -EINVAL; > > + goto err_finish; > > + } > > + > > + temp = 0; > > + for (ix = 16; ix < buf_len; ix++) > > + temp += buffer[ix]; > > + > > + if ((u8)temp != buffer[11]) > > By making temp u8 you avoid the cast. Noted. > > > + status = -EINVAL; > > +err_finish: > > + if (status) > > + pr_err("%s failed! %d\n", __func__, status); > > + return status; > > +} > > + > > +static int mxl692_write_fw_block(struct mxl692_dev *dev, const u8 *buffer, > > + u32 buf_len, u32 *index) > > +{ > > + int status = 0; > > + u32 ix = 0, total_len = 0, addr = 0, chunk_len = 0, prevchunk_len = 0; > > + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; > > + int payload_max = MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE; > > + > > + ix = *index; > > + > > + if (buffer[ix] == 0x53) { > > + total_len = buffer[ix + 1] << 16 | buffer[ix + 2] << 8 | buffer[ix + 3]; > > + total_len = (total_len + 3) & ~3; > > + addr = buffer[ix + 4] << 24 | buffer[ix + 5] << 16 | > > + buffer[ix + 6] << 8 | buffer[ix + 7]; > > + ix += MXL_EAGLE_FW_SEGMENT_HEADER_SIZE; > > + > > + while ((total_len > 0) && (status == 0)) { > > + plocal_buf = local_buf; > > + chunk_len = (total_len < payload_max) ? > > + total_len : payload_max; > > + > > + *plocal_buf++ = 0xFC; > > + *plocal_buf++ = chunk_len + sizeof(u32); > > + > > + *(u32 *)plocal_buf = cpu_to_le32(addr + prevchunk_len); > > + plocal_buf += sizeof(u32); > > + > > + memcpy(plocal_buf, &buffer[ix], chunk_len); > > + convert_endian(chunk_len, plocal_buf); > > + > > + if (mxl692_i2c_write(dev, local_buf, > > + (chunk_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { > > + status = -EREMOTEIO; > > + break; > > + } > > + > > + prevchunk_len += chunk_len; > > + total_len -= chunk_len; > > + ix += chunk_len; > > + } > > + *index = ix; > > + } else { > > + status = -EINVAL; > > + } > > + > > + if (status) > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > + > > + return status; > > +} > > + > > +static int mxl692_memwrite(struct mxl692_dev *dev, u32 addr, > > + u8 *buffer, u32 size) > > +{ > > + int status = 0, total_len = 0; > > + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; > > + > > + total_len = size; > > + total_len = (total_len + 3) & ~3; /* 4 byte alignment */ > > + > > + if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE)) > > + dev_dbg(&dev->i2c_client->dev, "hrmph?\n"); > > + > > + plocal_buf = local_buf; > > + > > + *plocal_buf++ = 0xFC; > > + *plocal_buf++ = total_len + sizeof(u32); > > + > > + *(u32 *)plocal_buf = addr; > > + plocal_buf += sizeof(u32); > > + > > + memcpy(plocal_buf, buffer, total_len); > > + if (g_big_endian) > > + convert_endian(sizeof(u32) + total_len, local_buf + 2); > > + > > + if (mxl692_i2c_write(dev, local_buf, > > + (total_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { > > + status = -EREMOTEIO; > > + goto err_finish; > > + } > > + > > + return status; > > +err_finish: > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > + return status; > > +} > > + > > +static int mxl692_memread(struct mxl692_dev *dev, u32 addr, > > + u8 *buffer, u32 size) > > +{ > > + int status = 0; > > + u8 local_buf[MXL_EAGLE_I2C_MHEADER_SIZE] = {}, *plocal_buf = NULL; > > + > > + plocal_buf = local_buf; > > + > > + *plocal_buf++ = 0xFB; > > + *plocal_buf++ = sizeof(u32); > > + *(u32 *)plocal_buf = addr; > > + > > + if (g_big_endian) > > + convert_endian(sizeof(u32), plocal_buf); > > + > > + if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_MHEADER_SIZE) > 0) { > > + size = (size + 3) & ~3; /* 4 byte alignment */ > > + status = mxl692_i2c_read(dev, buffer, (u16)size) < 0 ? > > + -EREMOTEIO : 0; > > + > > + if (status == 0 && g_big_endian) > > + convert_endian(size, buffer); > > + } else { > > + status = -EREMOTEIO; > > + } > > + > > + if (status) > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > + > > + return status; > > +} > > + > > +static int mxl692_opwrite(struct mxl692_dev *dev, u8 *buffer, > > + u32 size) > > +{ > > + int status = 0, total_len = 0; > > + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; > > + > > + total_len = size; > > + total_len = (total_len + 3) & ~3; /* 4 byte alignment */ > > + > > + if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) > > + dev_dbg(&dev->i2c_client->dev, "hrmph?\n"); > > + > > + plocal_buf = local_buf; > > + > > + *plocal_buf++ = 0xFE; > > + *plocal_buf++ = (u8)total_len; > > + > > + memcpy(plocal_buf, buffer, total_len); > > + convert_endian(total_len, plocal_buf); > > + > > + if (mxl692_i2c_write(dev, local_buf, > > + (total_len + MXL_EAGLE_I2C_PHEADER_SIZE)) < 0) { > > + status = -EREMOTEIO; > > + goto err_finish; > > + } > > +err_finish: > > + if (status) > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > + return status; > > +} > > + > > +static int mxl692_opread(struct mxl692_dev *dev, u8 *buffer, > > + u32 size) > > +{ > > + int status = 0; > > + u32 ix = 0; > > + u8 local_buf[MXL_EAGLE_I2C_PHEADER_SIZE] = {}; > > + > > + local_buf[0] = 0xFD; > > + local_buf[1] = 0; > > + > > + if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_PHEADER_SIZE) > 0) { > > + size = (size + 3) & ~3; /* 4 byte alignment */ > > + > > + //read in 4 byte chunks > > + for (ix = 0; ix < size; ix += 4) { > > + if (mxl692_i2c_read(dev, buffer + ix, 4) < 0) { > > + dev_dbg(&dev->i2c_client->dev, > > + "ix=%d size=%d\n", ix, size); > > + status = -EREMOTEIO; > > + goto err_finish; > > + } > > + } > > + convert_endian(size, buffer); > > + } else { > > + status = -EREMOTEIO; > > + } > > +err_finish: > > + if (status) > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > + return status; > > +} > > + > > +static int mxl692_i2c_writeread(struct mxl692_dev *dev, > > + u8 opcode, > > + u8 *tx_payload, > > + u8 tx_payload_size, > > + u8 *rx_payload, > > + u8 rx_payload_expected) > > +{ > > + int status = 0, timeout = 40; > > + u8 tx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > > + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > > + u32 resp_checksum = 0, resp_checksum_tmp = 0; > > + struct MXL_EAGLE_HOST_MSG_HEADER_T *tx_header; > > + struct MXL_EAGLE_HOST_MSG_HEADER_T *rx_header; > > + > > + mutex_lock(&dev->i2c_lock); > > + > > + if ((tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE) > > > + (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) { > > + status = -EINVAL; > > + goto err_finish; > > + } > > + > > + tx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)tx_buf; > > + tx_header->checksum = 0; > > + tx_header->opcode = opcode; > > + tx_header->payload_size = tx_payload_size; > > + tx_header->seqnum = dev->seqnum++; > > + > > + if (dev->seqnum == 0) > > + dev->seqnum = 1; > > + > > + if (tx_payload && tx_payload_size > 0) > > + memcpy(&tx_buf[MXL_EAGLE_HOST_MSG_HEADER_SIZE], > > + tx_payload, tx_payload_size); > > + > > + mxl692_tx_swap(opcode, tx_buf); > > + > > + tx_header->checksum = 0; > > + tx_header->checksum = mxl692_checksum(tx_buf, > > + MXL_EAGLE_HOST_MSG_HEADER_SIZE + tx_payload_size); > > + > > + /* send Tx message */ > > + status = mxl692_opwrite(dev, tx_buf, > > + tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE); > > + if (status) { > > + status = -EREMOTEIO; > > + goto err_finish; > > + } > > + > > + /* receive Rx message (polling) */ > > + rx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)rx_buf; > > + > > + do { > > + status = mxl692_opread(dev, rx_buf, > > + rx_payload_expected + MXL_EAGLE_HOST_MSG_HEADER_SIZE); > > + usleep_range(1000, 2000); > > + timeout--; > > + } while ((timeout > 0) && (status == 0) && > > + (rx_header->seqnum == 0) && > > + (rx_header->checksum == 0)); > > + > > + if (timeout == 0 || status) { > > + dev_dbg(&dev->i2c_client->dev, "timeout=%d status=%d\n", > > + timeout, status); > > + status = -ETIMEDOUT; > > + goto err_finish; > > + } > > + > > + if (rx_header->status) { > > + status = (int)rx_header->status; > > + goto err_finish; > > + } > > + > > + if (rx_header->seqnum != tx_header->seqnum || > > + rx_header->opcode != tx_header->opcode || > > + rx_header->payload_size != rx_payload_expected) { > > + dev_dbg(&dev->i2c_client->dev, "Something failed seq=%s opcode=%s pSize=%s\n", > > + rx_header->seqnum != tx_header->seqnum ? "X" : "0", > > + rx_header->opcode != tx_header->opcode ? "X" : "0", > > + rx_header->payload_size != rx_payload_expected ? "X" : "0"); > > + if (rx_header->payload_size != rx_payload_expected) > > + dev_dbg(&dev->i2c_client->dev, "rx_header->payloadSize=%d rx_payload_expected=%d\n", > > + rx_header->payload_size, rx_payload_expected); > > + status = -EREMOTEIO; > > + goto err_finish; > > + } > > + > > + resp_checksum = rx_header->checksum; > > + rx_header->checksum = 0; > > + resp_checksum_tmp = mxl692_checksum(rx_buf, > > + MXL_EAGLE_HOST_MSG_HEADER_SIZE + rx_header->payload_size); > > + > > + if (resp_checksum != resp_checksum_tmp) { > > + status = -EREMOTEIO; > > + goto err_finish; > > + } > > + > > + mxl692_rx_swap(rx_header->opcode, rx_buf); > > + > > + if (rx_header->payload_size > 0) { > > + if (!rx_payload) { > > + status = -EREMOTEIO; > > + goto err_finish; > > + } > > + memcpy(rx_payload, rx_buf + MXL_EAGLE_HOST_MSG_HEADER_SIZE, > > + rx_header->payload_size); > > + } > > +err_finish: > > + if (status) > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > + > > + mutex_unlock(&dev->i2c_lock); > > + return status; > > +} > > + > > +static int mxl692_fwdownload(struct mxl692_dev *dev, > > + const u8 *firmware_buf, u32 buf_len) > > +{ > > + int status = 0; > > + u32 ix, reg_val = 0x1; > > + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > > + struct MXL_EAGLE_DEV_STATUS_T *dev_status; > > + > > + if (buf_len < MXL_EAGLE_FW_HEADER_SIZE || > > + buf_len > MXL_EAGLE_FW_MAX_SIZE_IN_KB * 1000) > > + return -EINVAL; > > + > > + mutex_lock(&dev->i2c_lock); > > + > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > + > > + status = mxl692_validate_fw_header(firmware_buf, buf_len); > > + if (status) > > + goto err_finish; > > + > > + ix = 16; > > + status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* DRAM */ > > + if (status) > > + goto err_finish; > > + > > + status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* IRAM */ > > + if (status) > > + goto err_finish; > > + > > + /* release CPU from reset */ > > + status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + mutex_unlock(&dev->i2c_lock); > > + > > + if (status == 0) { > > + /* verify FW is alive */ > > + usleep_range(MXL_EAGLE_FW_LOAD_TIME * 1000, (MXL_EAGLE_FW_LOAD_TIME + 5) * 1000); > > + dev_status = (struct MXL_EAGLE_DEV_STATUS_T *)&rx_buf; > > + status = mxl692_i2c_writeread(dev, > > + MXL_EAGLE_OPCODE_DEVICE_STATUS_GET, > > + NULL, > > + 0, > > + (u8 *)dev_status, > > + sizeof(struct MXL_EAGLE_DEV_STATUS_T)); > > + } > > + > > + return status; > > +err_finish: > > + mutex_unlock(&dev->i2c_lock); > > + if (status) > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > + return status; > > +} > > + > > +static int mxl692_get_versions(struct mxl692_dev *dev) > > +{ > > + int status = 0; > > + struct MXL_EAGLE_DEV_VER_T dev_ver = {}; > > + static const char * const chip_id[] = {"N/A", "691", "248", "692"}; > > + > > + status = mxl692_i2c_writeread(dev, MXL_EAGLE_OPCODE_DEVICE_VERSION_GET, > > + NULL, > > + 0, > > + (u8 *)&dev_ver, > > + sizeof(struct MXL_EAGLE_DEV_VER_T)); > > + if (status) > > + return status; > > + > > + dev_info(&dev->i2c_client->dev, "MxL692_DEMOD Chip ID: %s\n", > > + chip_id[dev_ver.chip_id]); > > + > > + dev_info(&dev->i2c_client->dev, > > + "MxL692_DEMOD FW Version: %d.%d.%d.%d_RC%d\n", > > + dev_ver.firmware_ver[0], > > + dev_ver.firmware_ver[1], > > + dev_ver.firmware_ver[2], > > + dev_ver.firmware_ver[3], > > + dev_ver.firmware_ver[4]); > > + > > + return status; > > +} > > + > > +static int mxl692_reset(struct mxl692_dev *dev) > > +{ > > + int status = 0; > > + u32 dev_type = MXL_EAGLE_DEVICE_MAX, reg_val = 0x2; > > + > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > + > > + /* legacy i2c override */ > > + status = mxl692_memwrite(dev, 0x80000100, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + /* verify sku */ > > + status = mxl692_memread(dev, 0x70000188, (u8 *)&dev_type, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + if (dev_type != dev->device_type) > > + goto err_finish; > > + > > +err_finish: > > + if (status) > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > + return status; > > +} > > + > > +static int mxl692_config_regulators(struct mxl692_dev *dev, > > + enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E power_supply) > > +{ > > + int status = 0; > > + u32 reg_val; > > + > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > + > > + /* configure main regulator according to the power supply source */ > > + status = mxl692_memread(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + reg_val &= 0x00FFFFFF; > > + reg_val |= (power_supply == MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE) ? > > + 0x14000000 : 0x10000000; > > + > > + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + /* configure digital regulator to high current mode */ > > + status = mxl692_memread(dev, 0x90000018, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + reg_val |= 0x800; > > + > > + status = mxl692_memwrite(dev, 0x90000018, (u8 *)®_val, sizeof(u32)); > > + > > +err_finish: > > + if (status) > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > + return status; > > +} > > + > > +static int mxl692_config_xtal(struct mxl692_dev *dev, > > + struct MXL_EAGLE_DEV_XTAL_T *dev_xtal) > > +{ > > + int status = 0; > > + u32 reg_val, reg_val1; > > + > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > + > > + status = mxl692_memread(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + /* set XTAL capacitance */ > > + reg_val &= 0xFFFFFFE0; > > + reg_val |= dev_xtal->xtal_cap; > > + > > + /* set CLK OUT */ > > + reg_val = dev_xtal->clk_out_enable ? > > + (reg_val | 0x0100) : (reg_val & 0xFFFFFEFF); > > + > > + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + /* set CLK OUT divider */ > > + reg_val = dev_xtal->clk_out_div_enable ? > > + (reg_val | 0x0200) : (reg_val & 0xFFFFFDFF); > > + > > + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + /* set XTAL sharing */ > > + reg_val = dev_xtal->xtal_sharing_enable ? > > + (reg_val | 0x010400) : (reg_val & 0xFFFEFBFF); > > + > > + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + /* enable/disable XTAL calibration, based on master/slave device */ > > + status = mxl692_memread(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + if (dev_xtal->xtal_calibration_enable) { > > + /* enable XTAL calibration and set XTAL amplitude to a higher value */ > > + reg_val1 &= 0xFFFFFFFD; > > + reg_val1 |= 0x30; > > + > > + status = mxl692_memwrite(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + } else { > > + /* disable XTAL calibration */ > > + reg_val1 |= 0x2; > > + > > + status = mxl692_memwrite(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + /* set XTAL bias value */ > > + status = mxl692_memread(dev, 0x9000002c, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + reg_val &= 0xC0FFFFFF; > > + reg_val |= 0xA000000; > > + > > + status = mxl692_memwrite(dev, 0x9000002c, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + } > > + > > + /* start XTAL calibration */ > > + status = mxl692_memread(dev, 0x70000010, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + reg_val |= 0x8; > > + > > + status = mxl692_memwrite(dev, 0x70000010, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + status = mxl692_memread(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + reg_val |= 0x10; > > + > > + status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + status = mxl692_memread(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + reg_val &= 0xFFFFEFFF; > > + > > + status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + reg_val |= 0x1000; > > + > > + status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); > > + if (status) > > + goto err_finish; > > + > > + usleep_range(45000, 55000); > > + > > +err_finish: > > + if (status) > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > + return status; > > +} > > + > > +static int mxl692_powermode(struct mxl692_dev *dev, > > + enum MXL_EAGLE_POWER_MODE_E power_mode) > > +{ > > + int status = 0; > > + > > + dev_dbg(&dev->i2c_client->dev, "%s\n", > > + power_mode == MXL_EAGLE_POWER_MODE_SLEEP ? > > + "sleep" : "active"); > > + > > + status = mxl692_i2c_writeread(dev, > > + MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET, > > + (u8 *)&power_mode, > > + sizeof(u8), > > + NULL, > > + 0); > > + if (status) > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > + > > + dev->power_mode = power_mode; > > + > > + return status; > > +} > > + > > +static int mxl692_init(struct dvb_frontend *fe) > > +{ > > + struct mxl692_dev *dev = fe->demodulator_priv; > > + struct i2c_client *client = dev->i2c_client; > > + struct dtv_frontend_properties *c = &fe->dtv_property_cache; > > + int status = 0; > > + const struct firmware *firmware; > > + struct MXL_EAGLE_DEV_XTAL_T xtal_config = {}; > > + > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > + > > + if (dev->init_done) > > + goto warm; > > + > > + dev->seqnum = 1; > > + > > + status = mxl692_reset(dev); > > + if (status) > > + goto err; > > + > > + usleep_range(50 * 1000, 60 * 1000); /* was 1000! */ > > + > > + status = mxl692_config_regulators(dev, MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL); > > + if (status) > > + goto err; > > + > > + xtal_config.xtal_cap = 26; > > + xtal_config.clk_out_div_enable = 0; > > + xtal_config.clk_out_enable = 0; > > + xtal_config.xtal_calibration_enable = 0; > > + xtal_config.xtal_sharing_enable = 1; > > + status = mxl692_config_xtal(dev, &xtal_config); > > + if (status) > > + goto err; > > + > > + status = request_firmware(&firmware, MXL692_FIRMWARE, &client->dev); > > + if (status) { > > + dev_dbg(&dev->i2c_client->dev, "firmware missing? %s\n", > > + MXL692_FIRMWARE); > > + goto err; > > + } > > + > > + status = mxl692_fwdownload(dev, firmware->data, firmware->size); > > + if (status) > > + goto err_release_firmware; > > + > > + release_firmware(firmware); > > + > > +// usleep_range(500 * 1000, 510 * 1000); /* was 1000! */ > > Why is this commented out? This should either be explained or removed. This was part of the downstream driver and is temporarily deemed not required. It is left as a placemarker in case anyone experiences failure. None of my testers have, so it's probably ok to remove now. > > > + status = mxl692_get_versions(dev); > > + if (status) > > + goto err; > > + > > + dev->power_mode = MXL_EAGLE_POWER_MODE_SLEEP; > > + > > +warm: > > + /* Init stats here to indicate which stats are supported */ > > + c->cnr.len = 1; > > + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > + c->post_bit_error.len = 1; > > + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > + c->post_bit_count.len = 1; > > + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > + c->block_error.len = 1; > > + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > + > > + dev->init_done = 1; > > + return 0; > > +err_release_firmware: > > + release_firmware(firmware); > > +err: > > + return status; > > +} > > + > > +static int mxl692_sleep(struct dvb_frontend *fe) > > +{ > > + struct mxl692_dev *dev = fe->demodulator_priv; > > + > > + if (dev->power_mode != MXL_EAGLE_POWER_MODE_SLEEP) > > + mxl692_powermode(dev, MXL_EAGLE_POWER_MODE_SLEEP); > > + > > + return 0; > > +} > > + > > +static int mxl692_set_frontend(struct dvb_frontend *fe) > > +{ > > + struct dtv_frontend_properties *p = &fe->dtv_property_cache; > > + struct mxl692_dev *dev = fe->demodulator_priv; > > + > > + int status = 0; > > + enum MXL_EAGLE_DEMOD_TYPE_E demod_type; > > + enum MXL_EAGLE_POWER_MODE_E power_mode = MXL_EAGLE_POWER_MODE_ACTIVE; > > + struct MXL_EAGLE_MPEGOUT_PARAMS_T mpeg_params = {}; > > + enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E qam_annex = MXL_EAGLE_QAM_DEMOD_ANNEX_B; > > + struct MXL_EAGLE_QAM_DEMOD_PARAMS_T qam_params = {}; > > + struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T tuner_params = {}; > > + > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > + > > + switch (p->modulation) { > > + case VSB_8: > > + demod_type = MXL_EAGLE_DEMOD_TYPE_ATSC; > > + break; > > + case QAM_AUTO: > > + case QAM_64: > > + case QAM_128: > > + case QAM_256: > > + demod_type = MXL_EAGLE_DEMOD_TYPE_QAM; > > + break; > > + default: > > + return -EINVAL; > > + } > > + > > + if (dev->current_frequency == p->frequency && dev->demod_type == demod_type) > > + return 0; > > + > > + dev->current_frequency = -1; > > + dev->demod_type = -1; > > + > > + status = mxl692_i2c_writeread(dev, > > + MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET, > > + (u8 *)&demod_type, > > + sizeof(u8), > > + NULL, > > + 0); > > + if (status) { > > + dev_dbg(&dev->i2c_client->dev, "DEVICE_DEMODULATOR_TYPE_SET...FAIL %d\n", status); > > + goto err; > > + } > > + > > + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ > > + > > + //Config Device Power Mode > > + if (dev->power_mode != MXL_EAGLE_POWER_MODE_ACTIVE) { > > + status = mxl692_powermode(dev, power_mode); > > + if (status) > > + goto err; > > + > > + usleep_range(50 * 1000, 60 * 1000); /* was 500! */ > > + } > > + > > + mpeg_params.mpeg_parallel = 0; > > + mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_MSB_1ST; > > + mpeg_params.mpeg_sync_pulse_width = MXL_EAGLE_DATA_SYNC_WIDTH_BIT; > > + mpeg_params.mpeg_valid_pol = MXL_EAGLE_CLOCK_POSITIVE; > > + mpeg_params.mpeg_sync_pol = MXL_EAGLE_CLOCK_POSITIVE; > > + mpeg_params.mpeg_clk_pol = MXL_EAGLE_CLOCK_NEGATIVE; > > + mpeg_params.mpeg3wire_mode_enable = 0; > > + mpeg_params.mpeg_clk_freq = MXL_EAGLE_MPEG_CLOCK_27MHZ; > > + > > + switch (demod_type) { > > + case MXL_EAGLE_DEMOD_TYPE_ATSC: > > + status = mxl692_i2c_writeread(dev, > > + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, > > + (u8 *)&mpeg_params, > > + sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), > > + NULL, > > + 0); > > + if (status) > > + goto err; > > + break; > > + case MXL_EAGLE_DEMOD_TYPE_QAM: > > + if (qam_annex == MXL_EAGLE_QAM_DEMOD_ANNEX_A) > > + mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_LSB_1ST; > > + status = mxl692_i2c_writeread(dev, > > + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, > > + (u8 *)&mpeg_params, > > + sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), > > + NULL, > > + 0); > > + if (status) > > + goto err; > > + > > + qam_params.annex_type = qam_annex; > > + qam_params.qam_type = MXL_EAGLE_QAM_DEMOD_AUTO; > > + qam_params.iq_flip = MXL_EAGLE_DEMOD_IQ_AUTO; > > + if (p->modulation == QAM_64) > > + qam_params.symbol_rate_hz = 5057000; > > + else > > + qam_params.symbol_rate_hz = 5361000; > > + > > + qam_params.symbol_rate_256qam_hz = 5361000; > > + > > + status = mxl692_i2c_writeread(dev, > > + MXL_EAGLE_OPCODE_QAM_PARAMS_SET, > > + (u8 *)&qam_params, > > + sizeof(struct MXL_EAGLE_QAM_DEMOD_PARAMS_T), > > + NULL, 0); > > + if (status) > > + goto err; > > + > > + break; > > + default: > > + break; > > + } > > + > > + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ > > + > > + tuner_params.freq_hz = p->frequency; > > + tuner_params.bandwidth = MXL_EAGLE_TUNER_BW_6MHZ; > > + tuner_params.tune_mode = MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW; > > + > > + dev_dbg(&dev->i2c_client->dev, " Tuning Freq: %d %s\n", tuner_params.freq_hz, > > + demod_type == MXL_EAGLE_DEMOD_TYPE_ATSC ? "ATSC" : "QAM"); > > + > > + status = mxl692_i2c_writeread(dev, > > + MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET, > > + (u8 *)&tuner_params, > > + sizeof(struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T), > > + NULL, > > + 0); > > + if (status) > > + goto err; > > + > > + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ > > + > > + switch (demod_type) { > > + case MXL_EAGLE_DEMOD_TYPE_ATSC: > > + status = mxl692_i2c_writeread(dev, > > + MXL_EAGLE_OPCODE_ATSC_INIT_SET, > > + NULL, 0, NULL, 0); > > + if (status) > > + goto err; > > + break; > > + case MXL_EAGLE_DEMOD_TYPE_QAM: > > + status = mxl692_i2c_writeread(dev, > > + MXL_EAGLE_OPCODE_QAM_RESTART_SET, > > + NULL, 0, NULL, 0); > > + if (status) > > + goto err; > > + break; > > + default: > > + break; > > + } > > + > > + dev->demod_type = demod_type; > > + dev->current_frequency = p->frequency; > > +err: > > + return 0; > > +} > > + > > +static int mxl692_get_frontend(struct dvb_frontend *fe, > > + struct dtv_frontend_properties *p) > > +{ > > + struct dtv_frontend_properties *c = &fe->dtv_property_cache; > > + > > + p->modulation = c->modulation; > > + p->frequency = c->frequency; > > + > > + return 0; > > +} > > + > > +static int mxl692_read_snr(struct dvb_frontend *fe, u16 *snr) > > +{ > > + struct mxl692_dev *dev = fe->demodulator_priv; > > + struct dtv_frontend_properties *c = &fe->dtv_property_cache; > > + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > > + struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; > > + struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; > > + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; > > + int mxl_status = 0; > > + > > + *snr = 0; > > + > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > + > > + atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; > > + qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; > > It might be nicer to use a union rather casting, for all of these (including > below). Union the two status structs? They're not even the same size, why would I do that? > > > + > > + switch (demod_type) { > > + case MXL_EAGLE_DEMOD_TYPE_ATSC: > > + mxl_status = mxl692_i2c_writeread(dev, > > + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, > > + NULL, > > + 0, > > + rx_buf, > > + sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); > > + if (!mxl_status) { > > + *snr = (u16)(atsc_status->snr_db_tenths / 10); > > + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; > > + c->cnr.stat[0].svalue = *snr; > > + } > > + break; > > + case MXL_EAGLE_DEMOD_TYPE_QAM: > > + mxl_status = mxl692_i2c_writeread(dev, > > + MXL_EAGLE_OPCODE_QAM_STATUS_GET, > > + NULL, > > + 0, > > + rx_buf, > > + sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); > > + if (!mxl_status) > > + *snr = (u16)(qam_status->snr_db_tenths / 10); > > + break; > > + case MXL_EAGLE_DEMOD_TYPE_OOB: > > + default: > > + break; > > + } > > + return 0; > > +} > > + > > +static int mxl692_read_ber_ucb(struct dvb_frontend *fe) > > +{ > > + struct mxl692_dev *dev = fe->demodulator_priv; > > + struct dtv_frontend_properties *c = &fe->dtv_property_cache; > > + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > > + struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T *qam_errors; > > + struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *atsc_errors; > > + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; > > + int mxl_status = 0; > > + u32 utmp; > > + > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > + > > + qam_errors = (struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T *)&rx_buf; > > + atsc_errors = (struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *)&rx_buf; > > + > > + switch (demod_type) { > > + case MXL_EAGLE_DEMOD_TYPE_ATSC: > > + mxl_status = mxl692_i2c_writeread(dev, > > + MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, > > + NULL, > > + 0, > > + rx_buf, > > + sizeof(struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T)); > > + if (!mxl_status) { > > + if (atsc_errors->error_packets == 0) > > + utmp = 0; > > + else > > + utmp = ((atsc_errors->error_bytes / atsc_errors->error_packets) * > > + atsc_errors->total_packets); > > + /* ber */ > > + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; > > + c->post_bit_error.stat[0].uvalue += atsc_errors->error_bytes; > > + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; > > + c->post_bit_count.stat[0].uvalue += utmp; > > + /* ucb */ > > + c->block_error.stat[0].scale = FE_SCALE_COUNTER; > > + c->block_error.stat[0].uvalue += atsc_errors->error_packets; > > + > > + dev_dbg(&dev->i2c_client->dev, "%llu %llu\n", > > + c->post_bit_count.stat[0].uvalue, c->block_error.stat[0].uvalue); > > + } > > + break; > > + case MXL_EAGLE_DEMOD_TYPE_QAM: > > + case MXL_EAGLE_DEMOD_TYPE_OOB: > > + default: > > + break; > > + } > > + return 0; > > +} > > + > > +static int mxl692_read_status(struct dvb_frontend *fe, > > + enum fe_status *status) > > +{ > > + struct mxl692_dev *dev = fe->demodulator_priv; > > + struct dtv_frontend_properties *c = &fe->dtv_property_cache; > > + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > > + struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; > > + struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; > > + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; > > + int mxl_status = 0; > > + *status = 0; > > + > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > + > > + atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; > > + qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; > > + > > + switch (demod_type) { > > + case MXL_EAGLE_DEMOD_TYPE_ATSC: > > + mxl_status = mxl692_i2c_writeread(dev, > > + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, > > + NULL, > > + 0, > > + rx_buf, > > + sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); > > + if (!mxl_status && atsc_status->atsc_lock) { > > + *status |= FE_HAS_SIGNAL; > > + *status |= FE_HAS_CARRIER; > > + *status |= FE_HAS_VITERBI; > > + *status |= FE_HAS_SYNC; > > + *status |= FE_HAS_LOCK; > > + > > + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; > > + c->cnr.stat[0].svalue = atsc_status->snr_db_tenths / 10; > > + } > > + break; > > + case MXL_EAGLE_DEMOD_TYPE_QAM: > > + mxl_status = mxl692_i2c_writeread(dev, > > + MXL_EAGLE_OPCODE_QAM_STATUS_GET, > > + NULL, > > + 0, > > + rx_buf, > > + sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); > > + if (!mxl_status && qam_status->qam_locked) { > > + *status |= FE_HAS_SIGNAL; > > + *status |= FE_HAS_CARRIER; > > + *status |= FE_HAS_VITERBI; > > + *status |= FE_HAS_SYNC; > > + *status |= FE_HAS_LOCK; > > + > > + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; > > + c->cnr.stat[0].svalue = qam_status->snr_db_tenths / 10; > > + } > > + break; > > + case MXL_EAGLE_DEMOD_TYPE_OOB: > > + default: > > + break; > > + } > > + > > + if ((*status & FE_HAS_LOCK) == 0) { > > + /* No lock, reset all statistics */ > > + c->cnr.len = 1; > > + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > + return 0; > > + } > > + > > + if (mxl_status) > > + dev_err(&dev->i2c_client->dev, "err %d\n", mxl_status); > > + else > > + mxl_status = mxl692_read_ber_ucb(fe); > > + > > + return mxl_status; > > +} > > + > > +static const struct dvb_frontend_ops mxl692_ops = { > > + .delsys = { SYS_ATSC }, > > + .info = { > > + .name = "MaxLinear MxL692 VSB tuner-demodulator", > > + .frequency_min_hz = 54000000, > > + .frequency_max_hz = 858000000, > > + .frequency_stepsize_hz = 62500, > > + .caps = FE_CAN_8VSB > > + }, > > + > > + .init = mxl692_init, > > + .sleep = mxl692_sleep, > > + .set_frontend = mxl692_set_frontend, > > + .get_frontend = mxl692_get_frontend, > > + > > + .read_status = mxl692_read_status, > > + .read_snr = mxl692_read_snr, > > +}; > > + > > +static int mxl692_probe(struct i2c_client *client, > > + const struct i2c_device_id *id) > > +{ > > + struct mxl692_config *config = client->dev.platform_data; > > + struct mxl692_dev *dev; > > + int ret = 0; > > + > > + dev = kzalloc(sizeof(*dev), GFP_KERNEL); > > + if (!dev) { > > + ret = -ENOMEM; > > + dev_err(&client->dev, "kzalloc() failed\n"); > > + goto err; > > + } > > + > > + memcpy(&dev->fe.ops, &mxl692_ops, sizeof(struct dvb_frontend_ops)); > > + dev->fe.demodulator_priv = dev; > > + dev->i2c_client = client; > > + *config->fe = &dev->fe; > > + mutex_init(&dev->i2c_lock); > > + i2c_set_clientdata(client, dev); > > + > > + dev_info(&client->dev, "MaxLinear mxl692 successfully attached\n"); > > + detect_endianness(); > > + > > + return 0; > > +err: > > + dev_err(&client->dev, "failed %d\n", ret); > > + return -ENODEV; > > +} > > + > > +static int mxl692_remove(struct i2c_client *client) > > +{ > > + struct mxl692_dev *dev = i2c_get_clientdata(client); > > + > > + dev->fe.demodulator_priv = NULL; > > + i2c_set_clientdata(client, NULL); > > + kfree(dev); > > + > > + return 0; > > +} > > + > > +static const struct i2c_device_id mxl692_id_table[] = { > > + {"mxl692", 0}, > > + {} > > +}; > > +MODULE_DEVICE_TABLE(i2c, mxl692_id_table); > > + > > +static struct i2c_driver mxl692_driver = { > > + .driver = { > > + .owner = THIS_MODULE, > > + .name = "mxl692", > > + }, > > + .probe = mxl692_probe, > > + .remove = mxl692_remove, > > + .id_table = mxl692_id_table, > > +}; > > + > > +module_i2c_driver(mxl692_driver); > > + > > +MODULE_AUTHOR("Brad Love <brad@nextdimension.cc>"); > > +MODULE_DESCRIPTION("MaxLinear MxL692 demodulator/tuner driver"); > > +MODULE_FIRMWARE(MXL692_FIRMWARE); > > +MODULE_LICENSE("GPL"); > > diff --git a/drivers/media/dvb-frontends/mxl692.h b/drivers/media/dvb-frontends/mxl692.h > > new file mode 100644 > > index 000000000000..45bc48f1f12f > > --- /dev/null > > +++ b/drivers/media/dvb-frontends/mxl692.h > > @@ -0,0 +1,38 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * Driver for the MaxLinear MxL69x family of tuners/demods > > + * > > + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> > > + * > > + * based on code: > > + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved > > + * which was released under GPL V2 > > + * > > + * This program is free software; you can redistribute it and/or > > + * modify it under the terms of the GNU General Public License > > + * version 2, as published by the Free Software Foundation. > > + * > > + * This program is distributed in the hope that it will be useful, > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > + * GNU General Public License for more details. > > + */ > > + > > +#ifndef _MXL692_H_ > > +#define _MXL692_H_ > > + > > +#include <media/dvb_frontend.h> > > + > > +#define MXL692_FIRMWARE "dvb-demod-mxl692.fw" > > + > > +struct mxl692_config { > > + unsigned char id; > > + u8 i2c_addr; > > + /* > > + * frontend > > + * returned by driver > > + */ > > + struct dvb_frontend **fe; > > +}; > > + > > +#endif /* _MXL692_H_ */ > > diff --git a/drivers/media/dvb-frontends/mxl692_defs.h b/drivers/media/dvb-frontends/mxl692_defs.h > > new file mode 100644 > > index 000000000000..97fd18ae81ff > > --- /dev/null > > +++ b/drivers/media/dvb-frontends/mxl692_defs.h > > @@ -0,0 +1,493 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * Driver for the MaxLinear MxL69x family of combo tuners/demods > > + * > > + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> > > + * > > + * based on code: > > + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved > > + * which was released under GPL V2 > > + * > > + * This program is free software; you can redistribute it and/or > > + * modify it under the terms of the GNU General Public License > > + * version 2, as published by the Free Software Foundation. > > + * > > + * This program is distributed in the hope that it will be useful, > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > + * GNU General Public License for more details. > > + */ > > + > > +//#define __packed __attribute__((packed)) > > Comment not needed. > > > + > > +/***************************************************************************** > > + * Defines > > + ***************************************************************************** > > + */ > > +#define MXL_EAGLE_HOST_MSG_HEADER_SIZE 8 > > +#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76 > > +#define MXL_EAGLE_QAM_FFE_TAPS_LENGTH 16 > > +#define MXL_EAGLE_QAM_SPUR_TAPS_LENGTH 32 > > +#define MXL_EAGLE_QAM_DFE_TAPS_LENGTH 72 > > +#define MXL_EAGLE_ATSC_FFE_TAPS_LENGTH 4096 > > +#define MXL_EAGLE_ATSC_DFE_TAPS_LENGTH 384 > > +#define MXL_EAGLE_VERSION_SIZE 5 /* A.B.C.D-RCx */ > > +#define MXL_EAGLE_FW_LOAD_TIME 50 > > + > > +#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76 > > +#define MXL_EAGLE_FW_HEADER_SIZE 16 > > +#define MXL_EAGLE_FW_SEGMENT_HEADER_SIZE 8 > > +#define MXL_EAGLE_MAX_I2C_PACKET_SIZE 58 > > +#define MXL_EAGLE_I2C_MHEADER_SIZE 6 > > +#define MXL_EAGLE_I2C_PHEADER_SIZE 2 > > + > > +/* Enum of Eagle family devices */ > > +enum MXL_EAGLE_DEVICE_E { > > + MXL_EAGLE_DEVICE_691 = 1, /* Device Mxl691 */ > > + MXL_EAGLE_DEVICE_248 = 2, /* Device Mxl248 */ > > + MXL_EAGLE_DEVICE_692 = 3, /* Device Mxl692 */ > > + MXL_EAGLE_DEVICE_MAX, /* No such device */ > > +}; > > + > > +#define VER_A 1 > > +#define VER_B 1 > > +#define VER_C 1 > > +#define VER_D 3 > > +#define VER_E 6 > > + > > +/* Enum of Host to Eagle I2C protocol opcodes */ > > +enum MXL_EAGLE_OPCODE_E { > > + /* DEVICE */ > > + MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET, > > + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, > > + MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET, > > + MXL_EAGLE_OPCODE_DEVICE_GPIO_DIRECTION_SET, > > + MXL_EAGLE_OPCODE_DEVICE_GPO_LEVEL_SET, > > + MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET, > > + MXL_EAGLE_OPCODE_DEVICE_IO_MUX_SET, > > + MXL_EAGLE_OPCODE_DEVICE_VERSION_GET, > > + MXL_EAGLE_OPCODE_DEVICE_STATUS_GET, > > + MXL_EAGLE_OPCODE_DEVICE_GPI_LEVEL_GET, > > + > > + /* TUNER */ > > + MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET, > > + MXL_EAGLE_OPCODE_TUNER_LOCK_STATUS_GET, > > + MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET, > > + > > + /* ATSC */ > > + MXL_EAGLE_OPCODE_ATSC_INIT_SET, > > + MXL_EAGLE_OPCODE_ATSC_ACQUIRE_CARRIER_SET, > > + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, > > + MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, > > + MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_DFE_TAPS_GET, > > + MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET, > > + > > + /* QAM */ > > + MXL_EAGLE_OPCODE_QAM_PARAMS_SET, > > + MXL_EAGLE_OPCODE_QAM_RESTART_SET, > > + MXL_EAGLE_OPCODE_QAM_STATUS_GET, > > + MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET, > > + MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET, > > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET, > > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET, > > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET, > > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_TAPS_NUMBER_GET, > > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET, > > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET, > > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET, > > + > > + /* OOB */ > > + MXL_EAGLE_OPCODE_OOB_PARAMS_SET, > > + MXL_EAGLE_OPCODE_OOB_RESTART_SET, > > + MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET, > > + MXL_EAGLE_OPCODE_OOB_STATUS_GET, > > + > > + /* SMA */ > > + MXL_EAGLE_OPCODE_SMA_INIT_SET, > > + MXL_EAGLE_OPCODE_SMA_PARAMS_SET, > > + MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET, > > + MXL_EAGLE_OPCODE_SMA_RECEIVE_GET, > > + > > + /* DEBUG */ > > + MXL_EAGLE_OPCODE_INTERNAL, > > + > > + MXL_EAGLE_OPCODE_MAX = 70, > > +}; > > + > > +/* Enum of Callabck function types */ > > +enum MXL_EAGLE_CB_TYPE_E { > > + MXL_EAGLE_CB_FW_DOWNLOAD = 0, > > +}; > > + > > +/* Enum of power supply types */ > > +enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E { > > + MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE, /* Single supply of 3.3V */ > > + MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL, /* Dual supply, 1.8V & 3.3V */ > > +}; > > + > > +/* Enum of I/O pad drive modes */ > > +enum MXL_EAGLE_IO_MUX_DRIVE_MODE_E { > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_1X, > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_2X, > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_3X, > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_4X, > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_5X, > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_6X, > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_7X, > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_8X, > > +}; > > + > > +/* Enum of demodulator types. Used for selection of demodulator > > + * type in relevant devices, e.g. ATSC vs. QAM in Mxl691 > > + */ > > +enum MXL_EAGLE_DEMOD_TYPE_E { > > + MXL_EAGLE_DEMOD_TYPE_QAM, /* Mxl248 or Mxl692 */ > > + MXL_EAGLE_DEMOD_TYPE_OOB, /* Mxl248 only */ > > + MXL_EAGLE_DEMOD_TYPE_ATSC /* Mxl691 or Mxl692 */ > > +}; > > + > > +/* Enum of power modes. Used for initial > > + * activation, or for activating sleep mode > > + */ > > +enum MXL_EAGLE_POWER_MODE_E { > > + MXL_EAGLE_POWER_MODE_SLEEP, > > + MXL_EAGLE_POWER_MODE_ACTIVE > > +}; > > + > > +/* Enum of GPIOs, used in device GPIO APIs */ > > +enum MXL_EAGLE_GPIO_NUMBER_E { > > + MXL_EAGLE_GPIO_NUMBER_0, > > + MXL_EAGLE_GPIO_NUMBER_1, > > + MXL_EAGLE_GPIO_NUMBER_2, > > + MXL_EAGLE_GPIO_NUMBER_3, > > + MXL_EAGLE_GPIO_NUMBER_4, > > + MXL_EAGLE_GPIO_NUMBER_5, > > + MXL_EAGLE_GPIO_NUMBER_6 > > +}; > > + > > +/* Enum of GPIO directions, used in GPIO direction configuration API */ > > +enum MXL_EAGLE_GPIO_DIRECTION_E { > > + MXL_EAGLE_GPIO_DIRECTION_INPUT, > > + MXL_EAGLE_GPIO_DIRECTION_OUTPUT > > +}; > > + > > +/* Enum of GPIO level, used in device GPIO APIs */ > > +enum MXL_EAGLE_GPIO_LEVEL_E { > > + MXL_EAGLE_GPIO_LEVEL_LOW, > > + MXL_EAGLE_GPIO_LEVEL_HIGH, > > +}; > > + > > +/* Enum of I/O Mux function, used in device I/O mux configuration API */ > > +enum MXL_EAGLE_IOMUX_FUNCTION_E { > > + MXL_EAGLE_IOMUX_FUNC_FEC_LOCK, > > + MXL_EAGLE_IOMUX_FUNC_MERR, > > +}; > > + > > +/* Enum of MPEG Data format, used in MPEG and OOB output configuration */ > > +enum MXL_EAGLE_MPEG_DATA_FORMAT_E { > > + MXL_EAGLE_DATA_SERIAL_LSB_1ST = 0, > > + MXL_EAGLE_DATA_SERIAL_MSB_1ST, > > + > > + MXL_EAGLE_DATA_SYNC_WIDTH_BIT = 0, > > + MXL_EAGLE_DATA_SYNC_WIDTH_BYTE > > +}; > > + > > +/* Enum of MPEG Clock format, used in MPEG and OOB output configuration */ > > +enum MXL_EAGLE_MPEG_CLOCK_FORMAT_E { > > + MXL_EAGLE_CLOCK_ACTIVE_HIGH = 0, > > + MXL_EAGLE_CLOCK_ACTIVE_LOW, > > + > > + MXL_EAGLE_CLOCK_POSITIVE = 0, > > + MXL_EAGLE_CLOCK_NEGATIVE, > > + > > + MXL_EAGLE_CLOCK_IN_PHASE = 0, > > + MXL_EAGLE_CLOCK_INVERTED, > > +}; > > + > > +/* Enum of MPEG Clock speeds, used in MPEG output configuration */ > > +enum MXL_EAGLE_MPEG_CLOCK_RATE_E { > > + MXL_EAGLE_MPEG_CLOCK_54MHZ, > > + MXL_EAGLE_MPEG_CLOCK_40_5MHZ, > > + MXL_EAGLE_MPEG_CLOCK_27MHZ, > > + MXL_EAGLE_MPEG_CLOCK_13_5MHZ, > > +}; > > + > > +/* Enum of Interrupt mask bit, used in host interrupt configuration */ > > +enum MXL_EAGLE_INTR_MASK_BITS_E { > > + MXL_EAGLE_INTR_MASK_DEMOD = 0, > > + MXL_EAGLE_INTR_MASK_SMA_RX = 1, > > + MXL_EAGLE_INTR_MASK_WDOG = 31 > > +}; > > + > > +/* Enum of QAM Demodulator type, used in QAM configuration */ > > +enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E { > > + MXL_EAGLE_QAM_DEMOD_ANNEX_B, /* J.83B */ > > + MXL_EAGLE_QAM_DEMOD_ANNEX_A, /* DVB-C */ > > +}; > > + > > +/* Enum of QAM Demodulator modulation, used in QAM configuration and status */ > > +enum MXL_EAGLE_QAM_DEMOD_QAM_TYPE_E { > > + MXL_EAGLE_QAM_DEMOD_QAM16, > > + MXL_EAGLE_QAM_DEMOD_QAM64, > > + MXL_EAGLE_QAM_DEMOD_QAM256, > > + MXL_EAGLE_QAM_DEMOD_QAM1024, > > + MXL_EAGLE_QAM_DEMOD_QAM32, > > + MXL_EAGLE_QAM_DEMOD_QAM128, > > + MXL_EAGLE_QAM_DEMOD_QPSK, > > + MXL_EAGLE_QAM_DEMOD_AUTO, > > +}; > > + > > +/* Enum of Demodulator IQ setup, used in QAM, OOB configuration and status */ > > +enum MXL_EAGLE_IQ_FLIP_E { > > + MXL_EAGLE_DEMOD_IQ_NORMAL, > > + MXL_EAGLE_DEMOD_IQ_FLIPPED, > > + MXL_EAGLE_DEMOD_IQ_AUTO, > > +}; > > + > > +/* Enum of OOB Demodulator symbol rates, used in OOB configuration */ > > +enum MXL_EAGLE_OOB_DEMOD_SYMB_RATE_E { > > + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_0_772MHZ, /* ANSI/SCTE 55-2 0.772 MHz */ > > + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_024MHZ, /* ANSI/SCTE 55-1 1.024 MHz */ > > + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_544MHZ, /* ANSI/SCTE 55-2 1.544 MHz */ > > +}; > > + > > +/* Enum of tuner channel tuning mode */ > > +enum MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_E { > > + MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW, /* Normal "view" mode */ > > + MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_SCAN, /* Fast "scan" mode */ > > +}; > > + > > +/* Enum of tuner bandwidth */ > > +enum MXL_EAGLE_TUNER_BW_E { > > + MXL_EAGLE_TUNER_BW_6MHZ, > > + MXL_EAGLE_TUNER_BW_7MHZ, > > + MXL_EAGLE_TUNER_BW_8MHZ, > > +}; > > + > > +/* Enum of tuner bandwidth */ > > +enum MXL_EAGLE_JUNCTION_TEMPERATURE_E { > > + MXL_EAGLE_JUNCTION_TEMPERATURE_BELOW_0_CELSIUS = 0, > > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_0_TO_14_CELSIUS = 1, > > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_14_TO_28_CELSIUS = 3, > > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_28_TO_42_CELSIUS = 2, > > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_42_TO_57_CELSIUS = 6, > > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_57_TO_71_CELSIUS = 7, > > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_71_TO_85_CELSIUS = 5, > > + MXL_EAGLE_JUNCTION_TEMPERATURE_ABOVE_85_CELSIUS = 4, > > +}; > > + > > +/* Struct passed in optional callback used during FW download */ > > +struct MXL_EAGLE_FW_DOWNLOAD_CB_PAYLOAD_T { > > + u32 total_len; > > + u32 downloaded_len; > > +}; > > + > > +/* Struct used of I2C protocol between host and Eagle, internal use only */ > > +struct __packed MXL_EAGLE_HOST_MSG_HEADER_T { > > + u8 opcode; > > + u8 seqnum; > > + u8 payload_size; > > + u8 status; > > + u32 checksum; > > +}; > > + > > +/* Device version information struct */ > > +struct __packed MXL_EAGLE_DEV_VER_T { > > + u8 chip_id; > > + u8 firmware_ver[MXL_EAGLE_VERSION_SIZE]; > > + u8 mxlware_ver[MXL_EAGLE_VERSION_SIZE]; > > +}; > > + > > +/* Xtal configuration struct */ > > +struct __packed MXL_EAGLE_DEV_XTAL_T { > > + u8 xtal_cap; /* accepted range is 1..31 pF. Default is 26 */ > > + u8 clk_out_enable; > > + u8 clk_out_div_enable; /* clock out freq is xtal freq / 6 */ > > + u8 xtal_sharing_enable; /* if enabled set xtal_cap to 25 pF */ > > + u8 xtal_calibration_enable; /* enable for master, disable for slave */ > > +}; > > + > > +/* GPIO direction struct, internally used in GPIO configuration API */ > > +struct __packed MXL_EAGLE_DEV_GPIO_DIRECTION_T { > > + u8 gpio_number; > > + u8 gpio_direction; > > +}; > > + > > +/* GPO level struct, internally used in GPIO configuration API */ > > +struct __packed MXL_EAGLE_DEV_GPO_LEVEL_T { > > + u8 gpio_number; > > + u8 gpo_level; > > +}; > > + > > +/* Device Status struct */ > > +struct MXL_EAGLE_DEV_STATUS_T { > > + u8 temperature; > > + u8 demod_type; > > + u8 power_mode; > > + u8 cpu_utilization_percent; > > +}; > > + > > +/* Device interrupt configuration struct */ > > +struct __packed MXL_EAGLE_DEV_INTR_CFG_T { > > + u32 intr_mask; > > + u8 edge_trigger; > > + u8 positive_trigger; > > + u8 global_enable_interrupt; > > +}; > > + > > +/* MPEG pad drive parameters, used on MPEG output configuration */ > > +/* See MXL_EAGLE_IO_MUX_DRIVE_MODE_E */ > > +struct MXL_EAGLE_MPEG_PAD_DRIVE_T { > > + u8 pad_drv_mpeg_syn; > > + u8 pad_drv_mpeg_dat; > > + u8 pad_drv_mpeg_val; > > + u8 pad_drv_mpeg_clk; > > +}; > > + > > +/* MPEGOUT parameter struct, used in MPEG output configuration */ > > +struct MXL_EAGLE_MPEGOUT_PARAMS_T { > > + u8 mpeg_parallel; > > + u8 msb_first; > > + u8 mpeg_sync_pulse_width; /* See MXL_EAGLE_MPEG_DATA_FORMAT_E */ > > + u8 mpeg_valid_pol; > > + u8 mpeg_sync_pol; > > + u8 mpeg_clk_pol; > > + u8 mpeg3wire_mode_enable; > > + u8 mpeg_clk_freq; > > + struct MXL_EAGLE_MPEG_PAD_DRIVE_T mpeg_pad_drv; > > +}; > > + > > +/* QAM Demodulator parameters struct, used in QAM params configuration */ > > +struct __packed MXL_EAGLE_QAM_DEMOD_PARAMS_T { > > + u8 annex_type; > > + u8 qam_type; > > + u8 iq_flip; > > + u8 search_range_idx; > > + u8 spur_canceller_enable; > > + u32 symbol_rate_hz; > > + u32 symbol_rate_256qam_hz; > > +}; > > + > > +/* QAM Demodulator status */ > > +struct MXL_EAGLE_QAM_DEMOD_STATUS_T { > > + u8 annex_type; > > + u8 qam_type; > > + u8 iq_flip; > > + u8 interleaver_depth_i; > > + u8 interleaver_depth_j; > > + u8 qam_locked; > > + u8 fec_locked; > > + u8 mpeg_locked; > > + u16 snr_db_tenths; > > + s16 timing_offset; > > + s32 carrier_offset_hz; > > +}; > > + > > +/* QAM Demodulator error counters */ > > +struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T { > > + u32 corrected_code_words; > > + u32 uncorrected_code_words; > > + u32 total_code_words_received; > > + u32 corrected_bits; > > + u32 error_mpeg_frames; > > + u32 mpeg_frames_received; > > + u32 erasures; > > +}; > > + > > +/* QAM Demodulator constellation point */ > > +struct MXL_EAGLE_QAM_DEMOD_CONSTELLATION_VAL_T { > > + s16 i_value[12]; > > + s16 q_value[12]; > > +}; > > + > > +/* QAM Demodulator equalizer filter taps */ > > +struct MXL_EAGLE_QAM_DEMOD_EQU_FILTER_T { > > + s16 ffe_taps[MXL_EAGLE_QAM_FFE_TAPS_LENGTH]; > > + s16 spur_taps[MXL_EAGLE_QAM_SPUR_TAPS_LENGTH]; > > + s16 dfe_taps[MXL_EAGLE_QAM_DFE_TAPS_LENGTH]; > > + u8 ffe_leading_tap_index; > > + u8 dfe_taps_number; > > +}; > > + > > +/* OOB Demodulator parameters struct, used in OOB params configuration */ > > +struct __packed MXL_EAGLE_OOB_DEMOD_PARAMS_T { > > + u8 symbol_rate; > > + u8 iq_flip; > > + u8 clk_pol; > > +}; > > + > > +/* OOB Demodulator error counters */ > > +struct MXL_EAGLE_OOB_DEMOD_ERROR_COUNTERS_T { > > + u32 corrected_packets; > > + u32 uncorrected_packets; > > + u32 total_packets_received; > > +}; > > + > > +/* OOB status */ > > +struct __packed MXL_EAGLE_OOB_DEMOD_STATUS_T { > > + u16 snr_db_tenths; > > + s16 timing_offset; > > + s32 carrier_offsetHz; > > + u8 qam_locked; > > + u8 fec_locked; > > + u8 mpeg_locked; > > + u8 retune_required; > > + u8 iq_flip; > > +}; > > + > > +/* ATSC Demodulator status */ > > +struct __packed MXL_EAGLE_ATSC_DEMOD_STATUS_T { > > + s16 snr_db_tenths; > > + s16 timing_offset; > > + s32 carrier_offset_hz; > > + u8 frame_lock; > > + u8 atsc_lock; > > + u8 fec_lock; > > +}; > > + > > +/* ATSC Demodulator error counters */ > > +struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T { > > + u32 error_packets; > > + u32 total_packets; > > + u32 error_bytes; > > +}; > > + > > +/* ATSC Demodulator equalizers filter taps */ > > +struct __packed MXL_EAGLE_ATSC_DEMOD_EQU_FILTER_T { > > + s16 ffe_taps[MXL_EAGLE_ATSC_FFE_TAPS_LENGTH]; > > + s8 dfe_taps[MXL_EAGLE_ATSC_DFE_TAPS_LENGTH]; > > +}; > > + > > +/* Tuner AGC Status */ > > +struct __packed MXL_EAGLE_TUNER_AGC_STATUS_T { > > + u8 locked; > > + u16 raw_agc_gain; /* AGC gain [dB] = rawAgcGain / 2^6 */ > > + s16 rx_power_db_hundredths; > > +}; > > + > > +/* Tuner channel tune parameters */ > > +struct __packed MXL_EAGLE_TUNER_CHANNEL_PARAMS_T { > > + u32 freq_hz; > > + u8 tune_mode; > > + u8 bandwidth; > > +}; > > + > > +/* Tuner channel lock indications */ > > +struct __packed MXL_EAGLE_TUNER_LOCK_STATUS_T { > > + u8 rf_pll_locked; > > + u8 ref_pll_locked; > > +}; > > + > > +/* Smart antenna parameters used in Smart antenna params configuration */ > > +struct __packed MXL_EAGLE_SMA_PARAMS_T { > > + u8 full_duplex_enable; > > + u8 rx_disable; > > + u8 idle_logic_high; > > +}; > > + > > +/* Smart antenna message format */ > > +struct __packed MXL_EAGLE_SMA_MESSAGE_T { > > + u32 payload_bits; > > + u8 total_num_bits; > > +}; > > + > > -- > > 2.23.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver 2020-06-26 17:41 ` Bradford Love @ 2020-06-29 17:33 ` Sean Young 0 siblings, 0 replies; 20+ messages in thread From: Sean Young @ 2020-06-29 17:33 UTC (permalink / raw) To: Bradford Love; +Cc: linux-media Hi Brad, On Fri, Jun 26, 2020 at 12:41:08PM -0500, Bradford Love wrote: > Hi Sean, > > > On Wed, Jun 24, 2020 at 4:59 AM Sean Young <sean@mess.org> wrote: > > > > Hi Brad, > > > > Thank you for the patch, looks great apart from some minor issues. > > > > First of all, sparse finds a number of issues, it would be great to have > > those resolved. > > > Thanks for the feedback. I'm aware of some of the issues that were > reported to me by various bots and intend on fixing them. I'll have to > utilize sparse to see what else you're talking about. I know what you mean, I don't have enough time at all. There is a bunch of interesting stuff that needs doing like dvb dma-buf, dvb_attach removal, cleaning up drivers.. > A lot of the oddities you mention below are a symptom of porting the > downstream driver I used as reference. I'll work on them when I have > time, I just wanted to get this out there first for anyone to test. I think we can live with some strange endian handling. Too merge it, the sparse issues must be resolved. If you could remove the g_big_endian and replace it with #ifdef __BIG_ENDIAN, that would be great too. Thanks for you work Sean > > > > > > > On Fri, Jun 12, 2020 at 01:39:35PM -0500, Brad Love wrote: > > > MaxLinear 692 is a combo demod/tuner which has ATSC and QAM > > > capabilities. Only ATSC is currently advertised via DVB > > > properties. QAM still has issues. > > > > > > > > > Signed-off-by: Brad Love <brad@nextdimension.cc> > > > --- > > > drivers/media/dvb-frontends/Kconfig | 9 + > > > drivers/media/dvb-frontends/Makefile | 1 + > > > drivers/media/dvb-frontends/mxl692.c | 1363 +++++++++++++++++++++ > > > drivers/media/dvb-frontends/mxl692.h | 38 + > > > drivers/media/dvb-frontends/mxl692_defs.h | 493 ++++++++ > > > 5 files changed, 1904 insertions(+) > > > create mode 100644 drivers/media/dvb-frontends/mxl692.c > > > create mode 100644 drivers/media/dvb-frontends/mxl692.h > > > create mode 100644 drivers/media/dvb-frontends/mxl692_defs.h > > > > > > diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig > > > index 643b851a6b60..b1ded2137f0e 100644 > > > --- a/drivers/media/dvb-frontends/Kconfig > > > +++ b/drivers/media/dvb-frontends/Kconfig > > > @@ -708,6 +708,15 @@ config DVB_S5H1411 > > > An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want > > > to support this frontend. > > > > > > +config DVB_MXL692 > > > + tristate "MaxLinear MXL692 based" > > > + depends on DVB_CORE && I2C > > > + default m if !MEDIA_SUBDRV_AUTOSELECT > > > + help > > > + MaxLinear MxL692 is a combo tuner-demodulator that > > > + supports ATSC 8VSB and QAM modes. Say Y when you want to > > > + support this frontend. > > > + > > > comment "ISDB-T (terrestrial) frontends" > > > depends on DVB_CORE > > > > > > diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile > > > index e9179162658c..b9f47d68e14e 100644 > > > --- a/drivers/media/dvb-frontends/Makefile > > > +++ b/drivers/media/dvb-frontends/Makefile > > > @@ -55,6 +55,7 @@ obj-$(CONFIG_DVB_S5H1420) += s5h1420.o > > > obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o > > > obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o > > > obj-$(CONFIG_DVB_LGDT3306A) += lgdt3306a.o > > > +obj-$(CONFIG_DVB_MXL692) += mxl692.o > > > obj-$(CONFIG_DVB_LG2160) += lg2160.o > > > obj-$(CONFIG_DVB_CX24123) += cx24123.o > > > obj-$(CONFIG_DVB_LNBH25) += lnbh25.o > > > diff --git a/drivers/media/dvb-frontends/mxl692.c b/drivers/media/dvb-frontends/mxl692.c > > > new file mode 100644 > > > index 000000000000..622b7a7ebab5 > > > --- /dev/null > > > +++ b/drivers/media/dvb-frontends/mxl692.c > > > @@ -0,0 +1,1363 @@ > > > +// SPDX-License-Identifier: GPL-2.0 > > > +/* > > > + * Driver for the MaxLinear MxL69x family of combo tuners/demods > > > + * > > > + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> > > > + * > > > + * based on code: > > > + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved > > > + * which was released under GPL V2 > > > + * > > > + * This program is free software; you can redistribute it and/or > > > + * modify it under the terms of the GNU General Public License > > > + * version 2, as published by the Free Software Foundation. > > > + * > > > + * This program is distributed in the hope that it will be useful, > > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > > + * GNU General Public License for more details. > > > + */ > > > + > > > +#include <linux/mutex.h> > > > +#include <linux/i2c-mux.h> > > > +#include <linux/string.h> > > > +#include <linux/firmware.h> > > > + > > > +#include "mxl692.h" > > > +#include "mxl692_defs.h" > > > + > > > +static int g_big_endian = -1; > > > > g_big_endian is not needed. You can use > > > > #ifdef __BIG_ENDIAN > > ... > > #endif > > > It's on my list to retool the various endian code. > > > > > > > > + > > > +static const struct dvb_frontend_ops mxl692_ops; > > > + > > > +struct mxl692_dev { > > > + struct dvb_frontend fe; > > > + struct i2c_client *i2c_client; > > > + struct mutex i2c_lock; /* i2c command mutex */ > > > + enum MXL_EAGLE_DEMOD_TYPE_E demod_type; > > > + enum MXL_EAGLE_POWER_MODE_E power_mode; > > > + u32 current_frequency; > > > + int device_type; > > > + int seqnum; > > > + int init_done; > > > +}; > > > + > > > +static int mxl692_i2c_write(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) > > > +{ > > > + int ret = 0; > > > + struct i2c_msg msg = { > > > + .addr = dev->i2c_client->addr, > > > + .flags = 0, > > > + .buf = buffer, > > > + .len = buf_len > > > + }; > > > + > > > + ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1); > > > + if (ret != 1) > > > + dev_err(&dev->i2c_client->dev, "i2c write error!\n"); > > > + > > > + return ret; > > > +} > > > + > > > +static int mxl692_i2c_read(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) > > > +{ > > > + int ret = 0; > > > + struct i2c_msg msg = { > > > + .addr = dev->i2c_client->addr, > > > + .flags = I2C_M_RD, > > > + .buf = buffer, > > > + .len = buf_len > > > + }; > > > + > > > + ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1); > > > + if (ret != 1) > > > + dev_err(&dev->i2c_client->dev, "i2c read error!\n"); > > > + > > > + return ret; > > > +} > > > + > > > +static void detect_endianness(void) > > > +{ > > > + u32 temp = 1; > > > + u8 *p_temp = (u8 *)&temp; > > > + > > > + g_big_endian = (*p_temp == 0) ? 1 : 0; > > > + > > > + if (g_big_endian) > > > + pr_debug("%s( BIG )\n", __func__); > > > + else > > > + pr_debug("%s( SMALL )\n", __func__); > > > +} > > > > Function not needed, please remove. > > > > > +static int convert_endian(u32 size, u8 *d) > > > +{ > > > + u32 i; > > > + > > > + for (i = 0; i < (size & ~3); i += 4) { > > > + d[i + 0] ^= d[i + 3]; > > > + d[i + 3] ^= d[i + 0]; > > > + d[i + 0] ^= d[i + 3]; > > > + > > > + d[i + 1] ^= d[i + 2]; > > > + d[i + 2] ^= d[i + 1]; > > > + d[i + 1] ^= d[i + 2]; > > > > If I'm reading this correctly -- and it's hard to parse -- this byte > > swapping byte 0 and 3, and 1 and 2. convert_endian is only called on > > little endian. So this is doing le32_to_cpus()? > > > > > This zero copy endian conversion was pulled from the mxl5xx.c driver. > These chips expect everything in big endian. I'll look at using the > macros you describe here and below. > > > > > > > > + } > > > + > > > + switch (size & 3) { > > > + case 0: > > > + case 1: > > > + /* do nothing */ > > > + break; > > > + case 2: > > > + d[i + 0] ^= d[i + 1]; > > > + d[i + 1] ^= d[i + 0]; > > > + d[i + 0] ^= d[i + 1]; > > > > le16_to_cpus()? > > > > > + break; > > > + > > > + case 3: > > > + d[i + 0] ^= d[i + 2]; > > > + d[i + 2] ^= d[i + 0]; > > > + d[i + 0] ^= d[i + 2]; > > > > What is this? le24_to_cpus()? > > > > > + break; > > > + } > > > + return size; > > > +} > > > + > > > +static int convert_endian_n(int n, u32 size, u8 *d) > > > +{ > > > + int i, count = 0; > > > + > > > + for (i = 0; i < n; i += size) > > > + count += convert_endian(size, d + i); > > > + return count; > > > +} > > > > These functions are really hard to read. It would be much better if > > the structs like MXL_EAGLE_TUNER_CHANNEL_PARAMS_T have their freq_hz > > field to be type be32 rather than u32, and do the conversion when it > > is written/read rather than patching it up to the right endianness. > > > > mxl692_tx_swap() and mxl692_rx_swap() both really should be removed, > > if possible. > > > I'm hesitant to change the structs as they came exactly as they are from MXL. > > > > > > > > > + > > > +static void mxl692_tx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) > > > +{ > > > + if (g_big_endian) > > > + return; > > > + > > > + buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ > > > + > > > + switch (opcode) { > > > + case MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET: > > > + case MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET: > > > + case MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET: > > > + buffer += convert_endian(sizeof(u32), buffer); > > > + break; > > > + case MXL_EAGLE_OPCODE_QAM_PARAMS_SET: > > > + buffer += 5; > > > + buffer += convert_endian(2 * sizeof(u32), buffer); > > > + break; > > > + default: > > > + /* no swapping - all get opcodes */ > > > + /* ATSC/OOB no swapping */ > > > + break; > > > + } > > > +} > > > + > > > +static void mxl692_rx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) > > > +{ > > > + if (g_big_endian) > > > + return; > > > + > > > + buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ > > > + > > > + switch (opcode) { > > > + case MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET: > > > + buffer++; > > > + buffer += convert_endian(2 * sizeof(u16), buffer); > > > + break; > > > + case MXL_EAGLE_OPCODE_ATSC_STATUS_GET: > > > + buffer += convert_endian_n(2, sizeof(u16), buffer); > > > + buffer += convert_endian(sizeof(u32), buffer); > > > + break; > > > + case MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET: > > > + buffer += convert_endian(3 * sizeof(u32), buffer); > > > + break; > > > + case MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET: > > > + buffer += convert_endian_n(24, sizeof(u16), buffer); > > > + break; > > > + case MXL_EAGLE_OPCODE_QAM_STATUS_GET: > > > + buffer += 8; > > > + buffer += convert_endian_n(2, sizeof(u16), buffer); > > > + buffer += convert_endian(sizeof(u32), buffer); > > > + break; > > > + case MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET: > > > + buffer += convert_endian(7 * sizeof(u32), buffer); > > > + break; > > > + case MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET: > > > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET: > > > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET: > > > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET: > > > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET: > > > + buffer += convert_endian_n(24, sizeof(u16), buffer); > > > + break; > > > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET: > > > + buffer += convert_endian_n(8, sizeof(u16), buffer); > > > + break; > > > + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET: > > > + buffer += convert_endian_n(17, sizeof(u16), buffer); > > > + break; > > > + case MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET: > > > + buffer += convert_endian(3 * sizeof(u32), buffer); > > > + break; > > > + case MXL_EAGLE_OPCODE_OOB_STATUS_GET: > > > + buffer += convert_endian_n(2, sizeof(u16), buffer); > > > + buffer += convert_endian(sizeof(u32), buffer); > > > + break; > > > + case MXL_EAGLE_OPCODE_SMA_RECEIVE_GET: > > > + buffer += convert_endian(sizeof(u32), buffer); > > > + break; > > > + default: > > > + /* no swapping - all set opcodes */ > > > + break; > > > + } > > > +} > > > + > > > +static u32 mxl692_checksum(u8 *buffer, u32 size) > > > +{ > > > + u32 ix, remainder = 0, cur_cksum = 0; > > > + > > > + for (ix = 0; ix < size / 4; ix++) > > > + cur_cksum += cpu_to_be32(*(u32 *)(buffer + > > > + (ix * sizeof(u32)))); > > > > This is a bit messy. If buffer is a u32 pointer, you could do buffer[ix] > > and avoid the cast. > > > Yeh, I don't like this either, but it's another reference artifact. > > > > > > > > + remainder = size % 4; > > > + if (remainder > 0) > > > + cur_cksum += cpu_to_be32(*((u32 *)&buffer[size - remainder])); > > > > Ehm. If there is a remainder, then do another cksum += .. ? Can't this be > > solved by merging this into the above loop? (i.e. one more iteration if > > size % 4 > 0: > > > I'll see if that still passes checksum calc and rework this function. > A lot of your comments here are already on my list to do as I have > time. > > > > > > > for (ix = 0; ix < size; ix += 4) > > cur_cksum += cpu_to_be32(*(u32 *)(buffer + ix)); > > > > Since this is calculating the checksum, I would expect it do X_to_cpu(), > > not cpu_to_X. > > > The checksum is in big endian. Everything "checks out" according to > the hardware for all transactions. > > > > > > > + > > > + cur_cksum ^= 0xDEADBEEF; > > > + > > > + return be32_to_cpu(cur_cksum); > > > +} > > > + > > > +static int mxl692_validate_fw_header(const u8 *buffer, u32 buf_len) > > > +{ > > > + int status = 0; > > > + u32 ix, temp = 0; > > > + u32 *local_buf = NULL; > > > + > > > + if (buffer[0] != 0x4D || buffer[1] != 0x31 || > > > + buffer[2] != 0x10 || buffer[3] != 0x02 || > > > + buffer[4] != 0x40 || buffer[5] != 0x00 || > > > + buffer[6] != 0x00 || buffer[7] != 0x80) { > > > + status = -EINVAL; > > > + goto err_finish; > > > + } > > > > memcmp() might be shorter. > > > Agreed. > > > > > > > + > > > + local_buf = (u32 *)(buffer + 8); > > > + temp = cpu_to_be32(*(u32 *)local_buf); > > > + > > > + if ((buf_len - 16) != (temp >> 8)) { > > > + status = -EINVAL; > > > + goto err_finish; > > > + } > > > + > > > + temp = 0; > > > + for (ix = 16; ix < buf_len; ix++) > > > + temp += buffer[ix]; > > > + > > > + if ((u8)temp != buffer[11]) > > > > By making temp u8 you avoid the cast. > > > Noted. > > > > > > > + status = -EINVAL; > > > +err_finish: > > > + if (status) > > > + pr_err("%s failed! %d\n", __func__, status); > > > + return status; > > > +} > > > + > > > +static int mxl692_write_fw_block(struct mxl692_dev *dev, const u8 *buffer, > > > + u32 buf_len, u32 *index) > > > +{ > > > + int status = 0; > > > + u32 ix = 0, total_len = 0, addr = 0, chunk_len = 0, prevchunk_len = 0; > > > + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; > > > + int payload_max = MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE; > > > + > > > + ix = *index; > > > + > > > + if (buffer[ix] == 0x53) { > > > + total_len = buffer[ix + 1] << 16 | buffer[ix + 2] << 8 | buffer[ix + 3]; > > > + total_len = (total_len + 3) & ~3; > > > + addr = buffer[ix + 4] << 24 | buffer[ix + 5] << 16 | > > > + buffer[ix + 6] << 8 | buffer[ix + 7]; > > > + ix += MXL_EAGLE_FW_SEGMENT_HEADER_SIZE; > > > + > > > + while ((total_len > 0) && (status == 0)) { > > > + plocal_buf = local_buf; > > > + chunk_len = (total_len < payload_max) ? > > > + total_len : payload_max; > > > + > > > + *plocal_buf++ = 0xFC; > > > + *plocal_buf++ = chunk_len + sizeof(u32); > > > + > > > + *(u32 *)plocal_buf = cpu_to_le32(addr + prevchunk_len); > > > + plocal_buf += sizeof(u32); > > > + > > > + memcpy(plocal_buf, &buffer[ix], chunk_len); > > > + convert_endian(chunk_len, plocal_buf); > > > + > > > + if (mxl692_i2c_write(dev, local_buf, > > > + (chunk_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { > > > + status = -EREMOTEIO; > > > + break; > > > + } > > > + > > > + prevchunk_len += chunk_len; > > > + total_len -= chunk_len; > > > + ix += chunk_len; > > > + } > > > + *index = ix; > > > + } else { > > > + status = -EINVAL; > > > + } > > > + > > > + if (status) > > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > > + > > > + return status; > > > +} > > > + > > > +static int mxl692_memwrite(struct mxl692_dev *dev, u32 addr, > > > + u8 *buffer, u32 size) > > > +{ > > > + int status = 0, total_len = 0; > > > + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; > > > + > > > + total_len = size; > > > + total_len = (total_len + 3) & ~3; /* 4 byte alignment */ > > > + > > > + if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE)) > > > + dev_dbg(&dev->i2c_client->dev, "hrmph?\n"); > > > + > > > + plocal_buf = local_buf; > > > + > > > + *plocal_buf++ = 0xFC; > > > + *plocal_buf++ = total_len + sizeof(u32); > > > + > > > + *(u32 *)plocal_buf = addr; > > > + plocal_buf += sizeof(u32); > > > + > > > + memcpy(plocal_buf, buffer, total_len); > > > + if (g_big_endian) > > > + convert_endian(sizeof(u32) + total_len, local_buf + 2); > > > + > > > + if (mxl692_i2c_write(dev, local_buf, > > > + (total_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { > > > + status = -EREMOTEIO; > > > + goto err_finish; > > > + } > > > + > > > + return status; > > > +err_finish: > > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > > + return status; > > > +} > > > + > > > +static int mxl692_memread(struct mxl692_dev *dev, u32 addr, > > > + u8 *buffer, u32 size) > > > +{ > > > + int status = 0; > > > + u8 local_buf[MXL_EAGLE_I2C_MHEADER_SIZE] = {}, *plocal_buf = NULL; > > > + > > > + plocal_buf = local_buf; > > > + > > > + *plocal_buf++ = 0xFB; > > > + *plocal_buf++ = sizeof(u32); > > > + *(u32 *)plocal_buf = addr; > > > + > > > + if (g_big_endian) > > > + convert_endian(sizeof(u32), plocal_buf); > > > + > > > + if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_MHEADER_SIZE) > 0) { > > > + size = (size + 3) & ~3; /* 4 byte alignment */ > > > + status = mxl692_i2c_read(dev, buffer, (u16)size) < 0 ? > > > + -EREMOTEIO : 0; > > > + > > > + if (status == 0 && g_big_endian) > > > + convert_endian(size, buffer); > > > + } else { > > > + status = -EREMOTEIO; > > > + } > > > + > > > + if (status) > > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > > + > > > + return status; > > > +} > > > + > > > +static int mxl692_opwrite(struct mxl692_dev *dev, u8 *buffer, > > > + u32 size) > > > +{ > > > + int status = 0, total_len = 0; > > > + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; > > > + > > > + total_len = size; > > > + total_len = (total_len + 3) & ~3; /* 4 byte alignment */ > > > + > > > + if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) > > > + dev_dbg(&dev->i2c_client->dev, "hrmph?\n"); > > > + > > > + plocal_buf = local_buf; > > > + > > > + *plocal_buf++ = 0xFE; > > > + *plocal_buf++ = (u8)total_len; > > > + > > > + memcpy(plocal_buf, buffer, total_len); > > > + convert_endian(total_len, plocal_buf); > > > + > > > + if (mxl692_i2c_write(dev, local_buf, > > > + (total_len + MXL_EAGLE_I2C_PHEADER_SIZE)) < 0) { > > > + status = -EREMOTEIO; > > > + goto err_finish; > > > + } > > > +err_finish: > > > + if (status) > > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > > + return status; > > > +} > > > + > > > +static int mxl692_opread(struct mxl692_dev *dev, u8 *buffer, > > > + u32 size) > > > +{ > > > + int status = 0; > > > + u32 ix = 0; > > > + u8 local_buf[MXL_EAGLE_I2C_PHEADER_SIZE] = {}; > > > + > > > + local_buf[0] = 0xFD; > > > + local_buf[1] = 0; > > > + > > > + if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_PHEADER_SIZE) > 0) { > > > + size = (size + 3) & ~3; /* 4 byte alignment */ > > > + > > > + //read in 4 byte chunks > > > + for (ix = 0; ix < size; ix += 4) { > > > + if (mxl692_i2c_read(dev, buffer + ix, 4) < 0) { > > > + dev_dbg(&dev->i2c_client->dev, > > > + "ix=%d size=%d\n", ix, size); > > > + status = -EREMOTEIO; > > > + goto err_finish; > > > + } > > > + } > > > + convert_endian(size, buffer); > > > + } else { > > > + status = -EREMOTEIO; > > > + } > > > +err_finish: > > > + if (status) > > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > > + return status; > > > +} > > > + > > > +static int mxl692_i2c_writeread(struct mxl692_dev *dev, > > > + u8 opcode, > > > + u8 *tx_payload, > > > + u8 tx_payload_size, > > > + u8 *rx_payload, > > > + u8 rx_payload_expected) > > > +{ > > > + int status = 0, timeout = 40; > > > + u8 tx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > > > + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > > > + u32 resp_checksum = 0, resp_checksum_tmp = 0; > > > + struct MXL_EAGLE_HOST_MSG_HEADER_T *tx_header; > > > + struct MXL_EAGLE_HOST_MSG_HEADER_T *rx_header; > > > + > > > + mutex_lock(&dev->i2c_lock); > > > + > > > + if ((tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE) > > > > + (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) { > > > + status = -EINVAL; > > > + goto err_finish; > > > + } > > > + > > > + tx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)tx_buf; > > > + tx_header->checksum = 0; > > > + tx_header->opcode = opcode; > > > + tx_header->payload_size = tx_payload_size; > > > + tx_header->seqnum = dev->seqnum++; > > > + > > > + if (dev->seqnum == 0) > > > + dev->seqnum = 1; > > > + > > > + if (tx_payload && tx_payload_size > 0) > > > + memcpy(&tx_buf[MXL_EAGLE_HOST_MSG_HEADER_SIZE], > > > + tx_payload, tx_payload_size); > > > + > > > + mxl692_tx_swap(opcode, tx_buf); > > > + > > > + tx_header->checksum = 0; > > > + tx_header->checksum = mxl692_checksum(tx_buf, > > > + MXL_EAGLE_HOST_MSG_HEADER_SIZE + tx_payload_size); > > > + > > > + /* send Tx message */ > > > + status = mxl692_opwrite(dev, tx_buf, > > > + tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE); > > > + if (status) { > > > + status = -EREMOTEIO; > > > + goto err_finish; > > > + } > > > + > > > + /* receive Rx message (polling) */ > > > + rx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)rx_buf; > > > + > > > + do { > > > + status = mxl692_opread(dev, rx_buf, > > > + rx_payload_expected + MXL_EAGLE_HOST_MSG_HEADER_SIZE); > > > + usleep_range(1000, 2000); > > > + timeout--; > > > + } while ((timeout > 0) && (status == 0) && > > > + (rx_header->seqnum == 0) && > > > + (rx_header->checksum == 0)); > > > + > > > + if (timeout == 0 || status) { > > > + dev_dbg(&dev->i2c_client->dev, "timeout=%d status=%d\n", > > > + timeout, status); > > > + status = -ETIMEDOUT; > > > + goto err_finish; > > > + } > > > + > > > + if (rx_header->status) { > > > + status = (int)rx_header->status; > > > + goto err_finish; > > > + } > > > + > > > + if (rx_header->seqnum != tx_header->seqnum || > > > + rx_header->opcode != tx_header->opcode || > > > + rx_header->payload_size != rx_payload_expected) { > > > + dev_dbg(&dev->i2c_client->dev, "Something failed seq=%s opcode=%s pSize=%s\n", > > > + rx_header->seqnum != tx_header->seqnum ? "X" : "0", > > > + rx_header->opcode != tx_header->opcode ? "X" : "0", > > > + rx_header->payload_size != rx_payload_expected ? "X" : "0"); > > > + if (rx_header->payload_size != rx_payload_expected) > > > + dev_dbg(&dev->i2c_client->dev, "rx_header->payloadSize=%d rx_payload_expected=%d\n", > > > + rx_header->payload_size, rx_payload_expected); > > > + status = -EREMOTEIO; > > > + goto err_finish; > > > + } > > > + > > > + resp_checksum = rx_header->checksum; > > > + rx_header->checksum = 0; > > > + resp_checksum_tmp = mxl692_checksum(rx_buf, > > > + MXL_EAGLE_HOST_MSG_HEADER_SIZE + rx_header->payload_size); > > > + > > > + if (resp_checksum != resp_checksum_tmp) { > > > + status = -EREMOTEIO; > > > + goto err_finish; > > > + } > > > + > > > + mxl692_rx_swap(rx_header->opcode, rx_buf); > > > + > > > + if (rx_header->payload_size > 0) { > > > + if (!rx_payload) { > > > + status = -EREMOTEIO; > > > + goto err_finish; > > > + } > > > + memcpy(rx_payload, rx_buf + MXL_EAGLE_HOST_MSG_HEADER_SIZE, > > > + rx_header->payload_size); > > > + } > > > +err_finish: > > > + if (status) > > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > > + > > > + mutex_unlock(&dev->i2c_lock); > > > + return status; > > > +} > > > + > > > +static int mxl692_fwdownload(struct mxl692_dev *dev, > > > + const u8 *firmware_buf, u32 buf_len) > > > +{ > > > + int status = 0; > > > + u32 ix, reg_val = 0x1; > > > + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > > > + struct MXL_EAGLE_DEV_STATUS_T *dev_status; > > > + > > > + if (buf_len < MXL_EAGLE_FW_HEADER_SIZE || > > > + buf_len > MXL_EAGLE_FW_MAX_SIZE_IN_KB * 1000) > > > + return -EINVAL; > > > + > > > + mutex_lock(&dev->i2c_lock); > > > + > > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > > + > > > + status = mxl692_validate_fw_header(firmware_buf, buf_len); > > > + if (status) > > > + goto err_finish; > > > + > > > + ix = 16; > > > + status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* DRAM */ > > > + if (status) > > > + goto err_finish; > > > + > > > + status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* IRAM */ > > > + if (status) > > > + goto err_finish; > > > + > > > + /* release CPU from reset */ > > > + status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + mutex_unlock(&dev->i2c_lock); > > > + > > > + if (status == 0) { > > > + /* verify FW is alive */ > > > + usleep_range(MXL_EAGLE_FW_LOAD_TIME * 1000, (MXL_EAGLE_FW_LOAD_TIME + 5) * 1000); > > > + dev_status = (struct MXL_EAGLE_DEV_STATUS_T *)&rx_buf; > > > + status = mxl692_i2c_writeread(dev, > > > + MXL_EAGLE_OPCODE_DEVICE_STATUS_GET, > > > + NULL, > > > + 0, > > > + (u8 *)dev_status, > > > + sizeof(struct MXL_EAGLE_DEV_STATUS_T)); > > > + } > > > + > > > + return status; > > > +err_finish: > > > + mutex_unlock(&dev->i2c_lock); > > > + if (status) > > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > > + return status; > > > +} > > > + > > > +static int mxl692_get_versions(struct mxl692_dev *dev) > > > +{ > > > + int status = 0; > > > + struct MXL_EAGLE_DEV_VER_T dev_ver = {}; > > > + static const char * const chip_id[] = {"N/A", "691", "248", "692"}; > > > + > > > + status = mxl692_i2c_writeread(dev, MXL_EAGLE_OPCODE_DEVICE_VERSION_GET, > > > + NULL, > > > + 0, > > > + (u8 *)&dev_ver, > > > + sizeof(struct MXL_EAGLE_DEV_VER_T)); > > > + if (status) > > > + return status; > > > + > > > + dev_info(&dev->i2c_client->dev, "MxL692_DEMOD Chip ID: %s\n", > > > + chip_id[dev_ver.chip_id]); > > > + > > > + dev_info(&dev->i2c_client->dev, > > > + "MxL692_DEMOD FW Version: %d.%d.%d.%d_RC%d\n", > > > + dev_ver.firmware_ver[0], > > > + dev_ver.firmware_ver[1], > > > + dev_ver.firmware_ver[2], > > > + dev_ver.firmware_ver[3], > > > + dev_ver.firmware_ver[4]); > > > + > > > + return status; > > > +} > > > + > > > +static int mxl692_reset(struct mxl692_dev *dev) > > > +{ > > > + int status = 0; > > > + u32 dev_type = MXL_EAGLE_DEVICE_MAX, reg_val = 0x2; > > > + > > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > > + > > > + /* legacy i2c override */ > > > + status = mxl692_memwrite(dev, 0x80000100, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + /* verify sku */ > > > + status = mxl692_memread(dev, 0x70000188, (u8 *)&dev_type, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + if (dev_type != dev->device_type) > > > + goto err_finish; > > > + > > > +err_finish: > > > + if (status) > > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > > + return status; > > > +} > > > + > > > +static int mxl692_config_regulators(struct mxl692_dev *dev, > > > + enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E power_supply) > > > +{ > > > + int status = 0; > > > + u32 reg_val; > > > + > > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > > + > > > + /* configure main regulator according to the power supply source */ > > > + status = mxl692_memread(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + reg_val &= 0x00FFFFFF; > > > + reg_val |= (power_supply == MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE) ? > > > + 0x14000000 : 0x10000000; > > > + > > > + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + /* configure digital regulator to high current mode */ > > > + status = mxl692_memread(dev, 0x90000018, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + reg_val |= 0x800; > > > + > > > + status = mxl692_memwrite(dev, 0x90000018, (u8 *)®_val, sizeof(u32)); > > > + > > > +err_finish: > > > + if (status) > > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > > + return status; > > > +} > > > + > > > +static int mxl692_config_xtal(struct mxl692_dev *dev, > > > + struct MXL_EAGLE_DEV_XTAL_T *dev_xtal) > > > +{ > > > + int status = 0; > > > + u32 reg_val, reg_val1; > > > + > > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > > + > > > + status = mxl692_memread(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + /* set XTAL capacitance */ > > > + reg_val &= 0xFFFFFFE0; > > > + reg_val |= dev_xtal->xtal_cap; > > > + > > > + /* set CLK OUT */ > > > + reg_val = dev_xtal->clk_out_enable ? > > > + (reg_val | 0x0100) : (reg_val & 0xFFFFFEFF); > > > + > > > + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + /* set CLK OUT divider */ > > > + reg_val = dev_xtal->clk_out_div_enable ? > > > + (reg_val | 0x0200) : (reg_val & 0xFFFFFDFF); > > > + > > > + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + /* set XTAL sharing */ > > > + reg_val = dev_xtal->xtal_sharing_enable ? > > > + (reg_val | 0x010400) : (reg_val & 0xFFFEFBFF); > > > + > > > + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + /* enable/disable XTAL calibration, based on master/slave device */ > > > + status = mxl692_memread(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + if (dev_xtal->xtal_calibration_enable) { > > > + /* enable XTAL calibration and set XTAL amplitude to a higher value */ > > > + reg_val1 &= 0xFFFFFFFD; > > > + reg_val1 |= 0x30; > > > + > > > + status = mxl692_memwrite(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + } else { > > > + /* disable XTAL calibration */ > > > + reg_val1 |= 0x2; > > > + > > > + status = mxl692_memwrite(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + /* set XTAL bias value */ > > > + status = mxl692_memread(dev, 0x9000002c, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + reg_val &= 0xC0FFFFFF; > > > + reg_val |= 0xA000000; > > > + > > > + status = mxl692_memwrite(dev, 0x9000002c, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + } > > > + > > > + /* start XTAL calibration */ > > > + status = mxl692_memread(dev, 0x70000010, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + reg_val |= 0x8; > > > + > > > + status = mxl692_memwrite(dev, 0x70000010, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + status = mxl692_memread(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + reg_val |= 0x10; > > > + > > > + status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + status = mxl692_memread(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + reg_val &= 0xFFFFEFFF; > > > + > > > + status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + reg_val |= 0x1000; > > > + > > > + status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); > > > + if (status) > > > + goto err_finish; > > > + > > > + usleep_range(45000, 55000); > > > + > > > +err_finish: > > > + if (status) > > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > > + return status; > > > +} > > > + > > > +static int mxl692_powermode(struct mxl692_dev *dev, > > > + enum MXL_EAGLE_POWER_MODE_E power_mode) > > > +{ > > > + int status = 0; > > > + > > > + dev_dbg(&dev->i2c_client->dev, "%s\n", > > > + power_mode == MXL_EAGLE_POWER_MODE_SLEEP ? > > > + "sleep" : "active"); > > > + > > > + status = mxl692_i2c_writeread(dev, > > > + MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET, > > > + (u8 *)&power_mode, > > > + sizeof(u8), > > > + NULL, > > > + 0); > > > + if (status) > > > + dev_err(&dev->i2c_client->dev, "err %d\n", status); > > > + > > > + dev->power_mode = power_mode; > > > + > > > + return status; > > > +} > > > + > > > +static int mxl692_init(struct dvb_frontend *fe) > > > +{ > > > + struct mxl692_dev *dev = fe->demodulator_priv; > > > + struct i2c_client *client = dev->i2c_client; > > > + struct dtv_frontend_properties *c = &fe->dtv_property_cache; > > > + int status = 0; > > > + const struct firmware *firmware; > > > + struct MXL_EAGLE_DEV_XTAL_T xtal_config = {}; > > > + > > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > > + > > > + if (dev->init_done) > > > + goto warm; > > > + > > > + dev->seqnum = 1; > > > + > > > + status = mxl692_reset(dev); > > > + if (status) > > > + goto err; > > > + > > > + usleep_range(50 * 1000, 60 * 1000); /* was 1000! */ > > > + > > > + status = mxl692_config_regulators(dev, MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL); > > > + if (status) > > > + goto err; > > > + > > > + xtal_config.xtal_cap = 26; > > > + xtal_config.clk_out_div_enable = 0; > > > + xtal_config.clk_out_enable = 0; > > > + xtal_config.xtal_calibration_enable = 0; > > > + xtal_config.xtal_sharing_enable = 1; > > > + status = mxl692_config_xtal(dev, &xtal_config); > > > + if (status) > > > + goto err; > > > + > > > + status = request_firmware(&firmware, MXL692_FIRMWARE, &client->dev); > > > + if (status) { > > > + dev_dbg(&dev->i2c_client->dev, "firmware missing? %s\n", > > > + MXL692_FIRMWARE); > > > + goto err; > > > + } > > > + > > > + status = mxl692_fwdownload(dev, firmware->data, firmware->size); > > > + if (status) > > > + goto err_release_firmware; > > > + > > > + release_firmware(firmware); > > > + > > > +// usleep_range(500 * 1000, 510 * 1000); /* was 1000! */ > > > > Why is this commented out? This should either be explained or removed. > > > This was part of the downstream driver and is temporarily deemed not > required. It is left as a placemarker in case anyone experiences > failure. None of my testers have, so it's probably ok to remove now. > > > > > > + status = mxl692_get_versions(dev); > > > + if (status) > > > + goto err; > > > + > > > + dev->power_mode = MXL_EAGLE_POWER_MODE_SLEEP; > > > + > > > +warm: > > > + /* Init stats here to indicate which stats are supported */ > > > + c->cnr.len = 1; > > > + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > > + c->post_bit_error.len = 1; > > > + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > > + c->post_bit_count.len = 1; > > > + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > > + c->block_error.len = 1; > > > + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > > + > > > + dev->init_done = 1; > > > + return 0; > > > +err_release_firmware: > > > + release_firmware(firmware); > > > +err: > > > + return status; > > > +} > > > + > > > +static int mxl692_sleep(struct dvb_frontend *fe) > > > +{ > > > + struct mxl692_dev *dev = fe->demodulator_priv; > > > + > > > + if (dev->power_mode != MXL_EAGLE_POWER_MODE_SLEEP) > > > + mxl692_powermode(dev, MXL_EAGLE_POWER_MODE_SLEEP); > > > + > > > + return 0; > > > +} > > > + > > > +static int mxl692_set_frontend(struct dvb_frontend *fe) > > > +{ > > > + struct dtv_frontend_properties *p = &fe->dtv_property_cache; > > > + struct mxl692_dev *dev = fe->demodulator_priv; > > > + > > > + int status = 0; > > > + enum MXL_EAGLE_DEMOD_TYPE_E demod_type; > > > + enum MXL_EAGLE_POWER_MODE_E power_mode = MXL_EAGLE_POWER_MODE_ACTIVE; > > > + struct MXL_EAGLE_MPEGOUT_PARAMS_T mpeg_params = {}; > > > + enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E qam_annex = MXL_EAGLE_QAM_DEMOD_ANNEX_B; > > > + struct MXL_EAGLE_QAM_DEMOD_PARAMS_T qam_params = {}; > > > + struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T tuner_params = {}; > > > + > > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > > + > > > + switch (p->modulation) { > > > + case VSB_8: > > > + demod_type = MXL_EAGLE_DEMOD_TYPE_ATSC; > > > + break; > > > + case QAM_AUTO: > > > + case QAM_64: > > > + case QAM_128: > > > + case QAM_256: > > > + demod_type = MXL_EAGLE_DEMOD_TYPE_QAM; > > > + break; > > > + default: > > > + return -EINVAL; > > > + } > > > + > > > + if (dev->current_frequency == p->frequency && dev->demod_type == demod_type) > > > + return 0; > > > + > > > + dev->current_frequency = -1; > > > + dev->demod_type = -1; > > > + > > > + status = mxl692_i2c_writeread(dev, > > > + MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET, > > > + (u8 *)&demod_type, > > > + sizeof(u8), > > > + NULL, > > > + 0); > > > + if (status) { > > > + dev_dbg(&dev->i2c_client->dev, "DEVICE_DEMODULATOR_TYPE_SET...FAIL %d\n", status); > > > + goto err; > > > + } > > > + > > > + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ > > > + > > > + //Config Device Power Mode > > > + if (dev->power_mode != MXL_EAGLE_POWER_MODE_ACTIVE) { > > > + status = mxl692_powermode(dev, power_mode); > > > + if (status) > > > + goto err; > > > + > > > + usleep_range(50 * 1000, 60 * 1000); /* was 500! */ > > > + } > > > + > > > + mpeg_params.mpeg_parallel = 0; > > > + mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_MSB_1ST; > > > + mpeg_params.mpeg_sync_pulse_width = MXL_EAGLE_DATA_SYNC_WIDTH_BIT; > > > + mpeg_params.mpeg_valid_pol = MXL_EAGLE_CLOCK_POSITIVE; > > > + mpeg_params.mpeg_sync_pol = MXL_EAGLE_CLOCK_POSITIVE; > > > + mpeg_params.mpeg_clk_pol = MXL_EAGLE_CLOCK_NEGATIVE; > > > + mpeg_params.mpeg3wire_mode_enable = 0; > > > + mpeg_params.mpeg_clk_freq = MXL_EAGLE_MPEG_CLOCK_27MHZ; > > > + > > > + switch (demod_type) { > > > + case MXL_EAGLE_DEMOD_TYPE_ATSC: > > > + status = mxl692_i2c_writeread(dev, > > > + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, > > > + (u8 *)&mpeg_params, > > > + sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), > > > + NULL, > > > + 0); > > > + if (status) > > > + goto err; > > > + break; > > > + case MXL_EAGLE_DEMOD_TYPE_QAM: > > > + if (qam_annex == MXL_EAGLE_QAM_DEMOD_ANNEX_A) > > > + mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_LSB_1ST; > > > + status = mxl692_i2c_writeread(dev, > > > + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, > > > + (u8 *)&mpeg_params, > > > + sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), > > > + NULL, > > > + 0); > > > + if (status) > > > + goto err; > > > + > > > + qam_params.annex_type = qam_annex; > > > + qam_params.qam_type = MXL_EAGLE_QAM_DEMOD_AUTO; > > > + qam_params.iq_flip = MXL_EAGLE_DEMOD_IQ_AUTO; > > > + if (p->modulation == QAM_64) > > > + qam_params.symbol_rate_hz = 5057000; > > > + else > > > + qam_params.symbol_rate_hz = 5361000; > > > + > > > + qam_params.symbol_rate_256qam_hz = 5361000; > > > + > > > + status = mxl692_i2c_writeread(dev, > > > + MXL_EAGLE_OPCODE_QAM_PARAMS_SET, > > > + (u8 *)&qam_params, > > > + sizeof(struct MXL_EAGLE_QAM_DEMOD_PARAMS_T), > > > + NULL, 0); > > > + if (status) > > > + goto err; > > > + > > > + break; > > > + default: > > > + break; > > > + } > > > + > > > + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ > > > + > > > + tuner_params.freq_hz = p->frequency; > > > + tuner_params.bandwidth = MXL_EAGLE_TUNER_BW_6MHZ; > > > + tuner_params.tune_mode = MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW; > > > + > > > + dev_dbg(&dev->i2c_client->dev, " Tuning Freq: %d %s\n", tuner_params.freq_hz, > > > + demod_type == MXL_EAGLE_DEMOD_TYPE_ATSC ? "ATSC" : "QAM"); > > > + > > > + status = mxl692_i2c_writeread(dev, > > > + MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET, > > > + (u8 *)&tuner_params, > > > + sizeof(struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T), > > > + NULL, > > > + 0); > > > + if (status) > > > + goto err; > > > + > > > + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ > > > + > > > + switch (demod_type) { > > > + case MXL_EAGLE_DEMOD_TYPE_ATSC: > > > + status = mxl692_i2c_writeread(dev, > > > + MXL_EAGLE_OPCODE_ATSC_INIT_SET, > > > + NULL, 0, NULL, 0); > > > + if (status) > > > + goto err; > > > + break; > > > + case MXL_EAGLE_DEMOD_TYPE_QAM: > > > + status = mxl692_i2c_writeread(dev, > > > + MXL_EAGLE_OPCODE_QAM_RESTART_SET, > > > + NULL, 0, NULL, 0); > > > + if (status) > > > + goto err; > > > + break; > > > + default: > > > + break; > > > + } > > > + > > > + dev->demod_type = demod_type; > > > + dev->current_frequency = p->frequency; > > > +err: > > > + return 0; > > > +} > > > + > > > +static int mxl692_get_frontend(struct dvb_frontend *fe, > > > + struct dtv_frontend_properties *p) > > > +{ > > > + struct dtv_frontend_properties *c = &fe->dtv_property_cache; > > > + > > > + p->modulation = c->modulation; > > > + p->frequency = c->frequency; > > > + > > > + return 0; > > > +} > > > + > > > +static int mxl692_read_snr(struct dvb_frontend *fe, u16 *snr) > > > +{ > > > + struct mxl692_dev *dev = fe->demodulator_priv; > > > + struct dtv_frontend_properties *c = &fe->dtv_property_cache; > > > + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > > > + struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; > > > + struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; > > > + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; > > > + int mxl_status = 0; > > > + > > > + *snr = 0; > > > + > > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > > + > > > + atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; > > > + qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; > > > > It might be nicer to use a union rather casting, for all of these (including > > below). > > > Union the two status structs? They're not even the same size, why > would I do that? > > > > > > > > > > > + > > > + switch (demod_type) { > > > + case MXL_EAGLE_DEMOD_TYPE_ATSC: > > > + mxl_status = mxl692_i2c_writeread(dev, > > > + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, > > > + NULL, > > > + 0, > > > + rx_buf, > > > + sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); > > > + if (!mxl_status) { > > > + *snr = (u16)(atsc_status->snr_db_tenths / 10); > > > + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; > > > + c->cnr.stat[0].svalue = *snr; > > > + } > > > + break; > > > + case MXL_EAGLE_DEMOD_TYPE_QAM: > > > + mxl_status = mxl692_i2c_writeread(dev, > > > + MXL_EAGLE_OPCODE_QAM_STATUS_GET, > > > + NULL, > > > + 0, > > > + rx_buf, > > > + sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); > > > + if (!mxl_status) > > > + *snr = (u16)(qam_status->snr_db_tenths / 10); > > > + break; > > > + case MXL_EAGLE_DEMOD_TYPE_OOB: > > > + default: > > > + break; > > > + } > > > + return 0; > > > +} > > > + > > > +static int mxl692_read_ber_ucb(struct dvb_frontend *fe) > > > +{ > > > + struct mxl692_dev *dev = fe->demodulator_priv; > > > + struct dtv_frontend_properties *c = &fe->dtv_property_cache; > > > + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > > > + struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T *qam_errors; > > > + struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *atsc_errors; > > > + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; > > > + int mxl_status = 0; > > > + u32 utmp; > > > + > > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > > + > > > + qam_errors = (struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T *)&rx_buf; > > > + atsc_errors = (struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *)&rx_buf; > > > + > > > + switch (demod_type) { > > > + case MXL_EAGLE_DEMOD_TYPE_ATSC: > > > + mxl_status = mxl692_i2c_writeread(dev, > > > + MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, > > > + NULL, > > > + 0, > > > + rx_buf, > > > + sizeof(struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T)); > > > + if (!mxl_status) { > > > + if (atsc_errors->error_packets == 0) > > > + utmp = 0; > > > + else > > > + utmp = ((atsc_errors->error_bytes / atsc_errors->error_packets) * > > > + atsc_errors->total_packets); > > > + /* ber */ > > > + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; > > > + c->post_bit_error.stat[0].uvalue += atsc_errors->error_bytes; > > > + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; > > > + c->post_bit_count.stat[0].uvalue += utmp; > > > + /* ucb */ > > > + c->block_error.stat[0].scale = FE_SCALE_COUNTER; > > > + c->block_error.stat[0].uvalue += atsc_errors->error_packets; > > > + > > > + dev_dbg(&dev->i2c_client->dev, "%llu %llu\n", > > > + c->post_bit_count.stat[0].uvalue, c->block_error.stat[0].uvalue); > > > + } > > > + break; > > > + case MXL_EAGLE_DEMOD_TYPE_QAM: > > > + case MXL_EAGLE_DEMOD_TYPE_OOB: > > > + default: > > > + break; > > > + } > > > + return 0; > > > +} > > > + > > > +static int mxl692_read_status(struct dvb_frontend *fe, > > > + enum fe_status *status) > > > +{ > > > + struct mxl692_dev *dev = fe->demodulator_priv; > > > + struct dtv_frontend_properties *c = &fe->dtv_property_cache; > > > + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; > > > + struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; > > > + struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; > > > + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; > > > + int mxl_status = 0; > > > + *status = 0; > > > + > > > + dev_dbg(&dev->i2c_client->dev, "\n"); > > > + > > > + atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; > > > + qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; > > > + > > > + switch (demod_type) { > > > + case MXL_EAGLE_DEMOD_TYPE_ATSC: > > > + mxl_status = mxl692_i2c_writeread(dev, > > > + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, > > > + NULL, > > > + 0, > > > + rx_buf, > > > + sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); > > > + if (!mxl_status && atsc_status->atsc_lock) { > > > + *status |= FE_HAS_SIGNAL; > > > + *status |= FE_HAS_CARRIER; > > > + *status |= FE_HAS_VITERBI; > > > + *status |= FE_HAS_SYNC; > > > + *status |= FE_HAS_LOCK; > > > + > > > + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; > > > + c->cnr.stat[0].svalue = atsc_status->snr_db_tenths / 10; > > > + } > > > + break; > > > + case MXL_EAGLE_DEMOD_TYPE_QAM: > > > + mxl_status = mxl692_i2c_writeread(dev, > > > + MXL_EAGLE_OPCODE_QAM_STATUS_GET, > > > + NULL, > > > + 0, > > > + rx_buf, > > > + sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); > > > + if (!mxl_status && qam_status->qam_locked) { > > > + *status |= FE_HAS_SIGNAL; > > > + *status |= FE_HAS_CARRIER; > > > + *status |= FE_HAS_VITERBI; > > > + *status |= FE_HAS_SYNC; > > > + *status |= FE_HAS_LOCK; > > > + > > > + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; > > > + c->cnr.stat[0].svalue = qam_status->snr_db_tenths / 10; > > > + } > > > + break; > > > + case MXL_EAGLE_DEMOD_TYPE_OOB: > > > + default: > > > + break; > > > + } > > > + > > > + if ((*status & FE_HAS_LOCK) == 0) { > > > + /* No lock, reset all statistics */ > > > + c->cnr.len = 1; > > > + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > > + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > > + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > > + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; > > > + return 0; > > > + } > > > + > > > + if (mxl_status) > > > + dev_err(&dev->i2c_client->dev, "err %d\n", mxl_status); > > > + else > > > + mxl_status = mxl692_read_ber_ucb(fe); > > > + > > > + return mxl_status; > > > +} > > > + > > > +static const struct dvb_frontend_ops mxl692_ops = { > > > + .delsys = { SYS_ATSC }, > > > + .info = { > > > + .name = "MaxLinear MxL692 VSB tuner-demodulator", > > > + .frequency_min_hz = 54000000, > > > + .frequency_max_hz = 858000000, > > > + .frequency_stepsize_hz = 62500, > > > + .caps = FE_CAN_8VSB > > > + }, > > > + > > > + .init = mxl692_init, > > > + .sleep = mxl692_sleep, > > > + .set_frontend = mxl692_set_frontend, > > > + .get_frontend = mxl692_get_frontend, > > > + > > > + .read_status = mxl692_read_status, > > > + .read_snr = mxl692_read_snr, > > > +}; > > > + > > > +static int mxl692_probe(struct i2c_client *client, > > > + const struct i2c_device_id *id) > > > +{ > > > + struct mxl692_config *config = client->dev.platform_data; > > > + struct mxl692_dev *dev; > > > + int ret = 0; > > > + > > > + dev = kzalloc(sizeof(*dev), GFP_KERNEL); > > > + if (!dev) { > > > + ret = -ENOMEM; > > > + dev_err(&client->dev, "kzalloc() failed\n"); > > > + goto err; > > > + } > > > + > > > + memcpy(&dev->fe.ops, &mxl692_ops, sizeof(struct dvb_frontend_ops)); > > > + dev->fe.demodulator_priv = dev; > > > + dev->i2c_client = client; > > > + *config->fe = &dev->fe; > > > + mutex_init(&dev->i2c_lock); > > > + i2c_set_clientdata(client, dev); > > > + > > > + dev_info(&client->dev, "MaxLinear mxl692 successfully attached\n"); > > > + detect_endianness(); > > > + > > > + return 0; > > > +err: > > > + dev_err(&client->dev, "failed %d\n", ret); > > > + return -ENODEV; > > > +} > > > + > > > +static int mxl692_remove(struct i2c_client *client) > > > +{ > > > + struct mxl692_dev *dev = i2c_get_clientdata(client); > > > + > > > + dev->fe.demodulator_priv = NULL; > > > + i2c_set_clientdata(client, NULL); > > > + kfree(dev); > > > + > > > + return 0; > > > +} > > > + > > > +static const struct i2c_device_id mxl692_id_table[] = { > > > + {"mxl692", 0}, > > > + {} > > > +}; > > > +MODULE_DEVICE_TABLE(i2c, mxl692_id_table); > > > + > > > +static struct i2c_driver mxl692_driver = { > > > + .driver = { > > > + .owner = THIS_MODULE, > > > + .name = "mxl692", > > > + }, > > > + .probe = mxl692_probe, > > > + .remove = mxl692_remove, > > > + .id_table = mxl692_id_table, > > > +}; > > > + > > > +module_i2c_driver(mxl692_driver); > > > + > > > +MODULE_AUTHOR("Brad Love <brad@nextdimension.cc>"); > > > +MODULE_DESCRIPTION("MaxLinear MxL692 demodulator/tuner driver"); > > > +MODULE_FIRMWARE(MXL692_FIRMWARE); > > > +MODULE_LICENSE("GPL"); > > > diff --git a/drivers/media/dvb-frontends/mxl692.h b/drivers/media/dvb-frontends/mxl692.h > > > new file mode 100644 > > > index 000000000000..45bc48f1f12f > > > --- /dev/null > > > +++ b/drivers/media/dvb-frontends/mxl692.h > > > @@ -0,0 +1,38 @@ > > > +/* SPDX-License-Identifier: GPL-2.0 */ > > > +/* > > > + * Driver for the MaxLinear MxL69x family of tuners/demods > > > + * > > > + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> > > > + * > > > + * based on code: > > > + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved > > > + * which was released under GPL V2 > > > + * > > > + * This program is free software; you can redistribute it and/or > > > + * modify it under the terms of the GNU General Public License > > > + * version 2, as published by the Free Software Foundation. > > > + * > > > + * This program is distributed in the hope that it will be useful, > > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > > + * GNU General Public License for more details. > > > + */ > > > + > > > +#ifndef _MXL692_H_ > > > +#define _MXL692_H_ > > > + > > > +#include <media/dvb_frontend.h> > > > + > > > +#define MXL692_FIRMWARE "dvb-demod-mxl692.fw" > > > + > > > +struct mxl692_config { > > > + unsigned char id; > > > + u8 i2c_addr; > > > + /* > > > + * frontend > > > + * returned by driver > > > + */ > > > + struct dvb_frontend **fe; > > > +}; > > > + > > > +#endif /* _MXL692_H_ */ > > > diff --git a/drivers/media/dvb-frontends/mxl692_defs.h b/drivers/media/dvb-frontends/mxl692_defs.h > > > new file mode 100644 > > > index 000000000000..97fd18ae81ff > > > --- /dev/null > > > +++ b/drivers/media/dvb-frontends/mxl692_defs.h > > > @@ -0,0 +1,493 @@ > > > +/* SPDX-License-Identifier: GPL-2.0 */ > > > +/* > > > + * Driver for the MaxLinear MxL69x family of combo tuners/demods > > > + * > > > + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> > > > + * > > > + * based on code: > > > + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved > > > + * which was released under GPL V2 > > > + * > > > + * This program is free software; you can redistribute it and/or > > > + * modify it under the terms of the GNU General Public License > > > + * version 2, as published by the Free Software Foundation. > > > + * > > > + * This program is distributed in the hope that it will be useful, > > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > > + * GNU General Public License for more details. > > > + */ > > > + > > > +//#define __packed __attribute__((packed)) > > > > Comment not needed. > > > > > + > > > +/***************************************************************************** > > > + * Defines > > > + ***************************************************************************** > > > + */ > > > +#define MXL_EAGLE_HOST_MSG_HEADER_SIZE 8 > > > +#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76 > > > +#define MXL_EAGLE_QAM_FFE_TAPS_LENGTH 16 > > > +#define MXL_EAGLE_QAM_SPUR_TAPS_LENGTH 32 > > > +#define MXL_EAGLE_QAM_DFE_TAPS_LENGTH 72 > > > +#define MXL_EAGLE_ATSC_FFE_TAPS_LENGTH 4096 > > > +#define MXL_EAGLE_ATSC_DFE_TAPS_LENGTH 384 > > > +#define MXL_EAGLE_VERSION_SIZE 5 /* A.B.C.D-RCx */ > > > +#define MXL_EAGLE_FW_LOAD_TIME 50 > > > + > > > +#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76 > > > +#define MXL_EAGLE_FW_HEADER_SIZE 16 > > > +#define MXL_EAGLE_FW_SEGMENT_HEADER_SIZE 8 > > > +#define MXL_EAGLE_MAX_I2C_PACKET_SIZE 58 > > > +#define MXL_EAGLE_I2C_MHEADER_SIZE 6 > > > +#define MXL_EAGLE_I2C_PHEADER_SIZE 2 > > > + > > > +/* Enum of Eagle family devices */ > > > +enum MXL_EAGLE_DEVICE_E { > > > + MXL_EAGLE_DEVICE_691 = 1, /* Device Mxl691 */ > > > + MXL_EAGLE_DEVICE_248 = 2, /* Device Mxl248 */ > > > + MXL_EAGLE_DEVICE_692 = 3, /* Device Mxl692 */ > > > + MXL_EAGLE_DEVICE_MAX, /* No such device */ > > > +}; > > > + > > > +#define VER_A 1 > > > +#define VER_B 1 > > > +#define VER_C 1 > > > +#define VER_D 3 > > > +#define VER_E 6 > > > + > > > +/* Enum of Host to Eagle I2C protocol opcodes */ > > > +enum MXL_EAGLE_OPCODE_E { > > > + /* DEVICE */ > > > + MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET, > > > + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, > > > + MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET, > > > + MXL_EAGLE_OPCODE_DEVICE_GPIO_DIRECTION_SET, > > > + MXL_EAGLE_OPCODE_DEVICE_GPO_LEVEL_SET, > > > + MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET, > > > + MXL_EAGLE_OPCODE_DEVICE_IO_MUX_SET, > > > + MXL_EAGLE_OPCODE_DEVICE_VERSION_GET, > > > + MXL_EAGLE_OPCODE_DEVICE_STATUS_GET, > > > + MXL_EAGLE_OPCODE_DEVICE_GPI_LEVEL_GET, > > > + > > > + /* TUNER */ > > > + MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET, > > > + MXL_EAGLE_OPCODE_TUNER_LOCK_STATUS_GET, > > > + MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET, > > > + > > > + /* ATSC */ > > > + MXL_EAGLE_OPCODE_ATSC_INIT_SET, > > > + MXL_EAGLE_OPCODE_ATSC_ACQUIRE_CARRIER_SET, > > > + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, > > > + MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, > > > + MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_DFE_TAPS_GET, > > > + MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET, > > > + > > > + /* QAM */ > > > + MXL_EAGLE_OPCODE_QAM_PARAMS_SET, > > > + MXL_EAGLE_OPCODE_QAM_RESTART_SET, > > > + MXL_EAGLE_OPCODE_QAM_STATUS_GET, > > > + MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET, > > > + MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET, > > > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET, > > > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET, > > > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET, > > > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_TAPS_NUMBER_GET, > > > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET, > > > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET, > > > + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET, > > > + > > > + /* OOB */ > > > + MXL_EAGLE_OPCODE_OOB_PARAMS_SET, > > > + MXL_EAGLE_OPCODE_OOB_RESTART_SET, > > > + MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET, > > > + MXL_EAGLE_OPCODE_OOB_STATUS_GET, > > > + > > > + /* SMA */ > > > + MXL_EAGLE_OPCODE_SMA_INIT_SET, > > > + MXL_EAGLE_OPCODE_SMA_PARAMS_SET, > > > + MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET, > > > + MXL_EAGLE_OPCODE_SMA_RECEIVE_GET, > > > + > > > + /* DEBUG */ > > > + MXL_EAGLE_OPCODE_INTERNAL, > > > + > > > + MXL_EAGLE_OPCODE_MAX = 70, > > > +}; > > > + > > > +/* Enum of Callabck function types */ > > > +enum MXL_EAGLE_CB_TYPE_E { > > > + MXL_EAGLE_CB_FW_DOWNLOAD = 0, > > > +}; > > > + > > > +/* Enum of power supply types */ > > > +enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E { > > > + MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE, /* Single supply of 3.3V */ > > > + MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL, /* Dual supply, 1.8V & 3.3V */ > > > +}; > > > + > > > +/* Enum of I/O pad drive modes */ > > > +enum MXL_EAGLE_IO_MUX_DRIVE_MODE_E { > > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_1X, > > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_2X, > > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_3X, > > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_4X, > > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_5X, > > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_6X, > > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_7X, > > > + MXL_EAGLE_IO_MUX_DRIVE_MODE_8X, > > > +}; > > > + > > > +/* Enum of demodulator types. Used for selection of demodulator > > > + * type in relevant devices, e.g. ATSC vs. QAM in Mxl691 > > > + */ > > > +enum MXL_EAGLE_DEMOD_TYPE_E { > > > + MXL_EAGLE_DEMOD_TYPE_QAM, /* Mxl248 or Mxl692 */ > > > + MXL_EAGLE_DEMOD_TYPE_OOB, /* Mxl248 only */ > > > + MXL_EAGLE_DEMOD_TYPE_ATSC /* Mxl691 or Mxl692 */ > > > +}; > > > + > > > +/* Enum of power modes. Used for initial > > > + * activation, or for activating sleep mode > > > + */ > > > +enum MXL_EAGLE_POWER_MODE_E { > > > + MXL_EAGLE_POWER_MODE_SLEEP, > > > + MXL_EAGLE_POWER_MODE_ACTIVE > > > +}; > > > + > > > +/* Enum of GPIOs, used in device GPIO APIs */ > > > +enum MXL_EAGLE_GPIO_NUMBER_E { > > > + MXL_EAGLE_GPIO_NUMBER_0, > > > + MXL_EAGLE_GPIO_NUMBER_1, > > > + MXL_EAGLE_GPIO_NUMBER_2, > > > + MXL_EAGLE_GPIO_NUMBER_3, > > > + MXL_EAGLE_GPIO_NUMBER_4, > > > + MXL_EAGLE_GPIO_NUMBER_5, > > > + MXL_EAGLE_GPIO_NUMBER_6 > > > +}; > > > + > > > +/* Enum of GPIO directions, used in GPIO direction configuration API */ > > > +enum MXL_EAGLE_GPIO_DIRECTION_E { > > > + MXL_EAGLE_GPIO_DIRECTION_INPUT, > > > + MXL_EAGLE_GPIO_DIRECTION_OUTPUT > > > +}; > > > + > > > +/* Enum of GPIO level, used in device GPIO APIs */ > > > +enum MXL_EAGLE_GPIO_LEVEL_E { > > > + MXL_EAGLE_GPIO_LEVEL_LOW, > > > + MXL_EAGLE_GPIO_LEVEL_HIGH, > > > +}; > > > + > > > +/* Enum of I/O Mux function, used in device I/O mux configuration API */ > > > +enum MXL_EAGLE_IOMUX_FUNCTION_E { > > > + MXL_EAGLE_IOMUX_FUNC_FEC_LOCK, > > > + MXL_EAGLE_IOMUX_FUNC_MERR, > > > +}; > > > + > > > +/* Enum of MPEG Data format, used in MPEG and OOB output configuration */ > > > +enum MXL_EAGLE_MPEG_DATA_FORMAT_E { > > > + MXL_EAGLE_DATA_SERIAL_LSB_1ST = 0, > > > + MXL_EAGLE_DATA_SERIAL_MSB_1ST, > > > + > > > + MXL_EAGLE_DATA_SYNC_WIDTH_BIT = 0, > > > + MXL_EAGLE_DATA_SYNC_WIDTH_BYTE > > > +}; > > > + > > > +/* Enum of MPEG Clock format, used in MPEG and OOB output configuration */ > > > +enum MXL_EAGLE_MPEG_CLOCK_FORMAT_E { > > > + MXL_EAGLE_CLOCK_ACTIVE_HIGH = 0, > > > + MXL_EAGLE_CLOCK_ACTIVE_LOW, > > > + > > > + MXL_EAGLE_CLOCK_POSITIVE = 0, > > > + MXL_EAGLE_CLOCK_NEGATIVE, > > > + > > > + MXL_EAGLE_CLOCK_IN_PHASE = 0, > > > + MXL_EAGLE_CLOCK_INVERTED, > > > +}; > > > + > > > +/* Enum of MPEG Clock speeds, used in MPEG output configuration */ > > > +enum MXL_EAGLE_MPEG_CLOCK_RATE_E { > > > + MXL_EAGLE_MPEG_CLOCK_54MHZ, > > > + MXL_EAGLE_MPEG_CLOCK_40_5MHZ, > > > + MXL_EAGLE_MPEG_CLOCK_27MHZ, > > > + MXL_EAGLE_MPEG_CLOCK_13_5MHZ, > > > +}; > > > + > > > +/* Enum of Interrupt mask bit, used in host interrupt configuration */ > > > +enum MXL_EAGLE_INTR_MASK_BITS_E { > > > + MXL_EAGLE_INTR_MASK_DEMOD = 0, > > > + MXL_EAGLE_INTR_MASK_SMA_RX = 1, > > > + MXL_EAGLE_INTR_MASK_WDOG = 31 > > > +}; > > > + > > > +/* Enum of QAM Demodulator type, used in QAM configuration */ > > > +enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E { > > > + MXL_EAGLE_QAM_DEMOD_ANNEX_B, /* J.83B */ > > > + MXL_EAGLE_QAM_DEMOD_ANNEX_A, /* DVB-C */ > > > +}; > > > + > > > +/* Enum of QAM Demodulator modulation, used in QAM configuration and status */ > > > +enum MXL_EAGLE_QAM_DEMOD_QAM_TYPE_E { > > > + MXL_EAGLE_QAM_DEMOD_QAM16, > > > + MXL_EAGLE_QAM_DEMOD_QAM64, > > > + MXL_EAGLE_QAM_DEMOD_QAM256, > > > + MXL_EAGLE_QAM_DEMOD_QAM1024, > > > + MXL_EAGLE_QAM_DEMOD_QAM32, > > > + MXL_EAGLE_QAM_DEMOD_QAM128, > > > + MXL_EAGLE_QAM_DEMOD_QPSK, > > > + MXL_EAGLE_QAM_DEMOD_AUTO, > > > +}; > > > + > > > +/* Enum of Demodulator IQ setup, used in QAM, OOB configuration and status */ > > > +enum MXL_EAGLE_IQ_FLIP_E { > > > + MXL_EAGLE_DEMOD_IQ_NORMAL, > > > + MXL_EAGLE_DEMOD_IQ_FLIPPED, > > > + MXL_EAGLE_DEMOD_IQ_AUTO, > > > +}; > > > + > > > +/* Enum of OOB Demodulator symbol rates, used in OOB configuration */ > > > +enum MXL_EAGLE_OOB_DEMOD_SYMB_RATE_E { > > > + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_0_772MHZ, /* ANSI/SCTE 55-2 0.772 MHz */ > > > + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_024MHZ, /* ANSI/SCTE 55-1 1.024 MHz */ > > > + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_544MHZ, /* ANSI/SCTE 55-2 1.544 MHz */ > > > +}; > > > + > > > +/* Enum of tuner channel tuning mode */ > > > +enum MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_E { > > > + MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW, /* Normal "view" mode */ > > > + MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_SCAN, /* Fast "scan" mode */ > > > +}; > > > + > > > +/* Enum of tuner bandwidth */ > > > +enum MXL_EAGLE_TUNER_BW_E { > > > + MXL_EAGLE_TUNER_BW_6MHZ, > > > + MXL_EAGLE_TUNER_BW_7MHZ, > > > + MXL_EAGLE_TUNER_BW_8MHZ, > > > +}; > > > + > > > +/* Enum of tuner bandwidth */ > > > +enum MXL_EAGLE_JUNCTION_TEMPERATURE_E { > > > + MXL_EAGLE_JUNCTION_TEMPERATURE_BELOW_0_CELSIUS = 0, > > > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_0_TO_14_CELSIUS = 1, > > > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_14_TO_28_CELSIUS = 3, > > > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_28_TO_42_CELSIUS = 2, > > > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_42_TO_57_CELSIUS = 6, > > > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_57_TO_71_CELSIUS = 7, > > > + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_71_TO_85_CELSIUS = 5, > > > + MXL_EAGLE_JUNCTION_TEMPERATURE_ABOVE_85_CELSIUS = 4, > > > +}; > > > + > > > +/* Struct passed in optional callback used during FW download */ > > > +struct MXL_EAGLE_FW_DOWNLOAD_CB_PAYLOAD_T { > > > + u32 total_len; > > > + u32 downloaded_len; > > > +}; > > > + > > > +/* Struct used of I2C protocol between host and Eagle, internal use only */ > > > +struct __packed MXL_EAGLE_HOST_MSG_HEADER_T { > > > + u8 opcode; > > > + u8 seqnum; > > > + u8 payload_size; > > > + u8 status; > > > + u32 checksum; > > > +}; > > > + > > > +/* Device version information struct */ > > > +struct __packed MXL_EAGLE_DEV_VER_T { > > > + u8 chip_id; > > > + u8 firmware_ver[MXL_EAGLE_VERSION_SIZE]; > > > + u8 mxlware_ver[MXL_EAGLE_VERSION_SIZE]; > > > +}; > > > + > > > +/* Xtal configuration struct */ > > > +struct __packed MXL_EAGLE_DEV_XTAL_T { > > > + u8 xtal_cap; /* accepted range is 1..31 pF. Default is 26 */ > > > + u8 clk_out_enable; > > > + u8 clk_out_div_enable; /* clock out freq is xtal freq / 6 */ > > > + u8 xtal_sharing_enable; /* if enabled set xtal_cap to 25 pF */ > > > + u8 xtal_calibration_enable; /* enable for master, disable for slave */ > > > +}; > > > + > > > +/* GPIO direction struct, internally used in GPIO configuration API */ > > > +struct __packed MXL_EAGLE_DEV_GPIO_DIRECTION_T { > > > + u8 gpio_number; > > > + u8 gpio_direction; > > > +}; > > > + > > > +/* GPO level struct, internally used in GPIO configuration API */ > > > +struct __packed MXL_EAGLE_DEV_GPO_LEVEL_T { > > > + u8 gpio_number; > > > + u8 gpo_level; > > > +}; > > > + > > > +/* Device Status struct */ > > > +struct MXL_EAGLE_DEV_STATUS_T { > > > + u8 temperature; > > > + u8 demod_type; > > > + u8 power_mode; > > > + u8 cpu_utilization_percent; > > > +}; > > > + > > > +/* Device interrupt configuration struct */ > > > +struct __packed MXL_EAGLE_DEV_INTR_CFG_T { > > > + u32 intr_mask; > > > + u8 edge_trigger; > > > + u8 positive_trigger; > > > + u8 global_enable_interrupt; > > > +}; > > > + > > > +/* MPEG pad drive parameters, used on MPEG output configuration */ > > > +/* See MXL_EAGLE_IO_MUX_DRIVE_MODE_E */ > > > +struct MXL_EAGLE_MPEG_PAD_DRIVE_T { > > > + u8 pad_drv_mpeg_syn; > > > + u8 pad_drv_mpeg_dat; > > > + u8 pad_drv_mpeg_val; > > > + u8 pad_drv_mpeg_clk; > > > +}; > > > + > > > +/* MPEGOUT parameter struct, used in MPEG output configuration */ > > > +struct MXL_EAGLE_MPEGOUT_PARAMS_T { > > > + u8 mpeg_parallel; > > > + u8 msb_first; > > > + u8 mpeg_sync_pulse_width; /* See MXL_EAGLE_MPEG_DATA_FORMAT_E */ > > > + u8 mpeg_valid_pol; > > > + u8 mpeg_sync_pol; > > > + u8 mpeg_clk_pol; > > > + u8 mpeg3wire_mode_enable; > > > + u8 mpeg_clk_freq; > > > + struct MXL_EAGLE_MPEG_PAD_DRIVE_T mpeg_pad_drv; > > > +}; > > > + > > > +/* QAM Demodulator parameters struct, used in QAM params configuration */ > > > +struct __packed MXL_EAGLE_QAM_DEMOD_PARAMS_T { > > > + u8 annex_type; > > > + u8 qam_type; > > > + u8 iq_flip; > > > + u8 search_range_idx; > > > + u8 spur_canceller_enable; > > > + u32 symbol_rate_hz; > > > + u32 symbol_rate_256qam_hz; > > > +}; > > > + > > > +/* QAM Demodulator status */ > > > +struct MXL_EAGLE_QAM_DEMOD_STATUS_T { > > > + u8 annex_type; > > > + u8 qam_type; > > > + u8 iq_flip; > > > + u8 interleaver_depth_i; > > > + u8 interleaver_depth_j; > > > + u8 qam_locked; > > > + u8 fec_locked; > > > + u8 mpeg_locked; > > > + u16 snr_db_tenths; > > > + s16 timing_offset; > > > + s32 carrier_offset_hz; > > > +}; > > > + > > > +/* QAM Demodulator error counters */ > > > +struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T { > > > + u32 corrected_code_words; > > > + u32 uncorrected_code_words; > > > + u32 total_code_words_received; > > > + u32 corrected_bits; > > > + u32 error_mpeg_frames; > > > + u32 mpeg_frames_received; > > > + u32 erasures; > > > +}; > > > + > > > +/* QAM Demodulator constellation point */ > > > +struct MXL_EAGLE_QAM_DEMOD_CONSTELLATION_VAL_T { > > > + s16 i_value[12]; > > > + s16 q_value[12]; > > > +}; > > > + > > > +/* QAM Demodulator equalizer filter taps */ > > > +struct MXL_EAGLE_QAM_DEMOD_EQU_FILTER_T { > > > + s16 ffe_taps[MXL_EAGLE_QAM_FFE_TAPS_LENGTH]; > > > + s16 spur_taps[MXL_EAGLE_QAM_SPUR_TAPS_LENGTH]; > > > + s16 dfe_taps[MXL_EAGLE_QAM_DFE_TAPS_LENGTH]; > > > + u8 ffe_leading_tap_index; > > > + u8 dfe_taps_number; > > > +}; > > > + > > > +/* OOB Demodulator parameters struct, used in OOB params configuration */ > > > +struct __packed MXL_EAGLE_OOB_DEMOD_PARAMS_T { > > > + u8 symbol_rate; > > > + u8 iq_flip; > > > + u8 clk_pol; > > > +}; > > > + > > > +/* OOB Demodulator error counters */ > > > +struct MXL_EAGLE_OOB_DEMOD_ERROR_COUNTERS_T { > > > + u32 corrected_packets; > > > + u32 uncorrected_packets; > > > + u32 total_packets_received; > > > +}; > > > + > > > +/* OOB status */ > > > +struct __packed MXL_EAGLE_OOB_DEMOD_STATUS_T { > > > + u16 snr_db_tenths; > > > + s16 timing_offset; > > > + s32 carrier_offsetHz; > > > + u8 qam_locked; > > > + u8 fec_locked; > > > + u8 mpeg_locked; > > > + u8 retune_required; > > > + u8 iq_flip; > > > +}; > > > + > > > +/* ATSC Demodulator status */ > > > +struct __packed MXL_EAGLE_ATSC_DEMOD_STATUS_T { > > > + s16 snr_db_tenths; > > > + s16 timing_offset; > > > + s32 carrier_offset_hz; > > > + u8 frame_lock; > > > + u8 atsc_lock; > > > + u8 fec_lock; > > > +}; > > > + > > > +/* ATSC Demodulator error counters */ > > > +struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T { > > > + u32 error_packets; > > > + u32 total_packets; > > > + u32 error_bytes; > > > +}; > > > + > > > +/* ATSC Demodulator equalizers filter taps */ > > > +struct __packed MXL_EAGLE_ATSC_DEMOD_EQU_FILTER_T { > > > + s16 ffe_taps[MXL_EAGLE_ATSC_FFE_TAPS_LENGTH]; > > > + s8 dfe_taps[MXL_EAGLE_ATSC_DFE_TAPS_LENGTH]; > > > +}; > > > + > > > +/* Tuner AGC Status */ > > > +struct __packed MXL_EAGLE_TUNER_AGC_STATUS_T { > > > + u8 locked; > > > + u16 raw_agc_gain; /* AGC gain [dB] = rawAgcGain / 2^6 */ > > > + s16 rx_power_db_hundredths; > > > +}; > > > + > > > +/* Tuner channel tune parameters */ > > > +struct __packed MXL_EAGLE_TUNER_CHANNEL_PARAMS_T { > > > + u32 freq_hz; > > > + u8 tune_mode; > > > + u8 bandwidth; > > > +}; > > > + > > > +/* Tuner channel lock indications */ > > > +struct __packed MXL_EAGLE_TUNER_LOCK_STATUS_T { > > > + u8 rf_pll_locked; > > > + u8 ref_pll_locked; > > > +}; > > > + > > > +/* Smart antenna parameters used in Smart antenna params configuration */ > > > +struct __packed MXL_EAGLE_SMA_PARAMS_T { > > > + u8 full_duplex_enable; > > > + u8 rx_disable; > > > + u8 idle_logic_high; > > > +}; > > > + > > > +/* Smart antenna message format */ > > > +struct __packed MXL_EAGLE_SMA_MESSAGE_T { > > > + u32 payload_bits; > > > + u8 total_num_bits; > > > +}; > > > + > > > -- > > > 2.23.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 2/3] em28xx-core: Fix TS2 active led 2020-06-12 18:39 [PATCH 0/3] MaxLinear mxl692 demod-tuner / Hauppauge usb QuadHD Brad Love 2020-06-12 18:39 ` [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver Brad Love @ 2020-06-12 18:39 ` Brad Love 2020-06-12 18:39 ` [PATCH 3/3] em28xx: Add support for Hauppauge USB QuadHD Brad Love 2021-01-26 1:54 ` [PATCH 0/4] MaxLinear mxl692 demod-tuner / Hauppauge usb QuadHD Brad Love 3 siblings, 0 replies; 20+ messages in thread From: Brad Love @ 2020-06-12 18:39 UTC (permalink / raw) To: linux-media; +Cc: Brad Love TS2 active led is completely igored, fix that. Signed-off-by: Brad Love <brad@nextdimension.cc> --- drivers/media/usb/em28xx/em28xx-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index e6088b5d1b80..299cd38a86e9 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -698,8 +698,10 @@ int em28xx_capture_start(struct em28xx *dev, int start) if (dev->mode == EM28XX_ANALOG_MODE) led = em28xx_find_led(dev, EM28XX_LED_ANALOG_CAPTURING); - else + else if (dev->ts == PRIMARY_TS) led = em28xx_find_led(dev, EM28XX_LED_DIGITAL_CAPTURING); + else + led = em28xx_find_led(dev, EM28XX_LED_DIGITAL_CAPTURING_TS2); if (led) em28xx_write_reg_bits(dev, led->gpio_reg, -- 2.23.0 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 3/3] em28xx: Add support for Hauppauge USB QuadHD 2020-06-12 18:39 [PATCH 0/3] MaxLinear mxl692 demod-tuner / Hauppauge usb QuadHD Brad Love 2020-06-12 18:39 ` [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver Brad Love 2020-06-12 18:39 ` [PATCH 2/3] em28xx-core: Fix TS2 active led Brad Love @ 2020-06-12 18:39 ` Brad Love 2021-01-26 1:54 ` [PATCH 0/4] MaxLinear mxl692 demod-tuner / Hauppauge usb QuadHD Brad Love 3 siblings, 0 replies; 20+ messages in thread From: Brad Love @ 2020-06-12 18:39 UTC (permalink / raw) To: linux-media; +Cc: Brad Love Hauppauge USB QuadHD contains two two-tuner em28xx devices behind a usb hub. Each of the four dvb adapters contains a MaxLinear 692 combo ATSC demod/tuner. Bus 003 Device 006: ID 2040:846d Hauppauge Bus 003 Device 005: ID 2040:846d Hauppauge Signed-off-by: Brad Love <brad@nextdimension.cc> --- drivers/media/usb/em28xx/Kconfig | 1 + drivers/media/usb/em28xx/em28xx-cards.c | 46 +++++++++++++++++++++++++ drivers/media/usb/em28xx/em28xx-dvb.c | 26 ++++++++++++++ drivers/media/usb/em28xx/em28xx.h | 1 + 4 files changed, 74 insertions(+) diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index f2031a933e54..2237eb73f010 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -67,6 +67,7 @@ config VIDEO_EM28XX_DVB select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MXL692 if MEDIA_SUBDRV_AUTOSELECT help This adds support for DVB cards based on the Empiatech em28xx chips. diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index a8c321d11827..d9d53056cfd9 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -549,6 +549,21 @@ static const struct em28xx_reg_seq hauppauge_dualhd_dvb[] = { {-1, -1, -1, -1}, }; +/* Hauppauge USB QuadHD */ +static struct em28xx_reg_seq hauppauge_usb_quadhd_atsc_reg_seq[] = { + {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0}, + {0x0d, 0xff, 0xff, 200}, + {0x50, 0x04, 0xff, 300}, + {EM2874_R80_GPIO_P0_CTRL, 0xb0, 0xf0, 100}, /* demod 1 reset */ + {EM2874_R80_GPIO_P0_CTRL, 0xf0, 0xf0, 100}, + {EM2874_R80_GPIO_P0_CTRL, 0xd0, 0xf0, 100}, /* demod 2 reset */ + {EM2874_R80_GPIO_P0_CTRL, 0xf0, 0xf0, 100}, + {EM2874_R5F_TS_ENABLE, 0x44, 0xff, 50}, + {EM2874_R5D_TS1_PKT_SIZE, 0x05, 0xff, 50}, + {EM2874_R5E_TS2_PKT_SIZE, 0x05, 0xff, 50}, + {-1, -1, -1, -1}, +}; + /* * Button definitions */ @@ -644,6 +659,22 @@ static struct em28xx_led hauppauge_dualhd_leds[] = { {-1, 0, 0, 0}, }; +static struct em28xx_led hauppauge_usb_quadhd_leds[] = { + { + .role = EM28XX_LED_DIGITAL_CAPTURING, + .gpio_reg = EM2874_R80_GPIO_P0_CTRL, + .gpio_mask = EM_GPIO_2, + .inverted = 1, + }, + { + .role = EM28XX_LED_DIGITAL_CAPTURING_TS2, + .gpio_reg = EM2874_R80_GPIO_P0_CTRL, + .gpio_mask = EM_GPIO_0, + .inverted = 1, + }, + {-1, 0, 0, 0}, +}; + /* * Board definitions */ @@ -2519,6 +2550,19 @@ const struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + /* 2040:846d Hauppauge USB QuadHD + * Empia 28274, Max Linear 692 ATSC combo demod/tuner + */ + [EM2874S_BOARD_HAUPPAUGE_USB_QUADHD] = { + .name = "Hauppauge USB QuadHD ATSC", + .def_i2c_bus = 1, + .has_dual_ts = 1, + .has_dvb = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ, + .tuner_type = TUNER_ABSENT, + .tuner_gpio = hauppauge_usb_quadhd_atsc_reg_seq, + .leds = hauppauge_usb_quadhd_leds, + }, }; EXPORT_SYMBOL_GPL(em28xx_boards); @@ -2652,6 +2696,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, { USB_DEVICE(0x2040, 0x826d), .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, + { USB_DEVICE(0x2040, 0x846d), + .driver_info = EM2874S_BOARD_HAUPPAUGE_USB_QUADHD }, { USB_DEVICE(0x0438, 0xb002), .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 }, { USB_DEVICE(0x2001, 0xf112), diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index fb9cbfa81a84..3b6aaf810466 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -62,6 +62,7 @@ #include "si2157.h" #include "tc90522.h" #include "qm1d1c0042.h" +#include "mxl692.h" MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>"); MODULE_LICENSE("GPL v2"); @@ -1459,6 +1460,26 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_01595(struct em28xx *dev) return 0; } +static int em2874S_dvb_init_hauppauge_usb_quadhd(struct em28xx *dev) +{ + struct em28xx_dvb *dvb = dev->dvb; + struct mxl692_config mxl692_config = {}; + unsigned char addr; + + /* attach demod/tuner combo */ + mxl692_config.id = (dev->ts == PRIMARY_TS) ? 0 : 1; + mxl692_config.fe = &dvb->fe[0]; + addr = (dev->ts == PRIMARY_TS) ? 0x60 : 0x63; + + dvb->i2c_client_demod = dvb_module_probe("mxl692", NULL, + &dev->i2c_adap[dev->def_i2c_bus], + addr, &mxl692_config); + if (!dvb->i2c_client_demod) + return -ENODEV; + + return 0; +} + static int em28xx_dvb_init(struct em28xx *dev) { int result = 0, dvb_alt = 0; @@ -1945,6 +1966,11 @@ static int em28xx_dvb_init(struct em28xx *dev) if (result) goto out_free; break; + case EM2874S_BOARD_HAUPPAUGE_USB_QUADHD: + result = em2874S_dvb_init_hauppauge_usb_quadhd(dev); + if (result) + goto out_free; + break; default: dev_err(&dev->intf->dev, "The frontend of your DVB/ATSC card isn't supported yet\n"); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index acbb62397314..1782bfaf0093 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -151,6 +151,7 @@ #define EM2882_BOARD_ZOLID_HYBRID_TV_STICK 102 #define EM2861_BOARD_MAGIX_VIDEOWANDLER2 103 #define EM28178_BOARD_PCTV_461E_V2 104 +#define EM2874S_BOARD_HAUPPAUGE_USB_QUADHD 105 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 -- 2.23.0 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 0/4] MaxLinear mxl692 demod-tuner / Hauppauge usb QuadHD 2020-06-12 18:39 [PATCH 0/3] MaxLinear mxl692 demod-tuner / Hauppauge usb QuadHD Brad Love ` (2 preceding siblings ...) 2020-06-12 18:39 ` [PATCH 3/3] em28xx: Add support for Hauppauge USB QuadHD Brad Love @ 2021-01-26 1:54 ` Brad Love 2021-01-26 1:54 ` [PATCH 1/4] mxl692: MaxLinear 692 ATSC demod/tuner driver Brad Love ` (3 more replies) 3 siblings, 4 replies; 20+ messages in thread From: Brad Love @ 2021-01-26 1:54 UTC (permalink / raw) To: linux-media, sean; +Cc: Brad Love Attached is a new driver for MaxLinear mxl692 ATSC/QAM combo tuner-demodulator. The driver is used by em28xx based Hauppauge USB QuadHD. The Hauppauge USB QuadHD contains four instances of the mxl692, configured as two "dual tuner" em28xx devices. Bus 003 Device 006: ID 2040:846d Hauppauge Bus 003 Device 005: ID 2040:846d Hauppauge Bus 003 Device 004: ID 04e2:0404 Exar Corp. The ATSC portion works fully, the QAM portion needs some TLC, and is therefore not listed in the DVB capabilities. The driver is based off vendor provided GPLv2 source code. I have left the few long line warnings as is, due to recent developments/statements regarding this topic. I do not feel the driver would be made any clearer by arbitrarily splitting up the remaining lines further. All endianess issues have been sorted out. The driver is verified fully working on a (ancient) powerpc Mac Mini running current mainline kernel. Brad Love (4): mxl692: MaxLinear 692 ATSC demod/tuner driver em28xx-core: Fix TS2 active led em28xx-core: Fix i2c error debug em28xx: Add support for Hauppauge USB QuadHD drivers/media/dvb-frontends/Kconfig | 9 + drivers/media/dvb-frontends/Makefile | 1 + drivers/media/dvb-frontends/mxl692.c | 1378 +++++++++++++++++++++ drivers/media/dvb-frontends/mxl692.h | 38 + drivers/media/dvb-frontends/mxl692_defs.h | 548 ++++++++ drivers/media/usb/em28xx/Kconfig | 1 + drivers/media/usb/em28xx/em28xx-cards.c | 46 + drivers/media/usb/em28xx/em28xx-core.c | 4 +- drivers/media/usb/em28xx/em28xx-dvb.c | 26 + drivers/media/usb/em28xx/em28xx-i2c.c | 6 +- drivers/media/usb/em28xx/em28xx.h | 1 + 11 files changed, 2056 insertions(+), 2 deletions(-) create mode 100644 drivers/media/dvb-frontends/mxl692.c create mode 100644 drivers/media/dvb-frontends/mxl692.h create mode 100644 drivers/media/dvb-frontends/mxl692_defs.h -- 2.28.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 1/4] mxl692: MaxLinear 692 ATSC demod/tuner driver 2021-01-26 1:54 ` [PATCH 0/4] MaxLinear mxl692 demod-tuner / Hauppauge usb QuadHD Brad Love @ 2021-01-26 1:54 ` Brad Love 2021-01-26 1:54 ` [PATCH 2/4] em28xx-core: Fix TS2 active led Brad Love ` (2 subsequent siblings) 3 siblings, 0 replies; 20+ messages in thread From: Brad Love @ 2021-01-26 1:54 UTC (permalink / raw) To: linux-media, sean; +Cc: Brad Love MaxLinear 692 is a combo demod/tuner which has ATSC and QAM capabilities. Only ATSC is currently advertised via DVB properties. QAM still has issues. Confirmed working on both big and little endian. Big endian verified on PowerPC Mac mini. Signed-off-by: Brad Love <brad@nextdimension.cc> --- Since v1: - Rework all endianess related code - Fix big endian issues - Fix messy casts - Optimize checksum calc - Address all sparse issues - Use memcmp instead of byte comparison - Remove commented relics drivers/media/dvb-frontends/Kconfig | 9 + drivers/media/dvb-frontends/Makefile | 1 + drivers/media/dvb-frontends/mxl692.c | 1378 +++++++++++++++++++++ drivers/media/dvb-frontends/mxl692.h | 38 + drivers/media/dvb-frontends/mxl692_defs.h | 548 ++++++++ 5 files changed, 1974 insertions(+) create mode 100644 drivers/media/dvb-frontends/mxl692.c create mode 100644 drivers/media/dvb-frontends/mxl692.h create mode 100644 drivers/media/dvb-frontends/mxl692_defs.h diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 643b851a6b60..b1ded2137f0e 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -708,6 +708,15 @@ config DVB_S5H1411 An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. +config DVB_MXL692 + tristate "MaxLinear MXL692 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + MaxLinear MxL692 is a combo tuner-demodulator that + supports ATSC 8VSB and QAM modes. Say Y when you want to + support this frontend. + comment "ISDB-T (terrestrial) frontends" depends on DVB_CORE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index e9179162658c..b9f47d68e14e 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_DVB_S5H1420) += s5h1420.o obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o obj-$(CONFIG_DVB_LGDT3306A) += lgdt3306a.o +obj-$(CONFIG_DVB_MXL692) += mxl692.o obj-$(CONFIG_DVB_LG2160) += lg2160.o obj-$(CONFIG_DVB_CX24123) += cx24123.o obj-$(CONFIG_DVB_LNBH25) += lnbh25.o diff --git a/drivers/media/dvb-frontends/mxl692.c b/drivers/media/dvb-frontends/mxl692.c new file mode 100644 index 000000000000..86af831c0176 --- /dev/null +++ b/drivers/media/dvb-frontends/mxl692.c @@ -0,0 +1,1378 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the MaxLinear MxL69x family of combo tuners/demods + * + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> + * + * based on code: + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved + * which was released under GPL V2 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/mutex.h> +#include <linux/i2c-mux.h> +#include <linux/string.h> +#include <linux/firmware.h> + +#include "mxl692.h" +#include "mxl692_defs.h" + +static const struct dvb_frontend_ops mxl692_ops; + +struct mxl692_dev { + struct dvb_frontend fe; + struct i2c_client *i2c_client; + struct mutex i2c_lock; /* i2c command mutex */ + enum MXL_EAGLE_DEMOD_TYPE_E demod_type; + enum MXL_EAGLE_POWER_MODE_E power_mode; + u32 current_frequency; + int device_type; + int seqnum; + int init_done; +}; + +static int mxl692_i2c_write(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) +{ + int ret = 0; + struct i2c_msg msg = { + .addr = dev->i2c_client->addr, + .flags = 0, + .buf = buffer, + .len = buf_len + }; + + ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1); + if (ret != 1) + dev_dbg(&dev->i2c_client->dev, "i2c write error!\n"); + + return ret; +} + +static int mxl692_i2c_read(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) +{ + int ret = 0; + struct i2c_msg msg = { + .addr = dev->i2c_client->addr, + .flags = I2C_M_RD, + .buf = buffer, + .len = buf_len + }; + + ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1); + if (ret != 1) + dev_dbg(&dev->i2c_client->dev, "i2c read error!\n"); + + return ret; +} + +static int convert_endian(u32 size, u8 *d) +{ + u32 i; + + for (i = 0; i < (size & ~3); i += 4) { + d[i + 0] ^= d[i + 3]; + d[i + 3] ^= d[i + 0]; + d[i + 0] ^= d[i + 3]; + + d[i + 1] ^= d[i + 2]; + d[i + 2] ^= d[i + 1]; + d[i + 1] ^= d[i + 2]; + } + + switch (size & 3) { + case 0: + case 1: + /* do nothing */ + break; + case 2: + d[i + 0] ^= d[i + 1]; + d[i + 1] ^= d[i + 0]; + d[i + 0] ^= d[i + 1]; + break; + + case 3: + d[i + 0] ^= d[i + 2]; + d[i + 2] ^= d[i + 0]; + d[i + 0] ^= d[i + 2]; + break; + } + return size; +} + +static int convert_endian_n(int n, u32 size, u8 *d) +{ + int i, count = 0; + + for (i = 0; i < n; i += size) + count += convert_endian(size, d + i); + return count; +} + +static void mxl692_tx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) +{ +#ifdef __BIG_ENDIAN + return; +#endif + buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ + + switch (opcode) { + case MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET: + case MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET: + case MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET: + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_PARAMS_SET: + buffer += 5; + buffer += convert_endian(2 * sizeof(u32), buffer); + break; + default: + /* no swapping - all get opcodes */ + /* ATSC/OOB no swapping */ + break; + } +} + +static void mxl692_rx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) +{ +#ifdef __BIG_ENDIAN + return; +#endif + buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ + + switch (opcode) { + case MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET: + buffer++; + buffer += convert_endian(2 * sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_ATSC_STATUS_GET: + buffer += convert_endian_n(2, sizeof(u16), buffer); + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET: + buffer += convert_endian(3 * sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET: + buffer += convert_endian_n(24, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_STATUS_GET: + buffer += 8; + buffer += convert_endian_n(2, sizeof(u16), buffer); + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET: + buffer += convert_endian(7 * sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET: + buffer += convert_endian_n(24, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET: + buffer += convert_endian_n(8, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET: + buffer += convert_endian_n(17, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET: + buffer += convert_endian(3 * sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_OOB_STATUS_GET: + buffer += convert_endian_n(2, sizeof(u16), buffer); + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_SMA_RECEIVE_GET: + buffer += convert_endian(sizeof(u32), buffer); + break; + default: + /* no swapping - all set opcodes */ + break; + } +} + +static u32 mxl692_checksum(u8 *buffer, u32 size) +{ + u32 ix, div_size; + u32 cur_cksum = 0; + __be32 *buf; + + div_size = DIV_ROUND_UP(size, 4); + + buf = (__be32 *)buffer; + for (ix = 0; ix < div_size; ix++) + cur_cksum += be32_to_cpu(buf[ix]); + + cur_cksum ^= 0xDEADBEEF; + + return cur_cksum; +} + +static int mxl692_validate_fw_header(struct mxl692_dev *dev, + const u8 *buffer, u32 buf_len) +{ + int status = 0; + u32 ix, temp; + __be32 *local_buf = NULL; + u8 temp_cksum = 0; + const u8 fw_hdr[] = { 0x4D, 0x31, 0x10, 0x02, 0x40, 0x00, 0x00, 0x80 }; + + if (memcmp(buffer, fw_hdr, 8) != 0) { + status = -EINVAL; + goto err_finish; + } + + local_buf = (__be32 *)(buffer + 8); + temp = be32_to_cpu(*local_buf); + + if ((buf_len - 16) != temp >> 8) { + status = -EINVAL; + goto err_finish; + } + + for (ix = 16; ix < buf_len; ix++) + temp_cksum += buffer[ix]; + + if (temp_cksum != buffer[11]) + status = -EINVAL; + +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "failed\n"); + return status; +} + +static int mxl692_write_fw_block(struct mxl692_dev *dev, const u8 *buffer, + u32 buf_len, u32 *index) +{ + int status = 0; + u32 ix = 0, total_len = 0, addr = 0, chunk_len = 0, prevchunk_len = 0; + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; + int payload_max = MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE; + + ix = *index; + + if (buffer[ix] == 0x53) { + total_len = buffer[ix + 1] << 16 | buffer[ix + 2] << 8 | buffer[ix + 3]; + total_len = (total_len + 3) & ~3; + addr = buffer[ix + 4] << 24 | buffer[ix + 5] << 16 | + buffer[ix + 6] << 8 | buffer[ix + 7]; + ix += MXL_EAGLE_FW_SEGMENT_HEADER_SIZE; + + while ((total_len > 0) && (status == 0)) { + plocal_buf = local_buf; + chunk_len = (total_len < payload_max) ? total_len : payload_max; + + *plocal_buf++ = 0xFC; + *plocal_buf++ = chunk_len + sizeof(u32); + + *(u32 *)plocal_buf = addr + prevchunk_len; +#ifdef __BIG_ENDIAN + convert_endian(sizeof(u32), plocal_buf); +#endif + plocal_buf += sizeof(u32); + + memcpy(plocal_buf, &buffer[ix], chunk_len); + convert_endian(chunk_len, plocal_buf); + if (mxl692_i2c_write(dev, local_buf, + (chunk_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { + status = -EREMOTEIO; + break; + } + + prevchunk_len += chunk_len; + total_len -= chunk_len; + ix += chunk_len; + } + *index = ix; + } else { + status = -EINVAL; + } + + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + + return status; +} + +static int mxl692_memwrite(struct mxl692_dev *dev, u32 addr, + u8 *buffer, u32 size) +{ + int status = 0, total_len = 0; + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; + + total_len = size; + total_len = (total_len + 3) & ~3; /* 4 byte alignment */ + + if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE)) + dev_dbg(&dev->i2c_client->dev, "hrmph?\n"); + + plocal_buf = local_buf; + + *plocal_buf++ = 0xFC; + *plocal_buf++ = total_len + sizeof(u32); + + *(u32 *)plocal_buf = addr; + plocal_buf += sizeof(u32); + + memcpy(plocal_buf, buffer, total_len); +#ifdef __BIG_ENDIAN + convert_endian(sizeof(u32) + total_len, local_buf + 2); +#endif + if (mxl692_i2c_write(dev, local_buf, + (total_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { + status = -EREMOTEIO; + goto err_finish; + } + + return status; +err_finish: + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_memread(struct mxl692_dev *dev, u32 addr, + u8 *buffer, u32 size) +{ + int status = 0; + u8 local_buf[MXL_EAGLE_I2C_MHEADER_SIZE] = {}, *plocal_buf = NULL; + + plocal_buf = local_buf; + + *plocal_buf++ = 0xFB; + *plocal_buf++ = sizeof(u32); + *(u32 *)plocal_buf = addr; +#ifdef __BIG_ENDIAN + convert_endian(sizeof(u32), plocal_buf); +#endif + mutex_lock(&dev->i2c_lock); + + if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_MHEADER_SIZE) > 0) { + size = (size + 3) & ~3; /* 4 byte alignment */ + status = mxl692_i2c_read(dev, buffer, (u16)size) < 0 ? -EREMOTEIO : 0; +#ifdef __BIG_ENDIAN + if (status == 0) + convert_endian(size, buffer); +#endif + } else { + status = -EREMOTEIO; + } + + mutex_unlock(&dev->i2c_lock); + + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + + return status; +} + +static const char *mxl692_opcode_string(u8 opcode) +{ + if (opcode >= 0 && opcode <= MXL_EAGLE_OPCODE_INTERNAL) + return MXL_EAGLE_OPCODE_STRING[opcode]; + + return "invalid opcode"; +} + +static int mxl692_opwrite(struct mxl692_dev *dev, u8 *buffer, + u32 size) +{ + int status = 0, total_len = 0; + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; + struct MXL_EAGLE_HOST_MSG_HEADER_T *tx_hdr = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)buffer; + + total_len = size; + total_len = (total_len + 3) & ~3; /* 4 byte alignment */ + + if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) + dev_dbg(&dev->i2c_client->dev, "hrmph?\n"); + + plocal_buf = local_buf; + + *plocal_buf++ = 0xFE; + *plocal_buf++ = (u8)total_len; + + memcpy(plocal_buf, buffer, total_len); + convert_endian(total_len, plocal_buf); + + if (mxl692_i2c_write(dev, local_buf, + (total_len + MXL_EAGLE_I2C_PHEADER_SIZE)) < 0) { + status = -EREMOTEIO; + goto err_finish; + } +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "opcode %s err %d\n", + mxl692_opcode_string(tx_hdr->opcode), status); + return status; +} + +static int mxl692_opread(struct mxl692_dev *dev, u8 *buffer, + u32 size) +{ + int status = 0; + u32 ix = 0; + u8 local_buf[MXL_EAGLE_I2C_PHEADER_SIZE] = {}; + + local_buf[0] = 0xFD; + local_buf[1] = 0; + + if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_PHEADER_SIZE) > 0) { + size = (size + 3) & ~3; /* 4 byte alignment */ + + /* Read in 4 byte chunks */ + for (ix = 0; ix < size; ix += 4) { + if (mxl692_i2c_read(dev, buffer + ix, 4) < 0) { + dev_dbg(&dev->i2c_client->dev, "ix=%d size=%d\n", ix, size); + status = -EREMOTEIO; + goto err_finish; + } + } + convert_endian(size, buffer); + } else { + status = -EREMOTEIO; + } +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_i2c_writeread(struct mxl692_dev *dev, + u8 opcode, + u8 *tx_payload, + u8 tx_payload_size, + u8 *rx_payload, + u8 rx_payload_expected) +{ + int status = 0, timeout = 40; + u8 tx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + u32 resp_checksum = 0, resp_checksum_tmp = 0; + struct MXL_EAGLE_HOST_MSG_HEADER_T *tx_header; + struct MXL_EAGLE_HOST_MSG_HEADER_T *rx_header; + + mutex_lock(&dev->i2c_lock); + + if ((tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE) > + (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) { + status = -EINVAL; + goto err_finish; + } + + tx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)tx_buf; + tx_header->opcode = opcode; + tx_header->seqnum = dev->seqnum++; + tx_header->payload_size = tx_payload_size; + tx_header->checksum = 0; + + if (dev->seqnum == 0) + dev->seqnum = 1; + + if (tx_payload && tx_payload_size > 0) + memcpy(&tx_buf[MXL_EAGLE_HOST_MSG_HEADER_SIZE], tx_payload, tx_payload_size); + + mxl692_tx_swap(opcode, tx_buf); + + tx_header->checksum = 0; + tx_header->checksum = mxl692_checksum(tx_buf, + MXL_EAGLE_HOST_MSG_HEADER_SIZE + tx_payload_size); +#ifdef __LITTLE_ENDIAN + convert_endian(4, (u8 *)&tx_header->checksum); /* cksum is big endian */ +#endif + /* send Tx message */ + status = mxl692_opwrite(dev, tx_buf, + tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE); + if (status) { + status = -EREMOTEIO; + goto err_finish; + } + + /* receive Rx message (polling) */ + rx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)rx_buf; + + do { + status = mxl692_opread(dev, rx_buf, + rx_payload_expected + MXL_EAGLE_HOST_MSG_HEADER_SIZE); + usleep_range(1000, 2000); + timeout--; + } while ((timeout > 0) && (status == 0) && + (rx_header->seqnum == 0) && + (rx_header->checksum == 0)); + + if (timeout == 0 || status) { + dev_dbg(&dev->i2c_client->dev, "timeout=%d status=%d\n", + timeout, status); + status = -ETIMEDOUT; + goto err_finish; + } + + if (rx_header->status) { + dev_dbg(&dev->i2c_client->dev, "rx header status code: %d\n", rx_header->status); + status = -EREMOTEIO; + goto err_finish; + } + + if (rx_header->seqnum != tx_header->seqnum || + rx_header->opcode != tx_header->opcode || + rx_header->payload_size != rx_payload_expected) { + dev_dbg(&dev->i2c_client->dev, "Something failed seq=%s opcode=%s pSize=%s\n", + rx_header->seqnum != tx_header->seqnum ? "X" : "0", + rx_header->opcode != tx_header->opcode ? "X" : "0", + rx_header->payload_size != rx_payload_expected ? "X" : "0"); + if (rx_header->payload_size != rx_payload_expected) + dev_dbg(&dev->i2c_client->dev, + "rx_header->payloadSize=%d rx_payload_expected=%d\n", + rx_header->payload_size, rx_payload_expected); + status = -EREMOTEIO; + goto err_finish; + } + + resp_checksum = rx_header->checksum; + rx_header->checksum = 0; + + resp_checksum_tmp = mxl692_checksum(rx_buf, + MXL_EAGLE_HOST_MSG_HEADER_SIZE + rx_header->payload_size); +#ifdef __LITTLE_ENDIAN + convert_endian(4, (u8 *)&resp_checksum_tmp); /* cksum is big endian */ +#endif + if (resp_checksum != resp_checksum_tmp) { + dev_dbg(&dev->i2c_client->dev, "rx checksum failure\n"); + status = -EREMOTEIO; + goto err_finish; + } + + mxl692_rx_swap(rx_header->opcode, rx_buf); + + if (rx_header->payload_size > 0) { + if (!rx_payload) { + dev_dbg(&dev->i2c_client->dev, "no rx payload?!?\n"); + status = -EREMOTEIO; + goto err_finish; + } + memcpy(rx_payload, rx_buf + MXL_EAGLE_HOST_MSG_HEADER_SIZE, + rx_header->payload_size); + } +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + + mutex_unlock(&dev->i2c_lock); + return status; +} + +static int mxl692_fwdownload(struct mxl692_dev *dev, + const u8 *firmware_buf, u32 buf_len) +{ + int status = 0; + u32 ix, reg_val = 0x1; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_DEV_STATUS_T *dev_status; + + if (buf_len < MXL_EAGLE_FW_HEADER_SIZE || + buf_len > MXL_EAGLE_FW_MAX_SIZE_IN_KB * 1000) + return -EINVAL; + + mutex_lock(&dev->i2c_lock); + + dev_dbg(&dev->i2c_client->dev, "\n"); + + status = mxl692_validate_fw_header(dev, firmware_buf, buf_len); + if (status) + goto err_finish; + + ix = 16; + status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* DRAM */ + if (status) + goto err_finish; + + status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* IRAM */ + if (status) + goto err_finish; + + /* release CPU from reset */ + status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + mutex_unlock(&dev->i2c_lock); + + if (status == 0) { + /* verify FW is alive */ + usleep_range(MXL_EAGLE_FW_LOAD_TIME * 1000, (MXL_EAGLE_FW_LOAD_TIME + 5) * 1000); + dev_status = (struct MXL_EAGLE_DEV_STATUS_T *)&rx_buf; + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_STATUS_GET, + NULL, + 0, + (u8 *)dev_status, + sizeof(struct MXL_EAGLE_DEV_STATUS_T)); + } + + return status; +err_finish: + mutex_unlock(&dev->i2c_lock); + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_get_versions(struct mxl692_dev *dev) +{ + int status = 0; + struct MXL_EAGLE_DEV_VER_T dev_ver = {}; + static const char * const chip_id[] = {"N/A", "691", "248", "692"}; + + status = mxl692_i2c_writeread(dev, MXL_EAGLE_OPCODE_DEVICE_VERSION_GET, + NULL, + 0, + (u8 *)&dev_ver, + sizeof(struct MXL_EAGLE_DEV_VER_T)); + if (status) + return status; + + dev_info(&dev->i2c_client->dev, "MxL692_DEMOD Chip ID: %s\n", + chip_id[dev_ver.chip_id]); + + dev_info(&dev->i2c_client->dev, + "MxL692_DEMOD FW Version: %d.%d.%d.%d_RC%d\n", + dev_ver.firmware_ver[0], + dev_ver.firmware_ver[1], + dev_ver.firmware_ver[2], + dev_ver.firmware_ver[3], + dev_ver.firmware_ver[4]); + + return status; +} + +static int mxl692_reset(struct mxl692_dev *dev) +{ + int status = 0; + u32 dev_type = MXL_EAGLE_DEVICE_MAX, reg_val = 0x2; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + /* legacy i2c override */ + status = mxl692_memwrite(dev, 0x80000100, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* verify sku */ + status = mxl692_memread(dev, 0x70000188, (u8 *)&dev_type, sizeof(u32)); + if (status) + goto err_finish; + + if (dev_type != dev->device_type) + goto err_finish; + +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_config_regulators(struct mxl692_dev *dev, + enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E power_supply) +{ + int status = 0; + u32 reg_val; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + /* configure main regulator according to the power supply source */ + status = mxl692_memread(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val &= 0x00FFFFFF; + reg_val |= (power_supply == MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE) ? + 0x14000000 : 0x10000000; + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* configure digital regulator to high current mode */ + status = mxl692_memread(dev, 0x90000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x800; + + status = mxl692_memwrite(dev, 0x90000018, (u8 *)®_val, sizeof(u32)); + +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_config_xtal(struct mxl692_dev *dev, + struct MXL_EAGLE_DEV_XTAL_T *dev_xtal) +{ + int status = 0; + u32 reg_val, reg_val1; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + status = mxl692_memread(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* set XTAL capacitance */ + reg_val &= 0xFFFFFFE0; + reg_val |= dev_xtal->xtal_cap; + + /* set CLK OUT */ + reg_val = dev_xtal->clk_out_enable ? (reg_val | 0x0100) : (reg_val & 0xFFFFFEFF); + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* set CLK OUT divider */ + reg_val = dev_xtal->clk_out_div_enable ? (reg_val | 0x0200) : (reg_val & 0xFFFFFDFF); + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* set XTAL sharing */ + reg_val = dev_xtal->xtal_sharing_enable ? (reg_val | 0x010400) : (reg_val & 0xFFFEFBFF); + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* enable/disable XTAL calibration, based on master/slave device */ + status = mxl692_memread(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); + if (status) + goto err_finish; + + if (dev_xtal->xtal_calibration_enable) { + /* enable XTAL calibration and set XTAL amplitude to a higher value */ + reg_val1 &= 0xFFFFFFFD; + reg_val1 |= 0x30; + + status = mxl692_memwrite(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); + if (status) + goto err_finish; + } else { + /* disable XTAL calibration */ + reg_val1 |= 0x2; + + status = mxl692_memwrite(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); + if (status) + goto err_finish; + + /* set XTAL bias value */ + status = mxl692_memread(dev, 0x9000002c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val &= 0xC0FFFFFF; + reg_val |= 0xA000000; + + status = mxl692_memwrite(dev, 0x9000002c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + } + + /* start XTAL calibration */ + status = mxl692_memread(dev, 0x70000010, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x8; + + status = mxl692_memwrite(dev, 0x70000010, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + status = mxl692_memread(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x10; + + status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + status = mxl692_memread(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val &= 0xFFFFEFFF; + + status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x1000; + + status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + usleep_range(45000, 55000); + +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_powermode(struct mxl692_dev *dev, + enum MXL_EAGLE_POWER_MODE_E power_mode) +{ + int status = 0; + u8 mode = power_mode; + + dev_dbg(&dev->i2c_client->dev, "%s\n", + power_mode == MXL_EAGLE_POWER_MODE_SLEEP ? "sleep" : "active"); + + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET, + &mode, + sizeof(u8), + NULL, + 0); + if (status) { + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; + } + + dev->power_mode = power_mode; + + return status; +} + +static int mxl692_init(struct dvb_frontend *fe) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->i2c_client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int status = 0; + const struct firmware *firmware; + struct MXL_EAGLE_DEV_XTAL_T xtal_config = {}; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + if (dev->init_done) + goto warm; + + dev->seqnum = 1; + + status = mxl692_reset(dev); + if (status) + goto err; + + usleep_range(50 * 1000, 60 * 1000); /* was 1000! */ + + status = mxl692_config_regulators(dev, MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL); + if (status) + goto err; + + xtal_config.xtal_cap = 26; + xtal_config.clk_out_div_enable = 0; + xtal_config.clk_out_enable = 0; + xtal_config.xtal_calibration_enable = 0; + xtal_config.xtal_sharing_enable = 1; + status = mxl692_config_xtal(dev, &xtal_config); + if (status) + goto err; + + status = request_firmware(&firmware, MXL692_FIRMWARE, &client->dev); + if (status) { + dev_dbg(&dev->i2c_client->dev, "firmware missing? %s\n", + MXL692_FIRMWARE); + goto err; + } + + status = mxl692_fwdownload(dev, firmware->data, firmware->size); + if (status) + goto err_release_firmware; + + release_firmware(firmware); + + status = mxl692_get_versions(dev); + if (status) + goto err; + + dev->power_mode = MXL_EAGLE_POWER_MODE_SLEEP; +warm: + /* Config Device Power Mode */ + if (dev->power_mode != MXL_EAGLE_POWER_MODE_ACTIVE) { + status = mxl692_powermode(dev, MXL_EAGLE_POWER_MODE_ACTIVE); + if (status) + goto err; + + usleep_range(50 * 1000, 60 * 1000); /* was 500! */ + } + + /* Init stats here to indicate which stats are supported */ + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + dev->init_done = 1; + return 0; +err_release_firmware: + release_firmware(firmware); +err: + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_sleep(struct dvb_frontend *fe) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + + if (dev->power_mode != MXL_EAGLE_POWER_MODE_SLEEP) + mxl692_powermode(dev, MXL_EAGLE_POWER_MODE_SLEEP); + + return 0; +} + +static int mxl692_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct mxl692_dev *dev = fe->demodulator_priv; + + int status = 0; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type; + struct MXL_EAGLE_MPEGOUT_PARAMS_T mpeg_params = {}; + enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E qam_annex = MXL_EAGLE_QAM_DEMOD_ANNEX_B; + struct MXL_EAGLE_QAM_DEMOD_PARAMS_T qam_params = {}; + struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T tuner_params = {}; + u8 op_param = 0; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + switch (p->modulation) { + case VSB_8: + demod_type = MXL_EAGLE_DEMOD_TYPE_ATSC; + break; + case QAM_AUTO: + case QAM_64: + case QAM_128: + case QAM_256: + demod_type = MXL_EAGLE_DEMOD_TYPE_QAM; + break; + default: + return -EINVAL; + } + + if (dev->current_frequency == p->frequency && dev->demod_type == demod_type) { + dev_dbg(&dev->i2c_client->dev, "already set up\n"); + return 0; + } + + dev->current_frequency = -1; + dev->demod_type = -1; + + op_param = demod_type; + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET, + &op_param, + sizeof(u8), + NULL, + 0); + if (status) { + dev_dbg(&dev->i2c_client->dev, + "DEVICE_DEMODULATOR_TYPE_SET...FAIL err 0x%x\n", status); + goto err; + } + + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ + + mpeg_params.mpeg_parallel = 0; + mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_MSB_1ST; + mpeg_params.mpeg_sync_pulse_width = MXL_EAGLE_DATA_SYNC_WIDTH_BIT; + mpeg_params.mpeg_valid_pol = MXL_EAGLE_CLOCK_POSITIVE; + mpeg_params.mpeg_sync_pol = MXL_EAGLE_CLOCK_POSITIVE; + mpeg_params.mpeg_clk_pol = MXL_EAGLE_CLOCK_NEGATIVE; + mpeg_params.mpeg3wire_mode_enable = 0; + mpeg_params.mpeg_clk_freq = MXL_EAGLE_MPEG_CLOCK_27MHZ; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, + (u8 *)&mpeg_params, + sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), + NULL, + 0); + if (status) + goto err; + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + if (qam_annex == MXL_EAGLE_QAM_DEMOD_ANNEX_A) + mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_LSB_1ST; + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, + (u8 *)&mpeg_params, + sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), + NULL, + 0); + if (status) + goto err; + + qam_params.annex_type = qam_annex; + qam_params.qam_type = MXL_EAGLE_QAM_DEMOD_AUTO; + qam_params.iq_flip = MXL_EAGLE_DEMOD_IQ_AUTO; + if (p->modulation == QAM_64) + qam_params.symbol_rate_hz = 5057000; + else + qam_params.symbol_rate_hz = 5361000; + + qam_params.symbol_rate_256qam_hz = 5361000; + + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_PARAMS_SET, + (u8 *)&qam_params, + sizeof(struct MXL_EAGLE_QAM_DEMOD_PARAMS_T), + NULL, 0); + if (status) + goto err; + + break; + default: + break; + } + + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ + + tuner_params.freq_hz = p->frequency; + tuner_params.bandwidth = MXL_EAGLE_TUNER_BW_6MHZ; + tuner_params.tune_mode = MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW; + + dev_dbg(&dev->i2c_client->dev, " Tuning Freq: %d %s\n", tuner_params.freq_hz, + demod_type == MXL_EAGLE_DEMOD_TYPE_ATSC ? "ATSC" : "QAM"); + + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET, + (u8 *)&tuner_params, + sizeof(struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T), + NULL, + 0); + if (status) + goto err; + + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_INIT_SET, + NULL, 0, NULL, 0); + if (status) + goto err; + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_RESTART_SET, + NULL, 0, NULL, 0); + if (status) + goto err; + break; + default: + break; + } + + dev->demod_type = demod_type; + dev->current_frequency = p->frequency; + + return 0; +err: + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + p->modulation = c->modulation; + p->frequency = c->frequency; + + return 0; +} + +static int mxl692_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; + struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; + int mxl_status = 0; + + *snr = 0; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; + qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); + if (!mxl_status) { + *snr = (u16)(atsc_status->snr_db_tenths / 10); + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = *snr; + } + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); + if (!mxl_status) + *snr = (u16)(qam_status->snr_db_tenths / 10); + break; + case MXL_EAGLE_DEMOD_TYPE_OOB: + default: + break; + } + + if (mxl_status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", mxl_status); + return mxl_status; +} + +static int mxl692_read_ber_ucb(struct dvb_frontend *fe) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *atsc_errors; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; + int mxl_status = 0; + u32 utmp; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + atsc_errors = (struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *)&rx_buf; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T)); + if (!mxl_status) { + if (atsc_errors->error_packets == 0) + utmp = 0; + else + utmp = ((atsc_errors->error_bytes / atsc_errors->error_packets) * + atsc_errors->total_packets); + /* ber */ + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += atsc_errors->error_bytes; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += utmp; + /* ucb */ + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += atsc_errors->error_packets; + + dev_dbg(&dev->i2c_client->dev, "%llu %llu\n", + c->post_bit_count.stat[0].uvalue, c->block_error.stat[0].uvalue); + } + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + case MXL_EAGLE_DEMOD_TYPE_OOB: + default: + break; + } + + if (mxl_status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", mxl_status); + + return mxl_status; +} + +static int mxl692_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; + struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; + int mxl_status = 0; + *status = 0; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; + qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); + if (!mxl_status && atsc_status->atsc_lock) { + *status |= FE_HAS_SIGNAL; + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_LOCK; + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = atsc_status->snr_db_tenths / 10; + } + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); + if (!mxl_status && qam_status->qam_locked) { + *status |= FE_HAS_SIGNAL; + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_LOCK; + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = qam_status->snr_db_tenths / 10; + } + break; + case MXL_EAGLE_DEMOD_TYPE_OOB: + default: + break; + } + + if ((*status & FE_HAS_LOCK) == 0) { + /* No lock, reset all statistics */ + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return 0; + } + + if (mxl_status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", mxl_status); + else + mxl_status = mxl692_read_ber_ucb(fe); + + return mxl_status; +} + +static const struct dvb_frontend_ops mxl692_ops = { + .delsys = { SYS_ATSC }, + .info = { + .name = "MaxLinear MxL692 VSB tuner-demodulator", + .frequency_min_hz = 54000000, + .frequency_max_hz = 858000000, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_8VSB + }, + + .init = mxl692_init, + .sleep = mxl692_sleep, + .set_frontend = mxl692_set_frontend, + .get_frontend = mxl692_get_frontend, + + .read_status = mxl692_read_status, + .read_snr = mxl692_read_snr, +}; + +static int mxl692_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mxl692_config *config = client->dev.platform_data; + struct mxl692_dev *dev; + int ret = 0; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + dev_dbg(&client->dev, "kzalloc() failed\n"); + goto err; + } + + memcpy(&dev->fe.ops, &mxl692_ops, sizeof(struct dvb_frontend_ops)); + dev->fe.demodulator_priv = dev; + dev->i2c_client = client; + *config->fe = &dev->fe; + mutex_init(&dev->i2c_lock); + i2c_set_clientdata(client, dev); + + dev_info(&client->dev, "MaxLinear mxl692 successfully attached\n"); + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return -ENODEV; +} + +static int mxl692_remove(struct i2c_client *client) +{ + struct mxl692_dev *dev = i2c_get_clientdata(client); + + dev->fe.demodulator_priv = NULL; + i2c_set_clientdata(client, NULL); + kfree(dev); + + return 0; +} + +static const struct i2c_device_id mxl692_id_table[] = { + {"mxl692", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mxl692_id_table); + +static struct i2c_driver mxl692_driver = { + .driver = { + .name = "mxl692", + }, + .probe = mxl692_probe, + .remove = mxl692_remove, + .id_table = mxl692_id_table, +}; + +module_i2c_driver(mxl692_driver); + +MODULE_AUTHOR("Brad Love <brad@nextdimension.cc>"); +MODULE_DESCRIPTION("MaxLinear MxL692 demodulator/tuner driver"); +MODULE_FIRMWARE(MXL692_FIRMWARE); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/mxl692.h b/drivers/media/dvb-frontends/mxl692.h new file mode 100644 index 000000000000..45bc48f1f12f --- /dev/null +++ b/drivers/media/dvb-frontends/mxl692.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver for the MaxLinear MxL69x family of tuners/demods + * + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> + * + * based on code: + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved + * which was released under GPL V2 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MXL692_H_ +#define _MXL692_H_ + +#include <media/dvb_frontend.h> + +#define MXL692_FIRMWARE "dvb-demod-mxl692.fw" + +struct mxl692_config { + unsigned char id; + u8 i2c_addr; + /* + * frontend + * returned by driver + */ + struct dvb_frontend **fe; +}; + +#endif /* _MXL692_H_ */ diff --git a/drivers/media/dvb-frontends/mxl692_defs.h b/drivers/media/dvb-frontends/mxl692_defs.h new file mode 100644 index 000000000000..776ac407b4e7 --- /dev/null +++ b/drivers/media/dvb-frontends/mxl692_defs.h @@ -0,0 +1,548 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver for the MaxLinear MxL69x family of combo tuners/demods + * + * Copyright (C) 2020 Brad Love <brad@nextdimension.cc> + * + * based on code: + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved + * which was released under GPL V2 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/***************************************************************************** + * Defines + ***************************************************************************** + */ +#define MXL_EAGLE_HOST_MSG_HEADER_SIZE 8 +#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76 +#define MXL_EAGLE_QAM_FFE_TAPS_LENGTH 16 +#define MXL_EAGLE_QAM_SPUR_TAPS_LENGTH 32 +#define MXL_EAGLE_QAM_DFE_TAPS_LENGTH 72 +#define MXL_EAGLE_ATSC_FFE_TAPS_LENGTH 4096 +#define MXL_EAGLE_ATSC_DFE_TAPS_LENGTH 384 +#define MXL_EAGLE_VERSION_SIZE 5 /* A.B.C.D-RCx */ +#define MXL_EAGLE_FW_LOAD_TIME 50 + +#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76 +#define MXL_EAGLE_FW_HEADER_SIZE 16 +#define MXL_EAGLE_FW_SEGMENT_HEADER_SIZE 8 +#define MXL_EAGLE_MAX_I2C_PACKET_SIZE 58 +#define MXL_EAGLE_I2C_MHEADER_SIZE 6 +#define MXL_EAGLE_I2C_PHEADER_SIZE 2 + +/* Enum of Eagle family devices */ +enum MXL_EAGLE_DEVICE_E { + MXL_EAGLE_DEVICE_691 = 1, /* Device Mxl691 */ + MXL_EAGLE_DEVICE_248 = 2, /* Device Mxl248 */ + MXL_EAGLE_DEVICE_692 = 3, /* Device Mxl692 */ + MXL_EAGLE_DEVICE_MAX, /* No such device */ +}; + +#define VER_A 1 +#define VER_B 1 +#define VER_C 1 +#define VER_D 3 +#define VER_E 6 + +/* Enum of Host to Eagle I2C protocol opcodes */ +enum MXL_EAGLE_OPCODE_E { + /* DEVICE */ + MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET, + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, + MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET, + MXL_EAGLE_OPCODE_DEVICE_GPIO_DIRECTION_SET, + MXL_EAGLE_OPCODE_DEVICE_GPO_LEVEL_SET, + MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET, + MXL_EAGLE_OPCODE_DEVICE_IO_MUX_SET, + MXL_EAGLE_OPCODE_DEVICE_VERSION_GET, + MXL_EAGLE_OPCODE_DEVICE_STATUS_GET, + MXL_EAGLE_OPCODE_DEVICE_GPI_LEVEL_GET, + + /* TUNER */ + MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET, + MXL_EAGLE_OPCODE_TUNER_LOCK_STATUS_GET, + MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET, + + /* ATSC */ + MXL_EAGLE_OPCODE_ATSC_INIT_SET, + MXL_EAGLE_OPCODE_ATSC_ACQUIRE_CARRIER_SET, + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, + MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, + MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_DFE_TAPS_GET, + MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET, + + /* QAM */ + MXL_EAGLE_OPCODE_QAM_PARAMS_SET, + MXL_EAGLE_OPCODE_QAM_RESTART_SET, + MXL_EAGLE_OPCODE_QAM_STATUS_GET, + MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET, + MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_TAPS_NUMBER_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET, + + /* OOB */ + MXL_EAGLE_OPCODE_OOB_PARAMS_SET, + MXL_EAGLE_OPCODE_OOB_RESTART_SET, + MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET, + MXL_EAGLE_OPCODE_OOB_STATUS_GET, + + /* SMA */ + MXL_EAGLE_OPCODE_SMA_INIT_SET, + MXL_EAGLE_OPCODE_SMA_PARAMS_SET, + MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET, + MXL_EAGLE_OPCODE_SMA_RECEIVE_GET, + + /* DEBUG */ + MXL_EAGLE_OPCODE_INTERNAL, + + MXL_EAGLE_OPCODE_MAX = 70, +}; + +/* Enum of Host to Eagle I2C protocol opcodes */ +static const char * const MXL_EAGLE_OPCODE_STRING[] = { + /* DEVICE */ + "DEVICE_DEMODULATOR_TYPE_SET", + "DEVICE_MPEG_OUT_PARAMS_SET", + "DEVICE_POWERMODE_SET", + "DEVICE_GPIO_DIRECTION_SET", + "DEVICE_GPO_LEVEL_SET", + "DEVICE_INTR_MASK_SET", + "DEVICE_IO_MUX_SET", + "DEVICE_VERSION_GET", + "DEVICE_STATUS_GET", + "DEVICE_GPI_LEVEL_GET", + + /* TUNER */ + "TUNER_CHANNEL_TUNE_SET", + "TUNER_LOCK_STATUS_GET", + "TUNER_AGC_STATUS_GET", + + /* ATSC */ + "ATSC_INIT_SET", + "ATSC_ACQUIRE_CARRIER_SET", + "ATSC_STATUS_GET", + "ATSC_ERROR_COUNTERS_GET", + "ATSC_EQUALIZER_FILTER_DFE_TAPS_GET", + "ATSC_EQUALIZER_FILTER_FFE_TAPS_GET", + + /* QAM */ + "QAM_PARAMS_SET", + "QAM_RESTART_SET", + "QAM_STATUS_GET", + "QAM_ERROR_COUNTERS_GET", + "QAM_CONSTELLATION_VALUE_GET", + "QAM_EQUALIZER_FILTER_FFE_GET", + "QAM_EQUALIZER_FILTER_SPUR_START_GET", + "QAM_EQUALIZER_FILTER_SPUR_END_GET", + "QAM_EQUALIZER_FILTER_DFE_TAPS_NUMBER_GET", + "QAM_EQUALIZER_FILTER_DFE_START_GET", + "QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET", + "QAM_EQUALIZER_FILTER_DFE_END_GET", + + /* OOB */ + "OOB_PARAMS_SET", + "OOB_RESTART_SET", + "OOB_ERROR_COUNTERS_GET", + "OOB_STATUS_GET", + + /* SMA */ + "SMA_INIT_SET", + "SMA_PARAMS_SET", + "SMA_TRANSMIT_SET", + "SMA_RECEIVE_GET", + + /* DEBUG */ + "INTERNAL", +}; + +/* Enum of Callabck function types */ +enum MXL_EAGLE_CB_TYPE_E { + MXL_EAGLE_CB_FW_DOWNLOAD = 0, +}; + +/* Enum of power supply types */ +enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E { + MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE, /* Single supply of 3.3V */ + MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL, /* Dual supply, 1.8V & 3.3V */ +}; + +/* Enum of I/O pad drive modes */ +enum MXL_EAGLE_IO_MUX_DRIVE_MODE_E { + MXL_EAGLE_IO_MUX_DRIVE_MODE_1X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_2X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_3X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_4X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_5X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_6X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_7X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_8X, +}; + +/* Enum of demodulator types. Used for selection of demodulator + * type in relevant devices, e.g. ATSC vs. QAM in Mxl691 + */ +enum MXL_EAGLE_DEMOD_TYPE_E { + MXL_EAGLE_DEMOD_TYPE_QAM, /* Mxl248 or Mxl692 */ + MXL_EAGLE_DEMOD_TYPE_OOB, /* Mxl248 only */ + MXL_EAGLE_DEMOD_TYPE_ATSC /* Mxl691 or Mxl692 */ +}; + +/* Enum of power modes. Used for initial + * activation, or for activating sleep mode + */ +enum MXL_EAGLE_POWER_MODE_E { + MXL_EAGLE_POWER_MODE_SLEEP, + MXL_EAGLE_POWER_MODE_ACTIVE +}; + +/* Enum of GPIOs, used in device GPIO APIs */ +enum MXL_EAGLE_GPIO_NUMBER_E { + MXL_EAGLE_GPIO_NUMBER_0, + MXL_EAGLE_GPIO_NUMBER_1, + MXL_EAGLE_GPIO_NUMBER_2, + MXL_EAGLE_GPIO_NUMBER_3, + MXL_EAGLE_GPIO_NUMBER_4, + MXL_EAGLE_GPIO_NUMBER_5, + MXL_EAGLE_GPIO_NUMBER_6 +}; + +/* Enum of GPIO directions, used in GPIO direction configuration API */ +enum MXL_EAGLE_GPIO_DIRECTION_E { + MXL_EAGLE_GPIO_DIRECTION_INPUT, + MXL_EAGLE_GPIO_DIRECTION_OUTPUT +}; + +/* Enum of GPIO level, used in device GPIO APIs */ +enum MXL_EAGLE_GPIO_LEVEL_E { + MXL_EAGLE_GPIO_LEVEL_LOW, + MXL_EAGLE_GPIO_LEVEL_HIGH, +}; + +/* Enum of I/O Mux function, used in device I/O mux configuration API */ +enum MXL_EAGLE_IOMUX_FUNCTION_E { + MXL_EAGLE_IOMUX_FUNC_FEC_LOCK, + MXL_EAGLE_IOMUX_FUNC_MERR, +}; + +/* Enum of MPEG Data format, used in MPEG and OOB output configuration */ +enum MXL_EAGLE_MPEG_DATA_FORMAT_E { + MXL_EAGLE_DATA_SERIAL_LSB_1ST = 0, + MXL_EAGLE_DATA_SERIAL_MSB_1ST, + + MXL_EAGLE_DATA_SYNC_WIDTH_BIT = 0, + MXL_EAGLE_DATA_SYNC_WIDTH_BYTE +}; + +/* Enum of MPEG Clock format, used in MPEG and OOB output configuration */ +enum MXL_EAGLE_MPEG_CLOCK_FORMAT_E { + MXL_EAGLE_CLOCK_ACTIVE_HIGH = 0, + MXL_EAGLE_CLOCK_ACTIVE_LOW, + + MXL_EAGLE_CLOCK_POSITIVE = 0, + MXL_EAGLE_CLOCK_NEGATIVE, + + MXL_EAGLE_CLOCK_IN_PHASE = 0, + MXL_EAGLE_CLOCK_INVERTED, +}; + +/* Enum of MPEG Clock speeds, used in MPEG output configuration */ +enum MXL_EAGLE_MPEG_CLOCK_RATE_E { + MXL_EAGLE_MPEG_CLOCK_54MHZ, + MXL_EAGLE_MPEG_CLOCK_40_5MHZ, + MXL_EAGLE_MPEG_CLOCK_27MHZ, + MXL_EAGLE_MPEG_CLOCK_13_5MHZ, +}; + +/* Enum of Interrupt mask bit, used in host interrupt configuration */ +enum MXL_EAGLE_INTR_MASK_BITS_E { + MXL_EAGLE_INTR_MASK_DEMOD = 0, + MXL_EAGLE_INTR_MASK_SMA_RX = 1, + MXL_EAGLE_INTR_MASK_WDOG = 31 +}; + +/* Enum of QAM Demodulator type, used in QAM configuration */ +enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E { + MXL_EAGLE_QAM_DEMOD_ANNEX_B, /* J.83B */ + MXL_EAGLE_QAM_DEMOD_ANNEX_A, /* DVB-C */ +}; + +/* Enum of QAM Demodulator modulation, used in QAM configuration and status */ +enum MXL_EAGLE_QAM_DEMOD_QAM_TYPE_E { + MXL_EAGLE_QAM_DEMOD_QAM16, + MXL_EAGLE_QAM_DEMOD_QAM64, + MXL_EAGLE_QAM_DEMOD_QAM256, + MXL_EAGLE_QAM_DEMOD_QAM1024, + MXL_EAGLE_QAM_DEMOD_QAM32, + MXL_EAGLE_QAM_DEMOD_QAM128, + MXL_EAGLE_QAM_DEMOD_QPSK, + MXL_EAGLE_QAM_DEMOD_AUTO, +}; + +/* Enum of Demodulator IQ setup, used in QAM, OOB configuration and status */ +enum MXL_EAGLE_IQ_FLIP_E { + MXL_EAGLE_DEMOD_IQ_NORMAL, + MXL_EAGLE_DEMOD_IQ_FLIPPED, + MXL_EAGLE_DEMOD_IQ_AUTO, +}; + +/* Enum of OOB Demodulator symbol rates, used in OOB configuration */ +enum MXL_EAGLE_OOB_DEMOD_SYMB_RATE_E { + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_0_772MHZ, /* ANSI/SCTE 55-2 0.772 MHz */ + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_024MHZ, /* ANSI/SCTE 55-1 1.024 MHz */ + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_544MHZ, /* ANSI/SCTE 55-2 1.544 MHz */ +}; + +/* Enum of tuner channel tuning mode */ +enum MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_E { + MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW, /* Normal "view" mode */ + MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_SCAN, /* Fast "scan" mode */ +}; + +/* Enum of tuner bandwidth */ +enum MXL_EAGLE_TUNER_BW_E { + MXL_EAGLE_TUNER_BW_6MHZ, + MXL_EAGLE_TUNER_BW_7MHZ, + MXL_EAGLE_TUNER_BW_8MHZ, +}; + +/* Enum of tuner bandwidth */ +enum MXL_EAGLE_JUNCTION_TEMPERATURE_E { + MXL_EAGLE_JUNCTION_TEMPERATURE_BELOW_0_CELSIUS = 0, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_0_TO_14_CELSIUS = 1, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_14_TO_28_CELSIUS = 3, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_28_TO_42_CELSIUS = 2, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_42_TO_57_CELSIUS = 6, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_57_TO_71_CELSIUS = 7, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_71_TO_85_CELSIUS = 5, + MXL_EAGLE_JUNCTION_TEMPERATURE_ABOVE_85_CELSIUS = 4, +}; + +/* Struct passed in optional callback used during FW download */ +struct MXL_EAGLE_FW_DOWNLOAD_CB_PAYLOAD_T { + u32 total_len; + u32 downloaded_len; +}; + +/* Struct used of I2C protocol between host and Eagle, internal use only */ +struct __packed MXL_EAGLE_HOST_MSG_HEADER_T { + u8 opcode; + u8 seqnum; + u8 payload_size; + u8 status; + u32 checksum; +}; + +/* Device version information struct */ +struct __packed MXL_EAGLE_DEV_VER_T { + u8 chip_id; + u8 firmware_ver[MXL_EAGLE_VERSION_SIZE]; + u8 mxlware_ver[MXL_EAGLE_VERSION_SIZE]; +}; + +/* Xtal configuration struct */ +struct __packed MXL_EAGLE_DEV_XTAL_T { + u8 xtal_cap; /* accepted range is 1..31 pF. Default is 26 */ + u8 clk_out_enable; + u8 clk_out_div_enable; /* clock out freq is xtal freq / 6 */ + u8 xtal_sharing_enable; /* if enabled set xtal_cap to 25 pF */ + u8 xtal_calibration_enable; /* enable for master, disable for slave */ +}; + +/* GPIO direction struct, internally used in GPIO configuration API */ +struct __packed MXL_EAGLE_DEV_GPIO_DIRECTION_T { + u8 gpio_number; + u8 gpio_direction; +}; + +/* GPO level struct, internally used in GPIO configuration API */ +struct __packed MXL_EAGLE_DEV_GPO_LEVEL_T { + u8 gpio_number; + u8 gpo_level; +}; + +/* Device Status struct */ +struct MXL_EAGLE_DEV_STATUS_T { + u8 temperature; + u8 demod_type; + u8 power_mode; + u8 cpu_utilization_percent; +}; + +/* Device interrupt configuration struct */ +struct __packed MXL_EAGLE_DEV_INTR_CFG_T { + u32 intr_mask; + u8 edge_trigger; + u8 positive_trigger; + u8 global_enable_interrupt; +}; + +/* MPEG pad drive parameters, used on MPEG output configuration */ +/* See MXL_EAGLE_IO_MUX_DRIVE_MODE_E */ +struct MXL_EAGLE_MPEG_PAD_DRIVE_T { + u8 pad_drv_mpeg_syn; + u8 pad_drv_mpeg_dat; + u8 pad_drv_mpeg_val; + u8 pad_drv_mpeg_clk; +}; + +/* MPEGOUT parameter struct, used in MPEG output configuration */ +struct MXL_EAGLE_MPEGOUT_PARAMS_T { + u8 mpeg_parallel; + u8 msb_first; + u8 mpeg_sync_pulse_width; /* See MXL_EAGLE_MPEG_DATA_FORMAT_E */ + u8 mpeg_valid_pol; + u8 mpeg_sync_pol; + u8 mpeg_clk_pol; + u8 mpeg3wire_mode_enable; + u8 mpeg_clk_freq; + struct MXL_EAGLE_MPEG_PAD_DRIVE_T mpeg_pad_drv; +}; + +/* QAM Demodulator parameters struct, used in QAM params configuration */ +struct __packed MXL_EAGLE_QAM_DEMOD_PARAMS_T { + u8 annex_type; + u8 qam_type; + u8 iq_flip; + u8 search_range_idx; + u8 spur_canceller_enable; + u32 symbol_rate_hz; + u32 symbol_rate_256qam_hz; +}; + +/* QAM Demodulator status */ +struct MXL_EAGLE_QAM_DEMOD_STATUS_T { + u8 annex_type; + u8 qam_type; + u8 iq_flip; + u8 interleaver_depth_i; + u8 interleaver_depth_j; + u8 qam_locked; + u8 fec_locked; + u8 mpeg_locked; + u16 snr_db_tenths; + s16 timing_offset; + s32 carrier_offset_hz; +}; + +/* QAM Demodulator error counters */ +struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T { + u32 corrected_code_words; + u32 uncorrected_code_words; + u32 total_code_words_received; + u32 corrected_bits; + u32 error_mpeg_frames; + u32 mpeg_frames_received; + u32 erasures; +}; + +/* QAM Demodulator constellation point */ +struct MXL_EAGLE_QAM_DEMOD_CONSTELLATION_VAL_T { + s16 i_value[12]; + s16 q_value[12]; +}; + +/* QAM Demodulator equalizer filter taps */ +struct MXL_EAGLE_QAM_DEMOD_EQU_FILTER_T { + s16 ffe_taps[MXL_EAGLE_QAM_FFE_TAPS_LENGTH]; + s16 spur_taps[MXL_EAGLE_QAM_SPUR_TAPS_LENGTH]; + s16 dfe_taps[MXL_EAGLE_QAM_DFE_TAPS_LENGTH]; + u8 ffe_leading_tap_index; + u8 dfe_taps_number; +}; + +/* OOB Demodulator parameters struct, used in OOB params configuration */ +struct __packed MXL_EAGLE_OOB_DEMOD_PARAMS_T { + u8 symbol_rate; + u8 iq_flip; + u8 clk_pol; +}; + +/* OOB Demodulator error counters */ +struct MXL_EAGLE_OOB_DEMOD_ERROR_COUNTERS_T { + u32 corrected_packets; + u32 uncorrected_packets; + u32 total_packets_received; +}; + +/* OOB status */ +struct __packed MXL_EAGLE_OOB_DEMOD_STATUS_T { + u16 snr_db_tenths; + s16 timing_offset; + s32 carrier_offsetHz; + u8 qam_locked; + u8 fec_locked; + u8 mpeg_locked; + u8 retune_required; + u8 iq_flip; +}; + +/* ATSC Demodulator status */ +struct __packed MXL_EAGLE_ATSC_DEMOD_STATUS_T { + s16 snr_db_tenths; + s16 timing_offset; + s32 carrier_offset_hz; + u8 frame_lock; + u8 atsc_lock; + u8 fec_lock; +}; + +/* ATSC Demodulator error counters */ +struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T { + u32 error_packets; + u32 total_packets; + u32 error_bytes; +}; + +/* ATSC Demodulator equalizers filter taps */ +struct __packed MXL_EAGLE_ATSC_DEMOD_EQU_FILTER_T { + s16 ffe_taps[MXL_EAGLE_ATSC_FFE_TAPS_LENGTH]; + s8 dfe_taps[MXL_EAGLE_ATSC_DFE_TAPS_LENGTH]; +}; + +/* Tuner AGC Status */ +struct __packed MXL_EAGLE_TUNER_AGC_STATUS_T { + u8 locked; + u16 raw_agc_gain; /* AGC gain [dB] = rawAgcGain / 2^6 */ + s16 rx_power_db_hundredths; +}; + +/* Tuner channel tune parameters */ +struct __packed MXL_EAGLE_TUNER_CHANNEL_PARAMS_T { + u32 freq_hz; + u8 tune_mode; + u8 bandwidth; +}; + +/* Tuner channel lock indications */ +struct __packed MXL_EAGLE_TUNER_LOCK_STATUS_T { + u8 rf_pll_locked; + u8 ref_pll_locked; +}; + +/* Smart antenna parameters used in Smart antenna params configuration */ +struct __packed MXL_EAGLE_SMA_PARAMS_T { + u8 full_duplex_enable; + u8 rx_disable; + u8 idle_logic_high; +}; + +/* Smart antenna message format */ +struct __packed MXL_EAGLE_SMA_MESSAGE_T { + u32 payload_bits; + u8 total_num_bits; +}; + -- 2.28.0 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 2/4] em28xx-core: Fix TS2 active led 2021-01-26 1:54 ` [PATCH 0/4] MaxLinear mxl692 demod-tuner / Hauppauge usb QuadHD Brad Love 2021-01-26 1:54 ` [PATCH 1/4] mxl692: MaxLinear 692 ATSC demod/tuner driver Brad Love @ 2021-01-26 1:54 ` Brad Love 2021-01-26 1:54 ` [PATCH 3/4] em28xx-core: Fix i2c error debug Brad Love 2021-01-26 1:54 ` [PATCH 4/4] em28xx: Add support for Hauppauge USB QuadHD Brad Love 3 siblings, 0 replies; 20+ messages in thread From: Brad Love @ 2021-01-26 1:54 UTC (permalink / raw) To: linux-media, sean; +Cc: Brad Love TS2 active led is completely igored, fix that. Signed-off-by: Brad Love <brad@nextdimension.cc> --- Since v1: - None drivers/media/usb/em28xx/em28xx-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 3daa64bb1e1d..584fa400cd7d 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -698,8 +698,10 @@ int em28xx_capture_start(struct em28xx *dev, int start) if (dev->mode == EM28XX_ANALOG_MODE) led = em28xx_find_led(dev, EM28XX_LED_ANALOG_CAPTURING); - else + else if (dev->ts == PRIMARY_TS) led = em28xx_find_led(dev, EM28XX_LED_DIGITAL_CAPTURING); + else + led = em28xx_find_led(dev, EM28XX_LED_DIGITAL_CAPTURING_TS2); if (led) em28xx_write_reg_bits(dev, led->gpio_reg, -- 2.28.0 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 3/4] em28xx-core: Fix i2c error debug 2021-01-26 1:54 ` [PATCH 0/4] MaxLinear mxl692 demod-tuner / Hauppauge usb QuadHD Brad Love 2021-01-26 1:54 ` [PATCH 1/4] mxl692: MaxLinear 692 ATSC demod/tuner driver Brad Love 2021-01-26 1:54 ` [PATCH 2/4] em28xx-core: Fix TS2 active led Brad Love @ 2021-01-26 1:54 ` Brad Love 2021-01-26 1:54 ` [PATCH 4/4] em28xx: Add support for Hauppauge USB QuadHD Brad Love 3 siblings, 0 replies; 20+ messages in thread From: Brad Love @ 2021-01-26 1:54 UTC (permalink / raw) To: linux-media, sean; +Cc: Brad Love Read errors are currently reported as write errors. An incorrectly received read operation is never reported at all. Add a debug statement indicating the request mismatch. Signed-off-by: Brad Love <brad@nextdimension.cc> --- Since v1: - Was not in v1 drivers/media/usb/em28xx/em28xx-i2c.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 592b98b3643a..255395959255 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -294,6 +294,10 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) "reading from i2c device at 0x%x failed (error=%i)\n", addr, ret); return ret; + } else if (ret != len) { + dev_dbg(&dev->intf->dev, + "%i bytes read from i2c device at 0x%x requested, but %i bytes written\n", + ret, addr, len); } /* * NOTE: some devices with two i2c buses have the bad habit to return 0 @@ -329,7 +333,7 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) } dev_warn(&dev->intf->dev, - "write to i2c device at 0x%x failed with unknown error (status=%i)\n", + "read from i2c device at 0x%x failed with unknown error (status=%i)\n", addr, ret); return -EIO; } -- 2.28.0 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 4/4] em28xx: Add support for Hauppauge USB QuadHD 2021-01-26 1:54 ` [PATCH 0/4] MaxLinear mxl692 demod-tuner / Hauppauge usb QuadHD Brad Love ` (2 preceding siblings ...) 2021-01-26 1:54 ` [PATCH 3/4] em28xx-core: Fix i2c error debug Brad Love @ 2021-01-26 1:54 ` Brad Love 3 siblings, 0 replies; 20+ messages in thread From: Brad Love @ 2021-01-26 1:54 UTC (permalink / raw) To: linux-media, sean; +Cc: Brad Love Hauppauge USB QuadHD contains two two-tuner em28xx devices behind a usb hub. Each of the four dvb adapters contains a MaxLinear 692 combo ATSC demod/tuner. Bus 003 Device 006: ID 2040:846d Hauppauge Bus 003 Device 005: ID 2040:846d Hauppauge Bus 003 Device 004: ID 04e2:0404 Exar Corp. Signed-off-by: Brad Love <brad@nextdimension.cc> --- Since v1: - Bump board number drivers/media/usb/em28xx/Kconfig | 1 + drivers/media/usb/em28xx/em28xx-cards.c | 46 +++++++++++++++++++++++++ drivers/media/usb/em28xx/em28xx-dvb.c | 26 ++++++++++++++ drivers/media/usb/em28xx/em28xx.h | 1 + 4 files changed, 74 insertions(+) diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index f2031a933e54..2237eb73f010 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -67,6 +67,7 @@ config VIDEO_EM28XX_DVB select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MXL692 if MEDIA_SUBDRV_AUTOSELECT help This adds support for DVB cards based on the Empiatech em28xx chips. diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 5144888ae36f..d6c8ae213914 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -549,6 +549,21 @@ static const struct em28xx_reg_seq hauppauge_dualhd_dvb[] = { {-1, -1, -1, -1}, }; +/* Hauppauge USB QuadHD */ +static struct em28xx_reg_seq hauppauge_usb_quadhd_atsc_reg_seq[] = { + {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0}, + {0x0d, 0xff, 0xff, 200}, + {0x50, 0x04, 0xff, 300}, + {EM2874_R80_GPIO_P0_CTRL, 0xb0, 0xf0, 100}, /* demod 1 reset */ + {EM2874_R80_GPIO_P0_CTRL, 0xf0, 0xf0, 100}, + {EM2874_R80_GPIO_P0_CTRL, 0xd0, 0xf0, 100}, /* demod 2 reset */ + {EM2874_R80_GPIO_P0_CTRL, 0xf0, 0xf0, 100}, + {EM2874_R5F_TS_ENABLE, 0x44, 0xff, 50}, + {EM2874_R5D_TS1_PKT_SIZE, 0x05, 0xff, 50}, + {EM2874_R5E_TS2_PKT_SIZE, 0x05, 0xff, 50}, + {-1, -1, -1, -1}, +}; + /* * Button definitions */ @@ -644,6 +659,22 @@ static struct em28xx_led hauppauge_dualhd_leds[] = { {-1, 0, 0, 0}, }; +static struct em28xx_led hauppauge_usb_quadhd_leds[] = { + { + .role = EM28XX_LED_DIGITAL_CAPTURING, + .gpio_reg = EM2874_R80_GPIO_P0_CTRL, + .gpio_mask = EM_GPIO_2, + .inverted = 1, + }, + { + .role = EM28XX_LED_DIGITAL_CAPTURING_TS2, + .gpio_reg = EM2874_R80_GPIO_P0_CTRL, + .gpio_mask = EM_GPIO_0, + .inverted = 1, + }, + {-1, 0, 0, 0}, +}; + /* * Board definitions */ @@ -2539,6 +2570,19 @@ const struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + /* 2040:826d Hauppauge USB QuadHD + * Empia 28274, Max Linear 692 ATSC combo demod/tuner + */ + [EM2874_BOARD_HAUPPAUGE_USB_QUADHD] = { + .name = "Hauppauge USB QuadHD ATSC", + .def_i2c_bus = 1, + .has_dual_ts = 1, + .has_dvb = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ, + .tuner_type = TUNER_ABSENT, + .tuner_gpio = hauppauge_usb_quadhd_atsc_reg_seq, + .leds = hauppauge_usb_quadhd_leds, + }, }; EXPORT_SYMBOL_GPL(em28xx_boards); @@ -2672,6 +2716,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, { USB_DEVICE(0x2040, 0x826d), .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, + { USB_DEVICE(0x2040, 0x846d), + .driver_info = EM2874_BOARD_HAUPPAUGE_USB_QUADHD }, { USB_DEVICE(0x0438, 0xb002), .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 }, { USB_DEVICE(0x2001, 0xf112), diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index fb9cbfa81a84..526424279637 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -62,6 +62,7 @@ #include "si2157.h" #include "tc90522.h" #include "qm1d1c0042.h" +#include "mxl692.h" MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>"); MODULE_LICENSE("GPL v2"); @@ -1459,6 +1460,26 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_01595(struct em28xx *dev) return 0; } +static int em2874_dvb_init_hauppauge_usb_quadhd(struct em28xx *dev) +{ + struct em28xx_dvb *dvb = dev->dvb; + struct mxl692_config mxl692_config = {}; + unsigned char addr; + + /* attach demod/tuner combo */ + mxl692_config.id = (dev->ts == PRIMARY_TS) ? 0 : 1; + mxl692_config.fe = &dvb->fe[0]; + addr = (dev->ts == PRIMARY_TS) ? 0x60 : 0x63; + + dvb->i2c_client_demod = dvb_module_probe("mxl692", NULL, + &dev->i2c_adap[dev->def_i2c_bus], + addr, &mxl692_config); + if (!dvb->i2c_client_demod) + return -ENODEV; + + return 0; +} + static int em28xx_dvb_init(struct em28xx *dev) { int result = 0, dvb_alt = 0; @@ -1945,6 +1966,11 @@ static int em28xx_dvb_init(struct em28xx *dev) if (result) goto out_free; break; + case EM2874_BOARD_HAUPPAUGE_USB_QUADHD: + result = em2874_dvb_init_hauppauge_usb_quadhd(dev); + if (result) + goto out_free; + break; default: dev_err(&dev->intf->dev, "The frontend of your DVB/ATSC card isn't supported yet\n"); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 55a46faaf7b7..6648e11f1271 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -152,6 +152,7 @@ #define EM2861_BOARD_MAGIX_VIDEOWANDLER2 103 #define EM28178_BOARD_PCTV_461E_V2 104 #define EM2860_BOARD_MYGICA_IGRABBER 105 +#define EM2874_BOARD_HAUPPAUGE_USB_QUADHD 106 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 -- 2.28.0 ^ permalink raw reply related [flat|nested] 20+ messages in thread
end of thread, other threads:[~2021-01-27 12:26 UTC | newest] Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2020-06-12 18:39 [PATCH 0/3] MaxLinear mxl692 demod-tuner / Hauppauge usb QuadHD Brad Love 2020-06-12 18:39 ` [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver Brad Love 2020-06-12 21:49 ` kernel test robot 2020-06-12 21:49 ` kernel test robot 2020-06-13 8:49 ` kernel test robot 2020-06-13 8:49 ` kernel test robot 2020-06-13 21:07 ` kernel test robot 2020-06-13 21:07 ` kernel test robot 2020-06-13 21:07 ` [PATCH] mxl692: fix platform_no_drv_owner.cocci warnings kernel test robot 2020-06-13 21:07 ` kernel test robot 2020-06-24 9:59 ` [PATCH 1/3] mxl692: MaxLinear 692 ATSC demod-tuner driver Sean Young 2020-06-26 17:41 ` Bradford Love 2020-06-29 17:33 ` Sean Young 2020-06-12 18:39 ` [PATCH 2/3] em28xx-core: Fix TS2 active led Brad Love 2020-06-12 18:39 ` [PATCH 3/3] em28xx: Add support for Hauppauge USB QuadHD Brad Love 2021-01-26 1:54 ` [PATCH 0/4] MaxLinear mxl692 demod-tuner / Hauppauge usb QuadHD Brad Love 2021-01-26 1:54 ` [PATCH 1/4] mxl692: MaxLinear 692 ATSC demod/tuner driver Brad Love 2021-01-26 1:54 ` [PATCH 2/4] em28xx-core: Fix TS2 active led Brad Love 2021-01-26 1:54 ` [PATCH 3/4] em28xx-core: Fix i2c error debug Brad Love 2021-01-26 1:54 ` [PATCH 4/4] em28xx: Add support for Hauppauge USB QuadHD Brad Love
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.