18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Mirics MSi001 silicon tuner driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
68c2ecf20Sopenharmony_ci * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/gcd.h>
118c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
128c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic const struct v4l2_frequency_band bands[] = {
158c2ecf20Sopenharmony_ci	{
168c2ecf20Sopenharmony_ci		.type = V4L2_TUNER_RF,
178c2ecf20Sopenharmony_ci		.index = 0,
188c2ecf20Sopenharmony_ci		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
198c2ecf20Sopenharmony_ci		.rangelow   =   49000000,
208c2ecf20Sopenharmony_ci		.rangehigh  =  263000000,
218c2ecf20Sopenharmony_ci	}, {
228c2ecf20Sopenharmony_ci		.type = V4L2_TUNER_RF,
238c2ecf20Sopenharmony_ci		.index = 1,
248c2ecf20Sopenharmony_ci		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
258c2ecf20Sopenharmony_ci		.rangelow   =  390000000,
268c2ecf20Sopenharmony_ci		.rangehigh  =  960000000,
278c2ecf20Sopenharmony_ci	},
288c2ecf20Sopenharmony_ci};
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistruct msi001_dev {
318c2ecf20Sopenharmony_ci	struct spi_device *spi;
328c2ecf20Sopenharmony_ci	struct v4l2_subdev sd;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	/* Controls */
358c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler hdl;
368c2ecf20Sopenharmony_ci	struct v4l2_ctrl *bandwidth_auto;
378c2ecf20Sopenharmony_ci	struct v4l2_ctrl *bandwidth;
388c2ecf20Sopenharmony_ci	struct v4l2_ctrl *lna_gain;
398c2ecf20Sopenharmony_ci	struct v4l2_ctrl *mixer_gain;
408c2ecf20Sopenharmony_ci	struct v4l2_ctrl *if_gain;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	unsigned int f_tuner;
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic inline struct msi001_dev *sd_to_msi001_dev(struct v4l2_subdev *sd)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	return container_of(sd, struct msi001_dev, sd);
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int msi001_wreg(struct msi001_dev *dev, u32 data)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	/* Register format: 4 bits addr + 20 bits value */
538c2ecf20Sopenharmony_ci	return spi_write(dev->spi, &data, 3);
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int msi001_set_gain(struct msi001_dev *dev, int lna_gain, int mixer_gain,
578c2ecf20Sopenharmony_ci			   int if_gain)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct spi_device *spi = dev->spi;
608c2ecf20Sopenharmony_ci	int ret;
618c2ecf20Sopenharmony_ci	u32 reg;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	dev_dbg(&spi->dev, "lna=%d mixer=%d if=%d\n",
648c2ecf20Sopenharmony_ci		lna_gain, mixer_gain, if_gain);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	reg = 1 << 0;
678c2ecf20Sopenharmony_ci	reg |= (59 - if_gain) << 4;
688c2ecf20Sopenharmony_ci	reg |= 0 << 10;
698c2ecf20Sopenharmony_ci	reg |= (1 - mixer_gain) << 12;
708c2ecf20Sopenharmony_ci	reg |= (1 - lna_gain) << 13;
718c2ecf20Sopenharmony_ci	reg |= 4 << 14;
728c2ecf20Sopenharmony_ci	reg |= 0 << 17;
738c2ecf20Sopenharmony_ci	ret = msi001_wreg(dev, reg);
748c2ecf20Sopenharmony_ci	if (ret)
758c2ecf20Sopenharmony_ci		goto err;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	return 0;
788c2ecf20Sopenharmony_cierr:
798c2ecf20Sopenharmony_ci	dev_dbg(&spi->dev, "failed %d\n", ret);
808c2ecf20Sopenharmony_ci	return ret;
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic int msi001_set_tuner(struct msi001_dev *dev)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct spi_device *spi = dev->spi;
868c2ecf20Sopenharmony_ci	int ret, i;
878c2ecf20Sopenharmony_ci	unsigned int uitmp, div_n, k, k_thresh, k_frac, div_lo, f_if1;
888c2ecf20Sopenharmony_ci	u32 reg;
898c2ecf20Sopenharmony_ci	u64 f_vco;
908c2ecf20Sopenharmony_ci	u8 mode, filter_mode;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	static const struct {
938c2ecf20Sopenharmony_ci		u32 rf;
948c2ecf20Sopenharmony_ci		u8 mode;
958c2ecf20Sopenharmony_ci		u8 div_lo;
968c2ecf20Sopenharmony_ci	} band_lut[] = {
978c2ecf20Sopenharmony_ci		{ 50000000, 0xe1, 16}, /* AM_MODE2, antenna 2 */
988c2ecf20Sopenharmony_ci		{108000000, 0x42, 32}, /* VHF_MODE */
998c2ecf20Sopenharmony_ci		{330000000, 0x44, 16}, /* B3_MODE */
1008c2ecf20Sopenharmony_ci		{960000000, 0x48,  4}, /* B45_MODE */
1018c2ecf20Sopenharmony_ci		{      ~0U, 0x50,  2}, /* BL_MODE */
1028c2ecf20Sopenharmony_ci	};
1038c2ecf20Sopenharmony_ci	static const struct {
1048c2ecf20Sopenharmony_ci		u32 freq;
1058c2ecf20Sopenharmony_ci		u8 filter_mode;
1068c2ecf20Sopenharmony_ci	} if_freq_lut[] = {
1078c2ecf20Sopenharmony_ci		{      0, 0x03}, /* Zero IF */
1088c2ecf20Sopenharmony_ci		{ 450000, 0x02}, /* 450 kHz IF */
1098c2ecf20Sopenharmony_ci		{1620000, 0x01}, /* 1.62 MHz IF */
1108c2ecf20Sopenharmony_ci		{2048000, 0x00}, /* 2.048 MHz IF */
1118c2ecf20Sopenharmony_ci	};
1128c2ecf20Sopenharmony_ci	static const struct {
1138c2ecf20Sopenharmony_ci		u32 freq;
1148c2ecf20Sopenharmony_ci		u8 val;
1158c2ecf20Sopenharmony_ci	} bandwidth_lut[] = {
1168c2ecf20Sopenharmony_ci		{ 200000, 0x00}, /* 200 kHz */
1178c2ecf20Sopenharmony_ci		{ 300000, 0x01}, /* 300 kHz */
1188c2ecf20Sopenharmony_ci		{ 600000, 0x02}, /* 600 kHz */
1198c2ecf20Sopenharmony_ci		{1536000, 0x03}, /* 1.536 MHz */
1208c2ecf20Sopenharmony_ci		{5000000, 0x04}, /* 5 MHz */
1218c2ecf20Sopenharmony_ci		{6000000, 0x05}, /* 6 MHz */
1228c2ecf20Sopenharmony_ci		{7000000, 0x06}, /* 7 MHz */
1238c2ecf20Sopenharmony_ci		{8000000, 0x07}, /* 8 MHz */
1248c2ecf20Sopenharmony_ci	};
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	unsigned int f_rf = dev->f_tuner;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/*
1298c2ecf20Sopenharmony_ci	 * bandwidth (Hz)
1308c2ecf20Sopenharmony_ci	 * 200000, 300000, 600000, 1536000, 5000000, 6000000, 7000000, 8000000
1318c2ecf20Sopenharmony_ci	 */
1328c2ecf20Sopenharmony_ci	unsigned int bandwidth;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/*
1358c2ecf20Sopenharmony_ci	 * intermediate frequency (Hz)
1368c2ecf20Sopenharmony_ci	 * 0, 450000, 1620000, 2048000
1378c2ecf20Sopenharmony_ci	 */
1388c2ecf20Sopenharmony_ci	unsigned int f_if = 0;
1398c2ecf20Sopenharmony_ci	#define F_REF 24000000
1408c2ecf20Sopenharmony_ci	#define DIV_PRE_N 4
1418c2ecf20Sopenharmony_ci	#define	F_VCO_STEP div_lo
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	dev_dbg(&spi->dev, "f_rf=%d f_if=%d\n", f_rf, f_if);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(band_lut); i++) {
1468c2ecf20Sopenharmony_ci		if (f_rf <= band_lut[i].rf) {
1478c2ecf20Sopenharmony_ci			mode = band_lut[i].mode;
1488c2ecf20Sopenharmony_ci			div_lo = band_lut[i].div_lo;
1498c2ecf20Sopenharmony_ci			break;
1508c2ecf20Sopenharmony_ci		}
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci	if (i == ARRAY_SIZE(band_lut)) {
1538c2ecf20Sopenharmony_ci		ret = -EINVAL;
1548c2ecf20Sopenharmony_ci		goto err;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/* AM_MODE is upconverted */
1588c2ecf20Sopenharmony_ci	if ((mode >> 0) & 0x1)
1598c2ecf20Sopenharmony_ci		f_if1 =  5 * F_REF;
1608c2ecf20Sopenharmony_ci	else
1618c2ecf20Sopenharmony_ci		f_if1 =  0;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(if_freq_lut); i++) {
1648c2ecf20Sopenharmony_ci		if (f_if == if_freq_lut[i].freq) {
1658c2ecf20Sopenharmony_ci			filter_mode = if_freq_lut[i].filter_mode;
1668c2ecf20Sopenharmony_ci			break;
1678c2ecf20Sopenharmony_ci		}
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci	if (i == ARRAY_SIZE(if_freq_lut)) {
1708c2ecf20Sopenharmony_ci		ret = -EINVAL;
1718c2ecf20Sopenharmony_ci		goto err;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/* filters */
1758c2ecf20Sopenharmony_ci	bandwidth = dev->bandwidth->val;
1768c2ecf20Sopenharmony_ci	bandwidth = clamp(bandwidth, 200000U, 8000000U);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(bandwidth_lut); i++) {
1798c2ecf20Sopenharmony_ci		if (bandwidth <= bandwidth_lut[i].freq) {
1808c2ecf20Sopenharmony_ci			bandwidth = bandwidth_lut[i].val;
1818c2ecf20Sopenharmony_ci			break;
1828c2ecf20Sopenharmony_ci		}
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci	if (i == ARRAY_SIZE(bandwidth_lut)) {
1858c2ecf20Sopenharmony_ci		ret = -EINVAL;
1868c2ecf20Sopenharmony_ci		goto err;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	dev->bandwidth->val = bandwidth_lut[i].freq;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	dev_dbg(&spi->dev, "bandwidth selected=%d\n", bandwidth_lut[i].freq);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/*
1948c2ecf20Sopenharmony_ci	 * Fractional-N synthesizer
1958c2ecf20Sopenharmony_ci	 *
1968c2ecf20Sopenharmony_ci	 *           +---------------------------------------+
1978c2ecf20Sopenharmony_ci	 *           v                                       |
1988c2ecf20Sopenharmony_ci	 *  Fref   +----+     +-------+         +----+     +------+     +---+
1998c2ecf20Sopenharmony_ci	 * ------> | PD | --> |  VCO  | ------> | /4 | --> | /N.F | <-- | K |
2008c2ecf20Sopenharmony_ci	 *         +----+     +-------+         +----+     +------+     +---+
2018c2ecf20Sopenharmony_ci	 *                      |
2028c2ecf20Sopenharmony_ci	 *                      |
2038c2ecf20Sopenharmony_ci	 *                      v
2048c2ecf20Sopenharmony_ci	 *                    +-------+  Fout
2058c2ecf20Sopenharmony_ci	 *                    | /Rout | ------>
2068c2ecf20Sopenharmony_ci	 *                    +-------+
2078c2ecf20Sopenharmony_ci	 */
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* Calculate PLL integer and fractional control word. */
2108c2ecf20Sopenharmony_ci	f_vco = (u64) (f_rf + f_if + f_if1) * div_lo;
2118c2ecf20Sopenharmony_ci	div_n = div_u64_rem(f_vco, DIV_PRE_N * F_REF, &k);
2128c2ecf20Sopenharmony_ci	k_thresh = (DIV_PRE_N * F_REF) / F_VCO_STEP;
2138c2ecf20Sopenharmony_ci	k_frac = div_u64((u64) k * k_thresh, (DIV_PRE_N * F_REF));
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	/* Find out greatest common divisor and divide to smaller. */
2168c2ecf20Sopenharmony_ci	uitmp = gcd(k_thresh, k_frac);
2178c2ecf20Sopenharmony_ci	k_thresh /= uitmp;
2188c2ecf20Sopenharmony_ci	k_frac /= uitmp;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/* Force divide to reg max. Resolution will be reduced. */
2218c2ecf20Sopenharmony_ci	uitmp = DIV_ROUND_UP(k_thresh, 4095);
2228c2ecf20Sopenharmony_ci	k_thresh = DIV_ROUND_CLOSEST(k_thresh, uitmp);
2238c2ecf20Sopenharmony_ci	k_frac = DIV_ROUND_CLOSEST(k_frac, uitmp);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* Calculate real RF set. */
2268c2ecf20Sopenharmony_ci	uitmp = (unsigned int) F_REF * DIV_PRE_N * div_n;
2278c2ecf20Sopenharmony_ci	uitmp += (unsigned int) F_REF * DIV_PRE_N * k_frac / k_thresh;
2288c2ecf20Sopenharmony_ci	uitmp /= div_lo;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	dev_dbg(&spi->dev,
2318c2ecf20Sopenharmony_ci		"f_rf=%u:%u f_vco=%llu div_n=%u k_thresh=%u k_frac=%u div_lo=%u\n",
2328c2ecf20Sopenharmony_ci		f_rf, uitmp, f_vco, div_n, k_thresh, k_frac, div_lo);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	ret = msi001_wreg(dev, 0x00000e);
2358c2ecf20Sopenharmony_ci	if (ret)
2368c2ecf20Sopenharmony_ci		goto err;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	ret = msi001_wreg(dev, 0x000003);
2398c2ecf20Sopenharmony_ci	if (ret)
2408c2ecf20Sopenharmony_ci		goto err;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	reg = 0 << 0;
2438c2ecf20Sopenharmony_ci	reg |= mode << 4;
2448c2ecf20Sopenharmony_ci	reg |= filter_mode << 12;
2458c2ecf20Sopenharmony_ci	reg |= bandwidth << 14;
2468c2ecf20Sopenharmony_ci	reg |= 0x02 << 17;
2478c2ecf20Sopenharmony_ci	reg |= 0x00 << 20;
2488c2ecf20Sopenharmony_ci	ret = msi001_wreg(dev, reg);
2498c2ecf20Sopenharmony_ci	if (ret)
2508c2ecf20Sopenharmony_ci		goto err;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	reg = 5 << 0;
2538c2ecf20Sopenharmony_ci	reg |= k_thresh << 4;
2548c2ecf20Sopenharmony_ci	reg |= 1 << 19;
2558c2ecf20Sopenharmony_ci	reg |= 1 << 21;
2568c2ecf20Sopenharmony_ci	ret = msi001_wreg(dev, reg);
2578c2ecf20Sopenharmony_ci	if (ret)
2588c2ecf20Sopenharmony_ci		goto err;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	reg = 2 << 0;
2618c2ecf20Sopenharmony_ci	reg |= k_frac << 4;
2628c2ecf20Sopenharmony_ci	reg |= div_n << 16;
2638c2ecf20Sopenharmony_ci	ret = msi001_wreg(dev, reg);
2648c2ecf20Sopenharmony_ci	if (ret)
2658c2ecf20Sopenharmony_ci		goto err;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	ret = msi001_set_gain(dev, dev->lna_gain->cur.val,
2688c2ecf20Sopenharmony_ci			      dev->mixer_gain->cur.val, dev->if_gain->cur.val);
2698c2ecf20Sopenharmony_ci	if (ret)
2708c2ecf20Sopenharmony_ci		goto err;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	reg = 6 << 0;
2738c2ecf20Sopenharmony_ci	reg |= 63 << 4;
2748c2ecf20Sopenharmony_ci	reg |= 4095 << 10;
2758c2ecf20Sopenharmony_ci	ret = msi001_wreg(dev, reg);
2768c2ecf20Sopenharmony_ci	if (ret)
2778c2ecf20Sopenharmony_ci		goto err;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return 0;
2808c2ecf20Sopenharmony_cierr:
2818c2ecf20Sopenharmony_ci	dev_dbg(&spi->dev, "failed %d\n", ret);
2828c2ecf20Sopenharmony_ci	return ret;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic int msi001_standby(struct v4l2_subdev *sd)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	struct msi001_dev *dev = sd_to_msi001_dev(sd);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	return msi001_wreg(dev, 0x000000);
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic int msi001_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	struct msi001_dev *dev = sd_to_msi001_dev(sd);
2958c2ecf20Sopenharmony_ci	struct spi_device *spi = dev->spi;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	dev_dbg(&spi->dev, "index=%d\n", v->index);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	strscpy(v->name, "Mirics MSi001", sizeof(v->name));
3008c2ecf20Sopenharmony_ci	v->type = V4L2_TUNER_RF;
3018c2ecf20Sopenharmony_ci	v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
3028c2ecf20Sopenharmony_ci	v->rangelow =    49000000;
3038c2ecf20Sopenharmony_ci	v->rangehigh =  960000000;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	return 0;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic int msi001_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *v)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	struct msi001_dev *dev = sd_to_msi001_dev(sd);
3118c2ecf20Sopenharmony_ci	struct spi_device *spi = dev->spi;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	dev_dbg(&spi->dev, "index=%d\n", v->index);
3148c2ecf20Sopenharmony_ci	return 0;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic int msi001_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	struct msi001_dev *dev = sd_to_msi001_dev(sd);
3208c2ecf20Sopenharmony_ci	struct spi_device *spi = dev->spi;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	dev_dbg(&spi->dev, "tuner=%d\n", f->tuner);
3238c2ecf20Sopenharmony_ci	f->frequency = dev->f_tuner;
3248c2ecf20Sopenharmony_ci	return 0;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic int msi001_s_frequency(struct v4l2_subdev *sd,
3288c2ecf20Sopenharmony_ci			      const struct v4l2_frequency *f)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct msi001_dev *dev = sd_to_msi001_dev(sd);
3318c2ecf20Sopenharmony_ci	struct spi_device *spi = dev->spi;
3328c2ecf20Sopenharmony_ci	unsigned int band;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	dev_dbg(&spi->dev, "tuner=%d type=%d frequency=%u\n",
3358c2ecf20Sopenharmony_ci		f->tuner, f->type, f->frequency);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (f->frequency < ((bands[0].rangehigh + bands[1].rangelow) / 2))
3388c2ecf20Sopenharmony_ci		band = 0;
3398c2ecf20Sopenharmony_ci	else
3408c2ecf20Sopenharmony_ci		band = 1;
3418c2ecf20Sopenharmony_ci	dev->f_tuner = clamp_t(unsigned int, f->frequency,
3428c2ecf20Sopenharmony_ci			       bands[band].rangelow, bands[band].rangehigh);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	return msi001_set_tuner(dev);
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic int msi001_enum_freq_bands(struct v4l2_subdev *sd,
3488c2ecf20Sopenharmony_ci				  struct v4l2_frequency_band *band)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	struct msi001_dev *dev = sd_to_msi001_dev(sd);
3518c2ecf20Sopenharmony_ci	struct spi_device *spi = dev->spi;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	dev_dbg(&spi->dev, "tuner=%d type=%d index=%d\n",
3548c2ecf20Sopenharmony_ci		band->tuner, band->type, band->index);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (band->index >= ARRAY_SIZE(bands))
3578c2ecf20Sopenharmony_ci		return -EINVAL;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	band->capability = bands[band->index].capability;
3608c2ecf20Sopenharmony_ci	band->rangelow = bands[band->index].rangelow;
3618c2ecf20Sopenharmony_ci	band->rangehigh = bands[band->index].rangehigh;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	return 0;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_tuner_ops msi001_tuner_ops = {
3678c2ecf20Sopenharmony_ci	.standby                  = msi001_standby,
3688c2ecf20Sopenharmony_ci	.g_tuner                  = msi001_g_tuner,
3698c2ecf20Sopenharmony_ci	.s_tuner                  = msi001_s_tuner,
3708c2ecf20Sopenharmony_ci	.g_frequency              = msi001_g_frequency,
3718c2ecf20Sopenharmony_ci	.s_frequency              = msi001_s_frequency,
3728c2ecf20Sopenharmony_ci	.enum_freq_bands          = msi001_enum_freq_bands,
3738c2ecf20Sopenharmony_ci};
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops msi001_ops = {
3768c2ecf20Sopenharmony_ci	.tuner                    = &msi001_tuner_ops,
3778c2ecf20Sopenharmony_ci};
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic int msi001_s_ctrl(struct v4l2_ctrl *ctrl)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct msi001_dev *dev = container_of(ctrl->handler, struct msi001_dev, hdl);
3828c2ecf20Sopenharmony_ci	struct spi_device *spi = dev->spi;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	int ret;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	dev_dbg(&spi->dev, "id=%d name=%s val=%d min=%lld max=%lld step=%lld\n",
3878c2ecf20Sopenharmony_ci		ctrl->id, ctrl->name, ctrl->val, ctrl->minimum, ctrl->maximum,
3888c2ecf20Sopenharmony_ci		ctrl->step);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	switch (ctrl->id) {
3918c2ecf20Sopenharmony_ci	case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:
3928c2ecf20Sopenharmony_ci	case V4L2_CID_RF_TUNER_BANDWIDTH:
3938c2ecf20Sopenharmony_ci		ret = msi001_set_tuner(dev);
3948c2ecf20Sopenharmony_ci		break;
3958c2ecf20Sopenharmony_ci	case  V4L2_CID_RF_TUNER_LNA_GAIN:
3968c2ecf20Sopenharmony_ci		ret = msi001_set_gain(dev, dev->lna_gain->val,
3978c2ecf20Sopenharmony_ci				      dev->mixer_gain->cur.val,
3988c2ecf20Sopenharmony_ci				      dev->if_gain->cur.val);
3998c2ecf20Sopenharmony_ci		break;
4008c2ecf20Sopenharmony_ci	case  V4L2_CID_RF_TUNER_MIXER_GAIN:
4018c2ecf20Sopenharmony_ci		ret = msi001_set_gain(dev, dev->lna_gain->cur.val,
4028c2ecf20Sopenharmony_ci				      dev->mixer_gain->val,
4038c2ecf20Sopenharmony_ci				      dev->if_gain->cur.val);
4048c2ecf20Sopenharmony_ci		break;
4058c2ecf20Sopenharmony_ci	case  V4L2_CID_RF_TUNER_IF_GAIN:
4068c2ecf20Sopenharmony_ci		ret = msi001_set_gain(dev, dev->lna_gain->cur.val,
4078c2ecf20Sopenharmony_ci				      dev->mixer_gain->cur.val,
4088c2ecf20Sopenharmony_ci				      dev->if_gain->val);
4098c2ecf20Sopenharmony_ci		break;
4108c2ecf20Sopenharmony_ci	default:
4118c2ecf20Sopenharmony_ci		dev_dbg(&spi->dev, "unknown control %d\n", ctrl->id);
4128c2ecf20Sopenharmony_ci		ret = -EINVAL;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	return ret;
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops msi001_ctrl_ops = {
4198c2ecf20Sopenharmony_ci	.s_ctrl                   = msi001_s_ctrl,
4208c2ecf20Sopenharmony_ci};
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic int msi001_probe(struct spi_device *spi)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	struct msi001_dev *dev;
4258c2ecf20Sopenharmony_ci	int ret;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	dev_dbg(&spi->dev, "\n");
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
4308c2ecf20Sopenharmony_ci	if (!dev) {
4318c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4328c2ecf20Sopenharmony_ci		goto err;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	dev->spi = spi;
4368c2ecf20Sopenharmony_ci	dev->f_tuner = bands[0].rangelow;
4378c2ecf20Sopenharmony_ci	v4l2_spi_subdev_init(&dev->sd, spi, &msi001_ops);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/* Register controls */
4408c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(&dev->hdl, 5);
4418c2ecf20Sopenharmony_ci	dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, &msi001_ctrl_ops,
4428c2ecf20Sopenharmony_ci			V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1);
4438c2ecf20Sopenharmony_ci	dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, &msi001_ctrl_ops,
4448c2ecf20Sopenharmony_ci			V4L2_CID_RF_TUNER_BANDWIDTH, 200000, 8000000, 1, 200000);
4458c2ecf20Sopenharmony_ci	if (dev->hdl.error) {
4468c2ecf20Sopenharmony_ci		ret = dev->hdl.error;
4478c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "Could not initialize controls\n");
4488c2ecf20Sopenharmony_ci		/* control init failed, free handler */
4498c2ecf20Sopenharmony_ci		goto err_ctrl_handler_free;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false);
4538c2ecf20Sopenharmony_ci	dev->lna_gain = v4l2_ctrl_new_std(&dev->hdl, &msi001_ctrl_ops,
4548c2ecf20Sopenharmony_ci			V4L2_CID_RF_TUNER_LNA_GAIN, 0, 1, 1, 1);
4558c2ecf20Sopenharmony_ci	dev->mixer_gain = v4l2_ctrl_new_std(&dev->hdl, &msi001_ctrl_ops,
4568c2ecf20Sopenharmony_ci			V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 1, 1, 1);
4578c2ecf20Sopenharmony_ci	dev->if_gain = v4l2_ctrl_new_std(&dev->hdl, &msi001_ctrl_ops,
4588c2ecf20Sopenharmony_ci			V4L2_CID_RF_TUNER_IF_GAIN, 0, 59, 1, 0);
4598c2ecf20Sopenharmony_ci	if (dev->hdl.error) {
4608c2ecf20Sopenharmony_ci		ret = dev->hdl.error;
4618c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "Could not initialize controls\n");
4628c2ecf20Sopenharmony_ci		/* control init failed, free handler */
4638c2ecf20Sopenharmony_ci		goto err_ctrl_handler_free;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	dev->sd.ctrl_handler = &dev->hdl;
4678c2ecf20Sopenharmony_ci	return 0;
4688c2ecf20Sopenharmony_cierr_ctrl_handler_free:
4698c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&dev->hdl);
4708c2ecf20Sopenharmony_ci	kfree(dev);
4718c2ecf20Sopenharmony_cierr:
4728c2ecf20Sopenharmony_ci	return ret;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic int msi001_remove(struct spi_device *spi)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = spi_get_drvdata(spi);
4788c2ecf20Sopenharmony_ci	struct msi001_dev *dev = sd_to_msi001_dev(sd);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	dev_dbg(&spi->dev, "\n");
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	/*
4838c2ecf20Sopenharmony_ci	 * Registered by v4l2_spi_new_subdev() from master driver, but we must
4848c2ecf20Sopenharmony_ci	 * unregister it from here. Weird.
4858c2ecf20Sopenharmony_ci	 */
4868c2ecf20Sopenharmony_ci	v4l2_device_unregister_subdev(&dev->sd);
4878c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&dev->hdl);
4888c2ecf20Sopenharmony_ci	kfree(dev);
4898c2ecf20Sopenharmony_ci	return 0;
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic const struct spi_device_id msi001_id_table[] = {
4938c2ecf20Sopenharmony_ci	{"msi001", 0},
4948c2ecf20Sopenharmony_ci	{}
4958c2ecf20Sopenharmony_ci};
4968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, msi001_id_table);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic struct spi_driver msi001_driver = {
4998c2ecf20Sopenharmony_ci	.driver = {
5008c2ecf20Sopenharmony_ci		.name	= "msi001",
5018c2ecf20Sopenharmony_ci		.suppress_bind_attrs = true,
5028c2ecf20Sopenharmony_ci	},
5038c2ecf20Sopenharmony_ci	.probe		= msi001_probe,
5048c2ecf20Sopenharmony_ci	.remove		= msi001_remove,
5058c2ecf20Sopenharmony_ci	.id_table	= msi001_id_table,
5068c2ecf20Sopenharmony_ci};
5078c2ecf20Sopenharmony_cimodule_spi_driver(msi001_driver);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ciMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
5108c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Mirics MSi001");
5118c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
512