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