Version:  2.0.40 2.2.26 2.4.37 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17

Linux/drivers/media/radio/radio-si476x.c

  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/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 
 52 enum si476x_freq_bands {
 53         SI476X_BAND_FM,
 54         SI476X_BAND_AM,
 55 };
 56 
 57 static 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 
 81 static 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 
 87 static 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 
 94 static int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl);
 95 static int si476x_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl);
 96 
 97 enum 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 
105 static 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 
113 static inline enum phase_diversity_modes_idx
114 si476x_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 
131 static inline enum si476x_phase_diversity_mode
132 si476x_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 
145 static 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 
151 enum 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 };
159 static 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 
259 struct 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  */
279 struct 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  */
306 struct 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 
319 static inline struct si476x_radio *
320 v4l2_dev_to_radio(struct v4l2_device *d)
321 {
322         return container_of(d, struct si476x_radio, v4l2dev);
323 }
324 
325 static inline struct si476x_radio *
326 v4l2_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  */
334 static 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 
360 static 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 
397 static 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 
474 static 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 
491 static 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 
530 static 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 }
562 static 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 
621 static 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 
666 static 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 
701 static 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 
748 unlock:
749         si476x_core_unlock(radio->core);
750         return err;
751 }
752 
753 static 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);
839 unlock:
840         si476x_core_unlock(radio->core);
841 
842 
843 
844         return err;
845 }
846 
847 static 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 
881 static 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
1022 static 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 }
1038 static 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 
1055 static 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 
1088 power_down:
1089         si476x_core_set_power_state(radio->core,
1090                                     SI476X_POWER_DOWN);
1091 done:
1092         si476x_core_unlock(radio->core);
1093         v4l2_fh_release(file);
1094 
1095         return err;
1096 }
1097 
1098 static 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 
1113 static 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 
1152 static 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 
1173 static 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 
1183 static 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 
1203 static 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 
1211 static 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 
1233 static const struct file_operations radio_acf_fops = {
1234         .open   = simple_open,
1235         .llseek = default_llseek,
1236         .read   = si476x_radio_read_acf_blob,
1237 };
1238 
1239 static 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 
1262 static 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 
1268 static 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 
1290 static const struct file_operations radio_agc_fops = {
1291         .open   = simple_open,
1292         .llseek = default_llseek,
1293         .read   = si476x_radio_read_agc_blob,
1294 };
1295 
1296 static 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 
1325 static const struct file_operations radio_rsq_fops = {
1326         .open   = simple_open,
1327         .llseek = default_llseek,
1328         .read   = si476x_radio_read_rsq_blob,
1329 };
1330 
1331 static 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 
1360 static 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 
1367 static 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;
1417 cleanup:
1418         debugfs_remove_recursive(radio->debugfs);
1419 exit:
1420         return ret;
1421 }
1422 
1423 
1424 static 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 
1442 static 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                 si476x_radio_add_new_custom(radio, SI476X_IDX_DIVERSITY_MODE);
1534                 if (rval < 0)
1535                         goto exit;
1536 
1537                 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;
1556 exit:
1557         v4l2_ctrl_handler_free(radio->videodev.ctrl_handler);
1558         return rval;
1559 }
1560 
1561 static 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 
1573 MODULE_ALIAS("platform:si476x-radio");
1574 
1575 static struct platform_driver si476x_radio_driver = {
1576         .driver         = {
1577                 .name   = DRIVER_NAME,
1578                 .owner  = THIS_MODULE,
1579         },
1580         .probe          = si476x_radio_probe,
1581         .remove         = si476x_radio_remove,
1582 };
1583 module_platform_driver(si476x_radio_driver);
1584 
1585 MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
1586 MODULE_DESCRIPTION("Driver for Si4761/64/68 AM/FM Radio MFD Cell");
1587 MODULE_LICENSE("GPL");
1588 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us