All of lore.kernel.org
 help / color / mirror / Atom feed
* [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 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	/* configure digital regulator to high current mode */
+	status = mxl692_memread(dev, 0x90000018, (u8 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	reg_val |= 0x800;
+
+	status = mxl692_memwrite(dev, 0x90000018, (u8 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	/* enable/disable XTAL calibration, based on master/slave device */
+	status = mxl692_memread(dev, 0x90000030, (u8 *)&reg_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 *)&reg_val1, sizeof(u32));
+		if (status)
+			goto err_finish;
+	} else {
+		/* disable XTAL calibration */
+		reg_val1 |= 0x2;
+
+		status = mxl692_memwrite(dev, 0x90000030, (u8 *)&reg_val1, sizeof(u32));
+		if (status)
+			goto err_finish;
+
+		/* set XTAL bias value */
+		status = mxl692_memread(dev, 0x9000002c, (u8 *)&reg_val, sizeof(u32));
+		if (status)
+			goto err_finish;
+
+		reg_val &= 0xC0FFFFFF;
+		reg_val |= 0xA000000;
+
+		status = mxl692_memwrite(dev, 0x9000002c, (u8 *)&reg_val, sizeof(u32));
+		if (status)
+			goto err_finish;
+	}
+
+	/* start XTAL calibration */
+	status = mxl692_memread(dev, 0x70000010, (u8 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	reg_val |= 0x8;
+
+	status = mxl692_memwrite(dev, 0x70000010, (u8 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	status = mxl692_memread(dev, 0x70000018, (u8 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	reg_val |= 0x10;
+
+	status = mxl692_memwrite(dev, 0x70000018, (u8 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	status = mxl692_memread(dev, 0x9001014c, (u8 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	reg_val &= 0xFFFFEFFF;
+
+	status = mxl692_memwrite(dev, 0x9001014c, (u8 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	reg_val |= 0x1000;
+
+	status = mxl692_memwrite(dev, 0x9001014c, (u8 *)&reg_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

* [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

* 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 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_val, sizeof(u32));
> +	if (status)
> +		goto err_finish;
> +
> +	/* configure digital regulator to high current mode */
> +	status = mxl692_memread(dev, 0x90000018, (u8 *)&reg_val, sizeof(u32));
> +	if (status)
> +		goto err_finish;
> +
> +	reg_val |= 0x800;
> +
> +	status = mxl692_memwrite(dev, 0x90000018, (u8 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_val, sizeof(u32));
> +	if (status)
> +		goto err_finish;
> +
> +	/* enable/disable XTAL calibration, based on master/slave device */
> +	status = mxl692_memread(dev, 0x90000030, (u8 *)&reg_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 *)&reg_val1, sizeof(u32));
> +		if (status)
> +			goto err_finish;
> +	} else {
> +		/* disable XTAL calibration */
> +		reg_val1 |= 0x2;
> +
> +		status = mxl692_memwrite(dev, 0x90000030, (u8 *)&reg_val1, sizeof(u32));
> +		if (status)
> +			goto err_finish;
> +
> +		/* set XTAL bias value */
> +		status = mxl692_memread(dev, 0x9000002c, (u8 *)&reg_val, sizeof(u32));
> +		if (status)
> +			goto err_finish;
> +
> +		reg_val &= 0xC0FFFFFF;
> +		reg_val |= 0xA000000;
> +
> +		status = mxl692_memwrite(dev, 0x9000002c, (u8 *)&reg_val, sizeof(u32));
> +		if (status)
> +			goto err_finish;
> +	}
> +
> +	/* start XTAL calibration */
> +	status = mxl692_memread(dev, 0x70000010, (u8 *)&reg_val, sizeof(u32));
> +	if (status)
> +		goto err_finish;
> +
> +	reg_val |= 0x8;
> +
> +	status = mxl692_memwrite(dev, 0x70000010, (u8 *)&reg_val, sizeof(u32));
> +	if (status)
> +		goto err_finish;
> +
> +	status = mxl692_memread(dev, 0x70000018, (u8 *)&reg_val, sizeof(u32));
> +	if (status)
> +		goto err_finish;
> +
> +	reg_val |= 0x10;
> +
> +	status = mxl692_memwrite(dev, 0x70000018, (u8 *)&reg_val, sizeof(u32));
> +	if (status)
> +		goto err_finish;
> +
> +	status = mxl692_memread(dev, 0x9001014c, (u8 *)&reg_val, sizeof(u32));
> +	if (status)
> +		goto err_finish;
> +
> +	reg_val &= 0xFFFFEFFF;
> +
> +	status = mxl692_memwrite(dev, 0x9001014c, (u8 *)&reg_val, sizeof(u32));
> +	if (status)
> +		goto err_finish;
> +
> +	reg_val |= 0x1000;
> +
> +	status = mxl692_memwrite(dev, 0x9001014c, (u8 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_val, sizeof(u32));
> > +     if (status)
> > +             goto err_finish;
> > +
> > +     /* configure digital regulator to high current mode */
> > +     status = mxl692_memread(dev, 0x90000018, (u8 *)&reg_val, sizeof(u32));
> > +     if (status)
> > +             goto err_finish;
> > +
> > +     reg_val |= 0x800;
> > +
> > +     status = mxl692_memwrite(dev, 0x90000018, (u8 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_val, sizeof(u32));
> > +     if (status)
> > +             goto err_finish;
> > +
> > +     /* enable/disable XTAL calibration, based on master/slave device */
> > +     status = mxl692_memread(dev, 0x90000030, (u8 *)&reg_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 *)&reg_val1, sizeof(u32));
> > +             if (status)
> > +                     goto err_finish;
> > +     } else {
> > +             /* disable XTAL calibration */
> > +             reg_val1 |= 0x2;
> > +
> > +             status = mxl692_memwrite(dev, 0x90000030, (u8 *)&reg_val1, sizeof(u32));
> > +             if (status)
> > +                     goto err_finish;
> > +
> > +             /* set XTAL bias value */
> > +             status = mxl692_memread(dev, 0x9000002c, (u8 *)&reg_val, sizeof(u32));
> > +             if (status)
> > +                     goto err_finish;
> > +
> > +             reg_val &= 0xC0FFFFFF;
> > +             reg_val |= 0xA000000;
> > +
> > +             status = mxl692_memwrite(dev, 0x9000002c, (u8 *)&reg_val, sizeof(u32));
> > +             if (status)
> > +                     goto err_finish;
> > +     }
> > +
> > +     /* start XTAL calibration */
> > +     status = mxl692_memread(dev, 0x70000010, (u8 *)&reg_val, sizeof(u32));
> > +     if (status)
> > +             goto err_finish;
> > +
> > +     reg_val |= 0x8;
> > +
> > +     status = mxl692_memwrite(dev, 0x70000010, (u8 *)&reg_val, sizeof(u32));
> > +     if (status)
> > +             goto err_finish;
> > +
> > +     status = mxl692_memread(dev, 0x70000018, (u8 *)&reg_val, sizeof(u32));
> > +     if (status)
> > +             goto err_finish;
> > +
> > +     reg_val |= 0x10;
> > +
> > +     status = mxl692_memwrite(dev, 0x70000018, (u8 *)&reg_val, sizeof(u32));
> > +     if (status)
> > +             goto err_finish;
> > +
> > +     status = mxl692_memread(dev, 0x9001014c, (u8 *)&reg_val, sizeof(u32));
> > +     if (status)
> > +             goto err_finish;
> > +
> > +     reg_val &= 0xFFFFEFFF;
> > +
> > +     status = mxl692_memwrite(dev, 0x9001014c, (u8 *)&reg_val, sizeof(u32));
> > +     if (status)
> > +             goto err_finish;
> > +
> > +     reg_val |= 0x1000;
> > +
> > +     status = mxl692_memwrite(dev, 0x9001014c, (u8 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_val, sizeof(u32));
> > > +     if (status)
> > > +             goto err_finish;
> > > +
> > > +     /* configure digital regulator to high current mode */
> > > +     status = mxl692_memread(dev, 0x90000018, (u8 *)&reg_val, sizeof(u32));
> > > +     if (status)
> > > +             goto err_finish;
> > > +
> > > +     reg_val |= 0x800;
> > > +
> > > +     status = mxl692_memwrite(dev, 0x90000018, (u8 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_val, sizeof(u32));
> > > +     if (status)
> > > +             goto err_finish;
> > > +
> > > +     /* enable/disable XTAL calibration, based on master/slave device */
> > > +     status = mxl692_memread(dev, 0x90000030, (u8 *)&reg_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 *)&reg_val1, sizeof(u32));
> > > +             if (status)
> > > +                     goto err_finish;
> > > +     } else {
> > > +             /* disable XTAL calibration */
> > > +             reg_val1 |= 0x2;
> > > +
> > > +             status = mxl692_memwrite(dev, 0x90000030, (u8 *)&reg_val1, sizeof(u32));
> > > +             if (status)
> > > +                     goto err_finish;
> > > +
> > > +             /* set XTAL bias value */
> > > +             status = mxl692_memread(dev, 0x9000002c, (u8 *)&reg_val, sizeof(u32));
> > > +             if (status)
> > > +                     goto err_finish;
> > > +
> > > +             reg_val &= 0xC0FFFFFF;
> > > +             reg_val |= 0xA000000;
> > > +
> > > +             status = mxl692_memwrite(dev, 0x9000002c, (u8 *)&reg_val, sizeof(u32));
> > > +             if (status)
> > > +                     goto err_finish;
> > > +     }
> > > +
> > > +     /* start XTAL calibration */
> > > +     status = mxl692_memread(dev, 0x70000010, (u8 *)&reg_val, sizeof(u32));
> > > +     if (status)
> > > +             goto err_finish;
> > > +
> > > +     reg_val |= 0x8;
> > > +
> > > +     status = mxl692_memwrite(dev, 0x70000010, (u8 *)&reg_val, sizeof(u32));
> > > +     if (status)
> > > +             goto err_finish;
> > > +
> > > +     status = mxl692_memread(dev, 0x70000018, (u8 *)&reg_val, sizeof(u32));
> > > +     if (status)
> > > +             goto err_finish;
> > > +
> > > +     reg_val |= 0x10;
> > > +
> > > +     status = mxl692_memwrite(dev, 0x70000018, (u8 *)&reg_val, sizeof(u32));
> > > +     if (status)
> > > +             goto err_finish;
> > > +
> > > +     status = mxl692_memread(dev, 0x9001014c, (u8 *)&reg_val, sizeof(u32));
> > > +     if (status)
> > > +             goto err_finish;
> > > +
> > > +     reg_val &= 0xFFFFEFFF;
> > > +
> > > +     status = mxl692_memwrite(dev, 0x9001014c, (u8 *)&reg_val, sizeof(u32));
> > > +     if (status)
> > > +             goto err_finish;
> > > +
> > > +     reg_val |= 0x1000;
> > > +
> > > +     status = mxl692_memwrite(dev, 0x9001014c, (u8 *)&reg_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 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 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	/* configure digital regulator to high current mode */
+	status = mxl692_memread(dev, 0x90000018, (u8 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	reg_val |= 0x800;
+
+	status = mxl692_memwrite(dev, 0x90000018, (u8 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_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 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	/* enable/disable XTAL calibration, based on master/slave device */
+	status = mxl692_memread(dev, 0x90000030, (u8 *)&reg_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 *)&reg_val1, sizeof(u32));
+		if (status)
+			goto err_finish;
+	} else {
+		/* disable XTAL calibration */
+		reg_val1 |= 0x2;
+
+		status = mxl692_memwrite(dev, 0x90000030, (u8 *)&reg_val1, sizeof(u32));
+		if (status)
+			goto err_finish;
+
+		/* set XTAL bias value */
+		status = mxl692_memread(dev, 0x9000002c, (u8 *)&reg_val, sizeof(u32));
+		if (status)
+			goto err_finish;
+
+		reg_val &= 0xC0FFFFFF;
+		reg_val |= 0xA000000;
+
+		status = mxl692_memwrite(dev, 0x9000002c, (u8 *)&reg_val, sizeof(u32));
+		if (status)
+			goto err_finish;
+	}
+
+	/* start XTAL calibration */
+	status = mxl692_memread(dev, 0x70000010, (u8 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	reg_val |= 0x8;
+
+	status = mxl692_memwrite(dev, 0x70000010, (u8 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	status = mxl692_memread(dev, 0x70000018, (u8 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	reg_val |= 0x10;
+
+	status = mxl692_memwrite(dev, 0x70000018, (u8 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	status = mxl692_memread(dev, 0x9001014c, (u8 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	reg_val &= 0xFFFFEFFF;
+
+	status = mxl692_memwrite(dev, 0x9001014c, (u8 *)&reg_val, sizeof(u32));
+	if (status)
+		goto err_finish;
+
+	reg_val |= 0x1000;
+
+	status = mxl692_memwrite(dev, 0x9001014c, (u8 *)&reg_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.