cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

radio-si476x.c (39882B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * drivers/media/radio/radio-si476x.c -- V4L2 driver for SI476X chips
      4 *
      5 * Copyright (C) 2012 Innovative Converged Devices(ICD)
      6 * Copyright (C) 2013 Andrey Smirnov
      7 *
      8 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
      9 */
     10
     11#include <linux/module.h>
     12#include <linux/delay.h>
     13#include <linux/interrupt.h>
     14#include <linux/slab.h>
     15#include <linux/atomic.h>
     16#include <linux/videodev2.h>
     17#include <linux/mutex.h>
     18#include <linux/debugfs.h>
     19#include <media/v4l2-common.h>
     20#include <media/v4l2-ioctl.h>
     21#include <media/v4l2-ctrls.h>
     22#include <media/v4l2-event.h>
     23#include <media/v4l2-device.h>
     24
     25#include <media/drv-intf/si476x.h>
     26#include <linux/mfd/si476x-core.h>
     27
     28#define FM_FREQ_RANGE_LOW   64000000
     29#define FM_FREQ_RANGE_HIGH 108000000
     30
     31#define AM_FREQ_RANGE_LOW    520000
     32#define AM_FREQ_RANGE_HIGH 30000000
     33
     34#define PWRLINEFLTR (1 << 8)
     35
     36#define FREQ_MUL (10000000 / 625)
     37
     38#define SI476X_PHDIV_STATUS_LINK_LOCKED(status) (0x80 & (status))
     39
     40#define DRIVER_NAME "si476x-radio"
     41#define DRIVER_CARD "SI476x AM/FM Receiver"
     42
     43enum si476x_freq_bands {
     44	SI476X_BAND_FM,
     45	SI476X_BAND_AM,
     46};
     47
     48static const struct v4l2_frequency_band si476x_bands[] = {
     49	[SI476X_BAND_FM] = {
     50		.type		= V4L2_TUNER_RADIO,
     51		.index		= SI476X_BAND_FM,
     52		.capability	= V4L2_TUNER_CAP_LOW
     53		| V4L2_TUNER_CAP_STEREO
     54		| V4L2_TUNER_CAP_RDS
     55		| V4L2_TUNER_CAP_RDS_BLOCK_IO
     56		| V4L2_TUNER_CAP_FREQ_BANDS,
     57		.rangelow	=  64 * FREQ_MUL,
     58		.rangehigh	= 108 * FREQ_MUL,
     59		.modulation	= V4L2_BAND_MODULATION_FM,
     60	},
     61	[SI476X_BAND_AM] = {
     62		.type		= V4L2_TUNER_RADIO,
     63		.index		= SI476X_BAND_AM,
     64		.capability	= V4L2_TUNER_CAP_LOW
     65		| V4L2_TUNER_CAP_FREQ_BANDS,
     66		.rangelow	= 0.52 * FREQ_MUL,
     67		.rangehigh	= 30 * FREQ_MUL,
     68		.modulation	= V4L2_BAND_MODULATION_AM,
     69	},
     70};
     71
     72static inline bool si476x_radio_freq_is_inside_of_the_band(u32 freq, int band)
     73{
     74	return freq >= si476x_bands[band].rangelow &&
     75		freq <= si476x_bands[band].rangehigh;
     76}
     77
     78static inline bool si476x_radio_range_is_inside_of_the_band(u32 low, u32 high,
     79							    int band)
     80{
     81	return low  >= si476x_bands[band].rangelow &&
     82		high <= si476x_bands[band].rangehigh;
     83}
     84
     85static int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl);
     86static int si476x_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl);
     87
     88enum phase_diversity_modes_idx {
     89	SI476X_IDX_PHDIV_DISABLED,
     90	SI476X_IDX_PHDIV_PRIMARY_COMBINING,
     91	SI476X_IDX_PHDIV_PRIMARY_ANTENNA,
     92	SI476X_IDX_PHDIV_SECONDARY_ANTENNA,
     93	SI476X_IDX_PHDIV_SECONDARY_COMBINING,
     94};
     95
     96static const char * const phase_diversity_modes[] = {
     97	[SI476X_IDX_PHDIV_DISABLED]		= "Disabled",
     98	[SI476X_IDX_PHDIV_PRIMARY_COMBINING]	= "Primary with Secondary",
     99	[SI476X_IDX_PHDIV_PRIMARY_ANTENNA]	= "Primary Antenna",
    100	[SI476X_IDX_PHDIV_SECONDARY_ANTENNA]	= "Secondary Antenna",
    101	[SI476X_IDX_PHDIV_SECONDARY_COMBINING]	= "Secondary with Primary",
    102};
    103
    104static inline enum phase_diversity_modes_idx
    105si476x_phase_diversity_mode_to_idx(enum si476x_phase_diversity_mode mode)
    106{
    107	switch (mode) {
    108	default:
    109		fallthrough;
    110	case SI476X_PHDIV_DISABLED:
    111		return SI476X_IDX_PHDIV_DISABLED;
    112	case SI476X_PHDIV_PRIMARY_COMBINING:
    113		return SI476X_IDX_PHDIV_PRIMARY_COMBINING;
    114	case SI476X_PHDIV_PRIMARY_ANTENNA:
    115		return SI476X_IDX_PHDIV_PRIMARY_ANTENNA;
    116	case SI476X_PHDIV_SECONDARY_ANTENNA:
    117		return SI476X_IDX_PHDIV_SECONDARY_ANTENNA;
    118	case SI476X_PHDIV_SECONDARY_COMBINING:
    119		return SI476X_IDX_PHDIV_SECONDARY_COMBINING;
    120	}
    121}
    122
    123static inline enum si476x_phase_diversity_mode
    124si476x_phase_diversity_idx_to_mode(enum phase_diversity_modes_idx idx)
    125{
    126	static const int idx_to_value[] = {
    127		[SI476X_IDX_PHDIV_DISABLED]		= SI476X_PHDIV_DISABLED,
    128		[SI476X_IDX_PHDIV_PRIMARY_COMBINING]	= SI476X_PHDIV_PRIMARY_COMBINING,
    129		[SI476X_IDX_PHDIV_PRIMARY_ANTENNA]	= SI476X_PHDIV_PRIMARY_ANTENNA,
    130		[SI476X_IDX_PHDIV_SECONDARY_ANTENNA]	= SI476X_PHDIV_SECONDARY_ANTENNA,
    131		[SI476X_IDX_PHDIV_SECONDARY_COMBINING]	= SI476X_PHDIV_SECONDARY_COMBINING,
    132	};
    133
    134	return idx_to_value[idx];
    135}
    136
    137static const struct v4l2_ctrl_ops si476x_ctrl_ops = {
    138	.g_volatile_ctrl	= si476x_radio_g_volatile_ctrl,
    139	.s_ctrl			= si476x_radio_s_ctrl,
    140};
    141
    142
    143enum si476x_ctrl_idx {
    144	SI476X_IDX_RSSI_THRESHOLD,
    145	SI476X_IDX_SNR_THRESHOLD,
    146	SI476X_IDX_MAX_TUNE_ERROR,
    147	SI476X_IDX_HARMONICS_COUNT,
    148	SI476X_IDX_DIVERSITY_MODE,
    149	SI476X_IDX_INTERCHIP_LINK,
    150};
    151static struct v4l2_ctrl_config si476x_ctrls[] = {
    152
    153	/*
    154	 * SI476X during its station seeking(or tuning) process uses several
    155	 * parameters to determine if "the station" is valid:
    156	 *
    157	 *	- Signal's SNR(in dBuV) must be lower than
    158	 *	#V4L2_CID_SI476X_SNR_THRESHOLD
    159	 *	- Signal's RSSI(in dBuV) must be greater than
    160	 *	#V4L2_CID_SI476X_RSSI_THRESHOLD
    161	 *	- Signal's frequency deviation(in units of 2ppm) must not be
    162	 *	more than #V4L2_CID_SI476X_MAX_TUNE_ERROR
    163	 */
    164	[SI476X_IDX_RSSI_THRESHOLD] = {
    165		.ops	= &si476x_ctrl_ops,
    166		.id	= V4L2_CID_SI476X_RSSI_THRESHOLD,
    167		.name	= "Valid RSSI Threshold",
    168		.type	= V4L2_CTRL_TYPE_INTEGER,
    169		.min	= -128,
    170		.max	= 127,
    171		.step	= 1,
    172	},
    173	[SI476X_IDX_SNR_THRESHOLD] = {
    174		.ops	= &si476x_ctrl_ops,
    175		.id	= V4L2_CID_SI476X_SNR_THRESHOLD,
    176		.type	= V4L2_CTRL_TYPE_INTEGER,
    177		.name	= "Valid SNR Threshold",
    178		.min	= -128,
    179		.max	= 127,
    180		.step	= 1,
    181	},
    182	[SI476X_IDX_MAX_TUNE_ERROR] = {
    183		.ops	= &si476x_ctrl_ops,
    184		.id	= V4L2_CID_SI476X_MAX_TUNE_ERROR,
    185		.type	= V4L2_CTRL_TYPE_INTEGER,
    186		.name	= "Max Tune Errors",
    187		.min	= 0,
    188		.max	= 126 * 2,
    189		.step	= 2,
    190	},
    191
    192	/*
    193	 * #V4L2_CID_SI476X_HARMONICS_COUNT -- number of harmonics
    194	 * built-in power-line noise supression filter is to reject
    195	 * during AM-mode operation.
    196	 */
    197	[SI476X_IDX_HARMONICS_COUNT] = {
    198		.ops	= &si476x_ctrl_ops,
    199		.id	= V4L2_CID_SI476X_HARMONICS_COUNT,
    200		.type	= V4L2_CTRL_TYPE_INTEGER,
    201
    202		.name	= "Count of Harmonics to Reject",
    203		.min	= 0,
    204		.max	= 20,
    205		.step	= 1,
    206	},
    207
    208	/*
    209	 * #V4L2_CID_SI476X_DIVERSITY_MODE -- configuration which
    210	 * two tuners working in diversity mode are to work in.
    211	 *
    212	 *  - #SI476X_IDX_PHDIV_DISABLED diversity mode disabled
    213	 *  - #SI476X_IDX_PHDIV_PRIMARY_COMBINING diversity mode is
    214	 *  on, primary tuner's antenna is the main one.
    215	 *  - #SI476X_IDX_PHDIV_PRIMARY_ANTENNA diversity mode is
    216	 *  off, primary tuner's antenna is the main one.
    217	 *  - #SI476X_IDX_PHDIV_SECONDARY_ANTENNA diversity mode is
    218	 *  off, secondary tuner's antenna is the main one.
    219	 *  - #SI476X_IDX_PHDIV_SECONDARY_COMBINING diversity mode is
    220	 *  on, secondary tuner's antenna is the main one.
    221	 */
    222	[SI476X_IDX_DIVERSITY_MODE] = {
    223		.ops	= &si476x_ctrl_ops,
    224		.id	= V4L2_CID_SI476X_DIVERSITY_MODE,
    225		.type	= V4L2_CTRL_TYPE_MENU,
    226		.name	= "Phase Diversity Mode",
    227		.qmenu	= phase_diversity_modes,
    228		.min	= 0,
    229		.max	= ARRAY_SIZE(phase_diversity_modes) - 1,
    230	},
    231
    232	/*
    233	 * #V4L2_CID_SI476X_INTERCHIP_LINK -- inter-chip link in
    234	 * diversity mode indicator. Allows user to determine if two
    235	 * chips working in diversity mode have established a link
    236	 * between each other and if the system as a whole uses
    237	 * signals from both antennas to receive FM radio.
    238	 */
    239	[SI476X_IDX_INTERCHIP_LINK] = {
    240		.ops	= &si476x_ctrl_ops,
    241		.id	= V4L2_CID_SI476X_INTERCHIP_LINK,
    242		.type	= V4L2_CTRL_TYPE_BOOLEAN,
    243		.flags  = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE,
    244		.name	= "Inter-Chip Link",
    245		.min	= 0,
    246		.max	= 1,
    247		.step	= 1,
    248	},
    249};
    250
    251struct si476x_radio;
    252
    253/**
    254 * struct si476x_radio_ops - vtable of tuner functions
    255 *
    256 * This table holds pointers to functions implementing particular
    257 * operations depending on the mode in which the tuner chip was
    258 * configured to start. If the function is not supported
    259 * corresponding element is set to #NULL.
    260 *
    261 * @tune_freq: Tune chip to a specific frequency
    262 * @seek_start: Star station seeking
    263 * @rsq_status: Get Received Signal Quality(RSQ) status
    264 * @rds_blckcnt: Get received RDS blocks count
    265 * @phase_diversity: Change phase diversity mode of the tuner
    266 * @phase_div_status: Get phase diversity mode status
    267 * @acf_status: Get the status of Automatically Controlled
    268 * Features(ACF)
    269 * @agc_status: Get Automatic Gain Control(AGC) status
    270 */
    271struct si476x_radio_ops {
    272	int (*tune_freq)(struct si476x_core *, struct si476x_tune_freq_args *);
    273	int (*seek_start)(struct si476x_core *, bool, bool);
    274	int (*rsq_status)(struct si476x_core *, struct si476x_rsq_status_args *,
    275			  struct si476x_rsq_status_report *);
    276	int (*rds_blckcnt)(struct si476x_core *, bool,
    277			   struct si476x_rds_blockcount_report *);
    278
    279	int (*phase_diversity)(struct si476x_core *,
    280			       enum si476x_phase_diversity_mode);
    281	int (*phase_div_status)(struct si476x_core *);
    282	int (*acf_status)(struct si476x_core *,
    283			  struct si476x_acf_status_report *);
    284	int (*agc_status)(struct si476x_core *,
    285			  struct si476x_agc_status_report *);
    286};
    287
    288/**
    289 * struct si476x_radio - radio device
    290 *
    291 * @v4l2dev: Pointer to V4L2 device created by V4L2 subsystem
    292 * @videodev: Pointer to video device created by V4L2 subsystem
    293 * @ctrl_handler: V4L2 controls handler
    294 * @core: Pointer to underlying core device
    295 * @ops: Vtable of functions. See struct si476x_radio_ops for details
    296 * @debugfs: pointer to &strucd dentry for debugfs
    297 * @audmode: audio mode, as defined for the rxsubchans field
    298 *	     at videodev2.h
    299 *
    300 * core structure is the radio device is being used
    301 */
    302struct si476x_radio {
    303	struct v4l2_device v4l2dev;
    304	struct video_device videodev;
    305	struct v4l2_ctrl_handler ctrl_handler;
    306
    307	struct si476x_core  *core;
    308	/* This field should not be accesses unless core lock is held */
    309	const struct si476x_radio_ops *ops;
    310
    311	struct dentry	*debugfs;
    312	u32 audmode;
    313};
    314
    315static inline struct si476x_radio *
    316v4l2_ctrl_handler_to_radio(struct v4l2_ctrl_handler *d)
    317{
    318	return container_of(d, struct si476x_radio, ctrl_handler);
    319}
    320
    321/*
    322 * si476x_vidioc_querycap - query device capabilities
    323 */
    324static int si476x_radio_querycap(struct file *file, void *priv,
    325				 struct v4l2_capability *capability)
    326{
    327	struct si476x_radio *radio = video_drvdata(file);
    328
    329	strscpy(capability->driver, radio->v4l2dev.name,
    330		sizeof(capability->driver));
    331	strscpy(capability->card,   DRIVER_CARD, sizeof(capability->card));
    332	snprintf(capability->bus_info, sizeof(capability->bus_info),
    333		 "platform:%s", radio->v4l2dev.name);
    334	return 0;
    335}
    336
    337static int si476x_radio_enum_freq_bands(struct file *file, void *priv,
    338					struct v4l2_frequency_band *band)
    339{
    340	int err;
    341	struct si476x_radio *radio = video_drvdata(file);
    342
    343	if (band->tuner != 0)
    344		return -EINVAL;
    345
    346	switch (radio->core->chip_id) {
    347		/* AM/FM tuners -- all bands are supported */
    348	case SI476X_CHIP_SI4761:
    349	case SI476X_CHIP_SI4764:
    350		if (band->index < ARRAY_SIZE(si476x_bands)) {
    351			*band = si476x_bands[band->index];
    352			err = 0;
    353		} else {
    354			err = -EINVAL;
    355		}
    356		break;
    357		/* FM companion tuner chips -- only FM bands are
    358		 * supported */
    359	case SI476X_CHIP_SI4768:
    360		if (band->index == SI476X_BAND_FM) {
    361			*band = si476x_bands[band->index];
    362			err = 0;
    363		} else {
    364			err = -EINVAL;
    365		}
    366		break;
    367	default:
    368		err = -EINVAL;
    369	}
    370
    371	return err;
    372}
    373
    374static int si476x_radio_g_tuner(struct file *file, void *priv,
    375				struct v4l2_tuner *tuner)
    376{
    377	int err;
    378	struct si476x_rsq_status_report report;
    379	struct si476x_radio *radio = video_drvdata(file);
    380
    381	struct si476x_rsq_status_args args = {
    382		.primary	= false,
    383		.rsqack		= false,
    384		.attune		= false,
    385		.cancel		= false,
    386		.stcack		= false,
    387	};
    388
    389	if (tuner->index != 0)
    390		return -EINVAL;
    391
    392	tuner->type       = V4L2_TUNER_RADIO;
    393	tuner->capability = V4L2_TUNER_CAP_LOW /* Measure frequencies
    394						 * in multiples of
    395						 * 62.5 Hz */
    396		| V4L2_TUNER_CAP_STEREO
    397		| V4L2_TUNER_CAP_HWSEEK_BOUNDED
    398		| V4L2_TUNER_CAP_HWSEEK_WRAP
    399		| V4L2_TUNER_CAP_HWSEEK_PROG_LIM;
    400
    401	si476x_core_lock(radio->core);
    402
    403	if (si476x_core_is_a_secondary_tuner(radio->core)) {
    404		strscpy(tuner->name, "FM (secondary)", sizeof(tuner->name));
    405		tuner->rxsubchans = 0;
    406		tuner->rangelow = si476x_bands[SI476X_BAND_FM].rangelow;
    407	} else if (si476x_core_has_am(radio->core)) {
    408		if (si476x_core_is_a_primary_tuner(radio->core))
    409			strscpy(tuner->name, "AM/FM (primary)",
    410				sizeof(tuner->name));
    411		else
    412			strscpy(tuner->name, "AM/FM", sizeof(tuner->name));
    413
    414		tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO
    415			| V4L2_TUNER_SUB_RDS;
    416		tuner->capability |= V4L2_TUNER_CAP_RDS
    417			| V4L2_TUNER_CAP_RDS_BLOCK_IO
    418			| V4L2_TUNER_CAP_FREQ_BANDS;
    419
    420		tuner->rangelow = si476x_bands[SI476X_BAND_AM].rangelow;
    421	} else {
    422		strscpy(tuner->name, "FM", sizeof(tuner->name));
    423		tuner->rxsubchans = V4L2_TUNER_SUB_RDS;
    424		tuner->capability |= V4L2_TUNER_CAP_RDS
    425			| V4L2_TUNER_CAP_RDS_BLOCK_IO
    426			| V4L2_TUNER_CAP_FREQ_BANDS;
    427		tuner->rangelow = si476x_bands[SI476X_BAND_FM].rangelow;
    428	}
    429
    430	tuner->audmode = radio->audmode;
    431
    432	tuner->afc = 1;
    433	tuner->rangehigh = si476x_bands[SI476X_BAND_FM].rangehigh;
    434
    435	err = radio->ops->rsq_status(radio->core,
    436				     &args, &report);
    437	if (err < 0) {
    438		tuner->signal = 0;
    439	} else {
    440		/*
    441		 * tuner->signal value range: 0x0000 .. 0xFFFF,
    442		 * report.rssi: -128 .. 127
    443		 */
    444		tuner->signal = (report.rssi + 128) * 257;
    445	}
    446	si476x_core_unlock(radio->core);
    447
    448	return err;
    449}
    450
    451static int si476x_radio_s_tuner(struct file *file, void *priv,
    452				const struct v4l2_tuner *tuner)
    453{
    454	struct si476x_radio *radio = video_drvdata(file);
    455
    456	if (tuner->index != 0)
    457		return -EINVAL;
    458
    459	if (tuner->audmode == V4L2_TUNER_MODE_MONO ||
    460	    tuner->audmode == V4L2_TUNER_MODE_STEREO)
    461		radio->audmode = tuner->audmode;
    462	else
    463		radio->audmode = V4L2_TUNER_MODE_STEREO;
    464
    465	return 0;
    466}
    467
    468static int si476x_radio_init_vtable(struct si476x_radio *radio,
    469				    enum si476x_func func)
    470{
    471	static const struct si476x_radio_ops fm_ops = {
    472		.tune_freq		= si476x_core_cmd_fm_tune_freq,
    473		.seek_start		= si476x_core_cmd_fm_seek_start,
    474		.rsq_status		= si476x_core_cmd_fm_rsq_status,
    475		.rds_blckcnt		= si476x_core_cmd_fm_rds_blockcount,
    476		.phase_diversity	= si476x_core_cmd_fm_phase_diversity,
    477		.phase_div_status	= si476x_core_cmd_fm_phase_div_status,
    478		.acf_status		= si476x_core_cmd_fm_acf_status,
    479		.agc_status		= si476x_core_cmd_agc_status,
    480	};
    481
    482	static const struct si476x_radio_ops am_ops = {
    483		.tune_freq		= si476x_core_cmd_am_tune_freq,
    484		.seek_start		= si476x_core_cmd_am_seek_start,
    485		.rsq_status		= si476x_core_cmd_am_rsq_status,
    486		.rds_blckcnt		= NULL,
    487		.phase_diversity	= NULL,
    488		.phase_div_status	= NULL,
    489		.acf_status		= si476x_core_cmd_am_acf_status,
    490		.agc_status		= NULL,
    491	};
    492
    493	switch (func) {
    494	case SI476X_FUNC_FM_RECEIVER:
    495		radio->ops = &fm_ops;
    496		return 0;
    497
    498	case SI476X_FUNC_AM_RECEIVER:
    499		radio->ops = &am_ops;
    500		return 0;
    501	default:
    502		WARN(1, "Unexpected tuner function value\n");
    503		return -EINVAL;
    504	}
    505}
    506
    507static int si476x_radio_pretune(struct si476x_radio *radio,
    508				enum si476x_func func)
    509{
    510	int retval;
    511
    512	struct si476x_tune_freq_args args = {
    513		.zifsr		= false,
    514		.hd		= false,
    515		.injside	= SI476X_INJSIDE_AUTO,
    516		.tunemode	= SI476X_TM_VALIDATED_NORMAL_TUNE,
    517		.smoothmetrics	= SI476X_SM_INITIALIZE_AUDIO,
    518		.antcap		= 0,
    519	};
    520
    521	switch (func) {
    522	case SI476X_FUNC_FM_RECEIVER:
    523		args.freq = v4l2_to_si476x(radio->core,
    524					   92 * FREQ_MUL);
    525		retval = radio->ops->tune_freq(radio->core, &args);
    526		break;
    527	case SI476X_FUNC_AM_RECEIVER:
    528		args.freq = v4l2_to_si476x(radio->core,
    529					   0.6 * FREQ_MUL);
    530		retval = radio->ops->tune_freq(radio->core, &args);
    531		break;
    532	default:
    533		WARN(1, "Unexpected tuner function value\n");
    534		retval = -EINVAL;
    535	}
    536
    537	return retval;
    538}
    539static int si476x_radio_do_post_powerup_init(struct si476x_radio *radio,
    540					     enum si476x_func func)
    541{
    542	int err;
    543
    544	/* regcache_mark_dirty(radio->core->regmap); */
    545	err = regcache_sync_region(radio->core->regmap,
    546				   SI476X_PROP_DIGITAL_IO_INPUT_SAMPLE_RATE,
    547				   SI476X_PROP_DIGITAL_IO_OUTPUT_FORMAT);
    548	if (err < 0)
    549		return err;
    550
    551	err = regcache_sync_region(radio->core->regmap,
    552				   SI476X_PROP_AUDIO_DEEMPHASIS,
    553				   SI476X_PROP_AUDIO_PWR_LINE_FILTER);
    554	if (err < 0)
    555		return err;
    556
    557	err = regcache_sync_region(radio->core->regmap,
    558				   SI476X_PROP_INT_CTL_ENABLE,
    559				   SI476X_PROP_INT_CTL_ENABLE);
    560	if (err < 0)
    561		return err;
    562
    563	/*
    564	 * Is there any point in restoring SNR and the like
    565	 * when switching between AM/FM?
    566	 */
    567	err = regcache_sync_region(radio->core->regmap,
    568				   SI476X_PROP_VALID_MAX_TUNE_ERROR,
    569				   SI476X_PROP_VALID_MAX_TUNE_ERROR);
    570	if (err < 0)
    571		return err;
    572
    573	err = regcache_sync_region(radio->core->regmap,
    574				   SI476X_PROP_VALID_SNR_THRESHOLD,
    575				   SI476X_PROP_VALID_RSSI_THRESHOLD);
    576	if (err < 0)
    577		return err;
    578
    579	if (func == SI476X_FUNC_FM_RECEIVER) {
    580		if (si476x_core_has_diversity(radio->core)) {
    581			err = si476x_core_cmd_fm_phase_diversity(radio->core,
    582								 radio->core->diversity_mode);
    583			if (err < 0)
    584				return err;
    585		}
    586
    587		err = regcache_sync_region(radio->core->regmap,
    588					   SI476X_PROP_FM_RDS_INTERRUPT_SOURCE,
    589					   SI476X_PROP_FM_RDS_CONFIG);
    590		if (err < 0)
    591			return err;
    592	}
    593
    594	return si476x_radio_init_vtable(radio, func);
    595
    596}
    597
    598static int si476x_radio_change_func(struct si476x_radio *radio,
    599				    enum si476x_func func)
    600{
    601	int err;
    602	bool soft;
    603	/*
    604	 * Since power/up down is a very time consuming operation,
    605	 * try to avoid doing it if the requested mode matches the one
    606	 * the tuner is in
    607	 */
    608	if (func == radio->core->power_up_parameters.func)
    609		return 0;
    610
    611	soft = true;
    612	err = si476x_core_stop(radio->core, soft);
    613	if (err < 0) {
    614		/*
    615		 * OK, if the chip does not want to play nice let's
    616		 * try to reset it in more brutal way
    617		 */
    618		soft = false;
    619		err = si476x_core_stop(radio->core, soft);
    620		if (err < 0)
    621			return err;
    622	}
    623	/*
    624	  Set the desired radio tuner function
    625	 */
    626	radio->core->power_up_parameters.func = func;
    627
    628	err = si476x_core_start(radio->core, soft);
    629	if (err < 0)
    630		return err;
    631
    632	/*
    633	 * No need to do the rest of manipulations for the bootlader
    634	 * mode
    635	 */
    636	if (func != SI476X_FUNC_FM_RECEIVER &&
    637	    func != SI476X_FUNC_AM_RECEIVER)
    638		return err;
    639
    640	return si476x_radio_do_post_powerup_init(radio, func);
    641}
    642
    643static int si476x_radio_g_frequency(struct file *file, void *priv,
    644			      struct v4l2_frequency *f)
    645{
    646	int err;
    647	struct si476x_radio *radio = video_drvdata(file);
    648
    649	if (f->tuner != 0 ||
    650	    f->type  != V4L2_TUNER_RADIO)
    651		return -EINVAL;
    652
    653	si476x_core_lock(radio->core);
    654
    655	if (radio->ops->rsq_status) {
    656		struct si476x_rsq_status_report report;
    657		struct si476x_rsq_status_args   args = {
    658			.primary	= false,
    659			.rsqack		= false,
    660			.attune		= true,
    661			.cancel		= false,
    662			.stcack		= false,
    663		};
    664
    665		err = radio->ops->rsq_status(radio->core, &args, &report);
    666		if (!err)
    667			f->frequency = si476x_to_v4l2(radio->core,
    668						      report.readfreq);
    669	} else {
    670		err = -EINVAL;
    671	}
    672
    673	si476x_core_unlock(radio->core);
    674
    675	return err;
    676}
    677
    678static int si476x_radio_s_frequency(struct file *file, void *priv,
    679				    const struct v4l2_frequency *f)
    680{
    681	int err;
    682	u32 freq = f->frequency;
    683	struct si476x_tune_freq_args args;
    684	struct si476x_radio *radio = video_drvdata(file);
    685
    686	const u32 midrange = (si476x_bands[SI476X_BAND_AM].rangehigh +
    687			      si476x_bands[SI476X_BAND_FM].rangelow) / 2;
    688	const int band = (freq > midrange) ?
    689		SI476X_BAND_FM : SI476X_BAND_AM;
    690	const enum si476x_func func = (band == SI476X_BAND_AM) ?
    691		SI476X_FUNC_AM_RECEIVER : SI476X_FUNC_FM_RECEIVER;
    692
    693	if (f->tuner != 0 ||
    694	    f->type  != V4L2_TUNER_RADIO)
    695		return -EINVAL;
    696
    697	si476x_core_lock(radio->core);
    698
    699	freq = clamp(freq,
    700		     si476x_bands[band].rangelow,
    701		     si476x_bands[band].rangehigh);
    702
    703	if (si476x_radio_freq_is_inside_of_the_band(freq,
    704						    SI476X_BAND_AM) &&
    705	    (!si476x_core_has_am(radio->core) ||
    706	     si476x_core_is_a_secondary_tuner(radio->core))) {
    707		err = -EINVAL;
    708		goto unlock;
    709	}
    710
    711	err = si476x_radio_change_func(radio, func);
    712	if (err < 0)
    713		goto unlock;
    714
    715	args.zifsr		= false;
    716	args.hd			= false;
    717	args.injside		= SI476X_INJSIDE_AUTO;
    718	args.freq		= v4l2_to_si476x(radio->core, freq);
    719	args.tunemode		= SI476X_TM_VALIDATED_NORMAL_TUNE;
    720	args.smoothmetrics	= SI476X_SM_INITIALIZE_AUDIO;
    721	args.antcap		= 0;
    722
    723	err = radio->ops->tune_freq(radio->core, &args);
    724
    725unlock:
    726	si476x_core_unlock(radio->core);
    727	return err;
    728}
    729
    730static int si476x_radio_s_hw_freq_seek(struct file *file, void *priv,
    731				       const struct v4l2_hw_freq_seek *seek)
    732{
    733	int err;
    734	enum si476x_func func;
    735	u32 rangelow = seek->rangelow, rangehigh = seek->rangehigh;
    736	struct si476x_radio *radio = video_drvdata(file);
    737
    738	if (file->f_flags & O_NONBLOCK)
    739		return -EAGAIN;
    740
    741	if (seek->tuner != 0 ||
    742	    seek->type  != V4L2_TUNER_RADIO)
    743		return -EINVAL;
    744
    745	si476x_core_lock(radio->core);
    746
    747	if (!rangelow) {
    748		err = regmap_read(radio->core->regmap,
    749				  SI476X_PROP_SEEK_BAND_BOTTOM,
    750				  &rangelow);
    751		if (err)
    752			goto unlock;
    753		rangelow = si476x_to_v4l2(radio->core, rangelow);
    754	}
    755	if (!rangehigh) {
    756		err = regmap_read(radio->core->regmap,
    757				  SI476X_PROP_SEEK_BAND_TOP,
    758				  &rangehigh);
    759		if (err)
    760			goto unlock;
    761		rangehigh = si476x_to_v4l2(radio->core, rangehigh);
    762	}
    763
    764	if (rangelow > rangehigh) {
    765		err = -EINVAL;
    766		goto unlock;
    767	}
    768
    769	if (si476x_radio_range_is_inside_of_the_band(rangelow, rangehigh,
    770						     SI476X_BAND_FM)) {
    771		func = SI476X_FUNC_FM_RECEIVER;
    772
    773	} else if (si476x_core_has_am(radio->core) &&
    774		   si476x_radio_range_is_inside_of_the_band(rangelow, rangehigh,
    775							    SI476X_BAND_AM)) {
    776		func = SI476X_FUNC_AM_RECEIVER;
    777	} else {
    778		err = -EINVAL;
    779		goto unlock;
    780	}
    781
    782	err = si476x_radio_change_func(radio, func);
    783	if (err < 0)
    784		goto unlock;
    785
    786	if (seek->rangehigh) {
    787		err = regmap_write(radio->core->regmap,
    788				   SI476X_PROP_SEEK_BAND_TOP,
    789				   v4l2_to_si476x(radio->core,
    790						  seek->rangehigh));
    791		if (err)
    792			goto unlock;
    793	}
    794	if (seek->rangelow) {
    795		err = regmap_write(radio->core->regmap,
    796				   SI476X_PROP_SEEK_BAND_BOTTOM,
    797				   v4l2_to_si476x(radio->core,
    798						  seek->rangelow));
    799		if (err)
    800			goto unlock;
    801	}
    802	if (seek->spacing) {
    803		err = regmap_write(radio->core->regmap,
    804				     SI476X_PROP_SEEK_FREQUENCY_SPACING,
    805				     v4l2_to_si476x(radio->core,
    806						    seek->spacing));
    807		if (err)
    808			goto unlock;
    809	}
    810
    811	err = radio->ops->seek_start(radio->core,
    812				     seek->seek_upward,
    813				     seek->wrap_around);
    814unlock:
    815	si476x_core_unlock(radio->core);
    816
    817
    818
    819	return err;
    820}
    821
    822static int si476x_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
    823{
    824	int retval;
    825	struct si476x_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler);
    826
    827	si476x_core_lock(radio->core);
    828
    829	switch (ctrl->id) {
    830	case V4L2_CID_SI476X_INTERCHIP_LINK:
    831		if (si476x_core_has_diversity(radio->core)) {
    832			if (radio->ops->phase_diversity) {
    833				retval = radio->ops->phase_div_status(radio->core);
    834				if (retval < 0)
    835					break;
    836
    837				ctrl->val = !!SI476X_PHDIV_STATUS_LINK_LOCKED(retval);
    838				retval = 0;
    839				break;
    840			} else {
    841				retval = -ENOTTY;
    842				break;
    843			}
    844		}
    845		retval = -EINVAL;
    846		break;
    847	default:
    848		retval = -EINVAL;
    849		break;
    850	}
    851	si476x_core_unlock(radio->core);
    852	return retval;
    853
    854}
    855
    856static int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl)
    857{
    858	int retval;
    859	enum si476x_phase_diversity_mode mode;
    860	struct si476x_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler);
    861
    862	si476x_core_lock(radio->core);
    863
    864	switch (ctrl->id) {
    865	case V4L2_CID_SI476X_HARMONICS_COUNT:
    866		retval = regmap_update_bits(radio->core->regmap,
    867					    SI476X_PROP_AUDIO_PWR_LINE_FILTER,
    868					    SI476X_PROP_PWR_HARMONICS_MASK,
    869					    ctrl->val);
    870		break;
    871	case V4L2_CID_POWER_LINE_FREQUENCY:
    872		switch (ctrl->val) {
    873		case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED:
    874			retval = regmap_update_bits(radio->core->regmap,
    875						    SI476X_PROP_AUDIO_PWR_LINE_FILTER,
    876						    SI476X_PROP_PWR_ENABLE_MASK,
    877						    0);
    878			break;
    879		case V4L2_CID_POWER_LINE_FREQUENCY_50HZ:
    880			retval = regmap_update_bits(radio->core->regmap,
    881						    SI476X_PROP_AUDIO_PWR_LINE_FILTER,
    882						    SI476X_PROP_PWR_GRID_MASK,
    883						    SI476X_PROP_PWR_GRID_50HZ);
    884			break;
    885		case V4L2_CID_POWER_LINE_FREQUENCY_60HZ:
    886			retval = regmap_update_bits(radio->core->regmap,
    887						    SI476X_PROP_AUDIO_PWR_LINE_FILTER,
    888						    SI476X_PROP_PWR_GRID_MASK,
    889						    SI476X_PROP_PWR_GRID_60HZ);
    890			break;
    891		default:
    892			retval = -EINVAL;
    893			break;
    894		}
    895		break;
    896	case V4L2_CID_SI476X_RSSI_THRESHOLD:
    897		retval = regmap_write(radio->core->regmap,
    898				      SI476X_PROP_VALID_RSSI_THRESHOLD,
    899				      ctrl->val);
    900		break;
    901	case V4L2_CID_SI476X_SNR_THRESHOLD:
    902		retval = regmap_write(radio->core->regmap,
    903				      SI476X_PROP_VALID_SNR_THRESHOLD,
    904				      ctrl->val);
    905		break;
    906	case V4L2_CID_SI476X_MAX_TUNE_ERROR:
    907		retval = regmap_write(radio->core->regmap,
    908				      SI476X_PROP_VALID_MAX_TUNE_ERROR,
    909				      ctrl->val);
    910		break;
    911	case V4L2_CID_RDS_RECEPTION:
    912		/*
    913		 * It looks like RDS related properties are
    914		 * inaccessible when tuner is in AM mode, so cache the
    915		 * changes
    916		 */
    917		if (si476x_core_is_in_am_receiver_mode(radio->core))
    918			regcache_cache_only(radio->core->regmap, true);
    919
    920		if (ctrl->val) {
    921			retval = regmap_write(radio->core->regmap,
    922					      SI476X_PROP_FM_RDS_INTERRUPT_FIFO_COUNT,
    923					      radio->core->rds_fifo_depth);
    924			if (retval < 0)
    925				break;
    926
    927			if (radio->core->client->irq) {
    928				retval = regmap_write(radio->core->regmap,
    929						      SI476X_PROP_FM_RDS_INTERRUPT_SOURCE,
    930						      SI476X_RDSRECV);
    931				if (retval < 0)
    932					break;
    933			}
    934
    935			/* Drain RDS FIFO before enabling RDS processing */
    936			retval = si476x_core_cmd_fm_rds_status(radio->core,
    937							       false,
    938							       true,
    939							       true,
    940							       NULL);
    941			if (retval < 0)
    942				break;
    943
    944			retval = regmap_update_bits(radio->core->regmap,
    945						    SI476X_PROP_FM_RDS_CONFIG,
    946						    SI476X_PROP_RDSEN_MASK,
    947						    SI476X_PROP_RDSEN);
    948		} else {
    949			retval = regmap_update_bits(radio->core->regmap,
    950						    SI476X_PROP_FM_RDS_CONFIG,
    951						    SI476X_PROP_RDSEN_MASK,
    952						    !SI476X_PROP_RDSEN);
    953		}
    954
    955		if (si476x_core_is_in_am_receiver_mode(radio->core))
    956			regcache_cache_only(radio->core->regmap, false);
    957		break;
    958	case V4L2_CID_TUNE_DEEMPHASIS:
    959		retval = regmap_write(radio->core->regmap,
    960				      SI476X_PROP_AUDIO_DEEMPHASIS,
    961				      ctrl->val);
    962		break;
    963
    964	case V4L2_CID_SI476X_DIVERSITY_MODE:
    965		mode = si476x_phase_diversity_idx_to_mode(ctrl->val);
    966
    967		if (mode == radio->core->diversity_mode) {
    968			retval = 0;
    969			break;
    970		}
    971
    972		if (si476x_core_is_in_am_receiver_mode(radio->core)) {
    973			/*
    974			 * Diversity cannot be configured while tuner
    975			 * is in AM mode so save the changes and carry on.
    976			 */
    977			radio->core->diversity_mode = mode;
    978			retval = 0;
    979		} else {
    980			retval = radio->ops->phase_diversity(radio->core, mode);
    981			if (!retval)
    982				radio->core->diversity_mode = mode;
    983		}
    984		break;
    985
    986	default:
    987		retval = -EINVAL;
    988		break;
    989	}
    990
    991	si476x_core_unlock(radio->core);
    992
    993	return retval;
    994}
    995
    996#ifdef CONFIG_VIDEO_ADV_DEBUG
    997static int si476x_radio_g_register(struct file *file, void *fh,
    998				   struct v4l2_dbg_register *reg)
    999{
   1000	int err;
   1001	unsigned int value;
   1002	struct si476x_radio *radio = video_drvdata(file);
   1003
   1004	si476x_core_lock(radio->core);
   1005	reg->size = 2;
   1006	err = regmap_read(radio->core->regmap,
   1007			  (unsigned int)reg->reg, &value);
   1008	reg->val = value;
   1009	si476x_core_unlock(radio->core);
   1010
   1011	return err;
   1012}
   1013static int si476x_radio_s_register(struct file *file, void *fh,
   1014				   const struct v4l2_dbg_register *reg)
   1015{
   1016
   1017	int err;
   1018	struct si476x_radio *radio = video_drvdata(file);
   1019
   1020	si476x_core_lock(radio->core);
   1021	err = regmap_write(radio->core->regmap,
   1022			   (unsigned int)reg->reg,
   1023			   (unsigned int)reg->val);
   1024	si476x_core_unlock(radio->core);
   1025
   1026	return err;
   1027}
   1028#endif
   1029
   1030static int si476x_radio_fops_open(struct file *file)
   1031{
   1032	struct si476x_radio *radio = video_drvdata(file);
   1033	int err;
   1034
   1035	err = v4l2_fh_open(file);
   1036	if (err)
   1037		return err;
   1038
   1039	if (v4l2_fh_is_singular_file(file)) {
   1040		si476x_core_lock(radio->core);
   1041		err = si476x_core_set_power_state(radio->core,
   1042						  SI476X_POWER_UP_FULL);
   1043		if (err < 0)
   1044			goto done;
   1045
   1046		err = si476x_radio_do_post_powerup_init(radio,
   1047							radio->core->power_up_parameters.func);
   1048		if (err < 0)
   1049			goto power_down;
   1050
   1051		err = si476x_radio_pretune(radio,
   1052					   radio->core->power_up_parameters.func);
   1053		if (err < 0)
   1054			goto power_down;
   1055
   1056		si476x_core_unlock(radio->core);
   1057		/*Must be done after si476x_core_unlock to prevent a deadlock*/
   1058		v4l2_ctrl_handler_setup(&radio->ctrl_handler);
   1059	}
   1060
   1061	return err;
   1062
   1063power_down:
   1064	si476x_core_set_power_state(radio->core,
   1065				    SI476X_POWER_DOWN);
   1066done:
   1067	si476x_core_unlock(radio->core);
   1068	v4l2_fh_release(file);
   1069
   1070	return err;
   1071}
   1072
   1073static int si476x_radio_fops_release(struct file *file)
   1074{
   1075	int err;
   1076	struct si476x_radio *radio = video_drvdata(file);
   1077
   1078	if (v4l2_fh_is_singular_file(file) &&
   1079	    atomic_read(&radio->core->is_alive))
   1080		si476x_core_set_power_state(radio->core,
   1081					    SI476X_POWER_DOWN);
   1082
   1083	err = v4l2_fh_release(file);
   1084
   1085	return err;
   1086}
   1087
   1088static ssize_t si476x_radio_fops_read(struct file *file, char __user *buf,
   1089				      size_t count, loff_t *ppos)
   1090{
   1091	ssize_t      rval;
   1092	size_t       fifo_len;
   1093	unsigned int copied;
   1094
   1095	struct si476x_radio *radio = video_drvdata(file);
   1096
   1097	/* block if no new data available */
   1098	if (kfifo_is_empty(&radio->core->rds_fifo)) {
   1099		if (file->f_flags & O_NONBLOCK)
   1100			return -EWOULDBLOCK;
   1101
   1102		rval = wait_event_interruptible(radio->core->rds_read_queue,
   1103						(!kfifo_is_empty(&radio->core->rds_fifo) ||
   1104						 !atomic_read(&radio->core->is_alive)));
   1105		if (rval < 0)
   1106			return -EINTR;
   1107
   1108		if (!atomic_read(&radio->core->is_alive))
   1109			return -ENODEV;
   1110	}
   1111
   1112	fifo_len = kfifo_len(&radio->core->rds_fifo);
   1113
   1114	if (kfifo_to_user(&radio->core->rds_fifo, buf,
   1115			  min(fifo_len, count),
   1116			  &copied) != 0) {
   1117		dev_warn(&radio->videodev.dev,
   1118			 "Error during FIFO to userspace copy\n");
   1119		rval = -EIO;
   1120	} else {
   1121		rval = (ssize_t)copied;
   1122	}
   1123
   1124	return rval;
   1125}
   1126
   1127static __poll_t si476x_radio_fops_poll(struct file *file,
   1128				struct poll_table_struct *pts)
   1129{
   1130	struct si476x_radio *radio = video_drvdata(file);
   1131	__poll_t req_events = poll_requested_events(pts);
   1132	__poll_t err = v4l2_ctrl_poll(file, pts);
   1133
   1134	if (req_events & (EPOLLIN | EPOLLRDNORM)) {
   1135		if (atomic_read(&radio->core->is_alive))
   1136			poll_wait(file, &radio->core->rds_read_queue, pts);
   1137
   1138		if (!atomic_read(&radio->core->is_alive))
   1139			err = EPOLLHUP;
   1140
   1141		if (!kfifo_is_empty(&radio->core->rds_fifo))
   1142			err = EPOLLIN | EPOLLRDNORM;
   1143	}
   1144
   1145	return err;
   1146}
   1147
   1148static const struct v4l2_file_operations si476x_fops = {
   1149	.owner			= THIS_MODULE,
   1150	.read			= si476x_radio_fops_read,
   1151	.poll			= si476x_radio_fops_poll,
   1152	.unlocked_ioctl		= video_ioctl2,
   1153	.open			= si476x_radio_fops_open,
   1154	.release		= si476x_radio_fops_release,
   1155};
   1156
   1157
   1158static const struct v4l2_ioctl_ops si4761_ioctl_ops = {
   1159	.vidioc_querycap		= si476x_radio_querycap,
   1160	.vidioc_g_tuner			= si476x_radio_g_tuner,
   1161	.vidioc_s_tuner			= si476x_radio_s_tuner,
   1162
   1163	.vidioc_g_frequency		= si476x_radio_g_frequency,
   1164	.vidioc_s_frequency		= si476x_radio_s_frequency,
   1165	.vidioc_s_hw_freq_seek		= si476x_radio_s_hw_freq_seek,
   1166	.vidioc_enum_freq_bands		= si476x_radio_enum_freq_bands,
   1167
   1168	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
   1169	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
   1170
   1171#ifdef CONFIG_VIDEO_ADV_DEBUG
   1172	.vidioc_g_register		= si476x_radio_g_register,
   1173	.vidioc_s_register		= si476x_radio_s_register,
   1174#endif
   1175};
   1176
   1177
   1178static const struct video_device si476x_viddev_template = {
   1179	.fops			= &si476x_fops,
   1180	.name			= DRIVER_NAME,
   1181	.release		= video_device_release_empty,
   1182};
   1183
   1184
   1185
   1186static ssize_t si476x_radio_read_acf_blob(struct file *file,
   1187					  char __user *user_buf,
   1188					  size_t count, loff_t *ppos)
   1189{
   1190	int err;
   1191	struct si476x_radio *radio = file->private_data;
   1192	struct si476x_acf_status_report report;
   1193
   1194	si476x_core_lock(radio->core);
   1195	if (radio->ops->acf_status)
   1196		err = radio->ops->acf_status(radio->core, &report);
   1197	else
   1198		err = -ENOENT;
   1199	si476x_core_unlock(radio->core);
   1200
   1201	if (err < 0)
   1202		return err;
   1203
   1204	return simple_read_from_buffer(user_buf, count, ppos, &report,
   1205				       sizeof(report));
   1206}
   1207
   1208static const struct file_operations radio_acf_fops = {
   1209	.open	= simple_open,
   1210	.llseek = default_llseek,
   1211	.read	= si476x_radio_read_acf_blob,
   1212};
   1213
   1214static ssize_t si476x_radio_read_rds_blckcnt_blob(struct file *file,
   1215						  char __user *user_buf,
   1216						  size_t count, loff_t *ppos)
   1217{
   1218	int err;
   1219	struct si476x_radio *radio = file->private_data;
   1220	struct si476x_rds_blockcount_report report;
   1221
   1222	si476x_core_lock(radio->core);
   1223	if (radio->ops->rds_blckcnt)
   1224		err = radio->ops->rds_blckcnt(radio->core, true,
   1225					       &report);
   1226	else
   1227		err = -ENOENT;
   1228	si476x_core_unlock(radio->core);
   1229
   1230	if (err < 0)
   1231		return err;
   1232
   1233	return simple_read_from_buffer(user_buf, count, ppos, &report,
   1234				       sizeof(report));
   1235}
   1236
   1237static const struct file_operations radio_rds_blckcnt_fops = {
   1238	.open	= simple_open,
   1239	.llseek = default_llseek,
   1240	.read	= si476x_radio_read_rds_blckcnt_blob,
   1241};
   1242
   1243static ssize_t si476x_radio_read_agc_blob(struct file *file,
   1244					  char __user *user_buf,
   1245					  size_t count, loff_t *ppos)
   1246{
   1247	int err;
   1248	struct si476x_radio *radio = file->private_data;
   1249	struct si476x_agc_status_report report;
   1250
   1251	si476x_core_lock(radio->core);
   1252	if (radio->ops->rds_blckcnt)
   1253		err = radio->ops->agc_status(radio->core, &report);
   1254	else
   1255		err = -ENOENT;
   1256	si476x_core_unlock(radio->core);
   1257
   1258	if (err < 0)
   1259		return err;
   1260
   1261	return simple_read_from_buffer(user_buf, count, ppos, &report,
   1262				       sizeof(report));
   1263}
   1264
   1265static const struct file_operations radio_agc_fops = {
   1266	.open	= simple_open,
   1267	.llseek = default_llseek,
   1268	.read	= si476x_radio_read_agc_blob,
   1269};
   1270
   1271static ssize_t si476x_radio_read_rsq_blob(struct file *file,
   1272					  char __user *user_buf,
   1273					  size_t count, loff_t *ppos)
   1274{
   1275	int err;
   1276	struct si476x_radio *radio = file->private_data;
   1277	struct si476x_rsq_status_report report;
   1278	struct si476x_rsq_status_args args = {
   1279		.primary	= false,
   1280		.rsqack		= false,
   1281		.attune		= false,
   1282		.cancel		= false,
   1283		.stcack		= false,
   1284	};
   1285
   1286	si476x_core_lock(radio->core);
   1287	if (radio->ops->rds_blckcnt)
   1288		err = radio->ops->rsq_status(radio->core, &args, &report);
   1289	else
   1290		err = -ENOENT;
   1291	si476x_core_unlock(radio->core);
   1292
   1293	if (err < 0)
   1294		return err;
   1295
   1296	return simple_read_from_buffer(user_buf, count, ppos, &report,
   1297				       sizeof(report));
   1298}
   1299
   1300static const struct file_operations radio_rsq_fops = {
   1301	.open	= simple_open,
   1302	.llseek = default_llseek,
   1303	.read	= si476x_radio_read_rsq_blob,
   1304};
   1305
   1306static ssize_t si476x_radio_read_rsq_primary_blob(struct file *file,
   1307						  char __user *user_buf,
   1308						  size_t count, loff_t *ppos)
   1309{
   1310	int err;
   1311	struct si476x_radio *radio = file->private_data;
   1312	struct si476x_rsq_status_report report;
   1313	struct si476x_rsq_status_args args = {
   1314		.primary	= true,
   1315		.rsqack		= false,
   1316		.attune		= false,
   1317		.cancel		= false,
   1318		.stcack		= false,
   1319	};
   1320
   1321	si476x_core_lock(radio->core);
   1322	if (radio->ops->rds_blckcnt)
   1323		err = radio->ops->rsq_status(radio->core, &args, &report);
   1324	else
   1325		err = -ENOENT;
   1326	si476x_core_unlock(radio->core);
   1327
   1328	if (err < 0)
   1329		return err;
   1330
   1331	return simple_read_from_buffer(user_buf, count, ppos, &report,
   1332				       sizeof(report));
   1333}
   1334
   1335static const struct file_operations radio_rsq_primary_fops = {
   1336	.open	= simple_open,
   1337	.llseek = default_llseek,
   1338	.read	= si476x_radio_read_rsq_primary_blob,
   1339};
   1340
   1341
   1342static void si476x_radio_init_debugfs(struct si476x_radio *radio)
   1343{
   1344	radio->debugfs = debugfs_create_dir(dev_name(radio->v4l2dev.dev), NULL);
   1345
   1346	debugfs_create_file("acf", S_IRUGO, radio->debugfs, radio,
   1347			    &radio_acf_fops);
   1348
   1349	debugfs_create_file("rds_blckcnt", S_IRUGO, radio->debugfs, radio,
   1350			    &radio_rds_blckcnt_fops);
   1351
   1352	debugfs_create_file("agc", S_IRUGO, radio->debugfs, radio,
   1353			    &radio_agc_fops);
   1354
   1355	debugfs_create_file("rsq", S_IRUGO, radio->debugfs, radio,
   1356			    &radio_rsq_fops);
   1357
   1358	debugfs_create_file("rsq_primary", S_IRUGO, radio->debugfs, radio,
   1359			    &radio_rsq_primary_fops);
   1360}
   1361
   1362
   1363static int si476x_radio_add_new_custom(struct si476x_radio *radio,
   1364				       enum si476x_ctrl_idx idx)
   1365{
   1366	int rval;
   1367	struct v4l2_ctrl *ctrl;
   1368
   1369	ctrl = v4l2_ctrl_new_custom(&radio->ctrl_handler,
   1370				    &si476x_ctrls[idx],
   1371				    NULL);
   1372	rval = radio->ctrl_handler.error;
   1373	if (ctrl == NULL && rval)
   1374		dev_err(radio->v4l2dev.dev,
   1375			"Could not initialize '%s' control %d\n",
   1376			si476x_ctrls[idx].name, rval);
   1377
   1378	return rval;
   1379}
   1380
   1381static int si476x_radio_probe(struct platform_device *pdev)
   1382{
   1383	int rval;
   1384	struct si476x_radio *radio;
   1385	struct v4l2_ctrl *ctrl;
   1386
   1387	static atomic_t instance = ATOMIC_INIT(0);
   1388
   1389	radio = devm_kzalloc(&pdev->dev, sizeof(*radio), GFP_KERNEL);
   1390	if (!radio)
   1391		return -ENOMEM;
   1392
   1393	radio->core = i2c_mfd_cell_to_core(&pdev->dev);
   1394
   1395	v4l2_device_set_name(&radio->v4l2dev, DRIVER_NAME, &instance);
   1396
   1397	rval = v4l2_device_register(&pdev->dev, &radio->v4l2dev);
   1398	if (rval) {
   1399		dev_err(&pdev->dev, "Cannot register v4l2_device.\n");
   1400		return rval;
   1401	}
   1402
   1403	memcpy(&radio->videodev, &si476x_viddev_template,
   1404	       sizeof(struct video_device));
   1405
   1406	radio->videodev.v4l2_dev  = &radio->v4l2dev;
   1407	radio->videodev.ioctl_ops = &si4761_ioctl_ops;
   1408	radio->videodev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
   1409				      V4L2_CAP_HW_FREQ_SEEK;
   1410
   1411	si476x_core_lock(radio->core);
   1412	if (!si476x_core_is_a_secondary_tuner(radio->core))
   1413		radio->videodev.device_caps |= V4L2_CAP_RDS_CAPTURE |
   1414					       V4L2_CAP_READWRITE;
   1415	si476x_core_unlock(radio->core);
   1416
   1417	video_set_drvdata(&radio->videodev, radio);
   1418	platform_set_drvdata(pdev, radio);
   1419
   1420
   1421	radio->v4l2dev.ctrl_handler = &radio->ctrl_handler;
   1422	v4l2_ctrl_handler_init(&radio->ctrl_handler,
   1423			       1 + ARRAY_SIZE(si476x_ctrls));
   1424
   1425	if (si476x_core_has_am(radio->core)) {
   1426		ctrl = v4l2_ctrl_new_std_menu(&radio->ctrl_handler,
   1427					      &si476x_ctrl_ops,
   1428					      V4L2_CID_POWER_LINE_FREQUENCY,
   1429					      V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
   1430					      0, 0);
   1431		rval = radio->ctrl_handler.error;
   1432		if (ctrl == NULL && rval) {
   1433			dev_err(&pdev->dev, "Could not initialize V4L2_CID_POWER_LINE_FREQUENCY control %d\n",
   1434				rval);
   1435			goto exit;
   1436		}
   1437
   1438		rval = si476x_radio_add_new_custom(radio,
   1439						   SI476X_IDX_HARMONICS_COUNT);
   1440		if (rval < 0)
   1441			goto exit;
   1442	}
   1443
   1444	rval = si476x_radio_add_new_custom(radio, SI476X_IDX_RSSI_THRESHOLD);
   1445	if (rval < 0)
   1446		goto exit;
   1447
   1448	rval = si476x_radio_add_new_custom(radio, SI476X_IDX_SNR_THRESHOLD);
   1449	if (rval < 0)
   1450		goto exit;
   1451
   1452	rval = si476x_radio_add_new_custom(radio, SI476X_IDX_MAX_TUNE_ERROR);
   1453	if (rval < 0)
   1454		goto exit;
   1455
   1456	ctrl = v4l2_ctrl_new_std_menu(&radio->ctrl_handler,
   1457				      &si476x_ctrl_ops,
   1458				      V4L2_CID_TUNE_DEEMPHASIS,
   1459				      V4L2_DEEMPHASIS_75_uS, 0, 0);
   1460	rval = radio->ctrl_handler.error;
   1461	if (ctrl == NULL && rval) {
   1462		dev_err(&pdev->dev, "Could not initialize V4L2_CID_TUNE_DEEMPHASIS control %d\n",
   1463			rval);
   1464		goto exit;
   1465	}
   1466
   1467	ctrl = v4l2_ctrl_new_std(&radio->ctrl_handler, &si476x_ctrl_ops,
   1468				 V4L2_CID_RDS_RECEPTION,
   1469				 0, 1, 1, 1);
   1470	rval = radio->ctrl_handler.error;
   1471	if (ctrl == NULL && rval) {
   1472		dev_err(&pdev->dev, "Could not initialize V4L2_CID_RDS_RECEPTION control %d\n",
   1473			rval);
   1474		goto exit;
   1475	}
   1476
   1477	if (si476x_core_has_diversity(radio->core)) {
   1478		si476x_ctrls[SI476X_IDX_DIVERSITY_MODE].def =
   1479			si476x_phase_diversity_mode_to_idx(radio->core->diversity_mode);
   1480		rval = si476x_radio_add_new_custom(radio, SI476X_IDX_DIVERSITY_MODE);
   1481		if (rval < 0)
   1482			goto exit;
   1483
   1484		rval = si476x_radio_add_new_custom(radio, SI476X_IDX_INTERCHIP_LINK);
   1485		if (rval < 0)
   1486			goto exit;
   1487	}
   1488
   1489	/* register video device */
   1490	rval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, -1);
   1491	if (rval < 0) {
   1492		dev_err(&pdev->dev, "Could not register video device\n");
   1493		goto exit;
   1494	}
   1495
   1496	si476x_radio_init_debugfs(radio);
   1497
   1498	return 0;
   1499exit:
   1500	v4l2_ctrl_handler_free(radio->videodev.ctrl_handler);
   1501	return rval;
   1502}
   1503
   1504static int si476x_radio_remove(struct platform_device *pdev)
   1505{
   1506	struct si476x_radio *radio = platform_get_drvdata(pdev);
   1507
   1508	v4l2_ctrl_handler_free(radio->videodev.ctrl_handler);
   1509	video_unregister_device(&radio->videodev);
   1510	v4l2_device_unregister(&radio->v4l2dev);
   1511	debugfs_remove_recursive(radio->debugfs);
   1512
   1513	return 0;
   1514}
   1515
   1516MODULE_ALIAS("platform:si476x-radio");
   1517
   1518static struct platform_driver si476x_radio_driver = {
   1519	.driver		= {
   1520		.name	= DRIVER_NAME,
   1521	},
   1522	.probe		= si476x_radio_probe,
   1523	.remove		= si476x_radio_remove,
   1524};
   1525module_platform_driver(si476x_radio_driver);
   1526
   1527MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
   1528MODULE_DESCRIPTION("Driver for Si4761/64/68 AM/FM Radio MFD Cell");
   1529MODULE_LICENSE("GPL");