[alsa-devel] [PATCH v2 7/9] ASoC: Codec: Add sti platform codec

Arnaud Pouliquen arnaud.pouliquen at st.com
Mon May 18 14:12:54 CEST 2015


Codec part of the STi platform that support codec IPs.
This first version does not support HDMI, but only DAC and SPDIF out.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen at st.com>
---
 sound/soc/codecs/Kconfig   |   4 +
 sound/soc/codecs/Makefile  |   2 +
 sound/soc/codecs/sti-sas.c | 645 +++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/sti/Kconfig      |   6 +
 4 files changed, 657 insertions(+)
 create mode 100644 sound/soc/codecs/sti-sas.c

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 061c465..f3fe64e 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -102,6 +102,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_STA350 if I2C
 	select SND_SOC_STA529 if I2C
 	select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
+	select SND_SOC_STI_SAS
 	select SND_SOC_TAS2552 if I2C
 	select SND_SOC_TAS5086 if I2C
 	select SND_SOC_TFA9879 if I2C
@@ -603,6 +604,9 @@ config SND_SOC_STA529
 config SND_SOC_STAC9766
 	tristate
 
+config SND_SOC_STI_SAS
+	tristate
+
 config SND_SOC_TAS2552
 	tristate "Texas Instruments TAS2552 Mono Audio amplifier"
 	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index abe2d7e..249ef0d 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -105,6 +105,7 @@ snd-soc-sta32x-objs := sta32x.o
 snd-soc-sta350-objs := sta350.o
 snd-soc-sta529-objs := sta529.o
 snd-soc-stac9766-objs := stac9766.o
+snd-soc-sti-sas-objs := sti-sas.o
 snd-soc-tas5086-objs := tas5086.o
 snd-soc-tfa9879-objs := tfa9879.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
@@ -286,6 +287,7 @@ obj-$(CONFIG_SND_SOC_STA32X)   += snd-soc-sta32x.o
 obj-$(CONFIG_SND_SOC_STA350)   += snd-soc-sta350.o
 obj-$(CONFIG_SND_SOC_STA529)   += snd-soc-sta529.o
 obj-$(CONFIG_SND_SOC_STAC9766)	+= snd-soc-stac9766.o
+obj-$(CONFIG_SND_SOC_STI_SAS)	+= snd-soc-sti-sas.o
 obj-$(CONFIG_SND_SOC_TAS2552)	+= snd-soc-tas2552.o
 obj-$(CONFIG_SND_SOC_TAS5086)	+= snd-soc-tas5086.o
 obj-$(CONFIG_SND_SOC_TFA9879)	+= snd-soc-tfa9879.o
diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c
new file mode 100644
index 0000000..6cd4c1d
--- /dev/null
+++ b/sound/soc/codecs/sti-sas.c
@@ -0,0 +1,645 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2015
+ * Authors: Arnaud Pouliquen <arnaud.pouliquen at st.com>
+ *          for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+/* chipID supported */
+#define CHIPID_STIH416 0
+#define CHIPID_STIH407 1
+
+/* DAC definitions */
+
+/* stih416 DAC registers */
+/* sysconf 2517: Audio-DAC-Control */
+#define STIH416_AUDIO_DAC_CTRL 0x00000814
+/* sysconf 2519: Audio-Gue-Control */
+#define STIH416_AUDIO_GLUE_CTRL 0x0000081C
+
+/* stih407 DAC registers */
+/* sysconf 5041: Audio-Gue-Control */
+#define STIH407_AUDIO_GLUE_CTRL 0x000000A4
+/* sysconf 5042: Audio-DAC-Control */
+#define STIH407_AUDIO_DAC_CTRL 0x000000A8
+
+enum {
+	STI_SAS_DAI_SPDIF_OUT,
+	STI_SAS_DAI_ANALOG_OUT,
+};
+
+enum {
+	BIPHASE_ENABLE,
+	BIPHASE_IDLE,
+	STI_SPDIF_MAX_RF
+};
+
+enum {
+	STIH416_DAC_MODE,
+	STIH416_DAC_NOT_STANDBY,
+	STIH416_DAC_SOFTMUTE,
+	STIH416_DAC_ANALOG_PWR_DW,
+	STIH416_DAC_ANALOG_NOT_PWR_DW_BG,
+	STIH416_DAC_MAX_RF
+};
+
+enum {
+	STIH407_DAC_SOFTMUTE,
+	STIH407_DAC_STANDBY_ANA,
+	STIH407_DAC_STANDBY,
+	STIH407_DAC_MAX_RF
+};
+
+/* DAC configuration fields */
+const struct reg_field sti_sas_dac_stih416[STIH416_DAC_MAX_RF] = {
+	/*STIH416_DAC_MODE*/
+	REG_FIELD(STIH416_AUDIO_DAC_CTRL, 1, 2),
+	/*STIH416_DAC_NOT_STANDBY */
+	REG_FIELD(STIH416_AUDIO_DAC_CTRL, 3, 3),
+	/*STIH416_DAC_SOFTMUTE*/
+	REG_FIELD(STIH416_AUDIO_DAC_CTRL, 4, 4),
+	/*STIH416_DAC_ANALOG_PWR_DW*/
+	REG_FIELD(STIH416_AUDIO_DAC_CTRL, 5, 5),
+	/*STIH416_DAC_ANALOG_NOT_PWR_DW_BG*/
+	REG_FIELD(STIH416_AUDIO_DAC_CTRL, 6, 6),
+};
+
+const struct reg_field sti_sas_dac_stih407[STIH407_DAC_MAX_RF] = {
+	/*STIH407_DAC_SOFTMUTE*/
+	REG_FIELD(STIH407_AUDIO_DAC_CTRL, 0, 0),
+	/*STIH407_DAC_STANDBY_ANA*/
+	REG_FIELD(STIH407_AUDIO_DAC_CTRL, 1, 1),
+	/*STIH407_DAC_STANDBY */
+	REG_FIELD(STIH407_AUDIO_DAC_CTRL, 2, 2),
+};
+
+/* SPDIF configuration fields */
+const struct reg_field sti_sas_spdif_stih416[STI_SPDIF_MAX_RF] = {
+	/*BIPHASE_ENABLE */
+	REG_FIELD(STIH416_AUDIO_GLUE_CTRL, 6, 6),
+	/*BIPHASE_IDLE*/
+	REG_FIELD(STIH416_AUDIO_GLUE_CTRL, 7, 7),
+};
+
+const struct reg_field sti_sas_spdif_stih407[STI_SPDIF_MAX_RF] = {
+	/*BIPHASE_ENABLE */
+	REG_FIELD(STIH407_AUDIO_GLUE_CTRL, 6, 6),
+	/*BIPHASE_IDLE*/
+	REG_FIELD(STIH407_AUDIO_GLUE_CTRL, 7, 7),
+};
+
+struct sti_dac_audio {
+	struct regmap *regmap;
+	struct regmap_field  **field;
+	struct reset_control *rst;
+};
+
+struct sti_spdif_audio {
+	struct regmap *regmap;
+	struct regmap_field  **field;
+};
+
+const int sti_sas_dai_clk_div[];
+
+/* device data structure */
+struct sti_sas_dev_data {
+	const int chipid; /* IC version */
+	const struct reg_field *dac; /* DAC register description*/
+	const struct snd_soc_dai_ops *dac_ops;  /* DAC function callbacks */
+	const struct reg_field *spdif; /* SPDIFF register description */
+	const struct snd_soc_dapm_widget *dapm_widgets; /* dapms declaration */
+	const int num_dapm_widgets; /* dapms declaration */
+};
+
+/* driver data structure */
+struct sti_sas_data {
+	struct device *dev;
+	const struct sti_sas_dev_data *dev_data;
+	struct sti_dac_audio dac;
+	struct sti_spdif_audio spdif;
+};
+
+static int sti_sas_map_sas_registers(struct sti_sas_data *data)
+{
+	struct sti_spdif_audio *spdif = &data->spdif;
+	const struct reg_field *spdif_reg = data->dev_data->spdif;
+	struct sti_dac_audio *dac = &data->dac;
+	const struct reg_field *dac_reg = data->dev_data->dac;
+	int i, max_rf;
+
+	/* Allocate device structure */
+	spdif->field = devm_kzalloc(data->dev,
+			     sizeof(struct regmap_field *) * STI_SPDIF_MAX_RF,
+			     GFP_KERNEL);
+	if (!spdif->field)
+		return -ENOMEM;
+
+	if (data->dev_data->chipid == CHIPID_STIH407)
+		max_rf = STIH407_DAC_MAX_RF;
+	else
+		max_rf = STIH416_DAC_MAX_RF;
+	dac->field = devm_kzalloc(data->dev,
+				  sizeof(struct regmap_field *) * max_rf,
+				  GFP_KERNEL);
+	if (!dac->field)
+		return -ENOMEM;
+
+	/* Get spdif regmap fields */
+	for (i = 0; i < STI_SPDIF_MAX_RF; i++)
+		spdif->field[i] = regmap_field_alloc(spdif->regmap,
+						     spdif_reg[i]);
+	/* Get DAC regmap fields */
+	for (i = 0; i < max_rf; i++)
+		dac->field[i] = regmap_field_alloc(dac->regmap, dac_reg[i]);
+
+	return 0;
+}
+
+static int  sti_sas_init_sas_registers(struct device *dev,
+				       struct sti_sas_data *data)
+{
+	struct sti_spdif_audio *spdif = &data->spdif;
+	struct sti_dac_audio *dac = &data->dac;
+	int ret;
+	/*
+	 * DAC and SPDIF are activated by default
+	 * put them in IDLE to save power
+	 */
+
+	/* Initialise bi-phase formatter to disabled */
+	ret = regmap_field_write(spdif->field[BIPHASE_ENABLE], 0);
+
+	if (!ret)
+		/* Initialise bi-phase formatter idle value to 0 */
+		ret = regmap_field_write(spdif->field[BIPHASE_IDLE], 0);
+	if (ret < 0) {
+		dev_err(dev, "Failed to update SPDIF registers");
+		return ret;
+	}
+
+	/* Init DAC configuration */
+	if (data->dev_data->chipid == CHIPID_STIH407) {
+		/* init configuration */
+		ret = regmap_field_write(
+			dac->field[STIH407_DAC_STANDBY], 1);
+
+		if (!ret)
+			ret = regmap_field_write(
+				dac->field[STIH407_DAC_STANDBY_ANA], 1);
+		if (!ret)
+			ret = regmap_field_write(
+				dac->field[STIH407_DAC_SOFTMUTE], 1);
+	} else if (data->dev_data->chipid == CHIPID_STIH416) {
+		ret = regmap_field_write(
+			dac->field[STIH416_DAC_NOT_STANDBY], 0);
+		if (!ret)
+			ret = regmap_field_write(
+				dac->field[STIH416_DAC_ANALOG_PWR_DW], 0);
+		if (!ret)
+			ret = regmap_field_write(
+				dac->field[STIH416_DAC_ANALOG_NOT_PWR_DW_BG],
+				0);
+		if (!ret)
+			ret = regmap_field_write(
+				dac->field[STIH416_DAC_SOFTMUTE], 1);
+	}
+
+	if (ret < 0) {
+		dev_err(dev, "Failed to update DAC registers");
+		return ret;
+	}
+
+	return ret;
+}
+
+/*
+ * DAC
+ */
+static int sti_sas_dac_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	/* Sanity check only */
+	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
+		dev_err(dai->codec->dev,
+			"%s: ERROR: Unsupporter master mask 0x%x\n",
+			__func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int stih416_dac_probe(struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+	struct sti_dac_audio *dac = &drvdata->dac;
+
+	/* Get reset control */
+	dac->rst = devm_reset_control_get(codec->dev, "dac_rst");
+	if (IS_ERR(dac->rst)) {
+		dev_err(dai->codec->dev,
+			"%s: ERROR: DAC reset control not defined (%d)!\n",
+			__func__, (int)dac->rst);
+		dac->rst = NULL;
+		return -EFAULT;
+	}
+	/* Put the DAC into reset */
+	reset_control_assert(dac->rst);
+
+	return 0;
+}
+
+static int  stih416_sas_dac_supply(struct snd_soc_dapm_widget *w,
+				   struct snd_kcontrol *kcontrol, int event) {
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+	struct sti_dac_audio *dac = &drvdata->dac;
+	int ret = 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Take the DAC out of reset */
+		reset_control_deassert(dac->rst);
+
+		/* Enable analog */
+		ret = regmap_field_write(
+			dac->field[STIH416_DAC_ANALOG_NOT_PWR_DW_BG], 1);
+		if (!ret)
+			ret = regmap_field_write(
+				dac->field[STIH416_DAC_ANALOG_PWR_DW], 1);
+		/* Disable standby */
+		if (!ret)
+			ret = regmap_field_write(
+				dac->field[STIH416_DAC_NOT_STANDBY], 1);
+
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+
+		/* Enable standby */
+		ret = regmap_field_write(
+			dac->field[STIH416_DAC_NOT_STANDBY], 0);
+		/* Disable analog */
+		if (!ret)
+			ret = regmap_field_write(
+				dac->field[STIH416_DAC_ANALOG_PWR_DW], 0);
+		if (!ret)
+			ret = regmap_field_write(
+				dac->field[STIH416_DAC_ANALOG_NOT_PWR_DW_BG],
+				0);
+		/* Put the DAC into reset */
+		reset_control_assert(dac->rst);
+		break;
+	}
+
+	return ret;
+}
+
+static int  stih407_sas_dac_supply(struct snd_soc_dapm_widget *w,
+				   struct snd_kcontrol *kcontrol, int event) {
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+	struct sti_dac_audio *dac = &drvdata->dac;
+	int ret = 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Enable analog */
+		ret = regmap_field_write(dac->field[STIH407_DAC_STANDBY_ANA],
+					 0);
+		/* Disable standby */
+		if (!ret)
+			ret = regmap_field_write(
+				dac->field[STIH407_DAC_STANDBY], 0);
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		/* Enable standby */
+		ret = regmap_field_write(dac->field[STIH407_DAC_STANDBY], 1);
+		/* Disable analog */
+		if (!ret)
+			ret = regmap_field_write(
+				dac->field[STIH407_DAC_STANDBY_ANA], 1);
+		break;
+	}
+
+	return ret;
+}
+
+const struct snd_soc_dapm_widget stih416_sas_dapm_widgets[] = {
+	SND_SOC_DAPM_DAC_E("DAC enable", "dac_p", SND_SOC_NOPM, 0, 0,
+			   stih416_sas_dac_supply,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_OUTPUT("DAC Output"),
+};
+
+const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = {
+	SND_SOC_DAPM_DAC_E("DAC enable", "dac_p", SND_SOC_NOPM, 0, 0,
+			   stih407_sas_dac_supply,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_OUTPUT("DAC Output"),
+};
+
+const struct snd_soc_dapm_route sti_sas_route[] = {
+	{"DAC Output", NULL, "DAC enable"},
+};
+
+static int stih416_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+	struct sti_dac_audio *dac = &drvdata->dac;
+
+	if (mute)
+		return regmap_field_write(
+			dac->field[STIH416_DAC_SOFTMUTE], 1);
+	else
+		return regmap_field_write(
+			dac->field[STIH416_DAC_SOFTMUTE], 0);
+}
+
+static int stih407_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+	struct sti_dac_audio *dac = &drvdata->dac;
+
+	if (mute)
+		return regmap_field_write(
+			dac->field[STIH407_DAC_SOFTMUTE], 1);
+	else
+		return regmap_field_write(
+			dac->field[STIH407_DAC_SOFTMUTE], 0);
+}
+
+/*
+ * SPDIF
+ */
+static int sti_sas_spdif_set_fmt(struct snd_soc_dai *dai,
+				 unsigned int fmt)
+{
+	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
+		dev_err(dai->codec->dev,
+			"%s: ERROR: Unsupporter master mask 0x%x\n",
+			__func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * sti_sas_spdif_trigger:
+ * Trigger function is used to ensure that BiPhase Formater is disabled
+ * before CPU dai is stopped.
+ * This is mandatory to avoid that BPF is stalled
+ */
+static int sti_sas_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+				 struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+	struct sti_spdif_audio *spdif =  &drvdata->spdif;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		return regmap_field_write(spdif->field[BIPHASE_ENABLE],
+					  1);
+		break;
+
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		return regmap_field_write(spdif->field[BIPHASE_ENABLE],
+					  0);
+		break;
+	default:
+		return -EINVAL;
+	}
+}
+
+/*
+ * CODEC DAIS
+ */
+
+/*
+ * sti_sas_hw_params:
+ * Request MCLK-FS clocks division to CPU_DAI based on requested rate
+ */
+static int sti_sas_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;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int div;
+
+	div = sti_sas_dai_clk_div[dai->id];
+	if (cpu_dai->driver->ops->set_clkdiv)
+		return cpu_dai->driver->ops->set_clkdiv(cpu_dai,
+							SND_SOC_CLOCK_OUT, div);
+	dev_warn(codec->dev, "WARN: CPU DAI not support sysclk div");
+
+	return 0;
+}
+
+const struct snd_soc_dai_ops stih416_dac_ops = {
+	.set_fmt = sti_sas_dac_set_fmt,
+	.mute_stream = stih416_sas_dac_mute,
+	.hw_params = sti_sas_hw_params
+};
+
+const struct snd_soc_dai_ops stih407_dac_ops = {
+	.set_fmt = sti_sas_dac_set_fmt,
+	.mute_stream = stih407_sas_dac_mute,
+	.hw_params = sti_sas_hw_params
+};
+
+const struct sti_sas_dev_data stih416_data = {
+	.chipid = CHIPID_STIH416,
+	.dac = sti_sas_dac_stih416,
+	.dac_ops = &stih416_dac_ops,
+	.spdif = sti_sas_spdif_stih416,
+	.dapm_widgets = stih416_sas_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(stih416_sas_dapm_widgets)
+};
+
+const struct sti_sas_dev_data stih407_data = {
+	.chipid = CHIPID_STIH407,
+	.dac = sti_sas_dac_stih407,
+	.dac_ops = &stih407_dac_ops,
+	.spdif = sti_sas_spdif_stih407,
+	.dapm_widgets = stih407_sas_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(stih407_sas_dapm_widgets)
+};
+
+static struct snd_soc_dai_driver sti_sas_dai[] = {
+	{
+		.name = "sas-dai-spdif-out",
+		.id = STI_SAS_DAI_SPDIF_OUT,
+		.playback = {
+			.stream_name = "spdif_p",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+				 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 |
+				 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+				 SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = (struct snd_soc_dai_ops[]) {
+			{
+				.set_fmt = sti_sas_spdif_set_fmt,
+				.trigger = sti_sas_spdif_trigger,
+				.hw_params = sti_sas_hw_params,
+			}
+		},
+	},
+	{
+		.name = "sas-dai-dac",
+		.id = STI_SAS_DAI_ANALOG_OUT,
+		.playback = {
+			.stream_name = "dac_p",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+	},
+};
+
+const int sti_sas_dai_clk_div[ARRAY_SIZE(sti_sas_dai)] = {
+	128, /* spdif out */
+	256, /* dac */
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int sti_sas_resume(struct snd_soc_codec *codec)
+{
+	struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+
+	return sti_sas_init_sas_registers(codec->dev, drvdata);
+}
+#else
+#define sti_sas_resume NULL
+#endif
+
+static int sti_sas_codec_probe(struct snd_soc_codec *codec)
+{
+	struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev);
+	int ret;
+
+	ret = sti_sas_init_sas_registers(codec->dev, drvdata);
+
+	return ret;
+}
+
+static struct snd_soc_codec_driver sti_sas_driver = {
+	.probe = sti_sas_codec_probe,
+	.resume = sti_sas_resume,
+	.dapm_routes =	sti_sas_route,
+	.num_dapm_routes = ARRAY_SIZE(sti_sas_route),
+};
+
+static const struct of_device_id sti_sas_dev_match[] = {
+	{
+		.compatible = "st,stih416-sas-codec",
+		.data = &stih416_data,
+	},
+	{
+		.compatible = "st,stih407-sas-codec",
+		.data = &stih407_data,
+	},
+	{},
+};
+
+static int sti_sas_driver_probe(struct platform_device *pdev)
+{
+	struct device_node *pnode = pdev->dev.of_node;
+	int ret = 0;
+	struct sti_sas_data *drvdata;
+
+	/* Allocate device structure */
+	drvdata = devm_kzalloc(&pdev->dev, sizeof(struct sti_sas_data),
+			       GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+
+	/* Populate data structure depending on compatibility */
+	if (!of_match_node(sti_sas_dev_match, pnode)->data) {
+		dev_err(&pdev->dev, "data associated to device is missing");
+		return -EINVAL;
+	}
+
+	drvdata->dev_data = of_match_node(sti_sas_dev_match, pnode)->data;
+
+	/* Initialise device structure */
+	drvdata->dev = &pdev->dev;
+
+	/* Request the DAC & SPDIF registers memory region */
+	drvdata->dac.regmap =
+		syscon_regmap_lookup_by_phandle(pnode, "st,syscfg");
+	if (!drvdata->dac.regmap) {
+		dev_err(&pdev->dev, "audio registers not enabled\n");
+		return -EFAULT;
+	}
+	drvdata->spdif.regmap = drvdata->dac.regmap;
+
+	ret = sti_sas_map_sas_registers(drvdata);
+	if (ret < 0)
+		return ret;
+
+	/* Set DAC dai probe */
+	if (drvdata->dev_data->chipid == CHIPID_STIH416)
+		sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].probe = stih416_dac_probe;
+
+	sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].ops = drvdata->dev_data->dac_ops;
+
+	/* Set dapms*/
+	sti_sas_driver.dapm_widgets = drvdata->dev_data->dapm_widgets;
+	sti_sas_driver.num_dapm_widgets = drvdata->dev_data->num_dapm_widgets;
+
+	/* Store context */
+	dev_set_drvdata(&pdev->dev, drvdata);
+
+	return snd_soc_register_codec(&pdev->dev, &sti_sas_driver,
+					sti_sas_dai,
+					ARRAY_SIZE(sti_sas_dai));
+}
+
+static int sti_sas_driver_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_codec(&pdev->dev);
+
+	return 0;
+}
+
+static struct platform_driver sti_sas_platform_driver = {
+	.driver = {
+		.name = "sti-sas-codec",
+		.owner = THIS_MODULE,
+		.of_match_table = sti_sas_dev_match,
+	},
+	.probe = sti_sas_driver_probe,
+	.remove = sti_sas_driver_remove,
+};
+
+module_platform_driver(sti_sas_platform_driver);
+
+MODULE_DESCRIPTION("audio codec for STMicroelectronics sti platforms");
+MODULE_AUTHOR("Arnaud.pouliquen at st.com");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/sti/Kconfig b/sound/soc/sti/Kconfig
index c29e219..af5d6fd 100644
--- a/sound/soc/sti/Kconfig
+++ b/sound/soc/sti/Kconfig
@@ -9,3 +9,9 @@ menuconfig SND_SOC_STI
 	help
 		Say Y if you want to enable ASoC-support for
 		any of the STI platforms (e.g. STIH416).
+
+config SND_SOC_STI_SAS
+	tristate "codec Audio support for STI SAS codec"
+	depends on SND_SOC_STI
+	help
+		Say Y if you want to include STI SAS audio codec support
\ No newline at end of file
-- 
1.9.1



More information about the Alsa-devel mailing list