[alsa-devel] [PATCH 7/7] ASoc: Codec: add sti platform codec
Arnaud Pouliquen
arnaud.pouliquen at st.com
Tue Apr 14 15:35:31 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 | 663 +++++++++++++++++++++++++++++++++++++++++++++
sound/soc/sti/Kconfig | 6 +
4 files changed, 675 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..e3448a0
--- /dev/null
+++ b/sound/soc/codecs/sti-sas.c
@@ -0,0 +1,663 @@
+/*
+ * 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>
+
+/* 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
+
+#define STI_DAC_MIN_CHANNELS 2
+#define STI_DAC_MAX_CHANNELS 2
+
+/* SPDIF definitions */
+#define STI_SPDIF_MIN_CHANNELS 2
+#define STI_SPDIF_MAX_CHANNELS 2
+
+static const struct of_device_id sti_sas_codec_drv_match[];
+
+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
+};
+
+struct codec_regfield {
+ struct regmap_field *field;
+ const struct reg_field regfield;
+};
+
+/* dac configuration fields */
+static struct codec_regfield sti_sas_dac_stih416[STIH416_DAC_MAX_RF] = {
+ /*STIH416_DAC_MODE*/
+ {NULL, REG_FIELD(STIH416_AUDIO_DAC_CTRL, 1, 2)},
+ /*STIH416_DAC_NOT_STANDBY */
+ {NULL, REG_FIELD(STIH416_AUDIO_DAC_CTRL, 3, 3)},
+ /*STIH416_DAC_SOFTMUTE*/
+ {NULL, REG_FIELD(STIH416_AUDIO_DAC_CTRL, 4, 4)},
+ /*STIH416_DAC_ANALOG_PWR_DW*/
+ {NULL, REG_FIELD(STIH416_AUDIO_DAC_CTRL, 5, 5)},
+ /*STIH416_DAC_ANALOG_NOT_PWR_DW_BG*/
+ {NULL, REG_FIELD(STIH416_AUDIO_DAC_CTRL, 6, 6)},
+};
+
+static struct codec_regfield sti_sas_dac_stih407[STIH407_DAC_MAX_RF] = {
+ /*STIH407_DAC_SOFTMUTE*/
+ {NULL, REG_FIELD(STIH407_AUDIO_DAC_CTRL, 0, 0)},
+ /*STIH407_DAC_STANDBY_ANA*/
+ {NULL, REG_FIELD(STIH407_AUDIO_DAC_CTRL, 1, 1)},
+ /*STIH407_DAC_STANDBY */
+ {NULL, REG_FIELD(STIH407_AUDIO_DAC_CTRL, 2, 2)},
+};
+
+/* SPDIF configuration fields */
+static struct codec_regfield sti_sas_spdif_stih416[STI_SPDIF_MAX_RF] = {
+ /*BIPHASE_ENABLE */
+ {NULL, REG_FIELD(STIH416_AUDIO_GLUE_CTRL, 6, 6)},
+ /*BIPHASE_IDLE*/
+ {NULL, REG_FIELD(STIH416_AUDIO_GLUE_CTRL, 7, 7)},
+};
+
+static struct codec_regfield sti_sas_spdif_stih407[STI_SPDIF_MAX_RF] = {
+ /*BIPHASE_ENABLE */
+ {NULL, REG_FIELD(STIH407_AUDIO_GLUE_CTRL, 6, 6)},
+ /*BIPHASE_IDLE*/
+ {NULL, REG_FIELD(STIH407_AUDIO_GLUE_CTRL, 7, 7)},
+};
+
+/* specify ops depends on codec version */
+struct sti_codec_ops {
+ int (*dai_dac_probe)(struct snd_soc_dai *dai);
+ int (*mute)(struct snd_soc_dai *dai, int mute);
+ int (*startup)(struct snd_soc_dai *);
+ void (*shutdown)(struct snd_soc_dai *);
+};
+
+struct sti_dac_audio {
+ const struct sti_codec_ops *ops;
+ struct regmap *regmap;
+ struct codec_regfield *regfield;
+ struct reset_control *rst;
+};
+
+struct sti_spdif_audio {
+ struct regmap *regmap;
+ struct codec_regfield *regfield;
+ struct reset_control *rst;
+};
+
+int sti_sas_dai_clk_div[];
+
+struct sti_sas_codec_data {
+ const int chipid;
+ struct device *dev;
+ struct sti_dac_audio dac;
+ struct sti_spdif_audio spdif;
+};
+
+static void sti_sas_map_sas_registers(struct sti_sas_codec_data *data)
+{
+ struct sti_spdif_audio *spdif = &data->spdif;
+ struct sti_dac_audio *dac = &data->dac;
+ int i, max_rf;
+
+ /* Get spdif regmap fields */
+ for (i = 0; i < STI_SPDIF_MAX_RF; i++)
+ spdif->regfield[i].field =
+ regmap_field_alloc(spdif->regmap,
+ spdif->regfield[i].regfield);
+ /* Get DAC regmap fields */
+ if (data->chipid == CHIPID_STIH407)
+ max_rf = STIH407_DAC_MAX_RF;
+ else
+ max_rf = STIH416_DAC_MAX_RF;
+ for (i = 0; i < max_rf; i++)
+ dac->regfield[i].field =
+ regmap_field_alloc(dac->regmap,
+ dac->regfield[i].regfield);
+}
+
+static int sti_sas_init_sas_registers(struct device *dev,
+ struct sti_sas_codec_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->regfield[BIPHASE_ENABLE].field, 0);
+
+ /* Initialise bi-phase formatter idle value to 0 */
+ ret |= regmap_field_write(spdif->regfield[BIPHASE_IDLE].field, 0);
+ if (ret < 0) {
+ dev_err(dev, "Failed to update SPDIF registers");
+ return ret;
+ }
+
+ /* init DAC configuration */
+ if (data->chipid == CHIPID_STIH407) {
+ /* init configuration */
+ ret = regmap_field_write(
+ dac->regfield[STIH407_DAC_STANDBY].field, 1);
+ ret |= regmap_field_write(
+ dac->regfield[STIH407_DAC_STANDBY_ANA].field, 1);
+ ret |= regmap_field_write(
+ dac->regfield[STIH407_DAC_SOFTMUTE].field, 1);
+ } else if (data->chipid == CHIPID_STIH416) {
+ ret = regmap_field_write(
+ dac->regfield[STIH416_DAC_NOT_STANDBY].field, 0);
+ ret |= regmap_field_write(
+ dac->regfield[STIH416_DAC_ANALOG_PWR_DW].field, 0);
+ ret |= regmap_field_write(
+ dac->regfield[STIH416_DAC_ANALOG_NOT_PWR_DW_BG].field,
+ 0);
+ }
+
+ 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_codec_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 not declared in DT (%d)!\n",
+ __func__, (int)dac->rst);
+ return -EFAULT;
+ }
+ /* put the DAC into reset */
+ reset_control_assert(dac->rst);
+
+ return 0;
+}
+
+int stih416_sas_dac_startup(struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev);
+ struct sti_dac_audio *dac = &drvdata->dac;
+ int ret;
+
+ /* mute */
+ ret = regmap_field_write(
+ dac->regfield[STIH416_DAC_SOFTMUTE].field, 1);
+
+ /* Enable analog */
+ ret |= regmap_field_write(
+ dac->regfield[STIH416_DAC_ANALOG_NOT_PWR_DW_BG].field, 1);
+ ret |= regmap_field_write(
+ dac->regfield[STIH416_DAC_ANALOG_PWR_DW].field, 1);
+
+ /* Disable standby */
+ ret |= regmap_field_write(
+ dac->regfield[STIH416_DAC_NOT_STANDBY].field, 1);
+
+ /* Take the DAC out of reset */
+ reset_control_deassert(dac->rst);
+
+ return ret;
+}
+
+int stih407_sas_dac_startup(struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev);
+ struct sti_dac_audio *dac = &drvdata->dac;
+ int ret;
+
+ /* mute */
+ ret = regmap_field_write(
+ dac->regfield[STIH407_DAC_SOFTMUTE].field, 1);
+
+ /* Enable analog */
+ ret |= regmap_field_write(
+ dac->regfield[STIH407_DAC_STANDBY_ANA].field, 0);
+
+ /* Disable standby */
+ ret |= regmap_field_write(
+ dac->regfield[STIH407_DAC_STANDBY].field, 0);
+
+ return ret;
+}
+
+int sti_sas_dac_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev);
+ struct sti_dac_audio *dac = &drvdata->dac;
+
+ if (dac->ops->startup)
+ return dac->ops->startup(dai);
+
+ return 0;
+}
+
+void stih416_sas_dac_shutdown(struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev);
+ struct sti_dac_audio *dac = &drvdata->dac;
+ int ret;
+
+ /* put the DAC into reset */
+ reset_control_assert(dac->rst);
+
+ /* Enable standby */
+ ret = regmap_field_write(
+ dac->regfield[STIH416_DAC_NOT_STANDBY].field, 0);
+
+ /* Disable analog */
+ ret |= regmap_field_write(
+ dac->regfield[STIH416_DAC_ANALOG_PWR_DW].field, 0);
+ ret |= regmap_field_write(
+ dac->regfield[STIH416_DAC_ANALOG_NOT_PWR_DW_BG].field, 0);
+ if (ret)
+ dev_err(codec->dev, "error while updating DAC registers\n");
+}
+
+static void stih407_sas_dac_shutdown(struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev);
+ struct sti_dac_audio *dac = &drvdata->dac;
+ int ret;
+
+ /* Enable standby */
+ ret = regmap_field_write(
+ dac->regfield[STIH407_DAC_STANDBY].field, 1);
+
+ /* Disable analog */
+ ret |= regmap_field_write(
+ dac->regfield[STIH407_DAC_STANDBY_ANA].field, 1);
+ if (ret)
+ dev_err(codec->dev, "error while updating DAC registers\n");
+}
+
+static void sti_sas_dac_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev);
+ struct sti_dac_audio *dac = &drvdata->dac;
+
+ if (dac->ops->shutdown)
+ dac->ops->shutdown(dai);
+}
+
+static int stih416_sas_dac_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev);
+ struct sti_dac_audio *dac = &drvdata->dac;
+
+ if (mute)
+ return regmap_field_write(
+ dac->regfield[STIH416_DAC_SOFTMUTE].field, 1);
+ else
+ return regmap_field_write(
+ dac->regfield[STIH416_DAC_SOFTMUTE].field, 0);
+}
+
+static int stih407_sas_dac_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev);
+ struct sti_dac_audio *dac = &drvdata->dac;
+
+ if (mute)
+ return regmap_field_write(
+ dac->regfield[STIH407_DAC_SOFTMUTE].field, 1);
+ else
+ return regmap_field_write(
+ dac->regfield[STIH407_DAC_SOFTMUTE].field, 0);
+}
+
+int sti_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev);
+ struct sti_dac_audio *dac = &drvdata->dac;
+
+ if (dac->ops->mute)
+ return dac->ops->mute(dai, mute);
+
+ return 0;
+}
+
+struct sti_codec_ops stih416_ops = {
+ .dai_dac_probe = stih416_dac_probe,
+ .mute = stih416_sas_dac_mute,
+ .startup = stih416_sas_dac_startup,
+ .shutdown = stih416_sas_dac_shutdown,
+};
+
+struct sti_codec_ops stih407_ops = {
+ .mute = stih407_sas_dac_mute,
+ .startup = stih407_sas_dac_startup,
+ .shutdown = stih407_sas_dac_shutdown,
+};
+
+/*
+ * 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_codec_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->regfield[BIPHASE_ENABLE].field,
+ 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->regfield[BIPHASE_ENABLE].field,
+ 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 ret, div;
+
+ div = sti_sas_dai_clk_div[dai->id];
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, SND_SOC_CLOCK_OUT, div);
+ if (ret)
+ dev_warn(codec->dev, "WARN: CPU DAI not support sysclk div");
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver sti_sas_codec_dai[] = {
+ {
+ .name = "sas-dai-spdif-out",
+ .id = STI_SAS_DAI_SPDIF_OUT,
+ .playback = {
+ .stream_name = "spdif_p",
+ .channels_min = STI_SPDIF_MIN_CHANNELS,
+ .channels_max = STI_SPDIF_MAX_CHANNELS,
+ .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,
+ .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 = STI_DAC_MIN_CHANNELS,
+ .channels_max = STI_DAC_MAX_CHANNELS,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = (struct snd_soc_dai_ops[]) {
+ {
+ .set_fmt = sti_sas_dac_set_fmt,
+ .startup = sti_sas_dac_startup,
+ .shutdown = sti_sas_dac_shutdown,
+ .mute_stream = sti_sas_dac_mute,
+ .hw_params = sti_sas_hw_params,
+ }
+ },
+ },
+};
+
+int sti_sas_dai_clk_div[ARRAY_SIZE(sti_sas_codec_dai)] = {
+ 128, /* spdif out */
+ 256, /* dac */
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int sti_sas_codec_suspend(struct snd_soc_codec *codec)
+{
+ /* Nothing done but need to be declared for PM management*/
+ return 0;
+}
+
+static int sti_sas_codec_resume(struct snd_soc_codec *codec)
+{
+ struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev);
+
+ return sti_sas_init_sas_registers(codec->dev, drvdata);
+}
+#else
+#define sti_sas_codec_suspend NULL
+#define sti_sas_codec_resume NULL
+#endif
+
+static struct snd_soc_codec_driver sti_sas_codec_driver = {
+ .suspend = sti_sas_codec_suspend,
+ .resume = sti_sas_codec_resume,
+};
+
+static int sti_sas_codec_driver_probe(struct platform_device *pdev)
+{
+ struct device_node *pnode = pdev->dev.of_node;
+ int status = 0;
+ struct sti_sas_codec_data *drvdata;
+
+ /* Allocate device structure */
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(struct sti_sas_codec_data),
+ GFP_KERNEL);
+
+ if (!drvdata) {
+ dev_err(&pdev->dev, "Failed to allocate device structure");
+ return -ENOMEM;
+ }
+ /* Populate data structure depending on compatibility */
+ if (!of_match_node(sti_sas_codec_drv_match, pnode)->data) {
+ dev_err(&pdev->dev, "data associated to device is missing");
+ return -EINVAL;
+ }
+
+ memcpy(drvdata, of_match_node(sti_sas_codec_drv_match, pnode)->data,
+ sizeof(struct sti_sas_codec_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;
+
+ sti_sas_map_sas_registers(drvdata);
+
+ status = sti_sas_init_sas_registers(&pdev->dev, drvdata);
+ if (status < 0)
+ return status;
+
+ /*Set DAC dai probe */
+ sti_sas_codec_dai[STI_SAS_DAI_ANALOG_OUT].probe =
+ drvdata->dac.ops->dai_dac_probe;
+
+ /*Store context */
+ dev_set_drvdata(&pdev->dev, drvdata);
+
+ status = snd_soc_register_codec(&pdev->dev, &sti_sas_codec_driver,
+ sti_sas_codec_dai,
+ ARRAY_SIZE(sti_sas_codec_dai));
+
+ return 0;
+}
+
+static int sti_sas_codec_driver_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+
+ return 0;
+}
+
+const struct sti_sas_codec_data stih416_data = {
+ .chipid = CHIPID_STIH416,
+ .dac.ops = &stih416_ops,
+ .dac.regfield = sti_sas_dac_stih416,
+ .spdif.regfield = sti_sas_spdif_stih416,
+};
+
+const struct sti_sas_codec_data stih407_data = {
+ .chipid = CHIPID_STIH407,
+ .dac.ops = &stih407_ops,
+ .dac.regfield = sti_sas_dac_stih407,
+ .spdif.regfield = sti_sas_spdif_stih407,
+};
+
+static const struct of_device_id sti_sas_codec_drv_match[] = {
+ {
+ .compatible = "st,stih416-sas-codec",
+ .data = &stih416_data,
+ },
+ {
+ .compatible = "st,stih407-sas-codec",
+ .data = &stih407_data,
+ },
+ {},
+};
+
+static struct platform_driver sti_sas_codec_platform_driver = {
+ .driver = {
+ .name = "sti-sas-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = sti_sas_codec_drv_match,
+ },
+ .probe = sti_sas_codec_driver_probe,
+ .remove = sti_sas_codec_driver_remove,
+};
+
+module_platform_driver(sti_sas_codec_platform_driver);
+
+MODULE_DESCRIPTION("audio codec for STMicroelectronics sti platforms");
+MODULE_AUTHOR("Arnaud.pouliquen at st.com");
+MODULE_LICENSE("GPL");
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