1d2565c8bb28c49e2dea665d682452f8896f5066
[ffmpeg.git] / libavcodec / arm / mlpdsp_armv5te.S
1 /*
2  * Copyright (c) 2014 RISC OS Open Ltd
3  * Author: Ben Avison <bavison@riscosopen.org>
4  *
5  * This file is part of Libav.
6  *
7  * Libav is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * Libav is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with Libav; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21
22 #include "libavutil/arm/asm.S"
23
24 #define MAX_CHANNELS        8
25 #define MAX_FIR_ORDER       8
26 #define MAX_IIR_ORDER       4
27 #define MAX_RATEFACTOR      4
28 #define MAX_BLOCKSIZE       (40 * MAX_RATEFACTOR)
29
30 PST     .req    a1
31 PCO     .req    a2
32 AC0     .req    a3
33 AC1     .req    a4
34 CO0     .req    v1
35 CO1     .req    v2
36 CO2     .req    v3
37 CO3     .req    v4
38 ST0     .req    v5
39 ST1     .req    v6
40 ST2     .req    sl
41 ST3     .req    fp
42 I       .req    ip
43 PSAMP   .req    lr
44
45
46 // Some macros that do loads/multiplies where the register number is determined
47 // from an assembly-time expression. Boy is GNU assembler's syntax ugly...
48
49 .macro load  group, index, base, offset
50        .altmacro
51        load_ \group, %(\index), \base, \offset
52        .noaltmacro
53 .endm
54
55 .macro load_ group, index, base, offset
56         ldr     \group\index, [\base, #\offset]
57 .endm
58
59 .macro loadd  group, index, base, offset
60        .altmacro
61        loadd_ \group, %(\index), %(\index+1), \base, \offset
62        .noaltmacro
63 .endm
64
65 .macro loadd_ group, index0, index1, base, offset
66 A .if \offset >= 256
67 A       ldr     \group\index0, [\base, #\offset]
68 A       ldr     \group\index1, [\base, #(\offset) + 4]
69 A .else
70         ldrd    \group\index0, \group\index1, [\base, #\offset]
71 A .endif
72 .endm
73
74 .macro multiply  index, accumulate, long
75         .altmacro
76         multiply_ %(\index), \accumulate, \long
77         .noaltmacro
78 .endm
79
80 .macro multiply_  index, accumulate, long
81  .if \long
82   .if \accumulate
83         smlal   AC0, AC1, CO\index, ST\index
84   .else
85         smull   AC0, AC1, CO\index, ST\index
86   .endif
87  .else
88   .if \accumulate
89         mla     AC0, CO\index, ST\index, AC0
90   .else
91         mul     AC0, CO\index, ST\index
92   .endif
93  .endif
94 .endm
95
96 // A macro to update the load register number and load offsets
97
98 .macro inc  howmany
99   .set LOAD_REG, (LOAD_REG + \howmany) & 3
100   .set OFFSET_CO, OFFSET_CO + 4 * \howmany
101   .set OFFSET_ST, OFFSET_ST + 4 * \howmany
102   .if FIR_REMAIN > 0
103     .set FIR_REMAIN, FIR_REMAIN - \howmany
104     .if FIR_REMAIN == 0
105       .set OFFSET_CO, 4 * MAX_FIR_ORDER
106       .set OFFSET_ST, 4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER)
107     .endif
108   .elseif IIR_REMAIN > 0
109     .set IIR_REMAIN, IIR_REMAIN - \howmany
110   .endif
111 .endm
112
113 // Macro to implement the inner loop for one specific combination of parameters
114
115 .macro implement_filter  mask_minus1, shift_0, shift_8, iir_taps, fir_taps
116   .set TOTAL_TAPS, \iir_taps + \fir_taps
117
118   // Deal with register allocation...
119   .set DEFINED_SHIFT, 0
120   .set DEFINED_MASK, 0
121   .set SHUFFLE_SHIFT, 0
122   .set SHUFFLE_MASK, 0
123   .set SPILL_SHIFT, 0
124   .set SPILL_MASK, 0
125   .if TOTAL_TAPS == 0
126     // Little register pressure in this case - just keep MASK where it was
127     .if !\mask_minus1
128       MASK .req ST1
129       .set DEFINED_MASK, 1
130     .endif
131   .else
132     .if \shift_0
133       .if !\mask_minus1
134         // AC1 is unused with shift 0
135         MASK .req AC1
136         .set DEFINED_MASK, 1
137         .set SHUFFLE_MASK, 1
138       .endif
139     .elseif \shift_8
140       .if !\mask_minus1
141         .if TOTAL_TAPS <= 4
142         // All coefficients are preloaded (so pointer not needed)
143           MASK .req PCO
144           .set DEFINED_MASK, 1
145           .set SHUFFLE_MASK, 1
146         .else
147           .set SPILL_MASK, 1
148         .endif
149       .endif
150     .else // shift not 0 or 8
151       .if TOTAL_TAPS <= 3
152         // All coefficients are preloaded, and at least one CO register is unused
153         .if \fir_taps & 1
154           SHIFT .req CO0
155           .set DEFINED_SHIFT, 1
156           .set SHUFFLE_SHIFT, 1
157         .else
158           SHIFT .req CO3
159           .set DEFINED_SHIFT, 1
160           .set SHUFFLE_SHIFT, 1
161         .endif
162         .if !\mask_minus1
163           MASK .req PCO
164           .set DEFINED_MASK, 1
165           .set SHUFFLE_MASK, 1
166         .endif
167       .elseif TOTAL_TAPS == 4
168         // All coefficients are preloaded
169         SHIFT .req PCO
170         .set DEFINED_SHIFT, 1
171         .set SHUFFLE_SHIFT, 1
172         .if !\mask_minus1
173           .set SPILL_MASK, 1
174         .endif
175       .else
176         .set SPILL_SHIFT, 1
177         .if !\mask_minus1
178           .set SPILL_MASK, 1
179         .endif
180       .endif
181     .endif
182   .endif
183   .if SPILL_SHIFT
184     SHIFT .req ST0
185     .set DEFINED_SHIFT, 1
186   .endif
187   .if SPILL_MASK
188     MASK .req ST1
189     .set DEFINED_MASK, 1
190   .endif
191
192         // Preload coefficients if possible
193   .if TOTAL_TAPS <= 4
194     .set OFFSET_CO, 0
195     .if \fir_taps & 1
196       .set LOAD_REG, 1
197     .else
198       .set LOAD_REG, 0
199     .endif
200     .rept \fir_taps
201         load    CO, LOAD_REG, PCO, OFFSET_CO
202       .set LOAD_REG, (LOAD_REG + 1) & 3
203       .set OFFSET_CO, OFFSET_CO + 4
204     .endr
205     .set OFFSET_CO, 4 * MAX_FIR_ORDER
206     .rept \iir_taps
207         load    CO, LOAD_REG, PCO, OFFSET_CO
208       .set LOAD_REG, (LOAD_REG + 1) & 3
209       .set OFFSET_CO, OFFSET_CO + 4
210     .endr
211   .endif
212
213         // Move mask/shift to final positions if necessary
214         // Need to do this after preloading, because in some cases we
215         // reuse the coefficient pointer register
216   .if SHUFFLE_SHIFT
217         mov     SHIFT, ST0
218   .endif
219   .if SHUFFLE_MASK
220         mov     MASK, ST1
221   .endif
222
223         // Begin loop
224 01:
225   .if TOTAL_TAPS == 0
226         // Things simplify a lot in this case
227         // In fact this could be pipelined further if it's worth it...
228         ldr     ST0, [PSAMP]
229         subs    I, I, #1
230     .if !\mask_minus1
231         and     ST0, ST0, MASK
232     .endif
233         str     ST0, [PST, #-4]!
234         str     ST0, [PST, #4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER)]
235         str     ST0, [PSAMP], #4 * MAX_CHANNELS
236         bne     01b
237   .else
238     .if \fir_taps & 1
239       .set LOAD_REG, 1
240     .else
241       .set LOAD_REG, 0
242     .endif
243     .set LOAD_BANK, 0
244     .set FIR_REMAIN, \fir_taps
245     .set IIR_REMAIN, \iir_taps
246     .if FIR_REMAIN == 0 // only IIR terms
247       .set OFFSET_CO, 4 * MAX_FIR_ORDER
248       .set OFFSET_ST, 4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER)
249     .else
250       .set OFFSET_CO, 0
251       .set OFFSET_ST, 0
252     .endif
253     .set MUL_REG, LOAD_REG
254     .set COUNTER, 0
255     .rept TOTAL_TAPS + 2
256         // Do load(s)
257      .if FIR_REMAIN != 0 || IIR_REMAIN != 0
258       .if COUNTER == 0
259        .if TOTAL_TAPS > 4
260         load    CO, LOAD_REG, PCO, OFFSET_CO
261        .endif
262         load    ST, LOAD_REG, PST, OFFSET_ST
263         inc     1
264       .elseif COUNTER == 1 && (\fir_taps & 1) == 0
265        .if TOTAL_TAPS > 4
266         load    CO, LOAD_REG, PCO, OFFSET_CO
267        .endif
268         load    ST, LOAD_REG, PST, OFFSET_ST
269         inc     1
270       .elseif LOAD_BANK == 0
271        .if TOTAL_TAPS > 4
272         .if FIR_REMAIN == 0 && IIR_REMAIN == 1
273         load    CO, LOAD_REG, PCO, OFFSET_CO
274         .else
275         loadd   CO, LOAD_REG, PCO, OFFSET_CO
276         .endif
277        .endif
278        .set LOAD_BANK, 1
279       .else
280        .if FIR_REMAIN == 0 && IIR_REMAIN == 1
281         load    ST, LOAD_REG, PST, OFFSET_ST
282         inc     1
283        .else
284         loadd   ST, LOAD_REG, PST, OFFSET_ST
285         inc     2
286        .endif
287        .set LOAD_BANK, 0
288       .endif
289      .endif
290
291         // Do interleaved multiplies, slightly delayed
292      .if COUNTER >= 2
293         multiply MUL_REG, COUNTER > 2, !\shift_0
294       .set MUL_REG, (MUL_REG + 1) & 3
295      .endif
296      .set COUNTER, COUNTER + 1
297     .endr
298
299         // Post-process the result of the multiplies
300     .if SPILL_SHIFT
301         ldr     SHIFT, [sp, #9*4 + 0*4]
302     .endif
303     .if SPILL_MASK
304         ldr     MASK, [sp, #9*4 + 1*4]
305     .endif
306         ldr     ST2, [PSAMP]
307         subs    I, I, #1
308     .if \shift_8
309         mov     AC0, AC0, lsr #8
310         orr     AC0, AC0, AC1, lsl #24
311     .elseif !\shift_0
312         rsb     ST3, SHIFT, #32
313         mov     AC0, AC0, lsr SHIFT
314 A       orr     AC0, AC0, AC1, lsl ST3
315 T       mov     AC1, AC1, lsl ST3
316 T       orr     AC0, AC0, AC1
317     .endif
318     .if \mask_minus1
319         add     ST3, ST2, AC0
320     .else
321         add     ST2, ST2, AC0
322         and     ST3, ST2, MASK
323         sub     ST2, ST3, AC0
324     .endif
325         str     ST3, [PST, #-4]!
326         str     ST2, [PST, #4 * (MAX_BLOCKSIZE + MAX_FIR_ORDER)]
327         str     ST3, [PSAMP], #4 * MAX_CHANNELS
328         bne     01b
329   .endif
330         b       99f
331
332   .if DEFINED_SHIFT
333     .unreq SHIFT
334   .endif
335   .if DEFINED_MASK
336     .unreq MASK
337   .endif
338 .endm
339
340 .macro switch_on_fir_taps  mask_minus1, shift_0, shift_8, iir_taps
341 A       ldr     pc, [pc, a3, LSL #2] // firorder is in range 0-(8-iir_taps)
342 T       tbh     [pc, a3, lsl #1]
343 0:
344 A       .word   0, 70f, 71f, 72f, 73f, 74f
345 T       .hword  (70f - 0b) / 2, (71f - 0b) / 2, (72f - 0b) / 2, (73f - 0b) / 2, (74f - 0b) / 2
346  .if \iir_taps <= 3
347 A       .word   75f
348 T       .hword  (75f - 0b) / 2
349   .if \iir_taps <= 2
350 A       .word   76f
351 T       .hword  (76f - 0b) / 2
352    .if \iir_taps <= 1
353 A       .word   77f
354 T       .hword  (77f - 0b) / 2
355     .if \iir_taps == 0
356 A       .word   78f
357 T       .hword  (78f - 0b) / 2
358     .endif
359    .endif
360   .endif
361  .endif
362 70:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 0
363 71:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 1
364 72:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 2
365 73:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 3
366 74:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 4
367  .if \iir_taps <= 3
368 75:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 5
369   .if \iir_taps <= 2
370 76:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 6
371    .if \iir_taps <= 1
372 77:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 7
373     .if \iir_taps == 0
374 78:     implement_filter  \mask_minus1, \shift_0, \shift_8, \iir_taps, 8
375     .endif
376    .endif
377   .endif
378  .endif
379 .endm
380
381 .macro switch_on_iir_taps  mask_minus1, shift_0, shift_8
382 A       ldr     pc, [pc, a4, LSL #2] // irorder is in range 0-4
383 T       tbh    [pc, a4, lsl #1]
384 0:
385 A       .word   0, 60f, 61f, 62f, 63f, 64f
386 T       .hword  (60f - 0b) / 2, (61f - 0b) / 2, (62f - 0b) / 2, (63f - 0b) / 2, (64f - 0b) / 2
387 60:     switch_on_fir_taps  \mask_minus1, \shift_0, \shift_8, 0
388 61:     switch_on_fir_taps  \mask_minus1, \shift_0, \shift_8, 1
389 62:     switch_on_fir_taps  \mask_minus1, \shift_0, \shift_8, 2
390 63:     switch_on_fir_taps  \mask_minus1, \shift_0, \shift_8, 3
391 64:     switch_on_fir_taps  \mask_minus1, \shift_0, \shift_8, 4
392 .endm
393
394 /* void ff_mlp_filter_channel_arm(int32_t *state, const int32_t *coeff,
395  *                                int firorder, int iirorder,
396  *                                unsigned int filter_shift, int32_t mask,
397  *                                int blocksize, int32_t *sample_buffer);
398  */
399 function ff_mlp_filter_channel_arm, export=1
400         push    {v1-fp,lr}
401         add     v1, sp, #9*4 // point at arguments on stack
402         ldm     v1, {ST0,ST1,I,PSAMP}
403         cmp     ST1, #-1
404         bne     30f
405         movs    ST2, ST0, lsl #29 // shift is in range 0-15; we want to special-case 0 and 8
406         bne     20f
407         bcs     10f
408         switch_on_iir_taps 1, 1, 0
409 10:     switch_on_iir_taps 1, 0, 1
410 20:     switch_on_iir_taps 1, 0, 0
411 30:     movs    ST2, ST0, lsl #29 // shift is in range 0-15; we want to special-case 0 and 8
412         bne     50f
413         bcs     40f
414         switch_on_iir_taps 0, 1, 0
415 40:     switch_on_iir_taps 0, 0, 1
416 50:     switch_on_iir_taps 0, 0, 0
417 99:     pop     {v1-fp,pc}
418 endfunc
419
420         .unreq  PST
421         .unreq  PCO
422         .unreq  AC0
423         .unreq  AC1
424         .unreq  CO0
425         .unreq  CO1
426         .unreq  CO2
427         .unreq  CO3
428         .unreq  ST0
429         .unreq  ST1
430         .unreq  ST2
431         .unreq  ST3
432         .unreq  I
433         .unreq  PSAMP