From f5a5b946867677de76c405ee72e2ea47e36e4c83 Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Fri, 5 Apr 2013 04:36:05 -0400 Subject: Implement universal in-PCM-driver software volume control. Implements double-buffered volume, balance and prescaling control in the main PCM driver when HAVE_SW_VOLUME_CONTROL is defined ensuring that all PCM is volume controlled and level changes are low in latency. Supports -73 to +6 dB using a 15-bit factor so that no large-integer math is needed. Low-level hardware drivers do not have to implement it themselves but parameters can be changed (currently defined in pcm-internal.h) to work best with a particular SoC or to provide different volume ranges. Volume and prescale calls should be made in the codec driver. It should appear as a normal hardware interface. PCM volume calls expect .1 dB units. Change-Id: Idf6316a64ef4fb8abcede10707e1e6c6d01d57db Reviewed-on: http://gerrit.rockbox.org/423 Reviewed-by: Michael Sevakis Tested-by: Michael Sevakis --- firmware/export/config/ondavx747.h | 5 ---- firmware/export/config/ondavx777.h | 5 ---- firmware/export/jz4740-codec.h | 2 +- firmware/export/pcm-internal.h | 54 ++++++++++++++++++++++++++++++++++++-- firmware/export/pcm_sw_volume.h | 40 ++++++++++++++++++++++++++++ firmware/export/sound.h | 3 --- 6 files changed, 93 insertions(+), 16 deletions(-) create mode 100644 firmware/export/pcm_sw_volume.h (limited to 'firmware/export') diff --git a/firmware/export/config/ondavx747.h b/firmware/export/config/ondavx747.h index d303ea5925..8499c15ce9 100644 --- a/firmware/export/config/ondavx747.h +++ b/firmware/export/config/ondavx747.h @@ -132,11 +132,6 @@ /* has no volume control, so we use the software ones */ #define HAVE_SW_VOLUME_CONTROL -/* software controlled volume ranges from -73 -> 0 dB, other than that - is controlled by hardware */ -#define SW_VOLUME_MIN -73 -#define SW_VOLUME_MAX 0 - /* define the bitmask of hardware sample rates */ #define HW_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \ SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ diff --git a/firmware/export/config/ondavx777.h b/firmware/export/config/ondavx777.h index 33bf6442af..a254b0177c 100644 --- a/firmware/export/config/ondavx777.h +++ b/firmware/export/config/ondavx777.h @@ -126,11 +126,6 @@ /* has no volume control, so we use the software ones */ #define HAVE_SW_VOLUME_CONTROL -/* software controlled volume ranges from -73 -> 0 dB, other than that - is controlled by hardware */ -#define SW_VOLUME_MIN -73 -#define SW_VOLUME_MAX 0 - /* define the bitmask of hardware sample rates */ #define HW_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \ SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ diff --git a/firmware/export/jz4740-codec.h b/firmware/export/jz4740-codec.h index 37d2347f5b..3c088f5bf7 100644 --- a/firmware/export/jz4740-codec.h +++ b/firmware/export/jz4740-codec.h @@ -24,6 +24,6 @@ #define VOLUME_MIN -730 #define VOLUME_MAX 60 -void audiohw_set_volume(int v); +void audiohw_set_master_vol(int vol_l, int vol_r); #endif /* __JZ4740_CODEC_H_ */ diff --git a/firmware/export/pcm-internal.h b/firmware/export/pcm-internal.h index 397cf6832f..03e5c5e6e7 100644 --- a/firmware/export/pcm-internal.h +++ b/firmware/export/pcm-internal.h @@ -24,6 +24,19 @@ #include "config.h" +#ifdef HAVE_SW_VOLUME_CONTROL +/* Default settings - architecture may have other optimal values */ + +#define PCM_FACTOR_BITS 15 /* Allows -73 to +6dB gain, sans 64-bit math */ +#define PCM_PLAY_DBL_BUF_SAMPLES 1024 /* Max 4KByte chunks */ +#define PCM_DBL_BUF_BSS /* In DRAM, uncached may be better */ +#define PCM_FACTOR_MIN 0x00000 /* Minimum final factor */ +#define PCM_FACTOR_MAX 0x10000 /* Maximum final factor */ + +#define PCM_FACTOR_UNITY (1 << PCM_FACTOR_BITS) +#endif /* HAVE_SW_VOLUME_CONTROL */ + +#define PCM_SAMPLE_SIZE (2 * sizeof (int16_t)) /* Cheapo buffer align macro to align to the 16-16 PCM size */ #define ALIGN_AUDIOBUF(start, size) \ ({ (start) = (void *)(((uintptr_t)(start) + 3) & ~3); \ @@ -34,6 +47,23 @@ void pcm_do_peak_calculation(struct pcm_peaks *peaks, bool active, /** The following are for internal use between pcm.c and target- specific portion **/ +/* Call registered callback to obtain next buffer */ +static inline bool pcm_get_more_int(const void **addr, size_t *size) +{ + extern volatile pcm_play_callback_type pcm_callback_for_more; + pcm_play_callback_type get_more = pcm_callback_for_more; + + if (UNLIKELY(!get_more)) + return false; + + *addr = NULL; + *size = 0; + get_more(addr, size); + ALIGN_AUDIOBUF(*addr, *size); + + return *addr && *size; +} + static FORCE_INLINE enum pcm_dma_status pcm_call_status_cb( pcm_status_callback_type callback, enum pcm_dma_status status) { @@ -43,14 +73,34 @@ static FORCE_INLINE enum pcm_dma_status pcm_call_status_cb( return callback(status); } -static FORCE_INLINE enum pcm_dma_status -pcm_play_dma_status_callback(enum pcm_dma_status status) +static FORCE_INLINE enum pcm_dma_status pcm_play_call_status_cb( + enum pcm_dma_status status) { extern enum pcm_dma_status (* volatile pcm_play_status_callback)(enum pcm_dma_status); return pcm_call_status_cb(pcm_play_status_callback, status); } +static FORCE_INLINE enum pcm_dma_status +pcm_play_dma_status_callback(enum pcm_dma_status status) +{ +#ifdef HAVE_SW_VOLUME_CONTROL + extern enum pcm_dma_status + pcm_play_dma_status_callback_int(enum pcm_dma_status status); + return pcm_play_dma_status_callback_int(status); +#else + return pcm_play_call_status_cb(status); +#endif /* HAVE_SW_VOLUME_CONTROL */ +} + +#ifdef HAVE_SW_VOLUME_CONTROL +void pcm_play_dma_start_int(const void *addr, size_t size); +void pcm_play_dma_pause_int(bool pause); +void pcm_play_dma_stop_int(void); +void pcm_play_stop_int(void); +const void *pcm_play_dma_get_peak_buffer_int(int *count); +#endif /* HAVE_SW_VOLUME_CONTROL */ + /* Called by the bottom layer ISR when more data is needed. Returns true * if a new buffer is available, false otherwise. */ bool pcm_play_dma_complete_callback(enum pcm_dma_status status, diff --git a/firmware/export/pcm_sw_volume.h b/firmware/export/pcm_sw_volume.h new file mode 100644 index 0000000000..b86e78f500 --- /dev/null +++ b/firmware/export/pcm_sw_volume.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2013 by Michael Sevakis + * + * 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. + * + ****************************************************************************/ +#ifndef PCM_SW_VOLUME_H +#define PCM_SW_VOLUME_H + +#ifdef HAVE_SW_VOLUME_CONTROL + +#include + +#define PCM_MUTE_LEVEL INT_MIN + +#ifdef AUDIOHW_HAVE_PRESCALER +/* Set the prescaler value for all PCM playback */ +void pcm_set_prescaler(int prescale); +#endif /* AUDIOHW_HAVE_PRESCALER */ + +/* Set the per-channel volume cut/gain for all PCM playback */ +void pcm_set_master_volume(int vol_l, int vol_r); + +#endif /* HAVE_SW_VOLUME_CONTROL */ + +#endif /* PCM_SW_VOLUME_H */ diff --git a/firmware/export/sound.h b/firmware/export/sound.h index ba6120ce8f..ebf728c7c7 100644 --- a/firmware/export/sound.h +++ b/firmware/export/sound.h @@ -32,9 +32,6 @@ enum { DSP_CALLBACK_SET_TREBLE, DSP_CALLBACK_SET_CHANNEL_CONFIG, DSP_CALLBACK_SET_STEREO_WIDTH, -#ifdef HAVE_SW_VOLUME_CONTROL - DSP_CALLBACK_SET_SW_VOLUME, -#endif }; #endif -- cgit v1.2.3