/* * Copyright (c) 2016, Alliance for Open Media. All rights reserved * * This source code is subject to the terms of the BSD 2 Clause License and * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License * was not distributed with this source code in the LICENSE file, you can * obtain it at www.aomedia.org/license/software. If the Alliance for Open * Media Patent License 1.0 was not distributed with this source code in the * PATENTS file, you can obtain it at www.aomedia.org/license/patent. */ #include #include "config/aom_dsp_rtcd.h" #include "aom/aom_integer.h" #include "aom_dsp/mips/common_dspr2.h" #include "aom_dsp/mips/loopfilter_filters_dspr2.h" #include "aom_dsp/mips/loopfilter_macros_dspr2.h" #include "aom_dsp/mips/loopfilter_masks_dspr2.h" #include "aom_mem/aom_mem.h" #if HAVE_DSPR2 void aom_lpf_horizontal_4_dspr2(unsigned char *s, int pitch, const uint8_t *blimit, const uint8_t *limit, const uint8_t *thresh) { uint8_t i; uint32_t mask; uint32_t hev; uint32_t pm1, p0, p1, p2, p3, p4, p5, p6; uint8_t *sm1, *s0, *s1, *s2, *s3, *s4, *s5, *s6; uint32_t thresh_vec, flimit_vec, limit_vec; uint32_t uflimit, ulimit, uthresh; uflimit = *blimit; ulimit = *limit; uthresh = *thresh; /* create quad-byte */ __asm__ __volatile__( "replv.qb %[thresh_vec], %[uthresh] \n\t" "replv.qb %[flimit_vec], %[uflimit] \n\t" "replv.qb %[limit_vec], %[ulimit] \n\t" : [thresh_vec] "=&r"(thresh_vec), [flimit_vec] "=&r"(flimit_vec), [limit_vec] "=r"(limit_vec) : [uthresh] "r"(uthresh), [uflimit] "r"(uflimit), [ulimit] "r"(ulimit)); /* prefetch data for store */ prefetch_store(s); /* loop filter designed to work using chars so that we can make maximum use of 8 bit simd instructions. */ for (i = 0; i < 2; i++) { sm1 = s - (pitch << 2); s0 = sm1 + pitch; s1 = s0 + pitch; s2 = s - pitch; s3 = s; s4 = s + pitch; s5 = s4 + pitch; s6 = s5 + pitch; __asm__ __volatile__( "lw %[p1], (%[s1]) \n\t" "lw %[p2], (%[s2]) \n\t" "lw %[p3], (%[s3]) \n\t" "lw %[p4], (%[s4]) \n\t" : [p1] "=&r"(p1), [p2] "=&r"(p2), [p3] "=&r"(p3), [p4] "=&r"(p4) : [s1] "r"(s1), [s2] "r"(s2), [s3] "r"(s3), [s4] "r"(s4)); /* if (p1 - p4 == 0) and (p2 - p3 == 0) mask will be zero and filtering is not needed */ if (!(((p1 - p4) == 0) && ((p2 - p3) == 0))) { __asm__ __volatile__( "lw %[pm1], (%[sm1]) \n\t" "lw %[p0], (%[s0]) \n\t" "lw %[p5], (%[s5]) \n\t" "lw %[p6], (%[s6]) \n\t" : [pm1] "=&r"(pm1), [p0] "=&r"(p0), [p5] "=&r"(p5), [p6] "=&r"(p6) : [sm1] "r"(sm1), [s0] "r"(s0), [s5] "r"(s5), [s6] "r"(s6)); filter_hev_mask_dspr2(limit_vec, flimit_vec, p1, p2, pm1, p0, p3, p4, p5, p6, thresh_vec, &hev, &mask); /* if mask == 0 do filtering is not needed */ if (mask) { /* filtering */ filter_dspr2(mask, hev, &p1, &p2, &p3, &p4); __asm__ __volatile__( "sw %[p1], (%[s1]) \n\t" "sw %[p2], (%[s2]) \n\t" "sw %[p3], (%[s3]) \n\t" "sw %[p4], (%[s4]) \n\t" : : [p1] "r"(p1), [p2] "r"(p2), [p3] "r"(p3), [p4] "r"(p4), [s1] "r"(s1), [s2] "r"(s2), [s3] "r"(s3), [s4] "r"(s4)); } } s = s + 4; } } void aom_lpf_vertical_4_dspr2(unsigned char *s, int pitch, const uint8_t *blimit, const uint8_t *limit, const uint8_t *thresh) { uint8_t i; uint32_t mask, hev; uint32_t pm1, p0, p1, p2, p3, p4, p5, p6; uint8_t *s1, *s2, *s3, *s4; uint32_t prim1, prim2, sec3, sec4, prim3, prim4; uint32_t thresh_vec, flimit_vec, limit_vec; uint32_t uflimit, ulimit, uthresh; uflimit = *blimit; ulimit = *limit; uthresh = *thresh; /* create quad-byte */ __asm__ __volatile__( "replv.qb %[thresh_vec], %[uthresh] \n\t" "replv.qb %[flimit_vec], %[uflimit] \n\t" "replv.qb %[limit_vec], %[ulimit] \n\t" : [thresh_vec] "=&r"(thresh_vec), [flimit_vec] "=&r"(flimit_vec), [limit_vec] "=r"(limit_vec) : [uthresh] "r"(uthresh), [uflimit] "r"(uflimit), [ulimit] "r"(ulimit)); /* prefetch data for store */ prefetch_store(s + pitch); for (i = 0; i < 2; i++) { s1 = s; s2 = s + pitch; s3 = s2 + pitch; s4 = s3 + pitch; s = s4 + pitch; /* load quad-byte vectors * memory is 4 byte aligned */ p2 = *((uint32_t *)(s1 - 4)); p6 = *((uint32_t *)(s1)); p1 = *((uint32_t *)(s2 - 4)); p5 = *((uint32_t *)(s2)); p0 = *((uint32_t *)(s3 - 4)); p4 = *((uint32_t *)(s3)); pm1 = *((uint32_t *)(s4 - 4)); p3 = *((uint32_t *)(s4)); /* transpose pm1, p0, p1, p2 */ __asm__ __volatile__( "precrq.qb.ph %[prim1], %[p2], %[p1] \n\t" "precr.qb.ph %[prim2], %[p2], %[p1] \n\t" "precrq.qb.ph %[prim3], %[p0], %[pm1] \n\t" "precr.qb.ph %[prim4], %[p0], %[pm1] \n\t" "precrq.qb.ph %[p1], %[prim1], %[prim2] \n\t" "precr.qb.ph %[pm1], %[prim1], %[prim2] \n\t" "precrq.qb.ph %[sec3], %[prim3], %[prim4] \n\t" "precr.qb.ph %[sec4], %[prim3], %[prim4] \n\t" "precrq.ph.w %[p2], %[p1], %[sec3] \n\t" "precrq.ph.w %[p0], %[pm1], %[sec4] \n\t" "append %[p1], %[sec3], 16 \n\t" "append %[pm1], %[sec4], 16 \n\t" : [prim1] "=&r"(prim1), [prim2] "=&r"(prim2), [prim3] "=&r"(prim3), [prim4] "=&r"(prim4), [p2] "+r"(p2), [p1] "+r"(p1), [p0] "+r"(p0), [pm1] "+r"(pm1), [sec3] "=&r"(sec3), [sec4] "=&r"(sec4) :); /* transpose p3, p4, p5, p6 */ __asm__ __volatile__( "precrq.qb.ph %[prim1], %[p6], %[p5] \n\t" "precr.qb.ph %[prim2], %[p6], %[p5] \n\t" "precrq.qb.ph %[prim3], %[p4], %[p3] \n\t" "precr.qb.ph %[prim4], %[p4], %[p3] \n\t" "precrq.qb.ph %[p5], %[prim1], %[prim2] \n\t" "precr.qb.ph %[p3], %[prim1], %[prim2] \n\t" "precrq.qb.ph %[sec3], %[prim3], %[prim4] \n\t" "precr.qb.ph %[sec4], %[prim3], %[prim4] \n\t" "precrq.ph.w %[p6], %[p5], %[sec3] \n\t" "precrq.ph.w %[p4], %[p3], %[sec4] \n\t" "append %[p5], %[sec3], 16 \n\t" "append %[p3], %[sec4], 16 \n\t" : [prim1] "=&r"(prim1), [prim2] "=&r"(prim2), [prim3] "=&r"(prim3), [prim4] "=&r"(prim4), [p6] "+r"(p6), [p5] "+r"(p5), [p4] "+r"(p4), [p3] "+r"(p3), [sec3] "=&r"(sec3), [sec4] "=&r"(sec4) :); /* if (p1 - p4 == 0) and (p2 - p3 == 0) * mask will be zero and filtering is not needed */ if (!(((p1 - p4) == 0) && ((p2 - p3) == 0))) { filter_hev_mask_dspr2(limit_vec, flimit_vec, p1, p2, pm1, p0, p3, p4, p5, p6, thresh_vec, &hev, &mask); /* if mask == 0 do filtering is not needed */ if (mask) { /* filtering */ filter_dspr2(mask, hev, &p1, &p2, &p3, &p4); /* unpack processed 4x4 neighborhood * don't use transpose on output data * because memory isn't aligned */ __asm__ __volatile__( "sb %[p4], 1(%[s4]) \n\t" "sb %[p3], 0(%[s4]) \n\t" "sb %[p2], -1(%[s4]) \n\t" "sb %[p1], -2(%[s4]) \n\t" : : [p4] "r"(p4), [p3] "r"(p3), [p2] "r"(p2), [p1] "r"(p1), [s4] "r"(s4)); __asm__ __volatile__( "srl %[p4], %[p4], 8 \n\t" "srl %[p3], %[p3], 8 \n\t" "srl %[p2], %[p2], 8 \n\t" "srl %[p1], %[p1], 8 \n\t" : [p4] "+r"(p4), [p3] "+r"(p3), [p2] "+r"(p2), [p1] "+r"(p1) :); __asm__ __volatile__( "sb %[p4], 1(%[s3]) \n\t" "sb %[p3], 0(%[s3]) \n\t" "sb %[p2], -1(%[s3]) \n\t" "sb %[p1], -2(%[s3]) \n\t" : [p1] "+r"(p1) : [p4] "r"(p4), [p3] "r"(p3), [p2] "r"(p2), [s3] "r"(s3)); __asm__ __volatile__( "srl %[p4], %[p4], 8 \n\t" "srl %[p3], %[p3], 8 \n\t" "srl %[p2], %[p2], 8 \n\t" "srl %[p1], %[p1], 8 \n\t" : [p4] "+r"(p4), [p3] "+r"(p3), [p2] "+r"(p2), [p1] "+r"(p1) :); __asm__ __volatile__( "sb %[p4], 1(%[s2]) \n\t" "sb %[p3], 0(%[s2]) \n\t" "sb %[p2], -1(%[s2]) \n\t" "sb %[p1], -2(%[s2]) \n\t" : : [p4] "r"(p4), [p3] "r"(p3), [p2] "r"(p2), [p1] "r"(p1), [s2] "r"(s2)); __asm__ __volatile__( "srl %[p4], %[p4], 8 \n\t" "srl %[p3], %[p3], 8 \n\t" "srl %[p2], %[p2], 8 \n\t" "srl %[p1], %[p1], 8 \n\t" : [p4] "+r"(p4), [p3] "+r"(p3), [p2] "+r"(p2), [p1] "+r"(p1) :); __asm__ __volatile__( "sb %[p4], 1(%[s1]) \n\t" "sb %[p3], 0(%[s1]) \n\t" "sb %[p2], -1(%[s1]) \n\t" "sb %[p1], -2(%[s1]) \n\t" : : [p4] "r"(p4), [p3] "r"(p3), [p2] "r"(p2), [p1] "r"(p1), [s1] "r"(s1)); } } } } void aom_lpf_horizontal_4_dual_dspr2( uint8_t *s, int p /* pitch */, const uint8_t *blimit0, const uint8_t *limit0, const uint8_t *thresh0, const uint8_t *blimit1, const uint8_t *limit1, const uint8_t *thresh1) { aom_lpf_horizontal_4_dspr2(s, p, blimit0, limit0, thresh0); aom_lpf_horizontal_4_dspr2(s + 8, p, blimit1, limit1, thresh1); } void aom_lpf_horizontal_8_dual_dspr2( uint8_t *s, int p /* pitch */, const uint8_t *blimit0, const uint8_t *limit0, const uint8_t *thresh0, const uint8_t *blimit1, const uint8_t *limit1, const uint8_t *thresh1) { aom_lpf_horizontal_8_dspr2(s, p, blimit0, limit0, thresh0); aom_lpf_horizontal_8_dspr2(s + 8, p, blimit1, limit1, thresh1); } void aom_lpf_vertical_4_dual_dspr2(uint8_t *s, int p, const uint8_t *blimit0, const uint8_t *limit0, const uint8_t *thresh0, const uint8_t *blimit1, const uint8_t *limit1, const uint8_t *thresh1) { aom_lpf_vertical_4_dspr2(s, p, blimit0, limit0, thresh0); aom_lpf_vertical_4_dspr2(s + 8 * p, p, blimit1, limit1, thresh1); } void aom_lpf_vertical_8_dual_dspr2(uint8_t *s, int p, const uint8_t *blimit0, const uint8_t *limit0, const uint8_t *thresh0, const uint8_t *blimit1, const uint8_t *limit1, const uint8_t *thresh1) { aom_lpf_vertical_8_dspr2(s, p, blimit0, limit0, thresh0); aom_lpf_vertical_8_dspr2(s + 8 * p, p, blimit1, limit1, thresh1); } void aom_lpf_vertical_16_dual_dspr2(uint8_t *s, int p, const uint8_t *blimit, const uint8_t *limit, const uint8_t *thresh) { aom_lpf_vertical_16_dspr2(s, p, blimit, limit, thresh); aom_lpf_vertical_16_dspr2(s + 8 * p, p, blimit, limit, thresh); } #endif // #if HAVE_DSPR2