****************************************************************************/
#include <tinyara/config.h>
-
#include <sys/types.h>
#include <sys/ioctl.h>
-
#include <stdint.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <fixedmath.h>
-#include <queue.h>
#include <debug.h>
-
#include <tinyara/irq.h>
#include <tinyara/kmalloc.h>
#include <tinyara/clock.h>
};
/****************************************************************************
- * Private Functions
+ * Name: delay
+ *
+ * Description
+ * Delay in ms.
****************************************************************************/
-
static void delay(unsigned int mS)
{
volatile systime_t start = clock_systimer();
-
mS = mS / MSEC_PER_TICK + 1;
- while (1)
- if ((start + mS) < clock_systimer()) {
+ while (1) {
+ if ((start + mS) < clock_systimer())
return;
- }
+ }
}
/****************************************************************************
* Read the specified 16-bit register from the ALC5658 device.
*
****************************************************************************/
-
-#if !defined(CONFIG_ALC5658_REGDUMP) && !defined(CONFIG_ALC5658_CLKDEBUG)
-static
-#endif
uint16_t alc5658_readreg(FAR struct alc5658_dev_s *priv, uint16_t regaddr)
{
* Write the specified 16-bit register to the ALC5658 device.
*
************************************************************************************/
-
static void alc5658_writereg(FAR struct alc5658_dev_s *priv, uint16_t regaddr, uint16_t regval)
{
int32_t ret;
return;
}
+/************************************************************************************
+ * Name: alc5658_modifyreg
+ *
+ * Description:
+ * Modify the specified 16-bit register to the ALC5658 device.
+ *
+ ************************************************************************************/
static uint16_t alc5658_modifyreg(FAR struct alc5658_dev_s *priv, uint16_t regaddr, uint16_t set, uint16_t clear)
{
uint16_t data;
return alc5658_readreg(priv, regaddr);
}
+/************************************************************************************
+ * Name: alc5658_exec_i2c_script
+ *
+ * Description:
+ * Executes given script through i2c to configuure ALC5658 device.
+ *
+ ************************************************************************************/
static void alc5658_exec_i2c_script(FAR struct alc5658_dev_s *priv, t_codec_init_script_entry *script, uint32_t size)
{
uint32_t i;
* by a signal.
*
************************************************************************************/
-
static void alc5658_takesem(sem_t *sem)
{
int ret;
* volume and balance settings.
*
************************************************************************************/
-
#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
static inline uint16_t alc5658_scalevolume(uint16_t volume, b16_t scale)
{
* volume and balance settings.
*
************************************************************************************/
-
#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
static void alc5658_setvolume(FAR struct alc5658_dev_s *priv, uint16_t volume, bool mute)
{
- audvdbg("volume=%u mute=%u\n", volume, mute);
-
+ audvdbg(" alc5658_setvolume volume=%u mute=%u\n", volume, mute);
/* ADD VOLUME CODE HERE */
/* Remember the volume level and mute settings */
-
+ alc5658_takesem(&priv->devsem);
priv->volume = volume;
priv->mute = mute;
+ alc5658_givesem(&priv->devsem);
}
#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
* The level and range are in whole percentage levels (0-100).
*
************************************************************************************/
-
#ifndef CONFIG_AUDIO_EXCLUDE_TONE
static void alc5658_setbass(FAR struct alc5658_dev_s *priv, uint8_t bass)
{
* The level and range are in whole percentage levels (0-100).
*
************************************************************************************/
-
#ifndef CONFIG_AUDIO_EXCLUDE_TONE
static void alc5658_settreble(FAR struct alc5658_dev_s *priv, uint8_t treble)
{
#endif /* CONFIG_AUDIO_EXCLUDE_TONE */
/****************************************************************************
- * Name: alc5658_setdatawidth
+ * Name: alc5658_set_i2s_datawidth
*
* Description:
* Set the 8- 16- 24- bit data modes
*
****************************************************************************/
-
-static void alc5658_setdatawidth(FAR struct alc5658_dev_s *priv)
+static void alc5658_set_i2s_datawidth(FAR struct alc5658_dev_s *priv)
{
+ if (priv->inout)
+ I2S_RXDATAWIDTH(priv->i2s, priv->bpsamp);
+ else
+ I2S_TXDATAWIDTH(priv->i2s, priv->bpsamp);
}
/****************************************************************************
- * Name: alc5658_setbitrate
+ * Name: alc5658_set_i2s_samplerate
*
* Description:
*
****************************************************************************/
-
-static void alc5658_setbitrate(FAR struct alc5658_dev_s *priv)
+static void alc5658_set_i2s_samplerate(FAR struct alc5658_dev_s *priv)
{
-
+ if (priv->inout)
+ I2S_RXSAMPLERATE(priv->i2s, priv->samprate);
+ else
+ I2S_TXSAMPLERATE(priv->i2s, priv->samprate);
}
/****************************************************************************
* Get the audio device capabilities
*
****************************************************************************/
-
static int alc5658_getcaps(FAR struct audio_lowerhalf_s *dev, int type, FAR struct audio_caps_s *caps)
{
/* Validate the structure */
*/
/* The types of audio units we implement */
-
- caps->ac_controls.b[0] = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_FEATURE | AUDIO_TYPE_PROCESSING;
+ caps->ac_controls.b[0] = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_OUTPUT | AUDIO_TYPE_FEATURE | AUDIO_TYPE_PROCESSING;
break;
* Configure the audio device for the specified mode of operation.
*
****************************************************************************/
-
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int alc5658_configure(FAR struct audio_lowerhalf_s *dev, FAR void *session, FAR const struct audio_caps_s *caps)
#else
DEBUGASSERT(priv && caps);
audvdbg("ac_type: %d\n", caps->ac_type);
- /* Process the configure operation */
+ /* ALC5658 supports on the fly changes for almost all changes
+ so no need to do anything. But if any issue, worth looking here */
switch (caps->ac_type) {
case AUDIO_TYPE_FEATURE:
audvdbg(" AUDIO_TYPE_FEATURE\n");
- /* Process based on Feature Unit */
-
+ /* Inner swich case: Process based on Feature Unit */
switch (caps->ac_format.hw) {
#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
case AUDIO_FU_VOLUME: {
#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
#ifndef CONFIG_AUDIO_EXCLUDE_TONE
- case AUDIO_FU_BASS: {
- /* Set the bass. The percentage level (0-100) is in the
- * ac_controls.b[0] parameter.
- */
+ case AUDIO_FU_BASS:
+ { /* Set the bass. The percentage level (0-100) is in the
+ * ac_controls.b[0] parameter. */
uint8_t bass = caps->ac_controls.b[0];
audvdbg(" Bass: %d\n", bass);
-
- if (bass <= 100) {
+ if (bass <= 100)
alc5658_setbass(priv, bass);
- } else {
- ret = -EDOM;
- }
}
break;
-
- case AUDIO_FU_TREBLE: {
- /* Set the treble. The percentage level (0-100) is in the
- * ac_controls.b[0] parameter.
- */
+ case AUDIO_FU_TREBLE:
+ { /* Set the treble. The percentage level (0-100) is in the
+ * ac_controls.b[0] parameter. */
uint8_t treble = caps->ac_controls.b[0];
audvdbg(" Treble: %d\n", treble);
-
- if (treble <= 100) {
+ if (treble <= 100)
alc5658_settreble(priv, treble);
- } else {
- ret = -EDOM;
- }
}
break;
#endif /* CONFIG_AUDIO_EXCLUDE_TONE */
-
default:
auddbg(" ERROR: Unrecognized feature unit\n");
- ret = -ENOTTY;
break;
}
- break;
+ break; /* Break for inner switch case */
+ case AUDIO_TYPE_INPUT: {
+ audvdbg(" AUDIO_TYPE_OUTPUT:\n");
+ /* Verify that all of the requested values are supported */
+
+ ret = -ERANGE;
+ if (caps->ac_channels != 1 && caps->ac_channels != 2) {
+ auddbg("ERROR: Unsupported number of channels: %d\n", caps->ac_channels);
+ break;
+ }
+ if (caps->ac_controls.b[2] != 8 && caps->ac_controls.b[2] != 16) {
+ auddbg("ERROR: Unsupported bits per sample: %d\n", caps->ac_controls.b[2]);
+ break;
+ }
+
+ /* Save the current stream configuration */
+
+ priv->samprate = caps->ac_controls.hw[0];
+ priv->nchannels = caps->ac_channels;
+ priv->bpsamp = caps->ac_controls.b[2];
+
+ audvdbg(" Number of channels: 0x%x\n", priv->nchannels);
+ audvdbg(" Sample rate: 0x%x\n", priv->samprate);
+ audvdbg(" Sample width: 0x%x\n", priv->bpsamp);
+
+ /* Reconfigure the FLL to support the resulting number or channels,
+ * bits per sample, and bitrate.
+ */
+#if 0
+ alc5658_set_i2s_datawidth(priv);
+ alc5658_set_i2s_samplerate(priv);
+
+ alc5658_clock_analysis(&priv->dev, "AUDIO_TYPE_OUTPUT");
+#endif
+ priv->inout = true;
+ ret = OK;
+ }
case AUDIO_TYPE_OUTPUT: {
audvdbg(" AUDIO_TYPE_OUTPUT:\n");
- audvdbg(" Number of channels: %u\n", caps->ac_channels);
- audvdbg(" Sample rate: %u\n", caps->ac_controls.hw[0]);
- audvdbg(" Sample width: %u\n", caps->ac_controls.b[2]);
-
/* Verify that all of the requested values are supported */
ret = -ERANGE;
priv->nchannels = caps->ac_channels;
priv->bpsamp = caps->ac_controls.b[2];
+ audvdbg(" Number of channels: 0x%x\n", priv->nchannels);
+ audvdbg(" Sample rate: 0x%x\n", priv->samprate);
+ audvdbg(" Sample width: 0x%x\n", priv->bpsamp);
+
/* Reconfigure the FLL to support the resulting number or channels,
* bits per sample, and bitrate.
*/
-
- alc5658_setdatawidth(priv);
- alc5658_setbitrate(priv);
+#if 0
+ alc5658_set_i2s_datawidth(priv);
+ alc5658_set_i2s_samplerate(priv);
alc5658_clock_analysis(&priv->dev, "AUDIO_TYPE_OUTPUT");
+#endif
ret = OK;
+ priv->inout = false;
}
break;
* Shutdown the ALC5658 chip and put it in the lowest power state possible.
*
****************************************************************************/
-
static int alc5658_shutdown(FAR struct audio_lowerhalf_s *dev)
{
FAR struct alc5658_dev_s *priv = (FAR struct alc5658_dev_s *)dev;
}
/****************************************************************************
- * Name: alc5658_senddone
- *
- * Description:
- * This is the I2S callback function that is invoked when the transfer
- * completes.
- *
- ****************************************************************************/
-
-static void alc5658_senddone(FAR struct i2s_dev_s *i2s, FAR struct ap_buffer_s *apb, FAR void *arg, int result)
-{
- FAR struct alc5658_dev_s *priv = (FAR struct alc5658_dev_s *)arg;
- struct audio_msg_s msg;
- irqstate_t flags;
- int ret;
-
- DEBUGASSERT(i2s && priv && priv->running && apb);
- audvdbg("apb=%p inflight=%d result=%d\n", apb, priv->inflight, result);
-
- /* We do not place any restriction on the context in which this function
- * is called. It may be called from an interrupt handler. Therefore, the
- * doneq and in-flight values might be accessed from the interrupt level.
- * Not the best design. But we will use interrupt controls to protect
- * against that possibility.
- */
-
- flags = irqsave();
-
- /* Add the completed buffer to the end of our doneq. We do not yet
- * decrement the reference count.
- */
-
- dq_addlast((FAR dq_entry_t *) apb, &priv->doneq);
-
- /* And decrement the number of buffers in-flight */
-
- DEBUGASSERT(priv->inflight > 0);
- priv->inflight--;
-
- /* Save the result of the transfer */
- /* REVISIT: This can be overwritten */
-
- priv->result = result;
- irqrestore(flags);
-
- /* Now send a message to the worker thread, informing it that there are
- * buffers in the done queue that need to be cleaned up.
- */
-
- msg.msgId = AUDIO_MSG_COMPLETE;
- ret = mq_send(priv->mq, (FAR const char *)&msg, sizeof(msg), CONFIG_ALC5658_MSG_PRIO);
- if (ret < 0) {
- auddbg("ERROR: mq_send failed: %d\n", errno);
- }
-}
-
-/****************************************************************************
- * Name: alc5658_returnbuffers
- *
- * Description:
- * This function is called after the complete of one or more data
- * transfers. This function will empty the done queue and release our
- * reference to each buffer.
- *
- ****************************************************************************/
-
-static void alc5658_returnbuffers(FAR struct alc5658_dev_s *priv)
-{
- FAR struct ap_buffer_s *apb;
- irqstate_t flags;
-
- /* The doneq and in-flight values might be accessed from the interrupt
- * level in some implementations. Not the best design. But we will
- * use interrupt controls to protect against that possibility.
- */
-
- flags = irqsave();
- while (dq_peek(&priv->doneq) != NULL) {
- /* Take the next buffer from the queue of completed transfers */
-
- apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->doneq);
- irqrestore(flags);
-
- audvdbg("Returning: apb=%p curbyte=%d nbytes=%d flags=%04x\n", apb, apb->curbyte, apb->nbytes, apb->flags);
-
- /* Are we returning the final buffer in the stream? */
-
- if ((apb->flags & AUDIO_APB_FINAL) != 0) {
- /* Both the pending and the done queues should be empty and there
- * should be no buffers in-flight.
- */
-
- DEBUGASSERT(dq_empty(&priv->doneq) && dq_empty(&priv->pendq) && priv->inflight == 0);
-
- /* Set the terminating flag. This will, eventually, cause the
- * worker thread to exit (if it is not already terminating).
- */
-
- audvdbg("Terminating\n");
- priv->terminating = true;
- }
-
- /* Release our reference to the audio buffer */
-
- apb_free(apb);
-
- /* Send the buffer back up to the previous level. */
-
-#ifdef CONFIG_AUDIO_MULTI_SESSION
- priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK, NULL);
-#else
- priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK);
-#endif
- flags = irqsave();
- }
-
- irqrestore(flags);
-}
-
-/****************************************************************************
- * Name: alc5658_sendbuffer
- *
- * Description:
- * Start the transfer an audio buffer to the ALC5658 via I2S. This
- * will not wait for the transfer to complete but will return immediately.
- * the alc5658 called will be invoked when the transfer
- * completes, stimulating the worker thread to call this function again.
- *
- ****************************************************************************/
-
-static int alc5658_sendbuffer(FAR struct alc5658_dev_s *priv)
-{
- FAR struct ap_buffer_s *apb;
- irqstate_t flags;
- uint32_t timeout;
- int shift;
- int ret = OK;
-
- /* Loop while there are audio buffers to be sent and we have few than
- * CONFIG_ALC5658_INFLIGHT then "in-flight"
- *
- * The 'inflight' value might be modified from the interrupt level in some
- * implementations. We will use interrupt controls to protect against
- * that possibility.
- *
- * The 'pendq', on the other hand, is protected via a semaphore. Let's
- * hold the semaphore while we are busy here and disable the interrupts
- * only while accessing 'inflight'.
- */
-
- alc5658_takesem(&priv->pendsem);
- while (priv->inflight < CONFIG_ALC5658_INFLIGHT && dq_peek(&priv->pendq) != NULL && !priv->paused) {
- /* Take next buffer from the queue of pending transfers */
-
- apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq);
- audvdbg("Sending apb=%p, size=%d inflight=%d\n", apb, apb->nbytes, priv->inflight);
-
- /* Increment the number of buffers in-flight before sending in order
- * to avoid a possible race condition.
- */
-
- flags = irqsave();
- priv->inflight++;
- irqrestore(flags);
-
- /* Send the entire audio buffer via I2S. What is a reasonable timeout
- * to use? This would depend on the bit rate and size of the buffer.
- *
- * Samples in the buffer (samples):
- * = buffer_size * 8 / bpsamp samples
- * Sample rate (samples/second):
- * = samplerate * nchannels
- * Expected transfer time (seconds):
- * = (buffer_size * 8) / bpsamp / samplerate / nchannels
- *
- * We will set the timeout about twice that.
- *
- * NOTES:
- * - The multiplier of 8 becomes 16000 for 2x and units of
- * milliseconds.
- * - 16000 is a approximately 16384 (1 << 14), bpsamp is either
- * (1 << 3) or (1 << 4), and nchannels is either (1 << 0) or
- * (1 << 1). So this can be simplifies to (milliseconds):
- *
- * = (buffer_size << shift) / samplerate
- */
-
- shift = (priv->bpsamp == 8) ? 14 - 3 : 14 - 4;
- shift -= (priv->nchannels > 1) ? 1 : 0;
-
- timeout = MSEC2TICK(((uint32_t)(apb->nbytes - apb->curbyte) << shift) / (uint32_t) priv->samprate);
-
- ret = I2S_SEND(priv->i2s, apb, alc5658_senddone, priv, timeout);
- if (ret < 0) {
- auddbg("ERROR: I2S_SEND failed: %d\n", ret);
- break;
- }
- }
-
- alc5658_givesem(&priv->pendsem);
- return ret;
-}
-
-/****************************************************************************
* Name: alc5658_start
*
* Description:
static int alc5658_start(FAR struct audio_lowerhalf_s *dev)
#endif
{
+ audvdbg(" alc5658_start Entry\n");
FAR struct alc5658_dev_s *priv = (FAR struct alc5658_dev_s *)dev;
- struct sched_param sparam;
- struct mq_attr attr;
- pthread_attr_t tattr;
- FAR void *value;
- int ret;
-
- audvdbg("Entry\n");
+ if (priv->running)
+ return OK;
+
+ /* Fix me -- Need to support multiple samplerate */
+ alc5658_exec_i2c_script(priv, codec_init_pll_16K, sizeof(codec_init_pll_16K) / sizeof(t_codec_init_script_entry));
+ alc5658_exec_i2c_script(priv, codec_init_inout_script2, sizeof(codec_init_inout_script2) / sizeof(t_codec_init_script_entry));
+
+ priv->running = 1;
+
+ dq_entry_t *tmp = NULL;
+ dq_queue_t * q = &priv->pendq;
+
+ alc5658_takesem(&priv->devsem);
+ for (tmp = dq_peek(q); tmp; tmp = dq_next(tmp)) {
+ alc5658_enqueuebuffer(dev, (struct ap_buffer_s *) tmp);
+ }
+ alc5658_givesem(&priv->devsem);
+
/* Exit reduced power modes of operation */
/* REVISIT */
- /* Create a message queue for the worker thread */
-
- snprintf(priv->mqname, sizeof(priv->mqname), "/tmp/%X", priv);
-
- attr.mq_maxmsg = 16;
- attr.mq_msgsize = sizeof(struct audio_msg_s);
- attr.mq_curmsgs = 0;
- attr.mq_flags = 0;
-
- priv->mq = mq_open(priv->mqname, O_RDWR | O_CREAT, 0644, &attr);
- if (priv->mq == NULL) {
- /* Error creating message queue! */
-
- auddbg("ERROR: Couldn't allocate message queue\n");
- return -ENOMEM;
- }
-
- /* Join any old worker thread we had created to prevent a memory leak */
-
- if (priv->threadid != 0) {
- audvdbg("Joining old thread\n");
- pthread_join(priv->threadid, &value);
- }
-
- /* Start our thread for sending data to the device */
-
- pthread_attr_init(&tattr);
- sparam.sched_priority = sched_get_priority_max(SCHED_FIFO) - 3;
- (void)pthread_attr_setschedparam(&tattr, &sparam);
- (void)pthread_attr_setstacksize(&tattr, CONFIG_ALC5658_WORKER_STACKSIZE);
-
- audvdbg("Starting worker thread\n");
- ret = pthread_create(&priv->threadid, &tattr, alc5658_workerthread, (pthread_addr_t) priv);
- if (ret != OK) {
- auddbg("ERROR: pthread_create failed: %d\n", ret);
- } else {
- pthread_setname_np(priv->threadid, "alc5658");
- audvdbg("Created worker thread\n");
- }
-
- return ret;
+ return OK; /* Fix this -- always returns OK */
}
/****************************************************************************
#endif
{
FAR struct alc5658_dev_s *priv = (FAR struct alc5658_dev_s *)dev;
- struct audio_msg_s term_msg;
- FAR void *value;
-
- /* Send a message to stop all audio streaming */
- term_msg.msgId = AUDIO_MSG_STOP;
- term_msg.u.data = 0;
- mq_send(priv->mq, (FAR const char *)&term_msg, sizeof(term_msg), CONFIG_ALC5658_MSG_PRIO);
-
- /* Join the worker thread */
-
- pthread_join(priv->threadid, &value);
- priv->threadid = 0;
+ /* Need to run the stop script here */
+ alc5658_exec_i2c_script(priv, codec_stop_script, sizeof(codec_stop_script) / sizeof(t_codec_init_script_entry));
/* Enter into a reduced power usage mode */
/* REVISIT: */
* Description: Pauses the playback.
*
****************************************************************************/
-
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int alc5658_pause(FAR struct audio_lowerhalf_s *dev, FAR void *session)
priv->paused = true;
alc5658_setvolume(priv, priv->volume, true);
- ALC5658_DISABLE(priv->lower);
+ ALC5658_DISABLE(priv->lower); /* Need inputs from REALTEK */
}
- return OK;
+return OK;
}
#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
* Description: Resumes the playback.
*
****************************************************************************/
-
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int alc5658_resume(FAR struct audio_lowerhalf_s *dev, FAR void *session)
alc5658_setvolume(priv, priv->volume, false);
/* Enable interrupts to allow sampling data */
-
- alc5658_sendbuffer(priv);
+ /* Need resume logic later. Need to know if alc5658 dma can be paused and resumed */
#ifdef ALC5658_USE_FFLOCK_INT
ALC5658_ENABLE(priv->lower);
#endif
#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
/****************************************************************************
+ * Name: alc5658_rxcallback
+ *
+ * Description: Called when I2S filled a buffer. No recycling mechanism now.
+ *
+ ****************************************************************************/
+
+static void alc5658_rxtxcallback(FAR struct i2s_dev_s *dev, FAR struct ap_buffer_s *apb, FAR void *arg, int result)
+{
+ FAR struct alc5658_dev_s *priv = (FAR struct alc5658_dev_s *)arg;
+
+ DEBUGASSERT(priv && apb);
+ audvdbg("alc5658_rxcallback, devaddr= 0x%x, apbaddr =0x%x\n", dev, apb);
+
+ /* Call upper callback, let it post msg to user q */
+ priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK);
+
+ alc5658_takesem(&priv->devsem);
+
+ dq_entry_t *tmp;
+ for (tmp = (dq_entry_t*)dq_peek(&priv->pendq); tmp; tmp = dq_next(tmp)) {
+ if (tmp == (dq_entry_t*)apb) {
+ dq_rem(tmp, &priv->pendq);
+ audvdbg("found the apb to remove 0x%x\n", tmp);
+ break;
+ }
+ }
+
+ alc5658_givesem(&priv->devsem);
+}
+
+/****************************************************************************
* Name: alc5658_enqueuebuffer
*
* Description: Enqueue an Audio Pipeline Buffer for playback/ processing.
*
****************************************************************************/
-
static int alc5658_enqueuebuffer(FAR struct audio_lowerhalf_s *dev, FAR struct ap_buffer_s *apb)
{
FAR struct alc5658_dev_s *priv = (FAR struct alc5658_dev_s *)dev;
- struct audio_msg_s term_msg;
int ret;
- audvdbg("Enqueueing: apb=%p curbyte=%d nbytes=%d flags=%04x\n", apb, apb->curbyte, apb->nbytes, apb->flags);
-
- /* Take a reference on the new audio buffer */
-
- apb_reference(apb);
-
- /* Add the new buffer to the tail of pending audio buffers */
-
- alc5658_takesem(&priv->pendsem);
- apb->flags |= AUDIO_APB_OUTPUT_ENQUEUED;
- dq_addlast(&apb->dq_entry, &priv->pendq);
- alc5658_givesem(&priv->pendsem);
+ audvdbg("alc5658_enqueuebuffer: apbadr = 0x%x\n", apb);
- /* Send a message to the worker thread indicating that a new buffer has been
- * enqueued. If mq is NULL, then the playing has not yet started. In that
- * case we are just "priming the pump" and we don't need to send any message.
- */
-
- ret = OK;
- if (priv->mq != NULL) {
- term_msg.msgId = AUDIO_MSG_ENQUEUE;
- term_msg.u.data = 0;
-
- ret = mq_send(priv->mq, (FAR const char *)&term_msg, sizeof(term_msg), CONFIG_ALC5658_MSG_PRIO);
- if (ret < 0) {
- int errcode = errno;
- DEBUGASSERT(errcode > 0);
+ /* Need to fix later */
+ if (!priv->running) {
+ apb_reference(apb);
- auddbg("ERROR: mq_send failed: %d\n", errcode);
- UNUSED(errcode);
- }
+ /* Add the new buffer to the tail of pending audio buffers */
+ alc5658_takesem(&priv->devsem);
+ dq_addlast(&apb->dq_entry, &priv->pendq);
+ alc5658_givesem(&priv->devsem);
+ return OK;
}
-
+
+ if (priv->inout) /* record */
+ ret = I2S_RECEIVE(priv->i2s, apb, alc5658_rxtxcallback, priv, 0);
+ else /* playback */
+ ret = I2S_SEND(priv->i2s, apb, alc5658_rxtxcallback, priv, 0);
+
+ audvdbg("I2s returned 0x%x\n", ret);
+
return ret;
}
* Description: Called when an enqueued buffer is being cancelled.
*
****************************************************************************/
-
static int alc5658_cancelbuffer(FAR struct audio_lowerhalf_s *dev, FAR struct ap_buffer_s *apb)
{
audvdbg("apb=%p\n", apb);
+ /* Need to add logic here */
return OK;
}
* Description: Perform a device ioctl
*
****************************************************************************/
-
static int alc5658_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd, unsigned long arg)
{
#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
* Description: Reserves a session (the only one we have).
*
****************************************************************************/
-
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int alc5658_reserve(FAR struct audio_lowerhalf_s *dev, FAR void **session)
#else
/* Borrow the APBQ semaphore for thread sync */
- alc5658_takesem(&priv->pendsem);
+ alc5658_takesem(&priv->devsem);
if (priv->reserved) {
ret = -EBUSY;
} else {
priv->reserved = true;
}
- alc5658_givesem(&priv->pendsem);
+ alc5658_givesem(&priv->devsem);
return ret;
}
* Description: Releases the session (the only one we have).
*
****************************************************************************/
-
#ifdef CONFIG_AUDIO_MULTI_SESSION
static int alc5658_release(FAR struct audio_lowerhalf_s *dev, FAR void *session)
#else
#endif
{
FAR struct alc5658_dev_s *priv = (FAR struct alc5658_dev_s *)dev;
- void *value;
-
- /* Join any old worker thread we had created to prevent a memory leak */
-
- if (priv->threadid != 0) {
- pthread_join(priv->threadid, &value);
- priv->threadid = 0;
- }
-
- /* Borrow the APBQ semaphore for thread sync */
-
- alc5658_takesem(&priv->pendsem);
-
- /* Really we should free any queued buffers here */
+ alc5658_takesem(&priv->devsem);
priv->reserved = false;
- alc5658_givesem(&priv->pendsem);
+ alc5658_givesem(&priv->devsem);
return OK;
}
* ALC5658 interrupts were disabled in the interrupt handler.
*
****************************************************************************/
-
#ifdef ALC5658_USE_FFLOCK_INT
static void alc5658_interrupt_work(FAR void *arg)
{
#endif
/****************************************************************************
- * Name: alc5658_workerthread
- *
- * This is the thread that feeds data to the chip and keeps the audio
- * stream going.
- *
- ****************************************************************************/
-
-static void *alc5658_workerthread(pthread_addr_t pvarg)
-{
- FAR struct alc5658_dev_s *priv = (struct alc5658_dev_s *)pvarg;
- struct audio_msg_s msg;
- FAR struct ap_buffer_s *apb;
- int msglen;
- int prio;
-
- audvdbg("Entry\n");
-
-#ifndef CONFIG_AUDIO_EXCLUDE_STOP
- priv->terminating = false;
-#endif
-
- /* Mark ourself as running and make sure that ALC5658 interrupts are
- * enabled.
- */
-
- priv->running = true;
-#ifdef ALC5658_USE_FFLOCK_INT
- ALC5658_ENABLE(priv->lower);
-#endif
- alc5658_setvolume(priv, priv->volume, false);
-
- /* Loop as long as we are supposed to be running and as long as we have
- * buffers in-flight.
- */
-
- while (priv->running || priv->inflight > 0) {
- /* Check if we have been asked to terminate. We have to check if we
- * still have buffers in-flight. If we do, then we can't stop until
- * birds come back to roost.
- */
-
- if (priv->terminating && priv->inflight <= 0) {
- /* We are IDLE. Break out of the loop and exit. */
-
- break;
- } else {
- /* Check if we can send more audio buffers to the ALC5658 */
-
- alc5658_sendbuffer(priv);
- }
-
- /* Wait for messages from our message queue */
-
- msglen = mq_receive(priv->mq, (FAR char *)&msg, sizeof(msg), &prio);
-
- /* Handle the case when we return with no message */
-
- if (msglen < sizeof(struct audio_msg_s)) {
- auddbg("ERROR: Message too small: %d\n", msglen);
- continue;
- }
-
- /* Process the message */
-
- switch (msg.msgId) {
- /* The ISR has requested more data. We will catch this case at
- * the top of the loop.
- */
-
- case AUDIO_MSG_DATA_REQUEST:
- audvdbg("AUDIO_MSG_DATA_REQUEST\n");
- break;
-
- /* Stop the playback */
-
-#ifndef CONFIG_AUDIO_EXCLUDE_STOP
- case AUDIO_MSG_STOP:
- /* Indicate that we are terminating */
-
- audvdbg("AUDIO_MSG_STOP: Terminating\n");
- priv->terminating = true;
- break;
-#endif
-
- /* We have a new buffer to send. We will catch this case at
- * the top of the loop.
- */
-
- case AUDIO_MSG_ENQUEUE:
- audvdbg("AUDIO_MSG_ENQUEUE\n");
- break;
-
- /* We will wake up from the I2S callback with this message */
-
- case AUDIO_MSG_COMPLETE:
- audvdbg("AUDIO_MSG_COMPLETE\n");
- alc5658_returnbuffers(priv);
- break;
-
- default:
- auddbg("ERROR: Ignoring message ID %d\n", msg.msgId);
- break;
- }
- }
-
- /* Reset the ALC5658 hardware */
-
- alc5658_hw_reset(priv);
-
- /* Return any pending buffers in our pending queue */
-
- alc5658_takesem(&priv->pendsem);
- while ((apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq)) != NULL) {
- /* Release our reference to the buffer */
-
- apb_free(apb);
-
- /* Send the buffer back up to the previous level. */
-
-#ifdef CONFIG_AUDIO_MULTI_SESSION
- priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK, NULL);
-#else
- priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK);
-#endif
- }
-
- alc5658_givesem(&priv->pendsem);
-
- /* Return any pending buffers in our done queue */
-
- alc5658_returnbuffers(priv);
-
- /* Close the message queue */
-
- mq_close(priv->mq);
- mq_unlink(priv->mqname);
- priv->mq = NULL;
-
- /* Send an AUDIO_MSG_COMPLETE message to the client */
-
-#ifdef CONFIG_AUDIO_MULTI_SESSION
- priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK, NULL);
-#else
- priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK);
-#endif
-
- audvdbg("Exit\n");
- return NULL;
-}
-
-/****************************************************************************
* Name: alc5658_audio_output
*
* Description:
* None. No failures are detected.
*
****************************************************************************/
-
static void alc5658_audio_output(FAR struct alc5658_dev_s *priv)
{
- alc5658_exec_i2c_script(priv, codec_init_script, sizeof(codec_init_script) / sizeof(t_codec_init_script_entry));
+ alc5658_exec_i2c_script(priv, codec_init_out_script, sizeof(codec_init_out_script) / sizeof(t_codec_init_script_entry));
}
/****************************************************************************
* None. No failures are detected.
*
****************************************************************************/
-
static void alc5658_audio_input(FAR struct alc5658_dev_s *priv)
{
+ alc5658_exec_i2c_script(priv, codec_init_in_script, sizeof(codec_init_in_script) / sizeof(t_codec_init_script_entry));
}
/****************************************************************************
* None
*
****************************************************************************/
-
#ifdef ALC5658_USE_FFLOCK_INT
static void alc5658_configure_ints(FAR struct alc5658_dev_s *priv)
{
+ /* Missing */
}
#endif
* None
*
****************************************************************************/
-
static void alc5658_hw_reset(FAR struct alc5658_dev_s *priv)
{
/* Configure the FLL and the LRCLK */
- alc5658_setbitrate(priv);
+ alc5658_set_i2s_samplerate(priv);
/* Dump some information and return the device instance */
* success; NULL is returned on failure.
*
****************************************************************************/
-
FAR struct audio_lowerhalf_s *alc5658_initialize(FAR struct i2c_dev_s *i2c, FAR struct i2s_dev_s *i2s, FAR struct alc5658_lower_s *lower)
{
priv->i2c = i2c;
priv->i2s = i2s;
- sem_init(&priv->pendsem, 0, 1);
+ sem_init(&priv->devsem, 0, 1);
dq_init(&priv->pendq);
dq_init(&priv->doneq);
return NULL;
errout_with_dev:
- sem_destroy(&priv->pendsem);
+ sem_destroy(&priv->devsem);
kmm_free(priv);
return NULL;
}