diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 6013f963f4821b..57bb095d022140 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -87,6 +87,8 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, lockdep_register_key(&bus->bus_lock_key); __mutex_init(&bus->bus_lock, "bus_lock", &bus->bus_lock_key); + mutex_init(&bus->bpt_lock); + INIT_LIST_HEAD(&bus->slaves); INIT_LIST_HEAD(&bus->m_rt_list); @@ -2094,6 +2096,7 @@ EXPORT_SYMBOL(sdw_clear_slave_status); int sdw_bpt_send_async(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg) { int len = 0; + int ret; int i; for (i = 0; i < msg->sections; i++) @@ -2118,13 +2121,26 @@ int sdw_bpt_send_async(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_ return -EOPNOTSUPP; } - return bus->ops->bpt_send_async(bus, slave, msg); + /* Serialize BPT/BRA transfers per bus: PDIs and DMA resources are shared */ + mutex_lock(&bus->bpt_lock); + + ret = bus->ops->bpt_send_async(bus, slave, msg); + if (ret < 0) + mutex_unlock(&bus->bpt_lock); + + /* on success the lock is held until sdw_bpt_wait() */ + return ret; } EXPORT_SYMBOL(sdw_bpt_send_async); int sdw_bpt_wait(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg) { - return bus->ops->bpt_wait(bus, slave, msg); + int ret; + + ret = bus->ops->bpt_wait(bus, slave, msg); + mutex_unlock(&bus->bpt_lock); + + return ret; } EXPORT_SYMBOL(sdw_bpt_wait); diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index cbf7bd3d4e7bac..dbbcb3122fb440 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -2124,6 +2124,8 @@ int sdw_stream_remove_master(struct sdw_bus *bus, } EXPORT_SYMBOL(sdw_stream_remove_master); +#define CODEC_INIT_TIMEOUT 500 + /** * sdw_stream_add_slave() - Allocate and add master/slave runtime to a stream * @@ -2147,9 +2149,19 @@ int sdw_stream_add_slave(struct sdw_slave *slave, struct sdw_master_runtime *m_rt; bool alloc_master_rt = false; bool alloc_slave_rt = false; + unsigned long time; int ret; + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(CODEC_INIT_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + mutex_lock(&slave->bus->bus_lock); /* diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 3e0d21132ef2f7..dd1743e2f25874 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -1044,6 +1044,7 @@ struct sdw_bus { int stream_refcount; int bpt_stream_refcount; struct sdw_stream_runtime *bpt_stream; + struct mutex bpt_lock; /* serialize BPT/BRA transfers per bus */ const struct sdw_master_ops *ops; const struct sdw_master_port_ops *port_ops; struct sdw_master_prop prop; diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index 9e3d801e45ecd5..84f51edc3f9d67 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -188,6 +188,7 @@ struct snd_compr_ops { * @card: sound card pointer * @direction: Playback or capture direction * @lock: device lock + * @open_list: list of open compress files * @device: device id * @use_pause_in_draining: allow pause in draining, true when set */ @@ -199,6 +200,7 @@ struct snd_compr { struct snd_card *card; unsigned int direction; struct mutex lock; + struct list_head open_list; int device; bool use_pause_in_draining; #ifdef CONFIG_SND_VERBOSE_PROCFS diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 0519afd7217f13..6b6d9557a4e90b 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -90,6 +90,8 @@ struct snd_soc_acpi_mach_params { unsigned short subsystem_rev; bool subsystem_id_set; u32 bt_link_mask; + const char *card_name; + u32 dai_type_mask; }; /** diff --git a/include/sound/sof.h b/include/sound/sof.h index 38d6c8cb5e832b..95b12ae4464ef7 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -17,6 +17,7 @@ struct snd_sof_dsp_ops; struct snd_sof_dev; +struct sof_audio_ops; /** * enum sof_fw_state - DSP firmware state definitions @@ -171,6 +172,7 @@ struct sof_dev_desc { const char *default_fw_filename[SOF_IPC_TYPE_COUNT]; const struct snd_sof_dsp_ops *ops; + const struct sof_audio_ops *audio_ops; int (*ops_init)(struct snd_sof_dev *sdev); void (*ops_free)(struct snd_sof_dev *sdev); }; diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 5fd2486582cd49..00a9bb2796fc96 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -433,12 +433,10 @@ enum sof_ipc4_fw_config_params { SOF_IPC4_FW_CFG_RESERVED, SOF_IPC4_FW_CFG_POWER_GATING_POLICY, SOF_IPC4_FW_CFG_ASSERT_MODE, - SOF_IPC4_FW_RESERVED1, - SOF_IPC4_FW_RESERVED2, - SOF_IPC4_FW_RESERVED3, - SOF_IPC4_FW_RESERVED4, - SOF_IPC4_FW_RESERVED5, - SOF_IPC4_FW_CONTEXT_SAVE + /* Reserved: 24 - 28 */ + SOF_IPC4_FW_CONTEXT_SAVE = 29, + /* Reserved: 30 - 34 */ + SOF_IPC4_FW_CFG_SOF_INFO = 35, }; struct sof_ipc4_fw_version { @@ -448,6 +446,14 @@ struct sof_ipc4_fw_version { uint16_t build; } __packed; +/* + * tuple based array for SOF specific information under SOF_IPC4_FW_CFG_SOF_INFO + * tuple of fw_config + */ +enum ipc4_fw_sof_info_params { + SOF_IPC4_SOF_CODEC_INFO, +}; + /* Payload data for SOF_IPC4_MOD_SET_DX */ struct sof_ipc4_dx_state_info { /* core(s) to apply the change */ @@ -616,9 +622,10 @@ struct sof_ipc4_notify_module_data { * The event_data contains the struct sof_ipc4_control_msg_payload of the control * which sent the notification. */ -#define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_MASK GENMASK(31, 16) +#define SOF_IPC4_NOTIFY_MODULE_EVENTID_SOF_MAGIC_MASK GENMASK(31, 16) #define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL 0xA15A0000 #define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_PARAMID_MASK GENMASK(15, 0) +#define SOF_IPC4_NOTIFY_MODULE_EVENTID_COMPR_MAGIC_VAL 0xC0C00000 /* * Macros for creating struct sof_ipc4_module_init_ext_init payload @@ -681,7 +688,8 @@ struct sof_ipc4_module_init_ext_object { enum sof_ipc4_mod_init_ext_obj_id { SOF_IPC4_MOD_INIT_DATA_ID_INVALID = 0, SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA, - SOF_IPC4_MOD_INIT_DATA_ID_MAX = SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA, + SOF_IPC4_MOD_INIT_DATA_ID_MODULE_DATA, + SOF_IPC4_MOD_INIT_DATA_ID_MAX = SOF_IPC4_MOD_INIT_DATA_ID_MODULE_DATA, }; /* DP module memory configuration data object for ext_init object array */ diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index fd63d219bf8665..b0c9b082f6b1c8 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -42,6 +42,7 @@ #endif struct snd_compr_file { + struct list_head list; unsigned long caps; struct snd_compr_stream stream; }; @@ -75,11 +76,11 @@ static inline void snd_compr_task_free_all(struct snd_compr_stream *stream) { } static int snd_compr_open(struct inode *inode, struct file *f) { struct snd_compr *compr; - struct snd_compr_file *data; - struct snd_compr_runtime *runtime; + struct snd_compr_file *data = NULL; + struct snd_compr_runtime *runtime = NULL; enum snd_compr_direction dirn; int maj = imajor(inode); - int ret; + int ret = 0; if ((f->f_flags & O_ACCMODE) == O_WRONLY) dirn = SND_COMPRESS_PLAYBACK; @@ -101,17 +102,23 @@ static int snd_compr_open(struct inode *inode, struct file *f) return -ENODEV; } + if (!try_module_get(compr->card->module)) { + snd_card_unref(compr->card); + return -EFAULT; + } + if (dirn != compr->direction) { pr_err("this device doesn't support this direction\n"); - snd_card_unref(compr->card); - return -EINVAL; + ret = -EINVAL; + goto __error; } data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { - snd_card_unref(compr->card); - return -ENOMEM; + ret = -ENOMEM; + goto __error; } + INIT_LIST_HEAD(&data->list); INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work); @@ -121,9 +128,8 @@ static int snd_compr_open(struct inode *inode, struct file *f) data->stream.device = compr; runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); if (!runtime) { - kfree(data); - snd_card_unref(compr->card); - return -ENOMEM; + ret = -ENOMEM; + goto __error; } runtime->state = SNDRV_PCM_STATE_OPEN; init_waitqueue_head(&runtime->sleep); @@ -132,11 +138,17 @@ static int snd_compr_open(struct inode *inode, struct file *f) #endif data->stream.runtime = runtime; f->private_data = (void *)data; - scoped_guard(mutex, &compr->lock) + scoped_guard(mutex, &compr->lock) { ret = compr->ops->open(&data->stream); + if (!ret) + list_add_tail(&data->list, &compr->open_list); + } + +__error: if (ret) { kfree(runtime); kfree(data); + module_put(compr->card->module); } snd_card_unref(compr->card); return ret; @@ -146,17 +158,23 @@ static int snd_compr_free(struct inode *inode, struct file *f) { struct snd_compr_file *data = f->private_data; struct snd_compr_runtime *runtime = data->stream.runtime; + struct snd_compr *compr = data->stream.device; cancel_delayed_work_sync(&data->stream.error_work); - switch (runtime->state) { - case SNDRV_PCM_STATE_RUNNING: - case SNDRV_PCM_STATE_DRAINING: - case SNDRV_PCM_STATE_PAUSED: - data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP); - break; - default: - break; + scoped_guard(mutex, &compr->lock) { + if (!list_empty(&data->list)) + list_del_init(&data->list); + + switch (runtime->state) { + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_PAUSED: + data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP); + break; + default: + break; + } } snd_compr_task_free_all(&data->stream); @@ -164,6 +182,7 @@ static int snd_compr_free(struct inode *inode, struct file *f) data->stream.ops->free(&data->stream); if (!data->stream.runtime->dma_buffer_p) kfree(data->stream.runtime->buffer); + module_put(data->stream.device->card->module); kfree(data->stream.runtime); kfree(data); return 0; @@ -1427,8 +1446,27 @@ static int snd_compress_dev_register(struct snd_device *device) static int snd_compress_dev_disconnect(struct snd_device *device) { struct snd_compr *compr; + struct snd_compr_file *data; compr = device->device_data; + scoped_guard(mutex, &compr->lock) { + list_for_each_entry(data, &compr->open_list, list) { + switch (data->stream.runtime->state) { + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_PAUSED: + data->stream.ops->trigger(&data->stream, + SNDRV_PCM_TRIGGER_STOP); + break; + default: + break; + } + + data->stream.runtime->state = SNDRV_PCM_STATE_DISCONNECTED; + wake_up(&data->stream.runtime->sleep); + } + } + snd_unregister_device(compr->dev); return 0; } @@ -1536,6 +1574,7 @@ int snd_compress_new(struct snd_card *card, int device, compr->device = device; compr->direction = dirn; mutex_init(&compr->lock); + INIT_LIST_HEAD(&compr->open_list); snd_compress_set_id(compr, id); diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index 519218385fdf74..7d75f9a4cdb27f 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -109,7 +109,8 @@ static int skl_hda_audio_probe(struct platform_device *pdev) if (!card) return -ENOMEM; - card->name = "hda-dsp"; + card->name = mach->mach_params.card_name ? + mach->mach_params.card_name : "hda-dsp"; card->owner = THIS_MODULE; card->fully_routed = true; card->late_probe = skl_hda_card_late_probe; @@ -128,6 +129,8 @@ static int skl_hda_audio_probe(struct platform_device *pdev) if (mach->mach_params.codec_mask & IDISP_CODEC_MASK) ctx->hdmi.idisp_codec = true; + ctx->dmic_be_num = mach->mach_params.dmic_num; + ctx->link_order_overwrite = HDA_LINK_ORDER; ctx->link_id_overwrite = HDA_LINK_IDS; diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index d5383c8dcdf099..9807090bddd6f8 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1482,7 +1482,8 @@ static int mc_probe(struct platform_device *pdev) ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count(); card = &ctx->card; card->dev = &pdev->dev; - card->name = "soundwire"; + card->name = mach->mach_params.card_name ? + mach->mach_params.card_name : "soundwire"; card->owner = THIS_MODULE; card->late_probe = sof_sdw_card_late_probe; card->add_dai_link = sof_sdw_add_dai_link; diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c index b6e5a40b78cc60..d2e1d953e01814 100644 --- a/sound/soc/intel/common/sof-function-topology-lib.c +++ b/sound/soc/intel/common/sof-function-topology-lib.c @@ -46,6 +46,11 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ int ret; int i; + if (!mach || !mach->sof_tplg_filename) { + dev_err(card->dev, "Missing base topology filename for function topology\n"); + return -EINVAL; + } + ret = sscanf(mach->sof_tplg_filename, "sof-%3s-*.tplg", platform); if (ret != 1) { dev_err(card->dev, "Invalid platform name %s of tplg %s\n", @@ -101,10 +106,7 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ dev_dbg(card->dev, "dai_link %s is not supported by separated tplg yet\n", dai_link->name); - if (best_effort) - continue; - - return 0; + continue; } if (tplg_mask & BIT(tplg_dev)) continue; diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 5de33ff0ae7cb7..1269447a787b69 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -82,7 +82,10 @@ struct asoc_sdw_codec_info codec_info_list[] = { .direction = {true, false}, .dai_name = "tac5xx2-aif1", .dai_type = SOC_SDW_DAI_TYPE_AMP, - .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, + .dailink = { + SOC_SDW_AMP_OUT_DAI_ID, + SOC_SDW_UNUSED_DAI_ID, + }, .init = asoc_sdw_ti_amp_init, .rtd_init = asoc_sdw_ti_tac5xx2_spk_rtd_init, .controls = lr_spk_controls, @@ -103,7 +106,10 @@ struct asoc_sdw_codec_info codec_info_list[] = { .direction = {true, true}, .dai_name = "tac5xx2-aif3", .dai_type = SOC_SDW_DAI_TYPE_JACK, - .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, + .dailink = { + SOC_SDW_JACK_OUT_DAI_ID, + SOC_SDW_JACK_IN_DAI_ID, + }, .controls = generic_jack_controls, .num_controls = ARRAY_SIZE(generic_jack_controls), .widgets = generic_jack_widgets, @@ -1829,6 +1835,29 @@ static int is_sdca_aux_dev_present(struct device *dev, return ret; } +/* + * Check if a codec device has any endpoints matching the dai_type_mask. + * Returns true if no filtering (mask == 0) or at least one endpoint matches. + */ +static bool adr_dev_matches_dai_type_mask(const struct snd_soc_acpi_adr_device *adr_dev, + struct asoc_sdw_codec_info *codec_info, + u32 dai_type_mask) +{ + int j; + + if (!dai_type_mask) + return true; + + for (j = 0; j < adr_dev->num_endpoints; j++) { + int ep_num = adr_dev->endpoints[j].num; + + if (ep_num < codec_info->dai_num && + (dai_type_mask & BIT(codec_info->dais[ep_num].dai_type))) + return true; + } + return false; +} + int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, int *num_devs, int *num_ends, int *num_aux) { @@ -1851,6 +1880,11 @@ int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, if (!codec_info) return -EINVAL; + /* Skip aux devices for codecs with no matching endpoints */ + if (!adr_dev_matches_dai_type_mask(adr_dev, codec_info, + mach_params->dai_type_mask)) + continue; + for (j = 0; j < codec_info->aux_num; j++) { ret = is_sdca_aux_dev_present(dev, codec_info->auxs[j].codec_name, adr_link, i); @@ -2015,29 +2049,34 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, if (!codec_info) return -EINVAL; - for (j = 0; j < codec_info->aux_num; j++) { - struct snd_soc_component *component; + /* Skip aux devices for codecs with no matching endpoints */ + if (adr_dev_matches_dai_type_mask(adr_dev, codec_info, + mach_params->dai_type_mask)) { + for (j = 0; j < codec_info->aux_num; j++) { + struct snd_soc_component *component; + const char *aux_name = codec_info->auxs[j].codec_name; - ret = is_sdca_aux_dev_present(dev, codec_info->auxs[j].codec_name, - adr_link, i); - if (ret < 0) - return ret; + ret = is_sdca_aux_dev_present(dev, aux_name, adr_link, i); + if (ret < 0) + return ret; - if (ret == 0) - continue; + if (ret == 0) + continue; - component = snd_soc_lookup_component_by_name(codec_info->auxs[j].codec_name); - if (component) { - dev_dbg(dev, "%s found component %s for aux name %s\n", - __func__, component->name, - codec_info->auxs[j].codec_name); - soc_aux->dlc.name = component->name; - } else { - dev_dbg(dev, "%s the aux component %s is not registered yet\n", - __func__, codec_info->auxs[j].codec_name); - return -EPROBE_DEFER; + component = snd_soc_lookup_component_by_name(aux_name); + if (component) { + dev_dbg(dev, + "found component %s for aux name %s\n", + component->name, aux_name); + soc_aux->dlc.name = component->name; + } else { + dev_dbg(dev, + "aux component %s is not registered yet\n", + aux_name); + return -EPROBE_DEFER; + } + soc_aux++; } - soc_aux++; } ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic; @@ -2061,6 +2100,14 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, adr_end = &adr_dev->endpoints[j]; dai_info = &codec_info->dais[adr_end->num]; + + /* Filter by DAI type if mask is set */ + if (mach_params->dai_type_mask && + !(mach_params->dai_type_mask & BIT(dai_info->dai_type))) { + (*num_devs)--; + continue; + } + soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end); /* diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index b8402802ae7848..3a8da272248328 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -207,6 +207,16 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) struct snd_soc_dpcm *dpcm; int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ + /* + * The core will not send a STOP trigger on free if the device is in + * DRAIN state, but we need to stop BE and FE before we can proceed to + * free the stream. + * Run the STOP trigger if the DPCM state is START (DRAIN is not + * changing the DPCM state). + */ + if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_START) + cstream->ops->trigger(cstream, SNDRV_PCM_TRIGGER_STOP); + snd_soc_card_mutex_lock(fe->card); snd_soc_dpcm_mutex_lock(fe); @@ -273,31 +283,84 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) return ret; } -static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) +static int soc_compr_trigger_fe_be(struct snd_compr_stream *cstream, int cmd, + bool fe_first) { struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(fe, 0); + int ret; + + if (fe_first) { + dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n", + fe->dai_link->name, cmd); + + ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd); + if (ret < 0) + goto out; + + ret = snd_soc_component_compr_trigger(cstream, cmd); + if (ret < 0) + goto out; + + ret = dpcm_be_dai_trigger(fe, cstream->direction, cmd); + } else { + dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n", + fe->dai_link->name, cmd); + + ret = dpcm_be_dai_trigger(fe, cstream->direction, cmd); + if (ret < 0) + goto out; + + ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd); + if (ret < 0) + goto out; + + ret = snd_soc_component_compr_trigger(cstream, cmd); + } + +out: + return snd_soc_ret(fe->dev, ret, "trigger FE cmd: %d failed\n", cmd); +} + +static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) +{ + struct snd_soc_pcm_runtime *fe = cstream->private_data; int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ + bool fe_first; int ret; if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN || cmd == SND_COMPR_TRIGGER_DRAIN) return snd_soc_component_compr_trigger(cstream, cmd); + if (fe->dai_link->trigger[stream] == SND_SOC_DPCM_TRIGGER_POST) + fe_first = false; + else + fe_first = true; + snd_soc_card_mutex_lock(fe->card); - ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd); - if (ret < 0) - goto out; + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = soc_compr_trigger_fe_be(cstream, cmd, fe_first); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = soc_compr_trigger_fe_be(cstream, cmd, !fe_first); + break; + default: + ret = -EINVAL; + break; + } - ret = snd_soc_component_compr_trigger(cstream, cmd); if (ret < 0) goto out; - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; - - ret = dpcm_be_dai_trigger(fe, stream, cmd); - switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 3a9342633c4e65..c7caaa1c01a72f 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1987,6 +1987,7 @@ static void soc_check_tplg_fes(struct snd_soc_card *card) struct snd_soc_component *component; const struct snd_soc_component_driver *comp_drv; struct snd_soc_dai_link *dai_link; + struct snd_soc_dai_link_component *cpu; int i; for_each_component(component) { @@ -2021,11 +2022,35 @@ static void soc_check_tplg_fes(struct snd_soc_card *card) continue; } - if (component->dev->of_node) + if (component->dev->of_node) { dai_link->platforms->of_node = component->dev->of_node; - else + } else { + struct snd_soc_dai *dai; + int j; + dai_link->platforms->name = component->name; + /* + * Override CPU component name to ensure the + * correct component is matched when multiple + * components provide identically-named DAIs. + * Only override when the DAI is actually + * provided by this component. + */ + for_each_link_cpus(dai_link, j, cpu) { + if (cpu->of_node) + continue; + + for_each_component_dais(component, dai) { + if (cpu->dai_name && + !strcmp(dai->name, cpu->dai_name)) { + cpu->name = component->name; + break; + } + } + } + } + /* convert non BE into BE */ dai_link->no_pcm = 1; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 0e49290a8c903b..e593aabc10e86d 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1790,6 +1790,8 @@ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream, } __soc_pcm_close(be, be_substream); + if (fe->fe_compr) + kfree(be_substream->runtime); be_substream->runtime = NULL; be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; } @@ -1837,7 +1839,16 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: open %s BE %s\n", snd_pcm_direction_name(stream), be->dai_link->name); - be_substream->runtime = fe_substream->runtime; + if (!fe->fe_compr) { + be_substream->runtime = fe_substream->runtime; + } else { + be_substream->runtime = kzalloc(sizeof(*be_substream->runtime), GFP_KERNEL); + if (!be_substream->runtime) { + err = -ENOMEM; + goto unwind; + } + } + err = __soc_pcm_open(be, be_substream); if (err < 0) { be->dpcm[stream].users--; diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index f73a0e534851c8..3f5b541a592ba6 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -286,6 +286,7 @@ endif ## SND_SOC_SOF_DEVELOPER_SUPPORT config SND_SOC_SOF tristate select SND_SOC_TOPOLOGY + select SND_SOC_SOF_CLIENT select SND_SOC_SOF_NOCODEC if SND_SOC_SOF_NOCODEC_SUPPORT select SND_SOC_SOF_NOCODEC if SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT help diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 18dea3b4ade4fc..64d4159a2d2388 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -8,10 +8,12 @@ snd-sof-y := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ ifneq ($(CONFIG_SND_SOC_SOF_IPC3),) snd-sof-y += ipc3.o ipc3-loader.o ipc3-topology.o ipc3-control.o ipc3-pcm.o\ ipc3-dtrace.o +snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += ipc3-compress.o endif ifneq ($(CONFIG_SND_SOC_SOF_IPC4),) snd-sof-y += ipc4.o ipc4-loader.o ipc4-topology.o ipc4-control.o ipc4-pcm.o\ ipc4-mtrace.o ipc4-telemetry.o +snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += ipc4-compress.o endif # SOF client support @@ -19,8 +21,6 @@ ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),) snd-sof-y += sof-client.o endif -snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += compress.o - snd-sof-pci-y := sof-pci-dev.o snd-sof-acpi-y := sof-acpi-dev.o snd-sof-of-y := sof-of-dev.o @@ -28,6 +28,7 @@ snd-sof-of-y := sof-of-dev.o snd-sof-ipc-flood-test-y := sof-client-ipc-flood-test.o snd-sof-ipc-msg-injector-y := sof-client-ipc-msg-injector.o snd-sof-ipc-kernel-injector-y := sof-client-ipc-kernel-injector.o +snd-sof-audio-y := sof-client-audio.o snd-sof-probes-y := sof-client-probes.o ifneq ($(CONFIG_SND_SOC_SOF_IPC3),) snd-sof-probes-y += sof-client-probes-ipc3.o @@ -42,6 +43,7 @@ snd-sof-nocodec-y := nocodec.o snd-sof-utils-y := sof-utils.o obj-$(CONFIG_SND_SOC_SOF) += snd-sof.o +obj-$(CONFIG_SND_SOC_SOF) += snd-sof-audio.o obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o obj-$(CONFIG_SND_SOC_SOF) += snd-sof-utils.o diff --git a/sound/soc/sof/amd/acp-common.c b/sound/soc/sof/amd/acp-common.c index 0c3a92f5f942d0..0b73454a59b777 100644 --- a/sound/soc/sof/amd/acp-common.c +++ b/sound/soc/sof/amd/acp-common.c @@ -12,6 +12,7 @@ #include "../sof-priv.h" #include "../sof-audio.h" +#include "../sof-client.h" #include "../ops.h" #include "acp.h" #include "acp-dsp-offset.h" @@ -224,23 +225,15 @@ const struct snd_sof_dsp_ops sof_acp_common_ops = { .get_window_offset = acp_sof_ipc_get_window_offset, .irq_thread = acp_sof_ipc_irq_thread, - /* stream callbacks */ - .pcm_open = acp_pcm_open, - .pcm_close = acp_pcm_close, - .pcm_hw_params = acp_pcm_hw_params, - .pcm_pointer = acp_pcm_pointer, - - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, - /* Machine driver callbacks */ .machine_select = amd_sof_machine_select, .machine_register = sof_machine_register, .machine_unregister = sof_machine_unregister, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* Trace Logger */ .trace_init = acp_sof_trace_init, .trace_release = acp_sof_trace_release, @@ -263,5 +256,6 @@ EXPORT_SYMBOL_NS(sof_acp_common_ops, "SND_SOC_SOF_AMD_COMMON"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("ACP SOF COMMON Driver"); MODULE_IMPORT_NS("SND_SOC_SOF_AMD_COMMON"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT"); diff --git a/sound/soc/sof/amd/acp-pcm.c b/sound/soc/sof/amd/acp-pcm.c index 2802684f26de17..2692407e62ca2b 100644 --- a/sound/soc/sof/amd/acp-pcm.c +++ b/sound/soc/sof/amd/acp-pcm.c @@ -16,10 +16,12 @@ #include "acp.h" #include "acp-dsp-offset.h" -int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, +int acp_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_pcm_runtime *runtime = substream->runtime; struct acp_dsp_stream *stream = runtime->private_data; unsigned int buf_offset, index; @@ -54,8 +56,10 @@ int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substr } EXPORT_SYMBOL_NS(acp_pcm_hw_params, "SND_SOC_SOF_AMD_COMMON"); -int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) +int acp_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct acp_dsp_stream *stream; stream = acp_dsp_stream_get(sdev, 0); @@ -69,8 +73,10 @@ int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) } EXPORT_SYMBOL_NS(acp_pcm_open, "SND_SOC_SOF_AMD_COMMON"); -int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) +int acp_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct acp_dsp_stream *stream; stream = substream->runtime->private_data; @@ -86,18 +92,18 @@ int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) } EXPORT_SYMBOL_NS(acp_pcm_close, "SND_SOC_SOF_AMD_COMMON"); -snd_pcm_uframes_t acp_pcm_pointer(struct snd_sof_dev *sdev, +snd_pcm_uframes_t acp_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_component *scomp = sdev->component; + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_pcm_stream *stream; struct sof_ipc_stream_posn posn; struct snd_sof_pcm *spcm; snd_pcm_uframes_t pos; int ret; - spcm = snd_sof_find_spcm_dai(scomp, rtd); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) { dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", rtd->dai_link->id); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 2799e34b235395..ee6f16adc3c0b3 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -320,12 +320,12 @@ int acp_dsp_stream_put(struct snd_sof_dev *sdev, struct acp_dsp_stream *acp_stre /* * DSP PCM Operations. */ -int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); -int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); -int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, +int acp_pcm_open(struct snd_soc_component *component, struct snd_pcm_substream *substream); +int acp_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream); +int acp_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params); -snd_pcm_uframes_t acp_pcm_pointer(struct snd_sof_dev *sdev, +snd_pcm_uframes_t acp_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream); extern const struct snd_sof_dsp_ops sof_acp_common_ops; diff --git a/sound/soc/sof/amd/acp63.c b/sound/soc/sof/amd/acp63.c index a686620b13588b..10dc2113f45546 100644 --- a/sound/soc/sof/amd/acp63.c +++ b/sound/soc/sof/amd/acp63.c @@ -130,13 +130,28 @@ static struct snd_soc_dai_driver acp63_sof_dai[] = { struct snd_sof_dsp_ops sof_acp63_ops; EXPORT_SYMBOL_NS(sof_acp63_ops, "SND_SOC_SOF_AMD_COMMON"); +static const struct sof_audio_ops sof_acp63_audio_ops = { + .pcm_open = acp_pcm_open, + .pcm_close = acp_pcm_close, + .pcm_hw_params = acp_pcm_hw_params, + .pcm_pointer = acp_pcm_pointer, + + .drv = acp63_sof_dai, + .num_drv = ARRAY_SIZE(acp63_sof_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + int sof_acp63_ops_init(struct snd_sof_dev *sdev) { /* common defaults */ memcpy(&sof_acp63_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); - sof_acp63_ops.drv = acp63_sof_dai; - sof_acp63_ops.num_drv = ARRAY_SIZE(acp63_sof_dai); + sdev->audio_ops = &sof_acp63_audio_ops; return 0; } diff --git a/sound/soc/sof/amd/acp70.c b/sound/soc/sof/amd/acp70.c index 8314ac4008dae1..ab9f57048fde82 100644 --- a/sound/soc/sof/amd/acp70.c +++ b/sound/soc/sof/amd/acp70.c @@ -130,13 +130,28 @@ static struct snd_soc_dai_driver acp70_sof_dai[] = { struct snd_sof_dsp_ops sof_acp70_ops; EXPORT_SYMBOL_NS(sof_acp70_ops, "SND_SOC_SOF_AMD_COMMON"); +static const struct sof_audio_ops sof_acp70_audio_ops = { + .pcm_open = acp_pcm_open, + .pcm_close = acp_pcm_close, + .pcm_hw_params = acp_pcm_hw_params, + .pcm_pointer = acp_pcm_pointer, + + .drv = acp70_sof_dai, + .num_drv = ARRAY_SIZE(acp70_sof_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + int sof_acp70_ops_init(struct snd_sof_dev *sdev) { /* common defaults */ memcpy(&sof_acp70_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); - sof_acp70_ops.drv = acp70_sof_dai; - sof_acp70_ops.num_drv = ARRAY_SIZE(acp70_sof_dai); + sdev->audio_ops = &sof_acp70_audio_ops; return 0; } diff --git a/sound/soc/sof/amd/rembrandt.c b/sound/soc/sof/amd/rembrandt.c index 86ef59743fc875..50351b4ddf6949 100644 --- a/sound/soc/sof/amd/rembrandt.c +++ b/sound/soc/sof/amd/rembrandt.c @@ -130,13 +130,28 @@ static struct snd_soc_dai_driver rembrandt_sof_dai[] = { struct snd_sof_dsp_ops sof_rembrandt_ops; EXPORT_SYMBOL_NS(sof_rembrandt_ops, "SND_SOC_SOF_AMD_COMMON"); +static const struct sof_audio_ops sof_rembrandt_audio_ops = { + .pcm_open = acp_pcm_open, + .pcm_close = acp_pcm_close, + .pcm_hw_params = acp_pcm_hw_params, + .pcm_pointer = acp_pcm_pointer, + + .drv = rembrandt_sof_dai, + .num_drv = ARRAY_SIZE(rembrandt_sof_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + int sof_rembrandt_ops_init(struct snd_sof_dev *sdev) { /* common defaults */ memcpy(&sof_rembrandt_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); - sof_rembrandt_ops.drv = rembrandt_sof_dai; - sof_rembrandt_ops.num_drv = ARRAY_SIZE(rembrandt_sof_dai); + sdev->audio_ops = &sof_rembrandt_audio_ops; return 0; } diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c index b3b4639abf5065..203bcb3610bdf1 100644 --- a/sound/soc/sof/amd/renoir.c +++ b/sound/soc/sof/amd/renoir.c @@ -105,13 +105,28 @@ static struct snd_soc_dai_driver renoir_sof_dai[] = { struct snd_sof_dsp_ops sof_renoir_ops; EXPORT_SYMBOL_NS(sof_renoir_ops, "SND_SOC_SOF_AMD_COMMON"); +static const struct sof_audio_ops sof_renoir_audio_ops = { + .pcm_open = acp_pcm_open, + .pcm_close = acp_pcm_close, + .pcm_hw_params = acp_pcm_hw_params, + .pcm_pointer = acp_pcm_pointer, + + .drv = renoir_sof_dai, + .num_drv = ARRAY_SIZE(renoir_sof_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + int sof_renoir_ops_init(struct snd_sof_dev *sdev) { /* common defaults */ memcpy(&sof_renoir_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); - sof_renoir_ops.drv = renoir_sof_dai; - sof_renoir_ops.num_drv = ARRAY_SIZE(renoir_sof_dai); + sdev->audio_ops = &sof_renoir_audio_ops; return 0; } diff --git a/sound/soc/sof/amd/vangogh.c b/sound/soc/sof/amd/vangogh.c index 6ed5f9aaa414e6..9057ed9b39e91c 100644 --- a/sound/soc/sof/amd/vangogh.c +++ b/sound/soc/sof/amd/vangogh.c @@ -151,6 +151,22 @@ static int sof_vangogh_post_fw_run_delay(struct snd_sof_dev *sdev) struct snd_sof_dsp_ops sof_vangogh_ops; EXPORT_SYMBOL_NS(sof_vangogh_ops, "SND_SOC_SOF_AMD_COMMON"); +static const struct sof_audio_ops sof_vangogh_audio_ops = { + .pcm_open = acp_pcm_open, + .pcm_close = acp_pcm_close, + .pcm_hw_params = acp_pcm_hw_params, + .pcm_pointer = acp_pcm_pointer, + + .drv = vangogh_sof_dai, + .num_drv = ARRAY_SIZE(vangogh_sof_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + int sof_vangogh_ops_init(struct snd_sof_dev *sdev) { const struct dmi_system_id *dmi_id; @@ -159,8 +175,7 @@ int sof_vangogh_ops_init(struct snd_sof_dev *sdev) /* common defaults */ memcpy(&sof_vangogh_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); - sof_vangogh_ops.drv = vangogh_sof_dai; - sof_vangogh_ops.num_drv = ARRAY_SIZE(vangogh_sof_dai); + sdev->audio_ops = &sof_vangogh_audio_ops; dmi_id = dmi_first_match(acp_sof_quirk_table); if (dmi_id) { diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 74d997a4f6203c..24b32c86e2c2c9 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -21,7 +21,7 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol, struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->volume_get) @@ -36,7 +36,7 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol, struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->volume_put) @@ -73,7 +73,7 @@ int snd_sof_switch_get(struct snd_kcontrol *kcontrol, struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->switch_get) @@ -88,7 +88,7 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->switch_put) @@ -103,7 +103,7 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol, struct soc_enum *se = (struct soc_enum *)kcontrol->private_value; struct snd_sof_control *scontrol = se->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->enum_get) @@ -118,7 +118,7 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, struct soc_enum *se = (struct soc_enum *)kcontrol->private_value; struct snd_sof_control *scontrol = se->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->enum_put) @@ -133,7 +133,7 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_get) @@ -148,7 +148,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_put) @@ -164,7 +164,7 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); /* make sure we have at least a header */ @@ -183,7 +183,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); int ret, err; @@ -219,7 +219,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_get) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 5fa8c40de5d9a1..cd5aea017d2e62 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -469,11 +469,9 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE); - /* set up platform component driver */ - snd_sof_new_platform_drv(sdev); - if (sdev->dspless_mode_selected) { sof_set_fw_state(sdev, SOF_DSPLESS_MODE); + goto skip_dsp_init; } @@ -550,27 +548,10 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) /* hereafter all FW boot flows are for PM reasons */ sdev->first_boot = false; - /* now register audio DSP platform driver and dai */ - ret = devm_snd_soc_register_component(sdev->dev, &sdev->plat_drv, - sof_ops(sdev)->drv, - sof_ops(sdev)->num_drv); - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to register DSP DAI driver %d\n", ret); - goto fw_trace_err; - } - - ret = snd_sof_machine_register(sdev, plat_data); - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to register machine driver %d\n", ret); - goto fw_trace_err; - } - ret = sof_register_clients(sdev); if (ret < 0) { dev_err(sdev->dev, "failed to register clients %d\n", ret); - goto sof_machine_err; + goto fw_trace_err; } /* @@ -588,8 +569,6 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) return 0; -sof_machine_err: - snd_sof_machine_unregister(sdev, plat_data); fw_trace_err: sof_fw_trace_free(sdev); fw_run_err: @@ -674,23 +653,20 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sof_apply_profile_override(&plat_data->ipc_file_profile_base, plat_data); + sdev->audio_ops = plat_data->desc->audio_ops; + /* Initialize sof_ops based on the initial selected IPC version */ ret = sof_init_sof_ops(sdev); if (ret) return ret; - INIT_LIST_HEAD(&sdev->pcm_list); - INIT_LIST_HEAD(&sdev->kcontrol_list); - INIT_LIST_HEAD(&sdev->widget_list); - INIT_LIST_HEAD(&sdev->pipeline_list); - INIT_LIST_HEAD(&sdev->dai_list); - INIT_LIST_HEAD(&sdev->dai_link_list); - INIT_LIST_HEAD(&sdev->route_list); + INIT_LIST_HEAD(&sdev->audio_instance_list); INIT_LIST_HEAD(&sdev->ipc_client_list); INIT_LIST_HEAD(&sdev->ipc_rx_handler_list); INIT_LIST_HEAD(&sdev->fw_state_handler_list); spin_lock_init(&sdev->ipc_lock); spin_lock_init(&sdev->hw_lock); + spin_lock_init(&sdev->audio_instance_list_lock); mutex_init(&sdev->power_state_access); mutex_init(&sdev->ipc_client_mutex); mutex_init(&sdev->client_event_handler_mutex); @@ -746,7 +722,6 @@ EXPORT_SYMBOL(snd_sof_device_probe_completed); int snd_sof_device_remove(struct device *dev) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); - struct snd_sof_pdata *pdata = sdev->pdata; int ret; bool aborted = false; @@ -759,13 +734,6 @@ int snd_sof_device_remove(struct device *dev) */ sof_unregister_clients(sdev); - /* - * Unregister machine driver. This will unbind the snd_card which - * will remove the component driver and unload the topology - * before freeing the snd_card. - */ - snd_sof_machine_unregister(sdev, pdata); - /* * Balance the runtime pm usage count in case we are faced with an * exception and we forcably prevented D3 power state to preserve diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 9801c875dd7018..908462fec20c05 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -355,8 +355,6 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) (char **)&plat_data->tplg_filename_prefix); debugfs_create_str("fw_name", 0444, fw_profile, (char **)&plat_data->fw_filename); - debugfs_create_str("tplg_name", 0444, fw_profile, - (char **)&plat_data->tplg_filename); debugfs_create_u32("ipc_type", 0444, fw_profile, (u32 *)&plat_data->ipc_type); diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index 7a03c8cc5dd473..65daa45fda5fb6 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -12,6 +12,7 @@ #include #include "../ops.h" +#include "../sof-client.h" #include "imx-common.h" @@ -455,10 +456,11 @@ const struct snd_sof_dsp_ops sof_imx_ops = { .get_bar_index = imx_get_bar_index, .load_firmware = snd_sof_load_firmware_memcpy, - .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, - .pcm_open = sof_stream_pcm_open, - .pcm_close = sof_stream_pcm_close, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, .runtime_suspend = imx_runtime_suspend, .runtime_resume = imx_runtime_resume, @@ -466,15 +468,9 @@ const struct snd_sof_dsp_ops sof_imx_ops = { .resume = imx_resume, .set_power_state = imx_set_power_state, - - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; EXPORT_SYMBOL(sof_imx_ops); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF helpers for IMX platforms"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); diff --git a/sound/soc/sof/imx/imx-common.h b/sound/soc/sof/imx/imx-common.h index 9bd711dbb5d03f..8c21b3a652d068 100644 --- a/sound/soc/sof/imx/imx-common.h +++ b/sound/soc/sof/imx/imx-common.h @@ -100,8 +100,7 @@ struct imx_chip_info { /* does the chip have a reserved memory region for DMA? */ bool has_dma_reserved; struct imx_memory_info *memory; - struct snd_soc_dai_driver *drv; - int num_drv; + const struct sof_audio_ops *audio_ops; /* optional */ const struct imx_chip_ops *ops; }; diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 7e9eab2e303472..12baa41c52413b 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -270,6 +270,37 @@ static struct snd_soc_dai_driver imx8ulp_dai[] = { IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai6", 1, 32), }; +#define SOF_IMX_HW_INFO (SNDRV_PCM_INFO_MMAP | \ + SNDRV_PCM_INFO_MMAP_VALID | \ + SNDRV_PCM_INFO_INTERLEAVED | \ + SNDRV_PCM_INFO_PAUSE | \ + SNDRV_PCM_INFO_BATCH | \ + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) + +static const struct sof_audio_ops sof_imx8_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + .drv = imx8_dai, + .num_drv = ARRAY_SIZE(imx8_dai), + .hw_info = SOF_IMX_HW_INFO, +}; + +static const struct sof_audio_ops sof_imx8m_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + .drv = imx8m_dai, + .num_drv = ARRAY_SIZE(imx8m_dai), + .hw_info = SOF_IMX_HW_INFO, +}; + +static const struct sof_audio_ops sof_imx8ulp_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + .drv = imx8ulp_dai, + .num_drv = ARRAY_SIZE(imx8ulp_dai), + .hw_info = SOF_IMX_HW_INFO, +}; + static struct snd_sof_dsp_ops sof_imx8_ops; static int imx8_ops_init(struct snd_sof_dev *sdev) @@ -283,9 +314,7 @@ static int imx8_ops_init(struct snd_sof_dev *sdev) sof_imx8_ops.debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem; - /* ... and finally set DAI driver */ - sof_imx8_ops.drv = get_chip_info(sdev)->drv; - sof_imx8_ops.num_drv = get_chip_info(sdev)->num_drv; + sdev->audio_ops = get_chip_info(sdev)->audio_ops; return 0; } @@ -339,8 +368,7 @@ static const struct imx_chip_info imx8_chip_info = { .window_offset = 0x800000, }, .memory = imx8_memory_regions, - .drv = imx8_dai, - .num_drv = ARRAY_SIZE(imx8_dai), + .audio_ops = &sof_imx8_audio_ops, .ops = &imx8_chip_ops, }; @@ -351,8 +379,7 @@ static const struct imx_chip_info imx8x_chip_info = { .window_offset = 0x800000, }, .memory = imx8_memory_regions, - .drv = imx8_dai, - .num_drv = ARRAY_SIZE(imx8_dai), + .audio_ops = &sof_imx8_audio_ops, .ops = &imx8x_chip_ops, }; @@ -363,8 +390,7 @@ static const struct imx_chip_info imx8m_chip_info = { .window_offset = 0x800000, }, .memory = imx8m_memory_regions, - .drv = imx8m_dai, - .num_drv = ARRAY_SIZE(imx8m_dai), + .audio_ops = &sof_imx8m_audio_ops, .ops = &imx8m_chip_ops, }; @@ -376,8 +402,7 @@ static const struct imx_chip_info imx8ulp_chip_info = { }, .has_dma_reserved = true, .memory = imx8ulp_memory_regions, - .drv = imx8ulp_dai, - .num_drv = ARRAY_SIZE(imx8ulp_dai), + .audio_ops = &sof_imx8ulp_audio_ops, .ops = &imx8ulp_chip_ops, }; diff --git a/sound/soc/sof/imx/imx9.c b/sound/soc/sof/imx/imx9.c index e56e8a1c80222e..117cbd71bc4bc7 100644 --- a/sound/soc/sof/imx/imx9.c +++ b/sound/soc/sof/imx/imx9.c @@ -14,6 +14,19 @@ static struct snd_soc_dai_driver imx95_dai[] = { IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai3", 1, 32), }; +static const struct sof_audio_ops sof_imx95_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + .drv = imx95_dai, + .num_drv = ARRAY_SIZE(imx95_dai), + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + static struct snd_sof_dsp_ops sof_imx9_ops; static int imx95_ops_init(struct snd_sof_dev *sdev) @@ -21,9 +34,7 @@ static int imx95_ops_init(struct snd_sof_dev *sdev) /* first copy from template */ memcpy(&sof_imx9_ops, &sof_imx_ops, sizeof(sof_imx_ops)); - /* ... and finally set DAI driver */ - sof_imx9_ops.drv = get_chip_info(sdev)->drv; - sof_imx9_ops.num_drv = get_chip_info(sdev)->num_drv; + sdev->audio_ops = get_chip_info(sdev)->audio_ops; return 0; } @@ -74,8 +85,7 @@ static const struct imx_chip_info imx95_chip_info = { }, .has_dma_reserved = true, .memory = imx95_memory_regions, - .drv = imx95_dai, - .num_drv = ARRAY_SIZE(imx95_dai), + .audio_ops = &sof_imx95_audio_ops, .ops = &imx95_chip_ops, }; diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index e31f4c4061d80e..d23394e69406aa 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -186,8 +186,6 @@ config SND_SOC_SOF_INTEL_ICL tristate select SND_SOC_SOF_HDA_GENERIC select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE - select SND_SOC_SOF_IPC3 - select SND_SOC_SOF_IPC4 select SND_SOC_SOF_INTEL_CNL config SND_SOC_SOF_ICELAKE @@ -214,9 +212,8 @@ config SND_SOC_SOF_INTEL_TGL tristate select SND_SOC_SOF_HDA_GENERIC select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE - select SND_SOC_SOF_IPC3 - select SND_SOC_SOF_IPC4 select SND_SOC_SOF_INTEL_CNL + select SND_SOC_SOF_COMPRESS config SND_SOC_SOF_TIGERLAKE tristate "SOF support for Tigerlake" @@ -253,6 +250,7 @@ config SND_SOC_SOF_INTEL_MTL select SND_SOC_SOF_HDA_GENERIC select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE select SND_SOC_SOF_IPC4 + select SND_SOC_SOF_COMPRESS config SND_SOC_SOF_METEORLAKE tristate "SOF support for Meteorlake" @@ -270,7 +268,6 @@ config SND_SOC_SOF_INTEL_LNL select SND_SOC_SOF_HDA_GENERIC select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_SOUNDWIRE != n - select SND_SOC_SOF_IPC4 select SND_SOC_SOF_INTEL_MTL config SND_SOC_SOF_LUNARLAKE @@ -287,7 +284,6 @@ config SND_SOC_SOF_INTEL_PTL tristate select SND_SOC_SOF_HDA_COMMON select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE - select SND_SOC_SOF_IPC4 select SND_SOC_SOF_INTEL_LNL config SND_SOC_SOF_PANTHERLAKE @@ -304,7 +300,6 @@ config SND_SOC_SOF_INTEL_NVL tristate select SND_SOC_SOF_HDA_COMMON select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE - select SND_SOC_SOF_IPC4 select SND_SOC_SOF_INTEL_PTL config SND_SOC_SOF_NOVALAKE diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index b0072601181efc..6579e84f6a25f1 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -71,6 +71,7 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev) /* ipc */ sof_apl_ops.send_msg = hda_dsp_ipc4_send_msg; + sof_apl_ops.msg_timeout_handler = hda_dsp_ipc4_msg_timeout_handler; /* debug */ sof_apl_ops.ipc_dump = hda_ipc4_dump; @@ -79,7 +80,7 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev) } /* set DAI driver ops */ - hda_set_dai_drv_ops(sdev, &sof_apl_ops); + hda_set_dai_drv_ops(sdev); /* debug */ sof_apl_ops.debug_map = apl_dsp_debugfs; diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c index 32bf5e5e597881..7962c5abbe5c34 100644 --- a/sound/soc/sof/intel/atom.c +++ b/sound/soc/sof/intel/atom.c @@ -407,8 +407,8 @@ void atom_set_mach_params(struct snd_soc_acpi_mach *mach, mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); - mach_params->num_dai_drivers = desc->ops->num_drv; - mach_params->dai_drivers = desc->ops->drv; + mach_params->num_dai_drivers = desc->audio_ops->num_drv; + mach_params->dai_drivers = desc->audio_ops->drv; } EXPORT_SYMBOL_NS(atom_set_mach_params, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP"); diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 9534d18be97dfe..bc78bd6858b4e3 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -19,6 +19,7 @@ #include #include #include "../ops.h" +#include "../sof-client.h" #include "shim.h" #include "../sof-acpi-dev.h" #include "../sof-audio.h" @@ -530,8 +531,8 @@ static void bdw_set_mach_params(struct snd_soc_acpi_mach *mach, mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); - mach_params->num_dai_drivers = desc->ops->num_drv; - mach_params->dai_drivers = desc->ops->drv; + mach_params->num_dai_drivers = desc->audio_ops->num_drv; + mach_params->dai_drivers = desc->audio_ops->drv; } /* Broadwell DAIs */ @@ -560,6 +561,20 @@ static struct snd_soc_dai_driver bdw_dai[] = { }, }; +static const struct sof_audio_ops sof_bdw_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + + .drv = bdw_dai, + .num_drv = ARRAY_SIZE(bdw_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH, +}; + /* broadwell ops */ static const struct snd_sof_dsp_ops sof_bdw_ops = { /*Device init */ @@ -593,30 +608,19 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = { .machine_unregister = sof_machine_unregister, .set_mach_params = bdw_set_mach_params, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* debug */ .debug_map = bdw_debugfs, .debug_map_count = ARRAY_SIZE(bdw_debugfs), .dbg_dump = bdw_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_close = sof_stream_pcm_close, - /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, - /* DAI drivers */ - .drv = bdw_dai, - .num_drv = ARRAY_SIZE(bdw_dai), - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_BATCH, - .dsp_arch_ops = &sof_xtensa_arch_ops, }; @@ -646,6 +650,7 @@ static const struct sof_dev_desc sof_acpi_broadwell_desc = { }, .nocodec_tplg_filename = "sof-bdw-nocodec.tplg", .ops = &sof_bdw_ops, + .audio_ops = &sof_bdw_audio_ops, }; static const struct acpi_device_id sof_broadwell_match[] = { @@ -689,5 +694,6 @@ module_platform_driver(snd_sof_acpi_intel_bdw_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF support for Broadwell platforms"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SND_SOC_SOF_ACPI_DEV"); diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 18208f77b84dc6..a946266420b5cc 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -19,6 +19,7 @@ #include #include #include "../ops.h" +#include "../sof-client.h" #include "atom.h" #include "shim.h" #include "../sof-acpi-dev.h" @@ -213,6 +214,20 @@ static int byt_acpi_probe(struct snd_sof_dev *sdev) } /* baytrail ops */ +static const struct sof_audio_ops sof_byt_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + + .drv = atom_dai, + .num_drv = 3, + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH, +}; + static const struct snd_sof_dsp_ops sof_byt_ops = { /* device init */ .probe = byt_acpi_probe, @@ -250,16 +265,16 @@ static const struct snd_sof_dsp_ops sof_byt_ops = { .machine_unregister = sof_machine_unregister, .set_mach_params = atom_set_mach_params, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* debug */ .debug_map = byt_debugfs, .debug_map_count = ARRAY_SIZE(byt_debugfs), .dbg_dump = atom_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_close = sof_stream_pcm_close, - /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, @@ -267,17 +282,6 @@ static const struct snd_sof_dsp_ops sof_byt_ops = { .suspend = byt_suspend, .resume = byt_resume, - /* DAI drivers */ - .drv = atom_dai, - .num_drv = 3, /* we have only 3 SSPs on byt*/ - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_BATCH, - .dsp_arch_ops = &sof_xtensa_arch_ops, }; @@ -288,6 +292,20 @@ static const struct sof_intel_dsp_desc byt_chip_info = { }; /* cherrytrail and braswell ops */ +static const struct sof_audio_ops sof_cht_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + + .drv = atom_dai, + .num_drv = 6, + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH, +}; + static const struct snd_sof_dsp_ops sof_cht_ops = { /* device init */ .probe = byt_acpi_probe, @@ -325,16 +343,16 @@ static const struct snd_sof_dsp_ops sof_cht_ops = { .machine_unregister = sof_machine_unregister, .set_mach_params = atom_set_mach_params, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* debug */ .debug_map = cht_debugfs, .debug_map_count = ARRAY_SIZE(cht_debugfs), .dbg_dump = atom_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_close = sof_stream_pcm_close, - /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, @@ -342,18 +360,6 @@ static const struct snd_sof_dsp_ops sof_cht_ops = { .suspend = byt_suspend, .resume = byt_resume, - /* DAI drivers */ - .drv = atom_dai, - /* all 6 SSPs may be available for cherrytrail */ - .num_drv = 6, - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_BATCH, - .dsp_arch_ops = &sof_xtensa_arch_ops, }; @@ -384,6 +390,7 @@ static const struct sof_dev_desc sof_acpi_baytrailcr_desc = { }, .nocodec_tplg_filename = "sof-byt-nocodec.tplg", .ops = &sof_byt_ops, + .audio_ops = &sof_byt_audio_ops, }; static const struct sof_dev_desc sof_acpi_baytrail_desc = { @@ -406,6 +413,7 @@ static const struct sof_dev_desc sof_acpi_baytrail_desc = { }, .nocodec_tplg_filename = "sof-byt-nocodec.tplg", .ops = &sof_byt_ops, + .audio_ops = &sof_byt_audio_ops, }; static const struct sof_dev_desc sof_acpi_cherrytrail_desc = { @@ -428,6 +436,7 @@ static const struct sof_dev_desc sof_acpi_cherrytrail_desc = { }, .nocodec_tplg_filename = "sof-cht-nocodec.tplg", .ops = &sof_cht_ops, + .audio_ops = &sof_cht_audio_ops, }; static const struct acpi_device_id sof_baytrail_match[] = { @@ -475,6 +484,7 @@ module_platform_driver(snd_sof_acpi_intel_byt_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF support for Baytrail/Cherrytrail"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SND_SOC_SOF_ACPI_DEV"); MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_ATOM_HIFI_EP"); diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 660c1475e5a43f..4545af67d92316 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -418,6 +418,7 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev) /* ipc */ sof_cnl_ops.send_msg = cnl_ipc4_send_msg; + sof_cnl_ops.msg_timeout_handler = hda_dsp_ipc4_msg_timeout_handler; /* debug */ sof_cnl_ops.ipc_dump = cnl_ipc4_dump; @@ -426,7 +427,7 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev) } /* set DAI driver ops */ - hda_set_dai_drv_ops(sdev, &sof_cnl_ops); + hda_set_dai_drv_ops(sdev); /* debug */ sof_cnl_ops.debug_map = cnl_dsp_debugfs; diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c index 746b426b1329b0..7331160a68c382 100644 --- a/sound/soc/sof/intel/hda-common-ops.c +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -11,9 +11,37 @@ */ #include "../sof-priv.h" +#include "../sof-client.h" #include "hda.h" #include "../sof-audio.h" +const struct sof_audio_ops sof_hda_audio_ops = { + .pcm_open = hda_dsp_pcm_open, + .pcm_close = hda_dsp_pcm_close, + .pcm_hw_params = hda_dsp_pcm_hw_params, + .pcm_hw_free = hda_dsp_stream_hw_free, + .pcm_trigger = hda_dsp_pcm_trigger, + .pcm_pointer = hda_dsp_pcm_pointer, + .pcm_ack = hda_dsp_pcm_ack, + + .compr_open = hda_dsp_compr_open, + .compr_hw_params = hda_dsp_compr_hw_params, + .compr_hw_free = hda_dsp_stream_compr_hw_free, + .compr_close = hda_dsp_compr_close, + .compr_trigger = hda_dsp_compr_trigger, + .compr_pointer = hda_dsp_compr_pointer, + + .drv = skl_dai, + .num_drv = SOF_SKL_NUM_DAIS, + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; +EXPORT_SYMBOL_NS(sof_hda_audio_ops, "SND_SOC_SOF_INTEL_HDA_GENERIC"); + const struct snd_sof_dsp_ops sof_hda_common_ops = { /* probe/remove/shutdown */ .probe_early = hda_dsp_probe_early, @@ -49,13 +77,7 @@ const struct snd_sof_dsp_ops sof_hda_common_ops = { .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, /* stream callbacks */ - .pcm_open = hda_dsp_pcm_open, - .pcm_close = hda_dsp_pcm_close, - .pcm_hw_params = hda_dsp_pcm_hw_params, - .pcm_hw_free = hda_dsp_stream_hw_free, - .pcm_trigger = hda_dsp_pcm_trigger, - .pcm_pointer = hda_dsp_pcm_pointer, - .pcm_ack = hda_dsp_pcm_ack, + .compr_get_dai_frame_counter = hda_dsp_compr_get_stream_llp, .get_dai_frame_counter = hda_dsp_get_stream_llp, .get_host_byte_counter = hda_dsp_get_stream_ldp, @@ -83,9 +105,11 @@ const struct snd_sof_dsp_ops sof_hda_common_ops = { .register_ipc_clients = hda_register_clients, .unregister_ipc_clients = hda_unregister_clients, + /* audio client */ + .register_audio_client = hda_register_audio_client, + .unregister_audio_client = hda_unregister_audio_client, + /* DAI drivers */ - .drv = skl_dai, - .num_drv = SOF_SKL_NUM_DAIS, .is_chain_dma_supported = hda_is_chain_dma_supported, /* PM */ @@ -96,13 +120,6 @@ const struct snd_sof_dsp_ops sof_hda_common_ops = { .runtime_idle = hda_dsp_runtime_idle, .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume, - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, - .dsp_arch_ops = &sof_xtensa_arch_ops, }; EXPORT_SYMBOL_NS(sof_hda_common_ops, "SND_SOC_SOF_INTEL_HDA_GENERIC"); diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index f0be42048db33c..c8ae4710f32cff 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -356,6 +356,39 @@ static struct hdac_ext_link *sdw_get_hlink(struct snd_sof_dev *sdev, return hdac_bus_eml_sdw_get_hlink(bus); } +/* + * Check if all CPU DAIs sharing the same pipeline (spipe) have their link + * DMA streams running. Used to gate pipeline state transitions for aggregate + * DAIs: the RUNNING IPC is deferred until the last DAI's link DMA has started. + */ +static bool hda_ipc4_all_spipe_dmas_running(struct snd_pcm_substream *substream, + struct snd_sof_pipeline *target_spipe) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *dai; + int i; + + for_each_rtd_cpu_dais(rtd, i, dai) { + struct snd_soc_dapm_widget *w; + struct snd_sof_widget *sw; + struct hdac_ext_stream *hext_stream; + + w = snd_soc_dai_get_widget(dai, substream->stream); + if (!w) + continue; + + sw = w->dobj.private; + if (!sw || sw->spipe != target_spipe) + continue; + + hext_stream = snd_soc_dai_get_dma_data(dai, substream); + if (!hext_stream || !hext_stream->hstream.running) + return false; + } + + return true; +} + static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd) { @@ -383,13 +416,22 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: + /* + * For aggregate DAIs with shared pipelines, the state check + * deduplicates: the first DAI sends the IPC, subsequent DAIs + * sharing the same pipeline see it already paused and skip. + * For aggregate DAIs with different pipelines, each DAI pauses + * its own pipeline independently. + */ + if (pipeline->state == SOF_IPC4_PIPE_PAUSED) + break; + ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); if (ret < 0) return ret; pipeline->state = SOF_IPC4_PIPE_PAUSED; - break; default: dev_err(sdev->dev, "unknown trigger command %d\n", cmd); @@ -436,11 +478,13 @@ static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd) { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct snd_sof_widget *pipe_widget; struct sof_ipc4_pipeline *pipeline; struct snd_sof_widget *swidget; struct snd_soc_dapm_widget *w; + int num_cpus = rtd->dai_link->num_cpus; int ret = 0; w = snd_soc_dai_get_widget(cpu_dai, substream->stream); @@ -455,6 +499,16 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c switch (cmd) { case SNDRV_PCM_TRIGGER_START: + /* + * For aggregated DAIs (num_cpus > 1), defer pipeline RUNNING + * IPC until all CPU DAIs sharing this pipeline have started + * their link DMAs via hda_trigger(). The running state of each + * DAI's HDA stream naturally tracks completion. + */ + if (num_cpus > 1 && + !hda_ipc4_all_spipe_dmas_running(substream, swidget->spipe)) + break; + if (pipeline->state != SOF_IPC4_PIPE_PAUSED) { ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); @@ -473,6 +527,10 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c swidget->spipe->started_count++; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (num_cpus > 1 && + !hda_ipc4_all_spipe_dmas_running(substream, swidget->spipe)) + break; + ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_RUNNING); if (ret < 0) @@ -484,7 +542,7 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c case SNDRV_PCM_TRIGGER_STOP: /* * STOP/SUSPEND trigger is invoked only once when all users of this pipeline have - * been stopped. So, clear the started_count so that the pipeline can be reset + * been stopped. So, clear the started_count so that the pipeline can be reset. */ swidget->spipe->started_count = 0; break; diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index bb44d4f8a4da04..b1b2d22fce65de 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -705,7 +705,7 @@ static int hda_dai_suspend(struct hdac_bus *bus) return 0; } -static void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) +static void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev) { const struct sof_intel_dsp_desc *chip; int i; @@ -713,14 +713,14 @@ static void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops chip = get_chip_info(sdev->pdata); if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) { - for (i = 0; i < ops->num_drv; i++) { - if (strstr(ops->drv[i].name, "SSP")) - ops->drv[i].ops = &ssp_dai_ops; + for (i = 0; i < sdev->audio_ops->num_drv; i++) { + if (strstr(sdev->audio_ops->drv[i].name, "SSP")) + sdev->audio_ops->drv[i].ops = &ssp_dai_ops; } } } -static void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) +static void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev) { const struct sof_intel_dsp_desc *chip; int i; @@ -728,35 +728,35 @@ static void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_op chip = get_chip_info(sdev->pdata); if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) { - for (i = 0; i < ops->num_drv; i++) { - if (strstr(ops->drv[i].name, "DMIC")) - ops->drv[i].ops = &dmic_dai_ops; + for (i = 0; i < sdev->audio_ops->num_drv; i++) { + if (strstr(sdev->audio_ops->drv[i].name, "DMIC")) + sdev->audio_ops->drv[i].ops = &dmic_dai_ops; } } } #else -static inline void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {} -static inline void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {} +static inline void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev) {} +static inline void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev) {} #endif /* CONFIG_SND_SOC_SOF_HDA_LINK */ -void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) +void hda_set_dai_drv_ops(struct snd_sof_dev *sdev) { int i; - for (i = 0; i < ops->num_drv; i++) { + for (i = 0; i < sdev->audio_ops->num_drv; i++) { #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) - if (strstr(ops->drv[i].name, "iDisp") || - strstr(ops->drv[i].name, "Analog") || - strstr(ops->drv[i].name, "Digital")) - ops->drv[i].ops = &hda_dai_ops; + if (strstr(sdev->audio_ops->drv[i].name, "iDisp") || + strstr(sdev->audio_ops->drv[i].name, "Analog") || + strstr(sdev->audio_ops->drv[i].name, "Digital")) + sdev->audio_ops->drv[i].ops = &hda_dai_ops; #endif } - ssp_set_dai_drv_ops(sdev, ops); - dmic_set_dai_drv_ops(sdev, ops); + ssp_set_dai_drv_ops(sdev); + dmic_set_dai_drv_ops(sdev); if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4 && !hda_use_tplg_nhlt) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 9343102c4b1a9f..fe883f4cb3e7ac 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -529,12 +529,13 @@ static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value) */ static bool hda_dsp_d0i3_streaming_applicable(struct snd_sof_dev *sdev) { + struct snd_sof_audio_instance *instance; struct snd_pcm_substream *substream; struct snd_sof_pcm *spcm; bool playback_active = false; int dir; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_spcm_in_instances(spcm, sdev, instance) { for_each_pcm_streams(dir) { substream = spcm->stream[dir].substream; if (!substream || !substream->runtime) diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 2aef3954f4f7f7..3c71b0124f5dd1 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -128,6 +128,16 @@ int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) } EXPORT_SYMBOL_NS(hda_dsp_ipc4_send_msg, "SND_SOC_SOF_INTEL_HDA_COMMON"); +void hda_dsp_ipc4_msg_timeout_handler(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + + if (hdev->delayed_ipc_tx_msg == msg) + hdev->delayed_ipc_tx_msg = NULL; +} +EXPORT_SYMBOL_NS(hda_dsp_ipc4_msg_timeout_handler, "SND_SOC_SOF_INTEL_HDA_COMMON"); + void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) { struct snd_sof_ipc_msg *msg = sdev->msg; diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 16a3640728210b..7a381ce44f0559 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -98,11 +98,12 @@ u32 hda_dsp_get_bits(struct snd_sof_dev *sdev, int sample_bits) } }; -int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, +int hda_dsp_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct hdac_stream *hstream = substream->runtime->private_data; struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; @@ -151,9 +152,77 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, } EXPORT_SYMBOL_NS(hda_dsp_pcm_hw_params, "SND_SOC_SOF_INTEL_HDA_COMMON"); +int hda_dsp_compr_hw_params(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_params *params, + struct snd_sof_platform_stream_params *platform_params) +{ + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct hdac_stream *hstream = cstream->runtime->private_data; + struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + struct snd_dma_buffer *dmab; + u32 bits, rate; + int bps; + int ret; + + hstream->cstream = cstream; + dmab = cstream->runtime->dma_buffer_p; + + /* Use correct format based on the used codec */ + switch (params->codec.id) { + case SND_AUDIOCODEC_PCM: + bps = snd_pcm_format_physical_width(params->codec.format); + break; + case SND_AUDIOCODEC_VORBIS: + bps = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S16_LE); + break; + case SND_AUDIOCODEC_FLAC: + { + struct snd_dec_flac *dec_flac = ¶ms->codec.options.flac_d; + + if (dec_flac->sample_size == 16) + bps = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S16_LE); + else + bps = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32_LE); + break; + } + default: + bps = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32_LE); + } + + if (bps < 0) + return bps; + bits = hda_dsp_get_bits(sdev, bps); + rate = hda_dsp_get_mult_div(sdev, params->codec.sample_rate); + + hstream->format_val = rate | bits | (params->codec.ch_out - 1); + hstream->bufsize = cstream->runtime->buffer_size; + hstream->period_bytes = cstream->runtime->fragment_size; + hstream->no_period_wakeup = false; + + /* params is not used so pass NULL */ + dmab = cstream->runtime->dma_buffer_p; + ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL); + if (ret < 0) { + dev_err(sdev->dev, "%s: hdac prepare failed: %d\n", __func__, ret); + return ret; + } + + if (hda) + platform_params->no_ipc_position = hda->no_ipc_position; + + platform_params->stream_tag = hstream->stream_tag; + + return 0; +} +EXPORT_SYMBOL_NS(hda_dsp_compr_hw_params, "SND_SOC_SOF_INTEL_HDA_COMMON"); + /* update SPIB register with appl position */ -int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) +int hda_dsp_pcm_ack(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct hdac_stream *hstream = substream->runtime->private_data; struct snd_pcm_runtime *runtime = substream->runtime; ssize_t appl_pos, buf_size; @@ -174,9 +243,10 @@ int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substrea } EXPORT_SYMBOL_NS(hda_dsp_pcm_ack, "SND_SOC_SOF_INTEL_HDA_COMMON"); -int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, +int hda_dsp_pcm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct hdac_stream *hstream = substream->runtime->private_data; struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); @@ -184,17 +254,28 @@ int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, } EXPORT_SYMBOL_NS(hda_dsp_pcm_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON"); -snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, +int hda_dsp_compr_trigger(struct snd_soc_component *component, + struct snd_compr_stream *cstream, int cmd) +{ + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct hdac_stream *hstream = cstream->runtime->private_data; + struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); + + return hda_dsp_stream_trigger(sdev, hext_stream, cmd); +} +EXPORT_SYMBOL_NS(hda_dsp_compr_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON"); + +snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_component *scomp = sdev->component; + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct hdac_stream *hstream = substream->runtime->private_data; struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct snd_sof_pcm *spcm; snd_pcm_uframes_t pos; - spcm = snd_sof_find_spcm_dai(scomp, rtd); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) { dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", rtd->dai_link->id); @@ -216,19 +297,34 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, } EXPORT_SYMBOL_NS(hda_dsp_pcm_pointer, "SND_SOC_SOF_INTEL_HDA_COMMON"); -int hda_dsp_pcm_open(struct snd_sof_dev *sdev, +int hda_dsp_compr_pointer(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp64 *tstamp) +{ + struct hdac_stream *hstream = cstream->runtime->private_data; + + /* hstream->curr_pos is updated when we receive the ioc */ + tstamp->copied_total = hstream->curr_pos; + + tstamp->byte_offset = hda_dsp_stream_get_position(hstream, cstream->direction, true); + + return 0; +} +EXPORT_SYMBOL_NS(hda_dsp_compr_pointer, "SND_SOC_SOF_INTEL_HDA_COMMON"); + +int hda_dsp_pcm_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_component *scomp = sdev->component; struct hdac_ext_stream *dsp_stream; struct snd_sof_pcm *spcm; int direction = substream->stream; u32 flags = 0; - spcm = snd_sof_find_spcm_dai(scomp, rtd); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) { dev_err(sdev->dev, "error: can't find PCM with DAI ID %d\n", rtd->dai_link->id); return -EINVAL; @@ -342,9 +438,46 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, } EXPORT_SYMBOL_NS(hda_dsp_pcm_open, "SND_SOC_SOF_INTEL_HDA_COMMON"); -int hda_dsp_pcm_close(struct snd_sof_dev *sdev, +int hda_dsp_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *cstream) +{ + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct hdac_ext_stream *dsp_stream; + struct snd_sof_pcm *spcm; + int direction = cstream->direction; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) { + dev_err(sdev->dev, "%s: can't find PCM with DAI ID %d\n", + __func__, rtd->dai_link->id); + return -EINVAL; + } + + dsp_stream = hda_dsp_stream_get(sdev, direction, 0); + if (!dsp_stream) { + dev_err(sdev->dev, "%s: no stream available\n", __func__); + return -ENODEV; + } + + /* binding compr stream to hda stream */ + cstream->runtime->private_data = &dsp_stream->hstream; + + /* + * Reset the llp cache values (they are used for LLP compensation in + * case the counter is not reset) + */ + dsp_stream->pplcllpl = 0; + dsp_stream->pplcllpu = 0; + + return 0; +} +EXPORT_SYMBOL_NS(hda_dsp_compr_open, "SND_SOC_SOF_INTEL_HDA_COMMON"); + +int hda_dsp_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct hdac_stream *hstream = substream->runtime->private_data; int direction = substream->stream; int ret; @@ -361,3 +494,23 @@ int hda_dsp_pcm_close(struct snd_sof_dev *sdev, return 0; } EXPORT_SYMBOL_NS(hda_dsp_pcm_close, "SND_SOC_SOF_INTEL_HDA_COMMON"); + +int hda_dsp_compr_close(struct snd_soc_component *component, + struct snd_compr_stream *cstream) +{ + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct hdac_stream *hstream = cstream->runtime->private_data; + int direction = cstream->direction; + int ret; + + ret = hda_dsp_stream_put(sdev, direction, hstream->stream_tag); + if (ret) + return -ENODEV; + + /* unbinding compress stream to hda stream */ + hstream->cstream = NULL; + cstream->runtime->private_data = NULL; + + return 0; +} +EXPORT_SYMBOL_NS(hda_dsp_compr_close, "SND_SOC_SOF_INTEL_HDA_COMMON"); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 6c4447f52c681a..f589b38b3cdf53 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -293,9 +293,8 @@ static int _hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stre { const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata); struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + struct hdac_ext_stream *hext_stream_match = NULL; struct hdac_bus *bus = sof_to_bus(sdev); - struct sof_intel_hda_stream *hda_stream; - struct hdac_ext_stream *hext_stream; struct hdac_ext_stream *link_stream; struct hdac_stream *s; bool dmi_l1_enable = true; @@ -308,6 +307,9 @@ static int _hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stre * that are DMI L1 incompatible. */ list_for_each_entry(s, &bus->stream_list, list) { + struct sof_intel_hda_stream *hda_stream; + struct hdac_ext_stream *hext_stream; + hext_stream = stream_to_hdac_ext_stream(s); hda_stream = container_of(hext_stream, struct sof_intel_hda_stream, hext_stream); @@ -315,8 +317,10 @@ static int _hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stre continue; if (s->direction == direction && s->stream_tag == stream_tag) { + hext_stream_match = hext_stream; s->opened = false; found = true; + s->curr_pos = 0; if (pair) link_stream = hext_stream; } else if (!(hda_stream->flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) { @@ -324,6 +328,20 @@ static int _hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stre } } + if (!sdev->dspless_mode_selected && !pair && hext_stream_match) { + /* + * Couple host and link DMA if link DMA is unused + * In case of channel pair the snd_hdac_ext_stream_release() will + * take care of coupling te HDA channel. + */ + if (!hext_stream_match->link_locked) { + u32 mask = BIT(hext_stream_match->hstream.index); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, + SOF_HDA_REG_PP_PPCTL, mask, 0); + } + } + spin_unlock_irq(&bus->reg_lock); /* Enable DMI L1 if permitted */ @@ -751,13 +769,12 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, return ret; } -int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream) +static int _hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, + struct hdac_stream *hstream) { - struct hdac_stream *hstream = substream->runtime->private_data; struct hdac_ext_stream *hext_stream = container_of(hstream, - struct hdac_ext_stream, - hstream); + struct hdac_ext_stream, + hstream); int ret; ret = hda_dsp_stream_reset(sdev, hstream); @@ -782,8 +799,25 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, return 0; } + +int hda_dsp_stream_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + + return _hda_dsp_stream_hw_free(sdev, substream->runtime->private_data); +} EXPORT_SYMBOL_NS(hda_dsp_stream_hw_free, "SND_SOC_SOF_INTEL_HDA_COMMON"); +int hda_dsp_stream_compr_hw_free(struct snd_soc_component *component, + struct snd_compr_stream *cstream) +{ + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + + return _hda_dsp_stream_hw_free(sdev, cstream->runtime->private_data); +} +EXPORT_SYMBOL_NS(hda_dsp_stream_compr_hw_free, "SND_SOC_SOF_INTEL_HDA_COMMON"); + bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev) { struct hdac_bus *bus = sof_to_bus(sdev); @@ -1166,11 +1200,9 @@ EXPORT_SYMBOL_NS(hda_dsp_stream_get_position, "SND_SOC_SOF_INTEL_HDA_COMMON"); * * Returns the raw Linear Link Position value */ -u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev, - struct snd_soc_component *component, - struct snd_pcm_substream *substream) +static u64 hda_dsp_get_llp(struct snd_sof_dev *sdev, + struct snd_soc_pcm_runtime *rtd, int dir) { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_pcm_runtime *be_rtd = NULL; struct hdac_ext_stream *hext_stream; struct snd_soc_dai *cpu_dai; @@ -1181,7 +1213,7 @@ u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev, * The LLP needs to be read from the Link DMA used for this FE as it is * allowed to use any combination of Link and Host channels */ - for_each_dpcm_be(rtd, substream->stream, dpcm) { + for_each_dpcm_be(rtd, dir, dpcm) { if (dpcm->fe != rtd) continue; @@ -1195,7 +1227,7 @@ u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev, if (!cpu_dai) return 0; - hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream); + hext_stream = snd_soc_dai_dma_data_get(cpu_dai, dir); if (!hext_stream) return 0; @@ -1219,8 +1251,29 @@ u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev, return merge_u64(llp_u, llp_l); } + +u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return hda_dsp_get_llp(sdev, snd_soc_substream_to_rtd(substream), + substream->stream); +} EXPORT_SYMBOL_NS(hda_dsp_get_stream_llp, "SND_SOC_SOF_INTEL_HDA_COMMON"); +/** + * hda_dsp_compr_get_stream_llp - Retrieve the LLP (Linear Link Position) of the stream + * @sdev: SOF device + * @cstream: Compress stream + * + * Returns the raw Linear Link Position value + */ +u64 hda_dsp_compr_get_stream_llp(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream) +{ + return hda_dsp_get_llp(sdev, cstream->private_data, cstream->direction); +} +EXPORT_SYMBOL_NS(hda_dsp_compr_get_stream_llp, "SND_SOC_SOF_INTEL_HDA_COMMON"); + /** * hda_dsp_get_stream_ldp - Retrieve the LDP (Linear DMA Position) of the stream * @sdev: SOF device diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 77c7d2bc6fa211..1b861bd58f8604 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -31,6 +31,8 @@ #include #include #include "../sof-audio.h" +#include "../sof-client.h" +#include "../sof-client-audio.h" #include "../sof-pci-dev.h" #include "../ops.h" #include "../ipc4-topology.h" @@ -421,6 +423,20 @@ static inline void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev) { } /* pre fw run operations */ int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; + int ret; + + /* Power down DSP if left enabled to ensure a clean boot state. */ + if (hda_dsp_core_is_enabled(sdev, chip->host_managed_cores_mask)) { + dev_dbg(sdev->dev, "DSP core enabled, power down DSP first\n"); + + ret = chip->power_down_dsp(sdev); + if (ret < 0) + dev_warn(sdev->dev, + "%s: failed to power down already-enabled DSP\n", __func__); + } + /* disable clock gating and power gating */ return hda_dsp_ctrl_clock_power_gating(sdev, false); } @@ -808,7 +824,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_FORCE_IPC_POSITION) hdev->no_ipc_position = 0; #else - hdev->no_ipc_position = sof_ops(sdev)->pcm_pointer ? 1 : 0; + hdev->no_ipc_position = sdev->audio_ops && sdev->audio_ops->pcm_pointer ? 1 : 0; #endif if (sdev->dspless_mode_selected) @@ -1446,8 +1462,8 @@ void hda_set_mach_params(struct snd_soc_acpi_mach *mach, sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) mach_params->num_dai_drivers = SOF_SKL_NUM_DAIS_NOCODEC; else - mach_params->num_dai_drivers = desc->ops->num_drv; - mach_params->dai_drivers = desc->ops->drv; + mach_params->num_dai_drivers = desc->audio_ops->num_drv; + mach_params->dai_drivers = desc->audio_ops->drv; } static int check_tplg_quirk_mask(struct snd_soc_acpi_mach *mach) @@ -1753,6 +1769,345 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) } EXPORT_SYMBOL_NS(hda_pci_intel_probe, "SND_SOC_SOF_INTEL_HDA_GENERIC"); +static bool multi_card; +module_param(multi_card, bool, 0444); +MODULE_PARM_DESC(multi_card, "Split audio into per-function cards. Experimental."); + +static bool split_hdmi; +module_param(split_hdmi, bool, 0444); +MODULE_PARM_DESC(split_hdmi, "Split monolithic audio into analog/HDMI cards."); + +struct hda_sdw_func_card { + u32 dai_type_bit; + const char *card_name; +}; + +static const struct hda_sdw_func_card sdw_func_cards[] = { + { BIT(SOC_SDW_DAI_TYPE_JACK), "jack" }, + { BIT(SOC_SDW_DAI_TYPE_AMP), "speaker" }, + { BIT(SOC_SDW_DAI_TYPE_MIC), "mic" }, +}; + +static int hda_register_hdmi_audio_client(struct snd_sof_dev *sdev, + const struct snd_soc_acpi_mach *mach, + int card_idx) +{ + static const struct snd_soc_acpi_link_adr empty_links[] = { {} }; + struct sof_audio_client_pdata pdata; + + sof_audio_client_init_pdata(sdev, &pdata); + memcpy(&pdata.machine, mach, sizeof(pdata.machine)); + pdata.machine.mach_params.links = empty_links; + pdata.machine.mach_params.link_mask = 0; + pdata.machine.mach_params.codec_mask = BIT(HDA_IDISP_ADDR); + pdata.machine.mach_params.dmic_num = 0; + pdata.machine.mach_params.card_name = "hdmi"; + /* Always use a dedicated HDA generic HDMI card. */ + pdata.machine.get_function_tplg_files = NULL; + pdata.machine.drv_name = "skl_hda_dsp_generic"; + pdata.machine.sof_tplg_filename = "sof-hda-generic-idisp.tplg"; + + return sof_client_dev_register(sdev, "audio", card_idx, &pdata, sizeof(pdata)); +} + +/* + * Group SDW DAI types that share physical codec devices. Types on the + * same device must be on the same card because ASoC components can only + * be bound to one card. + * + * Returns the number of groups. Each group's merged DAI type bitmask + * is stored in groups[] and the representative codec name in names[]. + */ +static int hda_sdw_group_dai_types(const struct snd_soc_acpi_mach *mach, + u32 *groups, const char **names, + int max_groups) +{ + const struct snd_soc_acpi_link_adr *adr_link; + int num_groups = 0; + int i, j, k; + + for (adr_link = mach->mach_params.links; + adr_link && adr_link->num_adr; adr_link++) { + for (i = 0; i < adr_link->num_adr; i++) { + const struct snd_soc_acpi_adr_device *adr_dev = + &adr_link->adr_d[i]; + struct asoc_sdw_codec_info *codec_info; + const char *dev_name = NULL; + u32 dev_types = 0; + int merged = -1; + + codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr); + if (!codec_info) + continue; + + for (j = 0; j < adr_dev->num_endpoints; j++) { + int ep_num = adr_dev->endpoints[j].num; + + if (ep_num < codec_info->dai_num) + dev_types |= + BIT(codec_info->dais[ep_num].dai_type); + } + + if (!dev_types) + continue; + + /* + * Pick a representative codec name: prefer + * name_prefix for non-amp codecs, fall back to + * the first DAI's component_name for amps. + */ + if (!codec_info->is_amp) + dev_name = codec_info->name_prefix; + else if (codec_info->dai_num && + codec_info->dais[0].component_name) + dev_name = codec_info->dais[0].component_name; + + /* Merge with any existing group that overlaps */ + for (k = 0; k < num_groups; k++) { + if (!(groups[k] & dev_types)) + continue; + + if (merged < 0) { + groups[k] |= dev_types; + if (dev_name) + names[k] = dev_name; + merged = k; + } else { + groups[merged] |= groups[k]; + groups[k] = groups[--num_groups]; + names[k] = names[num_groups]; + k--; + } + } + + if (merged < 0 && num_groups < max_groups) { + groups[num_groups] = dev_types; + names[num_groups] = dev_name; + num_groups++; + } + } + } + + return num_groups; +} + +static int hda_register_audio_client_multi(struct snd_sof_dev *sdev) +{ + static const struct snd_soc_acpi_link_adr empty_links[] = { {} }; + const struct snd_soc_acpi_mach *mach = sdev->pdata->machine; + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + struct sof_audio_client_pdata pdata; + u32 dai_groups[ARRAY_SIZE(sdw_func_cards)]; + const char *group_names[ARRAY_SIZE(sdw_func_cards)]; + int num_groups; + int card_idx = 0; + int ret, i, j; + + if (!mach) + return -ENODEV; + + num_groups = hda_sdw_group_dai_types(mach, dai_groups, group_names, + ARRAY_SIZE(dai_groups)); + + dev_dbg(sdev->dev, "multi_card: %d SDW groups, dmic: %d, idisp: %s\n", + num_groups, mach->mach_params.dmic_num, + HDA_IDISP_CODEC(mach->mach_params.codec_mask) ? "yes" : "no"); + + sof_audio_client_init_pdata(sdev, &pdata); + + /* Create one card per SDW DAI type group */ + for (i = 0; i < num_groups; i++) { + const char *card_name; + + /* + * Prefer codec device name (e.g. "cs42l43") over the + * generic function type name (e.g. "jack"). + */ + card_name = group_names[i]; + if (!card_name) { + for (j = 0; j < ARRAY_SIZE(sdw_func_cards); j++) { + if (dai_groups[i] & sdw_func_cards[j].dai_type_bit) { + card_name = sdw_func_cards[j].card_name; + break; + } + } + } + if (!card_name) + continue; + + dev_dbg(sdev->dev, "multi_card: group %d mask %#x card %s\n", + i, dai_groups[i], card_name); + + memcpy(&pdata.machine, mach, sizeof(pdata.machine)); + pdata.machine.mach_params.dai_type_mask = dai_groups[i]; + pdata.machine.mach_params.codec_mask &= ~BIT(HDA_IDISP_ADDR); + pdata.machine.mach_params.dmic_num = 0; + pdata.machine.mach_params.card_name = card_name; + ret = sof_client_dev_register(sdev, "audio", card_idx, + &pdata, sizeof(pdata)); + if (ret) { + dev_warn(sdev->dev, + "multi_card: failed to register card %s: %d\n", + card_name, ret); + continue; + } + card_idx++; + } + + /* DMIC card */ + if (mach->mach_params.dmic_num) { + memcpy(&pdata.machine, mach, sizeof(pdata.machine)); + pdata.machine.mach_params.links = empty_links; + pdata.machine.mach_params.link_mask = 0; + pdata.machine.mach_params.codec_mask &= ~BIT(HDA_IDISP_ADDR); + pdata.machine.mach_params.card_name = "dmic"; + ret = sof_client_dev_register(sdev, "audio", card_idx, + &pdata, sizeof(pdata)); + if (ret) + dev_warn(sdev->dev, + "multi_card: failed to register card dmic: %d\n", + ret); + else + card_idx++; + } + + /* HDMI card */ + if (HDA_IDISP_CODEC(mach->mach_params.codec_mask)) { + ret = hda_register_hdmi_audio_client(sdev, mach, card_idx); + if (ret) + dev_warn(sdev->dev, + "multi_card: failed to register card hdmi: %d\n", + ret); + else + card_idx++; + } + + hdev->num_audio_clients = card_idx; + + return 0; +} + +static int hda_register_audio_client_split_hdmi(struct snd_sof_dev *sdev) +{ + const struct snd_soc_acpi_mach *mach = sdev->pdata->machine; + struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + bool function_tplg_enabled; + bool has_analog_card; + struct sof_audio_client_pdata pdata; + int ret; + + hdev->num_audio_clients = 0; + + if (!mach) + return -ENODEV; + + function_tplg_enabled = !sdev->pdata->disable_function_topology && + mach->get_function_tplg_files; + + if (!HDA_IDISP_CODEC(mach->mach_params.codec_mask)) + return sof_register_audio_client(sdev); + + has_analog_card = HDA_EXT_CODEC(bus->codec_mask) || + mach->mach_params.link_mask || + mach->mach_params.dmic_num; + + if (!function_tplg_enabled && !has_analog_card) { + dev_info(sdev->dev, + "split_hdmi is not supported on this machine, using single card\n"); + return sof_register_audio_client(sdev); + } + + sof_audio_client_init_pdata(sdev, &pdata); + + /* Analog card: remove iDisp codec from the base machine configuration */ + memcpy(&pdata.machine, mach, sizeof(pdata.machine)); + pdata.machine.mach_params.codec_mask &= ~BIT(HDA_IDISP_ADDR); + pdata.machine.mach_params.card_name = NULL; + if (function_tplg_enabled) { + pdata.machine.sof_tplg_filename = mach->sof_tplg_filename ? + mach->sof_tplg_filename : sdev->pdata->tplg_filename; + } else { + pdata.machine.get_function_tplg_files = NULL; + pdata.machine.sof_tplg_filename = NULL; + } + ret = sof_client_dev_register(sdev, "audio", 0, &pdata, sizeof(pdata)); + if (ret) { + dev_warn(sdev->dev, + "split_hdmi: failed to register analog card: %d\n", ret); + return sof_register_audio_client(sdev); + } + + ret = hda_register_hdmi_audio_client(sdev, mach, 1); + if (ret) { + dev_warn(sdev->dev, + "split_hdmi: failed to register HDMI card: %d, using single card\n", ret); + sof_client_dev_unregister(sdev, "audio", 0); + return sof_register_audio_client(sdev); + } + + hdev->num_audio_clients = 2; + + return 0; +} + +int hda_register_audio_client(struct snd_sof_dev *sdev) +{ + const struct snd_soc_acpi_mach *mach = sdev->pdata->machine; + + if (multi_card) { + if (mach && !sdev->pdata->disable_function_topology && + mach->get_function_tplg_files) + return hda_register_audio_client_multi(sdev); + + dev_warn(sdev->dev, + "multi_card is only supported with function topologies, using single card\n"); + } + + if (split_hdmi) + return hda_register_audio_client_split_hdmi(sdev); + + return sof_register_audio_client(sdev); +} + +void hda_unregister_audio_client(struct snd_sof_dev *sdev) +{ + const struct snd_soc_acpi_mach *mach = sdev->pdata->machine; + + if (multi_card) { + if (mach && !sdev->pdata->disable_function_topology && + mach->get_function_tplg_files) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + int i; + + for (i = hdev->num_audio_clients - 1; i >= 0; i--) + sof_client_dev_unregister(sdev, "audio", i); + hdev->num_audio_clients = 0; + return; + } + } + + if (split_hdmi && mach && HDA_IDISP_CODEC(mach->mach_params.codec_mask)) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + int i; + + /* + * split_hdmi may fall back to single-card registration. In that case, + * use the legacy single-client unregister path. + */ + if (hdev->num_audio_clients <= 1) + goto single_client_unregister; + + for (i = hdev->num_audio_clients - 1; i >= 0; i--) + sof_client_dev_unregister(sdev, "audio", i); + hdev->num_audio_clients = 0; + return; + } + +single_client_unregister: + sof_unregister_audio_client(sdev); +} + int hda_register_clients(struct snd_sof_dev *sdev) { return hda_probes_register(sdev); @@ -1769,6 +2124,7 @@ MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV"); MODULE_IMPORT_NS("SND_SOC_SOF_HDA_AUDIO_CODEC"); MODULE_IMPORT_NS("SND_SOC_SOF_HDA_AUDIO_CODEC_I915"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_INTEL_SOUNDWIRE_ACPI"); MODULE_IMPORT_NS("SOUNDWIRE_INTEL_INIT"); MODULE_IMPORT_NS("SOUNDWIRE_INTEL"); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 8a7c9a10e51c04..5e7d0505a534ee 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -580,6 +580,9 @@ struct sof_intel_hda_dev { * is received from the DSP for the previous message) */ struct snd_sof_ipc_msg *delayed_ipc_tx_msg; + + /* number of audio client devices registered in multi-card mode */ + int num_audio_clients; }; static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s) @@ -667,21 +670,40 @@ u32 hda_get_interface_mask(struct snd_sof_dev *sdev); */ u32 hda_dsp_get_mult_div(struct snd_sof_dev *sdev, int rate); u32 hda_dsp_get_bits(struct snd_sof_dev *sdev, int sample_bits); -int hda_dsp_pcm_open(struct snd_sof_dev *sdev, +int hda_dsp_pcm_open(struct snd_soc_component *component, struct snd_pcm_substream *substream); -int hda_dsp_pcm_close(struct snd_sof_dev *sdev, +int hda_dsp_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream); -int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, +int hda_dsp_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params); -int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, +int hda_dsp_stream_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream); -int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, +int hda_dsp_pcm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd); -snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, +snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream); -int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); +int hda_dsp_pcm_ack(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + +int hda_dsp_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *cstream); +int hda_dsp_compr_close(struct snd_soc_component *component, + struct snd_compr_stream *cstream); +int hda_dsp_compr_hw_params(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_params *params, + struct snd_sof_platform_stream_params *platform_params); +int hda_dsp_stream_compr_hw_free(struct snd_soc_component *component, + struct snd_compr_stream *cstream); +int hda_dsp_compr_trigger(struct snd_soc_component *component, + struct snd_compr_stream *cstream, int cmd); +int hda_dsp_compr_pointer(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp64 *tstamp); +u64 hda_dsp_compr_get_stream_llp(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream); /* * DSP Stream Operations. @@ -943,6 +965,7 @@ int hda_dsp_dais_suspend(struct snd_sof_dev *sdev); /* * Platform Specific HW abstraction Ops. */ +extern const struct sof_audio_ops sof_hda_audio_ops; extern const struct snd_sof_dsp_ops sof_hda_common_ops; extern struct snd_sof_dsp_ops sof_skl_ops; @@ -992,6 +1015,10 @@ static inline void hda_probes_unregister(struct snd_sof_dev *sdev) int hda_register_clients(struct snd_sof_dev *sdev); void hda_unregister_clients(struct snd_sof_dev *sdev); +/* SOF audio client registration for HDA platforms (multi-card) */ +int hda_register_audio_client(struct snd_sof_dev *sdev); +void hda_unregister_audio_client(struct snd_sof_dev *sdev); + /* machine driver select */ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev); void hda_set_mach_params(struct snd_soc_acpi_mach *mach, @@ -1009,7 +1036,7 @@ struct sof_ipc_dai_config; extern int sof_hda_position_quirk; -void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops); +void hda_set_dai_drv_ops(struct snd_sof_dev *sdev); void hda_ops_free(struct snd_sof_dev *sdev); /* SKL/KBL */ @@ -1024,6 +1051,8 @@ bool hda_ipc4_tx_is_busy(struct snd_sof_dev *sdev); void hda_dsp_ipc4_schedule_d0i3_work(struct sof_intel_hda_dev *hdev, struct snd_sof_ipc_msg *msg); int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); +void hda_dsp_ipc4_msg_timeout_handler(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg); void hda_ipc4_dump(struct snd_sof_dev *sdev); extern struct sdw_intel_ops sdw_callback; @@ -1086,7 +1115,7 @@ static inline struct snd_sof_dev *widget_to_sdev(struct snd_soc_dapm_widget *w) struct snd_sof_widget *swidget = w->dobj.private; struct snd_soc_component *component = swidget->scomp; - return snd_soc_component_get_drvdata(component); + return snd_sof_component_get_sdev(component); } #endif diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index c1018893750c4b..de5d8865b89bb5 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -139,6 +139,7 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev) /* ipc */ sof_icl_ops.send_msg = cnl_ipc4_send_msg; + sof_icl_ops.msg_timeout_handler = hda_dsp_ipc4_msg_timeout_handler; /* debug */ sof_icl_ops.ipc_dump = cnl_ipc4_dump; @@ -161,7 +162,7 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev) sof_icl_ops.core_get = hda_dsp_core_get; /* set DAI driver ops */ - hda_set_dai_drv_ops(sdev, &sof_icl_ops); + hda_set_dai_drv_ops(sdev); return 0; }; diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 68cf68135d054a..2d362ac5f3cf1c 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -236,6 +236,17 @@ int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) } EXPORT_SYMBOL_NS(mtl_enable_interrupts, "SND_SOC_SOF_INTEL_MTL"); +static bool mtl_dsp_is_enabled(struct snd_sof_dev *sdev) +{ + int val; + + val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFDSSCS); + if (val & MTL_HFDSSCS_CPA_MASK) + return true; + + return false; +} + /* pre fw run operations */ static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) { @@ -249,6 +260,18 @@ static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) u32 dsppwrsts; const struct sof_intel_dsp_desc *chip; + /* Power down the DSP if it is left enabled to ensure clean boot state */ + if (mtl_dsp_is_enabled(sdev)) { + dev_dbg(sdev->dev, "powering down DSP first\n"); + + ret = mtl_power_down_dsp(sdev); + if (ret < 0) { + dev_warn(sdev->dev, + "%s: failed to power down already-enabled DSP\n", __func__); + /* Continue anyway to attempt recovery */ + } + } + chip = get_chip_info(sdev->pdata); if (chip->hw_ip_version > SOF_INTEL_ACE_2_0) { dsppwrctl = PTL_HFPWRCTL2; @@ -717,6 +740,7 @@ int sof_mtl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops) /* ipc */ dsp_ops->send_msg = mtl_ipc_send_msg; + dsp_ops->msg_timeout_handler = hda_dsp_ipc4_msg_timeout_handler; dsp_ops->get_mailbox_offset = mtl_dsp_ipc_get_mailbox_offset; dsp_ops->get_window_offset = mtl_dsp_ipc_get_window_offset; @@ -754,7 +778,7 @@ int sof_mtl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops) dsp_ops->set_power_state = hda_dsp_set_power_state_ipc4; /* set DAI ops */ - hda_set_dai_drv_ops(sdev, dsp_ops); + hda_set_dai_drv_ops(sdev); return 0; } diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c index 3241403efa60a0..69ed5492a8d5f4 100644 --- a/sound/soc/sof/intel/pci-apl.c +++ b/sound/soc/sof/intel/pci-apl.c @@ -47,6 +47,7 @@ static const struct sof_dev_desc bxt_desc = { }, .nocodec_tplg_filename = "sof-apl-nocodec.tplg", .ops = &sof_apl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_apl_ops_init, .ops_free = hda_ops_free, }; @@ -79,6 +80,7 @@ static const struct sof_dev_desc glk_desc = { }, .nocodec_tplg_filename = "sof-glk-nocodec.tplg", .ops = &sof_apl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_apl_ops_init, .ops_free = hda_ops_free, }; diff --git a/sound/soc/sof/intel/pci-cnl.c b/sound/soc/sof/intel/pci-cnl.c index de48640024e493..ccd7ea7b5a822c 100644 --- a/sound/soc/sof/intel/pci-cnl.c +++ b/sound/soc/sof/intel/pci-cnl.c @@ -48,6 +48,7 @@ static const struct sof_dev_desc cnl_desc = { }, .nocodec_tplg_filename = "sof-cnl-nocodec.tplg", .ops = &sof_cnl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_cnl_ops_init, .ops_free = hda_ops_free, }; @@ -81,6 +82,7 @@ static const struct sof_dev_desc cfl_desc = { }, .nocodec_tplg_filename = "sof-cnl-nocodec.tplg", .ops = &sof_cnl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_cnl_ops_init, .ops_free = hda_ops_free, }; @@ -114,6 +116,7 @@ static const struct sof_dev_desc cml_desc = { }, .nocodec_tplg_filename = "sof-cnl-nocodec.tplg", .ops = &sof_cnl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_cnl_ops_init, .ops_free = hda_ops_free, }; diff --git a/sound/soc/sof/intel/pci-icl.c b/sound/soc/sof/intel/pci-icl.c index fd219e6548445c..87b059997edadf 100644 --- a/sound/soc/sof/intel/pci-icl.c +++ b/sound/soc/sof/intel/pci-icl.c @@ -48,6 +48,7 @@ static const struct sof_dev_desc icl_desc = { }, .nocodec_tplg_filename = "sof-icl-nocodec.tplg", .ops = &sof_icl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_icl_ops_init, .ops_free = hda_ops_free, }; @@ -80,6 +81,7 @@ static const struct sof_dev_desc jsl_desc = { }, .nocodec_tplg_filename = "sof-jsl-nocodec.tplg", .ops = &sof_cnl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_cnl_ops_init, .ops_free = hda_ops_free, }; diff --git a/sound/soc/sof/intel/pci-lnl.c b/sound/soc/sof/intel/pci-lnl.c index acb4429df9ecf6..3694fccf505fa9 100644 --- a/sound/soc/sof/intel/pci-lnl.c +++ b/sound/soc/sof/intel/pci-lnl.c @@ -55,6 +55,7 @@ static const struct sof_dev_desc lnl_desc = { }, .nocodec_tplg_filename = "sof-lnl-nocodec.tplg", .ops = &sof_lnl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_lnl_ops_init, }; diff --git a/sound/soc/sof/intel/pci-mtl.c b/sound/soc/sof/intel/pci-mtl.c index 23adc5d765b475..874cf2e8267e09 100644 --- a/sound/soc/sof/intel/pci-mtl.c +++ b/sound/soc/sof/intel/pci-mtl.c @@ -54,6 +54,7 @@ static const struct sof_dev_desc mtl_desc = { }, .nocodec_tplg_filename = "sof-mtl-nocodec.tplg", .ops = &sof_mtl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_mtl_ops_init, .ops_free = hda_ops_free, }; @@ -84,6 +85,7 @@ static const struct sof_dev_desc arl_desc = { }, .nocodec_tplg_filename = "sof-arl-nocodec.tplg", .ops = &sof_mtl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_mtl_ops_init, .ops_free = hda_ops_free, }; @@ -114,6 +116,7 @@ static const struct sof_dev_desc arl_s_desc = { }, .nocodec_tplg_filename = "sof-arl-nocodec.tplg", .ops = &sof_mtl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_mtl_ops_init, .ops_free = hda_ops_free, }; diff --git a/sound/soc/sof/intel/pci-nvl.c b/sound/soc/sof/intel/pci-nvl.c index bb3c29ef547773..8f370b0729a7d0 100644 --- a/sound/soc/sof/intel/pci-nvl.c +++ b/sound/soc/sof/intel/pci-nvl.c @@ -53,6 +53,7 @@ static const struct sof_dev_desc nvl_desc = { }, .nocodec_tplg_filename = "sof-nvl-nocodec.tplg", .ops = &sof_nvl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_nvl_ops_init, }; @@ -83,6 +84,7 @@ static const struct sof_dev_desc nvl_s_desc = { }, .nocodec_tplg_filename = "sof-nvl-nocodec.tplg", .ops = &sof_nvl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_nvl_ops_init, }; diff --git a/sound/soc/sof/intel/pci-ptl.c b/sound/soc/sof/intel/pci-ptl.c index 9cb785ef763f63..61b1095503d6bb 100644 --- a/sound/soc/sof/intel/pci-ptl.c +++ b/sound/soc/sof/intel/pci-ptl.c @@ -53,6 +53,7 @@ static const struct sof_dev_desc ptl_desc = { }, .nocodec_tplg_filename = "sof-ptl-nocodec.tplg", .ops = &sof_ptl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_ptl_ops_init, }; @@ -83,6 +84,7 @@ static const struct sof_dev_desc wcl_desc = { }, .nocodec_tplg_filename = "sof-ptl-nocodec.tplg", .ops = &sof_ptl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_ptl_ops_init, }; diff --git a/sound/soc/sof/intel/pci-skl.c b/sound/soc/sof/intel/pci-skl.c index a16945dc35f7dc..6848f4170d4cb8 100644 --- a/sound/soc/sof/intel/pci-skl.c +++ b/sound/soc/sof/intel/pci-skl.c @@ -38,6 +38,7 @@ static struct sof_dev_desc skl_desc = { }, .nocodec_tplg_filename = "sof-skl-nocodec.tplg", .ops = &sof_skl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_skl_ops_init, .ops_free = hda_ops_free, }; @@ -63,6 +64,7 @@ static struct sof_dev_desc kbl_desc = { }, .nocodec_tplg_filename = "sof-kbl-nocodec.tplg", .ops = &sof_skl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_skl_ops_init, .ops_free = hda_ops_free, }; diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c index 437c4381982555..28a2b11582a9e2 100644 --- a/sound/soc/sof/intel/pci-tgl.c +++ b/sound/soc/sof/intel/pci-tgl.c @@ -48,6 +48,7 @@ static const struct sof_dev_desc tgl_desc = { }, .nocodec_tplg_filename = "sof-tgl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; @@ -81,6 +82,7 @@ static const struct sof_dev_desc tglh_desc = { }, .nocodec_tplg_filename = "sof-tgl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; @@ -113,6 +115,7 @@ static const struct sof_dev_desc ehl_desc = { }, .nocodec_tplg_filename = "sof-ehl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; @@ -146,6 +149,7 @@ static const struct sof_dev_desc adls_desc = { }, .nocodec_tplg_filename = "sof-adl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; @@ -179,6 +183,7 @@ static const struct sof_dev_desc adl_desc = { }, .nocodec_tplg_filename = "sof-adl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; @@ -212,6 +217,7 @@ static const struct sof_dev_desc adln_desc = { }, .nocodec_tplg_filename = "sof-adl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; @@ -245,6 +251,7 @@ static const struct sof_dev_desc rpls_desc = { }, .nocodec_tplg_filename = "sof-rpl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; @@ -278,6 +285,7 @@ static const struct sof_dev_desc rpl_desc = { }, .nocodec_tplg_filename = "sof-rpl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c index 0c11cc1fd820eb..59411585e68ef3 100644 --- a/sound/soc/sof/intel/pci-tng.c +++ b/sound/soc/sof/intel/pci-tng.c @@ -14,6 +14,7 @@ #include #include #include "../ops.h" +#include "../sof-client.h" #include "atom.h" #include "../sof-pci-dev.h" #include "../sof-audio.h" @@ -132,6 +133,20 @@ static int tangier_pci_probe(struct snd_sof_dev *sdev) return ret; } +static const struct sof_audio_ops sof_tng_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + + .drv = atom_dai, + .num_drv = 3, + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH, +}; + const struct snd_sof_dsp_ops sof_tng_ops = { /* device init */ .probe = tangier_pci_probe, @@ -168,30 +183,19 @@ const struct snd_sof_dsp_ops sof_tng_ops = { .machine_unregister = sof_machine_unregister, .set_mach_params = atom_set_mach_params, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* debug */ .debug_map = tng_debugfs, .debug_map_count = ARRAY_SIZE(tng_debugfs), .dbg_dump = atom_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_close = sof_stream_pcm_close, - /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, - /* DAI drivers */ - .drv = atom_dai, - .num_drv = 3, /* we have only 3 SSPs on byt*/ - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_BATCH, - .dsp_arch_ops = &sof_xtensa_arch_ops, }; @@ -221,6 +225,7 @@ static const struct sof_dev_desc tng_desc = { }, .nocodec_tplg_filename = "sof-byt.tplg", .ops = &sof_tng_ops, + .audio_ops = &sof_tng_audio_ops, }; /* PCI IDs */ @@ -245,6 +250,7 @@ module_pci_driver(snd_sof_pci_intel_tng_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF support for Tangier platforms"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV"); MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_ATOM_HIFI_EP"); diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index 90519ebd316810..aa5cbd431a767d 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -79,9 +79,10 @@ int sof_skl_ops_init(struct snd_sof_dev *sdev) /* ipc */ sof_skl_ops.send_msg = hda_dsp_ipc4_send_msg; + sof_skl_ops.msg_timeout_handler = hda_dsp_ipc4_msg_timeout_handler; /* set DAI driver ops */ - hda_set_dai_drv_ops(sdev, &sof_skl_ops); + hda_set_dai_drv_ops(sdev); /* debug */ sof_skl_ops.debug_map = skl_dsp_debugfs; diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 7936361e2e39f5..5ee5018dba5406 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -109,6 +109,7 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev) /* ipc */ sof_tgl_ops.send_msg = cnl_ipc4_send_msg; + sof_tgl_ops.msg_timeout_handler = hda_dsp_ipc4_msg_timeout_handler; /* debug */ sof_tgl_ops.ipc_dump = cnl_ipc4_dump; @@ -120,7 +121,7 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev) } /* set DAI driver ops */ - hda_set_dai_drv_ops(sdev, &sof_tgl_ops); + hda_set_dai_drv_ops(sdev); /* pre/post fw run */ sof_tgl_ops.post_fw_run = hda_dsp_post_fw_run; diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/ipc3-compress.c similarity index 64% rename from sound/soc/sof/compress.c rename to sound/soc/sof/ipc3-compress.c index 93f2376585dbf5..09716d45331a62 100644 --- a/sound/soc/sof/compress.c +++ b/sound/soc/sof/ipc3-compress.c @@ -12,88 +12,8 @@ #include "sof-utils.h" #include "ops.h" -static void sof_set_transferred_bytes(struct sof_compr_stream *sstream, - u64 host_pos, u64 buffer_size) -{ - u64 prev_pos; - unsigned int copied; - - div64_u64_rem(sstream->copied_total, buffer_size, &prev_pos); - - if (host_pos < prev_pos) - copied = (buffer_size - prev_pos) + host_pos; - else - copied = host_pos - prev_pos; - - sstream->copied_total += copied; -} - -static void snd_sof_compr_fragment_elapsed_work(struct work_struct *work) -{ - struct snd_sof_pcm_stream *sps = - container_of(work, struct snd_sof_pcm_stream, - period_elapsed_work); - - snd_compr_fragment_elapsed(sps->cstream); -} - -void snd_sof_compr_init_elapsed_work(struct work_struct *work) -{ - INIT_WORK(work, snd_sof_compr_fragment_elapsed_work); -} - -/* - * sof compr fragment elapse, this could be called in irq thread context - */ -void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream) -{ - struct snd_soc_pcm_runtime *rtd; - struct snd_compr_runtime *crtd; - struct snd_soc_component *component; - struct sof_compr_stream *sstream; - struct snd_sof_pcm *spcm; - - if (!cstream) - return; - - rtd = cstream->private_data; - crtd = cstream->runtime; - sstream = crtd->private_data; - component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); - - spcm = snd_sof_find_spcm_dai(component, rtd); - if (!spcm) { - dev_err(component->dev, - "fragment elapsed called for unknown stream!\n"); - return; - } - - sof_set_transferred_bytes(sstream, spcm->stream[cstream->direction].posn.host_posn, - crtd->buffer_size); - - /* use the same workqueue-based solution as for PCM, cf. snd_sof_pcm_elapsed */ - schedule_work(&spcm->stream[cstream->direction].period_elapsed_work); -} - -static int create_page_table(struct snd_soc_component *component, - struct snd_compr_stream *cstream, - unsigned char *dma_area, size_t size) -{ - struct snd_dma_buffer *dmab = cstream->runtime->dma_buffer_p; - struct snd_soc_pcm_runtime *rtd = cstream->private_data; - int dir = cstream->direction; - struct snd_sof_pcm *spcm; - - spcm = snd_sof_find_spcm_dai(component, rtd); - if (!spcm) - return -EINVAL; - - return snd_sof_create_page_table(component->dev, dmab, - spcm->stream[dir].page_table.area, size); -} - -static int sof_compr_open(struct snd_soc_component *component, - struct snd_compr_stream *cstream) +static int sof_ipc3_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_compr_runtime *crtd = cstream->runtime; @@ -128,10 +48,10 @@ static int sof_compr_open(struct snd_soc_component *component, return 0; } -static int sof_compr_free(struct snd_soc_component *component, - struct snd_compr_stream *cstream) +static int sof_ipc3_compr_free(struct snd_soc_component *component, + struct snd_compr_stream *cstream) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct sof_compr_stream *sstream = cstream->runtime->private_data; struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct sof_ipc_stream stream; @@ -159,10 +79,11 @@ static int sof_compr_free(struct snd_soc_component *component, return ret; } -static int sof_compr_set_params(struct snd_soc_component *component, - struct snd_compr_stream *cstream, struct snd_compr_params *params) +static int sof_ipc3_compr_set_params(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_params *params) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_compr_runtime *crtd = cstream->runtime; struct sof_ipc_pcm_params_reply ipc_params_reply; @@ -213,7 +134,7 @@ static int sof_compr_set_params(struct snd_soc_component *component, if (ret < 0) goto out; - ret = create_page_table(component, cstream, crtd->dma_area, crtd->dma_bytes); + ret = snd_sof_compr_create_page_table(component, cstream, crtd->dma_area, crtd->dma_bytes); if (ret < 0) goto out; @@ -265,8 +186,9 @@ static int sof_compr_set_params(struct snd_soc_component *component, return ret; } -static int sof_compr_get_params(struct snd_soc_component *component, - struct snd_compr_stream *cstream, struct snd_codec *params) +static int sof_ipc3_compr_get_params(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_codec *params) { struct sof_compr_stream *sstream = cstream->runtime->private_data; @@ -275,10 +197,10 @@ static int sof_compr_get_params(struct snd_soc_component *component, return 0; } -static int sof_compr_trigger(struct snd_soc_component *component, - struct snd_compr_stream *cstream, int cmd) +static int sof_ipc3_compr_trigger(struct snd_soc_component *component, + struct snd_compr_stream *cstream, int cmd) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct sof_ipc_stream stream; struct snd_sof_pcm *spcm; @@ -312,8 +234,8 @@ static int sof_compr_trigger(struct snd_soc_component *component, return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream)); } -static int sof_compr_copy_playback(struct snd_compr_runtime *rtd, - char __user *buf, size_t count) +static int sof_ipc3_compr_copy_playback(struct snd_compr_runtime *rtd, + char __user *buf, size_t count) { void *ptr; unsigned int offset, n; @@ -333,8 +255,8 @@ static int sof_compr_copy_playback(struct snd_compr_runtime *rtd, return count - ret; } -static int sof_compr_copy_capture(struct snd_compr_runtime *rtd, - char __user *buf, size_t count) +static int sof_ipc3_compr_copy_capture(struct snd_compr_runtime *rtd, + char __user *buf, size_t count) { void *ptr; unsigned int offset, n; @@ -354,9 +276,9 @@ static int sof_compr_copy_capture(struct snd_compr_runtime *rtd, return count - ret; } -static int sof_compr_copy(struct snd_soc_component *component, - struct snd_compr_stream *cstream, - char __user *buf, size_t count) +static int sof_ipc3_compr_copy(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + char __user *buf, size_t count) { struct snd_compr_runtime *rtd = cstream->runtime; @@ -364,14 +286,14 @@ static int sof_compr_copy(struct snd_soc_component *component, count = rtd->buffer_size; if (cstream->direction == SND_COMPRESS_PLAYBACK) - return sof_compr_copy_playback(rtd, buf, count); + return sof_ipc3_compr_copy_playback(rtd, buf, count); else - return sof_compr_copy_capture(rtd, buf, count); + return sof_ipc3_compr_copy_capture(rtd, buf, count); } -static int sof_compr_pointer(struct snd_soc_component *component, - struct snd_compr_stream *cstream, - struct snd_compr_tstamp64 *tstamp) +static int sof_ipc3_compr_pointer(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp64 *tstamp) { struct snd_sof_pcm *spcm; struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -392,13 +314,12 @@ static int sof_compr_pointer(struct snd_soc_component *component, return 0; } -struct snd_compress_ops sof_compressed_ops = { - .open = sof_compr_open, - .free = sof_compr_free, - .set_params = sof_compr_set_params, - .get_params = sof_compr_get_params, - .trigger = sof_compr_trigger, - .pointer = sof_compr_pointer, - .copy = sof_compr_copy, +const struct snd_compress_ops sof_ipc3_compressed_ops = { + .open = sof_ipc3_compr_open, + .free = sof_ipc3_compr_free, + .set_params = sof_ipc3_compr_set_params, + .get_params = sof_ipc3_compr_get_params, + .trigger = sof_ipc3_compr_trigger, + .pointer = sof_ipc3_compr_pointer, + .copy = sof_ipc3_compr_copy, }; -EXPORT_SYMBOL(sof_compressed_ops); diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index d1697401b1da0d..95da9be0188a03 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -15,7 +15,9 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set, bool lock) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp); + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(scontrol->scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scontrol->scomp); struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; const struct sof_ipc_ops *iops = sdev->ipc->ops; enum sof_ipc_ctrl_type ctrl_type; @@ -24,7 +26,7 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, u32 ipc_cmd, msg_bytes; int ret = 0; - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (swidget->comp_id == scontrol->comp_id) { widget_found = true; break; @@ -32,7 +34,7 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, } if (!widget_found) { - dev_err(sdev->dev, "%s: can't find widget with id %d\n", __func__, + dev_err(scontrol->scomp->dev, "%s: can't find widget with id %d\n", __func__, scontrol->comp_id); return -EINVAL; } @@ -449,6 +451,14 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, goto err_restore; } + /* Verify user provided enough data for what the ABI header claims */ + if (cdata->data->size > header.length - sizeof(struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, "ABI data size %u exceeds TLV length %u\n", + cdata->data->size, + header.length - (unsigned int)sizeof(struct sof_abi_hdr)); + goto err_restore; + } + /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) { /* Actually send the data to the DSP; this is an opportunity to validate the data */ @@ -579,6 +589,7 @@ static void snd_sof_update_control(struct snd_sof_control *scontrol, static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_message) { struct sof_ipc_ctrl_data *cdata = ipc_control_message; + struct snd_sof_audio_instance *instance; struct snd_soc_dapm_widget *widget; struct snd_sof_control *scontrol; struct snd_sof_widget *swidget; @@ -593,11 +604,11 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET || cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) { dev_err(sdev->dev, "Component data is not supported in control notification\n"); - return; + return; /* swidget not yet found, sdev->dev is correct here */ } /* Find the swidget first */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + for_each_swidget_in_instances(swidget, sdev, instance) { if (swidget->comp_id == cdata->comp_id) { found = true; break; @@ -620,7 +631,7 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ type = SND_SOC_TPLG_TYPE_ENUM; break; default: - dev_err(sdev->dev, "Unknown cmd %u in %s\n", cdata->cmd, __func__); + dev_err(swidget->scomp->dev, "Unknown cmd %u in %s\n", cdata->cmd, __func__); return; } @@ -683,7 +694,7 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ } if (cdata->rhdr.hdr.size != expected_size) { - dev_err(sdev->dev, "Component notification size mismatch\n"); + dev_err(swidget->scomp->dev, "Component notification size mismatch\n"); return; } @@ -703,16 +714,18 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(swidget->scomp); struct snd_sof_control *scontrol; int ret; /* set up all controls for the widget */ - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) + list_for_each_entry(scontrol, &instance->kcontrol_list, list) if (scontrol->comp_id == swidget->comp_id) { /* set kcontrol data in DSP */ ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, false); if (ret < 0) { - dev_err(sdev->dev, + dev_err(swidget->scomp->dev, "kcontrol %d set up failed for widget %s\n", scontrol->comp_id, swidget->widget->name); return ret; @@ -729,7 +742,7 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev, ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, false); if (ret < 0) - dev_warn(sdev->dev, + dev_warn(swidget->scomp->dev, "kcontrol %d read failed for widget %s\n", scontrol->comp_id, swidget->widget->name); } diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 90ef5d99f626a9..bf0c94fa6512ed 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -14,23 +14,18 @@ #include "sof-audio.h" static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) + struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, int dir) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct sof_ipc_stream stream; - struct snd_sof_pcm *spcm; - - spcm = snd_sof_find_spcm_dai(component, rtd); - if (!spcm) - return -EINVAL; - if (!spcm->prepared[substream->stream]) + if (!spcm->prepared[dir]) return 0; stream.hdr.size = sizeof(stream); stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; - stream.comp_id = spcm->stream[substream->stream].comp_id; + stream.comp_id = spcm->stream[dir].comp_id; /* send IPC to the DSP */ return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream)); @@ -41,7 +36,7 @@ static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_pcm_runtime *runtime = substream->runtime; @@ -141,20 +136,15 @@ static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component, } static int sof_ipc3_pcm_trigger(struct snd_soc_component *component, - struct snd_pcm_substream *substream, int cmd) + struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, int cmd, int dir) { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct sof_ipc_stream stream; - struct snd_sof_pcm *spcm; - - spcm = snd_sof_find_spcm_dai(component, rtd); - if (!spcm) - return -EINVAL; stream.hdr.size = sizeof(stream); stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG; - stream.comp_id = spcm->stream[substream->stream].comp_id; + stream.comp_id = spcm->stream[dir].comp_id; switch (cmd) { case SNDRV_PCM_TRIGGER_PAUSE_PUSH: @@ -172,7 +162,7 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component, stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; break; default: - spcm_err(spcm, substream->stream, "Unhandled trigger cmd %d\n", cmd); + spcm_err(spcm, dir, "Unhandled trigger cmd %d\n", cmd); return -EINVAL; } @@ -180,9 +170,12 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component, return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream)); } -static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name, +static void ssp_dai_config_pcm_params_match(struct snd_soc_component *component, + const char *link_name, struct snd_pcm_hw_params *params) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct sof_ipc_dai_config *config; struct snd_sof_dai *dai; int i; @@ -191,7 +184,7 @@ static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char * Search for all matching DAIs as we can have both playback and capture DAI * associated with the same link. */ - list_for_each_entry(dai, &sdev->dai_list, list) { + list_for_each_entry(dai, &instance->dai_list, list) { if (!dai->name || strcmp(link_name, dai->name)) continue; for (i = 0; i < dai->number_configs; i++) { @@ -215,7 +208,6 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct sof_dai_private_data *private; struct snd_soc_dpcm *dpcm; @@ -254,7 +246,7 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, switch (private->dai_config->type) { case SOF_DAI_INTEL_SSP: /* search for config to pcm params match, if not found use default */ - ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params); + ssp_dai_config_pcm_params_match(component, (char *)rtd->dai_link->name, params); rate->min = private->dai_config[dai->current_config].ssp.fsync_rate; rate->max = private->dai_config[dai->current_config].ssp.fsync_rate; @@ -436,4 +428,7 @@ const struct sof_ipc_pcm_ops ipc3_pcm_ops = { .dai_link_fixup = sof_ipc3_pcm_dai_link_fixup, .reset_hw_params_during_stop = true, .d0i3_supported_in_s0ix = true, +#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) + .compress_ops = &sof_ipc3_compressed_ops, +#endif }; diff --git a/sound/soc/sof/ipc3-priv.h b/sound/soc/sof/ipc3-priv.h index 866c5f67b91a7c..f95957453ab8b2 100644 --- a/sound/soc/sof/ipc3-priv.h +++ b/sound/soc/sof/ipc3-priv.h @@ -17,6 +17,9 @@ extern const struct sof_ipc_tplg_ops ipc3_tplg_ops; extern const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops; extern const struct sof_ipc_fw_loader_ops ipc3_loader_ops; extern const struct sof_ipc_fw_tracing_ops ipc3_dtrace_ops; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) +extern const struct snd_compress_ops sof_ipc3_compressed_ops; +#endif /* helpers for fw_ready and ext_manifest parsing */ int sof_ipc3_get_ext_windows(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 4e066bbded9179..c58d324007230f 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -519,7 +519,7 @@ static int sof_ipc3_widget_setup_comp_mixer(struct snd_sof_widget *swidget) static int sof_ipc3_widget_setup_comp_pipeline(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_pipeline *spipe = swidget->spipe; struct sof_ipc_pipe_new *pipeline; struct snd_sof_widget *comp_swidget; @@ -752,7 +752,7 @@ static int sof_ipc3_widget_setup_comp_mux(struct snd_sof_widget *swidget) static int sof_ipc3_widget_setup_comp_pga(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct sof_ipc_comp_volume *volume; struct snd_sof_control *scontrol; size_t ipc_size = sizeof(*volume); @@ -785,7 +785,7 @@ static int sof_ipc3_widget_setup_comp_pga(struct snd_sof_widget *swidget) dev_dbg(scomp->dev, "loaded PGA %s\n", swidget->widget->name); sof_dbg_comp_config(scomp, &volume->config); - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + list_for_each_entry(scontrol, &instance->kcontrol_list, list) { if (scontrol->comp_id == swidget->comp_id && scontrol->volume_table) { min_step = scontrol->min_volume_step; @@ -1395,7 +1395,7 @@ static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai static int sof_link_ssp_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; struct sof_dai_private_data *private = dai->private; u32 size = sizeof(*config); @@ -1468,7 +1468,7 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, struct snd_sof_dai static int sof_link_dmic_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_dai_private_data *private = dai->private; struct sof_ipc_fw_ready *ready = &sdev->fw_ready; struct sof_ipc_fw_version *v = &ready->version; @@ -1566,7 +1566,7 @@ static int sof_link_alh_load(struct snd_soc_component *scomp, struct snd_sof_dai static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_dai *dai = swidget->private; struct sof_dai_private_data *private; struct sof_ipc_comp_dai *comp_dai; @@ -1608,7 +1608,7 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) /* Subtract the base to match the FW dai index. */ if (comp_dai->type == SOF_DAI_INTEL_ALH) { if (comp_dai->dai_index < INTEL_ALH_DAI_INDEX_BASE) { - dev_err(sdev->dev, + dev_err(scomp->dev, "Invalid ALH dai index %d, only Pin numbers >= %d can be used\n", comp_dai->dai_index, INTEL_ALH_DAI_INDEX_BASE); ret = -EINVAL; @@ -1622,7 +1622,7 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) sof_dbg_comp_config(scomp, &comp_dai->config); /* now update DAI config */ - list_for_each_entry(slink, &sdev->dai_link_list, list) { + list_for_each_entry(slink, &instance->dai_link_list, list) { struct sof_ipc_dai_config common_config; int i; @@ -1742,6 +1742,7 @@ static void sof_ipc3_widget_free_comp_dai(struct snd_sof_widget *swidget) static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) { + struct snd_soc_component *scomp = sroute->scomp; struct sof_ipc_pipe_comp_connect connect; int ret; @@ -1750,33 +1751,34 @@ static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * connect.source_id = sroute->src_widget->comp_id; connect.sink_id = sroute->sink_widget->comp_id; - dev_dbg(sdev->dev, "setting up route %s -> %s\n", + dev_dbg(scomp->dev, "setting up route %s -> %s\n", sroute->src_widget->widget->name, sroute->sink_widget->widget->name); /* send ipc */ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &connect, sizeof(connect)); if (ret < 0) - dev_err(sdev->dev, "%s: route %s -> %s failed\n", __func__, + dev_err(scomp->dev, "%s: route %s -> %s failed\n", __func__, sroute->src_widget->widget->name, sroute->sink_widget->widget->name); return ret; } -static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +static int sof_ipc3_control_load_bytes(struct snd_sof_control *scontrol) { + struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata; size_t priv_size_check; int ret; if (scontrol->max_size < (sizeof(*cdata) + sizeof(struct sof_abi_hdr))) { - dev_err(sdev->dev, "%s: insufficient size for a bytes control: %zu.\n", + dev_err(scomp->dev, "%s: insufficient size for a bytes control: %zu.\n", __func__, scontrol->max_size); return -EINVAL; } if (scontrol->priv_size > scontrol->max_size - sizeof(*cdata)) { - dev_err(sdev->dev, + dev_err(scomp->dev, "%s: bytes data size %zu exceeds max %zu.\n", __func__, scontrol->priv_size, scontrol->max_size - sizeof(*cdata)); return -EINVAL; @@ -1798,13 +1800,13 @@ static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ scontrol->priv = NULL; if (cdata->data->magic != SOF_ABI_MAGIC) { - dev_err(sdev->dev, "Wrong ABI magic 0x%08x.\n", cdata->data->magic); + dev_err(scomp->dev, "Wrong ABI magic 0x%08x.\n", cdata->data->magic); ret = -EINVAL; goto err; } if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { - dev_err(sdev->dev, "Incompatible ABI version 0x%08x.\n", + dev_err(scomp->dev, "Incompatible ABI version 0x%08x.\n", cdata->data->abi); ret = -EINVAL; goto err; @@ -1812,7 +1814,7 @@ static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ priv_size_check = cdata->data->size + sizeof(struct sof_abi_hdr); if (priv_size_check != scontrol->priv_size) { - dev_err(sdev->dev, "Conflict in bytes (%zu) vs. priv size (%zu).\n", + dev_err(scomp->dev, "Conflict in bytes (%zu) vs. priv size (%zu).\n", priv_size_check, scontrol->priv_size); ret = -EINVAL; goto err; @@ -1884,7 +1886,7 @@ static int sof_ipc3_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr case SND_SOC_TPLG_CTL_VOLSW_XR_SX: return sof_ipc3_control_load_volume(sdev, scontrol); case SND_SOC_TPLG_CTL_BYTES: - return sof_ipc3_control_load_bytes(sdev, scontrol); + return sof_ipc3_control_load_bytes(scontrol); case SND_SOC_TPLG_CTL_ENUM: case SND_SOC_TPLG_CTL_ENUM_VALUE: return sof_ipc3_control_load_enum(sdev, scontrol); @@ -1911,7 +1913,7 @@ static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_contro static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, int dir) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_pcm_hw_params *params; struct sof_ipc_pcm_params pcm; struct snd_sof_pcm *spcm; @@ -1967,7 +1969,7 @@ static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, in static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int cmd) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc_stream stream; int ret; @@ -2093,10 +2095,11 @@ static int sof_ipc3_widget_bind_event(struct snd_soc_component *scomp, static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { + struct snd_soc_component *scomp = swidget->scomp; struct sof_ipc_pipe_ready ready; int ret; - dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n", + dev_dbg(scomp->dev, "tplg: complete pipeline %s id %d\n", swidget->widget->name, swidget->comp_id); memset(&ready, 0, sizeof(ready)); @@ -2141,7 +2144,8 @@ static int sof_ipc3_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget ret = sof_ipc_tx_message_no_reply(sdev->ipc, &ipc_free, sizeof(ipc_free)); if (ret < 0) - dev_err(sdev->dev, "failed to free widget %s\n", swidget->widget->name); + dev_err(swidget->scomp->dev, "failed to free widget %s\n", + swidget->widget->name); return ret; } @@ -2156,19 +2160,21 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * int ret = 0; if (!dai || !dai->private) { - dev_err(sdev->dev, "No private data for DAI %s\n", swidget->widget->name); + dev_err(swidget->scomp->dev, "No private data for DAI %s\n", + swidget->widget->name); return -EINVAL; } private = dai->private; if (!private->dai_config) { - dev_err(sdev->dev, "No config for DAI %s\n", dai->name); + dev_err(swidget->scomp->dev, "No config for DAI %s\n", dai->name); return -EINVAL; } config = &private->dai_config[dai->current_config]; if (!config) { - dev_err(sdev->dev, "Invalid current config for DAI %s\n", dai->name); + dev_err(swidget->scomp->dev, "Invalid current config for DAI %s\n", + dai->name); return -EINVAL; } @@ -2193,7 +2199,7 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) { /* Subtract the base to match the FW dai index. */ if (data->dai_index < INTEL_ALH_DAI_INDEX_BASE) { - dev_err(sdev->dev, + dev_err(swidget->scomp->dev, "Invalid ALH dai index %d, only Pin numbers >= %d can be used\n", config->dai_index, INTEL_ALH_DAI_INDEX_BASE); return -EINVAL; @@ -2239,7 +2245,8 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * if (swidget->use_count > 0) { ret = sof_ipc_tx_message_no_reply(sdev->ipc, config, config->hdr.size); if (ret < 0) - dev_err(sdev->dev, "Failed to set dai config for %s\n", dai->name); + dev_err(swidget->scomp->dev, "Failed to set dai config for %s\n", + dai->name); /* clear the flags once the IPC has been sent even if it fails */ config->flags = SOF_DAI_CONFIG_FLAGS_NONE; @@ -2284,7 +2291,8 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget } } if (ret < 0) - dev_err(sdev->dev, "Failed to setup widget %s\n", swidget->widget->name); + dev_err(swidget->scomp->dev, "Failed to setup widget %s\n", + swidget->widget->name); return ret; } @@ -2292,12 +2300,13 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) { struct sof_ipc_fw_version *v = &sdev->fw_ready.version; + struct snd_sof_audio_instance *instance; struct snd_sof_widget *swidget; struct snd_sof_route *sroute; int ret; /* restore pipeline components */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + for_each_swidget_in_instances(swidget, sdev, instance) { /* only set up the widgets belonging to static pipelines */ if (!verify && swidget->dynamic_pipeline_widget) continue; @@ -2339,7 +2348,7 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) } /* restore pipeline connections */ - list_for_each_entry(sroute, &sdev->route_list, list) { + for_each_sroute_in_instances(sroute, sdev, instance) { /* only set up routes belonging to static pipelines */ if (!verify && (sroute->src_widget->dynamic_pipeline_widget || sroute->sink_widget->dynamic_pipeline_widget)) @@ -2356,13 +2365,13 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) ret = sof_route_setup(sdev, sroute->src_widget->widget, sroute->sink_widget->widget); if (ret < 0) { - dev_err(sdev->dev, "%s: route set up failed\n", __func__); + dev_err(sroute->scomp->dev, "%s: route set up failed\n", __func__); return ret; } } /* complete pipeline */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + for_each_swidget_in_instances(swidget, sdev, instance) { switch (swidget->id) { case snd_soc_dapm_scheduler: /* only complete static pipelines */ @@ -2393,6 +2402,7 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) */ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) { + struct snd_sof_audio_instance *instance; struct snd_sof_widget *swidget; int ret; @@ -2409,7 +2419,7 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) * free any left over DAI widgets. This is equivalent to the handling of suspend trigger * for the BE DAI for running streams. */ - list_for_each_entry(swidget, &sdev->widget_list, list) + for_each_swidget_in_instances(swidget, sdev, instance) if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) { ret = sof_widget_free(sdev, swidget); if (ret < 0) @@ -2422,11 +2432,12 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) static int sof_ipc3_free_widgets_in_list(struct snd_sof_dev *sdev, bool include_scheduler, bool *dyn_widgets, bool verify) { + struct snd_sof_audio_instance *instance; struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_sof_widget *swidget; int ret; - list_for_each_entry(swidget, &sdev->widget_list, list) { + for_each_swidget_in_instances(swidget, sdev, instance) { if (swidget->dynamic_pipeline_widget) { *dyn_widgets = true; continue; @@ -2464,6 +2475,7 @@ static int sof_ipc3_free_widgets_in_list(struct snd_sof_dev *sdev, bool include_ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify) { struct sof_ipc_fw_version *v = &sdev->fw_ready.version; + struct snd_sof_audio_instance *instance; struct snd_sof_widget *swidget; struct snd_sof_route *sroute; bool dyn_widgets = false; @@ -2501,7 +2513,7 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif if (ret < 0) return ret; - list_for_each_entry(sroute, &sdev->route_list, list) + for_each_sroute_in_instances(sroute, sdev, instance) sroute->setup = false; /* @@ -2509,9 +2521,10 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif * to recover at this point but this will help root cause bad sequences leading to * more issues on resume */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + for_each_swidget_in_instances(swidget, sdev, instance) { if (swidget->use_count != 0) { - dev_err(sdev->dev, "%s: widget %s is still in use: count %d\n", + dev_err(swidget->scomp->dev, + "%s: widget %s is still in use: count %d\n", __func__, swidget->widget->name, swidget->use_count); } } diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index 85bb22bbe18d08..4a350d9d91226b 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -861,13 +861,12 @@ static int ipc3_fw_ready(struct snd_sof_dev *sdev, u32 cmd) /* IPC stream position. */ static void ipc3_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) { - struct snd_soc_component *scomp = sdev->component; struct snd_sof_pcm_stream *stream; struct sof_ipc_stream_posn posn; struct snd_sof_pcm *spcm; int direction, ret; - spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); + spcm = snd_sof_find_spcm_comp_by_sdev(sdev, msg_id, &direction); if (!spcm) { dev_err(sdev->dev, "period elapsed for unknown stream, msg_id %d\n", msg_id); @@ -896,13 +895,12 @@ static void ipc3_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) /* DSP notifies host of an XRUN within FW */ static void ipc3_xrun(struct snd_sof_dev *sdev, u32 msg_id) { - struct snd_soc_component *scomp = sdev->component; struct snd_sof_pcm_stream *stream; struct sof_ipc_stream_posn posn; struct snd_sof_pcm *spcm; int direction, ret; - spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); + spcm = snd_sof_find_spcm_comp_by_sdev(sdev, msg_id, &direction); if (!spcm) { dev_err(sdev->dev, "XRUN for unknown stream, msg_id %d\n", msg_id); diff --git a/sound/soc/sof/ipc4-compress.c b/sound/soc/sof/ipc4-compress.c new file mode 100644 index 00000000000000..9b00c969c6a964 --- /dev/null +++ b/sound/soc/sof/ipc4-compress.c @@ -0,0 +1,768 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright 2026 Intel Corporation. All rights reserved. +// +#include +#include +#include +#include +#include "sof-audio.h" +#include "sof-priv.h" +#include "sof-utils.h" +#include "ops.h" +#include "ipc4-priv.h" +#include "ipc4-topology.h" +#include "ipc4-fw-reg.h" + +/* Maximum processing size of the decoder/encoder is 2048 bytes */ +#define SOF_IPC4_COMPR_MAX_PROCESSING_SIZE (SZ_2K) + +#define SOF_IPC4_COMPR_MIN_FRAGMENTS 3 +#define SOF_IPC4_COMPR_MAX_FRAGMENT_SIZE (SZ_128K) +#define SOF_IPC4_COMPR_MAX_FRAGMENTS 64 +#define SOF_IPC4_COMPR_MIN_BUFFER_SIZE(min_size) ((min_size) * \ + SOF_IPC4_COMPR_MIN_FRAGMENTS) + +struct sof_ipc4_compr_init_data { + struct snd_codec codec; + u32 dir; +} __packed __aligned(4); + +static struct sof_ipc4_process * +sof_ipc4_compr_get_module(struct snd_sof_pcm *spcm, int dir) +{ + int id = dir ? snd_soc_dapm_encoder : snd_soc_dapm_decoder; + struct snd_sof_pcm_stream *sps = &spcm->stream[dir]; + struct snd_soc_dapm_widget *widget; + int i; + + /* Find the (first) compr module in path */ + for_each_dapm_widgets(sps->list, i, widget) { + struct snd_sof_widget *swidget = widget->dobj.private; + + if (!swidget) + continue; + + if (swidget->widget->id == id) + return swidget->private; + } + + return NULL; +} + +static u32 sof_ipc4_compr_calc_min_fragment_size(struct snd_sof_pcm_stream *sps) +{ + u32 host_buffer_estimate; + + /* Estimated host DMA buffer size based on stereo S32_LE, 48KHz */ + host_buffer_estimate = snd_pcm_format_size(SNDRV_PCM_FORMAT_S32_LE, 2 * 48); + host_buffer_estimate *= sps->dsp_max_burst_size_in_ms; + /* + * The minimum fragment size must not be smaller than the processing size + * or in case of deep buffer on host side, the host DMA buffer size. + */ + return max(SOF_IPC4_COMPR_MAX_PROCESSING_SIZE, host_buffer_estimate); +} + +static int sof_ipc4_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_sof_pcm *spcm; + int dir, ret; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return -EINVAL; + + dir = cstream->direction; + + if (spcm->stream[dir].cstream) + return -EBUSY; + + spcm_dbg(spcm, dir, "Entry: open\n"); + + ret = snd_sof_compr_platform_open(component, cstream); + if (ret < 0) { + spcm_err(spcm, dir, "platform compress open failed %d\n", ret); + return ret; + } + + spcm->stream[dir].cstream = cstream; + spcm->stream[dir].posn.host_posn = 0; + spcm->stream[dir].posn.dai_posn = 0; + spcm->prepared[dir] = false; + spcm->pending_stop[dir] = false; + + return 0; +} + +static int sof_ipc4_compr_stream_free(struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm, + struct snd_compr_stream *cstream) +{ + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + int dir = cstream->direction; + int ret = 0; + int err = 0; + + if (spcm->prepared[dir]) { + if (spcm->pending_stop[dir]) + pcm_ops->trigger(spcm->scomp, NULL, spcm, + SNDRV_PCM_TRIGGER_STOP, dir); + + snd_sof_compr_platform_trigger(spcm->scomp, cstream, + SNDRV_PCM_TRIGGER_STOP); + + err = pcm_ops->hw_free(spcm->scomp, NULL, spcm, dir); + if (err < 0) + spcm_err(spcm, dir, "pcm_ops->hw_free failed %d\n", err); + } + + spcm->prepared[dir] = false; + spcm->pending_stop[dir] = false; + spcm->stream[dir].cstream = NULL; + + /* reset the DMA */ + ret = snd_sof_compr_platform_hw_free(spcm->scomp, cstream); + if (ret < 0) { + spcm_err(spcm, dir, "platform hw free failed %d\n", ret); + if (!err) + err = ret; + } + + /* free widget list */ + ret = sof_widget_list_free(sdev, spcm, dir); + if (ret < 0 && err == 0) { + spcm_err(spcm, dir, "sof_widget_list_free failed %d\n", ret); + err = ret; + } + + return err; +} + +static int sof_ipc4_compr_free(struct snd_soc_component *component, + struct snd_compr_stream *cstream) +{ + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_sof_pcm *spcm; + int ret, err; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return -EINVAL; + + spcm_dbg(spcm, cstream->direction, "Entry: free\n"); + + ret = sof_ipc4_compr_stream_free(sdev, spcm, cstream); + + /* unprepare and free the list of DAPM widgets */ + sof_widget_list_unprepare(sdev, spcm, cstream->direction); + + cancel_work_sync(&spcm->stream[cstream->direction].period_elapsed_work); + + snd_compr_free_pages(cstream); + + err = snd_sof_compr_platform_close(component, cstream); + if (err < 0) { + spcm_err(spcm, cstream->direction, + "platform compress close failed %d\n", ret); + if (!ret) + ret = err; + } + + return ret; +} + +#define SOF_IPC4_CODEC_INFO_GET_ID(value) ((value) & 0xff) +#define SOF_IPC4_CODEC_INFO_GET_DIR(value) (((value) >> 8) & 0xf) + +struct sof_ipc4_codec_info_data { + u32 count; + u32 items[]; +} __packed __aligned(4); + +static int sof_ipc4_compr_get_caps(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_caps *caps) +{ + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct sof_ipc4_codec_info_data *codec_info = ipc4_data->codec_info; + int dir = cstream->direction; + struct snd_sof_pcm *spcm; + int i; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return -EINVAL; + + /* No compress support available in booted firmware */ + if (!codec_info || !codec_info->count) { + spcm_err(spcm, dir, + "Compress is not supported (no codecs available)\n"); + return -EINVAL; + } + + for (i = 0; i < codec_info->count; i++) { + int _dir = SOF_IPC4_CODEC_INFO_GET_DIR(codec_info->items[i]); + + if (_dir == dir) { + int id = SOF_IPC4_CODEC_INFO_GET_ID(codec_info->items[i]); + + if (caps->num_codecs < ARRAY_SIZE(caps->codecs)) { + spcm_dbg(spcm, dir, "codec#%d: %d\n", + caps->num_codecs, id); + caps->codecs[caps->num_codecs++] = id; + } else { + spcm_dbg(spcm, dir, "codec#%d: %d ignored\n", + caps->num_codecs, id); + } + } + } + + caps->direction = dir; + caps->min_fragment_size = + sof_ipc4_compr_calc_min_fragment_size(&spcm->stream[dir]); + caps->max_fragment_size = SOF_IPC4_COMPR_MAX_FRAGMENT_SIZE; + if (caps->max_fragment_size < caps->min_fragment_size) + caps->max_fragment_size = caps->min_fragment_size; + + caps->min_fragments = SOF_IPC4_COMPR_MIN_FRAGMENTS; + caps->max_fragments = SOF_IPC4_COMPR_MAX_FRAGMENTS; + + spcm_dbg(spcm, dir, + "num_codecs: %u, fragment_size: %u-%u, fragments: %u-%u\n", + caps->num_codecs, + caps->min_fragment_size, caps->max_fragment_size, + caps->min_fragments, caps->max_fragments); + return 0; +} + +static int sof_ipc4_compr_alloc_pages(struct device *dev, + struct snd_sof_pcm_stream *sps, + struct snd_soc_component *component, + struct snd_compr_stream *cstream) +{ + u32 min_fragment_size = sof_ipc4_compr_calc_min_fragment_size(sps); + struct snd_compr_runtime *crtd = cstream->runtime; + u64 fragments = crtd->buffer_size; + int ret; + + if (crtd->buffer_size < SOF_IPC4_COMPR_MIN_BUFFER_SIZE(min_fragment_size)) { + dev_err(dev, "%s: Too small buffer size %llu (minimum is %u)\n", + __func__, crtd->buffer_size, + SOF_IPC4_COMPR_MIN_BUFFER_SIZE(min_fragment_size)); + return -EINVAL; + } + + do_div(fragments, crtd->fragment_size); + if (fragments < SOF_IPC4_COMPR_MIN_FRAGMENTS) { + dev_err(dev, + "%s: Insufficient amount of fragments: %llu (minimum is %d)\n", + __func__, fragments, SOF_IPC4_COMPR_MIN_FRAGMENTS); + return -EINVAL; + } + + cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; + cstream->dma_buffer.dev.dev = dev; + + ret = snd_compr_malloc_pages(cstream, crtd->buffer_size); + if (ret < 0) + return ret; + + ret = snd_sof_compr_create_page_table(component, cstream, crtd->dma_area, + crtd->dma_bytes); + if (ret < 0) + snd_compr_free_pages(cstream); + + return ret; +} + +static bool +sof_ipc4_compr_codec_supported(struct snd_sof_dev *sdev, int codec_id, int dir) +{ + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct sof_ipc4_codec_info_data *codec_info = ipc4_data->codec_info; + int i; + + /* No compress support available in booted firmware */ + if (!codec_info || !codec_info->count) + return false; + + for (i = 0; i < codec_info->count; i++) { + int _dir = SOF_IPC4_CODEC_INFO_GET_DIR(codec_info->items[i]); + int _id = SOF_IPC4_CODEC_INFO_GET_ID(codec_info->items[i]); + + if (_dir == dir && codec_id == _id) + return true; + } + + return false; +} + +static int sof_ipc4_compr_set_params(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + struct sof_ipc4_compr_init_data *compr_data __free(kfree) = NULL; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_sof_platform_stream_params *platform_params; + struct sof_ipc4_timestamp_info *time_info; + struct snd_compr_params *compr_params; + struct snd_soc_dapm_widget_list *list; + struct snd_sof_widget *host_swidget; + struct sof_ipc4_process *process; + struct snd_pcm_hw_params p = {0}; + struct snd_interval *interval; + struct snd_sof_pcm *spcm; + struct snd_mask *fmt; + int dir = cstream->direction; + int ret; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return -EINVAL; + + host_swidget = snd_sof_find_swidget_by_comp_id(sdev, spcm->stream[dir].comp_id); + if (!host_swidget) { + spcm_err(spcm, dir, "failed to find host widget with comp_id %d\n", + spcm->stream[dir].comp_id); + return -ENODEV; + } + + if (!sof_ipc4_compr_codec_supported(sdev, params->codec.id, dir)) { + spcm_err(spcm, dir, "Unsupported codec id: %u\n", params->codec.id); + return -EINVAL; + } + + spcm_dbg(spcm, dir, + "codec_id: %u, rate: %u, ch in/out: %u/%u, format: %u/%u\n", + params->codec.id, params->codec.sample_rate, params->codec.ch_in, + params->codec.ch_out, params->codec.format, params->codec.pcm_format); + + if (spcm->prepared[dir]) { + /* + * This can only happen if user space re-configures the device + * without closing it, for example after DRAIN completion + */ + ret = sof_ipc4_compr_stream_free(sdev, spcm, cstream); + if (ret) + return ret; + } + + /* save the compress params */ + compr_params = &spcm->cparams[dir]; + memcpy(compr_params, params, sizeof(*params)); + + /* + * Force format, rate and channels and use PCM hw_params structure to + * set up the pipelines. + */ + fmt = hw_param_mask(&p, SNDRV_PCM_HW_PARAM_FORMAT); + snd_mask_none(fmt); + /* Use correct format based on the used codec */ + switch (params->codec.id) { + case SND_AUDIOCODEC_PCM: + snd_mask_set_format(fmt, params->codec.format); + break; + case SND_AUDIOCODEC_VORBIS: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + break; + case SND_AUDIOCODEC_FLAC: + { + struct snd_dec_flac *dec_flac = ¶ms->codec.options.flac_d; + + if (dec_flac->sample_size == 16) + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + else + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); + break; + } + default: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); + } + + interval = hw_param_interval(&p, SNDRV_PCM_HW_PARAM_CHANNELS); + interval->min = compr_params->codec.ch_out; + interval->max = compr_params->codec.ch_out; + + interval = hw_param_interval(&p, SNDRV_PCM_HW_PARAM_RATE); + interval->min = compr_params->codec.sample_rate; + interval->max = compr_params->codec.sample_rate; + + ret = sof_ipc4_compr_alloc_pages(component->dev, &spcm->stream[dir], + component, cstream); + if (ret < 0) + return ret; + + spcm_dbg(spcm, dir, + "buffer_size: %llu, fragment_size: %u (fragments: %u)\n", + cstream->runtime->buffer_size, cstream->runtime->fragment_size, + (u32)cstream->runtime->buffer_size / cstream->runtime->fragment_size); + + interval = hw_param_interval(&p, SNDRV_PCM_HW_PARAM_PERIOD_BYTES); + interval->min = cstream->runtime->fragment_size; + interval->max = cstream->runtime->fragment_size; + + interval = hw_param_interval(&p, SNDRV_PCM_HW_PARAM_BUFFER_BYTES); + interval->min = cstream->runtime->buffer_size; + interval->max = cstream->runtime->buffer_size; + + platform_params = &spcm->platform_params[dir]; + ret = snd_sof_compr_platform_hw_params(component, cstream, compr_params, + platform_params); + if (ret < 0) { + spcm_err(spcm, dir, "platform compress hw params failed\n"); + goto free_pages; + } + + /* set up the list of DAPM widgets if not already done */ + if (!spcm->stream[dir].list) { + ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, &p, + platform_params, dir); + if (ret < 0) + goto free_pages; + } + + process = sof_ipc4_compr_get_module(spcm, dir); + if (!process) { + ret = -EINVAL; + goto free_list; + } + + compr_data = kzalloc(sizeof(*compr_data), GFP_KERNEL); + if (!compr_data) + goto free_list; + + memcpy(&compr_data->codec, &compr_params->codec, sizeof(compr_data->codec)); + compr_data->dir = dir; + + process->init_ext_module_data = compr_data; + process->init_ext_module_size = sizeof(*compr_data); + + /* + * Make sure that the DSP is booted up, which might not be the + * case if the on-demand DSP boot is used + */ + ret = snd_sof_boot_dsp_firmware(sdev); + if (ret) + goto clear_init_ext; + + /* set the host DMA ID */ + if (tplg_ops && tplg_ops->host_config) + tplg_ops->host_config(sdev, host_swidget, platform_params); + + /* set up the widgets and pipelines in the DSP */ + ret = sof_widget_list_setup(sdev, spcm, &p, platform_params, dir); + if (ret < 0) { + spcm_err(spcm, dir, "widget list set up failed\n"); + goto clear_init_ext; + } + + memcpy(&spcm->params[dir], &p, sizeof(p)); + spcm->prepared[dir] = true; + + time_info = sof_ipc4_sps_to_time_info(&spcm->stream[dir]); + if (time_info) { + /* delay calculation supported */ + time_info->stream_start_offset = SOF_IPC4_INVALID_STREAM_POSITION; + time_info->llp_offset = 0; + + sof_ipc4_build_time_info(sdev, &spcm->stream[dir]); + } + + process->init_ext_module_data = NULL; + process->init_ext_module_size = 0; + + return 0; + +clear_init_ext: + process->init_ext_module_data = NULL; + process->init_ext_module_size = 0; + +free_list: + list = spcm->stream[dir].list; + spcm->stream[dir].list = NULL; + snd_soc_dapm_dai_free_widgets(&list); + +free_pages: + snd_compr_free_pages(cstream); + + return ret; +} + +static int sof_ipc4_compr_get_params(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_codec *params) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_sof_pcm *spcm; + /* TODO: we don't query the supported codecs for now, if the + * application asks for an unsupported codec the set_params() will fail. + */ + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) { + dev_err(component->dev, "%s: can't find spcm\n", __func__); + return -EINVAL; + } + + spcm_dbg(spcm, cstream->direction, "Entry: get_params\n"); + + memcpy(params, &spcm->cparams[cstream->direction].codec, + sizeof(*params)); + + return 0; +} + +static int sof_ipc4_compr_trigger(struct snd_soc_component *component, + struct snd_compr_stream *cstream, int cmd) +{ + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + struct snd_sof_pcm *spcm; + int dir = cstream->direction; + bool trigger_platform = false; + int ret = 0; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) { + dev_err(component->dev, "%s: can't find spcm\n", __func__); + return -EINVAL; + } + + spcm->pending_stop[dir] = false; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + trigger_platform = true; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + case SND_COMPR_TRIGGER_DRAIN: + case SND_COMPR_TRIGGER_PARTIAL_DRAIN: + spcm->pending_stop[dir] = true; + break; + case SND_COMPR_TRIGGER_NEXT_TRACK: + spcm_dbg(spcm, dir, "Unsupported trigger cmd: %d\n", cmd); + return -EOPNOTSUPP; + default: + spcm_dbg(spcm, dir, "Unhandled trigger cmd: %d\n", cmd); + return 0; + } + + spcm_dbg(spcm, dir, "Entry: trigger (cmd: %d)\n", cmd); + + ret = pcm_ops->trigger(component, NULL, spcm, cmd, dir); + if (ret < 0) { + spcm_err(spcm, dir, "pcm_ops->trigger failed for cmd %d\n", cmd); + return ret; + } + + if (!ret && trigger_platform) { + ret = snd_sof_compr_platform_trigger(component, cstream, cmd); + if (ret < 0) + spcm_err(spcm, dir, + "platform compress trigger start failed %d\n", + ret); + } + + return ret; +} + +static int sof_ipc4_compr_copy_playback(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + char __user *buf, size_t count) +{ + struct snd_compr_runtime *crtd = cstream->runtime; + u64 offset, n; + void *ptr; + int ret; + + div64_u64_rem(crtd->total_bytes_available, crtd->buffer_size, &offset); + ptr = crtd->dma_area + offset; + n = crtd->buffer_size - offset; + + if (count < n) { + ret = copy_from_user(ptr, buf, count); + } else { + ret = copy_from_user(ptr, buf, n); + ret += copy_from_user(crtd->dma_area, buf + n, count - n); + } + + return count - ret; +} + +static int sof_ipc4_compr_copy_capture(struct snd_compr_runtime *crtd, + char __user *buf, size_t count) +{ + u64 offset, n; + void *ptr; + int ret; + + div64_u64_rem(crtd->total_bytes_transferred, crtd->buffer_size, &offset); + ptr = crtd->dma_area + offset; + n = crtd->buffer_size - offset; + + if (count < n) { + ret = copy_to_user(buf, ptr, count); + } else { + ret = copy_to_user(buf, ptr, n); + ret += copy_to_user(buf + n, crtd->dma_area, count - n); + } + + return count - ret; +} + +static int sof_ipc4_compr_copy(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + char __user *buf, size_t count) +{ + struct snd_compr_runtime *crtd = cstream->runtime; + + if (count > crtd->buffer_size) + count = crtd->buffer_size; + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + return sof_ipc4_compr_copy_playback(component, cstream, buf, count); + + return sof_ipc4_compr_copy_capture(crtd, buf, count); +} + +static int sof_ipc4_compr_pointer(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp64 *tstamp) +{ + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct sof_ipc4_timestamp_info *time_info; + struct snd_pcm_hw_params *params; + struct snd_sof_pcm_stream *sps; + struct snd_sof_pcm *spcm; + u64 dai_cnt = 0; + int ret; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return -EINVAL; + + params = &spcm->params[cstream->direction]; + + ret = snd_sof_compr_platform_pointer(component, cstream, tstamp); + if (ret < 0) { + spcm_err(spcm, cstream->direction, + "platform compress pointer failed %d\n", ret); + return ret; + } + + sps = &spcm->stream[cstream->direction]; + time_info = sof_ipc4_sps_to_time_info(sps); + if (!time_info) + goto host_only; + + /* + * stream_start_offset is updated to memory window by FW based on + * pipeline statistics and it may be invalid if host query happens before + * the statistics is complete. And it will not change after the first + * initialization. + */ + if (time_info->stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION) { + ret = sof_ipc4_get_stream_start_offset(sdev, NULL, sps, time_info); + if (ret < 0) + goto host_only; + } + + if (!time_info->llp_offset) { + dai_cnt = snd_sof_compr_get_dai_frame_counter(sdev, cstream); + } else { + struct sof_ipc4_llp_reading_slot llp; + + sof_mailbox_read(sdev, time_info->llp_offset, &llp, sizeof(llp)); + dai_cnt = ((u64)llp.reading.llp_u << 32) | llp.reading.llp_l; + } + + if (dai_cnt) { + dai_cnt = sof_ipc4_frames_dai_to_host(time_info, dai_cnt); + dai_cnt += time_info->stream_end_offset; + if (dai_cnt < time_info->stream_start_offset) + dai_cnt = 0; + else + dai_cnt -= time_info->stream_start_offset; + } + +host_only: + tstamp->sampling_rate = params_rate(params); + tstamp->pcm_io_frames = dai_cnt; + + return 0; +} + +void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, void *ipc_message) +{ + struct sof_ipc4_msg *ipc4_msg = ipc_message; + struct sof_ipc4_notify_module_data *ndata = ipc4_msg->data_ptr; + struct snd_sof_widget *swidget, *host_swidget; + struct snd_sof_audio_instance *instance; + bool widget_found = false; + struct snd_sof_pcm *spcm; + int dir; + + /* Find the swidget based on ndata->module_id and ndata->instance_id */ + swidget = sof_ipc4_find_swidget_by_ids(sdev, ndata->module_id, + ndata->instance_id); + if (!swidget) { + dev_err(sdev->dev, "%s: Failed to find widget for module %u.%u\n", + __func__, ndata->module_id, ndata->instance_id); + return; + } + + if (!swidget->spipe) + return; + + /* Find the swidget of the host copier on the same pipeline */ + instance = snd_sof_component_get_audio_instance(swidget->scomp); + list_for_each_entry(host_swidget, &instance->widget_list, list) { + if (WIDGET_IS_AIF(host_swidget->id) && + host_swidget->pipeline_id == swidget->pipeline_id) { + widget_found = true; + break; + } + } + + if (!widget_found) { + dev_err(swidget->scomp->dev, "%s: Host widget not found for pipeline: %s\n", + __func__, swidget->spipe->pipe_widget->widget->name); + return; + } + + /* Look up the spcm of the host copier */ + spcm = snd_sof_find_spcm_comp_by_sdev(sdev, host_swidget->comp_id, &dir); + if (!spcm) { + dev_err(host_swidget->scomp->dev, "%s: Stream cannot be found for %s\n", __func__, + host_swidget->widget->name); + return; + } + + spcm_dbg(spcm, dir, "Entry: EOS done\n"); + + if (spcm->stream[dir].cstream) + snd_compr_drain_notify(spcm->stream[dir].cstream); +} + +const struct snd_compress_ops sof_ipc4_compressed_ops = { + .open = sof_ipc4_compr_open, + .free = sof_ipc4_compr_free, + .get_caps = sof_ipc4_compr_get_caps, + .set_params = sof_ipc4_compr_set_params, + .get_params = sof_ipc4_compr_get_params, + .trigger = sof_ipc4_compr_trigger, + .pointer = sof_ipc4_compr_pointer, + .copy = sof_ipc4_compr_copy, +}; diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 8d86d32a16caea..de485f382b4d06 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -17,14 +17,15 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set, bool lock) { struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_ops *iops = sdev->ipc->ops; struct snd_sof_widget *swidget; bool widget_found = false; int ret = 0; /* find widget associated with the control */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (swidget->comp_id == scontrol->comp_id) { widget_found = true; break; @@ -81,9 +82,9 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, return ret; } -static int -sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, - struct snd_sof_control *scontrol, bool lock) +static int sof_ipc4_set_volume_data(struct snd_sof_widget *swidget, + struct snd_sof_control *scontrol, + bool lock) { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct sof_ipc4_gain *gain = swidget->private; @@ -126,7 +127,7 @@ sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidge ret = sof_ipc4_set_get_kcontrol_data(scontrol, &msg, true, lock); if (ret < 0) { - dev_err(sdev->dev, "Failed to set volume update for %s\n", + dev_err(swidget->scomp->dev, "Failed to set volume update for %s\n", scontrol->name); return ret; } @@ -143,7 +144,7 @@ static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol, { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); unsigned int channels = scontrol->num_channels; struct snd_sof_widget *swidget; bool widget_found = false; @@ -165,7 +166,7 @@ static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol, return change; /* find widget associated with the control */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (swidget->comp_id == scontrol->comp_id) { widget_found = true; break; @@ -177,7 +178,7 @@ static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol, return false; } - ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, true); + ret = sof_ipc4_set_volume_data(swidget, scontrol, true); if (ret < 0) return false; @@ -199,10 +200,9 @@ static int sof_ipc4_volume_get(struct snd_sof_control *scontrol, return 0; } -static int -sof_ipc4_set_generic_control_data(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget, - struct snd_sof_control *scontrol, bool lock) +static int sof_ipc4_set_generic_control_data(struct snd_sof_widget *swidget, + struct snd_sof_control *scontrol, + bool lock) { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct sof_ipc4_control_msg_payload *data; @@ -229,7 +229,7 @@ sof_ipc4_set_generic_control_data(struct snd_sof_dev *sdev, ret = sof_ipc4_set_get_kcontrol_data(scontrol, &msg, true, lock); if (ret < 0) - dev_err(sdev->dev, "Failed to set control update for %s\n", + dev_err(scontrol->scomp->dev, "Failed to set control update for %s\n", scontrol->name); kfree(data); @@ -383,7 +383,7 @@ static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol, { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *swidget; bool widget_found = false; bool change = false; @@ -403,7 +403,7 @@ static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol, return change; /* find widget associated with the control */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (swidget->comp_id == scontrol->comp_id) { widget_found = true; break; @@ -415,7 +415,7 @@ static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol, return false; } - ret = sof_ipc4_set_generic_control_data(sdev, swidget, scontrol, true); + ret = sof_ipc4_set_generic_control_data(swidget, scontrol, true); if (ret < 0) return false; @@ -442,7 +442,7 @@ static bool sof_ipc4_enum_put(struct snd_sof_control *scontrol, { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *swidget; bool widget_found = false; bool change = false; @@ -462,7 +462,7 @@ static bool sof_ipc4_enum_put(struct snd_sof_control *scontrol, return change; /* find widget associated with the control */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (swidget->comp_id == scontrol->comp_id) { widget_found = true; break; @@ -474,7 +474,7 @@ static bool sof_ipc4_enum_put(struct snd_sof_control *scontrol, return false; } - ret = sof_ipc4_set_generic_control_data(sdev, swidget, scontrol, true); + ret = sof_ipc4_set_generic_control_data(swidget, scontrol, true); if (ret < 0) return false; @@ -496,22 +496,22 @@ static int sof_ipc4_enum_get(struct snd_sof_control *scontrol, return 0; } -static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev, - struct snd_sof_control *scontrol, +static int sof_ipc4_set_get_bytes_data(struct snd_sof_control *scontrol, bool set, bool lock) { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; struct sof_abi_hdr *data = cdata->data; struct sof_ipc4_msg msg; int ret = 0; /* Send the new data to the firmware only if it is powered up */ if (set) { - if (!pm_runtime_active(sdev->dev)) + if (!pm_runtime_active(scomp->dev)) return 0; if (!data->size) { - dev_dbg(sdev->dev, "%s: No data to be sent.\n", + dev_dbg(scomp->dev, "%s: No data to be sent.\n", scontrol->name); return 0; } @@ -535,7 +535,7 @@ static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev, ret = sof_ipc4_set_get_kcontrol_data(scontrol, &msg, set, lock); if (ret < 0) { - dev_err(sdev->dev, "Failed to %s for %s\n", + dev_err(scomp->dev, "Failed to %s for %s\n", set ? "set bytes update" : "get bytes", scontrol->name); } else if (!set) { @@ -552,7 +552,6 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_abi_hdr *data = cdata->data; const struct sof_abi_hdr *new_hdr = (const struct sof_abi_hdr *)ucontrol->value.bytes.data; @@ -580,7 +579,7 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, /* copy from kcontrol */ memcpy(data, ucontrol->value.bytes.data, size); - ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); + ret = sof_ipc4_set_get_bytes_data(scontrol, true, true); if (!ret) /* Update the cdata size */ scontrol->size = sizeof(*cdata) + size; @@ -626,7 +625,6 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol, struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_abi_hdr *data = cdata->data; struct sof_abi_hdr abi_hdr; struct snd_ctl_tlv header; @@ -700,7 +698,7 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol, /* Update the cdata size */ scontrol->size = sizeof(*cdata) + header.length; - return sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); + return sof_ipc4_set_get_bytes_data(scontrol, true, true); } static int _sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol, @@ -725,8 +723,7 @@ static int _sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol, /* get all the component data from DSP */ if (from_dsp) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - int ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, false, true); + int ret = sof_ipc4_set_get_bytes_data(scontrol, false, true); if (ret < 0) return ret; @@ -777,14 +774,14 @@ static int sof_ipc4_bytes_ext_volatile_get(struct snd_sof_control *scontrol, return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, true); } -static int -sof_ipc4_volsw_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, - struct snd_sof_control *scontrol) +static int sof_ipc4_volsw_setup(struct snd_sof_widget *swidget, + struct snd_sof_control *scontrol) + { if (scontrol->max == 1) - return sof_ipc4_set_generic_control_data(sdev, swidget, scontrol, false); + return sof_ipc4_set_generic_control_data(swidget, scontrol, false); - return sof_ipc4_set_volume_data(sdev, swidget, scontrol, false); + return sof_ipc4_set_volume_data(swidget, scontrol, false); } #define PARAM_ID_FROM_EXTENSION(_ext) (((_ext) & SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK) \ @@ -795,6 +792,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) struct sof_ipc4_msg *ipc4_msg = ipc_message; struct sof_ipc4_notify_module_data *ndata = ipc4_msg->data_ptr; struct sof_ipc4_control_msg_payload *msg_data; + struct snd_sof_audio_instance *instance; struct sof_ipc4_control_data *cdata; struct snd_soc_dapm_widget *widget; struct snd_sof_control *scontrol; @@ -842,7 +840,8 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) /* Find the scontrol which is the source of the notification */ msg_data = (struct sof_ipc4_control_msg_payload *)ndata->event_data; - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + instance = snd_sof_component_get_audio_instance(swidget->scomp); + list_for_each_entry(scontrol, &instance->kcontrol_list, list) { if (scontrol->comp_id == swidget->comp_id) { u32 local_param_id; @@ -861,7 +860,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) } if (!scontrol_found) { - dev_err(sdev->dev, + dev_err(swidget->scomp->dev, "%s: Failed to find control on widget %s: %u:%u\n", __func__, swidget->widget->name, ndata->event_id & 0xffff, msg_data->id); @@ -878,7 +877,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) size_t source_size = struct_size(msg_data, data, msg_data->num_elems); if (source_size > ndata->event_data_size) { - dev_warn(sdev->dev, + dev_warn(swidget->scomp->dev, "%s: invalid bytes notification size for %s (%zu, %u)\n", __func__, scontrol->name, source_size, ndata->event_data_size); @@ -887,7 +886,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) } if (msg_data->num_elems > scontrol->max_size - sizeof(*data)) { - dev_warn(sdev->dev, + dev_warn(swidget->scomp->dev, "%s: no space for data in %s (%u, %zu)\n", __func__, scontrol->name, msg_data->num_elems, scontrol->max_size - sizeof(*data)); @@ -900,7 +899,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) size_t source_size = struct_size(msg_data, chanv, msg_data->num_elems); if (source_size > ndata->event_data_size) { - dev_warn(sdev->dev, + dev_warn(swidget->scomp->dev, "%s: invalid channel notification size for %s (%zu, %u)\n", __func__, scontrol->name, source_size, ndata->event_data_size); @@ -912,7 +911,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) u32 channel = msg_data->chanv[i].channel; if (channel >= scontrol->num_channels) { - dev_warn(sdev->dev, + dev_warn(swidget->scomp->dev, "Invalid channel index for %s: %u\n", scontrol->name, i); @@ -961,32 +960,32 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) /* set up all controls for the widget */ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(swidget->scomp); struct snd_sof_control *scontrol; int ret = 0; - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + list_for_each_entry(scontrol, &instance->kcontrol_list, list) { if (scontrol->comp_id == swidget->comp_id) { switch (scontrol->info_type) { case SND_SOC_TPLG_CTL_VOLSW: case SND_SOC_TPLG_CTL_VOLSW_SX: case SND_SOC_TPLG_CTL_VOLSW_XR_SX: - ret = sof_ipc4_volsw_setup(sdev, swidget, scontrol); + ret = sof_ipc4_volsw_setup(swidget, scontrol); break; case SND_SOC_TPLG_CTL_BYTES: - ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, - true, false); + ret = sof_ipc4_set_get_bytes_data(scontrol, true, false); break; case SND_SOC_TPLG_CTL_ENUM: case SND_SOC_TPLG_CTL_ENUM_VALUE: - ret = sof_ipc4_set_generic_control_data(sdev, swidget, - scontrol, false); + ret = sof_ipc4_set_generic_control_data(swidget, scontrol, false); break; default: break; } if (ret < 0) { - dev_err(sdev->dev, + dev_err(swidget->scomp->dev, "kcontrol %d set up failed for widget %s\n", scontrol->comp_id, swidget->widget->name); return ret; diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index e3007648d78681..8778cac0d52520 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -417,6 +417,52 @@ static int sof_ipc4_validate_firmware(struct snd_sof_dev *sdev) return 0; } +static int sof_ipc4_query_sof_info(struct snd_sof_dev *sdev, + void *sof_info_data, u32 sof_info_size) +{ + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct sof_ipc4_tuple *tuple; + size_t tuple_size; + size_t offset = 0; + int ret = 0; + + while (offset < sof_info_size) { + if (sof_info_size - offset < sizeof(*tuple)) { + dev_err(sdev->dev, "Invalid SOF info tuple header at offset %zu\n", offset); + ret = -EINVAL; + goto out; + } + + tuple = (struct sof_ipc4_tuple *)((u8 *)sof_info_data + offset); + tuple_size = sizeof(*tuple) + tuple->size; + if (tuple_size < sizeof(*tuple) || tuple_size > sof_info_size - offset) { + dev_err(sdev->dev, + "Invalid SOF info tuple size %u at offset %zu\n", + tuple->size, offset); + ret = -EINVAL; + goto out; + } + + switch (tuple->type) { + case SOF_IPC4_SOF_CODEC_INFO: + ipc4_data->codec_info = devm_kmemdup(sdev->dev, tuple->value, + tuple->size, GFP_KERNEL); + if (!ipc4_data->codec_info) { + ret = -ENOMEM; + goto out; + } + break; + default: + break; + } + + offset += tuple_size; + } + +out: + return ret; +} + int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; @@ -492,6 +538,11 @@ int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) */ ipc4_data->libraries_restored = ipc4_data->fw_context_save; break; + case SOF_IPC4_FW_CFG_SOF_INFO: + ret = sof_ipc4_query_sof_info(sdev, tuple->value, tuple->size); + if (ret) + goto out; + break; default: break; } diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 5929ecf6642ee3..9ac2c68768f865 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -15,29 +15,6 @@ #include "ipc4-topology.h" #include "ipc4-fw-reg.h" -/** - * struct sof_ipc4_timestamp_info - IPC4 timestamp info - * @host_copier: the host copier of the pcm stream - * @dai_copier: the dai copier of the pcm stream - * @stream_start_offset: reported by fw in memory window (converted to - * frames at host_copier sampling rate) - * @stream_end_offset: reported by fw in memory window (converted to - * frames at host_copier sampling rate) - * @llp_offset: llp offset in memory window - * @delay: Calculated and stored in pointer callback. The stored value is - * returned in the delay callback. Expressed in frames at host copier - * sampling rate. - */ -struct sof_ipc4_timestamp_info { - struct sof_ipc4_copier *host_copier; - struct sof_ipc4_copier *dai_copier; - u64 stream_start_offset; - u64 stream_end_offset; - u32 llp_offset; - - snd_pcm_sframes_t delay; -}; - /** * struct sof_ipc4_pcm_stream_priv - IPC4 specific private data * @time_info: pointer to time info struct if it is supported, otherwise NULL @@ -61,7 +38,7 @@ struct sof_ipc4_pcm_stream_priv { #define DELAY_MAX (DELAY_BOUNDARY >> 1) -static inline struct sof_ipc4_timestamp_info * +struct sof_ipc4_timestamp_info * sof_ipc4_sps_to_time_info(struct snd_sof_pcm_stream *sps) { struct sof_ipc4_pcm_stream_priv *stream_priv = sps->private; @@ -148,11 +125,12 @@ static void sof_ipc4_add_pipeline_by_priority(struct ipc4_pipeline_set_state_dat struct sof_ipc4_pipeline *pipeline = pipe_widget->private; int i, j; + /* 0 = highest priority, 7 = lowest */ for (i = 0; i < trigger_list->count; i++) { - /* add pipeline from low priority to high */ + /* ascend=true: sort ascending [0,1,2...] = highest priority first */ if (ascend && pipeline->priority < pipe_priority[i]) break; - /* add pipeline from high priority to low */ + /* ascend=false: sort descending [7,6,5...] = lowest priority first */ else if (!ascend && pipeline->priority > pipe_priority[i]) break; } @@ -176,7 +154,8 @@ sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state, struct snd_sof_widget *pipe_widget = spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - if (pipeline->skip_during_fe_trigger && state != SOF_IPC4_PIPE_RESET) + if (pipeline->skip_during_fe_trigger && state != SOF_IPC4_PIPE_RESET && + state != SOF_IPC4_PIPE_EOS) return; switch (state) { @@ -187,19 +166,24 @@ sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state, */ if (spipe->started_count == spipe->paused_count) sof_ipc4_add_pipeline_by_priority(trigger_list, pipe_widget, pipe_priority, - false); + true); break; case SOF_IPC4_PIPE_RESET: /* RESET if the pipeline is neither running nor paused */ if (!spipe->started_count && !spipe->paused_count) sof_ipc4_add_pipeline_by_priority(trigger_list, pipe_widget, pipe_priority, - true); + false); break; case SOF_IPC4_PIPE_PAUSED: - /* Pause the pipeline only when its started_count is 1 more than paused_count */ + case SOF_IPC4_PIPE_EOS: + /* + * Pause the pipeline only when its started_count is 1 more than + * paused_count. + * Same rule applies to EOS state. + */ if (spipe->paused_count == (spipe->started_count - 1)) sof_ipc4_add_pipeline_by_priority(trigger_list, pipe_widget, pipe_priority, - true); + false); break; default: break; @@ -412,34 +396,33 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, } static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, - struct snd_pcm_substream *substream, int state, int cmd) + struct snd_pcm_substream *substream, int state, int cmd, + struct snd_sof_pcm *spcm, int dir) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_pcm_stream_pipeline_list *pipeline_list; struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct ipc4_pipeline_set_state_data *trigger_list; struct snd_sof_widget *pipe_widget; struct sof_ipc4_pipeline *pipeline; struct snd_sof_pipeline *spipe; - struct snd_sof_pcm *spcm; u8 *pipe_priority; int ret; int i; - spcm = snd_sof_find_spcm_dai(component, rtd); - if (!spcm) - return -EINVAL; - - spcm_dbg(spcm, substream->stream, "cmd: %d, state: %d\n", cmd, state); + spcm_dbg(spcm, dir, "cmd: %d, state: %d\n", cmd, state); - pipeline_list = &spcm->stream[substream->stream].pipeline_list; + pipeline_list = &spcm->stream[dir].pipeline_list; + guard(mutex)(&ipc4_data->pipeline_state_mutex); /* nothing to trigger if the list is empty */ if (!pipeline_list->pipelines || !pipeline_list->count) return 0; spipe = pipeline_list->pipelines[0]; + if (!spipe || !spipe->pipe_widget || !spipe->pipe_widget->private) + return 0; + pipe_widget = spipe->pipe_widget; pipeline = pipe_widget->private; @@ -450,9 +433,9 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, if (pipeline->use_chain_dma) { struct sof_ipc4_timestamp_info *time_info; - time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]); + time_info = sof_ipc4_sps_to_time_info(&spcm->stream[dir]); - ret = sof_ipc4_chain_dma_trigger(sdev, spcm, substream->stream, + ret = sof_ipc4_chain_dma_trigger(sdev, spcm, dir, pipeline_list, state, cmd); if (ret || !time_info) return ret; @@ -461,12 +444,16 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, /* * Record the DAI position for delay reporting * To handle multiple pause/resume/xrun we need to add - * the positions to simulate how the firmware behaves + * the positions to simulate how the firmware behaves. + * Chained DMA does not support compress streams. We should + * never get here with compress. */ - u64 pos = snd_sof_pcm_get_dai_frame_counter(sdev, component, - substream); + if (substream) { + u64 pos = snd_sof_pcm_get_dai_frame_counter(sdev, component, + substream); - time_info->stream_end_offset += pos; + time_info->stream_end_offset += pos; + } } else if (state == SOF_IPC4_PIPE_RESET) { /* Reset the end offset as the stream is stopped */ time_info->stream_end_offset = 0; @@ -487,8 +474,6 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, return -ENOMEM; } - guard(mutex)(&ipc4_data->pipeline_state_mutex); - /* * IPC4 requires pipelines to be triggered in order starting at the sink and * walking all the way to the source. So traverse the pipeline_list in the order @@ -501,12 +486,16 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, if (state == SOF_IPC4_PIPE_RUNNING || state == SOF_IPC4_PIPE_RESET) for (i = pipeline_list->count - 1; i >= 0; i--) { spipe = pipeline_list->pipelines[i]; + if (!spipe || !spipe->pipe_widget || !spipe->pipe_widget->private) + continue; sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list, pipe_priority); } else for (i = 0; i < pipeline_list->count; i++) { spipe = pipeline_list->pipelines[i]; + if (!spipe || !spipe->pipe_widget || !spipe->pipe_widget->private) + continue; sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list, pipe_priority); } @@ -517,8 +506,9 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, goto free; } - /* no need to pause before reset or before pause release */ - if (state == SOF_IPC4_PIPE_RESET || cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) + /* no need to pause before reset, EOS or before pause release */ + if (state == SOF_IPC4_PIPE_RESET || state == SOF_IPC4_PIPE_EOS || + cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) goto skip_pause_transition; /* @@ -527,7 +517,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, */ ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, trigger_list); if (ret < 0) { - spcm_err(spcm, substream->stream, "failed to pause all pipelines\n"); + spcm_err(spcm, dir, "failed to pause all pipelines\n"); /* * workaround: if the firmware is crashed or the IPC timed out * while setting the pipeline state we must ignore the error @@ -546,6 +536,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, /* update PAUSED state for all pipelines just triggered */ for (i = 0; i < pipeline_list->count ; i++) { spipe = pipeline_list->pipelines[i]; + if (!spipe || !spipe->pipe_widget || !spipe->pipe_widget->private) + continue; sof_ipc4_update_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, cmd, spipe, trigger_list); } @@ -558,7 +550,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, * Invalidate the stream_start_offset to make sure that it is * going to be updated if the stream resumes */ - time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]); + time_info = sof_ipc4_sps_to_time_info(&spcm->stream[dir]); if (time_info) time_info->stream_start_offset = SOF_IPC4_INVALID_STREAM_POSITION; @@ -568,7 +560,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, /* else set the RUNNING/RESET state in the DSP */ ret = sof_ipc4_set_multi_pipeline_state(sdev, state, trigger_list); if (ret < 0) { - spcm_err(spcm, substream->stream, + spcm_err(spcm, dir, "failed to set final state %d for all pipelines\n", state); /* @@ -589,6 +581,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, /* update RUNNING/RESET state for all pipelines that were just triggered */ for (i = 0; i < pipeline_list->count; i++) { spipe = pipeline_list->pipelines[i]; + if (!spipe || !spipe->pipe_widget || !spipe->pipe_widget->private) + continue; sof_ipc4_update_pipeline_state(sdev, state, cmd, spipe, trigger_list); } @@ -599,7 +593,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, } static int sof_ipc4_pcm_trigger(struct snd_soc_component *component, - struct snd_pcm_substream *substream, int cmd) + struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, int cmd, int dir) { int state; @@ -615,26 +610,32 @@ static int sof_ipc4_pcm_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_STOP: state = SOF_IPC4_PIPE_PAUSED; break; + case SND_COMPR_TRIGGER_DRAIN: + case SND_COMPR_TRIGGER_PARTIAL_DRAIN: + state = SOF_IPC4_PIPE_EOS; + break; default: dev_err(component->dev, "%s: unhandled trigger cmd %d\n", __func__, cmd); return -EINVAL; } /* set the pipeline state */ - return sof_ipc4_trigger_pipelines(component, substream, state, cmd); + return sof_ipc4_trigger_pipelines(component, substream, state, cmd, spcm, dir); } static int sof_ipc4_pcm_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) + struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, int dir) { /* command is not relevant with RESET, so just pass 0 */ - return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0); + return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0, spcm, dir); } -static int ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, +static int ipc4_ssp_dai_config_pcm_params_match(struct snd_soc_component *component, const char *link_name, struct snd_pcm_hw_params *params) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); struct snd_sof_dai_link *slink; struct snd_sof_dai *dai; bool dai_link_found = false; @@ -642,7 +643,7 @@ static int ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, bool partial_match; int i; - list_for_each_entry(slink, &sdev->dai_link_list, list) { + list_for_each_entry(slink, &instance->dai_link_list, list) { if (!strcmp(slink->link->name, link_name)) { dai_link_found = true; break; @@ -679,17 +680,17 @@ static int ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, } if (current_config < 0) { - dev_err(sdev->dev, + dev_err(component->dev, "%s: No suitable hw_config found for %s (num_hw_configs: %d)\n", __func__, slink->link->name, slink->num_hw_configs); return -EINVAL; } - dev_dbg(sdev->dev, + dev_dbg(component->dev, "hw_config for %s: %d (num_hw_configs: %d) with %s match\n", slink->link->name, current_config, slink->num_hw_configs, partial_match ? "partial" : "full"); - list_for_each_entry(dai, &sdev->dai_list, list) + list_for_each_entry(dai, &instance->dai_list, list) if (!strcmp(slink->link->name, dai->name)) dai->current_config = current_config; @@ -700,7 +701,7 @@ static int ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, * Fixup DAI link parameters for sampling rate based on * DAI copier configuration. */ -static int sof_ipc4_pcm_dai_link_fixup_rate(struct snd_sof_dev *sdev, +static int sof_ipc4_pcm_dai_link_fixup_rate(struct snd_soc_component *component, struct snd_pcm_hw_params *params, struct sof_ipc4_copier *ipc4_copier) { @@ -741,7 +742,7 @@ static int sof_ipc4_pcm_dai_link_fixup_rate(struct snd_sof_dev *sdev, */ if (!fe_be_rate_match) { if (!single_be_rate) { - dev_err(sdev->dev, "Unable to select sampling rate for DAI link\n"); + dev_err(component->dev, "Unable to select sampling rate for DAI link\n"); return -EINVAL; } @@ -752,7 +753,7 @@ static int sof_ipc4_pcm_dai_link_fixup_rate(struct snd_sof_dev *sdev, return 0; } -static int sof_ipc4_pcm_dai_link_fixup_channels(struct snd_sof_dev *sdev, +static int sof_ipc4_pcm_dai_link_fixup_channels(struct snd_soc_component *component, struct snd_pcm_hw_params *params, struct sof_ipc4_copier *ipc4_copier) { @@ -793,7 +794,7 @@ static int sof_ipc4_pcm_dai_link_fixup_channels(struct snd_sof_dev *sdev, */ if (!fe_be_match) { if (!single_be_channels) { - dev_err(sdev->dev, "Unable to select channels for DAI link\n"); + dev_err(component->dev, "Unable to select channels for DAI link\n"); return -EINVAL; } @@ -810,7 +811,7 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); struct snd_sof_dai *dai = snd_sof_find_dai(component, rtd->dai_link->name); struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct sof_ipc4_audio_format *ipc4_fmt; struct sof_ipc4_copier *ipc4_copier; @@ -863,11 +864,11 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, } } - ret = sof_ipc4_pcm_dai_link_fixup_rate(sdev, params, ipc4_copier); + ret = sof_ipc4_pcm_dai_link_fixup_rate(component, params, ipc4_copier); if (ret) return ret; - ret = sof_ipc4_pcm_dai_link_fixup_channels(sdev, params, ipc4_copier); + ret = sof_ipc4_pcm_dai_link_fixup_channels(component, params, ipc4_copier); if (ret) return ret; @@ -893,7 +894,7 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, } if (ipc4_copier->dai_type == SOF_DAI_INTEL_SSP) - return ipc4_ssp_dai_config_pcm_params_match(sdev, + return ipc4_ssp_dai_config_pcm_params_match(component, (char *)rtd->dai_link->name, params); @@ -903,13 +904,17 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm) { struct snd_sof_pcm_stream_pipeline_list *pipeline_list; + struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct sof_ipc4_pcm_stream_priv *stream_priv; int stream; + guard(mutex)(&ipc4_data->pipeline_state_mutex); + for_each_pcm_streams(stream) { pipeline_list = &spcm->stream[stream].pipeline_list; kfree(pipeline_list->pipelines); pipeline_list->pipelines = NULL; + pipeline_list->count = 0; stream_priv = spcm->stream[stream].private; kfree(stream_priv->time_info); @@ -975,7 +980,7 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm return 0; } -static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps) +void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps) { struct sof_ipc4_copier *host_copier = NULL; struct sof_ipc4_copier *dai_copier = NULL; @@ -1051,7 +1056,7 @@ static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc4_timestamp_info *time_info; struct snd_sof_pcm *spcm; @@ -1073,7 +1078,7 @@ static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component, return 0; } -static u64 sof_ipc4_frames_dai_to_host(struct sof_ipc4_timestamp_info *time_info, u64 value) +u64 sof_ipc4_frames_dai_to_host(struct sof_ipc4_timestamp_info *time_info, u64 value) { u64 dai_rate, host_rate; @@ -1102,10 +1107,10 @@ static u64 sof_ipc4_frames_dai_to_host(struct sof_ipc4_timestamp_info *time_info return value; } -static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - struct snd_sof_pcm_stream *sps, - struct sof_ipc4_timestamp_info *time_info) +int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, + struct sof_ipc4_timestamp_info *time_info) { struct sof_ipc4_copier *host_copier = time_info->host_copier; struct sof_ipc4_copier *dai_copier = time_info->dai_copier; @@ -1119,7 +1124,8 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_INVALID_NODE_ID) { return -EINVAL; - } else if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_CHAIN_DMA_NODE_ID) { + } else if (substream && + host_copier->data.gtw_cfg.node_id == SOF_IPC4_CHAIN_DMA_NODE_ID) { /* * While the firmware does not support time_info reporting for * streams using ChainDMA, it is granted that ChainDMA can only @@ -1175,7 +1181,7 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream, snd_pcm_uframes_t *pointer) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc4_timestamp_info *time_info; struct sof_ipc4_llp_reading_slot llp; @@ -1332,4 +1338,7 @@ const struct sof_ipc_pcm_ops ipc4_pcm_ops = { .delay = sof_ipc4_pcm_delay, .ipc_first_on_start = true, .platform_stop_during_hw_free = true, +#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) + .compress_ops = &sof_ipc4_compressed_ops, +#endif }; diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index a8cdf9bc750b4d..b35930c16c6dbe 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -75,6 +75,8 @@ struct sof_ipc4_fw_library { * @fw_context_save: Firmware supports full context save and restore * @libraries_restored: The libraries have been retained during firmware boot * + * @codec_info: Information about the available codecs in booted firmware. The + * data is to be used by the code for compressed support. * @load_library: Callback function for platform dependent library loading * @pipeline_state_mutex: Mutex to protect pipeline triggers, ref counts, states and deletion */ @@ -91,6 +93,8 @@ struct sof_ipc4_fw_data { bool fw_context_save; bool libraries_restored; + void *codec_info; + int (*load_library)(struct snd_sof_dev *sdev, struct sof_ipc4_fw_library *fw_lib, bool reload); void (*intel_configure_mic_privacy)(struct snd_sof_dev *sdev, @@ -98,11 +102,37 @@ struct sof_ipc4_fw_data { struct mutex pipeline_state_mutex; /* protect pipeline triggers, ref counts and states */ }; +/** + * struct sof_ipc4_timestamp_info - IPC4 timestamp info + * @host_copier: the host copier of the pcm stream + * @dai_copier: the dai copier of the pcm stream + * @stream_start_offset: reported by fw in memory window (converted to + * frames at host_copier sampling rate) + * @stream_end_offset: reported by fw in memory window (converted to + * frames at host_copier sampling rate) + * @llp_offset: llp offset in memory window + * @delay: Calculated and stored in pointer callback. The stored value is + * returned in the delay callback. Expressed in frames at host copier + * sampling rate. + */ +struct sof_ipc4_timestamp_info { + struct sof_ipc4_copier *host_copier; + struct sof_ipc4_copier *dai_copier; + u64 stream_start_offset; + u64 stream_end_offset; + u32 llp_offset; + + snd_pcm_sframes_t delay; +}; + extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops; extern const struct sof_ipc_tplg_ops ipc4_tplg_ops; extern const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops; extern const struct sof_ipc_pcm_ops ipc4_pcm_ops; extern const struct sof_ipc_fw_tracing_ops ipc4_mtrace_ops; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) +extern const struct snd_compress_ops sof_ipc4_compressed_ops; +#endif int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 instance_id, u32 state); int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev, int core); @@ -129,4 +159,19 @@ void sof_ipc4_mic_privacy_state_change(struct snd_sof_dev *sdev, bool state); enum sof_ipc4_pipeline_state; const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state); +struct sof_ipc4_timestamp_info *sof_ipc4_sps_to_time_info(struct snd_sof_pcm_stream *sps); +void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps); +int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, + struct sof_ipc4_timestamp_info *time_info); +u64 sof_ipc4_frames_dai_to_host(struct sof_ipc4_timestamp_info *time_info, u64 value); + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) +void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, void *ipc_message); +#else +static inline void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, + void *ipc_message) { } +#endif + #endif diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 2b12954b89b8fa..fe9dd298dfbbc6 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -221,9 +221,10 @@ static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = { struct snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_sof_dev *sdev, u32 module_id, int instance_id) { + struct snd_sof_audio_instance *instance; struct snd_sof_widget *swidget; - list_for_each_entry(swidget, &sdev->widget_list, list) { + for_each_swidget_in_instances(swidget, sdev, instance) { struct sof_ipc4_fw_module *fw_module = swidget->module_info; /* Only active module instances have valid instance_id */ @@ -406,6 +407,39 @@ sof_ipc4_get_input_pin_audio_fmt(struct snd_sof_widget *swidget, int pin_index) return NULL; } +static void +sof_ipc4_evaluate_params_change(struct sof_ipc4_available_audio_format *available_fmt) +{ + struct sof_ipc4_audio_format *fmt; + u32 in_rate, in_channels, in_valid_bits; + u32 out_rate, out_channels, out_valid_bits; + u32 changed_params = 0; + int i, j; + + for (i = 0; i < available_fmt->num_input_formats; i++) { + fmt = &available_fmt->input_pin_fmts[i].audio_fmt; + in_rate = fmt->sampling_frequency; + in_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); + in_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + + for (j = 0; j < available_fmt->num_output_formats; j++) { + fmt = &available_fmt->output_pin_fmts[j].audio_fmt; + out_rate = fmt->sampling_frequency; + out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); + out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + + if (in_rate != out_rate) + changed_params |= BIT(SNDRV_PCM_HW_PARAM_RATE); + if (in_channels != out_channels) + changed_params |= BIT(SNDRV_PCM_HW_PARAM_CHANNELS); + if (in_valid_bits != out_valid_bits) + changed_params |= BIT(SNDRV_PCM_HW_PARAM_FORMAT); + } + } + + available_fmt->changed_params = changed_params; +} + /** * sof_ipc4_get_audio_fmt - get available audio formats from swidget->tuples * @scomp: pointer to pointer to SOC component @@ -497,6 +531,8 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, available_fmt->num_output_formats); } + sof_ipc4_evaluate_params_change(available_fmt); + return 0; err_out: @@ -525,14 +561,14 @@ static void sof_ipc4_widget_free_comp_pipeline(struct snd_sof_widget *swidget) static int sof_ipc4_widget_set_module_info(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); swidget->module_info = sof_ipc4_find_module_by_uuid(sdev, &swidget->uuid); if (swidget->module_info) return 0; - dev_err(sdev->dev, "failed to find module info for widget %s with UUID %pUL\n", + dev_err(scomp->dev, "failed to find module info for widget %s with UUID %pUL\n", swidget->widget->name, &swidget->uuid); return -EINVAL; } @@ -575,12 +611,12 @@ static int sof_ipc4_widget_setup_msg(struct snd_sof_widget *swidget, struct sof_ static void sof_ipc4_widget_update_kcontrol_module_id(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct sof_ipc4_fw_module *fw_module = swidget->module_info; struct snd_sof_control *scontrol; /* update module ID for all kcontrols for this widget */ - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + list_for_each_entry(scontrol, &instance->kcontrol_list, list) { if (scontrol->comp_id == swidget->comp_id) { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct sof_ipc4_msg *msg = &cdata->msg; @@ -661,6 +697,9 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) if (ret) goto free_copier; + /* Copier can only change format */ + available_fmt->changed_params &= BIT(SNDRV_PCM_HW_PARAM_FORMAT); + /* * This callback is used by host copier and module-to-module copier, * and only host copier needs to set gtw_cfg. @@ -706,6 +745,9 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) sps->dsp_max_burst_size_in_ms = 1; } + if (spcm->pcm.compress) + ipc4_copier->data.copier_feature_mask |= BIT(SOF_IPC4_COPIER_FAST_MODE); + skip_gtw_cfg: ipc4_copier->gtw_attr = kzalloc_obj(*ipc4_copier->gtw_attr); if (!ipc4_copier->gtw_attr) { @@ -768,7 +810,8 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) { struct sof_ipc4_available_audio_format *available_fmt; struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_dai *dai = swidget->private; struct sof_ipc4_copier *ipc4_copier; struct snd_sof_widget *pipe_widget; @@ -789,6 +832,9 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) if (ret) goto free_copier; + /* Copier can only change format */ + available_fmt->changed_params &= BIT(SNDRV_PCM_HW_PARAM_FORMAT); + ret = sof_update_ipc_object(scomp, &node_type, SOF_COPIER_TOKENS, swidget->tuples, swidget->num_tuples, sizeof(node_type), 1); @@ -849,7 +895,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) goto free_available_fmt; } - list_for_each_entry(w, &sdev->widget_list, list) { + list_for_each_entry(w, &instance->widget_list, list) { struct snd_sof_dai *alh_dai; if (!WIDGET_IS_DAI(w->id) || !w->widget->sname || @@ -945,7 +991,7 @@ static void sof_ipc4_widget_free_comp_dai(struct snd_sof_widget *swidget) static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc4_pipeline *pipeline; struct snd_sof_pipeline *spipe = swidget->spipe; int ret; @@ -1336,9 +1382,11 @@ static void sof_ipc4_widget_free_comp_process(struct snd_sof_widget *swidget) } static void -sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, +sof_ipc4_update_resource_usage(struct snd_sof_widget *swidget, struct sof_ipc4_base_module_cfg *base_config) { + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc4_fw_module *fw_module = swidget->module_info; struct snd_sof_widget *pipe_widget; struct sof_ipc4_pipeline *pipeline; @@ -1373,26 +1421,26 @@ sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget * sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config); if (ignore_cpc) { - dev_dbg(sdev->dev, "%s: ibs / obs: %u / %u, forcing cpc to 0 from %u\n", + dev_dbg(scomp->dev, "%s: ibs / obs: %u / %u, forcing cpc to 0 from %u\n", swidget->widget->name, base_config->ibs, base_config->obs, base_config->cpc); base_config->cpc = 0; } else { - dev_dbg(sdev->dev, "%s: ibs / obs / cpc: %u / %u / %u\n", + dev_dbg(scomp->dev, "%s: ibs / obs / cpc: %u / %u / %u\n", swidget->widget->name, base_config->ibs, base_config->obs, base_config->cpc); } } -static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget) +static int sof_ipc4_widget_assign_instance_id(struct snd_sof_widget *swidget) { + struct snd_soc_component *scomp = swidget->scomp; struct sof_ipc4_fw_module *fw_module = swidget->module_info; int max_instances = fw_module->man4_module_entry.instance_max_count; swidget->instance_id = ida_alloc_max(&fw_module->m_ida, max_instances, GFP_KERNEL); if (swidget->instance_id < 0) { - dev_err(sdev->dev, "failed to assign instance id for widget %s", + dev_err(scomp->dev, "failed to assign instance id for widget %s", swidget->widget->name); return swidget->instance_id; } @@ -1401,7 +1449,8 @@ static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev, } /* update hw_params based on the audio stream format */ -static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params, +static int sof_ipc4_update_hw_params(struct snd_soc_component *scomp, + struct snd_pcm_hw_params *params, struct sof_ipc4_audio_format *fmt, u32 param_to_update) { struct snd_interval *i; @@ -1425,7 +1474,7 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw snd_fmt = SNDRV_PCM_FORMAT_U8; break; default: - dev_err(sdev->dev, "Unsupported PCM 8-bit IPC4 type %d\n", type); + dev_err(scomp->dev, "Unsupported PCM 8-bit IPC4 type %d\n", type); return -EINVAL; } break; @@ -1444,12 +1493,12 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw snd_fmt = SNDRV_PCM_FORMAT_FLOAT_LE; break; default: - dev_err(sdev->dev, "Unsupported PCM 32-bit IPC4 type %d\n", type); + dev_err(scomp->dev, "Unsupported PCM 32-bit IPC4 type %d\n", type); return -EINVAL; } break; default: - dev_err(sdev->dev, "invalid PCM valid_bits %d\n", valid_bits); + dev_err(scomp->dev, "invalid PCM valid_bits %d\n", valid_bits); return -EINVAL; } @@ -1477,8 +1526,8 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw return 0; } -static bool sof_ipc4_is_single_format(struct snd_sof_dev *sdev, - struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size) +static bool sof_ipc4_is_single_format(struct sof_ipc4_pin_format *pin_fmts, + u32 pin_fmts_size) { struct sof_ipc4_audio_format *fmt; u32 rate, channels, valid_bits; @@ -1505,25 +1554,25 @@ static bool sof_ipc4_is_single_format(struct snd_sof_dev *sdev, return true; } -static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget, +static int sof_ipc4_init_output_audio_fmt(struct snd_sof_widget *swidget, struct sof_ipc4_base_module_cfg *base_config, struct sof_ipc4_available_audio_format *available_fmt, u32 out_ref_rate, u32 out_ref_channels, u32 out_ref_valid_bits, u32 out_ref_type) { + struct snd_soc_component *scomp = swidget->scomp; struct sof_ipc4_pin_format *pin_fmts = available_fmt->output_pin_fmts; u32 pin_fmts_size = available_fmt->num_output_formats; bool single_format; int i = 0; if (!pin_fmts_size) { - dev_err(sdev->dev, "no output formats for %s\n", + dev_err(scomp->dev, "no output formats for %s\n", swidget->widget->name); return -EINVAL; } - single_format = sof_ipc4_is_single_format(sdev, pin_fmts, pin_fmts_size); + single_format = sof_ipc4_is_single_format(pin_fmts, pin_fmts_size); /* pick the first format if there's only one available or if all formats are the same */ if (single_format) @@ -1548,7 +1597,7 @@ static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev, goto out_fmt; } - dev_err(sdev->dev, + dev_err(scomp->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels, type: %d\n", __func__, out_ref_rate, out_ref_valid_bits, out_ref_channels, out_ref_type); @@ -1561,7 +1610,8 @@ static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev, return i; } -static int sof_ipc4_get_valid_bits(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params) +static int sof_ipc4_get_valid_bits(struct snd_soc_component *scomp, + struct snd_pcm_hw_params *params) { switch (params_format(params)) { case SNDRV_PCM_FORMAT_U8: @@ -1577,12 +1627,13 @@ static int sof_ipc4_get_valid_bits(struct snd_sof_dev *sdev, struct snd_pcm_hw_p case SNDRV_PCM_FORMAT_FLOAT_LE: return 32; default: - dev_err(sdev->dev, "invalid pcm frame format %d\n", params_format(params)); + dev_err(scomp->dev, "invalid pcm frame format %d\n", params_format(params)); return -EINVAL; } } -static int sof_ipc4_get_sample_type(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params) +static int sof_ipc4_get_sample_type(struct snd_soc_component *scomp, + struct snd_pcm_hw_params *params) { switch (params_format(params)) { case SNDRV_PCM_FORMAT_A_LAW: @@ -1599,42 +1650,37 @@ static int sof_ipc4_get_sample_type(struct snd_sof_dev *sdev, struct snd_pcm_hw_ case SNDRV_PCM_FORMAT_FLOAT_LE: return SOF_IPC4_TYPE_FLOAT; default: - dev_err(sdev->dev, "invalid pcm sample type %d\n", params_format(params)); + dev_err(scomp->dev, "invalid pcm sample type %d\n", params_format(params)); return -EINVAL; } } -static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget, +static int sof_ipc4_init_input_audio_fmt(struct snd_sof_widget *swidget, struct sof_ipc4_base_module_cfg *base_config, struct snd_pcm_hw_params *params, struct sof_ipc4_available_audio_format *available_fmt) { + struct snd_soc_component *scomp = swidget->scomp; struct sof_ipc4_pin_format *pin_fmts = available_fmt->input_pin_fmts; u32 pin_fmts_size = available_fmt->num_input_formats; u32 valid_bits; u32 channels; u32 rate; u32 type; - bool single_format; int sample_valid_bits; int sample_type; int i = 0; if (!pin_fmts_size) { - dev_err(sdev->dev, "no input formats for %s\n", swidget->widget->name); + dev_err(scomp->dev, "no input formats for %s\n", swidget->widget->name); return -EINVAL; } - single_format = sof_ipc4_is_single_format(sdev, pin_fmts, pin_fmts_size); - if (single_format) - goto in_fmt; - - sample_valid_bits = sof_ipc4_get_valid_bits(sdev, params); + sample_valid_bits = sof_ipc4_get_valid_bits(scomp, params); if (sample_valid_bits < 0) return sample_valid_bits; - sample_type = sof_ipc4_get_sample_type(sdev, params); + sample_type = sof_ipc4_get_sample_type(scomp, params); if (sample_type < 0) return sample_type; @@ -1654,16 +1700,15 @@ static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev, type = sof_ipc4_fmt_cfg_to_type(fmt->fmt_cfg); if (params_rate(params) == rate && params_channels(params) == channels && sample_valid_bits == valid_bits && sample_type == type) - break; + goto in_fmt; } - if (i == pin_fmts_size) { - dev_err(sdev->dev, - "%s: Unsupported audio format: %uHz, %ubit, %u channels, type: %d\n", - __func__, params_rate(params), sample_valid_bits, - params_channels(params), sample_type); - return -EINVAL; - } + dev_err(scomp->dev, + "%s: Unsupported audio format: %uHz, %ubit, %u channels, type: %d\n", + __func__, params_rate(params), sample_valid_bits, + params_channels(params), sample_type); + + return -EINVAL; in_fmt: /* copy input format */ @@ -1733,6 +1778,7 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) static int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int *sample_rate, int *channel_count, int *bit_depth) { + struct snd_sof_audio_instance *instance; struct snd_soc_tplg_hw_config *hw_config; struct snd_sof_dai_link *slink; bool dai_link_found = false; @@ -1740,7 +1786,7 @@ static int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, struct snd_sof int i; /* get current hw_config from link */ - list_for_each_entry(slink, &sdev->dai_link_list, list) { + for_each_slink_in_instances(slink, sdev, instance) { if (!strcmp(slink->link->name, dai->name)) { dai_link_found = true; break; @@ -1823,6 +1869,12 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai dai_index); if (dev_type < 0) return dev_type; + + if (params_width(params) != bit_depth) { + format_change = true; + dev_dbg(sdev->dev, "SSP sample width change from %d to %d\n", + params_width(params), bit_depth); + } break; default: return 0; @@ -1945,7 +1997,7 @@ bool sof_ipc4_copier_is_single_bitdepth(struct snd_sof_dev *sdev, } static int -sof_ipc4_adjust_params_to_dai_format(struct snd_sof_dev *sdev, +sof_ipc4_adjust_params_to_dai_format(struct snd_soc_component *scomp, struct snd_pcm_hw_params *params, struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size) @@ -1986,7 +2038,7 @@ sof_ipc4_adjust_params_to_dai_format(struct snd_sof_dev *sdev, } if (params_mask) - return sof_ipc4_update_hw_params(sdev, params, + return sof_ipc4_update_hw_params(scomp, params, &pin_fmts[0].audio_fmt, params_mask); @@ -2023,7 +2075,7 @@ sof_ipc4_prepare_dai_copier(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, num_pin_fmts = available_fmt->num_input_formats; } - ret = sof_ipc4_adjust_params_to_dai_format(sdev, &dai_params, pin_fmts, + ret = sof_ipc4_adjust_params_to_dai_format(dai->scomp, &dai_params, pin_fmts, num_pin_fmts); if (ret) return ret; @@ -2043,15 +2095,21 @@ sof_ipc4_prepare_dai_copier(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, return ret; } -static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, +static void sof_ipc4_host_config(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget, struct snd_sof_platform_stream_params *platform_params) { + struct snd_soc_component *scomp = swidget->scomp; struct sof_ipc4_copier *ipc4_copier = (struct sof_ipc4_copier *)swidget->private; struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; u32 host_dma_id = platform_params->stream_tag - 1; + dev_dbg(scomp->dev, "Host copier %s, type %d, ChainDMA: %s, stream_tag: %d\n", + swidget->widget->name, swidget->id, str_yes_no(pipeline->use_chain_dma), + platform_params->stream_tag); + if (pipeline->use_chain_dma) { pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK; pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id); @@ -2063,14 +2121,61 @@ static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget } static int -sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, - struct snd_pcm_hw_params *fe_params, - struct snd_sof_platform_stream_params *platform_params, - struct snd_pcm_hw_params *pipeline_params, int dir) +sof_ipc4_copier_module_update_params(struct snd_sof_widget *swidget, + struct snd_pcm_hw_params *pipeline_params) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc4_copier_data *copier_data; + struct sof_ipc4_copier *ipc4_copier; + + switch (swidget->id) { + case snd_soc_dapm_aif_in: + case snd_soc_dapm_aif_out: + case snd_soc_dapm_buffer: + ipc4_copier = swidget->private; + copier_data = &ipc4_copier->data; + break; + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + { + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + struct snd_sof_dai *dai; + + if (pipeline->use_chain_dma) + return 0; + + dai = swidget->private; + + ipc4_copier = (struct sof_ipc4_copier *)dai->private; + copier_data = &ipc4_copier->data; + + break; + } + default: + dev_err(scomp->dev, "unsupported type %d for copier %s", + swidget->id, swidget->widget->name); + return -EINVAL; + } + + /* modify the input params for the next widget */ + return sof_ipc4_update_hw_params(scomp, pipeline_params, + &copier_data->out_format, + BIT(SNDRV_PCM_HW_PARAM_FORMAT) | + BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | + BIT(SNDRV_PCM_HW_PARAM_RATE)); +} + +static int +_sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, + struct snd_pcm_hw_params *pipeline_params, int dir) { struct sof_ipc4_available_audio_format *available_fmt; struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc4_copier_data *copier_data; int input_fmt_index, output_fmt_index; struct sof_ipc4_copier *ipc4_copier; @@ -2095,7 +2200,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct sof_ipc4_pipeline *pipeline = pipe_widget->private; struct sof_ipc4_gtw_attributes *gtw_attr; - dev_dbg(sdev->dev, + dev_dbg(scomp->dev, "Host copier %s, type %d, ChainDMA: %s, stream_tag: %d\n", swidget->widget->name, swidget->id, str_yes_no(pipeline->use_chain_dma), @@ -2174,7 +2279,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - dev_dbg(sdev->dev, "Dai copier %s, type %d, ChainDMA: %s\n", + dev_dbg(scomp->dev, "Dai copier %s, type %d, ChainDMA: %s\n", swidget->widget->name, swidget->id, str_yes_no(pipeline->use_chain_dma)); @@ -2187,34 +2292,38 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, copier_data = &ipc4_copier->data; available_fmt = &ipc4_copier->available_fmt; - /* - * Use the fe_params as a base for the copier configuration. - * The ref_params might get updated to reflect what format is - * supported by the copier on the DAI side. - * - * In case of capture the ref_params returned will be used to - * find the input configuration of the copier. - */ - ref_params = kmemdup(fe_params, sizeof(*ref_params), GFP_KERNEL); - if (!ref_params) - return -ENOMEM; - - ret = sof_ipc4_prepare_dai_copier(sdev, dai, ref_params, dir); - if (ret < 0) - return ret; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + /* + * For playback the pipeline_params needs to be used to + * find the input configuration of the copier. + */ + ref_params = kmemdup(pipeline_params, sizeof(*ref_params), + GFP_KERNEL); + if (!ref_params) + return -ENOMEM; + } else { + /* + * For capture the adjusted fe_params needs to be used + * to find the input configuration of the copier. + * + * The params might be updated in + * sof_ipc4_prepare_dai_copier() to reflect the supported + * input formats by the copier/dai. + */ + ref_params = kmemdup(fe_params, sizeof(*ref_params), GFP_KERNEL); + if (!ref_params) + return -ENOMEM; - /* - * For playback the pipeline_params needs to be used to find the - * input configuration of the copier. - */ - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - memcpy(ref_params, pipeline_params, sizeof(*ref_params)); + ret = sof_ipc4_prepare_dai_copier(sdev, dai, ref_params, dir); + if (ret < 0) + return ret; + } break; } case snd_soc_dapm_buffer: { - dev_dbg(sdev->dev, "Module copier %s, type %d\n", + dev_dbg(scomp->dev, "Module copier %s, type %d\n", swidget->widget->name, swidget->id); ipc4_copier = (struct sof_ipc4_copier *)swidget->private; @@ -2228,13 +2337,13 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, break; } default: - dev_err(sdev->dev, "unsupported type %d for copier %s", + dev_err(scomp->dev, "unsupported type %d for copier %s", swidget->id, swidget->widget->name); return -EINVAL; } /* set input and output audio formats */ - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + input_fmt_index = sof_ipc4_init_input_audio_fmt(swidget, &copier_data->base_config, ref_params, available_fmt); if (input_fmt_index < 0) @@ -2263,15 +2372,33 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, } case snd_soc_dapm_aif_out: case snd_soc_dapm_dai_in: - out_ref_rate = params_rate(fe_params); - out_ref_channels = params_channels(fe_params); - ret = sof_ipc4_get_sample_type(sdev, fe_params); + /* + * For capture the fe_params needs to be used to find the output + * configuration of the copier. + * + * For playback the adjusted fe_params needs to be used + * to find the output configuration of the copier. + * + * The params might be updated in + * sof_ipc4_prepare_dai_copier() to reflect the supported + * output formats by the copier/dai. + */ + memcpy(ref_params, fe_params, sizeof(*ref_params)); + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + ret = sof_ipc4_prepare_dai_copier(sdev, dai, ref_params, dir); + if (ret < 0) + return ret; + } + + out_ref_rate = params_rate(ref_params); + out_ref_channels = params_channels(ref_params); + ret = sof_ipc4_get_sample_type(scomp, ref_params); if (ret < 0) return ret; out_ref_type = (u32)ret; if (!single_output_bitdepth) { - out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); + out_ref_valid_bits = sof_ipc4_get_valid_bits(scomp, ref_params); if (out_ref_valid_bits < 0) return out_ref_valid_bits; } @@ -2297,7 +2424,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, out_ref_type = sof_ipc4_fmt_cfg_to_type(out_fmt->fmt_cfg); } - output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, + output_fmt_index = sof_ipc4_init_output_audio_fmt(swidget, &copier_data->base_config, available_fmt, out_ref_rate, out_ref_channels, out_ref_valid_bits, @@ -2368,7 +2495,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, * for all widgets with the same stream name */ i = 0; - list_for_each_entry(w, &sdev->widget_list, list) { + list_for_each_entry(w, &instance->widget_list, list) { u32 node_type; if (!WIDGET_IS_DAI(w->id) || !w->widget->sname || @@ -2431,7 +2558,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, } /* modify the input params for the next widget */ - ret = sof_ipc4_update_hw_params(sdev, pipeline_params, + ret = sof_ipc4_update_hw_params(scomp, pipeline_params, &copier_data->out_format, BIT(SNDRV_PCM_HW_PARAM_FORMAT) | BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | @@ -2453,7 +2580,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, copier_data->gtw_cfg.dma_buffer_size = max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms) * copier_data->base_config.ibs; - dev_dbg(sdev->dev, "copier %s, dma buffer%s: %u ms (%u bytes)", + dev_dbg(scomp->dev, "copier %s, dma buffer%s: %u ms (%u bytes)", swidget->widget->name, deep_buffer_dma_ms ? " (using Deep Buffer)" : "", max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms), @@ -2467,7 +2594,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, copier_data->gtw_cfg.dma_buffer_size = max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms) * copier_data->base_config.obs; - dev_dbg(sdev->dev, "copier %s, dma buffer%s: %u ms (%u bytes)", + dev_dbg(scomp->dev, "copier %s, dma buffer%s: %u ms (%u bytes)", swidget->widget->name, deep_buffer_dma_ms ? " (using Deep Buffer)" : "", max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms), @@ -2503,7 +2630,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, copier_data->gtw_cfg.config_length += dma_config_tlv_size / 4; } - dev_dbg(sdev->dev, "copier %s, IPC size is %d", swidget->widget->name, ipc_size); + dev_dbg(scomp->dev, "copier %s, IPC size is %d", swidget->widget->name, ipc_size); *ipc_config_data = kzalloc(ipc_size, GFP_KERNEL); if (!*ipc_config_data) @@ -2511,11 +2638,11 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, *ipc_config_size = ipc_size; - sof_ipc4_dbg_module_audio_format(sdev->dev, swidget, available_fmt, + sof_ipc4_dbg_module_audio_format(scomp->dev, swidget, available_fmt, input_fmt_index, output_fmt_index); /* update pipeline memory usage */ - sof_ipc4_update_resource_usage(sdev, swidget, &copier_data->base_config); + sof_ipc4_update_resource_usage(swidget, &copier_data->base_config); /* copy IPC data */ memcpy(*ipc_config_data, (void *)copier_data, sizeof(*copier_data)); @@ -2538,20 +2665,38 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, return 0; } +static int +sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, + struct snd_pcm_hw_params *pipeline_params, int dir) +{ + if (swidget->prepared) + return sof_ipc4_copier_module_update_params(swidget, + pipeline_params); + + return _sof_ipc4_prepare_copier_module(swidget, fe_params, + platform_params, pipeline_params, + dir); +} + static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *fe_params, struct snd_sof_platform_stream_params *platform_params, struct snd_pcm_hw_params *pipeline_params, int dir) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_gain *gain = swidget->private; struct sof_ipc4_available_audio_format *available_fmt = &gain->available_fmt; struct sof_ipc4_audio_format *in_fmt; u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; int input_fmt_index, output_fmt_index; - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + /* This cannot happen */ + if (unlikely(swidget->prepared)) + return 0; + + input_fmt_index = sof_ipc4_init_input_audio_fmt(swidget, &gain->data.base_config, pipeline_params, available_fmt); @@ -2564,7 +2709,7 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); - output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, + output_fmt_index = sof_ipc4_init_output_audio_fmt(swidget, &gain->data.base_config, available_fmt, out_ref_rate, @@ -2574,11 +2719,11 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, if (output_fmt_index < 0) return output_fmt_index; - sof_ipc4_dbg_module_audio_format(sdev->dev, swidget, available_fmt, + sof_ipc4_dbg_module_audio_format(scomp->dev, swidget, available_fmt, input_fmt_index, output_fmt_index); /* update pipeline memory usage */ - sof_ipc4_update_resource_usage(sdev, swidget, &gain->data.base_config); + sof_ipc4_update_resource_usage(swidget, &gain->data.base_config); return 0; } @@ -2589,14 +2734,17 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *pipeline_params, int dir) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_mixer *mixer = swidget->private; struct sof_ipc4_available_audio_format *available_fmt = &mixer->available_fmt; struct sof_ipc4_audio_format *in_fmt; u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; int input_fmt_index, output_fmt_index; - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + /* Already prepared, nothing to do */ + if (swidget->prepared) + return 0; + + input_fmt_index = sof_ipc4_init_input_audio_fmt(swidget, &mixer->base_config, pipeline_params, available_fmt); @@ -2609,7 +2757,7 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); - output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, + output_fmt_index = sof_ipc4_init_output_audio_fmt(swidget, &mixer->base_config, available_fmt, out_ref_rate, @@ -2619,11 +2767,11 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, if (output_fmt_index < 0) return output_fmt_index; - sof_ipc4_dbg_module_audio_format(sdev->dev, swidget, available_fmt, + sof_ipc4_dbg_module_audio_format(scomp->dev, swidget, available_fmt, input_fmt_index, output_fmt_index); /* update pipeline memory usage */ - sof_ipc4_update_resource_usage(sdev, swidget, &mixer->base_config); + sof_ipc4_update_resource_usage(swidget, &mixer->base_config); return 0; } @@ -2634,7 +2782,6 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *pipeline_params, int dir) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_src *src = swidget->private; struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt; struct sof_ipc4_audio_format *out_audio_fmt; @@ -2642,7 +2789,11 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; int output_fmt_index, input_fmt_index; - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + /* This cannot happen */ + if (unlikely(swidget->prepared)) + return 0; + + input_fmt_index = sof_ipc4_init_input_audio_fmt(swidget, &src->data.base_config, pipeline_params, available_fmt); @@ -2681,7 +2832,7 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, for (i = 1; i < available_fmt->num_output_formats; i++) { out_audio_fmt = &available_fmt->output_pin_fmts[i].audio_fmt; if (out_ref_rate != out_audio_fmt->sampling_frequency) { - dev_err(sdev->dev, + dev_err(scomp->dev, "Cannot determine the output rate for SRC: %s\n", swidget->widget->name); return -EINVAL; @@ -2689,7 +2840,7 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, } } - output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, + output_fmt_index = sof_ipc4_init_output_audio_fmt(swidget, &src->data.base_config, available_fmt, out_ref_rate, @@ -2699,17 +2850,17 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, if (output_fmt_index < 0) return output_fmt_index; - sof_ipc4_dbg_module_audio_format(sdev->dev, swidget, available_fmt, + sof_ipc4_dbg_module_audio_format(scomp->dev, swidget, available_fmt, input_fmt_index, output_fmt_index); /* update pipeline memory usage */ - sof_ipc4_update_resource_usage(sdev, swidget, &src->data.base_config); + sof_ipc4_update_resource_usage(swidget, &src->data.base_config); out_audio_fmt = &available_fmt->output_pin_fmts[output_fmt_index].audio_fmt; src->data.sink_rate = out_audio_fmt->sampling_frequency; /* update pipeline_params for sink widgets */ - return sof_ipc4_update_hw_params(sdev, pipeline_params, out_audio_fmt, + return sof_ipc4_update_hw_params(scomp, pipeline_params, out_audio_fmt, BIT(SNDRV_PCM_HW_PARAM_FORMAT) | BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | BIT(SNDRV_PCM_HW_PARAM_RATE)); @@ -2799,7 +2950,6 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *pipeline_params, int dir) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_process *process = swidget->private; struct sof_ipc4_available_audio_format *available_fmt = &process->available_fmt; void *cfg = process->ipc_config_data; @@ -2808,7 +2958,19 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, int ret; if (available_fmt->num_input_formats) { - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + if (swidget->prepared) { + if (!available_fmt->num_output_formats) + return 0; + + /* modify the pipeline params with the output format */ + return sof_ipc4_update_hw_params(scomp, pipeline_params, + &process->output_format, + BIT(SNDRV_PCM_HW_PARAM_FORMAT) | + BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | + BIT(SNDRV_PCM_HW_PARAM_RATE)); + } + + input_fmt_index = sof_ipc4_init_input_audio_fmt(swidget, &process->base_config, pipeline_params, available_fmt); @@ -2820,39 +2982,69 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, if (available_fmt->num_output_formats) { struct sof_ipc4_audio_format *in_fmt; struct sof_ipc4_pin_format *pin_fmt; - u32 out_ref_rate, out_ref_channels; - int out_ref_valid_bits, out_ref_type; + u32 ref_rate, ref_channels; + int ref_valid_bits, ref_type; if (available_fmt->num_input_formats) { + /* + * The process module can change parameters and their operation + * depends on the direction: + * Playback: typically they have single output format. This is + * to 'force' the conversion from input to output. + * Use the input format as reference since the single + * format is going to be picked. + * Capture: typically they have multiple output formats to + * convert from dai (input) to FE (output) parameters. + * Use the input format as base and replace the param + * which is changed by the module with the FE parameter + * Reason: we can have module which changes the + * parameters in path, we cannot use the full + * FE param set for the module output lookup. + */ in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt; - out_ref_rate = in_fmt->sampling_frequency; - out_ref_channels = + ref_rate = in_fmt->sampling_frequency; + ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); - out_ref_valid_bits = + ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); - out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); + ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); } else { /* for modules without input formats, use FE params as reference */ - out_ref_rate = params_rate(fe_params); - out_ref_channels = params_channels(fe_params); - ret = sof_ipc4_get_sample_type(sdev, fe_params); + ref_rate = params_rate(fe_params); + ref_channels = params_channels(fe_params); + ret = sof_ipc4_get_sample_type(scomp, fe_params); if (ret < 0) return ret; - out_ref_type = (u32)ret; + ref_type = (u32)ret; - out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); - if (out_ref_valid_bits < 0) - return out_ref_valid_bits; + ref_valid_bits = sof_ipc4_get_valid_bits(scomp, fe_params); + if (ref_valid_bits < 0) + return ref_valid_bits; } - output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, + if (dir == SNDRV_PCM_STREAM_CAPTURE) { + if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_RATE)) + ref_rate = params_rate(fe_params); + if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_CHANNELS)) + ref_channels = params_channels(fe_params); + if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) { + ref_valid_bits = sof_ipc4_get_valid_bits(scomp, fe_params); + if (ref_valid_bits < 0) + return ref_valid_bits; + + ref_type = sof_ipc4_get_sample_type(scomp, fe_params); + if (ref_type < 0) + return ref_type; + } + } + output_fmt_index = sof_ipc4_init_output_audio_fmt(swidget, &process->base_config, available_fmt, - out_ref_rate, - out_ref_channels, - out_ref_valid_bits, - out_ref_type); + ref_rate, + ref_channels, + ref_valid_bits, + ref_type); if (output_fmt_index < 0) return output_fmt_index; @@ -2864,11 +3056,9 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, sizeof(struct sof_ipc4_audio_format)); /* modify the pipeline params with the output format */ - ret = sof_ipc4_update_hw_params(sdev, pipeline_params, + ret = sof_ipc4_update_hw_params(scomp, pipeline_params, &process->output_format, - BIT(SNDRV_PCM_HW_PARAM_FORMAT) | - BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | - BIT(SNDRV_PCM_HW_PARAM_RATE)); + available_fmt->changed_params); if (ret) return ret; } @@ -2884,11 +3074,11 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, } } - sof_ipc4_dbg_module_audio_format(sdev->dev, swidget, available_fmt, + sof_ipc4_dbg_module_audio_format(scomp->dev, swidget, available_fmt, input_fmt_index, output_fmt_index); /* update pipeline memory usage */ - sof_ipc4_update_resource_usage(sdev, swidget, &process->base_config); + sof_ipc4_update_resource_usage(swidget, &process->base_config); /* ipc_config_data is composed of the base_config followed by an optional extension */ memcpy(cfg, &process->base_config, sizeof(struct sof_ipc4_base_module_cfg)); @@ -2979,8 +3169,9 @@ static int sof_ipc4_control_load_enum(struct snd_sof_dev *sdev, struct snd_sof_c return 0; } -static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +static int sof_ipc4_control_load_bytes(struct snd_sof_control *scontrol) { + struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc4_control_data *control_data; struct sof_ipc4_msg *msg; int ret; @@ -2991,21 +3182,21 @@ static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ * 'struct sof_ipc4_control_data' */ if (scontrol->max_size < sizeof(struct sof_abi_hdr)) { - dev_err(sdev->dev, + dev_err(scomp->dev, "insufficient maximum size for a bytes control %s: %zu.\n", scontrol->name, scontrol->max_size); return -EINVAL; } if (scontrol->priv_size > scontrol->max_size) { - dev_err(sdev->dev, + dev_err(scomp->dev, "bytes control %s initial data size %zu exceeds max %zu.\n", scontrol->name, scontrol->priv_size, scontrol->max_size); return -EINVAL; } if (scontrol->priv_size && scontrol->priv_size < sizeof(struct sof_abi_hdr)) { - dev_err(sdev->dev, + dev_err(scomp->dev, "bytes control %s initial data size %zu is insufficient.\n", scontrol->name, scontrol->priv_size); return -EINVAL; @@ -3031,7 +3222,7 @@ static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ scontrol->priv = NULL; if (control_data->data->magic != SOF_IPC4_ABI_MAGIC) { - dev_err(sdev->dev, "Wrong ABI magic (%#x) for control: %s\n", + dev_err(scomp->dev, "Wrong ABI magic (%#x) for control: %s\n", control_data->data->magic, scontrol->name); ret = -EINVAL; goto err; @@ -3041,7 +3232,7 @@ static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ if (control_data->data->size + sizeof(struct sof_abi_hdr) != scontrol->priv_size) { - dev_err(sdev->dev, "Control %s conflict in bytes %zu vs. priv size %zu.\n", + dev_err(scomp->dev, "Control %s conflict in bytes %zu vs. priv size %zu.\n", scontrol->name, control_data->data->size + sizeof(struct sof_abi_hdr), scontrol->priv_size); @@ -3072,7 +3263,7 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr case SND_SOC_TPLG_CTL_VOLSW_XR_SX: return sof_ipc4_control_load_volume(sdev, scontrol); case SND_SOC_TPLG_CTL_BYTES: - return sof_ipc4_control_load_bytes(sdev, scontrol); + return sof_ipc4_control_load_bytes(scontrol); case SND_SOC_TPLG_CTL_ENUM: case SND_SOC_TPLG_CTL_ENUM_VALUE: return sof_ipc4_control_load_enum(sdev, scontrol); @@ -3083,28 +3274,50 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr return 0; } +static void +sof_ipc4_add_init_ext_module_data(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget, + u32 *payload, u32 *ext_pos, + struct sof_ipc4_module_init_ext_object **hdr) +{ + struct sof_ipc4_process *process = swidget->private; + u32 data_size; + void *data; + + /* + * Check if widget is process module and if it is using + * init_ext_module_data + */ + if (!WIDGET_IS_PROCESS(swidget->id) || !process->init_ext_module_size) + return; + + data_size = process->init_ext_module_size; + data = process->init_ext_module_data; + + *hdr = (struct sof_ipc4_module_init_ext_object *)&payload[*ext_pos]; + (*hdr)->header = SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_MODULE_DATA) | + SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(data_size, sizeof(u32))); + *ext_pos += DIV_ROUND_UP(sizeof(*(*hdr)), sizeof(u32)); + + memcpy(&payload[*ext_pos], data, data_size); + + *ext_pos += DIV_ROUND_UP(data_size, sizeof(u32)); +} + static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, struct sof_ipc4_msg *msg, void *ipc_data, u32 ipc_size, void **new_data) { + struct snd_soc_component *scomp = swidget->scomp; struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data; struct sof_ipc4_module_init_ext_init *ext_init; - struct sof_ipc4_module_init_ext_object *hdr; + struct sof_ipc4_module_init_ext_object *hdr = NULL; int new_size; u32 *payload; u32 ext_pos; - /* For the moment the only reason for adding init_ext_init payload is DP - * memory data. If both stack and heap size are 0 (= use default), then - * there is no need for init_ext_init payload. - */ - if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) { - msg->extension &= ~SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK; - return 0; - } - payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL); if (!payload) return -ENOMEM; @@ -3119,7 +3332,7 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, /* Add dp_memory_data if comp_domain indicates DP */ if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) { hdr = (struct sof_ipc4_module_init_ext_object *)&payload[ext_pos]; - hdr->header = SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK | + hdr->header = SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) | SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32))); @@ -3131,12 +3344,20 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32)); } - /* If another array object is added, remember clear previous OBJ_LAST bit */ + sof_ipc4_add_init_ext_module_data(sdev, swidget, payload, &ext_pos, &hdr); + + /* Set last bit for the last object in the array */ + if (hdr) { + hdr->header |= SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK; + } else { + kfree(payload); + return 0; + } /* Calculate final size and check that it fits to max payload size */ new_size = ext_pos * sizeof(u32) + ipc_size; if (new_size > sdev->ipc->max_payload_size) { - dev_err(sdev->dev, "Max ipc payload size %zu exceeded: %u", + dev_err(scomp->dev, "Max ipc payload size %zu exceeded: %u", sdev->ipc->max_payload_size, new_size); kfree(payload); return -EINVAL; @@ -3156,6 +3377,7 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { + struct snd_soc_component *scomp = swidget->scomp; struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct sof_ipc4_pipeline *pipeline; @@ -3170,12 +3392,12 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget pipeline = swidget->private; if (pipeline->use_chain_dma) { - dev_warn(sdev->dev, "use_chain_dma set for scheduler %s", + dev_warn(scomp->dev, "use_chain_dma set for scheduler %s", swidget->widget->name); return 0; } - dev_dbg(sdev->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id, + dev_dbg(scomp->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id, pipeline->mem_usage); msg = &pipeline->msg; @@ -3184,7 +3406,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget swidget->instance_id = ida_alloc_max(&pipeline_ida, ipc4_data->max_num_pipelines, GFP_KERNEL); if (swidget->instance_id < 0) { - dev_err(sdev->dev, "failed to assign pipeline id for %s: %d\n", + dev_err(scomp->dev, "failed to assign pipeline id for %s: %d\n", swidget->widget->name, swidget->instance_id); return swidget->instance_id; } @@ -3296,12 +3518,14 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget msg = &asrc->msg; break; } + case snd_soc_dapm_decoder: + case snd_soc_dapm_encoder: case snd_soc_dapm_effect: { struct sof_ipc4_process *process = swidget->private; if (!process->ipc_config_size) { - dev_err(sdev->dev, "module %s has no config data!\n", + dev_err(scomp->dev, "module %s has no config data!\n", swidget->widget->name); return -EINVAL; } @@ -3313,16 +3537,16 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget break; } default: - dev_err(sdev->dev, "widget type %d not supported", swidget->id); + dev_err(scomp->dev, "widget type %d not supported", swidget->id); return -EINVAL; } if (swidget->id != snd_soc_dapm_scheduler) { int module_id = msg->primary & SOF_IPC4_MOD_ID_MASK; - ret = sof_ipc4_widget_assign_instance_id(sdev, swidget); + ret = sof_ipc4_widget_assign_instance_id(swidget); if (ret < 0) { - dev_err(sdev->dev, "failed to assign instance id for %s\n", + dev_err(scomp->dev, "failed to assign instance id for %s\n", swidget->widget->name); return ret; } @@ -3336,7 +3560,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget msg->extension &= ~SOF_IPC4_MOD_EXT_PPL_ID_MASK; msg->extension |= SOF_IPC4_MOD_EXT_PPL_ID(pipe_widget->instance_id); - dev_dbg(sdev->dev, "Create widget %s (pipe %d) - ID %d, instance %d, core %d\n", + dev_dbg(scomp->dev, "Create widget %s (pipe %d) - ID %d, instance %d, core %d\n", swidget->widget->name, swidget->pipeline_id, module_id, swidget->instance_id, swidget->core); @@ -3350,7 +3574,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget ipc_data = ext_data; } } else { - dev_dbg(sdev->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n", + dev_dbg(scomp->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n", swidget->widget->name, swidget->pipeline_id, swidget->instance_id, swidget->core); } @@ -3362,7 +3586,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget fail: if (ret < 0) { - dev_err(sdev->dev, "failed to create module %s\n", swidget->widget->name); + dev_err(scomp->dev, "failed to create module %s\n", swidget->widget->name); if (swidget->id != snd_soc_dapm_scheduler) { struct sof_ipc4_fw_module *fw_module = swidget->module_info; @@ -3379,6 +3603,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { + struct snd_soc_component *scomp = swidget->scomp; struct sof_ipc4_fw_module *fw_module = swidget->module_info; struct sof_ipc4_fw_data *ipc4_data = sdev->private; int ret = 0; @@ -3392,7 +3617,7 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget u32 header; if (pipeline->use_chain_dma) { - dev_warn(sdev->dev, "use_chain_dma set for scheduler %s", + dev_warn(scomp->dev, "use_chain_dma set for scheduler %s", swidget->widget->name); return 0; } @@ -3406,7 +3631,7 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); if (ret < 0) - dev_err(sdev->dev, "failed to free pipeline widget %s\n", + dev_err(scomp->dev, "failed to free pipeline widget %s\n", swidget->widget->name); pipeline->mem_usage = 0; @@ -3511,6 +3736,7 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, struct snd_sof_widget *sink_widget, struct snd_sof_route *sroute) { + struct snd_soc_component *scomp = sroute->scomp; struct sof_ipc4_copier_config_set_sink_format format; const struct sof_ipc_ops *iops = sdev->ipc->ops; struct sof_ipc4_base_module_cfg *src_config; @@ -3533,7 +3759,7 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, pin_fmt = sof_ipc4_get_input_pin_audio_fmt(sink_widget, sroute->dst_queue_id); if (!pin_fmt) { - dev_err(sdev->dev, + dev_err(scomp->dev, "Failed to get input audio format of %s:%d for output of %s:%d\n", sink_widget->widget->name, sroute->dst_queue_id, src_widget->widget->name, sroute->src_queue_id); @@ -3558,6 +3784,7 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) { + struct snd_soc_component *scomp = sroute->scomp; struct snd_sof_widget *src_widget = sroute->src_widget; struct snd_sof_widget *sink_widget = sroute->sink_widget; struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget; @@ -3573,7 +3800,7 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * /* no route set up if chain DMA is used */ if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) { if (!src_pipeline->use_chain_dma || !sink_pipeline->use_chain_dma) { - dev_err(sdev->dev, + dev_err(scomp->dev, "use_chain_dma must be set for both src %s and sink %s pipelines\n", src_widget->widget->name, sink_widget->widget->name); return -EINVAL; @@ -3582,7 +3809,7 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * } if (!src_fw_module || !sink_fw_module) { - dev_err(sdev->dev, + dev_err(scomp->dev, "cannot bind %s -> %s, no firmware module for: %s%s\n", src_widget->widget->name, sink_widget->widget->name, src_fw_module ? "" : " source", @@ -3594,7 +3821,7 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, SOF_PIN_TYPE_OUTPUT); if (sroute->src_queue_id < 0) { - dev_err(sdev->dev, + dev_err(scomp->dev, "failed to get src_queue_id ID from source widget %s\n", src_widget->widget->name); return sroute->src_queue_id; @@ -3603,7 +3830,7 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * sroute->dst_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, SOF_PIN_TYPE_INPUT); if (sroute->dst_queue_id < 0) { - dev_err(sdev->dev, + dev_err(scomp->dev, "failed to get dst_queue_id ID from sink widget %s\n", sink_widget->widget->name); sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, @@ -3616,14 +3843,14 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * ret = sof_ipc4_set_copier_sink_format(sdev, src_widget, sink_widget, sroute); if (ret < 0) { - dev_err(sdev->dev, + dev_err(scomp->dev, "failed to set sink format for source %s:%d\n", src_widget->widget->name, sroute->src_queue_id); goto out; } } - dev_dbg(sdev->dev, "bind %s:%d -> %s:%d\n", + dev_dbg(scomp->dev, "bind %s:%d -> %s:%d\n", src_widget->widget->name, sroute->src_queue_id, sink_widget->widget->name, sroute->dst_queue_id); @@ -3643,7 +3870,7 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); if (ret < 0) { - dev_err(sdev->dev, "failed to bind modules %s:%d -> %s:%d\n", + dev_err(scomp->dev, "failed to bind modules %s:%d -> %s:%d\n", src_widget->widget->name, sroute->src_queue_id, sink_widget->widget->name, sroute->dst_queue_id); goto out; @@ -3659,6 +3886,7 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) { + struct snd_soc_component *scomp = sroute->scomp; struct snd_sof_widget *src_widget = sroute->src_widget; struct snd_sof_widget *sink_widget = sroute->sink_widget; struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info; @@ -3675,7 +3903,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) return 0; - dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n", + dev_dbg(scomp->dev, "unbind modules %s:%d -> %s:%d\n", src_widget->widget->name, sroute->src_queue_id, sink_widget->widget->name, sroute->dst_queue_id); @@ -3702,7 +3930,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); if (ret < 0) - dev_err(sdev->dev, "failed to unbind modules %s:%d -> %s:%d\n", + dev_err(scomp->dev, "failed to unbind modules %s:%d -> %s:%d\n", src_widget->widget->name, sroute->src_queue_id, sink_widget->widget->name, sroute->dst_queue_id); out: @@ -3715,6 +3943,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, unsigned int flags, struct snd_sof_dai_config_data *data) { + struct snd_soc_component *scomp = swidget->scomp; struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; struct snd_sof_dai *dai = swidget->private; @@ -3723,7 +3952,7 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * struct sof_ipc4_copier *ipc4_copier; if (!dai || !dai->private) { - dev_err(sdev->dev, "Invalid DAI or DAI private data for %s\n", + dev_err(scomp->dev, "Invalid DAI or DAI private data for %s\n", swidget->widget->name); return -EINVAL; } @@ -3785,7 +4014,7 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * /* nothing to do for SSP/DMIC */ break; default: - dev_err(sdev->dev, "%s: unsupported dai type %d\n", __func__, + dev_err(scomp->dev, "%s: unsupported dai type %d\n", __func__, ipc4_copier->dai_type); return -EINVAL; } @@ -3796,7 +4025,7 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_manifest *man) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct sof_manifest_tlv *manifest_tlv; struct sof_manifest *manifest; @@ -3856,6 +4085,7 @@ static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index, static int sof_ipc4_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type) { + struct snd_sof_audio_instance *instance; struct sof_ipc4_copier *ipc4_copier = dai->private; struct snd_soc_tplg_hw_config *hw_config; struct snd_sof_dai_link *slink; @@ -3866,7 +4096,7 @@ static int sof_ipc4_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai * if (!ipc4_copier) return 0; - list_for_each_entry(slink, &sdev->dai_link_list, list) { + for_each_slink_in_instances(slink, sdev, instance) { if (!strcmp(slink->link->name, dai->name)) { dai_link_found = true; break; @@ -4055,6 +4285,15 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TY process_token_list, ARRAY_SIZE(process_token_list), NULL, sof_ipc4_prepare_process_module, NULL}, + /* for all practical purposes a decoder is like an effect type widget */ + [snd_soc_dapm_decoder] = {sof_ipc4_widget_setup_comp_process, + sof_ipc4_widget_free_comp_process, + process_token_list, ARRAY_SIZE(process_token_list), + NULL, sof_ipc4_prepare_process_module, NULL}, + [snd_soc_dapm_encoder] = {sof_ipc4_widget_setup_comp_process, + sof_ipc4_widget_free_comp_process, + process_token_list, ARRAY_SIZE(process_token_list), + NULL, sof_ipc4_prepare_process_module, NULL}, }; const struct sof_ipc_tplg_ops ipc4_tplg_ops = { diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index a289c1d8f3ff0e..82adde9ba819d9 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -197,12 +197,15 @@ struct sof_ipc4_pin_format { * @input_pin_fmts: Available input pin formats * @num_input_formats: Number of input pin formats * @num_output_formats: Number of output pin formats + * @changed_params: Mask of changed params by the module instance between it's + * input and output formts (rate, channels, depth) */ struct sof_ipc4_available_audio_format { struct sof_ipc4_pin_format *output_pin_fmts; struct sof_ipc4_pin_format *input_pin_fmts; u32 num_input_formats; u32 num_output_formats; + u32 changed_params; }; /** @@ -219,6 +222,11 @@ struct sof_copier_gateway_cfg { uint32_t config_data[]; }; +/* bit definition in copier_feature_mask */ +enum sof_ipc4_copier_feature { + SOF_IPC4_COPIER_FAST_MODE = 0, /* free running mode of host copier */ +}; + /** * struct sof_ipc4_copier_data - IPC data for copier * @base_config: Base configuration including input audio format @@ -518,6 +526,8 @@ struct sof_ipc4_base_module_cfg_ext { * @msg: IPC4 message struct containing header and data info * @base_config_ext_size: Size of the base config extension data in bytes * @init_config: Module init config type (SOF_IPC4_MODULE_INIT_CONFIG_TYPE_*) + * @init_ext_module_data: module_data for init_ext object + * @init_ext_module_size: size of init_ext_module_data */ struct sof_ipc4_process { struct sof_ipc4_base_module_cfg base_config; @@ -529,6 +539,8 @@ struct sof_ipc4_process { struct sof_ipc4_msg msg; u32 base_config_ext_size; u32 init_config; + void *init_ext_module_data; + size_t init_ext_module_size; }; bool sof_ipc4_copier_is_single_bitdepth(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index d9bb8b79f91ecf..2ceddc43dc3c11 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -332,6 +332,8 @@ static int ipc4_wait_tx_done(struct snd_sof_ipc *ipc, void *reply_data) dev_err(sdev->dev, "ipc timed out for %#x|%#x\n", ipc4_msg->primary, ipc4_msg->extension); snd_sof_handle_fw_exception(ipc->sdev, "IPC timeout"); + snd_sof_dsp_msg_timeout_handler(sdev, msg); + return -ETIMEDOUT; } @@ -699,12 +701,21 @@ static void sof_ipc4_module_notification_handler(struct snd_sof_dev *sdev, } /* Handle ALSA kcontrol notification */ - if ((data->event_id & SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_MASK) == - SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL) { + switch (data->event_id & SOF_IPC4_NOTIFY_MODULE_EVENTID_SOF_MAGIC_MASK) { + case SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL: + { const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; if (tplg_ops->control->update) tplg_ops->control->update(sdev, ipc4_msg); + + break; + } + case SOF_IPC4_NOTIFY_MODULE_EVENTID_COMPR_MAGIC_VAL: + sof_ipc4_compr_drain_done(sdev, ipc4_msg); + break; + default: + break; } } diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index c1bea967737d52..720fc29240fd52 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -20,6 +20,7 @@ #include #include #include "../../ops.h" +#include "../../sof-client.h" #include "../../sof-of-dev.h" #include "../adsp_helper.h" #include "../mtk-adsp-common.h" @@ -379,6 +380,22 @@ static struct snd_soc_dai_driver mt8186_dai[] = { }; /* mt8186 ops */ +static const struct sof_audio_ops sof_mt8186_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_hw_params = mtk_adsp_stream_pcm_hw_params, + .pcm_pointer = mtk_adsp_stream_pcm_pointer, + .pcm_close = sof_stream_pcm_close, + + .drv = mt8186_dai, + .num_drv = ARRAY_SIZE(mt8186_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + static const struct snd_sof_dsp_ops sof_mt8186_ops = { /* probe and remove */ .probe = mt8186_dsp_probe, @@ -412,22 +429,16 @@ static const struct snd_sof_dsp_ops sof_mt8186_ops = { /* misc */ .get_bar_index = mtk_adsp_get_bar_index, - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_hw_params = mtk_adsp_stream_pcm_hw_params, - .pcm_pointer = mtk_adsp_stream_pcm_pointer, - .pcm_close = sof_stream_pcm_close, - /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* Firmware ops */ .dsp_arch_ops = &sof_xtensa_arch_ops, - /* DAI drivers */ - .drv = mt8186_dai, - .num_drv = ARRAY_SIZE(mt8186_dai), - /* Debug information */ .dbg_dump = mt8186_adsp_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, @@ -435,13 +446,6 @@ static const struct snd_sof_dsp_ops sof_mt8186_ops = { /* PM */ .suspend = mt8186_dsp_suspend, .resume = mt8186_dsp_resume, - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; static struct snd_sof_of_mach sof_mt8186_machs[] = { @@ -467,6 +471,7 @@ static const struct sof_dev_desc sof_of_mt8186_desc = { }, .nocodec_tplg_filename = "sof-mt8186-nocodec.tplg", .ops = &sof_mt8186_ops, + .audio_ops = &sof_mt8186_audio_ops, }; /* @@ -505,6 +510,22 @@ static struct snd_soc_dai_driver mt8188_dai[] = { }; /* mt8188 ops */ +static const struct sof_audio_ops sof_mt8188_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_hw_params = mtk_adsp_stream_pcm_hw_params, + .pcm_pointer = mtk_adsp_stream_pcm_pointer, + .pcm_close = sof_stream_pcm_close, + + .drv = mt8188_dai, + .num_drv = ARRAY_SIZE(mt8188_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + static struct snd_sof_dsp_ops sof_mt8188_ops; static int sof_mt8188_ops_init(struct snd_sof_dev *sdev) @@ -512,8 +533,7 @@ static int sof_mt8188_ops_init(struct snd_sof_dev *sdev) /* common defaults */ memcpy(&sof_mt8188_ops, &sof_mt8186_ops, sizeof(sof_mt8188_ops)); - sof_mt8188_ops.drv = mt8188_dai; - sof_mt8188_ops.num_drv = ARRAY_SIZE(mt8188_dai); + sdev->audio_ops = &sof_mt8188_audio_ops; return 0; } @@ -541,6 +561,7 @@ static const struct sof_dev_desc sof_of_mt8188_desc = { }, .nocodec_tplg_filename = "sof-mt8188-nocodec.tplg", .ops = &sof_mt8188_ops, + .audio_ops = &sof_mt8188_audio_ops, .ops_init = sof_mt8188_ops_init, }; @@ -566,5 +587,6 @@ module_platform_driver(snd_sof_of_mt8186_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF support for MT8186/MT8188 platforms"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SND_SOC_SOF_MTK_COMMON"); diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 5dfa8721e059c5..23ec7f1ce5f3bf 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -20,6 +20,7 @@ #include #include #include "../../ops.h" +#include "../../sof-client.h" #include "../../sof-of-dev.h" #include "../adsp_helper.h" #include "../mtk-adsp-common.h" @@ -402,6 +403,22 @@ static struct snd_soc_dai_driver mt8195_dai[] = { }, }; +static const struct sof_audio_ops sof_mt8195_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_hw_params = mtk_adsp_stream_pcm_hw_params, + .pcm_pointer = mtk_adsp_stream_pcm_pointer, + .pcm_close = sof_stream_pcm_close, + + .drv = mt8195_dai, + .num_drv = ARRAY_SIZE(mt8195_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + /* mt8195 ops */ static const struct snd_sof_dsp_ops sof_mt8195_ops = { /* probe and remove */ @@ -436,15 +453,13 @@ static const struct snd_sof_dsp_ops sof_mt8195_ops = { /* misc */ .get_bar_index = mtk_adsp_get_bar_index, - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_hw_params = mtk_adsp_stream_pcm_hw_params, - .pcm_pointer = mtk_adsp_stream_pcm_pointer, - .pcm_close = sof_stream_pcm_close, - /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* Firmware ops */ .dsp_arch_ops = &sof_xtensa_arch_ops, @@ -452,20 +467,9 @@ static const struct snd_sof_dsp_ops sof_mt8195_ops = { .dbg_dump = mt8195_adsp_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - /* DAI drivers */ - .drv = mt8195_dai, - .num_drv = ARRAY_SIZE(mt8195_dai), - /* PM */ .suspend = mt8195_dsp_suspend, .resume = mt8195_dsp_resume, - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; static struct snd_sof_of_mach sof_mt8195_machs[] = { @@ -498,6 +502,7 @@ static const struct sof_dev_desc sof_of_mt8195_desc = { }, .nocodec_tplg_filename = "sof-mt8195-nocodec.tplg", .ops = &sof_mt8195_ops, + .audio_ops = &sof_mt8195_audio_ops, .ipc_timeout = 1000, }; @@ -522,5 +527,6 @@ module_platform_driver(snd_sof_of_mt8195_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF support for MTL 8195 platforms"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SND_SOC_SOF_MTK_COMMON"); diff --git a/sound/soc/sof/mediatek/mt8365/mt8365.c b/sound/soc/sof/mediatek/mt8365/mt8365.c index 7d2c6955c10c69..87b43f4e814254 100644 --- a/sound/soc/sof/mediatek/mt8365/mt8365.c +++ b/sound/soc/sof/mediatek/mt8365/mt8365.c @@ -19,6 +19,7 @@ #include #include #include "../../ops.h" +#include "../../sof-client.h" #include "../../sof-of-dev.h" #include "../../sof-audio.h" #include "../adsp_helper.h" @@ -436,7 +437,7 @@ static int mt8365_get_bar_index(struct snd_sof_dev *sdev, u32 type) return type; } -static int mt8365_pcm_hw_params(struct snd_sof_dev *sdev, +static int mt8365_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params) @@ -446,18 +447,18 @@ static int mt8365_pcm_hw_params(struct snd_sof_dev *sdev, return 0; } -static snd_pcm_uframes_t mt8365_pcm_pointer(struct snd_sof_dev *sdev, +static snd_pcm_uframes_t mt8365_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_pcm *spcm; struct sof_ipc_stream_posn posn; struct snd_sof_pcm_stream *stream; - struct snd_soc_component *scomp = sdev->component; struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); snd_pcm_uframes_t pos; int ret; - spcm = snd_sof_find_spcm_dai(scomp, rtd); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) { dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", rtd->dai_link->id); @@ -534,6 +535,22 @@ static struct snd_soc_dai_driver mt8365_dai[] = { }, }; +static const struct sof_audio_ops sof_mt8365_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_hw_params = mt8365_pcm_hw_params, + .pcm_pointer = mt8365_pcm_pointer, + .pcm_close = sof_stream_pcm_close, + + .drv = mt8365_dai, + .num_drv = ARRAY_SIZE(mt8365_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + /* mt8365 ops */ static struct snd_sof_dsp_ops sof_mt8365_ops = { /* probe and remove */ @@ -568,15 +585,13 @@ static struct snd_sof_dsp_ops sof_mt8365_ops = { /* misc */ .get_bar_index = mt8365_get_bar_index, - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_hw_params = mt8365_pcm_hw_params, - .pcm_pointer = mt8365_pcm_pointer, - .pcm_close = sof_stream_pcm_close, - /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* Firmware ops */ .dsp_arch_ops = &sof_xtensa_arch_ops, @@ -584,20 +599,9 @@ static struct snd_sof_dsp_ops sof_mt8365_ops = { .dbg_dump = mt8365_adsp_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - /* DAI drivers */ - .drv = mt8365_dai, - .num_drv = ARRAY_SIZE(mt8365_dai), - /* PM */ .suspend = mt8365_dsp_suspend, .resume = mt8365_dsp_resume, - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; static struct snd_sof_of_mach sof_mt8365_machs[] = { @@ -624,6 +628,7 @@ static const struct sof_dev_desc sof_of_mt8365_desc = { }, .nocodec_tplg_filename = "sof-mt8365-nocodec.tplg", .ops = &sof_mt8365_ops, + .audio_ops = &sof_mt8365_audio_ops, .ipc_timeout = 1000, }; @@ -646,6 +651,7 @@ static struct platform_driver snd_sof_of_mt8365_driver = { }; module_platform_driver(snd_sof_of_mt8365_driver); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SND_SOC_SOF_MTK_COMMON"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.c b/sound/soc/sof/mediatek/mtk-adsp-common.c index 75b4af4b5111de..57b075a6b85aff 100644 --- a/sound/soc/sof/mediatek/mtk-adsp-common.c +++ b/sound/soc/sof/mediatek/mtk-adsp-common.c @@ -161,7 +161,7 @@ EXPORT_SYMBOL(mtk_adsp_get_bar_index); * @params: hw params * @platform_params: Platform specific SOF stream parameters */ -int mtk_adsp_stream_pcm_hw_params(struct snd_sof_dev *sdev, +int mtk_adsp_stream_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params) @@ -176,18 +176,18 @@ EXPORT_SYMBOL(mtk_adsp_stream_pcm_hw_params); * @sdev: SOF device * @substream: PCM substream */ -snd_pcm_uframes_t mtk_adsp_stream_pcm_pointer(struct snd_sof_dev *sdev, +snd_pcm_uframes_t mtk_adsp_stream_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_component *scomp = sdev->component; + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_pcm_stream *stream; struct sof_ipc_stream_posn posn; struct snd_sof_pcm *spcm; snd_pcm_uframes_t pos; int ret; - spcm = snd_sof_find_spcm_dai(scomp, rtd); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) { dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", rtd->dai_link->id); diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.h b/sound/soc/sof/mediatek/mtk-adsp-common.h index dc36b91d6779a0..7980cd4ab4c0c2 100644 --- a/sound/soc/sof/mediatek/mtk-adsp-common.h +++ b/sound/soc/sof/mediatek/mtk-adsp-common.h @@ -6,15 +6,17 @@ #define EXCEPT_MAX_HDR_SIZE 0x400 #define MTK_ADSP_STACK_DUMP_SIZE 32 +struct snd_soc_component; + void mtk_adsp_dump(struct snd_sof_dev *sdev, u32 flags); int mtk_adsp_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); void mtk_adsp_handle_reply(struct mtk_adsp_ipc *ipc); void mtk_adsp_handle_request(struct mtk_adsp_ipc *ipc); int mtk_adsp_get_bar_index(struct snd_sof_dev *sdev, u32 type); -int mtk_adsp_stream_pcm_hw_params(struct snd_sof_dev *sdev, +int mtk_adsp_stream_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params); -snd_pcm_uframes_t mtk_adsp_stream_pcm_pointer(struct snd_sof_dev *sdev, +snd_pcm_uframes_t mtk_adsp_stream_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream); #endif diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c index c0c906a78ebae4..53a06b09a9d662 100644 --- a/sound/soc/sof/nocodec.c +++ b/sound/soc/sof/nocodec.c @@ -22,7 +22,8 @@ static struct snd_soc_card sof_nocodec_card = { static int sof_nocodec_bes_setup(struct device *dev, struct snd_soc_dai_driver *drv, struct snd_soc_dai_link *links, - int link_num, struct snd_soc_card *card) + int link_num, struct snd_soc_card *card, + const char *platform_name) { struct snd_soc_dai_link_component *dlc; int i; @@ -54,7 +55,7 @@ static int sof_nocodec_bes_setup(struct device *dev, links[i].id = i; links[i].no_pcm = 1; links[i].cpus->dai_name = drv[i].name; - links[i].platforms->name = dev_name(dev->parent); + links[i].platforms->name = platform_name; links[i].playback_only = drv[i].playback.channels_min && !drv[i].capture.channels_min; links[i].capture_only = !drv[i].playback.channels_min && drv[i].capture.channels_min; @@ -70,7 +71,8 @@ static int sof_nocodec_bes_setup(struct device *dev, static int sof_nocodec_setup(struct device *dev, u32 num_dai_drivers, - struct snd_soc_dai_driver *dai_drivers) + struct snd_soc_dai_driver *dai_drivers, + const char *platform_name) { struct snd_soc_dai_link *links; @@ -79,7 +81,8 @@ static int sof_nocodec_setup(struct device *dev, if (!links) return -ENOMEM; - return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers, &sof_nocodec_card); + return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers, + &sof_nocodec_card, platform_name); } static int sof_nocodec_probe(struct platform_device *pdev) @@ -93,7 +96,8 @@ static int sof_nocodec_probe(struct platform_device *pdev) mach = pdev->dev.platform_data; ret = sof_nocodec_setup(card->dev, mach->mach_params.num_dai_drivers, - mach->mach_params.dai_drivers); + mach->mach_params.dai_drivers, + mach->mach_params.platform); if (ret < 0) return ret; diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 4c9500dd8dd21f..7e2d2c3dfdaf1f 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -15,8 +15,10 @@ #include #include #include +#include #include #include "sof-priv.h" +#include "sof-audio.h" #define sof_ops(sdev) \ ((sdev)->pdata->desc->ops) @@ -412,60 +414,177 @@ static inline int snd_sof_dsp_send_msg(struct snd_sof_dev *sdev, return sof_ops(sdev)->send_msg(sdev, msg); } +/* ipc */ +static inline void snd_sof_dsp_msg_timeout_handler(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + if (sof_ops(sdev)->msg_timeout_handler) + sof_ops(sdev)->msg_timeout_handler(sdev, msg); +} + /* host PCM ops */ static inline int -snd_sof_pcm_platform_open(struct snd_sof_dev *sdev, +snd_sof_pcm_platform_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (sof_ops(sdev) && sof_ops(sdev)->pcm_open) - return sof_ops(sdev)->pcm_open(sdev, substream); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops && ins->audio_ops->pcm_open) + return ins->audio_ops->pcm_open(component, substream); return 0; } /* disconnect pcm substream to a host stream */ static inline int -snd_sof_pcm_platform_close(struct snd_sof_dev *sdev, +snd_sof_pcm_platform_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (sof_ops(sdev) && sof_ops(sdev)->pcm_close) - return sof_ops(sdev)->pcm_close(sdev, substream); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops && ins->audio_ops->pcm_close) + return ins->audio_ops->pcm_close(component, substream); return 0; } /* host stream hw params */ static inline int -snd_sof_pcm_platform_hw_params(struct snd_sof_dev *sdev, +snd_sof_pcm_platform_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params) { - if (sof_ops(sdev) && sof_ops(sdev)->pcm_hw_params) - return sof_ops(sdev)->pcm_hw_params(sdev, substream, params, - platform_params); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops && ins->audio_ops->pcm_hw_params) + return ins->audio_ops->pcm_hw_params(component, substream, + params, platform_params); + + return 0; +} + +static inline int +snd_sof_compr_platform_open(struct snd_soc_component *component, + struct snd_compr_stream *cstream) +{ + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops && ins->audio_ops->compr_open) + return ins->audio_ops->compr_open(component, cstream); + + return 0; +} + +/* disconnect pcm substream to a host stream */ +static inline int +snd_sof_compr_platform_close(struct snd_soc_component *component, + struct snd_compr_stream *cstream) +{ + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops && ins->audio_ops->compr_close) + return ins->audio_ops->compr_close(component, cstream); + + return 0; +} + +/* host stream hw params */ +static inline int +snd_sof_compr_platform_hw_params(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_params *params, + struct snd_sof_platform_stream_params *platform_params) +{ + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops && ins->audio_ops->compr_hw_params) + return ins->audio_ops->compr_hw_params(component, cstream, + params, platform_params); + + return 0; +} + +static inline int +snd_sof_compr_platform_hw_free(struct snd_soc_component *component, + struct snd_compr_stream *cstream) +{ + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops && ins->audio_ops->compr_hw_free) + return ins->audio_ops->compr_hw_free(component, cstream); + + return 0; +} + +static inline int +snd_sof_compr_platform_trigger(struct snd_soc_component *component, + struct snd_compr_stream *cstream, int cmd) +{ + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops && ins->audio_ops->compr_trigger) + return ins->audio_ops->compr_trigger(component, cstream, cmd); + + return 0; +} + +static inline int +snd_sof_compr_platform_pointer(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp64 *tstamp) +{ + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops && ins->audio_ops->compr_pointer) + return ins->audio_ops->compr_pointer(component, cstream, tstamp); + + return 0; +} + +static inline u64 +snd_sof_compr_get_dai_frame_counter(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream) +{ + if (sof_ops(sdev) && sof_ops(sdev)->compr_get_dai_frame_counter) + return sof_ops(sdev)->compr_get_dai_frame_counter(sdev, cstream); return 0; } /* host stream hw free */ static inline int -snd_sof_pcm_platform_hw_free(struct snd_sof_dev *sdev, +snd_sof_pcm_platform_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (sof_ops(sdev) && sof_ops(sdev)->pcm_hw_free) - return sof_ops(sdev)->pcm_hw_free(sdev, substream); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops && ins->audio_ops->pcm_hw_free) + return ins->audio_ops->pcm_hw_free(component, substream); return 0; } /* host stream trigger */ static inline int -snd_sof_pcm_platform_trigger(struct snd_sof_dev *sdev, +snd_sof_pcm_platform_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { - if (sof_ops(sdev) && sof_ops(sdev)->pcm_trigger) - return sof_ops(sdev)->pcm_trigger(sdev, substream, cmd); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops && ins->audio_ops->pcm_trigger) + return ins->audio_ops->pcm_trigger(component, substream, cmd); return 0; } @@ -502,21 +621,27 @@ snd_sof_set_stream_data_offset(struct snd_sof_dev *sdev, /* host stream pointer */ static inline snd_pcm_uframes_t -snd_sof_pcm_platform_pointer(struct snd_sof_dev *sdev, +snd_sof_pcm_platform_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (sof_ops(sdev) && sof_ops(sdev)->pcm_pointer) - return sof_ops(sdev)->pcm_pointer(sdev, substream); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops && ins->audio_ops->pcm_pointer) + return ins->audio_ops->pcm_pointer(component, substream); return 0; } /* pcm ack */ -static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev, +static inline int snd_sof_pcm_platform_ack(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (sof_ops(sdev) && sof_ops(sdev)->pcm_ack) - return sof_ops(sdev)->pcm_ack(sdev, substream); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops && ins->audio_ops->pcm_ack) + return ins->audio_ops->pcm_ack(component, substream); return 0; } diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index f748d072109aa6..6557da246c8bff 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -17,6 +17,8 @@ #include "sof-of-dev.h" #include "sof-priv.h" #include "sof-audio.h" +#include "sof-client.h" +#include "sof-client-audio.h" #include "sof-utils.h" #include "ops.h" @@ -66,7 +68,7 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) } EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); -static int +int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params, int dir) @@ -100,12 +102,13 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run return 0; } -static struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_sof_dev *sdev, - int comp_id) +struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_sof_dev *sdev, + int comp_id) { + struct snd_sof_audio_instance *instance; struct snd_sof_widget *swidget; - list_for_each_entry(swidget, &sdev->widget_list, list) { + for_each_swidget_in_instances(swidget, sdev, instance) { if (comp_id == swidget->comp_id) return swidget; } @@ -117,7 +120,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); @@ -152,7 +155,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, * between. At least ALSA OSS emulation depends on this. */ if (spcm->prepared[substream->stream] && pcm_ops && pcm_ops->hw_free) { - ret = pcm_ops->hw_free(component, substream); + ret = pcm_ops->hw_free(component, substream, spcm, substream->stream); if (ret < 0) return ret; @@ -160,7 +163,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, } platform_params = &spcm->platform_params[substream->stream]; - ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, platform_params); + ret = snd_sof_pcm_platform_hw_params(component, substream, params, platform_params); if (ret < 0) { spcm_err(spcm, substream->stream, "platform hw params failed\n"); return ret; @@ -207,6 +210,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, } static int sof_pcm_stream_free(struct snd_sof_dev *sdev, + struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_sof_pcm *spcm, int dir, bool free_widget_list) @@ -218,12 +222,13 @@ static int sof_pcm_stream_free(struct snd_sof_dev *sdev, if (spcm->prepared[substream->stream]) { /* stop DMA first if needed */ if (pcm_ops && pcm_ops->platform_stop_during_hw_free) - snd_sof_pcm_platform_trigger(sdev, substream, + snd_sof_pcm_platform_trigger(component, substream, SNDRV_PCM_TRIGGER_STOP); /* free PCM in the DSP */ if (pcm_ops && pcm_ops->hw_free) { - ret = pcm_ops->hw_free(sdev->component, substream); + ret = pcm_ops->hw_free(component, substream, spcm, + substream->stream); if (ret < 0) { spcm_err(spcm, substream->stream, "pcm_ops->hw_free failed %d\n", ret); @@ -236,7 +241,7 @@ static int sof_pcm_stream_free(struct snd_sof_dev *sdev, } /* reset the DMA */ - ret = snd_sof_pcm_platform_hw_free(sdev, substream); + ret = snd_sof_pcm_platform_hw_free(component, substream); if (ret < 0) { spcm_err(spcm, substream->stream, "platform hw free failed %d\n", ret); @@ -260,11 +265,12 @@ static int sof_pcm_stream_free(struct snd_sof_dev *sdev, int sof_pcm_free_all_streams(struct snd_sof_dev *sdev) { + struct snd_sof_audio_instance *instance; struct snd_pcm_substream *substream; struct snd_sof_pcm *spcm; int dir, ret; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_spcm_in_instances(spcm, sdev, instance) { for_each_pcm_streams(dir) { substream = spcm->stream[dir].substream; @@ -273,7 +279,7 @@ int sof_pcm_free_all_streams(struct snd_sof_dev *sdev) continue; if (spcm->stream[dir].list) { - ret = sof_pcm_stream_free(sdev, substream, spcm, + ret = sof_pcm_stream_free(sdev, spcm->scomp, substream, spcm, dir, true); if (ret < 0) return ret; @@ -288,7 +294,7 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_pcm *spcm; int ret; @@ -302,7 +308,8 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, spcm_dbg(spcm, substream->stream, "Entry: hw_free\n"); - ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true); + ret = sof_pcm_stream_free(sdev, component, substream, spcm, + substream->stream, true); /* unprepare and free the list of DAPM widgets */ sof_widget_list_unprepare(sdev, spcm, substream->stream); @@ -316,7 +323,7 @@ static int sof_pcm_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_platform_stream_params *platform_params; struct snd_soc_dapm_widget_list *list; @@ -343,7 +350,7 @@ static int sof_pcm_prepare(struct snd_soc_component *component, * this case should be reached in case of xruns where we absolutely * want to free-up and reset all PCM/DMA resources */ - ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true); + ret = sof_pcm_stream_free(sdev, component, substream, spcm, dir, true); if (ret < 0) return ret; } @@ -360,7 +367,7 @@ static int sof_pcm_prepare(struct snd_soc_component *component, platform_params = &spcm->platform_params[substream->stream]; ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir); if (ret < 0) { - dev_err(sdev->dev, "failed widget list set up for pcm %d dir %u\n", + dev_err(component->dev, "failed widget list set up for pcm %d dir %u\n", le32_to_cpu(spcm->pcm.pcm_id), dir); spcm->stream[dir].list = NULL; snd_soc_dapm_dai_free_widgets(&list); @@ -386,7 +393,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_pcm *spcm; bool reset_hw_params = false; @@ -455,24 +462,24 @@ static int sof_pcm_trigger(struct snd_soc_component *component, } if (!ipc_first) - snd_sof_pcm_platform_trigger(sdev, substream, cmd); + snd_sof_pcm_platform_trigger(component, substream, cmd); if (pcm_ops && pcm_ops->trigger) - ret = pcm_ops->trigger(component, substream, cmd); + ret = pcm_ops->trigger(component, substream, spcm, cmd, substream->stream); switch (cmd) { case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_START: /* invoke platform trigger to start DMA only if pcm_ops is successful */ if (ipc_first && !ret) - snd_sof_pcm_platform_trigger(sdev, substream, cmd); + snd_sof_pcm_platform_trigger(component, substream, cmd); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: /* invoke platform trigger to stop DMA even if pcm_ops isn't set or if it failed */ if (!pcm_ops || !pcm_ops->platform_stop_during_hw_free) - snd_sof_pcm_platform_trigger(sdev, substream, cmd); + snd_sof_pcm_platform_trigger(component, substream, cmd); /* * set the pending_stop flag to indicate that pipeline stop has been delayed. @@ -489,7 +496,8 @@ static int sof_pcm_trigger(struct snd_soc_component *component, /* free PCM if reset_hw_params is set and the STOP IPC is successful */ if (!ret && reset_hw_params) - ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, false); + ret = sof_pcm_stream_free(sdev, component, substream, spcm, + substream->stream, false); return ret; } @@ -498,7 +506,9 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_pcm *spcm; snd_pcm_uframes_t host, dai; @@ -515,8 +525,8 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component, return ret ? ret : host; /* use dsp ops pointer callback directly if set */ - if (sof_ops(sdev)->pcm_pointer) - return sof_ops(sdev)->pcm_pointer(sdev, substream); + if (ins && ins->audio_ops && ins->audio_ops->pcm_pointer) + return ins->audio_ops->pcm_pointer(component, substream); spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) @@ -538,8 +548,9 @@ static int sof_pcm_open(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct snd_sof_dsp_ops *ops = sof_ops(sdev); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); struct snd_sof_pcm *spcm; struct snd_soc_tplg_stream_caps *caps; int ret; @@ -548,6 +559,10 @@ static int sof_pcm_open(struct snd_soc_component *component, if (rtd->dai_link->no_pcm) return 0; + ret = sof_client_core_module_get(cdev); + if (ret) + return ret; + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return -EINVAL; @@ -557,7 +572,7 @@ static int sof_pcm_open(struct snd_soc_component *component, caps = &spcm->pcm.caps[substream->stream]; /* set runtime config */ - runtime->hw.info = ops->hw_info; /* platform-specific */ + runtime->hw.info = ins->audio_ops->hw_info; /* platform-specific */ /* set any runtime constraints based on topology */ runtime->hw.formats = le64_to_cpu(caps->formats); @@ -580,10 +595,11 @@ static int sof_pcm_open(struct snd_soc_component *component, spcm->stream[substream->stream].substream = substream; spcm->prepared[substream->stream] = false; - ret = snd_sof_pcm_platform_open(sdev, substream); + ret = snd_sof_pcm_platform_open(component, substream); if (ret < 0) { spcm_err(spcm, substream->stream, "platform pcm open failed %d\n", ret); + sof_client_core_module_put(cdev); return ret; } @@ -600,7 +616,7 @@ static int sof_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); struct snd_sof_pcm *spcm; int err; @@ -614,7 +630,7 @@ static int sof_pcm_close(struct snd_soc_component *component, spcm_dbg(spcm, substream->stream, "Entry: close\n"); - err = snd_sof_pcm_platform_close(sdev, substream); + err = snd_sof_pcm_platform_close(component, substream); if (err < 0) { spcm_err(spcm, substream->stream, "platform pcm close failed %d\n", err); @@ -626,6 +642,8 @@ static int sof_pcm_close(struct snd_soc_component *component, spcm->stream[substream->stream].substream = NULL; + sof_client_core_module_put(cdev); + return 0; } @@ -637,7 +655,7 @@ static int sof_pcm_close(struct snd_soc_component *component, static int sof_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_pcm *spcm; struct snd_pcm *pcm = rtd->pcm; struct snd_soc_tplg_stream_caps *caps; @@ -727,7 +745,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); /* no topology exists for this BE, try a common configuration */ @@ -758,29 +776,40 @@ EXPORT_SYMBOL(sof_pcm_dai_link_fixup); static int sof_pcm_probe(struct snd_soc_component *component) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); + struct sof_audio_client_pdata *audio_pdata = dev_get_platdata(&cdev->auxdev.dev); + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct snd_sof_pdata *plat_data = sdev->pdata; + struct snd_sof_audio_instance *instance; + bool got_runtime_pm; const char *tplg_filename; int ret; + instance = snd_sof_audio_instance_register(sdev, component); + if (!instance) + return -ENOMEM; + /* * make sure the device is pm_runtime_active before loading the * topology and initiating IPC or bus transactions */ ret = pm_runtime_resume_and_get(component->dev); - if (ret < 0 && ret != -EACCES) + if (ret < 0 && ret != -EACCES) { + snd_sof_audio_instance_unregister(instance); return ret; + } + got_runtime_pm = ret >= 0; /* load the default topology */ - sdev->component = component; - - tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL, + tplg_filename = devm_kasprintf(component->dev, GFP_KERNEL, "%s/%s", plat_data->tplg_filename_prefix, + audio_pdata->machine.sof_tplg_filename ? + audio_pdata->machine.sof_tplg_filename : plat_data->tplg_filename); if (!tplg_filename) { ret = -ENOMEM; - goto pm_error; + goto out; } ret = snd_sof_load_topology(component, tplg_filename); @@ -788,14 +817,33 @@ static int sof_pcm_probe(struct snd_soc_component *component) dev_err(component->dev, "error: failed to load DSP topology %d\n", ret); -pm_error: - pm_runtime_put_autosuspend(component->dev); +out: + if (ret) { + /* + * Remove topology objects to prevent dangling references. + * When sof_complete() or a late topology file fails, DAIs from + * previously successful loads remain on the component's DAI + * list. Their driver structs are allocated under the card + * device (tplg->dev) which gets freed by devres when mc_probe + * fails, leaving dangling driver pointers. Subsequent card + * binding would crash iterating these DAIs. + */ + snd_soc_tplg_component_remove(component); + snd_sof_audio_instance_unregister(instance); + } + if (got_runtime_pm) + pm_runtime_put_autosuspend(component->dev); return ret; } static void sof_pcm_remove(struct snd_soc_component *component) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); + + if (instance) + snd_sof_audio_instance_unregister(instance); + /* remove topology */ snd_soc_tplg_component_remove(component); } @@ -803,15 +851,13 @@ static void sof_pcm_remove(struct snd_soc_component *component) static int sof_pcm_ack(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - - return snd_sof_pcm_platform_ack(sdev, substream); + return snd_sof_pcm_platform_ack(component, substream); } static snd_pcm_sframes_t sof_pcm_delay(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); if (pcm_ops && pcm_ops->delay) @@ -820,9 +866,9 @@ static snd_pcm_sframes_t sof_pcm_delay(struct snd_soc_component *component, return 0; } -void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) +void snd_sof_new_platform_drv(struct snd_sof_dev *sdev, + struct snd_soc_component_driver * const pd) { - struct snd_soc_component_driver *pd = &sdev->plat_drv; struct snd_sof_pdata *plat_data = sdev->pdata; const char *drv_name; @@ -847,7 +893,10 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->delay = sof_pcm_delay; #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) - pd->compress_ops = &sof_compressed_ops; + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + + if (pcm_ops) + pd->compress_ops = pcm_ops->compress_ops; #endif pd->pcm_new = sof_pcm_new; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index acf56607bc9c11..2c8694de8b1bde 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -11,8 +11,64 @@ #include #include #include "sof-audio.h" +#include "sof-utils.h" #include "ops.h" +struct snd_sof_audio_instance * +snd_sof_audio_instance_register(struct snd_sof_dev *sdev, + struct snd_soc_component *component) +{ + struct snd_sof_audio_instance *instance; + + instance = devm_kzalloc(sdev->dev, sizeof(*instance), GFP_KERNEL); + if (!instance) + return NULL; + + instance->sdev = sdev; + instance->component = component; + instance->audio_ops = sdev->audio_ops; + INIT_LIST_HEAD(&instance->pipeline_list); + INIT_LIST_HEAD(&instance->dai_list); + INIT_LIST_HEAD(&instance->dai_link_list); + INIT_LIST_HEAD(&instance->route_list); + INIT_LIST_HEAD(&instance->pcm_list); + INIT_LIST_HEAD(&instance->kcontrol_list); + INIT_LIST_HEAD(&instance->widget_list); + + scoped_guard(spinlock, &sdev->audio_instance_list_lock) + list_add_tail_rcu(&instance->list, &sdev->audio_instance_list); + + return instance; +} + +void snd_sof_audio_instance_unregister(struct snd_sof_audio_instance *instance) +{ + struct snd_sof_dev *sdev = instance->sdev; + + scoped_guard(spinlock, &sdev->audio_instance_list_lock) + list_del_rcu(&instance->list); + synchronize_rcu(); +} + +struct snd_sof_audio_instance * +snd_sof_component_get_audio_instance(struct snd_soc_component *component) +{ + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_sof_audio_instance *instance; + + if (!sdev) + return NULL; + + guard(rcu)(); + list_for_each_entry_rcu(instance, &sdev->audio_instance_list, list) { + if (instance->component == component) + return instance; + } + + return NULL; +} +EXPORT_SYMBOL(snd_sof_component_get_audio_instance); + /* * Check if a DAI widget is an aggregated DAI. Aggregated DAI's have names ending in numbers * starting with 0. For example: in the case of a SDW speaker with 2 amps, the topology contains @@ -28,26 +84,32 @@ static bool is_aggregated_dai(struct snd_sof_widget *swidget) swidget->widget->name[strlen(swidget->widget->name) - 1] != '0'); } -static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, - const char *func) +static bool is_virtual_widget(struct snd_soc_dapm_widget *widget, const char *func) { + struct snd_sof_widget *swidget = widget->dobj.private; + switch (widget->id) { case snd_soc_dapm_out_drv: case snd_soc_dapm_output: case snd_soc_dapm_input: - dev_dbg(sdev->dev, "%s: %s is a virtual widget\n", func, widget->name); + if (swidget) + dev_dbg(swidget->scomp->dev, "%s: %s is a virtual widget\n", + func, widget->name); return true; default: return false; } } -static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget) +static void sof_reset_route_setup_status(struct snd_sof_widget *widget) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(widget->scomp); + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(widget->scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_route *sroute; - list_for_each_entry(sroute, &sdev->route_list, list) + list_for_each_entry(sroute, &instance->route_list, list) if (sroute->src_widget == widget || sroute->sink_widget == widget) { if (sroute->setup && tplg_ops && tplg_ops->route_free) tplg_ops->route_free(sdev, sroute); @@ -57,6 +119,7 @@ static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_so } static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, + struct snd_soc_component *scomp, struct snd_sof_widget *swidget) { const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); @@ -74,7 +137,7 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, return 0; /* reset route setup status for all routes that contain this widget */ - sof_reset_route_setup_status(sdev, swidget); + sof_reset_route_setup_status(swidget); /* free DAI config and continue to free widget even if it fails */ if (WIDGET_IS_DAI(swidget->id)) { @@ -86,7 +149,8 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, if (tplg_ops && tplg_ops->dai_config) { err = tplg_ops->dai_config(sdev, swidget, flags, &data); if (err < 0) - dev_err(sdev->dev, "failed to free config for widget %s\n", + dev_err(scomp->dev, + "failed to free config for widget %s\n", swidget->widget->name); } } @@ -102,34 +166,35 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, * decrement ref count for cores associated with all modules in the pipeline and clear * the complete flag */ - if (swidget->id == snd_soc_dapm_scheduler) { + if (swidget->id == snd_soc_dapm_scheduler && spipe) { int i; for_each_set_bit(i, &spipe->core_mask, sdev->num_cores) { ret = snd_sof_dsp_core_put(sdev, i); if (ret < 0) { - dev_err(sdev->dev, "failed to disable target core: %d for pipeline %s\n", + dev_err(scomp->dev, + "failed to disable target core: %d for pipeline %s\n", i, swidget->widget->name); if (!err) err = ret; } } - swidget->spipe->complete = 0; + spipe->complete = 0; } /* * free the scheduler widget (same as pipe_widget) associated with the current swidget. * skip for static pipelines */ - if (swidget->spipe && swidget->dynamic_pipeline_widget && + if (spipe && spipe->pipe_widget && swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { - ret = sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget); + ret = sof_widget_free_unlocked(sdev, scomp, spipe->pipe_widget); if (ret < 0 && !err) err = ret; } if (!err) - dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name); + dev_dbg(scomp->dev, "widget %s freed\n", swidget->widget->name); return err; } @@ -137,16 +202,16 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { guard(mutex)(&swidget->setup_mutex); - return sof_widget_free_unlocked(sdev, swidget); + return sof_widget_free_unlocked(sdev, swidget->scomp, swidget); } EXPORT_SYMBOL(sof_widget_free); static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, + struct snd_soc_component *scomp, struct snd_sof_widget *swidget) { const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_pipeline *spipe = swidget->spipe; - bool use_count_decremented = false; int ret; int i; @@ -169,12 +234,13 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, */ if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { if (!swidget->spipe || !swidget->spipe->pipe_widget) { - dev_err(sdev->dev, "No pipeline set for %s\n", swidget->widget->name); + dev_err(scomp->dev, "No pipeline set for %s\n", + swidget->widget->name); ret = -EINVAL; goto use_count_dec; } - ret = sof_widget_setup_unlocked(sdev, swidget->spipe->pipe_widget); + ret = sof_widget_setup_unlocked(sdev, scomp, swidget->spipe->pipe_widget); if (ret < 0) goto use_count_dec; } @@ -184,7 +250,8 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, for_each_set_bit(i, &spipe->core_mask, sdev->num_cores) { ret = snd_sof_dsp_core_get(sdev, i); if (ret < 0) { - dev_err(sdev->dev, "failed to enable target core %d for pipeline %s\n", + dev_err(scomp->dev, + "failed to enable target core %d for pipeline %s\n", i, swidget->widget->name); goto pipe_widget_free; } @@ -220,17 +287,18 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, goto widget_free; } - dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name); + dev_dbg(scomp->dev, "widget %s setup complete\n", swidget->widget->name); return 0; widget_free: - /* widget use_count will be decremented by sof_widget_free() */ - sof_widget_free_unlocked(sdev, swidget); - use_count_decremented = true; + /* widget use_count and core_put handled by sof_widget_free() */ + sof_widget_free_unlocked(sdev, scomp, swidget); + return ret; + pipe_widget_free: if (swidget->id != snd_soc_dapm_scheduler) { - sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget); + sof_widget_free_unlocked(sdev, scomp, swidget->spipe->pipe_widget); } else { int j; @@ -242,8 +310,7 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, } } use_count_dec: - if (!use_count_decremented) - swidget->use_count--; + swidget->use_count--; return ret; } @@ -251,7 +318,7 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { guard(mutex)(&swidget->setup_mutex); - return sof_widget_setup_unlocked(sdev, swidget); + return sof_widget_setup_unlocked(sdev, swidget->scomp, swidget); } EXPORT_SYMBOL(sof_widget_setup); @@ -261,12 +328,14 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_widget *src_widget = wsource->dobj.private; struct snd_sof_widget *sink_widget = wsink->dobj.private; + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(src_widget->scomp); struct snd_sof_route *sroute; bool route_found = false; /* ignore routes involving virtual widgets in topology */ - if (is_virtual_widget(sdev, src_widget->widget, __func__) || - is_virtual_widget(sdev, sink_widget->widget, __func__)) + if (is_virtual_widget(src_widget->widget, __func__) || + is_virtual_widget(sink_widget->widget, __func__)) return 0; /* skip route if source/sink widget is not set up */ @@ -274,14 +343,15 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc return 0; /* find route matching source and sink widgets */ - list_for_each_entry(sroute, &sdev->route_list, list) + list_for_each_entry(sroute, &instance->route_list, list) if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) { route_found = true; break; } if (!route_found) { - dev_err(sdev->dev, "error: cannot find SOF route for source %s -> %s sink\n", + dev_err(src_widget->scomp->dev, + "error: cannot find SOF route for source %s -> %s sink\n", wsource->name, wsink->name); return -EINVAL; } @@ -326,9 +396,11 @@ static int sof_set_up_same_dir_widget_routes(struct snd_sof_dev *sdev, return sof_route_setup(sdev, wsource, wsink); } -static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, - struct snd_soc_dapm_widget_list *list, int dir) +static int sof_setup_pipeline_connections(struct snd_soc_dapm_widget_list *list, + struct snd_soc_component *scomp, int dir) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_soc_dapm_widget *widget; struct snd_sof_route *sroute; struct snd_soc_dapm_path *p; @@ -383,7 +455,7 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, * different directions, e.g. a sidetone or an amplifier feedback connected to a speaker * protection module. */ - list_for_each_entry(sroute, &sdev->route_list, list) { + list_for_each_entry(sroute, &instance->route_list, list) { bool src_widget_in_dapm_list, sink_widget_in_dapm_list; if (sroute->setup) @@ -440,7 +512,7 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_soc_dapm_path *p; - if (is_virtual_widget(sdev, widget, __func__)) + if (is_virtual_widget(widget, __func__)) return; if (!swidget) @@ -488,7 +560,7 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget struct snd_soc_dapm_path *p; int ret; - if (is_virtual_widget(sdev, widget, __func__)) + if (is_virtual_widget(widget, __func__)) return 0; if (!swidget) @@ -502,16 +574,15 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget !sof_widget_in_same_direction(swidget, dir)) return 0; - /* skip widgets already prepared or aggregated DAI widgets*/ - if (!widget_ops[widget->id].ipc_prepare || swidget->prepared || - is_aggregated_dai(swidget)) + /* skip widgets aggregated DAI widgets */ + if (!widget_ops[widget->id].ipc_prepare || is_aggregated_dai(swidget)) goto sink_prepare; /* prepare the source widget */ ret = widget_ops[widget->id].ipc_prepare(swidget, fe_params, platform_params, pipeline_params, dir); if (ret < 0) { - dev_err(sdev->dev, "failed to prepare widget %s\n", widget->name); + dev_err(swidget->scomp->dev, "failed to prepare widget %s\n", widget->name); return ret; } @@ -548,16 +619,21 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget * free all widgets in the sink path starting from the source widget * (DAI type for capture, AIF type for playback) */ -static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, - int dir, struct snd_sof_pcm *spcm) +static int sof_free_widgets_in_path_internal(struct snd_sof_dev *sdev, + struct snd_soc_dapm_widget *widget, + int dir, struct snd_sof_pcm *spcm, + struct snd_soc_dapm_widget_list *list) { - struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; struct snd_sof_widget *swidget = widget->dobj.private; struct snd_soc_dapm_path *p; + struct snd_soc_dapm_path *next_p; int err; int ret = 0; - if (is_virtual_widget(sdev, widget, __func__)) + if (!list) + return 0; + + if (is_virtual_widget(widget, __func__)) return 0; if (!swidget) @@ -576,23 +652,52 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap ret = err; sink_free: /* free all widgets in the sink paths even in case of error to keep use counts balanced */ - snd_soc_dapm_widget_for_each_sink_path(widget, p) { + snd_soc_dapm_widget_for_each_path_safe(widget, SND_SOC_DAPM_DIR_IN, p, next_p) { if (!p->walking) { + if (!p->sink) + continue; + if (!widget_in_list(list, p->sink)) continue; p->walking = true; - err = sof_free_widgets_in_path(sdev, p->sink, dir, spcm); + err = sof_free_widgets_in_path_internal(sdev, p->sink, + dir, spcm, list); if (err < 0) ret = err; - p->walking = false; } } return ret; } +static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, + struct snd_soc_dapm_widget *widget, + int dir, struct snd_sof_pcm *spcm) +{ + return sof_free_widgets_in_path_internal(sdev, widget, dir, spcm, + spcm->stream[dir].list); +} + +static void sof_reset_path_walking_flags(struct snd_soc_dapm_widget_list *list) +{ + struct snd_soc_dapm_widget *widget; + struct snd_soc_dapm_path *p; + int i; + + if (!list) + return; + + for_each_dapm_widgets(list, i, widget) { + snd_soc_dapm_widget_for_each_sink_path(widget, p) + p->walking = false; + + snd_soc_dapm_widget_for_each_source_path(widget, p) + p->walking = false; + } +} + /* * set up all widgets in the sink path starting from the source widget * (DAI type for capture, AIF type for playback). @@ -608,7 +713,7 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d struct snd_soc_dapm_path *p; int ret; - if (is_virtual_widget(sdev, widget, __func__)) + if (is_virtual_widget(widget, __func__)) return 0; if (swidget) { @@ -723,15 +828,21 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, sof_unprepare_widgets_in_path(sdev, widget, list, dir); break; default: - dev_err(sdev->dev, "Invalid widget op %d\n", op); + dev_err(spcm->scomp->dev, "Invalid widget op %d\n", op); return -EINVAL; } if (ret < 0) { - dev_err(sdev->dev, "Failed to %s connected widgets\n", str); + if (op == SOF_WIDGET_FREE) + sof_reset_path_walking_flags(list); + + dev_err(spcm->scomp->dev, "Failed to %s connected widgets\n", str); return ret; } } + if (op == SOF_WIDGET_FREE) + sof_reset_path_walking_flags(list); + return 0; } @@ -786,7 +897,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, * error in setting pipeline connections will result in route status being reset for * routes that were successfully set up when the widgets are freed. */ - ret = sof_setup_pipeline_connections(sdev, list, dir); + ret = sof_setup_pipeline_connections(list, spcm->scomp, dir); if (ret < 0) goto widget_free; @@ -801,7 +912,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, spipe = swidget->spipe; if (!spipe) { - dev_err(sdev->dev, "no pipeline found for %s\n", + dev_err(swidget->scomp->dev, "no pipeline found for %s\n", swidget->widget->name); ret = -EINVAL; goto widget_free; @@ -809,7 +920,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, pipe_widget = spipe->pipe_widget; if (!pipe_widget) { - dev_err(sdev->dev, "error: no pipeline widget found for %s\n", + dev_err(swidget->scomp->dev, "error: no pipeline widget found for %s\n", swidget->widget->name); ret = -EINVAL; goto widget_free; @@ -864,12 +975,13 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int */ bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev) { + struct snd_sof_audio_instance *instance; struct snd_pcm_substream *substream; struct snd_sof_pcm *spcm; bool d0i3_compatible_active = false; int dir; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_spcm_in_instances(spcm, sdev, instance) { for_each_pcm_streams(dir) { substream = spcm->stream[dir].substream; if (!substream || !substream->runtime) @@ -893,9 +1005,10 @@ EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active); bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev) { + struct snd_sof_audio_instance *instance; struct snd_sof_pcm *spcm; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_spcm_in_instances(spcm, sdev, instance) { if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored || spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored) return true; @@ -911,10 +1024,10 @@ bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev) struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp, const char *name) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_pcm *spcm; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + list_for_each_entry(spcm, &instance->pcm_list, list) { /* match with PCM dai name */ if (strcmp(spcm->pcm.dai_name, name) == 0) return spcm; @@ -937,11 +1050,20 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, unsigned int comp_id, int *direction) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); + + return snd_sof_find_spcm_comp_by_sdev(sdev, comp_id, direction); +} + +struct snd_sof_pcm *snd_sof_find_spcm_comp_by_sdev(struct snd_sof_dev *sdev, + unsigned int comp_id, + int *direction) +{ + struct snd_sof_audio_instance *instance; struct snd_sof_pcm *spcm; int dir; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_spcm_in_instances(spcm, sdev, instance) { for_each_pcm_streams(dir) { if (spcm->stream[dir].comp_id == comp_id) { *direction = dir; @@ -956,10 +1078,10 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp, const char *name) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *swidget; - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (strcmp(name, swidget->widget->name) == 0) return swidget; } @@ -972,7 +1094,7 @@ struct snd_sof_widget * snd_sof_find_swidget_sname(struct snd_soc_component *scomp, const char *pcm_name, int dir) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *swidget; enum snd_soc_dapm_type type; @@ -981,7 +1103,7 @@ snd_sof_find_swidget_sname(struct snd_soc_component *scomp, else type = snd_soc_dapm_aif_out; - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (!strcmp(pcm_name, swidget->widget->sname) && swidget->id == type) return swidget; @@ -993,10 +1115,10 @@ snd_sof_find_swidget_sname(struct snd_soc_component *scomp, struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp, const char *name) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_dai *dai; - list_for_each_entry(dai, &sdev->dai_list, list) { + list_for_each_entry(dai, &instance->dai_list, list) { if (dai->name && (strcmp(name, dai->name) == 0)) return dai; } @@ -1010,7 +1132,7 @@ static int sof_dai_get_param(struct snd_soc_pcm_runtime *rtd, int param_type) snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); /* use the tplg configured mclk if existed */ @@ -1052,3 +1174,83 @@ int sof_dai_get_tdm_slots(struct snd_soc_pcm_runtime *rtd) return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS); } EXPORT_SYMBOL(sof_dai_get_tdm_slots); + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) +static void sof_set_transferred_bytes(struct sof_compr_stream *sstream, + u64 host_pos, u64 buffer_size) +{ + u64 prev_pos; + unsigned int copied; + + div64_u64_rem(sstream->copied_total, buffer_size, &prev_pos); + + if (host_pos < prev_pos) + copied = (buffer_size - prev_pos) + host_pos; + else + copied = host_pos - prev_pos; + + sstream->copied_total += copied; +} + +static void snd_sof_compr_fragment_elapsed_work(struct work_struct *work) +{ + struct snd_sof_pcm_stream *sps = container_of(work, struct snd_sof_pcm_stream, + period_elapsed_work); + + snd_compr_fragment_elapsed(sps->cstream); +} + +void snd_sof_compr_init_elapsed_work(struct work_struct *work) +{ + INIT_WORK(work, snd_sof_compr_fragment_elapsed_work); +} + +/* + * sof compr fragment elapse, this could be called in irq thread context + */ +void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd; + struct snd_compr_runtime *crtd; + struct snd_soc_component *component; + struct sof_compr_stream *sstream; + struct snd_sof_pcm *spcm; + + if (!cstream) + return; + + rtd = cstream->private_data; + crtd = cstream->runtime; + sstream = crtd->private_data; + component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) { + dev_err(component->dev, "fragment elapsed called for unknown stream!\n"); + return; + } + + sof_set_transferred_bytes(sstream, spcm->stream[cstream->direction].posn.host_posn, + crtd->buffer_size); + + /* use the same workqueue-based solution as for PCM, cf. snd_sof_pcm_elapsed */ + schedule_work(&spcm->stream[cstream->direction].period_elapsed_work); +} + +int snd_sof_compr_create_page_table(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + unsigned char *dma_area, size_t size) +{ + struct snd_dma_buffer *dmab = cstream->runtime->dma_buffer_p; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + int dir = cstream->direction; + struct snd_sof_pcm *spcm; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return -EINVAL; + + return snd_sof_create_page_table(component->dev, dmab, + spcm->stream[dir].page_table.area, size); +} +#endif diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 138e5fcc2dd094..77fc292566f1b2 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -43,6 +43,25 @@ #define WIDGET_IS_AIF(id) ((id) == snd_soc_dapm_aif_in || (id) == snd_soc_dapm_aif_out) #define WIDGET_IS_AIF_OR_DAI(id) (WIDGET_IS_DAI(id) || WIDGET_IS_AIF(id)) #define WIDGET_IS_COPIER(id) (WIDGET_IS_AIF_OR_DAI(id) || (id) == snd_soc_dapm_buffer) +#define WIDGET_IS_PROCESS(id) ((id) == snd_soc_dapm_effect || \ + (id) == snd_soc_dapm_decoder || \ + (id) == snd_soc_dapm_encoder) + +#define for_each_slink_in_instances(slink, sdev, instance) \ + list_for_each_entry(instance, &(sdev)->audio_instance_list, list) \ + list_for_each_entry(slink, &(instance)->dai_link_list, list) + +#define for_each_sroute_in_instances(sroute, sdev, instance) \ + list_for_each_entry(instance, &(sdev)->audio_instance_list, list) \ + list_for_each_entry(sroute, &(instance)->route_list, list) + +#define for_each_spcm_in_instances(spcm, sdev, instance) \ + list_for_each_entry(instance, &(sdev)->audio_instance_list, list) \ + list_for_each_entry(spcm, &(instance)->pcm_list, list) + +#define for_each_swidget_in_instances(swidget, sdev, instance) \ + list_for_each_entry(instance, &(sdev)->audio_instance_list, list) \ + list_for_each_entry(swidget, &(instance)->widget_list, list) #define SOF_DAI_PARAM_INTEL_SSP_MCLK 0 #define SOF_DAI_PARAM_INTEL_SSP_BCLK 1 @@ -119,14 +138,16 @@ struct snd_sof_dai_config_data { * therefore the host must do the same and should stop the DMA during * hw_free. * @d0i3_supported_in_s0ix: Allow DSP D0I3 during S0iX + * @compress_ops: Pointer to ops for compressed streams */ struct sof_ipc_pcm_ops { int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params); - int (*hw_free)(struct snd_soc_component *component, struct snd_pcm_substream *substream); - int (*trigger)(struct snd_soc_component *component, struct snd_pcm_substream *substream, - int cmd); + int (*hw_free)(struct snd_soc_component *component, struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, int dir); + int (*trigger)(struct snd_soc_component *component, struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, int cmd, int dir); int (*dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); int (*pcm_setup)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); void (*pcm_free)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); @@ -139,6 +160,7 @@ struct sof_ipc_pcm_ops { bool ipc_first_on_start; bool platform_stop_during_hw_free; bool d0i3_supported_in_s0ix; + const struct snd_compress_ops *compress_ops; }; /** @@ -354,6 +376,7 @@ struct snd_sof_pcm { struct snd_sof_pcm_stream stream[2]; struct list_head list; /* list in sdev pcm list */ struct snd_pcm_hw_params params[2]; + struct snd_compr_params cparams[2]; /* applicable for compress devices */ struct snd_sof_platform_stream_params platform_params[2]; bool prepared[2]; /* PCM_PARAMS set successfully */ bool setup_done[2]; /* the setup of the SOF PCM device is done */ @@ -612,14 +635,33 @@ snd_sof_find_swidget_sname(struct snd_soc_component *scomp, struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp, const char *name); +static inline struct sof_client_dev * +snd_sof_component_get_cdev(struct snd_soc_component *scomp) +{ + return snd_soc_component_get_drvdata(scomp); +} + +static inline struct snd_sof_dev * +snd_sof_component_get_sdev(struct snd_soc_component *scomp) +{ + return sof_client_dev_to_sof_dev(snd_sof_component_get_cdev(scomp)); +} + +struct snd_sof_audio_instance * +snd_sof_audio_instance_register(struct snd_sof_dev *sdev, + struct snd_soc_component *component); +void snd_sof_audio_instance_unregister(struct snd_sof_audio_instance *instance); +struct snd_sof_audio_instance * +snd_sof_component_get_audio_instance(struct snd_soc_component *component); + static inline struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_soc_component *scomp, struct snd_soc_pcm_runtime *rtd) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_pcm *spcm; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + list_for_each_entry(spcm, &instance->pcm_list, list) { if (le32_to_cpu(spcm->pcm.dai_id) == rtd->dai_link->id) return spcm; } @@ -632,9 +674,18 @@ struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp, struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, unsigned int comp_id, int *direction); +struct snd_sof_pcm *snd_sof_find_spcm_comp_by_sdev(struct snd_sof_dev *sdev, + unsigned int comp_id, + int *direction); + void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream); void snd_sof_pcm_init_elapsed_work(struct work_struct *work); - +int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, + struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *params, + struct snd_sof_platform_stream_params *platform_params, + int dir); +struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_sof_dev *sdev, + int comp_id); /* * snd_sof_pcm specific wrappers for dev_dbg() and dev_err() to provide * consistent and useful prints. @@ -660,6 +711,9 @@ void snd_sof_pcm_init_elapsed_work(struct work_struct *work); #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream); void snd_sof_compr_init_elapsed_work(struct work_struct *work); +int snd_sof_compr_create_page_table(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + unsigned char *dma_area, size_t size); #else static inline void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream) { } static inline void snd_sof_compr_init_elapsed_work(struct work_struct *work) { } diff --git a/sound/soc/sof/sof-client-audio.c b/sound/soc/sof/sof-client-audio.c new file mode 100644 index 00000000000000..f5b0057926d081 --- /dev/null +++ b/sound/soc/sof/sof-client-audio.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2026 Intel Corporation +// +// Authors: Peter Ujfalusi +// + +#include +#include +#include +#include +#include +#include + +#include "sof-client.h" +#include "sof-client-audio.h" + +#define SOF_AUDIO_SUSPEND_DELAY_MS 3000 + +static void sof_audio_client_init_debugfs(struct sof_client_dev *cdev, + struct sof_audio_client_pdata *pdata) +{ + struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev); + char *debugfs_dir; + + debugfs_dir = devm_kasprintf(&cdev->auxdev.dev, GFP_KERNEL, "audio.%u", + cdev->auxdev.id); + if (!debugfs_dir) + return; + + pdata->debugfs_root = debugfs_create_dir(debugfs_dir, debugfs_root); + if (IS_ERR_OR_NULL(pdata->debugfs_root)) + return; + + pdata->debug_topology_name = pdata->machine.sof_tplg_filename ? + pdata->machine.sof_tplg_filename : + sof_client_get_topology_name(cdev); + pdata->debug_card_name = pdata->machine.mach_params.card_name; + pdata->debug_machine_driver = pdata->machine.drv_name; + + debugfs_create_str("topology_name", 0444, pdata->debugfs_root, + (char **)&pdata->debug_topology_name); + + if (pdata->debug_card_name) + debugfs_create_str("card_name", 0444, pdata->debugfs_root, + (char **)&pdata->debug_card_name); + + if (pdata->debug_machine_driver) + debugfs_create_str("machine_driver", 0444, pdata->debugfs_root, + (char **)&pdata->debug_machine_driver); +} + +static int sof_audio_client_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_audio_client_pdata *pdata = dev_get_platdata(&auxdev->dev); + int ret; + + auxiliary_set_drvdata(auxdev, cdev); + + ret = snd_soc_register_component(&auxdev->dev, &pdata->plat_drv, + pdata->drv, pdata->num_drv); + if (ret < 0) + return ret; + + ret = sof_client_machine_register(cdev); + if (ret < 0) { + snd_soc_unregister_component(&auxdev->dev); + return ret; + } + + sof_audio_client_init_debugfs(cdev, pdata); + + pm_runtime_set_autosuspend_delay(&auxdev->dev, SOF_AUDIO_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(&auxdev->dev); + pm_runtime_enable(&auxdev->dev); + pm_runtime_mark_last_busy(&auxdev->dev); + pm_runtime_idle(&auxdev->dev); + + return 0; +} + +static void sof_audio_client_remove(struct auxiliary_device *auxdev) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_audio_client_pdata *pdata = dev_get_platdata(&auxdev->dev); + + if (pdata && pdata->debugfs_root) + debugfs_remove_recursive(pdata->debugfs_root); + + pm_runtime_disable(&auxdev->dev); + + sof_client_machine_unregister(cdev); + snd_soc_unregister_component(&auxdev->dev); +} + +static const struct auxiliary_device_id sof_audio_client_id_table[] = { + { .name = "snd_sof.audio", }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, sof_audio_client_id_table); + +static struct auxiliary_driver sof_audio_client_drv = { + .probe = sof_audio_client_probe, + .remove = sof_audio_client_remove, + .id_table = sof_audio_client_id_table, +}; + +module_auxiliary_driver(sof_audio_client_drv); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("SOF Audio Client Driver"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); diff --git a/sound/soc/sof/sof-client-audio.h b/sound/soc/sof/sof-client-audio.h new file mode 100644 index 00000000000000..9db10aa7889ada --- /dev/null +++ b/sound/soc/sof/sof-client-audio.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOF_CLIENT_AUDIO_H +#define __SOF_CLIENT_AUDIO_H + +#include + +struct dentry; + +/** + * struct sof_audio_client_pdata - platform data for the audio sof-client + * @plat_drv: Pre-built ASoC component driver + * @drv: Array of DAI drivers to register + * @num_drv: Number of DAI drivers + * @machine: Per-instance copy of the machine descriptor + * @debugfs_root: Per-audio-client debugfs directory + * @debug_topology_name: Topology loaded by this audio client + * @debug_card_name: Card name for this audio client + * @debug_machine_driver: Machine driver bound to this audio client + */ +struct sof_audio_client_pdata { + struct snd_soc_component_driver plat_drv; + struct snd_soc_dai_driver *drv; + int num_drv; + struct snd_soc_acpi_mach machine; + struct dentry *debugfs_root; + const char *debug_topology_name; + const char *debug_card_name; + const char *debug_machine_driver; +}; + +#endif /* __SOF_CLIENT_AUDIO_H */ diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index a90d8243d71a7a..d6db4edda85741 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -11,10 +11,12 @@ #include #include #include +#include #include #include #include "ops.h" #include "sof-client.h" +#include "sof-client-audio.h" #include "sof-priv.h" #include "ipc3-priv.h" #include "ipc4-priv.h" @@ -190,6 +192,16 @@ int sof_register_clients(struct snd_sof_dev *sdev) { int ret; + /* Register audio client (needed in dspless mode too) */ + if (sof_ops(sdev) && sof_ops(sdev)->register_audio_client) { + ret = sof_ops(sdev)->register_audio_client(sdev); + if (ret < 0) { + dev_err(sdev->dev, + "audio client registration failed: %d\n", ret); + return ret; + } + } + if (sdev->dspless_mode_selected) return 0; @@ -197,7 +209,7 @@ int sof_register_clients(struct snd_sof_dev *sdev) ret = sof_register_ipc_flood_test(sdev); if (ret) { dev_err(sdev->dev, "IPC flood test client registration failed\n"); - return ret; + goto err_audio; } ret = sof_register_ipc_msg_injector(sdev); @@ -237,6 +249,10 @@ int sof_register_clients(struct snd_sof_dev *sdev) err_msg_injector: sof_unregister_ipc_flood_test(sdev); +err_audio: + if (sof_ops(sdev) && sof_ops(sdev)->unregister_audio_client) + sof_ops(sdev)->unregister_audio_client(sdev); + return ret; } @@ -249,6 +265,10 @@ void sof_unregister_clients(struct snd_sof_dev *sdev) sof_unregister_ipc_msg_injector(sdev); sof_unregister_ipc_flood_test(sdev); sof_unregister_fw_gdb(sdev); + + /* Audio client last (reverse of registration order) */ + if (sof_ops(sdev) && sof_ops(sdev)->unregister_audio_client) + sof_ops(sdev)->unregister_audio_client(sdev); } int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, @@ -515,6 +535,14 @@ enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev) } EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_type, "SND_SOC_SOF_CLIENT"); +const char *sof_client_get_topology_name(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->pdata->tplg_filename; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_topology_name, "SND_SOC_SOF_CLIENT"); + int sof_client_boot_dsp(struct sof_client_dev *cdev) { return snd_sof_boot_dsp_firmware(sof_client_dev_to_sof_dev(cdev)); @@ -612,7 +640,7 @@ void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct sof_ipc_event_entry *event; - guard(mutex)(&sdev->ipc_client_mutex); + guard(mutex)(&sdev->client_event_handler_mutex); list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) { @@ -629,7 +657,7 @@ void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) { struct sof_state_event_entry *event; - guard(mutex)(&sdev->ipc_client_mutex); + guard(mutex)(&sdev->client_event_handler_mutex); list_for_each_entry(event, &sdev->fw_state_handler_list, list) event->callback(event->cdev, sdev->fw_state); @@ -664,7 +692,7 @@ void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev) struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct sof_state_event_entry *event; - guard(mutex)(&sdev->ipc_client_mutex); + guard(mutex)(&sdev->client_event_handler_mutex); list_for_each_entry(event, &sdev->fw_state_handler_list, list) { if (event->cdev == cdev) { @@ -705,3 +733,99 @@ void sof_client_mailbox_write(struct sof_client_dev *cdev, u32 offset, sof_mailbox_write(sof_client_dev_to_sof_dev(cdev), offset, message, bytes); } EXPORT_SYMBOL_NS_GPL(sof_client_mailbox_write, "SND_SOC_SOF_CLIENT"); + +bool sof_client_is_dspless(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->dspless_mode_selected; +} +EXPORT_SYMBOL_NS_GPL(sof_client_is_dspless, "SND_SOC_SOF_CLIENT"); + +int sof_client_get_num_cores(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->num_cores; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_num_cores, "SND_SOC_SOF_CLIENT"); + +int sof_client_machine_register(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + struct snd_sof_pdata *spdata = sdev->pdata; + struct snd_soc_acpi_mach *mach; + struct platform_device *pdev; + + /* Per-client machine: register a dedicated machine device */ + if (pdata->machine.drv_name) { + mach = &pdata->machine; + mach->mach_params.platform = dev_name(&cdev->auxdev.dev); + + pdev = platform_device_register_data(sdev->dev, + mach->drv_name, + PLATFORM_DEVID_AUTO, + mach, sizeof(*mach)); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + pdata->plat_drv.ignore_machine = dev_name(&pdev->dev); + cdev->data = pdev; + + return 0; + } + + /* + * Legacy: use shared machine from sdev. + * Update the platform name to match the audio component's device so + * that machine drivers can find the SOF platform component. + */ + mach = (struct snd_soc_acpi_mach *)spdata->machine; + if (mach) + mach->mach_params.platform = dev_name(&cdev->auxdev.dev); + + return snd_sof_machine_register(sdev, spdata); +} +EXPORT_SYMBOL_NS_GPL(sof_client_machine_register, "SND_SOC_SOF_CLIENT"); + +void sof_client_machine_unregister(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct platform_device *pdev = cdev->data; + + if (pdev) { + platform_device_unregister(pdev); + return; + } + + snd_sof_machine_unregister(sdev, sdev->pdata); +} +EXPORT_SYMBOL_NS_GPL(sof_client_machine_unregister, "SND_SOC_SOF_CLIENT"); + +void sof_audio_client_init_pdata(struct snd_sof_dev *sdev, + struct sof_audio_client_pdata *pdata) +{ + memset(pdata, 0, sizeof(*pdata)); + snd_sof_new_platform_drv(sdev, &pdata->plat_drv); + pdata->drv = sdev->audio_ops->drv; + pdata->num_drv = sdev->audio_ops->num_drv; +} +EXPORT_SYMBOL_NS_GPL(sof_audio_client_init_pdata, "SND_SOC_SOF_CLIENT"); + +int sof_register_audio_client(struct snd_sof_dev *sdev) +{ + struct sof_audio_client_pdata pdata; + + sof_audio_client_init_pdata(sdev, &pdata); + + return sof_client_dev_register(sdev, "audio", 0, + &pdata, sizeof(pdata)); +} +EXPORT_SYMBOL_NS_GPL(sof_register_audio_client, "SND_SOC_SOF_CLIENT"); + +void sof_unregister_audio_client(struct snd_sof_dev *sdev) +{ + sof_client_dev_unregister(sdev, "audio", 0); +} +EXPORT_SYMBOL_NS_GPL(sof_unregister_audio_client, "SND_SOC_SOF_CLIENT"); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index e796801570c823..fbb2fb3e4806fc 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -49,6 +49,7 @@ struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev); const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev); size_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev); enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev); +const char *sof_client_get_topology_name(struct sof_client_dev *cdev); /* DSP/firmware boot request */ int sof_client_boot_dsp(struct sof_client_dev *cdev); @@ -84,4 +85,20 @@ void sof_client_mailbox_write(struct sof_client_dev *cdev, u32 offset, ssize_t sof_client_ipc4_find_debug_slot_offset_by_type(struct sof_client_dev *cdev, u32 type); +bool sof_client_is_dspless(struct sof_client_dev *cdev); +int sof_client_get_num_cores(struct sof_client_dev *cdev); + +/* machine driver registration */ +int sof_client_machine_register(struct sof_client_dev *cdev); +void sof_client_machine_unregister(struct sof_client_dev *cdev); + +/* audio client pdata initialization */ +struct sof_audio_client_pdata; +void sof_audio_client_init_pdata(struct snd_sof_dev *sdev, + struct sof_audio_client_pdata *pdata); + +/* default audio client registration for vendor ops */ +int sof_register_audio_client(struct snd_sof_dev *sdev); +void sof_unregister_audio_client(struct snd_sof_dev *sdev); + #endif /* __SOC_SOF_CLIENT_H */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 3b6ec134d37988..759c2f782f962c 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -127,6 +127,74 @@ struct snd_sof_debugfs_map; struct snd_soc_tplg_ops; struct snd_soc_component; struct snd_sof_pdata; +struct snd_sof_platform_stream_params; + +/* + * SOF platform-specific audio operations. + * + * These callbacks handle platform-specific stream management (host DMA, + * stream allocation, etc.) and are provided per audio instance. + * All callbacks receive snd_soc_component to identify the instance, + * platform code internally resolves to the hardware device as needed. + */ +struct sof_audio_ops { + /* host stream management */ + int (*pcm_open)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + int (*pcm_close)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + int (*pcm_hw_params)(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_sof_platform_stream_params *platform_params); + int (*pcm_hw_free)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + int (*pcm_trigger)(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd); + snd_pcm_uframes_t (*pcm_pointer)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + int (*pcm_ack)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + + /* compressed streams */ + int (*compr_open)(struct snd_soc_component *component, + struct snd_compr_stream *cstream); + int (*compr_close)(struct snd_soc_component *component, + struct snd_compr_stream *cstream); + int (*compr_hw_params)(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_params *params, + struct snd_sof_platform_stream_params *platform_params); + int (*compr_hw_free)(struct snd_soc_component *component, + struct snd_compr_stream *cstream); + int (*compr_trigger)(struct snd_soc_component *component, + struct snd_compr_stream *cstream, int cmd); + int (*compr_pointer)(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp64 *tstamp); + + /* DAI drivers */ + struct snd_soc_dai_driver *drv; + int num_drv; + + /* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */ + u32 hw_info; +}; + +/* Per-component SOF audio integration state. */ +struct snd_sof_audio_instance { + struct snd_sof_dev *sdev; + struct snd_soc_component *component; + const struct sof_audio_ops *audio_ops; + struct list_head pipeline_list; + struct list_head dai_list; + struct list_head dai_link_list; + struct list_head route_list; + struct list_head pcm_list; + struct list_head kcontrol_list; + struct list_head widget_list; + struct list_head list; +}; /** * struct snd_sof_platform_stream_params - platform dependent stream parameters @@ -225,41 +293,16 @@ struct snd_sof_dsp_ops { /* ipc */ int (*send_msg)(struct snd_sof_dev *sof_dev, struct snd_sof_ipc_msg *msg); /* mandatory */ + void (*msg_timeout_handler)(struct snd_sof_dev *sof_dev, + struct snd_sof_ipc_msg *msg); /* optional */ /* FW loading */ int (*load_firmware)(struct snd_sof_dev *sof_dev, const char *fw_filename); /* mandatory */ int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr); /* optional */ - /* connect pcm substream to a host stream */ - int (*pcm_open)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream); /* optional */ - /* disconnect pcm substream to a host stream */ - int (*pcm_close)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream); /* optional */ - - /* host stream hw params */ - int (*pcm_hw_params)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_sof_platform_stream_params *platform_params); /* optional */ - - /* host stream hw_free */ - int (*pcm_hw_free)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream); /* optional */ - - /* host stream trigger */ - int (*pcm_trigger)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - int cmd); /* optional */ - - /* host stream pointer */ - snd_pcm_uframes_t (*pcm_pointer)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream); /* optional */ - - /* pcm ack */ - int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */ - + u64 (*compr_get_dai_frame_counter)(struct snd_sof_dev *sdev, + struct snd_compr_stream *cstream); /* * optional callback to retrieve the number of frames left/arrived from/to * the DSP on the DAI side (link/codec/DMIC/etc). @@ -352,15 +395,13 @@ struct snd_sof_dsp_ops { int (*register_ipc_clients)(struct snd_sof_dev *sdev); /* optional */ void (*unregister_ipc_clients)(struct snd_sof_dev *sdev); /* optional */ - /* DAI ops */ - struct snd_soc_dai_driver *drv; - int num_drv; + /* audio client ops */ + int (*register_audio_client)(struct snd_sof_dev *sdev); /* mandatory */ + void (*unregister_audio_client)(struct snd_sof_dev *sdev); /* mandatory */ + /* DAI ops */ 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 */ - u32 hw_info; - const struct dsp_arch_ops *dsp_arch_ops; }; @@ -575,12 +616,6 @@ struct snd_sof_dev { /* Main, Base firmware image */ struct sof_firmware basefw; - /* - * ASoC components. plat_drv fields are set dynamically so - * can't use const - */ - struct snd_soc_component_driver plat_drv; - /* current DSP power state */ struct sof_dsp_power_state dsp_power_state; /* mutex to protect the dsp_power_state access */ @@ -634,14 +669,9 @@ struct snd_sof_dev { /* topology */ struct snd_soc_tplg_ops *tplg_ops; - struct list_head pcm_list; - struct list_head kcontrol_list; - struct list_head widget_list; - struct list_head pipeline_list; - struct list_head dai_list; - struct list_head dai_link_list; - struct list_head route_list; - struct snd_soc_component *component; + const struct sof_audio_ops *audio_ops; + struct list_head audio_instance_list; + spinlock_t audio_instance_list_lock; u32 enabled_cores_mask; /* keep track of enabled cores */ bool led_present; @@ -723,7 +753,8 @@ int snd_sof_prepare(struct device *dev); void snd_sof_complete(struct device *dev); int snd_sof_boot_dsp_firmware(struct snd_sof_dev *sdev); -void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); +void snd_sof_new_platform_drv(struct snd_sof_dev *sdev, + struct snd_soc_component_driver * const pd); /* * Compress support @@ -851,9 +882,9 @@ int sof_set_stream_data_offset(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps, size_t posn_offset); -int sof_stream_pcm_open(struct snd_sof_dev *sdev, +int sof_stream_pcm_open(struct snd_soc_component *component, struct snd_pcm_substream *substream); -int sof_stream_pcm_close(struct snd_sof_dev *sdev, +int sof_stream_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream); /* SOF client support */ diff --git a/sound/soc/sof/stream-ipc.c b/sound/soc/sof/stream-ipc.c index 78758cf58f0142..44c0f98e2840ab 100644 --- a/sound/soc/sof/stream-ipc.c +++ b/sound/soc/sof/stream-ipc.c @@ -96,7 +96,7 @@ int sof_set_stream_data_offset(struct snd_sof_dev *sdev, } EXPORT_SYMBOL(sof_set_stream_data_offset); -int sof_stream_pcm_open(struct snd_sof_dev *sdev, +int sof_stream_pcm_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct sof_stream *stream = kmalloc_obj(*stream); @@ -118,7 +118,7 @@ int sof_stream_pcm_open(struct snd_sof_dev *sdev, } EXPORT_SYMBOL(sof_stream_pcm_open); -int sof_stream_pcm_close(struct snd_sof_dev *sdev, +int sof_stream_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct sof_stream *stream = substream->runtime->private_data; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 8fc7726aec29d0..3fb10de64c569d 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -17,6 +17,8 @@ #include #include "sof-priv.h" #include "sof-audio.h" +#include "sof-client.h" +#include "sof-client-audio.h" #include "ops.h" static bool disable_function_topology; @@ -64,7 +66,7 @@ int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum so struct snd_sof_tuple *tuples, int num_tuples, size_t object_size, int token_instance_num) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); const struct sof_token_info *token_list; const struct sof_topology_token *tokens; @@ -278,7 +280,7 @@ static int set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size) { struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->set_up_volume_table) @@ -496,7 +498,7 @@ static int sof_parse_uuid_tokens(struct snd_soc_component *scomp, /** * sof_copy_tuples - Parse tokens and copy them to the @tuples array - * @sdev: pointer to struct snd_sof_dev + * @scomp: pointer to struct snd_soc_component * @array: source pointer to consecutive vendor arrays in topology * @array_size: size of @array * @token_id: Token ID associated with a token array @@ -508,10 +510,12 @@ static int sof_parse_uuid_tokens(struct snd_soc_component *scomp, * @num_copied_tuples: pointer to the number of copied tuples in the tuples array * */ -static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_array *array, +static int sof_copy_tuples(struct snd_soc_component *scomp, + struct snd_soc_tplg_vendor_array *array, int array_size, u32 token_id, int token_instance_num, struct snd_sof_tuple *tuples, int tuples_size, int *num_copied_tuples) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); const struct sof_token_info *token_list; const struct sof_topology_token *tokens; @@ -525,7 +529,7 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_ return 0; if (!tuples || !num_copied_tuples) { - dev_err(sdev->dev, "Invalid tuples array\n"); + dev_err(scomp->dev, "Invalid tuples array\n"); return -EINVAL; } @@ -533,13 +537,13 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_ num_tokens = token_list[token_id].count; if (!tokens) { - dev_err(sdev->dev, "No token array defined for token ID: %d\n", token_id); + dev_err(scomp->dev, "No token array defined for token ID: %d\n", token_id); return -EINVAL; } /* check if there's space in the tuples array for new tokens */ if (*num_copied_tuples >= tuples_size) { - dev_err(sdev->dev, "No space in tuples array for new tokens from %s", + dev_err(scomp->dev, "No space in tuples array for new tokens from %s", token_list[token_id].name); return -EINVAL; } @@ -549,14 +553,14 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_ /* validate asize */ if (asize < 0) { - dev_err(sdev->dev, "Invalid array size 0x%x\n", asize); + dev_err(scomp->dev, "Invalid array size 0x%x\n", asize); return -EINVAL; } /* make sure there is enough data before parsing */ array_size -= asize; if (array_size < 0) { - dev_err(sdev->dev, "Invalid array size 0x%x\n", asize); + dev_err(scomp->dev, "Invalid array size 0x%x\n", asize); return -EINVAL; } @@ -583,7 +587,7 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_ tuples[*num_copied_tuples].token = tokens[j].token; tuples[*num_copied_tuples].value.s = - devm_kasprintf(sdev->dev, GFP_KERNEL, + devm_kasprintf(scomp->dev, GFP_KERNEL, "%s", elem->string); if (!tuples[*num_copied_tuples].value.s) return -ENOMEM; @@ -839,7 +843,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, struct snd_kcontrol_new *kc, struct snd_soc_tplg_ctl_hdr *hdr) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_soc_tplg_mixer_control *mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); int tlv[SOF_TLV_ITEMS]; @@ -919,7 +923,7 @@ static int sof_control_load_enum(struct snd_soc_component *scomp, struct snd_kcontrol_new *kc, struct snd_soc_tplg_ctl_hdr *hdr) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_soc_tplg_enum_control *ec = container_of(hdr, struct snd_soc_tplg_enum_control, hdr); @@ -941,7 +945,7 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, struct snd_kcontrol_new *kc, struct snd_soc_tplg_ctl_hdr *hdr) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_soc_tplg_bytes_control *control = container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value; @@ -972,7 +976,7 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, struct soc_mixer_control *sm; struct soc_bytes_ext *sbe; struct soc_enum *se; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_soc_dobj *dobj; struct snd_sof_control *scontrol; int ret; @@ -1040,14 +1044,14 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, scontrol->led_ctl.led_value = -1; dobj->private = scontrol; - list_add(&scontrol->list, &sdev->kcontrol_list); + list_add(&scontrol->list, &instance->kcontrol_list); return 0; } static int sof_control_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_control *scontrol = dobj->private; int ret = 0; @@ -1182,7 +1186,7 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, int dir) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_widget *host_widget; if (sdev->dspless_mode_selected) @@ -1220,7 +1224,7 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s struct snd_soc_tplg_dapm_widget *tw, enum sof_tokens *object_token_list, int count) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_soc_tplg_private *private = &tw->priv; const struct sof_token_info *token_list; @@ -1276,7 +1280,7 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_INPUT_AUDIO_FORMATS, swidget->tuples, swidget->num_tuples); if (num_sets < 0) { - dev_err(sdev->dev, "Invalid input audio format count for %s\n", + dev_err(scomp->dev, "Invalid input audio format count for %s\n", swidget->widget->name); ret = num_sets; goto err; @@ -1286,7 +1290,7 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_OUTPUT_AUDIO_FORMATS, swidget->tuples, swidget->num_tuples); if (num_sets < 0) { - dev_err(sdev->dev, "Invalid output audio format count for %s\n", + dev_err(scomp->dev, "Invalid output audio format count for %s\n", swidget->widget->name); ret = num_sets; goto err; @@ -1311,7 +1315,7 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s } /* copy one set of tuples per token ID into swidget->tuples */ - ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + ret = sof_copy_tuples(scomp, private->array, le32_to_cpu(private->size), object_token_list[i], num_sets, swidget->tuples, num_tuples, &swidget->num_tuples); if (ret < 0) { @@ -1419,7 +1423,8 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_soc_tplg_private *priv = &tw->priv; @@ -1429,6 +1434,9 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, int token_list_size = 0; int ret = 0; + if (!instance) + return -EINVAL; + swidget = kzalloc_obj(*swidget); if (!swidget) return -ENOMEM; @@ -1518,9 +1526,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, kfree(dai); break; } - list_add(&dai->list, &sdev->dai_list); + list_add(&dai->list, &instance->dai_list); swidget->private = dai; break; + case snd_soc_dapm_decoder: + case snd_soc_dapm_encoder: case snd_soc_dapm_effect: /* check we have some tokens - we need at least process type */ if (le32_to_cpu(tw->priv.size) == 0) { @@ -1610,11 +1620,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, spipe->pipe_widget = swidget; swidget->spipe = spipe; - list_add(&spipe->list, &sdev->pipeline_list); + list_add(&spipe->list, &instance->pipeline_list); } w->dobj.private = swidget; - list_add(&swidget->list, &sdev->widget_list); + list_add(&swidget->list, &instance->widget_list); return ret; free: kfree(swidget->private); @@ -1644,7 +1654,7 @@ 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); const struct sof_ipc_tplg_widget_ops *widget_ops; const struct snd_kcontrol_new *kc; @@ -1743,7 +1753,8 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_soc_tplg_stream_caps *caps; struct snd_soc_tplg_private *private = &pcm->priv; @@ -1782,7 +1793,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, } dai_drv->dobj.private = spcm; - list_add(&spcm->list, &sdev->pcm_list); + list_add(&spcm->list, &instance->pcm_list); ret = sof_parse_tokens(scomp, spcm, stream_tokens, ARRAY_SIZE(stream_tokens), private->array, @@ -1858,7 +1869,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, static int sof_dai_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_pcm *spcm = dobj->private; @@ -1889,7 +1900,8 @@ static const struct sof_topology_token common_dai_link_tokens[] = { static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_soc_tplg_private *private = &cfg->priv; const struct sof_token_info *token_list; @@ -2016,7 +2028,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ if (token_list[SOF_DAI_LINK_TOKENS].tokens) { /* parse one set of DAI link tokens */ - ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + ret = sof_copy_tuples(scomp, private->array, le32_to_cpu(private->size), SOF_DAI_LINK_TOKENS, 1, slink->tuples, num_tuples, &slink->num_tuples); if (ret < 0) { @@ -2031,7 +2043,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ goto out; /* parse "num_sets" sets of DAI-specific tokens */ - ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + ret = sof_copy_tuples(scomp, private->array, le32_to_cpu(private->size), token_id, num_sets, slink->tuples, num_tuples, &slink->num_tuples); if (ret < 0) { dev_err(scomp->dev, "failed to parse %s for dai link %s\n", @@ -2045,12 +2057,12 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ slink->tuples, slink->num_tuples); if (num_sets < 0) { - dev_err(sdev->dev, "Invalid active PDM count for %s\n", link->name); + dev_err(scomp->dev, "Invalid active PDM count for %s\n", link->name); ret = num_sets; goto err; } - ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + ret = sof_copy_tuples(scomp, private->array, le32_to_cpu(private->size), SOF_DMIC_PDM_TOKENS, num_sets, slink->tuples, num_tuples, &slink->num_tuples); if (ret < 0) { @@ -2061,7 +2073,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ } out: link->dobj.private = slink; - list_add(&slink->list, &sdev->dai_link_list); + list_add(&slink->list, &instance->dai_link_list); return 0; @@ -2093,7 +2105,7 @@ static int sof_link_unload(struct snd_soc_component *scomp, struct snd_soc_dobj static int sof_route_load(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_route *route) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *source_swidget, *sink_swidget; struct snd_soc_dobj *dobj = &route->dobj; struct snd_sof_route *sroute; @@ -2151,7 +2163,7 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, sroute->sink_widget = sink_swidget; /* add route to route list */ - list_add(&sroute->list, &sdev->route_list); + list_add(&sroute->list, &instance->route_list); return 0; err: @@ -2172,15 +2184,17 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, static int sof_set_widget_pipeline(struct snd_sof_dev *sdev, struct snd_sof_pipeline *spipe, struct snd_sof_widget *swidget) { + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(swidget->scomp); struct snd_sof_widget *pipe_widget = spipe->pipe_widget; struct snd_sof_control *scontrol; if (pipe_widget->dynamic_pipeline_widget) { /* dynamic widgets cannot have volatile kcontrols */ - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) + list_for_each_entry(scontrol, &instance->kcontrol_list, list) if (scontrol->comp_id == swidget->comp_id && (scontrol->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE)) { - dev_err(sdev->dev, + dev_err(swidget->scomp->dev, "error: volatile control found for dynamic widget %s\n", swidget->widget->name); return -EINVAL; @@ -2197,28 +2211,32 @@ static int sof_set_widget_pipeline(struct snd_sof_dev *sdev, struct snd_sof_pipe /* completion - called at completion of firmware loading */ static int sof_complete(struct snd_soc_component *scomp) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_sof_control *scontrol; struct snd_sof_pipeline *spipe; int ret; + if (!instance) + return -EINVAL; + widget_ops = tplg_ops ? tplg_ops->widget : NULL; /* first update all control IPC structures based on the IPC version */ if (tplg_ops && tplg_ops->control_setup) - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + list_for_each_entry(scontrol, &instance->kcontrol_list, list) { ret = tplg_ops->control_setup(sdev, scontrol); if (ret < 0) { - dev_err(sdev->dev, "failed updating IPC struct for control %s\n", + dev_err(scomp->dev, "failed updating IPC struct for control %s\n", scontrol->name); return ret; } } /* set up the IPC structures for the pipeline widgets */ - list_for_each_entry(spipe, &sdev->pipeline_list, list) { + list_for_each_entry(spipe, &instance->pipeline_list, list) { struct snd_sof_widget *pipe_widget = spipe->pipe_widget; struct snd_sof_widget *swidget; @@ -2228,14 +2246,14 @@ static int sof_complete(struct snd_soc_component *scomp) if (widget_ops && widget_ops[pipe_widget->id].ipc_setup) { ret = widget_ops[pipe_widget->id].ipc_setup(pipe_widget); if (ret < 0) { - dev_err(sdev->dev, "failed updating IPC struct for %s\n", + dev_err(scomp->dev, "failed updating IPC struct for %s\n", pipe_widget->widget->name); return ret; } } /* set the pipeline and update the IPC structure for the non scheduler widgets */ - list_for_each_entry(swidget, &sdev->widget_list, list) + list_for_each_entry(swidget, &instance->widget_list, list) if (swidget->widget->id != snd_soc_dapm_scheduler && swidget->pipeline_id == pipe_widget->pipeline_id) { ret = sof_set_widget_pipeline(sdev, spipe, swidget); @@ -2245,7 +2263,7 @@ static int sof_complete(struct snd_soc_component *scomp) if (widget_ops && widget_ops[swidget->id].ipc_setup) { ret = widget_ops[swidget->id].ipc_setup(swidget); if (ret < 0) { - dev_err(sdev->dev, + dev_err(scomp->dev, "failed updating IPC struct for %s\n", swidget->widget->name); return ret; @@ -2260,14 +2278,14 @@ static int sof_complete(struct snd_soc_component *scomp) tplg_ops->tear_down_all_pipelines) { ret = tplg_ops->set_up_all_pipelines(sdev, true); if (ret < 0) { - dev_err(sdev->dev, "Failed to set up all topology pipelines: %d\n", + dev_err(scomp->dev, "Failed to set up all topology pipelines: %d\n", ret); return ret; } ret = tplg_ops->tear_down_all_pipelines(sdev, true); if (ret < 0) { - dev_err(sdev->dev, "Failed to tear down topology pipelines: %d\n", + dev_err(scomp->dev, "Failed to tear down topology pipelines: %d\n", ret); return ret; } @@ -2285,7 +2303,7 @@ static int sof_complete(struct snd_soc_component *scomp) static int sof_manifest(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_manifest *man) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->parse_manifest) @@ -2400,7 +2418,8 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index, 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_audio_instance *instance = + snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *swidget; struct snd_sof_dai *sdai; @@ -2435,7 +2454,7 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index, swidget->private = sdai; mutex_init(&swidget->setup_mutex); w->dobj.private = swidget; - list_add(&swidget->list, &sdev->widget_list); + list_add(&swidget->list, &instance->widget_list); } return 0; @@ -2497,10 +2516,55 @@ static const struct snd_soc_tplg_ops sof_dspless_tplg_ops = { .bytes_ext_ops_count = ARRAY_SIZE(sof_dspless_bytes_ext_ops), }; +/* + * Check if a feature topology is compatible with the current card by + * verifying that matching BE DAI links exist. + * + * Feature topologies extend specific codec function types. If the card + * doesn't have the corresponding BE DAI links, the topology load would + * fail due to missing routes/widgets. This works transparently for both + * multi-card and single-card configurations. + */ +static bool +sof_feature_tplg_matches_card(struct snd_soc_component *scomp, + const char *feature_tplg) +{ + static const struct { + const char *tplg_key; /* substring in feature topology filename */ + const char *dai_key; /* substring in BE DAI link name */ + } match_table[] = { + { "amp", "SmartAmp" }, + { "jack", "SimpleJack" }, + { "mic", "SmartMic" }, + }; + struct snd_soc_dai_link *dai_link; + bool needs_match = false; + int i, j; + + for (i = 0; i < ARRAY_SIZE(match_table); i++) { + if (!strstr(feature_tplg, match_table[i].tplg_key)) + continue; + + needs_match = true; + + for_each_card_prelinks(scomp->card, j, dai_link) { + if (strstr(dai_link->name, match_table[i].dai_key)) + return true; + } + } + + /* Unknown feature type: load it (backwards compatible) */ + return !needs_match; +} + int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(scomp); + struct sof_audio_client_pdata *audio_pdata = dev_get_platdata(&cdev->auxdev.dev); + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct snd_sof_pdata *sof_pdata = sdev->pdata; + const struct snd_soc_acpi_mach *mach = audio_pdata->machine.drv_name ? + &audio_pdata->machine : sof_pdata->machine; const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix; const struct firmware *fw; const char **tplg_files; @@ -2514,7 +2578,7 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) /* Try to use function topologies if possible */ if (!sof_pdata->disable_function_topology && !disable_function_topology && - sof_pdata->machine && sof_pdata->machine->get_function_tplg_files) { + mach && mach->get_function_tplg_files) { /* * When the topology name contains 'dummy' word, it means that * there is no fallback option to monolithic topology in case @@ -2529,11 +2593,11 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) */ bool no_fallback = strstr(file, "dummy"); - tplg_cnt = sof_pdata->machine->get_function_tplg_files(scomp->card, - sof_pdata->machine, - tplg_filename_prefix, - &tplg_files, - no_fallback); + tplg_cnt = mach->get_function_tplg_files(scomp->card, + mach, + tplg_filename_prefix, + &tplg_files, + no_fallback); if (tplg_cnt < 0) { kfree(tplg_files); return tplg_cnt; @@ -2589,11 +2653,25 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) } } - /* Loading user defined topologies */ + /* + * Loading user defined feature topologies. + * Each feature topology extends a specific function type (amp, jack, + * mic, etc.). Only load if the card has a matching BE DAI link, + * otherwise the routes would fail due to missing widgets. + */ for (i = 0; i < feature_tplg_cnt; i++) { - const char *feature_topology = devm_kasprintf(scomp->dev, GFP_KERNEL, "%s/%s", - tplg_filename_prefix, - feature_topologies[i]); + const char *feature_topology; + + if (!sof_feature_tplg_matches_card(scomp, feature_topologies[i])) { + dev_dbg(scomp->dev, + "skip feature topology %s: no matching BE DAI link\n", + feature_topologies[i]); + continue; + } + + feature_topology = devm_kasprintf(scomp->dev, GFP_KERNEL, "%s/%s", + tplg_filename_prefix, + feature_topologies[i]); if (!feature_topology) { ret = -ENOMEM;