diff --git a/bsp/qemu-vexpress-a9/drivers/Kconfig b/bsp/qemu-vexpress-a9/drivers/Kconfig index a103b68352edb9fcec23a9a4658af6550ee37c4c..5f033c3c00ee2bb1ca06833e9610b958df0eed53 100644 --- a/bsp/qemu-vexpress-a9/drivers/Kconfig +++ b/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 diff --git a/bsp/qemu-vexpress-a9/drivers/SConscript b/bsp/qemu-vexpress-a9/drivers/SConscript index ed11ddbb6f785cdd90dc36025057e7a542724eda..4f10c262171804689c151339a081c037df768460 100644 --- a/bsp/qemu-vexpress-a9/drivers/SConscript +++ b/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') diff --git a/bsp/qemu-vexpress-a9/drivers/audio/SConscript b/bsp/qemu-vexpress-a9/drivers/audio/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..211863160892b57bbee329968649b5c0cc084ed0 --- /dev/null +++ b/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') diff --git a/bsp/qemu-vexpress-a9/drivers/audio/audio_device.c b/bsp/qemu-vexpress-a9/drivers/audio/audio_device.c new file mode 100644 index 0000000000000000000000000000000000000000..a0f37ed62100bee0dbd1c12b9c6475e395c04b6f --- /dev/null +++ b/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 +#include +#include +#include +#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 new file mode 100644 index 0000000000000000000000000000000000000000..d0143c231b4c91bc4f3d2414579d049b6fac040d --- /dev/null +++ b/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 \ No newline at end of file diff --git a/bsp/qemu-vexpress-a9/drivers/audio/drv_ac97.c b/bsp/qemu-vexpress-a9/drivers/audio/drv_ac97.c new file mode 100644 index 0000000000000000000000000000000000000000..46ea2e0feae8b60168caf814e235d7e1562e7aae --- /dev/null +++ b/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 +#include +#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 diff --git a/bsp/qemu-vexpress-a9/drivers/audio/drv_ac97.h b/bsp/qemu-vexpress-a9/drivers/audio/drv_ac97.h new file mode 100644 index 0000000000000000000000000000000000000000..31d496024d65112c68e05021c9a407ee75f44e02 --- /dev/null +++ b/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 diff --git a/bsp/qemu-vexpress-a9/drivers/audio/drv_audio.c b/bsp/qemu-vexpress-a9/drivers/audio/drv_audio.c new file mode 100644 index 0000000000000000000000000000000000000000..eb3cc82ea2f63570ac774130b9cb1ebe011e35f2 --- /dev/null +++ b/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 +#include +#include +#include +#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); diff --git a/bsp/qemu-vexpress-a9/drivers/audio/drv_audio.h b/bsp/qemu-vexpress-a9/drivers/audio/drv_audio.h new file mode 100644 index 0000000000000000000000000000000000000000..b2c07b40e428d297ea22ff584b0d02ce7c82a0de --- /dev/null +++ b/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 diff --git a/bsp/qemu-vexpress-a9/drivers/audio/drv_pl041.c b/bsp/qemu-vexpress-a9/drivers/audio/drv_pl041.c new file mode 100644 index 0000000000000000000000000000000000000000..10ef50b28187ce3f1c863da23fbe4a1a55052f38 --- /dev/null +++ b/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 +#include +#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 + +#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 diff --git a/bsp/qemu-vexpress-a9/drivers/audio/drv_pl041.h b/bsp/qemu-vexpress-a9/drivers/audio/drv_pl041.h new file mode 100644 index 0000000000000000000000000000000000000000..d0bacd8c613c1118a0ff8d4da49f5446f2895b0e --- /dev/null +++ b/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 diff --git a/bsp/qemu-vexpress-a9/drivers/audio/wav_play.c b/bsp/qemu-vexpress-a9/drivers/audio/wav_play.c new file mode 100644 index 0000000000000000000000000000000000000000..c000a0b0850960200b8a18702f85281ba55ac6f9 --- /dev/null +++ b/bsp/qemu-vexpress-a9/drivers/audio/wav_play.c @@ -0,0 +1,144 @@ +#include +#include +#include + +#include +#include +#include + +#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);