Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
| 8 | * $Id$ |
| 9 | * |
| 10 | * Copyright (C) 2006 by Michael Sevakis |
| 11 | * |
Daniel Stenberg | 2acc0ac | 2008-06-28 18:10:04 +0000 | [diff] [blame^] | 12 | * This program is free software; you can redistribute it and/or |
| 13 | * modify it under the terms of the GNU General Public License |
| 14 | * as published by the Free Software Foundation; either version 2 |
| 15 | * of the License, or (at your option) any later version. |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 16 | * |
| 17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 18 | * KIND, either express or implied. |
| 19 | * |
| 20 | ****************************************************************************/ |
| 21 | #include <stdlib.h> |
| 22 | #include "system.h" |
| 23 | #include "kernel.h" |
| 24 | #include "logf.h" |
| 25 | #include "audio.h" |
Michael Sevakis | c2d2106 | 2007-03-11 06:21:43 +0000 | [diff] [blame] | 26 | #include "sound.h" |
Michael Sevakis | 8f659ae | 2007-05-20 20:26:36 +0000 | [diff] [blame] | 27 | #if defined(HAVE_SPDIF_REC) || defined(HAVE_SPDIF_OUT) |
Michael Sevakis | cc50c14 | 2006-11-13 23:21:54 +0000 | [diff] [blame] | 28 | #include "spdif.h" |
| 29 | #endif |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 30 | |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 31 | #define IIS_PLAY_DEFPARM ( (freq_ent[FPARM_CLOCKSEL] << 12) | \ |
| 32 | (IIS_PLAY & (7 << 8)) | \ |
| 33 | (4 << 2) ) /* 64 bit clocks / word clock */ |
| 34 | #define IIS_FIFO_RESET (1 << 11) |
| 35 | #define PDIR2_FIFO_RESET (1 << 9) |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 36 | |
Jens Arnold | 3005cf5 | 2008-03-15 21:55:14 +0000 | [diff] [blame] | 37 | #if defined(IAUDIO_X5) || defined(IAUDIO_M5) || defined(IAUDIO_M3) |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 38 | #define SET_IIS_PLAY(x) IIS1CONFIG = (x) |
| 39 | #define IIS_PLAY IIS1CONFIG |
| 40 | #else |
| 41 | #define SET_IIS_PLAY(x) IIS2CONFIG = (x) |
| 42 | #define IIS_PLAY IIS2CONFIG |
| 43 | #endif |
| 44 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 45 | struct dma_lock |
| 46 | { |
| 47 | int locked; |
| 48 | unsigned long state; |
| 49 | }; |
| 50 | |
Michael Sevakis | 633f388 | 2007-03-07 06:23:02 +0000 | [diff] [blame] | 51 | static bool is_playback_monitoring(void) |
| 52 | { |
| 53 | return (IIS_PLAY & (7 << 8)) == (3 << 8); |
| 54 | } |
| 55 | |
| 56 | static void iis_play_reset_if_playback(bool if_playback) |
| 57 | { |
| 58 | bool is_playback = is_playback_monitoring(); |
| 59 | |
| 60 | if (is_playback != if_playback) |
| 61 | return; |
| 62 | |
| 63 | or_l(IIS_FIFO_RESET, &IIS_PLAY); |
| 64 | } |
| 65 | |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 66 | #define PLLCR_SET_AUDIO_BITS_DEFPARM \ |
| 67 | ((freq_ent[FPARM_CLSEL] << 28) | (1 << 22)) |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 68 | |
| 69 | /** Sample rates **/ |
| 70 | #define FPARM_CLOCKSEL 0 |
| 71 | #define FPARM_CLSEL 1 |
| 72 | #define FPARM_FSEL 2 |
| 73 | #if CONFIG_CPU == MCF5249 && defined(HAVE_UDA1380) |
| 74 | static const unsigned char pcm_freq_parms[HW_NUM_FREQ][3] = |
| 75 | { |
| 76 | [HW_FREQ_88] = { 0x0c, 0x01, 0x03 }, |
| 77 | [HW_FREQ_44] = { 0x06, 0x01, 0x02 }, |
| 78 | [HW_FREQ_22] = { 0x04, 0x02, 0x01 }, |
| 79 | [HW_FREQ_11] = { 0x02, 0x02, 0x00 }, |
| 80 | }; |
| 81 | #endif |
| 82 | |
Jens Arnold | 8a6291d | 2008-03-14 08:54:54 +0000 | [diff] [blame] | 83 | #if (CONFIG_CPU == MCF5250 || CONFIG_CPU == MCF5249) && defined(HAVE_TLV320) |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 84 | static const unsigned char pcm_freq_parms[HW_NUM_FREQ][3] = |
| 85 | { |
| 86 | [HW_FREQ_88] = { 0x0c, 0x01, 0x02 }, |
| 87 | [HW_FREQ_44] = { 0x06, 0x01, 0x01 }, |
| 88 | [HW_FREQ_22] = { 0x04, 0x01, 0x00 }, |
| 89 | [HW_FREQ_11] = { 0x02, 0x02, 0x00 }, |
| 90 | }; |
| 91 | #endif |
| 92 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 93 | static unsigned long pcm_freq = 0; /* 44.1 is default */ |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 94 | static const unsigned char *freq_ent = pcm_freq_parms[HW_FREQ_DEFAULT]; |
| 95 | |
| 96 | /* set frequency used by the audio hardware */ |
| 97 | void pcm_set_frequency(unsigned int frequency) |
| 98 | { |
| 99 | int index; |
| 100 | |
| 101 | switch(frequency) |
| 102 | { |
| 103 | case SAMPR_11: |
| 104 | index = HW_FREQ_11; |
| 105 | break; |
| 106 | case SAMPR_22: |
| 107 | index = HW_FREQ_22; |
| 108 | break; |
| 109 | default: |
| 110 | case SAMPR_44: |
| 111 | index = HW_FREQ_44; |
| 112 | break; |
| 113 | case SAMPR_88: |
| 114 | index = HW_FREQ_88; |
| 115 | break; |
| 116 | } |
| 117 | |
| 118 | /* remember table entry and rate */ |
| 119 | freq_ent = pcm_freq_parms[index]; |
| 120 | pcm_freq = hw_freq_sampr[index]; |
| 121 | } /* pcm_set_frequency */ |
| 122 | |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 123 | /* apply audio settings */ |
Bertrik Sikken | 1942590 | 2008-05-03 21:33:00 +0000 | [diff] [blame] | 124 | static bool _pcm_apply_settings(bool clear_reset) |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 125 | { |
Michael Sevakis | 633f388 | 2007-03-07 06:23:02 +0000 | [diff] [blame] | 126 | bool did_reset = false; |
Michael Sevakis | 6c8772d | 2007-03-14 10:06:57 +0000 | [diff] [blame] | 127 | unsigned long iis_play_defparm = IIS_PLAY_DEFPARM; |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 128 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 129 | if (pcm_freq != pcm_curr_sampr) |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 130 | { |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 131 | pcm_curr_sampr = pcm_freq; |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 132 | /* Reprogramming bits 15-12 requires FIFO to be in a reset |
| 133 | condition - Users Manual 17-8, Note 11 */ |
| 134 | or_l(IIS_FIFO_RESET, &IIS_PLAY); |
Michael Sevakis | abf41e3 | 2007-03-13 15:05:46 +0000 | [diff] [blame] | 135 | /* Important for TLV320 - this must happen in the correct order |
| 136 | or starting recording will sound absolutely awful once in |
| 137 | awhile - audiohw_set_frequency then coldfire_set_pllcr_audio_bits |
| 138 | */ |
Michael Sevakis | 6c8772d | 2007-03-14 10:06:57 +0000 | [diff] [blame] | 139 | SET_IIS_PLAY(iis_play_defparm | IIS_FIFO_RESET); |
Marcoen Hirschberg | 77d039b | 2006-12-06 10:24:59 +0000 | [diff] [blame] | 140 | audiohw_set_frequency(freq_ent[FPARM_FSEL]); |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 141 | coldfire_set_pllcr_audio_bits(PLLCR_SET_AUDIO_BITS_DEFPARM); |
Michael Sevakis | 633f388 | 2007-03-07 06:23:02 +0000 | [diff] [blame] | 142 | did_reset = true; |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 143 | } |
| 144 | |
Michael Sevakis | 6c8772d | 2007-03-14 10:06:57 +0000 | [diff] [blame] | 145 | /* If a reset was done because of a sample rate change, IIS_PLAY will have |
| 146 | been set already, so only needs to be set again if the reset flag must |
| 147 | be cleared. If the frequency didn't change, it was never altered and |
| 148 | the reset flag can just be removed or no action taken. */ |
| 149 | if (clear_reset) |
| 150 | SET_IIS_PLAY(iis_play_defparm & ~IIS_FIFO_RESET); |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 151 | #if 0 |
| 152 | logf("IISPLAY: %08X", IIS_PLAY); |
| 153 | #endif |
Michael Sevakis | 633f388 | 2007-03-07 06:23:02 +0000 | [diff] [blame] | 154 | |
| 155 | return did_reset; |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 156 | } /* _pcm_apply_settings */ |
| 157 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 158 | /* apply audio setting with all DMA interrupts disabled */ |
Bertrik Sikken | 1942590 | 2008-05-03 21:33:00 +0000 | [diff] [blame] | 159 | static void _pcm_apply_settings_irq_lock(bool clear_reset) |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 160 | { |
| 161 | int level = set_irq_level(DMA_IRQ_LEVEL); |
| 162 | _pcm_apply_settings(clear_reset); |
Michael Sevakis | af395f4 | 2008-03-26 01:50:41 +0000 | [diff] [blame] | 163 | restore_irq(level); |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 164 | } |
| 165 | |
Michael Sevakis | 633f388 | 2007-03-07 06:23:02 +0000 | [diff] [blame] | 166 | /* This clears the reset bit to enable monitoring immediately if monitoring |
| 167 | recording sources or always if playback is in progress - we might be |
| 168 | switching samplerates on the fly */ |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 169 | void pcm_apply_settings(void) |
| 170 | { |
Michael Sevakis | 633f388 | 2007-03-07 06:23:02 +0000 | [diff] [blame] | 171 | int level = set_irq_level(DMA_IRQ_LEVEL); |
| 172 | bool pbm = is_playback_monitoring(); |
| 173 | bool kick = (DCR0 & DMA_EEXT) != 0 && pbm; |
| 174 | |
| 175 | /* Clear reset if not playback monitoring or peripheral request is |
| 176 | active and playback monitoring */ |
| 177 | if (_pcm_apply_settings(!pbm || kick) && kick) |
| 178 | PDOR3 = 0; /* Kick FIFO out of reset by writing to it */ |
| 179 | |
Michael Sevakis | af395f4 | 2008-03-26 01:50:41 +0000 | [diff] [blame] | 180 | restore_irq(level); |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 181 | } /* pcm_apply_settings */ |
| 182 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 183 | void pcm_play_dma_init(void) |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 184 | { |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 185 | AUDIOGLOB = (1 << 8) /* IIS1 fifo auto sync */ |
| 186 | | (1 << 7) /* PDIR2 fifo auto sync */ |
| 187 | #ifdef HAVE_SPDIF_OUT |
| 188 | | (1 << 10) /* EBU TX auto sync */ |
| 189 | #endif |
| 190 | ; |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 191 | DIVR0 = 54; /* DMA0 is mapped into vector 54 in system.c */ |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 192 | and_l(0xffffff00, &DMAROUTE); |
| 193 | or_l(DMA0_REQ_AUDIO_1, &DMAROUTE); |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 194 | DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */ |
| 195 | |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 196 | /* Call pcm_play_dma_stop to initialize everything. */ |
| 197 | pcm_play_dma_stop(); |
Michael Sevakis | 5d63776 | 2007-03-11 05:04:48 +0000 | [diff] [blame] | 198 | |
Michael Sevakis | abf41e3 | 2007-03-13 15:05:46 +0000 | [diff] [blame] | 199 | /* Setup Coldfire I2S before initializing hardware or changing |
| 200 | other settings. */ |
| 201 | or_l(IIS_FIFO_RESET, &IIS_PLAY); |
Michael Sevakis | 6c8772d | 2007-03-14 10:06:57 +0000 | [diff] [blame] | 202 | SET_IIS_PLAY(IIS_PLAY_DEFPARM | IIS_FIFO_RESET); |
Michael Sevakis | abf41e3 | 2007-03-13 15:05:46 +0000 | [diff] [blame] | 203 | pcm_set_frequency(HW_FREQ_DEFAULT); |
| 204 | audio_set_output_source(AUDIO_SRC_PLAYBACK); |
Michael Sevakis | abf41e3 | 2007-03-13 15:05:46 +0000 | [diff] [blame] | 205 | |
Michael Sevakis | 5d63776 | 2007-03-11 05:04:48 +0000 | [diff] [blame] | 206 | /* Initialize default register values. */ |
| 207 | audiohw_init(); |
| 208 | |
Michael Sevakis | 2d48d0f | 2007-06-08 23:42:04 +0000 | [diff] [blame] | 209 | audio_input_mux(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 210 | |
Michael Sevakis | abf41e3 | 2007-03-13 15:05:46 +0000 | [diff] [blame] | 211 | audiohw_set_frequency(freq_ent[FPARM_FSEL]); |
| 212 | coldfire_set_pllcr_audio_bits(PLLCR_SET_AUDIO_BITS_DEFPARM); |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 213 | |
Michael Sevakis | 8f659ae | 2007-05-20 20:26:36 +0000 | [diff] [blame] | 214 | #if defined(HAVE_SPDIF_REC) || defined(HAVE_SPDIF_OUT) |
Michael Sevakis | cc50c14 | 2006-11-13 23:21:54 +0000 | [diff] [blame] | 215 | spdif_init(); |
| 216 | #endif |
Michael Sevakis | 633f388 | 2007-03-07 06:23:02 +0000 | [diff] [blame] | 217 | /* Enable interrupt at level 6, priority 0 */ |
| 218 | ICR6 = (6 << 2); |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 219 | } /* pcm_play_dma_init */ |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 220 | |
Michael Sevakis | c2d2106 | 2007-03-11 06:21:43 +0000 | [diff] [blame] | 221 | void pcm_postinit(void) |
| 222 | { |
| 223 | audiohw_postinit(); |
| 224 | } |
| 225 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 226 | /** DMA **/ |
| 227 | |
| 228 | /**************************************************************************** |
| 229 | ** Playback DMA transfer |
| 230 | **/ |
| 231 | /* For the locks, DMA interrupt must be disabled when manipulating the lock |
| 232 | if the handler ever calls these - right now things are arranged so it |
| 233 | doesn't */ |
| 234 | static struct dma_lock dma_play_lock = |
| 235 | { |
| 236 | .locked = 0, |
| 237 | .state = (0 << 14) /* bit 14 is DMA0 */ |
| 238 | }; |
| 239 | |
| 240 | void pcm_play_lock(void) |
| 241 | { |
| 242 | if (++dma_play_lock.locked == 1) |
| 243 | or_l((1 << 14), &IMR); |
| 244 | } |
| 245 | |
| 246 | void pcm_play_unlock(void) |
| 247 | { |
| 248 | if (--dma_play_lock.locked == 0) |
| 249 | and_l(~dma_play_lock.state, &IMR); |
| 250 | } |
| 251 | |
| 252 | /* Set up the DMA transfer that kicks in when the audio FIFO gets empty */ |
| 253 | void pcm_play_dma_start(const void *addr, size_t size) |
| 254 | { |
| 255 | logf("pcm_play_dma_start"); |
| 256 | |
| 257 | /* stop any DMA in progress */ |
| 258 | DSR0 = 1; |
| 259 | DCR0 = 0; |
| 260 | |
| 261 | /* Set up DMA transfer */ |
| 262 | SAR0 = (unsigned long)addr; /* Source address */ |
| 263 | DAR0 = (unsigned long)&PDOR3; /* Destination address */ |
| 264 | BCR0 = (unsigned long)size; /* Bytes to transfer */ |
| 265 | |
| 266 | /* Enable the FIFO and force one write to it */ |
| 267 | _pcm_apply_settings_irq_lock(is_playback_monitoring()); |
| 268 | |
| 269 | DCR0 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | |
| 270 | DMA_SINC | DMA_SSIZE(DMA_SIZE_LINE) | DMA_START; |
| 271 | |
| 272 | dma_play_lock.state = (1 << 14); |
| 273 | } /* pcm_play_dma_start */ |
| 274 | |
| 275 | /* Stops the DMA transfer and interrupt */ |
| 276 | void pcm_play_dma_stop(void) |
| 277 | { |
| 278 | DSR0 = 1; |
| 279 | DCR0 = 0; |
| 280 | BCR0 = 0; |
| 281 | |
| 282 | /* Place TX FIFO in reset condition if playback monitoring is on. |
| 283 | Recording monitoring something else should not be stopped. */ |
| 284 | iis_play_reset_if_playback(true); |
| 285 | |
| 286 | dma_play_lock.state = (0 << 14); |
| 287 | } /* pcm_play_dma_stop */ |
| 288 | |
| 289 | void pcm_play_dma_pause(bool pause) |
| 290 | { |
| 291 | if (pause) |
| 292 | { |
| 293 | /* pause playback on current buffer */ |
| 294 | and_l(~DMA_EEXT, &DCR0); |
| 295 | iis_play_reset_if_playback(true); |
| 296 | dma_play_lock.state = (0 << 14); |
| 297 | } |
| 298 | else |
| 299 | { |
| 300 | /* restart playback on current buffer */ |
| 301 | /* Enable the FIFO and force one write to it */ |
| 302 | _pcm_apply_settings_irq_lock(is_playback_monitoring()); |
| 303 | or_l(DMA_EEXT | DMA_START, &DCR0); |
| 304 | dma_play_lock.state = (1 << 14); |
| 305 | } |
| 306 | } /* pcm_play_dma_pause */ |
| 307 | |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 308 | size_t pcm_get_bytes_waiting(void) |
| 309 | { |
| 310 | return BCR0 & 0xffffff; |
| 311 | } /* pcm_get_bytes_waiting */ |
| 312 | |
| 313 | /* DMA0 Interrupt is called when the DMA has finished transfering a chunk |
| 314 | from the caller's buffer */ |
| 315 | void DMA0(void) __attribute__ ((interrupt_handler, section(".icode"))); |
| 316 | void DMA0(void) |
| 317 | { |
| 318 | int res = DSR0; |
| 319 | |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 320 | DSR0 = 1; /* Clear interrupt */ |
| 321 | and_l(~DMA_EEXT, &DCR0); /* Disable peripheral request */ |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 322 | /* Stop on error */ |
| 323 | if ((res & 0x70) == 0) |
| 324 | { |
| 325 | pcm_more_callback_type get_more = pcm_callback_for_more; |
| 326 | unsigned char *next_start; |
| 327 | size_t next_size = 0; |
| 328 | |
| 329 | if (get_more) |
| 330 | get_more(&next_start, &next_size); |
| 331 | |
| 332 | if (next_size > 0) |
| 333 | { |
| 334 | SAR0 = (unsigned long)next_start; /* Source address */ |
| 335 | BCR0 = next_size; /* Bytes to transfer */ |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 336 | or_l(DMA_EEXT, &DCR0); /* Enable peripheral request */ |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 337 | return; |
| 338 | } |
| 339 | else |
| 340 | { |
| 341 | /* Finished playing */ |
| 342 | #if 0 |
| 343 | /* int. logfs can trash the display */ |
| 344 | logf("DMA0 No Data:0x%04x", res); |
| 345 | #endif |
| 346 | } |
| 347 | } |
| 348 | else |
| 349 | { |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 350 | logf("DMA0 err: %02x", res); |
| 351 | #if 0 |
| 352 | logf(" SAR0: %08x", SAR0); |
| 353 | logf(" DAR0: %08x", DAR0); |
| 354 | logf(" BCR0: %08x", BCR0); |
| 355 | logf(" DCR0: %08x", DCR0); |
| 356 | #endif |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 357 | } |
| 358 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 359 | /* Stop interrupt and futher transfers */ |
| 360 | pcm_play_dma_stop(); |
| 361 | /* Inform PCM that we're done */ |
| 362 | pcm_play_dma_stopped_callback(); |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 363 | } /* DMA0 */ |
| 364 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 365 | const void * pcm_play_dma_get_peak_buffer(int *count) |
| 366 | { |
| 367 | unsigned long addr = SAR0; |
| 368 | int cnt = BCR0; |
| 369 | *count = (cnt & 0xffffff) >> 2; |
| 370 | return (void *)((addr + 2) & ~3); |
| 371 | } /* pcm_play_dma_get_peak_buffer */ |
| 372 | |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 373 | /**************************************************************************** |
| 374 | ** Recording DMA transfer |
| 375 | **/ |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 376 | static struct dma_lock dma_rec_lock = |
| 377 | { |
| 378 | .locked = 0, |
| 379 | .state = (0 << 15) /* bit 15 is DMA1 */ |
| 380 | }; |
| 381 | |
| 382 | /* For the locks, DMA interrupt must be disabled when manipulating the lock |
| 383 | if the handler ever calls these - right now things are arranged so it |
| 384 | doesn't */ |
| 385 | void pcm_rec_lock(void) |
| 386 | { |
| 387 | if (++dma_rec_lock.locked == 1) |
| 388 | or_l((1 << 15), &IMR); |
| 389 | } |
| 390 | |
| 391 | void pcm_rec_unlock(void) |
| 392 | { |
| 393 | if (--dma_rec_lock.locked == 0) |
| 394 | and_l(~dma_rec_lock.state, &IMR); |
| 395 | } |
| 396 | |
Michael Sevakis | 51189b4 | 2006-12-06 08:34:55 +0000 | [diff] [blame] | 397 | void pcm_rec_dma_start(void *addr, size_t size) |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 398 | { |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 399 | /* stop any DMA in progress */ |
| 400 | and_l(~DMA_EEXT, &DCR1); |
| 401 | DSR1 = 1; |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 402 | |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 403 | and_l(~PDIR2_FIFO_RESET, &DATAINCONTROL); |
Michael Sevakis | 633f388 | 2007-03-07 06:23:02 +0000 | [diff] [blame] | 404 | /* Clear TX FIFO reset bit if the source is not set to monitor playback |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 405 | otherwise maintain independence between playback and recording. */ |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 406 | _pcm_apply_settings_irq_lock(!is_playback_monitoring()); |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 407 | |
| 408 | /* Start the DMA transfer.. */ |
Michael Sevakis | 8f659ae | 2007-05-20 20:26:36 +0000 | [diff] [blame] | 409 | #ifdef HAVE_SPDIF_REC |
Michael Sevakis | 521d6a5 | 2007-04-18 02:07:56 +0000 | [diff] [blame] | 410 | /* clear: ebu1cnew, valnogood, symbolerr, parityerr */ |
| 411 | INTERRUPTCLEAR = (1 << 25) | (1 << 24) | (1 << 23) | (1 << 22); |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 412 | #endif |
| 413 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 414 | SAR1 = (unsigned long)&PDIR2; /* Source address */ |
| 415 | pcm_rec_peak_addr = (unsigned long *)addr; /* Start peaking at dest */ |
| 416 | DAR1 = (unsigned long)addr; /* Destination address */ |
| 417 | BCR1 = (unsigned long)size; /* Bytes to transfer */ |
Michael Sevakis | 51189b4 | 2006-12-06 08:34:55 +0000 | [diff] [blame] | 418 | |
Michael Sevakis | f51df0d | 2006-12-12 04:25:52 +0000 | [diff] [blame] | 419 | DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | DMA_DINC | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 420 | DMA_DSIZE(DMA_SIZE_LINE) | DMA_START; |
Michael Sevakis | 633f388 | 2007-03-07 06:23:02 +0000 | [diff] [blame] | 421 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 422 | dma_rec_lock.state = (1 << 15); |
| 423 | } /* pcm_rec_dma_start */ |
Michael Sevakis | 633f388 | 2007-03-07 06:23:02 +0000 | [diff] [blame] | 424 | |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 425 | void pcm_rec_dma_stop(void) |
| 426 | { |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 427 | DSR1 = 1; /* Clear interrupt */ |
| 428 | DCR1 = 0; |
| 429 | BCR1 = 0; |
| 430 | or_l(PDIR2_FIFO_RESET, &DATAINCONTROL); |
Michael Sevakis | 633f388 | 2007-03-07 06:23:02 +0000 | [diff] [blame] | 431 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 432 | iis_play_reset_if_playback(false); |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 433 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 434 | dma_rec_lock.state = (0 << 15); |
Michael Sevakis | 633f388 | 2007-03-07 06:23:02 +0000 | [diff] [blame] | 435 | } /* pcm_rec_dma_stop */ |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 436 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 437 | void pcm_rec_dma_init(void) |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 438 | { |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 439 | DIVR1 = 55; /* DMA1 is mapped into vector 55 in system.c */ |
| 440 | DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */ |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 441 | and_l(0xffff00ff, &DMAROUTE); |
| 442 | or_l(DMA1_REQ_AUDIO_2, &DMAROUTE); |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 443 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 444 | pcm_rec_dma_stop(); |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 445 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 446 | /* Enable interrupt at level 6, priority 1 */ |
| 447 | ICR7 = (6 << 2) | (1 << 0); |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 448 | } /* pcm_init_recording */ |
| 449 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 450 | void pcm_rec_dma_close(void) |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 451 | { |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 452 | and_l(0xffff00ff, &DMAROUTE); |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 453 | ICR7 = 0x00; /* Disable interrupt */ |
| 454 | dma_rec_lock.state = (0 << 15); |
| 455 | } /* pcm_rec_dma_close */ |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 456 | |
| 457 | /* DMA1 Interrupt is called when the DMA has finished transfering a chunk |
| 458 | into the caller's buffer */ |
| 459 | void DMA1(void) __attribute__ ((interrupt_handler, section(".icode"))); |
| 460 | void DMA1(void) |
| 461 | { |
| 462 | int res = DSR1; |
Michael Sevakis | 51189b4 | 2006-12-06 08:34:55 +0000 | [diff] [blame] | 463 | int status = 0; |
| 464 | pcm_more_callback_type2 more_ready; |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 465 | |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 466 | DSR1 = 1; /* Clear interrupt */ |
| 467 | and_l(~DMA_EEXT, &DCR1); /* Disable peripheral request */ |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 468 | |
| 469 | if (res & 0x70) |
| 470 | { |
Michael Sevakis | 51189b4 | 2006-12-06 08:34:55 +0000 | [diff] [blame] | 471 | status = DMA_REC_ERROR_DMA; |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 472 | logf("DMA1 err: %02x", res); |
| 473 | #if 0 |
| 474 | logf(" SAR1: %08x", SAR1); |
| 475 | logf(" DAR1: %08x", DAR1); |
| 476 | logf(" BCR1: %08x", BCR1); |
| 477 | logf(" DCR1: %08x", DCR1); |
| 478 | #endif |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 479 | } |
Michael Sevakis | 8f659ae | 2007-05-20 20:26:36 +0000 | [diff] [blame] | 480 | #ifdef HAVE_SPDIF_REC |
Michael Sevakis | ab1861a | 2006-11-23 19:21:15 +0000 | [diff] [blame] | 481 | else if (DATAINCONTROL == 0xc038 && |
Michael Sevakis | 521d6a5 | 2007-04-18 02:07:56 +0000 | [diff] [blame] | 482 | (INTERRUPTSTAT & ((1 << 24) | (1 << 23) | (1 << 22)))) |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 483 | { |
Michael Sevakis | 521d6a5 | 2007-04-18 02:07:56 +0000 | [diff] [blame] | 484 | /* reason: valnogood, symbolerr, parityerr */ |
| 485 | /* clear: ebu1cnew, valnogood, symbolerr, parityerr */ |
| 486 | INTERRUPTCLEAR = (1 << 25) | (1 << 24) | (1 << 23) | (1 << 22); |
Michael Sevakis | 51189b4 | 2006-12-06 08:34:55 +0000 | [diff] [blame] | 487 | status = DMA_REC_ERROR_SPDIF; |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 488 | logf("spdif err"); |
| 489 | } |
| 490 | #endif |
| 491 | |
| 492 | more_ready = pcm_callback_more_ready; |
| 493 | |
Michael Sevakis | 51189b4 | 2006-12-06 08:34:55 +0000 | [diff] [blame] | 494 | if (more_ready != NULL && more_ready(status) >= 0) |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 495 | return; |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 496 | |
Michael Sevakis | 51189b4 | 2006-12-06 08:34:55 +0000 | [diff] [blame] | 497 | #if 0 |
| 498 | /* int. logfs can trash the display */ |
| 499 | logf("DMA1 done:%04x %d", res, status); |
| 500 | #endif |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 501 | /* Finished recording */ |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 502 | pcm_rec_dma_stop(); |
| 503 | /* Inform PCM that we're done */ |
| 504 | pcm_rec_dma_stopped_callback(); |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 505 | } /* DMA1 */ |
| 506 | |
Michael Sevakis | 633f388 | 2007-03-07 06:23:02 +0000 | [diff] [blame] | 507 | /* Continue transferring data in - call from interrupt callback */ |
Michael Sevakis | 51189b4 | 2006-12-06 08:34:55 +0000 | [diff] [blame] | 508 | void pcm_record_more(void *start, size_t size) |
| 509 | { |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 510 | pcm_rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */ |
| 511 | DAR1 = (unsigned long)start; /* Destination address */ |
| 512 | BCR1 = (unsigned long)size; /* Bytes to transfer */ |
Michael Sevakis | 8867d00 | 2007-03-05 08:14:27 +0000 | [diff] [blame] | 513 | or_l(DMA_EEXT, &DCR1); /* Enable peripheral request */ |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 514 | } /* pcm_record_more */ |
Michael Sevakis | 51189b4 | 2006-12-06 08:34:55 +0000 | [diff] [blame] | 515 | |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 516 | const void * pcm_rec_dma_get_peak_buffer(int *count) |
Michael Sevakis | 5efee7c | 2006-11-06 18:18:05 +0000 | [diff] [blame] | 517 | { |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 518 | unsigned long addr = (unsigned long)pcm_rec_peak_addr; |
| 519 | unsigned long end = DAR1; |
| 520 | addr >>= 2; |
| 521 | *count = (end >> 2) - addr; |
| 522 | return (void *)(addr << 2); |
| 523 | } /* pcm_rec_dma_get_peak_buffer */ |