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);