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