| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- /*
- * Copyright (c) 2006-2025, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Email: opensource_embedded@phytium.com.cn
- *
- * Change Logs:
- * Date Author Notes
- * 2025-01-21 zhangyan first version
- *
- */
- #include <rtthread.h>
- #include <rtdevice.h>
- #include <drv_i2s.h>
- #include "fi2s.h"
- #include "fi2s_hw.h"
- #include "fddma.h"
- #include "fddma_hw.h"
- #include "fddma_bdl.h"
- #include "fdevice.h"
- #include "fes8336.h"
- #define DBG_TAG "drv.i2s"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- #define PER_BUFFER_SIZE 2048
- #define TX_RX_BUF_LEN 2048
- static struct phytium_i2s_device i2s_dev0;
- extern FI2c master_device;
- static FEs8336Controller fes8336 =
- {
- .fes8336_device.name = "es8336",
- .dev_type = DEV_TYPE_MIO,
- .controller_id = FMIO14_ID,
- };
- static const u32 ddma_ctrl_id = FDDMA2_I2S_ID;
- static const u32 i2s_ctrl_id = FI2S0_ID;
- struct phytium_i2s_device
- {
- const char *name;
- struct rt_audio_device audio;
- struct rt_audio_configure config;
- FI2s i2s_ctrl;
- FI2sConfig i2s_config;
- FDdma ddmac;
- FDdmaConfig ddmac_config;
- rt_uint8_t *rx_fifo;
- FDdmaChanConfig rx_config;
- rt_uint8_t rx_channel; /* 接收通道为DDMA通道1 */
- rt_uint8_t volume;
- };
- static void FDdmaSetupInterrupt(FDdma *const instance)
- {
- FASSERT(instance);
- FDdmaConfig *config = &instance->config;
- rt_uint32_t cpu_id = rt_hw_cpu_id();
- rt_hw_interrupt_set_target_cpus(config->irq_num, cpu_id);
- rt_hw_interrupt_set_priority(config->irq_num, 16);
- /* register intr callback */
- rt_hw_interrupt_install(config->irq_num,
- FDdmaIrqHandler,
- instance,
- NULL);
- /* enable ddma0 irq */
- rt_hw_interrupt_umask(config->irq_num);
- return;
- }
- static FError FI2sEs8336Init(u32 word_length)
- {
- FError ret = FT_SUCCESS;
- u32 volumel = 0x1;
- FIOMuxInit();
- FIOPadSetI2sMux();
- ret = FEs8336DevRegister(&fes8336.fes8336_device);
- if (FT_SUCCESS != ret)
- {
- printf("ES8336 dev register failed.\r\n");
- return ret;
- }
- ret = FDeviceInit(&fes8336.fes8336_device);
- if (FT_SUCCESS != ret)
- {
- printf("ES8336 dev init failed.\r\n");
- return ret;
- }
- ret = FDeviceOpen(&fes8336.fes8336_device, FDEVICE_FLAG_RDWR);
- if (FT_SUCCESS != ret)
- {
- printf("ES8336 dev open failed.\r\n");
- return ret;
- }
- ret = FDeviceControl(&fes8336.fes8336_device, FES8336_SET_FORMAT, &word_length); /* 设置ES8336工作模式 */
- if (FT_SUCCESS != ret)
- {
- printf("Set the ES8336 word length failed.\r\n");
- return ret;
- }
- ret = FDeviceControl(&fes8336.fes8336_device, FES8336_SET_VOLUMEL, &volumel); /* 设置ES8336工作模式 */
- if (FT_SUCCESS != ret)
- {
- printf("Set the ES8336 volumel failed.\r\n");
- return ret;
- }
- return ret;
- }
- static FError FI2sRxInit(struct phytium_i2s_device *i2s_dev, u32 word_length)
- {
- FError ret = FI2S_SUCCESS;
- memset(&i2s_dev->i2s_ctrl, 0, sizeof(FI2s));
- memset(&i2s_dev->i2s_ctrl, 0, sizeof(FI2sConfig));
- i2s_dev->i2s_ctrl.data_config.word_length = word_length;
- i2s_dev->i2s_config = *FI2sLookupConfig(i2s_ctrl_id);
- ret = FI2sCfgInitialize(&i2s_dev->i2s_ctrl, &i2s_dev->i2s_config);
- if (FI2S_SUCCESS != ret)
- {
- printf("Init the i2s failed.\r\n");
- return ret;
- }
- FI2sClkOutDiv(&i2s_dev->i2s_ctrl, i2s_dev->config.samplerate);
- FI2sTxRxEnable(&i2s_dev->i2s_ctrl, TRUE); /* 模块使能 */
- return ret;
- }
- static FError FI2sRxDdmaInit(struct phytium_i2s_device *i2s_dev)
- {
- FError ret = FI2S_SUCCESS;
- i2s_dev->ddmac_config = *FDdmaLookupConfig(ddma_ctrl_id);
- ret = FDdmaCfgInitialize(&i2s_dev->ddmac, &i2s_dev->ddmac_config);
- if (FI2S_SUCCESS != ret)
- {
- printf("DDMA config initialization failed.\r\n");
- return ret;
- }
- return ret;
- }
- static FError FI2sDdmaDeviceRX(struct phytium_i2s_device *i2s_dev, u32 work_mode, const void *src, fsize_t total_bytes, fsize_t per_buff_len)
- {
- FError ret = FI2S_SUCCESS;
- fsize_t bdl_num = total_bytes / per_buff_len;
- rt_hw_cpu_dcache_clean((uintptr)src, total_bytes);
- for (u32 chan = FDDMA_CHAN_0; chan < FDDMA_NUM_OF_CHAN; chan++) /* 清除中断 */
- {
- FDdmaClearChanIrq(i2s_dev->ddmac_config.base_addr, chan, i2s_dev->ddmac_config.caps);
- }
- FDdmaBdlDesc *bdl_desc_list = rt_malloc_align(bdl_num * sizeof(FDdmaBdlDesc), FDDMA_BDL_ADDR_ALIGMENT); /* DDMA描述符首地址需128字节对齐 */
- if ((NULL == bdl_desc_list))
- {
- printf("FDdmaBdlDesc allocate failed.\r\n");
- return FDDMA_ERR_IS_USED;
- }
- memset(bdl_desc_list, 0, bdl_num * sizeof(FDdmaBdlDesc));
- FDdmaBdlDescConfig *bdl_desc_config = rt_calloc(1, bdl_num * sizeof(FDdmaBdlDescConfig));
- if ((NULL == bdl_desc_config))
- {
- printf("FDdmaBdlDescConfig allocate failed.\r\n");
- return FDDMA_ERR_IS_USED;
- }
- /* set BDL descriptors */
- for (fsize_t loop = 0; loop < bdl_num; loop++)
- {
- bdl_desc_config[loop].current_desc_num = loop;
- bdl_desc_config[loop].src_addr = (uintptr)(src + per_buff_len * loop);
- bdl_desc_config[loop].trans_length = per_buff_len;
- }
- bdl_desc_config[bdl_num -1].ioc = TRUE;
- /* set BDL descriptor list with descriptor configs */
- for (fsize_t loop = 0; loop < bdl_num; loop++)
- {
- FDdmaBDLSetDesc(bdl_desc_list, &bdl_desc_config[loop]);
- }
- i2s_dev->rx_config.slave_id = 0U,
- i2s_dev->rx_config.req_mode = AUDIO_PCM_STREAM_CAPTURE;
- i2s_dev->rx_config.ddr_addr = (uintptr)src;
- i2s_dev->rx_config.dev_addr = i2s_dev->i2s_config.base_addr + FI2S_RXDMA ;
- i2s_dev->rx_config.trans_len = total_bytes;
- i2s_dev->rx_config.timeout = 0xffff,
- i2s_dev->rx_config.first_desc_addr = (uintptr)bdl_desc_list;
- i2s_dev->rx_config.valid_desc_num = bdl_num;
- ret = FDdmaChanBdlConfigure(&i2s_dev->ddmac, i2s_dev->rx_channel, &i2s_dev->rx_config);
- if (ret != FI2S_SUCCESS)
- {
- printf("DDMA BDL configure failer.\r\n");
- return ret;
- }
- rt_free(bdl_desc_config);
- return ret;
- }
- void dma_transfer_callback(void *args)
- {
- #if defined(RT_USING_I2S0)
- rt_audio_rx_done(&i2s_dev0.audio, &i2s_dev0.rx_fifo[0], TX_RX_BUF_LEN);
- #endif
- }
- static rt_err_t i2s_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
- {
- rt_err_t result = RT_EOK;
- struct phytium_i2s_device *i2s_dev;
- RT_ASSERT(audio != RT_NULL);
- i2s_dev = (struct phytium_i2s_device *)audio->parent.user_data;
- switch (caps->main_type)
- {
- case AUDIO_TYPE_QUERY: /* qurey the types of hw_codec device */
- {
- switch (caps->sub_type)
- {
- case AUDIO_TYPE_QUERY:
- caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;
- break;
- default:
- result = -RT_ERROR;
- break;
- }
- break;
- }
- case AUDIO_TYPE_OUTPUT: /* Provide capabilities of OUTPUT unit */
- {
- switch (caps->sub_type)
- {
- case AUDIO_DSP_PARAM:
- caps->udata.config.samplerate = i2s_dev->config.samplerate;
- caps->udata.config.channels = i2s_dev->config.channels;
- caps->udata.config.samplebits = i2s_dev->config.samplebits;
- break;
- case AUDIO_DSP_SAMPLERATE:
- caps->udata.config.samplerate = i2s_dev->config.samplerate;
- break;
- case AUDIO_DSP_CHANNELS:
- caps->udata.config.channels = i2s_dev->config.channels;
- break;
- case AUDIO_DSP_SAMPLEBITS:
- caps->udata.config.samplebits = i2s_dev->config.samplebits;
- break;
- default:
- result = -RT_ERROR;
- break;
- }
- break;
- }
- case AUDIO_TYPE_MIXER: /* report the Mixer Units */
- {
- switch (caps->sub_type)
- {
- case AUDIO_MIXER_QUERY:
- caps->udata.mask = AUDIO_MIXER_VOLUME;
- break;
- case AUDIO_MIXER_VOLUME:
- caps->udata.value = i2s_dev->volume;
- break;
- default:
- result = -RT_ERROR;
- break;
- }
- break;
- }
- default:
- result = -RT_ERROR;
- break;
- }
- return result;
- }
- static rt_err_t i2s_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
- {
- rt_err_t result = RT_EOK;
- struct phytium_i2s_device *i2s_dev;
- struct rt_audio_replay *replay;
- RT_ASSERT(audio != RT_NULL);
- i2s_dev = (struct phytium_i2s_device *)audio->parent.user_data;
- switch (caps->main_type)
- {
- case AUDIO_TYPE_INPUT:
- {
- switch (caps->sub_type)
- {
- case AUDIO_DSP_PARAM:
- {
- struct rt_audio_configure config = caps->udata.config;
- i2s_dev->config.channels = config.channels;
- i2s_dev->config.samplebits = config.samplebits;
- i2s_dev->config.samplerate = config.samplerate;
- }
- default:
- result = -RT_ERROR;
- break;
- }
- break;
- }
- default:
- break;
- }
- return result;
- }
- static rt_err_t i2s_init(struct rt_audio_device *audio)
- {
- struct phytium_i2s_device *i2s_dev;
- RT_ASSERT(audio != RT_NULL);
- i2s_dev = (struct phytium_i2s_device *)audio->parent.user_data;
- FError ret = FT_SUCCESS;
- u32 word_length = i2s_dev->config.samplebits; /* 16-bits word length */
- FI2sEs8336Init(word_length);
- if (FT_SUCCESS != ret)
- {
- printf("Init the escodec failed.\r\n");
- return ret;
- }
- ret = FI2sRxDdmaInit(i2s_dev);
- if (FT_SUCCESS != ret)
- {
- printf("Init DDMA-2 failed.\r\n");
- return ret;
- }
- ret = FI2sRxInit(i2s_dev, word_length);
- if (FI2S_SUCCESS != ret)
- {
- printf("Init the I2S failed.\r\n");
- return ret;
- }
- FDdmaSetupInterrupt(&i2s_dev->ddmac);
- FDdmaRegisterChanEvtHandler(&i2s_dev->ddmac, i2s_dev->rx_channel, FDDMA_CHAN_EVT_REQ_DONE, dma_transfer_callback, (void *)i2s_dev);
- return ret;
- }
- static rt_err_t i2s_start(struct rt_audio_device *audio, int stream)
- {
- struct phytium_i2s_device *i2s_dev;
- RT_ASSERT(audio != RT_NULL);
- i2s_dev = (struct phytium_i2s_device *)audio->parent.user_data;
- if (stream == AUDIO_STREAM_REPLAY)
- {
- }
- else if(stream == AUDIO_STREAM_RECORD)
- {
- FI2sDdmaDeviceRX(i2s_dev, AUDIO_PCM_STREAM_CAPTURE, &i2s_dev->rx_fifo[0], TX_RX_BUF_LEN, PER_BUFFER_SIZE);
- FDdmaChanActive(&i2s_dev->ddmac, i2s_dev->rx_channel);
- }
- FDdmaStart(&i2s_dev->ddmac);
- return RT_EOK;
- }
- static rt_err_t i2s_stop(struct rt_audio_device *audio, int stream)
- {
- struct phytium_i2s_device *i2s_dev;
- RT_ASSERT(audio != RT_NULL);
- i2s_dev = (struct phytium_i2s_device *)audio->parent.user_data;
- return RT_EOK;
- }
- static struct rt_audio_ops i2s_ops =
- {
- .getcaps = i2s_getcaps,
- .configure = i2s_configure,
- .init = i2s_init,
- .start = i2s_start,
- .stop = i2s_stop,
- .transmit = NULL,
- .buffer_info = NULL,
- };
- static int i2s_controller_init(struct phytium_i2s_device *i2s_dev)
- {
- struct rt_audio_device *audio = &i2s_dev->audio;
- i2s_dev->rx_fifo = rt_calloc(1, TX_RX_BUF_LEN);
- if (i2s_dev->rx_fifo == RT_NULL)
- {
- return -RT_ENOMEM;
- }
- i2s_dev->audio.ops = &i2s_ops;
- int ret = rt_audio_register(audio, i2s_dev->name, RT_DEVICE_FLAG_RDONLY, (void *)i2s_dev);
- RT_ASSERT(RT_EOK == ret);
- LOG_D("i2s_controller_init i2s bus reg success. \n");
- return ret;
- }
- int rt_hw_i2s_init(void)
- {
- #if defined(RT_USING_I2S0)
- i2s_dev0.name = "I2S0";
- i2s_dev0.i2s_ctrl.config.instance_id = FI2S0_ID;
- i2s_dev0.config.channels = 1;
- i2s_dev0.config.samplerate = RT_I2S_SAMPLERATE;
- i2s_dev0.config.samplebits = RT_I2S_SAMPLEBITS;
- i2s_controller_init(&i2s_dev0);
- #endif
- return RT_EOK;
- }
- INIT_DEVICE_EXPORT(rt_hw_i2s_init);
|