From 6e3f6714b111f77dbd4ec5a85111d3af1bbedfbd Mon Sep 17 00:00:00 2001 From: bsvt Date: Wed, 13 Sep 2017 03:56:50 -0700 Subject: [PATCH] Remove incomplete worker thread in alc5658 --- os/drivers/audio/alc5658.c | 728 ++++++++++------------------------------- os/drivers/audio/alc5658.h | 26 +- os/include/tinyara/audio/i2s.h | 4 +- 3 files changed, 189 insertions(+), 569 deletions(-) diff --git a/os/drivers/audio/alc5658.c b/os/drivers/audio/alc5658.c index 7287351..8de592d 100644 --- a/os/drivers/audio/alc5658.c +++ b/os/drivers/audio/alc5658.c @@ -21,19 +21,15 @@ ****************************************************************************/ #include - #include #include - #include #include #include #include #include #include -#include #include - #include #include #include @@ -82,19 +78,20 @@ static const struct audio_ops_s g_audioops = { }; /**************************************************************************** - * 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; - } + } } /**************************************************************************** @@ -104,10 +101,6 @@ static void delay(unsigned int mS) * 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) { @@ -144,7 +137,6 @@ 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; @@ -167,6 +159,13 @@ static void alc5658_writereg(FAR struct alc5658_dev_s *priv, uint16_t regaddr, u 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; @@ -179,6 +178,13 @@ static uint16_t alc5658_modifyreg(FAR struct alc5658_dev_s *priv, uint16_t regad 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; @@ -198,7 +204,6 @@ static void alc5658_exec_i2c_script(FAR struct alc5658_dev_s *priv, t_codec_init * by a signal. * ************************************************************************************/ - static void alc5658_takesem(sem_t *sem) { int ret; @@ -217,7 +222,6 @@ static void alc5658_takesem(sem_t *sem) * volume and balance settings. * ************************************************************************************/ - #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME static inline uint16_t alc5658_scalevolume(uint16_t volume, b16_t scale) { @@ -233,19 +237,18 @@ 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 */ @@ -258,7 +261,6 @@ static void alc5658_setvolume(FAR struct alc5658_dev_s *priv, uint16_t volume, b * 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) { @@ -275,7 +277,6 @@ 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) { @@ -284,27 +285,32 @@ 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); } /**************************************************************************** @@ -314,7 +320,6 @@ static void alc5658_setbitrate(FAR struct alc5658_dev_s *priv) * 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 */ @@ -345,8 +350,7 @@ static int alc5658_getcaps(FAR struct audio_lowerhalf_s *dev, int type, FAR stru */ /* 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; @@ -461,7 +465,6 @@ static int alc5658_getcaps(FAR struct audio_lowerhalf_s *dev, int type, FAR stru * 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 @@ -476,14 +479,14 @@ static int alc5658_configure(FAR struct audio_lowerhalf_s *dev, FAR const struct 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: { @@ -504,52 +507,71 @@ static int alc5658_configure(FAR struct audio_lowerhalf_s *dev, FAR const struct #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; @@ -569,15 +591,21 @@ static int alc5658_configure(FAR struct audio_lowerhalf_s *dev, FAR const struct 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; @@ -595,7 +623,6 @@ static int alc5658_configure(FAR struct audio_lowerhalf_s *dev, FAR const struct * 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; @@ -615,209 +642,6 @@ static int alc5658_shutdown(FAR struct audio_lowerhalf_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: @@ -831,59 +655,31 @@ static int alc5658_start(FAR struct audio_lowerhalf_s *dev, FAR void *session) 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 */ } /**************************************************************************** @@ -902,19 +698,9 @@ static int alc5658_stop(FAR struct audio_lowerhalf_s *dev) #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: */ @@ -929,7 +715,6 @@ static int alc5658_stop(FAR struct audio_lowerhalf_s *dev) * 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) @@ -944,10 +729,10 @@ static int alc5658_pause(FAR struct audio_lowerhalf_s *dev) 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 */ @@ -957,7 +742,6 @@ static int alc5658_pause(FAR struct audio_lowerhalf_s *dev) * 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) @@ -972,8 +756,7 @@ static int alc5658_resume(FAR struct audio_lowerhalf_s *dev) 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 @@ -984,51 +767,67 @@ static int alc5658_resume(FAR struct audio_lowerhalf_s *dev) #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; } @@ -1038,10 +837,10 @@ static int alc5658_enqueuebuffer(FAR struct audio_lowerhalf_s *dev, FAR struct a * 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; } @@ -1051,7 +850,6 @@ static int alc5658_cancelbuffer(FAR struct audio_lowerhalf_s *dev, FAR struct ap * 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 @@ -1101,7 +899,6 @@ static int alc5658_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd, unsigned lo * 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 @@ -1113,7 +910,7 @@ static int alc5658_reserve(FAR struct audio_lowerhalf_s *dev) /* Borrow the APBQ semaphore for thread sync */ - alc5658_takesem(&priv->pendsem); + alc5658_takesem(&priv->devsem); if (priv->reserved) { ret = -EBUSY; } else { @@ -1131,7 +928,7 @@ static int alc5658_reserve(FAR struct audio_lowerhalf_s *dev) priv->reserved = true; } - alc5658_givesem(&priv->pendsem); + alc5658_givesem(&priv->devsem); return ret; } @@ -1142,7 +939,6 @@ static int alc5658_reserve(FAR struct audio_lowerhalf_s *dev) * 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 @@ -1150,23 +946,10 @@ static int alc5658_release(FAR struct audio_lowerhalf_s *dev) #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; } @@ -1183,7 +966,6 @@ static int alc5658_release(FAR struct audio_lowerhalf_s *dev) * ALC5658 interrupts were disabled in the interrupt handler. * ****************************************************************************/ - #ifdef ALC5658_USE_FFLOCK_INT static void alc5658_interrupt_work(FAR void *arg) { @@ -1264,157 +1046,6 @@ static int alc5658_interrupt(FAR const struct alc5658_lower_s *lower, FAR void * #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: @@ -1427,10 +1058,9 @@ static void *alc5658_workerthread(pthread_addr_t pvarg) * 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)); } /**************************************************************************** @@ -1446,9 +1076,9 @@ static void alc5658_audio_output(FAR struct alc5658_dev_s *priv) * 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)); } /**************************************************************************** @@ -1464,10 +1094,10 @@ static void alc5658_audio_input(FAR struct alc5658_dev_s *priv) * None * ****************************************************************************/ - #ifdef ALC5658_USE_FFLOCK_INT static void alc5658_configure_ints(FAR struct alc5658_dev_s *priv) { + /* Missing */ } #endif @@ -1484,7 +1114,6 @@ static void alc5658_configure_ints(FAR struct alc5658_dev_s *priv) * None * ****************************************************************************/ - static void alc5658_hw_reset(FAR struct alc5658_dev_s *priv) { @@ -1509,7 +1138,7 @@ 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 */ @@ -1537,7 +1166,6 @@ static void alc5658_hw_reset(FAR struct alc5658_dev_s *priv) * 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) { @@ -1558,7 +1186,7 @@ FAR struct audio_lowerhalf_s *alc5658_initialize(FAR struct i2c_dev_s *i2c, FAR 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); @@ -1586,7 +1214,7 @@ FAR struct audio_lowerhalf_s *alc5658_initialize(FAR struct i2c_dev_s *i2c, FAR return NULL; errout_with_dev: - sem_destroy(&priv->pendsem); + sem_destroy(&priv->devsem); kmm_free(priv); return NULL; } diff --git a/os/drivers/audio/alc5658.h b/os/drivers/audio/alc5658.h index 826e914..16c6530 100644 --- a/os/drivers/audio/alc5658.h +++ b/os/drivers/audio/alc5658.h @@ -33,7 +33,7 @@ * Pre-processor Definitions ****************************************************************************/ -#define ALC5658_DEFAULT_SAMPRATE 48000 +#define ALC5658_DEFAULT_SAMPRATE 16000 #define ALC5658_DEFAULT_NCHANNELS 2 #define ALC5658_DEFAULT_BPSAMP 16 #define FAIL 0xFFFF @@ -51,6 +51,8 @@ /**************************************************************************** * Public Types ****************************************************************************/ +/* This should be put under hammer to strip size +by for status variables */ struct alc5658_dev_s { /* We are an audio lower half driver (We are also the upper "half" of @@ -71,11 +73,8 @@ struct alc5658_dev_s { FAR struct i2s_dev_s *i2s; /* I2S driver to use */ struct dq_queue_s pendq; /* Queue of pending buffers to be sent */ struct dq_queue_s doneq; /* Queue of sent buffers to be returned */ - mqd_t mq; /* Message queue for receiving messages */ - char mqname[16]; /* Our message queue name */ - pthread_t threadid; /* ID of our thread */ - uint32_t bitrate; /* Actual programmed bit rate */ - sem_t pendsem; /* Protect pendq */ + sem_t devsem; /* Protection for both pendq & dev*/ + #ifdef ALC5658_USE_FFLOCK_INT struct work_s work; /* Interrupt work */ #endif @@ -100,6 +99,7 @@ struct alc5658_dev_s { #endif bool reserved; /* True: Device is reserved */ volatile int result; /* The result of the last transfer */ + bool inout; /* True: IN device */ }; /**************************************************************************** @@ -129,8 +129,8 @@ static void alc5658_setbass(FAR struct alc5658_dev_s *priv, uint8_t bass); static void alc5658_settreble(FAR struct alc5658_dev_s *priv, uint8_t treble); #endif -static void alc5658_setdatawidth(FAR struct alc5658_dev_s *priv); -static void alc5658_setbitrate(FAR struct alc5658_dev_s *priv); +static void alc5658_set_i2s_datawidth(FAR struct alc5658_dev_s *priv); +static void alc5658_set_i2s_samplerate(FAR struct alc5658_dev_s *priv); /* Audio lower half methods (and close friends) */ @@ -141,9 +141,6 @@ static int alc5658_configure(FAR struct audio_lowerhalf_s *dev, FAR void *sessio static int alc5658_configure(FAR struct audio_lowerhalf_s *dev, FAR const struct audio_caps_s *caps); #endif static int alc5658_shutdown(FAR struct audio_lowerhalf_s *dev); -static void alc5658_senddone(FAR struct i2s_dev_s *i2s, FAR struct ap_buffer_s *apb, FAR void *arg, int result); -static void alc5658_returnbuffers(FAR struct alc5658_dev_s *priv); -static int alc5658_sendbuffer(FAR struct alc5658_dev_s *priv); #ifdef CONFIG_AUDIO_MULTI_SESSION static int alc5658_start(FAR struct audio_lowerhalf_s *dev, FAR void *session); @@ -187,10 +184,8 @@ static void alc5658_interrupt_work(FAR void *arg); static int alc5658_interrupt(FAR const struct alc5658_lower_s *lower, FAR void *arg); #endif -static void *alc5658_workerthread(pthread_addr_t pvarg); /* Initialization */ - static void alc5658_audio_output(FAR struct alc5658_dev_s *priv); static void alc5658_audio_input(FAR struct alc5658_dev_s *priv); #ifdef ALC5658_USE_FFLOCK_INT @@ -199,11 +194,8 @@ static void alc5658_configure_ints(FAR struct alc5658_dev_s *priv); #define alc5658_configure_ints(p) #endif static void alc5658_hw_reset(FAR struct alc5658_dev_s *priv); +uint16_t alc5658_readreg(FAR struct alc5658_dev_s *priv, uint16_t regaddr); -#if defined(CONFIG_ALC5658_REGDUMP) || defined(CONFIG_ALC5658_CLKDEBUG) -struct alc5658_dev_s; -uint16_t alc5658_readreg(FAR struct alc5658_dev_s *priv, uint8_t regaddr); -#endif #endif /* CONFIG_AUDIO */ #endif /* __DRIVERS_AUDIO_ALC5658_H */ diff --git a/os/include/tinyara/audio/i2s.h b/os/include/tinyara/audio/i2s.h index 96e615f..810b67f 100644 --- a/os/include/tinyara/audio/i2s.h +++ b/os/include/tinyara/audio/i2s.h @@ -91,7 +91,7 @@ * ****************************************************************************/ -#define I2S_RXSAMPLERATE(d,f) ((d)->ops->i2s_rxsamplerate(d,r)) +#define I2S_RXSAMPLERATE(d,f) ((d)->ops->i2s_rxsamplerate(d,f)) /**************************************************************************** * Name: I2S_RXDATAWIDTH @@ -161,7 +161,7 @@ * ****************************************************************************/ -#define I2S_TXSAMPLERATE(d,f) ((d)->ops->i2s_txsamplerate(d,r)) +#define I2S_TXSAMPLERATE(d,f) ((d)->ops->i2s_txsamplerate(d,f)) /**************************************************************************** * Name: I2S_TXDATAWIDTH -- 2.7.4