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