/*
 * Project Name MFC DRIVER IN WINCE
 * Copyright  2009 Samsung Electronics Co, Ltd. All Rights Reserved.
 *
 * This file implements MFC driver.
 *
 * @name MFC DRIVER Module (MfcEncAPI.c)
 * @author Jiyoung Shin (idon.shin@samsung.com)
 * @date 2009/08/05
 */

#include <windows.h>
#include "MfcInterface.h"
#include "CMMAPI.h"
#include "MfcLogMsg.h"
#include "SsbSipMfcApi.h"

#define _MFCLIB_MAGIC_NUMBER		0x92241001



void *SsbSipMfcEncOpen()
{
	_MFCLIB			*pCTX;
	HANDLE			hMFCOpen, hCMMOpen;

	hMFCOpen = CreateFile(L"MFC1:",
					   GENERIC_READ|GENERIC_WRITE,
					   0,
					   NULL,
					   OPEN_EXISTING,
					   FILE_ATTRIBUTE_NORMAL,
					   NULL);
	if (hMFCOpen == INVALID_HANDLE_VALUE)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncOpen", "MFC Open failure\n");
		return NULL;
	}

	pCTX = (_MFCLIB *) malloc(sizeof(_MFCLIB));
	if (pCTX == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncOpen", "malloc failed.\n");
		CloseHandle(hMFCOpen);
		return NULL;
	}

	//CMM init
	hCMMOpen = CreateFile(L"CMM1:",
					   GENERIC_READ|GENERIC_WRITE,
					   0,
					   NULL,
					   OPEN_EXISTING,
					   FILE_ATTRIBUTE_NORMAL,
					   NULL);
	
	if (hCMMOpen == INVALID_HANDLE_VALUE)
	{
		if(pCTX)
			free(pCTX);

		LOG_MSG(LOG_ERROR, "SsbSipMfcEncOpen", "CMM Open failure\n");
		return NULL;
	}
	
	memset(pCTX, 0, sizeof(_MFCLIB));

	pCTX->hMFC   = hMFCOpen;
	pCTX->hCMM   = hCMMOpen;
	pCTX->proc_id = GetCurrentProcessId();

	return (void *) pCTX;
}

SSBSIP_MFC_ERROR_CODE SsbSipMfcEncInit(void *openHandle, void *param)
{
	MFC_ARGS			EncArg;
	_MFCLIB 			*pCTX;
	int 				portA_dpb_size, portB_dpb_size;
	int					YSize, CbCrSize;
	CMM_ALLOC_PRAM_T	CMMParam;
	char				*virBuf;
	int 				phyBuf;
	BOOL				r;
	SSBSIP_MFC_ENC_MPEG4_PARAM *mpeg4_arg;
	SSBSIP_MFC_ENC_H263_PARAM *h263_arg;
	SSBSIP_MFC_ENC_H264_PARAM *h264_arg;
	MFC_ENC_INIT_H264_ARG *tmpArg;

	pCTX  = (_MFCLIB *) openHandle;
	memset(&EncArg, 0, sizeof(MFC_ARGS));

	h264_arg = (SSBSIP_MFC_ENC_H264_PARAM *)param;
	switch(h264_arg->codecType)
	{
		case MPEG4_ENC:
			{
				mpeg4_arg = (SSBSIP_MFC_ENC_MPEG4_PARAM *)param;
				memcpy(&EncArg.args.enc_init_mpeg4.in_cfg, mpeg4_arg, sizeof(SSBSIP_MFC_ENC_MPEG4_PARAM));
				pCTX->width = mpeg4_arg->SourceWidth;
				pCTX->height = mpeg4_arg->SourceHeight;
				break;
			}
		case H263_ENC:
			{
				h263_arg = (SSBSIP_MFC_ENC_H263_PARAM *)param;
				memcpy(&EncArg.args.enc_init_h263.in_cfg, h263_arg, sizeof(SSBSIP_MFC_ENC_H263_PARAM));
				pCTX->width = h263_arg->SourceWidth;
				pCTX->height = h263_arg->SourceHeight;
				break;
			}

		
		case H264_ENC:
			{
				h264_arg = (SSBSIP_MFC_ENC_H264_PARAM *)param;
				memcpy(&EncArg.args.enc_init_h264.in_cfg, h264_arg, sizeof(SSBSIP_MFC_ENC_H264_PARAM));
				pCTX->width = h264_arg->SourceWidth;
				pCTX->height = h264_arg->SourceHeight;
				break;
			}

		default:
            {
                LOG_MSG(LOG_ERROR, "SsbSipMfcEncInit", "Undefined codec type.(%d)\n", h264_arg->codecType);
                return MFC_RET_ENC_INIT_FAIL;
            }
	}

	pCTX->codec_type = h264_arg->codecType;
	YSize = Align(Align(pCTX->width, 128)*Align(pCTX->height, 32), 64*1024);
	CbCrSize = Align((Align(pCTX->width, 128)*Align(pCTX->height, 32))>>1, 64*1024);

	// stream buffer + Ref(Y0, Y1, B_Y0, B_C0)
	portA_dpb_size = MAX_ENCODER_OUTPUT_BUFFER_SIZE + YSize*3 +  CbCrSize;
	CMMParam.size = (unsigned int)(portA_dpb_size);
	CMMParam.dramLocation = DRAM_0;       // DRAM 0 or DRAM 1 = 0;
	printf("Allocation DPB_A : 0x%08x\n", portA_dpb_size);
	
	r = DeviceIoControl(pCTX->hCMM, IOCTL_CODEC_MEM_ALLOC, (PBYTE)&CMMParam, 
							sizeof(CMM_ALLOC_PRAM_T *), &virBuf, sizeof(virBuf), 
							NULL, NULL);
	if(r == FALSE)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncInit", "IOCTL_CODEC_MEM_ALLOC failed\n");
		return MFC_RET_ENC_INIT_FAIL;
	}

	r = DeviceIoControl(pCTX->hCMM, IOCTL_CODEC_GET_PHY_ADDR, (PBYTE)virBuf, 
							sizeof(virBuf), &phyBuf, sizeof(phyBuf), 
							NULL, NULL);
	if(r == FALSE)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncInit", "IOCTL_CODEC_GET_PHY_ADDR failed\n");
		return MFC_RET_ENC_INIT_FAIL;
	}

	pCTX->phyStrmBuf = (int)phyBuf;
	pCTX->virStrmBuf = (int)virBuf;
	pCTX->sizeStrmBuf = MAX_ENCODER_OUTPUT_BUFFER_SIZE;

	pCTX->phyEncDpbABuf = (int)(phyBuf + MAX_ENCODER_OUTPUT_BUFFER_SIZE);
	pCTX->virEncDpbABuf = (int)(virBuf + MAX_ENCODER_OUTPUT_BUFFER_SIZE);

	//Ref(C0, C1, YC2, YC3), Intra_pred buffer
	portB_dpb_size = 2*CbCrSize + 2*(YSize+CbCrSize) + 64*1024;
	CMMParam.size = (unsigned int)(portB_dpb_size);
	CMMParam.dramLocation = DRAM_1;       // DRAM 0 or DRAM 1 = 0;
	printf("Allocation DPB_B : 0x%08x\n", portB_dpb_size);

	r = DeviceIoControl(pCTX->hCMM, IOCTL_CODEC_MEM_ALLOC, (PBYTE)&CMMParam, 
							sizeof(CMM_ALLOC_PRAM_T *), &virBuf, sizeof(virBuf), 
							NULL, NULL);
	if(r == FALSE)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncInit", "IOCTL_CODEC_MEM_ALLOC failed\n");
		return MFC_RET_ENC_INIT_FAIL;
	}

	r = DeviceIoControl(pCTX->hCMM, IOCTL_CODEC_GET_PHY_ADDR, (PBYTE)virBuf, 
							sizeof(virBuf), &phyBuf, sizeof(phyBuf), 
							NULL, NULL);
	if(r == FALSE)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncInit", "IOCTL_CODEC_GET_PHY_ADDR failed\n");
		return MFC_RET_ENC_INIT_FAIL;
	}

	pCTX->phyEncDpbBBuf = (int)phyBuf;
	pCTX->virEncDpbBBuf = (int)virBuf;

    pCTX->encOutInfo.StrmPhyAddr = (void *)pCTX->phyStrmBuf;
    pCTX->encOutInfo.StrmVirAddr = (void *)pCTX->virStrmBuf;
    
	tmpArg = (MFC_ENC_INIT_H264_ARG *)&EncArg.args;
	tmpArg->in_dpb_portA_addr = pCTX->phyEncDpbABuf;
	tmpArg->in_dpb_portB_addr = pCTX->phyEncDpbBBuf;
    tmpArg->in_Strm_St = pCTX->phyStrmBuf;
    tmpArg->in_Strm_End = pCTX->phyStrmBuf + pCTX->sizeStrmBuf;
	printf("phyEnc StreamOutBuf : 0x%08x Vir StreamOutBuf : 0x%08x\n", pCTX->phyStrmBuf, pCTX->virStrmBuf);
	printf("phyEncDpbABuf : 0x%08x phyEncDpbBBuf : 0x%08x\n", pCTX->phyEncDpbABuf, pCTX->phyEncDpbBBuf);

	r = DeviceIoControl(pCTX->hMFC, IOCTL_MFC_ENC_INIT, &EncArg, sizeof(MFC_ARGS),
							NULL, 0,
							NULL,
							NULL);

	if (EncArg.ret_code != MFC_RET_OK)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncInit", "IOCTL_MFC_ENC_INIT failed(ret : %d)\n", EncArg.ret_code);
		return EncArg.ret_code;
	}

	pCTX->encOutInfo.headerSize = tmpArg->out_header_size;
	return EncArg.ret_code;
}

SSBSIP_MFC_ERROR_CODE SsbSipMfcEncExe(void *openHandle)
{
	_MFCLIB 			*pCTX;
	MFC_ARGS			EncArg;
	BOOL				r;

	if (openHandle == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecExe", "openHandle is NULL\n");
		return MFC_RET_ENC_EXE_ERR;
	}
	
	pCTX  = (_MFCLIB *) openHandle;

	memset(&EncArg, 0x00, sizeof(MFC_ARGS));
	
	EncArg.args.enc_exe.in_codecType			   = pCTX->codec_type;
 	EncArg.args.enc_exe.in_Y_addr				   = (unsigned int)pCTX->phyYFrmBuf;
	EncArg.args.enc_exe.in_CbCr_addr			   = (unsigned int)pCTX->phyCbFrmBuf;
    EncArg.args.enc_exe.in_Strm_St				   = (unsigned int)pCTX->phyStrmBuf;		
    EncArg.args.enc_exe.in_Strm_End				   = (unsigned int)pCTX->phyStrmBuf + pCTX->sizeStrmBuf;
	r = DeviceIoControl(pCTX->hMFC, IOCTL_MFC_ENC_EXE,
						&EncArg, sizeof(MFC_ARGS),
						NULL, 0,
						NULL,
						NULL);

	if (EncArg.ret_code != MFC_RET_OK)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecExe", "IOCTL_MFC_ENC_EXE failed(ret : %d)\n", EncArg.ret_code);
		return EncArg.ret_code;
	}

    pCTX->encOutInfo.dataSize = EncArg.args.enc_exe.out_encoded_size;
    pCTX->encOutInfo.frameType = EncArg.args.enc_exe.out_frameType;
    pCTX->encOutInfo.headerSize = EncArg.args.enc_exe.out_header_size;

	return EncArg.ret_code;
}

SSBSIP_MFC_ERROR_CODE SsbSipMfcEncClose(void *openHandle)
{
	_MFCLIB  *pCTX;

	if (openHandle == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecClose", "openHandle is NULL\n");
		return MFC_RET_CLOSE_FAIL;
	}

	pCTX  = (_MFCLIB *) openHandle;

	CloseHandle(pCTX->hMFC);
	CloseHandle(pCTX->hCMM);
	free(pCTX);

	return MFC_RET_OK;
}

SSBSIP_MFC_ERROR_CODE SsbSipMfcEncGetInBuf(void *openHandle, SSBSIP_MFC_ENC_INPUT_INFO *inbuf)
{
	_MFCLIB 			*pCTX;
	CMM_ALLOC_PRAM_T	CMMParam;
	BOOL				r;
	char				*virBuf;
	int 				phyBuf;
	int					YSize, CbCrSize;
	
	if (openHandle == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcDecClose", "openHandle is NULL\n");
		return MFC_RET_ENC_GET_INBUF_FAIL;
	}

	pCTX  = (_MFCLIB *) openHandle;

	YSize = Align(Align(pCTX->width, 128)*Align(pCTX->height, 32), 64*1024);
	CbCrSize = Align((Align(pCTX->width, 128)*Align(pCTX->height, 32))>>1, 64*1024);
	
	CMMParam.size = (unsigned int)(YSize+CbCrSize);
	CMMParam.dramLocation = DRAM_1;       // DRAM 0 or DRAM 1 = 0;

	r = DeviceIoControl(pCTX->hCMM, IOCTL_CODEC_MEM_ALLOC, (PBYTE)&CMMParam, 
							sizeof(CMM_ALLOC_PRAM_T *), &virBuf, sizeof(virBuf), 
							NULL, NULL);
	if(r == FALSE)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncGetInBuf", "IOCTL_CODEC_MEM_ALLOC failed\n");
		return MFC_RET_ENC_GET_INBUF_FAIL;
	}

	r = DeviceIoControl(pCTX->hCMM, IOCTL_CODEC_GET_PHY_ADDR, (PBYTE)virBuf, 
							sizeof(virBuf), &phyBuf, sizeof(phyBuf), 
							NULL, NULL);
	if(r == FALSE)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncGetInBuf", "IOCTL_CODEC_GET_PHY_ADDR failed\n");
		return MFC_RET_ENC_GET_INBUF_FAIL;
	}


	pCTX->phyYFrmBuf = phyBuf;
	pCTX->phyCbFrmBuf = phyBuf + YSize;
	pCTX->virYFrmBuf = (int)virBuf;
	pCTX->virCbFrmBuf = (int)(virBuf + YSize);
	pCTX->sizeYFrmBuf = YSize;
	pCTX->sizeCbFrmBuf = CbCrSize;
	
	inbuf->YPhyAddr = (void*)pCTX->phyYFrmBuf;
	inbuf->CPhyAddr = (void*)pCTX->phyCbFrmBuf;
	inbuf->YVirAddr = (void*)pCTX->virYFrmBuf;
	inbuf->CVirAddr = (void*)pCTX->virCbFrmBuf;
	inbuf->YSize = (int)(pCTX->sizeYFrmBuf);
	inbuf->CSize = (int)(pCTX->sizeCbFrmBuf);

	return MFC_RET_OK;
}

SSBSIP_MFC_ERROR_CODE SsbSipMfcEncSetInBuf (void *openHandle, SSBSIP_MFC_ENC_INPUT_INFO *inbuf)
{
	_MFCLIB 			*pCTX;

	if (openHandle == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncSetInBuf", "openHandle is NULL\n");
		return MFC_RET_ENC_SET_INBUF_FAIL;
	}

	pCTX  = (_MFCLIB *) openHandle;

	pCTX->phyYFrmBuf = (int)inbuf->YPhyAddr;
	pCTX->phyCbFrmBuf = (int)inbuf->CPhyAddr;
	pCTX->virYFrmBuf = (int)inbuf->YVirAddr;
	pCTX->virCbFrmBuf = (int)inbuf->CVirAddr;
	pCTX->sizeYFrmBuf = (int)inbuf->YSize;
	pCTX->sizeCbFrmBuf = (int)inbuf->CSize;

	return MFC_RET_OK;

}


SSBSIP_MFC_ERROR_CODE SsbSipMfcEncGetOutBuf(void *openHandle, SSBSIP_MFC_ENC_OUTPUT_INFO *output_info)
{
	_MFCLIB 			*pCTX;

	if (openHandle == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncGetOutBuf", "openHandle is NULL\n");
		return MFC_RET_ENC_GET_OUTBUF_FAIL;
	}

	pCTX  = (_MFCLIB *) openHandle;

	if(pCTX->virStrmBuf == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncGetOutBuf", "output buffer is NULL\n");
		return MFC_RET_ENC_GET_OUTBUF_FAIL;
	}
    output_info->dataSize = pCTX->encOutInfo.dataSize;
    output_info->frameType = pCTX->encOutInfo.frameType;
    output_info->headerSize = pCTX->encOutInfo.headerSize;
	output_info->StrmPhyAddr = (void *)pCTX->phyStrmBuf;
	output_info->StrmVirAddr = (void *)pCTX->virStrmBuf;
	return MFC_RET_OK;
}

SSBSIP_MFC_ERROR_CODE SsbSipMfcEncSetOutBuf (void *openHandle, void *phyOutbuf, void *virOutbuf, int outputBufferSize)
{
	_MFCLIB 			*pCTX;

	if (openHandle == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncSetOutBuf", "openHandle is NULL\n");
		return MFC_RET_ENC_SET_OUTBUF_FAIL;
	}

	pCTX  = (_MFCLIB *) openHandle;

	pCTX->phyStrmBuf = (int)phyOutbuf;
	pCTX->virStrmBuf = (int)virOutbuf;
	pCTX->sizeStrmBuf = outputBufferSize;

	return MFC_RET_OK;

}


SSBSIP_MFC_ERROR_CODE SsbSipMfcEncSetConfig(void *openHandle, SSBSIP_MFC_ENC_CONF conf_type, void *value)
{
	_MFCLIB  			*pCTX;
	MFC_ARGS			EncArg;
	BOOL				r;

	if (openHandle == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncSetConfig", "openHandle is NULL\n");
		return MFC_RET_ENC_SET_CONF_FAIL;
	}

	if (value == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncSetConfig", "value is NULL\n");
		return MFC_RET_ENC_SET_CONF_FAIL;
	}
	pCTX  = (_MFCLIB *) openHandle;
	memset(&EncArg, 0x00, sizeof(MFC_ARGS));

	EncArg.args.set_config.in_config_param = conf_type;
	EncArg.args.set_config.in_config_value[0]  = *((unsigned int *) value);
	EncArg.args.set_config.in_config_value[1]  = 0;

	r = DeviceIoControl(pCTX->hMFC, IOCTL_MFC_SET_CONFIG,
							&EncArg, sizeof(MFC_ARGS),
							NULL, 0,
							NULL,
							NULL);

	if (EncArg.ret_code != MFC_RET_OK)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncSetConfig", "IOCTL_MFC_SET_CONFIG failed(ret : %d)\n", EncArg.ret_code);
		return EncArg.ret_code;
	}
	return EncArg.ret_code;
}


SSBSIP_MFC_ERROR_CODE SsbSipMfcEncGetConfig(void *openHandle, SSBSIP_MFC_ENC_CONF conf_type, void *value)
{
	_MFCLIB 			*pCTX;
	MFC_ARGS			EncArg;
	
	if (openHandle == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncGetConfig", "openHandle is NULL\n");
		return MFC_RET_ENC_GET_CONF_FAIL;
	}
	if (value == NULL)
	{
		LOG_MSG(LOG_ERROR, "SsbSipMfcEncGetConfig", "value is NULL\n");
		return MFC_RET_ENC_GET_CONF_FAIL;
	}
	pCTX  = (_MFCLIB *) openHandle;

	switch (conf_type)
	{
		
	default:

		LOG_MSG(LOG_ERROR, "SsbSipMfcEncGetConfig", "No such conf_type is supported.\n");
		return MFC_RET_ENC_GET_CONF_FAIL;
	}

	return MFC_RET_OK;
}


