diff options
author | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
---|---|---|
committer | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
commit | c6da2cfeb05178a11c6d062a06f8078150ee492f (patch) | |
tree | f3b4021d252c52d6463a9b3c1bb7245e399b009c /sound/soc/codecs/mc1n2/mc1n2.c | |
parent | c6d7c4dbff353eac7919342ae6b3299a378160a6 (diff) | |
download | kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2 |
samsung update 1
Diffstat (limited to 'sound/soc/codecs/mc1n2/mc1n2.c')
-rw-r--r-- | sound/soc/codecs/mc1n2/mc1n2.c | 4563 |
1 files changed, 4563 insertions, 0 deletions
diff --git a/sound/soc/codecs/mc1n2/mc1n2.c b/sound/soc/codecs/mc1n2/mc1n2.c new file mode 100644 index 0000000..975f40b --- /dev/null +++ b/sound/soc/codecs/mc1n2/mc1n2.c @@ -0,0 +1,4563 @@ +/* + * MC-1N2 ASoC codec driver + * + * Copyright (c) 2010-2011 Yamaha Corporation + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/types.h> +#include <sound/hwdep.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> +#include "mc1n2.h" +#include "mc1n2_priv.h" + +#include <plat/gpio-cfg.h> +#include <mach/gpio.h> + +#ifdef CONFIG_TARGET_LOCALE_NAATT_TEMP +/* CONFIG_TARGET_LOCALE_NAATT_TEMP is intentionally introduced temporarily*/ + +#include "mc1n2_cfg_gsm.h" +#elif defined(CONFIG_MACH_Q1_BD) +#include "mc1n2_cfg_q1.h" +#elif defined(CONFIG_MACH_U1_KOR_LGT) +#include "mc1n2_cfg_lgt.h" +#elif defined(CONFIG_MACH_PX) +#include "mc1n2_cfg_px.h" +#else +#include "mc1n2_cfg.h" +#endif + +extern int mc1n2_set_mclk_source(bool on); +static int audio_ctrl_mic_bias_gpio(struct mc1n2_platform_data *pdata, int mic, bool on); + +#define MC1N2_DRIVER_VERSION "1.0.4" + +#define MC1N2_NAME "mc1n2" + +#define MC1N2_I2S_RATE (SNDRV_PCM_RATE_8000_48000) +#define MC1N2_I2S_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define MC1N2_PCM_RATE (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000) +#define MC1N2_PCM_FORMATS \ + (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ + SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_MU_LAW) + +#define MC1N2_HWDEP_ID "mc1n2" + +#define MC1N2_HW_ID_AA 0x78 +#define MC1N2_HW_ID_AB 0x79 + +#define MC1N2_WAITTIME_MICIN 100 + +#ifdef ALSA_VER_ANDROID_3_0 +static struct i2c_client *mc1n2_i2c; +#endif + +/* + * Driver private data structure + */ +static UINT8 mc1n2_hwid; + +static size_t mc1n2_path_channel_tbl[] = { + offsetof(MCDRV_PATH_INFO, asDit0[0]), + offsetof(MCDRV_PATH_INFO, asDit1[0]), + offsetof(MCDRV_PATH_INFO, asDit2[0]), + offsetof(MCDRV_PATH_INFO, asHpOut[0]), + offsetof(MCDRV_PATH_INFO, asHpOut[1]), + offsetof(MCDRV_PATH_INFO, asSpOut[0]), + offsetof(MCDRV_PATH_INFO, asSpOut[1]), + offsetof(MCDRV_PATH_INFO, asRcOut[0]), + offsetof(MCDRV_PATH_INFO, asLout1[0]), + offsetof(MCDRV_PATH_INFO, asLout1[1]), + offsetof(MCDRV_PATH_INFO, asLout2[0]), + offsetof(MCDRV_PATH_INFO, asLout2[1]), + offsetof(MCDRV_PATH_INFO, asDac[0]), + offsetof(MCDRV_PATH_INFO, asDac[1]), + offsetof(MCDRV_PATH_INFO, asAe[0]), + offsetof(MCDRV_PATH_INFO, asAdc0[0]), + offsetof(MCDRV_PATH_INFO, asAdc0[1]), + offsetof(MCDRV_PATH_INFO, asMix[0]), + offsetof(MCDRV_PATH_INFO, asBias[0]), +}; +#define MC1N2_N_PATH_CHANNELS (sizeof(mc1n2_path_channel_tbl) / sizeof(size_t)) + +struct mc1n2_port_params { + UINT8 rate; + UINT8 bits[SNDRV_PCM_STREAM_LAST+1]; + UINT8 pcm_mono[SNDRV_PCM_STREAM_LAST+1]; + UINT8 pcm_order[SNDRV_PCM_STREAM_LAST+1]; + UINT8 pcm_law[SNDRV_PCM_STREAM_LAST+1]; + UINT8 master; + UINT8 inv; + UINT8 format; + UINT8 bckfs; + UINT8 pcm_clkdown; + UINT8 channels; + UINT8 stream; /* bit0: Playback, bit1: Capture */ + UINT8 dir[MC1N2_N_PATH_CHANNELS]; /* path settings for DIR */ + MCDRV_CHANNEL dit; /* path settings for DIT */ +}; + +struct mc1n2_data { + struct mutex mutex; + struct mc1n2_setup setup; + struct mc1n2_port_params port[IOPORT_NUM]; + struct snd_hwdep *hwdep; + struct mc1n2_platform_data *pdata; + int clk_update; + MCDRV_PATH_INFO path_store; + MCDRV_VOL_INFO vol_store; + MCDRV_DIO_INFO dio_store; + MCDRV_DAC_INFO dac_store; + MCDRV_ADC_INFO adc_store; + MCDRV_SP_INFO sp_store; + MCDRV_DNG_INFO dng_store; + MCDRV_SYSEQ_INFO syseq_store; + MCDRV_AE_INFO ae_store; + MCDRV_PDM_INFO pdm_store; + UINT32 hdmicount; + UINT32 delay_mic1in; +}; + +struct mc1n2_info_store { + UINT32 get; + UINT32 set; + size_t offset; + UINT32 flags; +}; + +struct mc1n2_info_store mc1n2_info_store_tbl[] = { + {MCDRV_GET_DIGITALIO, MCDRV_SET_DIGITALIO, + offsetof(struct mc1n2_data, dio_store), 0x1ff}, + {MCDRV_GET_DAC, MCDRV_SET_DAC, + offsetof(struct mc1n2_data, dac_store), 0x7}, + {MCDRV_GET_ADC, MCDRV_SET_ADC, + offsetof(struct mc1n2_data, adc_store), 0x7}, + {MCDRV_GET_SP, MCDRV_SET_SP, + offsetof(struct mc1n2_data, sp_store), 0}, + {MCDRV_GET_DNG, MCDRV_SET_DNG, + offsetof(struct mc1n2_data, dng_store), 0x3f3f3f}, + {MCDRV_GET_SYSEQ, MCDRV_SET_SYSEQ, + offsetof(struct mc1n2_data, syseq_store), 0x3}, + {0, MCDRV_SET_AUDIOENGINE, + offsetof(struct mc1n2_data, ae_store), 0x1ff}, + {MCDRV_GET_PDM, MCDRV_SET_PDM, + offsetof(struct mc1n2_data, pdm_store), 0x7f}, + {MCDRV_GET_PATH, MCDRV_SET_PATH, + offsetof(struct mc1n2_data, path_store), 0}, + {MCDRV_GET_VOLUME, MCDRV_SET_VOLUME, + offsetof(struct mc1n2_data, vol_store), 0}, +}; +#define MC1N2_N_INFO_STORE (sizeof(mc1n2_info_store_tbl) / sizeof(struct mc1n2_info_store)) + +#define mc1n2_is_in_playback(p) ((p)->stream & (1 << SNDRV_PCM_STREAM_PLAYBACK)) +#define mc1n2_is_in_capture(p) ((p)->stream & (1 << SNDRV_PCM_STREAM_CAPTURE)) +#define get_port_id(id) (id-1) + +static int mc1n2_current_mode; + +#ifndef ALSA_VER_ANDROID_3_0 +static struct snd_soc_codec *mc1n2_codec; +#endif + +#ifndef ALSA_VER_ANDROID_3_0 +static struct snd_soc_codec *mc1n2_get_codec_data(void) +{ + return mc1n2_codec; +} + +static void mc1n2_set_codec_data(struct snd_soc_codec *codec) +{ + mc1n2_codec = codec; +} +#endif + +/* deliver i2c access to machdep */ +struct i2c_client *mc1n2_get_i2c_client(void) +{ +#ifdef ALSA_VER_ANDROID_3_0 + return mc1n2_i2c; +#else + return mc1n2_codec->control_data; +#endif +} + +static int audio_ctrl_mic_bias_gpio(struct mc1n2_platform_data *pdata, int mic, bool on) +{ + if (!pdata) { + pr_err("failed to control mic bias\n"); + return -EINVAL; + } + + if ((mic & MAIN_MIC) && (pdata->set_main_mic_bias != NULL)) + pdata->set_main_mic_bias(on); + + if ((mic & SUB_MIC) && (pdata->set_sub_mic_bias != NULL)) + pdata->set_sub_mic_bias(on); + + return 0; +} + +/* + * DAI (PCM interface) + */ +/* SRC_RATE settings @ 73728000Hz (ideal PLL output) */ +static int mc1n2_src_rate[][SNDRV_PCM_STREAM_LAST+1] = { + /* DIR, DIT */ + {32768, 4096}, /* MCDRV_FS_48000 */ + {30106, 4458}, /* MCDRV_FS_44100 */ + {21845, 6144}, /* MCDRV_FS_32000 */ + {0, 0}, /* N/A */ + {0, 0}, /* N/A */ + {15053, 8916}, /* MCDRV_FS_22050 */ + {10923, 12288}, /* MCDRV_FS_16000 */ + {0, 0}, /* N/A */ + {0, 0}, /* N/A */ + {7526, 17833}, /* MCDRV_FS_11025 */ + {5461, 24576}, /* MCDRV_FS_8000 */ +}; + +#define mc1n2_fs_to_srcrate(rate,dir) mc1n2_src_rate[(rate)][(dir)]; + +static int mc1n2_setup_dai(struct mc1n2_data *mc1n2, int id, int mode, int dir) +{ + MCDRV_DIO_INFO dio; + MCDRV_DIO_PORT *port = &dio.asPortInfo[id]; + struct mc1n2_setup *setup = &mc1n2->setup; + struct mc1n2_port_params *par = &mc1n2->port[id]; + UINT32 update = 0; + int i; + + memset(&dio, 0, sizeof(MCDRV_DIO_INFO)); + + if (par->stream == 0) { + port->sDioCommon.bMasterSlave = par->master; + port->sDioCommon.bAutoFs = MCDRV_AUTOFS_OFF; + port->sDioCommon.bFs = par->rate; + port->sDioCommon.bBckFs = par->bckfs; + port->sDioCommon.bInterface = mode; + port->sDioCommon.bBckInvert = par->inv; + if (mode == MCDRV_DIO_PCM) { + port->sDioCommon.bPcmHizTim = setup->pcm_hiz_redge[id]; + port->sDioCommon.bPcmClkDown = par->pcm_clkdown; + port->sDioCommon.bPcmFrame = par->format; + port->sDioCommon.bPcmHighPeriod = setup->pcm_hperiod[id]; + } + update |= MCDRV_DIO0_COM_UPDATE_FLAG; + } + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + port->sDir.wSrcRate = mc1n2_fs_to_srcrate(par->rate, dir); + if (mode == MCDRV_DIO_DA) { + port->sDir.sDaFormat.bBitSel = par->bits[dir]; + port->sDir.sDaFormat.bMode = par->format; + } else { + port->sDir.sPcmFormat.bMono = par->pcm_mono[dir]; + port->sDir.sPcmFormat.bOrder = par->pcm_order[dir]; + if (setup->pcm_extend[id]) { + port->sDir.sPcmFormat.bOrder |= + (1 << setup->pcm_extend[id]); + } + port->sDir.sPcmFormat.bLaw = par->pcm_law[dir]; + port->sDir.sPcmFormat.bBitSel = par->bits[dir]; + } + for (i = 0; i < DIO_CHANNELS; i++) { + if (i && par->channels == 1) { + port->sDir.abSlot[i] = port->sDir.abSlot[i-1]; + } else { + port->sDir.abSlot[i] = setup->slot[id][dir][i]; + } + + } + update |= MCDRV_DIO0_DIR_UPDATE_FLAG; + } + + if (dir == SNDRV_PCM_STREAM_CAPTURE) { + port->sDit.wSrcRate = mc1n2_fs_to_srcrate(par->rate, dir); + if (mode == MCDRV_DIO_DA) { + port->sDit.sDaFormat.bBitSel = par->bits[dir]; + port->sDit.sDaFormat.bMode = par->format; + } else { + port->sDit.sPcmFormat.bMono = par->pcm_mono[dir]; + port->sDit.sPcmFormat.bOrder = par->pcm_order[dir]; + if (setup->pcm_extend[id]) { + port->sDit.sPcmFormat.bOrder |= + (1 << setup->pcm_extend[id]); + } + port->sDit.sPcmFormat.bLaw = par->pcm_law[dir]; + port->sDit.sPcmFormat.bBitSel = par->bits[dir]; + } + for (i = 0; i < DIO_CHANNELS; i++) { + port->sDit.abSlot[i] = setup->slot[id][dir][i]; + } + update |= MCDRV_DIO0_DIT_UPDATE_FLAG; + } + + return _McDrv_Ctrl(MCDRV_SET_DIGITALIO, &dio, update << (id*3)); +} + +static int mc1n2_control_dir(struct mc1n2_data *mc1n2, int id, int enable) +{ + MCDRV_PATH_INFO info; + MCDRV_CHANNEL *ch; + int activate; + int i; + + memset(&info, 0, sizeof(MCDRV_PATH_INFO)); + + for (i = 0; i < MC1N2_N_PATH_CHANNELS; i++) { + ch = (MCDRV_CHANNEL *)((void *)&info + mc1n2_path_channel_tbl[i]); + + switch (i) { + case 0: +#ifdef DIO0_DAI_ENABLE + activate = enable && mc1n2_is_in_capture(&mc1n2->port[0]); +#else + activate = enable; +#endif + break; + + case 1: +#ifdef DIO1_DAI_ENABLE + activate = enable && mc1n2_is_in_capture(&mc1n2->port[1]); +#else + activate = enable; +#endif + break; + case 2: +#ifdef DIO2_DAI_ENABLE + activate = enable && mc1n2_is_in_capture(&mc1n2->port[2]); +#else + activate = enable; +#endif + break; + default: + activate = enable; + break; + } + + if (mc1n2->port[id].dir[i]) { + ch->abSrcOnOff[3] = 0x1 << (id * 2 + !activate); + } + } + + return _McDrv_Ctrl(MCDRV_SET_PATH, &info, 0); +} + +static int mc1n2_control_dit(struct mc1n2_data *mc1n2, int id, int enable) +{ + MCDRV_PATH_INFO info; + MCDRV_CHANNEL *ch = info.asDit0 + id; + int stream; + int i; + + memset(&info, 0, sizeof(MCDRV_PATH_INFO)); + + for (i = 0; i < SOURCE_BLOCK_NUM; i++) { + if (i == 3) { + stream = 0; + +#ifdef DIO0_DAI_ENABLE + stream |= mc1n2_is_in_playback(&mc1n2->port[0]); +#endif +#ifdef DIO1_DAI_ENABLE + stream |= mc1n2_is_in_playback(&mc1n2->port[1]) << 2; +#endif +#ifdef DIO2_DAI_ENABLE + stream |= mc1n2_is_in_playback(&mc1n2->port[2]) << 4; +#endif + + ch->abSrcOnOff[3] = (stream & mc1n2->port[id].dit.abSrcOnOff[3]) << !enable; + } else { + ch->abSrcOnOff[i] = mc1n2->port[id].dit.abSrcOnOff[i] << !enable; + } + } + + return _McDrv_Ctrl(MCDRV_SET_PATH, &info, 0); +} + +static int mc1n2_update_clock(struct mc1n2_data *mc1n2) +{ + MCDRV_CLOCK_INFO info; + + memset(&info, 0, sizeof(MCDRV_CLOCK_INFO)); + info.bCkSel = mc1n2->setup.init.bCkSel; + info.bDivR0 = mc1n2->setup.init.bDivR0; + info.bDivF0 = mc1n2->setup.init.bDivF0; + info.bDivR1 = mc1n2->setup.init.bDivR1; + info.bDivF1 = mc1n2->setup.init.bDivF1; + + return _McDrv_Ctrl(MCDRV_UPDATE_CLOCK, &info, 0); +} + +static int mc1n2_set_clkdiv_common(struct mc1n2_data *mc1n2, int div_id, int div) +{ + struct mc1n2_setup *setup = &mc1n2->setup; + + switch (div_id) { + case MC1N2_CKSEL: + switch (div) { + case 0: + setup->init.bCkSel = MCDRV_CKSEL_CMOS; + break; + case 1: + setup->init.bCkSel = MCDRV_CKSEL_TCXO; + break; + case 2: + setup->init.bCkSel = MCDRV_CKSEL_CMOS_TCXO; + break; + case 3: + setup->init.bCkSel = MCDRV_CKSEL_TCXO_CMOS; + break; + default: + return -EINVAL; + } + break; + case MC1N2_DIVR0: + if ((div < 1) || (div > 127)) { + return -EINVAL; + } + setup->init.bDivR0 = div; + break; + case MC1N2_DIVF0: + if ((div < 1) || (div > 255)) { + return -EINVAL; + } + setup->init.bDivF0 = div; + break; + case MC1N2_DIVR1: + if ((div < 1) || (div > 127)) { + return -EINVAL; + } + setup->init.bDivR1 = div; + break; + case MC1N2_DIVF1: + if ((div < 1) || (div > 255)) { + return -EINVAL; + } + setup->init.bDivF1 = div; + break; + default: + return -EINVAL; + } + + mc1n2->clk_update = 1; + + return 0; +} + +static int mc1n2_set_fmt_common(struct mc1n2_port_params *port, unsigned int fmt) +{ + /* master */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + port->master = MCDRV_DIO_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + port->master = MCDRV_DIO_SLAVE; + break; + default: + return -EINVAL; + } + + /* inv */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + port->inv = MCDRV_BCLK_NORMAL; + break; + case SND_SOC_DAIFMT_IB_NF: + port->inv = MCDRV_BCLK_INVERT; + break; + default: + return -EINVAL; + } + +#ifdef ALSA_VER_1_0_19 + /* clock */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SND_SOC_DAIFMT_SYNC: + /* just validating */ + break; + default: + return -EINVAL; + } +#endif + + return 0; +} + +static int mc1n2_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) +{ + struct snd_soc_codec *codec = dai->codec; +#if (defined ALSA_VER_ANDROID_2_6_35) || (defined ALSA_VER_ANDROID_3_0) + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct mc1n2_data *mc1n2 = codec->private_data; +#endif + struct mc1n2_port_params *port = &mc1n2->port[get_port_id(dai->id)]; + + switch (div_id) { + case MC1N2_BCLK_MULT: + switch (div) { + case MC1N2_LRCK_X32: + port->bckfs = MCDRV_BCKFS_32; + break; + case MC1N2_LRCK_X48: + port->bckfs = MCDRV_BCKFS_48; + break; + case MC1N2_LRCK_X64: + port->bckfs = MCDRV_BCKFS_64; + break; + default: + return -EINVAL; + } + break; + default: + return mc1n2_set_clkdiv_common(mc1n2, div_id, div); + } + + return 0; +} + +static int mc1n2_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; +#if (defined ALSA_VER_ANDROID_2_6_35) || (defined ALSA_VER_ANDROID_3_0) + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct mc1n2_data *mc1n2 = codec->private_data; +#endif + struct mc1n2_port_params *port = &mc1n2->port[get_port_id(dai->id)]; + + /* format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + port->format = MCDRV_DAMODE_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + port->format = MCDRV_DAMODE_TAILALIGN; + break; + case SND_SOC_DAIFMT_LEFT_J: + port->format = MCDRV_DAMODE_HEADALIGN; + break; + default: + return -EINVAL; + } + + return mc1n2_set_fmt_common(port, fmt); +} + +static int mc1n2_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ +#ifdef ALSA_VER_ANDROID_3_0 + struct snd_soc_codec *codec = dai->codec; +#else + struct snd_soc_pcm_runtime *runtime = snd_pcm_substream_chip(substream); +#ifdef ALSA_VER_1_0_19 + struct snd_soc_codec *codec = runtime->socdev->codec; +#else + struct snd_soc_codec *codec = runtime->socdev->card->codec; +#endif +#endif +#if (defined ALSA_VER_ANDROID_2_6_35) || (defined ALSA_VER_ANDROID_3_0) + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct mc1n2_data *mc1n2 = codec->private_data; +#endif + struct mc1n2_port_params *port = &mc1n2->port[get_port_id(dai->id)]; + int dir = substream->stream; + int rate; + int err = 0; + + dbg_info("hw_params: [%d] name=%s, dir=%d, rate=%d, bits=%d, ch=%d\n", + get_port_id(dai->id), substream->name, dir, + params_rate(params), params_format(params), params_channels(params)); + + /* format (bits) */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + port->bits[dir] = MCDRV_BITSEL_16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + port->bits[dir] = MCDRV_BITSEL_20; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + port->bits[dir] = MCDRV_BITSEL_24; + break; + default: + return -EINVAL; + } + + /* rate */ + switch (params_rate(params)) { + case 8000: + rate = MCDRV_FS_8000; + break; + case 11025: + rate = MCDRV_FS_11025; + break; + case 16000: + rate = MCDRV_FS_16000; + break; + case 22050: + rate = MCDRV_FS_22050; + break; + case 32000: + rate = MCDRV_FS_32000; + break; + case 44100: + rate = MCDRV_FS_44100; + break; + case 48000: + rate = MCDRV_FS_48000; + break; + default: + return -EINVAL; + } + + mutex_lock(&mc1n2->mutex); + + if ((port->stream & ~(1 << dir)) && (rate != port->rate)) { + err = -EBUSY; + goto error; + } + +#ifdef CONFIG_SND_SAMSUNG_RP + if ((dir == SNDRV_PCM_STREAM_PLAYBACK) && (get_port_id(dai->id) == 0) + && (port->stream & (1 << dir)) && (rate == port->rate)) { + /* During ULP Audio, DAI should not be touched + if i2s port already opened. */ + err = 0; + goto error; + } +#endif + + port->rate = rate; + port->channels = params_channels(params); + + err = mc1n2_update_clock(mc1n2); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, "%d: Error in mc1n2_update_clock\n", err); + err = -EIO; + goto error; + } + + err = mc1n2_setup_dai(mc1n2, get_port_id(dai->id), MCDRV_DIO_DA, dir); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, "%d: Error in mc1n2_setup_dai\n", err); + err = -EIO; + goto error; + } + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + err = mc1n2_control_dir(mc1n2, get_port_id(dai->id), 1); + } else { + err = mc1n2_control_dit(mc1n2, get_port_id(dai->id), 1); + } + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, "%d: Error in mc1n2_control_dir/dit\n", err); + err = -EIO; + goto error; + } + + port->stream |= (1 << dir); + +error: + mutex_unlock(&mc1n2->mutex); + + return err; +} + +static int mc1n2_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ +#ifdef ALSA_VER_ANDROID_3_0 + struct snd_soc_codec *codec = dai->codec; +#else + struct snd_soc_pcm_runtime *runtime = snd_pcm_substream_chip(substream); +#ifdef ALSA_VER_1_0_19 + struct snd_soc_codec *codec = runtime->socdev->codec; +#else + struct snd_soc_codec *codec = runtime->socdev->card->codec; +#endif +#endif +#if (defined ALSA_VER_ANDROID_2_6_35) || (defined ALSA_VER_ANDROID_3_0) + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct mc1n2_data *mc1n2 = codec->private_data; +#endif + struct mc1n2_port_params *port = &mc1n2->port[get_port_id(dai->id)]; + int dir = substream->stream; + int err; + + mutex_lock(&mc1n2->mutex); + + if (!(port->stream & (1 << dir))) { + err = 0; + goto error; + } + +#ifdef CONFIG_SND_SAMSUNG_RP + if ((dir == SNDRV_PCM_STREAM_PLAYBACK) && (get_port_id(dai->id) == 0)) { + /* Leave codec opened during ULP Audio */ + err = 0; + goto error; + } +#endif + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + err = mc1n2_control_dir(mc1n2, get_port_id(dai->id), 0); + } else { + err = mc1n2_control_dit(mc1n2, get_port_id(dai->id), 0); + } + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, "%d: Error in mc1n2_control_dir/dit\n", err); + err = -EIO; + goto error; + } + + port->stream &= ~(1 << dir); + +error: + mutex_unlock(&mc1n2->mutex); + + return err; +} + +static int mc1n2_pcm_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) +{ + struct snd_soc_codec *codec = dai->codec; +#if (defined ALSA_VER_ANDROID_2_6_35) || (defined ALSA_VER_ANDROID_3_0) + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct mc1n2_data *mc1n2 = codec->private_data; +#endif + struct mc1n2_port_params *port = &mc1n2->port[get_port_id(dai->id)]; + + switch (div_id) { + case MC1N2_BCLK_MULT: + switch (div) { + case MC1N2_LRCK_X8: + port->bckfs = MCDRV_BCKFS_16; + port->pcm_clkdown = MCDRV_PCM_CLKDOWN_HALF; + break; + case MC1N2_LRCK_X16: + port->bckfs = MCDRV_BCKFS_16; + port->pcm_clkdown = MCDRV_PCM_CLKDOWN_OFF; + break; + case MC1N2_LRCK_X24: + port->bckfs = MCDRV_BCKFS_48; + port->pcm_clkdown = MCDRV_PCM_CLKDOWN_HALF; + break; + case MC1N2_LRCK_X32: + port->bckfs = MCDRV_BCKFS_32; + port->pcm_clkdown = MCDRV_PCM_CLKDOWN_OFF; + break; + case MC1N2_LRCK_X48: + port->bckfs = MCDRV_BCKFS_48; + port->pcm_clkdown = MCDRV_PCM_CLKDOWN_OFF; + break; + case MC1N2_LRCK_X64: + port->bckfs = MCDRV_BCKFS_64; + port->pcm_clkdown = MCDRV_PCM_CLKDOWN_OFF; + break; + case MC1N2_LRCK_X128: + port->bckfs = MCDRV_BCKFS_128; + port->pcm_clkdown = MCDRV_PCM_CLKDOWN_OFF; + break; + case MC1N2_LRCK_X256: + port->bckfs = MCDRV_BCKFS_256; + port->pcm_clkdown = MCDRV_PCM_CLKDOWN_OFF; + break; + case MC1N2_LRCK_X512: + port->bckfs = MCDRV_BCKFS_512; + port->pcm_clkdown = MCDRV_PCM_CLKDOWN_OFF; + break; + } + break; + default: + return mc1n2_set_clkdiv_common(mc1n2, div_id, div); + } + + return 0; +} + +static int mc1n2_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; +#if (defined ALSA_VER_ANDROID_2_6_35) || (defined ALSA_VER_ANDROID_3_0) + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct mc1n2_data *mc1n2 = codec->private_data; +#endif + struct mc1n2_port_params *port = &mc1n2->port[get_port_id(dai->id)]; + + /* format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + port->format = MCDRV_PCM_SHORTFRAME; + break; + case SND_SOC_DAIFMT_DSP_B: + port->format = MCDRV_PCM_LONGFRAME; + break; + default: + return -EINVAL; + } + + return mc1n2_set_fmt_common(port, fmt); +} + +static int mc1n2_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ +#ifdef ALSA_VER_ANDROID_3_0 + struct snd_soc_codec *codec = dai->codec; +#else + struct snd_soc_pcm_runtime *runtime = snd_pcm_substream_chip(substream); +#ifdef ALSA_VER_1_0_19 + struct snd_soc_codec *codec = runtime->socdev->codec; +#else + struct snd_soc_codec *codec = runtime->socdev->card->codec; +#endif +#endif +#if (defined ALSA_VER_ANDROID_2_6_35) || (defined ALSA_VER_ANDROID_3_0) + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct mc1n2_data *mc1n2 = codec->private_data; +#endif + struct mc1n2_port_params *port = &mc1n2->port[get_port_id(dai->id)]; + int dir = substream->stream; + int rate; + int err; + + dbg_info("hw_params: [%d] name=%s, dir=%d, rate=%d, bits=%d, ch=%d\n", + get_port_id(dai->id), substream->name, dir, + params_rate(params), params_format(params), params_channels(params)); + + /* channels */ + switch (params_channels(params)) { + case 1: + port->pcm_mono[dir] = MCDRV_PCM_MONO; + break; + case 2: + port->pcm_mono[dir] = MCDRV_PCM_STEREO; + break; + default: + return -EINVAL; + } + + /* format (bits) */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + port->bits[dir] = MCDRV_PCM_BITSEL_8; + port->pcm_order[dir] = MCDRV_PCM_MSB_FIRST; + port->pcm_law[dir] = MCDRV_PCM_LINEAR; + break; + case SNDRV_PCM_FORMAT_S16_LE: + port->bits[dir] = MCDRV_PCM_BITSEL_16; + port->pcm_order[dir] = MCDRV_PCM_LSB_FIRST; + port->pcm_law[dir] = MCDRV_PCM_LINEAR; + break; + case SNDRV_PCM_FORMAT_S16_BE: + port->bits[dir] = MCDRV_PCM_BITSEL_16; + port->pcm_order[dir] = MCDRV_PCM_MSB_FIRST; + port->pcm_law[dir] = MCDRV_PCM_LINEAR; + break; + case SNDRV_PCM_FORMAT_A_LAW: + port->bits[dir] = MCDRV_PCM_BITSEL_8; + port->pcm_order[dir] = MCDRV_PCM_MSB_FIRST; + port->pcm_law[dir] = MCDRV_PCM_ALAW; + break; + case SNDRV_PCM_FORMAT_MU_LAW: + port->bits[dir] = MCDRV_PCM_BITSEL_8; + port->pcm_order[dir] = MCDRV_PCM_MSB_FIRST; + port->pcm_law[dir] = MCDRV_PCM_MULAW; + break; + default: + return -EINVAL; + } + + /* rate */ + switch (params_rate(params)) { + case 8000: + rate = MCDRV_FS_8000; + break; + case 16000: + rate = MCDRV_FS_16000; + break; + default: + return -EINVAL; + } + + mutex_lock(&mc1n2->mutex); + + if ((port->stream & ~(1 << dir)) && (rate != port->rate)) { + err = -EBUSY; + goto error; + } + + port->rate = rate; + port->channels = params_channels(params); + + err = mc1n2_update_clock(mc1n2); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, "%d: Error in mc1n2_update_clock\n", err); + err = -EIO; + goto error; + } + + err = mc1n2_setup_dai(mc1n2, get_port_id(dai->id), MCDRV_DIO_PCM, dir); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, "%d: Error in mc1n2_setup_dai\n", err); + err = -EIO; + goto error; + } + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + err = mc1n2_control_dir(mc1n2, get_port_id(dai->id), 1); + } else { + err = mc1n2_control_dit(mc1n2, get_port_id(dai->id), 1); + } + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, "%d: Error in mc1n2_control_dir/dit\n", err); + err = -EIO; + goto error; + } + + port->stream |= (1 << dir); + +error: + mutex_unlock(&mc1n2->mutex); + + return err; +} + +#ifndef ALSA_VER_1_0_19 +static struct snd_soc_dai_ops mc1n2_dai_ops[] = { + { + .set_clkdiv = mc1n2_i2s_set_clkdiv, + .set_fmt = mc1n2_i2s_set_fmt, + .hw_params = mc1n2_i2s_hw_params, + .hw_free = mc1n2_hw_free, + }, + { + .set_clkdiv = mc1n2_pcm_set_clkdiv, + .set_fmt = mc1n2_pcm_set_fmt, + .hw_params = mc1n2_pcm_hw_params, + .hw_free = mc1n2_hw_free, + }, + { + .set_clkdiv = mc1n2_i2s_set_clkdiv, + .set_fmt = mc1n2_i2s_set_fmt, + .hw_params = mc1n2_i2s_hw_params, + .hw_free = mc1n2_hw_free, + }, + { + .set_clkdiv = mc1n2_pcm_set_clkdiv, + .set_fmt = mc1n2_pcm_set_fmt, + .hw_params = mc1n2_pcm_hw_params, + .hw_free = mc1n2_hw_free, + }, + { + .set_clkdiv = mc1n2_i2s_set_clkdiv, + .set_fmt = mc1n2_i2s_set_fmt, + .hw_params = mc1n2_i2s_hw_params, + .hw_free = mc1n2_hw_free, + }, + { + .set_clkdiv = mc1n2_pcm_set_clkdiv, + .set_fmt = mc1n2_pcm_set_fmt, + .hw_params = mc1n2_pcm_hw_params, + .hw_free = mc1n2_hw_free, + } +}; +#endif + + +#ifdef ALSA_VER_ANDROID_3_0 +struct snd_soc_dai_driver mc1n2_dai[] = { +#else +struct snd_soc_dai mc1n2_dai[] = { +#endif + { + .name = MC1N2_NAME "-da0i", + .id = 1, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MC1N2_I2S_RATE, + .formats = MC1N2_I2S_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MC1N2_I2S_RATE, + .formats = MC1N2_I2S_FORMATS, + }, +#ifdef ALSA_VER_1_0_19 + .ops = { + .set_clkdiv = mc1n2_i2s_set_clkdiv, + .set_fmt = mc1n2_i2s_set_fmt, + .hw_params = mc1n2_i2s_hw_params, + .hw_free = mc1n2_hw_free, + } +#else + .ops = &mc1n2_dai_ops[0] +#endif + }, + { + .name = MC1N2_NAME "-da0p", + .id = 1, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MC1N2_PCM_RATE, + .formats = MC1N2_PCM_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MC1N2_PCM_RATE, + .formats = MC1N2_PCM_FORMATS, + }, +#ifdef ALSA_VER_1_0_19 + .ops = { + .set_clkdiv = mc1n2_pcm_set_clkdiv, + .set_fmt = mc1n2_pcm_set_fmt, + .hw_params = mc1n2_pcm_hw_params, + .hw_free = mc1n2_hw_free, + } +#else + .ops = &mc1n2_dai_ops[1] +#endif + }, + { + .name = MC1N2_NAME "-da1i", + .id = 2, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MC1N2_I2S_RATE, + .formats = MC1N2_I2S_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MC1N2_I2S_RATE, + .formats = MC1N2_I2S_FORMATS, + }, +#ifdef ALSA_VER_1_0_19 + .ops = { + .set_clkdiv = mc1n2_i2s_set_clkdiv, + .set_fmt = mc1n2_i2s_set_fmt, + .hw_params = mc1n2_i2s_hw_params, + .hw_free = mc1n2_hw_free, + } +#else + .ops = &mc1n2_dai_ops[2] +#endif + }, + { + .name = MC1N2_NAME "-da1p", + .id = 2, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MC1N2_PCM_RATE, + .formats = MC1N2_PCM_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MC1N2_PCM_RATE, + .formats = MC1N2_PCM_FORMATS, + }, +#ifdef ALSA_VER_1_0_19 + .ops = { + .set_clkdiv = mc1n2_pcm_set_clkdiv, + .set_fmt = mc1n2_pcm_set_fmt, + .hw_params = mc1n2_pcm_hw_params, + .hw_free = mc1n2_hw_free, + } +#else + .ops = &mc1n2_dai_ops[3] +#endif + }, + { + .name = MC1N2_NAME "-da2i", + .id = 3, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MC1N2_I2S_RATE, + .formats = MC1N2_I2S_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MC1N2_I2S_RATE, + .formats = MC1N2_I2S_FORMATS, + }, +#ifdef ALSA_VER_1_0_19 + .ops = { + .set_clkdiv = mc1n2_i2s_set_clkdiv, + .set_fmt = mc1n2_i2s_set_fmt, + .hw_params = mc1n2_i2s_hw_params, + .hw_free = mc1n2_hw_free, + } +#else + .ops = &mc1n2_dai_ops[4] +#endif + }, + { + .name = MC1N2_NAME "-da2p", + .id = 3, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MC1N2_PCM_RATE, + .formats = MC1N2_PCM_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MC1N2_PCM_RATE, + .formats = MC1N2_PCM_FORMATS, + }, +#ifdef ALSA_VER_1_0_19 + .ops = { + .set_clkdiv = mc1n2_pcm_set_clkdiv, + .set_fmt = mc1n2_pcm_set_fmt, + .hw_params = mc1n2_pcm_hw_params, + .hw_free = mc1n2_hw_free, + } +#else + .ops = &mc1n2_dai_ops[5] +#endif + }, +}; +#ifndef ALSA_VER_ANDROID_3_0 +EXPORT_SYMBOL_GPL(mc1n2_dai); +#endif + +/* + * Control interface + */ +/* + * Virtual register + * + * 16bit software registers are implemented for volumes and mute + * switches (as an exception, no mute switches for MIC and HP gain). + * Register contents are stored in codec's register cache. + * + * 15 14 8 7 6 0 + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * |swR| volume-R |swL| volume-L | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + */ +struct mc1n2_vreg_info { + size_t offset; + SINT16 *volmap; +}; + +/* volmap for Digital Volumes */ +static SINT16 mc1n2_vol_digital[] = { + 0xa000, 0xb600, 0xb700, 0xb800, 0xb900, 0xba00, 0xbb00, 0xbc00, + 0xbd00, 0xbe00, 0xbf00, 0xc000, 0xc100, 0xc200, 0xc300, 0xc400, + 0xc500, 0xc600, 0xc700, 0xc800, 0xc900, 0xca00, 0xcb00, 0xcc00, + 0xcd00, 0xce00, 0xcf00, 0xd000, 0xd100, 0xd200, 0xd300, 0xd400, + 0xd500, 0xd600, 0xd700, 0xd800, 0xd900, 0xda00, 0xdb00, 0xdc00, + 0xdd00, 0xde00, 0xdf00, 0xe000, 0xe100, 0xe200, 0xe300, 0xe400, + 0xe500, 0xe600, 0xe700, 0xe800, 0xe900, 0xea00, 0xeb00, 0xec00, + 0xed00, 0xee00, 0xef00, 0xf000, 0xf100, 0xf200, 0xf300, 0xf400, + 0xf500, 0xf600, 0xf700, 0xf800, 0xf900, 0xfa00, 0xfb00, 0xfc00, + 0xfd00, 0xfe00, 0xff00, 0x0000, 0x0100, 0x0200, 0x0300, 0x0400, + 0x0500, 0x0600, 0x0700, 0x0800, 0x0900, 0x0a00, 0x0b00, 0x0c00, + 0x0d00, 0x0e00, 0x0f00, 0x1000, 0x1100, 0x1200, +}; + +/* volmap for ADC Analog Volume */ +static SINT16 mc1n2_vol_adc[] = { + 0xa000, 0xe500, 0xe680, 0xe800, 0xe980, 0xeb00, 0xec80, 0xee00, + 0xef80, 0xf100, 0xf280, 0xf400, 0xf580, 0xf700, 0xf880, 0xfa00, + 0xfb80, 0xfd00, 0xfe80, 0x0000, 0x0180, 0x0300, 0x0480, 0x0600, + 0x0780, 0x0900, 0x0a80, 0x0c00, 0x0d80, 0x0f00, 0x1080, 0x1200, +}; + +/* volmap for LINE/MIC Input Volumes */ +static SINT16 mc1n2_vol_ain[] = { + 0xa000, 0xe200, 0xe380, 0xe500, 0xe680, 0xe800, 0xe980, 0xeb00, + 0xec80, 0xee00, 0xef80, 0xf100, 0xf280, 0xf400, 0xf580, 0xf700, + 0xf880, 0xfa00, 0xfb80, 0xfd00, 0xfe80, 0x0000, 0x0180, 0x0300, + 0x0480, 0x0600, 0x0780, 0x0900, 0x0a80, 0x0c00, 0x0d80, 0x0f00, +}; + +/* volmap for HP/SP Output Volumes */ +static SINT16 mc1n2_vol_hpsp[] = { + 0xa000, 0xdc00, 0xe400, 0xe800, 0xea00, 0xec00, 0xee00, 0xf000, + 0xf100, 0xf200, 0xf300, 0xf400, 0xf500, 0xf600, 0xf700, 0xf800, + 0xf880, 0xf900, 0xf980, 0xfa00, 0xfa80, 0xfb00, 0xfb80, 0xfc00, + 0xfc80, 0xfd00, 0xfd80, 0xfe00, 0xfe80, 0xff00, 0xff80, 0x0000, +}; + +/* volmap for RC/LINE Output Volumes */ +static SINT16 mc1n2_vol_aout[] = { + 0xa000, 0xe200, 0xe300, 0xe400, 0xe500, 0xe600, 0xe700, 0xe800, + 0xe900, 0xea00, 0xeb00, 0xec00, 0xed00, 0xee00, 0xef00, 0xf000, + 0xf100, 0xf200, 0xf300, 0xf400, 0xf500, 0xf600, 0xf700, 0xf800, + 0xf900, 0xfa00, 0xfb00, 0xfc00, 0xfd00, 0xfe00, 0xff00, 0x0000, +}; + +/* volmap for MIC Gain Volumes */ +static SINT16 mc1n2_vol_micgain[] = { + 0x0f00, 0x1400, 0x1900, 0x1e00, +}; + +/* volmap for HP Gain Volume */ +static SINT16 mc1n2_vol_hpgain[] = { + 0x0000, 0x0180, 0x0300, 0x0600, +}; + +struct mc1n2_vreg_info mc1n2_vreg_map[MC1N2_N_VOL_REG] = { + {offsetof(MCDRV_VOL_INFO, aswD_Ad0), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_Aeng6), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_Pdm), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_Dir0), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_Dir1), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_Dir2), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_Ad0Att), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_Dir0Att), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_Dir1Att), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_Dir2Att), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_SideTone), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_DacMaster), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_DacVoice), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_DacAtt), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_Dit0), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_Dit1), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswD_Dit2), mc1n2_vol_digital}, + {offsetof(MCDRV_VOL_INFO, aswA_Ad0), mc1n2_vol_adc}, + {offsetof(MCDRV_VOL_INFO, aswA_Lin1), mc1n2_vol_ain}, + {offsetof(MCDRV_VOL_INFO, aswA_Mic1), mc1n2_vol_ain}, + {offsetof(MCDRV_VOL_INFO, aswA_Mic2), mc1n2_vol_ain}, + {offsetof(MCDRV_VOL_INFO, aswA_Mic3), mc1n2_vol_ain}, + {offsetof(MCDRV_VOL_INFO, aswA_Hp), mc1n2_vol_hpsp}, + {offsetof(MCDRV_VOL_INFO, aswA_Sp), mc1n2_vol_hpsp}, + {offsetof(MCDRV_VOL_INFO, aswA_Rc), mc1n2_vol_hpsp}, + {offsetof(MCDRV_VOL_INFO, aswA_Lout1), mc1n2_vol_aout}, + {offsetof(MCDRV_VOL_INFO, aswA_Lout2), mc1n2_vol_aout}, + {offsetof(MCDRV_VOL_INFO, aswA_Mic1Gain), mc1n2_vol_micgain}, + {offsetof(MCDRV_VOL_INFO, aswA_Mic2Gain), mc1n2_vol_micgain}, + {offsetof(MCDRV_VOL_INFO, aswA_Mic3Gain), mc1n2_vol_micgain}, + {offsetof(MCDRV_VOL_INFO, aswA_HpGain), mc1n2_vol_hpgain}, +}; + +#ifdef ALSA_VER_ANDROID_3_0 +static int cache_read(struct snd_soc_codec *codec, unsigned int reg) +{ + int ret; + unsigned int val; + + ret = snd_soc_cache_read(codec, reg, &val); + if (ret != 0) { + dev_err(codec->dev, "Cache read to %x failed: %d\n", reg, ret); + return -EIO; + } + return val; +} +static int cache_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + return ((int)snd_soc_cache_write(codec, reg, value)); +} +#else +static int cache_read(struct snd_soc_codec *codec, unsigned int reg) +{ + return ((u16 *)codec->reg_cache)[reg]; +} +static int cache_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + u16 *cp = (u16 *)codec->reg_cache + reg; + *cp = value; + return 0; +} +#endif + +static unsigned int mc1n2_read_reg(struct snd_soc_codec *codec, unsigned int reg) +{ + int ret; + + ret = cache_read(codec, reg); + if (ret < 0) { + return -EIO; + } + return (unsigned int)ret; +} + +#ifdef ALSA_VER_ANDROID_3_0 +#define REG_CACHE_READ(reg) (mc1n2_read_reg(codec, reg)) +#else +#define REG_CACHE_READ(reg) ((u16 *)codec->reg_cache)[reg] +#endif + +static int write_reg_vol(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + MCDRV_VOL_INFO update; + SINT16 *vp; + int ret; + int err, i; + + memset(&update, 0, sizeof(MCDRV_VOL_INFO)); + vp = (SINT16 *)((void *)&update + mc1n2_vreg_map[reg].offset); + + for (i = 0; i < 2; i++, vp++) { + unsigned int v = (value >> (i*8)) & 0xff; + unsigned int c = (mc1n2_read_reg(codec, reg) >> (i*8)) & 0xff; + if (v != c) { + int sw, vol; + SINT16 db; + sw = (reg < MC1N2_AVOL_MIC1_GAIN) ? (v & 0x80) : 1; + vol = sw ? (v & 0x7f) : 0; + db = mc1n2_vreg_map[reg].volmap[vol]; + *vp = db | MCDRV_VOL_UPDATE; + } + } + + err = _McDrv_Ctrl(MCDRV_SET_VOLUME, &update, 0); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, "%d: Error in MCDRV_SET_VOLUME\n", err); + return -EIO; + } + ret = cache_write(codec, reg, value); + if (ret != 0) { + dev_err(codec->dev, "Cache write to %x failed: %d\n", reg, ret); + } + + return 0; +} + +static int mc1n2_hwdep_ioctl_set_path(struct snd_soc_codec *codec, + void *info, unsigned int update); + +static int write_reg_path(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + MCDRV_PATH_INFO update; + MCDRV_CHANNEL *pch; + MCDRV_AE_INFO *pae; + int ret = 0; + int err; + + memset(&update, 0, sizeof(MCDRV_PATH_INFO)); + + ret = cache_write(codec, reg, value); + if (ret != 0) { + dev_err(codec->dev, "Cache write to %x failed: %d\n",reg, ret); + } + + switch (reg) { + case MC1N2_ADCL_MIC1_SW: + if (value) { + update.asAdc0[0].abSrcOnOff[0] = MCDRV_SRC0_MIC1_ON; + } + else { + update.asAdc0[0].abSrcOnOff[0] = MCDRV_SRC0_MIC1_OFF; + } + break; + case MC1N2_ADCL_MIC2_SW: + if (value) { + update.asAdc0[0].abSrcOnOff[0] = MCDRV_SRC0_MIC2_ON; + } + else { + update.asAdc0[0].abSrcOnOff[0] = MCDRV_SRC0_MIC2_OFF; + } + break; + case MC1N2_ADCL_MIC3_SW: + if (value) { + update.asAdc0[0].abSrcOnOff[0] = MCDRV_SRC0_MIC3_ON; + } + else { + update.asAdc0[0].abSrcOnOff[0] = MCDRV_SRC0_MIC3_OFF; + } + break; + case MC1N2_ADCL_LINE_SW: + case MC1N2_ADCL_LINE_SRC: + if (REG_CACHE_READ(MC1N2_ADCL_LINE_SRC) == 0) { + if (REG_CACHE_READ(MC1N2_ADCL_LINE_SW)) { + update.asAdc0[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_L_ON; + } + else { + update.asAdc0[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_L_OFF; + } + } + else { + if (REG_CACHE_READ(MC1N2_ADCL_LINE_SW)) { + update.asAdc0[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_ON; + } + else { + update.asAdc0[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_OFF; + } + } + break; + case MC1N2_ADCR_MIC1_SW: + if (value) { + update.asAdc0[1].abSrcOnOff[0] = MCDRV_SRC0_MIC1_ON; + } + else { + update.asAdc0[1].abSrcOnOff[0] = MCDRV_SRC0_MIC1_OFF; + } + break; + case MC1N2_ADCR_MIC2_SW: + if (value) { + update.asAdc0[1].abSrcOnOff[0] = MCDRV_SRC0_MIC2_ON; + } + else { + update.asAdc0[1].abSrcOnOff[0] = MCDRV_SRC0_MIC2_OFF; + } + break; + case MC1N2_ADCR_MIC3_SW: + if (value) { + update.asAdc0[1].abSrcOnOff[0] = MCDRV_SRC0_MIC3_ON; + } + else { + update.asAdc0[1].abSrcOnOff[0] = MCDRV_SRC0_MIC3_OFF; + } + break; + case MC1N2_ADCR_LINE_SW: + case MC1N2_ADCR_LINE_SRC: + if (REG_CACHE_READ(MC1N2_ADCR_LINE_SRC) == 0) { + if (REG_CACHE_READ(MC1N2_ADCR_LINE_SW)) { + update.asAdc0[1].abSrcOnOff[1] = MCDRV_SRC1_LINE1_R_ON; + } + else { + update.asAdc0[1].abSrcOnOff[1] = MCDRV_SRC1_LINE1_R_OFF; + } + } + else { + if (REG_CACHE_READ(MC1N2_ADCR_LINE_SW)) { + update.asAdc0[1].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_ON; + } + else { + update.asAdc0[1].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_OFF; + } + } + break; + case MC1N2_HPL_MIC1_SW: + if (value) { + update.asHpOut[0].abSrcOnOff[0] = MCDRV_SRC0_MIC1_ON; + } + else { + update.asHpOut[0].abSrcOnOff[0] = MCDRV_SRC0_MIC1_OFF; + } + break; + case MC1N2_HPL_MIC2_SW: + if (value) { + update.asHpOut[0].abSrcOnOff[0] = MCDRV_SRC0_MIC2_ON; + } + else { + update.asHpOut[0].abSrcOnOff[0] = MCDRV_SRC0_MIC2_OFF; + } + break; + case MC1N2_HPL_MIC3_SW: + if (value) { + update.asHpOut[0].abSrcOnOff[0] = MCDRV_SRC0_MIC3_ON; + } + else { + update.asHpOut[0].abSrcOnOff[0] = MCDRV_SRC0_MIC3_OFF; + } + break; + case MC1N2_HPL_LINE_SW: + case MC1N2_HPL_LINE_SRC: + if (REG_CACHE_READ(MC1N2_HPL_LINE_SRC) == 0) { + if (REG_CACHE_READ(MC1N2_HPL_LINE_SW)) { + update.asHpOut[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_L_ON; + } + else { + update.asHpOut[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_L_OFF; + } + } + else { + if (REG_CACHE_READ(MC1N2_HPL_LINE_SW)) { + update.asHpOut[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_ON; + } + else { + update.asHpOut[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_OFF; + } + } + break; + case MC1N2_HPL_DAC_SW: + case MC1N2_HPL_DAC_SRC: + if (REG_CACHE_READ(MC1N2_HPL_DAC_SRC) == 0) { + if (REG_CACHE_READ(MC1N2_HPL_DAC_SW)) { + update.asHpOut[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_L_ON; + } + else { + update.asHpOut[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_L_OFF; + } + } + else { + if (REG_CACHE_READ(MC1N2_HPL_DAC_SW)) { + update.asHpOut[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_M_ON; + } + else { + update.asHpOut[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_M_OFF; + } + } + break; + case MC1N2_HPR_MIC1_SW: + if (value) { + update.asHpOut[1].abSrcOnOff[0] = MCDRV_SRC0_MIC1_ON; + } + else { + update.asHpOut[1].abSrcOnOff[0] = MCDRV_SRC0_MIC1_OFF; + } + break; + case MC1N2_HPR_MIC2_SW: + if (value) { + update.asHpOut[1].abSrcOnOff[0] = MCDRV_SRC0_MIC2_ON; + } + else { + update.asHpOut[1].abSrcOnOff[0] = MCDRV_SRC0_MIC2_OFF; + } + break; + case MC1N2_HPR_MIC3_SW: + if (value) { + update.asHpOut[1].abSrcOnOff[0] = MCDRV_SRC0_MIC3_ON; + } + else { + update.asHpOut[1].abSrcOnOff[0] = MCDRV_SRC0_MIC3_OFF; + } + break; + case MC1N2_HPR_LINER_SW: + if (value) { + update.asHpOut[1].abSrcOnOff[1] = MCDRV_SRC1_LINE1_R_ON; + } + else { + update.asHpOut[1].abSrcOnOff[1] = MCDRV_SRC1_LINE1_R_OFF; + } + break; + case MC1N2_HPR_DACR_SW: + if (value) { + update.asHpOut[1].abSrcOnOff[5] = MCDRV_SRC5_DAC_R_ON; + } + else { + update.asHpOut[1].abSrcOnOff[5] = MCDRV_SRC5_DAC_R_OFF; + } + break; + case MC1N2_SPL_LINE_SW: + case MC1N2_SPL_LINE_SRC: + if (REG_CACHE_READ(MC1N2_SPL_LINE_SRC) == 0) { + if (REG_CACHE_READ(MC1N2_SPL_LINE_SW)) { + update.asSpOut[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_L_ON; + } + else { + update.asSpOut[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_L_OFF; + } + } + else { + if (REG_CACHE_READ(MC1N2_SPL_LINE_SW)) { + update.asSpOut[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_ON; + } + else { + update.asSpOut[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_OFF; + } + } + break; + case MC1N2_SPL_DAC_SW: + case MC1N2_SPL_DAC_SRC: + if (REG_CACHE_READ(MC1N2_SPL_DAC_SRC) == 0) { + if (REG_CACHE_READ(MC1N2_SPL_DAC_SW)) { + update.asSpOut[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_L_ON; + } + else { + update.asSpOut[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_L_OFF; + } + } + else { + if (REG_CACHE_READ(MC1N2_SPL_DAC_SW)) { + update.asSpOut[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_M_ON; + } + else { + update.asSpOut[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_M_OFF; + } + } + break; + case MC1N2_SPR_LINE_SW: + case MC1N2_SPR_LINE_SRC: + if (REG_CACHE_READ(MC1N2_SPR_LINE_SRC) == 0) { + if (REG_CACHE_READ(MC1N2_SPR_LINE_SW)) { + update.asSpOut[1].abSrcOnOff[1] = MCDRV_SRC1_LINE1_R_ON; + } + else { + update.asSpOut[1].abSrcOnOff[1] = MCDRV_SRC1_LINE1_R_OFF; + } + } + else { + if (REG_CACHE_READ(MC1N2_SPR_LINE_SW)) { + update.asSpOut[1].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_ON; + } + else { + update.asSpOut[1].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_OFF; + } + } + break; + case MC1N2_SPR_DAC_SW: + case MC1N2_SPR_DAC_SRC: + if (REG_CACHE_READ(MC1N2_SPR_DAC_SRC) == 0) { + if (REG_CACHE_READ(MC1N2_SPR_DAC_SW)) { + update.asSpOut[1].abSrcOnOff[5] = MCDRV_SRC5_DAC_R_ON; + } + else { + update.asSpOut[1].abSrcOnOff[5] = MCDRV_SRC5_DAC_R_OFF; + } + } + else { + if (REG_CACHE_READ(MC1N2_SPR_DAC_SW)) { + update.asSpOut[1].abSrcOnOff[5] = MCDRV_SRC5_DAC_M_ON; + } + else { + update.asSpOut[1].abSrcOnOff[5] = MCDRV_SRC5_DAC_M_OFF; + } + } + break; + case MC1N2_RC_MIC1_SW: + if (value) { + update.asRcOut[0].abSrcOnOff[0] = MCDRV_SRC0_MIC1_ON; + } + else { + update.asRcOut[0].abSrcOnOff[0] = MCDRV_SRC0_MIC1_OFF; + } + break; + case MC1N2_RC_MIC2_SW: + if (value) { + update.asRcOut[0].abSrcOnOff[0] = MCDRV_SRC0_MIC2_ON; + } + else { + update.asRcOut[0].abSrcOnOff[0] = MCDRV_SRC0_MIC2_OFF; + } + break; + case MC1N2_RC_MIC3_SW: + if (value) { + update.asRcOut[0].abSrcOnOff[0] = MCDRV_SRC0_MIC3_ON; + } + else { + update.asRcOut[0].abSrcOnOff[0] = MCDRV_SRC0_MIC3_OFF; + } + break; + case MC1N2_RC_LINEMONO_SW: + if (value) { + update.asRcOut[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_ON; + } + else { + update.asRcOut[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_OFF; + } + break; + case MC1N2_RC_DACL_SW: + if (value) { + update.asRcOut[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_L_ON; + } + else { + update.asRcOut[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_L_OFF; + } + break; + case MC1N2_RC_DACR_SW: + if (value) { + update.asRcOut[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_R_ON; + } + else { + update.asRcOut[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_R_OFF; + } + break; + case MC1N2_LOUT1L_MIC1_SW: + if (value) { + update.asLout1[0].abSrcOnOff[0] = MCDRV_SRC0_MIC1_ON; + } + else { + update.asLout1[0].abSrcOnOff[0] = MCDRV_SRC0_MIC1_OFF; + } + break; + case MC1N2_LOUT1L_MIC2_SW: + if (value) { + update.asLout1[0].abSrcOnOff[0] = MCDRV_SRC0_MIC2_ON; + } + else { + update.asLout1[0].abSrcOnOff[0] = MCDRV_SRC0_MIC2_OFF; + } + break; + case MC1N2_LOUT1L_MIC3_SW: + if (value) { + update.asLout1[0].abSrcOnOff[0] = MCDRV_SRC0_MIC3_ON; + } + else { + update.asLout1[0].abSrcOnOff[0] = MCDRV_SRC0_MIC3_OFF; + } + break; + case MC1N2_LOUT1L_LINE_SW: + case MC1N2_LOUT1L_LINE_SRC: + if (REG_CACHE_READ(MC1N2_LOUT1L_LINE_SRC) == 0) { + if (REG_CACHE_READ(MC1N2_LOUT1L_LINE_SW)) { + update.asLout1[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_L_ON; + } + else { + update.asLout1[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_L_OFF; + } + } + else { + if (REG_CACHE_READ(MC1N2_LOUT1L_LINE_SW)) { + update.asLout1[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_ON; + } + else { + update.asLout1[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_OFF; + } + } + break; + case MC1N2_LOUT1L_DAC_SW: + case MC1N2_LOUT1L_DAC_SRC: + if (REG_CACHE_READ(MC1N2_LOUT1L_DAC_SRC) == 0) { + if (REG_CACHE_READ(MC1N2_LOUT1L_DAC_SW)) { + update.asLout1[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_L_ON; + } + else { + update.asLout1[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_L_OFF; + } + } + else { + if (value) { + update.asLout1[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_M_ON; + } + else { + update.asLout1[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_M_OFF; + } + } + break; + case MC1N2_LOUT1R_MIC1_SW: + if (value) { + update.asLout1[1].abSrcOnOff[0] = MCDRV_SRC0_MIC1_ON; + } + else { + update.asLout1[1].abSrcOnOff[0] = MCDRV_SRC0_MIC1_OFF; + } + break; + case MC1N2_LOUT1R_MIC2_SW: + if (value) { + update.asLout1[1].abSrcOnOff[0] = MCDRV_SRC0_MIC2_ON; + } + else { + update.asLout1[1].abSrcOnOff[0] = MCDRV_SRC0_MIC2_OFF; + } + break; + case MC1N2_LOUT1R_MIC3_SW: + if (value) { + update.asLout1[1].abSrcOnOff[0] = MCDRV_SRC0_MIC3_ON; + } + else { + update.asLout1[1].abSrcOnOff[0] = MCDRV_SRC0_MIC3_OFF; + } + break; + case MC1N2_LOUT1R_LINER_SW: + if (value) { + update.asLout1[1].abSrcOnOff[1] = MCDRV_SRC1_LINE1_R_ON; + } + else { + update.asLout1[1].abSrcOnOff[1] = MCDRV_SRC1_LINE1_R_OFF; + } + break; + case MC1N2_LOUT1R_DACR_SW: + if (value) { + update.asLout1[1].abSrcOnOff[5] = MCDRV_SRC5_DAC_R_ON; + } + else { + update.asLout1[1].abSrcOnOff[5] = MCDRV_SRC5_DAC_R_OFF; + } + break; + case MC1N2_LOUT2L_MIC1_SW: + if (value) { + update.asLout2[0].abSrcOnOff[0] = MCDRV_SRC0_MIC1_ON; + } + else { + update.asLout2[0].abSrcOnOff[0] = MCDRV_SRC0_MIC1_OFF; + } + break; + case MC1N2_LOUT2L_MIC2_SW: + if (value) { + update.asLout2[0].abSrcOnOff[0] = MCDRV_SRC0_MIC2_ON; + } + else { + update.asLout2[0].abSrcOnOff[0] = MCDRV_SRC0_MIC2_OFF; + } + break; + case MC1N2_LOUT2L_MIC3_SW: + if (value) { + update.asLout2[0].abSrcOnOff[0] = MCDRV_SRC0_MIC3_ON; + } + else { + update.asLout2[0].abSrcOnOff[0] = MCDRV_SRC0_MIC3_OFF; + } + break; + case MC1N2_LOUT2L_LINE_SW: + case MC1N2_LOUT2L_LINE_SRC: + if (REG_CACHE_READ(MC1N2_LOUT2L_LINE_SRC) == 0) { + if (REG_CACHE_READ(MC1N2_LOUT2L_LINE_SW)) { + update.asLout2[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_L_ON; + } + else { + update.asLout2[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_L_OFF; + } + } + else { + if (REG_CACHE_READ(MC1N2_LOUT2L_LINE_SW)) { + update.asLout2[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_ON; + } + else { + update.asLout2[0].abSrcOnOff[1] = MCDRV_SRC1_LINE1_M_OFF; + } + } + break; + case MC1N2_LOUT2L_DAC_SW: + case MC1N2_LOUT2L_DAC_SRC: + if (REG_CACHE_READ(MC1N2_LOUT2L_DAC_SRC) == 0) { + if (REG_CACHE_READ(MC1N2_LOUT2L_DAC_SW)) { + update.asLout2[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_L_ON; + } + else { + update.asLout2[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_L_OFF; + } + } + else { + if (REG_CACHE_READ(MC1N2_LOUT2L_DAC_SW)) { + update.asLout2[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_M_ON; + } + else { + update.asLout2[0].abSrcOnOff[5] = MCDRV_SRC5_DAC_M_OFF; + } + } + break; + case MC1N2_LOUT2R_MIC1_SW: + if (value) { + update.asLout2[1].abSrcOnOff[0] = MCDRV_SRC0_MIC1_ON; + } + else { + update.asLout2[1].abSrcOnOff[0] = MCDRV_SRC0_MIC1_OFF; + } + break; + case MC1N2_LOUT2R_MIC2_SW: + if (value) { + update.asLout2[1].abSrcOnOff[0] = MCDRV_SRC0_MIC2_ON; + } + else { + update.asLout2[1].abSrcOnOff[0] = MCDRV_SRC0_MIC2_OFF; + } + break; + case MC1N2_LOUT2R_MIC3_SW: + if (value) { + update.asLout2[1].abSrcOnOff[0] = MCDRV_SRC0_MIC3_ON; + } + else { + update.asLout2[1].abSrcOnOff[0] = MCDRV_SRC0_MIC3_OFF; + } + break; + case MC1N2_LOUT2R_LINER_SW: + if (value) { + update.asLout2[1].abSrcOnOff[1] = MCDRV_SRC1_LINE1_R_ON; + } + else { + update.asLout2[1].abSrcOnOff[1] = MCDRV_SRC1_LINE1_R_OFF; + } + break; + case MC1N2_LOUT2R_DACR_SW: + if (value) { + update.asLout2[1].abSrcOnOff[5] = MCDRV_SRC5_DAC_R_ON; + } + else { + update.asLout2[1].abSrcOnOff[5] = MCDRV_SRC5_DAC_R_OFF; + } + break; + case MC1N2_DACMAIN_SRC: + case MC1N2_DACVOICE_SRC: + case MC1N2_DIT0_SRC: + case MC1N2_DIT1_SRC: + case MC1N2_DIT2_SRC: + if (reg == MC1N2_DACMAIN_SRC) { + pch = &update.asDac[0]; + } + else if (reg == MC1N2_DACVOICE_SRC) { + pch = &update.asDac[1]; + } + else if (reg == MC1N2_DIT0_SRC) { + pch = &update.asDit0[0]; + } + else if (reg == MC1N2_DIT1_SRC) { + pch = &update.asDit1[0]; + } + else if (reg == MC1N2_DIT2_SRC) { + pch = &update.asDit2[0]; + } + + switch (value) { + case MC1N2_DSOURCE_OFF: + pch->abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + pch->abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + pch->abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + break; + case MC1N2_DSOURCE_ADC: /* ADC */ + pch->abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_ADC) { + pch->abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + pch->abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + if (REG_CACHE_READ(MC1N2_ADC_PDM_SEL)) { + pch->abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_ON; + } + else { + pch->abSrcOnOff[4] = MCDRV_SRC4_ADC0_ON | MCDRV_SRC4_PDM_OFF; + } + pch->abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR0: /* DIR0 */ + pch->abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR0) { + pch->abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + pch->abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + pch->abSrcOnOff[3] = + MCDRV_SRC3_DIR0_ON | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + pch->abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR1: /* DIR1 */ + pch->abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR1) { + pch->abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + pch->abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + pch->abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_ON | MCDRV_SRC3_DIR2_OFF; + pch->abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR2: /* DIR2 */ + pch->abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR2) { + pch->abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + pch->abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + pch->abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_ON; + pch->abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_MIX: /* MIX */ + pch->abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + pch->abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_MIX) { + pch->abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + pch->abSrcOnOff[6] = MCDRV_SRC6_MIX_ON | MCDRV_SRC6_AE_OFF; + } + break; + } + break; + case MC1N2_AE_SRC: + case MC1N2_ADC_PDM_SEL: + switch (REG_CACHE_READ(MC1N2_AE_SRC)) { + case MC1N2_DSOURCE_OFF: + update.asAe[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + update.asAe[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asAe[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF; + break; + case MC1N2_DSOURCE_ADC: /* ADC */ + if (REG_CACHE_READ(MC1N2_ADC_PDM_SEL)) { + update.asAe[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_ON; + } + else { + update.asAe[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_ON | MCDRV_SRC4_PDM_OFF; + } + update.asAe[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asAe[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF; + break; + case MC1N2_DSOURCE_DIR0: /* DIR0 */ + update.asAe[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + update.asAe[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_ON | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asAe[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF; + break; + case MC1N2_DSOURCE_DIR1:/* DIR1 */ + update.asAe[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + update.asAe[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_ON | MCDRV_SRC3_DIR2_OFF; + update.asAe[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF; + break; + case MC1N2_DSOURCE_DIR2:/* DIR2 */ + update.asAe[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + update.asAe[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_ON; + update.asAe[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF; + break; + case MC1N2_DSOURCE_MIX: /* MIX */ + update.asAe[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + update.asAe[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asAe[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_ON; + break; + } + + switch (REG_CACHE_READ(MC1N2_DACMAIN_SRC)) { + case MC1N2_DSOURCE_ADC: /* ADC */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_ADC) { + update.asDac[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + update.asDac[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + if (REG_CACHE_READ(MC1N2_ADC_PDM_SEL)) { + update.asDac[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_ON; + } + else { + update.asDac[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_ON | MCDRV_SRC4_PDM_OFF; + } + update.asDac[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR0: /* DIR0 */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR0) { + update.asDac[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDac[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDac[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_ON | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDac[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR1: /* DIR1 */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR1) { + update.asDac[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDac[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDac[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_ON | MCDRV_SRC3_DIR2_OFF; + update.asDac[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR2: /* DIR2 */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR2) { + update.asDac[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDac[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDac[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_ON; + update.asDac[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_MIX: /* MIX */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_MIX) { + update.asDac[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDac[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_ON | MCDRV_SRC6_AE_OFF; + } + break; + } + + switch (REG_CACHE_READ(MC1N2_DACVOICE_SRC)) { + case MC1N2_DSOURCE_ADC: /* ADC */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_ADC) { + update.asDac[1].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + update.asDac[1].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + if (REG_CACHE_READ(MC1N2_ADC_PDM_SEL)) { + update.asDac[1].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_ON; + } + else { + update.asDac[1].abSrcOnOff[4] = MCDRV_SRC4_ADC0_ON | MCDRV_SRC4_PDM_OFF; + } + update.asDac[1].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR0: /* DIR0 */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR0) { + update.asDac[1].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDac[1].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDac[1].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_ON | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDac[1].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR1: /* DIR1 */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR1) { + update.asDac[1].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDac[1].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDac[1].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_ON | MCDRV_SRC3_DIR2_OFF; + update.asDac[1].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR2: /* DIR2 */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR2) { + update.asDac[1].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDac[1].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDac[1].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_ON; + update.asDac[1].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_MIX: /* MIX */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_MIX) { + update.asDac[1].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDac[1].abSrcOnOff[6] = MCDRV_SRC6_MIX_ON | MCDRV_SRC6_AE_OFF; + } + break; + } + + switch (REG_CACHE_READ(MC1N2_DIT0_SRC)) { + case MC1N2_DSOURCE_ADC: /* ADC */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_ADC) { + update.asDit0[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + update.asDit0[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + if (REG_CACHE_READ(MC1N2_ADC_PDM_SEL)) { + update.asDit0[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_ON; + } + else { + update.asDit0[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_ON | MCDRV_SRC4_PDM_OFF; + } + update.asDit0[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR0: /* DIR0 */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR0) { + update.asDit0[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDit0[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDit0[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_ON | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDit0[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR1: /* DIR1 */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR1) { + update.asDit0[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDit0[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDit0[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_ON | MCDRV_SRC3_DIR2_OFF; + update.asDit0[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR2: /* DIR2 */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR2) { + update.asDit0[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDit0[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDit0[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_ON; + update.asDit0[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_MIX: /* MIX */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_MIX) { + update.asDit0[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDit0[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_ON | MCDRV_SRC6_AE_OFF; + } + break; + } + + switch (REG_CACHE_READ(MC1N2_DIT1_SRC)) { + case MC1N2_DSOURCE_ADC: /* ADC */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_ADC) { + update.asDit1[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + update.asDit1[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + if (REG_CACHE_READ(MC1N2_ADC_PDM_SEL)) { + update.asDit1[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_ON; + } + else { + update.asDit1[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_ON | MCDRV_SRC4_PDM_OFF; + } + update.asDit1[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR0: /* DIR0 */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR0) { + update.asDit1[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDit1[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDit1[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_ON | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDit1[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR1: /* DIR1 */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR1) { + update.asDit1[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDit1[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDit1[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_ON | MCDRV_SRC3_DIR2_OFF; + update.asDit1[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR2: /* DIR2 */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR2) { + update.asDit1[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDit1[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDit1[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_ON; + update.asDit1[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_MIX: /* MIX */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_MIX) { + update.asDit1[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDit1[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_ON | MCDRV_SRC6_AE_OFF; + } + break; + } + + switch (REG_CACHE_READ(MC1N2_DIT2_SRC)) { + case MC1N2_DSOURCE_ADC: /* ADC */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_ADC) { + update.asDit2[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + update.asDit2[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + if (REG_CACHE_READ(MC1N2_ADC_PDM_SEL)) { + update.asDit2[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_ON; + } + else { + update.asDit2[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_ON | MCDRV_SRC4_PDM_OFF; + } + update.asDit2[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR0: /* DIR0 */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR0) { + update.asDit2[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDit2[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDit2[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_ON | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDit2[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR1: /* DIR1 */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR1) { + update.asDit2[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDit2[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDit2[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_ON | MCDRV_SRC3_DIR2_OFF; + update.asDit2[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_DIR2: /* DIR2 */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_DIR2) { + update.asDit2[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_OFF; + update.asDit2[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDit2[0].abSrcOnOff[3] = + MCDRV_SRC3_DIR0_OFF | MCDRV_SRC3_DIR1_OFF | MCDRV_SRC3_DIR2_ON; + update.asDit2[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_OFF; + } + break; + case MC1N2_DSOURCE_MIX: /* MIX */ + if (REG_CACHE_READ(MC1N2_AE_SRC) == MC1N2_DSOURCE_MIX) { + update.asDit2[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_OFF | MCDRV_SRC6_AE_ON; + } + else { + update.asDit2[0].abSrcOnOff[6] = MCDRV_SRC6_MIX_ON | MCDRV_SRC6_AE_OFF; + } + break; + } + + break; + case MC1N2_DMIX_ADC_SW: + if (value) { + if (REG_CACHE_READ(MC1N2_ADC_PDM_SEL)) { + update.asMix[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_ON; + } + else { + update.asMix[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_ON | MCDRV_SRC4_PDM_OFF; + } + } + else { + update.asMix[0].abSrcOnOff[4] = MCDRV_SRC4_ADC0_OFF | MCDRV_SRC4_PDM_OFF; + } + break; + case MC1N2_DMIX_DIR0_SW: + if (value) { + update.asMix[0].abSrcOnOff[3] = MCDRV_SRC3_DIR0_ON; + } + else { + update.asMix[0].abSrcOnOff[3] = MCDRV_SRC3_DIR0_OFF; + } + break; + case MC1N2_DMIX_DIR1_SW: + if (value) { + update.asMix[0].abSrcOnOff[3] = MCDRV_SRC3_DIR1_ON; + } + else { + update.asMix[0].abSrcOnOff[3] = MCDRV_SRC3_DIR1_OFF; + } + break; + case MC1N2_DMIX_DIR2_SW: + if (value) { + update.asMix[0].abSrcOnOff[3] = MCDRV_SRC3_DIR2_ON; + } + else { + update.asMix[0].abSrcOnOff[3] = MCDRV_SRC3_DIR2_OFF; + } + break; + case MC1N2_AE_PARAM_SEL: + switch (value) { + case MC1N2_AE_PARAM_1: + pae = &sAeInfo_1; + break; + case MC1N2_AE_PARAM_2: + pae = &sAeInfo_2; + break; + case MC1N2_AE_PARAM_3: + pae = &sAeInfo_3; + break; + case MC1N2_AE_PARAM_4: + pae = &sAeInfo_4; + break; + case MC1N2_AE_PARAM_5: + pae = &sAeInfo_5; + break; + default: + pae = NULL; + break; + } + err = _McDrv_Ctrl(MCDRV_SET_AUDIOENGINE, pae, 0x1FF); + return err; + break; + case MC1N2_MICBIAS1: + if (value) { + update.asBias[0].abSrcOnOff[0] = MCDRV_SRC0_MIC1_ON; + } + else { + update.asBias[0].abSrcOnOff[0] = MCDRV_SRC0_MIC1_OFF; + } + break; + case MC1N2_MICBIAS2: + if (value) { + update.asBias[0].abSrcOnOff[0] = MCDRV_SRC0_MIC2_ON; + } + else { + update.asBias[0].abSrcOnOff[0] = MCDRV_SRC0_MIC2_OFF; + } + break; + case MC1N2_MICBIAS3: + if (value) { + update.asBias[0].abSrcOnOff[0] = MCDRV_SRC0_MIC3_ON; + } + else { + update.asBias[0].abSrcOnOff[0] = MCDRV_SRC0_MIC3_OFF; + } + break; + } + + mc1n2_hwdep_ioctl_set_path(codec, &update, 0); + err = _McDrv_Ctrl(MCDRV_SET_PATH, &update, 0); + + return err; +} + +static int mc1n2_write_reg(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + int err; + + if (reg < MC1N2_N_VOL_REG) { + err = write_reg_vol(codec, reg, value); + } + else { + err = write_reg_path(codec, reg, value); + } + + return err; +} + +static int mc1n2_get_codec_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + return 0; +} + +static int mc1n2_set_codec_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); + SINT16 *vol = (SINT16 *)&mc1n2->vol_store; + + int control_data = ucontrol->value.integer.value[0]; + int err, i; + + dev_info(codec->dev, "%s: Recovery [%d]\n", __func__, control_data); + + switch(control_data) + { + case CMD_CODEC_EMERGENCY_RECOVERY: + mutex_lock(&mc1n2->mutex); + + mc1n2_set_mclk_source(1); + + /* store parameters */ + for (i = 0; i < MC1N2_N_INFO_STORE; i++) { + struct mc1n2_info_store *store + = &mc1n2_info_store_tbl[i]; + if (store->get) { + err = _McDrv_Ctrl(store->get, + (void *)mc1n2 + store->offset, 0); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, + "%d: Error in MCDRV_GET\n", + err); + } + } + } + + err = _McDrv_Ctrl(MCDRV_TERM, NULL, 0); + if (err != MCDRV_SUCCESS) + dev_err(codec->dev, "%d: Error in MCDRV_TERM\n", err); + + err = _McDrv_Ctrl(MCDRV_INIT, &mc1n2->setup.init, 0); + if (err != MCDRV_SUCCESS) + dev_err(codec->dev, "%d: Error in MCDRV_INIT\n", err); + + /* restore parameters */ + for (i = 0; i < sizeof(MCDRV_VOL_INFO)/sizeof(SINT16); + i++, vol++) { + *vol |= 0x0001; + } + + for (i = 0; i < MC1N2_N_INFO_STORE; i++) { + struct mc1n2_info_store *store = + &mc1n2_info_store_tbl[i]; + if (store->set) { + err = _McDrv_Ctrl(store->set, + (void *) mc1n2 + store->offset, + store->flags); + + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, + "%d: Error in MCDRV_Set\n", + err); + } + } + } + + err = mc1n2_update_clock(mc1n2); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, + "%d: Error in mc1n2_update_clock\n", err); + } + + mutex_unlock(&mc1n2->mutex); + + dev_info(codec->dev, "%s: Recovery Done\n", __func__); + break; + + default: + break; + } + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(mc1n2_tlv_digital, -7500, 100, 1); +static const DECLARE_TLV_DB_SCALE(mc1n2_tlv_adc, -2850, 150, 1); +static const DECLARE_TLV_DB_SCALE(mc1n2_tlv_ain, -3150, 150, 1); +static const DECLARE_TLV_DB_SCALE(mc1n2_tlv_aout, -3100, 100, 1); +static const DECLARE_TLV_DB_SCALE(mc1n2_tlv_micgain, 1500, 500, 0); + +static unsigned int mc1n2_tlv_hpsp[] = { + TLV_DB_RANGE_HEAD(5), + 0, 2, TLV_DB_SCALE_ITEM(-4400, 800, 1), + 2, 3, TLV_DB_SCALE_ITEM(-2800, 400, 0), + 3, 7, TLV_DB_SCALE_ITEM(-2400, 200, 0), + 7, 15, TLV_DB_SCALE_ITEM(-1600, 100, 0), + 15, 31, TLV_DB_SCALE_ITEM(-800, 50, 0), +}; + +static unsigned int mc1n2_tlv_hpgain[] = { + TLV_DB_RANGE_HEAD(2), + 0, 2, TLV_DB_SCALE_ITEM(0, 150, 0), + 2, 3, TLV_DB_SCALE_ITEM(300, 300, 0), +}; + +static const char *codec_status_control[] = { + "REC_OFF", "REC_ON", "RESET_ON", "RESET_OFF" +}; + +static const struct soc_enum path_control_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(codec_status_control), codec_status_control), +}; + +static const struct snd_kcontrol_new mc1n2_snd_controls[] = { + /* + * digital volumes and mute switches + */ + SOC_DOUBLE_TLV("AD Digital Volume", + MC1N2_DVOL_AD0, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("AD Digital Switch", + MC1N2_DVOL_AD0, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("AENG6 Volume", + MC1N2_DVOL_AENG6, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("AENG6 Switch", + MC1N2_DVOL_AENG6, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("PDM Volume", + MC1N2_DVOL_PDM, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("PDM Switch", + MC1N2_DVOL_PDM, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("DIR#0 Volume", + MC1N2_DVOL_DIR0, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("DIR#0 Switch", + MC1N2_DVOL_DIR0, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("DIR#1 Volume", + MC1N2_DVOL_DIR1, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("DIR#1 Switch", + MC1N2_DVOL_DIR1, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("DIR#2 Volume", + MC1N2_DVOL_DIR2, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("DIR#2 Switch", + MC1N2_DVOL_DIR2, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("AD ATT Volume", + MC1N2_DVOL_AD0_ATT, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("AD ATT Switch", + MC1N2_DVOL_AD0_ATT, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("DIR#0 ATT Volume", + MC1N2_DVOL_DIR0_ATT, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("DIR#0 ATT Switch", + MC1N2_DVOL_DIR0_ATT, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("DIR#1 ATT Volume", + MC1N2_DVOL_DIR1_ATT, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("DIR#1 ATT Switch", + MC1N2_DVOL_DIR1_ATT, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("DIR#2 ATT Volume", + MC1N2_DVOL_DIR2_ATT, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("DIR#2 ATT Switch", + MC1N2_DVOL_DIR2_ATT, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("Side Tone Playback Volume", + MC1N2_DVOL_SIDETONE, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("Side Tone Playback Switch", + MC1N2_DVOL_SIDETONE, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("Master Playback Volume", + MC1N2_DVOL_DAC_MASTER, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("Master Playback Switch", + MC1N2_DVOL_DAC_MASTER, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("Voice Playback Volume", + MC1N2_DVOL_DAC_VOICE, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("Voice Playback Switch", + MC1N2_DVOL_DAC_VOICE, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("DAC Playback Volume", + MC1N2_DVOL_DAC_ATT, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("DAC Playback Switch", + MC1N2_DVOL_DAC_ATT, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("DIT#0 Capture Volume", + MC1N2_DVOL_DIT0, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("DIT#0 Capture Switch", + MC1N2_DVOL_DIT0, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("DIT#1 Capture Volume", + MC1N2_DVOL_DIT1, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("DIT#1 Capture Switch", + MC1N2_DVOL_DIT1, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("DIT#2 Capture Volume", + MC1N2_DVOL_DIT2, 0, 8, 93, 0, mc1n2_tlv_digital), + SOC_DOUBLE("DIT#2 Capture Switch", + MC1N2_DVOL_DIT2, 7, 15, 1, 0), + + /* + * analog volumes and mute switches + */ + SOC_DOUBLE_TLV("AD Analog Volume", + MC1N2_AVOL_AD0, 0, 8, 31, 0, mc1n2_tlv_adc), + SOC_DOUBLE("AD Analog Switch", + MC1N2_AVOL_AD0, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("Line Bypass Playback Volume", + MC1N2_AVOL_LIN1, 0, 8, 31, 0, mc1n2_tlv_ain), + SOC_DOUBLE("Line Bypass Playback Switch", + MC1N2_AVOL_LIN1, 7, 15, 1, 0), + + SOC_SINGLE_TLV("Mic 1 Bypass Playback Volume", + MC1N2_AVOL_MIC1, 0, 31, 0, mc1n2_tlv_ain), + SOC_SINGLE("Mic 1 Bypass Playback Switch", + MC1N2_AVOL_MIC1, 7, 1, 0), + + SOC_SINGLE_TLV("Mic 2 Bypass Playback Volume", + MC1N2_AVOL_MIC2, 0, 31, 0, mc1n2_tlv_ain), + SOC_SINGLE("Mic 2 Bypass Playback Switch", + MC1N2_AVOL_MIC2, 7, 1, 0), + + SOC_SINGLE_TLV("Mic 3 Bypass Playback Volume", + MC1N2_AVOL_MIC3, 0, 31, 0, mc1n2_tlv_ain), + SOC_SINGLE("Mic 3 Bypass Playback Switch", + MC1N2_AVOL_MIC3, 7, 1, 0), + + SOC_DOUBLE_TLV("Headphone Playback Volume", + MC1N2_AVOL_HP, 0, 8, 31, 0, mc1n2_tlv_hpsp), + SOC_DOUBLE("Headphone Playback Switch", + MC1N2_AVOL_HP, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("Speaker Playback Volume", + MC1N2_AVOL_SP, 0, 8, 31, 0, mc1n2_tlv_hpsp), + SOC_DOUBLE("Speaker Playback Switch", + MC1N2_AVOL_SP, 7, 15, 1, 0), + + SOC_SINGLE_TLV("Receiver Playback Volume", + MC1N2_AVOL_RC, 0, 31, 0, mc1n2_tlv_hpsp), + SOC_SINGLE("Receiver Playback Switch", + MC1N2_AVOL_RC, 7, 1, 0), + + SOC_DOUBLE_TLV("Line 1 Playback Volume", + MC1N2_AVOL_LOUT1, 0, 8, 31, 0, mc1n2_tlv_aout), + SOC_DOUBLE("Line 1 Playback Switch", + MC1N2_AVOL_LOUT1, 7, 15, 1, 0), + + SOC_DOUBLE_TLV("Line 2 Playback Volume", + MC1N2_AVOL_LOUT2, 0, 8, 31, 0, mc1n2_tlv_aout), + SOC_DOUBLE("Line 2 Playback Switch", + MC1N2_AVOL_LOUT2, 7, 15, 1, 0), + + SOC_SINGLE_TLV("Mic 1 Gain Volume", + MC1N2_AVOL_MIC1_GAIN, 0, 3, 0, mc1n2_tlv_micgain), + + SOC_SINGLE_TLV("Mic 2 Gain Volume", + MC1N2_AVOL_MIC2_GAIN, 0, 3, 0, mc1n2_tlv_micgain), + + SOC_SINGLE_TLV("Mic 3 Gain Volume", + MC1N2_AVOL_MIC3_GAIN, 0, 3, 0, mc1n2_tlv_micgain), + + SOC_SINGLE_TLV("HP Gain Playback Volume", + MC1N2_AVOL_HP_GAIN, 0, 3, 0, mc1n2_tlv_hpgain), + + SOC_ENUM_EXT("Codec Status", path_control_enum[0], + mc1n2_get_codec_status, mc1n2_set_codec_status), +}; + +/* + * Same as snd_soc_add_controls supported in alsa-driver 1.0.19 or later. + * This function is implimented for compatibility with linux 2.6.29. + */ +static int mc1n2_add_controls(struct snd_soc_codec *codec, + const struct snd_kcontrol_new *controls, int n) +{ + int err, i; + + for (i = 0; i < n; i++, controls++) { +#ifdef ALSA_VER_ANDROID_3_0 + if ((err = snd_ctl_add((struct snd_card *)codec->card->snd_card, + snd_soc_cnew(controls, codec, NULL, NULL))) < 0) { + return err; + } +#else + if ((err = snd_ctl_add(codec->card, + snd_soc_cnew(controls, codec, NULL))) < 0) { + return err; + } +#endif + } + + return 0; +} + +static const struct snd_kcontrol_new adcl_mix[] = { + SOC_DAPM_SINGLE("Mic1 Switch", MC1N2_ADCL_MIC1_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic2 Switch", MC1N2_ADCL_MIC2_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic3 Switch", MC1N2_ADCL_MIC3_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Line Switch", MC1N2_ADCL_LINE_SW, 0, 1, 0), +}; + +static const struct snd_kcontrol_new adcr_mix[] = { + SOC_DAPM_SINGLE("Mic1 Switch", MC1N2_ADCR_MIC1_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic2 Switch", MC1N2_ADCR_MIC2_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic3 Switch", MC1N2_ADCR_MIC3_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Line Switch", MC1N2_ADCR_LINE_SW, 0, 1, 0), +}; + +static const struct snd_kcontrol_new hpl_mix[] = { + SOC_DAPM_SINGLE("Mic1 Switch", MC1N2_HPL_MIC1_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic2 Switch", MC1N2_HPL_MIC2_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic3 Switch", MC1N2_HPL_MIC3_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Line Switch", MC1N2_HPL_LINE_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Dac Switch", MC1N2_HPL_DAC_SW, 0, 1, 0), +}; + +static const struct snd_kcontrol_new hpr_mix[] = { + SOC_DAPM_SINGLE("Mic1 Switch", MC1N2_HPR_MIC1_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic2 Switch", MC1N2_HPR_MIC2_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic3 Switch", MC1N2_HPR_MIC3_SW, 0, 1, 0), + SOC_DAPM_SINGLE("LineR Switch", MC1N2_HPR_LINER_SW, 0, 1, 0), + SOC_DAPM_SINGLE("DacR Switch", MC1N2_HPR_DACR_SW, 0, 1, 0), +}; + +static const struct snd_kcontrol_new spl_mix[] = { + SOC_DAPM_SINGLE("Line Switch", MC1N2_SPL_LINE_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Dac Switch", MC1N2_SPL_DAC_SW, 0, 1, 0), +}; + +static const struct snd_kcontrol_new spr_mix[] = { + SOC_DAPM_SINGLE("Line Switch", MC1N2_SPR_LINE_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Dac Switch", MC1N2_SPR_DAC_SW, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rc_mix[] = { + SOC_DAPM_SINGLE("Mic1 Switch", MC1N2_RC_MIC1_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic2 Switch", MC1N2_RC_MIC2_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic3 Switch", MC1N2_RC_MIC3_SW, 0, 1, 0), + SOC_DAPM_SINGLE("LineMono Switch", MC1N2_RC_LINEMONO_SW, 0, 1, 0), + SOC_DAPM_SINGLE("DacL Switch", MC1N2_RC_DACL_SW, 0, 1, 0), + SOC_DAPM_SINGLE("DacR Switch", MC1N2_RC_DACR_SW, 0, 1, 0), +}; + +static const struct snd_kcontrol_new lout1l_mix[] = { + SOC_DAPM_SINGLE("Mic1 Switch", MC1N2_LOUT1L_MIC1_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic2 Switch", MC1N2_LOUT1L_MIC2_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic3 Switch", MC1N2_LOUT1L_MIC3_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Line Switch", MC1N2_LOUT1L_LINE_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Dac Switch", MC1N2_LOUT1L_DAC_SW, 0, 1, 0), +}; + +static const struct snd_kcontrol_new lout1r_mix[] = { + SOC_DAPM_SINGLE("Mic1 Switch", MC1N2_LOUT1R_MIC1_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic2 Switch", MC1N2_LOUT1R_MIC2_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic3 Switch", MC1N2_LOUT1R_MIC3_SW, 0, 1, 0), + SOC_DAPM_SINGLE("LineR Switch", MC1N2_LOUT1R_LINER_SW, 0, 1, 0), + SOC_DAPM_SINGLE("DacR Switch", MC1N2_LOUT1R_DACR_SW, 0, 1, 0), +}; + +static const struct snd_kcontrol_new lout2l_mix[] = { + SOC_DAPM_SINGLE("Mic1 Switch", MC1N2_LOUT2L_MIC1_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic2 Switch", MC1N2_LOUT2L_MIC2_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic3 Switch", MC1N2_LOUT2L_MIC3_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Line Switch", MC1N2_LOUT2L_LINE_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Dac Switch", MC1N2_LOUT2L_DAC_SW, 0, 1, 0), +}; + +static const struct snd_kcontrol_new lout2r_mix[] = { + SOC_DAPM_SINGLE("Mic1 Switch", MC1N2_LOUT2R_MIC1_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic2 Switch", MC1N2_LOUT2R_MIC2_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Mic3 Switch", MC1N2_LOUT2R_MIC3_SW, 0, 1, 0), + SOC_DAPM_SINGLE("LineR Switch", MC1N2_LOUT2R_LINER_SW, 0, 1, 0), + SOC_DAPM_SINGLE("DacR Switch", MC1N2_LOUT2R_DACR_SW, 0, 1, 0), +}; + +static const struct snd_kcontrol_new digital_mix[] = { + SOC_DAPM_SINGLE("Adc Switch", MC1N2_DMIX_ADC_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Dir0 Switch", MC1N2_DMIX_DIR0_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Dir1 Switch", MC1N2_DMIX_DIR1_SW, 0, 1, 0), + SOC_DAPM_SINGLE("Dir2 Switch", MC1N2_DMIX_DIR2_SW, 0, 1, 0), +}; + +static const char *dsource_text[] = { + "OFF", "ADC", "DIR0", "DIR1", "DIR2", "MIX", +}; + +static const char *linel_mode_text[] = { + "LINEL", "LINEMONO", +}; + +static const char *liner_mode_text[] = { + "LINER", "LINEMONO", +}; + +static const char *dacl_mode_text[] = { + "DACL", "DACMONO", +}; + +static const char *dacr_mode_text[] = { + "DACR", "DACMONO", +}; + +static const char *ae_param_text[] = { + "PARAM1", "PARAM2", "PARAM3", "PARAM4", "PARAM5", +}; + +static const char *adc_pdm_text[] = { + "ADC", "PDM", +}; + +static const struct soc_enum dacmain_source_enum = + SOC_ENUM_SINGLE(MC1N2_DACMAIN_SRC, 0, 6, dsource_text); + +static const struct soc_enum dacvoice_source_enum = + SOC_ENUM_SINGLE(MC1N2_DACVOICE_SRC, 0, 6, dsource_text); + +static const struct soc_enum dit0_source_enum = + SOC_ENUM_SINGLE(MC1N2_DIT0_SRC, 0, 6, dsource_text); + +static const struct soc_enum dit1_source_enum = + SOC_ENUM_SINGLE(MC1N2_DIT1_SRC, 0, 6, dsource_text); + +static const struct soc_enum dit2_source_enum = + SOC_ENUM_SINGLE(MC1N2_DIT2_SRC, 0, 6, dsource_text); + +static const struct soc_enum ae_source_enum = + SOC_ENUM_SINGLE(MC1N2_AE_SRC, 0, 6, dsource_text); + +static const struct soc_enum adcl_line_enum = + SOC_ENUM_SINGLE(MC1N2_ADCL_LINE_SRC, 0, 2, linel_mode_text); + +static const struct soc_enum adcr_line_enum = + SOC_ENUM_SINGLE(MC1N2_ADCR_LINE_SRC, 0, 2, liner_mode_text); + +static const struct soc_enum hpl_line_enum = + SOC_ENUM_SINGLE(MC1N2_HPL_LINE_SRC, 0, 2, linel_mode_text); + +static const struct soc_enum hpl_dac_enum = + SOC_ENUM_SINGLE(MC1N2_HPL_DAC_SRC, 0, 2, dacl_mode_text); + +static const struct soc_enum spl_line_enum = + SOC_ENUM_SINGLE(MC1N2_SPL_LINE_SRC, 0, 2, linel_mode_text); + +static const struct soc_enum spl_dac_enum = + SOC_ENUM_SINGLE(MC1N2_SPL_DAC_SRC, 0, 2, dacl_mode_text); + +static const struct soc_enum spr_line_enum = + SOC_ENUM_SINGLE(MC1N2_SPR_LINE_SRC, 0, 2, liner_mode_text); + +static const struct soc_enum spr_dac_enum = + SOC_ENUM_SINGLE(MC1N2_SPR_DAC_SRC, 0, 2, dacr_mode_text); + +static const struct soc_enum lout1l_line_enum = + SOC_ENUM_SINGLE(MC1N2_LOUT1L_LINE_SRC, 0, 2, linel_mode_text); + +static const struct soc_enum lout1l_dac_enum = + SOC_ENUM_SINGLE(MC1N2_LOUT1L_DAC_SRC, 0, 2, dacl_mode_text); + +static const struct soc_enum lout2l_line_enum = + SOC_ENUM_SINGLE(MC1N2_LOUT2L_LINE_SRC, 0, 2, linel_mode_text); + +static const struct soc_enum lout2l_dac_enum = + SOC_ENUM_SINGLE(MC1N2_LOUT2L_DAC_SRC, 0, 2, dacl_mode_text); + +static const struct soc_enum ae_param_enum = + SOC_ENUM_SINGLE(MC1N2_AE_PARAM_SEL, 0, 5, ae_param_text); + +static const struct soc_enum adc_pdm_enum = + SOC_ENUM_SINGLE(MC1N2_ADC_PDM_SEL, 0, 2, adc_pdm_text); + +static const struct snd_kcontrol_new dacmain_mux = + SOC_DAPM_ENUM("DACMAIN SRC MUX", dacmain_source_enum); + +static const struct snd_kcontrol_new dacvoice_mux = + SOC_DAPM_ENUM("DACVOICE SRC MUX", dacvoice_source_enum); + +static const struct snd_kcontrol_new dit0_mux = + SOC_DAPM_ENUM("DIT0 SRC MUX", dit0_source_enum); + +static const struct snd_kcontrol_new dit1_mux = + SOC_DAPM_ENUM("DIT1 SRC MUX", dit1_source_enum); + +static const struct snd_kcontrol_new dit2_mux = + SOC_DAPM_ENUM("DIT2 SRC MUX", dit2_source_enum); + +static const struct snd_kcontrol_new ae_mux = + SOC_DAPM_ENUM("AE SRC MUX", ae_source_enum); + +static const struct snd_kcontrol_new adcl_line_mux = + SOC_DAPM_ENUM("ADCL LINE MUX", adcl_line_enum); + +static const struct snd_kcontrol_new adcr_line_mux = + SOC_DAPM_ENUM("ADCR LINE MUX", adcr_line_enum); + +static const struct snd_kcontrol_new hpl_line_mux = + SOC_DAPM_ENUM("HPL LINE MUX", hpl_line_enum); + +static const struct snd_kcontrol_new hpl_dac_mux = + SOC_DAPM_ENUM("HPL DAC MUX", hpl_dac_enum); + +static const struct snd_kcontrol_new spl_line_mux = + SOC_DAPM_ENUM("SPL LINE MUX", spl_line_enum); + +static const struct snd_kcontrol_new spl_dac_mux = + SOC_DAPM_ENUM("SPL DAC MUX", spl_dac_enum); + +static const struct snd_kcontrol_new spr_line_mux = + SOC_DAPM_ENUM("SPR LINE MUX", spr_line_enum); + +static const struct snd_kcontrol_new spr_dac_mux = + SOC_DAPM_ENUM("SPR DAC MUX", spr_dac_enum); + +static const struct snd_kcontrol_new lout1l_line_mux = + SOC_DAPM_ENUM("LOUT1L LINE MUX", lout1l_line_enum); + +static const struct snd_kcontrol_new lout1l_dac_mux = + SOC_DAPM_ENUM("LOUT1L DAC MUX", lout1l_dac_enum); + +static const struct snd_kcontrol_new lout2l_line_mux = + SOC_DAPM_ENUM("LOUT2L LINE MUX", lout2l_line_enum); + +static const struct snd_kcontrol_new lout2l_dac_mux = + SOC_DAPM_ENUM("LOUT2L DAC MUX", lout2l_dac_enum); + +static const struct snd_kcontrol_new ae_param_mux = + SOC_DAPM_ENUM("AE PARAMETER", ae_param_enum); + +static const struct snd_kcontrol_new adc_pdm_mux = + SOC_DAPM_ENUM("ADC PDM MUX", adc_pdm_enum); + +static const struct snd_kcontrol_new bias1_sw = + SOC_DAPM_SINGLE("Switch", MC1N2_MICBIAS1, 0, 1, 0); + +static const struct snd_kcontrol_new bias2_sw = + SOC_DAPM_SINGLE("Switch", MC1N2_MICBIAS2, 0, 1, 0); + +static const struct snd_kcontrol_new bias3_sw = + SOC_DAPM_SINGLE("Switch", MC1N2_MICBIAS3, 0, 1, 0); + +static const struct snd_soc_dapm_widget mc1n2_widgets[] = { +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2"), +SND_SOC_DAPM_INPUT("MIC3"), +SND_SOC_DAPM_INPUT("LINEIN"), + +SND_SOC_DAPM_INPUT("DIR0"), +SND_SOC_DAPM_INPUT("DIR1"), +SND_SOC_DAPM_INPUT("DIR2"), + +SND_SOC_DAPM_INPUT("PDM"), + +SND_SOC_DAPM_INPUT("AE1"), +SND_SOC_DAPM_INPUT("AE2"), +SND_SOC_DAPM_INPUT("AE3"), +SND_SOC_DAPM_INPUT("AE4"), +SND_SOC_DAPM_INPUT("AE5"), + +SND_SOC_DAPM_INPUT("BIAS1"), +SND_SOC_DAPM_INPUT("BIAS2"), +SND_SOC_DAPM_INPUT("BIAS3"), + +SND_SOC_DAPM_OUTPUT("DIT0"), +SND_SOC_DAPM_OUTPUT("DIT1"), +SND_SOC_DAPM_OUTPUT("DIT2"), + +SND_SOC_DAPM_ADC("ADC", NULL, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_MIXER("ADCL MIXER", SND_SOC_NOPM, 0, 0, adcl_mix, ARRAY_SIZE(adcl_mix)), +SND_SOC_DAPM_MIXER("ADCR MIXER", SND_SOC_NOPM, 0, 0, adcr_mix, ARRAY_SIZE(adcr_mix)), +SND_SOC_DAPM_MIXER("HPL MIXER", SND_SOC_NOPM, 0, 0, hpl_mix, ARRAY_SIZE(hpl_mix)), +SND_SOC_DAPM_MIXER("HPR MIXER", SND_SOC_NOPM, 0, 0, hpr_mix, ARRAY_SIZE(hpr_mix)), +SND_SOC_DAPM_MIXER("SPL MIXER", SND_SOC_NOPM, 0, 0, spl_mix, ARRAY_SIZE(spl_mix)), +SND_SOC_DAPM_MIXER("SPR MIXER", SND_SOC_NOPM, 0, 0, spr_mix, ARRAY_SIZE(spr_mix)), +SND_SOC_DAPM_MIXER("RC MIXER", SND_SOC_NOPM, 0, 0, rc_mix, ARRAY_SIZE(rc_mix)), +SND_SOC_DAPM_MIXER("LINEOUT1L MIXER", SND_SOC_NOPM, 0, 0, lout1l_mix, ARRAY_SIZE(lout1l_mix)), +SND_SOC_DAPM_MIXER("LINEOUT1R MIXER", SND_SOC_NOPM, 0, 0, lout1r_mix, ARRAY_SIZE(lout1r_mix)), +SND_SOC_DAPM_MIXER("LINEOUT2L MIXER", SND_SOC_NOPM, 0, 0, lout2l_mix, ARRAY_SIZE(lout2l_mix)), +SND_SOC_DAPM_MIXER("LINEOUT2R MIXER", SND_SOC_NOPM, 0, 0, lout2r_mix, ARRAY_SIZE(lout2r_mix)), + +SND_SOC_DAPM_MIXER("DIGITAL MIXER", SND_SOC_NOPM, 0, 0, digital_mix, ARRAY_SIZE(digital_mix)), + +SND_SOC_DAPM_MUX("DACMAIN SRC", SND_SOC_NOPM, 0, 0, &dacmain_mux), +SND_SOC_DAPM_MUX("DACVOICE SRC", SND_SOC_NOPM, 0, 0, &dacvoice_mux), +SND_SOC_DAPM_MUX("DIT0 SRC", SND_SOC_NOPM, 0, 0, &dit0_mux), +SND_SOC_DAPM_MUX("DIT1 SRC", SND_SOC_NOPM, 0, 0, &dit1_mux), +SND_SOC_DAPM_MUX("DIT2 SRC", SND_SOC_NOPM, 0, 0, &dit2_mux), + +SND_SOC_DAPM_MUX("AE SRC", SND_SOC_NOPM, 0, 0, &ae_mux), + +SND_SOC_DAPM_MUX("ADCL LINE MIXMODE", SND_SOC_NOPM, 0, 0, &adcl_line_mux), +SND_SOC_DAPM_MUX("ADCR LINE MIXMODE", SND_SOC_NOPM, 0, 0, &adcr_line_mux), + +SND_SOC_DAPM_MUX("HPL LINE MIXMODE", SND_SOC_NOPM, 0, 0, &hpl_line_mux), +SND_SOC_DAPM_MUX("HPL DAC MIXMODE", SND_SOC_NOPM, 0, 0, &hpl_dac_mux), + +SND_SOC_DAPM_MUX("SPL LINE MIXMODE", SND_SOC_NOPM, 0, 0, &spl_line_mux), +SND_SOC_DAPM_MUX("SPL DAC MIXMODE", SND_SOC_NOPM, 0, 0, &spl_dac_mux), +SND_SOC_DAPM_MUX("SPR LINE MIXMODE", SND_SOC_NOPM, 0, 0, &spr_line_mux), +SND_SOC_DAPM_MUX("SPR DAC MIXMODE", SND_SOC_NOPM, 0, 0, &spr_dac_mux), + +SND_SOC_DAPM_MUX("LINEOUT1L LINE MIXMODE", SND_SOC_NOPM, 0, 0, &lout1l_line_mux), +SND_SOC_DAPM_MUX("LINEOUT1L DAC MIXMODE", SND_SOC_NOPM, 0, 0, &lout1l_dac_mux), + +SND_SOC_DAPM_MUX("LINEOUT2L LINE MIXMODE", SND_SOC_NOPM, 0, 0, &lout2l_line_mux), +SND_SOC_DAPM_MUX("LINEOUT2L DAC MIXMODE", SND_SOC_NOPM, 0, 0, &lout2l_dac_mux), + +SND_SOC_DAPM_MUX("AE PARAMETER SEL", SND_SOC_NOPM, 0, 0, &ae_param_mux), + +SND_SOC_DAPM_MUX("ADC PDM SEL", SND_SOC_NOPM, 0, 0, &adc_pdm_mux), + +SND_SOC_DAPM_SWITCH("MB1", SND_SOC_NOPM, 0, 0, &bias1_sw), +SND_SOC_DAPM_SWITCH("MB2", SND_SOC_NOPM, 0, 0, &bias2_sw), +SND_SOC_DAPM_SWITCH("MB3", SND_SOC_NOPM, 0, 0, &bias3_sw), +}; + +static const struct snd_soc_dapm_route intercon[] = { + {"ADCL LINE MIXMODE", "LINEL", "LINEIN"}, + {"ADCL LINE MIXMODE", "LINEMONO", "LINEIN"}, + {"ADCR LINE MIXMODE", "LINER", "LINEIN"}, + {"ADCR LINE MIXMODE", "LINEMONO", "LINEIN"}, + + {"HPL LINE MIXMODE", "LINEL", "LINEIN"}, + {"HPL LINE MIXMODE", "LINEMONO", "LINEIN"}, + {"HPL DAC MIXMODE", "DACL", "DAC"}, + {"HPL DAC MIXMODE", "DACMONO", "DAC"}, + + {"SPL LINE MIXMODE", "LINEL", "LINEIN"}, + {"SPL LINE MIXMODE", "LINEMONO", "LINEIN"}, + {"SPL DAC MIXMODE", "DACL", "DAC"}, + {"SPL DAC MIXMODE", "DACMONO", "DAC"}, + + {"SPR LINE MIXMODE", "LINER", "LINEIN"}, + {"SPR LINE MIXMODE", "LINEMONO", "LINEIN"}, + {"SPR DAC MIXMODE", "DACR", "DAC"}, + {"SPR DAC MIXMODE", "DACMONO", "DAC"}, + + {"LINEOUT1L LINE MIXMODE", "LINEL", "LINEIN"}, + {"LINEOUT1L LINE MIXMODE", "LINEMONO", "LINEIN"}, + {"LINEOUT1L DAC MIXMODE", "DACL", "DAC"}, + {"LINEOUT1L DAC MIXMODE", "DACMONO", "DAC"}, + + {"LINEOUT2L LINE MIXMODE", "LINEL", "LINEIN"}, + {"LINEOUT2L LINE MIXMODE", "LINEMONO", "LINEIN"}, + {"LINEOUT2L DAC MIXMODE", "DACL", "DAC"}, + {"LINEOUT2L DAC MIXMODE", "DACMONO", "DAC"}, + + {"ADCL MIXER", "Mic1 Switch", "MIC1"}, + {"ADCL MIXER", "Mic2 Switch", "MIC2"}, + {"ADCL MIXER", "Mic3 Switch", "MIC3"}, + {"ADCL MIXER", "Line Switch", "ADCL LINE MIXMODE"}, + + {"ADCR MIXER", "Mic1 Switch", "MIC1"}, + {"ADCR MIXER", "Mic2 Switch", "MIC2"}, + {"ADCR MIXER", "Mic3 Switch", "MIC3"}, + {"ADCR MIXER", "Line Switch", "ADCR LINE MIXMODE"}, + + {"HPL MIXER", "Mic1 Switch", "MIC1"}, + {"HPL MIXER", "Mic2 Switch", "MIC2"}, + {"HPL MIXER", "Mic3 Switch", "MIC3"}, + {"HPL MIXER", "Line Switch", "HPL LINE MIXMODE"}, + {"HPL MIXER", "Dac Switch", "HPL DAC MIXMODE"}, + + {"HPR MIXER", "Mic1 Switch", "MIC1"}, + {"HPR MIXER", "Mic2 Switch", "MIC2"}, + {"HPR MIXER", "Mic3 Switch", "MIC3"}, + {"HPR MIXER", "LineR Switch", "LINEIN"}, + {"HPR MIXER", "DacR Switch", "DAC"}, + + {"SPL MIXER", "Line Switch", "SPL LINE MIXMODE"}, + {"SPL MIXER", "Dac Switch", "SPL DAC MIXMODE"}, + + {"SPR MIXER", "Line Switch", "SPR LINE MIXMODE"}, + {"SPR MIXER", "Dac Switch", "SPR DAC MIXMODE"}, + + {"RC MIXER", "Mic1 Switch", "MIC1"}, + {"RC MIXER", "Mic2 Switch", "MIC2"}, + {"RC MIXER", "Mic3 Switch", "MIC3"}, + {"RC MIXER", "LineMono Switch", "LINEIN"}, + {"RC MIXER", "DacL Switch", "DAC"}, + {"RC MIXER", "DacR Switch", "DAC"}, + + {"LINEOUT1L MIXER", "Mic1 Switch", "MIC1"}, + {"LINEOUT1L MIXER", "Mic2 Switch", "MIC2"}, + {"LINEOUT1L MIXER", "Mic3 Switch", "MIC3"}, + {"LINEOUT1L MIXER", "Line Switch", "LINEOUT1L LINE MIXMODE"}, + {"LINEOUT1L MIXER", "Dac Switch", "LINEOUT1L DAC MIXMODE"}, + + {"LINEOUT1R MIXER", "Mic1 Switch", "MIC1"}, + {"LINEOUT1R MIXER", "Mic2 Switch", "MIC2"}, + {"LINEOUT1R MIXER", "Mic3 Switch", "MIC3"}, + {"LINEOUT1R MIXER", "LineR Switch", "LINEIN"}, + {"LINEOUT1R MIXER", "DacR Switch", "DAC"}, + + {"LINEOUT2L MIXER", "Mic1 Switch", "MIC1"}, + {"LINEOUT2L MIXER", "Mic2 Switch", "MIC2"}, + {"LINEOUT2L MIXER", "Mic3 Switch", "MIC3"}, + + {"LINEOUT2L MIXER", "Line Switch", "LINEOUT2L LINE MIXMODE"}, + {"LINEOUT2L MIXER", "Dac Switch", "LINEOUT2L DAC MIXMODE"}, + + {"LINEOUT2R MIXER", "Mic1 Switch", "MIC1"}, + {"LINEOUT2R MIXER", "Mic2 Switch", "MIC2"}, + {"LINEOUT2R MIXER", "Mic3 Switch", "MIC3"}, + {"LINEOUT2R MIXER", "LineR Switch", "LINEIN"}, + {"LINEOUT2R MIXER", "DacR Switch", "DAC"}, + + {"ADC", NULL, "ADCL MIXER"}, + {"ADC", NULL, "ADCR MIXER"}, + + {"DIGITAL MIXER", "Adc Switch", "ADC PDM SEL"}, + {"DIGITAL MIXER", "Dir0 Switch", "DIR0"}, + {"DIGITAL MIXER", "Dir1 Switch", "DIR1"}, + {"DIGITAL MIXER", "Dir2 Switch", "DIR2"}, + + {"AE SRC", "ADC", "ADCL MIXER"}, + {"AE SRC", "ADC", "ADCR MIXER"}, + {"AE SRC", "DIR0", "DIR0"}, + {"AE SRC", "DIR1", "DIR1"}, + {"AE SRC", "DIR2", "DIR2"}, + {"AE SRC", "MIX", "DIGITAL MIXER"}, + + {"DACMAIN SRC", "ADC", "ADC PDM SEL"}, + {"DACMAIN SRC", "DIR0", "DIR0"}, + {"DACMAIN SRC", "DIR1", "DIR1"}, + {"DACMAIN SRC", "DIR2", "DIR2"}, + {"DACMAIN SRC", "MIX", "DIGITAL MIXER"}, + + {"DACVOICE SRC", "ADC", "ADC PDM SEL"}, + {"DACVOICE SRC", "DIR0", "DIR0"}, + {"DACVOICE SRC", "DIR1", "DIR1"}, + {"DACVOICE SRC", "DIR2", "DIR2"}, + {"DACVOICE SRC", "MIX", "DIGITAL MIXER"}, + + {"DIT0 SRC", "ADC", "ADC PDM SEL"}, + {"DIT0 SRC", "DIR0", "DIR0"}, + {"DIT0 SRC", "DIR1", "DIR1"}, + {"DIT0 SRC", "DIR2", "DIR2"}, + {"DIT0 SRC", "MIX", "DIGITAL MIXER"}, + + {"DIT1 SRC", "ADC", "ADC PDM SEL"}, + {"DIT1 SRC", "DIR0", "DIR0"}, + {"DIT1 SRC", "DIR1", "DIR1"}, + {"DIT1 SRC", "DIR2", "DIR2"}, + {"DIT1 SRC", "MIX", "DIGITAL MIXER"}, + + {"DIT2 SRC", "ADC", "ADC PDM SEL"}, + {"DIT2 SRC", "DIR0", "DIR0"}, + {"DIT2 SRC", "DIR1", "DIR1"}, + {"DIT2 SRC", "DIR2", "DIR2"}, + {"DIT2 SRC", "MIX", "DIGITAL MIXER"}, + + {"AE PARAMETER SEL", "PARAM1", "AE1"}, + {"AE PARAMETER SEL", "PARAM2", "AE2"}, + {"AE PARAMETER SEL", "PARAM3", "AE3"}, + {"AE PARAMETER SEL", "PARAM4", "AE4"}, + {"AE PARAMETER SEL", "PARAM5", "AE5"}, + + {"ADC PDM SEL", "ADC", "ADC"}, + {"ADC PDM SEL", "PDM", "PDM"}, + + {"MB1", "Switch", "BIAS1"}, + {"MB2", "Switch", "BIAS2"}, + {"MB3", "Switch", "BIAS3"}, + + {"MIC1", NULL, "MB1"}, + {"MIC2", NULL, "MB2"}, + {"MIC3", NULL, "MB3"}, +}; + +#ifdef ALSA_VER_ANDROID_3_0 +static int mc1n2_add_widgets(struct snd_soc_codec *codec) +{ + int err; + + err = snd_soc_dapm_new_controls(&codec->dapm, mc1n2_widgets, + ARRAY_SIZE(mc1n2_widgets)); + if(err < 0) { + return err; + } + + err = snd_soc_dapm_add_routes(&codec->dapm, intercon, ARRAY_SIZE(intercon)); + if(err < 0) { + return err; + } + + err = snd_soc_dapm_new_widgets(&codec->dapm); + if(err < 0) { + return err; + } + + return 0; +} +#else +static int mc1n2_add_widgets(struct snd_soc_codec *codec) +{ + int err; + + err = snd_soc_dapm_new_controls(codec, mc1n2_widgets, + ARRAY_SIZE(mc1n2_widgets)); + if(err < 0) { + return err; + } + + err = snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + if(err < 0) { + return err; + } + + err = snd_soc_dapm_new_widgets(codec); + if(err < 0) { + return err; + } + + return 0; +} +#endif + +/* + * Hwdep interface + */ +static int mc1n2_hwdep_open(struct snd_hwdep * hw, struct file *file) +{ + /* Nothing to do */ + return 0; +} + +static int mc1n2_hwdep_release(struct snd_hwdep *hw, struct file *file) +{ + /* Nothing to do */ + return 0; +} + +static int mc1n2_hwdep_map_error(int err) +{ + switch (err) { + case MCDRV_SUCCESS: + return 0; + case MCDRV_ERROR_ARGUMENT: + return -EINVAL; + case MCDRV_ERROR_STATE: + return -EBUSY; + case MCDRV_ERROR_TIMEOUT: + return -EIO; + default: + /* internal error */ + return -EIO; + } +} + +static int mc1n2_hwdep_ioctl_set_path(struct snd_soc_codec *codec, + void *info, unsigned int update) +{ +#if (defined ALSA_VER_ANDROID_2_6_35) || (defined ALSA_VER_ANDROID_3_0) + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct mc1n2_data *mc1n2 = codec->private_data; +#endif + MCDRV_CHANNEL *ch; + int i, j; + MCDRV_PATH_INFO *path = (MCDRV_PATH_INFO *) info; + + mutex_lock(&mc1n2->mutex); + + /* preserve DIR settings */ + for (i = 0; i < MC1N2_N_PATH_CHANNELS; i++) { + ch = (MCDRV_CHANNEL *)(info + mc1n2_path_channel_tbl[i]); +#ifdef DIO0_DAI_ENABLE + switch ((ch->abSrcOnOff[3]) & 0x3) { + case 1: + mc1n2->port[0].dir[i] = 1; + break; + case 2: + mc1n2->port[0].dir[i] = 0; + break; + } +#endif +#ifdef DIO1_DAI_ENABLE + switch ((ch->abSrcOnOff[3] >> 2) & 0x3) { + case 1: + mc1n2->port[1].dir[i] = 1; + break; + case 2: + mc1n2->port[1].dir[i] = 0; + break; + } +#endif +#ifdef DIO2_DAI_ENABLE + switch ((ch->abSrcOnOff[3] >> 4) & 0x3) { + case 1: + mc1n2->port[2].dir[i] = 1; + break; + case 2: + mc1n2->port[2].dir[i] = 0; + break; + } +#endif + } + + /* preserve DIT settings */ +#ifdef DIO0_DAI_ENABLE + ch = (MCDRV_CHANNEL *)(info + mc1n2_path_channel_tbl[0]); + for (j = 0; j < SOURCE_BLOCK_NUM; j++) { + mc1n2->port[0].dit.abSrcOnOff[j] |= + ch->abSrcOnOff[j] & 0x55; + mc1n2->port[0].dit.abSrcOnOff[j] &= + ~((ch->abSrcOnOff[j] & 0xaa) >> 1); + } +#endif +#ifdef DIO1_DAI_ENABLE + ch = (MCDRV_CHANNEL *)(info + mc1n2_path_channel_tbl[1]); + for (j = 0; j < SOURCE_BLOCK_NUM; j++) { + mc1n2->port[1].dit.abSrcOnOff[j] |= + ch->abSrcOnOff[j] & 0x55; + mc1n2->port[1].dit.abSrcOnOff[j] &= + ~((ch->abSrcOnOff[j] & 0xaa) >> 1); + } +#endif +#ifdef DIO2_DAI_ENABLE + ch = (MCDRV_CHANNEL *)(info + mc1n2_path_channel_tbl[2]); + for (j = 0; j < SOURCE_BLOCK_NUM; j++) { + mc1n2->port[2].dit.abSrcOnOff[j] |= + ch->abSrcOnOff[j] & 0x55; + mc1n2->port[2].dit.abSrcOnOff[j] &= + ~((ch->abSrcOnOff[j] & 0xaa) >> 1); + } +#endif + + /* modify path */ + for (i = 0; i < MC1N2_N_PATH_CHANNELS; i++) { + ch = (MCDRV_CHANNEL *)(info + mc1n2_path_channel_tbl[i]); + +#ifdef DIO0_DAI_ENABLE + if (!mc1n2_is_in_playback(&mc1n2->port[0])) { + ch->abSrcOnOff[3] &= ~(0x3); + } +#endif +#ifdef DIO1_DAI_ENABLE + if (!mc1n2_is_in_playback(&mc1n2->port[1])) { + ch->abSrcOnOff[3] &= ~(0x3 << 2); + } +#endif +#ifdef DIO2_DAI_ENABLE + if (!mc1n2_is_in_playback(&mc1n2->port[2])) { + ch->abSrcOnOff[3] &= ~(0x3 << 4); + } +#endif + } + +#ifdef DIO0_DAI_ENABLE + ch = (MCDRV_CHANNEL *)(info + mc1n2_path_channel_tbl[0]); + for (j = 0; j < SOURCE_BLOCK_NUM; j++) { + if (!mc1n2_is_in_capture(&mc1n2->port[0])) { + ch->abSrcOnOff[j] = 0; + } + } +#endif +#ifdef DIO1_DAI_ENABLE + ch = (MCDRV_CHANNEL *)(info + mc1n2_path_channel_tbl[1]); + for (j = 0; j < SOURCE_BLOCK_NUM; j++) { + if (!mc1n2_is_in_capture(&mc1n2->port[1])) { + ch->abSrcOnOff[j] = 0; + } + } +#endif +#ifdef DIO2_DAI_ENABLE + ch = (MCDRV_CHANNEL *)(info + mc1n2_path_channel_tbl[2]); + for (j = 0; j < SOURCE_BLOCK_NUM; j++) { + if (!mc1n2_is_in_capture(&mc1n2->port[2])) { + ch->abSrcOnOff[j] = 0; + } + } +#endif + + /* select mic path */ + if ((path->asAdc0[0].abSrcOnOff[0] & MCDRV_SRC0_MIC1_OFF) && (path->asAdc0[1].abSrcOnOff[0] & MCDRV_SRC0_MIC1_OFF)) { + audio_ctrl_mic_bias_gpio(mc1n2->pdata, MAIN_MIC, 0); + } else { + audio_ctrl_mic_bias_gpio(mc1n2->pdata, MAIN_MIC, 1); + mdelay(mc1n2->delay_mic1in); + } + + if ((path->asAdc0[0].abSrcOnOff[0] & MCDRV_SRC0_MIC3_OFF) && (path->asAdc0[1].abSrcOnOff[0] & MCDRV_SRC0_MIC3_OFF)) { + audio_ctrl_mic_bias_gpio(mc1n2->pdata, SUB_MIC, 0); + } else + audio_ctrl_mic_bias_gpio(mc1n2->pdata, SUB_MIC, 1); + + mutex_unlock(&mc1n2->mutex); + + return 0; +} + +static int mc1n2_hwdep_ioctl_set_ae(struct snd_soc_codec *codec, + void *info, unsigned int update) +{ +#if (defined ALSA_VER_ANDROID_2_6_35) || (defined ALSA_VER_ANDROID_3_0) + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct mc1n2_data *mc1n2 = codec->private_data; +#endif + UINT8 onoff = ((MCDRV_AE_INFO *)info)->bOnOff; + unsigned int mask = update & 0x0f; /* bit mask for bOnOff */ + int i; + + struct mc1n2_ae_copy { + UINT32 flag; + size_t offset; + size_t size; + }; + + struct mc1n2_ae_copy tbl[] = { + {MCDRV_AEUPDATE_FLAG_BEX, + offsetof(MCDRV_AE_INFO, abBex), BEX_PARAM_SIZE}, + {MCDRV_AEUPDATE_FLAG_WIDE, + offsetof(MCDRV_AE_INFO, abWide), WIDE_PARAM_SIZE}, + {MCDRV_AEUPDATE_FLAG_DRC, + offsetof(MCDRV_AE_INFO, abDrc), DRC_PARAM_SIZE}, + {MCDRV_AEUPDATE_FLAG_EQ5, + offsetof(MCDRV_AE_INFO, abEq5), EQ5_PARAM_SIZE}, + {MCDRV_AEUPDATE_FLAG_EQ3, + offsetof(MCDRV_AE_INFO, abEq3), EQ3_PARAM_SIZE}, + }; + + mutex_lock(&mc1n2->mutex); + + mc1n2->ae_store.bOnOff = (mc1n2->ae_store.bOnOff & ~mask) | onoff; + + for (i = 0; i < sizeof(tbl)/sizeof(struct mc1n2_ae_copy); i++) { + if (update & tbl[i].flag) { + memcpy((void *)&mc1n2->ae_store + tbl[i].offset, + info + tbl[i].offset, tbl[i].size); + } + } + + mutex_unlock(&mc1n2->mutex); + + return 0; +} + +struct mc1n2_hwdep_func { + int cmd; + size_t size; + int (*callback)(struct snd_soc_codec *, void *, unsigned int); +}; + +struct mc1n2_hwdep_func mc1n2_hwdep_func_map[] = { + {0, 0, NULL}, /* INIT */ + {0, 0, NULL}, /* TERM */ + {MC1N2_IOCTL_NR_BOTH, sizeof(MCDRV_REG_INFO), NULL}, /* READ_REG */ + {0, 0, NULL}, /* WRITE_REG */ + {MC1N2_IOCTL_NR_GET, sizeof(MCDRV_PATH_INFO), NULL}, /* GET_PATH */ + {MC1N2_IOCTL_NR_SET, sizeof(MCDRV_PATH_INFO), + mc1n2_hwdep_ioctl_set_path}, /* SET_PATH */ + {0, 0, NULL}, /* GET_VOLUME */ + {0, 0, NULL}, /* SET_VOLUME */ + {MC1N2_IOCTL_NR_GET, sizeof(MCDRV_DIO_INFO), NULL}, /* GET_DIGITALIO */ + {MC1N2_IOCTL_NR_SET, sizeof(MCDRV_DIO_INFO), NULL}, /* SET_DIGITALIO */ + {MC1N2_IOCTL_NR_GET, sizeof(MCDRV_DAC_INFO), NULL}, /* GET_DAC */ + {MC1N2_IOCTL_NR_SET, sizeof(MCDRV_DAC_INFO), NULL}, /* SET_DAC */ + {MC1N2_IOCTL_NR_GET, sizeof(MCDRV_ADC_INFO), NULL}, /* GET_ADC */ + {MC1N2_IOCTL_NR_SET, sizeof(MCDRV_ADC_INFO), NULL}, /* SET_ADC */ + {MC1N2_IOCTL_NR_GET, sizeof(MCDRV_SP_INFO), NULL}, /* GET_SP */ + {MC1N2_IOCTL_NR_SET, sizeof(MCDRV_SP_INFO), NULL}, /* SET_SP */ + {MC1N2_IOCTL_NR_GET, sizeof(MCDRV_DNG_INFO), NULL}, /* GET_DNG */ + {MC1N2_IOCTL_NR_SET, sizeof(MCDRV_DNG_INFO), NULL}, /* SET_DNG */ + {MC1N2_IOCTL_NR_SET, sizeof(MCDRV_AE_INFO), + mc1n2_hwdep_ioctl_set_ae}, /* SET_AE */ + {0, 0, NULL}, /* SET_AE_EX */ + {0, 0, NULL}, /* SET_CDSP */ + {0, 0, NULL}, /* GET_CDSP_PARAM */ + {0, 0, NULL}, /* SET_CDSP_PARAM */ + {0, 0, NULL}, /* REG_CDSP_CB */ + {MC1N2_IOCTL_NR_GET, sizeof(MCDRV_PDM_INFO), NULL}, /* GET PDM */ + {MC1N2_IOCTL_NR_SET, sizeof(MCDRV_PDM_INFO), NULL}, /* SET_PDM */ + {0, 0, NULL}, /* SET_DTMF */ + {0, 0, NULL}, /* CONFIG_GP */ + {0, 0, NULL}, /* MASK_GP */ + {0, 0, NULL}, /* GETSET_GP */ + {0, 0, NULL}, /* GET_PEAK */ + {0, 0, NULL}, /* IRQ */ + {0, 0, NULL}, /* UPDATE_CLOCK */ + {MC1N2_IOCTL_NR_SET, sizeof(MCDRV_CLKSW_INFO), NULL}, /* SWITCH_CLOCK */ + {MC1N2_IOCTL_NR_GET, sizeof(MCDRV_SYSEQ_INFO), NULL}, /* GET SYSEQ */ + {MC1N2_IOCTL_NR_SET, sizeof(MCDRV_SYSEQ_INFO), NULL}, /* SET_SYSEQ */ +}; +#define MC1N2_HWDEP_N_FUNC_MAP \ + (sizeof(mc1n2_hwdep_func_map)/sizeof(struct mc1n2_hwdep_func)) + +static int mc1n2_hwdep_ioctl_get_ctrl(struct snd_soc_codec *codec, + struct mc1n2_ctrl_args *args) +{ + struct mc1n2_hwdep_func *func = &mc1n2_hwdep_func_map[args->dCmd]; + void *info; + int err; + + if (func->cmd != MC1N2_IOCTL_NR_GET) { + return -EINVAL; + } + + if (!access_ok(VERIFY_WRITE, args->pvPrm, func->size)) { + return -EFAULT; + } + + if (!(info = kzalloc(func->size, GFP_KERNEL))) { + return -ENOMEM; + } + + err = _McDrv_Ctrl(args->dCmd, info, args->dPrm); + err = mc1n2_hwdep_map_error(err); + if (err < 0) { + goto error; + } + + if (func->callback) { /* call post-process */ + func->callback(codec, info, args->dPrm); + } + + if (copy_to_user(args->pvPrm, info, func->size) != 0) { + err = -EFAULT; + goto error; + } + +error: + kfree(info); + return err; +} + +static int mc1n2_hwdep_ioctl_set_ctrl(struct snd_soc_codec *codec, + struct mc1n2_ctrl_args *args) +{ + struct mc1n2_hwdep_func *func = &mc1n2_hwdep_func_map[args->dCmd]; + void *info; + int err; + + if (func->cmd != MC1N2_IOCTL_NR_SET) { + return -EINVAL; + } + + if (!access_ok(VERIFY_READ, args->pvPrm, func->size)) { + return -EFAULT; + } + + if (!(info = kzalloc(func->size, GFP_KERNEL))) { + return -ENOMEM; + } + + if (copy_from_user(info, args->pvPrm, func->size) != 0) { + kfree(info); + return -EFAULT; + } + + if (func->callback) { /* call pre-process */ + func->callback(codec, info, args->dPrm); + } + + if (args->dCmd == MCDRV_SET_DIGITALIO) { +#ifdef DIO0_DAI_ENABLE + args->dPrm &= ~(MCDRV_DIO0_COM_UPDATE_FLAG | MCDRV_DIO0_DIR_UPDATE_FLAG | MCDRV_DIO0_DIT_UPDATE_FLAG); +#endif +#ifdef DIO1_DAI_ENABLE + args->dPrm &= ~(MCDRV_DIO1_COM_UPDATE_FLAG | MCDRV_DIO1_DIR_UPDATE_FLAG | MCDRV_DIO1_DIT_UPDATE_FLAG); +#endif +#ifdef DIO2_DAI_ENABLE + args->dPrm &= ~(MCDRV_DIO2_COM_UPDATE_FLAG | MCDRV_DIO2_DIR_UPDATE_FLAG | MCDRV_DIO2_DIT_UPDATE_FLAG); +#endif + } + + err = _McDrv_Ctrl(args->dCmd, info, args->dPrm); + + kfree(info); + + return mc1n2_hwdep_map_error(err); +} + +static int mc1n2_hwdep_ioctl_read_reg(struct mc1n2_ctrl_args *args) +{ + struct mc1n2_hwdep_func *func = &mc1n2_hwdep_func_map[args->dCmd]; + MCDRV_REG_INFO info; + int err; + + if (func->cmd != MC1N2_IOCTL_NR_BOTH) { + return -EINVAL; + } + + if (!access_ok(VERIFY_WRITE, args->pvPrm, sizeof(MCDRV_REG_INFO))) { + return -EFAULT; + } + + if (copy_from_user(&info, args->pvPrm, sizeof(MCDRV_REG_INFO)) != 0) { + return -EFAULT; + } + + err = _McDrv_Ctrl(args->dCmd, &info, args->dPrm); + if (err != MCDRV_SUCCESS) { + return mc1n2_hwdep_map_error(err); + } + + if (copy_to_user(args->pvPrm, &info, sizeof(MCDRV_REG_INFO)) != 0) { + return -EFAULT; + } + + return 0; +} + +static int mc1n2_hwdep_ioctl_notify(struct snd_soc_codec *codec, + struct mc1n2_ctrl_args *args) +{ + MCDRV_PATH_INFO path; + int err; + +#if (defined ALSA_VER_ANDROID_2_6_35) || (defined ALSA_VER_ANDROID_3_0) + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct mc1n2_data *mc1n2 = codec->private_data; +#endif + + switch (args->dCmd) { + case MCDRV_NOTIFY_CALL_START: + case MCDRV_NOTIFY_2MIC_CALL_START: + mc1n2_current_mode |= MC1N2_MODE_CALL_ON; + err = mc1n2->pdata->set_adc_power_constraints(0); + if (err < 0) { + dev_err(codec->dev, + "%s:%d:Error VADC_3.3V[On]\n", __func__, err); + } + break; + case MCDRV_NOTIFY_CALL_STOP: + mc1n2_current_mode &= ~MC1N2_MODE_CALL_ON; + err = mc1n2->pdata->set_adc_power_constraints(1); + if (err < 0) { + dev_err(codec->dev, + "%s:%d:Error VADC_3.3V[Off]\n", __func__, err); + } + break; + case MCDRV_NOTIFY_MEDIA_PLAY_START: + break; + case MCDRV_NOTIFY_MEDIA_PLAY_STOP: + break; + case MCDRV_NOTIFY_FM_PLAY_START: + mc1n2_current_mode |= MC1N2_MODE_FM_ON; + break; + case MCDRV_NOTIFY_FM_PLAY_STOP: + mc1n2_current_mode &= ~MC1N2_MODE_FM_ON; + break; + case MCDRV_NOTIFY_BT_SCO_ENABLE: + break; + case MCDRV_NOTIFY_BT_SCO_DISABLE: + break; + case MCDRV_NOTIFY_VOICE_REC_START: + mc1n2->delay_mic1in = MC1N2_WAITTIME_MICIN; + break; + case MCDRV_NOTIFY_VOICE_REC_STOP: + mc1n2->delay_mic1in = 0; + break; + case MCDRV_NOTIFY_HDMI_START: + if (mc1n2->hdmicount == 0) { + memset(&path, 0, sizeof(path)); + path.asDit0[0].abSrcOnOff[3] = MCDRV_SRC3_DIR0_OFF; + path.asDit1[0].abSrcOnOff[3] = MCDRV_SRC3_DIR0_OFF; + path.asDit2[0].abSrcOnOff[3] = MCDRV_SRC3_DIR0_OFF; + path.asDac[0].abSrcOnOff[3] = MCDRV_SRC3_DIR0_OFF; + path.asDac[1].abSrcOnOff[3] = MCDRV_SRC3_DIR0_OFF; + path.asAe[0].abSrcOnOff[3] = MCDRV_SRC3_DIR0_OFF; + path.asMix[0].abSrcOnOff[3] = MCDRV_SRC3_DIR0_OFF; + _McDrv_Ctrl(MCDRV_SET_PATH, &path, 0); + } + + (mc1n2->hdmicount)++; + break; + case MCDRV_NOTIFY_HDMI_STOP: + if (mc1n2->hdmicount != 0) { + if (mc1n2->hdmicount == 1) { + memset(&path, 0, sizeof(path)); + path.asDit0[0].abSrcOnOff[3] = MCDRV_SRC3_DIR0_OFF; + path.asDit1[0].abSrcOnOff[3] = MCDRV_SRC3_DIR0_OFF; + path.asDit2[0].abSrcOnOff[3] = MCDRV_SRC3_DIR0_OFF; + path.asDac[0].abSrcOnOff[3] = MCDRV_SRC3_DIR0_OFF; + path.asDac[1].abSrcOnOff[3] = MCDRV_SRC3_DIR0_OFF; + path.asAe[0].abSrcOnOff[3] = MCDRV_SRC3_DIR0_OFF; + path.asMix[0].abSrcOnOff[3] = MCDRV_SRC3_DIR0_OFF; + _McDrv_Ctrl(MCDRV_SET_PATH, &path, 0); + } + + (mc1n2->hdmicount)--; + } + break; + case MCDRV_NOTIFY_RECOVER: + { + int err, i; + SINT16 *vol = (SINT16 *)&mc1n2->vol_store; + + mutex_lock(&mc1n2->mutex); + + /* store parameters */ + for (i = 0; i < MC1N2_N_INFO_STORE; i++) { + struct mc1n2_info_store *store = &mc1n2_info_store_tbl[i]; + if (store->get) { + err = _McDrv_Ctrl(store->get, (void *)mc1n2 + store->offset, 0); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, + "%d: Error in MCDRV_GET_xxx\n", err); + err = -EIO; + goto error_recover; + } else { + err = 0; + } + } + } + + err = _McDrv_Ctrl(MCDRV_TERM, NULL, 0); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, "%d: Error in MCDRV_TERM\n", err); + err = -EIO; + } else { + err = 0; + } + + err = _McDrv_Ctrl(MCDRV_INIT, &mc1n2->setup.init, 0); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, "%d: Error in MCDRV_INIT\n", err); + err = -EIO; + goto error_recover; + } else { + err = 0; + } + + /* restore parameters */ + for (i = 0; i < sizeof(MCDRV_VOL_INFO)/sizeof(SINT16); i++, vol++) { + *vol |= 0x0001; + } + + for (i = 0; i < MC1N2_N_INFO_STORE; i++) { + struct mc1n2_info_store *store = &mc1n2_info_store_tbl[i]; + if (store->set) { + err = _McDrv_Ctrl(store->set, (void *)mc1n2 + store->offset, + store->flags); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, + "%d: Error in MCDRV_SET_xxx\n", err); + err = -EIO; + goto error_recover; + } else { + err = 0; + } + } + } + +error_recover: + mutex_unlock(&mc1n2->mutex); + return err; + break; + } + } + + return 0; +} + +static int mc1n2_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct mc1n2_ctrl_args ctrl_args; + struct snd_soc_codec *codec = hw->private_data; + int err; + + if (!access_ok(VERIFY_READ, (struct mc1n2_ctrl_args *)arg, + sizeof(struct mc1n2_ctrl_args))) { + return -EFAULT; + } + + if (copy_from_user(&ctrl_args, (struct mc1n2_ctrl_args *)arg, + sizeof(struct mc1n2_ctrl_args)) != 0) { + return -EFAULT; + } + + if (cmd == MC1N2_IOCTL_NOTIFY) { + return mc1n2_hwdep_ioctl_notify(codec, &ctrl_args); + } + + if (ctrl_args.dCmd >= MC1N2_HWDEP_N_FUNC_MAP) { + return -EINVAL; + } + + switch (cmd) { + case MC1N2_IOCTL_GET_CTRL: + err = mc1n2_hwdep_ioctl_get_ctrl(codec, &ctrl_args); + break; + case MC1N2_IOCTL_SET_CTRL: + err = mc1n2_hwdep_ioctl_set_ctrl(codec, &ctrl_args); + break; + case MC1N2_IOCTL_READ_REG: + err = mc1n2_hwdep_ioctl_read_reg(&ctrl_args); + break; + default: + err = -EINVAL; + } + + return err; +} + +static int mc1n2_add_hwdep(struct snd_soc_codec *codec) +{ + struct snd_hwdep *hw; +#if (defined ALSA_VER_ANDROID_2_6_35) || (defined ALSA_VER_ANDROID_3_0) + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct mc1n2_data *mc1n2 = codec->private_data; +#endif + int err; + +#ifdef ALSA_VER_ANDROID_3_0 + err = snd_hwdep_new((struct snd_card *)codec->card->snd_card, + MC1N2_HWDEP_ID, 0, &hw); +#else + err = snd_hwdep_new(codec->card, MC1N2_HWDEP_ID, 0, &hw); +#endif + if (err < 0) { + return err; + } + + hw->iface = SNDRV_HWDEP_IFACE_MC1N2; + hw->private_data = codec; + hw->ops.open = mc1n2_hwdep_open; + hw->ops.release = mc1n2_hwdep_release; + hw->ops.ioctl = mc1n2_hwdep_ioctl; + hw->exclusive = 1; + strcpy(hw->name, MC1N2_HWDEP_ID); + mc1n2->hwdep = hw; + + return 0; +} + +/* + * Codec device + */ +#ifdef ALSA_VER_ANDROID_3_0 +static int mc1n2_probe(struct snd_soc_codec *codec) +#else +static int mc1n2_probe(struct platform_device *pdev) +#endif +{ +#ifdef ALSA_VER_ANDROID_3_0 + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); + struct device *dev = codec->dev; + struct mc1n2_setup *setup = &mc1n2_cfg_setup; +#else + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = mc1n2_get_codec_data(); +#ifdef ALSA_VER_ANDROID_2_6_35 + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct mc1n2_data *mc1n2 = codec->private_data; +#endif + struct mc1n2_setup *setup = socdev->codec_data; + struct device *dev = socdev->dev; +#endif + int err; + UINT32 update = 0; + + TRACE_FUNC(); + + if (!codec) { + dev_err(dev, "I2C bus is not probed successfully\n"); + err = -ENODEV; + goto error_codec_data; + } +#ifndef ALSA_VER_ANDROID_3_0 +#ifdef ALSA_VER_1_0_19 + socdev->codec = codec; +#else + socdev->card->codec = codec; +#endif +#endif + + /* init hardware */ + if (!setup) { + dev_err(dev, "No initialization parameters given\n"); + err = -EINVAL; + goto error_init_hw; + } + memcpy(&mc1n2->setup, setup, sizeof(struct mc1n2_setup)); + err = _McDrv_Ctrl(MCDRV_INIT, &mc1n2->setup.init, 0); + if (err != MCDRV_SUCCESS) { + dev_err(dev, "%d: Error in MCDRV_INIT\n", err); + err = -EIO; + goto error_init_hw; + } + + /* pcm */ +#ifndef ALSA_VER_ANDROID_3_0 + err = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (err < 0) { + dev_err(dev, "%d: Error in snd_soc_new_pcms\n", err); + goto error_new_pcm; + } +#endif + + /* controls */ + err = mc1n2_add_controls(codec, mc1n2_snd_controls, + ARRAY_SIZE(mc1n2_snd_controls)); + if (err < 0) { + dev_err(dev, "%d: Error in mc1n2_add_controls\n", err); + goto error_add_ctl; + } + + err = mc1n2_add_widgets(codec); + if (err < 0) { + dev_err(dev, "%d: Error in mc1n2_add_widgets\n", err); + goto error_add_ctl; + } + + /* hwdep */ + err = mc1n2_add_hwdep(codec); + if (err < 0) { + dev_err(dev, "%d: Error in mc1n2_add_hwdep\n", err); + goto error_add_hwdep; + } + +#if (defined ALSA_VER_1_0_19) || (defined ALSA_VER_1_0_21) + err = snd_soc_init_card(socdev); + if (err < 0) { + dev_err(dev, "%d: Error in snd_soc_init_card\n", err); + goto error_init_card; + } +#endif + +#ifndef DIO0_DAI_ENABLE + update |= (MCDRV_DIO0_COM_UPDATE_FLAG | MCDRV_DIO0_DIR_UPDATE_FLAG | MCDRV_DIO0_DIT_UPDATE_FLAG); +#endif + +#ifndef DIO1_DAI_ENABLE + update |= (MCDRV_DIO1_COM_UPDATE_FLAG | MCDRV_DIO1_DIR_UPDATE_FLAG | MCDRV_DIO1_DIT_UPDATE_FLAG); +#endif + +#ifndef DIO2_DAI_ENABLE + update |= (MCDRV_DIO2_COM_UPDATE_FLAG | MCDRV_DIO2_DIR_UPDATE_FLAG | MCDRV_DIO2_DIT_UPDATE_FLAG); +#endif + + err = _McDrv_Ctrl(MCDRV_SET_DIGITALIO, (void *)&stDioInfo_Default, update); + if (err < 0) { + dev_err(dev, "%d: Error in MCDRV_SET_DIGITALIO\n", err); + goto error_set_mode; + } + + err = _McDrv_Ctrl(MCDRV_SET_DAC, (void *)&stDacInfo_Default, 0x7); + if (err < 0) { + dev_err(dev, "%d: Error in MCDRV_SET_DAC\n", err); + goto error_set_mode; + } + + err = _McDrv_Ctrl(MCDRV_SET_ADC, (void *)&stAdcInfo_Default, 0x7); + if (err < 0) { + dev_err(dev, "%d: Error in MCDRV_SET_ADC\n", err); + goto error_set_mode; + } + + err = _McDrv_Ctrl(MCDRV_SET_SP, (void *)&stSpInfo_Default, 0); + if (err < 0) { + dev_err(dev, "%d: Error in MCDRV_SET_SP\n", err); + goto error_set_mode; + } + + err = _McDrv_Ctrl(MCDRV_SET_DNG, (void *)&stDngInfo_Default, 0x3F3F3F); + if (err < 0) { + dev_err(dev, "%d: Error in MCDRV_SET_DNG\n", err); + goto error_set_mode; + } + + if (mc1n2_hwid == MC1N2_HW_ID_AB) { + err = _McDrv_Ctrl(MCDRV_SET_SYSEQ, (void *)&stSyseqInfo_Default, 0x3); + + if (err < 0) { + dev_err(dev, "%d: Error in MCDRV_SET_SYSEQ\n", err); + goto error_set_mode; + } + } + + return 0; + +error_set_mode: +#if (defined ALSA_VER_1_0_19) || (defined ALSA_VER_1_0_21) +error_init_card: +#endif +error_add_hwdep: +error_add_ctl: +#ifndef ALSA_VER_ANDROID_3_0 + snd_soc_free_pcms(socdev); +error_new_pcm: +#endif + _McDrv_Ctrl(MCDRV_TERM, NULL, 0); +error_init_hw: +#ifndef ALSA_VER_ANDROID_3_0 +#ifdef ALSA_VER_1_0_19 + socdev->codec = NULL; +#else + socdev->card->codec = NULL; +#endif +#endif +error_codec_data: + return err; +} + +#ifdef ALSA_VER_ANDROID_3_0 +static int mc1n2_remove(struct snd_soc_codec *codec) +{ + int err; + + TRACE_FUNC(); + + if (codec) { + err = _McDrv_Ctrl(MCDRV_TERM, NULL, 0); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, "%d: Error in MCDRV_TERM\n", err); + return -EIO; + } + } + return 0; +} +#else +static int mc1n2_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + int err; + + TRACE_FUNC(); + +#ifdef ALSA_VER_1_0_19 + if (socdev->codec) { +#else + if (socdev->card->codec) { +#endif + snd_soc_free_pcms(socdev); + + err = _McDrv_Ctrl(MCDRV_TERM, NULL, 0); + if (err != MCDRV_SUCCESS) { + dev_err(socdev->dev, "%d: Error in MCDRV_TERM\n", err); + return -EIO; + } + } + + return 0; +} +#endif + +#ifdef ALSA_VER_ANDROID_3_0 +static int mc1n2_suspend(struct snd_soc_codec *codec, pm_message_t state) +#else +static int mc1n2_suspend(struct platform_device *pdev, pm_message_t state) +#endif +{ +#ifdef ALSA_VER_ANDROID_3_0 + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct snd_soc_device *socdev = platform_get_drvdata(pdev); +#ifdef ALSA_VER_1_0_19 + struct snd_soc_codec *codec = socdev->codec; +#else + struct snd_soc_codec *codec = socdev->card->codec; +#endif +#ifdef ALSA_VER_ANDROID_2_6_35 + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct mc1n2_data *mc1n2 = codec->private_data; +#endif +#endif + int err, i; + + TRACE_FUNC(); + + mutex_lock(&mc1n2->mutex); + + /* store parameters */ + for (i = 0; i < MC1N2_N_INFO_STORE; i++) { + struct mc1n2_info_store *store = &mc1n2_info_store_tbl[i]; + if (store->get) { + err = _McDrv_Ctrl(store->get, (void *)mc1n2 + store->offset, 0); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, + "%d: Error in mc1n2_suspend\n", err); + err = -EIO; + goto error; + } else { + err = 0; + } + } + } + + /* Do not enter suspend mode for voice call */ + if(mc1n2_current_mode != MC1N2_MODE_IDLE) { + err = 0; + goto error; + } + + err = _McDrv_Ctrl(MCDRV_TERM, NULL, 0); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, "%d: Error in MCDRV_TERM\n", err); + err = -EIO; + } else { + err = 0; + } + + /* Suepend MCLK */ + mc1n2_set_mclk_source(0); + +error: + mutex_unlock(&mc1n2->mutex); + + return err; +} + +#ifdef ALSA_VER_ANDROID_3_0 +static int mc1n2_resume(struct snd_soc_codec *codec) +#else +static int mc1n2_resume(struct platform_device *pdev) +#endif +{ +#ifdef ALSA_VER_ANDROID_3_0 + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct snd_soc_device *socdev = platform_get_drvdata(pdev); +#ifdef ALSA_VER_1_0_19 + struct snd_soc_codec *codec = socdev->codec; +#else + struct snd_soc_codec *codec = socdev->card->codec; +#endif +#ifdef ALSA_VER_ANDROID_2_6_35 + struct mc1n2_data *mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + struct mc1n2_data *mc1n2 = codec->private_data; +#endif +#endif + SINT16 *vol = (SINT16 *)&mc1n2->vol_store; + int err, i; + + TRACE_FUNC(); + + mutex_lock(&mc1n2->mutex); + + /* Resume MCLK */ + mc1n2_set_mclk_source(1); + + err = _McDrv_Ctrl(MCDRV_INIT, &mc1n2->setup.init, 0); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, "%d: Error in MCDRV_INIT\n", err); + err = -EIO; + goto error; + } else { + err = 0; + } + + /* restore parameters */ + for (i = 0; i < sizeof(MCDRV_VOL_INFO)/sizeof(SINT16); i++, vol++) { + *vol |= 0x0001; + } + + for (i = 0; i < MC1N2_N_INFO_STORE; i++) { + struct mc1n2_info_store *store = &mc1n2_info_store_tbl[i]; + if (store->set) { + err = _McDrv_Ctrl(store->set, (void *)mc1n2 + store->offset, + store->flags); + if (err != MCDRV_SUCCESS) { + dev_err(codec->dev, + "%d: Error in mc1n2_resume\n", err); + err = -EIO; + goto error; + } else { + err = 0; + } + } + } + +error: + mutex_unlock(&mc1n2->mutex); + + return err; +} + +#ifdef ALSA_VER_ANDROID_3_0 +struct snd_soc_codec_driver soc_codec_dev_mc1n2 = { + .probe = mc1n2_probe, + .remove = mc1n2_remove, + .suspend = mc1n2_suspend, + .resume = mc1n2_resume, + .read = mc1n2_read_reg, + .write = mc1n2_write_reg, + .reg_cache_size = MC1N2_N_REG, + .reg_word_size = sizeof(u16), + .reg_cache_step = 1 +}; +#else +struct snd_soc_codec_device soc_codec_dev_mc1n2 = { + .probe = mc1n2_probe, + .remove = mc1n2_remove, + .suspend = mc1n2_suspend, + .resume = mc1n2_resume +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_mc1n2); +#endif + +/* + * I2C client + */ +static int mc1n2_i2c_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + UINT8 bHwid = mc1n2_i2c_read_byte(client, 8); + + if (bHwid != MC1N2_HW_ID_AB && bHwid != MC1N2_HW_ID_AA) { + return -ENODEV; + } + mc1n2_hwid = bHwid; + + return 0; +} + +static int mc1n2_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct snd_soc_codec *codec; + struct mc1n2_data *mc1n2; + int err; + + TRACE_FUNC(); + + /* setup codec data */ + if (!(codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL))) { + err = -ENOMEM; + goto err_alloc_codec; + } + codec->name = MC1N2_NAME; +// codec->owner = THIS_MODULE; + mutex_init(&codec->mutex); + codec->dev = &client->dev; + + if (!(mc1n2 = kzalloc(sizeof(struct mc1n2_data), GFP_KERNEL))) { + err = -ENOMEM; + goto err_alloc_data; + } + mutex_init(&mc1n2->mutex); +#if (defined ALSA_VER_ANDROID_2_6_35) || (defined ALSA_VER_ANDROID_3_0) + snd_soc_codec_set_drvdata(codec, mc1n2); +#else + codec->private_data = mc1n2; +#endif + + mc1n2->hdmicount = 0; + + mc1n2->pdata = client->dev.platform_data; + + /* setup i2c client data */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + err = -ENODEV; + goto err_i2c; + } + + if ((err = mc1n2_i2c_detect(client, NULL)) < 0) { + goto err_i2c; + } + + +#ifdef ALSA_VER_ANDROID_3_0 + i2c_set_clientdata(client, mc1n2); +#else + i2c_set_clientdata(client, codec); + + codec->control_data = client; + codec->read = mc1n2_read_reg; + codec->write = mc1n2_write_reg; + codec->hw_write = NULL; + codec->hw_read = NULL; + codec->reg_cache = kzalloc(sizeof(u16) * MC1N2_N_REG, GFP_KERNEL); + if (codec->reg_cache == NULL) { + err = -ENOMEM; + goto err_alloc_cache; + } + codec->reg_cache_size = MC1N2_N_REG; + codec->reg_cache_step = 1; + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + codec->dai = mc1n2_dai; + codec->num_dai = ARRAY_SIZE(mc1n2_dai); + mc1n2_set_codec_data(codec); +#endif + +#ifdef ALSA_VER_ANDROID_3_0 + if ((err = snd_soc_register_codec(&client->dev, &soc_codec_dev_mc1n2, + mc1n2_dai, ARRAY_SIZE(mc1n2_dai))) < 0) { + goto err_reg_codec; + } + + mc1n2_i2c = client; +#else + if ((err = snd_soc_register_codec(codec)) < 0) { + goto err_reg_codec; + } + + /* setup DAI data */ + for (i = 0; i < ARRAY_SIZE(mc1n2_dai); i++) { + mc1n2_dai[i].dev = &client->dev; + } + if ((err = snd_soc_register_dais(mc1n2_dai, ARRAY_SIZE(mc1n2_dai))) < 0) { + goto err_reg_dai; + } +#endif + + return 0; + +#ifndef ALSA_VER_ANDROID_3_0 +err_reg_dai: + snd_soc_unregister_codec(codec); +#endif +err_reg_codec: +#ifndef ALSA_VER_ANDROID_3_0 + kfree(codec->reg_cache); +err_alloc_cache: +#endif + i2c_set_clientdata(client, NULL); +err_i2c: + kfree(mc1n2); +err_alloc_data: + kfree(codec); +err_alloc_codec: + dev_err(&client->dev, "err=%d: failed to probe MC-1N2\n", err); + return err; +} + +static int mc1n2_i2c_remove(struct i2c_client *client) +{ +#ifndef ALSA_VER_ANDROID_3_0 + struct snd_soc_codec *codec = i2c_get_clientdata(client); +#endif + struct mc1n2_data *mc1n2; + + TRACE_FUNC(); + +#ifdef ALSA_VER_ANDROID_3_0 + mc1n2 = (struct mc1n2_data*)(i2c_get_clientdata(client)); + mutex_destroy(&mc1n2->mutex); + snd_soc_unregister_codec(&client->dev); +#else + if (codec) { +#ifdef ALSA_VER_ANDROID_2_6_35 + mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + mc1n2 = codec->private_data; +#endif + snd_soc_unregister_dais(mc1n2_dai, ARRAY_SIZE(mc1n2_dai)); + snd_soc_unregister_codec(codec); + + mutex_destroy(&mc1n2->mutex); + kfree(mc1n2); + + mutex_destroy(&codec->mutex); + kfree(codec); + } +#endif + + return 0; +} + +#ifdef CONFIG_TARGET_LOCALE_KOR +/* + * Function to prevent tick-noise when reboot menu selected. + * if you have Power-Off sound and same problem, use this function + */ +static void mc1n2_i2c_shutdown(struct i2c_client *client) +{ +#ifndef ALSA_VER_ANDROID_3_0 + struct snd_soc_codec *codec = i2c_get_clientdata(client); +#endif + struct mc1n2_data *mc1n2; + int err, i; + + pr_info("%s\n", __func__); + + TRACE_FUNC(); + +#ifdef ALSA_VER_ANDROID_3_0 + mc1n2 = (struct mc1n2_data *)(i2c_get_clientdata(client)); +#else +#ifdef ALSA_VER_ANDROID_2_6_35 + mc1n2 = snd_soc_codec_get_drvdata(codec); +#else + mc1n2 = codec->private_data; +#endif +#endif + + mutex_lock(&mc1n2->mutex); + + /* store parameters */ + for (i = 0; i < MC1N2_N_INFO_STORE; i++) { + struct mc1n2_info_store *store = &mc1n2_info_store_tbl[i]; + if (store->get) { + err = _McDrv_Ctrl(store->get, + (void *)mc1n2 + store->offset, 0); + if (err != MCDRV_SUCCESS) { + pr_err("%d: Error in mc1n2_suspend\n", err); + err = -EIO; + goto error; + } else { + err = 0; + } + } + } + + /* Do not enter suspend mode for voice call */ + if (mc1n2_current_mode != MC1N2_MODE_IDLE) { + err = 0; + goto error; + } + + err = _McDrv_Ctrl(MCDRV_TERM, NULL, 0); + if (err != MCDRV_SUCCESS) { + pr_err("%d: Error in MCDRV_TERM\n", err); + err = -EIO; + } else { + err = 0; + } + + /* Suepend MCLK */ + mc1n2_set_mclk_source(0); + + pr_info("%s done\n", __func__); + +error: + mutex_unlock(&mc1n2->mutex); + + if (err != 0) + pr_err("%s: err = %d\n", __func__, err); + + return; +} +#endif + +static const struct i2c_device_id mc1n2_i2c_id[] = { + {MC1N2_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, mc1n2_i2c_id); + +static struct i2c_driver mc1n2_i2c_driver = { + .driver = { + .name = MC1N2_NAME, + .owner = THIS_MODULE, + }, + .probe = mc1n2_i2c_probe, + .remove = mc1n2_i2c_remove, +#ifdef CONFIG_TARGET_LOCALE_KOR + .shutdown = mc1n2_i2c_shutdown, +#endif + .id_table = mc1n2_i2c_id, +}; + +/* + * Module init and exit + */ +static int __init mc1n2_init(void) +{ + return i2c_add_driver(&mc1n2_i2c_driver); +} +module_init(mc1n2_init); + +static void __exit mc1n2_exit(void) +{ + i2c_del_driver(&mc1n2_i2c_driver); +} +module_exit(mc1n2_exit); + +MODULE_AUTHOR("Yamaha Corporation"); +MODULE_DESCRIPTION("Yamaha MC-1N2 ALSA SoC codec driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(MC1N2_DRIVER_VERSION); |