Commit 2775f88b authored by Mark Brown's avatar Mark Brown

ASoC: SOF: Extend ChainDMA and DSPless mode to LNL+

Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>:

For both ChainDMA and DSPless mode the requirement is that the link must
be serviced by HD-DMA.
On pre Lunar Lake platforms this was only valid for HDAudio links but with
Lunar Lake all link types now serviced by HD-DMA.

This allows us to enable ChainDMA and DSPless mode for SoundWire links as
well.
parents fd236653 2065610b
...@@ -83,6 +83,7 @@ struct snd_sof_dsp_ops sof_hda_common_ops = { ...@@ -83,6 +83,7 @@ struct snd_sof_dsp_ops sof_hda_common_ops = {
/* DAI drivers */ /* DAI drivers */
.drv = skl_dai, .drv = skl_dai,
.num_drv = SOF_SKL_NUM_DAIS, .num_drv = SOF_SKL_NUM_DAIS,
.is_chain_dma_supported = hda_is_chain_dma_supported,
/* PM */ /* PM */
.suspend = hda_dsp_suspend, .suspend = hda_dsp_suspend,
......
...@@ -522,6 +522,17 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { ...@@ -522,6 +522,17 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
.get_hlink = hda_get_hlink, .get_hlink = hda_get_hlink,
}; };
static const struct hda_dai_widget_dma_ops sdw_ipc4_chain_dma_ops = {
.get_hext_stream = hda_get_hext_stream,
.assign_hext_stream = hda_assign_hext_stream,
.release_hext_stream = hda_release_hext_stream,
.setup_hext_stream = hda_setup_hext_stream,
.reset_hext_stream = hda_reset_hext_stream,
.trigger = hda_trigger,
.calc_stream_format = generic_calc_stream_format,
.get_hlink = sdw_get_hlink,
};
static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
struct snd_pcm_substream *substream, int cmd) struct snd_pcm_substream *substream, int cmd)
{ {
...@@ -596,6 +607,13 @@ static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = { ...@@ -596,6 +607,13 @@ static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
.get_hlink = hda_get_hlink, .get_hlink = hda_get_hlink,
}; };
static const struct hda_dai_widget_dma_ops sdw_dspless_dma_ops = {
.get_hext_stream = hda_dspless_get_hext_stream,
.setup_hext_stream = hda_dspless_setup_hext_stream,
.calc_stream_format = generic_calc_stream_format,
.get_hlink = sdw_get_hlink,
};
#endif #endif
const struct hda_dai_widget_dma_ops * const struct hda_dai_widget_dma_ops *
...@@ -603,12 +621,24 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg ...@@ -603,12 +621,24 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg
{ {
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
struct snd_sof_dai *sdai; struct snd_sof_dai *sdai;
const struct sof_intel_dsp_desc *chip;
if (sdev->dspless_mode_selected) chip = get_chip_info(sdev->pdata);
return &hda_dspless_dma_ops;
sdai = swidget->private; sdai = swidget->private;
if (sdev->dspless_mode_selected) {
switch (sdai->type) {
case SOF_DAI_INTEL_HDA:
return &hda_dspless_dma_ops;
case SOF_DAI_INTEL_ALH:
if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
return NULL;
return &sdw_dspless_dma_ops;
default:
return NULL;
}
}
switch (sdev->pdata->ipc_type) { switch (sdev->pdata->ipc_type) {
case SOF_IPC_TYPE_3: case SOF_IPC_TYPE_3:
{ {
...@@ -620,22 +650,15 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg ...@@ -620,22 +650,15 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg
} }
case SOF_IPC_TYPE_4: case SOF_IPC_TYPE_4:
{ {
struct sof_ipc4_copier *ipc4_copier = sdai->private; struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
const struct sof_intel_dsp_desc *chip; struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
chip = get_chip_info(sdev->pdata);
switch (ipc4_copier->dai_type) { switch (sdai->type) {
case SOF_DAI_INTEL_HDA: case SOF_DAI_INTEL_HDA:
{
struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
if (pipeline->use_chain_dma) if (pipeline->use_chain_dma)
return &hda_ipc4_chain_dma_ops; return &hda_ipc4_chain_dma_ops;
return &hda_ipc4_dma_ops; return &hda_ipc4_dma_ops;
}
case SOF_DAI_INTEL_SSP: case SOF_DAI_INTEL_SSP:
if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
return NULL; return NULL;
...@@ -647,6 +670,8 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg ...@@ -647,6 +670,8 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg
case SOF_DAI_INTEL_ALH: case SOF_DAI_INTEL_ALH:
if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
return NULL; return NULL;
if (pipeline->use_chain_dma)
return &sdw_ipc4_chain_dma_ops;
return &sdw_ipc4_dma_ops; return &sdw_ipc4_dma_ops;
default: default:
......
...@@ -83,12 +83,13 @@ hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai ...@@ -83,12 +83,13 @@ hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai
sdev = widget_to_sdev(w); sdev = widget_to_sdev(w);
/* if (!swidget) {
* The swidget parameter of hda_select_dai_widget_ops() is ignored in dev_err(sdev->dev, "%s: swidget is NULL\n", __func__);
* case of DSPless mode return NULL;
*/ }
if (sdev->dspless_mode_selected) if (sdev->dspless_mode_selected)
return hda_select_dai_widget_ops(sdev, NULL); return hda_select_dai_widget_ops(sdev, swidget);
sdai = swidget->private; sdai = swidget->private;
...@@ -368,8 +369,11 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, ...@@ -368,8 +369,11 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
return ret; return ret;
} }
/* get stream_id */
sdev = widget_to_sdev(w); sdev = widget_to_sdev(w);
if (sdev->dspless_mode_selected)
goto skip_tlv;
/* get stream_id */
hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
if (!hext_stream) { if (!hext_stream) {
...@@ -402,6 +406,7 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, ...@@ -402,6 +406,7 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
dma_config->dma_stream_channel_map.device_count = 0; /* mapping not used */ dma_config->dma_stream_channel_map.device_count = 0; /* mapping not used */
dma_config->dma_priv_config_size = 0; dma_config->dma_priv_config_size = 0;
skip_tlv:
return 0; return 0;
} }
......
...@@ -748,6 +748,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) ...@@ -748,6 +748,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume) static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
{ {
const struct sof_intel_dsp_desc *chip;
int ret; int ret;
/* display codec must be powered before link reset */ /* display codec must be powered before link reset */
...@@ -780,6 +781,10 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume) ...@@ -780,6 +781,10 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
hda_dsp_ctrl_ppcap_int_enable(sdev, true); 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: cleanup:
/* display codec can powered off after controller init */ /* display codec can powered off after controller init */
hda_codec_i915_display_power(sdev, false); hda_codec_i915_display_power(sdev, false);
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <trace/events/sof_intel.h> #include <trace/events/sof_intel.h>
#include "../ops.h" #include "../ops.h"
#include "../sof-audio.h" #include "../sof-audio.h"
#include "../ipc4-priv.h"
#include "hda.h" #include "hda.h"
#define HDA_LTRP_GB_VALUE_US 95 #define HDA_LTRP_GB_VALUE_US 95
...@@ -937,6 +938,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) ...@@ -937,6 +938,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
/* store total stream count (playback + capture) from GCAP */ /* store total stream count (playback + capture) from GCAP */
sof_hda->stream_max = num_total; sof_hda->stream_max = num_total;
/* store stream count from GCAP required for CHAIN_DMA */
if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
ipc4_data->num_playback_streams = num_playback;
ipc4_data->num_capture_streams = num_capture;
}
return 0; return 0;
} }
......
...@@ -46,44 +46,83 @@ ...@@ -46,44 +46,83 @@
#define EXCEPT_MAX_HDR_SIZE 0x400 #define EXCEPT_MAX_HDR_SIZE 0x400
#define HDA_EXT_ROM_STATUS_SIZE 8 #define HDA_EXT_ROM_STATUS_SIZE 8
static u32 hda_get_interface_mask(struct snd_sof_dev *sdev) static void hda_get_interfaces(struct snd_sof_dev *sdev, u32 *interface_mask)
{ {
const struct sof_intel_dsp_desc *chip; const struct sof_intel_dsp_desc *chip;
u32 interface_mask[2] = { 0 };
chip = get_chip_info(sdev->pdata); chip = get_chip_info(sdev->pdata);
switch (chip->hw_ip_version) { switch (chip->hw_ip_version) {
case SOF_INTEL_TANGIER: case SOF_INTEL_TANGIER:
case SOF_INTEL_BAYTRAIL: case SOF_INTEL_BAYTRAIL:
case SOF_INTEL_BROADWELL: case SOF_INTEL_BROADWELL:
interface_mask[0] = BIT(SOF_DAI_INTEL_SSP); interface_mask[SOF_DAI_DSP_ACCESS] = BIT(SOF_DAI_INTEL_SSP);
break; break;
case SOF_INTEL_CAVS_1_5: case SOF_INTEL_CAVS_1_5:
case SOF_INTEL_CAVS_1_5_PLUS: case SOF_INTEL_CAVS_1_5_PLUS:
interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | interface_mask[SOF_DAI_DSP_ACCESS] =
BIT(SOF_DAI_INTEL_HDA); BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | BIT(SOF_DAI_INTEL_HDA);
interface_mask[1] = BIT(SOF_DAI_INTEL_HDA); interface_mask[SOF_DAI_HOST_ACCESS] = BIT(SOF_DAI_INTEL_HDA);
break; break;
case SOF_INTEL_CAVS_1_8: case SOF_INTEL_CAVS_1_8:
case SOF_INTEL_CAVS_2_0: case SOF_INTEL_CAVS_2_0:
case SOF_INTEL_CAVS_2_5: case SOF_INTEL_CAVS_2_5:
case SOF_INTEL_ACE_1_0: case SOF_INTEL_ACE_1_0:
interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | interface_mask[SOF_DAI_DSP_ACCESS] =
BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
interface_mask[1] = BIT(SOF_DAI_INTEL_HDA); BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
interface_mask[SOF_DAI_HOST_ACCESS] = BIT(SOF_DAI_INTEL_HDA);
break; break;
case SOF_INTEL_ACE_2_0: case SOF_INTEL_ACE_2_0:
interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | interface_mask[SOF_DAI_DSP_ACCESS] =
BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
interface_mask[1] = interface_mask[0]; /* all interfaces accessible without DSP */ BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
/* all interfaces accessible without DSP */
interface_mask[SOF_DAI_HOST_ACCESS] =
interface_mask[SOF_DAI_DSP_ACCESS];
break; break;
default: default:
break; break;
} }
}
static u32 hda_get_interface_mask(struct snd_sof_dev *sdev)
{
u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 };
hda_get_interfaces(sdev, interface_mask);
return interface_mask[sdev->dspless_mode_selected]; return interface_mask[sdev->dspless_mode_selected];
} }
bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type)
{
u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 };
const struct sof_intel_dsp_desc *chip;
if (sdev->dspless_mode_selected)
return false;
hda_get_interfaces(sdev, interface_mask);
if (!(interface_mask[SOF_DAI_DSP_ACCESS] & BIT(dai_type)))
return false;
if (dai_type == SOF_DAI_INTEL_HDA)
return true;
switch (dai_type) {
case SOF_DAI_INTEL_SSP:
case SOF_DAI_INTEL_DMIC:
case SOF_DAI_INTEL_ALH:
chip = get_chip_info(sdev->pdata);
if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
return false;
return true;
default:
return false;
}
}
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
/* /*
...@@ -1192,6 +1231,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) ...@@ -1192,6 +1231,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
{ {
struct pci_dev *pci = to_pci_dev(sdev->dev); struct pci_dev *pci = to_pci_dev(sdev->dev);
struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip;
int ret = 0; int ret = 0;
hdev->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec", hdev->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec",
...@@ -1305,12 +1345,28 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) ...@@ -1305,12 +1345,28 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work); INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work);
} }
chip = get_chip_info(sdev->pdata);
if (chip && chip->hw_ip_version >= SOF_INTEL_ACE_2_0) {
ret = hda_sdw_startup(sdev);
if (ret < 0) {
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); init_waitqueue_head(&hdev->waitq);
hdev->nhlt = intel_nhlt_init(sdev->dev); hdev->nhlt = intel_nhlt_init(sdev->dev);
return 0; return 0;
disable_pp_cap:
if (!sdev->dspless_mode_selected) {
hda_dsp_ctrl_ppcap_int_enable(sdev, false);
hda_dsp_ctrl_ppcap_enable(sdev, false);
}
free_ipc_irq: free_ipc_irq:
free_irq(sdev->ipc_irq, sdev); free_irq(sdev->ipc_irq, sdev);
free_irq_vector: free_irq_vector:
......
...@@ -573,6 +573,11 @@ struct sof_intel_hda_stream { ...@@ -573,6 +573,11 @@ struct sof_intel_hda_stream {
#define SOF_STREAM_SD_OFFSET_CRST 0x1 #define SOF_STREAM_SD_OFFSET_CRST 0x1
/*
* DAI support
*/
bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type);
/* /*
* DSP Core services. * DSP Core services.
*/ */
......
...@@ -77,6 +77,19 @@ static int lnl_hda_dsp_runtime_resume(struct snd_sof_dev *sdev) ...@@ -77,6 +77,19 @@ static int lnl_hda_dsp_runtime_resume(struct snd_sof_dev *sdev)
return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev)); return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev));
} }
static int lnl_dsp_post_fw_run(struct snd_sof_dev *sdev)
{
if (sdev->first_boot) {
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
/* Check if IMR boot is usable */
if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT))
hda->imrboot_supported = true;
}
return 0;
}
int sof_lnl_ops_init(struct snd_sof_dev *sdev) int sof_lnl_ops_init(struct snd_sof_dev *sdev)
{ {
struct sof_ipc4_fw_data *ipc4_data; struct sof_ipc4_fw_data *ipc4_data;
...@@ -85,7 +98,8 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev) ...@@ -85,7 +98,8 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev)
memcpy(&sof_lnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); memcpy(&sof_lnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
/* probe */ /* probe */
sof_lnl_ops.probe = lnl_hda_dsp_probe; if (!sdev->dspless_mode_selected)
sof_lnl_ops.probe = lnl_hda_dsp_probe;
/* shutdown */ /* shutdown */
sof_lnl_ops.shutdown = hda_dsp_shutdown; sof_lnl_ops.shutdown = hda_dsp_shutdown;
...@@ -106,7 +120,7 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev) ...@@ -106,7 +120,7 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev)
/* pre/post fw run */ /* pre/post fw run */
sof_lnl_ops.pre_fw_run = mtl_dsp_pre_fw_run; sof_lnl_ops.pre_fw_run = mtl_dsp_pre_fw_run;
sof_lnl_ops.post_fw_run = mtl_dsp_post_fw_run; sof_lnl_ops.post_fw_run = lnl_dsp_post_fw_run;
/* parse platform specific extended manifest */ /* parse platform specific extended manifest */
sof_lnl_ops.parse_platform_ext_manifest = NULL; sof_lnl_ops.parse_platform_ext_manifest = NULL;
...@@ -115,8 +129,10 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev) ...@@ -115,8 +129,10 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev)
/* TODO: add core_get and core_put */ /* TODO: add core_get and core_put */
/* PM */ /* PM */
sof_lnl_ops.resume = lnl_hda_dsp_resume; if (!sdev->dspless_mode_selected) {
sof_lnl_ops.runtime_resume = lnl_hda_dsp_runtime_resume; sof_lnl_ops.resume = lnl_hda_dsp_resume;
sof_lnl_ops.runtime_resume = lnl_hda_dsp_runtime_resume;
}
sof_lnl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position; sof_lnl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position;
......
...@@ -231,9 +231,11 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, ...@@ -231,9 +231,11 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd,
*/ */
static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
int direction,
struct snd_sof_pcm_stream_pipeline_list *pipeline_list, struct snd_sof_pcm_stream_pipeline_list *pipeline_list,
int state, int cmd) int state, int cmd)
{ {
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
bool allocate, enable, set_fifo_size; bool allocate, enable, set_fifo_size;
struct sof_ipc4_msg msg = {{ 0 }}; struct sof_ipc4_msg msg = {{ 0 }};
int i; int i;
...@@ -294,6 +296,20 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, ...@@ -294,6 +296,20 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
msg.extension |= pipeline->msg.extension; msg.extension |= pipeline->msg.extension;
} }
if (direction == SNDRV_PCM_STREAM_CAPTURE) {
/*
* For ChainDMA the DMA ids are unique with the following mapping:
* playback: 0 - (num_playback_streams - 1)
* capture: num_playback_streams - (num_playback_streams +
* num_capture_streams - 1)
*
* Add the num_playback_streams offset to the DMA ids stored in
* msg.primary in case capture
*/
msg.primary += SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(ipc4_data->num_playback_streams);
msg.primary += SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(ipc4_data->num_playback_streams);
}
if (allocate) if (allocate)
msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK; msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK;
...@@ -340,7 +356,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, ...@@ -340,7 +356,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
* trigger function that handles the rest for the substream. * trigger function that handles the rest for the substream.
*/ */
if (pipeline->use_chain_dma) if (pipeline->use_chain_dma)
return sof_ipc4_chain_dma_trigger(sdev, pipeline_list, state, cmd); return sof_ipc4_chain_dma_trigger(sdev, substream->stream,
pipeline_list, state, cmd);
/* allocate memory for the pipeline data */ /* allocate memory for the pipeline data */
trigger_list = kzalloc(struct_size(trigger_list, pipeline_instance_ids, trigger_list = kzalloc(struct_size(trigger_list, pipeline_instance_ids,
......
...@@ -66,6 +66,8 @@ struct sof_ipc4_fw_library { ...@@ -66,6 +66,8 @@ struct sof_ipc4_fw_library {
* @nhlt: NHLT table either from the BIOS or the topology manifest * @nhlt: NHLT table either from the BIOS or the topology manifest
* @mtrace_type: mtrace type supported on the booted platform * @mtrace_type: mtrace type supported on the booted platform
* @mtrace_log_bytes: log bytes as reported by the firmware via fw_config reply * @mtrace_log_bytes: log bytes as reported by the firmware via fw_config reply
* @num_playback_streams: max number of playback DMAs, needed for CHAIN_DMA offset
* @num_capture_streams: max number of capture DMAs
* @max_num_pipelines: max number of pipelines * @max_num_pipelines: max number of pipelines
* @max_libs_count: Maximum number of libraries support by the FW including the * @max_libs_count: Maximum number of libraries support by the FW including the
* base firmware * base firmware
...@@ -79,6 +81,8 @@ struct sof_ipc4_fw_data { ...@@ -79,6 +81,8 @@ struct sof_ipc4_fw_data {
void *nhlt; void *nhlt;
enum sof_ipc4_mtrace_type mtrace_type; enum sof_ipc4_mtrace_type mtrace_type;
u32 mtrace_log_bytes; u32 mtrace_log_bytes;
int num_playback_streams;
int num_capture_streams;
int max_num_pipelines; int max_num_pipelines;
u32 max_libs_count; u32 max_libs_count;
bool fw_context_save; bool fw_context_save;
......
...@@ -509,6 +509,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) ...@@ -509,6 +509,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
{ {
struct sof_ipc4_available_audio_format *available_fmt; struct sof_ipc4_available_audio_format *available_fmt;
struct snd_soc_component *scomp = swidget->scomp; struct snd_soc_component *scomp = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_sof_dai *dai = swidget->private; struct snd_sof_dai *dai = swidget->private;
struct sof_ipc4_copier *ipc4_copier; struct sof_ipc4_copier *ipc4_copier;
struct snd_sof_widget *pipe_widget; struct snd_sof_widget *pipe_widget;
...@@ -548,14 +549,16 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) ...@@ -548,14 +549,16 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
dev_dbg(scomp->dev, "dai %s node_type %u dai_type %u dai_index %d\n", swidget->widget->name, dev_dbg(scomp->dev, "dai %s node_type %u dai_type %u dai_index %d\n", swidget->widget->name,
node_type, ipc4_copier->dai_type, ipc4_copier->dai_index); node_type, ipc4_copier->dai_type, ipc4_copier->dai_index);
dai->type = ipc4_copier->dai_type;
ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type); ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);
pipe_widget = swidget->spipe->pipe_widget; pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private; pipeline = pipe_widget->private;
if (pipeline->use_chain_dma && ipc4_copier->dai_type != SOF_DAI_INTEL_HDA) {
dev_err(scomp->dev, if (pipeline->use_chain_dma &&
"Bad DAI type '%d', Chained DMA is only supported by HDA DAIs (%d).\n", !snd_sof_is_chain_dma_supported(sdev, ipc4_copier->dai_type)) {
ipc4_copier->dai_type, SOF_DAI_INTEL_HDA); dev_err(scomp->dev, "Bad DAI type '%d', Chain DMA is not supported\n",
ipc4_copier->dai_type);
ret = -ENODEV; ret = -ENODEV;
goto free_available_fmt; goto free_available_fmt;
} }
...@@ -2800,13 +2803,14 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * ...@@ -2800,13 +2803,14 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
if (!data) if (!data)
return 0; return 0;
if (pipeline->use_chain_dma) {
pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;
pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data);
return 0;
}
switch (ipc4_copier->dai_type) { switch (ipc4_copier->dai_type) {
case SOF_DAI_INTEL_HDA: case SOF_DAI_INTEL_HDA:
if (pipeline->use_chain_dma) {
pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;
pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data);
break;
}
gtw_attr = ipc4_copier->gtw_attr; gtw_attr = ipc4_copier->gtw_attr;
gtw_attr->lp_buffer_alloc = pipeline->lp_mode; gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
fallthrough; fallthrough;
......
...@@ -567,6 +567,15 @@ snd_sof_set_mach_params(struct snd_soc_acpi_mach *mach, ...@@ -567,6 +567,15 @@ snd_sof_set_mach_params(struct snd_soc_acpi_mach *mach,
sof_ops(sdev)->set_mach_params(mach, sdev); sof_ops(sdev)->set_mach_params(mach, sdev);
} }
static inline bool
snd_sof_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type)
{
if (sof_ops(sdev) && sof_ops(sdev)->is_chain_dma_supported)
return sof_ops(sdev)->is_chain_dma_supported(sdev, dai_type);
return false;
}
/** /**
* snd_sof_dsp_register_poll_timeout - Periodically poll an address * snd_sof_dsp_register_poll_timeout - Periodically poll an address
* until a condition is met or a timeout occurs * until a condition is met or a timeout occurs
......
...@@ -46,7 +46,6 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, ...@@ -46,7 +46,6 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
{ {
const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_pipeline *spipe = swidget->spipe; struct snd_sof_pipeline *spipe = swidget->spipe;
struct snd_sof_widget *pipe_widget;
int err = 0; int err = 0;
int ret; int ret;
...@@ -59,8 +58,6 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, ...@@ -59,8 +58,6 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
if (--swidget->use_count) if (--swidget->use_count)
return 0; return 0;
pipe_widget = swidget->spipe->pipe_widget;
/* reset route setup status for all routes that contain this widget */ /* reset route setup status for all routes that contain this widget */
sof_reset_route_setup_status(sdev, swidget); sof_reset_route_setup_status(sdev, swidget);
...@@ -109,8 +106,9 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, ...@@ -109,8 +106,9 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
* free the scheduler widget (same as pipe_widget) associated with the current swidget. * free the scheduler widget (same as pipe_widget) associated with the current swidget.
* skip for static pipelines * skip for static pipelines
*/ */
if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { if (swidget->spipe && swidget->dynamic_pipeline_widget &&
ret = sof_widget_free_unlocked(sdev, pipe_widget); swidget->id != snd_soc_dapm_scheduler) {
ret = sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);
if (ret < 0 && !err) if (ret < 0 && !err)
err = ret; err = ret;
} }
......
...@@ -514,6 +514,7 @@ struct snd_sof_route { ...@@ -514,6 +514,7 @@ struct snd_sof_route {
struct snd_sof_dai { struct snd_sof_dai {
struct snd_soc_component *scomp; struct snd_soc_component *scomp;
const char *name; const char *name;
u32 type;
int number_configs; int number_configs;
int current_config; int current_config;
......
...@@ -157,6 +157,13 @@ struct sof_firmware { ...@@ -157,6 +157,13 @@ struct sof_firmware {
u32 payload_offset; u32 payload_offset;
}; };
enum sof_dai_access {
SOF_DAI_DSP_ACCESS, /* access from DSP only */
SOF_DAI_HOST_ACCESS, /* access from host only */
SOF_DAI_ACCESS_NUM
};
/* /*
* SOF DSP HW abstraction operations. * SOF DSP HW abstraction operations.
* Used to abstract DSP HW architecture and any IO busses between host CPU * Used to abstract DSP HW architecture and any IO busses between host CPU
...@@ -338,6 +345,8 @@ struct snd_sof_dsp_ops { ...@@ -338,6 +345,8 @@ struct snd_sof_dsp_ops {
struct snd_soc_dai_driver *drv; struct snd_soc_dai_driver *drv;
int num_drv; int num_drv;
bool (*is_chain_dma_supported)(struct snd_sof_dev *sdev, u32 dai_type); /* optional */
/* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */ /* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */
u32 hw_info; u32 hw_info;
......
...@@ -2354,25 +2354,43 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index, ...@@ -2354,25 +2354,43 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_dapm_widget *tw) struct snd_soc_tplg_dapm_widget *tw)
{ {
if (WIDGET_IS_DAI(w->id)) { if (WIDGET_IS_DAI(w->id)) {
static const struct sof_topology_token dai_tokens[] = {
{SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, 0}};
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *priv = &tw->priv;
struct snd_sof_widget *swidget; struct snd_sof_widget *swidget;
struct snd_sof_dai dai; struct snd_sof_dai *sdai;
int ret; int ret;
swidget = kzalloc(sizeof(*swidget), GFP_KERNEL); swidget = kzalloc(sizeof(*swidget), GFP_KERNEL);
if (!swidget) if (!swidget)
return -ENOMEM; return -ENOMEM;
memset(&dai, 0, sizeof(dai)); sdai = kzalloc(sizeof(*sdai), GFP_KERNEL);
if (!sdai) {
kfree(swidget);
return -ENOMEM;
}
ret = sof_parse_tokens(scomp, &sdai->type, dai_tokens, ARRAY_SIZE(dai_tokens),
priv->array, le32_to_cpu(priv->size));
if (ret < 0) {
dev_err(scomp->dev, "Failed to parse DAI tokens for %s\n", tw->name);
kfree(swidget);
kfree(sdai);
return ret;
}
ret = sof_connect_dai_widget(scomp, w, tw, &dai); ret = sof_connect_dai_widget(scomp, w, tw, sdai);
if (ret) { if (ret) {
kfree(swidget); kfree(swidget);
kfree(sdai);
return ret; return ret;
} }
swidget->scomp = scomp; swidget->scomp = scomp;
swidget->widget = w; swidget->widget = w;
swidget->private = sdai;
mutex_init(&swidget->setup_mutex); mutex_init(&swidget->setup_mutex);
w->dobj.private = swidget; w->dobj.private = swidget;
list_add(&swidget->list, &sdev->widget_list); list_add(&swidget->list, &sdev->widget_list);
...@@ -2396,6 +2414,7 @@ static int sof_dspless_widget_unload(struct snd_soc_component *scomp, ...@@ -2396,6 +2414,7 @@ static int sof_dspless_widget_unload(struct snd_soc_component *scomp,
/* remove and free swidget object */ /* remove and free swidget object */
list_del(&swidget->list); list_del(&swidget->list);
kfree(swidget->private);
kfree(swidget); kfree(swidget);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment