All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 1/2] ASoC: add es8316 codec driver
@ 2017-06-05 20:16 Daniel Drake
  2017-06-05 20:16 ` [PATCH v3 2/2] ASoC: Intel: add machine driver for BYT/CHT + ES8316 Daniel Drake
  2017-06-06  0:30 ` [PATCH v3 1/2] ASoC: add es8316 codec driver Takashi Sakamoto
  0 siblings, 2 replies; 6+ messages in thread
From: Daniel Drake @ 2017-06-05 20:16 UTC (permalink / raw)
  To: lgirdwood, broonie; +Cc: alsa-devel, linux, pierre-louis.bossart, yangxiaohua

Add a codec driver for the Everest ES8316, based on code provided by
David Yang from Everest Semi.

I limited the functionality to items where the vendor code was clear,
and things that can be tested on the Weibu F3C (Intel Cherry Trail).
As a result the initial implementation only supports running in slave
mode at single speed (up to 48kHz sample rate) using I2S. HPD is not
supported.

Signed-off-by: David Yang <yangxiaohua@everest-semi.com>
[drake@endlessm.com: significant cleanups and simplifications,
                     remove dead/unclear code]
Signed-off-by: Daniel Drake <drake@endlessm.com>
---

Notes:
    v3:
     - move to boolean controls where appropriate
     - name controls with Volume suffix where appropriate
     - clean up probe routine
     - control more registers with DAPM, replacing set_bias_level
     - improve DAPM widget naming consistency
     - fix i2c device id
    
    v2:
     - Add sign-off from David Yang
     - Only support whole number rates calculated with mclk ratios

 sound/soc/codecs/Kconfig  |   4 +
 sound/soc/codecs/Makefile |   2 +
 sound/soc/codecs/es8316.c | 629 ++++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/es8316.h | 129 ++++++++++
 4 files changed, 764 insertions(+)
 create mode 100644 sound/soc/codecs/es8316.c
 create mode 100644 sound/soc/codecs/es8316.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 883ed4c8a551..c6286e5ba511 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -72,6 +72,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_DA9055 if I2C
 	select SND_SOC_DIO2125
 	select SND_SOC_DMIC
+	select SND_SOC_ES8316 if I2C
 	select SND_SOC_ES8328_SPI if SPI_MASTER
 	select SND_SOC_ES8328_I2C if I2C
 	select SND_SOC_ES7134
@@ -543,6 +544,9 @@ config SND_SOC_HDMI_CODEC
 config SND_SOC_ES7134
        tristate "Everest Semi ES7134 CODEC"
 
+config SND_SOC_ES8316
+	tristate "Everest Semi ES8316 CODEC"
+
 config SND_SOC_ES8328
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 28a63fdaf982..e878306ce46e 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -65,6 +65,7 @@ snd-soc-da732x-objs := da732x.o
 snd-soc-da9055-objs := da9055.o
 snd-soc-dmic-objs := dmic.o
 snd-soc-es7134-objs := es7134.o
+snd-soc-es8316-objs := es8316.o
 snd-soc-es8328-objs := es8328.o
 snd-soc-es8328-i2c-objs := es8328-i2c.o
 snd-soc-es8328-spi-objs := es8328-spi.o
@@ -300,6 +301,7 @@ obj-$(CONFIG_SND_SOC_DA732X)	+= snd-soc-da732x.o
 obj-$(CONFIG_SND_SOC_DA9055)	+= snd-soc-da9055.o
 obj-$(CONFIG_SND_SOC_DMIC)	+= snd-soc-dmic.o
 obj-$(CONFIG_SND_SOC_ES7134)	+= snd-soc-es7134.o
+obj-$(CONFIG_SND_SOC_ES8316)    += snd-soc-es8316.o
 obj-$(CONFIG_SND_SOC_ES8328)	+= snd-soc-es8328.o
 obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
 obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
new file mode 100644
index 000000000000..ced60a56b4cb
--- /dev/null
+++ b/sound/soc/codecs/es8316.c
@@ -0,0 +1,629 @@
+/*
+ * es8316.c -- es8316 ALSA SoC audio driver
+ * Copyright Everest Semiconductor Co.,Ltd
+ *
+ * Authors: David Yang <yangxiaohua@everest-semi.com>,
+ *          Daniel Drake <drake@endlessm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include "es8316.h"
+
+/* In slave mode at single speed, the codec is documented as accepting 5
+ * MCLK/LRCK ratios, but we also add ratio 400, which is commonly used on
+ * Intel Cherry Trail platforms (19.2MHz MCLK, 48kHz LRCK).
+ */
+#define NR_SUPPORTED_MCLK_LRCK_RATIOS 6
+static const unsigned int supported_mclk_lrck_ratios[] = {
+	256, 384, 400, 512, 768, 1024
+};
+
+struct es8316_priv {
+	unsigned int sysclk;
+	unsigned int allowed_rates[NR_SUPPORTED_MCLK_LRCK_RATIOS];
+	struct snd_pcm_hw_constraint_list sysclk_constraints;
+};
+
+/*
+ * ES8316 controls
+ */
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9600, 50, 1);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -9600, 50, 1);
+static const DECLARE_TLV_DB_SCALE(alc_max_gain_tlv, -650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(alc_min_gain_tlv, -1200, 150, 0);
+static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -1650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(hpmixer_gain_tlv, -1200, 150, 0);
+
+static unsigned int adc_pga_gain_tlv[] = {
+	TLV_DB_RANGE_HEAD(12),
+	0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+	1, 1, TLV_DB_SCALE_ITEM(300, 0, 0),
+	2, 2, TLV_DB_SCALE_ITEM(600, 0, 0),
+	3, 3, TLV_DB_SCALE_ITEM(900, 0, 0),
+	4, 4, TLV_DB_SCALE_ITEM(1200, 0, 0),
+	5, 5, TLV_DB_SCALE_ITEM(1500, 0, 0),
+	6, 6, TLV_DB_SCALE_ITEM(1800, 0, 0),
+	7, 7, TLV_DB_SCALE_ITEM(2100, 0, 0),
+	8, 8, TLV_DB_SCALE_ITEM(2400, 0, 0),
+};
+static unsigned int hpout_vol_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 3, TLV_DB_SCALE_ITEM(-4800, 1200, 0),
+};
+
+static const char * const ng_type_txt[] =
+	{ "Constant PGA Gain", "Mute ADC Output" };
+static const struct soc_enum ng_type =
+	SOC_ENUM_SINGLE(ES8316_ADC_ALC_NG, 6, 2, ng_type_txt);
+
+static const char * const adcpol_txt[] = { "Normal", "Invert" };
+static const struct soc_enum adcpol =
+	SOC_ENUM_SINGLE(ES8316_ADC_MUTE, 1, 2, adcpol_txt);
+static const char *const dacpol_txt[] =
+	{ "Normal", "R Invert", "L Invert", "L + R Invert" };
+static const struct soc_enum dacpol =
+	SOC_ENUM_SINGLE(ES8316_DAC_SET1, 0, 4, dacpol_txt);
+
+static const struct snd_kcontrol_new es8316_snd_controls[] = {
+	SOC_DOUBLE_TLV("HP Playback Volume", ES8316_CPHP_ICAL_VOL,
+		       4, 0, 0, 1, hpout_vol_tlv),
+	SOC_DOUBLE_TLV("HP Mixer Volume", ES8316_HPMIX_VOL,
+		       0, 4, 7, 0, hpmixer_gain_tlv),
+
+	SOC_ENUM("Playback Polarity", dacpol),
+	SOC_DOUBLE_R_TLV("DAC Playback Volume", ES8316_DAC_VOLL,
+			 ES8316_DAC_VOLR, 0, 0xc0, 1, dac_vol_tlv),
+	SOC_SINGLE("DAC Soft Ramp Switch", ES8316_DAC_SET1, 4, 1, 1),
+	SOC_SINGLE("DAC Soft Ramp Rate", ES8316_DAC_SET1, 2, 4, 0),
+	SOC_SINGLE("DAC Notch Filter Switch", ES8316_DAC_SET2, 6, 1, 0),
+	SOC_SINGLE("DAC Double Fs Switch", ES8316_DAC_SET2, 7, 1, 0),
+	SOC_SINGLE("DAC Stereo Enhancement", ES8316_DAC_SET3, 0, 7, 0),
+
+	SOC_ENUM("Capture Polarity", adcpol),
+	SOC_SINGLE("Mic Boost Switch", ES8316_ADC_D2SEPGA, 0, 1, 0),
+	SOC_SINGLE_TLV("ADC Capture Volume", ES8316_ADC_VOLUME,
+		       0, 0xc0, 1, adc_vol_tlv),
+	SOC_SINGLE_TLV("ADC PGA Gain Volume", ES8316_ADC_PGAGAIN,
+		       4, 8, 0, adc_pga_gain_tlv),
+	SOC_SINGLE("ADC Soft Ramp Switch", ES8316_ADC_MUTE, 4, 1, 0),
+	SOC_SINGLE("ADC Double Fs Switch", ES8316_ADC_DMIC, 4, 1, 0),
+
+	SOC_SINGLE("ALC Capture Switch", ES8316_ADC_ALC1, 6, 1, 0),
+	SOC_SINGLE_TLV("ALC Capture Max Volume", ES8316_ADC_ALC1, 0, 28, 0,
+		       alc_max_gain_tlv),
+	SOC_SINGLE_TLV("ALC Capture Min Volume", ES8316_ADC_ALC2, 0, 28, 0,
+		       alc_min_gain_tlv),
+	SOC_SINGLE_TLV("ALC Capture Target Volume", ES8316_ADC_ALC3, 4, 10, 0,
+		       alc_target_tlv),
+	SOC_SINGLE("ALC Capture Hold Time", ES8316_ADC_ALC3, 0, 10, 0),
+	SOC_SINGLE("ALC Capture Decay Time", ES8316_ADC_ALC4, 4, 10, 0),
+	SOC_SINGLE("ALC Capture Attack Time", ES8316_ADC_ALC4, 0, 10, 0),
+	SOC_SINGLE("ALC Capture Noise Gate Switch", ES8316_ADC_ALC_NG,
+		   5, 1, 0),
+	SOC_SINGLE("ALC Capture Noise Gate Threshold", ES8316_ADC_ALC_NG,
+		   0, 31, 0),
+	SOC_ENUM("ALC Capture Noise Gate Type", ng_type),
+};
+
+/* Analog Input Mux */
+static const char * const es8316_analog_in_txt[] = {
+		"lin1-rin1",
+		"lin2-rin2",
+		"lin1-rin1 with 20db Boost",
+		"lin2-rin2 with 20db Boost"
+};
+static const unsigned int es8316_analog_in_values[] = { 0, 1, 2, 3 };
+static const struct soc_enum es8316_analog_input_enum =
+	SOC_VALUE_ENUM_SINGLE(ES8316_ADC_PDN_LINSEL, 4, 3,
+			      ARRAY_SIZE(es8316_analog_in_txt),
+			      es8316_analog_in_txt,
+			      es8316_analog_in_values);
+static const struct snd_kcontrol_new es8316_analog_in_mux_controls =
+	SOC_DAPM_ENUM("Route", es8316_analog_input_enum);
+
+static const char * const es8316_dmic_txt[] = {
+		"dmic disable",
+		"dmic data at high level",
+		"dmic data at low level",
+};
+static const unsigned int es8316_dmic_values[] = { 0, 1, 2 };
+static const struct soc_enum es8316_dmic_src_enum =
+	SOC_VALUE_ENUM_SINGLE(ES8316_ADC_DMIC, 0, 3,
+			      ARRAY_SIZE(es8316_dmic_txt),
+			      es8316_dmic_txt,
+			      es8316_dmic_values);
+static const struct snd_kcontrol_new es8316_dmic_src_controls =
+	SOC_DAPM_ENUM("Route", es8316_dmic_src_enum);
+
+/* hp mixer mux */
+static const char * const es8316_hpmux_texts[] = {
+	"lin1-rin1",
+	"lin2-rin2",
+	"lin-rin with Boost",
+	"lin-rin with Boost and PGA"
+};
+
+static const unsigned int es8316_hpmux_values[] = { 0, 1, 2, 3 };
+
+static SOC_ENUM_SINGLE_DECL(es8316_left_hpmux_enum, ES8316_HPMIX_SEL,
+	4, es8316_hpmux_texts);
+
+static const struct snd_kcontrol_new es8316_left_hpmux_controls =
+	SOC_DAPM_ENUM("Route", es8316_left_hpmux_enum);
+
+static SOC_ENUM_SINGLE_DECL(es8316_right_hpmux_enum, ES8316_HPMIX_SEL,
+	0, es8316_hpmux_texts);
+
+static const struct snd_kcontrol_new es8316_right_hpmux_controls =
+	SOC_DAPM_ENUM("Route", es8316_right_hpmux_enum);
+
+/* headphone Output Mixer */
+static const struct snd_kcontrol_new es8316_out_left_mix[] = {
+	SOC_DAPM_SINGLE("LLIN Switch", ES8316_HPMIX_SWITCH, 6, 1, 0),
+	SOC_DAPM_SINGLE("Left DAC Switch", ES8316_HPMIX_SWITCH, 7, 1, 0),
+};
+static const struct snd_kcontrol_new es8316_out_right_mix[] = {
+	SOC_DAPM_SINGLE("RLIN Switch", ES8316_HPMIX_SWITCH, 2, 1, 0),
+	SOC_DAPM_SINGLE("Right DAC Switch", ES8316_HPMIX_SWITCH, 3, 1, 0),
+};
+
+/* DAC data source mux */
+static const char * const es8316_dacsrc_texts[] = {
+	"LDATA TO LDAC, RDATA TO RDAC",
+	"LDATA TO LDAC, LDATA TO RDAC",
+	"RDATA TO LDAC, RDATA TO RDAC",
+	"RDATA TO LDAC, LDATA TO RDAC",
+};
+
+static const unsigned int es8316_dacsrc_values[] = { 0, 1, 2, 3 };
+
+static SOC_ENUM_SINGLE_DECL(es8316_dacsrc_mux_enum, ES8316_DAC_SET1,
+	6, es8316_dacsrc_texts);
+
+static const struct snd_kcontrol_new es8316_dacsrc_mux_controls =
+	SOC_DAPM_ENUM("Route", es8316_dacsrc_mux_enum);
+
+static const struct snd_soc_dapm_widget es8316_dapm_widgets[] = {
+	SND_SOC_DAPM_SUPPLY("Bias", ES8316_SYS_PDN, 3, 1, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Analog power", ES8316_SYS_PDN, 4, 1, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Mic Bias", ES8316_SYS_PDN, 5, 1, NULL, 0),
+
+	SND_SOC_DAPM_INPUT("DMIC"),
+	SND_SOC_DAPM_INPUT("MIC1"),
+	SND_SOC_DAPM_INPUT("MIC2"),
+
+	/* Input Mux */
+	SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+			 &es8316_analog_in_mux_controls),
+
+	SND_SOC_DAPM_SUPPLY("ADC Vref", ES8316_SYS_PDN, 1, 1, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("ADC bias", ES8316_SYS_PDN, 2, 1, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("ADC Clock", ES8316_CLKMGR_CLKSW, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Line input PGA", ES8316_ADC_PDN_LINSEL,
+			 7, 1, NULL, 0),
+	SND_SOC_DAPM_ADC("Mono ADC", NULL, ES8316_ADC_PDN_LINSEL, 6, 1),
+	SND_SOC_DAPM_MUX("Digital Mic Mux", SND_SOC_NOPM, 0, 0,
+			 &es8316_dmic_src_controls),
+
+	/* Digital Interface */
+	SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture",  1,
+			     ES8316_SERDATA_ADC, 6, 1),
+	SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0,
+			    SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_MUX("DAC Source Mux", SND_SOC_NOPM, 0, 0,
+			 &es8316_dacsrc_mux_controls),
+
+	SND_SOC_DAPM_SUPPLY("DAC Vref", ES8316_SYS_PDN, 0, 1, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("DAC Clock", ES8316_CLKMGR_CLKSW, 2, 0, NULL, 0),
+	SND_SOC_DAPM_DAC("Right DAC", NULL, ES8316_DAC_PDN, 0, 1),
+	SND_SOC_DAPM_DAC("Left DAC", NULL, ES8316_DAC_PDN, 4, 1),
+
+	/* Headphone Output Side */
+	SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0,
+			 &es8316_left_hpmux_controls),
+	SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0,
+			 &es8316_right_hpmux_controls),
+	SND_SOC_DAPM_MIXER("Left Headphone Mixer", ES8316_HPMIX_PDN,
+			   5, 1, &es8316_out_left_mix[0],
+			   ARRAY_SIZE(es8316_out_left_mix)),
+	SND_SOC_DAPM_MIXER("Right Headphone Mixer", ES8316_HPMIX_PDN,
+			   1, 1, &es8316_out_right_mix[0],
+			   ARRAY_SIZE(es8316_out_right_mix)),
+	SND_SOC_DAPM_PGA("Left Headphone Mixer Out", ES8316_HPMIX_PDN,
+			 4, 1, NULL, 0),
+	SND_SOC_DAPM_PGA("Right Headphone Mixer Out", ES8316_HPMIX_PDN,
+			 0, 1, NULL, 0),
+
+	SND_SOC_DAPM_OUT_DRV("Left Headphone Charge Pump", ES8316_CPHP_OUTEN,
+			     6, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("Right Headphone Charge Pump", ES8316_CPHP_OUTEN,
+			     2, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Headphone Charge Pump", ES8316_CPHP_PDN2,
+			    5, 1, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Headphone Charge Pump Clock", ES8316_CLKMGR_CLKSW,
+			    4, 0, NULL, 0),
+
+	SND_SOC_DAPM_OUT_DRV("Left Headphone Driver", ES8316_CPHP_OUTEN,
+			     5, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("Right Headphone Driver", ES8316_CPHP_OUTEN,
+			     1, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Headphone Out", ES8316_CPHP_PDN1, 2, 1, NULL, 0),
+
+	/* pdn_Lical and pdn_Rical bits are documented as Reserved, but must
+	 * be explicitly unset in order to enable HP output
+	 */
+	SND_SOC_DAPM_SUPPLY("Left Headphone ical", ES8316_CPHP_ICAL_VOL,
+			    7, 1, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Right Headphone ical", ES8316_CPHP_ICAL_VOL,
+			    3, 1, NULL, 0),
+
+	SND_SOC_DAPM_OUTPUT("HPOL"),
+	SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route es8316_dapm_routes[] = {
+	/* Recording */
+	{"MIC1", NULL, "Mic Bias"},
+	{"MIC2", NULL, "Mic Bias"},
+	{"MIC1", NULL, "Bias"},
+	{"MIC2", NULL, "Bias"},
+	{"MIC1", NULL, "Analog power"},
+	{"MIC2", NULL, "Analog power"},
+
+	{"Differential Mux", "lin1-rin1", "MIC1"},
+	{"Differential Mux", "lin2-rin2", "MIC2"},
+	{"Line input PGA", NULL, "Differential Mux"},
+
+	{"Mono ADC", NULL, "ADC Clock"},
+	{"Mono ADC", NULL, "ADC Vref"},
+	{"Mono ADC", NULL, "ADC bias"},
+	{"Mono ADC", NULL, "Line input PGA"},
+
+	/* It's not clear why, but to avoid recording only silence,
+	 * the DAC clock must be running for the ADC to work.
+	 */
+	{"Mono ADC", NULL, "DAC Clock"},
+
+	{"Digital Mic Mux", "dmic disable", "Mono ADC"},
+
+	{"I2S OUT", NULL, "Digital Mic Mux"},
+
+	/* Playback */
+	{"DAC Source Mux", "LDATA TO LDAC, RDATA TO RDAC", "I2S IN"},
+
+	{"Left DAC", NULL, "DAC Clock"},
+	{"Right DAC", NULL, "DAC Clock"},
+
+	{"Left DAC", NULL, "DAC Vref"},
+	{"Right DAC", NULL, "DAC Vref"},
+
+	{"Left DAC", NULL, "DAC Source Mux"},
+	{"Right DAC", NULL, "DAC Source Mux"},
+
+	{"Left Headphone Mux", "lin-rin with Boost and PGA", "Line input PGA"},
+	{"Right Headphone Mux", "lin-rin with Boost and PGA", "Line input PGA"},
+
+	{"Left Headphone Mixer", "LLIN Switch", "Left Headphone Mux"},
+	{"Left Headphone Mixer", "Left DAC Switch", "Left DAC"},
+
+	{"Right Headphone Mixer", "RLIN Switch", "Right Headphone Mux"},
+	{"Right Headphone Mixer", "Right DAC Switch", "Right DAC"},
+
+	{"Left Headphone Mixer Out", NULL, "Left Headphone Mixer"},
+	{"Right Headphone Mixer Out", NULL, "Right Headphone Mixer"},
+
+	{"Left Headphone Charge Pump", NULL, "Left Headphone Mixer Out"},
+	{"Right Headphone Charge Pump", NULL, "Right Headphone Mixer Out"},
+
+	{"Left Headphone Charge Pump", NULL, "Headphone Charge Pump"},
+	{"Right Headphone Charge Pump", NULL, "Headphone Charge Pump"},
+
+	{"Left Headphone Charge Pump", NULL, "Headphone Charge Pump Clock"},
+	{"Right Headphone Charge Pump", NULL, "Headphone Charge Pump Clock"},
+
+	{"Left Headphone Driver", NULL, "Left Headphone Charge Pump"},
+	{"Right Headphone Driver", NULL, "Right Headphone Charge Pump"},
+
+	{"HPOL", NULL, "Left Headphone Driver"},
+	{"HPOR", NULL, "Right Headphone Driver"},
+
+	{"HPOL", NULL, "Left Headphone ical"},
+	{"HPOR", NULL, "Right Headphone ical"},
+
+	{"Headphone Out", NULL, "Bias"},
+	{"Headphone Out", NULL, "Analog power"},
+	{"HPOL", NULL, "Headphone Out"},
+	{"HPOR", NULL, "Headphone Out"},
+};
+
+static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+				 int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct es8316_priv *es8316 = snd_soc_codec_get_drvdata(codec);
+	int i;
+	int count = 0;
+
+	es8316->sysclk = freq;
+
+	if (freq == 0)
+		return 0;
+
+	/* Limit supported sample rates to ones that can be autodetected
+	 * by the codec running in slave mode.
+	 */
+	for (i = 0; i < NR_SUPPORTED_MCLK_LRCK_RATIOS; i++) {
+		const unsigned int ratio = supported_mclk_lrck_ratios[i];
+
+		if (freq % ratio == 0)
+			es8316->allowed_rates[count++] = freq / ratio;
+	}
+
+	es8316->sysclk_constraints.list = es8316->allowed_rates;
+	es8316->sysclk_constraints.count = count;
+
+	return 0;
+}
+
+static int es8316_set_dai_fmt(struct snd_soc_dai *codec_dai,
+			      unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u8 serdata1 = 0;
+	u8 serdata2 = 0;
+	u8 clksw;
+	u8 mask;
+
+	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
+		dev_err(codec->dev, "Codec driver only supports slave mode\n");
+		return -EINVAL;
+	}
+
+	if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) {
+		dev_err(codec->dev, "Codec driver only supports I2S format\n");
+		return -EINVAL;
+	}
+
+	/* Clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		serdata1 |= ES8316_SERDATA1_BCLK_INV;
+		serdata2 |= ES8316_SERDATA2_ADCLRP;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		serdata1 |= ES8316_SERDATA1_BCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		serdata2 |= ES8316_SERDATA2_ADCLRP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	mask = ES8316_SERDATA1_MASTER | ES8316_SERDATA1_BCLK_INV;
+	snd_soc_update_bits(codec, ES8316_SERDATA1, mask, serdata1);
+
+	mask = ES8316_SERDATA2_FMT_MASK | ES8316_SERDATA2_ADCLRP;
+	snd_soc_update_bits(codec, ES8316_SERDATA_ADC, mask, serdata2);
+	snd_soc_update_bits(codec, ES8316_SERDATA_DAC, mask, serdata2);
+
+	/* Enable BCLK and MCLK inputs in slave mode */
+	clksw = ES8316_CLKMGR_CLKSW_MCLK_ON | ES8316_CLKMGR_CLKSW_BCLK_ON;
+	snd_soc_update_bits(codec, ES8316_CLKMGR_CLKSW, clksw, clksw);
+
+	return 0;
+}
+
+static int es8316_pcm_startup(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct es8316_priv *es8316 = snd_soc_codec_get_drvdata(codec);
+
+	if (es8316->sysclk == 0) {
+		dev_err(codec->dev, "No sysclk provided\n");
+		return -EINVAL;
+	}
+
+	/* The set of sample rates that can be supported depends on the
+	 * MCLK supplied to the CODEC.
+	 */
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				   SNDRV_PCM_HW_PARAM_RATE,
+				   &es8316->sysclk_constraints);
+
+	return 0;
+}
+
+static int es8316_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec *codec = rtd->codec;
+	struct es8316_priv *es8316 = snd_soc_codec_get_drvdata(codec);
+	u8 wordlen = 0;
+
+	if (!es8316->sysclk) {
+		dev_err(codec->dev, "No MCLK configured\n");
+		return -EINVAL;
+	}
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		wordlen = ES8316_SERDATA2_LEN_16;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		wordlen = ES8316_SERDATA2_LEN_20;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		wordlen = ES8316_SERDATA2_LEN_24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		wordlen = ES8316_SERDATA2_LEN_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, ES8316_SERDATA_DAC,
+			    ES8316_SERDATA2_LEN_MASK, wordlen);
+	snd_soc_update_bits(codec, ES8316_SERDATA_ADC,
+			    ES8316_SERDATA2_LEN_MASK, wordlen);
+	return 0;
+}
+
+static int es8316_mute(struct snd_soc_dai *dai, int mute)
+{
+	snd_soc_update_bits(dai->codec, ES8316_DAC_SET1, 0x20,
+			    mute ? 0x20 : 0);
+	return 0;
+}
+
+#define ES8316_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+			SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops es8316_ops = {
+	.startup = es8316_pcm_startup,
+	.hw_params = es8316_pcm_hw_params,
+	.set_fmt = es8316_set_dai_fmt,
+	.set_sysclk = es8316_set_dai_sysclk,
+	.digital_mute = es8316_mute,
+};
+
+static struct snd_soc_dai_driver es8316_dai = {
+	.name = "ES8316 HiFi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = ES8316_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = ES8316_FORMATS,
+	},
+	.ops = &es8316_ops,
+	.symmetric_rates = 1,
+};
+
+static int es8316_probe(struct snd_soc_codec *codec)
+{
+	/* Reset codec and enable current state machine */
+	snd_soc_write(codec, ES8316_RESET, 0x3f);
+	usleep_range(5000, 5500);
+	snd_soc_write(codec, ES8316_RESET, ES8316_RESET_CSM_ON);
+	msleep(30);
+
+	/*
+	 * Documentation for this register is unclear and incomplete,
+	 * but here is a vendor-provided value that improves volume
+	 * and quality for Intel CHT platforms.
+	 */
+	snd_soc_write(codec, ES8316_CLKMGR_ADCOSR, 0x32);
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_es8316 = {
+	.probe		= es8316_probe,
+	.idle_bias_off	= true,
+
+	.component_driver = {
+		.controls		= es8316_snd_controls,
+		.num_controls		= ARRAY_SIZE(es8316_snd_controls),
+		.dapm_widgets		= es8316_dapm_widgets,
+		.num_dapm_widgets	= ARRAY_SIZE(es8316_dapm_widgets),
+		.dapm_routes		= es8316_dapm_routes,
+		.num_dapm_routes	= ARRAY_SIZE(es8316_dapm_routes),
+	},
+};
+
+static const struct regmap_config es8316_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0x53,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int es8316_i2c_probe(struct i2c_client *i2c_client,
+			    const struct i2c_device_id *id)
+{
+	struct es8316_priv *es8316;
+	struct regmap *regmap;
+
+	es8316 = devm_kzalloc(&i2c_client->dev, sizeof(struct es8316_priv),
+			      GFP_KERNEL);
+	if (es8316 == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c_client, es8316);
+
+	regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	return snd_soc_register_codec(&i2c_client->dev, &soc_codec_dev_es8316,
+				      &es8316_dai, 1);
+}
+
+static int es8316_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static const struct i2c_device_id es8316_i2c_id[] = {
+	{"es8316", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, es8316_i2c_id);
+
+static const struct of_device_id es8316_of_match[] = {
+	{ .compatible = "everest,es8316", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, es8316_of_match);
+
+static const struct acpi_device_id es8316_acpi_match[] = {
+	{"ESSX8316", 0},
+	{},
+};
+MODULE_DEVICE_TABLE(acpi, es8316_acpi_match);
+
+static struct i2c_driver es8316_i2c_driver = {
+	.driver = {
+		.name			= "es8316",
+		.acpi_match_table	= ACPI_PTR(es8316_acpi_match),
+		.of_match_table		= of_match_ptr(es8316_of_match),
+	},
+	.probe		= es8316_i2c_probe,
+	.remove		= es8316_i2c_remove,
+	.id_table	= es8316_i2c_id,
+};
+module_i2c_driver(es8316_i2c_driver);
+
+MODULE_DESCRIPTION("Everest Semi ES8316 ALSA SoC Codec Driver");
+MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/es8316.h b/sound/soc/codecs/es8316.h
new file mode 100644
index 000000000000..6bcdd63ea459
--- /dev/null
+++ b/sound/soc/codecs/es8316.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright Everest Semiconductor Co.,Ltd
+ *
+ * Author: 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.
+ *
+ */
+
+#ifndef _ES8316_H
+#define _ES8316_H
+
+/*
+ * ES8316 register space
+ */
+
+/* Reset Control */
+#define ES8316_RESET		0x00
+
+/* Clock Management */
+#define ES8316_CLKMGR_CLKSW	0x01
+#define ES8316_CLKMGR_CLKSEL	0x02
+#define ES8316_CLKMGR_ADCOSR	0x03
+#define ES8316_CLKMGR_ADCDIV1	0x04
+#define ES8316_CLKMGR_ADCDIV2	0x05
+#define ES8316_CLKMGR_DACDIV1	0x06
+#define ES8316_CLKMGR_DACDIV2	0x07
+#define ES8316_CLKMGR_CPDIV	0x08
+
+/* Serial Data Port Control */
+#define ES8316_SERDATA1		0x09
+#define ES8316_SERDATA_ADC	0x0a
+#define ES8316_SERDATA_DAC	0x0b
+
+/* System Control */
+#define ES8316_SYS_VMIDSEL	0x0c
+#define ES8316_SYS_PDN		0x0d
+#define ES8316_SYS_LP1		0x0e
+#define ES8316_SYS_LP2		0x0f
+#define ES8316_SYS_VMIDLOW	0x10
+#define ES8316_SYS_VSEL		0x11
+#define ES8316_SYS_REF		0x12
+
+/* Headphone Mixer */
+#define ES8316_HPMIX_SEL	0x13
+#define ES8316_HPMIX_SWITCH	0x14
+#define ES8316_HPMIX_PDN	0x15
+#define ES8316_HPMIX_VOL	0x16
+
+/* Charge Pump Headphone driver */
+#define ES8316_CPHP_OUTEN	0x17
+#define ES8316_CPHP_ICAL_VOL	0x18
+#define ES8316_CPHP_PDN1	0x19
+#define ES8316_CPHP_PDN2	0x1a
+#define ES8316_CPHP_LDOCTL	0x1b
+
+/* Calibration */
+#define ES8316_CAL_TYPE		0x1c
+#define ES8316_CAL_SET		0x1d
+#define ES8316_CAL_HPLIV	0x1e
+#define ES8316_CAL_HPRIV	0x1f
+#define ES8316_CAL_HPLMV	0x20
+#define ES8316_CAL_HPRMV	0x21
+
+/* ADC Control */
+#define ES8316_ADC_PDN_LINSEL	0x22
+#define ES8316_ADC_PGAGAIN	0x23
+#define ES8316_ADC_D2SEPGA	0x24
+#define ES8316_ADC_DMIC		0x25
+#define ES8316_ADC_MUTE		0x26
+#define ES8316_ADC_VOLUME	0x27
+#define ES8316_ADC_ALC1		0x29
+#define ES8316_ADC_ALC2		0x2a
+#define ES8316_ADC_ALC3		0x2b
+#define ES8316_ADC_ALC4		0x2c
+#define ES8316_ADC_ALC5		0x2d
+#define ES8316_ADC_ALC_NG	0x2e
+
+/* DAC Control */
+#define ES8316_DAC_PDN		0x2f
+#define ES8316_DAC_SET1		0x30
+#define ES8316_DAC_SET2		0x31
+#define ES8316_DAC_SET3		0x32
+#define ES8316_DAC_VOLL		0x33
+#define ES8316_DAC_VOLR		0x34
+
+/* GPIO */
+#define ES8316_GPIO_SEL		0x4d
+#define ES8316_GPIO_DEBOUNCE	0x4e
+#define ES8316_GPIO_FLAG	0x4f
+
+/* Test mode */
+#define ES8316_TESTMODE		0x50
+#define ES8316_TEST1		0x51
+#define ES8316_TEST2		0x52
+#define ES8316_TEST3		0x53
+
+/*
+ * Field definitions
+ */
+
+/* ES8316_RESET */
+#define ES8316_RESET_CSM_ON		0x80
+
+/* ES8316_CLKMGR_CLKSW */
+#define ES8316_CLKMGR_CLKSW_MCLK_ON	0x40
+#define ES8316_CLKMGR_CLKSW_BCLK_ON	0x20
+
+/* ES8316_SERDATA1 */
+#define ES8316_SERDATA1_MASTER		0x80
+#define ES8316_SERDATA1_BCLK_INV	0x20
+
+/* ES8316_SERDATA_ADC and _DAC */
+#define ES8316_SERDATA2_FMT_MASK	0x3
+#define ES8316_SERDATA2_FMT_I2S		0x00
+#define ES8316_SERDATA2_FMT_LEFTJ	0x01
+#define ES8316_SERDATA2_FMT_RIGHTJ	0x02
+#define ES8316_SERDATA2_FMT_PCM		0x03
+#define ES8316_SERDATA2_ADCLRP		0x20
+#define ES8316_SERDATA2_LEN_MASK	0x1c
+#define ES8316_SERDATA2_LEN_24		0x00
+#define ES8316_SERDATA2_LEN_20		0x04
+#define ES8316_SERDATA2_LEN_18		0x08
+#define ES8316_SERDATA2_LEN_16		0x0c
+#define ES8316_SERDATA2_LEN_32		0x10
+
+#endif
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v3 2/2] ASoC: Intel: add machine driver for BYT/CHT + ES8316
  2017-06-05 20:16 [PATCH v3 1/2] ASoC: add es8316 codec driver Daniel Drake
@ 2017-06-05 20:16 ` Daniel Drake
  2017-06-13 21:08   ` Applied "ASoC: Intel: add machine driver for BYT/CHT + ES8316" to the asoc tree Mark Brown
  2017-06-06  0:30 ` [PATCH v3 1/2] ASoC: add es8316 codec driver Takashi Sakamoto
  1 sibling, 1 reply; 6+ messages in thread
From: Daniel Drake @ 2017-06-05 20:16 UTC (permalink / raw)
  To: lgirdwood, broonie; +Cc: alsa-devel, linux, pierre-louis.bossart, yangxiaohua

Add new machine driver, tested with Weibu F3C MiniPC.

Based heavily on code provided by David Yang @ Everest, and other
machine drivers in the same directory.

Signed-off-by: David Yang <yangxiaohua@everest-semi.com>
[drake@endlessm.com: cleanups and modernization]
Signed-off-by: Daniel Drake <drake@endlessm.com>
---

Notes:
    v3:
     - fix inverted logic when checking clk_prepare_enable result
     - update for recent sst_acpi.c changes
    
    v2:
     - add sign-off from David Yang
     - fix bytbyt typo
     - mention that BYT-CR/SSP0 is not supported
     - mention which mic was tested
     - setup clocks at init time, avoids need for platform clock control

 sound/soc/intel/Kconfig                |  12 ++
 sound/soc/intel/atom/sst/sst_acpi.c    |   7 +
 sound/soc/intel/boards/Makefile        |   2 +
 sound/soc/intel/boards/bytcht_es8316.c | 300 +++++++++++++++++++++++++++++++++
 4 files changed, 321 insertions(+)
 create mode 100644 sound/soc/intel/boards/bytcht_es8316.c

diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index a9c50d022e73..35a6a5c55914 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -214,6 +214,18 @@ config SND_SOC_INTEL_BYT_CHT_DA7213_MACH
 	  platforms with DA7212/7213 audio codec.
 	  If unsure select "N".
 
+config SND_SOC_INTEL_BYT_CHT_ES8316_MACH
+	tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail with ES8316 codec"
+	depends on X86_INTEL_LPSS && I2C && ACPI
+	select SND_SOC_ES8316
+	select SND_SST_ATOM_HIFI2_PLATFORM
+	select SND_SST_IPC_ACPI
+	select SND_SOC_INTEL_SST_MATCH if ACPI
+	help
+	  This adds support for ASoC machine driver for Intel(R) Baytrail &
+	  Cherrytrail platforms with ES8316 audio codec.
+	  If unsure select "N".
+
 config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH
 	tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail platform with no codec (MinnowBoard MAX, Up)"
 	depends on X86_INTEL_LPSS && I2C && ACPI
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index 592f6afaf2a5..ff96d1ad3d4e 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -625,6 +625,13 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
 		.board = "bytcht_da7213",
 		.pdata = &chv_platform_data
 	},
+	{
+		.id = "ESSX8316",
+		.drv_name = "bytcht_es8316",
+		.fw_filename = "intel/fw_sst_22a8.bin",
+		.board = "bytcht_es8316",
+		.pdata = &chv_platform_data
+	},
 	/* some CHT-T platforms rely on RT5640, use Baytrail machine driver */
 	{
 		.id = "10EC5640",
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index c92ebcac0222..c4e986f03ec9 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -11,6 +11,7 @@ snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
 snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
 snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
 snd-soc-sst-byt-cht-da7213-objs := bytcht_da7213.o
+snd-soc-sst-byt-cht-es8316-objs := bytcht_es8316.o
 snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o
 snd-soc-kbl_rt5663_max98927-objs := kbl_rt5663_max98927.o
 snd-soc-skl_rt286-objs := skl_rt286.o
@@ -30,6 +31,7 @@ obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH) += snd-soc-sst-byt-cht-da7213.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH) += snd-soc-sst-byt-cht-es8316.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) += snd-soc-sst-byt-cht-nocodec.o
 obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH) += snd-soc-kbl_rt5663_max98927.o
 obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o
diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c
new file mode 100644
index 000000000000..52635462dac6
--- /dev/null
+++ b/sound/soc/intel/boards/bytcht_es8316.c
@@ -0,0 +1,300 @@
+/*
+ *  bytcht_es8316.c - ASoc Machine driver for Intel Baytrail/Cherrytrail
+ *                    platforms with Everest ES8316 SoC
+ *
+ *  Copyright (C) 2017 Endless Mobile, Inc.
+ *  Authors: David Yang <yangxiaohua@everest-semi.com>,
+ *           Daniel Drake <drake@endlessm.com>
+ *
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <asm/platform_sst_audio.h>
+#include <linux/clk.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../atom/sst-atom-controls.h"
+#include "../common/sst-acpi.h"
+#include "../common/sst-dsp.h"
+
+struct byt_cht_es8316_private {
+	struct clk *mclk;
+};
+
+#define CODEC_DAI1	"ES8316 HiFi"
+
+static inline struct snd_soc_dai *get_codec_dai(struct snd_soc_card *card)
+{
+	struct snd_soc_pcm_runtime *rtd;
+
+	list_for_each_entry(rtd, &card->rtd_list, list) {
+		if (!strncmp(rtd->codec_dai->name, CODEC_DAI1,
+			     strlen(CODEC_DAI1)))
+			return rtd->codec_dai;
+	}
+	return NULL;
+}
+
+static const struct snd_soc_dapm_widget byt_cht_es8316_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone", NULL),
+
+	/*
+	 * The codec supports two analog microphone inputs. I have only
+	 * tested MIC1. A DMIC route could also potentially be added
+	 * if such functionality is found on another platform.
+	 */
+	SND_SOC_DAPM_MIC("Microphone 1", NULL),
+	SND_SOC_DAPM_MIC("Microphone 2", NULL),
+};
+
+static const struct snd_soc_dapm_route byt_cht_es8316_audio_map[] = {
+	{"MIC1", NULL, "Microphone 1"},
+	{"MIC2", NULL, "Microphone 2"},
+
+	{"Headphone", NULL, "HPOL"},
+	{"Headphone", NULL, "HPOR"},
+
+	{"Playback", NULL, "ssp2 Tx"},
+	{"ssp2 Tx", NULL, "codec_out0"},
+	{"ssp2 Tx", NULL, "codec_out1"},
+	{"codec_in0", NULL, "ssp2 Rx" },
+	{"codec_in1", NULL, "ssp2 Rx" },
+	{"ssp2 Rx", NULL, "Capture"},
+};
+
+static const struct snd_kcontrol_new byt_cht_es8316_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Headphone"),
+	SOC_DAPM_PIN_SWITCH("Microphone 1"),
+	SOC_DAPM_PIN_SWITCH("Microphone 2"),
+};
+
+static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
+{
+	struct snd_soc_card *card = runtime->card;
+	struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
+	int ret;
+
+	card->dapm.idle_bias_off = true;
+
+	/*
+	 * The firmware might enable the clock at boot (this information
+	 * may or may not be reflected in the enable clock register).
+	 * To change the rate we must disable the clock first to cover these
+	 * cases. Due to common clock framework restrictions that do not allow
+	 * to disable a clock that has not been enabled, we need to enable
+	 * the clock first.
+	 */
+	ret = clk_prepare_enable(priv->mclk);
+	if (!ret)
+		clk_disable_unprepare(priv->mclk);
+
+	ret = clk_set_rate(priv->mclk, 19200000);
+	if (ret)
+		dev_err(card->dev, "unable to set MCLK rate\n");
+
+	ret = clk_prepare_enable(priv->mclk);
+	if (ret)
+		dev_err(card->dev, "unable to enable MCLK\n");
+
+	ret = snd_soc_dai_set_sysclk(runtime->codec_dai, 0, 19200000,
+				     SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		dev_err(card->dev, "can't set codec clock %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_pcm_stream byt_cht_es8316_dai_params = {
+	.formats = SNDRV_PCM_FMTBIT_S24_LE,
+	.rate_min = 48000,
+	.rate_max = 48000,
+	.channels_min = 2,
+	.channels_max = 2,
+};
+
+static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+			    struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+			SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+						SNDRV_PCM_HW_PARAM_CHANNELS);
+	int ret;
+
+	/* The DSP will covert the FE rate to 48k, stereo */
+	rate->min = rate->max = 48000;
+	channels->min = channels->max = 2;
+
+	/* set SSP2 to 24-bit */
+	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+	/*
+	 * Default mode for SSP configuration is TDM 4 slot, override config
+	 * with explicit setting to I2S 2ch 24-bit. The word length is set with
+	 * dai_set_tdm_slot() since there is no other API exposed
+	 */
+	ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
+				SND_SOC_DAIFMT_I2S     |
+				SND_SOC_DAIFMT_NB_NF   |
+				SND_SOC_DAIFMT_CBS_CFS
+		);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int byt_cht_es8316_aif1_startup(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_hw_constraint_single(substream->runtime,
+			SNDRV_PCM_HW_PARAM_RATE, 48000);
+}
+
+static const struct snd_soc_ops byt_cht_es8316_aif1_ops = {
+	.startup = byt_cht_es8316_aif1_startup,
+};
+
+static struct snd_soc_dai_link byt_cht_es8316_dais[] = {
+	[MERR_DPCM_AUDIO] = {
+		.name = "Audio Port",
+		.stream_name = "Audio",
+		.cpu_dai_name = "media-cpu-dai",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.platform_name = "sst-mfld-platform",
+		.nonatomic = true,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.ops = &byt_cht_es8316_aif1_ops,
+	},
+
+	[MERR_DPCM_DEEP_BUFFER] = {
+		.name = "Deep-Buffer Audio Port",
+		.stream_name = "Deep-Buffer Audio",
+		.cpu_dai_name = "deepbuffer-cpu-dai",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.platform_name = "sst-mfld-platform",
+		.nonatomic = true,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.ops = &byt_cht_es8316_aif1_ops,
+	},
+
+	[MERR_DPCM_COMPR] = {
+		.name = "Compressed Port",
+		.stream_name = "Compress",
+		.cpu_dai_name = "compress-cpu-dai",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.platform_name = "sst-mfld-platform",
+	},
+
+		/* back ends */
+	{
+		/* Only SSP2 has been tested here, so BYT-CR platforms that
+		 * require SSP0 will not work.
+		 */
+		.name = "SSP2-Codec",
+		.id = 1,
+		.cpu_dai_name = "ssp2-port",
+		.platform_name = "sst-mfld-platform",
+		.no_pcm = 1,
+		.codec_dai_name = "ES8316 HiFi",
+		.codec_name = "i2c-ESSX8316:00",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+						| SND_SOC_DAIFMT_CBS_CFS,
+		.be_hw_params_fixup = byt_cht_es8316_codec_fixup,
+		.nonatomic = true,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.init = byt_cht_es8316_init,
+	},
+};
+
+
+/* SoC card */
+static struct snd_soc_card byt_cht_es8316_card = {
+	.name = "bytcht-es8316",
+	.owner = THIS_MODULE,
+	.dai_link = byt_cht_es8316_dais,
+	.num_links = ARRAY_SIZE(byt_cht_es8316_dais),
+	.dapm_widgets = byt_cht_es8316_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(byt_cht_es8316_widgets),
+	.dapm_routes = byt_cht_es8316_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(byt_cht_es8316_audio_map),
+	.controls = byt_cht_es8316_controls,
+	.num_controls = ARRAY_SIZE(byt_cht_es8316_controls),
+	.fully_routed = true,
+};
+
+static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct byt_cht_es8316_private *priv;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
+	if (!priv)
+		return -ENOMEM;
+
+	/* register the soc card */
+	byt_cht_es8316_card.dev = &pdev->dev;
+	snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv);
+
+	priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
+	if (IS_ERR(priv->mclk)) {
+		ret = PTR_ERR(priv->mclk);
+		dev_err(&pdev->dev,
+			"Failed to get MCLK from pmc_plt_clk_3: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, &byt_cht_es8316_card);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
+		return ret;
+	}
+	platform_set_drvdata(pdev, &byt_cht_es8316_card);
+	return ret;
+}
+
+static struct platform_driver snd_byt_cht_es8316_mc_driver = {
+	.driver = {
+		.name = "bytcht_es8316",
+	},
+	.probe = snd_byt_cht_es8316_mc_probe,
+};
+
+module_platform_driver(snd_byt_cht_es8316_mc_driver);
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Machine driver");
+MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytcht_es8316");
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 1/2] ASoC: add es8316 codec driver
  2017-06-05 20:16 [PATCH v3 1/2] ASoC: add es8316 codec driver Daniel Drake
  2017-06-05 20:16 ` [PATCH v3 2/2] ASoC: Intel: add machine driver for BYT/CHT + ES8316 Daniel Drake
@ 2017-06-06  0:30 ` Takashi Sakamoto
  2017-06-06 13:59   ` Daniel Drake
  1 sibling, 1 reply; 6+ messages in thread
From: Takashi Sakamoto @ 2017-06-06  0:30 UTC (permalink / raw)
  To: Daniel Drake, lgirdwood, broonie
  Cc: alsa-devel, linux, pierre-louis.bossart, yangxiaohua

Hi,

On Jun 6 2017 05:16, Daniel Drake wrote:
> diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
> new file mode 100644
> index 000000000000..ced60a56b4cb
> --- /dev/null
> +++ b/sound/soc/codecs/es8316.c
> ...
> +/*
> + * ES8316 controls
> + */
> +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9600, 50, 1);
> +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -9600, 50, 1);
> +static const DECLARE_TLV_DB_SCALE(alc_max_gain_tlv, -650, 150, 0);
> +static const DECLARE_TLV_DB_SCALE(alc_min_gain_tlv, -1200, 150, 0);
> +static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -1650, 150, 0);
> +static const DECLARE_TLV_DB_SCALE(hpmixer_gain_tlv, -1200, 150, 0);
> +
> +static unsigned int adc_pga_gain_tlv[] = {
> +	TLV_DB_RANGE_HEAD(12),
> +	0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
> +	1, 1, TLV_DB_SCALE_ITEM(300, 0, 0),
> +	2, 2, TLV_DB_SCALE_ITEM(600, 0, 0),
> +	3, 3, TLV_DB_SCALE_ITEM(900, 0, 0),
> +	4, 4, TLV_DB_SCALE_ITEM(1200, 0, 0),
> +	5, 5, TLV_DB_SCALE_ITEM(1500, 0, 0),
> +	6, 6, TLV_DB_SCALE_ITEM(1800, 0, 0),
> +	7, 7, TLV_DB_SCALE_ITEM(2100, 0, 0),
> +	8, 8, TLV_DB_SCALE_ITEM(2400, 0, 0),
> +};
> +static unsigned int hpout_vol_tlv[] = {
> +	TLV_DB_RANGE_HEAD(1),
> +	0, 3, TLV_DB_SCALE_ITEM(-4800, 1200, 0),
> +};

To me, the array of 'adc_pga_gain_tlv' includes invalid members as 
'SNDRV_CTL_TLVT_DB_RANGE'. In your code, the array represents below table:

Linear range: dB range: coeff: mute
0 <-> 0:    0 <->    0: 0: 0
1 <-> 1:  300 <->  300: 0: 0
2 <-> 2:  600 <->  600: 0: 0
3 <-> 3:  900 <->  900: 0: 0
4 <-> 4: 1200 <-> 1200: 0: 0
5 <-> 5: 1500 <-> 1500: 0: 0
6 <-> 6: 1800 <-> 1800: 0: 0
7 <-> 7: 2100 <-> 2100: 0: 0
8 <-> 8: 2400 <-> 2400: 0: 0

Can I ask you to ensure that your hardware has jumps for current dB 
value against given linear value and investigate the reason that maximum 
value in this table has too large value (2,400) as dB representation.

If you don't have real hardware or don't find the detail in datasheet of 
ES8316, please inform it. Then, I leave the decision of merging to Mark 
Brown, a maintainer of ALSA SoC part.

Additionally, these macros are obsoleted. See memo in header[1]. It's 
better to macros in UAPI header[2] for newer codes. Furthermore, the 
usage of 'TLV_DB_RANGE_HEAD' is obsoleted with 'DECLARE_TLV_DB_RANGE'. 
It's better to use 'SNDRV_CTL_TLVD_DECLARE_DB_RANGE', instead. TLV data 
is exposed to userspace applications as is. It's better to use the same 
name of macro in UAPI header so that application developers can easily 
reach your codes.


[1] include/sound/tlv.h
https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git/tree/include/sound/tlv.h#n52
[2] include/uapi/sound/tlv.h
https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git/tree/include/uapi/sound/tlv.h

Regards

Takashi Sakamoto

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 1/2] ASoC: add es8316 codec driver
  2017-06-06  0:30 ` [PATCH v3 1/2] ASoC: add es8316 codec driver Takashi Sakamoto
@ 2017-06-06 13:59   ` Daniel Drake
  2017-06-06 15:02     ` Takashi Sakamoto
  0 siblings, 1 reply; 6+ messages in thread
From: Daniel Drake @ 2017-06-06 13:59 UTC (permalink / raw)
  To: Takashi Sakamoto
  Cc: alsa-devel, Liam Girdwood, Pierre-Louis Bossart, Mark Brown,
	Linux Upstreaming Team, yangxiaohua

Hi Takashi,

On Mon, Jun 5, 2017 at 6:30 PM, Takashi Sakamoto
<o-takashi@sakamocchi.jp> wrote:
> To me, the array of 'adc_pga_gain_tlv' includes invalid members as
> 'SNDRV_CTL_TLVT_DB_RANGE'. In your code, the array represents below table:
>
> Linear range: dB range: coeff: mute
> 0 <-> 0:    0 <->    0: 0: 0
> 1 <-> 1:  300 <->  300: 0: 0
> 2 <-> 2:  600 <->  600: 0: 0
> 3 <-> 3:  900 <->  900: 0: 0
> 4 <-> 4: 1200 <-> 1200: 0: 0
> 5 <-> 5: 1500 <-> 1500: 0: 0
> 6 <-> 6: 1800 <-> 1800: 0: 0
> 7 <-> 7: 2100 <-> 2100: 0: 0
> 8 <-> 8: 2400 <-> 2400: 0: 0
>
> Can I ask you to ensure that your hardware has jumps for current dB value
> against given linear value and investigate the reason that maximum value in
> this table has too large value (2,400) as dB representation.

Thanks for the the info here! Based on the datasheet and newer headers
I have updated it to:

static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(adc_pga_gain_tlv,
    0, 0, TLV_DB_SCALE_ITEM(-3500, 0, 0),
    1, 1, TLV_DB_SCALE_ITEM(0, 0, 0),
    2, 2, TLV_DB_SCALE_ITEM(250, 0, 0),
    3, 3, TLV_DB_SCALE_ITEM(450, 0, 0),
    4, 4, TLV_DB_SCALE_ITEM(700, 0, 0),
    5, 5, TLV_DB_SCALE_ITEM(1000, 0, 0),
    6, 6, TLV_DB_SCALE_ITEM(1300, 0, 0),
    7, 7, TLV_DB_SCALE_ITEM(1600, 0, 0),
    8, 8, TLV_DB_SCALE_ITEM(1800, 0, 0),
    9, 9, TLV_DB_SCALE_ITEM(2100, 0, 0),
    10, 10, TLV_DB_SCALE_ITEM(2400, 0, 0),
);

I believe the final entry means 24dB and does not have the large value
that you have interpreted.
There are many other drivers using similar values like this from rt5616.c:

/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(bst_tlv,
    0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
    1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
    2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
    3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
    6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
    7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
    8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
);

I'll test this change and wait for any further review comments before
sending a v4. I've also noticed that hpout_vol_tlv can be corrected
and simplified.

Thanks
Daniel

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 1/2] ASoC: add es8316 codec driver
  2017-06-06 13:59   ` Daniel Drake
@ 2017-06-06 15:02     ` Takashi Sakamoto
  0 siblings, 0 replies; 6+ messages in thread
From: Takashi Sakamoto @ 2017-06-06 15:02 UTC (permalink / raw)
  To: Daniel Drake
  Cc: alsa-devel, Liam Girdwood, Pierre-Louis Bossart, Mark Brown,
	Linux Upstreaming Team, yangxiaohua

Hi,

On Jul 6 2017 22:59, Daniel Drake wrote:
> Thanks for the the info here! Based on the datasheet and newer headers
> I have updated it to:
> 
> static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(adc_pga_gain_tlv,
>      0, 0, TLV_DB_SCALE_ITEM(-3500, 0, 0),
>      1, 1, TLV_DB_SCALE_ITEM(0, 0, 0),
>      2, 2, TLV_DB_SCALE_ITEM(250, 0, 0),
>      3, 3, TLV_DB_SCALE_ITEM(450, 0, 0),
>      4, 4, TLV_DB_SCALE_ITEM(700, 0, 0),
>      5, 5, TLV_DB_SCALE_ITEM(1000, 0, 0),
>      6, 6, TLV_DB_SCALE_ITEM(1300, 0, 0),
>      7, 7, TLV_DB_SCALE_ITEM(1600, 0, 0),
>      8, 8, TLV_DB_SCALE_ITEM(1800, 0, 0),
>      9, 9, TLV_DB_SCALE_ITEM(2100, 0, 0),
>      10, 10, TLV_DB_SCALE_ITEM(2400, 0, 0),
> );

This is valid data as TLV of DB_RANGE type (11 entries).

> I believe the final entry means 24dB and does not have the large value
> that you have interpreted.

Exactly. I sent my previous message with the mistake. Unit of the first 
field of SNDRV_CTL_TLVD_DB_SCALE_ITEM is 0.01 dB, therefore it's 24.0 
dB, as you realized.

> There are many other drivers using similar values like this from rt5616.c:
> 
> /* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
> static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(bst_tlv,
>      0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
>      1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
>      2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
>      3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
>      6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
>      7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
>      8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
> );
> 
> I'll test this change and wait for any further review comments before
> sending a v4. I've also noticed that hpout_vol_tlv can be corrected
> and simplified.

The data is valid. In this case, the table is:

Linear range: dB range: coeff: mute
  0 <-> 0:    0 <->    0:   0: 0
(0 <-> 1:    0 <-> 2000:2000: 0)
  1 <-> 1: 2000 <-> 2000:   0: 0
(1 <-> 2: 2000 <-> 2400: 400: 0)
  2 <-> 2: 2400 <-> 2400:   0: 0
(2 <-> 3: 2400 <-> 3000: 600: 0)
  3 <-> 5: 3000 <-> 4000: 500: 0
(5 <-> 6: 4000 <-> 4400: 400: 0)
  6 <-> 6: 4400 <-> 4400:   0: 0
(6 <-> 7: 4400 <-> 5000: 600: 0)
  7 <-> 7: 5000 <-> 5000:   0: 0
(7 <-> 8: 5000 <-> 5200: 200: 0)
  8 <-> 8: 5200 <-> 5299:   0: 0

We can use an alternate representation of the TLV data with brackets, like:

static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(bst_tlv,
      0, 1, SNDRV_CTL_TLVD_DB_SCALE_ITEM(0,    1000, 0),
      1, 2, SNDRV_CTL_TLVD_DB_SCALE_ITEM(2000,  400, 0),
      2, 3, SNDRV_CTL_TLVD_DB_SCALE_ITEM(2400,  600, 0),
      3, 5, SNDRV_CTL_TLVD_DB_SCALE_ITEM(3000,  500, 0),
      5, 6, SNDRV_CTL_TLVD_DB_SCALE_ITEM(4000,  400, 0), 

      6, 7, SNDRV_CTL_TLVD_DB_SCALE_ITEM(4400,  600, 0), 

      7, 8, SNDRV_CTL_TLVD_DB_SCALE_ITEM(5000,  200, 0), 

);


Well, in your case, the hardware supports between -35 dB and 24 dB with 
variable 10 steps and the table can also be:

0 <->  1: -3500 <->    0: 3500: 0
1 <->  2:     0 <->  250:  250: 0
2 <->  3:   250 <->  450:  200: 0
3 <->  4:   450 <->  700:  250: 0
4 <->  7:   700 <-> 1600:  300: 0
7 <->  8:  1600 <-> 1800:  200: 0
8 <-> 10:  1800 <-> 2400:  300: 0

Therefore, we can also use an alternate representation, below.

static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(adc_pga_gain_tlv,
     0,  1, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-3500, 3500, 0),
     1,  2, SNDRV_CTL_TLVD_DB_SCALE_ITEM(    0,  250, 0),
     2,  3, SNDRV_CTL_TLVD_DB_SCALE_ITEM(  250,  200, 0),
     3,  4, SNDRV_CTL_TLVD_DB_SCALE_ITEM(  450,  250, 0),
     4,  7, SNDRV_CTL_TLVD_DB_SCALE_ITEM(  700,  300, 0),
     7,  8, SNDRV_CTL_TLVD_DB_SCALE_ITEM( 1600,  200, 0),
     8, 10, SNDRV_CTL_TLVD_DB_SCALE_ITEM( 1800,  300, 0),
);

In alsa-lib, there's a sample program for user-defined control element 
set[1]. It includes some examples of TLV data. 
allocate_int64_elem_set_tlv() adds TLV data similar to your case. This 
data can be parsed by alsa-lib, like:

$ amixer contents
...
numid=4566,iface=MIXER,name='userspace-control-element-INTEGER64',index=896
   ; type=INTEGER64,access=rw---RW-,values=64,min=0,max=10000,step=1
   : 
values=1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
   | dBrange-
     rangemin=0,,rangemax=4
       | dBscale-min=-59.40dB,step=0.44dB,mute=1
     rangemin=4,,rangemax=22
       | dBscale-min=-56.36dB,step=0.60dB,mute=0
     rangemin=22,,rangemax=33
       | dBscale-min=-45.56dB,step=0.76dB,mute=0
     rangemin=33,,rangemax=37
       | dBscale-min=-40.72dB,step=0.44dB,mute=0
     rangemin=37,,rangemax=48
       | dBscale-min=-38.32dB,step=0.76dB,mute=0
     rangemin=48,,rangemax=66
       | dBscale-min=-29.96dB,step=0.60dB,mute=0
     rangemin=66,,rangemax=84
       | dBscale-min=-22.04dB,step=0.44dB,mute=0
     rangemin=84,,rangemax=95
       | dBscale-min=-8.36dB,step=0.60dB,mute=0
     rangemin=95,,rangemax=99
       | dBscale-min=-1.76dB,step=0.76dB,mute=0
     rangemin=100,,rangemax=10000
       | dBscale-min=0.00dB,step=0.00dB,mute=0
...

Both way of representation is valid. For your information.


[1] user-ctl-element-set.c
http://git.alsa-project.org/?p=alsa-lib.git;a=blob;f=test/user-ctl-element-set.c


Regards

Takashi Sakamoto

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Applied "ASoC: Intel: add machine driver for BYT/CHT + ES8316" to the asoc tree
  2017-06-05 20:16 ` [PATCH v3 2/2] ASoC: Intel: add machine driver for BYT/CHT + ES8316 Daniel Drake
@ 2017-06-13 21:08   ` Mark Brown
  0 siblings, 0 replies; 6+ messages in thread
From: Mark Brown @ 2017-06-13 21:08 UTC (permalink / raw)
  To: Daniel Drake
  Cc: alsa-devel, lgirdwood, pierre-louis.bossart, broonie, linux, yangxiaohua

The patch

   ASoC: Intel: add machine driver for BYT/CHT + ES8316

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From a03bdaa565cbf23fa86697727a7d2bf1465e7a03 Mon Sep 17 00:00:00 2001
From: Daniel Drake <drake@endlessm.com>
Date: Mon, 12 Jun 2017 11:01:46 -0600
Subject: [PATCH] ASoC: Intel: add machine driver for BYT/CHT + ES8316

Add new machine driver, tested with Weibu F3C MiniPC.

Based heavily on code provided by David Yang @ Everest, and other
machine drivers in the same directory.

Signed-off-by: David Yang <yangxiaohua@everest-semi.com>
[drake@endlessm.com: cleanups and modernization]
Signed-off-by: Daniel Drake <drake@endlessm.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 sound/soc/intel/Kconfig                |  12 ++
 sound/soc/intel/atom/sst/sst_acpi.c    |   7 +
 sound/soc/intel/boards/Makefile        |   2 +
 sound/soc/intel/boards/bytcht_es8316.c | 300 +++++++++++++++++++++++++++++++++
 4 files changed, 321 insertions(+)
 create mode 100644 sound/soc/intel/boards/bytcht_es8316.c

diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index a9c50d022e73..35a6a5c55914 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -214,6 +214,18 @@ config SND_SOC_INTEL_BYT_CHT_DA7213_MACH
 	  platforms with DA7212/7213 audio codec.
 	  If unsure select "N".
 
+config SND_SOC_INTEL_BYT_CHT_ES8316_MACH
+	tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail with ES8316 codec"
+	depends on X86_INTEL_LPSS && I2C && ACPI
+	select SND_SOC_ES8316
+	select SND_SST_ATOM_HIFI2_PLATFORM
+	select SND_SST_IPC_ACPI
+	select SND_SOC_INTEL_SST_MATCH if ACPI
+	help
+	  This adds support for ASoC machine driver for Intel(R) Baytrail &
+	  Cherrytrail platforms with ES8316 audio codec.
+	  If unsure select "N".
+
 config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH
 	tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail platform with no codec (MinnowBoard MAX, Up)"
 	depends on X86_INTEL_LPSS && I2C && ACPI
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index cf88cd1865fb..0e928d54305d 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -611,6 +611,13 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
 		.board = "bytcht_da7213",
 		.pdata = &chv_platform_data
 	},
+	{
+		.id = "ESSX8316",
+		.drv_name = "bytcht_es8316",
+		.fw_filename = "intel/fw_sst_22a8.bin",
+		.board = "bytcht_es8316",
+		.pdata = &chv_platform_data
+	},
 	/* some CHT-T platforms rely on RT5640, use Baytrail machine driver */
 	{
 		.id = "10EC5640",
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index c92ebcac0222..c4e986f03ec9 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -11,6 +11,7 @@ snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
 snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
 snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
 snd-soc-sst-byt-cht-da7213-objs := bytcht_da7213.o
+snd-soc-sst-byt-cht-es8316-objs := bytcht_es8316.o
 snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o
 snd-soc-kbl_rt5663_max98927-objs := kbl_rt5663_max98927.o
 snd-soc-skl_rt286-objs := skl_rt286.o
@@ -30,6 +31,7 @@ obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH) += snd-soc-sst-byt-cht-da7213.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH) += snd-soc-sst-byt-cht-es8316.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) += snd-soc-sst-byt-cht-nocodec.o
 obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH) += snd-soc-kbl_rt5663_max98927.o
 obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o
diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c
new file mode 100644
index 000000000000..52635462dac6
--- /dev/null
+++ b/sound/soc/intel/boards/bytcht_es8316.c
@@ -0,0 +1,300 @@
+/*
+ *  bytcht_es8316.c - ASoc Machine driver for Intel Baytrail/Cherrytrail
+ *                    platforms with Everest ES8316 SoC
+ *
+ *  Copyright (C) 2017 Endless Mobile, Inc.
+ *  Authors: David Yang <yangxiaohua@everest-semi.com>,
+ *           Daniel Drake <drake@endlessm.com>
+ *
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <asm/platform_sst_audio.h>
+#include <linux/clk.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../atom/sst-atom-controls.h"
+#include "../common/sst-acpi.h"
+#include "../common/sst-dsp.h"
+
+struct byt_cht_es8316_private {
+	struct clk *mclk;
+};
+
+#define CODEC_DAI1	"ES8316 HiFi"
+
+static inline struct snd_soc_dai *get_codec_dai(struct snd_soc_card *card)
+{
+	struct snd_soc_pcm_runtime *rtd;
+
+	list_for_each_entry(rtd, &card->rtd_list, list) {
+		if (!strncmp(rtd->codec_dai->name, CODEC_DAI1,
+			     strlen(CODEC_DAI1)))
+			return rtd->codec_dai;
+	}
+	return NULL;
+}
+
+static const struct snd_soc_dapm_widget byt_cht_es8316_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone", NULL),
+
+	/*
+	 * The codec supports two analog microphone inputs. I have only
+	 * tested MIC1. A DMIC route could also potentially be added
+	 * if such functionality is found on another platform.
+	 */
+	SND_SOC_DAPM_MIC("Microphone 1", NULL),
+	SND_SOC_DAPM_MIC("Microphone 2", NULL),
+};
+
+static const struct snd_soc_dapm_route byt_cht_es8316_audio_map[] = {
+	{"MIC1", NULL, "Microphone 1"},
+	{"MIC2", NULL, "Microphone 2"},
+
+	{"Headphone", NULL, "HPOL"},
+	{"Headphone", NULL, "HPOR"},
+
+	{"Playback", NULL, "ssp2 Tx"},
+	{"ssp2 Tx", NULL, "codec_out0"},
+	{"ssp2 Tx", NULL, "codec_out1"},
+	{"codec_in0", NULL, "ssp2 Rx" },
+	{"codec_in1", NULL, "ssp2 Rx" },
+	{"ssp2 Rx", NULL, "Capture"},
+};
+
+static const struct snd_kcontrol_new byt_cht_es8316_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Headphone"),
+	SOC_DAPM_PIN_SWITCH("Microphone 1"),
+	SOC_DAPM_PIN_SWITCH("Microphone 2"),
+};
+
+static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
+{
+	struct snd_soc_card *card = runtime->card;
+	struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
+	int ret;
+
+	card->dapm.idle_bias_off = true;
+
+	/*
+	 * The firmware might enable the clock at boot (this information
+	 * may or may not be reflected in the enable clock register).
+	 * To change the rate we must disable the clock first to cover these
+	 * cases. Due to common clock framework restrictions that do not allow
+	 * to disable a clock that has not been enabled, we need to enable
+	 * the clock first.
+	 */
+	ret = clk_prepare_enable(priv->mclk);
+	if (!ret)
+		clk_disable_unprepare(priv->mclk);
+
+	ret = clk_set_rate(priv->mclk, 19200000);
+	if (ret)
+		dev_err(card->dev, "unable to set MCLK rate\n");
+
+	ret = clk_prepare_enable(priv->mclk);
+	if (ret)
+		dev_err(card->dev, "unable to enable MCLK\n");
+
+	ret = snd_soc_dai_set_sysclk(runtime->codec_dai, 0, 19200000,
+				     SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		dev_err(card->dev, "can't set codec clock %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_pcm_stream byt_cht_es8316_dai_params = {
+	.formats = SNDRV_PCM_FMTBIT_S24_LE,
+	.rate_min = 48000,
+	.rate_max = 48000,
+	.channels_min = 2,
+	.channels_max = 2,
+};
+
+static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+			    struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+			SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+						SNDRV_PCM_HW_PARAM_CHANNELS);
+	int ret;
+
+	/* The DSP will covert the FE rate to 48k, stereo */
+	rate->min = rate->max = 48000;
+	channels->min = channels->max = 2;
+
+	/* set SSP2 to 24-bit */
+	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+	/*
+	 * Default mode for SSP configuration is TDM 4 slot, override config
+	 * with explicit setting to I2S 2ch 24-bit. The word length is set with
+	 * dai_set_tdm_slot() since there is no other API exposed
+	 */
+	ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
+				SND_SOC_DAIFMT_I2S     |
+				SND_SOC_DAIFMT_NB_NF   |
+				SND_SOC_DAIFMT_CBS_CFS
+		);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int byt_cht_es8316_aif1_startup(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_hw_constraint_single(substream->runtime,
+			SNDRV_PCM_HW_PARAM_RATE, 48000);
+}
+
+static const struct snd_soc_ops byt_cht_es8316_aif1_ops = {
+	.startup = byt_cht_es8316_aif1_startup,
+};
+
+static struct snd_soc_dai_link byt_cht_es8316_dais[] = {
+	[MERR_DPCM_AUDIO] = {
+		.name = "Audio Port",
+		.stream_name = "Audio",
+		.cpu_dai_name = "media-cpu-dai",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.platform_name = "sst-mfld-platform",
+		.nonatomic = true,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.ops = &byt_cht_es8316_aif1_ops,
+	},
+
+	[MERR_DPCM_DEEP_BUFFER] = {
+		.name = "Deep-Buffer Audio Port",
+		.stream_name = "Deep-Buffer Audio",
+		.cpu_dai_name = "deepbuffer-cpu-dai",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.platform_name = "sst-mfld-platform",
+		.nonatomic = true,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.ops = &byt_cht_es8316_aif1_ops,
+	},
+
+	[MERR_DPCM_COMPR] = {
+		.name = "Compressed Port",
+		.stream_name = "Compress",
+		.cpu_dai_name = "compress-cpu-dai",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.platform_name = "sst-mfld-platform",
+	},
+
+		/* back ends */
+	{
+		/* Only SSP2 has been tested here, so BYT-CR platforms that
+		 * require SSP0 will not work.
+		 */
+		.name = "SSP2-Codec",
+		.id = 1,
+		.cpu_dai_name = "ssp2-port",
+		.platform_name = "sst-mfld-platform",
+		.no_pcm = 1,
+		.codec_dai_name = "ES8316 HiFi",
+		.codec_name = "i2c-ESSX8316:00",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+						| SND_SOC_DAIFMT_CBS_CFS,
+		.be_hw_params_fixup = byt_cht_es8316_codec_fixup,
+		.nonatomic = true,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.init = byt_cht_es8316_init,
+	},
+};
+
+
+/* SoC card */
+static struct snd_soc_card byt_cht_es8316_card = {
+	.name = "bytcht-es8316",
+	.owner = THIS_MODULE,
+	.dai_link = byt_cht_es8316_dais,
+	.num_links = ARRAY_SIZE(byt_cht_es8316_dais),
+	.dapm_widgets = byt_cht_es8316_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(byt_cht_es8316_widgets),
+	.dapm_routes = byt_cht_es8316_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(byt_cht_es8316_audio_map),
+	.controls = byt_cht_es8316_controls,
+	.num_controls = ARRAY_SIZE(byt_cht_es8316_controls),
+	.fully_routed = true,
+};
+
+static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct byt_cht_es8316_private *priv;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
+	if (!priv)
+		return -ENOMEM;
+
+	/* register the soc card */
+	byt_cht_es8316_card.dev = &pdev->dev;
+	snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv);
+
+	priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
+	if (IS_ERR(priv->mclk)) {
+		ret = PTR_ERR(priv->mclk);
+		dev_err(&pdev->dev,
+			"Failed to get MCLK from pmc_plt_clk_3: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, &byt_cht_es8316_card);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
+		return ret;
+	}
+	platform_set_drvdata(pdev, &byt_cht_es8316_card);
+	return ret;
+}
+
+static struct platform_driver snd_byt_cht_es8316_mc_driver = {
+	.driver = {
+		.name = "bytcht_es8316",
+	},
+	.probe = snd_byt_cht_es8316_mc_probe,
+};
+
+module_platform_driver(snd_byt_cht_es8316_mc_driver);
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Machine driver");
+MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytcht_es8316");
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2017-06-13 21:08 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-05 20:16 [PATCH v3 1/2] ASoC: add es8316 codec driver Daniel Drake
2017-06-05 20:16 ` [PATCH v3 2/2] ASoC: Intel: add machine driver for BYT/CHT + ES8316 Daniel Drake
2017-06-13 21:08   ` Applied "ASoC: Intel: add machine driver for BYT/CHT + ES8316" to the asoc tree Mark Brown
2017-06-06  0:30 ` [PATCH v3 1/2] ASoC: add es8316 codec driver Takashi Sakamoto
2017-06-06 13:59   ` Daniel Drake
2017-06-06 15:02     ` Takashi Sakamoto

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.