mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 20:32:04 +09:00
ASoC: es8311: add support es8311 codec driver
Signed-off-by: Xing Zheng <zhengxing@rock-chips.com> Change-Id: Iae35145535664754f3babf4245c9682077c1fc00
This commit is contained in:
@@ -605,6 +605,10 @@ config SND_SOC_ES7134
|
||||
config SND_SOC_ES7241
|
||||
tristate "Everest Semi ES7241 CODEC"
|
||||
|
||||
config SND_SOC_ES8311
|
||||
tristate "Everest Semi ES8311 CODEC"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_ES8316
|
||||
tristate "Everest Semi ES8316 CODEC"
|
||||
depends on I2C
|
||||
|
||||
@@ -74,6 +74,7 @@ snd-soc-dmic-objs := dmic.o
|
||||
snd-soc-dummy-codec-objs := dummy-codec.o
|
||||
snd-soc-es7134-objs := es7134.o
|
||||
snd-soc-es7241-objs := es7241.o
|
||||
snd-soc-es8311-objs := es8311.o
|
||||
snd-soc-es8316-objs := es8316.o
|
||||
snd-soc-es8323-objs := es8323.o
|
||||
snd-soc-es8328-objs := es8328.o
|
||||
@@ -344,6 +345,7 @@ obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
|
||||
obj-$(CONFIG_SND_SOC_DUMMY_CODEC) += snd-soc-dummy-codec.o
|
||||
obj-$(CONFIG_SND_SOC_ES7134) += snd-soc-es7134.o
|
||||
obj-$(CONFIG_SND_SOC_ES7241) += snd-soc-es7241.o
|
||||
obj-$(CONFIG_SND_SOC_ES8311) += snd-soc-es8311.o
|
||||
obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o
|
||||
obj-$(CONFIG_SND_SOC_ES8323) += snd-soc-es8323.o
|
||||
obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
|
||||
|
||||
648
sound/soc/codecs/es8311.c
Normal file
648
sound/soc/codecs/es8311.c
Normal file
@@ -0,0 +1,648 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* es8311.c -- ES8311/ES8312 ALSA SoC Audio Codec
|
||||
*
|
||||
* Copyright (C) 2018 Everest Semiconductor Co., Ltd
|
||||
*
|
||||
* Authors: David Yang(yangxiaohua@everest-semi.com)
|
||||
*
|
||||
*
|
||||
* Based on es8374.c by David Yang(yangxiaohua@everest-semi.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
#include "es8311.h"
|
||||
|
||||
/* codec private data */
|
||||
struct es8311_priv {
|
||||
struct snd_soc_component *component;
|
||||
struct clk *mclk_in;
|
||||
struct gpio_desc *spk_ctl_gpio;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(vdac_tlv,
|
||||
-9550, 50, true);
|
||||
static const DECLARE_TLV_DB_SCALE(vadc_tlv,
|
||||
-9550, 50, true);
|
||||
static const DECLARE_TLV_DB_SCALE(mic_pga_tlv,
|
||||
0, 300, true);
|
||||
static const DECLARE_TLV_DB_SCALE(adc_scale_tlv,
|
||||
0, 600, false);
|
||||
static const DECLARE_TLV_DB_SCALE(alc_winsize_tlv,
|
||||
0, 25, false);
|
||||
static const DECLARE_TLV_DB_SCALE(alc_maxlevel_tlv,
|
||||
-3600, 200, false);
|
||||
static const DECLARE_TLV_DB_SCALE(alc_minlevel_tlv,
|
||||
-3600, 200, false);
|
||||
static const DECLARE_TLV_DB_SCALE(alc_noisegate_tlv,
|
||||
-9600, 600, false);
|
||||
static const DECLARE_TLV_DB_SCALE(alc_noisegate_winsize_tlv,
|
||||
4200, 4200, false);
|
||||
static const DECLARE_TLV_DB_SCALE(alc_automute_gain_tlv,
|
||||
4200, 4200, false);
|
||||
static const DECLARE_TLV_DB_SCALE(adc_ramprate_tlv,
|
||||
0, 25, false);
|
||||
|
||||
static const char *const dmic_type_txt[] = {
|
||||
"dmic at high level",
|
||||
"dmic at low level"
|
||||
};
|
||||
static const struct soc_enum dmic_type =
|
||||
SOC_ENUM_SINGLE(ES8311_ADC_REG15, 0, 1, dmic_type_txt);
|
||||
|
||||
static const char *const automute_type_txt[] = {
|
||||
"automute disabled",
|
||||
"automute enable"
|
||||
};
|
||||
static const struct soc_enum alc_automute_type =
|
||||
SOC_ENUM_SINGLE(ES8311_ADC_REG18, 6, 1, automute_type_txt);
|
||||
|
||||
static const char *const dacdsm_mute_type_txt[] = {
|
||||
"mute to 8",
|
||||
"mute to 7/9"
|
||||
};
|
||||
static const struct soc_enum dacdsm_mute_type =
|
||||
SOC_ENUM_SINGLE(ES8311_DAC_REG31, 7, 1, dacdsm_mute_type_txt);
|
||||
|
||||
static const char *const aec_type_txt[] = {
|
||||
"adc left, adc right",
|
||||
"adc left, null right",
|
||||
"null left, adc right",
|
||||
"null left, null right",
|
||||
"dac left, adc right",
|
||||
"adc left, dac right",
|
||||
"dac left, dac right",
|
||||
"N/A"
|
||||
};
|
||||
static const struct soc_enum aec_type =
|
||||
SOC_ENUM_SINGLE(ES8311_GPIO_REG44, 4, 7, aec_type_txt);
|
||||
|
||||
static const char *const adc2dac_sel_txt[] = {
|
||||
"disable",
|
||||
"adc data to dac",
|
||||
};
|
||||
static const struct soc_enum adc2dac_sel =
|
||||
SOC_ENUM_SINGLE(ES8311_GPIO_REG44, 7, 1, adc2dac_sel_txt);
|
||||
|
||||
static const char *const mclk_sel_txt[] = {
|
||||
"from mclk pin",
|
||||
"from bclk",
|
||||
};
|
||||
static const struct soc_enum mclk_src =
|
||||
SOC_ENUM_SINGLE(ES8311_CLK_MANAGER_REG01, 7, 1, mclk_sel_txt);
|
||||
|
||||
/*
|
||||
* es8311 Controls
|
||||
*/
|
||||
static const struct snd_kcontrol_new es8311_snd_controls[] = {
|
||||
SOC_SINGLE_TLV("MIC PGA GAIN", ES8311_SYSTEM_REG14,
|
||||
0, 10, 0, mic_pga_tlv),
|
||||
SOC_SINGLE_TLV("ADC SCALE", ES8311_ADC_REG16,
|
||||
0, 7, 0, adc_scale_tlv),
|
||||
SOC_ENUM("DMIC TYPE", dmic_type),
|
||||
SOC_SINGLE_TLV("ADC RAMP RATE", ES8311_ADC_REG15,
|
||||
4, 15, 0, adc_ramprate_tlv),
|
||||
SOC_SINGLE("ADC SDP MUTE", ES8311_SDPOUT_REG0A, 6, 1, 0),
|
||||
SOC_SINGLE("ADC INVERTED", ES8311_ADC_REG16, 4, 1, 0),
|
||||
SOC_SINGLE("ADC SYNC", ES8311_ADC_REG16, 5, 1, 1),
|
||||
SOC_SINGLE("ADC RAM CLR", ES8311_ADC_REG16, 3, 1, 0),
|
||||
SOC_SINGLE_TLV("ADC VOLUME", ES8311_ADC_REG17,
|
||||
0, 255, 0, vadc_tlv),
|
||||
SOC_SINGLE("ALC ENABLE", ES8311_ADC_REG18, 7, 1, 0),
|
||||
SOC_ENUM("ALC AUTOMUTE TYPE", alc_automute_type),
|
||||
SOC_SINGLE_TLV("ALC WIN SIZE", ES8311_ADC_REG18,
|
||||
0, 15, 0, alc_winsize_tlv),
|
||||
SOC_SINGLE_TLV("ALC MAX LEVEL", ES8311_ADC_REG19,
|
||||
4, 15, 0, alc_maxlevel_tlv),
|
||||
SOC_SINGLE_TLV("ALC MIN LEVEL", ES8311_ADC_REG19,
|
||||
0, 15, 0, alc_minlevel_tlv),
|
||||
SOC_SINGLE_TLV("ALC AUTOMUTE WINSIZE", ES8311_ADC_REG1A,
|
||||
4, 15, 0, alc_noisegate_winsize_tlv),
|
||||
SOC_SINGLE_TLV("ALC AUTOMUTE GATE THRESHOLD", ES8311_ADC_REG1A,
|
||||
0, 15, 0, alc_noisegate_tlv),
|
||||
SOC_SINGLE_TLV("ALC AUTOMUTE VOLUME", ES8311_ADC_REG1B,
|
||||
5, 7, 0, alc_automute_gain_tlv),
|
||||
SOC_SINGLE("ADC FS MODE", ES8311_CLK_MANAGER_REG03, 6, 1, 0),
|
||||
SOC_SINGLE("ADC OSR", ES8311_CLK_MANAGER_REG03, 0, 63, 0),
|
||||
SOC_SINGLE("DAC SDP MUTE", ES8311_SDPIN_REG09, 6, 1, 0),
|
||||
SOC_SINGLE("DAC DEM MUTE", ES8311_DAC_REG31, 5, 1, 0),
|
||||
SOC_SINGLE("DAC INVERT", ES8311_DAC_REG31, 4, 1, 0),
|
||||
SOC_SINGLE("DAC RAM CLR", ES8311_DAC_REG31, 3, 1, 0),
|
||||
SOC_ENUM("DAC DSM MUTE", dacdsm_mute_type),
|
||||
SOC_SINGLE("DAC OFFSET", ES8311_DAC_REG33, 0, 255, 0),
|
||||
SOC_SINGLE_TLV("DAC VOLUME", ES8311_DAC_REG32,
|
||||
0, 255, 0, vdac_tlv),
|
||||
SOC_SINGLE("DRC ENABLE", ES8311_DAC_REG34, 7, 1, 0),
|
||||
SOC_SINGLE_TLV("DRC WIN SIZE", ES8311_DAC_REG34,
|
||||
0, 15, 0, alc_winsize_tlv),
|
||||
SOC_SINGLE_TLV("DRC MAX LEVEL", ES8311_DAC_REG35,
|
||||
4, 15, 0, alc_maxlevel_tlv),
|
||||
SOC_SINGLE_TLV("DRC MIN LEVEL", ES8311_DAC_REG35,
|
||||
0, 15, 0, alc_minlevel_tlv),
|
||||
SOC_SINGLE_TLV("DAC RAMP RATE", ES8311_DAC_REG37,
|
||||
4, 15, 0, adc_ramprate_tlv),
|
||||
SOC_SINGLE("DAC OSR", ES8311_CLK_MANAGER_REG04, 0, 127, 0),
|
||||
SOC_ENUM("AEC MODE", aec_type),
|
||||
SOC_ENUM("ADC DATA TO DAC TEST MODE", adc2dac_sel),
|
||||
SOC_SINGLE("MCLK INVERT", ES8311_CLK_MANAGER_REG01, 6, 1, 0),
|
||||
SOC_SINGLE("BCLK INVERT", ES8311_CLK_MANAGER_REG06, 5, 1, 0),
|
||||
SOC_ENUM("MCLK SOURCE", mclk_src),
|
||||
};
|
||||
|
||||
/*
|
||||
* DAPM Controls
|
||||
*/
|
||||
static const char *const es8311_dmic_mux_txt[] = {
|
||||
"DMIC DISABLE",
|
||||
"DMIC ENABLE",
|
||||
};
|
||||
static const unsigned int es8311_dmic_mux_values[] = {
|
||||
0, 1
|
||||
};
|
||||
static const struct soc_enum es8311_dmic_mux_enum =
|
||||
SOC_VALUE_ENUM_SINGLE(ES8311_SYSTEM_REG14, 6, 1,
|
||||
ARRAY_SIZE(es8311_dmic_mux_txt),
|
||||
es8311_dmic_mux_txt,
|
||||
es8311_dmic_mux_values);
|
||||
static const struct snd_kcontrol_new es8311_dmic_mux_controls =
|
||||
SOC_DAPM_ENUM("DMIC ROUTE", es8311_dmic_mux_enum);
|
||||
|
||||
static const char *const es8311_adc_sdp_mux_txt[] = {
|
||||
"FROM ADC",
|
||||
"FROM EQUALIZER",
|
||||
};
|
||||
static const unsigned int es8311_adc_sdp_mux_values[] = {
|
||||
0, 1
|
||||
};
|
||||
static const struct soc_enum es8311_adc_sdp_mux_enum =
|
||||
SOC_VALUE_ENUM_SINGLE(ES8311_ADC_REG1C, 6, 1,
|
||||
ARRAY_SIZE(es8311_adc_sdp_mux_txt),
|
||||
es8311_adc_sdp_mux_txt,
|
||||
es8311_adc_sdp_mux_values);
|
||||
static const struct snd_kcontrol_new es8311_adc_sdp_mux_controls =
|
||||
SOC_DAPM_ENUM("ADC SDP ROUTE", es8311_adc_sdp_mux_enum);
|
||||
|
||||
/*
|
||||
* DAC data source
|
||||
*/
|
||||
static const char *const es8311_dac_data_mux_txt[] = {
|
||||
"SELECT SDP LEFT DATA",
|
||||
"SELECT SDP RIGHT DATA",
|
||||
};
|
||||
static const unsigned int es8311_dac_data_mux_values[] = {
|
||||
0, 1
|
||||
};
|
||||
static const struct soc_enum es8311_dac_data_mux_enum =
|
||||
SOC_VALUE_ENUM_SINGLE(ES8311_SDPIN_REG09, 7, 1,
|
||||
ARRAY_SIZE(es8311_dac_data_mux_txt),
|
||||
es8311_dac_data_mux_txt,
|
||||
es8311_dac_data_mux_values);
|
||||
static const struct snd_kcontrol_new es8311_dac_data_mux_controls =
|
||||
SOC_DAPM_ENUM("DAC SDP ROUTE", es8311_dac_data_mux_enum);
|
||||
|
||||
static const struct snd_soc_dapm_widget es8311_dapm_widgets[] = {
|
||||
/* Input*/
|
||||
SND_SOC_DAPM_INPUT("DMIC"),
|
||||
SND_SOC_DAPM_INPUT("AMIC"),
|
||||
|
||||
SND_SOC_DAPM_PGA("INPUT PGA", ES8311_SYSTEM_REG0E,
|
||||
6, 1, NULL, 0),
|
||||
/* ADCs */
|
||||
SND_SOC_DAPM_ADC("MONO ADC", NULL, ES8311_SYSTEM_REG0E, 5, 1),
|
||||
/* Dmic MUX */
|
||||
SND_SOC_DAPM_MUX("DMIC MUX", SND_SOC_NOPM, 0, 0,
|
||||
&es8311_dmic_mux_controls),
|
||||
/* sdp MUX */
|
||||
SND_SOC_DAPM_MUX("SDP OUT MUX", SND_SOC_NOPM, 0, 0,
|
||||
&es8311_adc_sdp_mux_controls),
|
||||
/* Digital Interface */
|
||||
SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture", 1,
|
||||
SND_SOC_NOPM, 0, 0),
|
||||
/* Render path */
|
||||
SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0,
|
||||
SND_SOC_NOPM, 0, 0),
|
||||
/*DACs SDP DATA SRC MUX */
|
||||
SND_SOC_DAPM_MUX("DAC SDP SRC MUX", SND_SOC_NOPM, 0, 0,
|
||||
&es8311_dac_data_mux_controls),
|
||||
SND_SOC_DAPM_DAC("MONO DAC", NULL, SND_SOC_NOPM, 0, 0),
|
||||
|
||||
/* Output Lines */
|
||||
SND_SOC_DAPM_OUTPUT("DIFFERENTIAL OUT"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route es8311_dapm_routes[] = {
|
||||
/* record route map */
|
||||
{"INPUT PGA", NULL, "AMIC"},
|
||||
{"MONO ADC", NULL, "INPUT PGA"},
|
||||
{"DMIC MUX", "DMIC DISABLE", "MONO ADC"},
|
||||
{"DMIC MUX", "DMIC ENABLE", "DMIC"},
|
||||
{"SDP OUT MUX", "FROM ADC OUT", "DMIC MUX"},
|
||||
{"SDP OUT MUX", "FROM EQUALIZER", "DMIC MUX"},
|
||||
{"I2S OUT", NULL, "SDP OUT MUX"},
|
||||
/* playback route map */
|
||||
{"DAC SDP SRC MUX", "SELECT SDP LEFT DATA", "I2S IN"},
|
||||
{"DAC SDP SRC MUX", "SELECT SDP RIGHT DATA", "I2S IN"},
|
||||
{"MONO DAC", NULL, "DAC SDP SRC MUX"},
|
||||
{"DIFFERENTIAL OUT", NULL, "MONO DAC"},
|
||||
};
|
||||
|
||||
static int es8311_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_component *component = codec_dai->component;
|
||||
u8 iface = 0;
|
||||
u8 adciface = 0;
|
||||
u8 daciface = 0;
|
||||
|
||||
iface = snd_soc_component_read32(component, ES8311_RESET_REG00);
|
||||
adciface = snd_soc_component_read32(component, ES8311_SDPOUT_REG0A);
|
||||
daciface = snd_soc_component_read32(component, ES8311_SDPIN_REG09);
|
||||
|
||||
/* set master/slave audio interface */
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM: /* MASTER MODE */
|
||||
iface |= 0x40;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFS: /* SLAVE MODE */
|
||||
iface &= 0xBF;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
snd_soc_component_write(component, ES8311_RESET_REG00, iface);
|
||||
|
||||
/* interface format */
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
adciface &= 0xFC;
|
||||
daciface &= 0xFC;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
adciface &= 0xFC;
|
||||
daciface &= 0xFC;
|
||||
adciface |= 0x01;
|
||||
daciface |= 0x01;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
adciface &= 0xDC;
|
||||
daciface &= 0xDC;
|
||||
adciface |= 0x03;
|
||||
daciface |= 0x03;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
adciface &= 0xDC;
|
||||
daciface &= 0xDC;
|
||||
adciface |= 0x23;
|
||||
daciface |= 0x23;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
iface = snd_soc_component_read32(component, ES8311_CLK_MANAGER_REG06);
|
||||
/* clock inversion */
|
||||
if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) ||
|
||||
((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_LEFT_J)) {
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
iface &= 0xDF;
|
||||
adciface &= 0xDF;
|
||||
daciface &= 0xDF;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
iface |= 0x20;
|
||||
adciface |= 0x20;
|
||||
daciface |= 0x20;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
iface |= 0x20;
|
||||
adciface &= 0xDF;
|
||||
daciface &= 0xDF;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
iface &= 0xDF;
|
||||
adciface |= 0x20;
|
||||
daciface |= 0x20;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
snd_soc_component_write(component, ES8311_CLK_MANAGER_REG06, iface);
|
||||
snd_soc_component_write(component, ES8311_SDPOUT_REG0A, adciface);
|
||||
snd_soc_component_write(component, ES8311_SDPIN_REG09, daciface);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8311_pcm_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);
|
||||
|
||||
clk_prepare_enable(es8311->mclk_in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void es8311_pcm_shutdown(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);
|
||||
|
||||
clk_disable_unprepare(es8311->mclk_in);
|
||||
}
|
||||
|
||||
static int es8311_pcm_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;
|
||||
u16 iface;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
iface = snd_soc_component_read32(component, ES8311_SDPIN_REG09) & 0xE3;
|
||||
/* bit size */
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
iface |= 0x0c;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
iface |= 0x04;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
iface |= 0x10;
|
||||
break;
|
||||
}
|
||||
/* set iface */
|
||||
snd_soc_component_write(component, ES8311_SDPIN_REG09, iface);
|
||||
} else {
|
||||
iface = snd_soc_component_read32(component, ES8311_SDPOUT_REG0A) & 0xE3;
|
||||
/* bit size */
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
iface |= 0x0c;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
iface |= 0x04;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
iface |= 0x10;
|
||||
break;
|
||||
}
|
||||
/* set iface */
|
||||
snd_soc_component_write(component, ES8311_SDPOUT_REG0A, iface);
|
||||
}
|
||||
|
||||
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);
|
||||
int ret;
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
break;
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
if (IS_ERR(es8311->mclk_in))
|
||||
break;
|
||||
|
||||
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) {
|
||||
clk_disable_unprepare(es8311->mclk_in);
|
||||
} else {
|
||||
ret = clk_prepare_enable(es8311->mclk_in);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_OFF:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8311_set_tristate(struct snd_soc_dai *dai, int tristate)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
|
||||
if (tristate)
|
||||
snd_soc_component_update_bits(component, ES8311_CLK_MANAGER_REG07,
|
||||
0x30, 0x30);
|
||||
else
|
||||
snd_soc_component_update_bits(component, ES8311_CLK_MANAGER_REG07,
|
||||
0x30, 0x00);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8311_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (mute) {
|
||||
snd_soc_component_write(component, ES8311_SYSTEM_REG12, 0x02);
|
||||
snd_soc_component_update_bits(component, ES8311_DAC_REG31, 0x60, 0x60);
|
||||
if (es8311->spk_ctl_gpio)
|
||||
gpiod_direction_output(es8311->spk_ctl_gpio, 0);
|
||||
} else {
|
||||
snd_soc_component_update_bits(component, ES8311_DAC_REG31, 0x60, 0x00);
|
||||
snd_soc_component_write(component, ES8311_SYSTEM_REG12, 0x00);
|
||||
if (es8311->spk_ctl_gpio)
|
||||
gpiod_direction_output(es8311->spk_ctl_gpio, 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ES8311_RATES SNDRV_PCM_RATE_8000_96000
|
||||
#define ES8311_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static struct snd_soc_dai_ops es8311_ops = {
|
||||
.startup = es8311_pcm_startup,
|
||||
.shutdown = es8311_pcm_shutdown,
|
||||
.hw_params = es8311_pcm_hw_params,
|
||||
.set_fmt = es8311_set_dai_fmt,
|
||||
.digital_mute = es8311_mute,
|
||||
.set_tristate = es8311_set_tristate,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver es8311_dai = {
|
||||
.name = "ES8311 HiFi",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = ES8311_RATES,
|
||||
.formats = ES8311_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = ES8311_RATES,
|
||||
.formats = ES8311_FORMATS,
|
||||
},
|
||||
.ops = &es8311_ops,
|
||||
.symmetric_rates = 1,
|
||||
};
|
||||
|
||||
static int es8311_regs_init(struct snd_soc_component *component)
|
||||
{
|
||||
/* reset codec */
|
||||
snd_soc_component_write(component, ES8311_RESET_REG00, 0x1F);
|
||||
snd_soc_component_write(component, ES8311_GP_REG45, 0x00);
|
||||
/* set ADC/DAC CLK */
|
||||
snd_soc_component_write(component, ES8311_CLK_MANAGER_REG01, 0x30);
|
||||
snd_soc_component_write(component, ES8311_CLK_MANAGER_REG02, 0x00);
|
||||
snd_soc_component_write(component, ES8311_CLK_MANAGER_REG03, 0x10);
|
||||
snd_soc_component_write(component, ES8311_CLK_MANAGER_REG04, 0x10);
|
||||
snd_soc_component_write(component, ES8311_CLK_MANAGER_REG05, 0x00);
|
||||
/* set system power up */
|
||||
snd_soc_component_write(component, ES8311_SYSTEM_REG0B, 0x00);
|
||||
snd_soc_component_write(component, ES8311_SYSTEM_REG0C, 0x00);
|
||||
snd_soc_component_write(component, ES8311_SYSTEM_REG10, 0x1F);
|
||||
snd_soc_component_write(component, ES8311_SYSTEM_REG11, 0x7F);
|
||||
/* chip powerup. slave mode */
|
||||
snd_soc_component_write(component, ES8311_RESET_REG00, 0x80);
|
||||
usleep_range(5000, 5500);
|
||||
|
||||
/* power up analog */
|
||||
snd_soc_component_write(component, ES8311_SYSTEM_REG0D, 0x01);
|
||||
/* power up digital */
|
||||
snd_soc_component_write(component, ES8311_CLK_MANAGER_REG01, 0x3F);
|
||||
/* set adc hpf, ADC_EQ bypass */
|
||||
snd_soc_component_write(component, ES8311_ADC_REG1C, 0x6A);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8311_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
es8311->component = component;
|
||||
es8311_regs_init(component);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver soc_component_dev_es8311 = {
|
||||
.probe = es8311_probe,
|
||||
.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,
|
||||
.non_legacy_dai_naming = 1,
|
||||
};
|
||||
|
||||
static struct regmap_config es8311_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = ES8311_MAX_REGISTER,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static int es8311_i2c_probe(struct i2c_client *i2c_client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct es8311_priv *es8311;
|
||||
struct regmap *regmap;
|
||||
int ret = 0;
|
||||
|
||||
es8311 = devm_kzalloc(&i2c_client->dev,
|
||||
sizeof(*es8311), GFP_KERNEL);
|
||||
if (es8311 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c_client, es8311);
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c_client, &es8311_regmap);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
es8311->mclk_in = devm_clk_get(&i2c_client->dev, "mclk");
|
||||
if (IS_ERR(es8311->mclk_in))
|
||||
return PTR_ERR(es8311->mclk_in);
|
||||
|
||||
es8311->spk_ctl_gpio = devm_gpiod_get_optional(&i2c_client->dev, "spk-ctl",
|
||||
GPIOD_OUT_LOW);
|
||||
if (!es8311->spk_ctl_gpio) {
|
||||
dev_info(&i2c_client->dev, "Don't need spk-ctl gpio\n");
|
||||
} else if (IS_ERR(es8311->spk_ctl_gpio)) {
|
||||
ret = PTR_ERR(es8311->spk_ctl_gpio);
|
||||
dev_err(&i2c_client->dev, "Unable to claim gpio spk-ctl\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_snd_soc_register_component(&i2c_client->dev,
|
||||
&soc_component_dev_es8311,
|
||||
&es8311_dai, 1);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id es8311_i2c_id[] = {
|
||||
{"es8311", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, es8311_i2c_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 = of_match_ptr(es8311_of_match),
|
||||
},
|
||||
.probe = es8311_i2c_probe,
|
||||
.id_table = es8311_i2c_id,
|
||||
};
|
||||
module_i2c_driver(es8311_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Everest Semi ES8311 ALSA SoC Codec Driver");
|
||||
MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
68
sound/soc/codecs/es8311.h
Normal file
68
sound/soc/codecs/es8311.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* ES8311.h -- ES8311 ALSA SoC Audio Codec
|
||||
*
|
||||
* Authors:
|
||||
*
|
||||
* Based on ES8374.h by David Yang
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _ES8311_H
|
||||
#define _ES8311_H
|
||||
|
||||
/*
|
||||
* ES8311_REGISTER NAME_REG_REGISTER ADDRESS
|
||||
*/
|
||||
#define ES8311_RESET_REG00 0x00 /*reset digital,csm,clock manager etc.*/
|
||||
|
||||
/*
|
||||
* Clock Scheme Register definition
|
||||
*/
|
||||
#define ES8311_CLK_MANAGER_REG01 0x01 /* select clk src for mclk, enable clock for codec */
|
||||
#define ES8311_CLK_MANAGER_REG02 0x02 /* clk divider and clk multiplier */
|
||||
#define ES8311_CLK_MANAGER_REG03 0x03 /* adc fsmode and osr */
|
||||
#define ES8311_CLK_MANAGER_REG04 0x04 /* dac osr */
|
||||
#define ES8311_CLK_MANAGER_REG05 0x05 /* clk divier for adc and dac */
|
||||
#define ES8311_CLK_MANAGER_REG06 0x06 /* bclk inverter and divider */
|
||||
#define ES8311_CLK_MANAGER_REG07 0x07 /* tri-state, lrck divider */
|
||||
#define ES8311_CLK_MANAGER_REG08 0x08 /* lrck divider */
|
||||
#define ES8311_SDPIN_REG09 0x09 /* dac serial digital port */
|
||||
#define ES8311_SDPOUT_REG0A 0x0A /* adc serial digital port */
|
||||
#define ES8311_SYSTEM_REG0B 0x0B /* system */
|
||||
#define ES8311_SYSTEM_REG0C 0x0C /* system */
|
||||
#define ES8311_SYSTEM_REG0D 0x0D /* system, power up/down */
|
||||
#define ES8311_SYSTEM_REG0E 0x0E /* system, power up/down */
|
||||
#define ES8311_SYSTEM_REG0F 0x0F /* system, low power */
|
||||
#define ES8311_SYSTEM_REG10 0x10 /* system */
|
||||
#define ES8311_SYSTEM_REG11 0x11 /* system */
|
||||
#define ES8311_SYSTEM_REG12 0x12 /* system, Enable DAC */
|
||||
#define ES8311_SYSTEM_REG13 0x13 /* system */
|
||||
#define ES8311_SYSTEM_REG14 0x14 /* system, select DMIC, select analog pga gain */
|
||||
#define ES8311_ADC_REG15 0x15 /* ADC, adc ramp rate, dmic sense */
|
||||
#define ES8311_ADC_REG16 0x16 /* ADC */
|
||||
#define ES8311_ADC_REG17 0x17 /* ADC, volume */
|
||||
#define ES8311_ADC_REG18 0x18 /* ADC, alc enable and winsize */
|
||||
#define ES8311_ADC_REG19 0x19 /* ADC, alc maxlevel */
|
||||
#define ES8311_ADC_REG1A 0x1A /* ADC, alc automute */
|
||||
#define ES8311_ADC_REG1B 0x1B /* ADC, alc automute, adc hpf s1 */
|
||||
#define ES8311_ADC_REG1C 0x1C /* ADC, equalizer, hpf s2 */
|
||||
#define ES8311_DAC_REG31 0x31 /* DAC, mute */
|
||||
#define ES8311_DAC_REG32 0x32 /* DAC, volume */
|
||||
#define ES8311_DAC_REG33 0x33 /* DAC, offset */
|
||||
#define ES8311_DAC_REG34 0x34 /* DAC, drc enable, drc winsize */
|
||||
#define ES8311_DAC_REG35 0x35 /* DAC, drc maxlevel, minilevel */
|
||||
#define ES8311_DAC_REG37 0x37 /* DAC, ramprate */
|
||||
#define ES8311_GPIO_REG44 0x44 /* GPIO, dac2adc for test */
|
||||
#define ES8311_GP_REG45 0x45 /* GP CONTROL */
|
||||
#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */
|
||||
#define ES8311_CHD2_REGFE 0xFE /* CHIP ID2 */
|
||||
#define ES8311_CHVER_REGFF 0xFF /* VERSION */
|
||||
#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */
|
||||
|
||||
#define ES8311_MAX_REGISTER 0xFF
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user