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