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