diff --git a/drivers/audio/Kconfig b/drivers/audio/Kconfig index 8f025cc716..87da8976a6 100644 --- a/drivers/audio/Kconfig +++ b/drivers/audio/Kconfig @@ -239,3 +239,9 @@ config AUDIO_NULL_WORKER_STACKSIZE default 768 endif # AUDIO_NULL + +config AUDIO_I2S + bool "Audio I2S" + depends on AUDIO + depends on I2S + diff --git a/drivers/audio/Make.defs b/drivers/audio/Make.defs index ab8d671621..cd2ab4afd7 100644 --- a/drivers/audio/Make.defs +++ b/drivers/audio/Make.defs @@ -77,6 +77,10 @@ ifeq ($(CONFIG_AUDIO_TONE),y) CSRCS += tone.c endif +ifeq ($(CONFIG_AUDIO_I2S),y) +CSRCS += audio_i2s.c +endif + # Include Audio driver support DEPPATH += --dep-path audio diff --git a/drivers/audio/audio_i2s.c b/drivers/audio/audio_i2s.c new file mode 100644 index 0000000000..63a2dc84bb --- /dev/null +++ b/drivers/audio/audio_i2s.c @@ -0,0 +1,463 @@ +/**************************************************************************** + * drivers/audio/audio_i2s.c + * + * Copyright (C) 2018 Pinecone Inc. All rights reserved. + * Author: Zhong An + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct audio_i2s_s +{ + struct audio_lowerhalf_s dev; + struct i2s_dev_s *i2s; + bool playback; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int audio_i2s_getcaps(FAR struct audio_lowerhalf_s *dev, int type, + FAR struct audio_caps_s *caps); +static int audio_i2s_shutdown(FAR struct audio_lowerhalf_s *dev); +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_i2s_configure(FAR struct audio_lowerhalf_s *dev, + FAR void *session, + FAR const struct audio_caps_s *caps); +static int audio_i2s_start(FAR struct audio_lowerhalf_s *dev, + FAR void *session); +#ifndef CONFIG_AUDIO_EXCLUDE_STOP +static int audio_i2s_stop(FAR struct audio_lowerhalf_s *dev, + FAR void *session); +#endif +#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME +static int audio_i2s_pause(FAR struct audio_lowerhalf_s *dev, + FAR void *session); +static int audio_i2s_resume(FAR struct audio_lowerhalf_s *dev, + FAR void *session); +#endif +static int audio_i2s_reserve(FAR struct audio_lowerhalf_s *dev, + FAR void **session); +static int audio_i2s_release(FAR struct audio_lowerhalf_s *dev, + FAR void *session); +#else +static int audio_i2s_configure(FAR struct audio_lowerhalf_s *dev, + FAR const struct audio_caps_s *caps); +static int audio_i2s_start(FAR struct audio_lowerhalf_s *dev); +#ifndef CONFIG_AUDIO_EXCLUDE_STOP +static int audio_i2s_stop(FAR struct audio_lowerhalf_s *dev); +#endif +#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME +static int audio_i2s_pause(FAR struct audio_lowerhalf_s *dev); +static int audio_i2s_resume(FAR struct audio_lowerhalf_s *dev); +#endif +static int audio_i2s_reserve(FAR struct audio_lowerhalf_s *dev); +static int audio_i2s_release(FAR struct audio_lowerhalf_s *dev); +#endif +static int audio_i2s_allocbuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct audio_buf_desc_s *bufdesc); +static int audio_i2s_freebuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct audio_buf_desc_s *bufdesc); +static int audio_i2s_enqueuebuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct ap_buffer_s *apb); +static int audio_i2s_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd, + unsigned long arg); +static void audio_i2s_callback(struct i2s_dev_s *dev, + FAR struct ap_buffer_s *apb, FAR void *arg, + int result); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct audio_ops_s g_audio_i2s_ops = +{ + .getcaps = audio_i2s_getcaps, + .configure = audio_i2s_configure, + .shutdown = audio_i2s_shutdown, + .start = audio_i2s_start, +#ifndef CONFIG_AUDIO_EXCLUDE_STOP + .stop = audio_i2s_stop, +#endif +#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME + .pause = audio_i2s_pause, + .resume = audio_i2s_resume, +#endif + .allocbuffer = audio_i2s_allocbuffer, + .freebuffer = audio_i2s_freebuffer, + .enqueuebuffer = audio_i2s_enqueuebuffer, + .ioctl = audio_i2s_ioctl, + .reserve = audio_i2s_reserve, + .release = audio_i2s_release, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int audio_i2s_getcaps(FAR struct audio_lowerhalf_s *dev, int type, + FAR struct audio_caps_s *caps) +{ + FAR struct audio_i2s_s *audio_i2s = (struct audio_i2s_s *)dev; + FAR struct i2s_dev_s *i2s = audio_i2s->i2s; + + /* Validate the structure */ + + DEBUGASSERT(caps && caps->ac_len >= sizeof(struct audio_caps_s)); + audinfo("type=%d ac_type=%d\n", type, caps->ac_type); + + caps->ac_format.hw = 0; + caps->ac_controls.w = 0; + + switch (caps->ac_type) + { + /* Caller is querying for the types of units we support */ + + case AUDIO_TYPE_QUERY: + + /* Provide our overall capabilities. The interfacing software + * must then call us back for specific info for each capability. + */ + + switch (caps->ac_subtype) + { + case AUDIO_TYPE_QUERY: + + /* We don't decode any formats! Only something above us in + * the audio stream can perform decoding on our behalf. + */ + + /* The types of audio units we implement */ + + if (audio_i2s->playback) + { + caps->ac_controls.b[0] = AUDIO_TYPE_OUTPUT; + } + else + { + caps->ac_controls.b[0] = AUDIO_TYPE_INPUT; + } + + caps->ac_format.hw = 1 << (AUDIO_FMT_PCM - 1); + break; + + default: + caps->ac_controls.b[0] = AUDIO_SUBFMT_END; + break; + } + break; + + /* Provide capabilities of our OUTPUT unit */ + + case AUDIO_TYPE_OUTPUT: + case AUDIO_TYPE_INPUT: + + switch (caps->ac_subtype) + { + case AUDIO_TYPE_QUERY: + + /* Report the Sample rates we support */ + + caps->ac_controls.hw[0] = + AUDIO_SAMP_RATE_8K | AUDIO_SAMP_RATE_11K | + AUDIO_SAMP_RATE_16K | AUDIO_SAMP_RATE_22K | + AUDIO_SAMP_RATE_32K | AUDIO_SAMP_RATE_44K | + AUDIO_SAMP_RATE_48K | AUDIO_SAMP_RATE_96K | + AUDIO_SAMP_RATE_128K | AUDIO_SAMP_RATE_160K | + AUDIO_SAMP_RATE_172K | AUDIO_SAMP_RATE_192K; + break; + + default: + I2S_IOCTL(i2s, AUDIOIOC_GETCAPS, (unsigned long)caps); + break; + } + break; + + default: + I2S_IOCTL(i2s, AUDIOIOC_GETCAPS, (unsigned long)caps); + break; + } + + return caps->ac_len; +} + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_i2s_configure(FAR struct audio_lowerhalf_s *dev, + FAR void *session, + FAR const struct audio_caps_s *caps) +#else +static int audio_i2s_configure(FAR struct audio_lowerhalf_s *dev, + FAR const struct audio_caps_s *caps) +#endif +{ + FAR struct audio_i2s_s *audio_i2s = (struct audio_i2s_s *)dev; + FAR struct i2s_dev_s *i2s = audio_i2s->i2s; + int samprate, nchannels, bpsamp; + int ret = OK; + + DEBUGASSERT(audio_i2s && caps); + audinfo("ac_type: %d\n", caps->ac_type); + + /* Process the configure operation */ + + switch (caps->ac_type) + { + case AUDIO_TYPE_OUTPUT: + case AUDIO_TYPE_INPUT: + + /* Save the current stream configuration */ + + samprate = caps->ac_controls.hw[0] | + (caps->ac_controls.b[3] << 16); + nchannels = caps->ac_channels; + bpsamp = caps->ac_controls.b[2]; + + if (audio_i2s->playback) + { + I2S_TXCHANNELS(i2s, nchannels); + I2S_TXDATAWIDTH(i2s, bpsamp); + I2S_TXSAMPLERATE(i2s, samprate); + } + else + { + I2S_RXCHANNELS(i2s, nchannels); + I2S_RXDATAWIDTH(i2s, bpsamp); + I2S_RXSAMPLERATE(i2s, samprate); + } + break; + + default: + ret = I2S_IOCTL(i2s, AUDIOIOC_CONFIGURE, (unsigned long)caps); + break; + } + + return ret; +} + +static int audio_i2s_shutdown(FAR struct audio_lowerhalf_s *dev) +{ + FAR struct audio_i2s_s *audio_i2s = (struct audio_i2s_s *)dev; + FAR struct i2s_dev_s *i2s = audio_i2s->i2s; + + return I2S_IOCTL(i2s, AUDIOIOC_SHUTDOWN, audio_i2s->playback); +} + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_i2s_start(FAR struct audio_lowerhalf_s *dev, + FAR void *session) +#else +static int audio_i2s_start(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct audio_i2s_s *audio_i2s = (struct audio_i2s_s *)dev; + FAR struct i2s_dev_s *i2s = audio_i2s->i2s; + + return I2S_IOCTL(i2s, AUDIOIOC_START, audio_i2s->playback); +} + +#ifndef CONFIG_AUDIO_EXCLUDE_STOP +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_i2s_stop(FAR struct audio_lowerhalf_s *dev, + FAR void *session) +#else +static int audio_i2s_stop(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct audio_i2s_s *audio_i2s = (struct audio_i2s_s *)dev; + FAR struct i2s_dev_s *i2s = audio_i2s->i2s; + + return I2S_IOCTL(i2s, AUDIOIOC_STOP, audio_i2s->playback); +} +#endif + +#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_i2s_pause(FAR struct audio_lowerhalf_s *dev, + FAR void *session) +#else +static int audio_i2s_pause(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct audio_i2s_s *audio_i2s = (struct audio_i2s_s *)dev; + FAR struct i2s_dev_s *i2s = audio_i2s->i2s; + + return I2S_IOCTL(i2s, AUDIOIOC_PAUSE, audio_i2s->playback); +} + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_i2s_resume(FAR struct audio_lowerhalf_s *dev, + FAR void *session) +#else +static int audio_i2s_resume(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct audio_i2s_s *audio_i2s = (struct audio_i2s_s *)dev; + FAR struct i2s_dev_s *i2s = audio_i2s->i2s; + + return I2S_IOCTL(i2s, AUDIOIOC_RESUME, audio_i2s->playback); +} +#endif + +static int audio_i2s_allocbuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct audio_buf_desc_s *bufdesc) +{ + FAR struct audio_i2s_s *audio_i2s = (struct audio_i2s_s *)dev; + FAR struct i2s_dev_s *i2s = audio_i2s->i2s; + + return I2S_IOCTL(i2s, AUDIOIOC_ALLOCBUFFER, (unsigned long)bufdesc); +} + +static int audio_i2s_freebuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct audio_buf_desc_s *bufdesc) +{ + FAR struct audio_i2s_s *audio_i2s = (struct audio_i2s_s *)dev; + FAR struct i2s_dev_s *i2s = audio_i2s->i2s; + + return I2S_IOCTL(i2s, AUDIOIOC_FREEBUFFER, (unsigned long)bufdesc); +} + +static int audio_i2s_enqueuebuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct ap_buffer_s *apb) +{ + FAR struct audio_i2s_s *audio_i2s = (struct audio_i2s_s *)dev; + FAR struct i2s_dev_s *i2s = audio_i2s->i2s; + + if (audio_i2s->playback) + { + return I2S_SEND(i2s, apb, audio_i2s_callback, audio_i2s, 0); + } + else + { + return I2S_RECEIVE(i2s, apb, audio_i2s_callback, audio_i2s, 0); + } +} + +static int audio_i2s_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd, + unsigned long arg) +{ + FAR struct audio_i2s_s *audio_i2s = (struct audio_i2s_s *)dev; + FAR struct i2s_dev_s *i2s = audio_i2s->i2s; + + return I2S_IOCTL(i2s, cmd, arg); +} + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_i2s_reserve(FAR struct audio_lowerhalf_s *dev, + FAR void **session) +#else +static int audio_i2s_reserve(FAR struct audio_lowerhalf_s *dev) +#endif +{ +#ifdef CONFIG_AUDIO_MULTI_SESSION + *session = (void *)audio_i2s->playback; +#endif + return OK; +} + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_i2s_release(FAR struct audio_lowerhalf_s *dev, + FAR void *session) +#else +static int audio_i2s_release(FAR struct audio_lowerhalf_s *dev) +#endif +{ + return OK; +} + +static void audio_i2s_callback(struct i2s_dev_s *dev, + FAR struct ap_buffer_s *apb, + FAR void *arg, int result) +{ + FAR struct audio_i2s_s *audio_i2s = arg; + bool final = false; + + if ((apb->flags & AUDIO_APB_FINAL) != 0) + { + final = true; + } + +#ifdef CONFIG_AUDIO_MULTI_SESSION + audio_i2s->dev.upper(audio_i2s->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, + OK, NULL); +#else + audio_i2s->dev.upper(audio_i2s->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, + OK); +#endif + if (final) + { +#ifdef CONFIG_AUDIO_MULTI_SESSION + audio_i2s->dev.upper(audio_i2s->dev.priv, AUDIO_CALLBACK_COMPLETE, + NULL, OK, NULL); +#else + audio_i2s->dev.upper(audio_i2s->dev.priv, AUDIO_CALLBACK_COMPLETE, + NULL, OK); +#endif + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +FAR struct audio_lowerhalf_s *audio_i2s_initialize(FAR struct i2s_dev_s *i2s, + bool playback) +{ + FAR struct audio_i2s_s *audio_i2s; + + if (i2s == NULL) + { + return NULL; + } + + audio_i2s = kmm_zalloc(sizeof(struct audio_i2s_s)); + if (audio_i2s == NULL) + { + return NULL; + } + + audio_i2s->playback = playback; + audio_i2s->i2s = i2s; + audio_i2s->dev.ops = &g_audio_i2s_ops; + + return &audio_i2s->dev; +} diff --git a/include/nuttx/audio/audio_i2s.h b/include/nuttx/audio/audio_i2s.h new file mode 100644 index 0000000000..d9f547cef5 --- /dev/null +++ b/include/nuttx/audio/audio_i2s.h @@ -0,0 +1,54 @@ +/**************************************************************************** + * include/nuttx/audio/audio_i2s.h + * + * Copyright (C) 2018 Pinecone Inc. All rights reserved. + * Author: Zhong An + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_AUDIO_AUDIO_I2S_H +#define __INCLUDE_NUTTX_AUDIO_AUDIO_I2S_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +FAR struct audio_lowerhalf_s *audio_i2s_initialize(FAR struct i2s_dev_s *i2s, + bool playback); + +#endif