radio-si476x.c 39 KB

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