linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
@ 2017-04-14  1:50 Yasunari.Takiguchi
  2017-04-14  2:00 ` [PATCH v2 01/15] [dt-bindings] [media] Add document file for CXD2880 SPI I/F Yasunari.Takiguchi
                   ` (15 more replies)
  0 siblings, 16 replies; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  1:50 UTC (permalink / raw)
  To: akpm, linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

Hi,

This is the patch series (version 2) of Sony CXD2880 DVB-T2/T tuner + demodulator driver.
The driver supports DVB-API and interfaces through SPI.

We have tested the driver on Raspberry Pi 3 and got picture and sound from a media player.

Thanks,
Takiguchi
---
 Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt        |   14 ++++++++++++++
 drivers/media/spi/cxd2880-spi.c                                     | 728 ++++++++++++++++++++++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880.h                       |   46 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_common.c                |   84 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_common.h                |   86 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_io.c                    |   68 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_io.h                    |   62 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h                |   35 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c        |   71 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_math.c                  |   89 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_math.h                  |   40 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c             |  147 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h             |   40 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h                   |   51 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c            |  130 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h            |   45 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h                   |   50 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c                | 3925 ++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h                |  395 ++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h |   29 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c            |  207 ++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h            |   52 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c                 |   99 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h                 |   44 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c                   | 1550 ++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h                  |   91 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c           | 1072 +++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h           |   62 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c            |  197 ++
 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h            |   58 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c       | 1190 +++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h       |  106 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h                 |  402 ++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c          | 1309 ++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h          |   82 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c           |  311 +++
 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h           |   64 +
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c      | 2523 ++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h      |  170 ++
 drivers/media/dvb-frontends/Makefile                                |    1 +
 drivers/media/dvb-frontends/cxd2880/Makefile                        |   21 +++++++++++++++++++++
 drivers/media/spi/Makefile                                          |    5 +++++
 drivers/media/dvb-frontends/Kconfig                                 |    2 ++
 drivers/media/dvb-frontends/cxd2880/Kconfig                         |    6 ++++++
 drivers/media/spi/Kconfig                                           |   14 ++++++++++++++
 MAINTAINERS                                                         |    9 +++++++++

 46 files changed, 15782 insertions(+)

 create mode 100644 Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt
 create mode 100644 drivers/media/spi/cxd2880-spi.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_math.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/Makefile
 create mode 100644 drivers/media/dvb-frontends/cxd2880/Kconfig
-- 
2.11.0

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH v2 01/15] [dt-bindings] [media] Add document file for CXD2880 SPI I/F
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
@ 2017-04-14  2:00 ` Yasunari.Takiguchi
  2017-04-17  4:58   ` Takiguchi, Yasunari
  2017-04-14  2:08 ` [PATCH v2 02/15] [media] cxd2880-spi: Add support for CXD2008 SPI interface Yasunari.Takiguchi
                   ` (14 subsequent siblings)
  15 siblings, 1 reply; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  2:00 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

This is the document file for Sony CXD2880 DVB-T2/T tuner + demodulator.
It contains the description of the SPI adapter binding.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
 .../devicetree/bindings/media/spi/sony-cxd2880.txt         | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt

diff --git a/Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt b/Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt
new file mode 100644
index 000000000000..fc5aa263abe5
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt
@@ -0,0 +1,14 @@
+Sony CXD2880 DVB-T2/T tuner + demodulator driver SPI adapter
+
+Required properties:
+- compatible: Should be "sony,cxd2880".
+- reg: SPI chip select number for the device.
+- spi-max-frequency: Maximum bus speed, should be set to <55000000> (55MHz).
+
+Example:
+
+cxd2880@0 {
+	compatible = "sony,cxd2880";
+	reg = <0>; /* CE0 */
+	spi-max-frequency = <55000000>; /* 55MHz */
+};
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 02/15] [media] cxd2880-spi: Add support for CXD2008 SPI interface
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
  2017-04-14  2:00 ` [PATCH v2 01/15] [dt-bindings] [media] Add document file for CXD2880 SPI I/F Yasunari.Takiguchi
@ 2017-04-14  2:08 ` Yasunari.Takiguchi
  2017-06-13 11:55   ` Mauro Carvalho Chehab
  2017-04-14  2:17 ` [PATCH v2 03/15] [media] cxd2880: Add common files for the driver Yasunari.Takiguchi
                   ` (13 subsequent siblings)
  15 siblings, 1 reply; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  2:08 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

This is the SPI adapter part of the driver for the
Sony CXD2880 DVB-T2/T tuner + demodulator.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
 drivers/media/spi/cxd2880-spi.c | 728 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 728 insertions(+)
 create mode 100644 drivers/media/spi/cxd2880-spi.c

diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c
new file mode 100644
index 000000000000..82e122349055
--- /dev/null
+++ b/drivers/media/spi/cxd2880-spi.c
@@ -0,0 +1,728 @@
+/*
+ * cxd2880-spi.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI adapter
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/spi/spi.h>
+
+#include "dvb_demux.h"
+#include "dmxdev.h"
+#include "dvb_frontend.h"
+#include "cxd2880.h"
+
+#define CXD2880_MAX_FILTER_SIZE 32
+#define BURST_WRITE_MAX 128
+#define MAX_TRANS_PACKET 300
+
+struct cxd2880_ts_buf_info {
+	u8 read_ready;
+	u8 almost_full;
+	u8 almost_empty;
+	u8 overflow;
+	u8 underflow;
+	u16 packet_num;
+};
+
+struct cxd2880_pid_config {
+	u8 is_enable;
+	u16 pid;
+};
+
+struct cxd2880_pid_filter_config {
+	u8 is_negative;
+	struct cxd2880_pid_config pid_config[CXD2880_MAX_FILTER_SIZE];
+};
+
+struct cxd2880_dvb_spi {
+	struct dvb_frontend dvb_fe;
+	struct dvb_adapter adapter;
+	struct dvb_demux demux;
+	struct dmxdev dmxdev;
+	struct dmx_frontend dmx_fe;
+	struct task_struct *cxd2880_ts_read_thread;
+	struct spi_device *spi;
+	struct mutex spi_mutex; /* For SPI access exclusive control */
+	int feed_count;
+	int all_pid_feed_count;
+	u8 *ts_buf;
+	struct cxd2880_pid_filter_config filter_config;
+};
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int cxd2880_write_spi(struct spi_device *spi, u8 *data, u32 size)
+{
+	struct spi_message msg;
+	struct spi_transfer tx;
+	int ret = 0;
+
+	if ((!spi) || (!data)) {
+		pr_err("%s: invalid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	memset(&tx, 0, sizeof(tx));
+	tx.tx_buf = data;
+	tx.len = size;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&tx, &msg);
+	ret = spi_sync(spi, &msg);
+
+	return ret;
+}
+
+static int cxd2880_write_reg(struct spi_device *spi,
+				u8 subAddress, const u8 *data, u32 size)
+{
+	u8 send_data[BURST_WRITE_MAX + 4];
+	const u8 *write_data_top = NULL;
+	int ret = 0;
+
+	if ((!spi) || (!data)) {
+		pr_err("%s: invalid arg\n", __func__);
+		return -EINVAL;
+	}
+	if (size > BURST_WRITE_MAX) {
+		pr_err("%s: data size > WRITE_MAX\n", __func__);
+		return -EINVAL;
+	}
+
+	if (subAddress + size > 0x100) {
+		pr_err("%s: out of range\n", __func__);
+		return -EINVAL;
+	}
+
+	send_data[0] = 0x0E;
+	write_data_top = data;
+
+	while (size > 0) {
+		send_data[1] = subAddress;
+		if (size > 255)
+			send_data[2] = 255;
+		else
+			send_data[2] = (u8)size;
+
+		memcpy(&send_data[3], write_data_top, send_data[2]);
+
+		ret = cxd2880_write_spi(spi, send_data, send_data[2] + 3);
+		if (ret) {
+			dev_err(&spi->dev, "%s: write spi failed %d\n",
+				__func__, ret);
+			break;
+		}
+		subAddress += send_data[2];
+		write_data_top += send_data[2];
+		size -= send_data[2];
+	}
+
+	return ret;
+}
+
+static int cxd2880_spi_read_ts(struct spi_device *spi,
+					u8 *read_data,
+					u32 packet_num)
+{
+	int ret = 0;
+	u8 data[3];
+	struct spi_message message;
+	struct spi_transfer transfer[2];
+
+	if ((!spi) || (!read_data) || (!packet_num)) {
+		pr_err("%s: invalid arg\n", __func__);
+		return -EINVAL;
+	}
+	if (packet_num > 0xFFFF) {
+		dev_err(&spi->dev, "%s: packet num > 0xFFFF\n", __func__);
+		return -EINVAL;
+	}
+
+	data[0] = 0x10;
+	data[1] = (u8)((packet_num >> 8) & 0xFF);
+	data[2] = (u8)(packet_num & 0xFF);
+
+	spi_message_init(&message);
+	memset(transfer, 0, sizeof(transfer));
+
+	transfer[0].len = 3;
+	transfer[0].tx_buf = data;
+	spi_message_add_tail(&transfer[0], &message);
+	transfer[1].len = packet_num * 188;
+	transfer[1].rx_buf = read_data;
+	spi_message_add_tail(&transfer[1], &message);
+
+	ret = spi_sync(spi, &message);
+	if (ret)
+		dev_err(&spi->dev, "%s: spi_write_then_read failed\n",
+			__func__);
+
+	return ret;
+}
+
+static int cxd2880_spi_read_ts_buffer_info(struct spi_device *spi,
+					struct cxd2880_ts_buf_info *info)
+{
+	u8 send_data = 0x20;
+	u8 recv_data[2];
+	int ret = 0;
+
+	if ((!spi) || (!info)) {
+		pr_err("%s: invalid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = spi_write_then_read(spi, &send_data, 1,
+			recv_data, sizeof(recv_data));
+	if (ret)
+		dev_err(&spi->dev,
+			"%s: spi_write_then_read failed\n", __func__);
+
+	info->read_ready = (u8)((recv_data[0] & 0x80) ? 1 : 0);
+	info->almost_full = (u8)((recv_data[0] & 0x40) ? 1 : 0);
+	info->almost_empty = (u8)((recv_data[0] & 0x20) ? 1 : 0);
+	info->overflow = (u8)((recv_data[0] & 0x10) ? 1 : 0);
+	info->underflow = (u8)((recv_data[0] & 0x08) ? 1 : 0);
+	info->packet_num = (u16)(((recv_data[0] & 0x07) << 8) | recv_data[1]);
+
+	return ret;
+}
+
+static int cxd2880_spi_clear_ts_buffer(struct spi_device *spi)
+{
+	u8 data = 0x03;
+	int ret = 0;
+
+	ret = cxd2880_write_spi(spi, &data, 1);
+
+	if (ret)
+		pr_err("%s: write spi failed\n", __func__);
+
+	return ret;
+}
+
+static int cxd2880_set_pid_filter(struct spi_device *spi,
+				struct cxd2880_pid_filter_config *cfg)
+{
+	u8 data[65];
+
+	if (!spi) {
+		pr_err("%s: ivnalid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	data[0] = 0x00;
+	if (cxd2880_write_reg(spi, 0x00, &data[0], 1) != 0)
+		return -EIO;
+	if (!cfg) {
+		data[0] = 0x02;
+		if (cxd2880_write_reg(spi, 0x50, &data[0], 1) != 0)
+			return -EIO;
+	} else {
+		data[0] = (u8)(cfg->is_negative ? 0x01 : 0x00);
+		{
+			int i = 0;
+			u16 pid = 0;
+
+			for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
+				pid = cfg->pid_config[i].pid;
+				if (cfg->pid_config[i].is_enable) {
+					data[1 + (i * 2)] =
+					    (u8)((u8)(pid >> 8) | 0x20);
+					data[2 + (i * 2)] =
+					    (u8)(pid & 0xFF);
+				} else {
+					data[1 + (i * 2)] = 0x00;
+					data[2 + (i * 2)] = 0x00;
+				}
+			}
+		}
+		if (cxd2880_write_reg(spi, 0x50, data, 65) != 0)
+			return -EIO;
+	}
+
+	return 0;
+}
+
+static int cxd2880_update_pid_filter(struct cxd2880_dvb_spi *dvb_spi,
+				struct cxd2880_pid_filter_config *cfg,
+				bool is_all_pid_filter)
+{
+	int ret = 0;
+
+	if ((!dvb_spi) || (!cfg)) {
+		pr_err("%s: invalid arg.\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&dvb_spi->spi_mutex);
+	if (is_all_pid_filter) {
+		struct cxd2880_pid_filter_config tmpcfg;
+
+		memset(&tmpcfg, 0, sizeof(tmpcfg));
+		tmpcfg.is_negative = 1;
+		tmpcfg.pid_config[0].is_enable = 1;
+		tmpcfg.pid_config[0].pid = 0x1FFF;
+
+		ret = cxd2880_set_pid_filter(dvb_spi->spi, &tmpcfg);
+	} else {
+		ret = cxd2880_set_pid_filter(dvb_spi->spi, cfg);
+	}
+	mutex_unlock(&dvb_spi->spi_mutex);
+
+	if (ret) {
+		dev_err(&dvb_spi->spi->dev,
+			"%s: set_pid_filter failed\n", __func__);
+	}
+
+	return ret;
+}
+
+static int cxd2880_ts_read(void *arg)
+{
+	struct cxd2880_dvb_spi *dvb_spi = NULL;
+	struct cxd2880_ts_buf_info info;
+	struct timespec ts;
+	long elapsed = 0;
+	long starttime = 0;
+	u32 i;
+	int ret;
+
+	dvb_spi = (struct cxd2880_dvb_spi *)arg;
+	if (!dvb_spi) {
+		pr_err("%s: invalid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = cxd2880_spi_clear_ts_buffer(dvb_spi->spi);
+	if (ret) {
+		dev_err(&dvb_spi->spi->dev,
+			"%s: set_clear_ts_buffer failed\n", __func__);
+		return ret;
+	}
+
+	getnstimeofday(&ts);
+	starttime = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
+	while (!kthread_should_stop()) {
+		getnstimeofday(&ts);
+		elapsed =
+			((ts.tv_sec * 1000) + (ts.tv_nsec / 1000000))
+			- starttime;
+		ret = cxd2880_spi_read_ts_buffer_info(dvb_spi->spi,
+							&info);
+		if (ret) {
+			pr_err("%s: spi_read_ts_buffer_info error\n",
+				__func__);
+			return ret;
+		}
+
+		if (info.packet_num > MAX_TRANS_PACKET) {
+			for (i = 0; i < info.packet_num / MAX_TRANS_PACKET;
+				i++) {
+				cxd2880_spi_read_ts(dvb_spi->spi,
+							dvb_spi->ts_buf,
+							MAX_TRANS_PACKET);
+				dvb_dmx_swfilter(&dvb_spi->demux,
+						dvb_spi->ts_buf,
+						MAX_TRANS_PACKET * 188);
+			}
+			starttime = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
+		} else if ((info.packet_num > 0) && (elapsed >= 500)) {
+			cxd2880_spi_read_ts(dvb_spi->spi,
+						dvb_spi->ts_buf,
+						info.packet_num);
+			dvb_dmx_swfilter(&dvb_spi->demux,
+					dvb_spi->ts_buf,
+					info.packet_num * 188);
+			starttime = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
+		} else {
+			usleep_range(10000, 11000);
+		}
+	}
+
+	return 0;
+}
+
+static int cxd2880_start_feed(struct dvb_demux_feed *feed)
+{
+	int ret = 0;
+	int i = 0;
+	struct dvb_demux *demux = NULL;
+	struct cxd2880_dvb_spi *dvb_spi = NULL;
+
+	if (!feed) {
+		pr_err("%s: invalid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	demux = feed->demux;
+	if (!demux) {
+		pr_err("%s: feed->demux is NULL\n", __func__);
+		return -EINVAL;
+	}
+	dvb_spi = (struct cxd2880_dvb_spi *)demux->priv;
+
+	if (dvb_spi->feed_count == CXD2880_MAX_FILTER_SIZE) {
+		dev_err(&dvb_spi->spi->dev,
+			"%s: Exceeded maximum PID count (32).", __func__);
+		dev_err(&dvb_spi->spi->dev,
+			"Selected PID cannot be enabled.\n");
+		return -EBUSY;
+	}
+
+	if (feed->pid == 0x2000) {
+		if (dvb_spi->all_pid_feed_count == 0) {
+			ret = cxd2880_update_pid_filter(dvb_spi,
+							&dvb_spi->filter_config,
+							true);
+			if (ret) {
+				dev_err(&dvb_spi->spi->dev,
+					"%s: update pid filter failed\n",
+					__func__);
+				return ret;
+			}
+		}
+		dvb_spi->all_pid_feed_count++;
+
+		dev_dbg(&dvb_spi->spi->dev,
+			"%s: all PID feed (count = %d)\n",
+			__func__, dvb_spi->all_pid_feed_count);
+	} else {
+		struct cxd2880_pid_filter_config cfgtmp;
+
+		cfgtmp = dvb_spi->filter_config;
+
+		for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
+			if (cfgtmp.pid_config[i].is_enable == 0) {
+				cfgtmp.pid_config[i].is_enable = 1;
+				cfgtmp.pid_config[i].pid = feed->pid;
+				dev_dbg(&dvb_spi->spi->dev,
+				"%s: store PID %d to #%d\n",
+				__func__, feed->pid, i);
+				break;
+			}
+		}
+		if (i == CXD2880_MAX_FILTER_SIZE) {
+			dev_err(&dvb_spi->spi->dev,
+				"%s: PID filter is full. Assumed bug.\n",
+				__func__);
+			return -EBUSY;
+		}
+		if (!dvb_spi->all_pid_feed_count)
+			ret = cxd2880_update_pid_filter(dvb_spi,
+							&cfgtmp,
+							false);
+		if (ret)
+			return ret;
+
+		dvb_spi->filter_config = cfgtmp;
+	}
+
+	if (dvb_spi->feed_count == 0) {
+		dvb_spi->ts_buf =
+			kmalloc(sizeof(u8) * MAX_TRANS_PACKET * 188,
+				GFP_KERNEL | GFP_DMA);
+		if (!dvb_spi->ts_buf) {
+			dev_err(&dvb_spi->spi->dev,
+			"%s: ts buffer allocate failed\n", __func__);
+			memset(&dvb_spi->filter_config, 0,
+				sizeof(dvb_spi->filter_config));
+			dvb_spi->all_pid_feed_count = 0;
+			return -ENOMEM;
+		}
+		dvb_spi->cxd2880_ts_read_thread = kthread_run(cxd2880_ts_read,
+								dvb_spi,
+								"cxd2880_ts_read");
+		if (IS_ERR(dvb_spi->cxd2880_ts_read_thread)) {
+			dev_err(&dvb_spi->spi->dev,
+				"%s: kthread_run failed/\n",
+				__func__);
+			kfree(dvb_spi->ts_buf);
+			dvb_spi->ts_buf = NULL;
+			memset(&dvb_spi->filter_config, 0,
+				sizeof(dvb_spi->filter_config));
+			dvb_spi->all_pid_feed_count = 0;
+			return PTR_ERR(dvb_spi->cxd2880_ts_read_thread);
+		}
+	}
+
+	dvb_spi->feed_count++;
+
+	dev_dbg(&dvb_spi->spi->dev, "%s: start feed (count %d)\n",
+		__func__, dvb_spi->feed_count);
+	return 0;
+}
+
+static int cxd2880_stop_feed(struct dvb_demux_feed *feed)
+{
+	int i = 0;
+	int ret = 0;
+	struct dvb_demux *demux = NULL;
+	struct cxd2880_dvb_spi *dvb_spi = NULL;
+
+	if (!feed) {
+		pr_err("%s: invalid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	demux = feed->demux;
+	if (!demux) {
+		pr_err("%s: feed->demux is NULL\n", __func__);
+		return -EINVAL;
+	}
+	dvb_spi = (struct cxd2880_dvb_spi *)demux->priv;
+
+	if (!dvb_spi->feed_count) {
+		dev_warn(&dvb_spi->spi->dev,
+			"%s: no feed is started\n", __func__);
+		return -EINVAL;
+	}
+
+	if (feed->pid == 0x2000) {
+		/*
+		 * Special PID case.
+		 * Number of 0x2000 feed request was stored
+		 * in dvb_spi->all_pid_feed_count.
+		 */
+		if (dvb_spi->all_pid_feed_count <= 0) {
+			dev_warn(&dvb_spi->spi->dev,
+				"%s: PID %d not found.\n",
+				__func__, feed->pid);
+			return -EINVAL;
+		}
+		dvb_spi->all_pid_feed_count--;
+	} else {
+		struct cxd2880_pid_filter_config cfgtmp;
+
+		cfgtmp = dvb_spi->filter_config;
+
+		for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
+			if (feed->pid == cfgtmp.pid_config[i].pid &&
+				cfgtmp.pid_config[i].is_enable != 0) {
+				cfgtmp.pid_config[i].is_enable = 0;
+				cfgtmp.pid_config[i].pid = 0;
+				dev_dbg(&dvb_spi->spi->dev,
+					"%s: removed PID %d from #%d\n",
+					__func__, feed->pid, i);
+				break;
+			}
+		}
+		dvb_spi->filter_config = cfgtmp;
+
+		if (i == CXD2880_MAX_FILTER_SIZE) {
+			dev_warn(&dvb_spi->spi->dev, "%s: PID %d not found\n",
+				__func__, feed->pid);
+			return -EINVAL;
+		}
+	}
+
+	ret = cxd2880_update_pid_filter(dvb_spi,
+					&dvb_spi->filter_config,
+					dvb_spi->all_pid_feed_count > 0);
+	dvb_spi->feed_count--;
+
+	if (dvb_spi->feed_count == 0) {
+		int ret_stop = 0;
+
+		ret_stop = kthread_stop(dvb_spi->cxd2880_ts_read_thread);
+		if (ret_stop) {
+			dev_err(&dvb_spi->spi->dev,
+			"%s: cxd2880_ts_read thread didn't terminate normally\n",
+			__func__);
+			ret = ret_stop;
+		}
+		kfree(dvb_spi->ts_buf);
+		dvb_spi->ts_buf = NULL;
+	}
+
+	dev_dbg(&dvb_spi->spi->dev, "%s: stop feed ok.(count %d)\n",
+		__func__, dvb_spi->feed_count);
+
+	return ret;
+}
+
+static const struct of_device_id cxd2880_spi_of_match[] = {
+	{ .compatible = "sony,cxd2880" },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, cxd2880_spi_of_match);
+
+static int
+cxd2880_spi_probe(struct spi_device *spi)
+{
+	int ret = 0;
+	struct cxd2880_dvb_spi *dvb_spi = NULL;
+	struct cxd2880_config config;
+
+	if (!spi) {
+		pr_err("%s: invalid arg.\n", __func__);
+		return -EINVAL;
+	}
+
+	dvb_spi = kzalloc(sizeof(struct cxd2880_dvb_spi), GFP_KERNEL);
+	if (!dvb_spi)
+		return -ENOMEM;
+
+	dvb_spi->spi = spi;
+	mutex_init(&dvb_spi->spi_mutex);
+	dev_set_drvdata(&spi->dev, dvb_spi);
+	config.spi = spi;
+	config.spi_mutex = &dvb_spi->spi_mutex;
+
+	ret = dvb_register_adapter(&dvb_spi->adapter,
+					"CXD2880",
+					THIS_MODULE,
+					&spi->dev,
+					adapter_nr);
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: dvb_register_adapter() failed\n",
+			__func__);
+		goto fail_adapter;
+	}
+
+	if (!dvb_attach(cxd2880_attach, &dvb_spi->dvb_fe, &config)) {
+		dev_err(&spi->dev, "%s: cxd2880_attach failed\n", __func__);
+		goto fail_attach;
+	}
+
+	ret = dvb_register_frontend(&dvb_spi->adapter,
+					&dvb_spi->dvb_fe);
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: dvb_register_frontend() failed\n",
+			__func__);
+		goto fail_frontend;
+	}
+
+	dvb_spi->demux.dmx.capabilities = DMX_TS_FILTERING;
+	dvb_spi->demux.priv = dvb_spi;
+	dvb_spi->demux.filternum = CXD2880_MAX_FILTER_SIZE;
+	dvb_spi->demux.feednum = CXD2880_MAX_FILTER_SIZE;
+	dvb_spi->demux.start_feed = cxd2880_start_feed;
+	dvb_spi->demux.stop_feed = cxd2880_stop_feed;
+
+	ret = dvb_dmx_init(&dvb_spi->demux);
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: dvb_dmx_init() failed\n", __func__);
+		goto fail_dmx;
+	}
+
+	dvb_spi->dmxdev.filternum = CXD2880_MAX_FILTER_SIZE;
+	dvb_spi->dmxdev.demux = &dvb_spi->demux.dmx;
+	dvb_spi->dmxdev.capabilities = 0;
+	ret = dvb_dmxdev_init(&dvb_spi->dmxdev,
+				&dvb_spi->adapter);
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: dvb_dmxdev_init() failed\n", __func__);
+		goto fail_dmxdev;
+	}
+
+	dvb_spi->dmx_fe.source = DMX_FRONTEND_0;
+	ret = dvb_spi->demux.dmx.add_frontend(&dvb_spi->demux.dmx,
+						&dvb_spi->dmx_fe);
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: add_frontend() failed\n", __func__);
+		goto fail_dmx_fe;
+	}
+
+	ret = dvb_spi->demux.dmx.connect_frontend(&dvb_spi->demux.dmx,
+						&dvb_spi->dmx_fe);
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: dvb_register_frontend() failed\n",
+			__func__);
+		goto fail_fe_conn;
+	}
+
+	dev_info(&spi->dev, "Sony CXD2880 has successfully attached.\n");
+
+	return 0;
+
+fail_fe_conn:
+	dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
+			&dvb_spi->dmx_fe);
+fail_dmx_fe:
+	dvb_dmxdev_release(&dvb_spi->dmxdev);
+fail_dmxdev:
+	dvb_dmx_release(&dvb_spi->demux);
+fail_dmx:
+	dvb_unregister_frontend(&dvb_spi->dvb_fe);
+fail_frontend:
+	dvb_frontend_detach(&dvb_spi->dvb_fe);
+fail_attach:
+	dvb_unregister_adapter(&dvb_spi->adapter);
+fail_adapter:
+	kfree(dvb_spi);
+	return ret;
+}
+
+static int
+cxd2880_spi_remove(struct spi_device *spi)
+{
+	struct cxd2880_dvb_spi *dvb_spi;
+
+	if (!spi) {
+		pr_err("%s: invalid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	dvb_spi = (struct cxd2880_dvb_spi *)dev_get_drvdata(&spi->dev);
+
+	if (!dvb_spi) {
+		pr_err("%s: failed\n", __func__);
+		return -EINVAL;
+	}
+	dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
+					&dvb_spi->dmx_fe);
+	dvb_dmxdev_release(&dvb_spi->dmxdev);
+	dvb_dmx_release(&dvb_spi->demux);
+	dvb_unregister_frontend(&dvb_spi->dvb_fe);
+	dvb_frontend_detach(&dvb_spi->dvb_fe);
+	dvb_unregister_adapter(&dvb_spi->adapter);
+
+	kfree(dvb_spi);
+	dev_info(&spi->dev, "%s: cxd2880_spi remove ok.\n", __func__);
+
+	return 0;
+}
+
+static const struct spi_device_id cxd2880_spi_id[] = {
+	{ "cxd2880", 0 },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, cxd2880_spi_id);
+
+static struct spi_driver cxd2880_spi_driver = {
+	.driver	= {
+		.name	= "cxd2880",
+		.of_match_table = cxd2880_spi_of_match,
+	},
+	.id_table = cxd2880_spi_id,
+	.probe    = cxd2880_spi_probe,
+	.remove   = cxd2880_spi_remove,
+};
+module_spi_driver(cxd2880_spi_driver);
+
+MODULE_DESCRIPTION(
+"Sony CXD2880 DVB-T2/T tuner + demodulator drvier SPI adapter");
+MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
+MODULE_LICENSE("GPL v2");
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 03/15] [media] cxd2880: Add common files for the driver
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
  2017-04-14  2:00 ` [PATCH v2 01/15] [dt-bindings] [media] Add document file for CXD2880 SPI I/F Yasunari.Takiguchi
  2017-04-14  2:08 ` [PATCH v2 02/15] [media] cxd2880-spi: Add support for CXD2008 SPI interface Yasunari.Takiguchi
@ 2017-04-14  2:17 ` Yasunari.Takiguchi
  2017-06-13 12:13   ` Mauro Carvalho Chehab
  2017-04-14  2:22 ` [PATCH v2 04/15] [media] cxd2880: Add math functions " Yasunari.Takiguchi
                   ` (12 subsequent siblings)
  15 siblings, 1 reply; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  2:17 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

These are common files for the driver for the
Sony CXD2880 DVB-T2/T tuner + demodulator.
These contains helper functions for the driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
 drivers/media/dvb-frontends/cxd2880/cxd2880.h      | 46 ++++++++++++
 .../media/dvb-frontends/cxd2880/cxd2880_common.c   | 84 +++++++++++++++++++++
 .../media/dvb-frontends/cxd2880/cxd2880_common.h   | 86 ++++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_io.c   | 68 +++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_io.h   | 62 ++++++++++++++++
 .../media/dvb-frontends/cxd2880/cxd2880_stdlib.h   | 35 +++++++++
 .../dvb-frontends/cxd2880/cxd2880_stopwatch_port.c | 71 ++++++++++++++++++
 7 files changed, 452 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880.h b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
new file mode 100644
index 000000000000..281f9a784eb5
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
@@ -0,0 +1,46 @@
+/*
+ * cxd2880.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver public definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_H
+#define CXD2880_H
+
+struct cxd2880_config {
+	struct spi_device *spi;
+	struct mutex *spi_mutex; /* For SPI access exclusive control */
+};
+
+#if IS_REACHABLE(CONFIG_DVB_CXD2880)
+extern struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
+					struct cxd2880_config *cfg);
+#else
+static inline struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
+					struct cxd2880_config *cfg)
+{
+	pr_warn("%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_CXD2880 */
+
+#endif /* CXD2880_H */
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
new file mode 100644
index 000000000000..850f3a76b2c7
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
@@ -0,0 +1,84 @@
+/*
+ * cxd2880_common.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_common.h"
+
+#define MASKUPPER(n) (((n) == 0) ? 0 : (0xFFFFFFFFU << (32 - (n))))
+#define MASKLOWER(n) (((n) == 0) ? 0 : (0xFFFFFFFFU >> (32 - (n))))
+
+int cxd2880_convert2s_complement(u32 value, u32 bitlen)
+{
+	if ((bitlen == 0) || (bitlen >= 32))
+		return (int)value;
+
+	if (value & (u32)(1 << (bitlen - 1)))
+		return (int)(MASKUPPER(32 - bitlen) | value);
+	else
+		return (int)(MASKLOWER(bitlen) & value);
+}
+
+u32 cxd2880_bit_split_from_byte_array(u8 *array, u32 start_bit, u32 bit_num)
+{
+	u32 value = 0;
+	u8 *array_read;
+	u8 bit_read;
+	u32 len_remain;
+
+	if (!array)
+		return 0;
+	if ((bit_num == 0) || (bit_num > 32))
+		return 0;
+
+	array_read = array + (start_bit / 8);
+	bit_read = (u8)(start_bit % 8);
+	len_remain = bit_num;
+
+	if (bit_read != 0) {
+		if (((int)len_remain) <= 8 - bit_read) {
+			value = (*array_read) >> ((8 - bit_read) - len_remain);
+			len_remain = 0;
+		} else {
+			value = *array_read++;
+			len_remain -= 8 - bit_read;
+		}
+	}
+
+	while (len_remain > 0) {
+		if (len_remain < 8) {
+			value <<= len_remain;
+			value |= (*array_read++ >> (8 - len_remain));
+			len_remain = 0;
+		} else {
+			value <<= 8;
+			value |= (u32)(*array_read++);
+			len_remain -= 8;
+		}
+	}
+
+	value &= MASKLOWER(bit_num);
+
+	return value;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
new file mode 100644
index 000000000000..b1ecb44bca10
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
@@ -0,0 +1,86 @@
+/*
+ * cxd2880_common.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver common definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_COMMON_H
+#define CXD2880_COMMON_H
+
+#include <linux/types.h>
+
+#ifndef NULL
+#ifdef __cplusplus
+#define NULL 0
+#else
+#define NULL ((void *)0)
+#endif
+#endif
+
+#include <linux/delay.h>
+#define CXD2880_SLEEP(n) msleep(n)
+#ifndef CXD2880_SLEEP_IN_MON
+#define CXD2880_SLEEP_IN_MON(n, obj) CXD2880_SLEEP(n)
+#endif
+
+#define CXD2880_ARG_UNUSED(arg) ((void)(arg))
+
+enum cxd2880_ret {
+	CXD2880_RESULT_OK,
+	CXD2880_RESULT_ERROR_ARG,
+	CXD2880_RESULT_ERROR_IO,
+	CXD2880_RESULT_ERROR_SW_STATE,
+	CXD2880_RESULT_ERROR_HW_STATE,
+	CXD2880_RESULT_ERROR_TIMEOUT,
+	CXD2880_RESULT_ERROR_UNLOCK,
+	CXD2880_RESULT_ERROR_RANGE,
+	CXD2880_RESULT_ERROR_NOSUPPORT,
+	CXD2880_RESULT_ERROR_CANCEL,
+	CXD2880_RESULT_ERROR_OTHER,
+	CXD2880_RESULT_ERROR_OVERFLOW,
+	CXD2880_RESULT_OK_CONFIRM
+};
+
+int cxd2880_convert2s_complement(u32 value, u32 bitlen);
+
+u32 cxd2880_bit_split_from_byte_array(u8 *array, u32 start_bit, u32 bit_num);
+
+struct cxd2880_atomic {
+	int counter;
+};
+
+#define cxd2880_atomic_set(a, i) ((a)->counter = i)
+#define cxd2880_atomic_read(a) ((a)->counter)
+
+struct cxd2880_stopwatch {
+	u32 start_time;
+};
+
+enum cxd2880_ret cxd2880_stopwatch_start(struct cxd2880_stopwatch *stopwatch);
+
+enum cxd2880_ret cxd2880_stopwatch_sleep(struct cxd2880_stopwatch *stopwatch,
+					 u32 ms);
+
+enum cxd2880_ret cxd2880_stopwatch_elapsed(struct cxd2880_stopwatch *stopwatch,
+					   u32 *elapsed);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
new file mode 100644
index 000000000000..f0f82055a953
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
@@ -0,0 +1,68 @@
+/*
+ * cxd2880_io.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * register I/O interface functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_io.h"
+
+enum cxd2880_ret cxd2880_io_common_write_one_reg(struct cxd2880_io *io,
+						 enum cxd2880_io_tgt tgt,
+						 u8 sub_address, u8 data)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!io)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret = io->write_regs(io, tgt, sub_address, &data, 1);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_io_set_reg_bits(struct cxd2880_io *io,
+					 enum cxd2880_io_tgt tgt,
+					 u8 sub_address, u8 data, u8 mask)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!io)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (mask == 0x00)
+		return CXD2880_RESULT_OK;
+
+	if (mask != 0xFF) {
+		u8 rdata = 0x00;
+
+		ret = io->read_regs(io, tgt, sub_address, &rdata, 1);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		data = (u8)((data & mask) | (rdata & (mask ^ 0xFF)));
+	}
+
+	ret = io->write_reg(io, tgt, sub_address, data);
+
+	return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
new file mode 100644
index 000000000000..4d6db13cf910
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
@@ -0,0 +1,62 @@
+/*
+ * cxd2880_io.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * register I/O interface definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_IO_H
+#define CXD2880_IO_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_io_tgt {
+	CXD2880_IO_TGT_SYS,
+	CXD2880_IO_TGT_DMD
+};
+
+struct cxd2880_io {
+	enum cxd2880_ret (*read_regs)(struct cxd2880_io *io,
+				       enum cxd2880_io_tgt tgt, u8 sub_address,
+				       u8 *data, u32 size);
+	enum cxd2880_ret (*write_regs)(struct cxd2880_io *io,
+					enum cxd2880_io_tgt tgt, u8 sub_address,
+					const u8 *data, u32 size);
+	enum cxd2880_ret (*write_reg)(struct cxd2880_io *io,
+				       enum cxd2880_io_tgt tgt, u8 sub_address,
+				       u8 data);
+	void *if_object;
+	u8 i2c_address_sys;
+	u8 i2c_address_demod;
+	u8 slave_select;
+	void *user;
+};
+
+enum cxd2880_ret cxd2880_io_common_write_one_reg(struct cxd2880_io *io,
+						 enum cxd2880_io_tgt tgt,
+						 u8 sub_address, u8 data);
+
+enum cxd2880_ret cxd2880_io_set_reg_bits(struct cxd2880_io *io,
+					 enum cxd2880_io_tgt tgt,
+					 u8 sub_address, u8 data, u8 mask);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
new file mode 100644
index 000000000000..b9ca1b9df110
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
@@ -0,0 +1,35 @@
+/*
+ * cxd2880_stdlib.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * standard lib function aliases
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_STDLIB_H
+#define CXD2880_STDLIB_H
+
+#include <linux/string.h>
+
+#define cxd2880_memcpy  memcpy
+#define cxd2880_memset  memset
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
new file mode 100644
index 000000000000..14ad6aa6c4c0
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
@@ -0,0 +1,71 @@
+/*
+ * cxd2880_stopwatch_port.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * time measurement functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_common.h"
+
+#include <linux/ktime.h>
+#include <linux/time.h>
+#include <linux/timekeeping.h>
+
+static u32 get_time_count(void)
+{
+	struct timespec tp;
+
+	getnstimeofday(&tp);
+
+	return (u32)((tp.tv_sec * 1000) + (tp.tv_nsec / 1000000));
+}
+
+enum cxd2880_ret cxd2880_stopwatch_start(struct cxd2880_stopwatch *stopwatch)
+{
+	if (!stopwatch)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	stopwatch->start_time = get_time_count();
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_stopwatch_sleep(struct cxd2880_stopwatch *stopwatch,
+					 u32 ms)
+{
+	if (!stopwatch)
+		return CXD2880_RESULT_ERROR_ARG;
+	CXD2880_ARG_UNUSED(*stopwatch);
+	CXD2880_SLEEP(ms);
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_stopwatch_elapsed(struct cxd2880_stopwatch *stopwatch,
+					   u32 *elapsed)
+{
+	if (!stopwatch || !elapsed)
+		return CXD2880_RESULT_ERROR_ARG;
+	*elapsed = get_time_count() - stopwatch->start_time;
+
+	return CXD2880_RESULT_OK;
+}
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 04/15] [media] cxd2880: Add math functions for the driver
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (2 preceding siblings ...)
  2017-04-14  2:17 ` [PATCH v2 03/15] [media] cxd2880: Add common files for the driver Yasunari.Takiguchi
@ 2017-04-14  2:22 ` Yasunari.Takiguchi
  2017-06-13 12:18   ` Mauro Carvalho Chehab
  2017-04-14  2:25 ` [PATCH v2 05/15] [media] cxd2880: Add spi device IO routines Yasunari.Takiguchi
                   ` (11 subsequent siblings)
  15 siblings, 1 reply; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  2:22 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

Provide some math support functions (fixed-point log functions)
for the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
 drivers/media/dvb-frontends/cxd2880/cxd2880_math.c | 89 ++++++++++++++++++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_math.h | 40 ++++++++++
 2 files changed, 129 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_math.h

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_math.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
new file mode 100644
index 000000000000..434c827898ff
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
@@ -0,0 +1,89 @@
+/*
+ * cxd2880_math.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * mathmatics functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_math.h"
+
+#define MAX_BIT_PRECISION	  5
+#define FRAC_BITMASK	    0x1F
+#define LOG2_10_100X	     332
+#define LOG2_E_100X	      144
+
+static const u8 log2_look_up[] = {
+	0, 4,
+	9, 13,
+	17, 21,
+	25, 29,
+	32, 36,
+	39, 43,
+	46, 49,
+	52, 55,
+	58, 61,
+	64, 67,
+	70, 73,
+	75, 78,
+	81, 83,
+	86, 88,
+	91, 93,
+	95, 98
+};
+
+u32 cxd2880_math_log2(u32 x)
+{
+	u8 count = 0;
+	u8 index = 0;
+	u32 xval = x;
+
+	for (x >>= 1; x > 0; x >>= 1)
+		count++;
+
+	x = count * 100;
+
+	if (count > 0) {
+		if (count <= MAX_BIT_PRECISION) {
+			index =
+			    (u8)(xval << (MAX_BIT_PRECISION - count)) &
+			    FRAC_BITMASK;
+			x += log2_look_up[index];
+		} else {
+			index =
+			    (u8)(xval >> (count - MAX_BIT_PRECISION)) &
+			    FRAC_BITMASK;
+			x += log2_look_up[index];
+		}
+	}
+
+	return x;
+}
+
+u32 cxd2880_math_log10(u32 x)
+{
+	return ((100 * cxd2880_math_log2(x) + LOG2_10_100X / 2) / LOG2_10_100X);
+}
+
+u32 cxd2880_math_log(u32 x)
+{
+	return ((100 * cxd2880_math_log2(x) + LOG2_E_100X / 2) / LOG2_E_100X);
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_math.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_math.h
new file mode 100644
index 000000000000..94211835a4ad
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_math.h
@@ -0,0 +1,40 @@
+/*
+ * cxd2880_math.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * mathmatics definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_MATH_H_
+#define CXD2880_MATH_H_
+
+#include "cxd2880_common.h"
+
+u32 cxd2880_math_log2(u32 x);
+u32 cxd2880_math_log10(u32 x);
+u32 cxd2880_math_log(u32 x);
+
+#ifndef min
+#define min(a, b)	    (((a) < (b)) ? (a) : (b))
+#endif
+
+#endif
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 05/15] [media] cxd2880: Add spi device IO routines
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (3 preceding siblings ...)
  2017-04-14  2:22 ` [PATCH v2 04/15] [media] cxd2880: Add math functions " Yasunari.Takiguchi
@ 2017-04-14  2:25 ` Yasunari.Takiguchi
  2017-06-13 12:25   ` Mauro Carvalho Chehab
  2017-04-14  2:26 ` [PATCH v2 06/15] [media] cxd2880: Add tuner part of the driver Yasunari.Takiguchi
                   ` (10 subsequent siblings)
  15 siblings, 1 reply; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  2:25 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

Add functions for initializing, reading and writing to the SPI
device for the Sony CXD2880 DVB-T2/T tuner + demodulator.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
 .../dvb-frontends/cxd2880/cxd2880_devio_spi.c      | 147 +++++++++++++++++++++
 .../dvb-frontends/cxd2880/cxd2880_devio_spi.h      |  40 ++++++
 drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h  |  51 +++++++
 .../dvb-frontends/cxd2880/cxd2880_spi_device.c     | 130 ++++++++++++++++++
 .../dvb-frontends/cxd2880/cxd2880_spi_device.h     |  45 +++++++
 5 files changed, 413 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
new file mode 100644
index 000000000000..516efade6bf5
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
@@ -0,0 +1,147 @@
+/*
+ * cxd2880_devio_spi.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * I/O interface via SPI
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_devio_spi.h"
+#include "cxd2880_stdlib.h"
+
+#define BURST_WRITE_MAX 128
+
+static enum cxd2880_ret cxd2880_io_spi_read_reg(struct cxd2880_io *io,
+						enum cxd2880_io_tgt tgt,
+						u8 sub_address, u8 *data,
+						u32 size)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	struct cxd2880_spi *spi = NULL;
+	u8 send_data[6];
+	u8 *read_data_top = data;
+
+	if ((!io) || (!io->if_object) || (!data))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (sub_address + size > 0x100)
+		return CXD2880_RESULT_ERROR_RANGE;
+
+	spi = (struct cxd2880_spi *)(io->if_object);
+
+	if (tgt == CXD2880_IO_TGT_SYS)
+		send_data[0] = 0x0B;
+	else
+		send_data[0] = 0x0A;
+
+	send_data[3] = 0;
+	send_data[4] = 0;
+	send_data[5] = 0;
+
+	while (size > 0) {
+		send_data[1] = sub_address;
+		if (size > 255)
+			send_data[2] = 255;
+		else
+			send_data[2] = (u8)size;
+
+		ret =
+		    spi->write_read(spi, send_data, sizeof(send_data),
+				    read_data_top, send_data[2]);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		sub_address += send_data[2];
+		read_data_top += send_data[2];
+		size -= send_data[2];
+	}
+
+	return ret;
+}
+
+static enum cxd2880_ret cxd2880_io_spi_write_reg(struct cxd2880_io *io,
+						 enum cxd2880_io_tgt tgt,
+						 u8 sub_address,
+						 const u8 *data, u32 size)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	struct cxd2880_spi *spi = NULL;
+	u8 send_data[BURST_WRITE_MAX + 4];
+	const u8 *write_data_top = data;
+
+	if ((!io) || (!io->if_object) || (!data))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (size > BURST_WRITE_MAX)
+		return CXD2880_RESULT_ERROR_OVERFLOW;
+
+	if (sub_address + size > 0x100)
+		return CXD2880_RESULT_ERROR_RANGE;
+
+	spi = (struct cxd2880_spi *)(io->if_object);
+
+	if (tgt == CXD2880_IO_TGT_SYS)
+		send_data[0] = 0x0F;
+	else
+		send_data[0] = 0x0E;
+
+	while (size > 0) {
+		send_data[1] = sub_address;
+		if (size > 255)
+			send_data[2] = 255;
+		else
+			send_data[2] = (u8)size;
+
+		cxd2880_memcpy(&send_data[3], write_data_top, send_data[2]);
+
+		if (tgt == CXD2880_IO_TGT_SYS) {
+			send_data[3 + send_data[2]] = 0x00;
+			ret = spi->write(spi, send_data, send_data[2] + 4);
+		} else {
+			ret = spi->write(spi, send_data, send_data[2] + 3);
+		}
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		sub_address += send_data[2];
+		write_data_top += send_data[2];
+		size -= send_data[2];
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_io_spi_create(struct cxd2880_io *io,
+				       struct cxd2880_spi *spi, u8 slave_select)
+{
+	if ((!io) || (!spi))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	io->read_regs = cxd2880_io_spi_read_reg;
+	io->write_regs = cxd2880_io_spi_write_reg;
+	io->write_reg = cxd2880_io_common_write_one_reg;
+	io->if_object = spi;
+	io->i2c_address_sys = 0;
+	io->i2c_address_demod = 0;
+	io->slave_select = slave_select;
+
+	return CXD2880_RESULT_OK;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
new file mode 100644
index 000000000000..15934bf11935
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
@@ -0,0 +1,40 @@
+/*
+ * cxd2880_devio_spi.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * I/O interface via SPI
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_DEVIO_SPI_H
+#define CXD2880_DEVIO_SPI_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_io.h"
+#include "cxd2880_spi.h"
+
+#include "cxd2880_tnrdmd.h"
+
+enum cxd2880_ret cxd2880_io_spi_create(struct cxd2880_io *io,
+				       struct cxd2880_spi *spi,
+				       u8 slave_select);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
new file mode 100644
index 000000000000..81e5be747962
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
@@ -0,0 +1,51 @@
+/*
+ * cxd2880_spi.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_SPI_H
+#define CXD2880_SPI_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_spi_mode {
+	CXD2880_SPI_MODE_0,
+	CXD2880_SPI_MODE_1,
+	CXD2880_SPI_MODE_2,
+	CXD2880_SPI_MODE_3
+};
+
+struct cxd2880_spi {
+	enum cxd2880_ret (*read)(struct cxd2880_spi *spi, u8 *data,
+				  u32 size);
+	enum cxd2880_ret (*write)(struct cxd2880_spi *spi, const u8 *data,
+				   u32 size);
+	enum cxd2880_ret (*write_read)(struct cxd2880_spi *spi,
+					const u8 *tx_data, u32 tx_size,
+					u8 *rx_data, u32 rx_size);
+	u32 flags;
+	void *user;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
new file mode 100644
index 000000000000..af9ed40c900b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
@@ -0,0 +1,130 @@
+/*
+ * cxd2880_spi_device.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/spi/spi.h>
+
+#include "cxd2880_spi_device.h"
+
+static enum cxd2880_ret cxd2880_spi_device_write(struct cxd2880_spi *spi,
+						const u8 *data, u32 size)
+{
+	struct cxd2880_spi_device *spi_device = NULL;
+	struct spi_message msg;
+	struct spi_transfer tx;
+	int result = 0;
+
+	if ((!spi) || (!spi->user) || (!data) || (size == 0))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	spi_device = (struct cxd2880_spi_device *)(spi->user);
+
+	memset(&tx, 0, sizeof(tx));
+	tx.tx_buf = data;
+	tx.len = size;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&tx, &msg);
+	result = spi_sync(spi_device->spi, &msg);
+
+	if (result < 0)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret cxd2880_spi_device_write_read(struct cxd2880_spi *spi,
+							const u8 *tx_data,
+							u32 tx_size,
+							u8 *rx_data,
+							u32 rx_size)
+{
+	struct cxd2880_spi_device *spi_device = NULL;
+	int result = 0;
+
+	if ((!spi) || (!spi->user) || (!tx_data) ||
+		 (tx_size == 0) || (!rx_data) || (rx_size == 0))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	spi_device = (struct cxd2880_spi_device *)(spi->user);
+
+	result = spi_write_then_read(spi_device->spi, tx_data,
+					tx_size, rx_data, rx_size);
+	if (result < 0)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret
+cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device,
+				enum cxd2880_spi_mode mode,
+				u32 speed_hz)
+{
+	int result = 0;
+	struct spi_device *spi = spi_device->spi;
+
+	switch (mode) {
+	case CXD2880_SPI_MODE_0:
+		spi->mode = SPI_MODE_0;
+		break;
+	case CXD2880_SPI_MODE_1:
+		spi->mode = SPI_MODE_1;
+		break;
+	case CXD2880_SPI_MODE_2:
+		spi->mode = SPI_MODE_2;
+		break;
+	case CXD2880_SPI_MODE_3:
+		spi->mode = SPI_MODE_3;
+		break;
+	default:
+		return CXD2880_RESULT_ERROR_ARG;
+	}
+
+	spi->max_speed_hz = speed_hz;
+	spi->bits_per_word = 8;
+	result = spi_setup(spi);
+	if (result != 0) {
+		pr_err("spi_setup failed %d\n", result);
+		return CXD2880_RESULT_ERROR_ARG;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_spi_device_create_spi(struct cxd2880_spi *spi,
+					struct cxd2880_spi_device *spi_device)
+{
+	if ((!spi) || (!spi_device))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	spi->read = NULL;
+	spi->write = cxd2880_spi_device_write;
+	spi->write_read = cxd2880_spi_device_write_read;
+	spi->flags = 0;
+	spi->user = spi_device;
+
+	return CXD2880_RESULT_OK;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
new file mode 100644
index 000000000000..343d9161d537
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
@@ -0,0 +1,45 @@
+/*
+ * cxd2880_spi_device.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_SPI_DEVICE_H
+#define CXD2880_SPI_DEVICE_H
+
+#include "cxd2880_spi.h"
+
+struct cxd2880_spi_device {
+	struct spi_device *spi;
+};
+
+enum cxd2880_ret
+cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device,
+				enum cxd2880_spi_mode mode,
+				u32 speedHz);
+
+enum cxd2880_ret
+cxd2880_spi_device_create_spi(struct cxd2880_spi *spi,
+				struct cxd2880_spi_device *spi_device);
+
+#endif /* CXD2880_SPI_DEVICE_H */
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 06/15] [media] cxd2880: Add tuner part of the driver
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (4 preceding siblings ...)
  2017-04-14  2:25 ` [PATCH v2 05/15] [media] cxd2880: Add spi device IO routines Yasunari.Takiguchi
@ 2017-04-14  2:26 ` Yasunari.Takiguchi
  2017-06-13 13:09   ` Mauro Carvalho Chehab
  2017-04-14  2:29 ` [PATCH v2 07/15] [media] cxd2880: Add integration layer for " Yasunari.Takiguchi
                   ` (9 subsequent siblings)
  15 siblings, 1 reply; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  2:26 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

This part of the driver has the main routines to handle
the tuner and demodulator functionality.  The tnrdmd_mon.* files
have monitor functions for the driver.
This is part of the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
 drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h  |   50 +
 .../media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c   | 3925 ++++++++++++++++++++
 .../media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h   |  395 ++
 .../cxd2880/cxd2880_tnrdmd_driver_version.h        |   29 +
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c     |  207 ++
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h     |   52 +
 6 files changed, 4658 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
new file mode 100644
index 000000000000..7de098d556fe
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
@@ -0,0 +1,50 @@
+/*
+ * cxd2880_dtv.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DTV related definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_DTV_H
+#define CXD2880_DTV_H
+
+enum cxd2880_dtv_sys {
+	CXD2880_DTV_SYS_UNKNOWN,
+	CXD2880_DTV_SYS_DVBT,
+	CXD2880_DTV_SYS_DVBT2,
+	CXD2880_DTV_SYS_ISDBT,
+	CXD2880_DTV_SYS_ISDBTSB,
+	CXD2880_DTV_SYS_ISDBTMM_A,
+	CXD2880_DTV_SYS_ISDBTMM_B,
+	CXD2880_DTV_SYS_ANY
+};
+
+enum cxd2880_dtv_bandwidth {
+	CXD2880_DTV_BW_UNKNOWN = 0,
+	CXD2880_DTV_BW_1_7_MHZ = 1,
+	CXD2880_DTV_BW_5_MHZ = 5,
+	CXD2880_DTV_BW_6_MHZ = 6,
+	CXD2880_DTV_BW_7_MHZ = 7,
+	CXD2880_DTV_BW_8_MHZ = 8
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
new file mode 100644
index 000000000000..286384ae0124
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
@@ -0,0 +1,3925 @@
+/*
+ * cxd2880_tnrdmd.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common control functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_common.h"
+#include "cxd2880_stdlib.h"
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt2.h"
+
+static enum cxd2880_ret p_init1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data = 0;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if ((tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) ||
+	    (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)) {
+		switch (tnr_dmd->create_param.ts_output_if) {
+		case CXD2880_TNRDMD_TSOUT_IF_TS:
+			data = 0x00;
+			break;
+		case CXD2880_TNRDMD_TSOUT_IF_SPI:
+			data = 0x01;
+			break;
+		case CXD2880_TNRDMD_TSOUT_IF_SDIO:
+			data = 0x02;
+			break;
+		default:
+			return CXD2880_RESULT_ERROR_ARG;
+		}
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x10,
+					   data) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x11,
+				   0x16) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	switch (tnr_dmd->chip_id) {
+	case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X:
+		data = 0x1A;
+		break;
+	case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11:
+		data = 0x16;
+		break;
+	default:
+		return CXD2880_RESULT_ERROR_NOSUPPORT;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x10,
+				   data) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->create_param.en_internal_ldo)
+		data = 0x01;
+	else
+		data = 0x00;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x11,
+				   data) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x13,
+				   data) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x12,
+				   data) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	switch (tnr_dmd->chip_id) {
+	case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X:
+		data = 0x01;
+		break;
+	case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11:
+		data = 0x00;
+		break;
+	default:
+		return CXD2880_RESULT_ERROR_NOSUPPORT;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x69,
+				   data) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret p_init2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data[6] = { 0 };
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = tnr_dmd->create_param.xosc_cap;
+	data[1] = tnr_dmd->create_param.xosc_i;
+	switch (tnr_dmd->create_param.xtal_share_type) {
+	case CXD2880_TNRDMD_XTAL_SHARE_NONE:
+		data[2] = 0x01;
+		data[3] = 0x00;
+		break;
+	case CXD2880_TNRDMD_XTAL_SHARE_EXTREF:
+		data[2] = 0x00;
+		data[3] = 0x00;
+		break;
+	case CXD2880_TNRDMD_XTAL_SHARE_MASTER:
+		data[2] = 0x01;
+		data[3] = 0x01;
+		break;
+	case CXD2880_TNRDMD_XTAL_SHARE_SLAVE:
+		data[2] = 0x00;
+		data[3] = 0x01;
+		break;
+	default:
+		return CXD2880_RESULT_ERROR_ARG;
+	}
+	data[4] = 0x06;
+	data[5] = 0x00;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x13, data,
+				    6) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret p_init3(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data[2] = { 0 };
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	switch (tnr_dmd->diver_mode) {
+	case CXD2880_TNRDMD_DIVERMODE_SINGLE:
+		data[0] = 0x00;
+		break;
+	case CXD2880_TNRDMD_DIVERMODE_MAIN:
+		data[0] = 0x03;
+		break;
+	case CXD2880_TNRDMD_DIVERMODE_SUB:
+		data[0] = 0x02;
+		break;
+	default:
+		return CXD2880_RESULT_ERROR_ARG;
+	}
+
+	data[1] = 0x01;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x1F, data,
+				    2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret rf_init1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data[80] = { 0 };
+	u8 addr = 0;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x01;
+	data[1] = 0x00;
+	data[2] = 0x01;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x21, data,
+				    3) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x01;
+	data[1] = 0x01;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x17, data,
+				    2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->create_param.stationary_use) {
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x1A,
+					   0x06) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x4F,
+				   0x18) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x61,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x71,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x9D,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x7D,
+				   0x02) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x8F,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x8B,
+				   0xC6) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x9A,
+				   0x03) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x1C,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x00;
+	if ((tnr_dmd->create_param.is_cxd2881gg) &&
+	    (tnr_dmd->create_param.xtal_share_type ==
+		CXD2880_TNRDMD_XTAL_SHARE_SLAVE))
+		data[1] = 0x00;
+	else
+		data[1] = 0x1F;
+	data[2] = 0x0A;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0xB5, data,
+				    3) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0xB9,
+				   0x07) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x33,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0xC1,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0xC4,
+				   0x1E) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) {
+		data[0] = 0x34;
+		data[1] = 0x2C;
+	} else {
+		data[0] = 0x2F;
+		data[1] = 0x25;
+	}
+	data[2] = 0x15;
+	data[3] = 0x19;
+	data[4] = 0x1B;
+	data[5] = 0x15;
+	data[6] = 0x19;
+	data[7] = 0x1B;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0xD9, data,
+				    8) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x11) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x6C;
+	data[1] = 0x10;
+	data[2] = 0xA6;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x44, data,
+				    3) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x16;
+	data[1] = 0xA8;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x50, data,
+				    2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x00;
+	data[1] = 0x22;
+	data[2] = 0x00;
+	data[3] = 0x88;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x62, data,
+				    4) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x74,
+				   0x75) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x05;
+	data[1] = 0x05;
+	data[2] = 0x05;
+	data[3] = 0x05;
+	data[4] = 0x05;
+	data[5] = 0x05;
+	data[6] = 0x05;
+	data[7] = 0x05;
+	data[8] = 0x05;
+	data[9] = 0x04;
+	data[10] = 0x04;
+	data[11] = 0x04;
+	data[12] = 0x03;
+	data[13] = 0x03;
+	data[14] = 0x03;
+	data[15] = 0x04;
+	data[16] = 0x04;
+	data[17] = 0x05;
+	data[18] = 0x05;
+	data[19] = 0x05;
+	data[20] = 0x02;
+	data[21] = 0x02;
+	data[22] = 0x02;
+	data[23] = 0x02;
+	data[24] = 0x02;
+	data[25] = 0x02;
+	data[26] = 0x02;
+	data[27] = 0x02;
+	data[28] = 0x02;
+	data[29] = 0x03;
+	data[30] = 0x02;
+	data[31] = 0x01;
+	data[32] = 0x01;
+	data[33] = 0x01;
+	data[34] = 0x02;
+	data[35] = 0x02;
+	data[36] = 0x03;
+	data[37] = 0x04;
+	data[38] = 0x04;
+	data[39] = 0x04;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x7F, data,
+				    40) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x16) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x00;
+	data[1] = 0x71;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x10, data,
+				    2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x23,
+				   0x89) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0xFF;
+	data[1] = 0x00;
+	data[2] = 0x00;
+	data[3] = 0x00;
+	data[4] = 0x00;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x27, data,
+				    5) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x00;
+	data[1] = 0x00;
+	data[2] = 0x00;
+	data[3] = 0x00;
+	data[4] = 0x00;
+	data[5] = 0x01;
+	data[6] = 0x00;
+	data[7] = 0x01;
+	data[8] = 0x00;
+	data[9] = 0x02;
+	data[10] = 0x00;
+	data[11] = 0x63;
+	data[12] = 0x00;
+	data[13] = 0x00;
+	data[14] = 0x00;
+	data[15] = 0x03;
+	data[16] = 0x00;
+	data[17] = 0x04;
+	data[18] = 0x00;
+	data[19] = 0x04;
+	data[20] = 0x00;
+	data[21] = 0x06;
+	data[22] = 0x00;
+	data[23] = 0x06;
+	data[24] = 0x00;
+	data[25] = 0x08;
+	data[26] = 0x00;
+	data[27] = 0x09;
+	data[28] = 0x00;
+	data[29] = 0x0B;
+	data[30] = 0x00;
+	data[31] = 0x0B;
+	data[32] = 0x00;
+	data[33] = 0x0D;
+	data[34] = 0x00;
+	data[35] = 0x0D;
+	data[36] = 0x00;
+	data[37] = 0x0F;
+	data[38] = 0x00;
+	data[39] = 0x0F;
+	data[40] = 0x00;
+	data[41] = 0x0F;
+	data[42] = 0x00;
+	data[43] = 0x10;
+	data[44] = 0x00;
+	data[45] = 0x79;
+	data[46] = 0x00;
+	data[47] = 0x00;
+	data[48] = 0x00;
+	data[49] = 0x02;
+	data[50] = 0x00;
+	data[51] = 0x00;
+	data[52] = 0x00;
+	data[53] = 0x03;
+	data[54] = 0x00;
+	data[55] = 0x01;
+	data[56] = 0x00;
+	data[57] = 0x03;
+	data[58] = 0x00;
+	data[59] = 0x03;
+	data[60] = 0x00;
+	data[61] = 0x03;
+	data[62] = 0x00;
+	data[63] = 0x04;
+	data[64] = 0x00;
+	data[65] = 0x04;
+	data[66] = 0x00;
+	data[67] = 0x06;
+	data[68] = 0x00;
+	data[69] = 0x05;
+	data[70] = 0x00;
+	data[71] = 0x07;
+	data[72] = 0x00;
+	data[73] = 0x07;
+	data[74] = 0x00;
+	data[75] = 0x08;
+	data[76] = 0x00;
+	data[77] = 0x0A;
+	data[78] = 0x03;
+	data[79] = 0xE0;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x3A, data,
+				    80) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	data[0] = 0x03;
+	data[1] = 0xE0;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0xBC, data,
+				    2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x51,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0xC5,
+				   0x07) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x11) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x70,
+				   0xE9) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x76,
+				   0x0A) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x78,
+				   0x32) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x7A,
+				   0x46) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x7C,
+				   0x86) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x7E,
+				   0xA4) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0xE1,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->create_param.stationary_use) {
+		data[0] = 0x06;
+		data[1] = 0x07;
+		data[2] = 0x1A;
+	} else {
+		data[0] = 0x00;
+		data[1] = 0x08;
+		data[2] = 0x19;
+	}
+	data[3] = 0x0E;
+	data[4] = 0x09;
+	data[5] = 0x0E;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x12) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	for (addr = 0x10; addr < 0x9F; addr += 6) {
+		if (tnr_dmd->lna_thrs_tbl_air) {
+			u8 idx = 0;
+
+			idx = (addr - 0x10) / 6;
+			data[0] =
+			    tnr_dmd->lna_thrs_tbl_air->thrs[idx].off_on;
+			data[1] =
+			    tnr_dmd->lna_thrs_tbl_air->thrs[idx].on_off;
+		}
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_SYS, addr,
+					    data,
+					    6) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	data[0] = 0x00;
+	data[1] = 0x08;
+	if (tnr_dmd->create_param.stationary_use)
+		data[2] = 0x1A;
+	else
+		data[2] = 0x19;
+	data[3] = 0x0E;
+	data[4] = 0x09;
+	data[5] = 0x0E;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x13) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	for (addr = 0x10; addr < 0xCF; addr += 6) {
+		if (tnr_dmd->lna_thrs_tbl_cable) {
+			u8 idx = 0;
+
+			idx = (addr - 0x10) / 6;
+			data[0] =
+			    tnr_dmd->lna_thrs_tbl_cable->thrs[idx].off_on;
+			data[1] =
+			    tnr_dmd->lna_thrs_tbl_cable->thrs[idx].on_off;
+		}
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_SYS, addr,
+					    data,
+					    6) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x11) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x08;
+	data[1] = 0x09;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0xBD, data,
+				    2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x08;
+	data[1] = 0x09;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0xC4, data,
+				    2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x20;
+	data[1] = 0x20;
+	data[2] = 0x30;
+	data[3] = 0x41;
+	data[4] = 0x50;
+	data[5] = 0x5F;
+	data[6] = 0x6F;
+	data[7] = 0x80;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0xC9, data,
+				    8) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x14) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x15;
+	data[1] = 0x18;
+	data[2] = 0x00;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x10, data,
+				    3) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x15,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x16) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x00;
+	data[1] = 0x09;
+	data[2] = 0x00;
+	data[3] = 0x08;
+	data[4] = 0x00;
+	data[5] = 0x07;
+	data[6] = 0x00;
+	data[7] = 0x06;
+	data[8] = 0x00;
+	data[9] = 0x05;
+	data[10] = 0x00;
+	data[11] = 0x03;
+	data[12] = 0x00;
+	data[13] = 0x02;
+	data[14] = 0x00;
+	data[15] = 0x00;
+	data[16] = 0x00;
+	data[17] = 0x78;
+	data[18] = 0x00;
+	data[19] = 0x00;
+	data[20] = 0x00;
+	data[21] = 0x06;
+	data[22] = 0x00;
+	data[23] = 0x08;
+	data[24] = 0x00;
+	data[25] = 0x08;
+	data[26] = 0x00;
+	data[27] = 0x0C;
+	data[28] = 0x00;
+	data[29] = 0x0C;
+	data[30] = 0x00;
+	data[31] = 0x0D;
+	data[32] = 0x00;
+	data[33] = 0x0F;
+	data[34] = 0x00;
+	data[35] = 0x0E;
+	data[36] = 0x00;
+	data[37] = 0x0E;
+	data[38] = 0x00;
+	data[39] = 0x10;
+	data[40] = 0x00;
+	data[41] = 0x0F;
+	data[42] = 0x00;
+	data[43] = 0x0E;
+	data[44] = 0x00;
+	data[45] = 0x10;
+	data[46] = 0x00;
+	data[47] = 0x0F;
+	data[48] = 0x00;
+	data[49] = 0x0E;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x12, data,
+				    50) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	CXD2880_SLEEP(1);
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x0A) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x10, data,
+				   1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if ((data[0] & 0x01) == 0x00)
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x25,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	CXD2880_SLEEP(1);
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x0A) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x11, data,
+				   1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if ((data[0] & 0x01) == 0x00)
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x02,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x21,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0xE1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x8F,
+				   0x16) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x67,
+				   0x60) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x6A,
+				   0x0F) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x6C,
+				   0x17) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x00;
+	data[1] = 0xFE;
+	data[2] = 0xEE;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_DMD, 0x6E, data,
+				    3) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0xA1;
+	data[1] = 0x8B;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_DMD, 0x8D, data,
+				    2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x08;
+	data[1] = 0x09;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_DMD, 0x77, data,
+				    2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->create_param.stationary_use) {
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x80,
+					   0xAA) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0xE2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x41,
+				   0xA0) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x4B,
+				   0x68) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x21,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x10,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x25,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	CXD2880_SLEEP(1);
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x1A) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x10, data,
+				   1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if ((data[0] & 0x01) == 0x00)
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x14,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x26,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret rf_init2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data[5] = { 0 };
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x40;
+	data[1] = 0x40;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0xEA, data,
+				    2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	CXD2880_SLEEP(1);
+
+	data[0] = 0x00;
+	if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X)
+		data[1] = 0x00;
+	else
+		data[1] = 0x01;
+	data[2] = 0x01;
+	data[3] = 0x03;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x30, data,
+				    4) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x14) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x1B,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x21,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0xE1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0xD3,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x21,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+				enum cxd2880_dtv_sys sys, u32 freq_khz,
+				enum cxd2880_dtv_bandwidth bandwidth,
+				u8 is_cable, int shift_frequency_khz)
+{
+	u8 data[11] = { 0 };
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x10,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	data[0] = 0x00;
+	data[1] = 0x00;
+	data[2] = 0x0E;
+	data[3] = 0x00;
+	data[4] = 0x03;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0xE7, data,
+				    5) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	data[0] = 0x1F;
+	data[1] = 0x80;
+	data[2] = 0x18;
+	data[3] = 0x00;
+	data[4] = 0x07;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0xE7, data,
+				    5) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	CXD2880_SLEEP(1);
+
+	data[0] = 0x72;
+	data[1] = 0x81;
+	data[3] = 0x1D;
+	data[4] = 0x6F;
+	data[5] = 0x7E;
+	data[7] = 0x1C;
+	switch (sys) {
+	case CXD2880_DTV_SYS_DVBT:
+	case CXD2880_DTV_SYS_ISDBT:
+	case CXD2880_DTV_SYS_ISDBTSB:
+	case CXD2880_DTV_SYS_ISDBTMM_A:
+	case CXD2880_DTV_SYS_ISDBTMM_B:
+		data[2] = 0x94;
+		data[6] = 0x91;
+		break;
+	case CXD2880_DTV_SYS_DVBT2:
+		data[2] = 0x96;
+		data[6] = 0x93;
+		break;
+	default:
+		return CXD2880_RESULT_ERROR_ARG;
+	}
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x44, data,
+				    8) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x62,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x15) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x03;
+	data[1] = 0xE2;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x1E, data,
+				    2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	data[0] = (u8)(is_cable ? 0x01 : 0x00);
+	data[1] = 0x00;
+	data[2] = 0x6B;
+	data[3] = 0x4D;
+
+	switch (bandwidth) {
+	case CXD2880_DTV_BW_1_7_MHZ:
+		data[4] = 0x03;
+		break;
+	case CXD2880_DTV_BW_5_MHZ:
+	case CXD2880_DTV_BW_6_MHZ:
+		data[4] = 0x00;
+		break;
+	case CXD2880_DTV_BW_7_MHZ:
+		data[4] = 0x01;
+		break;
+	case CXD2880_DTV_BW_8_MHZ:
+		data[4] = 0x02;
+		break;
+	default:
+		return CXD2880_RESULT_ERROR_ARG;
+	}
+
+	data[5] = 0x00;
+
+	freq_khz += shift_frequency_khz;
+
+	data[6] = (u8)((freq_khz >> 16) & 0x0F);
+	data[7] = (u8)((freq_khz >> 8) & 0xFF);
+	data[8] = (u8)(freq_khz & 0xFF);
+	data[9] = 0xFF;
+	data[10] = 0xFE;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x52, data,
+				    11) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+				enum cxd2880_dtv_bandwidth bandwidth,
+				enum cxd2880_tnrdmd_clockmode clk_mode,
+				int shift_frequency_khz)
+{
+	u8 data[3] = { 0 };
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x11) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	data[0] = 0x01;
+	data[1] = 0x0E;
+	data[2] = 0x01;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x2D, data,
+				    3) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x1A) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x29,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x2C, data,
+				   1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x60,
+				   data[0]) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x62,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x11) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x2D,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x2F,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x10,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x21,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (shift_frequency_khz != 0) {
+		int shift_freq = 0;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0xE1) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x60, data,
+					   2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		shift_freq = shift_frequency_khz * 1000;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+		default:
+			if (shift_freq >= 0)
+				shift_freq = (shift_freq + 183 / 2) / 183;
+			else
+				shift_freq = (shift_freq - 183 / 2) / 183;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			if (shift_freq >= 0)
+				shift_freq = (shift_freq + 178 / 2) / 178;
+			else
+				shift_freq = (shift_freq - 178 / 2) / 178;
+			break;
+		}
+
+		shift_freq +=
+		    cxd2880_convert2s_complement((data[0] << 8) | data[1], 16);
+
+		if (shift_freq > 32767)
+			shift_freq = 32767;
+		else if (shift_freq < -32768)
+			shift_freq = -32768;
+
+		data[0] = (u8)(((u32)shift_freq >> 8) & 0xFF);
+		data[1] = (u8)((u32)shift_freq & 0xFF);
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0x60, data,
+					    2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x69, data,
+					   1) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		shift_freq = -shift_frequency_khz;
+
+		if (bandwidth == CXD2880_DTV_BW_1_7_MHZ) {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+			default:
+				if (shift_freq >= 0)
+					shift_freq =
+					    (shift_freq * 1000 +
+					     17578 / 2) / 17578;
+				else
+					shift_freq =
+					    (shift_freq * 1000 -
+					     17578 / 2) / 17578;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				if (shift_freq >= 0)
+					shift_freq =
+					    (shift_freq * 1000 +
+					     17090 / 2) / 17090;
+				else
+					shift_freq =
+					    (shift_freq * 1000 -
+					     17090 / 2) / 17090;
+				break;
+			}
+		} else {
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+			default:
+				if (shift_freq >= 0)
+					shift_freq =
+					    (shift_freq * 1000 +
+					     35156 / 2) / 35156;
+				else
+					shift_freq =
+					    (shift_freq * 1000 -
+					     35156 / 2) / 35156;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				if (shift_freq >= 0)
+					shift_freq =
+					    (shift_freq * 1000 +
+					     34180 / 2) / 34180;
+				else
+					shift_freq =
+					    (shift_freq * 1000 -
+					     34180 / 2) / 34180;
+				break;
+			}
+		}
+
+		shift_freq += cxd2880_convert2s_complement(data[0], 8);
+
+		if (shift_freq > 127)
+			shift_freq = 127;
+		else if (shift_freq < -128)
+			shift_freq = -128;
+
+		data[0] = (u8)((u32)shift_freq & 0xFF);
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x69,
+					   data[0]) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->create_param.stationary_use) {
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0xE1) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x8A,
+					   0x87) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x21,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_tune3(struct cxd2880_tnrdmd *tnr_dmd,
+				enum cxd2880_dtv_sys sys,
+				u8 en_fef_intmtnt_ctrl)
+{
+	u8 data[6] = { 0 };
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x21,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0xE2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x41,
+				   0xA0) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x21,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0xFE,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if ((sys == CXD2880_DTV_SYS_DVBT2) && en_fef_intmtnt_ctrl) {
+		data[0] = 0x01;
+		data[1] = 0x01;
+		data[2] = 0x01;
+		data[3] = 0x01;
+		data[4] = 0x01;
+		data[5] = 0x01;
+	} else {
+		data[0] = 0x00;
+		data[1] = 0x00;
+		data[2] = 0x00;
+		data[3] = 0x00;
+		data[4] = 0x00;
+		data[5] = 0x00;
+	}
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0xEF, data,
+				    6) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x2D) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if ((sys == CXD2880_DTV_SYS_DVBT2) && en_fef_intmtnt_ctrl)
+		data[0] = 0x00;
+	else
+		data[0] = 0x01;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0xB1,
+				   data[0]) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_tune4(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data[2] = { 0 };
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	{
+		if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+						      CXD2880_IO_TGT_SYS, 0x00,
+						      0x00) !=
+		    CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		data[0] = 0x14;
+		data[1] = 0x00;
+		if (tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+						       CXD2880_IO_TGT_SYS, 0x55,
+						       data,
+						       2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	{
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x00,
+					   0x00) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		data[0] = 0x0B;
+		data[1] = 0xFF;
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_SYS, 0x53, data,
+					    2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x57,
+					   0x01) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		data[0] = 0x0B;
+		data[1] = 0xFF;
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_SYS, 0x55, data,
+					    2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	{
+		if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+						      CXD2880_IO_TGT_SYS, 0x00,
+						      0x00) !=
+		    CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		data[0] = 0x14;
+		data[1] = 0x00;
+		if (tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+						       CXD2880_IO_TGT_SYS, 0x53,
+						       data,
+						       2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+						      CXD2880_IO_TGT_SYS, 0x57,
+						      0x02) !=
+		    CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0xFE,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+					      CXD2880_IO_TGT_DMD, 0x00,
+					      0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+					      CXD2880_IO_TGT_DMD, 0xFE,
+					      0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_sleep1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data[3] = { 0 };
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	{
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x00,
+					   0x00) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x57,
+					   0x03) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		data[0] = 0x00;
+		data[1] = 0x00;
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_SYS, 0x53, data,
+					    2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	{
+		if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+						      CXD2880_IO_TGT_SYS, 0x00,
+						      0x00) !=
+		    CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		data[0] = 0x1F;
+		data[1] = 0xFF;
+		data[2] = 0x03;
+		if (tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+						       CXD2880_IO_TGT_SYS, 0x55,
+						       data,
+						       3) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		data[0] = 0x00;
+		data[1] = 0x00;
+		if (tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+						       CXD2880_IO_TGT_SYS, 0x53,
+						       data,
+						       2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	{
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x00,
+					   0x00) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		data[0] = 0x1F;
+		data[1] = 0xFF;
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_SYS, 0x55, data,
+					    2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_sleep2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 data = 0;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x2D) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0xB1,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	CXD2880_SLEEP(1);
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0xB2, &data,
+				   1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if ((data & 0x01) == 0x00)
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0xF4,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0xF3,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0xF2,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0xF1,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0xF0,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0xEF,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_sleep3(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0xFD,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_sleep4(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x21,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0xE2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x41,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x21,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret spll_reset(struct cxd2880_tnrdmd *tnr_dmd,
+				   enum cxd2880_tnrdmd_clockmode clockmode)
+{
+	u8 data[4] = { 0 };
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x29,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x28,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x27,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x26,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x10,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x27,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x22,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	switch (clockmode) {
+	case CXD2880_TNRDMD_CLOCKMODE_A:
+		data[0] = 0x00;
+		break;
+
+	case CXD2880_TNRDMD_CLOCKMODE_B:
+		data[0] = 0x01;
+		break;
+
+	case CXD2880_TNRDMD_CLOCKMODE_C:
+		data[0] = 0x02;
+		break;
+
+	default:
+		return CXD2880_RESULT_ERROR_ARG;
+	}
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x30,
+				   data[0]) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x22,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	CXD2880_SLEEP(2);
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x0A) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x10, data,
+				   1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if ((data[0] & 0x01) == 0x00)
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x27,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	CXD2880_SLEEP(1);
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x10,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x00;
+	data[1] = 0x00;
+	data[2] = 0x00;
+	data[3] = 0x00;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x26, data,
+				    4) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret t_power_x(struct cxd2880_tnrdmd *tnr_dmd, u8 on)
+{
+	u8 data[3] = { 0 };
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x29,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x28,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x27,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x10,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x27,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x25,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (on) {
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x2B,
+					   0x01) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		CXD2880_SLEEP(1);
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x00,
+					   0x0A) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x12, data,
+					   1) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		if ((data[0] & 0x01) == 0)
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x00,
+					   0x00) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x2A,
+					   0x00) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	} else {
+		data[0] = 0x03;
+		data[1] = 0x00;
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_SYS, 0x2A, data,
+					    2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		CXD2880_SLEEP(1);
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x00,
+					   0x0A) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x13, data,
+					   1) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+		if ((data[0] & 0x01) == 0)
+			return CXD2880_RESULT_ERROR_HW_STATE;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x25,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	CXD2880_SLEEP(1);
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x0A) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x11, data,
+				   1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if ((data[0] & 0x01) == 0)
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x27,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	CXD2880_SLEEP(1);
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x10,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	data[0] = 0x00;
+	data[1] = 0x00;
+	data[2] = 0x00;
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x27, data,
+				    3) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+struct cxd2880_tnrdmd_ts_clk_cfg {
+	u8 srl_clk_mode;
+	u8 srl_duty_mode;
+	u8 ts_clk_period;
+};
+
+static enum cxd2880_ret set_ts_clk_mode_and_freq(struct cxd2880_tnrdmd *tnr_dmd,
+						 enum cxd2880_dtv_sys sys)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	u8 backwards_compatible = 0;
+	struct cxd2880_tnrdmd_ts_clk_cfg ts_clk_cfg;
+
+	const struct cxd2880_tnrdmd_ts_clk_cfg srl_ts_clk_stgs[2][2] = {
+	{
+		{3, 1, 8,},
+		{0, 2, 16,}
+	},
+	{
+		{1, 1, 8,},
+		{2, 2, 16,}
+	}
+	};
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	{
+		u8 ts_rate_ctrl_off = 0;
+		u8 ts_in_off = 0;
+		u8 ts_clk_manaul_on = 0;
+
+		if ((sys == CXD2880_DTV_SYS_ISDBT) ||
+		    (sys == CXD2880_DTV_SYS_ISDBTSB) ||
+		    (sys == CXD2880_DTV_SYS_ISDBTMM_A) ||
+		    (sys == CXD2880_DTV_SYS_ISDBTMM_B)) {
+			backwards_compatible = 0;
+			ts_rate_ctrl_off = 1;
+			ts_in_off = 0;
+		} else if (tnr_dmd->is_ts_backwards_compatible_mode) {
+			backwards_compatible = 1;
+			ts_rate_ctrl_off = 1;
+			ts_in_off = 1;
+		} else {
+			backwards_compatible = 0;
+			ts_rate_ctrl_off = 0;
+			ts_in_off = 0;
+		}
+
+		if (tnr_dmd->ts_byte_clk_manual_setting) {
+			ts_clk_manaul_on = 1;
+			ts_rate_ctrl_off = 0;
+		}
+
+		ret =
+		    cxd2880_io_set_reg_bits(tnr_dmd->io, CXD2880_IO_TGT_DMD,
+					    0xD3, ts_rate_ctrl_off, 0x01);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		ret =
+		    cxd2880_io_set_reg_bits(tnr_dmd->io, CXD2880_IO_TGT_DMD,
+					    0xDE, ts_in_off, 0x01);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		ret =
+		    cxd2880_io_set_reg_bits(tnr_dmd->io, CXD2880_IO_TGT_DMD,
+					    0xDA, ts_clk_manaul_on, 0x01);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	ts_clk_cfg =
+	    srl_ts_clk_stgs[tnr_dmd->srl_ts_clk_mod_cnts]
+			[(u8)tnr_dmd->srl_ts_clk_frq];
+
+	if (tnr_dmd->ts_byte_clk_manual_setting)
+		ts_clk_cfg.ts_clk_period = tnr_dmd->ts_byte_clk_manual_setting;
+
+	ret =
+	    cxd2880_io_set_reg_bits(tnr_dmd->io, CXD2880_IO_TGT_DMD, 0xC4,
+				    ts_clk_cfg.srl_clk_mode, 0x03);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret =
+	    cxd2880_io_set_reg_bits(tnr_dmd->io, CXD2880_IO_TGT_DMD, 0xD1,
+				    ts_clk_cfg.srl_duty_mode, 0x03);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+				     CXD2880_IO_TGT_DMD, 0xD9,
+				     ts_clk_cfg.ts_clk_period);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	{
+		u8 data = (u8)(backwards_compatible ? 0x00 : 0x01);
+
+		if (sys == CXD2880_DTV_SYS_DVBT) {
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_DMD, 0x00,
+						   0x10) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+
+			ret =
+			    cxd2880_io_set_reg_bits(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x66,
+						    data, 0x01);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+static enum cxd2880_ret pid_ftr_setting(struct cxd2880_tnrdmd *tnr_dmd,
+					struct cxd2880_tnrdmd_pid_ftr_cfg
+					*pid_ftr_cfg)
+{
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (!pid_ftr_cfg) {
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x50,
+					   0x02) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	} else {
+		u8 data[65];
+
+		data[0] = (u8)(pid_ftr_cfg->is_negative ? 0x01 : 0x00);
+		{
+			int i = 0;
+
+			for (i = 0; i < 32; i++) {
+				if (pid_ftr_cfg->pid_cfg[i].is_en) {
+					data[1 + (i * 2)] =
+					    (u8)((u8)
+					    (pid_ftr_cfg->pid_cfg[i].pid
+					     >> 8) | 0x20);
+					data[2 + (i * 2)] =
+					    (u8)(pid_ftr_cfg->pid_cfg[i].pid
+					     & 0xFF);
+				} else {
+					data[1 + (i * 2)] = 0x00;
+					data[2 + (i * 2)] = 0x00;
+				}
+			}
+		}
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0x50, data,
+					    65) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret load_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	u8 i;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     tnr_dmd->cfg_mem[i].tgt,
+					     0x00, tnr_dmd->cfg_mem[i].bank);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+					      tnr_dmd->cfg_mem[i].tgt,
+					      tnr_dmd->cfg_mem[i].address,
+					      tnr_dmd->cfg_mem[i].value,
+					      tnr_dmd->cfg_mem[i].bit_mask);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	return ret;
+}
+
+static enum cxd2880_ret set_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd,
+				    enum cxd2880_io_tgt tgt,
+				    u8 bank, u8 address, u8 value, u8 bit_mask)
+{
+	u8 i;
+	u8 value_stored = 0;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) {
+		if ((value_stored == 0) &&
+		    (tnr_dmd->cfg_mem[i].tgt == tgt) &&
+		    (tnr_dmd->cfg_mem[i].bank == bank) &&
+		    (tnr_dmd->cfg_mem[i].address == address)) {
+			tnr_dmd->cfg_mem[i].value &= ~bit_mask;
+			tnr_dmd->cfg_mem[i].value |= (value & bit_mask);
+
+			tnr_dmd->cfg_mem[i].bit_mask |= bit_mask;
+
+			value_stored = 1;
+		}
+	}
+
+	if (value_stored == 0) {
+		if (tnr_dmd->cfg_mem_last_entry <
+		    CXD2880_TNRDMD_MAX_CFG_MEM_COUNT) {
+			tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].tgt = tgt;
+			tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bank =
+			    bank;
+			tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].address =
+			    address;
+			tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].value =
+			    (value & bit_mask);
+			tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bit_mask =
+			    bit_mask;
+			tnr_dmd->cfg_mem_last_entry++;
+		} else {
+			return CXD2880_RESULT_ERROR_OVERFLOW;
+		}
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd,
+				       struct cxd2880_io *io,
+				       struct cxd2880_tnrdmd_create_param
+				       *create_param)
+{
+	if ((!tnr_dmd) || (!io) || (!create_param))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	cxd2880_memset(tnr_dmd, 0, sizeof(struct cxd2880_tnrdmd));
+
+	tnr_dmd->io = io;
+	tnr_dmd->create_param = *create_param;
+
+	tnr_dmd->diver_mode = CXD2880_TNRDMD_DIVERMODE_SINGLE;
+	tnr_dmd->diver_sub = NULL;
+
+	tnr_dmd->srl_ts_clk_mod_cnts = 1;
+	tnr_dmd->en_fef_intmtnt_base = 1;
+	tnr_dmd->en_fef_intmtnt_lite = 1;
+	tnr_dmd->rf_lvl_cmpstn = NULL;
+	tnr_dmd->lna_thrs_tbl_air = NULL;
+	tnr_dmd->lna_thrs_tbl_cable = NULL;
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd
+					     *tnr_dmd_main,
+					     struct cxd2880_io *io_main,
+					     struct cxd2880_tnrdmd *tnr_dmd_sub,
+					     struct cxd2880_io *io_sub,
+					     struct
+					     cxd2880_tnrdmd_diver_create_param
+					     *create_param)
+{
+	if ((!tnr_dmd_main) || (!io_main) || (!tnr_dmd_sub) || (!io_sub) ||
+	    (!create_param))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	cxd2880_memset(tnr_dmd_main, 0, sizeof(struct cxd2880_tnrdmd));
+	cxd2880_memset(tnr_dmd_sub, 0, sizeof(struct cxd2880_tnrdmd));
+
+	tnr_dmd_main->io = io_main;
+	tnr_dmd_main->diver_mode = CXD2880_TNRDMD_DIVERMODE_MAIN;
+	tnr_dmd_main->diver_sub = tnr_dmd_sub;
+	tnr_dmd_main->create_param.en_internal_ldo =
+	    create_param->en_internal_ldo;
+	tnr_dmd_main->create_param.ts_output_if = create_param->ts_output_if;
+	tnr_dmd_main->create_param.xtal_share_type =
+	    CXD2880_TNRDMD_XTAL_SHARE_MASTER;
+	tnr_dmd_main->create_param.xosc_cap = create_param->xosc_cap_main;
+	tnr_dmd_main->create_param.xosc_i = create_param->xosc_i_main;
+	tnr_dmd_main->create_param.is_cxd2881gg = create_param->is_cxd2881gg;
+	tnr_dmd_main->create_param.stationary_use =
+	    create_param->stationary_use;
+
+	tnr_dmd_sub->io = io_sub;
+	tnr_dmd_sub->diver_mode = CXD2880_TNRDMD_DIVERMODE_SUB;
+	tnr_dmd_sub->diver_sub = NULL;
+	tnr_dmd_sub->create_param.en_internal_ldo =
+	    create_param->en_internal_ldo;
+	tnr_dmd_sub->create_param.ts_output_if = create_param->ts_output_if;
+	tnr_dmd_sub->create_param.xtal_share_type =
+	    CXD2880_TNRDMD_XTAL_SHARE_SLAVE;
+	tnr_dmd_sub->create_param.xosc_cap = 0;
+	tnr_dmd_sub->create_param.xosc_i = create_param->xosc_i_sub;
+	tnr_dmd_sub->create_param.is_cxd2881gg = create_param->is_cxd2881gg;
+	tnr_dmd_sub->create_param.stationary_use = create_param->stationary_use;
+
+	tnr_dmd_main->srl_ts_clk_mod_cnts = 1;
+	tnr_dmd_main->en_fef_intmtnt_base = 1;
+	tnr_dmd_main->en_fef_intmtnt_lite = 1;
+	tnr_dmd_main->rf_lvl_cmpstn = NULL;
+	tnr_dmd_main->lna_thrs_tbl_air = NULL;
+	tnr_dmd_main->lna_thrs_tbl_cable = NULL;
+
+	tnr_dmd_sub->srl_ts_clk_mod_cnts = 1;
+	tnr_dmd_sub->en_fef_intmtnt_base = 1;
+	tnr_dmd_sub->en_fef_intmtnt_lite = 1;
+	tnr_dmd_sub->rf_lvl_cmpstn = NULL;
+	tnr_dmd_sub->lna_thrs_tbl_air = NULL;
+	tnr_dmd_sub->lna_thrs_tbl_cable = NULL;
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	tnr_dmd->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
+	tnr_dmd->state = CXD2880_TNRDMD_STATE_UNKNOWN;
+	tnr_dmd->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN;
+	tnr_dmd->frequency_khz = 0;
+	tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN;
+	tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+	tnr_dmd->scan_mode = 0;
+	cxd2880_atomic_set(&tnr_dmd->cancel, 0);
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		tnr_dmd->diver_sub->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
+		tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_UNKNOWN;
+		tnr_dmd->diver_sub->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN;
+		tnr_dmd->diver_sub->frequency_khz = 0;
+		tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN;
+		tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+		tnr_dmd->diver_sub->scan_mode = 0;
+		cxd2880_atomic_set(&tnr_dmd->diver_sub->cancel, 0);
+	}
+
+	ret = cxd2880_tnrdmd_chip_id(tnr_dmd, &tnr_dmd->chip_id);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->chip_id))
+		return CXD2880_RESULT_ERROR_NOSUPPORT;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret =
+		    cxd2880_tnrdmd_chip_id(tnr_dmd->diver_sub,
+					   &tnr_dmd->diver_sub->chip_id);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->diver_sub->chip_id))
+			return CXD2880_RESULT_ERROR_NOSUPPORT;
+	}
+
+	ret = p_init1(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = p_init1(tnr_dmd->diver_sub);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	CXD2880_SLEEP(1);
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = p_init2(tnr_dmd->diver_sub);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	ret = p_init2(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	CXD2880_SLEEP(5);
+
+	ret = p_init3(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = p_init3(tnr_dmd->diver_sub);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	ret = rf_init1(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = rf_init1(tnr_dmd->diver_sub);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	{
+		u8 cpu_task_completed = 0;
+
+		ret =
+		    cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+						     &cpu_task_completed);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (!cpu_task_completed)
+			return CXD2880_RESULT_ERROR_HW_STATE;
+	}
+
+	ret = rf_init2(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = rf_init2(tnr_dmd->diver_sub);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	ret = load_cfg_mem(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = load_cfg_mem(tnr_dmd->diver_sub);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+		tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd
+							  *tnr_dmd,
+							  u8 *task_completed)
+{
+	u16 cpu_status = 0;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!task_completed))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret = cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd, &cpu_status);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		if (cpu_status == 0)
+			*task_completed = 1;
+		else
+			*task_completed = 0;
+
+		return ret;
+	}
+	if (cpu_status != 0) {
+		*task_completed = 0;
+		return ret;
+	}
+
+	ret = cxd2880_tnrdmd_mon_internal_cpu_status_sub(tnr_dmd, &cpu_status);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (cpu_status == 0)
+		*task_completed = 1;
+	else
+		*task_completed = 0;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd
+						     *tnr_dmd,
+						     enum cxd2880_dtv_sys sys,
+						     u32 frequency_khz,
+						     enum cxd2880_dtv_bandwidth
+						     bandwidth, u8 one_seg_opt,
+						     u8 one_seg_opt_shft_dir)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (frequency_khz < 4000)
+		return CXD2880_RESULT_ERROR_RANGE;
+
+	ret = cxd2880_tnrdmd_sleep(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	{
+		u8 data = 0;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x00,
+					   0x00) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x2B, &data,
+					   1) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		switch (sys) {
+		case CXD2880_DTV_SYS_DVBT:
+		case CXD2880_DTV_SYS_ISDBT:
+		case CXD2880_DTV_SYS_ISDBTSB:
+		case CXD2880_DTV_SYS_ISDBTMM_A:
+		case CXD2880_DTV_SYS_ISDBTMM_B:
+			if (data == 0x00) {
+				ret = t_power_x(tnr_dmd, 1);
+				if (ret != CXD2880_RESULT_OK)
+					return ret;
+
+				if (tnr_dmd->diver_mode ==
+				    CXD2880_TNRDMD_DIVERMODE_MAIN) {
+					ret = t_power_x(tnr_dmd->diver_sub, 1);
+					if (ret != CXD2880_RESULT_OK)
+						return ret;
+				}
+			}
+			break;
+
+		case CXD2880_DTV_SYS_DVBT2:
+			if (data == 0x01) {
+				ret = t_power_x(tnr_dmd, 0);
+				if (ret != CXD2880_RESULT_OK)
+					return ret;
+
+				if (tnr_dmd->diver_mode ==
+				    CXD2880_TNRDMD_DIVERMODE_MAIN) {
+					ret = t_power_x(tnr_dmd->diver_sub, 0);
+					if (ret != CXD2880_RESULT_OK)
+						return ret;
+				}
+			}
+			break;
+
+		default:
+			return CXD2880_RESULT_ERROR_ARG;
+		}
+	}
+
+	{
+		enum cxd2880_tnrdmd_clockmode new_clk_mode =
+		    CXD2880_TNRDMD_CLOCKMODE_A;
+
+		ret = spll_reset(tnr_dmd, new_clk_mode);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		tnr_dmd->clk_mode = new_clk_mode;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			ret = spll_reset(tnr_dmd->diver_sub, new_clk_mode);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+
+			tnr_dmd->diver_sub->clk_mode = new_clk_mode;
+		}
+
+		ret = load_cfg_mem(tnr_dmd);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			ret = load_cfg_mem(tnr_dmd->diver_sub);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+		}
+	}
+
+	{
+		int shift_frequency_khz = 0;
+
+		if (one_seg_opt) {
+			if (tnr_dmd->diver_mode ==
+			    CXD2880_TNRDMD_DIVERMODE_MAIN) {
+				shift_frequency_khz = 350;
+			} else {
+				if (one_seg_opt_shft_dir)
+					shift_frequency_khz = 350;
+				else
+					shift_frequency_khz = -350;
+
+				if (tnr_dmd->create_param.xtal_share_type ==
+				    CXD2880_TNRDMD_XTAL_SHARE_SLAVE)
+					shift_frequency_khz *= -1;
+			}
+		} else {
+			if (tnr_dmd->diver_mode ==
+			    CXD2880_TNRDMD_DIVERMODE_MAIN) {
+				shift_frequency_khz = 150;
+			} else {
+				switch (tnr_dmd->create_param.xtal_share_type) {
+				case CXD2880_TNRDMD_XTAL_SHARE_NONE:
+				case CXD2880_TNRDMD_XTAL_SHARE_EXTREF:
+				default:
+					shift_frequency_khz = 0;
+					break;
+				case CXD2880_TNRDMD_XTAL_SHARE_MASTER:
+					shift_frequency_khz = 150;
+					break;
+				case CXD2880_TNRDMD_XTAL_SHARE_SLAVE:
+					shift_frequency_khz = -150;
+					break;
+				}
+			}
+		}
+
+		ret =
+		    x_tune1(tnr_dmd, sys, frequency_khz, bandwidth,
+			    tnr_dmd->is_cable_input, shift_frequency_khz);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			ret =
+			    x_tune1(tnr_dmd->diver_sub, sys, frequency_khz,
+				    bandwidth, tnr_dmd->is_cable_input,
+				    -shift_frequency_khz);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+		}
+
+		CXD2880_SLEEP(10);
+
+		{
+			u8 cpu_task_completed = 0;
+
+			ret =
+			    cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+						     &cpu_task_completed);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+
+			if (!cpu_task_completed)
+				return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		ret =
+		    x_tune2(tnr_dmd, bandwidth, tnr_dmd->clk_mode,
+			    shift_frequency_khz);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			ret =
+			    x_tune2(tnr_dmd->diver_sub, bandwidth,
+				    tnr_dmd->diver_sub->clk_mode,
+				    -shift_frequency_khz);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+		}
+	}
+
+	if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS) {
+		ret = set_ts_clk_mode_and_freq(tnr_dmd, sys);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	} else {
+		struct cxd2880_tnrdmd_pid_ftr_cfg *pid_ftr_cfg;
+
+		if (tnr_dmd->pid_ftr_cfg_en)
+			pid_ftr_cfg = &tnr_dmd->pid_ftr_cfg;
+		else
+			pid_ftr_cfg = NULL;
+
+		ret = pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd
+						     *tnr_dmd,
+						     enum cxd2880_dtv_sys sys,
+						     u8 en_fef_intmtnt_ctrl)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret = x_tune3(tnr_dmd, sys, en_fef_intmtnt_ctrl);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = x_tune3(tnr_dmd->diver_sub, sys, en_fef_intmtnt_ctrl);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = x_tune4(tnr_dmd);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	ret = cxd2880_tnrdmd_set_ts_output(tnr_dmd, 1);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state == CXD2880_TNRDMD_STATE_SLEEP) {
+	} else if (tnr_dmd->state == CXD2880_TNRDMD_STATE_ACTIVE) {
+		ret = cxd2880_tnrdmd_set_ts_output(tnr_dmd, 0);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			ret = x_sleep1(tnr_dmd);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+		}
+
+		ret = x_sleep2(tnr_dmd);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			ret = x_sleep2(tnr_dmd->diver_sub);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+		}
+
+		switch (tnr_dmd->sys) {
+		case CXD2880_DTV_SYS_DVBT:
+			ret = cxd2880_tnrdmd_dvbt_sleep_setting(tnr_dmd);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+			break;
+
+		case CXD2880_DTV_SYS_DVBT2:
+			ret = cxd2880_tnrdmd_dvbt2_sleep_setting(tnr_dmd);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+			break;
+
+		default:
+			return CXD2880_RESULT_ERROR_SW_STATE;
+		}
+
+		ret = x_sleep3(tnr_dmd);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			ret = x_sleep3(tnr_dmd->diver_sub);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+		}
+
+		ret = x_sleep4(tnr_dmd);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			ret = x_sleep4(tnr_dmd->diver_sub);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+		}
+
+		tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP;
+		tnr_dmd->frequency_khz = 0;
+		tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN;
+		tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP;
+			tnr_dmd->diver_sub->frequency_khz = 0;
+			tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN;
+			tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+		}
+	} else {
+		return CXD2880_RESULT_ERROR_SW_STATE;
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+					enum cxd2880_tnrdmd_cfg_id id,
+					int value)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	u8 data[2] = { 0 };
+	u8 need_sub_setting = 0;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	switch (id) {
+	case CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xC4,
+							 (u8)(value ? 0x00 :
+							       0x10), 0x10);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xC5,
+							 (u8)(value ? 0x00 :
+							       0x02), 0x02);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xC5,
+							 (u8)(value ? 0x00 :
+							       0x04), 0x04);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xCB,
+							 (u8)(value ? 0x00 :
+							       0x01), 0x01);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xC5,
+							 (u8)(value ? 0x01 :
+							       0x00), 0x01);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSCLK_CONT:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		tnr_dmd->srl_ts_clk_mod_cnts = (u8)(value ? 0x01 : 0x00);
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSCLK_MASK:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		if ((value < 0) || (value > 0x1F))
+			return CXD2880_RESULT_ERROR_RANGE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xC6, (u8)value,
+							 0x1F);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSVALID_MASK:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		if ((value < 0) || (value > 0x1F))
+			return CXD2880_RESULT_ERROR_RANGE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xC8, (u8)value,
+							 0x1F);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSERR_MASK:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		if ((value < 0) || (value > 0x1F))
+			return CXD2880_RESULT_ERROR_RANGE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xC9, (u8)value,
+							 0x1F);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSERR_VALID_DIS:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x91,
+							 (u8)(value ? 0x01 :
+							       0x00), 0x01);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSPIN_CURRENT:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_SYS,
+							 0x00, 0x51, (u8)value,
+							 0x3F);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_SYS,
+							 0x00, 0x50,
+							 (u8)(value ? 0x80 :
+							       0x00), 0x80);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSPIN_PULLUP:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_SYS,
+							 0x00, 0x50, (u8)value,
+							 0x3F);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSCLK_FREQ:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		if ((value < 0) || (value > 1))
+			return CXD2880_RESULT_ERROR_RANGE;
+
+		tnr_dmd->srl_ts_clk_frq =
+		    (enum cxd2880_tnrdmd_serial_ts_clk)value;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		if ((value < 0) || (value > 0xFF))
+			return CXD2880_RESULT_ERROR_RANGE;
+
+		tnr_dmd->ts_byte_clk_manual_setting = (u8)value;
+
+		break;
+
+	case CXD2880_TNRDMD_CFG_TS_PACKET_GAP:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		if ((value < 0) || (value > 7))
+			return CXD2880_RESULT_ERROR_RANGE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0xD6, (u8)value,
+							 0x07);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		break;
+
+	case CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE:
+		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+			return CXD2880_RESULT_ERROR_SW_STATE;
+
+		tnr_dmd->is_ts_backwards_compatible_mode = (u8)(value ? 1 : 0);
+
+		break;
+
+	case CXD2880_TNRDMD_CFG_PWM_VALUE:
+		if ((value < 0) || (value > 0x1000))
+			return CXD2880_RESULT_ERROR_RANGE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x22,
+							 (u8)(value ? 0x01 :
+							       0x00), 0x01);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		{
+			u8 data[2];
+
+			data[0] = (u8)(((u16)value >> 8) & 0x1F);
+			data[1] = (u8)((u16)value & 0xFF);
+
+			ret =
+			    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x23,
+							 data[0], 0x1F);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+
+			ret =
+			    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x24,
+							 data[1], 0xFF);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+		}
+
+		break;
+
+	case CXD2880_TNRDMD_CFG_INTERRUPT:
+		data[0] = (u8)((value >> 8) & 0xFF);
+		data[1] = (u8)(value & 0xFF);
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_SYS,
+							 0x00, 0x48, data[0],
+							 0xFF);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_SYS,
+							 0x00, 0x49, data[1],
+							 0xFF);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL:
+		data[0] = (u8)(value & 0x07);
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_SYS,
+							 0x00, 0x4A, data[0],
+							 0x07);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL:
+		data[0] = (u8)((value & 0x07) << 3);
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_SYS,
+							 0x00, 0x4A, data[0],
+							 0x38);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE:
+		if ((value < (int)CXD2880_TNRDMD_CLOCKMODE_UNKNOWN) ||
+		    (value > (int)CXD2880_TNRDMD_CLOCKMODE_C))
+			return CXD2880_RESULT_ERROR_RANGE;
+		tnr_dmd->fixed_clk_mode = (enum cxd2880_tnrdmd_clockmode)value;
+		break;
+
+	case CXD2880_TNRDMD_CFG_CABLE_INPUT:
+		tnr_dmd->is_cable_input = (u8)(value ? 1 : 0);
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE:
+		tnr_dmd->en_fef_intmtnt_base = (u8)(value ? 1 : 0);
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE:
+		tnr_dmd->en_fef_intmtnt_lite = (u8)(value ? 1 : 0);
+		break;
+
+	case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS:
+		data[0] = (u8)((value >> 8) & 0x07);
+		data[1] = (u8)(value & 0xFF);
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x99, data[0],
+							 0x07);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x9A, data[1],
+							 0xFF);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS:
+		data[0] = (u8)((value >> 8) & 0x07);
+		data[1] = (u8)(value & 0xFF);
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x9B, data[0],
+							 0x07);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x9C, data[1],
+							 0xFF);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS:
+		data[0] = (u8)((value >> 8) & 0x07);
+		data[1] = (u8)(value & 0xFF);
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x9D, data[0],
+							 0x07);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x00, 0x9E, data[1],
+							 0xFF);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST:
+		tnr_dmd->blind_tune_dvbt2_first = (u8)(value ? 1 : 0);
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD:
+		if ((value < 0) || (value > 31))
+			return CXD2880_RESULT_ERROR_RANGE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x10, 0x60,
+							 (u8)(value & 0x1F),
+							 0x1F);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD:
+		if ((value < 0) || (value > 7))
+			return CXD2880_RESULT_ERROR_RANGE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x10, 0x6F,
+							 (u8)(value & 0x07),
+							 0x07);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT2_BBER_MES:
+		if ((value < 0) || (value > 15))
+			return CXD2880_RESULT_ERROR_RANGE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x20, 0x72,
+							 (u8)(value & 0x0F),
+							 0x0F);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT2_LBER_MES:
+		if ((value < 0) || (value > 15))
+			return CXD2880_RESULT_ERROR_RANGE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x20, 0x6F,
+							 (u8)(value & 0x0F),
+							 0x0F);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT_PER_MES:
+		if ((value < 0) || (value > 15))
+			return CXD2880_RESULT_ERROR_RANGE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x10, 0x5C,
+							 (u8)(value & 0x0F),
+							 0x0F);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_DVBT2_PER_MES:
+		if ((value < 0) || (value > 15))
+			return CXD2880_RESULT_ERROR_RANGE;
+
+		ret =
+		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x24, 0xDC,
+							 (u8)(value & 0x0F),
+							 0x0F);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+		break;
+
+	case CXD2880_TNRDMD_CFG_ISDBT_BERPER_PERIOD:
+		{
+			u8 data[2];
+
+			data[0] = (u8)((value & 0x00007F00) >> 8);
+			data[1] = (u8)(value & 0x000000FF);
+
+			ret =
+			    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x60, 0x5B,
+							 data[0], 0x7F);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+			ret =
+			    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+							 CXD2880_IO_TGT_DMD,
+							 0x60, 0x5C,
+							 data[1], 0xFF);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+		}
+		break;
+
+	default:
+		return CXD2880_RESULT_ERROR_ARG;
+	}
+
+	if (need_sub_setting &&
+	    (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)) {
+		ret = cxd2880_tnrdmd_set_cfg(tnr_dmd->diver_sub, id, value);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+					     u8 id,
+					     u8 en,
+					     enum cxd2880_tnrdmd_gpio_mode mode,
+					     u8 open_drain, u8 invert)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (id > 2)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (mode > CXD2880_TNRDMD_GPIO_MODE_EEW)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret =
+	    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+						 0x00, 0x40 + id, (u8)mode,
+						 0x0F);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+						 0x00, 0x43,
+						 (u8)(open_drain ? (1 << id) :
+						       0), (u8)(1 << id));
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+						 0x00, 0x44,
+						 (u8)(invert ? (1 << id) : 0),
+						 (u8)(1 << id));
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+						 0x00, 0x45,
+						 (u8)(en ? 0 : (1 << id)),
+						 (u8)(1 << id));
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd,
+						 u8 id,
+						 u8 en,
+						 enum cxd2880_tnrdmd_gpio_mode
+						 mode, u8 open_drain, u8 invert)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret =
+	    cxd2880_tnrdmd_gpio_set_cfg(tnr_dmd->diver_sub, id, en, mode,
+					open_drain, invert);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd,
+					  u8 id, u8 *value)
+{
+	u8 data = 0;
+
+	if ((!tnr_dmd) || (!value))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (id > 2)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x0A) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x20, &data,
+				   1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	*value = (u8)((data >> id) & 0x01);
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd,
+					      u8 id, u8 *value)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret = cxd2880_tnrdmd_gpio_read(tnr_dmd->diver_sub, id, value);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd,
+					   u8 id, u8 value)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (id > 2)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret =
+	    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+						 0x00, 0x46,
+						 (u8)(value ? (1 << id) : 0),
+						 (u8)(1 << id));
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd,
+					       u8 id, u8 value)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret = cxd2880_tnrdmd_gpio_write(tnr_dmd->diver_sub, id, value);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd,
+					       u16 *value)
+{
+	u8 data[2] = { 0 };
+
+	if ((!tnr_dmd) || (!value))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x0A) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x15, data,
+				   2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	*value = (u16)(((u16)data[0] << 8) | (data[1]));
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd,
+						u16 value)
+{
+	u8 data[2] = { 0 };
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	data[0] = (u8)((value >> 8) & 0xFF);
+	data[1] = (u8)(value & 0xFF);
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_SYS, 0x3C, data,
+				    2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd,
+					     u8 clear_overflow_flag,
+					     u8 clear_underflow_flag,
+					     u8 clear_buf)
+{
+	u8 data[2] = { 0 };
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	data[0] = (u8)(clear_overflow_flag ? 0x02 : 0x00);
+	data[0] |= (u8)(clear_underflow_flag ? 0x01 : 0x00);
+	data[1] = (u8)(clear_buf ? 0x01 : 0x00);
+	if (tnr_dmd->io->write_regs(tnr_dmd->io,
+				    CXD2880_IO_TGT_DMD, 0x9F, data,
+				    2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd,
+					enum cxd2880_tnrdmd_chip_id *chip_id)
+{
+	u8 data = 0;
+
+	if ((!tnr_dmd) || (!chip_id))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0xFD, &data,
+				   1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	*chip_id = (enum cxd2880_tnrdmd_chip_id)data;
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd
+						      *tnr_dmd,
+						      enum cxd2880_io_tgt tgt,
+						      u8 bank, u8 address,
+						      u8 value, u8 bit_mask)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   tgt, 0x00, bank) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (cxd2880_io_set_reg_bits(tnr_dmd->io, tgt, address, value, bit_mask)
+	    != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	ret = set_cfg_mem(tnr_dmd, tgt, bank, address, value, bit_mask);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd,
+					      enum cxd2880_dtv_sys sys,
+					      u8 scan_mode_end)
+{
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	CXD2880_ARG_UNUSED(sys);
+
+	tnr_dmd->scan_mode = scan_mode_end;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+		ret =
+		    cxd2880_tnrdmd_set_scan_mode(tnr_dmd->diver_sub, sys,
+						 scan_mode_end);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd,
+					    struct cxd2880_tnrdmd_pid_ftr_cfg
+					    *pid_ftr_cfg)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS)
+		return CXD2880_RESULT_ERROR_NOSUPPORT;
+
+	if (pid_ftr_cfg) {
+		tnr_dmd->pid_ftr_cfg = *pid_ftr_cfg;
+		tnr_dmd->pid_ftr_cfg_en = 1;
+	} else {
+		tnr_dmd->pid_ftr_cfg_en = 0;
+	}
+
+	if (tnr_dmd->state == CXD2880_TNRDMD_STATE_ACTIVE) {
+		ret = pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd
+					  *tnr_dmd,
+					  enum
+					  cxd2880_ret(*rf_lvl_cmpstn)
+					  (struct cxd2880_tnrdmd *,
+					   int *))
+{
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	tnr_dmd->rf_lvl_cmpstn = rf_lvl_cmpstn;
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd
+						      *tnr_dmd,
+						      enum
+						      cxd2880_ret
+						      (*rf_lvl_cmpstn)(struct
+								cxd2880_tnrdmd
+								*,
+								int *))
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret =
+	    cxd2880_tnrdmd_set_rf_lvl_cmpstn(tnr_dmd->diver_sub, rf_lvl_cmpstn);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd,
+					     struct
+					     cxd2880_tnrdmd_lna_thrs_tbl_air
+					     *tbl_air,
+					     struct
+					     cxd2880_tnrdmd_lna_thrs_tbl_cable
+					     *tbl_cable)
+{
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	tnr_dmd->lna_thrs_tbl_air = tbl_air;
+	tnr_dmd->lna_thrs_tbl_cable = tbl_cable;
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd,
+					 struct
+					 cxd2880_tnrdmd_lna_thrs_tbl_air
+					 *tbl_air,
+					 struct
+					 cxd2880_tnrdmd_lna_thrs_tbl_cable
+					 *tbl_cable)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret =
+	    cxd2880_tnrdmd_set_lna_thrs(tnr_dmd->diver_sub, tbl_air, tbl_cable);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd
+						    *tnr_dmd, u8 en, u8 value)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->create_param.ts_output_if != CXD2880_TNRDMD_TSOUT_IF_TS)
+		return CXD2880_RESULT_ERROR_NOSUPPORT;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (en) {
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x50,
+					   ((value & 0x1F) | 0x80)) !=
+		    CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x52,
+					   (value & 0x1F)) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	} else {
+		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+					     CXD2880_IO_TGT_SYS, 0x50, 0x3F);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x52,
+					   0x1F) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		ret = load_cfg_mem(tnr_dmd);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd,
+					      u8 en)
+{
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	switch (tnr_dmd->create_param.ts_output_if) {
+	case CXD2880_TNRDMD_TSOUT_IF_TS:
+		if (en) {
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_SYS, 0x00,
+						   0x00) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_SYS, 0x52,
+						   0x00) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_DMD, 0x00,
+						   0x00) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_DMD, 0xC3,
+						   0x00) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		} else {
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_DMD, 0x00,
+						   0x00) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_DMD, 0xC3,
+						   0x01) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_SYS, 0x00,
+						   0x00) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_SYS, 0x52,
+						   0x1F) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+		break;
+
+	case CXD2880_TNRDMD_TSOUT_IF_SPI:
+		break;
+
+	case CXD2880_TNRDMD_TSOUT_IF_SDIO:
+		break;
+
+	default:
+		return CXD2880_RESULT_ERROR_SW_STATE;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	switch (tnr_dmd->create_param.ts_output_if) {
+	case CXD2880_TNRDMD_TSOUT_IF_SPI:
+	case CXD2880_TNRDMD_TSOUT_IF_SDIO:
+		{
+			u8 data = 0;
+
+			if (tnr_dmd->io->read_regs(tnr_dmd->io,
+						   CXD2880_IO_TGT_DMD, 0x00,
+						   &data,
+						   1) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+		break;
+	case CXD2880_TNRDMD_TSOUT_IF_TS:
+	default:
+		break;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x01,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
new file mode 100644
index 000000000000..26e29b3b9f6b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
@@ -0,0 +1,395 @@
+/*
+ * cxd2880_tnrdmd.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common control interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_TNRDMD_H
+#define CXD2880_TNRDMD_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_io.h"
+#include "cxd2880_dtv.h"
+#include "cxd2880_dvbt.h"
+#include "cxd2880_dvbt2.h"
+
+#define CXD2880_TNRDMD_MAX_CFG_MEM_COUNT 100
+
+#define slvt_unfreeze_reg(tnr_dmd) ((void)((tnr_dmd)->io->write_reg\
+((tnr_dmd)->io, CXD2880_IO_TGT_DMD, 0x01, 0x00)))
+
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_UNDERFLOW     0x0001
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_OVERFLOW      0x0002
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_EMPTY  0x0004
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_FULL   0x0008
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_RRDY	  0x0010
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_COMMAND      0x0020
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_ACCESS       0x0040
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_CPU_ERROR	    0x0100
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_LOCK		 0x0200
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_INV_LOCK	     0x0400
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_NOOFDM	       0x0800
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_EWS		  0x1000
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_EEW		  0x2000
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_FEC_FAIL	     0x4000
+
+#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_L1POST_OK	0x01
+#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_DMD_LOCK	 0x02
+#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_TS_LOCK	  0x04
+
+enum cxd2880_tnrdmd_chip_id {
+	CXD2880_TNRDMD_CHIP_ID_UNKNOWN = 0x00,
+	CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X = 0x62,
+	CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11 = 0x6A
+};
+
+#define CXD2880_TNRDMD_CHIP_ID_VALID(chip_id) (((chip_id) == \
+CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) || \
+((chip_id) == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11))
+
+enum cxd2880_tnrdmd_state {
+	CXD2880_TNRDMD_STATE_UNKNOWN,
+	CXD2880_TNRDMD_STATE_SLEEP,
+	CXD2880_TNRDMD_STATE_ACTIVE,
+	CXD2880_TNRDMD_STATE_INVALID
+};
+
+enum cxd2880_tnrdmd_divermode {
+	CXD2880_TNRDMD_DIVERMODE_SINGLE,
+	CXD2880_TNRDMD_DIVERMODE_MAIN,
+	CXD2880_TNRDMD_DIVERMODE_SUB
+};
+
+enum cxd2880_tnrdmd_clockmode {
+	CXD2880_TNRDMD_CLOCKMODE_UNKNOWN,
+	CXD2880_TNRDMD_CLOCKMODE_A,
+	CXD2880_TNRDMD_CLOCKMODE_B,
+	CXD2880_TNRDMD_CLOCKMODE_C
+};
+
+enum cxd2880_tnrdmd_tsout_if {
+	CXD2880_TNRDMD_TSOUT_IF_TS,
+	CXD2880_TNRDMD_TSOUT_IF_SPI,
+	CXD2880_TNRDMD_TSOUT_IF_SDIO
+};
+
+enum cxd2880_tnrdmd_xtal_share {
+	CXD2880_TNRDMD_XTAL_SHARE_NONE,
+	CXD2880_TNRDMD_XTAL_SHARE_EXTREF,
+	CXD2880_TNRDMD_XTAL_SHARE_MASTER,
+	CXD2880_TNRDMD_XTAL_SHARE_SLAVE
+};
+
+enum cxd2880_tnrdmd_spectrum_sense {
+	CXD2880_TNRDMD_SPECTRUM_NORMAL,
+	CXD2880_TNRDMD_SPECTRUM_INV
+};
+
+enum cxd2880_tnrdmd_cfg_id {
+	CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB,
+	CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI,
+	CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI,
+	CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI,
+	CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE,
+	CXD2880_TNRDMD_CFG_TSCLK_CONT,
+	CXD2880_TNRDMD_CFG_TSCLK_MASK,
+	CXD2880_TNRDMD_CFG_TSVALID_MASK,
+	CXD2880_TNRDMD_CFG_TSERR_MASK,
+	CXD2880_TNRDMD_CFG_TSERR_VALID_DIS,
+	CXD2880_TNRDMD_CFG_TSPIN_CURRENT,
+	CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL,
+	CXD2880_TNRDMD_CFG_TSPIN_PULLUP,
+	CXD2880_TNRDMD_CFG_TSCLK_FREQ,
+	CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL,
+	CXD2880_TNRDMD_CFG_TS_PACKET_GAP,
+	CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE,
+	CXD2880_TNRDMD_CFG_PWM_VALUE,
+	CXD2880_TNRDMD_CFG_INTERRUPT,
+	CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL,
+	CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL,
+	CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS,
+	CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS,
+	CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS,
+	CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE,
+	CXD2880_TNRDMD_CFG_CABLE_INPUT,
+	CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE,
+	CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE,
+	CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST,
+	CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD,
+	CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD,
+	CXD2880_TNRDMD_CFG_DVBT_PER_MES,
+	CXD2880_TNRDMD_CFG_DVBT2_BBER_MES,
+	CXD2880_TNRDMD_CFG_DVBT2_LBER_MES,
+	CXD2880_TNRDMD_CFG_DVBT2_PER_MES,
+	CXD2880_TNRDMD_CFG_ISDBT_BERPER_PERIOD
+};
+
+enum cxd2880_tnrdmd_lock_result {
+	CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT,
+	CXD2880_TNRDMD_LOCK_RESULT_LOCKED,
+	CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED
+};
+
+enum cxd2880_tnrdmd_gpio_mode {
+	CXD2880_TNRDMD_GPIO_MODE_OUTPUT = 0x00,
+	CXD2880_TNRDMD_GPIO_MODE_INPUT = 0x01,
+	CXD2880_TNRDMD_GPIO_MODE_INT = 0x02,
+	CXD2880_TNRDMD_GPIO_MODE_FEC_FAIL = 0x03,
+	CXD2880_TNRDMD_GPIO_MODE_PWM = 0x04,
+	CXD2880_TNRDMD_GPIO_MODE_EWS = 0x05,
+	CXD2880_TNRDMD_GPIO_MODE_EEW = 0x06
+};
+
+enum cxd2880_tnrdmd_serial_ts_clk {
+	CXD2880_TNRDMD_SERIAL_TS_CLK_FULL,
+	CXD2880_TNRDMD_SERIAL_TS_CLK_HALF
+};
+
+struct cxd2880_tnrdmd_cfg_mem {
+	enum cxd2880_io_tgt tgt;
+	u8 bank;
+	u8 address;
+	u8 value;
+	u8 bit_mask;
+};
+
+struct cxd2880_tnrdmd_pid_cfg {
+	u8 is_en;
+	u16 pid;
+};
+
+struct cxd2880_tnrdmd_pid_ftr_cfg {
+	u8 is_negative;
+	struct cxd2880_tnrdmd_pid_cfg pid_cfg[32];
+};
+
+struct cxd2880_tnrdmd_ts_buf_info {
+	u8 read_ready;
+	u8 almost_full;
+	u8 almost_empty;
+	u8 overflow;
+	u8 underflow;
+	u16 packet_num;
+};
+
+struct cxd2880_tnrdmd_lna_thrs {
+	u8 off_on;
+	u8 on_off;
+};
+
+struct cxd2880_tnrdmd_lna_thrs_tbl_air {
+	struct cxd2880_tnrdmd_lna_thrs thrs[24];
+};
+
+struct cxd2880_tnrdmd_lna_thrs_tbl_cable {
+	struct cxd2880_tnrdmd_lna_thrs thrs[32];
+};
+
+struct cxd2880_tnrdmd_create_param {
+	enum cxd2880_tnrdmd_tsout_if ts_output_if;
+	u8 en_internal_ldo;
+	enum cxd2880_tnrdmd_xtal_share xtal_share_type;
+	u8 xosc_cap;
+	u8 xosc_i;
+	u8 is_cxd2881gg;
+	u8 stationary_use;
+};
+
+struct cxd2880_tnrdmd_diver_create_param {
+	enum cxd2880_tnrdmd_tsout_if ts_output_if;
+	u8 en_internal_ldo;
+	u8 xosc_cap_main;
+	u8 xosc_i_main;
+	u8 xosc_i_sub;
+	u8 is_cxd2881gg;
+	u8 stationary_use;
+};
+
+struct cxd2880_tnrdmd {
+	struct cxd2880_tnrdmd *diver_sub;
+	struct cxd2880_io *io;
+	struct cxd2880_tnrdmd_create_param create_param;
+	enum cxd2880_tnrdmd_divermode diver_mode;
+	enum cxd2880_tnrdmd_clockmode fixed_clk_mode;
+	u8 is_cable_input;
+	u8 en_fef_intmtnt_base;
+	u8 en_fef_intmtnt_lite;
+	u8 blind_tune_dvbt2_first;
+	enum cxd2880_ret (*rf_lvl_cmpstn)(struct cxd2880_tnrdmd *tnr_dmd,
+					   int *rf_lvl_db);
+	struct cxd2880_tnrdmd_lna_thrs_tbl_air *lna_thrs_tbl_air;
+	struct cxd2880_tnrdmd_lna_thrs_tbl_cable *lna_thrs_tbl_cable;
+	u8 srl_ts_clk_mod_cnts;
+	enum cxd2880_tnrdmd_serial_ts_clk srl_ts_clk_frq;
+	u8 ts_byte_clk_manual_setting;
+	u8 is_ts_backwards_compatible_mode;
+	struct cxd2880_tnrdmd_cfg_mem cfg_mem[CXD2880_TNRDMD_MAX_CFG_MEM_COUNT];
+	u8 cfg_mem_last_entry;
+	struct cxd2880_tnrdmd_pid_ftr_cfg pid_ftr_cfg;
+	u8 pid_ftr_cfg_en;
+	void *user;
+	enum cxd2880_tnrdmd_chip_id chip_id;
+	enum cxd2880_tnrdmd_state state;
+	enum cxd2880_tnrdmd_clockmode clk_mode;
+	u32 frequency_khz;
+	enum cxd2880_dtv_sys sys;
+	enum cxd2880_dtv_bandwidth bandwidth;
+	u8 scan_mode;
+	struct cxd2880_atomic cancel;
+};
+
+enum cxd2880_ret cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd,
+				       struct cxd2880_io *io,
+				       struct cxd2880_tnrdmd_create_param
+				       *create_param);
+
+enum cxd2880_ret cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd
+					     *tnr_dmd_main,
+					     struct cxd2880_io *io_main,
+					     struct cxd2880_tnrdmd *tnr_dmd_sub,
+					     struct cxd2880_io *io_sub,
+					     struct
+					     cxd2880_tnrdmd_diver_create_param
+					     *create_param);
+
+enum cxd2880_ret cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd
+							  *tnr_dmd,
+							  u8 *task_completed);
+
+enum cxd2880_ret cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd
+						     *tnr_dmd,
+						     enum cxd2880_dtv_sys sys,
+						     u32 frequency_khz,
+						     enum cxd2880_dtv_bandwidth
+						     bandwidth, u8 one_seg_opt,
+						     u8 one_seg_opt_shft_dir);
+
+enum cxd2880_ret cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd
+						     *tnr_dmd,
+						     enum cxd2880_dtv_sys sys,
+						     u8 en_fef_intmtnt_ctrl);
+
+enum cxd2880_ret cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+					enum cxd2880_tnrdmd_cfg_id id,
+					int value);
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+					     u8 id,
+					     u8 en,
+					     enum cxd2880_tnrdmd_gpio_mode mode,
+					     u8 open_drain, u8 invert);
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd,
+						 u8 id,
+						 u8 en,
+						 enum cxd2880_tnrdmd_gpio_mode
+						 mode, u8 open_drain,
+						 u8 invert);
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd,
+					  u8 id, u8 *value);
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd,
+					      u8 id, u8 *value);
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd,
+					   u8 id, u8 value);
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd,
+					       u8 id, u8 value);
+
+enum cxd2880_ret cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd,
+					       u16 *value);
+
+enum cxd2880_ret cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd,
+						u16 value);
+
+enum cxd2880_ret cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd,
+					     u8 clear_overflow_flag,
+					     u8 clear_underflow_flag,
+					     u8 clear_buf);
+
+enum cxd2880_ret cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd,
+					enum cxd2880_tnrdmd_chip_id *chip_id);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd
+						      *tnr_dmd,
+						      enum cxd2880_io_tgt tgt,
+						      u8 bank, u8 address,
+						      u8 value, u8 bit_mask);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd,
+					      enum cxd2880_dtv_sys sys,
+					      u8 scan_mode_end);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd,
+					    struct cxd2880_tnrdmd_pid_ftr_cfg
+					    *pid_ftr_cfg);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd
+						  *tnr_dmd,
+						  enum
+						  cxd2880_ret(*rf_lvl_cmpstn)
+						  (struct cxd2880_tnrdmd *,
+						   int *));
+
+enum cxd2880_ret cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd
+						      *tnr_dmd,
+						      enum
+						      cxd2880_ret
+						      (*rf_lvl_cmpstn)(struct
+								cxd2880_tnrdmd
+								*,
+								int *));
+
+enum cxd2880_ret cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd,
+					     struct
+					     cxd2880_tnrdmd_lna_thrs_tbl_air
+					     *tbl_air,
+					     struct
+					     cxd2880_tnrdmd_lna_thrs_tbl_cable
+					     *tbl_cable);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd,
+					 struct
+					 cxd2880_tnrdmd_lna_thrs_tbl_air
+					 *tbl_air,
+					 struct
+					 cxd2880_tnrdmd_lna_thrs_tbl_cable
+					 *tbl_cable);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd
+						    *tnr_dmd, u8 en, u8 value);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd,
+					      u8 en);
+
+enum cxd2880_ret slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
new file mode 100644
index 000000000000..68fb3af04bd4
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
@@ -0,0 +1,29 @@
+/*
+ * cxd2880_tnrdmd_driver_version.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * version information
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define CXD2880_TNRDMD_DRIVER_VERSION "1.4.1 - 1.0.1"
+
+#define CXD2880_TNRDMD_DRIVER_RELEASE_DATE "2017-04-13"
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
new file mode 100644
index 000000000000..0ac5b9bf3be8
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
@@ -0,0 +1,207 @@
+/*
+ * cxd2880_tnrdmd_mon.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common monitor functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd_mon.h"
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd,
+					   int *rf_lvl_db)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!rf_lvl_db))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x10,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	{
+		u8 data[2] = { 0x80, 0x00 };
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_SYS, 0x5B, data,
+					    2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	CXD2880_SLEEP_IN_MON(2, tnr_dmd);
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x1A) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	{
+		u8 data[2];
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x15, data,
+					   2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if ((data[0] != 0) || (data[1] != 0))
+			return CXD2880_RESULT_ERROR_OTHER;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_SYS, 0x11, data,
+					   2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		*rf_lvl_db =
+		    cxd2880_convert2s_complement((data[0] << 3) |
+						 ((data[1] & 0xE0) >> 5), 11);
+	}
+
+	*rf_lvl_db *= 125;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x10,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->rf_lvl_cmpstn) {
+		ret = tnr_dmd->rf_lvl_cmpstn(tnr_dmd, rf_lvl_db);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd,
+					       int *rf_lvl_db)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!rf_lvl_db))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, rf_lvl_db);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd
+							*tnr_dmd, u16 *status)
+{
+	u8 data[2] = { 0 };
+
+	if ((!tnr_dmd) || (!status))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x1A) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x15, data,
+				   2) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	*status = (u16)(((u16)data[0] << 8) | data[1]);
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct
+							    cxd2880_tnrdmd
+							    *tnr_dmd,
+							    u16 *status)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!status))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret =
+	    cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd->diver_sub, status);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_ts_buf_info(struct cxd2880_tnrdmd *tnr_dmd,
+						struct
+						cxd2880_tnrdmd_ts_buf_info
+						*info)
+{
+	u8 data[3] = { 0 };
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!info))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0A) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x50, data,
+				   3) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	info->read_ready = (u8)((data[0] & 0x10) ? 0x01 : 0x00);
+	info->almost_full = (u8)((data[0] & 0x08) ? 0x01 : 0x00);
+	info->almost_empty = (u8)((data[0] & 0x04) ? 0x01 : 0x00);
+	info->overflow = (u8)((data[0] & 0x02) ? 0x01 : 0x00);
+	info->underflow = (u8)((data[0] & 0x01) ? 0x01 : 0x00);
+
+	info->packet_num = (u16)(((u32)(data[1] & 0x07) << 8) | data[2]);
+
+	return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
new file mode 100644
index 000000000000..506bd559668f
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
@@ -0,0 +1,52 @@
+/*
+ * cxd2880_tnrdmd_mon.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common monitor interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_TNRDMD_MON_H
+#define CXD2880_TNRDMD_MON_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd,
+					   int *rf_lvl_db);
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd,
+					       int *rf_lvl_db);
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd
+							*tnr_dmd, u16 *status);
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct
+							    cxd2880_tnrdmd
+							    *tnr_dmd,
+							    u16 *status);
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_ts_buf_info(struct cxd2880_tnrdmd *tnr_dmd,
+						struct
+						cxd2880_tnrdmd_ts_buf_info
+						*info);
+
+#endif
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 07/15] [media] cxd2880: Add integration layer for the driver
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (5 preceding siblings ...)
  2017-04-14  2:26 ` [PATCH v2 06/15] [media] cxd2880: Add tuner part of the driver Yasunari.Takiguchi
@ 2017-04-14  2:29 ` Yasunari.Takiguchi
  2017-04-14  2:31 ` [PATCH v2 08/15] [media] cxd2880: Add top level of " Yasunari.Takiguchi
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  2:29 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

These functions monitor the driver and watch for task completion.
This is part of the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
 .../media/dvb-frontends/cxd2880/cxd2880_integ.c    | 99 ++++++++++++++++++++++
 .../media/dvb-frontends/cxd2880/cxd2880_integ.h    | 44 ++++++++++
 2 files changed, 143 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
new file mode 100644
index 000000000000..5ad6685e2a1d
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
@@ -0,0 +1,99 @@
+/*
+ * cxd2880_integ.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer common functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_integ.h"
+
+enum cxd2880_ret cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	struct cxd2880_stopwatch timer;
+	u32 elapsed_time = 0;
+	u8 cpu_task_completed = 0;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret = cxd2880_tnrdmd_init1(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret = cxd2880_stopwatch_start(&timer);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	while (1) {
+		ret = cxd2880_stopwatch_elapsed(&timer, &elapsed_time);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		ret =
+		    cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+						     &cpu_task_completed);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (cpu_task_completed)
+			break;
+
+		if (elapsed_time > CXD2880_TNRDMD_WAIT_INIT_TIMEOUT)
+			return CXD2880_RESULT_ERROR_TIMEOUT;
+		ret =
+		    cxd2880_stopwatch_sleep(&timer,
+					    CXD2880_TNRDMD_WAIT_INIT_INTVL);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	ret = cxd2880_tnrdmd_init2(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	cxd2880_atomic_set(&tnr_dmd->cancel, 1);
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd
+						  *tnr_dmd)
+{
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (cxd2880_atomic_read(&tnr_dmd->cancel) != 0)
+		return CXD2880_RESULT_ERROR_CANCEL;
+
+	return CXD2880_RESULT_OK;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
new file mode 100644
index 000000000000..9cfc52dbf9d4
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
@@ -0,0 +1,44 @@
+/*
+ * cxd2880_integ.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer common interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_INTEG_H
+#define CXD2880_INTEG_H
+
+#include "cxd2880_tnrdmd.h"
+
+#define CXD2880_TNRDMD_WAIT_INIT_TIMEOUT	500
+#define CXD2880_TNRDMD_WAIT_INIT_INTVL	10
+
+#define CXD2880_TNRDMD_WAIT_AGC_STABLE		100
+
+enum cxd2880_ret cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd
+						  *tnr_dmd);
+
+#endif
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 08/15] [media] cxd2880: Add top level of the driver
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (6 preceding siblings ...)
  2017-04-14  2:29 ` [PATCH v2 07/15] [media] cxd2880: Add integration layer for " Yasunari.Takiguchi
@ 2017-04-14  2:31 ` Yasunari.Takiguchi
  2017-06-13 13:30   ` Mauro Carvalho Chehab
  2017-04-14  2:33 ` [PATCH v2 09/15] [media] cxd2880: Add DVB-T control functions " Yasunari.Takiguchi
                   ` (7 subsequent siblings)
  15 siblings, 1 reply; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  2:31 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

This provides the main dvb frontend operation functions
for the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c | 1550 +++++++++++++++++++++
 1 file changed, 1550 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
new file mode 100644
index 000000000000..66d78fb93a13
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
@@ -0,0 +1,1550 @@
+/*
+ * cxd2880_top.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/spi/spi.h>
+
+#include "dvb_frontend.h"
+
+#include "cxd2880.h"
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+#include "cxd2880_tnrdmd_dvbt_mon.h"
+#include "cxd2880_integ_dvbt2.h"
+#include "cxd2880_integ_dvbt.h"
+#include "cxd2880_devio_spi.h"
+#include "cxd2880_spi_device.h"
+#include "cxd2880_tnrdmd_driver_version.h"
+
+struct cxd2880_priv {
+	struct cxd2880_tnrdmd tnrdmd;
+	struct spi_device *spi;
+	struct cxd2880_io regio;
+	struct cxd2880_spi_device spi_device;
+	struct cxd2880_spi cxd2880_spi;
+	struct cxd2880_dvbt_tune_param dvbt_tune_param;
+	struct cxd2880_dvbt2_tune_param dvbt2_tune_param;
+	struct mutex *spi_mutex; /* For SPI access exclusive control */
+};
+
+/*
+ * return value conversion table
+ */
+static int return_tbl[] = {
+	0,             /* CXD2880_RESULT_OK */
+	-EINVAL,       /* CXD2880_RESULT_ERROR_ARG*/
+	-EIO,          /* CXD2880_RESULT_ERROR_IO */
+	-EPERM,        /* CXD2880_RESULT_ERROR_SW_STATE */
+	-EBUSY,        /* CXD2880_RESULT_ERROR_HW_STATE */
+	-ETIME,        /* CXD2880_RESULT_ERROR_TIMEOUT */
+	-EAGAIN,       /* CXD2880_RESULT_ERROR_UNLOCK */
+	-ERANGE,       /* CXD2880_RESULT_ERROR_RANGE */
+	-EOPNOTSUPP,   /* CXD2880_RESULT_ERROR_NOSUPPORT */
+	-ECANCELED,    /* CXD2880_RESULT_ERROR_CANCEL */
+	-EPERM,        /* CXD2880_RESULT_ERROR_OTHER */
+	-EOVERFLOW,    /* CXD2880_RESULT_ERROR_OVERFLOW */
+	0,             /* CXD2880_RESULT_OK_CONFIRM */
+};
+
+static enum cxd2880_ret cxd2880_pre_bit_err_t(
+		struct cxd2880_tnrdmd *tnrdmd, u32 *pre_bit_err,
+		u32 *pre_bit_count)
+{
+	u8 rdata[2];
+
+	if ((!tnrdmd) || (!pre_bit_err) || (!pre_bit_count))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (slvt_freeze_reg(tnrdmd) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x00, 0x10) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnrdmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x39, rdata, 1) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnrdmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if ((rdata[0] & 0x01) == 0) {
+		slvt_unfreeze_reg(tnrdmd);
+		return CXD2880_RESULT_ERROR_HW_STATE;
+	}
+
+	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x22, rdata, 2) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnrdmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	*pre_bit_err = (rdata[0] << 8) | rdata[1];
+
+	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x6F, rdata, 1) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnrdmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	slvt_unfreeze_reg(tnrdmd);
+
+	*pre_bit_count = ((rdata[0] & 0x07) == 0) ?
+			256 : (0x1000 << (rdata[0] & 0x07));
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret cxd2880_pre_bit_err_t2(
+		struct cxd2880_tnrdmd *tnrdmd, u32 *pre_bit_err,
+		u32 *pre_bit_count)
+{
+	u32 period_exp = 0;
+	u32 n_ldpc = 0;
+	u8 data[5];
+
+	if ((!tnrdmd) || (!pre_bit_err) || (!pre_bit_count))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (slvt_freeze_reg(tnrdmd) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x00, 0x0B) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnrdmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x3C, data, sizeof(data))
+				 != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnrdmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (!(data[0] & 0x01)) {
+		slvt_unfreeze_reg(tnrdmd);
+		return CXD2880_RESULT_ERROR_HW_STATE;
+	}
+	*pre_bit_err =
+	((data[1] & 0x0F) << 24) | (data[2] << 16) | (data[3] << 8) | data[4];
+
+	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0xA0, data, 1) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnrdmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (((enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03)) ==
+	    CXD2880_DVBT2_FEC_LDPC_16K)
+		n_ldpc = 16200;
+	else
+		n_ldpc = 64800;
+	slvt_unfreeze_reg(tnrdmd);
+
+	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x00, 0x20) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x6F, data, 1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	period_exp = data[0] & 0x0F;
+
+	*pre_bit_count = (1U << period_exp) * n_ldpc;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret cxd2880_post_bit_err_t(struct cxd2880_tnrdmd *tnrdmd,
+						u32 *post_bit_err,
+						u32 *post_bit_count)
+{
+	u8 rdata[3];
+	u32 bit_error = 0;
+	u32 period_exp = 0;
+
+	if ((!tnrdmd) || (!post_bit_err) || (!post_bit_count))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x00, 0x0D) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x15, rdata, 3) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if ((rdata[0] & 0x40) == 0)
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	*post_bit_err = ((rdata[0] & 0x3F) << 16) | (rdata[1] << 8) | rdata[2];
+
+	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x00, 0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x60, rdata, 1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	period_exp = (rdata[0] & 0x1F);
+
+	if ((period_exp <= 11) && (bit_error > (1U << period_exp) * 204 * 8))
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	if (period_exp == 11)
+		*post_bit_count = 3342336;
+	else
+		*post_bit_count = (1U << period_exp) * 204 * 81;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret cxd2880_post_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd,
+						u32 *post_bit_err,
+						u32 *post_bit_count)
+{
+	u32 period_exp = 0;
+	u32 n_bch = 0;
+
+	if ((!tnrdmd) || (!post_bit_err) || (!post_bit_count))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 data[3];
+		enum cxd2880_dvbt2_plp_fec plp_fec_type =
+			CXD2880_DVBT2_FEC_LDPC_16K;
+		enum cxd2880_dvbt2_plp_code_rate plp_code_rate =
+			CXD2880_DVBT2_R1_2;
+
+		static const u16 n_bch_bits_lookup[2][8] = {
+			{7200, 9720, 10800, 11880, 12600, 13320, 5400, 6480},
+			{32400, 38880, 43200, 48600, 51840, 54000, 21600, 25920}
+		};
+
+		if (slvt_freeze_reg(tnrdmd) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+					0x00, 0x0B) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnrdmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+					 0x15, data, 3) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnrdmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (!(data[0] & 0x40)) {
+			slvt_unfreeze_reg(tnrdmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		*post_bit_err =
+			((data[0] & 0x3F) << 16) | (data[1] << 8) | data[2];
+
+		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+					0x9D, data, 1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnrdmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		plp_code_rate =
+		(enum cxd2880_dvbt2_plp_code_rate)(data[0] & 0x07);
+
+		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+					0xA0, data, 1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnrdmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		plp_fec_type = (enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03);
+
+		slvt_unfreeze_reg(tnrdmd);
+
+		if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+					0x00, 0x20) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+					0x72, data, 1) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		period_exp = data[0] & 0x0F;
+
+		if ((plp_fec_type > CXD2880_DVBT2_FEC_LDPC_64K) ||
+			(plp_code_rate > CXD2880_DVBT2_R2_5))
+			return CXD2880_RESULT_ERROR_HW_STATE;
+
+		n_bch = n_bch_bits_lookup[plp_fec_type][plp_code_rate];
+	}
+
+	if (*post_bit_err > ((1U << period_exp) * n_bch))
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	*post_bit_count = (1U << period_exp) * n_bch;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret cxd2880_read_block_err_t(
+					struct cxd2880_tnrdmd *tnrdmd,
+					u32 *block_err,
+					u32 *block_count)
+{
+	u8 rdata[3];
+
+	if ((!tnrdmd) || (!block_err) || (!block_count))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x00, 0x0D) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x18, rdata, 3) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if ((rdata[0] & 0x01) == 0)
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	*block_err = (rdata[1] << 8) | rdata[2];
+
+	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x00, 0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+				0x5C, rdata, 1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	*block_count = 1U << (rdata[0] & 0x0F);
+
+	if ((*block_count == 0) || (*block_err > *block_count))
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret cxd2880_read_block_err_t2(
+					struct cxd2880_tnrdmd *tnrdmd,
+					u32 *block_err,
+					u32 *block_count)
+{
+	if ((!tnrdmd) || (!block_err) || (!block_count))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 rdata[3];
+
+		if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+					0x00, 0x0B) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+					0x18, rdata, 3) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if ((rdata[0] & 0x01) == 0)
+			return CXD2880_RESULT_ERROR_HW_STATE;
+
+		*block_err = (rdata[1] << 8) | rdata[2];
+
+		if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+					0x00, 0x24) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+					0xDC, rdata, 1) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		*block_count = 1U << (rdata[0] & 0x0F);
+	}
+
+	if ((*block_count == 0) || (*block_err > *block_count))
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	return CXD2880_RESULT_OK;
+}
+
+static void cxd2880_release(struct dvb_frontend *fe)
+{
+	struct cxd2880_priv *priv = NULL;
+
+	if (!fe) {
+		pr_err("%s: invalid arg.\n", __func__);
+		return;
+	}
+	priv = (struct cxd2880_priv *)fe->demodulator_priv;
+	kfree(priv);
+}
+
+static int cxd2880_init(struct dvb_frontend *fe)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	struct cxd2880_priv *priv = NULL;
+	struct cxd2880_tnrdmd_create_param create_param;
+
+	if (!fe) {
+		pr_err("%s: invalid arg.\n", __func__);
+		return -EINVAL;
+	}
+
+	priv = (struct cxd2880_priv *)fe->demodulator_priv;
+
+	create_param.ts_output_if = CXD2880_TNRDMD_TSOUT_IF_SPI;
+	create_param.xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_NONE;
+	create_param.en_internal_ldo = 1;
+	create_param.xosc_cap = 18;
+	create_param.xosc_i = 8;
+	create_param.stationary_use = 1;
+
+	mutex_lock(priv->spi_mutex);
+	if (priv->tnrdmd.io != &priv->regio) {
+		ret = cxd2880_tnrdmd_create(&priv->tnrdmd,
+				&priv->regio, &create_param);
+		if (ret != CXD2880_RESULT_OK) {
+			mutex_unlock(priv->spi_mutex);
+			dev_info(&priv->spi->dev,
+				"%s: cxd2880 tnrdmd create failed %d\n",
+				__func__, ret);
+			return return_tbl[ret];
+		}
+	}
+	ret = cxd2880_integ_init(&priv->tnrdmd);
+	if (ret != CXD2880_RESULT_OK) {
+		mutex_unlock(priv->spi_mutex);
+		dev_err(&priv->spi->dev, "%s: cxd2880 integ init failed %d\n",
+				__func__, ret);
+		return return_tbl[ret];
+	}
+	mutex_unlock(priv->spi_mutex);
+
+	dev_dbg(&priv->spi->dev, "%s: OK.\n", __func__);
+
+	return return_tbl[ret];
+}
+
+static int cxd2880_sleep(struct dvb_frontend *fe)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	struct cxd2880_priv *priv = NULL;
+
+	if (!fe) {
+		pr_err("%s: inavlid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	priv = (struct cxd2880_priv *)fe->demodulator_priv;
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_sleep(&priv->tnrdmd);
+	mutex_unlock(priv->spi_mutex);
+
+	dev_dbg(&priv->spi->dev, "%s: tnrdmd_sleep ret %d\n",
+		__func__, ret);
+
+	return return_tbl[ret];
+}
+
+static int cxd2880_read_signal_strength(struct dvb_frontend *fe,
+				u16 *strength)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+	int level = 0;
+
+	if ((!fe) || (!strength)) {
+		pr_err("%s: inavlid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	priv = (struct cxd2880_priv *)fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+
+	mutex_lock(priv->spi_mutex);
+	if ((c->delivery_system == SYS_DVBT) ||
+		(c->delivery_system == SYS_DVBT2)) {
+		ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &level);
+	} else {
+		dev_dbg(&priv->spi->dev, "%s: invalid system\n", __func__);
+		mutex_unlock(priv->spi_mutex);
+		return -EINVAL;
+	}
+	mutex_unlock(priv->spi_mutex);
+
+	level /= 125;
+	/* -105dBm - -30dBm (-105000/125 = -840, -30000/125 = -240 */
+	level = clamp(level, -840, -240);
+	/* scale value to 0x0000-0xFFFF */
+	*strength = (u16)(((level + 840) * 0xFFFF) / (-240 + 840));
+
+	if (ret != CXD2880_RESULT_OK)
+		dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
+
+	return return_tbl[ret];
+}
+
+static int cxd2880_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	int snrvalue = 0;
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+
+	if ((!fe) || (!snr)) {
+		pr_err("%s: inavlid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	priv = (struct cxd2880_priv *)fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+
+	mutex_lock(priv->spi_mutex);
+	if (c->delivery_system == SYS_DVBT) {
+		ret = cxd2880_tnrdmd_dvbt_mon_snr(&priv->tnrdmd,
+						&snrvalue);
+	} else if (c->delivery_system == SYS_DVBT2) {
+		ret = cxd2880_tnrdmd_dvbt2_mon_snr(&priv->tnrdmd,
+						&snrvalue);
+	} else {
+		dev_err(&priv->spi->dev, "%s: invalid system\n", __func__);
+		mutex_unlock(priv->spi_mutex);
+		return -EINVAL;
+	}
+	mutex_unlock(priv->spi_mutex);
+
+	if (snrvalue < 0)
+		snrvalue = 0;
+	*snr = (u16)snrvalue;
+
+	if (ret != CXD2880_RESULT_OK)
+		dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
+
+	return return_tbl[ret];
+}
+
+static int cxd2880_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+
+	if ((!fe) || (!ucblocks)) {
+		pr_err("%s: inavlid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	priv = (struct cxd2880_priv *)fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+
+	mutex_lock(priv->spi_mutex);
+	if (c->delivery_system == SYS_DVBT) {
+		ret = cxd2880_tnrdmd_dvbt_mon_packet_error_number(
+								&priv->tnrdmd,
+								ucblocks);
+	} else if (c->delivery_system == SYS_DVBT2) {
+		ret = cxd2880_tnrdmd_dvbt2_mon_packet_error_number(
+								&priv->tnrdmd,
+								ucblocks);
+	} else {
+		dev_err(&priv->spi->dev, "%s: invlaid system\n", __func__);
+		mutex_unlock(priv->spi_mutex);
+		return -EINVAL;
+	}
+	mutex_unlock(priv->spi_mutex);
+
+	if (ret != CXD2880_RESULT_OK)
+		dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
+
+	return return_tbl[ret];
+}
+
+static int cxd2880_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+
+	if ((!fe) || (!ber)) {
+		pr_err("%s: inavlid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	priv = (struct cxd2880_priv *)fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+
+	mutex_lock(priv->spi_mutex);
+	if (c->delivery_system == SYS_DVBT) {
+		ret = cxd2880_tnrdmd_dvbt_mon_pre_rsber(&priv->tnrdmd,
+						ber);
+		/* x100 to change unit.(10^7 -> 10^9 */
+		*ber *= 100;
+	} else if (c->delivery_system == SYS_DVBT2) {
+		ret = cxd2880_tnrdmd_dvbt2_mon_pre_bchber(&priv->tnrdmd,
+						ber);
+	} else {
+		dev_err(&priv->spi->dev, "%s: invlaid system\n", __func__);
+		mutex_unlock(priv->spi_mutex);
+		return -EINVAL;
+	}
+	mutex_unlock(priv->spi_mutex);
+
+	if (ret != CXD2880_RESULT_OK)
+		dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
+
+	return return_tbl[ret];
+}
+
+static int cxd2880_set_frontend(struct dvb_frontend *fe)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	struct dtv_frontend_properties *c;
+	struct cxd2880_priv *priv;
+	enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
+
+	if (!fe) {
+		pr_err("%s: inavlid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	priv = (struct cxd2880_priv *)fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+
+	switch (c->bandwidth_hz) {
+	case 1712000:
+		bw = CXD2880_DTV_BW_1_7_MHZ;
+		break;
+	case 5000000:
+		bw = CXD2880_DTV_BW_5_MHZ;
+		break;
+	case 6000000:
+		bw = CXD2880_DTV_BW_6_MHZ;
+		break;
+	case 7000000:
+		bw = CXD2880_DTV_BW_7_MHZ;
+		break;
+	case 8000000:
+		bw = CXD2880_DTV_BW_8_MHZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	dev_info(&priv->spi->dev, "%s: sys:%d freq:%d bw:%d\n", __func__,
+			c->delivery_system, c->frequency, bw);
+	mutex_lock(priv->spi_mutex);
+	if (c->delivery_system == SYS_DVBT) {
+		priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT;
+		priv->dvbt_tune_param.center_freq_khz = c->frequency / 1000;
+		priv->dvbt_tune_param.bandwidth = bw;
+		priv->dvbt_tune_param.profile = CXD2880_DVBT_PROFILE_HP;
+		ret = cxd2880_integ_dvbt_tune(&priv->tnrdmd,
+						&priv->dvbt_tune_param);
+	} else if (c->delivery_system == SYS_DVBT2) {
+		priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT2;
+		priv->dvbt2_tune_param.center_freq_khz = c->frequency / 1000;
+		priv->dvbt2_tune_param.bandwidth = bw;
+		priv->dvbt2_tune_param.data_plp_id = (u16)c->stream_id;
+		ret = cxd2880_integ_dvbt2_tune(&priv->tnrdmd,
+						&priv->dvbt2_tune_param);
+	} else {
+		dev_err(&priv->spi->dev, "%s: invalid system\n", __func__);
+		mutex_unlock(priv->spi_mutex);
+		return -EINVAL;
+	}
+	mutex_unlock(priv->spi_mutex);
+	dev_info(&priv->spi->dev, "%s: tune result %d\n", __func__, ret);
+
+	return return_tbl[ret];
+}
+
+static int cxd2880_read_status(struct dvb_frontend *fe,
+				enum fe_status *status)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	u8 sync = 0;
+	u8 lock = 0;
+	u8 unlock = 0;
+	struct cxd2880_priv *priv = NULL;
+	struct dtv_frontend_properties *c = NULL;
+
+	if ((!fe) || (!status)) {
+		pr_err("%s: invalid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	priv = (struct cxd2880_priv *)fe->demodulator_priv;
+	c = &fe->dtv_property_cache;
+	*status = 0;
+
+	if (priv->tnrdmd.state == CXD2880_TNRDMD_STATE_ACTIVE) {
+		mutex_lock(priv->spi_mutex);
+		if (c->delivery_system == SYS_DVBT) {
+			ret = cxd2880_tnrdmd_dvbt_mon_sync_stat(
+							&priv->tnrdmd,
+							&sync,
+							&lock,
+							&unlock);
+		} else if (c->delivery_system == SYS_DVBT2) {
+			ret = cxd2880_tnrdmd_dvbt2_mon_sync_stat(
+							&priv->tnrdmd,
+							&sync,
+							&lock,
+							&unlock);
+		} else {
+			dev_err(&priv->spi->dev,
+				"%s: invlaid system", __func__);
+			mutex_unlock(priv->spi_mutex);
+			return -EINVAL;
+		}
+
+		mutex_unlock(priv->spi_mutex);
+		if (ret != CXD2880_RESULT_OK) {
+			dev_err(&priv->spi->dev, "%s: failed. sys = %d\n",
+				__func__, priv->tnrdmd.sys);
+			return  return_tbl[ret];
+		}
+
+		if (sync == 6) {
+			*status = FE_HAS_SIGNAL |
+					FE_HAS_CARRIER;
+		}
+		if (lock)
+			*status |= FE_HAS_VITERBI |
+					FE_HAS_SYNC |
+					FE_HAS_LOCK;
+	}
+
+	dev_dbg(&priv->spi->dev, "%s: status %d result %d\n", __func__,
+		*status, ret);
+
+	return  return_tbl[CXD2880_RESULT_OK];
+}
+
+static int cxd2880_tune(struct dvb_frontend *fe,
+			bool retune,
+			unsigned int mode_flags,
+			unsigned int *delay,
+			enum fe_status *status)
+{
+	int ret = 0;
+
+	if ((!fe) || (!delay) || (!status)) {
+		pr_err("%s: invalid arg.", __func__);
+		return -EINVAL;
+	}
+
+	if (retune) {
+		ret = cxd2880_set_frontend(fe);
+		if (ret) {
+			pr_err("%s: cxd2880_set_frontend failed %d\n",
+				__func__, ret);
+			return ret;
+		}
+	}
+
+	*delay = HZ / 5;
+
+	return cxd2880_read_status(fe, status);
+}
+
+static int cxd2880_get_frontend_t(struct dvb_frontend *fe,
+				struct dtv_frontend_properties *c)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	int result = 0;
+	struct cxd2880_priv *priv = NULL;
+	enum cxd2880_dvbt_mode mode = CXD2880_DVBT_MODE_2K;
+	enum cxd2880_dvbt_guard guard = CXD2880_DVBT_GUARD_1_32;
+	struct cxd2880_dvbt_tpsinfo tps;
+	enum cxd2880_tnrdmd_spectrum_sense sense;
+	u16 snr = 0;
+	int strength = 0;
+	u32 pre_bit_err = 0, pre_bit_count = 0;
+	u32 post_bit_err = 0, post_bit_count = 0;
+	u32 block_err = 0, block_count = 0;
+
+	if ((!fe) || (!c)) {
+		pr_err("%s: invalid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	priv = (struct cxd2880_priv *)fe->demodulator_priv;
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt_mon_mode_guard(&priv->tnrdmd,
+						 &mode, &guard);
+	mutex_unlock(priv->spi_mutex);
+	if (ret == CXD2880_RESULT_OK) {
+		switch (mode) {
+		case CXD2880_DVBT_MODE_2K:
+			c->transmission_mode = TRANSMISSION_MODE_2K;
+			break;
+		case CXD2880_DVBT_MODE_8K:
+			c->transmission_mode = TRANSMISSION_MODE_8K;
+			break;
+		default:
+			c->transmission_mode = TRANSMISSION_MODE_2K;
+			dev_err(&priv->spi->dev, "%s: get invalid mode %d\n",
+					__func__, mode);
+			break;
+		}
+		switch (guard) {
+		case CXD2880_DVBT_GUARD_1_32:
+			c->guard_interval = GUARD_INTERVAL_1_32;
+			break;
+		case CXD2880_DVBT_GUARD_1_16:
+			c->guard_interval = GUARD_INTERVAL_1_16;
+			break;
+		case CXD2880_DVBT_GUARD_1_8:
+			c->guard_interval = GUARD_INTERVAL_1_8;
+			break;
+		case CXD2880_DVBT_GUARD_1_4:
+			c->guard_interval = GUARD_INTERVAL_1_4;
+			break;
+		default:
+			c->guard_interval = GUARD_INTERVAL_1_32;
+			dev_err(&priv->spi->dev, "%s: get invalid guard %d\n",
+					__func__, guard);
+			break;
+		}
+	} else {
+		c->transmission_mode = TRANSMISSION_MODE_2K;
+		c->guard_interval = GUARD_INTERVAL_1_32;
+		dev_dbg(&priv->spi->dev,
+			"%s: ModeGuard err %d\n", __func__, ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, &tps);
+	mutex_unlock(priv->spi_mutex);
+	if (ret == CXD2880_RESULT_OK) {
+		switch (tps.hierarchy) {
+		case CXD2880_DVBT_HIERARCHY_NON:
+			c->hierarchy = HIERARCHY_NONE;
+			break;
+		case CXD2880_DVBT_HIERARCHY_1:
+			c->hierarchy = HIERARCHY_1;
+			break;
+		case CXD2880_DVBT_HIERARCHY_2:
+			c->hierarchy = HIERARCHY_2;
+			break;
+		case CXD2880_DVBT_HIERARCHY_4:
+			c->hierarchy = HIERARCHY_4;
+			break;
+		default:
+			c->hierarchy = HIERARCHY_NONE;
+			dev_err(&priv->spi->dev,
+				"%s: TPSInfo hierarchy invalid %d\n",
+				__func__, tps.hierarchy);
+			break;
+		}
+
+		switch (tps.rate_hp) {
+		case CXD2880_DVBT_CODERATE_1_2:
+			c->code_rate_HP = FEC_1_2;
+			break;
+		case CXD2880_DVBT_CODERATE_2_3:
+			c->code_rate_HP = FEC_2_3;
+			break;
+		case CXD2880_DVBT_CODERATE_3_4:
+			c->code_rate_HP = FEC_3_4;
+			break;
+		case CXD2880_DVBT_CODERATE_5_6:
+			c->code_rate_HP = FEC_5_6;
+			break;
+		case CXD2880_DVBT_CODERATE_7_8:
+			c->code_rate_HP = FEC_7_8;
+			break;
+		default:
+			c->code_rate_HP = FEC_NONE;
+			dev_err(&priv->spi->dev,
+				"%s: TPSInfo rateHP invalid %d\n",
+				__func__, tps.rate_hp);
+			break;
+		}
+		switch (tps.rate_lp) {
+		case CXD2880_DVBT_CODERATE_1_2:
+			c->code_rate_LP = FEC_1_2;
+			break;
+		case CXD2880_DVBT_CODERATE_2_3:
+			c->code_rate_LP = FEC_2_3;
+			break;
+		case CXD2880_DVBT_CODERATE_3_4:
+			c->code_rate_LP = FEC_3_4;
+			break;
+		case CXD2880_DVBT_CODERATE_5_6:
+			c->code_rate_LP = FEC_5_6;
+			break;
+		case CXD2880_DVBT_CODERATE_7_8:
+			c->code_rate_LP = FEC_7_8;
+			break;
+		default:
+			c->code_rate_LP = FEC_NONE;
+			dev_err(&priv->spi->dev,
+				"%s: TPSInfo rateLP invalid %d\n",
+				__func__, tps.rate_lp);
+			break;
+		}
+		switch (tps.constellation) {
+		case CXD2880_DVBT_CONSTELLATION_QPSK:
+			c->modulation = QPSK;
+			break;
+		case CXD2880_DVBT_CONSTELLATION_16QAM:
+			c->modulation = QAM_16;
+			break;
+		case CXD2880_DVBT_CONSTELLATION_64QAM:
+			c->modulation = QAM_64;
+			break;
+		default:
+			c->modulation = QPSK;
+			dev_err(&priv->spi->dev,
+				"%s: TPSInfo constellation invalid %d\n",
+				__func__, tps.constellation);
+			break;
+		}
+	} else {
+		c->hierarchy = HIERARCHY_NONE;
+		c->code_rate_HP = FEC_NONE;
+		c->code_rate_LP = FEC_NONE;
+		c->modulation = QPSK;
+		dev_dbg(&priv->spi->dev,
+			"%s: TPS info err %d\n", __func__, ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(&priv->tnrdmd, &sense);
+	mutex_unlock(priv->spi_mutex);
+	if (ret == CXD2880_RESULT_OK) {
+		switch (sense) {
+		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
+			c->inversion = INVERSION_OFF;
+			break;
+		case CXD2880_TNRDMD_SPECTRUM_INV:
+			c->inversion = INVERSION_ON;
+			break;
+		default:
+			c->inversion = INVERSION_OFF;
+			dev_err(&priv->spi->dev,
+				"%s: spectrum sense invalid %d\n",
+				__func__, sense);
+			break;
+		}
+	} else {
+		c->inversion = INVERSION_OFF;
+		dev_dbg(&priv->spi->dev,
+			"%s: spectrum_sense %d\n", __func__, ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
+	mutex_unlock(priv->spi_mutex);
+	if (ret == CXD2880_RESULT_OK) {
+		c->strength.len = 1;
+		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+		c->strength.stat[0].svalue = strength;
+	} else {
+		c->strength.len = 1;
+		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		dev_dbg(&priv->spi->dev, "%s: mon_rf_lvl %d\n",
+			__func__, result);
+	}
+
+	result = cxd2880_read_snr(fe, &snr);
+	if (!result) {
+		c->cnr.len = 1;
+		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+		c->cnr.stat[0].svalue = snr;
+	} else {
+		c->cnr.len = 1;
+		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		dev_dbg(&priv->spi->dev, "%s: read_snr %d\n", __func__, result);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_pre_bit_err_t(&priv->tnrdmd, &pre_bit_err,
+					&pre_bit_count);
+	mutex_unlock(priv->spi_mutex);
+	if (ret == CXD2880_RESULT_OK) {
+		c->pre_bit_error.len = 1;
+		c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+		c->pre_bit_error.stat[0].uvalue = pre_bit_err;
+		c->pre_bit_count.len = 1;
+		c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+		c->pre_bit_count.stat[0].uvalue = pre_bit_count;
+	} else {
+		c->pre_bit_error.len = 1;
+		c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->pre_bit_count.len = 1;
+		c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		dev_dbg(&priv->spi->dev,
+			"%s: pre_bit_error_t failed %d\n",
+			__func__, ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_post_bit_err_t(&priv->tnrdmd,
+				&post_bit_err, &post_bit_count);
+	mutex_unlock(priv->spi_mutex);
+	if (ret == CXD2880_RESULT_OK) {
+		c->post_bit_error.len = 1;
+		c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+		c->post_bit_error.stat[0].uvalue = post_bit_err;
+		c->post_bit_count.len = 1;
+		c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+		c->post_bit_count.stat[0].uvalue = post_bit_count;
+	} else {
+		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;
+		dev_dbg(&priv->spi->dev,
+			"%s: post_bit_err_t %d\n", __func__, ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_read_block_err_t(&priv->tnrdmd,
+					&block_err, &block_count);
+	mutex_unlock(priv->spi_mutex);
+	if (ret == CXD2880_RESULT_OK) {
+		c->block_error.len = 1;
+		c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+		c->block_error.stat[0].uvalue = block_err;
+		c->block_count.len = 1;
+		c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+		c->block_count.stat[0].uvalue = block_count;
+	} else {
+		c->block_error.len = 1;
+		c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->block_count.len = 1;
+		c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		dev_dbg(&priv->spi->dev,
+			"%s: read_block_err_t  %d\n", __func__, ret);
+	}
+
+	return 0;
+}
+
+static int cxd2880_get_frontend_t2(struct dvb_frontend *fe,
+				struct dtv_frontend_properties *c)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	int result = 0;
+	struct cxd2880_priv *priv = NULL;
+	struct cxd2880_dvbt2_l1pre l1pre;
+	enum cxd2880_dvbt2_plp_code_rate coderate;
+	enum cxd2880_dvbt2_plp_constell qam;
+	enum cxd2880_tnrdmd_spectrum_sense sense;
+	u16 snr = 0;
+	int strength = 0;
+	u32 pre_bit_err = 0, pre_bit_count = 0;
+	u32 post_bit_err = 0, post_bit_count = 0;
+	u32 block_err = 0, block_count = 0;
+
+	if ((!fe) || (!c)) {
+		pr_err("%s: invalid arg.\n", __func__);
+		return -EINVAL;
+	}
+
+	priv = (struct cxd2880_priv *)fe->demodulator_priv;
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre);
+	mutex_unlock(priv->spi_mutex);
+	if (ret == CXD2880_RESULT_OK) {
+		switch (l1pre.fft_mode) {
+		case CXD2880_DVBT2_M2K:
+			c->transmission_mode = TRANSMISSION_MODE_2K;
+			break;
+		case CXD2880_DVBT2_M8K:
+			c->transmission_mode = TRANSMISSION_MODE_8K;
+			break;
+		case CXD2880_DVBT2_M4K:
+			c->transmission_mode = TRANSMISSION_MODE_4K;
+			break;
+		case CXD2880_DVBT2_M1K:
+			c->transmission_mode = TRANSMISSION_MODE_1K;
+			break;
+		case CXD2880_DVBT2_M16K:
+			c->transmission_mode = TRANSMISSION_MODE_16K;
+			break;
+		case CXD2880_DVBT2_M32K:
+			c->transmission_mode = TRANSMISSION_MODE_32K;
+			break;
+		default:
+			c->transmission_mode = TRANSMISSION_MODE_2K;
+			dev_err(&priv->spi->dev,
+				"%s: L1Pre fft_mode invalid %d\n",
+				__func__, l1pre.fft_mode);
+			break;
+		}
+		switch (l1pre.gi) {
+		case CXD2880_DVBT2_G1_32:
+			c->guard_interval = GUARD_INTERVAL_1_32;
+			break;
+		case CXD2880_DVBT2_G1_16:
+			c->guard_interval = GUARD_INTERVAL_1_16;
+			break;
+		case CXD2880_DVBT2_G1_8:
+			c->guard_interval = GUARD_INTERVAL_1_8;
+			break;
+		case CXD2880_DVBT2_G1_4:
+			c->guard_interval = GUARD_INTERVAL_1_4;
+			break;
+		case CXD2880_DVBT2_G1_128:
+			c->guard_interval = GUARD_INTERVAL_1_128;
+			break;
+		case CXD2880_DVBT2_G19_128:
+			c->guard_interval = GUARD_INTERVAL_19_128;
+			break;
+		case CXD2880_DVBT2_G19_256:
+			c->guard_interval = GUARD_INTERVAL_19_256;
+			break;
+		default:
+			c->guard_interval = GUARD_INTERVAL_1_32;
+			dev_err(&priv->spi->dev,
+				"%s: L1Pre gi invalid %d\n",
+				__func__, l1pre.gi);
+			break;
+		}
+	} else {
+		c->transmission_mode = TRANSMISSION_MODE_2K;
+		c->guard_interval = GUARD_INTERVAL_1_32;
+		dev_dbg(&priv->spi->dev,
+			"%s: L1Pre err %d\n", __func__, ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt2_mon_code_rate(&priv->tnrdmd,
+						CXD2880_DVBT2_PLP_DATA,
+						&coderate);
+	mutex_unlock(priv->spi_mutex);
+	if (ret == CXD2880_RESULT_OK) {
+		switch (coderate) {
+		case CXD2880_DVBT2_R1_2:
+			c->fec_inner = FEC_1_2;
+			break;
+		case CXD2880_DVBT2_R3_5:
+			c->fec_inner = FEC_3_5;
+			break;
+		case CXD2880_DVBT2_R2_3:
+			c->fec_inner = FEC_2_3;
+			break;
+		case CXD2880_DVBT2_R3_4:
+			c->fec_inner = FEC_3_4;
+			break;
+		case CXD2880_DVBT2_R4_5:
+			c->fec_inner = FEC_4_5;
+			break;
+		case CXD2880_DVBT2_R5_6:
+			c->fec_inner = FEC_5_6;
+			break;
+		default:
+			c->fec_inner = FEC_NONE;
+			dev_err(&priv->spi->dev,
+				"%s: CodeRate invalid %d\n",
+				__func__, coderate);
+			break;
+		}
+	} else {
+		c->fec_inner = FEC_NONE;
+		dev_dbg(&priv->spi->dev, "%s: CodeRate %d\n", __func__, ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt2_mon_qam(&priv->tnrdmd,
+					CXD2880_DVBT2_PLP_DATA,
+					&qam);
+	mutex_unlock(priv->spi_mutex);
+	if (ret == CXD2880_RESULT_OK) {
+		switch (qam) {
+		case CXD2880_DVBT2_QPSK:
+			c->modulation = QPSK;
+			break;
+		case CXD2880_DVBT2_QAM16:
+			c->modulation = QAM_16;
+			break;
+		case CXD2880_DVBT2_QAM64:
+			c->modulation = QAM_64;
+			break;
+		case CXD2880_DVBT2_QAM256:
+			c->modulation = QAM_256;
+			break;
+		default:
+			c->modulation = QPSK;
+			dev_err(&priv->spi->dev,
+				"%s: QAM invalid %d\n",
+				__func__, qam);
+			break;
+		}
+	} else {
+		c->modulation = QPSK;
+		dev_dbg(&priv->spi->dev, "%s: QAM %d\n", __func__, ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(&priv->tnrdmd, &sense);
+	mutex_unlock(priv->spi_mutex);
+	if (ret == CXD2880_RESULT_OK) {
+		switch (sense) {
+		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
+			c->inversion = INVERSION_OFF;
+			break;
+		case CXD2880_TNRDMD_SPECTRUM_INV:
+			c->inversion = INVERSION_ON;
+			break;
+		default:
+			c->inversion = INVERSION_OFF;
+			dev_err(&priv->spi->dev,
+				"%s: spectrum sense invalid %d\n",
+				__func__, sense);
+			break;
+		}
+	} else {
+		c->inversion = INVERSION_OFF;
+		dev_dbg(&priv->spi->dev,
+			"%s: SpectrumSense %d\n", __func__, ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
+	mutex_unlock(priv->spi_mutex);
+	if (ret == CXD2880_RESULT_OK) {
+		c->strength.len = 1;
+		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+		c->strength.stat[0].svalue = strength;
+	} else {
+		c->strength.len = 1;
+		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		dev_dbg(&priv->spi->dev,
+			"%s: mon_rf_lvl %d\n", __func__, ret);
+	}
+
+	result = cxd2880_read_snr(fe, &snr);
+	if (!result) {
+		c->cnr.len = 1;
+		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+		c->cnr.stat[0].svalue = snr;
+	} else {
+		c->cnr.len = 1;
+		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		dev_dbg(&priv->spi->dev, "%s: read_snr %d\n", __func__, result);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_pre_bit_err_t2(&priv->tnrdmd,
+				&pre_bit_err,
+				&pre_bit_count);
+	mutex_unlock(priv->spi_mutex);
+	if (ret == CXD2880_RESULT_OK) {
+		c->pre_bit_error.len = 1;
+		c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+		c->pre_bit_error.stat[0].uvalue = pre_bit_err;
+		c->pre_bit_count.len = 1;
+		c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+		c->pre_bit_count.stat[0].uvalue = pre_bit_count;
+	} else {
+		c->pre_bit_error.len = 1;
+		c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->pre_bit_count.len = 1;
+		c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		dev_dbg(&priv->spi->dev,
+			"%s: read_bit_err_t2 %d\n", __func__, ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_post_bit_err_t2(&priv->tnrdmd,
+				&post_bit_err, &post_bit_count);
+	mutex_unlock(priv->spi_mutex);
+	if (ret == CXD2880_RESULT_OK) {
+		c->post_bit_error.len = 1;
+		c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+		c->post_bit_error.stat[0].uvalue = post_bit_err;
+		c->post_bit_count.len = 1;
+		c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+		c->post_bit_count.stat[0].uvalue = post_bit_count;
+	} else {
+		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;
+		dev_dbg(&priv->spi->dev,
+			"%s: post_bit_err_t2 %d\n", __func__, ret);
+	}
+
+	mutex_lock(priv->spi_mutex);
+	ret = cxd2880_read_block_err_t2(&priv->tnrdmd,
+					&block_err, &block_count);
+	mutex_unlock(priv->spi_mutex);
+	if (ret == CXD2880_RESULT_OK) {
+		c->block_error.len = 1;
+		c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+		c->block_error.stat[0].uvalue = block_err;
+		c->block_count.len = 1;
+		c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+		c->block_count.stat[0].uvalue = block_count;
+	} else {
+		c->block_error.len = 1;
+		c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		c->block_count.len = 1;
+		c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+		dev_dbg(&priv->spi->dev,
+			"%s: read_block_err_t2 %d\n", __func__, ret);
+	}
+
+	return 0;
+}
+
+static int cxd2880_get_frontend(struct dvb_frontend *fe,
+				struct dtv_frontend_properties *props)
+{
+	struct cxd2880_priv *priv = NULL;
+	int result = 0;
+
+	if ((!fe) || (!props)) {
+		pr_err("%s: invalid arg.", __func__);
+		return -EINVAL;
+	}
+
+	priv = (struct cxd2880_priv *)fe->demodulator_priv;
+
+	dev_dbg(&priv->spi->dev, "%s: system=%d\n", __func__,
+		fe->dtv_property_cache.delivery_system);
+	switch (fe->dtv_property_cache.delivery_system) {
+	case SYS_DVBT:
+		result = cxd2880_get_frontend_t(fe, props);
+		break;
+	case SYS_DVBT2:
+		result = cxd2880_get_frontend_t2(fe, props);
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+
+	return result;
+}
+
+static enum dvbfe_algo cxd2880_get_frontend_algo(struct dvb_frontend *fe)
+{
+	return DVBFE_ALGO_HW;
+}
+
+static struct dvb_frontend_ops cxd2880_dvbt_t2_ops;
+
+struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
+				struct cxd2880_config *cfg)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	enum cxd2880_tnrdmd_chip_id chipid =
+					CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
+	static struct cxd2880_priv *priv;
+	u8 data = 0;
+
+	if (!fe) {
+		pr_err("%s: invalid arg.\n", __func__);
+		return NULL;
+	}
+
+	priv = kzalloc(sizeof(struct cxd2880_priv), GFP_KERNEL);
+	if (!priv)
+		return NULL;
+
+	priv->spi = cfg->spi;
+	priv->spi_mutex = cfg->spi_mutex;
+	priv->spi_device.spi = cfg->spi;
+
+	memcpy(&fe->ops, &cxd2880_dvbt_t2_ops,
+			sizeof(struct dvb_frontend_ops));
+
+	ret = cxd2880_spi_device_initialize(&priv->spi_device,
+						CXD2880_SPI_MODE_0,
+						55000000);
+	if (ret != CXD2880_RESULT_OK) {
+		dev_err(&priv->spi->dev,
+			"%s: spi_device_initialize failed. %d\n",
+			__func__, ret);
+		kfree(priv);
+		return NULL;
+	}
+
+	ret = cxd2880_spi_device_create_spi(&priv->cxd2880_spi,
+					&priv->spi_device);
+	if (ret != CXD2880_RESULT_OK) {
+		dev_err(&priv->spi->dev,
+			"%s: spi_device_create_spi failed. %d\n",
+			__func__, ret);
+		kfree(priv);
+		return NULL;
+	}
+
+	ret = cxd2880_io_spi_create(&priv->regio, &priv->cxd2880_spi, 0);
+	if (ret != CXD2880_RESULT_OK) {
+		dev_err(&priv->spi->dev,
+			"%s: io_spi_create failed. %d\n", __func__, ret);
+		kfree(priv);
+		return NULL;
+	}
+	if (priv->regio.write_reg(&priv->regio, CXD2880_IO_TGT_SYS, 0x00, 0x00)
+		!= CXD2880_RESULT_OK) {
+		dev_err(&priv->spi->dev,
+			"%s: set bank to 0x00 failed.\n", __func__);
+		kfree(priv);
+		return NULL;
+	}
+	if (priv->regio.read_regs(&priv->regio,
+					CXD2880_IO_TGT_SYS, 0xFD, &data, 1)
+					!= CXD2880_RESULT_OK) {
+		dev_err(&priv->spi->dev,
+			"%s: read chip id failed.\n", __func__);
+		kfree(priv);
+		return NULL;
+	}
+
+	chipid = (enum cxd2880_tnrdmd_chip_id)data;
+	if ((chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) &&
+		(chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11)) {
+		dev_err(&priv->spi->dev,
+			"%s: chip id invalid.\n", __func__);
+		kfree(priv);
+		return NULL;
+	}
+
+	fe->demodulator_priv = priv;
+	dev_info(&priv->spi->dev,
+		"CXD2880 driver version: Ver %s\n",
+		CXD2880_TNRDMD_DRIVER_VERSION);
+
+	return fe;
+}
+EXPORT_SYMBOL(cxd2880_attach);
+
+static struct dvb_frontend_ops cxd2880_dvbt_t2_ops = {
+	.info = {
+		.name = "Sony CXD2880",
+		.frequency_min =  174000000,
+		.frequency_max = 862000000,
+		.frequency_stepsize = 1000,
+		.caps = FE_CAN_INVERSION_AUTO |
+				FE_CAN_FEC_1_2 |
+				FE_CAN_FEC_2_3 |
+				FE_CAN_FEC_3_4 |
+				FE_CAN_FEC_4_5 |
+				FE_CAN_FEC_5_6	|
+				FE_CAN_FEC_7_8	|
+				FE_CAN_FEC_AUTO |
+				FE_CAN_QPSK |
+				FE_CAN_QAM_16 |
+				FE_CAN_QAM_32 |
+				FE_CAN_QAM_64 |
+				FE_CAN_QAM_128 |
+				FE_CAN_QAM_256 |
+				FE_CAN_QAM_AUTO |
+				FE_CAN_TRANSMISSION_MODE_AUTO |
+				FE_CAN_GUARD_INTERVAL_AUTO |
+				FE_CAN_2G_MODULATION |
+				FE_CAN_RECOVER |
+				FE_CAN_MUTE_TS,
+	},
+	.delsys = { SYS_DVBT, SYS_DVBT2 },
+
+	.release = cxd2880_release,
+	.init = cxd2880_init,
+	.sleep = cxd2880_sleep,
+	.tune = cxd2880_tune,
+	.set_frontend = cxd2880_set_frontend,
+	.get_frontend = cxd2880_get_frontend,
+	.read_status = cxd2880_read_status,
+	.read_ber = cxd2880_read_ber,
+	.read_signal_strength = cxd2880_read_signal_strength,
+	.read_snr = cxd2880_read_snr,
+	.read_ucblocks = cxd2880_read_ucblocks,
+	.get_frontend_algo = cxd2880_get_frontend_algo,
+};
+
+MODULE_DESCRIPTION(
+"Sony CXD2880 DVB-T2/T tuner + demodulator drvier");
+MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
+MODULE_LICENSE("GPL v2");
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 09/15] [media] cxd2880: Add DVB-T control functions the driver
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (7 preceding siblings ...)
  2017-04-14  2:31 ` [PATCH v2 08/15] [media] cxd2880: Add top level of " Yasunari.Takiguchi
@ 2017-04-14  2:33 ` Yasunari.Takiguchi
  2017-04-14  2:35 ` [PATCH v2 10/15] [media] cxd2880: Add DVB-T monitor and integration layer functions Yasunari.Takiguchi
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  2:33 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

Provide definitions, interfaces and functions needed for DVB-T
of the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h |   91 ++
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c    | 1072 ++++++++++++++++++++
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h    |   62 ++
 3 files changed, 1225 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
new file mode 100644
index 000000000000..345c094760d2
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
@@ -0,0 +1,91 @@
+/*
+ * cxd2880_dvbt.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T related definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_DVBT_H
+#define CXD2880_DVBT_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_dvbt_constellation {
+	CXD2880_DVBT_CONSTELLATION_QPSK,
+	CXD2880_DVBT_CONSTELLATION_16QAM,
+	CXD2880_DVBT_CONSTELLATION_64QAM,
+	CXD2880_DVBT_CONSTELLATION_RESERVED_3
+};
+
+enum cxd2880_dvbt_hierarchy {
+	CXD2880_DVBT_HIERARCHY_NON,
+	CXD2880_DVBT_HIERARCHY_1,
+	CXD2880_DVBT_HIERARCHY_2,
+	CXD2880_DVBT_HIERARCHY_4
+};
+
+enum cxd2880_dvbt_coderate {
+	CXD2880_DVBT_CODERATE_1_2,
+	CXD2880_DVBT_CODERATE_2_3,
+	CXD2880_DVBT_CODERATE_3_4,
+	CXD2880_DVBT_CODERATE_5_6,
+	CXD2880_DVBT_CODERATE_7_8,
+	CXD2880_DVBT_CODERATE_RESERVED_5,
+	CXD2880_DVBT_CODERATE_RESERVED_6,
+	CXD2880_DVBT_CODERATE_RESERVED_7
+};
+
+enum cxd2880_dvbt_guard {
+	CXD2880_DVBT_GUARD_1_32,
+	CXD2880_DVBT_GUARD_1_16,
+	CXD2880_DVBT_GUARD_1_8,
+	CXD2880_DVBT_GUARD_1_4
+};
+
+enum cxd2880_dvbt_mode {
+	CXD2880_DVBT_MODE_2K,
+	CXD2880_DVBT_MODE_8K,
+	CXD2880_DVBT_MODE_RESERVED_2,
+	CXD2880_DVBT_MODE_RESERVED_3
+};
+
+enum cxd2880_dvbt_profile {
+	CXD2880_DVBT_PROFILE_HP = 0,
+	CXD2880_DVBT_PROFILE_LP
+};
+
+struct cxd2880_dvbt_tpsinfo {
+	enum cxd2880_dvbt_constellation constellation;
+	enum cxd2880_dvbt_hierarchy hierarchy;
+	enum cxd2880_dvbt_coderate rate_hp;
+	enum cxd2880_dvbt_coderate rate_lp;
+	enum cxd2880_dvbt_guard guard;
+	enum cxd2880_dvbt_mode mode;
+	u8 fnum;
+	u8 length_indicator;
+	u16 cell_id;
+	u8 cell_id_ok;
+	u8 reserved_even;
+	u8 reserved_odd;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
new file mode 100644
index 000000000000..f36cf533ec17
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
@@ -0,0 +1,1072 @@
+/*
+ * cxd2880_tnrdmd_dvbt.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control functions for DVB-T
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt_mon.h"
+
+static enum cxd2880_ret x_tune_dvbt_demod_setting(struct cxd2880_tnrdmd
+						  *tnr_dmd,
+						  enum cxd2880_dtv_bandwidth
+						  bandwidth,
+						  enum cxd2880_tnrdmd_clockmode
+						  clk_mode)
+{
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x31,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	{
+		u8 data_a[2] = { 0x52, 0x49 };
+		u8 data_b[2] = { 0x5D, 0x55 };
+		u8 data_c[2] = { 0x60, 0x00 };
+		u8 *data = NULL;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x04) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = data_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = data_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = data_c;
+			break;
+		default:
+			return CXD2880_RESULT_ERROR_SW_STATE;
+		}
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0x65, data,
+					    2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x5D,
+				   0x07) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+		u8 data[2] = { 0x01, 0x01 };
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x00) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0xCE, data,
+					    2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x04) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x5C,
+				   0xFB) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0xA4,
+				   0x03) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x14) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0xB0,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x25) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	{
+		u8 data[2] = { 0x01, 0xF0 };
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0xF0, data,
+					    2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if ((tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) ||
+	    (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)) {
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x12) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x44,
+					   0x00) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) {
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x11) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x87,
+					   0xD2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+		u8 data_a[3] = { 0x73, 0xCA, 0x49 };
+		u8 data_b[3] = { 0xC8, 0x13, 0xAA };
+		u8 data_c[3] = { 0xDC, 0x6C, 0x00 };
+		u8 *data = NULL;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x04) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data = data_a;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data = data_b;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data = data_c;
+			break;
+		default:
+			return CXD2880_RESULT_ERROR_SW_STATE;
+		}
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0x68, data,
+					    3) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x04) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	switch (bandwidth) {
+	case CXD2880_DTV_BW_8_MHZ:
+
+		{
+			u8 data_ac[5] = { 0x15, 0x00, 0x00, 0x00,
+				0x00
+			};
+			u8 data_b[5] = { 0x14, 0x6A, 0xAA, 0xAA,
+				0xAA
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_ac;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x60,
+						    data,
+						    5) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x4A,
+					   0x00) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		{
+			u8 data_a[2] = { 0x01, 0x28 };
+			u8 data_b[2] = { 0x11, 0x44 };
+			u8 data_c[2] = { 0x15, 0x28 };
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x7D,
+						    data,
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		{
+			u8 data = 0;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = 0x35;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = 0x34;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_DMD, 0x71,
+						   data) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			u8 data_a[5] = { 0x30, 0x00, 0x00, 0x90,
+				0x00
+			};
+			u8 data_b[5] = { 0x36, 0x71, 0x00, 0xA3,
+				0x55
+			};
+			u8 data_c[5] = { 0x38, 0x00, 0x00, 0xA8,
+				0x00
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x4B,
+						    &data[0],
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x51,
+						    &data[2],
+						    3) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		{
+			u8 data[4] = { 0xB3, 0x00, 0x01, 0x02 };
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x72,
+						    &data[0],
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x6B,
+						    &data[2],
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+		break;
+
+	case CXD2880_DTV_BW_7_MHZ:
+
+		{
+			u8 data_ac[5] = { 0x18, 0x00, 0x00, 0x00,
+				0x00
+			};
+			u8 data_b[5] = { 0x17, 0x55, 0x55, 0x55,
+				0x55
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_ac;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x60,
+						    data,
+						    5) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x4A,
+					   0x02) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		{
+			u8 data_a[2] = { 0x12, 0x4C };
+			u8 data_b[2] = { 0x1F, 0x15 };
+			u8 data_c[2] = { 0x1F, 0xF8 };
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x7D,
+						    data,
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		{
+			u8 data = 0;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = 0x2F;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = 0x2E;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_DMD, 0x71,
+						   data) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			u8 data_a[5] = { 0x36, 0xDB, 0x00, 0xA4,
+				0x92
+			};
+			u8 data_b[5] = { 0x3E, 0x38, 0x00, 0xBA,
+				0xAA
+			};
+			u8 data_c[5] = { 0x40, 0x00, 0x00, 0xC0,
+				0x00
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x4B,
+						    &data[0],
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x51,
+						    &data[2],
+						    3) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		{
+			u8 data[4] = { 0xB8, 0x00, 0x00, 0x03 };
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x72,
+						    &data[0],
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x6B,
+						    &data[2],
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+		break;
+
+	case CXD2880_DTV_BW_6_MHZ:
+
+		{
+			u8 data_ac[5] = { 0x1C, 0x00, 0x00, 0x00,
+				0x00
+			};
+			u8 data_b[5] = { 0x1B, 0x38, 0xE3, 0x8E,
+				0x38
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_ac;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x60,
+						    data,
+						    5) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x4A,
+					   0x04) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		{
+			u8 data_a[2] = { 0x1F, 0xF8 };
+			u8 data_b[2] = { 0x24, 0x43 };
+			u8 data_c[2] = { 0x25, 0x4C };
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x7D,
+						    data,
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		{
+			u8 data = 0;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = 0x29;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = 0x2A;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_DMD, 0x71,
+						   data) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			u8 data_a[5] = { 0x40, 0x00, 0x00, 0xC0,
+				0x00
+			};
+			u8 data_b[5] = { 0x48, 0x97, 0x00, 0xD9,
+				0xC7
+			};
+			u8 data_c[5] = { 0x4A, 0xAA, 0x00, 0xDF,
+				0xFF
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x4B,
+						    &data[0],
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x51,
+						    &data[2],
+						    3) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		{
+			u8 data[4] = { 0xBE, 0xAB, 0x00, 0x03 };
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x72,
+						    &data[0],
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x6B,
+						    &data[2],
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+		break;
+
+	case CXD2880_DTV_BW_5_MHZ:
+
+		{
+			u8 data_ac[5] = { 0x21, 0x99, 0x99, 0x99,
+				0x99
+			};
+			u8 data_b[5] = { 0x20, 0xAA, 0xAA, 0xAA,
+				0xAA
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_ac;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x60,
+						    data,
+						    5) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x4A,
+					   0x06) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		{
+			u8 data_a[2] = { 0x26, 0x5D };
+			u8 data_b[2] = { 0x2B, 0x84 };
+			u8 data_c[2] = { 0x2C, 0xC2 };
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x7D,
+						    data,
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		{
+			u8 data = 0;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = 0x24;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = 0x23;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_DMD, 0x71,
+						   data) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			u8 data_a[5] = { 0x4C, 0xCC, 0x00, 0xE6,
+				0x66
+			};
+			u8 data_b[5] = { 0x57, 0x1C, 0x01, 0x05,
+				0x55
+			};
+			u8 data_c[5] = { 0x59, 0x99, 0x01, 0x0C,
+				0xCC
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x4B,
+						    &data[0],
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x51,
+						    &data[2],
+						    3) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		{
+			u8 data[4] = { 0xC8, 0x01, 0x00, 0x03 };
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x72,
+						    &data[0],
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x6B,
+						    &data[2],
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+		break;
+
+	default:
+		return CXD2880_RESULT_ERROR_SW_STATE;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0xFD,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_sleep_dvbt_demod_setting(struct cxd2880_tnrdmd
+						   *tnr_dmd)
+{
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x04) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x5C,
+				   0xD8) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0xA4,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) {
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x11) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x87,
+					   0x04) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret dvbt_set_profile(struct cxd2880_tnrdmd *tnr_dmd,
+					 enum cxd2880_dvbt_profile profile)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x67,
+				   (profile ==
+				    CXD2880_DVBT_PROFILE_HP) ? 0x00 : 0x01) !=
+	    CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+					   struct cxd2880_dvbt_tune_param
+					   *tune_param)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!tune_param))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret =
+	    cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT,
+						tune_param->center_freq_khz,
+						tune_param->bandwidth, 0, 0);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret =
+	    x_tune_dvbt_demod_setting(tnr_dmd, tune_param->bandwidth,
+				      tnr_dmd->clk_mode);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret =
+		    x_tune_dvbt_demod_setting(tnr_dmd->diver_sub,
+					      tune_param->bandwidth,
+					      tnr_dmd->diver_sub->clk_mode);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	ret = dvbt_set_profile(tnr_dmd, tune_param->profile);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+					   struct cxd2880_dvbt_tune_param
+					   *tune_param)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!tune_param))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret =
+	    cxd2880_tnrdmd_common_tune_setting2(tnr_dmd, CXD2880_DTV_SYS_DVBT,
+						0);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE;
+	tnr_dmd->frequency_khz = tune_param->center_freq_khz;
+	tnr_dmd->sys = CXD2880_DTV_SYS_DVBT;
+	tnr_dmd->bandwidth = tune_param->bandwidth;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE;
+		tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz;
+		tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT;
+		tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd
+						   *tnr_dmd)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret = x_sleep_dvbt_demod_setting(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = x_sleep_dvbt_demod_setting(tnr_dmd->diver_sub);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd
+						      *tnr_dmd,
+						      enum
+						      cxd2880_tnrdmd_lock_result
+						      *lock)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	u8 sync_stat = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 unlock_detected_sub = 0;
+
+	if ((!tnr_dmd) || (!lock))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+					      &unlock_detected);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		if (sync_stat == 6)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		else if (unlock_detected)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+		else
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+		return ret;
+	}
+
+	if (sync_stat == 6) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		return ret;
+	}
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+						  &unlock_detected_sub);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (sync_stat == 6)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+	else if (unlock_detected && unlock_detected_sub)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+	else
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd
+						   *tnr_dmd,
+						   enum
+						   cxd2880_tnrdmd_lock_result
+						   *lock)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	u8 sync_stat = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 unlock_detected_sub = 0;
+
+	if ((!tnr_dmd) || (!lock))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+					      &unlock_detected);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		if (ts_lock)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		else if (unlock_detected)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+		else
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+		return ret;
+	}
+
+	if (ts_lock) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		return ret;
+	} else if (!unlock_detected) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+		return ret;
+	}
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+						  &unlock_detected_sub);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (unlock_detected && unlock_detected_sub)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+	else
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+	return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
new file mode 100644
index 000000000000..de394d8e27f3
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
@@ -0,0 +1,62 @@
+/*
+ * cxd2880_tnrdmd_dvbt.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control interface for DVB-T
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT_H
+#define CXD2880_TNRDMD_DVBT_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+
+struct cxd2880_dvbt_tune_param {
+	u32 center_freq_khz;
+	enum cxd2880_dtv_bandwidth bandwidth;
+	enum cxd2880_dvbt_profile profile;
+};
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+					   struct cxd2880_dvbt_tune_param
+					   *tune_param);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+					   struct cxd2880_dvbt_tune_param
+					   *tune_param);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd
+						   *tnr_dmd);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd
+						      *tnr_dmd,
+						      enum
+						      cxd2880_tnrdmd_lock_result
+						      *lock);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd
+						   *tnr_dmd,
+						   enum
+						   cxd2880_tnrdmd_lock_result
+						   *lock);
+
+#endif
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 10/15] [media] cxd2880: Add DVB-T monitor and integration layer functions
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (8 preceding siblings ...)
  2017-04-14  2:33 ` [PATCH v2 09/15] [media] cxd2880: Add DVB-T control functions " Yasunari.Takiguchi
@ 2017-04-14  2:35 ` Yasunari.Takiguchi
  2017-04-14  2:38 ` [PATCH v2 11/15] [media] cxd2880: Add DVB-T2 control functions for the driver Yasunari.Takiguchi
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  2:35 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

Provide monitor and integration layer functions (DVB-T)
for the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
 .../dvb-frontends/cxd2880/cxd2880_integ_dvbt.c     |  197 ++++
 .../dvb-frontends/cxd2880/cxd2880_integ_dvbt.h     |   58 +
 .../cxd2880/cxd2880_tnrdmd_dvbt_mon.c              | 1190 ++++++++++++++++++++
 .../cxd2880/cxd2880_tnrdmd_dvbt_mon.h              |  106 ++
 4 files changed, 1551 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
new file mode 100644
index 000000000000..43b7da69fc6d
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
@@ -0,0 +1,197 @@
+/*
+ * cxd2880_integ_dvbt.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer functions for DVB-T
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_integ_dvbt.h"
+
+static enum cxd2880_ret dvbt_wait_demod_lock(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_integ_dvbt_tune(struct cxd2880_tnrdmd *tnr_dmd,
+					 struct cxd2880_dvbt_tune_param
+					 *tune_param)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!tune_param))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	cxd2880_atomic_set(&tnr_dmd->cancel, 0);
+
+	if ((tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ) &&
+	    (tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ) &&
+	    (tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ) &&
+	    (tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ)) {
+		return CXD2880_RESULT_ERROR_NOSUPPORT;
+	}
+
+	ret = cxd2880_tnrdmd_dvbt_tune1(tnr_dmd, tune_param);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	CXD2880_SLEEP(CXD2880_TNRDMD_WAIT_AGC_STABLE);
+
+	ret = cxd2880_tnrdmd_dvbt_tune2(tnr_dmd, tune_param);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret = dvbt_wait_demod_lock(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_integ_dvbt_wait_ts_lock(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	enum cxd2880_tnrdmd_lock_result lock =
+	    CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+	struct cxd2880_stopwatch timer;
+	u8 continue_wait = 1;
+	u32 elapsed = 0;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret = cxd2880_stopwatch_start(&timer);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	for (;;) {
+		ret = cxd2880_stopwatch_elapsed(&timer, &elapsed);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (elapsed >= CXD2880_DVBT_WAIT_TS_LOCK)
+			continue_wait = 0;
+
+		ret = cxd2880_tnrdmd_dvbt_check_ts_lock(tnr_dmd, &lock);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		switch (lock) {
+		case CXD2880_TNRDMD_LOCK_RESULT_LOCKED:
+			return CXD2880_RESULT_OK;
+
+		case CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED:
+			return CXD2880_RESULT_ERROR_UNLOCK;
+
+		default:
+			break;
+		}
+
+		ret = cxd2880_integ_check_cancellation(tnr_dmd);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (continue_wait) {
+			ret =
+			    cxd2880_stopwatch_sleep(&timer,
+					    CXD2880_DVBT_WAIT_LOCK_INTVL);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+		} else {
+			ret = CXD2880_RESULT_ERROR_TIMEOUT;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static enum cxd2880_ret dvbt_wait_demod_lock(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	enum cxd2880_tnrdmd_lock_result lock =
+	    CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+	struct cxd2880_stopwatch timer;
+	u8 continue_wait = 1;
+	u32 elapsed = 0;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret = cxd2880_stopwatch_start(&timer);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	for (;;) {
+		ret = cxd2880_stopwatch_elapsed(&timer, &elapsed);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (elapsed >= CXD2880_DVBT_WAIT_DMD_LOCK)
+			continue_wait = 0;
+
+		ret = cxd2880_tnrdmd_dvbt_check_demod_lock(tnr_dmd, &lock);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		switch (lock) {
+		case CXD2880_TNRDMD_LOCK_RESULT_LOCKED:
+			return CXD2880_RESULT_OK;
+
+		case CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED:
+			return CXD2880_RESULT_ERROR_UNLOCK;
+
+		default:
+			break;
+		}
+
+		ret = cxd2880_integ_check_cancellation(tnr_dmd);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (continue_wait) {
+			ret =
+			    cxd2880_stopwatch_sleep(&timer,
+					    CXD2880_DVBT_WAIT_LOCK_INTVL);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+		} else {
+			ret = CXD2880_RESULT_ERROR_TIMEOUT;
+			break;
+		}
+	}
+
+	return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
new file mode 100644
index 000000000000..41f35c07a15e
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
@@ -0,0 +1,58 @@
+/*
+ * cxd2880_integ_dvbt.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer interface for DVB-T
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_INTEG_DVBT_H
+#define CXD2880_INTEG_DVBT_H
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_integ.h"
+
+#define CXD2880_DVBT_WAIT_DMD_LOCK	   1000
+#define CXD2880_DVBT_WAIT_TS_LOCK	      1000
+#define CXD2880_DVBT_WAIT_LOCK_INTVL	10
+
+struct cxd2880_integ_dvbt_scan_param {
+	u32 start_frequency_khz;
+	u32 end_frequency_khz;
+	u32 step_frequency_khz;
+	enum cxd2880_dtv_bandwidth bandwidth;
+};
+
+struct cxd2880_integ_dvbt_scan_result {
+	u32 center_freq_khz;
+	enum cxd2880_ret tune_result;
+	struct cxd2880_dvbt_tune_param dvbt_tune_param;
+};
+
+enum cxd2880_ret cxd2880_integ_dvbt_tune(struct cxd2880_tnrdmd *tnr_dmd,
+					 struct cxd2880_dvbt_tune_param
+					 *tune_param);
+
+enum cxd2880_ret cxd2880_integ_dvbt_wait_ts_lock(struct cxd2880_tnrdmd
+						 *tnr_dmd);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
new file mode 100644
index 000000000000..d890081b6424
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
@@ -0,0 +1,1190 @@
+/*
+ * cxd2880_tnrdmd_dvbt_mon.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T monitor functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt_mon.h"
+#include "cxd2880_math.h"
+
+static enum cxd2880_ret is_tps_locked(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sync_stat(struct cxd2880_tnrdmd
+						   *tnr_dmd, u8 *sync_stat,
+						   u8 *ts_lock_stat,
+						   u8 *unlock_detected)
+{
+	u8 rdata = 0x00;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!sync_stat) || (!ts_lock_stat) || (!unlock_detected))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0D) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x10, &rdata,
+				   1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	*unlock_detected = (u8)((rdata & 0x10) ? 1 : 0);
+	*sync_stat = (u8)(rdata & 0x07);
+	*ts_lock_stat = (u8)((rdata & 0x20) ? 1 : 0);
+
+	if (*sync_stat == 0x07)
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(struct cxd2880_tnrdmd
+						       *tnr_dmd, u8 *sync_stat,
+						       u8 *unlock_detected)
+{
+	u8 ts_lock_stat = 0;
+
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!sync_stat) || (!unlock_detected))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd->diver_sub, sync_stat,
+					      &ts_lock_stat, unlock_detected);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_mode_guard(struct cxd2880_tnrdmd
+						    *tnr_dmd,
+						    enum cxd2880_dvbt_mode
+						    *mode,
+						    enum cxd2880_dvbt_guard
+						    *guard)
+{
+	u8 rdata = 0x00;
+
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!mode) || (!guard))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	ret = is_tps_locked(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+			ret =
+			    cxd2880_tnrdmd_dvbt_mon_mode_guard(
+					tnr_dmd->diver_sub, mode, guard);
+
+		return ret;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0D) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x1B, &rdata,
+				   1) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	*mode = (enum cxd2880_dvbt_mode)((rdata >> 2) & 0x03);
+	*guard = (enum cxd2880_dvbt_guard)(rdata & 0x03);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_carrier_offset(struct cxd2880_tnrdmd
+							*tnr_dmd, int *offset)
+{
+	u8 rdata[4];
+	u32 ctl_val = 0;
+
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!offset))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	ret = is_tps_locked(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0D) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x1D, rdata,
+				   4) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	ctl_val =
+	    ((rdata[0] & 0x1F) << 24) | (rdata[1] << 16) | (rdata[2] << 8) |
+	    (rdata[3]);
+	*offset = cxd2880_convert2s_complement(ctl_val, 29);
+	*offset = -1 * ((*offset) * (u8)tnr_dmd->bandwidth / 235);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_carrier_offset_sub(struct
+							    cxd2880_tnrdmd
+							    *tnr_dmd,
+							    int *offset)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!offset))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_carrier_offset(tnr_dmd->diver_sub, offset);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_pre_viterbiber(struct cxd2880_tnrdmd
+							*tnr_dmd, u32 *ber)
+{
+	u8 rdata[2];
+	u32 bit_error = 0;
+	u32 period = 0;
+
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!ber))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x10) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x39, rdata,
+				   1) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if ((rdata[0] & 0x01) == 0) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_HW_STATE;
+	}
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x22, rdata,
+				   2) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	bit_error = (rdata[0] << 8) | rdata[1];
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x6F, rdata,
+				   1) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	period = ((rdata[0] & 0x07) == 0) ? 256 : (0x1000 << (rdata[0] & 0x07));
+
+	if ((period == 0) || (bit_error > period))
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	{
+		u32 div = 0;
+		u32 Q = 0;
+		u32 R = 0;
+
+		div = period / 128;
+
+		Q = (bit_error * 3125) / div;
+		R = (bit_error * 3125) % div;
+
+		R *= 25;
+		Q = Q * 25 + R / div;
+		R = R % div;
+
+		if (div / 2 <= R)
+			*ber = Q + 1;
+		else
+			*ber = Q;
+	}
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_pre_rsber(struct cxd2880_tnrdmd
+						   *tnr_dmd, u32 *ber)
+{
+	u8 rdata[3];
+	u32 bit_error = 0;
+	u32 period_exp = 0;
+
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!ber))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0D) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x15, rdata,
+				   3) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if ((rdata[0] & 0x40) == 0)
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	bit_error = ((rdata[0] & 0x3F) << 16) | (rdata[1] << 8) | rdata[2];
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x60, rdata,
+				   1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	period_exp = (rdata[0] & 0x1F);
+
+	if ((period_exp <= 11) && (bit_error > (1U << period_exp) * 204 * 8))
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	{
+		u32 div = 0;
+		u32 Q = 0;
+		u32 R = 0;
+
+		if (period_exp <= 8)
+			div = (1U << period_exp) * 51;
+		else
+			div = (1U << 8) * 51;
+
+		Q = (bit_error * 250) / div;
+		R = (bit_error * 250) % div;
+
+		R *= 1250;
+		Q = Q * 1250 + R / div;
+		R = R % div;
+
+		if (period_exp > 8) {
+			*ber =
+			    (Q + (1 << (period_exp - 9))) >> (period_exp - 8);
+		} else {
+			if (div / 2 <= R)
+				*ber = Q + 1;
+			else
+				*ber = Q;
+		}
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_tps_info(struct cxd2880_tnrdmd
+						  *tnr_dmd,
+						  struct cxd2880_dvbt_tpsinfo
+						  *info)
+{
+	u8 rdata[7];
+	u8 cell_id_ok = 0;
+
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!info))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	ret = is_tps_locked(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+			ret =
+			    cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd->diver_sub,
+							     info);
+
+		return ret;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0D) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x29, rdata,
+				   7) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x11) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0xD5, &cell_id_ok,
+				   1) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	info->constellation =
+	    (enum cxd2880_dvbt_constellation)((rdata[0] >> 6) & 0x03);
+	info->hierarchy = (enum cxd2880_dvbt_hierarchy)((rdata[0] >> 3) & 0x07);
+	info->rate_hp = (enum cxd2880_dvbt_coderate)(rdata[0] & 0x07);
+	info->rate_lp = (enum cxd2880_dvbt_coderate)((rdata[1] >> 5) & 0x07);
+	info->guard = (enum cxd2880_dvbt_guard)((rdata[1] >> 3) & 0x03);
+	info->mode = (enum cxd2880_dvbt_mode)((rdata[1] >> 1) & 0x03);
+	info->fnum = (rdata[2] >> 6) & 0x03;
+	info->length_indicator = rdata[2] & 0x3F;
+	info->cell_id = (u16)((rdata[3] << 8) | rdata[4]);
+	info->reserved_even = rdata[5] & 0x3F;
+	info->reserved_odd = rdata[6] & 0x3F;
+
+	info->cell_id_ok = cell_id_ok & 0x01;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_packet_error_number(struct
+							     cxd2880_tnrdmd
+							     *tnr_dmd,
+							     u32 *pen)
+{
+	u8 rdata[3];
+
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!pen))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0D) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x26, rdata,
+				   3) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (!(rdata[0] & 0x01))
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	*pen = (rdata[1] << 8) | rdata[2];
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_spectrum_sense(struct cxd2880_tnrdmd
+						*tnr_dmd,
+						enum
+						cxd2880_tnrdmd_spectrum_sense
+						*sense)
+{
+	u8 data = 0;
+
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!sense))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	ret = is_tps_locked(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+			ret =
+				cxd2880_tnrdmd_dvbt_mon_spectrum_sense(
+					tnr_dmd->diver_sub, sense);
+
+		return ret;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0D) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x1C, &data,
+				   sizeof(data)) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	*sense =
+	    (data & 0x01) ? CXD2880_TNRDMD_SPECTRUM_INV :
+	    CXD2880_TNRDMD_SPECTRUM_NORMAL;
+
+	return ret;
+}
+
+static enum cxd2880_ret dvbt_read_snr_reg(struct cxd2880_tnrdmd *tnr_dmd,
+					  u16 *reg_value)
+{
+	u8 rdata[2];
+
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!reg_value))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	ret = is_tps_locked(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0D) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x13, rdata,
+				   2) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	*reg_value = (rdata[0] << 8) | rdata[1];
+
+	return ret;
+}
+
+static enum cxd2880_ret dvbt_calc_snr(struct cxd2880_tnrdmd *tnr_dmd,
+				      u32 reg_value, int *snr)
+{
+	if ((!tnr_dmd) || (!snr))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (reg_value == 0)
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	if (reg_value > 4996)
+		reg_value = 4996;
+
+	*snr =
+	    10 * 10 * ((int)cxd2880_math_log10(reg_value) -
+		       (int)cxd2880_math_log10(5350 - reg_value));
+	*snr += 28500;
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+					     int *snr)
+{
+	u16 reg_value = 0;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!snr))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	*snr = -1000 * 1000;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		ret = dvbt_read_snr_reg(tnr_dmd, &reg_value);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		ret = dvbt_calc_snr(tnr_dmd, reg_value, snr);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	} else {
+		int snr_main = 0;
+		int snr_sub = 0;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt_mon_snr_diver(tnr_dmd, snr, &snr_main,
+						      &snr_sub);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_snr_diver(struct cxd2880_tnrdmd
+						   *tnr_dmd, int *snr,
+						   int *snr_main, int *snr_sub)
+{
+	u16 reg_value = 0;
+	u32 reg_value_sum = 0;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!snr) || (!snr_main) || (!snr_sub))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	*snr = -1000 * 1000;
+	*snr_main = -1000 * 1000;
+	*snr_sub = -1000 * 1000;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret = dvbt_read_snr_reg(tnr_dmd, &reg_value);
+	if (ret == CXD2880_RESULT_OK) {
+		ret = dvbt_calc_snr(tnr_dmd, reg_value, snr_main);
+		if (ret != CXD2880_RESULT_OK)
+			reg_value = 0;
+	} else if (ret == CXD2880_RESULT_ERROR_HW_STATE) {
+		reg_value = 0;
+	} else {
+		return ret;
+	}
+
+	reg_value_sum += reg_value;
+
+	ret = dvbt_read_snr_reg(tnr_dmd->diver_sub, &reg_value);
+	if (ret == CXD2880_RESULT_OK) {
+		ret = dvbt_calc_snr(tnr_dmd->diver_sub, reg_value, snr_sub);
+		if (ret != CXD2880_RESULT_OK)
+			reg_value = 0;
+	} else if (ret == CXD2880_RESULT_ERROR_HW_STATE) {
+		reg_value = 0;
+	} else {
+		return ret;
+	}
+
+	reg_value_sum += reg_value;
+
+	ret = dvbt_calc_snr(tnr_dmd, reg_value_sum, snr);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sampling_offset(struct cxd2880_tnrdmd
+							 *tnr_dmd, int *ppm)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!ppm))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 ctl_val_reg[5];
+		u8 nominal_rate_reg[5];
+		u32 trl_ctl_val = 0;
+		u32 trcg_nominal_rate = 0;
+		int num;
+		int den;
+		s8 diff_upper = 0;
+
+		if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		ret = is_tps_locked(tnr_dmd);
+		if (ret != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0D) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x21,
+					   ctl_val_reg,
+					   sizeof(ctl_val_reg)) !=
+		    CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x04) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x60,
+					   nominal_rate_reg,
+					   sizeof(nominal_rate_reg)) !=
+		    CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		slvt_unfreeze_reg(tnr_dmd);
+
+		diff_upper =
+		    (ctl_val_reg[0] & 0x7F) - (nominal_rate_reg[0] & 0x7F);
+
+		if ((diff_upper < -1) || (diff_upper > 1))
+			return CXD2880_RESULT_ERROR_HW_STATE;
+
+		trl_ctl_val = ctl_val_reg[1] << 24;
+		trl_ctl_val |= ctl_val_reg[2] << 16;
+		trl_ctl_val |= ctl_val_reg[3] << 8;
+		trl_ctl_val |= ctl_val_reg[4];
+
+		trcg_nominal_rate = nominal_rate_reg[1] << 24;
+		trcg_nominal_rate |= nominal_rate_reg[2] << 16;
+		trcg_nominal_rate |= nominal_rate_reg[3] << 8;
+		trcg_nominal_rate |= nominal_rate_reg[4];
+
+		trl_ctl_val >>= 1;
+		trcg_nominal_rate >>= 1;
+
+		if (diff_upper == 1)
+			num =
+			    (int)((trl_ctl_val + 0x80000000u) -
+				  trcg_nominal_rate);
+		else if (diff_upper == -1)
+			num =
+			    -(int)((trcg_nominal_rate + 0x80000000u) -
+				   trl_ctl_val);
+		else
+			num = (int)(trl_ctl_val - trcg_nominal_rate);
+
+		den = (nominal_rate_reg[0] & 0x7F) << 24;
+		den |= nominal_rate_reg[1] << 16;
+		den |= nominal_rate_reg[2] << 8;
+		den |= nominal_rate_reg[3];
+		den = (den + (390625 / 2)) / 390625;
+
+		den >>= 1;
+
+		if (num >= 0)
+			*ppm = (num + (den / 2)) / den;
+		else
+			*ppm = (num - (den / 2)) / den;
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sampling_offset_sub(struct
+							     cxd2880_tnrdmd
+							     *tnr_dmd, int *ppm)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!ppm))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret = cxd2880_tnrdmd_dvbt_mon_sampling_offset(tnr_dmd->diver_sub, ppm);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_quality(struct cxd2880_tnrdmd *tnr_dmd,
+						 u8 *quality)
+{
+	struct cxd2880_dvbt_tpsinfo tps;
+	enum cxd2880_dvbt_profile profile = CXD2880_DVBT_PROFILE_HP;
+	u32 ber = 0;
+	int sn = 0;
+	int sn_rel = 0;
+	int ber_sqi = 0;
+
+	static const int nordig_non_hdvbt_db_1000[3][5] = {
+		{5100, 6900, 7900, 8900, 9700},
+		{10800, 13100, 14600, 15600, 16000},
+		{16500, 18700, 20200, 21600, 22500}
+	};
+
+	static const int nordig_hier_hp_dvbt_db_1000[3][2][5] = {
+		{
+		 {9100, 12000, 13600, 15000, 16600},
+		 {10900, 14100, 15700, 19400, 20600}
+		 },
+		{
+		 {6800, 9100, 10400, 11900, 12700},
+		 {8500, 11000, 12800, 15000, 16000}
+		 },
+		{
+		 {5800, 7900, 9100, 10300, 12100},
+		 {8000, 9300, 11600, 13000, 12900}
+		}
+	};
+
+	static const int nordig_hier_lp_dvbt_db_1000[3][2][5] = {
+		{
+		 {12500, 14300, 15300, 16300, 16900},
+		 {16700, 19100, 20900, 22500, 23700}
+		 },
+		{
+		 {15000, 17200, 18400, 19100, 20100},
+		 {18500, 21200, 23600, 24700, 25900}
+		 },
+		{
+		 {19500, 21400, 22500, 23700, 24700},
+		 {21900, 24200, 25600, 26900, 27800}
+		}
+	};
+
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!quality))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd, &tps);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tps.hierarchy != CXD2880_DVBT_HIERARCHY_NON) {
+		u8 data = 0;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x10) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x67, &data,
+					   1) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		profile =
+		    ((data & 0x01) ==
+		     0x01) ? CXD2880_DVBT_PROFILE_LP : CXD2880_DVBT_PROFILE_HP;
+	}
+
+	ret = cxd2880_tnrdmd_dvbt_mon_pre_rsber(tnr_dmd, &ber);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret = cxd2880_tnrdmd_dvbt_mon_snr(tnr_dmd, &sn);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if ((tps.constellation >= CXD2880_DVBT_CONSTELLATION_RESERVED_3) ||
+	    (tps.rate_hp >= CXD2880_DVBT_CODERATE_RESERVED_5) ||
+	    (tps.rate_lp >= CXD2880_DVBT_CODERATE_RESERVED_5) ||
+	    (tps.hierarchy > CXD2880_DVBT_HIERARCHY_4)) {
+		return CXD2880_RESULT_ERROR_OTHER;
+	}
+
+	if ((tps.hierarchy != CXD2880_DVBT_HIERARCHY_NON) &&
+	    (tps.constellation == CXD2880_DVBT_CONSTELLATION_QPSK))
+		return CXD2880_RESULT_ERROR_OTHER;
+
+	if (tps.hierarchy == CXD2880_DVBT_HIERARCHY_NON)
+		sn_rel =
+		    sn -
+		    nordig_non_hdvbt_db_1000[tps.constellation][tps.rate_hp];
+	else if (profile == CXD2880_DVBT_PROFILE_LP)
+		sn_rel =
+		    sn - nordig_hier_lp_dvbt_db_1000[(int)tps.hierarchy -
+						     1][(int)tps.constellation -
+							1][tps.rate_lp];
+	else
+		sn_rel =
+		    sn - nordig_hier_hp_dvbt_db_1000[(int)tps.hierarchy -
+						     1][(int)tps.constellation -
+							1][tps.rate_hp];
+
+	if (ber > 10000) {
+		ber_sqi = 0;
+	} else if (ber > 1) {
+		ber_sqi = (int)(10 * cxd2880_math_log10(ber));
+		ber_sqi = 20 * (7 * 1000 - (ber_sqi)) - 40 * 1000;
+	} else {
+		ber_sqi = 100 * 1000;
+	}
+
+	if (sn_rel < -7 * 1000) {
+		*quality = 0;
+	} else if (sn_rel < 3 * 1000) {
+		int tmp_sqi = (((sn_rel - (3 * 1000)) / 10) + 1000);
+		*quality =
+		    (u8)(((tmp_sqi * ber_sqi) +
+			   (1000000 / 2)) / (1000000)) & 0xFF;
+	} else {
+		*quality = (u8)((ber_sqi + 500) / 1000);
+	}
+
+	if (*quality > 100)
+		*quality = 100;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_per(struct cxd2880_tnrdmd *tnr_dmd,
+					     u32 *per)
+{
+	u32 packet_error = 0;
+	u32 period = 0;
+	u8 rdata[3];
+
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!per))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0D) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x18, rdata,
+				   3) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if ((rdata[0] & 0x01) == 0)
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	packet_error = (rdata[1] << 8) | rdata[2];
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x10) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x5C, rdata,
+				   1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	period = 1U << (rdata[0] & 0x0F);
+
+	if ((period == 0) || (packet_error > period))
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	{
+		u32 div = 0;
+		u32 Q = 0;
+		u32 R = 0;
+
+		div = period;
+
+		Q = (packet_error * 1000) / div;
+		R = (packet_error * 1000) % div;
+
+		R *= 1000;
+		Q = Q * 1000 + R / div;
+		R = R % div;
+
+		if ((div != 1) && (div / 2 <= R))
+			*per = Q + 1;
+		else
+			*per = Q;
+	}
+
+	return ret;
+}
+
+static enum cxd2880_ret dvbt_calc_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+				      int rf_lvl, u8 *ssi)
+{
+	struct cxd2880_dvbt_tpsinfo tps;
+	int prel;
+	int temp_ssi = 0;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	static const int ref_dbm_1000[3][5] = {
+		{-93000, -91000, -90000, -89000, -88000},
+		{-87000, -85000, -84000, -83000, -82000},
+		{-82000, -80000, -78000, -77000, -76000},
+	};
+
+	if ((!tnr_dmd) || (!ssi))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd, &tps);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if ((tps.constellation >= CXD2880_DVBT_CONSTELLATION_RESERVED_3) ||
+	    (tps.rate_hp >= CXD2880_DVBT_CODERATE_RESERVED_5))
+		return CXD2880_RESULT_ERROR_OTHER;
+
+	prel = rf_lvl - ref_dbm_1000[tps.constellation][tps.rate_hp];
+
+	if (prel < -15000)
+		temp_ssi = 0;
+	else if (prel < 0)
+		temp_ssi = ((2 * (prel + 15000)) + 1500) / 3000;
+	else if (prel < 20000)
+		temp_ssi = (((4 * prel) + 500) / 1000) + 10;
+	else if (prel < 35000)
+		temp_ssi = (((2 * (prel - 20000)) + 1500) / 3000) + 90;
+	else
+		temp_ssi = 100;
+
+	*ssi = (temp_ssi > 100) ? 100 : (u8)temp_ssi;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+					     u8 *ssi)
+{
+	int rf_lvl = 0;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!ssi))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd, &rf_lvl);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret = dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_ssi_sub(struct cxd2880_tnrdmd *tnr_dmd,
+						 u8 *ssi)
+{
+	int rf_lvl = 0;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!ssi))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, &rf_lvl);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret = dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	return ret;
+}
+
+static enum cxd2880_ret is_tps_locked(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	u8 sync = 0;
+	u8 tslock = 0;
+	u8 early_unlock = 0;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync, &tslock,
+					      &early_unlock);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (sync != 6)
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	return CXD2880_RESULT_OK;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
new file mode 100644
index 000000000000..486fc466272e
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
@@ -0,0 +1,106 @@
+/*
+ * cxd2880_tnrdmd_dvbt_mon.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T monitor interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT_MON_H
+#define CXD2880_TNRDMD_DVBT_MON_H
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_dvbt.h"
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sync_stat(struct cxd2880_tnrdmd
+						   *tnr_dmd, u8 *sync_stat,
+						   u8 *ts_lock_stat,
+						   u8 *unlock_detected);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(struct cxd2880_tnrdmd
+						       *tnr_dmd, u8 *sync_stat,
+						       u8 *unlock_detected);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_mode_guard(struct cxd2880_tnrdmd
+						    *tnr_dmd,
+						    enum cxd2880_dvbt_mode
+						    *mode,
+						    enum cxd2880_dvbt_guard
+						    *guard);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_carrier_offset(struct cxd2880_tnrdmd
+							*tnr_dmd, int *offset);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_carrier_offset_sub(struct
+							    cxd2880_tnrdmd
+							    *tnr_dmd,
+							    int *offset);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_pre_viterbiber(struct cxd2880_tnrdmd
+							*tnr_dmd, u32 *ber);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_pre_rsber(struct cxd2880_tnrdmd
+						   *tnr_dmd, u32 *ber);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_tps_info(struct cxd2880_tnrdmd
+						  *tnr_dmd,
+						  struct cxd2880_dvbt_tpsinfo
+						  *info);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_packet_error_number(struct
+							     cxd2880_tnrdmd
+							     *tnr_dmd,
+							     u32 *pen);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_spectrum_sense(struct cxd2880_tnrdmd
+						*tnr_dmd,
+						enum
+						cxd2880_tnrdmd_spectrum_sense
+						*sense);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+					     int *snr);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_snr_diver(struct cxd2880_tnrdmd
+						   *tnr_dmd, int *snr,
+						   int *snr_main, int *snr_sub);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sampling_offset(struct cxd2880_tnrdmd
+							 *tnr_dmd, int *ppm);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sampling_offset_sub(struct
+							     cxd2880_tnrdmd
+							     *tnr_dmd,
+							     int *ppm);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_quality(struct cxd2880_tnrdmd *tnr_dmd,
+						 u8 *quality);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_per(struct cxd2880_tnrdmd *tnr_dmd,
+					     u32 *per);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+					     u8 *ssi);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_ssi_sub(struct cxd2880_tnrdmd *tnr_dmd,
+						 u8 *ssi);
+
+#endif
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 11/15] [media] cxd2880: Add DVB-T2 control functions for the driver
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (9 preceding siblings ...)
  2017-04-14  2:35 ` [PATCH v2 10/15] [media] cxd2880: Add DVB-T monitor and integration layer functions Yasunari.Takiguchi
@ 2017-04-14  2:38 ` Yasunari.Takiguchi
  2017-04-14  2:40 ` [PATCH v2 12/15] [media] cxd2880: Add DVB-T2 monitor and integration layer functions Yasunari.Takiguchi
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  2:38 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

Provide definitions, interfaces and functions needed for DVB-T2
of the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
 .../media/dvb-frontends/cxd2880/cxd2880_dvbt2.h    |  402 ++++++
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c   | 1309 ++++++++++++++++++++
 .../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h   |   82 ++
 3 files changed, 1793 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
new file mode 100644
index 000000000000..1870398cba9d
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
@@ -0,0 +1,402 @@
+/*
+ * cxd2880_dvbt2.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 related definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_DVBT2_H
+#define CXD2880_DVBT2_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_dvbt2_profile {
+	CXD2880_DVBT2_PROFILE_BASE,
+	CXD2880_DVBT2_PROFILE_LITE,
+	CXD2880_DVBT2_PROFILE_ANY
+};
+
+enum cxd2880_dvbt2_version {
+	CXD2880_DVBT2_V111,
+	CXD2880_DVBT2_V121,
+	CXD2880_DVBT2_V131
+};
+
+enum cxd2880_dvbt2_s1 {
+	CXD2880_DVBT2_S1_BASE_SISO = 0x00,
+	CXD2880_DVBT2_S1_BASE_MISO = 0x01,
+	CXD2880_DVBT2_S1_NON_DVBT2 = 0x02,
+	CXD2880_DVBT2_S1_LITE_SISO = 0x03,
+	CXD2880_DVBT2_S1_LITE_MISO = 0x04,
+	CXD2880_DVBT2_S1_RSVD3 = 0x05,
+	CXD2880_DVBT2_S1_RSVD4 = 0x06,
+	CXD2880_DVBT2_S1_RSVD5 = 0x07,
+	CXD2880_DVBT2_S1_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_base_s2 {
+	CXD2880_DVBT2_BASE_S2_M2K_G_ANY = 0x00,
+	CXD2880_DVBT2_BASE_S2_M8K_G_DVBT = 0x01,
+	CXD2880_DVBT2_BASE_S2_M4K_G_ANY = 0x02,
+	CXD2880_DVBT2_BASE_S2_M1K_G_ANY = 0x03,
+	CXD2880_DVBT2_BASE_S2_M16K_G_ANY = 0x04,
+	CXD2880_DVBT2_BASE_S2_M32K_G_DVBT = 0x05,
+	CXD2880_DVBT2_BASE_S2_M8K_G_DVBT2 = 0x06,
+	CXD2880_DVBT2_BASE_S2_M32K_G_DVBT2 = 0x07,
+	CXD2880_DVBT2_BASE_S2_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_lite_s2 {
+	CXD2880_DVBT2_LITE_S2_M2K_G_ANY = 0x00,
+	CXD2880_DVBT2_LITE_S2_M8K_G_DVBT = 0x01,
+	CXD2880_DVBT2_LITE_S2_M4K_G_ANY = 0x02,
+	CXD2880_DVBT2_LITE_S2_M16K_G_DVBT2 = 0x03,
+	CXD2880_DVBT2_LITE_S2_M16K_G_DVBT = 0x04,
+	CXD2880_DVBT2_LITE_S2_RSVD1 = 0x05,
+	CXD2880_DVBT2_LITE_S2_M8K_G_DVBT2 = 0x06,
+	CXD2880_DVBT2_LITE_S2_RSVD2 = 0x07,
+	CXD2880_DVBT2_LITE_S2_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_guard {
+	CXD2880_DVBT2_G1_32 = 0x00,
+	CXD2880_DVBT2_G1_16 = 0x01,
+	CXD2880_DVBT2_G1_8 = 0x02,
+	CXD2880_DVBT2_G1_4 = 0x03,
+	CXD2880_DVBT2_G1_128 = 0x04,
+	CXD2880_DVBT2_G19_128 = 0x05,
+	CXD2880_DVBT2_G19_256 = 0x06,
+	CXD2880_DVBT2_G_RSVD1 = 0x07,
+	CXD2880_DVBT2_G_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_mode {
+	CXD2880_DVBT2_M2K = 0x00,
+	CXD2880_DVBT2_M8K = 0x01,
+	CXD2880_DVBT2_M4K = 0x02,
+	CXD2880_DVBT2_M1K = 0x03,
+	CXD2880_DVBT2_M16K = 0x04,
+	CXD2880_DVBT2_M32K = 0x05,
+	CXD2880_DVBT2_M_RSVD1 = 0x06,
+	CXD2880_DVBT2_M_RSVD2 = 0x07
+};
+
+enum cxd2880_dvbt2_bw {
+	CXD2880_DVBT2_BW_8 = 0x00,
+	CXD2880_DVBT2_BW_7 = 0x01,
+	CXD2880_DVBT2_BW_6 = 0x02,
+	CXD2880_DVBT2_BW_5 = 0x03,
+	CXD2880_DVBT2_BW_10 = 0x04,
+	CXD2880_DVBT2_BW_1_7 = 0x05,
+	CXD2880_DVBT2_BW_RSVD1 = 0x06,
+	CXD2880_DVBT2_BW_RSVD2 = 0x07,
+	CXD2880_DVBT2_BW_RSVD3 = 0x08,
+	CXD2880_DVBT2_BW_RSVD4 = 0x09,
+	CXD2880_DVBT2_BW_RSVD5 = 0x0A,
+	CXD2880_DVBT2_BW_RSVD6 = 0x0B,
+	CXD2880_DVBT2_BW_RSVD7 = 0x0C,
+	CXD2880_DVBT2_BW_RSVD8 = 0x0D,
+	CXD2880_DVBT2_BW_RSVD9 = 0x0E,
+	CXD2880_DVBT2_BW_RSVD10 = 0x0F,
+	CXD2880_DVBT2_BW_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_l1pre_type {
+	CXD2880_DVBT2_L1PRE_TYPE_TS = 0x00,
+	CXD2880_DVBT2_L1PRE_TYPE_GS = 0x01,
+	CXD2880_DVBT2_L1PRE_TYPE_TS_GS = 0x02,
+	CXD2880_DVBT2_L1PRE_TYPE_RESERVED = 0x03,
+	CXD2880_DVBT2_L1PRE_TYPE_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_papr {
+	CXD2880_DVBT2_PAPR_0 = 0x00,
+	CXD2880_DVBT2_PAPR_1 = 0x01,
+	CXD2880_DVBT2_PAPR_2 = 0x02,
+	CXD2880_DVBT2_PAPR_3 = 0x03,
+	CXD2880_DVBT2_PAPR_RSVD1 = 0x04,
+	CXD2880_DVBT2_PAPR_RSVD2 = 0x05,
+	CXD2880_DVBT2_PAPR_RSVD3 = 0x06,
+	CXD2880_DVBT2_PAPR_RSVD4 = 0x07,
+	CXD2880_DVBT2_PAPR_RSVD5 = 0x08,
+	CXD2880_DVBT2_PAPR_RSVD6 = 0x09,
+	CXD2880_DVBT2_PAPR_RSVD7 = 0x0A,
+	CXD2880_DVBT2_PAPR_RSVD8 = 0x0B,
+	CXD2880_DVBT2_PAPR_RSVD9 = 0x0C,
+	CXD2880_DVBT2_PAPR_RSVD10 = 0x0D,
+	CXD2880_DVBT2_PAPR_RSVD11 = 0x0E,
+	CXD2880_DVBT2_PAPR_RSVD12 = 0x0F,
+	CXD2880_DVBT2_PAPR_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_l1post_constell {
+	CXD2880_DVBT2_L1POST_BPSK = 0x00,
+	CXD2880_DVBT2_L1POST_QPSK = 0x01,
+	CXD2880_DVBT2_L1POST_QAM16 = 0x02,
+	CXD2880_DVBT2_L1POST_QAM64 = 0x03,
+	CXD2880_DVBT2_L1POST_C_RSVD1 = 0x04,
+	CXD2880_DVBT2_L1POST_C_RSVD2 = 0x05,
+	CXD2880_DVBT2_L1POST_C_RSVD3 = 0x06,
+	CXD2880_DVBT2_L1POST_C_RSVD4 = 0x07,
+	CXD2880_DVBT2_L1POST_C_RSVD5 = 0x08,
+	CXD2880_DVBT2_L1POST_C_RSVD6 = 0x09,
+	CXD2880_DVBT2_L1POST_C_RSVD7 = 0x0A,
+	CXD2880_DVBT2_L1POST_C_RSVD8 = 0x0B,
+	CXD2880_DVBT2_L1POST_C_RSVD9 = 0x0C,
+	CXD2880_DVBT2_L1POST_C_RSVD10 = 0x0D,
+	CXD2880_DVBT2_L1POST_C_RSVD11 = 0x0E,
+	CXD2880_DVBT2_L1POST_C_RSVD12 = 0x0F,
+	CXD2880_DVBT2_L1POST_CONSTELL_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_l1post_cr {
+	CXD2880_DVBT2_L1POST_R1_2 = 0x00,
+	CXD2880_DVBT2_L1POST_R_RSVD1 = 0x01,
+	CXD2880_DVBT2_L1POST_R_RSVD2 = 0x02,
+	CXD2880_DVBT2_L1POST_R_RSVD3 = 0x03,
+	CXD2880_DVBT2_L1POST_R_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_l1post_fec_type {
+	CXD2880_DVBT2_L1POST_FEC_LDPC16K = 0x00,
+	CXD2880_DVBT2_L1POST_FEC_RSVD1 = 0x01,
+	CXD2880_DVBT2_L1POST_FEC_RSVD2 = 0x02,
+	CXD2880_DVBT2_L1POST_FEC_RSVD3 = 0x03,
+	CXD2880_DVBT2_L1POST_FEC_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_pp {
+	CXD2880_DVBT2_PP1 = 0x00,
+	CXD2880_DVBT2_PP2 = 0x01,
+	CXD2880_DVBT2_PP3 = 0x02,
+	CXD2880_DVBT2_PP4 = 0x03,
+	CXD2880_DVBT2_PP5 = 0x04,
+	CXD2880_DVBT2_PP6 = 0x05,
+	CXD2880_DVBT2_PP7 = 0x06,
+	CXD2880_DVBT2_PP8 = 0x07,
+	CXD2880_DVBT2_PP_RSVD1 = 0x08,
+	CXD2880_DVBT2_PP_RSVD2 = 0x09,
+	CXD2880_DVBT2_PP_RSVD3 = 0x0A,
+	CXD2880_DVBT2_PP_RSVD4 = 0x0B,
+	CXD2880_DVBT2_PP_RSVD5 = 0x0C,
+	CXD2880_DVBT2_PP_RSVD6 = 0x0D,
+	CXD2880_DVBT2_PP_RSVD7 = 0x0E,
+	CXD2880_DVBT2_PP_RSVD8 = 0x0F,
+	CXD2880_DVBT2_PP_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_plp_code_rate {
+	CXD2880_DVBT2_R1_2 = 0x00,
+	CXD2880_DVBT2_R3_5 = 0x01,
+	CXD2880_DVBT2_R2_3 = 0x02,
+	CXD2880_DVBT2_R3_4 = 0x03,
+	CXD2880_DVBT2_R4_5 = 0x04,
+	CXD2880_DVBT2_R5_6 = 0x05,
+	CXD2880_DVBT2_R1_3 = 0x06,
+	CXD2880_DVBT2_R2_5 = 0x07,
+	CXD2880_DVBT2_PLP_CR_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_plp_constell {
+	CXD2880_DVBT2_QPSK = 0x00,
+	CXD2880_DVBT2_QAM16 = 0x01,
+	CXD2880_DVBT2_QAM64 = 0x02,
+	CXD2880_DVBT2_QAM256 = 0x03,
+	CXD2880_DVBT2_CON_RSVD1 = 0x04,
+	CXD2880_DVBT2_CON_RSVD2 = 0x05,
+	CXD2880_DVBT2_CON_RSVD3 = 0x06,
+	CXD2880_DVBT2_CON_RSVD4 = 0x07,
+	CXD2880_DVBT2_CONSTELL_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_plp_type {
+	CXD2880_DVBT2_PLP_TYPE_COMMON = 0x00,
+	CXD2880_DVBT2_PLP_TYPE_DATA1 = 0x01,
+	CXD2880_DVBT2_PLP_TYPE_DATA2 = 0x02,
+	CXD2880_DVBT2_PLP_TYPE_RSVD1 = 0x03,
+	CXD2880_DVBT2_PLP_TYPE_RSVD2 = 0x04,
+	CXD2880_DVBT2_PLP_TYPE_RSVD3 = 0x05,
+	CXD2880_DVBT2_PLP_TYPE_RSVD4 = 0x06,
+	CXD2880_DVBT2_PLP_TYPE_RSVD5 = 0x07,
+	CXD2880_DVBT2_PLP_TYPE_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_plp_payload {
+	CXD2880_DVBT2_PLP_PAYLOAD_GFPS = 0x00,
+	CXD2880_DVBT2_PLP_PAYLOAD_GCS = 0x01,
+	CXD2880_DVBT2_PLP_PAYLOAD_GSE = 0x02,
+	CXD2880_DVBT2_PLP_PAYLOAD_TS = 0x03,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD1 = 0x04,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD2 = 0x05,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD3 = 0x06,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD4 = 0x07,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD5 = 0x08,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD6 = 0x09,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD7 = 0x0A,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD8 = 0x0B,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD9 = 0x0C,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD10 = 0x0D,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD11 = 0x0E,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD12 = 0x0F,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD13 = 0x10,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD14 = 0x11,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD15 = 0x12,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD16 = 0x13,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD17 = 0x14,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD18 = 0x15,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD19 = 0x16,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD20 = 0x17,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD21 = 0x18,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD22 = 0x19,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD23 = 0x1A,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD24 = 0x1B,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD25 = 0x1C,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD26 = 0x1D,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD27 = 0x1E,
+	CXD2880_DVBT2_PLP_PAYLOAD_RSVD28 = 0x1F,
+	CXD2880_DVBT2_PLP_PAYLOAD_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_plp_fec {
+	CXD2880_DVBT2_FEC_LDPC_16K = 0x00,
+	CXD2880_DVBT2_FEC_LDPC_64K = 0x01,
+	CXD2880_DVBT2_FEC_RSVD1 = 0x02,
+	CXD2880_DVBT2_FEC_RSVD2 = 0x03,
+	CXD2880_DVBT2_FEC_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_plp_mode {
+	CXD2880_DVBT2_PLP_MODE_NOTSPECIFIED = 0x00,
+	CXD2880_DVBT2_PLP_MODE_NM = 0x01,
+	CXD2880_DVBT2_PLP_MODE_HEM = 0x02,
+	CXD2880_DVBT2_PLP_MODE_RESERVED = 0x03,
+	CXD2880_DVBT2_PLP_MODE_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_plp_btype {
+	CXD2880_DVBT2_PLP_COMMON,
+	CXD2880_DVBT2_PLP_DATA
+};
+
+enum cxd2880_dvbt2_stream {
+	CXD2880_DVBT2_STREAM_GENERIC_PACKETIZED = 0x00,
+	CXD2880_DVBT2_STREAM_GENERIC_CONTINUOUS = 0x01,
+	CXD2880_DVBT2_STREAM_GENERIC_ENCAPSULATED = 0x02,
+	CXD2880_DVBT2_STREAM_TRANSPORT = 0x03,
+	CXD2880_DVBT2_STREAM_UNKNOWN = 0xFF
+};
+
+struct cxd2880_dvbt2_l1pre {
+	enum cxd2880_dvbt2_l1pre_type type;
+	u8 bw_ext;
+	enum cxd2880_dvbt2_s1 s1;
+	u8 s2;
+	u8 mixed;
+	enum cxd2880_dvbt2_mode fft_mode;
+	u8 l1_rep;
+	enum cxd2880_dvbt2_guard gi;
+	enum cxd2880_dvbt2_papr papr;
+	enum cxd2880_dvbt2_l1post_constell mod;
+	enum cxd2880_dvbt2_l1post_cr cr;
+	enum cxd2880_dvbt2_l1post_fec_type fec;
+	u32 l1_post_size;
+	u32 l1_post_info_size;
+	enum cxd2880_dvbt2_pp pp;
+	u8 tx_id_availability;
+	u16 cell_id;
+	u16 network_id;
+	u16 sys_id;
+	u8 num_frames;
+	u16 num_symbols;
+	u8 regen;
+	u8 post_ext;
+	u8 num_rf_freqs;
+	u8 rf_idx;
+	enum cxd2880_dvbt2_version t2_version;
+	u8 l1_post_scrambled;
+	u8 t2_base_lite;
+	u32 crc32;
+};
+
+struct cxd2880_dvbt2_plp {
+	u8 id;
+	enum cxd2880_dvbt2_plp_type type;
+	enum cxd2880_dvbt2_plp_payload payload;
+	u8 ff;
+	u8 first_rf_idx;
+	u8 first_frm_idx;
+	u8 group_id;
+	enum cxd2880_dvbt2_plp_constell constell;
+	enum cxd2880_dvbt2_plp_code_rate plp_cr;
+	u8 rot;
+	enum cxd2880_dvbt2_plp_fec fec;
+	u16 num_blocks_max;
+	u8 frm_int;
+	u8 til_len;
+	u8 til_type;
+	u8 in_band_a_flag;
+	u8 in_band_b_flag;
+	u16 rsvd;
+	enum cxd2880_dvbt2_plp_mode plp_mode;
+	u8 static_flag;
+	u8 static_padding_flag;
+};
+
+struct cxd2880_dvbt2_l1post {
+	u16 sub_slices_per_frame;
+	u8 num_plps;
+	u8 num_aux;
+	u8 aux_cfg_rfu;
+	u8 rf_idx;
+	u32 freq;
+	u8 fef_type;
+	u32 fef_length;
+	u8 fef_intvl;
+};
+
+struct cxd2880_dvbt2_ofdm {
+	u8 mixed;
+	u8 is_miso;
+	enum cxd2880_dvbt2_mode mode;
+	enum cxd2880_dvbt2_guard gi;
+	enum cxd2880_dvbt2_pp pp;
+	u8 bw_ext;
+	enum cxd2880_dvbt2_papr papr;
+	u16 num_symbols;
+};
+
+struct cxd2880_dvbt2_bbheader {
+	enum cxd2880_dvbt2_stream stream_input;
+	u8 is_single_input_stream;
+	u8 is_constant_coding_modulation;
+	u8 issy_indicator;
+	u8 null_packet_deletion;
+	u8 ext;
+	u8 input_stream_identifier;
+	u16 user_packet_length;
+	u16 data_field_length;
+	u8 sync_byte;
+	u32 issy;
+	enum cxd2880_dvbt2_plp_mode plp_mode;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
new file mode 100644
index 000000000000..bdad65b7298a
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
@@ -0,0 +1,1309 @@
+/*
+ * cxd2880_tnrdmd_dvbt2.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control functions for DVB-T2
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+
+static enum cxd2880_ret x_tune_dvbt2_demod_setting(struct cxd2880_tnrdmd
+						   *tnr_dmd,
+						   enum cxd2880_dtv_bandwidth
+						   bandwidth,
+						   enum cxd2880_tnrdmd_clockmode
+						   clk_mode)
+{
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_SYS, 0x31,
+				   0x02) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x04) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x5D,
+				   0x0B) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+		u8 data[2] = { 0x01, 0x01 };
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x00) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0xCE, data,
+					    2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	{
+		u8 data[14] = { 0x07, 0x06, 0x01, 0xF0,
+			0x00, 0x00, 0x04, 0xB0, 0x00, 0x00, 0x09, 0x9C, 0x0E,
+			    0x4C
+		};
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x20) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x8A,
+					   data[0]) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x90,
+					   data[1]) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x25) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0xF0, &data[2],
+					    2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x2A) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0xDC,
+					   data[4]) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0xDE,
+					   data[5]) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x2D) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0x73, &data[6],
+					    4) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0x8F, &data[10],
+					    4) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	{
+		u8 data_a_1[9] = { 0x52, 0x49, 0x2C, 0x51,
+			0x51, 0x3D, 0x15, 0x29, 0x0C
+		};
+		u8 data_b_1[9] = { 0x5D, 0x55, 0x32, 0x5C,
+			0x5C, 0x45, 0x17, 0x2E, 0x0D
+		};
+		u8 data_c_1[9] = { 0x60, 0x00, 0x34, 0x5E,
+			0x5E, 0x47, 0x18, 0x2F, 0x0E
+		};
+
+		u8 data_a_2[13] = { 0x04, 0xE7, 0x94, 0x92,
+			0x09, 0xCF, 0x7E, 0xD0, 0x49, 0xCD, 0xCD, 0x1F, 0x5B
+		};
+		u8 data_b_2[13] = { 0x05, 0x90, 0x27, 0x55,
+			0x0B, 0x20, 0x8F, 0xD6, 0xEA, 0xC8, 0xC8, 0x23, 0x91
+		};
+		u8 data_c_2[13] = { 0x05, 0xB8, 0xD8, 0x00,
+			0x0B, 0x72, 0x93, 0xF3, 0x00, 0xCD, 0xCD, 0x24, 0x95
+		};
+
+		u8 data_a_3[5] = { 0x0B, 0x6A, 0xC9, 0x03,
+			0x33
+		};
+		u8 data_b_3[5] = { 0x01, 0x02, 0xE4, 0x03,
+			0x39
+		};
+		u8 data_c_3[5] = { 0x01, 0x02, 0xEB, 0x03,
+			0x3B
+		};
+
+		u8 *data_1 = NULL;
+		u8 *data_2 = NULL;
+		u8 *data_3 = NULL;
+
+		switch (clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			data_1 = data_a_1;
+			data_2 = data_a_2;
+			data_3 = data_a_3;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			data_1 = data_b_1;
+			data_2 = data_b_2;
+			data_3 = data_b_3;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			data_1 = data_c_1;
+			data_2 = data_c_2;
+			data_3 = data_c_3;
+			break;
+		default:
+			return CXD2880_RESULT_ERROR_SW_STATE;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x04) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0x1D,
+					    &data_1[0], 3) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x22,
+					   data_1[3]) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x24,
+					   data_1[4]) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x26,
+					   data_1[5]) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0x29,
+					    &data_1[6], 2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x2D,
+					   data_1[8]) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x2E,
+						    &data_2[0],
+						    6) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x35,
+						    &data_2[6],
+						    7) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0x3C,
+					    &data_3[0], 2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0x56,
+					    &data_3[2], 3) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	switch (bandwidth) {
+	case CXD2880_DTV_BW_8_MHZ:
+
+		{
+			u8 data_ac[6] = { 0x15, 0x00, 0x00, 0x00,
+				0x00, 0x00
+			};
+			u8 data_b[6] = { 0x14, 0x6A, 0xAA, 0xAA,
+				0xAB, 0x00
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_ac;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x10,
+						    data,
+						    6) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x4A,
+					   0x00) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		{
+			u8 data_a[2] = { 0x19, 0xD2 };
+			u8 data_bc[2] = { 0x3F, 0xFF };
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_bc;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x19,
+						    data,
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		{
+			u8 data_a[2] = { 0x06, 0x2A };
+			u8 data_b[2] = { 0x06, 0x29 };
+			u8 data_c[2] = { 0x06, 0x28 };
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x1B,
+						    data,
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			u8 data_a[9] = { 0x28, 0x00, 0x50, 0x00,
+				0x60, 0x00, 0x00, 0x90, 0x00
+			};
+			u8 data_b[9] = { 0x2D, 0x5E, 0x5A, 0xBD,
+				0x6C, 0xE3, 0x00, 0xA3, 0x55
+			};
+			u8 data_c[9] = { 0x2E, 0xAA, 0x5D, 0x55,
+				0x70, 0x00, 0x00, 0xA8, 0x00
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x4B,
+						    data,
+						    9) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+		break;
+
+	case CXD2880_DTV_BW_7_MHZ:
+
+		{
+			u8 data_ac[6] = { 0x18, 0x00, 0x00, 0x00,
+				0x00, 0x00
+			};
+			u8 data_b[6] = { 0x17, 0x55, 0x55, 0x55,
+				0x55, 0x00
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_ac;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x10,
+						    data,
+						    6) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x4A,
+					   0x02) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		{
+			u8 data[2] = { 0x3F, 0xFF };
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x19,
+						    data,
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		{
+			u8 data_a[2] = { 0x06, 0x23 };
+			u8 data_b[2] = { 0x06, 0x22 };
+			u8 data_c[2] = { 0x06, 0x21 };
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x1B,
+						    data,
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			u8 data_a[9] = { 0x2D, 0xB6, 0x5B, 0x6D,
+				0x6D, 0xB6, 0x00, 0xA4, 0x92
+			};
+			u8 data_b[9] = { 0x33, 0xDA, 0x67, 0xB4,
+				0x7C, 0x71, 0x00, 0xBA, 0xAA
+			};
+			u8 data_c[9] = { 0x35, 0x55, 0x6A, 0xAA,
+				0x80, 0x00, 0x00, 0xC0, 0x00
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x4B,
+						    data,
+						    9) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+		break;
+
+	case CXD2880_DTV_BW_6_MHZ:
+
+		{
+			u8 data_ac[6] = { 0x1C, 0x00, 0x00, 0x00,
+				0x00, 0x00
+			};
+			u8 data_b[6] = { 0x1B, 0x38, 0xE3, 0x8E,
+				0x39, 0x00
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_ac;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x10,
+						    data,
+						    6) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x4A,
+					   0x04) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		{
+			u8 data[2] = { 0x3F, 0xFF };
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x19,
+						    data,
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		{
+			u8 data_a[2] = { 0x06, 0x1C };
+			u8 data_b[2] = { 0x06, 0x1B };
+			u8 data_c[2] = { 0x06, 0x1A };
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x1B,
+						    data,
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			u8 data_a[9] = { 0x35, 0x55, 0x6A, 0xAA,
+				0x80, 0x00, 0x00, 0xC0, 0x00
+			};
+			u8 data_b[9] = { 0x3C, 0x7E, 0x78, 0xFC,
+				0x91, 0x2F, 0x00, 0xD9, 0xC7
+			};
+			u8 data_c[9] = { 0x3E, 0x38, 0x7C, 0x71,
+				0x95, 0x55, 0x00, 0xDF, 0xFF
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x4B,
+						    data,
+						    9) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+		break;
+
+	case CXD2880_DTV_BW_5_MHZ:
+
+		{
+			u8 data_ac[6] = { 0x21, 0x99, 0x99, 0x99,
+				0x9A, 0x00
+			};
+			u8 data_b[6] = { 0x20, 0xAA, 0xAA, 0xAA,
+				0xAB, 0x00
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_ac;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x10,
+						    data,
+						    6) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x4A,
+					   0x06) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		{
+			u8 data[2] = { 0x3F, 0xFF };
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x19,
+						    data,
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		{
+			u8 data_a[2] = { 0x06, 0x15 };
+			u8 data_b[2] = { 0x06, 0x15 };
+			u8 data_c[2] = { 0x06, 0x14 };
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x1B,
+						    data,
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			u8 data_a[9] = { 0x40, 0x00, 0x6A, 0xAA,
+				0x80, 0x00, 0x00, 0xE6, 0x66
+			};
+			u8 data_b[9] = { 0x48, 0x97, 0x78, 0xFC,
+				0x91, 0x2F, 0x01, 0x05, 0x55
+			};
+			u8 data_c[9] = { 0x4A, 0xAA, 0x7C, 0x71,
+				0x95, 0x55, 0x01, 0x0C, 0xCC
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x4B,
+						    data,
+						    9) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+		break;
+
+	case CXD2880_DTV_BW_1_7_MHZ:
+
+		{
+			u8 data_a[6] = { 0x68, 0x0F, 0xA2, 0x32,
+				0xCF, 0x03
+			};
+			u8 data_c[6] = { 0x68, 0x0F, 0xA2, 0x32,
+				0xCF, 0x03
+			};
+			u8 data_b[6] = { 0x65, 0x2B, 0xA4, 0xCD,
+				0xD8, 0x03
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x10,
+						    data,
+						    6) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x4A,
+					   0x03) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		{
+			u8 data[2] = { 0x3F, 0xFF };
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x19,
+						    data,
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		{
+			u8 data_a[2] = { 0x06, 0x0C };
+			u8 data_b[2] = { 0x06, 0x0C };
+			u8 data_c[2] = { 0x06, 0x0B };
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x1B,
+						    data,
+						    2) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+			u8 data_a[9] = { 0x40, 0x00, 0x6A, 0xAA,
+				0x80, 0x00, 0x02, 0xC9, 0x8F
+			};
+			u8 data_b[9] = { 0x48, 0x97, 0x78, 0xFC,
+				0x91, 0x2F, 0x03, 0x29, 0x5D
+			};
+			u8 data_c[9] = { 0x4A, 0xAA, 0x7C, 0x71,
+				0x95, 0x55, 0x03, 0x40, 0x7D
+			};
+			u8 *data = NULL;
+
+			switch (clk_mode) {
+			case CXD2880_TNRDMD_CLOCKMODE_A:
+				data = data_a;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_B:
+				data = data_b;
+				break;
+			case CXD2880_TNRDMD_CLOCKMODE_C:
+				data = data_c;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_SW_STATE;
+			}
+
+			if (tnr_dmd->io->write_regs(tnr_dmd->io,
+						    CXD2880_IO_TGT_DMD, 0x4B,
+						    data,
+						    9) != CXD2880_RESULT_OK)
+				return CXD2880_RESULT_ERROR_IO;
+		}
+		break;
+
+	default:
+		return CXD2880_RESULT_ERROR_SW_STATE;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x00) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0xFD,
+				   0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_sleep_dvbt2_demod_setting(struct cxd2880_tnrdmd
+						    *tnr_dmd)
+{
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		u8 data[] = { 0, 1, 0, 2,
+			0, 4, 0, 8, 0, 16, 0, 32
+		};
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x1D) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0x47, data,
+					    12) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret dvbt2_set_profile(struct cxd2880_tnrdmd *tnr_dmd,
+					  enum cxd2880_dvbt2_profile profile)
+{
+	u8 t2_mode_tune_mode = 0;
+	u8 seq_not2_dtime = 0;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	{
+		u8 dtime1 = 0;
+		u8 dtime2 = 0;
+
+		switch (tnr_dmd->clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			dtime1 = 0x27;
+			dtime2 = 0x0C;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			dtime1 = 0x2C;
+			dtime2 = 0x0D;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			dtime1 = 0x2E;
+			dtime2 = 0x0E;
+			break;
+		default:
+			return CXD2880_RESULT_ERROR_SW_STATE;
+		}
+
+		switch (profile) {
+		case CXD2880_DVBT2_PROFILE_BASE:
+			t2_mode_tune_mode = 0x01;
+			seq_not2_dtime = dtime2;
+			break;
+
+		case CXD2880_DVBT2_PROFILE_LITE:
+			t2_mode_tune_mode = 0x05;
+			seq_not2_dtime = dtime1;
+			break;
+
+		case CXD2880_DVBT2_PROFILE_ANY:
+			t2_mode_tune_mode = 0x00;
+			seq_not2_dtime = dtime1;
+			break;
+
+		default:
+			return CXD2880_RESULT_ERROR_ARG;
+		}
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x2E) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x10,
+				   t2_mode_tune_mode) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x04) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x2C,
+				   seq_not2_dtime) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+					    struct cxd2880_dvbt2_tune_param
+					    *tune_param)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!tune_param))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if ((tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) &&
+	    (tune_param->profile == CXD2880_DVBT2_PROFILE_ANY))
+		return CXD2880_RESULT_ERROR_NOSUPPORT;
+
+	ret =
+	    cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT2,
+						tune_param->center_freq_khz,
+						tune_param->bandwidth, 0, 0);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret =
+	    x_tune_dvbt2_demod_setting(tnr_dmd, tune_param->bandwidth,
+				       tnr_dmd->clk_mode);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret =
+		    x_tune_dvbt2_demod_setting(tnr_dmd->diver_sub,
+					       tune_param->bandwidth,
+					       tnr_dmd->diver_sub->clk_mode);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	ret = dvbt2_set_profile(tnr_dmd, tune_param->profile);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret =
+		    dvbt2_set_profile(tnr_dmd->diver_sub, tune_param->profile);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	if (tune_param->data_plp_id == CXD2880_DVBT2_TUNE_PARAM_PLPID_AUTO) {
+		ret = cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 1, 0);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	} else {
+		ret =
+		    cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 0,
+					     (u8)(tune_param->data_plp_id));
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+					    struct cxd2880_dvbt2_tune_param
+					    *tune_param)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!tune_param))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 en_fef_intmtnt_ctrl = 1;
+
+		switch (tune_param->profile) {
+		case CXD2880_DVBT2_PROFILE_BASE:
+			en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_base;
+			break;
+		case CXD2880_DVBT2_PROFILE_LITE:
+			en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_lite;
+			break;
+		case CXD2880_DVBT2_PROFILE_ANY:
+			if (tnr_dmd->en_fef_intmtnt_base &&
+			    tnr_dmd->en_fef_intmtnt_lite)
+				en_fef_intmtnt_ctrl = 1;
+			else
+				en_fef_intmtnt_ctrl = 0;
+			break;
+		default:
+			return CXD2880_RESULT_ERROR_ARG;
+		}
+
+		ret =
+		    cxd2880_tnrdmd_common_tune_setting2(tnr_dmd,
+							CXD2880_DTV_SYS_DVBT2,
+							en_fef_intmtnt_ctrl);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE;
+	tnr_dmd->frequency_khz = tune_param->center_freq_khz;
+	tnr_dmd->sys = CXD2880_DTV_SYS_DVBT2;
+	tnr_dmd->bandwidth = tune_param->bandwidth;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE;
+		tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz;
+		tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT2;
+		tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_sleep_setting(struct cxd2880_tnrdmd
+						    *tnr_dmd)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret = x_sleep_dvbt2_demod_setting(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+		ret = x_sleep_dvbt2_demod_setting(tnr_dmd->diver_sub);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd
+					       *tnr_dmd,
+					       enum
+					       cxd2880_tnrdmd_lock_result
+					       *lock)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	u8 sync_stat = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 unlock_detected_sub = 0;
+
+	if ((!tnr_dmd) || (!lock))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+					       &unlock_detected);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		if (sync_stat == 6)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		else if (unlock_detected)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+		else
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+		return ret;
+	}
+
+	if (sync_stat == 6) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		return ret;
+	}
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+						   &unlock_detected_sub);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (sync_stat == 6)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+	else if (unlock_detected && unlock_detected_sub)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+	else
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd
+						    *tnr_dmd,
+						    enum
+						    cxd2880_tnrdmd_lock_result
+						    *lock)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	u8 sync_stat = 0;
+	u8 ts_lock = 0;
+	u8 unlock_detected = 0;
+	u8 unlock_detected_sub = 0;
+
+	if ((!tnr_dmd) || (!lock))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+					       &unlock_detected);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		if (ts_lock)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		else if (unlock_detected)
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+		else
+			*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+		return ret;
+	}
+
+	if (ts_lock) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+		return ret;
+	} else if (!unlock_detected) {
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+		return ret;
+	}
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+						   &unlock_detected_sub);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (unlock_detected && unlock_detected_sub)
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+	else
+		*lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_set_plp_cfg(struct cxd2880_tnrdmd
+						  *tnr_dmd, u8 auto_plp,
+						  u8 plp_id)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x23) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (!auto_plp) {
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0xAF,
+					   plp_id) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0xAD,
+				   auto_plp ? 0x00 : 0x01) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_diver_fef_setting(struct cxd2880_tnrdmd
+							*tnr_dmd)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE)
+		return CXD2880_RESULT_OK;
+
+	{
+		struct cxd2880_dvbt2_ofdm ofdm;
+
+		ret = cxd2880_tnrdmd_dvbt2_mon_ofdm(tnr_dmd, &ofdm);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (!ofdm.mixed)
+			return CXD2880_RESULT_OK;
+	}
+
+	{
+		u8 data[] = { 0, 8, 0, 16,
+			0, 32, 0, 64, 0, 128, 1, 0
+		};
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x1D) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_regs(tnr_dmd->io,
+					    CXD2880_IO_TGT_DMD, 0x47, data,
+					    12) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_check_l1post_valid(struct cxd2880_tnrdmd
+							 *tnr_dmd,
+							 u8 *l1_post_valid)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	u8 data;
+
+	if ((!tnr_dmd) || (!l1_post_valid))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0B) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x86, &data,
+				   1) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	*l1_post_valid = data & 0x01;
+
+	return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
new file mode 100644
index 000000000000..8735280f0143
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
@@ -0,0 +1,82 @@
+/*
+ * cxd2880_tnrdmd_dvbt2.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control interface for DVB-T2
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT2_H
+#define CXD2880_TNRDMD_DVBT2_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+
+enum cxd2880_tnrdmd_dvbt2_tune_info {
+	CXD2880_TNRDMD_DVBT2_TUNE_INFO_OK,
+	CXD2880_TNRDMD_DVBT2_TUNE_INFO_INVALID_PLP_ID
+};
+
+struct cxd2880_dvbt2_tune_param {
+	u32 center_freq_khz;
+	enum cxd2880_dtv_bandwidth bandwidth;
+	u16 data_plp_id;
+	enum cxd2880_dvbt2_profile profile;
+	enum cxd2880_tnrdmd_dvbt2_tune_info tune_info;
+};
+
+#define CXD2880_DVBT2_TUNE_PARAM_PLPID_AUTO  0xFFFF
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+					    struct cxd2880_dvbt2_tune_param
+					    *tune_param);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+					    struct cxd2880_dvbt2_tune_param
+					    *tune_param);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_sleep_setting(struct cxd2880_tnrdmd
+						    *tnr_dmd);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd
+					       *tnr_dmd,
+					       enum
+					       cxd2880_tnrdmd_lock_result
+					       *lock);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd
+						    *tnr_dmd,
+						    enum
+						    cxd2880_tnrdmd_lock_result
+						    *lock);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_set_plp_cfg(struct cxd2880_tnrdmd
+						  *tnr_dmd, u8 auto_plp,
+						  u8 plp_id);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_diver_fef_setting(struct cxd2880_tnrdmd
+							*tnr_dmd);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_check_l1post_valid(struct cxd2880_tnrdmd
+							 *tnr_dmd,
+							 u8 *l1_post_valid);
+
+#endif
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 12/15] [media] cxd2880: Add DVB-T2 monitor and integration layer functions
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (10 preceding siblings ...)
  2017-04-14  2:38 ` [PATCH v2 11/15] [media] cxd2880: Add DVB-T2 control functions for the driver Yasunari.Takiguchi
@ 2017-04-14  2:40 ` Yasunari.Takiguchi
  2017-04-14  2:44 ` [PATCH v2 13/15] [media] cxd2880: Add all Makefile files for the driver Yasunari.Takiguchi
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  2:40 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

Provide monitor and integration layer functions (DVB-T2)
for the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
 .../dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c    |  311 +++
 .../dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h    |   64 +
 .../cxd2880/cxd2880_tnrdmd_dvbt2_mon.c             | 2523 ++++++++++++++++++++
 .../cxd2880/cxd2880_tnrdmd_dvbt2_mon.h             |  170 ++
 4 files changed, 3068 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
 create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h

diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
new file mode 100644
index 000000000000..1d60b9c236d8
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
@@ -0,0 +1,311 @@
+/*
+ * cxd2880_integ_dvbt2.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer functions for DVB-T2
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+#include "cxd2880_integ_dvbt2.h"
+
+static enum cxd2880_ret dvbt2_wait_demod_lock(struct cxd2880_tnrdmd *tnr_dmd,
+					      enum cxd2880_dvbt2_profile
+					      profile);
+
+static enum cxd2880_ret dvbt2_wait_l1_post_lock(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_integ_dvbt2_tune(struct cxd2880_tnrdmd *tnr_dmd,
+					  struct cxd2880_dvbt2_tune_param
+					  *tune_param)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!tune_param))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	cxd2880_atomic_set(&tnr_dmd->cancel, 0);
+
+	if ((tune_param->bandwidth != CXD2880_DTV_BW_1_7_MHZ) &&
+	    (tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ) &&
+	    (tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ) &&
+	    (tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ) &&
+	    (tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ)) {
+		return CXD2880_RESULT_ERROR_NOSUPPORT;
+	}
+
+	if ((tune_param->profile != CXD2880_DVBT2_PROFILE_BASE) &&
+	    (tune_param->profile != CXD2880_DVBT2_PROFILE_LITE))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret = cxd2880_tnrdmd_dvbt2_tune1(tnr_dmd, tune_param);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	CXD2880_SLEEP(CXD2880_TNRDMD_WAIT_AGC_STABLE);
+
+	ret = cxd2880_tnrdmd_dvbt2_tune2(tnr_dmd, tune_param);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret = dvbt2_wait_demod_lock(tnr_dmd, tune_param->profile);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret = cxd2880_tnrdmd_dvbt2_diver_fef_setting(tnr_dmd);
+	if (ret == CXD2880_RESULT_ERROR_HW_STATE)
+		return CXD2880_RESULT_ERROR_UNLOCK;
+	else if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret = dvbt2_wait_l1_post_lock(tnr_dmd);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	{
+		u8 plp_not_found;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt2_mon_data_plp_error(tnr_dmd,
+							    &plp_not_found);
+		if (ret == CXD2880_RESULT_ERROR_HW_STATE)
+			return CXD2880_RESULT_ERROR_UNLOCK;
+		else if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (plp_not_found) {
+			ret = CXD2880_RESULT_OK_CONFIRM;
+			tune_param->tune_info =
+			    CXD2880_TNRDMD_DVBT2_TUNE_INFO_INVALID_PLP_ID;
+		} else {
+			tune_param->tune_info =
+			    CXD2880_TNRDMD_DVBT2_TUNE_INFO_OK;
+		}
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_integ_dvbt2_wait_ts_lock(struct cxd2880_tnrdmd
+						  *tnr_dmd,
+						  enum cxd2880_dvbt2_profile
+						  profile)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	enum cxd2880_tnrdmd_lock_result lock =
+	    CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+	u16 timeout = 0;
+	struct cxd2880_stopwatch timer;
+	u8 continue_wait = 1;
+	u32 elapsed = 0;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret = cxd2880_stopwatch_start(&timer);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (profile == CXD2880_DVBT2_PROFILE_BASE)
+		timeout = CXD2880_DVBT2_BASE_WAIT_TS_LOCK;
+	else if (profile == CXD2880_DVBT2_PROFILE_LITE)
+		timeout = CXD2880_DVBT2_LITE_WAIT_TS_LOCK;
+	else
+		return CXD2880_RESULT_ERROR_ARG;
+
+	for (;;) {
+		ret = cxd2880_stopwatch_elapsed(&timer, &elapsed);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (elapsed >= timeout)
+			continue_wait = 0;
+
+		ret = cxd2880_tnrdmd_dvbt2_check_ts_lock(tnr_dmd, &lock);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		switch (lock) {
+		case CXD2880_TNRDMD_LOCK_RESULT_LOCKED:
+			return CXD2880_RESULT_OK;
+
+		case CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED:
+			return CXD2880_RESULT_ERROR_UNLOCK;
+
+		default:
+			break;
+		}
+
+		ret = cxd2880_integ_check_cancellation(tnr_dmd);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (continue_wait) {
+			ret =
+			    cxd2880_stopwatch_sleep(&timer,
+					    CXD2880_DVBT2_WAIT_LOCK_INTVL);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+		} else {
+			ret = CXD2880_RESULT_ERROR_TIMEOUT;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static enum cxd2880_ret dvbt2_wait_demod_lock(struct cxd2880_tnrdmd *tnr_dmd,
+					      enum cxd2880_dvbt2_profile
+					      profile)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	enum cxd2880_tnrdmd_lock_result lock =
+	    CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+	u16 timeout = 0;
+	struct cxd2880_stopwatch timer;
+	u8 continue_wait = 1;
+	u32 elapsed = 0;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret = cxd2880_stopwatch_start(&timer);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if (profile == CXD2880_DVBT2_PROFILE_BASE)
+		timeout = CXD2880_DVBT2_BASE_WAIT_DMD_LOCK;
+	else if ((profile == CXD2880_DVBT2_PROFILE_LITE) ||
+		 (profile == CXD2880_DVBT2_PROFILE_ANY))
+		timeout = CXD2880_DVBT2_LITE_WAIT_DMD_LOCK;
+	else
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	for (;;) {
+		ret = cxd2880_stopwatch_elapsed(&timer, &elapsed);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (elapsed >= timeout)
+			continue_wait = 0;
+
+		ret = cxd2880_tnrdmd_dvbt2_check_demod_lock(tnr_dmd, &lock);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		switch (lock) {
+		case CXD2880_TNRDMD_LOCK_RESULT_LOCKED:
+			return CXD2880_RESULT_OK;
+
+		case CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED:
+			return CXD2880_RESULT_ERROR_UNLOCK;
+
+		default:
+			break;
+		}
+
+		ret = cxd2880_integ_check_cancellation(tnr_dmd);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (continue_wait) {
+			ret =
+			    cxd2880_stopwatch_sleep(&timer,
+					    CXD2880_DVBT2_WAIT_LOCK_INTVL);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+		} else {
+			ret = CXD2880_RESULT_ERROR_TIMEOUT;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static enum cxd2880_ret dvbt2_wait_l1_post_lock(struct cxd2880_tnrdmd *tnr_dmd)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	struct cxd2880_stopwatch timer;
+	u8 continue_wait = 1;
+	u32 elapsed = 0;
+	u8 l1_post_valid;
+
+	if (!tnr_dmd)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret = cxd2880_stopwatch_start(&timer);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	for (;;) {
+		ret = cxd2880_stopwatch_elapsed(&timer, &elapsed);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (elapsed >= CXD2880_DVBT2_L1POST_TIMEOUT)
+			continue_wait = 0;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt2_check_l1post_valid(tnr_dmd,
+							    &l1_post_valid);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (l1_post_valid)
+			return CXD2880_RESULT_OK;
+
+		ret = cxd2880_integ_check_cancellation(tnr_dmd);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		if (continue_wait) {
+			ret =
+			    cxd2880_stopwatch_sleep(&timer,
+					    CXD2880_DVBT2_WAIT_LOCK_INTVL);
+			if (ret != CXD2880_RESULT_OK)
+				return ret;
+		} else {
+			ret = CXD2880_RESULT_ERROR_TIMEOUT;
+			break;
+		}
+	}
+
+	return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
new file mode 100644
index 000000000000..bc72eb8188f2
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
@@ -0,0 +1,64 @@
+/*
+ * cxd2880_integ_dvbt2.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer interface for DVB-T2
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_INTEG_DVBT2_H
+#define CXD2880_INTEG_DVBT2_H
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_integ.h"
+
+#define CXD2880_DVBT2_BASE_WAIT_DMD_LOCK     3500
+#define CXD2880_DVBT2_BASE_WAIT_TS_LOCK	1500
+#define CXD2880_DVBT2_LITE_WAIT_DMD_LOCK     5000
+#define CXD2880_DVBT2_LITE_WAIT_TS_LOCK	2300
+#define CXD2880_DVBT2_WAIT_LOCK_INTVL       10
+#define CXD2880_DVBT2_L1POST_TIMEOUT	   500
+
+struct cxd2880_integ_dvbt2_scan_param {
+	u32 start_frequency_khz;
+	u32 end_frequency_khz;
+	u32 step_frequency_khz;
+	enum cxd2880_dtv_bandwidth bandwidth;
+	enum cxd2880_dvbt2_profile t2_profile;
+};
+
+struct cxd2880_integ_dvbt2_scan_result {
+	u32 center_freq_khz;
+	enum cxd2880_ret tune_result;
+	struct cxd2880_dvbt2_tune_param dvbt2_tune_param;
+};
+
+enum cxd2880_ret cxd2880_integ_dvbt2_tune(struct cxd2880_tnrdmd *tnr_dmd,
+					  struct cxd2880_dvbt2_tune_param
+					  *tune_param);
+
+enum cxd2880_ret cxd2880_integ_dvbt2_wait_ts_lock(struct cxd2880_tnrdmd
+						  *tnr_dmd,
+						  enum cxd2880_dvbt2_profile
+						  profile);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
new file mode 100644
index 000000000000..235db16f4a08
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
@@ -0,0 +1,2523 @@
+/*
+ * cxd2880_tnrdmd_dvbt2_mon.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 monitor functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+#include "cxd2880_math.h"
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sync_stat(struct cxd2880_tnrdmd
+						    *tnr_dmd, u8 *sync_stat,
+						    u8 *ts_lock_stat,
+						    u8 *unlock_detected)
+{
+	if ((!tnr_dmd) || (!sync_stat) || (!ts_lock_stat) || (!unlock_detected))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0B) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	{
+		u8 data;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x10, &data,
+					   sizeof(data)) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		*sync_stat = data & 0x07;
+		*ts_lock_stat = ((data & 0x20) ? 1 : 0);
+		*unlock_detected = ((data & 0x10) ? 1 : 0);
+	}
+
+	if (*sync_stat == 0x07)
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(struct cxd2880_tnrdmd
+							*tnr_dmd,
+							u8 *sync_stat,
+							u8 *unlock_detected)
+{
+	u8 ts_lock_stat = 0;
+
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!sync_stat) || (!unlock_detected))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd->diver_sub, sync_stat,
+					       &ts_lock_stat, unlock_detected);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_carrier_offset(struct cxd2880_tnrdmd
+							 *tnr_dmd, int *offset)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!offset))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 data[4];
+		u32 ctl_val = 0;
+		u8 sync_state = 0;
+		u8 ts_lock = 0;
+		u8 unlock_detected = 0;
+
+		if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+						       &ts_lock,
+						       &unlock_detected);
+		if (ret != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (sync_state != 6) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0B) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x30, data,
+					   sizeof(data)) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		slvt_unfreeze_reg(tnr_dmd);
+
+		ctl_val =
+		    ((data[0] & 0x0F) << 24) | (data[1] << 16) | (data[2] << 8)
+		    | (data[3]);
+		*offset = cxd2880_convert2s_complement(ctl_val, 28);
+
+		switch (tnr_dmd->bandwidth) {
+		case CXD2880_DTV_BW_1_7_MHZ:
+			*offset = -1 * ((*offset) / 582);
+			break;
+		case CXD2880_DTV_BW_5_MHZ:
+		case CXD2880_DTV_BW_6_MHZ:
+		case CXD2880_DTV_BW_7_MHZ:
+		case CXD2880_DTV_BW_8_MHZ:
+			*offset =
+			    -1 * ((*offset) * (u8)tnr_dmd->bandwidth / 940);
+			break;
+		default:
+			return CXD2880_RESULT_ERROR_SW_STATE;
+		}
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_carrier_offset_sub(struct
+							     cxd2880_tnrdmd
+							     *tnr_dmd,
+							     int *offset)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!offset))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_carrier_offset(tnr_dmd->diver_sub, offset);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_l1_pre(struct cxd2880_tnrdmd *tnr_dmd,
+						 struct cxd2880_dvbt2_l1pre
+						 *l1_pre)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!l1_pre))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 data[37];
+		u8 sync_state = 0;
+		u8 ts_lock = 0;
+		u8 unlock_detected = 0;
+		u8 version = 0;
+		enum cxd2880_dvbt2_profile profile;
+
+		if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+						       &ts_lock,
+						       &unlock_detected);
+		if (ret != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+
+		if (sync_state < 5) {
+			if (tnr_dmd->diver_mode ==
+			    CXD2880_TNRDMD_DIVERMODE_MAIN) {
+				ret =
+				    cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub
+				    (tnr_dmd, &sync_state, &unlock_detected);
+				if (ret != CXD2880_RESULT_OK) {
+					slvt_unfreeze_reg(tnr_dmd);
+					return ret;
+				}
+
+				if (sync_state < 5) {
+					slvt_unfreeze_reg(tnr_dmd);
+					return CXD2880_RESULT_ERROR_HW_STATE;
+				}
+			} else {
+				slvt_unfreeze_reg(tnr_dmd);
+				return CXD2880_RESULT_ERROR_HW_STATE;
+			}
+		}
+
+		ret = cxd2880_tnrdmd_dvbt2_mon_profile(tnr_dmd, &profile);
+		if (ret != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0B) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x61, data,
+					   sizeof(data)) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+		slvt_unfreeze_reg(tnr_dmd);
+
+		l1_pre->type = (enum cxd2880_dvbt2_l1pre_type)data[0];
+		l1_pre->bw_ext = data[1] & 0x01;
+		l1_pre->s1 = (enum cxd2880_dvbt2_s1)(data[2] & 0x07);
+		l1_pre->s2 = data[3] & 0x0F;
+		l1_pre->l1_rep = data[4] & 0x01;
+		l1_pre->gi = (enum cxd2880_dvbt2_guard)(data[5] & 0x07);
+		l1_pre->papr = (enum cxd2880_dvbt2_papr)(data[6] & 0x0F);
+		l1_pre->mod =
+		    (enum cxd2880_dvbt2_l1post_constell)(data[7] & 0x0F);
+		l1_pre->cr = (enum cxd2880_dvbt2_l1post_cr)(data[8] & 0x03);
+		l1_pre->fec =
+		    (enum cxd2880_dvbt2_l1post_fec_type)(data[9] & 0x03);
+		l1_pre->l1_post_size = (data[10] & 0x03) << 16;
+		l1_pre->l1_post_size |= (data[11]) << 8;
+		l1_pre->l1_post_size |= (data[12]);
+		l1_pre->l1_post_info_size = (data[13] & 0x03) << 16;
+		l1_pre->l1_post_info_size |= (data[14]) << 8;
+		l1_pre->l1_post_info_size |= (data[15]);
+		l1_pre->pp = (enum cxd2880_dvbt2_pp)(data[16] & 0x0F);
+		l1_pre->tx_id_availability = data[17];
+		l1_pre->cell_id = (data[18] << 8);
+		l1_pre->cell_id |= (data[19]);
+		l1_pre->network_id = (data[20] << 8);
+		l1_pre->network_id |= (data[21]);
+		l1_pre->sys_id = (data[22] << 8);
+		l1_pre->sys_id |= (data[23]);
+		l1_pre->num_frames = data[24];
+		l1_pre->num_symbols = (data[25] & 0x0F) << 8;
+		l1_pre->num_symbols |= data[26];
+		l1_pre->regen = data[27] & 0x07;
+		l1_pre->post_ext = data[28] & 0x01;
+		l1_pre->num_rf_freqs = data[29] & 0x07;
+		l1_pre->rf_idx = data[30] & 0x07;
+		version = (data[31] & 0x03) << 2;
+		version |= (data[32] & 0xC0) >> 6;
+		l1_pre->t2_version = (enum cxd2880_dvbt2_version)version;
+		l1_pre->l1_post_scrambled = (data[32] & 0x20) >> 5;
+		l1_pre->t2_base_lite = (data[32] & 0x10) >> 4;
+		l1_pre->crc32 = (data[33] << 24);
+		l1_pre->crc32 |= (data[34] << 16);
+		l1_pre->crc32 |= (data[35] << 8);
+		l1_pre->crc32 |= data[36];
+
+		if (profile == CXD2880_DVBT2_PROFILE_BASE) {
+			switch ((l1_pre->s2 >> 1)) {
+			case CXD2880_DVBT2_BASE_S2_M1K_G_ANY:
+				l1_pre->fft_mode = CXD2880_DVBT2_M1K;
+				break;
+			case CXD2880_DVBT2_BASE_S2_M2K_G_ANY:
+				l1_pre->fft_mode = CXD2880_DVBT2_M2K;
+				break;
+			case CXD2880_DVBT2_BASE_S2_M4K_G_ANY:
+				l1_pre->fft_mode = CXD2880_DVBT2_M4K;
+				break;
+			case CXD2880_DVBT2_BASE_S2_M8K_G_DVBT:
+			case CXD2880_DVBT2_BASE_S2_M8K_G_DVBT2:
+				l1_pre->fft_mode = CXD2880_DVBT2_M8K;
+				break;
+			case CXD2880_DVBT2_BASE_S2_M16K_G_ANY:
+				l1_pre->fft_mode = CXD2880_DVBT2_M16K;
+				break;
+			case CXD2880_DVBT2_BASE_S2_M32K_G_DVBT:
+			case CXD2880_DVBT2_BASE_S2_M32K_G_DVBT2:
+				l1_pre->fft_mode = CXD2880_DVBT2_M32K;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_HW_STATE;
+			}
+		} else if (profile == CXD2880_DVBT2_PROFILE_LITE) {
+			switch ((l1_pre->s2 >> 1)) {
+			case CXD2880_DVBT2_LITE_S2_M2K_G_ANY:
+				l1_pre->fft_mode = CXD2880_DVBT2_M2K;
+				break;
+			case CXD2880_DVBT2_LITE_S2_M4K_G_ANY:
+				l1_pre->fft_mode = CXD2880_DVBT2_M4K;
+				break;
+			case CXD2880_DVBT2_LITE_S2_M8K_G_DVBT:
+			case CXD2880_DVBT2_LITE_S2_M8K_G_DVBT2:
+				l1_pre->fft_mode = CXD2880_DVBT2_M8K;
+				break;
+			case CXD2880_DVBT2_LITE_S2_M16K_G_DVBT:
+			case CXD2880_DVBT2_LITE_S2_M16K_G_DVBT2:
+				l1_pre->fft_mode = CXD2880_DVBT2_M16K;
+				break;
+			default:
+				return CXD2880_RESULT_ERROR_HW_STATE;
+			}
+		} else {
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		l1_pre->mixed = l1_pre->s2 & 0x01;
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_version(struct cxd2880_tnrdmd
+						  *tnr_dmd,
+						  enum cxd2880_dvbt2_version
+						  *ver)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	u8 version = 0;
+
+	if ((!tnr_dmd) || (!ver))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 data[2];
+		u8 sync_state = 0;
+		u8 ts_lock = 0;
+		u8 unlock_detected = 0;
+
+		if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+						       &ts_lock,
+						       &unlock_detected);
+		if (ret != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (sync_state < 5) {
+			if (tnr_dmd->diver_mode ==
+			    CXD2880_TNRDMD_DIVERMODE_MAIN) {
+				ret =
+				    cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub
+				    (tnr_dmd, &sync_state, &unlock_detected);
+				if (ret != CXD2880_RESULT_OK) {
+					slvt_unfreeze_reg(tnr_dmd);
+					return ret;
+				}
+
+				if (sync_state < 5) {
+					slvt_unfreeze_reg(tnr_dmd);
+					return CXD2880_RESULT_ERROR_HW_STATE;
+				}
+			} else {
+				slvt_unfreeze_reg(tnr_dmd);
+				return CXD2880_RESULT_ERROR_HW_STATE;
+			}
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0B) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x80, data,
+					   sizeof(data)) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		slvt_unfreeze_reg(tnr_dmd);
+
+		version = ((data[0] & 0x03) << 2);
+		version |= ((data[1] & 0xC0) >> 6);
+		*ver = (enum cxd2880_dvbt2_version)version;
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ofdm(struct cxd2880_tnrdmd *tnr_dmd,
+					       struct cxd2880_dvbt2_ofdm *ofdm)
+{
+	if ((!tnr_dmd) || (!ofdm))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 data[5];
+		u8 sync_state = 0;
+		u8 ts_lock = 0;
+		u8 unlock_detected = 0;
+		enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+		if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+						       &ts_lock,
+						       &unlock_detected);
+		if (ret != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (sync_state != 6) {
+			slvt_unfreeze_reg(tnr_dmd);
+
+			ret = CXD2880_RESULT_ERROR_HW_STATE;
+
+			if (tnr_dmd->diver_mode ==
+			    CXD2880_TNRDMD_DIVERMODE_MAIN)
+				ret =
+				    cxd2880_tnrdmd_dvbt2_mon_ofdm(
+					tnr_dmd->diver_sub, ofdm);
+
+			return ret;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0B) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x1D, data,
+					   sizeof(data)) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		slvt_unfreeze_reg(tnr_dmd);
+
+		ofdm->mixed = ((data[0] & 0x20) ? 1 : 0);
+		ofdm->is_miso = ((data[0] & 0x10) >> 4);
+		ofdm->mode = (enum cxd2880_dvbt2_mode)(data[0] & 0x07);
+		ofdm->gi = (enum cxd2880_dvbt2_guard)((data[1] & 0x70) >> 4);
+		ofdm->pp = (enum cxd2880_dvbt2_pp)(data[1] & 0x07);
+		ofdm->bw_ext = (data[2] & 0x10) >> 4;
+		ofdm->papr = (enum cxd2880_dvbt2_papr)(data[2] & 0x0F);
+		ofdm->num_symbols = (data[3] << 8) | data[4];
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_data_plps(struct cxd2880_tnrdmd
+						    *tnr_dmd, u8 *plp_ids,
+						    u8 *num_plps)
+{
+	if ((!tnr_dmd) || (!num_plps))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 l1_post_ok = 0;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0B) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x86,
+					   &l1_post_ok,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (!(l1_post_ok & 0x01)) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0xC1, num_plps,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (*num_plps == 0) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_OTHER;
+		}
+
+		if (!plp_ids) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_OK;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0xC2, plp_ids,
+					   ((*num_plps >
+					     62) ? 62 : *num_plps)) !=
+		    CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (*num_plps > 62) {
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_DMD, 0x00,
+						   0x0C) != CXD2880_RESULT_OK) {
+				slvt_unfreeze_reg(tnr_dmd);
+				return CXD2880_RESULT_ERROR_IO;
+			}
+
+			if (tnr_dmd->io->read_regs(tnr_dmd->io,
+						   CXD2880_IO_TGT_DMD, 0x10,
+						   plp_ids + 62,
+						   *num_plps - 62) !=
+			    CXD2880_RESULT_OK) {
+				slvt_unfreeze_reg(tnr_dmd);
+				return CXD2880_RESULT_ERROR_IO;
+			}
+		}
+
+		slvt_unfreeze_reg(tnr_dmd);
+	}
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_active_plp(struct cxd2880_tnrdmd
+						     *tnr_dmd,
+						     enum
+						     cxd2880_dvbt2_plp_btype
+						     type,
+						     struct cxd2880_dvbt2_plp
+						     *plp_info)
+{
+	if ((!tnr_dmd) || (!plp_info))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 data[20];
+		u8 addr = 0;
+		u8 index = 0;
+		u8 l1_post_ok = 0;
+
+		if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0B) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x86,
+					   &l1_post_ok,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (!l1_post_ok) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		if (type == CXD2880_DVBT2_PLP_COMMON)
+			addr = 0xA9;
+		else
+			addr = 0x96;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, addr, data,
+					   sizeof(data)) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		slvt_unfreeze_reg(tnr_dmd);
+
+		if ((type == CXD2880_DVBT2_PLP_COMMON) && (data[13] == 0))
+			return CXD2880_RESULT_ERROR_HW_STATE;
+
+		plp_info->id = data[index++];
+		plp_info->type =
+		    (enum cxd2880_dvbt2_plp_type)(data[index++] & 0x07);
+		plp_info->payload =
+		    (enum cxd2880_dvbt2_plp_payload)(data[index++] & 0x1F);
+		plp_info->ff = data[index++] & 0x01;
+		plp_info->first_rf_idx = data[index++] & 0x07;
+		plp_info->first_frm_idx = data[index++];
+		plp_info->group_id = data[index++];
+		plp_info->plp_cr =
+		    (enum cxd2880_dvbt2_plp_code_rate)(data[index++] & 0x07);
+		plp_info->constell =
+		    (enum cxd2880_dvbt2_plp_constell)(data[index++] & 0x07);
+		plp_info->rot = data[index++] & 0x01;
+		plp_info->fec =
+		    (enum cxd2880_dvbt2_plp_fec)(data[index++] & 0x03);
+		plp_info->num_blocks_max = (u16)((data[index++] & 0x03)) << 8;
+		plp_info->num_blocks_max |= data[index++];
+		plp_info->frm_int = data[index++];
+		plp_info->til_len = data[index++];
+		plp_info->til_type = data[index++] & 0x01;
+
+		plp_info->in_band_a_flag = data[index++] & 0x01;
+		plp_info->rsvd = data[index++] << 8;
+		plp_info->rsvd |= data[index++];
+
+		plp_info->in_band_b_flag =
+		    (u8)((plp_info->rsvd & 0x8000) >> 15);
+		plp_info->plp_mode =
+		    (enum cxd2880_dvbt2_plp_mode)((plp_info->rsvd & 0x000C) >>
+						  2);
+		plp_info->static_flag = (u8)((plp_info->rsvd & 0x0002) >> 1);
+		plp_info->static_padding_flag = (u8)(plp_info->rsvd & 0x0001);
+		plp_info->rsvd = (u16)((plp_info->rsvd & 0x7FF0) >> 4);
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_data_plp_error(struct cxd2880_tnrdmd
+							 *tnr_dmd,
+							 u8 *plp_error)
+{
+	if ((!tnr_dmd) || (!plp_error))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	{
+		u8 data;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0B) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x86, &data,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if ((data & 0x01) == 0x00) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0xC0, &data,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		slvt_unfreeze_reg(tnr_dmd);
+
+		*plp_error = data & 0x01;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_l1_change(struct cxd2880_tnrdmd
+						    *tnr_dmd, u8 *l1_change)
+{
+	if ((!tnr_dmd) || (!l1_change))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 data;
+		u8 sync_state = 0;
+		u8 ts_lock = 0;
+		u8 unlock_detected = 0;
+		enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+		if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+						       &ts_lock,
+						       &unlock_detected);
+		if (ret != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (sync_state < 5) {
+			if (tnr_dmd->diver_mode ==
+			    CXD2880_TNRDMD_DIVERMODE_MAIN) {
+				ret =
+				    cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub
+				    (tnr_dmd, &sync_state, &unlock_detected);
+				if (ret != CXD2880_RESULT_OK) {
+					slvt_unfreeze_reg(tnr_dmd);
+					return ret;
+				}
+
+				if (sync_state < 5) {
+					slvt_unfreeze_reg(tnr_dmd);
+					return CXD2880_RESULT_ERROR_HW_STATE;
+				}
+			} else {
+				slvt_unfreeze_reg(tnr_dmd);
+				return CXD2880_RESULT_ERROR_HW_STATE;
+			}
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0B) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x5F, &data,
+					   sizeof(data)) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		*l1_change = data & 0x01;
+		if (*l1_change) {
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_DMD, 0x00,
+						   0x22) != CXD2880_RESULT_OK) {
+				slvt_unfreeze_reg(tnr_dmd);
+				return CXD2880_RESULT_ERROR_IO;
+			}
+
+			if (tnr_dmd->io->write_reg(tnr_dmd->io,
+						   CXD2880_IO_TGT_DMD, 0x16,
+						   0x01) != CXD2880_RESULT_OK) {
+				slvt_unfreeze_reg(tnr_dmd);
+				return CXD2880_RESULT_ERROR_IO;
+			}
+		}
+		slvt_unfreeze_reg(tnr_dmd);
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_l1_post(struct cxd2880_tnrdmd
+						  *tnr_dmd,
+						  struct cxd2880_dvbt2_l1post
+						  *l1_post)
+{
+	if ((!tnr_dmd) || (!l1_post))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 data[16];
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0B) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x86, data,
+					   sizeof(data)) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (!(data[0] & 0x01))
+			return CXD2880_RESULT_ERROR_HW_STATE;
+
+		l1_post->sub_slices_per_frame = (data[1] & 0x7F) << 8;
+		l1_post->sub_slices_per_frame |= data[2];
+		l1_post->num_plps = data[3];
+		l1_post->num_aux = data[4] & 0x0F;
+		l1_post->aux_cfg_rfu = data[5];
+		l1_post->rf_idx = data[6] & 0x07;
+		l1_post->freq = data[7] << 24;
+		l1_post->freq |= data[8] << 16;
+		l1_post->freq |= data[9] << 8;
+		l1_post->freq |= data[10];
+		l1_post->fef_type = data[11] & 0x0F;
+		l1_post->fef_length = data[12] << 16;
+		l1_post->fef_length |= data[13] << 8;
+		l1_post->fef_length |= data[14];
+		l1_post->fef_intvl = data[15];
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_bbheader(struct cxd2880_tnrdmd
+						   *tnr_dmd,
+						   enum cxd2880_dvbt2_plp_btype
+						   type,
+						   struct cxd2880_dvbt2_bbheader
+						   *bbheader)
+{
+	if ((!tnr_dmd) || (!bbheader))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	{
+		enum cxd2880_ret ret = CXD2880_RESULT_OK;
+		u8 sync_state = 0;
+		u8 ts_lock = 0;
+		u8 unlock_detected = 0;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+						       &ts_lock,
+						       &unlock_detected);
+		if (ret != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+
+		if (!ts_lock) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0B) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (type == CXD2880_DVBT2_PLP_COMMON) {
+		u8 l1_post_ok;
+		u8 data;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x86,
+					   &l1_post_ok,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (!(l1_post_ok & 0x01)) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0xB6, &data,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (data == 0) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+	}
+
+	{
+		u8 data[14];
+		u8 addr = 0;
+
+		if (type == CXD2880_DVBT2_PLP_COMMON)
+			addr = 0x51;
+		else
+			addr = 0x42;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, addr, data,
+					   sizeof(data)) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		slvt_unfreeze_reg(tnr_dmd);
+
+		bbheader->stream_input =
+		    (enum cxd2880_dvbt2_stream)((data[0] >> 6) & 0x03);
+		bbheader->is_single_input_stream = (u8)((data[0] >> 5) & 0x01);
+		bbheader->is_constant_coding_modulation =
+		    (u8)((data[0] >> 4) & 0x01);
+		bbheader->issy_indicator = (u8)((data[0] >> 3) & 0x01);
+		bbheader->null_packet_deletion = (u8)((data[0] >> 2) & 0x01);
+		bbheader->ext = (u8)(data[0] & 0x03);
+
+		bbheader->input_stream_identifier = data[1];
+		bbheader->plp_mode =
+		    (data[3] & 0x01) ? CXD2880_DVBT2_PLP_MODE_HEM :
+		    CXD2880_DVBT2_PLP_MODE_NM;
+		bbheader->data_field_length = (u16)((data[4] << 8) | data[5]);
+
+		if (bbheader->plp_mode == CXD2880_DVBT2_PLP_MODE_NM) {
+			bbheader->user_packet_length =
+			    (u16)((data[6] << 8) | data[7]);
+			bbheader->sync_byte = data[8];
+			bbheader->issy = 0;
+		} else {
+			bbheader->user_packet_length = 0;
+			bbheader->sync_byte = 0;
+			bbheader->issy =
+			    (u32)((data[11] << 16) | (data[12] << 8) |
+				   data[13]);
+		}
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_in_bandb_ts_rate(struct cxd2880_tnrdmd
+						   *tnr_dmd,
+						   enum
+						   cxd2880_dvbt2_plp_btype
+						   type,
+						   u32 *ts_rate_bps)
+{
+	if ((!tnr_dmd) || (!ts_rate_bps))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	{
+		enum cxd2880_ret ret = CXD2880_RESULT_OK;
+		u8 sync_state = 0;
+		u8 ts_lock = 0;
+		u8 unlock_detected = 0;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+						       &ts_lock,
+						       &unlock_detected);
+		if (ret != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return ret;
+		}
+
+		if (!ts_lock) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0B) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	{
+		u8 l1_post_ok = 0;
+		u8 addr = 0;
+		u8 data = 0;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x86,
+					   &l1_post_ok,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (!(l1_post_ok & 0x01)) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		if (type == CXD2880_DVBT2_PLP_COMMON)
+			addr = 0xBA;
+		else
+			addr = 0xA7;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, addr, &data,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if ((data & 0x80) == 0x00) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+	}
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x25) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	{
+		u8 data[4];
+		u8 addr = 0;
+
+		if (type == CXD2880_DVBT2_PLP_COMMON)
+			addr = 0xA6;
+		else
+			addr = 0xAA;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, addr, &data[0],
+					   4) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		*ts_rate_bps =
+		    (u32)(((data[0] & 0x07) << 24) | (data[1] << 16) |
+			   (data[2] << 8) | data[3]);
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(struct cxd2880_tnrdmd
+						 *tnr_dmd,
+						 enum
+						 cxd2880_tnrdmd_spectrum_sense
+						 *sense)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	u8 sync_state = 0;
+	u8 ts_lock = 0;
+	u8 early_unlock = 0;
+
+	if ((!tnr_dmd) || (!sense))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, &ts_lock,
+					       &early_unlock);
+	if (ret != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return ret;
+	}
+
+	if (sync_state != 6) {
+		slvt_unfreeze_reg(tnr_dmd);
+
+		ret = CXD2880_RESULT_ERROR_HW_STATE;
+
+		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+			ret =
+			    cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(
+				tnr_dmd->diver_sub, sense);
+
+		return ret;
+	}
+
+	{
+		u8 data = 0;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0B) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x2F, &data,
+					   sizeof(data)) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		slvt_unfreeze_reg(tnr_dmd);
+
+		*sense =
+		    (data & 0x01) ? CXD2880_TNRDMD_SPECTRUM_INV :
+		    CXD2880_TNRDMD_SPECTRUM_NORMAL;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret dvbt2_read_snr_reg(struct cxd2880_tnrdmd *tnr_dmd,
+					   u16 *reg_value)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!reg_value))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	{
+		u8 sync_state = 0;
+		u8 ts_lock = 0;
+		u8 unlock_detected = 0;
+		u8 data[2];
+
+		if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+						       &ts_lock,
+						       &unlock_detected);
+		if (ret != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (sync_state != 6) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0B) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x13, data,
+					   sizeof(data)) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		slvt_unfreeze_reg(tnr_dmd);
+
+		*reg_value = (data[0] << 8) | data[1];
+	}
+
+	return ret;
+}
+
+static enum cxd2880_ret dvbt2_calc_snr(struct cxd2880_tnrdmd *tnr_dmd,
+				       u32 reg_value, int *snr)
+{
+	if ((!tnr_dmd) || (!snr))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (reg_value == 0)
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	if (reg_value > 10876)
+		reg_value = 10876;
+
+	*snr =
+	    10 * 10 * ((int)cxd2880_math_log10(reg_value) -
+		       (int)cxd2880_math_log10(12600 - reg_value));
+	*snr += 32000;
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+					      int *snr)
+{
+	u16 reg_value = 0;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!snr))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	*snr = -1000 * 1000;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+		ret = dvbt2_read_snr_reg(tnr_dmd, &reg_value);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+
+		ret = dvbt2_calc_snr(tnr_dmd, reg_value, snr);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	} else {
+		int snr_main = 0;
+		int snr_sub = 0;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt2_mon_snr_diver(tnr_dmd, snr, &snr_main,
+						       &snr_sub);
+		if (ret != CXD2880_RESULT_OK)
+			return ret;
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_snr_diver(struct cxd2880_tnrdmd
+						    *tnr_dmd, int *snr,
+						    int *snr_main, int *snr_sub)
+{
+	u16 reg_value = 0;
+	u32 reg_value_sum = 0;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!snr) || (!snr_main) || (!snr_sub))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	*snr = -1000 * 1000;
+	*snr_main = -1000 * 1000;
+	*snr_sub = -1000 * 1000;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret = dvbt2_read_snr_reg(tnr_dmd, &reg_value);
+	if (ret == CXD2880_RESULT_OK) {
+		ret = dvbt2_calc_snr(tnr_dmd, reg_value, snr_main);
+		if (ret != CXD2880_RESULT_OK)
+			reg_value = 0;
+	} else if (ret == CXD2880_RESULT_ERROR_HW_STATE) {
+		reg_value = 0;
+	} else {
+		return ret;
+	}
+
+	reg_value_sum += reg_value;
+
+	ret = dvbt2_read_snr_reg(tnr_dmd->diver_sub, &reg_value);
+	if (ret == CXD2880_RESULT_OK) {
+		ret = dvbt2_calc_snr(tnr_dmd->diver_sub, reg_value, snr_sub);
+		if (ret != CXD2880_RESULT_OK)
+			reg_value = 0;
+	} else if (ret == CXD2880_RESULT_ERROR_HW_STATE) {
+		reg_value = 0;
+	} else {
+		return ret;
+	}
+
+	reg_value_sum += reg_value;
+
+	ret = dvbt2_calc_snr(tnr_dmd, reg_value_sum, snr);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_pre_ldpcber(struct cxd2880_tnrdmd
+						      *tnr_dmd, u32 *ber)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	u32 bit_error = 0;
+	u32 period_exp = 0;
+	u32 n_ldpc = 0;
+
+	if ((!tnr_dmd) || (!ber))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0B) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	{
+		u8 data[5];
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x3C, data,
+					   sizeof(data)) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (!(data[0] & 0x01)) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		bit_error =
+		    ((data[1] & 0x0F) << 24) | (data[2] << 16) | (data[3] << 8)
+		    | data[4];
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0xA0, data,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (((enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03)) ==
+		    CXD2880_DVBT2_FEC_LDPC_16K)
+			n_ldpc = 16200;
+		else
+			n_ldpc = 64800;
+
+		slvt_unfreeze_reg(tnr_dmd);
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x20) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x6F, data,
+					   1) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		period_exp = data[0] & 0x0F;
+	}
+
+	if (bit_error > ((1U << period_exp) * n_ldpc))
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	{
+		u32 div = 0;
+		u32 Q = 0;
+		u32 R = 0;
+
+		if (period_exp >= 4) {
+			div = (1U << (period_exp - 4)) * (n_ldpc / 200);
+
+			Q = (bit_error * 5) / div;
+			R = (bit_error * 5) % div;
+
+			R *= 625;
+			Q = Q * 625 + R / div;
+			R = R % div;
+		} else {
+			div = (1U << period_exp) * (n_ldpc / 200);
+
+			Q = (bit_error * 10) / div;
+			R = (bit_error * 10) % div;
+
+			R *= 5000;
+			Q = Q * 5000 + R / div;
+			R = R % div;
+		}
+
+		if (div / 2 <= R)
+			*ber = Q + 1;
+		else
+			*ber = Q;
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_post_bchfer(struct cxd2880_tnrdmd
+						      *tnr_dmd, u32 *fer)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	u32 fec_error = 0;
+	u32 period = 0;
+
+	if ((!tnr_dmd) || (!fer))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0B) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	{
+		u8 data[2];
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x1B, data,
+					   2) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (!(data[0] & 0x80))
+			return CXD2880_RESULT_ERROR_HW_STATE;
+
+		fec_error = ((data[0] & 0x7F) << 8) | (data[1]);
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x20) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x72, data,
+					   1) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		period = (1 << (data[0] & 0x0F));
+	}
+
+	if ((period == 0) || (fec_error > period))
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	{
+		u32 div = 0;
+		u32 Q = 0;
+		u32 R = 0;
+
+		div = period;
+
+		Q = (fec_error * 1000) / div;
+		R = (fec_error * 1000) % div;
+
+		R *= 1000;
+		Q = Q * 1000 + R / div;
+		R = R % div;
+
+		if ((div != 1) && (div / 2 <= R))
+			*fer = Q + 1;
+		else
+			*fer = Q;
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_pre_bchber(struct cxd2880_tnrdmd
+						     *tnr_dmd, u32 *ber)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	u32 bit_error = 0;
+	u32 period_exp = 0;
+	u32 n_bch = 0;
+
+	if ((!tnr_dmd) || (!ber))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 data[3];
+		enum cxd2880_dvbt2_plp_fec plp_fec_type =
+		    CXD2880_DVBT2_FEC_LDPC_16K;
+		enum cxd2880_dvbt2_plp_code_rate plp_cr = CXD2880_DVBT2_R1_2;
+
+		static const u16 n_bch_bits_lookup[2][8] = {
+			{7200, 9720, 10800, 11880, 12600, 13320, 5400, 6480},
+			{32400, 38880, 43200, 48600, 51840, 54000, 21600, 25920}
+		};
+
+		if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0B) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x15, data,
+					   3) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (!(data[0] & 0x40)) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		bit_error = ((data[0] & 0x3F) << 16) | (data[1] << 8) | data[2];
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x9D, data,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		plp_cr = (enum cxd2880_dvbt2_plp_code_rate)(data[0] & 0x07);
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0xA0, data,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		plp_fec_type = (enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03);
+
+		slvt_unfreeze_reg(tnr_dmd);
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x20) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x72, data,
+					   1) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		period_exp = data[0] & 0x0F;
+
+		if ((plp_fec_type > CXD2880_DVBT2_FEC_LDPC_64K) ||
+		    (plp_cr > CXD2880_DVBT2_R2_5))
+			return CXD2880_RESULT_ERROR_HW_STATE;
+
+		n_bch = n_bch_bits_lookup[plp_fec_type][plp_cr];
+	}
+
+	if (bit_error > ((1U << period_exp) * n_bch))
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	{
+		u32 div = 0;
+		u32 Q = 0;
+		u32 R = 0;
+
+		if (period_exp >= 6) {
+			div = (1U << (period_exp - 6)) * (n_bch / 40);
+
+			Q = (bit_error * 625) / div;
+			R = (bit_error * 625) % div;
+
+			R *= 625;
+			Q = Q * 625 + R / div;
+			R = R % div;
+		} else {
+			div = (1U << period_exp) * (n_bch / 40);
+
+			Q = (bit_error * 1000) / div;
+			R = (bit_error * 1000) % div;
+
+			R *= 25000;
+			Q = Q * 25000 + R / div;
+			R = R % div;
+		}
+
+		if (div / 2 <= R)
+			*ber = Q + 1;
+		else
+			*ber = Q;
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_packet_error_number(struct
+							      cxd2880_tnrdmd
+							      *tnr_dmd,
+							      u32 *pen)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	u8 data[3];
+
+	if ((!tnr_dmd) || (!pen))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0B) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x39, data,
+				   sizeof(data)) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (!(data[0] & 0x01))
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	*pen = ((data[1] << 8) | data[2]);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sampling_offset(struct cxd2880_tnrdmd
+							  *tnr_dmd, int *ppm)
+{
+	if ((!tnr_dmd) || (!ppm))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 ctl_val_reg[5];
+		u8 nominal_rate_reg[5];
+		u32 trl_ctl_val = 0;
+		u32 trcg_nominal_rate = 0;
+		int num;
+		int den;
+		enum cxd2880_ret ret = CXD2880_RESULT_OK;
+		u8 sync_state = 0;
+		u8 ts_lock = 0;
+		u8 unlock_detected = 0;
+		s8 diff_upper = 0;
+
+		if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+						       &ts_lock,
+						       &unlock_detected);
+		if (ret != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (sync_state != 6) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0B) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x34,
+					   ctl_val_reg,
+					   sizeof(ctl_val_reg)) !=
+		    CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x04) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x10,
+					   nominal_rate_reg,
+					   sizeof(nominal_rate_reg)) !=
+		    CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		slvt_unfreeze_reg(tnr_dmd);
+
+		diff_upper =
+		    (ctl_val_reg[0] & 0x7F) - (nominal_rate_reg[0] & 0x7F);
+
+		if ((diff_upper < -1) || (diff_upper > 1))
+			return CXD2880_RESULT_ERROR_HW_STATE;
+
+		trl_ctl_val = ctl_val_reg[1] << 24;
+		trl_ctl_val |= ctl_val_reg[2] << 16;
+		trl_ctl_val |= ctl_val_reg[3] << 8;
+		trl_ctl_val |= ctl_val_reg[4];
+
+		trcg_nominal_rate = nominal_rate_reg[1] << 24;
+		trcg_nominal_rate |= nominal_rate_reg[2] << 16;
+		trcg_nominal_rate |= nominal_rate_reg[3] << 8;
+		trcg_nominal_rate |= nominal_rate_reg[4];
+
+		trl_ctl_val >>= 1;
+		trcg_nominal_rate >>= 1;
+
+		if (diff_upper == 1)
+			num =
+			    (int)((trl_ctl_val + 0x80000000u) -
+				  trcg_nominal_rate);
+		else if (diff_upper == -1)
+			num =
+			    -(int)((trcg_nominal_rate + 0x80000000u) -
+				   trl_ctl_val);
+		else
+			num = (int)(trl_ctl_val - trcg_nominal_rate);
+
+		den = (nominal_rate_reg[0] & 0x7F) << 24;
+		den |= nominal_rate_reg[1] << 16;
+		den |= nominal_rate_reg[2] << 8;
+		den |= nominal_rate_reg[3];
+		den = (den + (390625 / 2)) / 390625;
+
+		den >>= 1;
+
+		if (num >= 0)
+			*ppm = (num + (den / 2)) / den;
+		else
+			*ppm = (num - (den / 2)) / den;
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sampling_offset_sub(struct
+							      cxd2880_tnrdmd
+							      *tnr_dmd,
+							      int *ppm)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!ppm))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret = cxd2880_tnrdmd_dvbt2_mon_sampling_offset(tnr_dmd->diver_sub, ppm);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_quality(struct cxd2880_tnrdmd
+						  *tnr_dmd, u8 *quality)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	int snr = 0;
+	int snr_rel = 0;
+	u32 ber = 0;
+	u32 ber_sqi = 0;
+	enum cxd2880_dvbt2_plp_constell qam;
+	enum cxd2880_dvbt2_plp_code_rate code_rate;
+
+	static const int snr_nordig_p1_db_1000[4][8] = {
+		{3500, 4700, 5600, 6600, 7200, 7700, 1300, 2200},
+		{8700, 10100, 11400, 12500, 13300, 13800, 6000, 7200},
+		{13000, 14800, 16200, 17700, 18700, 19400, 9800, 11100},
+		{17000, 19400, 20800, 22900, 24300, 25100, 13200, 14800},
+	};
+
+	if ((!tnr_dmd) || (!quality))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret = cxd2880_tnrdmd_dvbt2_mon_pre_bchber(tnr_dmd, &ber);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret = cxd2880_tnrdmd_dvbt2_mon_snr(tnr_dmd, &snr);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_qam(tnr_dmd, CXD2880_DVBT2_PLP_DATA, &qam);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_code_rate(tnr_dmd, CXD2880_DVBT2_PLP_DATA,
+					       &code_rate);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if ((code_rate > CXD2880_DVBT2_R2_5) || (qam > CXD2880_DVBT2_QAM256))
+		return CXD2880_RESULT_ERROR_OTHER;
+
+	if (ber > 100000)
+		ber_sqi = 0;
+	else if (ber >= 100)
+		ber_sqi = 6667;
+	else
+		ber_sqi = 16667;
+
+	snr_rel = snr - snr_nordig_p1_db_1000[qam][code_rate];
+
+	if (snr_rel < -3000) {
+		*quality = 0;
+	} else if (snr_rel <= 3000) {
+		u32 temp_sqi =
+		    (((snr_rel + 3000) * ber_sqi) + 500000) / 1000000;
+		*quality = (temp_sqi > 100) ? 100 : (u8)temp_sqi;
+	} else {
+		*quality = 100;
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ts_rate(struct cxd2880_tnrdmd
+						  *tnr_dmd, u32 *ts_rate_kbps)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	u32 rd_smooth_dp = 0;
+	u32 ep_ck_nume = 0;
+	u32 ep_ck_deno = 0;
+	u8 issy_on_data = 0;
+
+	if ((!tnr_dmd) || (!ts_rate_kbps))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 data[12];
+		u8 sync_state = 0;
+		u8 ts_lock = 0;
+		u8 unlock_detected = 0;
+
+		if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		ret =
+		    cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+						       &ts_lock,
+						       &unlock_detected);
+		if (ret != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (!ts_lock) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0B) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x23, data,
+					   12) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		rd_smooth_dp = (u32)((data[0] & 0x1F) << 24);
+		rd_smooth_dp |= (u32)(data[1] << 16);
+		rd_smooth_dp |= (u32)(data[2] << 8);
+		rd_smooth_dp |= (u32)data[3];
+
+		if (rd_smooth_dp < 214958) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		ep_ck_nume = (u32)((data[4] & 0x3F) << 24);
+		ep_ck_nume |= (u32)(data[5] << 16);
+		ep_ck_nume |= (u32)(data[6] << 8);
+		ep_ck_nume |= (u32)data[7];
+
+		ep_ck_deno = (u32)((data[8] & 0x3F) << 24);
+		ep_ck_deno |= (u32)(data[9] << 16);
+		ep_ck_deno |= (u32)(data[10] << 8);
+		ep_ck_deno |= (u32)data[11];
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x41, data,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		issy_on_data = data[0] & 0x01;
+
+		slvt_unfreeze_reg(tnr_dmd);
+	}
+
+	if (issy_on_data) {
+		if ((ep_ck_deno == 0) || (ep_ck_nume == 0) ||
+		    (ep_ck_deno >= ep_ck_nume))
+			return CXD2880_RESULT_ERROR_HW_STATE;
+	}
+
+	{
+		u32 ick_x100;
+		u32 div = 0;
+		u32 Q = 0;
+		u32 R = 0;
+
+		switch (tnr_dmd->clk_mode) {
+		case CXD2880_TNRDMD_CLOCKMODE_A:
+			ick_x100 = 8228;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_B:
+			ick_x100 = 9330;
+			break;
+		case CXD2880_TNRDMD_CLOCKMODE_C:
+			ick_x100 = 9600;
+			break;
+		default:
+			return CXD2880_RESULT_ERROR_SW_STATE;
+		}
+
+		div = rd_smooth_dp;
+
+		Q = ick_x100 * 262144U / div;
+		R = ick_x100 * 262144U % div;
+
+		R *= 5U;
+		Q = Q * 5 + R / div;
+		R = R % div;
+
+		R *= 2U;
+		Q = Q * 2 + R / div;
+		R = R % div;
+
+		if (div / 2 <= R)
+			*ts_rate_kbps = Q + 1;
+		else
+			*ts_rate_kbps = Q;
+	}
+
+	if (issy_on_data) {
+		u32 diff = ep_ck_nume - ep_ck_deno;
+
+		while (diff > 0x7FFF) {
+			diff >>= 1;
+			ep_ck_nume >>= 1;
+		}
+
+		*ts_rate_kbps -=
+		    (*ts_rate_kbps * diff + ep_ck_nume / 2) / ep_ck_nume;
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_per(struct cxd2880_tnrdmd *tnr_dmd,
+					      u32 *per)
+{
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+	u32 packet_error = 0;
+	u32 period = 0;
+
+	if (!tnr_dmd || !per)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	{
+		u8 rdata[3];
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x0B) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x18, rdata,
+					   3) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if ((rdata[0] & 0x01) == 0)
+			return CXD2880_RESULT_ERROR_HW_STATE;
+
+		packet_error = (rdata[1] << 8) | rdata[2];
+
+		if (tnr_dmd->io->write_reg(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x00,
+					   0x24) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0xDC, rdata,
+					   1) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		period = 1U << (rdata[0] & 0x0F);
+	}
+
+	if ((period == 0) || (packet_error > period))
+		return CXD2880_RESULT_ERROR_HW_STATE;
+
+	{
+		u32 div = 0;
+		u32 Q = 0;
+		u32 R = 0;
+
+		div = period;
+
+		Q = (packet_error * 1000) / div;
+		R = (packet_error * 1000) % div;
+
+		R *= 1000;
+		Q = Q * 1000 + R / div;
+		R = R % div;
+
+		if ((div != 1) && (div / 2 <= R))
+			*per = Q + 1;
+		else
+			*per = Q;
+	}
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_qam(struct cxd2880_tnrdmd *tnr_dmd,
+					      enum cxd2880_dvbt2_plp_btype type,
+					      enum cxd2880_dvbt2_plp_constell
+					      *qam)
+{
+	u8 data;
+	u8 l1_post_ok = 0;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!qam))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0B) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x86, &l1_post_ok,
+				   1) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (!(l1_post_ok & 0x01)) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_HW_STATE;
+	}
+
+	if (type == CXD2880_DVBT2_PLP_COMMON) {
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0xB6, &data,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (data == 0) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0xB1, &data,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+	} else {
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x9E, &data,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	*qam = (enum cxd2880_dvbt2_plp_constell)(data & 0x07);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_code_rate(struct cxd2880_tnrdmd
+						    *tnr_dmd,
+						    enum cxd2880_dvbt2_plp_btype
+						    type,
+						    enum
+						    cxd2880_dvbt2_plp_code_rate
+						    *code_rate)
+{
+	u8 data;
+	u8 l1_post_ok = 0;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!code_rate))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0B) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (tnr_dmd->io->read_regs(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x86, &l1_post_ok,
+				   1) != CXD2880_RESULT_OK) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_IO;
+	}
+
+	if (!(l1_post_ok & 0x01)) {
+		slvt_unfreeze_reg(tnr_dmd);
+		return CXD2880_RESULT_ERROR_HW_STATE;
+	}
+
+	if (type == CXD2880_DVBT2_PLP_COMMON) {
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0xB6, &data,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+
+		if (data == 0) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_HW_STATE;
+		}
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0xB0, &data,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+	} else {
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x9D, &data,
+					   1) != CXD2880_RESULT_OK) {
+			slvt_unfreeze_reg(tnr_dmd);
+			return CXD2880_RESULT_ERROR_IO;
+		}
+	}
+
+	slvt_unfreeze_reg(tnr_dmd);
+
+	*code_rate = (enum cxd2880_dvbt2_plp_code_rate)(data & 0x07);
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_profile(struct cxd2880_tnrdmd
+						  *tnr_dmd,
+						  enum cxd2880_dvbt2_profile
+						  *profile)
+{
+	if ((!tnr_dmd) || (!profile))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->io->write_reg(tnr_dmd->io,
+				   CXD2880_IO_TGT_DMD, 0x00,
+				   0x0B) != CXD2880_RESULT_OK)
+		return CXD2880_RESULT_ERROR_IO;
+
+	{
+		u8 data;
+
+		if (tnr_dmd->io->read_regs(tnr_dmd->io,
+					   CXD2880_IO_TGT_DMD, 0x22, &data,
+					   sizeof(data)) != CXD2880_RESULT_OK)
+			return CXD2880_RESULT_ERROR_IO;
+
+		if (data & 0x02) {
+			if (data & 0x01)
+				*profile = CXD2880_DVBT2_PROFILE_LITE;
+			else
+				*profile = CXD2880_DVBT2_PROFILE_BASE;
+		} else {
+			enum cxd2880_ret ret = CXD2880_RESULT_ERROR_HW_STATE;
+
+			if (tnr_dmd->diver_mode ==
+			    CXD2880_TNRDMD_DIVERMODE_MAIN)
+				ret =
+				    cxd2880_tnrdmd_dvbt2_mon_profile(
+					tnr_dmd->diver_sub, profile);
+
+			return ret;
+		}
+	}
+
+	return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret dvbt2_calc_sdi(struct cxd2880_tnrdmd *tnr_dmd,
+				       int rf_lvl, u8 *ssi)
+{
+	enum cxd2880_dvbt2_plp_constell qam;
+	enum cxd2880_dvbt2_plp_code_rate code_rate;
+	int prel;
+	int temp_ssi = 0;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	static const int ref_dbm_1000[4][8] = {
+		{-96000, -95000, -94000, -93000, -92000, -92000, -98000,
+		 -97000},
+		{-91000, -89000, -88000, -87000, -86000, -86000, -93000,
+		 -92000},
+		{-86000, -85000, -83000, -82000, -81000, -80000, -89000,
+		 -88000},
+		{-82000, -80000, -78000, -76000, -75000, -74000, -86000,
+		 -84000},
+	};
+
+	if ((!tnr_dmd) || (!ssi))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_qam(tnr_dmd, CXD2880_DVBT2_PLP_DATA, &qam);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret =
+	    cxd2880_tnrdmd_dvbt2_mon_code_rate(tnr_dmd, CXD2880_DVBT2_PLP_DATA,
+					       &code_rate);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	if ((code_rate > CXD2880_DVBT2_R2_5) || (qam > CXD2880_DVBT2_QAM256))
+		return CXD2880_RESULT_ERROR_OTHER;
+
+	prel = rf_lvl - ref_dbm_1000[qam][code_rate];
+
+	if (prel < -15000)
+		temp_ssi = 0;
+	else if (prel < 0)
+		temp_ssi = ((2 * (prel + 15000)) + 1500) / 3000;
+	else if (prel < 20000)
+		temp_ssi = (((4 * prel) + 500) / 1000) + 10;
+	else if (prel < 35000)
+		temp_ssi = (((2 * (prel - 20000)) + 1500) / 3000) + 90;
+	else
+		temp_ssi = 100;
+
+	*ssi = (temp_ssi > 100) ? 100 : (u8)temp_ssi;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+					      u8 *ssi)
+{
+	int rf_lvl = 0;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!ssi))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd, &rf_lvl);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret = dvbt2_calc_sdi(tnr_dmd, rf_lvl, ssi);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ssi_sub(struct cxd2880_tnrdmd
+						  *tnr_dmd, u8 *ssi)
+{
+	int rf_lvl = 0;
+	enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+	if ((!tnr_dmd) || (!ssi))
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+		return CXD2880_RESULT_ERROR_ARG;
+
+	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+		return CXD2880_RESULT_ERROR_SW_STATE;
+
+	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, &rf_lvl);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	ret = dvbt2_calc_sdi(tnr_dmd, rf_lvl, ssi);
+	if (ret != CXD2880_RESULT_OK)
+		return ret;
+
+	return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
new file mode 100644
index 000000000000..784ad2844d1b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
@@ -0,0 +1,170 @@
+/*
+ * cxd2880_tnrdmd_dvbt2_mon.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 monitor interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT2_MON_H
+#define CXD2880_TNRDMD_DVBT2_MON_H
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_dvbt2.h"
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sync_stat(struct cxd2880_tnrdmd
+						    *tnr_dmd, u8 *sync_stat,
+						    u8 *ts_lock_stat,
+						    u8 *unlock_detected);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(struct cxd2880_tnrdmd
+							*tnr_dmd,
+							u8 *sync_stat,
+							u8 *unlock_detected);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_carrier_offset(struct cxd2880_tnrdmd
+							 *tnr_dmd, int *offset);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_carrier_offset_sub(struct
+							     cxd2880_tnrdmd
+							     *tnr_dmd,
+							     int *offset);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_l1_pre(struct cxd2880_tnrdmd *tnr_dmd,
+						 struct cxd2880_dvbt2_l1pre
+						 *l1_pre);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_version(struct cxd2880_tnrdmd
+						  *tnr_dmd,
+						  enum cxd2880_dvbt2_version
+						  *ver);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ofdm(struct cxd2880_tnrdmd *tnr_dmd,
+					       struct cxd2880_dvbt2_ofdm *ofdm);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_data_plps(struct cxd2880_tnrdmd
+						    *tnr_dmd, u8 *plp_ids,
+						    u8 *num_plps);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_active_plp(struct cxd2880_tnrdmd
+						     *tnr_dmd,
+						     enum
+						     cxd2880_dvbt2_plp_btype
+						     type,
+						     struct cxd2880_dvbt2_plp
+						     *plp_info);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_data_plp_error(struct cxd2880_tnrdmd
+							 *tnr_dmd,
+							 u8 *plp_error);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_l1_change(struct cxd2880_tnrdmd
+						    *tnr_dmd, u8 *l1_change);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_l1_post(struct cxd2880_tnrdmd
+						  *tnr_dmd,
+						  struct cxd2880_dvbt2_l1post
+						  *l1_post);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_bbheader(struct cxd2880_tnrdmd
+						   *tnr_dmd,
+						   enum cxd2880_dvbt2_plp_btype
+						   type,
+						   struct cxd2880_dvbt2_bbheader
+						   *bbheader);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_in_bandb_ts_rate(struct cxd2880_tnrdmd
+						   *tnr_dmd,
+						   enum
+						   cxd2880_dvbt2_plp_btype
+						   type,
+						   u32 *ts_rate_bps);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(struct cxd2880_tnrdmd
+						 *tnr_dmd,
+						 enum
+						 cxd2880_tnrdmd_spectrum_sense
+						 *sense);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+					      int *snr);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_snr_diver(struct cxd2880_tnrdmd
+						    *tnr_dmd, int *snr,
+						    int *snr_main,
+						    int *snr_sub);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_pre_ldpcber(struct cxd2880_tnrdmd
+						      *tnr_dmd, u32 *ber);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_post_bchfer(struct cxd2880_tnrdmd
+						      *tnr_dmd, u32 *fer);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_pre_bchber(struct cxd2880_tnrdmd
+						     *tnr_dmd, u32 *ber);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_packet_error_number(struct
+							      cxd2880_tnrdmd
+							      *tnr_dmd,
+							      u32 *pen);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sampling_offset(struct cxd2880_tnrdmd
+							  *tnr_dmd, int *ppm);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sampling_offset_sub(struct
+							      cxd2880_tnrdmd
+							      *tnr_dmd,
+							      int *ppm);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ts_rate(struct cxd2880_tnrdmd
+						  *tnr_dmd, u32 *ts_rate_kbps);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_quality(struct cxd2880_tnrdmd
+						  *tnr_dmd, u8 *quality);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_per(struct cxd2880_tnrdmd *tnr_dmd,
+					      u32 *per);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_qam(struct cxd2880_tnrdmd *tnr_dmd,
+					      enum cxd2880_dvbt2_plp_btype type,
+					      enum cxd2880_dvbt2_plp_constell
+					      *qam);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_code_rate(struct cxd2880_tnrdmd
+						    *tnr_dmd,
+						    enum cxd2880_dvbt2_plp_btype
+						    type,
+						    enum
+						    cxd2880_dvbt2_plp_code_rate
+						    *code_rate);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_profile(struct cxd2880_tnrdmd
+						  *tnr_dmd,
+						  enum cxd2880_dvbt2_profile
+						  *profile);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+					      u8 *ssi);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ssi_sub(struct cxd2880_tnrdmd
+						  *tnr_dmd, u8 *ssi);
+
+#endif
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 13/15]  [media] cxd2880: Add all Makefile files for the driver
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (11 preceding siblings ...)
  2017-04-14  2:40 ` [PATCH v2 12/15] [media] cxd2880: Add DVB-T2 monitor and integration layer functions Yasunari.Takiguchi
@ 2017-04-14  2:44 ` Yasunari.Takiguchi
  2017-04-14  2:47 ` [PATCH v2 14/15] [media] cxd2880: Add all Kconfig " Yasunari.Takiguchi
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  2:44 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

This is the Makefile files of driver
for the Sony CXD2880 DVB-T2/T tuner + demodulator.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
 drivers/media/dvb-frontends/Makefile         |  1 +
 drivers/media/dvb-frontends/cxd2880/Makefile | 21 +++++++++++++++++++++
 drivers/media/spi/Makefile                   |  5 +++++
 3 files changed, 27 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/Makefile

diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index 3fccaf34ef52..d298c7954699 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -126,3 +126,4 @@ obj-$(CONFIG_DVB_HORUS3A) += horus3a.o
 obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o
 obj-$(CONFIG_DVB_HELENE) += helene.o
 obj-$(CONFIG_DVB_ZD1301_DEMOD) += zd1301_demod.o
+obj-$(CONFIG_DVB_CXD2880) += cxd2880/
diff --git a/drivers/media/dvb-frontends/cxd2880/Makefile b/drivers/media/dvb-frontends/cxd2880/Makefile
new file mode 100644
index 000000000000..2672c4a3d65c
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/Makefile
@@ -0,0 +1,21 @@
+cxd2880-objs := cxd2880_common.o \
+		cxd2880_devio_spi.o \
+		cxd2880_integ.o \
+		cxd2880_integ_dvbt2.o \
+		cxd2880_integ_dvbt.o \
+		cxd2880_io.o \
+		cxd2880_spi_device.o \
+		cxd2880_stopwatch_port.o \
+		cxd2880_tnrdmd.o \
+		cxd2880_tnrdmd_dvbt2.o \
+		cxd2880_tnrdmd_dvbt2_mon.o \
+		cxd2880_tnrdmd_dvbt.o \
+		cxd2880_tnrdmd_dvbt_mon.o\
+		cxd2880_tnrdmd_mon.o\
+		cxd2880_math.o \
+		cxd2880_top.o
+
+obj-$(CONFIG_DVB_CXD2880) += cxd2880.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
diff --git a/drivers/media/spi/Makefile b/drivers/media/spi/Makefile
index ea64013d16cc..40e0f88d9f6c 100644
--- a/drivers/media/spi/Makefile
+++ b/drivers/media/spi/Makefile
@@ -1 +1,6 @@
 obj-$(CONFIG_VIDEO_GS1662) += gs1662.o
+obj-$(CONFIG_CXD2880_SPI_DRV) += cxd2880-spi.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/dvb-frontends/cxd2880
\ No newline at end of file
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 14/15] [media] cxd2880: Add all Kconfig files for the driver
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (12 preceding siblings ...)
  2017-04-14  2:44 ` [PATCH v2 13/15] [media] cxd2880: Add all Makefile files for the driver Yasunari.Takiguchi
@ 2017-04-14  2:47 ` Yasunari.Takiguchi
  2017-04-14  2:48 ` [PATCH v2 15/15] [media] cxd2880 : Update MAINTAINERS file for CXD2880 driver Yasunari.Takiguchi
  2017-04-17  5:09 ` [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Takiguchi, Yasunari
  15 siblings, 0 replies; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  2:47 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

This is the Kconfig files of driver for
the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
 drivers/media/dvb-frontends/Kconfig         |  2 ++
 drivers/media/dvb-frontends/cxd2880/Kconfig |  6 ++++++
 drivers/media/spi/Kconfig                   | 14 ++++++++++++++
 3 files changed, 22 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/cxd2880/Kconfig

diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index e8c6554a47aa..3a3a7129a150 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -518,6 +518,8 @@ config DVB_GP8PSK_FE
 	depends on DVB_CORE
 	default DVB_USB_GP8PSK
 
+source "drivers/media/dvb-frontends/cxd2880/Kconfig"
+
 comment "DVB-C (cable) frontends"
 	depends on DVB_CORE
 
diff --git a/drivers/media/dvb-frontends/cxd2880/Kconfig b/drivers/media/dvb-frontends/cxd2880/Kconfig
new file mode 100644
index 000000000000..36b8b6f7c4f7
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/Kconfig
@@ -0,0 +1,6 @@
+config DVB_CXD2880
+	tristate "Sony CXD2880 DVB-T2/T tuner + demodulator"
+	depends on DVB_CORE && SPI
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Say Y when you want to support this frontend.
\ No newline at end of file
diff --git a/drivers/media/spi/Kconfig b/drivers/media/spi/Kconfig
index a21f5a39a440..b07ac86fc53c 100644
--- a/drivers/media/spi/Kconfig
+++ b/drivers/media/spi/Kconfig
@@ -12,3 +12,17 @@ config VIDEO_GS1662
 endmenu
 
 endif
+
+if SPI
+menu "Media SPI Adapters"
+
+config CXD2880_SPI_DRV
+	tristate "Sony CXD2880 SPI support"
+	depends on DVB_CORE && SPI
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  Choose if you would like to have SPI interface support for Sony CXD2880.
+
+endmenu
+
+endif
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 15/15] [media] cxd2880 : Update MAINTAINERS file for CXD2880 driver
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (13 preceding siblings ...)
  2017-04-14  2:47 ` [PATCH v2 14/15] [media] cxd2880: Add all Kconfig " Yasunari.Takiguchi
@ 2017-04-14  2:48 ` Yasunari.Takiguchi
  2017-04-17  5:09 ` [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Takiguchi, Yasunari
  15 siblings, 0 replies; 36+ messages in thread
From: Yasunari.Takiguchi @ 2017-04-14  2:48 UTC (permalink / raw)
  To: akpm, linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
	Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
	Satoshi Watanabe

From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>

This is MAINTAINERS file update about the driver for
the Sony CXD2880 DVB-T2/T tuner + demodulator.

Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
 MAINTAINERS | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index fdd5350fe261..62543a76d6bf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8042,6 +8042,15 @@ T:	git git://linuxtv.org/media_tree.git
 S:	Supported
 F:	drivers/media/dvb-frontends/cxd2841er*
 
+MEDIA DRIVERS FOR CXD2880
+M:	Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
+L:	linux-media@vger.kernel.org
+W:	http://linuxtv.org/
+T:	git git://linuxtv.org/media_tree.git
+S:	Supported
+F:	drivers/media/dvb-frontends/cxd2880/*
+F:	drivers/media/spi/cxd2880*
+
 MEDIA DRIVERS FOR HORUS3A
 M:	Sergey Kozlov <serjk@netup.ru>
 M:	Abylay Ospan <aospan@netup.ru>
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 01/15] [dt-bindings] [media] Add document file for CXD2880 SPI I/F
  2017-04-14  2:00 ` [PATCH v2 01/15] [dt-bindings] [media] Add document file for CXD2880 SPI I/F Yasunari.Takiguchi
@ 2017-04-17  4:58   ` Takiguchi, Yasunari
  0 siblings, 0 replies; 36+ messages in thread
From: Takiguchi, Yasunari @ 2017-04-17  4:58 UTC (permalink / raw)
  To: linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	yasunari.takiguchi

> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> This is the document file for Sony CXD2880 DVB-T2/T tuner + demodulator.
> It contains the description of the SPI adapter binding.
> 
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
No changes since version 1, I should have carried the ack forward:

Acked-by: Rob Herring <robh@kernel.org>


> ---
>  .../devicetree/bindings/media/spi/sony-cxd2880.txt         | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt b/Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt
> new file mode 100644
> index 000000000000..fc5aa263abe5
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt
> @@ -0,0 +1,14 @@
> +Sony CXD2880 DVB-T2/T tuner + demodulator driver SPI adapter
> +
> +Required properties:
> +- compatible: Should be "sony,cxd2880".
> +- reg: SPI chip select number for the device.
> +- spi-max-frequency: Maximum bus speed, should be set to <55000000> (55MHz).
> +
> +Example:
> +
> +cxd2880@0 {
> +	compatible = "sony,cxd2880";
> +	reg = <0>; /* CE0 */
> +	spi-max-frequency = <55000000>; /* 55MHz */
> +};
> 

Takiguchi

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
                   ` (14 preceding siblings ...)
  2017-04-14  2:48 ` [PATCH v2 15/15] [media] cxd2880 : Update MAINTAINERS file for CXD2880 driver Yasunari.Takiguchi
@ 2017-04-17  5:09 ` Takiguchi, Yasunari
  2017-05-25  6:15   ` Takiguchi, Yasunari
  15 siblings, 1 reply; 36+ messages in thread
From: Takiguchi, Yasunari @ 2017-04-17  5:09 UTC (permalink / raw)
  To: akpm, linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	yasunari.takiguchi

On 2017/04/14 10:50, Takiguchi, Yasunari wrote:
> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> Hi,
> 
> This is the patch series (version 2) of Sony CXD2880 DVB-T2/T tuner + demodulator driver.
> The driver supports DVB-API and interfaces through SPI.
> 
> We have tested the driver on Raspberry Pi 3 and got picture and sound from a media player.
> 
> Thanks,
> Takiguchi
> ---
>  Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt        |   14 ++++++++++++++
>  drivers/media/spi/cxd2880-spi.c                                     | 728 ++++++++++++++++++++++++++++++++++++++++
>  drivers/media/dvb-frontends/cxd2880/cxd2880.h                       |   46 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_common.c                |   84 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_common.h                |   86 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_io.c                    |   68 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_io.h                    |   62 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h                |   35 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c        |   71 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_math.c                  |   89 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_math.h                  |   40 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c             |  147 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h             |   40 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h                   |   51 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c            |  130 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h            |   45 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h                   |   50 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c                | 3925 ++++++++++++++++++++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h                |  395 ++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h |   29 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c            |  207 ++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h            |   52 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c                 |   99 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h                 |   44 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_top.c                   | 1550 ++++++++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h                  |   91 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c           | 1072 +++++++++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h           |   62 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c            |  197 ++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h            |   58 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c       | 1190 +++++++++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h       |  106 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h                 |  402 ++++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c          | 1309 ++++++++++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h          |   82 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c           |  311 +++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h           |   64 +
>  drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c      | 2523 ++++++++++++++++++++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h      |  170 ++
>  drivers/media/dvb-frontends/Makefile                                |    1 +
>  drivers/media/dvb-frontends/cxd2880/Makefile                        |   21 +++++++++++++++++++++
>  drivers/media/spi/Makefile                                          |    5 +++++
>  drivers/media/dvb-frontends/Kconfig                                 |    2 ++
>  drivers/media/dvb-frontends/cxd2880/Kconfig                         |    6 ++++++
>  drivers/media/spi/Kconfig                                           |   14 ++++++++++++++
>  MAINTAINERS                                                         |    9 +++++++++
> 
>  46 files changed, 15782 insertions(+)
> 
>  create mode 100644 Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt
>  create mode 100644 drivers/media/spi/cxd2880-spi.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_math.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/Makefile
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/Kconfig

I added change patches information from Version 1 to Version 2.

[Change list]
<V1->V2>
(1)[PATCH 2/5], [PATCH 3/5] and [PATCH 4/5] of version 1 were divided to change order and be small size patch.
    Total patch number was changed from 5 to 15

   <Previous>
   The changed or created files of version 1 [PATCH 2/5], [PATCH 3/5] and [PATCH 4/5]:
  [PATCH 2/5]
      drivers/media/spi/Kconfig
      drivers/media/spi/Makefile
      drivers/media/spi/cxd2880-spi.c
  [PATCH 3/5]
     drivers/media/dvb-frontends/Kconfig
      drivers/media/dvb-frontends/Makefile
      drivers/media/dvb-frontends/cxd2880/Kconfig
      drivers/media/dvb-frontends/cxd2880/Makefile
      drivers/media/dvb-frontends/cxd2880/cxd2880.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_math.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
    [PATCH 4/5]
      drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h

   <New>
   The changed or created files of version 2 from [PATCH v2 02/15] to [PATCH v2 14/15]:
    [PATCH v2 02/15]
      drivers/media/spi/cxd2880-spi.c
    [PATCH v2 03/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
    [PATCH v2 04/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_math.h
    [PATCH v2 05/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
    [PATCH v2 06/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
    [PATCH v2 07/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
    [PATCH v2 08/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
    [PATCH v2 09/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
    [PATCH v2 10/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
    [PATCH v2 11/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
    [PATCH v2 12/15]
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
      drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
    [PATCH v2 13/15]
      drivers/media/dvb-frontends/Makefile
      drivers/media/dvb-frontends/cxd2880/Makefile
      drivers/media/spi/Makefile
    [PATCH v2 14/15]
      drivers/media/dvb-frontends/Kconfig
      drivers/media/dvb-frontends/cxd2880/Kconfig
      drivers/media/spi/Kconfig

(2)Modified PID filter setting.
    drivers/media/spi/cxd2880-spi.c in [PATCH v2 02/15]

(3)Driver version up
    drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h in [PATCH v2 06/15]

Thanks,
Takiguchi

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-04-17  5:09 ` [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Takiguchi, Yasunari
@ 2017-05-25  6:15   ` Takiguchi, Yasunari
  2017-06-01  1:41     ` Takiguchi, Yasunari
  2017-06-13 14:38     ` Mauro Carvalho Chehab
  0 siblings, 2 replies; 36+ messages in thread
From: Takiguchi, Yasunari @ 2017-05-25  6:15 UTC (permalink / raw)
  To: akpm, linux-kernel, devicetree, linux-media
  Cc: tbird20d, frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	yasunari.takiguchi

Hi, all

I sent the patch series of Sony CXD2880 DVB-T2/T tuner + demodulator driver on Apr/14.
Are there any comments, advices and review results for them?

I'd like to get better understanding of current review status for our codes.

Regards,
Takiguchi

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-05-25  6:15   ` Takiguchi, Yasunari
@ 2017-06-01  1:41     ` Takiguchi, Yasunari
  2017-06-12 13:33       ` Abylay Ospan
  2017-06-13 14:38     ` Mauro Carvalho Chehab
  1 sibling, 1 reply; 36+ messages in thread
From: Takiguchi, Yasunari @ 2017-06-01  1:41 UTC (permalink / raw)
  To: akpm, linux-kernel, devicetree, linux-media, mchehab
  Cc: tbird20d, frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	yasunari.takiguchi

Hi, all

I sent the patch series of Sony CXD2880 DVB-T2/T tuner + demodulator driver on Apr/14.
Are there any comments, advices and review results for them?

I'd like to get better understanding of current review status for our codes.

Regards,
Takiguchi

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-06-01  1:41     ` Takiguchi, Yasunari
@ 2017-06-12 13:33       ` Abylay Ospan
  2017-06-13  5:35         ` Takiguchi, Yasunari
  0 siblings, 1 reply; 36+ messages in thread
From: Abylay Ospan @ 2017-06-12 13:33 UTC (permalink / raw)
  To: Takiguchi, Yasunari
  Cc: akpm, linux-kernel, devicetree, linux-media,
	Mauro Carvalho Chehab, tbird20d, frowand.list, Yamamoto,
	Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS)

Dear Takiguchi,

I'm working on 'drivers/media/dvb-frontends/cxd2841er.c' (universal
demod) and 'linux/drivers/media/dvb-frontends/helene.c' (universal
TERR/CABLE/SAT tuner).

How do you think - is your cxd2880 proposed driver have possibilities
to cover cxd2841er demod functionality too ?

I have quickly checked your cxd2880_top.c and looks like cxd2880 works
over SPI (not I2C) ?Also, looks like registers map, sequences
is different. Am I right ?

How do you think ?

Thanks a lot !

2017-05-31 21:41 GMT-04:00 Takiguchi, Yasunari <Yasunari.Takiguchi@sony.com>:
> Hi, all
>
> I sent the patch series of Sony CXD2880 DVB-T2/T tuner + demodulator driver on Apr/14.
> Are there any comments, advices and review results for them?
>
> I'd like to get better understanding of current review status for our codes.
>
> Regards,
> Takiguchi



-- 
Abylay Ospan,
NetUP Inc.
http://www.netup.tv

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-06-12 13:33       ` Abylay Ospan
@ 2017-06-13  5:35         ` Takiguchi, Yasunari
  2017-06-13  5:39           ` Abylay Ospan
  0 siblings, 1 reply; 36+ messages in thread
From: Takiguchi, Yasunari @ 2017-06-13  5:35 UTC (permalink / raw)
  To: Abylay Ospan
  Cc: akpm, linux-kernel, devicetree, linux-media,
	Mauro Carvalho Chehab, tbird20d, frowand.list, Yamamoto,
	Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	yasunari.takiguchi

Dear Abylay Ospan

Thank you for your review and proposal.

Unfortunately, we supposed it's difficult to cover CXD2841 functionality by CXD2880 driver.
CXD2880 is for mobile IC, tuner (RF) and demodulator convined IC.
On the other hand, CXD2841 is demodulator only IC for stationary use.
CXD2841 supports terrestrial, cable and satellite broadcasting systems.
But CXD2880 supports terrestrial broadcasting systems only.
And as you suggested, the driver supports SPI interface only.

Regards & Thanks a lot
Takiguchi


On 2017/06/12 22:33, Abylay Ospan wrote:
> Dear Takiguchi,
> 
> I'm working on 'drivers/media/dvb-frontends/cxd2841er.c' (universal
> demod) and 'linux/drivers/media/dvb-frontends/helene.c' (universal
> TERR/CABLE/SAT tuner).
> 
> How do you think - is your cxd2880 proposed driver have possibilities
> to cover cxd2841er demod functionality too ?
> 
> I have quickly checked your cxd2880_top.c and looks like cxd2880 works
> over SPI (not I2C) ?Also, looks like registers map, sequences
> is different. Am I right ?
> 
> How do you think ?
> 
> Thanks a lot !

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-06-13  5:35         ` Takiguchi, Yasunari
@ 2017-06-13  5:39           ` Abylay Ospan
  0 siblings, 0 replies; 36+ messages in thread
From: Abylay Ospan @ 2017-06-13  5:39 UTC (permalink / raw)
  To: Takiguchi, Yasunari
  Cc: akpm, linux-kernel, devicetree, linux-media,
	Mauro Carvalho Chehab, tbird20d, frowand.list, Yamamoto,
	Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS)

Dear Takiguchi,

Roger that.
Thanks for explanation !

2017-06-13 1:35 GMT-04:00 Takiguchi, Yasunari <Yasunari.Takiguchi@sony.com>:
> Dear Abylay Ospan
>
> Thank you for your review and proposal.
>
> Unfortunately, we supposed it's difficult to cover CXD2841 functionality by CXD2880 driver.
> CXD2880 is for mobile IC, tuner (RF) and demodulator convined IC.
> On the other hand, CXD2841 is demodulator only IC for stationary use.
> CXD2841 supports terrestrial, cable and satellite broadcasting systems.
> But CXD2880 supports terrestrial broadcasting systems only.
> And as you suggested, the driver supports SPI interface only.
>
> Regards & Thanks a lot
> Takiguchi
>
>
> On 2017/06/12 22:33, Abylay Ospan wrote:
>> Dear Takiguchi,
>>
>> I'm working on 'drivers/media/dvb-frontends/cxd2841er.c' (universal
>> demod) and 'linux/drivers/media/dvb-frontends/helene.c' (universal
>> TERR/CABLE/SAT tuner).
>>
>> How do you think - is your cxd2880 proposed driver have possibilities
>> to cover cxd2841er demod functionality too ?
>>
>> I have quickly checked your cxd2880_top.c and looks like cxd2880 works
>> over SPI (not I2C) ?Also, looks like registers map, sequences
>> is different. Am I right ?
>>
>> How do you think ?
>>
>> Thanks a lot !
>



-- 
Abylay Ospan,
NetUP Inc.
http://www.netup.tv

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 02/15] [media] cxd2880-spi: Add support for CXD2008 SPI interface
  2017-04-14  2:08 ` [PATCH v2 02/15] [media] cxd2880-spi: Add support for CXD2008 SPI interface Yasunari.Takiguchi
@ 2017-06-13 11:55   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 36+ messages in thread
From: Mauro Carvalho Chehab @ 2017-06-13 11:55 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
	Toshihiko Matsumoto, Satoshi Watanabe

Em Fri, 14 Apr 2017 11:08:23 +0900
<Yasunari.Takiguchi@sony.com> escreveu:

> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> This is the SPI adapter part of the driver for the
> Sony CXD2880 DVB-T2/T tuner + demodulator.
> 
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
> ---
>  drivers/media/spi/cxd2880-spi.c | 728 ++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 728 insertions(+)
>  create mode 100644 drivers/media/spi/cxd2880-spi.c
> 
> diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c
> new file mode 100644
> index 000000000000..82e122349055
> --- /dev/null
> +++ b/drivers/media/spi/cxd2880-spi.c
> @@ -0,0 +1,728 @@
> +/*
> + * cxd2880-spi.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * SPI adapter
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/spi/spi.h>
> +
> +#include "dvb_demux.h"
> +#include "dmxdev.h"
> +#include "dvb_frontend.h"
> +#include "cxd2880.h"
> +
> +#define CXD2880_MAX_FILTER_SIZE 32
> +#define BURST_WRITE_MAX 128
> +#define MAX_TRANS_PACKET 300
> +
> +struct cxd2880_ts_buf_info {
> +	u8 read_ready;
> +	u8 almost_full;
> +	u8 almost_empty;
> +	u8 overflow;
> +	u8 underflow;
> +	u16 packet_num;
> +};
> +
> +struct cxd2880_pid_config {
> +	u8 is_enable;
> +	u16 pid;
> +};
> +
> +struct cxd2880_pid_filter_config {
> +	u8 is_negative;
> +	struct cxd2880_pid_config pid_config[CXD2880_MAX_FILTER_SIZE];
> +};
> +
> +struct cxd2880_dvb_spi {
> +	struct dvb_frontend dvb_fe;
> +	struct dvb_adapter adapter;
> +	struct dvb_demux demux;
> +	struct dmxdev dmxdev;
> +	struct dmx_frontend dmx_fe;
> +	struct task_struct *cxd2880_ts_read_thread;
> +	struct spi_device *spi;
> +	struct mutex spi_mutex; /* For SPI access exclusive control */
> +	int feed_count;
> +	int all_pid_feed_count;
> +	u8 *ts_buf;
> +	struct cxd2880_pid_filter_config filter_config;
> +};
> +
> +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
> +
> +static int cxd2880_write_spi(struct spi_device *spi, u8 *data, u32 size)
> +{
> +	struct spi_message msg;
> +	struct spi_transfer tx;
> +	int ret = 0;
> +
> +	if ((!spi) || (!data)) {
> +		pr_err("%s: invalid arg\n", __func__);

The best would be to se dev_err() & friends for printing messages, as
they print the device's name as filled at struct device. If you don't use, 
please add a define that will print the name at the logs, like:

	#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

either at the begining of the driver or at some header file.

Btw, I'm noticing that you're also using dev_err() on other places
of the code. Please standardize. OK, on a few places, you may still
need to use pr_err(), if you need to print a message before
initializing struct device, but I suspect that you can initialize
it before reading/writing to SPI.

> +		return -EINVAL;
> +	}
> +
> +	memset(&tx, 0, sizeof(tx));
> +	tx.tx_buf = data;
> +	tx.len = size;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&tx, &msg);
> +	ret = spi_sync(spi, &msg);
> +
> +	return ret;
> +}
> +
> +static int cxd2880_write_reg(struct spi_device *spi,
> +				u8 subAddress, const u8 *data, u32 size)
> +{
> +	u8 send_data[BURST_WRITE_MAX + 4];
> +	const u8 *write_data_top = NULL;
> +	int ret = 0;
> +
> +	if ((!spi) || (!data)) {
> +		pr_err("%s: invalid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +	if (size > BURST_WRITE_MAX) {
> +		pr_err("%s: data size > WRITE_MAX\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (subAddress + size > 0x100) {
> +		pr_err("%s: out of range\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	send_data[0] = 0x0E;
> +	write_data_top = data;
> +
> +	while (size > 0) {
> +		send_data[1] = subAddress;
> +		if (size > 255)
> +			send_data[2] = 255;
> +		else
> +			send_data[2] = (u8)size;
> +
> +		memcpy(&send_data[3], write_data_top, send_data[2]);
> +
> +		ret = cxd2880_write_spi(spi, send_data, send_data[2] + 3);
> +		if (ret) {
> +			dev_err(&spi->dev, "%s: write spi failed %d\n",
> +				__func__, ret);
> +			break;
> +		}
> +		subAddress += send_data[2];
> +		write_data_top += send_data[2];
> +		size -= send_data[2];
> +	}
> +
> +	return ret;
> +}
> +
> +static int cxd2880_spi_read_ts(struct spi_device *spi,
> +					u8 *read_data,
> +					u32 packet_num)
> +{
> +	int ret = 0;
> +	u8 data[3];
> +	struct spi_message message;
> +	struct spi_transfer transfer[2];
> +
> +	if ((!spi) || (!read_data) || (!packet_num)) {
> +		pr_err("%s: invalid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +	if (packet_num > 0xFFFF) {
> +		dev_err(&spi->dev, "%s: packet num > 0xFFFF\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	data[0] = 0x10;
> +	data[1] = (u8)((packet_num >> 8) & 0xFF);
> +	data[2] = (u8)(packet_num & 0xFF);

Don't need to cast to (u8). We only use casts when needed at the Kernel
coding style.

> +
> +	spi_message_init(&message);
> +	memset(transfer, 0, sizeof(transfer));
> +
> +	transfer[0].len = 3;
> +	transfer[0].tx_buf = data;
> +	spi_message_add_tail(&transfer[0], &message);
> +	transfer[1].len = packet_num * 188;
> +	transfer[1].rx_buf = read_data;
> +	spi_message_add_tail(&transfer[1], &message);
> +
> +	ret = spi_sync(spi, &message);
> +	if (ret)
> +		dev_err(&spi->dev, "%s: spi_write_then_read failed\n",
> +			__func__);
> +
> +	return ret;
> +}
> +
> +static int cxd2880_spi_read_ts_buffer_info(struct spi_device *spi,
> +					struct cxd2880_ts_buf_info *info)
> +{
> +	u8 send_data = 0x20;
> +	u8 recv_data[2];
> +	int ret = 0;
> +
> +	if ((!spi) || (!info)) {
> +		pr_err("%s: invalid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ret = spi_write_then_read(spi, &send_data, 1,
> +			recv_data, sizeof(recv_data));
> +	if (ret)
> +		dev_err(&spi->dev,
> +			"%s: spi_write_then_read failed\n", __func__);
> +
> +	info->read_ready = (u8)((recv_data[0] & 0x80) ? 1 : 0);
> +	info->almost_full = (u8)((recv_data[0] & 0x40) ? 1 : 0);
> +	info->almost_empty = (u8)((recv_data[0] & 0x20) ? 1 : 0);
> +	info->overflow = (u8)((recv_data[0] & 0x10) ? 1 : 0);
> +	info->underflow = (u8)((recv_data[0] & 0x08) ? 1 : 0);
> +	info->packet_num = (u16)(((recv_data[0] & 0x07) << 8) | recv_data[1]);

Again, no need to cast.

> +
> +	return ret;
> +}
> +
> +static int cxd2880_spi_clear_ts_buffer(struct spi_device *spi)
> +{
> +	u8 data = 0x03;
> +	int ret = 0;
> +
> +	ret = cxd2880_write_spi(spi, &data, 1);
> +
> +	if (ret)
> +		pr_err("%s: write spi failed\n", __func__);
> +
> +	return ret;
> +}
> +
> +static int cxd2880_set_pid_filter(struct spi_device *spi,
> +				struct cxd2880_pid_filter_config *cfg)
> +{
> +	u8 data[65];
> +
> +	if (!spi) {
> +		pr_err("%s: ivnalid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	data[0] = 0x00;
> +	if (cxd2880_write_reg(spi, 0x00, &data[0], 1) != 0)
> +		return -EIO;
> +	if (!cfg) {
> +		data[0] = 0x02;
> +		if (cxd2880_write_reg(spi, 0x50, &data[0], 1) != 0)
> +			return -EIO;
> +	} else {
> +		data[0] = (u8)(cfg->is_negative ? 0x01 : 0x00);
> +		{
> +			int i = 0;
> +			u16 pid = 0;
> +
> +			for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
> +				pid = cfg->pid_config[i].pid;
> +				if (cfg->pid_config[i].is_enable) {
> +					data[1 + (i * 2)] =
> +					    (u8)((u8)(pid >> 8) | 0x20);
> +					data[2 + (i * 2)] =
> +					    (u8)(pid & 0xFF);
> +				} else {
> +					data[1 + (i * 2)] = 0x00;
> +					data[2 + (i * 2)] = 0x00;
> +				}
> +			}
> +		}
> +		if (cxd2880_write_reg(spi, 0x50, data, 65) != 0)
> +			return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +static int cxd2880_update_pid_filter(struct cxd2880_dvb_spi *dvb_spi,
> +				struct cxd2880_pid_filter_config *cfg,
> +				bool is_all_pid_filter)
> +{
> +	int ret = 0;
> +
> +	if ((!dvb_spi) || (!cfg)) {
> +		pr_err("%s: invalid arg.\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&dvb_spi->spi_mutex);
> +	if (is_all_pid_filter) {
> +		struct cxd2880_pid_filter_config tmpcfg;
> +
> +		memset(&tmpcfg, 0, sizeof(tmpcfg));
> +		tmpcfg.is_negative = 1;
> +		tmpcfg.pid_config[0].is_enable = 1;
> +		tmpcfg.pid_config[0].pid = 0x1FFF;
> +
> +		ret = cxd2880_set_pid_filter(dvb_spi->spi, &tmpcfg);
> +	} else {
> +		ret = cxd2880_set_pid_filter(dvb_spi->spi, cfg);
> +	}
> +	mutex_unlock(&dvb_spi->spi_mutex);
> +
> +	if (ret) {
> +		dev_err(&dvb_spi->spi->dev,
> +			"%s: set_pid_filter failed\n", __func__);
> +	}
> +
> +	return ret;
> +}
> +
> +static int cxd2880_ts_read(void *arg)
> +{
> +	struct cxd2880_dvb_spi *dvb_spi = NULL;
> +	struct cxd2880_ts_buf_info info;
> +	struct timespec ts;
> +	long elapsed = 0;
> +	long starttime = 0;
> +	u32 i;
> +	int ret;
> +
> +	dvb_spi = (struct cxd2880_dvb_spi *)arg;
> +	if (!dvb_spi) {
> +		pr_err("%s: invalid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ret = cxd2880_spi_clear_ts_buffer(dvb_spi->spi);
> +	if (ret) {
> +		dev_err(&dvb_spi->spi->dev,
> +			"%s: set_clear_ts_buffer failed\n", __func__);
> +		return ret;
> +	}
> +
> +	getnstimeofday(&getnstimeofdayts);
> +	starttime = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);

It is usually a very bad idea to use gettimeofday(), as this returns
a non-monotonic timestamp - e. g. if the wall clock is adjusted, it
can affect it. Also, it is slow. I suspect that the same is true for
getnstimeofday().

Anyway, as all you want to do here is to implement a timeout, you should use
jiffies instead, and macros to compare time with jiffies, e. g. something like
(from em28xx driver):

	unsigned long timeout = jiffies + msecs_to_jiffies(EM28XX_I2C_XFER_TIMEOUT);

	/* wait for completion */
	while (time_is_after_jiffies(timeout)) {
		ret = dev->em28xx_read_reg(dev, 0x05);
		if (ret == 0x80 + len - 1)
			return len;
		if (ret == 0x94 + len - 1) {
			dprintk(1, "R05 returned 0x%02x: I2C ACK error\n", ret);
			return -ENXIO;
		}
		if (ret < 0) {
			dev_warn(&dev->intf->dev,
				 "failed to get i2c transfer status from bridge register (error=%i)\n",
				ret);
			return ret;
		}
		msleep(5);
	}

> +	while (!kthread_should_stop()) {
> +		getnstimeofday(&ts);
> +		elapsed =
> +			((ts.tv_sec * 1000) + (ts.tv_nsec / 1000000))
> +			- starttime;
> +		ret = cxd2880_spi_read_ts_buffer_info(dvb_spi->spi,
> +							&info);
> +		if (ret) {
> +			pr_err("%s: spi_read_ts_buffer_info error\n",
> +				__func__);
> +			return ret;
> +		}
> +
> +		if (info.packet_num > MAX_TRANS_PACKET) {
> +			for (i = 0; i < info.packet_num / MAX_TRANS_PACKET;
> +				i++) {
> +				cxd2880_spi_read_ts(dvb_spi->spi,
> +							dvb_spi->ts_buf,
> +							MAX_TRANS_PACKET);
> +				dvb_dmx_swfilter(&dvb_spi->demux,
> +						dvb_spi->ts_buf,
> +						MAX_TRANS_PACKET * 188);
> +			}
> +			starttime = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
> +		} else if ((info.packet_num > 0) && (elapsed >= 500)) {
> +			cxd2880_spi_read_ts(dvb_spi->spi,
> +						dvb_spi->ts_buf,
> +						info.packet_num);
> +			dvb_dmx_swfilter(&dvb_spi->demux,
> +					dvb_spi->ts_buf,
> +					info.packet_num * 188);
> +			starttime = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
> +		} else {
> +			usleep_range(10000, 11000);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int cxd2880_start_feed(struct dvb_demux_feed *feed)
> +{
> +	int ret = 0;
> +	int i = 0;
> +	struct dvb_demux *demux = NULL;
> +	struct cxd2880_dvb_spi *dvb_spi = NULL;
> +
> +	if (!feed) {
> +		pr_err("%s: invalid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	demux = feed->demux;
> +	if (!demux) {
> +		pr_err("%s: feed->demux is NULL\n", __func__);
> +		return -EINVAL;
> +	}
> +	dvb_spi = (struct cxd2880_dvb_spi *)demux->priv;
> +
> +	if (dvb_spi->feed_count == CXD2880_MAX_FILTER_SIZE) {
> +		dev_err(&dvb_spi->spi->dev,
> +			"%s: Exceeded maximum PID count (32).", __func__);
> +		dev_err(&dvb_spi->spi->dev,
> +			"Selected PID cannot be enabled.\n");
> +		return -EBUSY;
> +	}
> +
> +	if (feed->pid == 0x2000) {
> +		if (dvb_spi->all_pid_feed_count == 0) {
> +			ret = cxd2880_update_pid_filter(dvb_spi,
> +							&dvb_spi->filter_config,
> +							true);
> +			if (ret) {
> +				dev_err(&dvb_spi->spi->dev,
> +					"%s: update pid filter failed\n",
> +					__func__);
> +				return ret;
> +			}
> +		}
> +		dvb_spi->all_pid_feed_count++;
> +
> +		dev_dbg(&dvb_spi->spi->dev,
> +			"%s: all PID feed (count = %d)\n",
> +			__func__, dvb_spi->all_pid_feed_count);
> +	} else {
> +		struct cxd2880_pid_filter_config cfgtmp;
> +
> +		cfgtmp = dvb_spi->filter_config;
> +
> +		for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
> +			if (cfgtmp.pid_config[i].is_enable == 0) {
> +				cfgtmp.pid_config[i].is_enable = 1;
> +				cfgtmp.pid_config[i].pid = feed->pid;
> +				dev_dbg(&dvb_spi->spi->dev,
> +				"%s: store PID %d to #%d\n",
> +				__func__, feed->pid, i);
> +				break;
> +			}
> +		}
> +		if (i == CXD2880_MAX_FILTER_SIZE) {
> +			dev_err(&dvb_spi->spi->dev,
> +				"%s: PID filter is full. Assumed bug.\n",
> +				__func__);
> +			return -EBUSY;
> +		}
> +		if (!dvb_spi->all_pid_feed_count)
> +			ret = cxd2880_update_pid_filter(dvb_spi,
> +							&cfgtmp,
> +							false);
> +		if (ret)
> +			return ret;
> +
> +		dvb_spi->filter_config = cfgtmp;
> +	}
> +
> +	if (dvb_spi->feed_count == 0) {
> +		dvb_spi->ts_buf =
> +			kmalloc(sizeof(u8) * MAX_TRANS_PACKET * 188,
> +				GFP_KERNEL | GFP_DMA);

nitpick: no need for sizeof(u8)

> +		if (!dvb_spi->ts_buf) {
> +			dev_err(&dvb_spi->spi->dev,
> +			"%s: ts buffer allocate failed\n", __func__);
> +			memset(&dvb_spi->filter_config, 0,
> +				sizeof(dvb_spi->filter_config));
> +			dvb_spi->all_pid_feed_count = 0;
> +			return -ENOMEM;
> +		}
> +		dvb_spi->cxd2880_ts_read_thread = kthread_run(cxd2880_ts_read,
> +								dvb_spi,
> +								"cxd2880_ts_read");

nitpick: please adjust the alignment here (and on similar places) to
match the open parenthesis, e. g.: 

		dvb_spi->cxd2880_ts_read_thread = kthread_run(cxd2880_ts_read,
							      dvb_spi,
							      "cxd2880_ts_read");


> +		if (IS_ERR(dvb_spi->cxd2880_ts_read_thread)) {
> +			dev_err(&dvb_spi->spi->dev,
> +				"%s: kthread_run failed/\n",
> +				__func__);
> +			kfree(dvb_spi->ts_buf);
> +			dvb_spi->ts_buf = NULL;
> +			memset(&dvb_spi->filter_config, 0,
> +				sizeof(dvb_spi->filter_config));
> +			dvb_spi->all_pid_feed_count = 0;
> +			return PTR_ERR(dvb_spi->cxd2880_ts_read_thread);
> +		}
> +	}
> +
> +	dvb_spi->feed_count++;
> +
> +	dev_dbg(&dvb_spi->spi->dev, "%s: start feed (count %d)\n",
> +		__func__, dvb_spi->feed_count);
> +	return 0;
> +}
> +
> +static int cxd2880_stop_feed(struct dvb_demux_feed *feed)
> +{
> +	int i = 0;
> +	int ret = 0;
> +	struct dvb_demux *demux = NULL;
> +	struct cxd2880_dvb_spi *dvb_spi = NULL;
> +
> +	if (!feed) {
> +		pr_err("%s: invalid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	demux = feed->demux;
> +	if (!demux) {
> +		pr_err("%s: feed->demux is NULL\n", __func__);
> +		return -EINVAL;
> +	}
> +	dvb_spi = (struct cxd2880_dvb_spi *)demux->priv;
> +
> +	if (!dvb_spi->feed_count) {
> +		dev_warn(&dvb_spi->spi->dev,
> +			"%s: no feed is started\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (feed->pid == 0x2000) {
> +		/*
> +		 * Special PID case.
> +		 * Number of 0x2000 feed request was stored
> +		 * in dvb_spi->all_pid_feed_count.
> +		 */
> +		if (dvb_spi->all_pid_feed_count <= 0) {
> +			dev_warn(&dvb_spi->spi->dev,
> +				"%s: PID %d not found.\n",
> +				__func__, feed->pid);
> +			return -EINVAL;
> +		}
> +		dvb_spi->all_pid_feed_count--;
> +	} else {
> +		struct cxd2880_pid_filter_config cfgtmp;
> +
> +		cfgtmp = dvb_spi->filter_config;
> +
> +		for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
> +			if (feed->pid == cfgtmp.pid_config[i].pid &&
> +				cfgtmp.pid_config[i].is_enable != 0) {
> +				cfgtmp.pid_config[i].is_enable = 0;
> +				cfgtmp.pid_config[i].pid = 0;
> +				dev_dbg(&dvb_spi->spi->dev,
> +					"%s: removed PID %d from #%d\n",
> +					__func__, feed->pid, i);
> +				break;
> +			}
> +		}
> +		dvb_spi->filter_config = cfgtmp;
> +
> +		if (i == CXD2880_MAX_FILTER_SIZE) {
> +			dev_warn(&dvb_spi->spi->dev, "%s: PID %d not found\n",
> +				__func__, feed->pid);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	ret = cxd2880_update_pid_filter(dvb_spi,
> +					&dvb_spi->filter_config,
> +					dvb_spi->all_pid_feed_count > 0);
> +	dvb_spi->feed_count--;
> +
> +	if (dvb_spi->feed_count == 0) {
> +		int ret_stop = 0;
> +
> +		ret_stop = kthread_stop(dvb_spi->cxd2880_ts_read_thread);
> +		if (ret_stop) {
> +			dev_err(&dvb_spi->spi->dev,
> +			"%s: cxd2880_ts_read thread didn't terminate normally\n",
> +			__func__);
> +			ret = ret_stop;
> +		}
> +		kfree(dvb_spi->ts_buf);
> +		dvb_spi->ts_buf = NULL;
> +	}
> +
> +	dev_dbg(&dvb_spi->spi->dev, "%s: stop feed ok.(count %d)\n",
> +		__func__, dvb_spi->feed_count);
> +
> +	return ret;
> +}
> +
> +static const struct of_device_id cxd2880_spi_of_match[] = {
> +	{ .compatible = "sony,cxd2880" },
> +	{ /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, cxd2880_spi_of_match);
> +
> +static int
> +cxd2880_spi_probe(struct spi_device *spi)
> +{
> +	int ret = 0;
> +	struct cxd2880_dvb_spi *dvb_spi = NULL;
> +	struct cxd2880_config config;
> +
> +	if (!spi) {
> +		pr_err("%s: invalid arg.\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	dvb_spi = kzalloc(sizeof(struct cxd2880_dvb_spi), GFP_KERNEL);
> +	if (!dvb_spi)
> +		return -ENOMEM;
> +
> +	dvb_spi->spi = spi;
> +	mutex_init(&dvb_spi->spi_mutex);
> +	dev_set_drvdata(&spi->dev, dvb_spi);
> +	config.spi = spi;
> +	config.spi_mutex = &dvb_spi->spi_mutex;
> +
> +	ret = dvb_register_adapter(&dvb_spi->adapter,
> +					"CXD2880",
> +					THIS_MODULE,
> +					&spi->dev,
> +					adapter_nr);
> +	if (ret < 0) {
> +		dev_err(&spi->dev, "%s: dvb_register_adapter() failed\n",
> +			__func__);
> +		goto fail_adapter;
> +	}
> +
> +	if (!dvb_attach(cxd2880_attach, &dvb_spi->dvb_fe, &config)) {
> +		dev_err(&spi->dev, "%s: cxd2880_attach failed\n", __func__);
> +		goto fail_attach;
> +	}
> +
> +	ret = dvb_register_frontend(&dvb_spi->adapter,
> +					&dvb_spi->dvb_fe);
> +	if (ret < 0) {
> +		dev_err(&spi->dev, "%s: dvb_register_frontend() failed\n",
> +			__func__);
> +		goto fail_frontend;
> +	}
> +
> +	dvb_spi->demux.dmx.capabilities = DMX_TS_FILTERING;
> +	dvb_spi->demux.priv = dvb_spi;
> +	dvb_spi->demux.filternum = CXD2880_MAX_FILTER_SIZE;
> +	dvb_spi->demux.feednum = CXD2880_MAX_FILTER_SIZE;
> +	dvb_spi->demux.start_feed = cxd2880_start_feed;
> +	dvb_spi->demux.stop_feed = cxd2880_stop_feed;
> +
> +	ret = dvb_dmx_init(&dvb_spi->demux);
> +	if (ret < 0) {
> +		dev_err(&spi->dev, "%s: dvb_dmx_init() failed\n", __func__);
> +		goto fail_dmx;
> +	}
> +
> +	dvb_spi->dmxdev.filternum = CXD2880_MAX_FILTER_SIZE;
> +	dvb_spi->dmxdev.demux = &dvb_spi->demux.dmx;
> +	dvb_spi->dmxdev.capabilities = 0;
> +	ret = dvb_dmxdev_init(&dvb_spi->dmxdev,
> +				&dvb_spi->adapter);
> +	if (ret < 0) {
> +		dev_err(&spi->dev, "%s: dvb_dmxdev_init() failed\n", __func__);
> +		goto fail_dmxdev;
> +	}
> +
> +	dvb_spi->dmx_fe.source = DMX_FRONTEND_0;
> +	ret = dvb_spi->demux.dmx.add_frontend(&dvb_spi->demux.dmx,
> +						&dvb_spi->dmx_fe);
> +	if (ret < 0) {
> +		dev_err(&spi->dev, "%s: add_frontend() failed\n", __func__);
> +		goto fail_dmx_fe;
> +	}
> +
> +	ret = dvb_spi->demux.dmx.connect_frontend(&dvb_spi->demux.dmx,
> +						&dvb_spi->dmx_fe);
> +	if (ret < 0) {
> +		dev_err(&spi->dev, "%s: dvb_register_frontend() failed\n",
> +			__func__);
> +		goto fail_fe_conn;
> +	}
> +
> +	dev_info(&spi->dev, "Sony CXD2880 has successfully attached.\n");
> +
> +	return 0;
> +
> +fail_fe_conn:
> +	dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
> +			&dvb_spi->dmx_fe);
> +fail_dmx_fe:
> +	dvb_dmxdev_release(&dvb_spi->dmxdev);
> +fail_dmxdev:
> +	dvb_dmx_release(&dvb_spi->demux);
> +fail_dmx:
> +	dvb_unregister_frontend(&dvb_spi->dvb_fe);
> +fail_frontend:
> +	dvb_frontend_detach(&dvb_spi->dvb_fe);
> +fail_attach:
> +	dvb_unregister_adapter(&dvb_spi->adapter);
> +fail_adapter:
> +	kfree(dvb_spi);
> +	return ret;
> +}
> +
> +static int
> +cxd2880_spi_remove(struct spi_device *spi)
> +{
> +	struct cxd2880_dvb_spi *dvb_spi;
> +
> +	if (!spi) {
> +		pr_err("%s: invalid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	dvb_spi = (struct cxd2880_dvb_spi *)dev_get_drvdata(&spi->dev);

No need to cast.

> +
> +	if (!dvb_spi) {
> +		pr_err("%s: failed\n", __func__);
> +		return -EINVAL;
> +	}
> +	dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
> +					&dvb_spi->dmx_fe);
> +	dvb_dmxdev_release(&dvb_spi->dmxdev);
> +	dvb_dmx_release(&dvb_spi->demux);
> +	dvb_unregister_frontend(&dvb_spi->dvb_fe);
> +	dvb_frontend_detach(&dvb_spi->dvb_fe);
> +	dvb_unregister_adapter(&dvb_spi->adapter);
> +
> +	kfree(dvb_spi);
> +	dev_info(&spi->dev, "%s: cxd2880_spi remove ok.\n", __func__);
> +
> +	return 0;
> +}
> +
> +static const struct spi_device_id cxd2880_spi_id[] = {
> +	{ "cxd2880", 0 },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(spi, cxd2880_spi_id);
> +
> +static struct spi_driver cxd2880_spi_driver = {
> +	.driver	= {
> +		.name	= "cxd2880",
> +		.of_match_table = cxd2880_spi_of_match,
> +	},
> +	.id_table = cxd2880_spi_id,
> +	.probe    = cxd2880_spi_probe,
> +	.remove   = cxd2880_spi_remove,
> +};
> +module_spi_driver(cxd2880_spi_driver);
> +
> +MODULE_DESCRIPTION(
> +"Sony CXD2880 DVB-T2/T tuner + demodulator drvier SPI adapter");
> +MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
> +MODULE_LICENSE("GPL v2");



Thanks,
Mauro

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 03/15] [media] cxd2880: Add common files for the driver
  2017-04-14  2:17 ` [PATCH v2 03/15] [media] cxd2880: Add common files for the driver Yasunari.Takiguchi
@ 2017-06-13 12:13   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 36+ messages in thread
From: Mauro Carvalho Chehab @ 2017-06-13 12:13 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
	Toshihiko Matsumoto, Satoshi Watanabe

Em Fri, 14 Apr 2017 11:17:01 +0900
<Yasunari.Takiguchi@sony.com> escreveu:

> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> These are common files for the driver for the
> Sony CXD2880 DVB-T2/T tuner + demodulator.
> These contains helper functions for the driver.
> 
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
> ---
>  drivers/media/dvb-frontends/cxd2880/cxd2880.h      | 46 ++++++++++++
>  .../media/dvb-frontends/cxd2880/cxd2880_common.c   | 84 +++++++++++++++++++++
>  .../media/dvb-frontends/cxd2880/cxd2880_common.h   | 86 ++++++++++++++++++++++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_io.c   | 68 +++++++++++++++++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_io.h   | 62 ++++++++++++++++
>  .../media/dvb-frontends/cxd2880/cxd2880_stdlib.h   | 35 +++++++++
>  .../dvb-frontends/cxd2880/cxd2880_stopwatch_port.c | 71 ++++++++++++++++++
>  7 files changed, 452 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
> 
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880.h b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
> new file mode 100644
> index 000000000000..281f9a784eb5
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
> @@ -0,0 +1,46 @@
> +/*
> + * cxd2880.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver public definitions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_H
> +#define CXD2880_H
> +
> +struct cxd2880_config {
> +	struct spi_device *spi;
> +	struct mutex *spi_mutex; /* For SPI access exclusive control */
> +};
> +
> +#if IS_REACHABLE(CONFIG_DVB_CXD2880)
> +extern struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
> +					struct cxd2880_config *cfg);
> +#else
> +static inline struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
> +					struct cxd2880_config *cfg)
> +{
> +	pr_warn("%s: driver disabled by Kconfig\n", __func__);
> +	return NULL;
> +}
> +#endif /* CONFIG_DVB_CXD2880 */
> +
> +#endif /* CXD2880_H */
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
> new file mode 100644
> index 000000000000..850f3a76b2c7
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
> @@ -0,0 +1,84 @@
> +/*
> + * cxd2880_common.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * common functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "cxd2880_common.h"
> +
> +#define MASKUPPER(n) (((n) == 0) ? 0 : (0xFFFFFFFFU << (32 - (n))))
> +#define MASKLOWER(n) (((n) == 0) ? 0 : (0xFFFFFFFFU >> (32 - (n))))

Please prefer using the already existing bit macros at:
	include/linux/bitfield.h
	include/linux/bitops.h

> +
> +int cxd2880_convert2s_complement(u32 value, u32 bitlen)
> +{
> +	if ((bitlen == 0) || (bitlen >= 32))
> +		return (int)value;
> +
> +	if (value & (u32)(1 << (bitlen - 1)))
> +		return (int)(MASKUPPER(32 - bitlen) | value);
> +	else
> +		return (int)(MASKLOWER(bitlen) & value);
> +}
> +
> +u32 cxd2880_bit_split_from_byte_array(u8 *array, u32 start_bit, u32 bit_num)
> +{
> +	u32 value = 0;
> +	u8 *array_read;
> +	u8 bit_read;
> +	u32 len_remain;
> +
> +	if (!array)
> +		return 0;
> +	if ((bit_num == 0) || (bit_num > 32))
> +		return 0;
> +
> +	array_read = array + (start_bit / 8);
> +	bit_read = (u8)(start_bit % 8);
> +	len_remain = bit_num;
> +
> +	if (bit_read != 0) {
> +		if (((int)len_remain) <= 8 - bit_read) {
> +			value = (*array_read) >> ((8 - bit_read) - len_remain);
> +			len_remain = 0;
> +		} else {
> +			value = *array_read++;
> +			len_remain -= 8 - bit_read;
> +		}
> +	}
> +
> +	while (len_remain > 0) {
> +		if (len_remain < 8) {
> +			value <<= len_remain;
> +			value |= (*array_read++ >> (8 - len_remain));
> +			len_remain = 0;
> +		} else {
> +			value <<= 8;
> +			value |= (u32)(*array_read++);
> +			len_remain -= 8;
> +		}
> +	}
> +
> +	value &= MASKLOWER(bit_num);
> +
> +	return value;
> +}

The Kernel has already optimized macros to handle with bit arrays.

I suspect that you could get what you're doing above with this
macro (defined at include/linux/bitops.h):
	for_each_set_bit()

The Kernel macros usually take advantage of some machine instructions
to find bits, if available at the specific architecture the driver is
compiled.

> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
> new file mode 100644
> index 000000000000..b1ecb44bca10
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
> @@ -0,0 +1,86 @@
> +/*
> + * cxd2880_common.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver common definitions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_COMMON_H
> +#define CXD2880_COMMON_H
> +
> +#include <linux/types.h>
> +
> +#ifndef NULL
> +#ifdef __cplusplus
> +#define NULL 0
> +#else
> +#define NULL ((void *)0)
> +#endif
> +#endif

Please remove the above. NULL is already defined, and we don't compile the
Kernel on c++.

> +
> +#include <linux/delay.h>

> +#define CXD2880_SLEEP(n) msleep(n)
> +#ifndef CXD2880_SLEEP_IN_MON
> +#define CXD2880_SLEEP_IN_MON(n, obj) CXD2880_SLEEP(n)
> +#endif
> +
> +#define CXD2880_ARG_UNUSED(arg) ((void)(arg))

Please get rid of the above too, as the above just obfuscates the
code.

> +
> +enum cxd2880_ret {
> +	CXD2880_RESULT_OK,
> +	CXD2880_RESULT_ERROR_ARG,
> +	CXD2880_RESULT_ERROR_IO,
> +	CXD2880_RESULT_ERROR_SW_STATE,
> +	CXD2880_RESULT_ERROR_HW_STATE,
> +	CXD2880_RESULT_ERROR_TIMEOUT,
> +	CXD2880_RESULT_ERROR_UNLOCK,
> +	CXD2880_RESULT_ERROR_RANGE,
> +	CXD2880_RESULT_ERROR_NOSUPPORT,
> +	CXD2880_RESULT_ERROR_CANCEL,
> +	CXD2880_RESULT_ERROR_OTHER,
> +	CXD2880_RESULT_ERROR_OVERFLOW,
> +	CXD2880_RESULT_OK_CONFIRM
> +};

Please don't define your own return codes. Use the Posix-based error
codes, e. g.:

0 for OK
-EINVAL
-EOVERFLOW
...


> +
> +int cxd2880_convert2s_complement(u32 value, u32 bitlen);
> +
> +u32 cxd2880_bit_split_from_byte_array(u8 *array, u32 start_bit, u32 bit_num);
> +
> +struct cxd2880_atomic {
> +	int counter;
> +};
> +
> +#define cxd2880_atomic_set(a, i) ((a)->counter = i)
> +#define cxd2880_atomic_read(a) ((a)->counter)

No. Use the macros at include/linux/atomic.h for atomic operations.

> +
> +struct cxd2880_stopwatch {
> +	u32 start_time;
> +};
> +
> +enum cxd2880_ret cxd2880_stopwatch_start(struct cxd2880_stopwatch *stopwatch);
> +
> +enum cxd2880_ret cxd2880_stopwatch_sleep(struct cxd2880_stopwatch *stopwatch,
> +					 u32 ms);
> +
> +enum cxd2880_ret cxd2880_stopwatch_elapsed(struct cxd2880_stopwatch *stopwatch,
> +					   u32 *elapsed);
> +
> +#endif
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
> new file mode 100644
> index 000000000000..f0f82055a953
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
> @@ -0,0 +1,68 @@
> +/*
> + * cxd2880_io.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * register I/O interface functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "cxd2880_io.h"
> +
> +enum cxd2880_ret cxd2880_io_common_write_one_reg(struct cxd2880_io *io,
> +						 enum cxd2880_io_tgt tgt,
> +						 u8 sub_address, u8 data)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!io)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	ret = io->write_regs(io, tgt, sub_address, &data, 1);
> +
> +	return ret;
> +}

Here (and below): use the standard linux error codes.

> +
> +enum cxd2880_ret cxd2880_io_set_reg_bits(struct cxd2880_io *io,
> +					 enum cxd2880_io_tgt tgt,
> +					 u8 sub_address, u8 data, u8 mask)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!io)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (mask == 0x00)
> +		return CXD2880_RESULT_OK;
> +
> +	if (mask != 0xFF) {

nitpick: we usually prefer hex numbers in low cases. In any case, please
be coherent along the driver.

> +		u8 rdata = 0x00;
> +
> +		ret = io->read_regs(io, tgt, sub_address, &rdata, 1);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		data = (u8)((data & mask) | (rdata & (mask ^ 0xFF)));
> +	}
> +
> +	ret = io->write_reg(io, tgt, sub_address, data);
> +
> +	return ret;
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
> new file mode 100644
> index 000000000000..4d6db13cf910
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
> @@ -0,0 +1,62 @@
> +/*
> + * cxd2880_io.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * register I/O interface definitions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_IO_H
> +#define CXD2880_IO_H
> +
> +#include "cxd2880_common.h"
> +
> +enum cxd2880_io_tgt {
> +	CXD2880_IO_TGT_SYS,
> +	CXD2880_IO_TGT_DMD
> +};
> +
> +struct cxd2880_io {
> +	enum cxd2880_ret (*read_regs)(struct cxd2880_io *io,
> +				       enum cxd2880_io_tgt tgt, u8 sub_address,
> +				       u8 *data, u32 size);
> +	enum cxd2880_ret (*write_regs)(struct cxd2880_io *io,
> +					enum cxd2880_io_tgt tgt, u8 sub_address,
> +					const u8 *data, u32 size);
> +	enum cxd2880_ret (*write_reg)(struct cxd2880_io *io,
> +				       enum cxd2880_io_tgt tgt, u8 sub_address,
> +				       u8 data);
> +	void *if_object;
> +	u8 i2c_address_sys;
> +	u8 i2c_address_demod;
> +	u8 slave_select;
> +	void *user;
> +};
> +
> +enum cxd2880_ret cxd2880_io_common_write_one_reg(struct cxd2880_io *io,
> +						 enum cxd2880_io_tgt tgt,
> +						 u8 sub_address, u8 data);
> +
> +enum cxd2880_ret cxd2880_io_set_reg_bits(struct cxd2880_io *io,
> +					 enum cxd2880_io_tgt tgt,
> +					 u8 sub_address, u8 data, u8 mask);
> +
> +#endif
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
> new file mode 100644
> index 000000000000..b9ca1b9df110
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
> @@ -0,0 +1,35 @@
> +/*
> + * cxd2880_stdlib.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * standard lib function aliases
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_STDLIB_H
> +#define CXD2880_STDLIB_H
> +
> +#include <linux/string.h>
> +
> +#define cxd2880_memcpy  memcpy
> +#define cxd2880_memset  memset

No. Just use memcpy()/memset() and get rid of this header.

> +
> +#endif
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
> new file mode 100644
> index 000000000000..14ad6aa6c4c0
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
> @@ -0,0 +1,71 @@
> +/*
> + * cxd2880_stopwatch_port.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * time measurement functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "cxd2880_common.h"
> +
> +#include <linux/ktime.h>
> +#include <linux/time.h>
> +#include <linux/timekeeping.h>
> +
> +static u32 get_time_count(void)
> +{
> +	struct timespec tp;
> +
> +	getnstimeofday(&tp);

As said before, don't use it for timeout checks.

> +
> +	return (u32)((tp.tv_sec * 1000) + (tp.tv_nsec / 1000000));
> +}
> +
> +enum cxd2880_ret cxd2880_stopwatch_start(struct cxd2880_stopwatch *stopwatch)
> +{
> +	if (!stopwatch)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	stopwatch->start_time = get_time_count();
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_stopwatch_sleep(struct cxd2880_stopwatch *stopwatch,
> +					 u32 ms)
> +{
> +	if (!stopwatch)
> +		return CXD2880_RESULT_ERROR_ARG;
> +	CXD2880_ARG_UNUSED(*stopwatch);
> +	CXD2880_SLEEP(ms);
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_stopwatch_elapsed(struct cxd2880_stopwatch *stopwatch,
> +					   u32 *elapsed)
> +{
> +	if (!stopwatch || !elapsed)
> +		return CXD2880_RESULT_ERROR_ARG;
> +	*elapsed = get_time_count() - stopwatch->start_time;
> +
> +	return CXD2880_RESULT_OK;
> +}

You should reimplement the above using jiffies.

Thanks,
Mauro

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 04/15] [media] cxd2880: Add math functions for the driver
  2017-04-14  2:22 ` [PATCH v2 04/15] [media] cxd2880: Add math functions " Yasunari.Takiguchi
@ 2017-06-13 12:18   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 36+ messages in thread
From: Mauro Carvalho Chehab @ 2017-06-13 12:18 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
	Toshihiko Matsumoto, Satoshi Watanabe

Em Fri, 14 Apr 2017 11:22:37 +0900
<Yasunari.Takiguchi@sony.com> escreveu:

> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> Provide some math support functions (fixed-point log functions)
> for the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

No need. The Kernel already provide log functions. You should use the
existing ones, instead of reinventing the wheel. The log2 functions
are at:
	include/linux/log2.h

We also have both log2 and log10 functions at:
	drivers/media/dvb-core/dvb_math.h

That should likely what you want.


> 
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
> ---
>  drivers/media/dvb-frontends/cxd2880/cxd2880_math.c | 89 ++++++++++++++++++++++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_math.h | 40 ++++++++++
>  2 files changed, 129 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_math.h
> 
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_math.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
> new file mode 100644
> index 000000000000..434c827898ff
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
> @@ -0,0 +1,89 @@
> +/*
> + * cxd2880_math.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * mathmatics functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "cxd2880_math.h"
> +
> +#define MAX_BIT_PRECISION	  5
> +#define FRAC_BITMASK	    0x1F
> +#define LOG2_10_100X	     332
> +#define LOG2_E_100X	      144
> +
> +static const u8 log2_look_up[] = {
> +	0, 4,
> +	9, 13,
> +	17, 21,
> +	25, 29,
> +	32, 36,
> +	39, 43,
> +	46, 49,
> +	52, 55,
> +	58, 61,
> +	64, 67,
> +	70, 73,
> +	75, 78,
> +	81, 83,
> +	86, 88,
> +	91, 93,
> +	95, 98
> +};
> +
> +u32 cxd2880_math_log2(u32 x)
> +{
> +	u8 count = 0;
> +	u8 index = 0;
> +	u32 xval = x;
> +
> +	for (x >>= 1; x > 0; x >>= 1)
> +		count++;
> +
> +	x = count * 100;
> +
> +	if (count > 0) {
> +		if (count <= MAX_BIT_PRECISION) {
> +			index =
> +			    (u8)(xval << (MAX_BIT_PRECISION - count)) &
> +			    FRAC_BITMASK;
> +			x += log2_look_up[index];
> +		} else {
> +			index =
> +			    (u8)(xval >> (count - MAX_BIT_PRECISION)) &
> +			    FRAC_BITMASK;
> +			x += log2_look_up[index];
> +		}
> +	}
> +
> +	return x;
> +}
> +
> +u32 cxd2880_math_log10(u32 x)
> +{
> +	return ((100 * cxd2880_math_log2(x) + LOG2_10_100X / 2) / LOG2_10_100X);
> +}
> +
> +u32 cxd2880_math_log(u32 x)
> +{
> +	return ((100 * cxd2880_math_log2(x) + LOG2_E_100X / 2) / LOG2_E_100X);
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_math.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_math.h
> new file mode 100644
> index 000000000000..94211835a4ad
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_math.h
> @@ -0,0 +1,40 @@
> +/*
> + * cxd2880_math.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * mathmatics definitions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_MATH_H_
> +#define CXD2880_MATH_H_
> +
> +#include "cxd2880_common.h"
> +
> +u32 cxd2880_math_log2(u32 x);
> +u32 cxd2880_math_log10(u32 x);
> +u32 cxd2880_math_log(u32 x);
> +
> +#ifndef min
> +#define min(a, b)	    (((a) < (b)) ? (a) : (b))
> +#endif

We also have min() macro at the Kernel.

> +
> +#endif



Thanks,
Mauro

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 05/15] [media] cxd2880: Add spi device IO routines
  2017-04-14  2:25 ` [PATCH v2 05/15] [media] cxd2880: Add spi device IO routines Yasunari.Takiguchi
@ 2017-06-13 12:25   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 36+ messages in thread
From: Mauro Carvalho Chehab @ 2017-06-13 12:25 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
	Toshihiko Matsumoto, Satoshi Watanabe

Em Fri, 14 Apr 2017 11:25:23 +0900
<Yasunari.Takiguchi@sony.com> escreveu:

> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> Add functions for initializing, reading and writing to the SPI
> device for the Sony CXD2880 DVB-T2/T tuner + demodulator.
> 
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
> ---
>  .../dvb-frontends/cxd2880/cxd2880_devio_spi.c      | 147 +++++++++++++++++++++
>  .../dvb-frontends/cxd2880/cxd2880_devio_spi.h      |  40 ++++++
>  drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h  |  51 +++++++
>  .../dvb-frontends/cxd2880/cxd2880_spi_device.c     | 130 ++++++++++++++++++
>  .../dvb-frontends/cxd2880/cxd2880_spi_device.h     |  45 +++++++
>  5 files changed, 413 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
> 
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
> new file mode 100644
> index 000000000000..516efade6bf5
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
> @@ -0,0 +1,147 @@
> +/*
> + * cxd2880_devio_spi.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * I/O interface via SPI
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "cxd2880_devio_spi.h"
> +#include "cxd2880_stdlib.h"
> +
> +#define BURST_WRITE_MAX 128
> +
> +static enum cxd2880_ret cxd2880_io_spi_read_reg(struct cxd2880_io *io,
> +						enum cxd2880_io_tgt tgt,
> +						u8 sub_address, u8 *data,
> +						u32 size)

Return argument should be integer, as it is an error code or zero.

> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	struct cxd2880_spi *spi = NULL;
> +	u8 send_data[6];
> +	u8 *read_data_top = data;
> +
> +	if ((!io) || (!io->if_object) || (!data))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (sub_address + size > 0x100)
> +		return CXD2880_RESULT_ERROR_RANGE;

Please use standard return error codes. In this specific case, likely
-EINVAL and -ERANGE.

Same apply to other functions below.

> +
> +	spi = (struct cxd2880_spi *)(io->if_object);
> +
> +	if (tgt == CXD2880_IO_TGT_SYS)
> +		send_data[0] = 0x0B;
> +	else
> +		send_data[0] = 0x0A;
> +
> +	send_data[3] = 0;
> +	send_data[4] = 0;
> +	send_data[5] = 0;
> +
> +	while (size > 0) {
> +		send_data[1] = sub_address;
> +		if (size > 255)
> +			send_data[2] = 255;
> +		else
> +			send_data[2] = (u8)size;

No need to cast.

> +
> +		ret =
> +		    spi->write_read(spi, send_data, sizeof(send_data),
> +				    read_data_top, send_data[2]);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;

Please keep it simple, e. g.:

	if (ret)
		return ret;

> +
> +		sub_address += send_data[2];
> +		read_data_top += send_data[2];
> +		size -= send_data[2];
> +	}
> +
> +	return ret;
> +}
> +
> +static enum cxd2880_ret cxd2880_io_spi_write_reg(struct cxd2880_io *io,
> +						 enum cxd2880_io_tgt tgt,
> +						 u8 sub_address,
> +						 const u8 *data, u32 size)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	struct cxd2880_spi *spi = NULL;
> +	u8 send_data[BURST_WRITE_MAX + 4];
> +	const u8 *write_data_top = data;
> +
> +	if ((!io) || (!io->if_object) || (!data))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (size > BURST_WRITE_MAX)
> +		return CXD2880_RESULT_ERROR_OVERFLOW;
> +
> +	if (sub_address + size > 0x100)
> +		return CXD2880_RESULT_ERROR_RANGE;
> +
> +	spi = (struct cxd2880_spi *)(io->if_object);

Is cast really needed here?

> +
> +	if (tgt == CXD2880_IO_TGT_SYS)
> +		send_data[0] = 0x0F;
> +	else
> +		send_data[0] = 0x0E;
> +
> +	while (size > 0) {
> +		send_data[1] = sub_address;
> +		if (size > 255)
> +			send_data[2] = 255;
> +		else
> +			send_data[2] = (u8)size;

cast not needed.

> +
> +		cxd2880_memcpy(&send_data[3], write_data_top, send_data[2]);

just memcpy().

> +
> +		if (tgt == CXD2880_IO_TGT_SYS) {
> +			send_data[3 + send_data[2]] = 0x00;
> +			ret = spi->write(spi, send_data, send_data[2] + 4);
> +		} else {
> +			ret = spi->write(spi, send_data, send_data[2] + 3);
> +		}
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		sub_address += send_data[2];
> +		write_data_top += send_data[2];
> +		size -= send_data[2];
> +	}
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_io_spi_create(struct cxd2880_io *io,
> +				       struct cxd2880_spi *spi, u8 slave_select)
> +{
> +	if ((!io) || (!spi))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	io->read_regs = cxd2880_io_spi_read_reg;
> +	io->write_regs = cxd2880_io_spi_write_reg;
> +	io->write_reg = cxd2880_io_common_write_one_reg;
> +	io->if_object = spi;
> +	io->i2c_address_sys = 0;
> +	io->i2c_address_demod = 0;
> +	io->slave_select = slave_select;
> +
> +	return CXD2880_RESULT_OK;
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
> new file mode 100644
> index 000000000000..15934bf11935
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
> @@ -0,0 +1,40 @@
> +/*
> + * cxd2880_devio_spi.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * I/O interface via SPI
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_DEVIO_SPI_H
> +#define CXD2880_DEVIO_SPI_H
> +
> +#include "cxd2880_common.h"
> +#include "cxd2880_io.h"
> +#include "cxd2880_spi.h"
> +
> +#include "cxd2880_tnrdmd.h"
> +
> +enum cxd2880_ret cxd2880_io_spi_create(struct cxd2880_io *io,
> +				       struct cxd2880_spi *spi,
> +				       u8 slave_select);
> +
> +#endif
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
> new file mode 100644
> index 000000000000..81e5be747962
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
> @@ -0,0 +1,51 @@
> +/*
> + * cxd2880_spi.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * SPI access definitions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_SPI_H
> +#define CXD2880_SPI_H
> +
> +#include "cxd2880_common.h"
> +
> +enum cxd2880_spi_mode {
> +	CXD2880_SPI_MODE_0,
> +	CXD2880_SPI_MODE_1,
> +	CXD2880_SPI_MODE_2,
> +	CXD2880_SPI_MODE_3
> +};
> +
> +struct cxd2880_spi {
> +	enum cxd2880_ret (*read)(struct cxd2880_spi *spi, u8 *data,
> +				  u32 size);
> +	enum cxd2880_ret (*write)(struct cxd2880_spi *spi, const u8 *data,
> +				   u32 size);
> +	enum cxd2880_ret (*write_read)(struct cxd2880_spi *spi,
> +					const u8 *tx_data, u32 tx_size,
> +					u8 *rx_data, u32 rx_size);

please use int for returned values, and convert to the linux
error codes.

> +	u32 flags;
> +	void *user;
> +};
> +
> +#endif
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
> new file mode 100644
> index 000000000000..af9ed40c900b
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
> @@ -0,0 +1,130 @@
> +/*
> + * cxd2880_spi_device.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * SPI access functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/spi/spi.h>
> +
> +#include "cxd2880_spi_device.h"
> +
> +static enum cxd2880_ret cxd2880_spi_device_write(struct cxd2880_spi *spi,
> +						const u8 *data, u32 size)
> +{
> +	struct cxd2880_spi_device *spi_device = NULL;
> +	struct spi_message msg;
> +	struct spi_transfer tx;
> +	int result = 0;
> +
> +	if ((!spi) || (!spi->user) || (!data) || (size == 0))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	spi_device = (struct cxd2880_spi_device *)(spi->user);

Is cast needed?

> +
> +	memset(&tx, 0, sizeof(tx));
> +	tx.tx_buf = data;
> +	tx.len = size;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&tx, &msg);
> +	result = spi_sync(spi_device->spi, &msg);
> +
> +	if (result < 0)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret cxd2880_spi_device_write_read(struct cxd2880_spi *spi,
> +							const u8 *tx_data,
> +							u32 tx_size,
> +							u8 *rx_data,
> +							u32 rx_size)
> +{
> +	struct cxd2880_spi_device *spi_device = NULL;
> +	int result = 0;
> +
> +	if ((!spi) || (!spi->user) || (!tx_data) ||
> +		 (tx_size == 0) || (!rx_data) || (rx_size == 0))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	spi_device = (struct cxd2880_spi_device *)(spi->user);
> +
> +	result = spi_write_then_read(spi_device->spi, tx_data,
> +					tx_size, rx_data, rx_size);
> +	if (result < 0)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret
> +cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device,
> +				enum cxd2880_spi_mode mode,
> +				u32 speed_hz)
> +{
> +	int result = 0;
> +	struct spi_device *spi = spi_device->spi;
> +
> +	switch (mode) {
> +	case CXD2880_SPI_MODE_0:
> +		spi->mode = SPI_MODE_0;
> +		break;
> +	case CXD2880_SPI_MODE_1:
> +		spi->mode = SPI_MODE_1;
> +		break;
> +	case CXD2880_SPI_MODE_2:
> +		spi->mode = SPI_MODE_2;
> +		break;
> +	case CXD2880_SPI_MODE_3:
> +		spi->mode = SPI_MODE_3;
> +		break;
> +	default:
> +		return CXD2880_RESULT_ERROR_ARG;
> +	}
> +
> +	spi->max_speed_hz = speed_hz;
> +	spi->bits_per_word = 8;
> +	result = spi_setup(spi);
> +	if (result != 0) {
> +		pr_err("spi_setup failed %d\n", result);
> +		return CXD2880_RESULT_ERROR_ARG;
> +	}
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_spi_device_create_spi(struct cxd2880_spi *spi,
> +					struct cxd2880_spi_device *spi_device)
> +{
> +	if ((!spi) || (!spi_device))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	spi->read = NULL;
> +	spi->write = cxd2880_spi_device_write;
> +	spi->write_read = cxd2880_spi_device_write_read;
> +	spi->flags = 0;
> +	spi->user = spi_device;
> +
> +	return CXD2880_RESULT_OK;
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
> new file mode 100644
> index 000000000000..343d9161d537
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
> @@ -0,0 +1,45 @@
> +/*
> + * cxd2880_spi_device.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * SPI access interface
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_SPI_DEVICE_H
> +#define CXD2880_SPI_DEVICE_H
> +
> +#include "cxd2880_spi.h"
> +
> +struct cxd2880_spi_device {
> +	struct spi_device *spi;
> +};
> +
> +enum cxd2880_ret
> +cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device,
> +				enum cxd2880_spi_mode mode,
> +				u32 speedHz);
> +
> +enum cxd2880_ret
> +cxd2880_spi_device_create_spi(struct cxd2880_spi *spi,
> +				struct cxd2880_spi_device *spi_device);
> +
> +#endif /* CXD2880_SPI_DEVICE_H */



Thanks,
Mauro

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 06/15] [media] cxd2880: Add tuner part of the driver
  2017-04-14  2:26 ` [PATCH v2 06/15] [media] cxd2880: Add tuner part of the driver Yasunari.Takiguchi
@ 2017-06-13 13:09   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 36+ messages in thread
From: Mauro Carvalho Chehab @ 2017-06-13 13:09 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
	Toshihiko Matsumoto, Satoshi Watanabe

Em Fri, 14 Apr 2017 11:26:56 +0900
<Yasunari.Takiguchi@sony.com> escreveu:

> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> This part of the driver has the main routines to handle
> the tuner and demodulator functionality.  The tnrdmd_mon.* files
> have monitor functions for the driver.
> This is part of the Sony CXD2880 DVB-T2/T tuner + demodulator driver.
> 
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
> ---
>  drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h  |   50 +
>  .../media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c   | 3925 ++++++++++++++++++++
>  .../media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h   |  395 ++
>  .../cxd2880/cxd2880_tnrdmd_driver_version.h        |   29 +
>  .../dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c     |  207 ++
>  .../dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h     |   52 +
>  6 files changed, 4658 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
> 
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
> new file mode 100644
> index 000000000000..7de098d556fe
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
> @@ -0,0 +1,50 @@
> +/*
> + * cxd2880_dtv.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * DTV related definitions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_DTV_H
> +#define CXD2880_DTV_H
> +
> +enum cxd2880_dtv_sys {
> +	CXD2880_DTV_SYS_UNKNOWN,
> +	CXD2880_DTV_SYS_DVBT,
> +	CXD2880_DTV_SYS_DVBT2,
> +	CXD2880_DTV_SYS_ISDBT,
> +	CXD2880_DTV_SYS_ISDBTSB,
> +	CXD2880_DTV_SYS_ISDBTMM_A,
> +	CXD2880_DTV_SYS_ISDBTMM_B,
> +	CXD2880_DTV_SYS_ANY
> +};
> +
> +enum cxd2880_dtv_bandwidth {
> +	CXD2880_DTV_BW_UNKNOWN = 0,
> +	CXD2880_DTV_BW_1_7_MHZ = 1,
> +	CXD2880_DTV_BW_5_MHZ = 5,
> +	CXD2880_DTV_BW_6_MHZ = 6,
> +	CXD2880_DTV_BW_7_MHZ = 7,
> +	CXD2880_DTV_BW_8_MHZ = 8
> +};
> +
> +#endif
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
> new file mode 100644
> index 000000000000..286384ae0124
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
> @@ -0,0 +1,3925 @@
> +/*
> + * cxd2880_tnrdmd.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * common control functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "cxd2880_common.h"
> +#include "cxd2880_stdlib.h"
> +#include "cxd2880_tnrdmd.h"
> +#include "cxd2880_tnrdmd_mon.h"
> +#include "cxd2880_tnrdmd_dvbt.h"
> +#include "cxd2880_tnrdmd_dvbt2.h"
> +
> +static enum cxd2880_ret p_init1(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	u8 data = 0;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;

I won't repeat it anymore: please use standard linux error codes
and return integers.

> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if ((tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) ||
> +	    (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)) {
> +		switch (tnr_dmd->create_param.ts_output_if) {
> +		case CXD2880_TNRDMD_TSOUT_IF_TS:
> +			data = 0x00;
> +			break;
> +		case CXD2880_TNRDMD_TSOUT_IF_SPI:
> +			data = 0x01;
> +			break;
> +		case CXD2880_TNRDMD_TSOUT_IF_SDIO:
> +			data = 0x02;
> +			break;
> +		default:
> +			return CXD2880_RESULT_ERROR_ARG;
> +		}
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x10,
> +					   data) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x11,
> +				   0x16) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	switch (tnr_dmd->chip_id) {
> +	case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X:
> +		data = 0x1A;
> +		break;
> +	case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11:
> +		data = 0x16;
> +		break;
> +	default:
> +		return CXD2880_RESULT_ERROR_NOSUPPORT;
> +	}
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x10,
> +				   data) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;

Just return the error code from the routine, e. g.:

	int ret;

	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
				     CXD2880_IO_TGT_SYS, 0x10,
				     data);
	if (ret)
		return ret;

Same applies to the code below.

> +
> +	if (tnr_dmd->create_param.en_internal_ldo)
> +		data = 0x01;
> +	else
> +		data = 0x00;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x11,
> +				   data) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x13,
> +				   data) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x12,
> +				   data) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	switch (tnr_dmd->chip_id) {
> +	case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X:
> +		data = 0x01;
> +		break;
> +	case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11:
> +		data = 0x00;
> +		break;
> +	default:
> +		return CXD2880_RESULT_ERROR_NOSUPPORT;
> +	}
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x69,
> +				   data) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret p_init2(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	u8 data[6] = { 0 };
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = tnr_dmd->create_param.xosc_cap;
> +	data[1] = tnr_dmd->create_param.xosc_i;
> +	switch (tnr_dmd->create_param.xtal_share_type) {
> +	case CXD2880_TNRDMD_XTAL_SHARE_NONE:
> +		data[2] = 0x01;
> +		data[3] = 0x00;
> +		break;
> +	case CXD2880_TNRDMD_XTAL_SHARE_EXTREF:
> +		data[2] = 0x00;
> +		data[3] = 0x00;
> +		break;
> +	case CXD2880_TNRDMD_XTAL_SHARE_MASTER:
> +		data[2] = 0x01;
> +		data[3] = 0x01;
> +		break;
> +	case CXD2880_TNRDMD_XTAL_SHARE_SLAVE:
> +		data[2] = 0x00;
> +		data[3] = 0x01;
> +		break;
> +	default:
> +		return CXD2880_RESULT_ERROR_ARG;
> +	}
> +	data[4] = 0x06;
> +	data[5] = 0x00;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x13, data,
> +				    6) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret p_init3(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	u8 data[2] = { 0 };
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	switch (tnr_dmd->diver_mode) {
> +	case CXD2880_TNRDMD_DIVERMODE_SINGLE:
> +		data[0] = 0x00;
> +		break;
> +	case CXD2880_TNRDMD_DIVERMODE_MAIN:
> +		data[0] = 0x03;
> +		break;
> +	case CXD2880_TNRDMD_DIVERMODE_SUB:
> +		data[0] = 0x02;
> +		break;
> +	default:
> +		return CXD2880_RESULT_ERROR_ARG;
> +	}
> +
> +	data[1] = 0x01;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x1F, data,
> +				    2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret rf_init1(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	u8 data[80] = { 0 };
> +	u8 addr = 0;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x01;
> +	data[1] = 0x00;
> +	data[2] = 0x01;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x21, data,
> +				    3) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x01;
> +	data[1] = 0x01;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x17, data,
> +				    2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->create_param.stationary_use) {
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x1A,
> +					   0x06) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x4F,
> +				   0x18) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x61,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x71,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x9D,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x7D,
> +				   0x02) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x8F,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x8B,
> +				   0xC6) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x9A,
> +				   0x03) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x1C,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x00;
> +	if ((tnr_dmd->create_param.is_cxd2881gg) &&
> +	    (tnr_dmd->create_param.xtal_share_type ==
> +		CXD2880_TNRDMD_XTAL_SHARE_SLAVE))
> +		data[1] = 0x00;
> +	else
> +		data[1] = 0x1F;
> +	data[2] = 0x0A;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0xB5, data,
> +				    3) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0xB9,
> +				   0x07) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x33,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0xC1,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0xC4,
> +				   0x1E) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) {
> +		data[0] = 0x34;
> +		data[1] = 0x2C;
> +	} else {
> +		data[0] = 0x2F;
> +		data[1] = 0x25;
> +	}
> +	data[2] = 0x15;
> +	data[3] = 0x19;
> +	data[4] = 0x1B;
> +	data[5] = 0x15;
> +	data[6] = 0x19;
> +	data[7] = 0x1B;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0xD9, data,
> +				    8) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x11) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x6C;
> +	data[1] = 0x10;
> +	data[2] = 0xA6;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x44, data,
> +				    3) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x16;
> +	data[1] = 0xA8;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x50, data,
> +				    2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x00;
> +	data[1] = 0x22;
> +	data[2] = 0x00;
> +	data[3] = 0x88;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x62, data,
> +				    4) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x74,
> +				   0x75) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x05;
> +	data[1] = 0x05;
> +	data[2] = 0x05;
> +	data[3] = 0x05;
> +	data[4] = 0x05;
> +	data[5] = 0x05;
> +	data[6] = 0x05;
> +	data[7] = 0x05;
> +	data[8] = 0x05;
> +	data[9] = 0x04;
> +	data[10] = 0x04;
> +	data[11] = 0x04;
> +	data[12] = 0x03;
> +	data[13] = 0x03;
> +	data[14] = 0x03;
> +	data[15] = 0x04;
> +	data[16] = 0x04;
> +	data[17] = 0x05;
> +	data[18] = 0x05;
> +	data[19] = 0x05;
> +	data[20] = 0x02;
> +	data[21] = 0x02;
> +	data[22] = 0x02;
> +	data[23] = 0x02;
> +	data[24] = 0x02;
> +	data[25] = 0x02;
> +	data[26] = 0x02;
> +	data[27] = 0x02;
> +	data[28] = 0x02;
> +	data[29] = 0x03;
> +	data[30] = 0x02;
> +	data[31] = 0x01;
> +	data[32] = 0x01;
> +	data[33] = 0x01;
> +	data[34] = 0x02;
> +	data[35] = 0x02;
> +	data[36] = 0x03;
> +	data[37] = 0x04;
> +	data[38] = 0x04;
> +	data[39] = 0x04;

Better to put the above inside a const and pass it through the
write_regs code. Same applies to other similar const data within
this function. That will likely reduce code size of this module.

> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x7F, data,
> +				    40) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x16) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x00;
> +	data[1] = 0x71;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x10, data,
> +				    2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x23,
> +				   0x89) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0xFF;
> +	data[1] = 0x00;
> +	data[2] = 0x00;
> +	data[3] = 0x00;
> +	data[4] = 0x00;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x27, data,
> +				    5) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x00;
> +	data[1] = 0x00;
> +	data[2] = 0x00;
> +	data[3] = 0x00;
> +	data[4] = 0x00;
> +	data[5] = 0x01;
> +	data[6] = 0x00;
> +	data[7] = 0x01;
> +	data[8] = 0x00;
> +	data[9] = 0x02;
> +	data[10] = 0x00;
> +	data[11] = 0x63;
> +	data[12] = 0x00;
> +	data[13] = 0x00;
> +	data[14] = 0x00;
> +	data[15] = 0x03;
> +	data[16] = 0x00;
> +	data[17] = 0x04;
> +	data[18] = 0x00;
> +	data[19] = 0x04;
> +	data[20] = 0x00;
> +	data[21] = 0x06;
> +	data[22] = 0x00;
> +	data[23] = 0x06;
> +	data[24] = 0x00;
> +	data[25] = 0x08;
> +	data[26] = 0x00;
> +	data[27] = 0x09;
> +	data[28] = 0x00;
> +	data[29] = 0x0B;
> +	data[30] = 0x00;
> +	data[31] = 0x0B;
> +	data[32] = 0x00;
> +	data[33] = 0x0D;
> +	data[34] = 0x00;
> +	data[35] = 0x0D;
> +	data[36] = 0x00;
> +	data[37] = 0x0F;
> +	data[38] = 0x00;
> +	data[39] = 0x0F;
> +	data[40] = 0x00;
> +	data[41] = 0x0F;
> +	data[42] = 0x00;
> +	data[43] = 0x10;
> +	data[44] = 0x00;
> +	data[45] = 0x79;
> +	data[46] = 0x00;
> +	data[47] = 0x00;
> +	data[48] = 0x00;
> +	data[49] = 0x02;
> +	data[50] = 0x00;
> +	data[51] = 0x00;
> +	data[52] = 0x00;
> +	data[53] = 0x03;
> +	data[54] = 0x00;
> +	data[55] = 0x01;
> +	data[56] = 0x00;
> +	data[57] = 0x03;
> +	data[58] = 0x00;
> +	data[59] = 0x03;
> +	data[60] = 0x00;
> +	data[61] = 0x03;
> +	data[62] = 0x00;
> +	data[63] = 0x04;
> +	data[64] = 0x00;
> +	data[65] = 0x04;
> +	data[66] = 0x00;
> +	data[67] = 0x06;
> +	data[68] = 0x00;
> +	data[69] = 0x05;
> +	data[70] = 0x00;
> +	data[71] = 0x07;
> +	data[72] = 0x00;
> +	data[73] = 0x07;
> +	data[74] = 0x00;
> +	data[75] = 0x08;
> +	data[76] = 0x00;
> +	data[77] = 0x0A;
> +	data[78] = 0x03;
> +	data[79] = 0xE0;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x3A, data,
> +				    80) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	data[0] = 0x03;
> +	data[1] = 0xE0;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0xBC, data,
> +				    2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x51,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0xC5,
> +				   0x07) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x11) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x70,
> +				   0xE9) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x76,
> +				   0x0A) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x78,
> +				   0x32) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x7A,
> +				   0x46) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x7C,
> +				   0x86) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x7E,
> +				   0xA4) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0xE1,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->create_param.stationary_use) {
> +		data[0] = 0x06;
> +		data[1] = 0x07;
> +		data[2] = 0x1A;
> +	} else {
> +		data[0] = 0x00;
> +		data[1] = 0x08;
> +		data[2] = 0x19;
> +	}
> +	data[3] = 0x0E;
> +	data[4] = 0x09;
> +	data[5] = 0x0E;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x12) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	for (addr = 0x10; addr < 0x9F; addr += 6) {
> +		if (tnr_dmd->lna_thrs_tbl_air) {
> +			u8 idx = 0;
> +
> +			idx = (addr - 0x10) / 6;
> +			data[0] =
> +			    tnr_dmd->lna_thrs_tbl_air->thrs[idx].off_on;
> +			data[1] =
> +			    tnr_dmd->lna_thrs_tbl_air->thrs[idx].on_off;
> +		}
> +		if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +					    CXD2880_IO_TGT_SYS, addr,
> +					    data,
> +					    6) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	data[0] = 0x00;
> +	data[1] = 0x08;
> +	if (tnr_dmd->create_param.stationary_use)
> +		data[2] = 0x1A;
> +	else
> +		data[2] = 0x19;
> +	data[3] = 0x0E;
> +	data[4] = 0x09;
> +	data[5] = 0x0E;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x13) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	for (addr = 0x10; addr < 0xCF; addr += 6) {
> +		if (tnr_dmd->lna_thrs_tbl_cable) {
> +			u8 idx = 0;
> +
> +			idx = (addr - 0x10) / 6;
> +			data[0] =
> +			    tnr_dmd->lna_thrs_tbl_cable->thrs[idx].off_on;
> +			data[1] =
> +			    tnr_dmd->lna_thrs_tbl_cable->thrs[idx].on_off;
> +		}
> +		if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +					    CXD2880_IO_TGT_SYS, addr,
> +					    data,
> +					    6) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x11) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x08;
> +	data[1] = 0x09;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0xBD, data,
> +				    2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x08;
> +	data[1] = 0x09;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0xC4, data,
> +				    2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x20;
> +	data[1] = 0x20;
> +	data[2] = 0x30;
> +	data[3] = 0x41;
> +	data[4] = 0x50;
> +	data[5] = 0x5F;
> +	data[6] = 0x6F;
> +	data[7] = 0x80;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0xC9, data,
> +				    8) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x14) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x15;
> +	data[1] = 0x18;
> +	data[2] = 0x00;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x10, data,
> +				    3) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x15,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x16) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x00;
> +	data[1] = 0x09;
> +	data[2] = 0x00;
> +	data[3] = 0x08;
> +	data[4] = 0x00;
> +	data[5] = 0x07;
> +	data[6] = 0x00;
> +	data[7] = 0x06;
> +	data[8] = 0x00;
> +	data[9] = 0x05;
> +	data[10] = 0x00;
> +	data[11] = 0x03;
> +	data[12] = 0x00;
> +	data[13] = 0x02;
> +	data[14] = 0x00;
> +	data[15] = 0x00;
> +	data[16] = 0x00;
> +	data[17] = 0x78;
> +	data[18] = 0x00;
> +	data[19] = 0x00;
> +	data[20] = 0x00;
> +	data[21] = 0x06;
> +	data[22] = 0x00;
> +	data[23] = 0x08;
> +	data[24] = 0x00;
> +	data[25] = 0x08;
> +	data[26] = 0x00;
> +	data[27] = 0x0C;
> +	data[28] = 0x00;
> +	data[29] = 0x0C;
> +	data[30] = 0x00;
> +	data[31] = 0x0D;
> +	data[32] = 0x00;
> +	data[33] = 0x0F;
> +	data[34] = 0x00;
> +	data[35] = 0x0E;
> +	data[36] = 0x00;
> +	data[37] = 0x0E;
> +	data[38] = 0x00;
> +	data[39] = 0x10;
> +	data[40] = 0x00;
> +	data[41] = 0x0F;
> +	data[42] = 0x00;
> +	data[43] = 0x0E;
> +	data[44] = 0x00;
> +	data[45] = 0x10;
> +	data[46] = 0x00;
> +	data[47] = 0x0F;
> +	data[48] = 0x00;
> +	data[49] = 0x0E;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x12, data,
> +				    50) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	CXD2880_SLEEP(1);
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x0A) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x10, data,
> +				   1) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if ((data[0] & 0x01) == 0x00)
> +		return CXD2880_RESULT_ERROR_HW_STATE;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x25,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	CXD2880_SLEEP(1);

just use msleep(). Actually, msleep(1) ensures that it will slip for
at least 1 ms, but, it can take up to 10 ms. So, if you want to
be more precise, you could, instead use usleeprange().

Same applies to other places where you're using this macro.

> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x0A) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x11, data,
> +				   1) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if ((data[0] & 0x01) == 0x00)
> +		return CXD2880_RESULT_ERROR_HW_STATE;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x02,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x21,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0xE1) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x8F,
> +				   0x16) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x67,
> +				   0x60) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x6A,
> +				   0x0F) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x6C,
> +				   0x17) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x00;
> +	data[1] = 0xFE;
> +	data[2] = 0xEE;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_DMD, 0x6E, data,
> +				    3) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0xA1;
> +	data[1] = 0x8B;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_DMD, 0x8D, data,
> +				    2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x08;
> +	data[1] = 0x09;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_DMD, 0x77, data,
> +				    2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->create_param.stationary_use) {
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_DMD, 0x80,
> +					   0xAA) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0xE2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x41,
> +				   0xA0) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x4B,
> +				   0x68) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x21,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x10,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x25,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	CXD2880_SLEEP(1);
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x1A) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x10, data,
> +				   1) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if ((data[0] & 0x01) == 0x00)
> +		return CXD2880_RESULT_ERROR_HW_STATE;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x14,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x26,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret rf_init2(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	u8 data[5] = { 0 };
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x40;
> +	data[1] = 0x40;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0xEA, data,
> +				    2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	CXD2880_SLEEP(1);
> +
> +	data[0] = 0x00;
> +	if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X)
> +		data[1] = 0x00;
> +	else
> +		data[1] = 0x01;
> +	data[2] = 0x01;
> +	data[3] = 0x03;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x30, data,
> +				    4) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x14) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x1B,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x21,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0xE1) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0xD3,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x21,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret x_tune1(struct cxd2880_tnrdmd *tnr_dmd,
> +				enum cxd2880_dtv_sys sys, u32 freq_khz,
> +				enum cxd2880_dtv_bandwidth bandwidth,
> +				u8 is_cable, int shift_frequency_khz)
> +{
> +	u8 data[11] = { 0 };
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x10,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	data[0] = 0x00;
> +	data[1] = 0x00;
> +	data[2] = 0x0E;
> +	data[3] = 0x00;
> +	data[4] = 0x03;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0xE7, data,
> +				    5) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	data[0] = 0x1F;
> +	data[1] = 0x80;
> +	data[2] = 0x18;
> +	data[3] = 0x00;
> +	data[4] = 0x07;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0xE7, data,
> +				    5) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	CXD2880_SLEEP(1);
> +
> +	data[0] = 0x72;
> +	data[1] = 0x81;
> +	data[3] = 0x1D;
> +	data[4] = 0x6F;
> +	data[5] = 0x7E;
> +	data[7] = 0x1C;
> +	switch (sys) {
> +	case CXD2880_DTV_SYS_DVBT:
> +	case CXD2880_DTV_SYS_ISDBT:
> +	case CXD2880_DTV_SYS_ISDBTSB:
> +	case CXD2880_DTV_SYS_ISDBTMM_A:
> +	case CXD2880_DTV_SYS_ISDBTMM_B:
> +		data[2] = 0x94;
> +		data[6] = 0x91;
> +		break;
> +	case CXD2880_DTV_SYS_DVBT2:
> +		data[2] = 0x96;
> +		data[6] = 0x93;
> +		break;
> +	default:
> +		return CXD2880_RESULT_ERROR_ARG;
> +	}
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x44, data,
> +				    8) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x62,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x15) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x03;
> +	data[1] = 0xE2;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x1E, data,
> +				    2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	data[0] = (u8)(is_cable ? 0x01 : 0x00);
> +	data[1] = 0x00;
> +	data[2] = 0x6B;
> +	data[3] = 0x4D;
> +
> +	switch (bandwidth) {
> +	case CXD2880_DTV_BW_1_7_MHZ:
> +		data[4] = 0x03;
> +		break;
> +	case CXD2880_DTV_BW_5_MHZ:
> +	case CXD2880_DTV_BW_6_MHZ:
> +		data[4] = 0x00;
> +		break;
> +	case CXD2880_DTV_BW_7_MHZ:
> +		data[4] = 0x01;
> +		break;
> +	case CXD2880_DTV_BW_8_MHZ:
> +		data[4] = 0x02;
> +		break;
> +	default:
> +		return CXD2880_RESULT_ERROR_ARG;
> +	}
> +
> +	data[5] = 0x00;
> +
> +	freq_khz += shift_frequency_khz;
> +
> +	data[6] = (u8)((freq_khz >> 16) & 0x0F);
> +	data[7] = (u8)((freq_khz >> 8) & 0xFF);
> +	data[8] = (u8)(freq_khz & 0xFF);

No need to cast.

> +	data[9] = 0xFF;
> +	data[10] = 0xFE;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x52, data,
> +				    11) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret x_tune2(struct cxd2880_tnrdmd *tnr_dmd,
> +				enum cxd2880_dtv_bandwidth bandwidth,
> +				enum cxd2880_tnrdmd_clockmode clk_mode,
> +				int shift_frequency_khz)
> +{
> +	u8 data[3] = { 0 };
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x11) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	data[0] = 0x01;
> +	data[1] = 0x0E;
> +	data[2] = 0x01;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x2D, data,
> +				    3) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x1A) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x29,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x2C, data,
> +				   1) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x60,
> +				   data[0]) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x62,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x11) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x2D,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x2F,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x10,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x21,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (shift_frequency_khz != 0) {
> +		int shift_freq = 0;
> +
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_DMD, 0x00,
> +					   0xE1) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +					   CXD2880_IO_TGT_DMD, 0x60, data,
> +					   2) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		shift_freq = shift_frequency_khz * 1000;
> +
> +		switch (clk_mode) {
> +		case CXD2880_TNRDMD_CLOCKMODE_A:
> +		case CXD2880_TNRDMD_CLOCKMODE_C:
> +		default:
> +			if (shift_freq >= 0)
> +				shift_freq = (shift_freq + 183 / 2) / 183;
> +			else
> +				shift_freq = (shift_freq - 183 / 2) / 183;
> +			break;
> +		case CXD2880_TNRDMD_CLOCKMODE_B:
> +			if (shift_freq >= 0)
> +				shift_freq = (shift_freq + 178 / 2) / 178;
> +			else
> +				shift_freq = (shift_freq - 178 / 2) / 178;
> +			break;
> +		}
> +
> +		shift_freq +=
> +		    cxd2880_convert2s_complement((data[0] << 8) | data[1], 16);
> +
> +		if (shift_freq > 32767)
> +			shift_freq = 32767;
> +		else if (shift_freq < -32768)
> +			shift_freq = -32768;
> +
> +		data[0] = (u8)(((u32)shift_freq >> 8) & 0xFF);
> +		data[1] = (u8)((u32)shift_freq & 0xFF);
> +
> +		if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +					    CXD2880_IO_TGT_DMD, 0x60, data,
> +					    2) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +					   CXD2880_IO_TGT_DMD, 0x69, data,
> +					   1) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		shift_freq = -shift_frequency_khz;
> +
> +		if (bandwidth == CXD2880_DTV_BW_1_7_MHZ) {
> +			switch (clk_mode) {
> +			case CXD2880_TNRDMD_CLOCKMODE_A:
> +			case CXD2880_TNRDMD_CLOCKMODE_C:
> +			default:
> +				if (shift_freq >= 0)
> +					shift_freq =
> +					    (shift_freq * 1000 +
> +					     17578 / 2) / 17578;
> +				else
> +					shift_freq =
> +					    (shift_freq * 1000 -
> +					     17578 / 2) / 17578;
> +				break;
> +			case CXD2880_TNRDMD_CLOCKMODE_B:
> +				if (shift_freq >= 0)
> +					shift_freq =
> +					    (shift_freq * 1000 +
> +					     17090 / 2) / 17090;
> +				else
> +					shift_freq =
> +					    (shift_freq * 1000 -
> +					     17090 / 2) / 17090;
> +				break;
> +			}
> +		} else {
> +			switch (clk_mode) {
> +			case CXD2880_TNRDMD_CLOCKMODE_A:
> +			case CXD2880_TNRDMD_CLOCKMODE_C:
> +			default:
> +				if (shift_freq >= 0)
> +					shift_freq =
> +					    (shift_freq * 1000 +
> +					     35156 / 2) / 35156;
> +				else
> +					shift_freq =
> +					    (shift_freq * 1000 -
> +					     35156 / 2) / 35156;
> +				break;
> +			case CXD2880_TNRDMD_CLOCKMODE_B:
> +				if (shift_freq >= 0)
> +					shift_freq =
> +					    (shift_freq * 1000 +
> +					     34180 / 2) / 34180;
> +				else
> +					shift_freq =
> +					    (shift_freq * 1000 -
> +					     34180 / 2) / 34180;
> +				break;
> +			}
> +		}
> +
> +		shift_freq += cxd2880_convert2s_complement(data[0], 8);
> +
> +		if (shift_freq > 127)
> +			shift_freq = 127;
> +		else if (shift_freq < -128)
> +			shift_freq = -128;
> +
> +		data[0] = (u8)((u32)shift_freq & 0xFF);
> +
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_DMD, 0x69,
> +					   data[0]) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	if (tnr_dmd->create_param.stationary_use) {
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_DMD, 0x00,
> +					   0xE1) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_DMD, 0x8A,
> +					   0x87) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x21,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret x_tune3(struct cxd2880_tnrdmd *tnr_dmd,
> +				enum cxd2880_dtv_sys sys,
> +				u8 en_fef_intmtnt_ctrl)
> +{
> +	u8 data[6] = { 0 };
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x21,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0xE2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x41,
> +				   0xA0) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x21,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0xFE,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if ((sys == CXD2880_DTV_SYS_DVBT2) && en_fef_intmtnt_ctrl) {
> +		data[0] = 0x01;
> +		data[1] = 0x01;
> +		data[2] = 0x01;
> +		data[3] = 0x01;
> +		data[4] = 0x01;
> +		data[5] = 0x01;
> +	} else {
> +		data[0] = 0x00;
> +		data[1] = 0x00;
> +		data[2] = 0x00;
> +		data[3] = 0x00;
> +		data[4] = 0x00;
> +		data[5] = 0x00;
> +	}
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0xEF, data,
> +				    6) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x2D) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if ((sys == CXD2880_DTV_SYS_DVBT2) && en_fef_intmtnt_ctrl)
> +		data[0] = 0x00;
> +	else
> +		data[0] = 0x01;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0xB1,
> +				   data[0]) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret x_tune4(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	u8 data[2] = { 0 };
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	{
> +		if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
> +						      CXD2880_IO_TGT_SYS, 0x00,
> +						      0x00) !=
> +		    CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		data[0] = 0x14;
> +		data[1] = 0x00;
> +		if (tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
> +						       CXD2880_IO_TGT_SYS, 0x55,
> +						       data,
> +						       2) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	{
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x00,
> +					   0x00) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		data[0] = 0x0B;
> +		data[1] = 0xFF;
> +		if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +					    CXD2880_IO_TGT_SYS, 0x53, data,
> +					    2) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x57,
> +					   0x01) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		data[0] = 0x0B;
> +		data[1] = 0xFF;
> +		if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +					    CXD2880_IO_TGT_SYS, 0x55, data,
> +					    2) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	{
> +		if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
> +						      CXD2880_IO_TGT_SYS, 0x00,
> +						      0x00) !=
> +		    CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		data[0] = 0x14;
> +		data[1] = 0x00;
> +		if (tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
> +						       CXD2880_IO_TGT_SYS, 0x53,
> +						       data,
> +						       2) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
> +						      CXD2880_IO_TGT_SYS, 0x57,
> +						      0x02) !=
> +		    CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0xFE,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
> +					      CXD2880_IO_TGT_DMD, 0x00,
> +					      0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
> +					      CXD2880_IO_TGT_DMD, 0xFE,
> +					      0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret x_sleep1(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	u8 data[3] = { 0 };
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	{
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x00,
> +					   0x00) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x57,
> +					   0x03) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		data[0] = 0x00;
> +		data[1] = 0x00;
> +		if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +					    CXD2880_IO_TGT_SYS, 0x53, data,
> +					    2) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	{
> +		if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
> +						      CXD2880_IO_TGT_SYS, 0x00,
> +						      0x00) !=
> +		    CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		data[0] = 0x1F;
> +		data[1] = 0xFF;
> +		data[2] = 0x03;
> +		if (tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
> +						       CXD2880_IO_TGT_SYS, 0x55,
> +						       data,
> +						       3) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		data[0] = 0x00;
> +		data[1] = 0x00;
> +		if (tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
> +						       CXD2880_IO_TGT_SYS, 0x53,
> +						       data,
> +						       2) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	{
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x00,
> +					   0x00) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		data[0] = 0x1F;
> +		data[1] = 0xFF;
> +		if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +					    CXD2880_IO_TGT_SYS, 0x55, data,
> +					    2) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret x_sleep2(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	u8 data = 0;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x2D) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0xB1,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	CXD2880_SLEEP(1);
> +
> +	if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0xB2, &data,
> +				   1) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if ((data & 0x01) == 0x00)
> +		return CXD2880_RESULT_ERROR_HW_STATE;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0xF4,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0xF3,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0xF2,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0xF1,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0xF0,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0xEF,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret x_sleep3(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0xFD,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret x_sleep4(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x21,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0xE2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x41,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x21,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret spll_reset(struct cxd2880_tnrdmd *tnr_dmd,
> +				   enum cxd2880_tnrdmd_clockmode clockmode)
> +{
> +	u8 data[4] = { 0 };
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x29,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x28,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x27,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x26,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x10,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x27,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x22,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	switch (clockmode) {
> +	case CXD2880_TNRDMD_CLOCKMODE_A:
> +		data[0] = 0x00;
> +		break;
> +
> +	case CXD2880_TNRDMD_CLOCKMODE_B:
> +		data[0] = 0x01;
> +		break;
> +
> +	case CXD2880_TNRDMD_CLOCKMODE_C:
> +		data[0] = 0x02;
> +		break;
> +
> +	default:
> +		return CXD2880_RESULT_ERROR_ARG;
> +	}
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x30,
> +				   data[0]) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x22,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	CXD2880_SLEEP(2);
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x0A) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x10, data,
> +				   1) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if ((data[0] & 0x01) == 0x00)
> +		return CXD2880_RESULT_ERROR_HW_STATE;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x27,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	CXD2880_SLEEP(1);
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x10,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x00;
> +	data[1] = 0x00;
> +	data[2] = 0x00;
> +	data[3] = 0x00;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x26, data,
> +				    4) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret t_power_x(struct cxd2880_tnrdmd *tnr_dmd, u8 on)
> +{
> +	u8 data[3] = { 0 };
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x29,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x28,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x27,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x10,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x27,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x25,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (on) {
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x2B,
> +					   0x01) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		CXD2880_SLEEP(1);
> +
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x00,
> +					   0x0A) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x12, data,
> +					   1) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		if ((data[0] & 0x01) == 0)
> +			return CXD2880_RESULT_ERROR_HW_STATE;
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x00,
> +					   0x00) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x2A,
> +					   0x00) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	} else {
> +		data[0] = 0x03;
> +		data[1] = 0x00;
> +		if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +					    CXD2880_IO_TGT_SYS, 0x2A, data,
> +					    2) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		CXD2880_SLEEP(1);
> +
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x00,
> +					   0x0A) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x13, data,
> +					   1) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +		if ((data[0] & 0x01) == 0)
> +			return CXD2880_RESULT_ERROR_HW_STATE;
> +	}
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x25,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	CXD2880_SLEEP(1);
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x0A) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x11, data,
> +				   1) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if ((data[0] & 0x01) == 0)
> +		return CXD2880_RESULT_ERROR_HW_STATE;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x27,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	CXD2880_SLEEP(1);
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x10,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	data[0] = 0x00;
> +	data[1] = 0x00;
> +	data[2] = 0x00;
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x27, data,
> +				    3) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +struct cxd2880_tnrdmd_ts_clk_cfg {
> +	u8 srl_clk_mode;
> +	u8 srl_duty_mode;
> +	u8 ts_clk_period;
> +};
> +
> +static enum cxd2880_ret set_ts_clk_mode_and_freq(struct cxd2880_tnrdmd *tnr_dmd,
> +						 enum cxd2880_dtv_sys sys)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	u8 backwards_compatible = 0;
> +	struct cxd2880_tnrdmd_ts_clk_cfg ts_clk_cfg;
> +
> +	const struct cxd2880_tnrdmd_ts_clk_cfg srl_ts_clk_stgs[2][2] = {
> +	{
> +		{3, 1, 8,},
> +		{0, 2, 16,}
> +	},
> +	{
> +		{1, 1, 8,},
> +		{2, 2, 16,}
> +	}
> +	};
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	{
> +		u8 ts_rate_ctrl_off = 0;
> +		u8 ts_in_off = 0;
> +		u8 ts_clk_manaul_on = 0;
> +
> +		if ((sys == CXD2880_DTV_SYS_ISDBT) ||
> +		    (sys == CXD2880_DTV_SYS_ISDBTSB) ||
> +		    (sys == CXD2880_DTV_SYS_ISDBTMM_A) ||
> +		    (sys == CXD2880_DTV_SYS_ISDBTMM_B)) {
> +			backwards_compatible = 0;
> +			ts_rate_ctrl_off = 1;
> +			ts_in_off = 0;
> +		} else if (tnr_dmd->is_ts_backwards_compatible_mode) {
> +			backwards_compatible = 1;
> +			ts_rate_ctrl_off = 1;
> +			ts_in_off = 1;
> +		} else {
> +			backwards_compatible = 0;
> +			ts_rate_ctrl_off = 0;
> +			ts_in_off = 0;
> +		}
> +
> +		if (tnr_dmd->ts_byte_clk_manual_setting) {
> +			ts_clk_manaul_on = 1;
> +			ts_rate_ctrl_off = 0;
> +		}
> +
> +		ret =
> +		    cxd2880_io_set_reg_bits(tnr_dmd->io, CXD2880_IO_TGT_DMD,
> +					    0xD3, ts_rate_ctrl_off, 0x01);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		ret =
> +		    cxd2880_io_set_reg_bits(tnr_dmd->io, CXD2880_IO_TGT_DMD,
> +					    0xDE, ts_in_off, 0x01);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		ret =
> +		    cxd2880_io_set_reg_bits(tnr_dmd->io, CXD2880_IO_TGT_DMD,
> +					    0xDA, ts_clk_manaul_on, 0x01);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	ts_clk_cfg =
> +	    srl_ts_clk_stgs[tnr_dmd->srl_ts_clk_mod_cnts]
> +			[(u8)tnr_dmd->srl_ts_clk_frq];
> +
> +	if (tnr_dmd->ts_byte_clk_manual_setting)
> +		ts_clk_cfg.ts_clk_period = tnr_dmd->ts_byte_clk_manual_setting;
> +
> +	ret =
> +	    cxd2880_io_set_reg_bits(tnr_dmd->io, CXD2880_IO_TGT_DMD, 0xC4,
> +				    ts_clk_cfg.srl_clk_mode, 0x03);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	ret =
> +	    cxd2880_io_set_reg_bits(tnr_dmd->io, CXD2880_IO_TGT_DMD, 0xD1,
> +				    ts_clk_cfg.srl_duty_mode, 0x03);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> +				     CXD2880_IO_TGT_DMD, 0xD9,
> +				     ts_clk_cfg.ts_clk_period);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	{
> +		u8 data = (u8)(backwards_compatible ? 0x00 : 0x01);
> +
> +		if (sys == CXD2880_DTV_SYS_DVBT) {
> +			if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +						   CXD2880_IO_TGT_DMD, 0x00,
> +						   0x10) != CXD2880_RESULT_OK)
> +				return CXD2880_RESULT_ERROR_IO;
> +
> +			ret =
> +			    cxd2880_io_set_reg_bits(tnr_dmd->io,
> +						    CXD2880_IO_TGT_DMD, 0x66,
> +						    data, 0x01);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static enum cxd2880_ret pid_ftr_setting(struct cxd2880_tnrdmd *tnr_dmd,
> +					struct cxd2880_tnrdmd_pid_ftr_cfg
> +					*pid_ftr_cfg)
> +{
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (!pid_ftr_cfg) {
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_DMD, 0x50,
> +					   0x02) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	} else {
> +		u8 data[65];
> +
> +		data[0] = (u8)(pid_ftr_cfg->is_negative ? 0x01 : 0x00);
> +		{
> +			int i = 0;
> +
> +			for (i = 0; i < 32; i++) {
> +				if (pid_ftr_cfg->pid_cfg[i].is_en) {
> +					data[1 + (i * 2)] =
> +					    (u8)((u8)
> +					    (pid_ftr_cfg->pid_cfg[i].pid
> +					     >> 8) | 0x20);
> +					data[2 + (i * 2)] =
> +					    (u8)(pid_ftr_cfg->pid_cfg[i].pid
> +					     & 0xFF);
> +				} else {
> +					data[1 + (i * 2)] = 0x00;
> +					data[2 + (i * 2)] = 0x00;
> +				}
> +			}
> +		}
> +		if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +					    CXD2880_IO_TGT_DMD, 0x50, data,
> +					    65) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret load_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	u8 i;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) {
> +		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> +					     tnr_dmd->cfg_mem[i].tgt,
> +					     0x00, tnr_dmd->cfg_mem[i].bank);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
> +					      tnr_dmd->cfg_mem[i].tgt,
> +					      tnr_dmd->cfg_mem[i].address,
> +					      tnr_dmd->cfg_mem[i].value,
> +					      tnr_dmd->cfg_mem[i].bit_mask);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static enum cxd2880_ret set_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd,
> +				    enum cxd2880_io_tgt tgt,
> +				    u8 bank, u8 address, u8 value, u8 bit_mask)
> +{
> +	u8 i;
> +	u8 value_stored = 0;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) {
> +		if ((value_stored == 0) &&
> +		    (tnr_dmd->cfg_mem[i].tgt == tgt) &&
> +		    (tnr_dmd->cfg_mem[i].bank == bank) &&
> +		    (tnr_dmd->cfg_mem[i].address == address)) {
> +			tnr_dmd->cfg_mem[i].value &= ~bit_mask;
> +			tnr_dmd->cfg_mem[i].value |= (value & bit_mask);
> +
> +			tnr_dmd->cfg_mem[i].bit_mask |= bit_mask;
> +
> +			value_stored = 1;
> +		}
> +	}
> +
> +	if (value_stored == 0) {
> +		if (tnr_dmd->cfg_mem_last_entry <
> +		    CXD2880_TNRDMD_MAX_CFG_MEM_COUNT) {
> +			tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].tgt = tgt;
> +			tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bank =
> +			    bank;
> +			tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].address =
> +			    address;
> +			tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].value =
> +			    (value & bit_mask);
> +			tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bit_mask =
> +			    bit_mask;
> +			tnr_dmd->cfg_mem_last_entry++;
> +		} else {
> +			return CXD2880_RESULT_ERROR_OVERFLOW;
> +		}
> +	}
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd,
> +				       struct cxd2880_io *io,
> +				       struct cxd2880_tnrdmd_create_param
> +				       *create_param)
> +{
> +	if ((!tnr_dmd) || (!io) || (!create_param))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	cxd2880_memset(tnr_dmd, 0, sizeof(struct cxd2880_tnrdmd));
> +
> +	tnr_dmd->io = io;
> +	tnr_dmd->create_param = *create_param;
> +
> +	tnr_dmd->diver_mode = CXD2880_TNRDMD_DIVERMODE_SINGLE;
> +	tnr_dmd->diver_sub = NULL;
> +
> +	tnr_dmd->srl_ts_clk_mod_cnts = 1;
> +	tnr_dmd->en_fef_intmtnt_base = 1;
> +	tnr_dmd->en_fef_intmtnt_lite = 1;
> +	tnr_dmd->rf_lvl_cmpstn = NULL;
> +	tnr_dmd->lna_thrs_tbl_air = NULL;
> +	tnr_dmd->lna_thrs_tbl_cable = NULL;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd
> +					     *tnr_dmd_main,
> +					     struct cxd2880_io *io_main,
> +					     struct cxd2880_tnrdmd *tnr_dmd_sub,
> +					     struct cxd2880_io *io_sub,
> +					     struct
> +					     cxd2880_tnrdmd_diver_create_param
> +					     *create_param)
> +{
> +	if ((!tnr_dmd_main) || (!io_main) || (!tnr_dmd_sub) || (!io_sub) ||
> +	    (!create_param))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	cxd2880_memset(tnr_dmd_main, 0, sizeof(struct cxd2880_tnrdmd));
> +	cxd2880_memset(tnr_dmd_sub, 0, sizeof(struct cxd2880_tnrdmd));

use memset().

> +
> +	tnr_dmd_main->io = io_main;
> +	tnr_dmd_main->diver_mode = CXD2880_TNRDMD_DIVERMODE_MAIN;
> +	tnr_dmd_main->diver_sub = tnr_dmd_sub;
> +	tnr_dmd_main->create_param.en_internal_ldo =
> +	    create_param->en_internal_ldo;
> +	tnr_dmd_main->create_param.ts_output_if = create_param->ts_output_if;
> +	tnr_dmd_main->create_param.xtal_share_type =
> +	    CXD2880_TNRDMD_XTAL_SHARE_MASTER;
> +	tnr_dmd_main->create_param.xosc_cap = create_param->xosc_cap_main;
> +	tnr_dmd_main->create_param.xosc_i = create_param->xosc_i_main;
> +	tnr_dmd_main->create_param.is_cxd2881gg = create_param->is_cxd2881gg;
> +	tnr_dmd_main->create_param.stationary_use =
> +	    create_param->stationary_use;
> +
> +	tnr_dmd_sub->io = io_sub;
> +	tnr_dmd_sub->diver_mode = CXD2880_TNRDMD_DIVERMODE_SUB;
> +	tnr_dmd_sub->diver_sub = NULL;
> +	tnr_dmd_sub->create_param.en_internal_ldo =
> +	    create_param->en_internal_ldo;
> +	tnr_dmd_sub->create_param.ts_output_if = create_param->ts_output_if;
> +	tnr_dmd_sub->create_param.xtal_share_type =
> +	    CXD2880_TNRDMD_XTAL_SHARE_SLAVE;
> +	tnr_dmd_sub->create_param.xosc_cap = 0;
> +	tnr_dmd_sub->create_param.xosc_i = create_param->xosc_i_sub;
> +	tnr_dmd_sub->create_param.is_cxd2881gg = create_param->is_cxd2881gg;
> +	tnr_dmd_sub->create_param.stationary_use = create_param->stationary_use;
> +
> +	tnr_dmd_main->srl_ts_clk_mod_cnts = 1;
> +	tnr_dmd_main->en_fef_intmtnt_base = 1;
> +	tnr_dmd_main->en_fef_intmtnt_lite = 1;
> +	tnr_dmd_main->rf_lvl_cmpstn = NULL;
> +	tnr_dmd_main->lna_thrs_tbl_air = NULL;
> +	tnr_dmd_main->lna_thrs_tbl_cable = NULL;
> +
> +	tnr_dmd_sub->srl_ts_clk_mod_cnts = 1;
> +	tnr_dmd_sub->en_fef_intmtnt_base = 1;
> +	tnr_dmd_sub->en_fef_intmtnt_lite = 1;
> +	tnr_dmd_sub->rf_lvl_cmpstn = NULL;
> +	tnr_dmd_sub->lna_thrs_tbl_air = NULL;
> +	tnr_dmd_sub->lna_thrs_tbl_cable = NULL;

Instead of this, it would likely be better if you create a const with the
default values above use, instead, memcpy().


> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if ((!tnr_dmd) || (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	tnr_dmd->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
> +	tnr_dmd->state = CXD2880_TNRDMD_STATE_UNKNOWN;
> +	tnr_dmd->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN;
> +	tnr_dmd->frequency_khz = 0;
> +	tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN;
> +	tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN;
> +	tnr_dmd->scan_mode = 0;
> +	cxd2880_atomic_set(&tnr_dmd->cancel, 0);
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +		tnr_dmd->diver_sub->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
> +		tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_UNKNOWN;
> +		tnr_dmd->diver_sub->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN;
> +		tnr_dmd->diver_sub->frequency_khz = 0;
> +		tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN;
> +		tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN;
> +		tnr_dmd->diver_sub->scan_mode = 0;
> +		cxd2880_atomic_set(&tnr_dmd->diver_sub->cancel, 0);
> +	}
> +
> +	ret = cxd2880_tnrdmd_chip_id(tnr_dmd, &tnr_dmd->chip_id);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->chip_id))
> +		return CXD2880_RESULT_ERROR_NOSUPPORT;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +		ret =
> +		    cxd2880_tnrdmd_chip_id(tnr_dmd->diver_sub,
> +					   &tnr_dmd->diver_sub->chip_id);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->diver_sub->chip_id))
> +			return CXD2880_RESULT_ERROR_NOSUPPORT;
> +	}
> +
> +	ret = p_init1(tnr_dmd);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +		ret = p_init1(tnr_dmd->diver_sub);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	CXD2880_SLEEP(1);
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +		ret = p_init2(tnr_dmd->diver_sub);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	ret = p_init2(tnr_dmd);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	CXD2880_SLEEP(5);
> +
> +	ret = p_init3(tnr_dmd);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +		ret = p_init3(tnr_dmd->diver_sub);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	ret = rf_init1(tnr_dmd);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +		ret = rf_init1(tnr_dmd->diver_sub);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	{
> +		u8 cpu_task_completed = 0;
> +
> +		ret =
> +		    cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
> +						     &cpu_task_completed);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		if (!cpu_task_completed)
> +			return CXD2880_RESULT_ERROR_HW_STATE;
> +	}
> +
> +	ret = rf_init2(tnr_dmd);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +		ret = rf_init2(tnr_dmd->diver_sub);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	ret = load_cfg_mem(tnr_dmd);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +		ret = load_cfg_mem(tnr_dmd->diver_sub);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP;
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd
> +							  *tnr_dmd,
> +							  u8 *task_completed)
> +{
> +	u16 cpu_status = 0;
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if ((!tnr_dmd) || (!task_completed))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	ret = cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd, &cpu_status);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
> +		if (cpu_status == 0)
> +			*task_completed = 1;
> +		else
> +			*task_completed = 0;
> +
> +		return ret;
> +	}
> +	if (cpu_status != 0) {
> +		*task_completed = 0;
> +		return ret;
> +	}
> +
> +	ret = cxd2880_tnrdmd_mon_internal_cpu_status_sub(tnr_dmd, &cpu_status);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	if (cpu_status == 0)
> +		*task_completed = 1;
> +	else
> +		*task_completed = 0;
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd
> +						     *tnr_dmd,
> +						     enum cxd2880_dtv_sys sys,
> +						     u32 frequency_khz,
> +						     enum cxd2880_dtv_bandwidth
> +						     bandwidth, u8 one_seg_opt,
> +						     u8 one_seg_opt_shft_dir)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> +	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (frequency_khz < 4000)
> +		return CXD2880_RESULT_ERROR_RANGE;
> +
> +	ret = cxd2880_tnrdmd_sleep(tnr_dmd);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	{
> +		u8 data = 0;
> +
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x00,
> +					   0x00) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x2B, &data,
> +					   1) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		switch (sys) {
> +		case CXD2880_DTV_SYS_DVBT:
> +		case CXD2880_DTV_SYS_ISDBT:
> +		case CXD2880_DTV_SYS_ISDBTSB:
> +		case CXD2880_DTV_SYS_ISDBTMM_A:
> +		case CXD2880_DTV_SYS_ISDBTMM_B:
> +			if (data == 0x00) {
> +				ret = t_power_x(tnr_dmd, 1);
> +				if (ret != CXD2880_RESULT_OK)
> +					return ret;
> +
> +				if (tnr_dmd->diver_mode ==
> +				    CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +					ret = t_power_x(tnr_dmd->diver_sub, 1);
> +					if (ret != CXD2880_RESULT_OK)
> +						return ret;
> +				}
> +			}
> +			break;
> +
> +		case CXD2880_DTV_SYS_DVBT2:
> +			if (data == 0x01) {
> +				ret = t_power_x(tnr_dmd, 0);
> +				if (ret != CXD2880_RESULT_OK)
> +					return ret;
> +
> +				if (tnr_dmd->diver_mode ==
> +				    CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +					ret = t_power_x(tnr_dmd->diver_sub, 0);
> +					if (ret != CXD2880_RESULT_OK)
> +						return ret;
> +				}
> +			}
> +			break;
> +
> +		default:
> +			return CXD2880_RESULT_ERROR_ARG;
> +		}
> +	}
> +
> +	{
> +		enum cxd2880_tnrdmd_clockmode new_clk_mode =
> +		    CXD2880_TNRDMD_CLOCKMODE_A;
> +
> +		ret = spll_reset(tnr_dmd, new_clk_mode);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		tnr_dmd->clk_mode = new_clk_mode;
> +
> +		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +			ret = spll_reset(tnr_dmd->diver_sub, new_clk_mode);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +
> +			tnr_dmd->diver_sub->clk_mode = new_clk_mode;
> +		}
> +
> +		ret = load_cfg_mem(tnr_dmd);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +			ret = load_cfg_mem(tnr_dmd->diver_sub);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +		}
> +	}
> +
> +	{
> +		int shift_frequency_khz = 0;
> +
> +		if (one_seg_opt) {
> +			if (tnr_dmd->diver_mode ==
> +			    CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +				shift_frequency_khz = 350;
> +			} else {
> +				if (one_seg_opt_shft_dir)
> +					shift_frequency_khz = 350;
> +				else
> +					shift_frequency_khz = -350;
> +
> +				if (tnr_dmd->create_param.xtal_share_type ==
> +				    CXD2880_TNRDMD_XTAL_SHARE_SLAVE)
> +					shift_frequency_khz *= -1;
> +			}
> +		} else {
> +			if (tnr_dmd->diver_mode ==
> +			    CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +				shift_frequency_khz = 150;
> +			} else {
> +				switch (tnr_dmd->create_param.xtal_share_type) {
> +				case CXD2880_TNRDMD_XTAL_SHARE_NONE:
> +				case CXD2880_TNRDMD_XTAL_SHARE_EXTREF:
> +				default:
> +					shift_frequency_khz = 0;
> +					break;
> +				case CXD2880_TNRDMD_XTAL_SHARE_MASTER:
> +					shift_frequency_khz = 150;
> +					break;
> +				case CXD2880_TNRDMD_XTAL_SHARE_SLAVE:
> +					shift_frequency_khz = -150;
> +					break;
> +				}
> +			}
> +		}
> +
> +		ret =
> +		    x_tune1(tnr_dmd, sys, frequency_khz, bandwidth,
> +			    tnr_dmd->is_cable_input, shift_frequency_khz);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +			ret =
> +			    x_tune1(tnr_dmd->diver_sub, sys, frequency_khz,
> +				    bandwidth, tnr_dmd->is_cable_input,
> +				    -shift_frequency_khz);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +		}
> +
> +		CXD2880_SLEEP(10);
> +
> +		{
> +			u8 cpu_task_completed = 0;
> +
> +			ret =
> +			    cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
> +						     &cpu_task_completed);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +
> +			if (!cpu_task_completed)
> +				return CXD2880_RESULT_ERROR_HW_STATE;
> +		}
> +
> +		ret =
> +		    x_tune2(tnr_dmd, bandwidth, tnr_dmd->clk_mode,
> +			    shift_frequency_khz);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +			ret =
> +			    x_tune2(tnr_dmd->diver_sub, bandwidth,
> +				    tnr_dmd->diver_sub->clk_mode,
> +				    -shift_frequency_khz);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +		}
> +	}
> +
> +	if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS) {
> +		ret = set_ts_clk_mode_and_freq(tnr_dmd, sys);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	} else {
> +		struct cxd2880_tnrdmd_pid_ftr_cfg *pid_ftr_cfg;
> +
> +		if (tnr_dmd->pid_ftr_cfg_en)
> +			pid_ftr_cfg = &tnr_dmd->pid_ftr_cfg;
> +		else
> +			pid_ftr_cfg = NULL;
> +
> +		ret = pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd
> +						     *tnr_dmd,
> +						     enum cxd2880_dtv_sys sys,
> +						     u8 en_fef_intmtnt_ctrl)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> +	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	ret = x_tune3(tnr_dmd, sys, en_fef_intmtnt_ctrl);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +		ret = x_tune3(tnr_dmd->diver_sub, sys, en_fef_intmtnt_ctrl);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +		ret = x_tune4(tnr_dmd);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	ret = cxd2880_tnrdmd_set_ts_output(tnr_dmd, 1);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->state == CXD2880_TNRDMD_STATE_SLEEP) {
> +	} else if (tnr_dmd->state == CXD2880_TNRDMD_STATE_ACTIVE) {
> +		ret = cxd2880_tnrdmd_set_ts_output(tnr_dmd, 0);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +			ret = x_sleep1(tnr_dmd);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +		}
> +
> +		ret = x_sleep2(tnr_dmd);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +			ret = x_sleep2(tnr_dmd->diver_sub);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +		}
> +
> +		switch (tnr_dmd->sys) {
> +		case CXD2880_DTV_SYS_DVBT:
> +			ret = cxd2880_tnrdmd_dvbt_sleep_setting(tnr_dmd);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +			break;
> +
> +		case CXD2880_DTV_SYS_DVBT2:
> +			ret = cxd2880_tnrdmd_dvbt2_sleep_setting(tnr_dmd);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +			break;
> +
> +		default:
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +		}
> +
> +		ret = x_sleep3(tnr_dmd);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +			ret = x_sleep3(tnr_dmd->diver_sub);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +		}
> +
> +		ret = x_sleep4(tnr_dmd);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +			ret = x_sleep4(tnr_dmd->diver_sub);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +		}
> +
> +		tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP;
> +		tnr_dmd->frequency_khz = 0;
> +		tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN;
> +		tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN;
> +
> +		if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +			tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP;
> +			tnr_dmd->diver_sub->frequency_khz = 0;
> +			tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN;
> +			tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN;
> +		}
> +	} else {
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +	}
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
> +					enum cxd2880_tnrdmd_cfg_id id,
> +					int value)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	u8 data[2] = { 0 };
> +	u8 need_sub_setting = 0;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> +	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	switch (id) {
> +	case CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0xC4,
> +							 (u8)(value ? 0x00 :
> +							       0x10), 0x10);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0xC5,
> +							 (u8)(value ? 0x00 :
> +							       0x02), 0x02);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0xC5,
> +							 (u8)(value ? 0x00 :
> +							       0x04), 0x04);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0xCB,
> +							 (u8)(value ? 0x00 :
> +							       0x01), 0x01);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0xC5,
> +							 (u8)(value ? 0x01 :
> +							       0x00), 0x01);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TSCLK_CONT:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		tnr_dmd->srl_ts_clk_mod_cnts = (u8)(value ? 0x01 : 0x00);
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TSCLK_MASK:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		if ((value < 0) || (value > 0x1F))
> +			return CXD2880_RESULT_ERROR_RANGE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0xC6, (u8)value,
> +							 0x1F);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TSVALID_MASK:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		if ((value < 0) || (value > 0x1F))
> +			return CXD2880_RESULT_ERROR_RANGE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0xC8, (u8)value,
> +							 0x1F);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TSERR_MASK:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		if ((value < 0) || (value > 0x1F))
> +			return CXD2880_RESULT_ERROR_RANGE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0xC9, (u8)value,
> +							 0x1F);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TSERR_VALID_DIS:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0x91,
> +							 (u8)(value ? 0x01 :
> +							       0x00), 0x01);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TSPIN_CURRENT:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_SYS,
> +							 0x00, 0x51, (u8)value,
> +							 0x3F);

no need for the cast. Same on similar calls.

> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_SYS,
> +							 0x00, 0x50,
> +							 (u8)(value ? 0x80 :
> +							       0x00), 0x80);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TSPIN_PULLUP:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_SYS,
> +							 0x00, 0x50, (u8)value,
> +							 0x3F);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TSCLK_FREQ:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		if ((value < 0) || (value > 1))
> +			return CXD2880_RESULT_ERROR_RANGE;
> +
> +		tnr_dmd->srl_ts_clk_frq =
> +		    (enum cxd2880_tnrdmd_serial_ts_clk)value;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		if ((value < 0) || (value > 0xFF))
> +			return CXD2880_RESULT_ERROR_RANGE;
> +
> +		tnr_dmd->ts_byte_clk_manual_setting = (u8)value;
> +
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TS_PACKET_GAP:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		if ((value < 0) || (value > 7))
> +			return CXD2880_RESULT_ERROR_RANGE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0xD6, (u8)value,
> +							 0x07);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE:
> +		if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +			return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +		tnr_dmd->is_ts_backwards_compatible_mode = (u8)(value ? 1 : 0);
> +
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_PWM_VALUE:
> +		if ((value < 0) || (value > 0x1000))
> +			return CXD2880_RESULT_ERROR_RANGE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0x22,
> +							 (u8)(value ? 0x01 :
> +							       0x00), 0x01);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		{
> +			u8 data[2];
> +
> +			data[0] = (u8)(((u16)value >> 8) & 0x1F);
> +			data[1] = (u8)((u16)value & 0xFF);

I don't think we need all those casts.
> +
> +			ret =
> +			    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0x23,
> +							 data[0], 0x1F);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +
> +			ret =
> +			    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0x24,
> +							 data[1], 0xFF);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +		}
> +
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_INTERRUPT:
> +		data[0] = (u8)((value >> 8) & 0xFF);
> +		data[1] = (u8)(value & 0xFF);

no uneeded casts, please.

> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_SYS,
> +							 0x00, 0x48, data[0],
> +							 0xFF);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_SYS,
> +							 0x00, 0x49, data[1],
> +							 0xFF);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL:
> +		data[0] = (u8)(value & 0x07);
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_SYS,
> +							 0x00, 0x4A, data[0],
> +							 0x07);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL:
> +		data[0] = (u8)((value & 0x07) << 3);
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_SYS,
> +							 0x00, 0x4A, data[0],
> +							 0x38);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE:
> +		if ((value < (int)CXD2880_TNRDMD_CLOCKMODE_UNKNOWN) ||
> +		    (value > (int)CXD2880_TNRDMD_CLOCKMODE_C))

no unneeded casts (same for all other casts you might be doing).

> +			return CXD2880_RESULT_ERROR_RANGE;
> +		tnr_dmd->fixed_clk_mode = (enum cxd2880_tnrdmd_clockmode)value;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_CABLE_INPUT:
> +		tnr_dmd->is_cable_input = (u8)(value ? 1 : 0);
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE:
> +		tnr_dmd->en_fef_intmtnt_base = (u8)(value ? 1 : 0);
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE:
> +		tnr_dmd->en_fef_intmtnt_lite = (u8)(value ? 1 : 0);
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS:
> +		data[0] = (u8)((value >> 8) & 0x07);
> +		data[1] = (u8)(value & 0xFF);
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0x99, data[0],
> +							 0x07);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0x9A, data[1],
> +							 0xFF);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS:
> +		data[0] = (u8)((value >> 8) & 0x07);
> +		data[1] = (u8)(value & 0xFF);
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0x9B, data[0],
> +							 0x07);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0x9C, data[1],
> +							 0xFF);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS:
> +		data[0] = (u8)((value >> 8) & 0x07);
> +		data[1] = (u8)(value & 0xFF);
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0x9D, data[0],
> +							 0x07);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x00, 0x9E, data[1],
> +							 0xFF);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST:
> +		tnr_dmd->blind_tune_dvbt2_first = (u8)(value ? 1 : 0);
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD:
> +		if ((value < 0) || (value > 31))
> +			return CXD2880_RESULT_ERROR_RANGE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x10, 0x60,
> +							 (u8)(value & 0x1F),
> +							 0x1F);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD:
> +		if ((value < 0) || (value > 7))
> +			return CXD2880_RESULT_ERROR_RANGE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x10, 0x6F,
> +							 (u8)(value & 0x07),
> +							 0x07);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_DVBT2_BBER_MES:
> +		if ((value < 0) || (value > 15))
> +			return CXD2880_RESULT_ERROR_RANGE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x20, 0x72,
> +							 (u8)(value & 0x0F),
> +							 0x0F);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_DVBT2_LBER_MES:
> +		if ((value < 0) || (value > 15))
> +			return CXD2880_RESULT_ERROR_RANGE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x20, 0x6F,
> +							 (u8)(value & 0x0F),
> +							 0x0F);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_DVBT_PER_MES:
> +		if ((value < 0) || (value > 15))
> +			return CXD2880_RESULT_ERROR_RANGE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x10, 0x5C,
> +							 (u8)(value & 0x0F),
> +							 0x0F);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_DVBT2_PER_MES:
> +		if ((value < 0) || (value > 15))
> +			return CXD2880_RESULT_ERROR_RANGE;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x24, 0xDC,
> +							 (u8)(value & 0x0F),
> +							 0x0F);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +		break;
> +
> +	case CXD2880_TNRDMD_CFG_ISDBT_BERPER_PERIOD:
> +		{
> +			u8 data[2];
> +
> +			data[0] = (u8)((value & 0x00007F00) >> 8);
> +			data[1] = (u8)(value & 0x000000FF);
> +
> +			ret =
> +			    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x60, 0x5B,
> +							 data[0], 0x7F);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +			ret =
> +			    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
> +							 CXD2880_IO_TGT_DMD,
> +							 0x60, 0x5C,
> +							 data[1], 0xFF);
> +			if (ret != CXD2880_RESULT_OK)
> +				return ret;
> +		}
> +		break;
> +
> +	default:
> +		return CXD2880_RESULT_ERROR_ARG;
> +	}
> +
> +	if (need_sub_setting &&
> +	    (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)) {
> +		ret = cxd2880_tnrdmd_set_cfg(tnr_dmd->diver_sub, id, value);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
> +					     u8 id,
> +					     u8 en,
> +					     enum cxd2880_tnrdmd_gpio_mode mode,
> +					     u8 open_drain, u8 invert)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (id > 2)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (mode > CXD2880_TNRDMD_GPIO_MODE_EEW)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> +	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	ret =
> +	    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
> +						 0x00, 0x40 + id, (u8)mode,
> +						 0x0F);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	ret =
> +	    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
> +						 0x00, 0x43,
> +						 (u8)(open_drain ? (1 << id) :
> +						       0), (u8)(1 << id));
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	ret =
> +	    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
> +						 0x00, 0x44,
> +						 (u8)(invert ? (1 << id) : 0),
> +						 (u8)(1 << id));
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	ret =
> +	    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
> +						 0x00, 0x45,
> +						 (u8)(en ? 0 : (1 << id)),
> +						 (u8)(1 << id));
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd,
> +						 u8 id,
> +						 u8 en,
> +						 enum cxd2880_tnrdmd_gpio_mode
> +						 mode, u8 open_drain, u8 invert)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	ret =
> +	    cxd2880_tnrdmd_gpio_set_cfg(tnr_dmd->diver_sub, id, en, mode,
> +					open_drain, invert);
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd,
> +					  u8 id, u8 *value)
> +{
> +	u8 data = 0;
> +
> +	if ((!tnr_dmd) || (!value))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (id > 2)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> +	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x0A) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x20, &data,
> +				   1) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	*value = (u8)((data >> id) & 0x01);
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd,
> +					      u8 id, u8 *value)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	ret = cxd2880_tnrdmd_gpio_read(tnr_dmd->diver_sub, id, value);
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd,
> +					   u8 id, u8 value)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (id > 2)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> +	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	ret =
> +	    cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
> +						 0x00, 0x46,
> +						 (u8)(value ? (1 << id) : 0),
> +						 (u8)(1 << id));
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd,
> +					       u8 id, u8 value)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	ret = cxd2880_tnrdmd_gpio_write(tnr_dmd->diver_sub, id, value);
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd,
> +					       u16 *value)
> +{
> +	u8 data[2] = { 0 };
> +
> +	if ((!tnr_dmd) || (!value))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> +	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x0A) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x15, data,
> +				   2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	*value = (u16)(((u16)data[0] << 8) | (data[1]));
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd,
> +						u16 value)
> +{
> +	u8 data[2] = { 0 };
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> +	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	data[0] = (u8)((value >> 8) & 0xFF);
> +	data[1] = (u8)(value & 0xFF);
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_SYS, 0x3C, data,
> +				    2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd,
> +					     u8 clear_overflow_flag,
> +					     u8 clear_underflow_flag,
> +					     u8 clear_buf)
> +{
> +	u8 data[2] = { 0 };
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> +	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	data[0] = (u8)(clear_overflow_flag ? 0x02 : 0x00);
> +	data[0] |= (u8)(clear_underflow_flag ? 0x01 : 0x00);
> +	data[1] = (u8)(clear_buf ? 0x01 : 0x00);
> +	if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +				    CXD2880_IO_TGT_DMD, 0x9F, data,
> +				    2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd,
> +					enum cxd2880_tnrdmd_chip_id *chip_id)
> +{
> +	u8 data = 0;
> +
> +	if ((!tnr_dmd) || (!chip_id))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0xFD, &data,
> +				   1) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	*chip_id = (enum cxd2880_tnrdmd_chip_id)data;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd
> +						      *tnr_dmd,
> +						      enum cxd2880_io_tgt tgt,
> +						      u8 bank, u8 address,
> +						      u8 value, u8 bit_mask)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> +	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   tgt, 0x00, bank) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (cxd2880_io_set_reg_bits(tnr_dmd->io, tgt, address, value, bit_mask)
> +	    != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	ret = set_cfg_mem(tnr_dmd, tgt, bank, address, value, bit_mask);
> +	if (ret != CXD2880_RESULT_OK)
> +		return ret;
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd,
> +					      enum cxd2880_dtv_sys sys,
> +					      u8 scan_mode_end)
> +{
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> +	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	CXD2880_ARG_UNUSED(sys);
> +
> +	tnr_dmd->scan_mode = scan_mode_end;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
> +		enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +		ret =
> +		    cxd2880_tnrdmd_set_scan_mode(tnr_dmd->diver_sub, sys,
> +						 scan_mode_end);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd,
> +					    struct cxd2880_tnrdmd_pid_ftr_cfg
> +					    *pid_ftr_cfg)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> +	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS)
> +		return CXD2880_RESULT_ERROR_NOSUPPORT;
> +
> +	if (pid_ftr_cfg) {
> +		tnr_dmd->pid_ftr_cfg = *pid_ftr_cfg;
> +		tnr_dmd->pid_ftr_cfg_en = 1;
> +	} else {
> +		tnr_dmd->pid_ftr_cfg_en = 0;
> +	}
> +
> +	if (tnr_dmd->state == CXD2880_TNRDMD_STATE_ACTIVE) {
> +		ret = pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd
> +					  *tnr_dmd,
> +					  enum
> +					  cxd2880_ret(*rf_lvl_cmpstn)
> +					  (struct cxd2880_tnrdmd *,
> +					   int *))
> +{
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	tnr_dmd->rf_lvl_cmpstn = rf_lvl_cmpstn;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd
> +						      *tnr_dmd,
> +						      enum
> +						      cxd2880_ret
> +						      (*rf_lvl_cmpstn)(struct
> +								cxd2880_tnrdmd
> +								*,
> +								int *))
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	ret =
> +	    cxd2880_tnrdmd_set_rf_lvl_cmpstn(tnr_dmd->diver_sub, rf_lvl_cmpstn);
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd,
> +					     struct
> +					     cxd2880_tnrdmd_lna_thrs_tbl_air
> +					     *tbl_air,
> +					     struct
> +					     cxd2880_tnrdmd_lna_thrs_tbl_cable
> +					     *tbl_cable)
> +{
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	tnr_dmd->lna_thrs_tbl_air = tbl_air;
> +	tnr_dmd->lna_thrs_tbl_cable = tbl_cable;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd,
> +					 struct
> +					 cxd2880_tnrdmd_lna_thrs_tbl_air
> +					 *tbl_air,
> +					 struct
> +					 cxd2880_tnrdmd_lna_thrs_tbl_cable
> +					 *tbl_cable)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	ret =
> +	    cxd2880_tnrdmd_set_lna_thrs(tnr_dmd->diver_sub, tbl_air, tbl_cable);
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd
> +						    *tnr_dmd, u8 en, u8 value)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnr_dmd->create_param.ts_output_if != CXD2880_TNRDMD_TSOUT_IF_TS)
> +		return CXD2880_RESULT_ERROR_NOSUPPORT;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (en) {
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x50,
> +					   ((value & 0x1F) | 0x80)) !=
> +		    CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x52,
> +					   (value & 0x1F)) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	} else {
> +		ret = tnr_dmd->io->write_reg(tnr_dmd->io,
> +					     CXD2880_IO_TGT_SYS, 0x50, 0x3F);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +
> +		if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x52,
> +					   0x1F) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		ret = load_cfg_mem(tnr_dmd);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd,
> +					      u8 en)
> +{
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> +	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	switch (tnr_dmd->create_param.ts_output_if) {
> +	case CXD2880_TNRDMD_TSOUT_IF_TS:
> +		if (en) {
> +			if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +						   CXD2880_IO_TGT_SYS, 0x00,
> +						   0x00) != CXD2880_RESULT_OK)
> +				return CXD2880_RESULT_ERROR_IO;
> +			if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +						   CXD2880_IO_TGT_SYS, 0x52,
> +						   0x00) != CXD2880_RESULT_OK)
> +				return CXD2880_RESULT_ERROR_IO;
> +			if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +						   CXD2880_IO_TGT_DMD, 0x00,
> +						   0x00) != CXD2880_RESULT_OK)
> +				return CXD2880_RESULT_ERROR_IO;
> +			if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +						   CXD2880_IO_TGT_DMD, 0xC3,
> +						   0x00) != CXD2880_RESULT_OK)
> +				return CXD2880_RESULT_ERROR_IO;
> +		} else {
> +			if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +						   CXD2880_IO_TGT_DMD, 0x00,
> +						   0x00) != CXD2880_RESULT_OK)
> +				return CXD2880_RESULT_ERROR_IO;
> +			if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +						   CXD2880_IO_TGT_DMD, 0xC3,
> +						   0x01) != CXD2880_RESULT_OK)
> +				return CXD2880_RESULT_ERROR_IO;
> +			if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +						   CXD2880_IO_TGT_SYS, 0x00,
> +						   0x00) != CXD2880_RESULT_OK)
> +				return CXD2880_RESULT_ERROR_IO;
> +			if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +						   CXD2880_IO_TGT_SYS, 0x52,
> +						   0x1F) != CXD2880_RESULT_OK)
> +				return CXD2880_RESULT_ERROR_IO;
> +		}
> +		break;
> +
> +	case CXD2880_TNRDMD_TSOUT_IF_SPI:
> +		break;
> +
> +	case CXD2880_TNRDMD_TSOUT_IF_SDIO:
> +		break;
> +
> +	default:
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +	}
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if (!tnr_dmd)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	switch (tnr_dmd->create_param.ts_output_if) {
> +	case CXD2880_TNRDMD_TSOUT_IF_SPI:
> +	case CXD2880_TNRDMD_TSOUT_IF_SDIO:
> +		{
> +			u8 data = 0;
> +
> +			if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +						   CXD2880_IO_TGT_DMD, 0x00,
> +						   &data,
> +						   1) != CXD2880_RESULT_OK)
> +				return CXD2880_RESULT_ERROR_IO;
> +		}
> +		break;
> +	case CXD2880_TNRDMD_TSOUT_IF_TS:
> +	default:
> +		break;
> +	}
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x01,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	return ret;
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
> new file mode 100644
> index 000000000000..26e29b3b9f6b
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
> @@ -0,0 +1,395 @@
> +/*
> + * cxd2880_tnrdmd.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * common control interface
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_TNRDMD_H
> +#define CXD2880_TNRDMD_H
> +
> +#include "cxd2880_common.h"
> +#include "cxd2880_io.h"
> +#include "cxd2880_dtv.h"
> +#include "cxd2880_dvbt.h"
> +#include "cxd2880_dvbt2.h"
> +
> +#define CXD2880_TNRDMD_MAX_CFG_MEM_COUNT 100
> +
> +#define slvt_unfreeze_reg(tnr_dmd) ((void)((tnr_dmd)->io->write_reg\
> +((tnr_dmd)->io, CXD2880_IO_TGT_DMD, 0x01, 0x00)))
> +
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_UNDERFLOW     0x0001
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_OVERFLOW      0x0002
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_EMPTY  0x0004
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_FULL   0x0008
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_RRDY	  0x0010
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_COMMAND      0x0020
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_ACCESS       0x0040
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_CPU_ERROR	    0x0100
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_LOCK		 0x0200
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_INV_LOCK	     0x0400
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_NOOFDM	       0x0800
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_EWS		  0x1000
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_EEW		  0x2000
> +#define CXD2880_TNRDMD_INTERRUPT_TYPE_FEC_FAIL	     0x4000
> +
> +#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_L1POST_OK	0x01
> +#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_DMD_LOCK	 0x02
> +#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_TS_LOCK	  0x04
> +
> +enum cxd2880_tnrdmd_chip_id {
> +	CXD2880_TNRDMD_CHIP_ID_UNKNOWN = 0x00,
> +	CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X = 0x62,
> +	CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11 = 0x6A
> +};
> +
> +#define CXD2880_TNRDMD_CHIP_ID_VALID(chip_id) (((chip_id) == \
> +CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) || \
> +((chip_id) == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11))
> +
> +enum cxd2880_tnrdmd_state {
> +	CXD2880_TNRDMD_STATE_UNKNOWN,
> +	CXD2880_TNRDMD_STATE_SLEEP,
> +	CXD2880_TNRDMD_STATE_ACTIVE,
> +	CXD2880_TNRDMD_STATE_INVALID
> +};
> +
> +enum cxd2880_tnrdmd_divermode {
> +	CXD2880_TNRDMD_DIVERMODE_SINGLE,
> +	CXD2880_TNRDMD_DIVERMODE_MAIN,
> +	CXD2880_TNRDMD_DIVERMODE_SUB
> +};
> +
> +enum cxd2880_tnrdmd_clockmode {
> +	CXD2880_TNRDMD_CLOCKMODE_UNKNOWN,
> +	CXD2880_TNRDMD_CLOCKMODE_A,
> +	CXD2880_TNRDMD_CLOCKMODE_B,
> +	CXD2880_TNRDMD_CLOCKMODE_C
> +};
> +
> +enum cxd2880_tnrdmd_tsout_if {
> +	CXD2880_TNRDMD_TSOUT_IF_TS,
> +	CXD2880_TNRDMD_TSOUT_IF_SPI,
> +	CXD2880_TNRDMD_TSOUT_IF_SDIO
> +};
> +
> +enum cxd2880_tnrdmd_xtal_share {
> +	CXD2880_TNRDMD_XTAL_SHARE_NONE,
> +	CXD2880_TNRDMD_XTAL_SHARE_EXTREF,
> +	CXD2880_TNRDMD_XTAL_SHARE_MASTER,
> +	CXD2880_TNRDMD_XTAL_SHARE_SLAVE
> +};
> +
> +enum cxd2880_tnrdmd_spectrum_sense {
> +	CXD2880_TNRDMD_SPECTRUM_NORMAL,
> +	CXD2880_TNRDMD_SPECTRUM_INV
> +};
> +
> +enum cxd2880_tnrdmd_cfg_id {
> +	CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB,
> +	CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI,
> +	CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI,
> +	CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI,
> +	CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE,
> +	CXD2880_TNRDMD_CFG_TSCLK_CONT,
> +	CXD2880_TNRDMD_CFG_TSCLK_MASK,
> +	CXD2880_TNRDMD_CFG_TSVALID_MASK,
> +	CXD2880_TNRDMD_CFG_TSERR_MASK,
> +	CXD2880_TNRDMD_CFG_TSERR_VALID_DIS,
> +	CXD2880_TNRDMD_CFG_TSPIN_CURRENT,
> +	CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL,
> +	CXD2880_TNRDMD_CFG_TSPIN_PULLUP,
> +	CXD2880_TNRDMD_CFG_TSCLK_FREQ,
> +	CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL,
> +	CXD2880_TNRDMD_CFG_TS_PACKET_GAP,
> +	CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE,
> +	CXD2880_TNRDMD_CFG_PWM_VALUE,
> +	CXD2880_TNRDMD_CFG_INTERRUPT,
> +	CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL,
> +	CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL,
> +	CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS,
> +	CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS,
> +	CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS,
> +	CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE,
> +	CXD2880_TNRDMD_CFG_CABLE_INPUT,
> +	CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE,
> +	CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE,
> +	CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST,
> +	CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD,
> +	CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD,
> +	CXD2880_TNRDMD_CFG_DVBT_PER_MES,
> +	CXD2880_TNRDMD_CFG_DVBT2_BBER_MES,
> +	CXD2880_TNRDMD_CFG_DVBT2_LBER_MES,
> +	CXD2880_TNRDMD_CFG_DVBT2_PER_MES,
> +	CXD2880_TNRDMD_CFG_ISDBT_BERPER_PERIOD
> +};
> +
> +enum cxd2880_tnrdmd_lock_result {
> +	CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT,
> +	CXD2880_TNRDMD_LOCK_RESULT_LOCKED,
> +	CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED
> +};
> +
> +enum cxd2880_tnrdmd_gpio_mode {
> +	CXD2880_TNRDMD_GPIO_MODE_OUTPUT = 0x00,
> +	CXD2880_TNRDMD_GPIO_MODE_INPUT = 0x01,
> +	CXD2880_TNRDMD_GPIO_MODE_INT = 0x02,
> +	CXD2880_TNRDMD_GPIO_MODE_FEC_FAIL = 0x03,
> +	CXD2880_TNRDMD_GPIO_MODE_PWM = 0x04,
> +	CXD2880_TNRDMD_GPIO_MODE_EWS = 0x05,
> +	CXD2880_TNRDMD_GPIO_MODE_EEW = 0x06
> +};
> +
> +enum cxd2880_tnrdmd_serial_ts_clk {
> +	CXD2880_TNRDMD_SERIAL_TS_CLK_FULL,
> +	CXD2880_TNRDMD_SERIAL_TS_CLK_HALF
> +};
> +
> +struct cxd2880_tnrdmd_cfg_mem {
> +	enum cxd2880_io_tgt tgt;
> +	u8 bank;
> +	u8 address;
> +	u8 value;
> +	u8 bit_mask;
> +};
> +
> +struct cxd2880_tnrdmd_pid_cfg {
> +	u8 is_en;
> +	u16 pid;
> +};
> +
> +struct cxd2880_tnrdmd_pid_ftr_cfg {
> +	u8 is_negative;
> +	struct cxd2880_tnrdmd_pid_cfg pid_cfg[32];
> +};
> +
> +struct cxd2880_tnrdmd_ts_buf_info {
> +	u8 read_ready;
> +	u8 almost_full;
> +	u8 almost_empty;
> +	u8 overflow;
> +	u8 underflow;
> +	u16 packet_num;
> +};
> +
> +struct cxd2880_tnrdmd_lna_thrs {
> +	u8 off_on;
> +	u8 on_off;
> +};
> +
> +struct cxd2880_tnrdmd_lna_thrs_tbl_air {
> +	struct cxd2880_tnrdmd_lna_thrs thrs[24];
> +};
> +
> +struct cxd2880_tnrdmd_lna_thrs_tbl_cable {
> +	struct cxd2880_tnrdmd_lna_thrs thrs[32];
> +};
> +
> +struct cxd2880_tnrdmd_create_param {
> +	enum cxd2880_tnrdmd_tsout_if ts_output_if;
> +	u8 en_internal_ldo;
> +	enum cxd2880_tnrdmd_xtal_share xtal_share_type;
> +	u8 xosc_cap;
> +	u8 xosc_i;
> +	u8 is_cxd2881gg;
> +	u8 stationary_use;
> +};
> +
> +struct cxd2880_tnrdmd_diver_create_param {
> +	enum cxd2880_tnrdmd_tsout_if ts_output_if;
> +	u8 en_internal_ldo;
> +	u8 xosc_cap_main;
> +	u8 xosc_i_main;
> +	u8 xosc_i_sub;
> +	u8 is_cxd2881gg;
> +	u8 stationary_use;
> +};
> +
> +struct cxd2880_tnrdmd {
> +	struct cxd2880_tnrdmd *diver_sub;
> +	struct cxd2880_io *io;
> +	struct cxd2880_tnrdmd_create_param create_param;
> +	enum cxd2880_tnrdmd_divermode diver_mode;
> +	enum cxd2880_tnrdmd_clockmode fixed_clk_mode;
> +	u8 is_cable_input;
> +	u8 en_fef_intmtnt_base;
> +	u8 en_fef_intmtnt_lite;
> +	u8 blind_tune_dvbt2_first;
> +	enum cxd2880_ret (*rf_lvl_cmpstn)(struct cxd2880_tnrdmd *tnr_dmd,
> +					   int *rf_lvl_db);
> +	struct cxd2880_tnrdmd_lna_thrs_tbl_air *lna_thrs_tbl_air;
> +	struct cxd2880_tnrdmd_lna_thrs_tbl_cable *lna_thrs_tbl_cable;
> +	u8 srl_ts_clk_mod_cnts;
> +	enum cxd2880_tnrdmd_serial_ts_clk srl_ts_clk_frq;
> +	u8 ts_byte_clk_manual_setting;
> +	u8 is_ts_backwards_compatible_mode;
> +	struct cxd2880_tnrdmd_cfg_mem cfg_mem[CXD2880_TNRDMD_MAX_CFG_MEM_COUNT];
> +	u8 cfg_mem_last_entry;
> +	struct cxd2880_tnrdmd_pid_ftr_cfg pid_ftr_cfg;
> +	u8 pid_ftr_cfg_en;
> +	void *user;
> +	enum cxd2880_tnrdmd_chip_id chip_id;
> +	enum cxd2880_tnrdmd_state state;
> +	enum cxd2880_tnrdmd_clockmode clk_mode;
> +	u32 frequency_khz;
> +	enum cxd2880_dtv_sys sys;
> +	enum cxd2880_dtv_bandwidth bandwidth;
> +	u8 scan_mode;
> +	struct cxd2880_atomic cancel;
> +};
> +
> +enum cxd2880_ret cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd,
> +				       struct cxd2880_io *io,
> +				       struct cxd2880_tnrdmd_create_param
> +				       *create_param);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd
> +					     *tnr_dmd_main,
> +					     struct cxd2880_io *io_main,
> +					     struct cxd2880_tnrdmd *tnr_dmd_sub,
> +					     struct cxd2880_io *io_sub,
> +					     struct
> +					     cxd2880_tnrdmd_diver_create_param
> +					     *create_param);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd
> +							  *tnr_dmd,
> +							  u8 *task_completed);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd
> +						     *tnr_dmd,
> +						     enum cxd2880_dtv_sys sys,
> +						     u32 frequency_khz,
> +						     enum cxd2880_dtv_bandwidth
> +						     bandwidth, u8 one_seg_opt,
> +						     u8 one_seg_opt_shft_dir);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd
> +						     *tnr_dmd,
> +						     enum cxd2880_dtv_sys sys,
> +						     u8 en_fef_intmtnt_ctrl);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
> +					enum cxd2880_tnrdmd_cfg_id id,
> +					int value);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
> +					     u8 id,
> +					     u8 en,
> +					     enum cxd2880_tnrdmd_gpio_mode mode,
> +					     u8 open_drain, u8 invert);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd,
> +						 u8 id,
> +						 u8 en,
> +						 enum cxd2880_tnrdmd_gpio_mode
> +						 mode, u8 open_drain,
> +						 u8 invert);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd,
> +					  u8 id, u8 *value);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd,
> +					      u8 id, u8 *value);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd,
> +					   u8 id, u8 value);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd,
> +					       u8 id, u8 value);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd,
> +					       u16 *value);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd,
> +						u16 value);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd,
> +					     u8 clear_overflow_flag,
> +					     u8 clear_underflow_flag,
> +					     u8 clear_buf);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd,
> +					enum cxd2880_tnrdmd_chip_id *chip_id);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd
> +						      *tnr_dmd,
> +						      enum cxd2880_io_tgt tgt,
> +						      u8 bank, u8 address,
> +						      u8 value, u8 bit_mask);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd,
> +					      enum cxd2880_dtv_sys sys,
> +					      u8 scan_mode_end);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd,
> +					    struct cxd2880_tnrdmd_pid_ftr_cfg
> +					    *pid_ftr_cfg);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd
> +						  *tnr_dmd,
> +						  enum
> +						  cxd2880_ret(*rf_lvl_cmpstn)
> +						  (struct cxd2880_tnrdmd *,
> +						   int *));
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd
> +						      *tnr_dmd,
> +						      enum
> +						      cxd2880_ret
> +						      (*rf_lvl_cmpstn)(struct
> +								cxd2880_tnrdmd
> +								*,
> +								int *));
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd,
> +					     struct
> +					     cxd2880_tnrdmd_lna_thrs_tbl_air
> +					     *tbl_air,
> +					     struct
> +					     cxd2880_tnrdmd_lna_thrs_tbl_cable
> +					     *tbl_cable);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd,
> +					 struct
> +					 cxd2880_tnrdmd_lna_thrs_tbl_air
> +					 *tbl_air,
> +					 struct
> +					 cxd2880_tnrdmd_lna_thrs_tbl_cable
> +					 *tbl_cable);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd
> +						    *tnr_dmd, u8 en, u8 value);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd,
> +					      u8 en);
> +
> +enum cxd2880_ret slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd);
> +
> +#endif
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
> new file mode 100644
> index 000000000000..68fb3af04bd4
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
> @@ -0,0 +1,29 @@
> +/*
> + * cxd2880_tnrdmd_driver_version.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * version information
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#define CXD2880_TNRDMD_DRIVER_VERSION "1.4.1 - 1.0.1"
> +
> +#define CXD2880_TNRDMD_DRIVER_RELEASE_DATE "2017-04-13"
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
> new file mode 100644
> index 000000000000..0ac5b9bf3be8
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
> @@ -0,0 +1,207 @@
> +/*
> + * cxd2880_tnrdmd_mon.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * common monitor functions
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "cxd2880_common.h"
> +#include "cxd2880_tnrdmd_mon.h"
> +
> +enum cxd2880_ret cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd,
> +					   int *rf_lvl_db)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if ((!tnr_dmd) || (!rf_lvl_db))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x10,
> +				   0x01) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	{
> +		u8 data[2] = { 0x80, 0x00 };
> +
> +		if (tnr_dmd->io->write_regs(tnr_dmd->io,
> +					    CXD2880_IO_TGT_SYS, 0x5B, data,
> +					    2) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	CXD2880_SLEEP_IN_MON(2, tnr_dmd);
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x1A) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	{
> +		u8 data[2];
> +
> +		if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x15, data,
> +					   2) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		if ((data[0] != 0) || (data[1] != 0))
> +			return CXD2880_RESULT_ERROR_OTHER;
> +
> +		if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +					   CXD2880_IO_TGT_SYS, 0x11, data,
> +					   2) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		*rf_lvl_db =
> +		    cxd2880_convert2s_complement((data[0] << 3) |
> +						 ((data[1] & 0xE0) >> 5), 11);
> +	}
> +
> +	*rf_lvl_db *= 125;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x10,
> +				   0x00) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnr_dmd->rf_lvl_cmpstn) {
> +		ret = tnr_dmd->rf_lvl_cmpstn(tnr_dmd, rf_lvl_db);
> +		if (ret != CXD2880_RESULT_OK)
> +			return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd,
> +					       int *rf_lvl_db)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if ((!tnr_dmd) || (!rf_lvl_db))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, rf_lvl_db);
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd
> +							*tnr_dmd, u16 *status)
> +{
> +	u8 data[2] = { 0 };
> +
> +	if ((!tnr_dmd) || (!status))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x00,
> +				   0x1A) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +				   CXD2880_IO_TGT_SYS, 0x15, data,
> +				   2) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	*status = (u16)(((u16)data[0] << 8) | data[1]);
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct
> +							    cxd2880_tnrdmd
> +							    *tnr_dmd,
> +							    u16 *status)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if ((!tnr_dmd) || (!status))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	ret =
> +	    cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd->diver_sub, status);
> +
> +	return ret;
> +}
> +
> +enum cxd2880_ret cxd2880_tnrdmd_mon_ts_buf_info(struct cxd2880_tnrdmd *tnr_dmd,
> +						struct
> +						cxd2880_tnrdmd_ts_buf_info
> +						*info)
> +{
> +	u8 data[3] = { 0 };
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +
> +	if ((!tnr_dmd) || (!info))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
> +	    (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnr_dmd->io->write_reg(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x00,
> +				   0x0A) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +	if (tnr_dmd->io->read_regs(tnr_dmd->io,
> +				   CXD2880_IO_TGT_DMD, 0x50, data,
> +				   3) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	info->read_ready = (u8)((data[0] & 0x10) ? 0x01 : 0x00);
> +	info->almost_full = (u8)((data[0] & 0x08) ? 0x01 : 0x00);
> +	info->almost_empty = (u8)((data[0] & 0x04) ? 0x01 : 0x00);
> +	info->overflow = (u8)((data[0] & 0x02) ? 0x01 : 0x00);
> +	info->underflow = (u8)((data[0] & 0x01) ? 0x01 : 0x00);
> +
> +	info->packet_num = (u16)(((u32)(data[1] & 0x07) << 8) | data[2]);
> +
> +	return ret;
> +}
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
> new file mode 100644
> index 000000000000..506bd559668f
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
> @@ -0,0 +1,52 @@
> +/*
> + * cxd2880_tnrdmd_mon.h
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + * common monitor interface
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CXD2880_TNRDMD_MON_H
> +#define CXD2880_TNRDMD_MON_H
> +
> +#include "cxd2880_common.h"
> +#include "cxd2880_tnrdmd.h"
> +
> +enum cxd2880_ret cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd,
> +					   int *rf_lvl_db);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd,
> +					       int *rf_lvl_db);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd
> +							*tnr_dmd, u16 *status);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct
> +							    cxd2880_tnrdmd
> +							    *tnr_dmd,
> +							    u16 *status);
> +
> +enum cxd2880_ret cxd2880_tnrdmd_mon_ts_buf_info(struct cxd2880_tnrdmd *tnr_dmd,
> +						struct
> +						cxd2880_tnrdmd_ts_buf_info
> +						*info);
> +
> +#endif



Thanks,
Mauro

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 08/15] [media] cxd2880: Add top level of the driver
  2017-04-14  2:31 ` [PATCH v2 08/15] [media] cxd2880: Add top level of " Yasunari.Takiguchi
@ 2017-06-13 13:30   ` Mauro Carvalho Chehab
  2017-06-19  7:56     ` Takiguchi, Yasunari
  0 siblings, 1 reply; 36+ messages in thread
From: Mauro Carvalho Chehab @ 2017-06-13 13:30 UTC (permalink / raw)
  To: Yasunari.Takiguchi
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
	Toshihiko Matsumoto, Satoshi Watanabe

Em Fri, 14 Apr 2017 11:31:50 +0900
<Yasunari.Takiguchi@sony.com> escreveu:

> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> 
> This provides the main dvb frontend operation functions
> for the Sony CXD2880 DVB-T2/T tuner + demodulator driver.

For now, I'll do only a quick review on patches 6-15, as there are several
things to be ajusted on the code, from my past comments.

I'll do a better review on the next version. So, I'll focus only on
a few random issues I see on the code below.

> 
> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
> ---
>  drivers/media/dvb-frontends/cxd2880/cxd2880_top.c | 1550 +++++++++++++++++++++
>  1 file changed, 1550 insertions(+)
>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
> 
> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
> new file mode 100644
> index 000000000000..66d78fb93a13
> --- /dev/null
> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
> @@ -0,0 +1,1550 @@
> +/*
> + * cxd2880_top.c
> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
> + *
> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/spi/spi.h>
> +
> +#include "dvb_frontend.h"
> +
> +#include "cxd2880.h"
> +#include "cxd2880_tnrdmd_mon.h"
> +#include "cxd2880_tnrdmd_dvbt2_mon.h"
> +#include "cxd2880_tnrdmd_dvbt_mon.h"
> +#include "cxd2880_integ_dvbt2.h"
> +#include "cxd2880_integ_dvbt.h"
> +#include "cxd2880_devio_spi.h"
> +#include "cxd2880_spi_device.h"
> +#include "cxd2880_tnrdmd_driver_version.h"
> +
> +struct cxd2880_priv {
> +	struct cxd2880_tnrdmd tnrdmd;
> +	struct spi_device *spi;
> +	struct cxd2880_io regio;
> +	struct cxd2880_spi_device spi_device;
> +	struct cxd2880_spi cxd2880_spi;
> +	struct cxd2880_dvbt_tune_param dvbt_tune_param;
> +	struct cxd2880_dvbt2_tune_param dvbt2_tune_param;
> +	struct mutex *spi_mutex; /* For SPI access exclusive control */
> +};
> +
> +/*
> + * return value conversion table
> + */
> +static int return_tbl[] = {
> +	0,             /* CXD2880_RESULT_OK */
> +	-EINVAL,       /* CXD2880_RESULT_ERROR_ARG*/
> +	-EIO,          /* CXD2880_RESULT_ERROR_IO */
> +	-EPERM,        /* CXD2880_RESULT_ERROR_SW_STATE */
> +	-EBUSY,        /* CXD2880_RESULT_ERROR_HW_STATE */
> +	-ETIME,        /* CXD2880_RESULT_ERROR_TIMEOUT */
> +	-EAGAIN,       /* CXD2880_RESULT_ERROR_UNLOCK */
> +	-ERANGE,       /* CXD2880_RESULT_ERROR_RANGE */
> +	-EOPNOTSUPP,   /* CXD2880_RESULT_ERROR_NOSUPPORT */
> +	-ECANCELED,    /* CXD2880_RESULT_ERROR_CANCEL */
> +	-EPERM,        /* CXD2880_RESULT_ERROR_OTHER */
> +	-EOVERFLOW,    /* CXD2880_RESULT_ERROR_OVERFLOW */
> +	0,             /* CXD2880_RESULT_OK_CONFIRM */
> +};

Please, don't use a conversion table. That makes the driver more
complex to be understood without any good reason. Instead, just 
replace the values at the original enum.

> +
> +static enum cxd2880_ret cxd2880_pre_bit_err_t(
> +		struct cxd2880_tnrdmd *tnrdmd, u32 *pre_bit_err,
> +		u32 *pre_bit_count)
> +{
> +	u8 rdata[2];
> +
> +	if ((!tnrdmd) || (!pre_bit_err) || (!pre_bit_count))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (slvt_freeze_reg(tnrdmd) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x00, 0x10) != CXD2880_RESULT_OK) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x39, rdata, 1) != CXD2880_RESULT_OK) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	if ((rdata[0] & 0x01) == 0) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return CXD2880_RESULT_ERROR_HW_STATE;
> +	}
> +
> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x22, rdata, 2) != CXD2880_RESULT_OK) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	*pre_bit_err = (rdata[0] << 8) | rdata[1];
> +
> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x6F, rdata, 1) != CXD2880_RESULT_OK) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	slvt_unfreeze_reg(tnrdmd);
> +
> +	*pre_bit_count = ((rdata[0] & 0x07) == 0) ?
> +			256 : (0x1000 << (rdata[0] & 0x07));
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret cxd2880_pre_bit_err_t2(
> +		struct cxd2880_tnrdmd *tnrdmd, u32 *pre_bit_err,
> +		u32 *pre_bit_count)
> +{
> +	u32 period_exp = 0;
> +	u32 n_ldpc = 0;
> +	u8 data[5];
> +
> +	if ((!tnrdmd) || (!pre_bit_err) || (!pre_bit_count))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (slvt_freeze_reg(tnrdmd) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x00, 0x0B) != CXD2880_RESULT_OK) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x3C, data, sizeof(data))
> +				 != CXD2880_RESULT_OK) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	if (!(data[0] & 0x01)) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return CXD2880_RESULT_ERROR_HW_STATE;
> +	}
> +	*pre_bit_err =
> +	((data[1] & 0x0F) << 24) | (data[2] << 16) | (data[3] << 8) | data[4];
> +
> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0xA0, data, 1) != CXD2880_RESULT_OK) {
> +		slvt_unfreeze_reg(tnrdmd);
> +		return CXD2880_RESULT_ERROR_IO;
> +	}
> +
> +	if (((enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03)) ==
> +	    CXD2880_DVBT2_FEC_LDPC_16K)
> +		n_ldpc = 16200;
> +	else
> +		n_ldpc = 64800;
> +	slvt_unfreeze_reg(tnrdmd);
> +
> +	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x00, 0x20) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x6F, data, 1) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	period_exp = data[0] & 0x0F;
> +
> +	*pre_bit_count = (1U << period_exp) * n_ldpc;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret cxd2880_post_bit_err_t(struct cxd2880_tnrdmd *tnrdmd,
> +						u32 *post_bit_err,
> +						u32 *post_bit_count)
> +{
> +	u8 rdata[3];
> +	u32 bit_error = 0;
> +	u32 period_exp = 0;
> +
> +	if ((!tnrdmd) || (!post_bit_err) || (!post_bit_count))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x00, 0x0D) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x15, rdata, 3) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if ((rdata[0] & 0x40) == 0)
> +		return CXD2880_RESULT_ERROR_HW_STATE;
> +
> +	*post_bit_err = ((rdata[0] & 0x3F) << 16) | (rdata[1] << 8) | rdata[2];
> +
> +	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x00, 0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x60, rdata, 1) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	period_exp = (rdata[0] & 0x1F);
> +
> +	if ((period_exp <= 11) && (bit_error > (1U << period_exp) * 204 * 8))
> +		return CXD2880_RESULT_ERROR_HW_STATE;
> +
> +	if (period_exp == 11)
> +		*post_bit_count = 3342336;
> +	else
> +		*post_bit_count = (1U << period_exp) * 204 * 81;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret cxd2880_post_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd,
> +						u32 *post_bit_err,
> +						u32 *post_bit_count)
> +{
> +	u32 period_exp = 0;
> +	u32 n_bch = 0;
> +
> +	if ((!tnrdmd) || (!post_bit_err) || (!post_bit_count))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	{
> +		u8 data[3];
> +		enum cxd2880_dvbt2_plp_fec plp_fec_type =
> +			CXD2880_DVBT2_FEC_LDPC_16K;
> +		enum cxd2880_dvbt2_plp_code_rate plp_code_rate =
> +			CXD2880_DVBT2_R1_2;
> +
> +		static const u16 n_bch_bits_lookup[2][8] = {
> +			{7200, 9720, 10800, 11880, 12600, 13320, 5400, 6480},
> +			{32400, 38880, 43200, 48600, 51840, 54000, 21600, 25920}
> +		};
> +
> +		if (slvt_freeze_reg(tnrdmd) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +					0x00, 0x0B) != CXD2880_RESULT_OK) {
> +			slvt_unfreeze_reg(tnrdmd);
> +			return CXD2880_RESULT_ERROR_IO;
> +		}
> +
> +		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +					 0x15, data, 3) != CXD2880_RESULT_OK) {
> +			slvt_unfreeze_reg(tnrdmd);
> +			return CXD2880_RESULT_ERROR_IO;
> +		}
> +
> +		if (!(data[0] & 0x40)) {
> +			slvt_unfreeze_reg(tnrdmd);
> +			return CXD2880_RESULT_ERROR_HW_STATE;
> +		}
> +
> +		*post_bit_err =
> +			((data[0] & 0x3F) << 16) | (data[1] << 8) | data[2];
> +
> +		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +					0x9D, data, 1) != CXD2880_RESULT_OK) {
> +			slvt_unfreeze_reg(tnrdmd);
> +			return CXD2880_RESULT_ERROR_IO;
> +		}
> +
> +		plp_code_rate =
> +		(enum cxd2880_dvbt2_plp_code_rate)(data[0] & 0x07);
> +
> +		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +					0xA0, data, 1) != CXD2880_RESULT_OK) {
> +			slvt_unfreeze_reg(tnrdmd);
> +			return CXD2880_RESULT_ERROR_IO;
> +		}
> +
> +		plp_fec_type = (enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03);
> +
> +		slvt_unfreeze_reg(tnrdmd);
> +
> +		if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +					0x00, 0x20) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +					0x72, data, 1) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		period_exp = data[0] & 0x0F;
> +
> +		if ((plp_fec_type > CXD2880_DVBT2_FEC_LDPC_64K) ||
> +			(plp_code_rate > CXD2880_DVBT2_R2_5))
> +			return CXD2880_RESULT_ERROR_HW_STATE;
> +
> +		n_bch = n_bch_bits_lookup[plp_fec_type][plp_code_rate];
> +	}
> +
> +	if (*post_bit_err > ((1U << period_exp) * n_bch))
> +		return CXD2880_RESULT_ERROR_HW_STATE;
> +
> +	*post_bit_count = (1U << period_exp) * n_bch;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret cxd2880_read_block_err_t(
> +					struct cxd2880_tnrdmd *tnrdmd,
> +					u32 *block_err,
> +					u32 *block_count)
> +{
> +	u8 rdata[3];
> +
> +	if ((!tnrdmd) || (!block_err) || (!block_count))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x00, 0x0D) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x18, rdata, 3) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if ((rdata[0] & 0x01) == 0)
> +		return CXD2880_RESULT_ERROR_HW_STATE;
> +
> +	*block_err = (rdata[1] << 8) | rdata[2];
> +
> +	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x00, 0x10) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +				0x5C, rdata, 1) != CXD2880_RESULT_OK)
> +		return CXD2880_RESULT_ERROR_IO;
> +
> +	*block_count = 1U << (rdata[0] & 0x0F);
> +
> +	if ((*block_count == 0) || (*block_err > *block_count))
> +		return CXD2880_RESULT_ERROR_HW_STATE;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static enum cxd2880_ret cxd2880_read_block_err_t2(
> +					struct cxd2880_tnrdmd *tnrdmd,
> +					u32 *block_err,
> +					u32 *block_count)
> +{
> +	if ((!tnrdmd) || (!block_err) || (!block_count))
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
> +		return CXD2880_RESULT_ERROR_ARG;
> +
> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
> +		return CXD2880_RESULT_ERROR_SW_STATE;
> +
> +	{
> +		u8 rdata[3];
> +
> +		if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +					0x00, 0x0B) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +					0x18, rdata, 3) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		if ((rdata[0] & 0x01) == 0)
> +			return CXD2880_RESULT_ERROR_HW_STATE;
> +
> +		*block_err = (rdata[1] << 8) | rdata[2];
> +
> +		if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +					0x00, 0x24) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
> +					0xDC, rdata, 1) != CXD2880_RESULT_OK)
> +			return CXD2880_RESULT_ERROR_IO;
> +
> +		*block_count = 1U << (rdata[0] & 0x0F);
> +	}
> +
> +	if ((*block_count == 0) || (*block_err > *block_count))
> +		return CXD2880_RESULT_ERROR_HW_STATE;
> +
> +	return CXD2880_RESULT_OK;
> +}
> +
> +static void cxd2880_release(struct dvb_frontend *fe)
> +{
> +	struct cxd2880_priv *priv = NULL;
> +
> +	if (!fe) {
> +		pr_err("%s: invalid arg.\n", __func__);
> +		return;
> +	}
> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
> +	kfree(priv);
> +}
> +
> +static int cxd2880_init(struct dvb_frontend *fe)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	struct cxd2880_priv *priv = NULL;
> +	struct cxd2880_tnrdmd_create_param create_param;
> +
> +	if (!fe) {
> +		pr_err("%s: invalid arg.\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
> +
> +	create_param.ts_output_if = CXD2880_TNRDMD_TSOUT_IF_SPI;
> +	create_param.xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_NONE;
> +	create_param.en_internal_ldo = 1;
> +	create_param.xosc_cap = 18;
> +	create_param.xosc_i = 8;
> +	create_param.stationary_use = 1;
> +
> +	mutex_lock(priv->spi_mutex);
> +	if (priv->tnrdmd.io != &priv->regio) {
> +		ret = cxd2880_tnrdmd_create(&priv->tnrdmd,
> +				&priv->regio, &create_param);
> +		if (ret != CXD2880_RESULT_OK) {
> +			mutex_unlock(priv->spi_mutex);
> +			dev_info(&priv->spi->dev,
> +				"%s: cxd2880 tnrdmd create failed %d\n",
> +				__func__, ret);
> +			return return_tbl[ret];
> +		}
> +	}
> +	ret = cxd2880_integ_init(&priv->tnrdmd);
> +	if (ret != CXD2880_RESULT_OK) {
> +		mutex_unlock(priv->spi_mutex);
> +		dev_err(&priv->spi->dev, "%s: cxd2880 integ init failed %d\n",
> +				__func__, ret);
> +		return return_tbl[ret];
> +	}
> +	mutex_unlock(priv->spi_mutex);
> +
> +	dev_dbg(&priv->spi->dev, "%s: OK.\n", __func__);
> +
> +	return return_tbl[ret];
> +}
> +
> +static int cxd2880_sleep(struct dvb_frontend *fe)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	struct cxd2880_priv *priv = NULL;
> +
> +	if (!fe) {
> +		pr_err("%s: inavlid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_sleep(&priv->tnrdmd);
> +	mutex_unlock(priv->spi_mutex);
> +
> +	dev_dbg(&priv->spi->dev, "%s: tnrdmd_sleep ret %d\n",
> +		__func__, ret);
> +
> +	return return_tbl[ret];
> +}
> +
> +static int cxd2880_read_signal_strength(struct dvb_frontend *fe,
> +				u16 *strength)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	struct cxd2880_priv *priv = NULL;
> +	struct dtv_frontend_properties *c = NULL;
> +	int level = 0;
> +
> +	if ((!fe) || (!strength)) {
> +		pr_err("%s: inavlid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
> +	c = &fe->dtv_property_cache;
> +
> +	mutex_lock(priv->spi_mutex);
> +	if ((c->delivery_system == SYS_DVBT) ||
> +		(c->delivery_system == SYS_DVBT2)) {
> +		ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &level);
> +	} else {
> +		dev_dbg(&priv->spi->dev, "%s: invalid system\n", __func__);
> +		mutex_unlock(priv->spi_mutex);
> +		return -EINVAL;
> +	}
> +	mutex_unlock(priv->spi_mutex);
> +
> +	level /= 125;
> +	/* -105dBm - -30dBm (-105000/125 = -840, -30000/125 = -240 */
> +	level = clamp(level, -840, -240);
> +	/* scale value to 0x0000-0xFFFF */
> +	*strength = (u16)(((level + 840) * 0xFFFF) / (-240 + 840));
> +
> +	if (ret != CXD2880_RESULT_OK)
> +		dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
> +
> +	return return_tbl[ret];
> +}
> +
> +static int cxd2880_read_snr(struct dvb_frontend *fe, u16 *snr)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	int snrvalue = 0;
> +	struct cxd2880_priv *priv = NULL;
> +	struct dtv_frontend_properties *c = NULL;
> +
> +	if ((!fe) || (!snr)) {
> +		pr_err("%s: inavlid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
> +	c = &fe->dtv_property_cache;
> +
> +	mutex_lock(priv->spi_mutex);
> +	if (c->delivery_system == SYS_DVBT) {
> +		ret = cxd2880_tnrdmd_dvbt_mon_snr(&priv->tnrdmd,
> +						&snrvalue);
> +	} else if (c->delivery_system == SYS_DVBT2) {
> +		ret = cxd2880_tnrdmd_dvbt2_mon_snr(&priv->tnrdmd,
> +						&snrvalue);
> +	} else {
> +		dev_err(&priv->spi->dev, "%s: invalid system\n", __func__);
> +		mutex_unlock(priv->spi_mutex);
> +		return -EINVAL;
> +	}
> +	mutex_unlock(priv->spi_mutex);
> +
> +	if (snrvalue < 0)
> +		snrvalue = 0;
> +	*snr = (u16)snrvalue;
> +
> +	if (ret != CXD2880_RESULT_OK)
> +		dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
> +
> +	return return_tbl[ret];
> +}
> +
> +static int cxd2880_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	struct cxd2880_priv *priv = NULL;
> +	struct dtv_frontend_properties *c = NULL;
> +
> +	if ((!fe) || (!ucblocks)) {
> +		pr_err("%s: inavlid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
> +	c = &fe->dtv_property_cache;
> +
> +	mutex_lock(priv->spi_mutex);
> +	if (c->delivery_system == SYS_DVBT) {
> +		ret = cxd2880_tnrdmd_dvbt_mon_packet_error_number(
> +								&priv->tnrdmd,
> +								ucblocks);
> +	} else if (c->delivery_system == SYS_DVBT2) {
> +		ret = cxd2880_tnrdmd_dvbt2_mon_packet_error_number(
> +								&priv->tnrdmd,
> +								ucblocks);
> +	} else {
> +		dev_err(&priv->spi->dev, "%s: invlaid system\n", __func__);
> +		mutex_unlock(priv->spi_mutex);
> +		return -EINVAL;
> +	}
> +	mutex_unlock(priv->spi_mutex);
> +
> +	if (ret != CXD2880_RESULT_OK)
> +		dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
> +
> +	return return_tbl[ret];
> +}
> +
> +static int cxd2880_read_ber(struct dvb_frontend *fe, u32 *ber)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	struct cxd2880_priv *priv = NULL;
> +	struct dtv_frontend_properties *c = NULL;
> +
> +	if ((!fe) || (!ber)) {
> +		pr_err("%s: inavlid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
> +	c = &fe->dtv_property_cache;
> +
> +	mutex_lock(priv->spi_mutex);
> +	if (c->delivery_system == SYS_DVBT) {
> +		ret = cxd2880_tnrdmd_dvbt_mon_pre_rsber(&priv->tnrdmd,
> +						ber);
> +		/* x100 to change unit.(10^7 -> 10^9 */
> +		*ber *= 100;
> +	} else if (c->delivery_system == SYS_DVBT2) {
> +		ret = cxd2880_tnrdmd_dvbt2_mon_pre_bchber(&priv->tnrdmd,
> +						ber);
> +	} else {
> +		dev_err(&priv->spi->dev, "%s: invlaid system\n", __func__);
> +		mutex_unlock(priv->spi_mutex);
> +		return -EINVAL;
> +	}
> +	mutex_unlock(priv->spi_mutex);
> +
> +	if (ret != CXD2880_RESULT_OK)
> +		dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
> +
> +	return return_tbl[ret];
> +}
> +
> +static int cxd2880_set_frontend(struct dvb_frontend *fe)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	struct dtv_frontend_properties *c;
> +	struct cxd2880_priv *priv;
> +	enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
> +
> +	if (!fe) {
> +		pr_err("%s: inavlid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
> +	c = &fe->dtv_property_cache;
> +
> +	switch (c->bandwidth_hz) {
> +	case 1712000:
> +		bw = CXD2880_DTV_BW_1_7_MHZ;
> +		break;
> +	case 5000000:
> +		bw = CXD2880_DTV_BW_5_MHZ;
> +		break;
> +	case 6000000:
> +		bw = CXD2880_DTV_BW_6_MHZ;
> +		break;
> +	case 7000000:
> +		bw = CXD2880_DTV_BW_7_MHZ;
> +		break;
> +	case 8000000:
> +		bw = CXD2880_DTV_BW_8_MHZ;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	dev_info(&priv->spi->dev, "%s: sys:%d freq:%d bw:%d\n", __func__,
> +			c->delivery_system, c->frequency, bw);
> +	mutex_lock(priv->spi_mutex);
> +	if (c->delivery_system == SYS_DVBT) {
> +		priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT;
> +		priv->dvbt_tune_param.center_freq_khz = c->frequency / 1000;
> +		priv->dvbt_tune_param.bandwidth = bw;
> +		priv->dvbt_tune_param.profile = CXD2880_DVBT_PROFILE_HP;
> +		ret = cxd2880_integ_dvbt_tune(&priv->tnrdmd,
> +						&priv->dvbt_tune_param);
> +	} else if (c->delivery_system == SYS_DVBT2) {
> +		priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT2;
> +		priv->dvbt2_tune_param.center_freq_khz = c->frequency / 1000;
> +		priv->dvbt2_tune_param.bandwidth = bw;
> +		priv->dvbt2_tune_param.data_plp_id = (u16)c->stream_id;
> +		ret = cxd2880_integ_dvbt2_tune(&priv->tnrdmd,
> +						&priv->dvbt2_tune_param);
> +	} else {
> +		dev_err(&priv->spi->dev, "%s: invalid system\n", __func__);
> +		mutex_unlock(priv->spi_mutex);
> +		return -EINVAL;
> +	}
> +	mutex_unlock(priv->spi_mutex);
> +	dev_info(&priv->spi->dev, "%s: tune result %d\n", __func__, ret);
> +
> +	return return_tbl[ret];
> +}
> +
> +static int cxd2880_read_status(struct dvb_frontend *fe,
> +				enum fe_status *status)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	u8 sync = 0;
> +	u8 lock = 0;
> +	u8 unlock = 0;
> +	struct cxd2880_priv *priv = NULL;
> +	struct dtv_frontend_properties *c = NULL;
> +
> +	if ((!fe) || (!status)) {
> +		pr_err("%s: invalid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
> +	c = &fe->dtv_property_cache;
> +	*status = 0;
> +
> +	if (priv->tnrdmd.state == CXD2880_TNRDMD_STATE_ACTIVE) {
> +		mutex_lock(priv->spi_mutex);
> +		if (c->delivery_system == SYS_DVBT) {
> +			ret = cxd2880_tnrdmd_dvbt_mon_sync_stat(
> +							&priv->tnrdmd,
> +							&sync,
> +							&lock,
> +							&unlock);
> +		} else if (c->delivery_system == SYS_DVBT2) {
> +			ret = cxd2880_tnrdmd_dvbt2_mon_sync_stat(
> +							&priv->tnrdmd,
> +							&sync,
> +							&lock,
> +							&unlock);
> +		} else {
> +			dev_err(&priv->spi->dev,
> +				"%s: invlaid system", __func__);
> +			mutex_unlock(priv->spi_mutex);
> +			return -EINVAL;
> +		}
> +
> +		mutex_unlock(priv->spi_mutex);
> +		if (ret != CXD2880_RESULT_OK) {
> +			dev_err(&priv->spi->dev, "%s: failed. sys = %d\n",
> +				__func__, priv->tnrdmd.sys);
> +			return  return_tbl[ret];
> +		}
> +
> +		if (sync == 6) {
> +			*status = FE_HAS_SIGNAL |
> +					FE_HAS_CARRIER;
> +		}
> +		if (lock)
> +			*status |= FE_HAS_VITERBI |
> +					FE_HAS_SYNC |
> +					FE_HAS_LOCK;
> +	}
> +
> +	dev_dbg(&priv->spi->dev, "%s: status %d result %d\n", __func__,
> +		*status, ret);
> +
> +	return  return_tbl[CXD2880_RESULT_OK];
> +}
> +
> +static int cxd2880_tune(struct dvb_frontend *fe,
> +			bool retune,
> +			unsigned int mode_flags,
> +			unsigned int *delay,
> +			enum fe_status *status)
> +{
> +	int ret = 0;
> +
> +	if ((!fe) || (!delay) || (!status)) {
> +		pr_err("%s: invalid arg.", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (retune) {
> +		ret = cxd2880_set_frontend(fe);
> +		if (ret) {
> +			pr_err("%s: cxd2880_set_frontend failed %d\n",
> +				__func__, ret);
> +			return ret;
> +		}
> +	}
> +
> +	*delay = HZ / 5;
> +
> +	return cxd2880_read_status(fe, status);
> +}
> +
> +static int cxd2880_get_frontend_t(struct dvb_frontend *fe,
> +				struct dtv_frontend_properties *c)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	int result = 0;
> +	struct cxd2880_priv *priv = NULL;
> +	enum cxd2880_dvbt_mode mode = CXD2880_DVBT_MODE_2K;
> +	enum cxd2880_dvbt_guard guard = CXD2880_DVBT_GUARD_1_32;
> +	struct cxd2880_dvbt_tpsinfo tps;
> +	enum cxd2880_tnrdmd_spectrum_sense sense;
> +	u16 snr = 0;
> +	int strength = 0;
> +	u32 pre_bit_err = 0, pre_bit_count = 0;
> +	u32 post_bit_err = 0, post_bit_count = 0;
> +	u32 block_err = 0, block_count = 0;
> +
> +	if ((!fe) || (!c)) {
> +		pr_err("%s: invalid arg\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_dvbt_mon_mode_guard(&priv->tnrdmd,
> +						 &mode, &guard);
> +	mutex_unlock(priv->spi_mutex);
> +	if (ret == CXD2880_RESULT_OK) {
> +		switch (mode) {
> +		case CXD2880_DVBT_MODE_2K:
> +			c->transmission_mode = TRANSMISSION_MODE_2K;
> +			break;
> +		case CXD2880_DVBT_MODE_8K:
> +			c->transmission_mode = TRANSMISSION_MODE_8K;
> +			break;
> +		default:
> +			c->transmission_mode = TRANSMISSION_MODE_2K;
> +			dev_err(&priv->spi->dev, "%s: get invalid mode %d\n",
> +					__func__, mode);
> +			break;
> +		}
> +		switch (guard) {
> +		case CXD2880_DVBT_GUARD_1_32:
> +			c->guard_interval = GUARD_INTERVAL_1_32;
> +			break;
> +		case CXD2880_DVBT_GUARD_1_16:
> +			c->guard_interval = GUARD_INTERVAL_1_16;
> +			break;
> +		case CXD2880_DVBT_GUARD_1_8:
> +			c->guard_interval = GUARD_INTERVAL_1_8;
> +			break;
> +		case CXD2880_DVBT_GUARD_1_4:
> +			c->guard_interval = GUARD_INTERVAL_1_4;
> +			break;
> +		default:
> +			c->guard_interval = GUARD_INTERVAL_1_32;
> +			dev_err(&priv->spi->dev, "%s: get invalid guard %d\n",
> +					__func__, guard);
> +			break;
> +		}
> +	} else {
> +		c->transmission_mode = TRANSMISSION_MODE_2K;
> +		c->guard_interval = GUARD_INTERVAL_1_32;
> +		dev_dbg(&priv->spi->dev,
> +			"%s: ModeGuard err %d\n", __func__, ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, &tps);
> +	mutex_unlock(priv->spi_mutex);
> +	if (ret == CXD2880_RESULT_OK) {
> +		switch (tps.hierarchy) {
> +		case CXD2880_DVBT_HIERARCHY_NON:
> +			c->hierarchy = HIERARCHY_NONE;
> +			break;
> +		case CXD2880_DVBT_HIERARCHY_1:
> +			c->hierarchy = HIERARCHY_1;
> +			break;
> +		case CXD2880_DVBT_HIERARCHY_2:
> +			c->hierarchy = HIERARCHY_2;
> +			break;
> +		case CXD2880_DVBT_HIERARCHY_4:
> +			c->hierarchy = HIERARCHY_4;
> +			break;
> +		default:
> +			c->hierarchy = HIERARCHY_NONE;
> +			dev_err(&priv->spi->dev,
> +				"%s: TPSInfo hierarchy invalid %d\n",
> +				__func__, tps.hierarchy);
> +			break;
> +		}
> +
> +		switch (tps.rate_hp) {
> +		case CXD2880_DVBT_CODERATE_1_2:
> +			c->code_rate_HP = FEC_1_2;
> +			break;
> +		case CXD2880_DVBT_CODERATE_2_3:
> +			c->code_rate_HP = FEC_2_3;
> +			break;
> +		case CXD2880_DVBT_CODERATE_3_4:
> +			c->code_rate_HP = FEC_3_4;
> +			break;
> +		case CXD2880_DVBT_CODERATE_5_6:
> +			c->code_rate_HP = FEC_5_6;
> +			break;
> +		case CXD2880_DVBT_CODERATE_7_8:
> +			c->code_rate_HP = FEC_7_8;
> +			break;
> +		default:
> +			c->code_rate_HP = FEC_NONE;
> +			dev_err(&priv->spi->dev,
> +				"%s: TPSInfo rateHP invalid %d\n",
> +				__func__, tps.rate_hp);
> +			break;
> +		}
> +		switch (tps.rate_lp) {
> +		case CXD2880_DVBT_CODERATE_1_2:
> +			c->code_rate_LP = FEC_1_2;
> +			break;
> +		case CXD2880_DVBT_CODERATE_2_3:
> +			c->code_rate_LP = FEC_2_3;
> +			break;
> +		case CXD2880_DVBT_CODERATE_3_4:
> +			c->code_rate_LP = FEC_3_4;
> +			break;
> +		case CXD2880_DVBT_CODERATE_5_6:
> +			c->code_rate_LP = FEC_5_6;
> +			break;
> +		case CXD2880_DVBT_CODERATE_7_8:
> +			c->code_rate_LP = FEC_7_8;
> +			break;
> +		default:
> +			c->code_rate_LP = FEC_NONE;
> +			dev_err(&priv->spi->dev,
> +				"%s: TPSInfo rateLP invalid %d\n",
> +				__func__, tps.rate_lp);
> +			break;
> +		}
> +		switch (tps.constellation) {
> +		case CXD2880_DVBT_CONSTELLATION_QPSK:
> +			c->modulation = QPSK;
> +			break;
> +		case CXD2880_DVBT_CONSTELLATION_16QAM:
> +			c->modulation = QAM_16;
> +			break;
> +		case CXD2880_DVBT_CONSTELLATION_64QAM:
> +			c->modulation = QAM_64;
> +			break;
> +		default:
> +			c->modulation = QPSK;
> +			dev_err(&priv->spi->dev,
> +				"%s: TPSInfo constellation invalid %d\n",
> +				__func__, tps.constellation);
> +			break;
> +		}
> +	} else {
> +		c->hierarchy = HIERARCHY_NONE;
> +		c->code_rate_HP = FEC_NONE;
> +		c->code_rate_LP = FEC_NONE;
> +		c->modulation = QPSK;
> +		dev_dbg(&priv->spi->dev,
> +			"%s: TPS info err %d\n", __func__, ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(&priv->tnrdmd, &sense);
> +	mutex_unlock(priv->spi_mutex);
> +	if (ret == CXD2880_RESULT_OK) {
> +		switch (sense) {
> +		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
> +			c->inversion = INVERSION_OFF;
> +			break;
> +		case CXD2880_TNRDMD_SPECTRUM_INV:
> +			c->inversion = INVERSION_ON;
> +			break;
> +		default:
> +			c->inversion = INVERSION_OFF;
> +			dev_err(&priv->spi->dev,
> +				"%s: spectrum sense invalid %d\n",
> +				__func__, sense);
> +			break;
> +		}
> +	} else {
> +		c->inversion = INVERSION_OFF;
> +		dev_dbg(&priv->spi->dev,
> +			"%s: spectrum_sense %d\n", __func__, ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
> +	mutex_unlock(priv->spi_mutex);
> +	if (ret == CXD2880_RESULT_OK) {
> +		c->strength.len = 1;
> +		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
> +		c->strength.stat[0].svalue = strength;
> +	} else {
> +		c->strength.len = 1;
> +		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		dev_dbg(&priv->spi->dev, "%s: mon_rf_lvl %d\n",
> +			__func__, result);
> +	}
> +
> +	result = cxd2880_read_snr(fe, &snr);
> +	if (!result) {
> +		c->cnr.len = 1;
> +		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
> +		c->cnr.stat[0].svalue = snr;
> +	} else {
> +		c->cnr.len = 1;
> +		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		dev_dbg(&priv->spi->dev, "%s: read_snr %d\n", __func__, result);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_pre_bit_err_t(&priv->tnrdmd, &pre_bit_err,
> +					&pre_bit_count);

Hmm... reading BER-based measures at get_frontend() may not be
the best idea, depending on how the hardware works.

I mean, you need to be sure that, if userspace is calling it too
often, it won't affect the hardware or the measurement.

On other drivers, where each call generates an I2C access,
we do that by moving the code that actually call the hardware
at get status, and we use jiffies to be sure that it won't be
called too often.

I dunno if, on your SPI design, this would be an issue or not.

> +	mutex_unlock(priv->spi_mutex);
> +	if (ret == CXD2880_RESULT_OK) {
> +		c->pre_bit_error.len = 1;
> +		c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
> +		c->pre_bit_error.stat[0].uvalue = pre_bit_err;
> +		c->pre_bit_count.len = 1;
> +		c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
> +		c->pre_bit_count.stat[0].uvalue = pre_bit_count;
> +	} else {
> +		c->pre_bit_error.len = 1;
> +		c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		c->pre_bit_count.len = 1;
> +		c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		dev_dbg(&priv->spi->dev,
> +			"%s: pre_bit_error_t failed %d\n",
> +			__func__, ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_post_bit_err_t(&priv->tnrdmd,
> +				&post_bit_err, &post_bit_count);
> +	mutex_unlock(priv->spi_mutex);
> +	if (ret == CXD2880_RESULT_OK) {
> +		c->post_bit_error.len = 1;
> +		c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
> +		c->post_bit_error.stat[0].uvalue = post_bit_err;
> +		c->post_bit_count.len = 1;
> +		c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
> +		c->post_bit_count.stat[0].uvalue = post_bit_count;
> +	} else {
> +		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;
> +		dev_dbg(&priv->spi->dev,
> +			"%s: post_bit_err_t %d\n", __func__, ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_read_block_err_t(&priv->tnrdmd,
> +					&block_err, &block_count);
> +	mutex_unlock(priv->spi_mutex);
> +	if (ret == CXD2880_RESULT_OK) {
> +		c->block_error.len = 1;
> +		c->block_error.stat[0].scale = FE_SCALE_COUNTER;
> +		c->block_error.stat[0].uvalue = block_err;
> +		c->block_count.len = 1;
> +		c->block_count.stat[0].scale = FE_SCALE_COUNTER;
> +		c->block_count.stat[0].uvalue = block_count;
> +	} else {
> +		c->block_error.len = 1;
> +		c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		c->block_count.len = 1;
> +		c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		dev_dbg(&priv->spi->dev,
> +			"%s: read_block_err_t  %d\n", __func__, ret);
> +	}
> +
> +	return 0;
> +}
> +
> +static int cxd2880_get_frontend_t2(struct dvb_frontend *fe,
> +				struct dtv_frontend_properties *c)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	int result = 0;
> +	struct cxd2880_priv *priv = NULL;
> +	struct cxd2880_dvbt2_l1pre l1pre;
> +	enum cxd2880_dvbt2_plp_code_rate coderate;
> +	enum cxd2880_dvbt2_plp_constell qam;
> +	enum cxd2880_tnrdmd_spectrum_sense sense;
> +	u16 snr = 0;
> +	int strength = 0;
> +	u32 pre_bit_err = 0, pre_bit_count = 0;
> +	u32 post_bit_err = 0, post_bit_count = 0;
> +	u32 block_err = 0, block_count = 0;
> +
> +	if ((!fe) || (!c)) {
> +		pr_err("%s: invalid arg.\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre);
> +	mutex_unlock(priv->spi_mutex);
> +	if (ret == CXD2880_RESULT_OK) {
> +		switch (l1pre.fft_mode) {
> +		case CXD2880_DVBT2_M2K:
> +			c->transmission_mode = TRANSMISSION_MODE_2K;
> +			break;
> +		case CXD2880_DVBT2_M8K:
> +			c->transmission_mode = TRANSMISSION_MODE_8K;
> +			break;
> +		case CXD2880_DVBT2_M4K:
> +			c->transmission_mode = TRANSMISSION_MODE_4K;
> +			break;
> +		case CXD2880_DVBT2_M1K:
> +			c->transmission_mode = TRANSMISSION_MODE_1K;
> +			break;
> +		case CXD2880_DVBT2_M16K:
> +			c->transmission_mode = TRANSMISSION_MODE_16K;
> +			break;
> +		case CXD2880_DVBT2_M32K:
> +			c->transmission_mode = TRANSMISSION_MODE_32K;
> +			break;
> +		default:
> +			c->transmission_mode = TRANSMISSION_MODE_2K;
> +			dev_err(&priv->spi->dev,
> +				"%s: L1Pre fft_mode invalid %d\n",
> +				__func__, l1pre.fft_mode);
> +			break;
> +		}
> +		switch (l1pre.gi) {
> +		case CXD2880_DVBT2_G1_32:
> +			c->guard_interval = GUARD_INTERVAL_1_32;
> +			break;
> +		case CXD2880_DVBT2_G1_16:
> +			c->guard_interval = GUARD_INTERVAL_1_16;
> +			break;
> +		case CXD2880_DVBT2_G1_8:
> +			c->guard_interval = GUARD_INTERVAL_1_8;
> +			break;
> +		case CXD2880_DVBT2_G1_4:
> +			c->guard_interval = GUARD_INTERVAL_1_4;
> +			break;
> +		case CXD2880_DVBT2_G1_128:
> +			c->guard_interval = GUARD_INTERVAL_1_128;
> +			break;
> +		case CXD2880_DVBT2_G19_128:
> +			c->guard_interval = GUARD_INTERVAL_19_128;
> +			break;
> +		case CXD2880_DVBT2_G19_256:
> +			c->guard_interval = GUARD_INTERVAL_19_256;
> +			break;
> +		default:
> +			c->guard_interval = GUARD_INTERVAL_1_32;
> +			dev_err(&priv->spi->dev,
> +				"%s: L1Pre gi invalid %d\n",
> +				__func__, l1pre.gi);
> +			break;
> +		}
> +	} else {
> +		c->transmission_mode = TRANSMISSION_MODE_2K;
> +		c->guard_interval = GUARD_INTERVAL_1_32;
> +		dev_dbg(&priv->spi->dev,
> +			"%s: L1Pre err %d\n", __func__, ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_dvbt2_mon_code_rate(&priv->tnrdmd,
> +						CXD2880_DVBT2_PLP_DATA,
> +						&coderate);
> +	mutex_unlock(priv->spi_mutex);
> +	if (ret == CXD2880_RESULT_OK) {
> +		switch (coderate) {
> +		case CXD2880_DVBT2_R1_2:
> +			c->fec_inner = FEC_1_2;
> +			break;
> +		case CXD2880_DVBT2_R3_5:
> +			c->fec_inner = FEC_3_5;
> +			break;
> +		case CXD2880_DVBT2_R2_3:
> +			c->fec_inner = FEC_2_3;
> +			break;
> +		case CXD2880_DVBT2_R3_4:
> +			c->fec_inner = FEC_3_4;
> +			break;
> +		case CXD2880_DVBT2_R4_5:
> +			c->fec_inner = FEC_4_5;
> +			break;
> +		case CXD2880_DVBT2_R5_6:
> +			c->fec_inner = FEC_5_6;
> +			break;
> +		default:
> +			c->fec_inner = FEC_NONE;
> +			dev_err(&priv->spi->dev,
> +				"%s: CodeRate invalid %d\n",
> +				__func__, coderate);
> +			break;
> +		}
> +	} else {
> +		c->fec_inner = FEC_NONE;
> +		dev_dbg(&priv->spi->dev, "%s: CodeRate %d\n", __func__, ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_dvbt2_mon_qam(&priv->tnrdmd,
> +					CXD2880_DVBT2_PLP_DATA,
> +					&qam);
> +	mutex_unlock(priv->spi_mutex);
> +	if (ret == CXD2880_RESULT_OK) {
> +		switch (qam) {
> +		case CXD2880_DVBT2_QPSK:
> +			c->modulation = QPSK;
> +			break;
> +		case CXD2880_DVBT2_QAM16:
> +			c->modulation = QAM_16;
> +			break;
> +		case CXD2880_DVBT2_QAM64:
> +			c->modulation = QAM_64;
> +			break;
> +		case CXD2880_DVBT2_QAM256:
> +			c->modulation = QAM_256;
> +			break;
> +		default:
> +			c->modulation = QPSK;
> +			dev_err(&priv->spi->dev,
> +				"%s: QAM invalid %d\n",
> +				__func__, qam);
> +			break;
> +		}
> +	} else {
> +		c->modulation = QPSK;
> +		dev_dbg(&priv->spi->dev, "%s: QAM %d\n", __func__, ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(&priv->tnrdmd, &sense);
> +	mutex_unlock(priv->spi_mutex);
> +	if (ret == CXD2880_RESULT_OK) {
> +		switch (sense) {
> +		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
> +			c->inversion = INVERSION_OFF;
> +			break;
> +		case CXD2880_TNRDMD_SPECTRUM_INV:
> +			c->inversion = INVERSION_ON;
> +			break;
> +		default:
> +			c->inversion = INVERSION_OFF;
> +			dev_err(&priv->spi->dev,
> +				"%s: spectrum sense invalid %d\n",
> +				__func__, sense);
> +			break;
> +		}
> +	} else {
> +		c->inversion = INVERSION_OFF;
> +		dev_dbg(&priv->spi->dev,
> +			"%s: SpectrumSense %d\n", __func__, ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
> +	mutex_unlock(priv->spi_mutex);
> +	if (ret == CXD2880_RESULT_OK) {
> +		c->strength.len = 1;
> +		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
> +		c->strength.stat[0].svalue = strength;
> +	} else {
> +		c->strength.len = 1;
> +		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		dev_dbg(&priv->spi->dev,
> +			"%s: mon_rf_lvl %d\n", __func__, ret);
> +	}
> +
> +	result = cxd2880_read_snr(fe, &snr);
> +	if (!result) {
> +		c->cnr.len = 1;
> +		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
> +		c->cnr.stat[0].svalue = snr;
> +	} else {
> +		c->cnr.len = 1;
> +		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		dev_dbg(&priv->spi->dev, "%s: read_snr %d\n", __func__, result);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_pre_bit_err_t2(&priv->tnrdmd,
> +				&pre_bit_err,
> +				&pre_bit_count);
> +	mutex_unlock(priv->spi_mutex);
> +	if (ret == CXD2880_RESULT_OK) {
> +		c->pre_bit_error.len = 1;
> +		c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
> +		c->pre_bit_error.stat[0].uvalue = pre_bit_err;
> +		c->pre_bit_count.len = 1;
> +		c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
> +		c->pre_bit_count.stat[0].uvalue = pre_bit_count;
> +	} else {
> +		c->pre_bit_error.len = 1;
> +		c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		c->pre_bit_count.len = 1;
> +		c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		dev_dbg(&priv->spi->dev,
> +			"%s: read_bit_err_t2 %d\n", __func__, ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_post_bit_err_t2(&priv->tnrdmd,
> +				&post_bit_err, &post_bit_count);
> +	mutex_unlock(priv->spi_mutex);
> +	if (ret == CXD2880_RESULT_OK) {
> +		c->post_bit_error.len = 1;
> +		c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
> +		c->post_bit_error.stat[0].uvalue = post_bit_err;
> +		c->post_bit_count.len = 1;
> +		c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
> +		c->post_bit_count.stat[0].uvalue = post_bit_count;
> +	} else {
> +		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;
> +		dev_dbg(&priv->spi->dev,
> +			"%s: post_bit_err_t2 %d\n", __func__, ret);
> +	}
> +
> +	mutex_lock(priv->spi_mutex);
> +	ret = cxd2880_read_block_err_t2(&priv->tnrdmd,
> +					&block_err, &block_count);
> +	mutex_unlock(priv->spi_mutex);
> +	if (ret == CXD2880_RESULT_OK) {
> +		c->block_error.len = 1;
> +		c->block_error.stat[0].scale = FE_SCALE_COUNTER;
> +		c->block_error.stat[0].uvalue = block_err;
> +		c->block_count.len = 1;
> +		c->block_count.stat[0].scale = FE_SCALE_COUNTER;
> +		c->block_count.stat[0].uvalue = block_count;
> +	} else {
> +		c->block_error.len = 1;
> +		c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		c->block_count.len = 1;
> +		c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> +		dev_dbg(&priv->spi->dev,
> +			"%s: read_block_err_t2 %d\n", __func__, ret);
> +	}
> +
> +	return 0;
> +}
> +
> +static int cxd2880_get_frontend(struct dvb_frontend *fe,
> +				struct dtv_frontend_properties *props)
> +{
> +	struct cxd2880_priv *priv = NULL;
> +	int result = 0;
> +
> +	if ((!fe) || (!props)) {
> +		pr_err("%s: invalid arg.", __func__);
> +		return -EINVAL;
> +	}
> +
> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
> +
> +	dev_dbg(&priv->spi->dev, "%s: system=%d\n", __func__,
> +		fe->dtv_property_cache.delivery_system);
> +	switch (fe->dtv_property_cache.delivery_system) {
> +	case SYS_DVBT:
> +		result = cxd2880_get_frontend_t(fe, props);
> +		break;
> +	case SYS_DVBT2:
> +		result = cxd2880_get_frontend_t2(fe, props);
> +		break;

Hmm... I noticed that already when reviewing other parts of the driver...

At patch 6, you defined:


enum cxd2880_dtv_sys {
+	CXD2880_DTV_SYS_UNKNOWN,
+	CXD2880_DTV_SYS_DVBT,
+	CXD2880_DTV_SYS_DVBT2,
+	CXD2880_DTV_SYS_ISDBT,
+	CXD2880_DTV_SYS_ISDBTSB,
+	CXD2880_DTV_SYS_ISDBTMM_A,
+	CXD2880_DTV_SYS_ISDBTMM_B,
+	CXD2880_DTV_SYS_ANY
+};

So, I was expecting to see not only DVB T/T2 here (and at the tuner
driver) but also ISDB, in order to match the defined standards,
but you're setting only DVB T/T2 here, and some parts of the tuner
driver also seem to be focused only on those two standards.

So, the question is: does the hardware support ISDB? If so,
please add a /* FIXME */ note where pertinent, saying that
ISDB support should be added in the future. If, on the other 
hand, the hardware doesn't support it at all, please cleanup the
code by removing references for it.


> +	default:
> +		result = -EINVAL;
> +		break;
> +	}
> +
> +	return result;
> +}
> +
> +static enum dvbfe_algo cxd2880_get_frontend_algo(struct dvb_frontend *fe)
> +{
> +	return DVBFE_ALGO_HW;
> +}
> +
> +static struct dvb_frontend_ops cxd2880_dvbt_t2_ops;
> +
> +struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
> +				struct cxd2880_config *cfg)
> +{
> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> +	enum cxd2880_tnrdmd_chip_id chipid =
> +					CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
> +	static struct cxd2880_priv *priv;
> +	u8 data = 0;
> +
> +	if (!fe) {
> +		pr_err("%s: invalid arg.\n", __func__);
> +		return NULL;
> +	}
> +
> +	priv = kzalloc(sizeof(struct cxd2880_priv), GFP_KERNEL);
> +	if (!priv)
> +		return NULL;
> +
> +	priv->spi = cfg->spi;
> +	priv->spi_mutex = cfg->spi_mutex;
> +	priv->spi_device.spi = cfg->spi;
> +
> +	memcpy(&fe->ops, &cxd2880_dvbt_t2_ops,
> +			sizeof(struct dvb_frontend_ops));
> +
> +	ret = cxd2880_spi_device_initialize(&priv->spi_device,
> +						CXD2880_SPI_MODE_0,
> +						55000000);
> +	if (ret != CXD2880_RESULT_OK) {
> +		dev_err(&priv->spi->dev,
> +			"%s: spi_device_initialize failed. %d\n",
> +			__func__, ret);
> +		kfree(priv);
> +		return NULL;
> +	}
> +
> +	ret = cxd2880_spi_device_create_spi(&priv->cxd2880_spi,
> +					&priv->spi_device);
> +	if (ret != CXD2880_RESULT_OK) {
> +		dev_err(&priv->spi->dev,
> +			"%s: spi_device_create_spi failed. %d\n",
> +			__func__, ret);
> +		kfree(priv);
> +		return NULL;
> +	}
> +
> +	ret = cxd2880_io_spi_create(&priv->regio, &priv->cxd2880_spi, 0);
> +	if (ret != CXD2880_RESULT_OK) {
> +		dev_err(&priv->spi->dev,
> +			"%s: io_spi_create failed. %d\n", __func__, ret);
> +		kfree(priv);
> +		return NULL;
> +	}
> +	if (priv->regio.write_reg(&priv->regio, CXD2880_IO_TGT_SYS, 0x00, 0x00)
> +		!= CXD2880_RESULT_OK) {
> +		dev_err(&priv->spi->dev,
> +			"%s: set bank to 0x00 failed.\n", __func__);
> +		kfree(priv);
> +		return NULL;
> +	}
> +	if (priv->regio.read_regs(&priv->regio,
> +					CXD2880_IO_TGT_SYS, 0xFD, &data, 1)
> +					!= CXD2880_RESULT_OK) {
> +		dev_err(&priv->spi->dev,
> +			"%s: read chip id failed.\n", __func__);
> +		kfree(priv);
> +		return NULL;
> +	}
> +
> +	chipid = (enum cxd2880_tnrdmd_chip_id)data;
> +	if ((chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) &&
> +		(chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11)) {
> +		dev_err(&priv->spi->dev,
> +			"%s: chip id invalid.\n", __func__);
> +		kfree(priv);
> +		return NULL;
> +	}
> +
> +	fe->demodulator_priv = priv;
> +	dev_info(&priv->spi->dev,
> +		"CXD2880 driver version: Ver %s\n",
> +		CXD2880_TNRDMD_DRIVER_VERSION);
> +
> +	return fe;
> +}
> +EXPORT_SYMBOL(cxd2880_attach);
> +
> +static struct dvb_frontend_ops cxd2880_dvbt_t2_ops = {
> +	.info = {
> +		.name = "Sony CXD2880",
> +		.frequency_min =  174000000,
> +		.frequency_max = 862000000,
> +		.frequency_stepsize = 1000,
> +		.caps = FE_CAN_INVERSION_AUTO |
> +				FE_CAN_FEC_1_2 |
> +				FE_CAN_FEC_2_3 |
> +				FE_CAN_FEC_3_4 |
> +				FE_CAN_FEC_4_5 |
> +				FE_CAN_FEC_5_6	|
> +				FE_CAN_FEC_7_8	|
> +				FE_CAN_FEC_AUTO |
> +				FE_CAN_QPSK |
> +				FE_CAN_QAM_16 |
> +				FE_CAN_QAM_32 |
> +				FE_CAN_QAM_64 |
> +				FE_CAN_QAM_128 |
> +				FE_CAN_QAM_256 |
> +				FE_CAN_QAM_AUTO |
> +				FE_CAN_TRANSMISSION_MODE_AUTO |
> +				FE_CAN_GUARD_INTERVAL_AUTO |
> +				FE_CAN_2G_MODULATION |
> +				FE_CAN_RECOVER |
> +				FE_CAN_MUTE_TS,
> +	},
> +	.delsys = { SYS_DVBT, SYS_DVBT2 },
> +
> +	.release = cxd2880_release,
> +	.init = cxd2880_init,
> +	.sleep = cxd2880_sleep,
> +	.tune = cxd2880_tune,
> +	.set_frontend = cxd2880_set_frontend,
> +	.get_frontend = cxd2880_get_frontend,
> +	.read_status = cxd2880_read_status,
> +	.read_ber = cxd2880_read_ber,
> +	.read_signal_strength = cxd2880_read_signal_strength,
> +	.read_snr = cxd2880_read_snr,
> +	.read_ucblocks = cxd2880_read_ucblocks,
> +	.get_frontend_algo = cxd2880_get_frontend_algo,
> +};
> +
> +MODULE_DESCRIPTION(
> +"Sony CXD2880 DVB-T2/T tuner + demodulator drvier");
> +MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
> +MODULE_LICENSE("GPL v2");



Thanks,
Mauro

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-05-25  6:15   ` Takiguchi, Yasunari
  2017-06-01  1:41     ` Takiguchi, Yasunari
@ 2017-06-13 14:38     ` Mauro Carvalho Chehab
  2017-06-14  3:01       ` Takiguchi, Yasunari
  1 sibling, 1 reply; 36+ messages in thread
From: Mauro Carvalho Chehab @ 2017-06-13 14:38 UTC (permalink / raw)
  To: Takiguchi, Yasunari
  Cc: akpm, linux-kernel, devicetree, linux-media, tbird20d,
	frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS)

Hi Takiguchi-san,

Em Thu, 25 May 2017 15:15:39 +0900
"Takiguchi, Yasunari" <Yasunari.Takiguchi@sony.com> escreveu:

> Hi, all
> 
> I sent the patch series of Sony CXD2880 DVB-T2/T tuner + demodulator driver on Apr/14.
> Are there any comments, advices and review results for them?

Usually, reviewing drivers takes more time, as one needs to reserve a
long period of time for reviewing. Sorry for the delay.

> I'd like to get better understanding of current review status for our codes.

Just sent today a review. There are some things that need to be
changed in order to get it into a better shape and make it easier
to review. In particular, it should be using some Kernel internal APIs,
instead of coming with re-implementation of existing core code. That's fine. 
It is very unusual that the first contributions from a new Kernel
developer to gets everything the way as it is expected mainstream ;-)

One thing that come into my mind, besides what was already commented,
is that it seems you added an abstraction layer. We don't like such
layers very much, as it makes harder to understand the driver, usually
for very little benefit.

On this first review, I didn't actually try to understand what's
going on there. As the driver doesn't contain any comments inside,
it makes harder to understand why some things were coded using
such approach.

Thanks,
Mauro

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator
  2017-06-13 14:38     ` Mauro Carvalho Chehab
@ 2017-06-14  3:01       ` Takiguchi, Yasunari
  0 siblings, 0 replies; 36+ messages in thread
From: Takiguchi, Yasunari @ 2017-06-14  3:01 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: akpm, linux-kernel, devicetree, linux-media, tbird20d,
	frowand.list, Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	yasunari.takiguchi

Dear Mauro

Thanks for your kind review during your busy schedule.

I will confirm with your findings and discuss about them
(how to change our codes) in our team.

Regards & Thanks
Takiguchi

On 2017/06/13 23:38, Mauro Carvalho Chehab wrote:
> Hi Takiguchi-san,
> 
> Em Thu, 25 May 2017 15:15:39 +0900
> "Takiguchi, Yasunari" <Yasunari.Takiguchi@sony.com> escreveu:
> 
>> Hi, all
>>
>> I sent the patch series of Sony CXD2880 DVB-T2/T tuner + demodulator driver on Apr/14.
>> Are there any comments, advices and review results for them?
> 
> Usually, reviewing drivers takes more time, as one needs to reserve a
> long period of time for reviewing. Sorry for the delay.
> 
>> I'd like to get better understanding of current review status for our codes.
> 
> Just sent today a review. There are some things that need to be
> changed in order to get it into a better shape and make it easier
> to review. In particular, it should be using some Kernel internal APIs,
> instead of coming with re-implementation of existing core code. That's fine. 
> It is very unusual that the first contributions from a new Kernel
> developer to gets everything the way as it is expected mainstream ;-)
> 
> One thing that come into my mind, besides what was already commented,
> is that it seems you added an abstraction layer. We don't like such
> layers very much, as it makes harder to understand the driver, usually
> for very little benefit.
> 
> On this first review, I didn't actually try to understand what's
> going on there. As the driver doesn't contain any comments inside,
> it makes harder to understand why some things were coded using
> such approach.
> 
> Thanks,
> Mauro

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 08/15] [media] cxd2880: Add top level of the driver
  2017-06-13 13:30   ` Mauro Carvalho Chehab
@ 2017-06-19  7:56     ` Takiguchi, Yasunari
  2017-06-23 13:02       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 36+ messages in thread
From: Takiguchi, Yasunari @ 2017-06-19  7:56 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	yasunari.takiguchi

Thanks for your kindly reply.

We are think of how to change our driver to follow the comments now.
I reply one thing about reading BER measures at get_frontend() in this mail.

On 2017/06/13 22:30, Mauro Carvalho Chehab wrote:
> Em Fri, 14 Apr 2017 11:31:50 +0900
> <Yasunari.Takiguchi@sony.com> escreveu:
> 
>> From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
>>
>> This provides the main dvb frontend operation functions
>> for the Sony CXD2880 DVB-T2/T tuner + demodulator driver.
> 
> For now, I'll do only a quick review on patches 6-15, as there are several
> things to be ajusted on the code, from my past comments.
> 
> I'll do a better review on the next version. So, I'll focus only on
> a few random issues I see on the code below.
> 
>>
>> Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
>> Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
>> Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
>> Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
>> Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
>> Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
>> ---
>>  drivers/media/dvb-frontends/cxd2880/cxd2880_top.c | 1550 +++++++++++++++++++++
>>  1 file changed, 1550 insertions(+)
>>  create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
>>
>> diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
>> new file mode 100644
>> index 000000000000..66d78fb93a13
>> --- /dev/null
>> +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
>> @@ -0,0 +1,1550 @@
>> +/*
>> + * cxd2880_top.c
>> + * Sony CXD2880 DVB-T2/T tuner + demodulator driver
>> + *
>> + * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License as published by the
>> + * Free Software Foundation; version 2 of the License.
>> + *
>> + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
>> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
>> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
>> + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
>> + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
>> + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
>> + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
>> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
>> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
>> + *
>> + * You should have received a copy of the GNU General Public License along
>> + * with this program; if not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/spi/spi.h>
>> +
>> +#include "dvb_frontend.h"
>> +
>> +#include "cxd2880.h"
>> +#include "cxd2880_tnrdmd_mon.h"
>> +#include "cxd2880_tnrdmd_dvbt2_mon.h"
>> +#include "cxd2880_tnrdmd_dvbt_mon.h"
>> +#include "cxd2880_integ_dvbt2.h"
>> +#include "cxd2880_integ_dvbt.h"
>> +#include "cxd2880_devio_spi.h"
>> +#include "cxd2880_spi_device.h"
>> +#include "cxd2880_tnrdmd_driver_version.h"
>> +
>> +struct cxd2880_priv {
>> +	struct cxd2880_tnrdmd tnrdmd;
>> +	struct spi_device *spi;
>> +	struct cxd2880_io regio;
>> +	struct cxd2880_spi_device spi_device;
>> +	struct cxd2880_spi cxd2880_spi;
>> +	struct cxd2880_dvbt_tune_param dvbt_tune_param;
>> +	struct cxd2880_dvbt2_tune_param dvbt2_tune_param;
>> +	struct mutex *spi_mutex; /* For SPI access exclusive control */
>> +};
>> +
>> +/*
>> + * return value conversion table
>> + */
>> +static int return_tbl[] = {
>> +	0,             /* CXD2880_RESULT_OK */
>> +	-EINVAL,       /* CXD2880_RESULT_ERROR_ARG*/
>> +	-EIO,          /* CXD2880_RESULT_ERROR_IO */
>> +	-EPERM,        /* CXD2880_RESULT_ERROR_SW_STATE */
>> +	-EBUSY,        /* CXD2880_RESULT_ERROR_HW_STATE */
>> +	-ETIME,        /* CXD2880_RESULT_ERROR_TIMEOUT */
>> +	-EAGAIN,       /* CXD2880_RESULT_ERROR_UNLOCK */
>> +	-ERANGE,       /* CXD2880_RESULT_ERROR_RANGE */
>> +	-EOPNOTSUPP,   /* CXD2880_RESULT_ERROR_NOSUPPORT */
>> +	-ECANCELED,    /* CXD2880_RESULT_ERROR_CANCEL */
>> +	-EPERM,        /* CXD2880_RESULT_ERROR_OTHER */
>> +	-EOVERFLOW,    /* CXD2880_RESULT_ERROR_OVERFLOW */
>> +	0,             /* CXD2880_RESULT_OK_CONFIRM */
>> +};
> 
> Please, don't use a conversion table. That makes the driver more
> complex to be understood without any good reason. Instead, just 
> replace the values at the original enum.
> 
>> +
>> +static enum cxd2880_ret cxd2880_pre_bit_err_t(
>> +		struct cxd2880_tnrdmd *tnrdmd, u32 *pre_bit_err,
>> +		u32 *pre_bit_count)
>> +{
>> +	u8 rdata[2];
>> +
>> +	if ((!tnrdmd) || (!pre_bit_err) || (!pre_bit_count))
>> +		return CXD2880_RESULT_ERROR_ARG;
>> +
>> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
>> +		return CXD2880_RESULT_ERROR_ARG;
>> +
>> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
>> +		return CXD2880_RESULT_ERROR_SW_STATE;
>> +
>> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
>> +		return CXD2880_RESULT_ERROR_SW_STATE;
>> +
>> +	if (slvt_freeze_reg(tnrdmd) != CXD2880_RESULT_OK)
>> +		return CXD2880_RESULT_ERROR_IO;
>> +
>> +	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x00, 0x10) != CXD2880_RESULT_OK) {
>> +		slvt_unfreeze_reg(tnrdmd);
>> +		return CXD2880_RESULT_ERROR_IO;
>> +	}
>> +
>> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x39, rdata, 1) != CXD2880_RESULT_OK) {
>> +		slvt_unfreeze_reg(tnrdmd);
>> +		return CXD2880_RESULT_ERROR_IO;
>> +	}
>> +
>> +	if ((rdata[0] & 0x01) == 0) {
>> +		slvt_unfreeze_reg(tnrdmd);
>> +		return CXD2880_RESULT_ERROR_HW_STATE;
>> +	}
>> +
>> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x22, rdata, 2) != CXD2880_RESULT_OK) {
>> +		slvt_unfreeze_reg(tnrdmd);
>> +		return CXD2880_RESULT_ERROR_IO;
>> +	}
>> +
>> +	*pre_bit_err = (rdata[0] << 8) | rdata[1];
>> +
>> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x6F, rdata, 1) != CXD2880_RESULT_OK) {
>> +		slvt_unfreeze_reg(tnrdmd);
>> +		return CXD2880_RESULT_ERROR_IO;
>> +	}
>> +
>> +	slvt_unfreeze_reg(tnrdmd);
>> +
>> +	*pre_bit_count = ((rdata[0] & 0x07) == 0) ?
>> +			256 : (0x1000 << (rdata[0] & 0x07));
>> +
>> +	return CXD2880_RESULT_OK;
>> +}
>> +
>> +static enum cxd2880_ret cxd2880_pre_bit_err_t2(
>> +		struct cxd2880_tnrdmd *tnrdmd, u32 *pre_bit_err,
>> +		u32 *pre_bit_count)
>> +{
>> +	u32 period_exp = 0;
>> +	u32 n_ldpc = 0;
>> +	u8 data[5];
>> +
>> +	if ((!tnrdmd) || (!pre_bit_err) || (!pre_bit_count))
>> +		return CXD2880_RESULT_ERROR_ARG;
>> +
>> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
>> +		return CXD2880_RESULT_ERROR_ARG;
>> +
>> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
>> +		return CXD2880_RESULT_ERROR_SW_STATE;
>> +
>> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
>> +		return CXD2880_RESULT_ERROR_SW_STATE;
>> +
>> +	if (slvt_freeze_reg(tnrdmd) != CXD2880_RESULT_OK)
>> +		return CXD2880_RESULT_ERROR_IO;
>> +
>> +	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x00, 0x0B) != CXD2880_RESULT_OK) {
>> +		slvt_unfreeze_reg(tnrdmd);
>> +		return CXD2880_RESULT_ERROR_IO;
>> +	}
>> +
>> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x3C, data, sizeof(data))
>> +				 != CXD2880_RESULT_OK) {
>> +		slvt_unfreeze_reg(tnrdmd);
>> +		return CXD2880_RESULT_ERROR_IO;
>> +	}
>> +
>> +	if (!(data[0] & 0x01)) {
>> +		slvt_unfreeze_reg(tnrdmd);
>> +		return CXD2880_RESULT_ERROR_HW_STATE;
>> +	}
>> +	*pre_bit_err =
>> +	((data[1] & 0x0F) << 24) | (data[2] << 16) | (data[3] << 8) | data[4];
>> +
>> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0xA0, data, 1) != CXD2880_RESULT_OK) {
>> +		slvt_unfreeze_reg(tnrdmd);
>> +		return CXD2880_RESULT_ERROR_IO;
>> +	}
>> +
>> +	if (((enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03)) ==
>> +	    CXD2880_DVBT2_FEC_LDPC_16K)
>> +		n_ldpc = 16200;
>> +	else
>> +		n_ldpc = 64800;
>> +	slvt_unfreeze_reg(tnrdmd);
>> +
>> +	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x00, 0x20) != CXD2880_RESULT_OK)
>> +		return CXD2880_RESULT_ERROR_IO;
>> +
>> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x6F, data, 1) != CXD2880_RESULT_OK)
>> +		return CXD2880_RESULT_ERROR_IO;
>> +
>> +	period_exp = data[0] & 0x0F;
>> +
>> +	*pre_bit_count = (1U << period_exp) * n_ldpc;
>> +
>> +	return CXD2880_RESULT_OK;
>> +}
>> +
>> +static enum cxd2880_ret cxd2880_post_bit_err_t(struct cxd2880_tnrdmd *tnrdmd,
>> +						u32 *post_bit_err,
>> +						u32 *post_bit_count)
>> +{
>> +	u8 rdata[3];
>> +	u32 bit_error = 0;
>> +	u32 period_exp = 0;
>> +
>> +	if ((!tnrdmd) || (!post_bit_err) || (!post_bit_count))
>> +		return CXD2880_RESULT_ERROR_ARG;
>> +
>> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
>> +		return CXD2880_RESULT_ERROR_ARG;
>> +
>> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
>> +		return CXD2880_RESULT_ERROR_SW_STATE;
>> +
>> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
>> +		return CXD2880_RESULT_ERROR_SW_STATE;
>> +
>> +	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x00, 0x0D) != CXD2880_RESULT_OK)
>> +		return CXD2880_RESULT_ERROR_IO;
>> +
>> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x15, rdata, 3) != CXD2880_RESULT_OK)
>> +		return CXD2880_RESULT_ERROR_IO;
>> +
>> +	if ((rdata[0] & 0x40) == 0)
>> +		return CXD2880_RESULT_ERROR_HW_STATE;
>> +
>> +	*post_bit_err = ((rdata[0] & 0x3F) << 16) | (rdata[1] << 8) | rdata[2];
>> +
>> +	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x00, 0x10) != CXD2880_RESULT_OK)
>> +		return CXD2880_RESULT_ERROR_IO;
>> +
>> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x60, rdata, 1) != CXD2880_RESULT_OK)
>> +		return CXD2880_RESULT_ERROR_IO;
>> +
>> +	period_exp = (rdata[0] & 0x1F);
>> +
>> +	if ((period_exp <= 11) && (bit_error > (1U << period_exp) * 204 * 8))
>> +		return CXD2880_RESULT_ERROR_HW_STATE;
>> +
>> +	if (period_exp == 11)
>> +		*post_bit_count = 3342336;
>> +	else
>> +		*post_bit_count = (1U << period_exp) * 204 * 81;
>> +
>> +	return CXD2880_RESULT_OK;
>> +}
>> +
>> +static enum cxd2880_ret cxd2880_post_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd,
>> +						u32 *post_bit_err,
>> +						u32 *post_bit_count)
>> +{
>> +	u32 period_exp = 0;
>> +	u32 n_bch = 0;
>> +
>> +	if ((!tnrdmd) || (!post_bit_err) || (!post_bit_count))
>> +		return CXD2880_RESULT_ERROR_ARG;
>> +
>> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
>> +		return CXD2880_RESULT_ERROR_ARG;
>> +
>> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
>> +		return CXD2880_RESULT_ERROR_SW_STATE;
>> +
>> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
>> +		return CXD2880_RESULT_ERROR_SW_STATE;
>> +
>> +	{
>> +		u8 data[3];
>> +		enum cxd2880_dvbt2_plp_fec plp_fec_type =
>> +			CXD2880_DVBT2_FEC_LDPC_16K;
>> +		enum cxd2880_dvbt2_plp_code_rate plp_code_rate =
>> +			CXD2880_DVBT2_R1_2;
>> +
>> +		static const u16 n_bch_bits_lookup[2][8] = {
>> +			{7200, 9720, 10800, 11880, 12600, 13320, 5400, 6480},
>> +			{32400, 38880, 43200, 48600, 51840, 54000, 21600, 25920}
>> +		};
>> +
>> +		if (slvt_freeze_reg(tnrdmd) != CXD2880_RESULT_OK)
>> +			return CXD2880_RESULT_ERROR_IO;
>> +
>> +		if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +					0x00, 0x0B) != CXD2880_RESULT_OK) {
>> +			slvt_unfreeze_reg(tnrdmd);
>> +			return CXD2880_RESULT_ERROR_IO;
>> +		}
>> +
>> +		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +					 0x15, data, 3) != CXD2880_RESULT_OK) {
>> +			slvt_unfreeze_reg(tnrdmd);
>> +			return CXD2880_RESULT_ERROR_IO;
>> +		}
>> +
>> +		if (!(data[0] & 0x40)) {
>> +			slvt_unfreeze_reg(tnrdmd);
>> +			return CXD2880_RESULT_ERROR_HW_STATE;
>> +		}
>> +
>> +		*post_bit_err =
>> +			((data[0] & 0x3F) << 16) | (data[1] << 8) | data[2];
>> +
>> +		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +					0x9D, data, 1) != CXD2880_RESULT_OK) {
>> +			slvt_unfreeze_reg(tnrdmd);
>> +			return CXD2880_RESULT_ERROR_IO;
>> +		}
>> +
>> +		plp_code_rate =
>> +		(enum cxd2880_dvbt2_plp_code_rate)(data[0] & 0x07);
>> +
>> +		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +					0xA0, data, 1) != CXD2880_RESULT_OK) {
>> +			slvt_unfreeze_reg(tnrdmd);
>> +			return CXD2880_RESULT_ERROR_IO;
>> +		}
>> +
>> +		plp_fec_type = (enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03);
>> +
>> +		slvt_unfreeze_reg(tnrdmd);
>> +
>> +		if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +					0x00, 0x20) != CXD2880_RESULT_OK)
>> +			return CXD2880_RESULT_ERROR_IO;
>> +
>> +		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +					0x72, data, 1) != CXD2880_RESULT_OK)
>> +			return CXD2880_RESULT_ERROR_IO;
>> +
>> +		period_exp = data[0] & 0x0F;
>> +
>> +		if ((plp_fec_type > CXD2880_DVBT2_FEC_LDPC_64K) ||
>> +			(plp_code_rate > CXD2880_DVBT2_R2_5))
>> +			return CXD2880_RESULT_ERROR_HW_STATE;
>> +
>> +		n_bch = n_bch_bits_lookup[plp_fec_type][plp_code_rate];
>> +	}
>> +
>> +	if (*post_bit_err > ((1U << period_exp) * n_bch))
>> +		return CXD2880_RESULT_ERROR_HW_STATE;
>> +
>> +	*post_bit_count = (1U << period_exp) * n_bch;
>> +
>> +	return CXD2880_RESULT_OK;
>> +}
>> +
>> +static enum cxd2880_ret cxd2880_read_block_err_t(
>> +					struct cxd2880_tnrdmd *tnrdmd,
>> +					u32 *block_err,
>> +					u32 *block_count)
>> +{
>> +	u8 rdata[3];
>> +
>> +	if ((!tnrdmd) || (!block_err) || (!block_count))
>> +		return CXD2880_RESULT_ERROR_ARG;
>> +
>> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
>> +		return CXD2880_RESULT_ERROR_ARG;
>> +
>> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
>> +		return CXD2880_RESULT_ERROR_SW_STATE;
>> +
>> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
>> +		return CXD2880_RESULT_ERROR_SW_STATE;
>> +
>> +	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x00, 0x0D) != CXD2880_RESULT_OK)
>> +		return CXD2880_RESULT_ERROR_IO;
>> +
>> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x18, rdata, 3) != CXD2880_RESULT_OK)
>> +		return CXD2880_RESULT_ERROR_IO;
>> +
>> +	if ((rdata[0] & 0x01) == 0)
>> +		return CXD2880_RESULT_ERROR_HW_STATE;
>> +
>> +	*block_err = (rdata[1] << 8) | rdata[2];
>> +
>> +	if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x00, 0x10) != CXD2880_RESULT_OK)
>> +		return CXD2880_RESULT_ERROR_IO;
>> +
>> +	if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +				0x5C, rdata, 1) != CXD2880_RESULT_OK)
>> +		return CXD2880_RESULT_ERROR_IO;
>> +
>> +	*block_count = 1U << (rdata[0] & 0x0F);
>> +
>> +	if ((*block_count == 0) || (*block_err > *block_count))
>> +		return CXD2880_RESULT_ERROR_HW_STATE;
>> +
>> +	return CXD2880_RESULT_OK;
>> +}
>> +
>> +static enum cxd2880_ret cxd2880_read_block_err_t2(
>> +					struct cxd2880_tnrdmd *tnrdmd,
>> +					u32 *block_err,
>> +					u32 *block_count)
>> +{
>> +	if ((!tnrdmd) || (!block_err) || (!block_count))
>> +		return CXD2880_RESULT_ERROR_ARG;
>> +
>> +	if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
>> +		return CXD2880_RESULT_ERROR_ARG;
>> +
>> +	if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
>> +		return CXD2880_RESULT_ERROR_SW_STATE;
>> +	if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
>> +		return CXD2880_RESULT_ERROR_SW_STATE;
>> +
>> +	{
>> +		u8 rdata[3];
>> +
>> +		if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +					0x00, 0x0B) != CXD2880_RESULT_OK)
>> +			return CXD2880_RESULT_ERROR_IO;
>> +
>> +		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +					0x18, rdata, 3) != CXD2880_RESULT_OK)
>> +			return CXD2880_RESULT_ERROR_IO;
>> +
>> +		if ((rdata[0] & 0x01) == 0)
>> +			return CXD2880_RESULT_ERROR_HW_STATE;
>> +
>> +		*block_err = (rdata[1] << 8) | rdata[2];
>> +
>> +		if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +					0x00, 0x24) != CXD2880_RESULT_OK)
>> +			return CXD2880_RESULT_ERROR_IO;
>> +
>> +		if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
>> +					0xDC, rdata, 1) != CXD2880_RESULT_OK)
>> +			return CXD2880_RESULT_ERROR_IO;
>> +
>> +		*block_count = 1U << (rdata[0] & 0x0F);
>> +	}
>> +
>> +	if ((*block_count == 0) || (*block_err > *block_count))
>> +		return CXD2880_RESULT_ERROR_HW_STATE;
>> +
>> +	return CXD2880_RESULT_OK;
>> +}
>> +
>> +static void cxd2880_release(struct dvb_frontend *fe)
>> +{
>> +	struct cxd2880_priv *priv = NULL;
>> +
>> +	if (!fe) {
>> +		pr_err("%s: invalid arg.\n", __func__);
>> +		return;
>> +	}
>> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
>> +	kfree(priv);
>> +}
>> +
>> +static int cxd2880_init(struct dvb_frontend *fe)
>> +{
>> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
>> +	struct cxd2880_priv *priv = NULL;
>> +	struct cxd2880_tnrdmd_create_param create_param;
>> +
>> +	if (!fe) {
>> +		pr_err("%s: invalid arg.\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
>> +
>> +	create_param.ts_output_if = CXD2880_TNRDMD_TSOUT_IF_SPI;
>> +	create_param.xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_NONE;
>> +	create_param.en_internal_ldo = 1;
>> +	create_param.xosc_cap = 18;
>> +	create_param.xosc_i = 8;
>> +	create_param.stationary_use = 1;
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	if (priv->tnrdmd.io != &priv->regio) {
>> +		ret = cxd2880_tnrdmd_create(&priv->tnrdmd,
>> +				&priv->regio, &create_param);
>> +		if (ret != CXD2880_RESULT_OK) {
>> +			mutex_unlock(priv->spi_mutex);
>> +			dev_info(&priv->spi->dev,
>> +				"%s: cxd2880 tnrdmd create failed %d\n",
>> +				__func__, ret);
>> +			return return_tbl[ret];
>> +		}
>> +	}
>> +	ret = cxd2880_integ_init(&priv->tnrdmd);
>> +	if (ret != CXD2880_RESULT_OK) {
>> +		mutex_unlock(priv->spi_mutex);
>> +		dev_err(&priv->spi->dev, "%s: cxd2880 integ init failed %d\n",
>> +				__func__, ret);
>> +		return return_tbl[ret];
>> +	}
>> +	mutex_unlock(priv->spi_mutex);
>> +
>> +	dev_dbg(&priv->spi->dev, "%s: OK.\n", __func__);
>> +
>> +	return return_tbl[ret];
>> +}
>> +
>> +static int cxd2880_sleep(struct dvb_frontend *fe)
>> +{
>> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
>> +	struct cxd2880_priv *priv = NULL;
>> +
>> +	if (!fe) {
>> +		pr_err("%s: inavlid arg\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_tnrdmd_sleep(&priv->tnrdmd);
>> +	mutex_unlock(priv->spi_mutex);
>> +
>> +	dev_dbg(&priv->spi->dev, "%s: tnrdmd_sleep ret %d\n",
>> +		__func__, ret);
>> +
>> +	return return_tbl[ret];
>> +}
>> +
>> +static int cxd2880_read_signal_strength(struct dvb_frontend *fe,
>> +				u16 *strength)
>> +{
>> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
>> +	struct cxd2880_priv *priv = NULL;
>> +	struct dtv_frontend_properties *c = NULL;
>> +	int level = 0;
>> +
>> +	if ((!fe) || (!strength)) {
>> +		pr_err("%s: inavlid arg\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
>> +	c = &fe->dtv_property_cache;
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	if ((c->delivery_system == SYS_DVBT) ||
>> +		(c->delivery_system == SYS_DVBT2)) {
>> +		ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &level);
>> +	} else {
>> +		dev_dbg(&priv->spi->dev, "%s: invalid system\n", __func__);
>> +		mutex_unlock(priv->spi_mutex);
>> +		return -EINVAL;
>> +	}
>> +	mutex_unlock(priv->spi_mutex);
>> +
>> +	level /= 125;
>> +	/* -105dBm - -30dBm (-105000/125 = -840, -30000/125 = -240 */
>> +	level = clamp(level, -840, -240);
>> +	/* scale value to 0x0000-0xFFFF */
>> +	*strength = (u16)(((level + 840) * 0xFFFF) / (-240 + 840));
>> +
>> +	if (ret != CXD2880_RESULT_OK)
>> +		dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
>> +
>> +	return return_tbl[ret];
>> +}
>> +
>> +static int cxd2880_read_snr(struct dvb_frontend *fe, u16 *snr)
>> +{
>> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
>> +	int snrvalue = 0;
>> +	struct cxd2880_priv *priv = NULL;
>> +	struct dtv_frontend_properties *c = NULL;
>> +
>> +	if ((!fe) || (!snr)) {
>> +		pr_err("%s: inavlid arg\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
>> +	c = &fe->dtv_property_cache;
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	if (c->delivery_system == SYS_DVBT) {
>> +		ret = cxd2880_tnrdmd_dvbt_mon_snr(&priv->tnrdmd,
>> +						&snrvalue);
>> +	} else if (c->delivery_system == SYS_DVBT2) {
>> +		ret = cxd2880_tnrdmd_dvbt2_mon_snr(&priv->tnrdmd,
>> +						&snrvalue);
>> +	} else {
>> +		dev_err(&priv->spi->dev, "%s: invalid system\n", __func__);
>> +		mutex_unlock(priv->spi_mutex);
>> +		return -EINVAL;
>> +	}
>> +	mutex_unlock(priv->spi_mutex);
>> +
>> +	if (snrvalue < 0)
>> +		snrvalue = 0;
>> +	*snr = (u16)snrvalue;
>> +
>> +	if (ret != CXD2880_RESULT_OK)
>> +		dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
>> +
>> +	return return_tbl[ret];
>> +}
>> +
>> +static int cxd2880_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
>> +{
>> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
>> +	struct cxd2880_priv *priv = NULL;
>> +	struct dtv_frontend_properties *c = NULL;
>> +
>> +	if ((!fe) || (!ucblocks)) {
>> +		pr_err("%s: inavlid arg\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
>> +	c = &fe->dtv_property_cache;
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	if (c->delivery_system == SYS_DVBT) {
>> +		ret = cxd2880_tnrdmd_dvbt_mon_packet_error_number(
>> +								&priv->tnrdmd,
>> +								ucblocks);
>> +	} else if (c->delivery_system == SYS_DVBT2) {
>> +		ret = cxd2880_tnrdmd_dvbt2_mon_packet_error_number(
>> +								&priv->tnrdmd,
>> +								ucblocks);
>> +	} else {
>> +		dev_err(&priv->spi->dev, "%s: invlaid system\n", __func__);
>> +		mutex_unlock(priv->spi_mutex);
>> +		return -EINVAL;
>> +	}
>> +	mutex_unlock(priv->spi_mutex);
>> +
>> +	if (ret != CXD2880_RESULT_OK)
>> +		dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
>> +
>> +	return return_tbl[ret];
>> +}
>> +
>> +static int cxd2880_read_ber(struct dvb_frontend *fe, u32 *ber)
>> +{
>> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
>> +	struct cxd2880_priv *priv = NULL;
>> +	struct dtv_frontend_properties *c = NULL;
>> +
>> +	if ((!fe) || (!ber)) {
>> +		pr_err("%s: inavlid arg\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
>> +	c = &fe->dtv_property_cache;
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	if (c->delivery_system == SYS_DVBT) {
>> +		ret = cxd2880_tnrdmd_dvbt_mon_pre_rsber(&priv->tnrdmd,
>> +						ber);
>> +		/* x100 to change unit.(10^7 -> 10^9 */
>> +		*ber *= 100;
>> +	} else if (c->delivery_system == SYS_DVBT2) {
>> +		ret = cxd2880_tnrdmd_dvbt2_mon_pre_bchber(&priv->tnrdmd,
>> +						ber);
>> +	} else {
>> +		dev_err(&priv->spi->dev, "%s: invlaid system\n", __func__);
>> +		mutex_unlock(priv->spi_mutex);
>> +		return -EINVAL;
>> +	}
>> +	mutex_unlock(priv->spi_mutex);
>> +
>> +	if (ret != CXD2880_RESULT_OK)
>> +		dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
>> +
>> +	return return_tbl[ret];
>> +}
>> +
>> +static int cxd2880_set_frontend(struct dvb_frontend *fe)
>> +{
>> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
>> +	struct dtv_frontend_properties *c;
>> +	struct cxd2880_priv *priv;
>> +	enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
>> +
>> +	if (!fe) {
>> +		pr_err("%s: inavlid arg\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
>> +	c = &fe->dtv_property_cache;
>> +
>> +	switch (c->bandwidth_hz) {
>> +	case 1712000:
>> +		bw = CXD2880_DTV_BW_1_7_MHZ;
>> +		break;
>> +	case 5000000:
>> +		bw = CXD2880_DTV_BW_5_MHZ;
>> +		break;
>> +	case 6000000:
>> +		bw = CXD2880_DTV_BW_6_MHZ;
>> +		break;
>> +	case 7000000:
>> +		bw = CXD2880_DTV_BW_7_MHZ;
>> +		break;
>> +	case 8000000:
>> +		bw = CXD2880_DTV_BW_8_MHZ;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	dev_info(&priv->spi->dev, "%s: sys:%d freq:%d bw:%d\n", __func__,
>> +			c->delivery_system, c->frequency, bw);
>> +	mutex_lock(priv->spi_mutex);
>> +	if (c->delivery_system == SYS_DVBT) {
>> +		priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT;
>> +		priv->dvbt_tune_param.center_freq_khz = c->frequency / 1000;
>> +		priv->dvbt_tune_param.bandwidth = bw;
>> +		priv->dvbt_tune_param.profile = CXD2880_DVBT_PROFILE_HP;
>> +		ret = cxd2880_integ_dvbt_tune(&priv->tnrdmd,
>> +						&priv->dvbt_tune_param);
>> +	} else if (c->delivery_system == SYS_DVBT2) {
>> +		priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT2;
>> +		priv->dvbt2_tune_param.center_freq_khz = c->frequency / 1000;
>> +		priv->dvbt2_tune_param.bandwidth = bw;
>> +		priv->dvbt2_tune_param.data_plp_id = (u16)c->stream_id;
>> +		ret = cxd2880_integ_dvbt2_tune(&priv->tnrdmd,
>> +						&priv->dvbt2_tune_param);
>> +	} else {
>> +		dev_err(&priv->spi->dev, "%s: invalid system\n", __func__);
>> +		mutex_unlock(priv->spi_mutex);
>> +		return -EINVAL;
>> +	}
>> +	mutex_unlock(priv->spi_mutex);
>> +	dev_info(&priv->spi->dev, "%s: tune result %d\n", __func__, ret);
>> +
>> +	return return_tbl[ret];
>> +}
>> +
>> +static int cxd2880_read_status(struct dvb_frontend *fe,
>> +				enum fe_status *status)
>> +{
>> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
>> +	u8 sync = 0;
>> +	u8 lock = 0;
>> +	u8 unlock = 0;
>> +	struct cxd2880_priv *priv = NULL;
>> +	struct dtv_frontend_properties *c = NULL;
>> +
>> +	if ((!fe) || (!status)) {
>> +		pr_err("%s: invalid arg\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
>> +	c = &fe->dtv_property_cache;
>> +	*status = 0;
>> +
>> +	if (priv->tnrdmd.state == CXD2880_TNRDMD_STATE_ACTIVE) {
>> +		mutex_lock(priv->spi_mutex);
>> +		if (c->delivery_system == SYS_DVBT) {
>> +			ret = cxd2880_tnrdmd_dvbt_mon_sync_stat(
>> +							&priv->tnrdmd,
>> +							&sync,
>> +							&lock,
>> +							&unlock);
>> +		} else if (c->delivery_system == SYS_DVBT2) {
>> +			ret = cxd2880_tnrdmd_dvbt2_mon_sync_stat(
>> +							&priv->tnrdmd,
>> +							&sync,
>> +							&lock,
>> +							&unlock);
>> +		} else {
>> +			dev_err(&priv->spi->dev,
>> +				"%s: invlaid system", __func__);
>> +			mutex_unlock(priv->spi_mutex);
>> +			return -EINVAL;
>> +		}
>> +
>> +		mutex_unlock(priv->spi_mutex);
>> +		if (ret != CXD2880_RESULT_OK) {
>> +			dev_err(&priv->spi->dev, "%s: failed. sys = %d\n",
>> +				__func__, priv->tnrdmd.sys);
>> +			return  return_tbl[ret];
>> +		}
>> +
>> +		if (sync == 6) {
>> +			*status = FE_HAS_SIGNAL |
>> +					FE_HAS_CARRIER;
>> +		}
>> +		if (lock)
>> +			*status |= FE_HAS_VITERBI |
>> +					FE_HAS_SYNC |
>> +					FE_HAS_LOCK;
>> +	}
>> +
>> +	dev_dbg(&priv->spi->dev, "%s: status %d result %d\n", __func__,
>> +		*status, ret);
>> +
>> +	return  return_tbl[CXD2880_RESULT_OK];
>> +}
>> +
>> +static int cxd2880_tune(struct dvb_frontend *fe,
>> +			bool retune,
>> +			unsigned int mode_flags,
>> +			unsigned int *delay,
>> +			enum fe_status *status)
>> +{
>> +	int ret = 0;
>> +
>> +	if ((!fe) || (!delay) || (!status)) {
>> +		pr_err("%s: invalid arg.", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (retune) {
>> +		ret = cxd2880_set_frontend(fe);
>> +		if (ret) {
>> +			pr_err("%s: cxd2880_set_frontend failed %d\n",
>> +				__func__, ret);
>> +			return ret;
>> +		}
>> +	}
>> +
>> +	*delay = HZ / 5;
>> +
>> +	return cxd2880_read_status(fe, status);
>> +}
>> +
>> +static int cxd2880_get_frontend_t(struct dvb_frontend *fe,
>> +				struct dtv_frontend_properties *c)
>> +{
>> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
>> +	int result = 0;
>> +	struct cxd2880_priv *priv = NULL;
>> +	enum cxd2880_dvbt_mode mode = CXD2880_DVBT_MODE_2K;
>> +	enum cxd2880_dvbt_guard guard = CXD2880_DVBT_GUARD_1_32;
>> +	struct cxd2880_dvbt_tpsinfo tps;
>> +	enum cxd2880_tnrdmd_spectrum_sense sense;
>> +	u16 snr = 0;
>> +	int strength = 0;
>> +	u32 pre_bit_err = 0, pre_bit_count = 0;
>> +	u32 post_bit_err = 0, post_bit_count = 0;
>> +	u32 block_err = 0, block_count = 0;
>> +
>> +	if ((!fe) || (!c)) {
>> +		pr_err("%s: invalid arg\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_tnrdmd_dvbt_mon_mode_guard(&priv->tnrdmd,
>> +						 &mode, &guard);
>> +	mutex_unlock(priv->spi_mutex);
>> +	if (ret == CXD2880_RESULT_OK) {
>> +		switch (mode) {
>> +		case CXD2880_DVBT_MODE_2K:
>> +			c->transmission_mode = TRANSMISSION_MODE_2K;
>> +			break;
>> +		case CXD2880_DVBT_MODE_8K:
>> +			c->transmission_mode = TRANSMISSION_MODE_8K;
>> +			break;
>> +		default:
>> +			c->transmission_mode = TRANSMISSION_MODE_2K;
>> +			dev_err(&priv->spi->dev, "%s: get invalid mode %d\n",
>> +					__func__, mode);
>> +			break;
>> +		}
>> +		switch (guard) {
>> +		case CXD2880_DVBT_GUARD_1_32:
>> +			c->guard_interval = GUARD_INTERVAL_1_32;
>> +			break;
>> +		case CXD2880_DVBT_GUARD_1_16:
>> +			c->guard_interval = GUARD_INTERVAL_1_16;
>> +			break;
>> +		case CXD2880_DVBT_GUARD_1_8:
>> +			c->guard_interval = GUARD_INTERVAL_1_8;
>> +			break;
>> +		case CXD2880_DVBT_GUARD_1_4:
>> +			c->guard_interval = GUARD_INTERVAL_1_4;
>> +			break;
>> +		default:
>> +			c->guard_interval = GUARD_INTERVAL_1_32;
>> +			dev_err(&priv->spi->dev, "%s: get invalid guard %d\n",
>> +					__func__, guard);
>> +			break;
>> +		}
>> +	} else {
>> +		c->transmission_mode = TRANSMISSION_MODE_2K;
>> +		c->guard_interval = GUARD_INTERVAL_1_32;
>> +		dev_dbg(&priv->spi->dev,
>> +			"%s: ModeGuard err %d\n", __func__, ret);
>> +	}
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, &tps);
>> +	mutex_unlock(priv->spi_mutex);
>> +	if (ret == CXD2880_RESULT_OK) {
>> +		switch (tps.hierarchy) {
>> +		case CXD2880_DVBT_HIERARCHY_NON:
>> +			c->hierarchy = HIERARCHY_NONE;
>> +			break;
>> +		case CXD2880_DVBT_HIERARCHY_1:
>> +			c->hierarchy = HIERARCHY_1;
>> +			break;
>> +		case CXD2880_DVBT_HIERARCHY_2:
>> +			c->hierarchy = HIERARCHY_2;
>> +			break;
>> +		case CXD2880_DVBT_HIERARCHY_4:
>> +			c->hierarchy = HIERARCHY_4;
>> +			break;
>> +		default:
>> +			c->hierarchy = HIERARCHY_NONE;
>> +			dev_err(&priv->spi->dev,
>> +				"%s: TPSInfo hierarchy invalid %d\n",
>> +				__func__, tps.hierarchy);
>> +			break;
>> +		}
>> +
>> +		switch (tps.rate_hp) {
>> +		case CXD2880_DVBT_CODERATE_1_2:
>> +			c->code_rate_HP = FEC_1_2;
>> +			break;
>> +		case CXD2880_DVBT_CODERATE_2_3:
>> +			c->code_rate_HP = FEC_2_3;
>> +			break;
>> +		case CXD2880_DVBT_CODERATE_3_4:
>> +			c->code_rate_HP = FEC_3_4;
>> +			break;
>> +		case CXD2880_DVBT_CODERATE_5_6:
>> +			c->code_rate_HP = FEC_5_6;
>> +			break;
>> +		case CXD2880_DVBT_CODERATE_7_8:
>> +			c->code_rate_HP = FEC_7_8;
>> +			break;
>> +		default:
>> +			c->code_rate_HP = FEC_NONE;
>> +			dev_err(&priv->spi->dev,
>> +				"%s: TPSInfo rateHP invalid %d\n",
>> +				__func__, tps.rate_hp);
>> +			break;
>> +		}
>> +		switch (tps.rate_lp) {
>> +		case CXD2880_DVBT_CODERATE_1_2:
>> +			c->code_rate_LP = FEC_1_2;
>> +			break;
>> +		case CXD2880_DVBT_CODERATE_2_3:
>> +			c->code_rate_LP = FEC_2_3;
>> +			break;
>> +		case CXD2880_DVBT_CODERATE_3_4:
>> +			c->code_rate_LP = FEC_3_4;
>> +			break;
>> +		case CXD2880_DVBT_CODERATE_5_6:
>> +			c->code_rate_LP = FEC_5_6;
>> +			break;
>> +		case CXD2880_DVBT_CODERATE_7_8:
>> +			c->code_rate_LP = FEC_7_8;
>> +			break;
>> +		default:
>> +			c->code_rate_LP = FEC_NONE;
>> +			dev_err(&priv->spi->dev,
>> +				"%s: TPSInfo rateLP invalid %d\n",
>> +				__func__, tps.rate_lp);
>> +			break;
>> +		}
>> +		switch (tps.constellation) {
>> +		case CXD2880_DVBT_CONSTELLATION_QPSK:
>> +			c->modulation = QPSK;
>> +			break;
>> +		case CXD2880_DVBT_CONSTELLATION_16QAM:
>> +			c->modulation = QAM_16;
>> +			break;
>> +		case CXD2880_DVBT_CONSTELLATION_64QAM:
>> +			c->modulation = QAM_64;
>> +			break;
>> +		default:
>> +			c->modulation = QPSK;
>> +			dev_err(&priv->spi->dev,
>> +				"%s: TPSInfo constellation invalid %d\n",
>> +				__func__, tps.constellation);
>> +			break;
>> +		}
>> +	} else {
>> +		c->hierarchy = HIERARCHY_NONE;
>> +		c->code_rate_HP = FEC_NONE;
>> +		c->code_rate_LP = FEC_NONE;
>> +		c->modulation = QPSK;
>> +		dev_dbg(&priv->spi->dev,
>> +			"%s: TPS info err %d\n", __func__, ret);
>> +	}
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(&priv->tnrdmd, &sense);
>> +	mutex_unlock(priv->spi_mutex);
>> +	if (ret == CXD2880_RESULT_OK) {
>> +		switch (sense) {
>> +		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
>> +			c->inversion = INVERSION_OFF;
>> +			break;
>> +		case CXD2880_TNRDMD_SPECTRUM_INV:
>> +			c->inversion = INVERSION_ON;
>> +			break;
>> +		default:
>> +			c->inversion = INVERSION_OFF;
>> +			dev_err(&priv->spi->dev,
>> +				"%s: spectrum sense invalid %d\n",
>> +				__func__, sense);
>> +			break;
>> +		}
>> +	} else {
>> +		c->inversion = INVERSION_OFF;
>> +		dev_dbg(&priv->spi->dev,
>> +			"%s: spectrum_sense %d\n", __func__, ret);
>> +	}
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
>> +	mutex_unlock(priv->spi_mutex);
>> +	if (ret == CXD2880_RESULT_OK) {
>> +		c->strength.len = 1;
>> +		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
>> +		c->strength.stat[0].svalue = strength;
>> +	} else {
>> +		c->strength.len = 1;
>> +		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
>> +		dev_dbg(&priv->spi->dev, "%s: mon_rf_lvl %d\n",
>> +			__func__, result);
>> +	}
>> +
>> +	result = cxd2880_read_snr(fe, &snr);
>> +	if (!result) {
>> +		c->cnr.len = 1;
>> +		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
>> +		c->cnr.stat[0].svalue = snr;
>> +	} else {
>> +		c->cnr.len = 1;
>> +		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
>> +		dev_dbg(&priv->spi->dev, "%s: read_snr %d\n", __func__, result);
>> +	}
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_pre_bit_err_t(&priv->tnrdmd, &pre_bit_err,
>> +					&pre_bit_count);
> 
> Hmm... reading BER-based measures at get_frontend() may not be
> the best idea, depending on how the hardware works.
> 
> I mean, you need to be sure that, if userspace is calling it too
> often, it won't affect the hardware or the measurement.
> 
> On other drivers, where each call generates an I2C access,
> we do that by moving the code that actually call the hardware
> at get status, and we use jiffies to be sure that it won't be
> called too often.
> 
> I dunno if, on your SPI design, this would be an issue or not.

CXD2880 IC can accept frequently counter register access by SPI.
Even if such access occurred, it will not affect to IC behavior.
We checked Sony demodulator IC driver code, dvb_frontends/cxd2841er.c and it reads register information
when get_frontend called.

In fact, CXD2880 IC do NOT have bit error/packet error counter function to achieve DVB framework API completely.
Sorry for long explanation, but I'll write in detail.

DTV_STAT_PRE_ERROR_BIT_COUNT
DTV_STAT_PRE_TOTAL_BIT_COUNT
DTV_STAT_POST_ERROR_BIT_COUNT
DTV_STAT_POST_TOTAL_BIT_COUNT
DTV_STAT_ERROR_BLOCK_COUNT
DTV_STAT_TOTAL_BLOCK_COUNT

>From DVB framework documentation, it seems that the demodulator hardware should have counter
to accumulate total bit/packet count to decode, and bit/packet error corrected by FEC.
But unfortunately, CXD2880 demodulator does not have such function.

Instead of it, Sony demodulator has bit/packet error counter for BER, PER calculation.
The user should configure total bit/packet count (denominator) for BER/PER measurement by some register setting.
The IC hardware will update the error counter automatically in period determined by
configured total bit/packet count (denominator).

So, we implemented like as follows.

DTV_STAT_PRE_ERROR_BIT_COUNT
DTV_STAT_POST_ERROR_BIT_COUNT
DTV_STAT_ERROR_BLOCK_COUNT
=> Return bit/packet error counter value in period determined by configured total bit/packet count (numerator).

DTV_STAT_PRE_TOTAL_BIT_COUNT
DTV_STAT_POST_TOTAL_BIT_COUNT
DTV_STAT_TOTAL_BLOCK_COUNT
=> Return currently configured total bit/packet count (denominator).

By this implementation, the user can calculate BER, PER by following calculation.

DTV_STAT_PRE_ERROR_BIT_COUNT / DTV_STAT_PRE_TOTAL_BIT_COUNT
DTV_STAT_POST_ERROR_BIT_COUNT / DTV_STAT_POST_TOTAL_BIT_COUNT
DTV_STAT_ERROR_BLOCK_COUNT / DTV_STAT_TOTAL_BLOCK_COUNT

But instead, DTV_STAT_XXX_ERROR_XXX_COUNT values are not monotonically increased,
and DTV_STAT_XXX_TOTAL_XX_COUNT will return fixed value.




> 
>> +	mutex_unlock(priv->spi_mutex);
>> +	if (ret == CXD2880_RESULT_OK) {
>> +		c->pre_bit_error.len = 1;
>> +		c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
>> +		c->pre_bit_error.stat[0].uvalue = pre_bit_err;
>> +		c->pre_bit_count.len = 1;
>> +		c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
>> +		c->pre_bit_count.stat[0].uvalue = pre_bit_count;
>> +	} else {
>> +		c->pre_bit_error.len = 1;
>> +		c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
>> +		c->pre_bit_count.len = 1;
>> +		c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
>> +		dev_dbg(&priv->spi->dev,
>> +			"%s: pre_bit_error_t failed %d\n",
>> +			__func__, ret);
>> +	}
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_post_bit_err_t(&priv->tnrdmd,
>> +				&post_bit_err, &post_bit_count);
>> +	mutex_unlock(priv->spi_mutex);
>> +	if (ret == CXD2880_RESULT_OK) {
>> +		c->post_bit_error.len = 1;
>> +		c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
>> +		c->post_bit_error.stat[0].uvalue = post_bit_err;
>> +		c->post_bit_count.len = 1;
>> +		c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
>> +		c->post_bit_count.stat[0].uvalue = post_bit_count;
>> +	} else {
>> +		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;
>> +		dev_dbg(&priv->spi->dev,
>> +			"%s: post_bit_err_t %d\n", __func__, ret);
>> +	}
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_read_block_err_t(&priv->tnrdmd,
>> +					&block_err, &block_count);
>> +	mutex_unlock(priv->spi_mutex);
>> +	if (ret == CXD2880_RESULT_OK) {
>> +		c->block_error.len = 1;
>> +		c->block_error.stat[0].scale = FE_SCALE_COUNTER;
>> +		c->block_error.stat[0].uvalue = block_err;
>> +		c->block_count.len = 1;
>> +		c->block_count.stat[0].scale = FE_SCALE_COUNTER;
>> +		c->block_count.stat[0].uvalue = block_count;
>> +	} else {
>> +		c->block_error.len = 1;
>> +		c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
>> +		c->block_count.len = 1;
>> +		c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
>> +		dev_dbg(&priv->spi->dev,
>> +			"%s: read_block_err_t  %d\n", __func__, ret);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int cxd2880_get_frontend_t2(struct dvb_frontend *fe,
>> +				struct dtv_frontend_properties *c)
>> +{
>> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
>> +	int result = 0;
>> +	struct cxd2880_priv *priv = NULL;
>> +	struct cxd2880_dvbt2_l1pre l1pre;
>> +	enum cxd2880_dvbt2_plp_code_rate coderate;
>> +	enum cxd2880_dvbt2_plp_constell qam;
>> +	enum cxd2880_tnrdmd_spectrum_sense sense;
>> +	u16 snr = 0;
>> +	int strength = 0;
>> +	u32 pre_bit_err = 0, pre_bit_count = 0;
>> +	u32 post_bit_err = 0, post_bit_count = 0;
>> +	u32 block_err = 0, block_count = 0;
>> +
>> +	if ((!fe) || (!c)) {
>> +		pr_err("%s: invalid arg.\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre);
>> +	mutex_unlock(priv->spi_mutex);
>> +	if (ret == CXD2880_RESULT_OK) {
>> +		switch (l1pre.fft_mode) {
>> +		case CXD2880_DVBT2_M2K:
>> +			c->transmission_mode = TRANSMISSION_MODE_2K;
>> +			break;
>> +		case CXD2880_DVBT2_M8K:
>> +			c->transmission_mode = TRANSMISSION_MODE_8K;
>> +			break;
>> +		case CXD2880_DVBT2_M4K:
>> +			c->transmission_mode = TRANSMISSION_MODE_4K;
>> +			break;
>> +		case CXD2880_DVBT2_M1K:
>> +			c->transmission_mode = TRANSMISSION_MODE_1K;
>> +			break;
>> +		case CXD2880_DVBT2_M16K:
>> +			c->transmission_mode = TRANSMISSION_MODE_16K;
>> +			break;
>> +		case CXD2880_DVBT2_M32K:
>> +			c->transmission_mode = TRANSMISSION_MODE_32K;
>> +			break;
>> +		default:
>> +			c->transmission_mode = TRANSMISSION_MODE_2K;
>> +			dev_err(&priv->spi->dev,
>> +				"%s: L1Pre fft_mode invalid %d\n",
>> +				__func__, l1pre.fft_mode);
>> +			break;
>> +		}
>> +		switch (l1pre.gi) {
>> +		case CXD2880_DVBT2_G1_32:
>> +			c->guard_interval = GUARD_INTERVAL_1_32;
>> +			break;
>> +		case CXD2880_DVBT2_G1_16:
>> +			c->guard_interval = GUARD_INTERVAL_1_16;
>> +			break;
>> +		case CXD2880_DVBT2_G1_8:
>> +			c->guard_interval = GUARD_INTERVAL_1_8;
>> +			break;
>> +		case CXD2880_DVBT2_G1_4:
>> +			c->guard_interval = GUARD_INTERVAL_1_4;
>> +			break;
>> +		case CXD2880_DVBT2_G1_128:
>> +			c->guard_interval = GUARD_INTERVAL_1_128;
>> +			break;
>> +		case CXD2880_DVBT2_G19_128:
>> +			c->guard_interval = GUARD_INTERVAL_19_128;
>> +			break;
>> +		case CXD2880_DVBT2_G19_256:
>> +			c->guard_interval = GUARD_INTERVAL_19_256;
>> +			break;
>> +		default:
>> +			c->guard_interval = GUARD_INTERVAL_1_32;
>> +			dev_err(&priv->spi->dev,
>> +				"%s: L1Pre gi invalid %d\n",
>> +				__func__, l1pre.gi);
>> +			break;
>> +		}
>> +	} else {
>> +		c->transmission_mode = TRANSMISSION_MODE_2K;
>> +		c->guard_interval = GUARD_INTERVAL_1_32;
>> +		dev_dbg(&priv->spi->dev,
>> +			"%s: L1Pre err %d\n", __func__, ret);
>> +	}
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_tnrdmd_dvbt2_mon_code_rate(&priv->tnrdmd,
>> +						CXD2880_DVBT2_PLP_DATA,
>> +						&coderate);
>> +	mutex_unlock(priv->spi_mutex);
>> +	if (ret == CXD2880_RESULT_OK) {
>> +		switch (coderate) {
>> +		case CXD2880_DVBT2_R1_2:
>> +			c->fec_inner = FEC_1_2;
>> +			break;
>> +		case CXD2880_DVBT2_R3_5:
>> +			c->fec_inner = FEC_3_5;
>> +			break;
>> +		case CXD2880_DVBT2_R2_3:
>> +			c->fec_inner = FEC_2_3;
>> +			break;
>> +		case CXD2880_DVBT2_R3_4:
>> +			c->fec_inner = FEC_3_4;
>> +			break;
>> +		case CXD2880_DVBT2_R4_5:
>> +			c->fec_inner = FEC_4_5;
>> +			break;
>> +		case CXD2880_DVBT2_R5_6:
>> +			c->fec_inner = FEC_5_6;
>> +			break;
>> +		default:
>> +			c->fec_inner = FEC_NONE;
>> +			dev_err(&priv->spi->dev,
>> +				"%s: CodeRate invalid %d\n",
>> +				__func__, coderate);
>> +			break;
>> +		}
>> +	} else {
>> +		c->fec_inner = FEC_NONE;
>> +		dev_dbg(&priv->spi->dev, "%s: CodeRate %d\n", __func__, ret);
>> +	}
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_tnrdmd_dvbt2_mon_qam(&priv->tnrdmd,
>> +					CXD2880_DVBT2_PLP_DATA,
>> +					&qam);
>> +	mutex_unlock(priv->spi_mutex);
>> +	if (ret == CXD2880_RESULT_OK) {
>> +		switch (qam) {
>> +		case CXD2880_DVBT2_QPSK:
>> +			c->modulation = QPSK;
>> +			break;
>> +		case CXD2880_DVBT2_QAM16:
>> +			c->modulation = QAM_16;
>> +			break;
>> +		case CXD2880_DVBT2_QAM64:
>> +			c->modulation = QAM_64;
>> +			break;
>> +		case CXD2880_DVBT2_QAM256:
>> +			c->modulation = QAM_256;
>> +			break;
>> +		default:
>> +			c->modulation = QPSK;
>> +			dev_err(&priv->spi->dev,
>> +				"%s: QAM invalid %d\n",
>> +				__func__, qam);
>> +			break;
>> +		}
>> +	} else {
>> +		c->modulation = QPSK;
>> +		dev_dbg(&priv->spi->dev, "%s: QAM %d\n", __func__, ret);
>> +	}
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(&priv->tnrdmd, &sense);
>> +	mutex_unlock(priv->spi_mutex);
>> +	if (ret == CXD2880_RESULT_OK) {
>> +		switch (sense) {
>> +		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
>> +			c->inversion = INVERSION_OFF;
>> +			break;
>> +		case CXD2880_TNRDMD_SPECTRUM_INV:
>> +			c->inversion = INVERSION_ON;
>> +			break;
>> +		default:
>> +			c->inversion = INVERSION_OFF;
>> +			dev_err(&priv->spi->dev,
>> +				"%s: spectrum sense invalid %d\n",
>> +				__func__, sense);
>> +			break;
>> +		}
>> +	} else {
>> +		c->inversion = INVERSION_OFF;
>> +		dev_dbg(&priv->spi->dev,
>> +			"%s: SpectrumSense %d\n", __func__, ret);
>> +	}
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
>> +	mutex_unlock(priv->spi_mutex);
>> +	if (ret == CXD2880_RESULT_OK) {
>> +		c->strength.len = 1;
>> +		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
>> +		c->strength.stat[0].svalue = strength;
>> +	} else {
>> +		c->strength.len = 1;
>> +		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
>> +		dev_dbg(&priv->spi->dev,
>> +			"%s: mon_rf_lvl %d\n", __func__, ret);
>> +	}
>> +
>> +	result = cxd2880_read_snr(fe, &snr);
>> +	if (!result) {
>> +		c->cnr.len = 1;
>> +		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
>> +		c->cnr.stat[0].svalue = snr;
>> +	} else {
>> +		c->cnr.len = 1;
>> +		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
>> +		dev_dbg(&priv->spi->dev, "%s: read_snr %d\n", __func__, result);
>> +	}
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_pre_bit_err_t2(&priv->tnrdmd,
>> +				&pre_bit_err,
>> +				&pre_bit_count);
>> +	mutex_unlock(priv->spi_mutex);
>> +	if (ret == CXD2880_RESULT_OK) {
>> +		c->pre_bit_error.len = 1;
>> +		c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
>> +		c->pre_bit_error.stat[0].uvalue = pre_bit_err;
>> +		c->pre_bit_count.len = 1;
>> +		c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
>> +		c->pre_bit_count.stat[0].uvalue = pre_bit_count;
>> +	} else {
>> +		c->pre_bit_error.len = 1;
>> +		c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
>> +		c->pre_bit_count.len = 1;
>> +		c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
>> +		dev_dbg(&priv->spi->dev,
>> +			"%s: read_bit_err_t2 %d\n", __func__, ret);
>> +	}
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_post_bit_err_t2(&priv->tnrdmd,
>> +				&post_bit_err, &post_bit_count);
>> +	mutex_unlock(priv->spi_mutex);
>> +	if (ret == CXD2880_RESULT_OK) {
>> +		c->post_bit_error.len = 1;
>> +		c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
>> +		c->post_bit_error.stat[0].uvalue = post_bit_err;
>> +		c->post_bit_count.len = 1;
>> +		c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
>> +		c->post_bit_count.stat[0].uvalue = post_bit_count;
>> +	} else {
>> +		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;
>> +		dev_dbg(&priv->spi->dev,
>> +			"%s: post_bit_err_t2 %d\n", __func__, ret);
>> +	}
>> +
>> +	mutex_lock(priv->spi_mutex);
>> +	ret = cxd2880_read_block_err_t2(&priv->tnrdmd,
>> +					&block_err, &block_count);
>> +	mutex_unlock(priv->spi_mutex);
>> +	if (ret == CXD2880_RESULT_OK) {
>> +		c->block_error.len = 1;
>> +		c->block_error.stat[0].scale = FE_SCALE_COUNTER;
>> +		c->block_error.stat[0].uvalue = block_err;
>> +		c->block_count.len = 1;
>> +		c->block_count.stat[0].scale = FE_SCALE_COUNTER;
>> +		c->block_count.stat[0].uvalue = block_count;
>> +	} else {
>> +		c->block_error.len = 1;
>> +		c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
>> +		c->block_count.len = 1;
>> +		c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
>> +		dev_dbg(&priv->spi->dev,
>> +			"%s: read_block_err_t2 %d\n", __func__, ret);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int cxd2880_get_frontend(struct dvb_frontend *fe,
>> +				struct dtv_frontend_properties *props)
>> +{
>> +	struct cxd2880_priv *priv = NULL;
>> +	int result = 0;
>> +
>> +	if ((!fe) || (!props)) {
>> +		pr_err("%s: invalid arg.", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
>> +
>> +	dev_dbg(&priv->spi->dev, "%s: system=%d\n", __func__,
>> +		fe->dtv_property_cache.delivery_system);
>> +	switch (fe->dtv_property_cache.delivery_system) {
>> +	case SYS_DVBT:
>> +		result = cxd2880_get_frontend_t(fe, props);
>> +		break;
>> +	case SYS_DVBT2:
>> +		result = cxd2880_get_frontend_t2(fe, props);
>> +		break;
> 
> Hmm... I noticed that already when reviewing other parts of the driver...
> 
> At patch 6, you defined:
> 
> 
> enum cxd2880_dtv_sys {
> +	CXD2880_DTV_SYS_UNKNOWN,
> +	CXD2880_DTV_SYS_DVBT,
> +	CXD2880_DTV_SYS_DVBT2,
> +	CXD2880_DTV_SYS_ISDBT,
> +	CXD2880_DTV_SYS_ISDBTSB,
> +	CXD2880_DTV_SYS_ISDBTMM_A,
> +	CXD2880_DTV_SYS_ISDBTMM_B,
> +	CXD2880_DTV_SYS_ANY
> +};
> 
> So, I was expecting to see not only DVB T/T2 here (and at the tuner
> driver) but also ISDB, in order to match the defined standards,
> but you're setting only DVB T/T2 here, and some parts of the tuner
> driver also seem to be focused only on those two standards.
> 
> So, the question is: does the hardware support ISDB? If so,
> please add a /* FIXME */ note where pertinent, saying that
> ISDB support should be added in the future. If, on the other 
> hand, the hardware doesn't support it at all, please cleanup the
> code by removing references for it.
> 
> 
>> +	default:
>> +		result = -EINVAL;
>> +		break;
>> +	}
>> +
>> +	return result;
>> +}
>> +
>> +static enum dvbfe_algo cxd2880_get_frontend_algo(struct dvb_frontend *fe)
>> +{
>> +	return DVBFE_ALGO_HW;
>> +}
>> +
>> +static struct dvb_frontend_ops cxd2880_dvbt_t2_ops;
>> +
>> +struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
>> +				struct cxd2880_config *cfg)
>> +{
>> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
>> +	enum cxd2880_tnrdmd_chip_id chipid =
>> +					CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
>> +	static struct cxd2880_priv *priv;
>> +	u8 data = 0;
>> +
>> +	if (!fe) {
>> +		pr_err("%s: invalid arg.\n", __func__);
>> +		return NULL;
>> +	}
>> +
>> +	priv = kzalloc(sizeof(struct cxd2880_priv), GFP_KERNEL);
>> +	if (!priv)
>> +		return NULL;
>> +
>> +	priv->spi = cfg->spi;
>> +	priv->spi_mutex = cfg->spi_mutex;
>> +	priv->spi_device.spi = cfg->spi;
>> +
>> +	memcpy(&fe->ops, &cxd2880_dvbt_t2_ops,
>> +			sizeof(struct dvb_frontend_ops));
>> +
>> +	ret = cxd2880_spi_device_initialize(&priv->spi_device,
>> +						CXD2880_SPI_MODE_0,
>> +						55000000);
>> +	if (ret != CXD2880_RESULT_OK) {
>> +		dev_err(&priv->spi->dev,
>> +			"%s: spi_device_initialize failed. %d\n",
>> +			__func__, ret);
>> +		kfree(priv);
>> +		return NULL;
>> +	}
>> +
>> +	ret = cxd2880_spi_device_create_spi(&priv->cxd2880_spi,
>> +					&priv->spi_device);
>> +	if (ret != CXD2880_RESULT_OK) {
>> +		dev_err(&priv->spi->dev,
>> +			"%s: spi_device_create_spi failed. %d\n",
>> +			__func__, ret);
>> +		kfree(priv);
>> +		return NULL;
>> +	}
>> +
>> +	ret = cxd2880_io_spi_create(&priv->regio, &priv->cxd2880_spi, 0);
>> +	if (ret != CXD2880_RESULT_OK) {
>> +		dev_err(&priv->spi->dev,
>> +			"%s: io_spi_create failed. %d\n", __func__, ret);
>> +		kfree(priv);
>> +		return NULL;
>> +	}
>> +	if (priv->regio.write_reg(&priv->regio, CXD2880_IO_TGT_SYS, 0x00, 0x00)
>> +		!= CXD2880_RESULT_OK) {
>> +		dev_err(&priv->spi->dev,
>> +			"%s: set bank to 0x00 failed.\n", __func__);
>> +		kfree(priv);
>> +		return NULL;
>> +	}
>> +	if (priv->regio.read_regs(&priv->regio,
>> +					CXD2880_IO_TGT_SYS, 0xFD, &data, 1)
>> +					!= CXD2880_RESULT_OK) {
>> +		dev_err(&priv->spi->dev,
>> +			"%s: read chip id failed.\n", __func__);
>> +		kfree(priv);
>> +		return NULL;
>> +	}
>> +
>> +	chipid = (enum cxd2880_tnrdmd_chip_id)data;
>> +	if ((chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) &&
>> +		(chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11)) {
>> +		dev_err(&priv->spi->dev,
>> +			"%s: chip id invalid.\n", __func__);
>> +		kfree(priv);
>> +		return NULL;
>> +	}
>> +
>> +	fe->demodulator_priv = priv;
>> +	dev_info(&priv->spi->dev,
>> +		"CXD2880 driver version: Ver %s\n",
>> +		CXD2880_TNRDMD_DRIVER_VERSION);
>> +
>> +	return fe;
>> +}
>> +EXPORT_SYMBOL(cxd2880_attach);
>> +
>> +static struct dvb_frontend_ops cxd2880_dvbt_t2_ops = {
>> +	.info = {
>> +		.name = "Sony CXD2880",
>> +		.frequency_min =  174000000,
>> +		.frequency_max = 862000000,
>> +		.frequency_stepsize = 1000,
>> +		.caps = FE_CAN_INVERSION_AUTO |
>> +				FE_CAN_FEC_1_2 |
>> +				FE_CAN_FEC_2_3 |
>> +				FE_CAN_FEC_3_4 |
>> +				FE_CAN_FEC_4_5 |
>> +				FE_CAN_FEC_5_6	|
>> +				FE_CAN_FEC_7_8	|
>> +				FE_CAN_FEC_AUTO |
>> +				FE_CAN_QPSK |
>> +				FE_CAN_QAM_16 |
>> +				FE_CAN_QAM_32 |
>> +				FE_CAN_QAM_64 |
>> +				FE_CAN_QAM_128 |
>> +				FE_CAN_QAM_256 |
>> +				FE_CAN_QAM_AUTO |
>> +				FE_CAN_TRANSMISSION_MODE_AUTO |
>> +				FE_CAN_GUARD_INTERVAL_AUTO |
>> +				FE_CAN_2G_MODULATION |
>> +				FE_CAN_RECOVER |
>> +				FE_CAN_MUTE_TS,
>> +	},
>> +	.delsys = { SYS_DVBT, SYS_DVBT2 },
>> +
>> +	.release = cxd2880_release,
>> +	.init = cxd2880_init,
>> +	.sleep = cxd2880_sleep,
>> +	.tune = cxd2880_tune,
>> +	.set_frontend = cxd2880_set_frontend,
>> +	.get_frontend = cxd2880_get_frontend,
>> +	.read_status = cxd2880_read_status,
>> +	.read_ber = cxd2880_read_ber,
>> +	.read_signal_strength = cxd2880_read_signal_strength,
>> +	.read_snr = cxd2880_read_snr,
>> +	.read_ucblocks = cxd2880_read_ucblocks,
>> +	.get_frontend_algo = cxd2880_get_frontend_algo,
>> +};
>> +
>> +MODULE_DESCRIPTION(
>> +"Sony CXD2880 DVB-T2/T tuner + demodulator drvier");
>> +MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
>> +MODULE_LICENSE("GPL v2");
> 
> 
> 
> Thanks,
> Mauro
> .

Regards and Thanks
Takiguchi

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 08/15] [media] cxd2880: Add top level of the driver
  2017-06-19  7:56     ` Takiguchi, Yasunari
@ 2017-06-23 13:02       ` Mauro Carvalho Chehab
  2017-06-25 12:15         ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 36+ messages in thread
From: Mauro Carvalho Chehab @ 2017-06-23 13:02 UTC (permalink / raw)
  To: Takiguchi, Yasunari
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS)

Em Mon, 19 Jun 2017 16:56:13 +0900
"Takiguchi, Yasunari" <Yasunari.Takiguchi@sony.com> escreveu:

> >> +static int cxd2880_get_frontend_t(struct dvb_frontend *fe,
> >> +				struct dtv_frontend_properties *c)
> >> +{
> >> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> >> +	int result = 0;
> >> +	struct cxd2880_priv *priv = NULL;
> >> +	enum cxd2880_dvbt_mode mode = CXD2880_DVBT_MODE_2K;
> >> +	enum cxd2880_dvbt_guard guard = CXD2880_DVBT_GUARD_1_32;
> >> +	struct cxd2880_dvbt_tpsinfo tps;
> >> +	enum cxd2880_tnrdmd_spectrum_sense sense;
> >> +	u16 snr = 0;
> >> +	int strength = 0;
> >> +	u32 pre_bit_err = 0, pre_bit_count = 0;
> >> +	u32 post_bit_err = 0, post_bit_count = 0;
> >> +	u32 block_err = 0, block_count = 0;
> >> +
> >> +	if ((!fe) || (!c)) {
> >> +		pr_err("%s: invalid arg\n", __func__);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
> >> +
> >> +	mutex_lock(priv->spi_mutex);
> >> +	ret = cxd2880_tnrdmd_dvbt_mon_mode_guard(&priv->tnrdmd,
> >> +						 &mode, &guard);
> >> +	mutex_unlock(priv->spi_mutex);
> >> +	if (ret == CXD2880_RESULT_OK) {
> >> +		switch (mode) {
> >> +		case CXD2880_DVBT_MODE_2K:
> >> +			c->transmission_mode = TRANSMISSION_MODE_2K;
> >> +			break;
> >> +		case CXD2880_DVBT_MODE_8K:
> >> +			c->transmission_mode = TRANSMISSION_MODE_8K;
> >> +			break;
> >> +		default:
> >> +			c->transmission_mode = TRANSMISSION_MODE_2K;
> >> +			dev_err(&priv->spi->dev, "%s: get invalid mode %d\n",
> >> +					__func__, mode);
> >> +			break;
> >> +		}
> >> +		switch (guard) {
> >> +		case CXD2880_DVBT_GUARD_1_32:
> >> +			c->guard_interval = GUARD_INTERVAL_1_32;
> >> +			break;
> >> +		case CXD2880_DVBT_GUARD_1_16:
> >> +			c->guard_interval = GUARD_INTERVAL_1_16;
> >> +			break;
> >> +		case CXD2880_DVBT_GUARD_1_8:
> >> +			c->guard_interval = GUARD_INTERVAL_1_8;
> >> +			break;
> >> +		case CXD2880_DVBT_GUARD_1_4:
> >> +			c->guard_interval = GUARD_INTERVAL_1_4;
> >> +			break;
> >> +		default:
> >> +			c->guard_interval = GUARD_INTERVAL_1_32;
> >> +			dev_err(&priv->spi->dev, "%s: get invalid guard %d\n",
> >> +					__func__, guard);
> >> +			break;
> >> +		}
> >> +	} else {
> >> +		c->transmission_mode = TRANSMISSION_MODE_2K;
> >> +		c->guard_interval = GUARD_INTERVAL_1_32;
> >> +		dev_dbg(&priv->spi->dev,
> >> +			"%s: ModeGuard err %d\n", __func__, ret);
> >> +	}
> >> +
> >> +	mutex_lock(priv->spi_mutex);
> >> +	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, &tps);
> >> +	mutex_unlock(priv->spi_mutex);
> >> +	if (ret == CXD2880_RESULT_OK) {
> >> +		switch (tps.hierarchy) {
> >> +		case CXD2880_DVBT_HIERARCHY_NON:
> >> +			c->hierarchy = HIERARCHY_NONE;
> >> +			break;
> >> +		case CXD2880_DVBT_HIERARCHY_1:
> >> +			c->hierarchy = HIERARCHY_1;
> >> +			break;
> >> +		case CXD2880_DVBT_HIERARCHY_2:
> >> +			c->hierarchy = HIERARCHY_2;
> >> +			break;
> >> +		case CXD2880_DVBT_HIERARCHY_4:
> >> +			c->hierarchy = HIERARCHY_4;
> >> +			break;
> >> +		default:
> >> +			c->hierarchy = HIERARCHY_NONE;
> >> +			dev_err(&priv->spi->dev,
> >> +				"%s: TPSInfo hierarchy invalid %d\n",
> >> +				__func__, tps.hierarchy);
> >> +			break;
> >> +		}
> >> +
> >> +		switch (tps.rate_hp) {
> >> +		case CXD2880_DVBT_CODERATE_1_2:
> >> +			c->code_rate_HP = FEC_1_2;
> >> +			break;
> >> +		case CXD2880_DVBT_CODERATE_2_3:
> >> +			c->code_rate_HP = FEC_2_3;
> >> +			break;
> >> +		case CXD2880_DVBT_CODERATE_3_4:
> >> +			c->code_rate_HP = FEC_3_4;
> >> +			break;
> >> +		case CXD2880_DVBT_CODERATE_5_6:
> >> +			c->code_rate_HP = FEC_5_6;
> >> +			break;
> >> +		case CXD2880_DVBT_CODERATE_7_8:
> >> +			c->code_rate_HP = FEC_7_8;
> >> +			break;
> >> +		default:
> >> +			c->code_rate_HP = FEC_NONE;
> >> +			dev_err(&priv->spi->dev,
> >> +				"%s: TPSInfo rateHP invalid %d\n",
> >> +				__func__, tps.rate_hp);
> >> +			break;
> >> +		}
> >> +		switch (tps.rate_lp) {
> >> +		case CXD2880_DVBT_CODERATE_1_2:
> >> +			c->code_rate_LP = FEC_1_2;
> >> +			break;
> >> +		case CXD2880_DVBT_CODERATE_2_3:
> >> +			c->code_rate_LP = FEC_2_3;
> >> +			break;
> >> +		case CXD2880_DVBT_CODERATE_3_4:
> >> +			c->code_rate_LP = FEC_3_4;
> >> +			break;
> >> +		case CXD2880_DVBT_CODERATE_5_6:
> >> +			c->code_rate_LP = FEC_5_6;
> >> +			break;
> >> +		case CXD2880_DVBT_CODERATE_7_8:
> >> +			c->code_rate_LP = FEC_7_8;
> >> +			break;
> >> +		default:
> >> +			c->code_rate_LP = FEC_NONE;
> >> +			dev_err(&priv->spi->dev,
> >> +				"%s: TPSInfo rateLP invalid %d\n",
> >> +				__func__, tps.rate_lp);
> >> +			break;
> >> +		}
> >> +		switch (tps.constellation) {
> >> +		case CXD2880_DVBT_CONSTELLATION_QPSK:
> >> +			c->modulation = QPSK;
> >> +			break;
> >> +		case CXD2880_DVBT_CONSTELLATION_16QAM:
> >> +			c->modulation = QAM_16;
> >> +			break;
> >> +		case CXD2880_DVBT_CONSTELLATION_64QAM:
> >> +			c->modulation = QAM_64;
> >> +			break;
> >> +		default:
> >> +			c->modulation = QPSK;
> >> +			dev_err(&priv->spi->dev,
> >> +				"%s: TPSInfo constellation invalid %d\n",
> >> +				__func__, tps.constellation);
> >> +			break;
> >> +		}
> >> +	} else {
> >> +		c->hierarchy = HIERARCHY_NONE;
> >> +		c->code_rate_HP = FEC_NONE;
> >> +		c->code_rate_LP = FEC_NONE;
> >> +		c->modulation = QPSK;
> >> +		dev_dbg(&priv->spi->dev,
> >> +			"%s: TPS info err %d\n", __func__, ret);
> >> +	}
> >> +
> >> +	mutex_lock(priv->spi_mutex);
> >> +	ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(&priv->tnrdmd, &sense);
> >> +	mutex_unlock(priv->spi_mutex);
> >> +	if (ret == CXD2880_RESULT_OK) {
> >> +		switch (sense) {
> >> +		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
> >> +			c->inversion = INVERSION_OFF;
> >> +			break;
> >> +		case CXD2880_TNRDMD_SPECTRUM_INV:
> >> +			c->inversion = INVERSION_ON;
> >> +			break;
> >> +		default:
> >> +			c->inversion = INVERSION_OFF;
> >> +			dev_err(&priv->spi->dev,
> >> +				"%s: spectrum sense invalid %d\n",
> >> +				__func__, sense);
> >> +			break;
> >> +		}
> >> +	} else {
> >> +		c->inversion = INVERSION_OFF;
> >> +		dev_dbg(&priv->spi->dev,
> >> +			"%s: spectrum_sense %d\n", __func__, ret);
> >> +	}
> >> +
> >> +	mutex_lock(priv->spi_mutex);
> >> +	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
> >> +	mutex_unlock(priv->spi_mutex);
> >> +	if (ret == CXD2880_RESULT_OK) {
> >> +		c->strength.len = 1;
> >> +		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
> >> +		c->strength.stat[0].svalue = strength;
> >> +	} else {
> >> +		c->strength.len = 1;
> >> +		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> >> +		dev_dbg(&priv->spi->dev, "%s: mon_rf_lvl %d\n",
> >> +			__func__, result);
> >> +	}
> >> +
> >> +	result = cxd2880_read_snr(fe, &snr);
> >> +	if (!result) {
> >> +		c->cnr.len = 1;
> >> +		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
> >> +		c->cnr.stat[0].svalue = snr;
> >> +	} else {
> >> +		c->cnr.len = 1;
> >> +		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> >> +		dev_dbg(&priv->spi->dev, "%s: read_snr %d\n", __func__, result);
> >> +	}
> >> +
> >> +	mutex_lock(priv->spi_mutex);
> >> +	ret = cxd2880_pre_bit_err_t(&priv->tnrdmd, &pre_bit_err,
> >> +					&pre_bit_count);  
> > 
> > Hmm... reading BER-based measures at get_frontend() may not be
> > the best idea, depending on how the hardware works.
> > 
> > I mean, you need to be sure that, if userspace is calling it too
> > often, it won't affect the hardware or the measurement.
> > 
> > On other drivers, where each call generates an I2C access,
> > we do that by moving the code that actually call the hardware
> > at get status, and we use jiffies to be sure that it won't be
> > called too often.
> > 
> > I dunno if, on your SPI design, this would be an issue or not.  
> 
> CXD2880 IC can accept frequently counter register access by SPI.
> Even if such access occurred, it will not affect to IC behavior.
> We checked Sony demodulator IC driver code, dvb_frontends/cxd2841er.c and it reads register information
> when get_frontend called.
> 
> In fact, CXD2880 IC do NOT have bit error/packet error counter function to achieve DVB framework API completely.
> Sorry for long explanation, but I'll write in detail.
> 
> DTV_STAT_PRE_ERROR_BIT_COUNT
> DTV_STAT_PRE_TOTAL_BIT_COUNT
> DTV_STAT_POST_ERROR_BIT_COUNT
> DTV_STAT_POST_TOTAL_BIT_COUNT
> DTV_STAT_ERROR_BLOCK_COUNT
> DTV_STAT_TOTAL_BLOCK_COUNT
> 
> From DVB framework documentation, it seems that the demodulator hardware should have counter
> to accumulate total bit/packet count to decode, and bit/packet error corrected by FEC.
> But unfortunately, CXD2880 demodulator does not have such function.

No, this is not a requirement. What actually happens is that userspace
expect those values to be monotonically incremented, as new data is
made available.

> 
> Instead of it, Sony demodulator has bit/packet error counter for BER, PER calculation.
> The user should configure total bit/packet count (denominator) for BER/PER measurement by some register setting.

Yeah, that's a common practice: usually, the counters are initialized
by some registers (on a few hardware, it is hardcoded).

> The IC hardware will update the error counter automatically in period determined by
> configured total bit/packet count (denominator).

So, the driver need to be sure that it won't read the register too
early, e. g. it should either be reading some register bits to know
when to read the data, or it needs a logic that will estimate when
the data will be ready, not updating the values to early.

An example of the first case is the mb86a20s. There, you can see at:
mb86a20s_get_pre_ber():

	/* Check if the BER measures are already available */
	rc = mb86a20s_readreg(state, 0x54);
	if (rc < 0)
		return rc;

	/* Check if data is available for that layer */
	if (!(rc & (1 << layer))) {
		dev_dbg(&state->i2c->dev,
			"%s: preBER for layer %c is not available yet.\n",
			__func__, 'A' + layer);
		return -EBUSY;
	}

An example of the second case is dib8000. There, you can see at:
dib8000_get_stats():


	/* Check if time for stats was elapsed */
	if (time_after(jiffies, state->per_jiffies_stats)) {
		state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000);

...
		/* Get UCB measures */
		dib8000_read_unc_blocks(fe, &val);
		if (val < state->init_ucb)
			state->init_ucb += 0x100000000LL;

		c->block_error.stat[0].scale = FE_SCALE_COUNTER;
		c->block_error.stat[0].uvalue = val + state->init_ucb;

		/* Estimate the number of packets based on bitrate */
		if (!time_us)
			time_us = dib8000_get_time_us(fe, -1);

		if (time_us) {
			blocks = 1250000ULL * 1000000ULL;
			do_div(blocks, time_us * 8 * 204);
			c->block_count.stat[0].scale = FE_SCALE_COUNTER;
			c->block_count.stat[0].uvalue += blocks;
		}

		show_per_stats = 1;
	}

At the above, the hardware is set to provide the number of uncorrected
blocks on every second, and the logic there calculates the number of
blocks based on the bitrate. It shouldn't be hard to do the opposite
(e. g. set the hardware for a given number of blocks and calculate
the time - I remember I coded it already - just don't remember on
what driver - perhaps on an earlier implementation at mb86a20s).

In any case, as the code will require periodic pulls, in order
to provide monotonic counters, we implement it via get_stats(), as
the DVB core will ensure that it will be called periodically
(typically 3 times per second).

> 
> So, we implemented like as follows.
> 
> DTV_STAT_PRE_ERROR_BIT_COUNT
> DTV_STAT_POST_ERROR_BIT_COUNT
> DTV_STAT_ERROR_BLOCK_COUNT
> => Return bit/packet error counter value in period determined by configured total bit/packet count (numerator).  
> 
> DTV_STAT_PRE_TOTAL_BIT_COUNT
> DTV_STAT_POST_TOTAL_BIT_COUNT
> DTV_STAT_TOTAL_BLOCK_COUNT
> => Return currently configured total bit/packet count (denominator).  
> 
> By this implementation, the user can calculate BER, PER by following calculation.
> 
> DTV_STAT_PRE_ERROR_BIT_COUNT / DTV_STAT_PRE_TOTAL_BIT_COUNT
> DTV_STAT_POST_ERROR_BIT_COUNT / DTV_STAT_POST_TOTAL_BIT_COUNT
> DTV_STAT_ERROR_BLOCK_COUNT / DTV_STAT_TOTAL_BLOCK_COUNT
> 
> But instead, DTV_STAT_XXX_ERROR_XXX_COUNT values are not monotonically increased,
> and DTV_STAT_XXX_TOTAL_XX_COUNT will return fixed value.

That's wrong. you should be doing:

	DTV_STAT_TOTAL_BLOCK_COUNT += number_of_blocks

every time the block counter registers are updated (and the
same for the total bit counter).

Thanks,
Mauro

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 08/15] [media] cxd2880: Add top level of the driver
  2017-06-23 13:02       ` Mauro Carvalho Chehab
@ 2017-06-25 12:15         ` Mauro Carvalho Chehab
  2017-06-26  1:24           ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 36+ messages in thread
From: Mauro Carvalho Chehab @ 2017-06-25 12:15 UTC (permalink / raw)
  To: Takiguchi, Yasunari
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS)

Em Fri, 23 Jun 2017 10:02:39 -0300
Mauro Carvalho Chehab <mchehab@s-opensource.com> escreveu:

> Em Mon, 19 Jun 2017 16:56:13 +0900
> "Takiguchi, Yasunari" <Yasunari.Takiguchi@sony.com> escreveu:
> 
> > >> +static int cxd2880_get_frontend_t(struct dvb_frontend *fe,
> > >> +				struct dtv_frontend_properties *c)
> > >> +{
> > >> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> > >> +	int result = 0;
> > >> +	struct cxd2880_priv *priv = NULL;
> > >> +	enum cxd2880_dvbt_mode mode = CXD2880_DVBT_MODE_2K;
> > >> +	enum cxd2880_dvbt_guard guard = CXD2880_DVBT_GUARD_1_32;
> > >> +	struct cxd2880_dvbt_tpsinfo tps;
> > >> +	enum cxd2880_tnrdmd_spectrum_sense sense;
> > >> +	u16 snr = 0;
> > >> +	int strength = 0;
> > >> +	u32 pre_bit_err = 0, pre_bit_count = 0;
> > >> +	u32 post_bit_err = 0, post_bit_count = 0;
> > >> +	u32 block_err = 0, block_count = 0;
> > >> +
> > >> +	if ((!fe) || (!c)) {
> > >> +		pr_err("%s: invalid arg\n", __func__);
> > >> +		return -EINVAL;
> > >> +	}
> > >> +
> > >> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
> > >> +
> > >> +	mutex_lock(priv->spi_mutex);
> > >> +	ret = cxd2880_tnrdmd_dvbt_mon_mode_guard(&priv->tnrdmd,
> > >> +						 &mode, &guard);
> > >> +	mutex_unlock(priv->spi_mutex);
> > >> +	if (ret == CXD2880_RESULT_OK) {
> > >> +		switch (mode) {
> > >> +		case CXD2880_DVBT_MODE_2K:
> > >> +			c->transmission_mode = TRANSMISSION_MODE_2K;
> > >> +			break;
> > >> +		case CXD2880_DVBT_MODE_8K:
> > >> +			c->transmission_mode = TRANSMISSION_MODE_8K;
> > >> +			break;
> > >> +		default:
> > >> +			c->transmission_mode = TRANSMISSION_MODE_2K;
> > >> +			dev_err(&priv->spi->dev, "%s: get invalid mode %d\n",
> > >> +					__func__, mode);
> > >> +			break;
> > >> +		}
> > >> +		switch (guard) {
> > >> +		case CXD2880_DVBT_GUARD_1_32:
> > >> +			c->guard_interval = GUARD_INTERVAL_1_32;
> > >> +			break;
> > >> +		case CXD2880_DVBT_GUARD_1_16:
> > >> +			c->guard_interval = GUARD_INTERVAL_1_16;
> > >> +			break;
> > >> +		case CXD2880_DVBT_GUARD_1_8:
> > >> +			c->guard_interval = GUARD_INTERVAL_1_8;
> > >> +			break;
> > >> +		case CXD2880_DVBT_GUARD_1_4:
> > >> +			c->guard_interval = GUARD_INTERVAL_1_4;
> > >> +			break;
> > >> +		default:
> > >> +			c->guard_interval = GUARD_INTERVAL_1_32;
> > >> +			dev_err(&priv->spi->dev, "%s: get invalid guard %d\n",
> > >> +					__func__, guard);
> > >> +			break;
> > >> +		}
> > >> +	} else {
> > >> +		c->transmission_mode = TRANSMISSION_MODE_2K;
> > >> +		c->guard_interval = GUARD_INTERVAL_1_32;
> > >> +		dev_dbg(&priv->spi->dev,
> > >> +			"%s: ModeGuard err %d\n", __func__, ret);
> > >> +	}
> > >> +
> > >> +	mutex_lock(priv->spi_mutex);
> > >> +	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, &tps);
> > >> +	mutex_unlock(priv->spi_mutex);
> > >> +	if (ret == CXD2880_RESULT_OK) {
> > >> +		switch (tps.hierarchy) {
> > >> +		case CXD2880_DVBT_HIERARCHY_NON:
> > >> +			c->hierarchy = HIERARCHY_NONE;
> > >> +			break;
> > >> +		case CXD2880_DVBT_HIERARCHY_1:
> > >> +			c->hierarchy = HIERARCHY_1;
> > >> +			break;
> > >> +		case CXD2880_DVBT_HIERARCHY_2:
> > >> +			c->hierarchy = HIERARCHY_2;
> > >> +			break;
> > >> +		case CXD2880_DVBT_HIERARCHY_4:
> > >> +			c->hierarchy = HIERARCHY_4;
> > >> +			break;
> > >> +		default:
> > >> +			c->hierarchy = HIERARCHY_NONE;
> > >> +			dev_err(&priv->spi->dev,
> > >> +				"%s: TPSInfo hierarchy invalid %d\n",
> > >> +				__func__, tps.hierarchy);
> > >> +			break;
> > >> +		}
> > >> +
> > >> +		switch (tps.rate_hp) {
> > >> +		case CXD2880_DVBT_CODERATE_1_2:
> > >> +			c->code_rate_HP = FEC_1_2;
> > >> +			break;
> > >> +		case CXD2880_DVBT_CODERATE_2_3:
> > >> +			c->code_rate_HP = FEC_2_3;
> > >> +			break;
> > >> +		case CXD2880_DVBT_CODERATE_3_4:
> > >> +			c->code_rate_HP = FEC_3_4;
> > >> +			break;
> > >> +		case CXD2880_DVBT_CODERATE_5_6:
> > >> +			c->code_rate_HP = FEC_5_6;
> > >> +			break;
> > >> +		case CXD2880_DVBT_CODERATE_7_8:
> > >> +			c->code_rate_HP = FEC_7_8;
> > >> +			break;
> > >> +		default:
> > >> +			c->code_rate_HP = FEC_NONE;
> > >> +			dev_err(&priv->spi->dev,
> > >> +				"%s: TPSInfo rateHP invalid %d\n",
> > >> +				__func__, tps.rate_hp);
> > >> +			break;
> > >> +		}
> > >> +		switch (tps.rate_lp) {
> > >> +		case CXD2880_DVBT_CODERATE_1_2:
> > >> +			c->code_rate_LP = FEC_1_2;
> > >> +			break;
> > >> +		case CXD2880_DVBT_CODERATE_2_3:
> > >> +			c->code_rate_LP = FEC_2_3;
> > >> +			break;
> > >> +		case CXD2880_DVBT_CODERATE_3_4:
> > >> +			c->code_rate_LP = FEC_3_4;
> > >> +			break;
> > >> +		case CXD2880_DVBT_CODERATE_5_6:
> > >> +			c->code_rate_LP = FEC_5_6;
> > >> +			break;
> > >> +		case CXD2880_DVBT_CODERATE_7_8:
> > >> +			c->code_rate_LP = FEC_7_8;
> > >> +			break;
> > >> +		default:
> > >> +			c->code_rate_LP = FEC_NONE;
> > >> +			dev_err(&priv->spi->dev,
> > >> +				"%s: TPSInfo rateLP invalid %d\n",
> > >> +				__func__, tps.rate_lp);
> > >> +			break;
> > >> +		}
> > >> +		switch (tps.constellation) {
> > >> +		case CXD2880_DVBT_CONSTELLATION_QPSK:
> > >> +			c->modulation = QPSK;
> > >> +			break;
> > >> +		case CXD2880_DVBT_CONSTELLATION_16QAM:
> > >> +			c->modulation = QAM_16;
> > >> +			break;
> > >> +		case CXD2880_DVBT_CONSTELLATION_64QAM:
> > >> +			c->modulation = QAM_64;
> > >> +			break;
> > >> +		default:
> > >> +			c->modulation = QPSK;
> > >> +			dev_err(&priv->spi->dev,
> > >> +				"%s: TPSInfo constellation invalid %d\n",
> > >> +				__func__, tps.constellation);
> > >> +			break;
> > >> +		}
> > >> +	} else {
> > >> +		c->hierarchy = HIERARCHY_NONE;
> > >> +		c->code_rate_HP = FEC_NONE;
> > >> +		c->code_rate_LP = FEC_NONE;
> > >> +		c->modulation = QPSK;
> > >> +		dev_dbg(&priv->spi->dev,
> > >> +			"%s: TPS info err %d\n", __func__, ret);
> > >> +	}
> > >> +
> > >> +	mutex_lock(priv->spi_mutex);
> > >> +	ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(&priv->tnrdmd, &sense);
> > >> +	mutex_unlock(priv->spi_mutex);
> > >> +	if (ret == CXD2880_RESULT_OK) {
> > >> +		switch (sense) {
> > >> +		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
> > >> +			c->inversion = INVERSION_OFF;
> > >> +			break;
> > >> +		case CXD2880_TNRDMD_SPECTRUM_INV:
> > >> +			c->inversion = INVERSION_ON;
> > >> +			break;
> > >> +		default:
> > >> +			c->inversion = INVERSION_OFF;
> > >> +			dev_err(&priv->spi->dev,
> > >> +				"%s: spectrum sense invalid %d\n",
> > >> +				__func__, sense);
> > >> +			break;
> > >> +		}
> > >> +	} else {
> > >> +		c->inversion = INVERSION_OFF;
> > >> +		dev_dbg(&priv->spi->dev,
> > >> +			"%s: spectrum_sense %d\n", __func__, ret);
> > >> +	}
> > >> +
> > >> +	mutex_lock(priv->spi_mutex);
> > >> +	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
> > >> +	mutex_unlock(priv->spi_mutex);
> > >> +	if (ret == CXD2880_RESULT_OK) {
> > >> +		c->strength.len = 1;
> > >> +		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
> > >> +		c->strength.stat[0].svalue = strength;
> > >> +	} else {
> > >> +		c->strength.len = 1;
> > >> +		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> > >> +		dev_dbg(&priv->spi->dev, "%s: mon_rf_lvl %d\n",
> > >> +			__func__, result);
> > >> +	}
> > >> +
> > >> +	result = cxd2880_read_snr(fe, &snr);
> > >> +	if (!result) {
> > >> +		c->cnr.len = 1;
> > >> +		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
> > >> +		c->cnr.stat[0].svalue = snr;
> > >> +	} else {
> > >> +		c->cnr.len = 1;
> > >> +		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> > >> +		dev_dbg(&priv->spi->dev, "%s: read_snr %d\n", __func__, result);
> > >> +	}
> > >> +
> > >> +	mutex_lock(priv->spi_mutex);
> > >> +	ret = cxd2880_pre_bit_err_t(&priv->tnrdmd, &pre_bit_err,
> > >> +					&pre_bit_count);    
> > > 
> > > Hmm... reading BER-based measures at get_frontend() may not be
> > > the best idea, depending on how the hardware works.
> > > 
> > > I mean, you need to be sure that, if userspace is calling it too
> > > often, it won't affect the hardware or the measurement.
> > > 
> > > On other drivers, where each call generates an I2C access,
> > > we do that by moving the code that actually call the hardware
> > > at get status, and we use jiffies to be sure that it won't be
> > > called too often.
> > > 
> > > I dunno if, on your SPI design, this would be an issue or not.    
> > 
> > CXD2880 IC can accept frequently counter register access by SPI.
> > Even if such access occurred, it will not affect to IC behavior.
> > We checked Sony demodulator IC driver code, dvb_frontends/cxd2841er.c and it reads register information
> > when get_frontend called.
> > 
> > In fact, CXD2880 IC do NOT have bit error/packet error counter function to achieve DVB framework API completely.
> > Sorry for long explanation, but I'll write in detail.
> > 
> > DTV_STAT_PRE_ERROR_BIT_COUNT
> > DTV_STAT_PRE_TOTAL_BIT_COUNT
> > DTV_STAT_POST_ERROR_BIT_COUNT
> > DTV_STAT_POST_TOTAL_BIT_COUNT
> > DTV_STAT_ERROR_BLOCK_COUNT
> > DTV_STAT_TOTAL_BLOCK_COUNT
> > 
> > From DVB framework documentation, it seems that the demodulator hardware should have counter
> > to accumulate total bit/packet count to decode, and bit/packet error corrected by FEC.
> > But unfortunately, CXD2880 demodulator does not have such function.  
> 
> No, this is not a requirement. What actually happens is that userspace
> expect those values to be monotonically incremented, as new data is
> made available.
> 
> > 
> > Instead of it, Sony demodulator has bit/packet error counter for BER, PER calculation.
> > The user should configure total bit/packet count (denominator) for BER/PER measurement by some register setting.  
> 
> Yeah, that's a common practice: usually, the counters are initialized
> by some registers (on a few hardware, it is hardcoded).
> 
> > The IC hardware will update the error counter automatically in period determined by
> > configured total bit/packet count (denominator).  
> 
> So, the driver need to be sure that it won't read the register too
> early, e. g. it should either be reading some register bits to know
> when to read the data, or it needs a logic that will estimate when
> the data will be ready, not updating the values to early.
> 
> An example of the first case is the mb86a20s. There, you can see at:
> mb86a20s_get_pre_ber():
> 
> 	/* Check if the BER measures are already available */
> 	rc = mb86a20s_readreg(state, 0x54);
> 	if (rc < 0)
> 		return rc;
> 
> 	/* Check if data is available for that layer */
> 	if (!(rc & (1 << layer))) {
> 		dev_dbg(&state->i2c->dev,
> 			"%s: preBER for layer %c is not available yet.\n",
> 			__func__, 'A' + layer);
> 		return -EBUSY;
> 	}
> 
> An example of the second case is dib8000. There, you can see at:
> dib8000_get_stats():
> 
> 
> 	/* Check if time for stats was elapsed */
> 	if (time_after(jiffies, state->per_jiffies_stats)) {
> 		state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000);
> 
> ...
> 		/* Get UCB measures */
> 		dib8000_read_unc_blocks(fe, &val);
> 		if (val < state->init_ucb)
> 			state->init_ucb += 0x100000000LL;
> 
> 		c->block_error.stat[0].scale = FE_SCALE_COUNTER;
> 		c->block_error.stat[0].uvalue = val + state->init_ucb;
> 
> 		/* Estimate the number of packets based on bitrate */
> 		if (!time_us)
> 			time_us = dib8000_get_time_us(fe, -1);
> 
> 		if (time_us) {
> 			blocks = 1250000ULL * 1000000ULL;
> 			do_div(blocks, time_us * 8 * 204);
> 			c->block_count.stat[0].scale = FE_SCALE_COUNTER;
> 			c->block_count.stat[0].uvalue += blocks;
> 		}
> 
> 		show_per_stats = 1;
> 	}
> 
> At the above, the hardware is set to provide the number of uncorrected
> blocks on every second, and the logic there calculates the number of
> blocks based on the bitrate. It shouldn't be hard to do the opposite
> (e. g. set the hardware for a given number of blocks and calculate
> the time - I remember I coded it already - just don't remember on
> what driver - perhaps on an earlier implementation at mb86a20s).
> 
> In any case, as the code will require periodic pulls, in order
> to provide monotonic counters, we implement it via get_stats(), as
> the DVB core will ensure that it will be called periodically
> (typically 3 times per second).
> 
> > 
> > So, we implemented like as follows.
> > 
> > DTV_STAT_PRE_ERROR_BIT_COUNT
> > DTV_STAT_POST_ERROR_BIT_COUNT
> > DTV_STAT_ERROR_BLOCK_COUNT  
> > => Return bit/packet error counter value in period determined by configured total bit/packet count (numerator).    
> > 
> > DTV_STAT_PRE_TOTAL_BIT_COUNT
> > DTV_STAT_POST_TOTAL_BIT_COUNT
> > DTV_STAT_TOTAL_BLOCK_COUNT  
> > => Return currently configured total bit/packet count (denominator).    
> > 
> > By this implementation, the user can calculate BER, PER by following calculation.
> > 
> > DTV_STAT_PRE_ERROR_BIT_COUNT / DTV_STAT_PRE_TOTAL_BIT_COUNT
> > DTV_STAT_POST_ERROR_BIT_COUNT / DTV_STAT_POST_TOTAL_BIT_COUNT
> > DTV_STAT_ERROR_BLOCK_COUNT / DTV_STAT_TOTAL_BLOCK_COUNT
> > 
> > But instead, DTV_STAT_XXX_ERROR_XXX_COUNT values are not monotonically increased,
> > and DTV_STAT_XXX_TOTAL_XX_COUNT will return fixed value.  
> 
> That's wrong. you should be doing:
> 
> 	DTV_STAT_TOTAL_BLOCK_COUNT += number_of_blocks
> 
> every time the block counter registers are updated (and the
> same for the total bit counter).

Btw, as we had another developer with some doubts about stats
implementation, I wrote a chapter at the documentation in
order to explain how to properly implement it:

	https://linuxtv.org/downloads/v4l-dvb-apis-new/kapi/dtv-core.html#digital-tv-frontend-statistics

> 
> Thanks,
> Mauro



Thanks,
Mauro

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 08/15] [media] cxd2880: Add top level of the driver
  2017-06-25 12:15         ` Mauro Carvalho Chehab
@ 2017-06-26  1:24           ` Mauro Carvalho Chehab
  2017-06-27  1:51             ` Takiguchi, Yasunari
  0 siblings, 1 reply; 36+ messages in thread
From: Mauro Carvalho Chehab @ 2017-06-26  1:24 UTC (permalink / raw)
  To: Takiguchi, Yasunari
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS)

Em Sun, 25 Jun 2017 09:15:06 -0300
Mauro Carvalho Chehab <mchehab@s-opensource.com> escreveu:

> Em Fri, 23 Jun 2017 10:02:39 -0300
> Mauro Carvalho Chehab <mchehab@s-opensource.com> escreveu:
> 
> > Em Mon, 19 Jun 2017 16:56:13 +0900
> > "Takiguchi, Yasunari" <Yasunari.Takiguchi@sony.com> escreveu:
> >   
> > > >> +static int cxd2880_get_frontend_t(struct dvb_frontend *fe,
> > > >> +				struct dtv_frontend_properties *c)
> > > >> +{
> > > >> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
> > > >> +	int result = 0;
> > > >> +	struct cxd2880_priv *priv = NULL;
> > > >> +	enum cxd2880_dvbt_mode mode = CXD2880_DVBT_MODE_2K;
> > > >> +	enum cxd2880_dvbt_guard guard = CXD2880_DVBT_GUARD_1_32;
> > > >> +	struct cxd2880_dvbt_tpsinfo tps;
> > > >> +	enum cxd2880_tnrdmd_spectrum_sense sense;
> > > >> +	u16 snr = 0;
> > > >> +	int strength = 0;
> > > >> +	u32 pre_bit_err = 0, pre_bit_count = 0;
> > > >> +	u32 post_bit_err = 0, post_bit_count = 0;
> > > >> +	u32 block_err = 0, block_count = 0;
> > > >> +
> > > >> +	if ((!fe) || (!c)) {
> > > >> +		pr_err("%s: invalid arg\n", __func__);
> > > >> +		return -EINVAL;
> > > >> +	}
> > > >> +
> > > >> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
> > > >> +
> > > >> +	mutex_lock(priv->spi_mutex);
> > > >> +	ret = cxd2880_tnrdmd_dvbt_mon_mode_guard(&priv->tnrdmd,
> > > >> +						 &mode, &guard);
> > > >> +	mutex_unlock(priv->spi_mutex);
> > > >> +	if (ret == CXD2880_RESULT_OK) {
> > > >> +		switch (mode) {
> > > >> +		case CXD2880_DVBT_MODE_2K:
> > > >> +			c->transmission_mode = TRANSMISSION_MODE_2K;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_MODE_8K:
> > > >> +			c->transmission_mode = TRANSMISSION_MODE_8K;
> > > >> +			break;
> > > >> +		default:
> > > >> +			c->transmission_mode = TRANSMISSION_MODE_2K;
> > > >> +			dev_err(&priv->spi->dev, "%s: get invalid mode %d\n",
> > > >> +					__func__, mode);
> > > >> +			break;
> > > >> +		}
> > > >> +		switch (guard) {
> > > >> +		case CXD2880_DVBT_GUARD_1_32:
> > > >> +			c->guard_interval = GUARD_INTERVAL_1_32;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_GUARD_1_16:
> > > >> +			c->guard_interval = GUARD_INTERVAL_1_16;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_GUARD_1_8:
> > > >> +			c->guard_interval = GUARD_INTERVAL_1_8;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_GUARD_1_4:
> > > >> +			c->guard_interval = GUARD_INTERVAL_1_4;
> > > >> +			break;
> > > >> +		default:
> > > >> +			c->guard_interval = GUARD_INTERVAL_1_32;
> > > >> +			dev_err(&priv->spi->dev, "%s: get invalid guard %d\n",
> > > >> +					__func__, guard);
> > > >> +			break;
> > > >> +		}
> > > >> +	} else {
> > > >> +		c->transmission_mode = TRANSMISSION_MODE_2K;
> > > >> +		c->guard_interval = GUARD_INTERVAL_1_32;
> > > >> +		dev_dbg(&priv->spi->dev,
> > > >> +			"%s: ModeGuard err %d\n", __func__, ret);
> > > >> +	}
> > > >> +
> > > >> +	mutex_lock(priv->spi_mutex);
> > > >> +	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, &tps);
> > > >> +	mutex_unlock(priv->spi_mutex);
> > > >> +	if (ret == CXD2880_RESULT_OK) {
> > > >> +		switch (tps.hierarchy) {
> > > >> +		case CXD2880_DVBT_HIERARCHY_NON:
> > > >> +			c->hierarchy = HIERARCHY_NONE;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_HIERARCHY_1:
> > > >> +			c->hierarchy = HIERARCHY_1;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_HIERARCHY_2:
> > > >> +			c->hierarchy = HIERARCHY_2;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_HIERARCHY_4:
> > > >> +			c->hierarchy = HIERARCHY_4;
> > > >> +			break;
> > > >> +		default:
> > > >> +			c->hierarchy = HIERARCHY_NONE;
> > > >> +			dev_err(&priv->spi->dev,
> > > >> +				"%s: TPSInfo hierarchy invalid %d\n",
> > > >> +				__func__, tps.hierarchy);
> > > >> +			break;
> > > >> +		}
> > > >> +
> > > >> +		switch (tps.rate_hp) {
> > > >> +		case CXD2880_DVBT_CODERATE_1_2:
> > > >> +			c->code_rate_HP = FEC_1_2;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_CODERATE_2_3:
> > > >> +			c->code_rate_HP = FEC_2_3;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_CODERATE_3_4:
> > > >> +			c->code_rate_HP = FEC_3_4;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_CODERATE_5_6:
> > > >> +			c->code_rate_HP = FEC_5_6;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_CODERATE_7_8:
> > > >> +			c->code_rate_HP = FEC_7_8;
> > > >> +			break;
> > > >> +		default:
> > > >> +			c->code_rate_HP = FEC_NONE;
> > > >> +			dev_err(&priv->spi->dev,
> > > >> +				"%s: TPSInfo rateHP invalid %d\n",
> > > >> +				__func__, tps.rate_hp);
> > > >> +			break;
> > > >> +		}
> > > >> +		switch (tps.rate_lp) {
> > > >> +		case CXD2880_DVBT_CODERATE_1_2:
> > > >> +			c->code_rate_LP = FEC_1_2;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_CODERATE_2_3:
> > > >> +			c->code_rate_LP = FEC_2_3;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_CODERATE_3_4:
> > > >> +			c->code_rate_LP = FEC_3_4;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_CODERATE_5_6:
> > > >> +			c->code_rate_LP = FEC_5_6;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_CODERATE_7_8:
> > > >> +			c->code_rate_LP = FEC_7_8;
> > > >> +			break;
> > > >> +		default:
> > > >> +			c->code_rate_LP = FEC_NONE;
> > > >> +			dev_err(&priv->spi->dev,
> > > >> +				"%s: TPSInfo rateLP invalid %d\n",
> > > >> +				__func__, tps.rate_lp);
> > > >> +			break;
> > > >> +		}
> > > >> +		switch (tps.constellation) {
> > > >> +		case CXD2880_DVBT_CONSTELLATION_QPSK:
> > > >> +			c->modulation = QPSK;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_CONSTELLATION_16QAM:
> > > >> +			c->modulation = QAM_16;
> > > >> +			break;
> > > >> +		case CXD2880_DVBT_CONSTELLATION_64QAM:
> > > >> +			c->modulation = QAM_64;
> > > >> +			break;
> > > >> +		default:
> > > >> +			c->modulation = QPSK;
> > > >> +			dev_err(&priv->spi->dev,
> > > >> +				"%s: TPSInfo constellation invalid %d\n",
> > > >> +				__func__, tps.constellation);
> > > >> +			break;
> > > >> +		}
> > > >> +	} else {
> > > >> +		c->hierarchy = HIERARCHY_NONE;
> > > >> +		c->code_rate_HP = FEC_NONE;
> > > >> +		c->code_rate_LP = FEC_NONE;
> > > >> +		c->modulation = QPSK;
> > > >> +		dev_dbg(&priv->spi->dev,
> > > >> +			"%s: TPS info err %d\n", __func__, ret);
> > > >> +	}
> > > >> +
> > > >> +	mutex_lock(priv->spi_mutex);
> > > >> +	ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(&priv->tnrdmd, &sense);
> > > >> +	mutex_unlock(priv->spi_mutex);
> > > >> +	if (ret == CXD2880_RESULT_OK) {
> > > >> +		switch (sense) {
> > > >> +		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
> > > >> +			c->inversion = INVERSION_OFF;
> > > >> +			break;
> > > >> +		case CXD2880_TNRDMD_SPECTRUM_INV:
> > > >> +			c->inversion = INVERSION_ON;
> > > >> +			break;
> > > >> +		default:
> > > >> +			c->inversion = INVERSION_OFF;
> > > >> +			dev_err(&priv->spi->dev,
> > > >> +				"%s: spectrum sense invalid %d\n",
> > > >> +				__func__, sense);
> > > >> +			break;
> > > >> +		}
> > > >> +	} else {
> > > >> +		c->inversion = INVERSION_OFF;
> > > >> +		dev_dbg(&priv->spi->dev,
> > > >> +			"%s: spectrum_sense %d\n", __func__, ret);
> > > >> +	}
> > > >> +
> > > >> +	mutex_lock(priv->spi_mutex);
> > > >> +	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
> > > >> +	mutex_unlock(priv->spi_mutex);
> > > >> +	if (ret == CXD2880_RESULT_OK) {
> > > >> +		c->strength.len = 1;
> > > >> +		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
> > > >> +		c->strength.stat[0].svalue = strength;
> > > >> +	} else {
> > > >> +		c->strength.len = 1;
> > > >> +		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> > > >> +		dev_dbg(&priv->spi->dev, "%s: mon_rf_lvl %d\n",
> > > >> +			__func__, result);
> > > >> +	}
> > > >> +
> > > >> +	result = cxd2880_read_snr(fe, &snr);
> > > >> +	if (!result) {
> > > >> +		c->cnr.len = 1;
> > > >> +		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
> > > >> +		c->cnr.stat[0].svalue = snr;
> > > >> +	} else {
> > > >> +		c->cnr.len = 1;
> > > >> +		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
> > > >> +		dev_dbg(&priv->spi->dev, "%s: read_snr %d\n", __func__, result);
> > > >> +	}
> > > >> +
> > > >> +	mutex_lock(priv->spi_mutex);
> > > >> +	ret = cxd2880_pre_bit_err_t(&priv->tnrdmd, &pre_bit_err,
> > > >> +					&pre_bit_count);      
> > > > 
> > > > Hmm... reading BER-based measures at get_frontend() may not be
> > > > the best idea, depending on how the hardware works.
> > > > 
> > > > I mean, you need to be sure that, if userspace is calling it too
> > > > often, it won't affect the hardware or the measurement.
> > > > 
> > > > On other drivers, where each call generates an I2C access,
> > > > we do that by moving the code that actually call the hardware
> > > > at get status, and we use jiffies to be sure that it won't be
> > > > called too often.
> > > > 
> > > > I dunno if, on your SPI design, this would be an issue or not.      
> > > 
> > > CXD2880 IC can accept frequently counter register access by SPI.
> > > Even if such access occurred, it will not affect to IC behavior.
> > > We checked Sony demodulator IC driver code, dvb_frontends/cxd2841er.c and it reads register information
> > > when get_frontend called.
> > > 
> > > In fact, CXD2880 IC do NOT have bit error/packet error counter function to achieve DVB framework API completely.
> > > Sorry for long explanation, but I'll write in detail.
> > > 
> > > DTV_STAT_PRE_ERROR_BIT_COUNT
> > > DTV_STAT_PRE_TOTAL_BIT_COUNT
> > > DTV_STAT_POST_ERROR_BIT_COUNT
> > > DTV_STAT_POST_TOTAL_BIT_COUNT
> > > DTV_STAT_ERROR_BLOCK_COUNT
> > > DTV_STAT_TOTAL_BLOCK_COUNT
> > > 
> > > From DVB framework documentation, it seems that the demodulator hardware should have counter
> > > to accumulate total bit/packet count to decode, and bit/packet error corrected by FEC.
> > > But unfortunately, CXD2880 demodulator does not have such function.    
> > 
> > No, this is not a requirement. What actually happens is that userspace
> > expect those values to be monotonically incremented, as new data is
> > made available.
> >   
> > > 
> > > Instead of it, Sony demodulator has bit/packet error counter for BER, PER calculation.
> > > The user should configure total bit/packet count (denominator) for BER/PER measurement by some register setting.    
> > 
> > Yeah, that's a common practice: usually, the counters are initialized
> > by some registers (on a few hardware, it is hardcoded).
> >   
> > > The IC hardware will update the error counter automatically in period determined by
> > > configured total bit/packet count (denominator).    
> > 
> > So, the driver need to be sure that it won't read the register too
> > early, e. g. it should either be reading some register bits to know
> > when to read the data, or it needs a logic that will estimate when
> > the data will be ready, not updating the values to early.
> > 
> > An example of the first case is the mb86a20s. There, you can see at:
> > mb86a20s_get_pre_ber():
> > 
> > 	/* Check if the BER measures are already available */
> > 	rc = mb86a20s_readreg(state, 0x54);
> > 	if (rc < 0)
> > 		return rc;
> > 
> > 	/* Check if data is available for that layer */
> > 	if (!(rc & (1 << layer))) {
> > 		dev_dbg(&state->i2c->dev,
> > 			"%s: preBER for layer %c is not available yet.\n",
> > 			__func__, 'A' + layer);
> > 		return -EBUSY;
> > 	}
> > 
> > An example of the second case is dib8000. There, you can see at:
> > dib8000_get_stats():
> > 
> > 
> > 	/* Check if time for stats was elapsed */
> > 	if (time_after(jiffies, state->per_jiffies_stats)) {
> > 		state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000);
> > 
> > ...
> > 		/* Get UCB measures */
> > 		dib8000_read_unc_blocks(fe, &val);
> > 		if (val < state->init_ucb)
> > 			state->init_ucb += 0x100000000LL;
> > 
> > 		c->block_error.stat[0].scale = FE_SCALE_COUNTER;
> > 		c->block_error.stat[0].uvalue = val + state->init_ucb;
> > 
> > 		/* Estimate the number of packets based on bitrate */
> > 		if (!time_us)
> > 			time_us = dib8000_get_time_us(fe, -1);
> > 
> > 		if (time_us) {
> > 			blocks = 1250000ULL * 1000000ULL;
> > 			do_div(blocks, time_us * 8 * 204);
> > 			c->block_count.stat[0].scale = FE_SCALE_COUNTER;
> > 			c->block_count.stat[0].uvalue += blocks;
> > 		}
> > 
> > 		show_per_stats = 1;
> > 	}
> > 
> > At the above, the hardware is set to provide the number of uncorrected
> > blocks on every second, and the logic there calculates the number of
> > blocks based on the bitrate. It shouldn't be hard to do the opposite
> > (e. g. set the hardware for a given number of blocks and calculate
> > the time - I remember I coded it already - just don't remember on
> > what driver - perhaps on an earlier implementation at mb86a20s).
> > 
> > In any case, as the code will require periodic pulls, in order
> > to provide monotonic counters, we implement it via get_stats(), as
> > the DVB core will ensure that it will be called periodically
> > (typically 3 times per second).
> >   
> > > 
> > > So, we implemented like as follows.
> > > 
> > > DTV_STAT_PRE_ERROR_BIT_COUNT
> > > DTV_STAT_POST_ERROR_BIT_COUNT
> > > DTV_STAT_ERROR_BLOCK_COUNT    
> > > => Return bit/packet error counter value in period determined by configured total bit/packet count (numerator).      
> > > 
> > > DTV_STAT_PRE_TOTAL_BIT_COUNT
> > > DTV_STAT_POST_TOTAL_BIT_COUNT
> > > DTV_STAT_TOTAL_BLOCK_COUNT    
> > > => Return currently configured total bit/packet count (denominator).      
> > > 
> > > By this implementation, the user can calculate BER, PER by following calculation.
> > > 
> > > DTV_STAT_PRE_ERROR_BIT_COUNT / DTV_STAT_PRE_TOTAL_BIT_COUNT
> > > DTV_STAT_POST_ERROR_BIT_COUNT / DTV_STAT_POST_TOTAL_BIT_COUNT
> > > DTV_STAT_ERROR_BLOCK_COUNT / DTV_STAT_TOTAL_BLOCK_COUNT
> > > 
> > > But instead, DTV_STAT_XXX_ERROR_XXX_COUNT values are not monotonically increased,
> > > and DTV_STAT_XXX_TOTAL_XX_COUNT will return fixed value.    
> > 
> > That's wrong. you should be doing:
> > 
> > 	DTV_STAT_TOTAL_BLOCK_COUNT += number_of_blocks
> > 
> > every time the block counter registers are updated (and the
> > same for the total bit counter).  
> 
> Btw, as we had another developer with some doubts about stats
> implementation, I wrote a chapter at the documentation in
> order to explain how to properly implement it:
> 
> 	https://linuxtv.org/downloads/v4l-dvb-apis-new/kapi/dtv-core.html#digital-tv-frontend-statistics

I added a few more patches today, improving the docs. They're not
yet upstream, but you can see the documentation at this link:

	https://www.infradead.org/~mchehab/kernel_docs/media/kapi/dtv-core.html#digital-tv-frontend-kabi

> 
> > 
> > Thanks,
> > Mauro  
> 
> 
> 
> Thanks,
> Mauro



Thanks,
Mauro

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 08/15] [media] cxd2880: Add top level of the driver
  2017-06-26  1:24           ` Mauro Carvalho Chehab
@ 2017-06-27  1:51             ` Takiguchi, Yasunari
  0 siblings, 0 replies; 36+ messages in thread
From: Takiguchi, Yasunari @ 2017-06-27  1:51 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: linux-kernel, devicetree, linux-media, tbird20d, frowand.list,
	Yamamoto, Masayuki, Nozawa, Hideki (STWN),
	Yonezawa, Kota, Matsumoto, Toshihiko, Watanabe, Satoshi (SSS),
	yasunari.takiguchi



On 2017/06/26 10:24, Mauro Carvalho Chehab wrote:
> Em Sun, 25 Jun 2017 09:15:06 -0300
> Mauro Carvalho Chehab <mchehab@s-opensource.com> escreveu:
> 
>> Em Fri, 23 Jun 2017 10:02:39 -0300
>> Mauro Carvalho Chehab <mchehab@s-opensource.com> escreveu:
>>
>>> Em Mon, 19 Jun 2017 16:56:13 +0900
>>> "Takiguchi, Yasunari" <Yasunari.Takiguchi@sony.com> escreveu:
>>>   
>>>>>> +static int cxd2880_get_frontend_t(struct dvb_frontend *fe,
>>>>>> +				struct dtv_frontend_properties *c)
>>>>>> +{
>>>>>> +	enum cxd2880_ret ret = CXD2880_RESULT_OK;
>>>>>> +	int result = 0;
>>>>>> +	struct cxd2880_priv *priv = NULL;
>>>>>> +	enum cxd2880_dvbt_mode mode = CXD2880_DVBT_MODE_2K;
>>>>>> +	enum cxd2880_dvbt_guard guard = CXD2880_DVBT_GUARD_1_32;
>>>>>> +	struct cxd2880_dvbt_tpsinfo tps;
>>>>>> +	enum cxd2880_tnrdmd_spectrum_sense sense;
>>>>>> +	u16 snr = 0;
>>>>>> +	int strength = 0;
>>>>>> +	u32 pre_bit_err = 0, pre_bit_count = 0;
>>>>>> +	u32 post_bit_err = 0, post_bit_count = 0;
>>>>>> +	u32 block_err = 0, block_count = 0;
>>>>>> +
>>>>>> +	if ((!fe) || (!c)) {
>>>>>> +		pr_err("%s: invalid arg\n", __func__);
>>>>>> +		return -EINVAL;
>>>>>> +	}
>>>>>> +
>>>>>> +	priv = (struct cxd2880_priv *)fe->demodulator_priv;
>>>>>> +
>>>>>> +	mutex_lock(priv->spi_mutex);
>>>>>> +	ret = cxd2880_tnrdmd_dvbt_mon_mode_guard(&priv->tnrdmd,
>>>>>> +						 &mode, &guard);
>>>>>> +	mutex_unlock(priv->spi_mutex);
>>>>>> +	if (ret == CXD2880_RESULT_OK) {
>>>>>> +		switch (mode) {
>>>>>> +		case CXD2880_DVBT_MODE_2K:
>>>>>> +			c->transmission_mode = TRANSMISSION_MODE_2K;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_MODE_8K:
>>>>>> +			c->transmission_mode = TRANSMISSION_MODE_8K;
>>>>>> +			break;
>>>>>> +		default:
>>>>>> +			c->transmission_mode = TRANSMISSION_MODE_2K;
>>>>>> +			dev_err(&priv->spi->dev, "%s: get invalid mode %d\n",
>>>>>> +					__func__, mode);
>>>>>> +			break;
>>>>>> +		}
>>>>>> +		switch (guard) {
>>>>>> +		case CXD2880_DVBT_GUARD_1_32:
>>>>>> +			c->guard_interval = GUARD_INTERVAL_1_32;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_GUARD_1_16:
>>>>>> +			c->guard_interval = GUARD_INTERVAL_1_16;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_GUARD_1_8:
>>>>>> +			c->guard_interval = GUARD_INTERVAL_1_8;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_GUARD_1_4:
>>>>>> +			c->guard_interval = GUARD_INTERVAL_1_4;
>>>>>> +			break;
>>>>>> +		default:
>>>>>> +			c->guard_interval = GUARD_INTERVAL_1_32;
>>>>>> +			dev_err(&priv->spi->dev, "%s: get invalid guard %d\n",
>>>>>> +					__func__, guard);
>>>>>> +			break;
>>>>>> +		}
>>>>>> +	} else {
>>>>>> +		c->transmission_mode = TRANSMISSION_MODE_2K;
>>>>>> +		c->guard_interval = GUARD_INTERVAL_1_32;
>>>>>> +		dev_dbg(&priv->spi->dev,
>>>>>> +			"%s: ModeGuard err %d\n", __func__, ret);
>>>>>> +	}
>>>>>> +
>>>>>> +	mutex_lock(priv->spi_mutex);
>>>>>> +	ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, &tps);
>>>>>> +	mutex_unlock(priv->spi_mutex);
>>>>>> +	if (ret == CXD2880_RESULT_OK) {
>>>>>> +		switch (tps.hierarchy) {
>>>>>> +		case CXD2880_DVBT_HIERARCHY_NON:
>>>>>> +			c->hierarchy = HIERARCHY_NONE;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_HIERARCHY_1:
>>>>>> +			c->hierarchy = HIERARCHY_1;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_HIERARCHY_2:
>>>>>> +			c->hierarchy = HIERARCHY_2;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_HIERARCHY_4:
>>>>>> +			c->hierarchy = HIERARCHY_4;
>>>>>> +			break;
>>>>>> +		default:
>>>>>> +			c->hierarchy = HIERARCHY_NONE;
>>>>>> +			dev_err(&priv->spi->dev,
>>>>>> +				"%s: TPSInfo hierarchy invalid %d\n",
>>>>>> +				__func__, tps.hierarchy);
>>>>>> +			break;
>>>>>> +		}
>>>>>> +
>>>>>> +		switch (tps.rate_hp) {
>>>>>> +		case CXD2880_DVBT_CODERATE_1_2:
>>>>>> +			c->code_rate_HP = FEC_1_2;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_CODERATE_2_3:
>>>>>> +			c->code_rate_HP = FEC_2_3;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_CODERATE_3_4:
>>>>>> +			c->code_rate_HP = FEC_3_4;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_CODERATE_5_6:
>>>>>> +			c->code_rate_HP = FEC_5_6;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_CODERATE_7_8:
>>>>>> +			c->code_rate_HP = FEC_7_8;
>>>>>> +			break;
>>>>>> +		default:
>>>>>> +			c->code_rate_HP = FEC_NONE;
>>>>>> +			dev_err(&priv->spi->dev,
>>>>>> +				"%s: TPSInfo rateHP invalid %d\n",
>>>>>> +				__func__, tps.rate_hp);
>>>>>> +			break;
>>>>>> +		}
>>>>>> +		switch (tps.rate_lp) {
>>>>>> +		case CXD2880_DVBT_CODERATE_1_2:
>>>>>> +			c->code_rate_LP = FEC_1_2;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_CODERATE_2_3:
>>>>>> +			c->code_rate_LP = FEC_2_3;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_CODERATE_3_4:
>>>>>> +			c->code_rate_LP = FEC_3_4;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_CODERATE_5_6:
>>>>>> +			c->code_rate_LP = FEC_5_6;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_CODERATE_7_8:
>>>>>> +			c->code_rate_LP = FEC_7_8;
>>>>>> +			break;
>>>>>> +		default:
>>>>>> +			c->code_rate_LP = FEC_NONE;
>>>>>> +			dev_err(&priv->spi->dev,
>>>>>> +				"%s: TPSInfo rateLP invalid %d\n",
>>>>>> +				__func__, tps.rate_lp);
>>>>>> +			break;
>>>>>> +		}
>>>>>> +		switch (tps.constellation) {
>>>>>> +		case CXD2880_DVBT_CONSTELLATION_QPSK:
>>>>>> +			c->modulation = QPSK;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_CONSTELLATION_16QAM:
>>>>>> +			c->modulation = QAM_16;
>>>>>> +			break;
>>>>>> +		case CXD2880_DVBT_CONSTELLATION_64QAM:
>>>>>> +			c->modulation = QAM_64;
>>>>>> +			break;
>>>>>> +		default:
>>>>>> +			c->modulation = QPSK;
>>>>>> +			dev_err(&priv->spi->dev,
>>>>>> +				"%s: TPSInfo constellation invalid %d\n",
>>>>>> +				__func__, tps.constellation);
>>>>>> +			break;
>>>>>> +		}
>>>>>> +	} else {
>>>>>> +		c->hierarchy = HIERARCHY_NONE;
>>>>>> +		c->code_rate_HP = FEC_NONE;
>>>>>> +		c->code_rate_LP = FEC_NONE;
>>>>>> +		c->modulation = QPSK;
>>>>>> +		dev_dbg(&priv->spi->dev,
>>>>>> +			"%s: TPS info err %d\n", __func__, ret);
>>>>>> +	}
>>>>>> +
>>>>>> +	mutex_lock(priv->spi_mutex);
>>>>>> +	ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(&priv->tnrdmd, &sense);
>>>>>> +	mutex_unlock(priv->spi_mutex);
>>>>>> +	if (ret == CXD2880_RESULT_OK) {
>>>>>> +		switch (sense) {
>>>>>> +		case CXD2880_TNRDMD_SPECTRUM_NORMAL:
>>>>>> +			c->inversion = INVERSION_OFF;
>>>>>> +			break;
>>>>>> +		case CXD2880_TNRDMD_SPECTRUM_INV:
>>>>>> +			c->inversion = INVERSION_ON;
>>>>>> +			break;
>>>>>> +		default:
>>>>>> +			c->inversion = INVERSION_OFF;
>>>>>> +			dev_err(&priv->spi->dev,
>>>>>> +				"%s: spectrum sense invalid %d\n",
>>>>>> +				__func__, sense);
>>>>>> +			break;
>>>>>> +		}
>>>>>> +	} else {
>>>>>> +		c->inversion = INVERSION_OFF;
>>>>>> +		dev_dbg(&priv->spi->dev,
>>>>>> +			"%s: spectrum_sense %d\n", __func__, ret);
>>>>>> +	}
>>>>>> +
>>>>>> +	mutex_lock(priv->spi_mutex);
>>>>>> +	ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
>>>>>> +	mutex_unlock(priv->spi_mutex);
>>>>>> +	if (ret == CXD2880_RESULT_OK) {
>>>>>> +		c->strength.len = 1;
>>>>>> +		c->strength.stat[0].scale = FE_SCALE_DECIBEL;
>>>>>> +		c->strength.stat[0].svalue = strength;
>>>>>> +	} else {
>>>>>> +		c->strength.len = 1;
>>>>>> +		c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
>>>>>> +		dev_dbg(&priv->spi->dev, "%s: mon_rf_lvl %d\n",
>>>>>> +			__func__, result);
>>>>>> +	}
>>>>>> +
>>>>>> +	result = cxd2880_read_snr(fe, &snr);
>>>>>> +	if (!result) {
>>>>>> +		c->cnr.len = 1;
>>>>>> +		c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
>>>>>> +		c->cnr.stat[0].svalue = snr;
>>>>>> +	} else {
>>>>>> +		c->cnr.len = 1;
>>>>>> +		c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
>>>>>> +		dev_dbg(&priv->spi->dev, "%s: read_snr %d\n", __func__, result);
>>>>>> +	}
>>>>>> +
>>>>>> +	mutex_lock(priv->spi_mutex);
>>>>>> +	ret = cxd2880_pre_bit_err_t(&priv->tnrdmd, &pre_bit_err,
>>>>>> +					&pre_bit_count);      
>>>>>
>>>>> Hmm... reading BER-based measures at get_frontend() may not be
>>>>> the best idea, depending on how the hardware works.
>>>>>
>>>>> I mean, you need to be sure that, if userspace is calling it too
>>>>> often, it won't affect the hardware or the measurement.
>>>>>
>>>>> On other drivers, where each call generates an I2C access,
>>>>> we do that by moving the code that actually call the hardware
>>>>> at get status, and we use jiffies to be sure that it won't be
>>>>> called too often.
>>>>>
>>>>> I dunno if, on your SPI design, this would be an issue or not.      
>>>>
>>>> CXD2880 IC can accept frequently counter register access by SPI.
>>>> Even if such access occurred, it will not affect to IC behavior.
>>>> We checked Sony demodulator IC driver code, dvb_frontends/cxd2841er.c and it reads register information
>>>> when get_frontend called.
>>>>
>>>> In fact, CXD2880 IC do NOT have bit error/packet error counter function to achieve DVB framework API completely.
>>>> Sorry for long explanation, but I'll write in detail.
>>>>
>>>> DTV_STAT_PRE_ERROR_BIT_COUNT
>>>> DTV_STAT_PRE_TOTAL_BIT_COUNT
>>>> DTV_STAT_POST_ERROR_BIT_COUNT
>>>> DTV_STAT_POST_TOTAL_BIT_COUNT
>>>> DTV_STAT_ERROR_BLOCK_COUNT
>>>> DTV_STAT_TOTAL_BLOCK_COUNT
>>>>
>>>> From DVB framework documentation, it seems that the demodulator hardware should have counter
>>>> to accumulate total bit/packet count to decode, and bit/packet error corrected by FEC.
>>>> But unfortunately, CXD2880 demodulator does not have such function.    
>>>
>>> No, this is not a requirement. What actually happens is that userspace
>>> expect those values to be monotonically incremented, as new data is
>>> made available.
>>>   
>>>>
>>>> Instead of it, Sony demodulator has bit/packet error counter for BER, PER calculation.
>>>> The user should configure total bit/packet count (denominator) for BER/PER measurement by some register setting.    
>>>
>>> Yeah, that's a common practice: usually, the counters are initialized
>>> by some registers (on a few hardware, it is hardcoded).
>>>   
>>>> The IC hardware will update the error counter automatically in period determined by
>>>> configured total bit/packet count (denominator).    
>>>
>>> So, the driver need to be sure that it won't read the register too
>>> early, e. g. it should either be reading some register bits to know
>>> when to read the data, or it needs a logic that will estimate when
>>> the data will be ready, not updating the values to early.
>>>
>>> An example of the first case is the mb86a20s. There, you can see at:
>>> mb86a20s_get_pre_ber():
>>>
>>> 	/* Check if the BER measures are already available */
>>> 	rc = mb86a20s_readreg(state, 0x54);
>>> 	if (rc < 0)
>>> 		return rc;
>>>
>>> 	/* Check if data is available for that layer */
>>> 	if (!(rc & (1 << layer))) {
>>> 		dev_dbg(&state->i2c->dev,
>>> 			"%s: preBER for layer %c is not available yet.\n",
>>> 			__func__, 'A' + layer);
>>> 		return -EBUSY;
>>> 	}
>>>
>>> An example of the second case is dib8000. There, you can see at:
>>> dib8000_get_stats():
>>>
>>>
>>> 	/* Check if time for stats was elapsed */
>>> 	if (time_after(jiffies, state->per_jiffies_stats)) {
>>> 		state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000);
>>>
>>> ...
>>> 		/* Get UCB measures */
>>> 		dib8000_read_unc_blocks(fe, &val);
>>> 		if (val < state->init_ucb)
>>> 			state->init_ucb += 0x100000000LL;
>>>
>>> 		c->block_error.stat[0].scale = FE_SCALE_COUNTER;
>>> 		c->block_error.stat[0].uvalue = val + state->init_ucb;
>>>
>>> 		/* Estimate the number of packets based on bitrate */
>>> 		if (!time_us)
>>> 			time_us = dib8000_get_time_us(fe, -1);
>>>
>>> 		if (time_us) {
>>> 			blocks = 1250000ULL * 1000000ULL;
>>> 			do_div(blocks, time_us * 8 * 204);
>>> 			c->block_count.stat[0].scale = FE_SCALE_COUNTER;
>>> 			c->block_count.stat[0].uvalue += blocks;
>>> 		}
>>>
>>> 		show_per_stats = 1;
>>> 	}
>>>
>>> At the above, the hardware is set to provide the number of uncorrected
>>> blocks on every second, and the logic there calculates the number of
>>> blocks based on the bitrate. It shouldn't be hard to do the opposite
>>> (e. g. set the hardware for a given number of blocks and calculate
>>> the time - I remember I coded it already - just don't remember on
>>> what driver - perhaps on an earlier implementation at mb86a20s).
>>>
>>> In any case, as the code will require periodic pulls, in order
>>> to provide monotonic counters, we implement it via get_stats(), as
>>> the DVB core will ensure that it will be called periodically
>>> (typically 3 times per second).
>>>   
>>>>
>>>> So, we implemented like as follows.
>>>>
>>>> DTV_STAT_PRE_ERROR_BIT_COUNT
>>>> DTV_STAT_POST_ERROR_BIT_COUNT
>>>> DTV_STAT_ERROR_BLOCK_COUNT    
>>>> => Return bit/packet error counter value in period determined by configured total bit/packet count (numerator).      
>>>>
>>>> DTV_STAT_PRE_TOTAL_BIT_COUNT
>>>> DTV_STAT_POST_TOTAL_BIT_COUNT
>>>> DTV_STAT_TOTAL_BLOCK_COUNT    
>>>> => Return currently configured total bit/packet count (denominator).      
>>>>
>>>> By this implementation, the user can calculate BER, PER by following calculation.
>>>>
>>>> DTV_STAT_PRE_ERROR_BIT_COUNT / DTV_STAT_PRE_TOTAL_BIT_COUNT
>>>> DTV_STAT_POST_ERROR_BIT_COUNT / DTV_STAT_POST_TOTAL_BIT_COUNT
>>>> DTV_STAT_ERROR_BLOCK_COUNT / DTV_STAT_TOTAL_BLOCK_COUNT
>>>>
>>>> But instead, DTV_STAT_XXX_ERROR_XXX_COUNT values are not monotonically increased,
>>>> and DTV_STAT_XXX_TOTAL_XX_COUNT will return fixed value.    
>>>
>>> That's wrong. you should be doing:
>>>
>>> 	DTV_STAT_TOTAL_BLOCK_COUNT += number_of_blocks
>>>
>>> every time the block counter registers are updated (and the
>>> same for the total bit counter).  
>>
>> Btw, as we had another developer with some doubts about stats
>> implementation, I wrote a chapter at the documentation in
>> order to explain how to properly implement it:
>>
>> 	https://linuxtv.org/downloads/v4l-dvb-apis-new/kapi/dtv-core.html#digital-tv-frontend-statistics
> 
> I added a few more patches today, improving the docs. They're not
> yet upstream, but you can see the documentation at this link:
> 
> 	https://www.infradead.org/~mchehab/kernel_docs/media/kapi/dtv-core.html#digital-tv-frontend-kabi

Thanks a lot for your detail explanation.
We understood your thought.
 
In fact, CXD2880 does NOT have mb86a20s like registers to know that
the error counters are updated from previous read or not.
 
In addition, unfortunately, CXD2880 does NOT have a feature that
the user can configure the error counter update period by time.
 
So, as you wrote in the documentation at above link, we'll need to implement like:
 “the driver need to ensure that it won’t be reading from the register too often
 and/or estimate the total number of bits/blocks.”

We'll discuss how to do it internally.

Thanks
Takiguchi

^ permalink raw reply	[flat|nested] 36+ messages in thread

end of thread, other threads:[~2017-06-27  1:52 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-04-14  1:50 [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Yasunari.Takiguchi
2017-04-14  2:00 ` [PATCH v2 01/15] [dt-bindings] [media] Add document file for CXD2880 SPI I/F Yasunari.Takiguchi
2017-04-17  4:58   ` Takiguchi, Yasunari
2017-04-14  2:08 ` [PATCH v2 02/15] [media] cxd2880-spi: Add support for CXD2008 SPI interface Yasunari.Takiguchi
2017-06-13 11:55   ` Mauro Carvalho Chehab
2017-04-14  2:17 ` [PATCH v2 03/15] [media] cxd2880: Add common files for the driver Yasunari.Takiguchi
2017-06-13 12:13   ` Mauro Carvalho Chehab
2017-04-14  2:22 ` [PATCH v2 04/15] [media] cxd2880: Add math functions " Yasunari.Takiguchi
2017-06-13 12:18   ` Mauro Carvalho Chehab
2017-04-14  2:25 ` [PATCH v2 05/15] [media] cxd2880: Add spi device IO routines Yasunari.Takiguchi
2017-06-13 12:25   ` Mauro Carvalho Chehab
2017-04-14  2:26 ` [PATCH v2 06/15] [media] cxd2880: Add tuner part of the driver Yasunari.Takiguchi
2017-06-13 13:09   ` Mauro Carvalho Chehab
2017-04-14  2:29 ` [PATCH v2 07/15] [media] cxd2880: Add integration layer for " Yasunari.Takiguchi
2017-04-14  2:31 ` [PATCH v2 08/15] [media] cxd2880: Add top level of " Yasunari.Takiguchi
2017-06-13 13:30   ` Mauro Carvalho Chehab
2017-06-19  7:56     ` Takiguchi, Yasunari
2017-06-23 13:02       ` Mauro Carvalho Chehab
2017-06-25 12:15         ` Mauro Carvalho Chehab
2017-06-26  1:24           ` Mauro Carvalho Chehab
2017-06-27  1:51             ` Takiguchi, Yasunari
2017-04-14  2:33 ` [PATCH v2 09/15] [media] cxd2880: Add DVB-T control functions " Yasunari.Takiguchi
2017-04-14  2:35 ` [PATCH v2 10/15] [media] cxd2880: Add DVB-T monitor and integration layer functions Yasunari.Takiguchi
2017-04-14  2:38 ` [PATCH v2 11/15] [media] cxd2880: Add DVB-T2 control functions for the driver Yasunari.Takiguchi
2017-04-14  2:40 ` [PATCH v2 12/15] [media] cxd2880: Add DVB-T2 monitor and integration layer functions Yasunari.Takiguchi
2017-04-14  2:44 ` [PATCH v2 13/15] [media] cxd2880: Add all Makefile files for the driver Yasunari.Takiguchi
2017-04-14  2:47 ` [PATCH v2 14/15] [media] cxd2880: Add all Kconfig " Yasunari.Takiguchi
2017-04-14  2:48 ` [PATCH v2 15/15] [media] cxd2880 : Update MAINTAINERS file for CXD2880 driver Yasunari.Takiguchi
2017-04-17  5:09 ` [PATCH v2 0/15] [dt-bindings] [media] Add document file and driver for Sony CXD2880 DVB-T2/T tuner + demodulator Takiguchi, Yasunari
2017-05-25  6:15   ` Takiguchi, Yasunari
2017-06-01  1:41     ` Takiguchi, Yasunari
2017-06-12 13:33       ` Abylay Ospan
2017-06-13  5:35         ` Takiguchi, Yasunari
2017-06-13  5:39           ` Abylay Ospan
2017-06-13 14:38     ` Mauro Carvalho Chehab
2017-06-14  3:01       ` Takiguchi, Yasunari

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).