radio-si476x.c (39882B)
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 43enum si476x_freq_bands { 44 SI476X_BAND_FM, 45 SI476X_BAND_AM, 46}; 47 48static const struct v4l2_frequency_band si476x_bands[] = { 49 [SI476X_BAND_FM] = { 50 .type = V4L2_TUNER_RADIO, 51 .index = SI476X_BAND_FM, 52 .capability = V4L2_TUNER_CAP_LOW 53 | V4L2_TUNER_CAP_STEREO 54 | V4L2_TUNER_CAP_RDS 55 | V4L2_TUNER_CAP_RDS_BLOCK_IO 56 | V4L2_TUNER_CAP_FREQ_BANDS, 57 .rangelow = 64 * FREQ_MUL, 58 .rangehigh = 108 * FREQ_MUL, 59 .modulation = V4L2_BAND_MODULATION_FM, 60 }, 61 [SI476X_BAND_AM] = { 62 .type = V4L2_TUNER_RADIO, 63 .index = SI476X_BAND_AM, 64 .capability = V4L2_TUNER_CAP_LOW 65 | V4L2_TUNER_CAP_FREQ_BANDS, 66 .rangelow = 0.52 * FREQ_MUL, 67 .rangehigh = 30 * FREQ_MUL, 68 .modulation = V4L2_BAND_MODULATION_AM, 69 }, 70}; 71 72static inline bool si476x_radio_freq_is_inside_of_the_band(u32 freq, int band) 73{ 74 return freq >= si476x_bands[band].rangelow && 75 freq <= si476x_bands[band].rangehigh; 76} 77 78static inline bool si476x_radio_range_is_inside_of_the_band(u32 low, u32 high, 79 int band) 80{ 81 return low >= si476x_bands[band].rangelow && 82 high <= si476x_bands[band].rangehigh; 83} 84 85static int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl); 86static int si476x_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl); 87 88enum phase_diversity_modes_idx { 89 SI476X_IDX_PHDIV_DISABLED, 90 SI476X_IDX_PHDIV_PRIMARY_COMBINING, 91 SI476X_IDX_PHDIV_PRIMARY_ANTENNA, 92 SI476X_IDX_PHDIV_SECONDARY_ANTENNA, 93 SI476X_IDX_PHDIV_SECONDARY_COMBINING, 94}; 95 96static const char * const phase_diversity_modes[] = { 97 [SI476X_IDX_PHDIV_DISABLED] = "Disabled", 98 [SI476X_IDX_PHDIV_PRIMARY_COMBINING] = "Primary with Secondary", 99 [SI476X_IDX_PHDIV_PRIMARY_ANTENNA] = "Primary Antenna", 100 [SI476X_IDX_PHDIV_SECONDARY_ANTENNA] = "Secondary Antenna", 101 [SI476X_IDX_PHDIV_SECONDARY_COMBINING] = "Secondary with Primary", 102}; 103 104static inline enum phase_diversity_modes_idx 105si476x_phase_diversity_mode_to_idx(enum si476x_phase_diversity_mode mode) 106{ 107 switch (mode) { 108 default: 109 fallthrough; 110 case SI476X_PHDIV_DISABLED: 111 return SI476X_IDX_PHDIV_DISABLED; 112 case SI476X_PHDIV_PRIMARY_COMBINING: 113 return SI476X_IDX_PHDIV_PRIMARY_COMBINING; 114 case SI476X_PHDIV_PRIMARY_ANTENNA: 115 return SI476X_IDX_PHDIV_PRIMARY_ANTENNA; 116 case SI476X_PHDIV_SECONDARY_ANTENNA: 117 return SI476X_IDX_PHDIV_SECONDARY_ANTENNA; 118 case SI476X_PHDIV_SECONDARY_COMBINING: 119 return SI476X_IDX_PHDIV_SECONDARY_COMBINING; 120 } 121} 122 123static inline enum si476x_phase_diversity_mode 124si476x_phase_diversity_idx_to_mode(enum phase_diversity_modes_idx idx) 125{ 126 static const int idx_to_value[] = { 127 [SI476X_IDX_PHDIV_DISABLED] = SI476X_PHDIV_DISABLED, 128 [SI476X_IDX_PHDIV_PRIMARY_COMBINING] = SI476X_PHDIV_PRIMARY_COMBINING, 129 [SI476X_IDX_PHDIV_PRIMARY_ANTENNA] = SI476X_PHDIV_PRIMARY_ANTENNA, 130 [SI476X_IDX_PHDIV_SECONDARY_ANTENNA] = SI476X_PHDIV_SECONDARY_ANTENNA, 131 [SI476X_IDX_PHDIV_SECONDARY_COMBINING] = SI476X_PHDIV_SECONDARY_COMBINING, 132 }; 133 134 return idx_to_value[idx]; 135} 136 137static const struct v4l2_ctrl_ops si476x_ctrl_ops = { 138 .g_volatile_ctrl = si476x_radio_g_volatile_ctrl, 139 .s_ctrl = si476x_radio_s_ctrl, 140}; 141 142 143enum si476x_ctrl_idx { 144 SI476X_IDX_RSSI_THRESHOLD, 145 SI476X_IDX_SNR_THRESHOLD, 146 SI476X_IDX_MAX_TUNE_ERROR, 147 SI476X_IDX_HARMONICS_COUNT, 148 SI476X_IDX_DIVERSITY_MODE, 149 SI476X_IDX_INTERCHIP_LINK, 150}; 151static struct v4l2_ctrl_config si476x_ctrls[] = { 152 153 /* 154 * 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 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 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 */ 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 * 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 */ 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 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 329 strscpy(capability->driver, radio->v4l2dev.name, 330 sizeof(capability->driver)); 331 strscpy(capability->card, DRIVER_CARD, sizeof(capability->card)); 332 snprintf(capability->bus_info, sizeof(capability->bus_info), 333 "platform:%s", radio->v4l2dev.name); 334 return 0; 335} 336 337static int si476x_radio_enum_freq_bands(struct file *file, void *priv, 338 struct v4l2_frequency_band *band) 339{ 340 int err; 341 struct si476x_radio *radio = video_drvdata(file); 342 343 if (band->tuner != 0) 344 return -EINVAL; 345 346 switch (radio->core->chip_id) { 347 /* 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)) { 404 strscpy(tuner->name, "FM (secondary)", sizeof(tuner->name)); 405 tuner->rxsubchans = 0; 406 tuner->rangelow = si476x_bands[SI476X_BAND_FM].rangelow; 407 } else if (si476x_core_has_am(radio->core)) { 408 if (si476x_core_is_a_primary_tuner(radio->core)) 409 strscpy(tuner->name, "AM/FM (primary)", 410 sizeof(tuner->name)); 411 else 412 strscpy(tuner->name, "AM/FM", sizeof(tuner->name)); 413 414 tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO 415 | V4L2_TUNER_SUB_RDS; 416 tuner->capability |= V4L2_TUNER_CAP_RDS 417 | V4L2_TUNER_CAP_RDS_BLOCK_IO 418 | V4L2_TUNER_CAP_FREQ_BANDS; 419 420 tuner->rangelow = si476x_bands[SI476X_BAND_AM].rangelow; 421 } else { 422 strscpy(tuner->name, "FM", sizeof(tuner->name)); 423 tuner->rxsubchans = V4L2_TUNER_SUB_RDS; 424 tuner->capability |= V4L2_TUNER_CAP_RDS 425 | V4L2_TUNER_CAP_RDS_BLOCK_IO 426 | V4L2_TUNER_CAP_FREQ_BANDS; 427 tuner->rangelow = si476x_bands[SI476X_BAND_FM].rangelow; 428 } 429 430 tuner->audmode = radio->audmode; 431 432 tuner->afc = 1; 433 tuner->rangehigh = si476x_bands[SI476X_BAND_FM].rangehigh; 434 435 err = radio->ops->rsq_status(radio->core, 436 &args, &report); 437 if (err < 0) { 438 tuner->signal = 0; 439 } else { 440 /* 441 * 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); 548 if (err < 0) 549 return err; 550 551 err = regcache_sync_region(radio->core->regmap, 552 SI476X_PROP_AUDIO_DEEMPHASIS, 553 SI476X_PROP_AUDIO_PWR_LINE_FILTER); 554 if (err < 0) 555 return err; 556 557 err = regcache_sync_region(radio->core->regmap, 558 SI476X_PROP_INT_CTL_ENABLE, 559 SI476X_PROP_INT_CTL_ENABLE); 560 if (err < 0) 561 return err; 562 563 /* 564 * 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; 735 u32 rangelow = seek->rangelow, rangehigh = seek->rangehigh; 736 struct si476x_radio *radio = video_drvdata(file); 737 738 if (file->f_flags & O_NONBLOCK) 739 return -EAGAIN; 740 741 if (seek->tuner != 0 || 742 seek->type != V4L2_TUNER_RADIO) 743 return -EINVAL; 744 745 si476x_core_lock(radio->core); 746 747 if (!rangelow) { 748 err = regmap_read(radio->core->regmap, 749 SI476X_PROP_SEEK_BAND_BOTTOM, 750 &rangelow); 751 if (err) 752 goto unlock; 753 rangelow = si476x_to_v4l2(radio->core, rangelow); 754 } 755 if (!rangehigh) { 756 err = regmap_read(radio->core->regmap, 757 SI476X_PROP_SEEK_BAND_TOP, 758 &rangehigh); 759 if (err) 760 goto unlock; 761 rangehigh = si476x_to_v4l2(radio->core, rangehigh); 762 } 763 764 if (rangelow > rangehigh) { 765 err = -EINVAL; 766 goto unlock; 767 } 768 769 if (si476x_radio_range_is_inside_of_the_band(rangelow, rangehigh, 770 SI476X_BAND_FM)) { 771 func = SI476X_FUNC_FM_RECEIVER; 772 773 } else if (si476x_core_has_am(radio->core) && 774 si476x_radio_range_is_inside_of_the_band(rangelow, rangehigh, 775 SI476X_BAND_AM)) { 776 func = SI476X_FUNC_AM_RECEIVER; 777 } else { 778 err = -EINVAL; 779 goto unlock; 780 } 781 782 err = si476x_radio_change_func(radio, func); 783 if (err < 0) 784 goto unlock; 785 786 if (seek->rangehigh) { 787 err = regmap_write(radio->core->regmap, 788 SI476X_PROP_SEEK_BAND_TOP, 789 v4l2_to_si476x(radio->core, 790 seek->rangehigh)); 791 if (err) 792 goto unlock; 793 } 794 if (seek->rangelow) { 795 err = regmap_write(radio->core->regmap, 796 SI476X_PROP_SEEK_BAND_BOTTOM, 797 v4l2_to_si476x(radio->core, 798 seek->rangelow)); 799 if (err) 800 goto unlock; 801 } 802 if (seek->spacing) { 803 err = regmap_write(radio->core->regmap, 804 SI476X_PROP_SEEK_FREQUENCY_SPACING, 805 v4l2_to_si476x(radio->core, 806 seek->spacing)); 807 if (err) 808 goto unlock; 809 } 810 811 err = radio->ops->seek_start(radio->core, 812 seek->seek_upward, 813 seek->wrap_around); 814unlock: 815 si476x_core_unlock(radio->core); 816 817 818 819 return err; 820} 821 822static int si476x_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl) 823{ 824 int retval; 825 struct si476x_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler); 826 827 si476x_core_lock(radio->core); 828 829 switch (ctrl->id) { 830 case V4L2_CID_SI476X_INTERCHIP_LINK: 831 if (si476x_core_has_diversity(radio->core)) { 832 if (radio->ops->phase_diversity) { 833 retval = radio->ops->phase_div_status(radio->core); 834 if (retval < 0) 835 break; 836 837 ctrl->val = !!SI476X_PHDIV_STATUS_LINK_LOCKED(retval); 838 retval = 0; 839 break; 840 } else { 841 retval = -ENOTTY; 842 break; 843 } 844 } 845 retval = -EINVAL; 846 break; 847 default: 848 retval = -EINVAL; 849 break; 850 } 851 si476x_core_unlock(radio->core); 852 return retval; 853 854} 855 856static int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl) 857{ 858 int retval; 859 enum si476x_phase_diversity_mode mode; 860 struct si476x_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler); 861 862 si476x_core_lock(radio->core); 863 864 switch (ctrl->id) { 865 case V4L2_CID_SI476X_HARMONICS_COUNT: 866 retval = regmap_update_bits(radio->core->regmap, 867 SI476X_PROP_AUDIO_PWR_LINE_FILTER, 868 SI476X_PROP_PWR_HARMONICS_MASK, 869 ctrl->val); 870 break; 871 case V4L2_CID_POWER_LINE_FREQUENCY: 872 switch (ctrl->val) { 873 case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: 874 retval = regmap_update_bits(radio->core->regmap, 875 SI476X_PROP_AUDIO_PWR_LINE_FILTER, 876 SI476X_PROP_PWR_ENABLE_MASK, 877 0); 878 break; 879 case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: 880 retval = regmap_update_bits(radio->core->regmap, 881 SI476X_PROP_AUDIO_PWR_LINE_FILTER, 882 SI476X_PROP_PWR_GRID_MASK, 883 SI476X_PROP_PWR_GRID_50HZ); 884 break; 885 case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: 886 retval = regmap_update_bits(radio->core->regmap, 887 SI476X_PROP_AUDIO_PWR_LINE_FILTER, 888 SI476X_PROP_PWR_GRID_MASK, 889 SI476X_PROP_PWR_GRID_60HZ); 890 break; 891 default: 892 retval = -EINVAL; 893 break; 894 } 895 break; 896 case V4L2_CID_SI476X_RSSI_THRESHOLD: 897 retval = regmap_write(radio->core->regmap, 898 SI476X_PROP_VALID_RSSI_THRESHOLD, 899 ctrl->val); 900 break; 901 case V4L2_CID_SI476X_SNR_THRESHOLD: 902 retval = regmap_write(radio->core->regmap, 903 SI476X_PROP_VALID_SNR_THRESHOLD, 904 ctrl->val); 905 break; 906 case V4L2_CID_SI476X_MAX_TUNE_ERROR: 907 retval = regmap_write(radio->core->regmap, 908 SI476X_PROP_VALID_MAX_TUNE_ERROR, 909 ctrl->val); 910 break; 911 case V4L2_CID_RDS_RECEPTION: 912 /* 913 * It looks like RDS related properties are 914 * inaccessible when tuner is in AM mode, so cache the 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 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{ 1075 int err; 1076 struct si476x_radio *radio = video_drvdata(file); 1077 1078 if (v4l2_fh_is_singular_file(file) && 1079 atomic_read(&radio->core->is_alive)) 1080 si476x_core_set_power_state(radio->core, 1081 SI476X_POWER_DOWN); 1082 1083 err = v4l2_fh_release(file); 1084 1085 return err; 1086} 1087 1088static ssize_t si476x_radio_fops_read(struct file *file, char __user *buf, 1089 size_t count, loff_t *ppos) 1090{ 1091 ssize_t rval; 1092 size_t fifo_len; 1093 unsigned int copied; 1094 1095 struct si476x_radio *radio = video_drvdata(file); 1096 1097 /* block if no new data available */ 1098 if (kfifo_is_empty(&radio->core->rds_fifo)) { 1099 if (file->f_flags & O_NONBLOCK) 1100 return -EWOULDBLOCK; 1101 1102 rval = wait_event_interruptible(radio->core->rds_read_queue, 1103 (!kfifo_is_empty(&radio->core->rds_fifo) || 1104 !atomic_read(&radio->core->is_alive))); 1105 if (rval < 0) 1106 return -EINTR; 1107 1108 if (!atomic_read(&radio->core->is_alive)) 1109 return -ENODEV; 1110 } 1111 1112 fifo_len = kfifo_len(&radio->core->rds_fifo); 1113 1114 if (kfifo_to_user(&radio->core->rds_fifo, buf, 1115 min(fifo_len, count), 1116 &copied) != 0) { 1117 dev_warn(&radio->videodev.dev, 1118 "Error during FIFO to userspace copy\n"); 1119 rval = -EIO; 1120 } else { 1121 rval = (ssize_t)copied; 1122 } 1123 1124 return rval; 1125} 1126 1127static __poll_t si476x_radio_fops_poll(struct file *file, 1128 struct poll_table_struct *pts) 1129{ 1130 struct si476x_radio *radio = video_drvdata(file); 1131 __poll_t req_events = poll_requested_events(pts); 1132 __poll_t err = v4l2_ctrl_poll(file, pts); 1133 1134 if (req_events & (EPOLLIN | EPOLLRDNORM)) { 1135 if (atomic_read(&radio->core->is_alive)) 1136 poll_wait(file, &radio->core->rds_read_queue, pts); 1137 1138 if (!atomic_read(&radio->core->is_alive)) 1139 err = EPOLLHUP; 1140 1141 if (!kfifo_is_empty(&radio->core->rds_fifo)) 1142 err = EPOLLIN | EPOLLRDNORM; 1143 } 1144 1145 return err; 1146} 1147 1148static const struct v4l2_file_operations si476x_fops = { 1149 .owner = THIS_MODULE, 1150 .read = si476x_radio_fops_read, 1151 .poll = si476x_radio_fops_poll, 1152 .unlocked_ioctl = video_ioctl2, 1153 .open = si476x_radio_fops_open, 1154 .release = si476x_radio_fops_release, 1155}; 1156 1157 1158static const struct v4l2_ioctl_ops si4761_ioctl_ops = { 1159 .vidioc_querycap = si476x_radio_querycap, 1160 .vidioc_g_tuner = si476x_radio_g_tuner, 1161 .vidioc_s_tuner = si476x_radio_s_tuner, 1162 1163 .vidioc_g_frequency = si476x_radio_g_frequency, 1164 .vidioc_s_frequency = si476x_radio_s_frequency, 1165 .vidioc_s_hw_freq_seek = si476x_radio_s_hw_freq_seek, 1166 .vidioc_enum_freq_bands = si476x_radio_enum_freq_bands, 1167 1168 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 1169 .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 1170 1171#ifdef CONFIG_VIDEO_ADV_DEBUG 1172 .vidioc_g_register = si476x_radio_g_register, 1173 .vidioc_s_register = si476x_radio_s_register, 1174#endif 1175}; 1176 1177 1178static const struct video_device si476x_viddev_template = { 1179 .fops = &si476x_fops, 1180 .name = DRIVER_NAME, 1181 .release = video_device_release_empty, 1182}; 1183 1184 1185 1186static ssize_t si476x_radio_read_acf_blob(struct file *file, 1187 char __user *user_buf, 1188 size_t count, loff_t *ppos) 1189{ 1190 int err; 1191 struct si476x_radio *radio = file->private_data; 1192 struct si476x_acf_status_report report; 1193 1194 si476x_core_lock(radio->core); 1195 if (radio->ops->acf_status) 1196 err = radio->ops->acf_status(radio->core, &report); 1197 else 1198 err = -ENOENT; 1199 si476x_core_unlock(radio->core); 1200 1201 if (err < 0) 1202 return err; 1203 1204 return simple_read_from_buffer(user_buf, count, ppos, &report, 1205 sizeof(report)); 1206} 1207 1208static const struct file_operations radio_acf_fops = { 1209 .open = simple_open, 1210 .llseek = default_llseek, 1211 .read = si476x_radio_read_acf_blob, 1212}; 1213 1214static ssize_t si476x_radio_read_rds_blckcnt_blob(struct file *file, 1215 char __user *user_buf, 1216 size_t count, loff_t *ppos) 1217{ 1218 int err; 1219 struct si476x_radio *radio = file->private_data; 1220 struct si476x_rds_blockcount_report report; 1221 1222 si476x_core_lock(radio->core); 1223 if (radio->ops->rds_blckcnt) 1224 err = radio->ops->rds_blckcnt(radio->core, true, 1225 &report); 1226 else 1227 err = -ENOENT; 1228 si476x_core_unlock(radio->core); 1229 1230 if (err < 0) 1231 return err; 1232 1233 return simple_read_from_buffer(user_buf, count, ppos, &report, 1234 sizeof(report)); 1235} 1236 1237static const struct file_operations radio_rds_blckcnt_fops = { 1238 .open = simple_open, 1239 .llseek = default_llseek, 1240 .read = si476x_radio_read_rds_blckcnt_blob, 1241}; 1242 1243static ssize_t si476x_radio_read_agc_blob(struct file *file, 1244 char __user *user_buf, 1245 size_t count, loff_t *ppos) 1246{ 1247 int err; 1248 struct si476x_radio *radio = file->private_data; 1249 struct si476x_agc_status_report report; 1250 1251 si476x_core_lock(radio->core); 1252 if (radio->ops->rds_blckcnt) 1253 err = radio->ops->agc_status(radio->core, &report); 1254 else 1255 err = -ENOENT; 1256 si476x_core_unlock(radio->core); 1257 1258 if (err < 0) 1259 return err; 1260 1261 return simple_read_from_buffer(user_buf, count, ppos, &report, 1262 sizeof(report)); 1263} 1264 1265static const struct file_operations radio_agc_fops = { 1266 .open = simple_open, 1267 .llseek = default_llseek, 1268 .read = si476x_radio_read_agc_blob, 1269}; 1270 1271static ssize_t si476x_radio_read_rsq_blob(struct file *file, 1272 char __user *user_buf, 1273 size_t count, loff_t *ppos) 1274{ 1275 int err; 1276 struct si476x_radio *radio = file->private_data; 1277 struct si476x_rsq_status_report report; 1278 struct si476x_rsq_status_args args = { 1279 .primary = false, 1280 .rsqack = false, 1281 .attune = false, 1282 .cancel = false, 1283 .stcack = false, 1284 }; 1285 1286 si476x_core_lock(radio->core); 1287 if (radio->ops->rds_blckcnt) 1288 err = radio->ops->rsq_status(radio->core, &args, &report); 1289 else 1290 err = -ENOENT; 1291 si476x_core_unlock(radio->core); 1292 1293 if (err < 0) 1294 return err; 1295 1296 return simple_read_from_buffer(user_buf, count, ppos, &report, 1297 sizeof(report)); 1298} 1299 1300static const struct file_operations radio_rsq_fops = { 1301 .open = simple_open, 1302 .llseek = default_llseek, 1303 .read = si476x_radio_read_rsq_blob, 1304}; 1305 1306static ssize_t si476x_radio_read_rsq_primary_blob(struct file *file, 1307 char __user *user_buf, 1308 size_t count, loff_t *ppos) 1309{ 1310 int err; 1311 struct si476x_radio *radio = file->private_data; 1312 struct si476x_rsq_status_report report; 1313 struct si476x_rsq_status_args args = { 1314 .primary = true, 1315 .rsqack = false, 1316 .attune = false, 1317 .cancel = false, 1318 .stcack = false, 1319 }; 1320 1321 si476x_core_lock(radio->core); 1322 if (radio->ops->rds_blckcnt) 1323 err = radio->ops->rsq_status(radio->core, &args, &report); 1324 else 1325 err = -ENOENT; 1326 si476x_core_unlock(radio->core); 1327 1328 if (err < 0) 1329 return err; 1330 1331 return simple_read_from_buffer(user_buf, count, ppos, &report, 1332 sizeof(report)); 1333} 1334 1335static const struct file_operations radio_rsq_primary_fops = { 1336 .open = simple_open, 1337 .llseek = default_llseek, 1338 .read = si476x_radio_read_rsq_primary_blob, 1339}; 1340 1341 1342static void si476x_radio_init_debugfs(struct si476x_radio *radio) 1343{ 1344 radio->debugfs = debugfs_create_dir(dev_name(radio->v4l2dev.dev), NULL); 1345 1346 debugfs_create_file("acf", S_IRUGO, radio->debugfs, radio, 1347 &radio_acf_fops); 1348 1349 debugfs_create_file("rds_blckcnt", S_IRUGO, radio->debugfs, radio, 1350 &radio_rds_blckcnt_fops); 1351 1352 debugfs_create_file("agc", S_IRUGO, radio->debugfs, radio, 1353 &radio_agc_fops); 1354 1355 debugfs_create_file("rsq", S_IRUGO, radio->debugfs, radio, 1356 &radio_rsq_fops); 1357 1358 debugfs_create_file("rsq_primary", S_IRUGO, radio->debugfs, radio, 1359 &radio_rsq_primary_fops); 1360} 1361 1362 1363static int si476x_radio_add_new_custom(struct si476x_radio *radio, 1364 enum si476x_ctrl_idx idx) 1365{ 1366 int rval; 1367 struct v4l2_ctrl *ctrl; 1368 1369 ctrl = v4l2_ctrl_new_custom(&radio->ctrl_handler, 1370 &si476x_ctrls[idx], 1371 NULL); 1372 rval = radio->ctrl_handler.error; 1373 if (ctrl == NULL && rval) 1374 dev_err(radio->v4l2dev.dev, 1375 "Could not initialize '%s' control %d\n", 1376 si476x_ctrls[idx].name, rval); 1377 1378 return rval; 1379} 1380 1381static int si476x_radio_probe(struct platform_device *pdev) 1382{ 1383 int rval; 1384 struct si476x_radio *radio; 1385 struct v4l2_ctrl *ctrl; 1386 1387 static atomic_t instance = ATOMIC_INIT(0); 1388 1389 radio = devm_kzalloc(&pdev->dev, sizeof(*radio), GFP_KERNEL); 1390 if (!radio) 1391 return -ENOMEM; 1392 1393 radio->core = i2c_mfd_cell_to_core(&pdev->dev); 1394 1395 v4l2_device_set_name(&radio->v4l2dev, DRIVER_NAME, &instance); 1396 1397 rval = v4l2_device_register(&pdev->dev, &radio->v4l2dev); 1398 if (rval) { 1399 dev_err(&pdev->dev, "Cannot register v4l2_device.\n"); 1400 return rval; 1401 } 1402 1403 memcpy(&radio->videodev, &si476x_viddev_template, 1404 sizeof(struct video_device)); 1405 1406 radio->videodev.v4l2_dev = &radio->v4l2dev; 1407 radio->videodev.ioctl_ops = &si4761_ioctl_ops; 1408 radio->videodev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | 1409 V4L2_CAP_HW_FREQ_SEEK; 1410 1411 si476x_core_lock(radio->core); 1412 if (!si476x_core_is_a_secondary_tuner(radio->core)) 1413 radio->videodev.device_caps |= V4L2_CAP_RDS_CAPTURE | 1414 V4L2_CAP_READWRITE; 1415 si476x_core_unlock(radio->core); 1416 1417 video_set_drvdata(&radio->videodev, radio); 1418 platform_set_drvdata(pdev, radio); 1419 1420 1421 radio->v4l2dev.ctrl_handler = &radio->ctrl_handler; 1422 v4l2_ctrl_handler_init(&radio->ctrl_handler, 1423 1 + ARRAY_SIZE(si476x_ctrls)); 1424 1425 if (si476x_core_has_am(radio->core)) { 1426 ctrl = v4l2_ctrl_new_std_menu(&radio->ctrl_handler, 1427 &si476x_ctrl_ops, 1428 V4L2_CID_POWER_LINE_FREQUENCY, 1429 V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1430 0, 0); 1431 rval = radio->ctrl_handler.error; 1432 if (ctrl == NULL && rval) { 1433 dev_err(&pdev->dev, "Could not initialize V4L2_CID_POWER_LINE_FREQUENCY control %d\n", 1434 rval); 1435 goto exit; 1436 } 1437 1438 rval = si476x_radio_add_new_custom(radio, 1439 SI476X_IDX_HARMONICS_COUNT); 1440 if (rval < 0) 1441 goto exit; 1442 } 1443 1444 rval = si476x_radio_add_new_custom(radio, SI476X_IDX_RSSI_THRESHOLD); 1445 if (rval < 0) 1446 goto exit; 1447 1448 rval = si476x_radio_add_new_custom(radio, SI476X_IDX_SNR_THRESHOLD); 1449 if (rval < 0) 1450 goto exit; 1451 1452 rval = si476x_radio_add_new_custom(radio, SI476X_IDX_MAX_TUNE_ERROR); 1453 if (rval < 0) 1454 goto exit; 1455 1456 ctrl = v4l2_ctrl_new_std_menu(&radio->ctrl_handler, 1457 &si476x_ctrl_ops, 1458 V4L2_CID_TUNE_DEEMPHASIS, 1459 V4L2_DEEMPHASIS_75_uS, 0, 0); 1460 rval = radio->ctrl_handler.error; 1461 if (ctrl == NULL && rval) { 1462 dev_err(&pdev->dev, "Could not initialize V4L2_CID_TUNE_DEEMPHASIS control %d\n", 1463 rval); 1464 goto exit; 1465 } 1466 1467 ctrl = v4l2_ctrl_new_std(&radio->ctrl_handler, &si476x_ctrl_ops, 1468 V4L2_CID_RDS_RECEPTION, 1469 0, 1, 1, 1); 1470 rval = radio->ctrl_handler.error; 1471 if (ctrl == NULL && rval) { 1472 dev_err(&pdev->dev, "Could not initialize V4L2_CID_RDS_RECEPTION control %d\n", 1473 rval); 1474 goto exit; 1475 } 1476 1477 if (si476x_core_has_diversity(radio->core)) { 1478 si476x_ctrls[SI476X_IDX_DIVERSITY_MODE].def = 1479 si476x_phase_diversity_mode_to_idx(radio->core->diversity_mode); 1480 rval = si476x_radio_add_new_custom(radio, SI476X_IDX_DIVERSITY_MODE); 1481 if (rval < 0) 1482 goto exit; 1483 1484 rval = si476x_radio_add_new_custom(radio, SI476X_IDX_INTERCHIP_LINK); 1485 if (rval < 0) 1486 goto exit; 1487 } 1488 1489 /* register video device */ 1490 rval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, -1); 1491 if (rval < 0) { 1492 dev_err(&pdev->dev, "Could not register video device\n"); 1493 goto exit; 1494 } 1495 1496 si476x_radio_init_debugfs(radio); 1497 1498 return 0; 1499exit: 1500 v4l2_ctrl_handler_free(radio->videodev.ctrl_handler); 1501 return rval; 1502} 1503 1504static int si476x_radio_remove(struct platform_device *pdev) 1505{ 1506 struct si476x_radio *radio = platform_get_drvdata(pdev); 1507 1508 v4l2_ctrl_handler_free(radio->videodev.ctrl_handler); 1509 video_unregister_device(&radio->videodev); 1510 v4l2_device_unregister(&radio->v4l2dev); 1511 debugfs_remove_recursive(radio->debugfs); 1512 1513 return 0; 1514} 1515 1516MODULE_ALIAS("platform:si476x-radio"); 1517 1518static struct platform_driver si476x_radio_driver = { 1519 .driver = { 1520 .name = DRIVER_NAME, 1521 }, 1522 .probe = si476x_radio_probe, 1523 .remove = si476x_radio_remove, 1524}; 1525module_platform_driver(si476x_radio_driver); 1526 1527MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>"); 1528MODULE_DESCRIPTION("Driver for Si4761/64/68 AM/FM Radio MFD Cell"); 1529MODULE_LICENSE("GPL");