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