blob: 295868d71e52a278103ce3cba0eb3661c780e2a0 [file] [log] [blame]
/*
* es8374.c -- ES8374 ALSA SoC Audio Codec
*
* Copyright (C) 2016 Everest Semiconductor Co., Ltd
*
* Authors: XianqingZheng(xqzheng@ambarella.com)
*
*
* Based on es8374.c by David Yang(yangxiaohua@everest-semi.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/regmap.h>
#include <linux/stddef.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <mach/gpio.h>
#include <mach/pcu.h>
#include <linux/gpio.h>
#include <sound/jack.h>
#include <linux/irq.h>
#include "es8374.h"
/*
* The pre-declare for es8374 microphone type
*/
//#define es8374_digital_mic 0
//#define es8374_analog_mic 1
#define es8374_digital_mic 1
#define es8374_analog_mic 0
#define es8374_mic_type es8374_analog_mic
/*
* The pre-declare for PLL output divider
*/
#define es8374_pll_out_div2 2
#define es8374_pll_out_div4 4
#define es8374_pll_out_div8 8
#define es8374_pll_out_div es8374_pll_out_div8
/*
* The pre-declare for DVDD supply voltage
*/
#define es8374_dvdd_supply_1v8 0x18
#define es8374_dvdd_supply_2v5 0x25
#define es8374_dvdd_supply_3v3 0x33
#define es8374_dvdd_supply es8374_dvdd_supply_3v3
/*
* es8374 register cache
*/
static const u8 es8374_reg_defaults[ES8374_MAX_REGISTER] = {
0x03, 0x03, 0x00, 0x20, 0x00, 0x11, 0x01, 0x00,
0x20, 0x80, 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x40, 0x40, 0x9C, 0xBE, 0x00, 0xA0,
0xFC, 0x00, 0x18, 0x00, 0x10, 0x10, 0x00, 0x08,
0x08, 0xD4, 0x00, 0x00, 0x18, 0xC0, 0x1C, 0x00,
0xB0, 0x32, 0x03, 0x00, 0x0D, 0x06, 0x1F, 0xF7,
0xFD, 0xFF, 0x1F, 0xF7, 0xFD, 0xFF, 0x04, 0x01,
0xC0, 0x00, 0x02, 0x17, 0xFD, 0xFF, 0x07, 0xFD,
0xFF, 0x00, 0xFF, 0xBB, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#if 0
static u8 es8374_equalizer_src[] = {
0x0A, 0x9B, 0x32, 0x03, 0x5C, 0x5D, 0x4B, 0x24, 0x0A, 0x9B,
0x32, 0x03, 0x4C, 0x1F, 0x43, 0x05, 0x6D, 0x27, 0x54, 0x06,
0x4D, 0xE1, 0x32, 0x02, 0x3E, 0x55, 0x2A, 0x20, 0x4D, 0xE1,
0x32, 0x02, 0x2E, 0x17, 0x22, 0x01, 0x9F, 0xE7, 0x43, 0x25,
0x4B, 0xD9, 0x21, 0x01, 0xF9, 0xD4, 0x11, 0x21, 0x4B, 0xD9,
0x21, 0x01, 0xE9, 0x96, 0x19, 0x00, 0x4C, 0xE7, 0x22, 0x23,
};
#endif
/*
* The declaration about i2s digital serial port structor
*/
struct sp_config {
u8 spc, mmcc, spfs;
u32 srate;
u8 lrcdiv;
u8 sclkdiv;
};
/*
* codec private data
*/
struct es8374_private {
struct snd_soc_codec *codec;
u32 clk_id;
u32 mclk;
u32 sysclk;
/* platform dependant DVDD voltage configuration */
u8 dvdd_pwr_vol;
u8 pll_div;
bool dmic_enable;
int irq_num;
};
//struct es8374_private *es8374_data;
/*
* Define ADC and DAC Volume
*/
static const DECLARE_TLV_DB_SCALE(vdac_tlv, -9600, 50, 1);
static const DECLARE_TLV_DB_SCALE(adc_rec_tlv, -9600, 50, 1);
/*
* Define D2SE MIC BOOST GAIN
*/
static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, 0, 1500, 0);
/*
* Define LINE PGA GAIN
*/
static const DECLARE_TLV_DB_SCALE(linin_pga_tlv, -3, 300, 0);
/*
* Define dmic boost gain
*/
static const DECLARE_TLV_DB_SCALE(dmic_6db_scaleup_tlv, 0, 600, 0);
/*
* Definitiiion ALC noise gate type
*/
static const char * const ng_type_txt[] = {"Constant PGA Gain",
"Mute ADC Output"};
static const struct soc_enum ng_type =
SOC_ENUM_SINGLE(ES8374_ALC_NGTH_REG2B, 6, 2, ng_type_txt);
/*
* Definitiiion dac auto mute type
*/
static const char * const dac_auto_mute_type_txt[] = {
"AUTO MUTE DISABLE",
"MONO OUTPUT MUTE",
"SPEAKER MUTE",
"MONO OUT & SPEAKER MUTE"
};
static const struct soc_enum dac_auto_mute_type =
SOC_ENUM_SINGLE(ES8374_DAC_CONTROL_REG37, 4, 4, dac_auto_mute_type_txt);
/*
* Definitiiion dac dsm mute type
*/
static const char * const dac_dsm_mute_type_txt[] = {
"DAC DSM UNMUTE",
"DAC DSM MUTE",
};
static const struct soc_enum dac_dsm_mute_type =
SOC_ENUM_SINGLE(ES8374_DAC_CONTROL_REG37, 0, 2, dac_dsm_mute_type_txt);
#ifdef _USE_7520V3_PHONE_TYPE_WTWD
static const DECLARE_TLV_DB_SCALE(spk_vol_tlv, 0, 150, 0);
/*
* Define MONO output gain
*/
static const DECLARE_TLV_DB_RANGE(mono_out_gain_tlv, \
48,52,TLV_DB_SCALE_ITEM(-2150, 150, 0), \
56,59,TLV_DB_SCALE_ITEM(-1400, 150, 0), \
16,20,TLV_DB_SCALE_ITEM(-800, 150, 0), \
24,27,TLV_DB_SCALE_ITEM(-500, 150, 0), \
);
/*
* Define spk mix gain
*/
static const DECLARE_TLV_DB_RANGE(spk_mix_tlv, \
32,36,TLV_DB_SCALE_ITEM(-2150, 150, 0), \
40,43,TLV_DB_SCALE_ITEM(-1400, 150, 0), \
0,4,TLV_DB_SCALE_ITEM(-800, 150, 0), \
8,11,TLV_DB_SCALE_ITEM(-500, 150, 0), \
);
#else
/*
* Define MONO output gain
*/
static const DECLARE_TLV_DB_SCALE(mono_out_gain_tlv, 0, 150, 0);
#endif
/*
* es8374 Controls
*/
static const struct snd_kcontrol_new es8374_snd_controls[] = {
/*
* controls for capture path
*/
SOC_SINGLE_TLV("D2SE MIC BOOST GAIN",
ES8374_AIN_PWR_SRC_REG21, 2, 1, 0, mic_boost_tlv),
SOC_SINGLE_TLV("LIN PGA GAIN",
ES8374_AIN_PGA_REG22, 0, 15, 0, linin_pga_tlv),
SOC_SINGLE_TLV("DMIC 6DB SCALE UP GAIN",
ES8374_ADC_CONTROL_REG24, 7, 1, 0, dmic_6db_scaleup_tlv),
SOC_SINGLE("ADC Double FS Mode", ES8374_ADC_CONTROL_REG24, 6, 1, 0),
SOC_SINGLE("ADC Soft Ramp", ES8374_ADC_CONTROL_REG24, 4, 1, 0),
SOC_SINGLE("ADC MUTE", ES8374_ADC_CONTROL_REG24, 5, 1, 0),
SOC_SINGLE("ADC INVERTED", ES8374_ADC_CONTROL_REG24, 2, 1, 0),
SOC_SINGLE("ADC HPF COEFFICIENT", ES8374_ADC_HPF_REG2C, 0, 31, 0),
SOC_SINGLE_TLV("ADC Capture Volume",
ES8374_ADC_VOLUME_REG25, 0, 192, 1, adc_rec_tlv),
SOC_SINGLE("ALC Capture Target Volume", ES8374_ALC_LVL_HLD_REG28, 4, 15, 0),
SOC_SINGLE("ALC Capture Max PGA", ES8374_ALC_EN_MAX_GAIN_REG26, 0, 31, 0),
SOC_SINGLE("ALC Capture Min PGA", ES8374_ALC_MIN_GAIN_REG27, 0, 31, 0),
SOC_SINGLE("ALC Capture Hold Time", ES8374_ALC_LVL_HLD_REG28, 0, 15, 0),
SOC_SINGLE("ALC Capture Decay Time", ES8374_ALC_DCY_ATK_REG29, 4, 15, 0),
SOC_SINGLE("ALC Capture Attack Time", ES8374_ALC_DCY_ATK_REG29, 0, 15, 0),
SOC_SINGLE("ALC WIN SIZE", ES8374_ALC_WIN_SIZE_REG2A, 0, 31, 0),
SOC_SINGLE("ALC Capture NG Threshold", ES8374_ALC_NGTH_REG2B, 0, 31, 0),
SOC_ENUM("ALC Capture NG Type", ng_type),
SOC_SINGLE("ALC Capture NG Switch", ES8374_ALC_NGTH_REG2B, 5, 1, 0),
/*
* controls for playback path
*/
SOC_SINGLE("DAC Double FS Mode", ES8374_DAC_CONTROL_REG37, 7, 1, 0),
SOC_SINGLE("DAC Soft Ramp Rate", ES8374_DAC_CONTROL_REG36, 2, 7, 0),
SOC_SINGLE("DAC MUTE", ES8374_DAC_CONTROL_REG36, 5, 1, 0),
SOC_SINGLE("DAC OFFSET", ES8374_DAC_OFFSET_REG39, 0, 255, 0),
SOC_ENUM("DAC AUTO MUTE TYPE", dac_auto_mute_type),
SOC_ENUM("DAC DSM MUTE TYPE", dac_dsm_mute_type),
SOC_SINGLE_TLV("DAC Playback Volume",
ES8374_DAC_VOLUME_REG38, 0, 192, 1, vdac_tlv),
#ifdef _USE_7520V3_PHONE_TYPE_WTWD
SOC_SINGLE_TLV("MONO OUT GAIN",
ES8374_MONO_GAIN_REG1B, 0, 59, 0, mono_out_gain_tlv),
SOC_SINGLE_TLV("SPEAKER MIXER GAIN",
ES8374_SPK_MIX_GAIN_REG1D, 0, 43, 0, spk_mix_tlv),
SOC_SINGLE_TLV("SPEAKER OUTPUT Volume",
ES8374_SPK_OUT_GAIN_REG1E, 0, 7, 0, spk_vol_tlv),
#else
SOC_SINGLE_TLV("MONO OUT GAIN",
ES8374_MONO_GAIN_REG1B, 0, 15, 0, mono_out_gain_tlv),
SOC_SINGLE_TLV("SPEAKER MIXER GAIN",
ES8374_SPK_MIX_GAIN_REG1D, 0, 15, 0, mono_out_gain_tlv),
SOC_SINGLE_TLV("SPEAKER OUTPUT Volume",
ES8374_SPK_OUT_GAIN_REG1E, 0, 7, 0, mono_out_gain_tlv),
#endif
};
/*
* DAPM Controls
*/
/*
* alc on/off
*/
static const char * const es8374_alc_enable_txt[] = {
"ALC OFF",
"ALC ON",
};
static const unsigned int es8374_alc_enable_values[] = {
0, 1};
static const struct soc_enum es8374_alc_enable_enum =
SOC_VALUE_ENUM_SINGLE(ES8374_ALC_EN_MAX_GAIN_REG26, 6, 1,
ARRAY_SIZE(es8374_alc_enable_txt),
es8374_alc_enable_txt,
es8374_alc_enable_values);
static const struct snd_kcontrol_new es8374_alc_enable_controls =
SOC_DAPM_ENUM("Route", es8374_alc_enable_enum);
/*
* adc line in select
*/
static const char * const es8374_adc_input_src_txt[] = {
"NO-IN",
"LIN1-RIN1",
"LIN2-RIN2",
"LIN1-LIN2",
};
static const unsigned int es8374_adc_input_src_values[] = {
0, 1, 2, 3};
static const struct soc_enum es8374_adc_input_src_enum =
SOC_VALUE_ENUM_SINGLE(ES8374_AIN_PWR_SRC_REG21, 4, 3,
ARRAY_SIZE(es8374_adc_input_src_txt),
es8374_adc_input_src_txt,
es8374_adc_input_src_values);
static const struct snd_kcontrol_new es8374_adc_input_src_controls =
SOC_DAPM_ENUM("Route", es8374_adc_input_src_enum);
/*
* ANALOG IN MUX
*/
static const char * const es8374_analog_input_mux_txt[] = {
"LIN1",
"LIN2",
"DIFF OUT1",
"DIFF OUT2",
"PGA OUT1",
"PGA OUT2"
};
static const unsigned int es8374_analog_input_mux_values[] = {
0, 1, 2, 3, 4, 5};
static const struct soc_enum es8374_analog_input_mux_enum =
SOC_VALUE_ENUM_SINGLE(ES8374_MONO_MIX_REG1A, 0, 7,
ARRAY_SIZE(es8374_analog_input_mux_txt),
es8374_analog_input_mux_txt,
es8374_analog_input_mux_values);
static const struct snd_kcontrol_new es8374_analog_input_mux_controls =
SOC_DAPM_ENUM("Route", es8374_analog_input_mux_enum);
/*
* MONO OUTPUT MIXER
*/
static const struct snd_kcontrol_new es8374_mono_out_mixer_controls[] = {
SOC_DAPM_SINGLE("LIN TO MONO OUT Switch", ES8374_MONO_MIX_REG1A, 6, 1, 0),
SOC_DAPM_SINGLE("DAC TO MONO OUT Switch", ES8374_MONO_MIX_REG1A, 7, 1, 0),
};
/*
* SPEAKER OUTPUT MIXER
*/
static const struct snd_kcontrol_new es8374_speaker_mixer_controls[] = {
SOC_DAPM_SINGLE("LIN TO SPEAKER OUT Switch", ES8374_SPK_MIX_REG1C, 6, 1, 0),
SOC_DAPM_SINGLE("DAC TO SPEAKER OUT Switch", ES8374_SPK_MIX_REG1C, 7, 1, 0),
};
/*
* digital microphone soure
*/
static const char * const es8374_dmic_mux_txt[] = {
"DMIC DISABLE1",
"DMIC DISABLE2",
"DMIC AT HIGH LEVEL",
"DMIC AT LOW LEVEL",
};
static const unsigned int es8374_dmic_mux_values[] = {
0, 1, 2, 3};
static const struct soc_enum es8374_dmic_mux_enum =
SOC_VALUE_ENUM_SINGLE(ES8374_ADC_CONTROL_REG24, 0, 3,
ARRAY_SIZE(es8374_dmic_mux_txt),
es8374_dmic_mux_txt,
es8374_dmic_mux_values);
static const struct snd_kcontrol_new es8374_dmic_mux_controls =
SOC_DAPM_ENUM("Route", es8374_dmic_mux_enum);
/*
* ADC sdp soure
*/
static const char * const es8374_adc_sdp_mux_txt[] = {
"FROM ADC OUT",
"FROM EQUALIZER",
};
static const unsigned int es8374_adc_sdp_mux_values[] = {
0, 1};
static const struct soc_enum es8374_adc_sdp_mux_enum =
SOC_VALUE_ENUM_SINGLE(ES8374_EQ_SRC_REG2D, 7, 1,
ARRAY_SIZE(es8374_adc_sdp_mux_txt),
es8374_adc_sdp_mux_txt,
es8374_adc_sdp_mux_values);
static const struct snd_kcontrol_new es8374_adc_sdp_mux_controls =
SOC_DAPM_ENUM("Route", es8374_adc_sdp_mux_enum);
/*
* DAC dsm soure
*/
static const char * const es8374_dac_dsm_mux_txt[] = {
"FROM SDP IN",
"FROM EQUALIZER",
};
static const unsigned int es8374_dac_dsm_mux_values[] = {
0, 1};
static const struct soc_enum es8374_dac_dsm_mux_enum =
SOC_VALUE_ENUM_SINGLE(ES8374_EQ_SRC_REG2D, 6, 1,
ARRAY_SIZE(es8374_dac_dsm_mux_txt),
es8374_dac_dsm_mux_txt,
es8374_dac_dsm_mux_values);
static const struct snd_kcontrol_new es8374_dac_dsm_mux_controls =
SOC_DAPM_ENUM("Route", es8374_dac_dsm_mux_enum);
/*
* equalizer data soure
*/
static const char * const es8374_equalizer_src_mux_txt[] = {
"FROM ADC OUT",
"FROM SDP IN",
};
static const unsigned int es8374_equalizer_src_mux_values[] = {
0, 1};
static const struct soc_enum es8374_equalizer_src_mux_enum =
SOC_VALUE_ENUM_SINGLE(ES8374_EQ_SRC_REG2D, 5, 1,
ARRAY_SIZE(es8374_equalizer_src_mux_txt),
es8374_equalizer_src_mux_txt,
es8374_equalizer_src_mux_values);
static const struct snd_kcontrol_new es8374_equalizer_src_mux_controls =
SOC_DAPM_ENUM("Route", es8374_equalizer_src_mux_enum);
/*
* DAC data soure
*/
static const char * const es8374_dac_data_mux_txt[] = {
"SELECT SDP LEFT DATA",
"SELECT SDP RIGHT DATA",
};
static const unsigned int es8374_dac_data_mux_values[] = {
0, 1};
static const struct soc_enum es8374_dac_data_mux_enum =
SOC_VALUE_ENUM_SINGLE(ES8374_DAC_CONTROL_REG36, 6, 1,
ARRAY_SIZE(es8374_dac_data_mux_txt),
es8374_dac_data_mux_txt,
es8374_dac_data_mux_values);
static const struct snd_kcontrol_new es8374_dac_data_mux_controls =
SOC_DAPM_ENUM("Route", es8374_dac_data_mux_enum);
static int es8374_adc_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
switch (event) {
case SND_SOC_DAPM_POST_PMU:
//snd_soc_update_bits(w->codec, ES8374_CLK_MANAGEMENT_REG01, 0x08, 0x08);
//snd_soc_update_bits(w->codec, ES8374_CLK_MANAGEMENT_REG01, 0x02, 0x02);
//snd_soc_write(w->codec, ES8374_AIN_PGA_REG22, 0x77);
snd_soc_update_bits(w->codec, ES8374_AIN_PWR_SRC_REG21, 0xc0, 0x00);
snd_soc_update_bits(w->codec, ES8374_ANA_PWR_CTL_REG15, 0xdc,0x40);
return 0;
case SND_SOC_DAPM_POST_PMD:
snd_soc_write(w->codec, ES8374_ADC_VOLUME_REG25, 0xc0);
snd_soc_write(w->codec, ES8374_ALC_LVL_HLD_REG28, 0x1C);
snd_soc_write(w->codec, ES8374_ALC_EN_MAX_GAIN_REG26, 0x00);
snd_soc_write(w->codec, ES8374_AIN_PGA_REG22, 0x00);
snd_soc_write(w->codec, ES8374_AIN_PWR_SRC_REG21, 0xD4);
//snd_soc_update_bits(w->codec, ES8374_ANA_PWR_CTL_REG15, 0x0c,0x0c);
//snd_soc_update_bits(w->codec, ES8374_CLK_MANAGEMENT_REG01, 0x0a, 0x00);
return 0;
default:
dev_dbg(w->codec->dev,
"Alsa Unhandled dapm widget event %d from %s\n",
event, w->name);
return 0;
}
return 0;
}
static int es8374_mono_out_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa mono out pevent wname=%s,stream=%s,event=%d ,REG1C=%x \n" ,w->name,w->sname,event,snd_soc_read(w->codec, ES8374_SPK_MIX_REG1C));
switch (event) {
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(w->codec, ES8374_ANA_PWR_CTL_REG15, 0xf2,0x40);
snd_soc_update_bits(w->codec, ES8374_MONO_MIX_REG1A, 0x38, 0x20);
//snd_soc_update_bits(w->codec, ES8374_DAC_CONTROL_REG36, 0x04, 0x04);
snd_soc_update_bits(w->codec, ES8374_DAC_CONTROL_REG37, 0x3f, 0x00);
return 0;
case SND_SOC_DAPM_POST_PMD:
//snd_soc_write(w->codec, ES8374_DAC_CONTROL_REG36, 0x20);
//snd_soc_write(w->codec, ES8374_DAC_CONTROL_REG37, 0x01);
snd_soc_write(w->codec, ES8374_MONO_MIX_REG1A, 0x08);
//snd_soc_update_bits(w->codec, ES8374_ANA_PWR_CTL_REG15, 0x22,0x22);
//snd_soc_update_bits(w->codec, ES8374_CLK_MANAGEMENT_REG01, 0x05, 0x05);
return 0;
default:
dev_dbg(w->codec->dev,
"Alsa Unhandled dapm widget event %d from %s\n",
event, w->name);
return 0;
}
return 0;
}
static int es8374_classD_out_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
CPPS_FUNC(cpps_callbacks, zDrv_Audio_Printf)("Alsa class-d pevent wname=%s,stream=%s,event=%d,REG1C=%x \n" ,w->name,w->sname,event,snd_soc_read(w->codec, ES8374_SPK_MIX_REG1C));
switch (event) {
case SND_SOC_DAPM_POST_PMU:
//snd_soc_update_bits(w->codec, ES8374_CLK_MANAGEMENT_REG01, 0x05, 0x05);
snd_soc_update_bits(w->codec, ES8374_ANA_PWR_CTL_REG15, 0xf2,0x40);
snd_soc_update_bits(w->codec, ES8374_SPK_MIX_REG1C, 0x10, 0x10);
snd_soc_update_bits(w->codec, ES8374_SPK_MIX_GAIN_REG1D, 0xf0, 0x00);
snd_soc_update_bits(w->codec, ES8374_SPK_OUT_GAIN_REG1E, 0xf8, 0xa0);
//snd_soc_update_bits(w->codec, ES8374_DAC_CONTROL_REG36, 0x04, 0x04);
snd_soc_update_bits(w->codec, ES8374_DAC_CONTROL_REG37, 0x3f, 0x00);
return 0;
case SND_SOC_DAPM_POST_PMD:
//snd_soc_write(w->codec, ES8374_DAC_CONTROL_REG36, 0x20);
//snd_soc_write(w->codec, ES8374_DAC_CONTROL_REG37, 0x01);
snd_soc_write(w->codec, ES8374_SPK_MIX_GAIN_REG1D, 0x10);
snd_soc_write(w->codec, ES8374_SPK_OUT_GAIN_REG1E, 0x40);
snd_soc_write(w->codec, ES8374_SPK_MIX_REG1C, 0x10);
//snd_soc_update_bits(w->codec, ES8374_ANA_PWR_CTL_REG15, 0x22,0x22);
//snd_soc_update_bits(w->codec, ES8374_CLK_MANAGEMENT_REG01, 0x05, 0x05);
return 0;
default:
dev_dbg(w->codec->dev,
"Alsa Unhandled dapm widget event %d from %s\n",
event, w->name);
return 0;
}
return 0;
}
static const struct snd_soc_dapm_widget es8374_dapm_widgets[] = {
/* Input Lines */
SND_SOC_DAPM_INPUT("DMIC"),
SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_INPUT("MIC2"),
SND_SOC_DAPM_INPUT("LIN1"),
SND_SOC_DAPM_INPUT("LIN2"),
/*
* Capture path
*/
// SND_SOC_DAPM_MICBIAS("micbias", ES8374_ANA_REF_REG14,
// 4, 1),
SND_SOC_DAPM_SUPPLY("micbias", ES8374_ANA_REF_REG14,
4, 1, NULL, 0),
/* Input MUX */
SND_SOC_DAPM_MUX("DIFFERENTIAL MUX", SND_SOC_NOPM, 0, 0,
&es8374_adc_input_src_controls),
SND_SOC_DAPM_PGA("DIFFERENTIAL PGA", SND_SOC_NOPM,
0, 0, NULL, 0),
SND_SOC_DAPM_PGA("LINE PGA", SND_SOC_NOPM,
0, 0, NULL, 0),
/* ADCs */
// SND_SOC_DAPM_ADC("MONO ADC", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_ADC_E("MONO ADC", "Mono Capture", SND_SOC_NOPM, 0, 0,
es8374_adc_power_event, SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_POST_PMD),
/* Dmic MUX */
SND_SOC_DAPM_MUX("DMIC MUX", SND_SOC_NOPM, 0, 0,
&es8374_dmic_mux_controls),
/* Dmic MUX */
SND_SOC_DAPM_MUX("ALC MUX", SND_SOC_NOPM, 0, 0,
&es8374_alc_enable_controls),
/* sdp MUX */
SND_SOC_DAPM_MUX("SDP OUT MUX", SND_SOC_NOPM, 0, 0,
&es8374_adc_sdp_mux_controls),
/* Digital Interface */
SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture", 1,
SND_SOC_NOPM, 0, 0),
/*
* Render path
*/
SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0,
SND_SOC_NOPM, 0, 0),
/* DACs SDP DATA SRC MUX */
SND_SOC_DAPM_MUX("DAC SDP SRC MUX", SND_SOC_NOPM, 0, 0,
&es8374_dac_data_mux_controls),
/* DACs DATA SRC MUX */
SND_SOC_DAPM_MUX("DAC SRC MUX", SND_SOC_NOPM, 0, 0,
&es8374_dac_dsm_mux_controls),
SND_SOC_DAPM_DAC("MONO DAC", NULL, SND_SOC_NOPM, 0, 0),
/* hpmux for hp mixer */
SND_SOC_DAPM_MUX("ANALOG INPUT MUX", SND_SOC_NOPM, 0, 0,
&es8374_analog_input_mux_controls),
/* Output mixer */
SND_SOC_DAPM_MIXER_E("MONO MIXER", SND_SOC_NOPM,
0, 0, &es8374_mono_out_mixer_controls[0], ARRAY_SIZE(es8374_mono_out_mixer_controls),
es8374_mono_out_power_event, SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MIXER_E("SPEAKER MIXER", SND_SOC_NOPM,
0, 0, &es8374_speaker_mixer_controls[0], ARRAY_SIZE(es8374_speaker_mixer_controls),
es8374_classD_out_power_event, SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_POST_PMD),
/*
* Equalizer path
*/
SND_SOC_DAPM_MUX("EQUALIZER MUX", SND_SOC_NOPM, 0, 0,
&es8374_equalizer_src_mux_controls),
/* Output Lines */
SND_SOC_DAPM_OUTPUT("MOUT"),
SND_SOC_DAPM_OUTPUT("SPKOUT"),
};
static const struct snd_soc_dapm_route es8374_dapm_routes[] = {
/*
* record route map
*/
{"MIC1", NULL, "micbias"},
{"MIC2", NULL, "micbias"},
{"DMIC", NULL, "micbias"},
{"DIFFERENTIAL MUX", "LIN1-RIN1", "MIC1"},
{"DIFFERENTIAL MUX", "LIN2-RIN2", "MIC2"},
// {"micbias", NULL, "DIFFERENTIAL MUX"},
// {"DIFFERENTIAL PGA", NULL, "micbias"},
{"DIFFERENTIAL PGA", NULL, "DIFFERENTIAL MUX"},
{"LINE PGA", NULL, "DIFFERENTIAL PGA"},
{"MONO ADC", NULL, "LINE PGA"},
{"DMIC MUX", "DMIC DISABLE1", "MONO ADC"},
{"DMIC MUX", "DMIC DISABLE2", "MONO ADC"},
{"DMIC MUX", "DMIC AT HIGH LEVEL", "DMIC"},
{"DMIC MUX", "DMIC AT LOW LEVEL", "DMIC"},
{"ALC MUX", "ALC OFF", "DMIC MUX"},
{"ALC MUX", "ALC ON", "DMIC MUX"},
#if 0
/*
* Equalizer path
*/
{"EQUALIZER MUX", "FROM ADC OUT", "ALC MUX"},
{"EQUALIZER MUX", "FROM SDP IN", "I2S IN"},
{"SDP OUT MUX", "FROM ADC OUT", "ALC MUX"},
{"SDP OUT MUX", "FROM EQUALIZER", "EQUALIZER MUX"},
{"I2S OUT", NULL, "SDP OUT MUX"},
/*
* playback route map
*/
{"DAC SDP SRC MUX", "SELECT SDP LEFT DATA", "I2S IN"},
{"DAC SDP SRC MUX", "SELECT SDP RIGHT DATA", "I2S IN"},
{"DAC SRC MUX", "FROM SDP IN", "DAC SDP SRC MUX"},
{"DAC SRC MUX", "FROM EQUALIZER", "EQUALIZER MUX"},
{"MONO DAC", NULL, "DAC SRC MUX"},
#endif
#if 1
/*
* Equalizer path
*/
{"EQUALIZER MUX", "FROM ADC OUT", "ALC MUX"},
{"EQUALIZER MUX", "FROM SDP IN", "I2S IN"},
{"SDP OUT MUX", "FROM ADC OUT", "ALC MUX"},
{"SDP OUT MUX", "FROM EQUALIZER", "EQUALIZER MUX"},
{"I2S OUT", NULL, "SDP OUT MUX"},
/*
* playback route map
*/
{"DAC SRC MUX", "FROM SDP IN", "I2S IN"},
// {"DAC SRC MUX", "FROM SDP IN", "DAC SDP SRC MUX"},
{"DAC SRC MUX", "FROM EQUALIZER", "EQUALIZER MUX"},
{"DAC SDP SRC MUX", "SELECT SDP LEFT DATA", "DAC SRC MUX"},
{"DAC SDP SRC MUX", "SELECT SDP RIGHT DATA", "DAC SRC MUX"},
{"MONO DAC", NULL, "DAC SDP SRC MUX"},
// {"MONO DAC", NULL, "DAC SRC MUX"},
#endif
{"ANALOG INPUT MUX", "LIN1", "LIN1"},
{"ANALOG INPUT MUX", "LIN2", "LIN2"},
{"ANALOG INPUT MUX", "DIFF OUT1", "DIFFERENTIAL MUX"},
{"ANALOG INPUT MUX", "DIFF OUT2", "DIFFERENTIAL PGA"},
{"ANALOG INPUT MUX", "PGA OUT1", "LINE PGA"},
{"ANALOG INPUT MUX", "PGA OUT2", "LINE PGA"},
{"MONO MIXER", "LIN TO MONO OUT Switch", "ANALOG INPUT MUX"},
{"MONO MIXER", "DAC TO MONO OUT Switch", "MONO DAC"},
{"SPEAKER MIXER", "LIN TO SPEAKER OUT Switch", "ANALOG INPUT MUX"},
{"SPEAKER MIXER", "DAC TO SPEAKER OUT Switch", "MONO DAC"},
{"MOUT", NULL, "MONO MIXER"},
{"SPKOUT", NULL, "SPEAKER MIXER"},
};
/*
* pll configuration structor
*/
struct _pll_coeff_div {
u32 freq_in; /* input frequency */
u32 freq_out; /* output frequency */
u8 mclkdiv2; /* mclk div2 */
u8 plldiv; /* pll output divider */
u8 pll_n; /* pll N */
u8 pll_k1; /* pll K */
u8 pll_k2; /* PLL K */
u8 pll_k3; /* PLL K */
};
/*
* codec pll coefficients
* this table is used for the non-standard audio clock condition
*/
static const struct _pll_coeff_div pll_coeff_div[] = {
{6000000 , 12288000, 0, 4, 0x08, 0x08, 0x13, 0xe1},
{6000000 , 11289600, 0, 4, 0x07, 0x16, 0x25, 0x6d},
{8000000 , 12288000, 0, 4, 0x06, 0x06, 0x0e, 0xe9},
{8000000 , 11289600, 0, 8, 0x0b, 0x0c, 0x2f, 0x0c},
{8192000 , 12288000, 0, 4, 0x06, 0x00, 0x00, 0x01},
{8192000 , 11289600, 0, 8, 0x0b, 0x01, 0x0d, 0x42},
{12000000, 12288000, 0, 8, 0x08, 0x08, 0x13, 0xe1},
{12000000, 11289600, 0, 8, 0x07, 0x16, 0x25, 0x6d},
{13000000, 12288000, 0, 8, 0x08, 0x17, 0xa3, 0x30},
{13000000, 11289600, 0, 8, 0x06, 0x27, 0xdc, 0x2c},
{16000000, 12288000, 0, 8, 0x06, 0x06, 0x0e, 0xe9},
{16000000, 11289600, 1, 8, 0x0b, 0x0c, 0x2f, 0x0c},
{16384000, 12288000, 0, 8, 0x06, 0x00, 0x00, 0x01},
{16384000, 11289600, 1, 8, 0x0b, 0x01, 0x0d, 0x42},
{19200000, 12288000, 1, 8, 0x0a, 0x0a, 0x18, 0xd9},
{19200000, 11289600, 1, 8, 0x09, 0x11, 0x2a, 0x3d},
{24000000, 12288000, 1, 8, 0x08, 0x08, 0x13, 0xe1},
{24000000, 11289600, 1, 8, 0x07, 0x16, 0x25, 0x6d},
{25000000, 12288000, 1, 8, 0x07, 0x24, 0x5c, 0xe3},
{25000000, 11289600, 1, 8, 0x07, 0x09, 0x7b, 0x00},
{26000000, 12288000, 1, 8, 0x07, 0x17, 0xa3, 0x30},
{26000000, 11289600, 1, 8, 0x06, 0x27, 0xdc, 0x2c},
{27000000, 12288000, 1, 8, 0x07, 0x0b, 0xda, 0xcd},
{27000000, 11289600, 1, 8, 0x06, 0x1d, 0x08, 0xdd},
{32000000, 12288000, 1, 8, 0x06, 0x06, 0x0e, 0xe9},
{32000000, 11289600, 1, 8, 0x05, 0x1b, 0x20, 0x9e},
};
static inline int get_pll_coeff(int freqin, int freqout)
{
int i;
for (i = 0; i < ARRAY_SIZE(pll_coeff_div); i++) {
if (pll_coeff_div[i].freq_in == freqin && \
pll_coeff_div[i].freq_out == freqout)
return i;
}
return -EINVAL;
}
/*
* a structor for standard clock divider configration
*/
struct _coeff_div {
u32 mclk; /* mclk frequency */
u32 rate; /* sample rate */
u8 mclkdiv2; /* mclkdiv2 */
u8 adc_dac_div; /* internal adc&dac clock divider */
u8 lrck_h; /* adc&dac lrck divider */
u8 lrck_l;
u8 sclk_div; /* sclk divider */
u8 doublespeed;
u8 osr; /* adc osr */
};
/*
* clock configuration
* this table is used for standard clock condition
*/
static const struct _coeff_div coeff_div[] = {
/* 8k */
{1024000 , 8000, 0, 0x11, 0x00, 0x80, 0x04, 1, 0x20},
{2048000 , 8000, 0, 0x11, 0x01, 0x00, 0x08, 0, 0x20},
{3072000 , 8000, 0, 0x11, 0x01, 0x80, 0x0c, 1, 0x30},
{4096000 , 8000, 1, 0x11, 0x00, 0x00, 0x08, 0, 0x20},
{6144000 , 8000, 0, 0x33, 0x03, 0x00, 0x15, 0, 0x20},
{8192000 , 8000, 1, 0x22, 0x02, 0x00, 0x10, 0, 0x20},
{12288000, 8000, 0, 0x66, 0x06, 0x00, 0x1d, 0, 0x20},
{16384000, 8000, 1, 0x44, 0x04, 0x00, 0x18, 0, 0x20},
{24576000, 8000, 1, 0x66, 0x06, 0x00, 0x1d, 0, 0x20},
/* 11.025k */
{11289600, 11025, 0, 0x44, 0x04, 0x00, 0x18, 0, 0x20},
{22579200, 11025, 0, 0x44, 0x04, 0x00, 0x18, 0, 0x20},
/* 12k */
{3072000 , 12000, 0, 0x11, 0x01, 0x00, 0x08, 0, 0x20},
{6144000 , 12000, 0, 0x22, 0x01, 0x00, 0x08, 0, 0x20},
{12288000, 12000, 0, 0x44, 0x04, 0x00, 0x18, 0, 0x20},
{24576000, 12000, 1, 0x44, 0x04, 0x00, 0x18, 0, 0x20},
/* 16k */
{2048000 , 16000, 0, 0x11, 0x00, 0x80, 0x04, 1, 0x20},
{3072000 , 16000, 0, 0x11, 0x00, 0xc0, 0x06, 1, 0x30},
{4096000 , 16000, 0, 0x11, 0x01, 0x00, 0x08, 0, 0x20},
{6144000 , 16000, 0, 0x11, 0x01, 0x80, 0x0c, 0, 0x30},
{8192000 , 16000, 0, 0x22, 0x02, 0x00, 0x10, 0, 0x20},
{12288000, 16000, 0, 0x33, 0x03, 0x00, 0x15, 0, 0x20},
{16384000, 16000, 1, 0x22, 0x02, 0x00, 0x10, 0, 0x20},
{24576000, 16000, 1, 0x33, 0x03, 0x00, 0x15, 0, 0x20},
/* 22.05k */
{11289600, 22050, 0, 0x22, 0x02, 0x00, 0x10, 0, 0x20},
{22579200, 22050, 1, 0x22, 0x02, 0x00, 0x10, 0, 0x20},
/* 24k */
{3072000 , 24000, 0, 0x11, 0x00, 0x80, 0x04, 1, 0x20},
{6144000 , 24000, 0, 0x11, 0x01, 0x00, 0x08, 0, 0x20},
{12288000, 24000, 0, 0x22, 0x02, 0x00, 0x10, 0, 0x20},
{24576000, 24000, 1, 0x22, 0x02, 0x00, 0x10, 0, 0x20},
/* 32k */
{4096000 , 32000, 0, 0x11, 0x00, 0x80, 0x04, 1, 0x20},
{6144000 , 32000, 0, 0x11, 0x00, 0xc0, 0x06, 1, 0x30},
{8192000 , 32000, 0, 0x11, 0x01, 0x00, 0x08, 0, 0x20},
{16384000, 32000, 1, 0x11, 0x01, 0x00, 0x08, 0, 0x20},
{12288000, 32000, 0, 0x11, 0x01, 0x80, 0x0c, 0, 0x30},
{24576000, 32000, 1, 0x11, 0x01, 0x80, 0x0c, 0, 0x30},
/* 44.1k */
{11289600, 44100, 0, 0x11, 0x01, 0x00, 0x08, 0, 0x20},
{22579200, 44100, 1, 0x11, 0x01, 0x00, 0x08, 0, 0x20},
/* 48k */
{12288000, 48000, 0, 0x11, 0x01, 0x00, 0x08, 0, 0x20},
{24576000, 48000, 1, 0x11, 0x01, 0x00, 0x08, 0, 0x20},
{6144000 , 48000, 1, 0x11, 0x00, 0x80, 0x04, 1, 0x20},
/* 64k */
{8192000 , 64000, 0, 0x11, 0x00, 0x80, 0x04, 1, 0x20},
{16384000, 64000, 0, 0x11, 0x01, 0x00, 0x08, 0, 0x20},
{12288000, 32000, 0, 0x11, 0x00, 0xc0, 0x06, 1, 0x30},
{24576000, 32000, 0, 0x11, 0x01, 0x80, 0x0c, 0, 0x30},
/* 88.2k */
{11289600, 44100, 0, 0x11, 0x00, 0x80, 0x04, 1, 0x20},
{22579200, 44100, 0, 0x11, 0x01, 0x00, 0x08, 0, 0x20},
/* 96k */
{12288000, 48000, 0, 0x11, 0x00, 0x80, 0x04, 1, 0x20},
{24576000, 48000, 0, 0x11, 0x01, 0x00, 0x08, 0, 0x20},
};
static inline int get_coeff(int mclk, int rate)
{
int i;
for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
return i;
}
return -EINVAL;
}
/*
* if PLL not be used, use internal clk1 for mclk,otherwise, use internal clk2 for PLL source.
*/
static int es8374_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
struct es8374_private *es8374 = snd_soc_codec_get_drvdata(codec);
es8374->clk_id = clk_id;
es8374->sysclk = freq;
if (clk_id == ES8374_CLKID_MCLK) {
snd_soc_write(codec,ES8374_CLK_MANAGEMENT_REG01,0x7F); //IC clk on
snd_soc_write(codec,ES8374_CLK_DIV_REG05,0x11); //clk div set
snd_soc_write(codec,ES8374_PLL_CONTROL1_REG09,0x01); //pll set:reset on ,set start
snd_soc_write(codec,ES8374_PLL_K_REG0C,0x22); //pll set:k
snd_soc_write(codec,ES8374_PLL_K_REG0D,0x2E); //pll set:k
snd_soc_write(codec,ES8374_PLL_K_REG0E,0xC6); //pll set:k
es8374->mclk = freq;
} else {
snd_soc_update_bits(codec, ES8374_CLK_MANAGEMENT_REG01, 0xff, 0xff);
}
return 0;
}
static int es8374_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u8 iface = 0;
u8 adciface = 0;
u8 daciface = 0;
iface = snd_soc_read(codec, ES8374_MS_BCKDIV_REG0F);
adciface = snd_soc_read(codec, ES8374_ADC_FMT_REG10);
daciface = snd_soc_read(codec, ES8374_DAC_FMT_REG11);
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM: /* MASTER MODE */
iface |= 0x80;
break;
case SND_SOC_DAIFMT_CBS_CFS: /* SLAVE MODE */
iface &= 0x7F;
break;
default:
return -EINVAL;
}
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
adciface &= 0xFC;
daciface &= 0xFC;
break;
case SND_SOC_DAIFMT_RIGHT_J:
return -EINVAL;
case SND_SOC_DAIFMT_LEFT_J:
adciface &= 0xFC;
daciface &= 0xFC;
adciface |= 0x01;
daciface |= 0x01;
break;
case SND_SOC_DAIFMT_DSP_A:
adciface &= 0xDC;
daciface &= 0xDC;
adciface |= 0x03;
daciface |= 0x03;
break;
case SND_SOC_DAIFMT_DSP_B:
adciface &= 0xDC;
daciface &= 0xDC;
adciface |= 0x23;
daciface |= 0x23;
break;
default:
return -EINVAL;
}
/* clock inversion */
if(((fmt & SND_SOC_DAIFMT_FORMAT_MASK)==SND_SOC_DAIFMT_I2S) ||
((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_LEFT_J)) {
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
iface &= 0xDF;
adciface &= 0xDF;
daciface &= 0xDF;
break;
case SND_SOC_DAIFMT_IB_IF:
iface |= 0x20;
adciface |= 0x20;
daciface |= 0x20;
break;
case SND_SOC_DAIFMT_IB_NF:
iface |= 0x20;
adciface &= 0xDF;
daciface &= 0xDF;
break;
case SND_SOC_DAIFMT_NB_IF:
iface &= 0xDF;
adciface |= 0x20;
daciface |= 0x20;
break;
default:
return -EINVAL;
}
}
snd_soc_write(codec, ES8374_MS_BCKDIV_REG0F, iface);
snd_soc_write(codec, ES8374_ADC_FMT_REG10, adciface);
snd_soc_write(codec, ES8374_DAC_FMT_REG11, daciface);
return 0;
}
static int es8374_pcm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
return 0;
}
static void es8374_pcm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
u16 rst_val;
rst_val = snd_soc_read(codec, ES8374_RESET_REG00);
if (!(rst_val & 0x80)) {
snd_soc_write(codec,0x00,0x3F); //IC Rst start
msleep(1); //DELAY_MS
snd_soc_write(codec,0x00,0x03); //IC Rst stop
snd_soc_write(codec,0x01,0xeF); //IC clk on
snd_soc_write(codec,0x05,0x33); //clk div set
snd_soc_write(codec,0x6F,0xA0); //pll set:mode enable
snd_soc_write(codec,0x72,0x41); //pll set:mode set
snd_soc_write(codec,0x09,0x01); //pll set:reset on ,set start
snd_soc_write(codec,0x0C,0x17); //pll set:k
snd_soc_write(codec,0x0D,0xa3); //pll set:k
snd_soc_write(codec,0x0E,0x30); //pll set:k
snd_soc_write(codec,0x0A,0x88); //pll set:
snd_soc_write(codec,0x0B,0x07); //pll set:n
snd_soc_write(codec,0x09,0x41); //pll set:reset off ,set stop
snd_soc_write(codec,0x24,0x08); //adc set
snd_soc_write(codec,0x36,0x20); //dac set xiu 180614
snd_soc_write(codec,0x12,0x30); //timming set
snd_soc_write(codec,0x13,0x20); //timming set
snd_soc_write(codec,0x21,0x50); //adc set: SEL LIN1 CH+PGAGAIN=0DB
snd_soc_write(codec,0x22,0x00); //adc set: PGA GAIN=0DB
snd_soc_write(codec,0x21,0x10); //adc set: SEL LIN1 CH+PGAGAIN=0DB
snd_soc_write(codec,0x00,0x80); // IC START
msleep(50); //DELAY_MS
snd_soc_write(codec,0x02,0x08);
snd_soc_write(codec,0x14,0x8a); // IC START
snd_soc_write(codec,0x15,0x40); // IC START
snd_soc_write(codec,0x1A,0xa0); // monoout set xiu 180614
snd_soc_write(codec,0x1B,0x14); // monoout set xiu 180614
snd_soc_write(codec,0x1C,0x10); // spk set xiu 180614
snd_soc_write(codec,0x1D,0x10); // spk set
snd_soc_write(codec,0x1F,0x00); // spk set
snd_soc_write(codec,0x1E,0x40); // spk on
snd_soc_write(codec,0x28,0x1c); // alc set
snd_soc_write(codec,0x25,0xc0); // ADCVOLUME on
snd_soc_write(codec,0x38,0xc0); // DACVOLUMEL on
snd_soc_write(codec,0x37,0x00); // dac set xiu 180614
snd_soc_write(codec,0x6D,0x15); //SEL:GPIO1=HPInserted in+SEL:GPIO2=Interrupt out
snd_soc_write(codec,0x2C,0x05);
snd_soc_write(codec,0x2D,0x05);
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
rtd->codec_dai->runtime = NULL;
return;
}
static int es8374_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
struct es8374_private *es8374 = snd_soc_codec_get_drvdata(codec);
u16 iface;
int coeff, rates;
int N;
// printk("Alsa %s level\n",__func__);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
rtd->codec_dai->runtime = substream->runtime;
if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
iface = snd_soc_read(codec, ES8374_DAC_FMT_REG11) & 0xE3;
/* bit size */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
iface |= 0x0c;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
iface |= 0x04;
break;
case SNDRV_PCM_FORMAT_S24_LE:
break;
case SNDRV_PCM_FORMAT_S32_LE:
iface |= 0x10;
break;
}
/* set iface & srate */
snd_soc_write(codec, ES8374_DAC_FMT_REG11, iface);
} else {
iface = snd_soc_read(codec, ES8374_ADC_FMT_REG10) & 0xE3;
/* bit size */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
iface |= 0x0c;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
iface |= 0x04;
break;
case SNDRV_PCM_FORMAT_S24_LE:
break;
case SNDRV_PCM_FORMAT_S32_LE:
iface |= 0x10;
break;
}
/* set iface */
snd_soc_write(codec, ES8374_ADC_FMT_REG10, iface);
}
if(es8374->dmic_enable){
// printk("dmic is enabled\n");
snd_soc_write(codec, ES8374_GPIO_INSERT_REG6D, 0x5F); //set gpio1 to DMIC CLK
} else {
// printk("dmic is not enabled\n");
// snd_soc_write(codec, ES8374_GPIO_INSERT_REG6D, 0x1F); //set gpio1 to GM SHORT
snd_soc_write(codec, ES8374_GPIO_INSERT_REG6D, 0x15); //set gpio1 to GM SHORT
}
/*
* do PLL configuration if PLL used
*/
if(es8374->clk_id == ES8374_CLKID_PLLO) { // if pll used
/*
* get the target PLL output clock according to the LRCK rates
*/
switch(params_rate(params)) {
case 8000:
case 16000:
case 24000:
case 48000:
rates = 12288000;
break;
case 11025:
case 22050:
case 44100:
rates = 11289600;
break;
default:
rates = 12288000;
break;
}
coeff = get_pll_coeff(es8374->sysclk, rates);
if(coeff < 0 ) {
dev_err(codec->dev,
"Unable to configure pll, freq input = %dHz, freq output = %dHz\n",
es8374->sysclk, rates);
return coeff;
}
snd_soc_update_bits(codec, ES8374_DAC_CONTROL_REG36, 0x20, 0x20); // mute dac firstly
snd_soc_update_bits(codec, ES8374_PLL_CONTROL1_REG09, 0x40, 0x00); // reset pll
mdelay(1); ///
if(pll_coeff_div[coeff].mclkdiv2 == 0) // set mclk div2
snd_soc_update_bits(codec, ES8374_CLK_MANAGEMENT_REG01, 0x80, 0x00); // mclkdiv2 = 0
else
snd_soc_update_bits(codec, ES8374_CLK_MANAGEMENT_REG01, 0x80, 0x80); // mclkdiv2 = 1
switch(pll_coeff_div[coeff].plldiv) { // set pll div
case 0x00: // not used pll
snd_soc_update_bits(codec, ES8374_PLL_CONTROL1_REG09, 0x03, 0x00);
break;
case 0x02: // internal mclk = pll out /2
snd_soc_update_bits(codec, ES8374_PLL_CONTROL1_REG09, 0x03, 0x03);
break;
case 0x04: // internal mclk = pll out / 4
snd_soc_update_bits(codec, ES8374_PLL_CONTROL1_REG09, 0x03, 0x02);
break;
case 0x08: // internal mclk = pll out /8
snd_soc_update_bits(codec, ES8374_PLL_CONTROL1_REG09, 0x03, 0x01);
break;
default:
dev_err(codec->dev,
"invalid pll divider coff\n");
break;
}
switch (es8374->dvdd_pwr_vol) {
case es8374_dvdd_supply_1v8:
snd_soc_update_bits(codec, ES8374_PLL_CONTROL2_REG0A, 0x0c, 0x00); /* dvdd=1.8v */
break;
case es8374_dvdd_supply_2v5:
snd_soc_update_bits(codec, ES8374_PLL_CONTROL2_REG0A, 0x0c, 0x04); /* dvdd=2.5v */
break;
case es8374_dvdd_supply_3v3:
snd_soc_update_bits(codec, ES8374_PLL_CONTROL2_REG0A, 0x0c, 0x08); /* dvdd=3.3v */
break;
default:
snd_soc_update_bits(codec, ES8374_PLL_CONTROL2_REG0A, 0x0c, 0x00); /* dvdd=1.8v */
break;
}
N = snd_soc_read(codec, ES8374_PLL_N_REG0B) & 0xf0;
N |= pll_coeff_div[coeff].pll_n & 0x0f;
snd_soc_write(codec, ES8374_PLL_N_REG0B, N);
snd_soc_write(codec, ES8374_PLL_K_REG0C, pll_coeff_div[coeff].pll_k1 & 0x3f);
snd_soc_write(codec, ES8374_PLL_K_REG0D, pll_coeff_div[coeff].pll_k2 & 0xff);
snd_soc_write(codec, ES8374_PLL_K_REG0E, pll_coeff_div[coeff].pll_k3 & 0xff);
snd_soc_update_bits(codec, ES8374_PLL_CONTROL1_REG09, 0xc0, 0x40); // start pll
snd_soc_update_bits(codec, ES8374_CLK_MANAGEMENT_REG02, 0x08, 0x08); // start pll
es8374->mclk = pll_coeff_div[coeff].freq_out; //reset the mclk frequency
}
/*
* to get correct clock configuration according to MCLK and LRCK frequecny.
*/
coeff = get_coeff(es8374->mclk, params_rate(params));
if (coeff < 0) {
dev_err(codec->dev,
"Unable to configure sample rate %dHz with %dHz MCLK\n",
params_rate(params), es8374->mclk);
return coeff;
}
if(es8374->clk_id == ES8374_CLKID_MCLK) { // if pll used
if(coeff_div[coeff].mclkdiv2 == 0) // set mclk div2
snd_soc_update_bits(codec, ES8374_CLK_MANAGEMENT_REG01, 0x80, 0x00); // mclkdiv2 = 0
else
snd_soc_update_bits(codec, ES8374_CLK_MANAGEMENT_REG01, 0x80, 0x80); // mclkdiv2 = 1
}
snd_soc_write(codec, ES8374_CLK_DIV_REG05, coeff_div[coeff].adc_dac_div); // set adc div & dac div
snd_soc_write(codec, ES8374_LRCK_DIV_REG06, coeff_div[coeff].lrck_h & 0x0f); //set lrck div
snd_soc_write(codec, ES8374_LRCK_DIV_REG07, coeff_div[coeff].lrck_l);
snd_soc_update_bits(codec, ES8374_MS_BCKDIV_REG0F, 0x1f, coeff_div[coeff].sclk_div); // set sclk divider
if(coeff_div[coeff].doublespeed == 1) //set double speed bits
snd_soc_update_bits(codec, ES8374_CLK_MANAGEMENT_REG02, 0x30, 0x30);
else
snd_soc_update_bits(codec, ES8374_CLK_MANAGEMENT_REG02, 0x30, 0x00);
snd_soc_update_bits(codec, ES8374_ADC_OSR_REG03, 0x3f, coeff_div[coeff].osr & 0x3f);
// snd_soc_update_bits(codec, ES8374_DAC_CONTROL_REG36, 0x20, 0x00); // un-mute dac firstly
return 0;
}
static int es8374_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
dev_dbg(codec->dev, "es8374_set_bias_level.= 0x%x.......\n", codec->dapm.bias_level);
dev_dbg(codec->dev, "level = %d\n", level);
// printk("Alsa %s level=%d\n",__func__, level);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
snd_soc_update_bits(codec, ES8374_ANA_REF_REG14, 0xf, 0xa);
break;
case SND_SOC_BIAS_STANDBY:
snd_soc_write(codec, ES8374_AIN_PWR_SRC_REG21, 0xD4);
snd_soc_write(codec, ES8374_ANA_PWR_CTL_REG15, 0xBF);
snd_soc_write(codec, ES8374_ANA_REF_REG14, 0x96);
snd_soc_write(codec, ES8374_PLL_CONTROL1_REG09, 0x80);
snd_soc_write(codec, ES8374_CLK_MANAGEMENT_REG01, 0xff);
snd_soc_write(codec, ES8374_CLK_MANAGEMENT_REG01, 0x03);
break;
case SND_SOC_BIAS_OFF:
snd_soc_write(codec, ES8374_ALC_LVL_HLD_REG28, 0x1c);
snd_soc_update_bits(codec, ES8374_AIN_PWR_SRC_REG21, 0xc0, 0x00);
snd_soc_write(codec, ES8374_ANA_PWR_CTL_REG15, 0xbf);
snd_soc_write(codec, ES8374_ANA_REF_REG14, 0x14);
snd_soc_write(codec, ES8374_CLK_MANAGEMENT_REG01, 0x03);
break;
}
codec->dapm.bias_level = level;
return 0;
}
static int es8374_set_tristate(struct snd_soc_dai *dai, int tristate)
{
struct snd_soc_codec *codec = dai->codec;
dev_dbg(codec->dev, "es8374_set_tristate........\n");
if(tristate) {
snd_soc_update_bits(codec, ES8374_MS_BCKDIV_REG0F,
0x40, 0x40);
} else {
snd_soc_update_bits(codec, ES8374_MS_BCKDIV_REG0F,
0x40, 0x00);
}
return 0;
}
static DEFINE_RAW_SPINLOCK(codec_pa_lock);
static int es8374_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
// printk("Alsa %s level mute=%d\n",__func__,mute);
unsigned long flags;
struct snd_pcm_runtime *runtime = dai->runtime;
dev_dbg(codec->dev, "%s %d\n", __func__, mute);
if (mute) {
snd_soc_update_bits(codec, ES8374_DAC_CONTROL_REG36, 0x20, 0x20);
mdelay(4);
#ifdef CONFIG_ARCH_ZX297520V3_WATCH
gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
mdelay(1);
#endif
} else {
if (dai->playback_active) {
#ifdef CONFIG_ARCH_ZX297520V3_WATCH
gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
mdelay(1);
raw_spin_lock_irqsave(&codec_pa_lock, flags);
gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
udelay(2);
gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
udelay(2);
gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
udelay(2);
gpio_set_value(ZX29_GPIO_125, GPIO_LOW);
udelay(2);
gpio_set_value(ZX29_GPIO_125, GPIO_HIGH);
raw_spin_unlock_irqrestore(&codec_pa_lock, flags);
#endif
if (runtime && (runtime->status->state != SNDRV_PCM_STATE_XRUN))
snd_soc_update_bits(codec, ES8374_DAC_CONTROL_REG36, 0x20, 0x00);
}
}
return 0;
}
#define es8374_RATES SNDRV_PCM_RATE_8000_96000
#define es8374_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_ops es8374_ops = {
.startup = es8374_pcm_startup,
.shutdown = es8374_pcm_shutdown,
.hw_params = es8374_pcm_hw_params,
.set_fmt = es8374_set_dai_fmt,
.set_sysclk = es8374_set_dai_sysclk,
.digital_mute = es8374_mute,
};
static struct snd_soc_dai_driver es8374_dai[] = {
{
.name = "ES8374 HiFi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = es8374_RATES,
.formats = es8374_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = es8374_RATES,
.formats = es8374_FORMATS,
},
.ops = &es8374_ops,
.symmetric_rates = 1,
},
};
static int es8374_suspend(struct snd_soc_codec *codec)
{
#if 0
// snd_soc_write(codec, ES8374_DAC_VOLUME_REG38, 0xc0);
// snd_soc_write(codec, ES8374_ADC_VOLUME_REG25, 0xc0);
snd_soc_write(codec, ES8374_DAC_CONTROL_REG36, 0x20);
snd_soc_write(codec, ES8374_DAC_CONTROL_REG37, 0x21);
snd_soc_write(codec, ES8374_MONO_MIX_REG1A, 0x08);
snd_soc_write(codec, ES8374_SPK_MIX_REG1C, 0x10);
snd_soc_write(codec, ES8374_SPK_MIX_GAIN_REG1D, 0x10);
snd_soc_write(codec, ES8374_SPK_OUT_GAIN_REG1E, 0x40);
snd_soc_update_bits(codec, ES8374_AIN_PWR_SRC_REG21, 0xc0, 0x00);
snd_soc_write(codec, ES8374_ANA_PWR_CTL_REG15, 0xbf);
snd_soc_write(codec, ES8374_ANA_REF_REG14, 0x14);
snd_soc_write(codec, ES8374_CLK_MANAGEMENT_REG01, 0x03);
#endif
return 0;
}
static int es8374_resume(struct snd_soc_codec *codec)
{
#if 0
snd_soc_write(codec, ES8374_CLK_MANAGEMENT_REG01, 0x7f);
snd_soc_write(codec, ES8374_ANA_REF_REG14, 0x8a);
snd_soc_write(codec, ES8374_ANA_PWR_CTL_REG15, 0x40);
snd_soc_update_bits(codec, ES8374_AIN_PWR_SRC_REG21, 0xc0, 0xc0);
snd_soc_write(codec, ES8374_MONO_MIX_REG1A, 0xa0);
snd_soc_write(codec, ES8374_SPK_MIX_REG1C, 0x90);
snd_soc_write(codec, ES8374_SPK_MIX_GAIN_REG1D, 0x02);
snd_soc_write(codec, ES8374_SPK_OUT_GAIN_REG1E, 0xa0);
snd_soc_write(codec, ES8374_DAC_CONTROL_REG36, 0x00);
snd_soc_write(codec, ES8374_DAC_CONTROL_REG37, 0x00);
// snd_soc_write(codec, ES8374_DAC_VOLUME_REG38, 0x00);
// snd_soc_write(codec, ES8374_ADC_VOLUME_REG25, 0x00);
#endif
return 0;
}
static int es8374_probe(struct snd_soc_codec *codec)
{
int ret = 0;
// struct es8374_private *es8374 = es8374_data;
struct es8374_private *es8374 = snd_soc_codec_get_drvdata(codec);
es8374->codec = codec;
ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
if (ret < 0)
return ret;
codec->cache_bypass = 1;
snd_soc_write(codec,0x00,0x3F); //IC Rst start
msleep(1); //DELAY_MS
snd_soc_write(codec,0x00,0x03); //IC Rst stop
snd_soc_write(codec,0x01,0xFF); //IC clk on
snd_soc_write(codec,0x05,0x33); //clk div set
snd_soc_write(codec,0x6F,0xA0); //pll set:mode enable
snd_soc_write(codec,0x72,0x41); //pll set:mode set
snd_soc_write(codec,0x09,0x01); //pll set:reset on ,set start
snd_soc_write(codec,0x0C,0x17); //pll set:k
snd_soc_write(codec,0x0D,0xa3); //pll set:k
snd_soc_write(codec,0x0E,0x30); //pll set:k
snd_soc_write(codec,0x0A,0x88); //pll set:
snd_soc_write(codec,0x0B,0x07); //pll set:n
snd_soc_write(codec,0x09,0x41); //pll set:reset off ,set stop
snd_soc_write(codec,0x24,0x08); //adc set
snd_soc_write(codec,0x36,0x20); //dac set xiu 180614
snd_soc_write(codec,0x12,0x30); //timming set
snd_soc_write(codec,0x13,0x20); //timming set
snd_soc_write(codec,0x21,0x50); //adc set: SEL LIN1 CH+PGAGAIN=0DB
snd_soc_write(codec,0x22,0x00); //adc set: PGA GAIN=0DB
snd_soc_write(codec,0x21,0x10); //adc set: SEL LIN1 CH+PGAGAIN=0DB
snd_soc_write(codec,0x00,0x80); // IC START
msleep(50); //DELAY_MS
snd_soc_write(codec,0x02,0x08);
snd_soc_write(codec,0x14,0x8A); // IC START
snd_soc_write(codec,0x15,0x40); // IC START
snd_soc_write(codec,0x1A,0x08); // monoout set xiu 180614
snd_soc_write(codec,0x1B,0x13); // monoout set xiu 180614
snd_soc_write(codec,0x1C,0x10); // spk set xiu 180614
snd_soc_write(codec,0x1D,0x10); // spk set
snd_soc_write(codec,0x1F,0x00); // spk set
snd_soc_write(codec,0x1E,0x40); // spk on
snd_soc_write(codec,0x28,0x1c); // alc set
snd_soc_write(codec,0x25,0xc0); // ADCVOLUME on
snd_soc_write(codec,0x38,0x00); // DACVOLUMEL on
snd_soc_write(codec,0x37,0x01); // dac set xiu 180614
// snd_soc_write(codec,0x6D,0x60); //SEL:GPIO1=DMIC CLK OUT+SEL:GPIO2=PLL CLK OUT
snd_soc_write(codec,0x6D,0x15); //SEL:GPIO1=HPInserted in+SEL:GPIO2=Interrupt out
snd_soc_write(codec,0x2C,0x05);
snd_soc_write(codec,0x2D,0x05);
// snd_soc_write(codec,0x6D,0x17); //SEL:GPIO1=HPInserted in+SEL:GPIO2=Interrupt out
// snd_soc_write(codec,0x2C,0x6f);
return ret;
}
static int es8374_remove(struct snd_soc_codec *codec)
{
es8374_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_es8374 = {
.probe = es8374_probe,
.remove = es8374_remove,
.suspend = es8374_suspend,
.resume = es8374_resume,
.set_bias_level = es8374_set_bias_level,
.reg_cache_size = ARRAY_SIZE(es8374_reg_defaults),
.reg_word_size = sizeof(u8),
.reg_cache_default = es8374_reg_defaults,
.controls = es8374_snd_controls,
.num_controls = ARRAY_SIZE(es8374_snd_controls),
.dapm_widgets = es8374_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(es8374_dapm_widgets),
.dapm_routes = es8374_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(es8374_dapm_routes),
};
#ifdef CONFIG_OF
static struct of_device_id es8374_if_dt_ids[] = {
{ .compatible = "ambarella,es8374", },
{ }
};
#endif
static void es8374_i2c_shutdown(struct i2c_client *i2c)
{
struct snd_soc_codec *codec;
struct es8374_private *es8374;
es8374 = i2c_get_clientdata(i2c);
codec = es8374->codec;
snd_soc_write(codec, ES8374_DAC_VOLUME_REG38, 0xc0);
snd_soc_write(codec, ES8374_ADC_VOLUME_REG25, 0xc0);
snd_soc_write(codec, ES8374_DAC_CONTROL_REG36, 0x20);
snd_soc_write(codec, ES8374_DAC_CONTROL_REG37, 0x21);
snd_soc_write(codec, ES8374_MONO_MIX_REG1A, 0x08);
snd_soc_write(codec, ES8374_SPK_MIX_REG1C, 0x10);
snd_soc_write(codec, ES8374_SPK_MIX_GAIN_REG1D, 0x10);
snd_soc_write(codec, ES8374_SPK_OUT_GAIN_REG1E, 0x40);
snd_soc_update_bits(codec, ES8374_AIN_PWR_SRC_REG21, 0xc0, 0x00);
snd_soc_write(codec, ES8374_ANA_PWR_CTL_REG15, 0xbf);
snd_soc_write(codec, ES8374_ANA_REF_REG14, 0x14);
snd_soc_write(codec, ES8374_CLK_MANAGEMENT_REG01, 0x03);
return;
}
static int es8374_i2c_probe(struct i2c_client *i2c_client,
const struct i2c_device_id *id)
{
struct es8374_private *es8374;
int ret = -1;
es8374 = kzalloc(sizeof(*es8374), GFP_KERNEL);
if (es8374 == NULL)
return -ENOMEM;
es8374->dmic_enable = es8374_mic_type; /* select the mic type */
es8374->pll_div = es8374_pll_out_div; /* select the pll out divider */
es8374->dvdd_pwr_vol = es8374_dvdd_supply; /* confirm the dvdd voltage for PLL */
es8374->clk_id = ES8374_CLKID; /* confirm PLL used or not */
i2c_set_clientdata(i2c_client, es8374);
// es8374_data = es8374;
ret = snd_soc_register_codec(&i2c_client->dev, &soc_codec_dev_es8374,
&es8374_dai[0], ARRAY_SIZE(es8374_dai));
if (ret < 0) {
return ret;
}
#ifdef CONFIG_ARCH_ZX297520V3_WATCH
ret = gpio_request_one(ZX29_GPIO_125, GPIOF_OUT_INIT_LOW, "codec_pa");
if (ret < 0) {
printk(KERN_ERR "Alsa es8374: codec_pa in use\n");
return ret;
}
#endif
return ret;
}
static int es8374_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
kfree(i2c_get_clientdata(client));
return 0;
}
static const struct i2c_device_id es8374_i2c_id[] = {
{"es8374", 0 },
{"10ES8374:00", 0},
{"10ES8374", 0},
{ }
};
MODULE_DEVICE_TABLE(i2c, es8374_i2c_id);
static struct i2c_driver es8374_i2c_driver = {
.driver = {
.name = "es8374",
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = of_match_ptr(es8374_if_dt_ids),
#endif
},
.shutdown = es8374_i2c_shutdown,
.probe = es8374_i2c_probe,
.remove = es8374_i2c_remove,
.id_table = es8374_i2c_id,
};
static int __init es8374_init(void)
{
return i2c_add_driver(&es8374_i2c_driver);
}
static void __exit es8374_exit(void)
{
return i2c_del_driver(&es8374_i2c_driver);
}
module_init(es8374_init);
module_exit(es8374_exit);
MODULE_DESCRIPTION("ASoC es8374 driver");
MODULE_AUTHOR("XianqingZheng <xqzheng@ambarella.com>");
MODULE_LICENSE("GPL");