si476x-core.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. /*
  2. * include/media/si476x-core.h -- Common definitions for si476x core
  3. * device
  4. *
  5. * Copyright (C) 2012 Innovative Converged Devices(ICD)
  6. * Copyright (C) 2013 Andrey Smirnov
  7. *
  8. * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; version 2 of the License.
  13. *
  14. * This program is distributed in the hope that it will be useful, but
  15. * WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * General Public License for more details.
  18. *
  19. */
  20. #ifndef SI476X_CORE_H
  21. #define SI476X_CORE_H
  22. #include <linux/kfifo.h>
  23. #include <linux/atomic.h>
  24. #include <linux/i2c.h>
  25. #include <linux/regmap.h>
  26. #include <linux/mutex.h>
  27. #include <linux/mfd/core.h>
  28. #include <linux/videodev2.h>
  29. #include <linux/regulator/consumer.h>
  30. #include <linux/mfd/si476x-platform.h>
  31. #include <linux/mfd/si476x-reports.h>
  32. /* Command Timeouts */
  33. #define SI476X_DEFAULT_TIMEOUT 100000
  34. #define SI476X_TIMEOUT_TUNE 700000
  35. #define SI476X_TIMEOUT_POWER_UP 330000
  36. #define SI476X_STATUS_POLL_US 0
  37. /* -------------------- si476x-i2c.c ----------------------- */
  38. enum si476x_freq_supported_chips {
  39. SI476X_CHIP_SI4761 = 1,
  40. SI476X_CHIP_SI4764,
  41. SI476X_CHIP_SI4768,
  42. };
  43. enum si476x_part_revisions {
  44. SI476X_REVISION_A10 = 0,
  45. SI476X_REVISION_A20 = 1,
  46. SI476X_REVISION_A30 = 2,
  47. };
  48. enum si476x_mfd_cells {
  49. SI476X_RADIO_CELL = 0,
  50. SI476X_CODEC_CELL,
  51. SI476X_MFD_CELLS,
  52. };
  53. /**
  54. * enum si476x_power_state - possible power state of the si476x
  55. * device.
  56. *
  57. * @SI476X_POWER_DOWN: In this state all regulators are turned off
  58. * and the reset line is pulled low. The device is completely
  59. * inactive.
  60. * @SI476X_POWER_UP_FULL: In this state all the power regualtors are
  61. * turned on, reset line pulled high, IRQ line is enabled(polling is
  62. * active for polling use scenario) and device is turned on with
  63. * POWER_UP command. The device is ready to be used.
  64. * @SI476X_POWER_INCONSISTENT: This state indicates that previous
  65. * power down was inconsistent, meaning some of the regulators were
  66. * not turned down and thus use of the device, without power-cycling
  67. * is impossible.
  68. */
  69. enum si476x_power_state {
  70. SI476X_POWER_DOWN = 0,
  71. SI476X_POWER_UP_FULL = 1,
  72. SI476X_POWER_INCONSISTENT = 2,
  73. };
  74. /**
  75. * struct si476x_core - internal data structure representing the
  76. * underlying "core" device which all the MFD cell-devices use.
  77. *
  78. * @client: Actual I2C client used to transfer commands to the chip.
  79. * @chip_id: Last digit of the chip model(E.g. "1" for SI4761)
  80. * @cells: MFD cell devices created by this driver.
  81. * @cmd_lock: Mutex used to serialize all the requests to the core
  82. * device. This filed should not be used directly. Instead
  83. * si476x_core_lock()/si476x_core_unlock() should be used to get
  84. * exclusive access to the "core" device.
  85. * @users: Active users counter(Used by the radio cell)
  86. * @rds_read_queue: Wait queue used to wait for RDS data.
  87. * @rds_fifo: FIFO in which all the RDS data received from the chip is
  88. * placed.
  89. * @rds_fifo_drainer: Worker that drains on-chip RDS FIFO.
  90. * @rds_drainer_is_working: Flag used for launching only one instance
  91. * of the @rds_fifo_drainer.
  92. * @rds_drainer_status_lock: Lock used to guard access to the
  93. * @rds_drainer_is_working variable.
  94. * @command: Wait queue for wainting on the command comapletion.
  95. * @cts: Clear To Send flag set upon receiving first status with CTS
  96. * set.
  97. * @tuning: Wait queue used for wainting for tune/seek comand
  98. * completion.
  99. * @stc: Similar to @cts, but for the STC bit of the status value.
  100. * @power_up_parameters: Parameters used as argument for POWER_UP
  101. * command when the device is started.
  102. * @state: Current power state of the device.
  103. * @supplues: Structure containing handles to all power supplies used
  104. * by the device (NULL ones are ignored).
  105. * @gpio_reset: GPIO pin connectet to the RSTB pin of the chip.
  106. * @pinmux: Chip's configurable pins configuration.
  107. * @diversity_mode: Chips role when functioning in diversity mode.
  108. * @status_monitor: Polling worker used in polling use case scenarion
  109. * (when IRQ is not avalible).
  110. * @revision: Chip's running firmware revision number(Used for correct
  111. * command set support).
  112. */
  113. struct si476x_core {
  114. struct i2c_client *client;
  115. struct regmap *regmap;
  116. int chip_id;
  117. struct mfd_cell cells[SI476X_MFD_CELLS];
  118. struct mutex cmd_lock; /* for serializing fm radio operations */
  119. atomic_t users;
  120. wait_queue_head_t rds_read_queue;
  121. struct kfifo rds_fifo;
  122. struct work_struct rds_fifo_drainer;
  123. bool rds_drainer_is_working;
  124. struct mutex rds_drainer_status_lock;
  125. wait_queue_head_t command;
  126. atomic_t cts;
  127. wait_queue_head_t tuning;
  128. atomic_t stc;
  129. struct si476x_power_up_args power_up_parameters;
  130. enum si476x_power_state power_state;
  131. struct regulator_bulk_data supplies[4];
  132. int gpio_reset;
  133. struct si476x_pinmux pinmux;
  134. enum si476x_phase_diversity_mode diversity_mode;
  135. atomic_t is_alive;
  136. struct delayed_work status_monitor;
  137. #define SI476X_WORK_TO_CORE(w) container_of(to_delayed_work(w), \
  138. struct si476x_core, \
  139. status_monitor)
  140. int revision;
  141. int rds_fifo_depth;
  142. };
  143. static inline struct si476x_core *i2c_mfd_cell_to_core(struct device *dev)
  144. {
  145. struct i2c_client *client = to_i2c_client(dev->parent);
  146. return i2c_get_clientdata(client);
  147. }
  148. /**
  149. * si476x_core_lock() - lock the core device to get an exclusive access
  150. * to it.
  151. */
  152. static inline void si476x_core_lock(struct si476x_core *core)
  153. {
  154. mutex_lock(&core->cmd_lock);
  155. }
  156. /**
  157. * si476x_core_unlock() - unlock the core device to relinquish an
  158. * exclusive access to it.
  159. */
  160. static inline void si476x_core_unlock(struct si476x_core *core)
  161. {
  162. mutex_unlock(&core->cmd_lock);
  163. }
  164. /* *_TUNE_FREQ family of commands accept frequency in multiples of
  165. 10kHz */
  166. static inline u16 hz_to_si476x(struct si476x_core *core, int freq)
  167. {
  168. u16 result;
  169. switch (core->power_up_parameters.func) {
  170. default:
  171. case SI476X_FUNC_FM_RECEIVER:
  172. result = freq / 10000;
  173. break;
  174. case SI476X_FUNC_AM_RECEIVER:
  175. result = freq / 1000;
  176. break;
  177. }
  178. return result;
  179. }
  180. static inline int si476x_to_hz(struct si476x_core *core, u16 freq)
  181. {
  182. int result;
  183. switch (core->power_up_parameters.func) {
  184. default:
  185. case SI476X_FUNC_FM_RECEIVER:
  186. result = freq * 10000;
  187. break;
  188. case SI476X_FUNC_AM_RECEIVER:
  189. result = freq * 1000;
  190. break;
  191. }
  192. return result;
  193. }
  194. /* Since the V4L2_TUNER_CAP_LOW flag is supplied, V4L2 subsystem
  195. * mesures frequency in 62.5 Hz units */
  196. static inline int hz_to_v4l2(int freq)
  197. {
  198. return (freq * 10) / 625;
  199. }
  200. static inline int v4l2_to_hz(int freq)
  201. {
  202. return (freq * 625) / 10;
  203. }
  204. static inline u16 v4l2_to_si476x(struct si476x_core *core, int freq)
  205. {
  206. return hz_to_si476x(core, v4l2_to_hz(freq));
  207. }
  208. static inline int si476x_to_v4l2(struct si476x_core *core, u16 freq)
  209. {
  210. return hz_to_v4l2(si476x_to_hz(core, freq));
  211. }
  212. /**
  213. * struct si476x_func_info - structure containing result of the
  214. * FUNC_INFO command.
  215. *
  216. * @firmware.major: Firmware major number.
  217. * @firmware.minor[...]: Firmware minor numbers.
  218. * @patch_id:
  219. * @func: Mode tuner is working in.
  220. */
  221. struct si476x_func_info {
  222. struct {
  223. u8 major, minor[2];
  224. } firmware;
  225. u16 patch_id;
  226. enum si476x_func func;
  227. };
  228. /**
  229. * struct si476x_power_down_args - structure used to pass parameters
  230. * to POWER_DOWN command
  231. *
  232. * @xosc: true - Power down, but leav oscillator running.
  233. * false - Full power down.
  234. */
  235. struct si476x_power_down_args {
  236. bool xosc;
  237. };
  238. /**
  239. * enum si476x_tunemode - enum representing possible tune modes for
  240. * the chip.
  241. * @SI476X_TM_VALIDATED_NORMAL_TUNE: Unconditionally stay on the new
  242. * channel after tune, tune status is valid.
  243. * @SI476X_TM_INVALIDATED_FAST_TUNE: Unconditionally stay in the new
  244. * channel after tune, tune status invalid.
  245. * @SI476X_TM_VALIDATED_AF_TUNE: Jump back to previous channel if
  246. * metric thresholds are not met.
  247. * @SI476X_TM_VALIDATED_AF_CHECK: Unconditionally jump back to the
  248. * previous channel.
  249. */
  250. enum si476x_tunemode {
  251. SI476X_TM_VALIDATED_NORMAL_TUNE = 0,
  252. SI476X_TM_INVALIDATED_FAST_TUNE = 1,
  253. SI476X_TM_VALIDATED_AF_TUNE = 2,
  254. SI476X_TM_VALIDATED_AF_CHECK = 3,
  255. };
  256. /**
  257. * enum si476x_smoothmetrics - enum containing the possible setting fo
  258. * audio transitioning of the chip
  259. * @SI476X_SM_INITIALIZE_AUDIO: Initialize audio state to match this
  260. * new channel
  261. * @SI476X_SM_TRANSITION_AUDIO: Transition audio state from previous
  262. * channel values to the new values
  263. */
  264. enum si476x_smoothmetrics {
  265. SI476X_SM_INITIALIZE_AUDIO = 0,
  266. SI476X_SM_TRANSITION_AUDIO = 1,
  267. };
  268. /**
  269. * struct si476x_rds_status_report - the structure representing the
  270. * response to 'FM_RD_STATUS' command
  271. * @rdstpptyint: Traffic program flag(TP) and/or program type(PTY)
  272. * code has changed.
  273. * @rdspiint: Program identification(PI) code has changed.
  274. * @rdssyncint: RDS synchronization has changed.
  275. * @rdsfifoint: RDS was received and the RDS FIFO has at least
  276. * 'FM_RDS_INTERRUPT_FIFO_COUNT' elements in it.
  277. * @tpptyvalid: TP flag and PTY code are valid falg.
  278. * @pivalid: PI code is valid flag.
  279. * @rdssync: RDS is currently synchronized.
  280. * @rdsfifolost: On or more RDS groups have been lost/discarded flag.
  281. * @tp: Current channel's TP flag.
  282. * @pty: Current channel's PTY code.
  283. * @pi: Current channel's PI code.
  284. * @rdsfifoused: Number of blocks remaining in the RDS FIFO (0 if
  285. * empty).
  286. */
  287. struct si476x_rds_status_report {
  288. bool rdstpptyint, rdspiint, rdssyncint, rdsfifoint;
  289. bool tpptyvalid, pivalid, rdssync, rdsfifolost;
  290. bool tp;
  291. u8 pty;
  292. u16 pi;
  293. u8 rdsfifoused;
  294. u8 ble[4];
  295. struct v4l2_rds_data rds[4];
  296. };
  297. struct si476x_rsq_status_args {
  298. bool primary;
  299. bool rsqack;
  300. bool attune;
  301. bool cancel;
  302. bool stcack;
  303. };
  304. enum si476x_injside {
  305. SI476X_INJSIDE_AUTO = 0,
  306. SI476X_INJSIDE_LOW = 1,
  307. SI476X_INJSIDE_HIGH = 2,
  308. };
  309. struct si476x_tune_freq_args {
  310. bool zifsr;
  311. bool hd;
  312. enum si476x_injside injside;
  313. int freq;
  314. enum si476x_tunemode tunemode;
  315. enum si476x_smoothmetrics smoothmetrics;
  316. int antcap;
  317. };
  318. int si476x_core_stop(struct si476x_core *, bool);
  319. int si476x_core_start(struct si476x_core *, bool);
  320. int si476x_core_set_power_state(struct si476x_core *, enum si476x_power_state);
  321. bool si476x_core_has_am(struct si476x_core *);
  322. bool si476x_core_has_diversity(struct si476x_core *);
  323. bool si476x_core_is_a_secondary_tuner(struct si476x_core *);
  324. bool si476x_core_is_a_primary_tuner(struct si476x_core *);
  325. bool si476x_core_is_in_am_receiver_mode(struct si476x_core *core);
  326. bool si476x_core_is_powered_up(struct si476x_core *core);
  327. enum si476x_i2c_type {
  328. SI476X_I2C_SEND,
  329. SI476X_I2C_RECV
  330. };
  331. int si476x_core_i2c_xfer(struct si476x_core *,
  332. enum si476x_i2c_type,
  333. char *, int);
  334. /* -------------------- si476x-cmd.c ----------------------- */
  335. int si476x_core_cmd_func_info(struct si476x_core *, struct si476x_func_info *);
  336. int si476x_core_cmd_set_property(struct si476x_core *, u16, u16);
  337. int si476x_core_cmd_get_property(struct si476x_core *, u16);
  338. int si476x_core_cmd_dig_audio_pin_cfg(struct si476x_core *,
  339. enum si476x_dclk_config,
  340. enum si476x_dfs_config,
  341. enum si476x_dout_config,
  342. enum si476x_xout_config);
  343. int si476x_core_cmd_zif_pin_cfg(struct si476x_core *,
  344. enum si476x_iqclk_config,
  345. enum si476x_iqfs_config,
  346. enum si476x_iout_config,
  347. enum si476x_qout_config);
  348. int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *,
  349. enum si476x_icin_config,
  350. enum si476x_icip_config,
  351. enum si476x_icon_config,
  352. enum si476x_icop_config);
  353. int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *,
  354. enum si476x_lrout_config);
  355. int si476x_core_cmd_intb_pin_cfg(struct si476x_core *, enum si476x_intb_config,
  356. enum si476x_a1_config);
  357. int si476x_core_cmd_fm_seek_start(struct si476x_core *, bool, bool);
  358. int si476x_core_cmd_am_seek_start(struct si476x_core *, bool, bool);
  359. int si476x_core_cmd_fm_rds_status(struct si476x_core *, bool, bool, bool,
  360. struct si476x_rds_status_report *);
  361. int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *, bool,
  362. struct si476x_rds_blockcount_report *);
  363. int si476x_core_cmd_fm_tune_freq(struct si476x_core *,
  364. struct si476x_tune_freq_args *);
  365. int si476x_core_cmd_am_tune_freq(struct si476x_core *,
  366. struct si476x_tune_freq_args *);
  367. int si476x_core_cmd_am_rsq_status(struct si476x_core *,
  368. struct si476x_rsq_status_args *,
  369. struct si476x_rsq_status_report *);
  370. int si476x_core_cmd_fm_rsq_status(struct si476x_core *,
  371. struct si476x_rsq_status_args *,
  372. struct si476x_rsq_status_report *);
  373. int si476x_core_cmd_power_up(struct si476x_core *,
  374. struct si476x_power_up_args *);
  375. int si476x_core_cmd_power_down(struct si476x_core *,
  376. struct si476x_power_down_args *);
  377. int si476x_core_cmd_fm_phase_div_status(struct si476x_core *);
  378. int si476x_core_cmd_fm_phase_diversity(struct si476x_core *,
  379. enum si476x_phase_diversity_mode);
  380. int si476x_core_cmd_fm_acf_status(struct si476x_core *,
  381. struct si476x_acf_status_report *);
  382. int si476x_core_cmd_am_acf_status(struct si476x_core *,
  383. struct si476x_acf_status_report *);
  384. int si476x_core_cmd_agc_status(struct si476x_core *,
  385. struct si476x_agc_status_report *);
  386. enum si476x_power_grid_type {
  387. SI476X_POWER_GRID_50HZ = 0,
  388. SI476X_POWER_GRID_60HZ,
  389. };
  390. /* Properties */
  391. enum si476x_interrupt_flags {
  392. SI476X_STCIEN = (1 << 0),
  393. SI476X_ACFIEN = (1 << 1),
  394. SI476X_RDSIEN = (1 << 2),
  395. SI476X_RSQIEN = (1 << 3),
  396. SI476X_ERRIEN = (1 << 6),
  397. SI476X_CTSIEN = (1 << 7),
  398. SI476X_STCREP = (1 << 8),
  399. SI476X_ACFREP = (1 << 9),
  400. SI476X_RDSREP = (1 << 10),
  401. SI476X_RSQREP = (1 << 11),
  402. };
  403. enum si476x_rdsint_sources {
  404. SI476X_RDSTPPTY = (1 << 4),
  405. SI476X_RDSPI = (1 << 3),
  406. SI476X_RDSSYNC = (1 << 1),
  407. SI476X_RDSRECV = (1 << 0),
  408. };
  409. enum si476x_status_response_bits {
  410. SI476X_CTS = (1 << 7),
  411. SI476X_ERR = (1 << 6),
  412. /* Status response for WB receiver */
  413. SI476X_WB_ASQ_INT = (1 << 4),
  414. SI476X_RSQ_INT = (1 << 3),
  415. /* Status response for FM receiver */
  416. SI476X_FM_RDS_INT = (1 << 2),
  417. SI476X_ACF_INT = (1 << 1),
  418. SI476X_STC_INT = (1 << 0),
  419. };
  420. /* -------------------- si476x-prop.c ----------------------- */
  421. enum si476x_common_receiver_properties {
  422. SI476X_PROP_INT_CTL_ENABLE = 0x0000,
  423. SI476X_PROP_DIGITAL_IO_INPUT_SAMPLE_RATE = 0x0200,
  424. SI476X_PROP_DIGITAL_IO_INPUT_FORMAT = 0x0201,
  425. SI476X_PROP_DIGITAL_IO_OUTPUT_SAMPLE_RATE = 0x0202,
  426. SI476X_PROP_DIGITAL_IO_OUTPUT_FORMAT = 0x0203,
  427. SI476X_PROP_SEEK_BAND_BOTTOM = 0x1100,
  428. SI476X_PROP_SEEK_BAND_TOP = 0x1101,
  429. SI476X_PROP_SEEK_FREQUENCY_SPACING = 0x1102,
  430. SI476X_PROP_VALID_MAX_TUNE_ERROR = 0x2000,
  431. SI476X_PROP_VALID_SNR_THRESHOLD = 0x2003,
  432. SI476X_PROP_VALID_RSSI_THRESHOLD = 0x2004,
  433. };
  434. enum si476x_am_receiver_properties {
  435. SI476X_PROP_AUDIO_PWR_LINE_FILTER = 0x0303,
  436. };
  437. enum si476x_fm_receiver_properties {
  438. SI476X_PROP_AUDIO_DEEMPHASIS = 0x0302,
  439. SI476X_PROP_FM_RDS_INTERRUPT_SOURCE = 0x4000,
  440. SI476X_PROP_FM_RDS_INTERRUPT_FIFO_COUNT = 0x4001,
  441. SI476X_PROP_FM_RDS_CONFIG = 0x4002,
  442. };
  443. enum si476x_prop_audio_pwr_line_filter_bits {
  444. SI476X_PROP_PWR_HARMONICS_MASK = 0x001f,
  445. SI476X_PROP_PWR_GRID_MASK = 0x0100,
  446. SI476X_PROP_PWR_ENABLE_MASK = 0x0200,
  447. SI476X_PROP_PWR_GRID_50HZ = 0x0000,
  448. SI476X_PROP_PWR_GRID_60HZ = 0x0100,
  449. };
  450. enum si476x_prop_fm_rds_config_bits {
  451. SI476X_PROP_RDSEN_MASK = 0x1,
  452. SI476X_PROP_RDSEN = 0x1,
  453. };
  454. struct regmap *devm_regmap_init_si476x(struct si476x_core *);
  455. #endif /* SI476X_CORE_H */