@@ -719,9 +719,31 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol,
struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
struct sof_ipc_fw_version *v = &ready->version;
struct sof_ipc_ctrl_data_params sparams;
+ struct snd_sof_widget *swidget;
+ bool widget_found = false;
size_t send_bytes;
int err;
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
+ if (swidget->comp_id == scontrol->comp_id) {
+ widget_found = true;
+ break;
+ }
+ }
+
+ if (!widget_found) {
+ dev_err(sdev->dev, "error: can't find widget with id %d\n", scontrol->comp_id);
+ return -EINVAL;
+ }
+
+ /*
+ * Volatile controls should always be part of static pipelines and the widget use_count
+ * would always be > 0 in this case. For the others, just return the cached value if the
+ * widget is not set up.
+ */
+ if (!swidget->use_count)
+ return 0;
+
/* read or write firmware volume */
if (scontrol->readback_offset != 0) {
/* write/read value header via mmaped region */
@@ -83,7 +83,53 @@ static int sof_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_wi
return 0;
}
-static int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+ struct sof_ipc_free ipc_free = {
+ .hdr = {
+ .size = sizeof(ipc_free),
+ .cmd = SOF_IPC_GLB_TPLG_MSG,
+ },
+ .id = swidget->comp_id,
+ };
+ struct sof_ipc_reply reply;
+ int ret;
+
+ if (!swidget->private)
+ return 0;
+
+ /* only free when use_count is 0 */
+ if (--swidget->use_count)
+ return 0;
+
+ switch (swidget->id) {
+ case snd_soc_dapm_scheduler:
+ ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE;
+ break;
+ case snd_soc_dapm_buffer:
+ ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE;
+ break;
+ default:
+ ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE;
+ break;
+ }
+
+ ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free),
+ &reply, sizeof(reply));
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name);
+ swidget->use_count++;
+ return ret;
+ }
+
+ swidget->complete = 0;
+ dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
+
+ return 0;
+}
+EXPORT_SYMBOL(sof_widget_free);
+
+int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
struct sof_ipc_pipe_new *pipeline;
struct sof_ipc_comp_reply r;
@@ -97,11 +143,15 @@ static int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swi
if (!swidget->private)
return 0;
+ /* widget already set up */
+ if (++swidget->use_count > 1)
+ return 0;
+
ret = sof_pipeline_core_enable(sdev, swidget);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to enable target core: %d for widget %s\n",
ret, swidget->widget->name);
- return ret;
+ goto use_count_dec;
}
switch (swidget->id) {
@@ -134,7 +184,7 @@ static int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swi
}
if (ret < 0) {
dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name);
- return ret;
+ goto use_count_dec;
}
/* restore kcontrols for widget */
@@ -147,8 +197,13 @@ static int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swi
dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name);
+ return 0;
+
+use_count_dec:
+ swidget->use_count--;
return ret;
}
+EXPORT_SYMBOL(sof_widget_setup);
/*
* helper to determine if there are only D0i3 compatible
@@ -258,6 +313,9 @@ int sof_set_up_pipelines(struct device *dev)
/* restore pipeline components */
list_for_each_entry_reverse(swidget, &sdev->widget_list, list) {
+ /* reset widget use_count after resuming */
+ swidget->use_count = 0;
+
ret = sof_widget_setup(sdev, swidget);
if (ret < 0)
return ret;
@@ -325,16 +383,23 @@ int sof_set_up_pipelines(struct device *dev)
return 0;
}
-/* This function doesn't free widgets. It only resets the set up status for all routes */
+/*
+ * This function doesn't free widgets. It only resets the set up status for all routes and
+ * use_count for all widgets.
+ */
void sof_tear_down_pipelines(struct device *dev)
{
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ struct snd_sof_widget *swidget;
struct snd_sof_route *sroute;
/*
- * No need to protect sroute->setup as this function is called only during the suspend
- * callback and all streams should be suspended by then
+ * No need to protect swidget->use_count and sroute->setup as this function is called only
+ * during the suspend callback and all streams should be suspended by then
*/
+ list_for_each_entry(swidget, &sdev->widget_list, list)
+ swidget->use_count = 0;
+
list_for_each_entry(sroute, &sdev->route_list, list)
sroute->setup = false;
}
@@ -89,6 +89,7 @@ struct snd_sof_widget {
int comp_id;
int pipeline_id;
int complete;
+ int use_count; /* use_count will be protected by the PCM mutex held by the core */
int core;
int id;
@@ -252,4 +253,7 @@ bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev);
int sof_machine_register(struct snd_sof_dev *sdev, void *pdata);
void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata);
+int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+
#endif