oleavr-rgl-a500-mini-linux-.../sound/soc/sunxi/audiocodec/sun8iw5_sndcodec.c
Ole André Vadla Ravnås 169c65d57e Initial commit
2022-05-07 01:01:45 +02:00

3995 lines
118 KiB
C

/*
* sound\soc\sunxi\audiocodec\sndcodec.c
* (C) Copyright 2010-2016
* Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
* huangxin <huangxin@Reuuimllatech.com>
*
* some simple description for this code
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/regulator/consumer.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/sys_config.h>
#include <linux/pinctrl/pinconf-sunxi.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm.h>
#include <linux/power/scenelock.h>
#include "sunxi_codecdma.h"
#include "sun8iw5_sndcodec.h"
#define SNDPCM_RATES (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_KNOT)
#define SNDPCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct regulator *hp_ldo;
static char *hp_ldo_str;
static int play_running;
static int cap_running;
static int current_running = -1;
/*for pa gpio ctrl*/
static unsigned int spk_gpio;
/*for phone call flag*/
static bool codec_analog_phonein_en;
static bool codec_analog_mainmic_en;
static bool codec_analog_phoneout_en;
static bool codec_lineinin_en;
static bool codec_lineincap_en;
static bool codec_analog_headsetmic_en;
static bool codec_speakerout_en;
static bool codec_earpieceout_en;
static bool codec_voice_record_en;
static bool codec_headphoneout_en;
/*Digital_bb*/
static bool codec_digital_headsetmic_en;
static bool codec_digital_mainmic_en;
static bool codec_digital_phoneout_en;
static bool codec_digital_phonein_en;
static bool codec_digital_bb_clk_format_init;
/*bluetooth*/
static bool codec_bt_clk_format;
static bool codec_bt_out_en;
static bool bt_bb_button_voice;
static bool codec_analog_btmic_en;
static bool codec_analog_btphonein_en;
static bool codec_digital_btmic_en;
static bool codec_digital_btphonein_en;
static bool codec_digital_bb_bt_clk_format;
static bool codec_system_bt_capture_en;
static bool codec_analog_bb_capture_mic;
static int codec_speaker_headset_earpiece_en;
static int pa_vol;
static int cap_vol;
static int earpiece_vol;
static int headphone_vol;
static int pa_double_used;
static int phone_main_mic_vol;
static int headphone_direct_used;
static int phone_headset_mic_vol;
static int aif2_used;
static int aif3_used;
static int dac_vol_ctrl_spk = 0x9e9e;
static int dac_vol_ctrl_headphone = 0xa0a0;
static int agc_used;
static int drc_used;
static struct label reg_labels[] = {
LABEL(SUNXI_DA_CTL),
LABEL(SUNXI_DA_FAT0),
LABEL(SUNXI_DA_FAT1),
LABEL(SUNXI_DA_TXFIFO),
LABEL(SUNXI_DA_RXFIFO),
LABEL(SUNXI_DA_FCTL),
LABEL(SUNXI_DA_FSTA),
LABEL(SUNXI_DA_INT),
LABEL(SUNXI_DA_ISTA),
LABEL(SUNXI_DA_CLKD),
LABEL(SUNXI_DA_TXCNT),
LABEL(SUNXI_DA_RXCNT),
LABEL(SUNXI_DA_TXCHSEL),
LABEL(SUNXI_DA_TXCHMAP),
LABEL(SUNXI_DA_RXCHSEL),
LABEL(SUNXI_DA_RXCHMAP),
LABEL(SUNXI_CHIP_AUDIO_RST),
LABEL(SUNXI_SYSCLK_CTL),
LABEL(SUNXI_MOD_CLK_ENA),
LABEL(SUNXI_MOD_RST_CTL),
LABEL(SUNXI_SYS_SR_CTRL),
LABEL(SUNXI_SYS_SRC_CLK),
LABEL(SUNXI_AIF1CLK_CTRL),
LABEL(SUNXI_AIF1_ADCDAT_CTRL),
LABEL(SUNXI_AIF1_DACDAT_CTRL),
LABEL(SUNXI_AIF1_MXR_SRC),
LABEL(SUNXI_AIF1_VOL_CTRL1),
LABEL(SUNXI_AIF1_VOL_CTRL2),
LABEL(SUNXI_AIF1_VOL_CTRL3),
LABEL(SUNXI_AIF1_VOL_CTRL4),
LABEL(SUNXI_AIF1_MXR_GAIN),
LABEL(SUNXI_AIF1_RXD_CTRL),
LABEL(SUNXI_AIF2_CLK_CTRL),
LABEL(SUNXI_AIF2_ADCDAT_CTRL),
LABEL(SUNXI_AIF2_DACDAT_CTRL),
LABEL(SUNXI_AIF2_MXR_SRC),
LABEL(SUNXI_AIF2_VOL_CTRL1),
LABEL(SUNXI_AIF2_VOL_CTRL2),
LABEL(SUNXI_AIF2_MXR_GAIN),
LABEL(SUNXI_AIF2_RXD_CTRL),
LABEL(SUNXI_AIF3_CLK_CTRL),
LABEL(SUNXI_AIF3_ADCDAT_CTRL),
LABEL(SUNXI_AIF3_DACDAT_CTRL),
LABEL(SUNXI_AIF3_SGP_CTRL),
LABEL(SUNXI_AIF3_RXD_CTRL),
LABEL(SUNXI_ADC_DIG_CTRL),
LABEL(SUNXI_ADC_VOL_CTRL),
LABEL(SUNXI_ADC_DBG_CTRL),
LABEL(SUNXI_HMIC_CTRL1),
LABEL(SUNXI_HMIC_CTRL2),
LABEL(SUNXI_HMIC_STS),
LABEL(SUNXI_DAC_DIG_CTRL),
LABEL(SUNXI_DAC_VOL_CTRL),
LABEL(SUNXI_DAC_DBG_CTRL),
LABEL(SUNXI_DAC_MXR_SRC),
LABEL(SUNXI_DAC_MXR_GAIN),
LABEL(SUNXI_AC_DAPHHPFC),
LABEL(SUNXI_AC_DAPLHPFC),
LABEL(SUNXI_AC_DAPOPT),
LABEL(SUNXI_AGC_ENA),
LABEL(SUNXI_DRC_ENA),
LABEL(SUNXI_SRC_BISTCR),
LABEL(SUNXI_SRC_BISTST),
LABEL(SUNXI_SRC1_CTRL1),
LABEL(SUNXI_SRC1_CTROL2),
LABEL(SUNXI_SRC1_CTRL3),
LABEL(SUNXI_SRC1_CTRL4),
LABEL(SUNXI_SRC2_CTRL1),
LABEL(SUNXI_SRC2_CTRL2),
LABEL(SUNXI_SRC2_CTRL3),
LABEL(SUNXI_SRC2_CTRL4),
LABEL(HP_VOLC),
LABEL(LOMIXSC),
LABEL(ROMIXSC),
LABEL(DAC_PA_SRC),
LABEL(PHONEIN_GCTRL),
LABEL(LINEIN_GCTRL),
LABEL(MICIN_GCTRL),
LABEL(PAEN_HP_CTRL),
LABEL(PHONEOUT_CTRL),
LABEL(LINEOUT_VOLC),
LABEL(MIC2G_LINEEN_CTRL),
LABEL(MIC1G_MICBIAS_CTRL),
LABEL(LADCMIXSC),
LABEL(RADCMIXSC),
LABEL(ADC_AP_EN),
LABEL(ADDA_APT0),
LABEL(ADDA_APT1),
LABEL(ADDA_APT2),
LABEL(BIAS_AD16_CAL_CTRL),
LABEL(BIAS_DA16_CAL_CTRL),
LABEL(DA16CALI),
LABEL(DA16VERIFY),
LABEL(BIASCALI),
LABEL(BIASVERIFY),
LABEL_END
};
static struct clk *codec_pll2clk, *codec_moduleclk, *codec_srcclk;
static unsigned int read_prcm_wvalue(unsigned int addr)
{
unsigned int reg;
reg = readl(ADDA_PR_CFG_REG);
reg |= (0x1<<28);
writel(reg, ADDA_PR_CFG_REG);
reg = readl(ADDA_PR_CFG_REG);
reg &= ~(0x1<<24);
writel(reg, ADDA_PR_CFG_REG);
reg = readl(ADDA_PR_CFG_REG);
reg &= ~(0x1f<<16);
reg |= (addr<<16);
writel(reg, ADDA_PR_CFG_REG);
reg = readl(ADDA_PR_CFG_REG);
reg &= (0xff<<0);
return reg;
}
static void write_prcm_wvalue(unsigned int addr, unsigned int val)
{
unsigned int reg;
reg = readl(ADDA_PR_CFG_REG);
reg |= (0x1<<28);
writel(reg, ADDA_PR_CFG_REG);
reg = readl(ADDA_PR_CFG_REG);
reg &= ~(0x1f<<16);
reg |= (addr<<16);
writel(reg, ADDA_PR_CFG_REG);
reg = readl(ADDA_PR_CFG_REG);
reg &= ~(0xff<<8);
reg |= (val<<8);
writel(reg, ADDA_PR_CFG_REG);
reg = readl(ADDA_PR_CFG_REG);
reg |= (0x1<<24);
writel(reg, ADDA_PR_CFG_REG);
reg = readl(ADDA_PR_CFG_REG);
reg &= ~(0x1<<24);
writel(reg, ADDA_PR_CFG_REG);
}
/**
* codec_wrreg_bits - update codec register bits
* @reg: codec register
* @mask: register mask
* @value: new value
*
* Writes new register value.
* Return 1 for change else 0.
*/
static int codec_wrreg_prcm_bits(unsigned short reg, unsigned int mask,
unsigned int value)
{
unsigned int old, new;
old = read_prcm_wvalue(reg);
new = (old & ~mask) | value;
write_prcm_wvalue(reg, new);
return 0;
}
static int codec_wr_prcm_control(u32 reg, u32 mask, u32 shift, u32 val)
{
u32 reg_val;
reg_val = val << shift;
mask = mask << shift;
codec_wrreg_prcm_bits(reg, mask, reg_val);
return 0;
}
/**
* codec_wrreg_bits - update codec register bits
* @reg: codec register
* @mask: register mask
* @value: new value
*
* Writes new register value.
* Return 1 for change else 0.
*/
int codec_wrreg_bits(unsigned short reg, unsigned int mask,
unsigned int value)
{
unsigned int old, new;
old = codec_rdreg(reg);
new = (old & ~mask) | value;
codec_wrreg(reg, new);
return 0;
}
/**
* snd_codec_info_volsw - single mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
* Callback to provide information about a single mixer control
*
* Returns 0 for success
*/
int snd_codec_info_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct codec_mixer_control *mc =
(struct codec_mixer_control *)kcontrol->private_value;
int max = mc->max;
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
if (max == 1)
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;/*the info of type*/
else
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = shift == rshift ? 1 : 2; /*the info of elem count*/
uinfo->value.integer.min = 0; /*the info of min value*/
uinfo->value.integer.max = max; /*the info of max value*/
return 0;
}
/**
* snd_codec_get_volsw - single mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a single mixer control
* return 0 for success.
*/
int snd_codec_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct codec_mixer_control *mc =
(struct codec_mixer_control *)kcontrol->private_value;
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
int max = mc->max;
/*fls(7) = 3,fls(1)=1,fls(0)=0,fls(15)=4,fls(3)=2,fls(23)=5*/
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
unsigned int reg = mc->reg;
ucontrol->value.integer.value[0] =
(read_prcm_wvalue(reg) >> shift) & mask;
if (shift != rshift)
ucontrol->value.integer.value[1] =
(read_prcm_wvalue(reg) >> rshift) & mask;
if (invert) {
ucontrol->value.integer.value[0] =
max - ucontrol->value.integer.value[0];
if (shift != rshift)
ucontrol->value.integer.value[1] =
max - ucontrol->value.integer.value[1];
}
return 0;
}
/**
* snd_codec_put_volsw - single mixer put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to put the value of a single mixer control
*
* return 0 for success.
*/
int snd_codec_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct codec_mixer_control *mc =
(struct codec_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
int max = mc->max;
unsigned int mask = (1<<fls(max))-1;
unsigned int invert = mc->invert;
unsigned int val, val2, val_mask;
val = (ucontrol->value.integer.value[0] & mask);
if (invert)
val = max - val;
val <<= shift;
val_mask = mask << shift;
if (shift != rshift) {
val2 = (ucontrol->value.integer.value[1] & mask);
if (invert)
val2 = max - val2;
val_mask |= mask << rshift;
val |= val2 << rshift;
}
return codec_wrreg_prcm_bits(reg, val_mask, val);
}
/**
* snd_codec_get_volsw_digital - single mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a single mixer control
* return 0 for success.
*/
int snd_codec_get_volsw_digital(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct codec_mixer_control *mc =
(struct codec_mixer_control *)kcontrol->private_value;
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
int max = mc->max;
/*fls(7) = 3,fls(1)=1,fls(0)=0,fls(15)=4,fls(3)=2,fls(23)=5*/
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
unsigned int reg = mc->reg;
ucontrol->value.integer.value[0] =
(codec_rdreg(reg) >> shift) & mask;
if (shift != rshift)
ucontrol->value.integer.value[1] =
(codec_rdreg(reg) >> rshift) & mask;
if (invert) {
ucontrol->value.integer.value[0] =
max - ucontrol->value.integer.value[0];
if (shift != rshift)
ucontrol->value.integer.value[1] =
max - ucontrol->value.integer.value[1];
}
return 0;
}
/**
* snd_codec_put_volsw_digital - single mixer put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to put the value of a single mixer control
*
* return 0 for success.
*/
int snd_codec_put_volsw_digital(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct codec_mixer_control *mc =
(struct codec_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int rshift = mc->rshift;
int max = mc->max;
unsigned int mask = (1<<fls(max))-1;
unsigned int invert = mc->invert;
unsigned int val, val2, val_mask;
val = (ucontrol->value.integer.value[0] & mask);
if (invert)
val = max - val;
val <<= shift;
val_mask = mask << shift;
if (shift != rshift) {
val2 = (ucontrol->value.integer.value[1] & mask);
if (invert)
val2 = max - val2;
val_mask |= mask << rshift;
val |= val2 << rshift;
}
return codec_wrreg_bits(reg, val_mask, val);
}
int codec_wr_control(u32 reg, u32 mask, u32 shift, u32 val)
{
u32 reg_val;
reg_val = val << shift;
mask = mask << shift;
codec_wrreg_bits(reg, mask, reg_val);
return 0;
}
#if 0
static int codec_rd_control(u32 reg, u32 bit, u32 *val)
{
return 0;
}
static void codec_resume_events(struct work_struct *work)
{
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, HPPAEN, 0x1);
msleep(400);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, HMICBIASEN, 0x1);
pr_debug("====codec_resume_events===\n");
}
#endif
static void get_audio_param(void)
{
}
/*
* enable the codec function which should be enable during system init.
*/
static void codec_init(void)
{
get_audio_param();
if (headphone_direct_used) {
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x3);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, COMPTEN, 0x1);
} else {
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, COMPTEN, 0x0);
}
if (drc_used) {
codec_wr_control(0x48c, 0x7ff, 0, 0x1);
/*codec_wr_control(0x48c, 0x7ff, 0, 0x0);*/
codec_wr_control(0x490, 0xffff, 0, 0x2baf);
/*codec_wr_control(0x490, 0xffff, 0, 0x1fb6);*/
codec_wr_control(0x494, 0x7ff, 0, 0x1);
codec_wr_control(0x498, 0xffff, 0, 0x2baf);
codec_wr_control(0x49c, 0x7ff, 0, 0x0);
codec_wr_control(0x4a0, 0xffff, 0, 0x44a);
codec_wr_control(0x4a4, 0x7ff, 0, 0x0);
codec_wr_control(0x4a8, 0xffff, 0, 0x1e06);
/*codec_wr_control(0x4ac, 0x7ff, 0, 0x27d);*/
codec_wr_control(0x4ac, 0x7ff, 0, 0x352);
/*codec_wr_control(0x4b0, 0xffff, 0, 0xcf68);*/
codec_wr_control(0x4b0, 0xffff, 0, 0x6910);
codec_wr_control(0x4b4, 0x7ff, 0, 0x77a);
codec_wr_control(0x4b8, 0xffff, 0, 0xaaaa);
/*codec_wr_control(0x4bc, 0x7ff, 0, 0x1fe);*/
codec_wr_control(0x4bc, 0x7ff, 0, 0x2de);
codec_wr_control(0x4c0, 0xffff, 0, 0xc982);
codec_wr_control(0x258, 0xffff, 0, 0x9f9f);
}
if (agc_used) {
codec_wr_control(0x4d0, 0x3, 6, 0x3);
codec_wr_control(0x410, 0x3f, 8, 0x31);
codec_wr_control(0x410, 0xff, 0, 0x28);
codec_wr_control(0x414, 0x3f, 8, 0x31);
codec_wr_control(0x414, 0xff, 0, 0x28);
codec_wr_control(0x428, 0x7fff, 0, 0x24);
codec_wr_control(0x42c, 0x7fff, 0, 0x2);
codec_wr_control(0x430, 0x7fff, 0, 0x24);
codec_wr_control(0x434, 0x7fff, 0, 0x2);
codec_wr_control(0x438, 0x1f, 8, 0xf);
codec_wr_control(0x438, 0x1f, 0, 0xf);
codec_wr_control(0x44c, 0x7ff, 0, 0xfc);
codec_wr_control(0x450, 0xffff, 0, 0xabb3);
}
/*mute headphone pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
}
/*
* the system voice come out from speaker
* this function just used for the system voice(such as music and
* moive voice and so on).
*/
static int codec_pa_play_open(void)
{
int i = 0;
int reg_val = 0;
pr_debug("%s,line:%d\n", __func__, __LINE__);
if (codec_speakerout_en != 1)
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, 0);
if (drc_used) {
codec_wr_control(0x4d4, 0xffff, 0, 0x80);
codec_wr_control(0x210, 0x1, 6, 0x1);
codec_wr_control(0x214, 0x1, 6, 0x1);
codec_wr_control(0x480, 0x7, 0, 0x7);
}
/*enable AIF1 DAC Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0L_ENA, 0x1);
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0R_ENA, 0x1);
/*confige AIF1 DAC Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0L_SRC, 0);
/*confige AIF1 DAC Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0R_SRC, 0);
/*confige dac digital mixer source */
codec_wr_control(SUNXI_DAC_MXR_SRC, 0x1, DACL_MXR_SRC_AIF1DA0L, 0x1);
codec_wr_control(SUNXI_DAC_MXR_SRC, 0x1, DACR_MXR_SRC_AIF1DA0R, 0x1);
codec_wr_control(SUNXI_DAC_VOL_CTRL, 0xffff, 0, dac_vol_ctrl_spk);
/*enable dac digital*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0x1);
codec_wr_prcm_control(HP_VOLC, 0x1, PA_CLK_GC, 0x0);
pr_debug("%s,line:%d\n", __func__, __LINE__);
/*enable dac analog*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACAREN, 0x1);
if (!pa_double_used) {/*single speaker*/
/*
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
codec_wr_prcm_control(ROMIXSC, 0x1, LMIXMUTEDACR, 0x1);
*/
codec_wr_prcm_control(ROMIXSC, 0x7f, RMIXMUTE, 0x1);
codec_wr_prcm_control(LOMIXSC, 0x7f, LMIXMUTE, 0x1);
/*enable output mixer */
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x1);
/*pa input source select:l_mixer*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x1);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
/*unmute headphone pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x1);
} else {/*double speaker*/
if (codec_speakerout_en) {
/*right output mixer source : dacr*/
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEDACR, 0x1);
/*left output mixer source : dacl*/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
/*enable output mixer */
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x1);
codec_wr_prcm_control(PAEN_HP_CTRL,
0x1, LTRNMUTE, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL,
0x1, RTLNMUTE, 0x0);
/*pa input source select:r_mixer,l_mixer*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x1);
} else {
codec_wr_prcm_control(PAEN_HP_CTRL,
0x1, LTRNMUTE, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL,
0x1, RTLNMUTE, 0x0);
/*pa input source select:r_mixer,l_mixer*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
}
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x1);
}
if (play_running == 1) {/*used for change the path*/
pr_debug("%s,line:%d\n", __func__, __LINE__);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if (!reg_val) {
for (i = 0; i < pa_vol; i++) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if ((i%2 == 0))
usleep_range(1000, 2000);
}
}
usleep_range(2000, 3000);
gpio_set_value(spk_gpio, 1);
/*msleep(62);*/
}
return 0;
}
/*
* the system voice come out from headphone
* this function just used for the system voice(such as music and
* moive voice and so on).
*/
static int codec_headphone_play_open(void)
{
int i = 0;
int reg_val = 0;
/*close spk pa*/
gpio_set_value(spk_gpio, 0);
usleep_range(1000, 2000);
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x0);
/*mute headphone pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
if (codec_headphoneout_en != 1)
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, 0);
if (drc_used) {
codec_wr_control(0x4d4, 0xffff, 0, 0x0);
codec_wr_control(0x210, 0x1, 6, 0x0);
codec_wr_control(0x214, 0x1, 6, 0x0);
codec_wr_control(0x480, 0x7, 0, 0x0);
}
/*enable AIF1 DAC Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0L_ENA, 0x1);
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0R_ENA, 0x1);
/*confige AIF1 DAC Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0L_SRC, 0);
/*confige AIF1 DAC Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0R_SRC, 0);
/*confige dac digital mixer source */
codec_wr_control(SUNXI_DAC_MXR_SRC, 0x1, DACL_MXR_SRC_AIF1DA0L, 0x1);
codec_wr_control(SUNXI_DAC_MXR_SRC, 0x1, DACR_MXR_SRC_AIF1DA0R, 0x1);
codec_wr_control(SUNXI_DAC_VOL_CTRL,
0xffff, 0, dac_vol_ctrl_headphone);
/*enable dac digital*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0x1);
/*enable dac_l and dac_r*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACAREN, 0x1);
if (codec_headphoneout_en == 1) {
/*
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEDACR, 0x1);
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
*/
codec_wr_prcm_control(ROMIXSC, 0x7f, RMIXMUTE, 0x2);
codec_wr_prcm_control(LOMIXSC, 0x7f, LMIXMUTE, 0x2);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x1);
} else {
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
}
/*hpout negative mute*/
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
if (play_running == 1) {/*used for change the path*/
pr_debug("%s,line:%d\n", __func__, __LINE__);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x1);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if (!reg_val) {
for (i = 0; i < headphone_vol; i++) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if ((i%2 == 0))
usleep_range(1000, 2000);
}
}
}
return 0;
}
static int codec_earpiece_play_open(void)
{
gpio_set_value(spk_gpio, 0);
usleep_range(2000, 3000);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
/*enable AIF1 DAC Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0L_ENA, 0x1);
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0R_ENA, 0x1);
/*confige AIF1 DAC Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0L_SRC, 0);
/*confige AIF1 DAC Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0R_SRC, 0);
/*confige dac digital mixer source */
codec_wr_control(SUNXI_DAC_MXR_SRC, 0xf, DACL_MXR_SRC, 0x8);
codec_wr_control(SUNXI_DAC_MXR_SRC, 0xf, DACR_MXR_SRC, 0x8);
/*enable dac digital*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0x1);
/*0*/
codec_wr_prcm_control(HP_VOLC, 0x1, PA_CLK_GC, 0x0);
/*enable dac ananlog*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACAREN, 0x1);
/*left output negative*/
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x1);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
/*slect output mixer source*/
codec_wr_prcm_control(ROMIXSC, 0x7f, RMIXMUTE, 0x0);
codec_wr_prcm_control(LOMIXSC, 0x7f, LMIXMUTE, 0x3);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
return 0;
}
/*
* the system voice come out from headphone and speaker
* while the phone call in, the phone use the headset, you can hear the voice
* from speaker and headset. this function just used for the system voice
* (such as music and moive voice and so on).
*/
static int codec_pa_and_headset_play_open(void)
{
pr_debug("%s,line:%d\n", __func__, __LINE__);
/*mute hppa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
/*enable AIF1 DAC Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0L_ENA, 0x1);
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0R_ENA, 0x1);
/*confige AIF1 DAC Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0L_SRC, 0);
/*confige AIF1 DAC Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0R_SRC, 0);
codec_wr_control(SUNXI_DAC_VOL_CTRL, 0xffff, 0, dac_vol_ctrl_spk);
/*confige dac digital mixer source */
codec_wr_control(SUNXI_DAC_MXR_SRC, 0xf, DACL_MXR_SRC, 0x8);
codec_wr_control(SUNXI_DAC_MXR_SRC, 0xf, DACR_MXR_SRC, 0x8);
/*enable dac digital*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0x1);
/*enble dac analog*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACAREN, 0x1);
codec_wr_prcm_control(HP_VOLC, 0x1, PA_CLK_GC, 0x0);
if (!pa_double_used) {/*single speaker*/
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x1);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(ROMIXSC, 0x7f, RMIXMUTE, 0x0);
codec_wr_prcm_control(LOMIXSC, 0x7f, LMIXMUTE, 0x3);
/*unmute headphone pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
} else {/*double speaker*/
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x1);
/*right output mixer source : dacr*/
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEDACR, 0x1);
/*left output mixer source : dacl*/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x1);
}
return 0;
}
static int codec_system_btout_open(void)
{
/*config clk fmt*/
/*config aif2 from pll2*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x1, AIF2CLK_ENA, 0x1);
/*aif2 clk source select*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x3, AIF2CLK_SRC, 0x3);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF2_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF2_MOD_RST_CTL, 0x1);
/*config aif2 fmt :pcm mono 16 lrck=8k,blck/lrck = 64*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_MSTR_MOD, 0x0);/*master*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_BCLK_INV, 0x0);/*bclk:normal*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_LRCK_INV, 0x0);/*lrck:normal*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0xf, AIF2_BCLK_DIV, 0x9);/*aif2/bclk=48*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x7, AIF2_LRCK_DIV, 0x2);/*bclk/lrck=64*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x3, AIF2_WORD_SIZ, 0x1);/*sr=16*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x3, AIF2_DATA_FMT, 0x3);/*dsp mode*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_MONO_PCM, 1);/*dsp mode*/
/*aif3 mode enable*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF3_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF3_MOD_RST_CTL, 0x1);
/*config aif3: clk ,fmt*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL, 0x1, AIF3_BCLK_INV, 0x0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL, 0x1, AIF3_LRCK_INV, 0x0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL,
0x3, AIF3_WORD_SIZ, 0x1);/*sr = 16*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL,
0x3, AIF3_CLOC_SRC, 0x1);/*clk form aif2*/
/*enable AIF1 DAC Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0L_ENA, 0x1);
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0R_ENA, 0x1);
/*confige AIF1 DAC Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0R_SRC, 3);
codec_wr_control(SUNXI_AIF2_MXR_SRC,
0x1, AIF2_ADCR_MXR_SRC_AIF1DA0R, 1);
/*enable aif2 adcl channel*/
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL, 0x1, AIF2_ADCR_EN, 1);
/*select aif3 pcm output source*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF3_ADC_SRC, 2);
return 0;
}
static int codec_system_bt_buttonvoice_open(void)
{
/*enable AIF1 DAC Timeslot0 channel enable*/
/*codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0L_ENA, 0x1);*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x1, AIF1_DA0R_ENA, 0x1);
/*confige AIF1 DAC Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0x3, AIF1_DA0R_SRC, 0);
codec_wr_control(SUNXI_AIF2_MXR_SRC,
0x1, AIF2_ADCR_MXR_SRC_AIF1DA0R, 1);
return 0;
}
/*
* use for the base system record(for pad record).
*/
static int codec_capture_open(void)
{
if (agc_used) {
codec_wr_control(0x210, 0x1, 7, 0x1);
codec_wr_control(0x214, 0x1, 7, 0x1);
codec_wr_control(0x408, 0xf, 0, 0x6);
codec_wr_control(0x408, 0x7, 12, 0x7);
codec_wr_control(0x40c, 0xf, 0, 0x6);
codec_wr_control(0x40c, 0x7, 12, 0x7);
}
/*enable AIF1 adc Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x1, AIF1_AD0L_ENA, 0x1);
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x1, AIF1_AD0R_ENA, 0x1);
/*confige AIF1 adc Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x3, AIF1_AD0L_SRC, 0);
/*confige AIF1 adc Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x3, AIF1_AD0R_SRC, 0);
/*confige AIF1 Digital Mixer Source*/
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0x1, AIF1_AD0L_MXL_SRC_ADCL, 0x1);
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0x1, AIF1_AD0R_MXR_SRC_ADCR, 0x1);
/*just for test*/
/*
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0xf, AIF1_AD0L_MXL_SRC, 0x8);
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0xf, AIF1_AD0R_MXR_SRC, 0x8);
*/
/*enable ADC Digital part*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 1);
/*enable Digital microphone*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENDM, 0);
/*ADC Delay Time*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x3, ADOUT_DTS, 3);
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ADOUT_DLY, 1);
/*enable adc_r adc_l analog*/
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCREN, 0x1);
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x1);
if (!codec_analog_headsetmic_en) {
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTEMIC2BOOST, 0x0);
codec_wr_prcm_control(RADCMIXSC,
0x1, RADCMIXMUTEMIC2BOOST, 0x0);
codec_wr_prcm_control(MIC2G_LINEEN_CTRL, 0x1, MIC2AMPEN, 0x0);
/*enable Left MIC1 Boost stage*/
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTEMIC1BOOST, 0x1);
/*enable Right MIC1 Boost stage*/
codec_wr_prcm_control(RADCMIXSC,
0x1, RADCMIXMUTEMIC1BOOST, 0x1);
/*enable mic1 pa*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC1AMPEN, 0x1);
/*mic1 gain 36dB,if capture volume is too small,
* enlarge the mic1boost
*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL,
0x7, MIC1BOOST, cap_vol);
/*enable Master microphone bias*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL,
0x1, MMICBIASEN, 0x1);
} else {
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTEMIC1BOOST, 0x0);
codec_wr_prcm_control(RADCMIXSC,
0x1, RADCMIXMUTEMIC1BOOST, 0x0);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL,
0x1, MIC1AMPEN, 0x0);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL,
0x1, MMICBIASEN, 0x0);
/*enable Left MIC2 Boost stage*/
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTEMIC2BOOST, 0x1);
/*enable Right MIC2 Boost stage*/
codec_wr_prcm_control(RADCMIXSC,
0x1, RADCMIXMUTEMIC2BOOST, 0x1);
/*enable mic2 pa*/
codec_wr_prcm_control(MIC2G_LINEEN_CTRL,
0x1, MIC2AMPEN, 0x1);
/*mic2 gain 36dB,if capture volume is too small,
*enlarge the mic2boost
*/
codec_wr_prcm_control(MIC2G_LINEEN_CTRL,
0x7, MIC2BOOST, cap_vol);
}
cap_running = 1;
/*hardware fifo delay*/
msleep(200);
return 0;
}
/*
* codec_speaker_headset_earpiece_en == 3,
* earpiece is open,speaker and headphone is close.
* codec_speaker_headset_earpiece_en == 2,
* speaker is open, headphone is open.
* codec_speaker_headset_earpiece_en == 1,
* speaker is open, headphone is close.
* codec_speaker_headset_earpiece_en == 0,
* speaker is closed, headphone is open.
* this function just used for the system voice
* (such as music and moive voice and so on),
* no the phone call.
*/
static int codec_set_spk_headset_earpiece(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int ret = 0;
codec_speaker_headset_earpiece_en = ucontrol->value.integer.value[0];
if (codec_speaker_headset_earpiece_en == 1) {
ret = codec_pa_play_open();
} else if (codec_speaker_headset_earpiece_en == 0) {
ret = codec_headphone_play_open();
} else if (codec_speaker_headset_earpiece_en == 2) {
ret = codec_pa_and_headset_play_open();
} else if (codec_speaker_headset_earpiece_en == 3) {
ret = codec_earpiece_play_open();
} else if (codec_speaker_headset_earpiece_en == 4) {
pr_debug("%s,line:%d\n", __func__, __LINE__);
ret = codec_system_btout_open();
} else if (codec_speaker_headset_earpiece_en == 5) {
ret = codec_system_bt_buttonvoice_open();
}
return 0;
}
static int codec_get_spk_headset_earpiece(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_speaker_headset_earpiece_en;
return 0;
}
static int sndpcm_unmute(struct snd_soc_dai *dai, int mute)
{
if (current_running == 0) {/*play stream*/
int i = 0;
int reg_val = 0;
if (codec_analog_phonein_en) {
msleep(20);
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEDACL, 0x1);
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
}
if (mute == 0) {
switch (codec_speaker_headset_earpiece_en) {
case 0:
codec_wr_prcm_control(DAC_PA_SRC,
0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC,
0x1, RHPPAMUTE, 0x1);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if (reg_val)
break;
for (i = 0; i < headphone_vol; i++) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC,
0x3f, HPVOL, i);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if ((i%2 == 0))
usleep_range(1000, 2000);
}
break;
case 1:
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if (reg_val)
goto set_gpio1;
for (i = 0; i < pa_vol; i++) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC,
0x3f, HPVOL, i);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if ((i%2 == 0))
usleep_range(1000, 2000);
}
set_gpio1:
usleep_range(2000, 3000);
gpio_set_value(spk_gpio, 1);
msleep(62);
break;
case 2:
if (!pa_double_used) {/*single speaker*/
codec_wr_prcm_control(DAC_PA_SRC,
0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC,
0x1, RHPPAMUTE, 0x0);
} else {/*double speaker*/
codec_wr_prcm_control(DAC_PA_SRC,
0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC,
0x1, RHPPAMUTE, 0x1);
}
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if (reg_val)
goto set_hpvol2;
for (i = 0; i < pa_vol; i++) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC,
0x3f, HPVOL, i);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if ((i%2 == 0))
usleep_range(1000, 2000);
}
set_hpvol2:
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC,
0x3f, HPVOL, pa_vol);
usleep_range(2000, 3000);
gpio_set_value(spk_gpio, 1);
msleep(62);
break;
case 3:
codec_wr_prcm_control(DAC_PA_SRC,
0x1, LHPPAMUTE, 0x1);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if (reg_val)
goto set_hpvol3;
for (i = 0; i < earpiece_vol; i++) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC,
0x3f, HPVOL, i);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if ((i%2 == 0))
usleep_range(1000, 2000);
}
set_hpvol3:
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC,
0x3f, HPVOL, earpiece_vol);
break;
default:
break;
}
} else {
}
}
return 0;
}
static int sndpcm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
/*enble aif1 clk */
codec_wr_control(SUNXI_SYSCLK_CTL , 0x1, AIF1CLK_ENA, 0x1);
/*enble sys clk */
codec_wr_control(SUNXI_SYSCLK_CTL , 0x1, SYSCLK_ENA, 0x1);
/**for test loopback******/
/*codec_wr_control(SUNXI_DA_CTL , 0x1, 3, 0x1);*/
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
play_running = 1;
current_running = 0;
/*enable module AIF1,DAC*/
codec_wr_control(SUNXI_MOD_CLK_ENA ,
0x1, AIF1_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA ,
0x1, DAC_DIGITAL_MOD_CLK_EN, 0x1);
/*reset module AIF1, DAC*/
codec_wr_control(SUNXI_MOD_RST_CTL ,
0x1, AIF1_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL ,
0x1, DAC_DIGITAL_MOD_RST_CTL, 0x1);
} else {
current_running = 1;
/*enable module AIF1,ADC*/
codec_wr_control(SUNXI_MOD_CLK_ENA ,
0x1, AIF1_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA ,
0x1, ADC_DIGITAL_MOD_CLK_EN, 0x1);
/*reset module AIF1, ADC*/
codec_wr_control(SUNXI_MOD_RST_CTL ,
0x1, AIF1_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL ,
0x1, ADC_DIGITAL_MOD_RST_CTL, 0x1);
}
/*global enable*/
codec_wr_control(SUNXI_DA_CTL , 0x1, GEN, 0x1);
return 0;
}
static void sndpcm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int i = 0;
int cur_vol = 0;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x0);
cur_vol = read_prcm_wvalue(HP_VOLC);
cur_vol &= 0x3f;
if (!(codec_analog_mainmic_en ||
codec_analog_headsetmic_en ||
codec_digital_headsetmic_en ||
codec_digital_mainmic_en ||
codec_analog_btmic_en ||
codec_digital_btmic_en)) {
gpio_set_value(spk_gpio, 0);
if (cur_vol > 48) {
for (i = cur_vol; i > 52; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC,
0x3f, HPVOL, i);
usleep_range(9000, 10000);
}
for (i = 52; i > 48; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC,
0x3f, HPVOL, i);
usleep_range(6000, 7000);
}
}
for (i = 48; i > 32; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
usleep_range(1000, 2000);
}
for (i = 32; i > 0; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
usleep_range(100, 200);
}
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, 0);
/*disable analog part*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
/*by xzd left select dacl&dacr*/
codec_wr_prcm_control(ROMIXSC, 0x7f, RMIXMUTE, 0x0);
/*by xzd right don't select src*/
codec_wr_prcm_control(LOMIXSC, 0x7f, LMIXMUTE, 0x0);
/*enable output mixer */
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL,
0x1, LTRNMUTE, 0x0);
/*by xzd only right negative left*/
codec_wr_prcm_control(PAEN_HP_CTRL,
0x1, RTLNMUTE, 0x0);
/*enable dac analog*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACAREN, 0x0);
/*disable dac digital*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0x0);
codec_wr_prcm_control(HP_VOLC, 0x1, PA_CLK_GC, 0x0);
/*confige dac digital mixer source */
codec_wr_control(SUNXI_DAC_MXR_SRC,
0xf, DACL_MXR_SRC, 0x0);
codec_wr_control(SUNXI_DAC_MXR_SRC,
0xf, DACR_MXR_SRC, 0x0);
/*confige AIF1 DAC Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL,
0x3, AIF1_DA0L_SRC, 0);
/*confige AIF1 DAC Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL,
0x3, AIF1_DA0R_SRC, 0);
/*disable AIF1 DAC Timeslot0 channel*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL,
0x1, AIF1_DA0L_ENA, 0x0);
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL,
0x1, AIF1_DA0R_ENA, 0x0);
/*diable clk parts*/
if (1 == cap_running) {
pr_debug("capture is running!!!!\n");
} else {
/*disable aif1clk*/
codec_wr_control(SUNXI_SYSCLK_CTL,
0x1, AIF1CLK_ENA, 0);
/*disble sys clk */
codec_wr_control(SUNXI_SYSCLK_CTL,
0x1, SYSCLK_ENA, 0);
/*disable module AIF1*/
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, AIF1_MOD_CLK_EN, 0x0);
/*reset module AIF1*/
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, AIF1_MOD_RST_CTL, 0x0);
/*global disable*/
codec_wr_control(SUNXI_DA_CTL,
0x1, GEN, 0x0);
}
/*disable module DAC*/
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, DAC_DIGITAL_MOD_CLK_EN, 0x0);
/*reset module DAC*/
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, DAC_DIGITAL_MOD_RST_CTL, 0x0);
/* I2S0 TX DISABLE */
codec_wr_control(SUNXI_DA_CTL, 0x1, TXEN, 0);
/*SDO ON*/
codec_wr_control(SUNXI_DA_CTL, 0x1, SDO_EN, 0);
}
if (drc_used) {
codec_wr_control(0x4d4, 0xffff, 0, 0x0);
codec_wr_control(0x210, 0x1, 6, 0x0);
codec_wr_control(0x214, 0x1, 6, 0x0);
codec_wr_control(0x480, 0x7, 0, 0x0);
}
play_running = 0;
} else {
if (!(codec_analog_mainmic_en ||
codec_analog_headsetmic_en ||
codec_digital_headsetmic_en ||
codec_digital_mainmic_en ||
codec_analog_btmic_en ||
codec_digital_btmic_en)) {
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL,
0x1, MIC1AMPEN, 0x0);
/*disable Master microphone bias*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL,
0x1, MMICBIASEN, 0x0);
codec_wr_prcm_control(MIC2G_LINEEN_CTRL,
0x1, MIC2AMPEN, 0x0);
/*disable Left MIC1 Boost stage*/
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTEMIC1BOOST, 0x0);
/*disable Right MIC1 Boost stage*/
codec_wr_prcm_control(RADCMIXSC,
0x1, RADCMIXMUTEMIC1BOOST, 0x0);
/*disable Left MIC1 Boost stage*/
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTEMIC2BOOST, 0x0);
/*disable Right MIC1 Boost stage*/
codec_wr_prcm_control(RADCMIXSC,
0x1, RADCMIXMUTEMIC2BOOST, 0x0);
/*disable PHONEP-PHONEN Boost stage*/
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTEPHONEPN, 0x0);
/*disable PHONEP-PHONEN Boost stage*/
codec_wr_prcm_control(RADCMIXSC,
0x1, RADCMIXMUTEPHONEPN, 0x0);
/*disable LINEINL ADC*/
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTELINEINL, 0x0);
/*disable LINEINR ADC*/
codec_wr_prcm_control(RADCMIXSC,
0x1, RADCMIXMUTELINEINR, 0x0);
/*disable adc_r adc_l analog*/
codec_wr_prcm_control(ADC_AP_EN,
0x1, ADCREN, 0x0);
codec_wr_prcm_control(ADC_AP_EN,
0x1, ADCLEN, 0x0);
/*disable AIF1 adc Timeslot0 channel*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL,
0x1, AIF1_AD0L_ENA, 0x0);
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL,
0x1, AIF1_AD0R_ENA, 0x0);
/*confige AIF1 Digital Mixer Source*/
codec_wr_control(SUNXI_AIF1_MXR_SRC,
0xf, AIF1_AD0L_MXL_SRC, 0x0);
codec_wr_control(SUNXI_AIF1_MXR_SRC,
0xf, AIF1_AD0R_MXR_SRC, 0x0);
/*disable ADC Digital part*/
codec_wr_control(SUNXI_ADC_DIG_CTRL,
0x1, ENAD, 0);
/*diable clk parts*/
if (play_running == 1) {
pr_debug("playback is running!!!!\n");
} else {
/*disable aif1clk*/
codec_wr_control(SUNXI_SYSCLK_CTL,
0x1, AIF1CLK_ENA, 0);
/*disble sys clk */
codec_wr_control(SUNXI_SYSCLK_CTL,
0x1, SYSCLK_ENA, 0);
/*disable module AIF1*/
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, AIF1_MOD_CLK_EN, 0x0);
/*reset module AIF1*/
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, AIF1_MOD_RST_CTL, 0x0);
/*global disable*/
codec_wr_control(SUNXI_DA_CTL,
0x1, GEN, 0x0);
}
/*disable module ADC*/
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, ADC_DIGITAL_MOD_CLK_EN, 0x0);
/*reset module ADC*/
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, ADC_DIGITAL_MOD_RST_CTL, 0x0);
}
/* I2S0 RX DISABLE */
codec_wr_control(SUNXI_DA_CTL , 0x1, RXEN, 0);
if (agc_used) {
codec_wr_control(0x210, 0x1, 7, 0x0);
codec_wr_control(0x214, 0x1, 7, 0x0);
codec_wr_control(0x408, 0xf, 0, 0x0);
codec_wr_control(0x408, 0x7, 12, 0x0);
codec_wr_control(0x40c, 0xf, 0, 0x0);
codec_wr_control(0x40c, 0x7, 12, 0x0);
}
cap_running = 0;
}
}
static int sndpcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
return 0;
}
/*
* phone record from main mic + phone in(digital bb) .
* or
* phone record from sub mic + phone in(digital bb) .
* mic1 uses as main mic. mic2 uses as sub mic
*/
static int codec_digital_voice_mic_bb_capture_open(void)
{
/*select phonein source */
codec_wr_control(SUNXI_AIF1_MXR_SRC,
0x1, AIF1_AD0L_MXL_SRC_AIF2DACL, 1);
/*select mic source*/
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0x1, AIF1_AD0L_MXL_SRC_ADCL, 1);
/*aif1 adc left channel source select */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x3, AIF1_AD0L_SRC, 0);
/*aif1 adc left channel enable */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x1, AIF1_AD0L_ENA, 1);
msleep(200);
return 0;
}
static int codec_digital_voice_bb_bt_capture_open(void)
{
/*select AIF1 input mixer source */
codec_wr_control(SUNXI_AIF1_MXR_SRC,
0x1, AIF1_AD0L_MXL_SRC_AIF2DACR, 1);
codec_wr_control(SUNXI_AIF1_MXR_SRC,
0x1, AIF1_AD0L_MXL_SRC_AIF2DACL, 1);
/*AIF1 ADC Timeslot0 left channel data source select*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x3, AIF1_AD0L_SRC, 0);
/*open ADC channel slot0 switch*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x1, AIF1_AD0L_ENA, 1);
msleep(200);
return 0;
}
static int codec_analog_voice_bb_bt_capture_open(void)
{
/*select AIF1 input mixer source */
codec_wr_control(SUNXI_AIF1_MXR_SRC,
0x1, AIF1_AD0R_MXR_SRC_AIF2DACL, 1);
codec_wr_control(SUNXI_AIF1_MXR_SRC,
0x1, AIF1_AD0R_MXR_SRC_ADCR, 1);
/*AIF1 ADC Timeslot0 left channel data source select*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL,
0x3, AIF1_AD0L_SRC, 1);
/*open ADC channel slot0 switch*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL,
0x1, AIF1_AD0L_ENA, 1);
msleep(200);
return 0;
}
/*
* use for phone record from main/headset mic + phone in.
* .
*/
static int codec_analog_voice_capture_open(void)
{
if (codec_analog_mainmic_en) {
/*select mic source*/
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTEMIC1BOOST, 0x1);
} else {
/*select mic2 source*/
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTEMIC2BOOST, 0x1);
}
/*select phonein source*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEPHONEPN, 0x1);
/*enable adc analog*/
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x1);
/*enable dac digital*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 1);
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENDM, 0);
/*select aif1 adc left channel mixer source */
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0x1, AIF1_AD0L_MXL_SRC_ADCL, 1);
/*select aif1 adc left channel source */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x3, AIF1_AD0L_SRC, 0);
/*enable aif1 adc left channel */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x1, AIF1_AD0L_ENA, 1);
msleep(200);
return 0;
}
/*
* use for the line_in record
*/
static int codec_voice_linein_capture_open(void)
{
/*enable AIF1 adc Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x1, AIF1_AD0L_ENA, 0x1);
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x1, AIF1_AD0R_ENA, 0x1);
/*confige AIF1 adc Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x3, AIF1_AD0L_SRC, 0);
/*confige AIF1 adc Timeslot0 right channel data source*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x3, AIF1_AD0R_SRC, 0);
/*confige AIF1 Digital Mixer Source*/
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0xf, AIF1_AD0L_MXL_SRC, 0x2);
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0xf, AIF1_AD0R_MXR_SRC, 0x2);
/*enable dac digital*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 1);
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENDM, 0);
/*enable adc_r adc_l analog*/
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCREN, 0x1);
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x1);
/*enable Right MIC2 Boost stage*/
codec_wr_prcm_control(RADCMIXSC, 0x1, RADCMIXMUTELINEINR, 0x1);
/*enable Left MIC2 Boost stage*/
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTELINEINL, 0x1);
msleep(200);
return 0;
}
static int codec_system_bt_capture_open(void)
{
/*config clk fmt*/
/*config aif2 from pll2*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x1, AIF2CLK_ENA, 0x1);
/*aif2 clk source select*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x3, AIF2CLK_SRC, 0x3);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF2_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF2_MOD_RST_CTL, 0x1);
/*config aif2 fmt :pcm mono 16 lrck=8k,blck/lrck = 64*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_MSTR_MOD, 0x0);/*master*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_BCLK_INV, 0x0);/*bclk:normal*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_LRCK_INV, 0x0);/*lrck:normal*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0xf, AIF2_BCLK_DIV, 0x9);/*aif2/bclk=48*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x7, AIF2_LRCK_DIV, 0x2);/*bclk/lrck=64*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x3, AIF2_WORD_SIZ, 0x1);/*sr=16*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x3, AIF2_DATA_FMT, 0x3);/*dsp mode*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_MONO_PCM, 1);/*dsp mode*/
/*aif3 mode enable*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF3_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL, 0x1, AIF3_MOD_RST_CTL, 0x1);
/*config aif3: clk ,fmt*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL, 0x1, AIF3_BCLK_INV, 0x0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL, 0x1, AIF3_LRCK_INV, 0x0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL,
0x3, AIF3_WORD_SIZ, 0x1);/*sr = 16*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL,
0x3, AIF3_CLOC_SRC, 0x1);/*clk form aif2*/
/*select aif2 dac input source*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF2_DAC_SRC, 1);
/*aif2 adc right channel enable*/
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL, 0x1, AIF2_ADCR_EN, 1);
/*aif2 adc right channel enable*/
/*codec_wr_control(SUNXI_AIF2_MXR_SRC,
*0x1, AIF2_ADCL_MXR_SRC_AIF2DACR,1);
*/
/*select bt source*/
codec_wr_control(SUNXI_AIF1_MXR_SRC,
0x1, AIF1_AD0L_MXL_SRC_AIF2DACL, 1);
/*aif1 adc left channel source select */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x3, AIF1_AD0L_SRC, 0);
/*aif1 adc left channel enable */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0x1, AIF1_AD0L_ENA, 1);
pr_debug("%s,line:%d,SUNXI_AIF3_DACDAT_CTRL:%x\n", __func__, __LINE__,
codec_rdreg(SUNXI_AIF3_DACDAT_CTRL));
return 0;
}
static int sndpcm_perpare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int play_ret = 0, capture_ret = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
pr_debug("%s,line:%d\n", __func__, __LINE__);
/*confige the sample for adc,dac*/
switch (substream->runtime->rate) {
case 8000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x0);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x0);
break;
case 11025:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x1);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x1);
break;
case 12000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x2);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x2);
break;
case 16000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x3);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x3);
break;
case 22050:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x4);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x4);
break;
case 24000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x5);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x5);
break;
case 32000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x6);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x6);
break;
case 44100:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x7);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x7);
break;
case 48000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x8);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x8);
break;
case 96000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0x9);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0x9);
break;
case 192000:
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF1_FS, 0xa);
codec_wr_control(SUNXI_SYS_SR_CTRL , 0xf, AIF2_FS, 0xa);
break;
default:
pr_err("AUDIO SAMPLE:no suitable sampling rate!!!\n");
break;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (!(codec_analog_mainmic_en ||
codec_analog_headsetmic_en ||
codec_digital_headsetmic_en ||
codec_digital_mainmic_en ||
codec_analog_btmic_en ||
codec_digital_btmic_en)) {
if (codec_speaker_headset_earpiece_en == 2)
play_ret = codec_pa_and_headset_play_open();
else if (codec_speaker_headset_earpiece_en == 1)
play_ret = codec_pa_play_open();
else if (codec_speaker_headset_earpiece_en == 3)
play_ret = codec_earpiece_play_open();
else if (codec_speaker_headset_earpiece_en == 0)
play_ret = codec_headphone_play_open();
else if (codec_speaker_headset_earpiece_en == 4)
play_ret = codec_system_btout_open();
play_running = 1;
}
/*play_running = 1;*/
return play_ret;
} else {
if (runtime->status->state == SNDRV_PCM_STATE_XRUN) {
pr_err("%s,-------capture:XRUN---------line:%d\n",
__func__, __LINE__);
}
if (codec_voice_record_en &&
(codec_digital_mainmic_en ||
codec_digital_headsetmic_en)) {
capture_ret =
codec_digital_voice_mic_bb_capture_open();
} else if (codec_voice_record_en && codec_digital_btmic_en) {
pr_debug("%s,line:%d\n", __func__, __LINE__);
capture_ret = codec_digital_voice_bb_bt_capture_open();
} else if (codec_voice_record_en && codec_analog_btmic_en) {
capture_ret = codec_analog_voice_bb_bt_capture_open();
} else if (codec_voice_record_en &&
(codec_analog_mainmic_en ||
codec_analog_headsetmic_en)) {
capture_ret = codec_analog_voice_capture_open();
} else if (codec_lineinin_en && codec_lineincap_en) {
capture_ret = codec_voice_linein_capture_open();
} else if (codec_voice_record_en &&
codec_system_bt_capture_en) {
pr_debug("%s,line:%d\n", __func__, __LINE__);
capture_ret = codec_system_bt_capture_open();
} else if (!codec_voice_record_en) {
capture_ret = codec_capture_open();
}
return capture_ret;
}
}
int sunxi_i2s_set_rate(int freq)
{
if (clk_set_rate(codec_pll2clk, freq))
pr_err("set codec_pll2clk rate fail\n");
if (clk_set_rate(codec_moduleclk, freq))
pr_err("set codec_moduleclk rate fail\n");
return 0;
}
EXPORT_SYMBOL(sunxi_i2s_set_rate);
static const char * const spk_headset_earpiece_function[] = {"headset", "spk",
"spk_headset", "earpiece", "btout", "bt_button_voice"};
static const struct soc_enum spk_headset_earpiece_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_headset_earpiece_function),
spk_headset_earpiece_function),
};
/*
* codec_analog_phoneout_en == 1,
* the phone out is open. receiver can hear the voice which you say.
* codec_analog_phoneout_en == 0,
* the phone out is close.
*/
static int codec_analog_set_phoneout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_analog_phoneout_en = ucontrol->value.integer.value[0];
if (codec_analog_phoneout_en)
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUT_EN, 0x1);
else
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUT_EN, 0x0);
return 0;
}
static int codec_analog_get_phoneout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_analog_phoneout_en;
return 0;
}
/*
* codec_analog_phonein_en == 1, the phone in is open.
* while you open one of the device(speaker,earpiece,headphone).
* you can hear the caller's voice.
* codec_analog_phonein_en == 0. the phone in is close.
*/
static int codec_analog_set_phonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_analog_phonein_en = ucontrol->value.integer.value[0];
if (codec_analog_phonein_en) {
pr_debug("%s,line:%d\n", __func__, __LINE__);
/*enable AIF1 DAC Timeslot0 channel enable*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL,
0x1, AIF1_DA0L_ENA, 0x1);
/*confige AIF1 DAC Timeslot0 left channel data source*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL,
0x3, AIF1_DA0L_SRC, 0);
/*confige dac digital mixer source */
codec_wr_control(SUNXI_DAC_MXR_SRC,
0x1, DACL_MXR_SRC_AIF1DA0L, 0x1);
/*enable dac digital*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0x1);
/*enable dac_l and dac_r*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x1);
/*
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEDACL, 0x1);
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
*/
/*select PHONEP-PHONEN*/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEPHONEPN, 0x1);
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEPHONEPN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x1);
} else {
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEPHONEPN, 0x0);
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEPHONEPN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x0);
}
return 0;
}
static int codec_analog_get_phonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_analog_phonein_en;
return 0;
}
/*
* codec_earpieceout_en == 1, open the earpiece.
* codec_earpieceout_en == 0, close the earpiece.
*/
static int codec_set_earpieceout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int i = 0;
int reg_val = 0;
codec_earpieceout_en = ucontrol->value.integer.value[0];
if (codec_earpieceout_en) {
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
gpio_set_value(spk_gpio, 0);
/*mute l_pa and r_pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x0);
/*open earpiece out routeway*/
/*unmute l_pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
/*select the analog mixer input source*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x1);
/*select HPL inverting output*/
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x1);
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x1);
for (i = 0; i < headphone_vol; i++) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
reg_val = read_prcm_wvalue(HP_VOLC);
reg_val &= 0x3f;
if ((i%2 == 0))
usleep_range(1000, 2000);
}
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, earpiece_vol);
codec_speakerout_en = 0;
codec_headphoneout_en = 0;
} else {
/*mute l_pa and r_pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
if (headphone_direct_used) {
codec_wr_prcm_control(PAEN_HP_CTRL,
0x3, HPCOM_FC, 0x3);
codec_wr_prcm_control(PAEN_HP_CTRL,
0x1, COMPTEN, 0x1);
} else {
codec_wr_prcm_control(PAEN_HP_CTRL,
0x3, HPCOM_FC, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL,
0x1, COMPTEN, 0x0);
}
/*codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x0);*/
codec_wr_prcm_control(ADDA_APT2,
0x1, ZERO_CROSS_EN, 0x0);
}
return 0;
}
static int codec_get_earpieceout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_earpieceout_en;
return 0;
}
/*
* codec_speakerout_en == 1, open the speaker.
* codec_speakerout_en == 0, close the speaker.
*/
static int codec_set_speakerout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_speakerout_en = ucontrol->value.integer.value[0];
if (codec_speakerout_en) {
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x0);
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, pa_vol);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x1);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
if (headphone_direct_used) {
codec_wr_prcm_control(PAEN_HP_CTRL,
0x3, HPCOM_FC, 0x3);
codec_wr_prcm_control(PAEN_HP_CTRL,
0x1, COMPTEN, 0x1);
} else {
codec_wr_prcm_control(PAEN_HP_CTRL,
0x3, HPCOM_FC, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL,
0x1, COMPTEN, 0x0);
}
usleep_range(2000, 3000);
gpio_set_value(spk_gpio, 1);
msleep(62);
codec_headphoneout_en = 0;
codec_earpieceout_en = 0;
} else {
gpio_set_value(spk_gpio, 0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0);
}
return 0;
}
static int codec_get_speakerout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_speakerout_en;
return 0;
}
/*
* codec_headphoneout_en == 1, open the headphone.
* codec_headphoneout_en == 0, close the headphone.
*/
static int codec_set_headphoneout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_headphoneout_en = ucontrol->value.integer.value[0];
if (codec_headphoneout_en) {
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
gpio_set_value(spk_gpio, 0);
/*msleep(62);*/
if (headphone_direct_used) {
codec_wr_prcm_control(PAEN_HP_CTRL,
0x3, HPCOM_FC, 0x3);
codec_wr_prcm_control(PAEN_HP_CTRL,
0x1, COMPTEN, 0x1);
} else {
codec_wr_prcm_control(PAEN_HP_CTRL,
0x3, HPCOM_FC, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL,
0x1, COMPTEN, 0x0);
}
/*select HPL inverting output*/
/*codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x0);*/
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, LTRNMUTE, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, RTLNMUTE, 0x0);
/*select the analog mixer input source*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x1);
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x1);
/*unmute l_pa and r_pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x1);
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, headphone_vol);
codec_speakerout_en = 0;
codec_earpieceout_en = 0;
} else {
/*mute l_pa and r_pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
/*select the default dac input source*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPIS, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPIS, 0x0);
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x0);
}
return 0;
}
static int codec_get_headphoneout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_headphoneout_en;
return 0;
}
/*
* codec_analog_mainmic_en == 1, open mic1.
* codec_analog_mainmic_en == 0, close mic1.
*/
static int codec_analog_set_mainmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_analog_mainmic_en = ucontrol->value.integer.value[0];
if (codec_analog_mainmic_en) {
/*close headset mic(mic2) routeway*/
codec_wr_prcm_control(MIC2G_LINEEN_CTRL, 0x1, MIC2AMPEN, 0x0);
codec_wr_prcm_control(PHONEOUT_CTRL, 0xf, PHONEOUTS0, 0x0);
/*open main mic(mic1) routeway*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC1AMPEN, 0x1);
/*mic1 gain 36dB,if capture volume is too small,
*enlarge the mic1boost
*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL,
0x7, MIC1BOOST, phone_main_mic_vol);
/*enable Master microphone bias*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL,
0x1, MMICBIASEN, 0x1);
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUTS3, 0x1);
codec_analog_headsetmic_en = 0;
} else {
/*disable mic pa*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC1AMPEN, 0x0);
/*disable Master microphone bias*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL,
0x1, MMICBIASEN, 0x0);
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUTS3, 0x0);
}
return 0;
}
static int codec_analog_get_mainmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_analog_mainmic_en;
return 0;
}
/*
* codec_analog_headsetmic_en == 1, open mic2.
* codec_analog_headsetmic_en == 0, close mic2.
*/
static int codec_analog_set_headsetmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_analog_headsetmic_en = ucontrol->value.integer.value[0];
if (codec_analog_headsetmic_en) {
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC1AMPEN, 0x0);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL,
0x1, MMICBIASEN, 0x0);
codec_wr_prcm_control(PHONEOUT_CTRL, 0xf, PHONEOUTS0, 0x0);
/*open headset mic(mic2) routeway*/
/*enable mic2 pa*/
codec_wr_prcm_control(MIC2G_LINEEN_CTRL, 0x1, MIC2AMPEN, 0x1);
/*mic2 gain 36dB,if capture volume is too small,
*enlarge the mic2boost
*/
codec_wr_prcm_control(MIC2G_LINEEN_CTRL,
0x7, MIC2BOOST, phone_headset_mic_vol);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC2_SS, 0x1);
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUTS2, 0x1);
codec_analog_mainmic_en = 0;
} else {
codec_wr_prcm_control(MIC2G_LINEEN_CTRL, 0x1, MIC2AMPEN, 0x0);
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUTS2, 0x0);
}
return 0;
}
static int codec_analog_get_headsetmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_analog_headsetmic_en;
return 0;
}
/*
* codec_voice_record_en == 1, set status.
* codec_voice_record_en == 0, set status.
*/
static int codec_set_voicerecord(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_voice_record_en = ucontrol->value.integer.value[0];
return 0;
}
static int codec_get_voicerecord(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_voice_record_en;
return 0;
}
/*
* close all phone routeway
*/
static int codec_set_endcall(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int cur_vol = 0;
int i = 0;
gpio_set_value(spk_gpio, 0);
msleep(50);
cur_vol = read_prcm_wvalue(HP_VOLC);
cur_vol &= 0x3f;
if (cur_vol > 48) {
for (i = cur_vol; i > 52; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
usleep_range(9000, 10000);
}
for (i = 52; i > 48; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
usleep_range(6000, 7000);
}
}
for (i = 48; i > 32; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
usleep_range(1000, 2000);
}
for (i = 32; i > 0; i--) {
/*set HPVOL volume*/
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, i);
usleep_range(100, 200);
}
codec_wr_prcm_control(HP_VOLC, 0x3f, HPVOL, 0);
/*close analog parts*/
codec_wr_prcm_control(DAC_PA_SRC, 0xff, LHPIS, 0x0);
codec_wr_prcm_control(LOMIXSC, 0xff, LMIXMUTE, 0x0);
codec_wr_prcm_control(ROMIXSC, 0xff, RMIXMUTE, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, RTLNMUTE, 0x0);
codec_wr_prcm_control(PHONEOUT_CTRL, 0xff, PHONEOUTS0, 0x60);
codec_wr_prcm_control(MIC2G_LINEEN_CTRL, 0xff, LINEOUTR_SS, 0x40);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0xf, MIC1BOOST, 0x4);
codec_wr_prcm_control(RADCMIXSC, 0xff, RADCMIXMUTE, 0x0);
codec_wr_prcm_control(LADCMIXSC, 0xff, LADCMIXMUTE, 0x0);
codec_wr_prcm_control(ADC_AP_EN, 0x3, ADCLEN, 0x0);
/*close digital parts*/
codec_wr_control(SUNXI_DA_CTL, 0xffffffff, GEN, 0x0);
codec_wr_control(SUNXI_DA_FAT0, 0xffffffff, FMT, 0x0);
codec_wr_control(SUNXI_DA_INT, 0x1, TX_DRQ, 0x0);
codec_wr_control(SUNXI_DA_INT, 0x1, RX_DRQ, 0x0);
codec_wr_control(SUNXI_DA_CLKD, 0xff, MCLKDIV, 0x0);
codec_wr_control(SUNXI_SYSCLK_CTL, 0xfff, SYSCLK_SRC, 0x0);
codec_wr_control(SUNXI_MOD_CLK_ENA, 0xffff, MODULE_CLK_EN_CTL, 0x0);
codec_wr_control(SUNXI_MOD_RST_CTL, 0xffff, MODULE_RST_CTL_BIT, 0x0);
codec_wr_control(SUNXI_AIF1CLK_CTRL, 0xffff, AIF1_TDMM_ENA, 0x0);
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL, 0xffff, AIF1_SLOT_SIZ, 0x0);
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL, 0xffff, AIF1_LOOP_ENA, 0x0);
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0xffff, AIF1_AD1R_MXR_SRC_C, 0x0);
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0xffff, 0, 0x0);
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL, 0xffff, AIF2_LOOP_EN, 0x0);
codec_wr_control(SUNXI_AIF2_DACDAT_CTRL, 0xffff, 0, 0x0);
codec_wr_control(SUNXI_AIF2_MXR_SRC, 0xff, AIF2_ADCR_MXR_SRC, 0x0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL, 0xffff, AIF3_CLOC_SRC, 0x0);
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 0x0);
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0x0);
codec_wr_control(SUNXI_DAC_MXR_SRC, 0xff, DACR_MXR_SRC, 0x0);
/*set all routeway flag false*/
codec_analog_phonein_en = 0;
codec_analog_mainmic_en = 0;
codec_analog_phoneout_en = 0;
codec_lineinin_en = 0;
codec_lineincap_en = 0;
codec_analog_headsetmic_en = 0;
codec_speakerout_en = 0;
codec_earpieceout_en = 0;
codec_voice_record_en = 0;
codec_headphoneout_en = 0;
/*Digital_bb*/
codec_digital_headsetmic_en = 0;
codec_digital_mainmic_en = 0;
codec_digital_phoneout_en = 0;
codec_digital_phonein_en = 0;
codec_digital_bb_clk_format_init = 0;
/*bluetooth*/
codec_bt_clk_format = 0;
codec_bt_out_en = 0;
bt_bb_button_voice = 0;
codec_analog_btmic_en = 0;
codec_analog_btphonein_en = 0;
codec_digital_btmic_en = 0;
codec_digital_btphonein_en = 0;
codec_digital_bb_bt_clk_format = 0;
codec_system_bt_capture_en = 0;
return 0;
}
static int codec_get_endcall(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return 0;
}
/*
* use for linein record
*/
static int codec_set_lineincap(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_lineincap_en = ucontrol->value.integer.value[0];
return 0;
}
static int codec_get_lineincap(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_lineincap_en;
return 0;
}
/*
* codec_lineinin_en == 1, open the linein in.
* codec_lineinin_en == 0, close the linein in.
*/
static int codec_set_lineinin(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_lineinin_en = ucontrol->value.integer.value[0];
if (codec_lineinin_en) {
/*select LINEINL*/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTELINEINL, 0x1);
/*select LINEINR*/
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTELINEINR, 0x1);
/*enable output mixer*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x1);
} else {
/*close LINEINL*/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTELINEINL, 0x0);
/*close LINEINR*/
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTELINEINR, 0x0);
/*disable output mixer*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x0);
}
return 0;
}
static int codec_get_lineinin(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_lineinin_en;
return 0;
}
/*
* codec_digital_mainmic_en == 1, open mic1 for digital bb.
* codec_digital_mainmic_en == 0, close mic1 for digital bb.
*/
static int codec_digital_set_mainmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_mainmic_en = ucontrol->value.integer.value[0];
if (codec_digital_mainmic_en) {
/*open main mic(mic1) routeway*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC1AMPEN, 0x1);
/*mic1 gain 36dB,if capture volume is too small,
*enlarge the mic1boost
*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL,
0x7, MIC1BOOST, phone_main_mic_vol);
/*enable Master microphone bias*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL,
0x1, MMICBIASEN, 0x1);
/*enable Left MIC1 Boost stage*/
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTEMIC1BOOST, 0x1);
/*enable adc analog*/
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x1);
/*enable ADC Digital part*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 1);
/*enable Digital microphone*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENDM, 0);
codec_wr_control(SUNXI_AIF2_MXR_SRC,
0x1, AIF2_ADCL_MXR_SRC, 1);
} else {
codec_wr_control(SUNXI_AIF2_MXR_SRC,
0x1, AIF2_ADCL_MXR_SRC, 0);
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 0);
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0);
codec_wr_prcm_control(LADCMIXSC, 0x1, LADCMIXMUTEMIC1BOOST, 0);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MMICBIASEN, 0);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC1AMPEN, 0);
}
return 0;
}
static int codec_digital_get_mainmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_mainmic_en;
return 0;
}
/*
* codec_digital_headsetmic_en == 1, open mic2 for digital bb.
* codec_digital_headsetmic_en == 0, close mic2 for digital bb.
*/
static int codec_digital_set_headsetmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_headsetmic_en = ucontrol->value.integer.value[0];
if (codec_digital_headsetmic_en) {
/*enable mic2 pa*/
codec_wr_prcm_control(MIC2G_LINEEN_CTRL, 0x1, MIC2AMPEN, 0x1);
/*mic2 gain 36dB,if capture volume is too small,
*enlarge the mic2boost
*/
codec_wr_prcm_control(MIC2G_LINEEN_CTRL,
0x7, MIC2BOOST, phone_headset_mic_vol);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MIC2_SS, 0x1);
/*enable Left MIC2 Boost stage*/
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTEMIC2BOOST, 0x1);
/*enable adc analog*/
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x1);
/*enable ADC Digital part*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 1);
/*enable Digital microphone*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENDM, 0);
codec_wr_control(SUNXI_AIF2_MXR_SRC,
0x1, AIF2_ADCL_MXR_SRC, 1);
} else {
codec_wr_control(SUNXI_AIF2_MXR_SRC,
0x1, AIF2_ADCL_MXR_SRC, 0);
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 0);
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x0);
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTEMIC2BOOST, 0x0);
codec_wr_prcm_control(MIC2G_LINEEN_CTRL, 0x1, MIC2AMPEN, 0);
}
return 0;
}
static int codec_digital_get_headsetmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_headsetmic_en;
return 0;
}
/*
* codec_digital_phoneout_en == 1, enable phoneout for digital bb.
* codec_digital_phoneout_en == 0, disable phoneout for digital bb.
*/
static int codec_digital_set_phoneout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_phoneout_en = ucontrol->value.integer.value[0];
if (codec_digital_phoneout_en) {
/*enable aif2 adcl chanel*/
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL, 0x1, AIF2_ADCL_EN, 1);
/*select aif2 adc left channel source */
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL,
0x3, AIF2_ADCL_SRC, 0);
} else {
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL, 0x1, AIF2_ADCL_EN, 0);
}
return 0;
}
static int codec_digital_get_phoneout(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_phoneout_en;
return 0;
}
/*
* codec_digital_phonein_en == 1, enble phonein for digital bb .
* codec_digital_phonein_en == 0. disable phonein for digital bb.
*/
static int codec_set_digital_phonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_phonein_en = ucontrol->value.integer.value[0];
if (codec_digital_phonein_en) {
/*enable aif2 dacl chanel*/
codec_wr_control(SUNXI_AIF2_DACDAT_CTRL,
0x1, AIF2_DACL_ENA, 1);
/*select aif2 dac left channel source */
codec_wr_control(SUNXI_AIF2_DACDAT_CTRL,
0x3, AIF2_DACL_SRC, 0);
/*aif2 dac input source select*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF2_DAC_SRC, 0);
/*dac left channel source select*/
codec_wr_control(SUNXI_DAC_MXR_SRC,
0x1, DACL_MXR_SRC_AIF2DACL, 1);
/*dac digital part enable*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 1);
/*dac analog part enable*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x1);
/*select output mixer source*/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEDACL, 0x1);
/*enable output mixer*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x1);
} else {
/*disable output mixer*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x0);
/**/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x0);
codec_wr_prcm_control(ROMIXSC, 0x1, RMIXMUTEDACL, 0x0);
/*disable dac digital part*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0);
/*disable dac analog part*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x0);
/*dac left channel source select*/
codec_wr_control(SUNXI_DAC_MXR_SRC,
0x1, DACL_MXR_SRC_AIF2DACL, 0);
/*disable aif2 dacl chanel*/
codec_wr_control(SUNXI_AIF2_DACDAT_CTRL,
0x1, AIF2_DACL_ENA, 0);
}
return 0;
}
static int codec_get_digital_phonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_phonein_en;
return 0;
}
/*
*codec_set_digital_bb_clk_format:confige the clk fmt for call with digital bb
*
*/
static int codec_set_digital_bb_clk_format(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_bb_clk_format_init = ucontrol->value.integer.value[0];
if (codec_digital_bb_clk_format_init) {
/*enable src clk*/
/*set pll2 :24576k*/
if (clk_set_rate(codec_pll2clk, 24576000))
pr_err("set codec_pll2clk rate fail\n");
/*enable pll2*4 for src*/
if ((!codec_srcclk) || (IS_ERR(codec_srcclk)))
pr_err("try to get codec_srcclk failed!\n");
if (clk_prepare_enable(codec_srcclk))
pr_err("err:open codec_srcclk failed;\n");
/*enable aif2,system clk from aif2*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x1, AIF2CLK_ENA, 0x1);
/*aif2 clk source select*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x3, AIF2CLK_SRC, 0x3);
/*sysclk from aif2*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x1, SYSCLK_SRC, 0x1);
/*enable aif2/da/da module*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF2_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, DAC_DIGITAL_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, ADC_DIGITAL_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, SRC1_MOD_CLK_EN, 0x1);/*src1*/
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, SRC2_MOD_CLK_EN, 0x1);/*src2*/
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, AIF2_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, DAC_DIGITAL_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, ADC_DIGITAL_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, SRC2_MOD_RST_CTL, 0x1);/*src2*/
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, SRC1_MOD_RST_CTL, 0x1);/*src1*/
/*confige ad/da sr:8k*/
codec_wr_control(SUNXI_SYS_SR_CTRL, 0xf, AIF1_FS, 0x0);
codec_wr_control(SUNXI_SYS_SR_CTRL, 0xf, AIF2_FS, 0x0);
/*select src1 source from aif2*/
codec_wr_control(SUNXI_SYS_SR_CTRL, 0x1, SRC1_SRC, 0x1);
/*select src2 source from aif2*/
codec_wr_control(SUNXI_SYS_SR_CTRL, 0x1, SRC2_SRC, 0x1);
codec_wr_control(SUNXI_SYS_SR_CTRL,
0x1, SRC1_ENA, 0x1);/*enable src1*/
codec_wr_control(SUNXI_SYS_SR_CTRL,
0x1, SRC2_ENA, 0x1);/*enable src2*/
/*confige aif2 lrck:8k,pcm,mono,slave*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_MSTR_MOD, 0x1);/*confige aif2 slave*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_BCLK_INV, 0x0);/*bclk_inv:0*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_LRCK_INV, 0x0);/*lrck_inv:0*/
/*codec_wr_control(SUNXI_AIF2_CLK_CTRL,
*0x3, AIF2_LRCK_DIV, 0x2);
*/
/*codec_wr_control(SUNXI_AIF2_CLK_CTRL,
* 0xf, AIF2_BCLK_DIV, 0x2);
*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x3, AIF2_WORD_SIZ, 0x1);/*wss:16*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x3, AIF2_DATA_FMT, 0x3);/*fmt:pcm*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_MONO_PCM, 0x1);/*fmt:pcm*/
} else {
/*disable aif2,system clk from aif2*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0xfff, SYSCLK_SRC, 0);
/*disable aif2/da/da module*/
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, AIF2_MOD_CLK_EN, 0x0);
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, DAC_DIGITAL_MOD_CLK_EN, 0x0);
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, ADC_DIGITAL_MOD_CLK_EN, 0x0);
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, SRC1_MOD_CLK_EN, 0x0);/*src1*/
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, SRC2_MOD_CLK_EN, 0x0);/*src2*/
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, AIF2_MOD_RST_CTL, 0x0);
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, DAC_DIGITAL_MOD_RST_CTL, 0x0);
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, ADC_DIGITAL_MOD_RST_CTL, 0x0);
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, SRC2_MOD_RST_CTL, 0x0);/*src2*/
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, SRC1_MOD_RST_CTL, 0x0);/*src1*/
codec_wr_control(SUNXI_SYS_SR_CTRL,
0x1, SRC1_ENA, 0x0);/*disable src1*/
codec_wr_control(SUNXI_SYS_SR_CTRL,
0x1, SRC2_ENA, 0x0);/*disable src2*/
/*confige aif2 lrck:8k,pcm,mono,slave*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0xffff, 0, 0x0);
if ((NULL == codec_srcclk) || (IS_ERR(codec_srcclk)))
pr_err("codec_srcclk is invaled, just return\n");
else
clk_disable_unprepare(codec_srcclk);
}
return 0;
}
static int codec_get_digital_bb_clk_format(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_bb_clk_format_init;
return 0;
}
static int codec_set_bt_clk_format(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_bt_clk_format = ucontrol->value.integer.value[0];
if (codec_bt_clk_format) {
pr_debug("%s,line:%d\n", __func__, __LINE__);
/*enable src clk*/
if (clk_set_rate(codec_pll2clk, 24576000))
pr_err("err:set codec_pll2clk rate fail\n");
if ((!codec_srcclk) || (IS_ERR(codec_srcclk)))
pr_err("err:try to get codec_srcclk failed!\n");
if (clk_prepare_enable(codec_srcclk))
pr_err("err:open codec_srcclk failed;\n");
/*enable aif2,system clk from aif2*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x1, AIF2CLK_ENA, 0x1);
/*aif2 clk source select*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x3, AIF2CLK_SRC, 0x3);
/*sysclk from aif2*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x1, SYSCLK_SRC, 0x1);
/*enable sysclk*/
codec_wr_control(SUNXI_SYSCLK_CTL, 0x1, SYSCLK_ENA, 0x1);
/*enable aif2/da/da module*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF2_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, DAC_DIGITAL_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, ADC_DIGITAL_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, SRC1_MOD_CLK_EN, 0x1);/*src1*/
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, SRC2_MOD_CLK_EN, 0x1);/*src2*/
/*enable aif3*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF3_MOD_CLK_EN, 0x1);
/*reset aif2/da/da module*/
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, AIF2_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, DAC_DIGITAL_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, ADC_DIGITAL_MOD_RST_CTL, 0x1);
/*
*codec_wr_control(SUNXI_MOD_RST_CTL,
* 0x1, SRC2_MOD_RST_CTL, 0x1);
*codec_wr_control(SUNXI_MOD_RST_CTL,
* 0x1, SRC1_MOD_RST_CTL, 0x1);
*/
/*reset aif3*/
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, AIF3_MOD_RST_CTL, 0x1);
/*confige ad/da sr:8k*/
codec_wr_control(SUNXI_SYS_SR_CTRL, 0xf, AIF1_FS, 0x0);
codec_wr_control(SUNXI_SYS_SR_CTRL, 0xf, AIF2_FS, 0x0);
/*select src1 source from aif2*/
/*codec_wr_control(SUNXI_SYS_SR_CTRL, 0x1, SRC1_SRC, 0x1);*/
/*select src2 source from aif2*/
/*codec_wr_control(SUNXI_SYS_SR_CTRL, 0x1, SRC2_SRC, 0x1);*/
/*codec_wr_control(SUNXI_SYS_SR_CTRL, 0x1, SRC1_ENA, 0x1);*/
/*codec_wr_control(SUNXI_SYS_SR_CTRL, 0x1, SRC2_ENA, 0x1);*/
/*confige aif2 lrck:8k,pcm,mono,master*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_MSTR_MOD, 0x0);/*confige aif2 master*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_BCLK_INV, 0x0);/*bclk_inv:0*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_LRCK_INV, 0x0);/*lrck_inv:0*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x3, AIF2_LRCK_DIV, 0x2);/*bclk/lrck:64*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0xf, AIF2_BCLK_DIV, 0x9);/*aif2/bclk:48*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x3, AIF2_WORD_SIZ, 0x1);/*wss:16*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x3, AIF2_DATA_FMT, 0x3);/*fmt:pcm*/
codec_wr_control(SUNXI_AIF2_CLK_CTRL,
0x1, AIF2_MONO_PCM, 0x1);/*fmt:pcm*/
/*confige aif3*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL,
0x1, AIF3_BCLK_INV, 0x0);/*bclk_inv:0*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL,
0x1, AIF3_LRCK_INV, 0x0);/*lrck_inv:0*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL,
0x3, AIF3_WORD_SIZ, 0x1);/*wss:16*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL,
0x3, AIF3_CLOC_SRC, 0x1);/*clk from aif2*/
} else {
codec_wr_control(SUNXI_SYSCLK_CTL, 0xfff, SYSCLK_SRC, 0);
/*enable aif2/da/da module*/
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, AIF2_MOD_CLK_EN, 0x0);
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, DAC_DIGITAL_MOD_CLK_EN, 0x0);
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, ADC_DIGITAL_MOD_CLK_EN, 0x0);
/*enable aif3*/
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF3_MOD_CLK_EN, 0x0);
/*reset aif2/da/da module*/
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, AIF2_MOD_RST_CTL, 0x0);
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, DAC_DIGITAL_MOD_RST_CTL, 0x0);
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, ADC_DIGITAL_MOD_RST_CTL, 0x0);
/*reset aif3*/
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, AIF3_MOD_RST_CTL, 0x0);
codec_wr_control(SUNXI_AIF2_CLK_CTRL, 0xffff, 0, 0x0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL, 0xffff, 0, 0x0);
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, SRC1_MOD_CLK_EN, 0x0);/*src1*/
codec_wr_control(SUNXI_MOD_CLK_ENA,
0x1, SRC2_MOD_CLK_EN, 0x0);/*src2*/
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, SRC2_MOD_RST_CTL, 0x0);/*src2*/
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, SRC1_MOD_RST_CTL, 0x0);/*src1*/
codec_wr_control(SUNXI_SYS_SR_CTRL,
0x1, SRC1_ENA, 0x0);/*disable src1*/
codec_wr_control(SUNXI_SYS_SR_CTRL,
0x1, SRC2_ENA, 0x0);/*disable src2*/
if ((NULL == codec_srcclk) || (IS_ERR(codec_srcclk)))
pr_err("codec_srcclk is invaled, just return\n");
else
clk_disable_unprepare(codec_srcclk);
}
return 0;
}
static int codec_get_bt_clk_format(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_bt_clk_format;
return 0;
}
static int codec_set_bt_out(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_bt_out_en = ucontrol->value.integer.value[0];
if (codec_bt_out_en) {
/*enable aif2 adcl channel(this bit for make clk)*/
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL, 0x1, AIF2_ADCL_EN, 1);
/*select aif3 pcm output source*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF3_ADC_SRC, 2);
} else {
/*disable aif2 adcl channel*/
codec_wr_control(SUNXI_AIF2_ADCDAT_CTRL, 0x1, AIF2_ADCR_EN, 0);
/*select aif3 pcm output source*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF3_ADC_SRC, 0);
}
return 0;
}
static int codec_get_bt_out(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_bt_out_en;
return 0;
}
static int codec_set_analog_btmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_analog_btmic_en = ucontrol->value.integer.value[0];
if (codec_analog_btmic_en) {
/*select aif2 dac input source*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF2_DAC_SRC, 1);
/*dac left channel mixer source select*/
codec_wr_control(SUNXI_DAC_MXR_SRC,
0x1, DACL_MXR_SRC_AIF2DACL, 1);
/*dac digital enble*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 1);
/*dac analog enble*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x1);
/*left output mixer source select*/
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x1);
/*left output mixer enble*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x1);
/*select phoneout source*/
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUTS0, 0x1);
} else {
codec_wr_prcm_control(PHONEOUT_CTRL, 0x1, PHONEOUTS0, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x0);
codec_wr_prcm_control(LOMIXSC, 0x1, LMIXMUTEDACL, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x0);
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0);
codec_wr_control(SUNXI_DAC_MXR_SRC,
0x1, DACL_MXR_SRC_AIF2DACL, 0);
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF3_ADC_SRC, 0);
}
return 0;
}
static int codec_get_analog_btmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_analog_btmic_en;
return 0;
}
static int codec_set_analog_btphonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_analog_btphonein_en = ucontrol->value.integer.value[0];
if (codec_analog_btphonein_en) {
/*right mixer source :phone_pn*/
codec_wr_prcm_control(RADCMIXSC, 0x1, RADCMIXMUTEPHONEPN, 0x1);
/*enable adc analog */
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCREN, 0x1);
/*enable adc digital*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 1);
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENDM, 0);
/*select AIF2 dac right channel mixer source */
codec_wr_control(SUNXI_AIF2_MXR_SRC,
0x1, AIF2_ADCR_MXR_SRC_ADCR, 1);
} else {
codec_wr_control(SUNXI_AIF2_MXR_SRC,
0x1, AIF2_ADCR_MXR_SRC_ADCR, 0);
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 0);
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCREN, 0x0);
codec_wr_prcm_control(RADCMIXSC, 0x1, RADCMIXMUTEPHONEPN, 0);
}
return 0;
}
static int codec_get_analog_btphonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_analog_btphonein_en;
return 0;
}
static int codec_set_digital_btmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_btmic_en = ucontrol->value.integer.value[0];
if (codec_digital_btmic_en) {
/*select aif2 dac input source*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF2_DAC_SRC, 2);
/*aif2 dac right channel enable*/
/*codec_wr_control(SUNXI_AIF2_DACDAT_CTRL,
* 0x1, AIF2_DACR_ENA,1);
*/
/*aif2 adc left channel micer source select*/
codec_wr_control(SUNXI_AIF2_MXR_SRC,
0x1, AIF2_ADCL_MXR_SRC_AIF2DACR, 1);
} else {
codec_wr_control(SUNXI_AIF2_MXR_SRC,
0x1, AIF2_ADCL_MXR_SRC_AIF2DACR, 0);
/*codec_wr_control(SUNXI_AIF2_DACDAT_CTRL,
* 0x1, AIF2_DACR_ENA, 0);
*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF2_DAC_SRC, 0);
}
return 0;
}
static int codec_get_digital_btmic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_btmic_en;
return 0;
}
static int codec_set_digital_btphonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_btphonein_en = ucontrol->value.integer.value[0];
if (codec_digital_btphonein_en) {
/*aif2 dac left channel enable*/
codec_wr_control(SUNXI_AIF2_DACDAT_CTRL,
0x1, AIF2_DACL_ENA, 1);
codec_wr_control(SUNXI_AIF2_DACDAT_CTRL,
0x3, AIF2_DACL_SRC, 0);
/*select aif2 dac input source*/
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF2_DAC_SRC, 2);
/*select aif2 adc right channle mixer source */
codec_wr_control(SUNXI_AIF2_MXR_SRC,
0x1, AIF2_ADCR_MXR_SRC_AIF2DACL, 1);
} else {
codec_wr_control(SUNXI_AIF2_MXR_SRC,
0x1, AIF2_ADCR_MXR_SRC_AIF2DACL, 0);
codec_wr_control(SUNXI_AIF2_DACDAT_CTRL,
0x1, AIF2_DACL_ENA, 0);
codec_wr_control(SUNXI_AIF3_SGP_CTRL, 0x3, AIF2_DAC_SRC, 0);
}
return 0;
}
static int codec_get_digital_btphonein(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_btphonein_en;
return 0;
}
static int codec_set_bt_button_voice(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
bt_bb_button_voice = ucontrol->value.integer.value[0];
return 0;
}
static int codec_get_bt_button_voice(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = bt_bb_button_voice;
return 0;
}
static int codec_set_digital_bb_bt_clk_format(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_digital_bb_bt_clk_format = ucontrol->value.integer.value[0];
if (codec_digital_bb_bt_clk_format) {
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF3_MOD_CLK_EN, 0x1);
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, AIF3_MOD_RST_CTL, 0x1);
codec_wr_control(SUNXI_AIF3_CLK_CTRL,
0x1, AIF3_BCLK_INV, 0x0);/*bclk_inv:0*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL,
0x1, AIF3_LRCK_INV, 0x0);/*lrck_inv:0*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL,
0x3, AIF3_WORD_SIZ, 0x1);/*wss:16*/
codec_wr_control(SUNXI_AIF3_CLK_CTRL,
0x3, AIF3_CLOC_SRC, 0x1);/*clk from aif2*/
} else {
codec_wr_control(SUNXI_MOD_CLK_ENA, 0x1, AIF3_MOD_CLK_EN, 0x0);
codec_wr_control(SUNXI_MOD_RST_CTL,
0x1, AIF3_MOD_RST_CTL, 0x0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL , 0x3, AIF3_WORD_SIZ, 0);
codec_wr_control(SUNXI_AIF3_CLK_CTRL , 0x3, AIF3_CLOC_SRC, 0);
}
return 0;
}
static int codec_get_digital_bb_bt_clk_format(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_digital_bb_bt_clk_format;
return 0;
}
static int codec_set_system_bt_capture_flag(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_system_bt_capture_en = ucontrol->value.integer.value[0];
return 0;
}
static int codec_get_system_bt_capture_flag(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_system_bt_capture_en;
return 0;
}
static int codec_set_analog_bb_capture_mic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
codec_analog_bb_capture_mic = ucontrol->value.integer.value[0];
if (codec_analog_bb_capture_mic) {
codec_analog_voice_capture_open();
} else {
/*enable aif1 adc left channel */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL,
0x1, AIF1_AD0L_ENA, 0);
/*select mic source*/
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTEMIC1BOOST, 0x0);
/*select mic2 source*/
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTEMIC2BOOST, 0x0);
/*select phonein source*/
codec_wr_prcm_control(LADCMIXSC,
0x1, LADCMIXMUTEPHONEPN, 0x0);
/*enable adc analog*/
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x0);
/*enable dac digital*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 0);
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENDM, 0);
/*select aif1 adc left channel mixer source */
codec_wr_control(SUNXI_AIF1_MXR_SRC,
0x1, AIF1_AD0L_MXL_SRC_ADCL, 0);
/*select aif1 adc left channel source */
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL,
0x3, AIF1_AD0L_SRC, 0);
}
return 0;
}
static int codec_get_analog_bb_capture_mic(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = codec_analog_bb_capture_mic;
return 0;
}
static const struct snd_kcontrol_new sunxi_codec_controls[] = {
/*volume ctl: headset ,speaker,earpiece*/
/*CODEC_SINGLE("Master Playback Volume", HP_VOLC, 0, 0x3f, 0),*/
CODEC_SINGLE("headphone volume control", HP_VOLC, 0, 0x3f, 0),
CODEC_SINGLE("earpiece volume control", HP_VOLC, 0, 0x3f, 0),
CODEC_SINGLE("speaker volume control", HP_VOLC, 0, 0x3f, 0),
CODEC_SINGLE("MIC1_G boost stage output mixer control",
MICIN_GCTRL, MIC1G, 0x7, 0),
CODEC_SINGLE("MIC2_G boost stage output mixer control",
MICIN_GCTRL, MIC2G, 0x7, 0),
CODEC_SINGLE("LINEIN_G boost stage output mixer control",
LINEIN_GCTRL, LINEING, 0x7, 0),
CODEC_SINGLE("PHONE_G boost stage output mixer control",
LINEIN_GCTRL, PHONEG, 0x7, 0),
CODEC_SINGLE("PHONE_PG boost stage output mixer control",
PHONEIN_GCTRL, PHONEPG, 0x7, 0),
CODEC_SINGLE("PHONE_NG boost stage output mixer control",
PHONEIN_GCTRL, PHONENG, 0x7, 0),
CODEC_SINGLE("MIC1 boost AMP gain control",
MIC1G_MICBIAS_CTRL, MIC1BOOST, 0x7, 0),
CODEC_SINGLE("MIC2 boost AMP gain control",
MIC2G_LINEEN_CTRL, MIC2BOOST, 0x7, 0),
CODEC_SINGLE("Lineout volume control", HP_VOLC, 0, 0x3f, 0),
CODEC_SINGLE("PHONEP-PHONEN pre-amp gain control",
LINEOUT_VOLC, PHONEPREG, 0x7, 0),
CODEC_SINGLE("Phoneout gain control",
PHONEOUT_CTRL, PHONEOUTG, 0x7, 0),
CODEC_SINGLE("ADC input gain ctrl", ADC_AP_EN, ADCG, 0x7, 0),
/*enable phoneout*/
SOC_SINGLE_BOOL_EXT("Audio phone out",
0, codec_analog_get_phoneout, codec_analog_set_phoneout),
/*open the phone in call*/
SOC_SINGLE_BOOL_EXT("Audio phone in",
0, codec_analog_get_phonein, codec_analog_set_phonein),
/*set the phone in call voice through earpiece out*/
SOC_SINGLE_BOOL_EXT("Audio earpiece out",
0, codec_get_earpieceout, codec_set_earpieceout),
/*set the phone in call voice through headphone out*/
SOC_SINGLE_BOOL_EXT("Audio headphone out",
0, codec_get_headphoneout, codec_set_headphoneout),
/*set the phone in call voice through speaker out*/
SOC_SINGLE_BOOL_EXT("Audio speaker out",
0, codec_get_speakerout, codec_set_speakerout),
/*set main mic(mic1)*/
SOC_SINGLE_BOOL_EXT("Audio analog main mic",
0, codec_analog_get_mainmic, codec_analog_set_mainmic),
/*set headset mic(mic2)*/
SOC_SINGLE_BOOL_EXT("Audio analog headsetmic",
0, codec_analog_get_headsetmic, codec_analog_set_headsetmic),
/*set voicerecord status*/
SOC_SINGLE_BOOL_EXT("Audio phone voicerecord",
0, codec_get_voicerecord, codec_set_voicerecord),
/*set endcall*/
SOC_SINGLE_BOOL_EXT("Audio phone endcall",
0, codec_get_endcall, codec_set_endcall),
SOC_SINGLE_BOOL_EXT("Audio linein record",
0, codec_get_lineincap, codec_set_lineincap),
SOC_SINGLE_BOOL_EXT("Audio linein in",
0, codec_get_lineinin, codec_set_lineinin),
SOC_ENUM_EXT("Speaker Function", spk_headset_earpiece_enum[0],
codec_get_spk_headset_earpiece,
codec_set_spk_headset_earpiece),
/*audio digital interface for phone case*/
/*set mic1 for digital bb*/
SOC_SINGLE_BOOL_EXT("Audio digital main mic",
0, codec_digital_get_mainmic, codec_digital_set_mainmic),
/*set mic2 for digital bb*/
SOC_SINGLE_BOOL_EXT("Audio digital headset mic",
0, codec_digital_get_headsetmic, codec_digital_set_headsetmic),
/*set phoneout for digital bb*/
SOC_SINGLE_BOOL_EXT("Audio digital phone out",
0, codec_digital_get_phoneout, codec_digital_set_phoneout),
/*set phonein for digtal bb*/
SOC_SINGLE_BOOL_EXT("Audio digital phonein",
0, codec_get_digital_phonein, codec_set_digital_phonein),
/*set clk,format for digtal bb*/
SOC_SINGLE_BOOL_EXT("Audio digital clk format status",
0, codec_get_digital_bb_clk_format,
codec_set_digital_bb_clk_format),
/*bluetooth*/
/*set clk,format for bt*/
SOC_SINGLE_BOOL_EXT("Audio bt clk format status",
0, codec_get_bt_clk_format, codec_set_bt_clk_format),
/*set bt out*/
SOC_SINGLE_BOOL_EXT("Audio bt out",
0, codec_get_bt_out, codec_set_bt_out),
/*set analog bt mic*/
SOC_SINGLE_BOOL_EXT("Audio analog bt mic",
0, codec_get_analog_btmic, codec_set_analog_btmic),
/*set analog bt phonein*/
SOC_SINGLE_BOOL_EXT("Audio analog bt phonein",
0, codec_get_analog_btphonein, codec_set_analog_btphonein),
/* set bt mic for dbb*/
SOC_SINGLE_BOOL_EXT("Audio digital bt mic",
0, codec_get_digital_btmic, codec_set_digital_btmic),
/*set bt phonein for dbb*/
SOC_SINGLE_BOOL_EXT("Audio digital bt phonein",
0, codec_get_digital_btphonein, codec_set_digital_btphonein),
/*bt_bb_out*/
SOC_SINGLE_BOOL_EXT("Audio bt button voice",
0, codec_get_bt_button_voice, codec_set_bt_button_voice),
/*set bt phonein for dbb*/
SOC_SINGLE_BOOL_EXT("Audio digital bb bt clk format",
0, codec_get_digital_bb_bt_clk_format,
codec_set_digital_bb_bt_clk_format),
/*set bt phonein for dbb*/
SOC_SINGLE_BOOL_EXT("Audio system bt capture flag",
0, codec_get_system_bt_capture_flag,
codec_set_system_bt_capture_flag),
/*set bt phonein for dbb*/
SOC_SINGLE_BOOL_EXT("Audio analog bb capture mic",
0, codec_get_analog_bb_capture_mic,
codec_set_analog_bb_capture_mic),
#if 1
/*test :1,default:0*/
CODEC_SINGLE_DIGITAL("aif3 loopback",
SUNXI_AIF3_DACDAT_CTRL, AIF3_LOOP_ENA, 0x1, 0),
/*test:1,default:0*/
CODEC_SINGLE_DIGITAL("aif2 loopback",
SUNXI_AIF2_DACDAT_CTRL, AIF2_LOOP_EN, 0x1, 0),
/*test:1,default:0*/
CODEC_SINGLE_DIGITAL("digital_bb_bt",
SUNXI_AIF2_MXR_SRC, AIF2_ADCR_MXR_SRC_AIF2DACL, 0x1, 0),
/*teset:0x8*/
CODEC_SINGLE_DIGITAL("system play_capture set 1",
SUNXI_AIF1_MXR_SRC, AIF1_AD0R_MXR_SRC, 0xf, 0),
/*teset:0x8*/
CODEC_SINGLE_DIGITAL("system play_capture set 2",
SUNXI_AIF1_MXR_SRC, AIF1_AD0L_MXL_SRC_AIF2DACR, 0xf, 0),
/*0x24c*/
CODEC_SINGLE_DIGITAL("AIF1_AD0L_MXR_SRC AIF1DA0Ldata",
SUNXI_AIF1_MXR_SRC, AIF1_AD0L_MXL_SRC_AIF1DA0L, 0x1, 0),
CODEC_SINGLE_DIGITAL("AIF1_AD0L_MXR_SRC AIF2DACLdata",
SUNXI_AIF1_MXR_SRC, AIF1_AD0L_MXL_SRC_AIF2DACL, 0x1, 0),
CODEC_SINGLE_DIGITAL("AIF1_AD0L_MXR_SRC ADCLdata",
SUNXI_AIF1_MXR_SRC, AIF1_AD0L_MXL_SRC_ADCL, 0x1, 0),
CODEC_SINGLE_DIGITAL("AIF1_AD0L_MXR_SRC AIF2DACRdata",
SUNXI_AIF1_MXR_SRC, AIF1_AD0L_MXL_SRC_AIF2DACR, 0x1, 0),
CODEC_SINGLE_DIGITAL("AIF1_AD0R_MXR_SRC AIF1DA0Rdata",
SUNXI_AIF1_MXR_SRC, AIF1_AD0R_MXR_SRC_AIF1DA0R, 0x1, 0),
CODEC_SINGLE_DIGITAL("AIF1_AD0R_MXR_SRC AIF2DACRdata",
SUNXI_AIF1_MXR_SRC, AIF1_AD0R_MXR_SRC_AIF2DACR, 0x1, 0),
CODEC_SINGLE_DIGITAL("AIF1_AD0R_MXR_SRC ADCRdata",
SUNXI_AIF1_MXR_SRC, AIF1_AD0R_MXR_SRC_ADCR, 0x1, 0),
CODEC_SINGLE_DIGITAL("AIF1_AD0R_MXR_SRC AIF2DACLdata",
SUNXI_AIF1_MXR_SRC, AIF1_AD0R_MXR_SRC_AIF2DACL, 0x1, 0),
/*ADC source*/
CODEC_SINGLE("Analog cap test disable phonein",
LADCMIXSC, LADCMIXMUTEPHONEPN, 0x1, 0),
CODEC_SINGLE("Analog cap test disable mic1",
LADCMIXSC, LADCMIXMUTEMIC1BOOST, 0x1, 0),
CODEC_SINGLE("Analog cap test disable mic2",
LADCMIXSC, LADCMIXMUTEMIC2BOOST, 0x1, 0),
#endif
};
static struct snd_soc_dai_ops sndpcm_dai_ops = {
.startup = sndpcm_startup,
.shutdown = sndpcm_shutdown,
.prepare = sndpcm_perpare,
.hw_params = sndpcm_hw_params,
.digital_mute = sndpcm_unmute,
};
static struct snd_soc_dai_driver sndpcm_dai = {
.name = "sndcodec",
/* playback capabilities */
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDPCM_RATES,
.formats = SNDPCM_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDPCM_RATES,
.formats = SNDPCM_FORMATS,
},
/* pcm operations */
.ops = &sndpcm_dai_ops,
};
EXPORT_SYMBOL(sndpcm_dai);
static int sndpcm_soc_probe(struct snd_soc_codec *codec)
{
/* Add virtual switch */
snd_soc_add_codec_controls(codec, sunxi_codec_controls,
ARRAY_SIZE(sunxi_codec_controls));
return 0;
}
static int sndpcm_suspend(struct snd_soc_codec *codec)
{
unsigned long config;
char pin_name[8];
pr_debug("[audio codec]:suspend start\n");
/* check if called in talking standby */
if (check_scene_locked(SCENE_TALKING_STANDBY) == 0) {
pr_err("In talking standby, audio codec do not suspend!!\n");
return 0;
}
gpio_set_value(spk_gpio, 0);
/*mute l_pa and r_pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
/*digital part*/
codec_wr_control(SUNXI_DA_CTL , 0xffffffff, GEN, 0x0);
/*SUNXI_AIF1_ADCDAT_CTRL:disable AIF1_AD0L_ENA, AIF1_AD0R_ENA*/
codec_wr_control(SUNXI_AIF1_ADCDAT_CTRL , 0x3, AIF1_AD0R_ENA, 0x0);
/*SUNXI_AIF1_DACDAT_CTRL:disable AIF1_DA0L_ENA, AIF1_DA0R_ENA*/
codec_wr_control(SUNXI_AIF1_DACDAT_CTRL , 0x3, AIF1_DA0R_ENA, 0x0);
/*SUNXI_AIF1_MXR_SRC:mute*/
codec_wr_control(SUNXI_AIF1_MXR_SRC, 0xffff, AIF1_AD1R_MXR_SRC_C, 0x0);
/*dsable adc digital*/
codec_wr_control(SUNXI_ADC_DIG_CTRL, 0x1, ENAD, 0x0);
/*disable dac digital*/
codec_wr_control(SUNXI_DAC_DIG_CTRL, 0x1, ENDA, 0x0);
/*analog parts*/
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MMICBIASEN, 0x0);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, HMICBIAS_MODE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LMIXEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RMIXEN, 0x0);
/*disable dac analog*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACAREN, 0x0);
/*disable adc analog*/
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCREN, 0x0);
codec_wr_prcm_control(ADC_AP_EN, 0x1, ADCLEN, 0x0);
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x0);
/*codec_wr_prcm_control(LINEOUT_VOLC, 0x1f, LINEOUTVOL, 0x0);*/
if ((NULL == codec_moduleclk) || (IS_ERR(codec_moduleclk)))
pr_err("codec_moduleclk handle is invaled, just return\n");
else
clk_disable_unprepare(codec_moduleclk);
pr_debug("[audio codec]:suspend end\n");
return 0;
}
static int sndpcm_resume(struct snd_soc_codec *codec)
{
pr_debug("[audio codec]:resume start\n");
if ((!codec_moduleclk) || (IS_ERR(codec_moduleclk)))
pr_err("try to get codec_moduleclk failed!\n");
if (clk_prepare_enable(codec_moduleclk))
pr_err("open codec_moduleclk failed;\n");
if (headphone_direct_used) {
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x3);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, COMPTEN, 0x1);
} else {
codec_wr_prcm_control(PAEN_HP_CTRL, 0x3, HPCOM_FC, 0x0);
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, COMPTEN, 0x0);
}
if (drc_used) {
codec_wr_control(0x48c, 0x7ff, 0, 0x1);
/*codec_wr_control(0x48c, 0x7ff, 0, 0x0);*/
codec_wr_control(0x490, 0xffff, 0, 0x2baf);
/*codec_wr_control(0x490, 0xffff, 0, 0x1fb6);*/
codec_wr_control(0x494, 0x7ff, 0, 0x1);
codec_wr_control(0x498, 0xffff, 0, 0x2baf);
codec_wr_control(0x49c, 0x7ff, 0, 0x0);
codec_wr_control(0x4a0, 0xffff, 0, 0x44a);
codec_wr_control(0x4a4, 0x7ff, 0, 0x0);
codec_wr_control(0x4a8, 0xffff, 0, 0x1e06);
/*codec_wr_control(0x4ac, 0x7ff, 0, 0x27d);*/
codec_wr_control(0x4ac, 0x7ff, 0, 0x352);
/*codec_wr_control(0x4b0, 0xffff, 0, 0xcf68);*/
codec_wr_control(0x4b0, 0xffff, 0, 0x6910);
codec_wr_control(0x4b4, 0x7ff, 0, 0x77a);
codec_wr_control(0x4b8, 0xffff, 0, 0xaaaa);
/*codec_wr_control(0x4bc, 0x7ff, 0, 0x1fe);*/
codec_wr_control(0x4bc, 0x7ff, 0, 0x2de);
codec_wr_control(0x4c0, 0xffff, 0, 0xc982);
codec_wr_control(0x258, 0xffff, 0, 0x9f9f);
}
if (agc_used) {
codec_wr_control(0x4d0, 0x3, 6, 0x3);
codec_wr_control(0x410, 0x3f, 8, 0x31);
codec_wr_control(0x410, 0xff, 0, 0x28);
codec_wr_control(0x414, 0x3f, 8, 0x31);
codec_wr_control(0x414, 0xff, 0, 0x28);
codec_wr_control(0x428, 0x7fff, 0, 0x24);
codec_wr_control(0x42c, 0x7fff, 0, 0x2);
codec_wr_control(0x430, 0x7fff, 0, 0x24);
codec_wr_control(0x434, 0x7fff, 0, 0x2);
codec_wr_control(0x438, 0x1f, 8, 0xf);
codec_wr_control(0x438, 0x1f, 0, 0xf);
codec_wr_control(0x44c, 0x7ff, 0, 0xfc);
codec_wr_control(0x450, 0xffff, 0, 0xabb3);
}
gpio_direction_output(spk_gpio, 1);
gpio_set_value(spk_gpio, 0);
pr_debug("[audio codec]:resume end\n");
return 0;
}
/* power down chip */
static int sndpcm_soc_remove(struct snd_soc_codec *codec)
{
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_sndpcm = {
.probe = sndpcm_soc_probe,
.remove = sndpcm_soc_remove,
.suspend = sndpcm_suspend,
.resume = sndpcm_resume,
};
static ssize_t show_audio_reg(struct device *dev,
struct device_attribute *attr, char *buf)
{
int count = 0;
int i = 0;
int reg_group = 0;
pr_info("%s,line:%d\n", __func__, __LINE__);
count += sprintf(buf, "dump audio reg:\n");
while (reg_labels[i].name != NULL) {
if (reg_labels[i].value == 0)
reg_group++;
if (reg_group == 1) {
count += sprintf(buf + count, "%s 0x%p: 0x%x\n",
reg_labels[i].name,
(baseaddr + reg_labels[i].value),
readl(baseaddr + reg_labels[i].value));
} else if (reg_group == 2) {
count += sprintf(buf + count, "%s 0x%x: 0x%x\n",
reg_labels[i].name,
(reg_labels[i].value),
read_prcm_wvalue(reg_labels[i].value));
}
i++;
}
return count;
}
/* ex:
read:
echo 0,1,0x00> audio_reg
echo 0,2,0x00> audio_reg
write:
echo 1,1,0x00,0xa > audio_reg
echo 1,2,0x00,0xff > audio_reg
*/
static ssize_t store_audio_reg(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int ret;
int input_reg_group = 0;
int input_reg_offset = 0;
int input_reg_val = 0;
int reg_val_read;
int rw_flag;
pr_info("%s,line:%d\n", __func__, __LINE__);
ret = sscanf(buf, "%d,%d,0x%x,0x%x", &rw_flag, &input_reg_group,
&input_reg_offset, &input_reg_val);
pr_info("ret:%d, reg_group:%d, reg_offset:%d, reg_val:0x%x\n",
ret, input_reg_group, input_reg_offset, input_reg_val);
if (!(input_reg_group == 1 || input_reg_group == 2)) {
pr_info("not exist reg group\n");
ret = count;
goto out;
}
if (!(rw_flag == 1 || rw_flag == 0)) {
pr_info("not rw_flag\n");
ret = count;
goto out;
}
if (input_reg_group == 1) {
if (rw_flag) {
writel(input_reg_val, baseaddr + input_reg_offset);
} else {
reg_val_read = readl(baseaddr + input_reg_offset);
pr_info("\n\n Reg[0x%x] : 0x%x\n\n",
input_reg_offset, reg_val_read);
}
} else if (input_reg_group == 2) {
if (rw_flag) {
write_prcm_wvalue(input_reg_offset,
input_reg_val & 0xff);
} else {
reg_val_read = read_prcm_wvalue(input_reg_offset);
pr_info("\n\n Reg[0x%x] : 0x%x\n\n",
input_reg_offset, reg_val_read);
}
}
ret = count;
out:
return ret;
}
static DEVICE_ATTR(audio_reg, 0644, show_audio_reg, store_audio_reg);
static struct attribute *audio_debug_attrs[] = {
&dev_attr_audio_reg.attr,
NULL,
};
static struct attribute_group audio_debug_attr_group = {
.name = "audio_reg_debug",
.attrs = audio_debug_attrs,
};
static int __init sndpcm_codec_probe(struct platform_device *pdev)
{
int err = -1;
int ret;
int reg_val = 0;
int req_status;
u32 temp_val;
struct gpio_config config;
struct device_node *np = pdev->dev.of_node;
pr_debug("%s,line:%d\n", __func__, __LINE__);
/* codec_pll2clk */
codec_pll2clk = of_clk_get(np, 0);
if (IS_ERR_OR_NULL(codec_pll2clk)) {
dev_err(&pdev->dev, "pllclk get failed\n");
err = PTR_ERR(codec_pll2clk);
return err;
}
if (clk_prepare_enable(codec_pll2clk))
pr_err("[ audio ] err:enable codec_pll2clk failed;\n");
/* codec_moduleclk */
codec_moduleclk = of_clk_get(np, 1);
if (IS_ERR_OR_NULL(codec_moduleclk)) {
dev_err(&pdev->dev, "moduleclk get failed\n");
err = PTR_ERR(codec_moduleclk);
return err;
}
if (clk_set_parent(codec_moduleclk, codec_pll2clk))
pr_err("[ audio ]set parent of codec_moduleclk failed!\n");
if (clk_set_rate(codec_moduleclk, 24576000))
pr_err("[ audio ]set codec_moduleclk freq 24576000 failed!\n");
if (clk_prepare_enable(codec_moduleclk))
pr_err("[ audio ] err:open codec_moduleclk failed;\n");
/*clk pll_audiox4 for audiocodec src*/
codec_srcclk = of_clk_get(np, 2);
if (IS_ERR_OR_NULL(codec_srcclk)) {
dev_err(&pdev->dev, "codec_srcclk get failed\n");
err = PTR_ERR(codec_srcclk);
return err;
}
/* check if hp_vcc_ldo exist, if exist enable it */;
err = of_property_read_string(np, "audio_hp_ldo", &hp_ldo_str);
if (WARN_ON(err)) {
pr_err("get audio_hp_ldo failed\n");
} else {
if (!strcmp(hp_ldo_str, "none"))
hp_ldo = NULL;
else {
hp_ldo = regulator_get(NULL, hp_ldo_str);
if (!hp_ldo) {
pr_err("get hp-vcc(%s) failed\n", hp_ldo_str);
return -EFAULT;
}
regulator_set_voltage(hp_ldo, 3000000, 3000000);
regulator_enable(hp_ldo);
}
}
/*get the default pa ctl(close)*/
spk_gpio = of_get_named_gpio_flags(np, "gpio-spk",
0, (enum of_gpio_flags *)&config);
if (!gpio_is_valid(spk_gpio)) {
pr_err("failed to get gpio-spk from dts,(%d)\n", spk_gpio);
return -EFAULT;
} else {
err = devm_gpio_request(&pdev->dev, spk_gpio, "SPK");
if (err) {
pr_err("failed to request gpio-spk gpio\n");
return err;
} else {
gpio_direction_output(spk_gpio, 1);
gpio_set_value(spk_gpio, 0);
}
}
ret = of_property_read_u32(np, "headphone_vol", &temp_val);
if (ret < 0) {
dev_warn(&pdev->dev, "headphone_vol missing or invalid\n");
headphone_vol = 0;
} else {
headphone_vol = temp_val;
dev_dbg(&pdev->dev, "headphone_vol:%d\n", temp_val);
}
ret = of_property_read_u32(np, "earpiece_vol", &temp_val);
if (ret < 0) {
dev_warn(&pdev->dev, "earpiece_vol missing or invalid\n");
earpiece_vol = 0;
} else {
earpiece_vol = temp_val;
dev_dbg(&pdev->dev, "earpiece_vol:%d\n", temp_val);
}
ret = of_property_read_u32(np, "cap_vol", &temp_val);
if (ret < 0) {
dev_warn(&pdev->dev, "cap_vol missing or invalid\n");
cap_vol = 0;
} else {
cap_vol = temp_val;
dev_dbg(&pdev->dev, "cap_vol:%d\n", temp_val);
}
ret = of_property_read_u32(np, "headset_mic_vol", &temp_val);
if (ret < 0) {
dev_warn(&pdev->dev, "headset_mic_vol missing or invalid\n");
phone_headset_mic_vol = 0;
} else {
phone_headset_mic_vol = temp_val;
dev_dbg(&pdev->dev, "headset_mic_vol:%d\n", temp_val);
}
ret = of_property_read_u32(np, "main_mic_vol", &temp_val);
if (ret < 0) {
dev_warn(&pdev->dev, "main_mic_vol missing or invalid\n");
phone_main_mic_vol = 0;
} else {
phone_main_mic_vol = temp_val;
dev_dbg(&pdev->dev, "main_mic_vol:%d\n", temp_val);
}
ret = of_property_read_u32(np, "pa_double_used", &temp_val);
if (ret < 0) {
dev_warn(&pdev->dev, "pa_double_used missing or invalid\n");
pa_double_used = 0;
} else {
pa_double_used = temp_val;
dev_dbg(&pdev->dev, "pa_double_used:%d\n", temp_val);
}
if (!pa_double_used) {
ret = of_property_read_u32(np, "pa_single_vol", &temp_val);
if (ret < 0) {
dev_warn(&pdev->dev, "pa_single_vol invalid\n");
pa_vol = 0;
} else {
pa_vol = temp_val;
dev_dbg(&pdev->dev, "pa_single_vol:%d\n", temp_val);
}
} else {
ret = of_property_read_u32(np, "pa_double_vol", &temp_val);
if (ret < 0) {
dev_warn(&pdev->dev, "pa_double_vol invalid\n");
pa_vol = 0;
} else {
pa_vol = temp_val;
dev_dbg(&pdev->dev, "pa_double_vol:%d\n", temp_val);
}
}
ret = of_property_read_u32(np, "DAC_VOL_CTRL_SPK", &temp_val);
if (ret < 0) {
dev_warn(&pdev->dev, "DAC_VOL_CTRL_SPK missing or invalid\n");
dac_vol_ctrl_spk = 0;
} else {
dac_vol_ctrl_spk = temp_val;
dev_dbg(&pdev->dev, "DAC_VOL_CTRL_SPK:%d\n", temp_val);
}
ret = of_property_read_u32(np, "DAC_VOL_CTRL_HEADPHONE", &temp_val);
if (ret < 0) {
dev_warn(&pdev->dev, "DAC_VOL_CTRL_HEADPHONE invalid\n");
dac_vol_ctrl_headphone = 0;
} else {
dac_vol_ctrl_headphone = temp_val;
dev_dbg(&pdev->dev, "DAC_VOL_CTRL_HEADPHONE:%d\n", temp_val);
}
ret = of_property_read_u32(np, "headphone_direct_used", &temp_val);
if (ret < 0) {
dev_warn(&pdev->dev, "headphone_direct_used invalid\n");
headphone_direct_used = 0;
} else {
headphone_direct_used = temp_val;
dev_dbg(&pdev->dev, "headphone_direct_used:%d\n", temp_val);
}
ret = of_property_read_u32(np, "agc_used", &temp_val);
if (ret < 0) {
dev_warn(&pdev->dev, "agc_used missing or invalid\n");
agc_used = 0;
} else {
agc_used = temp_val;
dev_dbg(&pdev->dev, "agc_used:%d\n", temp_val);
}
ret = of_property_read_u32(np, "drc_used", &temp_val);
if (ret < 0) {
dev_warn(&pdev->dev, "drc_used missing or invalid\n");
drc_used = 0;
} else {
drc_used = temp_val;
dev_dbg(&pdev->dev, "drc_used:%d\n", temp_val);
}
codec_init();
/**
*If use the aif2,aif3 interface in the audiocodec,
* you need config the related interfaces about aif2 and aif3.
*And can not use daudio0 and daudio1
*/
ret = of_property_read_u32(np, "aif2_used", &temp_val);
if (ret < 0) {
dev_warn(&pdev->dev, "aif2_used missing or invalid\n");
aif2_used = 0;
} else {
aif2_used = temp_val;
dev_dbg(&pdev->dev, "aif2_used:%d\n", temp_val);
}
if (aif2_used) {
pr_debug("[audiocodec]: aif2 init PB04 PB05 PB06 PB07!!\n");
reg_val = readl((void __iomem *)0xf1c20824);
reg_val &= 0xffff;
reg_val |= (0x3333<<16);
writel(reg_val, (void __iomem *)0xf1c20824);
} else {
pr_err("[audiocodec] : aif2 not used!\n");
}
ret = of_property_read_u32(np, "aif3_used", &temp_val);
if (ret < 0) {
dev_warn(&pdev->dev, "aif3_used missing or invalid\n");
aif3_used = 0;
} else {
aif3_used = temp_val;
dev_dbg(&pdev->dev, "aif3_used:%d\n", temp_val);
}
if (aif3_used) {
pr_err("[audiocodec}: aif3 init PG10 PG11 PG12 PG13!!\n");
reg_val = readl((void __iomem *)0xf1c208dc);
reg_val &= 0xff;
reg_val |= (0x3333<<8);
writel(reg_val, (void __iomem *)0xf1c208dc);
} else {
pr_err("[audiocodec] : aif3 not used!\n");
}
codec_wr_prcm_control(PAEN_HP_CTRL, 0x1, HPPAEN, 0x1);
snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_sndpcm, &sndpcm_dai, 1);
err = sysfs_create_group(&pdev->dev.kobj, &audio_debug_attr_group);
if (err)
pr_err("failed to create attr group\n");
return 0;
}
static int __exit sndpcm_codec_remove(struct platform_device *pdev)
{
if ((NULL == codec_moduleclk) || (IS_ERR(codec_moduleclk))) {
pr_err("codec_moduleclk handle is invaled, just return\n");
return -EINVAL;
} else {
clk_disable_unprepare(codec_moduleclk);
}
if ((NULL == codec_pll2clk) || (IS_ERR(codec_pll2clk))) {
pr_err("codec_pll2clk handle is invaled, just return\n");
return -EINVAL;
} else {
clk_put(codec_pll2clk);
}
/* disable audio hp-vcc ldo if it exist */
if (hp_ldo) {
regulator_disable(hp_ldo);
hp_ldo = NULL;
}
sysfs_remove_group(&pdev->dev.kobj, &audio_debug_attr_group);
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static void sunxi_codec_shutdown(struct platform_device *devptr)
{
codec_wr_prcm_control(ADDA_APT2, 0x1, ZERO_CROSS_EN, 0x0);
/*mute l_pa and r_pa*/
codec_wr_prcm_control(DAC_PA_SRC, 0x1, LHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, RHPPAMUTE, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACALEN, 0x0);
codec_wr_prcm_control(DAC_PA_SRC, 0x1, DACAREN, 0x0);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, MMICBIASEN, 0x0);
codec_wr_prcm_control(MIC1G_MICBIAS_CTRL, 0x1, HMICBIAS_MODE, 0x0);
gpio_set_value(spk_gpio, 0);
if ((NULL == codec_moduleclk) || (IS_ERR(codec_moduleclk)))
pr_err("codec_moduleclk handle is invaled, just return\n");
else
clk_disable_unprepare(codec_moduleclk);
}
static const struct of_device_id sunxi_codec_of_match[] = {
{ .compatible = "allwinner,sunxi-internal-codec", },
{},
};
/*method relating*/
static struct platform_driver sndpcm_codec_driver = {
.driver = {
.name = "sunxi-pcm-codec",
.owner = THIS_MODULE,
.of_match_table = sunxi_codec_of_match,
},
.probe = sndpcm_codec_probe,
.remove = sndpcm_codec_remove,
.shutdown = sunxi_codec_shutdown,
};
static int __init sndpcm_codec_init(void)
{
int err = 0;
err = platform_driver_register(&sndpcm_codec_driver);
if (err < 0)
return err;
return 0;
}
module_init(sndpcm_codec_init);
static void __exit sndpcm_codec_exit(void)
{
platform_driver_unregister(&sndpcm_codec_driver);
}
module_exit(sndpcm_codec_exit);
MODULE_DESCRIPTION("SNDPCM ALSA soc codec driver");
MODULE_AUTHOR("huanxin<huanxin@reuuimllatech.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:sunxi-pcm-codec");