1 /**
2  * @file    i2s.c
3  * @brief   Inter-Integrated Sound (I2S) driver implementation.
4  */
5 
6 /* ****************************************************************************
7  * Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved.
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included
17  * in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
23  * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  * OTHER DEALINGS IN THE SOFTWARE.
26  *
27  * Except as contained in this notice, the name of Maxim Integrated
28  * Products, Inc. shall not be used except as stated in the Maxim Integrated
29  * Products, Inc. Branding Policy.
30  *
31  * The mere transfer of this software does not imply any licenses
32  * of trade secrets, proprietary technology, copyrights, patents,
33  * trademarks, maskwork rights, or any other form of intellectual
34  * property whatsoever. Maxim Integrated Products, Inc. retains all
35  * ownership rights.
36  *
37  * $Date: 2018-12-18 15:37:22 -0600 (Tue, 18 Dec 2018) $
38  * $Revision: 40072 $
39  *
40  *************************************************************************** */
41 
42 #include <stddef.h>
43 #include <stdint.h>
44 #include "mxc_config.h"
45 #include "mxc_assert.h"
46 #include "mxc_lock.h"
47 #include "mxc_sys.h"
48 #include "dma.h"
49 #include "i2s.h"
50 
51 #define I2S_CHANNELS 2
52 #define I2S_WIDTH    16
53 
54 int dma_channel = -1;
55 
I2S_Init(const i2s_cfg_t * cfg,void (* dma_ctz_cb)(int,int),const sys_cfg_i2s_t * sys_cfg_i2s)56 int I2S_Init(const i2s_cfg_t *cfg, void (*dma_ctz_cb)(int, int), const sys_cfg_i2s_t* sys_cfg_i2s)
57 {
58     unsigned int i2s_clk, baud;
59     uint16_t clocks;
60     uint8_t ctz_en;
61     int err;
62 
63     SYS_I2S_Init(sys_cfg_i2s);
64 
65     /* Setup SPI_MSS as master, mode 0, 16 bit transfers as I2S Requires */
66     MXC_SPIMSS->ctrl = MXC_F_SPIMSS_CTRL_MMEN;
67     MXC_SPIMSS->mod = MXC_V_SPIMSS_MOD_NUMBITS_BITS16 | MXC_F_SPIMSS_MOD_SSIO;
68     MXC_SPIMSS->dma = MXC_S_SPIMSS_DMA_TX_FIFO_LEVEL_ENTRIES8;
69 
70     /* Setup I2S register from i2s_cfg_t */
71     MXC_SPIMSS->i2s_ctrl =  cfg->left_justify << MXC_F_SPIMSS_I2S_CTRL_I2S_LJ_POS |
72                             cfg->mono_audio << MXC_F_SPIMSS_I2S_CTRL_I2S_MONO_POS;
73 
74     /* Determine divisor for baud rate generator */
75     baud = cfg->sample_rate*I2S_CHANNELS*I2S_WIDTH;
76 
77     i2s_clk = SYS_I2S_GetFreq(MXC_SPIMSS);
78 
79     if (i2s_clk/4 < baud) {
80         return E_BAD_PARAM;
81     }
82 
83     clocks = i2s_clk / (2*baud);
84     MXC_SPIMSS->brg = clocks;
85 
86     /* Prepare SPIMSS DMA register for DMA setup */
87     if (dma_ctz_cb == NULL) {
88         ctz_en = 0;
89     } else {
90         ctz_en = 1;
91     }
92 
93     /* Initialize DMA */
94     if (cfg->audio_direction % 2) {
95         MXC_SPIMSS->dma |= MXC_F_SPIMSS_DMA_TX_DMA_EN | MXC_F_SPIMSS_DMA_TX_FIFO_CLEAR;
96         if ((err = DMA_Init()) != E_NO_ERROR) {
97             if (err != E_BAD_STATE) {
98                 return err;
99             }
100         }
101 
102         if ((err = DMA_AcquireChannel()) < 0) {
103             return err;
104         }
105 
106         dma_channel = err;
107 
108         DMA_ConfigChannel(dma_channel, DMA_PRIO_MEDHIGH,
109               sys_cfg_i2s->dma_reqsel_tx, 1, DMA_TIMEOUT_512_CLK,
110               DMA_PRESCALE_DIV64K, DMA_WIDTH_HALFWORD, 1,
111               DMA_WIDTH_HALFWORD, 0, 16, 0, ctz_en);
112 
113         if (ctz_en) {
114             DMA_SetCallback(dma_channel, dma_ctz_cb);
115             DMA_EnableInterrupt(dma_channel);
116         }
117     }
118     if (cfg->audio_direction / 2) {
119         MXC_SPIMSS->dma = MXC_F_SPIMSS_DMA_RX_DMA_EN | MXC_F_SPIMSS_DMA_RX_FIFO_CLEAR;
120         if ((err = DMA_Init()) != E_NO_ERROR) {
121             if (err != E_BAD_STATE) {    //DMA already initialized
122                 return err;
123             }
124         }
125 
126         if ((err = DMA_AcquireChannel()) < 0) {
127             return err;
128         }
129 
130         dma_channel = err;
131 
132         DMA_ConfigChannel(dma_channel, DMA_PRIO_MEDHIGH,
133                         sys_cfg_i2s->dma_reqsel_rx, 1, DMA_TIMEOUT_512_CLK,
134                         DMA_PRESCALE_DIV64K, DMA_WIDTH_HALFWORD, 0,
135                         DMA_WIDTH_HALFWORD, 1, 8, 0, ctz_en);
136 
137         if (ctz_en) {
138             DMA_SetCallback(dma_channel, dma_ctz_cb);
139             DMA_EnableInterrupt(dma_channel);
140         }
141     }
142 
143     I2S_DMA_SetAddrCnt(cfg->dma_src_addr, cfg->dma_dst_addr, cfg->dma_cnt);
144     if (cfg->dma_reload_en) {
145         I2S_DMA_SetReload(cfg->dma_src_addr, cfg->dma_dst_addr, cfg->dma_cnt);
146     }
147 
148     if (cfg->start_immediately) {
149         return I2S_Start();
150     }
151     return E_NO_ERROR;
152 }
153 
I2S_Shutdown(void)154 int I2S_Shutdown(void)
155 {
156     MXC_SPIMSS->ctrl = 0;
157     MXC_SPIMSS->i2s_ctrl = 0;
158     MXC_SPIMSS->brg = 0;
159     MXC_SPIMSS->mod = 0;
160     MXC_SPIMSS->dma = 0;
161     SYS_I2S_Shutdown();
162     return DMA_ReleaseChannel(dma_channel);
163 }
164 
I2S_Mute(void)165 int I2S_Mute(void)
166 {
167     MXC_SPIMSS->i2s_ctrl |= MXC_F_SPIMSS_I2S_CTRL_I2S_MUTE;
168     return E_NO_ERROR;
169 }
170 
I2S_Unmute(void)171 int I2S_Unmute(void)
172 {
173     MXC_SPIMSS->i2s_ctrl &= ~MXC_F_SPIMSS_I2S_CTRL_I2S_MUTE;
174     return E_NO_ERROR;
175 }
176 
I2S_Pause(void)177 int I2S_Pause(void)
178 {
179     MXC_SPIMSS->i2s_ctrl |= MXC_F_SPIMSS_I2S_CTRL_I2S_PAUSE;
180     return E_NO_ERROR;
181 }
182 
I2S_Unpause(void)183 int I2S_Unpause(void)
184 {
185     MXC_SPIMSS->i2s_ctrl &= ~MXC_F_SPIMSS_I2S_CTRL_I2S_PAUSE;
186     return E_NO_ERROR;
187 }
188 
I2S_Stop(void)189 int I2S_Stop(void)
190 {
191     MXC_SPIMSS->ctrl &= ~MXC_F_SPIMSS_CTRL_SPIEN;
192     MXC_SPIMSS->i2s_ctrl &= ~MXC_F_SPIMSS_I2S_CTRL_I2S_EN;
193     return DMA_Stop(dma_channel);
194 }
195 
I2S_Start(void)196 int I2S_Start(void)
197 {
198     MXC_SPIMSS->ctrl |= MXC_F_SPIMSS_CTRL_SPIEN;
199     MXC_SPIMSS->i2s_ctrl |= MXC_F_SPIMSS_I2S_CTRL_I2S_EN;
200     return DMA_Start(dma_channel);
201 }
202 
I2S_DMA_ClearFlags(void)203 int I2S_DMA_ClearFlags(void)
204 {
205     return DMA_ClearFlags(dma_channel);
206 }
207 
I2S_DMA_SetAddrCnt(void * src_addr,void * dst_addr,unsigned int count)208 int I2S_DMA_SetAddrCnt(void *src_addr, void *dst_addr, unsigned int count)
209 {
210     return DMA_SetSrcDstCnt(dma_channel, src_addr, dst_addr, count);
211 }
212 
I2S_DMA_SetReload(void * src_addr,void * dst_addr,unsigned int count)213 int I2S_DMA_SetReload(void *src_addr, void *dst_addr, unsigned int count)
214 {
215     return DMA_SetReload(dma_channel, src_addr, dst_addr, count);
216 }
217