blob: 8c57d440fc41f26cc1d7d89b55175d5e3e1b7fe2 [file] [log] [blame]
/*
* Copyright (c) 2019 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define LOG_LEVEL LOG_LEVEL_INF
#include <logging/log.h>
LOG_MODULE_REGISTER(audio_io);
#include <zephyr.h>
#include <soc.h>
#include <device.h>
#include <drivers/i2s.h>
#include <audio/codec.h>
#include <audio/dmic.h>
#include "audio_core.h"
#define DMIC_DEV_NAME "PDM"
#define SPK_OUT_DEV_NAME "I2S_1"
#define HOST_INOUT_DEV_NAME "I2S_2"
#define NUM_HOST_CHANNELS 2
#define NUM_SPK_CHANNELS 2
#define NUM_MIC_CHANNELS 8
#define AUDIO_SAMPLE_FREQ 48000
#define AUDIO_SAMPLE_WIDTH 32
#define HOST_FRAME_SAMPLES (AUDIO_SAMPLES_PER_FRAME * NUM_HOST_CHANNELS)
#define MIC_FRAME_SAMPLES (AUDIO_SAMPLES_PER_FRAME * NUM_MIC_CHANNELS)
#define SPK_FRAME_SAMPLES (AUDIO_SAMPLES_PER_FRAME * NUM_SPK_CHANNELS)
#define HOST_FRAME_BYTES (HOST_FRAME_SAMPLES * AUDIO_SAMPLE_WIDTH / 8)
#define MIC_FRAME_BYTES (MIC_FRAME_SAMPLES * AUDIO_SAMPLE_WIDTH / 8)
#define SPK_FRAME_BYTES (SPK_FRAME_SAMPLES * AUDIO_SAMPLE_WIDTH / 8)
#define SPK_OUT_BUF_COUNT 2
#define MIC_IN_BUF_COUNT 2
#define HOST_INOUT_BUF_COUNT 4 /* 2 for TX and 2 for RX */
static void audio_drv_thread(void);
__attribute__((section(".dma_buffers"))) static struct {
int32_t host_inout[HOST_INOUT_BUF_COUNT][HOST_FRAME_SAMPLES];
int32_t spk_out[SPK_OUT_BUF_COUNT][SPK_FRAME_SAMPLES];
int32_t mic_in[MIC_IN_BUF_COUNT][MIC_FRAME_SAMPLES];
} audio_buffers;
static const struct device *codec_dev;
static const struct device *i2s_spk_out_dev;
static const struct device *i2s_host_dev;
static const struct device *dmic_device;
static struct k_mem_slab mic_in_mem_slab;
static struct k_mem_slab host_inout_mem_slab;
static struct k_mem_slab spk_out_mem_slab;
K_SEM_DEFINE(audio_drv_sync_sem, 0, 1);
static bool audio_io_started;
K_THREAD_DEFINE(audio_drv_thread_id, AUDIO_DRIVER_THREAD_STACKSIZE,
audio_drv_thread, NULL, NULL, NULL,
AUDIO_DRIVER_THREAD_PRIORITY, 0, 0);
static void audio_driver_process_audio_input(void)
{
int32_t *host_in_buf;
int32_t *mic_in_buf;
size_t size;
int ret;
/* read capture input buffer */
ret = dmic_read(dmic_device, 0, (void **)&mic_in_buf, &size, SYS_FOREVER_MS);
if (ret) {
LOG_ERR("dmic_device read failed %d", ret);
return;
}
/* read playback input buffer */
ret = i2s_read(i2s_host_dev, (void **)&host_in_buf, &size);
if (ret) {
LOG_ERR("i2s_host_dev read failed %d", ret);
return;
}
audio_core_process_mic_source(mic_in_buf, NUM_MIC_CHANNELS);
audio_core_process_host_source(host_in_buf, NUM_HOST_CHANNELS);
/* free the consumed buffers */
k_mem_slab_free(&mic_in_mem_slab, (void **)&mic_in_buf);
k_mem_slab_free(&host_inout_mem_slab, (void **)&host_in_buf);
}
static void audio_driver_process_audio_output(void)
{
int32_t *spk_out_buf;
int32_t *host_out_buf;
int ret;
ret = k_mem_slab_alloc(&spk_out_mem_slab, (void *)&spk_out_buf,
K_NO_WAIT);
if (ret) {
LOG_ERR("speaker out buffer alloc failed %d mem_slab %p",
ret, &spk_out_mem_slab);
}
ret = k_mem_slab_alloc(&host_inout_mem_slab, (void *)&host_out_buf,
K_NO_WAIT);
if (ret) {
LOG_ERR("host out audio buffer alloc failed %d", ret);
}
audio_core_process_speaker_sink(spk_out_buf, NUM_SPK_CHANNELS);
audio_core_process_host_sink(host_out_buf, NUM_HOST_CHANNELS);
/* write buffer */
ret = i2s_write(i2s_spk_out_dev, (void *)spk_out_buf, SPK_FRAME_BYTES);
if (ret) {
LOG_ERR("i2s_write for speaker failed %d", ret);
}
ret = i2s_write(i2s_host_dev, (void *)host_out_buf, HOST_FRAME_BYTES);
if (ret) {
LOG_ERR("i2s_write for host failed %d", ret);
}
}
static int audio_driver_send_zeros_frame(void)
{
int ret = 0;
void *spk_out_buf;
void *host_out_buf;
/* allocate speaker output buffer */
ret = k_mem_slab_alloc(&spk_out_mem_slab, &spk_out_buf, K_NO_WAIT);
if (ret) {
LOG_ERR("Buffer alloc for spk output failed %d", ret);
return ret;
}
/* allocate host output buffer */
ret = k_mem_slab_alloc(&host_inout_mem_slab, &host_out_buf, K_NO_WAIT);
if (ret) {
LOG_ERR("Buffer alloc for host output failed %d", ret);
return ret;
}
/* fill buffer with zeros */
memset(spk_out_buf, 0, SPK_FRAME_BYTES);
memset(host_out_buf, 0, HOST_FRAME_BYTES);
ret = i2s_write(i2s_spk_out_dev, spk_out_buf, SPK_FRAME_BYTES);
if (ret) {
LOG_ERR("i2s_write for spk output failed %d", ret);
return ret;
}
ret = i2s_write(i2s_host_dev, host_out_buf, HOST_FRAME_BYTES);
if (ret) {
LOG_ERR("i2s_write for host output failed %d", ret);
return ret;
}
return ret;
}
static int audio_driver_start_host_streams(void)
{
int ret = 0;
/* trigger transmission */
ret = i2s_trigger(i2s_host_dev, I2S_DIR_TX, I2S_TRIGGER_START);
if (ret) {
LOG_ERR("I2S TX failed with code %d", ret);
}
/* trigger reception */
ret = i2s_trigger(i2s_host_dev, I2S_DIR_RX, I2S_TRIGGER_START);
if (ret) {
LOG_ERR("I2S RX failed with code %d", ret);
}
return ret;
}
static int audio_driver_stop_host_streams(void)
{
int ret;
/* stop transmission */
ret = i2s_trigger(i2s_host_dev, I2S_DIR_TX, I2S_TRIGGER_STOP);
if (ret) {
LOG_ERR("I2S TX failed with code %d", ret);
}
/* stop reception */
ret = i2s_trigger(i2s_host_dev, I2S_DIR_RX, I2S_TRIGGER_STOP);
if (ret) {
LOG_ERR("I2S RX failed with code %d", ret);
}
return ret;
}
static void audio_driver_config_host_streams(void)
{
int ret;
struct i2s_config i2s_cfg;
i2s_host_dev = device_get_binding(HOST_INOUT_DEV_NAME);
if (!i2s_host_dev) {
LOG_ERR("unable to find device %s", HOST_INOUT_DEV_NAME);
return;
}
/* Configure */
i2s_cfg.word_size = AUDIO_SAMPLE_WIDTH;
i2s_cfg.channels = NUM_HOST_CHANNELS;
i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S | I2S_FMT_CLK_NF_NB;
i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER;
i2s_cfg.frame_clk_freq = AUDIO_SAMPLE_FREQ;
i2s_cfg.block_size = HOST_FRAME_BYTES;
i2s_cfg.mem_slab = &host_inout_mem_slab;
i2s_cfg.timeout = 0;
k_mem_slab_init(&host_inout_mem_slab, &audio_buffers.host_inout[0][0],
HOST_FRAME_BYTES, HOST_INOUT_BUF_COUNT);
/* Configure host input/output I2S */
ret = i2s_configure(i2s_host_dev, I2S_DIR_RX, &i2s_cfg);
if (ret != 0) {
LOG_ERR("i2s_configure RX failed with %d error", ret);
}
}
static void audio_driver_config_periph_streams(void)
{
int ret;
struct i2s_config i2s_cfg;
struct audio_codec_cfg codec = {
.dai_type = AUDIO_DAI_TYPE_I2S,
.dai_cfg.i2s = {
.word_size = AUDIO_SAMPLE_WIDTH,
.channels = NUM_SPK_CHANNELS,
.format = I2S_FMT_DATA_FORMAT_I2S |
I2S_FMT_CLK_NF_NB,
.options = I2S_OPT_FRAME_CLK_SLAVE |
I2S_OPT_BIT_CLK_SLAVE,
.frame_clk_freq = AUDIO_SAMPLE_FREQ,
.block_size = SPK_FRAME_BYTES,
.timeout = 0,
},
};
struct pcm_stream_cfg stream = {
.pcm_rate = AUDIO_SAMPLE_FREQ,
.pcm_width = AUDIO_SAMPLE_WIDTH,
.block_size = MIC_FRAME_BYTES,
.mem_slab = &mic_in_mem_slab,
};
struct dmic_cfg cfg = {
.io = {
.min_pdm_clk_freq = 1024000,
.max_pdm_clk_freq = 4800000,
.min_pdm_clk_dc = 48,
.max_pdm_clk_dc = 52,
.pdm_clk_pol = 0,
.pdm_data_pol = 0,
},
.streams = &stream,
.channel = {
.req_chan_map_lo =
dmic_build_channel_map(0, 0, PDM_CHAN_RIGHT) |
dmic_build_channel_map(1, 0, PDM_CHAN_LEFT) |
dmic_build_channel_map(2, 2, PDM_CHAN_RIGHT) |
dmic_build_channel_map(3, 2, PDM_CHAN_LEFT) |
dmic_build_channel_map(4, 1, PDM_CHAN_RIGHT) |
dmic_build_channel_map(5, 1, PDM_CHAN_LEFT) |
dmic_build_channel_map(6, 3, PDM_CHAN_RIGHT) |
dmic_build_channel_map(7, 3, PDM_CHAN_LEFT),
.req_num_chan = NUM_MIC_CHANNELS,
.req_num_streams = 1,
},
};
k_mem_slab_init(&mic_in_mem_slab, &audio_buffers.mic_in[0][0],
MIC_FRAME_BYTES, MIC_IN_BUF_COUNT);
dmic_device = device_get_binding(DMIC_DEV_NAME);
if (!dmic_device) {
LOG_ERR("unable to find device %s", DMIC_DEV_NAME);
return;
}
ret = dmic_configure(dmic_device, &cfg);
if (ret != 0) {
LOG_ERR("dmic_configure failed with %d error", ret);
}
i2s_spk_out_dev = device_get_binding(SPK_OUT_DEV_NAME);
if (!i2s_spk_out_dev) {
LOG_ERR("unable to find device %s", SPK_OUT_DEV_NAME);
return;
}
codec_dev = device_get_binding(DT_LABEL(DT_INST(0, ti_tlv320dac)));
if (!codec_dev) {
LOG_ERR("unable to find device %s", DT_LABEL(DT_INST(0, ti_tlv320dac)));
return;
}
/* Configure */
i2s_cfg.word_size = AUDIO_SAMPLE_WIDTH;
i2s_cfg.channels = NUM_SPK_CHANNELS;
i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S | I2S_FMT_CLK_NF_NB;
i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER;
i2s_cfg.frame_clk_freq = AUDIO_SAMPLE_FREQ;
i2s_cfg.block_size = SPK_FRAME_BYTES;
i2s_cfg.mem_slab = &spk_out_mem_slab;
i2s_cfg.timeout = 0;
k_mem_slab_init(&spk_out_mem_slab, &audio_buffers.spk_out[0][0],
SPK_FRAME_BYTES, SPK_OUT_BUF_COUNT);
ret = i2s_configure(i2s_spk_out_dev, I2S_DIR_TX, &i2s_cfg);
if (ret != 0) {
LOG_ERR("i2s_configure TX failed with %d error", ret);
}
/* configure codec */
codec.mclk_freq = soc_get_ref_clk_freq();
audio_codec_configure(codec_dev, &codec);
}
static void audio_driver_start_periph_streams(void)
{
int ret;
/* start codec output */
audio_codec_start_output(codec_dev);
ret = i2s_trigger(i2s_spk_out_dev, I2S_DIR_TX, I2S_TRIGGER_START);
if (ret) {
LOG_ERR("I2S TX failed with code %d", ret);
}
ret = dmic_trigger(dmic_device, DMIC_TRIGGER_START);
if (ret) {
LOG_ERR("dmic_trigger failed with code %d", ret);
}
}
static void audio_driver_stop_periph_streams(void)
{
int ret;
ret = i2s_trigger(i2s_spk_out_dev, I2S_DIR_TX, I2S_TRIGGER_STOP);
if (ret) {
LOG_ERR("I2S TX failed with code %d", ret);
}
/* trigger transmission */
ret = dmic_trigger(dmic_device, DMIC_TRIGGER_STOP);
if (ret) {
LOG_ERR("dmic_trigger failed with code %d", ret);
}
}
int audio_driver_start(void)
{
if (audio_io_started == true) {
LOG_INF("Audio I/O already started ...");
return 0;
}
LOG_INF("Starting Audio I/O...");
/*
* start the playback path first followed by the capture path
* This ensures that by the time the capture frame tick is
* processed, the playback input samples are ready and playback
* output samples are transmitted
*/
audio_driver_send_zeros_frame();
audio_driver_send_zeros_frame();
audio_driver_start_host_streams();
audio_driver_start_periph_streams();
audio_io_started = true;
k_sem_give(&audio_drv_sync_sem);
return 0;
}
int audio_driver_stop(void)
{
if (audio_io_started == false) {
LOG_INF("Audio I/O already stopped ...");
return 0;
}
k_sem_take(&audio_drv_sync_sem, K_FOREVER);
audio_driver_stop_host_streams();
audio_driver_stop_periph_streams();
audio_io_started = false;
LOG_INF("Stopped Audio I/O...");
return 0;
}
static void audio_drv_thread(void)
{
LOG_INF("Starting Audio Driver thread ,,,");
LOG_INF("Configuring Host Audio Streams ...");
audio_driver_config_host_streams();
LOG_INF("Configuring Peripheral Audio Streams ...");
audio_driver_config_periph_streams();
LOG_INF("Initializing Audio Core Engine ...");
audio_core_initialize();
LOG_DBG("mic_in_mem_slab %p", &mic_in_mem_slab);
LOG_DBG("spk_out_mem_slab %p", &spk_out_mem_slab);
LOG_DBG("host_inout_mem_slab %p", &host_inout_mem_slab);
audio_driver_start();
while (true) {
k_sem_take(&audio_drv_sync_sem, K_FOREVER);
audio_driver_process_audio_input();
audio_driver_process_audio_output();
k_sem_give(&audio_drv_sync_sem);
audio_core_notify_frame_tick();
}
}