summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvalZero <[email protected]>2019-08-06 15:00:25 +0800
committerEvalZero <[email protected]>2019-08-06 15:01:23 +0800
commit559e297e4b17e73d30fc7317868889bc0f4d0e6a (patch)
tree70c435551e8e825900056ab6e24f470944709940
parent87267b41bca07c9a0d6e87a33b604d23de3acc0d (diff)
[bsp][qemu-vexpress-a9]update qemu-vexpress-a9 audio device drivers.
-rw-r--r--bsp/qemu-vexpress-a9/drivers/audio/audio_device.c191
-rw-r--r--bsp/qemu-vexpress-a9/drivers/audio/audio_device.h51
-rw-r--r--bsp/qemu-vexpress-a9/drivers/audio/drv_audio.c328
-rw-r--r--bsp/qemu-vexpress-a9/drivers/audio/drv_audio.h29
-rw-r--r--bsp/qemu-vexpress-a9/drivers/audio/drv_pl041.c2
-rw-r--r--bsp/qemu-vexpress-a9/drivers/audio/drv_pl041.h2
-rw-r--r--bsp/qemu-vexpress-a9/drivers/audio/drv_sound.c340
-rw-r--r--bsp/qemu-vexpress-a9/drivers/audio/drv_sound.h13
-rw-r--r--bsp/qemu-vexpress-a9/drivers/audio/wav_play.c144
9 files changed, 355 insertions, 745 deletions
diff --git a/bsp/qemu-vexpress-a9/drivers/audio/audio_device.c b/bsp/qemu-vexpress-a9/drivers/audio/audio_device.c
deleted file mode 100644
index a0f37ed621..0000000000
--- a/bsp/qemu-vexpress-a9/drivers/audio/audio_device.c
+++ /dev/null
@@ -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;
-}
diff --git a/bsp/qemu-vexpress-a9/drivers/audio/audio_device.h b/bsp/qemu-vexpress-a9/drivers/audio/audio_device.h
deleted file mode 100644
index d0143c231b..0000000000
--- a/bsp/qemu-vexpress-a9/drivers/audio/audio_device.h
+++ /dev/null
@@ -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 \ No newline at end of file
diff --git a/bsp/qemu-vexpress-a9/drivers/audio/drv_audio.c b/bsp/qemu-vexpress-a9/drivers/audio/drv_audio.c
deleted file mode 100644
index 53161ef22b..0000000000
--- a/bsp/qemu-vexpress-a9/drivers/audio/drv_audio.c
+++ /dev/null
@@ -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);
diff --git a/bsp/qemu-vexpress-a9/drivers/audio/drv_audio.h b/bsp/qemu-vexpress-a9/drivers/audio/drv_audio.h
deleted file mode 100644
index b2c07b40e4..0000000000
--- a/bsp/qemu-vexpress-a9/drivers/audio/drv_audio.h
+++ /dev/null
@@ -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
diff --git a/bsp/qemu-vexpress-a9/drivers/audio/drv_pl041.c b/bsp/qemu-vexpress-a9/drivers/audio/drv_pl041.c
index 51cf3710ef..0b1ee9827d 100644
--- a/bsp/qemu-vexpress-a9/drivers/audio/drv_pl041.c
+++ b/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;
mask = PL041_READ(&PL041->allints);
- PL041_WRITE(PL041->intclr, mask);
+ PL041_WRITE(&PL041->intclr, mask);
for (channle = 0; (channle < PL041_CHANNLE_NUM) && (mask); channle++)
{
diff --git a/bsp/qemu-vexpress-a9/drivers/audio/drv_pl041.h b/bsp/qemu-vexpress-a9/drivers/audio/drv_pl041.h
index d0bacd8c61..5ea146e436 100644
--- a/bsp/qemu-vexpress-a9/drivers/audio/drv_pl041.h
+++ b/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);
void aaci_ac97_write(rt_uint16_t reg, rt_uint16_t val);
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_read(int channle, rt_uint16_t *buff, int count);
int aaci_pl041_channle_enable(int channle);
diff --git a/bsp/qemu-vexpress-a9/drivers/audio/drv_sound.c b/bsp/qemu-vexpress-a9/drivers/audio/drv_sound.c
new file mode 100644
index 0000000000..026008708c
--- /dev/null
+++ b/bsp/qemu-vexpress-a9/drivers/audio/drv_sound.c
@@ -0,0 +1,340 @@
+/*
+ * 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);
diff --git a/bsp/qemu-vexpress-a9/drivers/audio/drv_sound.h b/bsp/qemu-vexpress-a9/drivers/audio/drv_sound.h
new file mode 100644
index 0000000000..b7e880c9a7
--- /dev/null
+++ b/bsp/qemu-vexpress-a9/drivers/audio/drv_sound.h
@@ -0,0 +1,13 @@
+/*
+ * 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
diff --git a/bsp/qemu-vexpress-a9/drivers/audio/wav_play.c b/bsp/qemu-vexpress-a9/drivers/audio/wav_play.c
deleted file mode 100644
index c000a0b085..0000000000
--- a/bsp/qemu-vexpress-a9/drivers/audio/wav_play.c
+++ /dev/null
@@ -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);