1/*
2 * Copyright (C) 2013-2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define ENTRY(f) .text; .align 4; .globl f; .type f,#function; f: .fnstart
18#define END(f) .fnend; .size f, .-f;
19
20#define BLEND_LIST(X) \
21    X(0, CLEAR) \
22    X(1, SRC) \
23    X(2, DST) \
24    X(3, SRC_OVER) \
25    X(4, DST_OVER) \
26    X(5, SRC_IN) \
27    X(6, DST_IN) \
28    X(7, SRC_OUT) \
29    X(8, DST_OUT) \
30    X(9, SRC_ATOP) \
31    X(10, DST_ATOP) \
32    X(11, XOR) \
33    X(14, MULTIPLY) \
34    X(21, DIFFERENCE) \
35    X(34, ADD) \
36    X(35, SUBTRACT)
37
38.eabi_attribute 25,1 @Tag_ABI_align8_preserved
39.arm
40
41/* For every blend operation supported, define a macro with just the arithmetic
42 * component.  The rest can be handled later on.
43 *
44 * At entry q0-q3 contain the RGBA data from the destination buffer, and q8-q11
45 * contain the data from the source buffer.  Both have already been split out
46 * into one colour component per register (if necessary).  q3 and q11 contain
47 * the alpha components.
48 *
49 * At the same time as defining the assembly macro, define a corresponding
50 * preprocessor macro indicating any other requirements.
51 *    zipped=0 -- The macro does not require the RGBA components to be
52 *                separated.
53 *    lddst=0  -- The macro does not require data from the destination buffer.
54 *    ldsrc=0  -- The macro does not require data from the source buffer.
55 *    nowrap=1 -- The macro requires no wrapper at all, and should simply be
56 *                inserted without any surrounding load/store or loop code.
57 */
58
59#define params_CLEAR zipped=0, lddst=0, ldsrc=0
60.macro blend_kernel_CLEAR
61        vmov.i8 q0, #0
62        vmov.i8 q1, #0
63        vmov.i8 q2, #0
64        vmov.i8 q3, #0
65.endm
66
67#define params_SRC zipped=0, lddst=0
68.macro blend_kernel_SRC
69        vmov    q0, q8
70        vmov    q1, q9
71        vmov    q2, q10
72        vmov    q3, q11
73.endm
74
75#define params_DST nowrap=1
76.macro blend_kernel_DST
77        /* nop */
78.endm
79
80#define params_SRC_OVER zipped=1
81.macro blend_kernel_SRC_OVER
82        vmvn        q7, q11
83
84        vmull.u8    q12, d15, d1
85        vmull.u8    q0,  d14, d0
86        vmull.u8    q13, d15, d3
87        vmull.u8    q1,  d14, d2
88        vmull.u8    q14, d15, d5
89        vmull.u8    q2,  d14, d4
90        vmull.u8    q15, d15, d7
91        vmull.u8    q3,  d14, d6
92
93        vrshrn.u16  d8,  q0,  #8
94        vrshrn.u16  d9,  q12, #8
95        vrshrn.u16  d10, q1,  #8
96        vrshrn.u16  d11, q13, #8
97        vrshrn.u16  d12, q2,  #8
98        vrshrn.u16  d13, q14, #8
99        vrshrn.u16  d14, q3,  #8
100        vrshrn.u16  d15, q15, #8
101
102        vaddw.u8    q0,  d8
103        vaddw.u8    q12, d9
104        vaddw.u8    q1,  d10
105        vaddw.u8    q13, d11
106        vaddw.u8    q2,  d12
107        vaddw.u8    q14, d13
108        vaddw.u8    q3,  d14
109        vaddw.u8    q15, d15
110
111        vrshrn.u16  d0, q0,  #8
112        vrshrn.u16  d1, q12, #8
113        vrshrn.u16  d2, q1,  #8
114        vrshrn.u16  d3, q13, #8
115        vrshrn.u16  d4, q2,  #8
116        vrshrn.u16  d5, q14, #8
117        vrshrn.u16  d6, q3,  #8
118        vrshrn.u16  d7, q15, #8
119
120        vqadd.u8    q0, q8
121        vqadd.u8    q1, q9
122        vqadd.u8    q2, q10
123        vqadd.u8    q3, q11
124.endm
125
126#define params_DST_OVER zipped=1
127.macro blend_kernel_DST_OVER
128        vmvn        q7, q3
129
130        vmull.u8    q12, d15, d17
131        vmull.u8    q8,  d14, d16
132        vmull.u8    q13, d15, d19
133        vmull.u8    q9,  d14, d18
134        vmull.u8    q14, d15, d21
135        vmull.u8    q10, d14, d20
136        vmull.u8    q15, d15, d23
137        vmull.u8    q11, d14, d22
138
139        vrshrn.u16  d8,  q0,  #8
140        vrshrn.u16  d9,  q12, #8
141        vrshrn.u16  d10, q1,  #8
142        vrshrn.u16  d11, q13, #8
143        vrshrn.u16  d12, q2,  #8
144        vrshrn.u16  d13, q14, #8
145        vrshrn.u16  d14, q3,  #8
146        vrshrn.u16  d15, q15, #8
147
148        vaddw.u8    q8,  d8
149        vaddw.u8    q12, d9
150        vaddw.u8    q9,  d10
151        vaddw.u8    q13, d11
152        vaddw.u8    q10, d12
153        vaddw.u8    q14, d13
154        vaddw.u8    q11, d14
155        vaddw.u8    q15, d15
156
157        vrshrn.u16  d16, q8,  #8
158        vrshrn.u16  d17, q12, #8
159        vrshrn.u16  d18, q9,  #8
160        vrshrn.u16  d19, q13, #8
161        vrshrn.u16  d20, q10, #8
162        vrshrn.u16  d21, q14, #8
163        vrshrn.u16  d22, q11, #8
164        vrshrn.u16  d23, q15, #8
165
166        vqadd.u8    q0, q8
167        vqadd.u8    q1, q9
168        vqadd.u8    q2, q10
169        vqadd.u8    q3, q11
170.endm
171
172#define params_SRC_IN zipped=1
173.macro blend_kernel_SRC_IN
174        vmull.u8    q12, d7, d17
175        vmull.u8    q0,  d6, d16
176        vmull.u8    q13, d7, d19
177        vmull.u8    q1,  d6, d18
178        vmull.u8    q14, d7, d21
179        vmull.u8    q2,  d6, d20
180        vmull.u8    q15, d7, d23
181        vmull.u8    q3,  d6, d22
182
183        vrshrn.u16  d8,  q0,  #8
184        vrshrn.u16  d9,  q12, #8
185        vrshrn.u16  d10, q1,  #8
186        vrshrn.u16  d11, q13, #8
187        vrshrn.u16  d12, q2,  #8
188        vrshrn.u16  d13, q14, #8
189        vrshrn.u16  d14, q3,  #8
190        vrshrn.u16  d15, q15, #8
191
192        vaddw.u8    q0,  d8
193        vaddw.u8    q12, d9
194        vaddw.u8    q1,  d10
195        vaddw.u8    q13, d11
196        vaddw.u8    q2,  d12
197        vaddw.u8    q14, d13
198        vaddw.u8    q3,  d14
199        vaddw.u8    q15, d15
200
201        vrshrn.u16  d0, q0,  #8
202        vrshrn.u16  d1, q12, #8
203        vrshrn.u16  d2, q1,  #8
204        vrshrn.u16  d3, q13, #8
205        vrshrn.u16  d4, q2,  #8
206        vrshrn.u16  d5, q14, #8
207        vrshrn.u16  d6, q3,  #8
208        vrshrn.u16  d7, q15, #8
209.endm
210
211#define params_DST_IN zipped=1
212.macro blend_kernel_DST_IN
213        vmull.u8    q12, d1, d23
214        vmull.u8    q0,  d0, d22
215        vmull.u8    q13, d3, d23
216        vmull.u8    q1,  d2, d22
217        vmull.u8    q14, d5, d23
218        vmull.u8    q2,  d4, d22
219        vmull.u8    q15, d7, d23
220        vmull.u8    q3,  d6, d22
221
222        vrshrn.u16  d8,  q0,  #8
223        vrshrn.u16  d9,  q12, #8
224        vrshrn.u16  d10, q1,  #8
225        vrshrn.u16  d11, q13, #8
226        vrshrn.u16  d12, q2,  #8
227        vrshrn.u16  d13, q14, #8
228        vrshrn.u16  d14, q3,  #8
229        vrshrn.u16  d15, q15, #8
230
231        vaddw.u8    q0,  d8
232        vaddw.u8    q12, d9
233        vaddw.u8    q1,  d10
234        vaddw.u8    q13, d11
235        vaddw.u8    q2,  d12
236        vaddw.u8    q14, d13
237        vaddw.u8    q3,  d14
238        vaddw.u8    q15, d15
239
240        vrshrn.u16  d0, q0,  #8
241        vrshrn.u16  d1, q12, #8
242        vrshrn.u16  d2, q1,  #8
243        vrshrn.u16  d3, q13, #8
244        vrshrn.u16  d4, q2,  #8
245        vrshrn.u16  d5, q14, #8
246        vrshrn.u16  d6, q3,  #8
247        vrshrn.u16  d7, q15, #8
248.endm
249
250#define params_SRC_OUT zipped=1
251.macro blend_kernel_SRC_OUT
252        vmvn        q3, q3
253        blend_kernel_SRC_IN
254.endm
255
256
257#define params_DST_OUT zipped=1
258.macro blend_kernel_DST_OUT
259        vmvn        q11, q11
260        blend_kernel_DST_IN
261.endm
262
263#define params_SRC_ATOP zipped=1
264.macro blend_kernel_SRC_ATOP
265        vmvn        q11, q11
266
267        vmull.u8    q12, d23, d1
268        vmull.u8    q0,  d22, d0
269        vmull.u8    q13, d23, d3
270        vmull.u8    q1,  d22, d2
271        vmull.u8    q14, d23, d5
272        vmull.u8    q2,  d22, d4
273
274        vmull.u8    q4,  d7, d17
275        vmull.u8    q8,  d6, d16
276        vmull.u8    q5,  d7, d19
277        vmull.u8    q9,  d6, d18
278        vmull.u8    q6,  d7, d21
279        vmull.u8    q10, d6, d20
280
281        vqadd.u16   q12, q4
282        vqadd.u16   q0,  q8
283        vqadd.u16   q13, q5
284        vqadd.u16   q1,  q9
285        vqadd.u16   q14, q6
286        vqadd.u16   q2,  q10
287
288        vrshr.u16   q8,  q0,  #8
289        vrshr.u16   q4,  q12, #8
290        vrshr.u16   q9,  q1,  #8
291        vrshr.u16   q5,  q13, #8
292        vrshr.u16   q10, q2,  #8
293        vrshr.u16   q6,  q14, #8
294
295        vqadd.u16   q0,  q8
296        vqadd.u16   q12, q4
297        vqadd.u16   q1,  q9
298        vqadd.u16   q13, q5
299        vqadd.u16   q2,  q10
300        vqadd.u16   q14, q6
301
302        vqrshrn.u16 d0, q0,  #8
303        vqrshrn.u16 d1, q12, #8
304        vqrshrn.u16 d2, q1,  #8
305        vqrshrn.u16 d3, q13, #8
306        vqrshrn.u16 d4, q2,  #8
307        vqrshrn.u16 d5, q14, #8
308.endm
309
310#define params_DST_ATOP zipped=1
311.macro blend_kernel_DST_ATOP
312        vmvn        q3, q3
313
314        vmull.u8    q12, d23, d1
315        vmull.u8    q0,  d22, d0
316        vmull.u8    q13, d23, d3
317        vmull.u8    q1,  d22, d2
318        vmull.u8    q14, d23, d5
319        vmull.u8    q2,  d22, d4
320
321        vmull.u8    q4,  d7, d17
322        vmull.u8    q8,  d6, d16
323        vmull.u8    q5,  d7, d19
324        vmull.u8    q9,  d6, d18
325        vmull.u8    q6,  d7, d21
326        vmull.u8    q10, d6, d20
327
328        vqadd.u16   q12, q4
329        vqadd.u16   q0,  q8
330        vqadd.u16   q13, q5
331        vqadd.u16   q1,  q9
332        vqadd.u16   q14, q6
333        vqadd.u16   q2,  q10
334
335        vrshr.u16   q8,  q0,  #8
336        vrshr.u16   q4,  q12, #8
337        vrshr.u16   q9,  q1,  #8
338        vrshr.u16   q5,  q13, #8
339        vrshr.u16   q10, q2,  #8
340        vrshr.u16   q6,  q14, #8
341
342        vqadd.u16   q0,  q8
343        vqadd.u16   q12, q4
344        vqadd.u16   q1,  q9
345        vqadd.u16   q13, q5
346        vqadd.u16   q2,  q10
347        vqadd.u16   q14, q6
348
349        vqrshrn.u16 d0, q0,  #8
350        vqrshrn.u16 d1, q12, #8
351        vqrshrn.u16 d2, q1,  #8
352        vqrshrn.u16 d3, q13, #8
353        vqrshrn.u16 d4, q2,  #8
354        vqrshrn.u16 d5, q14, #8
355
356        vmov        q3, q11
357.endm
358
359#define params_MULTIPLY zipped=0
360.macro blend_kernel_MULTIPLY
361        vmull.u8    q12, d1, d17
362        vmull.u8    q0,  d0, d16
363        vmull.u8    q13, d3, d19
364        vmull.u8    q1,  d2, d18
365        vmull.u8    q14, d5, d21
366        vmull.u8    q2,  d4, d20
367        vmull.u8    q15, d7, d23
368        vmull.u8    q3,  d6, d22
369
370        vrshrn.u16  d8,  q0,  #8
371        vrshrn.u16  d9,  q12, #8
372        vrshrn.u16  d10, q1,  #8
373        vrshrn.u16  d11, q13, #8
374        vrshrn.u16  d12, q2,  #8
375        vrshrn.u16  d13, q14, #8
376        vrshrn.u16  d14, q3,  #8
377        vrshrn.u16  d15, q15, #8
378
379        vaddw.u8    q0,  d8
380        vaddw.u8    q12, d9
381        vaddw.u8    q1,  d10
382        vaddw.u8    q13, d11
383        vaddw.u8    q2,  d12
384        vaddw.u8    q14, d13
385        vaddw.u8    q3,  d14
386        vaddw.u8    q15, d15
387
388        vrshrn.u16  d0, q0,  #8
389        vrshrn.u16  d1, q12, #8
390        vrshrn.u16  d2, q1,  #8
391        vrshrn.u16  d3, q13, #8
392        vrshrn.u16  d4, q2,  #8
393        vrshrn.u16  d5, q14, #8
394        vrshrn.u16  d6, q3,  #8
395        vrshrn.u16  d7, q15, #8
396.endm
397
398#define params_ADD zipped=0
399.macro blend_kernel_ADD
400        vqadd.u8 q0, q0, q8
401        vqadd.u8 q1, q1, q9
402        vqadd.u8 q2, q2, q10
403        vqadd.u8 q3, q3, q11
404.endm
405
406#define params_SUBTRACT zipped=0
407.macro blend_kernel_SUBTRACT
408        vqsub.u8 q0, q0, q8
409        vqsub.u8 q1, q1, q9
410        vqsub.u8 q2, q2, q10
411        vqsub.u8 q3, q3, q11
412.endm
413
414#define params_DIFFERENCE zipped=0
415.macro blend_kernel_DIFFERENCE
416        vabd.u8 q0, q0, q8
417        vabd.u8 q1, q1, q9
418        vabd.u8 q2, q2, q10
419        vabd.u8 q3, q3, q11
420.endm
421
422#define params_XOR zipped=0
423.macro blend_kernel_XOR
424        veor    q0, q0, q8
425        veor    q1, q1, q9
426        veor    q2, q2, q10
427        veor    q3, q3, q11
428.endm
429
430
431/* Define the wrapper code which will load and store the data, iterate the
432 * correct number of times, and safely handle the remainder at the end of the
433 * loop.  Various sections of assembly code are dropped or substituted for
434 * simpler operations if they're not needed.
435 */
436.macro wrap_line kernel, nowrap=0, zipped=1, lddst=1, ldsrc=1, pld=1
437.if \nowrap
438        \kernel
439.else
440        vpush   {d8-d15}
441        subs    r2, #64
442        b       2f
443        .align 4
4441:
445  .if \lddst
446    .if \zipped
447        vld4.8  {d0,d2,d4,d6}, [r0]!
448        vld4.8  {d1,d3,d5,d7}, [r0]!
449    .else
450        vld1.8  {d0-d3}, [r0]!
451        vld1.8  {d4-d7}, [r0]!
452    .endif
453        sub     r0, #64
454  .endif
455  .if \ldsrc
456    .if \zipped
457        vld4.8  {d16,d18,d20,d22}, [r1]!
458        vld4.8  {d17,d19,d21,d23}, [r1]!
459    .else
460        vld1.8  {d16-d19}, [r1]!
461        vld1.8  {d20-d23}, [r1]!
462    .endif
463  .endif
464  .if \pld
465    .if \lddst ; pld [r0, #192] ; .endif
466    .if \ldsrc ; pld [r1, #192] ; .endif
467  .endif
468
469        \kernel
470
471        subs    r2, #64
472  .if \zipped
473        vst4.8  {d0,d2,d4,d6}, [r0]!
474        vst4.8  {d1,d3,d5,d7}, [r0]!
475  .else
476        vst1.8  {d0-d3}, [r0]!
477        vst1.8  {d4-d7}, [r0]!
478  .endif
479
4802:      bge     1b
481        adds    r2, #64
482        beq     2f
483
484        /* To handle the tail portion of the data (something less than 64
485         * bytes) load small power-of-two chunks into working registers.  It
486         * doesn't matter where they end up in the register; the same process
487         * will store them back out using the same positions and the operations
488         * don't require data to interact with its neighbours.
489         */
490        vmov.i8 q0, #0
491        vmov.i8 q1, #0
492        vmov.i8 q2, #0
493        vmov.i8 q3, #0
494
495        vmov.i8 q8, #0
496        vmov.i8 q9, #0
497        vmov.i8 q10, #0
498        vmov.i8 q11, #0
499
500        tst     r2, #32
501        beq     1f
502  .if \lddst ; vld1.64 {d4-d7}, [r0]!   ; .endif
503  .if \ldsrc ; vld1.64 {d20-d23}, [r1]! ; .endif
5041:      tst     r2, #16
505        beq     1f
506  .if \lddst ; vld1.64 {d2-d3}, [r0]!   ; .endif
507  .if \ldsrc ; vld1.64 {d18-d19}, [r1]! ; .endif
5081:      tst     r2, #8
509        beq     1f
510  .if \lddst ; vld1.64 {d1}, [r0]!      ; .endif
511  .if \ldsrc ; vld1.64 {d17}, [r1]!     ; .endif
5121:      tst     r2, #4
513        beq     1f
514  .if \lddst ; vld1.32 {d0[1]}, [r0]!   ; .endif
515  .if \ldsrc ; vld1.32 {d16[1]}, [r1]!  ; .endif
5161:      tst     r2, #2
517        beq     1f
518  .if \lddst ; vld1.16 {d0[1]}, [r0]!   ; .endif
519  .if \ldsrc ; vld1.16 {d16[1]}, [r1]!  ; .endif
5201:      tst     r2, #1
521        beq     1f
522  .if \lddst ; vld1.8  {d0[1]}, [r0]!   ; .endif
523  .if \ldsrc ; vld1.8  {d16[1]}, [r1]!  ; .endif
5241:
525  .if \lddst ; sub     r0, r2           ; .endif
526
527  .if \zipped
528        /* One small impediment in the process above is that some of the load
529         * operations can't perform byte-wise structure deinterleaving at the
530         * same time as loading only part of a register.  So the data is loaded
531         * linearly and unpacked manually at this point.
532         */
533        vuzp.8  q0, q1
534        vuzp.8  q2, q3
535        vuzp.8  q0, q2
536        vuzp.8  q1, q3
537
538        vuzp.8  q8, q9
539        vuzp.8  q10, q11
540        vuzp.8  q8, q10
541        vuzp.8  q9, q11
542
543        \kernel
544
545        vzip.8  q0, q2
546        vzip.8  q1, q3
547        vzip.8  q0, q1
548        vzip.8  q2, q3
549  .else
550        \kernel
551  .endif
552
553        tst     r2, #32
554        beq     1f
555        vst1.64 {d4-d7}, [r0]!
5561:      tst     r2, #16
557        beq     1f
558        vst1.64 {d2-d3}, [r0]!
5591:      tst     r2, #8
560        beq     1f
561        vst1.64 {d1}, [r0]!
5621:      tst     r2, #4
563        beq     1f
564        vst1.32 {d0[1]}, [r0]!
5651:      tst     r2, #2
566        beq     1f
567        vst1.16 {d0[1]}, [r0]!
5681:      tst     r2, #1
569        beq     2f
570        vst1.8  {d0[1]}, [r0]!
5712:      vpop    {d8-d15}
572.endif
573        mov     r0, #0
574        bx      lr
575.endm
576
577
578/* produce list of blend_line_XX() functions; each function uses the wrap_line
579 * macro, passing it the name of the operation macro it wants along with
580 * optional parameters to remove unnecessary operations.
581 */
582#define BLEND_X(d, n) ENTRY(blend_line_##n) ; wrap_line blend_kernel_##n, params_##n ; END(blend_line_##n) ;
583    BLEND_LIST(BLEND_X)
584#undef BLEND_X
585
586
587/*  int rsdIntrinsicBlend_K(
588 *          uchar4 *out,        // r0
589 *          uchar4 const *in,   // r1
590 *          int slot,           // r2
591 *          size_t xstart,      // r3
592 *          size_t xend);       // [sp]
593 */
594ENTRY(rsdIntrinsicBlend_K)
595    adr     ip, blend_functions
596    cmp     r2, #(blend_functions_end - blend_functions) >> 2
597    ldrlo   ip, [ip, r2, LSL #2]
598    movhs   ip, #0
599    ldr     r2, [sp]
600    add     r0, r3, LSL #2
601    add     r1, r3, LSL #2
602    sub     r2, r3
603    mov     r2, r2, LSL #2
604    cmp     ip, #0
605    addne   ip, ip, pc
606    bxne    ip
6071:  mov     r0, #-1
608    bx      lr
609
610blend_functions:
611.set off,0
612#define BLEND_X(d, n) .rept d-off ; .word 0 ; .endr ; .word blend_line_##n-1b ; .set off, d+1 ;
613        BLEND_LIST(BLEND_X)
614#undef BLEND_X
615blend_functions_end:
616
617END(rsdIntrinsicBlend_K)
618