| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * es8311.c -- es8311 ALSA SoC audio driver | |
| * | |
| * Copyright (C) 2024 Matteo Martelli <matteomartelli3@gmail.com> | |
| * | |
| * Author: Matteo Martelli <matteomartelli3@gmail.com> | |
| */ | |
| #include "linux/array_size.h" | |
| #include "sound/pcm.h" | |
| #include <linux/clk.h> | |
| #include <linux/i2c.h> | |
| #include <linux/module.h> | |
| #include <linux/regmap.h> | |
| #include <sound/core.h> | |
| #include <sound/pcm_params.h> | |
| #include <sound/soc.h> | |
| #include <sound/tlv.h> | |
| #include "es8311.h" | |
| #define ES8311_NUM_RATES 10 | |
| #define ES8311_RATES (SNDRV_PCM_RATE_8000_96000) | |
| #define ES8311_FORMATS \ | |
| (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ | |
| SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE | \ | |
| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) | |
| struct es8311_priv { | |
| struct regmap *regmap; | |
| struct clk *mclk; | |
| unsigned long mclk_freq; | |
| bool provider; | |
| unsigned int rates[ES8311_NUM_RATES]; | |
| struct snd_pcm_hw_constraint_list constraints; | |
| }; | |
| static const DECLARE_TLV_DB_SCALE(es8311_adc_vol_tlv, -9550, 50, 0); | |
| static const DECLARE_TLV_DB_SCALE(es8311_pga_gain_tlv, 0, 300, 0); | |
| static const DECLARE_TLV_DB_SCALE(es8311_adc_scale_tlv, 0, 600, 0); | |
| #define ES8311_DB_LRCK_STEPS \ | |
| "0.25db/4LRCK", \ | |
| "0.25db/8LRCK", \ | |
| "0.25db/16LRCK", \ | |
| "0.25db/32LRCK", \ | |
| "0.25db/64LRCK", \ | |
| "0.25db/128LRCK", \ | |
| "0.25db/256LRCK", \ | |
| "0.25db/512LRCK", \ | |
| "0.25db/1024LRCK", \ | |
| "0.25db/2048LRCK", \ | |
| "0.25db/4096LRCK", \ | |
| "0.25db/8192LRCK", \ | |
| "0.25db/16384LRCK", \ | |
| "0.25db/32768LRCK", \ | |
| "0.25db/65536LRCK", | |
| static const char *const es8311_level_winsize_txt[] = { | |
| "0.25db/2LRCK", | |
| ES8311_DB_LRCK_STEPS | |
| }; | |
| static SOC_ENUM_SINGLE_DECL( | |
| es8311_alc_winsize, ES8311_ADC4, | |
| ES8311_ADC4_ALC_WINSIZE_SHIFT, es8311_level_winsize_txt); | |
| static const DECLARE_TLV_DB_RANGE(es8311_level_tlv, | |
| 0, 1, TLV_DB_SCALE_ITEM(-3010, 600, 0), | |
| 2, 3, TLV_DB_SCALE_ITEM(-2060, 250, 0), | |
| 4, 5, TLV_DB_SCALE_ITEM(-1610, 160, 0), | |
| 6, 7, TLV_DB_SCALE_ITEM(-1320, 120, 0), | |
| 8, 9, TLV_DB_SCALE_ITEM(-1100, 90, 0), | |
| 10, 11, TLV_DB_SCALE_ITEM(-930, 80, 0), | |
| 12, 15, TLV_DB_SCALE_ITEM(-780, 60, 0), | |
| ); | |
| static const char *const es8311_ramprate_txt[] = { | |
| "Disabled", | |
| ES8311_DB_LRCK_STEPS | |
| }; | |
| static SOC_ENUM_SINGLE_DECL( | |
| es8311_adc_ramprate, ES8311_ADC1, | |
| ES8311_ADC1_RAMPRATE_SHIFT, es8311_ramprate_txt); | |
| static const char *const es8311_automute_winsize_txt[] = { | |
| "2048 samples", | |
| "4096 samples", | |
| "6144 samples", | |
| "8192 samples", | |
| "10240 samples", | |
| "12288 samples", | |
| "14336 samples", | |
| "16384 samples", | |
| "18432 samples", | |
| "20480 samples", | |
| "22528 samples", | |
| "24576 samples", | |
| "26624 samples", | |
| "28672 samples", | |
| "30720 samples", | |
| "32768 samples", | |
| }; | |
| static SOC_ENUM_SINGLE_DECL( | |
| es8311_automute_winsize, ES8311_ADC6, | |
| ES8311_ADC6_AUTOMUTE_WS_SHIFT, es8311_automute_winsize_txt); | |
| static const DECLARE_TLV_DB_RANGE(es8311_automute_ng_tlv, | |
| 0, 7, TLV_DB_SCALE_ITEM(-9600, 600, 0), | |
| 8, 15, TLV_DB_SCALE_ITEM(-5100, 300, 0), | |
| ); | |
| static const DECLARE_TLV_DB_SCALE(es8311_automute_vol_tlv, -2800, 400, 0); | |
| static const DECLARE_TLV_DB_SCALE(es8311_dac_vol_tlv, -9550, 50, 0); | |
| static SOC_ENUM_SINGLE_DECL( | |
| es8311_drc_winsize, ES8311_DAC4, | |
| ES8311_DAC4_DRC_WINSIZE_SHIFT, es8311_level_winsize_txt); | |
| static SOC_ENUM_SINGLE_DECL( | |
| es8311_dac_ramprate, ES8311_DAC6, | |
| ES8311_DAC6_RAMPRATE_SHIFT, es8311_ramprate_txt); | |
| static const char *const es8311_out_mode_txt[] = { | |
| "Lineout", | |
| "Headphones" | |
| }; | |
| static SOC_ENUM_SINGLE_DECL( | |
| es8311_out_mode, ES8311_SYS9, | |
| ES8311_SYS9_HPSW_SHIFT, es8311_out_mode_txt); | |
| static const struct snd_kcontrol_new es8311_snd_controls[] = { | |
| /* Capture path */ | |
| SOC_SINGLE_TLV("PGA Capture Volume", ES8311_SYS10, | |
| ES8311_SYS10_PGAGAIN_SHIFT, ES8311_SYS10_PGAGAIN_MAX, 0, | |
| es8311_pga_gain_tlv), | |
| SOC_SINGLE("ADC Polarity Invert Capture Switch", ES8311_ADC2, | |
| ES8311_ADC2_INV_SHIFT, 1, 0), | |
| SOC_SINGLE_TLV("ADC Scale Capture Volume", ES8311_ADC2, | |
| ES8311_ADC2_SCALE_SHIFT, ES8311_ADC2_SCALE_MAX, 0, | |
| es8311_adc_scale_tlv), | |
| SOC_SINGLE_TLV("ADC Capture Volume", ES8311_ADC3, | |
| ES8311_ADC3_VOLUME_SHIFT, ES8311_ADC3_VOLUME_MAX, 0, | |
| es8311_adc_vol_tlv), | |
| SOC_ENUM("ADC Capture Ramp Rate", es8311_adc_ramprate), | |
| SOC_SINGLE("ADC Automute Capture Switch", ES8311_ADC4, | |
| ES8311_ADC4_AUTOMUTE_EN_SHIFT, 1, 0), | |
| SOC_ENUM("ADC Automute Capture Winsize", es8311_automute_winsize), | |
| SOC_SINGLE_TLV("ADC Automute Noise Gate Capture Volume", ES8311_ADC6, | |
| ES8311_ADC6_AUTOMUTE_NG_SHIFT, | |
| ES8311_ADC6_AUTOMUTE_NG_MAX, 0, es8311_automute_ng_tlv), | |
| SOC_SINGLE_TLV("ADC Automute Capture Volume", ES8311_ADC7, | |
| ES8311_ADC7_AUTOMUTE_VOL_SHIFT, | |
| ES8311_ADC7_AUTOMUTE_VOL_MAX, 0, | |
| es8311_automute_vol_tlv), | |
| SOC_SINGLE("ADC HPF Capture Switch", ES8311_ADC8, ES8311_ADC8_HPF_SHIFT, | |
| 1, 0), | |
| SOC_SINGLE("ADC EQ Capture Switch", ES8311_ADC8, | |
| ES8311_ADC8_EQBYPASS_SHIFT, 1, 1), | |
| SOC_SINGLE("ALC Capture Switch", ES8311_ADC4, ES8311_ADC4_ALC_EN_SHIFT, | |
| 1, 0), | |
| SOC_SINGLE_TLV("ALC Capture Max Volume", ES8311_ADC5, | |
| ES8311_ADC5_ALC_MAXLEVEL_SHIFT, | |
| ES8311_ADC5_ALC_MAXLEVEL_MAX, 0, es8311_level_tlv), | |
| SOC_SINGLE_TLV("ALC Capture Min Volume", ES8311_ADC5, | |
| ES8311_ADC5_ALC_MINLEVEL_SHIFT, | |
| ES8311_ADC5_ALC_MINLEVEL_MAX, 0, es8311_level_tlv), | |
| SOC_ENUM("ALC Capture Winsize", es8311_alc_winsize), | |
| /* Playback path */ | |
| SOC_SINGLE_TLV("DAC Playback Volume", ES8311_DAC2, 0, | |
| ES8311_DAC2_VOLUME_MAX, 0, es8311_dac_vol_tlv), | |
| SOC_SINGLE("DRC Playback Switch", ES8311_DAC4, ES8311_DAC4_DRC_EN_SHIFT, | |
| 1, 0), | |
| SOC_SINGLE_TLV("DRC Playback Max Volume", ES8311_DAC5, | |
| ES8311_DAC5_DRC_MAXLEVEL_SHIFT, | |
| ES8311_DAC5_DRC_MAXLEVEL_MAX, 0, es8311_level_tlv), | |
| SOC_SINGLE_TLV("DRC Playback Min Volume", ES8311_DAC5, | |
| ES8311_DAC5_DRC_MINLEVEL_SHIFT, | |
| ES8311_DAC5_DRC_MINLEVEL_MAX, 0, es8311_level_tlv), | |
| SOC_ENUM("DRC Playback Winsize", es8311_drc_winsize), | |
| SOC_ENUM("DAC Playback Ramp Rate", es8311_dac_ramprate), | |
| SOC_SINGLE("DAC EQ Playback Switch", ES8311_DAC6, | |
| ES8311_DAC6_EQBYPASS_SHIFT, 1, 1), | |
| SOC_ENUM("Output Mode", es8311_out_mode), | |
| }; | |
| static const char *const es8311_diff_src_txt[] = { | |
| "Disabled", | |
| "MIC1P-MIC1N", | |
| }; | |
| static SOC_ENUM_SINGLE_DECL( | |
| es8311_diff_src_enum, ES8311_SYS10, | |
| ES8311_SYS10_LINESEL_SHIFT, es8311_diff_src_txt); | |
| static const struct snd_kcontrol_new es8311_diff_src_mux = | |
| SOC_DAPM_ENUM("Differential Source", es8311_diff_src_enum); | |
| static const char *const es8311_dmic_src_txt[] = { | |
| "Disabled", | |
| "DMIC from MIC1P", | |
| }; | |
| static SOC_ENUM_SINGLE_DECL( | |
| es8311_dmic_src_enum, ES8311_SYS10, | |
| ES8311_SYS10_DMIC_ON_SHIFT, es8311_dmic_src_txt); | |
| static const struct snd_kcontrol_new es8311_dmic_src_mux = | |
| SOC_DAPM_ENUM("Digital Mic Source", es8311_dmic_src_enum); | |
| static const char * const es8311_aif1tx_src_txt[] = { | |
| "ADC + ADC", | |
| "ADC + 0", | |
| "0 + ADC", | |
| "0 + 0", | |
| "DACL + ADC", | |
| "ADC + DACR", | |
| "DACL + DACR", | |
| }; | |
| static SOC_ENUM_SINGLE_DECL( | |
| es8311_aif1tx_src_enum, ES8311_GPIO, | |
| ES8311_GPIO_ADCDAT_SEL_SHIFT, es8311_aif1tx_src_txt); | |
| static const struct snd_kcontrol_new es8311_aif1tx_src_mux = | |
| SOC_DAPM_ENUM("AIF1TX Source", es8311_aif1tx_src_enum); | |
| static const char * const es8311_dac_src_txt[] = { | |
| "Left", | |
| "Right" | |
| }; | |
| static SOC_ENUM_SINGLE_DECL( | |
| es8311_dac_src_enum, ES8311_SDP_IN, | |
| ES8311_SDP_IN_SEL_SHIFT, es8311_dac_src_txt); | |
| static const struct snd_kcontrol_new es8311_dac_src_mux = | |
| SOC_DAPM_ENUM("Mono DAC Source", es8311_dac_src_enum); | |
| static const struct snd_soc_dapm_widget es8311_dapm_widgets[] = { | |
| SND_SOC_DAPM_SUPPLY("Bias", ES8311_SYS3, ES8311_SYS3_PDN_IBIASGEN_SHIFT, | |
| 1, NULL, 0), | |
| SND_SOC_DAPM_SUPPLY("Analog power", ES8311_SYS3, | |
| ES8311_SYS3_PDN_ANA_SHIFT, 1, NULL, 0), | |
| SND_SOC_DAPM_SUPPLY("Vref", ES8311_SYS3, ES8311_SYS3_PDN_VREF_SHIFT, 1, | |
| NULL, 0), | |
| /* Capture path */ | |
| SND_SOC_DAPM_INPUT("DMIC"), | |
| SND_SOC_DAPM_INPUT("MIC1"), | |
| SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, | |
| &es8311_diff_src_mux), | |
| SND_SOC_DAPM_SUPPLY("ADC Bias Gen", ES8311_SYS3, | |
| ES8311_SYS3_PDN_ADCBIASGEN_SHIFT, 1, NULL, 0), | |
| SND_SOC_DAPM_SUPPLY("ADC Vref Gen", ES8311_SYS3, | |
| ES8311_SYS3_PDN_ADCVREFGEN_SHIFT, 1, NULL, 0), | |
| SND_SOC_DAPM_SUPPLY("ADC Clock", ES8311_CLKMGR1, | |
| ES8311_CLKMGR1_CLKADC_ON_SHIFT, 0, NULL, 0), | |
| SND_SOC_DAPM_SUPPLY("ADC Analog Clock", ES8311_CLKMGR1, | |
| ES8311_CLKMGR1_ANACLKADC_ON_SHIFT, 0, NULL, 0), | |
| SND_SOC_DAPM_PGA("PGA", ES8311_SYS4, ES8311_SYS4_PDN_PGA_SHIFT, 1, NULL, | |
| 0), | |
| SND_SOC_DAPM_ADC("Mono ADC", NULL, ES8311_SYS4, | |
| ES8311_SYS4_PDN_MOD_SHIFT, 1), | |
| SND_SOC_DAPM_MUX("Digital Mic Mux", SND_SOC_NOPM, 0, 0, | |
| &es8311_dmic_src_mux), | |
| SND_SOC_DAPM_MUX("AIF1TX Source Mux", SND_SOC_NOPM, 0, 0, | |
| &es8311_aif1tx_src_mux), | |
| SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, ES8311_SDP_OUT, | |
| ES8311_SDP_MUTE_SHIFT, 1), | |
| /* Playback path */ | |
| SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, ES8311_SDP_IN, | |
| ES8311_SDP_MUTE_SHIFT, 1), | |
| SND_SOC_DAPM_MUX("Mono DAC Source Mux", SND_SOC_NOPM, 0, 0, | |
| &es8311_dac_src_mux), | |
| SND_SOC_DAPM_DAC("Mono DAC", NULL, ES8311_SYS8, | |
| ES8311_SYS8_PDN_DAC_SHIFT, 1), | |
| SND_SOC_DAPM_SUPPLY("DAC Clock", ES8311_CLKMGR1, | |
| ES8311_CLKMGR1_CLKDAC_ON_SHIFT, 0, NULL, 0), | |
| SND_SOC_DAPM_SUPPLY("DAC Analog Clock", ES8311_CLKMGR1, | |
| ES8311_CLKMGR1_ANACLKDAC_ON_SHIFT, 0, NULL, 0), | |
| SND_SOC_DAPM_SUPPLY("DAC Vref Gen", ES8311_SYS3, | |
| ES8311_SYS3_PDN_DACVREFGEN_SHIFT, 1, NULL, 0), | |
| SND_SOC_DAPM_OUTPUT("OUT"), | |
| }; | |
| static const struct snd_soc_dapm_route es8311_dapm_routes[] = { | |
| /* Capture Path */ | |
| { "MIC1", NULL, "Bias" }, | |
| { "MIC1", NULL, "Analog power" }, | |
| { "MIC1", NULL, "Vref" }, | |
| { "Differential Mux", "MIC1P-MIC1N", "MIC1" }, | |
| { "PGA", NULL, "Differential Mux" }, | |
| { "Mono ADC", NULL, "PGA" }, | |
| { "Mono ADC", NULL, "ADC Bias Gen" }, | |
| { "Mono ADC", NULL, "ADC Vref Gen" }, | |
| { "Mono ADC", NULL, "ADC Clock" }, | |
| { "Mono ADC", NULL, "ADC Analog Clock" }, | |
| { "Digital Mic Mux", "Disabled", "Mono ADC" }, | |
| { "Digital Mic Mux", "DMIC from MIC1P", "DMIC" }, | |
| { "AIF1TX Source Mux", "ADC + ADC", "Digital Mic Mux" }, | |
| { "AIF1TX Source Mux", "ADC + 0", "Digital Mic Mux" }, | |
| { "AIF1TX Source Mux", "0 + ADC", "Digital Mic Mux" }, | |
| { "AIF1TX Source Mux", "DACL + ADC", "Digital Mic Mux" }, | |
| { "AIF1TX Source Mux", "ADC + DACR", "Digital Mic Mux" }, | |
| { "AIF1TX", NULL, "AIF1TX Source Mux" }, | |
| /* Playback Path */ | |
| { "Mono DAC Source Mux", "Left", "AIF1RX" }, | |
| { "Mono DAC Source Mux", "Right", "AIF1RX" }, | |
| { "Mono DAC", NULL, "Mono DAC Source Mux" }, | |
| { "Mono DAC", NULL, "DAC Clock" }, | |
| { "Mono DAC", NULL, "DAC Analog Clock" }, | |
| { "OUT", NULL, "Mono DAC" }, | |
| { "OUT", NULL, "Bias" }, | |
| { "OUT", NULL, "Analog power" }, | |
| { "OUT", NULL, "Vref" }, | |
| { "OUT", NULL, "DAC Vref Gen" }, | |
| }; | |
| /* Bit clock divider values: | |
| * from 1 to 20: the register takes the div value - 1 | |
| * above 20: the register takes the corresponding idx of the div value | |
| * in the following table + 20 | |
| */ | |
| #define ES8311_BCLK_DIV_IDX_OFFSET 20 | |
| static const unsigned int es8311_bclk_divs[] = { | |
| 22, 24, 25, 30, 32, 33, 34, 36, 44, 48, 66, 72 | |
| }; | |
| struct es8311_mclk_coeff { | |
| unsigned int rate; | |
| unsigned int mclk; | |
| unsigned int div; | |
| unsigned int mult; | |
| unsigned int div_adc_dac; | |
| }; | |
| #define ES8311_MCLK_MAX_FREQ 49200000 | |
| /* Coefficients for common master clock frequencies based on clock table from | |
| * documentation. Limited to have a ratio of adc (or dac) clock to lrclk equal | |
| * to 256. This to keep the default adc and dac oversampling and adc scale | |
| * settings. Internal mclk dividers and multipliers are dynamically adjusted to | |
| * support, respectively, multiples (up to x8) and factors (/2,4,8) of listed | |
| * mclks frequencies (see es8311_cmp_adj_mclk_coeff). | |
| * All rates are supported when mclk/rate ratio is 32, 64, 128, 256, 384 or 512 | |
| * (upper limit due to max mclk freq of 49.2MHz). | |
| */ | |
| static const struct es8311_mclk_coeff es8311_mclk_coeffs[] = { | |
| { 8000, 2048000, 1, 1, 1 }, | |
| { 8000, 6144000, 3, 1, 1 }, | |
| { 8000, 18432000, 3, 1, 3 }, | |
| { 11025, 2822400, 1, 1, 1 }, | |
| { 11025, 8467200, 3, 1, 1 }, | |
| { 16000, 4096000, 1, 1, 1 }, | |
| { 16000, 12288000, 3, 1, 1 }, | |
| { 16000, 18432000, 3, 2, 3 }, | |
| { 22050, 5644800, 1, 1, 1 }, | |
| { 22050, 16934400, 3, 1, 1 }, | |
| { 32000, 8192000, 1, 1, 1 }, | |
| { 32000, 12288000, 3, 2, 1 }, | |
| { 32000, 18432000, 3, 4, 3 }, | |
| { 44100, 11289600, 1, 1, 1 }, | |
| { 44100, 33868800, 3, 1, 1 }, | |
| { 48000, 12288000, 1, 1, 1 }, | |
| { 48000, 18432000, 3, 2, 1 }, | |
| { 64000, 8192000, 1, 2, 1 }, | |
| { 64000, 12288000, 3, 4, 1 }, | |
| { 88200, 11289600, 1, 2, 1 }, | |
| { 88200, 33868800, 3, 2, 1 }, | |
| { 96000, 12288000, 1, 2, 1 }, | |
| { 96000, 18432000, 3, 4, 1 }, | |
| }; | |
| /* Compare coeff with provided mclk_freq and adjust it if needed. | |
| * If frequencies match, return 0 and the unaltered coeff copy into out_coeff. | |
| * If mclk_freq is a valid multiple or factor of coeff mclk freq, return 0 and | |
| * the adjusted coeff copy into out_coeff. | |
| * Return -EINVAL otherwise. | |
| */ | |
| static int es8311_cmp_adj_mclk_coeff(unsigned int mclk_freq, | |
| const struct es8311_mclk_coeff *coeff, | |
| struct es8311_mclk_coeff *out_coeff) | |
| { | |
| if (WARN_ON_ONCE(!coeff)) | |
| return -EINVAL; | |
| unsigned int div = coeff->div; | |
| unsigned int mult = coeff->mult; | |
| bool match = false; | |
| if (coeff->mclk == mclk_freq) { | |
| match = true; | |
| } else if (mclk_freq % coeff->mclk == 0) { | |
| div = mclk_freq / coeff->mclk; | |
| div *= coeff->div; | |
| if (div <= 8) | |
| match = true; | |
| } else if (coeff->mclk % mclk_freq == 0) { | |
| mult = coeff->mclk / mclk_freq; | |
| if (mult == 2 || mult == 4 || mult == 8) { | |
| mult *= coeff->mult; | |
| if (mult <= 8) | |
| match = true; | |
| } | |
| } | |
| if (!match) | |
| return -EINVAL; | |
| if (out_coeff) { | |
| *out_coeff = *coeff; | |
| out_coeff->div = div; | |
| out_coeff->mult = mult; | |
| } | |
| return 0; | |
| } | |
| static int es8311_get_mclk_coeff(unsigned int mclk_freq, unsigned int rate, | |
| struct es8311_mclk_coeff *out_coeff) | |
| { | |
| for (unsigned int i = 0; i < ARRAY_SIZE(es8311_mclk_coeffs); i++) { | |
| const struct es8311_mclk_coeff *coeff = &es8311_mclk_coeffs[i]; | |
| if (coeff->rate != rate) | |
| continue; | |
| int ret = | |
| es8311_cmp_adj_mclk_coeff(mclk_freq, coeff, out_coeff); | |
| if (ret == 0) | |
| return 0; | |
| } | |
| return -EINVAL; | |
| } | |
| static void es8311_set_sysclk_constraints(unsigned int mclk_freq, | |
| struct es8311_priv *es8311) | |
| { | |
| unsigned int count = 0; | |
| for (unsigned int i = 0; i < ARRAY_SIZE(es8311_mclk_coeffs) && | |
| count < ARRAY_SIZE(es8311->rates); i++) { | |
| const struct es8311_mclk_coeff *coeff = &es8311_mclk_coeffs[i]; | |
| if (count > 0 && coeff->rate == es8311->rates[count - 1]) | |
| continue; | |
| int ret = es8311_cmp_adj_mclk_coeff(mclk_freq, coeff, NULL); | |
| if (ret == 0) | |
| es8311->rates[count++] = coeff->rate; | |
| } | |
| if (count) { | |
| es8311->constraints.list = es8311->rates; | |
| es8311->constraints.count = count; | |
| } | |
| } | |
| static int es8311_mute(struct snd_soc_dai *dai, int mute, int direction) | |
| { | |
| struct snd_soc_component *component = dai->component; | |
| struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); | |
| if (direction == SNDRV_PCM_STREAM_PLAYBACK) { | |
| unsigned int mask = ES8311_DAC1_DAC_DSMMUTE | | |
| ES8311_DAC1_DAC_DEMMUTE; | |
| unsigned int val = mute ? mask : 0; | |
| regmap_update_bits(es8311->regmap, ES8311_DAC1, mask, val); | |
| } | |
| return 0; | |
| } | |
| static int es8311_startup(struct snd_pcm_substream *substream, | |
| struct snd_soc_dai *dai) | |
| { | |
| struct snd_soc_component *component = dai->component; | |
| struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); | |
| if (es8311->constraints.list) { | |
| snd_pcm_hw_constraint_list(substream->runtime, 0, | |
| SNDRV_PCM_HW_PARAM_RATE, | |
| &es8311->constraints); | |
| } | |
| return 0; | |
| } | |
| static int es8311_hw_params(struct snd_pcm_substream *substream, | |
| struct snd_pcm_hw_params *params, | |
| struct snd_soc_dai *dai) | |
| { | |
| struct snd_soc_component *component = dai->component; | |
| struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); | |
| unsigned int wl; | |
| int par_width = params_width(params); | |
| switch (par_width) { | |
| case 16: | |
| wl = ES8311_SDP_WL_16; | |
| break; | |
| case 18: | |
| wl = ES8311_SDP_WL_18; | |
| break; | |
| case 20: | |
| wl = ES8311_SDP_WL_20; | |
| break; | |
| case 24: | |
| wl = ES8311_SDP_WL_24; | |
| break; | |
| case 32: | |
| wl = ES8311_SDP_WL_32; | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| unsigned int width = (unsigned int)par_width; | |
| if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
| snd_soc_component_update_bits(component, ES8311_SDP_IN, | |
| ES8311_SDP_WL_MASK, | |
| wl << ES8311_SDP_WL_SHIFT); | |
| } else { | |
| snd_soc_component_update_bits(component, ES8311_SDP_OUT, | |
| ES8311_SDP_WL_MASK, | |
| wl << ES8311_SDP_WL_SHIFT); | |
| } | |
| if (es8311->mclk_freq > ES8311_MCLK_MAX_FREQ) { | |
| dev_err(component->dev, "mclk frequency %lu too high\n", | |
| es8311->mclk_freq); | |
| return -EINVAL; | |
| } | |
| unsigned int mclk_freq = es8311->mclk_freq; | |
| unsigned int rate = params_rate(params); | |
| unsigned int clkmgr = ES8311_CLKMGR1_MCLK_ON; | |
| if (!mclk_freq) { | |
| if (es8311->provider) { | |
| dev_err(component->dev, | |
| "mclk not configured, cannot run as master\n"); | |
| return -EINVAL; | |
| } | |
| dev_dbg(component->dev, | |
| "mclk not configured, use bclk as internal mclk\n"); | |
| clkmgr = ES8311_CLKMGR1_MCLK_SEL; | |
| mclk_freq = rate * width * 2; | |
| } | |
| struct es8311_mclk_coeff coeff; | |
| int ret = es8311_get_mclk_coeff(mclk_freq, rate, &coeff); | |
| if (ret) { | |
| dev_err(component->dev, "unable to find mclk coefficient\n"); | |
| return ret; | |
| } | |
| unsigned int mask = ES8311_CLKMGR1_MCLK_SEL | ES8311_CLKMGR1_MCLK_ON | | |
| ES8311_CLKMGR1_BCLK_ON; | |
| clkmgr |= ES8311_CLKMGR1_BCLK_ON; | |
| snd_soc_component_update_bits(component, ES8311_CLKMGR1, mask, clkmgr); | |
| if (WARN_ON_ONCE(coeff.div == 0 || coeff.div > 8 || | |
| coeff.div_adc_dac == 0 || coeff.div_adc_dac > 8)) | |
| return -EINVAL; | |
| unsigned int mult; | |
| switch (coeff.mult) { | |
| case 1: | |
| mult = 0; | |
| break; | |
| case 2: | |
| mult = 1; | |
| break; | |
| case 4: | |
| mult = 2; | |
| break; | |
| case 8: | |
| mult = 3; | |
| break; | |
| default: | |
| WARN_ON_ONCE(true); | |
| return -EINVAL; | |
| } | |
| mask = ES8311_CLKMGR2_DIV_PRE_MASK | ES8311_CLKMGR2_MULT_PRE_MASK; | |
| clkmgr = (coeff.div - 1) << ES8311_CLKMGR2_DIV_PRE_SHIFT | | |
| mult << ES8311_CLKMGR2_MULT_PRE_SHIFT; | |
| snd_soc_component_update_bits(component, ES8311_CLKMGR2, mask, clkmgr); | |
| mask = ES8311_CLKMGR5_ADC_DIV_MASK | ES8311_CLKMGR5_DAC_DIV_MASK; | |
| clkmgr = (coeff.div_adc_dac - 1) << ES8311_CLKMGR5_ADC_DIV_SHIFT | | |
| (coeff.div_adc_dac - 1) << ES8311_CLKMGR5_DAC_DIV_SHIFT; | |
| snd_soc_component_update_bits(component, ES8311_CLKMGR5, mask, clkmgr); | |
| if (es8311->provider) { | |
| unsigned int div_lrclk = mclk_freq / rate; | |
| if (WARN_ON_ONCE(div_lrclk == 0 || | |
| div_lrclk > ES8311_CLKMGR_LRCLK_DIV_MAX + 1)) | |
| return -EINVAL; | |
| mask = ES8311_CLKMGR7_LRCLK_DIV_H_MASK; | |
| clkmgr = (div_lrclk - 1) >> 8; | |
| snd_soc_component_update_bits(component, ES8311_CLKMGR7, mask, | |
| clkmgr); | |
| clkmgr = (div_lrclk - 1) & 0xFF; | |
| snd_soc_component_write(component, ES8311_CLKMGR8, clkmgr); | |
| if (div_lrclk % (2 * width) != 0) { | |
| dev_err(component->dev, | |
| "unable to divide mclk %u to generate bclk\n", | |
| mclk_freq); | |
| return -EINVAL; | |
| } | |
| unsigned int div_bclk = div_lrclk / (2 * width); | |
| mask = ES8311_CLKMGR6_DIV_BCLK_MASK; | |
| if (div_bclk <= ES8311_BCLK_DIV_IDX_OFFSET) { | |
| clkmgr = div_bclk - 1; | |
| } else { | |
| unsigned int i; | |
| for (i = 0; i < ARRAY_SIZE(es8311_bclk_divs); i++) { | |
| if (es8311_bclk_divs[i] == div_bclk) | |
| break; | |
| } | |
| if (i == ARRAY_SIZE(es8311_bclk_divs)) { | |
| dev_err(component->dev, | |
| "bclk divider %u not supported\n", | |
| div_bclk); | |
| return -EINVAL; | |
| } | |
| clkmgr = i + ES8311_BCLK_DIV_IDX_OFFSET; | |
| } | |
| snd_soc_component_update_bits(component, ES8311_CLKMGR6, mask, | |
| clkmgr); | |
| } | |
| return 0; | |
| } | |
| static int es8311_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id, | |
| unsigned int freq, int dir) | |
| { | |
| struct snd_soc_component *component = codec_dai->component; | |
| struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); | |
| if (freq > ES8311_MCLK_MAX_FREQ) { | |
| dev_err(component->dev, "invalid frequency %u: too high\n", | |
| freq); | |
| return -EINVAL; | |
| } | |
| if (es8311->mclk_freq == freq) | |
| return 0; | |
| es8311->mclk_freq = freq; | |
| es8311->constraints.list = NULL; | |
| es8311->constraints.count = 0; | |
| if (freq == 0) | |
| return 0; | |
| int ret = clk_set_rate(es8311->mclk, freq); | |
| if (ret) { | |
| dev_err(component->dev, "unable to set mclk rate\n"); | |
| return ret; | |
| } | |
| es8311_set_sysclk_constraints(freq, es8311); | |
| return ret; | |
| } | |
| static int es8311_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) | |
| { | |
| struct snd_soc_component *component = codec_dai->component; | |
| struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); | |
| switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { | |
| case SND_SOC_DAIFMT_CBP_CFP: | |
| /* Master mode */ | |
| es8311->provider = true; | |
| snd_soc_component_update_bits(component, ES8311_RESET, | |
| ES8311_RESET_MSC, | |
| ES8311_RESET_MSC); | |
| break; | |
| case SND_SOC_DAIFMT_CBC_CFC: | |
| /* Slave mode */ | |
| es8311->provider = false; | |
| snd_soc_component_update_bits(component, ES8311_RESET, | |
| ES8311_RESET_MSC, 0); | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| unsigned int sdp = 0; | |
| switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | |
| case SND_SOC_DAIFMT_I2S: | |
| sdp |= ES8311_SDP_FMT_I2S; | |
| break; | |
| case SND_SOC_DAIFMT_LEFT_J: | |
| sdp |= ES8311_SDP_FMT_LEFT_J; | |
| break; | |
| case SND_SOC_DAIFMT_RIGHT_J: | |
| dev_err(component->dev, "right justified mode not supported\n"); | |
| return -EINVAL; | |
| case SND_SOC_DAIFMT_DSP_B: | |
| sdp |= ES8311_SDP_LRP; | |
| fallthrough; | |
| case SND_SOC_DAIFMT_DSP_A: | |
| sdp |= ES8311_SDP_FMT_DSP; | |
| switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | |
| case SND_SOC_DAIFMT_NB_NF: | |
| case SND_SOC_DAIFMT_IB_NF: | |
| break; | |
| default: | |
| dev_err(component->dev, | |
| "inverted fsync not supported in dsp mode\n"); | |
| return -EINVAL; | |
| } | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| unsigned int clkmgr = 0; | |
| switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | |
| case SND_SOC_DAIFMT_NB_NF: | |
| break; | |
| case SND_SOC_DAIFMT_NB_IF: | |
| sdp |= ES8311_SDP_LRP; | |
| break; | |
| case SND_SOC_DAIFMT_IB_NF: | |
| clkmgr |= ES8311_CLKMGR6_BCLK_INV; | |
| break; | |
| case SND_SOC_DAIFMT_IB_IF: | |
| clkmgr |= ES8311_CLKMGR6_BCLK_INV; | |
| sdp |= ES8311_SDP_LRP; | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| unsigned int mask = ES8311_CLKMGR6_BCLK_INV; | |
| snd_soc_component_update_bits(component, ES8311_CLKMGR6, mask, clkmgr); | |
| mask = ES8311_SDP_FMT_MASK | ES8311_SDP_LRP; | |
| snd_soc_component_update_bits(component, ES8311_SDP_IN, mask, sdp); | |
| snd_soc_component_update_bits(component, ES8311_SDP_OUT, mask, sdp); | |
| return 0; | |
| } | |
| static int es8311_set_bias_level(struct snd_soc_component *component, | |
| enum snd_soc_bias_level level) | |
| { | |
| struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); | |
| struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); | |
| int ret; | |
| switch (level) { | |
| case SND_SOC_BIAS_ON: | |
| break; | |
| case SND_SOC_BIAS_PREPARE: | |
| break; | |
| case SND_SOC_BIAS_STANDBY: | |
| if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) { | |
| ret = clk_prepare_enable(es8311->mclk); | |
| if (ret) { | |
| dev_err(component->dev, | |
| "unable to prepare mclk\n"); | |
| return ret; | |
| } | |
| ret = snd_soc_component_update_bits( | |
| component, ES8311_SYS3, | |
| ES8311_SYS3_PDN_VMIDSEL_MASK, | |
| ES8311_SYS3_PDN_VMIDSEL_STARTUP_NORMAL_SPEED); | |
| if (ret < 0) { | |
| clk_disable_unprepare(es8311->mclk); | |
| return ret; | |
| } | |
| } | |
| break; | |
| case SND_SOC_BIAS_OFF: | |
| clk_disable_unprepare(es8311->mclk); | |
| snd_soc_component_update_bits( | |
| component, ES8311_SYS3, ES8311_SYS3_PDN_VMIDSEL_MASK, | |
| ES8311_SYS3_PDN_VMIDSEL_POWER_DOWN); | |
| break; | |
| default: | |
| break; | |
| } | |
| return 0; | |
| } | |
| static const struct snd_soc_dai_ops es8311_dai_ops = { | |
| .startup = es8311_startup, | |
| .hw_params = es8311_hw_params, | |
| .mute_stream = es8311_mute, | |
| .set_sysclk = es8311_set_sysclk, | |
| .set_fmt = es8311_set_dai_fmt, | |
| .no_capture_mute = 1, | |
| }; | |
| static struct snd_soc_dai_driver es8311_dai = { | |
| .name = "es8311", | |
| .playback = { | |
| .stream_name = "AIF1 Playback", | |
| .channels_min = 1, | |
| .channels_max = 2, | |
| .rates = ES8311_RATES, | |
| .formats = ES8311_FORMATS, | |
| }, | |
| .capture = { | |
| .stream_name = "AIF1 Capture", | |
| .channels_min = 1, | |
| .channels_max = 2, | |
| .rates = ES8311_RATES, | |
| .formats = ES8311_FORMATS, | |
| }, | |
| .ops = &es8311_dai_ops, | |
| .symmetric_rate = 1, | |
| }; | |
| static void es8311_reset(struct snd_soc_component *component, bool reset) | |
| { | |
| /* Reset procedure: | |
| * (1) power down state machine and reset codec blocks then, | |
| * (2) after a short delay, power up state machine and leave reset mode. | |
| * Specific delay is not documented, using the same as es8316. | |
| */ | |
| unsigned int mask = ES8311_RESET_CSM_ON | ES8311_RESET_RST_MASK; | |
| if (reset) { | |
| /* Enter reset mode */ | |
| snd_soc_component_update_bits(component, ES8311_RESET, mask, | |
| ES8311_RESET_RST_MASK); | |
| } else { | |
| /* Leave reset mode */ | |
| usleep_range(5000, 5500); | |
| snd_soc_component_update_bits(component, ES8311_RESET, mask, | |
| ES8311_RESET_CSM_ON); | |
| } | |
| } | |
| static int es8311_suspend(struct snd_soc_component *component) | |
| { | |
| struct es8311_priv *es8311; | |
| es8311 = snd_soc_component_get_drvdata(component); | |
| es8311_reset(component, true); | |
| regcache_cache_only(es8311->regmap, true); | |
| regcache_mark_dirty(es8311->regmap); | |
| return 0; | |
| } | |
| static int es8311_resume(struct snd_soc_component *component) | |
| { | |
| struct es8311_priv *es8311; | |
| int ret; | |
| es8311 = snd_soc_component_get_drvdata(component); | |
| es8311_reset(component, false); | |
| regcache_cache_only(es8311->regmap, false); | |
| ret = regcache_sync(es8311->regmap); | |
| if (ret) { | |
| dev_err(component->dev, "unable to sync regcache\n"); | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| static int es8311_component_probe(struct snd_soc_component *component) | |
| { | |
| struct es8311_priv *es8311; | |
| es8311 = snd_soc_component_get_drvdata(component); | |
| es8311->mclk = devm_clk_get_optional(component->dev, "mclk"); | |
| if (IS_ERR(es8311->mclk)) { | |
| dev_err(component->dev, "invalid mclk\n"); | |
| return PTR_ERR(es8311->mclk); | |
| } | |
| es8311->mclk_freq = clk_get_rate(es8311->mclk); | |
| if (es8311->mclk_freq > 0 && es8311->mclk_freq < ES8311_MCLK_MAX_FREQ) | |
| es8311_set_sysclk_constraints(es8311->mclk_freq, es8311); | |
| es8311_reset(component, true); | |
| es8311_reset(component, false); | |
| /* Set minimal power up time */ | |
| snd_soc_component_write(component, ES8311_SYS1, 0); | |
| snd_soc_component_write(component, ES8311_SYS2, 0); | |
| return 0; | |
| } | |
| static const struct regmap_config es8311_regmap_config = { | |
| .reg_bits = 8, | |
| .val_bits = 8, | |
| .max_register = ES8311_REG_MAX, | |
| .cache_type = REGCACHE_MAPLE, | |
| .use_single_read = true, | |
| .use_single_write = true, | |
| }; | |
| static const struct snd_soc_component_driver es8311_component_driver = { | |
| .probe = es8311_component_probe, | |
| .suspend = es8311_suspend, | |
| .resume = es8311_resume, | |
| .set_bias_level = es8311_set_bias_level, | |
| .controls = es8311_snd_controls, | |
| .num_controls = ARRAY_SIZE(es8311_snd_controls), | |
| .dapm_widgets = es8311_dapm_widgets, | |
| .num_dapm_widgets = ARRAY_SIZE(es8311_dapm_widgets), | |
| .dapm_routes = es8311_dapm_routes, | |
| .num_dapm_routes = ARRAY_SIZE(es8311_dapm_routes), | |
| .use_pmdown_time = 1, | |
| .endianness = 1, | |
| }; | |
| static int es8311_i2c_probe(struct i2c_client *i2c_client) | |
| { | |
| struct es8311_priv *es8311; | |
| struct device *dev = &i2c_client->dev; | |
| es8311 = devm_kzalloc(dev, sizeof(*es8311), GFP_KERNEL); | |
| if (es8311 == NULL) | |
| return -ENOMEM; | |
| es8311->regmap = | |
| devm_regmap_init_i2c(i2c_client, &es8311_regmap_config); | |
| if (IS_ERR(es8311->regmap)) | |
| return PTR_ERR(es8311->regmap); | |
| i2c_set_clientdata(i2c_client, es8311); | |
| return devm_snd_soc_register_component(dev, &es8311_component_driver, | |
| &es8311_dai, 1); | |
| } | |
| static const struct i2c_device_id es8311_id[] = { | |
| { .name = "es8311" }, | |
| { } | |
| }; | |
| MODULE_DEVICE_TABLE(i2c, es8311_id); | |
| static const struct of_device_id es8311_of_match[] = { | |
| { | |
| .compatible = "everest,es8311", | |
| }, | |
| {} | |
| }; | |
| MODULE_DEVICE_TABLE(of, es8311_of_match); | |
| static struct i2c_driver es8311_i2c_driver = { | |
| .driver = { | |
| .name = "es8311", | |
| .of_match_table = es8311_of_match, | |
| }, | |
| .probe = es8311_i2c_probe, | |
| .id_table = es8311_id, | |
| }; | |
| module_i2c_driver(es8311_i2c_driver); | |
| MODULE_DESCRIPTION("ASoC ES8311 driver"); | |
| MODULE_AUTHOR("Matteo Martelli <matteomartelli3@gmail.com>"); | |
| MODULE_LICENSE("GPL"); |