18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ZyDAS ZD1301 driver (demodulator)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Antti Palosaari <crope@iki.fi>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "zd1301_demod.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_cistatic u8 zd1301_demod_gain = 0x38;
118c2ecf20Sopenharmony_cimodule_param_named(gain, zd1301_demod_gain, byte, 0644);
128c2ecf20Sopenharmony_ciMODULE_PARM_DESC(gain, "gain (value: 0x00 - 0x70, default: 0x38)");
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistruct zd1301_demod_dev {
158c2ecf20Sopenharmony_ci	struct platform_device *pdev;
168c2ecf20Sopenharmony_ci	struct dvb_frontend frontend;
178c2ecf20Sopenharmony_ci	struct i2c_adapter adapter;
188c2ecf20Sopenharmony_ci	u8 gain;
198c2ecf20Sopenharmony_ci};
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic int zd1301_demod_wreg(struct zd1301_demod_dev *dev, u16 reg, u8 val)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	struct platform_device *pdev = dev->pdev;
248c2ecf20Sopenharmony_ci	struct zd1301_demod_platform_data *pdata = pdev->dev.platform_data;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	return pdata->reg_write(pdata->reg_priv, reg, val);
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int zd1301_demod_rreg(struct zd1301_demod_dev *dev, u16 reg, u8 *val)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	struct platform_device *pdev = dev->pdev;
328c2ecf20Sopenharmony_ci	struct zd1301_demod_platform_data *pdata = pdev->dev.platform_data;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	return pdata->reg_read(pdata->reg_priv, reg, val);
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic int zd1301_demod_set_frontend(struct dvb_frontend *fe)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	struct zd1301_demod_dev *dev = fe->demodulator_priv;
408c2ecf20Sopenharmony_ci	struct platform_device *pdev = dev->pdev;
418c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
428c2ecf20Sopenharmony_ci	int ret;
438c2ecf20Sopenharmony_ci	u32 if_frequency;
448c2ecf20Sopenharmony_ci	u8 r6a50_val;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "frequency=%u bandwidth_hz=%u\n",
478c2ecf20Sopenharmony_ci		c->frequency, c->bandwidth_hz);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/* Program tuner */
508c2ecf20Sopenharmony_ci	if (fe->ops.tuner_ops.set_params &&
518c2ecf20Sopenharmony_ci	    fe->ops.tuner_ops.get_if_frequency) {
528c2ecf20Sopenharmony_ci		ret = fe->ops.tuner_ops.set_params(fe);
538c2ecf20Sopenharmony_ci		if (ret)
548c2ecf20Sopenharmony_ci			goto err;
558c2ecf20Sopenharmony_ci		ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
568c2ecf20Sopenharmony_ci		if (ret)
578c2ecf20Sopenharmony_ci			goto err;
588c2ecf20Sopenharmony_ci	} else {
598c2ecf20Sopenharmony_ci		ret = -EINVAL;
608c2ecf20Sopenharmony_ci		goto err;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "if_frequency=%u\n", if_frequency);
648c2ecf20Sopenharmony_ci	if (if_frequency != 36150000) {
658c2ecf20Sopenharmony_ci		ret = -EINVAL;
668c2ecf20Sopenharmony_ci		goto err;
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	switch (c->bandwidth_hz) {
708c2ecf20Sopenharmony_ci	case 6000000:
718c2ecf20Sopenharmony_ci		r6a50_val = 0x78;
728c2ecf20Sopenharmony_ci		break;
738c2ecf20Sopenharmony_ci	case 7000000:
748c2ecf20Sopenharmony_ci		r6a50_val = 0x68;
758c2ecf20Sopenharmony_ci		break;
768c2ecf20Sopenharmony_ci	case 8000000:
778c2ecf20Sopenharmony_ci		r6a50_val = 0x58;
788c2ecf20Sopenharmony_ci		break;
798c2ecf20Sopenharmony_ci	default:
808c2ecf20Sopenharmony_ci		ret = -EINVAL;
818c2ecf20Sopenharmony_ci		goto err;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a60, 0x11);
858c2ecf20Sopenharmony_ci	if (ret)
868c2ecf20Sopenharmony_ci		goto err;
878c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a47, 0x46);
888c2ecf20Sopenharmony_ci	if (ret)
898c2ecf20Sopenharmony_ci		goto err;
908c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a48, 0x46);
918c2ecf20Sopenharmony_ci	if (ret)
928c2ecf20Sopenharmony_ci		goto err;
938c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a4a, 0x15);
948c2ecf20Sopenharmony_ci	if (ret)
958c2ecf20Sopenharmony_ci		goto err;
968c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a4b, 0x63);
978c2ecf20Sopenharmony_ci	if (ret)
988c2ecf20Sopenharmony_ci		goto err;
998c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a5b, 0x99);
1008c2ecf20Sopenharmony_ci	if (ret)
1018c2ecf20Sopenharmony_ci		goto err;
1028c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a3b, 0x10);
1038c2ecf20Sopenharmony_ci	if (ret)
1048c2ecf20Sopenharmony_ci		goto err;
1058c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6806, 0x01);
1068c2ecf20Sopenharmony_ci	if (ret)
1078c2ecf20Sopenharmony_ci		goto err;
1088c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a41, 0x08);
1098c2ecf20Sopenharmony_ci	if (ret)
1108c2ecf20Sopenharmony_ci		goto err;
1118c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a42, 0x46);
1128c2ecf20Sopenharmony_ci	if (ret)
1138c2ecf20Sopenharmony_ci		goto err;
1148c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a44, 0x14);
1158c2ecf20Sopenharmony_ci	if (ret)
1168c2ecf20Sopenharmony_ci		goto err;
1178c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a45, 0x67);
1188c2ecf20Sopenharmony_ci	if (ret)
1198c2ecf20Sopenharmony_ci		goto err;
1208c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a38, 0x00);
1218c2ecf20Sopenharmony_ci	if (ret)
1228c2ecf20Sopenharmony_ci		goto err;
1238c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a4c, 0x52);
1248c2ecf20Sopenharmony_ci	if (ret)
1258c2ecf20Sopenharmony_ci		goto err;
1268c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a49, 0x2a);
1278c2ecf20Sopenharmony_ci	if (ret)
1288c2ecf20Sopenharmony_ci		goto err;
1298c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6840, 0x2e);
1308c2ecf20Sopenharmony_ci	if (ret)
1318c2ecf20Sopenharmony_ci		goto err;
1328c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a50, r6a50_val);
1338c2ecf20Sopenharmony_ci	if (ret)
1348c2ecf20Sopenharmony_ci		goto err;
1358c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a38, 0x07);
1368c2ecf20Sopenharmony_ci	if (ret)
1378c2ecf20Sopenharmony_ci		goto err;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return 0;
1408c2ecf20Sopenharmony_cierr:
1418c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "failed=%d\n", ret);
1428c2ecf20Sopenharmony_ci	return ret;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int zd1301_demod_sleep(struct dvb_frontend *fe)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	struct zd1301_demod_dev *dev = fe->demodulator_priv;
1488c2ecf20Sopenharmony_ci	struct platform_device *pdev = dev->pdev;
1498c2ecf20Sopenharmony_ci	int ret;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "\n");
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a43, 0x70);
1548c2ecf20Sopenharmony_ci	if (ret)
1558c2ecf20Sopenharmony_ci		goto err;
1568c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x684e, 0x00);
1578c2ecf20Sopenharmony_ci	if (ret)
1588c2ecf20Sopenharmony_ci		goto err;
1598c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6849, 0x00);
1608c2ecf20Sopenharmony_ci	if (ret)
1618c2ecf20Sopenharmony_ci		goto err;
1628c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x68e2, 0xd7);
1638c2ecf20Sopenharmony_ci	if (ret)
1648c2ecf20Sopenharmony_ci		goto err;
1658c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x68e0, 0x39);
1668c2ecf20Sopenharmony_ci	if (ret)
1678c2ecf20Sopenharmony_ci		goto err;
1688c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6840, 0x21);
1698c2ecf20Sopenharmony_ci	if (ret)
1708c2ecf20Sopenharmony_ci		goto err;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return 0;
1738c2ecf20Sopenharmony_cierr:
1748c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "failed=%d\n", ret);
1758c2ecf20Sopenharmony_ci	return ret;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic int zd1301_demod_init(struct dvb_frontend *fe)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct zd1301_demod_dev *dev = fe->demodulator_priv;
1818c2ecf20Sopenharmony_ci	struct platform_device *pdev = dev->pdev;
1828c2ecf20Sopenharmony_ci	int ret;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "\n");
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6840, 0x26);
1878c2ecf20Sopenharmony_ci	if (ret)
1888c2ecf20Sopenharmony_ci		goto err;
1898c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x68e0, 0xff);
1908c2ecf20Sopenharmony_ci	if (ret)
1918c2ecf20Sopenharmony_ci		goto err;
1928c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x68e2, 0xd8);
1938c2ecf20Sopenharmony_ci	if (ret)
1948c2ecf20Sopenharmony_ci		goto err;
1958c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6849, 0x4e);
1968c2ecf20Sopenharmony_ci	if (ret)
1978c2ecf20Sopenharmony_ci		goto err;
1988c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x684e, 0x01);
1998c2ecf20Sopenharmony_ci	if (ret)
2008c2ecf20Sopenharmony_ci		goto err;
2018c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a43, zd1301_demod_gain);
2028c2ecf20Sopenharmony_ci	if (ret)
2038c2ecf20Sopenharmony_ci		goto err;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return 0;
2068c2ecf20Sopenharmony_cierr:
2078c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "failed=%d\n", ret);
2088c2ecf20Sopenharmony_ci	return ret;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic int zd1301_demod_get_tune_settings(struct dvb_frontend *fe,
2128c2ecf20Sopenharmony_ci					  struct dvb_frontend_tune_settings *settings)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	struct zd1301_demod_dev *dev = fe->demodulator_priv;
2158c2ecf20Sopenharmony_ci	struct platform_device *pdev = dev->pdev;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "\n");
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/* ~180ms seems to be enough */
2208c2ecf20Sopenharmony_ci	settings->min_delay_ms = 400;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return 0;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int zd1301_demod_read_status(struct dvb_frontend *fe,
2268c2ecf20Sopenharmony_ci				    enum fe_status *status)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct zd1301_demod_dev *dev = fe->demodulator_priv;
2298c2ecf20Sopenharmony_ci	struct platform_device *pdev = dev->pdev;
2308c2ecf20Sopenharmony_ci	int ret;
2318c2ecf20Sopenharmony_ci	u8 u8tmp;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	ret = zd1301_demod_rreg(dev, 0x6a24, &u8tmp);
2348c2ecf20Sopenharmony_ci	if (ret)
2358c2ecf20Sopenharmony_ci		goto err;
2368c2ecf20Sopenharmony_ci	if (u8tmp > 0x00 && u8tmp < 0x20)
2378c2ecf20Sopenharmony_ci		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
2388c2ecf20Sopenharmony_ci			  FE_HAS_SYNC | FE_HAS_LOCK;
2398c2ecf20Sopenharmony_ci	else
2408c2ecf20Sopenharmony_ci		*status = 0;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "lock byte=%02x\n", u8tmp);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/*
2458c2ecf20Sopenharmony_ci	 * Interesting registers here are:
2468c2ecf20Sopenharmony_ci	 * 0x6a05: get some gain value
2478c2ecf20Sopenharmony_ci	 * 0x6a06: get about same gain value than set to 0x6a43
2488c2ecf20Sopenharmony_ci	 * 0x6a07: get some gain value
2498c2ecf20Sopenharmony_ci	 * 0x6a43: set gain value by driver
2508c2ecf20Sopenharmony_ci	 * 0x6a24: get demod lock bits (FSM stage?)
2518c2ecf20Sopenharmony_ci	 *
2528c2ecf20Sopenharmony_ci	 * Driver should implement some kind of algorithm to calculate suitable
2538c2ecf20Sopenharmony_ci	 * value for register 0x6a43, based likely values from register 0x6a05
2548c2ecf20Sopenharmony_ci	 * and 0x6a07. Looks like gain register 0x6a43 value could be from
2558c2ecf20Sopenharmony_ci	 * range 0x00 - 0x70.
2568c2ecf20Sopenharmony_ci	 */
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (dev->gain != zd1301_demod_gain) {
2598c2ecf20Sopenharmony_ci		dev->gain = zd1301_demod_gain;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6a43, dev->gain);
2628c2ecf20Sopenharmony_ci		if (ret)
2638c2ecf20Sopenharmony_ci			goto err;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	return 0;
2678c2ecf20Sopenharmony_cierr:
2688c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "failed=%d\n", ret);
2698c2ecf20Sopenharmony_ci	return ret;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops zd1301_demod_ops = {
2738c2ecf20Sopenharmony_ci	.delsys = {SYS_DVBT},
2748c2ecf20Sopenharmony_ci	.info = {
2758c2ecf20Sopenharmony_ci		.name = "ZyDAS ZD1301",
2768c2ecf20Sopenharmony_ci		.caps = FE_CAN_FEC_1_2 |
2778c2ecf20Sopenharmony_ci			FE_CAN_FEC_2_3 |
2788c2ecf20Sopenharmony_ci			FE_CAN_FEC_3_4 |
2798c2ecf20Sopenharmony_ci			FE_CAN_FEC_5_6 |
2808c2ecf20Sopenharmony_ci			FE_CAN_FEC_7_8 |
2818c2ecf20Sopenharmony_ci			FE_CAN_FEC_AUTO |
2828c2ecf20Sopenharmony_ci			FE_CAN_QPSK |
2838c2ecf20Sopenharmony_ci			FE_CAN_QAM_16 |
2848c2ecf20Sopenharmony_ci			FE_CAN_QAM_64 |
2858c2ecf20Sopenharmony_ci			FE_CAN_QAM_AUTO |
2868c2ecf20Sopenharmony_ci			FE_CAN_TRANSMISSION_MODE_AUTO |
2878c2ecf20Sopenharmony_ci			FE_CAN_GUARD_INTERVAL_AUTO |
2888c2ecf20Sopenharmony_ci			FE_CAN_HIERARCHY_AUTO |
2898c2ecf20Sopenharmony_ci			FE_CAN_MUTE_TS
2908c2ecf20Sopenharmony_ci	},
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	.sleep = zd1301_demod_sleep,
2938c2ecf20Sopenharmony_ci	.init = zd1301_demod_init,
2948c2ecf20Sopenharmony_ci	.set_frontend = zd1301_demod_set_frontend,
2958c2ecf20Sopenharmony_ci	.get_tune_settings = zd1301_demod_get_tune_settings,
2968c2ecf20Sopenharmony_ci	.read_status = zd1301_demod_read_status,
2978c2ecf20Sopenharmony_ci};
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistruct dvb_frontend *zd1301_demod_get_dvb_frontend(struct platform_device *pdev)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct zd1301_demod_dev *dev = platform_get_drvdata(pdev);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "\n");
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	return &dev->frontend;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(zd1301_demod_get_dvb_frontend);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic int zd1301_demod_i2c_master_xfer(struct i2c_adapter *adapter,
3108c2ecf20Sopenharmony_ci					struct i2c_msg msg[], int num)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	struct zd1301_demod_dev *dev = i2c_get_adapdata(adapter);
3138c2ecf20Sopenharmony_ci	struct platform_device *pdev = dev->pdev;
3148c2ecf20Sopenharmony_ci	int ret, i;
3158c2ecf20Sopenharmony_ci	unsigned long timeout;
3168c2ecf20Sopenharmony_ci	u8 u8tmp;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	#define I2C_XFER_TIMEOUT 5
3198c2ecf20Sopenharmony_ci	#define ZD1301_IS_I2C_XFER_WRITE_READ(_msg, _num) \
3208c2ecf20Sopenharmony_ci		(_num == 2 && !(_msg[0].flags & I2C_M_RD) && (_msg[1].flags & I2C_M_RD))
3218c2ecf20Sopenharmony_ci	#define ZD1301_IS_I2C_XFER_WRITE(_msg, _num) \
3228c2ecf20Sopenharmony_ci		(_num == 1 && !(_msg[0].flags & I2C_M_RD))
3238c2ecf20Sopenharmony_ci	#define ZD1301_IS_I2C_XFER_READ(_msg, _num) \
3248c2ecf20Sopenharmony_ci		(_num == 1 && (_msg[0].flags & I2C_M_RD))
3258c2ecf20Sopenharmony_ci	if (ZD1301_IS_I2C_XFER_WRITE_READ(msg, num)) {
3268c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "write&read msg[0].len=%u msg[1].len=%u\n",
3278c2ecf20Sopenharmony_ci			msg[0].len, msg[1].len);
3288c2ecf20Sopenharmony_ci		if (msg[0].len > 1 || msg[1].len > 8) {
3298c2ecf20Sopenharmony_ci			ret = -EOPNOTSUPP;
3308c2ecf20Sopenharmony_ci			goto err;
3318c2ecf20Sopenharmony_ci		}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6811, 0x80);
3348c2ecf20Sopenharmony_ci		if (ret)
3358c2ecf20Sopenharmony_ci			goto err;
3368c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6812, 0x05);
3378c2ecf20Sopenharmony_ci		if (ret)
3388c2ecf20Sopenharmony_ci			goto err;
3398c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6813, msg[1].addr << 1);
3408c2ecf20Sopenharmony_ci		if (ret)
3418c2ecf20Sopenharmony_ci			goto err;
3428c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6801, msg[0].buf[0]);
3438c2ecf20Sopenharmony_ci		if (ret)
3448c2ecf20Sopenharmony_ci			goto err;
3458c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6802, 0x00);
3468c2ecf20Sopenharmony_ci		if (ret)
3478c2ecf20Sopenharmony_ci			goto err;
3488c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6803, 0x06);
3498c2ecf20Sopenharmony_ci		if (ret)
3508c2ecf20Sopenharmony_ci			goto err;
3518c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6805, 0x00);
3528c2ecf20Sopenharmony_ci		if (ret)
3538c2ecf20Sopenharmony_ci			goto err;
3548c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6804, msg[1].len);
3558c2ecf20Sopenharmony_ci		if (ret)
3568c2ecf20Sopenharmony_ci			goto err;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		/* Poll xfer ready */
3598c2ecf20Sopenharmony_ci		timeout = jiffies + msecs_to_jiffies(I2C_XFER_TIMEOUT);
3608c2ecf20Sopenharmony_ci		for (u8tmp = 1; !time_after(jiffies, timeout) && u8tmp;) {
3618c2ecf20Sopenharmony_ci			usleep_range(500, 800);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci			ret = zd1301_demod_rreg(dev, 0x6804, &u8tmp);
3648c2ecf20Sopenharmony_ci			if (ret)
3658c2ecf20Sopenharmony_ci				goto err;
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci		for (i = 0; i < msg[1].len; i++) {
3698c2ecf20Sopenharmony_ci			ret = zd1301_demod_rreg(dev, 0x0600 + i, &msg[1].buf[i]);
3708c2ecf20Sopenharmony_ci			if (ret)
3718c2ecf20Sopenharmony_ci				goto err;
3728c2ecf20Sopenharmony_ci		}
3738c2ecf20Sopenharmony_ci	} else if (ZD1301_IS_I2C_XFER_WRITE(msg, num)) {
3748c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "write msg[0].len=%u\n", msg[0].len);
3758c2ecf20Sopenharmony_ci		if (msg[0].len > 1 + 8) {
3768c2ecf20Sopenharmony_ci			ret = -EOPNOTSUPP;
3778c2ecf20Sopenharmony_ci			goto err;
3788c2ecf20Sopenharmony_ci		}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6811, 0x80);
3818c2ecf20Sopenharmony_ci		if (ret)
3828c2ecf20Sopenharmony_ci			goto err;
3838c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6812, 0x01);
3848c2ecf20Sopenharmony_ci		if (ret)
3858c2ecf20Sopenharmony_ci			goto err;
3868c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6813, msg[0].addr << 1);
3878c2ecf20Sopenharmony_ci		if (ret)
3888c2ecf20Sopenharmony_ci			goto err;
3898c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6800, msg[0].buf[0]);
3908c2ecf20Sopenharmony_ci		if (ret)
3918c2ecf20Sopenharmony_ci			goto err;
3928c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6802, 0x00);
3938c2ecf20Sopenharmony_ci		if (ret)
3948c2ecf20Sopenharmony_ci			goto err;
3958c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6803, 0x06);
3968c2ecf20Sopenharmony_ci		if (ret)
3978c2ecf20Sopenharmony_ci			goto err;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		for (i = 0; i < msg[0].len - 1; i++) {
4008c2ecf20Sopenharmony_ci			ret = zd1301_demod_wreg(dev, 0x0600 + i, msg[0].buf[1 + i]);
4018c2ecf20Sopenharmony_ci			if (ret)
4028c2ecf20Sopenharmony_ci				goto err;
4038c2ecf20Sopenharmony_ci		}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6805, 0x80);
4068c2ecf20Sopenharmony_ci		if (ret)
4078c2ecf20Sopenharmony_ci			goto err;
4088c2ecf20Sopenharmony_ci		ret = zd1301_demod_wreg(dev, 0x6804, msg[0].len - 1);
4098c2ecf20Sopenharmony_ci		if (ret)
4108c2ecf20Sopenharmony_ci			goto err;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci		/* Poll xfer ready */
4138c2ecf20Sopenharmony_ci		timeout = jiffies + msecs_to_jiffies(I2C_XFER_TIMEOUT);
4148c2ecf20Sopenharmony_ci		for (u8tmp = 1; !time_after(jiffies, timeout) && u8tmp;) {
4158c2ecf20Sopenharmony_ci			usleep_range(500, 800);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci			ret = zd1301_demod_rreg(dev, 0x6804, &u8tmp);
4188c2ecf20Sopenharmony_ci			if (ret)
4198c2ecf20Sopenharmony_ci				goto err;
4208c2ecf20Sopenharmony_ci		}
4218c2ecf20Sopenharmony_ci	} else {
4228c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "unknown msg[0].len=%u\n", msg[0].len);
4238c2ecf20Sopenharmony_ci		ret = -EOPNOTSUPP;
4248c2ecf20Sopenharmony_ci		goto err;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	return num;
4288c2ecf20Sopenharmony_cierr:
4298c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "failed=%d\n", ret);
4308c2ecf20Sopenharmony_ci	return ret;
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic u32 zd1301_demod_i2c_functionality(struct i2c_adapter *adapter)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	return I2C_FUNC_I2C;
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic const struct i2c_algorithm zd1301_demod_i2c_algorithm = {
4398c2ecf20Sopenharmony_ci	.master_xfer   = zd1301_demod_i2c_master_xfer,
4408c2ecf20Sopenharmony_ci	.functionality = zd1301_demod_i2c_functionality,
4418c2ecf20Sopenharmony_ci};
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistruct i2c_adapter *zd1301_demod_get_i2c_adapter(struct platform_device *pdev)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	struct zd1301_demod_dev *dev = platform_get_drvdata(pdev);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "\n");
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	return &dev->adapter;
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(zd1301_demod_get_i2c_adapter);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci/* Platform driver interface */
4548c2ecf20Sopenharmony_cistatic int zd1301_demod_probe(struct platform_device *pdev)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct zd1301_demod_dev *dev;
4578c2ecf20Sopenharmony_ci	struct zd1301_demod_platform_data *pdata = pdev->dev.platform_data;
4588c2ecf20Sopenharmony_ci	int ret;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "\n");
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	if (!pdata) {
4638c2ecf20Sopenharmony_ci		ret = -EINVAL;
4648c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot proceed without platform data\n");
4658c2ecf20Sopenharmony_ci		goto err;
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci	if (!pdev->dev.parent->driver) {
4688c2ecf20Sopenharmony_ci		ret = -EINVAL;
4698c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "no parent device\n");
4708c2ecf20Sopenharmony_ci		goto err;
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
4748c2ecf20Sopenharmony_ci	if (!dev) {
4758c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4768c2ecf20Sopenharmony_ci		goto err;
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	/* Setup the state */
4808c2ecf20Sopenharmony_ci	dev->pdev = pdev;
4818c2ecf20Sopenharmony_ci	dev->gain = zd1301_demod_gain;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	/* Sleep */
4848c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6840, 0x21);
4858c2ecf20Sopenharmony_ci	if (ret)
4868c2ecf20Sopenharmony_ci		goto err_kfree;
4878c2ecf20Sopenharmony_ci	ret = zd1301_demod_wreg(dev, 0x6a38, 0x07);
4888c2ecf20Sopenharmony_ci	if (ret)
4898c2ecf20Sopenharmony_ci		goto err_kfree;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* Create I2C adapter */
4928c2ecf20Sopenharmony_ci	strscpy(dev->adapter.name, "ZyDAS ZD1301 demod",
4938c2ecf20Sopenharmony_ci		sizeof(dev->adapter.name));
4948c2ecf20Sopenharmony_ci	dev->adapter.algo = &zd1301_demod_i2c_algorithm;
4958c2ecf20Sopenharmony_ci	dev->adapter.algo_data = NULL;
4968c2ecf20Sopenharmony_ci	dev->adapter.dev.parent = pdev->dev.parent;
4978c2ecf20Sopenharmony_ci	i2c_set_adapdata(&dev->adapter, dev);
4988c2ecf20Sopenharmony_ci	ret = i2c_add_adapter(&dev->adapter);
4998c2ecf20Sopenharmony_ci	if (ret) {
5008c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "I2C adapter add failed %d\n", ret);
5018c2ecf20Sopenharmony_ci		goto err_kfree;
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	/* Create dvb frontend */
5058c2ecf20Sopenharmony_ci	memcpy(&dev->frontend.ops, &zd1301_demod_ops, sizeof(dev->frontend.ops));
5068c2ecf20Sopenharmony_ci	dev->frontend.demodulator_priv = dev;
5078c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, dev);
5088c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "ZyDAS ZD1301 demod attached\n");
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	return 0;
5118c2ecf20Sopenharmony_cierr_kfree:
5128c2ecf20Sopenharmony_ci	kfree(dev);
5138c2ecf20Sopenharmony_cierr:
5148c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "failed=%d\n", ret);
5158c2ecf20Sopenharmony_ci	return ret;
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_cistatic int zd1301_demod_remove(struct platform_device *pdev)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	struct zd1301_demod_dev *dev = platform_get_drvdata(pdev);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "\n");
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	i2c_del_adapter(&dev->adapter);
5258c2ecf20Sopenharmony_ci	kfree(dev);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	return 0;
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistatic struct platform_driver zd1301_demod_driver = {
5318c2ecf20Sopenharmony_ci	.driver = {
5328c2ecf20Sopenharmony_ci		.name                = "zd1301_demod",
5338c2ecf20Sopenharmony_ci		.suppress_bind_attrs = true,
5348c2ecf20Sopenharmony_ci	},
5358c2ecf20Sopenharmony_ci	.probe          = zd1301_demod_probe,
5368c2ecf20Sopenharmony_ci	.remove         = zd1301_demod_remove,
5378c2ecf20Sopenharmony_ci};
5388c2ecf20Sopenharmony_cimodule_platform_driver(zd1301_demod_driver);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ciMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
5418c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ZyDAS ZD1301 demodulator driver");
5428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
543