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