Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^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");