Skip to content

Commit 3394e2b

Browse files
bardliaovinodkoul
authored andcommitted
ASoC: SOF: Intel: hda-sdw-bpt: add CHAIN_DMA support
When the firmware is involved, the data can be transferred with a CHAIN_DMA on LNL+. The CHAIN_DMA needs to be programmed before the DMAs per the documentation. The states are not exactly symmetrical, on stop we must do a PAUSE and RESET. The FIFO size of 10ms was determined experimentally. With the minimum of 2ms, errors were reported by the codec, likely because of xruns. The code flow deals with the two TX and RX CHAIN_DMAs in symmetrical ways, i.e. alloc TX alloc RX enable TX enable RX disable RX disable TX free RX free TX Signed-off-by: Pierre-Louis Bossart <[email protected]> Signed-off-by: Bard Liao <[email protected]> Reviewed-by: Péter Ujfalusi <[email protected]> Reviewed-by: Liam Girdwood <[email protected]> Reviewed-by: Ranjani Sridharan <[email protected]> Acked-by: Mark Brown <[email protected]> Tested-by: [email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Vinod Koul <[email protected]>
1 parent 4c1ce9f commit 3394e2b

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed

sound/soc/sof/intel/hda-sdw-bpt.c

+126
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,80 @@
1414
#include <sound/hda-mlink.h>
1515
#include <sound/hda-sdw-bpt.h>
1616
#include <sound/sof.h>
17+
#include <sound/sof/ipc4/header.h>
1718
#include "../ops.h"
1819
#include "../sof-priv.h"
20+
#include "../ipc4-priv.h"
1921
#include "hda.h"
2022

2123
#define BPT_FREQUENCY 192000 /* The max rate defined in rate_bits[] hdac_device.c */
2224
#define BPT_MULTIPLIER ((BPT_FREQUENCY / 48000) - 1)
25+
#define BPT_CHAIN_DMA_FIFO_MS 10
26+
/*
27+
* This routine is directly inspired by sof_ipc4_chain_dma_trigger(),
28+
* with major simplifications since there are no pipelines defined
29+
* and no dependency on ALSA hw_params
30+
*/
31+
static int chain_dma_trigger(struct snd_sof_dev *sdev, unsigned int stream_tag,
32+
int direction, int state)
33+
{
34+
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
35+
bool allocate, enable, set_fifo_size;
36+
struct sof_ipc4_msg msg = {{ 0 }};
37+
int dma_id;
38+
39+
if (sdev->pdata->ipc_type != SOF_IPC_TYPE_4)
40+
return -EOPNOTSUPP;
41+
42+
switch (state) {
43+
case SOF_IPC4_PIPE_RUNNING: /* Allocate and start the chain */
44+
allocate = true;
45+
enable = true;
46+
set_fifo_size = true;
47+
break;
48+
case SOF_IPC4_PIPE_PAUSED: /* Stop the chain */
49+
allocate = true;
50+
enable = false;
51+
set_fifo_size = false;
52+
break;
53+
case SOF_IPC4_PIPE_RESET: /* Deallocate chain resources and remove the chain */
54+
allocate = false;
55+
enable = false;
56+
set_fifo_size = false;
57+
break;
58+
default:
59+
dev_err(sdev->dev, "Unexpected state %d", state);
60+
return -EINVAL;
61+
}
62+
63+
msg.primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_CHAIN_DMA);
64+
msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
65+
msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
66+
67+
/* for BPT/BRA we can use the same stream tag for host and link */
68+
dma_id = stream_tag - 1;
69+
if (direction == SNDRV_PCM_STREAM_CAPTURE)
70+
dma_id += ipc4_data->num_playback_streams;
71+
72+
msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(dma_id);
73+
msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(dma_id);
74+
75+
/* For BPT/BRA we use 32 bits so SCS is not set */
76+
77+
/* CHAIN DMA needs at least 2ms */
78+
if (set_fifo_size)
79+
msg.extension |= SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(BPT_FREQUENCY / 1000 *
80+
BPT_CHAIN_DMA_FIFO_MS *
81+
sizeof(u32));
82+
83+
if (allocate)
84+
msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK;
85+
86+
if (enable)
87+
msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK;
88+
89+
return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
90+
}
2391

2492
static int hda_sdw_bpt_dma_prepare(struct device *dev, struct hdac_ext_stream **sdw_bpt_stream,
2593
struct snd_dma_buffer *dmab_bdl, u32 bpt_num_bytes,
@@ -46,6 +114,21 @@ static int hda_sdw_bpt_dma_prepare(struct device *dev, struct hdac_ext_stream **
46114
}
47115
*sdw_bpt_stream = bpt_stream;
48116

117+
if (!sdev->dspless_mode_selected) {
118+
struct hdac_stream *hstream;
119+
u32 mask;
120+
121+
/* decouple host and link DMA if the DSP is used */
122+
hstream = &bpt_stream->hstream;
123+
mask = BIT(hstream->index);
124+
125+
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, mask, mask);
126+
127+
snd_hdac_ext_stream_reset(bpt_stream);
128+
129+
snd_hdac_ext_stream_setup(bpt_stream, format);
130+
}
131+
49132
if (hdac_stream(bpt_stream)->direction == SNDRV_PCM_STREAM_PLAYBACK) {
50133
struct hdac_bus *bus = sof_to_bus(sdev);
51134
struct hdac_ext_link *hlink;
@@ -63,6 +146,8 @@ static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream
63146
struct snd_dma_buffer *dmab_bdl)
64147
{
65148
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
149+
struct hdac_stream *hstream;
150+
u32 mask;
66151
int ret;
67152

68153
ret = hda_cl_cleanup(sdev->dev, dmab_bdl, true, sdw_bpt_stream);
@@ -83,6 +168,22 @@ static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream
83168
snd_hdac_ext_bus_link_clear_stream_id(hlink, stream_tag);
84169
}
85170

171+
if (!sdev->dspless_mode_selected) {
172+
/* Release CHAIN_DMA resources */
173+
ret = chain_dma_trigger(sdev, hdac_stream(sdw_bpt_stream)->stream_tag,
174+
hdac_stream(sdw_bpt_stream)->direction,
175+
SOF_IPC4_PIPE_RESET);
176+
if (ret < 0)
177+
dev_err(sdev->dev, "%s: chain_dma_trigger PIPE_RESET failed: %d\n",
178+
__func__, ret);
179+
180+
/* couple host and link DMA */
181+
hstream = &sdw_bpt_stream->hstream;
182+
mask = BIT(hstream->index);
183+
184+
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, mask, 0);
185+
}
186+
86187
return 0;
87188
}
88189

@@ -95,6 +196,20 @@ static int hda_sdw_bpt_dma_enable(struct device *dev, struct hdac_ext_stream *sd
95196
if (ret < 0)
96197
dev_err(sdev->dev, "%s: SDW BPT DMA trigger start failed\n", __func__);
97198

199+
if (!sdev->dspless_mode_selected) {
200+
/* the chain DMA needs to be programmed before the DMAs */
201+
ret = chain_dma_trigger(sdev, hdac_stream(sdw_bpt_stream)->stream_tag,
202+
hdac_stream(sdw_bpt_stream)->direction,
203+
SOF_IPC4_PIPE_RUNNING);
204+
if (ret < 0) {
205+
dev_err(sdev->dev, "%s: chain_dma_trigger failed: %d\n",
206+
__func__, ret);
207+
hda_cl_trigger(sdev->dev, sdw_bpt_stream, SNDRV_PCM_TRIGGER_STOP);
208+
return ret;
209+
}
210+
snd_hdac_ext_stream_start(sdw_bpt_stream);
211+
}
212+
98213
return ret;
99214
}
100215

@@ -103,6 +218,17 @@ static int hda_sdw_bpt_dma_disable(struct device *dev, struct hdac_ext_stream *s
103218
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
104219
int ret;
105220

221+
if (!sdev->dspless_mode_selected) {
222+
snd_hdac_ext_stream_clear(sdw_bpt_stream);
223+
224+
ret = chain_dma_trigger(sdev, hdac_stream(sdw_bpt_stream)->stream_tag,
225+
hdac_stream(sdw_bpt_stream)->direction,
226+
SOF_IPC4_PIPE_PAUSED);
227+
if (ret < 0)
228+
dev_err(sdev->dev, "%s: chain_dma_trigger PIPE_PAUSED failed: %d\n",
229+
__func__, ret);
230+
}
231+
106232
ret = hda_cl_trigger(sdev->dev, sdw_bpt_stream, SNDRV_PCM_TRIGGER_STOP);
107233
if (ret < 0)
108234
dev_err(sdev->dev, "%s: SDW BPT DMA trigger stop failed\n", __func__);

0 commit comments

Comments
 (0)