aboutsummaryrefslogtreecommitdiff
path: root/sound/soc/sof/intel
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sof/intel')
-rw-r--r--sound/soc/sof/intel/hda-dai.c4
-rw-r--r--sound/soc/sof/intel/hda-dsp.c5
-rw-r--r--sound/soc/sof/intel/hda-loader.c100
-rw-r--r--sound/soc/sof/intel/hda-mlink.c18
-rw-r--r--sound/soc/sof/intel/hda-stream.c32
-rw-r--r--sound/soc/sof/intel/hda.c27
-rw-r--r--sound/soc/sof/intel/hda.h14
-rw-r--r--sound/soc/sof/intel/lnl.c10
8 files changed, 152 insertions, 58 deletions
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index ac505c7ad342..ee274d445515 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -646,6 +646,10 @@ static int hda_dai_suspend(struct hdac_bus *bus)
sdai = swidget->private;
ops = sdai->platform_private;
+ if (rtd->dpcm[hext_stream->link_substream->stream].state !=
+ SND_SOC_DPCM_STATE_PAUSED)
+ continue;
+
/* for consistency with TRIGGER_SUSPEND */
if (ops->post_trigger) {
ret = ops->post_trigger(sdev, cpu_dai,
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index 4c88522d4048..6028a80418bb 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -858,7 +858,6 @@ skip_dsp:
static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
{
- const struct sof_intel_dsp_desc *chip;
int ret;
/* display codec must be powered before link reset */
@@ -891,10 +890,6 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
hda_dsp_ctrl_ppcap_int_enable(sdev, true);
}
- chip = get_chip_info(sdev->pdata);
- if (chip && chip->hw_ip_version >= SOF_INTEL_ACE_2_0)
- hda_sdw_int_enable(sdev, true);
-
cleanup:
/* display codec can powered off after controller init */
hda_codec_i915_display_power(sdev, false);
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index 9d8ebb7c6a10..76a03b6b2728 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -26,6 +26,11 @@
#include "../sof-priv.h"
#include "hda.h"
+static bool persistent_cl_buffer = true;
+module_param(persistent_cl_buffer, bool, 0444);
+MODULE_PARM_DESC(persistent_cl_buffer, "Persistent Code Loader DMA buffer "
+ "(default = Y, use N to force buffer re-allocation)");
+
static void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
@@ -43,9 +48,10 @@ static void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev)
}
}
-struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format,
- unsigned int size, struct snd_dma_buffer *dmab,
- int direction, bool is_iccmax)
+struct hdac_ext_stream*
+hda_cl_prepare(struct device *dev, unsigned int format, unsigned int size,
+ struct snd_dma_buffer *dmab, bool persistent_buffer, int direction,
+ bool is_iccmax)
{
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct hdac_ext_stream *hext_stream;
@@ -61,11 +67,19 @@ struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format,
hstream = &hext_stream->hstream;
hstream->substream = NULL;
- /* allocate DMA buffer */
- ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab);
- if (ret < 0) {
- dev_err(sdev->dev, "error: memory alloc failed: %d\n", ret);
- goto out_put;
+ /*
+ * Allocate DMA buffer if it is temporary or if the buffer is intended
+ * to be persistent but not yet allocated.
+ * We cannot rely solely on !dmab->area as caller might use a struct on
+ * stack (when it is temporary) without clearing it to 0.
+ */
+ if (!persistent_buffer || !dmab->area) {
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: memory alloc failed: %d\n",
+ __func__, ret);
+ goto out_put;
+ }
}
hstream->period_bytes = 0;/* initialize period_bytes */
@@ -91,6 +105,10 @@ struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format,
out_free:
snd_dma_free_pages(dmab);
+ dmab->area = NULL;
+ dmab->bytes = 0;
+ hstream->bufsize = 0;
+ hstream->format_val = 0;
out_put:
hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
return ERR_PTR(ret);
@@ -255,7 +273,7 @@ int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int
EXPORT_SYMBOL_NS(hda_cl_trigger, SND_SOC_SOF_INTEL_HDA_COMMON);
int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
- struct hdac_ext_stream *hext_stream)
+ bool persistent_buffer, struct hdac_ext_stream *hext_stream)
{
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct hdac_stream *hstream = &hext_stream->hstream;
@@ -279,10 +297,14 @@ int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0);
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0);
- snd_dma_free_pages(dmab);
- dmab->area = NULL;
- hstream->bufsize = 0;
- hstream->format_val = 0;
+
+ if (!persistent_buffer) {
+ snd_dma_free_pages(dmab);
+ dmab->area = NULL;
+ dmab->bytes = 0;
+ hstream->bufsize = 0;
+ hstream->format_val = 0;
+ }
return ret;
}
@@ -340,8 +362,8 @@ int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream
int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct hdac_ext_stream *iccmax_stream;
- struct snd_dma_buffer dmab_bdl;
int ret, ret1;
u8 original_gb;
@@ -354,7 +376,8 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
* the data, so use a buffer of PAGE_SIZE for receiving.
*/
iccmax_stream = hda_cl_prepare(sdev->dev, HDA_CL_STREAM_FORMAT, PAGE_SIZE,
- &dmab_bdl, SNDRV_PCM_STREAM_CAPTURE, true);
+ &hda->iccmax_dmab, persistent_cl_buffer,
+ SNDRV_PCM_STREAM_CAPTURE, true);
if (IS_ERR(iccmax_stream)) {
dev_err(sdev->dev, "error: dma prepare for ICCMAX stream failed\n");
return PTR_ERR(iccmax_stream);
@@ -366,7 +389,8 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
* Perform iccmax stream cleanup. This should be done even if firmware loading fails.
* If the cleanup also fails, we return the initial error
*/
- ret1 = hda_cl_cleanup(sdev->dev, &dmab_bdl, iccmax_stream);
+ ret1 = hda_cl_cleanup(sdev->dev, &hda->iccmax_dmab,
+ persistent_cl_buffer, iccmax_stream);
if (ret1 < 0) {
dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n");
@@ -408,7 +432,6 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
const struct sof_intel_dsp_desc *chip_info;
struct hdac_ext_stream *hext_stream;
struct firmware stripped_firmware;
- struct snd_dma_buffer dmab;
int ret, ret1, i;
if (hda->imrboot_supported && !sdev->first_boot && !hda->skip_imr_boot) {
@@ -432,23 +455,31 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
return -EINVAL;
}
- stripped_firmware.data = sdev->basefw.fw->data + sdev->basefw.payload_offset;
- stripped_firmware.size = sdev->basefw.fw->size - sdev->basefw.payload_offset;
-
/* init for booting wait */
init_waitqueue_head(&sdev->boot_wait);
/* prepare DMA for code loader stream */
+ stripped_firmware.size = sdev->basefw.fw->size - sdev->basefw.payload_offset;
hext_stream = hda_cl_prepare(sdev->dev, HDA_CL_STREAM_FORMAT,
stripped_firmware.size,
- &dmab, SNDRV_PCM_STREAM_PLAYBACK, false);
+ &hda->cl_dmab, persistent_cl_buffer,
+ SNDRV_PCM_STREAM_PLAYBACK, false);
if (IS_ERR(hext_stream)) {
dev_err(sdev->dev, "error: dma prepare for fw loading failed\n");
return PTR_ERR(hext_stream);
}
- memcpy(dmab.area, stripped_firmware.data,
- stripped_firmware.size);
+ /*
+ * Copy the payload to the DMA buffer if it is temporary or if the
+ * buffer is persistent but it does not have the basefw payload either
+ * because this is the first boot and the buffer needs to be initialized,
+ * or a library got loaded and it replaced the basefw.
+ */
+ if (!persistent_cl_buffer || !hda->cl_dmab_contains_basefw) {
+ stripped_firmware.data = sdev->basefw.fw->data + sdev->basefw.payload_offset;
+ memcpy(hda->cl_dmab.area, stripped_firmware.data, stripped_firmware.size);
+ hda->cl_dmab_contains_basefw = true;
+ }
/* try ROM init a few times before giving up */
for (i = 0; i < HDA_FW_BOOT_ATTEMPTS; i++) {
@@ -514,7 +545,8 @@ cleanup:
* This should be done even if firmware loading fails.
* If the cleanup also fails, we return the initial error
*/
- ret1 = hda_cl_cleanup(sdev->dev, &dmab, hext_stream);
+ ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab,
+ persistent_cl_buffer, hext_stream);
if (ret1 < 0) {
dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n");
@@ -545,7 +577,6 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
struct hdac_ext_stream *hext_stream;
struct firmware stripped_firmware;
struct sof_ipc4_msg msg = {};
- struct snd_dma_buffer dmab;
int ret, ret1;
/* if IMR booting is enabled and fw context is saved for D3 state, skip the loading */
@@ -556,16 +587,28 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
stripped_firmware.data = fw_lib->sof_fw.fw->data + fw_lib->sof_fw.payload_offset;
stripped_firmware.size = fw_lib->sof_fw.fw->size - fw_lib->sof_fw.payload_offset;
+ /*
+ * force re-allocation of the cl_dmab if the preserved DMA buffer is
+ * smaller than what is needed for the library
+ */
+ if (persistent_cl_buffer && stripped_firmware.size > hda->cl_dmab.bytes) {
+ snd_dma_free_pages(&hda->cl_dmab);
+ hda->cl_dmab.area = NULL;
+ hda->cl_dmab.bytes = 0;
+ }
+
/* prepare DMA for code loader stream */
hext_stream = hda_cl_prepare(sdev->dev, HDA_CL_STREAM_FORMAT,
stripped_firmware.size,
- &dmab, SNDRV_PCM_STREAM_PLAYBACK, false);
+ &hda->cl_dmab, persistent_cl_buffer,
+ SNDRV_PCM_STREAM_PLAYBACK, false);
if (IS_ERR(hext_stream)) {
dev_err(sdev->dev, "%s: DMA prepare failed\n", __func__);
return PTR_ERR(hext_stream);
}
- memcpy(dmab.area, stripped_firmware.data, stripped_firmware.size);
+ memcpy(hda->cl_dmab.area, stripped_firmware.data, stripped_firmware.size);
+ hda->cl_dmab_contains_basefw = false;
/*
* 1st stage: SOF_IPC4_GLB_LOAD_LIBRARY_PREPARE
@@ -628,7 +671,8 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
cleanup:
/* clean up even in case of error and return the first error */
- ret1 = hda_cl_cleanup(sdev->dev, &dmab, hext_stream);
+ ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, persistent_cl_buffer,
+ hext_stream);
if (ret1 < 0) {
dev_err(sdev->dev, "%s: Code loader DSP cleanup failed\n", __func__);
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c
index 9a3559c78b62..46f89d6d06f8 100644
--- a/sound/soc/sof/intel/hda-mlink.c
+++ b/sound/soc/sof/intel/hda-mlink.c
@@ -481,6 +481,24 @@ int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid)
}
EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, SND_SOC_SOF_HDA_MLINK);
+void hdac_bus_eml_enable_interrupt_unlocked(struct hdac_bus *bus, bool alt, int elid, bool enable)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return;
+
+ if (!h2link->intc)
+ return;
+
+ hlink = &h2link->hext_link;
+
+ hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt_unlocked, SND_SOC_SOF_HDA_MLINK);
+
void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable)
{
struct hdac_ext2_link *h2link;
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index 3ac63ce67ab1..519bafd3b947 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -119,13 +119,39 @@ int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev,
int remain, ioc;
period_bytes = hstream->period_bytes;
- dev_dbg(sdev->dev, "period_bytes:0x%x\n", period_bytes);
- if (!period_bytes)
+ dev_dbg(sdev->dev, "period_bytes: %#x, bufsize: %#x\n", period_bytes,
+ hstream->bufsize);
+
+ if (!period_bytes) {
+ unsigned int chunk_size;
+
+ chunk_size = snd_sgbuf_get_chunk_size(dmab, 0, hstream->bufsize);
+
period_bytes = hstream->bufsize;
+ /*
+ * HDA spec demands that the LVI value must be at least one
+ * before the DMA operation can begin. This means that there
+ * must be at least two BDLE present for the transfer.
+ *
+ * If the buffer is not a single continuous area then the
+ * hda_setup_bdle() will create multiple BDLEs for each segment.
+ * If the memory is a single continuous area, force it to be
+ * split into two 'periods', otherwise the transfer will be
+ * split to multiple BDLE for each chunk in hda_setup_bdle()
+ *
+ * Note: period_bytes == 0 can only happen for firmware or
+ * library loading. The data size is 4K aligned, which ensures
+ * that the second chunk's start address will be 128-byte
+ * aligned.
+ */
+ if (chunk_size == hstream->bufsize)
+ period_bytes /= 2;
+ }
+
periods = hstream->bufsize / period_bytes;
- dev_dbg(sdev->dev, "periods:%d\n", periods);
+ dev_dbg(sdev->dev, "periods: %d\n", periods);
remain = hstream->bufsize % period_bytes;
if (remain)
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 70fc08c8fc99..01b135068b1f 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -866,8 +866,6 @@ skip_dsp_setup:
dev_err(sdev->dev, "could not startup SoundWire links\n");
goto disable_pp_cap;
}
-
- hda_sdw_int_enable(sdev, true);
}
init_waitqueue_head(&hdev->waitq);
@@ -938,6 +936,12 @@ void hda_dsp_remove(struct snd_sof_dev *sdev)
/* disable DSP */
hda_dsp_ctrl_ppcap_enable(sdev, false);
+ /* Free the persistent DMA buffers used for base firmware download */
+ if (hda->cl_dmab.area)
+ snd_dma_free_pages(&hda->cl_dmab);
+ if (hda->iccmax_dmab.area)
+ snd_dma_free_pages(&hda->iccmax_dmab);
+
skip_disable_dsp:
free_irq(sdev->ipc_irq, sdev);
if (sdev->msi_enabled)
@@ -1066,7 +1070,7 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev
{
struct snd_sof_pdata *pdata = sdev->pdata;
const struct snd_soc_acpi_link_adr *link;
- struct sdw_extended_slave_id *ids;
+ struct sdw_peripherals *peripherals;
struct snd_soc_acpi_mach *mach;
struct sof_intel_hda_dev *hdev;
u32 link_mask;
@@ -1085,7 +1089,7 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev
return NULL;
}
- if (!hdev->sdw->num_slaves) {
+ if (!hdev->sdw->peripherals || !hdev->sdw->peripherals->num_peripherals) {
dev_warn(sdev->dev, "No SoundWire peripheral detected in ACPI tables\n");
return NULL;
}
@@ -1121,13 +1125,13 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev
* are not found on this link.
*/
if (!snd_soc_acpi_sdw_link_slaves_found(sdev->dev, link,
- hdev->sdw->ids,
- hdev->sdw->num_slaves))
+ hdev->sdw->peripherals))
break;
}
/* Found if all Slaves are checked */
if (i == hdev->info.count || !link->num_adr)
- break;
+ if (!mach->machine_check || mach->machine_check(hdev->sdw))
+ break;
}
if (mach && mach->link_mask) {
mach->mach_params.links = mach->links;
@@ -1138,10 +1142,13 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev
}
dev_info(sdev->dev, "No SoundWire machine driver found for the ACPI-reported configuration:\n");
- ids = hdev->sdw->ids;
- for (i = 0; i < hdev->sdw->num_slaves; i++)
+ peripherals = hdev->sdw->peripherals;
+ for (i = 0; i < peripherals->num_peripherals; i++)
dev_info(sdev->dev, "link %d mfg_id 0x%04x part_id 0x%04x version %#x\n",
- ids[i].link_id, ids[i].id.mfg_id, ids[i].id.part_id, ids[i].id.sdw_version);
+ peripherals->array[i]->bus->link_id,
+ peripherals->array[i]->id.mfg_id,
+ peripherals->array[i]->id.part_id,
+ peripherals->array[i]->id.sdw_version);
return NULL;
}
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index b74a472435b5..22bd9c3c8216 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -495,6 +495,15 @@ struct sof_intel_hda_dev {
int boot_iteration;
+ /*
+ * DMA buffers for base firmware download. By default the buffers are
+ * allocated once and kept through the lifetime of the driver.
+ * See module parameter: persistent_cl_buffer
+ */
+ struct snd_dma_buffer cl_dmab;
+ bool cl_dmab_contains_basefw;
+ struct snd_dma_buffer iccmax_dmab;
+
struct hda_bus hbus;
/* hw config */
@@ -714,11 +723,12 @@ int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream
struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format,
unsigned int size, struct snd_dma_buffer *dmab,
- int direction, bool is_iccmax);
+ bool persistent_buffer, int direction,
+ bool is_iccmax);
int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int cmd);
int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
- struct hdac_ext_stream *hext_stream);
+ bool persistent_buffer, struct hdac_ext_stream *hext_stream);
int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot);
#define HDA_CL_STREAM_FORMAT 0x40
diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c
index 3d5a1f8b17e5..e3c4b4a0d705 100644
--- a/sound/soc/sof/intel/lnl.c
+++ b/sound/soc/sof/intel/lnl.c
@@ -192,16 +192,8 @@ static bool lnl_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
return hdac_bus_eml_check_interrupt(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
}
-static void lnl_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable)
-{
- struct hdac_bus *bus = sof_to_bus(sdev);
-
- hdac_bus_eml_enable_interrupt(bus, true, AZX_REG_ML_LEPTR_ID_SDW, enable);
-}
-
static int lnl_dsp_disable_interrupts(struct snd_sof_dev *sdev)
{
- lnl_enable_sdw_irq(sdev, false);
mtl_disable_ipc_interrupts(sdev);
return mtl_enable_interrupts(sdev, false);
}
@@ -237,7 +229,6 @@ const struct sof_intel_dsp_desc lnl_chip_info = {
.ssp_count = MTL_SSP_COUNT,
.d0i3_offset = MTL_HDA_VS_D0I3C,
.read_sdw_lcount = hda_sdw_check_lcount_ext,
- .enable_sdw_irq = lnl_enable_sdw_irq,
.check_sdw_irq = lnl_dsp_check_sdw_irq,
.check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq,
.sdw_process_wakeen = hda_sdw_process_wakeen_common,
@@ -262,7 +253,6 @@ const struct sof_intel_dsp_desc ptl_chip_info = {
.ssp_count = MTL_SSP_COUNT,
.d0i3_offset = MTL_HDA_VS_D0I3C,
.read_sdw_lcount = hda_sdw_check_lcount_ext,
- .enable_sdw_irq = lnl_enable_sdw_irq,
.check_sdw_irq = lnl_dsp_check_sdw_irq,
.check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq,
.check_ipc_irq = mtl_dsp_check_ipc_irq,