[alsa-devel] [PATCH 1/3] sound: soc: codecs: Add es8328 codec

Sean Cross xobs at kosagi.com
Fri Feb 7 06:05:15 CET 2014


Add support for the ES8328 audio codec.
---
 sound/soc/codecs/Kconfig  |   4 +
 sound/soc/codecs/Makefile |   2 +
 sound/soc/codecs/es8328.c | 611 ++++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/es8328.h | 240 ++++++++++++++++++
 4 files changed, 857 insertions(+)
 create mode 100644 sound/soc/codecs/es8328.c
 create mode 100644 sound/soc/codecs/es8328.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 983d087a..2b0a821 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -43,6 +43,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_DA732X if I2C
 	select SND_SOC_DA9055 if I2C
 	select SND_SOC_BT_SCO
+	select SND_SOC_ES8328 if I2C
 	select SND_SOC_ISABELLE if I2C
 	select SND_SOC_JZ4740_CODEC
 	select SND_SOC_LM4857 if I2C
@@ -283,6 +284,9 @@ config SND_SOC_BT_SCO
 config SND_SOC_DMIC
 	tristate
 
+config SND_SOC_ES8328
+	tristate
+
 config SND_SOC_ISABELLE
         tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index bc12676..1b62c93 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -30,6 +30,7 @@ snd-soc-da732x-objs := da732x.o
 snd-soc-da9055-objs := da9055.o
 snd-soc-bt-sco-objs := bt-sco.o
 snd-soc-dmic-objs := dmic.o
+snd-soc-es8328-objs := es8328.o
 snd-soc-isabelle-objs := isabelle.o
 snd-soc-jz4740-codec-objs := jz4740.o
 snd-soc-l3-objs := l3.o
@@ -163,6 +164,7 @@ obj-$(CONFIG_SND_SOC_DA732X)	+= snd-soc-da732x.o
 obj-$(CONFIG_SND_SOC_DA9055)	+= snd-soc-da9055.o
 obj-$(CONFIG_SND_SOC_BT_SCO)	+= snd-soc-bt-sco.o
 obj-$(CONFIG_SND_SOC_DMIC)	+= snd-soc-dmic.o
+obj-$(CONFIG_SND_SOC_ES8328)    += snd-soc-es8328.o
 obj-$(CONFIG_SND_SOC_ISABELLE)	+= snd-soc-isabelle.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)	+= snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_L3)	+= snd-soc-l3.o
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
new file mode 100644
index 0000000..db118f4
--- /dev/null
+++ b/sound/soc/codecs/es8328.c
@@ -0,0 +1,611 @@
+/*
+ * es8328.c  --  ES8328 ALSA SoC Audio driver
+ *
+ * Copyright 2014 Sutajio Ko-Usagi PTE LTD
+ *
+ * Author: Sean Cross <xobs at kosagi.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/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include "es8328.h"
+
+/* Run the codec at 22.5792 MHz to support these rates */
+enum es8328_rate {
+	ES8328_RATE_8019,
+	ES8328_RATE_11025,
+	ES8328_RATE_22050,
+	ES8328_RATE_44100,
+};
+
+uint8_t sample_ratios[] = {
+	[ES8328_RATE_8019 ] = 0x9,
+	[ES8328_RATE_11025] = 0x7,
+	[ES8328_RATE_22050] = 0x4,
+	[ES8328_RATE_44100] = 0x2,
+};
+
+#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \
+		SNDRV_PCM_RATE_22050 | \
+		SNDRV_PCM_RATE_11025)
+#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+/* codec private data */
+struct es8328_priv {
+	struct regmap *regmap;
+	int sysclk;
+};
+
+static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cap_tlv, -9600, 50, 0);
+static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
+
+static const struct snd_kcontrol_new es8328_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Speaker Playback Volume",
+		ES8328_DACCONTROL26, ES8328_DACCONTROL27, 0, 0x24, 0, play_tlv),
+SOC_DOUBLE_R_TLV("Headphone Playback Volume",
+		ES8328_DACCONTROL24, ES8328_DACCONTROL25, 0, 0x24, 0, play_tlv),
+
+SOC_DOUBLE_R_TLV("Mic Capture Volume",
+		ES8328_ADCCONTROL8, ES8328_ADCCONTROL9, 0, 0xc0, 1, cap_tlv),
+SOC_DOUBLE_TLV("Mic PGA Volume",
+		ES8328_ADCCONTROL1, 4, 0, 0x08, 0, pga_tlv),
+};
+
+/*
+ * DAPM controls.
+ */
+static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
+	SND_SOC_DAPM_DAC("Speaker Volume", "HiFi Playback", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_OUTPUT("VOUTL"),
+	SND_SOC_DAPM_OUTPUT("VOUTR"),
+        SND_SOC_DAPM_INPUT("LINE_IN"),
+        SND_SOC_DAPM_INPUT("MIC_IN"),
+        SND_SOC_DAPM_OUTPUT("HP_OUT"),
+        SND_SOC_DAPM_OUTPUT("SPK_OUT"),
+};
+
+
+static const struct snd_soc_dapm_route es8328_intercon[] = {
+	{"VOUTL", NULL, "DAC"},
+	{"VOUTR", NULL, "DAC"},
+};
+
+static int es8328_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 mute_reg = snd_soc_read(codec, ES8328_DACCONTROL3);
+
+	if (mute)
+		mute_reg |= ES8328_DACCONTROL3_DACMUTE;
+	else
+		mute_reg &= ~ES8328_DACCONTROL3_DACMUTE;
+	return snd_soc_write(codec, ES8328_DACCONTROL3, mute_reg);
+}
+
+static int es8328_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params,
+	struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		u8 dac = snd_soc_read(codec, ES8328_DACCONTROL2);
+		dac &= ~ES8328_DACCONTROL2_RATEMASK;
+
+		switch (params_rate(params)) {
+		case 8000:
+			dac |= sample_ratios[ES8328_RATE_8019];
+			break;
+		case 11025:
+			dac |= sample_ratios[ES8328_RATE_11025];
+			break;
+		case 22050:
+			dac |= sample_ratios[ES8328_RATE_22050];
+			break;
+		case 44100:
+			dac |= sample_ratios[ES8328_RATE_44100];
+			break;
+		default:
+			dev_err(codec->dev, "%s: unknown rate %d\n",
+				 __func__, params_rate(params));
+			return -EINVAL;
+		}
+		snd_soc_write(codec, ES8328_DACCONTROL2, dac);
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		u8 adc = snd_soc_read(codec, ES8328_ADCCONTROL5);
+		adc &= ~ES8328_ADCCONTROL5_RATEMASK;
+
+		switch (params_rate(params)) {
+		case 8000:
+			adc |= sample_ratios[ES8328_RATE_8019];
+			break;
+		case 11025:
+			adc |= sample_ratios[ES8328_RATE_11025];
+			break;
+		case 22050:
+			adc |= sample_ratios[ES8328_RATE_22050];
+			break;
+		case 44100:
+			adc |= sample_ratios[ES8328_RATE_44100];
+			break;
+		default:
+			dev_err(codec->dev, "%s: unknown rate %d\n",
+				 __func__, params_rate(params));
+			return -EINVAL;
+		}
+		snd_soc_write(codec, ES8328_ADCCONTROL5, adc);
+	}
+
+	return 0;
+}
+
+static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S)
+		return -EINVAL;
+
+	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM)
+		return -EINVAL;
+
+	if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int es8328_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 es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+
+	switch (clk_id) {
+	case 0:
+		es8328->sysclk = freq;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int es8328_adc_enable(struct snd_soc_codec *codec)
+{
+	u16 reg = snd_soc_read(codec, ES8328_CHIPPOWER);
+	reg &= ~(ES8328_CHIPPOWER_ADCVREF_OFF |
+		 ES8328_CHIPPOWER_ADCPLL_OFF |
+		 ES8328_CHIPPOWER_ADCSTM_RESET |
+		 ES8328_CHIPPOWER_ADCDIG_OFF);
+	snd_soc_write(codec, ES8328_CHIPPOWER, reg);
+
+	/* Set up microphone to be differential input */
+	snd_soc_write(codec, ES8328_ADCCONTROL2, 0xf0);
+
+	/* Set ADC to act as I2S master */
+	snd_soc_write(codec, ES8328_ADCCONTROL3, 0x02);
+
+	/* Set I2S to 16-bit mode */
+	snd_soc_write(codec, ES8328_ADCCONTROL4, 0x18);
+
+	/* Frequency clock of 272 */
+	snd_soc_write(codec, ES8328_ADCCONTROL5, 0x02);
+
+	return 0;
+}
+
+static int es8328_dac_enable(struct snd_soc_codec *codec)
+{
+	u16 old_volumes[4];
+	u16 reg;
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		old_volumes[i] = snd_soc_read(codec, i + ES8328_DACCONTROL24);
+		snd_soc_write(codec, i + ES8328_DACCONTROL24, 0);
+	}
+
+	/* Power up LOUT2 ROUT2, and power down xOUT1 */
+	snd_soc_write(codec, ES8328_DACPOWER,
+			ES8328_DACPOWER_ROUT2_ON |
+			ES8328_DACPOWER_LOUT2_ON);
+
+	/* Enable click-free power up */
+	snd_soc_write(codec, ES8328_DACCONTROL6, ES8328_DACCONTROL6_CLICKFREE);
+	snd_soc_write(codec, ES8328_DACCONTROL3, 0x36);
+
+	/* Set I2S to 16-bit mode */
+	snd_soc_write(codec, ES8328_DACCONTROL1, ES8328_DACCONTROL1_DACWL_16);
+
+	/* No attenuation */
+	snd_soc_write(codec, ES8328_DACCONTROL4, 0x00);
+	snd_soc_write(codec, ES8328_DACCONTROL5, 0x00);
+
+	/* Set LIN2 for the output mixer */
+	snd_soc_write(codec, ES8328_DACCONTROL16,
+			ES8328_DACCONTROL16_RMIXSEL_RIN2 |
+			ES8328_DACCONTROL16_LMIXSEL_LIN2);
+
+	/* Point the left DAC at the left mixer */
+	snd_soc_write(codec, ES8328_DACCONTROL17, ES8328_DACCONTROL17_LD2LO);
+	/* Point the right DAC at the right mixer */
+	snd_soc_write(codec, ES8328_DACCONTROL20, ES8328_DACCONTROL20_RD2RO);
+
+	/* Disable all other outputs */
+	snd_soc_write(codec, ES8328_DACCONTROL18, 0x00);
+	snd_soc_write(codec, ES8328_DACCONTROL19, 0x00);
+
+
+	/* Disable mono mode for DACL, and mute DACR */
+	snd_soc_write(codec, ES8328_DACCONTROL7, 0x00);
+
+	for (i = 0; i < 4; i++)
+		snd_soc_write(codec, i + ES8328_DACCONTROL24, old_volumes[i]);
+
+	reg = snd_soc_read(codec, ES8328_CHIPPOWER);
+	reg &= ~(ES8328_CHIPPOWER_DACVREF_OFF |
+		 ES8328_CHIPPOWER_DACPLL_OFF |
+		 ES8328_CHIPPOWER_DACSTM_RESET |
+		 ES8328_CHIPPOWER_DACDIG_OFF);
+	snd_soc_write(codec, ES8328_CHIPPOWER, reg);
+	snd_soc_write(codec, ES8328_DACCONTROL3, 0x32);
+
+	return 0;
+}
+
+static int es8328_pcm_prepare(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		es8328_dac_enable(codec);
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		es8328_adc_enable(codec);
+
+	return 0;
+}
+
+static void es8328_pcm_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 reg;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* Mute DAC */
+		snd_soc_write(codec, ES8328_DACCONTROL3,
+				ES8328_DACCONTROL3_DACZEROCROSS |
+				ES8328_DACCONTROL3_DACSOFTRAMP |
+				ES8328_DACCONTROL3_DACMUTE);
+
+		/* Power down DAC and disable LOUT/ROUT */
+		snd_soc_write(codec, ES8328_DACPOWER,
+				ES8328_DACPOWER_LDAC_OFF |
+				ES8328_DACPOWER_RDAC_OFF);
+
+		/* Power down DEM and STM */
+		reg = snd_soc_read(codec, ES8328_CHIPPOWER);
+		reg |= (ES8328_CHIPPOWER_DACVREF_OFF |
+			ES8328_CHIPPOWER_DACPLL_OFF |
+			ES8328_CHIPPOWER_DACDIG_OFF);
+		snd_soc_write(codec, ES8328_CHIPPOWER, reg);
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		/* Mute ADC */
+		snd_soc_write(codec, ES8328_ADCCONTROL7,
+			ES8328_ADCCONTROL7_ADC_LER |
+			ES8328_ADCCONTROL7_ADC_ZERO_CROSS |
+			ES8328_ADCCONTROL7_ADC_SOFT_RAMP);
+
+		/* Power down ADC */
+		snd_soc_write(codec, ES8328_ADCPOWER,
+			ES8328_ADCPOWER_ADC_BIAS_GEN_OFF |
+			ES8328_ADCPOWER_MIC_BIAS_OFF |
+			ES8328_ADCPOWER_ADCR_OFF |
+			ES8328_ADCPOWER_ADCL_OFF |
+			ES8328_ADCPOWER_AINR_OFF |
+			ES8328_ADCPOWER_AINL_OFF);
+
+		/* Power down DEM and STM */
+		reg = snd_soc_read(codec, ES8328_CHIPPOWER);
+		reg |= (ES8328_CHIPPOWER_ADCVREF_OFF |
+			ES8328_CHIPPOWER_ADCPLL_OFF |
+			ES8328_CHIPPOWER_ADCDIG_OFF);
+		snd_soc_write(codec, ES8328_CHIPPOWER, reg);
+	}
+
+	return;
+}
+
+static int es8328_init(struct snd_soc_codec *codec)
+{
+	/* Master serial port mode */
+	snd_soc_write(codec, ES8328_MASTERMODE,
+			ES8328_MASTERMODE_MCLKDIV2 |
+			ES8328_MASTERMODE_MSC);
+
+	/* Power everything down and reset the cip */
+	snd_soc_write(codec, ES8328_CHIPPOWER,
+			ES8328_CHIPPOWER_DACSTM_RESET |
+			ES8328_CHIPPOWER_ADCSTM_RESET |
+			ES8328_CHIPPOWER_DACDIG_OFF |
+			ES8328_CHIPPOWER_ADCDIG_OFF |
+			ES8328_CHIPPOWER_DACVREF_OFF |
+			ES8328_CHIPPOWER_ADCVREF_OFF);
+
+	/* Power up.  Set ADC and DAC to use different frequency ratios */
+	snd_soc_write(codec, ES8328_CONTROL1,
+			ES8328_CONTROL1_VMIDSEL_50k |
+			ES8328_CONTROL1_ENREF);
+
+	/* Power up more blocks */
+	snd_soc_write(codec, ES8328_CONTROL2,
+			ES8328_CONTROL2_OVERCURRENT_ON |
+			ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
+
+
+	/* Power on the chip (but leave VRET off) */
+	/*
+	snd_soc_write(codec, ES8328_CHIPPOWER,
+			ES8328_CHIPPOWER_DACVREF_OFF |
+			ES8328_CHIPPOWER_ADCVREF_OFF);
+	*/
+
+
+	/* Enable muting, and turn on zerocross */
+	snd_soc_write(codec, ES8328_DACCONTROL3,
+			ES8328_DACCONTROL3_DACZEROCROSS |
+			ES8328_DACCONTROL3_DACSOFTRAMP |
+			ES8328_DACCONTROL3_DACMUTE);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops es8328_dai_ops = {
+	.hw_params	= es8328_hw_params,
+	.prepare	= es8328_pcm_prepare,
+	.shutdown	= es8328_pcm_shutdown,
+//	.digital_mute	= es8328_mute,
+	.set_fmt	= es8328_set_dai_fmt,
+	.set_sysclk	= es8328_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver es8328_dai = {
+	.name = "es8328-hifi-analog",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = ES8328_RATES,
+		.formats = ES8328_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = ES8328_RATES,
+		.formats = ES8328_FORMATS,
+	},
+	.ops = &es8328_dai_ops,
+};
+
+static int es8328_suspend(struct snd_soc_codec *codec)
+{
+	return 0;
+}
+
+static int es8328_resume(struct snd_soc_codec *codec)
+{
+	es8328_init(codec);
+	return 0;
+}
+
+static int es8328_probe(struct snd_soc_codec *codec)
+{
+	int ret;
+	struct device *dev = codec->dev;
+
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
+	if (ret < 0) {
+		dev_err(dev, "failed to configure cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	/* power on device */
+	es8328_init(codec);
+
+	return 0;
+}
+
+static int es8328_remove(struct snd_soc_codec *codec)
+{
+	/* Power everything down and reset the cip */
+	snd_soc_write(codec, ES8328_CHIPPOWER,
+			ES8328_CHIPPOWER_DACSTM_RESET |
+			ES8328_CHIPPOWER_ADCSTM_RESET |
+			ES8328_CHIPPOWER_DACDIG_OFF |
+			ES8328_CHIPPOWER_ADCDIG_OFF |
+			ES8328_CHIPPOWER_DACVREF_OFF |
+			ES8328_CHIPPOWER_ADCVREF_OFF);
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_es8328 = {
+	.probe =		es8328_probe,
+	.remove =		es8328_remove,
+	.suspend =		es8328_suspend,
+	.resume =		es8328_resume,
+	.controls =		es8328_snd_controls,
+	.num_controls =		ARRAY_SIZE(es8328_snd_controls),
+	.dapm_widgets =		es8328_dapm_widgets,
+	.num_dapm_widgets =	ARRAY_SIZE(es8328_dapm_widgets),
+	.dapm_routes =		es8328_intercon,
+	.num_dapm_routes =	ARRAY_SIZE(es8328_intercon),
+};
+
+static const struct of_device_id es8328_of_match[] = {
+	{ .compatible = "everest,es8328", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, es8328_of_match);
+
+static const struct regmap_config es8328_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = ES8328_REG_MAX,
+
+	.cache_type = REGCACHE_NONE,
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int es8328_spi_probe(struct spi_device *spi)
+{
+	struct es8328_priv *es8328;
+	int ret;
+
+	es8328 = devm_kzalloc(&spi->dev, sizeof(struct es8328_priv),
+			      GFP_KERNEL);
+	if (es8328 == NULL)
+		return -ENOMEM;
+
+	es8328->regmap = devm_regmap_init_spi(spi, &es8328_regmap);
+	if (IS_ERR(es8328->regmap))
+		return PTR_ERR(es8328->regmap);
+
+	spi_set_drvdata(spi, es8328);
+
+	ret = snd_soc_register_codec(&spi->dev,
+			&soc_codec_dev_es8328, &es8328_dai, 1);
+	if (ret < 0)
+		dev_err(&spi->dev, "unable to register codec: %d\n", ret);
+
+	return ret;
+}
+
+static int es8328_spi_remove(struct spi_device *spi)
+{
+	snd_soc_unregister_codec(&spi->dev);
+
+	return 0;
+}
+
+static struct spi_driver es8328_spi_driver = {
+	.driver = {
+		.name	= "es8328",
+		.owner	= THIS_MODULE,
+		.of_match_table = es8328_of_match,
+	},
+	.probe		= es8328_spi_probe,
+	.remove		= es8328_spi_remove,
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static int es8328_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct es8328_priv *es8328;
+	int ret;
+
+	es8328 = devm_kzalloc(&i2c->dev, sizeof(struct es8328_priv),
+			      GFP_KERNEL);
+	if (es8328 == NULL)
+		return -ENOMEM;
+
+	es8328->regmap = devm_regmap_init_i2c(i2c, &es8328_regmap);
+	if (IS_ERR(es8328->regmap))
+		return PTR_ERR(es8328->regmap);
+
+	i2c_set_clientdata(i2c, es8328);
+
+	ret =  snd_soc_register_codec(&i2c->dev,
+			&soc_codec_dev_es8328, &es8328_dai, 1);
+
+	return ret;
+}
+
+static int es8328_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static const struct i2c_device_id es8328_i2c_id[] = {
+	{ "es8328", 0x11 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, es8328_i2c_id);
+
+static struct i2c_driver es8328_i2c_driver = {
+	.driver = {
+		.name = "es8328-codec",
+		.owner = THIS_MODULE,
+		.of_match_table = es8328_of_match,
+	},
+	.probe =    es8328_i2c_probe,
+	.remove =   es8328_i2c_remove,
+	.id_table = es8328_i2c_id,
+};
+#endif
+
+static int __init es8328_modinit(void)
+{
+	int ret = 0;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	ret = i2c_add_driver(&es8328_i2c_driver);
+	if (ret != 0) {
+		pr_err("failed to register es8328 I2C driver: %d\n", ret);
+	}
+#endif
+#if defined(CONFIG_SPI_MASTER)
+	ret = spi_register_driver(&es8328_spi_driver);
+	if (ret != 0) {
+		pr_err("Failed to register es8328 SPI driver: %d\n", ret);
+	}
+#endif
+	return ret;
+}
+module_init(es8328_modinit);
+
+static void __exit es8328_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&es8328_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+	spi_unregister_driver(&es8328_spi_driver);
+#endif
+}
+module_exit(es8328_exit);
+
+MODULE_DESCRIPTION("ASoC ES8328 driver");
+MODULE_AUTHOR("Sean Cross <xobs at kosagi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h
new file mode 100644
index 0000000..234a6cb
--- /dev/null
+++ b/sound/soc/codecs/es8328.h
@@ -0,0 +1,240 @@
+/*
+ * es8328.h  --  ES8328 ALSA SoC Audio driver
+ */
+
+#ifndef _ES8328_H
+#define _ES8328_H
+
+#define ES8328_DACLVOL 46
+#define ES8328_DACRVOL 47
+#define ES8328_DACCTL 28
+
+#define ES8328_CONTROL1		0x00
+#define ES8328_CONTROL1_VMIDSEL_OFF (0 << 0)
+#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
+#define ES8328_CONTROL1_VMIDSEL_500k (2 << 0)
+#define ES8328_CONTROL1_VMIDSEL_5k (3 << 0)
+#define ES8328_CONTROL1_ENREF (1 << 2)
+#define ES8328_CONTROL1_SEQEN (1 << 3)
+#define ES8328_CONTROL1_SAMEFS (1 << 4)
+#define ES8328_CONTROL1_DACMCLK_ADC (0 << 5)
+#define ES8328_CONTROL1_DACMCLK_DAC (1 << 5)
+#define ES8328_CONTROL1_LRCM (1 << 6)
+#define ES8328_CONTROL1_SCP_RESET (1 << 7)
+
+#define ES8328_CONTROL2		0x01
+#define ES8328_CONTROL2_VREF_BUF_OFF (1 << 0)
+#define ES8328_CONTROL2_VREF_LOWPOWER (1 << 1)
+#define ES8328_CONTROL2_IBIASGEN_OFF (1 << 2)
+#define ES8328_CONTROL2_ANALOG_OFF (1 << 3)
+#define ES8328_CONTROL2_VREF_BUF_LOWPOWER (1 << 4)
+#define ES8328_CONTROL2_VCM_MOD_LOWPOWER (1 << 5)
+#define ES8328_CONTROL2_OVERCURRENT_ON (1 << 6)
+#define ES8328_CONTROL2_THERMAL_SHUTDOWN_ON (1 << 7)
+
+#define ES8328_CHIPPOWER	0x02
+#define ES8328_CHIPPOWER_DACVREF_OFF (1 << 0)
+#define ES8328_CHIPPOWER_ADCVREF_OFF (1 << 1)
+#define ES8328_CHIPPOWER_DACPLL_OFF (1 << 2)
+#define ES8328_CHIPPOWER_ADCPLL_OFF (1 << 3)
+#define ES8328_CHIPPOWER_DACSTM_RESET (1 << 4)
+#define ES8328_CHIPPOWER_ADCSTM_RESET (1 << 5)
+#define ES8328_CHIPPOWER_DACDIG_OFF (1 << 6)
+#define ES8328_CHIPPOWER_ADCDIG_OFF (1 << 7)
+
+#define ES8328_ADCPOWER		0x03
+#define ES8328_ADCPOWER_INT1_LOWPOWER (1 << 0)
+#define ES8328_ADCPOWER_FLASH_ADC_LOWPOWER (1 << 1)
+#define ES8328_ADCPOWER_ADC_BIAS_GEN_OFF (1 << 2)
+#define ES8328_ADCPOWER_MIC_BIAS_OFF (1 << 3)
+#define ES8328_ADCPOWER_ADCR_OFF (1 << 4)
+#define ES8328_ADCPOWER_ADCL_OFF (1 << 5)
+#define ES8328_ADCPOWER_AINR_OFF (1 << 6)
+#define ES8328_ADCPOWER_AINL_OFF (1 << 7)
+
+#define ES8328_DACPOWER		0x04
+#define ES8328_DACPOWER_OUT3_ON (1 << 0)
+#define ES8328_DACPOWER_MONO_ON (1 << 1)
+#define ES8328_DACPOWER_ROUT2_ON (1 << 2)
+#define ES8328_DACPOWER_LOUT2_ON (1 << 3)
+#define ES8328_DACPOWER_ROUT1_ON (1 << 4)
+#define ES8328_DACPOWER_LOUT1_ON (1 << 5)
+#define ES8328_DACPOWER_RDAC_OFF (1 << 6)
+#define ES8328_DACPOWER_LDAC_OFF (1 << 7)
+
+#define ES8328_CHIPLOPOW1	0x05
+#define ES8328_CHIPLOPOW2	0x06
+#define ES8328_ANAVOLMANAG	0x07
+
+#define ES8328_MASTERMODE	0x08
+#define ES8328_MASTERMODE_BCLKDIV (0 << 0)
+#define ES8328_MASTERMODE_BCLK_INV (1 << 5)
+#define ES8328_MASTERMODE_MCLKDIV2 (1 << 6)
+#define ES8328_MASTERMODE_MSC (1 << 7)
+
+#define ES8328_ADCCONTROL1	0x09
+#define ES8328_ADCCONTROL2	0x0a
+#define ES8328_ADCCONTROL3	0x0b
+#define ES8328_ADCCONTROL4	0x0c
+#define ES8328_ADCCONTROL5	0x0d
+#define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0)
+
+#define ES8328_ADCCONTROL6	0x0e
+
+#define ES8328_ADCCONTROL7	0x0f
+#define ES8328_ADCCONTROL7_ADC_MUTE (1 << 2)
+#define ES8328_ADCCONTROL7_ADC_LER (1 << 3)
+#define ES8328_ADCCONTROL7_ADC_ZERO_CROSS (1 << 4)
+#define ES8328_ADCCONTROL7_ADC_SOFT_RAMP (1 << 5)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_4 (0 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_8 (1 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_16 (2 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_32 (3 << 6)
+
+#define ES8328_ADCCONTROL8	0x10
+#define ES8328_ADCCONTROL9	0x11
+#define ES8328_ADCCONTROL10	0x12
+#define ES8328_ADCCONTROL11	0x13
+#define ES8328_ADCCONTROL12	0x14
+#define ES8328_ADCCONTROL13	0x15
+#define ES8328_ADCCONTROL14	0x16
+
+#define ES8328_DACCONTROL1	0x17
+#define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1)
+#define ES8328_DACCONTROL1_DACWL_24 (0 << 3)
+#define ES8328_DACCONTROL1_DACWL_20 (1 << 3)
+#define ES8328_DACCONTROL1_DACWL_18 (2 << 3)
+#define ES8328_DACCONTROL1_DACWL_16 (3 << 3)
+#define ES8328_DACCONTROL1_DACWL_32 (4 << 3)
+#define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
+#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
+#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
+#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK1 (0 << 6)
+#define ES8328_DACCONTROL1_LRSWAP (1 << 7)
+
+#define ES8328_DACCONTROL2	0x18
+#define ES8328_DACCONTROL2_RATEMASK (0x1f << 0)
+
+#define ES8328_DACCONTROL3	0x19
+#define ES8328_DACCONTROL3_AUTOMUTE (1 << 2)
+#define ES8328_DACCONTROL3_DACMUTE (1 << 2)
+#define ES8328_DACCONTROL3_LEFTGAINVOL (1 << 3)
+#define ES8328_DACCONTROL3_DACZEROCROSS (1 << 4)
+#define ES8328_DACCONTROL3_DACSOFTRAMP (1 << 5)
+#define ES8328_DACCONTROL3_DACRAMPRATE (3 << 6)
+
+#define ES8328_DACCONTROL4	0x1a
+#define ES8328_DACCONTROL5	0x1b
+
+#define ES8328_DACCONTROL6	0x1c
+#define ES8328_DACCONTROL6_CLICKFREE (1 << 3)
+#define ES8328_DACCONTROL6_DAC_INVR (1 << 4)
+#define ES8328_DACCONTROL6_DAC_INVL (1 << 5)
+#define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_48k (3 << 6)
+
+#define ES8328_DACCONTROL7	0x1d
+#define ES8328_DACCONTROL8	0x1e
+#define ES8328_DACCONTROL9	0x1f
+#define ES8328_DACCONTROL10	0x20
+#define ES8328_DACCONTROL11	0x21
+#define ES8328_DACCONTROL12	0x22
+#define ES8328_DACCONTROL13	0x23
+#define ES8328_DACCONTROL14	0x24
+#define ES8328_DACCONTROL15	0x25
+
+#define ES8328_DACCONTROL16	0x26
+#define ES8328_DACCONTROL16_RMIXSEL_RIN1 (0 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RIN2 (1 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RIN3 (2 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RADC (3 << 0)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN1 (0 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN2 (1 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN3 (2 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LADC (3 << 3)
+
+#define ES8328_DACCONTROL17	0x27
+#define ES8328_DACCONTROL17_LI2LOVOL (7 << 3)
+#define ES8328_DACCONTROL17_LI2LO (1 << 6)
+#define ES8328_DACCONTROL17_LD2LO (1 << 7)
+
+#define ES8328_DACCONTROL18	0x28
+#define ES8328_DACCONTROL18_RI2LOVOL (7 << 3)
+#define ES8328_DACCONTROL18_RI2LO (1 << 6)
+#define ES8328_DACCONTROL18_RD2LO (1 << 7)
+
+#define ES8328_DACCONTROL19	0x29
+#define ES8328_DACCONTROL19_LI2ROVOL (7 << 3)
+#define ES8328_DACCONTROL19_LI2RO (1 << 6)
+#define ES8328_DACCONTROL19_LD2RO (1 << 7)
+
+#define ES8328_DACCONTROL20	0x2a
+#define ES8328_DACCONTROL20_RI2ROVOL (7 << 3)
+#define ES8328_DACCONTROL20_RI2RO (1 << 6)
+#define ES8328_DACCONTROL20_RD2RO (1 << 7)
+
+#define ES8328_DACCONTROL21	0x2b
+#define ES8328_DACCONTROL22	0x2c
+#define ES8328_DACCONTROL23	0x2d
+#define ES8328_DACCONTROL24	0x2e
+#define ES8328_DACCONTROL25	0x2f
+#define ES8328_DACCONTROL26	0x30
+#define ES8328_DACCONTROL27	0x31
+#define ES8328_DACCONTROL28	0x32
+#define ES8328_DACCONTROL29	0x33
+#define ES8328_DACCONTROL30	0x34
+#define ES8328_SYSCLK		0
+
+#define ES8328_REG_MAX		0x35
+
+#define ES8328_PLL1		0
+#define ES8328_PLL2		1
+
+/* clock inputs */
+#define ES8328_MCLK		0
+#define ES8328_PCMCLK		1
+
+/* clock divider id's */
+#define ES8328_PCMDIV		0
+#define ES8328_BCLKDIV		1
+#define ES8328_VXCLKDIV		2
+
+/* PCM clock dividers */
+#define ES8328_PCM_DIV_1	(0 << 6)
+#define ES8328_PCM_DIV_3	(2 << 6)
+#define ES8328_PCM_DIV_5_5	(3 << 6)
+#define ES8328_PCM_DIV_2	(4 << 6)
+#define ES8328_PCM_DIV_4	(5 << 6)
+#define ES8328_PCM_DIV_6	(6 << 6)
+#define ES8328_PCM_DIV_8	(7 << 6)
+
+/* BCLK clock dividers */
+#define ES8328_BCLK_DIV_1	(0 << 7)
+#define ES8328_BCLK_DIV_2	(1 << 7)
+#define ES8328_BCLK_DIV_4	(2 << 7)
+#define ES8328_BCLK_DIV_8	(3 << 7)
+
+/* VXCLK clock dividers */
+#define ES8328_VXCLK_DIV_1	(0 << 6)
+#define ES8328_VXCLK_DIV_2	(1 << 6)
+#define ES8328_VXCLK_DIV_4	(2 << 6)
+#define ES8328_VXCLK_DIV_8	(3 << 6)
+#define ES8328_VXCLK_DIV_16	(4 << 6)
+
+#define ES8328_DAI_HIFI		0
+#define ES8328_DAI_VOICE	1
+
+#define ES8328_1536FS		1536
+#define ES8328_1024FS		1024
+#define ES8328_768FS		768
+#define ES8328_512FS		512
+#define ES8328_384FS		384
+#define ES8328_256FS		256
+#define ES8328_128FS		128
+
+#endif
-- 
1.8.3.2



More information about the Alsa-devel mailing list