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