^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * drivers/media/radio/si470x/radio-si470x-common.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Driver for radios with Silicon Labs Si470x FM Radio Receivers
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * History:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * Version 1.0.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * - First working version
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) * 2008-01-13 Tobias Lorenz <tobias.lorenz@gmx.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * Version 1.0.1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) * - Improved error handling, every function now returns errno
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * - Improved multi user access (start/mute/stop)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * - Channel doesn't get lost anymore after start/mute/stop
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * - RDS support added (polling mode via interrupt EP 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * - marked default module parameters with *value*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * - switched from bit structs to bit masks
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) * - header file cleaned and integrated
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * 2008-01-14 Tobias Lorenz <tobias.lorenz@gmx.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) * Version 1.0.2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * - hex values are now lower case
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) * - commented USB ID for ADS/Tech moved on todo list
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * - blacklisted si470x in hid-quirks.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) * - rds buffer handling functions integrated into *_work, *_read
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) * - rds_command in si470x_poll exchanged against simple retval
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) * - check for firmware version 15
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) * - code order and prototypes still remain the same
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) * - spacing and bottom of band codes remain the same
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) * 2008-01-16 Tobias Lorenz <tobias.lorenz@gmx.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) * Version 1.0.3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) * - code reordered to avoid function prototypes
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) * - switch/case defaults are now more user-friendly
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) * - unified comment style
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) * - applied all checkpatch.pl v1.12 suggestions
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) * except the warning about the too long lines with bit comments
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) * 2008-01-22 Tobias Lorenz <tobias.lorenz@gmx.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) * Version 1.0.4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) * - avoid poss. locking when doing copy_to_user which may sleep
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) * - RDS is automatically activated on read now
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) * - code cleaned of unnecessary rds_commands
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) * - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) * (thanks to Guillaume RAMOUSSE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) * 2008-01-27 Tobias Lorenz <tobias.lorenz@gmx.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) * Version 1.0.5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) * - number of seek_retries changed to tune_timeout
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) * - fixed problem with incomplete tune operations by own buffers
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) * - optimization of variables and printf types
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) * - improved error logging
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) * 2008-01-31 Tobias Lorenz <tobias.lorenz@gmx.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) * Oliver Neukum <oliver@neukum.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) * Version 1.0.6
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) * - fixed coverity checker warnings in *_usb_driver_disconnect
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) * - probe()/open() race by correct ordering in probe()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) * - DMA coherency rules by separate allocation of all buffers
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) * - use of endianness macros
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) * - abuse of spinlock, replaced by mutex
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) * - racy handling of timer in disconnect,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) * replaced by delayed_work
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) * - racy interruptible_sleep_on(),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) * replaced with wait_event_interruptible()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) * - handle signals in read()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) * 2008-02-08 Tobias Lorenz <tobias.lorenz@gmx.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) * Oliver Neukum <oliver@neukum.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) * Version 1.0.7
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) * - usb autosuspend support
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) * - unplugging fixed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) * 2008-05-07 Tobias Lorenz <tobias.lorenz@gmx.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) * Version 1.0.8
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) * - hardware frequency seek support
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) * - afc indication
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) * - more safety checks, let si470x_get_freq return errno
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) * - vidioc behavior corrected according to v4l2 spec
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) * 2008-10-20 Alexey Klimov <klimov.linux@gmail.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) * - add support for KWorld USB FM Radio FM700
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) * - blacklisted KWorld radio in hid-core.c and hid-ids.h
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) * 2008-12-03 Mark Lord <mlord@pobox.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) * - add support for DealExtreme USB Radio
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) * 2009-01-31 Bob Ross <pigiron@gmx.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) * - correction of stereo detection/setting
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) * - correction of signal strength indicator scaling
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) * 2009-01-31 Rick Bronson <rick@efn.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) * Tobias Lorenz <tobias.lorenz@gmx.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) * - add LED status output
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) * - get HW/SW version from scratchpad
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) * 2009-06-16 Edouard Lafargue <edouard@lafargue.name>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) * Version 1.0.10
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) * - add support for interrupt mode for RDS endpoint,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) * instead of polling.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) * Improves RDS reception significantly
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) /* kernel includes */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) #include "radio-si470x.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) /**************************************************************************
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) * Module Parameters
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) **************************************************************************/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) /* Spacing (kHz) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) /* 0: 200 kHz (USA, Australia) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) /* 1: 100 kHz (Europe, Japan) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) /* 2: 50 kHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) static unsigned short space = 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) module_param(space, ushort, 0444);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) /* De-emphasis */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) /* 0: 75 us (USA) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) /* 1: 50 us (Europe, Australia, Japan) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) static unsigned short de = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) module_param(de, ushort, 0444);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) /* Tune timeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) static unsigned int tune_timeout = 3000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) module_param(tune_timeout, uint, 0644);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) /* Seek timeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) static unsigned int seek_timeout = 5000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) module_param(seek_timeout, uint, 0644);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) static const struct v4l2_frequency_band bands[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) .type = V4L2_TUNER_RADIO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) .index = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) V4L2_TUNER_CAP_FREQ_BANDS |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) V4L2_TUNER_CAP_HWSEEK_BOUNDED |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) V4L2_TUNER_CAP_HWSEEK_WRAP,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) .rangelow = 87500 * 16,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) .rangehigh = 108000 * 16,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) .modulation = V4L2_BAND_MODULATION_FM,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) .type = V4L2_TUNER_RADIO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) .index = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) V4L2_TUNER_CAP_FREQ_BANDS |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) V4L2_TUNER_CAP_HWSEEK_BOUNDED |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) V4L2_TUNER_CAP_HWSEEK_WRAP,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) .rangelow = 76000 * 16,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) .rangehigh = 108000 * 16,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) .modulation = V4L2_BAND_MODULATION_FM,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) .type = V4L2_TUNER_RADIO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) .index = 2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) V4L2_TUNER_CAP_FREQ_BANDS |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) V4L2_TUNER_CAP_HWSEEK_BOUNDED |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) V4L2_TUNER_CAP_HWSEEK_WRAP,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) .rangelow = 76000 * 16,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) .rangehigh = 90000 * 16,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) .modulation = V4L2_BAND_MODULATION_FM,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) /**************************************************************************
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) * Generic Functions
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) **************************************************************************/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) * si470x_set_band - set the band
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) static int si470x_set_band(struct si470x_device *radio, int band)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) if (radio->band == band)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) radio->band = band;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_BAND;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) radio->registers[SYSCONFIG2] |= radio->band << 6;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) return radio->set_register(radio, SYSCONFIG2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) * si470x_set_chan - set the channel
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) int retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) unsigned long time_left;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) bool timed_out = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) retval = radio->get_register(radio, POWERCFG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) if (retval)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) if ((radio->registers[POWERCFG] & (POWERCFG_ENABLE|POWERCFG_DMUTE))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) != (POWERCFG_ENABLE|POWERCFG_DMUTE)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) /* start tuning */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) radio->registers[CHANNEL] &= ~CHANNEL_CHAN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) retval = radio->set_register(radio, CHANNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) if (retval < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) goto done;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) /* wait till tune operation has completed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) reinit_completion(&radio->completion);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) time_left = wait_for_completion_timeout(&radio->completion,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) msecs_to_jiffies(tune_timeout));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) if (time_left == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) timed_out = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) dev_warn(&radio->videodev.dev, "tune does not complete\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) if (timed_out)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) dev_warn(&radio->videodev.dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) "tune timed out after %u ms\n", tune_timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) /* stop tuning */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) retval = radio->set_register(radio, CHANNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) done:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) * si470x_get_step - get channel spacing
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) static unsigned int si470x_get_step(struct si470x_device *radio)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) /* Spacing (kHz) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) /* 0: 200 kHz (USA, Australia) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) case 0:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) return 200 * 16;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) /* 1: 100 kHz (Europe, Japan) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) case 1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) return 100 * 16;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) /* 2: 50 kHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) return 50 * 16;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) * si470x_get_freq - get the frequency
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) int chan, retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) /* read channel */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) retval = radio->get_register(radio, READCHAN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) chan = radio->registers[READCHAN] & READCHAN_READCHAN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) *freq = chan * si470x_get_step(radio) + bands[radio->band].rangelow;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) * si470x_set_freq - set the frequency
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) unsigned short chan;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) freq = clamp(freq, bands[radio->band].rangelow,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) bands[radio->band].rangehigh);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) chan = (freq - bands[radio->band].rangelow) / si470x_get_step(radio);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) return si470x_set_chan(radio, chan);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) EXPORT_SYMBOL_GPL(si470x_set_freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) * si470x_set_seek - set seek
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) static int si470x_set_seek(struct si470x_device *radio,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) const struct v4l2_hw_freq_seek *seek)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) int band, retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) unsigned int freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) bool timed_out = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) unsigned long time_left;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) /* set band */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) if (seek->rangelow || seek->rangehigh) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) for (band = 0; band < ARRAY_SIZE(bands); band++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) if (bands[band].rangelow == seek->rangelow &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) bands[band].rangehigh == seek->rangehigh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) if (band == ARRAY_SIZE(bands))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) return -EINVAL; /* No matching band found */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) } else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) band = 1; /* If nothing is specified seek 76 - 108 Mhz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) if (radio->band != band) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) retval = si470x_get_freq(radio, &freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) if (retval)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) retval = si470x_set_band(radio, band);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) if (retval)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) retval = si470x_set_freq(radio, freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) if (retval)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) /* start seeking */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) radio->registers[POWERCFG] |= POWERCFG_SEEK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) if (seek->wrap_around)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) radio->registers[POWERCFG] |= POWERCFG_SKMODE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) if (seek->seek_upward)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) retval = radio->set_register(radio, POWERCFG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) if (retval < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) /* wait till tune operation has completed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) reinit_completion(&radio->completion);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) time_left = wait_for_completion_timeout(&radio->completion,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) msecs_to_jiffies(seek_timeout));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) if (time_left == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) timed_out = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) dev_warn(&radio->videodev.dev, "seek does not complete\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) if (radio->registers[STATUSRSSI] & STATUSRSSI_SF)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) dev_warn(&radio->videodev.dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) "seek failed / band limit reached\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) /* stop seeking */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) retval = radio->set_register(radio, POWERCFG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) /* try again, if timed out */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) if (retval == 0 && timed_out)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) return -ENODATA;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) * si470x_start - switch on radio
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) int si470x_start(struct si470x_device *radio)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) int retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) /* powercfg */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) radio->registers[POWERCFG] =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) retval = radio->set_register(radio, POWERCFG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) if (retval < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) goto done;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) /* sysconfig 1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDSIEN | SYSCONFIG1_STCIEN |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) SYSCONFIG1_RDS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_GPIO2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) radio->registers[SYSCONFIG1] |= SYSCONFIG1_GPIO2_INT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) if (de)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) radio->registers[SYSCONFIG1] |= SYSCONFIG1_DE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) retval = radio->set_register(radio, SYSCONFIG1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) if (retval < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) goto done;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) /* sysconfig 2 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) radio->registers[SYSCONFIG2] =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) (0x1f << 8) | /* SEEKTH */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) ((radio->band << 6) & SYSCONFIG2_BAND) |/* BAND */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) 15; /* VOLUME (max) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) retval = radio->set_register(radio, SYSCONFIG2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) if (retval < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) goto done;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) /* reset last channel */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) retval = si470x_set_chan(radio,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) radio->registers[CHANNEL] & CHANNEL_CHAN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) done:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) EXPORT_SYMBOL_GPL(si470x_start);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) * si470x_stop - switch off radio
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) int si470x_stop(struct si470x_device *radio)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) int retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) /* sysconfig 1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) retval = radio->set_register(radio, SYSCONFIG1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) if (retval < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) goto done;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) /* powercfg */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) /* POWERCFG_ENABLE has to automatically go low */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) retval = radio->set_register(radio, POWERCFG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) done:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) EXPORT_SYMBOL_GPL(si470x_stop);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) * si470x_rds_on - switch on rds reception
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) static int si470x_rds_on(struct si470x_device *radio)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) int retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) /* sysconfig 1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) retval = radio->set_register(radio, SYSCONFIG1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) if (retval < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) /**************************************************************************
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) * File Operations Interface
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) **************************************************************************/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) * si470x_fops_read - read RDS data
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) static ssize_t si470x_fops_read(struct file *file, char __user *buf,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) size_t count, loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) struct si470x_device *radio = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) int retval = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) unsigned int block_count = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) /* switch on rds reception */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) si470x_rds_on(radio);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) /* block if no new data available */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) while (radio->wr_index == radio->rd_index) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) if (file->f_flags & O_NONBLOCK) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) retval = -EWOULDBLOCK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) goto done;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) if (wait_event_interruptible(radio->read_queue,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) radio->wr_index != radio->rd_index) < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) retval = -EINTR;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) goto done;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) /* calculate block count from byte count */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) count /= 3;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) /* copy RDS block out of internal buffer and to user buffer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) while (block_count < count) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) if (radio->rd_index == radio->wr_index)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) /* always transfer rds complete blocks */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) /* retval = -EFAULT; */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) /* increment and wrap read pointer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) radio->rd_index += 3;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) if (radio->rd_index >= radio->buf_size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) radio->rd_index = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) /* increment counters */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) block_count++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) buf += 3;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) retval += 3;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) done:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) * si470x_fops_poll - poll RDS data
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) static __poll_t si470x_fops_poll(struct file *file,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) struct poll_table_struct *pts)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) struct si470x_device *radio = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) __poll_t req_events = poll_requested_events(pts);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520) __poll_t retval = v4l2_ctrl_poll(file, pts);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) if (req_events & (EPOLLIN | EPOLLRDNORM)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) /* switch on rds reception */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) si470x_rds_on(radio);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) poll_wait(file, &radio->read_queue, pts);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) if (radio->rd_index != radio->wr_index)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) retval |= EPOLLIN | EPOLLRDNORM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) static int si470x_fops_open(struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) struct si470x_device *radio = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) return radio->fops_open(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) * si470x_fops_release - file release
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) static int si470x_fops_release(struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) struct si470x_device *radio = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) return radio->fops_release(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) * si470x_fops - file operations interface
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) static const struct v4l2_file_operations si470x_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) .read = si470x_fops_read,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) .poll = si470x_fops_poll,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) .unlocked_ioctl = video_ioctl2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) .open = si470x_fops_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) .release = si470x_fops_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) /**************************************************************************
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) * Video4Linux Interface
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) **************************************************************************/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) static int si470x_s_ctrl(struct v4l2_ctrl *ctrl)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) struct si470x_device *radio =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) container_of(ctrl->handler, struct si470x_device, hdl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) switch (ctrl->id) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) case V4L2_CID_AUDIO_VOLUME:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) radio->registers[SYSCONFIG2] |= ctrl->val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) return radio->set_register(radio, SYSCONFIG2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) case V4L2_CID_AUDIO_MUTE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) if (ctrl->val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) radio->registers[POWERCFG] |= POWERCFG_DMUTE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) return radio->set_register(radio, POWERCFG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) * si470x_vidioc_g_tuner - get tuner attributes
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) static int si470x_vidioc_g_tuner(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) struct v4l2_tuner *tuner)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) struct si470x_device *radio = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) int retval = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) if (tuner->index != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609) if (!radio->status_rssi_auto_update) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) retval = radio->get_register(radio, STATUSRSSI);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) if (retval < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615) /* driver constants */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) strscpy(tuner->name, "FM", sizeof(tuner->name));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617) tuner->type = V4L2_TUNER_RADIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618) tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619) V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620) V4L2_TUNER_CAP_HWSEEK_BOUNDED |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) V4L2_TUNER_CAP_HWSEEK_WRAP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) tuner->rangelow = 76 * FREQ_MUL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) tuner->rangehigh = 108 * FREQ_MUL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) /* stereo indicator == stereo (instead of mono) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626) if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629) tuner->rxsubchans = V4L2_TUNER_SUB_STEREO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630) /* If there is a reliable method of detecting an RDS channel,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) then this code should check for that before setting this
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) RDS subchannel. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) tuner->rxsubchans |= V4L2_TUNER_SUB_RDS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) /* mono/stereo selector */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636) if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) tuner->audmode = V4L2_TUNER_MODE_STEREO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639) tuner->audmode = V4L2_TUNER_MODE_MONO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643) tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) /* the ideal factor is 0xffff/75 = 873,8 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646) if (tuner->signal > 0xffff)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) tuner->signal = 0xffff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) /* automatic frequency control: -1: freq to low, 1 freq to high */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) /* AFCRL does only indicate that freq. differs, not if too low/high */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658) * si470x_vidioc_s_tuner - set tuner attributes
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) static int si470x_vidioc_s_tuner(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661) const struct v4l2_tuner *tuner)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) struct si470x_device *radio = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665) if (tuner->index != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) /* mono/stereo selector */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669) switch (tuner->audmode) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) case V4L2_TUNER_MODE_MONO:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671) radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673) case V4L2_TUNER_MODE_STEREO:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675) radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 679) return radio->set_register(radio, POWERCFG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 680) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 681)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 682)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 683) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 684) * si470x_vidioc_g_frequency - get tuner or modulator radio frequency
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 685) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 686) static int si470x_vidioc_g_frequency(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 687) struct v4l2_frequency *freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 688) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 689) struct si470x_device *radio = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 690)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 691) if (freq->tuner != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 692) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 693)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 694) freq->type = V4L2_TUNER_RADIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 695) return si470x_get_freq(radio, &freq->frequency);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 696) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 697)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 698)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 699) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 700) * si470x_vidioc_s_frequency - set tuner or modulator radio frequency
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 701) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 702) static int si470x_vidioc_s_frequency(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 703) const struct v4l2_frequency *freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 704) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 705) struct si470x_device *radio = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 706) int retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 707)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 708) if (freq->tuner != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 709) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 710)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 711) if (freq->frequency < bands[radio->band].rangelow ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 712) freq->frequency > bands[radio->band].rangehigh) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 713) /* Switch to band 1 which covers everything we support */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 714) retval = si470x_set_band(radio, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 715) if (retval)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 716) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 717) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 718) return si470x_set_freq(radio, freq->frequency);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 719) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 720)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 721)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 722) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 723) * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 724) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 725) static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 726) const struct v4l2_hw_freq_seek *seek)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 727) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 728) struct si470x_device *radio = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 729)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 730) if (seek->tuner != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 731) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 732)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 733) if (file->f_flags & O_NONBLOCK)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 734) return -EWOULDBLOCK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 735)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 736) return si470x_set_seek(radio, seek);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 737) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 738)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 739) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 740) * si470x_vidioc_enum_freq_bands - enumerate supported bands
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 741) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 742) static int si470x_vidioc_enum_freq_bands(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 743) struct v4l2_frequency_band *band)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 744) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 745) if (band->tuner != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 746) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 747) if (band->index >= ARRAY_SIZE(bands))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 748) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 749) *band = bands[band->index];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 750) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 751) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 752)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 753) const struct v4l2_ctrl_ops si470x_ctrl_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 754) .s_ctrl = si470x_s_ctrl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 755) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 756) EXPORT_SYMBOL_GPL(si470x_ctrl_ops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 757)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 758) static int si470x_vidioc_querycap(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 759) struct v4l2_capability *capability)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 760) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 761) struct si470x_device *radio = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 762)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 763) return radio->vidioc_querycap(file, priv, capability);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 764) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 765)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 766) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 767) * si470x_ioctl_ops - video device ioctl operations
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 768) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 769) static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 770) .vidioc_querycap = si470x_vidioc_querycap,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 771) .vidioc_g_tuner = si470x_vidioc_g_tuner,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 772) .vidioc_s_tuner = si470x_vidioc_s_tuner,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 773) .vidioc_g_frequency = si470x_vidioc_g_frequency,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 774) .vidioc_s_frequency = si470x_vidioc_s_frequency,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 775) .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 776) .vidioc_enum_freq_bands = si470x_vidioc_enum_freq_bands,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 777) .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 778) .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 779) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 780)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 781)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 782) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 783) * si470x_viddev_template - video device interface
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 784) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 785) const struct video_device si470x_viddev_template = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 786) .fops = &si470x_fops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 787) .name = DRIVER_NAME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 788) .release = video_device_release_empty,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 789) .ioctl_ops = &si470x_ioctl_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 790) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 791) EXPORT_SYMBOL_GPL(si470x_viddev_template);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 792)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 793) MODULE_LICENSE("GPL");