si476x-cmd.c 46 KB

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