From dfa33c246b5f0244e0703722c63fe91f94633a97 Mon Sep 17 00:00:00 2001 From: mojyack Date: Wed, 17 Dec 2025 16:19:07 +0900 Subject: [PATCH] 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 --- apps/plugin.c | 1 + apps/plugin.h | 2 + docs/PLUGIN_API | 4 + firmware/export/pcm-internal.h | 18 +- firmware/export/pcm.h | 6 + firmware/export/pcm_sink.h | 57 +++++++ firmware/pcm.c | 154 +++++++++++------- firmware/pcm_sw_volume.c | 4 +- firmware/target/arm/as3525/pcm-as3525.c | 57 ++++--- firmware/target/arm/imx233/pcm-imx233.c | 54 +++--- .../arm/imx31/gigabeat-s/pcm-gigabeat-s.c | 37 +++-- firmware/target/arm/pcm-telechips.c | 35 ++-- firmware/target/arm/pp/pcm-pp.c | 68 +++++--- firmware/target/arm/rk27xx/pcm-rk27xx.c | 41 +++-- .../arm/s3c2440/gigabeat-fx/pcm-meg-fx.c | 37 +++-- firmware/target/arm/s5l8700/pcm-s5l8700.c | 38 +++-- firmware/target/arm/s5l8702/pcm-s5l8702.c | 58 ++++--- firmware/target/arm/stm32/pcm-stm32h7.c | 40 +++-- .../arm/tms320dm320/mrobe-500/pcm-mr500.c | 35 +++- .../sansa-connect/pcm-sansaconnect.c | 38 +++-- firmware/target/coldfire/pcm-coldfire.c | 70 +++++--- firmware/target/hosted/android/pcm-android.c | 34 +++- firmware/target/hosted/ctru/pcm-ctru.c | 42 +++-- firmware/target/hosted/ibasso/pcm-ibasso.c | 65 +++++--- firmware/target/hosted/pcm-alsa.c | 72 +++++--- firmware/target/hosted/sdl/pcm-sdl.c | 45 +++-- .../target/mips/ingenic_jz47xx/pcm-jz4740.c | 61 ++++--- .../target/mips/ingenic_jz47xx/pcm-jz4760.c | 58 ++++--- .../target/mips/ingenic_x1000/pcm-x1000.c | 39 +++-- 29 files changed, 846 insertions(+), 424 deletions(-) create mode 100644 firmware/export/pcm_sink.h diff --git a/apps/plugin.c b/apps/plugin.c index 0aa800b5cd..d15f88c376 100644 --- a/apps/plugin.c +++ b/apps/plugin.c @@ -647,6 +647,7 @@ static const struct plugin_api rockbox_api = { pcm_apply_settings, pcm_play_lock, pcm_play_unlock, + pcm_current_sink_caps, beep_play, #ifdef HAVE_RECORDING &rec_freq_sampr[0], diff --git a/apps/plugin.h b/apps/plugin.h index 52c11a5af3..69cf7659c9 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -93,6 +93,7 @@ int plugin_open(const char *plugin, const char *parameter); #include "misc.h" #include "pathfuncs.h" #include "pcm_mixer.h" +#include "pcm_sink.h" #include "dsp-util.h" #include "dsp_core.h" #include "dsp_proc_settings.h" @@ -745,6 +746,7 @@ struct plugin_api { void (*pcm_apply_settings)(void); void (*pcm_play_lock)(void); void (*pcm_play_unlock)(void); + const struct pcm_sink_caps* (*pcm_current_sink_caps)(void); void (*beep_play)(unsigned int frequency, unsigned int duration, unsigned int amplitude); #ifdef HAVE_RECORDING diff --git a/docs/PLUGIN_API b/docs/PLUGIN_API index af999d317b..8fdc10f9eb 100644 --- a/docs/PLUGIN_API +++ b/docs/PLUGIN_API @@ -1783,6 +1783,10 @@ void pcm_play_unlock(void) \group sound \description +const struct pcm_sink_caps* pcm_current_sink_caps(enum pcm_sink_ids sink) + \group sound + \description + void pcm_record_data(pcm_rec_callback_type more_ready, pcm_status_callback_type status_cb, void *start, size_t size) \group sound \conditions (defined(HAVE_RECORDING)) diff --git a/firmware/export/pcm-internal.h b/firmware/export/pcm-internal.h index fde7446014..4e9cb58c91 100644 --- a/firmware/export/pcm-internal.h +++ b/firmware/export/pcm-internal.h @@ -22,7 +22,11 @@ #ifndef PCM_INTERNAL_H #define PCM_INTERNAL_H +#include + #include "config.h" +#include "pcm.h" +#include "pcm_sink.h" #include "gcc_extensions.h" /* for FORCE_INLINE */ #ifdef HAVE_SW_VOLUME_CONTROL @@ -129,24 +133,12 @@ void pcm_play_stop_int(void); bool pcm_play_dma_complete_callback(enum pcm_dma_status status, const void **addr, size_t *size); -extern unsigned long pcm_curr_sampr; -extern unsigned long pcm_sampr; -extern int pcm_fsel; - #ifdef HAVE_PCM_DMA_ADDRESS void * pcm_dma_addr(void *addr); #endif extern volatile bool pcm_playing; - -void pcm_play_dma_lock(void); -void pcm_play_dma_unlock(void); -void pcm_play_dma_init(void) INIT_ATTR; -void pcm_play_dma_postinit(void); -void pcm_play_dma_start(const void *addr, size_t size); -void pcm_play_dma_stop(void); - -void pcm_dma_apply_settings(void); +struct pcm_sink* pcm_get_current_sink(void); #ifdef HAVE_RECORDING diff --git a/firmware/export/pcm.h b/firmware/export/pcm.h index 9a89375c08..8c5170ac0e 100644 --- a/firmware/export/pcm.h +++ b/firmware/export/pcm.h @@ -71,6 +71,12 @@ void pcm_init(void) INIT_ATTR; void pcm_postinit(void); bool pcm_is_initialized(void); +enum pcm_sink_ids pcm_current_sink(void); +const struct pcm_sink_caps* pcm_sink_caps(enum pcm_sink_ids sink); + +/* shortcut for plugins */ +const struct pcm_sink_caps* pcm_current_sink_caps(void); + /* This is for playing "raw" PCM data */ void pcm_play_data(pcm_play_callback_type get_more, pcm_status_callback_type status_cb, diff --git a/firmware/export/pcm_sink.h b/firmware/export/pcm_sink.h new file mode 100644 index 0000000000..27c0c31db1 --- /dev/null +++ b/firmware/export/pcm_sink.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#pragma once +#include +#include + +struct pcm_sink_caps { + const unsigned long* samprs; + uint16_t num_samprs; + uint16_t default_freq; +}; + +struct pcm_sink_ops { + void (*init)(void); + void (*postinit)(void); + void (*set_freq)(uint16_t freq); + void (*lock)(void); + void (*unlock)(void); + void (*play)(const void* addr, size_t size); + void (*stop)(void); +}; + +struct pcm_sink { + /* characteristics */ + const struct pcm_sink_caps caps; + + /* operations */ + const struct pcm_sink_ops ops; + + /* runtime states */ + unsigned long pending_freq; + unsigned long configured_freq; +}; + +enum pcm_sink_ids { + PCM_SINK_BUILTIN = 0, +}; + +/* defined in each platform pcm source */ +extern struct pcm_sink builtin_pcm_sink; diff --git a/firmware/pcm.c b/firmware/pcm.c index de01af484f..c19ed8e070 100644 --- a/firmware/pcm.c +++ b/firmware/pcm.c @@ -42,21 +42,19 @@ * Semi-private - * pcm_play_dma_complete_callback * pcm_play_dma_status_callback - * pcm_play_dma_init - * pcm_play_dma_postinit - * pcm_play_dma_start - * pcm_play_dma_stop + * pcm_get_current_sink + * pcm_sink.init + * pcm_sink.postinit + * pcm_sink.play + * pcm_sink.stop * Data Read/Written within TSP - - * pcm_sampr (R) - * pcm_fsel (R) - * pcm_curr_sampr (R) * pcm_playing (R) * * ==Playback/Recording== * Public - * pcm_dma_addr * Semi-private - - * pcm_dma_apply_settings + * pcm_sink.set_freq * * ==Recording== * Public - @@ -81,6 +79,12 @@ /* 'true' when all stages of pcm initialization have completed */ static bool pcm_is_ready = false; +static struct pcm_sink* sinks[1] = { + [PCM_SINK_BUILTIN] = &builtin_pcm_sink, +}; +static enum pcm_sink_ids cur_sink = PCM_SINK_BUILTIN; +static struct mutex sink_mutex; /* protects sinks and cur_sink */ + /* The registered callback function to ask for more mp3 data */ volatile pcm_play_callback_type pcm_callback_for_more SHAREDBSS_ATTR = NULL; @@ -89,16 +93,14 @@ volatile pcm_status_callback_type pcm_play_status_callback SHAREDBSS_ATTR = NULL; /* PCM playback state */ volatile bool pcm_playing SHAREDBSS_ATTR = false; -/* samplerate of currently playing audio - undefined if stopped */ -unsigned long pcm_curr_sampr SHAREDBSS_ATTR = 0; -/* samplerate waiting to be set */ -unsigned long pcm_sampr SHAREDBSS_ATTR = HW_SAMPR_DEFAULT; -/* samplerate frequency selection index */ -int pcm_fsel SHAREDBSS_ATTR = HW_FREQ_DEFAULT; -static void pcm_play_data_start_int(const void *addr, size_t size); void pcm_play_stop_int(void); +struct pcm_sink* pcm_get_current_sink(void) +{ + return sinks[cur_sink]; +} + #if !defined(HAVE_SW_VOLUME_CONTROL) || defined(PCM_SW_VOLUME_UNBUFFERED) /** Standard hw volume/unbuffered control functions - otherwise, see ** pcm_sw_volume.c **/ @@ -108,12 +110,12 @@ static inline void pcm_play_dma_start_int(const void *addr, size_t size) /* Smoothed transition might not have happened so sync now */ pcm_sync_pcm_factors(); #endif - pcm_play_dma_start(addr, size); + sinks[cur_sink]->ops.play(addr, size); } static inline void pcm_play_dma_stop_int(void) { - pcm_play_dma_stop(); + sinks[cur_sink]->ops.stop(); } bool pcm_play_dma_complete_callback(enum pcm_dma_status status, @@ -123,8 +125,9 @@ bool pcm_play_dma_complete_callback(enum pcm_dma_status status, if (status < PCM_DMAST_OK) status = pcm_play_dma_status_callback(status); - if (status >= PCM_DMAST_OK && pcm_get_more_int(addr, size)) + if (status >= PCM_DMAST_OK && pcm_get_more_int(addr, size)) { return true; + } /* Error, callback missing or no more DMA to do */ pcm_play_stop_int(); @@ -132,25 +135,6 @@ bool pcm_play_dma_complete_callback(enum pcm_dma_status status, } #endif /* !HAVE_SW_VOLUME_CONTROL || PCM_SW_VOLUME_UNBUFFERED */ -static void pcm_play_data_start_int(const void *addr, size_t size) -{ - ALIGN_AUDIOBUF(addr, size); - - if ((addr && size) || pcm_get_more_int(&addr, &size)) - { - pcm_apply_settings(); - logf(" pcm_play_dma_start_int"); - pcm_play_dma_start_int(addr, size); - pcm_playing = true; - } - else - { - /* Force a stop */ - logf(" pcm_play_stop_int"); - pcm_play_stop_int(); - } -} - void pcm_play_stop_int(void) { pcm_play_dma_stop_int(); @@ -223,7 +207,15 @@ void pcm_do_peak_calculation(struct pcm_peaks *peaks, bool active, if (active) { - int framecount = peaks->period*pcm_curr_sampr / HZ; + struct pcm_sink* sink = sinks[cur_sink]; + if (sink->configured_freq == -1UL) + { + logf("not configured yet"); + return; + } + + unsigned long sampr = sink->caps.samprs[sink->configured_freq]; + int framecount = peaks->period * sampr / HZ; count = MIN(framecount, count); if (count > 0) @@ -247,16 +239,29 @@ bool pcm_is_playing(void) * interface */ +void pcm_play_lock(void) { + mutex_lock(&sink_mutex); + sinks[cur_sink]->ops.lock(); + /* hold sink_mutex until pcm_play_unlock() */ +} + +void pcm_play_unlock(void) { + sinks[cur_sink]->ops.unlock(); + mutex_unlock(&sink_mutex); +} + /* This should only be called at startup before any audio playback or recording is attempted */ void pcm_init(void) { logf("pcm_init"); - pcm_set_frequency(HW_SAMPR_DEFAULT); - - logf(" pcm_play_dma_init"); - pcm_play_dma_init(); + mutex_init(&sink_mutex); + for(size_t i = 0; i < ARRAYLEN(sinks); i += 1) { + sinks[i]->pending_freq = sinks[i]->caps.default_freq; + sinks[i]->configured_freq = -1UL; + sinks[i]->ops.init(); + } } /* Finish delayed init */ @@ -264,9 +269,9 @@ void pcm_postinit(void) { logf("pcm_postinit"); - logf(" pcm_play_dma_postinit"); - - pcm_play_dma_postinit(); + for(size_t i = 0; i < ARRAYLEN(sinks); i += 1) { + sinks[i]->ops.postinit(); + } pcm_is_ready = true; } @@ -276,6 +281,21 @@ bool pcm_is_initialized(void) return pcm_is_ready; } +enum pcm_sink_ids pcm_current_sink(void) +{ + return cur_sink; +} + +const struct pcm_sink_caps* pcm_sink_caps(enum pcm_sink_ids sink) +{ + return &sinks[sink]->caps; +} + +const struct pcm_sink_caps* pcm_current_sink_caps(void) +{ + return pcm_sink_caps(pcm_current_sink()); +} + void pcm_play_data(pcm_play_callback_type get_more, pcm_status_callback_type status_cb, const void *start, size_t size) @@ -287,8 +307,20 @@ void pcm_play_data(pcm_play_callback_type get_more, pcm_callback_for_more = get_more; pcm_play_status_callback = status_cb; - logf(" pcm_play_data_start_int"); - pcm_play_data_start_int(start, size); + ALIGN_AUDIOBUF(start, size); + if ((start && size) || pcm_get_more_int(&start, &size)) + { + pcm_apply_settings(); + logf(" pcm_play_dma_start_int"); + pcm_play_dma_start_int(start, size); + pcm_playing = true; + } + else + { + /* Force a stop */ + logf(" pcm_play_stop_int"); + pcm_play_stop_int(); + } pcm_play_unlock(); } @@ -329,20 +361,22 @@ void pcm_set_frequency(unsigned int samplerate) samplerate = pcm_sampr_to_hw_sampr(samplerate, type); #endif /* CONFIG_SAMPR_TYPES */ - index = round_value_to_list32(samplerate, hw_freq_sampr, - HW_NUM_FREQ, false); + mutex_lock(&sink_mutex); + struct pcm_sink* sink = sinks[cur_sink]; + index = round_value_to_list32(samplerate, sink->caps.samprs, sink->caps.num_samprs, false); - if (samplerate != hw_freq_sampr[index]) - index = HW_FREQ_DEFAULT; /* Invalid = default */ + if (samplerate != sink->caps.samprs[index]) + index = sink->caps.default_freq; /* Invalid = default */ - pcm_sampr = hw_freq_sampr[index]; - pcm_fsel = index; + sink->pending_freq = index; + mutex_unlock(&sink_mutex); } /* return last-set frequency */ unsigned int pcm_get_frequency(void) { - return pcm_sampr; + struct pcm_sink* sink = sinks[cur_sink]; + return sink->caps.samprs[sink->pending_freq]; } /* apply pcm settings to the hardware */ @@ -352,12 +386,14 @@ void pcm_apply_settings(void) pcm_wait_for_init(); - if (pcm_sampr != pcm_curr_sampr) - { - logf(" pcm_dma_apply_settings"); - pcm_dma_apply_settings(); - pcm_curr_sampr = pcm_sampr; + mutex_lock(&sink_mutex); + struct pcm_sink* sink = sinks[cur_sink]; + if(sink->pending_freq != sink->configured_freq) { + logf(" sink->set_freq"); + sink->ops.set_freq(sink->pending_freq); + sink->configured_freq = sink->pending_freq; } + mutex_unlock(&sink_mutex); } #ifdef HAVE_RECORDING diff --git a/firmware/pcm_sw_volume.c b/firmware/pcm_sw_volume.c index 61a66d650a..ae07bbd3ec 100644 --- a/firmware/pcm_sw_volume.c +++ b/firmware/pcm_sw_volume.c @@ -325,7 +325,7 @@ static void start_pcm(bool reframe) pcm_play_dma_status_callback(PCM_DMAST_STARTED); pcm_play_dma_status_callback(PCM_DMAST_STARTED); - pcm_play_dma_start(pcm_dbl_buf[1], pcm_dbl_buf_size[1]); + pcm_get_current_sink()->ops.play(pcm_dbl_buf[1], pcm_dbl_buf_size[1]); } void pcm_play_dma_start_int(const void *addr, size_t size) @@ -338,7 +338,7 @@ void pcm_play_dma_start_int(const void *addr, size_t size) void pcm_play_dma_stop_int(void) { - pcm_play_dma_stop(); + pcm_get_current_sink()->ops.stop(); src_buf_addr = NULL; src_buf_rem = 0; } diff --git a/firmware/target/arm/as3525/pcm-as3525.c b/firmware/target/arm/as3525/pcm-as3525.c index 8800602b47..a5a9ee0cee 100644 --- a/firmware/target/arm/as3525/pcm-as3525.c +++ b/firmware/target/arm/as3525/pcm-as3525.c @@ -31,6 +31,7 @@ #include "mmu-arm.h" #include "cpucache-arm.h" #include "pcm-internal.h" +#include "pcm_sink.h" #define MAX_TRANSFER (4*((1<<11)-1)) /* maximum data we can transfer via DMA * i.e. 32 bits at once (size of I2SO_DATA) @@ -53,13 +54,13 @@ static bool volatile is_recording = false; #endif /* Mask the DMA interrupt */ -void pcm_play_lock(void) +static void sink_lock(void) { ++locked; } /* Unmask the DMA interrupt if enabled */ -void pcm_play_unlock(void) +static void sink_unlock(void) { if(--locked == 0 && is_playing) { @@ -119,7 +120,7 @@ static void dma_callback(void) } } -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_start(const void *addr, size_t size) { is_playing = true; @@ -136,7 +137,7 @@ void pcm_play_dma_start(const void *addr, size_t size) play_start_pcm(); } -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { is_playing = false; @@ -153,20 +154,6 @@ void pcm_play_dma_stop(void) play_callback_pending = false; } -void pcm_play_dma_init(void) -{ - bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); - I2SOUT_CONTROL = (1<<6) | (1<<3); /* enable dma, stereo */ - - audiohw_preinit(); - pcm_dma_apply_settings(); -} - -void pcm_play_dma_postinit(void) -{ - audiohw_postinit(); -} - /* divider is 9 bits but the highest one (for 8kHz) fit in 8 bits */ static const unsigned char divider[SAMPR_NUM_FREQ] = { [HW_FREQ_96] = ((AS3525_MCLK_FREQ/128 + SAMPR_96/2) / SAMPR_96) - 1, @@ -183,12 +170,7 @@ static const unsigned char divider[SAMPR_NUM_FREQ] = { [HW_FREQ_8 ] = ((AS3525_MCLK_FREQ/128 + SAMPR_8 /2) / SAMPR_8 ) - 1, }; -static inline unsigned char mclk_divider(void) -{ - return divider[pcm_fsel]; -} - -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { bitmod32(&CGU_AUDIO, (0<<24) | /* I2SI_MCLK2PAD_EN = disabled */ @@ -196,11 +178,20 @@ void pcm_dma_apply_settings(void) (0<<14) | /* I2SI_MCLK_DIV_SEL = unused */ (0<<12) | /* I2SI_MCLK_SEL = clk_main */ (1<<11) | /* I2SO_MCLK_EN */ - (mclk_divider() << 2) | /* I2SO_MCLK_DIV_SEL */ + (divider[freq] << 2) | /* I2SO_MCLK_DIV_SEL */ (AS3525_MCLK_SEL << 0), /* I2SO_MCLK_SEL */ 0x01ffffff); } +static void sink_dma_init(void) +{ + bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); + I2SOUT_CONTROL = (1<<6) | (1<<3); /* enable dma, stereo */ + + audiohw_preinit(); + sink_set_freq(HW_SAMPR_DEFAULT); +} + #ifdef HAVE_PCM_DMA_ADDRESS void * pcm_dma_addr(void *addr) { @@ -210,6 +201,22 @@ void * pcm_dma_addr(void *addr) } #endif +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = audiohw_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; /**************************************************************************** ** Recording DMA transfer diff --git a/firmware/target/arm/imx233/pcm-imx233.c b/firmware/target/arm/imx233/pcm-imx233.c index 1588d2c874..05103f6a98 100644 --- a/firmware/target/arm/imx233/pcm-imx233.c +++ b/firmware/target/arm/imx233/pcm-imx233.c @@ -25,6 +25,8 @@ #include "dma-imx233.h" #include "pcm-internal.h" #include "audioout-imx233.h" +#include "pcm_sampr.h" +#include "pcm_sink.h" struct pcm_dma_command_t { @@ -47,7 +49,7 @@ static const void *dac_buf; /* current buffer */ static size_t dac_size; /* remaining size */ /* for both recording and playback: maximum transfer size, see - * pcm_dma_apply_settings */ + * sink_set_sampr */ static size_t dma_max_size = CACHEALIGN_UP(1600); enum @@ -110,33 +112,33 @@ void INT_DAC_ERROR(void) imx233_dma_clear_channel_interrupt(APB_AUDIO_DAC); } -void pcm_play_lock(void) +static void sink_lock(void) { if(dac_locked++ == 0) imx233_dma_enable_channel_interrupt(APB_AUDIO_DAC, false); } -void pcm_play_unlock(void) +static void sink_unlock(void) { if(--dac_locked == 0) imx233_dma_enable_channel_interrupt(APB_AUDIO_DAC, true); } -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { /* do not interrupt the current transaction because resetting the dma * would halt the DAC and clearing RUN causes sound havoc so simply * wait for the end of transfer */ - pcm_play_lock(); + sink_lock(); dac_buf = NULL; dac_size = 0; dac_state = DAC_STOP_PENDING; - pcm_play_unlock(); + sink_unlock(); } -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_start(const void *addr, size_t size) { - pcm_play_lock(); + sink_lock(); /* update pending buffer */ dac_buf = addr; dac_size = size; @@ -145,15 +147,10 @@ void pcm_play_dma_start(const void *addr, size_t size) play(); else dac_state = DAC_PLAYING; - pcm_play_unlock(); + sink_unlock(); } -void pcm_play_dma_init(void) -{ - audiohw_preinit(); -} - -void pcm_play_dma_postinit(void) +static void sink_dma_postinit(void) { audiohw_postinit(); imx233_icoll_enable_interrupt(INT_SRC_DAC_DMA, true); @@ -162,18 +159,35 @@ void pcm_play_dma_postinit(void) imx233_dma_enable_channel_interrupt(APB_AUDIO_DAC, true); } -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { - pcm_play_lock(); + sink_lock(); /* update frequency */ - audiohw_set_frequency(pcm_fsel); + audiohw_set_frequency(freq); /* compute maximum transfer size: aim at ~1/100s stop time maximum, make sure * the resulting value is a multiple of cache line. At sample rate F we * transfer two samples (2 x 2 bytes) F times per second = 4F b/s */ - dma_max_size = CACHEALIGN_UP(4 * pcm_sampr / 100); - pcm_play_unlock(); + dma_max_size = CACHEALIGN_UP(4 * hw_freq_sampr[freq] / 100); + sink_unlock(); } +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = audiohw_preinit, + .postinit = sink_dma_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; + /* * Recording */ diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c index 955301b4da..e9eae4d67c 100644 --- a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c @@ -26,6 +26,7 @@ #include "sdma-imx31.h" #include "mmu-imx31.h" #include "pcm-internal.h" +#include "pcm_sink.h" #define DMA_PLAY_CH_NUM 2 #define DMA_REC_CH_NUM 1 @@ -112,12 +113,12 @@ static void play_dma_callback(void) } } -void pcm_play_lock(void) +static void sink_lock(void) { ++dma_play_data.locked; } -void pcm_play_unlock(void) +static void sink_unlock(void) { if (--dma_play_data.locked == 0 && dma_play_data.state != 0) { @@ -131,12 +132,12 @@ void pcm_play_unlock(void) } } -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { - audiohw_set_frequency(pcm_fsel); + audiohw_set_frequency(freq); } -void pcm_play_dma_init(void) +static void sink_dma_init(void) { /* Init DMA channel information */ sdma_channel_init(DMA_PLAY_CH_NUM, &dma_play_cd, &dma_play_bd); @@ -146,11 +147,6 @@ void pcm_play_dma_init(void) audiohw_init(); } -void pcm_play_dma_postinit(void) -{ - audiohw_postinit(); -} - static void play_start_pcm(void) { /* Stop transmission (if in progress) */ @@ -200,7 +196,7 @@ static void play_stop_pcm(void) dma_play_data.callback_pending = 0; } -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_start(const void *addr, size_t size) { sdma_channel_stop(DMA_PLAY_CH_NUM); @@ -218,7 +214,7 @@ void pcm_play_dma_start(const void *addr, size_t size) play_start_dma(addr, size); } -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { sdma_channel_stop(DMA_PLAY_CH_NUM); play_stop_pcm(); @@ -229,6 +225,23 @@ void * pcm_dma_addr(void *addr) return (void *)addr_virt_to_phys((unsigned long)addr); } +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = audiohw_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; + #ifdef HAVE_RECORDING static struct buffer_descriptor dma_rec_bd NOCACHEBSS_ATTR; diff --git a/firmware/target/arm/pcm-telechips.c b/firmware/target/arm/pcm-telechips.c index 45044bc664..7f8829cda4 100644 --- a/firmware/target/arm/pcm-telechips.c +++ b/firmware/target/arm/pcm-telechips.c @@ -28,6 +28,7 @@ #include "i2s.h" #include "pcm.h" #include "pcm-internal.h" +#include "pcm_sink.h" struct dma_data { @@ -62,7 +63,7 @@ struct dma_data dma_play_data SHAREDBSS_ATTR = .state = 0 }; -void pcm_play_dma_init(void) +static void sink_dma_init(void) { DAVC = 0x0; /* Digital Volume = max */ #ifdef COWON_D2 @@ -89,12 +90,7 @@ void pcm_play_dma_init(void) #endif } -void pcm_play_dma_postinit(void) -{ - audiohw_postinit(); -} - -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { } @@ -125,7 +121,7 @@ static void play_stop_pcm(void) dma_play_data.state = 0; } -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_start(const void *addr, size_t size) { dma_play_data.p_r = addr; dma_play_data.size = size; @@ -140,7 +136,7 @@ void pcm_play_dma_start(const void *addr, size_t size) play_start_pcm(); } -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { play_stop_pcm(); dma_play_data.size = 0; @@ -149,7 +145,7 @@ void pcm_play_dma_stop(void) #endif } -void pcm_play_lock(void) +static void sink_lock(void) { int status = disable_fiq_save(); @@ -161,7 +157,7 @@ void pcm_play_lock(void) restore_fiq(status); } -void pcm_play_unlock(void) +static void sink_unlock(void) { int status = disable_fiq_save(); @@ -173,6 +169,23 @@ void pcm_play_unlock(void) restore_fiq(status); } +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = audiohw_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; + #ifdef HAVE_RECORDING /* TODO: implement */ void pcm_rec_dma_init(void) diff --git a/firmware/target/arm/pp/pcm-pp.c b/firmware/target/arm/pp/pcm-pp.c index 94b1c5ae10..7909464878 100644 --- a/firmware/target/arm/pp/pcm-pp.c +++ b/firmware/target/arm/pp/pcm-pp.c @@ -27,6 +27,7 @@ #include "pcm.h" #include "pcm_sampr.h" #include "pcm-internal.h" +#include "pcm_sink.h" /** DMA **/ @@ -89,10 +90,12 @@ static struct dma_data dma_play_data IBSS_ATTR = .state = 0 }; -void pcm_dma_apply_settings(void) -{ - audiohw_set_frequency(pcm_fsel); -} +/* DMA status cannot be viewed from outside code in control because that can + * clear the interrupt from outside the handler and prevent the handler from + * from being called. Split up transfers to a reasonable size that is good as + * a timer and peaking yet still keeps the FIQ count low. + */ +static uint16_t max_dma_chunk_size; #if defined(CPU_PP502x) /* 16-bit, L-R packed into 32 bits with left in the least significant halfword */ @@ -102,12 +105,6 @@ void pcm_dma_apply_settings(void) #define DMA_PLAY_CONFIG ((DMA_REQ_IIS << DMA_CMD_REQ_ID_POS) | \ DMA_CMD_RAM_TO_PER | DMA_CMD_SINGLE | \ DMA_CMD_WAIT_REQ | DMA_CMD_INTR) -/* DMA status cannot be viewed from outside code in control because that can - * clear the interrupt from outside the handler and prevent the handler from - * from being called. Split up transfers to a reasonable size that is good as - * a timer and peaking yet still keeps the FIQ count low. - */ -#define MAX_DMA_CHUNK_SIZE (pcm_curr_sampr >> 6) /* ~1/256 seconds */ static inline void dma_tx_init(void) { @@ -145,9 +142,9 @@ static inline unsigned long dma_tx_buf_prepare(const void *addr) static inline void dma_tx_start(bool begin) { - size_t size = MAX_DMA_CHUNK_SIZE; + size_t size = max_dma_chunk_size; - /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less + /* Not at least max_dma_chunk_size left or there would be less * than a FIFO's worth of data after this transfer? */ if (size + 16*4 > dma_play_data.size) size = dma_play_data.size; @@ -430,10 +427,15 @@ void fiq_playback(void) #endif /* ASM / C selection */ #endif /* CPU_PP502x */ +static void sink_set_freq(uint16_t freq) { + max_dma_chunk_size = hw_freq_sampr[freq] >> 6; + audiohw_set_frequency(freq); +} + /* For the locks, FIQ must be disabled because the handler manipulates IISCONFIG and the operation is not atomic - dual core support will require other measures */ -void pcm_play_lock(void) +static void sink_lock(void) { int status = disable_fiq_save(); @@ -444,7 +446,7 @@ void pcm_play_lock(void) restore_fiq(status); } -void pcm_play_unlock(void) +static void sink_unlock(void) { int status = disable_fiq_save(); @@ -455,14 +457,14 @@ void pcm_play_unlock(void) restore_fiq(status); } -static void play_start_pcm(void) +static void sink_start_pcm(void) { fiq_function = fiq_playback; dma_play_data.state = 1; dma_tx_start(true); } -static void play_stop_pcm(void) +static void sink_stop_pcm(void) { dma_tx_stop(); @@ -472,9 +474,11 @@ static void play_stop_pcm(void) dma_play_data.state = 0; } -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_stop(void); + +static void sink_dma_start(const void *addr, size_t size) { - pcm_play_dma_stop(); + sink_dma_stop(); #if NUM_CORES > 1 /* This will become more important later - and different ! */ @@ -485,13 +489,13 @@ void pcm_play_dma_start(const void *addr, size_t size) dma_play_data.addr = dma_tx_buf_prepare(addr); dma_play_data.size = size; - play_start_pcm(); + sink_start_pcm(); } /* Stops the DMA transfer and interrupt */ -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { - play_stop_pcm(); + sink_stop_pcm(); dma_play_data.addr = 0; dma_play_data.size = 0; #if NUM_CORES > 1 @@ -499,7 +503,7 @@ void pcm_play_dma_stop(void) #endif } -void pcm_play_dma_init(void) +static void sink_dma_init(void) { /* Initialize default register values. */ audiohw_init(); @@ -509,10 +513,22 @@ void pcm_play_dma_init(void) IISCONFIG |= IIS_TXFIFOEN; } -void pcm_play_dma_postinit(void) -{ - audiohw_postinit(); -} +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = audiohw_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; /**************************************************************************** ** Recording DMA transfer diff --git a/firmware/target/arm/rk27xx/pcm-rk27xx.c b/firmware/target/arm/rk27xx/pcm-rk27xx.c index 26e3ad87ba..af81f1e7e4 100644 --- a/firmware/target/arm/rk27xx/pcm-rk27xx.c +++ b/firmware/target/arm/rk27xx/pcm-rk27xx.c @@ -28,11 +28,12 @@ #include "audiohw.h" #include "sound.h" #include "pcm-internal.h" +#include "pcm_sink.h" static int locked = 0; /* Mask the DMA interrupt */ -void pcm_play_lock(void) +static void sink_lock(void) { if (++locked == 1) { @@ -43,7 +44,7 @@ void pcm_play_lock(void) } /* Unmask the DMA interrupt if enabled */ -void pcm_play_unlock(void) +static void sink_unlock(void) { if(--locked == 0) { @@ -53,7 +54,7 @@ void pcm_play_unlock(void) } } -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { HDMA_CON0 = 0x00; HDMA_ISR = 0x00; @@ -105,10 +106,10 @@ static void hdma_i2s_transfer(const void *addr, size_t size) (1<<0)); /* hardware trigger DMA mode */ } -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_start(const void *addr, size_t size) { /* Stop any DMA in progress */ - pcm_play_dma_stop(); + sink_dma_stop(); /* kick in DMA transfer */ hdma_i2s_transfer(addr, size); @@ -225,7 +226,7 @@ static void set_codec_freq(unsigned int freq) } #endif -void pcm_play_dma_init(void) +static void sink_dma_init(void) { /* unmask HDMA interrupt in INTC */ INTC_IMR |= IRQ_ARM_HDMA; @@ -236,18 +237,13 @@ void pcm_play_dma_init(void) i2s_init(); } -void pcm_play_dma_postinit(void) -{ - audiohw_postinit(); -} - -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { #ifdef CODEC_SLAVE - set_codec_freq(pcm_fsel); + set_codec_freq(freq); #endif - audiohw_set_frequency(pcm_fsel); + audiohw_set_frequency(freq); } /* audio DMA ISR called when chunk from callers buffer has been transfered */ @@ -263,6 +259,23 @@ void INT_HDMA(void) } } +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = audiohw_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; + /**************************************************************************** ** Recording DMA transfer **/ diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c index e75c3fe7c6..7567627213 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c +++ b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c @@ -26,6 +26,7 @@ #include "sound.h" #include "file.h" #include "pcm-internal.h" +#include "pcm_sink.h" /* PCM interrupt routine lockout */ static struct @@ -48,20 +49,20 @@ static struct void fiq_handler(void) __attribute__((interrupt ("FIQ"))); /* Mask the DMA interrupt */ -void pcm_play_lock(void) +static void sink_lock(void) { if (++dma_play_lock.locked == 1) bitset32(&INTMSK, DMA2_MASK); } /* Unmask the DMA interrupt if enabled */ -void pcm_play_unlock(void) +static void sink_unlock(void) { if (--dma_play_lock.locked == 0) bitclr32(&INTMSK, dma_play_lock.state); } -void pcm_play_dma_init(void) +static void sink_dma_init(void) { /* There seem to be problems when changing the IIS interface configuration * when a clock is not present. @@ -94,14 +95,9 @@ void pcm_play_dma_init(void) bitset32(&INTMOD, DMA2_MASK); } -void pcm_play_dma_postinit(void) +static void sink_set_freq(uint16_t freq) { - audiohw_postinit(); -} - -void pcm_dma_apply_settings(void) -{ - audiohw_set_frequency(pcm_fsel); + audiohw_set_frequency(freq); } /* Connect the DMA and start filling the FIFO */ @@ -158,7 +154,7 @@ static void play_stop_pcm(void) IISCON &= ~(1<<0); } -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_start(const void *addr, size_t size) { /* Enable the IIS clock */ bitset32(&CLKCON, 1<<17); @@ -187,7 +183,7 @@ void pcm_play_dma_start(const void *addr, size_t size) } /* Promptly stop DMA transfers and stop IIS */ -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { play_stop_pcm(); @@ -219,3 +215,20 @@ void fiq_handler(void) pcm_play_dma_status_callback(PCM_DMAST_STARTED); } + +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = audiohw_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; diff --git a/firmware/target/arm/s5l8700/pcm-s5l8700.c b/firmware/target/arm/s5l8700/pcm-s5l8700.c index fa7ff75ada..0cc2691719 100644 --- a/firmware/target/arm/s5l8700/pcm-s5l8700.c +++ b/firmware/target/arm/s5l8700/pcm-s5l8700.c @@ -32,11 +32,12 @@ #include "dma-target.h" #include "mmu-arm.h" #include "cpucache-arm.h" +#include "pcm_sink.h" /* Driver for the IIS/PCM part of the s5l8700 using DMA Notes: - - pcm_play_dma_stop is untested, not sure if implemented the right way + - sink_dma_stop is untested, not sure if implemented the right way - recording is not implemented */ @@ -83,7 +84,7 @@ static const struct div_entry { }; /* Mask the DMA interrupt */ -void pcm_play_lock(void) +static void sink_lock(void) { if (locked++ == 0) { INTMSK &= ~(1 << 10); @@ -91,7 +92,7 @@ void pcm_play_lock(void) } /* Unmask the DMA interrupt if enabled */ -void pcm_play_unlock(void) +static void sink_unlock(void) { if (--locked == 0) { INTMSK |= (1 << 10); @@ -141,7 +142,7 @@ void INT_DMA(void) } -void pcm_play_dma_start(const void* addr, size_t size) +static void sink_dma_start(const void* addr, size_t size) { /* DMA channel on */ nextbuf = addr; @@ -161,7 +162,7 @@ void pcm_play_dma_start(const void* addr, size_t size) (0 << 0); /* 0 = LRCK on */ } -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { /* DMA channel off */ DMACOM0 = 5; @@ -195,7 +196,7 @@ static void pcm_dma_set_freq(enum hw_freq_indexes idx) (div.cdiv - 1); /* MCLK_DIV_VAL */ } -void pcm_play_dma_init(void) +static void sink_dma_init(void) { /* configure IIS pins */ #ifdef IPOD_NANO2G @@ -252,15 +253,10 @@ void pcm_play_dma_init(void) audiohw_preinit(); } -void pcm_play_dma_postinit(void) -{ - audiohw_postinit(); -} - /* set the configured PCM frequency */ -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { - pcm_dma_set_freq(pcm_fsel); + pcm_dma_set_freq(hw_freq_sampr[freq]); } #ifdef HAVE_PCM_DMA_ADDRESS @@ -272,6 +268,22 @@ void * pcm_dma_addr(void *addr) } #endif +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = audiohw_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; /**************************************************************************** ** Recording DMA transfer diff --git a/firmware/target/arm/s5l8702/pcm-s5l8702.c b/firmware/target/arm/s5l8702/pcm-s5l8702.c index 0f532cc1bf..a10479e95e 100644 --- a/firmware/target/arm/s5l8702/pcm-s5l8702.c +++ b/firmware/target/arm/s5l8702/pcm-s5l8702.c @@ -31,6 +31,7 @@ #include "pcm_sampr.h" #include "pcm-target.h" #include "dma-s5l8702.h" +#include "pcm_sink.h" /* DMA configuration */ @@ -86,14 +87,14 @@ static int active_dblbuf; size_t pcm_remaining; /* Mask the DMA interrupt */ -void pcm_play_lock(void) +static void sink_lock(void) { if (locked++ == 0) dmac_ch_lock_int(&dma_play_ch); } /* Unmask the DMA interrupt if enabled */ -void pcm_play_unlock(void) +static void sink_unlock(void) { if (--locked == 0) dmac_ch_unlock_int(&dma_play_ch); @@ -141,30 +142,29 @@ static void dma_play_callback(void *cb_data) pcm_play_dma_status_callback(PCM_DMAST_STARTED); } -void pcm_play_dma_start(const void* addr, size_t size) +static void sink_dma_stop(void) { - pcm_play_dma_stop(); + dmac_ch_stop(&dma_play_ch); + I2STXCOM = 0xa; +} + +static void sink_dma_start(const void* addr, size_t size) +{ + sink_dma_stop(); pcm_remaining = size; I2STXCOM = 0xe; dma_play_callback((void*)addr); } -void pcm_play_dma_stop(void) -{ - dmac_ch_stop(&dma_play_ch); - I2STXCOM = 0xa; -} - /* MCLK = 12MHz (MCLKDIV2=1), [CS42L55 DS, s4.8] */ #define MCLK_FREQ 12000000 /* set the configured PCM frequency */ -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { static uint16_t last_clkcon3l = 0; uint16_t clkcon3l; - int fsel; /* For unknown reasons, s5l8702 I2S controller does not synchronize * with CS42L55 at 32000 Hz. To fix it, the CODEC is configured with @@ -172,12 +172,11 @@ void pcm_dma_apply_settings(void) * obtaining 32 KHz in LRCK controller input and 8 MHz in SCLK input. * OF uses this trick. */ - if (pcm_fsel == HW_FREQ_32) { - fsel = HW_FREQ_48; + if (freq == HW_FREQ_32) { + freq = HW_FREQ_48; clkcon3l = 0x3028; /* PLL2 / 3 / 9 -> 8 MHz */ } else { - fsel = pcm_fsel; clkcon3l = 0; /* OSC0 -> 12 MHz */ } @@ -192,12 +191,12 @@ void pcm_dma_apply_settings(void) } /* configure I2S clock ratio */ - I2SCLKDIV = MCLK_FREQ / hw_freq_sampr[fsel]; + I2SCLKDIV = MCLK_FREQ / hw_freq_sampr[freq]; /* select CS42L55 sample rate */ - audiohw_set_frequency(fsel); + audiohw_set_frequency(freq); } -void pcm_play_dma_init(void) +static void sink_dma_init(void) { PWRCON(1) &= ~(1 << 7); @@ -207,12 +206,7 @@ void pcm_play_dma_init(void) I2SCLKCON = 1; audiohw_preinit(); - pcm_dma_apply_settings(); -} - -void pcm_play_dma_postinit(void) -{ - audiohw_postinit(); + sink_set_freq(HW_FREQ_DEFAULT); } #ifdef HAVE_PCM_DMA_ADDRESS @@ -222,6 +216,22 @@ void * pcm_dma_addr(void *addr) } #endif +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = audiohw_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; /**************************************************************************** ** Recording DMA transfer diff --git a/firmware/target/arm/stm32/pcm-stm32h7.c b/firmware/target/arm/stm32/pcm-stm32h7.c index e9d9696d19..472ab4a3b9 100644 --- a/firmware/target/arm/stm32/pcm-stm32h7.c +++ b/firmware/target/arm/stm32/pcm-stm32h7.c @@ -20,37 +20,57 @@ ****************************************************************************/ #include "pcm.h" #include "pcm-internal.h" +#include "pcm_sampr.h" +#include "pcm_sink.h" -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) +{ + (void)freq; +} + +static void sink_dma_init(void) { } -void pcm_play_dma_init(void) +static void sink_dma_postinit(void) { } -void pcm_play_dma_postinit(void) -{ -} - -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_start(const void *addr, size_t size) { (void)addr; (void)size; } -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { } -void pcm_play_lock(void) +static void sink_lock(void) { } -void pcm_play_unlock(void) +static void sink_unlock(void) { } +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = sink_dma_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; + #ifdef HAVE_RECORDING void pcm_rec_dma_init(void) { diff --git a/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c b/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c index 7befeb40c0..c46fa00022 100644 --- a/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c +++ b/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c @@ -28,13 +28,14 @@ #include "dsp-target.h" #include "dsp/ipc.h" #include "pcm-internal.h" +#include "pcm_sink.h" /* This is global to save some latency when pcm_play_dma_get_peak_buffer is * called. */ static const void *start; -void pcm_play_dma_postinit(void) +static void sink_dma_postinit(void) { /* Configure clock divider */ tsc2100_writereg(CONTROL_PAGE2, TSPP1_ADDRESS, 0x1120); @@ -45,7 +46,7 @@ void pcm_play_dma_postinit(void) audiohw_postinit(); } -void pcm_play_dma_init(void) +static void sink_dma_init(void) { IO_INTC_IRQ0 = INTR_IRQ0_IMGBUF; bitset16(&IO_INTC_EINT0, INTR_EINT0_IMGBUF); @@ -67,15 +68,15 @@ void pcm_play_dma_init(void) dsp_wake(); } -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { - audiohw_set_frequency(pcm_fsel); + audiohw_set_frequency(freq); } /* Note that size is actually limited to the size of a short right now due to * the implementation on the DSP side (and the way that we access it) */ -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_start(const void *addr, size_t size) { unsigned long sdem_addr=(unsigned long)addr - CONFIG_SDRAM_START; /* Initialize codec. */ @@ -87,18 +88,18 @@ void pcm_play_dma_start(const void *addr, size_t size) dsp_wake(); } -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { DSP_(_dma0_stopped)=1; dsp_wake(); } -void pcm_play_lock(void) +static void sink_lock(void) { } -void pcm_play_unlock(void) +static void sink_unlock(void) { } @@ -156,4 +157,20 @@ void DSPHINT(void) DEBUGF("DSP: %s", buffer); } - + +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = sink_dma_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; diff --git a/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c index 95c3e456fa..7069d0b0e6 100644 --- a/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c +++ b/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c @@ -29,6 +29,7 @@ #include "dsp/ipc.h" #include "pcm-internal.h" #include "dma-target.h" +#include "pcm_sink.h" /* This is global to save some latency when pcm_play_dma_get_peak_buffer is * called. @@ -36,12 +37,7 @@ static const void *start; static int dma_channel; -void pcm_play_dma_postinit(void) -{ - audiohw_postinit(); -} - -void pcm_play_dma_init(void) +static void sink_dma_init(void) { /* GIO16 is DSP/AIC3X CLK */ IO_GIO_FSEL0 &= 0x3FFF; @@ -74,15 +70,15 @@ void pcm_play_dma_init(void) dsp_wake(); } -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { - audiohw_set_frequency(pcm_fsel); + audiohw_set_frequency(freq); } /* Note that size is actually limited to the size of a short right now due to * the implementation on the DSP side (and the way that we access it) */ -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_start(const void *addr, size_t size) { unsigned long sdem_addr=(unsigned long)addr - CONFIG_SDRAM_START; /* Initialize codec. */ @@ -94,18 +90,18 @@ void pcm_play_dma_start(const void *addr, size_t size) dsp_wake(); } -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { DSP_(_dma0_stopped)=1; dsp_wake(); } -void pcm_play_lock(void) +static void sink_lock(void) { } -void pcm_play_unlock(void) +static void sink_unlock(void) { } @@ -163,4 +159,20 @@ void DSPHINT(void) DEBUGF("DSP: %s", buffer); } - + +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = audiohw_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; diff --git a/firmware/target/coldfire/pcm-coldfire.c b/firmware/target/coldfire/pcm-coldfire.c index 36eb00c31b..6c4d701d5b 100644 --- a/firmware/target/coldfire/pcm-coldfire.c +++ b/firmware/target/coldfire/pcm-coldfire.c @@ -29,6 +29,7 @@ #include "spdif.h" #endif #include "pcm-internal.h" +#include "pcm_sink.h" #define IIS_PLAY_DEFPARM ( (freq_ent[FPARM_CLOCKSEL] << 12) | \ (IIS_PLAY & (7 << 8)) | \ @@ -142,12 +143,12 @@ static void iis_play_reset_if_playback(bool if_playback) /* This clears the reset bit to enable monitoring immediately if monitoring recording sources or always if playback is in progress - we might be switching samplerates on the fly */ -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { int level = set_irq_level(DMA_IRQ_LEVEL); /* remember table entry */ - freq_ent = pcm_freq_parms[pcm_fsel]; + freq_ent = pcm_freq_parms[freq]; /* Reprogramming bits 15-12 requires FIFO to be in a reset condition - Users Manual 17-8, Note 11 */ @@ -159,7 +160,7 @@ void pcm_dma_apply_settings(void) IIS_PLAY = IIS_PLAY_DEFPARM | IIS_FIFO_RESET; restore_irq(level); - audiohw_set_frequency(pcm_fsel); + audiohw_set_frequency(freq); coldfire_set_pllcr_audio_bits(PLLCR_SET_AUDIO_BITS_DEFPARM); level = set_irq_level(DMA_IRQ_LEVEL); @@ -170,11 +171,11 @@ void pcm_dma_apply_settings(void) PDOR3 = 0; /* Kick FIFO out of reset by writing to it */ restore_irq(level); -} /* pcm_dma_apply_settings */ +} /* sink_set_sampr */ -void pcm_play_dma_init(void) +static void sink_dma_init(void) { - freq_ent = pcm_freq_parms[pcm_fsel]; + freq_ent = pcm_freq_parms[HW_FREQ_DEFAULT]; AUDIOGLOB = AUDIOGLOB_DEFPARM; DIVR0 = 54; /* DMA0 is mapped into vector 54 in system.c */ @@ -195,15 +196,15 @@ void pcm_play_dma_init(void) audio_input_mux(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); - audiohw_set_frequency(pcm_fsel); + audiohw_set_frequency(HW_FREQ_DEFAULT); coldfire_set_pllcr_audio_bits(PLLCR_SET_AUDIO_BITS_DEFPARM); #if defined(HAVE_SPDIF_REC) || defined(HAVE_SPDIF_OUT) spdif_init(); #endif -} /* pcm_play_dma_init */ +} /* sink_dma_init */ -void pcm_play_dma_postinit(void) +void sink_dma_postinit(void) { audiohw_postinit(); iis_play_reset(); @@ -223,23 +224,35 @@ static struct dma_lock dma_play_lock = .state = (1 << 14) /* bit 14 is DMA0 */ }; -void pcm_play_lock(void) +static void sink_lock(void) { if (++dma_play_lock.locked == 1) coldfire_imr_mod(1 << 14, 1 << 14); } -void pcm_play_unlock(void) +static void sink_unlock(void) { if (--dma_play_lock.locked == 0) coldfire_imr_mod(dma_play_lock.state, 1 << 14); } +/* Stops the DMA transfer and interrupt */ +static void sink_dma_stop(void) +{ + and_l(~(DMA_EEXT | DMA_INT), &DCR0); /* per request and int OFF */ + BCR0 = 0; /* No bytes remaining */ + DSR0 = 1; /* Clear interrupt, errors, stop transfer */ + + iis_play_reset_if_playback(true); + + dma_play_lock.state = (1 << 14); +} /* sink_dma_stop */ + /* Set up the DMA transfer that kicks in when the audio FIFO gets empty */ -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_start(const void *addr, size_t size) { /* Stop any DMA in progress */ - pcm_play_dma_stop(); + sink_dma_stop(); /* Set up DMA transfer */ SAR0 = (unsigned long)addr; /* Source address */ @@ -250,19 +263,7 @@ void pcm_play_dma_start(const void *addr, size_t size) DMA_SSIZE(DMA_SIZE_LINE) | DMA_START; dma_play_lock.state = (0 << 14); -} /* pcm_play_dma_start */ - -/* Stops the DMA transfer and interrupt */ -void pcm_play_dma_stop(void) -{ - and_l(~(DMA_EEXT | DMA_INT), &DCR0); /* per request and int OFF */ - BCR0 = 0; /* No bytes remaining */ - DSR0 = 1; /* Clear interrupt, errors, stop transfer */ - - iis_play_reset_if_playback(true); - - dma_play_lock.state = (1 << 14); -} /* pcm_play_dma_stop */ +} /* sink_dma_start */ /* DMA0 Interrupt is called when the DMA has finished transfering a chunk from the caller's buffer */ @@ -301,6 +302,23 @@ void DMA0(void) /* else inished playing */ } /* DMA0 */ +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = sink_dma_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; + #ifdef HAVE_RECORDING /**************************************************************************** ** Recording DMA transfer diff --git a/firmware/target/hosted/android/pcm-android.c b/firmware/target/hosted/android/pcm-android.c index c23b802b2d..189099eecc 100644 --- a/firmware/target/hosted/android/pcm-android.c +++ b/firmware/target/hosted/android/pcm-android.c @@ -28,6 +28,8 @@ #include "debug.h" #include "pcm.h" #include "pcm-internal.h" +#include "pcm_sampr.h" +#include "pcm_sink.h" extern JNIEnv *env_ptr; @@ -133,23 +135,24 @@ Java_org_rockbox_RockboxPCM_nativeWrite(JNIEnv *env, jobject this, return max_size - left; } -void pcm_play_lock(void) +static void sink_lock(void) { if (++audio_locked == 1) lock_audio(); } -void pcm_play_unlock(void) +static void sink_unlock(void) { if (--audio_locked == 0) unlock_audio(); } -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { + (void)freq; } -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_start(const void *addr, size_t size) { pcm_data_start = addr; pcm_data_size = size; @@ -160,7 +163,7 @@ void pcm_play_dma_start(const void *addr, size_t size) 0); } -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { /* NOTE: due to how pcm_play_dma_complete_callback() works, this is * possibly called from nativeWrite(), i.e. another (host) thread @@ -171,7 +174,7 @@ void pcm_play_dma_stop(void) stop_method); } -void pcm_play_dma_init(void) +static void sink_dma_init(void) { /* in order to have background music playing after leaving the activity, * we need to allocate the PCM object from the Rockbox thread (the Activity @@ -194,7 +197,7 @@ void pcm_play_dma_init(void) write_method = e->GetMethodID(env_ptr, RockboxPCM_class, "write", "([BII)I"); } -void pcm_play_dma_postinit(void) +static void sink_dma_postinit(void) { } @@ -213,6 +216,23 @@ void pcm_shutdown(void) pthread_mutex_destroy(&audio_lock_mutex); } +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = sink_dma_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; + JNIEXPORT void JNICALL Java_org_rockbox_RockboxPCM_postVolumeChangedEvent(JNIEnv *env, jobject this, diff --git a/firmware/target/hosted/ctru/pcm-ctru.c b/firmware/target/hosted/ctru/pcm-ctru.c index d7c9d36458..da941ecc31 100644 --- a/firmware/target/hosted/ctru/pcm-ctru.c +++ b/firmware/target/hosted/ctru/pcm-ctru.c @@ -45,6 +45,7 @@ #include "pcm-internal.h" #include "pcm_sampr.h" #include "pcm_mixer.h" +#include "pcm_sink.h" #include <3ds/ndsp/ndsp.h> #include <3ds/ndsp/channel.h> @@ -102,14 +103,14 @@ static inline bool is_in_audio_thread(int audio_thread_id) return false; } -void pcm_play_lock(void) +static void sink_lock(void) { if (!is_in_audio_thread(_pcm_thread_id)) { RecursiveLock_Lock(&_pcm_lock_mtx); } } -void pcm_play_unlock(void) +static void sink_unlock(void) { if (!is_in_audio_thread(_pcm_thread_id)) { RecursiveLock_Unlock(&_pcm_lock_mtx); @@ -188,13 +189,13 @@ void dsp_callback(void *const nul_) { LightEvent_Signal(&_dsp_callback_event); } -static void pcm_dma_apply_settings_nolock(void) +static void sink_set_freq_nolock(uint16_t freq) { ndspChnReset(0); ndspSetOutputMode(NDSP_OUTPUT_STEREO); - ndspChnSetRate(0, pcm_sampr); + ndspChnSetRate(0, hw_freq_sampr[freq]); ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16); /* ndspChnSetInterp(0, NDSP_INTERP_POLYPHASE); */ /* ndspChnSetInterp(0, NDSP_INTERP_NONE); */ @@ -235,7 +236,7 @@ static void pcm_dma_apply_settings_nolock(void) -1, /* run on any core */ false); } -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_start(const void *addr, size_t size) { _pcm_buffer = addr; _pcm_buffer_size = size; @@ -245,7 +246,7 @@ void pcm_play_dma_start(const void *addr, size_t size) RecursiveLock_Unlock(&_pcm_lock_mtx); } -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { RecursiveLock_Lock(&_pcm_lock_mtx); ndspChnSetPaused(0, true); @@ -303,7 +304,7 @@ unsigned long spdif_measure_frequency(void) #endif /* HAVE_RECORDING */ -void pcm_play_dma_init(void) +static void sink_dma_init(void) { Result ndsp_init_res = ndspInit(); if (R_FAILED(ndsp_init_res)) { @@ -319,15 +320,15 @@ void pcm_play_dma_init(void) LightEvent_Init(&_dsp_callback_event, RESET_ONESHOT); } -void pcm_play_dma_postinit(void) +static void sink_dma_postinit(void) { } -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { - pcm_play_lock(); - pcm_dma_apply_settings_nolock(); - pcm_play_unlock(); + sink_lock(); + sink_set_freq_nolock(freq); + sink_unlock(); } void pcm_close_device(void) @@ -357,3 +358,20 @@ void audiohw_close(void) { pcm_close_device(); } + +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = sink_dma_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; diff --git a/firmware/target/hosted/ibasso/pcm-ibasso.c b/firmware/target/hosted/ibasso/pcm-ibasso.c index 7857e60b6d..ea5651ac7e 100644 --- a/firmware/target/hosted/ibasso/pcm-ibasso.c +++ b/firmware/target/hosted/ibasso/pcm-ibasso.c @@ -31,6 +31,8 @@ #include "panic.h" #include "pcm.h" #include "pcm-internal.h" +#include "pcm_sampr.h" +#include "pcm_sink.h" #include "sound/asound.h" #include "tinyalsa/asoundlib.h" @@ -187,26 +189,26 @@ static const unsigned int DEVICE = 0; static struct pcm_config _config; -void pcm_play_dma_init(void) +static void sink_dma_init(void) { TRACE; #ifdef DEBUG /* - DEBUG pcm_play_dma_init: Access: 0x000009 - DEBUG pcm_play_dma_init: Format[0]: 0x000044 - DEBUG pcm_play_dma_init: Format[1]: 0x000010 - DEBUG pcm_play_dma_init: Format: S16_LE - DEBUG pcm_play_dma_init: Format: S24_LE - DEBUG pcm_play_dma_init: Format: S20_3LE - DEBUG pcm_play_dma_init: Subformat: 0x000001 - DEBUG pcm_play_dma_init: Rate: min = 8000Hz, max = 192000Hz - DEBUG pcm_play_dma_init: Channels: min = 2, max = 2 - DEBUG pcm_play_dma_init: Sample bits: min=16, max=32 - DEBUG pcm_play_dma_init: Period size: min=8, max=10922 - DEBUG pcm_play_dma_init: Period count: min=3, max=128 - DEBUG pcm_play_dma_init: 0 mixer controls. + DEBUG sink_dma_init: Access: 0x000009 + DEBUG sink_dma_init: Format[0]: 0x000044 + DEBUG sink_dma_init: Format[1]: 0x000010 + DEBUG sink_dma_init: Format: S16_LE + DEBUG sink_dma_init: Format: S24_LE + DEBUG sink_dma_init: Format: S20_3LE + DEBUG sink_dma_init: Subformat: 0x000001 + DEBUG sink_dma_init: Rate: min = 8000Hz, max = 192000Hz + DEBUG sink_dma_init: Channels: min = 2, max = 2 + DEBUG sink_dma_init: Sample bits: min=16, max=32 + DEBUG sink_dma_init: Period size: min=8, max=10922 + DEBUG sink_dma_init: Period count: min=3, max=128 + DEBUG sink_dma_init: 0 mixer controls. */ struct pcm_params* params = pcm_params_get(CARD, DEVICE, PCM_OUT); @@ -310,7 +312,7 @@ void pcm_play_dma_init(void) pcm_thread_run relies on this size match. See pcm_mixer.h. */ _config.channels = 2; - _config.rate = pcm_sampr; + _config.rate = hw_freq_sampr[HW_FREQ_DEFAULT]; _config.period_size = 256; _config.period_count = 4; _config.format = PCM_FORMAT_S16_LE; @@ -337,7 +339,7 @@ void pcm_play_dma_init(void) } -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_start(const void *addr, size_t size) { TRACE; @@ -364,7 +366,7 @@ void pcm_play_dma_start(const void *addr, size_t size) pthread_mutex_unlock(&_dma_suspended_mtx); } -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { TRACE; @@ -375,11 +377,11 @@ void pcm_play_dma_stop(void) } -/* Unessecary play locks before pcm_play_dma_postinit. */ +/* Unessecary play locks before sink_dma_postinit. */ static int _play_lock_recursion_count = -10000; -void pcm_play_dma_postinit(void) +static void sink_dma_postinit(void) { TRACE; @@ -387,7 +389,7 @@ void pcm_play_dma_postinit(void) } -void pcm_play_lock(void) +static void sink_lock(void) { TRACE; @@ -402,7 +404,7 @@ void pcm_play_lock(void) } -void pcm_play_unlock(void) +static void sink_unlock(void) { TRACE; @@ -418,9 +420,9 @@ void pcm_play_unlock(void) } -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { - unsigned int rate = pcm_get_frequency(); + unsigned int rate = hw_freq_sampr[freq]; DEBUGF("DEBUG %s: Current sample rate: %u, next sampe rate: %u.", __func__, _config.rate, rate); @@ -450,3 +452,20 @@ void pcm_close_device(void) pcm_close(_alsa_handle); _alsa_handle = NULL; } + +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = sink_dma_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; diff --git a/firmware/target/hosted/pcm-alsa.c b/firmware/target/hosted/pcm-alsa.c index 9286369a25..7e4833fdfe 100644 --- a/firmware/target/hosted/pcm-alsa.c +++ b/firmware/target/hosted/pcm-alsa.c @@ -48,6 +48,7 @@ #include "pcm-internal.h" #include "pcm_mixer.h" #include "pcm_sampr.h" +#include "pcm_sink.h" #include "audiohw.h" #include "pcm-alsa.h" #include "fixedpoint.h" @@ -122,7 +123,7 @@ void pcm_alsa_set_capture_device(const char *device) } #endif -static int set_hwparams(snd_pcm_t *handle) +static int set_hwparams(snd_pcm_t *handle, unsigned long sampr) { int err; unsigned int srate; @@ -136,10 +137,10 @@ static int set_hwparams(snd_pcm_t *handle) Note these are in FRAMES, and are sized to be about 8.5ms for the buffer and 2.1ms for the period */ - if (pcm_sampr > SAMPR_96) { + if (sampr > SAMPR_96) { buffer_size = MIX_FRAME_SAMPLES * 4 * 4; period_size = MIX_FRAME_SAMPLES * 4; - } else if (pcm_sampr > SAMPR_48) { + } else if (sampr > SAMPR_48) { buffer_size = MIX_FRAME_SAMPLES * 2 * 4; period_size = MIX_FRAME_SAMPLES * 2; } else { @@ -176,17 +177,17 @@ static int set_hwparams(snd_pcm_t *handle) goto error; } /* set the stream rate */ - srate = pcm_sampr; + srate = sampr; err = snd_pcm_hw_params_set_rate_near(handle, params, &srate, 0); if (err < 0) { - logf("Rate %luHz not available for playback: %s", pcm_sampr, snd_strerror(err)); + logf("Rate %luHz not available for playback: %s", sampr, snd_strerror(err)); goto error; } real_sample_rate = srate; - if (real_sample_rate != pcm_sampr) + if (real_sample_rate != sampr) { - logf("Rate doesn't match (requested %luHz, get %dHz)", pcm_sampr, real_sample_rate); + logf("Rate doesn't match (requested %luHz, get %dHz)", sampr, real_sample_rate); err = -EINVAL; goto error; } @@ -595,7 +596,7 @@ static void open_hwdev(const char *device, snd_pcm_stream_t mode) atexit(alsadev_cleanup); } -void pcm_play_dma_init(void) +static void sink_dma_init(void) { logf("PCM DMA Init"); @@ -606,48 +607,50 @@ void pcm_play_dma_init(void) return; } -void pcm_play_lock(void) +static void sink_lock(void) { pthread_mutex_lock(&pcm_mtx); } -void pcm_play_unlock(void) +static void sink_unlock(void) { pthread_mutex_unlock(&pcm_mtx); } -static void pcm_dma_apply_settings_nolock(void) +static void sink_set_freq_nolock(uint16_t freq) { - logf("PCM DMA Settings %d %lu", last_sample_rate, pcm_sampr); + unsigned int sampr = hw_freq_sampr[freq]; - if (last_sample_rate != pcm_sampr) + logf("PCM DMA Settings %d %lu", last_sample_rate, sampr); + + if (last_sample_rate != sampr) { - last_sample_rate = pcm_sampr; + last_sample_rate = sampr; #ifdef AUDIOHW_MUTE_ON_SRATE_CHANGE audiohw_mute(true); #endif snd_pcm_drop(handle); - set_hwparams(handle); // FIXME: check return code? + set_hwparams(handle, sampr); // FIXME: check return code? set_swparams(handle); // FIXME: check return code? #if defined(HAVE_NWZ_LINUX_CODEC) /* Sony NWZ linux driver uses a nonstandard mecanism to set the sampling rate */ - audiohw_set_frequency(pcm_sampr); + audiohw_set_frequency(sampr); #endif /* (Will be unmuted by pcm resuming) */ } } -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { - pcm_play_lock(); - pcm_dma_apply_settings_nolock(); - pcm_play_unlock(); + sink_lock(); + sink_set_freq_nolock(freq); + sink_unlock(); } -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { logf("PCM DMA stop (%d)", snd_pcm_state(handle)); @@ -660,10 +663,9 @@ void pcm_play_dma_stop(void) #endif } -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_start(const void *addr, size_t size) { logf("PCM DMA start (%p %d)", addr, size); - pcm_dma_apply_settings_nolock(); pcm_data = addr; pcm_size = size; @@ -749,7 +751,7 @@ void pcm_play_dma_start(const void *addr, size_t size) } } -void pcm_play_dma_postinit(void) +static void sink_dma_postinit(void) { audiohw_postinit(); @@ -768,15 +770,32 @@ unsigned int pcm_alsa_get_xruns(void) return xruns; } +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = sink_dma_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; + #ifdef HAVE_RECORDING void pcm_rec_lock(void) { - pcm_play_lock(); + sink_lock(); } void pcm_rec_unlock(void) { - pcm_play_unlock(); + sink_unlock(); } void pcm_rec_dma_init(void) @@ -796,7 +815,6 @@ void pcm_rec_dma_close(void) void pcm_rec_dma_start(void *start, size_t size) { logf("PCM REC DMA start (%p %d)", start, size); - pcm_dma_apply_settings_nolock(); pcm_data_rec = start; pcm_size = size; diff --git a/firmware/target/hosted/sdl/pcm-sdl.c b/firmware/target/hosted/sdl/pcm-sdl.c index 2b96c6a252..26c0bef563 100644 --- a/firmware/target/hosted/sdl/pcm-sdl.c +++ b/firmware/target/hosted/sdl/pcm-sdl.c @@ -44,6 +44,7 @@ #include "pcm-internal.h" #include "pcm_sampr.h" #include "pcm_mixer.h" +#include "pcm_sink.h" /*#define LOGF_ENABLE*/ #include "logf.h" @@ -57,6 +58,8 @@ extern const char *audiodev; static int cvt_status = -1; +static unsigned long pcm_sampr; + static const void *pcm_data; static size_t pcm_data_size; static size_t pcm_sample_bytes; @@ -80,13 +83,13 @@ static SDL_AudioCVT cvt; static int audio_locked = 0; static SDL_mutex *audio_lock; -void pcm_play_lock(void) +static void sink_lock(void) { if (++audio_locked == 1) SDL_LockMutex(audio_lock); } -void pcm_play_unlock(void) +static void sink_unlock(void) { if (--audio_locked == 0) SDL_UnlockMutex(audio_lock); @@ -97,8 +100,11 @@ void pcm_play_unlock(void) #endif static void sdl_audio_callback(void *handle, Uint8 *stream, int len); -static void pcm_dma_apply_settings_nolock(void) + +static void sink_set_freq_nolock(uint16_t freq) { + pcm_sampr = hw_freq_sampr[freq]; + SDL_AudioSpec wanted_spec; wanted_spec.freq = pcm_sampr; wanted_spec.format = AUDIO_S16SYS; @@ -161,14 +167,14 @@ static void pcm_dma_apply_settings_nolock(void) } } -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { - pcm_play_lock(); - pcm_dma_apply_settings_nolock(); - pcm_play_unlock(); + sink_lock(); + sink_set_freq_nolock(freq); + sink_unlock(); } -void pcm_play_dma_start(const void *addr, size_t size) +static void sink_dma_start(const void *addr, size_t size) { pcm_data = addr; pcm_data_size = size; @@ -180,7 +186,7 @@ void pcm_play_dma_start(const void *addr, size_t size) #endif } -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { #if SDL_MAJOR_VERSION > 1 SDL_PauseAudioDevice(pcm_devid, 1); @@ -397,7 +403,7 @@ unsigned long spdif_measure_frequency(void) #endif /* HAVE_RECORDING */ -void pcm_play_dma_init(void) +static void sink_dma_init(void) { if (SDL_InitSubSystem(SDL_INIT_AUDIO)) { @@ -435,6 +441,23 @@ void pcm_play_dma_init(void) #endif } -void pcm_play_dma_postinit(void) +static void sink_dma_postinit(void) { } + +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = sink_dma_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; diff --git a/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c b/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c index 178da0b153..b0cbefc66f 100644 --- a/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c +++ b/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c @@ -26,14 +26,14 @@ #include "sound.h" #include "pcm.h" #include "pcm-internal.h" +#include "pcm_sink.h" #include "jz4740.h" - /**************************************************************************** ** Playback DMA transfer **/ -void pcm_play_dma_postinit(void) +static void sink_dma_postinit(void) { audiohw_postinit(); @@ -47,7 +47,7 @@ void pcm_play_dma_postinit(void) __aic_flush_fifo(); } -void pcm_play_dma_init(void) +static void sink_dma_init(void) { /* TODO */ @@ -57,10 +57,10 @@ void pcm_play_dma_init(void) audiohw_init(); } -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { /* TODO */ - audiohw_set_frequency(pcm_sampr); + audiohw_set_frequency(freq); } static const void* playback_address; @@ -135,21 +135,7 @@ void DMA_CALLBACK(DMA_AIC_TX_CHANNEL)(void) } } -void pcm_play_dma_start(const void *addr, size_t size) -{ - pcm_play_dma_stop(); - - dma_enable(); - - set_dma(addr, size); - - __aic_enable_transmit_dma(); - __aic_enable_replay(); - - REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN; -} - -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { int flags = disable_irq_save(); @@ -163,8 +149,22 @@ void pcm_play_dma_stop(void) restore_irq(flags); } +static void sink_dma_start(const void *addr, size_t size) +{ + sink_dma_stop(); + + dma_enable(); + + set_dma(addr, size); + + __aic_enable_transmit_dma(); + __aic_enable_replay(); + + REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN; +} + static unsigned int play_lock = 0; -void pcm_play_lock(void) +static void sink_lock(void) { int flags = disable_irq_save(); @@ -174,7 +174,7 @@ void pcm_play_lock(void) restore_irq(flags); } -void pcm_play_unlock(void) +static void sink_unlock(void) { int flags = disable_irq_save(); @@ -189,6 +189,23 @@ void audiohw_close(void) /* TODO: prevent pop */ } +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = sink_dma_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; + #ifdef HAVE_RECORDING /* TODO */ void pcm_rec_dma_init(void) diff --git a/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c b/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c index 72bcbeccd0..fa4102a846 100644 --- a/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c +++ b/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c @@ -26,13 +26,14 @@ #include "sound.h" #include "pcm.h" #include "pcm-internal.h" +#include "pcm_sink.h" #include "cpu.h" /**************************************************************************** ** Playback DMA transfer **/ -void pcm_play_dma_postinit(void) +static void sink_dma_postinit(void) { audiohw_postinit(); @@ -40,7 +41,7 @@ void pcm_play_dma_postinit(void) __aic_flush_tfifo(); } -void pcm_play_dma_init(void) +static void sink_dma_init(void) { system_enable_irq(DMA_IRQ(DMA_AIC_TX_CHANNEL)); @@ -48,9 +49,9 @@ void pcm_play_dma_init(void) audiohw_init(); } -void pcm_dma_apply_settings(void) +static void sink_set_freq(uint16_t freq) { - audiohw_set_frequency(pcm_fsel); + audiohw_set_frequency(freq); } static const void* playback_address; @@ -126,20 +127,7 @@ void DMA_CALLBACK(DMA_AIC_TX_CHANNEL)(void) } } -void pcm_play_dma_start(const void *addr, size_t size) -{ - pcm_play_dma_stop(); - - __dmac_channel_enable_clk(DMA_AIC_TX_CHANNEL); - - set_dma(addr, size); - - __aic_enable_replay(); - - __dmac_channel_enable_irq(DMA_AIC_TX_CHANNEL); -} - -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { int flags = disable_irq_save(); @@ -152,8 +140,21 @@ void pcm_play_dma_stop(void) restore_irq(flags); } +static void sink_dma_start(const void *addr, size_t size) +{ + sink_dma_stop(); + + __dmac_channel_enable_clk(DMA_AIC_TX_CHANNEL); + + set_dma(addr, size); + + __aic_enable_replay(); + + __dmac_channel_enable_irq(DMA_AIC_TX_CHANNEL); +} + static unsigned int play_lock = 0; -void pcm_play_lock(void) +static void sink_lock(void) { int flags = disable_irq_save(); @@ -163,7 +164,7 @@ void pcm_play_lock(void) restore_irq(flags); } -void pcm_play_unlock(void) +static void sink_unlock(void) { int flags = disable_irq_save(); @@ -172,3 +173,20 @@ void pcm_play_unlock(void) restore_irq(flags); } + +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = sink_dma_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; diff --git a/firmware/target/mips/ingenic_x1000/pcm-x1000.c b/firmware/target/mips/ingenic_x1000/pcm-x1000.c index 4b9cb85b57..9bd6f4ae0f 100644 --- a/firmware/target/mips/ingenic_x1000/pcm-x1000.c +++ b/firmware/target/mips/ingenic_x1000/pcm-x1000.c @@ -25,6 +25,7 @@ #include "audiohw.h" #include "pcm.h" #include "pcm-internal.h" +#include "pcm_sink.h" #include "panic.h" #include "dma-x1000.h" #include "irq-x1000.h" @@ -53,7 +54,7 @@ static dma_desc rec_dma_desc; static void pcm_rec_dma_int_cb(int event); #endif -void pcm_play_dma_init(void) +static void sink_dma_init(void) { /* Ungate clock */ jz_writef(CPM_CLKGR, AIC(0)); @@ -99,14 +100,9 @@ void pcm_play_dma_init(void) system_enable_irq(IRQ_AIC); } -void pcm_play_dma_postinit(void) +static void sink_set_freq(uint16_t freq) { - audiohw_postinit(); -} - -void pcm_dma_apply_settings(void) -{ - audiohw_set_frequency(pcm_fsel); + audiohw_set_frequency(freq); } static void play_dma_start(const void* addr, size_t size) @@ -157,7 +153,7 @@ static void pcm_play_dma_int_cb(int event) } } -void pcm_play_dma_start(const void* addr, size_t size) +static void sink_dma_start(const void* addr, size_t size) { play_dma_pending_event = DMA_EVENT_NONE; aic_state |= AIC_STATE_PLAYING; @@ -166,7 +162,7 @@ void pcm_play_dma_start(const void* addr, size_t size) jz_writef(AIC_CCR, TDMS(1), ETUR(1), ERPL(1)); } -void pcm_play_dma_stop(void) +static void sink_dma_stop(void) { /* disable DMA and underrun interrupts */ jz_writef(AIC_CCR, TDMS(0), ETUR(0)); @@ -180,7 +176,7 @@ void pcm_play_dma_stop(void) if (jz_readf(AIC_I2SCR, STPBK) == 0) { while(jz_readf(AIC_SR, TFL) != 0); } else { - panicf("pcm_play_dma_stop: No bit clock running!"); + panicf("sink_dma_stop: No bit clock running!"); } /* disable playback */ @@ -190,14 +186,14 @@ void pcm_play_dma_stop(void) aic_state &= ~AIC_STATE_PLAYING; } -void pcm_play_lock(void) +static void sink_lock(void) { int irq = disable_irq_save(); ++play_lock; restore_irq(irq); } -void pcm_play_unlock(void) +static void sink_unlock(void) { int irq = disable_irq_save(); if(--play_lock == 0 && (aic_state & AIC_STATE_PLAYING)) { @@ -208,6 +204,23 @@ void pcm_play_unlock(void) restore_irq(irq); } +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = audiohw_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; + #ifdef HAVE_RECORDING /* * Recording