Merge "remove unused VP8 com/dec asm offsets"
diff --git a/test/test.mk b/test/test.mk
index 52a54fb..c2a3aec 100644
--- a/test/test.mk
+++ b/test/test.mk
@@ -26,6 +26,7 @@
 LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += borders_test.cc
 LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += resize_test.cc
 LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += cpu_speed_test.cc
+LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += vp9_lossless_test.cc
 
 LIBVPX_TEST_SRCS-$(CONFIG_DECODERS)    += ../md5_utils.h ../md5_utils.c
 LIBVPX_TEST_SRCS-yes                   += decode_test_driver.cc
diff --git a/test/vp9_lossless_test.cc b/test/vp9_lossless_test.cc
new file mode 100644
index 0000000..441cc44
--- /dev/null
+++ b/test/vp9_lossless_test.cc
@@ -0,0 +1,75 @@
+/*
+  Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+
+  Use of this source code is governed by a BSD-style license
+  that can be found in the LICENSE file in the root of the source
+  tree. An additional intellectual property rights grant can be found
+  in the file PATENTS.  All contributing project authors may
+  be found in the AUTHORS file in the root of the source tree.
+*/
+
+#include "third_party/googletest/src/include/gtest/gtest.h"
+#include "test/codec_factory.h"
+#include "test/encode_test_driver.h"
+#include "test/i420_video_source.h"
+#include "test/util.h"
+
+namespace {
+
+const int kMaxPsnr = 100;
+
+class LossLessTest : public ::libvpx_test::EncoderTest,
+    public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
+ protected:
+  LossLessTest() : EncoderTest(GET_PARAM(0)),
+                   psnr_(kMaxPsnr),
+                   nframes_(0),
+                   encoding_mode_(GET_PARAM(1)) {
+  }
+
+  virtual ~LossLessTest() {}
+
+  virtual void SetUp() {
+    InitializeConfig();
+    SetMode(encoding_mode_);
+  }
+
+  virtual void BeginPassHook(unsigned int /*pass*/) {
+    psnr_ = 0.0;
+    nframes_ = 0;
+  }
+
+  virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
+    if (pkt->data.psnr.psnr[0] < psnr_)
+      psnr_= pkt->data.psnr.psnr[0];
+  }
+
+  double GetMinPsnr() const {
+      return psnr_;
+  }
+
+ private:
+  double psnr_;
+  unsigned int nframes_;
+  libvpx_test::TestMode encoding_mode_;
+};
+
+TEST_P(LossLessTest, TestLossLessEncoding) {
+  const vpx_rational timebase = { 33333333, 1000000000 };
+  cfg_.g_timebase = timebase;
+  cfg_.rc_target_bitrate = 2000;
+  cfg_.g_lag_in_frames = 25;
+  cfg_.rc_min_quantizer = 0;
+  cfg_.rc_max_quantizer = 0;
+
+  init_flags_ = VPX_CODEC_USE_PSNR;
+
+  // intentionally changed the dimension for better testing coverage
+  libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 356, 284,
+                                     timebase.den, timebase.num, 0, 30);
+
+  const double psnr_lossless = GetMinPsnr();
+  EXPECT_GE(psnr_lossless, kMaxPsnr);
+}
+VP9_INSTANTIATE_TEST_CASE(LossLessTest, ALL_TEST_MODES);
+}  // namespace
diff --git a/vp9/common/arm/neon/vp9_loopfilter_neon.asm b/vp9/common/arm/neon/vp9_loopfilter_neon.asm
index 5011315..02cf2f5 100644
--- a/vp9/common/arm/neon/vp9_loopfilter_neon.asm
+++ b/vp9/common/arm/neon/vp9_loopfilter_neon.asm
@@ -10,6 +10,8 @@
 
     EXPORT  |vp9_loop_filter_horizontal_edge_neon|
     EXPORT  |vp9_loop_filter_vertical_edge_neon|
+    EXPORT  |vp9_mbloop_filter_horizontal_edge_neon|
+    EXPORT  |vp9_mbloop_filter_vertical_edge_neon|
     ARM
 
     AREA ||.text||, CODE, READONLY, ALIGN=2
@@ -33,50 +35,47 @@
 ; sp    const uint8_t *thresh,
 ; sp+4  int count
 |vp9_loop_filter_horizontal_edge_neon| PROC
-    push        {r4-r6, lr}
+    push        {lr}
 
-    ldr         r12, [sp,#20]              ; load count
-    ldrb        r4, [r2]                   ; load *blimit
-    ldrb        r5, [r3]                   ; load *limit
+    ldr         r12, [sp,#8]               ; load count
     cmp         r12, #0
     beq         end_vp9_lf_h_edge
 
-    ldr         r3, [sp, #16]              ; load thresh
-    vdup.u8     d0, r4                     ; duplicate blimit
-    ldrb        r6, [r3]                   ; load *thresh
-    vdup.u8     d1, r5                     ; duplicate limit
-    vdup.u8     d2, r6                     ; duplicate thresh
+    vld1.8      {d0[]}, [r2]               ; duplicate *blimit
+    ldr         r2, [sp, #4]               ; load thresh
+    vld1.8      {d1[]}, [r3]               ; duplicate *limit
+    vld1.8      {d2[]}, [r2]               ; duplicate *thresh
 
 count_lf_h_loop
     sub         r2, r0, r1, lsl #2         ; move src pointer down by 4 lines
-    add         r6, r2, r1
+    add         r3, r2, r1
     add         r1, r1, r1
 
     vld1.u8     {d3}, [r2@64], r1          ; p3
-    vld1.u8     {d4}, [r6@64], r1          ; p2
+    vld1.u8     {d4}, [r3@64], r1          ; p2
     vld1.u8     {d5}, [r2@64], r1          ; p1
-    vld1.u8     {d6}, [r6@64], r1          ; p0
+    vld1.u8     {d6}, [r3@64], r1          ; p0
     vld1.u8     {d7}, [r2@64], r1          ; q0
-    vld1.u8     {d16}, [r6@64], r1         ; q1
+    vld1.u8     {d16}, [r3@64], r1         ; q1
     vld1.u8     {d17}, [r2@64]             ; q2
-    vld1.u8     {d18}, [r6@64]             ; q3
+    vld1.u8     {d18}, [r3@64]             ; q3
 
     sub         r2, r2, r1, lsl #1
-    sub         r6, r6, r1, lsl #1
+    sub         r3, r3, r1, lsl #1
 
     bl          vp9_loop_filter_neon
 
     vst1.u8     {d4}, [r2@64], r1          ; store op1
-    vst1.u8     {d5}, [r6@64], r1          ; store op0
+    vst1.u8     {d5}, [r3@64], r1          ; store op0
     vst1.u8     {d6}, [r2@64], r1          ; store oq0
-    vst1.u8     {d7}, [r6@64], r1          ; store oq1
+    vst1.u8     {d7}, [r3@64], r1          ; store oq1
 
     add         r0, r0, #8
     subs        r12, r12, #1
     bne         count_lf_h_loop
 
 end_vp9_lf_h_edge
-    pop         {r4-r6, pc}
+    pop         {pc}
     ENDP        ; |vp9_loop_filter_horizontal_edge_neon|
 
 ; Currently vp9 only works on iterations 8 at a time. The vp8 loop filter
@@ -98,31 +97,28 @@
 ; sp    const uint8_t *thresh,
 ; sp+4  int count
 |vp9_loop_filter_vertical_edge_neon| PROC
-    push        {r4-r6, lr}
+    push        {lr}
 
-    ldr         r12, [sp,#20]              ; load count
-    ldrb        r4, [r2]                   ; load *blimit
-    ldrb        r5, [r3]                   ; load *limit
+    ldr         r12, [sp,#8]               ; load count
     cmp         r12, #0
     beq         end_vp9_lf_v_edge
 
-    ldr         r3, [sp, #16]              ; load thresh
-    vdup.u8     d0, r4                     ; duplicate blimit
-    ldrb        r6, [r3]                   ; load *thresh
-    vdup.u8     d1, r5                     ; duplicate limit
-    vdup.u8     d2, r6                     ; duplicate thresh
+    vld1.8      {d0[]}, [r2]               ; duplicate *blimit
+    ldr         r2, [sp, #4]               ; load thresh
+    vld1.8      {d1[]}, [r3]               ; duplicate *limit
+    vld1.8      {d2[]}, [r2]               ; duplicate *thresh
 
 count_lf_v_loop
-    sub         r6, r0, #4                 ; move s pointer down by 4 columns
+    sub         r2, r0, #4                 ; move s pointer down by 4 columns
 
-    vld1.u8     {d3}, [r6], r1             ; load s data
-    vld1.u8     {d4}, [r6], r1
-    vld1.u8     {d5}, [r6], r1
-    vld1.u8     {d6}, [r6], r1
-    vld1.u8     {d7}, [r6], r1
-    vld1.u8     {d16}, [r6], r1
-    vld1.u8     {d17}, [r6], r1
-    vld1.u8     {d18}, [r6]
+    vld1.u8     {d3}, [r2], r1             ; load s data
+    vld1.u8     {d4}, [r2], r1
+    vld1.u8     {d5}, [r2], r1
+    vld1.u8     {d6}, [r2], r1
+    vld1.u8     {d7}, [r2], r1
+    vld1.u8     {d16}, [r2], r1
+    vld1.u8     {d17}, [r2], r1
+    vld1.u8     {d18}, [r2]
 
     ;transpose to 8x16 matrix
     vtrn.32     d3, d7
@@ -159,7 +155,7 @@
     bne         count_lf_v_loop
 
 end_vp9_lf_v_edge
-    pop         {r4-r6, pc}
+    pop         {pc}
     ENDP        ; |vp9_loop_filter_vertical_edge_neon|
 
 ; void vp9_loop_filter_neon();
@@ -224,7 +220,7 @@
 
     vsub.s8     d28, d7, d6                 ; ( qs0 - ps0)
 
-    vcge.u8     d17, d0, d17                ; (a > blimit * 2 + limit) * -1
+    vcge.u8     d17, d0, d17                ; a > blimit
 
     vqsub.s8    d27, d5, d16                ; filter = clamp(ps1-qs1)
     vorr        d22, d21, d22               ; hevmask
@@ -267,4 +263,331 @@
     bx          lr
     ENDP        ; |vp9_loop_filter_neon|
 
+; void vp9_mbloop_filter_horizontal_edge_neon(uint8_t *s, int p,
+;                                             const uint8_t *blimit,
+;                                             const uint8_t *limit,
+;                                             const uint8_t *thresh,
+;                                             int count)
+; r0    uint8_t *s,
+; r1    int p, /* pitch */
+; r2    const uint8_t *blimit,
+; r3    const uint8_t *limit,
+; sp    const uint8_t *thresh,
+; sp+4  int count
+|vp9_mbloop_filter_horizontal_edge_neon| PROC
+    push        {lr}
+
+    ldr         r12, [sp,#8]               ; load count
+    cmp         r12, #0
+    beq         end_vp9_mblf_h_edge
+
+    vld1.8      {d0[]}, [r2]               ; duplicate *blimit
+    ldr         r2, [sp, #4]               ; load thresh
+    vld1.8      {d1[]}, [r3]               ; duplicate *limit
+    vld1.8      {d2[]}, [r2]               ; duplicate *thresh
+
+count_mblf_h_loop
+    sub         r3, r0, r1, lsl #2         ; move src pointer down by 4 lines
+    add         r2, r3, r1
+    add         r1, r1, r1
+
+    vld1.u8     {d3}, [r3@64], r1          ; p3
+    vld1.u8     {d4}, [r2@64], r1          ; p2
+    vld1.u8     {d5}, [r3@64], r1          ; p1
+    vld1.u8     {d6}, [r2@64], r1          ; p0
+    vld1.u8     {d7}, [r3@64], r1          ; q0
+    vld1.u8     {d16}, [r2@64], r1         ; q1
+    vld1.u8     {d17}, [r3@64]             ; q2
+    vld1.u8     {d18}, [r2@64], r1         ; q3
+
+    sub         r3, r3, r1, lsl #1
+    sub         r2, r2, r1, lsl #2
+
+    bl          vp9_mbloop_filter_neon
+
+    vst1.u8     {d2}, [r2@64], r1          ; store op2
+    vst1.u8     {d3}, [r3@64], r1          ; store op1
+    vst1.u8     {d4}, [r2@64], r1          ; store op0
+    vst1.u8     {d5}, [r3@64], r1          ; store oq0
+    vst1.u8     {d6}, [r2@64], r1          ; store oq1
+    vst1.u8     {d7}, [r3@64], r1          ; store oq2
+
+    add         r0, r0, #8
+    subs        r12, r12, #1
+    bne         count_mblf_h_loop
+
+end_vp9_mblf_h_edge
+    pop         {pc}
+
+    ENDP        ; |vp9_mbloop_filter_horizontal_edge_neon|
+
+; void vp9_mbloop_filter_vertical_edge_neon(uint8_t *s,
+;                                           int pitch,
+;                                           const uint8_t *blimit,
+;                                           const uint8_t *limit,
+;                                           const uint8_t *thresh,
+;                                           int count)
+;
+; r0    uint8_t *s,
+; r1    int pitch,
+; r2    const uint8_t *blimit,
+; r3    const uint8_t *limit,
+; sp    const uint8_t *thresh,
+; sp+4  int count
+|vp9_mbloop_filter_vertical_edge_neon| PROC
+    push        {lr}
+
+    ldr         r12, [sp,#8]               ; load count
+    cmp         r12, #0
+    beq         end_vp9_mblf_v_edge
+
+    vld1.8      {d0[]}, [r2]               ; duplicate *blimit
+    ldr         r2, [sp, #4]               ; load thresh
+    vld1.8      {d1[]}, [r3]               ; duplicate *limit
+    vld1.8      {d2[]}, [r2]               ; duplicate *thresh
+
+count_mblf_v_loop
+    sub         r2, r0, #4                 ; move s pointer down by 4 columns
+
+    vld1.u8     {d3}, [r2], r1             ; load s data
+    vld1.u8     {d4}, [r2], r1
+    vld1.u8     {d5}, [r2], r1
+    vld1.u8     {d6}, [r2], r1
+    vld1.u8     {d7}, [r2], r1
+    vld1.u8     {d16}, [r2], r1
+    vld1.u8     {d17}, [r2], r1
+    vld1.u8     {d18}, [r2]
+
+    ;transpose to 8x16 matrix
+    vtrn.32     d3, d7
+    vtrn.32     d4, d16
+    vtrn.32     d5, d17
+    vtrn.32     d6, d18
+
+    vtrn.16     d3, d5
+    vtrn.16     d4, d6
+    vtrn.16     d7, d17
+    vtrn.16     d16, d18
+
+    vtrn.8      d3, d4
+    vtrn.8      d5, d6
+    vtrn.8      d7, d16
+    vtrn.8      d17, d18
+
+    sub         r2, r0, #3
+    add         r3, r0, #1
+
+    bl          vp9_mbloop_filter_neon
+
+    ;store op2, op1, op0, oq0
+    vst4.8      {d2[0], d3[0], d4[0], d5[0]}, [r2], r1
+    vst4.8      {d2[1], d3[1], d4[1], d5[1]}, [r2], r1
+    vst4.8      {d2[2], d3[2], d4[2], d5[2]}, [r2], r1
+    vst4.8      {d2[3], d3[3], d4[3], d5[3]}, [r2], r1
+    vst4.8      {d2[4], d3[4], d4[4], d5[4]}, [r2], r1
+    vst4.8      {d2[5], d3[5], d4[5], d5[5]}, [r2], r1
+    vst4.8      {d2[6], d3[6], d4[6], d5[6]}, [r2], r1
+    vst4.8      {d2[7], d3[7], d4[7], d5[7]}, [r2]
+
+    ;store oq1, oq2
+    vst2.8      {d6[0], d7[0]}, [r3], r1
+    vst2.8      {d6[1], d7[1]}, [r3], r1
+    vst2.8      {d6[2], d7[2]}, [r3], r1
+    vst2.8      {d6[3], d7[3]}, [r3], r1
+    vst2.8      {d6[4], d7[4]}, [r3], r1
+    vst2.8      {d6[5], d7[5]}, [r3], r1
+    vst2.8      {d6[6], d7[6]}, [r3], r1
+    vst2.8      {d6[7], d7[7]}, [r3]
+
+    add         r0, r0, r1, lsl #3         ; s += pitch * 8
+    subs        r12, r12, #1
+    bne         count_mblf_v_loop
+
+end_vp9_mblf_v_edge
+    pop         {pc}
+    ENDP        ; |vp9_mbloop_filter_vertical_edge_neon|
+
+; void vp9_mbloop_filter_neon();
+; This is a helper function for the loopfilters. The invidual functions do the
+; necessary load, transpose (if necessary) and store. The function does not use
+; registers d8-d15.
+;
+; r0-r3 PRESERVE
+; d0    blimit
+; d1    limit
+; d2    thresh
+; d3    p3
+; d4    p2
+; d5    p1
+; d6    p0
+; d7    q0
+; d16   q1
+; d17   q2
+; d18   q3
+|vp9_mbloop_filter_neon| PROC
+    ; filter_mask
+    vabd.u8     d19, d3, d4                ; abs(p3 - p2)
+    vabd.u8     d20, d4, d5                ; abs(p2 - p1)
+    vabd.u8     d21, d5, d6                ; abs(p1 - p0)
+    vabd.u8     d22, d16, d7               ; abs(q1 - q0)
+    vabd.u8     d23, d17, d16              ; abs(q2 - q1)
+    vabd.u8     d24, d18, d17              ; abs(q3 - q2)
+
+    ; only compare the largest value to limit
+    vmax.u8     d19, d19, d20              ; max(abs(p3 - p2), abs(p2 - p1))
+    vmax.u8     d20, d21, d22              ; max(abs(p1 - p0), abs(q1 - q0))
+    vmax.u8     d23, d23, d24              ; max(abs(q2 - q1), abs(q3 - q2))
+    vmax.u8     d19, d19, d20
+
+    vabd.u8     d24, d6, d7                ; abs(p0 - q0)
+
+    vmax.u8     d19, d19, d23
+
+    vabd.u8     d23, d5, d16               ; a = abs(p1 - q1)
+    vqadd.u8    d24, d24, d24              ; b = abs(p0 - q0) * 2
+
+    ; abs () > limit
+    vcge.u8     d19, d1, d19
+
+    ; flatmask4
+    vabd.u8     d25, d6, d4                ; abs(p0 - p2)
+    vabd.u8     d26, d7, d17               ; abs(q0 - q2)
+    vabd.u8     d27, d3, d6                ; abs(p3 - p0)
+    vabd.u8     d28, d18, d7               ; abs(q3 - q0)
+
+    ; only compare the largest value to thresh
+    vmax.u8     d25, d25, d26              ; max(abs(p0 - p2), abs(q0 - q2))
+    vmax.u8     d26, d27, d28              ; max(abs(p3 - p0), abs(q3 - q0))
+    vmax.u8     d25, d25, d26
+    vmax.u8     d20, d20, d25
+
+    vshr.u8     d23, d23, #1               ; a = a / 2
+    vqadd.u8    d24, d24, d23              ; a = b + a
+
+    vmov.u8     d23, #1
+    vcge.u8     d24, d0, d24               ; a > blimit
+
+    vcge.u8     d20, d23, d20              ; flat
+
+    vand        d19, d19, d24              ; mask
+
+    ; hevmask
+    vcgt.u8     d21, d21, d2               ; (abs(p1 - p0) > thresh)*-1
+    vcgt.u8     d22, d22, d2               ; (abs(q1 - q0) > thresh)*-1
+    vorr        d21, d21, d22              ; hev
+
+    vmov.u8     d22, #0x80
+
+    ; mbfilter() function
+
+    ; filter() function
+    ; convert to signed
+    veor        d23, d7, d22               ; qs0
+    veor        d24, d6, d22               ; ps0
+    veor        d25, d5, d22               ; ps1
+    veor        d26, d16, d22              ; qs1
+
+    vmov.u8     d27, #3
+
+    vsub.s8     d28, d23, d24              ; ( qs0 - ps0)
+
+    vqsub.s8    d29, d25, d26              ; filter = clamp(ps1-qs1)
+
+    vmull.s8    q15, d28, d27              ; 3 * ( qs0 - ps0)
+
+    vand        d29, d29, d21              ; filter &= hev
+
+    vaddw.s8    q15, q15, d29              ; filter + 3 * (qs0 - ps0)
+
+    vmov.u8     d29, #4
+
+    ; filter = clamp(filter + 3 * ( qs0 - ps0))
+    vqmovn.s16  d28, q15
+
+    vand        d28, d28, d19              ; filter &= mask
+
+    vqadd.s8    d30, d28, d27              ; filter2 = clamp(filter+3)
+    vqadd.s8    d29, d28, d29              ; filter1 = clamp(filter+4)
+    vshr.s8     d30, d30, #3               ; filter2 >>= 3
+    vshr.s8     d29, d29, #3               ; filter1 >>= 3
+
+    vand        d20, d20, d19              ; flat & mask
+
+    vqadd.s8    d24, d24, d30              ; op0 = clamp(ps0 + filter2)
+    vqsub.s8    d23, d23, d29              ; oq0 = clamp(qs0 - filter1)
+
+    ; outer tap adjustments: ++filter1 >> 1
+    vrshr.s8    d29, d29, #1
+    vbic        d29, d29, d21              ; filter &= ~hev
+
+    vqadd.s8    d25, d25, d29              ; op1 = clamp(ps1 + filter)
+    vqsub.s8    d26, d26, d29              ; oq1 = clamp(qs1 - filter)
+
+    veor        d24, d24, d22              ; *f_op0 = u^0x80
+    veor        d23, d23, d22              ; *f_oq0 = u^0x80
+    veor        d25, d25, d22              ; *f_op1 = u^0x80
+    veor        d26, d26, d22              ; *f_oq1 = u^0x80
+
+    ; mbfilter flat && mask branch
+    ; TODO(fgalligan): Can I decrease the cycles shifting to consective d's
+    ; and using vibt on the q's?
+    vmov.u8     d21, #2
+    vaddl.u8    q14, d6, d7                ; op2 = p0 + q0
+    vmlal.u8    q14, d3, d27               ; op2 += p3 * 3
+    vmlal.u8    q14, d4, d21               ; op2 += p2 * 2
+    vaddw.u8    q14, d5                    ; op2 += p1
+    vqrshrn.u16 d30, q14, #3               ; r_op2
+
+    vsubw.u8    q14, d3                    ; op1 = op2 - p3
+    vsubw.u8    q14, d4                    ; op1 -= p2
+    vaddw.u8    q14, d5                    ; op1 += p1
+    vaddw.u8    q14, d16                   ; op1 += q1
+    vqrshrn.u16 d31, q14, #3               ; r_op1
+
+    vsubw.u8    q14, d3                    ; op0 = op1 - p3
+    vsubw.u8    q14, d5                    ; op0 -= p1
+    vaddw.u8    q14, d6                    ; op0 += p0
+    vaddw.u8    q14, d17                   ; op0 += q2
+    vqrshrn.u16 d21, q14, #3               ; r_op0
+
+    vsubw.u8    q14, d3                    ; oq0 = op0 - p3
+    vsubw.u8    q14, d6                    ; oq0 -= p0
+    vaddw.u8    q14, d7                    ; oq0 += q0
+    vaddw.u8    q14, d18                   ; oq0 += q3
+    vqrshrn.u16 d22, q14, #3               ; r_oq0
+
+    vsubw.u8    q14, d4                    ; oq1 = oq0 - p2
+    vsubw.u8    q14, d7                    ; oq1 -= q0
+    vaddw.u8    q14, d16                   ; oq1 += q1
+    vaddw.u8    q14, d18                   ; oq1 += q3
+    vqrshrn.u16 d0, q14, #3                ; r_oq1
+
+    vsubw.u8    q14, d5                    ; oq2 = oq0 - p1
+    vsubw.u8    q14, d16                   ; oq2 -= q1
+    vaddw.u8    q14, d17                   ; oq2 += q2
+    vaddw.u8    q14, d18                   ; oq2 += q3
+    vqrshrn.u16 d1, q14, #3                ; r_oq2
+
+    ; Filter does not set op2 or oq2, so use p2 and q2.
+    vbit        d2, d30, d20               ; op2 |= r_op2 & (flat & mask)
+    vbif        d2, d4, d20                ; op2 |= op2 & ~(flat & mask)
+
+    vbit        d3, d31, d20               ; op1 |= r_op1 & (flat & mask)
+    vbif        d3, d25, d20               ; op1 |= f_op1 & ~(flat & mask)
+
+    vbit        d4, d21, d20               ; op0 |= r_op0 & (flat & mask)
+    vbif        d4, d24, d20               ; op0 |= f_op0 & ~(flat & mask)
+
+    vbit        d5, d22, d20               ; oq0 |= r_oq0 & (flat & mask)
+    vbif        d5, d23, d20               ; oq0 |= f_oq0 & ~(flat & mask)
+
+    vbit        d6, d0, d20                ; oq1 |= r_oq1 & (flat & mask)
+    vbif        d6, d26, d20               ; oq1 |= f_oq1 & ~(flat & mask)
+
+    vbit        d7, d1, d20                ; oq2 |= r_oq2 & (flat & mask)
+    vbif        d7, d17, d20               ; oq2 |= oq2 & ~(flat & mask)
+
+    bx          lr
+    ENDP        ; |vp9_mbloop_filter_neon|
+
     END
diff --git a/vp9/common/vp9_loopfilter.c b/vp9/common/vp9_loopfilter.c
index ef8ed2b..9d2e2ea 100644
--- a/vp9/common/vp9_loopfilter.c
+++ b/vp9/common/vp9_loopfilter.c
@@ -35,16 +35,12 @@
 
 void vp9_loop_filter_update_sharpness(loop_filter_info_n *lfi,
                                       int sharpness_lvl) {
-  int i;
+  int lvl;
 
-  /* For each possible value for the loop filter fill out limits */
-  for (i = 0; i <= MAX_LOOP_FILTER; i++) {
-    int filt_lvl = i;
-    int block_inside_limit = 0;
-
-    /* Set loop filter paramaeters that control sharpness. */
-    block_inside_limit = filt_lvl >> (sharpness_lvl > 0);
-    block_inside_limit = block_inside_limit >> (sharpness_lvl > 4);
+  // For each possible value for the loop filter fill out limits
+  for (lvl = 0; lvl <= MAX_LOOP_FILTER; lvl++) {
+    // Set loop filter paramaeters that control sharpness.
+    int block_inside_limit = lvl >> ((sharpness_lvl > 0) + (sharpness_lvl > 4));
 
     if (sharpness_lvl > 0) {
       if (block_inside_limit > (9 - sharpness_lvl))
@@ -54,10 +50,9 @@
     if (block_inside_limit < 1)
       block_inside_limit = 1;
 
-    vpx_memset(lfi->lim[i], block_inside_limit, SIMD_WIDTH);
-    vpx_memset(lfi->blim[i], (2 * filt_lvl + block_inside_limit),
-               SIMD_WIDTH);
-    vpx_memset(lfi->mblim[i], (2 * (filt_lvl + 2) + block_inside_limit),
+    vpx_memset(lfi->lim[lvl], block_inside_limit, SIMD_WIDTH);
+    vpx_memset(lfi->blim[lvl], (2 * lvl + block_inside_limit), SIMD_WIDTH);
+    vpx_memset(lfi->mblim[lvl], (2 * (lvl + 2) + block_inside_limit),
                SIMD_WIDTH);
   }
 }
@@ -78,98 +73,68 @@
     vpx_memset(lfi->hev_thr[i], i, SIMD_WIDTH);
 }
 
-void vp9_loop_filter_frame_init(VP9_COMMON *cm,
-                                MACROBLOCKD *xd,
+void vp9_loop_filter_frame_init(VP9_COMMON *cm, MACROBLOCKD *xd,
                                 int default_filt_lvl) {
-  int seg,    // segment number
-      ref,    // index in ref_lf_deltas
-      mode;   // index in mode_lf_deltas
+  int seg;
   // n_shift is the a multiplier for lf_deltas
   // the multiplier is 1 for when filter_lvl is between 0 and 31;
   // 2 when filter_lvl is between 32 and 63
-  int n_shift = default_filt_lvl >> 5;
+  const int n_shift = default_filt_lvl >> 5;
+  loop_filter_info_n *const lfi = &cm->lf_info;
 
-  loop_filter_info_n *lfi = &cm->lf_info;
-
-  /* update limits if sharpness has changed */
-  // printf("vp9_loop_filter_frame_init %d\n", default_filt_lvl);
-  // printf("sharpness level: %d [%d]\n",
-  //        cm->sharpness_level, cm->last_sharpness_level);
+  // update limits if sharpness has changed
   if (cm->last_sharpness_level != cm->sharpness_level) {
     vp9_loop_filter_update_sharpness(lfi, cm->sharpness_level);
     cm->last_sharpness_level = cm->sharpness_level;
   }
 
   for (seg = 0; seg < MAX_MB_SEGMENTS; seg++) {
-    int lvl_seg = default_filt_lvl;
-    int lvl_ref, lvl_mode;
-
+    int lvl_seg = default_filt_lvl, ref, mode, intra_lvl;
 
     // Set the baseline filter values for each segment
     if (vp9_segfeature_active(xd, seg, SEG_LVL_ALT_LF)) {
-      /* Abs value */
-      if (xd->mb_segment_abs_delta == SEGMENT_ABSDATA) {
-        lvl_seg = vp9_get_segdata(xd, seg, SEG_LVL_ALT_LF);
-      } else { /* Delta Value */
-        lvl_seg += vp9_get_segdata(xd, seg, SEG_LVL_ALT_LF);
-        lvl_seg = clamp(lvl_seg, 0, 63);
-      }
+      const int data = vp9_get_segdata(xd, seg, SEG_LVL_ALT_LF);
+      lvl_seg = xd->mb_segment_abs_delta == SEGMENT_ABSDATA
+                  ? data
+                  : clamp(default_filt_lvl + data, 0, MAX_LOOP_FILTER);
     }
 
     if (!xd->mode_ref_lf_delta_enabled) {
-      /* we could get rid of this if we assume that deltas are set to
-       * zero when not in use; encoder always uses deltas
-       */
+      // we could get rid of this if we assume that deltas are set to
+      // zero when not in use; encoder always uses deltas
       vpx_memset(lfi->lvl[seg][0], lvl_seg, 4 * 4);
       continue;
     }
 
-    lvl_ref = lvl_seg;
+    intra_lvl = lvl_seg + (xd->ref_lf_deltas[INTRA_FRAME] << n_shift);
+    lfi->lvl[seg][INTRA_FRAME][0] = clamp(intra_lvl, 0, MAX_LOOP_FILTER);
 
-    /* INTRA_FRAME */
-    ref = INTRA_FRAME;
-
-    /* Apply delta for reference frame */
-    lvl_ref += xd->ref_lf_deltas[ref] << n_shift;
-
-    mode = 0; /* all the rest of Intra modes */
-    lvl_mode = lvl_ref;
-    lfi->lvl[seg][ref][mode] = clamp(lvl_mode, 0, 63);
-
-    /* LAST, GOLDEN, ALT */
-    for (ref = 1; ref < MAX_REF_FRAMES; ref++) {
-      int lvl_ref = lvl_seg;
-
-      /* Apply delta for reference frame */
-      lvl_ref += xd->ref_lf_deltas[ref] << n_shift;
-
-      /* Apply delta for Inter modes */
-      for (mode = 0; mode < MAX_MODE_LF_DELTAS; mode++) {
-        lvl_mode = lvl_ref + (xd->mode_lf_deltas[mode] << n_shift);
-        lfi->lvl[seg][ref][mode] = clamp(lvl_mode, 0, 63);
+    for (ref = LAST_FRAME; ref < MAX_REF_FRAMES; ++ref)
+      for (mode = 0; mode < MAX_MODE_LF_DELTAS; ++mode) {
+        const int inter_lvl = lvl_seg + (xd->ref_lf_deltas[ref] << n_shift)
+                                      + (xd->mode_lf_deltas[mode] << n_shift);
+        lfi->lvl[seg][ref][mode] = clamp(inter_lvl, 0, MAX_LOOP_FILTER);
       }
-    }
   }
 }
 
 static int build_lfi(const VP9_COMMON *cm, const MB_MODE_INFO *mbmi,
                       struct loop_filter_info *lfi) {
-  const loop_filter_info_n *lfi_n = &cm->lf_info;
-  int mode = mbmi->mode;
-  int mode_index = lfi_n->mode_lf_lut[mode];
-  int seg = mbmi->segment_id;
-  int ref_frame = mbmi->ref_frame[0];
-  int filter_level = lfi_n->lvl[seg][ref_frame][mode_index];
+  const loop_filter_info_n *const lfi_n = &cm->lf_info;
+  const int seg = mbmi->segment_id;
+  const int ref = mbmi->ref_frame[0];
+  const int mode = lfi_n->mode_lf_lut[mbmi->mode];
+  const int filter_level = lfi_n->lvl[seg][ref][mode];
 
-  if (filter_level) {
-    const int hev_index = filter_level >> 4;
+  if (filter_level > 0) {
     lfi->mblim = lfi_n->mblim[filter_level];
     lfi->blim = lfi_n->blim[filter_level];
     lfi->lim = lfi_n->lim[filter_level];
-    lfi->hev_thr = lfi_n->hev_thr[hev_index];
+    lfi->hev_thr = lfi_n->hev_thr[filter_level >> 4];
     return 1;
+  } else {
+    return 0;
   }
-  return 0;
 }
 
 static void filter_selectively_vert(uint8_t *s, int pitch,
diff --git a/vp9/common/vp9_loopfilter.h b/vp9/common/vp9_loopfilter.h
index ce954c0..2582979 100644
--- a/vp9/common/vp9_loopfilter.h
+++ b/vp9/common/vp9_loopfilter.h
@@ -16,34 +16,35 @@
 #include "vp9/common/vp9_blockd.h"
 
 #define MAX_LOOP_FILTER 63
+#define MAX_SHARPNESS 7
+
 #define SIMD_WIDTH 16
 
-/* Need to align this structure so when it is declared and
- * passed it can be loaded into vector registers.
- */
+// Need to align this structure so when it is declared and
+// passed it can be loaded into vector registers.
 typedef struct {
-  DECLARE_ALIGNED(SIMD_WIDTH, unsigned char,
+  DECLARE_ALIGNED(SIMD_WIDTH, uint8_t,
                   mblim[MAX_LOOP_FILTER + 1][SIMD_WIDTH]);
-  DECLARE_ALIGNED(SIMD_WIDTH, unsigned char,
+  DECLARE_ALIGNED(SIMD_WIDTH, uint8_t,
                   blim[MAX_LOOP_FILTER + 1][SIMD_WIDTH]);
-  DECLARE_ALIGNED(SIMD_WIDTH, unsigned char,
+  DECLARE_ALIGNED(SIMD_WIDTH, uint8_t,
                   lim[MAX_LOOP_FILTER + 1][SIMD_WIDTH]);
-  DECLARE_ALIGNED(SIMD_WIDTH, unsigned char,
+  DECLARE_ALIGNED(SIMD_WIDTH, uint8_t,
                   hev_thr[4][SIMD_WIDTH]);
-  unsigned char lvl[MAX_MB_SEGMENTS][4][4];
-  unsigned char mode_lf_lut[MB_MODE_COUNT];
+  uint8_t lvl[MAX_MB_SEGMENTS][MAX_REF_FRAMES][MAX_MODE_LF_DELTAS];
+  uint8_t mode_lf_lut[MB_MODE_COUNT];
 } loop_filter_info_n;
 
 struct loop_filter_info {
-  const unsigned char *mblim;
-  const unsigned char *blim;
-  const unsigned char *lim;
-  const unsigned char *hev_thr;
+  const uint8_t *mblim;
+  const uint8_t *blim;
+  const uint8_t *lim;
+  const uint8_t *hev_thr;
 };
 
 #define prototype_loopfilter(sym) \
-  void sym(uint8_t *src, int pitch, const unsigned char *blimit, \
-           const unsigned char *limit, const unsigned char *thresh, int count)
+  void sym(uint8_t *src, int pitch, const uint8_t *blimit, \
+           const uint8_t *limit, const uint8_t *thresh, int count)
 
 #define prototype_loopfilter_block(sym) \
   void sym(uint8_t *y, uint8_t *u, uint8_t *v, \
@@ -53,11 +54,10 @@
 #include "x86/vp9_loopfilter_x86.h"
 #endif
 
-typedef void loop_filter_uvfunction(uint8_t *u,   /* source pointer */
-                                    int p,              /* pitch */
-                                    const unsigned char *blimit,
-                                    const unsigned char *limit,
-                                    const unsigned char *thresh,
+typedef void loop_filter_uvfunction(uint8_t *src, int pitch,
+                                    const uint8_t *blimit,
+                                    const uint8_t *limit,
+                                    const uint8_t *thresh,
                                     uint8_t *v);
 
 /* assorted loopfilter functions which get used elsewhere */
diff --git a/vp9/common/vp9_rtcd_defs.sh b/vp9/common/vp9_rtcd_defs.sh
index 7e2cc6d..c6167d2 100644
--- a/vp9/common/vp9_rtcd_defs.sh
+++ b/vp9/common/vp9_rtcd_defs.sh
@@ -109,7 +109,7 @@
 specialize vp9_mb_lpf_vertical_edge_w sse2
 
 prototype void vp9_mbloop_filter_vertical_edge "uint8_t *s, int pitch, const uint8_t *blimit, const uint8_t *limit, const uint8_t *thresh, int count"
-specialize vp9_mbloop_filter_vertical_edge sse2
+specialize vp9_mbloop_filter_vertical_edge sse2 neon
 
 prototype void vp9_loop_filter_vertical_edge "uint8_t *s, int pitch, const uint8_t *blimit, const uint8_t *limit, const uint8_t *thresh, int count"
 specialize vp9_loop_filter_vertical_edge mmx neon
@@ -118,7 +118,7 @@
 specialize vp9_mb_lpf_horizontal_edge_w sse2
 
 prototype void vp9_mbloop_filter_horizontal_edge "uint8_t *s, int pitch, const uint8_t *blimit, const uint8_t *limit, const uint8_t *thresh, int count"
-specialize vp9_mbloop_filter_horizontal_edge sse2
+specialize vp9_mbloop_filter_horizontal_edge sse2 neon
 
 prototype void vp9_loop_filter_horizontal_edge "uint8_t *s, int pitch, const uint8_t *blimit, const uint8_t *limit, const uint8_t *thresh, int count"
 specialize vp9_loop_filter_horizontal_edge mmx neon
diff --git a/vp9/common/vp9_seg_common.c b/vp9/common/vp9_seg_common.c
index df7747c..9bf2072 100644
--- a/vp9/common/vp9_seg_common.c
+++ b/vp9/common/vp9_seg_common.c
@@ -13,7 +13,9 @@
 #include "vp9/common/vp9_seg_common.h"
 
 static const int seg_feature_data_signed[SEG_LVL_MAX] = { 1, 1, 0, 0 };
-static const int seg_feature_data_max[SEG_LVL_MAX] = { MAXQ, 63, 3, 0 };
+
+static const int seg_feature_data_max[SEG_LVL_MAX] = {
+  MAXQ, MAX_LOOP_FILTER, 3, 0 };
 
 // These functions provide access to new segment level features.
 // Eventually these function may be "optimized out" but for the moment,