Skip to content
Merged
25 changes: 19 additions & 6 deletions include/sound/sof/dai.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,25 @@
#define SOF_DAI_FMT_INV_MASK 0x0f00
#define SOF_DAI_FMT_CLOCK_PROVIDER_MASK 0xf000

/* DAI_CONFIG flags */
#define SOF_DAI_CONFIG_FLAGS_MASK 0x3
#define SOF_DAI_CONFIG_FLAGS_NONE (0 << 0) /**< DAI_CONFIG sent without stage information */
#define SOF_DAI_CONFIG_FLAGS_HW_PARAMS (1 << 0) /**< DAI_CONFIG sent during hw_params stage */
#define SOF_DAI_CONFIG_FLAGS_HW_FREE (2 << 0) /**< DAI_CONFIG sent during hw_free stage */
#define SOF_DAI_CONFIG_FLAGS_RFU (3 << 0) /**< not used, reserved for future use */
/*
* DAI_CONFIG flags. The 4 LSB bits are used for the commands, HW_PARAMS, HW_FREE and PAUSE
* representing when the IPC is sent. The 4 MSB bits are used to add quirks along with the above
* commands.
*/
#define SOF_DAI_CONFIG_FLAGS_CMD_MASK 0xF
#define SOF_DAI_CONFIG_FLAGS_NONE 0 /**< DAI_CONFIG sent without stage information */
#define SOF_DAI_CONFIG_FLAGS_HW_PARAMS BIT(0) /**< DAI_CONFIG sent during hw_params stage */
#define SOF_DAI_CONFIG_FLAGS_HW_FREE BIT(1) /**< DAI_CONFIG sent during hw_free stage */
/**< DAI_CONFIG sent during pause trigger. Only available ABI 3.20 onwards */
#define SOF_DAI_CONFIG_FLAGS_PAUSE BIT(2)
#define SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT 4
#define SOF_DAI_CONFIG_FLAGS_QUIRK_MASK (0xF << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT)
/*
* This should be used along with the SOF_DAI_CONFIG_FLAGS_HW_PARAMS to indicate that pipeline
* stop/pause and DAI DMA stop/pause should happen in two steps. This change is only available
* ABI 3.20 onwards.
*/
#define SOF_DAI_CONFIG_FLAGS_2_STEP_STOP BIT(0)

/** \brief Types of DAI */
enum sof_ipc_dai_type {
Expand Down
53 changes: 43 additions & 10 deletions sound/soc/sof/intel/hda-dai.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,9 @@ static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream,

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

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

static int hda_link_hw_params(struct snd_pcm_substream *substream,
Expand Down Expand Up @@ -283,6 +283,36 @@ static int hda_link_pcm_prepare(struct snd_pcm_substream *substream,
dai);
}

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_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->dai_config) {
dev_err(sdev->dev, "No config for DAI %s\n", w->name);
return -EINVAL;
}

config = &sof_dai->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;
}

static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
Expand All @@ -308,6 +338,9 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
hda_stream = hstream_to_sof_hda_stream(link_dev);

dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);

w = snd_soc_dai_get_widget(dai, substream->stream);

switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
/* set up hw_params */
Expand All @@ -325,10 +358,7 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
w = dai->playback_widget;
else
w = dai->capture_widget;
snd_hdac_ext_link_stream_clear(link_dev);

/*
* free DAI widget during stop/suspend to keep widget use_count's balanced.
Expand All @@ -343,10 +373,13 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
}

link_dev->link_prepared = 0;

fallthrough;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
snd_hdac_ext_link_stream_clear(link_dev);

ret = hda_link_dai_config_pause_push_ipc(w);
if (ret < 0)
return ret;
break;
default:
return -EINVAL;
Expand Down Expand Up @@ -447,9 +480,9 @@ static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd
return 0;

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

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

static int ssp_dai_startup(struct snd_pcm_substream *substream,
Expand Down
81 changes: 49 additions & 32 deletions sound/soc/sof/intel/hda-stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,45 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
return 0;
}

static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hstream)
{
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
int timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
u32 val;

/* enter stream reset */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST,
SOF_STREAM_SD_OFFSET_CRST);
do {
val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset);
if (val & SOF_STREAM_SD_OFFSET_CRST)
break;
} while (--timeout);
if (timeout == 0) {
dev_err(sdev->dev, "timeout waiting for stream reset\n");
return -ETIMEDOUT;
}

timeout = HDA_DSP_STREAM_RESET_TIMEOUT;

/* exit stream reset and wait to read a zero before reading any other register */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST, 0x0);

/* wait for hardware to report that stream is out of reset */
udelay(3);
do {
val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset);
if ((val & SOF_STREAM_SD_OFFSET_CRST) == 0)
break;
} while (--timeout);
if (timeout == 0) {
dev_err(sdev->dev, "timeout waiting for stream to exit reset\n");
return -ETIMEDOUT;
}

return 0;
}

int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
struct hdac_ext_stream *stream, int cmd)
{
Expand Down Expand Up @@ -437,9 +476,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
struct hdac_bus *bus = sof_to_bus(sdev);
struct hdac_stream *hstream = &stream->hstream;
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
int ret;
u32 dma_start = SOF_HDA_SD_CTL_DMA_START;
u32 val, mask;
u32 mask;
u32 run;

if (!stream) {
Expand Down Expand Up @@ -484,36 +523,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
SOF_HDA_CL_DMA_SD_INT_MASK);

/* stream reset */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1,
0x1);
udelay(3);
do {
val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
sd_offset);
if (val & 0x1)
break;
} while (--timeout);
if (timeout == 0) {
dev_err(sdev->dev, "error: stream reset failed\n");
return -ETIMEDOUT;
}

timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1,
0x0);

/* wait for hardware to report that stream is out of reset */
udelay(3);
do {
val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
sd_offset);
if ((val & 0x1) == 0)
break;
} while (--timeout);
if (timeout == 0) {
dev_err(sdev->dev, "error: timeout waiting for stream reset\n");
return -ETIMEDOUT;
}
ret = hda_dsp_stream_reset(sdev, hstream);
if (ret < 0)
return ret;

if (hstream->posbuf)
*hstream->posbuf = 0;
Expand Down Expand Up @@ -654,6 +666,11 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
hstream);
struct hdac_bus *bus = sof_to_bus(sdev);
u32 mask = 0x1 << stream->index;
int ret;

ret = hda_dsp_stream_reset(sdev, stream);
if (ret < 0)
return ret;

spin_lock_irq(&bus->reg_lock);
/* couple host and link DMA if link DMA channel is idle */
Expand Down
21 changes: 12 additions & 9 deletions sound/soc/sof/intel/hda.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
#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)
int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_soc_component *component = swidget->scomp;
Expand All @@ -60,7 +60,7 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w)

/* DAI already configured, reset it before reconfiguring it */
if (sof_dai->configured) {
ret = hda_ctrl_dai_widget_free(w);
ret = hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
if (ret < 0)
return ret;
}
Expand All @@ -78,8 +78,10 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w)
return ret;
}

/* set HW_PARAMS flag */
config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_HW_PARAMS);
/* set HW_PARAMS flag along with quirks */
config->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,
Expand All @@ -94,7 +96,7 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w)
return 0;
}

int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w)
int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_soc_component *component = swidget->scomp;
Expand All @@ -117,8 +119,9 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w)

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

/* set HW_FREE flag */
config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_HW_FREE);
/* set HW_FREE flag along with any quirks */
config->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));
Expand Down Expand Up @@ -173,9 +176,9 @@ static int sdw_dai_config_ipc(struct snd_sof_dev *sdev,
config->alh.stream_id = alh_stream_id;

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

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

static int sdw_params_stream(struct device *dev,
Expand Down
6 changes: 4 additions & 2 deletions sound/soc/sof/intel/hda.h
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,8 @@ struct sof_intel_hda_stream {
(SOF_HDA_ADSP_SD_ENTRY_SIZE * ((s)->index) \
+ SOF_HDA_ADSP_LOADER_BASE)

#define SOF_STREAM_SD_OFFSET_CRST 0x1

/*
* DSP Core services.
*/
Expand Down Expand Up @@ -736,8 +738,8 @@ 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);
int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w);
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);

#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