summaryrefslogtreecommitdiffstats
path: root/sound/soc/intel
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/intel')
-rw-r--r--sound/soc/intel/Kconfig9
-rw-r--r--sound/soc/intel/Makefile4
-rw-r--r--sound/soc/intel/byt-max98090.c203
-rw-r--r--sound/soc/intel/byt-rt5640.c37
-rw-r--r--sound/soc/intel/haswell.c15
-rw-r--r--sound/soc/intel/sst-acpi.c2
-rw-r--r--sound/soc/intel/sst-baytrail-dsp.c16
-rw-r--r--sound/soc/intel/sst-baytrail-ipc.c140
-rw-r--r--sound/soc/intel/sst-baytrail-ipc.h7
-rw-r--r--sound/soc/intel/sst-baytrail-pcm.c163
-rw-r--r--sound/soc/intel/sst-dsp-priv.h5
-rw-r--r--sound/soc/intel/sst-dsp.c1
-rw-r--r--sound/soc/intel/sst-dsp.h1
-rw-r--r--sound/soc/intel/sst-firmware.c67
-rw-r--r--sound/soc/intel/sst-haswell-dsp.c4
-rw-r--r--sound/soc/intel/sst-haswell-ipc.c53
-rw-r--r--sound/soc/intel/sst-haswell-ipc.h4
-rw-r--r--sound/soc/intel/sst-haswell-pcm.c116
-rw-r--r--sound/soc/intel/sst-mfld-dsp.h8
-rw-r--r--sound/soc/intel/sst-mfld-platform-compress.c237
-rw-r--r--sound/soc/intel/sst-mfld-platform-pcm.c (renamed from sound/soc/intel/sst-mfld-platform.c)250
-rw-r--r--sound/soc/intel/sst-mfld-platform.h11
22 files changed, 910 insertions, 443 deletions
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 3c81b38..c30fedb 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -49,3 +49,12 @@ config SND_SOC_INTEL_BYT_RT5640_MACH
help
This adds audio driver for Intel Baytrail platform based boards
with the RT5640 audio codec.
+
+config SND_SOC_INTEL_BYT_MAX98090_MACH
+ tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec"
+ depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C
+ select SND_SOC_INTEL_BAYTRAIL
+ select SND_SOC_MAX98090
+ help
+ This adds audio driver for Intel Baytrail platform based boards
+ with the MAX98090 audio codec.
diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile
index edeb79ae..4bfca79 100644
--- a/sound/soc/intel/Makefile
+++ b/sound/soc/intel/Makefile
@@ -2,7 +2,7 @@
snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
snd-soc-sst-acpi-objs := sst-acpi.o
-snd-soc-sst-mfld-platform-objs := sst-mfld-platform.o
+snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o sst-mfld-platform-compress.o
snd-soc-mfld-machine-objs := mfld_machine.o
obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o
@@ -23,6 +23,8 @@ obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o
# Machine support
snd-soc-sst-haswell-objs := haswell.o
snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
+snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
diff --git a/sound/soc/intel/byt-max98090.c b/sound/soc/intel/byt-max98090.c
new file mode 100644
index 0000000..5fc98c6
--- /dev/null
+++ b/sound/soc/intel/byt-max98090.c
@@ -0,0 +1,203 @@
+/*
+ * Intel Baytrail SST MAX98090 machine driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/acpi.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../codecs/max98090.h"
+
+struct byt_max98090_private {
+ struct snd_soc_jack jack;
+};
+
+static const struct snd_soc_dapm_widget byt_max98090_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route byt_max98090_audio_map[] = {
+ {"IN34", NULL, "Headset Mic"},
+ {"IN34", NULL, "MICBIAS"},
+ {"MICBIAS", NULL, "Headset Mic"},
+ {"DMICL", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPL"},
+ {"Headphone", NULL, "HPR"},
+ {"Ext Spk", NULL, "SPKL"},
+ {"Ext Spk", NULL, "SPKR"},
+};
+
+static const struct snd_kcontrol_new byt_max98090_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Ext Spk",
+ .mask = SND_JACK_LINEOUT,
+ },
+ {
+ .pin = "Int Mic",
+ .mask = SND_JACK_LINEIN,
+ },
+};
+
+static struct snd_soc_jack_gpio hs_jack_gpios[] = {
+ {
+ .name = "hp-gpio",
+ .idx = 0,
+ .report = SND_JACK_HEADPHONE | SND_JACK_LINEOUT,
+ .debounce_time = 200,
+ },
+ {
+ .name = "mic-gpio",
+ .idx = 1,
+ .report = SND_JACK_MICROPHONE | SND_JACK_LINEIN,
+ .debounce_time = 200,
+ },
+};
+
+static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime)
+{
+ int ret;
+ struct snd_soc_codec *codec = runtime->codec;
+ struct snd_soc_card *card = runtime->card;
+ struct byt_max98090_private *drv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_jack *jack = &drv->jack;
+
+ card->dapm.idle_bias_off = true;
+
+ ret = snd_soc_dai_set_sysclk(runtime->codec_dai,
+ M98090_REG_SYSTEM_CLOCK,
+ 25000000, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "Can't set codec clock %d\n", ret);
+ return ret;
+ }
+
+ /* Enable jack detection */
+ ret = snd_soc_jack_new(codec, "Headphone", SND_JACK_HEADPHONE, jack);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_jack_add_pins(jack, ARRAY_SIZE(hs_jack_pins),
+ hs_jack_pins);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_jack_add_gpiods(card->dev->parent, jack,
+ ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
+ if (ret)
+ return ret;
+
+ return max98090_mic_detect(codec, jack);
+}
+
+static struct snd_soc_dai_link byt_max98090_dais[] = {
+ {
+ .name = "Baytrail Audio",
+ .stream_name = "Audio",
+ .cpu_dai_name = "baytrail-pcm-audio",
+ .codec_dai_name = "HiFi",
+ .codec_name = "i2c-193C9890:00",
+ .platform_name = "baytrail-pcm-audio",
+ .init = byt_max98090_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ },
+};
+
+static struct snd_soc_card byt_max98090_card = {
+ .name = "byt-max98090",
+ .dai_link = byt_max98090_dais,
+ .num_links = ARRAY_SIZE(byt_max98090_dais),
+ .dapm_widgets = byt_max98090_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(byt_max98090_widgets),
+ .dapm_routes = byt_max98090_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(byt_max98090_audio_map),
+ .controls = byt_max98090_controls,
+ .num_controls = ARRAY_SIZE(byt_max98090_controls),
+};
+
+static int byt_max98090_probe(struct platform_device *pdev)
+{
+ int ret_val = 0;
+ struct byt_max98090_private *priv;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
+ if (!priv) {
+ dev_err(&pdev->dev, "allocation failed\n");
+ return -ENOMEM;
+ }
+
+ byt_max98090_card.dev = &pdev->dev;
+ snd_soc_card_set_drvdata(&byt_max98090_card, priv);
+ ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_max98090_card);
+ if (ret_val) {
+ dev_err(&pdev->dev,
+ "snd_soc_register_card failed %d\n", ret_val);
+ return ret_val;
+ }
+
+ return ret_val;
+}
+
+static int byt_max98090_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct byt_max98090_private *priv = snd_soc_card_get_drvdata(card);
+
+ snd_soc_jack_free_gpios(&priv->jack, ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
+
+ return 0;
+}
+
+static struct platform_driver byt_max98090_driver = {
+ .probe = byt_max98090_probe,
+ .remove = byt_max98090_remove,
+ .driver = {
+ .name = "byt-max98090",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+};
+module_platform_driver(byt_max98090_driver)
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver");
+MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:byt-max98090");
diff --git a/sound/soc/intel/byt-rt5640.c b/sound/soc/intel/byt-rt5640.c
index eff97c8..53d160d 100644
--- a/sound/soc/intel/byt-rt5640.c
+++ b/sound/soc/intel/byt-rt5640.c
@@ -100,12 +100,6 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
snd_soc_dapm_ignore_suspend(dapm, "SPORP");
snd_soc_dapm_ignore_suspend(dapm, "SPORN");
- snd_soc_dapm_enable_pin(dapm, "Headset Mic");
- snd_soc_dapm_enable_pin(dapm, "Headphone");
- snd_soc_dapm_enable_pin(dapm, "Speaker");
- snd_soc_dapm_enable_pin(dapm, "Internal Mic");
-
- snd_soc_dapm_sync(dapm);
return ret;
}
@@ -117,27 +111,13 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
{
.name = "Baytrail Audio",
.stream_name = "Audio",
- .cpu_dai_name = "Front-cpu-dai",
+ .cpu_dai_name = "baytrail-pcm-audio",
.codec_dai_name = "rt5640-aif1",
.codec_name = "i2c-10EC5640:00",
.platform_name = "baytrail-pcm-audio",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.init = byt_rt5640_init,
- .ignore_suspend = 1,
- .ops = &byt_rt5640_ops,
- },
- {
- .name = "Baytrail Voice",
- .stream_name = "Voice",
- .cpu_dai_name = "Mic1-cpu-dai",
- .codec_dai_name = "rt5640-aif1",
- .codec_name = "i2c-10EC5640:00",
- .platform_name = "baytrail-pcm-audio",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .init = NULL,
- .ignore_suspend = 1,
.ops = &byt_rt5640_ops,
},
};
@@ -155,28 +135,17 @@ static struct snd_soc_card byt_rt5640_card = {
static int byt_rt5640_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &byt_rt5640_card;
- struct device *dev = &pdev->dev;
card->dev = &pdev->dev;
- dev_set_drvdata(dev, card);
- return snd_soc_register_card(card);
-}
-
-static int byt_rt5640_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
-
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, card);
}
static struct platform_driver byt_rt5640_audio = {
.probe = byt_rt5640_probe,
- .remove = byt_rt5640_remove,
.driver = {
.name = "byt-rt5640",
.owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
},
};
module_platform_driver(byt_rt5640_audio)
diff --git a/sound/soc/intel/haswell.c b/sound/soc/intel/haswell.c
index 54345a2..3981982 100644
--- a/sound/soc/intel/haswell.c
+++ b/sound/soc/intel/haswell.c
@@ -89,8 +89,6 @@ static struct snd_soc_ops haswell_rt5640_ops = {
static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev);
struct sst_hsw *haswell = pdata->dsp;
int ret;
@@ -104,10 +102,6 @@ static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
- /* always connected */
- snd_soc_dapm_enable_pin(dapm, "Headphones");
- snd_soc_dapm_enable_pin(dapm, "Mic");
-
return 0;
}
@@ -208,18 +202,11 @@ static int haswell_audio_probe(struct platform_device *pdev)
{
haswell_rt5640.dev = &pdev->dev;
- return snd_soc_register_card(&haswell_rt5640);
-}
-
-static int haswell_audio_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_card(&haswell_rt5640);
- return 0;
+ return devm_snd_soc_register_card(&pdev->dev, &haswell_rt5640);
}
static struct platform_driver haswell_audio = {
.probe = haswell_audio_probe,
- .remove = haswell_audio_remove,
.driver = {
.name = "haswell-audio",
.owner = THIS_MODULE,
diff --git a/sound/soc/intel/sst-acpi.c b/sound/soc/intel/sst-acpi.c
index 5d06eec..42edc6f 100644
--- a/sound/soc/intel/sst-acpi.c
+++ b/sound/soc/intel/sst-acpi.c
@@ -138,6 +138,7 @@ static int sst_acpi_probe(struct platform_device *pdev)
sst_pdata = &sst_acpi->sst_pdata;
sst_pdata->id = desc->sst_id;
+ sst_pdata->dma_dev = dev;
sst_acpi->desc = desc;
sst_acpi->mach = mach;
@@ -246,6 +247,7 @@ static struct sst_acpi_desc sst_acpi_broadwell_desc = {
static struct sst_acpi_mach baytrail_machines[] = {
{ "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-i2s_master" },
+ { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-i2s_master" },
{}
};
diff --git a/sound/soc/intel/sst-baytrail-dsp.c b/sound/soc/intel/sst-baytrail-dsp.c
index a50bf7f..fc58876 100644
--- a/sound/soc/intel/sst-baytrail-dsp.c
+++ b/sound/soc/intel/sst-baytrail-dsp.c
@@ -214,6 +214,13 @@ static void sst_byt_boot(struct sst_dsp *sst)
{
int tries = 10;
+ /*
+ * save the physical address of extended firmware block in the first
+ * 4 bytes of the mailbox
+ */
+ memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET,
+ &sst->pdata->fw_base, sizeof(u32));
+
/* release stall and wait to unstall */
sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_STALL, 0x0);
while (tries--) {
@@ -317,14 +324,7 @@ static int sst_byt_init(struct sst_dsp *sst, struct sst_pdata *pdata)
return ret;
}
- /*
- * save the physical address of extended firmware block in the first
- * 4 bytes of the mailbox
- */
- memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET,
- &pdata->fw_base, sizeof(u32));
-
- ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ ret = dma_coerce_mask_and_coherent(sst->dma_dev, DMA_BIT_MASK(32));
if (ret)
return ret;
diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/sst-baytrail-ipc.c
index d0eaeee..d207b22 100644
--- a/sound/soc/intel/sst-baytrail-ipc.c
+++ b/sound/soc/intel/sst-baytrail-ipc.c
@@ -22,7 +22,6 @@
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/kthread.h>
#include <linux/firmware.h>
@@ -173,6 +172,7 @@ struct sst_byt {
/* boot */
wait_queue_head_t boot_wait;
bool boot_complete;
+ struct sst_fw *fw;
/* IPC messaging */
struct list_head tx_list;
@@ -299,6 +299,24 @@ static inline void sst_byt_tx_msg_reply_complete(struct sst_byt *byt,
wake_up(&msg->waitq);
}
+static void sst_byt_drop_all(struct sst_byt *byt)
+{
+ struct ipc_message *msg, *tmp;
+ unsigned long flags;
+
+ /* drop all TX and Rx messages before we stall + reset DSP */
+ spin_lock_irqsave(&byt->dsp->spinlock, flags);
+ list_for_each_entry_safe(msg, tmp, &byt->tx_list, list) {
+ list_move(&msg->list, &byt->empty_list);
+ }
+
+ list_for_each_entry_safe(msg, tmp, &byt->rx_list, list) {
+ list_move(&msg->list, &byt->empty_list);
+ }
+
+ spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
+}
+
static int sst_byt_tx_wait_done(struct sst_byt *byt, struct ipc_message *msg,
void *rx_data)
{
@@ -542,16 +560,20 @@ struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id,
void *data)
{
struct sst_byt_stream *stream;
+ struct sst_dsp *sst = byt->dsp;
+ unsigned long flags;
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
if (stream == NULL)
return NULL;
+ spin_lock_irqsave(&sst->spinlock, flags);
list_add(&stream->node, &byt->stream_list);
stream->notify_position = notify_position;
stream->pdata = data;
stream->byt = byt;
stream->str_id = id;
+ spin_unlock_irqrestore(&sst->spinlock, flags);
return stream;
}
@@ -630,6 +652,8 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
{
u64 header;
int ret = 0;
+ struct sst_dsp *sst = byt->dsp;
+ unsigned long flags;
if (!stream->commited)
goto out;
@@ -644,8 +668,10 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
stream->commited = false;
out:
+ spin_lock_irqsave(&sst->spinlock, flags);
list_del(&stream->node);
kfree(stream);
+ spin_unlock_irqrestore(&sst->spinlock, flags);
return ret;
}
@@ -653,36 +679,33 @@ out:
static int sst_byt_stream_operations(struct sst_byt *byt, int type,
int stream_id, int wait)
{
- struct sst_byt_start_stream_params start_stream;
u64 header;
- void *tx_msg = NULL;
- size_t size = 0;
-
- if (type != IPC_IA_START_STREAM) {
- header = sst_byt_header(type, 0, false, stream_id);
- } else {
- start_stream.byte_offset = 0;
- header = sst_byt_header(IPC_IA_START_STREAM,
- sizeof(start_stream) + sizeof(u32),
- true, stream_id);
- tx_msg = &start_stream;
- size = sizeof(start_stream);
- }
+ header = sst_byt_header(type, 0, false, stream_id);
if (wait)
- return sst_byt_ipc_tx_msg_wait(byt, header,
- tx_msg, size, NULL, 0);
+ return sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0);
else
- return sst_byt_ipc_tx_msg_nowait(byt, header, tx_msg, size);
+ return sst_byt_ipc_tx_msg_nowait(byt, header, NULL, 0);
}
/* stream ALSA trigger operations */
-int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream)
+int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream,
+ u32 start_offset)
{
+ struct sst_byt_start_stream_params start_stream;
+ void *tx_msg;
+ size_t size;
+ u64 header;
int ret;
- ret = sst_byt_stream_operations(byt, IPC_IA_START_STREAM,
- stream->str_id, 0);
+ start_stream.byte_offset = start_offset;
+ header = sst_byt_header(IPC_IA_START_STREAM,
+ sizeof(start_stream) + sizeof(u32),
+ true, stream->str_id);
+ tx_msg = &start_stream;
+ size = sizeof(start_stream);
+
+ ret = sst_byt_ipc_tx_msg_nowait(byt, header, tx_msg, size);
if (ret < 0)
dev_err(byt->dev, "ipc: error failed to start stream %d\n",
stream->str_id);
@@ -774,6 +797,73 @@ static struct sst_dsp_device byt_dev = {
.ops = &sst_byt_ops,
};
+int sst_byt_dsp_suspend_noirq(struct device *dev, struct sst_pdata *pdata)
+{
+ struct sst_byt *byt = pdata->dsp;
+
+ dev_dbg(byt->dev, "dsp reset\n");
+ sst_dsp_reset(byt->dsp);
+ sst_byt_drop_all(byt);
+ dev_dbg(byt->dev, "dsp in reset\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_noirq);
+
+int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata)
+{
+ struct sst_byt *byt = pdata->dsp;
+
+ dev_dbg(byt->dev, "free all blocks and unload fw\n");
+ sst_fw_unload(byt->fw);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_late);
+
+int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata)
+{
+ struct sst_byt *byt = pdata->dsp;
+ int ret;
+
+ dev_dbg(byt->dev, "reload dsp fw\n");
+
+ sst_dsp_reset(byt->dsp);
+
+ ret = sst_fw_reload(byt->fw);
+ if (ret < 0) {
+ dev_err(dev, "error: failed to reload firmware\n");
+ return ret;
+ }
+
+ /* wait for DSP boot completion */
+ byt->boot_complete = false;
+ sst_dsp_boot(byt->dsp);
+ dev_dbg(byt->dev, "dsp booting...\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_byt_dsp_boot);
+
+int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata)
+{
+ struct sst_byt *byt = pdata->dsp;
+ int err;
+
+ dev_dbg(byt->dev, "wait for dsp reboot\n");
+
+ err = wait_event_timeout(byt->boot_wait, byt->boot_complete,
+ msecs_to_jiffies(IPC_BOOT_MSECS));
+ if (err == 0) {
+ dev_err(byt->dev, "ipc: error DSP boot timeout\n");
+ return -EIO;
+ }
+
+ dev_dbg(byt->dev, "dsp rebooted\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready);
+
int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
{
struct sst_byt *byt;
@@ -801,7 +891,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
/* start the IPC message thread */
init_kthread_worker(&byt->kworker);
byt->tx_thread = kthread_run(kthread_worker_fn,
- &byt->kworker,
+ &byt->kworker, "%s",
dev_name(byt->dev));
if (IS_ERR(byt->tx_thread)) {
err = PTR_ERR(byt->tx_thread);
@@ -816,7 +906,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
byt->dsp = sst_dsp_new(dev, &byt_dev, pdata);
if (byt->dsp == NULL) {
err = -ENODEV;
- goto err_free_msg;
+ goto dsp_err;
}
/* keep the DSP in reset state for base FW loading */
@@ -840,6 +930,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
}
pdata->dsp = byt;
+ byt->fw = byt_sst_fw;
return 0;
@@ -848,6 +939,8 @@ boot_err:
sst_fw_free(byt_sst_fw);
fw_err:
sst_dsp_free(byt->dsp);
+dsp_err:
+ kthread_stop(byt->tx_thread);
err_free_msg:
kfree(byt->msg);
@@ -862,6 +955,7 @@ void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata)
sst_dsp_reset(byt->dsp);
sst_fw_free_all(byt->dsp);
sst_dsp_free(byt->dsp);
+ kthread_stop(byt->tx_thread);
kfree(byt->msg);
}
EXPORT_SYMBOL_GPL(sst_byt_dsp_free);
diff --git a/sound/soc/intel/sst-baytrail-ipc.h b/sound/soc/intel/sst-baytrail-ipc.h
index f172b64..06a4d20 100644
--- a/sound/soc/intel/sst-baytrail-ipc.h
+++ b/sound/soc/intel/sst-baytrail-ipc.h
@@ -53,7 +53,8 @@ int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream);
int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream);
/* stream ALSA trigger operations */
-int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream);
+int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream,
+ u32 start_offset);
int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream);
int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream);
int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream);
@@ -65,5 +66,9 @@ int sst_byt_get_dsp_position(struct sst_byt *byt,
int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata);
void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata);
struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt);
+int sst_byt_dsp_suspend_noirq(struct device *dev, struct sst_pdata *pdata);
+int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata);
+int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata);
+int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata);
#endif
diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/sst-baytrail-pcm.c
index 6d101f3..8eab973 100644
--- a/sound/soc/intel/sst-baytrail-pcm.c
+++ b/sound/soc/intel/sst-baytrail-pcm.c
@@ -45,6 +45,11 @@ struct sst_byt_pcm_data {
struct sst_byt_stream *stream;
struct snd_pcm_substream *substream;
struct mutex mutex;
+
+ /* latest DSP DMA hw pointer */
+ u32 hw_ptr;
+
+ struct work_struct work;
};
/* private data for the driver */
@@ -63,7 +68,7 @@ static int sst_byt_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct sst_byt_priv_data *pdata =
snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+ struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
struct sst_byt *byt = pdata->byt;
u32 rate, bits;
u8 channels;
@@ -130,21 +135,57 @@ static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream)
return 0;
}
+static int sst_byt_pcm_restore_stream_context(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sst_byt_priv_data *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
+ struct sst_byt *byt = pdata->byt;
+ int ret;
+
+ /* commit stream using existing stream params */
+ ret = sst_byt_stream_commit(byt, pcm_data->stream);
+ if (ret < 0) {
+ dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret);
+ return ret;
+ }
+
+ sst_byt_stream_start(byt, pcm_data->stream, pcm_data->hw_ptr);
+
+ dev_dbg(rtd->dev, "stream context restored at offset %d\n",
+ pcm_data->hw_ptr);
+
+ return 0;
+}
+
+static void sst_byt_pcm_work(struct work_struct *work)
+{
+ struct sst_byt_pcm_data *pcm_data =
+ container_of(work, struct sst_byt_pcm_data, work);
+
+ if (snd_pcm_running(pcm_data->substream))
+ sst_byt_pcm_restore_stream_context(pcm_data->substream);
+}
+
static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct sst_byt_priv_data *pdata =
snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+ struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
struct sst_byt *byt = pdata->byt;
dev_dbg(rtd->dev, "PCM: trigger %d\n", cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- sst_byt_stream_start(byt, pcm_data->stream);
+ pcm_data->hw_ptr = 0;
+ sst_byt_stream_start(byt, pcm_data->stream, 0);
break;
case SNDRV_PCM_TRIGGER_RESUME:
+ schedule_work(&pcm_data->work);
+ break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
sst_byt_stream_resume(byt, pcm_data->stream);
break;
@@ -168,13 +209,19 @@ static u32 byt_notify_pointer(struct sst_byt_stream *stream, void *data)
struct snd_pcm_substream *substream = pcm_data->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- u32 pos;
+ struct sst_byt_priv_data *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct sst_byt *byt = pdata->byt;
+ u32 pos, hw_pos;
+ hw_pos = sst_byt_get_dsp_position(byt, pcm_data->stream,
+ snd_pcm_lib_buffer_bytes(substream));
+ pcm_data->hw_ptr = hw_pos;
pos = frames_to_bytes(runtime,
(runtime->control->appl_ptr %
runtime->buffer_size));
- dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
+ dev_dbg(rtd->dev, "PCM: App/DMA pointer %u/%u bytes\n", pos, hw_pos);
snd_pcm_period_elapsed(substream);
return pos;
@@ -186,18 +233,11 @@ static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream
struct snd_pcm_runtime *runtime = substream->runtime;
struct sst_byt_priv_data *pdata =
snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
- struct sst_byt *byt = pdata->byt;
- snd_pcm_uframes_t offset;
- int pos;
+ struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
- pos = sst_byt_get_dsp_position(byt, pcm_data->stream,
- snd_pcm_lib_buffer_bytes(substream));
- offset = bytes_to_frames(runtime, pos);
+ dev_dbg(rtd->dev, "PCM: DMA pointer %u bytes\n", pcm_data->hw_ptr);
- dev_dbg(rtd->dev, "PCM: DMA pointer %zu bytes\n",
- frames_to_bytes(runtime, (u32)offset));
- return offset;
+ return bytes_to_frames(runtime, pcm_data->hw_ptr);
}
static int sst_byt_pcm_open(struct snd_pcm_substream *substream)
@@ -205,20 +245,18 @@ static int sst_byt_pcm_open(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct sst_byt_priv_data *pdata =
snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+ struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
struct sst_byt *byt = pdata->byt;
dev_dbg(rtd->dev, "PCM: open\n");
- pcm_data = &pdata->pcm[rtd->cpu_dai->id];
mutex_lock(&pcm_data->mutex);
- snd_soc_pcm_set_drvdata(rtd, pcm_data);
pcm_data->substream = substream;
snd_soc_set_runtime_hwparams(substream, &sst_byt_pcm_hardware);
- pcm_data->stream = sst_byt_stream_new(byt, rtd->cpu_dai->id + 1,
+ pcm_data->stream = sst_byt_stream_new(byt, substream->stream + 1,
byt_notify_pointer, pcm_data);
if (pcm_data->stream == NULL) {
dev_err(rtd->dev, "failed to create stream\n");
@@ -235,12 +273,13 @@ static int sst_byt_pcm_close(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct sst_byt_priv_data *pdata =
snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+ struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
struct sst_byt *byt = pdata->byt;
int ret;
dev_dbg(rtd->dev, "PCM: close\n");
+ cancel_work_sync(&pcm_data->work);
mutex_lock(&pcm_data->mutex);
ret = sst_byt_stream_free(byt, pcm_data->stream);
if (ret < 0) {
@@ -283,18 +322,16 @@ static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
size_t size;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct sst_pdata *pdata = dev_get_platdata(platform->dev);
int ret = 0;
- ret = dma_coerce_mask_and_coherent(rtd->card->dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
size = sst_byt_pcm_hardware.buffer_bytes_max;
ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_DEV,
- rtd->card->dev,
+ pdata->dma_dev,
size, size);
if (ret) {
dev_err(rtd->dev, "dma buffer allocation failed %d\n",
@@ -308,7 +345,7 @@ static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd)
static struct snd_soc_dai_driver byt_dais[] = {
{
- .name = "Front-cpu-dai",
+ .name = "Baytrail PCM",
.playback = {
.stream_name = "System Playback",
.channels_min = 2,
@@ -317,9 +354,6 @@ static struct snd_soc_dai_driver byt_dais[] = {
.formats = SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_S16_LE,
},
- },
- {
- .name = "Mic1-cpu-dai",
.capture = {
.stream_name = "Analog Capture",
.channels_min = 2,
@@ -344,8 +378,10 @@ static int sst_byt_pcm_probe(struct snd_soc_platform *platform)
priv_data->byt = plat_data->dsp;
snd_soc_platform_set_drvdata(platform, priv_data);
- for (i = 0; i < ARRAY_SIZE(byt_dais); i++)
+ for (i = 0; i < BYT_PCM_COUNT; i++) {
mutex_init(&priv_data->pcm[i].mutex);
+ INIT_WORK(&priv_data->pcm[i].work, sst_byt_pcm_work);
+ }
return 0;
}
@@ -367,6 +403,72 @@ static const struct snd_soc_component_driver byt_dai_component = {
.name = "byt-dai",
};
+#ifdef CONFIG_PM
+static int sst_byt_pcm_dev_suspend_noirq(struct device *dev)
+{
+ struct sst_pdata *sst_pdata = dev_get_platdata(dev);
+ int ret;
+
+ dev_dbg(dev, "suspending noirq\n");
+
+ /* at this point all streams will be stopped and context saved */
+ ret = sst_byt_dsp_suspend_noirq(dev, sst_pdata);
+ if (ret < 0) {
+ dev_err(dev, "failed to suspend %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int sst_byt_pcm_dev_suspend_late(struct device *dev)
+{
+ struct sst_pdata *sst_pdata = dev_get_platdata(dev);
+ int ret;
+
+ dev_dbg(dev, "suspending late\n");
+
+ ret = sst_byt_dsp_suspend_late(dev, sst_pdata);
+ if (ret < 0) {
+ dev_err(dev, "failed to suspend %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int sst_byt_pcm_dev_resume_early(struct device *dev)
+{
+ struct sst_pdata *sst_pdata = dev_get_platdata(dev);
+
+ dev_dbg(dev, "resume early\n");
+
+ /* load fw and boot DSP */
+ return sst_byt_dsp_boot(dev, sst_pdata);
+}
+
+static int sst_byt_pcm_dev_resume(struct device *dev)
+{
+ struct sst_pdata *sst_pdata = dev_get_platdata(dev);
+
+ dev_dbg(dev, "resume\n");
+
+ /* wait for FW to finish booting */
+ return sst_byt_dsp_wait_for_ready(dev, sst_pdata);
+}
+
+static const struct dev_pm_ops sst_byt_pm_ops = {
+ .suspend_noirq = sst_byt_pcm_dev_suspend_noirq,
+ .suspend_late = sst_byt_pcm_dev_suspend_late,
+ .resume_early = sst_byt_pcm_dev_resume_early,
+ .resume = sst_byt_pcm_dev_resume,
+};
+
+#define SST_BYT_PM_OPS (&sst_byt_pm_ops)
+#else
+#define SST_BYT_PM_OPS NULL
+#endif
+
static int sst_byt_pcm_dev_probe(struct platform_device *pdev)
{
struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
@@ -409,6 +511,7 @@ static struct platform_driver sst_byt_pcm_driver = {
.driver = {
.name = "baytrail-pcm-audio",
.owner = THIS_MODULE,
+ .pm = SST_BYT_PM_OPS,
},
.probe = sst_byt_pcm_dev_probe,
diff --git a/sound/soc/intel/sst-dsp-priv.h b/sound/soc/intel/sst-dsp-priv.h
index fe8e81aa..ffb308b 100644
--- a/sound/soc/intel/sst-dsp-priv.h
+++ b/sound/soc/intel/sst-dsp-priv.h
@@ -136,7 +136,7 @@ struct sst_module_data {
enum sst_data_type data_type; /* type of module data */
u32 size; /* size in bytes */
- u32 offset; /* offset in FW file */
+ int32_t offset; /* offset in FW file */
u32 data_offset; /* offset in ADSP memory space */
void *data; /* module data */
};
@@ -228,6 +228,7 @@ struct sst_dsp {
spinlock_t spinlock; /* IPC locking */
struct mutex mutex; /* DSP FW lock */
struct device *dev;
+ struct device *dma_dev;
void *thread_context;
int irq;
u32 id;
@@ -283,6 +284,8 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
const struct firmware *fw, void *private);
void sst_fw_free(struct sst_fw *sst_fw);
void sst_fw_free_all(struct sst_dsp *dsp);
+int sst_fw_reload(struct sst_fw *sst_fw);
+void sst_fw_unload(struct sst_fw *sst_fw);
/* Create/Free firmware modules */
struct sst_module *sst_module_new(struct sst_fw *sst_fw,
diff --git a/sound/soc/intel/sst-dsp.c b/sound/soc/intel/sst-dsp.c
index 0c129fd..0b715b2 100644
--- a/sound/soc/intel/sst-dsp.c
+++ b/sound/soc/intel/sst-dsp.c
@@ -337,6 +337,7 @@ struct sst_dsp *sst_dsp_new(struct device *dev,
spin_lock_init(&sst->spinlock);
mutex_init(&sst->mutex);
sst->dev = dev;
+ sst->dma_dev = pdata->dma_dev;
sst->thread_context = sst_dev->thread_context;
sst->sst_dev = sst_dev;
sst->id = pdata->id;
diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/sst-dsp.h
index 74052b5..e44423b 100644
--- a/sound/soc/intel/sst-dsp.h
+++ b/sound/soc/intel/sst-dsp.h
@@ -169,6 +169,7 @@ struct sst_pdata {
u32 dma_base;
u32 dma_size;
int dma_engine;
+ struct device *dma_dev;
/* DSP */
u32 id;
diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c
index f768710..3bb43da 100644
--- a/sound/soc/intel/sst-firmware.c
+++ b/sound/soc/intel/sst-firmware.c
@@ -30,6 +30,8 @@
#include "sst-dsp.h"
#include "sst-dsp-priv.h"
+static void block_module_remove(struct sst_module *module);
+
static void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
{
u32 i;
@@ -57,14 +59,8 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
sst_fw->private = private;
sst_fw->size = fw->size;
- err = dma_coerce_mask_and_coherent(dsp->dev, DMA_BIT_MASK(32));
- if (err < 0) {
- kfree(sst_fw);
- return NULL;
- }
-
/* allocate DMA buffer to store FW data */
- sst_fw->dma_buf = dma_alloc_coherent(dsp->dev, sst_fw->size,
+ sst_fw->dma_buf = dma_alloc_coherent(dsp->dma_dev, sst_fw->size,
&sst_fw->dmable_fw_paddr, GFP_DMA | GFP_KERNEL);
if (!sst_fw->dma_buf) {
dev_err(dsp->dev, "error: DMA alloc failed\n");
@@ -97,6 +93,42 @@ parse_err:
}
EXPORT_SYMBOL_GPL(sst_fw_new);
+int sst_fw_reload(struct sst_fw *sst_fw)
+{
+ struct sst_dsp *dsp = sst_fw->dsp;
+ int ret;
+
+ dev_dbg(dsp->dev, "reloading firmware\n");
+
+ /* call core specific FW paser to load FW data into DSP */
+ ret = dsp->ops->parse_fw(sst_fw);
+ if (ret < 0)
+ dev_err(dsp->dev, "error: parse fw failed %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_fw_reload);
+
+void sst_fw_unload(struct sst_fw *sst_fw)
+{
+ struct sst_dsp *dsp = sst_fw->dsp;
+ struct sst_module *module, *tmp;
+
+ dev_dbg(dsp->dev, "unloading firmware\n");
+
+ mutex_lock(&dsp->mutex);
+ list_for_each_entry_safe(module, tmp, &dsp->module_list, list) {
+ if (module->sst_fw == sst_fw) {
+ block_module_remove(module);
+ list_del(&module->list);
+ kfree(module);
+ }
+ }
+
+ mutex_unlock(&dsp->mutex);
+}
+EXPORT_SYMBOL_GPL(sst_fw_unload);
+
/* free single firmware object */
void sst_fw_free(struct sst_fw *sst_fw)
{
@@ -106,7 +138,7 @@ void sst_fw_free(struct sst_fw *sst_fw)
list_del(&sst_fw->list);
mutex_unlock(&dsp->mutex);
- dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf,
+ dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf,
sst_fw->dmable_fw_paddr);
kfree(sst_fw);
}
@@ -202,6 +234,9 @@ static int block_alloc_contiguous(struct sst_module *module,
size -= block->size;
}
+ list_for_each_entry(block, &tmp, list)
+ list_add(&block->module_list, &module->block_list);
+
list_splice(&tmp, &dsp->used_block_list);
return 0;
}
@@ -247,8 +282,7 @@ static int block_alloc(struct sst_module *module,
/* do we span > 1 blocks */
if (data->size > block->size) {
ret = block_alloc_contiguous(module, data,
- block->offset + block->size,
- data->size - block->size);
+ block->offset, data->size);
if (ret == 0)
return ret;
}
@@ -344,7 +378,7 @@ static int block_alloc_fixed(struct sst_module *module,
err = block_alloc_contiguous(module, data,
block->offset + block->size,
- data->size - block->size + data->offset - block->offset);
+ data->size - block->size);
if (err < 0)
return -ENOMEM;
@@ -371,15 +405,10 @@ static int block_alloc_fixed(struct sst_module *module,
if (data->offset >= block->offset && data->offset < block_end) {
err = block_alloc_contiguous(module, data,
- block->offset + block->size,
- data->size - block->size);
+ block->offset, data->size);
if (err < 0)
return -ENOMEM;
- /* add block */
- block->data_type = data->data_type;
- list_move(&block->list, &dsp->used_block_list);
- list_add(&block->module_list, &module->block_list);
return 0;
}
@@ -505,9 +534,7 @@ struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp)
/* calculate required scratch size */
list_for_each_entry(sst_module, &dsp->module_list, list) {
- if (scratch->s.size > sst_module->s.size)
- scratch->s.size = scratch->s.size;
- else
+ if (scratch->s.size < sst_module->s.size)
scratch->s.size = sst_module->s.size;
}
diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/sst-haswell-dsp.c
index f5ebf36..535f517 100644
--- a/sound/soc/intel/sst-haswell-dsp.c
+++ b/sound/soc/intel/sst-haswell-dsp.c
@@ -433,7 +433,7 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
int ret = -ENODEV, i, j, region_count;
u32 offset, size;
- dev = sst->dev;
+ dev = sst->dma_dev;
switch (sst->id) {
case SST_DEV_ID_LYNX_POINT:
@@ -466,7 +466,7 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
return ret;
}
- ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31));
if (ret)
return ret;
diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c
index f46bb4d..4342363 100644
--- a/sound/soc/intel/sst-haswell-ipc.c
+++ b/sound/soc/intel/sst-haswell-ipc.c
@@ -25,7 +25,6 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/sched.h>
-#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/kthread.h>
#include <linux/firmware.h>
@@ -617,7 +616,7 @@ static void hsw_notification_work(struct work_struct *work)
case IPC_POSITION_CHANGED:
trace_ipc_notification("DSP stream position changed for",
stream->reply.stream_hw_id);
- sst_dsp_inbox_read(hsw->dsp, pos, sizeof(pos));
+ sst_dsp_inbox_read(hsw->dsp, pos, sizeof(*pos));
if (stream->notify_position)
stream->notify_position(stream, stream->pdata);
@@ -991,7 +990,8 @@ int sst_hsw_stream_get_volume(struct sst_hsw *hsw, struct sst_hsw_stream *stream
return -EINVAL;
sst_dsp_read(hsw->dsp, volume,
- stream->reply.volume_register_address[channel], sizeof(volume));
+ stream->reply.volume_register_address[channel],
+ sizeof(*volume));
return 0;
}
@@ -1158,11 +1158,14 @@ struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
void *data)
{
struct sst_hsw_stream *stream;
+ struct sst_dsp *sst = hsw->dsp;
+ unsigned long flags;
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
if (stream == NULL)
return NULL;
+ spin_lock_irqsave(&sst->spinlock, flags);
list_add(&stream->node, &hsw->stream_list);
stream->notify_position = notify_position;
stream->pdata = data;
@@ -1171,6 +1174,7 @@ struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
/* work to process notification messages */
INIT_WORK(&stream->notify_work, hsw_notification_work);
+ spin_unlock_irqrestore(&sst->spinlock, flags);
return stream;
}
@@ -1179,6 +1183,8 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
{
u32 header;
int ret = 0;
+ struct sst_dsp *sst = hsw->dsp;
+ unsigned long flags;
/* dont free DSP streams that are not commited */
if (!stream->commited)
@@ -1200,8 +1206,11 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
trace_hsw_stream_free_req(stream, &stream->free_req);
out:
+ cancel_work_sync(&stream->notify_work);
+ spin_lock_irqsave(&sst->spinlock, flags);
list_del(&stream->node);
kfree(stream);
+ spin_unlock_irqrestore(&sst->spinlock, flags);
return ret;
}
@@ -1537,10 +1546,28 @@ int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
}
/* Stream pointer positions */
-int sst_hsw_get_dsp_position(struct sst_hsw *hsw,
+u32 sst_hsw_get_dsp_position(struct sst_hsw *hsw,
struct sst_hsw_stream *stream)
{
- return stream->rpos.position;
+ u32 rpos;
+
+ sst_dsp_read(hsw->dsp, &rpos,
+ stream->reply.read_position_register_address, sizeof(rpos));
+
+ return rpos;
+}
+
+/* Stream presentation (monotonic) positions */
+u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream)
+{
+ u64 ppos;
+
+ sst_dsp_read(hsw->dsp, &ppos,
+ stream->reply.presentation_position_register_address,
+ sizeof(ppos));
+
+ return ppos;
}
int sst_hsw_stream_set_write_position(struct sst_hsw *hsw,
@@ -1609,7 +1636,7 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw,
trace_ipc_request("PM enter Dx state", state);
ret = ipc_tx_message_wait(hsw, header, &state_, sizeof(state_),
- dx, sizeof(dx));
+ dx, sizeof(*dx));
if (ret < 0) {
dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state);
return ret;
@@ -1702,17 +1729,17 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
ret = msg_empty_list_init(hsw);
if (ret < 0)
- goto list_err;
+ return -ENOMEM;
/* start the IPC message thread */
init_kthread_worker(&hsw->kworker);
hsw->tx_thread = kthread_run(kthread_worker_fn,
- &hsw->kworker,
+ &hsw->kworker, "%s",
dev_name(hsw->dev));
if (IS_ERR(hsw->tx_thread)) {
ret = PTR_ERR(hsw->tx_thread);
dev_err(hsw->dev, "error: failed to create message TX task\n");
- goto list_err;
+ goto err_free_msg;
}
init_kthread_work(&hsw->kwork, ipc_tx_msgs);
@@ -1722,7 +1749,7 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
hsw->dsp = sst_dsp_new(dev, &hsw_dev, pdata);
if (hsw->dsp == NULL) {
ret = -ENODEV;
- goto list_err;
+ goto dsp_err;
}
/* keep the DSP in reset state for base FW loading */
@@ -1766,8 +1793,11 @@ boot_err:
sst_fw_free(hsw_sst_fw);
fw_err:
sst_dsp_free(hsw->dsp);
+dsp_err:
+ kthread_stop(hsw->tx_thread);
+err_free_msg:
kfree(hsw->msg);
-list_err:
+
return ret;
}
EXPORT_SYMBOL_GPL(sst_hsw_dsp_init);
@@ -1780,6 +1810,7 @@ void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
sst_fw_free_all(hsw->dsp);
sst_dsp_free(hsw->dsp);
kfree(hsw->scratch);
+ kthread_stop(hsw->tx_thread);
kfree(hsw->msg);
}
EXPORT_SYMBOL_GPL(sst_hsw_dsp_free);
diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h
index d517929..2ac194a 100644
--- a/sound/soc/intel/sst-haswell-ipc.h
+++ b/sound/soc/intel/sst-haswell-ipc.h
@@ -464,7 +464,9 @@ int sst_hsw_stream_get_write_pos(struct sst_hsw *hsw,
struct sst_hsw_stream *stream, u32 *position);
int sst_hsw_stream_set_write_position(struct sst_hsw *hsw,
struct sst_hsw_stream *stream, u32 stage_id, u32 position);
-int sst_hsw_get_dsp_position(struct sst_hsw *hsw,
+u32 sst_hsw_get_dsp_position(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream);
+u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw,
struct sst_hsw_stream *stream);
/* HW port config */
diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c
index 0a32dd1..058efb1 100644
--- a/sound/soc/intel/sst-haswell-pcm.c
+++ b/sound/soc/intel/sst-haswell-pcm.c
@@ -17,7 +17,6 @@
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
-#include <linux/module.h>
#include <linux/delay.h>
#include <asm/page.h>
#include <asm/pgtable.h>
@@ -99,6 +98,7 @@ struct hsw_pcm_data {
struct snd_compr_stream *cstream;
unsigned int wpos;
struct mutex mutex;
+ bool allocated;
};
/* private data for the driver */
@@ -107,12 +107,14 @@ struct hsw_priv_data {
struct sst_hsw *hsw;
/* page tables */
- unsigned char *pcm_pg[HSW_PCM_COUNT][2];
+ struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
/* DAI data */
struct hsw_pcm_data pcm[HSW_PCM_COUNT];
};
+static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data);
+
static inline u32 hsw_mixer_to_ipc(unsigned int value)
{
if (value >= ARRAY_SIZE(volume_map))
@@ -136,7 +138,7 @@ static inline unsigned int hsw_ipc_to_mixer(u32 value)
static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct hsw_priv_data *pdata =
@@ -174,7 +176,7 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct hsw_priv_data *pdata =
@@ -206,7 +208,7 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
static int hsw_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
struct sst_hsw *hsw = pdata->hsw;
u32 volume;
@@ -231,7 +233,7 @@ static int hsw_volume_put(struct snd_kcontrol *kcontrol,
static int hsw_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
struct sst_hsw *hsw = pdata->hsw;
unsigned int volume = 0;
@@ -273,28 +275,26 @@ static const struct snd_kcontrol_new hsw_volume_controls[] = {
};
/* Create DMA buffer page table for DSP */
-static int create_adsp_page_table(struct hsw_priv_data *pdata,
- struct snd_soc_pcm_runtime *rtd,
- unsigned char *dma_area, size_t size, int pcm, int stream)
+static int create_adsp_page_table(struct snd_pcm_substream *substream,
+ struct hsw_priv_data *pdata, struct snd_soc_pcm_runtime *rtd,
+ unsigned char *dma_area, size_t size, int pcm)
{
- int i, pages;
+ struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
+ int i, pages, stream = substream->stream;
- if (size % PAGE_SIZE)
- pages = (size / PAGE_SIZE) + 1;
- else
- pages = size / PAGE_SIZE;
+ pages = snd_sgbuf_aligned_pages(size);
dev_dbg(rtd->dev, "generating page table for %p size 0x%zu pages %d\n",
dma_area, size, pages);
for (i = 0; i < pages; i++) {
u32 idx = (((i << 2) + i)) >> 1;
- u32 pfn = (virt_to_phys(dma_area + i * PAGE_SIZE)) >> PAGE_SHIFT;
+ u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT;
u32 *pg_table;
dev_dbg(rtd->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn);
- pg_table = (u32*)(pdata->pcm_pg[pcm][stream] + idx);
+ pg_table = (u32 *)(pdata->dmab[pcm][stream].area + idx);
if (i & 1)
*pg_table |= (pfn << 4);
@@ -317,12 +317,36 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
struct sst_hsw *hsw = pdata->hsw;
struct sst_module *module_data;
struct sst_dsp *dsp;
+ struct snd_dma_buffer *dmab;
enum sst_hsw_stream_type stream_type;
enum sst_hsw_stream_path_id path_id;
u32 rate, bits, map, pages, module_id;
u8 channels;
int ret;
+ /* check if we are being called a subsequent time */
+ if (pcm_data->allocated) {
+ ret = sst_hsw_stream_reset(hsw, pcm_data->stream);
+ if (ret < 0)
+ dev_dbg(rtd->dev, "error: reset stream failed %d\n",
+ ret);
+
+ ret = sst_hsw_stream_free(hsw, pcm_data->stream);
+ if (ret < 0) {
+ dev_dbg(rtd->dev, "error: free stream failed %d\n",
+ ret);
+ return ret;
+ }
+ pcm_data->allocated = false;
+
+ pcm_data->stream = sst_hsw_stream_new(hsw, rtd->cpu_dai->id,
+ hsw_notify_pointer, pcm_data);
+ if (pcm_data->stream == NULL) {
+ dev_err(rtd->dev, "error: failed to create stream\n");
+ return -EINVAL;
+ }
+ }
+
/* stream direction */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
@@ -416,8 +440,10 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- ret = create_adsp_page_table(pdata, rtd, runtime->dma_area,
- runtime->dma_bytes, rtd->cpu_dai->id, substream->stream);
+ dmab = snd_pcm_get_dma_buf(substream);
+
+ ret = create_adsp_page_table(substream, pdata, rtd, runtime->dma_area,
+ runtime->dma_bytes, rtd->cpu_dai->id);
if (ret < 0)
return ret;
@@ -430,9 +456,9 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
pages = runtime->dma_bytes / PAGE_SIZE;
ret = sst_hsw_stream_buffer(hsw, pcm_data->stream,
- virt_to_phys(pdata->pcm_pg[rtd->cpu_dai->id][substream->stream]),
+ pdata->dmab[rtd->cpu_dai->id][substream->stream].addr,
pages, runtime->dma_bytes, 0,
- (u32)(virt_to_phys(runtime->dma_area) >> PAGE_SHIFT));
+ snd_sgbuf_get_addr(dmab, 0) >> PAGE_SHIFT);
if (ret < 0) {
dev_err(rtd->dev, "error: failed to set DMA buffer %d\n", ret);
return ret;
@@ -474,6 +500,7 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
dev_err(rtd->dev, "error: failed to commit stream %d\n", ret);
return ret;
}
+ pcm_data->allocated = true;
ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1);
if (ret < 0)
@@ -541,12 +568,14 @@ static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream)
struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
struct sst_hsw *hsw = pdata->hsw;
snd_pcm_uframes_t offset;
+ uint64_t ppos;
+ u32 position = sst_hsw_get_dsp_position(hsw, pcm_data->stream);
- offset = bytes_to_frames(runtime,
- sst_hsw_get_dsp_position(hsw, pcm_data->stream));
+ offset = bytes_to_frames(runtime, position);
+ ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream);
- dev_dbg(rtd->dev, "PCM: DMA pointer %zu bytes\n",
- frames_to_bytes(runtime, (u32)offset));
+ dev_dbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
+ position, ppos);
return offset;
}
@@ -606,6 +635,7 @@ static int hsw_pcm_close(struct snd_pcm_substream *substream)
dev_dbg(rtd->dev, "error: free stream failed %d\n", ret);
goto out;
}
+ pcm_data->allocated = 0;
pcm_data->stream = NULL;
out:
@@ -621,7 +651,7 @@ static struct snd_pcm_ops hsw_pcm_ops = {
.hw_free = hsw_pcm_hw_free,
.trigger = hsw_pcm_trigger,
.pointer = hsw_pcm_pointer,
- .mmap = snd_pcm_lib_default_mmap,
+ .page = snd_pcm_sgbuf_ops_page,
};
static void hsw_pcm_free(struct snd_pcm *pcm)
@@ -632,17 +662,16 @@ static void hsw_pcm_free(struct snd_pcm *pcm)
static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct sst_pdata *pdata = dev_get_platdata(platform->dev);
+ struct device *dev = pdata->dma_dev;
int ret = 0;
- ret = dma_coerce_mask_and_coherent(rtd->card->dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_DEV,
- rtd->card->dev,
+ SNDRV_DMA_TYPE_DEV_SG,
+ dev,
hsw_pcm_hardware.buffer_bytes_max,
hsw_pcm_hardware.buffer_bytes_max);
if (ret) {
@@ -742,11 +771,14 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
{
struct sst_pdata *pdata = dev_get_platdata(platform->dev);
struct hsw_priv_data *priv_data;
- int i;
+ struct device *dma_dev;
+ int i, ret = 0;
if (!pdata)
return -ENODEV;
+ dma_dev = pdata->dma_dev;
+
priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data), GFP_KERNEL);
priv_data->hsw = pdata->dsp;
snd_soc_platform_set_drvdata(platform, priv_data);
@@ -758,15 +790,17 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
/* playback */
if (hsw_dais[i].playback.channels_min) {
- priv_data->pcm_pg[i][0] = kzalloc(PAGE_SIZE, GFP_DMA);
- if (priv_data->pcm_pg[i][0] == NULL)
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev,
+ PAGE_SIZE, &priv_data->dmab[i][0]);
+ if (ret < 0)
goto err;
}
/* capture */
if (hsw_dais[i].capture.channels_min) {
- priv_data->pcm_pg[i][1] = kzalloc(PAGE_SIZE, GFP_DMA);
- if (priv_data->pcm_pg[i][1] == NULL)
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev,
+ PAGE_SIZE, &priv_data->dmab[i][1]);
+ if (ret < 0)
goto err;
}
}
@@ -776,11 +810,11 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
err:
for (;i >= 0; i--) {
if (hsw_dais[i].playback.channels_min)
- kfree(priv_data->pcm_pg[i][0]);
+ snd_dma_free_pages(&priv_data->dmab[i][0]);
if (hsw_dais[i].capture.channels_min)
- kfree(priv_data->pcm_pg[i][1]);
+ snd_dma_free_pages(&priv_data->dmab[i][1]);
}
- return -ENOMEM;
+ return ret;
}
static int hsw_pcm_remove(struct snd_soc_platform *platform)
@@ -791,9 +825,9 @@ static int hsw_pcm_remove(struct snd_soc_platform *platform)
for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
if (hsw_dais[i].playback.channels_min)
- kfree(priv_data->pcm_pg[i][0]);
+ snd_dma_free_pages(&priv_data->dmab[i][0]);
if (hsw_dais[i].capture.channels_min)
- kfree(priv_data->pcm_pg[i][1]);
+ snd_dma_free_pages(&priv_data->dmab[i][1]);
}
return 0;
diff --git a/sound/soc/intel/sst-mfld-dsp.h b/sound/soc/intel/sst-mfld-dsp.h
index 3b63edc..8d482d7 100644
--- a/sound/soc/intel/sst-mfld-dsp.h
+++ b/sound/soc/intel/sst-mfld-dsp.h
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
@@ -40,7 +36,6 @@ enum stream_type {
};
struct snd_pcm_params {
- u16 codec; /* codec type */
u8 num_chan; /* 1=Mono, 2=Stereo */
u8 pcm_wd_sz; /* 16/24 - bit*/
u32 reserved; /* Bitrate in bits per second */
@@ -53,7 +48,6 @@ struct snd_pcm_params {
/* MP3 Music Parameters Message */
struct snd_mp3_params {
- u16 codec;
u8 num_chan; /* 1=Mono, 2=Stereo */
u8 pcm_wd_sz; /* 16/24 - bit*/
u8 crc_check; /* crc_check - disable (0) or enable (1) */
@@ -67,7 +61,6 @@ struct snd_mp3_params {
/* AAC Music Parameters Message */
struct snd_aac_params {
- u16 codec;
u8 num_chan; /* 1=Mono, 2=Stereo*/
u8 pcm_wd_sz; /* 16/24 - bit*/
u8 bdownsample; /*SBR downsampling 0 - disable 1 -enabled AAC+ only */
@@ -81,7 +74,6 @@ struct snd_aac_params {
/* WMA Music Parameters Message */
struct snd_wma_params {
- u16 codec;
u8 num_chan; /* 1=Mono, 2=Stereo */
u8 pcm_wd_sz; /* 16/24 - bit*/
u32 brate; /* Use the hard coded value. */
diff --git a/sound/soc/intel/sst-mfld-platform-compress.c b/sound/soc/intel/sst-mfld-platform-compress.c
new file mode 100644
index 0000000..02abd19
--- /dev/null
+++ b/sound/soc/intel/sst-mfld-platform-compress.c
@@ -0,0 +1,237 @@
+/*
+ * sst_mfld_platform.c - Intel MID Platform driver
+ *
+ * Copyright (C) 2010-2014 Intel Corp
+ * Author: Vinod Koul <vinod.koul@intel.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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include "sst-mfld-platform.h"
+
+/* compress stream operations */
+static void sst_compr_fragment_elapsed(void *arg)
+{
+ struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
+
+ pr_debug("fragment elapsed by driver\n");
+ if (cstream)
+ snd_compr_fragment_elapsed(cstream);
+}
+
+static void sst_drain_notify(void *arg)
+{
+ struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
+
+ pr_debug("drain notify by driver\n");
+ if (cstream)
+ snd_compr_drain_notify(cstream);
+}
+
+static int sst_platform_compr_open(struct snd_compr_stream *cstream)
+{
+
+ int ret_val = 0;
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct sst_runtime_stream *stream;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ spin_lock_init(&stream->status_lock);
+
+ /* get the sst ops */
+ if (!sst || !try_module_get(sst->dev->driver->owner)) {
+ pr_err("no device available to run\n");
+ ret_val = -ENODEV;
+ goto out_ops;
+ }
+ stream->compr_ops = sst->compr_ops;
+
+ stream->id = 0;
+ sst_set_stream_status(stream, SST_PLATFORM_INIT);
+ runtime->private_data = stream;
+ return 0;
+out_ops:
+ kfree(stream);
+ return ret_val;
+}
+
+static int sst_platform_compr_free(struct snd_compr_stream *cstream)
+{
+ struct sst_runtime_stream *stream;
+ int ret_val = 0, str_id;
+
+ stream = cstream->runtime->private_data;
+ /*need to check*/
+ str_id = stream->id;
+ if (str_id)
+ ret_val = stream->compr_ops->close(str_id);
+ module_put(sst->dev->driver->owner);
+ kfree(stream);
+ pr_debug("%s: %d\n", __func__, ret_val);
+ return 0;
+}
+
+static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
+ struct snd_compr_params *params)
+{
+ struct sst_runtime_stream *stream;
+ int retval;
+ struct snd_sst_params str_params;
+ struct sst_compress_cb cb;
+
+ stream = cstream->runtime->private_data;
+ /* construct fw structure for this*/
+ memset(&str_params, 0, sizeof(str_params));
+
+ str_params.ops = STREAM_OPS_PLAYBACK;
+ str_params.stream_type = SST_STREAM_TYPE_MUSIC;
+ str_params.device_type = SND_SST_DEVICE_COMPRESS;
+
+ switch (params->codec.id) {
+ case SND_AUDIOCODEC_MP3: {
+ str_params.codec = SST_CODEC_TYPE_MP3;
+ str_params.sparams.uc.mp3_params.num_chan = params->codec.ch_in;
+ str_params.sparams.uc.mp3_params.pcm_wd_sz = 16;
+ break;
+ }
+
+ case SND_AUDIOCODEC_AAC: {
+ str_params.codec = SST_CODEC_TYPE_AAC;
+ str_params.sparams.uc.aac_params.num_chan = params->codec.ch_in;
+ str_params.sparams.uc.aac_params.pcm_wd_sz = 16;
+ if (params->codec.format == SND_AUDIOSTREAMFORMAT_MP4ADTS)
+ str_params.sparams.uc.aac_params.bs_format =
+ AAC_BIT_STREAM_ADTS;
+ else if (params->codec.format == SND_AUDIOSTREAMFORMAT_RAW)
+ str_params.sparams.uc.aac_params.bs_format =
+ AAC_BIT_STREAM_RAW;
+ else {
+ pr_err("Undefined format%d\n", params->codec.format);
+ return -EINVAL;
+ }
+ str_params.sparams.uc.aac_params.externalsr =
+ params->codec.sample_rate;
+ break;
+ }
+
+ default:
+ pr_err("codec not supported, id =%d\n", params->codec.id);
+ return -EINVAL;
+ }
+
+ str_params.aparams.ring_buf_info[0].addr =
+ virt_to_phys(cstream->runtime->buffer);
+ str_params.aparams.ring_buf_info[0].size =
+ cstream->runtime->buffer_size;
+ str_params.aparams.sg_count = 1;
+ str_params.aparams.frag_size = cstream->runtime->fragment_size;
+
+ cb.param = cstream;
+ cb.compr_cb = sst_compr_fragment_elapsed;
+ cb.drain_cb_param = cstream;
+ cb.drain_notify = sst_drain_notify;
+
+ retval = stream->compr_ops->open(&str_params, &cb);
+ if (retval < 0) {
+ pr_err("stream allocation failed %d\n", retval);
+ return retval;
+ }
+
+ stream->id = retval;
+ return 0;
+}
+
+static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
+{
+ struct sst_runtime_stream *stream =
+ cstream->runtime->private_data;
+
+ return stream->compr_ops->control(cmd, stream->id);
+}
+
+static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *tstamp)
+{
+ struct sst_runtime_stream *stream;
+
+ stream = cstream->runtime->private_data;
+ stream->compr_ops->tstamp(stream->id, tstamp);
+ tstamp->byte_offset = tstamp->copied_total %
+ (u32)cstream->runtime->buffer_size;
+ pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset);
+ return 0;
+}
+
+static int sst_platform_compr_ack(struct snd_compr_stream *cstream,
+ size_t bytes)
+{
+ struct sst_runtime_stream *stream;
+
+ stream = cstream->runtime->private_data;
+ stream->compr_ops->ack(stream->id, (unsigned long)bytes);
+ stream->bytes_written += bytes;
+
+ return 0;
+}
+
+static int sst_platform_compr_get_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_caps *caps)
+{
+ struct sst_runtime_stream *stream =
+ cstream->runtime->private_data;
+
+ return stream->compr_ops->get_caps(caps);
+}
+
+static int sst_platform_compr_get_codec_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_codec_caps *codec)
+{
+ struct sst_runtime_stream *stream =
+ cstream->runtime->private_data;
+
+ return stream->compr_ops->get_codec_caps(codec);
+}
+
+static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream,
+ struct snd_compr_metadata *metadata)
+{
+ struct sst_runtime_stream *stream =
+ cstream->runtime->private_data;
+
+ return stream->compr_ops->set_metadata(stream->id, metadata);
+}
+
+struct snd_compr_ops sst_platform_compr_ops = {
+
+ .open = sst_platform_compr_open,
+ .free = sst_platform_compr_free,
+ .set_params = sst_platform_compr_set_params,
+ .set_metadata = sst_platform_compr_set_metadata,
+ .trigger = sst_platform_compr_trigger,
+ .pointer = sst_platform_compr_pointer,
+ .ack = sst_platform_compr_ack,
+ .get_caps = sst_platform_compr_get_caps,
+ .get_codec_caps = sst_platform_compr_get_codec_caps,
+};
diff --git a/sound/soc/intel/sst-mfld-platform.c b/sound/soc/intel/sst-mfld-platform-pcm.c
index 840306c..7c790f5 100644
--- a/sound/soc/intel/sst-mfld-platform.c
+++ b/sound/soc/intel/sst-mfld-platform-pcm.c
@@ -15,13 +15,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- *
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -35,8 +29,9 @@
#include <sound/compress_driver.h>
#include "sst-mfld-platform.h"
-static struct sst_device *sst;
+struct sst_device *sst;
static DEFINE_MUTEX(sst_lock);
+extern struct snd_compr_ops sst_platform_compr_ops;
int sst_register_dsp(struct sst_device *dev)
{
@@ -116,36 +111,6 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
},
},
{
- .name = "Speaker-cpu-dai",
- .id = 1,
- .playback = {
- .channels_min = SST_MONO,
- .channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S24_LE,
- },
-},
-{
- .name = "Vibra1-cpu-dai",
- .id = 2,
- .playback = {
- .channels_min = SST_MONO,
- .channels_max = SST_MONO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S24_LE,
- },
-},
-{
- .name = "Vibra2-cpu-dai",
- .id = 3,
- .playback = {
- .channels_min = SST_MONO,
- .channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S24_LE,
- },
-},
-{
.name = "Compress-cpu-dai",
.compress_dai = 1,
.playback = {
@@ -157,12 +122,8 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
},
};
-static const struct snd_soc_component_driver sst_component = {
- .name = "sst",
-};
-
/* helper functions */
-static inline void sst_set_stream_status(struct sst_runtime_stream *stream,
+void sst_set_stream_status(struct sst_runtime_stream *stream,
int state)
{
unsigned long flags;
@@ -186,7 +147,6 @@ static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
struct sst_pcm_params *param)
{
- param->codec = SST_CODEC_TYPE_PCM;
param->num_chan = (u8) substream->runtime->channels;
param->pcm_wd_sz = substream->runtime->sample_bits;
param->reserved = 0;
@@ -471,205 +431,6 @@ static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
return retval;
}
-/* compress stream operations */
-static void sst_compr_fragment_elapsed(void *arg)
-{
- struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
-
- pr_debug("fragment elapsed by driver\n");
- if (cstream)
- snd_compr_fragment_elapsed(cstream);
-}
-
-static int sst_platform_compr_open(struct snd_compr_stream *cstream)
-{
-
- int ret_val = 0;
- struct snd_compr_runtime *runtime = cstream->runtime;
- struct sst_runtime_stream *stream;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
-
- spin_lock_init(&stream->status_lock);
-
- /* get the sst ops */
- if (!sst || !try_module_get(sst->dev->driver->owner)) {
- pr_err("no device available to run\n");
- ret_val = -ENODEV;
- goto out_ops;
- }
- stream->compr_ops = sst->compr_ops;
-
- stream->id = 0;
- sst_set_stream_status(stream, SST_PLATFORM_INIT);
- runtime->private_data = stream;
- return 0;
-out_ops:
- kfree(stream);
- return ret_val;
-}
-
-static int sst_platform_compr_free(struct snd_compr_stream *cstream)
-{
- struct sst_runtime_stream *stream;
- int ret_val = 0, str_id;
-
- stream = cstream->runtime->private_data;
- /*need to check*/
- str_id = stream->id;
- if (str_id)
- ret_val = stream->compr_ops->close(str_id);
- module_put(sst->dev->driver->owner);
- kfree(stream);
- pr_debug("%s: %d\n", __func__, ret_val);
- return 0;
-}
-
-static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
- struct snd_compr_params *params)
-{
- struct sst_runtime_stream *stream;
- int retval;
- struct snd_sst_params str_params;
- struct sst_compress_cb cb;
-
- stream = cstream->runtime->private_data;
- /* construct fw structure for this*/
- memset(&str_params, 0, sizeof(str_params));
-
- str_params.ops = STREAM_OPS_PLAYBACK;
- str_params.stream_type = SST_STREAM_TYPE_MUSIC;
- str_params.device_type = SND_SST_DEVICE_COMPRESS;
-
- switch (params->codec.id) {
- case SND_AUDIOCODEC_MP3: {
- str_params.codec = SST_CODEC_TYPE_MP3;
- str_params.sparams.uc.mp3_params.codec = SST_CODEC_TYPE_MP3;
- str_params.sparams.uc.mp3_params.num_chan = params->codec.ch_in;
- str_params.sparams.uc.mp3_params.pcm_wd_sz = 16;
- break;
- }
-
- case SND_AUDIOCODEC_AAC: {
- str_params.codec = SST_CODEC_TYPE_AAC;
- str_params.sparams.uc.aac_params.codec = SST_CODEC_TYPE_AAC;
- str_params.sparams.uc.aac_params.num_chan = params->codec.ch_in;
- str_params.sparams.uc.aac_params.pcm_wd_sz = 16;
- if (params->codec.format == SND_AUDIOSTREAMFORMAT_MP4ADTS)
- str_params.sparams.uc.aac_params.bs_format =
- AAC_BIT_STREAM_ADTS;
- else if (params->codec.format == SND_AUDIOSTREAMFORMAT_RAW)
- str_params.sparams.uc.aac_params.bs_format =
- AAC_BIT_STREAM_RAW;
- else {
- pr_err("Undefined format%d\n", params->codec.format);
- return -EINVAL;
- }
- str_params.sparams.uc.aac_params.externalsr =
- params->codec.sample_rate;
- break;
- }
-
- default:
- pr_err("codec not supported, id =%d\n", params->codec.id);
- return -EINVAL;
- }
-
- str_params.aparams.ring_buf_info[0].addr =
- virt_to_phys(cstream->runtime->buffer);
- str_params.aparams.ring_buf_info[0].size =
- cstream->runtime->buffer_size;
- str_params.aparams.sg_count = 1;
- str_params.aparams.frag_size = cstream->runtime->fragment_size;
-
- cb.param = cstream;
- cb.compr_cb = sst_compr_fragment_elapsed;
-
- retval = stream->compr_ops->open(&str_params, &cb);
- if (retval < 0) {
- pr_err("stream allocation failed %d\n", retval);
- return retval;
- }
-
- stream->id = retval;
- return 0;
-}
-
-static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
-{
- struct sst_runtime_stream *stream =
- cstream->runtime->private_data;
-
- return stream->compr_ops->control(cmd, stream->id);
-}
-
-static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
- struct snd_compr_tstamp *tstamp)
-{
- struct sst_runtime_stream *stream;
-
- stream = cstream->runtime->private_data;
- stream->compr_ops->tstamp(stream->id, tstamp);
- tstamp->byte_offset = tstamp->copied_total %
- (u32)cstream->runtime->buffer_size;
- pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset);
- return 0;
-}
-
-static int sst_platform_compr_ack(struct snd_compr_stream *cstream,
- size_t bytes)
-{
- struct sst_runtime_stream *stream;
-
- stream = cstream->runtime->private_data;
- stream->compr_ops->ack(stream->id, (unsigned long)bytes);
- stream->bytes_written += bytes;
-
- return 0;
-}
-
-static int sst_platform_compr_get_caps(struct snd_compr_stream *cstream,
- struct snd_compr_caps *caps)
-{
- struct sst_runtime_stream *stream =
- cstream->runtime->private_data;
-
- return stream->compr_ops->get_caps(caps);
-}
-
-static int sst_platform_compr_get_codec_caps(struct snd_compr_stream *cstream,
- struct snd_compr_codec_caps *codec)
-{
- struct sst_runtime_stream *stream =
- cstream->runtime->private_data;
-
- return stream->compr_ops->get_codec_caps(codec);
-}
-
-static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream,
- struct snd_compr_metadata *metadata)
-{
- struct sst_runtime_stream *stream =
- cstream->runtime->private_data;
-
- return stream->compr_ops->set_metadata(stream->id, metadata);
-}
-
-static struct snd_compr_ops sst_platform_compr_ops = {
-
- .open = sst_platform_compr_open,
- .free = sst_platform_compr_free,
- .set_params = sst_platform_compr_set_params,
- .set_metadata = sst_platform_compr_set_metadata,
- .trigger = sst_platform_compr_trigger,
- .pointer = sst_platform_compr_pointer,
- .ack = sst_platform_compr_ack,
- .get_caps = sst_platform_compr_get_caps,
- .get_codec_caps = sst_platform_compr_get_codec_caps,
-};
-
static struct snd_soc_platform_driver sst_soc_platform_drv = {
.ops = &sst_platform_ops,
.compr_ops = &sst_platform_compr_ops,
@@ -677,6 +438,11 @@ static struct snd_soc_platform_driver sst_soc_platform_drv = {
.pcm_free = sst_pcm_free,
};
+static const struct snd_soc_component_driver sst_component = {
+ .name = "sst",
+};
+
+
static int sst_platform_probe(struct platform_device *pdev)
{
int ret;
diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h
index 0c4e2dd..6c5e7dc 100644
--- a/sound/soc/intel/sst-mfld-platform.h
+++ b/sound/soc/intel/sst-mfld-platform.h
@@ -15,13 +15,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- *
*/
#ifndef __SST_PLATFORMDRV_H__
@@ -29,6 +23,8 @@
#include "sst-mfld-dsp.h"
+extern struct sst_device *sst;
+
#define SST_MONO 1
#define SST_STEREO 2
#define SST_MAX_CAP 5
@@ -108,6 +104,8 @@ struct sst_stream_params {
struct sst_compress_cb {
void *param;
void (*compr_cb)(void *param);
+ void *drain_cb_param;
+ void (*drain_notify)(void *param);
};
struct compress_sst_ops {
@@ -148,6 +146,7 @@ struct sst_device {
struct compress_sst_ops *compr_ops;
};
+void sst_set_stream_status(struct sst_runtime_stream *stream, int state);
int sst_register_dsp(struct sst_device *sst);
int sst_unregister_dsp(struct sst_device *sst);
#endif
OpenPOWER on IntegriCloud