Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 150
Fixes for HD-Audio DMA stopping#3167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
4900760cd2cac8c70906e152184e8725d4cc281082392c2eb905737f0a79964693860cFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -52,12 +52,25 @@ | ||
| #define SOF_DAI_FMT_INV_MASK 0x0f00 | ||
| #define SOF_DAI_FMT_CLOCK_PROVIDER_MASK 0xf000 | ||
kv2019i marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| /* 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 */ | ||
| /* | ||
ranj063 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| * 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 { | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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, | ||
| @@ -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) | ||
kv2019i marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| { | ||
| 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; | ||
| } | ||
plbossart marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, | ||
| int cmd, struct snd_soc_dai *dai) | ||
| { | ||
| @@ -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); | ||
ranj063 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| switch (cmd) { | ||
| case SNDRV_PCM_TRIGGER_RESUME: | ||
| /* set up hw_params */ | ||
| @@ -325,10 +358,7 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, | ||
| break; | ||
kv2019i marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| 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. | ||
| @@ -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; | ||
| @@ -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, | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| } | ||
ranj063 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| 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) | ||
| { | ||
| @@ -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) { | ||
| @@ -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); | ||
ranj063 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| 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; | ||
ranj063 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| if (hstream->posbuf) | ||
| *hstream->posbuf = 0; | ||
| @@ -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); | ||
kv2019i marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| if (ret < 0) | ||
| return ret; | ||
| spin_lock_irq(&bus->reg_lock); | ||
| /* couple host and link DMA if link DMA channel is idle */ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) | ||
kv2019i marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| { | ||
| struct snd_sof_widget *swidget = w->dobj.private; | ||
| struct snd_soc_component *component = swidget->scomp; | ||
| @@ -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); | ||
ujfalusi marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| if (ret < 0) | ||
| return ret; | ||
| } | ||
| @@ -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, | ||
| @@ -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; | ||
| @@ -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)); | ||
| @@ -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, | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.