/*
 * iWMMXt optimized DSP utils
 * Copyright (c) 2004 AGAWA Koji
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "../dsputil.h"

#define DEF(x, y) x ## _no_rnd_ ## y ##_iwmmxt
#define SET_RND(regd)  __asm__ __volatile__ ("mov r12, #1 \n\t tbcstw " #regd ", r12":::"r12");
#define WAVG2B "wavg2b"
#include "dsputil_iwmmxt_rnd.h"
#undef DEF
#undef SET_RND
#undef WAVG2B

#define DEF(x, y) x ## _ ## y ##_iwmmxt
#define SET_RND(regd)  __asm__ __volatile__ ("mov r12, #2 \n\t tbcstw " #regd ", r12":::"r12");
#define WAVG2B "wavg2br"
#include "dsputil_iwmmxt_rnd.h"
#undef DEF
#undef SET_RND
#undef WAVG2BR

// need scheduling
#define OP(AVG)						\
    asm volatile (					\
	/* alignment */					\
	"and r12, %[pixels], #7 \n\t"			\
	"bic %[pixels], %[pixels], #7 \n\t"		\
	"tmcr wcgr1, r12 \n\t"				\
							\
	"wldrd wr0, [%[pixels]] \n\t"			\
	"wldrd wr1, [%[pixels], #8] \n\t"		\
	"add %[pixels], %[pixels], %[line_size] \n\t"	\
	"walignr1 wr4, wr0, wr1 \n\t"			\
							\
	"1: \n\t"					\
							\
	"wldrd wr2, [%[pixels]] \n\t"			\
	"wldrd wr3, [%[pixels], #8] \n\t"		\
	"add %[pixels], %[pixels], %[line_size] \n\t"	\
	"pld [%[pixels]] \n\t"				\
	"walignr1 wr5, wr2, wr3 \n\t"			\
	AVG " wr6, wr4, wr5 \n\t"			\
	"wstrd wr6, [%[block]] \n\t"			\
	"add %[block], %[block], %[line_size] \n\t"	\
							\
	"wldrd wr0, [%[pixels]] \n\t"			\
	"wldrd wr1, [%[pixels], #8] \n\t"		\
	"add %[pixels], %[pixels], %[line_size] \n\t"	\
	"walignr1 wr4, wr0, wr1 \n\t"			\
	"pld [%[pixels]] \n\t"				\
	AVG " wr6, wr4, wr5 \n\t"			\
	"wstrd wr6, [%[block]] \n\t"			\
	"add %[block], %[block], %[line_size] \n\t"	\
							\
	"subs %[h], %[h], #2 \n\t"			\
	"bne 1b \n\t"					\
	: [block]"+r"(block), [pixels]"+r"(pixels), [h]"+r"(h)	\
	: [line_size]"r"(line_size) \
	: "memory", "r12");
void put_pixels8_y2_iwmmxt(uint8_t *block, const uint8_t *pixels, const int line_size, int h)
{
    OP("wavg2br");
}
void put_no_rnd_pixels8_y2_iwmmxt(uint8_t *block, const uint8_t *pixels, const int line_size, int h)
{
    OP("wavg2b");
}
#undef OP

// need scheduling
#define OP(AVG)						\
    asm volatile (					\
	/* alignment */					\
	"and r12, %[pixels], #7 \n\t"			\
	"bic %[pixels], %[pixels], #7 \n\t"		\
	"tmcr wcgr1, r12 \n\t"				\
							\
	"wldrd wr0, [%[pixels]] \n\t"			\
	"wldrd wr1, [%[pixels], #8] \n\t"		\
	"wldrd wr2, [%[pixels], #16] \n\t"		\
	"add %[pixels], %[pixels], %[line_size] \n\t"	\
	"walignr1 wr6, wr0, wr1 \n\t"			\
	"walignr1 wr7, wr1, wr2 \n\t"			\
							\
	"1: \n\t"					\
							\
	"wldrd wr3, [%[pixels]] \n\t"			\
	"wldrd wr4, [%[pixels], #8] \n\t"		\
	"wldrd wr5, [%[pixels], #16] \n\t"		\
	"add %[pixels], %[pixels], %[line_size] \n\t"	\
	"pld [%[pixels]] \n\t"				\
	"walignr1 wr8, wr3, wr4 \n\t"			\
	"walignr1 wr9, wr4, wr5 \n\t"			\
	AVG " wr6, wr6, wr8 \n\t"			\
	AVG " wr7, wr7, wr9 \n\t"			\
	"wstrd wr6, [%[block]] \n\t"			\
	"wstrd wr7, [%[block], #8] \n\t"		\
	"add %[block], %[block], %[line_size] \n\t"	\
							\
	"wldrd wr0, [%[pixels]] \n\t"			\
	"wldrd wr1, [%[pixels], #8] \n\t"		\
	"wldrd wr2, [%[pixels], #16] \n\t"		\
	"add %[pixels], %[pixels], %[line_size] \n\t"	\
	"pld [%[pixels]] \n\t"				\
	"walignr1 wr6, wr0, wr1 \n\t"			\
	"walignr1 wr7, wr1, wr2 \n\t"			\
	AVG " wr6, wr6, wr8 \n\t"			\
	AVG " wr7, wr7, wr9 \n\t"			\
	"wstrd wr6, [%[block]] \n\t"			\
	"wstrd wr7, [%[block], #8] \n\t"		\
	"add %[block], %[block], %[line_size] \n\t"	\
							\
	"subs %[h], %[h], #2 \n\t"			\
	"bne 1b \n\t"					\
	: [block]"+r"(block), [pixels]"+r"(pixels), [h]"+r"(h)	\
	: [line_size]"r"(line_size) \
	: "memory", "r12");
void put_pixels16_y2_iwmmxt(uint8_t *block, const uint8_t *pixels, const int line_size, int h)
{
    OP("wavg2br");
}
void put_no_rnd_pixels16_y2_iwmmxt(uint8_t *block, const uint8_t *pixels, const int line_size, int h)
{
    OP("wavg2b");
}
#undef OP

// need scheduling
#define OP(AVG)						\
    asm volatile (					\
	/* alignment */					\
	"and r12, %[pixels], #7 \n\t"			\
	"bic %[pixels], %[pixels], #7 \n\t"		\
	"tmcr wcgr1, r12 \n\t"				\
							\
	"wldrd wr0, [%[pixels]] \n\t"			\
	"wldrd wr1, [%[pixels], #8] \n\t"		\
	"add %[pixels], %[pixels], %[line_size] \n\t"	\
	"walignr1 wr4, wr0, wr1 \n\t"			\
							\
	"1: \n\t"					\
							\
	"wldrd wr2, [%[pixels]] \n\t"			\
	"wldrd wr3, [%[pixels], #8] \n\t"		\
	"add %[pixels], %[pixels], %[line_size] \n\t"	\
	"pld [%[pixels]] \n\t"				\
	"walignr1 wr5, wr2, wr3 \n\t"			\
	AVG " wr6, wr4, wr5 \n\t"			\
	"wldrd wr7, [%[block]] \n\t"			\
	AVG " wr8, wr7, wr6 \n\t"			\
	"wstrd wr8, [%[block]] \n\t"			\
	"add %[block], %[block], %[line_size] \n\t"	\
							\
	"wldrd wr0, [%[pixels]] \n\t"			\
	"wldrd wr1, [%[pixels], #8] \n\t"		\
	"add %[pixels], %[pixels], %[line_size] \n\t"	\
	"walignr1 wr4, wr0, wr1 \n\t"			\
	"pld [%[pixels]] \n\t"				\
	AVG " wr6, wr4, wr5 \n\t"			\
	"wldrd wr7, [%[block]] \n\t"			\
	AVG " wr8, wr7, wr6 \n\t"			\
	"wstrd wr8, [%[block]] \n\t"			\
	"add %[block], %[block], %[line_size] \n\t"	\
							\
	"subs %[h], %[h], #1 \n\t"			\
	"bne 1b \n\t"					\
	: [block]"+r"(block), [pixels]"+r"(pixels), [h]"+r"(h) \
	: [line_size]"r"(line_size)	\
	:"memory", "r12");
void avg_pixels8_y2_iwmmxt(uint8_t *block, const uint8_t *pixels, const int line_size, int h)
{
    OP("wavg2br");
}
void avg_no_rnd_pixels8_y2_iwmmxt(uint8_t *block, const uint8_t *pixels, const int line_size, int h)
{
    OP("wavg2b");
}
#undef OP

// need scheduling
#define OP(AVG)						\
    asm volatile (					\
	/* alignment */					\
	"and r12, %[pixels], #7 \n\t"			\
	"bic %[pixels], %[pixels], #7 \n\t"		\
	"tmcr wcgr1, r12 \n\t"				\
							\
	"wldrd wr0, [%[pixels]] \n\t"			\
	"wldrd wr1, [%[pixels], #8] \n\t"		\
	"wldrd wr2, [%[pixels], #16] \n\t"		\
	"add %[pixels], %[pixels], %[line_size] \n\t"	\
	"walignr1 wr6, wr0, wr1 \n\t"			\
	"walignr1 wr7, wr1, wr2 \n\t"			\
							\
	"1: \n\t"					\
							\
	"wldrd wr3, [%[pixels]] \n\t"			\
	"wldrd wr4, [%[pixels], #8] \n\t"		\
	"wldrd wr5, [%[pixels], #16] \n\t"		\
	"add %[pixels], %[pixels], %[line_size] \n\t"	\
	"pld [%[pixels]] \n\t"				\
	"walignr1 wr8, wr3, wr4 \n\t"			\
	"walignr1 wr9, wr4, wr5 \n\t"			\
	AVG " wr6, wr6, wr8 \n\t"			\
	AVG " wr7, wr7, wr9 \n\t"			\
	"wldrd wr14, [%[block]] \n\t"			\
	"wldrd wr15, [%[block], #8] \n\t"		\
	AVG " wr6, wr6, wr14 \n\t"			\
	AVG " wr7, wr7, wr15 \n\t"			\
	"wstrd wr6, [%[block]] \n\t"			\
	"wstrd wr7, [%[block], #8] \n\t"		\
	"add %[block], %[block], %[line_size] \n\t"	\
							\
	"wldrd wr0, [%[pixels]] \n\t"			\
	"wldrd wr1, [%[pixels], #8] \n\t"		\
	"wldrd wr2, [%[pixels], #16] \n\t"		\
	"add %[pixels], %[pixels], %[line_size] \n\t"	\
	"pld [%[pixels]] \n\t"				\
	"walignr1 wr6, wr0, wr1 \n\t"			\
	"walignr1 wr7, wr1, wr2 \n\t"			\
	AVG " wr6, wr6, wr8 \n\t"			\
	AVG " wr7, wr7, wr9 \n\t"			\
	"wldrd wr14, [%[block]] \n\t"			\
	"wldrd wr15, [%[block], #8] \n\t"		\
	AVG " wr6, wr6, wr14 \n\t"			\
	AVG " wr7, wr7, wr15 \n\t"			\
	"wstrd wr6, [%[block]] \n\t"			\
	"wstrd wr7, [%[block], #8] \n\t"		\
	"add %[block], %[block], %[line_size] \n\t"	\
							\
	"subs %[h], %[h], #2 \n\t"			\
	"bne 1b \n\t"					\
	: [block]"+r"(block), [pixels]"+r"(pixels), [h]"+r"(h)	\
	: [line_size]"r"(line_size)	\
	: "memory", "r12");
void avg_pixels16_y2_iwmmxt(uint8_t *block, const uint8_t *pixels, const int line_size, int h)
{
    OP("wavg2br");
}
void avg_no_rnd_pixels16_y2_iwmmxt(uint8_t *block, const uint8_t *pixels, const int line_size, int h)
{
    OP("wavg2b");
}
#undef OP

#if 1
void add_pixels_clamped_iwmmxt(const DCTELEM *block, uint8_t *pixels, int line_size)
{
    const DCTELEM *p;
    uint8_t *pix;
    int i;

#if 0
    if (((unsigned int)block) & 7)
	printf("1");
    if (((unsigned int)pixels) & 7)
	printf("2");
    if (((unsigned int)line_size) & 7)
	printf("3");
#endif

    /* read the pixels */
    p = block;
    pix = pixels;
    i = 8;
    do {
    asm volatile (
		  "wldrd wr0, [%1] \n\t"
		  "wldrd wr1, [%1, #8] \n\t"
		  "wldrd wr2, [%0] \n\t"
		  "wunpckelub wr4, wr2 \n\t"
		  "wunpckehub wr5, wr2 \n\t"
		  "waddhss wr6, wr0, wr4 \n\t"
		  "waddhss wr7, wr1, wr5 \n\t"
		  "wpackhus wr2, wr6, wr7 \n\t"
		  "wstrd wr2, [%0] \n\t"
		:
		:"r"(pix), "r"(p)
		:"memory");
        pix += line_size;
        p += 8;

#if 0
	__asm __volatile(
		"movq	(%2), %%mm0\n\t"
		"movq	8(%2), %%mm1\n\t"
		"movq	16(%2), %%mm2\n\t"
		"movq	24(%2), %%mm3\n\t"
		"movq	%0, %%mm4\n\t"
		"movq	%1, %%mm6\n\t"
		"movq	%%mm4, %%mm5\n\t"
		"punpcklbw %%mm7, %%mm4\n\t"
		"punpckhbw %%mm7, %%mm5\n\t"
		"paddsw	%%mm4, %%mm0\n\t"
		"paddsw	%%mm5, %%mm1\n\t"
		"movq	%%mm6, %%mm5\n\t"
		"punpcklbw %%mm7, %%mm6\n\t"
		"punpckhbw %%mm7, %%mm5\n\t"
		"paddsw	%%mm6, %%mm2\n\t"
		"paddsw	%%mm5, %%mm3\n\t"
		"packuswb %%mm1, %%mm0\n\t"
		"packuswb %%mm3, %%mm2\n\t"
		"movq	%%mm0, %0\n\t"
		"movq	%%mm2, %1\n\t"
		:"+m"(*pix), "+m"(*(pix+line_size))
		:"r"(p)
		:"memory");
        pix += line_size*2;
        p += 16;
#endif
    } while (--i);
}
#endif

static void nop(uint8_t *block, const uint8_t *pixels, int line_size, int h)
{
    return;
}

void dsputil_init_iwmmxt(DSPContext* c, AVCodecContext *avctx)
{
    c->put_pixels_tab[0][0] = put_pixels16_iwmmxt;
    c->put_pixels_tab[0][1] = put_pixels16_x2_iwmmxt;
    c->put_pixels_tab[0][2] = put_pixels16_y2_iwmmxt;
    c->put_pixels_tab[0][3] = put_pixels16_xy2_iwmmxt;
    c->put_no_rnd_pixels_tab[0][0] = put_pixels16_iwmmxt;
    c->put_no_rnd_pixels_tab[0][1] = put_no_rnd_pixels16_x2_iwmmxt;
    c->put_no_rnd_pixels_tab[0][2] = put_no_rnd_pixels16_y2_iwmmxt;
    c->put_no_rnd_pixels_tab[0][3] = put_no_rnd_pixels16_xy2_iwmmxt;

    c->put_pixels_tab[1][0] = put_pixels8_iwmmxt;
    c->put_pixels_tab[1][1] = put_pixels8_x2_iwmmxt;
    c->put_pixels_tab[1][2] = put_pixels8_y2_iwmmxt;
    c->put_pixels_tab[1][3] = put_pixels8_xy2_iwmmxt;
    c->put_no_rnd_pixels_tab[1][0] = put_pixels8_iwmmxt;
    c->put_no_rnd_pixels_tab[1][1] = put_no_rnd_pixels8_x2_iwmmxt;
    c->put_no_rnd_pixels_tab[1][2] = put_no_rnd_pixels8_y2_iwmmxt;
    c->put_no_rnd_pixels_tab[1][3] = put_no_rnd_pixels8_xy2_iwmmxt;

    c->avg_pixels_tab[0][0] = avg_pixels16_iwmmxt;
    c->avg_pixels_tab[0][1] = avg_pixels16_x2_iwmmxt;
    c->avg_pixels_tab[0][2] = avg_pixels16_y2_iwmmxt;
    c->avg_pixels_tab[0][3] = avg_pixels16_xy2_iwmmxt;
    c->avg_no_rnd_pixels_tab[0][0] = avg_pixels16_iwmmxt;
    c->avg_no_rnd_pixels_tab[0][1] = avg_no_rnd_pixels16_x2_iwmmxt;
    c->avg_no_rnd_pixels_tab[0][2] = avg_no_rnd_pixels16_y2_iwmmxt;
    c->avg_no_rnd_pixels_tab[0][3] = avg_no_rnd_pixels16_xy2_iwmmxt;

    c->avg_pixels_tab[1][0] = avg_pixels8_iwmmxt;
    c->avg_pixels_tab[1][1] = avg_pixels8_x2_iwmmxt;
    c->avg_pixels_tab[1][2] = avg_pixels8_y2_iwmmxt;
    c->avg_pixels_tab[1][3] = avg_pixels8_xy2_iwmmxt;
    c->avg_no_rnd_pixels_tab[1][0] = avg_no_rnd_pixels8_iwmmxt;
    c->avg_no_rnd_pixels_tab[1][1] = avg_no_rnd_pixels8_x2_iwmmxt;
    c->avg_no_rnd_pixels_tab[1][2] = avg_no_rnd_pixels8_y2_iwmmxt;
    c->avg_no_rnd_pixels_tab[1][3] = avg_no_rnd_pixels8_xy2_iwmmxt;
}
