Skip to content

Commit 335302d

Browse files
committed
ASoC: SOF: Fixes for Intel HD-Audio DMA stopping
Merge series from Kai Vehmanen <kai.vehmanen@linux.intel.com>: Implement an updated programming sequence to handle DMA stop for Intel HD-Audio DMA. The new flow is only used if the firmware is sufficiently new to support the feature. SOF1.9.2 is the first release with the updated flow. The kernel changes are backwards compatible with old firmware releases. Likewise new firmware releases will work with old kernel. Series reviewed originally at: #3167
2 parents 4dcddad + 69acac5 commit 335302d

8 files changed

Lines changed: 180 additions & 84 deletions

File tree

include/sound/sof/dai.h

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,25 @@
5252
#define SOF_DAI_FMT_INV_MASK 0x0f00
5353
#define SOF_DAI_FMT_CLOCK_PROVIDER_MASK 0xf000
5454

55-
/* DAI_CONFIG flags */
56-
#define SOF_DAI_CONFIG_FLAGS_MASK 0x3
57-
#define SOF_DAI_CONFIG_FLAGS_NONE (0 << 0) /**< DAI_CONFIG sent without stage information */
58-
#define SOF_DAI_CONFIG_FLAGS_HW_PARAMS (1 << 0) /**< DAI_CONFIG sent during hw_params stage */
59-
#define SOF_DAI_CONFIG_FLAGS_HW_FREE (2 << 0) /**< DAI_CONFIG sent during hw_free stage */
60-
#define SOF_DAI_CONFIG_FLAGS_RFU (3 << 0) /**< not used, reserved for future use */
55+
/*
56+
* DAI_CONFIG flags. The 4 LSB bits are used for the commands, HW_PARAMS, HW_FREE and PAUSE
57+
* representing when the IPC is sent. The 4 MSB bits are used to add quirks along with the above
58+
* commands.
59+
*/
60+
#define SOF_DAI_CONFIG_FLAGS_CMD_MASK 0xF
61+
#define SOF_DAI_CONFIG_FLAGS_NONE 0 /**< DAI_CONFIG sent without stage information */
62+
#define SOF_DAI_CONFIG_FLAGS_HW_PARAMS BIT(0) /**< DAI_CONFIG sent during hw_params stage */
63+
#define SOF_DAI_CONFIG_FLAGS_HW_FREE BIT(1) /**< DAI_CONFIG sent during hw_free stage */
64+
/**< DAI_CONFIG sent during pause trigger. Only available ABI 3.20 onwards */
65+
#define SOF_DAI_CONFIG_FLAGS_PAUSE BIT(2)
66+
#define SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT 4
67+
#define SOF_DAI_CONFIG_FLAGS_QUIRK_MASK (0xF << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT)
68+
/*
69+
* This should be used along with the SOF_DAI_CONFIG_FLAGS_HW_PARAMS to indicate that pipeline
70+
* stop/pause and DAI DMA stop/pause should happen in two steps. This change is only available
71+
* ABI 3.20 onwards.
72+
*/
73+
#define SOF_DAI_CONFIG_FLAGS_2_STEP_STOP BIT(0)
6174

6275
/** \brief Types of DAI */
6376
enum sof_ipc_dai_type {

sound/soc/sof/intel/hda-dai.c

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,9 @@ static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream,
197197

198198
/* set up/free DAI widget and send DAI_CONFIG IPC */
199199
if (widget_setup)
200-
return hda_ctrl_dai_widget_setup(w);
200+
return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP);
201201

202-
return hda_ctrl_dai_widget_free(w);
202+
return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
203203
}
204204

205205
static int hda_link_hw_params(struct snd_pcm_substream *substream,
@@ -287,6 +287,36 @@ static int hda_link_pcm_prepare(struct snd_pcm_substream *substream,
287287
dai);
288288
}
289289

290+
static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
291+
{
292+
struct snd_sof_widget *swidget = w->dobj.private;
293+
struct snd_soc_component *component = swidget->scomp;
294+
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
295+
struct sof_ipc_dai_config *config;
296+
struct snd_sof_dai *sof_dai;
297+
struct sof_ipc_reply reply;
298+
int ret;
299+
300+
sof_dai = swidget->private;
301+
302+
if (!sof_dai || !sof_dai->dai_config) {
303+
dev_err(sdev->dev, "No config for DAI %s\n", w->name);
304+
return -EINVAL;
305+
}
306+
307+
config = &sof_dai->dai_config[sof_dai->current_config];
308+
309+
/* set PAUSE command flag */
310+
config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_CMD_MASK, SOF_DAI_CONFIG_FLAGS_PAUSE);
311+
312+
ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
313+
&reply, sizeof(reply));
314+
if (ret < 0)
315+
dev_err(sdev->dev, "DAI config for %s failed during pause push\n", w->name);
316+
317+
return ret;
318+
}
319+
290320
static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
291321
int cmd, struct snd_soc_dai *dai)
292322
{
@@ -312,6 +342,9 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
312342
hda_stream = hstream_to_sof_hda_stream(link_dev);
313343

314344
dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
345+
346+
w = snd_soc_dai_get_widget(dai, substream->stream);
347+
315348
switch (cmd) {
316349
case SNDRV_PCM_TRIGGER_RESUME:
317350
/* set up hw_params */
@@ -329,10 +362,7 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
329362
break;
330363
case SNDRV_PCM_TRIGGER_SUSPEND:
331364
case SNDRV_PCM_TRIGGER_STOP:
332-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
333-
w = dai->playback_widget;
334-
else
335-
w = dai->capture_widget;
365+
snd_hdac_ext_link_stream_clear(link_dev);
336366

337367
/*
338368
* free DAI widget during stop/suspend to keep widget use_count's balanced.
@@ -347,10 +377,13 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
347377
}
348378

349379
link_dev->link_prepared = 0;
350-
351-
fallthrough;
380+
break;
352381
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
353382
snd_hdac_ext_link_stream_clear(link_dev);
383+
384+
ret = hda_link_dai_config_pause_push_ipc(w);
385+
if (ret < 0)
386+
return ret;
354387
break;
355388
default:
356389
return -EINVAL;
@@ -451,9 +484,9 @@ static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd
451484
return 0;
452485

453486
if (setup)
454-
return hda_ctrl_dai_widget_setup(w);
487+
return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE);
455488

456-
return hda_ctrl_dai_widget_free(w);
489+
return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
457490
}
458491

459492
static int ssp_dai_startup(struct snd_pcm_substream *substream,

sound/soc/sof/intel/hda-stream.c

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,45 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
279279
return 0;
280280
}
281281

282+
static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hstream)
283+
{
284+
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
285+
int timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
286+
u32 val;
287+
288+
/* enter stream reset */
289+
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST,
290+
SOF_STREAM_SD_OFFSET_CRST);
291+
do {
292+
val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset);
293+
if (val & SOF_STREAM_SD_OFFSET_CRST)
294+
break;
295+
} while (--timeout);
296+
if (timeout == 0) {
297+
dev_err(sdev->dev, "timeout waiting for stream reset\n");
298+
return -ETIMEDOUT;
299+
}
300+
301+
timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
302+
303+
/* exit stream reset and wait to read a zero before reading any other register */
304+
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST, 0x0);
305+
306+
/* wait for hardware to report that stream is out of reset */
307+
udelay(3);
308+
do {
309+
val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset);
310+
if ((val & SOF_STREAM_SD_OFFSET_CRST) == 0)
311+
break;
312+
} while (--timeout);
313+
if (timeout == 0) {
314+
dev_err(sdev->dev, "timeout waiting for stream to exit reset\n");
315+
return -ETIMEDOUT;
316+
}
317+
318+
return 0;
319+
}
320+
282321
int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
283322
struct hdac_ext_stream *stream, int cmd)
284323
{
@@ -436,9 +475,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
436475
struct hdac_bus *bus = sof_to_bus(sdev);
437476
struct hdac_stream *hstream = &stream->hstream;
438477
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
439-
int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
478+
int ret;
440479
u32 dma_start = SOF_HDA_SD_CTL_DMA_START;
441-
u32 val, mask;
480+
u32 mask;
442481
u32 run;
443482

444483
if (!stream) {
@@ -483,36 +522,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
483522
SOF_HDA_CL_DMA_SD_INT_MASK);
484523

485524
/* stream reset */
486-
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1,
487-
0x1);
488-
udelay(3);
489-
do {
490-
val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
491-
sd_offset);
492-
if (val & 0x1)
493-
break;
494-
} while (--timeout);
495-
if (timeout == 0) {
496-
dev_err(sdev->dev, "error: stream reset failed\n");
497-
return -ETIMEDOUT;
498-
}
499-
500-
timeout = HDA_DSP_STREAM_RESET_TIMEOUT;
501-
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1,
502-
0x0);
503-
504-
/* wait for hardware to report that stream is out of reset */
505-
udelay(3);
506-
do {
507-
val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
508-
sd_offset);
509-
if ((val & 0x1) == 0)
510-
break;
511-
} while (--timeout);
512-
if (timeout == 0) {
513-
dev_err(sdev->dev, "error: timeout waiting for stream reset\n");
514-
return -ETIMEDOUT;
515-
}
525+
ret = hda_dsp_stream_reset(sdev, hstream);
526+
if (ret < 0)
527+
return ret;
516528

517529
if (hstream->posbuf)
518530
*hstream->posbuf = 0;
@@ -647,6 +659,11 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
647659
hstream);
648660
struct hdac_bus *bus = sof_to_bus(sdev);
649661
u32 mask = 0x1 << stream->index;
662+
int ret;
663+
664+
ret = hda_dsp_stream_reset(sdev, stream);
665+
if (ret < 0)
666+
return ret;
650667

651668
spin_lock_irq(&bus->reg_lock);
652669
/* couple host and link DMA if link DMA channel is idle */

sound/soc/sof/intel/hda.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
#define EXCEPT_MAX_HDR_SIZE 0x400
4242
#define HDA_EXT_ROM_STATUS_SIZE 8
4343

44-
int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w)
44+
int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags)
4545
{
4646
struct snd_sof_widget *swidget = w->dobj.private;
4747
struct snd_soc_component *component = swidget->scomp;
@@ -58,6 +58,13 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w)
5858
return -EINVAL;
5959
}
6060

61+
/* DAI already configured, reset it before reconfiguring it */
62+
if (sof_dai->configured) {
63+
ret = hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
64+
if (ret < 0)
65+
return ret;
66+
}
67+
6168
config = &sof_dai->dai_config[sof_dai->current_config];
6269

6370
/*
@@ -71,8 +78,10 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w)
7178
return ret;
7279
}
7380

74-
/* set HW_PARAMS flag */
75-
config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_HW_PARAMS);
81+
/* set HW_PARAMS flag along with quirks */
82+
config->flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS |
83+
quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
84+
7685

7786
/* send DAI_CONFIG IPC */
7887
ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
@@ -87,7 +96,7 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w)
8796
return 0;
8897
}
8998

90-
int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w)
99+
int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags)
91100
{
92101
struct snd_sof_widget *swidget = w->dobj.private;
93102
struct snd_soc_component *component = swidget->scomp;
@@ -110,8 +119,9 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w)
110119

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

113-
/* set HW_FREE flag */
114-
config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_MASK, SOF_DAI_CONFIG_FLAGS_HW_FREE);
122+
/* set HW_FREE flag along with any quirks */
123+
config->flags = SOF_DAI_CONFIG_FLAGS_HW_FREE |
124+
quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
115125

116126
ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
117127
&reply, sizeof(reply));
@@ -166,9 +176,9 @@ static int sdw_dai_config_ipc(struct snd_sof_dev *sdev,
166176
config->alh.stream_id = alh_stream_id;
167177

168178
if (setup)
169-
return hda_ctrl_dai_widget_setup(w);
179+
return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE);
170180

171-
return hda_ctrl_dai_widget_free(w);
181+
return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
172182
}
173183

174184
static int sdw_params_stream(struct device *dev,

sound/soc/sof/intel/hda.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,8 @@ struct sof_intel_hda_stream {
487487
(SOF_HDA_ADSP_SD_ENTRY_SIZE * ((s)->index) \
488488
+ SOF_HDA_ADSP_LOADER_BASE)
489489

490+
#define SOF_STREAM_SD_OFFSET_CRST 0x1
491+
490492
/*
491493
* DSP Core services.
492494
*/
@@ -737,7 +739,7 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
737739

738740
struct snd_sof_dai;
739741
struct sof_ipc_dai_config;
740-
int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w);
741-
int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w);
742+
int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags);
743+
int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags);
742744

743745
#endif

0 commit comments

Comments
 (0)