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