/* 2008-10-21: File added and changed by Sony Corporation */
/*
 * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

/*!
 * @file mc13783/pmic_audio.c
 * @brief Implementation of the PMIC(mc13783) Audio driver APIs.
 *
 * The PMIC Audio driver and this API were developed to support the
 * audio playback, recording, and mixing capabilities of the power
 * management ICs that are available from Freescale Semiconductor, Inc.
 *
 * The following operating modes are supported:
 *
 * @verbatim
       Operating Mode               mc13783
       ---------------------------- -------
       Stereo DAC Playback           Yes
       Stereo DAC Input Mixing       Yes
       Voice CODEC Playback          Yes
       Voice CODEC Input Mixing      Yes
       Voice CODEC Mono Recording    Yes
       Voice CODEC Stereo Recording  Yes
       Microphone Bias Control       Yes
       Output Amplifier Control      Yes
       Output Mixing Control         Yes
       Input Amplifier Control       Yes
       Master/Slave Mode Select      Yes
       Anti Pop Bias Circuit Control Yes
   @endverbatim
 *
 * Note that the Voice CODEC may also be referred to as the Telephone
 * CODEC in the PMIC DTS documentation. Also note that, while the power
 * management ICs do provide similar audio capabilities, each PMIC may
 * support additional configuration settings and features. Therefore, it
 * is highly recommended that the appropriate power management IC DTS
 * documents be used in conjunction with this API interface.
 *
 * @ingroup PMIC_AUDIO
 */

#include <linux/module.h>
#include <linux/interrupt.h>	/* For tasklet interface.              */
#include <linux/platform_device.h>	/* For kernel module interface.        */
#include <linux/init.h>
#include <linux/spinlock.h>	/* For spinlock interface.             */
#include <asm/arch/pmic_audio.h>	/* For PMIC Audio driver interface.    */
#include <asm/arch/pmic_adc.h>	/* For PMIC ADC driver interface.      */

#include "../core/pmic_config.h"

/*
 * mc13783 PMIC Audio API
 */

/* EXPORTED FUNCTIONS */
EXPORT_SYMBOL(MIN_STDAC_SAMPLING_RATE_HZ);
EXPORT_SYMBOL(MAX_STDAC_SAMPLING_RATE_HZ);
EXPORT_SYMBOL(pmic_audio_open);
EXPORT_SYMBOL(pmic_audio_close);
EXPORT_SYMBOL(pmic_audio_set_protocol);
EXPORT_SYMBOL(pmic_audio_get_protocol);
EXPORT_SYMBOL(pmic_audio_enable);
EXPORT_SYMBOL(pmic_audio_disable);
EXPORT_SYMBOL(pmic_audio_reset);
EXPORT_SYMBOL(pmic_audio_reset_all);
EXPORT_SYMBOL(pmic_audio_set_callback);
EXPORT_SYMBOL(pmic_audio_clear_callback);
EXPORT_SYMBOL(pmic_audio_get_callback);
EXPORT_SYMBOL(pmic_audio_antipop_enable);
EXPORT_SYMBOL(pmic_audio_antipop_disable);
EXPORT_SYMBOL(pmic_audio_digital_filter_reset);
EXPORT_SYMBOL(pmic_audio_vcodec_set_clock);
EXPORT_SYMBOL(pmic_audio_vcodec_get_clock);
EXPORT_SYMBOL(pmic_audio_vcodec_set_rxtx_timeslot);
EXPORT_SYMBOL(pmic_audio_vcodec_get_rxtx_timeslot);
EXPORT_SYMBOL(pmic_audio_vcodec_set_secondary_txslot);
EXPORT_SYMBOL(pmic_audio_vcodec_get_secondary_txslot);
EXPORT_SYMBOL(pmic_audio_vcodec_set_config);
EXPORT_SYMBOL(pmic_audio_vcodec_clear_config);
EXPORT_SYMBOL(pmic_audio_vcodec_get_config);
EXPORT_SYMBOL(pmic_audio_vcodec_enable_bypass);
EXPORT_SYMBOL(pmic_audio_vcodec_disable_bypass);
EXPORT_SYMBOL(pmic_audio_stdac_set_clock);
EXPORT_SYMBOL(pmic_audio_stdac_get_clock);
EXPORT_SYMBOL(pmic_audio_stdac_set_rxtx_timeslot);
EXPORT_SYMBOL(pmic_audio_stdac_get_rxtx_timeslot);
EXPORT_SYMBOL(pmic_audio_stdac_set_config);
EXPORT_SYMBOL(pmic_audio_stdac_clear_config);
EXPORT_SYMBOL(pmic_audio_stdac_get_config);
EXPORT_SYMBOL(pmic_audio_input_set_config);
EXPORT_SYMBOL(pmic_audio_input_clear_config);
EXPORT_SYMBOL(pmic_audio_input_get_config);
EXPORT_SYMBOL(pmic_audio_vcodec_set_mic);
EXPORT_SYMBOL(pmic_audio_vcodec_get_mic);
EXPORT_SYMBOL(pmic_audio_vcodec_set_mic_on_off);
EXPORT_SYMBOL(pmic_audio_vcodec_get_mic_on_off);
EXPORT_SYMBOL(pmic_audio_vcodec_set_record_gain);
EXPORT_SYMBOL(pmic_audio_vcodec_get_record_gain);
EXPORT_SYMBOL(pmic_audio_vcodec_enable_micbias);
EXPORT_SYMBOL(pmic_audio_vcodec_disable_micbias);
EXPORT_SYMBOL(pmic_audio_vcodec_enable_mixer);
EXPORT_SYMBOL(pmic_audio_vcodec_disable_mixer);
EXPORT_SYMBOL(pmic_audio_stdac_enable_mixer);
EXPORT_SYMBOL(pmic_audio_stdac_disable_mixer);
EXPORT_SYMBOL(pmic_audio_output_set_port);
EXPORT_SYMBOL(pmic_audio_output_get_port);
EXPORT_SYMBOL(pmic_audio_output_set_stereo_in_gain);
EXPORT_SYMBOL(pmic_audio_output_get_stereo_in_gain);
EXPORT_SYMBOL(pmic_audio_output_set_pgaGain);
EXPORT_SYMBOL(pmic_audio_output_get_pgaGain);
EXPORT_SYMBOL(pmic_audio_output_enable_mixer);
EXPORT_SYMBOL(pmic_audio_output_disable_mixer);
EXPORT_SYMBOL(pmic_audio_output_set_balance);
EXPORT_SYMBOL(pmic_audio_output_get_balance);
EXPORT_SYMBOL(pmic_audio_output_enable_mono_adder);
EXPORT_SYMBOL(pmic_audio_output_disable_mono_adder);
EXPORT_SYMBOL(pmic_audio_output_set_mono_adder_gain);
EXPORT_SYMBOL(pmic_audio_output_get_mono_adder_gain);
EXPORT_SYMBOL(pmic_audio_output_set_config);
EXPORT_SYMBOL(pmic_audio_output_clear_config);
EXPORT_SYMBOL(pmic_audio_output_get_config);
EXPORT_SYMBOL(pmic_audio_output_enable_phantom_ground);
EXPORT_SYMBOL(pmic_audio_output_disable_phantom_ground);
EXPORT_SYMBOL(pmic_audio_set_autodetect);

#ifdef DEBUG_AUDIO
EXPORT_SYMBOL(pmic_audio_dump_registers);
#endif				/* DEBUG_AUDIO */

/*!
 * Define the minimum sampling rate (in Hz) that is supported by the
 * Stereo DAC.
 */
const unsigned MIN_STDAC_SAMPLING_RATE_HZ = 8000;

/*!
 * Define the maximum sampling rate (in Hz) that is supported by the
 * Stereo DAC.
 */
const unsigned MAX_STDAC_SAMPLING_RATE_HZ = 96000;

/*! @def SET_BITS
 * Set a register field to a given value.
 */
#define SET_BITS(reg, field, value)    (((value) << reg.field.offset) & \
                                        reg.field.mask)
/*! @def GET_BITS
 * Get the current value of a given register field.
 */
#define GET_BITS(reg, field, value)    (((value) & reg.field.mask) >> \
                                        reg.field.offset)

/*!
 * @brief Define the possible states for a device handle.
 *
 * This enumeration is used to track the current state of each device handle.
 */
typedef enum {
	HANDLE_FREE,		/*!< Handle is available for use. */
	HANDLE_IN_USE		/*!< Handle is currently in use.  */
} HANDLE_STATE;

/*!
 * @brief Identifies the hardware interrupt source.
 *
 * This enumeration identifies which of the possible hardware interrupt
 * sources actually caused the current interrupt handler to be called.
 */
typedef enum {
	CORE_EVENT_MC2BI,	/*!< Microphone Bias 2 detect. */
	CORE_EVENT_HSDETI,	/*!< Detect Headset attach  */
	CORE_EVENT_HSLI,	/*!< Detect Stereo Headset  */
	CORE_EVENT_ALSPTHI,	/*!< Detect Thermal shutdown of ALSP  */
	CORE_EVENT_AHSSHORTI	/*!< Detect Short circuit on AHS outputs  */
} PMIC_CORE_EVENT;

/*!
 * @brief This structure is used to track the state of a microphone input.
 */
typedef struct {
	PMIC_AUDIO_INPUT_PORT mic;	/*!< Microphone input port.      */
	PMIC_AUDIO_INPUT_MIC_STATE micOnOff;	/*!< Microphone On/Off state.    */
	PMIC_AUDIO_MIC_AMP_MODE ampMode;	/*!< Input amplifier mode.       */
	PMIC_AUDIO_MIC_GAIN gain;	/*!< Input amplifier gain level. */
} PMIC_MICROPHONE_STATE;

/*!
 * @brief Tracks whether a headset is currently attached or not.
 */
typedef enum {
	NO_HEADSET,		/*!< No headset currently attached. */
	HEADSET_ON		/*!< Headset has been attached.     */
} HEADSET_STATUS;

/*!
 * @brief mc13783 only enum that indicates the path to output taken
 * by the voice codec output
 */
typedef enum {
	VCODEC_DIRECT_OUT,	/*!< Vcodec signal out direct */
	VCODEC_MIXER_OUT	/*!< Output via the mixer     */
} PMIC_AUDIO_VCODEC_OUTPUT_PATH;

/*!
 * @brief This structure is used to define a specific hardware register field.
 *
 * All hardware register fields are defined using an offset to the LSB
 * and a mask. The offset is used to right shift a register value before
 * applying the mask to actually obtain the value of the field.
 */
typedef struct {
	const unsigned char offset;	/*!< Offset of LSB of register field.          */
	const unsigned int mask;	/*!< Mask value used to isolate register field. */
} REGFIELD;

/*!
 * @brief This structure lists all fields of the AUD_CODEC hardware register.
 */
typedef struct {
	REGFIELD CDCSSISEL;	/*!< codec SSI bus select              */
	REGFIELD CDCCLKSEL;	/*!< Codec clock input select          */
	REGFIELD CDCSM;		/*!< Codec slave / master select       */
	REGFIELD CDCBCLINV;	/*!< Codec bitclock inversion          */
	REGFIELD CDCFSINV;	/*!< Codec framesync inversion         */
	REGFIELD CDCFS;		/*!< Bus protocol selection - 2 bits   */
	REGFIELD CDCCLK;	/*!< Codec clock setting - 3 bits      */
	REGFIELD CDCFS8K16K;	/*!< Codec framesync select            */
	REGFIELD CDCEN;		/*!< Codec enable                      */
	REGFIELD CDCCLKEN;	/*!< Codec clocking enable             */
	REGFIELD CDCTS;		/*!< Codec SSI tristate                */
	REGFIELD CDCDITH;	/*!< Codec dithering                   */
	REGFIELD CDCRESET;	/*!< Codec filter reset                */
	REGFIELD CDCBYP;	/*!< Codec bypass                      */
	REGFIELD CDCALM;	/*!< Codec analog loopback             */
	REGFIELD CDCDLM;	/*!< Codec digital loopback            */
	REGFIELD AUDIHPF;	/*!< Transmit high pass filter enable  */
	REGFIELD AUDOHPF;	/*!< Receive high pass filter enable   */
} REGISTER_AUD_CODEC;

/*!
 * @brief This variable is used to access the AUD_CODEC hardware register.
 *
 * This variable defines how to access all of the fields within the
 * AUD_CODEC hardware register. The initial values consist of the offset
 * and mask values needed to access each of the register fields.
 */
static const REGISTER_AUD_CODEC regAUD_CODEC = {
	{0, 0x000001},		/* CDCSSISEL    */
	{1, 0x000002},		/* CDCCLKSEL    */
	{2, 0x000004},		/* CDCSM        */
	{3, 0x000008},		/* CDCBCLINV    */
	{4, 0x000010},		/* CDCFSINV     */
	{5, 0x000060},		/* CDCFS        */
	{7, 0x000380},		/* CDCCLK       */
	{10, 0x000400},		/* CDCFS8K16K   */
	{11, 0x000800},		/* CDCEN        */
	{12, 0x001000},		/* CDCCLKEN     */
	{13, 0x002000},		/* CDCTS        */
	{14, 0x004000},		/* CDCDITH      */
	{15, 0x008000},		/* CDCRESET     */
	{16, 0x010000},		/* CDCBYP       */
	{17, 0x020000},		/* CDCALM       */
	{18, 0x040000},		/* CDCDLM       */
	{19, 0x080000},		/* AUDIHPF      */
	{20, 0x100000}		/* AUDOHPF      */
	/* Unused       */
	/* Unused       */
	/* Unused       */

};

/*!
 * @brief This structure lists all fields of the ST_DAC hardware register.
 */
 /* VVV */
typedef struct {
	REGFIELD STDCSSISEL;	/*!< Stereo DAC SSI bus select                            */
	REGFIELD STDCCLKSEL;	/*!< Stereo DAC clock input select                        */
	REGFIELD STDCSM;	/*!< Stereo DAC slave / master select                     */
	REGFIELD STDCBCLINV;	/*!< Stereo DAC bitclock inversion                        */
	REGFIELD STDCFSINV;	/*!< Stereo DAC framesync inversion                       */
	REGFIELD STDCFS;	/*!< Bus protocol selection - 2 bits                      */
	REGFIELD STDCCLK;	/*!< Stereo DAC clock setting - 3 bits                    */
	REGFIELD STDCFSDLYB;	/*!< Stereo DAC framesync delay bar                       */
	REGFIELD STDCEN;	/*!< Stereo DAC enable                                    */
	REGFIELD STDCCLKEN;	/*!< Stereo DAC clocking enable                           */
	REGFIELD STDCRESET;	/*!< Stereo DAC filter reset                              */
	REGFIELD SPDIF;		/*!< Stereo DAC SSI SPDIF mode. Mode no longer available. */
	REGFIELD SR;		/*!< Stereo DAC sample rate - 4 bits                      */
} REGISTER_ST_DAC;

/*!
 * @brief This variable is used to access the ST_DAC hardware register.
 *
 * This variable defines how to access all of the fields within the
 * ST_DAC hardware register. The initial values consist of the offset
 * and mask values needed to access each of the register fields.
 */
static const REGISTER_ST_DAC regST_DAC = {
	{0, 0x000001},		/* STDCSSISEL        */
	{1, 0x000002},		/* STDCCLKSEL        */
	{2, 0x000004},		/* STDCSM            */
	{3, 0x000008},		/* STDCBCLINV        */
	{4, 0x000010},		/* STDCFSINV         */
	{5, 0x000060},		/* STDCFS            */
	{7, 0x000380},		/* STDCCLK           */
	{10, 0x000400},		/* STDCFSDLYB        */
	{11, 0x000800},		/* STDCEN            */
	{12, 0x001000},		/* STDCCLKEN         */
	{15, 0x008000},		/* STDCRESET         */
	{16, 0x010000},		/* SPDIF             */
	{17, 0x1E0000}		/* SR                */
};

/*!
 * @brief This structure lists all of the fields in the SSI_NETWORK hardware register.
 */
typedef struct {
	REGFIELD CDCTXRXSLOT;	/*!< Codec timeslot assignment  - 2 bits                                  */
	REGFIELD CDCTXSECSLOT;	/*!< Codec secondary transmit timeslot - 2 bits                           */
	REGFIELD CDCRXSECSLOT;	/*!< Codec secondary receive timeslot - 2 bits                            */
	REGFIELD CDCRXSECGAIN;	/*!< Codec secondary receive channel gain setting - 2 bits                */
	REGFIELD CDCSUMGAIN;	/*!< Codec summed receive signal gain setting                             */
	REGFIELD CDCFSDLY;	/*!< Codec framesync delay                                                */
	REGFIELD STDCSLOTS;	/*!< Stereo DAC number of timeslots select  - 2 bits                      */
	REGFIELD STDCRXSLOT;	/*!< Stereo DAC timeslot assignment - 2 bits                              */
	REGFIELD STDCRXSECSLOT;	/*!< Stereo DAC secondary receive timeslot  - 2 bits                      */
	REGFIELD STDCRXSECGAIN;	/*!< Stereo DAC secondary receive channel gain setting   - 2 bits         */
	REGFIELD STDCSUMGAIN;	/*!< Stereo DAC summed receive signal gain setting                        */
} REGISTER_SSI_NETWORK;

/*!
 * @brief This variable is used to access the SSI_NETWORK hardware register.
 *
 * This variable defines how to access all of the fields within the
 * SSI_NETWORK hardware register. The initial values consist of the offset
 * and mask values needed to access each of the register fields.
 */
static const REGISTER_SSI_NETWORK regSSI_NETWORK = {
	{2, 0x00000c},		/* CDCTXRXSLOT          */
	{4, 0x000030},		/* CDCTXSECSLOT         */
	{6, 0x0000c0},		/* CDCRXSECSLOT         */
	{8, 0x000300},		/* CDCRXSECGAIN         */
	{10, 0x000400},		/* CDCSUMGAIN           */
	{11, 0x000800},		/* CDCFSDLY             */
	{12, 0x003000},		/* STDCSLOTS            */
	{14, 0x00c000},		/* STDCRXSLOT           */
	{16, 0x030000},		/* STDCRXSECSLOT        */
	{18, 0x0c0000},		/* STDCRXSECGAIN        */
	{20, 0x100000}		/* STDCSUMGAIN          */
};

/*!
 * @brief This structure lists all fields of the AUDIO_TX hardware register.
 *
 *
 */
typedef struct {
	REGFIELD MC1BEN;	/*!< Microphone bias 1 enable                                */
	REGFIELD MC2BEN;	/*!< Microphone bias 2 enable                                */
	REGFIELD MC2BDETDBNC;	/*!< Microphone bias detect debounce setting                 */
	REGFIELD MC2BDETEN;	/*!< Microphone bias 2 detect enable                         */
	REGFIELD AMC1REN;	/*!< Amplifier Amc1R enable                                  */
	REGFIELD AMC1RITOV;	/*!< Amplifier Amc1R current to voltage mode enable          */
	REGFIELD AMC1LEN;	/*!< Amplifier Amc1L enable                                  */
	REGFIELD AMC1LITOV;	/*!< Amplifier Amc1L current to voltage mode enable          */
	REGFIELD AMC2EN;	/*!< Amplifier Amc2 enable                                   */
	REGFIELD AMC2ITOV;	/*!< Amplifier Amc2 current to voltage mode enable           */
	REGFIELD ATXINEN;	/*!< Amplifier Atxin enable                                  */
	REGFIELD ATXOUTEN;	/*!< Reserved for output TXOUT enable, currently not used    */
	REGFIELD RXINREC;	/*!< RXINR/RXINL to voice CODEC ADC routing enable           */
	REGFIELD PGATXR;	/*!< Transmit gain setting right - 5 bits                    */
	REGFIELD PGATXL;	/*!< Transmit gain setting left  - 5 bits                    */
} REGISTER_AUDIO_TX;

/*!
 * @brief This variable is used to access the AUDIO_TX hardware register.
 *
 * This variable defines how to access all of the fields within the
 * AUDIO_TX hardware register. The initial values consist of the offset
 * and mask values needed to access each of the register fields.
 */
static const REGISTER_AUDIO_TX regAUDIO_TX = {
	{0, 0x000001},		/* MC1BEN              */
	{1, 0x000002},		/* MC2BEN              */
	{2, 0x000004},		/* MC2BDETDBNC         */
	{3, 0x000008},		/* MC2BDETEN           */
	{5, 0x000020},		/* AMC1REN             */
	{6, 0x000040},		/* AMC1RITOV           */
	{7, 0x000080},		/* AMC1LEN             */
	{8, 0x000100},		/* AMC1LITOV           */
	{9, 0x000200},		/* AMC2EN              */
	{10, 0x000400},		/* AMC2ITOV            */
	{11, 0x000800},		/* ATXINEN             */
	{12, 0x001000},		/* ATXOUTEN            */
	{13, 0x002000},		/* RXINREC             */
	{14, 0x07c000},		/* PGATXR              */
	{19, 0xf80000}		/* PGATXL              */
};

/*!
 * @brief This structure lists all fields of the AUDIO_RX_0 hardware register.
 */
typedef struct {
	REGFIELD VAUDIOON;	/*!<    Forces VAUDIO in active on mode               */
	REGFIELD BIASEN;	/*!<    Audio bias enable                             */
	REGFIELD BIASSPEED;	/*!<    Turn on ramp speed of the audio bias          */
	REGFIELD ASPEN;		/*!<    Amplifier Asp enable                          */
	REGFIELD ASPSEL;	/*!<    Asp input selector                            */
	REGFIELD ALSPEN;	/*!<    Amplifier Alsp enable                         */
	REGFIELD ALSPREF;	/*!<    Bias Alsp at common audio reference           */
	REGFIELD ALSPSEL;	/*!<    Alsp input selector                           */
	REGFIELD LSPLEN;	/*!<    Output LSPL enable                            */
	REGFIELD AHSREN;	/*!<    Amplifier AhsR enable                         */
	REGFIELD AHSLEN;	/*!<    Amplifier AhsL enable                         */
	REGFIELD AHSSEL;	/*!<    Ahsr and Ahsl input selector                  */
	REGFIELD HSPGDIS;	/*!<    Phantom ground disable                        */
	REGFIELD HSDETEN;	/*!<    Headset detect enable                         */
	REGFIELD HSDETAUTOB;	/*!<    Amplifier state determined by headset detect  */
	REGFIELD ARXOUTREN;	/*!<    Output RXOUTR enable                          */
	REGFIELD ARXOUTLEN;	/*!<    Output RXOUTL enable                          */
	REGFIELD ARXOUTSEL;	/*!<    Arxout input selector                         */
	REGFIELD CDCOUTEN;	/*!<    Output CDCOUT enable                          */
	REGFIELD HSLDETEN;	/*!<    Headset left channel detect enable            */
	REGFIELD ADDCDC;	/*!<    Adder channel codec selection                 */
	REGFIELD ADDSTDC;	/*!<    Adder channel stereo DAC selection            */
	REGFIELD ADDRXIN;	/*!<    Adder channel line in selection               */
} REGISTER_AUDIO_RX_0;

/*!
 * @brief This variable is used to access the AUDIO_RX_0 hardware register.
 *
 * This variable defines how to access all of the fields within the
 * AUDIO_RX_0 hardware register. The initial values consist of the offset
 * and mask values needed to access each of the register fields.
 */
static const REGISTER_AUDIO_RX_0 regAUDIO_RX_0 = {
	{0, 0x000001},		/* VAUDIOON    */
	{1, 0x000002},		/* BIASEN      */
	{2, 0x000004},		/* BIASSPEED   */
	{3, 0x000008},		/* ASPEN       */
	{4, 0x000010},		/* ASPSEL      */
	{5, 0x000020},		/* ALSPEN      */
	{6, 0x000040},		/* ALSPREF     */
	{7, 0x000080},		/* ALSPSEL     */
	{8, 0x000100},		/* LSPLEN      */
	{9, 0x000200},		/* AHSREN      */
	{10, 0x000400},		/* AHSLEN      */
	{11, 0x000800},		/* AHSSEL      */
	{12, 0x001000},		/* HSPGDIS     */
	{13, 0x002000},		/* HSDETEN     */
	{14, 0x004000},		/* HSDETAUTOB  */
	{15, 0x008000},		/* ARXOUTREN   */
	{16, 0x010000},		/* ARXOUTLEN   */
	{17, 0x020000},		/* ARXOUTSEL   */
	{18, 0x040000},		/* CDCOUTEN    */
	{19, 0x080000},		/* HSLDETEN    */
	{21, 0x200000},		/* ADDCDC      */
	{22, 0x400000},		/* ADDSTDC     */
	{23, 0x800000}		/* ADDRXIN     */
};

/*!
 * @brief This structure lists all fields of the AUDIO_RX_1 hardware register.
 */
typedef struct {
	REGFIELD PGARXEN;	/*!<    Codec receive PGA enable                 */
	REGFIELD PGARX;		/*!<    Codec receive gain setting - 4 bits      */
	REGFIELD PGASTEN;	/*!<    Stereo DAC PGA enable                    */
	REGFIELD PGAST;		/*!<    Stereo DAC gain setting  - 4 bits        */
	REGFIELD ARXINEN;	/*!<    Amplifier Arx enable                     */
	REGFIELD ARXIN;		/*!<    Amplifier Arx additional gain setting    */
	REGFIELD PGARXIN;	/*!<    PGArxin gain setting  - 4 bits           */
	REGFIELD MONO;		/*!<    Mono adder setting   - 2 bits            */
	REGFIELD BAL;		/*!<    Balance control      - 3 bits            */
	REGFIELD BALLR;		/*!<    Left / right balance                     */
} REGISTER_AUDIO_RX_1;

/*!
 * @brief This variable is used to access the AUDIO_RX_1 hardware register.
 *
 * This variable defines how to access all of the fields within the
 * AUDIO_RX_1 hardware register. The initial values consist of the offset
 * and mask values needed to access each of the register fields.
 */
static const REGISTER_AUDIO_RX_1 regAUDIO_RX_1 = {
	{0, 0x000001},		/* PGARXEN    */
	{1, 0x00001e},		/* PGARX      */
	{5, 0x000020},		/* PGASTEN   */
	{6, 0x0003c0},		/* PGAST       */
	{10, 0x000400},		/* ARXINEN      */
	{11, 0x000800},		/* ARXIN      */
	{12, 0x00f000},		/* PGARXIN     */
	{16, 0x030000},		/* MONO     */
	{18, 0x1c0000},		/* BAL      */
	{21, 0x200000}		/* BALLR      */
};

/*! Define a mask to access the entire hardware register. */
static const unsigned int REG_FULLMASK = 0xffffff;

/*! Reset value for the AUD_CODEC register. */
static const unsigned int RESET_AUD_CODEC = 0x180027;

/*! Reset value for the ST_DAC register.
 *
 *  Note that we avoid resetting any of the arbitration bits.
 */
static const unsigned int RESET_ST_DAC = 0x0E0004;

/*! Reset value for the SSI_NETWORK register. */
static const unsigned int RESET_SSI_NETWORK = 0x013060;

/*! Reset value for the AUDIO_TX register.
 *
 *  Note that we avoid resetting any of the arbitration bits.
 */
static const unsigned int RESET_AUDIO_TX = 0x420000;

/*! Reset value for the AUDIO_RX_0 register. */
static const unsigned int RESET_AUDIO_RX_0 = 0x001000;

/*! Reset value for the AUDIO_RX_1 register. */
static const unsigned int RESET_AUDIO_RX_1 = 0x00D35A;

/*! Reset mask for the SSI network Vcodec part. first 12 bits
 * 0 - 11 */
static const unsigned int REG_SSI_VCODEC_MASK = 0x000fff;

/*! Reset mask for the SSI network STDAC part. last 12 bits
 * 12 - 24 */
static const unsigned int REG_SSI_STDAC_MASK = 0xfff000;

/*! Constant NULL value for initializing/reseting the audio handles. */
static const PMIC_AUDIO_HANDLE AUDIO_HANDLE_NULL = (PMIC_AUDIO_HANDLE) NULL;

/*!
 * @brief This structure maintains the current state of the Stereo DAC.
 */
typedef struct {
	PMIC_AUDIO_HANDLE handle;	/*!< Handle used to access
					   the Stereo DAC.         */
	HANDLE_STATE handleState;	/*!< Current handle state.   */
	PMIC_AUDIO_DATA_BUS busID;	/*!< Data bus used to access
					   the Stereo DAC.         */
	bool protocol_set;
	PMIC_AUDIO_BUS_PROTOCOL protocol;	/*!< Data bus protocol.      */
	PMIC_AUDIO_BUS_MODE masterSlave;	/*!< Master/Slave mode
						   select.                 */
	PMIC_AUDIO_NUMSLOTS numSlots;	/*!< Number of timeslots
					   used.                   */
	PMIC_AUDIO_CALLBACK callback;	/*!< Event notification
					   callback function
					   pointer.                */
	PMIC_AUDIO_EVENTS eventMask;	/*!< Event notification mask. */
	PMIC_AUDIO_CLOCK_IN_SOURCE clockIn;	/*!< Stereo DAC clock input
						   source select.          */
	PMIC_AUDIO_STDAC_SAMPLING_RATE samplingRate;	/*!< Stereo DAC sampling rate
							   select.                 */
	PMIC_AUDIO_STDAC_CLOCK_IN_FREQ clockFreq;	/*!< Stereo DAC clock input
							   frequency.              */
	PMIC_AUDIO_CLOCK_INVERT invert;	/*!< Stereo DAC clock signal
					   invert select.          */
	PMIC_AUDIO_STDAC_TIMESLOTS timeslot;	/*!< Stereo DAC data
						   timeslots select.       */
	PMIC_AUDIO_STDAC_CONFIG config;	/*!< Stereo DAC configuration
					   options.                */
} PMIC_AUDIO_STDAC_STATE;

/*!
 * @brief This variable maintains the current state of the Stereo DAC.
 *
 * This variable tracks the current state of the Stereo DAC audio hardware
 * along with any information that is required by the device driver to
 * manage the hardware (e.g., callback functions and event notification
 * masks).
 *
 * The initial values represent the reset/power on state of the Stereo DAC.
 */
static PMIC_AUDIO_STDAC_STATE stDAC = {
	(PMIC_AUDIO_HANDLE) NULL,	/* handle       */
	HANDLE_FREE,		/* handleState  */
	AUDIO_DATA_BUS_1,	/* busID        */
	false,
	NORMAL_MSB_JUSTIFIED_MODE,	/* protocol     */
	BUS_MASTER_MODE,	/* masterSlave  */
	USE_2_TIMESLOTS,	/* numSlots     */
	(PMIC_AUDIO_CALLBACK) NULL,	/* callback     */
	(PMIC_AUDIO_EVENTS) NULL,	/* eventMask    */
	CLOCK_IN_CLIA,		/* clockIn      */
	STDAC_RATE_44_1_KHZ,	/* samplingRate */
	STDAC_CLI_13MHZ,	/* clockFreq    */
	NO_INVERT,		/* invert       */
	USE_TS0_TS1,		/* timeslot     */
	(PMIC_AUDIO_STDAC_CONFIG) 0	/* config       */
};

/*!
 * @brief This structure maintains the current state of the Voice CODEC.
 */
typedef struct {
	PMIC_AUDIO_HANDLE handle;	/*!< Handle used to access
					   the Voice CODEC.       */
	HANDLE_STATE handleState;	/*!< Current handle state.  */
	PMIC_AUDIO_DATA_BUS busID;	/*!< Data bus used to access
					   the Voice CODEC.       */
	bool protocol_set;
	PMIC_AUDIO_BUS_PROTOCOL protocol;	/*!< Data bus protocol.     */
	PMIC_AUDIO_BUS_MODE masterSlave;	/*!< Master/Slave mode
						   select.                */
	PMIC_AUDIO_NUMSLOTS numSlots;	/*!< Number of timeslots
					   used.                  */
	PMIC_AUDIO_CALLBACK callback;	/*!< Event notification
					   callback function
					   pointer.               */
	PMIC_AUDIO_EVENTS eventMask;	/*!< Event notification
					   mask.                  */
	PMIC_AUDIO_CLOCK_IN_SOURCE clockIn;	/*!< Voice CODEC clock input
						   source select.         */
	PMIC_AUDIO_VCODEC_SAMPLING_RATE samplingRate;	/*!< Voice CODEC sampling
							   rate select.           */
	PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ clockFreq;	/*!< Voice CODEC clock input
							   frequency.             */
	PMIC_AUDIO_CLOCK_INVERT invert;	/*!< Voice CODEC clock
					   signal invert select.  */
	PMIC_AUDIO_VCODEC_TIMESLOT timeslot;	/*!< Voice CODEC data
						   timeslot select.       */
	PMIC_AUDIO_VCODEC_TIMESLOT secondaryTXtimeslot;

	PMIC_AUDIO_VCODEC_CONFIG config;	/*!< Voice CODEC
						   configuration
						   options.            */
	PMIC_MICROPHONE_STATE leftChannelMic;	/*!< Left channel
						   microphone
						   configuration.      */
	PMIC_MICROPHONE_STATE rightChannelMic;	/*!< Right channel
						   microphone
						   configuration.      */
} PMIC_AUDIO_VCODEC_STATE;

/*!
 * @brief This variable maintains the current state of the Voice CODEC.
 *
 * This variable tracks the current state of the Voice CODEC audio hardware
 * along with any information that is required by the device driver to
 * manage the hardware (e.g., callback functions and event notification
 * masks).
 *
 * The initial values represent the reset/power on state of the Voice CODEC.
 */
static PMIC_AUDIO_VCODEC_STATE vCodec = {
	(PMIC_AUDIO_HANDLE) NULL,	/* handle          */
	HANDLE_FREE,		/* handleState     */
	AUDIO_DATA_BUS_2,	/* busID           */
	false,
	NETWORK_MODE,		/* protocol        */
	BUS_SLAVE_MODE,		/* masterSlave     */
	USE_4_TIMESLOTS,	/* numSlots        */
	(PMIC_AUDIO_CALLBACK) NULL,	/* callback        */
	(PMIC_AUDIO_EVENTS) NULL,	/* eventMask       */
	CLOCK_IN_CLIB,		/* clockIn         */
	VCODEC_RATE_8_KHZ,	/* samplingRate    */
	VCODEC_CLI_13MHZ,	/* clockFreq       */
	NO_INVERT,		/* invert          */
	USE_TS0,		/* timeslot pri    */
	USE_TS2,		/* timeslot sec TX */
	INPUT_HIGHPASS_FILTER | OUTPUT_HIGHPASS_FILTER,	/* config          */
	/* leftChannelMic  */
	{NO_MIC,		/*    mic          */
	 MICROPHONE_OFF,	/*    micOnOff     */
	 AMP_OFF,		/*    ampMode      */
	 MIC_GAIN_0DB		/*    gain         */
	 },
	/* rightChannelMic */
	{NO_MIC,		/*    mic          */
	 MICROPHONE_OFF,	/*    micOnOff     */
	 AMP_OFF,		/*    ampMode      */
	 MIC_GAIN_0DB		/*    gain         */
	 }
};

/*!
 * @brief This maintains the current state of the External Stereo Input.
 */
typedef struct {
	PMIC_AUDIO_HANDLE handle;	/*!< Handle used to access the
					   External Stereo Inputs.     */
	HANDLE_STATE handleState;	/*!< Current handle state.       */
	PMIC_AUDIO_CALLBACK callback;	/*!< Event notification callback
					   function pointer.           */
	PMIC_AUDIO_EVENTS eventMask;	/*!< Event notification mask.    */
	PMIC_AUDIO_STEREO_IN_GAIN inputGain;	/*!< External Stereo Input
						   amplifier gain level.       */
} PMIC_AUDIO_EXT_STEREO_IN_STATE;

/*!
 * @brief This maintains the current state of the External Stereo Input.
 *
 * This variable tracks the current state of the External Stereo Input audio
 * hardware along with any information that is required by the device driver
 * to manage the hardware (e.g., callback functions and event notification
 * masks).
 *
 * The initial values represent the reset/power on state of the External
 * Stereo Input.
 */
static PMIC_AUDIO_EXT_STEREO_IN_STATE extStereoIn = {
	(PMIC_AUDIO_HANDLE) NULL,	/* handle      */
	HANDLE_FREE,		/* handleState */
	(PMIC_AUDIO_CALLBACK) NULL,	/* callback    */
	(PMIC_AUDIO_EVENTS) NULL,	/* eventMask   */
	STEREO_IN_GAIN_0DB	/* inputGain   */
};

/*!
 * @brief This maintains the current state of the Audio Output Section.
 */
typedef struct {
	PMIC_AUDIO_OUTPUT_PORT outputPort;	/*!< Current audio
						   output port.     */
	PMIC_AUDIO_OUTPUT_PGA_GAIN vCodecoutputPGAGain;	/*!< Output PGA gain
							   level codec      */
	PMIC_AUDIO_OUTPUT_PGA_GAIN stDacoutputPGAGain;	/*!< Output PGA gain
							   level stDAC      */
	PMIC_AUDIO_OUTPUT_PGA_GAIN extStereooutputPGAGain;	/*!< Output PGA gain
								   level stereo ext */
	PMIC_AUDIO_OUTPUT_BALANCE_GAIN balanceLeftGain;	/*!< Left channel
							   balance gain
							   level.           */
	PMIC_AUDIO_OUTPUT_BALANCE_GAIN balanceRightGain;	/*!< Right channel
								   balance gain
								   level.           */
	PMIC_AUDIO_MONO_ADDER_OUTPUT_GAIN monoAdderGain;	/*!< Mono adder gain
								   level.           */
	PMIC_AUDIO_OUTPUT_CONFIG config;	/*!< Audio output
						   section config
						   options.         */
	PMIC_AUDIO_VCODEC_OUTPUT_PATH vCodecOut;

} PMIC_AUDIO_AUDIO_OUTPUT_STATE;

/*!
 * @brief This variable maintains the current state of the Audio Output Section.
 *
 * This variable tracks the current state of the Audio Output Section.
 *
 * The initial values represent the reset/power on state of the Audio
 * Output Section.
 */
static PMIC_AUDIO_AUDIO_OUTPUT_STATE audioOutput = {
	(PMIC_AUDIO_OUTPUT_PORT) NULL,	/* outputPort       */
	OUTPGA_GAIN_0DB,	/* outputPGAGain    */
	OUTPGA_GAIN_0DB,	/* outputPGAGain    */
	OUTPGA_GAIN_0DB,	/* outputPGAGain    */
	BAL_GAIN_0DB,		/* balanceLeftGain  */
	BAL_GAIN_0DB,		/* balanceRightGain */
	MONOADD_GAIN_0DB,	/* monoAdderGain    */
	(PMIC_AUDIO_OUTPUT_CONFIG) 0,	/* config           */
	VCODEC_DIRECT_OUT
};

/*! The current headset status. */
static HEADSET_STATUS headsetState = NO_HEADSET;

/* Removed PTT variable */
/*! Define a 1 ms wait interval that is needed to ensure that certain
 *  hardware operations are successfully completed.
 */
static const unsigned long delay_1ms = (HZ / 1000);

/*!
 * @brief This spinlock is used to provide mutual exclusion.
 *
 * Create a spinlock that can be used to provide mutually exclusive
 * read/write access to the globally accessible data structures
 * that were defined above. Mutually exclusive access is required to
 * ensure that the audio data structures are consistent at all times
 * when possibly accessed by multiple threads of execution (for example,
 * while simultaneously handling a user request and an interrupt event).
 *
 * We need to use a spinlock whenever we do need to provide mutual
 * exclusion while possibly executing in a hardware interrupt context.
 * Spinlocks should be held for the minimum time that is necessary
 * because hardware interrupts are disabled while a spinlock is held.
 *
 *static spinlock_t lock = SPIN_LOCK_UNLOCKED;
 */

/*!
 * @brief This mutex is used to provide mutual exclusion.
 *
 * Create a mutex that can be used to provide mutually exclusive
 * read/write access to the globally accessible data structures
 * that were defined above. Mutually exclusive access is required to
 * ensure that the audio data structures are consistent at all times
 * when possibly accessed by multiple threads of execution.
 *
 * Note that we use a mutex instead of the spinlock whenever disabling
 * interrupts while in the critical section is not required. This helps
 * to minimize kernel interrupt handling latency.
 */
static DECLARE_MUTEX(mutex);

/* Prototype for the audio driver tasklet function. */
static void pmic_audio_tasklet(unsigned long arg);

/*!
 * @brief Tasklet handler for the audio driver.
 *
 * Declare a tasklet that will do most of the processing for
 * mc13783 audio interrupt events. Note that we cannot do all of the required
 * processing within the interrupt handler itself because we may need to
 * call the ADC driver to measure voltages as well as calling any
 * user-registered callback functions.
 */
DECLARE_TASKLET(audioTasklet, pmic_audio_tasklet, 0);

/*!
 * @brief Global variable to track currently active interrupt events.
 *
 * This global variable is used to keep track of all of the currently
 * active interrupt events for the audio driver. Note that access to this
 * variable may occur while within an interrupt context and, therefore,
 * must be guarded by using a spinlock.
 */
/* static PMIC_CORE_EVENT eventID = 0; */

/* Prototypes for all static audio driver functions. */
/*
static PMIC_STATUS pmic_audio_mic_boost_enable(void);
static PMIC_STATUS pmic_audio_mic_boost_disable(void);*/
static PMIC_STATUS pmic_audio_close_handle(const PMIC_AUDIO_HANDLE handle);
static PMIC_STATUS pmic_audio_reset_device(const PMIC_AUDIO_HANDLE handle);
/*
static PMIC_STATUS pmic_audio_deregister(PMIC_AUDIO_CALLBACK * const callback,
					 PMIC_AUDIO_EVENTS * const eventMask);
static void pmic_audio_event_handler(void *param);*/

/*************************************************************************
 * Audio device access APIs.
 *************************************************************************
 */

/*!
 * @name General Setup and Configuration APIs
 * Functions for general setup and configuration of the PMIC Audio
 * hardware.
 */
/*@{*/

PMIC_STATUS pmic_audio_set_autodetect(int val)
{
	PMIC_STATUS status;
	unsigned int reg_mask = 0;
	reg_mask = SET_BITS(regAUDIO_RX_0, VAUDIOON, 1);
	status = pmic_write_reg(REG_AUDIO_RX_0, reg_mask, reg_mask);
	if (status != PMIC_SUCCESS)
		return status;
	reg_mask = 0;
	reg_mask =
	    SET_BITS(regAUDIO_RX_0, HSDETEN, 1) | SET_BITS(regAUDIO_RX_0,
							   HSDETAUTOB, 1);
	status = pmic_write_reg(REG_AUDIO_RX_0, reg_mask, reg_mask);
	return status;
}

/*!
 * @brief Request exclusive access to the PMIC Audio hardware.
 *
 * Attempt to open and gain exclusive access to a key PMIC audio hardware
 * component (e.g., the Stereo DAC or the Voice CODEC). Depending upon the
 * type of audio operation that is desired and the nature of the audio data
 * stream, the Stereo DAC and/or the Voice CODEC will be a required hardware
 * component and needs to be acquired by calling this function.
 *
 * If the open request is successful, then a numeric handle is returned
 * and this handle must be used in all subsequent function calls to complete
 * the configuration of either the Stereo DAC or the Voice CODEC and along
 * with any other associated audio hardware components that will be needed.
 *
 * The same handle must also be used in the close call when use of the PMIC
 * audio hardware is no longer required.
 *
 * The open request will fail if the requested audio hardware component has
 * already been acquired by a previous open call but not yet closed.
 *
 * @param  handle          Device handle to be used for subsequent PMIC
 *                              audio API calls.
 * @param  device          The required PMIC audio hardware component.
 *
 * @retval      PMIC_SUCCESS         If the open request was successful
 * @retval      PMIC_PARAMETER_ERROR If the handle argument is NULL.
 * @retval      PMIC_ERROR           If the audio hardware component is
 *                                   unavailable.
 */
PMIC_STATUS pmic_audio_open(PMIC_AUDIO_HANDLE * const handle,
			    const PMIC_AUDIO_SOURCE device)
{
	PMIC_STATUS rc = PMIC_ERROR;

	if (handle == (PMIC_AUDIO_HANDLE *) NULL) {
		/* Do not dereference a NULL pointer. */
		return PMIC_PARAMETER_ERROR;
	}

	/* We only need to acquire a mutex here because the interrupt handler
	 * never modifies the device handle or device handle state. Therefore,
	 * we don't need to worry about conflicts with the interrupt handler
	 * or the need to execute in an interrupt context.
	 *
	 * But we do need a critical section here to avoid problems in case
	 * multiple calls to pmic_audio_open() are made since we can only allow
	 * one of them to succeed.
	 */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	/* Check the current device handle state and acquire the handle if
	 * it is available.
	 */

	if ((device == STEREO_DAC) && (stDAC.handleState == HANDLE_FREE)) {
		stDAC.handle = (PMIC_AUDIO_HANDLE) (&stDAC);
		stDAC.handleState = HANDLE_IN_USE;
		*handle = stDAC.handle;
		rc = PMIC_SUCCESS;
	} else if ((device == VOICE_CODEC)
		   && (vCodec.handleState == HANDLE_FREE)) {
		vCodec.handle = (PMIC_AUDIO_HANDLE) (&vCodec);
		vCodec.handleState = HANDLE_IN_USE;
		*handle = vCodec.handle;
		rc = PMIC_SUCCESS;
	} else if ((device == EXTERNAL_STEREO_IN) &&
		   (extStereoIn.handleState == HANDLE_FREE)) {
		extStereoIn.handle = (PMIC_AUDIO_HANDLE) (&extStereoIn);
		extStereoIn.handleState = HANDLE_IN_USE;
		*handle = extStereoIn.handle;
		rc = PMIC_SUCCESS;
	} else {
		*handle = AUDIO_HANDLE_NULL;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Terminate further access to the PMIC audio hardware.
 *
 * Terminate further access to the PMIC audio hardware that was previously
 * acquired by calling pmic_audio_open(). This now allows another thread to
 * successfully call pmic_audio_open() to gain access.
 *
 * Note that we will shutdown/reset the Voice CODEC or Stereo DAC as well as
 * any associated audio input/output components that are no longer required.
 *
 * @param   handle          Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the close request was successful.
 * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
 */
PMIC_STATUS pmic_audio_close(const PMIC_AUDIO_HANDLE handle)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* We need a critical section here to maintain a consistent state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	/* We can now call pmic_audio_close_handle() to actually do the work. */
	rc = pmic_audio_close_handle(handle);

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Configure the data bus protocol to be used.
 *
 * Provide the parameters needed to properly configure the audio data bus
 * protocol so that data can be read/written to either the Stereo DAC or
 * the Voice CODEC.
 *
 * @param   handle          Device handle from pmic_audio_open() call.
 * @param   busID           Select data bus to be used.
 * @param   protocol        Select the data bus protocol.
 * @param   masterSlave     Select the data bus timing mode.
 * @param   numSlots        Define the number of timeslots (only if in
 *                              master mode).
 *
 * @retval      PMIC_SUCCESS         If the protocol was successful configured.
 * @retval      PMIC_PARAMETER_ERROR If the handle or the protocol parameters
 *                                   are invalid.
 */
PMIC_STATUS pmic_audio_set_protocol(const PMIC_AUDIO_HANDLE handle,
				    const PMIC_AUDIO_DATA_BUS busID,
				    const PMIC_AUDIO_BUS_PROTOCOL protocol,
				    const PMIC_AUDIO_BUS_MODE masterSlave,
				    const PMIC_AUDIO_NUMSLOTS numSlots)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	const unsigned int ST_DAC_MASK = SET_BITS(regST_DAC, STDCSSISEL, 1) |
	    SET_BITS(regST_DAC, STDCFS, 3) | SET_BITS(regST_DAC, STDCSM, 1);

	unsigned int reg_mask;
	/*unsigned int VCODEC_MASK = SET_BITS(regAUD_CODEC, CDCSSISEL, 1) |
	   SET_BITS(regAUD_CODEC, CDCFS, 3) | SET_BITS(regAUD_CODEC, CDCSM, 1); */

	unsigned int SSI_NW_MASK = SET_BITS(regSSI_NETWORK, STDCSLOTS, 1);
	unsigned int reg_value = 0;
	unsigned int ssi_nw_value = 0;

	/* Enter a critical section so that we can ensure only one
	 * state change request is completed at a time.
	 */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if (handle == (PMIC_AUDIO_HANDLE) NULL) {
		rc = PMIC_PARAMETER_ERROR;
	} else {
		if ((handle == vCodec.handle) &&
		    (vCodec.handleState == HANDLE_IN_USE)) {
			if ((stDAC.handleState == HANDLE_IN_USE) &&
			    (stDAC.busID == busID) && (stDAC.protocol_set)) {
				pr_debug("The requested bus already in USE\n");
				rc = PMIC_PARAMETER_ERROR;
			} else if ((masterSlave == BUS_MASTER_MODE)
				   && (numSlots != USE_4_TIMESLOTS)) {
				pr_debug
				    ("mc13783 supports only 4 slots in Master mode\n");
				rc = PMIC_NOT_SUPPORTED;
			} else if ((masterSlave == BUS_SLAVE_MODE)
				   && (numSlots != USE_4_TIMESLOTS)) {
				pr_debug
				    ("Driver currently supports only 4 slots in Slave mode\n");
				rc = PMIC_NOT_SUPPORTED;
			} else if (!((protocol == NETWORK_MODE) ||
				     (protocol == I2S_MODE))) {
				pr_debug
				    ("mc13783 Voice codec works only in Network and I2S modes\n");
				rc = PMIC_NOT_SUPPORTED;
			} else {
				pr_debug
				    ("Proceeding to configure Voice Codec\n");
				if (busID == AUDIO_DATA_BUS_1) {
					reg_value =
					    SET_BITS(regAUD_CODEC, CDCSSISEL,
						     0);
				} else {
					reg_value =
					    SET_BITS(regAUD_CODEC, CDCSSISEL,
						     1);
				}
				reg_mask = SET_BITS(regAUD_CODEC, CDCSSISEL, 1);
				if (PMIC_SUCCESS !=
				    pmic_write_reg(REG_AUDIO_CODEC,
						   reg_value, reg_mask))
					return PMIC_ERROR;

				if (masterSlave == BUS_MASTER_MODE) {
					reg_value =
					    SET_BITS(regAUD_CODEC, CDCSM, 0);
				} else {
					reg_value =
					    SET_BITS(regAUD_CODEC, CDCSM, 1);
				}
				reg_mask = SET_BITS(regAUD_CODEC, CDCSM, 1);
				if (PMIC_SUCCESS !=
				    pmic_write_reg(REG_AUDIO_CODEC,
						   reg_value, reg_mask))
					return PMIC_ERROR;

				if (protocol == NETWORK_MODE) {
					reg_value =
					    SET_BITS(regAUD_CODEC, CDCFS, 1);
				} else {	/* protocol == I2S, other options have been already eliminated */
					reg_value =
					    SET_BITS(regAUD_CODEC, CDCFS, 2);
				}
				reg_mask = SET_BITS(regAUD_CODEC, CDCFS, 3);
				if (PMIC_SUCCESS !=
				    pmic_write_reg(REG_AUDIO_CODEC,
						   reg_value, reg_mask))
					return PMIC_ERROR;

				ssi_nw_value =
				    SET_BITS(regSSI_NETWORK, CDCFSDLY, 1);
				/*if (pmic_write_reg
				   (REG_AUDIO_CODEC, reg_value,
				   VCODEC_MASK) != PMIC_SUCCESS) {
				   rc = PMIC_ERROR;
				   } else { */
				vCodec.busID = busID;
				vCodec.protocol = protocol;
				vCodec.masterSlave = masterSlave;
				vCodec.numSlots = numSlots;
				vCodec.protocol_set = true;
				//pmic_write_reg(REG_AUDIO_SSI_NETWORK, ssi_nw_value, ssi_nw_value);

				pr_debug
				    ("mc13783 Voice codec successfully configured\n");
				rc = PMIC_SUCCESS;
				//}

			}

		} else if ((handle == stDAC.handle) &&
			   (stDAC.handleState == HANDLE_IN_USE)) {
			if ((vCodec.handleState == HANDLE_IN_USE) &&
			    (vCodec.busID == busID) && (vCodec.protocol_set)) {
				pr_debug("The requested bus already in USE\n");
				rc = PMIC_PARAMETER_ERROR;
			} else if (((protocol == NORMAL_MSB_JUSTIFIED_MODE) ||
				    (protocol == I2S_MODE))
				   && (numSlots != USE_2_TIMESLOTS)) {
				pr_debug
				    ("STDAC uses only 2 slots in Normal and I2S modes\n");
				rc = PMIC_PARAMETER_ERROR;
			} else if ((protocol == NETWORK_MODE) &&
				   !((numSlots == USE_2_TIMESLOTS) ||
				     (numSlots == USE_4_TIMESLOTS) ||
				     (numSlots == USE_8_TIMESLOTS))) {
				pr_debug
				    ("STDAC uses only 2,4 or 8 slots in Network mode\n");
				rc = PMIC_PARAMETER_ERROR;
			} else if (protocol == SPD_IF_MODE) {
				pr_debug
				    ("STDAC driver currently does not support SPD IF mode\n");
				rc = PMIC_NOT_SUPPORTED;
			} else {
				pr_debug
				    ("Proceeding to configure Stereo DAC\n");
				if (busID == AUDIO_DATA_BUS_1) {
					reg_value =
					    SET_BITS(regST_DAC, STDCSSISEL, 0);
				} else {
					reg_value =
					    SET_BITS(regST_DAC, STDCSSISEL, 1);
				}
				if (masterSlave == BUS_MASTER_MODE) {
					reg_value |=
					    SET_BITS(regST_DAC, STDCSM, 0);
				} else {
					reg_value |=
					    SET_BITS(regST_DAC, STDCSM, 1);
				}
				if (protocol == NETWORK_MODE) {
					reg_value |=
					    SET_BITS(regST_DAC, STDCFS, 1);
				} else if (protocol ==
					   NORMAL_MSB_JUSTIFIED_MODE) {
					reg_value |=
					    SET_BITS(regST_DAC, STDCFS, 0);
				} else {	/* I2S mode as the other option has already been eliminated */
					reg_value |=
					    SET_BITS(regST_DAC, STDCFS, 2);
				}

				if (pmic_write_reg
				    (REG_AUDIO_STEREO_DAC,
				     reg_value, ST_DAC_MASK) != PMIC_SUCCESS) {
					rc = PMIC_ERROR;
				} else {
					if (numSlots == USE_2_TIMESLOTS) {
						reg_value =
						    SET_BITS(regSSI_NETWORK,
							     STDCSLOTS, 3);
					} else if (numSlots == USE_4_TIMESLOTS) {
						reg_value =
						    SET_BITS(regSSI_NETWORK,
							     STDCSLOTS, 2);
					} else {	/* Use 8 timeslots - L , R and 6 other */
						reg_value =
						    SET_BITS(regSSI_NETWORK,
							     STDCSLOTS, 1);
					}
					if (pmic_write_reg
					    (REG_AUDIO_SSI_NETWORK,
					     reg_value,
					     SSI_NW_MASK) != PMIC_SUCCESS) {
						rc = PMIC_ERROR;
					} else {
						stDAC.busID = busID;
						stDAC.protocol = protocol;
						stDAC.protocol_set = true;
						stDAC.masterSlave = masterSlave;
						stDAC.numSlots = numSlots;
						pr_debug
						    ("mc13783 Stereo DAC successfully configured\n");
						rc = PMIC_SUCCESS;
					}
				}

			}
		} else {
			rc = PMIC_PARAMETER_ERROR;
			/* Handle  can only be Voice Codec or Stereo DAC */
			pr_debug("Handles only STDAC and VCODEC\n");
		}

	}
	/* Exit critical section. */
	up(&mutex);
	return rc;
}

/*!
 * @brief Retrieve the current data bus protocol configuration.
 *
 * Retrieve the parameters that define the current audio data bus protocol.
 *
 * @param  handle          Device handle from pmic_audio_open() call.
 * @param  busID           The data bus being used.
 * @param  protocol        The data bus protocol being used.
 * @param  masterSlave     The data bus timing mode being used.
 * @param  numSlots        The number of timeslots being used (if in
 *                              master mode).
 *
 * @retval      PMIC_SUCCESS         If the protocol was successful retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
 */
PMIC_STATUS pmic_audio_get_protocol(const PMIC_AUDIO_HANDLE handle,
				    PMIC_AUDIO_DATA_BUS * const busID,
				    PMIC_AUDIO_BUS_PROTOCOL * const protocol,
				    PMIC_AUDIO_BUS_MODE * const masterSlave,
				    PMIC_AUDIO_NUMSLOTS * const numSlots)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	if ((busID != (PMIC_AUDIO_DATA_BUS *) NULL) &&
	    (protocol != (PMIC_AUDIO_BUS_PROTOCOL *) NULL) &&
	    (masterSlave != (PMIC_AUDIO_BUS_MODE *) NULL) &&
	    (numSlots != (PMIC_AUDIO_NUMSLOTS *) NULL)) {
		/* Enter a critical section so that we return a consistent state. */
		if (down_interruptible(&mutex))
			return PMIC_SYSTEM_ERROR_EINTR;

		if ((handle == stDAC.handle) &&
		    (stDAC.handleState == HANDLE_IN_USE)) {
			*busID = stDAC.busID;
			*protocol = stDAC.protocol;
			*masterSlave = stDAC.masterSlave;
			*numSlots = stDAC.numSlots;
			rc = PMIC_SUCCESS;
		} else if ((handle == vCodec.handle) &&
			   (vCodec.handleState == HANDLE_IN_USE)) {
			*busID = vCodec.busID;
			*protocol = vCodec.protocol;
			*masterSlave = vCodec.masterSlave;
			*numSlots = vCodec.numSlots;
			rc = PMIC_SUCCESS;
		}

		/* Exit critical section. */
		up(&mutex);
	}

	return rc;
}

/*!
 * @brief Enable the Stereo DAC or the Voice CODEC.
 *
 * Explicitly enable the Stereo DAC or the Voice CODEC to begin audio
 * playback or recording as required. This should only be done after
 * successfully configuring all of the associated audio components (e.g.,
 * microphones, amplifiers, etc.).
 *
 * Note that the timed delays used in this function are necessary to
 * ensure reliable operation of the Voice CODEC and Stereo DAC. The
 * Stereo DAC seems to be particularly sensitive and it has been observed
 * to fail to generate the required master mode clock signals if it is
 * not allowed enough time to initialize properly.
 *
 * @param      handle          Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the device was successful enabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
 * @retval      PMIC_ERROR           If the device could not be enabled.
 */
PMIC_STATUS pmic_audio_enable(const PMIC_AUDIO_HANDLE handle)
{
	const unsigned int AUDIO_BIAS_ENABLE = SET_BITS(regAUDIO_RX_0,
							VAUDIOON, 1);
	const unsigned int STDAC_ENABLE = SET_BITS(regST_DAC, STDCEN, 1);
	const unsigned int VCODEC_ENABLE = SET_BITS(regAUD_CODEC, CDCEN, 1);
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	unsigned int reg_mask = 0;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
		/* We can enable the Stereo DAC. */
		rc = pmic_write_reg(REG_AUDIO_STEREO_DAC,
				    STDAC_ENABLE, STDAC_ENABLE);
		/*pmic_read_reg(REG_AUDIO_STEREO_DAC, &reg_value); */
		if (rc != PMIC_SUCCESS) {
			pr_debug("Failed to enable the Stereo DAC\n");
			rc = PMIC_ERROR;
		}
	} else if ((handle == vCodec.handle)
		   && (vCodec.handleState == HANDLE_IN_USE)) {
		/* Must first set the audio bias bit to power up the audio circuits. */
		pmic_write_reg(REG_AUDIO_RX_0, AUDIO_BIAS_ENABLE,
			       AUDIO_BIAS_ENABLE);
		reg_mask = SET_BITS(regAUDIO_RX_0, HSDETEN, 1) |
		    SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
		reg_write = SET_BITS(regAUDIO_RX_0, HSDETEN, 1) |
		    SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
		rc = pmic_write_reg(REG_AUDIO_RX_0, reg_write, reg_mask);
		if (rc == PMIC_SUCCESS) {
		}
		/* Then we can enable the Voice CODEC. */
		rc = pmic_write_reg(REG_AUDIO_CODEC, VCODEC_ENABLE,
				    VCODEC_ENABLE);

		/* pmic_read_reg(REG_AUDIO_CODEC, &reg_value); */
		if (rc != PMIC_SUCCESS) {
			pr_debug("Failed to enable the Voice codec\n");
			rc = PMIC_ERROR;
		}
	}
	/* Exit critical section. */
	up(&mutex);
	return rc;
}

/*!
 * @brief Disable the Stereo DAC or the Voice CODEC.
 *
 * Explicitly disable the Stereo DAC or the Voice CODEC to end audio
 * playback or recording as required.
 *
 * @param   	handle          Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the device was successful disabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
 * @retval      PMIC_ERROR           If the device could not be disabled.
 */
PMIC_STATUS pmic_audio_disable(const PMIC_AUDIO_HANDLE handle)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	const unsigned int STDAC_DISABLE = SET_BITS(regST_DAC, STDCEN, 1);
	const unsigned int VCODEC_DISABLE = SET_BITS(regAUD_CODEC, CDCEN, 1);

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;
	if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
		rc = pmic_write_reg(REG_AUDIO_STEREO_DAC, 0, STDAC_DISABLE);
	} else if ((handle == vCodec.handle)
		   && (vCodec.handleState == HANDLE_IN_USE)) {
		rc = pmic_write_reg(REG_AUDIO_CODEC, 0, VCODEC_DISABLE);
	}
	if (rc == PMIC_SUCCESS) {
		pr_debug("Disabled successfully\n");
	}
	/* Exit critical section. */
	up(&mutex);
	return rc;
}

/*!
 * @brief Reset the selected audio hardware control registers to their
 *        power on state.
 *
 * This resets all of the audio hardware control registers currently
 * associated with the device handle back to their power on states. For
 * example, if the handle is associated with the Stereo DAC and a
 * specific output port and output amplifiers, then this function will
 * reset all of those components to their initial power on state.
 *
 * @param       handle          Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the reset operation was successful.
 * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
 * @retval      PMIC_ERROR           If the reset was unsuccessful.
 */
PMIC_STATUS pmic_audio_reset(const PMIC_AUDIO_HANDLE handle)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	rc = pmic_audio_reset_device(handle);

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Reset all audio hardware control registers to their power on state.
 *
 * This resets all of the audio hardware control registers back to their
 * power on states. Use this function with care since it also invalidates
 * (i.e., automatically closes) all currently opened device handles.
 *
 * @retval      PMIC_SUCCESS         If the reset operation was successful.
 * @retval      PMIC_ERROR           If the reset was unsuccessful.
 */
PMIC_STATUS pmic_audio_reset_all(void)
{
	PMIC_STATUS rc = PMIC_SUCCESS;
	unsigned int audio_ssi_reset = 0;
	unsigned int audio_rx1_reset = 0;
	/* We need a critical section here to maintain a consistent state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	/* First close all opened device handles, also deregisters callbacks. */
	pmic_audio_close_handle(stDAC.handle);
	pmic_audio_close_handle(vCodec.handle);
	pmic_audio_close_handle(extStereoIn.handle);

	if (pmic_write_reg(REG_AUDIO_RX_1, RESET_AUDIO_RX_1,
			   PMIC_ALL_BITS) != PMIC_SUCCESS) {
		rc = PMIC_ERROR;
	} else {
		audio_rx1_reset = 1;
	}
	if (pmic_write_reg(REG_AUDIO_SSI_NETWORK, RESET_SSI_NETWORK,
			   PMIC_ALL_BITS) != PMIC_SUCCESS) {
		rc = PMIC_ERROR;
	} else {
		audio_ssi_reset = 1;
	}
	if (pmic_write_reg
	    (REG_AUDIO_STEREO_DAC, RESET_ST_DAC,
	     PMIC_ALL_BITS) != PMIC_SUCCESS) {
		rc = PMIC_ERROR;
	} else {
		/* Also reset the driver state information to match. Note that we
		 * keep the device handle and event callback settings unchanged
		 * since these don't affect the actual hardware and we rely on
		 * the user to explicitly close the handle or deregister callbacks
		 */
		if (audio_ssi_reset) {
			/* better to check if SSI is also reset as some fields are represennted in SSI reg */
			stDAC.busID = AUDIO_DATA_BUS_1;
			stDAC.protocol = NORMAL_MSB_JUSTIFIED_MODE;
			stDAC.masterSlave = BUS_MASTER_MODE;
			stDAC.protocol_set = false;
			stDAC.numSlots = USE_2_TIMESLOTS;
			stDAC.clockIn = CLOCK_IN_CLIA;
			stDAC.samplingRate = STDAC_RATE_44_1_KHZ;
			stDAC.clockFreq = STDAC_CLI_13MHZ;
			stDAC.invert = NO_INVERT;
			stDAC.timeslot = USE_TS0_TS1;
			stDAC.config = (PMIC_AUDIO_STDAC_CONFIG) 0;
		}
	}

	if (pmic_write_reg(REG_AUDIO_CODEC, RESET_AUD_CODEC,
			   PMIC_ALL_BITS) != PMIC_SUCCESS) {
		rc = PMIC_ERROR;
	} else {
		/* Also reset the driver state information to match. Note that we
		 * keep the device handle and event callback settings unchanged
		 * since these don't affect the actual hardware and we rely on
		 * the user to explicitly close the handle or deregister callbacks
		 */
		if (audio_ssi_reset) {
			vCodec.busID = AUDIO_DATA_BUS_2;
			vCodec.protocol = NETWORK_MODE;
			vCodec.masterSlave = BUS_SLAVE_MODE;
			vCodec.protocol_set = false;
			vCodec.numSlots = USE_4_TIMESLOTS;
			vCodec.clockIn = CLOCK_IN_CLIB;
			vCodec.samplingRate = VCODEC_RATE_8_KHZ;
			vCodec.clockFreq = VCODEC_CLI_13MHZ;
			vCodec.invert = NO_INVERT;
			vCodec.timeslot = USE_TS0;
			vCodec.config =
			    INPUT_HIGHPASS_FILTER | OUTPUT_HIGHPASS_FILTER;
		}
	}

	if (pmic_write_reg(REG_AUDIO_RX_0, RESET_AUDIO_RX_0,
			   PMIC_ALL_BITS) != PMIC_SUCCESS) {
		rc = PMIC_ERROR;
	} else {
		/* Also reset the driver state information to match. */
		audioOutput.outputPort = (PMIC_AUDIO_OUTPUT_PORT) NULL;
		audioOutput.vCodecoutputPGAGain = OUTPGA_GAIN_0DB;
		audioOutput.stDacoutputPGAGain = OUTPGA_GAIN_0DB;
		audioOutput.extStereooutputPGAGain = OUTPGA_GAIN_0DB;
		audioOutput.balanceLeftGain = BAL_GAIN_0DB;
		audioOutput.balanceRightGain = BAL_GAIN_0DB;
		audioOutput.monoAdderGain = MONOADD_GAIN_0DB;
		audioOutput.config = (PMIC_AUDIO_OUTPUT_CONFIG) 0;
		audioOutput.vCodecOut = VCODEC_DIRECT_OUT;
	}

	if (pmic_write_reg(REG_AUDIO_TX, RESET_AUDIO_TX,
			   PMIC_ALL_BITS) != PMIC_SUCCESS) {
		rc = PMIC_ERROR;
	} else {
		/* Also reset the driver state information to match. Note that we
		 * reset the vCodec fields since all of the input/recording
		 * devices are only connected to the Voice CODEC and are managed
		 * as part of the Voice CODEC state.
		 */
		if (audio_rx1_reset) {
			vCodec.leftChannelMic.mic = NO_MIC;
			vCodec.leftChannelMic.micOnOff = MICROPHONE_OFF;
			vCodec.leftChannelMic.ampMode = CURRENT_TO_VOLTAGE;
			vCodec.leftChannelMic.gain = MIC_GAIN_0DB;
			vCodec.rightChannelMic.mic = NO_MIC;
			vCodec.rightChannelMic.micOnOff = MICROPHONE_OFF;
			vCodec.rightChannelMic.ampMode = AMP_OFF;
			vCodec.rightChannelMic.gain = MIC_GAIN_0DB;
		}
	}
	/* Finally, also reset any global state variables. */
	headsetState = NO_HEADSET;
	/* Exit the critical section. */
	up(&mutex);
	return rc;
}

/*!
 * @brief Set the Audio callback function.
 *
 * Register a callback function that will be used to signal PMIC audio
 * events. For example, the OSS audio driver should register a callback
 * function in order to be notified of headset connect/disconnect events.
 *
 * @param   handle          Device handle from pmic_audio_open() call.
 * @param   func            A pointer to the callback function.
 * @param   eventMask       A mask selecting events to be notified.
 *
 * @retval      PMIC_SUCCESS         If the callback was successfully
 *                                   registered.
 * @retval      PMIC_PARAMETER_ERROR If the handle or the eventMask is invalid.
 */
PMIC_STATUS pmic_audio_set_callback(const PMIC_AUDIO_HANDLE handle,
				    const PMIC_AUDIO_CALLBACK func,
				    const PMIC_AUDIO_EVENTS eventMask)
{
	/* Event handling and callback are currently not supported for mc13783 */
	/* Both in case of PMIC and old API */
	PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
	return rc;
}

/*!
 * @brief Deregisters the existing audio callback function.
 *
 * Deregister the callback function that was previously registered by calling
 * pmic_audio_set_callback().
 *
 * @param       handle          Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the callback was successfully
 *                                   deregistered.
 * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
 */
PMIC_STATUS pmic_audio_clear_callback(const PMIC_AUDIO_HANDLE handle)
{
	/* Event handling and callback are currently not supported for mc13783 */
	/* Both in case of PMIC and old API */
	PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
	return rc;
}

/*!
 * @brief Get the current audio callback function settings.
 *
 * Get the current callback function and event mask.
 *
 * @param   handle          Device handle from pmic_audio_open() call.
 * @param   func            The current callback function.
 * @param   eventMask       The current event selection mask.
 *
 * @retval      PMIC_SUCCESS         If the callback information was
 *                                   successfully retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
 */
PMIC_STATUS pmic_audio_get_callback(const PMIC_AUDIO_HANDLE handle,
				    PMIC_AUDIO_CALLBACK * const func,
				    PMIC_AUDIO_EVENTS * const eventMask)
{
	/* Event handling and callback are currently not supported for mc13783 */
	/* Both in case of PMIC and old API */
	PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
	return rc;
}

/*!
 * @brief Enable the anti-pop circuitry to avoid extra noise when inserting
 *        or removing a external device (e.g., a headset).
 *
 * Enable the use of the built-in anti-pop circuitry to prevent noise from
 * being generated when an external audio device is inserted or removed
 * from an audio plug. A slow ramp speed may be needed to avoid extra noise.
 *
 * @param       rampSpeed       The desired anti-pop circuitry ramp speed.
 *
 * @retval      PMIC_SUCCESS         If the anti-pop circuitry was successfully
 *                                   enabled.
 * @retval      PMIC_ERROR           If the anti-pop circuitry could not be
 *                                   enabled.
 */
PMIC_STATUS pmic_audio_antipop_enable(const PMIC_AUDIO_ANTI_POP_RAMP_SPEED
				      rampSpeed)
{
	PMIC_STATUS rc = PMIC_ERROR;
	unsigned int reg_value = 0;
	const unsigned int reg_mask = SET_BITS(regAUDIO_RX_0, BIASEN, 1) |
	    SET_BITS(regAUDIO_RX_0, BIASSPEED, 1);

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	/*
	 * Antipop is enabled by enabling the BIAS (BIASEN) and setting the
	 * BIASSPEED .
	 * BIASEN is just to make sure that BIAS is enabled
	 */
	reg_value = SET_BITS(regAUDIO_RX_0, BIASEN, 1)
	    | SET_BITS(regAUDIO_RX_0, BIASSPEED, 0);
	rc = pmic_write_reg(REG_AUDIO_RX_0, reg_value, reg_mask);
	return rc;
}

/*!
 * @brief Disable the anti-pop circuitry.
 *
 * Disable the use of the built-in anti-pop circuitry to prevent noise from
 * being generated when an external audio device is inserted or removed
 * from an audio plug.
 *
 * @retval      PMIC_SUCCESS         If the anti-pop circuitry was successfully
 *                                   disabled.
 * @retval      PMIC_ERROR           If the anti-pop circuitry could not be
 *                                   disabled.
 */
PMIC_STATUS pmic_audio_antipop_disable(void)
{
	PMIC_STATUS rc = PMIC_ERROR;
	const unsigned int reg_mask = SET_BITS(regAUDIO_RX_0, BIASSPEED, 1) |
	    SET_BITS(regAUDIO_RX_0, BIASEN, 1);
	const unsigned int reg_write = SET_BITS(regAUDIO_RX_0, BIASSPEED, 1) |
	    SET_BITS(regAUDIO_RX_0, BIASEN, 0);

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	/*
	 * Antipop is disabled by setting BIASSPEED  = 0. BIASEN bit remains set
	 * as only antipop needs to be disabled
	 */
	rc = pmic_write_reg(REG_AUDIO_RX_0, reg_write, reg_mask);

	return rc;
}

/*!
 * @brief Performs a reset of the Voice CODEC/Stereo DAC digital filter.
 *
 * The digital filter should be reset whenever the clock or sampling rate
 * configuration has been changed.
 *
 * @param       handle          Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the digital filter was successfully
 *                                   reset.
 * @retval      PMIC_ERROR           If the digital filter could not be reset.
 */
PMIC_STATUS pmic_audio_digital_filter_reset(const PMIC_AUDIO_HANDLE handle)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_mask = 0;

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
		reg_mask = SET_BITS(regST_DAC, STDCRESET, 1);
		if (pmic_write_reg(REG_AUDIO_STEREO_DAC, reg_mask,
				   reg_mask) != PMIC_SUCCESS) {
			rc = PMIC_ERROR;
		} else {
			pr_debug("STDAC filter reset\n");
		}

	} else if ((handle == vCodec.handle) &&
		   (vCodec.handleState == HANDLE_IN_USE)) {
		reg_mask = SET_BITS(regAUD_CODEC, CDCRESET, 1);
		if (pmic_write_reg(REG_AUDIO_CODEC, reg_mask,
				   reg_mask) != PMIC_SUCCESS) {
			rc = PMIC_ERROR;
		} else {
			pr_debug("CODEC filter reset\n");
		}
	}
	return rc;
}

/*!
 * @brief Get the most recent PTT button voltage reading.
 *
 * This feature is not supported by mc13783
 * @param       level                PTT button level.
 *
 * @retval      PMIC_SUCCESS         If the most recent PTT button voltage was
 *                                   returned.
 * @retval      PMIC_PARAMETER_ERROR If a NULL pointer argument was given.
 */
PMIC_STATUS pmic_audio_get_ptt_button_level(unsigned int *const level)
{
	PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
	return rc;
}

#ifdef DEBUG_AUDIO

/*!
 * @brief Provide a hexadecimal dump of all PMIC audio registers (DEBUG only)
 *
 * This function is intended strictly for debugging purposes only and will
 * print the current values of the following PMIC registers:
 *
 * - AUD_CODEC
 * - ST_DAC
 * - AUDIO_RX_0
 * - AUDIO_RX_1
 * - AUDIO_TX
 * - AUDIO_SSI_NW
 *
 * The register fields will not be decoded.
 *
 * Note that we don't dump any of the arbitration bits because we cannot
 * access the true arbitration bit settings when reading the registers
 * from the secondary SPI bus.
 *
 * Also note that we must not call this function with interrupts disabled,
 * for example, while holding a spinlock, because calls to pmic_read_reg()
 * eventually end up in the SPI driver which will want to perform a
 * schedule() operation. If schedule() is called with interrupts disabled,
 * then you will see messages like the following:
 *
 * BUG: scheduling while atomic: ...
 *
 */
void pmic_audio_dump_registers(void)
{
	unsigned int reg_value = 0;

	/* Dump the AUD_CODEC (Voice CODEC) register. */
	if (pmic_read_reg(REG_AUDIO_CODEC, &reg_value, REG_FULLMASK)
	    == PMIC_SUCCESS) {
		pr_debug("Audio Codec = 0x%x\n", reg_value);
	} else {
		pr_debug("Failed to read audio codec\n");
	}

	/* Dump the ST DAC (Stereo DAC) register. */
	if (pmic_read_reg
	    (REG_AUDIO_STEREO_DAC, &reg_value, REG_FULLMASK) == PMIC_SUCCESS) {
		pr_debug("Stereo DAC = 0x%x\n", reg_value);
	} else {
		pr_debug("Failed to read Stereo DAC\n");
	}

	/* Dump the SSI NW register. */
	if (pmic_read_reg
	    (REG_AUDIO_SSI_NETWORK, &reg_value, REG_FULLMASK) == PMIC_SUCCESS) {
		pr_debug("SSI Network = 0x%x\n", reg_value);
	} else {
		pr_debug("Failed to read SSI network\n");
	}

	/* Dump the Audio RX 0 register. */
	if (pmic_read_reg(REG_AUDIO_RX_0, &reg_value, REG_FULLMASK)
	    == PMIC_SUCCESS) {
		pr_debug("Audio RX 0 = 0x%x\n", reg_value);
	} else {
		pr_debug("Failed to read audio RX 0\n");
	}

	/* Dump the Audio RX 1 register. */
	if (pmic_read_reg(REG_AUDIO_RX_1, &reg_value, REG_FULLMASK)
	    == PMIC_SUCCESS) {
		pr_debug("Audio RX 1 = 0x%x\n", reg_value);
	} else {
		pr_debug("Failed to read audio RX 1\n");
	}
	/* Dump the Audio TX register. */
	if (pmic_read_reg(REG_AUDIO_TX, &reg_value, REG_FULLMASK) ==
	    PMIC_SUCCESS) {
		pr_debug("Audio Tx = 0x%x\n", reg_value);
	} else {
		pr_debug("Failed to read audio TX\n");
	}

}

#endif				/* DEBUG_AUDIO */

/*@}*/

/*************************************************************************
 * General Voice CODEC configuration.
 *************************************************************************
 */

/*!
 * @name General Voice CODEC Setup and Configuration APIs
 * Functions for general setup and configuration of the PMIC Voice
 * CODEC hardware.
 */
/*@{*/

/*!
 * @brief Set the Voice CODEC clock source and operating characteristics.
 *
 * Define the Voice CODEC clock source and operating characteristics. This
 * must be done before the Voice CODEC is enabled.
 *
 *
 *
 * @param       handle          Device handle from pmic_audio_open() call.
 * @param       clockIn         Select the clock signal source.
 * @param       clockFreq       Select the clock signal frequency.
 * @param       samplingRate    Select the audio data sampling rate.
 * @param       invert          Enable inversion of the frame sync and/or
 *                              bit clock inputs.
 *
 * @retval      PMIC_SUCCESS         If the Voice CODEC clock settings were
 *                                   successfully configured.
 * @retval      PMIC_PARAMETER_ERROR If the handle or clock configuration was
 *                                   invalid.
 * @retval      PMIC_ERROR           If the Voice CODEC clock configuration
 *                                   could not be set.
 */
PMIC_STATUS pmic_audio_vcodec_set_clock(const PMIC_AUDIO_HANDLE handle,
					const PMIC_AUDIO_CLOCK_IN_SOURCE
					clockIn,
					const PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ
					clockFreq,
					const PMIC_AUDIO_VCODEC_SAMPLING_RATE
					samplingRate,
					const PMIC_AUDIO_CLOCK_INVERT invert)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_value = 0;
	unsigned int reg_mask = 0;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	/* Validate all of the calling parameters. */
	if (handle == (PMIC_AUDIO_HANDLE) NULL) {
		rc = PMIC_PARAMETER_ERROR;
	} else if ((handle == vCodec.handle) &&
		   (vCodec.handleState == HANDLE_IN_USE)) {
		if ((clockIn != CLOCK_IN_CLIA) && (clockIn != CLOCK_IN_CLIB)) {
			rc = PMIC_PARAMETER_ERROR;
		} else if (!((clockFreq >= VCODEC_CLI_13MHZ)
			     && (clockFreq <= VCODEC_CLI_33_6MHZ))) {
			rc = PMIC_PARAMETER_ERROR;
		} else if ((samplingRate != VCODEC_RATE_8_KHZ)
			   && (samplingRate != VCODEC_RATE_16_KHZ)) {
			rc = PMIC_PARAMETER_ERROR;
		} else if (!((invert >= NO_INVERT)
			     && (invert <= INVERT_FRAMESYNC))) {
			rc = PMIC_PARAMETER_ERROR;
		} else {
			/*reg_mask = SET_BITS(regAUD_CODEC, CDCCLK, 7) |
			   SET_BITS(regAUD_CODEC, CDCCLKSEL, 1) |
			   SET_BITS(regAUD_CODEC, CDCFS8K16K, 1) |
			   SET_BITS(regAUD_CODEC, CDCBCLINV, 1) |
			   SET_BITS(regAUD_CODEC, CDCFSINV, 1); */
			if (clockIn == CLOCK_IN_CLIA) {
				reg_value =
				    SET_BITS(regAUD_CODEC, CDCCLKSEL, 0);
			} else {
				reg_value =
				    SET_BITS(regAUD_CODEC, CDCCLKSEL, 1);
			}
			reg_mask = SET_BITS(regAUD_CODEC, CDCCLKSEL, 1);
			if (PMIC_SUCCESS !=
			    pmic_write_reg(REG_AUDIO_CODEC,
					   reg_value, reg_mask))
				return PMIC_ERROR;

			reg_value = 0;
			if (clockFreq == VCODEC_CLI_13MHZ) {
				reg_value |= SET_BITS(regAUD_CODEC, CDCCLK, 0);
			} else if (clockFreq == VCODEC_CLI_15_36MHZ) {
				reg_value |= SET_BITS(regAUD_CODEC, CDCCLK, 1);
			} else if (clockFreq == VCODEC_CLI_16_8MHZ) {
				reg_value |= SET_BITS(regAUD_CODEC, CDCCLK, 2);
			} else if (clockFreq == VCODEC_CLI_26MHZ) {
				reg_value |= SET_BITS(regAUD_CODEC, CDCCLK, 4);
			} else {
				reg_value |= SET_BITS(regAUD_CODEC, CDCCLK, 7);
			}
			reg_mask = SET_BITS(regAUD_CODEC, CDCCLK, 7);
			if (PMIC_SUCCESS !=
			    pmic_write_reg(REG_AUDIO_CODEC,
					   reg_value, reg_mask))
				return PMIC_ERROR;

			reg_value = 0;
			reg_mask = 0;

			if (samplingRate == VCODEC_RATE_8_KHZ) {
				reg_value |=
				    SET_BITS(regAUD_CODEC, CDCFS8K16K, 0);
			} else {
				reg_value |=
				    SET_BITS(regAUD_CODEC, CDCFS8K16K, 1);
			}
			reg_mask = SET_BITS(regAUD_CODEC, CDCFS8K16K, 1);
			if (PMIC_SUCCESS !=
			    pmic_write_reg(REG_AUDIO_CODEC,
					   reg_value, reg_mask))
				return PMIC_ERROR;
			reg_value = 0;
			reg_mask =
			    SET_BITS(regAUD_CODEC, CDCBCLINV,
				     1) | SET_BITS(regAUD_CODEC, CDCFSINV, 1);

			if (invert & INVERT_BITCLOCK) {
				reg_value |=
				    SET_BITS(regAUD_CODEC, CDCBCLINV, 1);
			}
			if (invert & INVERT_FRAMESYNC) {
				reg_value |=
				    SET_BITS(regAUD_CODEC, CDCFSINV, 1);
			}
			if (invert & NO_INVERT) {
				reg_value |=
				    SET_BITS(regAUD_CODEC, CDCBCLINV, 0);
				reg_value |=
				    SET_BITS(regAUD_CODEC, CDCFSINV, 0);
			}
			if (pmic_write_reg
			    (REG_AUDIO_CODEC, reg_value,
			     reg_mask) != PMIC_SUCCESS) {
				rc = PMIC_ERROR;
			} else {
				pr_debug("CODEC clock set\n");
				vCodec.clockIn = clockIn;
				vCodec.clockFreq = clockFreq;
				vCodec.samplingRate = samplingRate;
				vCodec.invert = invert;
			}

		}

	} else {
		rc = PMIC_PARAMETER_ERROR;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Get the Voice CODEC clock source and operating characteristics.
 *
 * Get the current Voice CODEC clock source and operating characteristics.
 *
 * @param  	handle          Device handle from pmic_audio_open() call.
 * @param  	clockIn         The clock signal source.
 * @param  	clockFreq       The clock signal frequency.
 * @param  	samplingRate    The audio data sampling rate.
 * @param       invert          Inversion of the frame sync and/or
 *                              bit clock inputs is enabled/disabled.
 *
 * @retval      PMIC_SUCCESS         If the Voice CODEC clock settings were
 *                                   successfully retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle invalid.
 * @retval      PMIC_ERROR           If the Voice CODEC clock configuration
 *                                   could not be retrieved.
 */
PMIC_STATUS pmic_audio_vcodec_get_clock(const PMIC_AUDIO_HANDLE handle,
					PMIC_AUDIO_CLOCK_IN_SOURCE *
					const clockIn,
					PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ *
					const clockFreq,
					PMIC_AUDIO_VCODEC_SAMPLING_RATE *
					const samplingRate,
					PMIC_AUDIO_CLOCK_INVERT * const invert)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure that we return a consistent state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == vCodec.handle) &&
	    (vCodec.handleState == HANDLE_IN_USE) &&
	    (clockIn != (PMIC_AUDIO_CLOCK_IN_SOURCE *) NULL) &&
	    (clockFreq != (PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ *) NULL) &&
	    (samplingRate != (PMIC_AUDIO_VCODEC_SAMPLING_RATE *) NULL) &&
	    (invert != (PMIC_AUDIO_CLOCK_INVERT *) NULL)) {
		*clockIn = vCodec.clockIn;
		*clockFreq = vCodec.clockFreq;
		*samplingRate = vCodec.samplingRate;
		*invert = vCodec.invert;

		rc = PMIC_SUCCESS;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Set the Voice CODEC primary audio channel timeslot.
 *
 * Set the Voice CODEC primary audio channel timeslot. This function must be
 * used if the default timeslot for the primary audio channel is to be changed.
 *
 * @param       handle          Device handle from pmic_audio_open() call.
 * @param       timeslot        Select the primary audio channel timeslot.
 *
 * @retval      PMIC_SUCCESS         If the Voice CODEC primary audio channel
 *                                   timeslot was successfully configured.
 * @retval      PMIC_PARAMETER_ERROR If the handle or audio channel timeslot
 *                                   was invalid.
 * @retval      PMIC_ERROR           If the Voice CODEC primary audio channel
 *                                   timeslot could not be set.
 */
PMIC_STATUS pmic_audio_vcodec_set_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
						const PMIC_AUDIO_VCODEC_TIMESLOT
						timeslot)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	const unsigned int reg_mask = SET_BITS(regSSI_NETWORK, CDCTXRXSLOT, 3);

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == vCodec.handle) &&
	    (vCodec.handleState == HANDLE_IN_USE) &&
	    ((timeslot == USE_TS0) || (timeslot == USE_TS1) ||
	     (timeslot == USE_TS2) || (timeslot == USE_TS3))) {
		reg_write = SET_BITS(regSSI_NETWORK, CDCTXRXSLOT, timeslot);

		rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK, reg_write, reg_mask);

		if (rc == PMIC_SUCCESS) {
			vCodec.timeslot = timeslot;
		}
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Get the current Voice CODEC primary audio channel timeslot.
 *
 * Get the current Voice CODEC primary audio channel timeslot.
 *
 * @param	handle          Device handle from pmic_audio_open() call.
 * @param       timeslot        The primary audio channel timeslot.
 *
 * @retval      PMIC_SUCCESS         If the Voice CODEC primary audio channel
 *                                   timeslot was successfully retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the Voice CODEC primary audio channel
 *                                   timeslot could not be retrieved.
 */
PMIC_STATUS pmic_audio_vcodec_get_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
						PMIC_AUDIO_VCODEC_TIMESLOT *
						const timeslot)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == vCodec.handle) &&
	    (vCodec.handleState == HANDLE_IN_USE) &&
	    (timeslot != (PMIC_AUDIO_VCODEC_TIMESLOT *) NULL)) {
		*timeslot = vCodec.timeslot;

		rc = PMIC_SUCCESS;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Set the Voice CODEC secondary recording audio channel timeslot.
 *
 * Set the Voice CODEC secondary audio channel timeslot. This function must be
 * used if the default timeslot for the secondary audio channel is to be
 * changed. The secondary audio channel timeslot is used to transmit the audio
 * data that was recorded by the Voice CODEC from the secondary audio input
 * channel.
 *
 * @param   handle          Device handle from pmic_audio_open() call.
 * @param   timeslot        Select the secondary audio channel timeslot.
 *
 * @retval      PMIC_SUCCESS         If the Voice CODEC secondary audio channel
 *                                   timeslot was successfully configured.
 * @retval      PMIC_PARAMETER_ERROR If the handle or audio channel timeslot
 *                                   was invalid.
 * @retval      PMIC_ERROR           If the Voice CODEC secondary audio channel
 *                                   timeslot could not be set.
 */
PMIC_STATUS pmic_audio_vcodec_set_secondary_txslot(const PMIC_AUDIO_HANDLE
						   handle,
						   const
						   PMIC_AUDIO_VCODEC_TIMESLOT
						   timeslot)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_mask = SET_BITS(regSSI_NETWORK, CDCTXSECSLOT, 3);
	unsigned int reg_write = 0;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
		/* How to handle primary slot and secondary slot being the same */
		if ((timeslot >= USE_TS0) && (timeslot <= USE_TS3)
		    && (timeslot != vCodec.timeslot)) {
			reg_write =
			    SET_BITS(regSSI_NETWORK, CDCTXSECSLOT, timeslot);

			rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
					    reg_write, reg_mask);

			if (rc == PMIC_SUCCESS) {
				vCodec.secondaryTXtimeslot = timeslot;
			}
		}

	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Get the Voice CODEC secondary recording audio channel timeslot.
 *
 * Get the Voice CODEC secondary audio channel timeslot.
 *
 * @param       handle          Device handle from pmic_audio_open() call.
 * @param       timeslot        The secondary audio channel timeslot.
 *
 * @retval      PMIC_SUCCESS         If the Voice CODEC secondary audio channel
 *                                   timeslot was successfully retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the Voice CODEC secondary audio channel
 *                                   timeslot could not be retrieved.
 */
PMIC_STATUS pmic_audio_vcodec_get_secondary_txslot(const PMIC_AUDIO_HANDLE
						   handle,
						   PMIC_AUDIO_VCODEC_TIMESLOT *
						   const timeslot)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == vCodec.handle) &&
	    (vCodec.handleState == HANDLE_IN_USE) &&
	    (timeslot != (PMIC_AUDIO_VCODEC_TIMESLOT *) NULL)) {
		rc = PMIC_SUCCESS;
		*timeslot = vCodec.secondaryTXtimeslot;
	}

	/* Exit the critical section. */
	up(&mutex);
	return rc;
}

/*!
 * @brief Set/Enable the Voice CODEC options.
 *
 * Set or enable various Voice CODEC options. The available options include
 * the use of dithering, highpass digital filters, and loopback modes.
 *
 * @param       handle          Device handle from pmic_audio_open() call.
 * @param       config          The Voice CODEC options to enable.
 *
 * @retval      PMIC_SUCCESS         If the Voice CODEC options were
 *                                   successfully configured.
 * @retval      PMIC_PARAMETER_ERROR If the handle or Voice CODEC options
 *                                   were invalid.
 * @retval      PMIC_ERROR           If the Voice CODEC options could not be
 *                                   successfully set/enabled.
 */
PMIC_STATUS pmic_audio_vcodec_set_config(const PMIC_AUDIO_HANDLE handle,
					 const PMIC_AUDIO_VCODEC_CONFIG config)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	unsigned int reg_mask = 0;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
		if (config & DITHERING) {
			reg_write = SET_BITS(regAUD_CODEC, CDCDITH, 0);
			reg_mask = SET_BITS(regAUD_CODEC, CDCDITH, 1);
		}

		if (config & INPUT_HIGHPASS_FILTER) {
			reg_write |= SET_BITS(regAUD_CODEC, AUDIHPF, 1);
			reg_mask |= SET_BITS(regAUD_CODEC, AUDIHPF, 1);
		}

		if (config & OUTPUT_HIGHPASS_FILTER) {
			reg_write |= SET_BITS(regAUD_CODEC, AUDOHPF, 1);
			reg_mask |= SET_BITS(regAUD_CODEC, AUDOHPF, 1);
		}

		if (config & DIGITAL_LOOPBACK) {
			reg_write |= SET_BITS(regAUD_CODEC, CDCDLM, 1);
			reg_mask |= SET_BITS(regAUD_CODEC, CDCDLM, 1);
		}

		if (config & ANALOG_LOOPBACK) {
			reg_write |= SET_BITS(regAUD_CODEC, CDCALM, 1);
			reg_mask |= SET_BITS(regAUD_CODEC, CDCALM, 1);
		}

		if (config & VCODEC_MASTER_CLOCK_OUTPUTS) {
			reg_write |= SET_BITS(regAUD_CODEC, CDCCLKEN, 1) |
			    SET_BITS(regAUD_CODEC, CDCTS, 0);
			reg_mask |= SET_BITS(regAUD_CODEC, CDCCLKEN, 1) |
			    SET_BITS(regAUD_CODEC, CDCTS, 1);

		}

		if (config & TRISTATE_TS) {
			reg_write |= SET_BITS(regAUD_CODEC, CDCTS, 1);
			reg_mask |= SET_BITS(regAUD_CODEC, CDCTS, 1);
		}

		if (reg_mask == 0) {
			/* We should not reach this point without having to configure
			 * anything so we flag it as an error.
			 */
			rc = PMIC_ERROR;
		} else {
			rc = pmic_write_reg(REG_AUDIO_CODEC,
					    reg_write, reg_mask);
		}

		if (rc == PMIC_SUCCESS) {
			vCodec.config |= config;
		}
	}
	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Clear/Disable the Voice CODEC options.
 *
 * Clear or disable various Voice CODEC options.
 *
 * @param   handle          Device handle from pmic_audio_open() call.
 * @param   config          The Voice CODEC options to be cleared/disabled.
 *
 * @retval      PMIC_SUCCESS         If the Voice CODEC options were
 *                                   successfully cleared/disabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle or the Voice CODEC options
 *                                   were invalid.
 * @retval      PMIC_ERROR           If the Voice CODEC options could not be
 *                                   cleared/disabled.
 */
PMIC_STATUS pmic_audio_vcodec_clear_config(const PMIC_AUDIO_HANDLE handle,
					   const PMIC_AUDIO_VCODEC_CONFIG
					   config)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	unsigned int reg_mask = 0;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
		if (config & DITHERING) {
			reg_mask = SET_BITS(regAUD_CODEC, CDCDITH, 1);
			reg_write = SET_BITS(regAUD_CODEC, CDCDITH, 1);
		}

		if (config & INPUT_HIGHPASS_FILTER) {
			reg_mask |= SET_BITS(regAUD_CODEC, AUDIHPF, 1);
		}

		if (config & OUTPUT_HIGHPASS_FILTER) {
			reg_mask |= SET_BITS(regAUD_CODEC, AUDOHPF, 1);
		}

		if (config & DIGITAL_LOOPBACK) {
			reg_mask |= SET_BITS(regAUD_CODEC, CDCDLM, 1);
		}

		if (config & ANALOG_LOOPBACK) {
			reg_mask |= SET_BITS(regAUD_CODEC, CDCALM, 1);
		}

		if (config & VCODEC_MASTER_CLOCK_OUTPUTS) {
			reg_mask |= SET_BITS(regAUD_CODEC, CDCCLKEN, 1);
		}

		if (config & TRISTATE_TS) {
			reg_mask |= SET_BITS(regAUD_CODEC, CDCTS, 1);
		}

		if (reg_mask == 0) {
			/* We should not reach this point without having to configure
			 * anything so we flag it as an error.
			 */
			rc = PMIC_ERROR;
		} else {
			rc = pmic_write_reg(REG_AUDIO_CODEC,
					    reg_write, reg_mask);
		}

		if (rc == PMIC_SUCCESS) {
			vCodec.config |= config;
		}

	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Get the current Voice CODEC options.
 *
 * Get the current Voice CODEC options.
 *
 * @param   	handle          Device handle from pmic_audio_open() call.
 * @param	config          The current set of Voice CODEC options.
 *
 * @retval      PMIC_SUCCESS         If the Voice CODEC options were
 *                                   successfully retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the Voice CODEC options could not be
 *                                   retrieved.
 */
PMIC_STATUS pmic_audio_vcodec_get_config(const PMIC_AUDIO_HANDLE handle,
					 PMIC_AUDIO_VCODEC_CONFIG *
					 const config)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == vCodec.handle) &&
	    (vCodec.handleState == HANDLE_IN_USE) &&
	    (config != (PMIC_AUDIO_VCODEC_CONFIG *) NULL)) {
		*config = vCodec.config;

		rc = PMIC_SUCCESS;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Enable the Voice CODEC bypass audio pathway.
 *
 * Enables the Voice CODEC bypass pathway for audio data. This allows direct
 * output of the voltages on the TX data bus line to the output amplifiers
 * (bypassing the digital-to-analog converters within the Voice CODEC).
 *
 * @param       handle          Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the Voice CODEC bypass was successfully
 *                                   enabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the Voice CODEC bypass could not be
 *                                   enabled.
 */
PMIC_STATUS pmic_audio_vcodec_enable_bypass(const PMIC_AUDIO_HANDLE handle)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	const unsigned int reg_write = SET_BITS(regAUD_CODEC, CDCBYP, 1);
	const unsigned int reg_mask = SET_BITS(regAUD_CODEC, CDCBYP, 1);

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
		rc = pmic_write_reg(REG_AUDIO_CODEC, reg_write, reg_mask);
	}

	return rc;
}

/*!
 * @brief Disable the Voice CODEC bypass audio pathway.
 *
 * Disables the Voice CODEC bypass pathway for audio data. This means that
 * the TX data bus line will deliver digital data to the digital-to-analog
 * converters within the Voice CODEC.
 *
 * @param       handle          Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the Voice CODEC bypass was successfully
 *                                   disabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the Voice CODEC bypass could not be
 *                                   disabled.
 */
PMIC_STATUS pmic_audio_vcodec_disable_bypass(const PMIC_AUDIO_HANDLE handle)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	const unsigned int reg_write = 0;
	const unsigned int reg_mask = SET_BITS(regAUD_CODEC, CDCBYP, 1);

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
		rc = pmic_write_reg(REG_AUDIO_CODEC, reg_write, reg_mask);
	}

	return rc;
}

/*@}*/

/*************************************************************************
 * General Stereo DAC configuration.
 *************************************************************************
 */

/*!
 * @name General Stereo DAC Setup and Configuration APIs
 * Functions for general setup and configuration of the PMIC Stereo
 * DAC hardware.
 */
/*@{*/

/*!
 * @brief Set the Stereo DAC clock source and operating characteristics.
 *
 * Define the Stereo DAC clock source and operating characteristics. This
 * must be done before the Stereo DAC is enabled.
 *
 *
 * @param   handle          Device handle from pmic_audio_open() call.
 * @param   clockIn         Select the clock signal source.
 * @param   clockFreq       Select the clock signal frequency.
 * @param   samplingRate    Select the audio data sampling rate.
 * @param   invert          Enable inversion of the frame sync and/or
 *                              bit clock inputs.
 *
 * @retval      PMIC_SUCCESS         If the Stereo DAC clock settings were
 *                                   successfully configured.
 * @retval      PMIC_PARAMETER_ERROR If the handle or clock configuration was
 *                                   invalid.
 * @retval      PMIC_ERROR           If the Stereo DAC clock configuration
 *                                   could not be set.
 */
PMIC_STATUS pmic_audio_stdac_set_clock(const PMIC_AUDIO_HANDLE handle,
				       const PMIC_AUDIO_CLOCK_IN_SOURCE clockIn,
				       const PMIC_AUDIO_STDAC_CLOCK_IN_FREQ
				       clockFreq,
				       const PMIC_AUDIO_STDAC_SAMPLING_RATE
				       samplingRate,
				       const PMIC_AUDIO_CLOCK_INVERT invert)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_value = 0;
	unsigned int reg_mask = 0;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;
	/* Validate all of the calling parameters. */
	if (handle == (PMIC_AUDIO_HANDLE) NULL) {
		rc = PMIC_PARAMETER_ERROR;
	} else if ((handle == stDAC.handle) &&
		   (stDAC.handleState == HANDLE_IN_USE)) {
		if ((clockIn != CLOCK_IN_CLIA) && (clockIn != CLOCK_IN_CLIB)) {
			rc = PMIC_PARAMETER_ERROR;
		} else if ((stDAC.masterSlave == BUS_MASTER_MODE)
			   && !((clockFreq >= STDAC_CLI_3_36864MHZ)
				&& (clockFreq <= STDAC_CLI_33_6MHZ))) {
			rc = PMIC_PARAMETER_ERROR;
		} else if ((stDAC.masterSlave == BUS_SLAVE_MODE)
			   && !((clockFreq >= STDAC_MCLK_PLL_DISABLED)
				&& (clockFreq <= STDAC_BCLK_IN_PLL))) {
			rc = PMIC_PARAMETER_ERROR;
		} else if (!((samplingRate >= STDAC_RATE_8_KHZ)
			     && (samplingRate <= STDAC_RATE_96_KHZ))) {
			rc = PMIC_PARAMETER_ERROR;
		}
		/*
		   else if(!((invert >= NO_INVERT) && (invert <= INVERT_FRAMESYNC)))
		   {
		   rc = PMIC_PARAMETER_ERROR;
		   } */
		else {
			reg_mask = SET_BITS(regST_DAC, STDCCLK, 7) |
			    SET_BITS(regST_DAC, STDCCLKSEL, 1) |
			    SET_BITS(regST_DAC, SR, 15) |
			    SET_BITS(regST_DAC, STDCBCLINV, 1) |
			    SET_BITS(regST_DAC, STDCFSINV, 1);
			if (clockIn == CLOCK_IN_CLIA) {
				reg_value = SET_BITS(regST_DAC, STDCCLKSEL, 0);
			} else {
				reg_value = SET_BITS(regST_DAC, STDCCLKSEL, 1);
			}
			/* How to take care of sample rates in SLAVE mode */
			if ((clockFreq == STDAC_CLI_3_36864MHZ)
			    || ((clockFreq == STDAC_FSYNC_IN_PLL))) {
				reg_value |= SET_BITS(regST_DAC, STDCCLK, 6);
			} else if ((clockFreq == STDAC_CLI_12MHZ)
				   || (clockFreq == STDAC_MCLK_PLL_DISABLED)) {
				reg_value |= SET_BITS(regST_DAC, STDCCLK, 5);
			} else if (clockFreq == STDAC_CLI_13MHZ) {
				reg_value |= SET_BITS(regST_DAC, STDCCLK, 0);
			} else if (clockFreq == STDAC_CLI_15_36MHZ) {
				reg_value |= SET_BITS(regST_DAC, STDCCLK, 1);
			} else if (clockFreq == STDAC_CLI_16_8MHZ) {
				reg_value |= SET_BITS(regST_DAC, STDCCLK, 2);
			} else if (clockFreq == STDAC_CLI_26MHZ) {
				reg_value |= SET_BITS(regST_DAC, STDCCLK, 4);
			} else if ((clockFreq == STDAC_CLI_33_6MHZ)
				   || (clockFreq == STDAC_BCLK_IN_PLL)) {
				reg_value |= SET_BITS(regST_DAC, STDCCLK, 7);
			}

			reg_value |= SET_BITS(regST_DAC, SR, samplingRate);

			if (invert & INVERT_BITCLOCK) {
				reg_value |= SET_BITS(regST_DAC, STDCBCLINV, 1);
			}
			if (invert & INVERT_FRAMESYNC) {
				reg_value |= SET_BITS(regST_DAC, STDCFSINV, 1);
			}
			if (invert & NO_INVERT) {
				reg_value |= SET_BITS(regST_DAC, STDCBCLINV, 0);
				reg_value |= SET_BITS(regST_DAC, STDCFSINV, 0);
			}
			if (pmic_write_reg
			    (REG_AUDIO_STEREO_DAC, reg_value,
			     reg_mask) != PMIC_SUCCESS) {
				rc = PMIC_ERROR;
			} else {
				pr_debug("STDAC clock set\n");
				rc = PMIC_SUCCESS;
				stDAC.clockIn = clockIn;
				stDAC.clockFreq = clockFreq;
				stDAC.samplingRate = samplingRate;
				stDAC.invert = invert;
			}

		}

	} else {
		rc = PMIC_PARAMETER_ERROR;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Get the Stereo DAC clock source and operating characteristics.
 *
 * Get the current Stereo DAC clock source and operating characteristics.
 *
 * @param  handle          Device handle from pmic_audio_open() call.
 * @param  clockIn         The clock signal source.
 * @param  clockFreq       The clock signal frequency.
 * @param  samplingRate    The audio data sampling rate.
 * @param  invert          Inversion of the frame sync and/or
 *                         bit clock inputs is enabled/disabled.
 *
 * @retval      PMIC_SUCCESS         If the Stereo DAC clock settings were
 *                                   successfully retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle invalid.
 * @retval      PMIC_ERROR           If the Stereo DAC clock configuration
 *                                   could not be retrieved.
 */
PMIC_STATUS pmic_audio_stdac_get_clock(const PMIC_AUDIO_HANDLE handle,
				       PMIC_AUDIO_CLOCK_IN_SOURCE *
				       const clockIn,
				       PMIC_AUDIO_STDAC_SAMPLING_RATE *
				       const samplingRate,
				       PMIC_AUDIO_STDAC_CLOCK_IN_FREQ *
				       const clockFreq,
				       PMIC_AUDIO_CLOCK_INVERT * const invert)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == stDAC.handle) &&
	    (stDAC.handleState == HANDLE_IN_USE) &&
	    (clockIn != (PMIC_AUDIO_CLOCK_IN_SOURCE *) NULL) &&
	    (samplingRate != (PMIC_AUDIO_STDAC_SAMPLING_RATE *) NULL) &&
	    (clockFreq != (PMIC_AUDIO_STDAC_CLOCK_IN_FREQ *) NULL) &&
	    (invert != (PMIC_AUDIO_CLOCK_INVERT *) NULL)) {
		*clockIn = stDAC.clockIn;
		*samplingRate = stDAC.samplingRate;
		*clockFreq = stDAC.clockFreq;
		*invert = stDAC.invert;
		rc = PMIC_SUCCESS;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Set the Stereo DAC primary audio channel timeslot.
 *
 * Set the Stereo DAC primary audio channel timeslot. This function must be
 * used if the default timeslot for the primary audio channel is to be changed.
 *
 * @param       handle          Device handle from pmic_audio_open() call.
 * @param	timeslot        Select the primary audio channel timeslot.
 *
 * @retval      PMIC_SUCCESS         If the Stereo DAC primary audio channel
 *                                   timeslot was successfully configured.
 * @retval      PMIC_PARAMETER_ERROR If the handle or audio channel timeslot
 *                                   was invalid.
 * @retval      PMIC_ERROR           If the Stereo DAC primary audio channel
 *                                   timeslot could not be set.
 */
PMIC_STATUS pmic_audio_stdac_set_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
					       const PMIC_AUDIO_STDAC_TIMESLOTS
					       timeslot)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_mask = SET_BITS(regSSI_NETWORK, STDCRXSLOT, 3);

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
		if ((timeslot == USE_TS0_TS1) || (timeslot == USE_TS2_TS3)
		    || (timeslot == USE_TS4_TS5) || (timeslot == USE_TS6_TS7)) {
			if (pmic_write_reg
			    (REG_AUDIO_SSI_NETWORK, timeslot,
			     reg_mask) != PMIC_SUCCESS) {
				rc = PMIC_ERROR;
			} else {
				pr_debug("STDAC primary timeslot set\n");
				stDAC.timeslot = timeslot;
				rc = PMIC_SUCCESS;
			}

		} else {
			rc = PMIC_PARAMETER_ERROR;
		}
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Get the current Stereo DAC primary audio channel timeslot.
 *
 * Get the current Stereo DAC primary audio channel timeslot.
 *
 * @param   handle          Device handle from pmic_audio_open() call.
 * @param   timeslot        The primary audio channel timeslot.
 *
 * @retval      PMIC_SUCCESS         If the Stereo DAC primary audio channel
 *                                   timeslot was successfully retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the Stereo DAC primary audio channel
 *                                   timeslot could not be retrieved.
 */
PMIC_STATUS pmic_audio_stdac_get_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
					       PMIC_AUDIO_STDAC_TIMESLOTS *
					       const timeslot)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == stDAC.handle) &&
	    (stDAC.handleState == HANDLE_IN_USE) &&
	    (timeslot != (PMIC_AUDIO_STDAC_TIMESLOTS *) NULL)) {
		*timeslot = stDAC.timeslot;

		rc = PMIC_SUCCESS;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Set/Enable the Stereo DAC options.
 *
 * Set or enable various Stereo DAC options. The available options include
 * resetting the digital filter and enabling the bus master clock outputs.
 *
 * @param   handle          Device handle from pmic_audio_open() call.
 * @param   config          The Stereo DAC options to enable.
 *
 * @retval      PMIC_SUCCESS         If the Stereo DAC options were
 *                                   successfully configured.
 * @retval      PMIC_PARAMETER_ERROR If the handle or Stereo DAC options
 *                                   were invalid.
 * @retval      PMIC_ERROR           If the Stereo DAC options could not be
 *                                   successfully set/enabled.
 */
PMIC_STATUS pmic_audio_stdac_set_config(const PMIC_AUDIO_HANDLE handle,
					const PMIC_AUDIO_STDAC_CONFIG config)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	unsigned int reg_mask = 0;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
		if (config & STDAC_MASTER_CLOCK_OUTPUTS) {
			reg_write |= SET_BITS(regST_DAC, STDCCLKEN, 1);
			reg_mask |= SET_BITS(regST_DAC, STDCCLKEN, 1);
		}

		rc = pmic_write_reg(REG_AUDIO_STEREO_DAC, reg_write, reg_mask);

		if (rc == PMIC_SUCCESS) {
			stDAC.config |= config;
			pr_debug("STDAC config set\n");

		}
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Clear/Disable the Stereo DAC options.
 *
 * Clear or disable various Stereo DAC options.
 *
 * @param       handle          Device handle from pmic_audio_open() call.
 * @param       config          The Stereo DAC options to be cleared/disabled.
 *
 * @retval      PMIC_SUCCESS         If the Stereo DAC options were
 *                                   successfully cleared/disabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle or the Stereo DAC options
 *                                   were invalid.
 * @retval      PMIC_ERROR           If the Stereo DAC options could not be
 *                                   cleared/disabled.
 */
PMIC_STATUS pmic_audio_stdac_clear_config(const PMIC_AUDIO_HANDLE handle,
					  const PMIC_AUDIO_STDAC_CONFIG config)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	unsigned int reg_write = 0;
	unsigned int reg_mask = 0;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {

		if (config & STDAC_MASTER_CLOCK_OUTPUTS) {
			reg_mask |= SET_BITS(regST_DAC, STDCCLKEN, 1);
		}

		if (reg_mask != 0) {
			rc = pmic_write_reg(REG_AUDIO_STEREO_DAC,
					    reg_write, reg_mask);

			if (rc == PMIC_SUCCESS) {
				stDAC.config &= ~config;
			}
		}
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Get the current Stereo DAC options.
 *
 * Get the current Stereo DAC options.
 *
 * @param   	handle          Device handle from pmic_audio_open() call.
 * @param   	config          The current set of Stereo DAC options.
 *
 * @retval      PMIC_SUCCESS         If the Stereo DAC options were
 *                                   successfully retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the Stereo DAC options could not be
 *                                   retrieved.
 */
PMIC_STATUS pmic_audio_stdac_get_config(const PMIC_AUDIO_HANDLE handle,
					PMIC_AUDIO_STDAC_CONFIG * const config)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == stDAC.handle) &&
	    (stDAC.handleState == HANDLE_IN_USE) &&
	    (config != (PMIC_AUDIO_STDAC_CONFIG *) NULL)) {
		*config = stDAC.config;

		rc = PMIC_SUCCESS;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*@}*/

/*************************************************************************
 * Audio input section configuration.
 *************************************************************************
 */

/*!
 * @name Audio Input Setup and Configuration APIs
 * Functions for general setup and configuration of the PMIC audio
 * input hardware.
 */
/*@{*/

/*!
 * @brief Set/Enable the audio input section options.
 *
 * Set or enable various audio input section options. The only available
 * option right now is to enable the automatic disabling of the microphone
 * input amplifiers when a microphone/headset is inserted or removed.
 * NOT SUPPORTED BY MC13783
 *
 * @param  	handle          Device handle from pmic_audio_open() call.
 * @param   	config          The audio input section options to enable.
 *
 * @retval      PMIC_SUCCESS         If the audio input section options were
 *                                   successfully configured.
 * @retval      PMIC_PARAMETER_ERROR If the handle or audio input section
 *                                   options were invalid.
 * @retval      PMIC_ERROR           If the audio input section options could
 *                                   not be successfully set/enabled.
 */
PMIC_STATUS pmic_audio_input_set_config(const PMIC_AUDIO_HANDLE handle,
					const PMIC_AUDIO_INPUT_CONFIG config)
{
	PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
	return rc;
}

/*!
 * @brief Clear/Disable the audio input section options.
 *
 * Clear or disable various audio input section options.
 *
 * @param   	handle          Device handle from pmic_audio_open() call.
 * @param   	config          The audio input section options to be
 *                              cleared/disabled.
 * NOT SUPPORTED BY MC13783
 *
 * @retval      PMIC_SUCCESS         If the audio input section options were
 *                                   successfully cleared/disabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle or the audio input section
 *                                   options were invalid.
 * @retval      PMIC_ERROR           If the audio input section options could
 *                                   not be cleared/disabled.
 */
PMIC_STATUS pmic_audio_input_clear_config(const PMIC_AUDIO_HANDLE handle,
					  const PMIC_AUDIO_INPUT_CONFIG config)
{
	PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
	return rc;

}

/*!
 * @brief Get the current audio input section options.
 *
 * Get the current audio input section options.
 *
 * @param[in]   handle          Device handle from pmic_audio_open() call.
 * @param[out]  config          The current set of audio input section options.
 * NOT SUPPORTED BY MC13783
 *
 * @retval      PMIC_SUCCESS         If the audio input section options were
 *                                   successfully retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the audio input section options could
 *                                   not be retrieved.
 */
PMIC_STATUS pmic_audio_input_get_config(const PMIC_AUDIO_HANDLE handle,
					PMIC_AUDIO_INPUT_CONFIG * const config)
{
	PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
	return rc;
}

/*@}*/

/*************************************************************************
 * Audio recording using the Voice CODEC.
 *************************************************************************
 */

/*!
 * @name Audio Recording Using the Voice CODEC Setup and Configuration APIs
 * Functions for general setup and configuration of the PMIC Voice CODEC
 * to perform audio recording.
 */
/*@{*/

/*!
 * @brief Select the microphone inputs to be used for Voice CODEC recording.
 *
 * Select left (mc13783-only) and right microphone inputs for Voice CODEC
 * recording. It is possible to disable or not use a particular microphone
 * input channel by specifying NO_MIC as a parameter.
 *
 * @param       handle          Device handle from pmic_audio_open() call.
 * @param       leftChannel     Select the left microphone input channel.
 * @param       rightChannel    Select the right microphone input channel.
 *
 * @retval      PMIC_SUCCESS         If the microphone input channels were
 *                                   successfully enabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle or microphone input ports
 *                                   were invalid.
 * @retval      PMIC_ERROR           If the microphone input channels could
 *                                   not be successfully enabled.
 */
PMIC_STATUS pmic_audio_vcodec_set_mic(const PMIC_AUDIO_HANDLE handle,
				      const PMIC_AUDIO_INPUT_PORT leftChannel,
				      const PMIC_AUDIO_INPUT_PORT rightChannel)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	unsigned int reg_mask = 0;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
		if (!((leftChannel == NO_MIC) || (leftChannel == MIC1_LEFT))) {
			rc = PMIC_PARAMETER_ERROR;
		} else if (!((rightChannel == NO_MIC)
			     || (rightChannel == MIC1_RIGHT_MIC_MONO)
			     || (rightChannel == TXIN_EXT)
			     || (rightChannel == MIC2_AUX))) {
			rc = PMIC_PARAMETER_ERROR;
		} else {
			if (leftChannel == NO_MIC) {
			} else {	/* Left channel MIC enable */
				reg_mask = SET_BITS(regAUDIO_TX, AMC1LEN, 1) |
				    SET_BITS(regAUDIO_TX, RXINREC, 1);
				reg_write = SET_BITS(regAUDIO_TX, AMC1LEN, 1) |
				    SET_BITS(regAUDIO_TX, RXINREC, 0);
			}
			/*For right channel enable one and clear the other two as well as RXINREC */
			if (rightChannel == NO_MIC) {
			} else if (rightChannel == MIC1_RIGHT_MIC_MONO) {
				reg_mask |= SET_BITS(regAUDIO_TX, AMC1REN, 1) |
				    SET_BITS(regAUDIO_TX, RXINREC, 1) |
				    SET_BITS(regAUDIO_TX, AMC2EN, 1) |
				    SET_BITS(regAUDIO_TX, ATXINEN, 1);
				reg_write |= SET_BITS(regAUDIO_TX, AMC1REN, 1) |
				    SET_BITS(regAUDIO_TX, RXINREC, 0) |
				    SET_BITS(regAUDIO_TX, AMC2EN, 0) |
				    SET_BITS(regAUDIO_TX, ATXINEN, 0);
			} else if (rightChannel == MIC2_AUX) {
				reg_mask |= SET_BITS(regAUDIO_TX, AMC1REN, 1) |
				    SET_BITS(regAUDIO_TX, RXINREC, 1) |
				    SET_BITS(regAUDIO_TX, AMC2EN, 1) |
				    SET_BITS(regAUDIO_TX, ATXINEN, 1);
				reg_write |= SET_BITS(regAUDIO_TX, AMC1REN, 0) |
				    SET_BITS(regAUDIO_TX, RXINREC, 0) |
				    SET_BITS(regAUDIO_TX, AMC2EN, 1) |
				    SET_BITS(regAUDIO_TX, ATXINEN, 0);
			} else {	/* TX line in */
				reg_mask |= SET_BITS(regAUDIO_TX, AMC1REN, 1) |
				    SET_BITS(regAUDIO_TX, RXINREC, 1) |
				    SET_BITS(regAUDIO_TX, AMC2EN, 1) |
				    SET_BITS(regAUDIO_TX, ATXINEN, 1);
				reg_write |= SET_BITS(regAUDIO_TX, AMC1REN, 0) |
				    SET_BITS(regAUDIO_TX, RXINREC, 0) |
				    SET_BITS(regAUDIO_TX, AMC2EN, 0) |
				    SET_BITS(regAUDIO_TX, ATXINEN, 1);
			}

			if (reg_mask == 0) {
				rc = PMIC_PARAMETER_ERROR;
			} else {
				rc = pmic_write_reg(REG_AUDIO_TX,
						    reg_write, reg_mask);

				if (rc == PMIC_SUCCESS) {
					pr_debug
					    ("MIC inputs configured successfully\n");
					vCodec.leftChannelMic.mic = leftChannel;
					vCodec.rightChannelMic.mic =
					    rightChannel;

				}
			}
		}
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Get the current microphone inputs being used for Voice CODEC
 *        recording.
 *
 * Get the left (mc13783-only) and right microphone inputs currently being
 * used for Voice CODEC recording.
 *
 * @param   	handle          Device handle from pmic_audio_open() call.
 * @param 	leftChannel     The left microphone input channel.
 * @param	rightChannel    The right microphone input channel.
 *
 * @retval      PMIC_SUCCESS         If the microphone input channels were
 *                                   successfully retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the microphone input channels could
 *                                   not be retrieved.
 */
PMIC_STATUS pmic_audio_vcodec_get_mic(const PMIC_AUDIO_HANDLE handle,
				      PMIC_AUDIO_INPUT_PORT * const leftChannel,
				      PMIC_AUDIO_INPUT_PORT *
				      const rightChannel)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == vCodec.handle) &&
	    (vCodec.handleState == HANDLE_IN_USE) &&
	    (leftChannel != (PMIC_AUDIO_INPUT_PORT *) NULL) &&
	    (rightChannel != (PMIC_AUDIO_INPUT_PORT *) NULL)) {
		*leftChannel = vCodec.leftChannelMic.mic;
		*rightChannel = vCodec.rightChannelMic.mic;
		rc = PMIC_SUCCESS;
	}

	/* Exit the critical section. */
	up(&mutex);
	return rc;
}

/*!
 * @brief Enable/disable the microphone input.
 *
 * This function enables/disables the current microphone input channel. The
 * input amplifier is automatically turned off when the microphone input is
 * disabled.
 *
 * @param       handle          Device handle from pmic_audio_open() call.
 * @param       leftChannel     The left microphone input channel state.
 * @param       rightChannel    the right microphone input channel state.
 *
 * @retval      PMIC_SUCCESS         If the microphone input channels were
 *                                   successfully reconfigured.
 * @retval      PMIC_PARAMETER_ERROR If the handle or microphone input states
 *                                   were invalid.
 * @retval      PMIC_ERROR           If the microphone input channels could
 *                                   not be reconfigured.
 */
PMIC_STATUS pmic_audio_vcodec_set_mic_on_off(const PMIC_AUDIO_HANDLE handle,
					     const PMIC_AUDIO_INPUT_MIC_STATE
					     leftChannel,
					     const PMIC_AUDIO_INPUT_MIC_STATE
					     rightChannel)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	unsigned int reg_mask = 0;
	unsigned int curr_left = 0;
	unsigned int curr_right = 0;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
		curr_left = vCodec.leftChannelMic.mic;
		curr_right = vCodec.rightChannelMic.mic;
		if ((curr_left == NO_MIC) && (curr_right == NO_MIC)) {
			rc = PMIC_PARAMETER_ERROR;
		} else {
			if (curr_left == MIC1_LEFT) {
				if ((leftChannel == MICROPHONE_ON) &&
				    (vCodec.leftChannelMic.micOnOff ==
				     MICROPHONE_OFF)) {
					/* Enable the microphone */
					reg_mask |=
					    SET_BITS(regAUDIO_TX, AMC1LEN,
						     1) | SET_BITS(regAUDIO_TX,
								   RXINREC, 1);
					reg_write |=
					    SET_BITS(regAUDIO_TX, AMC1LEN,
						     1) | SET_BITS(regAUDIO_TX,
								   RXINREC, 0);

				} else if ((leftChannel == MICROPHONE_OFF) &&
					   (vCodec.leftChannelMic.micOnOff ==
					    MICROPHONE_ON)) {
					/* Disable the microphone */
					reg_mask |=
					    SET_BITS(regAUDIO_TX, AMC1LEN,
						     1) | SET_BITS(regAUDIO_TX,
								   RXINREC, 1);
					reg_write |=
					    SET_BITS(regAUDIO_TX, AMC1LEN,
						     0) | SET_BITS(regAUDIO_TX,
								   RXINREC, 0);

				} else {
					/* Both are in same state . Nothing to be done */
				}

			}
			if (curr_right == MIC1_RIGHT_MIC_MONO) {
				if ((rightChannel == MICROPHONE_ON) &&
				    (vCodec.leftChannelMic.micOnOff ==
				     MICROPHONE_OFF)) {
					/* Enable the microphone */
					reg_mask |=
					    SET_BITS(regAUDIO_TX, AMC1REN,
						     1) | SET_BITS(regAUDIO_TX,
								   RXINREC,
								   1) |
					    SET_BITS(regAUDIO_TX, AMC2EN,
						     1) | SET_BITS(regAUDIO_TX,
								   ATXINEN, 1);
					reg_write |=
					    SET_BITS(regAUDIO_TX, AMC1REN,
						     1) | SET_BITS(regAUDIO_TX,
								   RXINREC,
								   0) |
					    SET_BITS(regAUDIO_TX, AMC2EN,
						     0) | SET_BITS(regAUDIO_TX,
								   ATXINEN, 0);
				} else if ((rightChannel == MICROPHONE_OFF)
					   && (vCodec.leftChannelMic.micOnOff ==
					       MICROPHONE_ON)) {
					/* Disable the microphone */
					reg_mask |=
					    SET_BITS(regAUDIO_TX, AMC1REN,
						     1) | SET_BITS(regAUDIO_TX,
								   RXINREC,
								   1) |
					    SET_BITS(regAUDIO_TX, AMC2EN,
						     1) | SET_BITS(regAUDIO_TX,
								   ATXINEN, 1);
					reg_write |=
					    SET_BITS(regAUDIO_TX, AMC1REN,
						     0) | SET_BITS(regAUDIO_TX,
								   RXINREC,
								   0) |
					    SET_BITS(regAUDIO_TX, AMC2EN,
						     0) | SET_BITS(regAUDIO_TX,
								   ATXINEN, 0);
				} else {
					/* Both are in same state . Nothing to be done */
				}
			} else if (curr_right == MIC2_AUX) {
				if ((rightChannel == MICROPHONE_ON)
				    && (vCodec.leftChannelMic.micOnOff ==
					MICROPHONE_OFF)) {
					/* Enable the microphone */
					reg_mask |=
					    SET_BITS(regAUDIO_TX, AMC1REN,
						     1) | SET_BITS(regAUDIO_TX,
								   RXINREC,
								   1) |
					    SET_BITS(regAUDIO_TX, AMC2EN,
						     1) | SET_BITS(regAUDIO_TX,
								   ATXINEN, 1);
					reg_write |=
					    SET_BITS(regAUDIO_TX, AMC1REN,
						     0) | SET_BITS(regAUDIO_TX,
								   RXINREC,
								   0) |
					    SET_BITS(regAUDIO_TX, AMC2EN,
						     1) | SET_BITS(regAUDIO_TX,
								   ATXINEN, 0);
				} else if ((rightChannel == MICROPHONE_OFF)
					   && (vCodec.leftChannelMic.micOnOff ==
					       MICROPHONE_ON)) {
					/* Disable the microphone */
					reg_mask |=
					    SET_BITS(regAUDIO_TX, AMC1REN,
						     1) | SET_BITS(regAUDIO_TX,
								   RXINREC,
								   1) |
					    SET_BITS(regAUDIO_TX, AMC2EN,
						     1) | SET_BITS(regAUDIO_TX,
								   ATXINEN, 1);
					reg_write |=
					    SET_BITS(regAUDIO_TX, AMC1REN,
						     0) | SET_BITS(regAUDIO_TX,
								   RXINREC,
								   0) |
					    SET_BITS(regAUDIO_TX, AMC2EN,
						     0) | SET_BITS(regAUDIO_TX,
								   ATXINEN, 0);
				} else {
					/* Both are in same state . Nothing to be done */
				}
			} else if (curr_right == TXIN_EXT) {
				if ((rightChannel == MICROPHONE_ON)
				    && (vCodec.leftChannelMic.micOnOff ==
					MICROPHONE_OFF)) {
					/* Enable the microphone */
					reg_mask |=
					    SET_BITS(regAUDIO_TX, AMC1REN,
						     1) | SET_BITS(regAUDIO_TX,
								   RXINREC,
								   1) |
					    SET_BITS(regAUDIO_TX, AMC2EN,
						     1) | SET_BITS(regAUDIO_TX,
								   ATXINEN, 1);
					reg_write |=
					    SET_BITS(regAUDIO_TX, AMC1REN,
						     0) | SET_BITS(regAUDIO_TX,
								   RXINREC,
								   0) |
					    SET_BITS(regAUDIO_TX, AMC2EN,
						     0) | SET_BITS(regAUDIO_TX,
								   ATXINEN, 1);
				} else if ((rightChannel == MICROPHONE_OFF)
					   && (vCodec.leftChannelMic.micOnOff ==
					       MICROPHONE_ON)) {
					/* Disable the microphone */
					reg_mask |=
					    SET_BITS(regAUDIO_TX, AMC1REN,
						     1) | SET_BITS(regAUDIO_TX,
								   RXINREC,
								   1) |
					    SET_BITS(regAUDIO_TX, AMC2EN,
						     1) | SET_BITS(regAUDIO_TX,
								   ATXINEN, 1);
					reg_write |=
					    SET_BITS(regAUDIO_TX, AMC1REN,
						     0) | SET_BITS(regAUDIO_TX,
								   RXINREC,
								   0) |
					    SET_BITS(regAUDIO_TX, AMC2EN,
						     0) | SET_BITS(regAUDIO_TX,
								   ATXINEN, 0);
				} else {
					/* Both are in same state . Nothing to be done */
				}
			}
			if (reg_mask == 0) {
			} else {
				rc = pmic_write_reg(REG_AUDIO_TX,
						    reg_write, reg_mask);

				if (rc == PMIC_SUCCESS) {
					pr_debug
					    ("MIC states configured successfully\n");
					vCodec.leftChannelMic.micOnOff =
					    leftChannel;
					vCodec.rightChannelMic.micOnOff =
					    rightChannel;
				}
			}
		}

	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Return the current state of the microphone inputs.
 *
 * This function returns the current state (on/off) of the microphone
 * input channels.
 *
 * @param       handle          Device handle from pmic_audio_open() call.
 * @param	leftChannel     The current left microphone input channel
 *                              state.
 * @param	rightChannel    the current right microphone input channel
 *                              state.
 *
 * @retval      PMIC_SUCCESS         If the microphone input channel states
 *                                   were successfully retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the microphone input channel states
 *                                   could not be retrieved.
 */
PMIC_STATUS pmic_audio_vcodec_get_mic_on_off(const PMIC_AUDIO_HANDLE handle,
					     PMIC_AUDIO_INPUT_MIC_STATE *
					     const leftChannel,
					     PMIC_AUDIO_INPUT_MIC_STATE *
					     const rightChannel)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == vCodec.handle) &&
	    (vCodec.handleState == HANDLE_IN_USE) &&
	    (leftChannel != (PMIC_AUDIO_INPUT_MIC_STATE *) NULL) &&
	    (rightChannel != (PMIC_AUDIO_INPUT_MIC_STATE *) NULL)) {
		*leftChannel = vCodec.leftChannelMic.micOnOff;
		*rightChannel = vCodec.rightChannelMic.micOnOff;

		rc = PMIC_SUCCESS;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Set the microphone input amplifier mode and gain level.
 *
 * This function sets the current microphone input amplifier operating mode
 * and gain level.
 *
 * @param       handle           Device handle from pmic_audio_open() call.
 * @param       leftChannelMode  The left microphone input amplifier mode.
 * @param       leftChannelGain  The left microphone input amplifier gain level.
 * @param       rightChannelMode The right microphone input amplifier mode.
 * @param       rightChannelGain The right microphone input amplifier gain
 *                               level.
 *
 * @retval      PMIC_SUCCESS         If the microphone input amplifiers were
 *                                   successfully reconfigured.
 * @retval      PMIC_PARAMETER_ERROR If the handle or microphone input amplifier
 *                                   modes or gain levels were invalid.
 * @retval      PMIC_ERROR           If the microphone input amplifiers could
 *                                   not be reconfigured.
 */
PMIC_STATUS pmic_audio_vcodec_set_record_gain(const PMIC_AUDIO_HANDLE handle,
					      const PMIC_AUDIO_MIC_AMP_MODE
					      leftChannelMode,
					      const PMIC_AUDIO_MIC_GAIN
					      leftChannelGain,
					      const PMIC_AUDIO_MIC_AMP_MODE
					      rightChannelMode,
					      const PMIC_AUDIO_MIC_GAIN
					      rightChannelGain)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	unsigned int reg_mask = 0;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
		if (!(((leftChannelGain >= MIC_GAIN_MINUS_8DB)
		       && (leftChannelGain <= MIC_GAIN_PLUS_23DB))
		      && ((rightChannelGain >= MIC_GAIN_MINUS_8DB)
			  && (rightChannelGain <= MIC_GAIN_PLUS_23DB)))) {
			rc = PMIC_PARAMETER_ERROR;
			pr_debug("VCODEC set record gain - wrong gain value\n");
		} else if (((leftChannelMode != AMP_OFF)
			    && (leftChannelMode != VOLTAGE_TO_VOLTAGE)
			    && (leftChannelMode != CURRENT_TO_VOLTAGE))
			   || ((rightChannelMode != VOLTAGE_TO_VOLTAGE)
			       && (rightChannelMode != CURRENT_TO_VOLTAGE)
			       && (rightChannelMode != AMP_OFF))) {
			rc = PMIC_PARAMETER_ERROR;
			pr_debug("VCODEC set record gain - wrong amp mode\n");
		} else {
			if (vCodec.leftChannelMic.mic == MIC1_LEFT) {
				reg_mask = SET_BITS(regAUDIO_TX, AMC1LITOV, 1) |
				    SET_BITS(regAUDIO_TX, PGATXL, 31);
				if (leftChannelMode == VOLTAGE_TO_VOLTAGE) {
					reg_write =
					    SET_BITS(regAUDIO_TX, AMC1LITOV, 0);
				} else {
					reg_write =
					    SET_BITS(regAUDIO_TX, AMC1LITOV, 1);
				}
				reg_write |=
				    SET_BITS(regAUDIO_TX, PGATXL,
					     leftChannelGain);
			}
			if (vCodec.rightChannelMic.mic == MIC1_RIGHT_MIC_MONO) {
				reg_mask |=
				    SET_BITS(regAUDIO_TX, AMC1RITOV,
					     1) | SET_BITS(regAUDIO_TX, PGATXR,
							   31);
				if (rightChannelMode == VOLTAGE_TO_VOLTAGE) {
					reg_write |=
					    SET_BITS(regAUDIO_TX, AMC1RITOV, 0);
				} else {
					reg_write |=
					    SET_BITS(regAUDIO_TX, AMC1RITOV, 1);
				}
				reg_write |=
				    SET_BITS(regAUDIO_TX, PGATXR,
					     rightChannelGain);
			} else if (vCodec.rightChannelMic.mic == MIC2_AUX) {
				reg_mask |= SET_BITS(regAUDIO_TX, AMC2ITOV, 1);
				reg_mask |= SET_BITS(regAUDIO_TX, PGATXR, 31);
				if (rightChannelMode == VOLTAGE_TO_VOLTAGE) {
					reg_write |=
					    SET_BITS(regAUDIO_TX, AMC2ITOV, 0);
				} else {
					reg_write |=
					    SET_BITS(regAUDIO_TX, AMC2ITOV, 1);
				}
				reg_write |=
				    SET_BITS(regAUDIO_TX, PGATXR,
					     rightChannelGain);
			} else if (vCodec.rightChannelMic.mic == TXIN_EXT) {
				reg_mask |= SET_BITS(regAUDIO_TX, PGATXR, 31);
				/* No current to voltage option for TX IN amplifier */
				reg_write |=
				    SET_BITS(regAUDIO_TX, PGATXR,
					     rightChannelGain);
			}

			if (reg_mask == 0) {
			} else {
				rc = pmic_write_reg(REG_AUDIO_TX,
						    reg_write, reg_mask);
				reg_write =
				    SET_BITS(regAUDIO_TX, PGATXL,
					     leftChannelGain);
				reg_mask = SET_BITS(regAUDIO_TX, PGATXL, 31);
				rc = pmic_write_reg(REG_AUDIO_TX,
						    reg_write, reg_mask);

				if (rc == PMIC_SUCCESS) {
					pr_debug("MIC amp mode and gain set\n");
					vCodec.leftChannelMic.ampMode =
					    leftChannelMode;
					vCodec.leftChannelMic.gain =
					    leftChannelGain;
					vCodec.rightChannelMic.ampMode =
					    rightChannelMode;
					vCodec.rightChannelMic.gain =
					    rightChannelGain;

				}
			}
		}
	}

	/* Exit critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Get the current microphone input amplifier mode and gain level.
 *
 * This function gets the current microphone input amplifier operating mode
 * and gain level.
 *
 * @param	handle           Device handle from pmic_audio_open() call.
 * @param	leftChannelMode  The left microphone input amplifier mode.
 * @param	leftChannelGain  The left microphone input amplifier gain level.
 * @param 	rightChannelMode The right microphone input amplifier mode.
 * @param       rightChannelGain The right microphone input amplifier gain
 *                               level.
 *
 * @retval      PMIC_SUCCESS         If the microphone input amplifier modes
 *                                   and gain levels were successfully
 *                                   retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the microphone input amplifier modes
 *                                   and gain levels could not be retrieved.
 */
PMIC_STATUS pmic_audio_vcodec_get_record_gain(const PMIC_AUDIO_HANDLE handle,
					      PMIC_AUDIO_MIC_AMP_MODE *
					      const leftChannelMode,
					      PMIC_AUDIO_MIC_GAIN *
					      const leftChannelGain,
					      PMIC_AUDIO_MIC_AMP_MODE *
					      const rightChannelMode,
					      PMIC_AUDIO_MIC_GAIN *
					      const rightChannelGain)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == vCodec.handle) &&
	    (vCodec.handleState == HANDLE_IN_USE) &&
	    (leftChannelMode != (PMIC_AUDIO_MIC_AMP_MODE *) NULL) &&
	    (leftChannelGain != (PMIC_AUDIO_MIC_GAIN *) NULL) &&
	    (rightChannelMode != (PMIC_AUDIO_MIC_AMP_MODE *) NULL) &&
	    (rightChannelGain != (PMIC_AUDIO_MIC_GAIN *) NULL)) {
		*leftChannelMode = vCodec.leftChannelMic.ampMode;
		*leftChannelGain = vCodec.leftChannelMic.gain;
		*rightChannelMode = vCodec.rightChannelMic.ampMode;
		*rightChannelGain = vCodec.rightChannelMic.gain;

		rc = PMIC_SUCCESS;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Enable a microphone bias circuit.
 *
 * This function enables one of the available microphone bias circuits.
 *
 * @param       handle           Device handle from pmic_audio_open() call.
 * @param       biasCircuit      The microphone bias circuit to be enabled.
 *
 * @retval      PMIC_SUCCESS         If the microphone bias circuit was
 *                                   successfully enabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle or selected microphone bias
 *                                   circuit was invalid.
 * @retval      PMIC_ERROR           If the microphone bias circuit could not
 *                                   be enabled.
 */
PMIC_STATUS pmic_audio_vcodec_enable_micbias(const PMIC_AUDIO_HANDLE handle,
					     const PMIC_AUDIO_MIC_BIAS
					     biasCircuit)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	unsigned int reg_mask = 0;

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
		if (biasCircuit & MIC_BIAS1) {
			reg_write = SET_BITS(regAUDIO_TX, MC1BEN, 1);
			reg_mask = SET_BITS(regAUDIO_TX, MC1BEN, 1);
		}
		if (biasCircuit & MIC_BIAS2) {
			reg_write |= SET_BITS(regAUDIO_TX, MC2BEN, 1);
			reg_mask |= SET_BITS(regAUDIO_TX, MC2BEN, 1);
		}
		if (reg_mask != 0) {
			rc = pmic_write_reg(REG_AUDIO_TX, reg_write, reg_mask);
		}
	}

	return rc;
}

/*!
 * @brief Disable a microphone bias circuit.
 *
 * This function disables one of the available microphone bias circuits.
 *
 * @param      handle           Device handle from pmic_audio_open() call.
 * @param      biasCircuit      The microphone bias circuit to be disabled.
 *
 * @retval      PMIC_SUCCESS         If the microphone bias circuit was
 *                                   successfully disabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle or selected microphone bias
 *                                   circuit was invalid.
 * @retval      PMIC_ERROR           If the microphone bias circuit could not
 *                                   be disabled.
 */
PMIC_STATUS pmic_audio_vcodec_disable_micbias(const PMIC_AUDIO_HANDLE handle,
					      const PMIC_AUDIO_MIC_BIAS
					      biasCircuit)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	unsigned int reg_mask = 0;

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
		if (biasCircuit & MIC_BIAS1) {
			reg_mask = SET_BITS(regAUDIO_TX, MC1BEN, 1);
		}

		if (biasCircuit & MIC_BIAS2) {
			reg_mask |= SET_BITS(regAUDIO_TX, MC2BEN, 1);
		}

		if (reg_mask != 0) {
			rc = pmic_write_reg(REG_AUDIO_TX, reg_write, reg_mask);
		}
	}

	return rc;
}

/*@}*/

/*************************************************************************
 * Audio Playback Using the Voice CODEC.
 *************************************************************************
 */

/*!
 * @name Audio Playback Using the Voice CODEC Setup and Configuration APIs
 * Functions for general setup and configuration of the PMIC Voice CODEC
 * to perform audio playback.
 */
/*@{*/

/*!
 * @brief Configure and enable the Voice CODEC mixer.
 *
 * This function configures and enables the Voice CODEC mixer.
 *
 * @param       handle              Device handle from pmic_audio_open() call.
 * @param       rxSecondaryTimeslot The timeslot used for the secondary audio
 *                                  channel.
 * @param       gainIn              The secondary audio channel gain level.
 * @param       gainOut             The mixer output gain level.
 *
 * @retval      PMIC_SUCCESS         If the Voice CODEC mixer was successfully
 *                                   configured and enabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle or mixer configuration
 *                                   was invalid.
 * @retval      PMIC_ERROR           If the Voice CODEC mixer could not be
 *                                   reconfigured or enabled.
 */
PMIC_STATUS pmic_audio_vcodec_enable_mixer(const PMIC_AUDIO_HANDLE handle,
					   const PMIC_AUDIO_VCODEC_TIMESLOT
					   rxSecondaryTimeslot,
					   const PMIC_AUDIO_VCODEC_MIX_IN_GAIN
					   gainIn,
					   const PMIC_AUDIO_VCODEC_MIX_OUT_GAIN
					   gainOut)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	unsigned int reg_mask = 0;

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
		if (!((rxSecondaryTimeslot >= USE_TS0)
		      && (rxSecondaryTimeslot <= USE_TS3))) {
			pr_debug
			    ("VCODEC enable mixer - wrong sec rx timeslot\n");
		} else if (!((gainIn >= VCODEC_NO_MIX)
			     && (gainIn <= VCODEC_MIX_IN_MINUS_12DB))) {
			pr_debug("VCODEC enable mixer - wrong mix in gain\n");

		} else if (!((gainOut >= VCODEC_MIX_OUT_0DB)
			     && (gainOut <= VCODEC_MIX_OUT_MINUS_6DB))) {
			pr_debug("VCODEC enable mixer - wrong mix out gain\n");
		} else {

			reg_mask = SET_BITS(regSSI_NETWORK, CDCRXSECSLOT, 3) |
			    SET_BITS(regSSI_NETWORK, CDCRXSECGAIN, 3) |
			    SET_BITS(regSSI_NETWORK, CDCSUMGAIN, 1);
			reg_write =
			    SET_BITS(regSSI_NETWORK, CDCRXSECSLOT,
				     rxSecondaryTimeslot) |
			    SET_BITS(regSSI_NETWORK, CDCRXSECGAIN,
				     gainIn) | SET_BITS(regSSI_NETWORK,
							CDCSUMGAIN, gainOut);
			rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
					    reg_write, reg_mask);
			if (rc == PMIC_SUCCESS) {
				pr_debug("Vcodec mixer enabled\n");
			}
		}
	}

	return rc;
}

/*!
 * @brief Disable the Voice CODEC mixer.
 *
 * This function disables the Voice CODEC mixer.
 *
 * @param       handle              Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the Voice CODEC mixer was successfully
 *                                   disabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the Voice CODEC mixer could not be
 *                                   disabled.
 */
PMIC_STATUS pmic_audio_vcodec_disable_mixer(const PMIC_AUDIO_HANDLE handle)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_mask;

	if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
		reg_mask = SET_BITS(regSSI_NETWORK, CDCRXSECGAIN, 1);
		rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
				    VCODEC_NO_MIX, reg_mask);

		if (rc == PMIC_SUCCESS) {
			pr_debug("Vcodec mixer disabled\n");
		}

	}

	return rc;
}

/*@}*/

/*************************************************************************
 * Audio Playback Using the Stereo DAC.
 *************************************************************************
 */

/*!
 * @name Audio Playback Using the Stereo DAC Setup and Configuration APIs
 * Functions for general setup and configuration of the PMIC Stereo DAC
 * to perform audio playback.
 */
/*@{*/

/*!
 * @brief Configure and enable the Stereo DAC mixer.
 *
 * This function configures and enables the Stereo DAC mixer.
 *
 * @param      handle              Device handle from pmic_audio_open() call.
 * @param      rxSecondaryTimeslot The timeslot used for the secondary audio
 *                                  channel.
 * @param      gainIn              The secondary audio channel gain level.
 * @param      gainOut             The mixer output gain level.
 *
 * @retval      PMIC_SUCCESS         If the Stereo DAC mixer was successfully
 *                                   configured and enabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle or mixer configuration
 *                                   was invalid.
 * @retval      PMIC_ERROR           If the Stereo DAC mixer could not be
 *                                   reconfigured or enabled.
 */
PMIC_STATUS pmic_audio_stdac_enable_mixer(const PMIC_AUDIO_HANDLE handle,
					  const PMIC_AUDIO_STDAC_TIMESLOTS
					  rxSecondaryTimeslot,
					  const PMIC_AUDIO_STDAC_MIX_IN_GAIN
					  gainIn,
					  const PMIC_AUDIO_STDAC_MIX_OUT_GAIN
					  gainOut)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	unsigned int reg_mask = 0;

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
		if (!((rxSecondaryTimeslot >= USE_TS0_TS1)
		      && (rxSecondaryTimeslot <= USE_TS6_TS7))) {
			rc = PMIC_PARAMETER_ERROR;
			pr_debug("STDAC enable mixer - wrong sec timeslot\n");
		} else if (!((gainIn >= STDAC_NO_MIX)
			     && (gainIn <= STDAC_MIX_IN_MINUS_12DB))) {
			rc = PMIC_PARAMETER_ERROR;
			pr_debug("STDAC enable mixer - wrong mix in gain\n");
		} else if (!((gainOut >= STDAC_MIX_OUT_0DB)
			     && (gainOut <= STDAC_MIX_OUT_MINUS_6DB))) {
			rc = PMIC_PARAMETER_ERROR;
			pr_debug("STDAC enable mixer - wrong mix out gain\n");
		} else {

			reg_mask = SET_BITS(regSSI_NETWORK, STDCRXSECSLOT, 3) |
			    SET_BITS(regSSI_NETWORK, STDCRXSECGAIN, 3) |
			    SET_BITS(regSSI_NETWORK, STDCSUMGAIN, 1);
			reg_write =
			    SET_BITS(regSSI_NETWORK, STDCRXSECSLOT,
				     rxSecondaryTimeslot) |
			    SET_BITS(regSSI_NETWORK, STDCRXSECGAIN,
				     gainIn) | SET_BITS(regSSI_NETWORK,
							STDCSUMGAIN, gainOut);
			rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
					    reg_write, reg_mask);
			if (rc == PMIC_SUCCESS) {
				pr_debug("STDAC mixer enabled\n");
			}
		}

	}

	return rc;
}

/*!
 * @brief Disable the Stereo DAC mixer.
 *
 * This function disables the Stereo DAC mixer.
 *
 * @param       handle              Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the Stereo DAC mixer was successfully
 *                                   disabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the Stereo DAC mixer could not be
 *                                   disabled.
 */
PMIC_STATUS pmic_audio_stdac_disable_mixer(const PMIC_AUDIO_HANDLE handle)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	const unsigned int reg_write = 0;
	const unsigned int reg_mask =
	    SET_BITS(regSSI_NETWORK, STDCRXSECGAIN, 1);

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
		rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK, reg_write, reg_mask);
	}

	return rc;
}

/*@}*/

/*************************************************************************
 * Audio Output Control
 *************************************************************************
 */

/*!
 * @name Audio Output Section Setup and Configuration APIs
 * Functions for general setup and configuration of the PMIC audio output
 * section to support playback.
 */
/*@{*/

/*!
 * @brief Select the audio output ports.
 *
 * This function selects the audio output ports to be used. This also enables
 * the appropriate output amplifiers.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 * @param   port                The audio output ports to be used.
 *
 * @retval      PMIC_SUCCESS         If the audio output ports were successfully
 *                                   acquired.
 * @retval      PMIC_PARAMETER_ERROR If the handle or output ports were
 *                                   invalid.
 * @retval      PMIC_ERROR           If the audio output ports could not be
 *                                   acquired.
 */
PMIC_STATUS pmic_audio_output_set_port(const PMIC_AUDIO_HANDLE handle,
				       const PMIC_AUDIO_OUTPUT_PORT port)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	unsigned int reg_mask = 0;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((port == MONO_ALERT) || (port == MONO_EXTOUT)) {
		rc = PMIC_NOT_SUPPORTED;
	} else {
		if (((handle == stDAC.handle)
		     && (stDAC.handleState == HANDLE_IN_USE))
		    || ((handle == extStereoIn.handle)
			&& (extStereoIn.handleState == HANDLE_IN_USE))
		    || ((handle == vCodec.handle)
			&& (vCodec.handleState == HANDLE_IN_USE)
			&& (audioOutput.vCodecOut == VCODEC_MIXER_OUT))) {
			/* Stereo signal and MIXER source needs to be routed to the port
			   / Avoid Codec direct out */

			if (port & MONO_SPEAKER) {
				reg_mask = SET_BITS(regAUDIO_RX_0, ASPEN, 1) |
				    SET_BITS(regAUDIO_RX_0, ASPSEL, 1);
				reg_write = SET_BITS(regAUDIO_RX_0, ASPEN, 1) |
				    SET_BITS(regAUDIO_RX_0, ASPSEL, 1);
			}
			if (port & MONO_LOUDSPEAKER) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, ALSPEN, 1) |
				    SET_BITS(regAUDIO_RX_0, ALSPREF, 1) |
				    SET_BITS(regAUDIO_RX_0, ALSPSEL, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, ALSPEN,
					     1) | SET_BITS(regAUDIO_RX_0,
							   ALSPREF,
							   1) |
				    SET_BITS(regAUDIO_RX_0, ALSPSEL, 1);
			}
			if (port & STEREO_HEADSET_LEFT) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1) |
				    SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, AHSLEN,
					     1) | SET_BITS(regAUDIO_RX_0,
							   AHSSEL, 1);
			}
			if (port & STEREO_HEADSET_RIGHT) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1) |
				    SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, AHSREN,
					     1) | SET_BITS(regAUDIO_RX_0,
							   AHSSEL, 1);
			}
			if (port & STEREO_OUT_LEFT) {
				reg_mask |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTLEN,
					     1) | SET_BITS(regAUDIO_RX_0,
							   ARXOUTSEL, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTLEN,
					     1) | SET_BITS(regAUDIO_RX_0,
							   ARXOUTSEL, 1);
			}
			if (port & STEREO_OUT_RIGHT) {
				reg_mask |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTREN,
					     1) | SET_BITS(regAUDIO_RX_0,
							   ARXOUTSEL, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTREN,
					     1) | SET_BITS(regAUDIO_RX_0,
							   ARXOUTSEL, 1);
			}
			if (port & STEREO_LEFT_LOW_POWER) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, LSPLEN, 1);

				reg_write |= SET_BITS(regAUDIO_RX_0, LSPLEN, 1);
			}
		} else if ((handle == vCodec.handle)
			   && (vCodec.handleState == HANDLE_IN_USE)
			   && (audioOutput.vCodecOut = VCODEC_DIRECT_OUT)) {
			if (port & MONO_SPEAKER) {
				reg_mask = SET_BITS(regAUDIO_RX_0, ASPEN, 1) |
				    SET_BITS(regAUDIO_RX_0, ASPSEL, 1);
				reg_write = SET_BITS(regAUDIO_RX_0, ASPEN, 1) |
				    SET_BITS(regAUDIO_RX_0, ASPSEL, 0);
			}
			if (port & MONO_LOUDSPEAKER) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, ALSPEN, 1) |
				    SET_BITS(regAUDIO_RX_0, ALSPREF, 1) |
				    SET_BITS(regAUDIO_RX_0, ALSPSEL, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, ALSPEN,
					     1) | SET_BITS(regAUDIO_RX_0,
							   ALSPREF,
							   1) |
				    SET_BITS(regAUDIO_RX_0, ALSPSEL, 0);
			}

			if (port & STEREO_HEADSET_LEFT) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1) |
				    SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, AHSLEN,
					     1) | SET_BITS(regAUDIO_RX_0,
							   AHSSEL, 0);
			}
			if (port & STEREO_HEADSET_RIGHT) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1) |
				    SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, AHSREN,
					     1) | SET_BITS(regAUDIO_RX_0,
							   AHSSEL, 0);
			}
			if (port & STEREO_OUT_LEFT) {
				reg_mask |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTLEN,
					     1) | SET_BITS(regAUDIO_RX_0,
							   ARXOUTSEL, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTLEN,
					     1) | SET_BITS(regAUDIO_RX_0,
							   ARXOUTSEL, 0);
			}
			if (port & STEREO_OUT_RIGHT) {
				reg_mask |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTREN,
					     1) | SET_BITS(regAUDIO_RX_0,
							   ARXOUTSEL, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTREN,
					     1) | SET_BITS(regAUDIO_RX_0,
							   ARXOUTSEL, 0);
			}
			if (port & MONO_CDCOUT) {
				reg_mask |=
				    SET_BITS(regAUDIO_RX_0, CDCOUTEN, 1);

				reg_write |=
				    SET_BITS(regAUDIO_RX_0, CDCOUTEN, 1);
			}
		}

		if (reg_mask == 0) {

		} else {
			rc = pmic_write_reg(REG_AUDIO_RX_0,
					    reg_write, reg_mask);

			if (rc == PMIC_SUCCESS) {
				pr_debug("output ports enabled\n");
				audioOutput.outputPort = port;

			}
		}
	}
	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Deselect/disable the audio output ports.
 *
 * This function disables the audio output ports that were previously enabled
 * by calling pmic_audio_output_set_port().
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 * @param   port                The audio output ports to be disabled.
 *
 * @retval      PMIC_SUCCESS         If the audio output ports were successfully
 *                                   disabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle or output ports were
 *                                   invalid.
 * @retval      PMIC_ERROR           If the audio output ports could not be
 *                                   disabled.
 */
PMIC_STATUS pmic_audio_output_clear_port(const PMIC_AUDIO_HANDLE handle,
					 const PMIC_AUDIO_OUTPUT_PORT port)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	unsigned int reg_mask = 0;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((port == MONO_ALERT) || (port == MONO_EXTOUT)) {
		rc = PMIC_NOT_SUPPORTED;
	} else {
		if (((handle == stDAC.handle)
		     && (stDAC.handleState == HANDLE_IN_USE))
		    || ((handle == extStereoIn.handle)
			&& (extStereoIn.handleState == HANDLE_IN_USE))
		    || ((handle == vCodec.handle)
			&& (vCodec.handleState == HANDLE_IN_USE)
			&& (audioOutput.vCodecOut = VCODEC_MIXER_OUT))) {
			/* Stereo signal and MIXER source needs to be routed to the port /
			   Avoid Codec direct out */
			if (port & MONO_SPEAKER) {
				reg_mask = SET_BITS(regAUDIO_RX_0, ASPEN, 1);
				reg_write = SET_BITS(regAUDIO_RX_0, ASPEN, 0);
			}
			if (port & MONO_LOUDSPEAKER) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, ALSPEN, 1) |
				    SET_BITS(regAUDIO_RX_0, ALSPREF, 1);

				reg_write |=
				    SET_BITS(regAUDIO_RX_0, ALSPEN,
					     0) | SET_BITS(regAUDIO_RX_0,
							   ALSPREF, 0);

			}
			if (port & STEREO_HEADSET_LEFT) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1);
				reg_write |= SET_BITS(regAUDIO_RX_0, AHSLEN, 0);
			}
			if (port & STEREO_HEADSET_RIGHT) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1);
				reg_write |= SET_BITS(regAUDIO_RX_0, AHSREN, 0);
			}
			if (port & STEREO_OUT_LEFT) {
				reg_mask |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTLEN, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTLEN, 0);
			}
			if (port & STEREO_OUT_RIGHT) {
				reg_mask |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTREN, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTREN, 0);
			}
			if (port & STEREO_LEFT_LOW_POWER) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, LSPLEN, 1);
				reg_write |= SET_BITS(regAUDIO_RX_0, LSPLEN, 0);
			}
		} else if ((handle == vCodec.handle)
			   && (vCodec.handleState == HANDLE_IN_USE)
			   && (audioOutput.vCodecOut = VCODEC_DIRECT_OUT)) {
			if (port & MONO_SPEAKER) {
				reg_mask = SET_BITS(regAUDIO_RX_0, ASPEN, 1);
				reg_write = SET_BITS(regAUDIO_RX_0, ASPEN, 0);
			}
			if (port & MONO_LOUDSPEAKER) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, ALSPEN, 1) |
				    SET_BITS(regAUDIO_RX_0, ALSPREF, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, ALSPEN,
					     0) | SET_BITS(regAUDIO_RX_0,
							   ALSPREF, 0);
			}
			if (port & STEREO_HEADSET_LEFT) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1);
				reg_write |= SET_BITS(regAUDIO_RX_0, AHSLEN, 0);
			}
			if (port & STEREO_HEADSET_RIGHT) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1);
				reg_write |= SET_BITS(regAUDIO_RX_0, AHSREN, 0);
			}
			if (port & STEREO_OUT_LEFT) {
				reg_mask |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTLEN, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTLEN, 0);
			}
			if (port & STEREO_OUT_RIGHT) {
				reg_mask |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTREN, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, ARXOUTREN, 0);
			}
			if (port & MONO_CDCOUT) {
				reg_mask |=
				    SET_BITS(regAUDIO_RX_0, CDCOUTEN, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, CDCOUTEN, 0);
			}
		}

		if (reg_mask == 0) {

		} else {
			rc = pmic_write_reg(REG_AUDIO_RX_0,
					    reg_write, reg_mask);
			if (rc == PMIC_SUCCESS) {
				pr_debug("output ports disabled\n");
				audioOutput.outputPort &= ~port;
			}
		}
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Get the current audio output ports.
 *
 * This function retrieves the audio output ports that are currently being
 * used.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 * @param   port                The audio output ports currently being used.
 *
 * @retval      PMIC_SUCCESS         If the audio output ports were successfully
 *                                   retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the audio output ports could not be
 *                                   retrieved.
 */
PMIC_STATUS pmic_audio_output_get_port(const PMIC_AUDIO_HANDLE handle,
				       PMIC_AUDIO_OUTPUT_PORT * const port)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((((handle == stDAC.handle) &&
	      (stDAC.handleState == HANDLE_IN_USE)) ||
	     ((handle == vCodec.handle) &&
	      (vCodec.handleState == HANDLE_IN_USE)) ||
	     ((handle == extStereoIn.handle) &&
	      (extStereoIn.handleState == HANDLE_IN_USE))) &&
	    (port != (PMIC_AUDIO_OUTPUT_PORT *) NULL)) {
		*port = audioOutput.outputPort;

		rc = PMIC_SUCCESS;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Set the gain level for the external stereo inputs.
 *
 * This function sets the gain levels for the external stereo inputs.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 * @param   gain                The external stereo input gain level.
 *
 * @retval      PMIC_SUCCESS         If the gain level was successfully set.
 * @retval      PMIC_PARAMETER_ERROR If the handle or gain level was invalid.
 * @retval      PMIC_ERROR           If the gain level could not be set.
 */
PMIC_STATUS pmic_audio_output_set_stereo_in_gain(const PMIC_AUDIO_HANDLE handle,
						 const PMIC_AUDIO_STEREO_IN_GAIN
						 gain)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1) |
	    SET_BITS(regAUDIO_RX_1, ARXIN, 1);
	unsigned int reg_write = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	/* The ARX amplifier for stereo is also enabled over here */

	if ((gain == STEREO_IN_GAIN_0DB) || (gain == STEREO_IN_GAIN_PLUS_18DB)) {
		if ((handle == extStereoIn.handle) &&
		    (extStereoIn.handleState == HANDLE_IN_USE)) {

			if (gain == STEREO_IN_GAIN_0DB) {
				reg_write |= SET_BITS(regAUDIO_RX_1, ARXIN, 1);
			} else {
				reg_write |= SET_BITS(regAUDIO_RX_1, ARXIN, 0);
			}

			rc = pmic_write_reg(REG_AUDIO_RX_1,
					    reg_write, reg_mask);

			if (rc == PMIC_SUCCESS) {
				pr_debug("Ext stereo gain set\n");
				extStereoIn.inputGain = gain;

			}

		} else {
			rc = PMIC_PARAMETER_ERROR;
		}
	}

	return rc;
}

/*!
 * @brief Get the current gain level for the external stereo inputs.
 *
 * This function retrieves the current gain levels for the external stereo
 * inputs.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 * @param   gain                The current external stereo input gain
 *                                  level.
 *
 * @retval      PMIC_SUCCESS         If the gain level was successfully
 *                                   retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the gain level could not be retrieved.
 */
PMIC_STATUS pmic_audio_output_get_stereo_in_gain(const PMIC_AUDIO_HANDLE handle,
						 PMIC_AUDIO_STEREO_IN_GAIN *
						 const gain)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((handle == extStereoIn.handle) &&
	    (extStereoIn.handleState == HANDLE_IN_USE) &&
	    (gain != (PMIC_AUDIO_STEREO_IN_GAIN *) NULL)) {
		*gain = extStereoIn.inputGain;
		rc = PMIC_SUCCESS;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Set the output PGA gain level.
 *
 * This function sets the audio output PGA gain level.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 * @param   gain                The output PGA gain level.
 *
 * @retval      PMIC_SUCCESS         If the gain level was successfully set.
 * @retval      PMIC_PARAMETER_ERROR If the handle or gain level was invalid.
 * @retval      PMIC_ERROR           If the gain level could not be set.
 */
PMIC_STATUS pmic_audio_output_set_pgaGain(const PMIC_AUDIO_HANDLE handle,
					  const PMIC_AUDIO_OUTPUT_PGA_GAIN gain)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	unsigned int reg_mask = 0;
	unsigned int reg_gain;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if (!((gain >= OUTPGA_GAIN_MINUS_33DB)
	      && (gain <= OUTPGA_GAIN_PLUS_6DB))) {
		rc = PMIC_NOT_SUPPORTED;
		pr_debug("output set PGA gain - wrong gain value\n");
	} else {
		if ((gain >= OUTPGA_GAIN_MINUS_33DB)
		    && (gain <= OUTPGA_GAIN_PLUS_6DB)) {
			reg_gain = gain + 2;
		} else {
			reg_gain = gain;
		}
		if ((handle == extStereoIn.handle) &&
		    (extStereoIn.handleState == HANDLE_IN_USE)) {
			reg_mask = SET_BITS(regAUDIO_RX_1, ARXIN, 15) |
			    SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
			reg_write = SET_BITS(regAUDIO_RX_1, ARXIN, reg_gain) |
			    SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
		} else if ((handle == vCodec.handle) &&
			   (vCodec.handleState == HANDLE_IN_USE)) {
			reg_mask = SET_BITS(regAUDIO_RX_1, PGARX, 15);
			reg_write = SET_BITS(regAUDIO_RX_1, PGARX, reg_gain);
		} else if ((handle == stDAC.handle) &&
			   (stDAC.handleState == HANDLE_IN_USE)) {
			reg_mask = SET_BITS(regAUDIO_RX_1, PGAST, 15);
			reg_write = SET_BITS(regAUDIO_RX_1, PGAST, reg_gain);
		}

		if (reg_mask == 0) {

		} else {
			rc = pmic_write_reg(REG_AUDIO_RX_1,
					    reg_write, reg_mask);

			if (rc == PMIC_SUCCESS) {
				pr_debug("Output PGA gains set\n");

				if (handle == stDAC.handle) {
					audioOutput.stDacoutputPGAGain = gain;
				} else if (handle == vCodec.handle) {
					audioOutput.vCodecoutputPGAGain = gain;
				} else {
					audioOutput.extStereooutputPGAGain =
					    gain;
				}
			} else {
				pr_debug
				    ("Error writing PGA gains to register\n");
			}
		}
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Get the output PGA gain level.
 *
 * This function retrieves the current audio output PGA gain level.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 * @param   gain                The current output PGA gain level.
 *
 * @retval      PMIC_SUCCESS         If the gain level was successfully
 *                                   retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the gain level could not be retrieved.
 */
PMIC_STATUS pmic_audio_output_get_pgaGain(const PMIC_AUDIO_HANDLE handle,
					  PMIC_AUDIO_OUTPUT_PGA_GAIN *
					  const gain)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if (gain != (PMIC_AUDIO_OUTPUT_PGA_GAIN *) NULL) {
		if ((handle == extStereoIn.handle) &&
		    (extStereoIn.handleState == HANDLE_IN_USE)) {
			*gain = audioOutput.extStereooutputPGAGain;
			rc = PMIC_SUCCESS;
		} else if ((handle == vCodec.handle) &&
			   (vCodec.handleState == HANDLE_IN_USE)) {
			*gain = audioOutput.vCodecoutputPGAGain;
			rc = PMIC_SUCCESS;
		} else if ((handle == stDAC.handle) &&
			   (stDAC.handleState == HANDLE_IN_USE)) {
			*gain = audioOutput.stDacoutputPGAGain;
			rc = PMIC_SUCCESS;
		} else {
			rc = PMIC_PARAMETER_ERROR;
		}
	} else {
		rc = PMIC_PARAMETER_ERROR;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Enable the output mixer.
 *
 * This function enables the output mixer for the audio stream that
 * corresponds to the current handle (i.e., the Voice CODEC, Stereo DAC, or
 * the external stereo inputs).
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the mixer was successfully enabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the mixer could not be enabled.
 */
PMIC_STATUS pmic_audio_output_enable_mixer(const PMIC_AUDIO_HANDLE handle)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_mask = 0;
	unsigned int reg_write = 0;
	unsigned int reg_mask_mix = 0;
	unsigned int reg_write_mix = 0;

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	if (((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE))) {
		reg_mask = SET_BITS(regAUDIO_RX_1, PGASTEN, 1);
		reg_write = SET_BITS(regAUDIO_RX_1, PGASTEN, 1);
		reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDSTDC, 1);
		reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDSTDC, 1);
	} else if ((handle == vCodec.handle) &&
		   (vCodec.handleState == HANDLE_IN_USE)) {
		reg_mask = SET_BITS(regAUDIO_RX_1, PGARXEN, 1);
		reg_write = SET_BITS(regAUDIO_RX_1, PGARXEN, 1);
		audioOutput.vCodecOut = VCODEC_MIXER_OUT;

		reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDCDC, 1);
		reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDCDC, 1);
	} else if ((handle == extStereoIn.handle) &&
		   (extStereoIn.handleState == HANDLE_IN_USE)) {
		reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
		reg_write = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
		reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
		reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
	}

	if (reg_mask == 0) {

	} else {
		rc = pmic_write_reg(REG_AUDIO_RX_1, reg_write, reg_mask);

		if (rc == PMIC_SUCCESS) {

			rc = pmic_write_reg(REG_AUDIO_RX_0,
					    reg_write_mix, reg_mask_mix);
			if (rc == PMIC_SUCCESS) {
				pr_debug("Output PGA mixers enabled\n");
				rc = PMIC_SUCCESS;
			}

		} else {
			pr_debug("Error writing mixer enable to register\n");
		}

	}

	return rc;
}

/*!
 * @brief Disable the output mixer.
 *
 * This function disables the output mixer for the audio stream that
 * corresponds to the current handle (i.e., the Voice CODEC, Stereo DAC, or
 * the external stereo inputs).
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the mixer was successfully disabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the mixer could not be disabled.
 */
PMIC_STATUS pmic_audio_output_disable_mixer(const PMIC_AUDIO_HANDLE handle)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_mask = 0;
	unsigned int reg_write = 0;

	unsigned int reg_mask_mix = 0;
	unsigned int reg_write_mix = 0;

	/* No critical section required here since we are not updating any
	 * global data.
	 */
	if (((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE))) {
		/*reg_mask = SET_BITS(regAUDIO_RX_1, PGASTEN, 1);
		   reg_write = SET_BITS(regAUDIO_RX_1, PGASTEN, 0); */

		reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDSTDC, 1);
		reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDSTDC, 0);
	} else if ((handle == vCodec.handle) &&
		   (vCodec.handleState == HANDLE_IN_USE)) {
		reg_mask = SET_BITS(regAUDIO_RX_1, PGARXEN, 1);
		reg_write = SET_BITS(regAUDIO_RX_1, PGARXEN, 0);
		audioOutput.vCodecOut = VCODEC_DIRECT_OUT;

		reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDCDC, 1);
		reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDCDC, 0);
	} else if ((handle == extStereoIn.handle) &&
		   (extStereoIn.handleState == HANDLE_IN_USE)) {
		/*reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
		   reg_write = SET_BITS(regAUDIO_RX_1, ARXINEN, 0); */

		reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
		reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
	}

	if (reg_mask == 0) {

	} else {
		rc = pmic_write_reg(REG_AUDIO_RX_1, reg_write, reg_mask);

		if (rc == PMIC_SUCCESS) {

			rc = pmic_write_reg(REG_AUDIO_RX_0,
					    reg_write_mix, reg_mask_mix);
			if (rc == PMIC_SUCCESS) {
				pr_debug("Output PGA mixers disabled\n");
			}
		}
	}
	return rc;
}

/*!
 * @brief Configure and enable the output balance amplifiers.
 *
 * This function configures and enables the output balance amplifiers.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 * @param   leftGain            The desired left channel gain level.
 * @param   rightGain           The desired right channel gain level.
 *
 * @retval      PMIC_SUCCESS         If the output balance amplifiers were
 *                                   successfully configured and enabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle or gain levels were invalid.
 * @retval      PMIC_ERROR           If the output balance amplifiers could not
 *                                   be reconfigured or enabled.
 */
PMIC_STATUS pmic_audio_output_set_balance(const PMIC_AUDIO_HANDLE handle,
					  const PMIC_AUDIO_OUTPUT_BALANCE_GAIN
					  leftGain,
					  const PMIC_AUDIO_OUTPUT_BALANCE_GAIN
					  rightGain)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_mask = 0;
	unsigned int reg_write = 0;
	unsigned int reg_mask_ch = 0;
	unsigned int reg_write_ch = 0;

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	if (!((leftGain >= BAL_GAIN_MINUS_21DB) && (leftGain <= BAL_GAIN_0DB))) {
		rc = PMIC_PARAMETER_ERROR;
	} else if (!((rightGain >= BAL_GAIN_MINUS_21DB)
		     && (rightGain <= BAL_GAIN_0DB))) {
		rc = PMIC_PARAMETER_ERROR;
	} else {
		if (((handle == stDAC.handle) &&
		     (stDAC.handleState == HANDLE_IN_USE)) ||
		    ((handle == vCodec.handle) &&
		     (vCodec.handleState == HANDLE_IN_USE)) ||
		    ((handle == extStereoIn.handle) &&
		     (extStereoIn.handleState == HANDLE_IN_USE))) {
			/* In mc13783 only one channel can be attenuated wrt the other.
			 * It is not possible to specify attenuation for both
			 * This function will return an error if both channels
			 * are required to be attenuated
			 * The BALLR bit is set/reset depending on whether leftGain
			 * or rightGain is specified*/
			if ((rightGain == BAL_GAIN_0DB)
			    && (leftGain == BAL_GAIN_0DB)) {
				/* Nothing to be done */
			} else if ((rightGain != BAL_GAIN_0DB)
				   && (leftGain == BAL_GAIN_0DB)) {
				/* Attenuate right channel */
				reg_mask = SET_BITS(regAUDIO_RX_1, BAL, 7);
				reg_mask_ch = SET_BITS(regAUDIO_RX_1, BALLR, 1);
				reg_write =
				    SET_BITS(regAUDIO_RX_1, BAL,
					     (BAL_GAIN_0DB - rightGain));
				/* The enum and the register values are reversed in order .. */
				reg_write_ch =
				    SET_BITS(regAUDIO_RX_1, BALLR, 0);
				/* BALLR = 0 selects right channel for atten */
			} else if ((rightGain == BAL_GAIN_0DB)
				   && (leftGain != BAL_GAIN_0DB)) {
				/* Attenuate left channel */

				reg_mask = SET_BITS(regAUDIO_RX_1, BAL, 7);
				reg_mask_ch = SET_BITS(regAUDIO_RX_1, BALLR, 1);
				reg_write =
				    SET_BITS(regAUDIO_RX_1, BAL,
					     (BAL_GAIN_0DB - leftGain));
				reg_write_ch =
				    SET_BITS(regAUDIO_RX_1, BALLR, 1);
				/* BALLR = 1 selects left channel for atten */
			} else {
				rc = PMIC_PARAMETER_ERROR;
			}

			if ((reg_mask == 0) || (reg_mask_ch == 0)) {

			} else {
				rc = pmic_write_reg(REG_AUDIO_RX_1,
						    reg_write_ch, reg_mask_ch);

				if (rc == PMIC_SUCCESS) {
					rc = pmic_write_reg(REG_AUDIO_RX_1,
							    reg_write,
							    reg_mask);

					if (rc == PMIC_SUCCESS) {
						pr_debug
						    ("Output balance attenuation set\n");
						audioOutput.balanceLeftGain =
						    leftGain;
						audioOutput.balanceRightGain =
						    rightGain;
					}
				}
			}
		}
	}
	return rc;
}

/*!
 * @brief Get the current output balance amplifier gain levels.
 *
 * This function retrieves the current output balance amplifier gain levels.
 *
 * @param   	handle              Device handle from pmic_audio_open() call.
 * @param  	leftGain            The current left channel gain level.
 * @param 	rightGain           The current right channel gain level.
 *
 * @retval      PMIC_SUCCESS         If the output balance amplifier gain levels
 *                                   were successfully retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the output balance amplifier gain levels
 *                                   could be retrieved.
 */
PMIC_STATUS pmic_audio_output_get_balance(const PMIC_AUDIO_HANDLE handle,
					  PMIC_AUDIO_OUTPUT_BALANCE_GAIN *
					  const leftGain,
					  PMIC_AUDIO_OUTPUT_BALANCE_GAIN *
					  const rightGain)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((((handle == stDAC.handle) &&
	      (stDAC.handleState == HANDLE_IN_USE)) ||
	     ((handle == vCodec.handle) &&
	      (vCodec.handleState == HANDLE_IN_USE)) ||
	     ((handle == extStereoIn.handle) &&
	      (extStereoIn.handleState == HANDLE_IN_USE))) &&
	    ((leftGain != (PMIC_AUDIO_OUTPUT_BALANCE_GAIN *) NULL) &&
	     (rightGain != (PMIC_AUDIO_OUTPUT_BALANCE_GAIN *) NULL))) {
		*leftGain = audioOutput.balanceLeftGain;
		*rightGain = audioOutput.balanceRightGain;

		rc = PMIC_SUCCESS;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Configure and enable the output mono adder.
 *
 * This function configures and enables the output mono adder.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 * @param   mode                The desired mono adder operating mode.
 *
 * @retval      PMIC_SUCCESS         If the mono adder was successfully
 *                                   configured and enabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle or mono adder mode was
 *                                   invalid.
 * @retval      PMIC_ERROR           If the mono adder could not be reconfigured
 *                                   or enabled.
 */
PMIC_STATUS pmic_audio_output_enable_mono_adder(const PMIC_AUDIO_HANDLE handle,
						const PMIC_AUDIO_MONO_ADDER_MODE
						mode)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_write = 0;
	unsigned int reg_mask = SET_BITS(regAUDIO_RX_1, MONO, 3);

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	if ((mode >= MONO_ADDER_OFF) && (mode <= STEREO_OPPOSITE_PHASE)) {
		if (((handle == stDAC.handle) &&
		     (stDAC.handleState == HANDLE_IN_USE)) ||
		    ((handle == vCodec.handle) &&
		     (vCodec.handleState == HANDLE_IN_USE)) ||
		    ((handle == extStereoIn.handle) &&
		     (extStereoIn.handleState == HANDLE_IN_USE))) {
			if (mode == MONO_ADDER_OFF) {
				reg_write = SET_BITS(regAUDIO_RX_1, MONO, 0);
			} else if (mode == MONO_ADD_LEFT_RIGHT) {
				reg_write = SET_BITS(regAUDIO_RX_1, MONO, 2);
			} else if (mode == MONO_ADD_OPPOSITE_PHASE) {
				reg_write = SET_BITS(regAUDIO_RX_1, MONO, 3);
			} else {	/* stereo opposite */

				reg_write = SET_BITS(regAUDIO_RX_1, MONO, 1);
			}

			rc = pmic_write_reg(REG_AUDIO_RX_1,
					    reg_write, reg_mask);

			if (rc == PMIC_SUCCESS) {
				pr_debug("Output mono adder mode set\n");

			}

		} else {
			rc = PMIC_PARAMETER_ERROR;
		}
	} else {
		rc = PMIC_PARAMETER_ERROR;
	}
	return rc;
}

/*!
 * @brief Disable the output mono adder.
 *
 * This function disables the output mono adder.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the mono adder was successfully
 *                                   disabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the mono adder could not be disabled.
 */
PMIC_STATUS pmic_audio_output_disable_mono_adder(const PMIC_AUDIO_HANDLE handle)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	const unsigned int reg_write = 0;
	const unsigned int reg_mask = SET_BITS(regAUDIO_RX_1, MONO, 3);

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	if (((handle == stDAC.handle) &&
	     (stDAC.handleState == HANDLE_IN_USE)) ||
	    ((handle == vCodec.handle) &&
	     (vCodec.handleState == HANDLE_IN_USE)) ||
	    ((handle == extStereoIn.handle) &&
	     (extStereoIn.handleState == HANDLE_IN_USE))) {
		rc = pmic_write_reg(REG_AUDIO_RX_1, reg_write, reg_mask);
	}

	return rc;
}

/*!
 * @brief Configure the mono adder output gain level.
 *
 * This function configures the mono adder output amplifier gain level.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 * @param   gain                The desired output gain level.
 *
 * @retval      PMIC_SUCCESS         If the mono adder output amplifier gain
 *                                   level was successfully set.
 * @retval      PMIC_PARAMETER_ERROR If the handle or gain level was invalid.
 * @retval      PMIC_ERROR           If the mono adder output amplifier gain
 *                                   level could not be reconfigured.
 */
PMIC_STATUS pmic_audio_output_set_mono_adder_gain(const PMIC_AUDIO_HANDLE
						  handle,
						  const
						  PMIC_AUDIO_MONO_ADDER_OUTPUT_GAIN
						  gain)
{
	PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
	return rc;
}

/*!
 * @brief Get the current mono adder output gain level.
 *
 * This function retrieves the current mono adder output amplifier gain level.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 * @param   gain                The current output gain level.
 *
 * @retval      PMIC_SUCCESS         If the mono adder output amplifier gain
 *                                   level was successfully retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the mono adder output amplifier gain
 *                                   level could not be retrieved.
 */
PMIC_STATUS pmic_audio_output_get_mono_adder_gain(const PMIC_AUDIO_HANDLE
						  handle,
						  PMIC_AUDIO_MONO_ADDER_OUTPUT_GAIN
						  * const gain)
{
	PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
	return rc;
}

/*!
 * @brief Set various audio output section options.
 *
 * This function sets one or more audio output section configuration
 * options. The currently supported options include whether to disable
 * the non-inverting mono speaker output, enabling the loudspeaker common
 * bias circuit, enabling detection of headset insertion/removal, and
 * whether to automatically disable the headset amplifiers when a headset
 * insertion/removal has been detected.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 * @param   config              The desired audio output section
 *                                  configuration options to be set.
 *
 * @retval      PMIC_SUCCESS         If the desired configuration options were
 *                                   all successfully set.
 * @retval      PMIC_PARAMETER_ERROR If the handle or configuration options
 *                                   were invalid.
 * @retval      PMIC_ERROR           If the desired configuration options
 *                                   could not be set.
 */
PMIC_STATUS pmic_audio_output_set_config(const PMIC_AUDIO_HANDLE handle,
					 const PMIC_AUDIO_OUTPUT_CONFIG config)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_mask = 0;
	unsigned int reg_write = 0;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if (((handle == stDAC.handle) &&
	     (stDAC.handleState == HANDLE_IN_USE)) ||
	    ((handle == vCodec.handle) &&
	     (vCodec.handleState == HANDLE_IN_USE)) ||
	    ((handle == extStereoIn.handle) &&
	     (extStereoIn.handleState == HANDLE_IN_USE))) {
		if (config & MONO_SPEAKER_INVERT_OUT_ONLY) {
			/* If this is one of the parameters */
			rc = PMIC_NOT_SUPPORTED;
		} else {
			if (config & MONO_LOUDSPEAKER_COMMON_BIAS) {
				reg_mask = SET_BITS(regAUDIO_RX_0, ALSPREF, 1);
				reg_write = SET_BITS(regAUDIO_RX_0, ALSPREF, 1);
			}
			if (config & HEADSET_DETECT_ENABLE) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, HSDETEN, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, HSDETEN, 1);
			}
			if (config & STEREO_HEADSET_AMP_AUTO_DISABLE) {
				reg_mask |=
				    SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
			}

			if (reg_mask == 0) {
				rc = PMIC_PARAMETER_ERROR;
			} else {
				rc = pmic_write_reg(REG_AUDIO_RX_0,
						    reg_write, reg_mask);

				if (rc == PMIC_SUCCESS) {
					pr_debug("Output config set\n");
					audioOutput.config |= config;

				}
			}
		}
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Clear various audio output section options.
 *
 * This function clears one or more audio output section configuration
 * options.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 * @param   config              The desired audio output section
 *                                  configuration options to be cleared.
 *
 * @retval      PMIC_SUCCESS         If the desired configuration options were
 *                                   all successfully cleared.
 * @retval      PMIC_PARAMETER_ERROR If the handle or configuration options
 *                                   were invalid.
 * @retval      PMIC_ERROR           If the desired configuration options
 *                                   could not be cleared.
 */
PMIC_STATUS pmic_audio_output_clear_config(const PMIC_AUDIO_HANDLE handle,
					   const PMIC_AUDIO_OUTPUT_CONFIG
					   config)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	/*unsigned int reg_write_RX = 0;
	   unsigned int reg_mask_RX  = 0;
	   unsigned int reg_write_TX = 0;
	   unsigned int reg_mask_TX  = 0; */
	unsigned int reg_mask = 0;
	unsigned int reg_write = 0;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if (((handle == stDAC.handle) &&
	     (stDAC.handleState == HANDLE_IN_USE)) ||
	    ((handle == vCodec.handle) &&
	     (vCodec.handleState == HANDLE_IN_USE)) ||
	    ((handle == extStereoIn.handle) &&
	     (extStereoIn.handleState == HANDLE_IN_USE))) {
		if (config & MONO_SPEAKER_INVERT_OUT_ONLY) {
			/* If this is one of the parameters */
			rc = PMIC_NOT_SUPPORTED;
		} else {
			if (config & MONO_LOUDSPEAKER_COMMON_BIAS) {
				reg_mask = SET_BITS(regAUDIO_RX_0, ALSPREF, 1);
				reg_write = SET_BITS(regAUDIO_RX_0, ALSPREF, 0);
			}

			if (config & HEADSET_DETECT_ENABLE) {
				reg_mask |= SET_BITS(regAUDIO_RX_0, HSDETEN, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, HSDETEN, 0);
			}

			if (config & STEREO_HEADSET_AMP_AUTO_DISABLE) {
				reg_mask |=
				    SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
				reg_write |=
				    SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 0);
			}

			if (reg_mask == 0) {
				rc = PMIC_PARAMETER_ERROR;
			} else {
				rc = pmic_write_reg(REG_AUDIO_RX_0,
						    reg_write, reg_mask);

				if (rc == PMIC_SUCCESS) {
					pr_debug("Output config cleared\n");
					audioOutput.config &= ~config;

				}
			}
		}
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Get the current audio output section options.
 *
 * This function retrieves the current audio output section configuration
 * option settings.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 * @param  config              The current audio output section
 *                                  configuration option settings.
 *
 * @retval      PMIC_SUCCESS         If the current configuration options were
 *                                   successfully retrieved.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the current configuration options
 *                                   could not be retrieved.
 */
PMIC_STATUS pmic_audio_output_get_config(const PMIC_AUDIO_HANDLE handle,
					 PMIC_AUDIO_OUTPUT_CONFIG *
					 const config)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Use a critical section to ensure a consistent hardware state. */
	if (down_interruptible(&mutex))
		return PMIC_SYSTEM_ERROR_EINTR;

	if ((((handle == stDAC.handle) &&
	      (stDAC.handleState == HANDLE_IN_USE)) ||
	     ((handle == vCodec.handle) &&
	      (vCodec.handleState == HANDLE_IN_USE)) ||
	     ((handle == extStereoIn.handle) &&
	      (extStereoIn.handleState == HANDLE_IN_USE))) &&
	    (config != (PMIC_AUDIO_OUTPUT_CONFIG *) NULL)) {
		*config = audioOutput.config;

		rc = PMIC_SUCCESS;
	}

	/* Exit the critical section. */
	up(&mutex);

	return rc;
}

/*!
 * @brief Enable the phantom ground circuit that is used to help identify
 *        the type of headset that has been inserted.
 *
 * This function enables the phantom ground circuit that is used to help
 * identify the type of headset (e.g., stereo or mono) that has been inserted.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the phantom ground circuit was
 *                                   successfully enabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the phantom ground circuit could not
 *                                   be enabled.
 */
PMIC_STATUS pmic_audio_output_enable_phantom_ground(const PMIC_AUDIO_HANDLE
						    handle)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	const unsigned int reg_mask = SET_BITS(regAUDIO_RX_0, HSPGDIS, 1);

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	if (((handle == stDAC.handle) &&
	     (stDAC.handleState == HANDLE_IN_USE)) ||
	    ((handle == vCodec.handle) &&
	     (vCodec.handleState == HANDLE_IN_USE)) ||
	    ((handle == extStereoIn.handle) &&
	     (extStereoIn.handleState == HANDLE_IN_USE))) {
		rc = pmic_write_reg(REG_AUDIO_RX_0, 0, reg_mask);
		if (rc == PMIC_SUCCESS) {
			pr_debug("Phantom ground enabled\n");

		}
	}
	return rc;
}

/*!
 * @brief Disable the phantom ground circuit that is used to help identify
 *        the type of headset that has been inserted.
 *
 * This function disables the phantom ground circuit that is used to help
 * identify the type of headset (e.g., stereo or mono) that has been inserted.
 *
 * @param   handle              Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the phantom ground circuit was
 *                                   successfully disabled.
 * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
 * @retval      PMIC_ERROR           If the phantom ground circuit could not
 *                                   be disabled.
 */
PMIC_STATUS pmic_audio_output_disable_phantom_ground(const PMIC_AUDIO_HANDLE
						     handle)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	const unsigned int reg_mask = SET_BITS(regAUDIO_RX_0, HSPGDIS, 1);

	/* No critical section required here since we are not updating any
	 * global data.
	 */

	if (((handle == stDAC.handle) &&
	     (stDAC.handleState == HANDLE_IN_USE)) ||
	    ((handle == vCodec.handle) &&
	     (vCodec.handleState == HANDLE_IN_USE)) ||
	    ((handle == extStereoIn.handle) &&
	     (extStereoIn.handleState == HANDLE_IN_USE))) {
		rc = pmic_write_reg(REG_AUDIO_RX_0, 1, reg_mask);
		if (rc == PMIC_SUCCESS) {
			pr_debug("Phantom ground disabled\n");

		}
	}
	return rc;
}

/*@}*/

/**************************************************************************
 * Static functions.
 **************************************************************************
 */

/*!
 * @name Audio Driver Internal Support Functions
 * These non-exported internal functions are used to support the functionality
 * of the exported audio APIs.
 */
/*@{*/

/*!
 * @brief Enables the 5.6V boost for the microphone bias 2 circuit.
 *
 * This function enables the switching regulator SW3 and configures it to
 * provide the 5.6V boost that is required for driving the microphone bias 2
 * circuit when using a 5-pole jack configuration (which is the case for the
 * Sphinx board).
 *
 * @retval      PMIC_SUCCESS         The 5.6V boost was successfully enabled.
 * @retval      PMIC_ERROR           Failed to enable the 5.6V boost.
 */
/*
static PMIC_STATUS pmic_audio_mic_boost_enable(void)
{
	PMIC_STATUS rc = PMIC_NOT_SUPPORTED;

	return rc;
}
*/
/*!
 * @brief Disables the 5.6V boost for the microphone bias 2 circuit.
 *
 * This function disables the switching regulator SW3 to turn off the 5.6V
 * boost for the microphone bias 2 circuit.
 *
 * @retval      PMIC_SUCCESS         The 5.6V boost was successfully disabled.
 * @retval      PMIC_ERROR           Failed to disable the 5.6V boost.
 */
/*
static PMIC_STATUS pmic_audio_mic_boost_disable(void)
{
	PMIC_STATUS rc = PMIC_NOT_SUPPORTED;

	return rc;
}
*/

/*!
 * @brief Free a device handle previously acquired by calling pmic_audio_open().
 *
 * Terminate further access to the PMIC audio hardware that was previously
 * acquired by calling pmic_audio_open(). This now allows another thread to
 * successfully call pmic_audio_open() to gain access.
 *
 * Note that we will shutdown/reset the Voice CODEC or Stereo DAC as well as
 * any associated audio input/output components that are no longer required.
 *
 * Also note that this function should only be called with the mutex already
 * acquired.
 *
 * @param   handle          Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the close request was successful.
 * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
 */
static PMIC_STATUS pmic_audio_close_handle(const PMIC_AUDIO_HANDLE handle)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;

	/* Match up the handle to the audio device and then close it. */
	if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
		/* Also shutdown the Stereo DAC hardware. The simplest way to
		 * do this is to simply call pmic_audio_reset_device() which will
		 * restore the ST_DAC register to it's initial power-on state.
		 *
		 * This will also shutdown the audio output section if no one
		 * else is still using it.
		 */
		rc = pmic_audio_reset_device(stDAC.handle);

		if (rc == PMIC_SUCCESS) {
			stDAC.handle = AUDIO_HANDLE_NULL;
			stDAC.handleState = HANDLE_FREE;
		}
	} else if ((handle == vCodec.handle) &&
		   (vCodec.handleState == HANDLE_IN_USE)) {
		/* Also shutdown the Voice CODEC and audio input hardware. The
		 * simplest way to do this is to simply call pmic_audio_reset_device()
		 * which will restore the AUD_CODEC register to it's initial
		 * power-on state.
		 *
		 * This will also shutdown the audio output section if no one
		 * else is still using it.
		 */
		rc = pmic_audio_reset_device(vCodec.handle);
		if (rc == PMIC_SUCCESS) {
			vCodec.handle = AUDIO_HANDLE_NULL;
			vCodec.handleState = HANDLE_FREE;
		}
	} else if ((handle == extStereoIn.handle) &&
		   (extStereoIn.handleState == HANDLE_IN_USE)) {

		/* Call pmic_audio_reset_device() here to shutdown the audio output
		 * section if no one else is still using it.
		 */
		rc = pmic_audio_reset_device(extStereoIn.handle);

		if (rc == PMIC_SUCCESS) {
			extStereoIn.handle = AUDIO_HANDLE_NULL;
			extStereoIn.handleState = HANDLE_FREE;
		}
	}

	return rc;
}

/*!
 * @brief Reset the selected audio hardware control registers to their
 *        power on state.
 *
 * This resets all of the audio hardware control registers currently
 * associated with the device handle back to their power on states. For
 * example, if the handle is associated with the Stereo DAC and a
 * specific output port and output amplifiers, then this function will
 * reset all of those components to their initial power on state.
 *
 * This function can only be called if the mutex has already been acquired.
 *
 * @param   handle          Device handle from pmic_audio_open() call.
 *
 * @retval      PMIC_SUCCESS         If the reset operation was successful.
 * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
 * @retval      PMIC_ERROR           If the reset was unsuccessful.
 */
static PMIC_STATUS pmic_audio_reset_device(const PMIC_AUDIO_HANDLE handle)
{
	PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
	unsigned int reg_mask = 0;

	if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
		/* Also shutdown the audio output section if nobody else is using it.
		   if ((vCodec.handleState == HANDLE_FREE) &&
		   (extStereoIn.handleState == HANDLE_FREE))
		   {
		   pmic_write_reg(REG_RX_AUD_AMPS, RESET_RX_AUD_AMPS,
		   REG_FULLMASK);
		   } */

		rc = pmic_write_reg(REG_AUDIO_STEREO_DAC,
				    RESET_ST_DAC, REG_FULLMASK);

		if (rc == PMIC_SUCCESS) {
			rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
					    RESET_SSI_NETWORK,
					    REG_SSI_STDAC_MASK);
			if (rc == PMIC_SUCCESS) {
				/* Also reset the driver state information to match. Note that we
				 * keep the device handle and event callback settings unchanged
				 * since these don't affect the actual hardware and we rely on
				 * the user to explicitly close the handle or deregister callbacks
				 */
				stDAC.busID = AUDIO_DATA_BUS_1;
				stDAC.protocol = NORMAL_MSB_JUSTIFIED_MODE;
				stDAC.protocol_set = false;
				stDAC.masterSlave = BUS_MASTER_MODE;
				stDAC.numSlots = USE_2_TIMESLOTS;
				stDAC.clockIn = CLOCK_IN_CLIA;
				stDAC.samplingRate = STDAC_RATE_44_1_KHZ;
				stDAC.clockFreq = STDAC_CLI_13MHZ;
				stDAC.invert = NO_INVERT;
				stDAC.timeslot = USE_TS0_TS1;
				stDAC.config = (PMIC_AUDIO_STDAC_CONFIG) 0;

			}
		}
	} else if ((handle == vCodec.handle)
		   && (vCodec.handleState == HANDLE_IN_USE)) {
		/* Disable the audio input section when disabling the Voice CODEC. */
		pmic_write_reg(REG_AUDIO_TX, RESET_AUDIO_TX, REG_FULLMASK);

		rc = pmic_write_reg(REG_AUDIO_CODEC,
				    RESET_AUD_CODEC, REG_FULLMASK);

		if (rc == PMIC_SUCCESS) {
			rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
					    RESET_SSI_NETWORK,
					    REG_SSI_VCODEC_MASK);
			if (rc == PMIC_SUCCESS) {

				/* Also reset the driver state information to match. Note that we
				 * keep the device handle and event callback settings unchanged
				 * since these don't affect the actual hardware and we rely on
				 * the user to explicitly close the handle or deregister callbacks
				 */
				vCodec.busID = AUDIO_DATA_BUS_2;
				vCodec.protocol = NETWORK_MODE;
				vCodec.protocol_set = false;
				vCodec.masterSlave = BUS_SLAVE_MODE;
				vCodec.numSlots = USE_4_TIMESLOTS;
				vCodec.clockIn = CLOCK_IN_CLIB;
				vCodec.samplingRate = VCODEC_RATE_8_KHZ;
				vCodec.clockFreq = VCODEC_CLI_13MHZ;
				vCodec.invert = NO_INVERT;
				vCodec.timeslot = USE_TS0;
				vCodec.config =
				    INPUT_HIGHPASS_FILTER |
				    OUTPUT_HIGHPASS_FILTER;

			}
		}

	} else if ((handle == extStereoIn.handle) &&
		   (extStereoIn.handleState == HANDLE_IN_USE)) {
		/* Disable the Ext stereo Amplifier and disable it as analog mixer input */
		reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
		pmic_write_reg(REG_AUDIO_RX_1, 0, reg_mask);

		reg_mask = SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
		pmic_write_reg(REG_AUDIO_RX_0, 0, reg_mask);

		/* We don't need to reset any other registers for this case. */
		rc = PMIC_SUCCESS;
	}

	return rc;
}

/*!
 * @brief Deregister the callback function and event mask currently associated
 *        with an audio device handle.
 *
 * This function deregisters any existing callback function and event mask for
 * the given audio device handle. This is done by either calling the
 * pmic_audio_clear_callback() API or by closing the device handle.
 *
 * Note that this function should only be called with the mutex already
 * acquired. We will also acquire the spinlock here to prevent possible
 * race conditions with the interrupt handler.
 *
 * @param[in]   callback            The current event callback function pointer.
 * @param[in]   eventMask           The current audio event mask.
 *
 * @retval      PMIC_SUCCESS         If the callback function and event mask
 *                                   were both successfully deregistered.
 * @retval      PMIC_ERROR           If either the callback function or the
 *                                   event mask was not successfully
 *                                   deregistered.
 */
/*
static PMIC_STATUS pmic_audio_deregister(PMIC_AUDIO_CALLBACK * const callback,
					 PMIC_AUDIO_EVENTS * const eventMask)
{

	PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
	return rc;
}
*/
/*!
 * @brief This is the default event handler for the audio driver.
 *
 * This function is called by the low-level PMIC interrupt handler whenever
 * a registered event occurs. This function will perform whatever additional
 * processing may be required to fully identify the event that just occur and
 * then call the appropriate callback function to complete the handling of the
 * event.
 *
 * @param[in]   param               The parameter that was provided when this
 *                                  event handler was registered (unused).
 */
/*
static void pmic_audio_event_handler(void *param)
{
}
*/

/*!
 * @brief This is the audio driver tasklet that handles interrupt events.
 *
 * This is currently not supported by MC13783
 *
 * @param[in]   arg                 The parameter that was provided above in
 *                                  the DECLARE_TASKLET() macro (unused).
 */
void pmic_audio_tasklet(unsigned long arg)
{

}

/*@}*/

/**************************************************************************
 * Module initialization and termination functions.
 *
 * Note that if this code is compiled into the kernel, then the
 * module_init() function will be called within the device_initcall()
 * group.
 **************************************************************************
 */

/*!
 * @name Audio Driver Loading/Unloading Functions
 * These non-exported internal functions are used to support the audio
 * device driver initialization and de-initialization operations.
 */
/*@{*/

/*!
 * @brief This is the audio device driver initialization function.
 *
 * This function is called by the kernel when this device driver is first
 * loaded.
 */
static int __init mc13783_pmic_audio_init(void)
{
	printk(KERN_INFO "PMIC Audio driver loading...\n");

	return 0;
}

/*!
 * @brief This is the audio device driver de-initialization function.
 *
 * This function is called by the kernel when this device driver is about
 * to be unloaded.
 */
static void __exit mc13783_pmic_audio_exit(void)
{
	printk(KERN_INFO "PMIC Audio driver unloading...\n");

	/* Close all device handles that are still open. This will also
	 * deregister any callbacks that may still be active.
	 */
	if (stDAC.handleState == HANDLE_IN_USE) {
		pmic_audio_close(stDAC.handle);
	}
	if (vCodec.handleState == HANDLE_IN_USE) {
		pmic_audio_close(vCodec.handle);
	}
	if (extStereoIn.handleState == HANDLE_IN_USE) {
		pmic_audio_close(extStereoIn.handle);
	}

	/* Explicitly reset all of the audio registers so that there is no
	 * possibility of leaving the  audio hardware in a state
	 * where it can cause problems if there is no device driver loaded.
	 */
	pmic_write_reg(REG_AUDIO_STEREO_DAC, RESET_ST_DAC, REG_FULLMASK);
	pmic_write_reg(REG_AUDIO_CODEC, RESET_AUD_CODEC, REG_FULLMASK);
	pmic_write_reg(REG_AUDIO_TX, RESET_AUDIO_TX, REG_FULLMASK);
	pmic_write_reg(REG_AUDIO_SSI_NETWORK, RESET_SSI_NETWORK, REG_FULLMASK);
	pmic_write_reg(REG_AUDIO_RX_0, RESET_AUDIO_RX_0, REG_FULLMASK);
	pmic_write_reg(REG_AUDIO_RX_1, RESET_AUDIO_RX_1, REG_FULLMASK);
}

/*@}*/

/*
 * Module entry points and description information.
 */

module_init(mc13783_pmic_audio_init);
module_exit(mc13783_pmic_audio_exit);

MODULE_DESCRIPTION("PMIC - mc13783 ADC driver");
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_LICENSE("GPL");
