Browse Source

Merge pull request #2928 from Zero-Free/audio_developer

添加qemu和stm32l475-atk-pandora对音频设备驱动框架的支持
Bernard Xiong 6 years ago
parent
commit
fcfa986bca

+ 0 - 191
bsp/qemu-vexpress-a9/drivers/audio/audio_device.c

@@ -1,191 +0,0 @@
-/*
- * File      : audio_device.c
- * This file is part of RT-Thread RTOS
- * COPYRIGHT (C) 2017, RT-Thread Development Team
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Change Logs:
- * Date           Author       Notes
- * 2018-05-26     RT-Thread    the first version
- */
-
-#include <rtthread.h>
-#include <rthw.h>
-#include <rtdevice.h>
-#include <string.h>
-#include "drv_pl041.h"
-#include "drv_ac97.h"
-#include "audio_device.h"
-
-struct audio_device
-{
-    struct rt_device *snd;
-    struct rt_mempool mp;
-
-    int state;
-
-    void (*evt_handler)(void *parameter, int state);
-    void *parameter;
-};
-
-static struct audio_device *_audio_device = NULL;
-
-void *audio_device_get_buffer(int *bufsz)
-{
-    if (bufsz)
-    {
-        *bufsz = AUDIO_DEVICE_DECODE_MP_BLOCK_SZ * 2;
-    }
-
-    return rt_mp_alloc(&(_audio_device->mp), RT_WAITING_FOREVER);
-}
-
-void audio_device_put_buffer(void *ptr)
-{
-    if (ptr) rt_mp_free(ptr);
-    return ;
-}
-
-static rt_err_t audio_device_write_done(struct rt_device *device, void *ptr)
-{
-    if (!ptr)
-    {
-        rt_kprintf("device buf_release NULL\n");
-        return -RT_ERROR;
-    }
-
-    rt_mp_free(ptr);
-    return RT_EOK;
-}
-
-void audio_device_write(void *buffer, int size)
-{
-    if (_audio_device->snd && size != 0)
-    {
-        if (_audio_device->state == AUDIO_DEVICE_IDLE)
-        {
-            if (_audio_device->evt_handler)
-                _audio_device->evt_handler(_audio_device->parameter, AUDIO_DEVICE_PLAYBACK);
-
-            /* change audio device state */
-            _audio_device->state = AUDIO_DEVICE_PLAYBACK;
-        }
-
-        rt_device_write(_audio_device->snd, 0, buffer, size);
-    }
-    else
-    {
-        /* release buffer directly */
-        rt_mp_free(buffer);
-    }
-
-    return ;
-}
-
-int audio_device_init(void)
-{
-    uint8_t *mempool_ptr;
-
-    if (!_audio_device)
-    {
-        _audio_device = (struct audio_device *) rt_malloc(sizeof(struct audio_device) + AUDIO_DEVICE_DECODE_MP_SZ);
-        if (_audio_device == NULL)
-        {
-            rt_kprintf("malloc memeory for _audio_device failed! \n");
-            return -RT_ERROR;
-        }
-
-        _audio_device->evt_handler = NULL;
-        _audio_device->parameter   = NULL;
-
-        mempool_ptr = (uint8_t *)(_audio_device + 1);
-        rt_mp_init(&(_audio_device->mp), "adbuf", mempool_ptr, AUDIO_DEVICE_DECODE_MP_SZ, AUDIO_DEVICE_DECODE_MP_BLOCK_SZ * 2);
-
-        /* find snd device */
-        _audio_device->snd = rt_device_find("sound");
-        if (_audio_device->snd == NULL)
-        {
-            rt_kprintf("sound device not found \n");
-            return -1;
-        }
-
-        /* set tx complete call back function */
-        rt_device_set_tx_complete(_audio_device->snd, audio_device_write_done);
-    }
-
-    return RT_EOK;
-}
-
-int audio_device_set_evt_handler(void (*handler)(void *parameter, int state), void *parameter)
-{
-    if (_audio_device)
-    {
-        _audio_device->evt_handler = handler;
-        _audio_device->parameter   = parameter;
-    }
-
-    return 0;
-}
-
-void audio_device_set_rate(int sample_rate)
-{
-    if (_audio_device->snd)
-    {
-        int rate = sample_rate;
-
-        rt_device_control(_audio_device->snd, CODEC_CMD_SAMPLERATE, &rate);
-    }
-}
-
-void audio_device_set_volume(int value)
-{
-    if (_audio_device->snd)
-    {
-        rt_device_control(_audio_device->snd, CODEC_CMD_SET_VOLUME, &value);
-    }
-}
-
-int audio_device_get_volume(void)
-{
-    int value = 0;
-
-    if (_audio_device->snd)
-    {
-        rt_device_control(_audio_device->snd, CODEC_CMD_GET_VOLUME, &value);
-    }
-
-    return value;
-}
-
-void audio_device_open(void)
-{
-    _audio_device->state = AUDIO_DEVICE_IDLE;
-    rt_device_open(_audio_device->snd, RT_DEVICE_OFLAG_WRONLY);
-}
-
-void audio_device_close(void)
-{
-    rt_device_close(_audio_device->snd);
-
-    if (_audio_device->state == AUDIO_DEVICE_PLAYBACK)
-    {
-        if (_audio_device->evt_handler)
-            _audio_device->evt_handler(_audio_device->parameter, AUDIO_DEVICE_CLOSE);
-    }
-
-    /* set to idle */
-    _audio_device->state = AUDIO_DEVICE_CLOSE;
-}

+ 0 - 51
bsp/qemu-vexpress-a9/drivers/audio/audio_device.h

@@ -1,51 +0,0 @@
-/*
- * File      : audio_device.h
- * This file is part of RT-Thread RTOS
- * COPYRIGHT (C) 2017, RT-Thread Development Team
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Change Logs:
- * Date           Author       Notes
- * 2018-05-26     RT-Thread    the first version
- */
-
-#ifndef AUDIO_DEVICE_H__
-#define AUDIO_DEVICE_H__
-
-enum AUDIO_DEVICE_STATE
-{
-    AUDIO_DEVICE_IDLE,
-    AUDIO_DEVICE_PLAYBACK,
-    AUDIO_DEVICE_CLOSE,
-};
-
-void *audio_device_get_buffer(int *bufsz);
-void audio_device_put_buffer(void *ptr);
-
-void audio_device_write(void *buffer, int size);
-
-int audio_device_init(void);
-void audio_device_close(void);
-
-void audio_device_open(void);
-
-int audio_device_set_evt_handler(void (*handler)(void *parameter, int state), void *parameter);
-
-void audio_device_set_rate(int sample_rate);
-void audio_device_set_volume(int volume);
-void audio_device_wait_free(void);
-
-#endif

+ 0 - 328
bsp/qemu-vexpress-a9/drivers/audio/drv_audio.c

@@ -1,328 +0,0 @@
-/*
- * File      : drv_audio.c
- * This file is part of RT-Thread RTOS
- * COPYRIGHT (C) 2017, RT-Thread Development Team
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Change Logs:
- * Date           Author       Notes
- * 2018-05-26     RT-Thread    the first version
- */
-
-#include <rtthread.h>
-#include <rthw.h>
-#include <rtdevice.h>
-#include <string.h>
-#include "drv_pl041.h"
-#include "drv_ac97.h"
-#include "drv_audio.h"
-
-#define DATA_NODE_MAX          (10)
-#define CODEC_TX_FIFO_SIZE     (256)
-
-#define AUDIO_DEVICE_DECODE_MP_SIZE  (4096)
-#define AUDIO_DEVICE_DECODE_MP_CONUT (4)
-
-struct codec_data_node
-{
-    char *data_ptr;
-    rt_size_t  data_size;
-};
-
-struct audio_buff_des
-{
-    struct codec_data_node *data_list;
-    void (*free_fun)(void *);
-    rt_uint32_t read_offset;
-    rt_uint16_t node_num;
-    rt_uint16_t read_index, put_index;
-};
-
-struct audio_device
-{
-    /* inherit from rt_device */
-    struct rt_device parent;
-};
-
-static struct audio_device audio_device_drive;
-static struct audio_buff_des *audio_buff;
-static int irq_flag = 0;
-
-static void _audio_buff_cb(void *buff)
-{
-    if (audio_device_drive.parent.tx_complete != RT_NULL)
-    {
-        audio_device_drive.parent.tx_complete(&audio_device_drive.parent, buff);
-    }
-}
-
-static rt_size_t _audio_buff_push(struct audio_buff_des *hdle, void *buff, int size)
-{
-    struct codec_data_node *node;
-    rt_uint16_t next_index;
-    rt_uint32_t level;
-
-    if ((buff == RT_NULL) || (size == 0))
-    {
-        return 0;
-    }
-
-    next_index = hdle->put_index + 1;
-    if (next_index >= hdle->node_num)
-        next_index = 0;
-    /* check data_list full */
-    if (next_index == hdle->read_index)
-    {
-        rt_kprintf("data_list full\n");
-        rt_set_errno(-RT_EFULL);
-        return 0;
-    }
-
-    level = rt_hw_interrupt_disable();
-    node = &hdle->data_list[hdle->put_index];
-    hdle->put_index = next_index;
-
-    /* set node attribute */
-    node->data_ptr = (char *) buff;
-    node->data_size = size;
-    rt_hw_interrupt_enable(level);
-
-    return size;
-}
-
-static rt_size_t _audio_buff_pop(struct audio_buff_des *hdle, void *buff, int size)
-{
-    struct codec_data_node *node;
-    rt_uint32_t next_index, count = 0, cp_size = 0, offset = 0;
-
-    node = &hdle->data_list[hdle->read_index];
-    if ((hdle->read_index == hdle->put_index) && (node->data_ptr == RT_NULL))
-    {
-        memset(buff, 0xff, size);
-        return 0;
-    }
-
-    while (count < size)
-    {
-        node = &hdle->data_list[hdle->read_index];
-        offset = hdle->read_offset;
-        cp_size = (node->data_size - offset) > (size - count) ? (size - count) : (node->data_size - offset);
-
-        if (node->data_ptr == RT_NULL)
-        {
-            memset(buff, 0, size - count);
-            return count;
-        }
-
-        memcpy((rt_uint8_t *)buff + count, (rt_uint8_t *)(node->data_ptr) + offset, cp_size);
-        hdle->read_offset += cp_size;
-        count += cp_size;
-
-        if (hdle->read_offset >= node->data_size)
-        {
-            /* notify transmitted complete. */
-            if (hdle->free_fun != RT_NULL)
-            {
-                hdle->free_fun(node->data_ptr);
-            }
-            /* clear current node */
-            memset(node, 0, sizeof(struct codec_data_node));
-            next_index = hdle->read_index + 1;
-            if (next_index >= hdle->node_num)
-            {
-                next_index = 0;
-            }
-            hdle->read_offset = 0;
-            hdle->read_index = next_index;
-        }
-    }
-
-    return count;
-}
-
-static void transit_wav_data(rt_uint32_t status)
-{
-    rt_uint16_t sample[CODEC_TX_FIFO_SIZE];
-    int i = 0, size;
-
-    size = _audio_buff_pop(audio_buff, sample, CODEC_TX_FIFO_SIZE * sizeof(rt_uint16_t));
-    if ((size == 0) && (irq_flag == 1))
-    {
-        aaci_pl041_irq_disable(0, AACI_IE_UR | AACI_IE_TX | AACI_IE_TXC);
-        irq_flag = 0;
-    }
-
-    for (i = 0; i < (size >> 1); i++)
-    {
-        aaci_pl041_channle_write(0, &sample[i], 1);
-    }
-}
-
-static void rt_hw_aaci_isr(rt_uint32_t status, void *user_data)
-{
-    if (status & AACI_SR_TXHE)
-    {
-        transit_wav_data(status);
-    }
-}
-
-static rt_err_t codec_init(rt_device_t dev)
-{
-    struct pl041_cfg _cfg;
-
-    _cfg.itype = PL041_CHANNLE_LEFT_ADC | PL041_CHANNLE_RIGHT_ADC;
-    _cfg.otype = PL041_CHANNLE_LEFT_DAC | PL041_CHANNLE_RIGHT_DAC;
-    _cfg.vol = 50;
-    _cfg.rate = 8000;
-
-    ac97_reset();
-    aaci_pl041_channle_cfg(0, &_cfg);
-    aaci_pl041_irq_register(0, rt_hw_aaci_isr, RT_NULL);
-
-    return RT_EOK;
-}
-
-static rt_err_t codec_open(rt_device_t dev, rt_uint16_t oflag)
-{
-    return RT_EOK;
-}
-
-static rt_err_t codec_close(rt_device_t dev)
-{
-    rt_uint16_t temp = 0, i = 1024 * 10;
-
-    while (PL041->sr1 & AACI_SR_TXB);
-    while (i)
-    {
-        if (aaci_pl041_channle_write(0, &temp, 1) != 0)
-        {
-            i--;
-        }
-    }
-    return RT_EOK;
-}
-
-static rt_size_t codec_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
-{
-    return 0;
-}
-
-static rt_size_t codec_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
-{
-    _audio_buff_push(audio_buff, (void *)buffer, size);
-
-    if (irq_flag == 0)
-    {
-        //open irq
-        irq_flag = 1;
-        aaci_pl041_channle_enable(0);
-        aaci_pl041_irq_enable(0, AACI_IE_UR | AACI_IE_TX | AACI_IE_TXC);
-    }
-    return 0;
-}
-
-static rt_err_t codec_control(rt_device_t dev, int cmd, void *args)
-{
-    rt_err_t result = RT_EOK;
-
-    switch (cmd)
-    {
-    case CODEC_CMD_RESET:
-    {
-        break;
-    }
-    case CODEC_CMD_SET_VOLUME:
-    {
-        uint32_t v;
-
-        v = *(rt_uint32_t *)args;
-        result = ac97_set_vol(v);
-        break;
-    }
-    case CODEC_CMD_GET_VOLUME:
-    {
-        int *v = args;
-        *v = ac97_get_vol();
-        break;
-    }
-    case CODEC_CMD_SAMPLERATE:
-    {
-        int v;
-
-        v = *(rt_uint32_t *)args;
-        ac97_set_rate(v);
-        break;
-    }
-
-    default:
-        result = RT_ERROR;
-    }
-
-    return result;
-}
-
-#ifdef RT_USING_DEVICE_OPS
-const static struct rt_device_ops codec_ops = 
-{
-    codec_init,
-    codec_open,
-    codec_close,
-    codec_read,
-    codec_write,
-    codec_control
-};
-#endif
-
-int audio_hw_init(void)
-{
-    struct audio_device *codec = &audio_device_drive;
-
-    codec->parent.type        = RT_Device_Class_Sound;
-    codec->parent.rx_indicate = RT_NULL;
-    codec->parent.tx_complete = RT_NULL;
-
-#ifdef RT_USING_DEVICE_OPS
-    codec->parent.ops     = &codec_ops;
-#else
-    codec->parent.init    = codec_init;
-    codec->parent.open    = codec_open;
-    codec->parent.close   = codec_close;
-    codec->parent.read    = codec_read;
-    codec->parent.write   = codec_write;
-    codec->parent.control = codec_control;
-#endif
-
-    codec->parent.user_data   = RT_NULL;
-
-    audio_buff = rt_malloc(sizeof(struct audio_buff_des) + sizeof(struct codec_data_node) * DATA_NODE_MAX);
-    if (audio_buff == RT_NULL)
-    {
-        rt_kprintf("audio buff malloc fail\n");
-        return -1;
-    }
-    rt_memset(audio_buff, 0, sizeof(struct audio_buff_des) + sizeof(struct codec_data_node) * DATA_NODE_MAX);
-    audio_buff->data_list = (struct codec_data_node *)((rt_uint8_t *)audio_buff + sizeof(struct audio_buff_des));
-    audio_buff->free_fun = _audio_buff_cb;
-    audio_buff->node_num = DATA_NODE_MAX;
-    /* register the device */
-    rt_device_register(&codec->parent, "sound", RT_DEVICE_FLAG_WRONLY | RT_DEVICE_FLAG_DMA_TX);
-
-    aaci_pl041_init();
-    rt_device_init(&codec->parent);
-
-    return 0;
-}
-INIT_DEVICE_EXPORT(audio_hw_init);

+ 0 - 29
bsp/qemu-vexpress-a9/drivers/audio/drv_audio.h

@@ -1,29 +0,0 @@
-/*
- * File      : drv_audio.h
- * This file is part of RT-Thread RTOS
- * COPYRIGHT (C) 2017, RT-Thread Development Team
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Change Logs:
- * Date           Author       Notes
- * 2018-05-26     RT-Thread    the first version
- */
-
-#ifndef __DRV_AUDIO_H__
-#define __DRV_AUDIO_H__
-
-
-#endif

+ 1 - 1
bsp/qemu-vexpress-a9/drivers/audio/drv_pl041.c

@@ -302,7 +302,7 @@ static void aaci_pl041_irq_handle(int irqno, void *param)
     void *p_status;
     void *p_status;
 
 
     mask = PL041_READ(&PL041->allints);
     mask = PL041_READ(&PL041->allints);
-    PL041_WRITE(PL041->intclr, mask);
+    PL041_WRITE(&PL041->intclr, mask);
 
 
     for (channle = 0; (channle < PL041_CHANNLE_NUM) && (mask); channle++)
     for (channle = 0; (channle < PL041_CHANNLE_NUM) && (mask); channle++)
     {
     {

+ 1 - 1
bsp/qemu-vexpress-a9/drivers/audio/drv_pl041.h

@@ -225,7 +225,7 @@ typedef void (*pl041_irq_fun_t)(rt_uint32_t status, void * user_data);
 rt_err_t aaci_pl041_init(void);
 rt_err_t aaci_pl041_init(void);
 void aaci_ac97_write(rt_uint16_t reg, rt_uint16_t val);
 void aaci_ac97_write(rt_uint16_t reg, rt_uint16_t val);
 rt_uint16_t aaci_ac97_read(rt_uint16_t reg);
 rt_uint16_t aaci_ac97_read(rt_uint16_t reg);
-int aaci_pl041_channle_cfg(int channle, pl041_cfg_t cgf);
+int aaci_pl041_channle_cfg(int channle, pl041_cfg_t cfg);
 int aaci_pl041_channle_write(int channle, rt_uint16_t *buff, int count);
 int aaci_pl041_channle_write(int channle, rt_uint16_t *buff, int count);
 int aaci_pl041_channle_read(int channle, rt_uint16_t *buff, int count);
 int aaci_pl041_channle_read(int channle, rt_uint16_t *buff, int count);
 int aaci_pl041_channle_enable(int channle);
 int aaci_pl041_channle_enable(int channle);

+ 342 - 0
bsp/qemu-vexpress-a9/drivers/audio/drv_sound.c

@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Date           Author       Notes
+ * 2019-07-23     Zero-Free    first implementation
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include <drv_sound.h>
+#include <drv_pl041.h>
+#include <drv_ac97.h>
+
+#define DBG_TAG              "drv.sound"
+#define DBG_LVL              DBG_INFO
+#include <rtdbg.h>
+
+#define TX_FIFO_SIZE         (3840)
+
+struct sound_device
+{
+    struct rt_audio_device audio;
+    struct rt_audio_configure replay_config;
+    rt_uint8_t *tx_fifo;
+    rt_uint8_t volume;
+};
+
+static struct sound_device snd_dev = {0};
+
+static void rt_hw_aaci_isr(rt_uint32_t status, void *user_data)
+{
+    if (status & AACI_SR_TXHE)
+    {
+        rt_audio_tx_complete(&snd_dev.audio);
+    }
+}
+
+static rt_err_t sound_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
+{
+    rt_err_t result = RT_EOK;
+    struct sound_device *snd_dev;
+
+    RT_ASSERT(audio != RT_NULL);
+    snd_dev = (struct sound_device *)audio->parent.user_data;
+
+    switch (caps->main_type)
+    {
+    case AUDIO_TYPE_QUERY: /* qurey the types of hw_codec device */
+    {
+        switch (caps->sub_type)
+        {
+        case AUDIO_TYPE_QUERY:
+            caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;
+            break;
+
+        default:
+            result = -RT_ERROR;
+            break;
+        }
+
+        break;
+    }
+
+    case AUDIO_TYPE_OUTPUT: /* Provide capabilities of OUTPUT unit */
+    {
+        switch (caps->sub_type)
+        {
+        case AUDIO_DSP_PARAM:
+            caps->udata.config.samplerate   = snd_dev->replay_config.samplerate;
+            caps->udata.config.channels     = snd_dev->replay_config.channels;
+            caps->udata.config.samplebits   = snd_dev->replay_config.samplebits;
+            break;
+
+        case AUDIO_DSP_SAMPLERATE:
+            caps->udata.config.samplerate   = snd_dev->replay_config.samplerate;
+            break;
+
+        case AUDIO_DSP_CHANNELS:
+            caps->udata.config.channels     = snd_dev->replay_config.channels;
+            break;
+
+        case AUDIO_DSP_SAMPLEBITS:
+            caps->udata.config.samplebits   = snd_dev->replay_config.samplebits;
+            break;
+
+        default:
+            result = -RT_ERROR;
+            break;
+        }
+
+        break;
+    }
+
+    case AUDIO_TYPE_MIXER: /* report the Mixer Units */
+    {
+        switch (caps->sub_type)
+        {
+        case AUDIO_MIXER_QUERY:
+            caps->udata.mask = AUDIO_MIXER_VOLUME;
+            break;
+
+        case AUDIO_MIXER_VOLUME:
+            caps->udata.value = snd_dev->volume;
+            break;
+
+        default:
+            result = -RT_ERROR;
+            break;
+        }
+
+        break;
+    }
+
+    default:
+        result = -RT_ERROR;
+        break;
+    }
+
+    return result;
+}
+
+static rt_err_t sound_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
+{
+    rt_err_t result = RT_EOK;
+    struct sound_device *snd_dev;
+    struct rt_audio_replay *replay;
+
+    RT_ASSERT(audio != RT_NULL);
+    snd_dev = (struct sound_device *)audio->parent.user_data;
+
+    switch (caps->main_type)
+    {
+    case AUDIO_TYPE_MIXER:
+    {
+        switch (caps->sub_type)
+        {
+        case AUDIO_MIXER_VOLUME:
+        {
+            rt_uint8_t volume = caps->udata.value;
+
+            snd_dev->volume = volume;
+            ac97_set_vol(volume);
+            LOG_I("set volume %d", volume);
+            break;
+        }
+
+        default:
+            result = -RT_ERROR;
+            break;
+        }
+
+        break;
+    }
+
+    case AUDIO_TYPE_OUTPUT:
+    {
+        switch (caps->sub_type)
+        {
+        case AUDIO_DSP_PARAM:
+        {
+            /* set samplerate */
+            ac97_set_rate(caps->udata.config.samplerate);
+
+            /* update buffer fifo informaition according samplerate */
+            replay = snd_dev->audio.replay;
+            replay->buf_info.total_size = caps->udata.config.samplerate / 50 * 4;
+            replay->buf_info.block_size = replay->buf_info.total_size / 2;
+
+            /* save configs */
+            snd_dev->replay_config.samplerate = caps->udata.config.samplerate;
+            snd_dev->replay_config.channels   = caps->udata.config.channels;
+            snd_dev->replay_config.samplebits = caps->udata.config.samplebits;
+            LOG_D("set samplerate %d", snd_dev->replay_config.samplerate);
+            break;
+        }
+
+        case AUDIO_DSP_SAMPLERATE:
+        {
+            ac97_set_rate(caps->udata.config.samplerate);
+
+            snd_dev->replay_config.samplerate = caps->udata.config.samplerate;
+            LOG_D("set samplerate %d", snd_dev->replay_config.samplerate);
+            break;
+        }
+
+        case AUDIO_DSP_CHANNELS:
+        {
+            /* not support */
+            snd_dev->replay_config.channels   = caps->udata.config.channels;
+            LOG_D("set channels %d", snd_dev->replay_config.channels);
+            break;
+        }
+
+        case AUDIO_DSP_SAMPLEBITS:
+        {
+            /* not support */
+            snd_dev->replay_config.samplebits = caps->udata.config.samplebits;
+            break;
+        }
+
+        default:
+            result = -RT_ERROR;
+            break;
+        }
+
+        break;
+    }
+
+    default:
+        break;
+    }
+
+    return result;
+}
+
+static rt_err_t sound_init(struct rt_audio_device *audio)
+{
+    rt_err_t result = RT_EOK;
+    struct sound_device *snd_dev;
+    struct pl041_cfg _cfg;
+
+    RT_ASSERT(audio != RT_NULL);
+    snd_dev = (struct sound_device *)audio->parent.user_data;
+
+    aaci_pl041_init();
+
+    _cfg.itype = PL041_CHANNLE_LEFT_ADC | PL041_CHANNLE_RIGHT_ADC;
+    _cfg.otype = PL041_CHANNLE_LEFT_DAC | PL041_CHANNLE_RIGHT_DAC;
+    _cfg.vol   = snd_dev->volume;
+    _cfg.rate  = snd_dev->replay_config.samplerate;
+
+    ac97_reset();
+    aaci_pl041_channle_cfg(0, &_cfg);
+    aaci_pl041_irq_register(0, rt_hw_aaci_isr, RT_NULL);
+
+    return result;
+}
+
+static rt_err_t sound_start(struct rt_audio_device *audio, int stream)
+{
+    RT_ASSERT(audio != RT_NULL);
+
+    if (stream == AUDIO_STREAM_REPLAY)
+    {
+        LOG_D("open sound device");
+        aaci_pl041_channle_enable(0);
+        aaci_pl041_irq_enable(0, AACI_IE_UR | AACI_IE_TX | AACI_IE_TXC);
+    }
+
+    return RT_EOK;
+}
+
+static rt_err_t sound_stop(struct rt_audio_device *audio, int stream)
+{
+    RT_ASSERT(audio != RT_NULL);
+
+    if (stream == AUDIO_STREAM_REPLAY)
+    {
+        /* wait codec free */
+        rt_thread_mdelay(100);
+        /* disable irq and channels 0 */
+        aaci_pl041_irq_disable(0, AACI_IE_UR | AACI_IE_TX | AACI_IE_TXC);
+        aaci_pl041_channle_disable(0);
+        LOG_D("close sound device");
+    }
+
+    return RT_EOK;
+}
+
+static void sound_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)
+{
+    struct sound_device *snd_dev;
+
+    RT_ASSERT(audio != RT_NULL);
+    snd_dev = (struct sound_device *)audio->parent.user_data;
+
+    /**
+     *               TX_FIFO
+     * +----------------+----------------+
+     * |     block1     |     block2     |
+     * +----------------+----------------+
+     *  \  block_size  /
+     */
+    info->buffer      = snd_dev->tx_fifo;
+    info->total_size  = TX_FIFO_SIZE;
+    info->block_size  = TX_FIFO_SIZE/2;
+    info->block_count = 2;
+}
+
+static rt_size_t sound_transmit(struct rt_audio_device *audio, const void *writeBuf, void *readBuf, rt_size_t size)
+{
+    RT_ASSERT(audio != RT_NULL);
+
+    /* write data to channel_0 fifo */
+    aaci_pl041_channle_write(0, (rt_uint16_t *)writeBuf, size >> 1);
+
+    return size;
+}
+
+static struct rt_audio_ops snd_ops =
+{
+    .getcaps     = sound_getcaps,
+    .configure   = sound_configure,
+    .init        = sound_init,
+    .start       = sound_start,
+    .stop        = sound_stop,
+    .transmit    = sound_transmit,
+    .buffer_info = sound_buffer_info,
+};
+
+int rt_hw_audio_init(void)
+{
+    rt_uint8_t *tx_fifo;
+
+    if (snd_dev.tx_fifo)
+        return RT_EOK;
+
+    tx_fifo = rt_malloc(TX_FIFO_SIZE);
+    if (tx_fifo == RT_NULL)
+        return -RT_ENOMEM;
+    rt_memset(tx_fifo, 0, TX_FIFO_SIZE);
+    snd_dev.tx_fifo = tx_fifo;
+
+    /* init default configuration */
+    {
+        snd_dev.replay_config.samplerate = 44100;
+        snd_dev.replay_config.channels   = 2;
+        snd_dev.replay_config.samplebits = 16;
+        snd_dev.volume                   = 55;
+    }
+
+    /* register sound device */
+    snd_dev.audio.ops = &snd_ops;
+    rt_audio_register(&snd_dev.audio, "sound0", RT_DEVICE_FLAG_WRONLY, &snd_dev);
+
+    return RT_EOK;
+}
+
+INIT_DEVICE_EXPORT(rt_hw_audio_init);

+ 15 - 0
bsp/qemu-vexpress-a9/drivers/audio/drv_sound.h

@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Date           Author       Notes
+ * 2019-07-23     Zero-Free    first implementation
+ */
+
+#ifndef __DRV_SOUND_H__
+#define __DRV_SOUND_H__
+
+int rt_hw_audio_init(void);
+
+#endif

+ 0 - 144
bsp/qemu-vexpress-a9/drivers/audio/wav_play.c

@@ -1,144 +0,0 @@
-#include <rtthread.h>
-#include <rtdevice.h>
-#include <finsh.h>
-
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "audio_device.h"
-
-#define BUFSZ   2048
-
-struct RIFF_HEADER_DEF
-{
-    char riff_id[4];     // 'R','I','F','F'
-    uint32_t riff_size;
-    char riff_format[4]; // 'W','A','V','E'
-};
-
-struct WAVE_FORMAT_DEF
-{
-    uint16_t FormatTag;
-    uint16_t Channels;
-    uint32_t SamplesPerSec;
-    uint32_t AvgBytesPerSec;
-    uint16_t BlockAlign;
-    uint16_t BitsPerSample;
-};
-
-struct FMT_BLOCK_DEF
-{
-    char fmt_id[4];    // 'f','m','t',' '
-    uint32_t fmt_size;
-    struct WAVE_FORMAT_DEF wav_format;
-};
-
-struct DATA_BLOCK_DEF
-{
-    char data_id[4];     // 'R','I','F','F'
-    uint32_t data_size;
-};
-
-struct wav_info
-{
-    struct RIFF_HEADER_DEF header;
-    struct FMT_BLOCK_DEF   fmt_block;
-    struct DATA_BLOCK_DEF  data_block;
-};
-
-static char file_name[32];
-
-void wavplay_thread_entry(void *parameter)
-{
-    FILE *fp = NULL;
-    uint16_t *buffer = NULL;
-    struct wav_info *info = NULL;
-
-    fp = fopen(file_name, "rb");
-    if (!fp)
-    {
-        printf("open file failed!\n");
-        goto __exit;
-    }
-
-    info = (struct wav_info *) malloc(sizeof(*info));
-    if (!info) goto __exit;
-
-    if (fread(&(info->header),     sizeof(struct RIFF_HEADER_DEF), 1, fp) != 1) goto __exit;
-    if (fread(&(info->fmt_block),  sizeof(struct FMT_BLOCK_DEF),   1, fp) != 1) goto __exit;
-    if (fread(&(info->data_block), sizeof(struct DATA_BLOCK_DEF),  1, fp) != 1) goto __exit;
-
-    printf("wav information:\n");
-    printf("samplerate %u\n", info->fmt_block.wav_format.SamplesPerSec);
-    printf("channel %u\n", info->fmt_block.wav_format.Channels);
-
-    audio_device_init();
-    audio_device_open();
-    audio_device_set_rate(info->fmt_block.wav_format.SamplesPerSec);
-
-    while (!feof(fp))
-    {
-        int length;
-
-        buffer = (uint16_t *)audio_device_get_buffer(RT_NULL);
-
-        length = fread(buffer, 1, BUFSZ, fp);
-        if (length)
-        {
-            if (info->fmt_block.wav_format.Channels == 1)
-            {
-                /* extend to stereo channels */
-                int index;
-                uint16_t *ptr;
-
-                ptr = (uint16_t *)((uint8_t *)buffer + BUFSZ * 2);
-                for (index = 1; index < BUFSZ / 2; index ++)
-                {
-                    *ptr = *(ptr - 1) = buffer[BUFSZ / 2 - index];
-                    ptr -= 2;
-                }
-
-                length = length * 2;
-            }
-
-            audio_device_write((uint8_t *)buffer, length);
-        }
-        else
-        {
-            audio_device_put_buffer((uint8_t *)buffer);
-            break;
-        }
-    }
-    audio_device_close();
-
-__exit:
-    if (fp) fclose(fp);
-    if (info) free(info);
-}
-
-int wavplay(int argc, char **argv)
-{
-    rt_thread_t tid = RT_NULL;
-
-    if (argc != 2)
-    {
-        printf("Usage:\n");
-        printf("wavplay song.wav\n");
-        return 0;
-    }
-
-    memset(file_name, 0, sizeof(file_name));
-    memcpy(file_name, argv[1], strlen(argv[1]));
-
-    tid = rt_thread_create("wayplay",
-                           wavplay_thread_entry,
-                           RT_NULL,
-                           1024 * 8,
-                           22,
-                           10);
-    if (tid != RT_NULL)
-        rt_thread_startup(tid);
-}
-
-MSH_CMD_EXPORT(wavplay, wavplay song.wav);

File diff suppressed because it is too large
+ 2 - 2
bsp/stm32/stm32l475-atk-pandora/board/CubeMX_Config/.mxproject


+ 1 - 1
bsp/stm32/stm32l475-atk-pandora/board/CubeMX_Config/Inc/stm32l4xx_hal_conf.h

@@ -81,7 +81,7 @@
 #define HAL_QSPI_MODULE_ENABLED
 #define HAL_QSPI_MODULE_ENABLED
 /*#define HAL_RNG_MODULE_ENABLED   */
 /*#define HAL_RNG_MODULE_ENABLED   */
 #define HAL_RTC_MODULE_ENABLED
 #define HAL_RTC_MODULE_ENABLED
-/*#define HAL_SAI_MODULE_ENABLED   */
+#define HAL_SAI_MODULE_ENABLED
 /*#define HAL_SD_MODULE_ENABLED   */
 /*#define HAL_SD_MODULE_ENABLED   */
 /*#define HAL_SMBUS_MODULE_ENABLED   */
 /*#define HAL_SMBUS_MODULE_ENABLED   */
 /*#define HAL_SMARTCARD_MODULE_ENABLED   */
 /*#define HAL_SMARTCARD_MODULE_ENABLED   */

+ 89 - 53
bsp/stm32/stm32l475-atk-pandora/board/CubeMX_Config/STM32L475VE.ioc

@@ -11,65 +11,73 @@ KeepUserPlacement=false
 Mcu.Family=STM32L4
 Mcu.Family=STM32L4
 Mcu.IP0=ADC1
 Mcu.IP0=ADC1
 Mcu.IP1=IWDG
 Mcu.IP1=IWDG
-Mcu.IP10=TIM2
-Mcu.IP11=TIM4
-Mcu.IP12=TIM15
-Mcu.IP13=TIM16
-Mcu.IP14=TIM17
-Mcu.IP15=USART1
-Mcu.IP16=USART2
-Mcu.IP17=USB_OTG_FS
+Mcu.IP10=TIM1
+Mcu.IP11=TIM2
+Mcu.IP12=TIM4
+Mcu.IP13=TIM15
+Mcu.IP14=TIM16
+Mcu.IP15=TIM17
+Mcu.IP16=USART1
+Mcu.IP17=USART2
+Mcu.IP18=USB_OTG_FS
 Mcu.IP2=NVIC
 Mcu.IP2=NVIC
 Mcu.IP3=QUADSPI
 Mcu.IP3=QUADSPI
 Mcu.IP4=RCC
 Mcu.IP4=RCC
 Mcu.IP5=RTC
 Mcu.IP5=RTC
-Mcu.IP6=SPI1
-Mcu.IP7=SPI2
-Mcu.IP8=SYS
-Mcu.IP9=TIM1
-Mcu.IPNb=18
+Mcu.IP6=SAI1
+Mcu.IP7=SPI1
+Mcu.IP8=SPI2
+Mcu.IP9=SYS
+Mcu.IPNb=19
 Mcu.Name=STM32L475V(C-E-G)Tx
 Mcu.Name=STM32L475V(C-E-G)Tx
 Mcu.Package=LQFP100
 Mcu.Package=LQFP100
-Mcu.Pin0=PC14-OSC32_IN (PC14)
-Mcu.Pin1=PC15-OSC32_OUT (PC15)
-Mcu.Pin10=PE9
-Mcu.Pin11=PE10
-Mcu.Pin12=PE11
-Mcu.Pin13=PE12
-Mcu.Pin14=PE13
-Mcu.Pin15=PE14
-Mcu.Pin16=PE15
-Mcu.Pin17=PB10
-Mcu.Pin18=PB11
-Mcu.Pin19=PB13
-Mcu.Pin2=PH0-OSC_IN (PH0)
-Mcu.Pin20=PB14
-Mcu.Pin21=PB15
-Mcu.Pin22=PA9
-Mcu.Pin23=PA10
-Mcu.Pin24=PA11
-Mcu.Pin25=PA12
-Mcu.Pin26=PA13 (JTMS-SWDIO)
-Mcu.Pin27=PA14 (JTCK-SWCLK)
-Mcu.Pin28=PB7
-Mcu.Pin29=PB8
-Mcu.Pin3=PH1-OSC_OUT (PH1)
-Mcu.Pin30=VP_IWDG_VS_IWDG
-Mcu.Pin31=VP_RTC_VS_RTC_Activate
-Mcu.Pin32=VP_SYS_VS_Systick
-Mcu.Pin33=VP_TIM1_VS_ClockSourceINT
-Mcu.Pin34=VP_TIM2_VS_ClockSourceINT
-Mcu.Pin35=VP_TIM4_VS_ClockSourceINT
-Mcu.Pin36=VP_TIM15_VS_ClockSourceINT
-Mcu.Pin37=VP_TIM16_VS_ClockSourceINT
-Mcu.Pin38=VP_TIM17_VS_ClockSourceINT
-Mcu.Pin4=PA2
-Mcu.Pin5=PA3
-Mcu.Pin6=PA5
-Mcu.Pin7=PA6
-Mcu.Pin8=PA7
-Mcu.Pin9=PC5
-Mcu.PinsNb=39
+Mcu.Pin0=PE2
+Mcu.Pin1=PE3
+Mcu.Pin10=PA3
+Mcu.Pin11=PA5
+Mcu.Pin12=PA6
+Mcu.Pin13=PA7
+Mcu.Pin14=PC5
+Mcu.Pin15=PE9
+Mcu.Pin16=PE10
+Mcu.Pin17=PE11
+Mcu.Pin18=PE12
+Mcu.Pin19=PE13
+Mcu.Pin2=PE4
+Mcu.Pin20=PE14
+Mcu.Pin21=PE15
+Mcu.Pin22=PB10
+Mcu.Pin23=PB11
+Mcu.Pin24=PB13
+Mcu.Pin25=PB14
+Mcu.Pin26=PB15
+Mcu.Pin27=PA9
+Mcu.Pin28=PA10
+Mcu.Pin29=PA11
+Mcu.Pin3=PE5
+Mcu.Pin30=PA12
+Mcu.Pin31=PA13 (JTMS-SWDIO)
+Mcu.Pin32=PA14 (JTCK-SWCLK)
+Mcu.Pin33=PB7
+Mcu.Pin34=PB8
+Mcu.Pin35=VP_IWDG_VS_IWDG
+Mcu.Pin36=VP_RTC_VS_RTC_Activate
+Mcu.Pin37=VP_SAI1_VP_$IpInstance_SAIA_SAI_BASIC
+Mcu.Pin38=VP_SAI1_VP_$IpInstance_SAIB_SAI_BASIC
+Mcu.Pin39=VP_SYS_VS_Systick
+Mcu.Pin4=PE6
+Mcu.Pin40=VP_TIM1_VS_ClockSourceINT
+Mcu.Pin41=VP_TIM2_VS_ClockSourceINT
+Mcu.Pin42=VP_TIM4_VS_ClockSourceINT
+Mcu.Pin43=VP_TIM15_VS_ClockSourceINT
+Mcu.Pin44=VP_TIM16_VS_ClockSourceINT
+Mcu.Pin45=VP_TIM17_VS_ClockSourceINT
+Mcu.Pin5=PC14-OSC32_IN (PC14)
+Mcu.Pin6=PC15-OSC32_OUT (PC15)
+Mcu.Pin7=PH0-OSC_IN (PH0)
+Mcu.Pin8=PH1-OSC_OUT (PH1)
+Mcu.Pin9=PA2
+Mcu.PinsNb=46
 Mcu.ThirdPartyNb=0
 Mcu.ThirdPartyNb=0
 Mcu.UserConstants=
 Mcu.UserConstants=
 Mcu.UserName=STM32L475VETx
 Mcu.UserName=STM32L475VETx
@@ -152,6 +160,16 @@ PE14.Signal=QUADSPI_BK1_IO2
 PE15.Locked=true
 PE15.Locked=true
 PE15.Mode=Single Bank
 PE15.Mode=Single Bank
 PE15.Signal=QUADSPI_BK1_IO3
 PE15.Signal=QUADSPI_BK1_IO3
+PE2.Mode=SAI_A_MasterWithClock
+PE2.Signal=SAI1_MCLK_A
+PE3.Mode=SAI_B_SyncSlave
+PE3.Signal=SAI1_SD_B
+PE4.Mode=SAI_A_MasterWithClock
+PE4.Signal=SAI1_FS_A
+PE5.Mode=SAI_A_MasterWithClock
+PE5.Signal=SAI1_SCK_A
+PE6.Mode=SAI_A_MasterWithClock
+PE6.Signal=SAI1_SD_A
 PE9.Signal=S_TIM1_CH1
 PE9.Signal=S_TIM1_CH1
 PH0-OSC_IN\ (PH0).Mode=HSE-External-Oscillator
 PH0-OSC_IN\ (PH0).Mode=HSE-External-Oscillator
 PH0-OSC_IN\ (PH0).Signal=RCC_OSC_IN
 PH0-OSC_IN\ (PH0).Signal=RCC_OSC_IN
@@ -240,6 +258,20 @@ RCC.VCOInputFreq_Value=8000000
 RCC.VCOOutputFreq_Value=160000000
 RCC.VCOOutputFreq_Value=160000000
 RCC.VCOSAI1OutputFreq_Value=96000000
 RCC.VCOSAI1OutputFreq_Value=96000000
 RCC.VCOSAI2OutputFreq_Value=64000000
 RCC.VCOSAI2OutputFreq_Value=64000000
+SAI1.AudioFrequency-SAI_A_MasterWithClock=SAI_AUDIO_FREQUENCY_44K
+SAI1.ErrorAudioFreq-SAI_A_MasterWithClock=-72.09 %
+SAI1.IPParameters=Instance-SAI_A_MasterWithClock,VirtualMode-SAI_A_MasterWithClock,MClockEnable-SAI_A_MasterWithClock,RealAudioFreq-SAI_A_MasterWithClock,ErrorAudioFreq-SAI_A_MasterWithClock,InitProtocol-SAI_A_MasterWithClock,VirtualProtocol-SAI_A_BASIC,AudioFrequency-SAI_A_MasterWithClock,OutputDrive-SAI_A_MasterWithClock,Instance-SAI_B_SyncSlave,VirtualMode-SAI_B_SyncSlave,InitProtocol-SAI_B_SyncSlave,VirtualProtocol-SAI_B_BASIC
+SAI1.InitProtocol-SAI_A_MasterWithClock=Enable
+SAI1.InitProtocol-SAI_B_SyncSlave=Enable
+SAI1.Instance-SAI_A_MasterWithClock=SAI$Index_Block_A
+SAI1.Instance-SAI_B_SyncSlave=SAI$Index_Block_B
+SAI1.MClockEnable-SAI_A_MasterWithClock=SAI_MASTERCLOCK_ENABLE
+SAI1.OutputDrive-SAI_A_MasterWithClock=SAI_OUTPUTDRIVE_ENABLE
+SAI1.RealAudioFreq-SAI_A_MasterWithClock=53.571 KHz
+SAI1.VirtualMode-SAI_A_MasterWithClock=VM_MASTER
+SAI1.VirtualMode-SAI_B_SyncSlave=VM_SLAVE
+SAI1.VirtualProtocol-SAI_A_BASIC=VM_BASIC_PROTOCOL
+SAI1.VirtualProtocol-SAI_B_BASIC=VM_BASIC_PROTOCOL
 SH.ADCx_IN14.0=ADC1_IN14,IN14-Single-Ended
 SH.ADCx_IN14.0=ADC1_IN14,IN14-Single-Ended
 SH.ADCx_IN14.ConfNb=1
 SH.ADCx_IN14.ConfNb=1
 SH.S_TIM1_CH1.0=TIM1_CH1,PWM Generation1 CH1
 SH.S_TIM1_CH1.0=TIM1_CH1,PWM Generation1 CH1
@@ -280,6 +312,10 @@ VP_IWDG_VS_IWDG.Mode=IWDG_Activate
 VP_IWDG_VS_IWDG.Signal=IWDG_VS_IWDG
 VP_IWDG_VS_IWDG.Signal=IWDG_VS_IWDG
 VP_RTC_VS_RTC_Activate.Mode=RTC_Enabled
 VP_RTC_VS_RTC_Activate.Mode=RTC_Enabled
 VP_RTC_VS_RTC_Activate.Signal=RTC_VS_RTC_Activate
 VP_RTC_VS_RTC_Activate.Signal=RTC_VS_RTC_Activate
+VP_SAI1_VP_$IpInstance_SAIA_SAI_BASIC.Mode=SAI_A_BASIC
+VP_SAI1_VP_$IpInstance_SAIA_SAI_BASIC.Signal=SAI1_VP_$IpInstance_SAIA_SAI_BASIC
+VP_SAI1_VP_$IpInstance_SAIB_SAI_BASIC.Mode=SAI_B_BASIC
+VP_SAI1_VP_$IpInstance_SAIB_SAI_BASIC.Signal=SAI1_VP_$IpInstance_SAIB_SAI_BASIC
 VP_SYS_VS_Systick.Mode=SysTick
 VP_SYS_VS_Systick.Mode=SysTick
 VP_SYS_VS_Systick.Signal=SYS_VS_Systick
 VP_SYS_VS_Systick.Signal=SYS_VS_Systick
 VP_TIM15_VS_ClockSourceINT.Mode=Internal
 VP_TIM15_VS_ClockSourceINT.Mode=Internal

+ 126 - 4
bsp/stm32/stm32l475-atk-pandora/board/CubeMX_Config/Src/main.c

@@ -70,10 +70,14 @@ QSPI_HandleTypeDef hqspi;
 
 
 RTC_HandleTypeDef hrtc;
 RTC_HandleTypeDef hrtc;
 
 
+SAI_HandleTypeDef hsai_BlockA1;
+SAI_HandleTypeDef hsai_BlockB1;
+
 SPI_HandleTypeDef hspi1;
 SPI_HandleTypeDef hspi1;
 SPI_HandleTypeDef hspi2;
 SPI_HandleTypeDef hspi2;
 
 
 TIM_HandleTypeDef htim1;
 TIM_HandleTypeDef htim1;
+TIM_HandleTypeDef htim2;
 TIM_HandleTypeDef htim4;
 TIM_HandleTypeDef htim4;
 TIM_HandleTypeDef htim15;
 TIM_HandleTypeDef htim15;
 TIM_HandleTypeDef htim16;
 TIM_HandleTypeDef htim16;
@@ -105,6 +109,8 @@ static void MX_TIM16_Init(void);
 static void MX_TIM15_Init(void);
 static void MX_TIM15_Init(void);
 static void MX_TIM4_Init(void);
 static void MX_TIM4_Init(void);
 static void MX_TIM1_Init(void);
 static void MX_TIM1_Init(void);
+static void MX_SAI1_Init(void);
+static void MX_TIM2_Init(void);
 static void MX_USB_OTG_FS_PCD_Init(void);
 static void MX_USB_OTG_FS_PCD_Init(void);
 /* USER CODE BEGIN PFP */
 /* USER CODE BEGIN PFP */
 /* Private function prototypes -----------------------------------------------*/
 /* Private function prototypes -----------------------------------------------*/
@@ -158,6 +164,8 @@ int main(void)
   MX_TIM15_Init();
   MX_TIM15_Init();
   MX_TIM4_Init();
   MX_TIM4_Init();
   MX_TIM1_Init();
   MX_TIM1_Init();
+  MX_SAI1_Init();
+  MX_TIM2_Init();
   MX_USB_OTG_FS_PCD_Init();
   MX_USB_OTG_FS_PCD_Init();
   /* USER CODE BEGIN 2 */
   /* USER CODE BEGIN 2 */
 
 
@@ -222,10 +230,11 @@ void SystemClock_Config(void)
     Error_Handler();
     Error_Handler();
   }
   }
   PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USART1
   PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USART1
-                              |RCC_PERIPHCLK_USART2|RCC_PERIPHCLK_USB
-                              |RCC_PERIPHCLK_ADC;
+                              |RCC_PERIPHCLK_USART2|RCC_PERIPHCLK_SAI1
+                              |RCC_PERIPHCLK_USB|RCC_PERIPHCLK_ADC;
   PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
   PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
   PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
   PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
+  PeriphClkInit.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI1;
   PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1;
   PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1;
   PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
   PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
   PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLLSAI1;
   PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLLSAI1;
@@ -235,7 +244,8 @@ void SystemClock_Config(void)
   PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7;
   PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7;
   PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2;
   PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2;
   PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2;
   PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2;
-  PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_48M2CLK|RCC_PLLSAI1_ADC1CLK;
+  PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_SAI1CLK|RCC_PLLSAI1_48M2CLK
+                              |RCC_PLLSAI1_ADC1CLK;
   if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
   if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
   {
   {
     Error_Handler();
     Error_Handler();
@@ -409,6 +419,55 @@ static void MX_RTC_Init(void)
 
 
 }
 }
 
 
+/**
+  * @brief SAI1 Initialization Function
+  * @param None
+  * @retval None
+  */
+static void MX_SAI1_Init(void)
+{
+
+  /* USER CODE BEGIN SAI1_Init 0 */
+
+  /* USER CODE END SAI1_Init 0 */
+
+  /* USER CODE BEGIN SAI1_Init 1 */
+
+  /* USER CODE END SAI1_Init 1 */
+  hsai_BlockA1.Instance = SAI1_Block_A;
+  hsai_BlockA1.Init.AudioMode = SAI_MODEMASTER_TX;
+  hsai_BlockA1.Init.Synchro = SAI_ASYNCHRONOUS;
+  hsai_BlockA1.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLE;
+  hsai_BlockA1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
+  hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
+  hsai_BlockA1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_44K;
+  hsai_BlockA1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
+  hsai_BlockA1.Init.MonoStereoMode = SAI_STEREOMODE;
+  hsai_BlockA1.Init.CompandingMode = SAI_NOCOMPANDING;
+  hsai_BlockA1.Init.TriState = SAI_OUTPUT_NOTRELEASED;
+  if (HAL_SAI_InitProtocol(&hsai_BlockA1, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_16BIT, 2) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  hsai_BlockB1.Instance = SAI1_Block_B;
+  hsai_BlockB1.Init.AudioMode = SAI_MODESLAVE_RX;
+  hsai_BlockB1.Init.Synchro = SAI_SYNCHRONOUS;
+  hsai_BlockB1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
+  hsai_BlockB1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
+  hsai_BlockB1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
+  hsai_BlockB1.Init.MonoStereoMode = SAI_STEREOMODE;
+  hsai_BlockB1.Init.CompandingMode = SAI_NOCOMPANDING;
+  hsai_BlockB1.Init.TriState = SAI_OUTPUT_NOTRELEASED;
+  if (HAL_SAI_InitProtocol(&hsai_BlockB1, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_16BIT, 2) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  /* USER CODE BEGIN SAI1_Init 2 */
+
+  /* USER CODE END SAI1_Init 2 */
+
+}
+
 /**
 /**
   * @brief SPI1 Initialization Function
   * @brief SPI1 Initialization Function
   * @param None
   * @param None
@@ -569,6 +628,69 @@ static void MX_TIM1_Init(void)
 
 
 }
 }
 
 
+/**
+  * @brief TIM2 Initialization Function
+  * @param None
+  * @retval None
+  */
+static void MX_TIM2_Init(void)
+{
+
+  /* USER CODE BEGIN TIM2_Init 0 */
+
+  /* USER CODE END TIM2_Init 0 */
+
+  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
+  TIM_MasterConfigTypeDef sMasterConfig = {0};
+  TIM_OC_InitTypeDef sConfigOC = {0};
+
+  /* USER CODE BEGIN TIM2_Init 1 */
+
+  /* USER CODE END TIM2_Init 1 */
+  htim2.Instance = TIM2;
+  htim2.Init.Prescaler = 0;
+  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
+  htim2.Init.Period = 0;
+  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
+  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
+  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
+  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
+  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
+  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  sConfigOC.OCMode = TIM_OCMODE_PWM1;
+  sConfigOC.Pulse = 0;
+  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
+  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
+  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  /* USER CODE BEGIN TIM2_Init 2 */
+
+  /* USER CODE END TIM2_Init 2 */
+  HAL_TIM_MspPostInit(&htim2);
+
+}
+
 /**
 /**
   * @brief TIM4 Initialization Function
   * @brief TIM4 Initialization Function
   * @param None
   * @param None
@@ -856,10 +978,10 @@ static void MX_GPIO_Init(void)
 {
 {
 
 
   /* GPIO Ports Clock Enable */
   /* GPIO Ports Clock Enable */
+  __HAL_RCC_GPIOE_CLK_ENABLE();
   __HAL_RCC_GPIOC_CLK_ENABLE();
   __HAL_RCC_GPIOC_CLK_ENABLE();
   __HAL_RCC_GPIOH_CLK_ENABLE();
   __HAL_RCC_GPIOH_CLK_ENABLE();
   __HAL_RCC_GPIOA_CLK_ENABLE();
   __HAL_RCC_GPIOA_CLK_ENABLE();
-  __HAL_RCC_GPIOE_CLK_ENABLE();
   __HAL_RCC_GPIOB_CLK_ENABLE();
   __HAL_RCC_GPIOB_CLK_ENABLE();
 
 
 }
 }

+ 91 - 0
bsp/stm32/stm32l475-atk-pandora/board/CubeMX_Config/Src/stm32l4xx_hal_msp.c

@@ -814,6 +814,97 @@ void HAL_PCD_MspDeInit(PCD_HandleTypeDef* hpcd)
 
 
 }
 }
 
 
+static uint32_t SAI1_client =0;
+
+void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)
+{
+
+  GPIO_InitTypeDef GPIO_InitStruct;
+/* SAI1 */
+    if(hsai->Instance==SAI1_Block_A)
+    {
+    /* Peripheral clock enable */
+    if (SAI1_client == 0)
+    {
+       __HAL_RCC_SAI1_CLK_ENABLE();
+    }
+    SAI1_client ++;
+    
+    /**SAI1_A_Block_A GPIO Configuration    
+    PE2     ------> SAI1_MCLK_A
+    PE4     ------> SAI1_FS_A
+    PE5     ------> SAI1_SCK_A
+    PE6     ------> SAI1_SD_A 
+    */
+    GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6;
+    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
+    GPIO_InitStruct.Alternate = GPIO_AF13_SAI1;
+    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
+
+    }
+    if(hsai->Instance==SAI1_Block_B)
+    {
+      /* Peripheral clock enable */
+      if (SAI1_client == 0)
+      {
+       __HAL_RCC_SAI1_CLK_ENABLE();
+      }
+    SAI1_client ++;
+    
+    /**SAI1_B_Block_B GPIO Configuration    
+    PE3     ------> SAI1_SD_B 
+    */
+    GPIO_InitStruct.Pin = GPIO_PIN_3;
+    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
+    GPIO_InitStruct.Alternate = GPIO_AF13_SAI1;
+    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
+
+    }
+}
+
+void HAL_SAI_MspDeInit(SAI_HandleTypeDef* hsai)
+{
+
+/* SAI1 */
+    if(hsai->Instance==SAI1_Block_A)
+    {
+    SAI1_client --;
+    if (SAI1_client == 0)
+      {
+      /* Peripheral clock disable */ 
+       __HAL_RCC_SAI1_CLK_DISABLE();
+      }
+    
+    /**SAI1_A_Block_A GPIO Configuration    
+    PE2     ------> SAI1_MCLK_A
+    PE4     ------> SAI1_FS_A
+    PE5     ------> SAI1_SCK_A
+    PE6     ------> SAI1_SD_A 
+    */
+    HAL_GPIO_DeInit(GPIOE, GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6);
+
+    }
+    if(hsai->Instance==SAI1_Block_B)
+    {
+    SAI1_client --;
+      if (SAI1_client == 0)
+      {
+      /* Peripheral clock disable */
+      __HAL_RCC_SAI1_CLK_DISABLE();
+      }
+    
+    /**SAI1_B_Block_B GPIO Configuration    
+    PE3     ------> SAI1_SD_B 
+    */
+    HAL_GPIO_DeInit(GPIOE, GPIO_PIN_3);
+
+    }
+}
+
 /* USER CODE BEGIN 1 */
 /* USER CODE BEGIN 1 */
 
 
 /* USER CODE END 1 */
 /* USER CODE END 1 */

+ 17 - 0
bsp/stm32/stm32l475-atk-pandora/board/Kconfig

@@ -47,6 +47,23 @@ menu "Onboard Peripheral Drivers"
         select PKG_USING_AHT10_LATEST_VERSION
         select PKG_USING_AHT10_LATEST_VERSION
         default n
         default n
 
 
+    menuconfig BSP_USING_AUDIO
+        bool "Enable Audio Device"
+        select RT_USING_AUDIO
+        select BSP_USING_I2C
+        select BSP_USING_I2C3
+        default n
+
+        if BSP_USING_AUDIO
+            config BSP_USING_AUDIO_PLAY
+            bool "Enable Audio Play"
+            default y
+
+            config BSP_USING_AUDIO_RECORD
+            bool "Enable Audio Record"
+            default n
+        endif
+
 endmenu
 endmenu
 
 
 menu "On-chip Peripheral Drivers"
 menu "On-chip Peripheral Drivers"

+ 10 - 0
bsp/stm32/stm32l475-atk-pandora/board/SConscript

@@ -21,10 +21,20 @@ if GetDepend(['BSP_USING_SDCARD']):
 if GetDepend(['BSP_USING_ICM20608']) or GetDepend(['BSP_USING_AHT10']):
 if GetDepend(['BSP_USING_ICM20608']) or GetDepend(['BSP_USING_AHT10']):
     src += Glob('ports/sensor_port.c')
     src += Glob('ports/sensor_port.c')
 
 
+if GetDepend(['BSP_USING_AUDIO']):
+    src += Glob('ports/audio/drv_es8388.c')
+    src += Glob('ports/audio/drv_sound.c')
+
+if GetDepend(['BSP_USING_AUDIO_RECORD']):
+    src += Glob('ports/audio/drv_mic.c')
+
 path =  [cwd]
 path =  [cwd]
 path += [cwd + '/CubeMX_Config/Inc']
 path += [cwd + '/CubeMX_Config/Inc']
 path += [cwd + '/ports']
 path += [cwd + '/ports']
 
 
+if GetDepend(['BSP_USING_AUDIO']):
+    path += [cwd + '/ports/audio']
+
 startup_path_prefix = SDK_LIB
 startup_path_prefix = SDK_LIB
 
 
 if rtconfig.CROSS_TOOL == 'gcc':
 if rtconfig.CROSS_TOOL == 'gcc':

+ 287 - 0
bsp/stm32/stm32l475-atk-pandora/board/ports/audio/drv_es8388.c

@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Date           Author       Notes
+ * 2019-07-31     Zero-Free    first implementation
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include "drv_es8388.h"
+
+/* ES8388 address */
+#define ES8388_ADDR 0x10  /*0x11:CE=1;0x10:CE=0*/
+
+struct es8388_device
+{
+    struct rt_i2c_bus_device *i2c;
+    rt_uint16_t pin;
+};
+
+static struct es8388_device es_dev = {0};
+
+static rt_uint16_t reg_read(rt_uint8_t addr)
+{
+    struct rt_i2c_msg msg[2] = {0};
+    uint8_t val = 0xff;
+
+    RT_ASSERT(es_dev.i2c != RT_NULL);
+
+    msg[0].addr  = ES8388_ADDR;
+    msg[0].flags = RT_I2C_WR;
+    msg[0].len   = 1;
+    msg[0].buf   = &addr;
+
+    msg[1].addr  = ES8388_ADDR;
+    msg[1].flags = RT_I2C_RD;
+    msg[1].len   = 1;
+    msg[1].buf   = &val;
+
+    if (rt_i2c_transfer(es_dev.i2c, msg, 2) != 2)
+    {
+        rt_kprintf("I2C read data failed, reg = 0x%02x. \n", addr);
+        return 0xff;
+    }
+
+    return val;
+}
+
+static void reg_write(rt_uint8_t addr, rt_uint8_t val)
+{
+    struct rt_i2c_msg msgs[1] = {0};
+    rt_uint8_t buff[2] = {0};
+
+    RT_ASSERT(es_dev.i2c != RT_NULL);
+
+    buff[0] = addr;
+    buff[1] = val;
+
+    msgs[0].addr  = ES8388_ADDR;
+    msgs[0].flags = RT_I2C_WR;
+    msgs[0].buf   = buff;
+    msgs[0].len   = 2;
+
+    if (rt_i2c_transfer(es_dev.i2c, msgs, 1) != 1)
+    {
+        rt_kprintf("I2C write data failed, reg = 0x%2x. \n", addr);
+        return;
+    }
+}
+
+static int es8388_set_adc_dac_volume(int mode, int volume, int dot)
+{
+    int res = 0;
+    if (volume < -96 || volume > 0)
+    {
+        if (volume < -96)
+            volume = -96;
+        else
+            volume = 0;
+    }
+    dot = (dot >= 5 ? 1 : 0);
+    volume = (-volume << 1) + dot;
+    if (mode == ES_MODE_ADC || mode == ES_MODE_DAC_ADC)
+    {
+        reg_write(ES8388_ADCCONTROL8, volume);
+        reg_write(ES8388_ADCCONTROL9, volume);  //ADC Right Volume=0db
+    }
+    if (mode == ES_MODE_DAC || mode == ES_MODE_DAC_ADC)
+    {
+        reg_write(ES8388_DACCONTROL5, volume);
+        reg_write(ES8388_DACCONTROL4, volume);
+    }
+    return res;
+}
+
+void es8388_set_voice_mute(rt_bool_t enable)
+{
+    uint8_t reg = 0;
+
+    reg = reg_read(ES8388_DACCONTROL3);
+    reg = reg & 0xFB;
+    reg_write(ES8388_DACCONTROL3, reg | (((int)enable) << 2));
+}
+
+rt_err_t es8388_init(const char *i2c_name, rt_uint16_t pin)
+{
+    es_dev.i2c = rt_i2c_bus_device_find(i2c_name);
+    if (es_dev.i2c == RT_NULL)
+    {
+        rt_kprintf("%s bus not found\n", i2c_name);
+        return -RT_ERROR;
+    }
+
+    es_dev.pin = pin;
+
+    reg_write(ES8388_DACCONTROL3, 0x04);  // 0x04 mute/0x00 unmute&ramp;DAC unmute and  disabled digital volume control soft ramp
+    /* Chip Control and Power Management */
+    reg_write(ES8388_CONTROL2, 0x50);
+    reg_write(ES8388_CHIPPOWER, 0x00); //normal all and power up all
+    reg_write(ES8388_MASTERMODE, 0x00); //TODO:CODEC IN I2S SLAVE MODE
+
+    /* dac */
+    reg_write(ES8388_DACPOWER, 0xC0);  //disable DAC and disable Lout/Rout/1/2
+    reg_write(ES8388_CONTROL1, 0x12);  //Enfr=0,Play&Record Mode,(0x17-both of mic&paly)
+    // reg_write(ES8388_CONTROL2, 0);  //LPVrefBuf=0,Pdn_ana=0
+    reg_write(ES8388_DACCONTROL1, 0x18);//1a 0x18:16bit iis , 0x00:24
+    reg_write(ES8388_DACCONTROL2, 0x02);  //DACFsMode,SINGLE SPEED; DACFsRatio,256
+    reg_write(ES8388_DACCONTROL16, 0x00); // 0x00 audio on LIN1&RIN1,  0x09 LIN2&RIN2
+    reg_write(ES8388_DACCONTROL17, 0x90); // only left DAC to left mixer enable 0db
+    reg_write(ES8388_DACCONTROL20, 0x90); // only right DAC to right mixer enable 0db
+    reg_write(ES8388_DACCONTROL21, 0x80); //set internal ADC and DAC use the same LRCK clock, ADC LRCK as internal LRCK
+    reg_write(ES8388_DACCONTROL23, 0x00);   //vroi=0
+    es8388_set_adc_dac_volume(ES_MODE_DAC, 0, 0);          // 0db
+
+    reg_write(ES8388_DACPOWER, 0x3c);  //0x3c Enable DAC and Enable Lout/Rout/1/2
+    /* adc */
+    reg_write(ES8388_ADCPOWER, 0xFF);
+    reg_write(ES8388_ADCCONTROL1, 0xbb); // MIC Left and Right channel PGA gain
+    reg_write(ES8388_ADCCONTROL2, 0x00);  //0x00 LINSEL & RINSEL, LIN1/RIN1 as ADC Input; DSSEL,use one DS Reg11; DSR, LINPUT1-RINPUT1
+    reg_write(ES8388_ADCCONTROL3, 0x02);
+    reg_write(ES8388_ADCCONTROL4, 0x0d); // Left/Right data, Left/Right justified mode, Bits length, I2S format
+    reg_write(ES8388_ADCCONTROL5, 0x02);  //ADCFsMode,singel SPEED,RATIO=256
+    //ALC for Microphone
+    es8388_set_adc_dac_volume(ES_MODE_ADC, 0, 0);      // 0db
+    reg_write(ES8388_ADCPOWER, 0x09); //Power on ADC, Enable LIN&RIN, Power off MICBIAS, set int1lp to low power mode
+    /* enable es8388 PA */
+    es8388_pa_power(RT_TRUE);
+
+    return RT_EOK;
+}
+
+rt_err_t es8388_start(enum es8388_mode mode)
+{
+    int res = 0;
+    uint8_t prev_data = 0, data = 0;
+
+    prev_data = reg_read(ES8388_DACCONTROL21);
+    if (mode == ES_MODE_LINE)
+    {
+        reg_write(ES8388_DACCONTROL16, 0x09); // 0x00 audio on LIN1&RIN1,  0x09 LIN2&RIN2 by pass enable
+        reg_write(ES8388_DACCONTROL17, 0x50); // left DAC to left mixer enable  and  LIN signal to left mixer enable 0db  : bupass enable
+        reg_write(ES8388_DACCONTROL20, 0x50); // right DAC to right mixer enable  and  LIN signal to right mixer enable 0db : bupass enable
+        reg_write(ES8388_DACCONTROL21, 0xC0); //enable adc
+    }
+    else
+    {
+        reg_write(ES8388_DACCONTROL21, 0x80);   //enable dac
+    }
+    data = reg_read(ES8388_DACCONTROL21);
+
+    if (prev_data != data)
+    {
+        reg_write(ES8388_CHIPPOWER, 0xF0);   //start state machine
+        // reg_write(ES8388_ADDR, ES8388_CONTROL1, 0x16);
+        // reg_write(ES8388_ADDR, ES8388_CONTROL2, 0x50);
+        reg_write(ES8388_CHIPPOWER, 0x00);   //start state machine
+    }
+    if (mode == ES_MODE_ADC || mode == ES_MODE_DAC_ADC || mode == ES_MODE_LINE)
+    {
+        reg_write(ES8388_ADCPOWER, 0x00);   //power up adc and line in
+    }
+    if (mode == ES_MODE_DAC || mode == ES_MODE_DAC_ADC || mode == ES_MODE_LINE)
+    {
+        reg_write(ES8388_DACPOWER, 0x3c);   //power up dac and line out
+        es8388_set_voice_mute(RT_FALSE);
+    }
+
+    return res;
+}
+
+rt_err_t es8388_stop(enum es8388_mode mode)
+{
+    int res = 0;
+    if (mode == ES_MODE_LINE)
+    {
+        reg_write(ES8388_DACCONTROL21, 0x80); //enable dac
+        reg_write(ES8388_DACCONTROL16, 0x00); // 0x00 audio on LIN1&RIN1,  0x09 LIN2&RIN2
+        reg_write(ES8388_DACCONTROL17, 0x90); // only left DAC to left mixer enable 0db
+        reg_write(ES8388_DACCONTROL20, 0x90); // only right DAC to right mixer enable 0db
+        return res;
+    }
+    if (mode == ES_MODE_DAC || mode == ES_MODE_DAC_ADC)
+    {
+        reg_write(ES8388_DACPOWER, 0x00);
+        es8388_set_voice_mute(RT_TRUE); //res |= Es8388SetAdcDacVolume(ES_MODULE_DAC, -96, 5);      // 0db
+        // reg_write(ES8388_ADDR, ES8388_DACPOWER, 0xC0);  //power down dac and line out
+    }
+    if (mode == ES_MODE_ADC || mode == ES_MODE_DAC_ADC)
+    {
+        // Es8388SetAdcDacVolume(ES_MODULE_ADC, -96, 5);      // 0db
+        reg_write(ES8388_ADCPOWER, 0xFF);  //power down adc and line in
+    }
+    if (mode == ES_MODE_DAC_ADC)
+    {
+        reg_write(ES8388_DACCONTROL21, 0x9C);   //disable mclk
+        // reg_write(ES8388_CONTROL1, 0x00);
+        // reg_write(ES8388_CONTROL2, 0x58);
+        // reg_write(ES8388_CHIPPOWER, 0xF3);  //stop state machine
+    }
+
+    return RT_EOK;
+}
+
+rt_err_t es8388_fmt_set(enum es8388_mode mode, enum es8388_format fmt)
+{
+    uint8_t reg = 0;
+
+    if (mode == ES_MODE_ADC || mode == ES_MODE_DAC_ADC)
+    {
+        reg = reg_read(ES8388_ADCCONTROL4);
+        reg = reg & 0xfc;
+        reg_write(ES8388_ADCCONTROL4, reg | fmt);
+    }
+    if (mode == ES_MODE_DAC || mode == ES_MODE_DAC_ADC)
+    {
+        reg = reg_read(ES8388_DACCONTROL1);
+        reg = reg & 0xf9;
+        reg_write(ES8388_DACCONTROL1, reg | (fmt << 1));
+    }
+
+    return RT_EOK;
+}
+
+void es8388_volume_set(rt_uint8_t volume)
+{
+    if (volume > 100)
+        volume = 100;
+    volume /= 3;
+
+    reg_write(ES8388_DACCONTROL24, volume);
+    reg_write(ES8388_DACCONTROL25, volume);
+}
+
+rt_uint8_t es8388_volume_get(void)
+{
+    rt_uint8_t volume;
+
+    volume = reg_read(ES8388_DACCONTROL24);
+    if (volume == 0xff)
+    {
+        volume = 0;
+    }
+    else
+    {
+        volume *= 3;
+        if (volume == 99)
+            volume = 100;
+    }
+
+    return volume;
+}
+
+void es8388_pa_power(rt_bool_t enable)
+{
+    rt_pin_mode(es_dev.pin, PIN_MODE_OUTPUT);
+
+    if (enable)
+    {
+        rt_pin_write(es_dev.pin, PIN_HIGH);
+    }
+    else
+    {
+        rt_pin_write(es_dev.pin, PIN_LOW);
+    }
+}

+ 95 - 0
bsp/stm32/stm32l475-atk-pandora/board/ports/audio/drv_es8388.h

@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Date           Author       Notes
+ * 2019-07-31     Zero-Free    first implementation
+ */
+
+#ifndef __DRV_ES8388_H__
+#define __DRV_ES8388_H__
+
+/* ES8388 register space */
+#define ES8388_CONTROL1         0x00
+#define ES8388_CONTROL2         0x01
+#define ES8388_CHIPPOWER        0x02
+#define ES8388_ADCPOWER         0x03
+#define ES8388_DACPOWER         0x04
+#define ES8388_CHIPLOPOW1       0x05
+#define ES8388_CHIPLOPOW2       0x06
+#define ES8388_ANAVOLMANAG      0x07
+#define ES8388_MASTERMODE       0x08
+#define ES8388_ADCCONTROL1      0x09
+#define ES8388_ADCCONTROL2      0x0a
+#define ES8388_ADCCONTROL3      0x0b
+#define ES8388_ADCCONTROL4      0x0c
+#define ES8388_ADCCONTROL5      0x0d
+#define ES8388_ADCCONTROL6      0x0e
+#define ES8388_ADCCONTROL7      0x0f
+#define ES8388_ADCCONTROL8      0x10
+#define ES8388_ADCCONTROL9      0x11
+#define ES8388_ADCCONTROL10     0x12
+#define ES8388_ADCCONTROL11     0x13
+#define ES8388_ADCCONTROL12     0x14
+#define ES8388_ADCCONTROL13     0x15
+#define ES8388_ADCCONTROL14     0x16
+
+#define ES8388_DACCONTROL1      0x17
+#define ES8388_DACCONTROL2      0x18
+#define ES8388_DACCONTROL3      0x19
+#define ES8388_DACCONTROL4      0x1a
+#define ES8388_DACCONTROL5      0x1b
+#define ES8388_DACCONTROL6      0x1c
+#define ES8388_DACCONTROL7      0x1d
+#define ES8388_DACCONTROL8      0x1e
+#define ES8388_DACCONTROL9      0x1f
+#define ES8388_DACCONTROL10     0x20
+#define ES8388_DACCONTROL11     0x21
+#define ES8388_DACCONTROL12     0x22
+#define ES8388_DACCONTROL13     0x23
+#define ES8388_DACCONTROL14     0x24
+#define ES8388_DACCONTROL15     0x25
+#define ES8388_DACCONTROL16     0x26
+#define ES8388_DACCONTROL17     0x27
+#define ES8388_DACCONTROL18     0x28
+#define ES8388_DACCONTROL19     0x29
+#define ES8388_DACCONTROL20     0x2a
+#define ES8388_DACCONTROL21     0x2b
+#define ES8388_DACCONTROL22     0x2c
+#define ES8388_DACCONTROL23     0x2d
+#define ES8388_DACCONTROL24     0x2e
+#define ES8388_DACCONTROL25     0x2f
+#define ES8388_DACCONTROL26     0x30
+#define ES8388_DACCONTROL27     0x31
+#define ES8388_DACCONTROL28     0x32
+#define ES8388_DACCONTROL29     0x33
+#define ES8388_DACCONTROL30     0x34
+
+enum es8388_mode
+{
+    ES_MODE_NONE    = 0x00,
+    ES_MODE_DAC     = 0x01,
+    ES_MODE_ADC     = 0x02,
+    ES_MODE_DAC_ADC = 0x03,
+    ES_MODE_LINE    = 0x04,
+    ES_MODE_MAX     = 0x06,
+};
+
+enum es8388_format
+{
+    ES_FMT_NORMAL = 0,
+    ES_FMT_LEFT   = 1,
+    ES_FMT_RIGHT  = 2,
+    ES_FMT_DSP    = 3,
+};
+
+rt_err_t es8388_init(const char *i2c_name, rt_uint16_t pin);
+rt_err_t es8388_start(enum es8388_mode mode);
+rt_err_t es8388_stop(enum es8388_mode mode);
+rt_err_t es8388_fmt_set(enum es8388_mode mode, enum es8388_format fmt);
+void es8388_volume_set(rt_uint8_t volume);
+rt_uint8_t es8388_volume_get(void);
+void es8388_pa_power(rt_bool_t enable);
+
+#endif

+ 369 - 0
bsp/stm32/stm32l475-atk-pandora/board/ports/audio/drv_mic.c

@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Date           Author       Notes
+ * 2019-07-31     Zero-Free    first implementation
+ */
+
+#include <board.h>
+
+#include "drv_es8388.h"
+
+#define DBG_TAG              "drv.mic"
+#define DBG_LVL              DBG_INFO
+#include <rtdbg.h>
+
+#define RX_FIFO_SIZE (1024)
+
+struct mic_device
+{
+    struct rt_audio_device audio;
+    struct rt_audio_configure record_config;
+    rt_uint8_t *rx_fifo;
+    rt_uint8_t volume;
+};
+
+static struct mic_device mic_dev = {0};
+static rt_uint16_t zero_frame[2] = {0};
+
+static SAI_HandleTypeDef SAI1B_Handler = {0};
+static DMA_HandleTypeDef SAI1_RXDMA_Handler = {0};
+
+extern SAI_HandleTypeDef SAI1A_Handler;
+extern DMA_HandleTypeDef SAI1_RXDMA_Handler;
+extern void SAIA_Frequency_Set(uint32_t frequency);
+
+void SAIB_Init(void)
+{
+    HAL_SAI_DeInit(&SAI1B_Handler);
+
+    SAI1B_Handler.Init.AudioFrequency         = SAI_AUDIO_FREQUENCY_44K;
+    SAI1B_Handler.Instance                    = SAI1_Block_B;
+    SAI1B_Handler.Init.AudioMode              = SAI_MODESLAVE_RX;
+    SAI1B_Handler.Init.Synchro                = SAI_SYNCHRONOUS;
+    SAI1B_Handler.Init.OutputDrive            = SAI_OUTPUTDRIVE_ENABLE;
+    SAI1B_Handler.Init.NoDivider              = SAI_MASTERDIVIDER_ENABLE;
+    SAI1B_Handler.Init.FIFOThreshold          = SAI_FIFOTHRESHOLD_EMPTY;
+    SAI1B_Handler.Init.MonoStereoMode         = SAI_MONOMODE;
+    SAI1B_Handler.Init.Protocol               = SAI_FREE_PROTOCOL;
+    SAI1B_Handler.Init.DataSize               = SAI_DATASIZE_16;
+    SAI1B_Handler.Init.FirstBit               = SAI_FIRSTBIT_MSB;
+    SAI1B_Handler.Init.ClockStrobing          = SAI_CLOCKSTROBING_RISINGEDGE;
+
+    SAI1B_Handler.FrameInit.FrameLength       = 64;
+    SAI1B_Handler.FrameInit.ActiveFrameLength = 32;
+    SAI1B_Handler.FrameInit.FSDefinition      = SAI_FS_CHANNEL_IDENTIFICATION;
+    SAI1B_Handler.FrameInit.FSPolarity        = SAI_FS_ACTIVE_LOW;
+    SAI1B_Handler.FrameInit.FSOffset          = SAI_FS_BEFOREFIRSTBIT;
+    SAI1B_Handler.SlotInit.FirstBitOffset     = 0;
+    SAI1B_Handler.SlotInit.SlotSize           = SAI_SLOTSIZE_32B;
+    SAI1B_Handler.SlotInit.SlotNumber         = 2;
+    SAI1B_Handler.SlotInit.SlotActive         = SAI_SLOTACTIVE_0 | SAI_SLOTACTIVE_1;
+
+    HAL_SAI_Init(&SAI1B_Handler);
+    __HAL_SAI_ENABLE(&SAI1B_Handler);
+
+    /* Configure DMA used for SAI1 */
+    __HAL_RCC_DMA2_CLK_ENABLE();
+
+    SAI1_RXDMA_Handler.Init.Request             = DMA_REQUEST_1;
+    SAI1_RXDMA_Handler.Init.Direction           = DMA_PERIPH_TO_MEMORY;
+    SAI1_RXDMA_Handler.Init.PeriphInc           = DMA_PINC_DISABLE;
+    SAI1_RXDMA_Handler.Init.MemInc              = DMA_MINC_ENABLE;
+    SAI1_RXDMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
+    SAI1_RXDMA_Handler.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;
+    SAI1_RXDMA_Handler.Init.Mode                = DMA_CIRCULAR;
+    SAI1_RXDMA_Handler.Init.Priority            = DMA_PRIORITY_HIGH;
+    SAI1_RXDMA_Handler.Instance                 = DMA2_Channel2;
+    __HAL_LINKDMA(&SAI1B_Handler, hdmarx, SAI1_RXDMA_Handler);
+    HAL_DMA_DeInit(&SAI1_RXDMA_Handler);
+    HAL_DMA_Init(&SAI1_RXDMA_Handler);
+    __HAL_DMA_ENABLE(&SAI1_RXDMA_Handler);
+
+    HAL_NVIC_SetPriority(DMA2_Channel2_IRQn, 0x01, 0);
+    HAL_NVIC_EnableIRQ(DMA2_Channel2_IRQn);
+}
+
+void SAIB_Channels_Set(uint8_t channels)
+{
+    if (channels == 1)
+    {
+        SAI1B_Handler.Init.MonoStereoMode         = SAI_MONOMODE;
+    }
+    else
+    {
+        SAI1B_Handler.Init.MonoStereoMode         = SAI_STEREOMODE;
+    }
+
+    __HAL_SAI_DISABLE(&SAI1B_Handler);
+    HAL_SAI_Init(&SAI1B_Handler);
+    __HAL_SAI_ENABLE(&SAI1B_Handler);
+}
+
+void DMA2_Channel2_IRQHandler(void)
+{
+    HAL_DMA_IRQHandler(&SAI1_RXDMA_Handler);
+}
+
+void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai)
+{
+    rt_audio_rx_done(&mic_dev.audio, &mic_dev.rx_fifo[0], RX_FIFO_SIZE / 2);
+}
+
+void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
+{
+    rt_audio_rx_done(&mic_dev.audio, &mic_dev.rx_fifo[RX_FIFO_SIZE / 2], RX_FIFO_SIZE / 2);
+}
+
+static rt_err_t mic_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
+{
+    rt_err_t result = RT_EOK;
+    struct mic_device *mic_dev;
+
+    RT_ASSERT(audio != RT_NULL);
+    mic_dev = (struct mic_device *)audio->parent.user_data;
+
+    switch (caps->main_type)
+    {
+    case AUDIO_TYPE_QUERY: /* qurey the types of hw_codec device */
+    {
+        switch (caps->sub_type)
+        {
+        case AUDIO_TYPE_QUERY:
+            caps->udata.mask = AUDIO_TYPE_INPUT | AUDIO_TYPE_MIXER;
+            break;
+
+        default:
+            result = -RT_ERROR;
+            break;
+        }
+
+        break;
+    }
+
+    case AUDIO_TYPE_INPUT: /* Provide capabilities of INPUT unit */
+    {
+        switch (caps->sub_type)
+        {
+        case AUDIO_DSP_PARAM:
+            caps->udata.config.samplerate   = mic_dev->record_config.samplerate;
+            caps->udata.config.channels     = mic_dev->record_config.channels;
+            caps->udata.config.samplebits   = mic_dev->record_config.samplebits;
+            break;
+
+        case AUDIO_DSP_SAMPLERATE:
+            caps->udata.config.samplerate   = mic_dev->record_config.samplerate;
+            break;
+
+        case AUDIO_DSP_CHANNELS:
+            caps->udata.config.channels     = mic_dev->record_config.channels;
+            break;
+
+        case AUDIO_DSP_SAMPLEBITS:
+            caps->udata.config.samplebits   = mic_dev->record_config.samplebits;
+            break;
+
+        default:
+            result = -RT_ERROR;
+            break;
+        }
+
+        break;
+    }
+
+    case AUDIO_TYPE_MIXER: /* report the Mixer Units */
+    {
+        switch (caps->sub_type)
+        {
+        case AUDIO_MIXER_QUERY:
+            caps->udata.mask = AUDIO_MIXER_VOLUME | AUDIO_MIXER_LINE;
+            break;
+
+        case AUDIO_MIXER_VOLUME:
+            caps->udata.value = mic_dev->volume;
+            break;
+
+        case AUDIO_MIXER_LINE:
+            break;
+
+        default:
+            result = -RT_ERROR;
+            break;
+        }
+
+        break;
+    }
+
+    default:
+        result = -RT_ERROR;
+        break;
+    }
+
+    return result;
+}
+
+static rt_err_t mic_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
+{
+    rt_err_t result = RT_EOK;
+    struct mic_device *mic_dev;
+
+    RT_ASSERT(audio != RT_NULL);
+    mic_dev = (struct mic_device *)audio->parent.user_data;
+
+    switch (caps->main_type)
+    {
+    case AUDIO_TYPE_MIXER:
+    {
+        switch (caps->sub_type)
+        {
+        case AUDIO_MIXER_VOLUME:
+        {
+            rt_uint32_t volume = caps->udata.value;
+            mic_dev->volume = volume;
+            LOG_D("set volume %d", volume);
+            break;
+        }
+
+        default:
+            result = -RT_ERROR;
+            break;
+        }
+
+        break;
+    }
+
+    case AUDIO_TYPE_INPUT:
+    {
+        switch (caps->sub_type)
+        {
+        case AUDIO_DSP_PARAM:
+        {
+            SAIA_Frequency_Set(caps->udata.config.samplerate);
+            HAL_SAI_DMAStop(&SAI1B_Handler);
+            SAIB_Channels_Set(caps->udata.config.channels);
+            HAL_SAI_Transmit(&SAI1A_Handler, (uint8_t *)&zero_frame[0], 2, 0);
+            HAL_SAI_Receive_DMA(&SAI1B_Handler, mic_dev->rx_fifo, RX_FIFO_SIZE / 2);
+
+            /* save configs */
+            mic_dev->record_config.samplerate = caps->udata.config.samplerate;
+            mic_dev->record_config.channels   = caps->udata.config.channels;
+            mic_dev->record_config.samplebits = caps->udata.config.samplebits;
+            LOG_D("set samplerate %d", mic_dev->record_config.samplerate);
+            LOG_D("set channels %d", mic_dev->record_config.channels);
+            break;
+        }
+
+        case AUDIO_DSP_SAMPLERATE:
+        {
+            mic_dev->record_config.samplerate = caps->udata.config.samplerate;
+            LOG_D("set channels %d", mic_dev->record_config.channels);
+            break;
+        }
+
+        case AUDIO_DSP_CHANNELS:
+        {
+            mic_dev->record_config.channels   = caps->udata.config.channels;
+            LOG_D("set channels %d", mic_dev->record_config.channels);
+            break;
+        }
+
+        default:
+            break;
+        }
+
+        break;
+    }
+
+    default:
+        break;
+    }
+
+    return result;
+}
+
+static rt_err_t mic_init(struct rt_audio_device *audio)
+{
+    struct mic_device *mic_dev;
+
+    RT_ASSERT(audio != RT_NULL);
+    mic_dev = (struct mic_device *)audio->parent.user_data;
+
+    SAIB_Init();
+
+    /* set default params */
+    SAIB_Channels_Set(mic_dev->record_config.channels);
+
+    return RT_EOK;
+}
+
+static rt_err_t mic_start(struct rt_audio_device *audio, int stream)
+{
+    struct mic_device *mic_dev;
+
+    RT_ASSERT(audio != RT_NULL);
+    mic_dev = (struct mic_device *)audio->parent.user_data;
+
+    if (stream == AUDIO_STREAM_RECORD)
+    {
+        es8388_start(ES_MODE_ADC);
+        HAL_SAI_Transmit(&SAI1A_Handler, (uint8_t *)&zero_frame[0], 2, 0);
+        HAL_SAI_Receive_DMA(&SAI1B_Handler, mic_dev->rx_fifo, RX_FIFO_SIZE / 2);
+    }
+
+    return RT_EOK;
+}
+
+static rt_err_t mic_stop(struct rt_audio_device *audio, int stream)
+{
+    if (stream == AUDIO_STREAM_RECORD)
+    {
+        HAL_SAI_DMAStop(&SAI1B_Handler);
+        HAL_SAI_Abort(&SAI1A_Handler);
+        es8388_stop(ES_MODE_ADC);
+    }
+
+    return RT_EOK;
+}
+
+static struct rt_audio_ops mic_ops =
+{
+    .getcaps     = mic_getcaps,
+    .configure   = mic_configure,
+    .init        = mic_init,
+    .start       = mic_start,
+    .stop        = mic_stop,
+    .transmit    = RT_NULL,
+    .buffer_info = RT_NULL,
+};
+
+int rt_hw_mic_init(void)
+{
+    rt_uint8_t *rx_fifo;
+
+    if (mic_dev.rx_fifo)
+        return RT_EOK;
+
+    rx_fifo = rt_malloc(RX_FIFO_SIZE);
+    if (rx_fifo == RT_NULL)
+        return -RT_ENOMEM;
+    rt_memset(rx_fifo, 0, RX_FIFO_SIZE);
+    mic_dev.rx_fifo = rx_fifo;
+
+    /* init default configuration */
+    {
+        mic_dev.record_config.samplerate = 44100;
+        mic_dev.record_config.channels   = 2;
+        mic_dev.record_config.samplebits = 16;
+        mic_dev.volume                   = 55;
+    }
+
+    /* register sound device */
+    mic_dev.audio.ops = &mic_ops;
+    rt_audio_register(&mic_dev.audio, "mic0", RT_DEVICE_FLAG_RDONLY, &mic_dev);
+
+    return RT_EOK;
+}
+
+INIT_DEVICE_EXPORT(rt_hw_mic_init);

+ 458 - 0
bsp/stm32/stm32l475-atk-pandora/board/ports/audio/drv_sound.c

@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Date           Author       Notes
+ * 2019-07-31     Zero-Free    first implementation
+ */
+
+#include <board.h>
+
+#include "drv_sound.h"
+#include "drv_es8388.h"
+
+#define DBG_TAG              "drv.sound"
+#define DBG_LVL              DBG_INFO
+#include <rtdbg.h>
+
+#define TX_FIFO_SIZE         (2048)
+
+struct sound_device
+{
+    struct rt_audio_device audio;
+    struct rt_audio_configure replay_config;
+    rt_uint8_t *tx_fifo;
+    rt_uint8_t volume;
+};
+
+static struct sound_device snd_dev = {0};
+SAI_HandleTypeDef SAI1A_Handler = {0};
+DMA_HandleTypeDef SAI1_TXDMA_Handler = {0};
+
+static void SAIA_Init(void)
+{
+    RCC_PeriphCLKInitTypeDef PeriphClkInit;
+
+    /* Configure and enable PLLSAI1 clock to generate 45.714286MHz */
+    PeriphClkInit.PeriphClockSelection        = RCC_PERIPHCLK_SAI1;
+    PeriphClkInit.Sai1ClockSelection          = RCC_SAI1CLKSOURCE_PLLSAI2;
+    PeriphClkInit.PLLSAI2.PLLSAI2Source       = RCC_PLLSOURCE_HSE;
+    PeriphClkInit.PLLSAI2.PLLSAI2M            = 1;
+    PeriphClkInit.PLLSAI2.PLLSAI2N            = 40;
+    PeriphClkInit.PLLSAI2.PLLSAI2P            = RCC_PLLP_DIV7;
+    PeriphClkInit.PLLSAI2.PLLSAI2R            = RCC_PLLR_DIV2;
+    PeriphClkInit.PLLSAI2.PLLSAI2ClockOut     = RCC_PLLSAI2_SAI2CLK;
+    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
+
+    HAL_SAI_DeInit(&SAI1A_Handler);
+    SAI1A_Handler.Init.AudioFrequency         = SAI_AUDIO_FREQUENCY_44K;
+    SAI1A_Handler.Instance                    = SAI1_Block_A;
+    SAI1A_Handler.Init.AudioMode              = SAI_MODEMASTER_TX;
+    SAI1A_Handler.Init.Synchro                = SAI_ASYNCHRONOUS;
+    SAI1A_Handler.Init.OutputDrive            = SAI_OUTPUTDRIVE_ENABLE;
+    SAI1A_Handler.Init.NoDivider              = SAI_MASTERDIVIDER_ENABLE;
+    SAI1A_Handler.Init.FIFOThreshold          = SAI_FIFOTHRESHOLD_EMPTY;
+    SAI1A_Handler.Init.MonoStereoMode         = SAI_STEREOMODE;
+    SAI1A_Handler.Init.Protocol               = SAI_FREE_PROTOCOL;
+    SAI1A_Handler.Init.DataSize               = SAI_DATASIZE_16;
+    SAI1A_Handler.Init.FirstBit               = SAI_FIRSTBIT_MSB;
+    SAI1A_Handler.Init.ClockStrobing          = SAI_CLOCKSTROBING_RISINGEDGE;
+
+    SAI1A_Handler.FrameInit.FrameLength       = 64;
+    SAI1A_Handler.FrameInit.ActiveFrameLength = 32;
+    SAI1A_Handler.FrameInit.FSDefinition      = SAI_FS_CHANNEL_IDENTIFICATION;
+    SAI1A_Handler.FrameInit.FSPolarity        = SAI_FS_ACTIVE_LOW;
+    SAI1A_Handler.FrameInit.FSOffset          = SAI_FS_BEFOREFIRSTBIT;
+    SAI1A_Handler.SlotInit.FirstBitOffset     = 0;
+    SAI1A_Handler.SlotInit.SlotSize           = SAI_SLOTSIZE_32B;
+    SAI1A_Handler.SlotInit.SlotNumber         = 2;
+    SAI1A_Handler.SlotInit.SlotActive         = SAI_SLOTACTIVE_0 | SAI_SLOTACTIVE_1;
+
+    HAL_SAI_Init(&SAI1A_Handler);
+    __HAL_SAI_ENABLE(&SAI1A_Handler);
+
+    /* Configure DMA used for SAI1 */
+    __HAL_RCC_DMA2_CLK_ENABLE();
+
+    SAI1_TXDMA_Handler.Init.Request             = DMA_REQUEST_1;
+    SAI1_TXDMA_Handler.Init.Direction           = DMA_MEMORY_TO_PERIPH;
+    SAI1_TXDMA_Handler.Init.PeriphInc           = DMA_PINC_DISABLE;
+    SAI1_TXDMA_Handler.Init.MemInc              = DMA_MINC_ENABLE;
+    SAI1_TXDMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
+    SAI1_TXDMA_Handler.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;
+    SAI1_TXDMA_Handler.Init.Mode                = DMA_CIRCULAR;
+    SAI1_TXDMA_Handler.Init.Priority            = DMA_PRIORITY_HIGH;
+    SAI1_TXDMA_Handler.Instance                 = DMA2_Channel1;
+    __HAL_LINKDMA(&SAI1A_Handler, hdmatx, SAI1_TXDMA_Handler);
+    HAL_DMA_DeInit(&SAI1_TXDMA_Handler);
+    HAL_DMA_Init(&SAI1_TXDMA_Handler);
+    __HAL_DMA_ENABLE(&SAI1_TXDMA_Handler);
+
+    HAL_NVIC_SetPriority(DMA2_Channel1_IRQn, 0x01, 0);
+    HAL_NVIC_EnableIRQ(DMA2_Channel1_IRQn);
+}
+
+void DMA2_Channel1_IRQHandler(void)
+{
+    HAL_DMA_IRQHandler(&SAI1_TXDMA_Handler);
+}
+
+void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai)
+{
+    if (hsai == &SAI1A_Handler)
+    {
+        rt_audio_tx_complete(&snd_dev.audio);
+    }
+}
+
+void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai)
+{
+    if (hsai == &SAI1A_Handler)
+    {
+        rt_audio_tx_complete(&snd_dev.audio);
+    }
+}
+
+void SAIA_Frequency_Set(uint32_t frequency)
+{
+    RCC_PeriphCLKInitTypeDef PeriphClkInit;
+
+    HAL_RCCEx_GetPeriphCLKConfig(&PeriphClkInit);
+
+    if ((frequency == SAI_AUDIO_FREQUENCY_11K) || (frequency == SAI_AUDIO_FREQUENCY_22K) || (frequency == SAI_AUDIO_FREQUENCY_44K))
+    {
+        /* Configure and enable PLLSAI1 clock to generate 45.714286MHz */
+        PeriphClkInit.PeriphClockSelection      = RCC_PERIPHCLK_SAI1;
+        PeriphClkInit.Sai1ClockSelection        = RCC_SAI1CLKSOURCE_PLLSAI2;
+        PeriphClkInit.PLLSAI2.PLLSAI2Source     = RCC_PLLSOURCE_HSE;
+        PeriphClkInit.PLLSAI2.PLLSAI2M          = 1;
+        PeriphClkInit.PLLSAI2.PLLSAI2N          = 40;
+        PeriphClkInit.PLLSAI2.PLLSAI2ClockOut   = RCC_PLLSAI2_SAI2CLK;
+
+        HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
+    }
+    else
+    {
+        /* Configure and enable PLLSAI1 clock to generate 49.142857MHz */
+        PeriphClkInit.PeriphClockSelection      = RCC_PERIPHCLK_SAI1;
+        PeriphClkInit.Sai1ClockSelection        = RCC_SAI1CLKSOURCE_PLLSAI2;
+        PeriphClkInit.PLLSAI2.PLLSAI2Source     = RCC_PLLSOURCE_HSE;
+        PeriphClkInit.PLLSAI2.PLLSAI2M          = 1;
+        PeriphClkInit.PLLSAI2.PLLSAI2N          = 43;
+        PeriphClkInit.PLLSAI2.PLLSAI2P          = RCC_PLLP_DIV7;
+        PeriphClkInit.PLLSAI2.PLLSAI2ClockOut   = RCC_PLLSAI2_SAI2CLK;
+
+        HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
+    }
+
+    /* Disable SAI peripheral to allow access to SAI internal registers */
+    __HAL_SAI_DISABLE(&SAI1A_Handler);
+    /* Update the SAI audio frequency configuration */
+    SAI1A_Handler.Init.AudioFrequency         = frequency;
+    HAL_SAI_Init(&SAI1A_Handler);
+    /* Enable SAI peripheral to generate MCLK */
+    __HAL_SAI_ENABLE(&SAI1A_Handler);
+}
+
+void SAIA_Channels_Set(uint8_t channels)
+{
+    if (channels == 1)
+    {
+        SAI1A_Handler.Init.MonoStereoMode         = SAI_MONOMODE;
+    }
+    else
+    {
+        SAI1A_Handler.Init.MonoStereoMode         = SAI_STEREOMODE;
+    }
+
+    __HAL_SAI_DISABLE(&SAI1A_Handler);
+    HAL_SAI_Init(&SAI1A_Handler);
+    __HAL_SAI_ENABLE(&SAI1A_Handler);
+}
+
+/**
+ * RT-Thread Audio Device Driver Interface
+ */
+static rt_err_t sound_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
+{
+    rt_err_t result = RT_EOK;
+    struct sound_device *snd_dev;
+
+    RT_ASSERT(audio != RT_NULL);
+    snd_dev = (struct sound_device *)audio->parent.user_data;
+
+    switch (caps->main_type)
+    {
+    case AUDIO_TYPE_QUERY: /* qurey the types of hw_codec device */
+    {
+        switch (caps->sub_type)
+        {
+        case AUDIO_TYPE_QUERY:
+            caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;
+            break;
+
+        default:
+            result = -RT_ERROR;
+            break;
+        }
+
+        break;
+    }
+
+    case AUDIO_TYPE_OUTPUT: /* Provide capabilities of OUTPUT unit */
+    {
+        switch (caps->sub_type)
+        {
+        case AUDIO_DSP_PARAM:
+            caps->udata.config.samplerate   = snd_dev->replay_config.samplerate;
+            caps->udata.config.channels     = snd_dev->replay_config.channels;
+            caps->udata.config.samplebits   = snd_dev->replay_config.samplebits;
+            break;
+
+        case AUDIO_DSP_SAMPLERATE:
+            caps->udata.config.samplerate   = snd_dev->replay_config.samplerate;
+            break;
+
+        case AUDIO_DSP_CHANNELS:
+            caps->udata.config.channels     = snd_dev->replay_config.channels;
+            break;
+
+        case AUDIO_DSP_SAMPLEBITS:
+            caps->udata.config.samplebits   = snd_dev->replay_config.samplebits;
+            break;
+
+        default:
+            result = -RT_ERROR;
+            break;
+        }
+
+        break;
+    }
+
+    case AUDIO_TYPE_MIXER: /* report the Mixer Units */
+    {
+        switch (caps->sub_type)
+        {
+        case AUDIO_MIXER_QUERY:
+            caps->udata.mask = AUDIO_MIXER_VOLUME;
+            break;
+
+        case AUDIO_MIXER_VOLUME:
+            caps->udata.value =  es8388_volume_get();
+            break;
+
+        default:
+            result = -RT_ERROR;
+            break;
+        }
+
+        break;
+    }
+
+    default:
+        result = -RT_ERROR;
+        break;
+    }
+
+    return result;
+}
+
+static rt_err_t sound_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
+{
+    rt_err_t result = RT_EOK;
+    struct sound_device *snd_dev;
+
+    RT_ASSERT(audio != RT_NULL);
+    snd_dev = (struct sound_device *)audio->parent.user_data;
+
+    switch (caps->main_type)
+    {
+    case AUDIO_TYPE_MIXER:
+    {
+        switch (caps->sub_type)
+        {
+        case AUDIO_MIXER_VOLUME:
+        {
+            rt_uint8_t volume = caps->udata.value;
+
+            es8388_volume_set(volume);
+            snd_dev->volume = volume;
+            LOG_D("set volume %d", volume);
+            break;
+        }
+
+        default:
+            result = -RT_ERROR;
+            break;
+        }
+
+        break;
+    }
+
+    case AUDIO_TYPE_OUTPUT:
+    {
+        switch (caps->sub_type)
+        {
+        case AUDIO_DSP_PARAM:
+        {
+            /* set samplerate */
+            SAIA_Frequency_Set(caps->udata.config.samplerate);
+            /* set channels */
+            SAIA_Channels_Set(caps->udata.config.channels);
+
+            /* save configs */
+            snd_dev->replay_config.samplerate = caps->udata.config.samplerate;
+            snd_dev->replay_config.channels   = caps->udata.config.channels;
+            snd_dev->replay_config.samplebits = caps->udata.config.samplebits;
+            LOG_D("set samplerate %d", snd_dev->replay_config.samplerate);
+            break;
+        }
+
+        case AUDIO_DSP_SAMPLERATE:
+        {
+            SAIA_Frequency_Set(caps->udata.config.samplerate);
+            snd_dev->replay_config.samplerate = caps->udata.config.samplerate;
+            LOG_D("set samplerate %d", snd_dev->replay_config.samplerate);
+            break;
+        }
+
+        case AUDIO_DSP_CHANNELS:
+        {
+            SAIA_Channels_Set(caps->udata.config.channels);
+            snd_dev->replay_config.channels   = caps->udata.config.channels;
+            LOG_D("set channels %d", snd_dev->replay_config.channels);
+            break;
+        }
+
+        case AUDIO_DSP_SAMPLEBITS:
+        {
+            /* not support */
+            snd_dev->replay_config.samplebits = caps->udata.config.samplebits;
+            break;
+        }
+
+        default:
+            result = -RT_ERROR;
+            break;
+        }
+
+        break;
+    }
+
+    default:
+        break;
+    }
+
+    return result;
+}
+
+static rt_err_t sound_init(struct rt_audio_device *audio)
+{
+    rt_err_t result = RT_EOK;
+    struct sound_device *snd_dev;
+
+    RT_ASSERT(audio != RT_NULL);
+    snd_dev = (struct sound_device *)audio->parent.user_data;
+
+    es8388_init("i2c3", GET_PIN(A, 5));
+    SAIA_Init();
+
+    /* set default params */
+    SAIA_Frequency_Set(snd_dev->replay_config.samplerate);
+    SAIA_Channels_Set(snd_dev->replay_config.channels);
+
+    return result;
+}
+
+static rt_err_t sound_start(struct rt_audio_device *audio, int stream)
+{
+    struct sound_device *snd_dev;
+
+    RT_ASSERT(audio != RT_NULL);
+    snd_dev = (struct sound_device *)audio->parent.user_data;
+
+    if (stream == AUDIO_STREAM_REPLAY)
+    {
+        LOG_D("open sound device");
+        es8388_start(ES_MODE_DAC);
+        HAL_SAI_Transmit_DMA(&SAI1A_Handler, snd_dev->tx_fifo, TX_FIFO_SIZE / 2);
+    }
+
+    return RT_EOK;
+}
+
+static rt_err_t sound_stop(struct rt_audio_device *audio, int stream)
+{
+    RT_ASSERT(audio != RT_NULL);
+
+    if (stream == AUDIO_STREAM_REPLAY)
+    {
+        HAL_SAI_DMAStop(&SAI1A_Handler);
+        es8388_stop(ES_MODE_DAC);
+        LOG_D("close sound device");
+    }
+
+    return RT_EOK;
+}
+
+static void sound_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)
+{
+    struct sound_device *snd_dev;
+
+    RT_ASSERT(audio != RT_NULL);
+    snd_dev = (struct sound_device *)audio->parent.user_data;
+
+    /**
+     *               TX_FIFO
+     * +----------------+----------------+
+     * |     block1     |     block2     |
+     * +----------------+----------------+
+     *  \  block_size  /
+     */
+    info->buffer      = snd_dev->tx_fifo;
+    info->total_size  = TX_FIFO_SIZE;
+    info->block_size  = TX_FIFO_SIZE / 2;
+    info->block_count = 2;
+}
+
+static struct rt_audio_ops snd_ops =
+{
+    .getcaps     = sound_getcaps,
+    .configure   = sound_configure,
+    .init        = sound_init,
+    .start       = sound_start,
+    .stop        = sound_stop,
+    .transmit    = RT_NULL,
+    .buffer_info = sound_buffer_info,
+};
+
+int rt_hw_sound_init(void)
+{
+    rt_uint8_t *tx_fifo;
+
+    if (snd_dev.tx_fifo)
+        return RT_EOK;
+
+    tx_fifo = rt_malloc(TX_FIFO_SIZE);
+    if (tx_fifo == RT_NULL)
+        return -RT_ENOMEM;
+    rt_memset(tx_fifo, 0, TX_FIFO_SIZE);
+    snd_dev.tx_fifo = tx_fifo;
+
+    /* init default configuration */
+    {
+        snd_dev.replay_config.samplerate = 44100;
+        snd_dev.replay_config.channels   = 2;
+        snd_dev.replay_config.samplebits = 16;
+        snd_dev.volume                   = 55;
+    }
+
+    /* register sound device */
+    snd_dev.audio.ops = &snd_ops;
+    rt_audio_register(&snd_dev.audio, "sound0", RT_DEVICE_FLAG_WRONLY, &snd_dev);
+
+    return RT_EOK;
+}
+
+INIT_DEVICE_EXPORT(rt_hw_sound_init);

+ 16 - 0
bsp/stm32/stm32l475-atk-pandora/board/ports/audio/drv_sound.h

@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Date           Author       Notes
+ * 2019-07-31     Zero-Free    first implementation
+ */
+
+#ifndef __DRV_SOUND_H__
+#define __DRV_SOUND_H__
+
+int rt_hw_sound_init(void);
+int rt_hw_mic_init(void);
+
+#endif

Some files were not shown because too many files changed in this diff