/* SPDX-License-Identifier: LGPL-2.1-only OR BSD-3-Clause */
/******************************************************************************
 *
 * This file is provided under a dual license.  When you use or
 * distribute this software, you may choose to be licensed under
 * version 2.1 of the GNU Lesser General Public License
 * ("LGPLv2.1 License") or BSD License.
 *
 * LGPLv2.1 License
 *
 * Copyright(C) 2019 MediaTek Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2.1 of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * 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 https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html for more
 * details.
 *
 * BSD LICENSE
 *
 * Copyright(C) 2019 MediaTek Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *****************************************************************************/


#if defined(LINUX_TURNKEY_SOLUTION) && defined(CC_ENABLE_AOMX)

#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include "x_os.h"
#include "x_assert.h"
#include "x_printf.h"
#include "aud_if.h"
#include "aud_omx.h"

#ifndef SYS_Printf
//#define SYS_Printf Printf
#define SYS_Printf(fmt...)
#endif

extern INT32 _CB_PutEvent(CB_FCT_ID_T eFctId, INT32 i4TagSize, void *pvTag);

static long _i4AdecIoctl(struct file *filp, unsigned int cmd, unsigned long param);
static int _i4AdecOpen(struct inode *inode, struct file *file);
static int _i4AdecRelease(struct inode *inode, struct file *file);

static int _adec_opencnt = 0;

struct file_operations aomx_fops =
{
    .owner          = THIS_MODULE,
    .open           = _i4AdecOpen,
    .release        = _i4AdecRelease,
    .unlocked_ioctl = _i4AdecIoctl,
};

static void _k_adec_omx_event_nfy(VOID* u4Info, VOID* u4Tag)
{
    DEC_OMX_CB_EVENT_T rEvent;
    rEvent.u4Info = *((PARAMETER_OMX_CB_EVENTHANDLER*)u4Info);;
    rEvent.u4Tag = (UINT32)u4Tag;
    _CB_PutEvent(CB_MTAL_ADEC_OMX_EVENT_MESSAGE, sizeof(DEC_OMX_CB_EVENT_T), (void*)&rEvent);
}

static void _k_adec_omx_empty_buffer_done_nfy(VOID* u4Info, VOID* u4Tag)
{
    DEC_OMX_CB_BUF_T rEmptyBufDone;
    rEmptyBufDone.u4Info = *((PARAMETER_OMX_CB_BUFFER*)u4Info);
    rEmptyBufDone.u4Tag = (UINT32)u4Tag;
    _CB_PutEvent(CB_MTAL_ADEC_OMX_EMPTYBUFFERDONE_MESSAGE, sizeof(DEC_OMX_CB_BUF_T), (void*)&rEmptyBufDone);
}

static void _k_adec_omx_fill_buffer_done_nfy(VOID* u4Info, VOID* u4Tag)
{
    DEC_OMX_CB_BUF_T rFillBufDone;
    rFillBufDone.u4Info = *((PARAMETER_OMX_CB_BUFFER*)u4Info);
    rFillBufDone.u4Tag = (UINT32)u4Tag;
    _CB_PutEvent(CB_MTAL_ADEC_OMX_FILLBUFFERDONE_MESSAGE, sizeof(DEC_OMX_CB_BUF_T), (void*)&rFillBufDone);
}

static long _i4AdecIoctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    INT32 i4Ret = 0;

    SYS_Printf("[AUD][OMX] _i4AdecIoctl: cmd = 0x%X/%X.\n", cmd, OMX_PARAM_IndexParamAudioAmr);

    switch (cmd)
    {
    case OMX_GETHANDLE:
        // return within 20ms
        {
            //Create instance
            PARAMETER_OMX_CB rOmxCb;
            rOmxCb.pEventHandler = _k_adec_omx_event_nfy;
            rOmxCb.pEmptyBufferDone = _k_adec_omx_empty_buffer_done_nfy;
            rOmxCb.pFillBufferDone = _k_adec_omx_fill_buffer_done_nfy;
            SYS_Printf("[AUD][OMX] OMX_GETHANDLE.\n");            
            i4Ret = i4AudOmxCompGetHandle((PARAMETER_OMX_GETHANDLE*)arg, &rOmxCb);
        }
        break;
    case OMX_FREEHANDLE:
        // return within 20ms
        SYS_Printf("[AUD][OMX] OMX_FREEHANDLE.\n");
        i4Ret = i4AudOmxCompFreeHandle((PARAMETER_OMX_FREEHANDLE *)arg);
        break;
    case OMX_ALLOCATEBUFFER:
        // blocking, return within 5ms
        SYS_Printf("[AUD][OMX] OMX_ALLOCATEBUFFER.\n");
        i4Ret = i4AudOmxCompAllocateBuffer((PARAMETER_OMX_ALLOCATEBUFFER *)arg);
        break;
    case OMX_FREEBUFFER:
        // blocking, return within 20ms
        SYS_Printf("[AUD][OMX] OMX_FREEBUFFER.\n");
        i4Ret = i4AudOmxCompFreeBuffer((PARAMETER_OMX_FREEBUFFER *)arg);
        break;
    case OMX_EMPTYTHISBUFFER:
        // non-blocking
        SYS_Printf("[AUD][OMX] OMX_EMPTYTHISBUFFER.\n");
        i4Ret = i4AudOmxCompEmptyBuffer((PARAMETER_OMX_EMPTYTHISBUFFER *)arg);
        break;
    case OMX_FILLTHISBUFFER:
        // non-blocking
        SYS_Printf("[AUD][OMX] OMX_FILLTHISBUFFER.\n");
        i4Ret = i4AudOmxCompFillBuffer((PARAMETER_OMX_FILLTHISBUFFER *)arg);
        break;
    case OMX_GETSTATE:
        // return within 5ms
        SYS_Printf("[AUD][OMX] OMX_GETSTATE.\n");
        i4Ret = i4AudOmxCompGetState((PARAMETER_OMX_GETSTATE *)arg);
        break;
    case OMX_COMMAND_CommandStateSet:
        // non-blocking
        SYS_Printf("[AUD][OMX] OMX_COMMAND_CommandStateSet.\n");
        i4Ret = i4AudOmxCompSetStateAsync((PARAMETER_OMX_COMMAND_STATESET *)arg);
        break;
    case OMX_COMMAND_CommandFlush:
        // non-blocking
        SYS_Printf("[AUD][OMX] OMX_COMMAND_CommandFlush.\n");
        i4Ret = i4AudOmxCompFlushAsync((PARAMETER_OMX_COMMAND_PORT *)arg);
        break;
    case OMX_COMMAND_CommandPortDisable:
        // non-blocking
        SYS_Printf("[AUD][OMX] OMX_COMMAND_CommandPortDisable.\n");
        i4AudOmxCompPortDisableAsync((PARAMETER_OMX_COMMAND_PORT *)arg);
        break;
    case OMX_COMMAND_CommandPortEnable:
        // non-blocking
        SYS_Printf("[AUD][OMX] OMX_COMMAND_CommandPortEnable.\n");
        i4AudOmxCompPortEnableAsync((PARAMETER_OMX_COMMAND_PORT *)arg);
        break;
    case OMX_PARAM_IndexParamAudioInit:
        // blocking, return within 20ms (for both set and get)
        SYS_Printf("[AUD][OMX] OMX_PARAM_IndexParamAudioInit.\n");
        i4Ret = i4AudOmxCompGetParamAudioInit((PARAMETER_OMX_PORT_PARAM_TYPE *)arg);
        break;
    case OMX_PARAM_IndexParamPortDefinition:
        // blocking, return within 20ms (for both set and get)
        SYS_Printf("[AUD][OMX] OMX_PARAM_IndexParamPortDefinition.\n");
        i4Ret = i4AudOmxCompParamPortDef((PARAMETER_OMX_PARAM_PORTDEFINITIONTYPE *)arg);
        break;
    case OMX_PARAM_IndexParamAudioPcm:
        // blocking, return within 20ms (for both set and get)
        // currently not supported
        SYS_Printf("[AUD][OMX] OMX_PARAM_IndexParamAudioPcm.\n");
        return i4AudOmxCompParamPCM((PARAMETER_OMX_AUDIO_PARAM_PCMMODETYPE*)arg);
        break;
    case OMX_PARAM_IndexParamAudioAac:
        // blocking, return within 20ms (for both set and get)
        SYS_Printf("[AUD][OMX] OMX_PARAM_IndexParamAudioAac.\n");
        i4Ret = i4AudOmxCompParamAAC((PARAMETER_OMX_AUDIO_PARAM_AACPROFILETYPE *)arg);
        break;
    case OMX_PARAM_IndexParamAudioMp3:
        // blocking, return within 20ms (for both set and get)
        SYS_Printf("[AUD][OMX] OMX_PARAM_IndexParamAudioMp3.\n");
        i4Ret = i4AudOmxCompParamMP3((PARAMETER_OMX_AUDIO_PARAM_MP3TYPE *)arg);
        break;
    case OMX_PARAM_IndexParamAudioWma:
        // blocking, return within 20ms (for both set and get)
        SYS_Printf("[AUD][OMX] OMX_PARAM_IndexParamAudioWma.\n");
        i4Ret = i4AudOmxCompParamWMA((PARAMETER_OMX_AUDIO_PARAM_WMATYPE *)arg);
        break;
    case OMX_PARAM_IndexParamAudioWmaPro:
        // blocking, return within 20ms (for both set and get)
        SYS_Printf("[AUD][OMX] OMX_PARAM_IndexParamAudioWmaPro.\n");
        i4Ret = i4AudOmxCompParamWMAPRO((PARAMETER_OMX_AUDIO_PARAM_WMAPROTYPE *)arg);
        break;
    case OMX_PARAM_IndexParamAudioRa:
        // blocking, return within 20ms (for both set and get)
        SYS_Printf("[AUD][OMX] OMX_PARAM_IndexParamAudioRa.\n");
        i4Ret = i4AudOmxCompParamRA((PARAMETER_OMX_AUDIO_PARAM_RATYPE *)arg);
        break;
    case OMX_PARAM_IndexParamAudioAdpcm:
        // blocking, return within 20ms (for both set and get)
        SYS_Printf("[AUD][OMX] OMX_PARAM_IndexParamAudioAdpcm.\n");
        i4Ret = i4AudOmxCompParamADPCM((PARAMETER_OMX_AUDIO_PARAM_ADPCMTYPE *)arg);
        break;
    case OMX_PARAM_IndexParamAudioVorbis:
        // blocking, return within 20ms (for both set and get)
        SYS_Printf("[AUD][OMX] OMX_PARAM_IndexParamAudioVorbis.\n");
        i4Ret = i4AudOmxCompParamVORBIS((PARAMETER_OMX_AUDIO_PARAM_VORBISTYPE *)arg);
        break;
    case OMX_PARAM_IndexParamAudioAmr:
        // blocking, return within 20ms (for both set and get)
        SYS_Printf("[AUD][OMX] OMX_PARAM_IndexParamAudioAmr.\n");
        i4Ret = i4AudOmxCompParamAMR((PARAMETER_OMX_AUDIO_PARAM_AMRTYPE *)arg);
        break;
    case OMX_PARAM_IndexConfigAudioVolume:
        // blocking, return within 20ms (for both set and get)
        // currently not supported
        SYS_Printf("[AUD][OMX] OMX_PARAM_IndexConfigAudioVolume not implemented.\n");
        break;
    case OMX_PARAM_IndexParamAudioDecodeSize:
        i4Ret = i4AudOmxCompParamDecodeSize((PARAMETER_OMX_AUDIO_PARAM_DECODESIZE *)arg);
        break;
    default:
        SYS_Printf("[AUD][OMX]%s, UNKNOWN COMMAND\n",__FUNCTION__);
        return -1;
    }

    return i4Ret;
}

static int _i4AdecOpen(struct inode *inode, struct file *file)
{
    SYS_Printf("[ADEC][OMX] _i4AdecOpen.\n");

    if (_adec_opencnt >= AUD_COMP_MAX)
    {   
        Printf("!!!!!!!! FATAL ERROR: AOMX open number > %d !!!!!!!!\n", _adec_opencnt);
        return -1;
    }

    if (0 == _adec_opencnt)
    {
        AUD_Init();
        _AUD_OutPadEnable(AUD_DEC_MAIN, TRUE);
        _AUD_OutPadEnable(AUD_DEC_AUX, TRUE);
        ADAC_Mute(FALSE);
        i4AudOmxCompInit();
        vAudOmxBufferInit();
        i4AudOmxTaskSemInit();
        i4AudOmxCmdSemInit();		
    }

    _adec_opencnt ++;

    return 0;
}

static int _i4AdecRelease(struct inode *inode, struct file *file)
{
    SYS_Printf("[ADEC][OMX] _i4AdecRelease.\n");

    _adec_opencnt --;

    if (0 == _adec_opencnt)
    {
        i4AudOmxCompUninit();
        i4AudOmxTaskSemUninit();
        i4AudOmxCmdSemUninit();		
        vAudOmxBufferUninit();
    }

    return 0;
}

#endif

