/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"@(#)mlib_v_VideoDCTQuantize.c	9.3	07/11/05 SMI"

/*
 * FUNCTIONS
 *	mlib_VideoDCT8x8Quantize_S16_U8[_NA]  - computes the forward DCT
 * 						in the intra mode and
 *						performs quantization
 *	mlib_VideoDCT8x8Quantize_S16_S16_B12[_NA] - computes forward DCT
 *						in the inter mode and
 *						 performs quantization
 *
 * SYNOPSIS
 *	mlib_status  mlib_VideoDCT8x8Quantize_S16_U8[_NA] (
 *					mlib_s16       coeffs[64],
 *					const mlib_u8 *block,
 *					const mlib_d64 qtable[64],
 *					mlib_s32       stride)
 *
 *	mlib_status  mlib_VideoDCT8x8Quantize_S16_S16_B12[_NA](
 *					mlib_s16        coeffs[64],
 *					const mlib_s16 *block,
 *					const mlib_d64  qtable[64])
 *
 *
 * ARGUMENTS
 *      coeffs	Pointer to output after DCT and quantization
 *		coefficients.
 *      block	Pointer to 8x8 block in current frame or
 *		motion-compensated reference block.
 *      qtable	Pointer to the quantization table generated by
 *		mlib_VideoQuantizeInit_S16.
 *      stride	Stride in bytes between adjacent rows in a block.
 *
 * RESTRICTIONS
 *	The block and the coeffs must be 8-byte aligned and the stride
 *	must be a multiple of 8 for mlib_VideoDCT8x8Quantize_S16_U8
 *	and mlib_VideoDCT8x8Quantize_S16_S16_B12.
 *
 *	The values of the block should be within [-2048, 2047] range
 *	for mlib_VideoDCT8x8Quantize_S16_S16_B12 and
 *	mlib_VideoDCT8x8Quantize_S16_S16_B12_NA.
 *
 * DESCRIPTION
 *	mlib_VideoDCT8x8Quantize_S16_U8[_NA] computes the forward
 *	discrete cosine transform (DCT) in the intra mode and
 *	performs quantization.
 *
 *	mlib_VideoDCT8x8Quantize_S16_S16_B12[_NA] computes the
 *	forward discrete cosine transform (DCT) in the inter mode
 *	and performs quantization.
 *
 *	DCT algorithm:
 *
 *	F(u,v) =
 *      1             7   7             pi*(2*x+1)*u      pi*(2*y+1)*v
 *	-*C(u)*C(v)* SUM SUM f(x,y)*cos(------------)*cos(------------)
 *      4            x=0 y=0                 16                16
 *
 *	C(u) = 1/sqrt(2) if u == 0, otherwise C(u) = 1
 *
 *	where	x = 0, 1, ... ,7; y = 0, 1, ... ,7
 *		u = 0, 1, ... ,7; v = 0, 1, ... ,7
 *
 *	Quantization algorithm:
 *
 *	if (F(u,v) > 0)
 *		coeffs(u,v) = (mlib_s32)(F(u,v) * qtable(u,v) + 0.5)
 *	else
 * 		coeffs(u,v) = (mlib_s32)(F(u,v) * qtable(u,v) - 0.5)
 *
 *	where	u = 0, 1, ... ,7; v = 0, 1, ... ,7
 */

#include <mlib_video.h>
#include <vis_proto.h>
#include <mlib_v_VideoDCT.h>
/* *********************************************************** */

#if ! defined(__MEDIALIB_OLD_NAMES)
#if defined(__SUNPRO_C)

#pragma	weak mlib_VideoDCT8x8Quantize_S16_U8 = \
			__mlib_VideoDCT8x8Quantize_S16_U8
#pragma weak mlib_VideoDCT8x8Quantize_S16_U8_NA = \
			__mlib_VideoDCT8x8Quantize_S16_U8_NA
#pragma weak mlib_VideoDCT8x8Quantize_S16_S16_B12 = \
			__mlib_VideoDCT8x8Quantize_S16_S16_B12
#pragma weak mlib_VideoDCT8x8Quantize_S16_S16_B12_NA = \
			__mlib_VideoDCT8x8Quantize_S16_S16_B12_NA

#elif defined(__GNUC__)	/* defined(__SUNPRO_C) */
__typeof__(__mlib_VideoDCT8x8Quantize_S16_U8)	\
		mlib_VideoDCT8x8Quantize_S16_U8
__attribute__((weak, alias("__mlib_VideoDCT8x8Quantize_S16_U8")));
__typeof__(__mlib_VideoDCT8x8Quantize_S16_U8_NA) \
		mlib_VideoDCT8x8Quantize_S16_U8_NA
__attribute__((weak, alias("__mlib_VideoDCT8x8Quantize_S16_U8_NA")));
__typeof__(__mlib_VideoDCT8x8Quantize_S16_S16_B12) \
		mlib_VideoDCT8x8Quantize_S16_S16_B12
__attribute__((weak, alias("__mlib_VideoDCT8x8Quantize_S16_S16_B12")));
__typeof__(__mlib_VideoDCT8x8Quantize_S16_S16_B12_NA) \
		mlib_VideoDCT8x8Quantize_S16_S16_B12_NA
__attribute__((weak, alias("__mlib_VideoDCT8x8Quantize_S16_S16_B12_NA")));

#else /* defined(__SUNPRO_C) */

#error  "unknown platform"

#endif /* defined(__SUNPRO_C) */
#endif /* ! defined(__MEDIALIB_OLD_NAMES) */

#define	Quant_ST(i, dx, dy)                                   \
	{                                                              \
	    mlib_d64 dr2, dr3;                                         \
	    mlib_d64 dr, dr1;                                         \
	                                                               \
	    dr = vis_fmuld8sux16(vis_read_hi(dx), vis_read_hi(dy));    \
	    dr1 = vis_fmuld8ulx16(vis_read_hi(dx), vis_read_hi(dy));   \
	    dr = vis_fpadd32(dr, dr1);                                 \
	    dr2 = vis_fmuld8sux16(vis_read_lo(dx), vis_read_lo(dy));   \
	    dr3 = vis_fmuld8ulx16(vis_read_lo(dx), vis_read_lo(dy));   \
	    dr1 = vis_fpadd32(dr2, dr3);                               \
	    dp[i] = vis_fpackfix_pair(dr, dr1);			       \
	}

#define	Quant_ST_NA(dst, dx, dy)                                   \
	{                                                              \
	    mlib_d64 dr2, dr3;                                         \
	    mlib_d64 dr, dr1;                                         \
	                                                               \
	    dr = vis_fmuld8sux16(vis_read_hi(dx), vis_read_hi(dy));    \
	    dr1 = vis_fmuld8ulx16(vis_read_hi(dx), vis_read_hi(dy));   \
	    dr = vis_fpadd32(dr, dr1);                                 \
	    dr2 = vis_fmuld8sux16(vis_read_lo(dx), vis_read_lo(dy));   \
	    dr3 = vis_fmuld8ulx16(vis_read_lo(dx), vis_read_lo(dy));   \
	    dr1 = vis_fpadd32(dr2, dr3);                               \
	    dr = vis_fpadd32(dr, w_const);			       \
	    dr1 = vis_fpadd32(dr1, w_const);			       \
	    dst = vis_fpackfix_pair(dr, dr1);			       \
	}

/* *********************************************************** */

mlib_status
__mlib_VideoDCT8x8Quantize_S16_U8(
	mlib_s16 coeffs[64],
	const mlib_u8 *block,
	const mlib_d64 qtable[64],
	mlib_s32 stride)
{
	mlib_u8 *sp = (void *)block;
	mlib_d64 d00, d10, d20, d30, d40, d50, d60, d70;
	mlib_d64 d01, d11, d21, d31, d41, d51, d61, d71;
	mlib_d64 t00, t10, t20, t30, t40, t50, t60, t70, t80, t90;
	mlib_d64 t01, t11, t21, t31, t41, t51, t61, t71, t81, t91;
	mlib_d64 r00, r10, r20, r30, r40, r50, r60, r70;
	mlib_d64 r01, r11, r21, r31, r41, r51, r61, r71;
	mlib_d64 *dp = (mlib_d64 *)coeffs;
	mlib_f32 FCOS, FONE, c17, c26, c35;

	vis_write_gsr(1 << 3);
/*
 * first stage
 */

	LOAD_DATA_AA_INTRA TRANSPOSE_8X8(
		d00,
		d10,
		d20,
		d30,
		d40,
		d50,
		d60,
		d70,
		d01,
		d11,
		d21,
		d31,
		d41,
		d51,
		d61,
		d71);
	LOADCONSTS5 PREPARE_DATA_INTRA(
		hi,
		0);

	COMPUTING_DATA(0);

	PREPARE_DATA_INTRA(lo, 1);
	TRANSPOSE(d40, d50, d60, d70, r01, r11, r21, r31);
	TRANSPOSE(d00, d10, d20, d30, r00, r10, r20, r30);
	COMPUTING_DATA(1);

	TRANSPOSE(d01, d11, d21, d31, r40, r50, r60, r70);

/*
 * second stage
 */
	PREPARE_DATA_INTER(0);
	COMPUTING_DATA(0);

	TRANSPOSE(d41, d51, d61, d71, r41, r51, r61, r71);

	ENDSCALE(0);

	Quant_ST(0, d00, qtable[0]);
	Quant_ST(2, d10, qtable[2]);
	Quant_ST(4, d20, qtable[4]);
	Quant_ST(6, d30, qtable[6]);
	Quant_ST(8, d40, qtable[8]);
	Quant_ST(10, d50, qtable[10]);
	Quant_ST(12, d60, qtable[12]);
	Quant_ST(14, d70, qtable[14]);

	PREPARE_DATA_INTER(1);
	COMPUTING_DATA(1);

	ENDSCALE(1);

	Quant_ST(1, d01, qtable[1]);
	Quant_ST(3, d11, qtable[3]);
	Quant_ST(5, d21, qtable[5]);
	Quant_ST(7, d31, qtable[7]);
	Quant_ST(9, d41, qtable[9]);
	Quant_ST(11, d51, qtable[11]);
	Quant_ST(13, d61, qtable[13]);
	Quant_ST(15, d71, qtable[15]);


	return (MLIB_SUCCESS);
}

mlib_status
__mlib_VideoDCT8x8Quantize_S16_U8_NA(
	mlib_s16 coeffs[64],
	const mlib_u8 *block,
	const mlib_d64 qtable[64],
	mlib_s32 stride)
{
	mlib_u8 *sp = (void *)block;
	mlib_d64 d00, d10, d20, d30, d40, d50, d60, d70;
	mlib_d64 d01, d11, d21, d31, d41, d51, d61, d71;
	mlib_d64 t00, t10, t20, t30, t40, t50, t60, t70, t80, t90;
	mlib_d64 t01, t11, t21, t31, t41, t51, t61, t71, t81, t91;
	mlib_d64 r00, r10, r20, r30, r40, r50, r60, r70;
	mlib_d64 r01, r11, r21, r31, r41, r51, r61, r71;
	mlib_d64 *dp;
	mlib_d64 w_const = vis_to_double_dup(0x4000);

	mlib_s32 mask;
	mlib_f32 FCOS, FONE, c17, c26, c35;

	if (block == NULL || coeffs == NULL || stride <= 0)
		return (MLIB_FAILURE);

	if (!(((mlib_addr)block | (mlib_addr)coeffs | stride) & 7)) {
		return (__mlib_VideoDCT8x8Quantize_S16_U8(coeffs,
					block, qtable, stride));
	}

	vis_write_gsr(1 << 3);
/*
 * first stage
 */

	LOADCONSTS5;
	LOAD_DATA_GE_INTRA;

	TRANSPOSE_8X8(d00, d10, d20, d30, d40, d50, d60, d70,
		d01, d11, d21, d31, d41, d51, d61, d71);

	PREPARE_DATA_INTRA(hi, 0);
	COMPUTING_DATA(0);

	PREPARE_DATA_INTRA(lo, 1);
	TRANSPOSE(d40, d50, d60, d70, r01, r11, r21, r31);
	TRANSPOSE(d00, d10, d20, d30, r00, r10, r20, r30);
	COMPUTING_DATA(1);

	TRANSPOSE(d01, d11, d21, d31, r40, r50, r60, r70);

/*
 * second stage
 */

	PREPARE_DATA_INTER(0);
	COMPUTING_DATA(0);

	ENDSCALE(0);
	TRANSPOSE(d41, d51, d61, d71, r41, r51, r61, r71);

	dp = (mlib_d64 *)vis_alignaddr(coeffs, -1);
	mask = 0xFF >> ((mlib_addr)coeffs - (mlib_addr)dp);
	vis_alignaddrl((void *)coeffs, 0);

	PREPARE_DATA_INTER(1);
	COMPUTING_DATA(1);

	ENDSCALE(1);

	Quant_ST_NA(d00, d00, qtable[0]);
	Quant_ST_NA(d01, d01, qtable[1]);
	Quant_ST_NA(d10, d10, qtable[2]);
	Quant_ST_NA(d11, d11, qtable[3]);
	Quant_ST_NA(d20, d20, qtable[4]);
	Quant_ST_NA(d21, d21, qtable[5]);
	Quant_ST_NA(d30, d30, qtable[6]);
	Quant_ST_NA(d31, d31, qtable[7]);
	Quant_ST_NA(d40, d40, qtable[8]);
	Quant_ST_NA(d41, d41, qtable[9]);
	Quant_ST_NA(d50, d50, qtable[10]);
	Quant_ST_NA(d51, d51, qtable[11]);
	Quant_ST_NA(d60, d60, qtable[12]);
	Quant_ST_NA(d61, d61, qtable[13]);
	Quant_ST_NA(d70, d70, qtable[14]);
	Quant_ST_NA(d71, d71, qtable[15]);

	dp[1] = vis_faligndata(d00, d01);
	dp[2] = vis_faligndata(d01, d10);
	dp[3] = vis_faligndata(d10, d11);
	dp[4] = vis_faligndata(d11, d20);
	dp[5] = vis_faligndata(d20, d21);
	dp[6] = vis_faligndata(d21, d30);
	dp[7] = vis_faligndata(d30, d31);
	dp[8] = vis_faligndata(d31, d40);
	dp[9] = vis_faligndata(d40, d41);
	dp[10] = vis_faligndata(d41, d50);
	dp[11] = vis_faligndata(d50, d51);
	dp[12] = vis_faligndata(d51, d60);
	dp[13] = vis_faligndata(d60, d61);
	dp[14] = vis_faligndata(d61, d70);
	dp[15] = vis_faligndata(d70, d71);
	vis_pst_8(vis_faligndata(d71, d71), dp + 16, ~mask);
	if ((mlib_addr)coeffs & 7)
		vis_pst_8(vis_faligndata(d00, d00), dp, mask);
	return (MLIB_SUCCESS);
}

mlib_status
__mlib_VideoDCT8x8Quantize_S16_S16_B12(
	mlib_s16 coeffs[64],
	const mlib_s16 *block,
	const mlib_d64 qtable[64])
{
	mlib_d64 *sp = (mlib_d64 *)block;
	mlib_d64 *dp = (mlib_d64 *)coeffs;

	mlib_d64 d00, d10, d20, d30, d40, d50, d60, d70;
	mlib_d64 d01, d11, d21, d31, d41, d51, d61, d71;
	mlib_d64 t00, t10, t20, t30, t40, t50, t60, t70, t80, t90;
	mlib_d64 t01, t11, t21, t31, t41, t51, t61, t71, t81, t91;
	mlib_d64 r00, r10, r20, r30, r40, r50, r60, r70;
	mlib_d64 r01, r11, r21, r31, r41, r51, r61, r71;
	mlib_f32 FCOS, c17, c26, c35, c_4;

	vis_write_gsr(1 << 3);
/*
 * first stage
 */

	LOAD_DATA_AA_INTER1;
	TRANSPOSE(d00, d20, d40, d60, r00, r10, r20, r30);
	TRANSPOSE(d10, d30, d50, d70, r40, r50, r60, r70);
	LOADCONSTS4_12;

	PREPARE_DATA_INTER(0);

	LOAD_DATA_AA_INTER2;
	TRANSPOSE(d01, d21, d41, d61, r01, r11, r21, r31);

	COMPUTING_DATA(0);

	TRANSPOSE(d11, d31, d51, d71, r41, r51, r61, r71);
	PREPARE_DATA_INTER(1);
	COMPUTING_DATA(1);

/*
 * second stage
 */

	TRANSPOSE(d01, d11, d21, d31, r40, r50, r60, r70);
	TRANSPOSE(d00, d10, d20, d30, r00, r10, r20, r30);
	PREPARE_DATA_INTER(0);
	TRANSPOSE(d40, d50, d60, d70, r01, r11, r21, r31);
	COMPUTING_DATA_12(0);

	TRANSPOSE(d41, d51, d61, d71, r41, r51, r61, r71);
	ENDSCALE_12(0);

	Quant_ST(0, d00, qtable[0]);
	Quant_ST(2, d10, qtable[2]);
	Quant_ST(4, d20, qtable[4]);
	Quant_ST(6, d30, qtable[6]);
	Quant_ST(8, d40, qtable[8]);
	Quant_ST(10, d50, qtable[10]);
	Quant_ST(12, d60, qtable[12]);
	Quant_ST(14, d70, qtable[14]);


	PREPARE_DATA_INTER(1);
	COMPUTING_DATA_12(1);

	ENDSCALE_12(1);

	Quant_ST(1, d01, qtable[1]);
	Quant_ST(3, d11, qtable[3]);
	Quant_ST(5, d21, qtable[5]);
	Quant_ST(7, d31, qtable[7]);
	Quant_ST(9, d41, qtable[9]);
	Quant_ST(11, d51, qtable[11]);
	Quant_ST(13, d61, qtable[13]);
	Quant_ST(15, d71, qtable[15]);

	return (MLIB_SUCCESS);
}

mlib_status
__mlib_VideoDCT8x8Quantize_S16_S16_B12_NA(
	mlib_s16 coeffs[64],
	const mlib_s16 *block,
	const mlib_d64 qtable[64])
{
	mlib_d64 *sp = (mlib_d64 *)block;
	mlib_d64 *dp = (mlib_d64 *)coeffs;

	mlib_d64 d00, d10, d20, d30, d40, d50, d60, d70;
	mlib_d64 d01, d11, d21, d31, d41, d51, d61, d71;
	mlib_d64 t00, t10, t20, t30, t40, t50, t60, t70, t80, t90;
	mlib_d64 t01, t11, t21, t31, t41, t51, t61, t71, t81, t91;
	mlib_d64 r00, r10, r20, r30, r40, r50, r60, r70;
	mlib_d64 r01, r11, r21, r31, r41, r51, r61, r71;
	mlib_f32 FCOS, c17, c26, c35, c_4;
	mlib_s32 mask;
	mlib_d64 w_const = vis_to_double_dup(0x4000);

	if (block == NULL || coeffs == NULL)
		return (MLIB_FAILURE);

	if (!(((mlib_addr)block | (mlib_addr)coeffs) & 7)) {
		return (__mlib_VideoDCT8x8Quantize_S16_S16_B12(coeffs,
					block, qtable));
	}

	vis_write_gsr(1 << 3);
/*
 * first stage
 */

	LOAD_DATA_GE_INTER1;

	TRANSPOSE(d00, d20, d40, d60, r00, r10, r20, r30);
	TRANSPOSE(d10, d30, d50, d70, r40, r50, r60, r70);
	LOADCONSTS4_12;

	PREPARE_DATA_INTER(0);

	LOAD_DATA_GE_INTER2;
	TRANSPOSE(d01, d21, d41, d61, r01, r11, r21, r31);

	COMPUTING_DATA(0);

	TRANSPOSE(d11, d31, d51, d71, r41, r51, r61, r71);
	PREPARE_DATA_INTER(1);
	COMPUTING_DATA(1);

/*
 * second stage
 */


	TRANSPOSE(d01, d11, d21, d31, r40, r50, r60, r70);
	TRANSPOSE(d00, d10, d20, d30, r00, r10, r20, r30);
	PREPARE_DATA_INTER(0);
	TRANSPOSE(d40, d50, d60, d70, r01, r11, r21, r31);
	COMPUTING_DATA_12(0);

	TRANSPOSE(d41, d51, d61, d71, r41, r51, r61, r71);
	ENDSCALE_12(0);


	dp = (mlib_d64 *)vis_alignaddr(coeffs, -1);
	mask = 0xFF >> ((mlib_addr)coeffs - (mlib_addr)dp);
	vis_alignaddrl((void *)coeffs, 0);

	PREPARE_DATA_INTER(1);
	COMPUTING_DATA_12(1);

	ENDSCALE_12(1);

	Quant_ST_NA(d00, d00, qtable[0]);
	Quant_ST_NA(d01, d01, qtable[1]);
	Quant_ST_NA(d10, d10, qtable[2]);
	Quant_ST_NA(d11, d11, qtable[3]);
	Quant_ST_NA(d20, d20, qtable[4]);
	Quant_ST_NA(d21, d21, qtable[5]);
	Quant_ST_NA(d30, d30, qtable[6]);
	Quant_ST_NA(d31, d31, qtable[7]);
	Quant_ST_NA(d40, d40, qtable[8]);
	Quant_ST_NA(d41, d41, qtable[9]);
	Quant_ST_NA(d50, d50, qtable[10]);
	Quant_ST_NA(d51, d51, qtable[11]);
	Quant_ST_NA(d60, d60, qtable[12]);
	Quant_ST_NA(d61, d61, qtable[13]);
	Quant_ST_NA(d70, d70, qtable[14]);
	Quant_ST_NA(d71, d71, qtable[15]);

	dp[1] = vis_faligndata(d00, d01);
	dp[2] = vis_faligndata(d01, d10);
	dp[3] = vis_faligndata(d10, d11);
	dp[4] = vis_faligndata(d11, d20);
	dp[5] = vis_faligndata(d20, d21);
	dp[6] = vis_faligndata(d21, d30);
	dp[7] = vis_faligndata(d30, d31);
	dp[8] = vis_faligndata(d31, d40);
	dp[9] = vis_faligndata(d40, d41);
	dp[10] = vis_faligndata(d41, d50);
	dp[11] = vis_faligndata(d50, d51);
	dp[12] = vis_faligndata(d51, d60);
	dp[13] = vis_faligndata(d60, d61);
	dp[14] = vis_faligndata(d61, d70);
	dp[15] = vis_faligndata(d70, d71);
	vis_pst_8(vis_faligndata(d71, d71), dp + 16, ~mask);

	if ((mlib_addr)coeffs & 7)
		vis_pst_8(vis_faligndata(d00, d00), dp, mask);

	return (MLIB_SUCCESS);
}
