Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * drivers/media/radio/radio-si476x.c -- V4L2 driver for SI476X chips
0004  *
0005  * Copyright (C) 2012 Innovative Converged Devices(ICD)
0006  * Copyright (C) 2013 Andrey Smirnov
0007  *
0008  * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
0009  */
0010 
0011 #include <linux/module.h>
0012 #include <linux/delay.h>
0013 #include <linux/interrupt.h>
0014 #include <linux/slab.h>
0015 #include <linux/atomic.h>
0016 #include <linux/videodev2.h>
0017 #include <linux/mutex.h>
0018 #include <linux/debugfs.h>
0019 #include <media/v4l2-common.h>
0020 #include <media/v4l2-ioctl.h>
0021 #include <media/v4l2-ctrls.h>
0022 #include <media/v4l2-event.h>
0023 #include <media/v4l2-device.h>
0024 
0025 #include <media/drv-intf/si476x.h>
0026 #include <linux/mfd/si476x-core.h>
0027 
0028 #define FM_FREQ_RANGE_LOW   64000000
0029 #define FM_FREQ_RANGE_HIGH 108000000
0030 
0031 #define AM_FREQ_RANGE_LOW    520000
0032 #define AM_FREQ_RANGE_HIGH 30000000
0033 
0034 #define PWRLINEFLTR (1 << 8)
0035 
0036 #define FREQ_MUL (10000000 / 625)
0037 
0038 #define SI476X_PHDIV_STATUS_LINK_LOCKED(status) (0x80 & (status))
0039 
0040 #define DRIVER_NAME "si476x-radio"
0041 #define DRIVER_CARD "SI476x AM/FM Receiver"
0042 
0043 enum si476x_freq_bands {
0044     SI476X_BAND_FM,
0045     SI476X_BAND_AM,
0046 };
0047 
0048 static const struct v4l2_frequency_band si476x_bands[] = {
0049     [SI476X_BAND_FM] = {
0050         .type       = V4L2_TUNER_RADIO,
0051         .index      = SI476X_BAND_FM,
0052         .capability = V4L2_TUNER_CAP_LOW
0053         | V4L2_TUNER_CAP_STEREO
0054         | V4L2_TUNER_CAP_RDS
0055         | V4L2_TUNER_CAP_RDS_BLOCK_IO
0056         | V4L2_TUNER_CAP_FREQ_BANDS,
0057         .rangelow   =  64 * FREQ_MUL,
0058         .rangehigh  = 108 * FREQ_MUL,
0059         .modulation = V4L2_BAND_MODULATION_FM,
0060     },
0061     [SI476X_BAND_AM] = {
0062         .type       = V4L2_TUNER_RADIO,
0063         .index      = SI476X_BAND_AM,
0064         .capability = V4L2_TUNER_CAP_LOW
0065         | V4L2_TUNER_CAP_FREQ_BANDS,
0066         .rangelow   = 0.52 * FREQ_MUL,
0067         .rangehigh  = 30 * FREQ_MUL,
0068         .modulation = V4L2_BAND_MODULATION_AM,
0069     },
0070 };
0071 
0072 static inline bool si476x_radio_freq_is_inside_of_the_band(u32 freq, int band)
0073 {
0074     return freq >= si476x_bands[band].rangelow &&
0075         freq <= si476x_bands[band].rangehigh;
0076 }
0077 
0078 static inline bool si476x_radio_range_is_inside_of_the_band(u32 low, u32 high,
0079                                 int band)
0080 {
0081     return low  >= si476x_bands[band].rangelow &&
0082         high <= si476x_bands[band].rangehigh;
0083 }
0084 
0085 static int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl);
0086 static int si476x_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl);
0087 
0088 enum phase_diversity_modes_idx {
0089     SI476X_IDX_PHDIV_DISABLED,
0090     SI476X_IDX_PHDIV_PRIMARY_COMBINING,
0091     SI476X_IDX_PHDIV_PRIMARY_ANTENNA,
0092     SI476X_IDX_PHDIV_SECONDARY_ANTENNA,
0093     SI476X_IDX_PHDIV_SECONDARY_COMBINING,
0094 };
0095 
0096 static const char * const phase_diversity_modes[] = {
0097     [SI476X_IDX_PHDIV_DISABLED]     = "Disabled",
0098     [SI476X_IDX_PHDIV_PRIMARY_COMBINING]    = "Primary with Secondary",
0099     [SI476X_IDX_PHDIV_PRIMARY_ANTENNA]  = "Primary Antenna",
0100     [SI476X_IDX_PHDIV_SECONDARY_ANTENNA]    = "Secondary Antenna",
0101     [SI476X_IDX_PHDIV_SECONDARY_COMBINING]  = "Secondary with Primary",
0102 };
0103 
0104 static inline enum phase_diversity_modes_idx
0105 si476x_phase_diversity_mode_to_idx(enum si476x_phase_diversity_mode mode)
0106 {
0107     switch (mode) {
0108     default:
0109         fallthrough;
0110     case SI476X_PHDIV_DISABLED:
0111         return SI476X_IDX_PHDIV_DISABLED;
0112     case SI476X_PHDIV_PRIMARY_COMBINING:
0113         return SI476X_IDX_PHDIV_PRIMARY_COMBINING;
0114     case SI476X_PHDIV_PRIMARY_ANTENNA:
0115         return SI476X_IDX_PHDIV_PRIMARY_ANTENNA;
0116     case SI476X_PHDIV_SECONDARY_ANTENNA:
0117         return SI476X_IDX_PHDIV_SECONDARY_ANTENNA;
0118     case SI476X_PHDIV_SECONDARY_COMBINING:
0119         return SI476X_IDX_PHDIV_SECONDARY_COMBINING;
0120     }
0121 }
0122 
0123 static inline enum si476x_phase_diversity_mode
0124 si476x_phase_diversity_idx_to_mode(enum phase_diversity_modes_idx idx)
0125 {
0126     static const int idx_to_value[] = {
0127         [SI476X_IDX_PHDIV_DISABLED]     = SI476X_PHDIV_DISABLED,
0128         [SI476X_IDX_PHDIV_PRIMARY_COMBINING]    = SI476X_PHDIV_PRIMARY_COMBINING,
0129         [SI476X_IDX_PHDIV_PRIMARY_ANTENNA]  = SI476X_PHDIV_PRIMARY_ANTENNA,
0130         [SI476X_IDX_PHDIV_SECONDARY_ANTENNA]    = SI476X_PHDIV_SECONDARY_ANTENNA,
0131         [SI476X_IDX_PHDIV_SECONDARY_COMBINING]  = SI476X_PHDIV_SECONDARY_COMBINING,
0132     };
0133 
0134     return idx_to_value[idx];
0135 }
0136 
0137 static const struct v4l2_ctrl_ops si476x_ctrl_ops = {
0138     .g_volatile_ctrl    = si476x_radio_g_volatile_ctrl,
0139     .s_ctrl         = si476x_radio_s_ctrl,
0140 };
0141 
0142 
0143 enum si476x_ctrl_idx {
0144     SI476X_IDX_RSSI_THRESHOLD,
0145     SI476X_IDX_SNR_THRESHOLD,
0146     SI476X_IDX_MAX_TUNE_ERROR,
0147     SI476X_IDX_HARMONICS_COUNT,
0148     SI476X_IDX_DIVERSITY_MODE,
0149     SI476X_IDX_INTERCHIP_LINK,
0150 };
0151 static struct v4l2_ctrl_config si476x_ctrls[] = {
0152 
0153     /*
0154      * SI476X during its station seeking(or tuning) process uses several
0155      * parameters to determine if "the station" is valid:
0156      *
0157      *  - Signal's SNR(in dBuV) must be lower than
0158      *  #V4L2_CID_SI476X_SNR_THRESHOLD
0159      *  - Signal's RSSI(in dBuV) must be greater than
0160      *  #V4L2_CID_SI476X_RSSI_THRESHOLD
0161      *  - Signal's frequency deviation(in units of 2ppm) must not be
0162      *  more than #V4L2_CID_SI476X_MAX_TUNE_ERROR
0163      */
0164     [SI476X_IDX_RSSI_THRESHOLD] = {
0165         .ops    = &si476x_ctrl_ops,
0166         .id = V4L2_CID_SI476X_RSSI_THRESHOLD,
0167         .name   = "Valid RSSI Threshold",
0168         .type   = V4L2_CTRL_TYPE_INTEGER,
0169         .min    = -128,
0170         .max    = 127,
0171         .step   = 1,
0172     },
0173     [SI476X_IDX_SNR_THRESHOLD] = {
0174         .ops    = &si476x_ctrl_ops,
0175         .id = V4L2_CID_SI476X_SNR_THRESHOLD,
0176         .type   = V4L2_CTRL_TYPE_INTEGER,
0177         .name   = "Valid SNR Threshold",
0178         .min    = -128,
0179         .max    = 127,
0180         .step   = 1,
0181     },
0182     [SI476X_IDX_MAX_TUNE_ERROR] = {
0183         .ops    = &si476x_ctrl_ops,
0184         .id = V4L2_CID_SI476X_MAX_TUNE_ERROR,
0185         .type   = V4L2_CTRL_TYPE_INTEGER,
0186         .name   = "Max Tune Errors",
0187         .min    = 0,
0188         .max    = 126 * 2,
0189         .step   = 2,
0190     },
0191 
0192     /*
0193      * #V4L2_CID_SI476X_HARMONICS_COUNT -- number of harmonics
0194      * built-in power-line noise supression filter is to reject
0195      * during AM-mode operation.
0196      */
0197     [SI476X_IDX_HARMONICS_COUNT] = {
0198         .ops    = &si476x_ctrl_ops,
0199         .id = V4L2_CID_SI476X_HARMONICS_COUNT,
0200         .type   = V4L2_CTRL_TYPE_INTEGER,
0201 
0202         .name   = "Count of Harmonics to Reject",
0203         .min    = 0,
0204         .max    = 20,
0205         .step   = 1,
0206     },
0207 
0208     /*
0209      * #V4L2_CID_SI476X_DIVERSITY_MODE -- configuration which
0210      * two tuners working in diversity mode are to work in.
0211      *
0212      *  - #SI476X_IDX_PHDIV_DISABLED diversity mode disabled
0213      *  - #SI476X_IDX_PHDIV_PRIMARY_COMBINING diversity mode is
0214      *  on, primary tuner's antenna is the main one.
0215      *  - #SI476X_IDX_PHDIV_PRIMARY_ANTENNA diversity mode is
0216      *  off, primary tuner's antenna is the main one.
0217      *  - #SI476X_IDX_PHDIV_SECONDARY_ANTENNA diversity mode is
0218      *  off, secondary tuner's antenna is the main one.
0219      *  - #SI476X_IDX_PHDIV_SECONDARY_COMBINING diversity mode is
0220      *  on, secondary tuner's antenna is the main one.
0221      */
0222     [SI476X_IDX_DIVERSITY_MODE] = {
0223         .ops    = &si476x_ctrl_ops,
0224         .id = V4L2_CID_SI476X_DIVERSITY_MODE,
0225         .type   = V4L2_CTRL_TYPE_MENU,
0226         .name   = "Phase Diversity Mode",
0227         .qmenu  = phase_diversity_modes,
0228         .min    = 0,
0229         .max    = ARRAY_SIZE(phase_diversity_modes) - 1,
0230     },
0231 
0232     /*
0233      * #V4L2_CID_SI476X_INTERCHIP_LINK -- inter-chip link in
0234      * diversity mode indicator. Allows user to determine if two
0235      * chips working in diversity mode have established a link
0236      * between each other and if the system as a whole uses
0237      * signals from both antennas to receive FM radio.
0238      */
0239     [SI476X_IDX_INTERCHIP_LINK] = {
0240         .ops    = &si476x_ctrl_ops,
0241         .id = V4L2_CID_SI476X_INTERCHIP_LINK,
0242         .type   = V4L2_CTRL_TYPE_BOOLEAN,
0243         .flags  = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE,
0244         .name   = "Inter-Chip Link",
0245         .min    = 0,
0246         .max    = 1,
0247         .step   = 1,
0248     },
0249 };
0250 
0251 struct si476x_radio;
0252 
0253 /**
0254  * struct si476x_radio_ops - vtable of tuner functions
0255  *
0256  * This table holds pointers to functions implementing particular
0257  * operations depending on the mode in which the tuner chip was
0258  * configured to start. If the function is not supported
0259  * corresponding element is set to #NULL.
0260  *
0261  * @tune_freq: Tune chip to a specific frequency
0262  * @seek_start: Star station seeking
0263  * @rsq_status: Get Received Signal Quality(RSQ) status
0264  * @rds_blckcnt: Get received RDS blocks count
0265  * @phase_diversity: Change phase diversity mode of the tuner
0266  * @phase_div_status: Get phase diversity mode status
0267  * @acf_status: Get the status of Automatically Controlled
0268  * Features(ACF)
0269  * @agc_status: Get Automatic Gain Control(AGC) status
0270  */
0271 struct si476x_radio_ops {
0272     int (*tune_freq)(struct si476x_core *, struct si476x_tune_freq_args *);
0273     int (*seek_start)(struct si476x_core *, bool, bool);
0274     int (*rsq_status)(struct si476x_core *, struct si476x_rsq_status_args *,
0275               struct si476x_rsq_status_report *);
0276     int (*rds_blckcnt)(struct si476x_core *, bool,
0277                struct si476x_rds_blockcount_report *);
0278 
0279     int (*phase_diversity)(struct si476x_core *,
0280                    enum si476x_phase_diversity_mode);
0281     int (*phase_div_status)(struct si476x_core *);
0282     int (*acf_status)(struct si476x_core *,
0283               struct si476x_acf_status_report *);
0284     int (*agc_status)(struct si476x_core *,
0285               struct si476x_agc_status_report *);
0286 };
0287 
0288 /**
0289  * struct si476x_radio - radio device
0290  *
0291  * @v4l2dev: Pointer to V4L2 device created by V4L2 subsystem
0292  * @videodev: Pointer to video device created by V4L2 subsystem
0293  * @ctrl_handler: V4L2 controls handler
0294  * @core: Pointer to underlying core device
0295  * @ops: Vtable of functions. See struct si476x_radio_ops for details
0296  * @debugfs: pointer to &strucd dentry for debugfs
0297  * @audmode: audio mode, as defined for the rxsubchans field
0298  *       at videodev2.h
0299  *
0300  * core structure is the radio device is being used
0301  */
0302 struct si476x_radio {
0303     struct v4l2_device v4l2dev;
0304     struct video_device videodev;
0305     struct v4l2_ctrl_handler ctrl_handler;
0306 
0307     struct si476x_core  *core;
0308     /* This field should not be accesses unless core lock is held */
0309     const struct si476x_radio_ops *ops;
0310 
0311     struct dentry   *debugfs;
0312     u32 audmode;
0313 };
0314 
0315 static inline struct si476x_radio *
0316 v4l2_ctrl_handler_to_radio(struct v4l2_ctrl_handler *d)
0317 {
0318     return container_of(d, struct si476x_radio, ctrl_handler);
0319 }
0320 
0321 /*
0322  * si476x_vidioc_querycap - query device capabilities
0323  */
0324 static int si476x_radio_querycap(struct file *file, void *priv,
0325                  struct v4l2_capability *capability)
0326 {
0327     struct si476x_radio *radio = video_drvdata(file);
0328 
0329     strscpy(capability->driver, radio->v4l2dev.name,
0330         sizeof(capability->driver));
0331     strscpy(capability->card,   DRIVER_CARD, sizeof(capability->card));
0332     snprintf(capability->bus_info, sizeof(capability->bus_info),
0333          "platform:%s", radio->v4l2dev.name);
0334     return 0;
0335 }
0336 
0337 static int si476x_radio_enum_freq_bands(struct file *file, void *priv,
0338                     struct v4l2_frequency_band *band)
0339 {
0340     int err;
0341     struct si476x_radio *radio = video_drvdata(file);
0342 
0343     if (band->tuner != 0)
0344         return -EINVAL;
0345 
0346     switch (radio->core->chip_id) {
0347         /* AM/FM tuners -- all bands are supported */
0348     case SI476X_CHIP_SI4761:
0349     case SI476X_CHIP_SI4764:
0350         if (band->index < ARRAY_SIZE(si476x_bands)) {
0351             *band = si476x_bands[band->index];
0352             err = 0;
0353         } else {
0354             err = -EINVAL;
0355         }
0356         break;
0357         /* FM companion tuner chips -- only FM bands are
0358          * supported */
0359     case SI476X_CHIP_SI4768:
0360         if (band->index == SI476X_BAND_FM) {
0361             *band = si476x_bands[band->index];
0362             err = 0;
0363         } else {
0364             err = -EINVAL;
0365         }
0366         break;
0367     default:
0368         err = -EINVAL;
0369     }
0370 
0371     return err;
0372 }
0373 
0374 static int si476x_radio_g_tuner(struct file *file, void *priv,
0375                 struct v4l2_tuner *tuner)
0376 {
0377     int err;
0378     struct si476x_rsq_status_report report;
0379     struct si476x_radio *radio = video_drvdata(file);
0380 
0381     struct si476x_rsq_status_args args = {
0382         .primary    = false,
0383         .rsqack     = false,
0384         .attune     = false,
0385         .cancel     = false,
0386         .stcack     = false,
0387     };
0388 
0389     if (tuner->index != 0)
0390         return -EINVAL;
0391 
0392     tuner->type       = V4L2_TUNER_RADIO;
0393     tuner->capability = V4L2_TUNER_CAP_LOW /* Measure frequencies
0394                          * in multiples of
0395                          * 62.5 Hz */
0396         | V4L2_TUNER_CAP_STEREO
0397         | V4L2_TUNER_CAP_HWSEEK_BOUNDED
0398         | V4L2_TUNER_CAP_HWSEEK_WRAP
0399         | V4L2_TUNER_CAP_HWSEEK_PROG_LIM;
0400 
0401     si476x_core_lock(radio->core);
0402 
0403     if (si476x_core_is_a_secondary_tuner(radio->core)) {
0404         strscpy(tuner->name, "FM (secondary)", sizeof(tuner->name));
0405         tuner->rxsubchans = 0;
0406         tuner->rangelow = si476x_bands[SI476X_BAND_FM].rangelow;
0407     } else if (si476x_core_has_am(radio->core)) {
0408         if (si476x_core_is_a_primary_tuner(radio->core))
0409             strscpy(tuner->name, "AM/FM (primary)",
0410                 sizeof(tuner->name));
0411         else
0412             strscpy(tuner->name, "AM/FM", sizeof(tuner->name));
0413 
0414         tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO
0415             | V4L2_TUNER_SUB_RDS;
0416         tuner->capability |= V4L2_TUNER_CAP_RDS
0417             | V4L2_TUNER_CAP_RDS_BLOCK_IO
0418             | V4L2_TUNER_CAP_FREQ_BANDS;
0419 
0420         tuner->rangelow = si476x_bands[SI476X_BAND_AM].rangelow;
0421     } else {
0422         strscpy(tuner->name, "FM", sizeof(tuner->name));
0423         tuner->rxsubchans = V4L2_TUNER_SUB_RDS;
0424         tuner->capability |= V4L2_TUNER_CAP_RDS
0425             | V4L2_TUNER_CAP_RDS_BLOCK_IO
0426             | V4L2_TUNER_CAP_FREQ_BANDS;
0427         tuner->rangelow = si476x_bands[SI476X_BAND_FM].rangelow;
0428     }
0429 
0430     tuner->audmode = radio->audmode;
0431 
0432     tuner->afc = 1;
0433     tuner->rangehigh = si476x_bands[SI476X_BAND_FM].rangehigh;
0434 
0435     err = radio->ops->rsq_status(radio->core,
0436                      &args, &report);
0437     if (err < 0) {
0438         tuner->signal = 0;
0439     } else {
0440         /*
0441          * tuner->signal value range: 0x0000 .. 0xFFFF,
0442          * report.rssi: -128 .. 127
0443          */
0444         tuner->signal = (report.rssi + 128) * 257;
0445     }
0446     si476x_core_unlock(radio->core);
0447 
0448     return err;
0449 }
0450 
0451 static int si476x_radio_s_tuner(struct file *file, void *priv,
0452                 const struct v4l2_tuner *tuner)
0453 {
0454     struct si476x_radio *radio = video_drvdata(file);
0455 
0456     if (tuner->index != 0)
0457         return -EINVAL;
0458 
0459     if (tuner->audmode == V4L2_TUNER_MODE_MONO ||
0460         tuner->audmode == V4L2_TUNER_MODE_STEREO)
0461         radio->audmode = tuner->audmode;
0462     else
0463         radio->audmode = V4L2_TUNER_MODE_STEREO;
0464 
0465     return 0;
0466 }
0467 
0468 static int si476x_radio_init_vtable(struct si476x_radio *radio,
0469                     enum si476x_func func)
0470 {
0471     static const struct si476x_radio_ops fm_ops = {
0472         .tune_freq      = si476x_core_cmd_fm_tune_freq,
0473         .seek_start     = si476x_core_cmd_fm_seek_start,
0474         .rsq_status     = si476x_core_cmd_fm_rsq_status,
0475         .rds_blckcnt        = si476x_core_cmd_fm_rds_blockcount,
0476         .phase_diversity    = si476x_core_cmd_fm_phase_diversity,
0477         .phase_div_status   = si476x_core_cmd_fm_phase_div_status,
0478         .acf_status     = si476x_core_cmd_fm_acf_status,
0479         .agc_status     = si476x_core_cmd_agc_status,
0480     };
0481 
0482     static const struct si476x_radio_ops am_ops = {
0483         .tune_freq      = si476x_core_cmd_am_tune_freq,
0484         .seek_start     = si476x_core_cmd_am_seek_start,
0485         .rsq_status     = si476x_core_cmd_am_rsq_status,
0486         .rds_blckcnt        = NULL,
0487         .phase_diversity    = NULL,
0488         .phase_div_status   = NULL,
0489         .acf_status     = si476x_core_cmd_am_acf_status,
0490         .agc_status     = NULL,
0491     };
0492 
0493     switch (func) {
0494     case SI476X_FUNC_FM_RECEIVER:
0495         radio->ops = &fm_ops;
0496         return 0;
0497 
0498     case SI476X_FUNC_AM_RECEIVER:
0499         radio->ops = &am_ops;
0500         return 0;
0501     default:
0502         WARN(1, "Unexpected tuner function value\n");
0503         return -EINVAL;
0504     }
0505 }
0506 
0507 static int si476x_radio_pretune(struct si476x_radio *radio,
0508                 enum si476x_func func)
0509 {
0510     int retval;
0511 
0512     struct si476x_tune_freq_args args = {
0513         .zifsr      = false,
0514         .hd     = false,
0515         .injside    = SI476X_INJSIDE_AUTO,
0516         .tunemode   = SI476X_TM_VALIDATED_NORMAL_TUNE,
0517         .smoothmetrics  = SI476X_SM_INITIALIZE_AUDIO,
0518         .antcap     = 0,
0519     };
0520 
0521     switch (func) {
0522     case SI476X_FUNC_FM_RECEIVER:
0523         args.freq = v4l2_to_si476x(radio->core,
0524                        92 * FREQ_MUL);
0525         retval = radio->ops->tune_freq(radio->core, &args);
0526         break;
0527     case SI476X_FUNC_AM_RECEIVER:
0528         args.freq = v4l2_to_si476x(radio->core,
0529                        0.6 * FREQ_MUL);
0530         retval = radio->ops->tune_freq(radio->core, &args);
0531         break;
0532     default:
0533         WARN(1, "Unexpected tuner function value\n");
0534         retval = -EINVAL;
0535     }
0536 
0537     return retval;
0538 }
0539 static int si476x_radio_do_post_powerup_init(struct si476x_radio *radio,
0540                          enum si476x_func func)
0541 {
0542     int err;
0543 
0544     /* regcache_mark_dirty(radio->core->regmap); */
0545     err = regcache_sync_region(radio->core->regmap,
0546                    SI476X_PROP_DIGITAL_IO_INPUT_SAMPLE_RATE,
0547                    SI476X_PROP_DIGITAL_IO_OUTPUT_FORMAT);
0548     if (err < 0)
0549         return err;
0550 
0551     err = regcache_sync_region(radio->core->regmap,
0552                    SI476X_PROP_AUDIO_DEEMPHASIS,
0553                    SI476X_PROP_AUDIO_PWR_LINE_FILTER);
0554     if (err < 0)
0555         return err;
0556 
0557     err = regcache_sync_region(radio->core->regmap,
0558                    SI476X_PROP_INT_CTL_ENABLE,
0559                    SI476X_PROP_INT_CTL_ENABLE);
0560     if (err < 0)
0561         return err;
0562 
0563     /*
0564      * Is there any point in restoring SNR and the like
0565      * when switching between AM/FM?
0566      */
0567     err = regcache_sync_region(radio->core->regmap,
0568                    SI476X_PROP_VALID_MAX_TUNE_ERROR,
0569                    SI476X_PROP_VALID_MAX_TUNE_ERROR);
0570     if (err < 0)
0571         return err;
0572 
0573     err = regcache_sync_region(radio->core->regmap,
0574                    SI476X_PROP_VALID_SNR_THRESHOLD,
0575                    SI476X_PROP_VALID_RSSI_THRESHOLD);
0576     if (err < 0)
0577         return err;
0578 
0579     if (func == SI476X_FUNC_FM_RECEIVER) {
0580         if (si476x_core_has_diversity(radio->core)) {
0581             err = si476x_core_cmd_fm_phase_diversity(radio->core,
0582                                  radio->core->diversity_mode);
0583             if (err < 0)
0584                 return err;
0585         }
0586 
0587         err = regcache_sync_region(radio->core->regmap,
0588                        SI476X_PROP_FM_RDS_INTERRUPT_SOURCE,
0589                        SI476X_PROP_FM_RDS_CONFIG);
0590         if (err < 0)
0591             return err;
0592     }
0593 
0594     return si476x_radio_init_vtable(radio, func);
0595 
0596 }
0597 
0598 static int si476x_radio_change_func(struct si476x_radio *radio,
0599                     enum si476x_func func)
0600 {
0601     int err;
0602     bool soft;
0603     /*
0604      * Since power/up down is a very time consuming operation,
0605      * try to avoid doing it if the requested mode matches the one
0606      * the tuner is in
0607      */
0608     if (func == radio->core->power_up_parameters.func)
0609         return 0;
0610 
0611     soft = true;
0612     err = si476x_core_stop(radio->core, soft);
0613     if (err < 0) {
0614         /*
0615          * OK, if the chip does not want to play nice let's
0616          * try to reset it in more brutal way
0617          */
0618         soft = false;
0619         err = si476x_core_stop(radio->core, soft);
0620         if (err < 0)
0621             return err;
0622     }
0623     /*
0624       Set the desired radio tuner function
0625      */
0626     radio->core->power_up_parameters.func = func;
0627 
0628     err = si476x_core_start(radio->core, soft);
0629     if (err < 0)
0630         return err;
0631 
0632     /*
0633      * No need to do the rest of manipulations for the bootlader
0634      * mode
0635      */
0636     if (func != SI476X_FUNC_FM_RECEIVER &&
0637         func != SI476X_FUNC_AM_RECEIVER)
0638         return err;
0639 
0640     return si476x_radio_do_post_powerup_init(radio, func);
0641 }
0642 
0643 static int si476x_radio_g_frequency(struct file *file, void *priv,
0644                   struct v4l2_frequency *f)
0645 {
0646     int err;
0647     struct si476x_radio *radio = video_drvdata(file);
0648 
0649     if (f->tuner != 0 ||
0650         f->type  != V4L2_TUNER_RADIO)
0651         return -EINVAL;
0652 
0653     si476x_core_lock(radio->core);
0654 
0655     if (radio->ops->rsq_status) {
0656         struct si476x_rsq_status_report report;
0657         struct si476x_rsq_status_args   args = {
0658             .primary    = false,
0659             .rsqack     = false,
0660             .attune     = true,
0661             .cancel     = false,
0662             .stcack     = false,
0663         };
0664 
0665         err = radio->ops->rsq_status(radio->core, &args, &report);
0666         if (!err)
0667             f->frequency = si476x_to_v4l2(radio->core,
0668                               report.readfreq);
0669     } else {
0670         err = -EINVAL;
0671     }
0672 
0673     si476x_core_unlock(radio->core);
0674 
0675     return err;
0676 }
0677 
0678 static int si476x_radio_s_frequency(struct file *file, void *priv,
0679                     const struct v4l2_frequency *f)
0680 {
0681     int err;
0682     u32 freq = f->frequency;
0683     struct si476x_tune_freq_args args;
0684     struct si476x_radio *radio = video_drvdata(file);
0685 
0686     const u32 midrange = (si476x_bands[SI476X_BAND_AM].rangehigh +
0687                   si476x_bands[SI476X_BAND_FM].rangelow) / 2;
0688     const int band = (freq > midrange) ?
0689         SI476X_BAND_FM : SI476X_BAND_AM;
0690     const enum si476x_func func = (band == SI476X_BAND_AM) ?
0691         SI476X_FUNC_AM_RECEIVER : SI476X_FUNC_FM_RECEIVER;
0692 
0693     if (f->tuner != 0 ||
0694         f->type  != V4L2_TUNER_RADIO)
0695         return -EINVAL;
0696 
0697     si476x_core_lock(radio->core);
0698 
0699     freq = clamp(freq,
0700              si476x_bands[band].rangelow,
0701              si476x_bands[band].rangehigh);
0702 
0703     if (si476x_radio_freq_is_inside_of_the_band(freq,
0704                             SI476X_BAND_AM) &&
0705         (!si476x_core_has_am(radio->core) ||
0706          si476x_core_is_a_secondary_tuner(radio->core))) {
0707         err = -EINVAL;
0708         goto unlock;
0709     }
0710 
0711     err = si476x_radio_change_func(radio, func);
0712     if (err < 0)
0713         goto unlock;
0714 
0715     args.zifsr      = false;
0716     args.hd         = false;
0717     args.injside        = SI476X_INJSIDE_AUTO;
0718     args.freq       = v4l2_to_si476x(radio->core, freq);
0719     args.tunemode       = SI476X_TM_VALIDATED_NORMAL_TUNE;
0720     args.smoothmetrics  = SI476X_SM_INITIALIZE_AUDIO;
0721     args.antcap     = 0;
0722 
0723     err = radio->ops->tune_freq(radio->core, &args);
0724 
0725 unlock:
0726     si476x_core_unlock(radio->core);
0727     return err;
0728 }
0729 
0730 static int si476x_radio_s_hw_freq_seek(struct file *file, void *priv,
0731                        const struct v4l2_hw_freq_seek *seek)
0732 {
0733     int err;
0734     enum si476x_func func;
0735     u32 rangelow = seek->rangelow, rangehigh = seek->rangehigh;
0736     struct si476x_radio *radio = video_drvdata(file);
0737 
0738     if (file->f_flags & O_NONBLOCK)
0739         return -EAGAIN;
0740 
0741     if (seek->tuner != 0 ||
0742         seek->type  != V4L2_TUNER_RADIO)
0743         return -EINVAL;
0744 
0745     si476x_core_lock(radio->core);
0746 
0747     if (!rangelow) {
0748         err = regmap_read(radio->core->regmap,
0749                   SI476X_PROP_SEEK_BAND_BOTTOM,
0750                   &rangelow);
0751         if (err)
0752             goto unlock;
0753         rangelow = si476x_to_v4l2(radio->core, rangelow);
0754     }
0755     if (!rangehigh) {
0756         err = regmap_read(radio->core->regmap,
0757                   SI476X_PROP_SEEK_BAND_TOP,
0758                   &rangehigh);
0759         if (err)
0760             goto unlock;
0761         rangehigh = si476x_to_v4l2(radio->core, rangehigh);
0762     }
0763 
0764     if (rangelow > rangehigh) {
0765         err = -EINVAL;
0766         goto unlock;
0767     }
0768 
0769     if (si476x_radio_range_is_inside_of_the_band(rangelow, rangehigh,
0770                              SI476X_BAND_FM)) {
0771         func = SI476X_FUNC_FM_RECEIVER;
0772 
0773     } else if (si476x_core_has_am(radio->core) &&
0774            si476x_radio_range_is_inside_of_the_band(rangelow, rangehigh,
0775                                 SI476X_BAND_AM)) {
0776         func = SI476X_FUNC_AM_RECEIVER;
0777     } else {
0778         err = -EINVAL;
0779         goto unlock;
0780     }
0781 
0782     err = si476x_radio_change_func(radio, func);
0783     if (err < 0)
0784         goto unlock;
0785 
0786     if (seek->rangehigh) {
0787         err = regmap_write(radio->core->regmap,
0788                    SI476X_PROP_SEEK_BAND_TOP,
0789                    v4l2_to_si476x(radio->core,
0790                           seek->rangehigh));
0791         if (err)
0792             goto unlock;
0793     }
0794     if (seek->rangelow) {
0795         err = regmap_write(radio->core->regmap,
0796                    SI476X_PROP_SEEK_BAND_BOTTOM,
0797                    v4l2_to_si476x(radio->core,
0798                           seek->rangelow));
0799         if (err)
0800             goto unlock;
0801     }
0802     if (seek->spacing) {
0803         err = regmap_write(radio->core->regmap,
0804                      SI476X_PROP_SEEK_FREQUENCY_SPACING,
0805                      v4l2_to_si476x(radio->core,
0806                             seek->spacing));
0807         if (err)
0808             goto unlock;
0809     }
0810 
0811     err = radio->ops->seek_start(radio->core,
0812                      seek->seek_upward,
0813                      seek->wrap_around);
0814 unlock:
0815     si476x_core_unlock(radio->core);
0816 
0817 
0818 
0819     return err;
0820 }
0821 
0822 static int si476x_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
0823 {
0824     int retval;
0825     struct si476x_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler);
0826 
0827     si476x_core_lock(radio->core);
0828 
0829     switch (ctrl->id) {
0830     case V4L2_CID_SI476X_INTERCHIP_LINK:
0831         if (si476x_core_has_diversity(radio->core)) {
0832             if (radio->ops->phase_diversity) {
0833                 retval = radio->ops->phase_div_status(radio->core);
0834                 if (retval < 0)
0835                     break;
0836 
0837                 ctrl->val = !!SI476X_PHDIV_STATUS_LINK_LOCKED(retval);
0838                 retval = 0;
0839                 break;
0840             } else {
0841                 retval = -ENOTTY;
0842                 break;
0843             }
0844         }
0845         retval = -EINVAL;
0846         break;
0847     default:
0848         retval = -EINVAL;
0849         break;
0850     }
0851     si476x_core_unlock(radio->core);
0852     return retval;
0853 
0854 }
0855 
0856 static int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl)
0857 {
0858     int retval;
0859     enum si476x_phase_diversity_mode mode;
0860     struct si476x_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler);
0861 
0862     si476x_core_lock(radio->core);
0863 
0864     switch (ctrl->id) {
0865     case V4L2_CID_SI476X_HARMONICS_COUNT:
0866         retval = regmap_update_bits(radio->core->regmap,
0867                         SI476X_PROP_AUDIO_PWR_LINE_FILTER,
0868                         SI476X_PROP_PWR_HARMONICS_MASK,
0869                         ctrl->val);
0870         break;
0871     case V4L2_CID_POWER_LINE_FREQUENCY:
0872         switch (ctrl->val) {
0873         case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED:
0874             retval = regmap_update_bits(radio->core->regmap,
0875                             SI476X_PROP_AUDIO_PWR_LINE_FILTER,
0876                             SI476X_PROP_PWR_ENABLE_MASK,
0877                             0);
0878             break;
0879         case V4L2_CID_POWER_LINE_FREQUENCY_50HZ:
0880             retval = regmap_update_bits(radio->core->regmap,
0881                             SI476X_PROP_AUDIO_PWR_LINE_FILTER,
0882                             SI476X_PROP_PWR_GRID_MASK,
0883                             SI476X_PROP_PWR_GRID_50HZ);
0884             break;
0885         case V4L2_CID_POWER_LINE_FREQUENCY_60HZ:
0886             retval = regmap_update_bits(radio->core->regmap,
0887                             SI476X_PROP_AUDIO_PWR_LINE_FILTER,
0888                             SI476X_PROP_PWR_GRID_MASK,
0889                             SI476X_PROP_PWR_GRID_60HZ);
0890             break;
0891         default:
0892             retval = -EINVAL;
0893             break;
0894         }
0895         break;
0896     case V4L2_CID_SI476X_RSSI_THRESHOLD:
0897         retval = regmap_write(radio->core->regmap,
0898                       SI476X_PROP_VALID_RSSI_THRESHOLD,
0899                       ctrl->val);
0900         break;
0901     case V4L2_CID_SI476X_SNR_THRESHOLD:
0902         retval = regmap_write(radio->core->regmap,
0903                       SI476X_PROP_VALID_SNR_THRESHOLD,
0904                       ctrl->val);
0905         break;
0906     case V4L2_CID_SI476X_MAX_TUNE_ERROR:
0907         retval = regmap_write(radio->core->regmap,
0908                       SI476X_PROP_VALID_MAX_TUNE_ERROR,
0909                       ctrl->val);
0910         break;
0911     case V4L2_CID_RDS_RECEPTION:
0912         /*
0913          * It looks like RDS related properties are
0914          * inaccessible when tuner is in AM mode, so cache the
0915          * changes
0916          */
0917         if (si476x_core_is_in_am_receiver_mode(radio->core))
0918             regcache_cache_only(radio->core->regmap, true);
0919 
0920         if (ctrl->val) {
0921             retval = regmap_write(radio->core->regmap,
0922                           SI476X_PROP_FM_RDS_INTERRUPT_FIFO_COUNT,
0923                           radio->core->rds_fifo_depth);
0924             if (retval < 0)
0925                 break;
0926 
0927             if (radio->core->client->irq) {
0928                 retval = regmap_write(radio->core->regmap,
0929                               SI476X_PROP_FM_RDS_INTERRUPT_SOURCE,
0930                               SI476X_RDSRECV);
0931                 if (retval < 0)
0932                     break;
0933             }
0934 
0935             /* Drain RDS FIFO before enabling RDS processing */
0936             retval = si476x_core_cmd_fm_rds_status(radio->core,
0937                                    false,
0938                                    true,
0939                                    true,
0940                                    NULL);
0941             if (retval < 0)
0942                 break;
0943 
0944             retval = regmap_update_bits(radio->core->regmap,
0945                             SI476X_PROP_FM_RDS_CONFIG,
0946                             SI476X_PROP_RDSEN_MASK,
0947                             SI476X_PROP_RDSEN);
0948         } else {
0949             retval = regmap_update_bits(radio->core->regmap,
0950                             SI476X_PROP_FM_RDS_CONFIG,
0951                             SI476X_PROP_RDSEN_MASK,
0952                             !SI476X_PROP_RDSEN);
0953         }
0954 
0955         if (si476x_core_is_in_am_receiver_mode(radio->core))
0956             regcache_cache_only(radio->core->regmap, false);
0957         break;
0958     case V4L2_CID_TUNE_DEEMPHASIS:
0959         retval = regmap_write(radio->core->regmap,
0960                       SI476X_PROP_AUDIO_DEEMPHASIS,
0961                       ctrl->val);
0962         break;
0963 
0964     case V4L2_CID_SI476X_DIVERSITY_MODE:
0965         mode = si476x_phase_diversity_idx_to_mode(ctrl->val);
0966 
0967         if (mode == radio->core->diversity_mode) {
0968             retval = 0;
0969             break;
0970         }
0971 
0972         if (si476x_core_is_in_am_receiver_mode(radio->core)) {
0973             /*
0974              * Diversity cannot be configured while tuner
0975              * is in AM mode so save the changes and carry on.
0976              */
0977             radio->core->diversity_mode = mode;
0978             retval = 0;
0979         } else {
0980             retval = radio->ops->phase_diversity(radio->core, mode);
0981             if (!retval)
0982                 radio->core->diversity_mode = mode;
0983         }
0984         break;
0985 
0986     default:
0987         retval = -EINVAL;
0988         break;
0989     }
0990 
0991     si476x_core_unlock(radio->core);
0992 
0993     return retval;
0994 }
0995 
0996 #ifdef CONFIG_VIDEO_ADV_DEBUG
0997 static int si476x_radio_g_register(struct file *file, void *fh,
0998                    struct v4l2_dbg_register *reg)
0999 {
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 }
1013 static 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 
1030 static 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 
1063 power_down:
1064     si476x_core_set_power_state(radio->core,
1065                     SI476X_POWER_DOWN);
1066 done:
1067     si476x_core_unlock(radio->core);
1068     v4l2_fh_release(file);
1069 
1070     return err;
1071 }
1072 
1073 static 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 
1088 static 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 
1127 static __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 
1148 static 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 
1158 static 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 
1178 static 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 
1186 static 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 
1208 static const struct file_operations radio_acf_fops = {
1209     .open   = simple_open,
1210     .llseek = default_llseek,
1211     .read   = si476x_radio_read_acf_blob,
1212 };
1213 
1214 static 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 
1237 static 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 
1243 static 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 
1265 static const struct file_operations radio_agc_fops = {
1266     .open   = simple_open,
1267     .llseek = default_llseek,
1268     .read   = si476x_radio_read_agc_blob,
1269 };
1270 
1271 static 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 
1300 static const struct file_operations radio_rsq_fops = {
1301     .open   = simple_open,
1302     .llseek = default_llseek,
1303     .read   = si476x_radio_read_rsq_blob,
1304 };
1305 
1306 static 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 
1335 static 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 
1342 static 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 
1363 static 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 
1381 static 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;
1499 exit:
1500     v4l2_ctrl_handler_free(radio->videodev.ctrl_handler);
1501     return rval;
1502 }
1503 
1504 static 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 
1516 MODULE_ALIAS("platform:si476x-radio");
1517 
1518 static struct platform_driver si476x_radio_driver = {
1519     .driver     = {
1520         .name   = DRIVER_NAME,
1521     },
1522     .probe      = si476x_radio_probe,
1523     .remove     = si476x_radio_remove,
1524 };
1525 module_platform_driver(si476x_radio_driver);
1526 
1527 MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
1528 MODULE_DESCRIPTION("Driver for Si4761/64/68 AM/FM Radio MFD Cell");
1529 MODULE_LICENSE("GPL");