summaryrefslogtreecommitdiff
path: root/bsp/phytium/libraries/drivers/drv_i2s.c
blob: 1fe7658f183f62ac3be4b8555a8ed9057cd16713 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
/*
 * Copyright (c) 2006-2025, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Email: [email protected]
 *
 * 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);