Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

pcm: introduce pcm_sink

move target-specific pcm operations into builtin_pcm_sink.
in subsequent commits, another pcm_sink is added, and it becomes
possible to switch between them.

Change-Id: I8f8b9661e01d6e6472f34224ddc3760856778457

mojyack dfa33c24 99317811

+840 -418
+1
apps/plugin.c
··· 647 647 pcm_apply_settings, 648 648 pcm_play_lock, 649 649 pcm_play_unlock, 650 + pcm_current_sink_caps, 650 651 beep_play, 651 652 #ifdef HAVE_RECORDING 652 653 &rec_freq_sampr[0],
+2
apps/plugin.h
··· 93 93 #include "misc.h" 94 94 #include "pathfuncs.h" 95 95 #include "pcm_mixer.h" 96 + #include "pcm_sink.h" 96 97 #include "dsp-util.h" 97 98 #include "dsp_core.h" 98 99 #include "dsp_proc_settings.h" ··· 745 746 void (*pcm_apply_settings)(void); 746 747 void (*pcm_play_lock)(void); 747 748 void (*pcm_play_unlock)(void); 749 + const struct pcm_sink_caps* (*pcm_current_sink_caps)(void); 748 750 void (*beep_play)(unsigned int frequency, unsigned int duration, 749 751 unsigned int amplitude); 750 752 #ifdef HAVE_RECORDING
+4
docs/PLUGIN_API
··· 1783 1783 \group sound 1784 1784 \description 1785 1785 1786 + const struct pcm_sink_caps* pcm_current_sink_caps(enum pcm_sink_ids sink) 1787 + \group sound 1788 + \description 1789 + 1786 1790 void pcm_record_data(pcm_rec_callback_type more_ready, pcm_status_callback_type status_cb, void *start, size_t size) 1787 1791 \group sound 1788 1792 \conditions (defined(HAVE_RECORDING))
+5 -13
firmware/export/pcm-internal.h
··· 22 22 #ifndef PCM_INTERNAL_H 23 23 #define PCM_INTERNAL_H 24 24 25 + #include <stdbool.h> 26 + 25 27 #include "config.h" 28 + #include "pcm.h" 29 + #include "pcm_sink.h" 26 30 #include "gcc_extensions.h" /* for FORCE_INLINE */ 27 31 28 32 #ifdef HAVE_SW_VOLUME_CONTROL ··· 129 133 bool pcm_play_dma_complete_callback(enum pcm_dma_status status, 130 134 const void **addr, size_t *size); 131 135 132 - extern unsigned long pcm_curr_sampr; 133 - extern unsigned long pcm_sampr; 134 - extern int pcm_fsel; 135 - 136 136 #ifdef HAVE_PCM_DMA_ADDRESS 137 137 void * pcm_dma_addr(void *addr); 138 138 #endif 139 139 140 140 extern volatile bool pcm_playing; 141 - 142 - void pcm_play_dma_lock(void); 143 - void pcm_play_dma_unlock(void); 144 - void pcm_play_dma_init(void) INIT_ATTR; 145 - void pcm_play_dma_postinit(void); 146 - void pcm_play_dma_start(const void *addr, size_t size); 147 - void pcm_play_dma_stop(void); 148 - 149 - void pcm_dma_apply_settings(void); 141 + struct pcm_sink* pcm_get_current_sink(void); 150 142 151 143 #ifdef HAVE_RECORDING 152 144
+6
firmware/export/pcm.h
··· 71 71 void pcm_postinit(void); 72 72 bool pcm_is_initialized(void); 73 73 74 + enum pcm_sink_ids pcm_current_sink(void); 75 + const struct pcm_sink_caps* pcm_sink_caps(enum pcm_sink_ids sink); 76 + 77 + /* shortcut for plugins */ 78 + const struct pcm_sink_caps* pcm_current_sink_caps(void); 79 + 74 80 /* This is for playing "raw" PCM data */ 75 81 void pcm_play_data(pcm_play_callback_type get_more, 76 82 pcm_status_callback_type status_cb,
+57
firmware/export/pcm_sink.h
··· 1 + /*************************************************************************** 2 + * __________ __ ___. 3 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 + * \/ \/ \/ \/ \/ 8 + * 9 + * Copyright (C) 2025 by Sho Tanimoto 10 + * 11 + * This program is free software; you can redistribute it and/or 12 + * modify it under the terms of the GNU General Public License 13 + * as published by the Free Software Foundation; either version 2 14 + * of the License, or (at your option) any later version. 15 + * 16 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 17 + * KIND, either express or implied. 18 + * 19 + ****************************************************************************/ 20 + #pragma once 21 + #include <stddef.h> 22 + #include <stdint.h> 23 + 24 + struct pcm_sink_caps { 25 + const unsigned long* samprs; 26 + uint16_t num_samprs; 27 + uint16_t default_freq; 28 + }; 29 + 30 + struct pcm_sink_ops { 31 + void (*init)(void); 32 + void (*postinit)(void); 33 + void (*set_freq)(uint16_t freq); 34 + void (*lock)(void); 35 + void (*unlock)(void); 36 + void (*play)(const void* addr, size_t size); 37 + void (*stop)(void); 38 + }; 39 + 40 + struct pcm_sink { 41 + /* characteristics */ 42 + const struct pcm_sink_caps caps; 43 + 44 + /* operations */ 45 + const struct pcm_sink_ops ops; 46 + 47 + /* runtime states */ 48 + unsigned long pending_freq; 49 + unsigned long configured_freq; 50 + }; 51 + 52 + enum pcm_sink_ids { 53 + PCM_SINK_BUILTIN = 0, 54 + }; 55 + 56 + /* defined in each platform pcm source */ 57 + extern struct pcm_sink builtin_pcm_sink;
+95 -59
firmware/pcm.c
··· 42 42 * Semi-private - 43 43 * pcm_play_dma_complete_callback 44 44 * pcm_play_dma_status_callback 45 - * pcm_play_dma_init 46 - * pcm_play_dma_postinit 47 - * pcm_play_dma_start 48 - * pcm_play_dma_stop 45 + * pcm_get_current_sink 46 + * pcm_sink.init 47 + * pcm_sink.postinit 48 + * pcm_sink.play 49 + * pcm_sink.stop 49 50 * Data Read/Written within TSP - 50 - * pcm_sampr (R) 51 - * pcm_fsel (R) 52 - * pcm_curr_sampr (R) 53 51 * pcm_playing (R) 54 52 * 55 53 * ==Playback/Recording== 56 54 * Public - 57 55 * pcm_dma_addr 58 56 * Semi-private - 59 - * pcm_dma_apply_settings 57 + * pcm_sink.set_freq 60 58 * 61 59 * ==Recording== 62 60 * Public - ··· 80 78 81 79 /* 'true' when all stages of pcm initialization have completed */ 82 80 static bool pcm_is_ready = false; 81 + 82 + static struct pcm_sink* sinks[1] = { 83 + [PCM_SINK_BUILTIN] = &builtin_pcm_sink, 84 + }; 85 + static enum pcm_sink_ids cur_sink = PCM_SINK_BUILTIN; 86 + static struct mutex sink_mutex; /* protects sinks and cur_sink */ 83 87 84 88 /* The registered callback function to ask for more mp3 data */ 85 89 volatile pcm_play_callback_type ··· 89 93 pcm_play_status_callback SHAREDBSS_ATTR = NULL; 90 94 /* PCM playback state */ 91 95 volatile bool pcm_playing SHAREDBSS_ATTR = false; 92 - /* samplerate of currently playing audio - undefined if stopped */ 93 - unsigned long pcm_curr_sampr SHAREDBSS_ATTR = 0; 94 - /* samplerate waiting to be set */ 95 - unsigned long pcm_sampr SHAREDBSS_ATTR = HW_SAMPR_DEFAULT; 96 - /* samplerate frequency selection index */ 97 - int pcm_fsel SHAREDBSS_ATTR = HW_FREQ_DEFAULT; 98 96 99 - static void pcm_play_data_start_int(const void *addr, size_t size); 100 97 void pcm_play_stop_int(void); 101 98 99 + struct pcm_sink* pcm_get_current_sink(void) 100 + { 101 + return sinks[cur_sink]; 102 + } 103 + 102 104 #if !defined(HAVE_SW_VOLUME_CONTROL) || defined(PCM_SW_VOLUME_UNBUFFERED) 103 105 /** Standard hw volume/unbuffered control functions - otherwise, see 104 106 ** pcm_sw_volume.c **/ ··· 108 110 /* Smoothed transition might not have happened so sync now */ 109 111 pcm_sync_pcm_factors(); 110 112 #endif 111 - pcm_play_dma_start(addr, size); 113 + sinks[cur_sink]->ops.play(addr, size); 112 114 } 113 115 114 116 static inline void pcm_play_dma_stop_int(void) 115 117 { 116 - pcm_play_dma_stop(); 118 + sinks[cur_sink]->ops.stop(); 117 119 } 118 120 119 121 bool pcm_play_dma_complete_callback(enum pcm_dma_status status, ··· 123 125 if (status < PCM_DMAST_OK) 124 126 status = pcm_play_dma_status_callback(status); 125 127 126 - if (status >= PCM_DMAST_OK && pcm_get_more_int(addr, size)) 128 + if (status >= PCM_DMAST_OK && pcm_get_more_int(addr, size)) { 127 129 return true; 130 + } 128 131 129 132 /* Error, callback missing or no more DMA to do */ 130 133 pcm_play_stop_int(); ··· 132 135 } 133 136 #endif /* !HAVE_SW_VOLUME_CONTROL || PCM_SW_VOLUME_UNBUFFERED */ 134 137 135 - static void pcm_play_data_start_int(const void *addr, size_t size) 136 - { 137 - ALIGN_AUDIOBUF(addr, size); 138 - 139 - if ((addr && size) || pcm_get_more_int(&addr, &size)) 140 - { 141 - pcm_apply_settings(); 142 - logf(" pcm_play_dma_start_int"); 143 - pcm_play_dma_start_int(addr, size); 144 - pcm_playing = true; 145 - } 146 - else 147 - { 148 - /* Force a stop */ 149 - logf(" pcm_play_stop_int"); 150 - pcm_play_stop_int(); 151 - } 152 - } 153 - 154 138 void pcm_play_stop_int(void) 155 139 { 156 140 pcm_play_dma_stop_int(); ··· 223 207 224 208 if (active) 225 209 { 226 - int framecount = peaks->period*pcm_curr_sampr / HZ; 210 + struct pcm_sink* sink = sinks[cur_sink]; 211 + if (sink->configured_freq == -1UL) 212 + { 213 + logf("not configured yet"); 214 + return; 215 + } 216 + 217 + unsigned long sampr = sink->caps.samprs[sink->configured_freq]; 218 + int framecount = peaks->period * sampr / HZ; 227 219 count = MIN(framecount, count); 228 220 229 221 if (count > 0) ··· 247 239 * interface 248 240 */ 249 241 242 + void pcm_play_lock(void) { 243 + mutex_lock(&sink_mutex); 244 + sinks[cur_sink]->ops.lock(); 245 + /* hold sink_mutex until pcm_play_unlock() */ 246 + } 247 + 248 + void pcm_play_unlock(void) { 249 + sinks[cur_sink]->ops.unlock(); 250 + mutex_unlock(&sink_mutex); 251 + } 252 + 250 253 /* This should only be called at startup before any audio playback or 251 254 recording is attempted */ 252 255 void pcm_init(void) 253 256 { 254 257 logf("pcm_init"); 255 258 256 - pcm_set_frequency(HW_SAMPR_DEFAULT); 257 - 258 - logf(" pcm_play_dma_init"); 259 - pcm_play_dma_init(); 259 + mutex_init(&sink_mutex); 260 + for(size_t i = 0; i < ARRAYLEN(sinks); i += 1) { 261 + sinks[i]->pending_freq = sinks[i]->caps.default_freq; 262 + sinks[i]->configured_freq = -1UL; 263 + sinks[i]->ops.init(); 264 + } 260 265 } 261 266 262 267 /* Finish delayed init */ ··· 264 269 { 265 270 logf("pcm_postinit"); 266 271 267 - logf(" pcm_play_dma_postinit"); 268 - 269 - pcm_play_dma_postinit(); 272 + for(size_t i = 0; i < ARRAYLEN(sinks); i += 1) { 273 + sinks[i]->ops.postinit(); 274 + } 270 275 271 276 pcm_is_ready = true; 272 277 } ··· 276 281 return pcm_is_ready; 277 282 } 278 283 284 + enum pcm_sink_ids pcm_current_sink(void) 285 + { 286 + return cur_sink; 287 + } 288 + 289 + const struct pcm_sink_caps* pcm_sink_caps(enum pcm_sink_ids sink) 290 + { 291 + return &sinks[sink]->caps; 292 + } 293 + 294 + const struct pcm_sink_caps* pcm_current_sink_caps(void) 295 + { 296 + return pcm_sink_caps(pcm_current_sink()); 297 + } 298 + 279 299 void pcm_play_data(pcm_play_callback_type get_more, 280 300 pcm_status_callback_type status_cb, 281 301 const void *start, size_t size) ··· 287 307 pcm_callback_for_more = get_more; 288 308 pcm_play_status_callback = status_cb; 289 309 290 - logf(" pcm_play_data_start_int"); 291 - pcm_play_data_start_int(start, size); 310 + ALIGN_AUDIOBUF(start, size); 311 + if ((start && size) || pcm_get_more_int(&start, &size)) 312 + { 313 + pcm_apply_settings(); 314 + logf(" pcm_play_dma_start_int"); 315 + pcm_play_dma_start_int(start, size); 316 + pcm_playing = true; 317 + } 318 + else 319 + { 320 + /* Force a stop */ 321 + logf(" pcm_play_stop_int"); 322 + pcm_play_stop_int(); 323 + } 292 324 293 325 pcm_play_unlock(); 294 326 } ··· 329 361 samplerate = pcm_sampr_to_hw_sampr(samplerate, type); 330 362 #endif /* CONFIG_SAMPR_TYPES */ 331 363 332 - index = round_value_to_list32(samplerate, hw_freq_sampr, 333 - HW_NUM_FREQ, false); 364 + mutex_lock(&sink_mutex); 365 + struct pcm_sink* sink = sinks[cur_sink]; 366 + index = round_value_to_list32(samplerate, sink->caps.samprs, sink->caps.num_samprs, false); 334 367 335 - if (samplerate != hw_freq_sampr[index]) 336 - index = HW_FREQ_DEFAULT; /* Invalid = default */ 368 + if (samplerate != sink->caps.samprs[index]) 369 + index = sink->caps.default_freq; /* Invalid = default */ 337 370 338 - pcm_sampr = hw_freq_sampr[index]; 339 - pcm_fsel = index; 371 + sink->pending_freq = index; 372 + mutex_unlock(&sink_mutex); 340 373 } 341 374 342 375 /* return last-set frequency */ 343 376 unsigned int pcm_get_frequency(void) 344 377 { 345 - return pcm_sampr; 378 + struct pcm_sink* sink = sinks[cur_sink]; 379 + return sink->caps.samprs[sink->pending_freq]; 346 380 } 347 381 348 382 /* apply pcm settings to the hardware */ ··· 352 386 353 387 pcm_wait_for_init(); 354 388 355 - if (pcm_sampr != pcm_curr_sampr) 356 - { 357 - logf(" pcm_dma_apply_settings"); 358 - pcm_dma_apply_settings(); 359 - pcm_curr_sampr = pcm_sampr; 389 + mutex_lock(&sink_mutex); 390 + struct pcm_sink* sink = sinks[cur_sink]; 391 + if(sink->pending_freq != sink->configured_freq) { 392 + logf(" sink->set_freq"); 393 + sink->ops.set_freq(sink->pending_freq); 394 + sink->configured_freq = sink->pending_freq; 360 395 } 396 + mutex_unlock(&sink_mutex); 361 397 } 362 398 363 399 #ifdef HAVE_RECORDING
+2 -2
firmware/pcm_sw_volume.c
··· 325 325 pcm_play_dma_status_callback(PCM_DMAST_STARTED); 326 326 pcm_play_dma_status_callback(PCM_DMAST_STARTED); 327 327 328 - pcm_play_dma_start(pcm_dbl_buf[1], pcm_dbl_buf_size[1]); 328 + pcm_get_current_sink()->ops.play(pcm_dbl_buf[1], pcm_dbl_buf_size[1]); 329 329 } 330 330 331 331 void pcm_play_dma_start_int(const void *addr, size_t size) ··· 338 338 339 339 void pcm_play_dma_stop_int(void) 340 340 { 341 - pcm_play_dma_stop(); 341 + pcm_get_current_sink()->ops.stop(); 342 342 src_buf_addr = NULL; 343 343 src_buf_rem = 0; 344 344 }
+32 -25
firmware/target/arm/as3525/pcm-as3525.c
··· 31 31 #include "mmu-arm.h" 32 32 #include "cpucache-arm.h" 33 33 #include "pcm-internal.h" 34 + #include "pcm_sink.h" 34 35 35 36 #define MAX_TRANSFER (4*((1<<11)-1)) /* maximum data we can transfer via DMA 36 37 * i.e. 32 bits at once (size of I2SO_DATA) ··· 53 54 #endif 54 55 55 56 /* Mask the DMA interrupt */ 56 - void pcm_play_lock(void) 57 + static void sink_lock(void) 57 58 { 58 59 ++locked; 59 60 } 60 61 61 62 /* Unmask the DMA interrupt if enabled */ 62 - void pcm_play_unlock(void) 63 + static void sink_unlock(void) 63 64 { 64 65 if(--locked == 0 && is_playing) 65 66 { ··· 119 120 } 120 121 } 121 122 122 - void pcm_play_dma_start(const void *addr, size_t size) 123 + static void sink_dma_start(const void *addr, size_t size) 123 124 { 124 125 is_playing = true; 125 126 ··· 136 137 play_start_pcm(); 137 138 } 138 139 139 - void pcm_play_dma_stop(void) 140 + static void sink_dma_stop(void) 140 141 { 141 142 is_playing = false; 142 143 ··· 153 154 play_callback_pending = false; 154 155 } 155 156 156 - void pcm_play_dma_init(void) 157 - { 158 - bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); 159 - I2SOUT_CONTROL = (1<<6) | (1<<3); /* enable dma, stereo */ 160 - 161 - audiohw_preinit(); 162 - pcm_dma_apply_settings(); 163 - } 164 - 165 - void pcm_play_dma_postinit(void) 166 - { 167 - audiohw_postinit(); 168 - } 169 - 170 157 /* divider is 9 bits but the highest one (for 8kHz) fit in 8 bits */ 171 158 static const unsigned char divider[SAMPR_NUM_FREQ] = { 172 159 [HW_FREQ_96] = ((AS3525_MCLK_FREQ/128 + SAMPR_96/2) / SAMPR_96) - 1, ··· 183 170 [HW_FREQ_8 ] = ((AS3525_MCLK_FREQ/128 + SAMPR_8 /2) / SAMPR_8 ) - 1, 184 171 }; 185 172 186 - static inline unsigned char mclk_divider(void) 187 - { 188 - return divider[pcm_fsel]; 189 - } 190 - 191 - void pcm_dma_apply_settings(void) 173 + static void sink_set_freq(uint16_t freq) 192 174 { 193 175 bitmod32(&CGU_AUDIO, 194 176 (0<<24) | /* I2SI_MCLK2PAD_EN = disabled */ ··· 196 178 (0<<14) | /* I2SI_MCLK_DIV_SEL = unused */ 197 179 (0<<12) | /* I2SI_MCLK_SEL = clk_main */ 198 180 (1<<11) | /* I2SO_MCLK_EN */ 199 - (mclk_divider() << 2) | /* I2SO_MCLK_DIV_SEL */ 181 + (divider[freq] << 2) | /* I2SO_MCLK_DIV_SEL */ 200 182 (AS3525_MCLK_SEL << 0), /* I2SO_MCLK_SEL */ 201 183 0x01ffffff); 202 184 } 203 185 186 + static void sink_dma_init(void) 187 + { 188 + bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); 189 + I2SOUT_CONTROL = (1<<6) | (1<<3); /* enable dma, stereo */ 190 + 191 + audiohw_preinit(); 192 + sink_set_freq(HW_SAMPR_DEFAULT); 193 + } 194 + 204 195 #ifdef HAVE_PCM_DMA_ADDRESS 205 196 void * pcm_dma_addr(void *addr) 206 197 { ··· 210 201 } 211 202 #endif 212 203 204 + struct pcm_sink builtin_pcm_sink = { 205 + .caps = { 206 + .samprs = hw_freq_sampr, 207 + .num_samprs = HW_NUM_FREQ, 208 + .default_freq = HW_FREQ_DEFAULT, 209 + }, 210 + .ops = { 211 + .init = sink_dma_init, 212 + .postinit = audiohw_postinit, 213 + .set_freq = sink_set_freq, 214 + .lock = sink_lock, 215 + .unlock = sink_unlock, 216 + .play = sink_dma_start, 217 + .stop = sink_dma_stop, 218 + }, 219 + }; 213 220 214 221 /**************************************************************************** 215 222 ** Recording DMA transfer
+34 -20
firmware/target/arm/imx233/pcm-imx233.c
··· 25 25 #include "dma-imx233.h" 26 26 #include "pcm-internal.h" 27 27 #include "audioout-imx233.h" 28 + #include "pcm_sampr.h" 29 + #include "pcm_sink.h" 28 30 29 31 struct pcm_dma_command_t 30 32 { ··· 47 49 static size_t dac_size; /* remaining size */ 48 50 49 51 /* for both recording and playback: maximum transfer size, see 50 - * pcm_dma_apply_settings */ 52 + * sink_set_sampr */ 51 53 static size_t dma_max_size = CACHEALIGN_UP(1600); 52 54 53 55 enum ··· 110 112 imx233_dma_clear_channel_interrupt(APB_AUDIO_DAC); 111 113 } 112 114 113 - void pcm_play_lock(void) 115 + static void sink_lock(void) 114 116 { 115 117 if(dac_locked++ == 0) 116 118 imx233_dma_enable_channel_interrupt(APB_AUDIO_DAC, false); 117 119 } 118 120 119 - void pcm_play_unlock(void) 121 + static void sink_unlock(void) 120 122 { 121 123 if(--dac_locked == 0) 122 124 imx233_dma_enable_channel_interrupt(APB_AUDIO_DAC, true); 123 125 } 124 126 125 - void pcm_play_dma_stop(void) 127 + static void sink_dma_stop(void) 126 128 { 127 129 /* do not interrupt the current transaction because resetting the dma 128 130 * would halt the DAC and clearing RUN causes sound havoc so simply 129 131 * wait for the end of transfer */ 130 - pcm_play_lock(); 132 + sink_lock(); 131 133 dac_buf = NULL; 132 134 dac_size = 0; 133 135 dac_state = DAC_STOP_PENDING; 134 - pcm_play_unlock(); 136 + sink_unlock(); 135 137 } 136 138 137 - void pcm_play_dma_start(const void *addr, size_t size) 139 + static void sink_dma_start(const void *addr, size_t size) 138 140 { 139 - pcm_play_lock(); 141 + sink_lock(); 140 142 /* update pending buffer */ 141 143 dac_buf = addr; 142 144 dac_size = size; ··· 145 147 play(); 146 148 else 147 149 dac_state = DAC_PLAYING; 148 - pcm_play_unlock(); 150 + sink_unlock(); 149 151 } 150 152 151 - void pcm_play_dma_init(void) 152 - { 153 - audiohw_preinit(); 154 - } 155 - 156 - void pcm_play_dma_postinit(void) 153 + static void sink_dma_postinit(void) 157 154 { 158 155 audiohw_postinit(); 159 156 imx233_icoll_enable_interrupt(INT_SRC_DAC_DMA, true); ··· 162 159 imx233_dma_enable_channel_interrupt(APB_AUDIO_DAC, true); 163 160 } 164 161 165 - void pcm_dma_apply_settings(void) 162 + static void sink_set_freq(uint16_t freq) 166 163 { 167 - pcm_play_lock(); 164 + sink_lock(); 168 165 /* update frequency */ 169 - audiohw_set_frequency(pcm_fsel); 166 + audiohw_set_frequency(freq); 170 167 /* compute maximum transfer size: aim at ~1/100s stop time maximum, make sure 171 168 * the resulting value is a multiple of cache line. At sample rate F we 172 169 * transfer two samples (2 x 2 bytes) F times per second = 4F b/s */ 173 - dma_max_size = CACHEALIGN_UP(4 * pcm_sampr / 100); 174 - pcm_play_unlock(); 170 + dma_max_size = CACHEALIGN_UP(4 * hw_freq_sampr[freq] / 100); 171 + sink_unlock(); 175 172 } 173 + 174 + struct pcm_sink builtin_pcm_sink = { 175 + .caps = { 176 + .samprs = hw_freq_sampr, 177 + .num_samprs = HW_NUM_FREQ, 178 + .default_freq = HW_FREQ_DEFAULT, 179 + }, 180 + .ops = { 181 + .init = audiohw_preinit, 182 + .postinit = sink_dma_postinit, 183 + .set_freq = sink_set_freq, 184 + .lock = sink_lock, 185 + .unlock = sink_unlock, 186 + .play = sink_dma_start, 187 + .stop = sink_dma_stop, 188 + }, 189 + }; 176 190 177 191 /* 178 192 * Recording
+25 -12
firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
··· 26 26 #include "sdma-imx31.h" 27 27 #include "mmu-imx31.h" 28 28 #include "pcm-internal.h" 29 + #include "pcm_sink.h" 29 30 30 31 #define DMA_PLAY_CH_NUM 2 31 32 #define DMA_REC_CH_NUM 1 ··· 112 113 } 113 114 } 114 115 115 - void pcm_play_lock(void) 116 + static void sink_lock(void) 116 117 { 117 118 ++dma_play_data.locked; 118 119 } 119 120 120 - void pcm_play_unlock(void) 121 + static void sink_unlock(void) 121 122 { 122 123 if (--dma_play_data.locked == 0 && dma_play_data.state != 0) 123 124 { ··· 131 132 } 132 133 } 133 134 134 - void pcm_dma_apply_settings(void) 135 + static void sink_set_freq(uint16_t freq) 135 136 { 136 - audiohw_set_frequency(pcm_fsel); 137 + audiohw_set_frequency(freq); 137 138 } 138 139 139 - void pcm_play_dma_init(void) 140 + static void sink_dma_init(void) 140 141 { 141 142 /* Init DMA channel information */ 142 143 sdma_channel_init(DMA_PLAY_CH_NUM, &dma_play_cd, &dma_play_bd); ··· 144 145 145 146 /* Init audio interfaces */ 146 147 audiohw_init(); 147 - } 148 - 149 - void pcm_play_dma_postinit(void) 150 - { 151 - audiohw_postinit(); 152 148 } 153 149 154 150 static void play_start_pcm(void) ··· 200 196 dma_play_data.callback_pending = 0; 201 197 } 202 198 203 - void pcm_play_dma_start(const void *addr, size_t size) 199 + static void sink_dma_start(const void *addr, size_t size) 204 200 { 205 201 sdma_channel_stop(DMA_PLAY_CH_NUM); 206 202 ··· 218 214 play_start_dma(addr, size); 219 215 } 220 216 221 - void pcm_play_dma_stop(void) 217 + static void sink_dma_stop(void) 222 218 { 223 219 sdma_channel_stop(DMA_PLAY_CH_NUM); 224 220 play_stop_pcm(); ··· 228 224 { 229 225 return (void *)addr_virt_to_phys((unsigned long)addr); 230 226 } 227 + 228 + struct pcm_sink builtin_pcm_sink = { 229 + .caps = { 230 + .samprs = hw_freq_sampr, 231 + .num_samprs = HW_NUM_FREQ, 232 + .default_freq = HW_FREQ_DEFAULT, 233 + }, 234 + .ops = { 235 + .init = sink_dma_init, 236 + .postinit = audiohw_postinit, 237 + .set_freq = sink_set_freq, 238 + .lock = sink_lock, 239 + .unlock = sink_unlock, 240 + .play = sink_dma_start, 241 + .stop = sink_dma_stop, 242 + }, 243 + }; 231 244 232 245 #ifdef HAVE_RECORDING 233 246 static struct buffer_descriptor dma_rec_bd NOCACHEBSS_ATTR;
+24 -11
firmware/target/arm/pcm-telechips.c
··· 28 28 #include "i2s.h" 29 29 #include "pcm.h" 30 30 #include "pcm-internal.h" 31 + #include "pcm_sink.h" 31 32 32 33 struct dma_data 33 34 { ··· 62 63 .state = 0 63 64 }; 64 65 65 - void pcm_play_dma_init(void) 66 + static void sink_dma_init(void) 66 67 { 67 68 DAVC = 0x0; /* Digital Volume = max */ 68 69 #ifdef COWON_D2 ··· 89 90 #endif 90 91 } 91 92 92 - void pcm_play_dma_postinit(void) 93 - { 94 - audiohw_postinit(); 95 - } 96 - 97 - void pcm_dma_apply_settings(void) 93 + static void sink_set_freq(uint16_t freq) 98 94 { 99 95 } 100 96 ··· 125 121 dma_play_data.state = 0; 126 122 } 127 123 128 - void pcm_play_dma_start(const void *addr, size_t size) 124 + static void sink_dma_start(const void *addr, size_t size) 129 125 { 130 126 dma_play_data.p_r = addr; 131 127 dma_play_data.size = size; ··· 140 136 play_start_pcm(); 141 137 } 142 138 143 - void pcm_play_dma_stop(void) 139 + static void sink_dma_stop(void) 144 140 { 145 141 play_stop_pcm(); 146 142 dma_play_data.size = 0; ··· 149 145 #endif 150 146 } 151 147 152 - void pcm_play_lock(void) 148 + static void sink_lock(void) 153 149 { 154 150 int status = disable_fiq_save(); 155 151 ··· 161 157 restore_fiq(status); 162 158 } 163 159 164 - void pcm_play_unlock(void) 160 + static void sink_unlock(void) 165 161 { 166 162 int status = disable_fiq_save(); 167 163 ··· 172 168 173 169 restore_fiq(status); 174 170 } 171 + 172 + struct pcm_sink builtin_pcm_sink = { 173 + .caps = { 174 + .samprs = hw_freq_sampr, 175 + .num_samprs = HW_NUM_FREQ, 176 + .default_freq = HW_FREQ_DEFAULT, 177 + }, 178 + .ops = { 179 + .init = sink_dma_init, 180 + .postinit = audiohw_postinit, 181 + .set_freq = sink_set_freq, 182 + .lock = sink_lock, 183 + .unlock = sink_unlock, 184 + .play = sink_dma_start, 185 + .stop = sink_dma_stop, 186 + }, 187 + }; 175 188 176 189 #ifdef HAVE_RECORDING 177 190 /* TODO: implement */
+42 -26
firmware/target/arm/pp/pcm-pp.c
··· 27 27 #include "pcm.h" 28 28 #include "pcm_sampr.h" 29 29 #include "pcm-internal.h" 30 + #include "pcm_sink.h" 30 31 31 32 /** DMA **/ 32 33 ··· 89 90 .state = 0 90 91 }; 91 92 92 - void pcm_dma_apply_settings(void) 93 - { 94 - audiohw_set_frequency(pcm_fsel); 95 - } 93 + /* DMA status cannot be viewed from outside code in control because that can 94 + * clear the interrupt from outside the handler and prevent the handler from 95 + * from being called. Split up transfers to a reasonable size that is good as 96 + * a timer and peaking yet still keeps the FIQ count low. 97 + */ 98 + static uint16_t max_dma_chunk_size; 96 99 97 100 #if defined(CPU_PP502x) 98 101 /* 16-bit, L-R packed into 32 bits with left in the least significant halfword */ ··· 102 105 #define DMA_PLAY_CONFIG ((DMA_REQ_IIS << DMA_CMD_REQ_ID_POS) | \ 103 106 DMA_CMD_RAM_TO_PER | DMA_CMD_SINGLE | \ 104 107 DMA_CMD_WAIT_REQ | DMA_CMD_INTR) 105 - /* DMA status cannot be viewed from outside code in control because that can 106 - * clear the interrupt from outside the handler and prevent the handler from 107 - * from being called. Split up transfers to a reasonable size that is good as 108 - * a timer and peaking yet still keeps the FIQ count low. 109 - */ 110 - #define MAX_DMA_CHUNK_SIZE (pcm_curr_sampr >> 6) /* ~1/256 seconds */ 111 108 112 109 static inline void dma_tx_init(void) 113 110 { ··· 145 142 146 143 static inline void dma_tx_start(bool begin) 147 144 { 148 - size_t size = MAX_DMA_CHUNK_SIZE; 145 + size_t size = max_dma_chunk_size; 149 146 150 - /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less 147 + /* Not at least max_dma_chunk_size left or there would be less 151 148 * than a FIFO's worth of data after this transfer? */ 152 149 if (size + 16*4 > dma_play_data.size) 153 150 size = dma_play_data.size; ··· 430 427 #endif /* ASM / C selection */ 431 428 #endif /* CPU_PP502x */ 432 429 430 + static void sink_set_freq(uint16_t freq) { 431 + max_dma_chunk_size = hw_freq_sampr[freq] >> 6; 432 + audiohw_set_frequency(freq); 433 + } 434 + 433 435 /* For the locks, FIQ must be disabled because the handler manipulates 434 436 IISCONFIG and the operation is not atomic - dual core support 435 437 will require other measures */ 436 - void pcm_play_lock(void) 438 + static void sink_lock(void) 437 439 { 438 440 int status = disable_fiq_save(); 439 441 ··· 444 446 restore_fiq(status); 445 447 } 446 448 447 - void pcm_play_unlock(void) 449 + static void sink_unlock(void) 448 450 { 449 451 int status = disable_fiq_save(); 450 452 ··· 455 457 restore_fiq(status); 456 458 } 457 459 458 - static void play_start_pcm(void) 460 + static void sink_start_pcm(void) 459 461 { 460 462 fiq_function = fiq_playback; 461 463 dma_play_data.state = 1; 462 464 dma_tx_start(true); 463 465 } 464 466 465 - static void play_stop_pcm(void) 467 + static void sink_stop_pcm(void) 466 468 { 467 469 dma_tx_stop(); 468 470 ··· 472 474 dma_play_data.state = 0; 473 475 } 474 476 475 - void pcm_play_dma_start(const void *addr, size_t size) 477 + static void sink_dma_stop(void); 478 + 479 + static void sink_dma_start(const void *addr, size_t size) 476 480 { 477 - pcm_play_dma_stop(); 481 + sink_dma_stop(); 478 482 479 483 #if NUM_CORES > 1 480 484 /* This will become more important later - and different ! */ ··· 485 489 486 490 dma_play_data.addr = dma_tx_buf_prepare(addr); 487 491 dma_play_data.size = size; 488 - play_start_pcm(); 492 + sink_start_pcm(); 489 493 } 490 494 491 495 /* Stops the DMA transfer and interrupt */ 492 - void pcm_play_dma_stop(void) 496 + static void sink_dma_stop(void) 493 497 { 494 - play_stop_pcm(); 498 + sink_stop_pcm(); 495 499 dma_play_data.addr = 0; 496 500 dma_play_data.size = 0; 497 501 #if NUM_CORES > 1 ··· 499 503 #endif 500 504 } 501 505 502 - void pcm_play_dma_init(void) 506 + static void sink_dma_init(void) 503 507 { 504 508 /* Initialize default register values. */ 505 509 audiohw_init(); ··· 509 513 IISCONFIG |= IIS_TXFIFOEN; 510 514 } 511 515 512 - void pcm_play_dma_postinit(void) 513 - { 514 - audiohw_postinit(); 515 - } 516 + struct pcm_sink builtin_pcm_sink = { 517 + .caps = { 518 + .samprs = hw_freq_sampr, 519 + .num_samprs = HW_NUM_FREQ, 520 + .default_freq = HW_FREQ_DEFAULT, 521 + }, 522 + .ops = { 523 + .init = sink_dma_init, 524 + .postinit = audiohw_postinit, 525 + .set_freq = sink_set_freq, 526 + .lock = sink_lock, 527 + .unlock = sink_unlock, 528 + .play = sink_dma_start, 529 + .stop = sink_dma_stop, 530 + }, 531 + }; 516 532 517 533 /**************************************************************************** 518 534 ** Recording DMA transfer
+27 -14
firmware/target/arm/rk27xx/pcm-rk27xx.c
··· 28 28 #include "audiohw.h" 29 29 #include "sound.h" 30 30 #include "pcm-internal.h" 31 + #include "pcm_sink.h" 31 32 32 33 static int locked = 0; 33 34 34 35 /* Mask the DMA interrupt */ 35 - void pcm_play_lock(void) 36 + static void sink_lock(void) 36 37 { 37 38 if (++locked == 1) 38 39 { ··· 43 44 } 44 45 45 46 /* Unmask the DMA interrupt if enabled */ 46 - void pcm_play_unlock(void) 47 + static void sink_unlock(void) 47 48 { 48 49 if(--locked == 0) 49 50 { ··· 53 54 } 54 55 } 55 56 56 - void pcm_play_dma_stop(void) 57 + static void sink_dma_stop(void) 57 58 { 58 59 HDMA_CON0 = 0x00; 59 60 HDMA_ISR = 0x00; ··· 105 106 (1<<0)); /* hardware trigger DMA mode */ 106 107 } 107 108 108 - void pcm_play_dma_start(const void *addr, size_t size) 109 + static void sink_dma_start(const void *addr, size_t size) 109 110 { 110 111 /* Stop any DMA in progress */ 111 - pcm_play_dma_stop(); 112 + sink_dma_stop(); 112 113 113 114 /* kick in DMA transfer */ 114 115 hdma_i2s_transfer(addr, size); ··· 225 226 } 226 227 #endif 227 228 228 - void pcm_play_dma_init(void) 229 + static void sink_dma_init(void) 229 230 { 230 231 /* unmask HDMA interrupt in INTC */ 231 232 INTC_IMR |= IRQ_ARM_HDMA; ··· 236 237 i2s_init(); 237 238 } 238 239 239 - void pcm_play_dma_postinit(void) 240 - { 241 - audiohw_postinit(); 242 - } 243 - 244 - void pcm_dma_apply_settings(void) 240 + static void sink_set_freq(uint16_t freq) 245 241 { 246 242 #ifdef CODEC_SLAVE 247 - set_codec_freq(pcm_fsel); 243 + set_codec_freq(freq); 248 244 #endif 249 245 250 - audiohw_set_frequency(pcm_fsel); 246 + audiohw_set_frequency(freq); 251 247 } 252 248 253 249 /* audio DMA ISR called when chunk from callers buffer has been transfered */ ··· 262 258 pcm_play_dma_status_callback(PCM_DMAST_STARTED); 263 259 } 264 260 } 261 + 262 + struct pcm_sink builtin_pcm_sink = { 263 + .caps = { 264 + .samprs = hw_freq_sampr, 265 + .num_samprs = HW_NUM_FREQ, 266 + .default_freq = HW_FREQ_DEFAULT, 267 + }, 268 + .ops = { 269 + .init = sink_dma_init, 270 + .postinit = audiohw_postinit, 271 + .set_freq = sink_set_freq, 272 + .lock = sink_lock, 273 + .unlock = sink_unlock, 274 + .play = sink_dma_start, 275 + .stop = sink_dma_stop, 276 + }, 277 + }; 265 278 266 279 /**************************************************************************** 267 280 ** Recording DMA transfer
+25 -12
firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
··· 26 26 #include "sound.h" 27 27 #include "file.h" 28 28 #include "pcm-internal.h" 29 + #include "pcm_sink.h" 29 30 30 31 /* PCM interrupt routine lockout */ 31 32 static struct ··· 48 49 void fiq_handler(void) __attribute__((interrupt ("FIQ"))); 49 50 50 51 /* Mask the DMA interrupt */ 51 - void pcm_play_lock(void) 52 + static void sink_lock(void) 52 53 { 53 54 if (++dma_play_lock.locked == 1) 54 55 bitset32(&INTMSK, DMA2_MASK); 55 56 } 56 57 57 58 /* Unmask the DMA interrupt if enabled */ 58 - void pcm_play_unlock(void) 59 + static void sink_unlock(void) 59 60 { 60 61 if (--dma_play_lock.locked == 0) 61 62 bitclr32(&INTMSK, dma_play_lock.state); 62 63 } 63 64 64 - void pcm_play_dma_init(void) 65 + static void sink_dma_init(void) 65 66 { 66 67 /* There seem to be problems when changing the IIS interface configuration 67 68 * when a clock is not present. ··· 94 95 bitset32(&INTMOD, DMA2_MASK); 95 96 } 96 97 97 - void pcm_play_dma_postinit(void) 98 + static void sink_set_freq(uint16_t freq) 98 99 { 99 - audiohw_postinit(); 100 - } 101 - 102 - void pcm_dma_apply_settings(void) 103 - { 104 - audiohw_set_frequency(pcm_fsel); 100 + audiohw_set_frequency(freq); 105 101 } 106 102 107 103 /* Connect the DMA and start filling the FIFO */ ··· 158 154 IISCON &= ~(1<<0); 159 155 } 160 156 161 - void pcm_play_dma_start(const void *addr, size_t size) 157 + static void sink_dma_start(const void *addr, size_t size) 162 158 { 163 159 /* Enable the IIS clock */ 164 160 bitset32(&CLKCON, 1<<17); ··· 187 183 } 188 184 189 185 /* Promptly stop DMA transfers and stop IIS */ 190 - void pcm_play_dma_stop(void) 186 + static void sink_dma_stop(void) 191 187 { 192 188 play_stop_pcm(); 193 189 ··· 219 215 220 216 pcm_play_dma_status_callback(PCM_DMAST_STARTED); 221 217 } 218 + 219 + struct pcm_sink builtin_pcm_sink = { 220 + .caps = { 221 + .samprs = hw_freq_sampr, 222 + .num_samprs = HW_NUM_FREQ, 223 + .default_freq = HW_FREQ_DEFAULT, 224 + }, 225 + .ops = { 226 + .init = sink_dma_init, 227 + .postinit = audiohw_postinit, 228 + .set_freq = sink_set_freq, 229 + .lock = sink_lock, 230 + .unlock = sink_unlock, 231 + .play = sink_dma_start, 232 + .stop = sink_dma_stop, 233 + }, 234 + };
+25 -13
firmware/target/arm/s5l8700/pcm-s5l8700.c
··· 32 32 #include "dma-target.h" 33 33 #include "mmu-arm.h" 34 34 #include "cpucache-arm.h" 35 + #include "pcm_sink.h" 35 36 36 37 /* Driver for the IIS/PCM part of the s5l8700 using DMA 37 38 38 39 Notes: 39 - - pcm_play_dma_stop is untested, not sure if implemented the right way 40 + - sink_dma_stop is untested, not sure if implemented the right way 40 41 - recording is not implemented 41 42 */ 42 43 ··· 83 84 }; 84 85 85 86 /* Mask the DMA interrupt */ 86 - void pcm_play_lock(void) 87 + static void sink_lock(void) 87 88 { 88 89 if (locked++ == 0) { 89 90 INTMSK &= ~(1 << 10); ··· 91 92 } 92 93 93 94 /* Unmask the DMA interrupt if enabled */ 94 - void pcm_play_unlock(void) 95 + static void sink_unlock(void) 95 96 { 96 97 if (--locked == 0) { 97 98 INTMSK |= (1 << 10); ··· 141 142 142 143 } 143 144 144 - void pcm_play_dma_start(const void* addr, size_t size) 145 + static void sink_dma_start(const void* addr, size_t size) 145 146 { 146 147 /* DMA channel on */ 147 148 nextbuf = addr; ··· 161 162 (0 << 0); /* 0 = LRCK on */ 162 163 } 163 164 164 - void pcm_play_dma_stop(void) 165 + static void sink_dma_stop(void) 165 166 { 166 167 /* DMA channel off */ 167 168 DMACOM0 = 5; ··· 195 196 (div.cdiv - 1); /* MCLK_DIV_VAL */ 196 197 } 197 198 198 - void pcm_play_dma_init(void) 199 + static void sink_dma_init(void) 199 200 { 200 201 /* configure IIS pins */ 201 202 #ifdef IPOD_NANO2G ··· 252 253 audiohw_preinit(); 253 254 } 254 255 255 - void pcm_play_dma_postinit(void) 256 - { 257 - audiohw_postinit(); 258 - } 259 - 260 256 /* set the configured PCM frequency */ 261 - void pcm_dma_apply_settings(void) 257 + static void sink_set_freq(uint16_t freq) 262 258 { 263 - pcm_dma_set_freq(pcm_fsel); 259 + pcm_dma_set_freq(hw_freq_sampr[freq]); 264 260 } 265 261 266 262 #ifdef HAVE_PCM_DMA_ADDRESS ··· 272 268 } 273 269 #endif 274 270 271 + struct pcm_sink builtin_pcm_sink = { 272 + .caps = { 273 + .samprs = hw_freq_sampr, 274 + .num_samprs = HW_NUM_FREQ, 275 + .default_freq = HW_FREQ_DEFAULT, 276 + }, 277 + .ops = { 278 + .init = sink_dma_init, 279 + .postinit = audiohw_postinit, 280 + .set_freq = sink_set_freq, 281 + .lock = sink_lock, 282 + .unlock = sink_unlock, 283 + .play = sink_dma_start, 284 + .stop = sink_dma_stop, 285 + }, 286 + }; 275 287 276 288 /**************************************************************************** 277 289 ** Recording DMA transfer
+34 -24
firmware/target/arm/s5l8702/pcm-s5l8702.c
··· 31 31 #include "pcm_sampr.h" 32 32 #include "pcm-target.h" 33 33 #include "dma-s5l8702.h" 34 + #include "pcm_sink.h" 34 35 35 36 /* DMA configuration */ 36 37 ··· 86 87 size_t pcm_remaining; 87 88 88 89 /* Mask the DMA interrupt */ 89 - void pcm_play_lock(void) 90 + static void sink_lock(void) 90 91 { 91 92 if (locked++ == 0) 92 93 dmac_ch_lock_int(&dma_play_ch); 93 94 } 94 95 95 96 /* Unmask the DMA interrupt if enabled */ 96 - void pcm_play_unlock(void) 97 + static void sink_unlock(void) 97 98 { 98 99 if (--locked == 0) 99 100 dmac_ch_unlock_int(&dma_play_ch); ··· 141 142 pcm_play_dma_status_callback(PCM_DMAST_STARTED); 142 143 } 143 144 144 - void pcm_play_dma_start(const void* addr, size_t size) 145 + static void sink_dma_stop(void) 146 + { 147 + dmac_ch_stop(&dma_play_ch); 148 + I2STXCOM = 0xa; 149 + } 150 + 151 + static void sink_dma_start(const void* addr, size_t size) 145 152 { 146 - pcm_play_dma_stop(); 153 + sink_dma_stop(); 147 154 148 155 pcm_remaining = size; 149 156 I2STXCOM = 0xe; 150 157 dma_play_callback((void*)addr); 151 158 } 152 159 153 - void pcm_play_dma_stop(void) 154 - { 155 - dmac_ch_stop(&dma_play_ch); 156 - I2STXCOM = 0xa; 157 - } 158 - 159 160 /* MCLK = 12MHz (MCLKDIV2=1), [CS42L55 DS, s4.8] */ 160 161 #define MCLK_FREQ 12000000 161 162 162 163 /* set the configured PCM frequency */ 163 - void pcm_dma_apply_settings(void) 164 + static void sink_set_freq(uint16_t freq) 164 165 { 165 166 static uint16_t last_clkcon3l = 0; 166 167 uint16_t clkcon3l; 167 - int fsel; 168 168 169 169 /* For unknown reasons, s5l8702 I2S controller does not synchronize 170 170 * with CS42L55 at 32000 Hz. To fix it, the CODEC is configured with ··· 172 172 * obtaining 32 KHz in LRCK controller input and 8 MHz in SCLK input. 173 173 * OF uses this trick. 174 174 */ 175 - if (pcm_fsel == HW_FREQ_32) { 176 - fsel = HW_FREQ_48; 175 + if (freq == HW_FREQ_32) { 176 + freq = HW_FREQ_48; 177 177 clkcon3l = 0x3028; /* PLL2 / 3 / 9 -> 8 MHz */ 178 178 } 179 179 else { 180 - fsel = pcm_fsel; 181 180 clkcon3l = 0; /* OSC0 -> 12 MHz */ 182 181 } 183 182 ··· 192 191 } 193 192 194 193 /* configure I2S clock ratio */ 195 - I2SCLKDIV = MCLK_FREQ / hw_freq_sampr[fsel]; 194 + I2SCLKDIV = MCLK_FREQ / hw_freq_sampr[freq]; 196 195 /* select CS42L55 sample rate */ 197 - audiohw_set_frequency(fsel); 196 + audiohw_set_frequency(freq); 198 197 } 199 198 200 - void pcm_play_dma_init(void) 199 + static void sink_dma_init(void) 201 200 { 202 201 PWRCON(1) &= ~(1 << 7); 203 202 ··· 207 206 I2SCLKCON = 1; 208 207 209 208 audiohw_preinit(); 210 - pcm_dma_apply_settings(); 211 - } 212 - 213 - void pcm_play_dma_postinit(void) 214 - { 215 - audiohw_postinit(); 209 + sink_set_freq(HW_FREQ_DEFAULT); 216 210 } 217 211 218 212 #ifdef HAVE_PCM_DMA_ADDRESS ··· 222 216 } 223 217 #endif 224 218 219 + struct pcm_sink builtin_pcm_sink = { 220 + .caps = { 221 + .samprs = hw_freq_sampr, 222 + .num_samprs = HW_NUM_FREQ, 223 + .default_freq = HW_FREQ_DEFAULT, 224 + }, 225 + .ops = { 226 + .init = sink_dma_init, 227 + .postinit = audiohw_postinit, 228 + .set_freq = sink_set_freq, 229 + .lock = sink_lock, 230 + .unlock = sink_unlock, 231 + .play = sink_dma_start, 232 + .stop = sink_dma_stop, 233 + }, 234 + }; 225 235 226 236 /**************************************************************************** 227 237 ** Recording DMA transfer
+27 -7
firmware/target/arm/stm32/pcm-stm32h7.c
··· 20 20 ****************************************************************************/ 21 21 #include "pcm.h" 22 22 #include "pcm-internal.h" 23 + #include "pcm_sampr.h" 24 + #include "pcm_sink.h" 23 25 24 - void pcm_dma_apply_settings(void) 26 + static void sink_set_freq(uint16_t freq) 25 27 { 28 + (void)freq; 26 29 } 27 30 28 - void pcm_play_dma_init(void) 31 + static void sink_dma_init(void) 29 32 { 30 33 } 31 34 32 - void pcm_play_dma_postinit(void) 35 + static void sink_dma_postinit(void) 33 36 { 34 37 } 35 38 36 - void pcm_play_dma_start(const void *addr, size_t size) 39 + static void sink_dma_start(const void *addr, size_t size) 37 40 { 38 41 (void)addr; 39 42 (void)size; 40 43 } 41 44 42 - void pcm_play_dma_stop(void) 45 + static void sink_dma_stop(void) 43 46 { 44 47 } 45 48 46 - void pcm_play_lock(void) 49 + static void sink_lock(void) 47 50 { 48 51 } 49 52 50 - void pcm_play_unlock(void) 53 + static void sink_unlock(void) 51 54 { 52 55 } 56 + 57 + struct pcm_sink builtin_pcm_sink = { 58 + .caps = { 59 + .samprs = hw_freq_sampr, 60 + .num_samprs = HW_NUM_FREQ, 61 + .default_freq = HW_FREQ_DEFAULT, 62 + }, 63 + .ops = { 64 + .init = sink_dma_init, 65 + .postinit = sink_dma_postinit, 66 + .set_freq = sink_set_freq, 67 + .lock = sink_lock, 68 + .unlock = sink_unlock, 69 + .play = sink_dma_start, 70 + .stop = sink_dma_stop, 71 + }, 72 + }; 53 73 54 74 #ifdef HAVE_RECORDING 55 75 void pcm_rec_dma_init(void)
+26 -9
firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c
··· 28 28 #include "dsp-target.h" 29 29 #include "dsp/ipc.h" 30 30 #include "pcm-internal.h" 31 + #include "pcm_sink.h" 31 32 32 33 /* This is global to save some latency when pcm_play_dma_get_peak_buffer is 33 34 * called. 34 35 */ 35 36 static const void *start; 36 37 37 - void pcm_play_dma_postinit(void) 38 + static void sink_dma_postinit(void) 38 39 { 39 40 /* Configure clock divider */ 40 41 tsc2100_writereg(CONTROL_PAGE2, TSPP1_ADDRESS, 0x1120); ··· 45 46 audiohw_postinit(); 46 47 } 47 48 48 - void pcm_play_dma_init(void) 49 + static void sink_dma_init(void) 49 50 { 50 51 IO_INTC_IRQ0 = INTR_IRQ0_IMGBUF; 51 52 bitset16(&IO_INTC_EINT0, INTR_EINT0_IMGBUF); ··· 67 68 dsp_wake(); 68 69 } 69 70 70 - void pcm_dma_apply_settings(void) 71 + static void sink_set_freq(uint16_t freq) 71 72 { 72 - audiohw_set_frequency(pcm_fsel); 73 + audiohw_set_frequency(freq); 73 74 } 74 75 75 76 /* Note that size is actually limited to the size of a short right now due to 76 77 * the implementation on the DSP side (and the way that we access it) 77 78 */ 78 - void pcm_play_dma_start(const void *addr, size_t size) 79 + static void sink_dma_start(const void *addr, size_t size) 79 80 { 80 81 unsigned long sdem_addr=(unsigned long)addr - CONFIG_SDRAM_START; 81 82 /* Initialize codec. */ ··· 87 88 dsp_wake(); 88 89 } 89 90 90 - void pcm_play_dma_stop(void) 91 + static void sink_dma_stop(void) 91 92 { 92 93 DSP_(_dma0_stopped)=1; 93 94 dsp_wake(); 94 95 } 95 96 96 - void pcm_play_lock(void) 97 + static void sink_lock(void) 97 98 { 98 99 99 100 } 100 101 101 - void pcm_play_unlock(void) 102 + static void sink_unlock(void) 102 103 { 103 104 104 105 } ··· 156 157 157 158 DEBUGF("DSP: %s", buffer); 158 159 } 159 - 160 + 161 + struct pcm_sink builtin_pcm_sink = { 162 + .caps = { 163 + .samprs = hw_freq_sampr, 164 + .num_samprs = HW_NUM_FREQ, 165 + .default_freq = HW_FREQ_DEFAULT, 166 + }, 167 + .ops = { 168 + .init = sink_dma_init, 169 + .postinit = sink_dma_postinit, 170 + .set_freq = sink_set_freq, 171 + .lock = sink_lock, 172 + .unlock = sink_unlock, 173 + .play = sink_dma_start, 174 + .stop = sink_dma_stop, 175 + }, 176 + };
+25 -13
firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c
··· 29 29 #include "dsp/ipc.h" 30 30 #include "pcm-internal.h" 31 31 #include "dma-target.h" 32 + #include "pcm_sink.h" 32 33 33 34 /* This is global to save some latency when pcm_play_dma_get_peak_buffer is 34 35 * called. ··· 36 37 static const void *start; 37 38 static int dma_channel; 38 39 39 - void pcm_play_dma_postinit(void) 40 - { 41 - audiohw_postinit(); 42 - } 43 - 44 - void pcm_play_dma_init(void) 40 + static void sink_dma_init(void) 45 41 { 46 42 /* GIO16 is DSP/AIC3X CLK */ 47 43 IO_GIO_FSEL0 &= 0x3FFF; ··· 74 70 dsp_wake(); 75 71 } 76 72 77 - void pcm_dma_apply_settings(void) 73 + static void sink_set_freq(uint16_t freq) 78 74 { 79 - audiohw_set_frequency(pcm_fsel); 75 + audiohw_set_frequency(freq); 80 76 } 81 77 82 78 /* Note that size is actually limited to the size of a short right now due to 83 79 * the implementation on the DSP side (and the way that we access it) 84 80 */ 85 - void pcm_play_dma_start(const void *addr, size_t size) 81 + static void sink_dma_start(const void *addr, size_t size) 86 82 { 87 83 unsigned long sdem_addr=(unsigned long)addr - CONFIG_SDRAM_START; 88 84 /* Initialize codec. */ ··· 94 90 dsp_wake(); 95 91 } 96 92 97 - void pcm_play_dma_stop(void) 93 + static void sink_dma_stop(void) 98 94 { 99 95 DSP_(_dma0_stopped)=1; 100 96 dsp_wake(); 101 97 } 102 98 103 - void pcm_play_lock(void) 99 + static void sink_lock(void) 104 100 { 105 101 106 102 } 107 103 108 - void pcm_play_unlock(void) 104 + static void sink_unlock(void) 109 105 { 110 106 111 107 } ··· 163 159 164 160 DEBUGF("DSP: %s", buffer); 165 161 } 166 - 162 + 163 + struct pcm_sink builtin_pcm_sink = { 164 + .caps = { 165 + .samprs = hw_freq_sampr, 166 + .num_samprs = HW_NUM_FREQ, 167 + .default_freq = HW_FREQ_DEFAULT, 168 + }, 169 + .ops = { 170 + .init = sink_dma_init, 171 + .postinit = audiohw_postinit, 172 + .set_freq = sink_set_freq, 173 + .lock = sink_lock, 174 + .unlock = sink_unlock, 175 + .play = sink_dma_start, 176 + .stop = sink_dma_stop, 177 + }, 178 + };
+44 -26
firmware/target/coldfire/pcm-coldfire.c
··· 29 29 #include "spdif.h" 30 30 #endif 31 31 #include "pcm-internal.h" 32 + #include "pcm_sink.h" 32 33 33 34 #define IIS_PLAY_DEFPARM ( (freq_ent[FPARM_CLOCKSEL] << 12) | \ 34 35 (IIS_PLAY & (7 << 8)) | \ ··· 142 143 /* This clears the reset bit to enable monitoring immediately if monitoring 143 144 recording sources or always if playback is in progress - we might be 144 145 switching samplerates on the fly */ 145 - void pcm_dma_apply_settings(void) 146 + static void sink_set_freq(uint16_t freq) 146 147 { 147 148 int level = set_irq_level(DMA_IRQ_LEVEL); 148 149 149 150 /* remember table entry */ 150 - freq_ent = pcm_freq_parms[pcm_fsel]; 151 + freq_ent = pcm_freq_parms[freq]; 151 152 152 153 /* Reprogramming bits 15-12 requires FIFO to be in a reset 153 154 condition - Users Manual 17-8, Note 11 */ ··· 159 160 IIS_PLAY = IIS_PLAY_DEFPARM | IIS_FIFO_RESET; 160 161 restore_irq(level); 161 162 162 - audiohw_set_frequency(pcm_fsel); 163 + audiohw_set_frequency(freq); 163 164 coldfire_set_pllcr_audio_bits(PLLCR_SET_AUDIO_BITS_DEFPARM); 164 165 165 166 level = set_irq_level(DMA_IRQ_LEVEL); ··· 170 171 PDOR3 = 0; /* Kick FIFO out of reset by writing to it */ 171 172 172 173 restore_irq(level); 173 - } /* pcm_dma_apply_settings */ 174 + } /* sink_set_sampr */ 174 175 175 - void pcm_play_dma_init(void) 176 + static void sink_dma_init(void) 176 177 { 177 - freq_ent = pcm_freq_parms[pcm_fsel]; 178 + freq_ent = pcm_freq_parms[HW_FREQ_DEFAULT]; 178 179 179 180 AUDIOGLOB = AUDIOGLOB_DEFPARM; 180 181 DIVR0 = 54; /* DMA0 is mapped into vector 54 in system.c */ ··· 195 196 196 197 audio_input_mux(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); 197 198 198 - audiohw_set_frequency(pcm_fsel); 199 + audiohw_set_frequency(HW_FREQ_DEFAULT); 199 200 coldfire_set_pllcr_audio_bits(PLLCR_SET_AUDIO_BITS_DEFPARM); 200 201 201 202 #if defined(HAVE_SPDIF_REC) || defined(HAVE_SPDIF_OUT) 202 203 spdif_init(); 203 204 #endif 204 - } /* pcm_play_dma_init */ 205 + } /* sink_dma_init */ 205 206 206 - void pcm_play_dma_postinit(void) 207 + void sink_dma_postinit(void) 207 208 { 208 209 audiohw_postinit(); 209 210 iis_play_reset(); ··· 223 224 .state = (1 << 14) /* bit 14 is DMA0 */ 224 225 }; 225 226 226 - void pcm_play_lock(void) 227 + static void sink_lock(void) 227 228 { 228 229 if (++dma_play_lock.locked == 1) 229 230 coldfire_imr_mod(1 << 14, 1 << 14); 230 231 } 231 232 232 - void pcm_play_unlock(void) 233 + static void sink_unlock(void) 233 234 { 234 235 if (--dma_play_lock.locked == 0) 235 236 coldfire_imr_mod(dma_play_lock.state, 1 << 14); 236 237 } 238 + 239 + /* Stops the DMA transfer and interrupt */ 240 + static void sink_dma_stop(void) 241 + { 242 + and_l(~(DMA_EEXT | DMA_INT), &DCR0); /* per request and int OFF */ 243 + BCR0 = 0; /* No bytes remaining */ 244 + DSR0 = 1; /* Clear interrupt, errors, stop transfer */ 245 + 246 + iis_play_reset_if_playback(true); 247 + 248 + dma_play_lock.state = (1 << 14); 249 + } /* sink_dma_stop */ 237 250 238 251 /* Set up the DMA transfer that kicks in when the audio FIFO gets empty */ 239 - void pcm_play_dma_start(const void *addr, size_t size) 252 + static void sink_dma_start(const void *addr, size_t size) 240 253 { 241 254 /* Stop any DMA in progress */ 242 - pcm_play_dma_stop(); 255 + sink_dma_stop(); 243 256 244 257 /* Set up DMA transfer */ 245 258 SAR0 = (unsigned long)addr; /* Source address */ ··· 250 263 DMA_SSIZE(DMA_SIZE_LINE) | DMA_START; 251 264 252 265 dma_play_lock.state = (0 << 14); 253 - } /* pcm_play_dma_start */ 254 - 255 - /* Stops the DMA transfer and interrupt */ 256 - void pcm_play_dma_stop(void) 257 - { 258 - and_l(~(DMA_EEXT | DMA_INT), &DCR0); /* per request and int OFF */ 259 - BCR0 = 0; /* No bytes remaining */ 260 - DSR0 = 1; /* Clear interrupt, errors, stop transfer */ 261 - 262 - iis_play_reset_if_playback(true); 263 - 264 - dma_play_lock.state = (1 << 14); 265 - } /* pcm_play_dma_stop */ 266 + } /* sink_dma_start */ 266 267 267 268 /* DMA0 Interrupt is called when the DMA has finished transfering a chunk 268 269 from the caller's buffer */ ··· 300 301 } 301 302 /* else inished playing */ 302 303 } /* DMA0 */ 304 + 305 + struct pcm_sink builtin_pcm_sink = { 306 + .caps = { 307 + .samprs = hw_freq_sampr, 308 + .num_samprs = HW_NUM_FREQ, 309 + .default_freq = HW_FREQ_DEFAULT, 310 + }, 311 + .ops = { 312 + .init = sink_dma_init, 313 + .postinit = sink_dma_postinit, 314 + .set_freq = sink_set_freq, 315 + .lock = sink_lock, 316 + .unlock = sink_unlock, 317 + .play = sink_dma_start, 318 + .stop = sink_dma_stop, 319 + }, 320 + }; 303 321 304 322 #ifdef HAVE_RECORDING 305 323 /****************************************************************************
+27 -7
firmware/target/hosted/android/pcm-android.c
··· 28 28 #include "debug.h" 29 29 #include "pcm.h" 30 30 #include "pcm-internal.h" 31 + #include "pcm_sampr.h" 32 + #include "pcm_sink.h" 31 33 32 34 extern JNIEnv *env_ptr; 33 35 ··· 133 135 return max_size - left; 134 136 } 135 137 136 - void pcm_play_lock(void) 138 + static void sink_lock(void) 137 139 { 138 140 if (++audio_locked == 1) 139 141 lock_audio(); 140 142 } 141 143 142 - void pcm_play_unlock(void) 144 + static void sink_unlock(void) 143 145 { 144 146 if (--audio_locked == 0) 145 147 unlock_audio(); 146 148 } 147 149 148 - void pcm_dma_apply_settings(void) 150 + static void sink_set_freq(uint16_t freq) 149 151 { 152 + (void)freq; 150 153 } 151 154 152 - void pcm_play_dma_start(const void *addr, size_t size) 155 + static void sink_dma_start(const void *addr, size_t size) 153 156 { 154 157 pcm_data_start = addr; 155 158 pcm_data_size = size; ··· 160 163 0); 161 164 } 162 165 163 - void pcm_play_dma_stop(void) 166 + static void sink_dma_stop(void) 164 167 { 165 168 /* NOTE: due to how pcm_play_dma_complete_callback() works, this is 166 169 * possibly called from nativeWrite(), i.e. another (host) thread ··· 171 174 stop_method); 172 175 } 173 176 174 - void pcm_play_dma_init(void) 177 + static void sink_dma_init(void) 175 178 { 176 179 /* in order to have background music playing after leaving the activity, 177 180 * we need to allocate the PCM object from the Rockbox thread (the Activity ··· 194 197 write_method = e->GetMethodID(env_ptr, RockboxPCM_class, "write", "([BII)I"); 195 198 } 196 199 197 - void pcm_play_dma_postinit(void) 200 + static void sink_dma_postinit(void) 198 201 { 199 202 } 200 203 ··· 212 215 e->CallVoidMethod(env_ptr, RockboxPCM_instance, release); 213 216 pthread_mutex_destroy(&audio_lock_mutex); 214 217 } 218 + 219 + struct pcm_sink builtin_pcm_sink = { 220 + .caps = { 221 + .samprs = hw_freq_sampr, 222 + .num_samprs = HW_NUM_FREQ, 223 + .default_freq = HW_FREQ_DEFAULT, 224 + }, 225 + .ops = { 226 + .init = sink_dma_init, 227 + .postinit = sink_dma_postinit, 228 + .set_freq = sink_set_freq, 229 + .lock = sink_lock, 230 + .unlock = sink_unlock, 231 + .play = sink_dma_start, 232 + .stop = sink_dma_stop, 233 + }, 234 + }; 215 235 216 236 JNIEXPORT void JNICALL 217 237 Java_org_rockbox_RockboxPCM_postVolumeChangedEvent(JNIEnv *env,
+30 -12
firmware/target/hosted/ctru/pcm-ctru.c
··· 45 45 #include "pcm-internal.h" 46 46 #include "pcm_sampr.h" 47 47 #include "pcm_mixer.h" 48 + #include "pcm_sink.h" 48 49 49 50 #include <3ds/ndsp/ndsp.h> 50 51 #include <3ds/ndsp/channel.h> ··· 102 103 return false; 103 104 } 104 105 105 - void pcm_play_lock(void) 106 + static void sink_lock(void) 106 107 { 107 108 if (!is_in_audio_thread(_pcm_thread_id)) { 108 109 RecursiveLock_Lock(&_pcm_lock_mtx); 109 110 } 110 111 } 111 112 112 - void pcm_play_unlock(void) 113 + static void sink_unlock(void) 113 114 { 114 115 if (!is_in_audio_thread(_pcm_thread_id)) { 115 116 RecursiveLock_Unlock(&_pcm_lock_mtx); ··· 188 189 LightEvent_Signal(&_dsp_callback_event); 189 190 } 190 191 191 - static void pcm_dma_apply_settings_nolock(void) 192 + static void sink_set_freq_nolock(uint16_t freq) 192 193 { 193 194 ndspChnReset(0); 194 195 195 196 ndspSetOutputMode(NDSP_OUTPUT_STEREO); 196 197 197 - ndspChnSetRate(0, pcm_sampr); 198 + ndspChnSetRate(0, hw_freq_sampr[freq]); 198 199 ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16); 199 200 /* ndspChnSetInterp(0, NDSP_INTERP_POLYPHASE); */ 200 201 /* ndspChnSetInterp(0, NDSP_INTERP_NONE); */ ··· 235 236 -1, /* run on any core */ false); 236 237 } 237 238 238 - void pcm_play_dma_start(const void *addr, size_t size) 239 + static void sink_dma_start(const void *addr, size_t size) 239 240 { 240 241 _pcm_buffer = addr; 241 242 _pcm_buffer_size = size; ··· 245 246 RecursiveLock_Unlock(&_pcm_lock_mtx); 246 247 } 247 248 248 - void pcm_play_dma_stop(void) 249 + static void sink_dma_stop(void) 249 250 { 250 251 RecursiveLock_Lock(&_pcm_lock_mtx); 251 252 ndspChnSetPaused(0, true); ··· 303 304 304 305 #endif /* HAVE_RECORDING */ 305 306 306 - void pcm_play_dma_init(void) 307 + static void sink_dma_init(void) 307 308 { 308 309 Result ndsp_init_res = ndspInit(); 309 310 if (R_FAILED(ndsp_init_res)) { ··· 319 320 LightEvent_Init(&_dsp_callback_event, RESET_ONESHOT); 320 321 } 321 322 322 - void pcm_play_dma_postinit(void) 323 + static void sink_dma_postinit(void) 323 324 { 324 325 } 325 326 326 - void pcm_dma_apply_settings(void) 327 + static void sink_set_freq(uint16_t freq) 327 328 { 328 - pcm_play_lock(); 329 - pcm_dma_apply_settings_nolock(); 330 - pcm_play_unlock(); 329 + sink_lock(); 330 + sink_set_freq_nolock(freq); 331 + sink_unlock(); 331 332 } 332 333 333 334 void pcm_close_device(void) ··· 357 358 { 358 359 pcm_close_device(); 359 360 } 361 + 362 + struct pcm_sink builtin_pcm_sink = { 363 + .caps = { 364 + .samprs = hw_freq_sampr, 365 + .num_samprs = HW_NUM_FREQ, 366 + .default_freq = HW_FREQ_DEFAULT, 367 + }, 368 + .ops = { 369 + .init = sink_dma_init, 370 + .postinit = sink_dma_postinit, 371 + .set_freq = sink_set_freq, 372 + .lock = sink_lock, 373 + .unlock = sink_unlock, 374 + .play = sink_dma_start, 375 + .stop = sink_dma_stop, 376 + }, 377 + };
+42 -23
firmware/target/hosted/ibasso/pcm-ibasso.c
··· 31 31 #include "panic.h" 32 32 #include "pcm.h" 33 33 #include "pcm-internal.h" 34 + #include "pcm_sampr.h" 35 + #include "pcm_sink.h" 34 36 35 37 #include "sound/asound.h" 36 38 #include "tinyalsa/asoundlib.h" ··· 187 189 static struct pcm_config _config; 188 190 189 191 190 - void pcm_play_dma_init(void) 192 + static void sink_dma_init(void) 191 193 { 192 194 TRACE; 193 195 194 196 #ifdef DEBUG 195 197 196 198 /* 197 - DEBUG pcm_play_dma_init: Access: 0x000009 198 - DEBUG pcm_play_dma_init: Format[0]: 0x000044 199 - DEBUG pcm_play_dma_init: Format[1]: 0x000010 200 - DEBUG pcm_play_dma_init: Format: S16_LE 201 - DEBUG pcm_play_dma_init: Format: S24_LE 202 - DEBUG pcm_play_dma_init: Format: S20_3LE 203 - DEBUG pcm_play_dma_init: Subformat: 0x000001 204 - DEBUG pcm_play_dma_init: Rate: min = 8000Hz, max = 192000Hz 205 - DEBUG pcm_play_dma_init: Channels: min = 2, max = 2 206 - DEBUG pcm_play_dma_init: Sample bits: min=16, max=32 207 - DEBUG pcm_play_dma_init: Period size: min=8, max=10922 208 - DEBUG pcm_play_dma_init: Period count: min=3, max=128 209 - DEBUG pcm_play_dma_init: 0 mixer controls. 199 + DEBUG sink_dma_init: Access: 0x000009 200 + DEBUG sink_dma_init: Format[0]: 0x000044 201 + DEBUG sink_dma_init: Format[1]: 0x000010 202 + DEBUG sink_dma_init: Format: S16_LE 203 + DEBUG sink_dma_init: Format: S24_LE 204 + DEBUG sink_dma_init: Format: S20_3LE 205 + DEBUG sink_dma_init: Subformat: 0x000001 206 + DEBUG sink_dma_init: Rate: min = 8000Hz, max = 192000Hz 207 + DEBUG sink_dma_init: Channels: min = 2, max = 2 208 + DEBUG sink_dma_init: Sample bits: min=16, max=32 209 + DEBUG sink_dma_init: Period size: min=8, max=10922 210 + DEBUG sink_dma_init: Period count: min=3, max=128 211 + DEBUG sink_dma_init: 0 mixer controls. 210 212 */ 211 213 212 214 struct pcm_params* params = pcm_params_get(CARD, DEVICE, PCM_OUT); ··· 310 312 pcm_thread_run relies on this size match. See pcm_mixer.h. 311 313 */ 312 314 _config.channels = 2; 313 - _config.rate = pcm_sampr; 315 + _config.rate = hw_freq_sampr[HW_FREQ_DEFAULT]; 314 316 _config.period_size = 256; 315 317 _config.period_count = 4; 316 318 _config.format = PCM_FORMAT_S16_LE; ··· 337 339 } 338 340 339 341 340 - void pcm_play_dma_start(const void *addr, size_t size) 342 + static void sink_dma_start(const void *addr, size_t size) 341 343 { 342 344 TRACE; 343 345 ··· 364 366 pthread_mutex_unlock(&_dma_suspended_mtx); 365 367 } 366 368 367 - void pcm_play_dma_stop(void) 369 + static void sink_dma_stop(void) 368 370 { 369 371 TRACE; 370 372 ··· 375 377 } 376 378 377 379 378 - /* Unessecary play locks before pcm_play_dma_postinit. */ 380 + /* Unessecary play locks before sink_dma_postinit. */ 379 381 static int _play_lock_recursion_count = -10000; 380 382 381 383 382 - void pcm_play_dma_postinit(void) 384 + static void sink_dma_postinit(void) 383 385 { 384 386 TRACE; 385 387 ··· 387 389 } 388 390 389 391 390 - void pcm_play_lock(void) 392 + static void sink_lock(void) 391 393 { 392 394 TRACE; 393 395 ··· 402 404 } 403 405 404 406 405 - void pcm_play_unlock(void) 407 + static void sink_unlock(void) 406 408 { 407 409 TRACE; 408 410 ··· 418 420 } 419 421 420 422 421 - void pcm_dma_apply_settings(void) 423 + static void sink_set_freq(uint16_t freq) 422 424 { 423 - unsigned int rate = pcm_get_frequency(); 425 + unsigned int rate = hw_freq_sampr[freq]; 424 426 425 427 DEBUGF("DEBUG %s: Current sample rate: %u, next sampe rate: %u.", __func__, _config.rate, rate); 426 428 ··· 450 452 pcm_close(_alsa_handle); 451 453 _alsa_handle = NULL; 452 454 } 455 + 456 + struct pcm_sink builtin_pcm_sink = { 457 + .caps = { 458 + .samprs = hw_freq_sampr, 459 + .num_samprs = HW_NUM_FREQ, 460 + .default_freq = HW_FREQ_DEFAULT, 461 + }, 462 + .ops = { 463 + .init = sink_dma_init, 464 + .postinit = sink_dma_postinit, 465 + .set_freq = sink_set_freq, 466 + .lock = sink_lock, 467 + .unlock = sink_unlock, 468 + .play = sink_dma_start, 469 + .stop = sink_dma_stop, 470 + }, 471 + };
+45 -27
firmware/target/hosted/pcm-alsa.c
··· 48 48 #include "pcm-internal.h" 49 49 #include "pcm_mixer.h" 50 50 #include "pcm_sampr.h" 51 + #include "pcm_sink.h" 51 52 #include "audiohw.h" 52 53 #include "pcm-alsa.h" 53 54 #include "fixedpoint.h" ··· 122 123 } 123 124 #endif 124 125 125 - static int set_hwparams(snd_pcm_t *handle) 126 + static int set_hwparams(snd_pcm_t *handle, unsigned long sampr) 126 127 { 127 128 int err; 128 129 unsigned int srate; ··· 136 137 Note these are in FRAMES, and are sized to be about 8.5ms 137 138 for the buffer and 2.1ms for the period 138 139 */ 139 - if (pcm_sampr > SAMPR_96) { 140 + if (sampr > SAMPR_96) { 140 141 buffer_size = MIX_FRAME_SAMPLES * 4 * 4; 141 142 period_size = MIX_FRAME_SAMPLES * 4; 142 - } else if (pcm_sampr > SAMPR_48) { 143 + } else if (sampr > SAMPR_48) { 143 144 buffer_size = MIX_FRAME_SAMPLES * 2 * 4; 144 145 period_size = MIX_FRAME_SAMPLES * 2; 145 146 } else { ··· 176 177 goto error; 177 178 } 178 179 /* set the stream rate */ 179 - srate = pcm_sampr; 180 + srate = sampr; 180 181 err = snd_pcm_hw_params_set_rate_near(handle, params, &srate, 0); 181 182 if (err < 0) 182 183 { 183 - logf("Rate %luHz not available for playback: %s", pcm_sampr, snd_strerror(err)); 184 + logf("Rate %luHz not available for playback: %s", sampr, snd_strerror(err)); 184 185 goto error; 185 186 } 186 187 real_sample_rate = srate; 187 - if (real_sample_rate != pcm_sampr) 188 + if (real_sample_rate != sampr) 188 189 { 189 - logf("Rate doesn't match (requested %luHz, get %dHz)", pcm_sampr, real_sample_rate); 190 + logf("Rate doesn't match (requested %luHz, get %dHz)", sampr, real_sample_rate); 190 191 err = -EINVAL; 191 192 goto error; 192 193 } ··· 595 596 atexit(alsadev_cleanup); 596 597 } 597 598 598 - void pcm_play_dma_init(void) 599 + static void sink_dma_init(void) 599 600 { 600 601 logf("PCM DMA Init"); 601 602 ··· 606 607 return; 607 608 } 608 609 609 - void pcm_play_lock(void) 610 + static void sink_lock(void) 610 611 { 611 612 pthread_mutex_lock(&pcm_mtx); 612 613 } 613 614 614 - void pcm_play_unlock(void) 615 + static void sink_unlock(void) 615 616 { 616 617 pthread_mutex_unlock(&pcm_mtx); 617 618 } 618 619 619 - static void pcm_dma_apply_settings_nolock(void) 620 + static void sink_set_freq_nolock(uint16_t freq) 620 621 { 621 - logf("PCM DMA Settings %d %lu", last_sample_rate, pcm_sampr); 622 + unsigned int sampr = hw_freq_sampr[freq]; 623 + 624 + logf("PCM DMA Settings %d %lu", last_sample_rate, sampr); 622 625 623 - if (last_sample_rate != pcm_sampr) 626 + if (last_sample_rate != sampr) 624 627 { 625 - last_sample_rate = pcm_sampr; 628 + last_sample_rate = sampr; 626 629 627 630 #ifdef AUDIOHW_MUTE_ON_SRATE_CHANGE 628 631 audiohw_mute(true); 629 632 #endif 630 633 snd_pcm_drop(handle); 631 634 632 - set_hwparams(handle); // FIXME: check return code? 635 + set_hwparams(handle, sampr); // FIXME: check return code? 633 636 set_swparams(handle); // FIXME: check return code? 634 637 635 638 #if defined(HAVE_NWZ_LINUX_CODEC) 636 639 /* Sony NWZ linux driver uses a nonstandard mecanism to set the sampling rate */ 637 - audiohw_set_frequency(pcm_sampr); 640 + audiohw_set_frequency(sampr); 638 641 #endif 639 642 /* (Will be unmuted by pcm resuming) */ 640 643 } 641 644 } 642 645 643 - void pcm_dma_apply_settings(void) 646 + static void sink_set_freq(uint16_t freq) 644 647 { 645 - pcm_play_lock(); 646 - pcm_dma_apply_settings_nolock(); 647 - pcm_play_unlock(); 648 + sink_lock(); 649 + sink_set_freq_nolock(freq); 650 + sink_unlock(); 648 651 } 649 652 650 - void pcm_play_dma_stop(void) 653 + static void sink_dma_stop(void) 651 654 { 652 655 logf("PCM DMA stop (%d)", snd_pcm_state(handle)); 653 656 ··· 660 663 #endif 661 664 } 662 665 663 - void pcm_play_dma_start(const void *addr, size_t size) 666 + static void sink_dma_start(const void *addr, size_t size) 664 667 { 665 668 logf("PCM DMA start (%p %d)", addr, size); 666 - pcm_dma_apply_settings_nolock(); 667 669 668 670 pcm_data = addr; 669 671 pcm_size = size; ··· 749 751 } 750 752 } 751 753 752 - void pcm_play_dma_postinit(void) 754 + static void sink_dma_postinit(void) 753 755 { 754 756 audiohw_postinit(); 755 757 ··· 768 770 return xruns; 769 771 } 770 772 773 + struct pcm_sink builtin_pcm_sink = { 774 + .caps = { 775 + .samprs = hw_freq_sampr, 776 + .num_samprs = HW_NUM_FREQ, 777 + .default_freq = HW_FREQ_DEFAULT, 778 + }, 779 + .ops = { 780 + .init = sink_dma_init, 781 + .postinit = sink_dma_postinit, 782 + .set_freq = sink_set_freq, 783 + .lock = sink_lock, 784 + .unlock = sink_unlock, 785 + .play = sink_dma_start, 786 + .stop = sink_dma_stop, 787 + }, 788 + }; 789 + 771 790 #ifdef HAVE_RECORDING 772 791 void pcm_rec_lock(void) 773 792 { 774 - pcm_play_lock(); 793 + sink_lock(); 775 794 } 776 795 777 796 void pcm_rec_unlock(void) 778 797 { 779 - pcm_play_unlock(); 798 + sink_unlock(); 780 799 } 781 800 782 801 void pcm_rec_dma_init(void) ··· 796 815 void pcm_rec_dma_start(void *start, size_t size) 797 816 { 798 817 logf("PCM REC DMA start (%p %d)", start, size); 799 - pcm_dma_apply_settings_nolock(); 800 818 pcm_data_rec = start; 801 819 pcm_size = size; 802 820
+34 -11
firmware/target/hosted/sdl/pcm-sdl.c
··· 44 44 #include "pcm-internal.h" 45 45 #include "pcm_sampr.h" 46 46 #include "pcm_mixer.h" 47 + #include "pcm_sink.h" 47 48 48 49 /*#define LOGF_ENABLE*/ 49 50 #include "logf.h" ··· 56 57 57 58 58 59 static int cvt_status = -1; 60 + 61 + static unsigned long pcm_sampr; 59 62 60 63 static const void *pcm_data; 61 64 static size_t pcm_data_size; ··· 80 83 static int audio_locked = 0; 81 84 static SDL_mutex *audio_lock; 82 85 83 - void pcm_play_lock(void) 86 + static void sink_lock(void) 84 87 { 85 88 if (++audio_locked == 1) 86 89 SDL_LockMutex(audio_lock); 87 90 } 88 91 89 - void pcm_play_unlock(void) 92 + static void sink_unlock(void) 90 93 { 91 94 if (--audio_locked == 0) 92 95 SDL_UnlockMutex(audio_lock); ··· 97 100 #endif 98 101 99 102 static void sdl_audio_callback(void *handle, Uint8 *stream, int len); 100 - static void pcm_dma_apply_settings_nolock(void) 103 + 104 + static void sink_set_freq_nolock(uint16_t freq) 101 105 { 106 + pcm_sampr = hw_freq_sampr[freq]; 107 + 102 108 SDL_AudioSpec wanted_spec; 103 109 wanted_spec.freq = pcm_sampr; 104 110 wanted_spec.format = AUDIO_S16SYS; ··· 161 167 } 162 168 } 163 169 164 - void pcm_dma_apply_settings(void) 170 + static void sink_set_freq(uint16_t freq) 165 171 { 166 - pcm_play_lock(); 167 - pcm_dma_apply_settings_nolock(); 168 - pcm_play_unlock(); 172 + sink_lock(); 173 + sink_set_freq_nolock(freq); 174 + sink_unlock(); 169 175 } 170 176 171 - void pcm_play_dma_start(const void *addr, size_t size) 177 + static void sink_dma_start(const void *addr, size_t size) 172 178 { 173 179 pcm_data = addr; 174 180 pcm_data_size = size; ··· 180 186 #endif 181 187 } 182 188 183 - void pcm_play_dma_stop(void) 189 + static void sink_dma_stop(void) 184 190 { 185 191 #if SDL_MAJOR_VERSION > 1 186 192 SDL_PauseAudioDevice(pcm_devid, 1); ··· 397 403 398 404 #endif /* HAVE_RECORDING */ 399 405 400 - void pcm_play_dma_init(void) 406 + static void sink_dma_init(void) 401 407 { 402 408 if (SDL_InitSubSystem(SDL_INIT_AUDIO)) 403 409 { ··· 435 441 #endif 436 442 } 437 443 438 - void pcm_play_dma_postinit(void) 444 + static void sink_dma_postinit(void) 439 445 { 440 446 } 447 + 448 + struct pcm_sink builtin_pcm_sink = { 449 + .caps = { 450 + .samprs = hw_freq_sampr, 451 + .num_samprs = HW_NUM_FREQ, 452 + .default_freq = HW_FREQ_DEFAULT, 453 + }, 454 + .ops = { 455 + .init = sink_dma_init, 456 + .postinit = sink_dma_postinit, 457 + .set_freq = sink_set_freq, 458 + .lock = sink_lock, 459 + .unlock = sink_unlock, 460 + .play = sink_dma_start, 461 + .stop = sink_dma_stop, 462 + }, 463 + };
+38 -21
firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c
··· 26 26 #include "sound.h" 27 27 #include "pcm.h" 28 28 #include "pcm-internal.h" 29 + #include "pcm_sink.h" 29 30 #include "jz4740.h" 30 - 31 31 32 32 /**************************************************************************** 33 33 ** Playback DMA transfer 34 34 **/ 35 35 36 - void pcm_play_dma_postinit(void) 36 + static void sink_dma_postinit(void) 37 37 { 38 38 audiohw_postinit(); 39 39 ··· 47 47 __aic_flush_fifo(); 48 48 } 49 49 50 - void pcm_play_dma_init(void) 50 + static void sink_dma_init(void) 51 51 { 52 52 /* TODO */ 53 53 ··· 57 57 audiohw_init(); 58 58 } 59 59 60 - void pcm_dma_apply_settings(void) 60 + static void sink_set_freq(uint16_t freq) 61 61 { 62 62 /* TODO */ 63 - audiohw_set_frequency(pcm_sampr); 63 + audiohw_set_frequency(freq); 64 64 } 65 65 66 66 static const void* playback_address; ··· 135 135 } 136 136 } 137 137 138 - void pcm_play_dma_start(const void *addr, size_t size) 138 + static void sink_dma_stop(void) 139 139 { 140 - pcm_play_dma_stop(); 140 + int flags = disable_irq_save(); 141 141 142 - dma_enable(); 142 + REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) = (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) | DMAC_DCCSR_HLT) & ~DMAC_DCCSR_EN; 143 143 144 - set_dma(addr, size); 144 + dma_disable(); 145 145 146 - __aic_enable_transmit_dma(); 147 - __aic_enable_replay(); 146 + __aic_disable_transmit_dma(); 147 + __aic_disable_replay(); 148 148 149 - REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN; 149 + restore_irq(flags); 150 150 } 151 151 152 - void pcm_play_dma_stop(void) 152 + static void sink_dma_start(const void *addr, size_t size) 153 153 { 154 - int flags = disable_irq_save(); 154 + sink_dma_stop(); 155 155 156 - REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) = (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) | DMAC_DCCSR_HLT) & ~DMAC_DCCSR_EN; 156 + dma_enable(); 157 157 158 - dma_disable(); 158 + set_dma(addr, size); 159 159 160 - __aic_disable_transmit_dma(); 161 - __aic_disable_replay(); 160 + __aic_enable_transmit_dma(); 161 + __aic_enable_replay(); 162 162 163 - restore_irq(flags); 163 + REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN; 164 164 } 165 165 166 166 static unsigned int play_lock = 0; 167 - void pcm_play_lock(void) 167 + static void sink_lock(void) 168 168 { 169 169 int flags = disable_irq_save(); 170 170 ··· 174 174 restore_irq(flags); 175 175 } 176 176 177 - void pcm_play_unlock(void) 177 + static void sink_unlock(void) 178 178 { 179 179 int flags = disable_irq_save(); 180 180 ··· 188 188 { 189 189 /* TODO: prevent pop */ 190 190 } 191 + 192 + struct pcm_sink builtin_pcm_sink = { 193 + .caps = { 194 + .samprs = hw_freq_sampr, 195 + .num_samprs = HW_NUM_FREQ, 196 + .default_freq = HW_FREQ_DEFAULT, 197 + }, 198 + .ops = { 199 + .init = sink_dma_init, 200 + .postinit = sink_dma_postinit, 201 + .set_freq = sink_set_freq, 202 + .lock = sink_lock, 203 + .unlock = sink_unlock, 204 + .play = sink_dma_start, 205 + .stop = sink_dma_stop, 206 + }, 207 + }; 191 208 192 209 #ifdef HAVE_RECORDING 193 210 /* TODO */
+36 -18
firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c
··· 26 26 #include "sound.h" 27 27 #include "pcm.h" 28 28 #include "pcm-internal.h" 29 + #include "pcm_sink.h" 29 30 #include "cpu.h" 30 31 31 32 /**************************************************************************** 32 33 ** Playback DMA transfer 33 34 **/ 34 35 35 - void pcm_play_dma_postinit(void) 36 + static void sink_dma_postinit(void) 36 37 { 37 38 audiohw_postinit(); 38 39 ··· 40 41 __aic_flush_tfifo(); 41 42 } 42 43 43 - void pcm_play_dma_init(void) 44 + static void sink_dma_init(void) 44 45 { 45 46 system_enable_irq(DMA_IRQ(DMA_AIC_TX_CHANNEL)); 46 47 ··· 48 49 audiohw_init(); 49 50 } 50 51 51 - void pcm_dma_apply_settings(void) 52 + static void sink_set_freq(uint16_t freq) 52 53 { 53 - audiohw_set_frequency(pcm_fsel); 54 + audiohw_set_frequency(freq); 54 55 } 55 56 56 57 static const void* playback_address; ··· 126 127 } 127 128 } 128 129 129 - void pcm_play_dma_start(const void *addr, size_t size) 130 + static void sink_dma_stop(void) 130 131 { 131 - pcm_play_dma_stop(); 132 + int flags = disable_irq_save(); 132 133 133 - __dmac_channel_enable_clk(DMA_AIC_TX_CHANNEL); 134 + REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) = (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) | DMAC_DCCSR_HLT) & ~DMAC_DCCSR_EN; 134 135 135 - set_dma(addr, size); 136 + __dmac_channel_disable_clk(DMA_AIC_TX_CHANNEL); 136 137 137 - __aic_enable_replay(); 138 + __aic_disable_replay(); 138 139 139 - __dmac_channel_enable_irq(DMA_AIC_TX_CHANNEL); 140 + restore_irq(flags); 140 141 } 141 142 142 - void pcm_play_dma_stop(void) 143 + static void sink_dma_start(const void *addr, size_t size) 143 144 { 144 - int flags = disable_irq_save(); 145 + sink_dma_stop(); 145 146 146 - REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) = (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) | DMAC_DCCSR_HLT) & ~DMAC_DCCSR_EN; 147 + __dmac_channel_enable_clk(DMA_AIC_TX_CHANNEL); 147 148 148 - __dmac_channel_disable_clk(DMA_AIC_TX_CHANNEL); 149 + set_dma(addr, size); 149 150 150 - __aic_disable_replay(); 151 + __aic_enable_replay(); 151 152 152 - restore_irq(flags); 153 + __dmac_channel_enable_irq(DMA_AIC_TX_CHANNEL); 153 154 } 154 155 155 156 static unsigned int play_lock = 0; 156 - void pcm_play_lock(void) 157 + static void sink_lock(void) 157 158 { 158 159 int flags = disable_irq_save(); 159 160 ··· 163 164 restore_irq(flags); 164 165 } 165 166 166 - void pcm_play_unlock(void) 167 + static void sink_unlock(void) 167 168 { 168 169 int flags = disable_irq_save(); 169 170 ··· 172 173 173 174 restore_irq(flags); 174 175 } 176 + 177 + struct pcm_sink builtin_pcm_sink = { 178 + .caps = { 179 + .samprs = hw_freq_sampr, 180 + .num_samprs = HW_NUM_FREQ, 181 + .default_freq = HW_FREQ_DEFAULT, 182 + }, 183 + .ops = { 184 + .init = sink_dma_init, 185 + .postinit = sink_dma_postinit, 186 + .set_freq = sink_set_freq, 187 + .lock = sink_lock, 188 + .unlock = sink_unlock, 189 + .play = sink_dma_start, 190 + .stop = sink_dma_stop, 191 + }, 192 + };
+26 -13
firmware/target/mips/ingenic_x1000/pcm-x1000.c
··· 25 25 #include "audiohw.h" 26 26 #include "pcm.h" 27 27 #include "pcm-internal.h" 28 + #include "pcm_sink.h" 28 29 #include "panic.h" 29 30 #include "dma-x1000.h" 30 31 #include "irq-x1000.h" ··· 53 54 static void pcm_rec_dma_int_cb(int event); 54 55 #endif 55 56 56 - void pcm_play_dma_init(void) 57 + static void sink_dma_init(void) 57 58 { 58 59 /* Ungate clock */ 59 60 jz_writef(CPM_CLKGR, AIC(0)); ··· 99 100 system_enable_irq(IRQ_AIC); 100 101 } 101 102 102 - void pcm_play_dma_postinit(void) 103 - { 104 - audiohw_postinit(); 105 - } 106 - 107 - void pcm_dma_apply_settings(void) 103 + static void sink_set_freq(uint16_t freq) 108 104 { 109 - audiohw_set_frequency(pcm_fsel); 105 + audiohw_set_frequency(freq); 110 106 } 111 107 112 108 static void play_dma_start(const void* addr, size_t size) ··· 157 153 } 158 154 } 159 155 160 - void pcm_play_dma_start(const void* addr, size_t size) 156 + static void sink_dma_start(const void* addr, size_t size) 161 157 { 162 158 play_dma_pending_event = DMA_EVENT_NONE; 163 159 aic_state |= AIC_STATE_PLAYING; ··· 166 162 jz_writef(AIC_CCR, TDMS(1), ETUR(1), ERPL(1)); 167 163 } 168 164 169 - void pcm_play_dma_stop(void) 165 + static void sink_dma_stop(void) 170 166 { 171 167 /* disable DMA and underrun interrupts */ 172 168 jz_writef(AIC_CCR, TDMS(0), ETUR(0)); ··· 180 176 if (jz_readf(AIC_I2SCR, STPBK) == 0) { 181 177 while(jz_readf(AIC_SR, TFL) != 0); 182 178 } else { 183 - panicf("pcm_play_dma_stop: No bit clock running!"); 179 + panicf("sink_dma_stop: No bit clock running!"); 184 180 } 185 181 186 182 /* disable playback */ ··· 190 186 aic_state &= ~AIC_STATE_PLAYING; 191 187 } 192 188 193 - void pcm_play_lock(void) 189 + static void sink_lock(void) 194 190 { 195 191 int irq = disable_irq_save(); 196 192 ++play_lock; 197 193 restore_irq(irq); 198 194 } 199 195 200 - void pcm_play_unlock(void) 196 + static void sink_unlock(void) 201 197 { 202 198 int irq = disable_irq_save(); 203 199 if(--play_lock == 0 && (aic_state & AIC_STATE_PLAYING)) { ··· 207 203 208 204 restore_irq(irq); 209 205 } 206 + 207 + struct pcm_sink builtin_pcm_sink = { 208 + .caps = { 209 + .samprs = hw_freq_sampr, 210 + .num_samprs = HW_NUM_FREQ, 211 + .default_freq = HW_FREQ_DEFAULT, 212 + }, 213 + .ops = { 214 + .init = sink_dma_init, 215 + .postinit = audiohw_postinit, 216 + .set_freq = sink_set_freq, 217 + .lock = sink_lock, 218 + .unlock = sink_unlock, 219 + .play = sink_dma_start, 220 + .stop = sink_dma_stop, 221 + }, 222 + }; 210 223 211 224 #ifdef HAVE_RECORDING 212 225 /*