14faba767SHans de Goede /* 24faba767SHans de Goede * v4l2 driver for TEA5777 Philips AM/FM radio tuner chips 34faba767SHans de Goede * 44faba767SHans de Goede * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com> 54faba767SHans de Goede * 64faba767SHans de Goede * Based on the ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips: 74faba767SHans de Goede * 84faba767SHans de Goede * Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz> 94faba767SHans de Goede * 104faba767SHans de Goede * This program is free software; you can redistribute it and/or modify 114faba767SHans de Goede * it under the terms of the GNU General Public License as published by 124faba767SHans de Goede * the Free Software Foundation; either version 2 of the License, or 134faba767SHans de Goede * (at your option) any later version. 144faba767SHans de Goede * 154faba767SHans de Goede * This program is distributed in the hope that it will be useful, 164faba767SHans de Goede * but WITHOUT ANY WARRANTY; without even the implied warranty of 174faba767SHans de Goede * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 184faba767SHans de Goede * GNU General Public License for more details. 194faba767SHans de Goede * 204faba767SHans de Goede * You should have received a copy of the GNU General Public License 214faba767SHans de Goede * along with this program; if not, write to the Free Software 224faba767SHans de Goede * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 234faba767SHans de Goede * 244faba767SHans de Goede */ 254faba767SHans de Goede 264faba767SHans de Goede #include <linux/delay.h> 274faba767SHans de Goede #include <linux/init.h> 284faba767SHans de Goede #include <linux/module.h> 294faba767SHans de Goede #include <linux/sched.h> 304faba767SHans de Goede #include <linux/slab.h> 314faba767SHans de Goede #include <media/v4l2-device.h> 324faba767SHans de Goede #include <media/v4l2-dev.h> 334faba767SHans de Goede #include <media/v4l2-fh.h> 344faba767SHans de Goede #include <media/v4l2-ioctl.h> 354faba767SHans de Goede #include <media/v4l2-event.h> 364faba767SHans de Goede #include "radio-tea5777.h" 374faba767SHans de Goede 384faba767SHans de Goede MODULE_AUTHOR("Hans de Goede <perex@perex.cz>"); 394faba767SHans de Goede MODULE_DESCRIPTION("Routines for control of TEA5777 Philips AM/FM radio tuner chips"); 404faba767SHans de Goede MODULE_LICENSE("GPL"); 414faba767SHans de Goede 424faba767SHans de Goede #define TEA5777_FM_IF 150 /* kHz */ 434faba767SHans de Goede #define TEA5777_FM_FREQ_STEP 50 /* kHz */ 444faba767SHans de Goede 45d7aab0bfSHans de Goede #define TEA5777_AM_IF 21 /* kHz */ 46d7aab0bfSHans de Goede #define TEA5777_AM_FREQ_STEP 1 /* kHz */ 47d7aab0bfSHans de Goede 484faba767SHans de Goede /* Write reg, common bits */ 494faba767SHans de Goede #define TEA5777_W_MUTE_MASK (1LL << 47) 504faba767SHans de Goede #define TEA5777_W_MUTE_SHIFT 47 514faba767SHans de Goede #define TEA5777_W_AM_FM_MASK (1LL << 46) 524faba767SHans de Goede #define TEA5777_W_AM_FM_SHIFT 46 534faba767SHans de Goede #define TEA5777_W_STB_MASK (1LL << 45) 544faba767SHans de Goede #define TEA5777_W_STB_SHIFT 45 554faba767SHans de Goede 564faba767SHans de Goede #define TEA5777_W_IFCE_MASK (1LL << 29) 574faba767SHans de Goede #define TEA5777_W_IFCE_SHIFT 29 584faba767SHans de Goede #define TEA5777_W_IFW_MASK (1LL << 28) 594faba767SHans de Goede #define TEA5777_W_IFW_SHIFT 28 604faba767SHans de Goede #define TEA5777_W_HILO_MASK (1LL << 27) 614faba767SHans de Goede #define TEA5777_W_HILO_SHIFT 27 624faba767SHans de Goede #define TEA5777_W_DBUS_MASK (1LL << 26) 634faba767SHans de Goede #define TEA5777_W_DBUS_SHIFT 26 644faba767SHans de Goede 654faba767SHans de Goede #define TEA5777_W_INTEXT_MASK (1LL << 24) 664faba767SHans de Goede #define TEA5777_W_INTEXT_SHIFT 24 674faba767SHans de Goede #define TEA5777_W_P1_MASK (1LL << 23) 684faba767SHans de Goede #define TEA5777_W_P1_SHIFT 23 694faba767SHans de Goede #define TEA5777_W_P0_MASK (1LL << 22) 704faba767SHans de Goede #define TEA5777_W_P0_SHIFT 22 714faba767SHans de Goede #define TEA5777_W_PEN1_MASK (1LL << 21) 724faba767SHans de Goede #define TEA5777_W_PEN1_SHIFT 21 734faba767SHans de Goede #define TEA5777_W_PEN0_MASK (1LL << 20) 744faba767SHans de Goede #define TEA5777_W_PEN0_SHIFT 20 754faba767SHans de Goede 764faba767SHans de Goede #define TEA5777_W_CHP0_MASK (1LL << 18) 774faba767SHans de Goede #define TEA5777_W_CHP0_SHIFT 18 784faba767SHans de Goede #define TEA5777_W_DEEM_MASK (1LL << 17) 794faba767SHans de Goede #define TEA5777_W_DEEM_SHIFT 17 804faba767SHans de Goede 814faba767SHans de Goede #define TEA5777_W_SEARCH_MASK (1LL << 7) 824faba767SHans de Goede #define TEA5777_W_SEARCH_SHIFT 7 834faba767SHans de Goede #define TEA5777_W_PROGBLIM_MASK (1LL << 6) 844faba767SHans de Goede #define TEA5777_W_PROGBLIM_SHIFT 6 854faba767SHans de Goede #define TEA5777_W_UPDWN_MASK (1LL << 5) 864faba767SHans de Goede #define TEA5777_W_UPDWN_SHIFT 5 874faba767SHans de Goede #define TEA5777_W_SLEV_MASK (3LL << 3) 884faba767SHans de Goede #define TEA5777_W_SLEV_SHIFT 3 894faba767SHans de Goede 904faba767SHans de Goede /* Write reg, FM specific bits */ 914faba767SHans de Goede #define TEA5777_W_FM_PLL_MASK (0x1fffLL << 32) 924faba767SHans de Goede #define TEA5777_W_FM_PLL_SHIFT 32 934faba767SHans de Goede #define TEA5777_W_FM_FREF_MASK (0x03LL << 30) 944faba767SHans de Goede #define TEA5777_W_FM_FREF_SHIFT 30 95d7aab0bfSHans de Goede #define TEA5777_W_FM_FREF_VALUE 0LL /* 50k steps, 150k IF */ 964faba767SHans de Goede 974faba767SHans de Goede #define TEA5777_W_FM_FORCEMONO_MASK (1LL << 15) 984faba767SHans de Goede #define TEA5777_W_FM_FORCEMONO_SHIFT 15 994faba767SHans de Goede #define TEA5777_W_FM_SDSOFF_MASK (1LL << 14) 1004faba767SHans de Goede #define TEA5777_W_FM_SDSOFF_SHIFT 14 1014faba767SHans de Goede #define TEA5777_W_FM_DOFF_MASK (1LL << 13) 1024faba767SHans de Goede #define TEA5777_W_FM_DOFF_SHIFT 13 1034faba767SHans de Goede 1044faba767SHans de Goede #define TEA5777_W_FM_STEP_MASK (3LL << 1) 1054faba767SHans de Goede #define TEA5777_W_FM_STEP_SHIFT 1 1064faba767SHans de Goede 1074faba767SHans de Goede /* Write reg, AM specific bits */ 1084faba767SHans de Goede #define TEA5777_W_AM_PLL_MASK (0x7ffLL << 34) 1094faba767SHans de Goede #define TEA5777_W_AM_PLL_SHIFT 34 1104faba767SHans de Goede #define TEA5777_W_AM_AGCRF_MASK (1LL << 33) 1114faba767SHans de Goede #define TEA5777_W_AM_AGCRF_SHIFT 33 1124faba767SHans de Goede #define TEA5777_W_AM_AGCIF_MASK (1LL << 32) 1134faba767SHans de Goede #define TEA5777_W_AM_AGCIF_SHIFT 32 1144faba767SHans de Goede #define TEA5777_W_AM_MWLW_MASK (1LL << 31) 1154faba767SHans de Goede #define TEA5777_W_AM_MWLW_SHIFT 31 116d7aab0bfSHans de Goede #define TEA5777_W_AM_LW 0LL 117d7aab0bfSHans de Goede #define TEA5777_W_AM_MW 1LL 1184faba767SHans de Goede #define TEA5777_W_AM_LNA_MASK (1LL << 30) 1194faba767SHans de Goede #define TEA5777_W_AM_LNA_SHIFT 30 1204faba767SHans de Goede 1214faba767SHans de Goede #define TEA5777_W_AM_PEAK_MASK (1LL << 25) 1224faba767SHans de Goede #define TEA5777_W_AM_PEAK_SHIFT 25 1234faba767SHans de Goede 1244faba767SHans de Goede #define TEA5777_W_AM_RFB_MASK (1LL << 16) 1254faba767SHans de Goede #define TEA5777_W_AM_RFB_SHIFT 16 1264faba767SHans de Goede #define TEA5777_W_AM_CALLIGN_MASK (1LL << 15) 1274faba767SHans de Goede #define TEA5777_W_AM_CALLIGN_SHIFT 15 1284faba767SHans de Goede #define TEA5777_W_AM_CBANK_MASK (0x7fLL << 8) 1294faba767SHans de Goede #define TEA5777_W_AM_CBANK_SHIFT 8 1304faba767SHans de Goede 1314faba767SHans de Goede #define TEA5777_W_AM_DELAY_MASK (1LL << 2) 1324faba767SHans de Goede #define TEA5777_W_AM_DELAY_SHIFT 2 1334faba767SHans de Goede #define TEA5777_W_AM_STEP_MASK (1LL << 1) 1344faba767SHans de Goede #define TEA5777_W_AM_STEP_SHIFT 1 1354faba767SHans de Goede 1364faba767SHans de Goede /* Read reg, common bits */ 1374faba767SHans de Goede #define TEA5777_R_LEVEL_MASK (0x0f << 17) 1384faba767SHans de Goede #define TEA5777_R_LEVEL_SHIFT 17 1394faba767SHans de Goede #define TEA5777_R_SFOUND_MASK (0x01 << 16) 1404faba767SHans de Goede #define TEA5777_R_SFOUND_SHIFT 16 1414faba767SHans de Goede #define TEA5777_R_BLIM_MASK (0x01 << 15) 1424faba767SHans de Goede #define TEA5777_R_BLIM_SHIFT 15 1434faba767SHans de Goede 1444faba767SHans de Goede /* Read reg, FM specific bits */ 1454faba767SHans de Goede #define TEA5777_R_FM_STEREO_MASK (0x01 << 21) 1464faba767SHans de Goede #define TEA5777_R_FM_STEREO_SHIFT 21 1474faba767SHans de Goede #define TEA5777_R_FM_PLL_MASK 0x1fff 1484faba767SHans de Goede #define TEA5777_R_FM_PLL_SHIFT 0 1494faba767SHans de Goede 150d7aab0bfSHans de Goede enum { BAND_FM, BAND_AM }; 151d7aab0bfSHans de Goede 152d7aab0bfSHans de Goede static const struct v4l2_frequency_band bands[] = { 153d7aab0bfSHans de Goede { 154d7aab0bfSHans de Goede .type = V4L2_TUNER_RADIO, 155d7aab0bfSHans de Goede .index = 0, 156d7aab0bfSHans de Goede .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | 157d7aab0bfSHans de Goede V4L2_TUNER_CAP_FREQ_BANDS | 158d7aab0bfSHans de Goede V4L2_TUNER_CAP_HWSEEK_BOUNDED | 159d7aab0bfSHans de Goede V4L2_TUNER_CAP_HWSEEK_PROG_LIM, 160d7aab0bfSHans de Goede .rangelow = 76000 * 16, 161d7aab0bfSHans de Goede .rangehigh = 108000 * 16, 162d7aab0bfSHans de Goede .modulation = V4L2_BAND_MODULATION_FM, 163d7aab0bfSHans de Goede }, 164d7aab0bfSHans de Goede { 165d7aab0bfSHans de Goede .type = V4L2_TUNER_RADIO, 166d7aab0bfSHans de Goede .index = 1, 167d7aab0bfSHans de Goede .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS | 168d7aab0bfSHans de Goede V4L2_TUNER_CAP_HWSEEK_BOUNDED | 169d7aab0bfSHans de Goede V4L2_TUNER_CAP_HWSEEK_PROG_LIM, 170d7aab0bfSHans de Goede .rangelow = 530 * 16, 171d7aab0bfSHans de Goede .rangehigh = 1710 * 16, 172d7aab0bfSHans de Goede .modulation = V4L2_BAND_MODULATION_AM, 173d7aab0bfSHans de Goede }, 174d7aab0bfSHans de Goede }; 175d7aab0bfSHans de Goede 1764faba767SHans de Goede static u32 tea5777_freq_to_v4l2_freq(struct radio_tea5777 *tea, u32 freq) 1774faba767SHans de Goede { 178d7aab0bfSHans de Goede switch (tea->band) { 179d7aab0bfSHans de Goede case BAND_FM: 1804faba767SHans de Goede return (freq * TEA5777_FM_FREQ_STEP + TEA5777_FM_IF) * 16; 181d7aab0bfSHans de Goede case BAND_AM: 182d7aab0bfSHans de Goede return (freq * TEA5777_AM_FREQ_STEP + TEA5777_AM_IF) * 16; 183d7aab0bfSHans de Goede } 184d7aab0bfSHans de Goede return 0; /* Never reached */ 1854faba767SHans de Goede } 1864faba767SHans de Goede 187d1f280d6SHans de Goede int radio_tea5777_set_freq(struct radio_tea5777 *tea) 1884faba767SHans de Goede { 1894fad5c47SHans de Goede u32 freq; 1904faba767SHans de Goede int res; 1914faba767SHans de Goede 192d7aab0bfSHans de Goede freq = clamp(tea->freq, bands[tea->band].rangelow, 193d7aab0bfSHans de Goede bands[tea->band].rangehigh); 1944fad5c47SHans de Goede freq = (freq + 8) / 16; /* to kHz */ 1954faba767SHans de Goede 196d7aab0bfSHans de Goede switch (tea->band) { 197d7aab0bfSHans de Goede case BAND_FM: 198d7aab0bfSHans de Goede tea->write_reg &= ~TEA5777_W_AM_FM_MASK; 1994fad5c47SHans de Goede freq = (freq - TEA5777_FM_IF) / TEA5777_FM_FREQ_STEP; 200d7aab0bfSHans de Goede tea->write_reg &= ~TEA5777_W_FM_PLL_MASK; 2014fad5c47SHans de Goede tea->write_reg |= (u64)freq << TEA5777_W_FM_PLL_SHIFT; 202d7aab0bfSHans de Goede tea->write_reg &= ~TEA5777_W_FM_FREF_MASK; 203d7aab0bfSHans de Goede tea->write_reg |= TEA5777_W_FM_FREF_VALUE << 204d7aab0bfSHans de Goede TEA5777_W_FM_FREF_SHIFT; 205d7aab0bfSHans de Goede tea->write_reg &= ~TEA5777_W_FM_FORCEMONO_MASK; 206d7aab0bfSHans de Goede if (tea->audmode == V4L2_TUNER_MODE_MONO) 207d7aab0bfSHans de Goede tea->write_reg |= 1LL << TEA5777_W_FM_FORCEMONO_SHIFT; 208d7aab0bfSHans de Goede break; 209d7aab0bfSHans de Goede case BAND_AM: 210d7aab0bfSHans de Goede tea->write_reg &= ~TEA5777_W_AM_FM_MASK; 211d7aab0bfSHans de Goede tea->write_reg |= (1LL << TEA5777_W_AM_FM_SHIFT); 212d7aab0bfSHans de Goede freq = (freq - TEA5777_AM_IF) / TEA5777_AM_FREQ_STEP; 213d7aab0bfSHans de Goede tea->write_reg &= ~TEA5777_W_AM_PLL_MASK; 214d7aab0bfSHans de Goede tea->write_reg |= (u64)freq << TEA5777_W_AM_PLL_SHIFT; 215d7aab0bfSHans de Goede tea->write_reg &= ~TEA5777_W_AM_AGCRF_MASK; 216d7aab0bfSHans de Goede tea->write_reg &= ~TEA5777_W_AM_AGCRF_MASK; 217d7aab0bfSHans de Goede tea->write_reg &= ~TEA5777_W_AM_MWLW_MASK; 218d7aab0bfSHans de Goede tea->write_reg |= TEA5777_W_AM_MW << TEA5777_W_AM_MWLW_SHIFT; 219d7aab0bfSHans de Goede tea->write_reg &= ~TEA5777_W_AM_LNA_MASK; 220d7aab0bfSHans de Goede tea->write_reg |= 1LL << TEA5777_W_AM_LNA_SHIFT; 221d7aab0bfSHans de Goede tea->write_reg &= ~TEA5777_W_AM_PEAK_MASK; 222d7aab0bfSHans de Goede tea->write_reg |= 1LL << TEA5777_W_AM_PEAK_SHIFT; 223d7aab0bfSHans de Goede tea->write_reg &= ~TEA5777_W_AM_CALLIGN_MASK; 224d7aab0bfSHans de Goede break; 225d7aab0bfSHans de Goede } 2264faba767SHans de Goede 2274faba767SHans de Goede res = tea->ops->write_reg(tea, tea->write_reg); 2284faba767SHans de Goede if (res) 2294faba767SHans de Goede return res; 2304faba767SHans de Goede 2314faba767SHans de Goede tea->needs_write = false; 2324faba767SHans de Goede tea->read_reg = -1; 2334faba767SHans de Goede tea->freq = tea5777_freq_to_v4l2_freq(tea, freq); 2344faba767SHans de Goede 2354faba767SHans de Goede return 0; 2364faba767SHans de Goede } 2374faba767SHans de Goede 2384faba767SHans de Goede static int radio_tea5777_update_read_reg(struct radio_tea5777 *tea, int wait) 2394faba767SHans de Goede { 2404faba767SHans de Goede int res; 2414faba767SHans de Goede 2424faba767SHans de Goede if (tea->read_reg != -1) 2434faba767SHans de Goede return 0; 2444faba767SHans de Goede 2454faba767SHans de Goede if (tea->write_before_read && tea->needs_write) { 2464faba767SHans de Goede res = radio_tea5777_set_freq(tea); 2474faba767SHans de Goede if (res) 2484faba767SHans de Goede return res; 2494faba767SHans de Goede } 2504faba767SHans de Goede 2514faba767SHans de Goede if (wait) { 2524faba767SHans de Goede if (schedule_timeout_interruptible(msecs_to_jiffies(wait))) 2534faba767SHans de Goede return -ERESTARTSYS; 2544faba767SHans de Goede } 2554faba767SHans de Goede 2564faba767SHans de Goede res = tea->ops->read_reg(tea, &tea->read_reg); 2574faba767SHans de Goede if (res) 2584faba767SHans de Goede return res; 2594faba767SHans de Goede 2604faba767SHans de Goede tea->needs_write = true; 2614faba767SHans de Goede return 0; 2624faba767SHans de Goede } 2634faba767SHans de Goede 2644faba767SHans de Goede /* 2654faba767SHans de Goede * Linux Video interface 2664faba767SHans de Goede */ 2674faba767SHans de Goede 2684faba767SHans de Goede static int vidioc_querycap(struct file *file, void *priv, 2694faba767SHans de Goede struct v4l2_capability *v) 2704faba767SHans de Goede { 2714faba767SHans de Goede struct radio_tea5777 *tea = video_drvdata(file); 2724faba767SHans de Goede 2734faba767SHans de Goede strlcpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver)); 2744faba767SHans de Goede strlcpy(v->card, tea->card, sizeof(v->card)); 2754faba767SHans de Goede strlcat(v->card, " TEA5777", sizeof(v->card)); 2764faba767SHans de Goede strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info)); 2774faba767SHans de Goede v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; 2784faba767SHans de Goede v->device_caps |= V4L2_CAP_HW_FREQ_SEEK; 2794faba767SHans de Goede v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; 2804faba767SHans de Goede return 0; 2814faba767SHans de Goede } 2824faba767SHans de Goede 283d7aab0bfSHans de Goede static int vidioc_enum_freq_bands(struct file *file, void *priv, 284d7aab0bfSHans de Goede struct v4l2_frequency_band *band) 285d7aab0bfSHans de Goede { 286d7aab0bfSHans de Goede struct radio_tea5777 *tea = video_drvdata(file); 287d7aab0bfSHans de Goede 288d7aab0bfSHans de Goede if (band->tuner != 0 || band->index >= ARRAY_SIZE(bands) || 289d7aab0bfSHans de Goede (!tea->has_am && band->index == BAND_AM)) 290d7aab0bfSHans de Goede return -EINVAL; 291d7aab0bfSHans de Goede 292d7aab0bfSHans de Goede *band = bands[band->index]; 293d7aab0bfSHans de Goede return 0; 294d7aab0bfSHans de Goede } 295d7aab0bfSHans de Goede 2964faba767SHans de Goede static int vidioc_g_tuner(struct file *file, void *priv, 2974faba767SHans de Goede struct v4l2_tuner *v) 2984faba767SHans de Goede { 2994faba767SHans de Goede struct radio_tea5777 *tea = video_drvdata(file); 3004faba767SHans de Goede int res; 3014faba767SHans de Goede 3024faba767SHans de Goede if (v->index > 0) 3034faba767SHans de Goede return -EINVAL; 3044faba767SHans de Goede 3054faba767SHans de Goede res = radio_tea5777_update_read_reg(tea, 0); 3064faba767SHans de Goede if (res) 3074faba767SHans de Goede return res; 3084faba767SHans de Goede 3094faba767SHans de Goede memset(v, 0, sizeof(*v)); 3104faba767SHans de Goede if (tea->has_am) 3114faba767SHans de Goede strlcpy(v->name, "AM/FM", sizeof(v->name)); 3124faba767SHans de Goede else 3134faba767SHans de Goede strlcpy(v->name, "FM", sizeof(v->name)); 3144faba767SHans de Goede v->type = V4L2_TUNER_RADIO; 3154faba767SHans de Goede v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | 316d7aab0bfSHans de Goede V4L2_TUNER_CAP_FREQ_BANDS | 317d7aab0bfSHans de Goede V4L2_TUNER_CAP_HWSEEK_BOUNDED | 318d7aab0bfSHans de Goede V4L2_TUNER_CAP_HWSEEK_PROG_LIM; 319d7aab0bfSHans de Goede v->rangelow = tea->has_am ? bands[BAND_AM].rangelow : 320d7aab0bfSHans de Goede bands[BAND_FM].rangelow; 321d7aab0bfSHans de Goede v->rangehigh = bands[BAND_FM].rangehigh; 322d7aab0bfSHans de Goede if (tea->band == BAND_FM && 323d7aab0bfSHans de Goede (tea->read_reg & TEA5777_R_FM_STEREO_MASK)) 324d7aab0bfSHans de Goede v->rxsubchans = V4L2_TUNER_SUB_STEREO; 325d7aab0bfSHans de Goede else 326d7aab0bfSHans de Goede v->rxsubchans = V4L2_TUNER_SUB_MONO; 327d7aab0bfSHans de Goede v->audmode = tea->audmode; 3284faba767SHans de Goede /* shift - 12 to convert 4-bits (0-15) scale to 16-bits (0-65535) */ 3294faba767SHans de Goede v->signal = (tea->read_reg & TEA5777_R_LEVEL_MASK) >> 3304faba767SHans de Goede (TEA5777_R_LEVEL_SHIFT - 12); 3314faba767SHans de Goede 3324faba767SHans de Goede /* Invalidate read_reg, so that next call we return up2date signal */ 3334faba767SHans de Goede tea->read_reg = -1; 3344faba767SHans de Goede 3354faba767SHans de Goede return 0; 3364faba767SHans de Goede } 3374faba767SHans de Goede 3384faba767SHans de Goede static int vidioc_s_tuner(struct file *file, void *priv, 3394faba767SHans de Goede struct v4l2_tuner *v) 3404faba767SHans de Goede { 3414faba767SHans de Goede struct radio_tea5777 *tea = video_drvdata(file); 342d7aab0bfSHans de Goede u32 orig_audmode = tea->audmode; 3434faba767SHans de Goede 3444faba767SHans de Goede if (v->index) 3454faba767SHans de Goede return -EINVAL; 3464faba767SHans de Goede 347d7aab0bfSHans de Goede if (v->audmode > V4L2_TUNER_MODE_STEREO) 348d7aab0bfSHans de Goede v->audmode = V4L2_TUNER_MODE_STEREO; 3494faba767SHans de Goede 350d7aab0bfSHans de Goede tea->audmode = v->audmode; 351d7aab0bfSHans de Goede 352d7aab0bfSHans de Goede if (tea->audmode != orig_audmode && tea->band == BAND_FM) 3534faba767SHans de Goede return radio_tea5777_set_freq(tea); 354d7aab0bfSHans de Goede 355d7aab0bfSHans de Goede return 0; 3564faba767SHans de Goede } 3574faba767SHans de Goede 3584faba767SHans de Goede static int vidioc_g_frequency(struct file *file, void *priv, 3594faba767SHans de Goede struct v4l2_frequency *f) 3604faba767SHans de Goede { 3614faba767SHans de Goede struct radio_tea5777 *tea = video_drvdata(file); 3624faba767SHans de Goede 3634faba767SHans de Goede if (f->tuner != 0) 3644faba767SHans de Goede return -EINVAL; 3654faba767SHans de Goede f->type = V4L2_TUNER_RADIO; 3664faba767SHans de Goede f->frequency = tea->freq; 3674faba767SHans de Goede return 0; 3684faba767SHans de Goede } 3694faba767SHans de Goede 3704faba767SHans de Goede static int vidioc_s_frequency(struct file *file, void *priv, 3714faba767SHans de Goede struct v4l2_frequency *f) 3724faba767SHans de Goede { 3734faba767SHans de Goede struct radio_tea5777 *tea = video_drvdata(file); 3744faba767SHans de Goede 3754faba767SHans de Goede if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) 3764faba767SHans de Goede return -EINVAL; 3774faba767SHans de Goede 378d7aab0bfSHans de Goede if (tea->has_am && f->frequency < (20000 * 16)) 379d7aab0bfSHans de Goede tea->band = BAND_AM; 380d7aab0bfSHans de Goede else 381d7aab0bfSHans de Goede tea->band = BAND_FM; 382d7aab0bfSHans de Goede 3834faba767SHans de Goede tea->freq = f->frequency; 3844faba767SHans de Goede return radio_tea5777_set_freq(tea); 3854faba767SHans de Goede } 3864faba767SHans de Goede 3874faba767SHans de Goede static int vidioc_s_hw_freq_seek(struct file *file, void *fh, 388ec6f4328SHans Verkuil const struct v4l2_hw_freq_seek *a) 3894faba767SHans de Goede { 3904faba767SHans de Goede struct radio_tea5777 *tea = video_drvdata(file); 3914faba767SHans de Goede unsigned long timeout; 392ec6f4328SHans Verkuil u32 rangelow = a->rangelow; 393ec6f4328SHans Verkuil u32 rangehigh = a->rangehigh; 394d7aab0bfSHans de Goede int i, res, spacing; 395d7aab0bfSHans de Goede u32 orig_freq; 3964faba767SHans de Goede 3974faba767SHans de Goede if (a->tuner || a->wrap_around) 3984faba767SHans de Goede return -EINVAL; 3994faba767SHans de Goede 400ec6f4328SHans Verkuil if (rangelow || rangehigh) { 401d7aab0bfSHans de Goede for (i = 0; i < ARRAY_SIZE(bands); i++) { 402d7aab0bfSHans de Goede if (i == BAND_AM && !tea->has_am) 403d7aab0bfSHans de Goede continue; 404ec6f4328SHans Verkuil if (bands[i].rangelow >= rangelow && 405ec6f4328SHans Verkuil bands[i].rangehigh <= rangehigh) 406d7aab0bfSHans de Goede break; 4074faba767SHans de Goede } 408d7aab0bfSHans de Goede if (i == ARRAY_SIZE(bands)) 409d7aab0bfSHans de Goede return -EINVAL; /* No matching band found */ 410d7aab0bfSHans de Goede 411d7aab0bfSHans de Goede tea->band = i; 412ec6f4328SHans Verkuil if (tea->freq < rangelow || tea->freq > rangehigh) { 413ec6f4328SHans Verkuil tea->freq = clamp(tea->freq, rangelow, 414ec6f4328SHans Verkuil rangehigh); 415d7aab0bfSHans de Goede res = radio_tea5777_set_freq(tea); 416d7aab0bfSHans de Goede if (res) 417d7aab0bfSHans de Goede return res; 418d7aab0bfSHans de Goede } 419d7aab0bfSHans de Goede } else { 420ec6f4328SHans Verkuil rangelow = bands[tea->band].rangelow; 421ec6f4328SHans Verkuil rangehigh = bands[tea->band].rangehigh; 422d7aab0bfSHans de Goede } 423d7aab0bfSHans de Goede 424d7aab0bfSHans de Goede spacing = (tea->band == BAND_AM) ? (5 * 16) : (200 * 16); /* kHz */ 425d7aab0bfSHans de Goede orig_freq = tea->freq; 426d7aab0bfSHans de Goede 427d7aab0bfSHans de Goede tea->write_reg |= TEA5777_W_PROGBLIM_MASK; 428ec6f4328SHans Verkuil if (tea->seek_rangelow != rangelow) { 429d7aab0bfSHans de Goede tea->write_reg &= ~TEA5777_W_UPDWN_MASK; 430ec6f4328SHans Verkuil tea->freq = rangelow; 4314faba767SHans de Goede res = radio_tea5777_set_freq(tea); 4324faba767SHans de Goede if (res) 4334faba767SHans de Goede goto leave; 434ec6f4328SHans Verkuil tea->seek_rangelow = rangelow; 435d7aab0bfSHans de Goede } 436ec6f4328SHans Verkuil if (tea->seek_rangehigh != rangehigh) { 437d7aab0bfSHans de Goede tea->write_reg |= TEA5777_W_UPDWN_MASK; 438ec6f4328SHans Verkuil tea->freq = rangehigh; 439d7aab0bfSHans de Goede res = radio_tea5777_set_freq(tea); 440d7aab0bfSHans de Goede if (res) 441d7aab0bfSHans de Goede goto leave; 442ec6f4328SHans Verkuil tea->seek_rangehigh = rangehigh; 4434faba767SHans de Goede } 4444faba767SHans de Goede tea->write_reg &= ~TEA5777_W_PROGBLIM_MASK; 4454faba767SHans de Goede 4464faba767SHans de Goede tea->write_reg |= TEA5777_W_SEARCH_MASK; 4474faba767SHans de Goede if (a->seek_upward) { 4484faba767SHans de Goede tea->write_reg |= TEA5777_W_UPDWN_MASK; 4494faba767SHans de Goede tea->freq = orig_freq + spacing; 4504faba767SHans de Goede } else { 4514faba767SHans de Goede tea->write_reg &= ~TEA5777_W_UPDWN_MASK; 4524faba767SHans de Goede tea->freq = orig_freq - spacing; 4534faba767SHans de Goede } 4544faba767SHans de Goede res = radio_tea5777_set_freq(tea); 4554faba767SHans de Goede if (res) 4564faba767SHans de Goede goto leave; 4574faba767SHans de Goede 4584faba767SHans de Goede timeout = jiffies + msecs_to_jiffies(5000); 4594faba767SHans de Goede for (;;) { 4604faba767SHans de Goede if (time_after(jiffies, timeout)) { 4614faba767SHans de Goede res = -ENODATA; 4624faba767SHans de Goede break; 4634faba767SHans de Goede } 4644faba767SHans de Goede 4654faba767SHans de Goede res = radio_tea5777_update_read_reg(tea, 100); 4664faba767SHans de Goede if (res) 4674faba767SHans de Goede break; 4684faba767SHans de Goede 4694faba767SHans de Goede /* 4704faba767SHans de Goede * Note we use tea->freq to track how far we've searched sofar 4714faba767SHans de Goede * this is necessary to ensure we continue seeking at the right 4724faba767SHans de Goede * point, in the write_before_read case. 4734faba767SHans de Goede */ 4744faba767SHans de Goede tea->freq = (tea->read_reg & TEA5777_R_FM_PLL_MASK); 4754faba767SHans de Goede tea->freq = tea5777_freq_to_v4l2_freq(tea, tea->freq); 4764faba767SHans de Goede 4774faba767SHans de Goede if ((tea->read_reg & TEA5777_R_SFOUND_MASK)) { 4784faba767SHans de Goede tea->write_reg &= ~TEA5777_W_SEARCH_MASK; 4794faba767SHans de Goede return 0; 4804faba767SHans de Goede } 4814faba767SHans de Goede 4824faba767SHans de Goede if (tea->read_reg & TEA5777_R_BLIM_MASK) { 4834faba767SHans de Goede res = -ENODATA; 4844faba767SHans de Goede break; 4854faba767SHans de Goede } 4864faba767SHans de Goede 4874faba767SHans de Goede /* Force read_reg update */ 4884faba767SHans de Goede tea->read_reg = -1; 4894faba767SHans de Goede } 4904faba767SHans de Goede leave: 4914faba767SHans de Goede tea->write_reg &= ~TEA5777_W_PROGBLIM_MASK; 4924faba767SHans de Goede tea->write_reg &= ~TEA5777_W_SEARCH_MASK; 4934faba767SHans de Goede tea->freq = orig_freq; 4944faba767SHans de Goede radio_tea5777_set_freq(tea); 4954faba767SHans de Goede return res; 4964faba767SHans de Goede } 4974faba767SHans de Goede 4984faba767SHans de Goede static int tea575x_s_ctrl(struct v4l2_ctrl *c) 4994faba767SHans de Goede { 5004faba767SHans de Goede struct radio_tea5777 *tea = 5014faba767SHans de Goede container_of(c->handler, struct radio_tea5777, ctrl_handler); 5024faba767SHans de Goede 5034faba767SHans de Goede switch (c->id) { 5044faba767SHans de Goede case V4L2_CID_AUDIO_MUTE: 5054faba767SHans de Goede if (c->val) 5064faba767SHans de Goede tea->write_reg |= TEA5777_W_MUTE_MASK; 5074faba767SHans de Goede else 5084faba767SHans de Goede tea->write_reg &= ~TEA5777_W_MUTE_MASK; 5094faba767SHans de Goede 5104faba767SHans de Goede return radio_tea5777_set_freq(tea); 5114faba767SHans de Goede } 5124faba767SHans de Goede 5134faba767SHans de Goede return -EINVAL; 5144faba767SHans de Goede } 5154faba767SHans de Goede 5164faba767SHans de Goede static const struct v4l2_file_operations tea575x_fops = { 5174faba767SHans de Goede .unlocked_ioctl = video_ioctl2, 5184faba767SHans de Goede .open = v4l2_fh_open, 5194faba767SHans de Goede .release = v4l2_fh_release, 5204faba767SHans de Goede .poll = v4l2_ctrl_poll, 5214faba767SHans de Goede }; 5224faba767SHans de Goede 5234faba767SHans de Goede static const struct v4l2_ioctl_ops tea575x_ioctl_ops = { 5244faba767SHans de Goede .vidioc_querycap = vidioc_querycap, 5254faba767SHans de Goede .vidioc_g_tuner = vidioc_g_tuner, 5264faba767SHans de Goede .vidioc_s_tuner = vidioc_s_tuner, 5274faba767SHans de Goede .vidioc_g_frequency = vidioc_g_frequency, 5284faba767SHans de Goede .vidioc_s_frequency = vidioc_s_frequency, 5294faba767SHans de Goede .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek, 530d7aab0bfSHans de Goede .vidioc_enum_freq_bands = vidioc_enum_freq_bands, 5314faba767SHans de Goede .vidioc_log_status = v4l2_ctrl_log_status, 5324faba767SHans de Goede .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 5334faba767SHans de Goede .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 5344faba767SHans de Goede }; 5354faba767SHans de Goede 5364faba767SHans de Goede static const struct video_device tea575x_radio = { 5374faba767SHans de Goede .ioctl_ops = &tea575x_ioctl_ops, 5384faba767SHans de Goede .release = video_device_release_empty, 5394faba767SHans de Goede }; 5404faba767SHans de Goede 5414faba767SHans de Goede static const struct v4l2_ctrl_ops tea575x_ctrl_ops = { 5424faba767SHans de Goede .s_ctrl = tea575x_s_ctrl, 5434faba767SHans de Goede }; 5444faba767SHans de Goede 5454faba767SHans de Goede int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner) 5464faba767SHans de Goede { 5474faba767SHans de Goede int res; 5484faba767SHans de Goede 5494faba767SHans de Goede tea->write_reg = (1LL << TEA5777_W_IFCE_SHIFT) | 5504faba767SHans de Goede (1LL << TEA5777_W_IFW_SHIFT) | 5514faba767SHans de Goede (1LL << TEA5777_W_INTEXT_SHIFT) | 5524faba767SHans de Goede (1LL << TEA5777_W_CHP0_SHIFT) | 553d7aab0bfSHans de Goede (1LL << TEA5777_W_SLEV_SHIFT); 5544faba767SHans de Goede tea->freq = 90500 * 16; /* 90.5Mhz default */ 555d7aab0bfSHans de Goede tea->audmode = V4L2_TUNER_MODE_STEREO; 5564faba767SHans de Goede res = radio_tea5777_set_freq(tea); 5574faba767SHans de Goede if (res) { 5584faba767SHans de Goede v4l2_err(tea->v4l2_dev, "can't set initial freq (%d)\n", res); 5594faba767SHans de Goede return res; 5604faba767SHans de Goede } 5614faba767SHans de Goede 5624faba767SHans de Goede tea->vd = tea575x_radio; 5634faba767SHans de Goede video_set_drvdata(&tea->vd, tea); 5644faba767SHans de Goede mutex_init(&tea->mutex); 5654faba767SHans de Goede strlcpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name)); 5664faba767SHans de Goede tea->vd.lock = &tea->mutex; 5674faba767SHans de Goede tea->vd.v4l2_dev = tea->v4l2_dev; 5684faba767SHans de Goede tea->fops = tea575x_fops; 5694faba767SHans de Goede tea->fops.owner = owner; 5704faba767SHans de Goede tea->vd.fops = &tea->fops; 5714faba767SHans de Goede set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags); 5724faba767SHans de Goede 5734faba767SHans de Goede tea->vd.ctrl_handler = &tea->ctrl_handler; 5744faba767SHans de Goede v4l2_ctrl_handler_init(&tea->ctrl_handler, 1); 5754faba767SHans de Goede v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, 5764faba767SHans de Goede V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); 5774faba767SHans de Goede res = tea->ctrl_handler.error; 5784faba767SHans de Goede if (res) { 5794faba767SHans de Goede v4l2_err(tea->v4l2_dev, "can't initialize controls\n"); 5804faba767SHans de Goede v4l2_ctrl_handler_free(&tea->ctrl_handler); 5814faba767SHans de Goede return res; 5824faba767SHans de Goede } 5834faba767SHans de Goede v4l2_ctrl_handler_setup(&tea->ctrl_handler); 5844faba767SHans de Goede 5854faba767SHans de Goede res = video_register_device(&tea->vd, VFL_TYPE_RADIO, -1); 5864faba767SHans de Goede if (res) { 5874faba767SHans de Goede v4l2_err(tea->v4l2_dev, "can't register video device!\n"); 5884faba767SHans de Goede v4l2_ctrl_handler_free(tea->vd.ctrl_handler); 5894faba767SHans de Goede return res; 5904faba767SHans de Goede } 5914faba767SHans de Goede 5924faba767SHans de Goede return 0; 5934faba767SHans de Goede } 5944faba767SHans de Goede EXPORT_SYMBOL_GPL(radio_tea5777_init); 5954faba767SHans de Goede 5964faba767SHans de Goede void radio_tea5777_exit(struct radio_tea5777 *tea) 5974faba767SHans de Goede { 5984faba767SHans de Goede video_unregister_device(&tea->vd); 5994faba767SHans de Goede v4l2_ctrl_handler_free(tea->vd.ctrl_handler); 6004faba767SHans de Goede } 6014faba767SHans de Goede EXPORT_SYMBOL_GPL(radio_tea5777_exit); 602