Skip to content

Commit

Permalink
ASoC: SOF: Make sof_widget_setup/free IPC agnostic
Browse files Browse the repository at this point in the history
Add 3 new topology IPC ops for widget_setup, widget_free and dai_config
in order to make the pipeline management code IPC agnostic and implement
the ops for IPC3.

Use the newly introduced tplg dai_config op to configure the DAI during
BE DAI hw_params and hw_free.

Also, in preparation for IPC4, modify BE hw_params callback to skip
setting up the DAI widget. All widgets will be set up during FW
hw_params and the DAI_CONFIG IPC should be sent only if the widget
use_count is > 0. With setting up/freeing removed from the BE hw_params,
remove the configured flag as it is no longer needed.

Signed-off-by: Ranjani Sridharan <[email protected]>
Reviewed-by: Péter Ujfalusi <[email protected]>
Reviewed-by: Pierre-Louis Bossart <[email protected]>
Reviewed-by: Bard Liao <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Mark Brown <[email protected]>
  • Loading branch information
ranj063 authored and broonie committed Mar 18, 2022
1 parent 40c2c63 commit 051744b
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 326 deletions.
97 changes: 13 additions & 84 deletions sound/soc/sof/intel/hda-dai.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,58 +162,19 @@ static int hda_link_dma_params(struct hdac_ext_stream *hext_stream,
return 0;
}

/* Update config for the DAI widget */
static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widget *w,
int channel)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct sof_dai_private_data *private;
struct sof_ipc_dai_config *config;
struct snd_sof_dai *sof_dai;

if (!swidget)
return NULL;

sof_dai = swidget->private;

if (!sof_dai || !sof_dai->private) {
dev_err(swidget->scomp->dev, "%s: No private data for DAI %s\n", __func__,
w->name);
return NULL;
}

private = sof_dai->private;
if (!private->dai_config) {
dev_err(swidget->scomp->dev, "%s: No config for DAI %s\n", __func__, w->name);
return NULL;
}

config = &private->dai_config[sof_dai->current_config];

/* update config with stream tag */
config->hda.link_dma_ch = channel;

return config;
}

static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream,
struct snd_soc_dapm_widget *w,
int channel, bool widget_setup)
{
struct snd_sof_dev *sdev = hda_stream->sdev;
struct sof_ipc_dai_config *config;
struct snd_sof_dai_config_data data;

config = hda_dai_update_config(w, channel);
if (!config) {
dev_err(sdev->dev, "error: no config for DAI %s\n", w->name);
return -ENOENT;
}
data.dai_data = channel;

/* set up/free DAI widget and send DAI_CONFIG IPC */
if (widget_setup)
return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP);
return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP, &data);

return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
}

static int hda_link_hw_params(struct snd_pcm_substream *substream,
Expand Down Expand Up @@ -302,35 +263,16 @@ static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_soc_component *component = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct sof_dai_private_data *private;
struct sof_ipc_dai_config *config;
struct snd_sof_dai *sof_dai;
struct sof_ipc_reply reply;
int ret;

sof_dai = swidget->private;

if (!sof_dai || !sof_dai->private) {
dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name);
return -EINVAL;
}
const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
int ret = 0;

private = sof_dai->private;
if (!private->dai_config) {
dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name);
return -EINVAL;
if (tplg_ops->dai_config) {
ret = tplg_ops->dai_config(sdev, swidget, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
if (ret < 0)
dev_err(sdev->dev, "%s: DAI config failed for widget %s\n", __func__,
w->name);
}

config = &private->dai_config[sof_dai->current_config];

/* set PAUSE command flag */
config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_CMD_MASK, SOF_DAI_CONFIG_FLAGS_PAUSE);

ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
&reply, sizeof(reply));
if (ret < 0)
dev_err(sdev->dev, "DAI config for %s failed during pause push\n", w->name);

return ret;
}

Expand Down Expand Up @@ -470,30 +412,17 @@ struct ssp_dai_dma_data {
static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
bool setup)
{
struct snd_soc_component *component;
struct snd_sof_widget *swidget;
struct snd_soc_dapm_widget *w;
struct sof_ipc_fw_version *v;
struct snd_sof_dev *sdev;

if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
w = dai->playback_widget;
else
w = dai->capture_widget;

swidget = w->dobj.private;
component = swidget->scomp;
sdev = snd_soc_component_get_drvdata(component);
v = &sdev->fw_ready.version;

/* DAI_CONFIG IPC during hw_params is not supported in older firmware */
if (v->abi_version < SOF_ABI_VER(3, 18, 0))
return 0;

if (setup)
return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE);
return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);

return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);
}

static int ssp_dai_startup(struct snd_pcm_substream *substream,
Expand Down
169 changes: 44 additions & 125 deletions sound/soc/sof/intel/hda.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,114 +41,68 @@
#define EXCEPT_MAX_HDR_SIZE 0x400
#define HDA_EXT_ROM_STATUS_SIZE 8

int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags)
int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
struct snd_sof_dai_config_data *data)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_soc_component *component = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct sof_ipc_dai_config *config;
struct sof_dai_private_data *private;
struct snd_sof_dai *sof_dai;
struct sof_ipc_reply reply;
const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
struct snd_sof_dai *sof_dai = swidget->private;
int ret;

sof_dai = swidget->private;

if (!sof_dai || !sof_dai->private) {
dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name);
return -EINVAL;
}

private = sof_dai->private;
if (!private->dai_config) {
dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name);
if (!sof_dai) {
dev_err(sdev->dev, "%s: No DAI for DAI widget %s\n", __func__, w->name);
return -EINVAL;
}

/* DAI already configured, reset it before reconfiguring it */
if (sof_dai->configured) {
ret = hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
if (ret < 0)
return ret;
}

config = &private->dai_config[sof_dai->current_config];

/*
* For static pipelines, the DAI widget would already be set up and calling
* sof_widget_setup() simply returns without doing anything.
* For dynamic pipelines, the DAI widget will be set up now.
*/
ret = sof_widget_setup(sdev, swidget);
if (ret < 0) {
dev_err(sdev->dev, "error: failed setting up DAI widget %s\n", w->name);
return ret;
}
if (tplg_ops->dai_config) {
unsigned int flags;

/* set HW_PARAMS flag along with quirks */
config->flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS |
quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
/* set HW_PARAMS flag along with quirks */
flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS |
quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;


/* send DAI_CONFIG IPC */
ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
&reply, sizeof(reply));
if (ret < 0) {
dev_err(sdev->dev, "error: failed setting DAI config for %s\n", w->name);
return ret;
ret = tplg_ops->dai_config(sdev, swidget, flags, data);
if (ret < 0) {
dev_err(sdev->dev, "%s: DAI config failed for widget %s\n", __func__,
w->name);
return ret;
}
}

sof_dai->configured = true;

return 0;
}

int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags)
int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
struct snd_sof_dai_config_data *data)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_soc_component *component = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct sof_dai_private_data *private;
struct sof_ipc_dai_config *config;
struct snd_sof_dai *sof_dai;
struct sof_ipc_reply reply;
int ret;
const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
struct snd_sof_dai *sof_dai = swidget->private;

sof_dai = swidget->private;

if (!sof_dai || !sof_dai->private) {
dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name);
return -EINVAL;
}

private = sof_dai->private;
if (!private->dai_config) {
dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name);
if (!sof_dai) {
dev_err(sdev->dev, "%s: No DAI for BE DAI widget %s\n", __func__, w->name);
return -EINVAL;
}

/* nothing to do if hw_free() is called without restarting the stream after resume. */
if (!sof_dai->configured)
return 0;

config = &private->dai_config[sof_dai->current_config];
if (tplg_ops->dai_config) {
unsigned int flags;
int ret;

/* set HW_FREE flag along with any quirks */
config->flags = SOF_DAI_CONFIG_FLAGS_HW_FREE |
quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
/* set HW_FREE flag along with any quirks */
flags = SOF_DAI_CONFIG_FLAGS_HW_FREE |
quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;

ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
&reply, sizeof(reply));
if (ret < 0)
dev_err(sdev->dev, "error: failed resetting DAI config for %s\n", w->name);

/*
* Reset the configured_flag and free the widget even if the IPC fails to keep
* the widget use_count balanced
*/
sof_dai->configured = false;
ret = tplg_ops->dai_config(sdev, swidget, flags, data);
if (ret < 0)
dev_err(sdev->dev, "%s: DAI config failed for widget '%s'\n", __func__,
w->name);
}

return sof_widget_free(sdev, swidget);
return 0;
}

#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
Expand All @@ -163,69 +117,34 @@ static int sdw_clock_stop_quirks = SDW_INTEL_CLK_STOP_BUS_RESET;
module_param(sdw_clock_stop_quirks, int, 0444);
MODULE_PARM_DESC(sdw_clock_stop_quirks, "SOF SoundWire clock stop quirks");

static int sdw_dai_config_ipc(struct snd_sof_dev *sdev,
struct snd_soc_dapm_widget *w,
int link_id, int alh_stream_id, int dai_id, bool setup)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct sof_dai_private_data *private;
struct sof_ipc_dai_config *config;
struct snd_sof_dai *sof_dai;

if (!swidget) {
dev_err(sdev->dev, "error: No private data for widget %s\n", w->name);
return -EINVAL;
}

sof_dai = swidget->private;

if (!sof_dai || !sof_dai->private) {
dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name);
return -EINVAL;
}

private = sof_dai->private;
if (!private->dai_config) {
dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name);
return -EINVAL;
}

config = &private->dai_config[sof_dai->current_config];

/* update config with link and stream ID */
config->dai_index = (link_id << 8) | dai_id;
config->alh.stream_id = alh_stream_id;

if (setup)
return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE);

return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
}

static int sdw_params_stream(struct device *dev,
struct sdw_intel_stream_params_data *params_data)
{
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct snd_soc_dai *d = params_data->dai;
struct snd_sof_dai_config_data data;
struct snd_soc_dapm_widget *w;

w = snd_soc_dai_get_widget(d, params_data->stream);
data.dai_index = (params_data->link_id << 8) | d->id;
data.dai_data = params_data->alh_stream_id;

return sdw_dai_config_ipc(sdev, w, params_data->link_id, params_data->alh_stream_id,
d->id, true);
return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
}

static int sdw_free_stream(struct device *dev,
struct sdw_intel_stream_free_data *free_data)
{
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct snd_soc_dai *d = free_data->dai;
struct snd_sof_dai_config_data data;
struct snd_soc_dapm_widget *w;

w = snd_soc_dai_get_widget(d, free_data->stream);
data.dai_index = (free_data->link_id << 8) | d->id;

/* send invalid stream_id */
return sdw_dai_config_ipc(sdev, w, free_data->link_id, 0xFFFF, d->id, false);
data.dai_data = 0xFFFF;

return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
}

static const struct sdw_intel_ops sdw_callback = {
Expand Down
7 changes: 5 additions & 2 deletions sound/soc/sof/intel/hda.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <sound/hda_codec.h>
#include <sound/hdaudio_ext.h>
#include "../sof-client-probes.h"
#include "../sof-audio.h"
#include "shim.h"

/* PCI registers */
Expand Down Expand Up @@ -730,8 +731,10 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)

struct snd_sof_dai;
struct sof_ipc_dai_config;
int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags);
int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags);
int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
struct snd_sof_dai_config_data *data);
int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
struct snd_sof_dai_config_data *data);

#define SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY (0) /* previous implementation */
#define SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS (1) /* recommended if VC0 only */
Expand Down
Loading

0 comments on commit 051744b

Please sign in to comment.