summaryrefslogtreecommitdiff
path: root/bsp/nuvoton/libraries/n9h30/Driver/Source/nu_i2s.c
blob: f53870352606857f026e6ed6d176f0c25d234dfa (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
/**************************************************************************//**
* @file     i2s.c
* @brief    N9H30 I2S driver source file
*
* @note
* SPDX-License-Identifier: Apache-2.0
* Copyright (C) 2018 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "N9H30.h"
#include "nu_sys.h"
#include "nu_i2s.h"

/** @addtogroup N9H30_Device_Driver N9H30 Device Driver
  @{
*/

/** @addtogroup N9H30_I2S_Driver I2S Driver
  @{
*/

/** @addtogroup N9H30_I2S_EXPORTED_CONSTANTS I2S Exported Constants
  @{
*/

/// @cond HIDDEN_SYMBOLS

typedef uint32_t (AU_CB_FUNC_T)(uint32_t);

static AU_CB_FUNC_T *g_fnPlayCallBack;
static AU_CB_FUNC_T *g_fnRecCallBack;
static uint8_t i2sOpened = 0;

/// @endcond /* HIDDEN_SYMBOLS */

/*@}*/ /* end of group N9H30_I2S_EXPORTED_CONSTANTS */

/** @addtogroup N9H30_I2S_EXPORTED_FUNCTIONS I2S Exported Functions
  @{
*/

/// @cond HIDDEN_SYMBOLS
/**
  * @brief Start to play
  * @param None
  * @return None
  */
static void i2sStartPlay(void)
{
    /* start playing */
    //sysprintf("IIS start playing...\n");

    outpw(REG_ACTL_PSR, 0x1);
    outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) | (1 << 5));
}

/**
  * @brief Stop to play
  * @param None
  * @return None
  */
static void i2sStopPlay(void)
{
    //sysprintf("IIS stop playing\n");

    /* stop playing */
    outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) & ~(1 << 5));
}

/**
  * @brief Start to record
  * @param None
  * @return None
  */
static void i2sStartRecord(void)
{
    /* start recording */
    //sysprintf("IIS start recording...\n");

    outpw(REG_ACTL_RSR, 0x1);
    outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) | (1 << 6));
}

/**
  * @brief Stop to record
  * @param None
  * @return None
  */
static void i2sStopRecord(void)
{
    //sysprintf("I2S stop recording\n");

    /* stop recording */
    outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) & ~(1 << 6));
}

/**
  * @brief Delay function
  * @param None
  * @return None
  */
static void Delay(int nCnt)
{
    int volatile loop;
    for (loop = 0; loop < nCnt * 10; loop++);
}

/**
  * @brief Interrupt service routine for i2s
  * @param None
  * @return None
  */
static void i2sISR(void)
{
    uint8_t u8SN;

    if (inpw(REG_ACTL_CON) & (1 << 10))
    {
        outpw(REG_ACTL_CON, inpw(REG_ACTL_CON) | (1 << 10)); //Clear TX INT

        if (inpw(REG_ACTL_PSR) & (1 << 4))
        {
            outpw(REG_ACTL_PSR, (1 << 4));
            //sysprintf("\ndebug:DMA_COUNTER_IRQ occur");
        }

        if (inpw(REG_ACTL_PSR) & (1 << 3))
        {
            outpw(REG_ACTL_PSR, (1 << 3));
            //sysprintf("\ndebug:DMA_DATA_ZERO_IRQ occur");
        }

        if (inpw(REG_ACTL_PSR) & 0x1)
        {
            outpw(REG_ACTL_PSR, 0x1);
            u8SN = (inpw(REG_ACTL_PSR) >> 5) & 0x7;
            g_fnPlayCallBack(u8SN);
        }
    }

    if (inpw(REG_ACTL_CON) & (1 << 11))
    {
        outpw(REG_ACTL_CON, inpw(REG_ACTL_CON) | (1 << 11)); //Clear RX INT

        if (inpw(REG_ACTL_RSR) & 0x1)
        {
            outpw(REG_ACTL_RSR, 0x1);
            u8SN = (inpw(REG_ACTL_RSR) >> 5) & 0x7;
            g_fnRecCallBack(u8SN);
        }
    }
}
/// @endcond /* HIDDEN_SYMBOLS */

/**
  * @brief Open i2s interface
  * @return open status
  * @retval I2S_ERR_BUSY error.
  * @retval 0 success.
  */
int32_t i2sOpen(void)
{
    if (i2sOpened)
        return I2S_ERR_BUSY;

    /* reset audio interface */
    outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) | (1 << 16));
    Delay(100);
    outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) & ~(1 << 16));
    Delay(100);

    /* reset IIS interface */
    outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) | 0x1);
    Delay(100);
    outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) & ~0x1);
    Delay(100);

    outpw(REG_ACTL_CON, inpw(REG_ACTL_CON) | (1 << 21) | (1 << 20));

    i2sOpened = 1;

    return 0;
}

/**
  * @brief Close i2s interface
  * @return None
  */
void i2sClose(void)
{
    // reset some variables
    i2sOpened = 0;
    g_fnPlayCallBack = NULL;
    g_fnRecCallBack = NULL;

    // reset i2s interface
    outpw(REG_SYS_AHBIPRST, inpw(REG_SYS_AHBIPRST) | (1 << 8));
    outpw(REG_SYS_AHBIPRST, inpw(REG_SYS_AHBIPRST) & ~(1 << 8));
}

/**
  * @brief Initialize i2s interface and setup interrupt
  * @return None
  */
void i2sInit(void)
{
    // enable i2s engine clock
    outpw(REG_CLK_HCLKEN, inpw(REG_CLK_HCLKEN) | (1 << 24));

    // enable interrupt and set ISR
    sysSetInterruptType(ACTL_IRQn, HIGH_LEVEL_SENSITIVE);
    sysInstallISR(IRQ_LEVEL_1, ACTL_IRQn, (PVOID)i2sISR);
    sysEnableInterrupt(ACTL_IRQn);
    sysSetLocalInterrupt(ENABLE_IRQ);
}

/**
  * @brief IO control for i2s interface
  * @param[in] cmd Command for io control, value could be
  *                                     - \ref I2S_SET_PLAY
  *                                     - \ref I2S_SET_RECORD
  *                                     - \ref I2S_SELECT_BLOCK
  *                                     - \ref I2S_SELECT_BIT
  *                                     - \ref I2S_SET_PLAY_DMA_INT_SEL
  *                                     - \ref I2S_SET_REC_DMA_INT_SEL
  *                                     - \ref I2S_SET_ZEROCROSS
  *                                     - \ref I2S_SET_DMACOUNTER
  *                                     - \ref I2S_SET_CHANNEL
  *                                     - \ref I2S_SET_MODE
  *                                     - \ref I2S_SET_SPLITDATA
  *                                     - \ref I2S_SET_DMA_ADDRESS
  *                                     - \ref I2S_SET_DMA_LENGTH
  *                                     - \ref I2S_GET_DMA_CUR_ADDRESS
  *                                     - \ref I2S_SET_I2S_FORMAT
  *                                     - \ref I2S_SET_I2S_CALLBACKFUN
  *                                     - \ref I2S_SET_PCMSLOT
  * @param[in] arg0 argument 0 for io control
  * @param[in] arg1 argument 1 for io control
  * @retval I2S_ERR_IO Command error.
  * @retval 0 success.
  */
int32_t i2sIoctl(uint32_t cmd, uint32_t arg0, uint32_t arg1)
{
    uint32_t *buf;
    AU_CB_FUNC_T *ptr;

    switch (cmd)
    {
    // #define I2S_START_PLAY  0
    // #define I2S_STOP_PLAY   1
    case I2S_SET_PLAY:
        if (arg0 == I2S_START_PLAY)
            i2sStartPlay();
        else
            i2sStopPlay();
        break;
    // #define I2S_START_REC  0
    // #define I2S_STOP_REC   1
    case I2S_SET_RECORD:
        if (arg0 == I2S_START_REC)
            i2sStartRecord();
        else
            i2sStopRecord();
        break;
    // #define I2S_BLOCK_I2S  0
    // #define I2S_BLOCK_PCM   1
    case I2S_SELECT_BLOCK:
        if (arg0 == I2S_BLOCK_I2S)
            outpw(REG_ACTL_CON, (inpw(REG_ACTL_CON) & ~0x3) | 0x1);
        else
            outpw(REG_ACTL_CON, (inpw(REG_ACTL_CON) & ~0x3) | 0x2);
        break;
    // #define I2S_BIT_WIDTH_8  0
    // #define I2S_BIT_WIDTH_16 1
    // #define I2S_BIT_WIDTH_24 2
    case I2S_SELECT_BIT:
        outpw(REG_ACTL_CON, (inpw(REG_ACTL_CON) & ~0x300) | (arg0 << 8));
        break;
    // #define I2S_DMA_INT_END         0
    // #define I2S_DMA_INT_HALF        1
    // #define I2S_DMA_INT_QUARTER     2
    // #define I2S_DMA_INT_EIGTH       3
    case I2S_SET_PLAY_DMA_INT_SEL:
        outpw(REG_ACTL_CON, (inpw(REG_ACTL_CON) & ~0x3000) | (arg0 << 12));
        break;

    case I2S_SET_REC_DMA_INT_SEL:
        outpw(REG_ACTL_CON, (inpw(REG_ACTL_CON) & ~0xc000) | (arg0 << 14));
        break;

    case I2S_SET_ZEROCROSS:
        if (arg0 == I2S_ENABLE)
            outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) | 0x8);
        else
            outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) & ~0x8);
        break;

    case I2S_SET_DMACOUNTER:
        if (arg0 == I2S_ENABLE)
            outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) | 0x10);
        else
            outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) & ~0x10);
        break;
    // #define I2S_CHANNEL_I2S_ONE         2
    // #define I2S_CHANNEL_I2S_TWO         3
    // #define I2S_CHANNEL_PCM_TWO         3
    // #define I2S_CHANNEL_PCM_TWO_SLOT1   0
    // #define I2S_CHANNEL_PCM_TWO_SLOT0   1
    // #define I2S_CHANNEL_PCM_ONE_SLOT0   2
    case I2S_SET_CHANNEL:
        if (arg0 == I2S_PLAY)
            outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) & ~(0x3 << 12) | (arg1 << 12));
        else
            outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) & ~(0x3 << 14) | (arg1 << 14));
        break;
    // #define I2S_MODE_MASTER  0
    // #define I2S_MODE_SLAVE  1
    case I2S_SET_MODE:
        if (arg0 == I2S_MODE_MASTER)
            outpw(REG_ACTL_I2SCON, inpw(REG_ACTL_I2SCON) & ~(0x1 << 20));
        else
            outpw(REG_ACTL_I2SCON, inpw(REG_ACTL_I2SCON) | (0x1 << 20));
        break;

    case I2S_SET_SPLITDATA:
        if (arg0 == I2S_ENABLE)
            outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) | (0x1 << 20));
        else
            outpw(REG_ACTL_RESET, inpw(REG_ACTL_RESET) & ~(0x1 << 20));
        break;

    case I2S_SET_DMA_ADDRESS:
        if (arg0 == I2S_PLAY)
            outpw(REG_ACTL_PDESB, arg1 | 0x80000000);
        else if (arg0 == I2S_REC)
            outpw(REG_ACTL_RDESB, arg1 | 0x80000000);
        else if (arg0 == PCM_PLAY)
            outpw(REG_ACTL_PDESB2, arg1 | 0x80000000);
        else
            outpw(REG_ACTL_RDESB2, arg1 | 0x80000000);
        break;

    case I2S_SET_DMA_LENGTH:
        if (arg0 == I2S_PLAY)
            outpw(REG_ACTL_PDES_LENGTH, arg1);
        else
            outpw(REG_ACTL_RDES_LENGTH, arg1);
        break;

    case I2S_GET_DMA_CUR_ADDRESS:
        buf = (uint32_t *)arg0;
        if (arg0 == I2S_PLAY)
            *buf = inpw(REG_ACTL_PDESC);
        else
            *buf = inpw(REG_ACTL_RDESC);
        break;

    // #define I2S_FORMAT_I2S  0
    // #define I2S_FORMAT_MSB  1
    case I2S_SET_I2S_FORMAT:
        if (arg0 == I2S_FORMAT_I2S)
            outpw(REG_ACTL_I2SCON, inpw(REG_ACTL_I2SCON) & ~ 0x8);
        else
            outpw(REG_ACTL_I2SCON, inpw(REG_ACTL_I2SCON) | 0x8);
        break;

    case I2S_SET_I2S_CALLBACKFUN:
        ptr = (AU_CB_FUNC_T *)arg1;
        if (arg0 == I2S_PLAY)
            g_fnPlayCallBack = ptr;
        else
            g_fnRecCallBack = ptr;
        break;
    // #define PCM_SLOT1_IN        0
    // #define PCM_SLOT1_OUT       1
    // #define PCM_SLOT2_IN        2
    // #define PCM_SLOT2_OUT       3
    case I2S_SET_PCMSLOT:
        if (arg0 == PCM_SLOT1_IN)
            outpw(REG_ACTL_PCMS1ST, (inpw(REG_ACTL_PCMS1ST) & ~0x3ff) | (arg1 & 0x3ff));
        else if (arg0 == PCM_SLOT1_OUT)
            outpw(REG_ACTL_PCMS1ST, (inpw(REG_ACTL_PCMS1ST) & ~0x3ff0000) | ((arg1 & 0x3ff) << 16));
        else if (arg0 == PCM_SLOT2_IN)
            outpw(REG_ACTL_PCMS2ST, (inpw(REG_ACTL_PCMS2ST) & ~0x3ff) | (arg1 & 0x3ff));
        else
            outpw(REG_ACTL_PCMS2ST, (inpw(REG_ACTL_PCMS2ST) & ~0x3ff0000) | ((arg1 & 0x3ff) << 16));
        break;

    case I2S_SET_PCM_FS_PERIOD:
        outpw(REG_ACTL_PCMCON, (inpw(REG_ACTL_PCMCON) & ~0x03FF0000 | (((arg0 - 1) & 0x3ff) << 16)));
        break;

    default:
        return I2S_ERR_IO;
    }
    return 0;
}

/**
  * @brief Configure sampling rate for audio
  * @param[in] u32SourceClockRate source speed to i2s interface
  * @param[in] u32SampleRate sampling rate
  * @param[in] u32DataBit data width
  * @param[in] u32Channel channel number
  * @return None
  */
void i2sSetSampleRate(uint32_t u32SourceClockRate, uint32_t u32SampleRate, uint32_t u32DataBit, uint32_t u32Channel)
{
    uint32_t u32BCLKDiv;
    uint32_t u32MCLK, u32MCLKDiv;

    u32MCLK = (u32SampleRate * 256);
    u32MCLKDiv = u32SourceClockRate / u32MCLK;
    outpw(REG_ACTL_I2SCON, (inpw(REG_ACTL_I2SCON) & ~0x000F0000) | (u32MCLKDiv - 1) << 16);

    u32BCLKDiv = u32MCLK / (u32SampleRate * u32DataBit * u32Channel);
    u32BCLKDiv = u32BCLKDiv / 2 - 1;
    outpw(REG_ACTL_I2SCON, (inpw(REG_ACTL_I2SCON) & ~0xF0) | u32BCLKDiv << 5);
}

/**
  * @brief Configure MCLK frequency (master mode)
  * @param[in] u32SourceClockRate source clock rate
  * @param[in] u32SampleRate sampling rate
  * @return None
  */
void i2sSetMCLKFrequency(uint32_t u32SourceClockRate, uint32_t u32SampleRate)
{
    uint32_t u32MCLK, u32MCLKDiv;

    u32MCLK = (u32SampleRate * 256);
    u32MCLKDiv = u32SourceClockRate / u32MCLK;
    outpw(REG_ACTL_I2SCON, (inpw(REG_ACTL_I2SCON) & ~0x000F0000) | (u32MCLKDiv - 1) << 16);
}

/**
  * @brief Configure PCM BCLK frequency (master mode)
  * @param[in] u32SourceClockRate source clock rate
  * @param[in] u32Rate target rate
  * @return None
  */
void i2sSetPCMBCLKFrequency(uint32_t u32SourceClockRate, uint32_t u32Rate)
{
    uint32_t u32BCLKDiv;

    u32BCLKDiv = (u32SourceClockRate / (2 * u32Rate)) - 1;
    outpw(REG_ACTL_PCMCON, (inpw(REG_ACTL_PCMCON) & ~0x0000FF00) | (u32BCLKDiv << 8));
}


/*@}*/ /* end of group N9H30_I2S_EXPORTED_FUNCTIONS */

/*@}*/ /* end of group N9H30_I2S_Driver */

/*@}*/ /* end of group N9H30_Device_Driver */

/*** (C) COPYRIGHT 2018 Nuvoton Technology Corp. ***/