1 /******************************************************************************
2 *  Filename:       i2s.c
3 *
4 *  Description:    Driver for the I2S.
5 *
6 *  Copyright (c) 2015 - 2022, Texas Instruments Incorporated
7 *  All rights reserved.
8 *
9 *  Redistribution and use in source and binary forms, with or without
10 *  modification, are permitted provided that the following conditions are met:
11 *
12 *  1) Redistributions of source code must retain the above copyright notice,
13 *     this list of conditions and the following disclaimer.
14 *
15 *  2) Redistributions in binary form must reproduce the above copyright notice,
16 *     this list of conditions and the following disclaimer in the documentation
17 *     and/or other materials provided with the distribution.
18 *
19 *  3) Neither the name of the ORGANIZATION nor the names of its contributors may
20 *     be used to endorse or promote products derived from this software without
21 *     specific prior written permission.
22 *
23 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 *  POSSIBILITY OF SUCH DAMAGE.
34 *
35 ******************************************************************************/
36 
37 #include "i2s.h"
38 
39 //*****************************************************************************
40 //
41 // Handle support for DriverLib in ROM:
42 // This section will undo prototype renaming made in the header file
43 //
44 //*****************************************************************************
45 #if !defined(DOXYGEN)
46     #undef  I2SEnable
47     #define I2SEnable                       NOROM_I2SEnable
48     #undef  I2SAudioFormatConfigure
49     #define I2SAudioFormatConfigure         NOROM_I2SAudioFormatConfigure
50     #undef  I2SChannelConfigure
51     #define I2SChannelConfigure             NOROM_I2SChannelConfigure
52     #undef  I2SBufferConfig
53     #define I2SBufferConfig                 NOROM_I2SBufferConfig
54     #undef  I2SPointerUpdate
55     #define I2SPointerUpdate                NOROM_I2SPointerUpdate
56     #undef  I2SPointerSet
57     #define I2SPointerSet                   NOROM_I2SPointerSet
58     #undef  I2SSampleStampConfigure
59     #define I2SSampleStampConfigure         NOROM_I2SSampleStampConfigure
60     #undef  I2SSampleStampGet
61     #define I2SSampleStampGet               NOROM_I2SSampleStampGet
62 #endif
63 
64 //*****************************************************************************
65 //
66 // Global pointer to the current I2S data structure
67 //
68 //*****************************************************************************
69 I2SControlTable *g_pControlTable;
70 
71 //*****************************************************************************
72 //
73 // Enables the I2S module for operation
74 //
75 //*****************************************************************************
76 void
I2SEnable(uint32_t ui32Base)77 I2SEnable(uint32_t ui32Base)
78 {
79     // Check the arguments.
80     ASSERT(I2SBaseValid(ui32Base));
81 
82     // Make sure the control table pointer is setup to a memory location.
83     if(!(g_pControlTable))
84     {
85         return;
86     }
87 
88     // Write the address to the first input/output buffer.
89     HWREG(I2S0_BASE + I2S_O_AIFINPTRNEXT) = g_pControlTable->ui32InBase;
90     g_pControlTable->ui32InOffset = 0;
91     HWREG(I2S0_BASE + I2S_O_AIFOUTPTRNEXT) = g_pControlTable->ui32OutBase;
92     g_pControlTable->ui32OutOffset = 0;
93 
94     // Enable the I2S module.
95     HWREG(I2S0_BASE + I2S_O_AIFDMACFG) = (uint32_t)g_pControlTable->ui16DMABufSize - 1;
96 }
97 
98 //*****************************************************************************
99 //
100 // Configures the I2S module
101 //
102 //*****************************************************************************
103 void
I2SAudioFormatConfigure(uint32_t ui32Base,uint32_t ui32FmtCfg,uint32_t ui32BitClkDelay)104 I2SAudioFormatConfigure(uint32_t ui32Base, uint32_t ui32FmtCfg,
105                         uint32_t ui32BitClkDelay)
106 {
107     // Check the arguments.
108     ASSERT(I2SBaseValid(ui32Base));
109     ASSERT(ui32BitClkDelay <= 255);
110 
111     // Save the length of the audio words stored in memory.
112     g_pControlTable->ui16MemLen = (ui32FmtCfg & I2S_MEM_LENGTH_24) ? 24 : 16;
113 
114     // Write the configuration.
115     HWREG(I2S0_BASE + I2S_O_AIFFMTCFG) = ui32FmtCfg | (ui32BitClkDelay << I2S_AIFFMTCFG_DATA_DELAY_S);
116 }
117 
118 //****************************************************************************
119 //
120 // Setup the audio channel configuration
121 //
122 //****************************************************************************
123 void
I2SChannelConfigure(uint32_t ui32Base,uint32_t ui32Chan0Cfg,uint32_t ui32Chan1Cfg)124 I2SChannelConfigure(uint32_t ui32Base, uint32_t ui32Chan0Cfg,
125                     uint32_t ui32Chan1Cfg)
126 {
127     uint32_t ui32InChan;
128     uint32_t ui32OutChan;
129     uint32_t ui32ChanMask;
130 
131     // Check the arguments.
132     ASSERT(I2SBaseValid(ui32Base));
133     ASSERT(ui32Chan0Cfg & (I2S_CHAN_CFG_MASK | I2S_LINE_MASK))
134     ASSERT(ui32Chan1Cfg & (I2S_CHAN_CFG_MASK | I2S_LINE_MASK))
135 
136     ui32InChan = 0;
137     ui32OutChan = 0;
138 
139     // Configure input/output channels.
140     HWREG(I2S0_BASE + I2S_O_AIFDIRCFG) = (
141         (( ui32Chan0Cfg << I2S_AIFDIRCFG_AD0_S) & I2S_AIFDIRCFG_AD0_M ) |
142         (( ui32Chan1Cfg << I2S_AIFDIRCFG_AD1_S) & I2S_AIFDIRCFG_AD1_M )   );
143 
144     // Configure the valid channel mask.
145     HWREG(I2S0_BASE + I2S_O_AIFWMASK0) = (ui32Chan0Cfg >> 8) & I2S_AIFWMASK0_MASK_M;
146     HWREG(I2S0_BASE + I2S_O_AIFWMASK1) = (ui32Chan1Cfg >> 8) & I2S_AIFWMASK1_MASK_M;
147 
148     // Resolve and save the number of input and output channels.
149     ui32ChanMask = (ui32Chan0Cfg & I2S_CHAN_CFG_MASK) >> 8;
150     if(ui32Chan0Cfg & I2S_LINE_INPUT)
151     {
152         while(ui32ChanMask)
153         {
154             if(ui32ChanMask & 0x1)
155             {
156                 ui32InChan++;
157             }
158             // Shift down channel mask
159             ui32ChanMask >>= 1;
160         }
161 
162     }
163     else if(ui32Chan0Cfg & I2S_LINE_OUTPUT)
164     {
165         while(ui32ChanMask)
166         {
167             if(ui32ChanMask & 0x1)
168             {
169                 ui32OutChan++;
170             }
171             // Shift down channel mask
172             ui32ChanMask >>= 1;
173         }
174     }
175 
176     ui32ChanMask = (ui32Chan1Cfg & I2S_CHAN_CFG_MASK) >> 8;
177     if(ui32Chan1Cfg & I2S_LINE_INPUT)
178     {
179         while(ui32ChanMask)
180         {
181             if(ui32ChanMask & 0x1)
182             {
183                 ui32InChan++;
184             }
185             // Shift down channel mask
186             ui32ChanMask >>= 1;
187         }
188     }
189     else if(ui32Chan1Cfg & I2S_LINE_OUTPUT)
190     {
191         while(ui32ChanMask)
192         {
193             if(ui32ChanMask & 0x1)
194             {
195                 ui32OutChan++;
196             }
197             // Shift down channel mask
198             ui32ChanMask >>= 1;
199         }
200     }
201 
202     g_pControlTable->ui8InChan = (uint8_t)ui32InChan;
203     g_pControlTable->ui8OutChan = (uint8_t)ui32OutChan;
204 }
205 
206 //****************************************************************************
207 //
208 // Set the input buffer pointers
209 //
210 //****************************************************************************
211 void
I2SBufferConfig(uint32_t ui32Base,uint32_t ui32InBufBase,uint32_t ui32OutBufBase,uint16_t ui16DMABufSize,uint16_t ui16ChanBufSize)212 I2SBufferConfig(uint32_t ui32Base, uint32_t ui32InBufBase,
213                 uint32_t ui32OutBufBase, uint16_t ui16DMABufSize,
214                 uint16_t ui16ChanBufSize)
215 {
216     // Check the arguments.
217     ASSERT(I2SBaseValid(ui32Base));
218     ASSERT(ui16DMABufSize > 0);
219 
220     // Setup the input data pointer and buffer sizes.
221     g_pControlTable->ui16DMABufSize = ui16DMABufSize;
222     g_pControlTable->ui16ChBufSize = ui16ChanBufSize;
223     g_pControlTable->ui32InBase = ui32InBufBase;
224     g_pControlTable->ui32OutBase = ui32OutBufBase;
225 }
226 
227 //****************************************************************************
228 //
229 // Set the buffer pointers
230 //
231 //****************************************************************************
232 void
I2SPointerSet(uint32_t ui32Base,bool bInput,void * pNextPointer)233 I2SPointerSet(uint32_t ui32Base, bool bInput, void * pNextPointer)
234 {
235     // Check the arguments.
236     ASSERT(I2SBaseValid(ui32Base));
237 
238     // Update the next input/output pointer with the correct address.
239     if(bInput == true)
240     {
241         HWREG(I2S0_BASE + I2S_O_AIFINPTRNEXT) = (uint32_t)pNextPointer;
242     }
243     else
244     {
245         HWREG(I2S0_BASE + I2S_O_AIFOUTPTRNEXT) = (uint32_t)pNextPointer;
246     }
247 }
248 
249 //****************************************************************************
250 //
251 // Update the buffer pointers
252 //
253 //****************************************************************************
254 void
I2SPointerUpdate(uint32_t ui32Base,bool bInput)255 I2SPointerUpdate(uint32_t ui32Base, bool bInput)
256 {
257     uint32_t ui32NextPtr;
258 
259     // Check the arguments.
260     ASSERT(I2SBaseValid(ui32Base));
261 
262     // Update the next input/output pointer with the correct address.
263     if(bInput == true)
264     {
265         ui32NextPtr = (g_pControlTable->ui8InChan *
266                        (g_pControlTable->ui16MemLen >> 3)) *
267                       g_pControlTable->ui16DMABufSize;
268         g_pControlTable->ui32InOffset = ((g_pControlTable->ui32InOffset +
269                                          ui32NextPtr) %
270                                          g_pControlTable->ui16ChBufSize);
271         HWREG(I2S0_BASE + I2S_O_AIFINPTRNEXT) = g_pControlTable->ui32InOffset +
272                                                g_pControlTable->ui32InBase;
273     }
274     else
275     {
276         ui32NextPtr = (g_pControlTable->ui8OutChan *
277                        (g_pControlTable->ui16MemLen >> 3)) *
278                       g_pControlTable->ui16DMABufSize;
279         g_pControlTable->ui32OutOffset = ((g_pControlTable->ui32OutOffset +
280                                          ui32NextPtr) %
281                                          g_pControlTable->ui16ChBufSize);
282         HWREG(I2S0_BASE + I2S_O_AIFOUTPTRNEXT) =
283                          g_pControlTable->ui32OutOffset +
284                          g_pControlTable->ui32OutBase;
285     }
286 }
287 
288 //*****************************************************************************
289 //
290 // Configure the sample stamp generator
291 //
292 //*****************************************************************************
293 void
I2SSampleStampConfigure(uint32_t ui32Base,bool bInput,bool bOutput)294 I2SSampleStampConfigure(uint32_t ui32Base, bool bInput, bool bOutput)
295 {
296     uint32_t ui32Trigger;
297 
298     // Check the arguments.
299     ASSERT(I2SBaseValid(ui32Base));
300 
301     ui32Trigger = HWREG(I2S0_BASE + I2S_O_STMPWCNT);
302     ui32Trigger = (ui32Trigger + 2) % g_pControlTable->ui16ChBufSize;
303 
304     // Setup the sample stamp trigger for input streams.
305     if(bInput)
306     {
307         HWREG(I2S0_BASE + I2S_O_STMPINTRIG) = ui32Trigger;
308     }
309 
310     // Setup the sample stamp trigger for output streams.
311     if(bOutput)
312     {
313         HWREG(I2S0_BASE + I2S_O_STMPOUTTRIG) = ui32Trigger;
314     }
315 
316 }
317 
318 //*****************************************************************************
319 //
320 // Get the current value of a sample stamp counter
321 //
322 //*****************************************************************************
323 uint32_t
I2SSampleStampGet(uint32_t ui32Base,uint32_t ui32Channel)324 I2SSampleStampGet(uint32_t ui32Base, uint32_t ui32Channel)
325 {
326     uint32_t ui32FrameClkCnt;
327     uint32_t ui32SysClkCnt;
328     uint32_t ui32PeriodSysClkCnt;
329     uint32_t ui32SampleStamp;
330 
331     // Get the number of Frame clock counts since last stamp.
332     ui32FrameClkCnt = HWREG(I2S0_BASE + I2S_O_STMPWCNTCAPT0);
333 
334     // Get the number of system clock ticks since last frame clock edge.
335     ui32SysClkCnt = HWREG(I2S0_BASE + I2S_O_STMPXCNTCAPT0);
336 
337     // Get the number system clock ticks in the last frame clock period.
338     ui32PeriodSysClkCnt = HWREG(I2S0_BASE + I2S_O_STMPXPER);
339 
340     // Calculate the sample stamp.
341     ui32SampleStamp = (ui32SysClkCnt << 16) / ui32PeriodSysClkCnt;
342     ui32SampleStamp = (ui32SampleStamp > I2S_STMP_SATURATION) ?
343                       I2S_STMP_SATURATION : ui32SampleStamp;
344     ui32SampleStamp |= (ui32FrameClkCnt << 16);
345 
346     return (ui32SampleStamp);
347 }
348