Merge tag 'locking-core-2023-05-05' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-block.git] / drivers / media / radio / radio-si476x.c
CommitLineData
8e8e69d6 1// SPDX-License-Identifier: GPL-2.0-only
b879a9c2
AS
2/*
3 * drivers/media/radio/radio-si476x.c -- V4L2 driver for SI476X chips
4 *
5 * Copyright (C) 2012 Innovative Converged Devices(ICD)
6 * Copyright (C) 2013 Andrey Smirnov
7 *
8 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
b879a9c2
AS
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
d647f0b7 25#include <media/drv-intf/si476x.h>
b879a9c2
AS
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
ab671788 38#define SI476X_PHDIV_STATUS_LINK_LOCKED(status) (0x80 & (status))
b879a9c2
AS
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) {
df561f66
GS
108 default:
109 fallthrough;
b879a9c2
AS
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
d156f293 153 /*
b879a9c2 154 * SI476X during its station seeking(or tuning) process uses several
30bcc510 155 * parameters to determine if "the station" is valid:
b879a9c2
AS
156 *
157 * - Signal's SNR(in dBuV) must be lower than
158 * #V4L2_CID_SI476X_SNR_THRESHOLD
159 * - Signal's RSSI(in dBuV) must be greater than
160 * #V4L2_CID_SI476X_RSSI_THRESHOLD
161 * - Signal's frequency deviation(in units of 2ppm) must not be
162 * more than #V4L2_CID_SI476X_MAX_TUNE_ERROR
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
d156f293 192 /*
b879a9c2
AS
193 * #V4L2_CID_SI476X_HARMONICS_COUNT -- number of harmonics
194 * built-in power-line noise supression filter is to reject
195 * during AM-mode operation.
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
d156f293 208 /*
b879a9c2
AS
209 * #V4L2_CID_SI476X_DIVERSITY_MODE -- configuration which
210 * two tuners working in diversity mode are to work in.
211 *
212 * - #SI476X_IDX_PHDIV_DISABLED diversity mode disabled
213 * - #SI476X_IDX_PHDIV_PRIMARY_COMBINING diversity mode is
214 * on, primary tuner's antenna is the main one.
215 * - #SI476X_IDX_PHDIV_PRIMARY_ANTENNA diversity mode is
216 * off, primary tuner's antenna is the main one.
217 * - #SI476X_IDX_PHDIV_SECONDARY_ANTENNA diversity mode is
218 * off, secondary tuner's antenna is the main one.
219 * - #SI476X_IDX_PHDIV_SECONDARY_COMBINING diversity mode is
220 * on, secondary tuner's antenna is the main one.
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
d156f293 232 /*
b879a9c2
AS
233 * #V4L2_CID_SI476X_INTERCHIP_LINK -- inter-chip link in
234 * diversity mode indicator. Allows user to determine if two
235 * chips working in diversity mode have established a link
236 * between each other and if the system as a whole uses
237 * signals from both antennas to receive FM radio.
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 * struct si476x_radio_ops - vtable of tuner functions
255 *
256 * This table holds pointers to functions implementing particular
257 * operations depending on the mode in which the tuner chip was
30bcc510 258 * configured to start. If the function is not supported
b879a9c2
AS
259 * corresponding element is set to #NULL.
260 *
261 * @tune_freq: Tune chip to a specific frequency
262 * @seek_start: Star station seeking
39c1cb2b
JM
263 * @rsq_status: Get Received Signal Quality(RSQ) status
264 * @rds_blckcnt: Get received RDS blocks count
b879a9c2
AS
265 * @phase_diversity: Change phase diversity mode of the tuner
266 * @phase_div_status: Get phase diversity mode status
267 * @acf_status: Get the status of Automatically Controlled
268 * Features(ACF)
269 * @agc_status: Get Automatic Gain Control(AGC) status
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 * struct si476x_radio - radio device
290 *
d156f293 291 * @v4l2dev: Pointer to V4L2 device created by V4L2 subsystem
b879a9c2 292 * @videodev: Pointer to video device created by V4L2 subsystem
d156f293
MCC
293 * @ctrl_handler: V4L2 controls handler
294 * @core: Pointer to underlying core device
b879a9c2 295 * @ops: Vtable of functions. See struct si476x_radio_ops for details
d156f293
MCC
296 * @debugfs: pointer to &strucd dentry for debugfs
297 * @audmode: audio mode, as defined for the rxsubchans field
298 * at videodev2.h
299 *
b879a9c2
AS
300 * core structure is the radio device is being used
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 /* This field should not be accesses unless core lock is held */
309 const struct si476x_radio_ops *ops;
310
311 struct dentry *debugfs;
312 u32 audmode;
313};
314
b879a9c2
AS
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 * si476x_vidioc_querycap - query device capabilities
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
c0decac1 329 strscpy(capability->driver, radio->v4l2dev.name,
b879a9c2 330 sizeof(capability->driver));
c0decac1 331 strscpy(capability->card, DRIVER_CARD, sizeof(capability->card));
b879a9c2
AS
332 snprintf(capability->bus_info, sizeof(capability->bus_info),
333 "platform:%s", radio->v4l2dev.name);
b879a9c2
AS
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 /* AM/FM tuners -- all bands are supported */
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 /* FM companion tuner chips -- only FM bands are
358 * supported */
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 /* Measure frequencies
394 * in multiples of
395 * 62.5 Hz */
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)) {
c0decac1 404 strscpy(tuner->name, "FM (secondary)", sizeof(tuner->name));
b879a9c2
AS
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))
c0decac1 409 strscpy(tuner->name, "AM/FM (primary)",
b879a9c2
AS
410 sizeof(tuner->name));
411 else
c0decac1 412 strscpy(tuner->name, "AM/FM", sizeof(tuner->name));
b879a9c2
AS
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 {
c0decac1 422 strscpy(tuner->name, "FM", sizeof(tuner->name));
b879a9c2
AS
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 * tuner->signal value range: 0x0000 .. 0xFFFF,
442 * report.rssi: -128 .. 127
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 /* regcache_mark_dirty(radio->core->regmap); */
545 err = regcache_sync_region(radio->core->regmap,
546 SI476X_PROP_DIGITAL_IO_INPUT_SAMPLE_RATE,
547 SI476X_PROP_DIGITAL_IO_OUTPUT_FORMAT);
1cb3b795
MCC
548 if (err < 0)
549 return err;
b879a9c2
AS
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 * Is there any point in restoring SNR and the like
565 * when switching between AM/FM?
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 * Since power/up down is a very time consuming operation,
605 * try to avoid doing it if the requested mode matches the one
606 * the tuner is in
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 * OK, if the chip does not want to play nice let's
616 * try to reset it in more brutal way
617 */
618 soft = false;
619 err = si476x_core_stop(radio->core, soft);
620 if (err < 0)
621 return err;
622 }
623 /*
624 Set the desired radio tuner function
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 * No need to do the rest of manipulations for the bootlader
634 * mode
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;
02d73243 735 u32 rangelow = seek->rangelow, rangehigh = seek->rangehigh;
b879a9c2
AS
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
02d73243 747 if (!rangelow) {
b879a9c2
AS
748 err = regmap_read(radio->core->regmap,
749 SI476X_PROP_SEEK_BAND_BOTTOM,
750 &rangelow);
02d73243 751 if (err)
b879a9c2 752 goto unlock;
02d73243 753 rangelow = si476x_to_v4l2(radio->core, rangelow);
b879a9c2 754 }
02d73243 755 if (!rangehigh) {
b879a9c2
AS
756 err = regmap_read(radio->core->regmap,
757 SI476X_PROP_SEEK_BAND_TOP,
758 &rangehigh);
02d73243 759 if (err)
b879a9c2 760 goto unlock;
02d73243 761 rangehigh = si476x_to_v4l2(radio->core, rangehigh);
b879a9c2
AS
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 * It looks like RDS related properties are
30bcc510 914 * inaccessible when tuner is in AM mode, so cache the
b879a9c2
AS
915 * changes
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 /* Drain RDS FIFO before enabling RDS processing */
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 * Diversity cannot be configured while tuner
975 * is in AM mode so save the changes and carry on.
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
b879a9c2
AS
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 /*Must be done after si476x_core_unlock to prevent a deadlock*/
1058 v4l2_ctrl_handler_setup(&radio->ctrl_handler);
1059 }
1060
1061 return err;
1062
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{
b879a9c2
AS
1075 struct si476x_radio *radio = video_drvdata(file);
1076
1077 if (v4l2_fh_is_singular_file(file) &&
1078 atomic_read(&radio->core->is_alive))
1079 si476x_core_set_power_state(radio->core,
1080 SI476X_POWER_DOWN);
1081
bf4ed9e3 1082 return v4l2_fh_release(file);
b879a9c2
AS
1083}
1084
1085static ssize_t si476x_radio_fops_read(struct file *file, char __user *buf,
1086 size_t count, loff_t *ppos)
1087{
1088 ssize_t rval;
1089 size_t fifo_len;
1090 unsigned int copied;
1091
1092 struct si476x_radio *radio = video_drvdata(file);
1093
1094 /* block if no new data available */
1095 if (kfifo_is_empty(&radio->core->rds_fifo)) {
1096 if (file->f_flags & O_NONBLOCK)
1097 return -EWOULDBLOCK;
1098
1099 rval = wait_event_interruptible(radio->core->rds_read_queue,
1100 (!kfifo_is_empty(&radio->core->rds_fifo) ||
1101 !atomic_read(&radio->core->is_alive)));
1102 if (rval < 0)
1103 return -EINTR;
1104
1105 if (!atomic_read(&radio->core->is_alive))
1106 return -ENODEV;
1107 }
1108
1109 fifo_len = kfifo_len(&radio->core->rds_fifo);
1110
1111 if (kfifo_to_user(&radio->core->rds_fifo, buf,
1112 min(fifo_len, count),
1113 &copied) != 0) {
1114 dev_warn(&radio->videodev.dev,
1115 "Error during FIFO to userspace copy\n");
1116 rval = -EIO;
1117 } else {
1118 rval = (ssize_t)copied;
1119 }
1120
1121 return rval;
1122}
1123
c23e0cb8 1124static __poll_t si476x_radio_fops_poll(struct file *file,
b879a9c2
AS
1125 struct poll_table_struct *pts)
1126{
1127 struct si476x_radio *radio = video_drvdata(file);
01699437 1128 __poll_t req_events = poll_requested_events(pts);
c23e0cb8 1129 __poll_t err = v4l2_ctrl_poll(file, pts);
b879a9c2 1130
a9a08845 1131 if (req_events & (EPOLLIN | EPOLLRDNORM)) {
b879a9c2
AS
1132 if (atomic_read(&radio->core->is_alive))
1133 poll_wait(file, &radio->core->rds_read_queue, pts);
1134
1135 if (!atomic_read(&radio->core->is_alive))
a9a08845 1136 err = EPOLLHUP;
b879a9c2
AS
1137
1138 if (!kfifo_is_empty(&radio->core->rds_fifo))
a9a08845 1139 err = EPOLLIN | EPOLLRDNORM;
b879a9c2
AS
1140 }
1141
1142 return err;
1143}
1144
1145static const struct v4l2_file_operations si476x_fops = {
1146 .owner = THIS_MODULE,
1147 .read = si476x_radio_fops_read,
1148 .poll = si476x_radio_fops_poll,
1149 .unlocked_ioctl = video_ioctl2,
1150 .open = si476x_radio_fops_open,
1151 .release = si476x_radio_fops_release,
1152};
1153
1154
1155static const struct v4l2_ioctl_ops si4761_ioctl_ops = {
1156 .vidioc_querycap = si476x_radio_querycap,
1157 .vidioc_g_tuner = si476x_radio_g_tuner,
1158 .vidioc_s_tuner = si476x_radio_s_tuner,
1159
1160 .vidioc_g_frequency = si476x_radio_g_frequency,
1161 .vidioc_s_frequency = si476x_radio_s_frequency,
1162 .vidioc_s_hw_freq_seek = si476x_radio_s_hw_freq_seek,
1163 .vidioc_enum_freq_bands = si476x_radio_enum_freq_bands,
1164
1165 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
1166 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
1167
b879a9c2
AS
1168#ifdef CONFIG_VIDEO_ADV_DEBUG
1169 .vidioc_g_register = si476x_radio_g_register,
1170 .vidioc_s_register = si476x_radio_s_register,
1171#endif
1172};
1173
1174
1175static const struct video_device si476x_viddev_template = {
1176 .fops = &si476x_fops,
1177 .name = DRIVER_NAME,
1178 .release = video_device_release_empty,
1179};
1180
1181
1182
1183static ssize_t si476x_radio_read_acf_blob(struct file *file,
1184 char __user *user_buf,
1185 size_t count, loff_t *ppos)
1186{
1187 int err;
1188 struct si476x_radio *radio = file->private_data;
1189 struct si476x_acf_status_report report;
1190
1191 si476x_core_lock(radio->core);
1192 if (radio->ops->acf_status)
1193 err = radio->ops->acf_status(radio->core, &report);
1194 else
1195 err = -ENOENT;
1196 si476x_core_unlock(radio->core);
1197
1198 if (err < 0)
1199 return err;
1200
1201 return simple_read_from_buffer(user_buf, count, ppos, &report,
1202 sizeof(report));
1203}
1204
1205static const struct file_operations radio_acf_fops = {
1206 .open = simple_open,
1207 .llseek = default_llseek,
1208 .read = si476x_radio_read_acf_blob,
1209};
1210
1211static ssize_t si476x_radio_read_rds_blckcnt_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_rds_blockcount_report report;
1218
1219 si476x_core_lock(radio->core);
1220 if (radio->ops->rds_blckcnt)
1221 err = radio->ops->rds_blckcnt(radio->core, true,
1222 &report);
1223 else
1224 err = -ENOENT;
1225 si476x_core_unlock(radio->core);
1226
1227 if (err < 0)
1228 return err;
1229
1230 return simple_read_from_buffer(user_buf, count, ppos, &report,
1231 sizeof(report));
1232}
1233
1234static const struct file_operations radio_rds_blckcnt_fops = {
1235 .open = simple_open,
1236 .llseek = default_llseek,
1237 .read = si476x_radio_read_rds_blckcnt_blob,
1238};
1239
1240static ssize_t si476x_radio_read_agc_blob(struct file *file,
1241 char __user *user_buf,
1242 size_t count, loff_t *ppos)
1243{
1244 int err;
1245 struct si476x_radio *radio = file->private_data;
1246 struct si476x_agc_status_report report;
1247
1248 si476x_core_lock(radio->core);
1249 if (radio->ops->rds_blckcnt)
1250 err = radio->ops->agc_status(radio->core, &report);
1251 else
1252 err = -ENOENT;
1253 si476x_core_unlock(radio->core);
1254
1255 if (err < 0)
1256 return err;
1257
1258 return simple_read_from_buffer(user_buf, count, ppos, &report,
1259 sizeof(report));
1260}
1261
1262static const struct file_operations radio_agc_fops = {
1263 .open = simple_open,
1264 .llseek = default_llseek,
1265 .read = si476x_radio_read_agc_blob,
1266};
1267
1268static ssize_t si476x_radio_read_rsq_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_rsq_status_report report;
1275 struct si476x_rsq_status_args args = {
1276 .primary = false,
1277 .rsqack = false,
1278 .attune = false,
1279 .cancel = false,
1280 .stcack = false,
1281 };
1282
1283 si476x_core_lock(radio->core);
1284 if (radio->ops->rds_blckcnt)
1285 err = radio->ops->rsq_status(radio->core, &args, &report);
1286 else
1287 err = -ENOENT;
1288 si476x_core_unlock(radio->core);
1289
1290 if (err < 0)
1291 return err;
1292
1293 return simple_read_from_buffer(user_buf, count, ppos, &report,
1294 sizeof(report));
1295}
1296
1297static const struct file_operations radio_rsq_fops = {
1298 .open = simple_open,
1299 .llseek = default_llseek,
1300 .read = si476x_radio_read_rsq_blob,
1301};
1302
1303static ssize_t si476x_radio_read_rsq_primary_blob(struct file *file,
1304 char __user *user_buf,
1305 size_t count, loff_t *ppos)
1306{
1307 int err;
1308 struct si476x_radio *radio = file->private_data;
1309 struct si476x_rsq_status_report report;
1310 struct si476x_rsq_status_args args = {
1311 .primary = true,
1312 .rsqack = false,
1313 .attune = false,
1314 .cancel = false,
1315 .stcack = false,
1316 };
1317
1318 si476x_core_lock(radio->core);
1319 if (radio->ops->rds_blckcnt)
1320 err = radio->ops->rsq_status(radio->core, &args, &report);
1321 else
1322 err = -ENOENT;
1323 si476x_core_unlock(radio->core);
1324
1325 if (err < 0)
1326 return err;
1327
1328 return simple_read_from_buffer(user_buf, count, ppos, &report,
1329 sizeof(report));
1330}
1331
1332static const struct file_operations radio_rsq_primary_fops = {
1333 .open = simple_open,
1334 .llseek = default_llseek,
1335 .read = si476x_radio_read_rsq_primary_blob,
1336};
1337
1338
1d8f95c4 1339static void si476x_radio_init_debugfs(struct si476x_radio *radio)
b879a9c2 1340{
1d8f95c4 1341 radio->debugfs = debugfs_create_dir(dev_name(radio->v4l2dev.dev), NULL);
b879a9c2 1342
1d8f95c4
GKH
1343 debugfs_create_file("acf", S_IRUGO, radio->debugfs, radio,
1344 &radio_acf_fops);
b879a9c2 1345
1d8f95c4
GKH
1346 debugfs_create_file("rds_blckcnt", S_IRUGO, radio->debugfs, radio,
1347 &radio_rds_blckcnt_fops);
b879a9c2 1348
1d8f95c4
GKH
1349 debugfs_create_file("agc", S_IRUGO, radio->debugfs, radio,
1350 &radio_agc_fops);
b879a9c2 1351
1d8f95c4
GKH
1352 debugfs_create_file("rsq", S_IRUGO, radio->debugfs, radio,
1353 &radio_rsq_fops);
b879a9c2 1354
1d8f95c4
GKH
1355 debugfs_create_file("rsq_primary", S_IRUGO, radio->debugfs, radio,
1356 &radio_rsq_primary_fops);
b879a9c2
AS
1357}
1358
1359
1360static int si476x_radio_add_new_custom(struct si476x_radio *radio,
1361 enum si476x_ctrl_idx idx)
1362{
1363 int rval;
1364 struct v4l2_ctrl *ctrl;
1365
1366 ctrl = v4l2_ctrl_new_custom(&radio->ctrl_handler,
1367 &si476x_ctrls[idx],
1368 NULL);
1369 rval = radio->ctrl_handler.error;
1370 if (ctrl == NULL && rval)
1371 dev_err(radio->v4l2dev.dev,
1372 "Could not initialize '%s' control %d\n",
1373 si476x_ctrls[idx].name, rval);
1374
1375 return rval;
1376}
1377
1378static int si476x_radio_probe(struct platform_device *pdev)
1379{
1380 int rval;
1381 struct si476x_radio *radio;
1382 struct v4l2_ctrl *ctrl;
1383
1384 static atomic_t instance = ATOMIC_INIT(0);
1385
1386 radio = devm_kzalloc(&pdev->dev, sizeof(*radio), GFP_KERNEL);
1387 if (!radio)
1388 return -ENOMEM;
1389
1390 radio->core = i2c_mfd_cell_to_core(&pdev->dev);
1391
1392 v4l2_device_set_name(&radio->v4l2dev, DRIVER_NAME, &instance);
1393
1394 rval = v4l2_device_register(&pdev->dev, &radio->v4l2dev);
1395 if (rval) {
1396 dev_err(&pdev->dev, "Cannot register v4l2_device.\n");
1397 return rval;
1398 }
1399
1400 memcpy(&radio->videodev, &si476x_viddev_template,
1401 sizeof(struct video_device));
1402
1403 radio->videodev.v4l2_dev = &radio->v4l2dev;
1404 radio->videodev.ioctl_ops = &si4761_ioctl_ops;
e83ce300
HV
1405 radio->videodev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
1406 V4L2_CAP_HW_FREQ_SEEK;
1407
1408 si476x_core_lock(radio->core);
1409 if (!si476x_core_is_a_secondary_tuner(radio->core))
1410 radio->videodev.device_caps |= V4L2_CAP_RDS_CAPTURE |
1411 V4L2_CAP_READWRITE;
1412 si476x_core_unlock(radio->core);
b879a9c2
AS
1413
1414 video_set_drvdata(&radio->videodev, radio);
1415 platform_set_drvdata(pdev, radio);
1416
b879a9c2
AS
1417
1418 radio->v4l2dev.ctrl_handler = &radio->ctrl_handler;
1419 v4l2_ctrl_handler_init(&radio->ctrl_handler,
1420 1 + ARRAY_SIZE(si476x_ctrls));
1421
1422 if (si476x_core_has_am(radio->core)) {
1423 ctrl = v4l2_ctrl_new_std_menu(&radio->ctrl_handler,
1424 &si476x_ctrl_ops,
1425 V4L2_CID_POWER_LINE_FREQUENCY,
1426 V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
1427 0, 0);
1428 rval = radio->ctrl_handler.error;
1429 if (ctrl == NULL && rval) {
1430 dev_err(&pdev->dev, "Could not initialize V4L2_CID_POWER_LINE_FREQUENCY control %d\n",
1431 rval);
1432 goto exit;
1433 }
1434
1435 rval = si476x_radio_add_new_custom(radio,
1436 SI476X_IDX_HARMONICS_COUNT);
1437 if (rval < 0)
1438 goto exit;
1439 }
1440
1441 rval = si476x_radio_add_new_custom(radio, SI476X_IDX_RSSI_THRESHOLD);
1442 if (rval < 0)
1443 goto exit;
1444
1445 rval = si476x_radio_add_new_custom(radio, SI476X_IDX_SNR_THRESHOLD);
1446 if (rval < 0)
1447 goto exit;
1448
1449 rval = si476x_radio_add_new_custom(radio, SI476X_IDX_MAX_TUNE_ERROR);
1450 if (rval < 0)
1451 goto exit;
1452
1453 ctrl = v4l2_ctrl_new_std_menu(&radio->ctrl_handler,
1454 &si476x_ctrl_ops,
1455 V4L2_CID_TUNE_DEEMPHASIS,
1456 V4L2_DEEMPHASIS_75_uS, 0, 0);
1457 rval = radio->ctrl_handler.error;
1458 if (ctrl == NULL && rval) {
1459 dev_err(&pdev->dev, "Could not initialize V4L2_CID_TUNE_DEEMPHASIS control %d\n",
1460 rval);
1461 goto exit;
1462 }
1463
1464 ctrl = v4l2_ctrl_new_std(&radio->ctrl_handler, &si476x_ctrl_ops,
1465 V4L2_CID_RDS_RECEPTION,
1466 0, 1, 1, 1);
1467 rval = radio->ctrl_handler.error;
1468 if (ctrl == NULL && rval) {
1469 dev_err(&pdev->dev, "Could not initialize V4L2_CID_RDS_RECEPTION control %d\n",
1470 rval);
1471 goto exit;
1472 }
1473
1474 if (si476x_core_has_diversity(radio->core)) {
1475 si476x_ctrls[SI476X_IDX_DIVERSITY_MODE].def =
1476 si476x_phase_diversity_mode_to_idx(radio->core->diversity_mode);
949cf706 1477 rval = si476x_radio_add_new_custom(radio, SI476X_IDX_DIVERSITY_MODE);
b879a9c2
AS
1478 if (rval < 0)
1479 goto exit;
1480
949cf706 1481 rval = si476x_radio_add_new_custom(radio, SI476X_IDX_INTERCHIP_LINK);
b879a9c2
AS
1482 if (rval < 0)
1483 goto exit;
1484 }
1485
1486 /* register video device */
1487 rval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, -1);
1488 if (rval < 0) {
1489 dev_err(&pdev->dev, "Could not register video device\n");
1490 goto exit;
1491 }
1492
1d8f95c4 1493 si476x_radio_init_debugfs(radio);
b879a9c2
AS
1494
1495 return 0;
1496exit:
1497 v4l2_ctrl_handler_free(radio->videodev.ctrl_handler);
1498 return rval;
1499}
1500
830d1151 1501static void si476x_radio_remove(struct platform_device *pdev)
b879a9c2
AS
1502{
1503 struct si476x_radio *radio = platform_get_drvdata(pdev);
1504
1505 v4l2_ctrl_handler_free(radio->videodev.ctrl_handler);
1506 video_unregister_device(&radio->videodev);
1507 v4l2_device_unregister(&radio->v4l2dev);
1508 debugfs_remove_recursive(radio->debugfs);
b879a9c2
AS
1509}
1510
1511MODULE_ALIAS("platform:si476x-radio");
1512
1513static struct platform_driver si476x_radio_driver = {
1514 .driver = {
1515 .name = DRIVER_NAME,
b879a9c2
AS
1516 },
1517 .probe = si476x_radio_probe,
830d1151 1518 .remove_new = si476x_radio_remove,
b879a9c2
AS
1519};
1520module_platform_driver(si476x_radio_driver);
1521
1522MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
1523MODULE_DESCRIPTION("Driver for Si4761/64/68 AM/FM Radio MFD Cell");
1524MODULE_LICENSE("GPL");