153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2004-2006 Lennart Poettering 553a5a1b3Sopenharmony_ci Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk> 653a5a1b3Sopenharmony_ci 753a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 853a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 953a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 1053a5a1b3Sopenharmony_ci or (at your option) any later version. 1153a5a1b3Sopenharmony_ci 1253a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1353a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1453a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1553a5a1b3Sopenharmony_ci General Public License for more details. 1653a5a1b3Sopenharmony_ci 1753a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1853a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1953a5a1b3Sopenharmony_ci***/ 2053a5a1b3Sopenharmony_ci 2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2253a5a1b3Sopenharmony_ci#include <config.h> 2353a5a1b3Sopenharmony_ci#endif 2453a5a1b3Sopenharmony_ci 2553a5a1b3Sopenharmony_ci#include <pulsecore/random.h> 2653a5a1b3Sopenharmony_ci#include <pulsecore/macro.h> 2753a5a1b3Sopenharmony_ci#include <pulsecore/endianmacros.h> 2853a5a1b3Sopenharmony_ci 2953a5a1b3Sopenharmony_ci#include "cpu-arm.h" 3053a5a1b3Sopenharmony_ci 3153a5a1b3Sopenharmony_ci#include "sample-util.h" 3253a5a1b3Sopenharmony_ci 3353a5a1b3Sopenharmony_ci#if defined (__arm__) && defined (HAVE_ARMV6) 3453a5a1b3Sopenharmony_ci 3553a5a1b3Sopenharmony_ci#define MOD_INC() \ 3653a5a1b3Sopenharmony_ci " subs r0, r6, %2 \n\t" \ 3753a5a1b3Sopenharmony_ci " itt cs \n\t" \ 3853a5a1b3Sopenharmony_ci " addcs r0, %1 \n\t" \ 3953a5a1b3Sopenharmony_ci " movcs r6, r0 \n\t" 4053a5a1b3Sopenharmony_ci 4153a5a1b3Sopenharmony_cistatic pa_do_volume_func_t _volume_ref; 4253a5a1b3Sopenharmony_ci 4353a5a1b3Sopenharmony_cistatic void pa_volume_s16ne_arm(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) { 4453a5a1b3Sopenharmony_ci /* Channels must be at least 4, and always a multiple of the original number. 4553a5a1b3Sopenharmony_ci * This is also the max amount we overread the volume array, which should 4653a5a1b3Sopenharmony_ci * have enough padding. */ 4753a5a1b3Sopenharmony_ci const int32_t *ve = volumes + (channels == 3 ? 6 : PA_MAX (4U, channels)); 4853a5a1b3Sopenharmony_ci unsigned rem = PA_ALIGN((size_t) samples) - (size_t) samples; 4953a5a1b3Sopenharmony_ci 5053a5a1b3Sopenharmony_ci /* Make sure we're word-aligned, else performance _really_ sucks */ 5153a5a1b3Sopenharmony_ci if (rem) { 5253a5a1b3Sopenharmony_ci _volume_ref(samples, volumes, channels, rem < length ? rem : length); 5353a5a1b3Sopenharmony_ci 5453a5a1b3Sopenharmony_ci if (rem < length) { 5553a5a1b3Sopenharmony_ci length -= rem; 5653a5a1b3Sopenharmony_ci samples += rem / sizeof(*samples); 5753a5a1b3Sopenharmony_ci } else 5853a5a1b3Sopenharmony_ci return; /* we're done */ 5953a5a1b3Sopenharmony_ci } 6053a5a1b3Sopenharmony_ci 6153a5a1b3Sopenharmony_ci __asm__ __volatile__ ( 6253a5a1b3Sopenharmony_ci " mov r6, %4 \n\t" /* r6 = volumes + rem */ 6353a5a1b3Sopenharmony_ci " mov %3, %3, LSR #1 \n\t" /* length /= sizeof (int16_t) */ 6453a5a1b3Sopenharmony_ci 6553a5a1b3Sopenharmony_ci " cmp %3, #4 \n\t" /* check for 4+ samples */ 6653a5a1b3Sopenharmony_ci " blt 2f \n\t" 6753a5a1b3Sopenharmony_ci 6853a5a1b3Sopenharmony_ci /* See final case for how the multiplication works */ 6953a5a1b3Sopenharmony_ci 7053a5a1b3Sopenharmony_ci "1: \n\t" 7153a5a1b3Sopenharmony_ci " ldrd r2, [r6], #8 \n\t" /* 4 samples at a time */ 7253a5a1b3Sopenharmony_ci " ldrd r4, [r6], #8 \n\t" 7353a5a1b3Sopenharmony_ci " ldrd r0, [%0] \n\t" 7453a5a1b3Sopenharmony_ci 7553a5a1b3Sopenharmony_ci#ifdef WORDS_BIGENDIAN 7653a5a1b3Sopenharmony_ci " smulwt r2, r2, r0 \n\t" 7753a5a1b3Sopenharmony_ci " smulwb r3, r3, r0 \n\t" 7853a5a1b3Sopenharmony_ci " smulwt r4, r4, r1 \n\t" 7953a5a1b3Sopenharmony_ci " smulwb r5, r5, r1 \n\t" 8053a5a1b3Sopenharmony_ci#else 8153a5a1b3Sopenharmony_ci " smulwb r2, r2, r0 \n\t" 8253a5a1b3Sopenharmony_ci " smulwt r3, r3, r0 \n\t" 8353a5a1b3Sopenharmony_ci " smulwb r4, r4, r1 \n\t" 8453a5a1b3Sopenharmony_ci " smulwt r5, r5, r1 \n\t" 8553a5a1b3Sopenharmony_ci#endif 8653a5a1b3Sopenharmony_ci 8753a5a1b3Sopenharmony_ci " ssat r2, #16, r2 \n\t" 8853a5a1b3Sopenharmony_ci " ssat r3, #16, r3 \n\t" 8953a5a1b3Sopenharmony_ci " ssat r4, #16, r4 \n\t" 9053a5a1b3Sopenharmony_ci " ssat r5, #16, r5 \n\t" 9153a5a1b3Sopenharmony_ci 9253a5a1b3Sopenharmony_ci#ifdef WORDS_BIGENDIAN 9353a5a1b3Sopenharmony_ci " pkhbt r0, r3, r2, LSL #16 \n\t" 9453a5a1b3Sopenharmony_ci " pkhbt r1, r5, r4, LSL #16 \n\t" 9553a5a1b3Sopenharmony_ci#else 9653a5a1b3Sopenharmony_ci " pkhbt r0, r2, r3, LSL #16 \n\t" 9753a5a1b3Sopenharmony_ci " pkhbt r1, r4, r5, LSL #16 \n\t" 9853a5a1b3Sopenharmony_ci#endif 9953a5a1b3Sopenharmony_ci " strd r0, [%0], #8 \n\t" 10053a5a1b3Sopenharmony_ci 10153a5a1b3Sopenharmony_ci MOD_INC() 10253a5a1b3Sopenharmony_ci 10353a5a1b3Sopenharmony_ci " subs %3, %3, #4 \n\t" 10453a5a1b3Sopenharmony_ci " cmp %3, #4 \n\t" 10553a5a1b3Sopenharmony_ci " bge 1b \n\t" 10653a5a1b3Sopenharmony_ci 10753a5a1b3Sopenharmony_ci "2: \n\t" 10853a5a1b3Sopenharmony_ci " cmp %3, #2 \n\t" 10953a5a1b3Sopenharmony_ci " blt 3f \n\t" 11053a5a1b3Sopenharmony_ci 11153a5a1b3Sopenharmony_ci " ldrd r2, [r6], #8 \n\t" /* 2 samples at a time */ 11253a5a1b3Sopenharmony_ci " ldr r0, [%0] \n\t" 11353a5a1b3Sopenharmony_ci 11453a5a1b3Sopenharmony_ci#ifdef WORDS_BIGENDIAN 11553a5a1b3Sopenharmony_ci " smulwt r2, r2, r0 \n\t" 11653a5a1b3Sopenharmony_ci " smulwb r3, r3, r0 \n\t" 11753a5a1b3Sopenharmony_ci#else 11853a5a1b3Sopenharmony_ci " smulwb r2, r2, r0 \n\t" 11953a5a1b3Sopenharmony_ci " smulwt r3, r3, r0 \n\t" 12053a5a1b3Sopenharmony_ci#endif 12153a5a1b3Sopenharmony_ci 12253a5a1b3Sopenharmony_ci " ssat r2, #16, r2 \n\t" 12353a5a1b3Sopenharmony_ci " ssat r3, #16, r3 \n\t" 12453a5a1b3Sopenharmony_ci 12553a5a1b3Sopenharmony_ci#ifdef WORDS_BIGENDIAN 12653a5a1b3Sopenharmony_ci " pkhbt r0, r3, r2, LSL #16 \n\t" 12753a5a1b3Sopenharmony_ci#else 12853a5a1b3Sopenharmony_ci " pkhbt r0, r2, r3, LSL #16 \n\t" 12953a5a1b3Sopenharmony_ci#endif 13053a5a1b3Sopenharmony_ci " str r0, [%0], #4 \n\t" 13153a5a1b3Sopenharmony_ci 13253a5a1b3Sopenharmony_ci MOD_INC() 13353a5a1b3Sopenharmony_ci 13453a5a1b3Sopenharmony_ci " subs %3, %3, #2 \n\t" 13553a5a1b3Sopenharmony_ci 13653a5a1b3Sopenharmony_ci "3: \n\t" /* check for odd # of samples */ 13753a5a1b3Sopenharmony_ci " cmp %3, #1 \n\t" 13853a5a1b3Sopenharmony_ci " bne 4f \n\t" 13953a5a1b3Sopenharmony_ci 14053a5a1b3Sopenharmony_ci " ldr r0, [r6], #4 \n\t" /* r0 = volume */ 14153a5a1b3Sopenharmony_ci " ldrh r2, [%0] \n\t" /* r2 = sample */ 14253a5a1b3Sopenharmony_ci 14353a5a1b3Sopenharmony_ci " smulwb r0, r0, r2 \n\t" /* r0 = (r0 * r2) >> 16 */ 14453a5a1b3Sopenharmony_ci " ssat r0, #16, r0 \n\t" /* r0 = PA_CLAMP(r0, 0x7FFF) */ 14553a5a1b3Sopenharmony_ci 14653a5a1b3Sopenharmony_ci " strh r0, [%0], #2 \n\t" /* sample = r0 */ 14753a5a1b3Sopenharmony_ci 14853a5a1b3Sopenharmony_ci "4: \n\t" 14953a5a1b3Sopenharmony_ci 15053a5a1b3Sopenharmony_ci : "+r" (samples), "+r" (volumes), "+r" (ve), "+r" (length) 15153a5a1b3Sopenharmony_ci : "r" (volumes + ((rem / sizeof(*samples)) % channels)) 15253a5a1b3Sopenharmony_ci : "r6", "r5", "r4", "r3", "r2", "r1", "r0", "cc" 15353a5a1b3Sopenharmony_ci ); 15453a5a1b3Sopenharmony_ci} 15553a5a1b3Sopenharmony_ci 15653a5a1b3Sopenharmony_ci#endif /* defined (__arm__) && defined (HAVE_ARMV6) */ 15753a5a1b3Sopenharmony_ci 15853a5a1b3Sopenharmony_civoid pa_volume_func_init_arm(pa_cpu_arm_flag_t flags) { 15953a5a1b3Sopenharmony_ci#if defined (__arm__) && defined (HAVE_ARMV6) 16053a5a1b3Sopenharmony_ci pa_log_info("Initialising ARM optimized volume functions."); 16153a5a1b3Sopenharmony_ci 16253a5a1b3Sopenharmony_ci if (!_volume_ref) 16353a5a1b3Sopenharmony_ci _volume_ref = pa_get_volume_func(PA_SAMPLE_S16NE); 16453a5a1b3Sopenharmony_ci 16553a5a1b3Sopenharmony_ci pa_set_volume_func(PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_arm); 16653a5a1b3Sopenharmony_ci#endif /* defined (__arm__) && defined (HAVE_ARMV6) */ 16753a5a1b3Sopenharmony_ci} 168