ASoC: codecs: es8311: add everest es8311 codec support
Add support for the Everest-semi ES8311 codec.
Everest-semi ES8311 codec is a low-power mono audio codec with I2S audio
interface and I2C control.
Supported features:
* Both master and slave mode. Master clock is optional in slave mode.
* Sample rates from 8KHz to 96KHz.
* Sample formats: S16_LE, S18_3LE, S20_3LE, S24_3LE, S24_LE and S32_LE.
* I2S formats: I2S, LEFT_J, DSP_A, DSP_B.
* BCLK and FSYNC clocks inversion.
* Component suspend/resume.
* ADC, PGA, DAC controls.
* ADC DSP controls: volume, fade (ramp rate), ALC, automute, HPF, EQ.
* DAC DSP controls: volume, fade (ramp rate), DRC, EQ.
* DAPM routes: capture path with input source selection (differential
MIC/DMIC) and AIF channel source selection; playback path with DAC
channel source selection.
Limitations:
* Support only for master clocks with 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. Anyway all 8-96KHz sample rates are supported
when the ratio of MCLK to sample rate is 32, 64, 128, 256, 384 or 512
(upper limit due to max MCLK freq of 49.2MHz).
* Coefficients for ADC HPF and ADC/DAC EQ not supported.
* Digital mic supported but not tested.
* S18_3LE, S20_3LE and S24_3LE formats supported but not tested.
Signed-off-by: Matteo Martelli <matteomartelli3@gmail.com>
Link: https://msgid.link/r/20240522164722.954656-3-matteomartelli3@gmail.com
Signed-off-by: Mark Brown <broonie@kernel.org>
2024-05-22 16:46:56 +00:00
|
|
|
// 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);
|
|
|
|
|
|
|
|
switch (level) {
|
|
|
|
case SND_SOC_BIAS_ON:
|
|
|
|
break;
|
|
|
|
case SND_SOC_BIAS_PREPARE:
|
|
|
|
break;
|
|
|
|
case SND_SOC_BIAS_STANDBY:
|
|
|
|
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
|
|
|
|
int ret = clk_prepare_enable(es8311->mclk);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(component->dev,
|
|
|
|
"unable to prepare mclk\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
snd_soc_component_update_bits(
|
|
|
|
component, ES8311_SYS3,
|
|
|
|
ES8311_SYS3_PDN_VMIDSEL_MASK,
|
|
|
|
ES8311_SYS3_PDN_VMIDSEL_STARTUP_NORMAL_SPEED);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
es8311 = snd_soc_component_get_drvdata(component);
|
|
|
|
|
|
|
|
es8311_reset(component, false);
|
|
|
|
|
|
|
|
regcache_cache_only(es8311->regmap, false);
|
|
|
|
regcache_sync(es8311->regmap);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2024-06-24 13:17:27 +00:00
|
|
|
static const struct i2c_device_id es8311_id[] = {
|
|
|
|
{ "es8311" },
|
|
|
|
{ }
|
|
|
|
};
|
ASoC: codecs: es8311: add everest es8311 codec support
Add support for the Everest-semi ES8311 codec.
Everest-semi ES8311 codec is a low-power mono audio codec with I2S audio
interface and I2C control.
Supported features:
* Both master and slave mode. Master clock is optional in slave mode.
* Sample rates from 8KHz to 96KHz.
* Sample formats: S16_LE, S18_3LE, S20_3LE, S24_3LE, S24_LE and S32_LE.
* I2S formats: I2S, LEFT_J, DSP_A, DSP_B.
* BCLK and FSYNC clocks inversion.
* Component suspend/resume.
* ADC, PGA, DAC controls.
* ADC DSP controls: volume, fade (ramp rate), ALC, automute, HPF, EQ.
* DAC DSP controls: volume, fade (ramp rate), DRC, EQ.
* DAPM routes: capture path with input source selection (differential
MIC/DMIC) and AIF channel source selection; playback path with DAC
channel source selection.
Limitations:
* Support only for master clocks with 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. Anyway all 8-96KHz sample rates are supported
when the ratio of MCLK to sample rate is 32, 64, 128, 256, 384 or 512
(upper limit due to max MCLK freq of 49.2MHz).
* Coefficients for ADC HPF and ADC/DAC EQ not supported.
* Digital mic supported but not tested.
* S18_3LE, S20_3LE and S24_3LE formats supported but not tested.
Signed-off-by: Matteo Martelli <matteomartelli3@gmail.com>
Link: https://msgid.link/r/20240522164722.954656-3-matteomartelli3@gmail.com
Signed-off-by: Mark Brown <broonie@kernel.org>
2024-05-22 16:46:56 +00:00
|
|
|
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");
|