Add i2s implementation for the sam3x.
[bertos.git] / bertos / cpu / cortex-m3 / drv / i2s_sam3.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2011 Develer S.r.l. (http://www.develer.com/)
30  * -->
31  *
32  * \brief I2S driver implementation.
33  *
34  * \author Daniele Basile <asterix@develer.com>
35  */
36
37
38 #include "cfg/cfg_i2s.h"
39
40 // Define log settings for cfg/log.h.
41 #define LOG_LEVEL         I2S_LOG_LEVEL
42 #define LOG_FORMAT        I2S_LOG_FORMAT
43 #include <cfg/log.h>
44
45 #include <drv/timer.h>
46 #include <drv/i2s.h>
47 #include <cpu/irq.h>
48
49 #include <io/cm3.h>
50
51 struct I2sHardware
52 {
53 };
54
55 struct I2sHardware i2s_hw;
56
57
58 /* We divite for 2 because the min clock for i2s i MCLK/2 */
59 #define MCK_DIV     (CPU_FREQ / (48000 * CONFIG_WORD_BIT_SIZE * CONFIG_CHANNEL_NUM * 2))
60 #define DATALEN     ((CONFIG_WORD_BIT_SIZE - 1) & SSC_DATLEN_MASK)
61 #define DELAY       ((CONFIG_DELAY << SSC_STTDLY_SHIFT) & SSC_STTDLY_MASK)
62 #define PERIOD      ((CONFIG_PERIOD << (SSC_PERIOD_SHIFT)) & SSC_PERIOD_MASK)
63 #define DATNB       ((CONFIG_WORD_PER_FRAME << SSC_DATNB_SHIFT) & SSC_DATNB_MASK)
64 #define FSLEN       ((CONFIG_FRAME_SYNC_SIZE << SSC_FSLEN_SHIFT) & SSC_FSLEN_MASK)
65 #define EXTRA_FSLEN (CONFIG_EXTRA_FRAME_SYNC_SIZE << SSC_FSLEN_EXT)
66
67
68 static void sam3_i2s_txStop(I2s *i2s)
69 {
70         (void)i2s;
71         SSC_CR = BV(SSC_TXDIS);
72 }
73
74 static void sam3_i2s_txWait(I2s *i2s)
75 {
76         (void)i2s;
77 }
78
79 static void sam3_i2s_txStart(I2s *i2s, void *buf, size_t len, size_t slice_len)
80 {
81         (void)i2s;
82         (void)buf;
83         (void)len;
84         (void)slice_len;
85 }
86
87 static void sam3_i2s_rxStop(I2s *i2s)
88 {
89         (void)i2s;
90         SSC_CR = BV(SSC_TXDIS);
91 }
92
93 static void sam3_i2s_rxWait(I2s *i2s)
94 {
95         (void)i2s;
96 }
97
98 static void sam3_i2s_rxStart(I2s *i2s, void *buf, size_t len, size_t slice_len)
99 {
100         (void)i2s;
101         (void)buf;
102         (void)len;
103         (void)slice_len;
104 }
105
106
107 static bool sam3_i2s_isTxFinish(struct I2s *i2s)
108 {
109         (void)i2s;
110         return false;
111 }
112
113 static bool sam3_i2s_isRxFinish(struct I2s *i2s)
114 {
115         (void)i2s;
116         return false;
117 }
118
119 static void sam3_i2s_txBuf(struct I2s *i2s, void *buf, size_t len)
120 {
121         (void)i2s;
122         (void)buf;
123         (void)len;
124 }
125
126 static void sam3_i2s_rxBuf(struct I2s *i2s, void *buf, size_t len)
127 {
128         (void)i2s;
129         (void)buf;
130         (void)len;
131 }
132
133 static int sam3_i2s_write(struct I2s *i2s, uint32_t sample)
134 {
135         (void)i2s;
136         while(!(SSC_SR & BV(SSC_TXRDY)));
137         SSC_THR = sample;
138         return 0;
139 }
140
141
142
143 static uint32_t sam3_i2s_read(struct I2s *i2s)
144 {
145         (void)i2s;
146         while(!(SSC_SR & BV(SSC_RXRDY)));
147         return SSC_RHR;
148 }
149
150 /*
151 static DECLARE_ISR(irq_ssc)
152 {
153 }
154 */
155 void i2s_init(I2s *i2s, int channel)
156 {
157         (void)channel;
158         i2s->ctx.write = sam3_i2s_write;
159         i2s->ctx.tx_buf = sam3_i2s_txBuf;
160         i2s->ctx.tx_isFinish = sam3_i2s_isTxFinish;
161         i2s->ctx.tx_start = sam3_i2s_txStart;
162         i2s->ctx.tx_wait = sam3_i2s_txWait;
163         i2s->ctx.tx_stop = sam3_i2s_txStop;
164
165         i2s->ctx.read = sam3_i2s_read;
166         i2s->ctx.rx_buf = sam3_i2s_rxBuf;
167         i2s->ctx.rx_isFinish = sam3_i2s_isRxFinish;
168         i2s->ctx.rx_start = sam3_i2s_rxStart;
169         i2s->ctx.rx_wait = sam3_i2s_rxWait;
170         i2s->ctx.rx_stop = sam3_i2s_rxStop;
171
172         DB(i2s->ctx._type = I2S_SAM3X;)
173         i2s->hw = &i2s_hw;
174
175         PIOA_PDR = BV(SSC_TK) | BV(SSC_TF) | BV(SSC_TD);
176         PIO_PERIPH_SEL(SSC_PORT, BV(SSC_TK) | BV(SSC_TF) | BV(SSC_TD), PIO_PERIPH_B);
177
178         /* clock the ssc */
179         pmc_periphEnable(SSC_ID);
180
181         /* reset device */
182         SSC_CR = BV(SSC_SWRST) | BV(SSC_TXDIS) | BV(SSC_RXDIS);
183
184         /* Set transmission clock */
185         SSC_CMR = MCK_DIV & SSC_DIV_MASK;
186         /* Set the transmission mode:
187          * - the clk is generate from master clock
188          * - clock only during transfer
189          * - transmit Clock Gating Selection none
190          * - DELAY cycle insert before starting transmission
191          * - generate frame sync each 2*(PERIOD + 1) tramit clock
192          * - Receive start on falling edge RF
193          */
194         SSC_TCMR = SSC_CKS_DIV | SSC_CKO_CONT | SSC_CKG_NONE | DELAY | PERIOD | SSC_START_FALL_F;
195         /* Set the transmission frame mode:
196          * - data len DATALEN + 1
197          * - word per frame DATNB + 1
198          * - frame sync len FSLEN + (FSLEN_EXT * 16) + 1
199          * - DELAY cycle insert before starting transmission
200          * - MSB
201          * - Frame sync output selection negative
202          */
203         SSC_TFMR = DATALEN | DATNB | FSLEN | EXTRA_FSLEN | BV(SSC_MSBF) | SSC_FSOS_POSITIVE;
204
205         SSC_IDR = 0xFFFFFFFF;
206         SSC_CR = BV(SSC_TXEN) | BV(SSC_RXEN);
207 }