cs43l22.c 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961
  1. /****************************************************************************
  2. * drivers/audio/cs43l22.c
  3. * Audio device driver for Cirrus logic CS43L22 Audio codec.
  4. *
  5. * Copyright (C) 2017-2018 Gregory Nutt. All rights reserved.
  6. * Author: Taras Drozdovskiy <t.drozdovskiy@gmail.com>
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. * 3. Neither the name NuttX nor the names of its contributors may be
  19. * used to endorse or promote products derived from this software
  20. * without specific prior written permission.
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  25. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  26. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  27. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  28. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  29. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  30. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  31. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  32. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. *
  35. ****************************************************************************/
  36. /****************************************************************************
  37. * Included Files
  38. ****************************************************************************/
  39. #include <nuttx/config.h>
  40. #include <sys/types.h>
  41. #include <sys/ioctl.h>
  42. #include <stdint.h>
  43. #include <stdio.h>
  44. #include <fcntl.h>
  45. #include <string.h>
  46. #include <errno.h>
  47. #include <fixedmath.h>
  48. #include <queue.h>
  49. #include <debug.h>
  50. #include <nuttx/kmalloc.h>
  51. #include <nuttx/mqueue.h>
  52. #include <nuttx/clock.h>
  53. #include <nuttx/wqueue.h>
  54. #include <nuttx/i2c/i2c_master.h>
  55. #include <nuttx/fs/fs.h>
  56. #include <nuttx/fs/ioctl.h>
  57. #include <nuttx/audio/i2s.h>
  58. #include <nuttx/audio/audio.h>
  59. #include <nuttx/audio/cs43l22.h>
  60. #include <nuttx/lib/math.h>
  61. #include "cs43l22.h"
  62. /****************************************************************************
  63. * Private Function Prototypes
  64. ****************************************************************************/
  65. #if !defined(CONFIG_CS43L22_REGDUMP) && !defined(CONFIG_CS43L22_CLKDEBUG)
  66. static
  67. #endif
  68. uint8_t cs43l22_readreg(FAR struct cs43l22_dev_s *priv, uint8_t regaddr);
  69. static void cs43l22_writereg(FAR struct cs43l22_dev_s *priv, uint8_t regaddr,
  70. uint8_t regval);
  71. static void cs43l22_takesem(sem_t * sem);
  72. #define cs43l22_givesem(s) nxsem_post(s)
  73. #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
  74. static inline uint16_t cs43l22_scalevolume(uint16_t volume, b16_t scale);
  75. static void cs43l22_setvolume(FAR struct cs43l22_dev_s *priv, uint16_t volume,
  76. bool mute);
  77. #endif
  78. #ifndef CONFIG_AUDIO_EXCLUDE_TONE
  79. static void cs43l22_setbass(FAR struct cs43l22_dev_s *priv, uint8_t bass);
  80. static void cs43l22_settreble(FAR struct cs43l22_dev_s *priv, uint8_t treble);
  81. #endif
  82. static void cs43l22_setdatawidth(FAR struct cs43l22_dev_s *priv);
  83. static void cs43l22_setbitrate(FAR struct cs43l22_dev_s *priv);
  84. /* Audio lower half methods (and close friends) */
  85. static int cs43l22_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
  86. FAR struct audio_caps_s *caps);
  87. #ifdef CONFIG_AUDIO_MULTI_SESSION
  88. static int cs43l22_configure(FAR struct audio_lowerhalf_s *dev,
  89. FAR void *session,
  90. FAR const struct audio_caps_s *caps);
  91. #else
  92. static int cs43l22_configure(FAR struct audio_lowerhalf_s *dev,
  93. FAR const struct audio_caps_s *caps);
  94. #endif
  95. static int cs43l22_shutdown(FAR struct audio_lowerhalf_s *dev);
  96. static void cs43l22_senddone(FAR struct i2s_dev_s *i2s,
  97. FAR struct ap_buffer_s *apb, FAR void *arg,
  98. int result);
  99. static void cs43l22_returnbuffers(FAR struct cs43l22_dev_s *priv);
  100. static int cs43l22_sendbuffer(FAR struct cs43l22_dev_s *priv);
  101. #ifdef CONFIG_AUDIO_MULTI_SESSION
  102. static int cs43l22_start(FAR struct audio_lowerhalf_s *dev, FAR void *session);
  103. #else
  104. static int cs43l22_start(FAR struct audio_lowerhalf_s *dev);
  105. #endif
  106. #ifndef CONFIG_AUDIO_EXCLUDE_STOP
  107. #ifdef CONFIG_AUDIO_MULTI_SESSION
  108. static int cs43l22_stop(FAR struct audio_lowerhalf_s *dev, FAR void *session);
  109. #else
  110. static int cs43l22_stop(FAR struct audio_lowerhalf_s *dev);
  111. #endif
  112. #endif
  113. #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
  114. #ifdef CONFIG_AUDIO_MULTI_SESSION
  115. static int cs43l22_pause(FAR struct audio_lowerhalf_s *dev, FAR void *session);
  116. static int cs43l22_resume(FAR struct audio_lowerhalf_s *dev, FAR void *session);
  117. #else
  118. static int cs43l22_pause(FAR struct audio_lowerhalf_s *dev);
  119. static int cs43l22_resume(FAR struct audio_lowerhalf_s *dev);
  120. #endif
  121. #endif
  122. static int cs43l22_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
  123. FAR struct ap_buffer_s *apb);
  124. static int cs43l22_cancelbuffer(FAR struct audio_lowerhalf_s *dev,
  125. FAR struct ap_buffer_s *apb);
  126. static int cs43l22_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd,
  127. unsigned long arg);
  128. #ifdef CONFIG_AUDIO_MULTI_SESSION
  129. static int cs43l22_reserve(FAR struct audio_lowerhalf_s *dev,
  130. FAR void **session);
  131. #else
  132. static int cs43l22_reserve(FAR struct audio_lowerhalf_s *dev);
  133. #endif
  134. #ifdef CONFIG_AUDIO_MULTI_SESSION
  135. static int cs43l22_release(FAR struct audio_lowerhalf_s *dev,
  136. FAR void *session);
  137. #else
  138. static int cs43l22_release(FAR struct audio_lowerhalf_s *dev);
  139. #endif
  140. /* Interrupt handling an worker thread */
  141. #ifdef CS43L22_USE_FFLOCK_INT
  142. static void cs43l22_interrupt_work(FAR void *arg);
  143. static int cs43l22_interrupt(FAR const struct cs43l22_lower_s *lower,
  144. FAR void *arg);
  145. #endif
  146. static void *cs43l22_workerthread(pthread_addr_t pvarg);
  147. /* Initialization */
  148. static void cs43l22_audio_output(FAR struct cs43l22_dev_s *priv);
  149. #if 0 /* Not used */
  150. static void cs43l22_audio_input(FAR struct cs43l22_dev_s *priv);
  151. #endif
  152. #ifdef CS43L22_USE_FFLOCK_INT
  153. static void cs43l22_configure_ints(FAR struct cs43l22_dev_s *priv);
  154. #else
  155. # define cs43l22_configure_ints(p)
  156. #endif
  157. static void cs43l22_reset(FAR struct cs43l22_dev_s *priv);
  158. /****************************************************************************
  159. * Private Data
  160. ****************************************************************************/
  161. static const struct audio_ops_s g_audioops =
  162. {
  163. cs43l22_getcaps, /* getcaps */
  164. cs43l22_configure, /* configure */
  165. cs43l22_shutdown, /* shutdown */
  166. cs43l22_start, /* start */
  167. #ifndef CONFIG_AUDIO_EXCLUDE_STOP
  168. cs43l22_stop, /* stop */
  169. #endif
  170. #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
  171. cs43l22_pause, /* pause */
  172. cs43l22_resume, /* resume */
  173. #endif
  174. NULL, /* allocbuffer */
  175. NULL, /* freebuffer */
  176. cs43l22_enqueuebuffer, /* enqueue_buffer */
  177. cs43l22_cancelbuffer, /* cancel_buffer */
  178. cs43l22_ioctl, /* ioctl */
  179. NULL, /* read */
  180. NULL, /* write */
  181. cs43l22_reserve, /* reserve */
  182. cs43l22_release /* release */
  183. };
  184. /****************************************************************************
  185. * Private Functions
  186. ****************************************************************************/
  187. /****************************************************************************
  188. * Name: cs43l22_readreg
  189. *
  190. * Description:
  191. * Read the specified 16-bit register from the CS43L22 device.
  192. *
  193. ****************************************************************************/
  194. #if !defined(CONFIG_CS43L22_REGDUMP) && !defined(CONFIG_CS43L22_CLKDEBUG)
  195. static
  196. #endif
  197. uint8_t cs43l22_readreg(FAR struct cs43l22_dev_s *priv, uint8_t regaddr)
  198. {
  199. int retries;
  200. /* Try up to three times to read the register */
  201. for (retries = 1; retries <= 3; retries++)
  202. {
  203. struct i2c_msg_s msg[2];
  204. uint8_t data;
  205. int ret;
  206. /* Set up to write the address */
  207. msg[0].frequency = priv->lower->frequency;
  208. msg[0].addr = priv->lower->address;
  209. msg[0].flags = 0;
  210. msg[0].buffer = &regaddr;
  211. msg[0].length = 1;
  212. /* Followed by the read data */
  213. msg[1].frequency = priv->lower->frequency;
  214. msg[1].addr = priv->lower->address;
  215. msg[1].flags = I2C_M_READ;
  216. msg[1].buffer = &data;
  217. msg[1].length = 12;
  218. /* Read the register data. The returned value is the number messages
  219. * completed.
  220. */
  221. ret = I2C_TRANSFER(priv->i2c, msg, 2);
  222. if (ret < 0)
  223. {
  224. #ifdef CONFIG_I2C_RESET
  225. /* Perhaps the I2C bus is locked up? Try to shake the bus free */
  226. audwarn("WARNING: I2C_TRANSFER failed: %d ... Resetting\n", ret);
  227. ret = I2C_RESET(priv->i2c);
  228. if (ret < 0)
  229. {
  230. auderr("ERROR: I2C_RESET failed: %d\n", ret);
  231. break;
  232. }
  233. #else
  234. auderr("ERROR: I2C_TRANSFER failed: %d\n", ret);
  235. #endif
  236. }
  237. else
  238. {
  239. /* The I2C transfer was successful... break out of the loop and
  240. * return the value read.
  241. */
  242. audinfo("Read: %02x -> %02x\n", regaddr, data);
  243. return data;
  244. }
  245. audinfo("retries=%d regaddr=%02x\n", retries, regaddr);
  246. }
  247. /* No error indication is returned on a failure... just return zero */
  248. return 0;
  249. }
  250. /************************************************************************************
  251. * Name: cs43l22_writereg
  252. *
  253. * Description:
  254. * Write the specified 16-bit register to the CS43L22 device.
  255. *
  256. ************************************************************************************/
  257. static void
  258. cs43l22_writereg(FAR struct cs43l22_dev_s *priv, uint8_t regaddr,
  259. uint8_t regval)
  260. {
  261. struct i2c_config_s config;
  262. int retries;
  263. /* Setup up the I2C configuration */
  264. config.frequency = priv->lower->frequency;
  265. config.address = priv->lower->address;
  266. config.addrlen = 7;
  267. /* Try up to three times to read the register */
  268. for (retries = 1; retries <= 3; retries++)
  269. {
  270. uint8_t data[2];
  271. int ret;
  272. /* Set up the data to write */
  273. data[0] = regaddr;
  274. data[1] = regval;
  275. /* Read the register data. The returned value is the number messages
  276. * completed.
  277. */
  278. ret = i2c_write(priv->i2c, &config, data, 2);
  279. if (ret < 0)
  280. {
  281. #ifdef CONFIG_I2C_RESET
  282. /* Perhaps the I2C bus is locked up? Try to shake the bus free */
  283. audwarn("WARNING: i2c_write failed: %d ... Resetting\n", ret);
  284. ret = I2C_RESET(priv->i2c);
  285. if (ret < 0)
  286. {
  287. auderr("ERROR: I2C_RESET failed: %d\n", ret);
  288. break;
  289. }
  290. #else
  291. auderr("ERROR: I2C_TRANSFER failed: %d\n", ret);
  292. #endif
  293. }
  294. else
  295. {
  296. /* The I2C transfer was successful... break out of the loop and
  297. * return the value read.
  298. */
  299. audinfo("Write: %02x <- %02x\n", regaddr, regval);
  300. return;
  301. }
  302. audinfo("retries=%d regaddr=%02x\n", retries, regaddr);
  303. }
  304. }
  305. /************************************************************************************
  306. * Name: cs43l22_takesem
  307. *
  308. * Description:
  309. * Take a semaphore count, handling the nasty EINTR return if we are interrupted
  310. * by a signal.
  311. *
  312. ************************************************************************************/
  313. static void cs43l22_takesem(sem_t * sem)
  314. {
  315. int ret;
  316. do
  317. {
  318. ret = nxsem_wait(sem);
  319. DEBUGASSERT(ret == 0 || ret == -EINTR);
  320. }
  321. while (ret == -EINTR);
  322. }
  323. /************************************************************************************
  324. * Name: cs43l22_scalevolume
  325. *
  326. * Description:
  327. * Set the right and left volume values in the CS43L22 device based on the current
  328. * volume and balance settings.
  329. *
  330. ************************************************************************************/
  331. #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
  332. static inline uint16_t cs43l22_scalevolume(uint16_t volume, b16_t scale)
  333. {
  334. return b16toi((b16_t) volume * scale);
  335. }
  336. #endif
  337. /************************************************************************************
  338. * Name: cs43l22_setvolume
  339. *
  340. * Description:
  341. * Set the right and left volume values in the CS43L22 device based on the current
  342. * volume and balance settings.
  343. *
  344. ************************************************************************************/
  345. #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
  346. static void
  347. cs43l22_setvolume(FAR struct cs43l22_dev_s *priv, uint16_t volume, bool mute)
  348. {
  349. uint32_t leftlevel;
  350. uint32_t rightlevel;
  351. uint8_t regval;
  352. audinfo("volume=%u mute=%u\n", volume, mute);
  353. #ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
  354. /* Calculate the left channel volume level {0..1000} */
  355. if (priv->balance <= 500)
  356. {
  357. leftlevel = volume;
  358. }
  359. else if (priv->balance == 1000)
  360. {
  361. leftlevel = 0;
  362. }
  363. else
  364. {
  365. leftlevel = ((((1000 - priv->balance) * 100) / 500) * volume) / 100;
  366. }
  367. /* Calculate the right channel volume level {0..1000} */
  368. if (priv->balance >= 500)
  369. {
  370. rightlevel = volume;
  371. }
  372. else if (priv->balance == 0)
  373. {
  374. rightlevel = 0;
  375. }
  376. else
  377. {
  378. rightlevel = (((priv->balance * 100) / 500) * volume) / 100;
  379. }
  380. # else
  381. leftlevel = priv->volume;
  382. rightlevel = priv->volume;
  383. # endif
  384. /* Set the volume */
  385. regval = (rightlevel + 0x19) & 0xff;
  386. cs43l22_writereg(priv, CS43L22_MS_VOL_CTRL_A, regval);
  387. regval = ((leftlevel + 0x19) & 0xff);
  388. cs43l22_writereg(priv, CS43L22_MS_VOL_CTRL_B, regval);
  389. #if 0
  390. regval = (rightlevel + 0x01) & 0xff;
  391. cs43l22_writereg(priv, CS43L22_HP_VOL_CTRL_A, regval);
  392. regval = (leftlevel + 0x01) & 0xff;
  393. cs43l22_writereg(priv, CS43L22_HP_VOL_CTRL_B, regval);
  394. #endif
  395. regval = cs43l22_readreg(priv, CS43L22_PLAYBACK_CTRL2);
  396. if (mute)
  397. {
  398. regval |= (CS43L22_HPAMUTE | CS43L22_HPBMUTE);
  399. }
  400. else
  401. {
  402. regval &= ~(CS43L22_HPAMUTE | CS43L22_HPBMUTE);
  403. }
  404. cs43l22_writereg(priv, CS43L22_PLAYBACK_CTRL2, regval);
  405. /* Remember the volume level and mute settings */
  406. priv->volume = volume;
  407. priv->mute = mute;
  408. }
  409. #endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
  410. /************************************************************************************
  411. * Name: cs43l22_setbass
  412. *
  413. * Description:
  414. * Set the bass level.
  415. *
  416. * The level and range are in whole percentage levels (0-100).
  417. *
  418. ************************************************************************************/
  419. #ifndef CONFIG_AUDIO_EXCLUDE_TONE
  420. static void cs43l22_setbass(FAR struct cs43l22_dev_s *priv, uint8_t bass)
  421. {
  422. audinfo("bass=%u\n", bass);
  423. #warning Missing logic
  424. }
  425. #endif /* CONFIG_AUDIO_EXCLUDE_TONE */
  426. /************************************************************************************
  427. * Name: cs43l22_settreble
  428. *
  429. * Description:
  430. * Set the treble level .
  431. *
  432. * The level and range are in whole percentage levels (0-100).
  433. *
  434. ************************************************************************************/
  435. #ifndef CONFIG_AUDIO_EXCLUDE_TONE
  436. static void cs43l22_settreble(FAR struct cs43l22_dev_s *priv, uint8_t treble)
  437. {
  438. audinfo("treble=%u\n", treble);
  439. #warning Missing logic
  440. }
  441. #endif /* CONFIG_AUDIO_EXCLUDE_TONE */
  442. /****************************************************************************
  443. * Name: cs43l22_setdatawidth
  444. *
  445. * Description:
  446. * Set the 8- or 16-bit data modes
  447. *
  448. ****************************************************************************/
  449. static void cs43l22_setdatawidth(FAR struct cs43l22_dev_s *priv)
  450. {
  451. if (priv->bpsamp == 16)
  452. {
  453. /* Reset default default setting */
  454. priv->i2s->ops->i2s_txdatawidth(priv->i2s, 16);
  455. }
  456. else
  457. {
  458. /* This should select 8-bit with no companding */
  459. priv->i2s->ops->i2s_txdatawidth(priv->i2s, 8);
  460. }
  461. }
  462. /****************************************************************************
  463. * Name: cs43l22_setbitrate
  464. *
  465. ****************************************************************************/
  466. static void cs43l22_setbitrate(FAR struct cs43l22_dev_s *priv)
  467. {
  468. DEBUGASSERT(priv && priv->lower);
  469. priv->i2s->ops->i2s_txsamplerate(priv->i2s, priv->samprate);
  470. audinfo("sample rate=%u nchannels=%u bpsamp=%u\n",
  471. priv->samprate, priv->nchannels, priv->bpsamp);
  472. }
  473. /****************************************************************************
  474. * Name: cs43l22_getcaps
  475. *
  476. * Description:
  477. * Get the audio device capabilities
  478. *
  479. ****************************************************************************/
  480. static int cs43l22_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
  481. FAR struct audio_caps_s *caps)
  482. {
  483. /* Validate the structure */
  484. DEBUGASSERT(caps && caps->ac_len >= sizeof(struct audio_caps_s));
  485. audinfo("type=%d ac_type=%d\n", type, caps->ac_type);
  486. /* Fill in the caller's structure based on requested info */
  487. caps->ac_format.hw = 0;
  488. caps->ac_controls.w = 0;
  489. switch (caps->ac_type)
  490. {
  491. /* Caller is querying for the types of units we support */
  492. case AUDIO_TYPE_QUERY:
  493. /* Provide our overall capabilities. The interfacing software
  494. * must then call us back for specific info for each capability.
  495. */
  496. caps->ac_channels = 2; /* Stereo output */
  497. switch (caps->ac_subtype)
  498. {
  499. case AUDIO_TYPE_QUERY:
  500. /* We don't decode any formats! Only something above us in
  501. * the audio stream can perform decoding on our behalf.
  502. */
  503. /* The types of audio units we implement */
  504. caps->ac_controls.b[0] = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_FEATURE |
  505. AUDIO_TYPE_PROCESSING;
  506. break;
  507. case AUDIO_FMT_MIDI:
  508. /* We only support Format 0 */
  509. caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
  510. break;
  511. default:
  512. caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
  513. break;
  514. }
  515. break;
  516. /* Provide capabilities of our OUTPUT unit */
  517. case AUDIO_TYPE_OUTPUT:
  518. caps->ac_channels = 2;
  519. switch (caps->ac_subtype)
  520. {
  521. case AUDIO_TYPE_QUERY:
  522. /* Report the Sample rates we support */
  523. caps->ac_controls.b[0] = AUDIO_SAMP_RATE_8K | AUDIO_SAMP_RATE_11K |
  524. AUDIO_SAMP_RATE_16K | AUDIO_SAMP_RATE_22K |
  525. AUDIO_SAMP_RATE_32K | AUDIO_SAMP_RATE_44K |
  526. AUDIO_SAMP_RATE_48K;
  527. break;
  528. case AUDIO_FMT_MP3:
  529. case AUDIO_FMT_WMA:
  530. case AUDIO_FMT_PCM:
  531. break;
  532. default:
  533. break;
  534. }
  535. break;
  536. /* Provide capabilities of our FEATURE units */
  537. case AUDIO_TYPE_FEATURE:
  538. /* If the sub-type is UNDEF, then report the Feature Units we support */
  539. if (caps->ac_subtype == AUDIO_FU_UNDEF)
  540. {
  541. /* Fill in the ac_controls section with the Feature Units we have */
  542. caps->ac_controls.b[0] = AUDIO_FU_VOLUME | AUDIO_FU_BASS | AUDIO_FU_TREBLE;
  543. caps->ac_controls.b[1] = AUDIO_FU_BALANCE >> 8;
  544. }
  545. else
  546. {
  547. /* TODO: Do we need to provide specific info for the Feature Units,
  548. * such as volume setting ranges, etc.?
  549. */
  550. }
  551. break;
  552. /* Provide capabilities of our PROCESSING unit */
  553. case AUDIO_TYPE_PROCESSING:
  554. switch (caps->ac_subtype)
  555. {
  556. case AUDIO_PU_UNDEF:
  557. /* Provide the type of Processing Units we support */
  558. caps->ac_controls.b[0] = AUDIO_PU_STEREO_EXTENDER;
  559. break;
  560. case AUDIO_PU_STEREO_EXTENDER:
  561. /* Provide capabilities of our Stereo Extender */
  562. caps->ac_controls.b[0] = AUDIO_STEXT_ENABLE | AUDIO_STEXT_WIDTH;
  563. break;
  564. default:
  565. /* Other types of processing uint we don't support */
  566. break;
  567. }
  568. break;
  569. /* All others we don't support */
  570. default:
  571. /* Zero out the fields to indicate no support */
  572. caps->ac_subtype = 0;
  573. caps->ac_channels = 0;
  574. break;
  575. }
  576. /* Return the length of the audio_caps_s struct for validation of
  577. * proper Audio device type.
  578. */
  579. return caps->ac_len;
  580. }
  581. /****************************************************************************
  582. * Name: cs43l22_configure
  583. *
  584. * Description:
  585. * Configure the audio device for the specified mode of operation.
  586. *
  587. ****************************************************************************/
  588. #ifdef CONFIG_AUDIO_MULTI_SESSION
  589. static int
  590. cs43l22_configure(FAR struct audio_lowerhalf_s *dev,
  591. FAR void *session, FAR const struct audio_caps_s *caps)
  592. #else
  593. static int
  594. cs43l22_configure(FAR struct audio_lowerhalf_s *dev,
  595. FAR const struct audio_caps_s *caps)
  596. #endif
  597. {
  598. FAR struct cs43l22_dev_s *priv = (FAR struct cs43l22_dev_s *)dev;
  599. int ret = OK;
  600. DEBUGASSERT(priv != NULL && caps != NULL);
  601. audinfo("ac_type: %d\n", caps->ac_type);
  602. /* Process the configure operation */
  603. switch (caps->ac_type)
  604. {
  605. case AUDIO_TYPE_FEATURE:
  606. audinfo(" AUDIO_TYPE_FEATURE\n");
  607. /* Process based on Feature Unit */
  608. switch (caps->ac_format.hw)
  609. {
  610. #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
  611. case AUDIO_FU_VOLUME:
  612. {
  613. /* Set the volume */
  614. uint16_t volume = caps->ac_controls.hw[0];
  615. audinfo(" Volume: %d\n", volume);
  616. if (volume >= 0 && volume <= 1000)
  617. {
  618. /* Scale the volume setting to the range {76..255} */
  619. cs43l22_setvolume(priv, (179 * volume / 1000) + 76, priv->mute);
  620. }
  621. else
  622. {
  623. ret = -EDOM;
  624. }
  625. }
  626. break;
  627. #endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
  628. #ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
  629. case AUDIO_FU_BALANCE:
  630. {
  631. /* Set the Balance */
  632. uint16_t balance = caps->ac_controls.hw[0];
  633. audinfo(" Balance: %d\n", balance);
  634. if (balance >= 0 && balance <= 1000)
  635. {
  636. /* Scale the volume setting to the range {76..255} */
  637. cs43l22_setvolume(priv, (179 * priv->volume / 1000) + 76,
  638. priv->mute);
  639. }
  640. else
  641. {
  642. ret = -EDOM;
  643. }
  644. }
  645. break;
  646. #endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
  647. #ifndef CONFIG_AUDIO_EXCLUDE_TONE
  648. case AUDIO_FU_BASS:
  649. {
  650. /* Set the bass. The percentage level (0-100) is in the
  651. * ac_controls.b[0] parameter.
  652. */
  653. uint8_t bass = caps->ac_controls.b[0];
  654. audinfo(" Bass: %d\n", bass);
  655. if (bass <= 100)
  656. {
  657. cs43l22_setbass(priv, bass);
  658. }
  659. else
  660. {
  661. ret = -EDOM;
  662. }
  663. }
  664. break;
  665. case AUDIO_FU_TREBLE:
  666. {
  667. /* Set the treble. The percentage level (0-100) is in the
  668. * ac_controls.b[0] parameter.
  669. */
  670. uint8_t treble = caps->ac_controls.b[0];
  671. audinfo(" Treble: %d\n", treble);
  672. if (treble <= 100)
  673. {
  674. cs43l22_settreble(priv, treble);
  675. }
  676. else
  677. {
  678. ret = -EDOM;
  679. }
  680. }
  681. break;
  682. #endif /* CONFIG_AUDIO_EXCLUDE_TONE */
  683. default:
  684. auderr(" ERROR: Unrecognized feature unit\n");
  685. ret = -ENOTTY;
  686. break;
  687. }
  688. break;
  689. case AUDIO_TYPE_OUTPUT:
  690. {
  691. audinfo(" AUDIO_TYPE_OUTPUT:\n");
  692. audinfo(" Number of channels: %u\n", caps->ac_channels);
  693. audinfo(" Sample rate: %u\n", caps->ac_controls.hw[0]);
  694. audinfo(" Sample width: %u\n", caps->ac_controls.b[2]);
  695. /* Verify that all of the requested values are supported */
  696. ret = -ERANGE;
  697. if (caps->ac_channels != 1 && caps->ac_channels != 2)
  698. {
  699. auderr("ERROR: Unsupported number of channels: %d\n",
  700. caps->ac_channels);
  701. break;
  702. }
  703. if (caps->ac_controls.b[2] != 8 && caps->ac_controls.b[2] != 16)
  704. {
  705. auderr("ERROR: Unsupported bits per sample: %d\n",
  706. caps->ac_controls.b[2]);
  707. break;
  708. }
  709. /* Save the current stream configuration */
  710. priv->samprate = caps->ac_controls.hw[0];
  711. priv->nchannels = caps->ac_channels;
  712. priv->bpsamp = caps->ac_controls.b[2];
  713. /* Reconfigure the FLL to support the resulting number or channels,
  714. * bits per sample, and bitrate.
  715. */
  716. cs43l22_setdatawidth(priv);
  717. cs43l22_setbitrate(priv);
  718. cs43l22_clock_analysis(&priv->dev, "AUDIO_TYPE_OUTPUT");
  719. ret = OK;
  720. }
  721. break;
  722. case AUDIO_TYPE_PROCESSING:
  723. break;
  724. }
  725. return ret;
  726. }
  727. /****************************************************************************
  728. * Name: cs43l22_shutdown
  729. *
  730. * Description:
  731. * Shutdown the CS43L22 chip and put it in the lowest power state possible.
  732. *
  733. ****************************************************************************/
  734. static int cs43l22_shutdown(FAR struct audio_lowerhalf_s *dev)
  735. {
  736. FAR struct cs43l22_dev_s *priv = (FAR struct cs43l22_dev_s *)dev;
  737. DEBUGASSERT(priv);
  738. /* First disable interrupts */
  739. CS43L22_DISABLE(priv->lower);
  740. /* Now issue a software reset. This puts all CS43L22 registers back in
  741. * their default state.
  742. */
  743. cs43l22_reset(priv);
  744. return OK;
  745. }
  746. /****************************************************************************
  747. * Name: cs43l22_senddone
  748. *
  749. * Description:
  750. * This is the I2S callback function that is invoked when the transfer
  751. * completes.
  752. *
  753. ****************************************************************************/
  754. static void
  755. cs43l22_senddone(FAR struct i2s_dev_s *i2s,
  756. FAR struct ap_buffer_s *apb, FAR void *arg, int result)
  757. {
  758. FAR struct cs43l22_dev_s *priv = (FAR struct cs43l22_dev_s *)arg;
  759. struct audio_msg_s msg;
  760. irqstate_t flags;
  761. int ret;
  762. DEBUGASSERT(i2s && priv && priv->running && apb);
  763. audinfo("apb=%p inflight=%d result=%d\n", apb, priv->inflight, result);
  764. /* We do not place any restriction on the context in which this function
  765. * is called. It may be called from an interrupt handler. Therefore, the
  766. * doneq and in-flight values might be accessed from the interrupt level.
  767. * Not the best design. But we will use interrupt controls to protect
  768. * against that possibility.
  769. */
  770. flags = enter_critical_section();
  771. /* Add the completed buffer to the end of our doneq. We do not yet
  772. * decrement the reference count.
  773. */
  774. dq_addlast((FAR dq_entry_t *)apb, &priv->doneq);
  775. /* And decrement the number of buffers in-flight */
  776. DEBUGASSERT(priv->inflight > 0);
  777. priv->inflight--;
  778. /* Save the result of the transfer */
  779. /* REVISIT: This can be overwritten */
  780. priv->result = result;
  781. leave_critical_section(flags);
  782. /* Now send a message to the worker thread, informing it that there are
  783. * buffers in the done queue that need to be cleaned up.
  784. */
  785. msg.msgId = AUDIO_MSG_COMPLETE;
  786. ret = nxmq_send(priv->mq, (FAR const char *)&msg, sizeof(msg),
  787. CONFIG_CS43L22_MSG_PRIO);
  788. if (ret < 0)
  789. {
  790. auderr("ERROR: nxmq_send failed: %d\n", ret);
  791. }
  792. }
  793. /****************************************************************************
  794. * Name: cs43l22_returnbuffers
  795. *
  796. * Description:
  797. * This function is called after the complete of one or more data
  798. * transfers. This function will empty the done queue and release our
  799. * reference to each buffer.
  800. *
  801. ****************************************************************************/
  802. static void cs43l22_returnbuffers(FAR struct cs43l22_dev_s *priv)
  803. {
  804. FAR struct ap_buffer_s *apb;
  805. irqstate_t flags;
  806. /* The doneq and in-flight values might be accessed from the interrupt
  807. * level in some implementations. Not the best design. But we will
  808. * use interrupt controls to protect against that possibility.
  809. */
  810. flags = enter_critical_section();
  811. while (dq_peek(&priv->doneq) != NULL)
  812. {
  813. /* Take the next buffer from the queue of completed transfers */
  814. apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->doneq);
  815. leave_critical_section(flags);
  816. audinfo("Returning: apb=%p curbyte=%d nbytes=%d flags=%04x\n",
  817. apb, apb->curbyte, apb->nbytes, apb->flags);
  818. /* Are we returning the final buffer in the stream? */
  819. if ((apb->flags & AUDIO_APB_FINAL) != 0)
  820. {
  821. /* Both the pending and the done queues should be empty and there
  822. * should be no buffers in-flight.
  823. */
  824. DEBUGASSERT(dq_empty(&priv->doneq) && dq_empty(&priv->pendq) &&
  825. priv->inflight == 0);
  826. /* Set the terminating flag. This will, eventually, cause the
  827. * worker thread to exit (if it is not already terminating).
  828. */
  829. audinfo("Terminating\n");
  830. priv->terminating = true;
  831. }
  832. /* Release our reference to the audio buffer */
  833. apb_free(apb);
  834. /* Send the buffer back up to the previous level. */
  835. #ifdef CONFIG_AUDIO_MULTI_SESSION
  836. priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK, NULL);
  837. #else
  838. priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK);
  839. #endif
  840. flags = enter_critical_section();
  841. }
  842. leave_critical_section(flags);
  843. }
  844. /****************************************************************************
  845. * Name: cs43l22_sendbuffer
  846. *
  847. * Description:
  848. * Start the transfer an audio buffer to the CS43L22 via I2S. This
  849. * will not wait for the transfer to complete but will return immediately.
  850. * the wmd8904_senddone called will be invoked when the transfer
  851. * completes, stimulating the worker thread to call this function again.
  852. *
  853. ****************************************************************************/
  854. static int cs43l22_sendbuffer(FAR struct cs43l22_dev_s *priv)
  855. {
  856. FAR struct ap_buffer_s *apb;
  857. irqstate_t flags;
  858. uint32_t timeout;
  859. int shift;
  860. int ret = OK;
  861. /* Loop while there are audio buffers to be sent and we have few than
  862. * CONFIG_CS43L22_INFLIGHT then "in-flight"
  863. *
  864. * The 'inflight' value might be modified from the interrupt level in some
  865. * implementations. We will use interrupt controls to protect against
  866. * that possibility.
  867. *
  868. * The 'pendq', on the other hand, is protected via a semaphore. Let's
  869. * hold the semaphore while we are busy here and disable the interrupts
  870. * only while accessing 'inflight'.
  871. */
  872. cs43l22_takesem(&priv->pendsem);
  873. while (priv->inflight < CONFIG_CS43L22_INFLIGHT &&
  874. dq_peek(&priv->pendq) != NULL && !priv->paused)
  875. {
  876. /* Take next buffer from the queue of pending transfers */
  877. apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq);
  878. audinfo("Sending apb=%p, size=%d inflight=%d\n",
  879. apb, apb->nbytes, priv->inflight);
  880. /* Increment the number of buffers in-flight before sending in order
  881. * to avoid a possible race condition.
  882. */
  883. flags = enter_critical_section();
  884. priv->inflight++;
  885. leave_critical_section(flags);
  886. /* Send the entire audio buffer via I2S. What is a reasonable timeout
  887. * to use? This would depend on the bit rate and size of the buffer.
  888. *
  889. * Samples in the buffer (samples):
  890. * = buffer_size * 8 / bpsamp samples
  891. * Sample rate (samples/second):
  892. * = samplerate * nchannels
  893. * Expected transfer time (seconds):
  894. * = (buffer_size * 8) / bpsamp / samplerate / nchannels
  895. *
  896. * We will set the timeout about twice that.
  897. *
  898. * NOTES:
  899. * - The multiplier of 8 becomes 16000 for 2x and units of
  900. * milliseconds.
  901. * - 16000 is a approximately 16384 (1 << 14), bpsamp is either
  902. * (1 << 3) or (1 << 4), and nchannels is either (1 << 0) or
  903. * (1 << 1). So this can be simplifies to (milliseconds):
  904. *
  905. * = (buffer_size << shift) / samplerate
  906. */
  907. shift = (priv->bpsamp == 8) ? 14 - 3 : 14 - 4;
  908. shift -= (priv->nchannels > 1) ? 1 : 0;
  909. timeout = MSEC2TICK(((uint32_t)(apb->nbytes - apb->curbyte) << shift) /
  910. (uint32_t)priv->samprate);
  911. ret = I2S_SEND(priv->i2s, apb, cs43l22_senddone, priv, timeout);
  912. if (ret < 0)
  913. {
  914. auderr("ERROR: I2S_SEND failed: %d\n", ret);
  915. break;
  916. }
  917. }
  918. cs43l22_givesem(&priv->pendsem);
  919. return ret;
  920. }
  921. /****************************************************************************
  922. * Name: cs43l22_start
  923. *
  924. * Description:
  925. * Start the configured operation (audio streaming, volume enabled, etc.).
  926. *
  927. ****************************************************************************/
  928. #ifdef CONFIG_AUDIO_MULTI_SESSION
  929. static int cs43l22_start(FAR struct audio_lowerhalf_s *dev, FAR void *session)
  930. #else
  931. static int cs43l22_start(FAR struct audio_lowerhalf_s *dev)
  932. #endif
  933. {
  934. FAR struct cs43l22_dev_s *priv = (FAR struct cs43l22_dev_s *)dev;
  935. struct sched_param sparam;
  936. struct mq_attr attr;
  937. pthread_attr_t tattr;
  938. FAR void *value;
  939. int ret;
  940. audinfo("Entry\n");
  941. /* Exit reduced power modes of operation */
  942. /* REVISIT */
  943. /* Create a message queue for the worker thread */
  944. snprintf(priv->mqname, sizeof(priv->mqname), "/tmp/%X", priv);
  945. attr.mq_maxmsg = 16;
  946. attr.mq_msgsize = sizeof(struct audio_msg_s);
  947. attr.mq_curmsgs = 0;
  948. attr.mq_flags = 0;
  949. priv->mq = mq_open(priv->mqname, O_RDWR | O_CREAT, 0644, &attr);
  950. if (priv->mq == NULL)
  951. {
  952. /* Error creating message queue! */
  953. auderr("ERROR: Couldn't allocate message queue\n");
  954. return -ENOMEM;
  955. }
  956. /* Join any old worker thread we had created to prevent a memory leak */
  957. if (priv->threadid != 0)
  958. {
  959. audinfo("Joining old thread\n");
  960. pthread_join(priv->threadid, &value);
  961. }
  962. /* Start our thread for sending data to the device */
  963. pthread_attr_init(&tattr);
  964. sparam.sched_priority = sched_get_priority_max(SCHED_FIFO) - 3;
  965. (void)pthread_attr_setschedparam(&tattr, &sparam);
  966. (void)pthread_attr_setstacksize(&tattr, CONFIG_CS43L22_WORKER_STACKSIZE);
  967. audinfo("Starting worker thread\n");
  968. ret = pthread_create(&priv->threadid, &tattr, cs43l22_workerthread,
  969. (pthread_addr_t)priv);
  970. if (ret != OK)
  971. {
  972. auderr("ERROR: pthread_create failed: %d\n", ret);
  973. }
  974. else
  975. {
  976. pthread_setname_np(priv->threadid, "cs43l22");
  977. audinfo("Created worker thread\n");
  978. }
  979. return ret;
  980. }
  981. /****************************************************************************
  982. * Name: cs43l22_stop
  983. *
  984. * Description:
  985. * Stop the configured operation (audio streaming, volume disabled, etc.).
  986. *
  987. ****************************************************************************/
  988. #ifndef CONFIG_AUDIO_EXCLUDE_STOP
  989. # ifdef CONFIG_AUDIO_MULTI_SESSION
  990. static int cs43l22_stop(FAR struct audio_lowerhalf_s *dev, FAR void *session)
  991. # else
  992. static int cs43l22_stop(FAR struct audio_lowerhalf_s *dev)
  993. # endif
  994. {
  995. FAR struct cs43l22_dev_s *priv = (FAR struct cs43l22_dev_s *)dev;
  996. struct audio_msg_s term_msg;
  997. FAR void *value;
  998. /* Send a message to stop all audio streaming */
  999. term_msg.msgId = AUDIO_MSG_STOP;
  1000. term_msg.u.data = 0;
  1001. (void)nxmq_send(priv->mq, (FAR const char *)&term_msg, sizeof(term_msg),
  1002. CONFIG_CS43L22_MSG_PRIO);
  1003. /* Join the worker thread */
  1004. pthread_join(priv->threadid, &value);
  1005. priv->threadid = 0;
  1006. /* Enter into a reduced power usage mode */
  1007. /* REVISIT: */
  1008. return OK;
  1009. }
  1010. #endif
  1011. /****************************************************************************
  1012. * Name: cs43l22_pause
  1013. *
  1014. * Description:
  1015. * Pauses the playback.
  1016. *
  1017. ****************************************************************************/
  1018. #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
  1019. # ifdef CONFIG_AUDIO_MULTI_SESSION
  1020. static int cs43l22_pause(FAR struct audio_lowerhalf_s *dev, FAR void *session)
  1021. # else
  1022. static int cs43l22_pause(FAR struct audio_lowerhalf_s *dev)
  1023. # endif
  1024. {
  1025. FAR struct cs43l22_dev_s *priv = (FAR struct cs43l22_dev_s *)dev;
  1026. if (priv->running && !priv->paused)
  1027. {
  1028. /* Disable interrupts to prevent us from suppling any more data */
  1029. priv->paused = true;
  1030. cs43l22_setvolume(priv, priv->volume, true);
  1031. CS43L22_DISABLE(priv->lower);
  1032. }
  1033. return OK;
  1034. }
  1035. #endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
  1036. /****************************************************************************
  1037. * Name: cs43l22_resume
  1038. *
  1039. * Description:
  1040. * Resumes the playback.
  1041. *
  1042. ****************************************************************************/
  1043. #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
  1044. # ifdef CONFIG_AUDIO_MULTI_SESSION
  1045. static int cs43l22_resume(FAR struct audio_lowerhalf_s *dev, FAR void *session)
  1046. # else
  1047. static int cs43l22_resume(FAR struct audio_lowerhalf_s *dev)
  1048. # endif
  1049. {
  1050. FAR struct cs43l22_dev_s *priv = (FAR struct cs43l22_dev_s *)dev;
  1051. if (priv->running && priv->paused)
  1052. {
  1053. priv->paused = false;
  1054. cs43l22_setvolume(priv, priv->volume, false);
  1055. /* Enable interrupts to allow sampling data */
  1056. cs43l22_sendbuffer(priv);
  1057. #ifdef CS43L22_USE_FFLOCK_INT
  1058. CS43L22_ENABLE(priv->lower);
  1059. #endif
  1060. }
  1061. return OK;
  1062. }
  1063. #endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
  1064. /****************************************************************************
  1065. * Name: cs43l22_enqueuebuffer
  1066. *
  1067. * Description:
  1068. * Enqueue an Audio Pipeline Buffer for playback/ processing.
  1069. *
  1070. ****************************************************************************/
  1071. static int cs43l22_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
  1072. FAR struct ap_buffer_s *apb)
  1073. {
  1074. FAR struct cs43l22_dev_s *priv = (FAR struct cs43l22_dev_s *)dev;
  1075. struct audio_msg_s term_msg;
  1076. int ret;
  1077. audinfo("Enqueueing: apb=%p curbyte=%d nbytes=%d flags=%04x\n",
  1078. apb, apb->curbyte, apb->nbytes, apb->flags);
  1079. /* Take a reference on the new audio buffer */
  1080. apb_reference(apb);
  1081. /* Add the new buffer to the tail of pending audio buffers */
  1082. cs43l22_takesem(&priv->pendsem);
  1083. apb->flags |= AUDIO_APB_OUTPUT_ENQUEUED;
  1084. dq_addlast(&apb->dq_entry, &priv->pendq);
  1085. cs43l22_givesem(&priv->pendsem);
  1086. /* Send a message to the worker thread indicating that a new buffer has been
  1087. * enqueued. If mq is NULL, then the playing has not yet started. In that
  1088. * case we are just "priming the pump" and we don't need to send any message.
  1089. */
  1090. ret = OK;
  1091. if (priv->mq != NULL)
  1092. {
  1093. term_msg.msgId = AUDIO_MSG_ENQUEUE;
  1094. term_msg.u.data = 0;
  1095. ret = nxmq_send(priv->mq, (FAR const char *)&term_msg,
  1096. sizeof(term_msg), CONFIG_CS43L22_MSG_PRIO);
  1097. if (ret < 0)
  1098. {
  1099. auderr("ERROR: nxmq_send failed: %d\n", ret);
  1100. }
  1101. }
  1102. return ret;
  1103. }
  1104. /****************************************************************************
  1105. * Name: cs43l22_cancelbuffer
  1106. *
  1107. * Description:
  1108. * Called when an enqueued buffer is being cancelled.
  1109. *
  1110. ****************************************************************************/
  1111. static int cs43l22_cancelbuffer(FAR struct audio_lowerhalf_s *dev,
  1112. FAR struct ap_buffer_s *apb)
  1113. {
  1114. audinfo("apb=%p\n", apb);
  1115. return OK;
  1116. }
  1117. /****************************************************************************
  1118. * Name: cs43l22_ioctl
  1119. *
  1120. * Description:
  1121. * Perform a device ioctl
  1122. *
  1123. ****************************************************************************/
  1124. static int cs43l22_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd,
  1125. unsigned long arg)
  1126. {
  1127. #ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
  1128. FAR struct ap_buffer_info_s *bufinfo;
  1129. #endif
  1130. /* Deal with ioctls passed from the upper-half driver */
  1131. switch (cmd)
  1132. {
  1133. /* Check for AUDIOIOC_HWRESET ioctl. This ioctl is passed straight
  1134. * through from the upper-half audio driver.
  1135. */
  1136. case AUDIOIOC_HWRESET:
  1137. {
  1138. /* REVISIT: Should we completely re-initialize the chip? We
  1139. * can't just issue a software reset; that would puts all WM8904
  1140. * registers back in their default state.
  1141. */
  1142. audinfo("AUDIOIOC_HWRESET:\n");
  1143. }
  1144. break;
  1145. /* Report our preferred buffer size and quantity */
  1146. #ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
  1147. case AUDIOIOC_GETBUFFERINFO:
  1148. {
  1149. audinfo("AUDIOIOC_GETBUFFERINFO:\n");
  1150. bufinfo = (FAR struct ap_buffer_info_s *)arg;
  1151. bufinfo->buffer_size = CONFIG_CS43L22_BUFFER_SIZE;
  1152. bufinfo->nbuffers = CONFIG_CS43L22_NUM_BUFFERS;
  1153. }
  1154. break;
  1155. #endif
  1156. default:
  1157. audinfo("Ignored\n");
  1158. break;
  1159. }
  1160. return OK;
  1161. }
  1162. /****************************************************************************
  1163. * Name: cs43l22_reserve
  1164. *
  1165. * Description:
  1166. * Reserves a session (the only one we have).
  1167. *
  1168. ****************************************************************************/
  1169. #ifdef CONFIG_AUDIO_MULTI_SESSION
  1170. static int
  1171. cs43l22_reserve(FAR struct audio_lowerhalf_s *dev, FAR void **session)
  1172. #else
  1173. static int cs43l22_reserve(FAR struct audio_lowerhalf_s *dev)
  1174. #endif
  1175. {
  1176. FAR struct cs43l22_dev_s *priv = (FAR struct cs43l22_dev_s *)dev;
  1177. int ret = OK;
  1178. /* Borrow the APBQ semaphore for thread sync */
  1179. cs43l22_takesem(&priv->pendsem);
  1180. if (priv->reserved)
  1181. {
  1182. ret = -EBUSY;
  1183. }
  1184. else
  1185. {
  1186. /* Initialize the session context */
  1187. #ifdef CONFIG_AUDIO_MULTI_SESSION
  1188. *session = NULL;
  1189. #endif
  1190. priv->inflight = 0;
  1191. priv->running = false;
  1192. priv->paused = false;
  1193. #ifndef CONFIG_AUDIO_EXCLUDE_STOP
  1194. priv->terminating = false;
  1195. #endif
  1196. priv->reserved = true;
  1197. }
  1198. cs43l22_givesem(&priv->pendsem);
  1199. return ret;
  1200. }
  1201. /****************************************************************************
  1202. * Name: cs43l22_release
  1203. *
  1204. * Description:
  1205. * Releases the session (the only one we have).
  1206. *
  1207. ****************************************************************************/
  1208. #ifdef CONFIG_AUDIO_MULTI_SESSION
  1209. static int cs43l22_release(FAR struct audio_lowerhalf_s *dev, FAR void *session)
  1210. #else
  1211. static int cs43l22_release(FAR struct audio_lowerhalf_s *dev)
  1212. #endif
  1213. {
  1214. FAR struct cs43l22_dev_s *priv = (FAR struct cs43l22_dev_s *)dev;
  1215. void *value;
  1216. /* Join any old worker thread we had created to prevent a memory leak */
  1217. if (priv->threadid != 0)
  1218. {
  1219. pthread_join(priv->threadid, &value);
  1220. priv->threadid = 0;
  1221. }
  1222. /* Borrow the APBQ semaphore for thread sync */
  1223. cs43l22_takesem(&priv->pendsem);
  1224. /* Really we should free any queued buffers here */
  1225. priv->reserved = false;
  1226. cs43l22_givesem(&priv->pendsem);
  1227. return OK;
  1228. }
  1229. /****************************************************************************
  1230. * Name: cs43l22_interrupt_work
  1231. *
  1232. * Description:
  1233. * CS43L22 interrupt actions cannot be performed in the interrupt handler
  1234. * because I2C access is not possible in that context. Instead, all I2C
  1235. * operations are deferred to the work queue.
  1236. *
  1237. * Assumptions:
  1238. * CS43L22 interrupts were disabled in the interrupt handler.
  1239. *
  1240. ****************************************************************************/
  1241. #ifdef CS43L22_USE_FFLOCK_INT
  1242. static void cs43l22_interrupt_work(FAR void *arg)
  1243. {
  1244. /* TODO */
  1245. #warning Missing logic
  1246. }
  1247. #endif
  1248. /****************************************************************************
  1249. * Name: cs43l22_interrupt
  1250. *
  1251. * Description:
  1252. * This is the ISR that services the GPIO1/IRQ pin from the CS43L22. It
  1253. * signals CS43L22 events such FLL lock.
  1254. *
  1255. ****************************************************************************/
  1256. #ifdef CS43L22_USE_FFLOCK_INT
  1257. static int
  1258. cs43l22_interrupt(FAR const struct cs43l22_lower_s *lower, FAR void *arg)
  1259. {
  1260. /* TODO */
  1261. #warning Missing logic
  1262. }
  1263. #endif
  1264. /****************************************************************************
  1265. * Name: cs43l22_workerthread
  1266. *
  1267. * This is the thread that feeds data to the chip and keeps the audio
  1268. * stream going.
  1269. *
  1270. ****************************************************************************/
  1271. static void *cs43l22_workerthread(pthread_addr_t pvarg)
  1272. {
  1273. FAR struct cs43l22_dev_s *priv = (struct cs43l22_dev_s *)pvarg;
  1274. struct audio_msg_s msg;
  1275. FAR struct ap_buffer_s *apb;
  1276. int msglen;
  1277. int prio;
  1278. audinfo("Entry\n");
  1279. #ifndef CONFIG_AUDIO_EXCLUDE_STOP
  1280. priv->terminating = false;
  1281. #endif
  1282. /* Mark ourself as running and make sure that CS43L22 interrupts are
  1283. * enabled.
  1284. */
  1285. priv->running = true;
  1286. #ifdef CS43L22_USE_FFLOCK_INT
  1287. CS43L22_ENABLE(priv->lower);
  1288. #endif
  1289. cs43l22_setvolume(priv, priv->volume, false);
  1290. /* Loop as long as we are supposed to be running and as long as we have
  1291. * buffers in-flight.
  1292. */
  1293. while (priv->running || priv->inflight > 0)
  1294. {
  1295. /* Check if we have been asked to terminate. We have to check if we
  1296. * still have buffers in-flight. If we do, then we can't stop until
  1297. * birds come back to roost.
  1298. */
  1299. if (priv->terminating && priv->inflight <= 0)
  1300. {
  1301. /* We are IDLE. Break out of the loop and exit. */
  1302. break;
  1303. }
  1304. else
  1305. {
  1306. /* Check if we can send more audio buffers to the CS43L22 */
  1307. cs43l22_sendbuffer(priv);
  1308. }
  1309. /* Wait for messages from our message queue */
  1310. msglen = nxmq_receive(priv->mq, (FAR char *)&msg, sizeof(msg), &prio);
  1311. /* Handle the case when we return with no message */
  1312. if (msglen < sizeof(struct audio_msg_s))
  1313. {
  1314. auderr("ERROR: Message too small: %d\n", msglen);
  1315. continue;
  1316. }
  1317. /* Process the message */
  1318. switch (msg.msgId)
  1319. {
  1320. /* The ISR has requested more data. We will catch this case at
  1321. * the top of the loop.
  1322. */
  1323. case AUDIO_MSG_DATA_REQUEST:
  1324. audinfo("AUDIO_MSG_DATA_REQUEST\n");
  1325. break;
  1326. /* Stop the playback */
  1327. #ifndef CONFIG_AUDIO_EXCLUDE_STOP
  1328. case AUDIO_MSG_STOP:
  1329. /* Indicate that we are terminating */
  1330. audinfo("AUDIO_MSG_STOP: Terminating\n");
  1331. priv->terminating = true;
  1332. break;
  1333. #endif
  1334. /* We have a new buffer to send. We will catch this case at
  1335. * the top of the loop.
  1336. */
  1337. case AUDIO_MSG_ENQUEUE:
  1338. audinfo("AUDIO_MSG_ENQUEUE\n");
  1339. break;
  1340. /* We will wake up from the I2S callback with this message */
  1341. case AUDIO_MSG_COMPLETE:
  1342. audinfo("AUDIO_MSG_COMPLETE\n");
  1343. cs43l22_returnbuffers(priv);
  1344. break;
  1345. default:
  1346. auderr("ERROR: Ignoring message ID %d\n", msg.msgId);
  1347. break;
  1348. }
  1349. }
  1350. /* Reset the CS43L22 hardware */
  1351. cs43l22_reset(priv);
  1352. /* Return any pending buffers in our pending queue */
  1353. cs43l22_takesem(&priv->pendsem);
  1354. while ((apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq)) != NULL)
  1355. {
  1356. /* Release our reference to the buffer */
  1357. apb_free(apb);
  1358. /* Send the buffer back up to the previous level. */
  1359. #ifdef CONFIG_AUDIO_MULTI_SESSION
  1360. priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK, NULL);
  1361. #else
  1362. priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK);
  1363. #endif
  1364. }
  1365. cs43l22_givesem(&priv->pendsem);
  1366. /* Return any pending buffers in our done queue */
  1367. cs43l22_returnbuffers(priv);
  1368. /* Close the message queue */
  1369. mq_close(priv->mq);
  1370. mq_unlink(priv->mqname);
  1371. priv->mq = NULL;
  1372. /* Send an AUDIO_MSG_COMPLETE message to the client */
  1373. #ifdef CONFIG_AUDIO_MULTI_SESSION
  1374. priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK, NULL);
  1375. #else
  1376. priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK);
  1377. #endif
  1378. audinfo("Exit\n");
  1379. return NULL;
  1380. }
  1381. /****************************************************************************
  1382. * Name: cs43l22_audio_output
  1383. *
  1384. * Description:
  1385. * Initialize and configure the CS43L22 device as an audio output device.
  1386. *
  1387. * Input Parameters:
  1388. * priv - A reference to the driver state structure
  1389. *
  1390. * Returned Value:
  1391. * None. No failures are detected.
  1392. *
  1393. ****************************************************************************/
  1394. static void cs43l22_audio_output(FAR struct cs43l22_dev_s *priv)
  1395. {
  1396. uint8_t regval;
  1397. /* Keep codec powered off */
  1398. regval = CS43L22_POWER_DOWN;
  1399. cs43l22_writereg(priv, CS43L22_POWER_CTRL1, regval);
  1400. /* SPK always off and HP always on */
  1401. regval = CS43L22_PDN_HPB_ON | CS43L22_PDN_HPA_ON | CS43L22_PDN_SPKB_OFF | CS43L22_PDN_SPKA_OFF;
  1402. cs43l22_writereg(priv, CS43L22_POWER_CTRL2, regval);
  1403. /* Clock configuration: Auto detection */
  1404. regval = CS43L22_AUTO_DETECT_ENABLE | CS43L22_CLKDIV2_ENABLE;
  1405. cs43l22_writereg(priv, CS43L22_CLOCK_CTRL, regval);
  1406. /* Set slave mode and Philips audio standard */
  1407. regval = CS43L22_DAC_IF_I2S;
  1408. cs43l22_writereg(priv, CS43L22_INTERFACE_CTRL1, regval);
  1409. /* Power on the codec */
  1410. regval = CS43L22_POWER_UP;
  1411. cs43l22_writereg(priv, CS43L22_POWER_CTRL1, regval);
  1412. /* Disable the analog soft ramp */
  1413. regval = 0x00;
  1414. cs43l22_writereg(priv, CS43L22_ANLG_ZC_SR_SEL, regval);
  1415. /* Disable the digital soft ramp */
  1416. regval = CS43L22_DEEMPHASIS_ENABLE;
  1417. cs43l22_writereg(priv, CS43L22_MISCLLNS_CTRL, regval);
  1418. /* Disable the limiter attack level */
  1419. regval = 0x00;
  1420. cs43l22_writereg(priv, CS43L22_LIM_CTRL1, regval);
  1421. /* Adjust bass and treble levels */
  1422. regval = CS43L22_BASS_GAIN(0x0f);
  1423. cs43l22_writereg(priv, CS43L22_TONE_CTRL, regval);
  1424. /* Adjust PCM volume level */
  1425. regval = 0x0a;
  1426. cs43l22_writereg(priv, CS43L22_PCM_VOL_A, regval);
  1427. cs43l22_writereg(priv, CS43L22_PCM_VOL_B, regval);
  1428. cs43l22_setdatawidth(priv);
  1429. cs43l22_setvolume(priv, CONFIG_CS43L22_INITVOLUME, false);
  1430. }
  1431. /****************************************************************************
  1432. * Name: cs43l22_audio_input
  1433. *
  1434. * Description:
  1435. * Initialize and configure the CS43L22 device as an audio output device
  1436. * (Right input only). cs43l22_audio_output() must be called first, this
  1437. * function then modifies the configuration to support audio input.
  1438. *
  1439. * Input Parameters:
  1440. * priv - A reference to the driver state structure
  1441. *
  1442. * Returned Value:
  1443. * None. No failures are detected.
  1444. *
  1445. ****************************************************************************/
  1446. #if 0 /* Not used */
  1447. static void cs43l22_audio_input(FAR struct cs43l22_dev_s *priv)
  1448. {
  1449. /* TODO */
  1450. #warning Missing logic
  1451. }
  1452. #endif
  1453. /****************************************************************************
  1454. * Name: cs43l22_configure_ints
  1455. *
  1456. * Description:
  1457. * Configure the GPIO/IRQ interrupt
  1458. *
  1459. * Input Parameters:
  1460. * priv - A reference to the driver state structure
  1461. *
  1462. * Returned Value:
  1463. * None
  1464. *
  1465. ****************************************************************************/
  1466. #ifdef CS43L22_USE_FFLOCK_INT
  1467. static void cs43l22_configure_ints(FAR struct cs43l22_dev_s *priv)
  1468. {
  1469. /* TODO */
  1470. #warning Missing logic
  1471. }
  1472. #endif
  1473. /****************************************************************************
  1474. * Name: cs43l22_reset
  1475. *
  1476. * Description:
  1477. * Reset and re-initialize the CS43L22
  1478. *
  1479. * Input Parameters:
  1480. * priv - A reference to the driver state structure
  1481. *
  1482. * Returned Value:
  1483. * None
  1484. *
  1485. ****************************************************************************/
  1486. static void cs43l22_reset(FAR struct cs43l22_dev_s *priv)
  1487. {
  1488. /* Put audio output back to its initial configuration */
  1489. /* Put audio output back to its initial configuration */
  1490. priv->samprate = CS43L22_DEFAULT_SAMPRATE;
  1491. priv->nchannels = CS43L22_DEFAULT_NCHANNELS;
  1492. priv->bpsamp = CS43L22_DEFAULT_BPSAMP;
  1493. #if !defined(CONFIG_AUDIO_EXCLUDE_VOLUME) && !defined(CONFIG_AUDIO_EXCLUDE_BALANCE)
  1494. priv->balance = 500; // b16HALF; /* Center balance */
  1495. #endif
  1496. /* Software reset. This puts all CS43L22 registers back in their
  1497. * default state.
  1498. */
  1499. /* cs43l22_writereg(priv, CS43L22_SWRST, 0); */
  1500. /* Configure the CS43L22 hardware as an audio input device */
  1501. cs43l22_audio_output(priv);
  1502. /* Configure interrupts */
  1503. /* cs43l22_configure_ints(priv); */
  1504. /* Configure the FLL and the LRCLK */
  1505. cs43l22_setbitrate(priv);
  1506. /* Dump some information and return the device instance */
  1507. cs43l22_dump_registers(&priv->dev, "After configuration");
  1508. cs43l22_clock_analysis(&priv->dev, "After configuration");
  1509. }
  1510. /****************************************************************************
  1511. * Public Functions
  1512. ****************************************************************************/
  1513. /****************************************************************************
  1514. * Name: cs43l22_initialize
  1515. *
  1516. * Description:
  1517. * Initialize the CS43L22 device.
  1518. *
  1519. * Input Parameters:
  1520. * i2c - An I2C driver instance
  1521. * i2s - An I2S driver instance
  1522. * lower - Persistent board configuration data
  1523. *
  1524. * Returned Value:
  1525. * A new lower half audio interface for the CS43L22 device is returned on
  1526. * success; NULL is returned on failure.
  1527. *
  1528. ****************************************************************************/
  1529. FAR struct audio_lowerhalf_s *cs43l22_initialize(FAR struct i2c_master_s *i2c,
  1530. FAR struct i2s_dev_s *i2s,
  1531. FAR const struct
  1532. cs43l22_lower_s *lower)
  1533. {
  1534. FAR struct cs43l22_dev_s *priv;
  1535. uint16_t regval;
  1536. /* Sanity check */
  1537. DEBUGASSERT(i2c && i2s && lower);
  1538. /* Allocate a CS43L22 device structure */
  1539. priv = (FAR struct cs43l22_dev_s *)kmm_zalloc(sizeof(struct cs43l22_dev_s));
  1540. if (priv)
  1541. {
  1542. /* Initialize the CS43L22 device structure. Since we used kmm_zalloc,
  1543. * only the non-zero elements of the structure need to be initialized.
  1544. */
  1545. priv->dev.ops = &g_audioops;
  1546. priv->lower = lower;
  1547. priv->i2c = i2c;
  1548. priv->i2s = i2s;
  1549. nxsem_init(&priv->pendsem, 0, 1);
  1550. dq_init(&priv->pendq);
  1551. dq_init(&priv->doneq);
  1552. /* Initialize I2C */
  1553. audinfo("address=%02x frequency=%d\n", lower->address, lower->frequency);
  1554. /* Software reset. This puts all CS43L22 registers back in their default
  1555. * state. */
  1556. CS43L22_HW_RESET(priv->lower);
  1557. cs43l22_dump_registers(&priv->dev, "After reset");
  1558. /* Verify that CS43L22 is present and available on this I2C */
  1559. regval = cs43l22_readreg(priv, CS43L22_ID_REV);
  1560. if ((regval & 0xff) != CS43L22_DEV_ID_REV)
  1561. {
  1562. auderr("ERROR: CS43L22 not found: ID=%02x\n", regval);
  1563. goto errout_with_dev;
  1564. }
  1565. /* Reset and reconfigure the CS43L22 hardware */
  1566. cs43l22_reset(priv);
  1567. return &priv->dev;
  1568. }
  1569. return NULL;
  1570. errout_with_dev:
  1571. nxsem_destroy(&priv->pendsem);
  1572. kmm_free(priv);
  1573. return NULL;
  1574. }