Commit 27744454 authored by Mark Brown's avatar Mark Brown

ASoC: SOF: enable multicore with dynamic pipelines

Merge series from Kai Vehmanen <kai.vehmanen@linux.intel.com>:

	When a pipeline is marked dynamic in the SOF DSP firmware
	topology definition (the tplg file kernel loads from filesystem),
	it means the pipeline resources are not allocated when DSP is
	booted (at driver probe, or at runtime resume), but rather delayed
	until the pipeline is actually used.
parents fdd53528 05827a15
......@@ -188,6 +188,7 @@ static int imx8_probe(struct snd_sof_dev *sdev)
if (!priv)
return -ENOMEM;
sdev->num_cores = 1;
sdev->pdata->hw_pdata = priv;
priv->dev = sdev->dev;
priv->sdev = sdev;
......
......@@ -108,6 +108,7 @@ static int imx8m_probe(struct snd_sof_dev *sdev)
if (!priv)
return -ENOMEM;
sdev->num_cores = 1;
sdev->pdata->hw_pdata = priv;
priv->dev = sdev->dev;
priv->sdev = sdev;
......
......@@ -101,9 +101,8 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
/* parse platform specific extended manifest */
.parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
/* dsp core power up/down */
.core_power_up = hda_dsp_enable_core,
.core_power_down = hda_dsp_core_reset_power_down,
/* dsp core get/put */
.core_get = hda_dsp_core_get,
/* trace callback */
.trace_init = hda_dsp_trace_init,
......
......@@ -412,10 +412,19 @@ static int bdw_probe(struct snd_sof_dev *sdev)
const struct sof_dev_desc *desc = pdata->desc;
struct platform_device *pdev =
container_of(sdev->dev, struct platform_device, dev);
const struct sof_intel_dsp_desc *chip;
struct resource *mmio;
u32 base, size;
int ret;
chip = get_chip_info(sdev->pdata);
if (!chip) {
dev_err(sdev->dev, "error: no such device supported\n");
return -EIO;
}
sdev->num_cores = chip->cores_num;
/* LPE base */
mmio = platform_get_resource(pdev, IORESOURCE_MEM,
desc->resindex_lpe_base);
......
......@@ -113,10 +113,19 @@ static int byt_acpi_probe(struct snd_sof_dev *sdev)
const struct sof_dev_desc *desc = pdata->desc;
struct platform_device *pdev =
container_of(sdev->dev, struct platform_device, dev);
const struct sof_intel_dsp_desc *chip;
struct resource *mmio;
u32 base, size;
int ret;
chip = get_chip_info(sdev->pdata);
if (!chip) {
dev_err(sdev->dev, "error: no such device supported\n");
return -EIO;
}
sdev->num_cores = chip->cores_num;
/* DSP DMA can only access low 31 bits of host memory */
ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31));
if (ret < 0) {
......
......@@ -303,9 +303,8 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
/* parse platform specific extended manifest */
.parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
/* dsp core power up/down */
.core_power_up = hda_dsp_enable_core,
.core_power_down = hda_dsp_core_reset_power_down,
/* dsp core get/put */
.core_get = hda_dsp_core_get,
/* firmware run */
.run = hda_dsp_cl_boot_firmware,
......
......@@ -182,24 +182,6 @@ static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widg
return config;
}
static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream,
struct snd_soc_dapm_widget *w, int channel)
{
struct snd_sof_dev *sdev = hda_stream->sdev;
struct sof_ipc_dai_config *config;
struct sof_ipc_reply reply;
config = hda_dai_update_config(w, channel);
if (!config) {
dev_err(sdev->dev, "error: no config for DAI %s\n", w->name);
return -ENOENT;
}
/* send DAI_CONFIG IPC */
return sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
&reply, sizeof(reply));
}
static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream,
struct snd_soc_dapm_widget *w,
int channel, bool widget_setup)
......@@ -353,10 +335,9 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
w = dai->capture_widget;
/*
* clear link DMA channel. It will be assigned when
* hw_params is set up again after resume.
* free DAI widget during stop/suspend to keep widget use_count's balanced.
*/
ret = hda_link_config_ipc(hda_stream, w, DMA_CHAN_INVALID);
ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false);
if (ret < 0)
return ret;
......
......@@ -614,7 +614,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
struct hdac_bus *bus = sof_to_bus(sdev);
#endif
int ret;
int ret, j;
hda_sdw_int_enable(sdev, false);
......@@ -629,13 +629,17 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
#endif
/* power down DSP */
ret = snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask);
ret = hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask);
if (ret < 0) {
dev_err(sdev->dev,
"error: failed to power down core during suspend\n");
return ret;
}
/* reset ref counts for all cores */
for (j = 0; j < chip->cores_num; j++)
sdev->dsp_core_ref_count[j] = 0;
/* disable ppcap interrupt */
hda_dsp_ctrl_ppcap_enable(sdev, false);
hda_dsp_ctrl_ppcap_int_enable(sdev, false);
......@@ -962,3 +966,47 @@ void hda_dsp_d0i3_work(struct work_struct *work)
"error: failed to set DSP state %d substate %d\n",
target_state.state, target_state.substate);
}
int hda_dsp_core_get(struct snd_sof_dev *sdev, int core)
{
struct sof_ipc_pm_core_config pm_core_config = {
.hdr = {
.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE,
.size = sizeof(pm_core_config),
},
.enable_mask = sdev->enabled_cores_mask | BIT(core),
};
int ret, ret1;
/* power up core */
ret = hda_dsp_enable_core(sdev, BIT(core));
if (ret < 0) {
dev_err(sdev->dev, "failed to power up core %d with err: %d\n",
core, ret);
return ret;
}
/* No need to send IPC for primary core or if FW boot is not complete */
if (sdev->fw_state != SOF_FW_BOOT_COMPLETE || core == SOF_DSP_PRIMARY_CORE)
return 0;
/* Now notify DSP for secondary cores */
ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd,
&pm_core_config, sizeof(pm_core_config),
&pm_core_config, sizeof(pm_core_config));
if (ret < 0) {
dev_err(sdev->dev, "failed to enable secondary core '%d' failed with %d\n",
core, ret);
goto power_down;
}
return ret;
power_down:
/* power down core if it is host managed and return the original error if this fails too */
ret1 = hda_dsp_core_reset_power_down(sdev, BIT(core));
if (ret1 < 0)
dev_err(sdev->dev, "failed to power down core: %d with err: %d\n", core, ret1);
return ret;
}
......@@ -88,12 +88,13 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
unsigned int status;
u32 flags;
unsigned long mask;
u32 flags, j;
int ret;
int i;
/* step 1: power up corex */
ret = snd_sof_dsp_core_power_up(sdev, chip->host_managed_cores_mask);
ret = hda_dsp_enable_core(sdev, chip->host_managed_cores_mask);
if (ret < 0) {
if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n");
......@@ -148,8 +149,8 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
chip->ipc_ack_mask);
/* step 5: power down cores that are no longer needed */
ret = snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask &
~(chip->init_core_mask));
ret = hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask &
~(chip->init_core_mask));
if (ret < 0) {
if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
dev_err(sdev->dev,
......@@ -168,8 +169,14 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
HDA_DSP_REG_POLL_INTERVAL_US,
chip->rom_init_timeout *
USEC_PER_MSEC);
if (!ret)
if (!ret) {
/* set enabled cores mask and increment ref count for cores in init_core_mask */
sdev->enabled_cores_mask |= chip->init_core_mask;
mask = sdev->enabled_cores_mask;
for_each_set_bit(j, &mask, SOF_MAX_DSP_NUM_CORES)
sdev->dsp_core_ref_count[j]++;
return 0;
}
if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
dev_err(sdev->dev,
......@@ -184,7 +191,7 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
flags &= ~SOF_DBG_DUMP_OPTIONAL;
snd_sof_dsp_dbg_dump(sdev, flags);
snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask);
hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask);
return ret;
}
......@@ -501,12 +508,15 @@ int hda_dsp_post_fw_run_icl(struct snd_sof_dev *sdev)
* the host whereas on TGL it is handled by the firmware.
*/
if (!hda->clk_config_lpro) {
ret = snd_sof_dsp_core_power_up(sdev, BIT(3));
ret = hda_dsp_enable_core(sdev, BIT(3));
if (ret < 0) {
dev_err(sdev->dev, "error: dsp core power up failed on core 3\n");
return ret;
}
sdev->enabled_cores_mask |= BIT(3);
sdev->dsp_core_ref_count[3]++;
snd_sof_dsp_stall(sdev, BIT(3));
}
......
......@@ -127,17 +127,6 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w)
return sof_widget_free(sdev, swidget);
}
static const struct sof_intel_dsp_desc
*get_chip_info(struct snd_sof_pdata *pdata)
{
const struct sof_dev_desc *desc = pdata->desc;
const struct sof_intel_dsp_desc *chip_info;
chip_info = desc->chip_info;
return chip_info;
}
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
/*
......@@ -908,6 +897,8 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
goto err;
}
sdev->num_cores = chip->cores_num;
hdev = devm_kzalloc(sdev->dev, sizeof(*hdev), GFP_KERNEL);
if (!hdev)
return -ENOMEM;
......@@ -1043,9 +1034,9 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
int hda_dsp_remove(struct snd_sof_dev *sdev)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
struct hdac_bus *bus = sof_to_bus(sdev);
struct pci_dev *pci = to_pci_dev(sdev->dev);
const struct sof_intel_dsp_desc *chip = hda->desc;
/* cancel any attempt for DSP D0I3 */
cancel_delayed_work_sync(&hda->d0i3_work);
......@@ -1070,7 +1061,7 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
/* disable cores */
if (chip)
snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask);
hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask);
/* disable DSP */
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
......
......@@ -496,6 +496,7 @@ int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask);
int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask);
int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev,
unsigned int core_mask);
int hda_dsp_core_get(struct snd_sof_dev *sdev, int core);
void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev);
void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev);
......
......@@ -97,9 +97,8 @@ const struct snd_sof_dsp_ops sof_icl_ops = {
/* parse platform specific extended manifest */
.parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
/* dsp core power up/down */
.core_power_up = hda_dsp_enable_core,
.core_power_down = hda_dsp_core_reset_power_down,
/* dsp core get/put */
.core_get = hda_dsp_core_get,
/* firmware run */
.run = hda_dsp_cl_boot_firmware_iccmax,
......
......@@ -55,9 +55,18 @@ static int tangier_pci_probe(struct snd_sof_dev *sdev)
struct snd_sof_pdata *pdata = sdev->pdata;
const struct sof_dev_desc *desc = pdata->desc;
struct pci_dev *pci = to_pci_dev(sdev->dev);
const struct sof_intel_dsp_desc *chip;
u32 base, size;
int ret;
chip = get_chip_info(sdev->pdata);
if (!chip) {
dev_err(sdev->dev, "error: no such device supported\n");
return -EIO;
}
sdev->num_cores = chip->cores_num;
/* DSP DMA can only access low 31 bits of host memory */
ret = dma_coerce_mask_and_coherent(&pci->dev, DMA_BIT_MASK(31));
if (ret < 0) {
......
......@@ -177,4 +177,11 @@ struct sof_intel_stream {
size_t posn_offset;
};
static inline const struct sof_intel_dsp_desc *get_chip_info(struct snd_sof_pdata *pdata)
{
const struct sof_dev_desc *desc = pdata->desc;
return desc->chip_info;
}
#endif
......@@ -20,6 +20,46 @@ static const struct snd_sof_debugfs_map tgl_dsp_debugfs[] = {
{"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS},
};
static int tgl_dsp_core_get(struct snd_sof_dev *sdev, int core)
{
struct sof_ipc_pm_core_config pm_core_config = {
.hdr = {
.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE,
.size = sizeof(pm_core_config),
},
.enable_mask = sdev->enabled_cores_mask | BIT(core),
};
/* power up primary core if not already powered up and return */
if (core == SOF_DSP_PRIMARY_CORE)
return hda_dsp_enable_core(sdev, BIT(core));
/* notify DSP for secondary cores */
return sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd,
&pm_core_config, sizeof(pm_core_config),
&pm_core_config, sizeof(pm_core_config));
}
static int tgl_dsp_core_put(struct snd_sof_dev *sdev, int core)
{
struct sof_ipc_pm_core_config pm_core_config = {
.hdr = {
.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE,
.size = sizeof(pm_core_config),
},
.enable_mask = sdev->enabled_cores_mask & ~BIT(core),
};
/* power down primary core and return */
if (core == SOF_DSP_PRIMARY_CORE)
return hda_dsp_core_reset_power_down(sdev, BIT(core));
/* notify DSP for secondary cores */
return sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd,
&pm_core_config, sizeof(pm_core_config),
&pm_core_config, sizeof(pm_core_config));
}
/* Tigerlake ops */
const struct snd_sof_dsp_ops sof_tgl_ops = {
/* probe/remove/shutdown */
......@@ -93,9 +133,9 @@ const struct snd_sof_dsp_ops sof_tgl_ops = {
/* parse platform specific extended manifest */
.parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data,
/* dsp core power up/down */
.core_power_up = hda_dsp_enable_core,
.core_power_down = hda_dsp_core_reset_power_down,
/* dsp core get/put */
.core_get = tgl_dsp_core_get,
.core_put = tgl_dsp_core_put,
/* firmware run */
.run = hda_dsp_cl_boot_firmware_iccmax,
......
......@@ -72,35 +72,68 @@ static inline int snd_sof_dsp_reset(struct snd_sof_dev *sdev)
return 0;
}
/* dsp core power up/power down */
static inline int snd_sof_dsp_core_power_up(struct snd_sof_dev *sdev,
unsigned int core_mask)
/* dsp core get/put */
static inline int snd_sof_dsp_core_get(struct snd_sof_dev *sdev, int core)
{
int ret = 0;
if (core > sdev->num_cores - 1) {
dev_err(sdev->dev, "invalid core id: %d for num_cores: %d\n", core,
sdev->num_cores);
return -EINVAL;
}
if (sof_ops(sdev)->core_get) {
int ret;
/* if current ref_count is > 0, increment it and return */
if (sdev->dsp_core_ref_count[core] > 0) {
sdev->dsp_core_ref_count[core]++;
return 0;
}
/* power up the core */
ret = sof_ops(sdev)->core_get(sdev, core);
if (ret < 0)
return ret;
core_mask &= ~sdev->enabled_cores_mask;
if (sof_ops(sdev)->core_power_up && core_mask) {
ret = sof_ops(sdev)->core_power_up(sdev, core_mask);
if (!ret)
sdev->enabled_cores_mask |= core_mask;
/* increment ref_count */
sdev->dsp_core_ref_count[core]++;
/* and update enabled_cores_mask */
sdev->enabled_cores_mask |= BIT(core);
dev_dbg(sdev->dev, "Core %d powered up\n", core);
}
return ret;
return 0;
}
static inline int snd_sof_dsp_core_power_down(struct snd_sof_dev *sdev,
unsigned int core_mask)
static inline int snd_sof_dsp_core_put(struct snd_sof_dev *sdev, int core)
{
int ret = 0;
if (core > sdev->num_cores - 1) {
dev_err(sdev->dev, "invalid core id: %d for num_cores: %d\n", core,
sdev->num_cores);
return -EINVAL;
}
if (sof_ops(sdev)->core_put) {
int ret;
/* decrement ref_count and return if it is > 0 */
if (--(sdev->dsp_core_ref_count[core]) > 0)
return 0;
core_mask &= sdev->enabled_cores_mask;
if (sof_ops(sdev)->core_power_down && core_mask) {
ret = sof_ops(sdev)->core_power_down(sdev, core_mask);
if (!ret)
sdev->enabled_cores_mask &= ~core_mask;
/* power down the core */
ret = sof_ops(sdev)->core_put(sdev, core);
if (ret < 0)
return ret;
/* and update enabled_cores_mask */
sdev->enabled_cores_mask &= ~BIT(core);
dev_dbg(sdev->dev, "Core %d powered down\n", core);
}
return ret;
return 0;
}
/* pre/post fw load */
......
......@@ -106,7 +106,7 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
.id = swidget->comp_id,
};
struct sof_ipc_reply reply;
int ret;
int ret, ret1, core;
if (!swidget->private)
return 0;
......@@ -115,10 +115,17 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (--swidget->use_count)
return 0;
core = swidget->core;
switch (swidget->id) {
case snd_soc_dapm_scheduler:
{
const struct sof_ipc_pipe_new *pipeline = swidget->private;
core = pipeline->core;
ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE;
break;
}
case snd_soc_dapm_buffer:
ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE;
break;
......@@ -127,20 +134,32 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
break;
}
/* continue to disable core even if IPC fails */
ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free),
&reply, sizeof(reply));
if (ret < 0) {
if (ret < 0)
dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name);
swidget->use_count++;
return ret;
/*
* disable widget core. continue to route setup status and complete flag
* even if this fails and return the appropriate error
*/
ret1 = snd_sof_dsp_core_put(sdev, core);
if (ret1 < 0) {
dev_err(sdev->dev, "error: failed to disable target core: %d for widget %s\n",
core, swidget->widget->name);
if (!ret)
ret = ret1;
}
/* reset route setup status for all routes that contain this widget */
sof_reset_route_setup_status(sdev, swidget);
swidget->complete = 0;
dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
return 0;
if (!ret)
dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
return ret;
}
EXPORT_SYMBOL(sof_widget_free);
......@@ -153,6 +172,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
struct snd_sof_dai *dai;
size_t ipc_size;
int ret;
int core;
/* skip if there is no private data */
if (!swidget->private)
......@@ -162,10 +182,18 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (++swidget->use_count > 1)
return 0;
ret = sof_pipeline_core_enable(sdev, swidget);
/* set core ID */
core = swidget->core;
if (swidget->id == snd_soc_dapm_scheduler) {
pipeline = swidget->private;
core = pipeline->core;
}
/* enable widget core */
ret = snd_sof_dsp_core_get(sdev, core);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to enable target core: %d for widget %s\n",
ret, swidget->widget->name);
dev_err(sdev->dev, "error: failed to enable target core for widget %s\n",
swidget->widget->name);
goto use_count_dec;
}
......@@ -174,8 +202,10 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
case snd_soc_dapm_dai_out:
ipc_size = sizeof(struct sof_ipc_comp_dai) + sizeof(struct sof_ipc_comp_ext);
comp = kzalloc(ipc_size, GFP_KERNEL);
if (!comp)
return -ENOMEM;
if (!comp) {
ret = -ENOMEM;
goto core_put;
}
dai = swidget->private;
dai->configured = false;
......@@ -190,20 +220,26 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (ret < 0) {
dev_err(sdev->dev, "error: failed to load widget %s\n",
swidget->widget->name);
goto use_count_dec;
goto core_put;
}
ret = sof_dai_config_setup(sdev, dai);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to load dai config for DAI %s\n",
swidget->widget->name);
/*
* widget use_count and core ref_count will both be decremented by
* sof_widget_free()
*/
sof_widget_free(sdev, swidget);
return ret;
}
break;
case snd_soc_dapm_scheduler:
pipeline = swidget->private;
ret = sof_load_pipeline_ipc(sdev, pipeline, &r);
ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline,
sizeof(*pipeline), &r, sizeof(r));
break;
default:
hdr = swidget->private;
......@@ -213,7 +249,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
}
if (ret < 0) {
dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name);
goto use_count_dec;
goto core_put;
}
/* restore kcontrols for widget */
......@@ -221,6 +257,10 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (ret < 0) {
dev_err(sdev->dev, "error: failed to restore kcontrols for widget %s\n",
swidget->widget->name);
/*
* widget use_count and core ref_count will both be decremented by
* sof_widget_free()
*/
sof_widget_free(sdev, swidget);
return ret;
}
......@@ -229,6 +269,8 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
return 0;
core_put:
snd_sof_dsp_core_put(sdev, core);
use_count_dec:
swidget->use_count--;
return ret;
......@@ -664,11 +706,12 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify)
}
/*
* This function doesn't free widgets during suspend. It only resets the set up status for all
* routes and use_count for all widgets.
* For older firmware, this function doesn't free widgets for static pipelines during suspend.
* It only resets use_count for all widgets.
*/
int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify)
{
struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
struct snd_sof_widget *swidget;
struct snd_sof_route *sroute;
int ret;
......@@ -680,8 +723,14 @@ int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify)
* loading the sound card unavailable to open PCMs.
*/
list_for_each_entry_reverse(swidget, &sdev->widget_list, list) {
if (!verify) {
if (swidget->dynamic_pipeline_widget)
continue;
/* Do not free widgets for static pipelines with FW ABI older than 3.19 */
if (!verify && !swidget->dynamic_pipeline_widget &&
v->abi_version < SOF_ABI_VER(3, 19, 0)) {
swidget->use_count = 0;
swidget->complete = 0;
continue;
}
......
......@@ -185,12 +185,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file);
int snd_sof_complete_pipeline(struct snd_sof_dev *sdev,
struct snd_sof_widget *swidget);
int sof_load_pipeline_ipc(struct snd_sof_dev *sdev,
struct sof_ipc_pipe_new *pipeline,
struct sof_ipc_comp_reply *r);
int sof_pipeline_core_enable(struct snd_sof_dev *sdev,
const struct snd_sof_widget *swidget);
/*
* Stream IPC
*/
......
......@@ -71,6 +71,9 @@ extern int sof_core_debug;
/* So far the primary core on all DSPs has ID 0 */
#define SOF_DSP_PRIMARY_CORE 0
/* max number of DSP cores */
#define SOF_MAX_DSP_NUM_CORES 8
/* DSP power state */
enum sof_dsp_power_states {
SOF_DSP_PM_D0,
......@@ -127,10 +130,8 @@ struct snd_sof_dsp_ops {
int (*run)(struct snd_sof_dev *sof_dev); /* mandatory */
int (*stall)(struct snd_sof_dev *sof_dev, unsigned int core_mask); /* optional */
int (*reset)(struct snd_sof_dev *sof_dev); /* optional */
int (*core_power_up)(struct snd_sof_dev *sof_dev,
unsigned int core_mask); /* optional */
int (*core_power_down)(struct snd_sof_dev *sof_dev,
unsigned int core_mask); /* optional */
int (*core_get)(struct snd_sof_dev *sof_dev, int core); /* optional */
int (*core_put)(struct snd_sof_dev *sof_dev, int core); /* optional */
/*
* Register IO: only used by respective drivers themselves,
......@@ -477,6 +478,18 @@ struct snd_sof_dev {
bool msi_enabled;
/* DSP core context */
u32 num_cores;
/*
* ref count per core that will be modified during system suspend/resume and during pcm
* hw_params/hw_free. This doesn't need to be protected with a mutex because pcm
* hw_params/hw_free are already protected by the PCM mutex in the ALSA framework in
* sound/core/ when streams are active and during system suspend/resume, streams are
* already suspended.
*/
int dsp_core_ref_count[SOF_MAX_DSP_NUM_CORES];
void *private; /* core does not touch this */
};
......
......@@ -1346,69 +1346,6 @@ static int sof_control_unload(struct snd_soc_component *scomp,
* DAI Topology
*/
/* Static DSP core power management so far, should be extended in the future */
static int sof_core_enable(struct snd_sof_dev *sdev, int core)
{
struct sof_ipc_pm_core_config pm_core_config = {
.hdr = {
.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE,
.size = sizeof(pm_core_config),
},
.enable_mask = sdev->enabled_cores_mask | BIT(core),
};
int ret;
if (sdev->enabled_cores_mask & BIT(core))
return 0;
/* power up the core if it is host managed */
ret = snd_sof_dsp_core_power_up(sdev, BIT(core));
if (ret < 0) {
dev_err(sdev->dev, "error: %d powering up core %d\n",
ret, core);
return ret;
}
/* Now notify DSP */
ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd,
&pm_core_config, sizeof(pm_core_config),
&pm_core_config, sizeof(pm_core_config));
if (ret < 0) {
dev_err(sdev->dev, "error: core %d enable ipc failure %d\n",
core, ret);
goto err;
}
return ret;
err:
/* power down core if it is host managed and return the original error if this fails too */
if (snd_sof_dsp_core_power_down(sdev, BIT(core)) < 0)
dev_err(sdev->dev, "error: powering down core %d\n", core);
return ret;
}
int sof_pipeline_core_enable(struct snd_sof_dev *sdev,
const struct snd_sof_widget *swidget)
{
const struct sof_ipc_pipe_new *pipeline;
int ret;
if (swidget->id == snd_soc_dapm_scheduler) {
pipeline = swidget->private;
} else {
pipeline = snd_sof_pipeline_find(sdev, swidget->pipeline_id);
if (!pipeline)
return -ENOENT;
}
/* First enable the pipeline core */
ret = sof_core_enable(sdev, pipeline->core);
if (ret < 0)
return ret;
return sof_core_enable(sdev, swidget->core);
}
static int sof_connect_dai_widget(struct snd_soc_component *scomp,
struct snd_soc_dapm_widget *w,
struct snd_soc_tplg_dapm_widget *tw,
......@@ -1707,23 +1644,6 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index,
/*
* Pipeline Topology
*/
int sof_load_pipeline_ipc(struct snd_sof_dev *sdev,
struct sof_ipc_pipe_new *pipeline,
struct sof_ipc_comp_reply *r)
{
int ret = sof_core_enable(sdev, pipeline->core);
if (ret < 0)
return ret;
ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline,
sizeof(*pipeline), r, sizeof(*r));
if (ret < 0)
dev_err(sdev->dev, "error: load pipeline ipc failure\n");
return ret;
}
static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *swidget,
struct snd_soc_tplg_dapm_widget *tw)
......@@ -2502,10 +2422,8 @@ static int sof_route_unload(struct snd_soc_component *scomp,
static int sof_widget_unload(struct snd_soc_component *scomp,
struct snd_soc_dobj *dobj)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
const struct snd_kcontrol_new *kc;
struct snd_soc_dapm_widget *widget;
struct sof_ipc_pipe_new *pipeline;
struct snd_sof_control *scontrol;
struct snd_sof_widget *swidget;
struct soc_mixer_control *sm;
......@@ -2532,24 +2450,6 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
list_del(&dai->list);
}
break;
case snd_soc_dapm_scheduler:
/* power down the pipeline schedule core */
pipeline = swidget->private;
/*
* Runtime PM should still function normally if topology loading fails and
* it's components are unloaded. Do not power down the primary core so that the
* CTX_SAVE IPC can succeed during runtime suspend.
*/
if (pipeline->core == SOF_DSP_PRIMARY_CORE)
break;
ret = snd_sof_dsp_core_power_down(sdev, 1 << pipeline->core);
if (ret < 0)
dev_err(scomp->dev, "error: powering down pipeline schedule core %d\n",
pipeline->core);
break;
default:
break;
}
......
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