| /* |
| * 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/spi/spi.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 <linux/of_gpio.h> |
| #include "es8374.h" |
| |
| //#define ES8374_SPI |
| /* |
| * es8374 register cache |
| */ |
| static struct reg_default es8374_reg_defaults[] = { |
| { 0x00, 0x03 }, |
| { 0x01, 0x03 }, |
| { 0x02, 0x00 }, |
| { 0x03, 0x20 }, |
| { 0x04, 0x00 }, |
| { 0x05, 0x11 }, |
| { 0x06, 0x01 }, |
| { 0x07, 0x00 }, |
| { 0x08, 0x20 }, |
| { 0x09, 0x80 }, |
| { 0x0a, 0x4A }, |
| { 0x0b, 0x00 }, |
| { 0x0c, 0x00 }, |
| { 0x0d, 0x00 }, |
| { 0x0e, 0x00 }, |
| { 0x0f, 0x00 }, |
| |
| { 0x10, 0x00 }, |
| { 0x11, 0x00 }, |
| { 0x12, 0x40 }, |
| { 0x13, 0x40 }, |
| { 0x14, 0x9C }, |
| { 0x15, 0xBE }, |
| { 0x16, 0x00 }, |
| { 0x17, 0xA0 }, |
| { 0x18, 0xFC }, |
| { 0x19, 0x00 }, |
| { 0x1a, 0x18 }, |
| { 0x1b, 0x00 }, |
| { 0x1c, 0x10 }, |
| { 0x1d, 0x10 }, |
| { 0x1e, 0x00 }, |
| { 0x1f, 0x08 }, |
| |
| { 0x20, 0x08 }, |
| { 0x21, 0xD4 }, |
| { 0x22, 0x00 }, |
| { 0x23, 0x00 }, |
| { 0x24, 0x18 }, |
| { 0x25, 0xC0 }, |
| { 0x26, 0x1C }, |
| { 0x27, 0x00 }, |
| { 0x28, 0xB0 }, |
| { 0x29, 0x32 }, |
| { 0x2a, 0x03 }, |
| { 0x2b, 0x00 }, |
| { 0x2c, 0x0D }, |
| { 0x2d, 0x06 }, |
| { 0x2e, 0x1F }, |
| { 0x2f, 0xF7 }, |
| |
| { 0x30, 0xFD }, |
| { 0x31, 0xFF }, |
| { 0x32, 0x1F }, |
| { 0x33, 0xF7 }, |
| { 0x34, 0xFD }, |
| { 0x35, 0xFF }, |
| { 0x36, 0x04 }, |
| { 0x37, 0x01 }, |
| { 0x38, 0xC0 }, |
| { 0x39, 0x00 }, |
| { 0x3a, 0x02 }, |
| { 0x3b, 0x17 }, |
| { 0x3c, 0xFD }, |
| { 0x3d, 0xFF }, |
| { 0x3e, 0x07 }, |
| { 0x3f, 0xFD }, |
| |
| { 0x40, 0xFF }, |
| { 0x41, 0x00 }, |
| { 0x42, 0xFF }, |
| { 0x43, 0xBB }, |
| { 0x44, 0xFF }, |
| { 0x45, 0x00 }, |
| { 0x46, 0x00 }, |
| { 0x47, 0x00 }, |
| { 0x48, 0x00 }, |
| { 0x49, 0x00 }, |
| { 0x4a, 0x00 }, |
| { 0x4b, 0x00 }, |
| { 0x4c, 0x00 }, |
| { 0x4d, 0x00 }, |
| { 0x4e, 0x00 }, |
| { 0x4f, 0x00 }, |
| |
| { 0x50, 0x00 }, |
| { 0x51, 0x00 }, |
| { 0x52, 0x00 }, |
| { 0x53, 0x00 }, |
| { 0x54, 0x00 }, |
| { 0x55, 0x00 }, |
| { 0x56, 0x00 }, |
| { 0x57, 0x00 }, |
| { 0x58, 0x00 }, |
| { 0x59, 0x00 }, |
| { 0x5a, 0x00 }, |
| { 0x5b, 0x00 }, |
| { 0x5c, 0x00 }, |
| { 0x5d, 0x00 }, |
| { 0x5e, 0x00 }, |
| { 0x5f, 0x00 }, |
| |
| { 0x60, 0x00 }, |
| { 0x61, 0x00 }, |
| { 0x62, 0x00 }, |
| { 0x63, 0x00 }, |
| { 0x64, 0x00 }, |
| { 0x65, 0x00 }, |
| { 0x66, 0x00 }, |
| { 0x67, 0x00 }, |
| { 0x68, 0x00 }, |
| { 0x69, 0x00 }, |
| { 0x6a, 0x00 }, |
| { 0x6b, 0x00 }, |
| { 0x6c, 0x00 }, |
| { 0x6d, 0x00 }, |
| { 0x6e, 0x00 }, |
| { 0x6f, 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 |
| struct sp_config { |
| u8 spc, mmcc, spfs; |
| u32 srate; |
| u8 lrcdiv; |
| u8 sclkdiv; |
| }; |
| |
| /* codec private data */ |
| |
| struct es8374_private { |
| enum snd_soc_control_type control_type; |
| struct spi_device *spi; |
| struct i2c_client *i2c; |
| |
| struct snd_soc_codec *codec; |
| struct regmap *regmap; |
| u32 clk_id; |
| u32 mclk; |
| |
| /* platform dependant DVDD voltage configuration */ |
| /* u8 dvdd_pwr_vol; |
| u8 pll_div; |
| */ |
| int pwr_gpio; |
| unsigned int pwr_active; |
| bool dmic_enable; |
| u8 reg_cache[110]; |
| }; |
| |
| struct es8374_private *es8374_data; |
| |
| static bool es8374_volatile_register(struct device *dev, |
| unsigned int reg) |
| { |
| if ((reg < 0x80)) { |
| if((reg != 0x19) && (reg != 0x23)) { |
| return true; |
| } else { |
| return false; |
| } |
| } else { |
| return false; |
| } |
| } |
| |
| static bool es8374_readable_register(struct device *dev, |
| unsigned int reg) |
| { |
| if ((reg < 0x80)) { |
| if((reg != 0x19) && (reg != 0x23)) { |
| return true; |
| } else { |
| return false; |
| } |
| } else { |
| return false; |
| } |
| } |
| static bool es8374_writable_register(struct device *dev, |
| unsigned int reg) |
| { |
| if ((reg < 0x80)) { |
| if((reg != 0x19) && (reg != 0x23)) { |
| return true; |
| } else { |
| return false; |
| } |
| } else { |
| return false; |
| } |
| } |
| |
| |
| /* |
| * Define ADC and DAC Volume |
| */ |
| static const DECLARE_TLV_DB_SCALE(vdac_tlv, 0, 50, 0); |
| static const DECLARE_TLV_DB_SCALE(vadc_tlv, 0, 50, 0); |
| /* |
| * 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, 0, 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); |
| |
| |
| static const char * const alc_mode_txt[] = { |
| "ALC Mode", |
| "Limiter Mode" |
| }; |
| |
| static const struct soc_enum alc_mode = |
| SOC_ENUM_SINGLE(ES8374_ALC_EN_MAX_GAIN_REG26, 5, 2, alc_mode_txt); |
| |
| /* |
| * Define MONO output gain |
| */ |
| static const DECLARE_TLV_DB_SCALE(mono_out_gain_tlv, 0, 150, 0); |
| /* |
| * 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); |
| |
| /* |
| * 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_ENUM("ALC Capture Function", alc_func), |
| SOC_ENUM("ALC Mode", alc_mode), |
| 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 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("ADC Capture Volume", |
| ES8374_ADC_VOLUME_REG25, 0, 192, 1, vadc_tlv), |
| SOC_SINGLE_TLV("DAC Playback Volume", |
| ES8374_DAC_VOLUME_REG38, 0, 192, 1, vdac_tlv), |
| 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), |
| }; |
| |
| /* |
| * DAPM Controls |
| */ |
| /* |
| * alc on/off |
| */ |
| static const char * const es8374_alc_enable_txt[] = { |
| "ALC OFF", |
| "ALC ON", |
| }; |
| |
| static const struct soc_enum es8374_alc_enable_enum = |
| SOC_ENUM_SINGLE(ES8374_ALC_EN_MAX_GAIN_REG26, 6, |
| ARRAY_SIZE(es8374_alc_enable_txt), es8374_alc_enable_txt); |
| |
| |
| 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[] = { |
| "LIN1-RIN1", |
| "LIN2-RIN2", |
| }; |
| static const unsigned int es8374_adc_input_src_values[] = { |
| 0, 1}; |
| static const struct soc_enum es8374_adc_input_src_enum = |
| SOC_VALUE_ENUM_SINGLE(ES8374_AIN_PWR_SRC_REG21, 4, 0x30, |
| 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, 0x7, |
| 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, 0x3, |
| 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, 0x80, |
| 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, 0x40, |
| 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, 0x20, |
| 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, 0x40, |
| 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 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), |
| |
| /* 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), |
| |
| /* 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("MONO MIXER", SND_SOC_NOPM, |
| 0, 0, &es8374_mono_out_mixer_controls[0], ARRAY_SIZE(es8374_mono_out_mixer_controls)), |
| SND_SOC_DAPM_MIXER("SPEAKER MIXER", SND_SOC_NOPM, |
| 0, 0, &es8374_speaker_mixer_controls[0], ARRAY_SIZE(es8374_speaker_mixer_controls)), |
| |
| /* |
| * 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"}, |
| |
| {"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"}, |
| |
| /* |
| * 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"}, |
| |
| {"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"}, |
| |
| }; |
| |
| #if 0 |
| static int es8374_set_pll(struct snd_soc_dai *dai, int pll_id, |
| int source, unsigned int freq_in, unsigned int freq_out) |
| { |
| struct snd_soc_codec *codec = dai->codec; |
| struct es8374_private *priv = snd_soc_codec_get_drvdata(codec); |
| u16 reg; |
| u8 N, K1, K2, K3; |
| float tmp; |
| u32 Kcoefficient; |
| switch (pll_id) { |
| case ES8374_PLL: |
| break; |
| default: |
| return -EINVAL; |
| break; |
| } |
| /* Disable PLL */ |
| // pll hold in reset state |
| snd_soc_update_bits(codec, ES8374_PLL_CONTROL1_REG09, |
| 0x40, 0x00); |
| |
| if (!freq_in || !freq_out) |
| return 0; |
| |
| switch (source) { |
| |
| case ES8374_PLL_SRC_FRM_MCLK: |
| // Select PLL |
| snd_soc_update_bits(codec, ES8374_CLK_MANAGEMENT_REG02, |
| 0x08, 0x08); |
| break; |
| default: |
| //Disable PLL |
| snd_soc_update_bits(codec, ES8374_CLK_MANAGEMENT_REG02, |
| 0x08, 0x00); |
| return -EINVAL; |
| } |
| /*get N & K */ |
| tmp = 0; |
| if (source == ES8374_PLL_SRC_FRM_MCLK) { |
| if(freq_in >19200000) |
| { |
| freq_in /= 2; |
| snd_soc_update_bits(codec, ES8374_CLK_MANAGEMENT_REG01, |
| 0x80, 0x80); /* mclk div2 = 1 */ |
| } |
| tmp = (float)freq_out * (float)priv->pll_div + 4000; |
| tmp /= (float)freq_in; |
| N = (u8)tmp; |
| tmp = tmp - (float)N; |
| tmp = tmp * 0.6573598222296 * (1<<22); |
| Kcoefficient = (u32)tmp; |
| K1 = Kcoefficient / 65536; |
| Kcoefficient = Kcoefficient - K1 * 65536; |
| K2 = Kcoefficient /256; |
| K3 = Kcoefficient - K2 * 256; |
| } |
| dev_dbg(codec->dev, "N=%x, K3=%x, K2=%x, K1=%x\n", N, K3, K2, K1); |
| |
| reg = snd_soc_read(codec, ES8374_PLL_N_REG0B); |
| reg &= 0xF0; |
| reg |= (N & 0x0F); |
| snd_soc_write(codec, ES8374_PLL_N_REG0B, reg); |
| |
| K1 &= 0x3F; |
| snd_soc_write(codec, ES8374_PLL_K_REG0C, K1); |
| snd_soc_write(codec, ES8374_PLL_K_REG0D, K2); |
| snd_soc_write(codec, ES8374_PLL_K_REG0E, K3); |
| |
| |
| /* pll div 8 */ |
| reg = snd_soc_read(codec, ES8374_PLL_CONTROL1_REG09); |
| reg &= 0xfc; |
| reg |= 0x01; |
| snd_soc_write(codec, ES8374_PLL_CONTROL1_REG09, reg); |
| |
| /* configure the pll power voltage */ |
| switch (priv->dvdd_pwr_vol) { |
| case 0x18: |
| snd_soc_update_bits(codec, ES8374_PLL_CONTROL2_REG0A, 0x0c, 0x00); /* dvdd=1.8v */ |
| break; |
| case 0x25: |
| snd_soc_update_bits(codec, ES8374_PLL_CONTROL2_REG0A, 0x0c, 0x04); /* dvdd=2.5v */ |
| break; |
| case 0x33: |
| 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; |
| } |
| /* enable PLL */ |
| snd_soc_update_bits(codec, ES8374_PLL_CONTROL1_REG09, |
| 0x40, 0x40); |
| priv->mclk = freq_out; |
| return 0; |
| } |
| #endif |
| |
| struct _coeff_div { |
| u32 mclk; /* mclk frequency */ |
| u32 rate; /* sample rate */ |
| u8 div; /* adcclk and dacclk divider */ |
| u8 fsmode; /* adcclk and dacclk divider */ |
| u8 divdouble; /* adcclk and dacclk divider */ |
| u8 lrck_h; /* adclrck divider and daclrck divider */ |
| u8 lrck_l; |
| u8 sr; /* sclk divider */ |
| u8 osr; /* adc osr */ |
| }; |
| |
| |
| /* codec hifi mclk clock divider coefficients */ |
| static const struct _coeff_div coeff_div[] = { |
| |
| // MCLK , LRCK,DIV,FSMODE,divDOUBLE,LRCK-H,LRCK-L,BCLK,OSR |
| /* |
| //12M288 |
| {12288000, 96000 , 1 , 1 , 0 , 0x00 , 0x80 , 2 , 32}, |
| {12288000, 64000 , 3 , 1 , 1 , 0x00 , 0xC0 , 2 , 32}, |
| {12288000, 48000 , 1 , 0 , 0 , 0x01 , 0x00 , 2 , 32}, |
| {12288000, 32000 , 3 , 0 , 1 , 0x01 , 0x80 , 2 , 32}, |
| {12288000, 24000 , 2 , 0 , 0 , 0x02 , 0x00 , 2 , 32}, |
| {12288000, 16000 , 3 , 0 , 0 , 0x03 , 0x00 , 2 , 32}, |
| {12288000, 12000 , 4 , 0 , 0 , 0x04 , 0x00 , 2 , 32}, |
| {12288000, 8000 , 6 , 0 , 0 , 0x06 , 0x00 , 2 , 32}, |
| |
| // 12M |
| {12000000, 96000 , 1 , 1 , 0 , 0x00 , 0x7D , 2 , 31}, |
| {12000000, 88200 , 1 , 1 , 0 , 0x00 , 0x88 , 2 , 34}, |
| {12000000, 48000 , 5 , 1 , 1 , 0x00 , 0xFA , 2 , 25}, |
| {12000000, 44100 , 1 , 0 , 0 , 0x01 , 0x10 , 2 , 34}, |
| {12000000, 32000 , 3 , 1 , 0 , 0x01 , 0x77 , 2 , 31}, |
| {12000000, 24000 , 5 , 1 , 0 , 0x02 , 0x00 , 2 , 25}, |
| {12000000, 22050 , 2 , 0 , 0 , 0x02 , 0x20 , 2 , 34}, |
| {12000000, 16000 , 15, 1 , 1 , 0x02 , 0xEE , 2 , 25}, |
| {12000000, 12000 , 5 , 0 , 0 , 0x03 , 0xE8 , 2 , 25}, |
| {12000000, 11025 , 4 , 0 , 0 , 0x04 , 0x40 , 2 , 34}, |
| {12000000, 8000 , 15, 0 , 1 , 0x05 , 0xDC , 2 , 25}, |
| |
| //11M2896 |
| {11289600, 88200 , 1 , 1 , 0 , 0x00 , 0x80 , 2 , 32}, |
| {11289600, 44100 , 1 , 0 , 0 , 0x01 , 0x00 , 2 , 32}, |
| {11289600, 22050 , 2 , 0 , 0 , 0x02 , 0x00 , 2 , 32}, |
| {11289600, 11025 , 4 , 0 , 0 , 0x04 , 0x00 , 2 , 32}, |
| |
| */ |
| |
| /* 8k */ |
| {12288000, 8000 , 6 , 0 , 0 , 0x06 , 0x00 , 2 , 32}, |
| {12000000, 8000 , 15, 0 , 1 , 0x05 , 0xDC , 2 , 25}, |
| {11289600, 8000 , 6 , 0 , 0 , 0x05, 0x83, 20, 29}, |
| {18432000, 8000 , 9 , 0 , 0 , 0x09, 0x00, 27, 32}, |
| {16934400, 8000 , 8 , 0 , 0 , 0x08, 0x44, 25, 33}, |
| {12000000, 8000 , 7 , 0 , 0 , 0x05, 0xdc, 21, 25}, |
| {19200000, 8000 , 12, 0 , 0 , 0x09, 0x60, 27, 25}, |
| |
| /* 11.025k */ |
| {11289600, 11025 , 4 , 0 , 0 , 0x04 , 0x00 , 2 , 32}, |
| {12000000, 11025 , 4 , 0 , 0 , 0x04 , 0x40 , 2 , 34}, |
| {16934400, 11025, 6 , 0 , 0 , 0x06, 0x00, 21, 32}, |
| |
| |
| /* 12k */ |
| {12000000, 12000 , 5 , 0 , 0 , 0x03 , 0xE8 , 2 , 25}, |
| {12288000, 12000 , 4 , 0 , 0 , 0x04 , 0x00 , 2 , 32}, |
| |
| /* 16k */ |
| {12288000, 16000 , 3 , 0 , 0 , 0x03 , 0x00 , 2 , 32}, |
| {18432000, 16000, 5 , 0 , 0 , 0x04, 0x80, 18, 25}, |
| {12000000, 16000 , 15, 1 , 1 , 0x02 , 0xEE , 2 , 25}, |
| {19200000, 16000, 6 , 0 , 0 , 0x04, 0xb0, 18, 25}, |
| |
| /* 22.05k */ |
| {11289600, 22050 , 2 , 0 , 0 , 0x02 , 0x00 , 2 , 32}, |
| {16934400, 22050, 3 , 0 , 0 , 0x03, 0x00, 12, 32}, |
| {12000000, 22050 , 2 , 0 , 0 , 0x02 , 0x20 , 2 , 34}, |
| |
| /* 24k */ |
| {12000000, 24000 , 5 , 1 , 0 , 0x02 , 0x00 , 2 , 25}, |
| {12288000, 24000 , 2 , 0 , 0 , 0x02 , 0x00 , 2 , 32}, |
| |
| /* 32k */ |
| {12288000, 32000 , 3 , 0 , 1 , 0x01 , 0x80 , 2 , 32}, |
| {18432000, 32000, 2 , 0 , 0 , 0x02, 0x40, 9 , 32}, |
| {12000000, 32000 , 3 , 1 , 0 , 0x01 , 0x77 , 2 , 31}, |
| {19200000, 32000, 3 , 0 , 0 , 0x02, 0x58, 10, 25}, |
| |
| /* 44.1k */ |
| {11289600, 44100 , 1 , 0 , 0 , 0x01 , 0x00 , 2 , 32}, |
| {16934400, 44100, 1 , 0 , 0 , 0x01, 0x80, 6 , 32}, |
| {12000000, 44100 , 1 , 0 , 0 , 0x01 , 0x10 , 2 , 34}, |
| |
| /* 48k */ |
| {12288000, 48000 , 1 , 0 , 0 , 0x01 , 0x00 , 2 , 32}, |
| {18432000, 48000, 1 , 0 , 0 , 0x01, 0x80, 6 , 32}, |
| {12000000, 48000 , 5 , 1 , 1 , 0x00 , 0xFA , 2 , 25}, |
| {19200000, 48000, 2 , 0 , 0 , 0x01, 0x90, 6, 25}, |
| |
| /* 64k */ |
| {12288000, 64000 , 3 , 1 , 1 , 0x00 , 0xC0 , 2 , 32}, |
| |
| /* 88.2k */ |
| {11289600, 88200 , 1 , 1 , 0 , 0x00 , 0x80 , 2 , 32}, |
| {16934400, 88200, 1 , 0 , 0 , 0x00, 0xc0, 3 , 48}, |
| {12000000, 88200 , 1 , 1 , 0 , 0x00 , 0x88 , 2 , 34}, |
| |
| /* 96k */ |
| {12288000, 96000 , 1 , 1 , 0 , 0x00 , 0x80 , 2 , 32}, |
| {18432000, 96000, 1 , 0 , 0 , 0x00, 0xc0, 3 , 48}, |
| {12000000, 96000 , 1 , 1 , 0 , 0x00 , 0x7D , 2 , 31}, |
| {19200000, 96000, 1 , 0 , 0 , 0x00, 0xc8, 3 , 25}, |
| }; |
| 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; |
| } |
| |
| /* The set of rates we can generate from the above for each SYSCLK */ |
| #if 0 |
| static unsigned int rates_12288[] = { |
| 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000, |
| }; |
| |
| static struct snd_pcm_hw_constraint_list constraints_12288 = { |
| .count = ARRAY_SIZE(rates_12288), |
| .list = rates_12288, |
| }; |
| |
| static unsigned int rates_112896[] = { |
| 8000, 11025, 22050, 44100, |
| }; |
| |
| static struct snd_pcm_hw_constraint_list constraints_112896 = { |
| .count = ARRAY_SIZE(rates_112896), |
| .list = rates_112896, |
| }; |
| |
| static unsigned int rates_12[] = { |
| 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, |
| 48000, 88235, 96000, |
| }; |
| |
| static struct snd_pcm_hw_constraint_list constraints_12 = { |
| .count = ARRAY_SIZE(rates_12), |
| .list = rates_12, |
| }; |
| #endif |
| |
| /* |
| * 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; |
| |
| if (clk_id == ES8374_CLKID_MCLK) { |
| snd_soc_write(codec,0x09,0x80); //pll set:reset on ,set start |
| snd_soc_write(codec,0x0C,0x00); //pll set:k |
| snd_soc_write(codec,0x0D,0x00); //pll set:k |
| snd_soc_write(codec,0x0E,0x00); //pll set:k |
| |
| } |
| |
| 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 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; |
| |
| 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); |
| /*set speak and mono for playback*/ |
| snd_soc_write(codec, ES8374_MONO_MIX_REG1A, 0xA0); |
| snd_soc_write(codec, ES8374_SPK_MIX_REG1C, 0x90); |
| |
| } 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){ |
| snd_soc_write(codec, ES8374_ADC_CONTROL_REG24, 0x8A); //6DB & DIMC=H |
| snd_soc_write(codec,0x12,0x40); |
| snd_soc_write(codec,0x13,0x40); |
| snd_soc_write(codec,0x22,0x00); |
| snd_soc_write(codec, 0x26, 0x50); |
| snd_soc_write(codec, 0x28, 0x80); |
| |
| } else { |
| snd_soc_write(codec,0x24,0x08); //adc set |
| snd_soc_write(codec, ES8374_GPIO_INSERT_REG6D, 0x1F); //set gpio1 to GM SHORT |
| } |
| |
| /* |
| * please add the hardware clock divider setting to get the correct LRCK, SCLK frequency |
| */ |
| return 0; |
| } |
| |
| static int es8374_set_bias_level(struct snd_soc_codec *codec, |
| enum snd_soc_bias_level level) |
| { |
| |
| switch (level) { |
| case SND_SOC_BIAS_ON: |
| 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, 0x00); |
| 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); |
| snd_soc_write(codec, ES8374_AIN_PGA_REG22, 0x06); |
| |
| break; |
| |
| case SND_SOC_BIAS_PREPARE: |
| break; |
| |
| case SND_SOC_BIAS_STANDBY: |
| snd_soc_update_bits(codec, ES8374_AIN_PWR_SRC_REG21, 0xc0, 0xc0); |
| break; |
| |
| case SND_SOC_BIAS_OFF: |
| snd_soc_update_bits(codec, ES8374_AIN_PWR_SRC_REG21, 0xc0, 0xc0); |
| 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); |
| snd_soc_write(codec, ES8374_AIN_PGA_REG22, 0x00); |
| 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 int es8374_mute(struct snd_soc_dai *dai, int mute) |
| { |
| struct snd_soc_codec *codec = dai->codec; |
| |
| dev_dbg(codec->dev, "%s %d\n", __func__, mute); |
| if (mute) { |
| snd_soc_update_bits(codec, ES8374_DAC_CONTROL_REG36, 0x20, 0x20); |
| } else { |
| if (dai->playback_active) |
| 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, |
| .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) |
| { |
| struct es8374_private *es8374 = snd_soc_codec_get_drvdata(codec); |
| int i; |
| |
| for(i = 0; i <= 110; i++) { |
| es8374->reg_cache[i] = snd_soc_read(codec, i); |
| } |
| |
| 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 0; |
| } |
| |
| static int es8374_resume(struct snd_soc_codec *codec) |
| { |
| struct es8374_private *es8374 = snd_soc_codec_get_drvdata(codec); |
| int i; |
| #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, 0x50); |
| 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 |
| |
| for(i = 0; i <= 110; i++) { |
| snd_soc_write(codec, i, es8374->reg_cache[i]); |
| } |
| |
| return 0; |
| } |
| |
| static int es8374_parse_dts(struct es8374_private *es8374) |
| { |
| #ifdef CONFIG_OF |
| struct device *dev; |
| struct device_node *np; |
| enum of_gpio_flags flags; |
| int ret, dmic; |
| |
| if (es8374->control_type == SND_SOC_I2C) { |
| dev = &(es8374->i2c->dev); |
| } else { |
| dev = &(es8374->spi->dev); |
| } |
| |
| np = dev->of_node; |
| |
| if (!np) |
| return -1; |
| |
| ret = of_property_read_u32(np, "es8374,dmic", &dmic); |
| if(ret == 0 && dmic == 1) { |
| es8374->dmic_enable = 1; |
| } else { |
| es8374->dmic_enable = 0; |
| } |
| |
| es8374->pwr_gpio = of_get_named_gpio_flags(np, "es8374,pwr-gpio", 0, &flags); |
| if (es8374->pwr_gpio < 0 || !gpio_is_valid(es8374->pwr_gpio)) { |
| es8374->pwr_gpio = -1; |
| } else { |
| es8374->pwr_active = !!(flags & OF_GPIO_ACTIVE_LOW); |
| } |
| |
| #endif |
| |
| return 0; |
| } |
| |
| static int es8374_probe(struct snd_soc_codec *codec) |
| { |
| int ret = 0; |
| struct es8374_private *es8374 = es8374_data; |
| |
| codec->control_data = es8374->regmap; |
| ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); |
| if (ret < 0) |
| return ret; |
| |
| es8374->codec = codec; |
| snd_soc_codec_set_drvdata(codec, es8374); |
| |
| es8374_parse_dts(es8374); |
| if (gpio_is_valid(es8374->pwr_gpio)) { |
| ret = devm_gpio_request(codec->dev, es8374->pwr_gpio, "es8374 power pin"); |
| if(ret == 0) |
| gpio_direction_output(es8374->pwr_gpio, es8374->pwr_active); |
| } |
| |
| 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,0x7F); //IC clk on |
| snd_soc_write(codec,0x05,0x11); //clk div set |
| snd_soc_write(codec,0x36,0x00); //dac set |
| 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,0xFF); //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,0x14,0x8A); // IC START |
| snd_soc_write(codec,0x15,0x40); // IC START |
| snd_soc_write(codec,0x1A,0xA0); // monoout set |
| snd_soc_write(codec,0x1B,0x19); // monoout set |
| snd_soc_write(codec,0x1C,0x90); // spk set |
| snd_soc_write(codec,0x1D,0x02); // spk set |
| snd_soc_write(codec,0x1F,0x00); // spk set |
| snd_soc_write(codec,0x1E,0xA0); // spk on |
| snd_soc_write(codec,0x28,0x00); // alc set |
| snd_soc_write(codec,0x25,0x00); // ADCVOLUME on |
| snd_soc_write(codec,0x38,0x00); // DACVOLUMEL on |
| snd_soc_write(codec,0x37,0x00); // dac set |
| snd_soc_write(codec,0x6D,0x40); //SEL:GPIO1=DMIC CLK OUT+SEL:GPIO2=PLL CLK OUT |
| |
| es8374_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
| |
| 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, |
| |
| .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), |
| }; |
| |
| static struct regmap_config es8374_regmap = { |
| .reg_bits = 8, |
| .val_bits = 8, |
| |
| .max_register = ES8374_MAX_REGISTER, |
| .reg_defaults = es8374_reg_defaults, |
| .num_reg_defaults = ARRAY_SIZE(es8374_reg_defaults), |
| .volatile_reg = es8374_volatile_register, |
| .writeable_reg = es8374_writable_register, |
| .readable_reg = es8374_readable_register, |
| .cache_type = REGCACHE_RBTREE, |
| }; |
| |
| #ifdef CONFIG_OF |
| static struct of_device_id es8374_if_dt_ids[] = { |
| { .compatible = "ambarella,es8374", }, |
| { } |
| }; |
| #endif |
| |
| #if defined(ES8374_SPI) |
| static int es8374_spi_probe(struct spi_device *spi) |
| { |
| struct es8374_private *es8374; |
| int ret; |
| |
| es8374 = kzalloc(sizeof(struct es8374_private), GFP_KERNEL); |
| if (es8374 == NULL) |
| return -ENOMEM; |
| |
| es8374->control_type = SND_SOC_SPI; |
| es8374->spi = spi; |
| |
| spi_set_drvdata(spi, es8374); |
| |
| es8374->regmap = devm_regmap_init_spi(spi, &es8374_regmap); |
| if (IS_ERR(es8374->regmap)) { |
| ret = PTR_ERR(es8374->regmap); |
| dev_err(&spi->dev, "regmap_init() failed: %d\n", ret); |
| return ret; |
| } |
| |
| es8374_data = es8374; |
| |
| ret = snd_soc_register_codec(&spi->dev, |
| &soc_codec_dev_es8374, &es8374_dai, 1); |
| if (ret < 0) |
| kfree(es8374); |
| return ret; |
| } |
| |
| static int es8374_spi_remove(struct spi_device *spi) |
| { |
| snd_soc_unregister_codec(&spi->dev); |
| kfree(spi_get_drvdata(spi)); |
| return 0; |
| } |
| |
| static struct spi_driver es8374_spi_driver = { |
| .driver = { |
| .name = "es8374", |
| .owner = THIS_MODULE, |
| #ifdef CONFIG_OF |
| .of_match_table = of_match_ptr(es8374_if_dt_ids), |
| #endif |
| }, |
| .probe = es8374_spi_probe, |
| .remove = es8374_spi_remove, |
| }; |
| #endif /* CONFIG_SPI_MASTER */ |
| |
| #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
| 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, 0xc0); |
| 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->control_type = SND_SOC_I2C; |
| es8374->i2c = i2c_client; |
| |
| i2c_set_clientdata(i2c_client, es8374); |
| es8374->regmap = devm_regmap_init_i2c(i2c_client, &es8374_regmap); |
| if (IS_ERR(es8374->regmap)) { |
| ret = PTR_ERR(es8374->regmap); |
| dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); |
| return ret; |
| } |
| |
| 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) { |
| kfree(es8374); |
| return ret; |
| } |
| |
| 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, |
| }; |
| #endif |
| |
| static int __init es8374_init(void) |
| { |
| #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
| return i2c_add_driver(&es8374_i2c_driver); |
| #endif |
| #ifdef ES8374_SPI |
| return spi_register_driver(&es8374_spi_driver); |
| #endif |
| } |
| |
| static void __exit es8374_exit(void) |
| { |
| #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
| return i2c_del_driver(&es8374_i2c_driver); |
| #endif |
| #ifdef ES8374_SPI |
| return spi_unregister_driver(&es8374_spi_driver); |
| #endif |
| } |
| |
| module_init(es8374_init); |
| module_exit(es8374_exit); |
| |
| MODULE_DESCRIPTION("ASoC es8374 driver"); |
| MODULE_AUTHOR("XianqingZheng <xqzheng@ambarella.com>"); |
| MODULE_LICENSE("GPL"); |
| |