Bladeren bron

[bsp][qemu]添加Audio驱动

tangyuxin 7 jaren geleden
bovenliggende
commit
bc544bec47

+ 5 - 0
bsp/qemu-vexpress-a9/drivers/Kconfig

@@ -15,3 +15,8 @@ config BSP_DRV_EMAC
     bool "EMAC driver"
     depends on RT_USING_LWIP
     default y
+
+config BSP_DRV_AUDIO
+    bool "Audio driver"
+    select RT_USING_AUDIO
+    default n

+ 11 - 3
bsp/qemu-vexpress-a9/drivers/SConscript

@@ -1,8 +1,10 @@
 from building import *
 
-cwd     = GetCurrentDir()
-src     = Glob('*.c')
+cwd  = GetCurrentDir()
+src  = Glob('*.c')
+list = os.listdir(cwd)
 CPPPATH = [cwd]
+objs = []
 
 if not GetDepend('BSP_DRV_EMAC'):
     SrcRemove(src, ['drv_smc911x.c'])
@@ -12,4 +14,10 @@ if not GetDepend('BSP_DRV_CLCD'):
 
 group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH)
 
-Return('group')
+for d in list:
+    path = os.path.join(cwd, d)
+    if os.path.isfile(os.path.join(path, 'SConscript')):
+        objs = objs + SConscript(os.path.join(d, 'SConscript'))
+objs = objs + group
+
+Return('objs')

+ 9 - 0
bsp/qemu-vexpress-a9/drivers/audio/SConscript

@@ -0,0 +1,9 @@
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c') + Glob('*.S')
+CPPPATH = [cwd]
+
+group = DefineGroup('drv_audio', src, depend = ['BSP_DRV_AUDIO'], CPPPATH = CPPPATH)
+
+Return('group')

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

@@ -0,0 +1,191 @@
+/*
+ * 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;
+}

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

@@ -0,0 +1,51 @@
+/*
+ * 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

+ 121 - 0
bsp/qemu-vexpress-a9/drivers/audio/drv_ac97.c

@@ -0,0 +1,121 @@
+/*
+ * File      : drv_ac97.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-25     RT-Thread    the first version
+ */
+
+#include <rtthread.h>
+#include <rthw.h>
+#include "drv_pl041.h"
+#include "drv_ac97.h"
+
+void ac97_reset(void)
+{
+    aaci_ac97_write(AC97_RESET, 0xFFFF);
+}
+
+rt_err_t ac97_set_vol(int vol)
+{
+    rt_uint16_t tmp = vol | (vol << 8);
+
+    if (vol < 0 || vol > 0x3f)
+    {
+        return -RT_ERROR;
+    }
+
+    aaci_ac97_write(AC97_MASTER, tmp);
+    aaci_ac97_write(AC97_HEADPHONE, tmp);
+    aaci_ac97_write(AC97_MASTER_MONO, tmp);
+    aaci_ac97_write(AC97_PCM, tmp);
+
+    return RT_EOK;
+}
+
+int ac97_get_vol(void)
+{
+    rt_uint16_t v;
+
+    v = aaci_ac97_read(AC97_MASTER);
+    if (v == (~0x0))
+    {
+        v = 0;
+    }
+    return 0x3F & v;
+}
+
+rt_err_t ac97_set_rate(int rate)
+{
+    if (rate < 0)
+    {
+        return -RT_ERROR;
+    }
+    aaci_ac97_write(AC97_PCM_FRONT_DAC_RATE, rate);
+    return 0;
+}
+
+int ac97_get_rate(void)
+{
+    rt_uint16_t v;
+
+    v = aaci_ac97_read(AC97_PCM_FRONT_DAC_RATE);
+    if (v == (~0x0))
+    {
+        v = 0;
+    }
+    return v;
+}
+
+#if 0
+#define AC97_DUMP(_v)    rt_kprintf("%32s:addr:0x%08x data:0x%08x\n", #_v, (_v), (aaci_ac97_read(_v)))
+int _ac97_reg_dump(int argc, char **argv)
+{
+    AC97_DUMP(AC97_RESET);
+    AC97_DUMP(AC97_MASTER);
+    AC97_DUMP(AC97_HEADPHONE);
+    AC97_DUMP(AC97_MASTER_MONO);
+    AC97_DUMP(AC97_MASTER_TONE);
+    AC97_DUMP(AC97_PC_BEEP);
+    AC97_DUMP(AC97_PHONE);
+    AC97_DUMP(AC97_MIC);
+    AC97_DUMP(AC97_LINE);
+    AC97_DUMP(AC97_CD);
+    AC97_DUMP(AC97_VIDEO);
+    AC97_DUMP(AC97_AUX);
+    AC97_DUMP(AC97_PCM);
+    AC97_DUMP(AC97_REC_SEL);
+    AC97_DUMP(AC97_REC_GAIN);
+    AC97_DUMP(AC97_REC_GAIN_MIC);
+    AC97_DUMP(AC97_GENERAL_PURPOSE);
+    AC97_DUMP(AC97_3D_CONTROL);
+    AC97_DUMP(AC97_INT_PAGING);
+    AC97_DUMP(AC97_POWERDOWN);
+    AC97_DUMP(AC97_PCM_FRONT_DAC_RATE);
+    AC97_DUMP(AC97_PCM_SURR_DAC_RATE);
+    AC97_DUMP(AC97_PCM_LFE_DAC_RATE);
+    AC97_DUMP(AC97_PCM_LR_ADC_RATE);
+    AC97_DUMP(AC97_PCM_MIC_ADC_RATE);
+    AC97_DUMP(AC97_DAC_SLOT_MAP);
+    AC97_DUMP(AC97_ADC_SLOT_MAP);
+    return 0;
+}
+FINSH_FUNCTION_EXPORT_ALIAS(_ac97_reg_dump, __cmd_ac97_dump, ac97 dump reg.);
+
+#endif

+ 63 - 0
bsp/qemu-vexpress-a9/drivers/audio/drv_ac97.h

@@ -0,0 +1,63 @@
+/*
+ * File      : drv_ac97.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-25     RT-Thread    the first version
+ */
+
+#ifndef __DRV_AC97_H__
+#define __DRV_AC97_H__
+
+/* Register offsets */
+#define AC97_RESET				0x00
+#define AC97_MASTER				0x02
+#define AC97_HEADPHONE			0x04
+#define AC97_MASTER_MONO		0x06
+#define AC97_MASTER_TONE		0x08
+#define AC97_PC_BEEP			0x0A    //mixer volume
+#define AC97_PHONE				0x0C
+#define AC97_MIC				0x0E    //qwert db
+#define AC97_LINE				0x10		
+#define AC97_CD					0x12
+#define AC97_VIDEO				0x14
+#define AC97_AUX				0x16
+#define AC97_PCM				0x18
+#define AC97_REC_SEL			0x1A	//0 represent mic
+#define AC97_REC_GAIN			0x1C
+#define AC97_REC_GAIN_MIC		0x1E
+#define AC97_GENERAL_PURPOSE	0x20
+#define AC97_3D_CONTROL			0x22
+#define AC97_INT_PAGING			0x24    //qwert
+#define AC97_POWERDOWN			0x26
+#define AC97_PCM_FRONT_DAC_RATE 0x2c	/* PCM Front DAC Rate */
+#define AC97_PCM_SURR_DAC_RATE	0x2e	/* PCM Surround DAC Rate */
+#define AC97_PCM_LFE_DAC_RATE	0x30	/* PCM LFE DAC Rate */
+#define AC97_PCM_LR_ADC_RATE	0x32	/* PCM LR ADC Rate */
+#define AC97_PCM_MIC_ADC_RATE	0x34	/* PCM MIC ADC Rate */
+#define AC97_DAC_SLOT_MAP		0x6C
+#define AC97_ADC_SLOT_MAP		0x6E
+
+void ac97_reset(void);
+rt_err_t ac97_set_vol(int vol);
+int ac97_get_vol(void);
+rt_err_t ac97_set_rate(int rate);
+int ac97_get_rate(void);
+
+#endif

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

@@ -0,0 +1,312 @@
+/*
+ * 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;
+}
+
+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;
+
+    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;
+
+    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);

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

@@ -0,0 +1,29 @@
+/*
+ * 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

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

@@ -0,0 +1,411 @@
+/*
+ * File      : drv_pl041.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-25     RT-Thread    the first version
+ */
+
+#include <rtthread.h>
+#include <rthw.h>
+#include "drv_pl041.h"
+#include "drv_ac97.h"
+#include "realview.h"
+
+#define DBG_ENABLE
+#define DBG_SECTION_NAME  "[PL041]"
+// #define DBG_LEVEL         DBG_LOG
+// #define DBG_LEVEL         DBG_INFO
+#define DBG_LEVEL         DBG_WARNING
+// #define DBG_LEVEL         DBG_ERROR
+#define DBG_COLOR
+#include <rtdbg.h>
+
+#define FRAME_PERIOD_US    (50)
+#define PL041_CHANNLE_NUM  (4)
+
+#define PL041_READ(_a)        (*(volatile rt_uint32_t *)(_a))
+#define PL041_WRITE(_a, _v)   (*(volatile rt_uint32_t *)(_a) = (_v))
+
+struct pl041_irq_def
+{
+    pl041_irq_fun_t fun;
+    void *user_data;
+};
+
+static struct pl041_irq_def irq_tbl[PL041_CHANNLE_NUM];
+
+static void aaci_pl041_delay(rt_uint32_t us)
+{
+    volatile int i;
+
+    for (i = us * 10; i != 0; i--);
+}
+
+static void aaci_ac97_select_codec(void)
+{
+    rt_uint32_t v, maincr;
+
+    maincr = AACI_MAINCR_SCRA(0) | AACI_MAINCR_IE | AACI_MAINCR_SL1RXEN | \
+             AACI_MAINCR_SL1TXEN | AACI_MAINCR_SL2RXEN | AACI_MAINCR_SL2TXEN;
+
+    v = PL041_READ(&PL041->slfr);
+    if (v & AACI_SLFR_2RXV)
+    {
+        PL041_READ(&PL041->sl2rx);
+    }
+    if (v & AACI_SLFR_1RXV)
+    {
+        PL041_READ(&PL041->sl1rx);
+    }
+
+    if (maincr != PL041_READ(&PL041->maincr))
+    {
+        PL041_WRITE(&PL041->maincr, maincr);
+        aaci_pl041_delay(1);
+    }
+}
+
+void aaci_ac97_write(rt_uint16_t reg, rt_uint16_t val)
+{
+    rt_uint32_t v, timeout;
+
+    aaci_ac97_select_codec();
+
+    PL041_WRITE(&PL041->sl2tx, val << 4);
+    PL041_WRITE(&PL041->sl1tx, reg << 12);
+
+    aaci_pl041_delay(FRAME_PERIOD_US);
+
+    timeout = FRAME_PERIOD_US * 8;
+    do
+    {
+        aaci_pl041_delay(1);
+        v = PL041_READ(&PL041->slfr);
+    }
+    while ((v & (AACI_SLFR_1TXB | AACI_SLFR_2TXB)) && --timeout);
+
+    if (v & (AACI_SLFR_1TXB | AACI_SLFR_2TXB))
+    {
+        dbg_log(DBG_ERROR, "timeout waiting for write to complete\n");
+    }
+}
+
+rt_uint16_t aaci_ac97_read(rt_uint16_t reg)
+{
+    rt_uint32_t v, timeout, retries = 10;
+
+    aaci_ac97_select_codec();
+
+    PL041_WRITE(&PL041->sl1tx, (reg << 12) | (1 << 19));
+    aaci_pl041_delay(FRAME_PERIOD_US);
+
+    timeout = FRAME_PERIOD_US * 8;
+    do
+    {
+        aaci_pl041_delay(1);
+        v = PL041_READ(&PL041->slfr);
+    }
+    while ((v & AACI_SLFR_1TXB) && --timeout);
+
+    if (v & AACI_SLFR_1TXB)
+    {
+        dbg_log(DBG_ERROR, "timeout on slot 1 TX busy\n");
+        v = ~0x0;
+        return v;
+    }
+
+    aaci_pl041_delay(FRAME_PERIOD_US);
+    timeout = FRAME_PERIOD_US * 8;
+    do
+    {
+        aaci_pl041_delay(1);
+        v = PL041_READ(&PL041->slfr) & (AACI_SLFR_1RXV | AACI_SLFR_2RXV);
+    }
+    while ((v != (AACI_SLFR_1RXV | AACI_SLFR_2RXV)) && --timeout);
+
+    if (v != (AACI_SLFR_1RXV | AACI_SLFR_2RXV))
+    {
+        dbg_log(DBG_ERROR, "timeout on RX valid\n");
+        v = ~0x0;
+        return v;
+    }
+
+    do
+    {
+        v = PL041_READ(&PL041->sl1rx) >> 12;
+        if (v == reg)
+        {
+            v = PL041_READ(&PL041->sl2rx) >> 4;
+            break;
+        }
+        else if (--retries)
+        {
+            dbg_log(DBG_ERROR, "ac97 read back fail. retry\n");
+            continue;
+        }
+        else
+        {
+            dbg_log(DBG_ERROR, "wrong ac97 register read back (%x != %x)\n", v, reg);
+            v = ~0x0;
+        }
+    }
+    while (retries);
+
+    return v;
+}
+
+int aaci_pl041_channle_disable(int channle)
+{
+    rt_uint32_t v;
+    void *p_rx, *p_tx;
+
+    p_rx = (void *)((rt_uint32_t)(&PL041->rxcr1) + channle * 0x14);
+    p_tx = (void *)((rt_uint32_t)(&PL041->txcr1) + channle * 0x14);
+    v = PL041_READ(p_rx);
+    v &= ~AACI_CR_EN;
+    PL041_WRITE(p_rx, v);
+    v = PL041_READ(p_tx);
+    v &= ~AACI_CR_EN;
+    PL041_WRITE(p_tx, v);
+    return 0;
+}
+
+int aaci_pl041_channle_enable(int channle)
+{
+    rt_uint32_t v;
+    void *p_rx, *p_tx;
+
+    p_rx = (void *)((rt_uint32_t)(&PL041->rxcr1) + channle * 0x14);
+    p_tx = (void *)((rt_uint32_t)(&PL041->txcr1) + channle * 0x14);
+    v = PL041_READ(p_rx);
+    v |= AACI_CR_EN;
+    PL041_WRITE(p_rx, v);
+    v = PL041_READ(p_tx);
+    v |= AACI_CR_EN;
+    PL041_WRITE(p_tx, v);
+    return 0;
+}
+
+int aaci_pl041_channle_read(int channle, rt_uint16_t *buff, int count)
+{
+    void *p_data, *p_status;
+    int i = 0;
+
+    p_status = (void *)((rt_uint32_t)(&PL041->sr1) + channle * 0x14);
+    p_data = (void *)((rt_uint32_t)(&(PL041->dr1[0])) + channle * 0x20);
+    for (i = 0; (!(PL041_READ(p_status) & AACI_SR_RXFE)) && (i < count); i++)
+    {
+        buff[i] = (rt_uint16_t)PL041_READ(p_data);
+    }
+    return i;
+}
+
+int aaci_pl041_channle_write(int channle, rt_uint16_t *buff, int count)
+{
+    void *p_data, *p_status;
+    int i = 0;
+
+    p_status = (void *)((rt_uint32_t)(&PL041->sr1) + channle * 0x14);
+    p_data = (void *)((rt_uint32_t)(&(PL041->dr1[0])) + channle * 0x20);
+    for (i = 0; (!(PL041_READ(p_status) & AACI_SR_TXFF)) && (i < count); i++)
+    {
+        PL041_WRITE(p_data, buff[i]);
+    }
+    return i;
+}
+
+int aaci_pl041_channle_cfg(int channle, pl041_cfg_t cgf)
+{
+    rt_uint32_t v;
+    void *p_rx, *p_tx;
+
+    p_rx = (void *)((rt_uint32_t)(&PL041->rxcr1) + channle * 0x14);
+    p_tx = (void *)((rt_uint32_t)(&PL041->txcr1) + channle * 0x14);
+    v = AACI_CR_FEN | AACI_CR_SZ16 | cgf->itype;
+    PL041_WRITE(p_rx, v);
+    v = AACI_CR_FEN | AACI_CR_SZ16 | cgf->otype;
+    PL041_WRITE(p_tx, v);
+
+    ac97_set_vol(cgf->vol);
+    ac97_set_rate(cgf->rate);
+
+    return 0;
+}
+
+void aaci_pl041_irq_enable(int channle, rt_uint32_t vector)
+{
+    rt_uint32_t v;
+    void *p_irq;
+
+    vector &= vector & 0x7f;
+    p_irq = (void *)((rt_uint32_t)(&PL041->iie1) + channle * 0x14);
+    v = PL041_READ(p_irq);
+    v |= vector;
+    PL041_WRITE(p_irq, v);
+}
+
+void aaci_pl041_irq_disable(int channle, rt_uint32_t vector)
+{
+    rt_uint32_t v;
+    void *p_irq;
+
+    vector &= vector & 0x7f;
+    p_irq = (void *)((rt_uint32_t)(&PL041->iie1) + channle * 0x14);
+    v = PL041_READ(p_irq);
+    v &= ~vector;
+    PL041_WRITE(p_irq, v);
+}
+
+rt_err_t aaci_pl041_irq_register(int channle, pl041_irq_fun_t fun, void *user_data)
+{
+    if (channle < 0 || channle >= PL041_CHANNLE_NUM)
+    {
+        dbg_log(DBG_ERROR, "%s channle:%d err.\n", __FUNCTION__, channle);
+        return -RT_ERROR;
+    }
+    irq_tbl[channle].fun = fun;
+    irq_tbl[channle].user_data = user_data;
+    return RT_EOK;
+}
+
+rt_err_t aaci_pl041_irq_unregister(int channle)
+{
+    if (channle < 0 || channle >= PL041_CHANNLE_NUM)
+    {
+        dbg_log(DBG_ERROR, "%s channle:%d err.\n", __FUNCTION__, channle);
+        return -RT_ERROR;
+    }
+    irq_tbl[channle].fun = RT_NULL;
+    irq_tbl[channle].user_data = RT_NULL;
+    return RT_EOK;
+}
+
+static void aaci_pl041_irq_handle(int irqno, void *param)
+{
+    rt_uint32_t mask, channle, m;
+    struct pl041_irq_def *_irq = param;
+    void *p_status;
+
+    mask = PL041_READ(&PL041->allints);
+    PL041_WRITE(PL041->intclr, mask);
+
+    for (channle = 0; (channle < PL041_CHANNLE_NUM) && (mask); channle++)
+    {
+        mask = mask >> 7;
+        m = mask & 0x7f;
+        if (m & AACI_ISR_ORINTR)
+        {
+            dbg_log(DBG_WARNING, "RX overrun on chan %d\n", channle);
+        }
+
+        if (m & AACI_ISR_RXTOINTR)
+        {
+            dbg_log(DBG_WARNING, "RX timeout on chan %d\n", channle);
+        }
+
+        if (mask & AACI_ISR_URINTR)
+        {
+            dbg_log(DBG_WARNING, "TX underrun on chan %d\n", channle);
+        }
+
+        p_status = (void *)((rt_uint32_t)(&PL041->sr1) + channle * 0x14);
+        if (_irq[channle].fun != RT_NULL)
+        {
+            _irq[channle].fun(PL041_READ(p_status), _irq[channle].user_data);
+        }
+    }
+}
+
+rt_err_t aaci_pl041_init(void)
+{
+    rt_uint32_t i, maincr;
+
+    maincr  = AACI_MAINCR_SCRA(0) | AACI_MAINCR_IE | AACI_MAINCR_SL1RXEN | \
+              AACI_MAINCR_SL1TXEN | AACI_MAINCR_SL2RXEN | AACI_MAINCR_SL2TXEN;
+
+    for (i = 0; i < 4; i++)
+    {
+        void *base = (void *)((rt_uint32_t)(&PL041->rxcr1) + i * 0x14);
+
+        PL041_WRITE(base + AACI_IE, 0);
+        PL041_WRITE(base + AACI_TXCR, 0);
+        PL041_WRITE(base + AACI_RXCR, 0);
+    }
+
+    PL041_WRITE(&PL041->intclr, 0x1fff);
+    PL041_WRITE(&PL041->maincr, maincr);
+
+    PL041_WRITE(&PL041->reset, 0);
+    aaci_pl041_delay(2);
+    PL041_WRITE(&PL041->reset, RESET_NRST);
+
+    rt_hw_interrupt_install(43, aaci_pl041_irq_handle, &irq_tbl, "aaci_pl041");
+    rt_hw_interrupt_umask(43);
+
+    return 0;
+}
+
+#if 0
+#define PL041_DUMP(_v)    rt_kprintf("%32s:addr:0x%08x data:0x%08x\n", #_v, &(_v), (_v))
+int _aaci_pl041_reg_dump(int argc, char **argv)
+{
+    PL041_DUMP(PL041->rxcr1);
+    PL041_DUMP(PL041->txcr1);
+    PL041_DUMP(PL041->sr1);
+    PL041_DUMP(PL041->isr1);
+    PL041_DUMP(PL041->iie1);
+    PL041_DUMP(PL041->rxcr2);
+    PL041_DUMP(PL041->txcr2);
+    PL041_DUMP(PL041->sr2);
+    PL041_DUMP(PL041->isr2);
+    PL041_DUMP(PL041->iie2);
+    PL041_DUMP(PL041->rxcr3);
+    PL041_DUMP(PL041->txcr3);
+    PL041_DUMP(PL041->sr3);
+    PL041_DUMP(PL041->isr3);
+    PL041_DUMP(PL041->iie3);
+    PL041_DUMP(PL041->rxcr4);
+    PL041_DUMP(PL041->txcr4);
+    PL041_DUMP(PL041->sr4);
+    PL041_DUMP(PL041->isr4);
+    PL041_DUMP(PL041->iie4);
+    PL041_DUMP(PL041->sl1rx);
+    PL041_DUMP(PL041->sl1tx);
+    PL041_DUMP(PL041->sl2rx);
+    PL041_DUMP(PL041->sl2tx);
+    PL041_DUMP(PL041->sl12rx);
+    PL041_DUMP(PL041->sl12tx);
+    PL041_DUMP(PL041->slfr);
+    PL041_DUMP(PL041->slistat);
+    PL041_DUMP(PL041->slien);
+    PL041_DUMP(PL041->intclr);
+    PL041_DUMP(PL041->maincr);
+    PL041_DUMP(PL041->reset);
+    PL041_DUMP(PL041->sync);
+    PL041_DUMP(PL041->allints);
+    PL041_DUMP(PL041->mainfr);
+    PL041_DUMP(PL041->dr1[0]);
+    PL041_DUMP(PL041->dr2[0]);
+    PL041_DUMP(PL041->dr3[0]);
+    PL041_DUMP(PL041->dr4[0]);
+    return 0;
+}
+FINSH_FUNCTION_EXPORT_ALIAS(_aaci_pl041_reg_dump, __cmd_pl041_dump, aaci pl041 dump reg.);
+#endif

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

@@ -0,0 +1,237 @@
+/*
+ * File      : drv_pl041.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-25     RT-Thread    the first version
+ */
+
+#ifndef __DRV_PL041_H__
+#define __DRV_PL041_H__
+
+#define PL041_BASE_ADDR    (0x10004000)
+
+/* offsets in CTRL_CH */
+#define AACI_RXCR        0x00	/* 29 bits Control Rx FIFO */
+#define AACI_TXCR        0x04	/* 17 bits Control Tx FIFO */
+#define AACI_SR          0x08	/* 12 bits Status */
+#define AACI_ISR         0x0C	/* 7 bits  Int Status */
+#define AACI_IE          0x10	/* 7 bits  Int Enable */
+
+/* both for AACI_RXCR and AACI_TXCR */
+#define AACI_CR_FEN           (1 << 16)  /* fifo enable */
+#define AACI_CR_COMPACT       (1 << 15)  /* compact mode */
+#define AACI_CR_SZ16          (0 << 13)  /* 16 bits */
+#define AACI_CR_SZ18          (1 << 13)  /* 18 bits */
+#define AACI_CR_SZ20          (2 << 13)  /* 20 bits */
+#define AACI_CR_SZ12          (3 << 13)  /* 12 bits */
+#define AACI_CR_SL12          (1 << 12)
+#define AACI_CR_SL11          (1 << 11)
+#define AACI_CR_SL10          (1 << 10)
+#define AACI_CR_SL9           (1 << 9)
+#define AACI_CR_SL8           (1 << 8)
+#define AACI_CR_SL7           (1 << 7)
+#define AACI_CR_SL6           (1 << 6)
+#define AACI_CR_SL5           (1 << 5)
+#define AACI_CR_SL4           (1 << 4)
+#define AACI_CR_SL3           (1 << 3)
+#define AACI_CR_SL2           (1 << 2)
+#define AACI_CR_SL1           (1 << 1)
+#define AACI_CR_EN            (1 << 0)	/* receive enable */
+
+/* status register bits */
+#define AACI_SR_RXTOFE        (1 << 11)  /* rx timeout fifo empty */
+#define AACI_SR_TXTO          (1 << 10)  /* rx timeout fifo nonempty */
+#define AACI_SR_TXU           (1 << 9)   /* tx underrun */
+#define AACI_SR_RXO           (1 << 8)   /* rx overrun */
+#define AACI_SR_TXB           (1 << 7)   /* tx busy */
+#define AACI_SR_RXB           (1 << 6)   /* rx busy */
+#define AACI_SR_TXFF          (1 << 5)   /* tx fifo full */
+#define AACI_SR_RXFF          (1 << 4)   /* rx fifo full */
+#define AACI_SR_TXHE          (1 << 3)   /* tx fifo half empty */
+#define AACI_SR_RXHF          (1 << 2)   /* rx fifo half full */
+#define AACI_SR_TXFE          (1 << 1)   /* tx fifo empty */
+#define AACI_SR_RXFE          (1 << 0)   /* rx fifo empty */
+
+#define AACI_ISR_RXTOFEINTR   (1 << 6)    /* rx fifo empty */
+#define AACI_ISR_URINTR       (1 << 5)    /* tx underflow */
+#define AACI_ISR_ORINTR       (1 << 4)    /* rx overflow */
+#define AACI_ISR_RXINTR       (1 << 3)    /* rx fifo */
+#define AACI_ISR_TXINTR       (1 << 2)    /* tx fifo intr */
+#define AACI_ISR_RXTOINTR     (1 << 1)    /* rx timeout */
+#define AACI_ISR_TXCINTR      (1 << 0)    /* tx complete */
+
+/* interrupt enable */
+#define AACI_IE_RXTOIE        (1 << 6)   /*rx timeout interrupt enable*/
+#define AACI_IE_URIE          (1 << 5)   /*Transmit underrun interrupt enable*/
+#define AACI_IE_ORIE          (1 << 4)	 /*Overrun receive interrupt enable*/
+#define AACI_IE_RXIE          (1 << 3)   /*Receive interrupt enable*/
+#define AACI_IE_TXIE          (1 << 2)   /*Transmit interrupt enable*/
+#define AACI_IE_RXTIE         (1 << 1)   /*Receive timeout interrupt enable*/
+#define AACI_IE_TXCIE         (1 << 0)	 /*Transmit complete interrupt enable*/
+
+/* interrupt status */
+#define AACI_ISR_RXTOFE	(1 << 6)	/* rx timeout fifo empty */
+#define AACI_ISR_UR		(1 << 5)	/* tx fifo underrun */
+#define AACI_ISR_OR		(1 << 4)	/* rx fifo overrun */
+#define AACI_ISR_RX		(1 << 3)	/* rx interrupt status */
+#define AACI_ISR_TX		(1 << 2)	/* tx interrupt status */
+#define AACI_ISR_RXTO	(1 << 1)	/* rx timeout */
+#define AACI_ISR_TXC	(1 << 0)	/* tx complete */
+
+/* interrupt enable */
+#define AACI_IE_RXTOFE	(1 << 6)	/* rx timeout fifo empty */
+#define AACI_IE_UR		(1 << 5)	/* tx fifo underrun */
+#define AACI_IE_OR		(1 << 4)	/* rx fifo overrun */
+#define AACI_IE_RX		(1 << 3)	/* rx interrupt status */
+#define AACI_IE_TX		(1 << 2)	/* tx interrupt status */
+#define AACI_IE_RXTO	(1 << 1)	/* rx timeout */
+#define AACI_IE_TXC		(1 << 0)	/* tx complete */
+
+/* slot flag register bits */
+#define AACI_SLFR_RWIS        (1 << 13)    /* raw wake-up interrupt status */
+#define AACI_SLFR_RGPIOINTR   (1 << 12)    /* raw gpio interrupt */
+#define AACI_SLFR_12TXE       (1 << 11)    /* slot 12 tx empty */
+#define AACI_SLFR_12RXV       (1 << 10)    /* slot 12 rx valid */
+#define AACI_SLFR_2TXE        (1 << 9)     /* slot 2 tx empty */
+#define AACI_SLFR_2RXV        (1 << 8)     /* slot 2 rx valid */
+#define AACI_SLFR_1TXE        (1 << 7)     /* slot 1 tx empty */
+#define AACI_SLFR_1RXV        (1 << 6)     /* slot 1 rx valid */
+#define AACI_SLFR_12TXB       (1 << 5)     /* slot 12 tx busy */
+#define AACI_SLFR_12RXB       (1 << 4)     /* slot 12 rx busy */
+#define AACI_SLFR_2TXB        (1 << 3)     /* slot 2 tx busy */
+#define AACI_SLFR_2RXB        (1 << 2)     /* slot 2 rx busy */
+#define AACI_SLFR_1TXB        (1 << 1)     /* slot 1 tx busy */
+#define AACI_SLFR_1RXB        (1 << 0)     /* slot 1 rx busy */
+
+/* Interrupt clear register */
+#define AACI_ICLR_RXTOFEC4    (1 << 12)      /* Receive timeout FIFO empty clear */
+#define AACI_ICLR_RXTOFEC3    (1 << 11)      /* Receive timeout FIFO empty clear */
+#define AACI_ICLR_RXTOFEC2    (1 << 10)      /* Receive timeout FIFO empty clear */
+#define AACI_ICLR_RXTOFEC1    (1 << 9)       /* Receive timeout FIFO empty clear */
+#define AACI_ICLR_TXUEC4      (1 << 8)       /* Transmit underrun error clear */
+#define AACI_ICLR_TXUEC3      (1 << 7)       /* Transmit underrun error clear */
+#define AACI_ICLR_TXUEC2      (1 << 6)       /* Transmit underrun error clear*/
+#define AACI_ICLR_TXUEC1      (1 << 5)       /* Transmit underrun error clear */
+#define AACI_ICLR_RXOEC4      (1 << 4)       /* Receive overrun error clear */
+#define AACI_ICLR_RXOEC3      (1 << 3)       /* Receive overrun error clear */
+#define AACI_ICLR_RXOEC2      (1 << 2)       /* Receive overrun error clear */
+#define AACI_ICLR_RXOEC1      (1 << 1)       /* Receive overrun error clear */
+#define AACI_ICLR_WISC        (1 << 0)       /* Wake-up interrupt status clear */
+                                
+/* Main control register bits AACI_MAINCR */
+#define AACI_MAINCR_SCRA(x)   ((x) << 10)     /* secondary codec reg access */
+#define AACI_MAINCR_DMAEN     (1 << 9)        /* dma enable */
+#define AACI_MAINCR_SL12TXEN  (1 << 8)        /* slot 12 transmit enable */
+#define AACI_MAINCR_SL12RXEN  (1 << 7)        /* slot 12 receive enable */
+#define AACI_MAINCR_SL2TXEN   (1 << 6)        /* slot 2 transmit enable */
+#define AACI_MAINCR_SL2RXEN   (1 << 5)        /* slot 2 receive enable */
+#define AACI_MAINCR_SL1TXEN   (1 << 4)        /* slot 1 transmit enable */
+#define AACI_MAINCR_SL1RXEN   (1 << 3)        /* slot 1 receive enable */
+#define AACI_MAINCR_LPM       (1 << 2)        /* low power mode */
+#define AACI_MAINCR_LOOPBK    (1 << 1)        /* loopback */
+#define AACI_MAINCR_IE        (1 << 0)        /* aaci interface enable */
+
+/* Reset register bits. P65 */
+#define RESET_NRST   (1 << 0)
+
+/* Sync register bits. P65 */
+#define SYNC_FORCE   (1 << 0)
+
+/* Main flag register bits. P66 */
+#define MAINFR_TXB   (1 << 1)	/* transmit busy */
+#define MAINFR_RXB   (1 << 0)	/* receive busy */
+
+#define PL041_CHANNLE_LEFT_DAC    (0x1 << 3)
+#define PL041_CHANNLE_RIGHT_DAC   (0x1 << 3)
+#define PL041_CHANNLE_LEFT_ADC    (0x1 << 3)
+#define PL041_CHANNLE_RIGHT_ADC   (0x1 << 3)
+
+struct reg_pl041
+{
+volatile rt_uint32_t  rxcr1;    /* 0x000 */
+volatile rt_uint32_t  txcr1;    /* 0x004 */
+volatile rt_uint32_t  sr1;      /* 0x008 */
+volatile rt_uint32_t  isr1;     /* 0x00c */
+volatile rt_uint32_t  iie1;     /* 0x010 */
+volatile rt_uint32_t  rxcr2;    /* 0x014 */
+volatile rt_uint32_t  txcr2;    /* 0x018 */
+volatile rt_uint32_t  sr2;      /* 0x01c */
+volatile rt_uint32_t  isr2;     /* 0x020 */
+volatile rt_uint32_t  iie2;     /* 0x024 */
+volatile rt_uint32_t  rxcr3;    /* 0x028 */
+volatile rt_uint32_t  txcr3;    /* 0x02c */
+volatile rt_uint32_t  sr3;      /* 0x030 */
+volatile rt_uint32_t  isr3;     /* 0x034 */
+volatile rt_uint32_t  iie3;     /* 0x038 */
+volatile rt_uint32_t  rxcr4;    /* 0x03c */
+volatile rt_uint32_t  txcr4;    /* 0x040 */
+volatile rt_uint32_t  sr4;      /* 0x044 */
+volatile rt_uint32_t  isr4;     /* 0x048 */
+volatile rt_uint32_t  iie4;     /* 0x04c */
+volatile rt_uint32_t  sl1rx;    /* 0x050 */
+volatile rt_uint32_t  sl1tx;    /* 0x054 */
+volatile rt_uint32_t  sl2rx;    /* 0x058 */
+volatile rt_uint32_t  sl2tx;    /* 0x05c */
+volatile rt_uint32_t  sl12rx;   /* 0x060 */
+volatile rt_uint32_t  sl12tx;   /* 0x064 */
+volatile rt_uint32_t  slfr;     /* 0x068 */
+volatile rt_uint32_t  slistat;  /* 0x06c */
+volatile rt_uint32_t  slien;    /* 0x070 */
+volatile rt_uint32_t  intclr;   /* 0x074 */
+volatile rt_uint32_t  maincr;   /* 0x078 */
+volatile rt_uint32_t  reset;    /* 0x07c */
+volatile rt_uint32_t  sync;     /* 0x080 */
+volatile rt_uint32_t  allints;  /* 0x084 */
+volatile rt_uint32_t  mainfr;   /* 0x088 */
+volatile rt_uint32_t  res08c;
+volatile rt_uint32_t  dr1[8];   /* 0x090 */
+volatile rt_uint32_t  dr2[8];   /* 0x0b0 */
+volatile rt_uint32_t  dr3[8];   /* 0x0d0 */
+volatile rt_uint32_t  dr4[8];   /* 0x0f0 */
+};
+
+typedef struct reg_pl041 *reg_pl041_t;
+
+#define PL041  ((reg_pl041_t)PL041_BASE_ADDR)
+
+struct pl041_cfg
+{
+    rt_uint32_t itype;
+    rt_uint32_t otype;
+    int vol;
+    int rate;
+};
+typedef struct pl041_cfg *pl041_cfg_t;
+
+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_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);
+int aaci_pl041_channle_disable(int channle);
+rt_err_t aaci_pl041_irq_register(int channle, pl041_irq_fun_t fun, void *user_data);
+rt_err_t aaci_pl041_irq_unregister(int channle);
+void aaci_pl041_irq_disable(int channle, rt_uint32_t vector);
+void aaci_pl041_irq_enable(int channle, rt_uint32_t vector);
+#endif

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

@@ -0,0 +1,144 @@
+#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);