/*
 * Copyright (C) 2007 Loren Merritt <lorenm@u.washington.edu>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "../common.h"

int  xlsc_cpuid_test( void );
void xlsc_cpuid( uint32_t op, uint32_t *regs );

void xlsc_mc_copy_24_mmx( uint8_t *dst, uint8_t *src, int strided, int strides, int height );
void xlsc_mc_copy_16_mmx( uint8_t *dst, uint8_t *src, int strided, int strides, int height );
void xlsc_mc_h_24_mmx2( uint8_t *dst, uint8_t *src, int strided, int strides, int height );
void xlsc_mc_h_16_mmx2( uint8_t *dst, uint8_t *src, int strided, int strides, int height );
void xlsc_mc_v_24_mmx2( uint8_t *dst, uint8_t *src, int strided, int strides, int height );
void xlsc_mc_v_16_mmx2( uint8_t *dst, uint8_t *src, int strided, int strides, int height );
void xlsc_mc_hv_24_mmx2( uint8_t *dst, uint8_t *src, int strided, int strides, int height );
void xlsc_mc_hv_16_mmx2( uint8_t *dst, uint8_t *src, int strided, int strides, int height );

int xlsc_sad_16_mmx2( uint8_t *fenc, uint8_t *fref, int stride );
int xlsc_ssd_16_sse2( uint8_t *fenc, uint8_t *fref, int stride );
int xlsc_satd_16_mmx2( uint8_t *fenc, uint8_t *fref, int stride );
void xlsc_sad_x4_16_mmx2( uint8_t *fenc, uint8_t *r0, uint8_t *r1, uint8_t *r2, uint8_t *r3, int stride, int res[4] );

void xlsc_dct_inter_sse2( int16_t *dct, uint8_t *fenc, uint8_t *fdec );
void xlsc_dct_intra_sse2( int16_t *dct, uint8_t *fenc, uint8_t *ignored );
void xlsc_dct_inter_ssse3( int16_t *dct, uint8_t *fenc, uint8_t *fdec );
void xlsc_dct_intra_ssse3( int16_t *dct, uint8_t *fenc, uint8_t *ignored );
void xlsc_idct_inter_sse2( uint8_t *fdec, int16_t *dct );
void xlsc_idct_intra_sse2( uint8_t *fdec, int16_t *dct );

void xlsc_quant_sse2( int16_t *dct, int qp, int bias );
void xlsc_quant_ssse3( int16_t *dct, int qp, int bias );
void xlsc_dequant_sse2( int16_t *dct, int qp );

static uint32_t get_simd()
{
    uint32_t cpu = 0;
    uint32_t regs[4];
    int amd;

    if( !xlsc_cpuid_test() )
        return 0;
    xlsc_cpuid( 0, regs );
    if( !regs[0] )
        return 0;
    XCHG( uint32_t, regs[2], regs[3] );
    amd = !memcmp( regs+1, "AuthenticAMD", 12 );

    xlsc_cpuid( 1, regs );
    if( (regs[3]>>23)&1 )
        cpu = XLSC_CPU_MMX;
    else
        return 0;
    if( (regs[3]>>25)&1 )
        cpu |= XLSC_CPU_SSE | XLSC_CPU_MMX2;
    if( (regs[3]>>26)&1 )
        cpu |= XLSC_CPU_SSE2;
    if( (regs[2]>>0)&1 )
        cpu |= XLSC_CPU_SSE3;
    if( (regs[2]>>9)&1 )
        cpu |= XLSC_CPU_SSSE3;

    xlsc_cpuid( 1<<31, regs );
    if( regs[0] <= 1<<31 )
        return cpu;

    xlsc_cpuid( (1<<31)+1, regs );
    if( (regs[3]>>31)&1 )
        cpu |= XLSC_CPU_3DNOW;
    if( (regs[3]>>30)&1 )
        cpu |= XLSC_CPU_3DNOW2;
    if( amd && ((regs[3]>>22)&1) )
        cpu |= XLSC_CPU_MMX2;

    return cpu;
}

void xlsc_dsp_init_asm( xlsc_t *h )
{
    h->dsp.cpu = get_simd();

    if( h->dsp.cpu & XLSC_CPU_MMX )
    {
        h->dsp.copy = xlsc_mc_copy_16_mmx;
        h->dsp.mc[XLSC_PIXEL_16][0] = xlsc_mc_copy_16_mmx;
        h->dsp.mc[XLSC_PIXEL_17][0] = xlsc_mc_copy_24_mmx;
    }

    if( h->dsp.cpu & XLSC_CPU_MMX2 )
    {
        h->dsp.sad = xlsc_sad_16_mmx2;
        h->dsp.satd = xlsc_satd_16_mmx2;
        h->dsp.sad_x4 = xlsc_sad_x4_16_mmx2;
        h->dsp.mc[XLSC_PIXEL_16][1] = xlsc_mc_h_16_mmx2;
        h->dsp.mc[XLSC_PIXEL_17][1] = xlsc_mc_h_24_mmx2;
        h->dsp.mc[XLSC_PIXEL_16][2] = xlsc_mc_v_16_mmx2;
        h->dsp.mc[XLSC_PIXEL_17][2] = xlsc_mc_v_24_mmx2;
        h->dsp.mc[XLSC_PIXEL_16][3] = xlsc_mc_hv_16_mmx2;
        h->dsp.mc[XLSC_PIXEL_17][3] = xlsc_mc_hv_24_mmx2;
    }

    if( h->dsp.cpu & XLSC_CPU_SSE2 )
    {
        h->dsp.sub_dct[0] = xlsc_dct_inter_sse2;
        h->dsp.sub_dct[1] = xlsc_dct_intra_sse2;
        h->dsp.add_idct[0] = xlsc_idct_inter_sse2;
        h->dsp.add_idct[1] = xlsc_idct_intra_sse2;
        h->dsp.quant   = xlsc_quant_sse2;
        h->dsp.dequant = xlsc_dequant_sse2;
        h->dsp.ssd = xlsc_ssd_16_sse2;
    }

    if( h->dsp.cpu & XLSC_CPU_SSSE3 )
    {
        h->dsp.sub_dct[0] = xlsc_dct_inter_ssse3;
        h->dsp.sub_dct[1] = xlsc_dct_intra_ssse3;
        h->dsp.quant = xlsc_quant_ssse3;
    }
}

