Implement the dac streaming api.
[bertos.git] / bertos / cpu / cortex-m3 / drv / dac_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  *
33  * \brief DAC hardware-specific implementation
34  *
35  * \author Daniele Basile <asterix@develer.com>
36  */
37
38 #include "dac_sam3.h"
39
40 #include "cfg/cfg_dac.h"
41
42 #include <cfg/macros.h>
43 #include <cfg/compiler.h>
44
45 // Define log settings for cfg/log.h.
46 #define LOG_LEVEL         DAC_LOG_LEVEL
47 #define LOG_FORMAT        DAC_LOG_FORMAT
48 #include <cfg/log.h>
49
50 #include <drv/dac.h>
51 #include <cpu/irq.h>
52 #include <drv/irq_cm3.h>
53
54 #include <cpu/types.h>
55
56 #include <mware/event.h>
57
58 #include <io/cm3.h>
59
60 #include <string.h>
61
62 struct DacHardware
63 {
64         uint16_t channels;
65         uint32_t rate;
66         bool end;
67 };
68
69 struct DacHardware dac_hw;
70
71
72 /* We use event to signal the end of conversion */
73 static Event buff_emtpy;
74
75 #if CONFIG_DAC_TIMER == DACC_TRGSEL_TIO_CH0 /* Select Timer counter TIO Channel 0 */
76         #define DAC_TC_ID         TC0_ID
77         #define DAC_TC_CCR        TC0_CCR0
78         #define DAC_TC_IDR        TC0_IDR0
79         #define DAC_TC_CMR        TC0_CMR0
80         #define DAC_TC_SR         TC0_SR0
81         #define DAC_TC_RA         TC0_RA0
82         #define DAC_TC_RC         TC0_RC0
83 #elif CONFIG_DAC_TIMER == DACC_TRGSEL_TIO_CH1 /* Select Timer counter TIO Channel 1 */
84         #define DAC_TC_ID         TC1_ID
85         #define DAC_TC_CCR        TC0_CCR1
86         #define DAC_TC_IDR        TC0_IDR1
87         #define DAC_TC_CMR        TC0_CMR1
88         #define DAC_TC_SR         TC0_SR1
89         #define DAC_TC_RA         TC0_RA1
90         #define DAC_TC_RC         TC0_RC1
91 #elif CONFIG_DAC_TIMER == DACC_TRGSEL_TIO_CH2 /* Select Timer counter TIO Channel 2 */
92         #define DAC_TC_ID         TC2_ID
93         #define DAC_TC_CCR        TC0_CCR2
94         #define DAC_TC_IDR        TC0_IDR2
95         #define DAC_TC_CMR        TC0_CMR2
96         #define DAC_TC_SR         TC0_SR2
97         #define DAC_TC_RA         TC0_RA2
98         #define DAC_TC_RC         TC0_RC2
99 #elif CONFIG_DAC_TIMER == DACC_TRGSEL_PWM0 || CONFIG_DAC_TIMER == DACC_TRGSEL_PWM1
100         #error unimplemented pwm triger select.
101 #endif
102
103
104 INLINE void tc_init(void)
105 {
106         pmc_periphEnable(DAC_TC_ID);
107
108         /*  Disable TC clock */
109         DAC_TC_CCR = BV(TC_CCR_CLKDIS);
110         /*  Disable interrupts */
111         DAC_TC_IDR = 0xFFFFFFFF;
112         /*  Clear status register */
113         volatile uint32_t dummy = DAC_TC_SR;
114         (void)dummy;
115
116         /*
117          * Setup the timer counter:
118          * - select clock TCLK1 (MCK/2)
119          * - enable wave form mode
120          * - RA compare effect SET
121          * - RC compare effect CLEAR
122          * - UP mode with automatic trigger on RC Compare
123          */
124         DAC_TC_CMR = TC_TIMER_CLOCK1 | BV(TC_CMR_WAVE) | TC_CMR_ACPA_SET | TC_CMR_ACPC_CLEAR | BV(TC_CMR_CPCTRG);
125
126
127         /* Setup the pio: TODO: fix for more generic */
128         PIOB_PDR = BV(25);
129         PIO_PERIPH_SEL(PIOB_BASE, BV(25), PIO_PERIPH_B);
130 }
131
132 INLINE void tc_setup(uint32_t freq, size_t n_sample)
133 {
134         /*
135          * Compute the sample frequency
136          * the RC counter will update every MCK/2 (see above)
137          * so to convert one sample at the user freq we generate
138          * the trigger every TC_CLK / (numer_of_sample * user_freq)
139          * where TC_CLK = MCK / 2.
140          */
141         uint32_t rc = DIV_ROUND((CPU_FREQ / 2), n_sample * freq);
142         DAC_TC_RC = rc;
143         /* generate the square wave with duty = 50% */
144         DAC_TC_RA = DIV_ROUND(50 * rc, 100);
145 }
146
147 INLINE void tc_start(void)
148 {
149         DAC_TC_CCR = BV(TC_CCR_CLKEN)| BV(TC_CCR_SWTRG);
150 }
151
152 INLINE void tc_stop(void)
153 {
154         DAC_TC_CCR =  BV(TC_CCR_CLKDIS);
155 }
156
157 static int sam3x_dac_write(struct Dac *dac, unsigned channel, uint16_t sample)
158 {
159         (void)dac;
160
161         ASSERT(channel <= DAC_MAXCH);
162
163         DACC_MR |= (channel << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK;
164         DACC_CHER |= BV(channel);
165
166         DACC_CDR = sample ;
167
168         return 0;
169 }
170
171 static void sam3x_dac_setCh(struct Dac *dac, uint32_t mask)
172 {
173         /* we have only the ch0 and ch1 */
174         ASSERT(mask < BV(3));
175         dac->hw->channels = mask;
176
177         if (mask & BV(DACC_CH0))
178                 DACC_MR |= (DACC_CH0 << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK;
179
180         if (mask & BV(DACC_CH1))
181                 DACC_MR |= (DACC_CH1 << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK;
182
183         DACC_CHER |= mask;
184
185 }
186
187 static void sam3x_dac_setSampleRate(struct Dac *dac, uint32_t rate)
188 {
189         /* Eneble hw trigger */
190         DACC_MR |= BV(DACC_TRGEN) | (CONFIG_DAC_TIMER << DACC_TRGSEL_SHIFT);
191         dac->hw->rate = rate;
192 }
193
194 static void sam3x_dac_conversion(struct Dac *dac, void *buf, size_t len)
195 {
196         /* setup timer and start it */
197         tc_setup(dac->hw->rate, len);
198         tc_start();
199
200         /* Setup dma and start it */
201         DACC_TPR = (uint32_t)buf;
202         DACC_TCR = len;
203         DACC_PTCR |= BV(DACC_PTCR_TXTEN);
204 }
205
206 static uint16_t *sample_buff;
207 static size_t next_idx = 0;
208 static size_t chunk_size = 0;
209 static size_t remaing_size = 0;
210
211 static DECLARE_ISR(irq_dac)
212 {
213         if (DACC_ISR & BV(DACC_ENDTX))
214         {
215                 if (remaing_size > 0)
216                 {
217                         DACC_TNPR = (uint32_t)&sample_buff[next_idx];
218                         DACC_TNCR = chunk_size;
219
220                         remaing_size -= chunk_size;
221                         next_idx += chunk_size;
222                 }
223                 else
224                         /* Clear the pending irq when the dma ends the conversion */
225                         DACC_TCR = 1;
226         }
227         event_do(&buff_emtpy);
228 }
229
230
231 static bool sam3x_dac_isFinished(struct Dac *dac)
232 {
233         return dac->hw->end;
234 }
235
236 static void sam3x_dac_start(struct Dac *dac, void *_buf, size_t len, size_t slice_len)
237 {
238         ASSERT(dac);
239         ASSERT(len >= slice_len);
240
241         /* Reset the previous status. */
242         dac->hw->end = false;
243
244         sample_buff = (uint16_t *)_buf;
245         next_idx = 0;
246         chunk_size = slice_len;
247         remaing_size = len;
248
249
250         /* Program the dma with the first and second chunk of samples and update counter */
251         dac->ctx.callback(dac, &sample_buff[0], chunk_size);
252         DACC_TPR = (uint32_t)&sample_buff[0];
253         DACC_TCR = chunk_size;
254         remaing_size -= chunk_size;
255         next_idx += chunk_size;
256
257         if (chunk_size <= remaing_size)
258         {
259                 dac->ctx.callback(dac, &sample_buff[next_idx], chunk_size);
260
261                 DACC_TNPR = (uint32_t)&sample_buff[next_idx];
262                 DACC_TNCR = chunk_size;
263
264                 remaing_size -= chunk_size;
265                 next_idx += chunk_size;
266
267         }
268
269         DACC_PTCR |= BV(DACC_PTCR_TXTEN);
270         DACC_IER = BV(DACC_ENDTX);
271
272         /* Set up timer and trig the conversions */
273         tc_setup(dac->hw->rate, len);
274         tc_start();
275
276         while (1)
277         {
278                 event_wait(&buff_emtpy);
279                 if (remaing_size <= 0)
280                 {
281                         DAC_TC_CCR = BV(TC_CCR_CLKDIS);
282                         dac->hw->end = true;
283                         next_idx = 0;
284                         chunk_size = 0;
285                         remaing_size = 0;
286                         break;
287                 }
288
289                 dac->ctx.callback(dac, &sample_buff[next_idx], chunk_size);
290         }
291 }
292
293 static void sam3x_dac_stop(struct Dac *dac)
294 {
295         dac->hw->end = false;
296         /* Disable the irq, timer and channel */
297         DACC_IDR = BV(DACC_ENDTX);
298         DACC_PTCR |= BV(DACC_PTCR_TXTDIS);
299         DAC_TC_CCR = BV(TC_CCR_CLKDIS);
300 }
301
302
303 void dac_init(struct Dac *dac)
304 {
305         /* Initialize the dataready event */
306         event_initGeneric(&buff_emtpy);
307
308         /* Fill the virtual table */
309         dac->ctx.write = sam3x_dac_write;
310         dac->ctx.setCh = sam3x_dac_setCh;
311         dac->ctx.setSampleRate = sam3x_dac_setSampleRate;
312         dac->ctx.conversion = sam3x_dac_conversion;
313         dac->ctx.isFinished = sam3x_dac_isFinished;
314         dac->ctx.start = sam3x_dac_start;
315         dac->ctx.stop = sam3x_dac_stop;
316         DB(dac->ctx._type = DAC_SAM3X;)
317         dac->hw = &dac_hw;
318
319         /* Clock DAC peripheral */
320         pmc_periphEnable(DACC_ID);
321
322         /* Reset hw */
323         DACC_CR |= BV(DACC_SWRST);
324         DACC_MR = 0;
325
326         /* Configure the dac */
327         DACC_MR |= (CONFIG_DAC_REFRESH << DACC_REFRESH_SHIFT) & DACC_REFRESH_MASK;
328         DACC_MR |= (CONFIG_DAC_STARTUP << DACC_STARTUP_SHIFT) & DACC_STARTUP_MASK;
329
330         DACC_IDR = 0xFFFFFFFF;
331         sysirq_setHandler(INT_DACC, irq_dac);
332
333         tc_init();
334 }