2c182fe8ddcde15fe23d01b788dd13d0526d16d9
[firefly-linux-kernel-4.4.55.git] / drivers / staging / echo / fir.h
1 /*
2  * SpanDSP - a series of DSP components for telephony
3  *
4  * fir.h - General telephony FIR routines
5  *
6  * Written by Steve Underwood <steveu@coppice.org>
7  *
8  * Copyright (C) 2002 Steve Underwood
9  *
10  * All rights reserved.
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License version 2, as
14  * published by the Free Software Foundation.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  */
25
26 /*! \page fir_page FIR filtering
27 \section fir_page_sec_1 What does it do?
28 ???.
29
30 \section fir_page_sec_2 How does it work?
31 ???.
32 */
33
34 #if !defined(_FIR_H_)
35 #define _FIR_H_
36
37 /*
38    Blackfin NOTES & IDEAS:
39
40    A simple dot product function is used to implement the filter.  This performs
41    just one MAC/cycle which is inefficient but was easy to implement as a first
42    pass.  The current Blackfin code also uses an unrolled form of the filter
43    history to avoid 0 length hardware loop issues.  This is wasteful of
44    memory.
45
46    Ideas for improvement:
47
48    1/ Rewrite filter for dual MAC inner loop.  The issue here is handling
49    history sample offsets that are 16 bit aligned - the dual MAC needs
50    32 bit aligmnent.  There are some good examples in libbfdsp.
51
52    2/ Use the hardware circular buffer facility tohalve memory usage.
53
54    3/ Consider using internal memory.
55
56    Using less memory might also improve speed as cache misses will be
57    reduced. A drop in MIPs and memory approaching 50% should be
58    possible.
59
60    The foreground and background filters currenlty use a total of
61    about 10 MIPs/ch as measured with speedtest.c on a 256 TAP echo
62    can.
63 */
64
65 /*!
66     16 bit integer FIR descriptor. This defines the working state for a single
67     instance of an FIR filter using 16 bit integer coefficients.
68 */
69 struct fir16_state_t {
70         int taps;
71         int curr_pos;
72         const int16_t *coeffs;
73         int16_t *history;
74 };
75
76 /*!
77     32 bit integer FIR descriptor. This defines the working state for a single
78     instance of an FIR filter using 32 bit integer coefficients, and filtering
79     16 bit integer data.
80 */
81 struct fir32_state_t {
82         int taps;
83         int curr_pos;
84         const int32_t *coeffs;
85         int16_t *history;
86 };
87
88 /*!
89     Floating point FIR descriptor. This defines the working state for a single
90     instance of an FIR filter using floating point coefficients and data.
91 */
92 struct fir_float_state_t {
93         int taps;
94         int curr_pos;
95         const float *coeffs;
96         float *history;
97 };
98
99 static inline const int16_t *fir16_create(struct fir16_state_t *fir,
100                                               const int16_t *coeffs, int taps)
101 {
102         fir->taps = taps;
103         fir->curr_pos = taps - 1;
104         fir->coeffs = coeffs;
105 #if defined(USE_SSE2) || defined(__bfin__)
106         fir->history = kcalloc(2 * taps, sizeof(int16_t), GFP_KERNEL);
107 #else
108         fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL);
109 #endif
110         return fir->history;
111 }
112
113 static inline void fir16_flush(struct fir16_state_t *fir)
114 {
115 #if defined(USE_SSE2) || defined(__bfin__)
116         memset(fir->history, 0, 2 * fir->taps * sizeof(int16_t));
117 #else
118         memset(fir->history, 0, fir->taps * sizeof(int16_t));
119 #endif
120 }
121
122 static inline void fir16_free(struct fir16_state_t *fir)
123 {
124         kfree(fir->history);
125 }
126
127 #ifdef __bfin__
128 static inline int32_t dot_asm(short *x, short *y, int len)
129 {
130         int dot;
131
132         len--;
133
134         __asm__("I0 = %1;\n\t"
135                 "I1 = %2;\n\t"
136                 "A0 = 0;\n\t"
137                 "R0.L = W[I0++] || R1.L = W[I1++];\n\t"
138                 "LOOP dot%= LC0 = %3;\n\t"
139                 "LOOP_BEGIN dot%=;\n\t"
140                 "A0 += R0.L * R1.L (IS) || R0.L = W[I0++] || R1.L = W[I1++];\n\t"
141                 "LOOP_END dot%=;\n\t"
142                 "A0 += R0.L*R1.L (IS);\n\t"
143                 "R0 = A0;\n\t"
144                 "%0 = R0;\n\t"
145                 : "=&d"(dot)
146                 : "a"(x), "a"(y), "a"(len)
147                 : "I0", "I1", "A1", "A0", "R0", "R1"
148         );
149
150         return dot;
151 }
152 #endif
153
154 static inline int16_t fir16(struct fir16_state_t *fir, int16_t sample)
155 {
156         int32_t y;
157 #if defined(USE_SSE2)
158         int i;
159         union xmm_t *xmm_coeffs;
160         union xmm_t *xmm_hist;
161
162         fir->history[fir->curr_pos] = sample;
163         fir->history[fir->curr_pos + fir->taps] = sample;
164
165         xmm_coeffs = (union xmm_t *)fir->coeffs;
166         xmm_hist = (union xmm_t *)&fir->history[fir->curr_pos];
167         i = fir->taps;
168         pxor_r2r(xmm4, xmm4);
169         /* 16 samples per iteration, so the filter must be a multiple of 16 long. */
170         while (i > 0) {
171                 movdqu_m2r(xmm_coeffs[0], xmm0);
172                 movdqu_m2r(xmm_coeffs[1], xmm2);
173                 movdqu_m2r(xmm_hist[0], xmm1);
174                 movdqu_m2r(xmm_hist[1], xmm3);
175                 xmm_coeffs += 2;
176                 xmm_hist += 2;
177                 pmaddwd_r2r(xmm1, xmm0);
178                 pmaddwd_r2r(xmm3, xmm2);
179                 paddd_r2r(xmm0, xmm4);
180                 paddd_r2r(xmm2, xmm4);
181                 i -= 16;
182         }
183         movdqa_r2r(xmm4, xmm0);
184         psrldq_i2r(8, xmm0);
185         paddd_r2r(xmm0, xmm4);
186         movdqa_r2r(xmm4, xmm0);
187         psrldq_i2r(4, xmm0);
188         paddd_r2r(xmm0, xmm4);
189         movd_r2m(xmm4, y);
190 #elif defined(__bfin__)
191         fir->history[fir->curr_pos] = sample;
192         fir->history[fir->curr_pos + fir->taps] = sample;
193         y = dot_asm((int16_t *) fir->coeffs, &fir->history[fir->curr_pos],
194                     fir->taps);
195 #else
196         int i;
197         int offset1;
198         int offset2;
199
200         fir->history[fir->curr_pos] = sample;
201
202         offset2 = fir->curr_pos;
203         offset1 = fir->taps - offset2;
204         y = 0;
205         for (i = fir->taps - 1; i >= offset1; i--)
206                 y += fir->coeffs[i] * fir->history[i - offset1];
207         for (; i >= 0; i--)
208                 y += fir->coeffs[i] * fir->history[i + offset2];
209 #endif
210         if (fir->curr_pos <= 0)
211                 fir->curr_pos = fir->taps;
212         fir->curr_pos--;
213         return (int16_t) (y >> 15);
214 }
215
216 static inline const int16_t *fir32_create(struct fir32_state_t *fir,
217                                               const int32_t *coeffs, int taps)
218 {
219         fir->taps = taps;
220         fir->curr_pos = taps - 1;
221         fir->coeffs = coeffs;
222         fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL);
223         return fir->history;
224 }
225
226 static inline void fir32_flush(struct fir32_state_t *fir)
227 {
228         memset(fir->history, 0, fir->taps * sizeof(int16_t));
229 }
230
231 static inline void fir32_free(struct fir32_state_t *fir)
232 {
233         kfree(fir->history);
234 }
235
236 static inline int16_t fir32(struct fir32_state_t *fir, int16_t sample)
237 {
238         int i;
239         int32_t y;
240         int offset1;
241         int offset2;
242
243         fir->history[fir->curr_pos] = sample;
244         offset2 = fir->curr_pos;
245         offset1 = fir->taps - offset2;
246         y = 0;
247         for (i = fir->taps - 1; i >= offset1; i--)
248                 y += fir->coeffs[i] * fir->history[i - offset1];
249         for (; i >= 0; i--)
250                 y += fir->coeffs[i] * fir->history[i + offset2];
251         if (fir->curr_pos <= 0)
252                 fir->curr_pos = fir->taps;
253         fir->curr_pos--;
254         return (int16_t) (y >> 15);
255 }
256
257 #endif
258 /*- End of file ------------------------------------------------------------*/