/*
 * 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 "config/aom_dsp_rtcd.h"

#include "aom_dsp/mips/macros_msa.h"

#define CALC_MSE_B(src, ref, var)                                   \
  {                                                                 \
    v16u8 src_l0_m, src_l1_m;                                       \
    v8i16 res_l0_m, res_l1_m;                                       \
                                                                    \
    ILVRL_B2_UB(src, ref, src_l0_m, src_l1_m);                      \
    HSUB_UB2_SH(src_l0_m, src_l1_m, res_l0_m, res_l1_m);            \
    DPADD_SH2_SW(res_l0_m, res_l1_m, res_l0_m, res_l1_m, var, var); \
  }

#define CALC_MSE_AVG_B(src, ref, var, sub)                          \
  {                                                                 \
    v16u8 src_l0_m, src_l1_m;                                       \
    v8i16 res_l0_m, res_l1_m;                                       \
                                                                    \
    ILVRL_B2_UB(src, ref, src_l0_m, src_l1_m);                      \
    HSUB_UB2_SH(src_l0_m, src_l1_m, res_l0_m, res_l1_m);            \
    DPADD_SH2_SW(res_l0_m, res_l1_m, res_l0_m, res_l1_m, var, var); \
                                                                    \
    sub += res_l0_m + res_l1_m;                                     \
  }

#define VARIANCE_WxH(sse, diff, shift) sse - (((uint32_t)diff * diff) >> shift)

#define VARIANCE_LARGE_WxH(sse, diff, shift) \
  sse - (((int64_t)diff * diff) >> shift)

static uint32_t sse_diff_4width_msa(const uint8_t *src_ptr, int32_t src_stride,
                                    const uint8_t *ref_ptr, int32_t ref_stride,
                                    int32_t height, int32_t *diff) {
  uint32_t src0, src1, src2, src3;
  uint32_t ref0, ref1, ref2, ref3;
  int32_t ht_cnt;
  v16u8 src = { 0 };
  v16u8 ref = { 0 };
  v8i16 avg = { 0 };
  v4i32 vec, var = { 0 };

  for (ht_cnt = (height >> 2); ht_cnt--;) {
    LW4(src_ptr, src_stride, src0, src1, src2, src3);
    src_ptr += (4 * src_stride);
    LW4(ref_ptr, ref_stride, ref0, ref1, ref2, ref3);
    ref_ptr += (4 * ref_stride);

    INSERT_W4_UB(src0, src1, src2, src3, src);
    INSERT_W4_UB(ref0, ref1, ref2, ref3, ref);
    CALC_MSE_AVG_B(src, ref, var, avg);
  }

  vec = __msa_hadd_s_w(avg, avg);
  *diff = HADD_SW_S32(vec);

  return HADD_SW_S32(var);
}

static uint32_t sse_diff_8width_msa(const uint8_t *src_ptr, int32_t src_stride,
                                    const uint8_t *ref_ptr, int32_t ref_stride,
                                    int32_t height, int32_t *diff) {
  int32_t ht_cnt;
  v16u8 src0, src1, src2, src3;
  v16u8 ref0, ref1, ref2, ref3;
  v8i16 avg = { 0 };
  v4i32 vec, var = { 0 };

  for (ht_cnt = (height >> 2); ht_cnt--;) {
    LD_UB4(src_ptr, src_stride, src0, src1, src2, src3);
    src_ptr += (4 * src_stride);
    LD_UB4(ref_ptr, ref_stride, ref0, ref1, ref2, ref3);
    ref_ptr += (4 * ref_stride);

    PCKEV_D4_UB(src1, src0, src3, src2, ref1, ref0, ref3, ref2, src0, src1,
                ref0, ref1);
    CALC_MSE_AVG_B(src0, ref0, var, avg);
    CALC_MSE_AVG_B(src1, ref1, var, avg);
  }

  vec = __msa_hadd_s_w(avg, avg);
  *diff = HADD_SW_S32(vec);

  return HADD_SW_S32(var);
}

static uint32_t sse_diff_16width_msa(const uint8_t *src_ptr, int32_t src_stride,
                                     const uint8_t *ref_ptr, int32_t ref_stride,
                                     int32_t height, int32_t *diff) {
  int32_t ht_cnt;
  v16u8 src, ref;
  v8i16 avg = { 0 };
  v4i32 vec, var = { 0 };

  for (ht_cnt = (height >> 2); ht_cnt--;) {
    src = LD_UB(src_ptr);
    src_ptr += src_stride;
    ref = LD_UB(ref_ptr);
    ref_ptr += ref_stride;
    CALC_MSE_AVG_B(src, ref, var, avg);

    src = LD_UB(src_ptr);
    src_ptr += src_stride;
    ref = LD_UB(ref_ptr);
    ref_ptr += ref_stride;
    CALC_MSE_AVG_B(src, ref, var, avg);

    src = LD_UB(src_ptr);
    src_ptr += src_stride;
    ref = LD_UB(ref_ptr);
    ref_ptr += ref_stride;
    CALC_MSE_AVG_B(src, ref, var, avg);

    src = LD_UB(src_ptr);
    src_ptr += src_stride;
    ref = LD_UB(ref_ptr);
    ref_ptr += ref_stride;
    CALC_MSE_AVG_B(src, ref, var, avg);
  }

  vec = __msa_hadd_s_w(avg, avg);
  *diff = HADD_SW_S32(vec);

  return HADD_SW_S32(var);
}

static uint32_t sse_diff_32width_msa(const uint8_t *src_ptr, int32_t src_stride,
                                     const uint8_t *ref_ptr, int32_t ref_stride,
                                     int32_t height, int32_t *diff) {
  int32_t ht_cnt;
  v16u8 src0, src1, ref0, ref1;
  v8i16 avg = { 0 };
  v4i32 vec, var = { 0 };

  for (ht_cnt = (height >> 2); ht_cnt--;) {
    LD_UB2(src_ptr, 16, src0, src1);
    src_ptr += src_stride;
    LD_UB2(ref_ptr, 16, ref0, ref1);
    ref_ptr += ref_stride;
    CALC_MSE_AVG_B(src0, ref0, var, avg);
    CALC_MSE_AVG_B(src1, ref1, var, avg);

    LD_UB2(src_ptr, 16, src0, src1);
    src_ptr += src_stride;
    LD_UB2(ref_ptr, 16, ref0, ref1);
    ref_ptr += ref_stride;
    CALC_MSE_AVG_B(src0, ref0, var, avg);
    CALC_MSE_AVG_B(src1, ref1, var, avg);

    LD_UB2(src_ptr, 16, src0, src1);
    src_ptr += src_stride;
    LD_UB2(ref_ptr, 16, ref0, ref1);
    ref_ptr += ref_stride;
    CALC_MSE_AVG_B(src0, ref0, var, avg);
    CALC_MSE_AVG_B(src1, ref1, var, avg);

    LD_UB2(src_ptr, 16, src0, src1);
    src_ptr += src_stride;
    LD_UB2(ref_ptr, 16, ref0, ref1);
    ref_ptr += ref_stride;
    CALC_MSE_AVG_B(src0, ref0, var, avg);
    CALC_MSE_AVG_B(src1, ref1, var, avg);
  }

  vec = __msa_hadd_s_w(avg, avg);
  *diff = HADD_SW_S32(vec);

  return HADD_SW_S32(var);
}

static uint32_t sse_diff_32x64_msa(const uint8_t *src_ptr, int32_t src_stride,
                                   const uint8_t *ref_ptr, int32_t ref_stride,
                                   int32_t *diff) {
  int32_t ht_cnt;
  v16u8 src0, src1, ref0, ref1;
  v8i16 avg0 = { 0 };
  v8i16 avg1 = { 0 };
  v4i32 vec, var = { 0 };

  for (ht_cnt = 16; ht_cnt--;) {
    LD_UB2(src_ptr, 16, src0, src1);
    src_ptr += src_stride;
    LD_UB2(ref_ptr, 16, ref0, ref1);
    ref_ptr += ref_stride;
    CALC_MSE_AVG_B(src0, ref0, var, avg0);
    CALC_MSE_AVG_B(src1, ref1, var, avg1);

    LD_UB2(src_ptr, 16, src0, src1);
    src_ptr += src_stride;
    LD_UB2(ref_ptr, 16, ref0, ref1);
    ref_ptr += ref_stride;
    CALC_MSE_AVG_B(src0, ref0, var, avg0);
    CALC_MSE_AVG_B(src1, ref1, var, avg1);

    LD_UB2(src_ptr, 16, src0, src1);
    src_ptr += src_stride;
    LD_UB2(ref_ptr, 16, ref0, ref1);
    ref_ptr += ref_stride;
    CALC_MSE_AVG_B(src0, ref0, var, avg0);
    CALC_MSE_AVG_B(src1, ref1, var, avg1);

    LD_UB2(src_ptr, 16, src0, src1);
    src_ptr += src_stride;
    LD_UB2(ref_ptr, 16, ref0, ref1);
    ref_ptr += ref_stride;
    CALC_MSE_AVG_B(src0, ref0, var, avg0);
    CALC_MSE_AVG_B(src1, ref1, var, avg1);
  }

  vec = __msa_hadd_s_w(avg0, avg0);
  vec += __msa_hadd_s_w(avg1, avg1);
  *diff = HADD_SW_S32(vec);

  return HADD_SW_S32(var);
}

static uint32_t sse_diff_64x32_msa(const uint8_t *src_ptr, int32_t src_stride,
                                   const uint8_t *ref_ptr, int32_t ref_stride,
                                   int32_t *diff) {
  int32_t ht_cnt;
  v16u8 src0, src1, src2, src3;
  v16u8 ref0, ref1, ref2, ref3;
  v8i16 avg0 = { 0 };
  v8i16 avg1 = { 0 };
  v4i32 vec, var = { 0 };

  for (ht_cnt = 16; ht_cnt--;) {
    LD_UB4(src_ptr, 16, src0, src1, src2, src3);
    src_ptr += src_stride;
    LD_UB4(ref_ptr, 16, ref0, ref1, ref2, ref3);
    ref_ptr += ref_stride;
    CALC_MSE_AVG_B(src0, ref0, var, avg0);
    CALC_MSE_AVG_B(src2, ref2, var, avg0);
    CALC_MSE_AVG_B(src1, ref1, var, avg1);
    CALC_MSE_AVG_B(src3, ref3, var, avg1);

    LD_UB4(src_ptr, 16, src0, src1, src2, src3);
    src_ptr += src_stride;
    LD_UB4(ref_ptr, 16, ref0, ref1, ref2, ref3);
    ref_ptr += ref_stride;
    CALC_MSE_AVG_B(src0, ref0, var, avg0);
    CALC_MSE_AVG_B(src2, ref2, var, avg0);
    CALC_MSE_AVG_B(src1, ref1, var, avg1);
    CALC_MSE_AVG_B(src3, ref3, var, avg1);
  }

  vec = __msa_hadd_s_w(avg0, avg0);
  vec += __msa_hadd_s_w(avg1, avg1);
  *diff = HADD_SW_S32(vec);

  return HADD_SW_S32(var);
}

static uint32_t sse_diff_64x64_msa(const uint8_t *src_ptr, int32_t src_stride,
                                   const uint8_t *ref_ptr, int32_t ref_stride,
                                   int32_t *diff) {
  int32_t ht_cnt;
  v16u8 src0, src1, src2, src3;
  v16u8 ref0, ref1, ref2, ref3;
  v8i16 avg0 = { 0 };
  v8i16 avg1 = { 0 };
  v8i16 avg2 = { 0 };
  v8i16 avg3 = { 0 };
  v4i32 vec, var = { 0 };

  for (ht_cnt = 32; ht_cnt--;) {
    LD_UB4(src_ptr, 16, src0, src1, src2, src3);
    src_ptr += src_stride;
    LD_UB4(ref_ptr, 16, ref0, ref1, ref2, ref3);
    ref_ptr += ref_stride;

    CALC_MSE_AVG_B(src0, ref0, var, avg0);
    CALC_MSE_AVG_B(src1, ref1, var, avg1);
    CALC_MSE_AVG_B(src2, ref2, var, avg2);
    CALC_MSE_AVG_B(src3, ref3, var, avg3);
    LD_UB4(src_ptr, 16, src0, src1, src2, src3);
    src_ptr += src_stride;
    LD_UB4(ref_ptr, 16, ref0, ref1, ref2, ref3);
    ref_ptr += ref_stride;
    CALC_MSE_AVG_B(src0, ref0, var, avg0);
    CALC_MSE_AVG_B(src1, ref1, var, avg1);
    CALC_MSE_AVG_B(src2, ref2, var, avg2);
    CALC_MSE_AVG_B(src3, ref3, var, avg3);
  }

  vec = __msa_hadd_s_w(avg0, avg0);
  vec += __msa_hadd_s_w(avg1, avg1);
  vec += __msa_hadd_s_w(avg2, avg2);
  vec += __msa_hadd_s_w(avg3, avg3);
  *diff = HADD_SW_S32(vec);

  return HADD_SW_S32(var);
}

static uint32_t get_mb_ss_msa(const int16_t *src) {
  uint32_t sum, cnt;
  v8i16 src0, src1, src2, src3;
  v4i32 src0_l, src1_l, src2_l, src3_l;
  v4i32 src0_r, src1_r, src2_r, src3_r;
  v2i64 sq_src_l = { 0 };
  v2i64 sq_src_r = { 0 };

  for (cnt = 8; cnt--;) {
    LD_SH4(src, 8, src0, src1, src2, src3);
    src += 4 * 8;

    UNPCK_SH_SW(src0, src0_l, src0_r);
    UNPCK_SH_SW(src1, src1_l, src1_r);
    UNPCK_SH_SW(src2, src2_l, src2_r);
    UNPCK_SH_SW(src3, src3_l, src3_r);

    DPADD_SD2_SD(src0_l, src0_r, sq_src_l, sq_src_r);
    DPADD_SD2_SD(src1_l, src1_r, sq_src_l, sq_src_r);
    DPADD_SD2_SD(src2_l, src2_r, sq_src_l, sq_src_r);
    DPADD_SD2_SD(src3_l, src3_r, sq_src_l, sq_src_r);
  }

  sq_src_l += __msa_splati_d(sq_src_l, 1);
  sq_src_r += __msa_splati_d(sq_src_r, 1);

  sum = __msa_copy_s_d(sq_src_l, 0);
  sum += __msa_copy_s_d(sq_src_r, 0);

  return sum;
}

static uint32_t sse_4width_msa(const uint8_t *src_ptr, int32_t src_stride,
                               const uint8_t *ref_ptr, int32_t ref_stride,
                               int32_t height) {
  int32_t ht_cnt;
  uint32_t src0, src1, src2, src3;
  uint32_t ref0, ref1, ref2, ref3;
  v16u8 src = { 0 };
  v16u8 ref = { 0 };
  v4i32 var = { 0 };

  for (ht_cnt = (height >> 2); ht_cnt--;) {
    LW4(src_ptr, src_stride, src0, src1, src2, src3);
    src_ptr += (4 * src_stride);
    LW4(ref_ptr, ref_stride, ref0, ref1, ref2, ref3);
    ref_ptr += (4 * ref_stride);

    INSERT_W4_UB(src0, src1, src2, src3, src);
    INSERT_W4_UB(ref0, ref1, ref2, ref3, ref);
    CALC_MSE_B(src, ref, var);
  }

  return HADD_SW_S32(var);
}

static uint32_t sse_8width_msa(const uint8_t *src_ptr, int32_t src_stride,
                               const uint8_t *ref_ptr, int32_t ref_stride,
                               int32_t height) {
  int32_t ht_cnt;
  v16u8 src0, src1, src2, src3;
  v16u8 ref0, ref1, ref2, ref3;
  v4i32 var = { 0 };

  for (ht_cnt = (height >> 2); ht_cnt--;) {
    LD_UB4(src_ptr, src_stride, src0, src1, src2, src3);
    src_ptr += (4 * src_stride);
    LD_UB4(ref_ptr, ref_stride, ref0, ref1, ref2, ref3);
    ref_ptr += (4 * ref_stride);

    PCKEV_D4_UB(src1, src0, src3, src2, ref1, ref0, ref3, ref2, src0, src1,
                ref0, ref1);
    CALC_MSE_B(src0, ref0, var);
    CALC_MSE_B(src1, ref1, var);
  }

  return HADD_SW_S32(var);
}

static uint32_t sse_16width_msa(const uint8_t *src_ptr, int32_t src_stride,
                                const uint8_t *ref_ptr, int32_t ref_stride,
                                int32_t height) {
  int32_t ht_cnt;
  v16u8 src, ref;
  v4i32 var = { 0 };

  for (ht_cnt = (height >> 2); ht_cnt--;) {
    src = LD_UB(src_ptr);
    src_ptr += src_stride;
    ref = LD_UB(ref_ptr);
    ref_ptr += ref_stride;
    CALC_MSE_B(src, ref, var);

    src = LD_UB(src_ptr);
    src_ptr += src_stride;
    ref = LD_UB(ref_ptr);
    ref_ptr += ref_stride;
    CALC_MSE_B(src, ref, var);

    src = LD_UB(src_ptr);
    src_ptr += src_stride;
    ref = LD_UB(ref_ptr);
    ref_ptr += ref_stride;
    CALC_MSE_B(src, ref, var);

    src = LD_UB(src_ptr);
    src_ptr += src_stride;
    ref = LD_UB(ref_ptr);
    ref_ptr += ref_stride;
    CALC_MSE_B(src, ref, var);
  }

  return HADD_SW_S32(var);
}

static uint32_t sse_32width_msa(const uint8_t *src_ptr, int32_t src_stride,
                                const uint8_t *ref_ptr, int32_t ref_stride,
                                int32_t height) {
  int32_t ht_cnt;
  v16u8 src0, src1, ref0, ref1;
  v4i32 var = { 0 };

  for (ht_cnt = (height >> 2); ht_cnt--;) {
    LD_UB2(src_ptr, 16, src0, src1);
    src_ptr += src_stride;
    LD_UB2(ref_ptr, 16, ref0, ref1);
    ref_ptr += ref_stride;
    CALC_MSE_B(src0, ref0, var);
    CALC_MSE_B(src1, ref1, var);

    LD_UB2(src_ptr, 16, src0, src1);
    src_ptr += src_stride;
    LD_UB2(ref_ptr, 16, ref0, ref1);
    ref_ptr += ref_stride;
    CALC_MSE_B(src0, ref0, var);
    CALC_MSE_B(src1, ref1, var);

    LD_UB2(src_ptr, 16, src0, src1);
    src_ptr += src_stride;
    LD_UB2(ref_ptr, 16, ref0, ref1);
    ref_ptr += ref_stride;
    CALC_MSE_B(src0, ref0, var);
    CALC_MSE_B(src1, ref1, var);

    LD_UB2(src_ptr, 16, src0, src1);
    src_ptr += src_stride;
    LD_UB2(ref_ptr, 16, ref0, ref1);
    ref_ptr += ref_stride;
    CALC_MSE_B(src0, ref0, var);
    CALC_MSE_B(src1, ref1, var);
  }

  return HADD_SW_S32(var);
}

static uint32_t sse_64width_msa(const uint8_t *src_ptr, int32_t src_stride,
                                const uint8_t *ref_ptr, int32_t ref_stride,
                                int32_t height) {
  int32_t ht_cnt;
  v16u8 src0, src1, src2, src3;
  v16u8 ref0, ref1, ref2, ref3;
  v4i32 var = { 0 };

  for (ht_cnt = height >> 1; ht_cnt--;) {
    LD_UB4(src_ptr, 16, src0, src1, src2, src3);
    src_ptr += src_stride;
    LD_UB4(ref_ptr, 16, ref0, ref1, ref2, ref3);
    ref_ptr += ref_stride;
    CALC_MSE_B(src0, ref0, var);
    CALC_MSE_B(src2, ref2, var);
    CALC_MSE_B(src1, ref1, var);
    CALC_MSE_B(src3, ref3, var);

    LD_UB4(src_ptr, 16, src0, src1, src2, src3);
    src_ptr += src_stride;
    LD_UB4(ref_ptr, 16, ref0, ref1, ref2, ref3);
    ref_ptr += ref_stride;
    CALC_MSE_B(src0, ref0, var);
    CALC_MSE_B(src2, ref2, var);
    CALC_MSE_B(src1, ref1, var);
    CALC_MSE_B(src3, ref3, var);
  }

  return HADD_SW_S32(var);
}

uint32_t aom_get4x4sse_cs_msa(const uint8_t *src_ptr, int32_t src_stride,
                              const uint8_t *ref_ptr, int32_t ref_stride) {
  uint32_t err = 0;
  uint32_t src0, src1, src2, src3;
  uint32_t ref0, ref1, ref2, ref3;
  v16i8 src = { 0 };
  v16i8 ref = { 0 };
  v16u8 src_vec0, src_vec1;
  v8i16 diff0, diff1;
  v4i32 err0 = { 0 };
  v4i32 err1 = { 0 };

  LW4(src_ptr, src_stride, src0, src1, src2, src3);
  LW4(ref_ptr, ref_stride, ref0, ref1, ref2, ref3);
  INSERT_W4_SB(src0, src1, src2, src3, src);
  INSERT_W4_SB(ref0, ref1, ref2, ref3, ref);
  ILVRL_B2_UB(src, ref, src_vec0, src_vec1);
  HSUB_UB2_SH(src_vec0, src_vec1, diff0, diff1);
  DPADD_SH2_SW(diff0, diff1, diff0, diff1, err0, err1);
  err = HADD_SW_S32(err0);
  err += HADD_SW_S32(err1);

  return err;
}

#define VARIANCE_4Wx4H(sse, diff) VARIANCE_WxH(sse, diff, 4);
#define VARIANCE_4Wx8H(sse, diff) VARIANCE_WxH(sse, diff, 5);
#define VARIANCE_8Wx4H(sse, diff) VARIANCE_WxH(sse, diff, 5);
#define VARIANCE_8Wx8H(sse, diff) VARIANCE_WxH(sse, diff, 6);
#define VARIANCE_8Wx16H(sse, diff) VARIANCE_WxH(sse, diff, 7);
#define VARIANCE_16Wx8H(sse, diff) VARIANCE_WxH(sse, diff, 7);
#define VARIANCE_16Wx16H(sse, diff) VARIANCE_WxH(sse, diff, 8);

#define VARIANCE_16Wx32H(sse, diff) VARIANCE_LARGE_WxH(sse, diff, 9);
#define VARIANCE_32Wx16H(sse, diff) VARIANCE_LARGE_WxH(sse, diff, 9);
#define VARIANCE_32Wx32H(sse, diff) VARIANCE_LARGE_WxH(sse, diff, 10);
#define VARIANCE_32Wx64H(sse, diff) VARIANCE_LARGE_WxH(sse, diff, 11);
#define VARIANCE_64Wx32H(sse, diff) VARIANCE_LARGE_WxH(sse, diff, 11);
#define VARIANCE_64Wx64H(sse, diff) VARIANCE_LARGE_WxH(sse, diff, 12);

#define AOM_VARIANCE_WDXHT_MSA(wd, ht)                                         \
  uint32_t aom_variance##wd##x##ht##_msa(                                      \
      const uint8_t *src, int32_t src_stride, const uint8_t *ref,              \
      int32_t ref_stride, uint32_t *sse) {                                     \
    int32_t diff;                                                              \
                                                                               \
    *sse =                                                                     \
        sse_diff_##wd##width_msa(src, src_stride, ref, ref_stride, ht, &diff); \
                                                                               \
    return VARIANCE_##wd##Wx##ht##H(*sse, diff);                               \
  }

/* clang-format off */
AOM_VARIANCE_WDXHT_MSA(4, 4)
AOM_VARIANCE_WDXHT_MSA(4, 8)

AOM_VARIANCE_WDXHT_MSA(8, 4)
AOM_VARIANCE_WDXHT_MSA(8, 8)
AOM_VARIANCE_WDXHT_MSA(8, 16)

AOM_VARIANCE_WDXHT_MSA(16, 8)
AOM_VARIANCE_WDXHT_MSA(16, 16)
AOM_VARIANCE_WDXHT_MSA(16, 32)

AOM_VARIANCE_WDXHT_MSA(32, 16)
AOM_VARIANCE_WDXHT_MSA(32, 32)
/* clang-format on */

uint32_t aom_variance32x64_msa(const uint8_t *src, int32_t src_stride,
                               const uint8_t *ref, int32_t ref_stride,
                               uint32_t *sse) {
  int32_t diff;

  *sse = sse_diff_32x64_msa(src, src_stride, ref, ref_stride, &diff);

  return VARIANCE_32Wx64H(*sse, diff);
}

uint32_t aom_variance64x32_msa(const uint8_t *src, int32_t src_stride,
                               const uint8_t *ref, int32_t ref_stride,
                               uint32_t *sse) {
  int32_t diff;

  *sse = sse_diff_64x32_msa(src, src_stride, ref, ref_stride, &diff);

  return VARIANCE_64Wx32H(*sse, diff);
}

uint32_t aom_variance64x64_msa(const uint8_t *src, int32_t src_stride,
                               const uint8_t *ref, int32_t ref_stride,
                               uint32_t *sse) {
  int32_t diff;

  *sse = sse_diff_64x64_msa(src, src_stride, ref, ref_stride, &diff);

  return VARIANCE_64Wx64H(*sse, diff);
}

uint32_t aom_mse8x8_msa(const uint8_t *src, int32_t src_stride,
                        const uint8_t *ref, int32_t ref_stride, uint32_t *sse) {
  *sse = sse_8width_msa(src, src_stride, ref, ref_stride, 8);

  return *sse;
}

uint32_t aom_mse8x16_msa(const uint8_t *src, int32_t src_stride,
                         const uint8_t *ref, int32_t ref_stride,
                         uint32_t *sse) {
  *sse = sse_8width_msa(src, src_stride, ref, ref_stride, 16);

  return *sse;
}

uint32_t aom_mse16x8_msa(const uint8_t *src, int32_t src_stride,
                         const uint8_t *ref, int32_t ref_stride,
                         uint32_t *sse) {
  *sse = sse_16width_msa(src, src_stride, ref, ref_stride, 8);

  return *sse;
}

uint32_t aom_mse16x16_msa(const uint8_t *src, int32_t src_stride,
                          const uint8_t *ref, int32_t ref_stride,
                          uint32_t *sse) {
  *sse = sse_16width_msa(src, src_stride, ref, ref_stride, 16);

  return *sse;
}

void aom_get8x8var_msa(const uint8_t *src, int32_t src_stride,
                       const uint8_t *ref, int32_t ref_stride, uint32_t *sse,
                       int32_t *sum) {
  *sse = sse_diff_8width_msa(src, src_stride, ref, ref_stride, 8, sum);
}

void aom_get16x16var_msa(const uint8_t *src, int32_t src_stride,
                         const uint8_t *ref, int32_t ref_stride, uint32_t *sse,
                         int32_t *sum) {
  *sse = sse_diff_16width_msa(src, src_stride, ref, ref_stride, 16, sum);
}

uint32_t aom_get_mb_ss_msa(const int16_t *src) { return get_mb_ss_msa(src); }
