11553Srgrimes// SPDX-License-Identifier: GPL-2.0-only 21553Srgrimes/* 31553Srgrimes * drivers/media/radio/radio-si476x.c -- V4L2 driver for SI476X chips 41553Srgrimes * 51553Srgrimes * Copyright (C) 2012 Innovative Converged Devices(ICD) 61553Srgrimes * Copyright (C) 2013 Andrey Smirnov 71553Srgrimes * 81553Srgrimes * Author: Andrey Smirnov <andrew.smirnov@gmail.com> 91553Srgrimes */ 101553Srgrimes 111553Srgrimes#include <linux/module.h> 121553Srgrimes#include <linux/delay.h> 131553Srgrimes#include <linux/interrupt.h> 141553Srgrimes#include <linux/slab.h> 151553Srgrimes#include <linux/atomic.h> 161553Srgrimes#include <linux/videodev2.h> 171553Srgrimes#include <linux/mutex.h> 181553Srgrimes#include <linux/debugfs.h> 191553Srgrimes#include <media/v4l2-common.h> 201553Srgrimes#include <media/v4l2-ioctl.h> 211553Srgrimes#include <media/v4l2-ctrls.h> 221553Srgrimes#include <media/v4l2-event.h> 231553Srgrimes#include <media/v4l2-device.h> 241553Srgrimes 251553Srgrimes#include <media/drv-intf/si476x.h> 261553Srgrimes#include <linux/mfd/si476x-core.h> 271553Srgrimes 281553Srgrimes#define FM_FREQ_RANGE_LOW 64000000 291553Srgrimes#define FM_FREQ_RANGE_HIGH 108000000 301553Srgrimes 311553Srgrimes#define AM_FREQ_RANGE_LOW 520000 321553Srgrimes#define AM_FREQ_RANGE_HIGH 30000000 331553Srgrimes 341553Srgrimes#define PWRLINEFLTR (1 << 8) 3529602Scharnier 361553Srgrimes#define FREQ_MUL (10000000 / 625) 371553Srgrimes 381553Srgrimes#define SI476X_PHDIV_STATUS_LINK_LOCKED(status) (0x80 & (status)) 391553Srgrimes 401553Srgrimes#define DRIVER_NAME "si476x-radio" 4129602Scharnier#define DRIVER_CARD "SI476x AM/FM Receiver" 4229602Scharnier 4329602Scharnierenum si476x_freq_bands { 4429602Scharnier SI476X_BAND_FM, 4545588Smarkm SI476X_BAND_AM, 461553Srgrimes}; 471553Srgrimes 481553Srgrimesstatic const struct v4l2_frequency_band si476x_bands[] = { 491553Srgrimes [SI476X_BAND_FM] = { 501553Srgrimes .type = V4L2_TUNER_RADIO, 511553Srgrimes .index = SI476X_BAND_FM, 521553Srgrimes .capability = V4L2_TUNER_CAP_LOW 531553Srgrimes | V4L2_TUNER_CAP_STEREO 541553Srgrimes | V4L2_TUNER_CAP_RDS 551553Srgrimes | V4L2_TUNER_CAP_RDS_BLOCK_IO 561553Srgrimes | V4L2_TUNER_CAP_FREQ_BANDS, 571553Srgrimes .rangelow = 64 * FREQ_MUL, 581553Srgrimes .rangehigh = 108 * FREQ_MUL, 591553Srgrimes .modulation = V4L2_BAND_MODULATION_FM, 601553Srgrimes }, 611553Srgrimes [SI476X_BAND_AM] = { 621553Srgrimes .type = V4L2_TUNER_RADIO, 638857Srgrimes .index = SI476X_BAND_AM, 641553Srgrimes .capability = V4L2_TUNER_CAP_LOW 651553Srgrimes | V4L2_TUNER_CAP_FREQ_BANDS, 661553Srgrimes .rangelow = 0.52 * FREQ_MUL, 671553Srgrimes .rangehigh = 30 * FREQ_MUL, 681553Srgrimes .modulation = V4L2_BAND_MODULATION_AM, 691553Srgrimes }, 701553Srgrimes}; 711553Srgrimes 721553Srgrimesstatic inline bool si476x_radio_freq_is_inside_of_the_band(u32 freq, int band) 731553Srgrimes{ 741553Srgrimes return freq >= si476x_bands[band].rangelow && 751553Srgrimes freq <= si476x_bands[band].rangehigh; 761553Srgrimes} 771553Srgrimes 781553Srgrimesstatic inline bool si476x_radio_range_is_inside_of_the_band(u32 low, u32 high, 791553Srgrimes int band) 801553Srgrimes{ 811553Srgrimes return low >= si476x_bands[band].rangelow && 821553Srgrimes high <= si476x_bands[band].rangehigh; 831553Srgrimes} 841553Srgrimes 851553Srgrimesstatic int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl); 861553Srgrimesstatic int si476x_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl); 871553Srgrimes 881553Srgrimesenum phase_diversity_modes_idx { 891553Srgrimes SI476X_IDX_PHDIV_DISABLED, 901553Srgrimes SI476X_IDX_PHDIV_PRIMARY_COMBINING, 911553Srgrimes SI476X_IDX_PHDIV_PRIMARY_ANTENNA, 921553Srgrimes SI476X_IDX_PHDIV_SECONDARY_ANTENNA, 931553Srgrimes SI476X_IDX_PHDIV_SECONDARY_COMBINING, 941553Srgrimes}; 952657Scsgr 962657Scsgrstatic const char * const phase_diversity_modes[] = { 972657Scsgr [SI476X_IDX_PHDIV_DISABLED] = "Disabled", 982657Scsgr [SI476X_IDX_PHDIV_PRIMARY_COMBINING] = "Primary with Secondary", 992657Scsgr [SI476X_IDX_PHDIV_PRIMARY_ANTENNA] = "Primary Antenna", 1002657Scsgr [SI476X_IDX_PHDIV_SECONDARY_ANTENNA] = "Secondary Antenna", 1012657Scsgr [SI476X_IDX_PHDIV_SECONDARY_COMBINING] = "Secondary with Primary", 1022657Scsgr}; 1032657Scsgr 1041553Srgrimesstatic inline enum phase_diversity_modes_idx 1051553Srgrimessi476x_phase_diversity_mode_to_idx(enum si476x_phase_diversity_mode mode) 1061553Srgrimes{ 1071553Srgrimes switch (mode) { 1081553Srgrimes default: 1091553Srgrimes fallthrough; 1101553Srgrimes case SI476X_PHDIV_DISABLED: 1111553Srgrimes return SI476X_IDX_PHDIV_DISABLED; 1121553Srgrimes case SI476X_PHDIV_PRIMARY_COMBINING: 1131553Srgrimes return SI476X_IDX_PHDIV_PRIMARY_COMBINING; 1141553Srgrimes case SI476X_PHDIV_PRIMARY_ANTENNA: 11536042Sguido return SI476X_IDX_PHDIV_PRIMARY_ANTENNA; 1161553Srgrimes case SI476X_PHDIV_SECONDARY_ANTENNA: 1172657Scsgr return SI476X_IDX_PHDIV_SECONDARY_ANTENNA; 11819617Sjulian case SI476X_PHDIV_SECONDARY_COMBINING: 1191553Srgrimes return SI476X_IDX_PHDIV_SECONDARY_COMBINING; 1201553Srgrimes } 12129602Scharnier} 1221553Srgrimes 12330807Sachestatic inline enum si476x_phase_diversity_mode 1241553Srgrimessi476x_phase_diversity_idx_to_mode(enum phase_diversity_modes_idx idx) 1251553Srgrimes{ 1261553Srgrimes static const int idx_to_value[] = { 1271553Srgrimes [SI476X_IDX_PHDIV_DISABLED] = SI476X_PHDIV_DISABLED, 1281553Srgrimes [SI476X_IDX_PHDIV_PRIMARY_COMBINING] = SI476X_PHDIV_PRIMARY_COMBINING, 1291553Srgrimes [SI476X_IDX_PHDIV_PRIMARY_ANTENNA] = SI476X_PHDIV_PRIMARY_ANTENNA, 1301553Srgrimes [SI476X_IDX_PHDIV_SECONDARY_ANTENNA] = SI476X_PHDIV_SECONDARY_ANTENNA, 1311553Srgrimes [SI476X_IDX_PHDIV_SECONDARY_COMBINING] = SI476X_PHDIV_SECONDARY_COMBINING, 13213142Speter }; 13319617Sjulian 1341553Srgrimes return idx_to_value[idx]; 13545089Smarkm} 13645089Smarkm 13745089Smarkmstatic const struct v4l2_ctrl_ops si476x_ctrl_ops = { 13845089Smarkm .g_volatile_ctrl = si476x_radio_g_volatile_ctrl, 13945089Smarkm .s_ctrl = si476x_radio_s_ctrl, 14045089Smarkm}; 14145089Smarkm 14245089Smarkm 14345089Smarkmenum si476x_ctrl_idx { 14445089Smarkm SI476X_IDX_RSSI_THRESHOLD, 14545089Smarkm SI476X_IDX_SNR_THRESHOLD, 14645089Smarkm SI476X_IDX_MAX_TUNE_ERROR, 14745089Smarkm SI476X_IDX_HARMONICS_COUNT, 14845089Smarkm SI476X_IDX_DIVERSITY_MODE, 14945089Smarkm SI476X_IDX_INTERCHIP_LINK, 15045089Smarkm}; 15145089Smarkmstatic struct v4l2_ctrl_config si476x_ctrls[] = { 15245089Smarkm 15321640Speter /* 15421640Speter * SI476X during its station seeking(or tuning) process uses several 15530792Sache * parameters to determine if "the station" is valid: 15630792Sache * 15730792Sache * - Signal's SNR(in dBuV) must be lower than 15830792Sache * #V4L2_CID_SI476X_SNR_THRESHOLD 15921640Speter * - Signal's RSSI(in dBuV) must be greater than 16021640Speter * #V4L2_CID_SI476X_RSSI_THRESHOLD 1611553Srgrimes * - Signal's frequency deviation(in units of 2ppm) must not be 1621553Srgrimes * more than #V4L2_CID_SI476X_MAX_TUNE_ERROR 16333794Spst */ 16433794Spst [SI476X_IDX_RSSI_THRESHOLD] = { 16533794Spst .ops = &si476x_ctrl_ops, 16633794Spst .id = V4L2_CID_SI476X_RSSI_THRESHOLD, 16733794Spst .name = "Valid RSSI Threshold", 16833794Spst .type = V4L2_CTRL_TYPE_INTEGER, 16933794Spst .min = -128, 17033794Spst .max = 127, 17133794Spst .step = 1, 17233794Spst }, 17333794Spst [SI476X_IDX_SNR_THRESHOLD] = { 1742659Scsgr .ops = &si476x_ctrl_ops, 1751553Srgrimes .id = V4L2_CID_SI476X_SNR_THRESHOLD, 1761553Srgrimes .type = V4L2_CTRL_TYPE_INTEGER, 17719618Sjulian .name = "Valid SNR Threshold", 1781553Srgrimes .min = -128, 1791553Srgrimes .max = 127, 1801553Srgrimes .step = 1, 1811553Srgrimes }, 1822659Scsgr [SI476X_IDX_MAX_TUNE_ERROR] = { 1831553Srgrimes .ops = &si476x_ctrl_ops, 1841553Srgrimes .id = V4L2_CID_SI476X_MAX_TUNE_ERROR, 1851553Srgrimes .type = V4L2_CTRL_TYPE_INTEGER, 1861553Srgrimes .name = "Max Tune Errors", 1871553Srgrimes .min = 0, 18833794Spst .max = 126 * 2, 18933794Spst .step = 2, 1901553Srgrimes }, 1912657Scsgr 19217482Sjulian /* 19342122Sdes * #V4L2_CID_SI476X_HARMONICS_COUNT -- number of harmonics 1941553Srgrimes * built-in power-line noise supression filter is to reject 1951553Srgrimes * during AM-mode operation. 1961553Srgrimes */ 1971553Srgrimes [SI476X_IDX_HARMONICS_COUNT] = { 1981553Srgrimes .ops = &si476x_ctrl_ops, 19930847Sdima .id = V4L2_CID_SI476X_HARMONICS_COUNT, 20030847Sdima .type = V4L2_CTRL_TYPE_INTEGER, 20130847Sdima 20219618Sjulian .name = "Count of Harmonics to Reject", 2031553Srgrimes .min = 0, 20430807Sache .max = 20, 20530792Sache .step = 1, 20630792Sache }, 20730792Sache 2081553Srgrimes /* 2091553Srgrimes * #V4L2_CID_SI476X_DIVERSITY_MODE -- configuration which 21045588Smarkm * two tuners working in diversity mode are to work in. 2111553Srgrimes * 2121553Srgrimes * - #SI476X_IDX_PHDIV_DISABLED diversity mode disabled 2131553Srgrimes * - #SI476X_IDX_PHDIV_PRIMARY_COMBINING diversity mode is 2141553Srgrimes * on, primary tuner's antenna is the main one. 21519618Sjulian * - #SI476X_IDX_PHDIV_PRIMARY_ANTENNA diversity mode is 21619618Sjulian * off, primary tuner's antenna is the main one. 21719618Sjulian * - #SI476X_IDX_PHDIV_SECONDARY_ANTENNA diversity mode is 21819618Sjulian * off, secondary tuner's antenna is the main one. 2192657Scsgr * - #SI476X_IDX_PHDIV_SECONDARY_COMBINING diversity mode is 2202657Scsgr * on, secondary tuner's antenna is the main one. 2212657Scsgr */ 2221553Srgrimes [SI476X_IDX_DIVERSITY_MODE] = { 2231553Srgrimes .ops = &si476x_ctrl_ops, 2241553Srgrimes .id = V4L2_CID_SI476X_DIVERSITY_MODE, 2251553Srgrimes .type = V4L2_CTRL_TYPE_MENU, 2261553Srgrimes .name = "Phase Diversity Mode", 2271553Srgrimes .qmenu = phase_diversity_modes, 2281553Srgrimes .min = 0, 2291553Srgrimes .max = ARRAY_SIZE(phase_diversity_modes) - 1, 23036042Sguido }, 2311553Srgrimes 2321553Srgrimes /* 2331553Srgrimes * #V4L2_CID_SI476X_INTERCHIP_LINK -- inter-chip link in 23436042Sguido * diversity mode indicator. Allows user to determine if two 2351553Srgrimes * chips working in diversity mode have established a link 2361553Srgrimes * between each other and if the system as a whole uses 2371553Srgrimes * signals from both antennas to receive FM radio. 2381553Srgrimes */ 2391553Srgrimes [SI476X_IDX_INTERCHIP_LINK] = { 24042122Sdes .ops = &si476x_ctrl_ops, 24142122Sdes .id = V4L2_CID_SI476X_INTERCHIP_LINK, 24242122Sdes .type = V4L2_CTRL_TYPE_BOOLEAN, 2431553Srgrimes .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE, 2441553Srgrimes .name = "Inter-Chip Link", 2451553Srgrimes .min = 0, 2461553Srgrimes .max = 1, 2471553Srgrimes .step = 1, 2481553Srgrimes }, 2491553Srgrimes}; 2501553Srgrimes 2511553Srgrimesstruct si476x_radio; 2521553Srgrimes 25340910Sphk/** 2541553Srgrimes * struct si476x_radio_ops - vtable of tuner functions 2551553Srgrimes * 2561553Srgrimes * This table holds pointers to functions implementing particular 2571553Srgrimes * operations depending on the mode in which the tuner chip was 2581553Srgrimes * configured to start. If the function is not supported 25919618Sjulian * corresponding element is set to #NULL. 26042122Sdes * 26142122Sdes * @tune_freq: Tune chip to a specific frequency 26219618Sjulian * @seek_start: Star station seeking 26319618Sjulian * @rsq_status: Get Received Signal Quality(RSQ) status 26442122Sdes * @rds_blckcnt: Get received RDS blocks count 26542122Sdes * @phase_diversity: Change phase diversity mode of the tuner 2661553Srgrimes * @phase_div_status: Get phase diversity mode status 2671553Srgrimes * @acf_status: Get the status of Automatically Controlled 2681553Srgrimes * Features(ACF) 2691553Srgrimes * @agc_status: Get Automatic Gain Control(AGC) status 2701553Srgrimes */ 27130847Sdimastruct si476x_radio_ops { 2721553Srgrimes int (*tune_freq)(struct si476x_core *, struct si476x_tune_freq_args *); 2732657Scsgr int (*seek_start)(struct si476x_core *, bool, bool); 2742657Scsgr int (*rsq_status)(struct si476x_core *, struct si476x_rsq_status_args *, 2751553Srgrimes struct si476x_rsq_status_report *); 2761553Srgrimes int (*rds_blckcnt)(struct si476x_core *, bool, 2771553Srgrimes struct si476x_rds_blockcount_report *); 2781553Srgrimes 27930847Sdima int (*phase_diversity)(struct si476x_core *, 2801553Srgrimes enum si476x_phase_diversity_mode); 2811553Srgrimes int (*phase_div_status)(struct si476x_core *); 2821553Srgrimes int (*acf_status)(struct si476x_core *, 2831553Srgrimes struct si476x_acf_status_report *); 2841553Srgrimes int (*agc_status)(struct si476x_core *, 2851553Srgrimes struct si476x_agc_status_report *); 2861553Srgrimes}; 2871553Srgrimes 2881553Srgrimes/** 2891553Srgrimes * struct si476x_radio - radio device 29045089Smarkm * 2911553Srgrimes * @v4l2dev: Pointer to V4L2 device created by V4L2 subsystem 2921553Srgrimes * @videodev: Pointer to video device created by V4L2 subsystem 2931553Srgrimes * @ctrl_handler: V4L2 controls handler 2941553Srgrimes * @core: Pointer to underlying core device 2951553Srgrimes * @ops: Vtable of functions. See struct si476x_radio_ops for details 2961553Srgrimes * @debugfs: pointer to &strucd dentry for debugfs 2971553Srgrimes * @audmode: audio mode, as defined for the rxsubchans field 2981553Srgrimes * at videodev2.h 2991553Srgrimes * 3001553Srgrimes * core structure is the radio device is being used 3011553Srgrimes */ 3021553Srgrimesstruct si476x_radio { 3031553Srgrimes struct v4l2_device v4l2dev; 30440910Sphk struct video_device videodev; 30540910Sphk struct v4l2_ctrl_handler ctrl_handler; 3061553Srgrimes 3071553Srgrimes struct si476x_core *core; 3081553Srgrimes /* This field should not be accesses unless core lock is held */ 3091553Srgrimes const struct si476x_radio_ops *ops; 3101553Srgrimes 31117482Sjulian struct dentry *debugfs; 31213142Speter u32 audmode; 31313142Speter}; 3141553Srgrimes 3151553Srgrimesstatic inline struct si476x_radio * 31613142Speterv4l2_ctrl_handler_to_radio(struct v4l2_ctrl_handler *d) 3171553Srgrimes{ 3181553Srgrimes return container_of(d, struct si476x_radio, ctrl_handler); 31933794Spst} 32033794Spst 32133794Spst/* 32233794Spst * si476x_vidioc_querycap - query device capabilities 32333794Spst */ 32433794Spststatic int si476x_radio_querycap(struct file *file, void *priv, 32533794Spst struct v4l2_capability *capability) 32633794Spst{ 32733794Spst struct si476x_radio *radio = video_drvdata(file); 32833794Spst 32933794Spst strscpy(capability->driver, radio->v4l2dev.name, 33033794Spst sizeof(capability->driver)); 33133794Spst strscpy(capability->card, DRIVER_CARD, sizeof(capability->card)); 33233794Spst return 0; 33333794Spst} 33433794Spst 33533794Spststatic int si476x_radio_enum_freq_bands(struct file *file, void *priv, 3361553Srgrimes struct v4l2_frequency_band *band) 3371553Srgrimes{ 3381553Srgrimes int err; 3391553Srgrimes struct si476x_radio *radio = video_drvdata(file); 3401553Srgrimes 3411553Srgrimes if (band->tuner != 0) 34230807Sache return -EINVAL; 34335848Sguido 3441553Srgrimes switch (radio->core->chip_id) { 3451553Srgrimes /* AM/FM tuners -- all bands are supported */ 3461553Srgrimes case SI476X_CHIP_SI4761: 3472659Scsgr case SI476X_CHIP_SI4764: 3482659Scsgr if (band->index < ARRAY_SIZE(si476x_bands)) { 34921640Speter *band = si476x_bands[band->index]; 35021640Speter err = 0; 35121640Speter } else { 35245089Smarkm err = -EINVAL; 35345089Smarkm } 35445089Smarkm break; 35545089Smarkm /* FM companion tuner chips -- only FM bands are 35645089Smarkm * supported */ 3571553Srgrimes case SI476X_CHIP_SI4768: 35813142Speter if (band->index == SI476X_BAND_FM) { 35913142Speter *band = si476x_bands[band->index]; 3601553Srgrimes err = 0; 3611553Srgrimes } else { 3621553Srgrimes err = -EINVAL; 3631553Srgrimes } 3641553Srgrimes break; 3651553Srgrimes default: 36613142Speter err = -EINVAL; 3671553Srgrimes } 3681553Srgrimes 3691553Srgrimes return err; 37017482Sjulian} 37133794Spst 3721553Srgrimesstatic int si476x_radio_g_tuner(struct file *file, void *priv, 3731553Srgrimes struct v4l2_tuner *tuner) 3741553Srgrimes{ 3751553Srgrimes int err; 3761553Srgrimes struct si476x_rsq_status_report report; 3772659Scsgr struct si476x_radio *radio = video_drvdata(file); 3782659Scsgr 3792659Scsgr struct si476x_rsq_status_args args = { 38033794Spst .primary = false, 38133794Spst .rsqack = false, 38233794Spst .attune = false, 3831553Srgrimes .cancel = false, 38433794Spst .stcack = false, 38533794Spst }; 38633794Spst 38733794Spst if (tuner->index != 0) 38833794Spst return -EINVAL; 38933794Spst 39033794Spst tuner->type = V4L2_TUNER_RADIO; 39133794Spst tuner->capability = V4L2_TUNER_CAP_LOW /* Measure frequencies 39217482Sjulian * in multiples of 39317482Sjulian * 62.5 Hz */ 39417482Sjulian | V4L2_TUNER_CAP_STEREO 39517482Sjulian | V4L2_TUNER_CAP_HWSEEK_BOUNDED 39619617Sjulian | V4L2_TUNER_CAP_HWSEEK_WRAP 39717482Sjulian | V4L2_TUNER_CAP_HWSEEK_PROG_LIM; 39817482Sjulian 39917482Sjulian si476x_core_lock(radio->core); 40017482Sjulian 40117482Sjulian if (si476x_core_is_a_secondary_tuner(radio->core)) { 4021553Srgrimes strscpy(tuner->name, "FM (secondary)", sizeof(tuner->name)); 4031553Srgrimes tuner->rxsubchans = 0; 4041553Srgrimes tuner->rangelow = si476x_bands[SI476X_BAND_FM].rangelow; 40517482Sjulian } else if (si476x_core_has_am(radio->core)) { 40633794Spst if (si476x_core_is_a_primary_tuner(radio->core)) 40717482Sjulian strscpy(tuner->name, "AM/FM (primary)", 40819617Sjulian sizeof(tuner->name)); 4091553Srgrimes else 4101553Srgrimes strscpy(tuner->name, "AM/FM", sizeof(tuner->name)); 4111553Srgrimes 4121553Srgrimes tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO 4131553Srgrimes | V4L2_TUNER_SUB_RDS; 4141553Srgrimes tuner->capability |= V4L2_TUNER_CAP_RDS 4151553Srgrimes | V4L2_TUNER_CAP_RDS_BLOCK_IO 41611447Swollman | V4L2_TUNER_CAP_FREQ_BANDS; 41712024Speter 41812024Speter tuner->rangelow = si476x_bands[SI476X_BAND_AM].rangelow; 41912024Speter } else { 42012024Speter strscpy(tuner->name, "FM", sizeof(tuner->name)); 42112024Speter tuner->rxsubchans = V4L2_TUNER_SUB_RDS; 42212024Speter tuner->capability |= V4L2_TUNER_CAP_RDS 42312024Speter | V4L2_TUNER_CAP_RDS_BLOCK_IO 42412024Speter | V4L2_TUNER_CAP_FREQ_BANDS; 42512024Speter tuner->rangelow = si476x_bands[SI476X_BAND_FM].rangelow; 42612024Speter } 42712024Speter 42812024Speter tuner->audmode = radio->audmode; 42911447Swollman 43017482Sjulian tuner->afc = 1; 43111447Swollman tuner->rangehigh = si476x_bands[SI476X_BAND_FM].rangehigh; 43211447Swollman 43311447Swollman err = radio->ops->rsq_status(radio->core, 43411447Swollman &args, &report); 43517482Sjulian if (err < 0) { 43611447Swollman tuner->signal = 0; 4371553Srgrimes } else { 43835948Sbde /* 43935948Sbde * tuner->signal value range: 0x0000 .. 0xFFFF, 44035948Sbde * report.rssi: -128 .. 127 44135948Sbde */ 44235948Sbde tuner->signal = (report.rssi + 128) * 257; 44342122Sdes } 44435848Sguido si476x_core_unlock(radio->core); 44542122Sdes 44642122Sdes return err; 44735848Sguido} 44842122Sdes 44935848Sguidostatic int si476x_radio_s_tuner(struct file *file, void *priv, 45035848Sguido const struct v4l2_tuner *tuner) 45135948Sbde{ 4521553Srgrimes struct si476x_radio *radio = video_drvdata(file); 4531553Srgrimes 4541553Srgrimes if (tuner->index != 0) 4551553Srgrimes return -EINVAL; 4561553Srgrimes 4571553Srgrimes if (tuner->audmode == V4L2_TUNER_MODE_MONO || 45819298Salex tuner->audmode == V4L2_TUNER_MODE_STEREO) 4591553Srgrimes radio->audmode = tuner->audmode; 4601553Srgrimes else 4611553Srgrimes radio->audmode = V4L2_TUNER_MODE_STEREO; 4621553Srgrimes 46342250Sdes return 0; 46442250Sdes} 46542250Sdes 46642122Sdesstatic int si476x_radio_init_vtable(struct si476x_radio *radio, 46742122Sdes enum si476x_func func) 46842122Sdes{ 46941685Sdillon static const struct si476x_radio_ops fm_ops = { 4701553Srgrimes .tune_freq = si476x_core_cmd_fm_tune_freq, 4711553Srgrimes .seek_start = si476x_core_cmd_fm_seek_start, 4721553Srgrimes .rsq_status = si476x_core_cmd_fm_rsq_status, 4731553Srgrimes .rds_blckcnt = si476x_core_cmd_fm_rds_blockcount, 4741553Srgrimes .phase_diversity = si476x_core_cmd_fm_phase_diversity, 47542122Sdes .phase_div_status = si476x_core_cmd_fm_phase_div_status, 4761553Srgrimes .acf_status = si476x_core_cmd_fm_acf_status, 4771553Srgrimes .agc_status = si476x_core_cmd_agc_status, 47842122Sdes }; 4791553Srgrimes 4801553Srgrimes static const struct si476x_radio_ops am_ops = { 48142122Sdes .tune_freq = si476x_core_cmd_am_tune_freq, 48242122Sdes .seek_start = si476x_core_cmd_am_seek_start, 48342122Sdes .rsq_status = si476x_core_cmd_am_rsq_status, 4841553Srgrimes .rds_blckcnt = NULL, 48528907Simp .phase_diversity = NULL, 48628907Simp .phase_div_status = NULL, 4871553Srgrimes .acf_status = si476x_core_cmd_am_acf_status, 4881553Srgrimes .agc_status = NULL, 48942122Sdes }; 49042250Sdes 49142122Sdes switch (func) { 49242250Sdes case SI476X_FUNC_FM_RECEIVER: 49342122Sdes radio->ops = &fm_ops; 49442122Sdes return 0; 49542122Sdes 49642250Sdes case SI476X_FUNC_AM_RECEIVER: 49742250Sdes radio->ops = &am_ops; 49842250Sdes return 0; 49942250Sdes default: 50042250Sdes WARN(1, "Unexpected tuner function value\n"); 50142250Sdes return -EINVAL; 50242250Sdes } 50342250Sdes} 50442250Sdes 50542250Sdesstatic int si476x_radio_pretune(struct si476x_radio *radio, 50642250Sdes enum si476x_func func) 50742250Sdes{ 50842250Sdes int retval; 50942250Sdes 51042250Sdes struct si476x_tune_freq_args args = { 51142250Sdes .zifsr = false, 51242250Sdes .hd = false, 51342250Sdes .injside = SI476X_INJSIDE_AUTO, 51442250Sdes .tunemode = SI476X_TM_VALIDATED_NORMAL_TUNE, 51542250Sdes .smoothmetrics = SI476X_SM_INITIALIZE_AUDIO, 51642122Sdes .antcap = 0, 5171553Srgrimes }; 5181553Srgrimes 5191553Srgrimes switch (func) { 5201553Srgrimes case SI476X_FUNC_FM_RECEIVER: 52129602Scharnier args.freq = v4l2_to_si476x(radio->core, 52219618Sjulian 92 * FREQ_MUL); 5231553Srgrimes retval = radio->ops->tune_freq(radio->core, &args); 5241553Srgrimes break; 5251553Srgrimes case SI476X_FUNC_AM_RECEIVER: 52629602Scharnier args.freq = v4l2_to_si476x(radio->core, 5271553Srgrimes 0.6 * FREQ_MUL); 5281553Srgrimes retval = radio->ops->tune_freq(radio->core, &args); 5291553Srgrimes break; 5301553Srgrimes default: 53137844Sphk WARN(1, "Unexpected tuner function value\n"); 53237816Sphk retval = -EINVAL; 53337816Sphk } 53437816Sphk 5351553Srgrimes return retval; 5361553Srgrimes} 53730847Sdimastatic int si476x_radio_do_post_powerup_init(struct si476x_radio *radio, 53830847Sdima enum si476x_func func) 53930847Sdima{ 54030847Sdima int err; 54119617Sjulian 5422659Scsgr /* regcache_mark_dirty(radio->core->regmap); */ 54330847Sdima err = regcache_sync_region(radio->core->regmap, 5442659Scsgr SI476X_PROP_DIGITAL_IO_INPUT_SAMPLE_RATE, 5452659Scsgr SI476X_PROP_DIGITAL_IO_OUTPUT_FORMAT); 5462659Scsgr if (err < 0) 5472659Scsgr return err; 54830847Sdima 5492659Scsgr err = regcache_sync_region(radio->core->regmap, 5502659Scsgr SI476X_PROP_AUDIO_DEEMPHASIS, 5512659Scsgr SI476X_PROP_AUDIO_PWR_LINE_FILTER); 5522659Scsgr if (err < 0) 5532659Scsgr return err; 5542659Scsgr 5551553Srgrimes err = regcache_sync_region(radio->core->regmap, 5561553Srgrimes SI476X_PROP_INT_CTL_ENABLE, 55742122Sdes SI476X_PROP_INT_CTL_ENABLE); 5581553Srgrimes if (err < 0) 55945089Smarkm return err; 56045089Smarkm 56145089Smarkm /* 5621553Srgrimes * Is there any point in restoring SNR and the like 56345089Smarkm * when switching between AM/FM? 5641553Srgrimes */ 5651553Srgrimes err = regcache_sync_region(radio->core->regmap, 56637856Sache SI476X_PROP_VALID_MAX_TUNE_ERROR, 5671553Srgrimes SI476X_PROP_VALID_MAX_TUNE_ERROR); 5681553Srgrimes if (err < 0) 5691553Srgrimes return err; 57037856Sache 5711553Srgrimes err = regcache_sync_region(radio->core->regmap, 5721553Srgrimes SI476X_PROP_VALID_SNR_THRESHOLD, 5731553Srgrimes SI476X_PROP_VALID_RSSI_THRESHOLD); 5741553Srgrimes if (err < 0) 5751553Srgrimes return err; 5761553Srgrimes 5771553Srgrimes if (func == SI476X_FUNC_FM_RECEIVER) { 5781553Srgrimes if (si476x_core_has_diversity(radio->core)) { 5791553Srgrimes err = si476x_core_cmd_fm_phase_diversity(radio->core, 58042122Sdes radio->core->diversity_mode); 5811553Srgrimes if (err < 0) 5821553Srgrimes return err; 5831553Srgrimes } 5841553Srgrimes 5851553Srgrimes err = regcache_sync_region(radio->core->regmap, 5861553Srgrimes SI476X_PROP_FM_RDS_INTERRUPT_SOURCE, 5871553Srgrimes SI476X_PROP_FM_RDS_CONFIG); 5881553Srgrimes if (err < 0) 5891553Srgrimes return err; 5901553Srgrimes } 5911553Srgrimes 59219618Sjulian return si476x_radio_init_vtable(radio, func); 5931553Srgrimes 5941553Srgrimes} 59542122Sdes 5961553Srgrimesstatic int si476x_radio_change_func(struct si476x_radio *radio, 5971553Srgrimes enum si476x_func func) 5981553Srgrimes{ 59919618Sjulian int err; 60019618Sjulian bool soft; 60142122Sdes /* 6021553Srgrimes * Since power/up down is a very time consuming operation, 6031553Srgrimes * try to avoid doing it if the requested mode matches the one 6041553Srgrimes * the tuner is in 60529602Scharnier */ 6061553Srgrimes if (func == radio->core->power_up_parameters.func) 6071553Srgrimes return 0; 60819617Sjulian 6091553Srgrimes soft = true; 61035829Sguido err = si476x_core_stop(radio->core, soft); 61135829Sguido if (err < 0) { 61235829Sguido /* 61335829Sguido * OK, if the chip does not want to play nice let's 61435829Sguido * try to reset it in more brutal way 61535829Sguido */ 61635829Sguido soft = false; 61735829Sguido err = si476x_core_stop(radio->core, soft); 61835829Sguido if (err < 0) 61935829Sguido return err; 62035829Sguido } 62145089Smarkm /* 62245089Smarkm Set the desired radio tuner function 62345089Smarkm */ 62445089Smarkm radio->core->power_up_parameters.func = func; 62545089Smarkm 62645089Smarkm err = si476x_core_start(radio->core, soft); 62745089Smarkm if (err < 0) 62845588Smarkm return err; 62945588Smarkm 63045089Smarkm /* 63145089Smarkm * No need to do the rest of manipulations for the bootlader 63245089Smarkm * mode 63345089Smarkm */ 63445089Smarkm if (func != SI476X_FUNC_FM_RECEIVER && 63545089Smarkm func != SI476X_FUNC_AM_RECEIVER) 63645089Smarkm return err; 63745089Smarkm 63845089Smarkm return si476x_radio_do_post_powerup_init(radio, func); 63945089Smarkm} 64045089Smarkm 64145089Smarkmstatic int si476x_radio_g_frequency(struct file *file, void *priv, 64245089Smarkm struct v4l2_frequency *f) 64345089Smarkm{ 64445089Smarkm int err; 64545089Smarkm struct si476x_radio *radio = video_drvdata(file); 64645089Smarkm 64745089Smarkm if (f->tuner != 0 || 64845089Smarkm f->type != V4L2_TUNER_RADIO) 64945089Smarkm return -EINVAL; 65045089Smarkm 65145089Smarkm si476x_core_lock(radio->core); 65245089Smarkm 65345089Smarkm if (radio->ops->rsq_status) { 65445089Smarkm struct si476x_rsq_status_report report; 65519617Sjulian struct si476x_rsq_status_args args = { 6561553Srgrimes .primary = false, 65719617Sjulian .rsqack = false, 65819617Sjulian .attune = true, 6591553Srgrimes .cancel = false, 66029602Scharnier .stcack = false, 66129602Scharnier }; 6621553Srgrimes 6631553Srgrimes err = radio->ops->rsq_status(radio->core, &args, &report); 6641553Srgrimes if (!err) 6651553Srgrimes f->frequency = si476x_to_v4l2(radio->core, 6661553Srgrimes report.readfreq); 6671553Srgrimes } else { 6681553Srgrimes err = -EINVAL; 6691553Srgrimes } 6701553Srgrimes 6711553Srgrimes si476x_core_unlock(radio->core); 6721553Srgrimes 67319617Sjulian return err; 6741553Srgrimes} 67530807Sache 67630807Sachestatic int si476x_radio_s_frequency(struct file *file, void *priv, 67730807Sache const struct v4l2_frequency *f) 67830807Sache{ 67930807Sache int err; 68030807Sache u32 freq = f->frequency; 68130807Sache struct si476x_tune_freq_args args; 68230807Sache struct si476x_radio *radio = video_drvdata(file); 68330807Sache 68430807Sache const u32 midrange = (si476x_bands[SI476X_BAND_AM].rangehigh + 68530807Sache si476x_bands[SI476X_BAND_FM].rangelow) / 2; 68630807Sache const int band = (freq > midrange) ? 68730807Sache SI476X_BAND_FM : SI476X_BAND_AM; 68830807Sache const enum si476x_func func = (band == SI476X_BAND_AM) ? 68921640Speter SI476X_FUNC_AM_RECEIVER : SI476X_FUNC_FM_RECEIVER; 69030792Sache 69130792Sache if (f->tuner != 0 || 69230792Sache f->type != V4L2_TUNER_RADIO) 69330792Sache return -EINVAL; 69437850Sache 69537850Sache si476x_core_lock(radio->core); 69630792Sache 69730792Sache freq = clamp(freq, 69830792Sache si476x_bands[band].rangelow, 69930792Sache si476x_bands[band].rangehigh); 70021640Speter 70112024Speter if (si476x_radio_freq_is_inside_of_the_band(freq, 70212024Speter SI476X_BAND_AM) && 70312024Speter (!si476x_core_has_am(radio->core) || 70412024Speter si476x_core_is_a_secondary_tuner(radio->core))) { 70519617Sjulian err = -EINVAL; 70612024Speter goto unlock; 70721640Speter } 70821640Speter 70921640Speter err = si476x_radio_change_func(radio, func); 71021640Speter if (err < 0) 71121640Speter goto unlock; 71221640Speter 71321640Speter args.zifsr = false; 71421640Speter args.hd = false; 71521640Speter args.injside = SI476X_INJSIDE_AUTO; 7161553Srgrimes args.freq = v4l2_to_si476x(radio->core, freq); 71712024Speter args.tunemode = SI476X_TM_VALIDATED_NORMAL_TUNE; 71812024Speter args.smoothmetrics = SI476X_SM_INITIALIZE_AUDIO; 71912024Speter args.antcap = 0; 72012024Speter 72119617Sjulian err = radio->ops->tune_freq(radio->core, &args); 72212024Speter 7231553Srgrimesunlock: 7241553Srgrimes si476x_core_unlock(radio->core); 7258857Srgrimes return err; 7261553Srgrimes} 72719617Sjulian 7281553Srgrimesstatic int si476x_radio_s_hw_freq_seek(struct file *file, void *priv, 7291553Srgrimes const struct v4l2_hw_freq_seek *seek) 7301553Srgrimes{ 7311553Srgrimes int err; 7321553Srgrimes enum si476x_func func; 7338857Srgrimes u32 rangelow = seek->rangelow, rangehigh = seek->rangehigh; 7341553Srgrimes struct si476x_radio *radio = video_drvdata(file); 73519617Sjulian 7361553Srgrimes if (file->f_flags & O_NONBLOCK) 7371553Srgrimes return -EAGAIN; 73821640Speter 73935948Sbde if (seek->tuner != 0 || 74035948Sbde seek->type != V4L2_TUNER_RADIO) 7411553Srgrimes return -EINVAL; 74245089Smarkm 74345089Smarkm si476x_core_lock(radio->core); 74445089Smarkm 74545089Smarkm if (!rangelow) { 74645089Smarkm err = regmap_read(radio->core->regmap, 7471553Srgrimes SI476X_PROP_SEEK_BAND_BOTTOM, 7481553Srgrimes &rangelow); 74919617Sjulian if (err) 7501553Srgrimes goto unlock; 7511553Srgrimes rangelow = si476x_to_v4l2(radio->core, rangelow); 75219618Sjulian } 7531553Srgrimes if (!rangehigh) { 7541553Srgrimes err = regmap_read(radio->core->regmap, 7551553Srgrimes SI476X_PROP_SEEK_BAND_TOP, 7561553Srgrimes &rangehigh); 7571553Srgrimes if (err) 75819618Sjulian goto unlock; 75942122Sdes rangehigh = si476x_to_v4l2(radio->core, rangehigh); 76042122Sdes } 76142122Sdes 76242122Sdes if (rangelow > rangehigh) { 76342122Sdes err = -EINVAL; 76442122Sdes goto unlock; 76542250Sdes } 76642250Sdes 76742250Sdes if (si476x_radio_range_is_inside_of_the_band(rangelow, rangehigh, 76842250Sdes SI476X_BAND_FM)) { 76942122Sdes func = SI476X_FUNC_FM_RECEIVER; 77042122Sdes 77142122Sdes } else if (si476x_core_has_am(radio->core) && 77219618Sjulian si476x_radio_range_is_inside_of_the_band(rangelow, rangehigh, 77319618Sjulian SI476X_BAND_AM)) { 77419618Sjulian func = SI476X_FUNC_AM_RECEIVER; 77519618Sjulian } else { 7761553Srgrimes err = -EINVAL; 77719618Sjulian goto unlock; 77819618Sjulian } 77919618Sjulian 78019618Sjulian err = si476x_radio_change_func(radio, func); 78119618Sjulian if (err < 0) 78219618Sjulian goto unlock; 78319618Sjulian 78419618Sjulian if (seek->rangehigh) { 78519618Sjulian err = regmap_write(radio->core->regmap, 78619618Sjulian SI476X_PROP_SEEK_BAND_TOP, 78719618Sjulian v4l2_to_si476x(radio->core, 78819618Sjulian seek->rangehigh)); 78919618Sjulian if (err) 79019618Sjulian goto unlock; 79119618Sjulian } 79219618Sjulian if (seek->rangelow) { 79319618Sjulian err = regmap_write(radio->core->regmap, 79419618Sjulian SI476X_PROP_SEEK_BAND_BOTTOM, 79519618Sjulian v4l2_to_si476x(radio->core, 79619618Sjulian seek->rangelow)); 79719618Sjulian if (err) 79842122Sdes goto unlock; 7991553Srgrimes } 8001553Srgrimes if (seek->spacing) { 80142250Sdes err = regmap_write(radio->core->regmap, 80242122Sdes SI476X_PROP_SEEK_FREQUENCY_SPACING, 80342122Sdes v4l2_to_si476x(radio->core, 80442122Sdes seek->spacing)); 80542122Sdes if (err) 80642122Sdes goto unlock; 80719618Sjulian } 8081553Srgrimes 8091553Srgrimes err = radio->ops->seek_start(radio->core, 8101553Srgrimes seek->seek_upward, 8111553Srgrimes seek->wrap_around); 8121553Srgrimesunlock: 8131553Srgrimes si476x_core_unlock(radio->core); 8141553Srgrimes 8151553Srgrimes 81629602Scharnier 81719618Sjulian return err; 81819618Sjulian} 81919618Sjulian 82019618Sjulianstatic int si476x_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl) 82119618Sjulian{ 82219618Sjulian int retval; 82319618Sjulian struct si476x_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler); 82419618Sjulian 82519618Sjulian si476x_core_lock(radio->core); 82619618Sjulian 82719618Sjulian switch (ctrl->id) { 82819618Sjulian case V4L2_CID_SI476X_INTERCHIP_LINK: 82919618Sjulian if (si476x_core_has_diversity(radio->core)) { 83019618Sjulian if (radio->ops->phase_diversity) { 83119618Sjulian retval = radio->ops->phase_div_status(radio->core); 8321553Srgrimes if (retval < 0) 8331553Srgrimes break; 8341553Srgrimes 8351553Srgrimes ctrl->val = !!SI476X_PHDIV_STATUS_LINK_LOCKED(retval); 83642122Sdes retval = 0; 8371553Srgrimes break; 8381553Srgrimes } else { 83942250Sdes retval = -ENOTTY; 84042122Sdes break; 84142122Sdes } 84242122Sdes } 84342122Sdes retval = -EINVAL; 84419618Sjulian break; 84542122Sdes default: 8461553Srgrimes retval = -EINVAL; 8471553Srgrimes break; 8481553Srgrimes } 8491553Srgrimes si476x_core_unlock(radio->core); 8501553Srgrimes return retval; 8511553Srgrimes 8521553Srgrimes} 85319618Sjulian 85430807Sachestatic int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl) 8551553Srgrimes{ 8561553Srgrimes int retval; 85719618Sjulian enum si476x_phase_diversity_mode mode; 8581553Srgrimes struct si476x_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler); 8591553Srgrimes 86030807Sache si476x_core_lock(radio->core); 86130807Sache 86230807Sache switch (ctrl->id) { 86330807Sache case V4L2_CID_SI476X_HARMONICS_COUNT: 86430807Sache retval = regmap_update_bits(radio->core->regmap, 86530807Sache SI476X_PROP_AUDIO_PWR_LINE_FILTER, 86630792Sache SI476X_PROP_PWR_HARMONICS_MASK, 86730792Sache ctrl->val); 86830792Sache break; 86930792Sache case V4L2_CID_POWER_LINE_FREQUENCY: 87037850Sache switch (ctrl->val) { 87137850Sache case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: 87230792Sache retval = regmap_update_bits(radio->core->regmap, 87330792Sache SI476X_PROP_AUDIO_PWR_LINE_FILTER, 87430792Sache SI476X_PROP_PWR_ENABLE_MASK, 8751553Srgrimes 0); 87619618Sjulian break; 87719618Sjulian case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: 8781553Srgrimes retval = regmap_update_bits(radio->core->regmap, 8791553Srgrimes SI476X_PROP_AUDIO_PWR_LINE_FILTER, 8801553Srgrimes SI476X_PROP_PWR_GRID_MASK, 8811553Srgrimes SI476X_PROP_PWR_GRID_50HZ); 88219618Sjulian break; 88342122Sdes case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: 88419618Sjulian retval = regmap_update_bits(radio->core->regmap, 88519618Sjulian SI476X_PROP_AUDIO_PWR_LINE_FILTER, 88619618Sjulian SI476X_PROP_PWR_GRID_MASK, 88719618Sjulian SI476X_PROP_PWR_GRID_60HZ); 88819618Sjulian break; 88919618Sjulian default: 89019618Sjulian retval = -EINVAL; 89119618Sjulian break; 89219618Sjulian } 89319618Sjulian break; 89419618Sjulian case V4L2_CID_SI476X_RSSI_THRESHOLD: 89530847Sdima retval = regmap_write(radio->core->regmap, 89619618Sjulian SI476X_PROP_VALID_RSSI_THRESHOLD, 89719618Sjulian ctrl->val); 89819618Sjulian break; 89919618Sjulian case V4L2_CID_SI476X_SNR_THRESHOLD: 90019618Sjulian retval = regmap_write(radio->core->regmap, 90119618Sjulian SI476X_PROP_VALID_SNR_THRESHOLD, 90219618Sjulian ctrl->val); 90319618Sjulian break; 90419618Sjulian case V4L2_CID_SI476X_MAX_TUNE_ERROR: 90519618Sjulian retval = regmap_write(radio->core->regmap, 90619618Sjulian SI476X_PROP_VALID_MAX_TUNE_ERROR, 90719618Sjulian ctrl->val); 90830807Sache break; 90930807Sache case V4L2_CID_RDS_RECEPTION: 91030792Sache /* 91130807Sache * It looks like RDS related properties are 91230792Sache * inaccessible when tuner is in AM mode, so cache the 91330807Sache * changes 9141553Srgrimes */ 91519618Sjulian if (si476x_core_is_in_am_receiver_mode(radio->core)) 91642122Sdes regcache_cache_only(radio->core->regmap, true); 91719618Sjulian 9181553Srgrimes if (ctrl->val) { 9191553Srgrimes retval = regmap_write(radio->core->regmap, 9201553Srgrimes SI476X_PROP_FM_RDS_INTERRUPT_FIFO_COUNT, 92119618Sjulian radio->core->rds_fifo_depth); 9221553Srgrimes if (retval < 0) 9231553Srgrimes break; 9241553Srgrimes 9251553Srgrimes if (radio->core->client->irq) { 9261553Srgrimes retval = regmap_write(radio->core->regmap, 9271553Srgrimes SI476X_PROP_FM_RDS_INTERRUPT_SOURCE, 9281553Srgrimes SI476X_RDSRECV); 9291553Srgrimes if (retval < 0) 9302657Scsgr break; 9312657Scsgr } 9322657Scsgr 9332657Scsgr /* Drain RDS FIFO before enabling RDS processing */ 9342657Scsgr retval = si476x_core_cmd_fm_rds_status(radio->core, 9352657Scsgr false, 9362657Scsgr true, 9372657Scsgr true, 9382657Scsgr NULL); 9392657Scsgr if (retval < 0) 94022306Sjulian break; 9412657Scsgr 9422657Scsgr retval = regmap_update_bits(radio->core->regmap, 9432657Scsgr SI476X_PROP_FM_RDS_CONFIG, 9442657Scsgr SI476X_PROP_RDSEN_MASK, 9452657Scsgr SI476X_PROP_RDSEN); 9462657Scsgr } else { 9472657Scsgr retval = regmap_update_bits(radio->core->regmap, 9482657Scsgr SI476X_PROP_FM_RDS_CONFIG, 9492657Scsgr SI476X_PROP_RDSEN_MASK, 9502657Scsgr !SI476X_PROP_RDSEN); 9512657Scsgr } 9522657Scsgr 9532657Scsgr if (si476x_core_is_in_am_receiver_mode(radio->core)) 9542657Scsgr regcache_cache_only(radio->core->regmap, false); 9552657Scsgr break; 9562657Scsgr case V4L2_CID_TUNE_DEEMPHASIS: 9572657Scsgr retval = regmap_write(radio->core->regmap, 9582657Scsgr SI476X_PROP_AUDIO_DEEMPHASIS, 9592657Scsgr ctrl->val); 9602657Scsgr break; 9612657Scsgr 9622657Scsgr case V4L2_CID_SI476X_DIVERSITY_MODE: 9631553Srgrimes mode = si476x_phase_diversity_idx_to_mode(ctrl->val); 9641553Srgrimes 9651553Srgrimes if (mode == radio->core->diversity_mode) { 9661553Srgrimes retval = 0; 9671553Srgrimes break; 9681553Srgrimes } 9691553Srgrimes 9701553Srgrimes if (si476x_core_is_in_am_receiver_mode(radio->core)) { 97142122Sdes /* 9721553Srgrimes * Diversity cannot be configured while tuner 97319617Sjulian * is in AM mode so save the changes and carry on. 9741553Srgrimes */ 9751553Srgrimes radio->core->diversity_mode = mode; 9761553Srgrimes retval = 0; 9771553Srgrimes } else { 9781553Srgrimes retval = radio->ops->phase_diversity(radio->core, mode); 9791553Srgrimes if (!retval) 9801553Srgrimes radio->core->diversity_mode = mode; 9811553Srgrimes } 9821553Srgrimes break; 9832657Scsgr 9842657Scsgr default: 9851553Srgrimes retval = -EINVAL; 9861553Srgrimes break; 9871553Srgrimes } 98842122Sdes 9891553Srgrimes si476x_core_unlock(radio->core); 9901553Srgrimes 9911553Srgrimes return retval; 9922657Scsgr} 9932657Scsgr 9942657Scsgr#ifdef CONFIG_VIDEO_ADV_DEBUG 9952657Scsgrstatic int si476x_radio_g_register(struct file *file, void *fh, 9962657Scsgr struct v4l2_dbg_register *reg) 99742122Sdes{ 9982657Scsgr int err; 99942122Sdes unsigned int value; 10002657Scsgr struct si476x_radio *radio = video_drvdata(file); 10012657Scsgr 10022657Scsgr si476x_core_lock(radio->core); 10032657Scsgr reg->size = 2; 10042657Scsgr err = regmap_read(radio->core->regmap, 10052657Scsgr (unsigned int)reg->reg, &value); 10062657Scsgr reg->val = value; 10072657Scsgr si476x_core_unlock(radio->core); 10082657Scsgr 10092657Scsgr return err; 10102657Scsgr} 10112657Scsgrstatic int si476x_radio_s_register(struct file *file, void *fh, 10122657Scsgr const struct v4l2_dbg_register *reg) 10132657Scsgr{ 10142657Scsgr 10152657Scsgr int err; 101642122Sdes struct si476x_radio *radio = video_drvdata(file); 10172657Scsgr 10182657Scsgr si476x_core_lock(radio->core); 10192657Scsgr err = regmap_write(radio->core->regmap, 102042122Sdes (unsigned int)reg->reg, 10211553Srgrimes (unsigned int)reg->val); 10221553Srgrimes si476x_core_unlock(radio->core); 102342250Sdes 102442122Sdes return err; 102542122Sdes} 102642122Sdes#endif 102742122Sdes 102842122Sdesstatic int si476x_radio_fops_open(struct file *file) 10291553Srgrimes{ 10301553Srgrimes struct si476x_radio *radio = video_drvdata(file); 10311553Srgrimes int err; 10321553Srgrimes 103319617Sjulian err = v4l2_fh_open(file); 10341553Srgrimes if (err) 10351553Srgrimes return err; 10361553Srgrimes 10371553Srgrimes if (v4l2_fh_is_singular_file(file)) { 10381553Srgrimes si476x_core_lock(radio->core); 10391553Srgrimes err = si476x_core_set_power_state(radio->core, 10401553Srgrimes SI476X_POWER_UP_FULL); 10411553Srgrimes if (err < 0) 10421553Srgrimes goto done; 10431553Srgrimes 10441553Srgrimes err = si476x_radio_do_post_powerup_init(radio, 104529602Scharnier radio->core->power_up_parameters.func); 104629602Scharnier if (err < 0) 10471553Srgrimes goto power_down; 10481553Srgrimes 10491553Srgrimes err = si476x_radio_pretune(radio, 10501553Srgrimes radio->core->power_up_parameters.func); 10511553Srgrimes if (err < 0) 10521553Srgrimes goto power_down; 10531553Srgrimes 10541553Srgrimes si476x_core_unlock(radio->core); 10551553Srgrimes /*Must be done after si476x_core_unlock to prevent a deadlock*/ 10561553Srgrimes v4l2_ctrl_handler_setup(&radio->ctrl_handler); 10571553Srgrimes } 105825253Swollman 105913956Swollman return err; 106013956Swollman 106125253Swollmanpower_down: 10621553Srgrimes si476x_core_set_power_state(radio->core, 106336042Sguido SI476X_POWER_DOWN); 106436042Sguidodone: 106536042Sguido si476x_core_unlock(radio->core); 106636042Sguido v4l2_fh_release(file); 10671553Srgrimes 10681553Srgrimes return err; 10691553Srgrimes} 107029602Scharnier 107129602Scharnierstatic int si476x_radio_fops_release(struct file *file) 10721553Srgrimes{ 10731553Srgrimes struct si476x_radio *radio = video_drvdata(file); 10741553Srgrimes 10751553Srgrimes if (v4l2_fh_is_singular_file(file) && 10761553Srgrimes atomic_read(&radio->core->is_alive)) 10771553Srgrimes si476x_core_set_power_state(radio->core, 10781553Srgrimes SI476X_POWER_DOWN); 10791553Srgrimes 10801553Srgrimes return v4l2_fh_release(file); 10811553Srgrimes} 10822657Scsgr 10832657Scsgrstatic ssize_t si476x_radio_fops_read(struct file *file, char __user *buf, 10842657Scsgr size_t count, loff_t *ppos) 10858857Srgrimes{ 10862657Scsgr ssize_t rval; 10872657Scsgr size_t fifo_len; 10882657Scsgr unsigned int copied; 10892657Scsgr 10902657Scsgr struct si476x_radio *radio = video_drvdata(file); 10918857Srgrimes 10922657Scsgr /* block if no new data available */ 10932657Scsgr if (kfifo_is_empty(&radio->core->rds_fifo)) { 10942657Scsgr if (file->f_flags & O_NONBLOCK) 10952657Scsgr return -EWOULDBLOCK; 10962657Scsgr 10972657Scsgr rval = wait_event_interruptible(radio->core->rds_read_queue, 10982657Scsgr (!kfifo_is_empty(&radio->core->rds_fifo) || 10992657Scsgr !atomic_read(&radio->core->is_alive))); 11002657Scsgr if (rval < 0) 11012657Scsgr return -EINTR; 11028857Srgrimes 11032657Scsgr if (!atomic_read(&radio->core->is_alive)) 11041553Srgrimes return -ENODEV; 110517197Sdg } 110619618Sjulian 11071553Srgrimes fifo_len = kfifo_len(&radio->core->rds_fifo); 110829602Scharnier 11091553Srgrimes if (kfifo_to_user(&radio->core->rds_fifo, buf, 11101553Srgrimes min(fifo_len, count), 11111553Srgrimes &copied) != 0) { 11121553Srgrimes dev_warn(&radio->videodev.dev, 11131553Srgrimes "Error during FIFO to userspace copy\n"); 11141553Srgrimes rval = -EIO; 11151553Srgrimes } else { 11161553Srgrimes rval = (ssize_t)copied; 11171553Srgrimes } 11181553Srgrimes 11191553Srgrimes return rval; 11201553Srgrimes} 112119618Sjulian 112219618Sjulianstatic __poll_t si476x_radio_fops_poll(struct file *file, 11231553Srgrimes struct poll_table_struct *pts) 11241553Srgrimes{ 11251553Srgrimes struct si476x_radio *radio = video_drvdata(file); 11261553Srgrimes __poll_t req_events = poll_requested_events(pts); 112719618Sjulian __poll_t err = v4l2_ctrl_poll(file, pts); 11281553Srgrimes 11291553Srgrimes if (req_events & (EPOLLIN | EPOLLRDNORM)) { 11301553Srgrimes if (atomic_read(&radio->core->is_alive)) 11311553Srgrimes poll_wait(file, &radio->core->rds_read_queue, pts); 11321553Srgrimes 11331553Srgrimes if (!atomic_read(&radio->core->is_alive)) 11341553Srgrimes err = EPOLLHUP; 113542122Sdes 11361553Srgrimes if (!kfifo_is_empty(&radio->core->rds_fifo)) 11371553Srgrimes err = EPOLLIN | EPOLLRDNORM; 11381553Srgrimes } 11391553Srgrimes 114019617Sjulian return err; 11411553Srgrimes} 11421553Srgrimes 11431553Srgrimesstatic const struct v4l2_file_operations si476x_fops = { 114442122Sdes .owner = THIS_MODULE, 11451553Srgrimes .read = si476x_radio_fops_read, 11461553Srgrimes .poll = si476x_radio_fops_poll, 114742122Sdes .unlocked_ioctl = video_ioctl2, 11481553Srgrimes .open = si476x_radio_fops_open, 11491553Srgrimes .release = si476x_radio_fops_release, 11501553Srgrimes}; 115119618Sjulian 115219618Sjulian 115319618Sjulianstatic const struct v4l2_ioctl_ops si4761_ioctl_ops = { 115419618Sjulian .vidioc_querycap = si476x_radio_querycap, 115529602Scharnier .vidioc_g_tuner = si476x_radio_g_tuner, 115619618Sjulian .vidioc_s_tuner = si476x_radio_s_tuner, 115719618Sjulian 115819618Sjulian .vidioc_g_frequency = si476x_radio_g_frequency, 115919618Sjulian .vidioc_s_frequency = si476x_radio_s_frequency, 116019618Sjulian .vidioc_s_hw_freq_seek = si476x_radio_s_hw_freq_seek, 116119618Sjulian .vidioc_enum_freq_bands = si476x_radio_enum_freq_bands, 116219618Sjulian 116319618Sjulian .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 116419618Sjulian .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 116519618Sjulian 116619618Sjulian#ifdef CONFIG_VIDEO_ADV_DEBUG 116719618Sjulian .vidioc_g_register = si476x_radio_g_register, 116819618Sjulian .vidioc_s_register = si476x_radio_s_register, 116919618Sjulian#endif 117019618Sjulian}; 117119618Sjulian 117219618Sjulian 117319618Sjulianstatic const struct video_device si476x_viddev_template = { 117419618Sjulian .fops = &si476x_fops, 117519618Sjulian .name = DRIVER_NAME, 117619618Sjulian .release = video_device_release_empty, 117719618Sjulian}; 117819618Sjulian 117919618Sjulian 118019618Sjulian 118119618Sjulianstatic ssize_t si476x_radio_read_acf_blob(struct file *file, 118219618Sjulian char __user *user_buf, 118319618Sjulian size_t count, loff_t *ppos) 118429602Scharnier{ 118519618Sjulian int err; 118619618Sjulian struct si476x_radio *radio = file->private_data; 118719618Sjulian struct si476x_acf_status_report report; 118819618Sjulian 118919618Sjulian si476x_core_lock(radio->core); 119019618Sjulian if (radio->ops->acf_status) 119119618Sjulian err = radio->ops->acf_status(radio->core, &report); 119219618Sjulian else 119319618Sjulian err = -ENOENT; 119419618Sjulian si476x_core_unlock(radio->core); 119519618Sjulian 119619618Sjulian if (err < 0) 119719618Sjulian return err; 119819618Sjulian 119919618Sjulian return simple_read_from_buffer(user_buf, count, ppos, &report, 120019618Sjulian sizeof(report)); 120119618Sjulian} 120219618Sjulian 120319618Sjulianstatic const struct file_operations radio_acf_fops = { 120419618Sjulian .open = simple_open, 120519618Sjulian .llseek = default_llseek, 120619618Sjulian .read = si476x_radio_read_acf_blob, 120719618Sjulian}; 120819618Sjulian 120919618Sjulianstatic ssize_t si476x_radio_read_rds_blckcnt_blob(struct file *file, 121019618Sjulian char __user *user_buf, 121119618Sjulian size_t count, loff_t *ppos) 121219618Sjulian{ 12131553Srgrimes int err; 12141553Srgrimes struct si476x_radio *radio = file->private_data; 12151553Srgrimes struct si476x_rds_blockcount_report report; 12161553Srgrimes 12171553Srgrimes si476x_core_lock(radio->core); 12181553Srgrimes if (radio->ops->rds_blckcnt) 12191553Srgrimes err = radio->ops->rds_blckcnt(radio->core, true, 12201553Srgrimes &report); 12211553Srgrimes else 12221553Srgrimes err = -ENOENT; 12231553Srgrimes si476x_core_unlock(radio->core); 12241553Srgrimes 12251553Srgrimes if (err < 0) 12261553Srgrimes return err; 12271553Srgrimes 12281553Srgrimes return simple_read_from_buffer(user_buf, count, ppos, &report, 12291553Srgrimes sizeof(report)); 12301553Srgrimes} 12311553Srgrimes 12321553Srgrimesstatic const struct file_operations radio_rds_blckcnt_fops = { 12331553Srgrimes .open = simple_open, 12341553Srgrimes .llseek = default_llseek, 12351553Srgrimes .read = si476x_radio_read_rds_blckcnt_blob, 12361553Srgrimes}; 12371553Srgrimes 12381553Srgrimesstatic ssize_t si476x_radio_read_agc_blob(struct file *file, 12391553Srgrimes char __user *user_buf, 12401553Srgrimes size_t count, loff_t *ppos) 12411553Srgrimes{ 12421553Srgrimes int err; 124319618Sjulian struct si476x_radio *radio = file->private_data; 12442657Scsgr struct si476x_agc_status_report report; 12451553Srgrimes 12461553Srgrimes si476x_core_lock(radio->core); 12471553Srgrimes if (radio->ops->rds_blckcnt) 12481553Srgrimes err = radio->ops->agc_status(radio->core, &report); 12491553Srgrimes else 12501553Srgrimes err = -ENOENT; 12511553Srgrimes si476x_core_unlock(radio->core); 12521553Srgrimes 12531553Srgrimes if (err < 0) 12541553Srgrimes return err; 12551553Srgrimes 12561553Srgrimes return simple_read_from_buffer(user_buf, count, ppos, &report, 12571553Srgrimes sizeof(report)); 12581553Srgrimes} 12591553Srgrimes 12601553Srgrimesstatic const struct file_operations radio_agc_fops = { 12611553Srgrimes .open = simple_open, 12621553Srgrimes .llseek = default_llseek, 12631553Srgrimes .read = si476x_radio_read_agc_blob, 12641553Srgrimes}; 12651553Srgrimes 12661553Srgrimesstatic ssize_t si476x_radio_read_rsq_blob(struct file *file, 12671553Srgrimes char __user *user_buf, 12681553Srgrimes size_t count, loff_t *ppos) 12691553Srgrimes{ 12701553Srgrimes int err; 12711553Srgrimes struct si476x_radio *radio = file->private_data; 12721553Srgrimes struct si476x_rsq_status_report report; 12731553Srgrimes struct si476x_rsq_status_args args = { 12741553Srgrimes .primary = false, 12751553Srgrimes .rsqack = false, 12761553Srgrimes .attune = false, 12771553Srgrimes .cancel = false, 12781553Srgrimes .stcack = false, 12791553Srgrimes }; 12801553Srgrimes 12811553Srgrimes si476x_core_lock(radio->core); 12821553Srgrimes if (radio->ops->rds_blckcnt) 12831553Srgrimes err = radio->ops->rsq_status(radio->core, &args, &report); 12841553Srgrimes else 12851553Srgrimes err = -ENOENT; 12861553Srgrimes si476x_core_unlock(radio->core); 12871553Srgrimes 128836042Sguido if (err < 0) 128936042Sguido return err; 129036042Sguido 129136042Sguido return simple_read_from_buffer(user_buf, count, ppos, &report, 129236042Sguido sizeof(report)); 129336042Sguido} 129436042Sguido 129536042Sguidostatic const struct file_operations radio_rsq_fops = { 12962657Scsgr .open = simple_open, 129719237Sjoerg .llseek = default_llseek, 129819237Sjoerg .read = si476x_radio_read_rsq_blob, 12992657Scsgr}; 13002657Scsgr 13012657Scsgrstatic ssize_t si476x_radio_read_rsq_primary_blob(struct file *file, 13022657Scsgr char __user *user_buf, 13032657Scsgr size_t count, loff_t *ppos) 130417482Sjulian{ 13052657Scsgr int err; 13062657Scsgr struct si476x_radio *radio = file->private_data; 13072657Scsgr struct si476x_rsq_status_report report; 13082657Scsgr struct si476x_rsq_status_args args = { 13092657Scsgr .primary = true, 13102657Scsgr .rsqack = false, 13112657Scsgr .attune = false, 13122657Scsgr .cancel = false, 13132657Scsgr .stcack = false, 13142657Scsgr }; 13152657Scsgr 13162657Scsgr si476x_core_lock(radio->core); 13178857Srgrimes if (radio->ops->rds_blckcnt) 13188857Srgrimes err = radio->ops->rsq_status(radio->core, &args, &report); 13192657Scsgr else 13202657Scsgr err = -ENOENT; 13212657Scsgr si476x_core_unlock(radio->core); 13222657Scsgr 13232657Scsgr if (err < 0) 13242657Scsgr return err; 13252657Scsgr 13262657Scsgr return simple_read_from_buffer(user_buf, count, ppos, &report, 13272657Scsgr sizeof(report)); 13282657Scsgr} 13291553Srgrimes 133019618Sjulianstatic const struct file_operations radio_rsq_primary_fops = { 133119618Sjulian .open = simple_open, 133219618Sjulian .llseek = default_llseek, 133319618Sjulian .read = si476x_radio_read_rsq_primary_blob, 133419618Sjulian}; 133519618Sjulian 133619618Sjulian 133719618Sjulianstatic void si476x_radio_init_debugfs(struct si476x_radio *radio) 133819618Sjulian{ 133919618Sjulian radio->debugfs = debugfs_create_dir(dev_name(radio->v4l2dev.dev), NULL); 134033794Spst 134133794Spst debugfs_create_file("acf", S_IRUGO, radio->debugfs, radio, 134219618Sjulian &radio_acf_fops); 134319618Sjulian 134419618Sjulian debugfs_create_file("rds_blckcnt", S_IRUGO, radio->debugfs, radio, 134519618Sjulian &radio_rds_blckcnt_fops); 134619618Sjulian 134730847Sdima debugfs_create_file("agc", S_IRUGO, radio->debugfs, radio, 134819618Sjulian &radio_agc_fops); 134919618Sjulian 135019618Sjulian debugfs_create_file("rsq", S_IRUGO, radio->debugfs, radio, 135119618Sjulian &radio_rsq_fops); 135219618Sjulian 135319618Sjulian debugfs_create_file("rsq_primary", S_IRUGO, radio->debugfs, radio, 135430847Sdima &radio_rsq_primary_fops); 135530847Sdima} 135630847Sdima 135730847Sdima 135830847Sdimastatic int si476x_radio_add_new_custom(struct si476x_radio *radio, 135930847Sdima enum si476x_ctrl_idx idx) 136019618Sjulian{ 13611553Srgrimes int rval; 13621553Srgrimes struct v4l2_ctrl *ctrl; 136319618Sjulian 136419618Sjulian ctrl = v4l2_ctrl_new_custom(&radio->ctrl_handler, 13651553Srgrimes &si476x_ctrls[idx], 136619618Sjulian NULL); 13671553Srgrimes rval = radio->ctrl_handler.error; 13688857Srgrimes if (ctrl == NULL && rval) 13691553Srgrimes dev_err(radio->v4l2dev.dev, 13701553Srgrimes "Could not initialize '%s' control %d\n", 13711553Srgrimes si476x_ctrls[idx].name, rval); 13721553Srgrimes 13731553Srgrimes return rval; 13748857Srgrimes} 13751553Srgrimes 13761553Srgrimesstatic int si476x_radio_probe(struct platform_device *pdev) 13771553Srgrimes{ 13781553Srgrimes int rval; 13791553Srgrimes struct si476x_radio *radio; 13801553Srgrimes struct v4l2_ctrl *ctrl; 138130792Sache 138230792Sache static atomic_t instance = ATOMIC_INIT(0); 138330792Sache 138430792Sache radio = devm_kzalloc(&pdev->dev, sizeof(*radio), GFP_KERNEL); 138530792Sache if (!radio) 138630792Sache return -ENOMEM; 138730792Sache 138830807Sache radio->core = i2c_mfd_cell_to_core(&pdev->dev); 138930807Sache 139030807Sache v4l2_device_set_name(&radio->v4l2dev, DRIVER_NAME, &instance); 139130807Sache 139230807Sache rval = v4l2_device_register(&pdev->dev, &radio->v4l2dev); 13931553Srgrimes if (rval) { 139445588Smarkm dev_err(&pdev->dev, "Cannot register v4l2_device.\n"); 139545588Smarkm return rval; 13961553Srgrimes } 13971553Srgrimes 13981553Srgrimes memcpy(&radio->videodev, &si476x_viddev_template, 13991553Srgrimes sizeof(struct video_device)); 14001553Srgrimes 14011553Srgrimes radio->videodev.v4l2_dev = &radio->v4l2dev; 14021553Srgrimes radio->videodev.ioctl_ops = &si4761_ioctl_ops; 14031553Srgrimes radio->videodev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | 14041553Srgrimes V4L2_CAP_HW_FREQ_SEEK; 14051553Srgrimes 14061553Srgrimes si476x_core_lock(radio->core); 14071553Srgrimes if (!si476x_core_is_a_secondary_tuner(radio->core)) 140819618Sjulian radio->videodev.device_caps |= V4L2_CAP_RDS_CAPTURE | 14091553Srgrimes V4L2_CAP_READWRITE; 14101553Srgrimes si476x_core_unlock(radio->core); 14111553Srgrimes 141245588Smarkm video_set_drvdata(&radio->videodev, radio); 141319618Sjulian platform_set_drvdata(pdev, radio); 141419618Sjulian 141519618Sjulian 141619618Sjulian radio->v4l2dev.ctrl_handler = &radio->ctrl_handler; 141745588Smarkm v4l2_ctrl_handler_init(&radio->ctrl_handler, 141819618Sjulian 1 + ARRAY_SIZE(si476x_ctrls)); 141919618Sjulian 142019618Sjulian if (si476x_core_has_am(radio->core)) { 142119618Sjulian ctrl = v4l2_ctrl_new_std_menu(&radio->ctrl_handler, 142219618Sjulian &si476x_ctrl_ops, 142319618Sjulian V4L2_CID_POWER_LINE_FREQUENCY, 142419618Sjulian V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 14251553Srgrimes 0, 0); 14261553Srgrimes rval = radio->ctrl_handler.error; 142719618Sjulian if (ctrl == NULL && rval) { 14281553Srgrimes dev_err(&pdev->dev, "Could not initialize V4L2_CID_POWER_LINE_FREQUENCY control %d\n", 142919618Sjulian rval); 143019618Sjulian goto exit; 143119618Sjulian } 143219618Sjulian 143319618Sjulian rval = si476x_radio_add_new_custom(radio, 143419618Sjulian SI476X_IDX_HARMONICS_COUNT); 14351553Srgrimes if (rval < 0) 14361553Srgrimes goto exit; 14371553Srgrimes } 14381553Srgrimes 14391553Srgrimes rval = si476x_radio_add_new_custom(radio, SI476X_IDX_RSSI_THRESHOLD); 14401553Srgrimes if (rval < 0) 14411553Srgrimes goto exit; 14421553Srgrimes 14431553Srgrimes rval = si476x_radio_add_new_custom(radio, SI476X_IDX_SNR_THRESHOLD); 14441553Srgrimes if (rval < 0) 14451553Srgrimes goto exit; 14461553Srgrimes 14471553Srgrimes rval = si476x_radio_add_new_custom(radio, SI476X_IDX_MAX_TUNE_ERROR); 14481553Srgrimes if (rval < 0) 14491553Srgrimes goto exit; 14501553Srgrimes 14511553Srgrimes ctrl = v4l2_ctrl_new_std_menu(&radio->ctrl_handler, 145230807Sache &si476x_ctrl_ops, 145330807Sache V4L2_CID_TUNE_DEEMPHASIS, 145430792Sache V4L2_DEEMPHASIS_75_uS, 0, 0); 145530792Sache rval = radio->ctrl_handler.error; 145630792Sache if (ctrl == NULL && rval) { 145730792Sache dev_err(&pdev->dev, "Could not initialize V4L2_CID_TUNE_DEEMPHASIS control %d\n", 14581553Srgrimes rval); 14591553Srgrimes goto exit; 146019618Sjulian } 146119618Sjulian 14621553Srgrimes ctrl = v4l2_ctrl_new_std(&radio->ctrl_handler, &si476x_ctrl_ops, 14631553Srgrimes V4L2_CID_RDS_RECEPTION, 14641553Srgrimes 0, 1, 1, 1); 14651553Srgrimes rval = radio->ctrl_handler.error; 14661553Srgrimes if (ctrl == NULL && rval) { 14671553Srgrimes dev_err(&pdev->dev, "Could not initialize V4L2_CID_RDS_RECEPTION control %d\n", 14681553Srgrimes rval); 14691553Srgrimes goto exit; 14701553Srgrimes } 14711553Srgrimes 14721553Srgrimes if (si476x_core_has_diversity(radio->core)) { 14731553Srgrimes si476x_ctrls[SI476X_IDX_DIVERSITY_MODE].def = 14741553Srgrimes si476x_phase_diversity_mode_to_idx(radio->core->diversity_mode); 14751553Srgrimes rval = si476x_radio_add_new_custom(radio, SI476X_IDX_DIVERSITY_MODE); 14761553Srgrimes if (rval < 0) 14771553Srgrimes goto exit; 14781553Srgrimes 14791553Srgrimes rval = si476x_radio_add_new_custom(radio, SI476X_IDX_INTERCHIP_LINK); 14801553Srgrimes if (rval < 0) 148119617Sjulian goto exit; 14821553Srgrimes } 14831553Srgrimes 14841553Srgrimes /* register video device */ 14851553Srgrimes rval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, -1); 14861553Srgrimes if (rval < 0) { 14871553Srgrimes dev_err(&pdev->dev, "Could not register video device\n"); 14881553Srgrimes goto exit; 14891553Srgrimes } 14901553Srgrimes 14911553Srgrimes si476x_radio_init_debugfs(radio); 149211933Sadam 14931553Srgrimes return 0; 14941553Srgrimesexit: 14951553Srgrimes v4l2_ctrl_handler_free(radio->videodev.ctrl_handler); 14961553Srgrimes return rval; 14971553Srgrimes} 14981553Srgrimes 14991553Srgrimesstatic void si476x_radio_remove(struct platform_device *pdev) 15001553Srgrimes{ 15011553Srgrimes struct si476x_radio *radio = platform_get_drvdata(pdev); 15021553Srgrimes 150319617Sjulian v4l2_ctrl_handler_free(radio->videodev.ctrl_handler); 15041553Srgrimes video_unregister_device(&radio->videodev); 15051553Srgrimes v4l2_device_unregister(&radio->v4l2dev); 15061553Srgrimes debugfs_remove_recursive(radio->debugfs); 15071553Srgrimes} 150811933Sadam 150911933SadamMODULE_ALIAS("platform:si476x-radio"); 15101553Srgrimes 151111933Sadamstatic struct platform_driver si476x_radio_driver = { 151211933Sadam .driver = { 151311933Sadam .name = DRIVER_NAME, 151411933Sadam }, 151511933Sadam .probe = si476x_radio_probe, 151611933Sadam .remove_new = si476x_radio_remove, 15171553Srgrimes}; 15181553Srgrimesmodule_platform_driver(si476x_radio_driver); 15191553Srgrimes 15201553SrgrimesMODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>"); 15211553SrgrimesMODULE_DESCRIPTION("Driver for Si4761/64/68 AM/FM Radio MFD Cell"); 15221553SrgrimesMODULE_LICENSE("GPL"); 15231553Srgrimes