81b38c10ce462897bf7252dc84e619b1f6153ff4
[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
39 #include "hw/hw_i2s.h"
40
41 #include "cfg/cfg_i2s.h"
42
43 // Define log settings for cfg/log.h.
44 #define LOG_LEVEL         I2S_LOG_LEVEL
45 #define LOG_FORMAT        I2S_LOG_FORMAT
46 #include <cfg/log.h>
47
48 #include <drv/timer.h>
49 #include <drv/i2s.h>
50 #include <drv/dmac_sam3.h>
51
52 #include <mware/event.h>
53
54 #include <cpu/irq.h>
55
56 #include <io/cm3.h>
57
58 #include <string.h>
59
60
61 #define I2S_DMAC_CH    3
62
63 #define I2S_CACHED_CHUNK_SIZE 2
64
65 #define I2S_TX_DMAC_CFG  (BV(DMAC_CFG_DST_H2SEL) | \
66                                                   BV(DMAC_CFG_SOD) | \
67                                                 ((3 << DMAC_CFG_DST_PER_SHIFT) & DMAC_CFG_DST_PER_MASK) | \
68                                                  (4 & DMAC_CFG_SRC_PER_MASK))
69
70 #define I2S_TX_DMAC_CTRLA  (DMAC_CTRLA_SRC_WIDTH_HALF_WORD | \
71                                                         DMAC_CTRLA_DST_WIDTH_HALF_WORD)
72
73 #define I2S_TX_DMAC_CTRLB  (DMAC_CTRLB_FC_MEM2PER_DMA_FC | \
74                                                                          DMAC_CTRLB_DST_INCR_FIXED | \
75                                                                         DMAC_CTRLB_SRC_INCR_INCREMENTING)
76
77 #define I2S_RX_DMAC_CFG  (BV(DMAC_CFG_SRC_H2SEL) | \
78                                                   BV(DMAC_CFG_SOD) | \
79                                                 ((4 << DMAC_CFG_DST_PER_SHIFT) & DMAC_CFG_DST_PER_MASK) | \
80                                                  (4 & DMAC_CFG_SRC_PER_MASK))
81
82 #define I2S_RX_DMAC_CTRLA  (DMAC_CTRLA_SRC_WIDTH_HALF_WORD | \
83                                                         DMAC_CTRLA_DST_WIDTH_HALF_WORD)
84
85 #define I2S_RX_DMAC_CTRLB  (DMAC_CTRLB_FC_PER2MEM_DMA_FC | \
86                                                     DMAC_CTRLB_DST_INCR_INCREMENTING | \
87                                                         DMAC_CTRLB_SRC_INCR_FIXED)
88
89
90
91 #define I2S_STATUS_ERR              BV(0)
92 #define I2S_STATUS_SINGLE_TRASF     BV(1)
93 #define I2S_STATUS_TX               BV(2)
94 #define I2S_STATUS_RX               BV(3)
95 struct I2sHardware
96 {
97         bool end;
98 };
99
100 struct I2sHardware i2s_hw;
101 static Event data_ready;
102
103 DmacDesc lli0;
104 DmacDesc lli1;
105 DmacDesc *curr;
106 DmacDesc *next;
107 DmacDesc *prev;
108
109 static uint8_t i2s_status;
110 static int16_t *sample_buff;
111 static size_t next_idx = 0;
112 static size_t chunk_size = 0;
113 static size_t remaing_size = 0;
114 static size_t transfer_size = 0;
115
116 static void sam3_i2s_txStop(I2s *i2s)
117 {
118         (void)i2s;
119         SSC_CR = BV(SSC_TXDIS);
120         dmac_stop(I2S_DMAC_CH);
121
122         i2s->hw->end = true;
123         remaing_size = 0;
124         next_idx = 0;
125         transfer_size = 0;
126
127         i2s_status &= ~I2S_STATUS_TX;
128
129         event_do(&data_ready);
130 }
131
132 static void sam3_i2s_txWait(I2s *i2s)
133 {
134         (void)i2s;
135         event_wait(&data_ready);
136 }
137
138 static void i2s_dmac_irq(uint32_t status)
139 {
140         I2S_STROBE_ON();
141         if (i2s_status & I2S_STATUS_SINGLE_TRASF)
142         {
143                 i2s_status &= ~I2S_STATUS_SINGLE_TRASF;
144         }
145         else
146         {
147                 if (status & (BV(I2S_DMAC_CH) << DMAC_EBCIDR_ERR0))
148                 {
149                         i2s_status |= I2S_STATUS_ERR;
150                         // Disable to reset channel and clear fifo
151                         dmac_stop(I2S_DMAC_CH);
152                 }
153                 else
154                 {
155                         prev = curr;
156                         curr = next;
157                         next = prev;
158
159                         if (i2s_status & I2S_STATUS_TX)
160                         {
161                                 curr->src_addr = (uint32_t)&sample_buff[next_idx];
162                                 curr->dst_addr = (uint32_t)&SSC_THR;
163                                 curr->dsc_addr = (uint32_t)next;
164                                 curr->ctrla    = I2S_TX_DMAC_CTRLA | (chunk_size & 0xffff);
165                                 curr->ctrlb    = I2S_TX_DMAC_CTRLB & ~BV(DMAC_CTRLB_IEN);
166                         }
167                         else
168                         {
169                                 curr->src_addr = (uint32_t)&SSC_RHR;
170                                 curr->dst_addr = (uint32_t)&sample_buff[next_idx];
171                                 curr->dsc_addr = (uint32_t)next;
172                                 curr->ctrla    = I2S_RX_DMAC_CTRLA | (chunk_size & 0xffff);
173                                 curr->ctrlb    = I2S_RX_DMAC_CTRLB & ~BV(DMAC_CTRLB_IEN);
174                         }
175
176                         remaing_size -= chunk_size;
177                         next_idx += chunk_size;
178
179                         if (remaing_size <= 0)
180                         {
181                                 remaing_size = transfer_size;
182                                 next_idx = 0;
183                         }
184                 }
185         }
186         event_do(&data_ready);
187         I2S_STROBE_OFF();
188 }
189
190
191 static void sam3_i2s_txStart(I2s *i2s, void *buf, size_t len, size_t slice_len)
192 {
193         ASSERT(buf);
194         ASSERT(len >= slice_len);
195         ASSERT(!(len % slice_len));
196
197         i2s->hw->end = false;
198         i2s_status &= ~I2S_STATUS_SINGLE_TRASF;
199
200         sample_buff = (int16_t *)buf;
201         next_idx = 0;
202         chunk_size = slice_len / 2;
203         remaing_size = len / 2;
204         transfer_size = len / 2;
205
206
207         memset(&lli0, 0, sizeof(DmacDesc));
208         memset(&lli1, 0, sizeof(DmacDesc));
209
210         prev = 0;
211         curr = &lli1;
212         next = &lli0;
213
214         for (int i = 0; i < I2S_CACHED_CHUNK_SIZE; i++)
215         {
216                 prev = curr;
217                 curr = next;
218                 next = prev;
219
220                 i2s->ctx.tx_callback(i2s, &sample_buff[next_idx], chunk_size * 2);
221
222                 curr->src_addr = (uint32_t)&sample_buff[next_idx];
223                 curr->dst_addr = (uint32_t)&SSC_THR;
224                 curr->dsc_addr = (uint32_t)next;
225                 curr->ctrla    = I2S_TX_DMAC_CTRLA | (chunk_size & 0xffff);
226                 curr->ctrlb    = I2S_TX_DMAC_CTRLB & ~BV(DMAC_CTRLB_IEN);
227
228                 remaing_size -= chunk_size;
229                 next_idx += chunk_size;
230
231                 if (chunk_size > remaing_size)
232                         break;
233
234         }
235
236         dmac_setLLITransfer(I2S_DMAC_CH, prev, I2S_TX_DMAC_CFG);
237
238         if (dmac_start(I2S_DMAC_CH) < 0)
239         {
240                 LOG_ERR("DMAC start[%x]\n", dmac_error(I2S_DMAC_CH));
241                 return;
242         }
243
244         i2s_status &= ~I2S_STATUS_ERR;
245         i2s_status |= I2S_STATUS_TX;
246
247         SSC_CR = BV(SSC_TXEN);
248         I2S_STROBE_OFF();
249
250         while (1)
251         {
252                 event_wait(&data_ready);
253                 if (i2s_status & I2S_STATUS_ERR)
254                 {
255                         LOG_ERR("Error while streaming.\n");
256                         break;
257                 }
258
259                 if (i2s->hw->end)
260                 {
261                         LOG_INFO("Stop streaming.\n");
262                         break;
263                 }
264
265                 i2s->ctx.tx_callback(i2s, &sample_buff[next_idx], chunk_size * 2);
266                 cpu_relax();
267         }
268 }
269
270 static void sam3_i2s_rxStop(I2s *i2s)
271 {
272         (void)i2s;
273         SSC_CR = BV(SSC_RXDIS) | BV(SSC_TXDIS);
274         dmac_stop(I2S_DMAC_CH);
275
276         i2s->hw->end = true;
277         remaing_size = 0;
278         next_idx = 0;
279         transfer_size = 0;
280
281         i2s_status &= ~I2S_STATUS_RX;
282
283         event_do(&data_ready);
284 }
285
286 static void sam3_i2s_rxWait(I2s *i2s)
287 {
288         (void)i2s;
289         event_wait(&data_ready);
290 }
291
292 static void sam3_i2s_rxStart(I2s *i2s, void *buf, size_t len, size_t slice_len)
293 {
294         ASSERT(buf);
295         ASSERT(len >= slice_len);
296         ASSERT(!(len % slice_len));
297
298         i2s->hw->end = false;
299         i2s_status &= ~I2S_STATUS_SINGLE_TRASF;
300
301         sample_buff = (int16_t *)buf;
302         next_idx = 0;
303         chunk_size = slice_len / 2;
304         remaing_size = len / 2;
305         transfer_size = len / 2;
306
307         memset(&lli0, 0, sizeof(DmacDesc));
308         memset(&lli1, 0, sizeof(DmacDesc));
309
310         prev = 0;
311         curr = &lli1;
312         next = &lli0;
313
314         for (int i = 0; i < I2S_CACHED_CHUNK_SIZE; i++)
315         {
316                 prev = curr;
317                 curr = next;
318                 next = prev;
319
320                 i2s->ctx.rx_callback(i2s, &sample_buff[next_idx], chunk_size * 2);
321                 curr->src_addr = (uint32_t)&SSC_RHR;
322                 curr->dst_addr = (uint32_t)&sample_buff[next_idx];
323                 curr->dsc_addr = (uint32_t)next;
324                 curr->ctrla    = I2S_RX_DMAC_CTRLA | (chunk_size & 0xffff);
325                 curr->ctrlb    = I2S_RX_DMAC_CTRLB & ~BV(DMAC_CTRLB_IEN);
326
327                 remaing_size -= chunk_size;
328                 next_idx += chunk_size;
329
330                 if (chunk_size > remaing_size)
331                         break;
332
333         }
334         dmac_setLLITransfer(I2S_DMAC_CH, prev, I2S_RX_DMAC_CFG);
335
336         if (dmac_start(I2S_DMAC_CH) < 0)
337         {
338                 LOG_ERR("DMAC start[%x]\n", dmac_error(I2S_DMAC_CH));
339                 return;
340         }
341
342         i2s_status &= ~I2S_STATUS_ERR;
343         i2s_status |= I2S_STATUS_RX;
344
345         SSC_CR = BV(SSC_RXEN) | BV(SSC_TXEN);
346         I2S_STROBE_OFF();
347
348         while (1)
349         {
350                 event_wait(&data_ready);
351                 if (i2s_status & I2S_STATUS_ERR)
352                 {
353                         LOG_ERR("Error while streaming.\n");
354                         break;
355                 }
356
357                 if (i2s->hw->end)
358                 {
359                         LOG_INFO("Stop streaming.\n");
360                         break;
361                 }
362                 i2s->ctx.rx_callback(i2s, &sample_buff[next_idx], chunk_size * 2);
363                 cpu_relax();
364         }
365 }
366
367
368 static bool sam3_i2s_isTxFinish(struct I2s *i2s)
369 {
370         return i2s->hw->end;
371 }
372
373 static bool sam3_i2s_isRxFinish(struct I2s *i2s)
374 {
375         return i2s->hw->end;
376 }
377
378 static void sam3_i2s_txBuf(struct I2s *i2s, void *buf, size_t len)
379 {
380         (void)i2s;
381         i2s_status |= I2S_STATUS_SINGLE_TRASF;
382
383         dmac_setSources(I2S_DMAC_CH, (uint32_t)buf, (uint32_t)&SSC_THR);
384         dmac_configureDmac(I2S_DMAC_CH, len, I2S_TX_DMAC_CFG, I2S_TX_DMAC_CTRLA, I2S_TX_DMAC_CTRLB);
385         dmac_start(I2S_DMAC_CH);
386
387         SSC_CR = BV(SSC_TXEN);
388 }
389
390 static void sam3_i2s_rxBuf(struct I2s *i2s, void *buf, size_t len)
391 {
392         (void)i2s;
393
394         i2s_status |= I2S_STATUS_SINGLE_TRASF;
395
396         dmac_setSources(I2S_DMAC_CH, (uint32_t)&SSC_RHR, (uint32_t)buf);
397         dmac_configureDmac(I2S_DMAC_CH, len, I2S_RX_DMAC_CFG, I2S_RX_DMAC_CTRLA, I2S_RX_DMAC_CTRLB);
398         dmac_start(I2S_DMAC_CH);
399
400         SSC_CR = BV(SSC_RXEN);
401 }
402
403 static int sam3_i2s_write(struct I2s *i2s, uint32_t sample)
404 {
405         (void)i2s;
406
407         SSC_CR = BV(SSC_TXEN);
408         while(!(SSC_SR & BV(SSC_TXRDY)))
409                 cpu_relax();
410
411         SSC_THR = sample;
412         return 0;
413 }
414
415 static uint32_t sam3_i2s_read(struct I2s *i2s)
416 {
417         (void)i2s;
418
419         SSC_CR = BV(SSC_RXEN);
420         while(!(SSC_SR & BV(SSC_RXRDY)))
421                 cpu_relax();
422
423         return SSC_RHR;
424 }
425
426
427 /* We divite for 2 because the min clock for i2s i MCLK/2 */
428 #define MCK_DIV     (CPU_FREQ / (CONFIG_SAMPLE_FREQ * CONFIG_WORD_BIT_SIZE * CONFIG_CHANNEL_NUM * 2))
429 #define DATALEN     ((CONFIG_WORD_BIT_SIZE - 1) & SSC_DATLEN_MASK)
430 #define DELAY       ((CONFIG_DELAY << SSC_STTDLY_SHIFT) & SSC_STTDLY_MASK)
431 #define PERIOD      ((CONFIG_PERIOD << (SSC_PERIOD_SHIFT)) & SSC_PERIOD_MASK)
432 #define DATNB       ((CONFIG_WORD_PER_FRAME << SSC_DATNB_SHIFT) & SSC_DATNB_MASK)
433 #define FSLEN       ((CONFIG_FRAME_SYNC_SIZE << SSC_FSLEN_SHIFT) & SSC_FSLEN_MASK)
434 #define EXTRA_FSLEN (CONFIG_EXTRA_FRAME_SYNC_SIZE << SSC_FSLEN_EXT)
435
436 void i2s_init(I2s *i2s, int channel)
437 {
438         (void)channel;
439         i2s->ctx.write = sam3_i2s_write;
440         i2s->ctx.tx_buf = sam3_i2s_txBuf;
441         i2s->ctx.tx_isFinish = sam3_i2s_isTxFinish;
442         i2s->ctx.tx_start = sam3_i2s_txStart;
443         i2s->ctx.tx_wait = sam3_i2s_txWait;
444         i2s->ctx.tx_stop = sam3_i2s_txStop;
445
446         i2s->ctx.read = sam3_i2s_read;
447         i2s->ctx.rx_buf = sam3_i2s_rxBuf;
448         i2s->ctx.rx_isFinish = sam3_i2s_isRxFinish;
449         i2s->ctx.rx_start = sam3_i2s_rxStart;
450         i2s->ctx.rx_wait = sam3_i2s_rxWait;
451         i2s->ctx.rx_stop = sam3_i2s_rxStop;
452
453         DB(i2s->ctx._type = I2S_SAM3X;)
454         i2s->hw = &i2s_hw;
455
456         I2S_STROBE_INIT();
457
458         PIOA_PDR = BV(SSC_TK) | BV(SSC_TF) | BV(SSC_TD);
459         PIO_PERIPH_SEL(PIOA_BASE, BV(SSC_TK) | BV(SSC_TF) | BV(SSC_TD), PIO_PERIPH_B);
460
461         PIOB_PDR = BV(SSC_RD) | BV(SSC_RF);
462         PIO_PERIPH_SEL(PIOB_BASE, BV(SSC_RD) | BV(SSC_RF), PIO_PERIPH_A);
463
464         /* clock the ssc */
465         pmc_periphEnable(SSC_ID);
466
467         /* reset device */
468         SSC_CR = BV(SSC_SWRST) | BV(SSC_TXDIS) | BV(SSC_RXDIS);
469
470         /* Set transmission clock */
471         SSC_CMR = MCK_DIV & SSC_DIV_MASK;
472         /* Set the transmission mode:
473          * - the clk is generate from master clock
474          * - clock only during transfer
475          * - transmit Clock Gating Selection none
476          * - DELAY cycle insert before starting transmission
477          * - generate frame sync each 2*(PERIOD + 1) tramit clock
478          * - Receive start on falling edge RF
479          */
480         SSC_TCMR = SSC_CKS_DIV | SSC_CKO_CONT | SSC_CKG_NONE | DELAY | PERIOD | SSC_START_FALL_F;
481         /* Set the transmission frame mode:
482          * - data len DATALEN + 1
483          * - word per frame DATNB + 1
484          * - frame sync len FSLEN + (FSLEN_EXT * 16) + 1
485          * - DELAY cycle insert before starting transmission
486          * - MSB
487          * - Frame sync output selection negative
488          */
489         SSC_TFMR = DATALEN | DATNB | FSLEN | EXTRA_FSLEN | BV(SSC_MSBF) | SSC_FSOS_NEGATIVE;
490
491
492         // Receiver should start on TX and take the clock from TK
493     SSC_RCMR = SSC_CKS_CLK | BV(SSC_CKI) | SSC_CKO_CONT | SSC_CKG_NONE | DELAY | PERIOD | SSC_START_TX;
494     SSC_RFMR = DATALEN | DATNB | FSLEN  | EXTRA_FSLEN | BV(SSC_MSBF) | SSC_FSOS_NEGATIVE;
495
496
497         SSC_IDR = 0xFFFFFFFF;
498
499         dmac_enableCh(I2S_DMAC_CH, i2s_dmac_irq);
500         event_initGeneric(&data_ready);
501 }