-
Notifications
You must be signed in to change notification settings - Fork 350
Fix HDA DMA stop sequence #4844
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
Changes from all commits
007c617
ed3599c
133d162
adf1405
5abc075
44158b3
97bf71c
f0fe7d2
dc252d7
936526b
083113f
e0cb877
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -219,6 +219,7 @@ static void dai_free(struct comp_dev *dev) | |
| if (dd->chan) { | ||
| notifier_unregister(dev, dd->chan, NOTIFIER_ID_DMA_COPY); | ||
| dma_channel_put(dd->chan); | ||
| dd->chan->dev_data = NULL; | ||
| } | ||
|
|
||
| dma_put(dd->dma); | ||
|
|
@@ -599,6 +600,8 @@ static int dai_config_prepare(struct comp_dev *dev) | |
| return -EIO; | ||
| } | ||
|
|
||
| dd->chan->dev_data = dd; | ||
|
|
||
| comp_info(dev, "dai_config_prepare(): new configured dma channel index %d", | ||
| dd->chan->index); | ||
|
|
||
|
|
@@ -609,27 +612,6 @@ static int dai_config_prepare(struct comp_dev *dev) | |
| return 0; | ||
| } | ||
|
|
||
| static void dai_config_reset(struct comp_dev *dev) | ||
|
||
| { | ||
| struct dai_data *dd = comp_get_drvdata(dev); | ||
|
|
||
| /* cannot configure DAI while active */ | ||
| if (dev->state == COMP_STATE_ACTIVE) { | ||
| comp_info(dev, "dai_config(): Component is in active state. Ignore resetting"); | ||
| return; | ||
| } | ||
|
|
||
| /* put the allocated DMA channel first */ | ||
| if (dd->chan) { | ||
| dma_channel_put(dd->chan); | ||
| dd->chan = NULL; | ||
|
|
||
| /* remove callback */ | ||
| notifier_unregister(dev, dd->chan, | ||
| NOTIFIER_ID_DMA_COPY); | ||
| } | ||
| } | ||
|
|
||
| static int dai_prepare(struct comp_dev *dev) | ||
| { | ||
| struct dai_data *dd = comp_get_drvdata(dev); | ||
|
|
@@ -686,7 +668,12 @@ static int dai_reset(struct comp_dev *dev) | |
|
|
||
| comp_info(dev, "dai_reset()"); | ||
|
|
||
| dai_config_reset(dev); | ||
| /* | ||
| * DMA channel release should be skipped now for DAI's that support the two-step stop option. | ||
| * It will be done when the host sends the DAI_CONFIG IPC during hw_free. | ||
| */ | ||
| if (!dd->delayed_dma_stop) | ||
| dai_dma_release(dev); | ||
|
|
||
| dma_sg_free(&config->elem_array); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -847,9 +847,12 @@ static int host_reset(struct comp_dev *dev) | |
| comp_dbg(dev, "host_reset()"); | ||
kv2019i marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| if (hd->chan) { | ||
| dma_stop_delayed(hd->chan); | ||
|
|
||
|
||
| /* remove callback */ | ||
| notifier_unregister(dev, hd->chan, NOTIFIER_ID_DMA_COPY); | ||
| dma_channel_put(hd->chan); | ||
| hd->chan = NULL; | ||
| } | ||
|
|
||
| /* free all DMA elements */ | ||
|
|
@@ -863,9 +866,6 @@ static int host_reset(struct comp_dev *dev) | |
| hd->dma_buffer = NULL; | ||
| } | ||
|
|
||
| /* reset dma channel as we have put it */ | ||
| hd->chan = NULL; | ||
|
|
||
| host_pointer_reset(dev); | ||
| hd->copy_type = COMP_COPY_NORMAL; | ||
| hd->source = NULL; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ | |
| #include <sof/lib/alloc.h> | ||
| #include <sof/lib/clk.h> | ||
| #include <sof/lib/cpu.h> | ||
| #include <sof/lib/dai.h> | ||
| #include <sof/lib/dma.h> | ||
| #include <sof/lib/pm_runtime.h> | ||
| #include <sof/lib/notifier.h> | ||
|
|
@@ -28,6 +29,77 @@ | |
| #include <stddef.h> | ||
| #include <stdint.h> | ||
|
|
||
| /* | ||
| * HD-Audio DMA programming sequence | ||
| * | ||
| * START stream (Playback): | ||
| * 1. Host sends the DAI_CONFIG IPC with SOF_DAI_CONFIG_FLAGS_2_STEP_STOP_PAUSE flag along with | ||
| * SOF_DAI_CONFIG_FLAGS_HW_PARAMS | ||
|
||
| * 2. Host sets DGCS.RUN bit for link DMA. This step would be skipped if link DMA is already | ||
| * running with mixer-based pipelines | ||
| * 3. Host sets DGCS.RUN bit for host DMA | ||
| * 4. Host sends the STREAM_TRIG_START IPC to the DSP | ||
| * 5. FW starts the pipeline and sets DGCS.GEN bit to 1 | ||
| * | ||
| * START stream (Capture): | ||
| * 1. Host sends the DAI_CONFIG IPC with SOF_DAI_CONFIG_FLAGS_2_STEP_STOP_PAUSE flag along with | ||
| * SOF_DAI_CONFIG_FLAGS_HW_PARAMS | ||
| * 2. Host sets DGCS.RUN bit for host DMA | ||
| * 3. Host sends the STREAM_TRIG_START IPC to the DSP | ||
| * 4. FW starts the pipeline and sets DGCS.GEN bit to 1 | ||
| * 5. Host sets DGCS.RUN bit for link DMA | ||
| * | ||
| * PAUSE_PUSH an active stream (Playback): | ||
| * 1. Host sends the STREAM_TRIG_PAUSE IPC to the DSP | ||
| * 2. FW pauses the pipeline but keeps the DGCS.GEN bit set to 1 for host DMA | ||
| * 3. Host clears DGCS.RUN bit for host DMA | ||
| * 4. Host clears DGCS.RUN bit for link DMA | ||
|
||
| * 5. Host sends the DAI_CONFIG IPC with only the SOF_DAI_CONFIG_FLAGS_PAUSE command flag | ||
| * 6. FW clears the DGCS.GEN bit for link DMA | ||
| * | ||
| * PAUSE_PUSH an active stream (Capture): | ||
| * 1. Host clears DGCS.RUN bit for link DMA | ||
| * 2. Host sends the DAI_CONFIG IPC with only the SOF_DAI_CONFIG_FLAGS_PAUSE command flag | ||
| * 3. FW clears the DGCS.GEN bit for link DMA | ||
| * 4. Host sends the STREAM_TRIG_PAUSE IPC to the DSP | ||
| * 5. FW pauses the pipeline but keeps DGCS.GEN bit set to 1 for host DMA | ||
| * 6. Host clears DGCS.RUN bit for host DMA | ||
| * | ||
| * PAUSE_RELEASE a paused stream (Playback): | ||
| * 1. Host sets DGCS.RUN bit for link DMA | ||
| * 2. Host sets DGCS.RUN bit for host DMA | ||
| * 3. Host sends the STREAM_TRIG_PAUSE_RELEASE IPC to the DSP | ||
| * 4. FW releases the pipeline and sets the DGCS.GEN bit to 1 | ||
| * | ||
| * PAUSE_RELEASE a paused stream (Capture): | ||
| * 1. Host sets DGCS.RUN bit for host DMA | ||
| * 2. Host sends the STREAM_TRIG_PAUSE_RELEASE IPC to the DSP | ||
| * 3. FW releases the pipeline and sets the DGCS.GEN bit to 1 | ||
| * 4. Host sets DGCS.RUN bit for link DMA | ||
| * | ||
| * STOP an active/paused stream (Playback): | ||
| * 1. Host sends the STREAM_TRIG_STOP IPC to the DSP | ||
| * 2. FW stops the pipeline | ||
| * 3. Host clears the DGCS.RUN bit for host DMA | ||
| * 4. Host sends the PCM_FREE IPC | ||
| * 5. FW clears DGCS.GEN bit for host DMA during host component reset and checks DGCS.GBUSY bit | ||
| * to ensure DMA is idle | ||
| * 6. Host clears DGCS.RUN bit for link DMA | ||
| * 7. Host sends the DAI_CONFIG IPC with the SOF_DAI_CONFIG_FLAGS_HW_FREE flag | ||
| * 8. FW clears the DGCS.GEN bit for link DMA and checks DGCS.GBUSY bit to ensure DMA is idle | ||
|
||
| * | ||
| * STOP an active/paused stream (Capture): | ||
| * 1. Host clears DGCS.RUN bit for link DMA | ||
| * 2. Host sends the DAI_CONFIG IPC with the SOF_DAI_CONFIG_FLAGS_HW_FREE flag | ||
|
||
| * 3. FW clears the DGCS.GEN bit for link DMA and checks GBUSY bit to ensure DMA is idle | ||
| * 4. Host sends the STREAM_TRIG_STOP IPC to the DSP | ||
| * 5. FW stops the pipeline | ||
| * 6. Host clears the DGCS.RUN bit for host DMA | ||
| * 7. Host sends the PCM_FREE IPC | ||
| * 8. FW clears DGCS.GEN bit for host DMA during host component reset and checks DGCS.GBUSY bit | ||
| * to ensure DMA is idle | ||
| */ | ||
|
|
||
| /* ee12fa71-4579-45d7-bde2-b32c6893a122 */ | ||
| DECLARE_SOF_UUID("hda-dma", hda_dma_uuid, 0xee12fa71, 0x4579, 0x45d7, | ||
| 0xbd, 0xe2, 0xb3, 0x2c, 0x68, 0x93, 0xa1, 0x22); | ||
|
|
@@ -57,6 +129,7 @@ DECLARE_TR_CTX(hdma_tr, SOF_UUID(hda_dma_uuid), LOG_LEVEL_INFO); | |
| #define DGCS_BF BIT(9) /* buffer full */ | ||
| #define DGCS_BNE BIT(8) /* buffer not empty */ | ||
| #define DGCS_FIFORDY BIT(5) /* enable FIFO */ | ||
| #define DGCS_GBUSY BIT(15) /* indicates if the DMA channel is idle or not */ | ||
|
|
||
| /* DGBBA */ | ||
| #define DGBBA_MASK 0xffff80 | ||
|
|
@@ -148,7 +221,7 @@ struct hda_chan_data { | |
| #endif | ||
| }; | ||
|
|
||
| static int hda_dma_stop(struct dma_chan_data *channel); | ||
| static int hda_dma_stop_common(struct dma_chan_data *channel); | ||
|
|
||
| static inline void hda_dma_inc_fp(struct dma_chan_data *chan, | ||
| uint32_t value) | ||
|
|
@@ -570,30 +643,10 @@ static int hda_dma_release(struct dma_chan_data *channel) | |
| return 0; | ||
| } | ||
|
|
||
| static int hda_dma_pause(struct dma_chan_data *channel) | ||
| { | ||
| uint32_t flags; | ||
|
|
||
| irq_local_disable(flags); | ||
|
|
||
| tr_dbg(&hdma_tr, "hda-dmac: %d channel %d -> pause", | ||
| channel->dma->plat_data.id, channel->index); | ||
|
|
||
| if (channel->status != COMP_STATE_ACTIVE) | ||
| goto out; | ||
|
|
||
| /* stop the channel */ | ||
| hda_dma_stop(channel); | ||
|
|
||
| out: | ||
| irq_local_enable(flags); | ||
| return 0; | ||
| } | ||
|
|
||
| static int hda_dma_stop(struct dma_chan_data *channel) | ||
| static int hda_dma_stop_common(struct dma_chan_data *channel) | ||
| { | ||
| struct hda_chan_data *hda_chan; | ||
| uint32_t flags; | ||
| uint32_t flags, dgcs; | ||
|
|
||
| irq_local_disable(flags); | ||
|
|
||
|
|
@@ -613,6 +666,15 @@ static int hda_dma_stop(struct dma_chan_data *channel) | |
| } else { | ||
| dma_chan_reg_update_bits(channel, DGCS, DGCS_GEN, 0); | ||
| } | ||
|
|
||
| /* check if channel is idle. No need to wait after clearing the GEN bit */ | ||
| dgcs = dma_chan_reg_read(channel, DGCS); | ||
| if (dgcs & DGCS_GBUSY) { | ||
| tr_err(&hdma_tr, "hda-dmac: %d channel %d not idle after stop", | ||
| channel->dma->plat_data.id, channel->index); | ||
| irq_local_enable(flags); | ||
| return -EBUSY; | ||
kv2019i marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
plbossart marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| channel->status = COMP_STATE_PREPARE; | ||
| hda_chan = dma_chan_get_data(channel); | ||
| hda_chan->state = 0; | ||
|
|
@@ -624,6 +686,40 @@ static int hda_dma_stop(struct dma_chan_data *channel) | |
| return 0; | ||
| } | ||
|
|
||
| static int hda_dma_link_stop(struct dma_chan_data *channel) | ||
| { | ||
| struct dai_data *dd = channel->dev_data; | ||
|
|
||
| if (!dd) { | ||
| tr_err(&hdma_tr, "hda-dmac: %d channel %d no device data", | ||
| channel->dma->plat_data.id, channel->index); | ||
| return -EINVAL; | ||
| } | ||
|
|
||
| /* | ||
| * The delayed_dma_stop flag will be set with the newer kernel and DMA will be stopped during | ||
| * reset. With older kernel, the DMA will be stopped during the stop/pause trigger. | ||
| */ | ||
| if (!dd->delayed_dma_stop) | ||
| return hda_dma_stop_common(channel); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| static int hda_dma_link_pause(struct dma_chan_data *channel) | ||
| { | ||
| /* | ||
| * with two-step pause, DMA will be stopped when the DAI_CONFIG IPC is sent with the | ||
| * SOF_DAI_CONFIG_FLAGS_TWO_STEP_STOP flag | ||
| */ | ||
| return hda_dma_link_stop(channel); | ||
| } | ||
|
|
||
| static int hda_dma_stop_delayed(struct dma_chan_data *channel) | ||
| { | ||
| return hda_dma_stop_common(channel); | ||
| } | ||
|
|
||
| /* fill in "status" with current DMA channel state and position */ | ||
| static int hda_dma_status(struct dma_chan_data *channel, | ||
| struct dma_chan_status *status, uint8_t direction) | ||
|
|
@@ -974,7 +1070,7 @@ const struct dma_ops hda_host_dma_ops = { | |
| .channel_get = hda_dma_channel_get, | ||
| .channel_put = hda_dma_channel_put, | ||
| .start = hda_dma_start, | ||
| .stop = hda_dma_stop, | ||
| .stop_delayed = hda_dma_stop_delayed, | ||
| .copy = hda_dma_host_copy, | ||
| .status = hda_dma_status, | ||
| .set_config = hda_dma_set_config, | ||
|
|
@@ -991,9 +1087,10 @@ const struct dma_ops hda_link_dma_ops = { | |
| .channel_get = hda_dma_channel_get, | ||
| .channel_put = hda_dma_channel_put, | ||
| .start = hda_dma_start, | ||
| .stop = hda_dma_stop, | ||
| .stop = hda_dma_link_stop, | ||
| .stop_delayed = hda_dma_stop_delayed, | ||
| .copy = hda_dma_link_copy, | ||
| .pause = hda_dma_pause, | ||
| .pause = hda_dma_link_pause, | ||
| .release = hda_dma_release, | ||
| .status = hda_dma_status, | ||
| .set_config = hda_dma_set_config, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.