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