162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Mirics MSi001 silicon tuner driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
662306a36Sopenharmony_ci * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/gcd.h>
1162306a36Sopenharmony_ci#include <media/v4l2-device.h>
1262306a36Sopenharmony_ci#include <media/v4l2-ctrls.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic const struct v4l2_frequency_band bands[] = {
1562306a36Sopenharmony_ci	{
1662306a36Sopenharmony_ci		.type = V4L2_TUNER_RF,
1762306a36Sopenharmony_ci		.index = 0,
1862306a36Sopenharmony_ci		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
1962306a36Sopenharmony_ci		.rangelow   =   49000000,
2062306a36Sopenharmony_ci		.rangehigh  =  263000000,
2162306a36Sopenharmony_ci	}, {
2262306a36Sopenharmony_ci		.type = V4L2_TUNER_RF,
2362306a36Sopenharmony_ci		.index = 1,
2462306a36Sopenharmony_ci		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
2562306a36Sopenharmony_ci		.rangelow   =  390000000,
2662306a36Sopenharmony_ci		.rangehigh  =  960000000,
2762306a36Sopenharmony_ci	},
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistruct msi001_dev {
3162306a36Sopenharmony_ci	struct spi_device *spi;
3262306a36Sopenharmony_ci	struct v4l2_subdev sd;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	/* Controls */
3562306a36Sopenharmony_ci	struct v4l2_ctrl_handler hdl;
3662306a36Sopenharmony_ci	struct v4l2_ctrl *bandwidth_auto;
3762306a36Sopenharmony_ci	struct v4l2_ctrl *bandwidth;
3862306a36Sopenharmony_ci	struct v4l2_ctrl *lna_gain;
3962306a36Sopenharmony_ci	struct v4l2_ctrl *mixer_gain;
4062306a36Sopenharmony_ci	struct v4l2_ctrl *if_gain;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	unsigned int f_tuner;
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic inline struct msi001_dev *sd_to_msi001_dev(struct v4l2_subdev *sd)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	return container_of(sd, struct msi001_dev, sd);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int msi001_wreg(struct msi001_dev *dev, u32 data)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	/* Register format: 4 bits addr + 20 bits value */
5362306a36Sopenharmony_ci	return spi_write(dev->spi, &data, 3);
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int msi001_set_gain(struct msi001_dev *dev, int lna_gain, int mixer_gain,
5762306a36Sopenharmony_ci			   int if_gain)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct spi_device *spi = dev->spi;
6062306a36Sopenharmony_ci	int ret;
6162306a36Sopenharmony_ci	u32 reg;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	dev_dbg(&spi->dev, "lna=%d mixer=%d if=%d\n",
6462306a36Sopenharmony_ci		lna_gain, mixer_gain, if_gain);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	reg = 1 << 0;
6762306a36Sopenharmony_ci	reg |= (59 - if_gain) << 4;
6862306a36Sopenharmony_ci	reg |= 0 << 10;
6962306a36Sopenharmony_ci	reg |= (1 - mixer_gain) << 12;
7062306a36Sopenharmony_ci	reg |= (1 - lna_gain) << 13;
7162306a36Sopenharmony_ci	reg |= 4 << 14;
7262306a36Sopenharmony_ci	reg |= 0 << 17;
7362306a36Sopenharmony_ci	ret = msi001_wreg(dev, reg);
7462306a36Sopenharmony_ci	if (ret)
7562306a36Sopenharmony_ci		goto err;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return 0;
7862306a36Sopenharmony_cierr:
7962306a36Sopenharmony_ci	dev_dbg(&spi->dev, "failed %d\n", ret);
8062306a36Sopenharmony_ci	return ret;
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int msi001_set_tuner(struct msi001_dev *dev)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct spi_device *spi = dev->spi;
8662306a36Sopenharmony_ci	int ret, i;
8762306a36Sopenharmony_ci	unsigned int uitmp, div_n, k, k_thresh, k_frac, div_lo, f_if1;
8862306a36Sopenharmony_ci	u32 reg;
8962306a36Sopenharmony_ci	u64 f_vco;
9062306a36Sopenharmony_ci	u8 mode, filter_mode;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	static const struct {
9362306a36Sopenharmony_ci		u32 rf;
9462306a36Sopenharmony_ci		u8 mode;
9562306a36Sopenharmony_ci		u8 div_lo;
9662306a36Sopenharmony_ci	} band_lut[] = {
9762306a36Sopenharmony_ci		{ 50000000, 0xe1, 16}, /* AM_MODE2, antenna 2 */
9862306a36Sopenharmony_ci		{108000000, 0x42, 32}, /* VHF_MODE */
9962306a36Sopenharmony_ci		{330000000, 0x44, 16}, /* B3_MODE */
10062306a36Sopenharmony_ci		{960000000, 0x48,  4}, /* B45_MODE */
10162306a36Sopenharmony_ci		{      ~0U, 0x50,  2}, /* BL_MODE */
10262306a36Sopenharmony_ci	};
10362306a36Sopenharmony_ci	static const struct {
10462306a36Sopenharmony_ci		u32 freq;
10562306a36Sopenharmony_ci		u8 filter_mode;
10662306a36Sopenharmony_ci	} if_freq_lut[] = {
10762306a36Sopenharmony_ci		{      0, 0x03}, /* Zero IF */
10862306a36Sopenharmony_ci		{ 450000, 0x02}, /* 450 kHz IF */
10962306a36Sopenharmony_ci		{1620000, 0x01}, /* 1.62 MHz IF */
11062306a36Sopenharmony_ci		{2048000, 0x00}, /* 2.048 MHz IF */
11162306a36Sopenharmony_ci	};
11262306a36Sopenharmony_ci	static const struct {
11362306a36Sopenharmony_ci		u32 freq;
11462306a36Sopenharmony_ci		u8 val;
11562306a36Sopenharmony_ci	} bandwidth_lut[] = {
11662306a36Sopenharmony_ci		{ 200000, 0x00}, /* 200 kHz */
11762306a36Sopenharmony_ci		{ 300000, 0x01}, /* 300 kHz */
11862306a36Sopenharmony_ci		{ 600000, 0x02}, /* 600 kHz */
11962306a36Sopenharmony_ci		{1536000, 0x03}, /* 1.536 MHz */
12062306a36Sopenharmony_ci		{5000000, 0x04}, /* 5 MHz */
12162306a36Sopenharmony_ci		{6000000, 0x05}, /* 6 MHz */
12262306a36Sopenharmony_ci		{7000000, 0x06}, /* 7 MHz */
12362306a36Sopenharmony_ci		{8000000, 0x07}, /* 8 MHz */
12462306a36Sopenharmony_ci	};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	unsigned int f_rf = dev->f_tuner;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/*
12962306a36Sopenharmony_ci	 * bandwidth (Hz)
13062306a36Sopenharmony_ci	 * 200000, 300000, 600000, 1536000, 5000000, 6000000, 7000000, 8000000
13162306a36Sopenharmony_ci	 */
13262306a36Sopenharmony_ci	unsigned int bandwidth;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/*
13562306a36Sopenharmony_ci	 * intermediate frequency (Hz)
13662306a36Sopenharmony_ci	 * 0, 450000, 1620000, 2048000
13762306a36Sopenharmony_ci	 */
13862306a36Sopenharmony_ci	unsigned int f_if = 0;
13962306a36Sopenharmony_ci	#define F_REF 24000000
14062306a36Sopenharmony_ci	#define DIV_PRE_N 4
14162306a36Sopenharmony_ci	#define	F_VCO_STEP div_lo
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	dev_dbg(&spi->dev, "f_rf=%d f_if=%d\n", f_rf, f_if);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(band_lut); i++) {
14662306a36Sopenharmony_ci		if (f_rf <= band_lut[i].rf) {
14762306a36Sopenharmony_ci			mode = band_lut[i].mode;
14862306a36Sopenharmony_ci			div_lo = band_lut[i].div_lo;
14962306a36Sopenharmony_ci			break;
15062306a36Sopenharmony_ci		}
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci	if (i == ARRAY_SIZE(band_lut)) {
15362306a36Sopenharmony_ci		ret = -EINVAL;
15462306a36Sopenharmony_ci		goto err;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/* AM_MODE is upconverted */
15862306a36Sopenharmony_ci	if ((mode >> 0) & 0x1)
15962306a36Sopenharmony_ci		f_if1 =  5 * F_REF;
16062306a36Sopenharmony_ci	else
16162306a36Sopenharmony_ci		f_if1 =  0;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(if_freq_lut); i++) {
16462306a36Sopenharmony_ci		if (f_if == if_freq_lut[i].freq) {
16562306a36Sopenharmony_ci			filter_mode = if_freq_lut[i].filter_mode;
16662306a36Sopenharmony_ci			break;
16762306a36Sopenharmony_ci		}
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci	if (i == ARRAY_SIZE(if_freq_lut)) {
17062306a36Sopenharmony_ci		ret = -EINVAL;
17162306a36Sopenharmony_ci		goto err;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* filters */
17562306a36Sopenharmony_ci	bandwidth = dev->bandwidth->val;
17662306a36Sopenharmony_ci	bandwidth = clamp(bandwidth, 200000U, 8000000U);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(bandwidth_lut); i++) {
17962306a36Sopenharmony_ci		if (bandwidth <= bandwidth_lut[i].freq) {
18062306a36Sopenharmony_ci			bandwidth = bandwidth_lut[i].val;
18162306a36Sopenharmony_ci			break;
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci	if (i == ARRAY_SIZE(bandwidth_lut)) {
18562306a36Sopenharmony_ci		ret = -EINVAL;
18662306a36Sopenharmony_ci		goto err;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	dev->bandwidth->val = bandwidth_lut[i].freq;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	dev_dbg(&spi->dev, "bandwidth selected=%d\n", bandwidth_lut[i].freq);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/*
19462306a36Sopenharmony_ci	 * Fractional-N synthesizer
19562306a36Sopenharmony_ci	 *
19662306a36Sopenharmony_ci	 *           +---------------------------------------+
19762306a36Sopenharmony_ci	 *           v                                       |
19862306a36Sopenharmony_ci	 *  Fref   +----+     +-------+         +----+     +------+     +---+
19962306a36Sopenharmony_ci	 * ------> | PD | --> |  VCO  | ------> | /4 | --> | /N.F | <-- | K |
20062306a36Sopenharmony_ci	 *         +----+     +-------+         +----+     +------+     +---+
20162306a36Sopenharmony_ci	 *                      |
20262306a36Sopenharmony_ci	 *                      |
20362306a36Sopenharmony_ci	 *                      v
20462306a36Sopenharmony_ci	 *                    +-------+  Fout
20562306a36Sopenharmony_ci	 *                    | /Rout | ------>
20662306a36Sopenharmony_ci	 *                    +-------+
20762306a36Sopenharmony_ci	 */
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* Calculate PLL integer and fractional control word. */
21062306a36Sopenharmony_ci	f_vco = (u64) (f_rf + f_if + f_if1) * div_lo;
21162306a36Sopenharmony_ci	div_n = div_u64_rem(f_vco, DIV_PRE_N * F_REF, &k);
21262306a36Sopenharmony_ci	k_thresh = (DIV_PRE_N * F_REF) / F_VCO_STEP;
21362306a36Sopenharmony_ci	k_frac = div_u64((u64) k * k_thresh, (DIV_PRE_N * F_REF));
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Find out greatest common divisor and divide to smaller. */
21662306a36Sopenharmony_ci	uitmp = gcd(k_thresh, k_frac);
21762306a36Sopenharmony_ci	k_thresh /= uitmp;
21862306a36Sopenharmony_ci	k_frac /= uitmp;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* Force divide to reg max. Resolution will be reduced. */
22162306a36Sopenharmony_ci	uitmp = DIV_ROUND_UP(k_thresh, 4095);
22262306a36Sopenharmony_ci	k_thresh = DIV_ROUND_CLOSEST(k_thresh, uitmp);
22362306a36Sopenharmony_ci	k_frac = DIV_ROUND_CLOSEST(k_frac, uitmp);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* Calculate real RF set. */
22662306a36Sopenharmony_ci	uitmp = (unsigned int) F_REF * DIV_PRE_N * div_n;
22762306a36Sopenharmony_ci	uitmp += (unsigned int) F_REF * DIV_PRE_N * k_frac / k_thresh;
22862306a36Sopenharmony_ci	uitmp /= div_lo;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	dev_dbg(&spi->dev,
23162306a36Sopenharmony_ci		"f_rf=%u:%u f_vco=%llu div_n=%u k_thresh=%u k_frac=%u div_lo=%u\n",
23262306a36Sopenharmony_ci		f_rf, uitmp, f_vco, div_n, k_thresh, k_frac, div_lo);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	ret = msi001_wreg(dev, 0x00000e);
23562306a36Sopenharmony_ci	if (ret)
23662306a36Sopenharmony_ci		goto err;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	ret = msi001_wreg(dev, 0x000003);
23962306a36Sopenharmony_ci	if (ret)
24062306a36Sopenharmony_ci		goto err;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	reg = 0 << 0;
24362306a36Sopenharmony_ci	reg |= mode << 4;
24462306a36Sopenharmony_ci	reg |= filter_mode << 12;
24562306a36Sopenharmony_ci	reg |= bandwidth << 14;
24662306a36Sopenharmony_ci	reg |= 0x02 << 17;
24762306a36Sopenharmony_ci	reg |= 0x00 << 20;
24862306a36Sopenharmony_ci	ret = msi001_wreg(dev, reg);
24962306a36Sopenharmony_ci	if (ret)
25062306a36Sopenharmony_ci		goto err;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	reg = 5 << 0;
25362306a36Sopenharmony_ci	reg |= k_thresh << 4;
25462306a36Sopenharmony_ci	reg |= 1 << 19;
25562306a36Sopenharmony_ci	reg |= 1 << 21;
25662306a36Sopenharmony_ci	ret = msi001_wreg(dev, reg);
25762306a36Sopenharmony_ci	if (ret)
25862306a36Sopenharmony_ci		goto err;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	reg = 2 << 0;
26162306a36Sopenharmony_ci	reg |= k_frac << 4;
26262306a36Sopenharmony_ci	reg |= div_n << 16;
26362306a36Sopenharmony_ci	ret = msi001_wreg(dev, reg);
26462306a36Sopenharmony_ci	if (ret)
26562306a36Sopenharmony_ci		goto err;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	ret = msi001_set_gain(dev, dev->lna_gain->cur.val,
26862306a36Sopenharmony_ci			      dev->mixer_gain->cur.val, dev->if_gain->cur.val);
26962306a36Sopenharmony_ci	if (ret)
27062306a36Sopenharmony_ci		goto err;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	reg = 6 << 0;
27362306a36Sopenharmony_ci	reg |= 63 << 4;
27462306a36Sopenharmony_ci	reg |= 4095 << 10;
27562306a36Sopenharmony_ci	ret = msi001_wreg(dev, reg);
27662306a36Sopenharmony_ci	if (ret)
27762306a36Sopenharmony_ci		goto err;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return 0;
28062306a36Sopenharmony_cierr:
28162306a36Sopenharmony_ci	dev_dbg(&spi->dev, "failed %d\n", ret);
28262306a36Sopenharmony_ci	return ret;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic int msi001_standby(struct v4l2_subdev *sd)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct msi001_dev *dev = sd_to_msi001_dev(sd);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return msi001_wreg(dev, 0x000000);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic int msi001_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct msi001_dev *dev = sd_to_msi001_dev(sd);
29562306a36Sopenharmony_ci	struct spi_device *spi = dev->spi;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	dev_dbg(&spi->dev, "index=%d\n", v->index);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	strscpy(v->name, "Mirics MSi001", sizeof(v->name));
30062306a36Sopenharmony_ci	v->type = V4L2_TUNER_RF;
30162306a36Sopenharmony_ci	v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
30262306a36Sopenharmony_ci	v->rangelow =    49000000;
30362306a36Sopenharmony_ci	v->rangehigh =  960000000;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	return 0;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic int msi001_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *v)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct msi001_dev *dev = sd_to_msi001_dev(sd);
31162306a36Sopenharmony_ci	struct spi_device *spi = dev->spi;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	dev_dbg(&spi->dev, "index=%d\n", v->index);
31462306a36Sopenharmony_ci	return 0;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic int msi001_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct msi001_dev *dev = sd_to_msi001_dev(sd);
32062306a36Sopenharmony_ci	struct spi_device *spi = dev->spi;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	dev_dbg(&spi->dev, "tuner=%d\n", f->tuner);
32362306a36Sopenharmony_ci	f->frequency = dev->f_tuner;
32462306a36Sopenharmony_ci	return 0;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic int msi001_s_frequency(struct v4l2_subdev *sd,
32862306a36Sopenharmony_ci			      const struct v4l2_frequency *f)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	struct msi001_dev *dev = sd_to_msi001_dev(sd);
33162306a36Sopenharmony_ci	struct spi_device *spi = dev->spi;
33262306a36Sopenharmony_ci	unsigned int band;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	dev_dbg(&spi->dev, "tuner=%d type=%d frequency=%u\n",
33562306a36Sopenharmony_ci		f->tuner, f->type, f->frequency);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (f->frequency < ((bands[0].rangehigh + bands[1].rangelow) / 2))
33862306a36Sopenharmony_ci		band = 0;
33962306a36Sopenharmony_ci	else
34062306a36Sopenharmony_ci		band = 1;
34162306a36Sopenharmony_ci	dev->f_tuner = clamp_t(unsigned int, f->frequency,
34262306a36Sopenharmony_ci			       bands[band].rangelow, bands[band].rangehigh);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return msi001_set_tuner(dev);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic int msi001_enum_freq_bands(struct v4l2_subdev *sd,
34862306a36Sopenharmony_ci				  struct v4l2_frequency_band *band)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct msi001_dev *dev = sd_to_msi001_dev(sd);
35162306a36Sopenharmony_ci	struct spi_device *spi = dev->spi;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	dev_dbg(&spi->dev, "tuner=%d type=%d index=%d\n",
35462306a36Sopenharmony_ci		band->tuner, band->type, band->index);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (band->index >= ARRAY_SIZE(bands))
35762306a36Sopenharmony_ci		return -EINVAL;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	band->capability = bands[band->index].capability;
36062306a36Sopenharmony_ci	band->rangelow = bands[band->index].rangelow;
36162306a36Sopenharmony_ci	band->rangehigh = bands[band->index].rangehigh;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	return 0;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic const struct v4l2_subdev_tuner_ops msi001_tuner_ops = {
36762306a36Sopenharmony_ci	.standby                  = msi001_standby,
36862306a36Sopenharmony_ci	.g_tuner                  = msi001_g_tuner,
36962306a36Sopenharmony_ci	.s_tuner                  = msi001_s_tuner,
37062306a36Sopenharmony_ci	.g_frequency              = msi001_g_frequency,
37162306a36Sopenharmony_ci	.s_frequency              = msi001_s_frequency,
37262306a36Sopenharmony_ci	.enum_freq_bands          = msi001_enum_freq_bands,
37362306a36Sopenharmony_ci};
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic const struct v4l2_subdev_ops msi001_ops = {
37662306a36Sopenharmony_ci	.tuner                    = &msi001_tuner_ops,
37762306a36Sopenharmony_ci};
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int msi001_s_ctrl(struct v4l2_ctrl *ctrl)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct msi001_dev *dev = container_of(ctrl->handler, struct msi001_dev, hdl);
38262306a36Sopenharmony_ci	struct spi_device *spi = dev->spi;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	int ret;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	dev_dbg(&spi->dev, "id=%d name=%s val=%d min=%lld max=%lld step=%lld\n",
38762306a36Sopenharmony_ci		ctrl->id, ctrl->name, ctrl->val, ctrl->minimum, ctrl->maximum,
38862306a36Sopenharmony_ci		ctrl->step);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	switch (ctrl->id) {
39162306a36Sopenharmony_ci	case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:
39262306a36Sopenharmony_ci	case V4L2_CID_RF_TUNER_BANDWIDTH:
39362306a36Sopenharmony_ci		ret = msi001_set_tuner(dev);
39462306a36Sopenharmony_ci		break;
39562306a36Sopenharmony_ci	case  V4L2_CID_RF_TUNER_LNA_GAIN:
39662306a36Sopenharmony_ci		ret = msi001_set_gain(dev, dev->lna_gain->val,
39762306a36Sopenharmony_ci				      dev->mixer_gain->cur.val,
39862306a36Sopenharmony_ci				      dev->if_gain->cur.val);
39962306a36Sopenharmony_ci		break;
40062306a36Sopenharmony_ci	case  V4L2_CID_RF_TUNER_MIXER_GAIN:
40162306a36Sopenharmony_ci		ret = msi001_set_gain(dev, dev->lna_gain->cur.val,
40262306a36Sopenharmony_ci				      dev->mixer_gain->val,
40362306a36Sopenharmony_ci				      dev->if_gain->cur.val);
40462306a36Sopenharmony_ci		break;
40562306a36Sopenharmony_ci	case  V4L2_CID_RF_TUNER_IF_GAIN:
40662306a36Sopenharmony_ci		ret = msi001_set_gain(dev, dev->lna_gain->cur.val,
40762306a36Sopenharmony_ci				      dev->mixer_gain->cur.val,
40862306a36Sopenharmony_ci				      dev->if_gain->val);
40962306a36Sopenharmony_ci		break;
41062306a36Sopenharmony_ci	default:
41162306a36Sopenharmony_ci		dev_dbg(&spi->dev, "unknown control %d\n", ctrl->id);
41262306a36Sopenharmony_ci		ret = -EINVAL;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	return ret;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops msi001_ctrl_ops = {
41962306a36Sopenharmony_ci	.s_ctrl                   = msi001_s_ctrl,
42062306a36Sopenharmony_ci};
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int msi001_probe(struct spi_device *spi)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct msi001_dev *dev;
42562306a36Sopenharmony_ci	int ret;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	dev_dbg(&spi->dev, "\n");
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
43062306a36Sopenharmony_ci	if (!dev) {
43162306a36Sopenharmony_ci		ret = -ENOMEM;
43262306a36Sopenharmony_ci		goto err;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	dev->spi = spi;
43662306a36Sopenharmony_ci	dev->f_tuner = bands[0].rangelow;
43762306a36Sopenharmony_ci	v4l2_spi_subdev_init(&dev->sd, spi, &msi001_ops);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	/* Register controls */
44062306a36Sopenharmony_ci	v4l2_ctrl_handler_init(&dev->hdl, 5);
44162306a36Sopenharmony_ci	dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, &msi001_ctrl_ops,
44262306a36Sopenharmony_ci			V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1);
44362306a36Sopenharmony_ci	dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, &msi001_ctrl_ops,
44462306a36Sopenharmony_ci			V4L2_CID_RF_TUNER_BANDWIDTH, 200000, 8000000, 1, 200000);
44562306a36Sopenharmony_ci	if (dev->hdl.error) {
44662306a36Sopenharmony_ci		ret = dev->hdl.error;
44762306a36Sopenharmony_ci		dev_err(&spi->dev, "Could not initialize controls\n");
44862306a36Sopenharmony_ci		/* control init failed, free handler */
44962306a36Sopenharmony_ci		goto err_ctrl_handler_free;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false);
45362306a36Sopenharmony_ci	dev->lna_gain = v4l2_ctrl_new_std(&dev->hdl, &msi001_ctrl_ops,
45462306a36Sopenharmony_ci			V4L2_CID_RF_TUNER_LNA_GAIN, 0, 1, 1, 1);
45562306a36Sopenharmony_ci	dev->mixer_gain = v4l2_ctrl_new_std(&dev->hdl, &msi001_ctrl_ops,
45662306a36Sopenharmony_ci			V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 1, 1, 1);
45762306a36Sopenharmony_ci	dev->if_gain = v4l2_ctrl_new_std(&dev->hdl, &msi001_ctrl_ops,
45862306a36Sopenharmony_ci			V4L2_CID_RF_TUNER_IF_GAIN, 0, 59, 1, 0);
45962306a36Sopenharmony_ci	if (dev->hdl.error) {
46062306a36Sopenharmony_ci		ret = dev->hdl.error;
46162306a36Sopenharmony_ci		dev_err(&spi->dev, "Could not initialize controls\n");
46262306a36Sopenharmony_ci		/* control init failed, free handler */
46362306a36Sopenharmony_ci		goto err_ctrl_handler_free;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	dev->sd.ctrl_handler = &dev->hdl;
46762306a36Sopenharmony_ci	return 0;
46862306a36Sopenharmony_cierr_ctrl_handler_free:
46962306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&dev->hdl);
47062306a36Sopenharmony_ci	kfree(dev);
47162306a36Sopenharmony_cierr:
47262306a36Sopenharmony_ci	return ret;
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic void msi001_remove(struct spi_device *spi)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct v4l2_subdev *sd = spi_get_drvdata(spi);
47862306a36Sopenharmony_ci	struct msi001_dev *dev = sd_to_msi001_dev(sd);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	dev_dbg(&spi->dev, "\n");
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	/*
48362306a36Sopenharmony_ci	 * Registered by v4l2_spi_new_subdev() from master driver, but we must
48462306a36Sopenharmony_ci	 * unregister it from here. Weird.
48562306a36Sopenharmony_ci	 */
48662306a36Sopenharmony_ci	v4l2_device_unregister_subdev(&dev->sd);
48762306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&dev->hdl);
48862306a36Sopenharmony_ci	kfree(dev);
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic const struct spi_device_id msi001_id_table[] = {
49262306a36Sopenharmony_ci	{"msi001", 0},
49362306a36Sopenharmony_ci	{}
49462306a36Sopenharmony_ci};
49562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(spi, msi001_id_table);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic struct spi_driver msi001_driver = {
49862306a36Sopenharmony_ci	.driver = {
49962306a36Sopenharmony_ci		.name	= "msi001",
50062306a36Sopenharmony_ci		.suppress_bind_attrs = true,
50162306a36Sopenharmony_ci	},
50262306a36Sopenharmony_ci	.probe		= msi001_probe,
50362306a36Sopenharmony_ci	.remove		= msi001_remove,
50462306a36Sopenharmony_ci	.id_table	= msi001_id_table,
50562306a36Sopenharmony_ci};
50662306a36Sopenharmony_cimodule_spi_driver(msi001_driver);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ciMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
50962306a36Sopenharmony_ciMODULE_DESCRIPTION("Mirics MSi001");
51062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
511