79ed830b29aad38212240d2c1441c3d9bd498852
[platform/upstream/pulseaudio.git] / src / pulsecore / svolume_arm.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2004-2006 Lennart Poettering
5   Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
6
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as published
9   by the Free Software Foundation; either version 2.1 of the License,
10   or (at your option) any later version.
11
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with PulseAudio; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20   USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <pulsecore/random.h>
28 #include <pulsecore/macro.h>
29 #include <pulsecore/endianmacros.h>
30
31 #include "cpu-arm.h"
32
33 #include "sample-util.h"
34
35 #if defined (__arm__) && defined (HAVE_ARMV6)
36
37 #define MOD_INC() \
38     " subs  r0, r6, %2              \n\t" \
39     " itt cs                        \n\t" \
40     " addcs r0, %1                  \n\t" \
41     " movcs r6, r0                  \n\t"
42
43 static void pa_volume_s16ne_arm(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
44     int32_t *ve;
45
46     /* Channels must be at least 4, and always a multiple of the original number.
47      * This is also the max amount we overread the volume array, which should
48      * have enough padding. */
49     channels = channels == 3 ? 6 : PA_MAX (4U, channels);
50     ve = volumes + channels;
51
52     __asm__ __volatile__ (
53         " mov r6, %1                      \n\t"
54         " mov %3, %3, LSR #1              \n\t" /* length /= sizeof (int16_t) */
55         " tst %3, #1                      \n\t" /* check for odd samples */
56         " beq  2f                         \n\t"
57
58         "1:                               \n\t"
59         " ldr  r0, [r6], #4               \n\t" /* odd samples volumes */
60         " ldrh r2, [%0]                   \n\t"
61
62         " smulwb r0, r0, r2               \n\t"
63         " ssat r0, #16, r0                \n\t"
64
65         " strh r0, [%0], #2               \n\t"
66
67         MOD_INC()
68
69         "2:                               \n\t"
70         " mov %3, %3, LSR #1              \n\t"
71         " tst %3, #1                      \n\t" /* check for odd samples */
72         " beq  4f                         \n\t"
73
74         "3:                               \n\t"
75         " ldrd r2, [r6], #8               \n\t" /* 2 samples at a time */
76         " ldr  r0, [%0]                   \n\t"
77
78         " smulwt r2, r2, r0               \n\t"
79         " smulwb r3, r3, r0               \n\t"
80
81         " ssat r2, #16, r2                \n\t"
82         " ssat r3, #16, r3                \n\t"
83
84         " pkhbt r0, r3, r2, LSL #16       \n\t"
85         " str  r0, [%0], #4               \n\t"
86
87         MOD_INC()
88
89         "4:                               \n\t"
90         " movs %3, %3, LSR #1             \n\t"
91         " beq  6f                         \n\t"
92
93         "5:                               \n\t"
94         " ldrd r2, [r6], #8               \n\t" /* 4 samples at a time */
95         " ldrd r4, [r6], #8               \n\t"
96         " ldrd r0, [%0]                   \n\t"
97
98         " smulwt r2, r2, r0               \n\t"
99         " smulwb r3, r3, r0               \n\t"
100         " smulwt r4, r4, r1               \n\t"
101         " smulwb r5, r5, r1               \n\t"
102
103         " ssat r2, #16, r2                \n\t"
104         " ssat r3, #16, r3                \n\t"
105         " ssat r4, #16, r4                \n\t"
106         " ssat r5, #16, r5                \n\t"
107
108         " pkhbt r0, r3, r2, LSL #16       \n\t"
109         " pkhbt r1, r5, r4, LSL #16       \n\t"
110         " strd  r0, [%0], #8              \n\t"
111
112         MOD_INC()
113
114         " subs %3, %3, #1                 \n\t"
115         " bne 5b                          \n\t"
116         "6:                               \n\t"
117
118         : "+r" (samples), "+r" (volumes), "+r" (ve), "+r" (length)
119         :
120         : "r6", "r5", "r4", "r3", "r2", "r1", "r0", "cc"
121     );
122 }
123
124 #undef RUN_TEST
125
126 #ifdef RUN_TEST
127 #define CHANNELS 2
128 #define SAMPLES 1022
129 #define TIMES 1000
130 #define TIMES2 100
131 #define PADDING 16
132
133 static void run_test(void) {
134     int16_t samples[SAMPLES];
135     int16_t samples_ref[SAMPLES];
136     int16_t samples_orig[SAMPLES];
137     int32_t volumes[CHANNELS + PADDING];
138     int i, j, padding;
139     pa_do_volume_func_t func;
140     pa_usec_t start, stop;
141     int k;
142     pa_usec_t min = INT_MAX, max = 0;
143     double s1 = 0, s2 = 0;
144
145     func = pa_get_volume_func(PA_SAMPLE_S16NE);
146
147     printf("checking ARM %zd\n", sizeof(samples));
148
149     pa_random(samples, sizeof(samples));
150     memcpy(samples_ref, samples, sizeof(samples));
151     memcpy(samples_orig, samples, sizeof(samples));
152
153     for (i = 0; i < CHANNELS; i++)
154         volumes[i] = PA_CLAMP_VOLUME(rand() >> 15);
155     for (padding = 0; padding < PADDING; padding++, i++)
156         volumes[i] = volumes[padding];
157
158     func(samples_ref, volumes, CHANNELS, sizeof(samples));
159     pa_volume_s16ne_arm(samples, volumes, CHANNELS, sizeof(samples));
160     for (i = 0; i < SAMPLES; i++) {
161         if (samples[i] != samples_ref[i]) {
162             printf ("%d: %04x != %04x (%04x * %04x)\n", i, samples[i], samples_ref[i],
163                       samples_orig[i], volumes[i % CHANNELS]);
164         }
165     }
166
167     for (k = 0; k < TIMES2; k++) {
168         start = pa_rtclock_now();
169         for (j = 0; j < TIMES; j++) {
170             memcpy(samples, samples_orig, sizeof(samples));
171             pa_volume_s16ne_arm(samples, volumes, CHANNELS, sizeof(samples));
172         }
173         stop = pa_rtclock_now();
174
175         if (min > (stop - start)) min = stop - start;
176         if (max < (stop - start)) max = stop - start;
177         s1 += stop - start;
178         s2 += (stop - start) * (stop - start);
179     }
180     pa_log_info("ARM: %llu usec (min = %llu, max = %llu, stddev = %g).", (long long unsigned int)s1,
181             (long long unsigned int)min, (long long unsigned int)max, sqrt(TIMES2 * s2 - s1 * s1) / TIMES2);
182
183     min = INT_MAX; max = 0;
184     s1 = s2 = 0;
185     for (k = 0; k < TIMES2; k++) {
186         start = pa_rtclock_now();
187         for (j = 0; j < TIMES; j++) {
188             memcpy(samples_ref, samples_orig, sizeof(samples));
189             func(samples_ref, volumes, CHANNELS, sizeof(samples));
190         }
191         stop = pa_rtclock_now();
192
193         if (min > (stop - start)) min = stop - start;
194         if (max < (stop - start)) max = stop - start;
195         s1 += stop - start;
196         s2 += (stop - start) * (stop - start);
197     }
198     pa_log_info("ref: %llu usec (min = %llu, max = %llu, stddev = %g).", (long long unsigned int)s1,
199             (long long unsigned int)min, (long long unsigned int)max, sqrt(TIMES2 * s2 - s1 * s1) / TIMES2);
200
201     pa_assert_se(memcmp(samples_ref, samples, sizeof(samples)) == 0);
202 }
203 #endif
204
205 #endif /* defined (__arm__) && defined (HAVE_ARMV6) */
206
207
208 void pa_volume_func_init_arm(pa_cpu_arm_flag_t flags) {
209 #if defined (__arm__) && defined (HAVE_ARMV6)
210     pa_log_info("Initialising ARM optimized volume functions.");
211
212 #ifdef RUN_TEST
213     run_test();
214 #endif
215
216     pa_set_volume_func(PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_arm);
217 #endif /* defined (__arm__) && defined (HAVE_ARMV6) */
218 }