// 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);
}
static const struct i2c_device_id es8311_id[] = {
{ "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 " );
MODULE_LICENSE("GPL" );
Messung V0.5 C=99 H=99 G=98
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland