wm8776.c 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359
  1. /****************************************************************************
  2. * drivers/audio/wm8776.c
  3. *
  4. * Copyright 2017, 2018 Sony Video & Sound Products Inc.
  5. * Author: Masayuki Ishikawa <Masayuki.Ishikawa@jp.sony.com>
  6. *
  7. * Based on drivers/audio/wm8904.c
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions
  11. * are met:
  12. *
  13. * 1. Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. * 3. Neither the name NuttX nor the names of its contributors may be
  20. * used to endorse or promote products derived from this software
  21. * without specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  24. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  25. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  26. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  27. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  28. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  29. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  30. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  31. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  33. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  34. * POSSIBILITY OF SUCH DAMAGE.
  35. *
  36. ****************************************************************************/
  37. /****************************************************************************
  38. * Included Files
  39. ****************************************************************************/
  40. #include <nuttx/config.h>
  41. #include <sys/types.h>
  42. #include <sys/ioctl.h>
  43. #include <stdint.h>
  44. #include <stdio.h>
  45. #include <fcntl.h>
  46. #include <string.h>
  47. #include <errno.h>
  48. #include <fixedmath.h>
  49. #include <queue.h>
  50. #include <debug.h>
  51. #include <nuttx/irq.h>
  52. #include <nuttx/kmalloc.h>
  53. #include <nuttx/semaphore.h>
  54. #include <nuttx/clock.h>
  55. #include <nuttx/wqueue.h>
  56. #include <nuttx/i2c/i2c_master.h>
  57. #include <nuttx/fs/fs.h>
  58. #include <nuttx/fs/ioctl.h>
  59. #include <nuttx/audio/i2s.h>
  60. #include <nuttx/audio/audio.h>
  61. #include <nuttx/audio/wm8776.h>
  62. #include "wm8776.h"
  63. /****************************************************************************
  64. * Pre-processor Definitions
  65. ****************************************************************************/
  66. static void wm8776_writereg(FAR struct wm8776_dev_s *priv,
  67. uint8_t regaddr, uint16_t regval);
  68. static void wm8776_takesem(sem_t *sem);
  69. #define wm8776_givesem(s) nxsem_post(s)
  70. static int wm8776_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
  71. FAR struct audio_caps_s *caps);
  72. #ifdef CONFIG_AUDIO_MULTI_SESSION
  73. static int wm8776_configure(FAR struct audio_lowerhalf_s *dev,
  74. FAR void *session, FAR const struct audio_caps_s *caps);
  75. #else
  76. static int wm8776_configure(FAR struct audio_lowerhalf_s *dev,
  77. FAR const struct audio_caps_s *caps);
  78. #endif
  79. static int wm8776_shutdown(FAR struct audio_lowerhalf_s *dev);
  80. static void wm8776_senddone(FAR struct i2s_dev_s *i2s,
  81. FAR struct ap_buffer_s *apb, FAR void *arg, int result);
  82. static void wm8776_returnbuffers(FAR struct wm8776_dev_s *priv);
  83. static int wm8776_sendbuffer(FAR struct wm8776_dev_s *priv);
  84. #ifdef CONFIG_AUDIO_MULTI_SESSION
  85. static int wm8776_start(FAR struct audio_lowerhalf_s *dev,
  86. FAR void *session);
  87. #else
  88. static int wm8776_start(FAR struct audio_lowerhalf_s *dev);
  89. #endif
  90. #ifndef CONFIG_AUDIO_EXCLUDE_STOP
  91. #ifdef CONFIG_AUDIO_MULTI_SESSION
  92. static int wm8776_stop(FAR struct audio_lowerhalf_s *dev,
  93. FAR void *session);
  94. #else
  95. static int wm8776_stop(FAR struct audio_lowerhalf_s *dev);
  96. #endif
  97. #endif
  98. #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
  99. #ifdef CONFIG_AUDIO_MULTI_SESSION
  100. static int wm8776_pause(FAR struct audio_lowerhalf_s *dev,
  101. FAR void *session);
  102. static int wm8776_resume(FAR struct audio_lowerhalf_s *dev,
  103. FAR void *session);
  104. #else
  105. static int wm8776_pause(FAR struct audio_lowerhalf_s *dev);
  106. static int wm8776_resume(FAR struct audio_lowerhalf_s *dev);
  107. #endif
  108. #endif
  109. static int wm8776_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
  110. FAR struct ap_buffer_s *apb);
  111. static int wm8776_cancelbuffer(FAR struct audio_lowerhalf_s *dev,
  112. FAR struct ap_buffer_s *apb);
  113. static int wm8776_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd,
  114. unsigned long arg);
  115. #ifdef CONFIG_AUDIO_MULTI_SESSION
  116. static int wm8776_reserve(FAR struct audio_lowerhalf_s *dev,
  117. FAR void **session);
  118. #else
  119. static int wm8776_reserve(FAR struct audio_lowerhalf_s *dev);
  120. #endif
  121. #ifdef CONFIG_AUDIO_MULTI_SESSION
  122. static int wm8776_release(FAR struct audio_lowerhalf_s *dev,
  123. FAR void *session);
  124. #else
  125. static int wm8776_release(FAR struct audio_lowerhalf_s *dev);
  126. #endif
  127. static void *wm8776_workerthread(pthread_addr_t pvarg);
  128. /* Initialization */
  129. static void wm8776_audio_output(FAR struct wm8776_dev_s *priv);
  130. #if 0 /* Not used */
  131. static void wm8776_audio_input(FAR struct wm8776_dev_s *priv);
  132. #endif
  133. static void wm8776_hw_reset(FAR struct wm8776_dev_s *priv);
  134. /****************************************************************************
  135. * Private Data
  136. ****************************************************************************/
  137. static const struct audio_ops_s g_audioops =
  138. {
  139. wm8776_getcaps, /* getcaps */
  140. wm8776_configure, /* configure */
  141. wm8776_shutdown, /* shutdown */
  142. wm8776_start, /* start */
  143. #ifndef CONFIG_AUDIO_EXCLUDE_STOP
  144. wm8776_stop, /* stop */
  145. #endif
  146. #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
  147. wm8776_pause, /* pause */
  148. wm8776_resume, /* resume */
  149. #endif
  150. NULL, /* allocbuffer */
  151. NULL, /* freebuffer */
  152. wm8776_enqueuebuffer, /* enqueue_buffer */
  153. wm8776_cancelbuffer, /* cancel_buffer */
  154. wm8776_ioctl, /* ioctl */
  155. NULL, /* read */
  156. NULL, /* write */
  157. wm8776_reserve, /* reserve */
  158. wm8776_release /* release */
  159. };
  160. /************************************************************************************
  161. * Name: wm8776_writereg
  162. *
  163. * Description:
  164. * Write the specified 16-bit register to the WM8776 device.
  165. *
  166. ************************************************************************************/
  167. static void wm8776_writereg(FAR struct wm8776_dev_s *priv,
  168. uint8_t regaddr,
  169. uint16_t regval)
  170. {
  171. struct i2c_config_s config;
  172. uint8_t data[2];
  173. int ret;
  174. /* Setup up the I2C configuration */
  175. config.frequency = priv->lower->frequency;
  176. config.address = priv->lower->address;
  177. config.addrlen = 7;
  178. /* Set up the data to write */
  179. data[0] = (regaddr << 1) + ((regval >> 8) & 0x1);
  180. data[1] = (regval & 0xff);
  181. ret = i2c_write(priv->i2c, &config, data, sizeof(data));
  182. if (ret < 0)
  183. {
  184. auderr("ERROR: I2C_TRANSFER failed: %d\n", ret);
  185. }
  186. }
  187. /************************************************************************************
  188. * Name: wm8776_takesem
  189. *
  190. * Description:
  191. * Take a semaphore count, handling the nasty EINTR return if we are interrupted
  192. * by a signal.
  193. *
  194. ************************************************************************************/
  195. static void wm8776_takesem(sem_t *sem)
  196. {
  197. int ret;
  198. do
  199. {
  200. /* Take the semaphore (perhaps waiting) */
  201. ret = nxsem_wait(sem);
  202. /* The only case that an error should occur here is if the wait was
  203. * awakened by a signal.
  204. */
  205. DEBUGASSERT(ret == OK || ret == -EINTR);
  206. }
  207. while (ret == -EINTR);
  208. }
  209. /************************************************************************************
  210. * Name: wm8776_setvolume
  211. *
  212. * Description:
  213. * Set the right and left volume values in the WM8776 device based on the current
  214. * volume and balance settings.
  215. *
  216. ************************************************************************************/
  217. #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
  218. static void wm8776_setvolume(FAR struct wm8776_dev_s *priv, uint16_t volume,
  219. bool mute)
  220. {
  221. uint16_t regval;
  222. uint16_t tmp_vol;
  223. /* TODO: balance */
  224. if (mute)
  225. {
  226. tmp_vol = 0;
  227. }
  228. else
  229. {
  230. tmp_vol = volume;
  231. }
  232. /* limit the max vol */
  233. if (tmp_vol > 0x69)
  234. {
  235. tmp_vol = 0x69; /* -10db */
  236. }
  237. regval = WM8776_UPDATE | WM8776_HPOUT_VOL(tmp_vol);
  238. wm8776_writereg(priv, WM8776_MASTER_ATT, regval);
  239. audinfo("volume=%d mute=%d tmp_vol=%d (regval=0x%x) \n",
  240. volume, mute, tmp_vol, regval);
  241. /* Remember the volume level and mute settings */
  242. priv->volume = volume;
  243. priv->mute = mute;
  244. }
  245. #endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
  246. /****************************************************************************
  247. * Name: wm8776_getcaps
  248. *
  249. * Description:
  250. * Get the audio device capabilities
  251. *
  252. ****************************************************************************/
  253. static int wm8776_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
  254. FAR struct audio_caps_s *caps)
  255. {
  256. /* Validate the structure */
  257. DEBUGASSERT(caps && caps->ac_len >= sizeof(struct audio_caps_s));
  258. audinfo("type=%d ac_type=%d\n", type, caps->ac_type);
  259. /* Fill in the caller's structure based on requested info */
  260. caps->ac_format.hw = 0;
  261. caps->ac_controls.w = 0;
  262. switch (caps->ac_type)
  263. {
  264. /* Caller is querying for the types of units we support */
  265. case AUDIO_TYPE_QUERY:
  266. /* Provide our overall capabilities. The interfacing software
  267. * must then call us back for specific info for each capability.
  268. */
  269. caps->ac_channels = 2; /* Stereo output */
  270. switch (caps->ac_subtype)
  271. {
  272. case AUDIO_TYPE_QUERY:
  273. /* We don't decode any formats! Only something above us in
  274. * the audio stream can perform decoding on our behalf.
  275. */
  276. /* The types of audio units we implement */
  277. caps->ac_controls.b[0] = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_FEATURE |
  278. AUDIO_TYPE_PROCESSING;
  279. break;
  280. default:
  281. caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
  282. break;
  283. }
  284. break;
  285. /* Provide capabilities of our OUTPUT unit */
  286. case AUDIO_TYPE_OUTPUT:
  287. caps->ac_channels = 2;
  288. switch (caps->ac_subtype)
  289. {
  290. case AUDIO_TYPE_QUERY:
  291. /* Report the Sample rates we support */
  292. caps->ac_controls.b[0] = AUDIO_SAMP_RATE_44K;
  293. break;
  294. case AUDIO_FMT_MP3:
  295. case AUDIO_FMT_WMA:
  296. case AUDIO_FMT_PCM:
  297. default:
  298. break;
  299. }
  300. break;
  301. /* All others we don't support */
  302. default:
  303. /* Zero out the fields to indicate no support */
  304. caps->ac_subtype = 0;
  305. caps->ac_channels = 0;
  306. break;
  307. }
  308. /* Return the length of the audio_caps_s struct for validation of
  309. * proper Audio device type.
  310. */
  311. return caps->ac_len;
  312. }
  313. /****************************************************************************
  314. * Name: wm8776_configure
  315. *
  316. * Description:
  317. * Configure the audio device for the specified mode of operation.
  318. *
  319. ****************************************************************************/
  320. #ifdef CONFIG_AUDIO_MULTI_SESSION
  321. static int wm8776_configure(FAR struct audio_lowerhalf_s *dev,
  322. FAR void *session,
  323. FAR const struct audio_caps_s *caps)
  324. #else
  325. static int wm8776_configure(FAR struct audio_lowerhalf_s *dev,
  326. FAR const struct audio_caps_s *caps)
  327. #endif
  328. {
  329. FAR struct wm8776_dev_s *priv = (FAR struct wm8776_dev_s *)dev;
  330. int ret = OK;
  331. DEBUGASSERT(priv != NULL && caps != NULL);
  332. audinfo("ac_type: %d\n", caps->ac_type);
  333. /* Process the configure operation */
  334. switch (caps->ac_type)
  335. {
  336. case AUDIO_TYPE_FEATURE:
  337. audinfo(" AUDIO_TYPE_FEATURE\n");
  338. /* Process based on Feature Unit */
  339. switch (caps->ac_format.hw)
  340. {
  341. #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
  342. case AUDIO_FU_VOLUME:
  343. {
  344. /* Set the volume */
  345. uint16_t volume = caps->ac_controls.hw[0];
  346. audinfo(" Volume: %d\n", volume);
  347. if (volume >= 0 && volume <= 1000)
  348. {
  349. /* Scale the volume setting to the range {0x2f .. 0x79} */
  350. wm8776_setvolume(priv, (0x4a * volume / 1000) + 0x2f, priv->mute);
  351. }
  352. else
  353. {
  354. ret = -EDOM;
  355. }
  356. }
  357. break;
  358. #endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
  359. default:
  360. auderr(" ERROR: Unrecognized feature unit\n");
  361. ret = -ENOTTY;
  362. break;
  363. }
  364. break;
  365. case AUDIO_TYPE_OUTPUT:
  366. {
  367. audinfo(" AUDIO_TYPE_OUTPUT:\n");
  368. audinfo(" Number of channels: %u\n", caps->ac_channels);
  369. audinfo(" Sample rate: %u\n", caps->ac_controls.hw[0]);
  370. audinfo(" Sample width: %u\n", caps->ac_controls.b[2]);
  371. /* Verify that all of the requested values are supported */
  372. ret = -ERANGE;
  373. if (caps->ac_channels != 1 && caps->ac_channels != 2)
  374. {
  375. auderr("ERROR: Unsupported number of channels: %d\n",
  376. caps->ac_channels);
  377. break;
  378. }
  379. if (caps->ac_controls.b[2] != 8 && caps->ac_controls.b[2] != 16)
  380. {
  381. auderr("ERROR: Unsupported bits per sample: %d\n",
  382. caps->ac_controls.b[2]);
  383. break;
  384. }
  385. /* Save the current stream configuration */
  386. priv->samprate = caps->ac_controls.hw[0];
  387. priv->nchannels = caps->ac_channels;
  388. priv->bpsamp = caps->ac_controls.b[2];
  389. /* TODO : channels, bits per sample, bitrate */
  390. ret = OK;
  391. }
  392. break;
  393. case AUDIO_TYPE_PROCESSING:
  394. break;
  395. }
  396. return ret;
  397. }
  398. /****************************************************************************
  399. * Name: wm8776_shutdown
  400. *
  401. * Description:
  402. * Shutdown the WM8776 chip and put it in the lowest power state possible.
  403. *
  404. ****************************************************************************/
  405. static int wm8776_shutdown(FAR struct audio_lowerhalf_s *dev)
  406. {
  407. FAR struct wm8776_dev_s *priv = (FAR struct wm8776_dev_s *)dev;
  408. DEBUGASSERT(priv);
  409. /* Now issue a software reset. This puts all WM8776 registers back in
  410. * their default state.
  411. */
  412. wm8776_hw_reset(priv);
  413. return OK;
  414. }
  415. /****************************************************************************
  416. * Name: wm8776_senddone
  417. *
  418. * Description:
  419. * This is the I2S callback function that is invoked when the transfer
  420. * completes.
  421. *
  422. ****************************************************************************/
  423. static void wm8776_senddone(FAR struct i2s_dev_s *i2s,
  424. FAR struct ap_buffer_s *apb, FAR void *arg,
  425. int result)
  426. {
  427. FAR struct wm8776_dev_s *priv = (FAR struct wm8776_dev_s *)arg;
  428. struct audio_msg_s msg;
  429. irqstate_t flags;
  430. int ret;
  431. DEBUGASSERT(i2s && priv && priv->running && apb);
  432. audinfo("apb=%p inflight=%d result=%d\n", apb, priv->inflight, result);
  433. /* We do not place any restriction on the context in which this function
  434. * is called. It may be called from an interrupt handler. Therefore, the
  435. * doneq and in-flight values might be accessed from the interrupt level.
  436. * Not the best design. But we will use interrupt controls to protect
  437. * against that possibility.
  438. */
  439. flags = spin_lock_irqsave();
  440. /* Add the completed buffer to the end of our doneq. We do not yet
  441. * decrement the reference count.
  442. */
  443. dq_addlast((FAR dq_entry_t *)apb, &priv->doneq);
  444. /* And decrement the number of buffers in-flight */
  445. DEBUGASSERT(priv->inflight > 0);
  446. priv->inflight--;
  447. /* Save the result of the transfer */
  448. /* REVISIT: This can be overwritten */
  449. priv->result = result;
  450. spin_unlock_irqrestore(flags);
  451. /* Now send a message to the worker thread, informing it that there are
  452. * buffers in the done queue that need to be cleaned up.
  453. */
  454. msg.msgId = AUDIO_MSG_COMPLETE;
  455. ret = mq_send(priv->mq, (FAR const char *)&msg, sizeof(msg),
  456. CONFIG_WM8776_MSG_PRIO);
  457. if (ret < 0)
  458. {
  459. auderr("ERROR: mq_send failed: %d\n", get_errno());
  460. }
  461. }
  462. /****************************************************************************
  463. * Name: wm8776_returnbuffers
  464. *
  465. * Description:
  466. * This function is called after the complete of one or more data
  467. * transfers. This function will empty the done queue and release our
  468. * reference to each buffer.
  469. *
  470. ****************************************************************************/
  471. static void wm8776_returnbuffers(FAR struct wm8776_dev_s *priv)
  472. {
  473. FAR struct ap_buffer_s *apb;
  474. irqstate_t flags;
  475. /* The doneq and in-flight values might be accessed from the interrupt
  476. * level in some implementations. Not the best design. But we will
  477. * use interrupt controls to protect against that possibility.
  478. */
  479. flags = spin_lock_irqsave();
  480. while (dq_peek(&priv->doneq) != NULL)
  481. {
  482. /* Take the next buffer from the queue of completed transfers */
  483. apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->doneq);
  484. spin_unlock_irqrestore(flags);
  485. audinfo("Returning: apb=%p curbyte=%d nbytes=%d flags=%04x\n",
  486. apb, apb->curbyte, apb->nbytes, apb->flags);
  487. /* Are we returning the final buffer in the stream? */
  488. if ((apb->flags & AUDIO_APB_FINAL) != 0)
  489. {
  490. /* Both the pending and the done queues should be empty and there
  491. * should be no buffers in-flight.
  492. */
  493. DEBUGASSERT(dq_empty(&priv->doneq) && dq_empty(&priv->pendq) &&
  494. priv->inflight == 0);
  495. /* Set the terminating flag. This will, eventually, cause the
  496. * worker thread to exit (if it is not already terminating).
  497. */
  498. audinfo("Terminating\n");
  499. priv->terminating = true;
  500. }
  501. /* Release our reference to the audio buffer */
  502. apb_free(apb);
  503. /* Send the buffer back up to the previous level. */
  504. #ifdef CONFIG_AUDIO_MULTI_SESSION
  505. priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK, NULL);
  506. #else
  507. priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK);
  508. #endif
  509. flags = spin_lock_irqsave();
  510. }
  511. spin_unlock_irqrestore(flags);
  512. }
  513. /****************************************************************************
  514. * Name: wm8776_sendbuffer
  515. *
  516. * Description:
  517. * Start the transfer an audio buffer to the WM8776 via I2S. This
  518. * will not wait for the transfer to complete but will return immediately.
  519. * the wmd8776_senddone called will be invoked when the transfer
  520. * completes, stimulating the worker thread to call this function again.
  521. *
  522. ****************************************************************************/
  523. static int wm8776_sendbuffer(FAR struct wm8776_dev_s *priv)
  524. {
  525. FAR struct ap_buffer_s *apb;
  526. irqstate_t flags;
  527. uint32_t timeout;
  528. int shift;
  529. int ret = OK;
  530. /* Loop while there are audio buffers to be sent and we have few than
  531. * CONFIG_WM8776_INFLIGHT then "in-flight"
  532. *
  533. * The 'inflight' value might be modified from the interrupt level in some
  534. * implementations. We will use interrupt controls to protect against
  535. * that possibility.
  536. *
  537. * The 'pendq', on the other hand, is protected via a semaphore. Let's
  538. * hold the semaphore while we are busy here and disable the interrupts
  539. * only while accessing 'inflight'.
  540. */
  541. wm8776_takesem(&priv->pendsem);
  542. while (priv->inflight < CONFIG_WM8776_INFLIGHT &&
  543. dq_peek(&priv->pendq) != NULL && !priv->paused)
  544. {
  545. /* Take next buffer from the queue of pending transfers */
  546. apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq);
  547. audinfo("Sending apb=%p, size=%d inflight=%d\n",
  548. apb, apb->nbytes, priv->inflight);
  549. /* Increment the number of buffers in-flight before sending in order
  550. * to avoid a possible race condition.
  551. */
  552. flags = spin_lock_irqsave();
  553. priv->inflight++;
  554. spin_unlock_irqrestore(flags);
  555. shift = (priv->bpsamp == 8) ? 14 - 3 : 14 - 4;
  556. shift -= (priv->nchannels > 1) ? 1 : 0;
  557. timeout = MSEC2TICK(((uint32_t)(apb->nbytes - apb->curbyte) << shift) /
  558. (uint32_t)priv->samprate);
  559. ret = I2S_SEND(priv->i2s, apb, wm8776_senddone, priv, timeout);
  560. if (ret < 0)
  561. {
  562. auderr("ERROR: I2S_SEND failed: %d\n", ret);
  563. break;
  564. }
  565. }
  566. wm8776_givesem(&priv->pendsem);
  567. return ret;
  568. }
  569. /****************************************************************************
  570. * Name: wm8776_start
  571. *
  572. * Description:
  573. * Start the configured operation (audio streaming, volume enabled, etc.).
  574. *
  575. ****************************************************************************/
  576. #ifdef CONFIG_AUDIO_MULTI_SESSION
  577. static int wm8776_start(FAR struct audio_lowerhalf_s *dev, FAR void *session)
  578. #else
  579. static int wm8776_start(FAR struct audio_lowerhalf_s *dev)
  580. #endif
  581. {
  582. FAR struct wm8776_dev_s *priv = (FAR struct wm8776_dev_s *)dev;
  583. struct sched_param sparam;
  584. struct mq_attr attr;
  585. pthread_attr_t tattr;
  586. FAR void *value;
  587. int ret;
  588. audinfo("Entry\n");
  589. /* Exit reduced power modes of operation */
  590. /* REVISIT */
  591. /* Create a message queue for the worker thread */
  592. snprintf(priv->mqname, sizeof(priv->mqname), "/tmp/%X", priv);
  593. attr.mq_maxmsg = 16;
  594. attr.mq_msgsize = sizeof(struct audio_msg_s);
  595. attr.mq_curmsgs = 0;
  596. attr.mq_flags = 0;
  597. priv->mq = mq_open(priv->mqname, O_RDWR | O_CREAT, 0644, &attr);
  598. if (priv->mq == NULL)
  599. {
  600. /* Error creating message queue! */
  601. auderr("ERROR: Couldn't allocate message queue\n");
  602. return -ENOMEM;
  603. }
  604. /* Join any old worker thread we had created to prevent a memory leak */
  605. if (priv->threadid != 0)
  606. {
  607. audinfo("Joining old thread\n");
  608. pthread_join(priv->threadid, &value);
  609. }
  610. /* Start our thread for sending data to the device */
  611. pthread_attr_init(&tattr);
  612. sparam.sched_priority = sched_get_priority_max(SCHED_FIFO) - 3;
  613. (void)pthread_attr_setschedparam(&tattr, &sparam);
  614. (void)pthread_attr_setstacksize(&tattr, CONFIG_WM8776_WORKER_STACKSIZE);
  615. audinfo("Starting worker thread\n");
  616. ret = pthread_create(&priv->threadid, &tattr, wm8776_workerthread,
  617. (pthread_addr_t)priv);
  618. if (ret != OK)
  619. {
  620. auderr("ERROR: pthread_create failed: %d\n", ret);
  621. }
  622. else
  623. {
  624. pthread_setname_np(priv->threadid, "wm8776");
  625. audinfo("Created worker thread\n");
  626. }
  627. return ret;
  628. }
  629. /****************************************************************************
  630. * Name: wm8776_stop
  631. *
  632. * Description: Stop the configured operation (audio streaming, volume
  633. * disabled, etc.).
  634. *
  635. ****************************************************************************/
  636. #ifndef CONFIG_AUDIO_EXCLUDE_STOP
  637. #ifdef CONFIG_AUDIO_MULTI_SESSION
  638. static int wm8776_stop(FAR struct audio_lowerhalf_s *dev, FAR void *session)
  639. #else
  640. static int wm8776_stop(FAR struct audio_lowerhalf_s *dev)
  641. #endif
  642. {
  643. FAR struct wm8776_dev_s *priv = (FAR struct wm8776_dev_s *)dev;
  644. struct audio_msg_s term_msg;
  645. FAR void *value;
  646. /* Send a message to stop all audio streaming */
  647. term_msg.msgId = AUDIO_MSG_STOP;
  648. term_msg.u.data = 0;
  649. mq_send(priv->mq, (FAR const char *)&term_msg, sizeof(term_msg),
  650. CONFIG_WM8776_MSG_PRIO);
  651. /* Join the worker thread */
  652. pthread_join(priv->threadid, &value);
  653. priv->threadid = 0;
  654. /* Enter into a reduced power usage mode */
  655. /* REVISIT: */
  656. return OK;
  657. }
  658. #endif
  659. /****************************************************************************
  660. * Name: wm8776_pause
  661. *
  662. * Description: Pauses the playback.
  663. *
  664. ****************************************************************************/
  665. #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
  666. #ifdef CONFIG_AUDIO_MULTI_SESSION
  667. static int wm8776_pause(FAR struct audio_lowerhalf_s *dev, FAR void *session)
  668. #else
  669. static int wm8776_pause(FAR struct audio_lowerhalf_s *dev)
  670. #endif
  671. {
  672. FAR struct wm8776_dev_s *priv = (FAR struct wm8776_dev_s *)dev;
  673. if (priv->running && !priv->paused)
  674. {
  675. priv->paused = true;
  676. wm8776_setvolume(priv, priv->volume, true);
  677. }
  678. return OK;
  679. }
  680. #endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
  681. /****************************************************************************
  682. * Name: wm8776_resume
  683. *
  684. * Description: Resumes the playback.
  685. *
  686. ****************************************************************************/
  687. #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
  688. #ifdef CONFIG_AUDIO_MULTI_SESSION
  689. static int wm8776_resume(FAR struct audio_lowerhalf_s *dev, FAR void *session)
  690. #else
  691. static int wm8776_resume(FAR struct audio_lowerhalf_s *dev)
  692. #endif
  693. {
  694. FAR struct wm8776_dev_s *priv = (FAR struct wm8776_dev_s *)dev;
  695. if (priv->running && priv->paused)
  696. {
  697. priv->paused = false;
  698. wm8776_setvolume(priv, priv->volume, false);
  699. wm8776_sendbuffer(priv);
  700. }
  701. return OK;
  702. }
  703. #endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
  704. /****************************************************************************
  705. * Name: wm8776_enqueuebuffer
  706. *
  707. * Description: Enqueue an Audio Pipeline Buffer for playback/ processing.
  708. *
  709. ****************************************************************************/
  710. static int wm8776_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
  711. FAR struct ap_buffer_s *apb)
  712. {
  713. FAR struct wm8776_dev_s *priv = (FAR struct wm8776_dev_s *)dev;
  714. struct audio_msg_s term_msg;
  715. int ret;
  716. audinfo("Enqueueing: apb=%p curbyte=%d nbytes=%d flags=%04x\n",
  717. apb, apb->curbyte, apb->nbytes, apb->flags);
  718. /* Take a reference on the new audio buffer */
  719. apb_reference(apb);
  720. /* Add the new buffer to the tail of pending audio buffers */
  721. wm8776_takesem(&priv->pendsem);
  722. apb->flags |= AUDIO_APB_OUTPUT_ENQUEUED;
  723. dq_addlast(&apb->dq_entry, &priv->pendq);
  724. wm8776_givesem(&priv->pendsem);
  725. /* Send a message to the worker thread indicating that a new buffer has been
  726. * enqueued. If mq is NULL, then the playing has not yet started. In that
  727. * case we are just "priming the pump" and we don't need to send any message.
  728. */
  729. ret = OK;
  730. if (priv->mq != NULL)
  731. {
  732. term_msg.msgId = AUDIO_MSG_ENQUEUE;
  733. term_msg.u.data = 0;
  734. ret = mq_send(priv->mq, (FAR const char *)&term_msg, sizeof(term_msg),
  735. CONFIG_WM8776_MSG_PRIO);
  736. if (ret < 0)
  737. {
  738. int errcode = get_errno();
  739. DEBUGASSERT(errcode > 0);
  740. auderr("ERROR: mq_send failed: %d\n", errcode);
  741. UNUSED(errcode);
  742. }
  743. }
  744. return ret;
  745. }
  746. /****************************************************************************
  747. * Name: wm8776_cancelbuffer
  748. *
  749. * Description: Called when an enqueued buffer is being cancelled.
  750. *
  751. ****************************************************************************/
  752. static int wm8776_cancelbuffer(FAR struct audio_lowerhalf_s *dev,
  753. FAR struct ap_buffer_s *apb)
  754. {
  755. audinfo("apb=%p\n", apb);
  756. return OK;
  757. }
  758. /****************************************************************************
  759. * Name: wm8776_ioctl
  760. *
  761. * Description: Perform a device ioctl
  762. *
  763. ****************************************************************************/
  764. static int wm8776_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd,
  765. unsigned long arg)
  766. {
  767. #ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
  768. FAR struct ap_buffer_info_s *bufinfo;
  769. #endif
  770. /* Deal with ioctls passed from the upper-half driver */
  771. switch (cmd)
  772. {
  773. /* Check for AUDIOIOC_HWRESET ioctl. This ioctl is passed straight
  774. * through from the upper-half audio driver.
  775. */
  776. case AUDIOIOC_HWRESET:
  777. {
  778. /* REVISIT: Should we completely re-initialize the chip? We
  779. * can't just issue a software reset; that would puts all WM8776
  780. * registers back in their default state.
  781. */
  782. audinfo("AUDIOIOC_HWRESET:\n");
  783. }
  784. break;
  785. /* Report our preferred buffer size and quantity */
  786. #ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
  787. case AUDIOIOC_GETBUFFERINFO:
  788. {
  789. audinfo("AUDIOIOC_GETBUFFERINFO:\n");
  790. bufinfo = (FAR struct ap_buffer_info_s *) arg;
  791. bufinfo->buffer_size = CONFIG_WM8776_BUFFER_SIZE;
  792. bufinfo->nbuffers = CONFIG_WM8776_NUM_BUFFERS;
  793. }
  794. break;
  795. #endif
  796. default:
  797. audinfo("Ignored\n");
  798. break;
  799. }
  800. return OK;
  801. }
  802. /****************************************************************************
  803. * Name: wm8776_reserve
  804. *
  805. * Description: Reserves a session (the only one we have).
  806. *
  807. ****************************************************************************/
  808. #ifdef CONFIG_AUDIO_MULTI_SESSION
  809. static int wm8776_reserve(FAR struct audio_lowerhalf_s *dev,
  810. FAR void **session)
  811. #else
  812. static int wm8776_reserve(FAR struct audio_lowerhalf_s *dev)
  813. #endif
  814. {
  815. FAR struct wm8776_dev_s *priv = (FAR struct wm8776_dev_s *) dev;
  816. int ret = OK;
  817. /* Borrow the APBQ semaphore for thread sync */
  818. wm8776_takesem(&priv->pendsem);
  819. if (priv->reserved)
  820. {
  821. ret = -EBUSY;
  822. }
  823. else
  824. {
  825. /* Initialize the session context */
  826. #ifdef CONFIG_AUDIO_MULTI_SESSION
  827. *session = NULL;
  828. #endif
  829. priv->inflight = 0;
  830. priv->running = false;
  831. priv->paused = false;
  832. #ifndef CONFIG_AUDIO_EXCLUDE_STOP
  833. priv->terminating = false;
  834. #endif
  835. priv->reserved = true;
  836. }
  837. wm8776_givesem(&priv->pendsem);
  838. return ret;
  839. }
  840. /****************************************************************************
  841. * Name: wm8776_release
  842. *
  843. * Description: Releases the session (the only one we have).
  844. *
  845. ****************************************************************************/
  846. #ifdef CONFIG_AUDIO_MULTI_SESSION
  847. static int wm8776_release(FAR struct audio_lowerhalf_s *dev,
  848. FAR void *session)
  849. #else
  850. static int wm8776_release(FAR struct audio_lowerhalf_s *dev)
  851. #endif
  852. {
  853. FAR struct wm8776_dev_s *priv = (FAR struct wm8776_dev_s *)dev;
  854. void *value;
  855. /* Join any old worker thread we had created to prevent a memory leak */
  856. if (priv->threadid != 0)
  857. {
  858. pthread_join(priv->threadid, &value);
  859. priv->threadid = 0;
  860. }
  861. /* Borrow the APBQ semaphore for thread sync */
  862. wm8776_takesem(&priv->pendsem);
  863. /* Really we should free any queued buffers here */
  864. priv->reserved = false;
  865. wm8776_givesem(&priv->pendsem);
  866. return OK;
  867. }
  868. /****************************************************************************
  869. * Name: wm8776_audio_output
  870. *
  871. * Description:
  872. * Initialize and configure the WM8776 device as an audio output device.
  873. *
  874. * Input Parameters:
  875. * priv - A reference to the driver state structure
  876. *
  877. * Returned Value:
  878. * None. No failures are detected.
  879. *
  880. ****************************************************************************/
  881. static void wm8776_audio_output(FAR struct wm8776_dev_s *priv)
  882. {
  883. wm8776_writereg(priv, WM8776_MASTER_ATT, WM8776_UPDATE | 0x58); /* -33db */
  884. wm8776_writereg(priv, WM8776_DAC_IF, 0x32); /* 32bit, I2S, standard pol */
  885. #ifdef CONFIG_WM8776_SWAP_HPOUT
  886. wm8776_writereg(priv, WM8776_DAC_CC, 0x62); /* Swap HPOUT L/R */
  887. #endif
  888. wm8776_writereg(priv, WM8776_MASTER_MODE, 0x00); /* slave mode, 128fs */
  889. wm8776_writereg(priv, WM8776_PWR_DOWN, 0x12); /* AINPD, ADCPD */
  890. }
  891. /****************************************************************************
  892. * Name: wm8776_hw_reset
  893. *
  894. * Description:
  895. * Reset and re-initialize the WM8776
  896. *
  897. * Input Parameters:
  898. * priv - A reference to the driver state structure
  899. *
  900. * Returned Value:
  901. * None
  902. *
  903. ****************************************************************************/
  904. static void wm8776_hw_reset(FAR struct wm8776_dev_s *priv)
  905. {
  906. /* Put audio output back to its initial configuration */
  907. priv->samprate = WM8776_DEFAULT_SAMPRATE;
  908. priv->nchannels = WM8776_DEFAULT_NCHANNELS;
  909. priv->bpsamp = WM8776_DEFAULT_BPSAMP;
  910. #if !defined(CONFIG_AUDIO_EXCLUDE_VOLUME) && !defined(CONFIG_AUDIO_EXCLUDE_BALANCE)
  911. priv->balance = b16HALF; /* Center balance */
  912. #endif
  913. /* Software reset. This puts all WM8776 registers back in their
  914. * default state.
  915. */
  916. wm8776_writereg(priv, WM8776_SOFT_RESET, 0x00);
  917. /* Configure the WM8776 hardware as an audio input device */
  918. wm8776_audio_output(priv);
  919. }
  920. /****************************************************************************
  921. * Name: wm8776_workerthread
  922. *
  923. * This is the thread that feeds data to the chip and keeps the audio
  924. * stream going.
  925. *
  926. ****************************************************************************/
  927. static void *wm8776_workerthread(pthread_addr_t pvarg)
  928. {
  929. FAR struct wm8776_dev_s *priv = (struct wm8776_dev_s *) pvarg;
  930. struct audio_msg_s msg;
  931. FAR struct ap_buffer_s *apb;
  932. int msglen;
  933. unsigned int prio;
  934. struct mq_attr attr;
  935. audinfo("Entry\n");
  936. #ifndef CONFIG_AUDIO_EXCLUDE_STOP
  937. priv->terminating = false;
  938. #endif
  939. priv->running = true;
  940. wm8776_setvolume(priv, priv->volume, false);
  941. /* Loop as long as we are supposed to be running and as long as we have
  942. * buffers in-flight.
  943. */
  944. while (priv->running || priv->inflight > 0)
  945. {
  946. /* Check if we have been asked to terminate. We have to check if we
  947. * still have buffers in-flight. If we do, then we can't stop until
  948. * birds come back to roost.
  949. */
  950. if (priv->terminating && priv->inflight <= 0)
  951. {
  952. /* We are IDLE. Break out of the loop and exit. */
  953. break;
  954. }
  955. else
  956. {
  957. /* Check if we can send more audio buffers to the WM8776 */
  958. wm8776_sendbuffer(priv);
  959. }
  960. repeat:
  961. /* Wait for messages from our message queue */
  962. msglen = mq_receive(priv->mq, (FAR char *)&msg, sizeof(msg), &prio);
  963. /* Handle the case when we return with no message */
  964. if (msglen < sizeof(struct audio_msg_s))
  965. {
  966. auderr("ERROR: Message too small: %d\n", msglen);
  967. continue;
  968. }
  969. /* Process the message */
  970. switch (msg.msgId)
  971. {
  972. /* The ISR has requested more data. We will catch this case at
  973. * the top of the loop.
  974. */
  975. case AUDIO_MSG_DATA_REQUEST:
  976. audinfo("AUDIO_MSG_DATA_REQUEST\n");
  977. break;
  978. /* Stop the playback */
  979. #ifndef CONFIG_AUDIO_EXCLUDE_STOP
  980. case AUDIO_MSG_STOP:
  981. /* Indicate that we are terminating */
  982. audinfo("AUDIO_MSG_STOP: Terminating\n");
  983. priv->terminating = true;
  984. break;
  985. #endif
  986. /* We have a new buffer to send. We will catch this case at
  987. * the top of the loop.
  988. */
  989. case AUDIO_MSG_ENQUEUE:
  990. audinfo("AUDIO_MSG_ENQUEUE\n");
  991. break;
  992. /* We will wake up from the I2S callback with this message */
  993. case AUDIO_MSG_COMPLETE:
  994. audinfo("AUDIO_MSG_COMPLETE\n");
  995. wm8776_returnbuffers(priv);
  996. break;
  997. default:
  998. auderr("ERROR: Ignoring message ID %d\n", msg.msgId);
  999. break;
  1000. }
  1001. (void)mq_getattr(priv->mq, &attr);
  1002. /* If there is a message in the queue, process it */
  1003. if (0 < attr.mq_curmsgs)
  1004. {
  1005. goto repeat;
  1006. }
  1007. }
  1008. /* Reset the WM8776 hardware */
  1009. wm8776_hw_reset(priv);
  1010. /* Return any pending buffers in our pending queue */
  1011. wm8776_takesem(&priv->pendsem);
  1012. while ((apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq)) != NULL)
  1013. {
  1014. /* Release our reference to the buffer */
  1015. apb_free(apb);
  1016. /* Send the buffer back up to the previous level. */
  1017. #ifdef CONFIG_AUDIO_MULTI_SESSION
  1018. priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK, NULL);
  1019. #else
  1020. priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK);
  1021. #endif
  1022. }
  1023. wm8776_givesem(&priv->pendsem);
  1024. /* Return any pending buffers in our done queue */
  1025. wm8776_returnbuffers(priv);
  1026. /* Close the message queue */
  1027. mq_close(priv->mq);
  1028. mq_unlink(priv->mqname);
  1029. priv->mq = NULL;
  1030. /* Send an AUDIO_MSG_COMPLETE message to the client */
  1031. #ifdef CONFIG_AUDIO_MULTI_SESSION
  1032. priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK, NULL);
  1033. #else
  1034. priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK);
  1035. #endif
  1036. audinfo("Exit\n");
  1037. return NULL;
  1038. }
  1039. /****************************************************************************
  1040. * Public Functions
  1041. ****************************************************************************/
  1042. /****************************************************************************
  1043. * Name: wm8776_initialize
  1044. *
  1045. * Description:
  1046. * Initialize the WM8776 device.
  1047. *
  1048. * Input Parameters:
  1049. * i2c - An I2C driver instance
  1050. * i2s - An I2S driver instance
  1051. * lower - Persistent board configuration data
  1052. *
  1053. * Returned Value:
  1054. * A new lower half audio interface for the WM8776 device is returned on
  1055. * success; NULL is returned on failure.
  1056. *
  1057. ****************************************************************************/
  1058. FAR struct audio_lowerhalf_s *
  1059. wm8776_initialize(FAR struct i2c_master_s *i2c,
  1060. FAR struct i2s_dev_s *i2s,
  1061. FAR const struct wm8776_lower_s *lower)
  1062. {
  1063. FAR struct wm8776_dev_s *priv;
  1064. /* Sanity check */
  1065. DEBUGASSERT(i2c && i2s && lower);
  1066. /* Allocate a WM8776 device structure */
  1067. priv = (FAR struct wm8776_dev_s *)kmm_zalloc(sizeof(struct wm8776_dev_s));
  1068. if (priv)
  1069. {
  1070. priv->dev.ops = &g_audioops;
  1071. priv->lower = lower;
  1072. priv->i2c = i2c;
  1073. priv->i2s = i2s;
  1074. nxsem_init(&priv->pendsem, 0, 1);
  1075. dq_init(&priv->pendq);
  1076. dq_init(&priv->doneq);
  1077. /* Reset and reconfigure the WM8776 hardwaqre */
  1078. wm8776_hw_reset(priv);
  1079. return &priv->dev;
  1080. }
  1081. return NULL;
  1082. }