aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/sound/soc/intel/skylake
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/intel/skylake')
-rw-r--r--sound/soc/intel/skylake/Makefile2
-rw-r--r--sound/soc/intel/skylake/bxt-sst.c328
-rw-r--r--sound/soc/intel/skylake/skl-messages.c122
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c15
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c119
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.c2
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.h15
-rw-r--r--sound/soc/intel/skylake/skl-sst.c13
-rw-r--r--sound/soc/intel/skylake/skl-topology.c27
-rw-r--r--sound/soc/intel/skylake/skl-topology.h2
-rw-r--r--sound/soc/intel/skylake/skl-tplg-interface.h2
-rw-r--r--sound/soc/intel/skylake/skl.c39
-rw-r--r--sound/soc/intel/skylake/skl.h6
13 files changed, 648 insertions, 44 deletions
diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile
index 914b6dab9bea..c28f5d0e1d99 100644
--- a/sound/soc/intel/skylake/Makefile
+++ b/sound/soc/intel/skylake/Makefile
@@ -5,6 +5,6 @@ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
# Skylake IPC Support
snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o skl-sst-cldma.o \
- skl-sst.o
+ skl-sst.o bxt-sst.o
obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o
diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c
new file mode 100644
index 000000000000..965ce40ce752
--- /dev/null
+++ b/sound/soc/intel/skylake/bxt-sst.c
@@ -0,0 +1,328 @@
+/*
+ * bxt-sst.c - DSP library functions for BXT platform
+ *
+ * Copyright (C) 2015-16 Intel Corp
+ * Author:Rafal Redzimski <rafal.f.redzimski@intel.com>
+ * Jeeja KP <jeeja.kp@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.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/device.h>
+
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "skl-sst-ipc.h"
+
+#define BXT_BASEFW_TIMEOUT 3000
+#define BXT_INIT_TIMEOUT 500
+#define BXT_IPC_PURGE_FW 0x01004000
+
+#define BXT_ROM_INIT 0x5
+#define BXT_ADSP_SRAM0_BASE 0x80000
+
+/* Firmware status window */
+#define BXT_ADSP_FW_STATUS BXT_ADSP_SRAM0_BASE
+#define BXT_ADSP_ERROR_CODE (BXT_ADSP_FW_STATUS + 0x4)
+
+#define BXT_ADSP_SRAM1_BASE 0xA0000
+
+static unsigned int bxt_get_errorcode(struct sst_dsp *ctx)
+{
+ return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE);
+}
+
+static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
+ const void *fwdata, u32 fwsize)
+{
+ int stream_tag, ret, i;
+ u32 reg;
+
+ stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab);
+ if (stream_tag < 0) {
+ dev_err(ctx->dev, "Failed to prepare DMA FW loading err: %x\n",
+ stream_tag);
+ return stream_tag;
+ }
+
+ ctx->dsp_ops.stream_tag = stream_tag;
+ memcpy(ctx->dmab.area, fwdata, fwsize);
+
+ /* Purge FW request */
+ sst_dsp_shim_write(ctx, SKL_ADSP_REG_HIPCI, SKL_ADSP_REG_HIPCI_BUSY |
+ BXT_IPC_PURGE_FW | (stream_tag - 1));
+
+ ret = skl_dsp_enable_core(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret);
+ ret = -EIO;
+ goto base_fw_load_failed;
+ }
+
+ for (i = BXT_INIT_TIMEOUT; i > 0; --i) {
+ reg = sst_dsp_shim_read(ctx, SKL_ADSP_REG_HIPCIE);
+
+ if (reg & SKL_ADSP_REG_HIPCIE_DONE) {
+ sst_dsp_shim_update_bits_forced(ctx,
+ SKL_ADSP_REG_HIPCIE,
+ SKL_ADSP_REG_HIPCIE_DONE,
+ SKL_ADSP_REG_HIPCIE_DONE);
+ break;
+ }
+ mdelay(1);
+ }
+ if (!i) {
+ dev_info(ctx->dev, "Waiting for HIPCIE done, reg: 0x%x\n", reg);
+ sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_HIPCIE,
+ SKL_ADSP_REG_HIPCIE_DONE,
+ SKL_ADSP_REG_HIPCIE_DONE);
+ }
+
+ /* enable Interrupt */
+ skl_ipc_int_enable(ctx);
+ skl_ipc_op_int_enable(ctx);
+
+ for (i = BXT_INIT_TIMEOUT; i > 0; --i) {
+ if (SKL_FW_INIT ==
+ (sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS) &
+ SKL_FW_STS_MASK)) {
+
+ dev_info(ctx->dev, "ROM loaded, continue FW loading\n");
+ break;
+ }
+ mdelay(1);
+ }
+ if (!i) {
+ dev_err(ctx->dev, "Timeout for ROM init, HIPCIE: 0x%x\n", reg);
+ ret = -EIO;
+ goto base_fw_load_failed;
+ }
+
+ return ret;
+
+base_fw_load_failed:
+ ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag);
+ skl_dsp_disable_core(ctx);
+ return ret;
+}
+
+static int sst_transfer_fw_host_dma(struct sst_dsp *ctx)
+{
+ int ret;
+
+ ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag);
+ ret = sst_dsp_register_poll(ctx, BXT_ADSP_FW_STATUS, SKL_FW_STS_MASK,
+ BXT_ROM_INIT, BXT_BASEFW_TIMEOUT, "Firmware boot");
+
+ ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag);
+ ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag);
+
+ return ret;
+}
+
+static int bxt_load_base_firmware(struct sst_dsp *ctx)
+{
+ const struct firmware *fw = NULL;
+ struct skl_sst *skl = ctx->thread_context;
+ int ret;
+
+ ret = request_firmware(&fw, ctx->fw_name, ctx->dev);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Request firmware failed %d\n", ret);
+ goto sst_load_base_firmware_failed;
+ }
+
+ ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size);
+ /* Retry Enabling core and ROM load. Retry seemed to help */
+ if (ret < 0) {
+ ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret);
+ goto sst_load_base_firmware_failed;
+ }
+ }
+
+ ret = sst_transfer_fw_host_dma(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Transfer firmware failed %d\n", ret);
+ dev_info(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
+ sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
+ sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
+
+ skl_dsp_disable_core(ctx);
+ } else {
+ dev_dbg(ctx->dev, "Firmware download successful\n");
+ ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
+ msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
+ if (ret == 0) {
+ dev_err(ctx->dev, "DSP boot fail, FW Ready timeout\n");
+ skl_dsp_disable_core(ctx);
+ ret = -EIO;
+ } else {
+ skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
+ ret = 0;
+ }
+ }
+
+sst_load_base_firmware_failed:
+ release_firmware(fw);
+ return ret;
+}
+
+static int bxt_set_dsp_D0(struct sst_dsp *ctx)
+{
+ struct skl_sst *skl = ctx->thread_context;
+ int ret;
+
+ skl->boot_complete = false;
+
+ ret = skl_dsp_enable_core(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "enable dsp core failed ret: %d\n", ret);
+ return ret;
+ }
+
+ /* enable interrupt */
+ skl_ipc_int_enable(ctx);
+ skl_ipc_op_int_enable(ctx);
+
+ ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
+ msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
+ if (ret == 0) {
+ dev_err(ctx->dev, "ipc: error DSP boot timeout\n");
+ dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
+ sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
+ sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
+ return -EIO;
+ }
+
+ skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
+ return 0;
+}
+
+static int bxt_set_dsp_D3(struct sst_dsp *ctx)
+{
+ struct skl_ipc_dxstate_info dx;
+ struct skl_sst *skl = ctx->thread_context;
+ int ret = 0;
+
+ if (!is_skl_dsp_running(ctx))
+ return ret;
+
+ dx.core_mask = SKL_DSP_CORE0_MASK;
+ dx.dx_mask = SKL_IPC_D3_MASK;
+
+ ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID,
+ SKL_BASE_FW_MODULE_ID, &dx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to set DSP to D3 state: %d\n", ret);
+ return ret;
+ }
+
+ ret = skl_dsp_disable_core(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "disbale dsp core failed: %d\n", ret);
+ ret = -EIO;
+ }
+
+ skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
+ return 0;
+}
+
+static struct skl_dsp_fw_ops bxt_fw_ops = {
+ .set_state_D0 = bxt_set_dsp_D0,
+ .set_state_D3 = bxt_set_dsp_D3,
+ .load_fw = bxt_load_base_firmware,
+ .get_fw_errcode = bxt_get_errorcode,
+};
+
+static struct sst_ops skl_ops = {
+ .irq_handler = skl_dsp_sst_interrupt,
+ .write = sst_shim32_write,
+ .read = sst_shim32_read,
+ .ram_read = sst_memcpy_fromio_32,
+ .ram_write = sst_memcpy_toio_32,
+ .free = skl_dsp_free,
+};
+
+static struct sst_dsp_device skl_dev = {
+ .thread = skl_dsp_irq_thread_handler,
+ .ops = &skl_ops,
+};
+
+int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
+ const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
+ struct skl_sst **dsp)
+{
+ struct skl_sst *skl;
+ struct sst_dsp *sst;
+ int ret;
+
+ skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL);
+ if (skl == NULL)
+ return -ENOMEM;
+
+ skl->dev = dev;
+ skl_dev.thread_context = skl;
+
+ skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
+ if (!skl->dsp) {
+ dev_err(skl->dev, "skl_dsp_ctx_init failed\n");
+ return -ENODEV;
+ }
+
+ sst = skl->dsp;
+ sst->fw_name = fw_name;
+ sst->dsp_ops = dsp_ops;
+ sst->fw_ops = bxt_fw_ops;
+ sst->addr.lpe = mmio_base;
+ sst->addr.shim = mmio_base;
+
+ sst_dsp_mailbox_init(sst, (BXT_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ),
+ SKL_ADSP_W0_UP_SZ, BXT_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ);
+
+ ret = skl_ipc_init(dev, skl);
+ if (ret)
+ return ret;
+
+ skl->boot_complete = false;
+ init_waitqueue_head(&skl->boot_wait);
+
+ ret = sst->fw_ops.load_fw(sst);
+ if (ret < 0) {
+ dev_err(dev, "Load base fw failed: %x", ret);
+ return ret;
+ }
+
+ if (dsp)
+ *dsp = skl;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(bxt_sst_dsp_init);
+
+
+void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
+{
+ skl_ipc_free(&ctx->ipc);
+ ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp);
+
+ if (ctx->dsp->addr.lpe)
+ iounmap(ctx->dsp->addr.lpe);
+
+ ctx->dsp->ops->free(ctx->dsp);
+}
+EXPORT_SYMBOL_GPL(bxt_sst_dsp_cleanup);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel Broxton IPC driver");
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c
index 79c5089b85d6..226db84ba20f 100644
--- a/sound/soc/intel/skylake/skl-messages.c
+++ b/sound/soc/intel/skylake/skl-messages.c
@@ -72,6 +72,105 @@ static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask);
}
+static int skl_dsp_setup_spib(struct device *dev, unsigned int size,
+ int stream_tag, int enable)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct hdac_stream *stream = snd_hdac_get_stream(bus,
+ SNDRV_PCM_STREAM_PLAYBACK, stream_tag);
+ struct hdac_ext_stream *estream;
+
+ if (!stream)
+ return -EINVAL;
+
+ estream = stream_to_hdac_ext_stream(stream);
+ /* enable/disable SPIB for this hdac stream */
+ snd_hdac_ext_stream_spbcap_enable(ebus, enable, stream->index);
+
+ /* set the spib value */
+ snd_hdac_ext_stream_set_spib(ebus, estream, size);
+
+ return 0;
+}
+
+static int skl_dsp_prepare(struct device *dev, unsigned int format,
+ unsigned int size, struct snd_dma_buffer *dmab)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct hdac_ext_stream *estream;
+ struct hdac_stream *stream;
+ struct snd_pcm_substream substream;
+ int ret;
+
+ if (!bus)
+ return -ENODEV;
+
+ memset(&substream, 0, sizeof(substream));
+ substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+
+ estream = snd_hdac_ext_stream_assign(ebus, &substream,
+ HDAC_EXT_STREAM_TYPE_HOST);
+ if (!estream)
+ return -ENODEV;
+
+ stream = hdac_stream(estream);
+
+ /* assign decouple host dma channel */
+ ret = snd_hdac_dsp_prepare(stream, format, size, dmab);
+ if (ret < 0)
+ return ret;
+
+ skl_dsp_setup_spib(dev, size, stream->stream_tag, true);
+
+ return stream->stream_tag;
+}
+
+static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+ struct hdac_stream *stream;
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+
+ if (!bus)
+ return -ENODEV;
+
+ stream = snd_hdac_get_stream(bus,
+ SNDRV_PCM_STREAM_PLAYBACK, stream_tag);
+ if (!stream)
+ return -EINVAL;
+
+ snd_hdac_dsp_trigger(stream, start);
+
+ return 0;
+}
+
+static int skl_dsp_cleanup(struct device *dev,
+ struct snd_dma_buffer *dmab, int stream_tag)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+ struct hdac_stream *stream;
+ struct hdac_ext_stream *estream;
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+
+ if (!bus)
+ return -ENODEV;
+
+ stream = snd_hdac_get_stream(bus,
+ SNDRV_PCM_STREAM_PLAYBACK, stream_tag);
+ if (!stream)
+ return -EINVAL;
+
+ estream = stream_to_hdac_ext_stream(stream);
+ skl_dsp_setup_spib(dev, 0, stream_tag, false);
+ snd_hdac_ext_stream_release(estream, HDAC_EXT_STREAM_TYPE_HOST);
+
+ snd_hdac_dsp_cleanup(stream, dmab);
+
+ return 0;
+}
+
static struct skl_dsp_loader_ops skl_get_loader_ops(void)
{
struct skl_dsp_loader_ops loader_ops;
@@ -84,6 +183,21 @@ static struct skl_dsp_loader_ops skl_get_loader_ops(void)
return loader_ops;
};
+static struct skl_dsp_loader_ops bxt_get_loader_ops(void)
+{
+ struct skl_dsp_loader_ops loader_ops;
+
+ memset(&loader_ops, 0, sizeof(loader_ops));
+
+ loader_ops.alloc_dma_buf = skl_alloc_dma_buf;
+ loader_ops.free_dma_buf = skl_free_dma_buf;
+ loader_ops.prepare = skl_dsp_prepare;
+ loader_ops.trigger = skl_dsp_trigger;
+ loader_ops.cleanup = skl_dsp_cleanup;
+
+ return loader_ops;
+};
+
static const struct skl_dsp_ops dsp_ops[] = {
{
.id = 0x9d70,
@@ -91,6 +205,12 @@ static const struct skl_dsp_ops dsp_ops[] = {
.init = skl_sst_dsp_init,
.cleanup = skl_sst_dsp_cleanup
},
+ {
+ .id = 0x5a98,
+ .loader_ops = bxt_get_loader_ops,
+ .init = bxt_sst_dsp_init,
+ .cleanup = bxt_sst_dsp_cleanup
+ },
};
static int skl_get_dsp_ops(int pci_id)
@@ -744,7 +864,7 @@ int skl_init_module(struct skl_sst *ctx,
return ret;
}
mconfig->m_state = SKL_MODULE_INIT_DONE;
-
+ kfree(param_data);
return ret;
}
diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c
index 14d1916ea9f8..7d73648e5f9a 100644
--- a/sound/soc/intel/skylake/skl-nhlt.c
+++ b/sound/soc/intel/skylake/skl-nhlt.c
@@ -25,11 +25,12 @@ static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45,
#define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS"
-void *skl_nhlt_init(struct device *dev)
+struct nhlt_acpi_table *skl_nhlt_init(struct device *dev)
{
acpi_handle handle;
union acpi_object *obj;
struct nhlt_resource_desc *nhlt_ptr = NULL;
+ struct nhlt_acpi_table *nhlt_table = NULL;
if (ACPI_FAILURE(acpi_get_handle(NULL, DSDT_NHLT_PATH, &handle))) {
dev_err(dev, "Requested NHLT device not found\n");
@@ -39,18 +40,20 @@ void *skl_nhlt_init(struct device *dev)
obj = acpi_evaluate_dsm(handle, OSC_UUID, 1, 1, NULL);
if (obj && obj->type == ACPI_TYPE_BUFFER) {
nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer;
-
- return memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
+ nhlt_table = (struct nhlt_acpi_table *)
+ memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
MEMREMAP_WB);
+ ACPI_FREE(obj);
+ return nhlt_table;
}
dev_err(dev, "device specific method to extract NHLT blob failed\n");
return NULL;
}
-void skl_nhlt_free(void *addr)
+void skl_nhlt_free(struct nhlt_acpi_table *nhlt)
{
- memunmap(addr);
+ memunmap((void *) nhlt);
}
static struct nhlt_specific_cfg *skl_get_specific_cfg(
@@ -120,7 +123,7 @@ struct nhlt_specific_cfg
struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
struct device *dev = bus->dev;
struct nhlt_specific_cfg *sp_config;
- struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
+ struct nhlt_acpi_table *nhlt = skl->nhlt;
u16 bps = (s_fmt == 16) ? 16 : 32;
u8 j;
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index dab0900eef26..7c81b31748ff 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -51,7 +51,7 @@ static struct snd_pcm_hardware azx_pcm_hw = {
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 1,
- .channels_max = HDA_QUAD,
+ .channels_max = 8,
.buffer_bytes_max = AZX_MAX_BUF_SIZE,
.period_bytes_min = 128,
.period_bytes_max = AZX_MAX_BUF_SIZE / 2,
@@ -213,7 +213,7 @@ static int skl_be_prepare(struct snd_pcm_substream *substream,
struct skl_sst *ctx = skl->skl_sst;
struct skl_module_cfg *mconfig;
- if ((dai->playback_active > 1) || (dai->capture_active > 1))
+ if (dai->playback_widget->power || dai->capture_widget->power)
return 0;
mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream);
@@ -402,23 +402,33 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
struct skl_module_cfg *mconfig;
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+ struct snd_soc_dapm_widget *w;
int ret;
mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
if (!mconfig)
return -EIO;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ w = dai->playback_widget;
+ else
+ w = dai->capture_widget;
+
switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
- skl_pcm_prepare(substream, dai);
- /*
- * enable DMA Resume enable bit for the stream, set the dpib
- * & lpib position to resune before starting the DMA
- */
- snd_hdac_ext_stream_drsm_enable(ebus, true,
- hdac_stream(stream)->index);
- snd_hdac_ext_stream_set_dpibr(ebus, stream, stream->dpib);
- snd_hdac_ext_stream_set_lpib(stream, stream->lpib);
+ if (!w->ignore_suspend) {
+ skl_pcm_prepare(substream, dai);
+ /*
+ * enable DMA Resume enable bit for the stream, set the
+ * dpib & lpib position to resume before starting the
+ * DMA
+ */
+ snd_hdac_ext_stream_drsm_enable(ebus, true,
+ hdac_stream(stream)->index);
+ snd_hdac_ext_stream_set_dpibr(ebus, stream,
+ stream->dpib);
+ snd_hdac_ext_stream_set_lpib(stream, stream->lpib);
+ }
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
@@ -448,7 +458,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
return ret;
ret = skl_decoupled_trigger(substream, cmd);
- if (cmd == SNDRV_PCM_TRIGGER_SUSPEND) {
+ if ((cmd == SNDRV_PCM_TRIGGER_SUSPEND) && !w->ignore_suspend) {
/* save the dpib and lpib positions */
stream->dpib = readl(ebus->bus.remap_addr +
AZX_REG_VS_SDXDPIB_XBASE +
@@ -523,7 +533,6 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
if (!link)
return -EINVAL;
- snd_hdac_ext_bus_link_power_up(link);
snd_hdac_ext_link_stream_reset(link_dev);
snd_hdac_ext_link_stream_setup(link_dev, format_val);
@@ -682,7 +691,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.playback = {
.stream_name = "HDMI1 Playback",
.channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
+ .channels_max = 8,
.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
@@ -697,7 +706,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.playback = {
.stream_name = "HDMI2 Playback",
.channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
+ .channels_max = 8,
.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
@@ -712,7 +721,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.playback = {
.stream_name = "HDMI3 Playback",
.channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
+ .channels_max = 8,
.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
@@ -760,12 +769,84 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
},
},
{
+ .name = "SSP2 Pin",
+ .ops = &skl_be_ssp_dai_ops,
+ .playback = {
+ .stream_name = "ssp2 Tx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp2 Rx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "SSP3 Pin",
+ .ops = &skl_be_ssp_dai_ops,
+ .playback = {
+ .stream_name = "ssp3 Tx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp3 Rx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "SSP4 Pin",
+ .ops = &skl_be_ssp_dai_ops,
+ .playback = {
+ .stream_name = "ssp4 Tx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp4 Rx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "SSP5 Pin",
+ .ops = &skl_be_ssp_dai_ops,
+ .playback = {
+ .stream_name = "ssp5 Tx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp5 Rx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
.name = "iDisp1 Pin",
.ops = &skl_link_dai_ops,
.playback = {
.stream_name = "iDisp1 Tx",
.channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
+ .channels_max = 8,
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
SNDRV_PCM_FMTBIT_S24_LE,
@@ -777,7 +858,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.playback = {
.stream_name = "iDisp2 Tx",
.channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
+ .channels_max = 8,
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
@@ -790,7 +871,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.playback = {
.stream_name = "iDisp3 Tx",
.channels_min = HDA_STEREO,
- .channels_max = HDA_STEREO,
+ .channels_max = 8,
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c
index 2962ef22fc84..13c19855ee1a 100644
--- a/sound/soc/intel/skylake/skl-sst-dsp.c
+++ b/sound/soc/intel/skylake/skl-sst-dsp.c
@@ -336,8 +336,6 @@ void skl_dsp_free(struct sst_dsp *dsp)
skl_ipc_int_disable(dsp);
free_irq(dsp->irq, dsp);
- dsp->cl_dev.ops.cl_cleanup_controller(dsp);
- skl_cldma_int_disable(dsp);
skl_ipc_op_int_disable(dsp);
skl_ipc_int_disable(dsp);
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h
index b6e310d49dd6..deabe7308d3b 100644
--- a/sound/soc/intel/skylake/skl-sst-dsp.h
+++ b/sound/soc/intel/skylake/skl-sst-dsp.h
@@ -118,16 +118,25 @@ struct skl_dsp_fw_ops {
int (*set_state_D0)(struct sst_dsp *ctx);
int (*set_state_D3)(struct sst_dsp *ctx);
unsigned int (*get_fw_errcode)(struct sst_dsp *ctx);
- int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, char *mod_name);
+ int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name);
int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id);
};
struct skl_dsp_loader_ops {
+ int stream_tag;
+
int (*alloc_dma_buf)(struct device *dev,
struct snd_dma_buffer *dmab, size_t size);
int (*free_dma_buf)(struct device *dev,
struct snd_dma_buffer *dmab);
+ int (*prepare)(struct device *dev, unsigned int format,
+ unsigned int byte_size,
+ struct snd_dma_buffer *bufp);
+ int (*trigger)(struct device *dev, bool start, int stream_tag);
+
+ int (*cleanup)(struct device *dev, struct snd_dma_buffer *dmab,
+ int stream_tag);
};
struct skl_load_module_info {
@@ -160,6 +169,10 @@ int skl_dsp_boot(struct sst_dsp *ctx);
int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
struct skl_sst **dsp);
+int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
+ const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
+ struct skl_sst **dsp);
void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
+void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
#endif /*__SKL_SST_DSP_H__*/
diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c
index 348a734f8e24..13ec8d53b526 100644
--- a/sound/soc/intel/skylake/skl-sst.c
+++ b/sound/soc/intel/skylake/skl-sst.c
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/uuid.h>
#include "../common/sst-dsp.h"
#include "../common/sst-dsp-priv.h"
#include "../common/sst-ipc.h"
@@ -304,14 +305,16 @@ static int skl_transfer_module(struct sst_dsp *ctx,
return ret;
}
-static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, char *guid)
+static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, u8 *guid)
{
struct skl_module_table *module_entry = NULL;
int ret = 0;
char mod_name[64]; /* guid str = 32 chars + 4 hyphens */
+ uuid_le *uuid_mod;
- snprintf(mod_name, sizeof(mod_name), "%s%s%s",
- "intel/dsp_fw_", guid, ".bin");
+ uuid_mod = (uuid_le *)guid;
+ snprintf(mod_name, sizeof(mod_name), "%s%pUL%s",
+ "intel/dsp_fw_", uuid_mod, ".bin");
module_entry = skl_module_get_from_id(ctx, mod_id);
if (module_entry == NULL) {
@@ -451,6 +454,10 @@ void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
skl_clear_module_table(ctx->dsp);
skl_ipc_free(&ctx->ipc);
ctx->dsp->ops->free(ctx->dsp);
+ if (ctx->boot_complete) {
+ ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp);
+ skl_cldma_int_disable(ctx->dsp);
+ }
}
EXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup);
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
index cdb78b7e5a14..3e036b0349b9 100644
--- a/sound/soc/intel/skylake/skl-topology.c
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -154,13 +154,32 @@ static void skl_dump_mconfig(struct skl_sst *ctx,
dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt[0].ch_cfg);
}
+static void skl_tplg_update_chmap(struct skl_module_fmt *fmt, int chs)
+{
+ int slot_map = 0xFFFFFFFF;
+ int start_slot = 0;
+ int i;
+
+ for (i = 0; i < chs; i++) {
+ /*
+ * For 2 channels with starting slot as 0, slot map will
+ * look like 0xFFFFFF10.
+ */
+ slot_map &= (~(0xF << (4 * i)) | (start_slot << (4 * i)));
+ start_slot++;
+ }
+ fmt->ch_map = slot_map;
+}
+
static void skl_tplg_update_params(struct skl_module_fmt *fmt,
struct skl_pipe_params *params, int fixup)
{
if (fixup & SKL_RATE_FIXUP_MASK)
fmt->s_freq = params->s_freq;
- if (fixup & SKL_CH_FIXUP_MASK)
+ if (fixup & SKL_CH_FIXUP_MASK) {
fmt->channels = params->ch;
+ skl_tplg_update_chmap(fmt, fmt->channels);
+ }
if (fixup & SKL_FMT_FIXUP_MASK) {
fmt->valid_bit_depth = skl_get_bit_depth(params->s_fmt);
@@ -1564,6 +1583,8 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
return -ENOMEM;
w->priv = mconfig;
+ memcpy(&mconfig->guid, &dfw_config->uuid, 16);
+
mconfig->id.module_id = dfw_config->module_id;
mconfig->id.instance_id = dfw_config->instance_id;
mconfig->mcps = dfw_config->max_mcps;
@@ -1593,10 +1614,6 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
mconfig->time_slot = dfw_config->time_slot;
mconfig->formats_config.caps_size = dfw_config->caps.caps_size;
- if (dfw_config->is_loadable)
- memcpy(mconfig->guid, dfw_config->uuid,
- ARRAY_SIZE(dfw_config->uuid));
-
mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) *
sizeof(*mconfig->m_in_pin),
GFP_KERNEL);
diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h
index d2d923002d5c..e4b399cd7868 100644
--- a/sound/soc/intel/skylake/skl-topology.h
+++ b/sound/soc/intel/skylake/skl-topology.h
@@ -281,7 +281,7 @@ enum skl_module_state {
};
struct skl_module_cfg {
- char guid[SKL_UUID_STR_SZ];
+ u8 guid[16];
struct skl_module_inst_id id;
u8 domain;
bool homogenous_inputs;
diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h
index 1db88a63ac17..a32e5e9cc530 100644
--- a/sound/soc/intel/skylake/skl-tplg-interface.h
+++ b/sound/soc/intel/skylake/skl-tplg-interface.h
@@ -181,7 +181,7 @@ struct skl_dfw_pipe {
} __packed;
struct skl_dfw_module {
- char uuid[SKL_UUID_STR_SZ];
+ u8 uuid[16];
u16 module_id;
u16 instance_id;
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index 3982f5536f2d..06d8c263c68f 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -229,7 +229,12 @@ static int skl_suspend(struct device *dev)
* running, we need to save the state for these and continue
*/
if (skl->supend_active) {
+ /* turn off the links and stop the CORB/RIRB DMA if it is On */
snd_hdac_ext_bus_link_power_down_all(ebus);
+
+ if (ebus->cmd_dma_state)
+ snd_hdac_bus_stop_cmd_io(&ebus->bus);
+
enable_irq_wake(bus->irq);
pci_save_state(pci);
pci_disable_device(pci);
@@ -255,6 +260,7 @@ static int skl_resume(struct device *dev)
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
struct skl *skl = ebus_to_skl(ebus);
struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct hdac_ext_link *hlink = NULL;
int ret;
/* Turned OFF in HDMI codec driver after codec reconfiguration */
@@ -276,8 +282,29 @@ static int skl_resume(struct device *dev)
ret = pci_enable_device(pci);
snd_hdac_ext_bus_link_power_up_all(ebus);
disable_irq_wake(bus->irq);
+ /*
+ * turn On the links which are On before active suspend
+ * and start the CORB/RIRB DMA if On before
+ * active suspend.
+ */
+ list_for_each_entry(hlink, &ebus->hlink_list, list) {
+ if (hlink->ref_count)
+ snd_hdac_ext_bus_link_power_up(hlink);
+ }
+
+ if (ebus->cmd_dma_state)
+ snd_hdac_bus_init_cmd_io(&ebus->bus);
} else {
ret = _skl_resume(ebus);
+
+ /* turn off the links which are off before suspend */
+ list_for_each_entry(hlink, &ebus->hlink_list, list) {
+ if (!hlink->ref_count)
+ snd_hdac_ext_bus_link_power_down(hlink);
+ }
+
+ if (!ebus->cmd_dma_state)
+ snd_hdac_bus_stop_cmd_io(&ebus->bus);
}
return ret;
@@ -613,6 +640,7 @@ static int skl_probe(struct pci_dev *pci,
struct skl *skl;
struct hdac_ext_bus *ebus = NULL;
struct hdac_bus *bus = NULL;
+ struct hdac_ext_link *hlink = NULL;
int err;
/* we use ext core ops, so provide NULL for ops here */
@@ -643,7 +671,7 @@ static int skl_probe(struct pci_dev *pci,
err = skl_machine_device_register(skl,
(void *)pci_id->driver_data);
if (err < 0)
- goto out_free;
+ goto out_nhlt_free;
err = skl_init_dsp(skl);
if (err < 0) {
@@ -679,6 +707,12 @@ static int skl_probe(struct pci_dev *pci,
}
}
+ /*
+ * we are done probling so decrement link counts
+ */
+ list_for_each_entry(hlink, &ebus->hlink_list, list)
+ snd_hdac_ext_bus_link_put(ebus, hlink);
+
/*configure PM */
pm_runtime_put_noidle(bus->dev);
pm_runtime_allow(bus->dev);
@@ -693,6 +727,8 @@ out_dsp_free:
skl_free_dsp(skl);
out_mach_free:
skl_machine_device_unregister(skl);
+out_nhlt_free:
+ skl_nhlt_free(skl->nhlt);
out_free:
skl->init_failed = 1;
skl_free(ebus);
@@ -743,6 +779,7 @@ static void skl_remove(struct pci_dev *pci)
skl_free_dsp(skl);
skl_machine_device_unregister(skl);
skl_dmic_device_unregister(skl);
+ skl_nhlt_free(skl->nhlt);
skl_free(ebus);
dev_set_drvdata(&pci->dev, NULL);
}
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
index 39e16fa7a92b..4b4b3876aea9 100644
--- a/sound/soc/intel/skylake/skl.h
+++ b/sound/soc/intel/skylake/skl.h
@@ -66,7 +66,7 @@ struct skl {
struct platform_device *dmic_dev;
struct platform_device *i2s_dev;
- void *nhlt; /* nhlt ptr */
+ struct nhlt_acpi_table *nhlt; /* nhlt ptr */
struct skl_sst *skl_sst; /* sst skl ctx */
struct skl_dsp_resource resource;
@@ -103,8 +103,8 @@ struct skl_dsp_ops {
int skl_platform_unregister(struct device *dev);
int skl_platform_register(struct device *dev);
-void *skl_nhlt_init(struct device *dev);
-void skl_nhlt_free(void *addr);
+struct nhlt_acpi_table *skl_nhlt_init(struct device *dev);
+void skl_nhlt_free(struct nhlt_acpi_table *addr);
struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance,
u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn);