1 /*
2  * Copyright (c) 2006-2025, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Email: opensource_embedded@phytium.com.cn
7  *
8  * Change Logs:
9  * Date           Author       Notes
10  * 2025-01-21     zhangyan     first version
11  *
12  */
13 
14 #include <rtthread.h>
15 #include <rtdevice.h>
16 
17 #include <drv_i2s.h>
18 #include "fi2s.h"
19 #include "fi2s_hw.h"
20 #include "fddma.h"
21 #include "fddma_hw.h"
22 #include "fddma_bdl.h"
23 #include "fdevice.h"
24 #include "fes8336.h"
25 
26 #define DBG_TAG              "drv.i2s"
27 #define DBG_LVL              DBG_INFO
28 #include <rtdbg.h>
29 
30 #define PER_BUFFER_SIZE          2048
31 #define TX_RX_BUF_LEN            2048
32 
33 static struct phytium_i2s_device i2s_dev0;
34 extern FI2c master_device;
35 
36 static FEs8336Controller fes8336 =
37 {
38     .fes8336_device.name = "es8336",
39     .dev_type = DEV_TYPE_MIO,
40     .controller_id = FMIO14_ID,
41 };
42 static const u32 ddma_ctrl_id = FDDMA2_I2S_ID;
43 static const u32 i2s_ctrl_id = FI2S0_ID;
44 
45 
46 struct phytium_i2s_device
47 {
48     const char *name;
49 
50     struct rt_audio_device audio;
51     struct rt_audio_configure config;
52 
53     FI2s i2s_ctrl;
54     FI2sConfig i2s_config;
55     FDdma ddmac;
56     FDdmaConfig ddmac_config;
57 
58     rt_uint8_t *rx_fifo;
59     FDdmaChanConfig rx_config;
60     rt_uint8_t rx_channel; /* 接收通道为DDMA通道1 */
61     rt_uint8_t volume;
62 };
63 
FDdmaSetupInterrupt(FDdma * const instance)64 static void FDdmaSetupInterrupt(FDdma *const instance)
65 {
66     FASSERT(instance);
67     FDdmaConfig *config = &instance->config;
68 
69     rt_uint32_t cpu_id = rt_hw_cpu_id();
70     rt_hw_interrupt_set_target_cpus(config->irq_num, cpu_id);
71     rt_hw_interrupt_set_priority(config->irq_num, 16);
72     /* register intr callback */
73     rt_hw_interrupt_install(config->irq_num,
74                             FDdmaIrqHandler,
75                             instance,
76                             NULL);
77 
78     /* enable ddma0 irq */
79     rt_hw_interrupt_umask(config->irq_num);
80 
81     return;
82 }
83 
FI2sEs8336Init(u32 word_length)84 static FError FI2sEs8336Init(u32 word_length)
85 {
86     FError ret = FT_SUCCESS;
87     u32 volumel = 0x1;
88 
89     FIOMuxInit();
90     FIOPadSetI2sMux();
91 
92     ret = FEs8336DevRegister(&fes8336.fes8336_device);
93     if (FT_SUCCESS != ret)
94     {
95         printf("ES8336 dev register failed.\r\n");
96         return ret;
97     }
98 
99     ret = FDeviceInit(&fes8336.fes8336_device);
100     if (FT_SUCCESS != ret)
101     {
102         printf("ES8336 dev init failed.\r\n");
103         return ret;
104     }
105 
106     ret = FDeviceOpen(&fes8336.fes8336_device, FDEVICE_FLAG_RDWR);
107     if (FT_SUCCESS != ret)
108     {
109         printf("ES8336 dev open failed.\r\n");
110         return ret;
111     }
112 
113     ret = FDeviceControl(&fes8336.fes8336_device, FES8336_SET_FORMAT, &word_length); /* 设置ES8336工作模式 */
114     if (FT_SUCCESS != ret)
115     {
116         printf("Set the ES8336 word length failed.\r\n");
117         return ret;
118     }
119 
120     ret = FDeviceControl(&fes8336.fes8336_device, FES8336_SET_VOLUMEL, &volumel); /* 设置ES8336工作模式 */
121     if (FT_SUCCESS != ret)
122     {
123         printf("Set the ES8336 volumel failed.\r\n");
124         return ret;
125     }
126 
127     return ret;
128 }
129 
FI2sRxInit(struct phytium_i2s_device * i2s_dev,u32 word_length)130 static FError FI2sRxInit(struct phytium_i2s_device *i2s_dev, u32 word_length)
131 {
132     FError ret = FI2S_SUCCESS;
133 
134     memset(&i2s_dev->i2s_ctrl, 0, sizeof(FI2s));
135     memset(&i2s_dev->i2s_ctrl, 0, sizeof(FI2sConfig));
136     i2s_dev->i2s_ctrl.data_config.word_length = word_length;
137     i2s_dev->i2s_config = *FI2sLookupConfig(i2s_ctrl_id);
138 
139     ret = FI2sCfgInitialize(&i2s_dev->i2s_ctrl, &i2s_dev->i2s_config);
140     if (FI2S_SUCCESS != ret)
141     {
142         printf("Init the i2s failed.\r\n");
143         return ret;
144     }
145 
146     FI2sClkOutDiv(&i2s_dev->i2s_ctrl, i2s_dev->config.samplerate);
147     FI2sTxRxEnable(&i2s_dev->i2s_ctrl, TRUE); /* 模块使能 */
148     return ret;
149 }
150 
FI2sRxDdmaInit(struct phytium_i2s_device * i2s_dev)151 static FError FI2sRxDdmaInit(struct phytium_i2s_device *i2s_dev)
152 {
153     FError ret = FI2S_SUCCESS;
154     i2s_dev->ddmac_config = *FDdmaLookupConfig(ddma_ctrl_id);
155 
156     ret = FDdmaCfgInitialize(&i2s_dev->ddmac, &i2s_dev->ddmac_config);
157     if (FI2S_SUCCESS != ret)
158     {
159         printf("DDMA config initialization failed.\r\n");
160         return ret;
161     }
162 
163     return ret;
164 }
165 
FI2sDdmaDeviceRX(struct phytium_i2s_device * i2s_dev,u32 work_mode,const void * src,fsize_t total_bytes,fsize_t per_buff_len)166 static FError FI2sDdmaDeviceRX(struct phytium_i2s_device *i2s_dev, u32 work_mode, const void *src, fsize_t total_bytes, fsize_t per_buff_len)
167 {
168     FError ret = FI2S_SUCCESS;
169     fsize_t bdl_num = total_bytes / per_buff_len;
170 
171     rt_hw_cpu_dcache_clean((uintptr)src, total_bytes);
172 
173     for (u32 chan = FDDMA_CHAN_0; chan < FDDMA_NUM_OF_CHAN; chan++) /* 清除中断 */
174     {
175         FDdmaClearChanIrq(i2s_dev->ddmac_config.base_addr, chan, i2s_dev->ddmac_config.caps);
176     }
177 
178     FDdmaBdlDesc *bdl_desc_list = rt_malloc_align(bdl_num * sizeof(FDdmaBdlDesc), FDDMA_BDL_ADDR_ALIGMENT); /* DDMA描述符首地址需128字节对齐 */
179     if ((NULL == bdl_desc_list))
180     {
181         printf("FDdmaBdlDesc allocate failed.\r\n");
182         return FDDMA_ERR_IS_USED;
183     }
184     memset(bdl_desc_list, 0, bdl_num * sizeof(FDdmaBdlDesc));
185 
186     FDdmaBdlDescConfig *bdl_desc_config = rt_calloc(1, bdl_num * sizeof(FDdmaBdlDescConfig));
187     if ((NULL == bdl_desc_config))
188     {
189         printf("FDdmaBdlDescConfig allocate failed.\r\n");
190         return FDDMA_ERR_IS_USED;
191     }
192     /* set BDL descriptors */
193     for (fsize_t loop = 0; loop < bdl_num; loop++)
194     {
195         bdl_desc_config[loop].current_desc_num = loop;
196         bdl_desc_config[loop].src_addr = (uintptr)(src + per_buff_len * loop);
197         bdl_desc_config[loop].trans_length = per_buff_len;
198     }
199        bdl_desc_config[bdl_num -1].ioc = TRUE;
200     /* set BDL descriptor list with descriptor configs */
201     for (fsize_t loop = 0; loop <  bdl_num; loop++)
202     {
203         FDdmaBDLSetDesc(bdl_desc_list, &bdl_desc_config[loop]);
204     }
205 
206     i2s_dev->rx_config.slave_id = 0U,
207     i2s_dev->rx_config.req_mode = AUDIO_PCM_STREAM_CAPTURE;
208     i2s_dev->rx_config.ddr_addr = (uintptr)src;
209     i2s_dev->rx_config.dev_addr = i2s_dev->i2s_config.base_addr + FI2S_RXDMA ;
210     i2s_dev->rx_config.trans_len = total_bytes;
211     i2s_dev->rx_config.timeout = 0xffff,
212     i2s_dev->rx_config.first_desc_addr = (uintptr)bdl_desc_list;
213     i2s_dev->rx_config.valid_desc_num = bdl_num;
214 
215     ret = FDdmaChanBdlConfigure(&i2s_dev->ddmac, i2s_dev->rx_channel, &i2s_dev->rx_config);
216 
217     if (ret !=  FI2S_SUCCESS)
218     {
219         printf("DDMA BDL configure failer.\r\n");
220         return ret;
221     }
222     rt_free(bdl_desc_config);
223 
224     return ret;
225 }
226 
dma_transfer_callback(void * args)227 void dma_transfer_callback(void *args)
228 {
229 #if defined(RT_USING_I2S0)
230     rt_audio_rx_done(&i2s_dev0.audio, &i2s_dev0.rx_fifo[0], TX_RX_BUF_LEN);
231 #endif
232 }
233 
i2s_getcaps(struct rt_audio_device * audio,struct rt_audio_caps * caps)234 static rt_err_t i2s_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
235 {
236     rt_err_t result = RT_EOK;
237     struct phytium_i2s_device *i2s_dev;
238 
239     RT_ASSERT(audio != RT_NULL);
240     i2s_dev = (struct phytium_i2s_device *)audio->parent.user_data;
241 
242     switch (caps->main_type)
243     {
244     case AUDIO_TYPE_QUERY: /* qurey the types of hw_codec device */
245     {
246         switch (caps->sub_type)
247         {
248         case AUDIO_TYPE_QUERY:
249             caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;
250             break;
251 
252         default:
253             result = -RT_ERROR;
254             break;
255         }
256 
257         break;
258     }
259 
260     case AUDIO_TYPE_OUTPUT: /* Provide capabilities of OUTPUT unit */
261     {
262         switch (caps->sub_type)
263         {
264         case AUDIO_DSP_PARAM:
265             caps->udata.config.samplerate   = i2s_dev->config.samplerate;
266             caps->udata.config.channels     = i2s_dev->config.channels;
267             caps->udata.config.samplebits   = i2s_dev->config.samplebits;
268             break;
269 
270         case AUDIO_DSP_SAMPLERATE:
271             caps->udata.config.samplerate   = i2s_dev->config.samplerate;
272             break;
273 
274         case AUDIO_DSP_CHANNELS:
275             caps->udata.config.channels     = i2s_dev->config.channels;
276             break;
277 
278         case AUDIO_DSP_SAMPLEBITS:
279             caps->udata.config.samplebits   = i2s_dev->config.samplebits;
280             break;
281 
282         default:
283             result = -RT_ERROR;
284             break;
285         }
286 
287         break;
288     }
289 
290     case AUDIO_TYPE_MIXER: /* report the Mixer Units */
291     {
292         switch (caps->sub_type)
293         {
294         case AUDIO_MIXER_QUERY:
295             caps->udata.mask = AUDIO_MIXER_VOLUME;
296             break;
297 
298         case AUDIO_MIXER_VOLUME:
299             caps->udata.value = i2s_dev->volume;
300             break;
301 
302         default:
303             result = -RT_ERROR;
304             break;
305         }
306 
307         break;
308     }
309 
310     default:
311         result = -RT_ERROR;
312         break;
313     }
314 
315     return result;
316 }
317 
i2s_configure(struct rt_audio_device * audio,struct rt_audio_caps * caps)318 static rt_err_t i2s_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
319 {
320     rt_err_t result = RT_EOK;
321     struct phytium_i2s_device *i2s_dev;
322     struct rt_audio_replay *replay;
323 
324     RT_ASSERT(audio != RT_NULL);
325     i2s_dev = (struct phytium_i2s_device *)audio->parent.user_data;
326 
327     switch (caps->main_type)
328     {
329     case AUDIO_TYPE_INPUT:
330     {
331         switch (caps->sub_type)
332         {
333         case AUDIO_DSP_PARAM:
334         {
335             struct rt_audio_configure config = caps->udata.config;
336             i2s_dev->config.channels = config.channels;
337             i2s_dev->config.samplebits = config.samplebits;
338             i2s_dev->config.samplerate = config.samplerate;
339         }
340 
341         default:
342             result = -RT_ERROR;
343             break;
344         }
345 
346         break;
347     }
348 
349     default:
350         break;
351     }
352     return result;
353 }
354 
i2s_init(struct rt_audio_device * audio)355 static rt_err_t i2s_init(struct rt_audio_device *audio)
356 {
357     struct phytium_i2s_device *i2s_dev;
358     RT_ASSERT(audio != RT_NULL);
359     i2s_dev = (struct phytium_i2s_device *)audio->parent.user_data;
360     FError ret = FT_SUCCESS;
361     u32 word_length = i2s_dev->config.samplebits; /* 16-bits word length */
362 
363     FI2sEs8336Init(word_length);
364     if (FT_SUCCESS != ret)
365     {
366         printf("Init the escodec failed.\r\n");
367         return ret;
368     }
369     ret = FI2sRxDdmaInit(i2s_dev);
370     if (FT_SUCCESS != ret)
371     {
372         printf("Init DDMA-2 failed.\r\n");
373         return ret;
374     }
375     ret = FI2sRxInit(i2s_dev, word_length);
376     if (FI2S_SUCCESS != ret)
377     {
378         printf("Init the I2S failed.\r\n");
379         return ret;
380     }
381 
382     FDdmaSetupInterrupt(&i2s_dev->ddmac);
383 
384     FDdmaRegisterChanEvtHandler(&i2s_dev->ddmac, i2s_dev->rx_channel, FDDMA_CHAN_EVT_REQ_DONE, dma_transfer_callback, (void *)i2s_dev);
385 
386     return ret;
387 }
388 
i2s_start(struct rt_audio_device * audio,int stream)389 static rt_err_t i2s_start(struct rt_audio_device *audio, int stream)
390 {
391     struct phytium_i2s_device *i2s_dev;
392     RT_ASSERT(audio != RT_NULL);
393     i2s_dev = (struct phytium_i2s_device *)audio->parent.user_data;
394 
395     if (stream == AUDIO_STREAM_REPLAY)
396     {
397 
398     }
399     else if(stream == AUDIO_STREAM_RECORD)
400     {
401         FI2sDdmaDeviceRX(i2s_dev, AUDIO_PCM_STREAM_CAPTURE, &i2s_dev->rx_fifo[0], TX_RX_BUF_LEN, PER_BUFFER_SIZE);
402         FDdmaChanActive(&i2s_dev->ddmac, i2s_dev->rx_channel);
403     }
404 
405     FDdmaStart(&i2s_dev->ddmac);
406 
407     return RT_EOK;
408 }
409 
i2s_stop(struct rt_audio_device * audio,int stream)410 static rt_err_t i2s_stop(struct rt_audio_device *audio, int stream)
411 {
412     struct phytium_i2s_device *i2s_dev;
413     RT_ASSERT(audio != RT_NULL);
414     i2s_dev = (struct phytium_i2s_device *)audio->parent.user_data;
415 
416     return RT_EOK;
417 }
418 
419 static struct rt_audio_ops i2s_ops =
420 {
421     .getcaps     = i2s_getcaps,
422     .configure   = i2s_configure,
423     .init        = i2s_init,
424     .start       = i2s_start,
425     .stop        = i2s_stop,
426     .transmit    = NULL,
427     .buffer_info = NULL,
428 };
429 
i2s_controller_init(struct phytium_i2s_device * i2s_dev)430 static int i2s_controller_init(struct phytium_i2s_device *i2s_dev)
431 {
432     struct rt_audio_device *audio = &i2s_dev->audio;
433 
434     i2s_dev->rx_fifo = rt_calloc(1, TX_RX_BUF_LEN);
435     if (i2s_dev->rx_fifo == RT_NULL)
436     {
437         return -RT_ENOMEM;
438     }
439 
440     i2s_dev->audio.ops = &i2s_ops;
441     int ret = rt_audio_register(audio, i2s_dev->name, RT_DEVICE_FLAG_RDONLY, (void *)i2s_dev);
442     RT_ASSERT(RT_EOK == ret);
443     LOG_D("i2s_controller_init i2s bus reg success. \n");
444     return ret;
445 }
446 
rt_hw_i2s_init(void)447 int rt_hw_i2s_init(void)
448 {
449 
450 #if defined(RT_USING_I2S0)
451     i2s_dev0.name = "I2S0";
452     i2s_dev0.i2s_ctrl.config.instance_id = FI2S0_ID;
453     i2s_dev0.config.channels = 1;
454     i2s_dev0.config.samplerate = RT_I2S_SAMPLERATE;
455     i2s_dev0.config.samplebits = RT_I2S_SAMPLEBITS;
456 
457     i2s_controller_init(&i2s_dev0);
458 #endif
459 
460     return RT_EOK;
461 }
462 
463 INIT_DEVICE_EXPORT(rt_hw_i2s_init);