/* 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.
 *
 *****************************************************************************/

/*-----------------------------------------------------------------------------
 * $RCSfile: io_mtadac.c,v $
 * $Revision: #1 $
 *---------------------------------------------------------------------------*/

/** @file io_mtadac.c
 */

//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------

#include "mtadac.h"
#include "aud_if.h"
#include <linux/mtal_ioctl.h>

//-----------------------------------------------------------------------------
// Constant definitions
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Type definitions
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Public functions
//-----------------------------------------------------------------------------
extern void _MT5365_Mute(UINT8 u1ALRx,BOOL fgMute);
extern void _MT5398_MuteALRx(UINT8 u1ALRx,BOOL fgMute);


//-----------------------------------------------------------------------------
// Macro definitions
//-----------------------------------------------------------------------------

#define USR_SPACE_ACCESS_VALIDATE_1ARG(arg)                 \
    if (!access_ok(VERIFY_READ, (void __user *)arg,         \
                   sizeof(INT32)))                            \
    {                                                       \
        printk("mtal_ioctl_adc argument error\n");     \
        return MTR_ERR_INV;                                          \
    }                                                       \

#define COPY_FROM_USER_1ARG(ArgUsr, ArgKernel)              \
    if (copy_from_user(&ArgKernel, (void __user *)ArgUsr,   \
                       sizeof(INT32)))                        \
    {                                                       \
        printk("mtal_ioctl_adc argument error\n");      \
        return MTR_ERR_INV;                                          \
    }                                                       \

#define USR_SPACE_ACCESS_VALIDATE_2ARG(arg)                 \
    if (!access_ok(VERIFY_READ, (void __user *)arg,         \
                   sizeof(MTAL_IOCTL_2ARG_T)))              \
    {                                                       \
        printk("mtal_ioctl_adc argument error\n");     \
        return MTR_ERR_INV;                                          \
    }                                                       \

#define COPY_FROM_USER_2ARG(ArgUsr, ArgKernel)              \
    if (copy_from_user(&ArgKernel, (void __user *)ArgUsr,   \
                       sizeof(MTAL_IOCTL_2ARG_T)))          \
    {                                                       \
        printk("mtal_ioctl_adc argument error\n");      \
        return MTR_ERR_INV;                                          \
    }

#define USR_SPACE_ACCESS_VALIDATE_3ARG(arg)                 \
    if (!access_ok(VERIFY_READ, (void __user *)arg,         \
                   sizeof(MTAL_IOCTL_3ARG_T)))              \
    {                                                       \
        printk("mtal_ioctl_adc argument error\n");     \
        return MTR_ERR_INV;                                          \
    }                                                       \

#define COPY_FROM_USER_3ARG(ArgUsr, ArgKernel)              \
    if (copy_from_user(&ArgKernel, (void __user *)ArgUsr,   \
                       sizeof(MTAL_IOCTL_3ARG_T)))          \
    {                                                       \
        printk("mtal_ioctl_adc argument error\n");      \
        return MTR_ERR_INV;                                          \
    }       
/** Initialize ADAC module.
*@param void
*@retval MTR_OK                    Success.
*@retval MTR_NOT_OK                Fail.
*/
static MT_RESULT_T _MTADAC_Init(void)
{
    static BOOL fgInit = FALSE;

    MTADAC_PRINT(" - void\n");

    if (!fgInit)
    {
        fgInit = TRUE;
        ADAC_Init();
    }

    return MTR_OK;
}

/** Mute ADAC.
*@param eDacId                     Audio dac id
*@param fgMute                     Mute or not.
*@retval MTR_OK                    Success.
*@retval MTR_NOT_OK                Fail.
*/
static MT_RESULT_T _MTADAC_SetMute(unsigned long arg)
{
    MTAL_IOCTL_2ARG_T    rArg;
    MTADAC_DAC_ID_T eDacId;
    UINT8           u1DacId = 0;
    BOOL fgMute;

    USR_SPACE_ACCESS_VALIDATE_2ARG(arg);
    COPY_FROM_USER_2ARG(arg, rArg);

    eDacId = (MTADAC_DAC_ID_T)rArg.ai4Arg[0];
    fgMute = (BOOL)rArg.ai4Arg[1];

    MTADAC_PRINT(" - eDacId = %d, fgMute = %d\n", eDacId, fgMute);

    switch(eDacId)
    {
    case MTADAC_DAC_ID_DAC0:
        u1DacId = 0;
        break;
    case MTADAC_DAC_ID_DAC1:
        u1DacId = 1;
        break;
    case MTADAC_DAC_ID_DAC2:
        u1DacId = 2;
        break;
    case MTADAC_DAC_ID_HP:    
        u1DacId = 3;
        break;
    default:
        u1DacId = 0;
    }
#if (defined(CC_MT5365)||defined(CC_MT5395)||defined(CC_MT5368)||defined(CC_MT5396)||defined(CC_MT5389))
    _MT5365_Mute(u1DacId,fgMute);
#elif (defined(CC_MT5398)||defined(CC_MT5880)|| defined(CC_MT5399))
    _MT5398_MuteALRx(u1DacId,fgMute);
#endif

    return MTR_OK;
}

/** Setup ADAC refer to which channel.
*@param eDacId                     Audio dac id.
*@param eChannel                   Channel number.
*@retval MTR_OK                    Success.
*@retval MTR_NOT_OK                Fail.
*/
MT_RESULT_T _MTADAC_SetDacChannel(unsigned long arg)
{
    MTAL_IOCTL_2ARG_T  rArg;
    MTADAC_DAC_ID_T    eDacId;
    MTAUDDEC_PCM_CH_T  eChannelId;
    AUD_CHL_DEF_T      eChannel;

    USR_SPACE_ACCESS_VALIDATE_2ARG(arg);
    COPY_FROM_USER_2ARG(arg, rArg);

    eDacId = (MTADAC_DAC_ID_T)rArg.ai4Arg[0];
    eChannelId = (MTAUDDEC_PCM_CH_T)rArg.ai4Arg[1];

    MTADAC_PRINT(" - eDacId = %d, eChannelId = %d\n", eDacId, eChannelId);

    switch (eChannelId)
    {
    case MTAUDDEC_PCM_CH_L_R:
        eChannel = AUD_CHL_L_R;
        break;
    case MTAUDDEC_PCM_CH_LS_RS:
        eChannel = AUD_CHL_LS_RS;
        break;
    case MTAUDDEC_PCM_CH_C_SW:
        eChannel = AUD_CHL_C_SW;
        break;
    case MTAUDDEC_PCM_CH_BYPASS_L_R:
        eChannel = AUD_CHL_BYPASS;
        break;
    case MTAUDDEC_PCM_CH_DOWNMIX_L_R:
        eChannel = AUD_CHL_DOWNMIX;
        break;
    case MTAUDDEC_PCM_CH_SPEC_BYPASS_L_R:
        // Suppot multi-pair output(Copy CH9/10 to LS/RS/C/SUB)
        eChannel = AUD_CHL_LS_RS; 
        break;
    case MTAUDDEC_PCM_CH_TV_SCART_L_R:
        eChannel = AUD_CHL_AUX;  // Not support yet
        break;     
    default:
        eChannel = AUD_CHL_L_R;
    }
    
    ADAC_DacSetChl((UINT8)eDacId, eChannel);
        
    return MTR_OK;
}

/** Digital I2S format configuration.
*@param u1DecId                    Decoder Id (AUD_DEC_MAIN or AUD_DEC_AUX).
*@param eFormat                    I2S format.
*@param eMclk                      MCLK frequency
*@retval MTR_OK                    Success.
*@retval MTR_NOT_OK                Fail.
*/
static MT_RESULT_T _MTADAC_SetDacFormatCfg(unsigned long arg)
{
    MTAL_IOCTL_3ARG_T rArg;

    UINT8 u1DecId;
    MTADAC_DATA_FORMAT_T eFormat;
    MTADAC_MCLK_FREQUENCY_T eMclk;

    USR_SPACE_ACCESS_VALIDATE_3ARG(arg);
    COPY_FROM_USER_3ARG(arg, rArg);

    u1DecId = (UINT8)rArg.ai4Arg[0];
    eFormat = (MTADAC_DATA_FORMAT_T)rArg.ai4Arg[1];
    eMclk = (MTADAC_MCLK_FREQUENCY_T)rArg.ai4Arg[2];

    MTADAC_PRINT(" - u1DecId = %d, eFormat = %d, eMclk = %d\n",
        u1DecId,
        eFormat,
        eMclk);

    //add driver implementation code here

    return MTR_OK;
}

/** Mute Speaker.
*@param fgMute                     Mute or not.
*@retval MTR_OK                    Success.
*@retval MTR_NOT_OK                Fail.
*/
static MT_RESULT_T _MTADAC_SetSpeakerMute(unsigned long arg)
{
    BOOL fgMute;

    fgMute = (BOOL)arg;

    MTADAC_PRINT(" - fgMute = %d\n", fgMute);

    ADAC_SpeakerEnable(!fgMute);

    return MTR_OK;
}

/** Set HDMI ARC .
*@param fgMute                     on / off.
*@retval MTR_OK                    Success.
*@retval MTR_NOT_OK                Fail.
*/
static MT_RESULT_T _MTADAC_SetArcEnable(unsigned long arg)
{
    BOOL fgEnable;

    fgEnable = (BOOL)arg;

    MTADAC_PRINT(" - fgEnable = %d\n", fgEnable);
    #ifdef CC_DSP_SUPPORT_ARC
    ADAC_ArcEnable(fgEnable);
    #endif
    return MTR_OK;
}

/** Set AMP register.
*@param fgMute                     Mute or not.
*@retval MTR_OK                    Success.
*@retval MTR_NOT_OK                Fail.
*/
static MT_RESULT_T _MTADAC_SetAMPRegister(unsigned long arg)
{
    MTAL_IOCTL_2ARG_T rArg;
    UINT8 eAddress;
    UINT8 eValue;
    USR_SPACE_ACCESS_VALIDATE_2ARG(arg);
    COPY_FROM_USER_2ARG(arg, rArg);
    eAddress = (UINT8)rArg.ai4Arg[0];
    eValue = (UINT8)rArg.ai4Arg[1];
    
    MTADAC_PRINT(" - eAddress = %d, eValue = %d\n", eAddress, eValue);
    
    ADAC_AmpRegWrite(eAddress,eValue); 
    
    return MTR_OK;

}

static MT_RESULT_T _MTADAC_HwMute(unsigned long arg)
{
    BOOL fgMute;

    fgMute = (BOOL)arg;

    if (!fgMute)
    {
        //Unmute Audio
        ADAC_Enable(TRUE);
        ADAC_Mute(FALSE);
        ADAC_CodecMute(FALSE);

        x_thread_delay(50); // For PWM first power on pop noise.
        ADAC_GpioCodecMute(FALSE);
    }

    return MTR_OK;
}

static MT_RESULT_T _MTADAC_SIFSrcSel(unsigned long arg)
{
    BOOL isUseSif;
    
    isUseSif = (BOOL)arg;

    ADAC_SIFSrcSel(isUseSif); 

    return MTR_OK;
}
int mtal_ioctl_mtadac(struct inode *inode, struct file *file, unsigned int cmd,
              unsigned long arg)
{
    switch (cmd)
    {
        case MTAL_IO_ADAC_INIT:
            return _MTADAC_Init();
        case MTAL_IO_ADAC_SETMUTE:
            return _MTADAC_SetMute(arg);
        case MTAL_IO_ADAC_SETDACFORMATCFG:
            return _MTADAC_SetDacFormatCfg(arg);
        case MTAL_IO_ADAC_SETSPEAKERMUTE:
            return _MTADAC_SetSpeakerMute(arg);
        case MTAL_IO_ADAC_SETDACCHANNEL:
            return _MTADAC_SetDacChannel(arg);
        case MTAL_IO_ADAC_SETAMPREGISTER:
            return _MTADAC_SetAMPRegister(arg);
        case MTAL_IO_ADAC_SETARCENABLE:
            return _MTADAC_SetArcEnable(arg);
        case MTAL_IO_ADAC_HWMUTE:
            return _MTADAC_HwMute(arg);
        case MTAL_IO_ADAC_SIFSEL:
            return _MTADAC_SIFSrcSel(arg);            
        default:
            return MTR_NOT_SUPPORTED;
    }
    return MTR_OK;
}
