si476x-cmd.c 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555
  1. /*
  2. * drivers/mfd/si476x-cmd.c -- Subroutines implementing command
  3. * protocol of si476x series of chips
  4. *
  5. * Copyright (C) 2012 Innovative Converged Devices(ICD)
  6. * Copyright (C) 2013 Andrey Smirnov
  7. *
  8. * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
  9. *
  10. * 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. #include <linux/module.h>
  21. #include <linux/completion.h>
  22. #include <linux/delay.h>
  23. #include <linux/atomic.h>
  24. #include <linux/i2c.h>
  25. #include <linux/device.h>
  26. #include <linux/gpio.h>
  27. #include <linux/videodev2.h>
  28. #include <linux/mfd/si476x-core.h>
  29. #include <asm/unaligned.h>
  30. #define msb(x) ((u8)((u16) x >> 8))
  31. #define lsb(x) ((u8)((u16) x & 0x00FF))
  32. #define CMD_POWER_UP 0x01
  33. #define CMD_POWER_UP_A10_NRESP 1
  34. #define CMD_POWER_UP_A10_NARGS 5
  35. #define CMD_POWER_UP_A20_NRESP 1
  36. #define CMD_POWER_UP_A20_NARGS 5
  37. #define POWER_UP_DELAY_MS 110
  38. #define CMD_POWER_DOWN 0x11
  39. #define CMD_POWER_DOWN_A10_NRESP 1
  40. #define CMD_POWER_DOWN_A20_NRESP 1
  41. #define CMD_POWER_DOWN_A20_NARGS 1
  42. #define CMD_FUNC_INFO 0x12
  43. #define CMD_FUNC_INFO_NRESP 7
  44. #define CMD_SET_PROPERTY 0x13
  45. #define CMD_SET_PROPERTY_NARGS 5
  46. #define CMD_SET_PROPERTY_NRESP 1
  47. #define CMD_GET_PROPERTY 0x14
  48. #define CMD_GET_PROPERTY_NARGS 3
  49. #define CMD_GET_PROPERTY_NRESP 4
  50. #define CMD_AGC_STATUS 0x17
  51. #define CMD_AGC_STATUS_NRESP_A10 2
  52. #define CMD_AGC_STATUS_NRESP_A20 6
  53. #define PIN_CFG_BYTE(x) (0x7F & (x))
  54. #define CMD_DIG_AUDIO_PIN_CFG 0x18
  55. #define CMD_DIG_AUDIO_PIN_CFG_NARGS 4
  56. #define CMD_DIG_AUDIO_PIN_CFG_NRESP 5
  57. #define CMD_ZIF_PIN_CFG 0x19
  58. #define CMD_ZIF_PIN_CFG_NARGS 4
  59. #define CMD_ZIF_PIN_CFG_NRESP 5
  60. #define CMD_IC_LINK_GPO_CTL_PIN_CFG 0x1A
  61. #define CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS 4
  62. #define CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP 5
  63. #define CMD_ANA_AUDIO_PIN_CFG 0x1B
  64. #define CMD_ANA_AUDIO_PIN_CFG_NARGS 1
  65. #define CMD_ANA_AUDIO_PIN_CFG_NRESP 2
  66. #define CMD_INTB_PIN_CFG 0x1C
  67. #define CMD_INTB_PIN_CFG_NARGS 2
  68. #define CMD_INTB_PIN_CFG_A10_NRESP 6
  69. #define CMD_INTB_PIN_CFG_A20_NRESP 3
  70. #define CMD_FM_TUNE_FREQ 0x30
  71. #define CMD_FM_TUNE_FREQ_A10_NARGS 5
  72. #define CMD_FM_TUNE_FREQ_A20_NARGS 3
  73. #define CMD_FM_TUNE_FREQ_NRESP 1
  74. #define CMD_FM_RSQ_STATUS 0x32
  75. #define CMD_FM_RSQ_STATUS_A10_NARGS 1
  76. #define CMD_FM_RSQ_STATUS_A10_NRESP 17
  77. #define CMD_FM_RSQ_STATUS_A30_NARGS 1
  78. #define CMD_FM_RSQ_STATUS_A30_NRESP 23
  79. #define CMD_FM_SEEK_START 0x31
  80. #define CMD_FM_SEEK_START_NARGS 1
  81. #define CMD_FM_SEEK_START_NRESP 1
  82. #define CMD_FM_RDS_STATUS 0x36
  83. #define CMD_FM_RDS_STATUS_NARGS 1
  84. #define CMD_FM_RDS_STATUS_NRESP 16
  85. #define CMD_FM_RDS_BLOCKCOUNT 0x37
  86. #define CMD_FM_RDS_BLOCKCOUNT_NARGS 1
  87. #define CMD_FM_RDS_BLOCKCOUNT_NRESP 8
  88. #define CMD_FM_PHASE_DIVERSITY 0x38
  89. #define CMD_FM_PHASE_DIVERSITY_NARGS 1
  90. #define CMD_FM_PHASE_DIVERSITY_NRESP 1
  91. #define CMD_FM_PHASE_DIV_STATUS 0x39
  92. #define CMD_FM_PHASE_DIV_STATUS_NRESP 2
  93. #define CMD_AM_TUNE_FREQ 0x40
  94. #define CMD_AM_TUNE_FREQ_NARGS 3
  95. #define CMD_AM_TUNE_FREQ_NRESP 1
  96. #define CMD_AM_RSQ_STATUS 0x42
  97. #define CMD_AM_RSQ_STATUS_NARGS 1
  98. #define CMD_AM_RSQ_STATUS_NRESP 13
  99. #define CMD_AM_SEEK_START 0x41
  100. #define CMD_AM_SEEK_START_NARGS 1
  101. #define CMD_AM_SEEK_START_NRESP 1
  102. #define CMD_AM_ACF_STATUS 0x45
  103. #define CMD_AM_ACF_STATUS_NRESP 6
  104. #define CMD_AM_ACF_STATUS_NARGS 1
  105. #define CMD_FM_ACF_STATUS 0x35
  106. #define CMD_FM_ACF_STATUS_NRESP 8
  107. #define CMD_FM_ACF_STATUS_NARGS 1
  108. #define CMD_MAX_ARGS_COUNT (10)
  109. enum si476x_acf_status_report_bits {
  110. SI476X_ACF_BLEND_INT = (1 << 4),
  111. SI476X_ACF_HIBLEND_INT = (1 << 3),
  112. SI476X_ACF_HICUT_INT = (1 << 2),
  113. SI476X_ACF_CHBW_INT = (1 << 1),
  114. SI476X_ACF_SOFTMUTE_INT = (1 << 0),
  115. SI476X_ACF_SMUTE = (1 << 0),
  116. SI476X_ACF_SMATTN = 0x1f,
  117. SI476X_ACF_PILOT = (1 << 7),
  118. SI476X_ACF_STBLEND = ~SI476X_ACF_PILOT,
  119. };
  120. enum si476x_agc_status_report_bits {
  121. SI476X_AGC_MXHI = (1 << 5),
  122. SI476X_AGC_MXLO = (1 << 4),
  123. SI476X_AGC_LNAHI = (1 << 3),
  124. SI476X_AGC_LNALO = (1 << 2),
  125. };
  126. enum si476x_errors {
  127. SI476X_ERR_BAD_COMMAND = 0x10,
  128. SI476X_ERR_BAD_ARG1 = 0x11,
  129. SI476X_ERR_BAD_ARG2 = 0x12,
  130. SI476X_ERR_BAD_ARG3 = 0x13,
  131. SI476X_ERR_BAD_ARG4 = 0x14,
  132. SI476X_ERR_BUSY = 0x18,
  133. SI476X_ERR_BAD_INTERNAL_MEMORY = 0x20,
  134. SI476X_ERR_BAD_PATCH = 0x30,
  135. SI476X_ERR_BAD_BOOT_MODE = 0x31,
  136. SI476X_ERR_BAD_PROPERTY = 0x40,
  137. };
  138. static int si476x_core_parse_and_nag_about_error(struct si476x_core *core)
  139. {
  140. int err;
  141. char *cause;
  142. u8 buffer[2];
  143. if (core->revision != SI476X_REVISION_A10) {
  144. err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV,
  145. buffer, sizeof(buffer));
  146. if (err == sizeof(buffer)) {
  147. switch (buffer[1]) {
  148. case SI476X_ERR_BAD_COMMAND:
  149. cause = "Bad command";
  150. err = -EINVAL;
  151. break;
  152. case SI476X_ERR_BAD_ARG1:
  153. cause = "Bad argument #1";
  154. err = -EINVAL;
  155. break;
  156. case SI476X_ERR_BAD_ARG2:
  157. cause = "Bad argument #2";
  158. err = -EINVAL;
  159. break;
  160. case SI476X_ERR_BAD_ARG3:
  161. cause = "Bad argument #3";
  162. err = -EINVAL;
  163. break;
  164. case SI476X_ERR_BAD_ARG4:
  165. cause = "Bad argument #4";
  166. err = -EINVAL;
  167. break;
  168. case SI476X_ERR_BUSY:
  169. cause = "Chip is busy";
  170. err = -EBUSY;
  171. break;
  172. case SI476X_ERR_BAD_INTERNAL_MEMORY:
  173. cause = "Bad internal memory";
  174. err = -EIO;
  175. break;
  176. case SI476X_ERR_BAD_PATCH:
  177. cause = "Bad patch";
  178. err = -EINVAL;
  179. break;
  180. case SI476X_ERR_BAD_BOOT_MODE:
  181. cause = "Bad boot mode";
  182. err = -EINVAL;
  183. break;
  184. case SI476X_ERR_BAD_PROPERTY:
  185. cause = "Bad property";
  186. err = -EINVAL;
  187. break;
  188. default:
  189. cause = "Unknown";
  190. err = -EIO;
  191. }
  192. dev_err(&core->client->dev,
  193. "[Chip error status]: %s\n", cause);
  194. } else {
  195. dev_err(&core->client->dev,
  196. "Failed to fetch error code\n");
  197. err = (err >= 0) ? -EIO : err;
  198. }
  199. } else {
  200. err = -EIO;
  201. }
  202. return err;
  203. }
  204. /**
  205. * si476x_core_send_command() - sends a command to si476x and waits its
  206. * response
  207. * @core: si476x_device structure for the device we are
  208. * communicating with
  209. * @command: command id
  210. * @args: command arguments we are sending
  211. * @argn: actual size of @args
  212. * @response: buffer to place the expected response from the device
  213. * @respn: actual size of @response
  214. * @usecs: amount of time to wait before reading the response (in
  215. * usecs)
  216. *
  217. * Function returns 0 on succsess and negative error code on
  218. * failure
  219. */
  220. static int si476x_core_send_command(struct si476x_core *core,
  221. const u8 command,
  222. const u8 args[],
  223. const int argn,
  224. u8 resp[],
  225. const int respn,
  226. const int usecs)
  227. {
  228. struct i2c_client *client = core->client;
  229. int err;
  230. u8 data[CMD_MAX_ARGS_COUNT + 1];
  231. if (argn > CMD_MAX_ARGS_COUNT) {
  232. err = -ENOMEM;
  233. goto exit;
  234. }
  235. if (!client->adapter) {
  236. err = -ENODEV;
  237. goto exit;
  238. }
  239. /* First send the command and its arguments */
  240. data[0] = command;
  241. memcpy(&data[1], args, argn);
  242. dev_dbg(&client->dev, "Command:\n %*ph\n", argn + 1, data);
  243. err = si476x_core_i2c_xfer(core, SI476X_I2C_SEND,
  244. (char *) data, argn + 1);
  245. if (err != argn + 1) {
  246. dev_err(&core->client->dev,
  247. "Error while sending command 0x%02x\n",
  248. command);
  249. err = (err >= 0) ? -EIO : err;
  250. goto exit;
  251. }
  252. /* Set CTS to zero only after the command is send to avoid
  253. * possible racing conditions when working in polling mode */
  254. atomic_set(&core->cts, 0);
  255. /* if (unlikely(command == CMD_POWER_DOWN) */
  256. if (!wait_event_timeout(core->command,
  257. atomic_read(&core->cts),
  258. usecs_to_jiffies(usecs) + 1))
  259. dev_warn(&core->client->dev,
  260. "(%s) [CMD 0x%02x] Answer timeout.\n",
  261. __func__, command);
  262. /*
  263. When working in polling mode, for some reason the tuner will
  264. report CTS bit as being set in the first status byte read,
  265. but all the consequtive ones will return zeros until the
  266. tuner is actually completed the POWER_UP command. To
  267. workaround that we wait for second CTS to be reported
  268. */
  269. if (unlikely(!core->client->irq && command == CMD_POWER_UP)) {
  270. if (!wait_event_timeout(core->command,
  271. atomic_read(&core->cts),
  272. usecs_to_jiffies(usecs) + 1))
  273. dev_warn(&core->client->dev,
  274. "(%s) Power up took too much time.\n",
  275. __func__);
  276. }
  277. /* Then get the response */
  278. err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, resp, respn);
  279. if (err != respn) {
  280. dev_err(&core->client->dev,
  281. "Error while reading response for command 0x%02x\n",
  282. command);
  283. err = (err >= 0) ? -EIO : err;
  284. goto exit;
  285. }
  286. dev_dbg(&client->dev, "Response:\n %*ph\n", respn, resp);
  287. err = 0;
  288. if (resp[0] & SI476X_ERR) {
  289. dev_err(&core->client->dev,
  290. "[CMD 0x%02x] Chip set error flag\n", command);
  291. err = si476x_core_parse_and_nag_about_error(core);
  292. goto exit;
  293. }
  294. if (!(resp[0] & SI476X_CTS))
  295. err = -EBUSY;
  296. exit:
  297. return err;
  298. }
  299. static int si476x_cmd_clear_stc(struct si476x_core *core)
  300. {
  301. int err;
  302. struct si476x_rsq_status_args args = {
  303. .primary = false,
  304. .rsqack = false,
  305. .attune = false,
  306. .cancel = false,
  307. .stcack = true,
  308. };
  309. switch (core->power_up_parameters.func) {
  310. case SI476X_FUNC_FM_RECEIVER:
  311. err = si476x_core_cmd_fm_rsq_status(core, &args, NULL);
  312. break;
  313. case SI476X_FUNC_AM_RECEIVER:
  314. err = si476x_core_cmd_am_rsq_status(core, &args, NULL);
  315. break;
  316. default:
  317. err = -EINVAL;
  318. }
  319. return err;
  320. }
  321. static int si476x_cmd_tune_seek_freq(struct si476x_core *core,
  322. uint8_t cmd,
  323. const uint8_t args[], size_t argn,
  324. uint8_t *resp, size_t respn)
  325. {
  326. int err;
  327. atomic_set(&core->stc, 0);
  328. err = si476x_core_send_command(core, cmd, args, argn, resp, respn,
  329. SI476X_TIMEOUT_TUNE);
  330. if (!err) {
  331. wait_event_killable(core->tuning,
  332. atomic_read(&core->stc));
  333. si476x_cmd_clear_stc(core);
  334. }
  335. return err;
  336. }
  337. /**
  338. * si476x_cmd_func_info() - send 'FUNC_INFO' command to the device
  339. * @core: device to send the command to
  340. * @info: struct si476x_func_info to fill all the information
  341. * returned by the command
  342. *
  343. * The command requests the firmware and patch version for currently
  344. * loaded firmware (dependent on the function of the device FM/AM/WB)
  345. *
  346. * Function returns 0 on succsess and negative error code on
  347. * failure
  348. */
  349. int si476x_core_cmd_func_info(struct si476x_core *core,
  350. struct si476x_func_info *info)
  351. {
  352. int err;
  353. u8 resp[CMD_FUNC_INFO_NRESP];
  354. err = si476x_core_send_command(core, CMD_FUNC_INFO,
  355. NULL, 0,
  356. resp, ARRAY_SIZE(resp),
  357. SI476X_DEFAULT_TIMEOUT);
  358. info->firmware.major = resp[1];
  359. info->firmware.minor[0] = resp[2];
  360. info->firmware.minor[1] = resp[3];
  361. info->patch_id = ((u16) resp[4] << 8) | resp[5];
  362. info->func = resp[6];
  363. return err;
  364. }
  365. EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info);
  366. /**
  367. * si476x_cmd_set_property() - send 'SET_PROPERTY' command to the device
  368. * @core: device to send the command to
  369. * @property: property address
  370. * @value: property value
  371. *
  372. * Function returns 0 on succsess and negative error code on
  373. * failure
  374. */
  375. int si476x_core_cmd_set_property(struct si476x_core *core,
  376. u16 property, u16 value)
  377. {
  378. u8 resp[CMD_SET_PROPERTY_NRESP];
  379. const u8 args[CMD_SET_PROPERTY_NARGS] = {
  380. 0x00,
  381. msb(property),
  382. lsb(property),
  383. msb(value),
  384. lsb(value),
  385. };
  386. return si476x_core_send_command(core, CMD_SET_PROPERTY,
  387. args, ARRAY_SIZE(args),
  388. resp, ARRAY_SIZE(resp),
  389. SI476X_DEFAULT_TIMEOUT);
  390. }
  391. EXPORT_SYMBOL_GPL(si476x_core_cmd_set_property);
  392. /**
  393. * si476x_cmd_get_property() - send 'GET_PROPERTY' command to the device
  394. * @core: device to send the command to
  395. * @property: property address
  396. *
  397. * Function return the value of property as u16 on success or a
  398. * negative error on failure
  399. */
  400. int si476x_core_cmd_get_property(struct si476x_core *core, u16 property)
  401. {
  402. int err;
  403. u8 resp[CMD_GET_PROPERTY_NRESP];
  404. const u8 args[CMD_GET_PROPERTY_NARGS] = {
  405. 0x00,
  406. msb(property),
  407. lsb(property),
  408. };
  409. err = si476x_core_send_command(core, CMD_GET_PROPERTY,
  410. args, ARRAY_SIZE(args),
  411. resp, ARRAY_SIZE(resp),
  412. SI476X_DEFAULT_TIMEOUT);
  413. if (err < 0)
  414. return err;
  415. else
  416. return get_unaligned_be16(resp + 2);
  417. }
  418. EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property);
  419. /**
  420. * si476x_cmd_dig_audio_pin_cfg() - send 'DIG_AUDIO_PIN_CFG' command to
  421. * the device
  422. * @core: device to send the command to
  423. * @dclk: DCLK pin function configuration:
  424. * #SI476X_DCLK_NOOP - do not modify the behaviour
  425. * #SI476X_DCLK_TRISTATE - put the pin in tristate condition,
  426. * enable 1MOhm pulldown
  427. * #SI476X_DCLK_DAUDIO - set the pin to be a part of digital
  428. * audio interface
  429. * @dfs: DFS pin function configuration:
  430. * #SI476X_DFS_NOOP - do not modify the behaviour
  431. * #SI476X_DFS_TRISTATE - put the pin in tristate condition,
  432. * enable 1MOhm pulldown
  433. * SI476X_DFS_DAUDIO - set the pin to be a part of digital
  434. * audio interface
  435. * @dout - DOUT pin function configuration:
  436. * SI476X_DOUT_NOOP - do not modify the behaviour
  437. * SI476X_DOUT_TRISTATE - put the pin in tristate condition,
  438. * enable 1MOhm pulldown
  439. * SI476X_DOUT_I2S_OUTPUT - set this pin to be digital out on I2S
  440. * port 1
  441. * SI476X_DOUT_I2S_INPUT - set this pin to be digital in on I2S
  442. * port 1
  443. * @xout - XOUT pin function configuration:
  444. * SI476X_XOUT_NOOP - do not modify the behaviour
  445. * SI476X_XOUT_TRISTATE - put the pin in tristate condition,
  446. * enable 1MOhm pulldown
  447. * SI476X_XOUT_I2S_INPUT - set this pin to be digital in on I2S
  448. * port 1
  449. * SI476X_XOUT_MODE_SELECT - set this pin to be the input that
  450. * selects the mode of the I2S audio
  451. * combiner (analog or HD)
  452. * [SI4761/63/65/67 Only]
  453. *
  454. * Function returns 0 on success and negative error code on failure
  455. */
  456. int si476x_core_cmd_dig_audio_pin_cfg(struct si476x_core *core,
  457. enum si476x_dclk_config dclk,
  458. enum si476x_dfs_config dfs,
  459. enum si476x_dout_config dout,
  460. enum si476x_xout_config xout)
  461. {
  462. u8 resp[CMD_DIG_AUDIO_PIN_CFG_NRESP];
  463. const u8 args[CMD_DIG_AUDIO_PIN_CFG_NARGS] = {
  464. PIN_CFG_BYTE(dclk),
  465. PIN_CFG_BYTE(dfs),
  466. PIN_CFG_BYTE(dout),
  467. PIN_CFG_BYTE(xout),
  468. };
  469. return si476x_core_send_command(core, CMD_DIG_AUDIO_PIN_CFG,
  470. args, ARRAY_SIZE(args),
  471. resp, ARRAY_SIZE(resp),
  472. SI476X_DEFAULT_TIMEOUT);
  473. }
  474. EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg);
  475. /**
  476. * si476x_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND'
  477. * @core - device to send the command to
  478. * @iqclk - IQCL pin function configuration:
  479. * SI476X_IQCLK_NOOP - do not modify the behaviour
  480. * SI476X_IQCLK_TRISTATE - put the pin in tristate condition,
  481. * enable 1MOhm pulldown
  482. * SI476X_IQCLK_IQ - set pin to be a part of I/Q interace
  483. * in master mode
  484. * @iqfs - IQFS pin function configuration:
  485. * SI476X_IQFS_NOOP - do not modify the behaviour
  486. * SI476X_IQFS_TRISTATE - put the pin in tristate condition,
  487. * enable 1MOhm pulldown
  488. * SI476X_IQFS_IQ - set pin to be a part of I/Q interace
  489. * in master mode
  490. * @iout - IOUT pin function configuration:
  491. * SI476X_IOUT_NOOP - do not modify the behaviour
  492. * SI476X_IOUT_TRISTATE - put the pin in tristate condition,
  493. * enable 1MOhm pulldown
  494. * SI476X_IOUT_OUTPUT - set pin to be I out
  495. * @qout - QOUT pin function configuration:
  496. * SI476X_QOUT_NOOP - do not modify the behaviour
  497. * SI476X_QOUT_TRISTATE - put the pin in tristate condition,
  498. * enable 1MOhm pulldown
  499. * SI476X_QOUT_OUTPUT - set pin to be Q out
  500. *
  501. * Function returns 0 on success and negative error code on failure
  502. */
  503. int si476x_core_cmd_zif_pin_cfg(struct si476x_core *core,
  504. enum si476x_iqclk_config iqclk,
  505. enum si476x_iqfs_config iqfs,
  506. enum si476x_iout_config iout,
  507. enum si476x_qout_config qout)
  508. {
  509. u8 resp[CMD_ZIF_PIN_CFG_NRESP];
  510. const u8 args[CMD_ZIF_PIN_CFG_NARGS] = {
  511. PIN_CFG_BYTE(iqclk),
  512. PIN_CFG_BYTE(iqfs),
  513. PIN_CFG_BYTE(iout),
  514. PIN_CFG_BYTE(qout),
  515. };
  516. return si476x_core_send_command(core, CMD_ZIF_PIN_CFG,
  517. args, ARRAY_SIZE(args),
  518. resp, ARRAY_SIZE(resp),
  519. SI476X_DEFAULT_TIMEOUT);
  520. }
  521. EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg);
  522. /**
  523. * si476x_cmd_ic_link_gpo_ctl_pin_cfg - send
  524. * 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device
  525. * @core - device to send the command to
  526. * @icin - ICIN pin function configuration:
  527. * SI476X_ICIN_NOOP - do not modify the behaviour
  528. * SI476X_ICIN_TRISTATE - put the pin in tristate condition,
  529. * enable 1MOhm pulldown
  530. * SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high
  531. * SI476X_ICIN_GPO1_LOW - set pin to be an output, drive it low
  532. * SI476X_ICIN_IC_LINK - set the pin to be a part of Inter-Chip link
  533. * @icip - ICIP pin function configuration:
  534. * SI476X_ICIP_NOOP - do not modify the behaviour
  535. * SI476X_ICIP_TRISTATE - put the pin in tristate condition,
  536. * enable 1MOhm pulldown
  537. * SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high
  538. * SI476X_ICIP_GPO1_LOW - set pin to be an output, drive it low
  539. * SI476X_ICIP_IC_LINK - set the pin to be a part of Inter-Chip link
  540. * @icon - ICON pin function configuration:
  541. * SI476X_ICON_NOOP - do not modify the behaviour
  542. * SI476X_ICON_TRISTATE - put the pin in tristate condition,
  543. * enable 1MOhm pulldown
  544. * SI476X_ICON_I2S - set the pin to be a part of audio
  545. * interface in slave mode (DCLK)
  546. * SI476X_ICON_IC_LINK - set the pin to be a part of Inter-Chip link
  547. * @icop - ICOP pin function configuration:
  548. * SI476X_ICOP_NOOP - do not modify the behaviour
  549. * SI476X_ICOP_TRISTATE - put the pin in tristate condition,
  550. * enable 1MOhm pulldown
  551. * SI476X_ICOP_I2S - set the pin to be a part of audio
  552. * interface in slave mode (DOUT)
  553. * [Si4761/63/65/67 Only]
  554. * SI476X_ICOP_IC_LINK - set the pin to be a part of Inter-Chip link
  555. *
  556. * Function returns 0 on success and negative error code on failure
  557. */
  558. int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *core,
  559. enum si476x_icin_config icin,
  560. enum si476x_icip_config icip,
  561. enum si476x_icon_config icon,
  562. enum si476x_icop_config icop)
  563. {
  564. u8 resp[CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP];
  565. const u8 args[CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS] = {
  566. PIN_CFG_BYTE(icin),
  567. PIN_CFG_BYTE(icip),
  568. PIN_CFG_BYTE(icon),
  569. PIN_CFG_BYTE(icop),
  570. };
  571. return si476x_core_send_command(core, CMD_IC_LINK_GPO_CTL_PIN_CFG,
  572. args, ARRAY_SIZE(args),
  573. resp, ARRAY_SIZE(resp),
  574. SI476X_DEFAULT_TIMEOUT);
  575. }
  576. EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg);
  577. /**
  578. * si476x_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the
  579. * device
  580. * @core - device to send the command to
  581. * @lrout - LROUT pin function configuration:
  582. * SI476X_LROUT_NOOP - do not modify the behaviour
  583. * SI476X_LROUT_TRISTATE - put the pin in tristate condition,
  584. * enable 1MOhm pulldown
  585. * SI476X_LROUT_AUDIO - set pin to be audio output
  586. * SI476X_LROUT_MPX - set pin to be MPX output
  587. *
  588. * Function returns 0 on success and negative error code on failure
  589. */
  590. int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *core,
  591. enum si476x_lrout_config lrout)
  592. {
  593. u8 resp[CMD_ANA_AUDIO_PIN_CFG_NRESP];
  594. const u8 args[CMD_ANA_AUDIO_PIN_CFG_NARGS] = {
  595. PIN_CFG_BYTE(lrout),
  596. };
  597. return si476x_core_send_command(core, CMD_ANA_AUDIO_PIN_CFG,
  598. args, ARRAY_SIZE(args),
  599. resp, ARRAY_SIZE(resp),
  600. SI476X_DEFAULT_TIMEOUT);
  601. }
  602. EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg);
  603. /**
  604. * si476x_cmd_intb_pin_cfg - send 'INTB_PIN_CFG' command to the device
  605. * @core - device to send the command to
  606. * @intb - INTB pin function configuration:
  607. * SI476X_INTB_NOOP - do not modify the behaviour
  608. * SI476X_INTB_TRISTATE - put the pin in tristate condition,
  609. * enable 1MOhm pulldown
  610. * SI476X_INTB_DAUDIO - set pin to be a part of digital
  611. * audio interface in slave mode
  612. * SI476X_INTB_IRQ - set pin to be an interrupt request line
  613. * @a1 - A1 pin function configuration:
  614. * SI476X_A1_NOOP - do not modify the behaviour
  615. * SI476X_A1_TRISTATE - put the pin in tristate condition,
  616. * enable 1MOhm pulldown
  617. * SI476X_A1_IRQ - set pin to be an interrupt request line
  618. *
  619. * Function returns 0 on success and negative error code on failure
  620. */
  621. static int si476x_core_cmd_intb_pin_cfg_a10(struct si476x_core *core,
  622. enum si476x_intb_config intb,
  623. enum si476x_a1_config a1)
  624. {
  625. u8 resp[CMD_INTB_PIN_CFG_A10_NRESP];
  626. const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
  627. PIN_CFG_BYTE(intb),
  628. PIN_CFG_BYTE(a1),
  629. };
  630. return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
  631. args, ARRAY_SIZE(args),
  632. resp, ARRAY_SIZE(resp),
  633. SI476X_DEFAULT_TIMEOUT);
  634. }
  635. static int si476x_core_cmd_intb_pin_cfg_a20(struct si476x_core *core,
  636. enum si476x_intb_config intb,
  637. enum si476x_a1_config a1)
  638. {
  639. u8 resp[CMD_INTB_PIN_CFG_A20_NRESP];
  640. const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
  641. PIN_CFG_BYTE(intb),
  642. PIN_CFG_BYTE(a1),
  643. };
  644. return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
  645. args, ARRAY_SIZE(args),
  646. resp, ARRAY_SIZE(resp),
  647. SI476X_DEFAULT_TIMEOUT);
  648. }
  649. /**
  650. * si476x_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the
  651. * device
  652. * @core - device to send the command to
  653. * @rsqack - if set command clears RSQINT, SNRINT, SNRLINT, RSSIHINT,
  654. * RSSSILINT, BLENDINT, MULTHINT and MULTLINT
  655. * @attune - when set the values in the status report are the values
  656. * that were calculated at tune
  657. * @cancel - abort ongoing seek/tune opertation
  658. * @stcack - clear the STCINT bin in status register
  659. * @report - all signal quality information retured by the command
  660. * (if NULL then the output of the command is ignored)
  661. *
  662. * Function returns 0 on success and negative error code on failure
  663. */
  664. int si476x_core_cmd_am_rsq_status(struct si476x_core *core,
  665. struct si476x_rsq_status_args *rsqargs,
  666. struct si476x_rsq_status_report *report)
  667. {
  668. int err;
  669. u8 resp[CMD_AM_RSQ_STATUS_NRESP];
  670. const u8 args[CMD_AM_RSQ_STATUS_NARGS] = {
  671. rsqargs->rsqack << 3 | rsqargs->attune << 2 |
  672. rsqargs->cancel << 1 | rsqargs->stcack,
  673. };
  674. err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS,
  675. args, ARRAY_SIZE(args),
  676. resp, ARRAY_SIZE(resp),
  677. SI476X_DEFAULT_TIMEOUT);
  678. /*
  679. * Besides getting received signal quality information this
  680. * command can be used to just acknowledge different interrupt
  681. * flags in those cases it is useless to copy and parse
  682. * received data so user can pass NULL, and thus avoid
  683. * unnecessary copying.
  684. */
  685. if (!report)
  686. return err;
  687. report->snrhint = 0x08 & resp[1];
  688. report->snrlint = 0x04 & resp[1];
  689. report->rssihint = 0x02 & resp[1];
  690. report->rssilint = 0x01 & resp[1];
  691. report->bltf = 0x80 & resp[2];
  692. report->snr_ready = 0x20 & resp[2];
  693. report->rssiready = 0x08 & resp[2];
  694. report->afcrl = 0x02 & resp[2];
  695. report->valid = 0x01 & resp[2];
  696. report->readfreq = get_unaligned_be16(resp + 3);
  697. report->freqoff = resp[5];
  698. report->rssi = resp[6];
  699. report->snr = resp[7];
  700. report->lassi = resp[9];
  701. report->hassi = resp[10];
  702. report->mult = resp[11];
  703. report->dev = resp[12];
  704. return err;
  705. }
  706. EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status);
  707. int si476x_core_cmd_fm_acf_status(struct si476x_core *core,
  708. struct si476x_acf_status_report *report)
  709. {
  710. int err;
  711. u8 resp[CMD_FM_ACF_STATUS_NRESP];
  712. const u8 args[CMD_FM_ACF_STATUS_NARGS] = {
  713. 0x0,
  714. };
  715. if (!report)
  716. return -EINVAL;
  717. err = si476x_core_send_command(core, CMD_FM_ACF_STATUS,
  718. args, ARRAY_SIZE(args),
  719. resp, ARRAY_SIZE(resp),
  720. SI476X_DEFAULT_TIMEOUT);
  721. if (err < 0)
  722. return err;
  723. report->blend_int = resp[1] & SI476X_ACF_BLEND_INT;
  724. report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT;
  725. report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT;
  726. report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT;
  727. report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT;
  728. report->smute = resp[2] & SI476X_ACF_SMUTE;
  729. report->smattn = resp[3] & SI476X_ACF_SMATTN;
  730. report->chbw = resp[4];
  731. report->hicut = resp[5];
  732. report->hiblend = resp[6];
  733. report->pilot = resp[7] & SI476X_ACF_PILOT;
  734. report->stblend = resp[7] & SI476X_ACF_STBLEND;
  735. return err;
  736. }
  737. EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status);
  738. int si476x_core_cmd_am_acf_status(struct si476x_core *core,
  739. struct si476x_acf_status_report *report)
  740. {
  741. int err;
  742. u8 resp[CMD_AM_ACF_STATUS_NRESP];
  743. const u8 args[CMD_AM_ACF_STATUS_NARGS] = {
  744. 0x0,
  745. };
  746. if (!report)
  747. return -EINVAL;
  748. err = si476x_core_send_command(core, CMD_AM_ACF_STATUS,
  749. args, ARRAY_SIZE(args),
  750. resp, ARRAY_SIZE(resp),
  751. SI476X_DEFAULT_TIMEOUT);
  752. if (err < 0)
  753. return err;
  754. report->blend_int = resp[1] & SI476X_ACF_BLEND_INT;
  755. report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT;
  756. report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT;
  757. report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT;
  758. report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT;
  759. report->smute = resp[2] & SI476X_ACF_SMUTE;
  760. report->smattn = resp[3] & SI476X_ACF_SMATTN;
  761. report->chbw = resp[4];
  762. report->hicut = resp[5];
  763. return err;
  764. }
  765. EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status);
  766. /**
  767. * si476x_cmd_fm_seek_start - send 'FM_SEEK_START' command to the
  768. * device
  769. * @core - device to send the command to
  770. * @seekup - if set the direction of the search is 'up'
  771. * @wrap - if set seek wraps when hitting band limit
  772. *
  773. * This function begins search for a valid station. The station is
  774. * considered valid when 'FM_VALID_SNR_THRESHOLD' and
  775. * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
  776. * are met.
  777. } *
  778. * Function returns 0 on success and negative error code on failure
  779. */
  780. int si476x_core_cmd_fm_seek_start(struct si476x_core *core,
  781. bool seekup, bool wrap)
  782. {
  783. u8 resp[CMD_FM_SEEK_START_NRESP];
  784. const u8 args[CMD_FM_SEEK_START_NARGS] = {
  785. seekup << 3 | wrap << 2,
  786. };
  787. return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START,
  788. args, sizeof(args),
  789. resp, sizeof(resp));
  790. }
  791. EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start);
  792. /**
  793. * si476x_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the
  794. * device
  795. * @core - device to send the command to
  796. * @status_only - if set the data is not removed from RDSFIFO,
  797. * RDSFIFOUSED is not decremented and data in all the
  798. * rest RDS data contains the last valid info received
  799. * @mtfifo if set the command clears RDS receive FIFO
  800. * @intack if set the command clards the RDSINT bit.
  801. *
  802. * Function returns 0 on success and negative error code on failure
  803. */
  804. int si476x_core_cmd_fm_rds_status(struct si476x_core *core,
  805. bool status_only,
  806. bool mtfifo,
  807. bool intack,
  808. struct si476x_rds_status_report *report)
  809. {
  810. int err;
  811. u8 resp[CMD_FM_RDS_STATUS_NRESP];
  812. const u8 args[CMD_FM_RDS_STATUS_NARGS] = {
  813. status_only << 2 | mtfifo << 1 | intack,
  814. };
  815. err = si476x_core_send_command(core, CMD_FM_RDS_STATUS,
  816. args, ARRAY_SIZE(args),
  817. resp, ARRAY_SIZE(resp),
  818. SI476X_DEFAULT_TIMEOUT);
  819. /*
  820. * Besides getting RDS status information this command can be
  821. * used to just acknowledge different interrupt flags in those
  822. * cases it is useless to copy and parse received data so user
  823. * can pass NULL, and thus avoid unnecessary copying.
  824. */
  825. if (err < 0 || report == NULL)
  826. return err;
  827. report->rdstpptyint = 0x10 & resp[1];
  828. report->rdspiint = 0x08 & resp[1];
  829. report->rdssyncint = 0x02 & resp[1];
  830. report->rdsfifoint = 0x01 & resp[1];
  831. report->tpptyvalid = 0x10 & resp[2];
  832. report->pivalid = 0x08 & resp[2];
  833. report->rdssync = 0x02 & resp[2];
  834. report->rdsfifolost = 0x01 & resp[2];
  835. report->tp = 0x20 & resp[3];
  836. report->pty = 0x1f & resp[3];
  837. report->pi = get_unaligned_be16(resp + 4);
  838. report->rdsfifoused = resp[6];
  839. report->ble[V4L2_RDS_BLOCK_A] = 0xc0 & resp[7];
  840. report->ble[V4L2_RDS_BLOCK_B] = 0x30 & resp[7];
  841. report->ble[V4L2_RDS_BLOCK_C] = 0x0c & resp[7];
  842. report->ble[V4L2_RDS_BLOCK_D] = 0x03 & resp[7];
  843. report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A;
  844. report->rds[V4L2_RDS_BLOCK_A].msb = resp[8];
  845. report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9];
  846. report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B;
  847. report->rds[V4L2_RDS_BLOCK_B].msb = resp[10];
  848. report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11];
  849. report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C;
  850. report->rds[V4L2_RDS_BLOCK_C].msb = resp[12];
  851. report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13];
  852. report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D;
  853. report->rds[V4L2_RDS_BLOCK_D].msb = resp[14];
  854. report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15];
  855. return err;
  856. }
  857. EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status);
  858. int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core,
  859. bool clear,
  860. struct si476x_rds_blockcount_report *report)
  861. {
  862. int err;
  863. u8 resp[CMD_FM_RDS_BLOCKCOUNT_NRESP];
  864. const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = {
  865. clear,
  866. };
  867. if (!report)
  868. return -EINVAL;
  869. err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT,
  870. args, ARRAY_SIZE(args),
  871. resp, ARRAY_SIZE(resp),
  872. SI476X_DEFAULT_TIMEOUT);
  873. if (!err) {
  874. report->expected = get_unaligned_be16(resp + 2);
  875. report->received = get_unaligned_be16(resp + 4);
  876. report->uncorrectable = get_unaligned_be16(resp + 6);
  877. }
  878. return err;
  879. }
  880. EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount);
  881. int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core,
  882. enum si476x_phase_diversity_mode mode)
  883. {
  884. u8 resp[CMD_FM_PHASE_DIVERSITY_NRESP];
  885. const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = {
  886. mode & 0x07,
  887. };
  888. return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY,
  889. args, ARRAY_SIZE(args),
  890. resp, ARRAY_SIZE(resp),
  891. SI476X_DEFAULT_TIMEOUT);
  892. }
  893. EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity);
  894. /**
  895. * si476x_core_cmd_fm_phase_div_status() - get the phase diversity
  896. * status
  897. *
  898. * @core: si476x device
  899. *
  900. * NOTE caller must hold core lock
  901. *
  902. * Function returns the value of the status bit in case of success and
  903. * negative error code in case of failre.
  904. */
  905. int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core)
  906. {
  907. int err;
  908. u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP];
  909. err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS,
  910. NULL, 0,
  911. resp, ARRAY_SIZE(resp),
  912. SI476X_DEFAULT_TIMEOUT);
  913. return (err < 0) ? err : resp[1];
  914. }
  915. EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status);
  916. /**
  917. * si476x_cmd_am_seek_start - send 'FM_SEEK_START' command to the
  918. * device
  919. * @core - device to send the command to
  920. * @seekup - if set the direction of the search is 'up'
  921. * @wrap - if set seek wraps when hitting band limit
  922. *
  923. * This function begins search for a valid station. The station is
  924. * considered valid when 'FM_VALID_SNR_THRESHOLD' and
  925. * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
  926. * are met.
  927. *
  928. * Function returns 0 on success and negative error code on failure
  929. */
  930. int si476x_core_cmd_am_seek_start(struct si476x_core *core,
  931. bool seekup, bool wrap)
  932. {
  933. u8 resp[CMD_AM_SEEK_START_NRESP];
  934. const u8 args[CMD_AM_SEEK_START_NARGS] = {
  935. seekup << 3 | wrap << 2,
  936. };
  937. return si476x_cmd_tune_seek_freq(core, CMD_AM_SEEK_START,
  938. args, sizeof(args),
  939. resp, sizeof(resp));
  940. }
  941. EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start);
  942. static int si476x_core_cmd_power_up_a10(struct si476x_core *core,
  943. struct si476x_power_up_args *puargs)
  944. {
  945. u8 resp[CMD_POWER_UP_A10_NRESP];
  946. const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
  947. const bool ctsen = (core->client->irq != 0);
  948. const u8 args[CMD_POWER_UP_A10_NARGS] = {
  949. 0xF7, /* Reserved, always 0xF7 */
  950. 0x3F & puargs->xcload, /* First two bits are reserved to be
  951. * zeros */
  952. ctsen << 7 | intsel << 6 | 0x07, /* Last five bits
  953. * are reserved to
  954. * be written as 0x7 */
  955. puargs->func << 4 | puargs->freq,
  956. 0x11, /* Reserved, always 0x11 */
  957. };
  958. return si476x_core_send_command(core, CMD_POWER_UP,
  959. args, ARRAY_SIZE(args),
  960. resp, ARRAY_SIZE(resp),
  961. SI476X_TIMEOUT_POWER_UP);
  962. }
  963. static int si476x_core_cmd_power_up_a20(struct si476x_core *core,
  964. struct si476x_power_up_args *puargs)
  965. {
  966. u8 resp[CMD_POWER_UP_A20_NRESP];
  967. const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
  968. const bool ctsen = (core->client->irq != 0);
  969. const u8 args[CMD_POWER_UP_A20_NARGS] = {
  970. puargs->ibias6x << 7 | puargs->xstart,
  971. 0x3F & puargs->xcload, /* First two bits are reserved to be
  972. * zeros */
  973. ctsen << 7 | intsel << 6 | puargs->fastboot << 5 |
  974. puargs->xbiashc << 3 | puargs->xbias,
  975. puargs->func << 4 | puargs->freq,
  976. 0x10 | puargs->xmode,
  977. };
  978. return si476x_core_send_command(core, CMD_POWER_UP,
  979. args, ARRAY_SIZE(args),
  980. resp, ARRAY_SIZE(resp),
  981. SI476X_TIMEOUT_POWER_UP);
  982. }
  983. static int si476x_core_cmd_power_down_a10(struct si476x_core *core,
  984. struct si476x_power_down_args *pdargs)
  985. {
  986. u8 resp[CMD_POWER_DOWN_A10_NRESP];
  987. return si476x_core_send_command(core, CMD_POWER_DOWN,
  988. NULL, 0,
  989. resp, ARRAY_SIZE(resp),
  990. SI476X_DEFAULT_TIMEOUT);
  991. }
  992. static int si476x_core_cmd_power_down_a20(struct si476x_core *core,
  993. struct si476x_power_down_args *pdargs)
  994. {
  995. u8 resp[CMD_POWER_DOWN_A20_NRESP];
  996. const u8 args[CMD_POWER_DOWN_A20_NARGS] = {
  997. pdargs->xosc,
  998. };
  999. return si476x_core_send_command(core, CMD_POWER_DOWN,
  1000. args, ARRAY_SIZE(args),
  1001. resp, ARRAY_SIZE(resp),
  1002. SI476X_DEFAULT_TIMEOUT);
  1003. }
  1004. static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core,
  1005. struct si476x_tune_freq_args *tuneargs)
  1006. {
  1007. const int am_freq = tuneargs->freq;
  1008. u8 resp[CMD_AM_TUNE_FREQ_NRESP];
  1009. const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
  1010. (tuneargs->hd << 6),
  1011. msb(am_freq),
  1012. lsb(am_freq),
  1013. };
  1014. return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args,
  1015. sizeof(args),
  1016. resp, sizeof(resp));
  1017. }
  1018. static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core,
  1019. struct si476x_tune_freq_args *tuneargs)
  1020. {
  1021. const int am_freq = tuneargs->freq;
  1022. u8 resp[CMD_AM_TUNE_FREQ_NRESP];
  1023. const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
  1024. (tuneargs->zifsr << 6) | (tuneargs->injside & 0x03),
  1025. msb(am_freq),
  1026. lsb(am_freq),
  1027. };
  1028. return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ,
  1029. args, sizeof(args),
  1030. resp, sizeof(resp));
  1031. }
  1032. static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core,
  1033. struct si476x_rsq_status_args *rsqargs,
  1034. struct si476x_rsq_status_report *report)
  1035. {
  1036. int err;
  1037. u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP];
  1038. const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = {
  1039. rsqargs->rsqack << 3 | rsqargs->attune << 2 |
  1040. rsqargs->cancel << 1 | rsqargs->stcack,
  1041. };
  1042. err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
  1043. args, ARRAY_SIZE(args),
  1044. resp, ARRAY_SIZE(resp),
  1045. SI476X_DEFAULT_TIMEOUT);
  1046. /*
  1047. * Besides getting received signal quality information this
  1048. * command can be used to just acknowledge different interrupt
  1049. * flags in those cases it is useless to copy and parse
  1050. * received data so user can pass NULL, and thus avoid
  1051. * unnecessary copying.
  1052. */
  1053. if (err < 0 || report == NULL)
  1054. return err;
  1055. report->multhint = 0x80 & resp[1];
  1056. report->multlint = 0x40 & resp[1];
  1057. report->snrhint = 0x08 & resp[1];
  1058. report->snrlint = 0x04 & resp[1];
  1059. report->rssihint = 0x02 & resp[1];
  1060. report->rssilint = 0x01 & resp[1];
  1061. report->bltf = 0x80 & resp[2];
  1062. report->snr_ready = 0x20 & resp[2];
  1063. report->rssiready = 0x08 & resp[2];
  1064. report->afcrl = 0x02 & resp[2];
  1065. report->valid = 0x01 & resp[2];
  1066. report->readfreq = get_unaligned_be16(resp + 3);
  1067. report->freqoff = resp[5];
  1068. report->rssi = resp[6];
  1069. report->snr = resp[7];
  1070. report->lassi = resp[9];
  1071. report->hassi = resp[10];
  1072. report->mult = resp[11];
  1073. report->dev = resp[12];
  1074. report->readantcap = get_unaligned_be16(resp + 13);
  1075. report->assi = resp[15];
  1076. report->usn = resp[16];
  1077. return err;
  1078. }
  1079. static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core,
  1080. struct si476x_rsq_status_args *rsqargs,
  1081. struct si476x_rsq_status_report *report)
  1082. {
  1083. int err;
  1084. u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP];
  1085. const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
  1086. rsqargs->primary << 4 | rsqargs->rsqack << 3 |
  1087. rsqargs->attune << 2 | rsqargs->cancel << 1 |
  1088. rsqargs->stcack,
  1089. };
  1090. err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
  1091. args, ARRAY_SIZE(args),
  1092. resp, ARRAY_SIZE(resp),
  1093. SI476X_DEFAULT_TIMEOUT);
  1094. /*
  1095. * Besides getting received signal quality information this
  1096. * command can be used to just acknowledge different interrupt
  1097. * flags in those cases it is useless to copy and parse
  1098. * received data so user can pass NULL, and thus avoid
  1099. * unnecessary copying.
  1100. */
  1101. if (err < 0 || report == NULL)
  1102. return err;
  1103. report->multhint = 0x80 & resp[1];
  1104. report->multlint = 0x40 & resp[1];
  1105. report->snrhint = 0x08 & resp[1];
  1106. report->snrlint = 0x04 & resp[1];
  1107. report->rssihint = 0x02 & resp[1];
  1108. report->rssilint = 0x01 & resp[1];
  1109. report->bltf = 0x80 & resp[2];
  1110. report->snr_ready = 0x20 & resp[2];
  1111. report->rssiready = 0x08 & resp[2];
  1112. report->afcrl = 0x02 & resp[2];
  1113. report->valid = 0x01 & resp[2];
  1114. report->readfreq = get_unaligned_be16(resp + 3);
  1115. report->freqoff = resp[5];
  1116. report->rssi = resp[6];
  1117. report->snr = resp[7];
  1118. report->lassi = resp[9];
  1119. report->hassi = resp[10];
  1120. report->mult = resp[11];
  1121. report->dev = resp[12];
  1122. report->readantcap = get_unaligned_be16(resp + 13);
  1123. report->assi = resp[15];
  1124. report->usn = resp[16];
  1125. return err;
  1126. }
  1127. static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core,
  1128. struct si476x_rsq_status_args *rsqargs,
  1129. struct si476x_rsq_status_report *report)
  1130. {
  1131. int err;
  1132. u8 resp[CMD_FM_RSQ_STATUS_A30_NRESP];
  1133. const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
  1134. rsqargs->primary << 4 | rsqargs->rsqack << 3 |
  1135. rsqargs->attune << 2 | rsqargs->cancel << 1 |
  1136. rsqargs->stcack,
  1137. };
  1138. err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
  1139. args, ARRAY_SIZE(args),
  1140. resp, ARRAY_SIZE(resp),
  1141. SI476X_DEFAULT_TIMEOUT);
  1142. /*
  1143. * Besides getting received signal quality information this
  1144. * command can be used to just acknowledge different interrupt
  1145. * flags in those cases it is useless to copy and parse
  1146. * received data so user can pass NULL, and thus avoid
  1147. * unnecessary copying.
  1148. */
  1149. if (err < 0 || report == NULL)
  1150. return err;
  1151. report->multhint = 0x80 & resp[1];
  1152. report->multlint = 0x40 & resp[1];
  1153. report->snrhint = 0x08 & resp[1];
  1154. report->snrlint = 0x04 & resp[1];
  1155. report->rssihint = 0x02 & resp[1];
  1156. report->rssilint = 0x01 & resp[1];
  1157. report->bltf = 0x80 & resp[2];
  1158. report->snr_ready = 0x20 & resp[2];
  1159. report->rssiready = 0x08 & resp[2];
  1160. report->injside = 0x04 & resp[2];
  1161. report->afcrl = 0x02 & resp[2];
  1162. report->valid = 0x01 & resp[2];
  1163. report->readfreq = get_unaligned_be16(resp + 3);
  1164. report->freqoff = resp[5];
  1165. report->rssi = resp[6];
  1166. report->snr = resp[7];
  1167. report->issi = resp[8];
  1168. report->lassi = resp[9];
  1169. report->hassi = resp[10];
  1170. report->mult = resp[11];
  1171. report->dev = resp[12];
  1172. report->readantcap = get_unaligned_be16(resp + 13);
  1173. report->assi = resp[15];
  1174. report->usn = resp[16];
  1175. report->pilotdev = resp[17];
  1176. report->rdsdev = resp[18];
  1177. report->assidev = resp[19];
  1178. report->strongdev = resp[20];
  1179. report->rdspi = get_unaligned_be16(resp + 21);
  1180. return err;
  1181. }
  1182. static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core,
  1183. struct si476x_tune_freq_args *tuneargs)
  1184. {
  1185. u8 resp[CMD_FM_TUNE_FREQ_NRESP];
  1186. const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = {
  1187. (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
  1188. | (tuneargs->smoothmetrics << 2),
  1189. msb(tuneargs->freq),
  1190. lsb(tuneargs->freq),
  1191. msb(tuneargs->antcap),
  1192. lsb(tuneargs->antcap)
  1193. };
  1194. return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
  1195. args, sizeof(args),
  1196. resp, sizeof(resp));
  1197. }
  1198. static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core,
  1199. struct si476x_tune_freq_args *tuneargs)
  1200. {
  1201. u8 resp[CMD_FM_TUNE_FREQ_NRESP];
  1202. const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = {
  1203. (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
  1204. | (tuneargs->smoothmetrics << 2) | (tuneargs->injside),
  1205. msb(tuneargs->freq),
  1206. lsb(tuneargs->freq),
  1207. };
  1208. return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
  1209. args, sizeof(args),
  1210. resp, sizeof(resp));
  1211. }
  1212. static int si476x_core_cmd_agc_status_a20(struct si476x_core *core,
  1213. struct si476x_agc_status_report *report)
  1214. {
  1215. int err;
  1216. u8 resp[CMD_AGC_STATUS_NRESP_A20];
  1217. if (!report)
  1218. return -EINVAL;
  1219. err = si476x_core_send_command(core, CMD_AGC_STATUS,
  1220. NULL, 0,
  1221. resp, ARRAY_SIZE(resp),
  1222. SI476X_DEFAULT_TIMEOUT);
  1223. if (err < 0)
  1224. return err;
  1225. report->mxhi = resp[1] & SI476X_AGC_MXHI;
  1226. report->mxlo = resp[1] & SI476X_AGC_MXLO;
  1227. report->lnahi = resp[1] & SI476X_AGC_LNAHI;
  1228. report->lnalo = resp[1] & SI476X_AGC_LNALO;
  1229. report->fmagc1 = resp[2];
  1230. report->fmagc2 = resp[3];
  1231. report->pgagain = resp[4];
  1232. report->fmwblang = resp[5];
  1233. return err;
  1234. }
  1235. static int si476x_core_cmd_agc_status_a10(struct si476x_core *core,
  1236. struct si476x_agc_status_report *report)
  1237. {
  1238. int err;
  1239. u8 resp[CMD_AGC_STATUS_NRESP_A10];
  1240. if (!report)
  1241. return -EINVAL;
  1242. err = si476x_core_send_command(core, CMD_AGC_STATUS,
  1243. NULL, 0,
  1244. resp, ARRAY_SIZE(resp),
  1245. SI476X_DEFAULT_TIMEOUT);
  1246. if (err < 0)
  1247. return err;
  1248. report->mxhi = resp[1] & SI476X_AGC_MXHI;
  1249. report->mxlo = resp[1] & SI476X_AGC_MXLO;
  1250. report->lnahi = resp[1] & SI476X_AGC_LNAHI;
  1251. report->lnalo = resp[1] & SI476X_AGC_LNALO;
  1252. return err;
  1253. }
  1254. typedef int (*tune_freq_func_t) (struct si476x_core *core,
  1255. struct si476x_tune_freq_args *tuneargs);
  1256. static struct {
  1257. int (*power_up)(struct si476x_core *,
  1258. struct si476x_power_up_args *);
  1259. int (*power_down)(struct si476x_core *,
  1260. struct si476x_power_down_args *);
  1261. tune_freq_func_t fm_tune_freq;
  1262. tune_freq_func_t am_tune_freq;
  1263. int (*fm_rsq_status)(struct si476x_core *,
  1264. struct si476x_rsq_status_args *,
  1265. struct si476x_rsq_status_report *);
  1266. int (*agc_status)(struct si476x_core *,
  1267. struct si476x_agc_status_report *);
  1268. int (*intb_pin_cfg)(struct si476x_core *core,
  1269. enum si476x_intb_config intb,
  1270. enum si476x_a1_config a1);
  1271. } si476x_cmds_vtable[] = {
  1272. [SI476X_REVISION_A10] = {
  1273. .power_up = si476x_core_cmd_power_up_a10,
  1274. .power_down = si476x_core_cmd_power_down_a10,
  1275. .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a10,
  1276. .am_tune_freq = si476x_core_cmd_am_tune_freq_a10,
  1277. .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a10,
  1278. .agc_status = si476x_core_cmd_agc_status_a10,
  1279. .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a10,
  1280. },
  1281. [SI476X_REVISION_A20] = {
  1282. .power_up = si476x_core_cmd_power_up_a20,
  1283. .power_down = si476x_core_cmd_power_down_a20,
  1284. .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20,
  1285. .am_tune_freq = si476x_core_cmd_am_tune_freq_a20,
  1286. .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a20,
  1287. .agc_status = si476x_core_cmd_agc_status_a20,
  1288. .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20,
  1289. },
  1290. [SI476X_REVISION_A30] = {
  1291. .power_up = si476x_core_cmd_power_up_a20,
  1292. .power_down = si476x_core_cmd_power_down_a20,
  1293. .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20,
  1294. .am_tune_freq = si476x_core_cmd_am_tune_freq_a20,
  1295. .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a30,
  1296. .agc_status = si476x_core_cmd_agc_status_a20,
  1297. .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20,
  1298. },
  1299. };
  1300. int si476x_core_cmd_power_up(struct si476x_core *core,
  1301. struct si476x_power_up_args *args)
  1302. {
  1303. BUG_ON(core->revision > SI476X_REVISION_A30 ||
  1304. core->revision == -1);
  1305. return si476x_cmds_vtable[core->revision].power_up(core, args);
  1306. }
  1307. EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up);
  1308. int si476x_core_cmd_power_down(struct si476x_core *core,
  1309. struct si476x_power_down_args *args)
  1310. {
  1311. BUG_ON(core->revision > SI476X_REVISION_A30 ||
  1312. core->revision == -1);
  1313. return si476x_cmds_vtable[core->revision].power_down(core, args);
  1314. }
  1315. EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down);
  1316. int si476x_core_cmd_fm_tune_freq(struct si476x_core *core,
  1317. struct si476x_tune_freq_args *args)
  1318. {
  1319. BUG_ON(core->revision > SI476X_REVISION_A30 ||
  1320. core->revision == -1);
  1321. return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args);
  1322. }
  1323. EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq);
  1324. int si476x_core_cmd_am_tune_freq(struct si476x_core *core,
  1325. struct si476x_tune_freq_args *args)
  1326. {
  1327. BUG_ON(core->revision > SI476X_REVISION_A30 ||
  1328. core->revision == -1);
  1329. return si476x_cmds_vtable[core->revision].am_tune_freq(core, args);
  1330. }
  1331. EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq);
  1332. int si476x_core_cmd_fm_rsq_status(struct si476x_core *core,
  1333. struct si476x_rsq_status_args *args,
  1334. struct si476x_rsq_status_report *report)
  1335. {
  1336. BUG_ON(core->revision > SI476X_REVISION_A30 ||
  1337. core->revision == -1);
  1338. return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args,
  1339. report);
  1340. }
  1341. EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status);
  1342. int si476x_core_cmd_agc_status(struct si476x_core *core,
  1343. struct si476x_agc_status_report *report)
  1344. {
  1345. BUG_ON(core->revision > SI476X_REVISION_A30 ||
  1346. core->revision == -1);
  1347. return si476x_cmds_vtable[core->revision].agc_status(core, report);
  1348. }
  1349. EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status);
  1350. int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core,
  1351. enum si476x_intb_config intb,
  1352. enum si476x_a1_config a1)
  1353. {
  1354. BUG_ON(core->revision > SI476X_REVISION_A30 ||
  1355. core->revision == -1);
  1356. return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1);
  1357. }
  1358. EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg);
  1359. MODULE_LICENSE("GPL");
  1360. MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
  1361. MODULE_DESCRIPTION("API for command exchange for si476x");