drivers: Intel: hda-dma: Handle two-step stop and pause

The recommended programming sequence for HD-Audio DMA's is to clear the
GEN bit after the host has cleared the RUN bit. For host DMA, we can
replace the stop op with the stop_delayed op so that the GEN bit can be
cleared during host reset. For link DMA, add the stop_delayed op to
perform DMA stop during DAI_CONFIG IPC with hw_free if the two-step stop
flag is set for the DAI. For single-step op, there's no change in
sequence.

The two-step sequence for pause is similar to stop. The DMA will be
stopped with dma_stop_delayed when the DAI_CONFIG IPC with the
SOF_DAI_CONFIG_FLAGS_2_STEP_STOP_PAUSE flag after the host has cleard
the RUN bit.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
This commit is contained in:
Ranjani Sridharan 2021-10-06 10:53:23 -07:00 committed by Liam Girdwood
parent e3b6a326c6
commit 2774464719
1 changed files with 41 additions and 25 deletions

View File

@ -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>
@ -148,7 +149,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,27 +571,7 @@ 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;
@ -624,6 +605,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 +989,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 +1006,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,