Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 8 additions & 26 deletions sound/soc/sof/intel/hda-pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@
#define SDnFMT_BITS(x) ((x) << 4)
#define SDnFMT_CHAN(x) ((x) << 0)

#define HDA_MAX_PERIOD_TIME_HEADROOM 10

static bool hda_always_enable_dmi_l1;
module_param_named(always_enable_dmi_l1, hda_always_enable_dmi_l1, bool, 0444);
MODULE_PARM_DESC(always_enable_dmi_l1, "SOF HDA always enable DMI l1");
Expand Down Expand Up @@ -287,35 +285,19 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32);

/*
* The dsp_max_burst_size_in_ms is the length of the maximum burst size
* The dsp_min_burst_size_in_ms is the length of the minimum burst size
* of the host DMA in the ALSA buffer.
*
* On playback start the DMA will transfer dsp_max_burst_size_in_ms
* amount of data in one initial burst to fill up the host DMA buffer.
* Consequent DMA burst sizes are shorter and their length can vary.
* To avoid immediate xrun by the initial burst we need to place
* constraint on the period size (via PERIOD_TIME) to cover the size of
* the host buffer.
* We need to add headroom of max 10ms as the firmware needs time to
* settle to the 1ms pacing and initially it can run faster for few
* internal periods.
*
* On capture the DMA will transfer 1ms chunks.
* Set a constraint to period time min to be at least twice as long as
* the minimum burst size to avoid DMA overruns
*/
if (spcm->stream[direction].dsp_max_burst_size_in_ms) {
unsigned int period_time = spcm->stream[direction].dsp_max_burst_size_in_ms;

/*
* add headroom over the maximum burst size to cover the time
* needed for the DMA pace to settle.
* Limit the headroom time to HDA_MAX_PERIOD_TIME_HEADROOM
*/
period_time += min(period_time, HDA_MAX_PERIOD_TIME_HEADROOM);
if (spcm->stream[direction].dsp_min_burst_size_in_ms) {
unsigned int burst_time = spcm->stream[direction].dsp_min_burst_size_in_ms;

snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
period_time * USEC_PER_MSEC,
UINT_MAX);
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
burst_time * USEC_PER_MSEC * 2,
UINT_MAX);
}

/* binding pcm substream to hda stream */
Expand Down
128 changes: 84 additions & 44 deletions sound/soc/sof/ipc4-topology.c
Original file line number Diff line number Diff line change
Expand Up @@ -669,22 +669,15 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
goto free_available_fmt;

sps = &spcm->stream[dir];
sof_update_ipc_object(scomp, &sps->dsp_max_burst_size_in_ms,
SOF_COPIER_DEEP_BUFFER_TOKENS,
swidget->tuples,
swidget->num_tuples, sizeof(u32), 1);

/* Set default DMA buffer size if it is not specified in topology */
if (!sps->dsp_max_burst_size_in_ms) {
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;

if (dir == SNDRV_PCM_STREAM_PLAYBACK)
sps->dsp_max_burst_size_in_ms = pipeline->use_chain_dma ?
SOF_IPC4_CHAIN_DMA_BUFFER_SIZE : SOF_IPC4_MIN_DMA_BUFFER_SIZE;
else
/* Capture data is copied from DSP to host in 1ms bursts */
sps->dsp_max_burst_size_in_ms = 1;
sps->dsp_min_burst_size_in_ms = pipeline->use_chain_dma ?
SOF_IPC4_CHAIN_DMA_BUFFER_SIZE : SOF_IPC4_MIN_DMA_BUFFER_SIZE;
} else {
/* Capture data is copied from DSP to host in 1ms bursts */
sps->dsp_min_burst_size_in_ms = 1;
}

skip_gtw_cfg:
Expand Down Expand Up @@ -2042,6 +2035,79 @@ static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget
copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(host_dma_id);
}

static void
sof_ipc4_set_host_copier_dma_buffer_size(struct snd_sof_widget *swidget,
unsigned int fe_period_bytes)
{
unsigned int min_size, no_headroom_mark, fw_period_bytes;
struct snd_soc_component *scomp = swidget->scomp;
struct sof_ipc4_copier_data *copier_data;
struct sof_ipc4_copier *ipc4_copier;
unsigned int deep_buffer_dma_ms = 0;
u32 buffer_bytes;
int ret;

ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
copier_data = &ipc4_copier->data;

if (swidget->id == snd_soc_dapm_aif_in)
fw_period_bytes = copier_data->base_config.ibs;
else
fw_period_bytes = copier_data->base_config.obs;

/*
* Calculate the minimum size of the host copier DMA host buffer and the
* cut-out watermark when no headroom is needed to be added between the
* host copier buffer size and the ALSA period size
*/
min_size = SOF_IPC4_MIN_DMA_BUFFER_SIZE * fw_period_bytes;
no_headroom_mark = SOF_IPC4_NO_DMA_BUFFER_HEADROOM_MS * fw_period_bytes;

/* parse the deep buffer dma size */
ret = sof_update_ipc_object(scomp, &deep_buffer_dma_ms,
SOF_COPIER_DEEP_BUFFER_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(u32), 1);
if (ret) {
dev_dbg(scomp->dev,
"Failed to parse deep buffer dma size for %s\n",
swidget->widget->name);
buffer_bytes = min_size;
goto out;
}

/*
* Non Deepbuffer and small ALSA periods must use the minimal host DMA
* buffer size in firmware.
* Note: smaller than 2x the minimum host DMA buffer size for ALSA
* period is not allowed and should be protected by platform code with
* constraint.
*
* Add headroom the between host copier DMA buffer size and the ALSA
* period size if the ALSA period is less than
* SOF_IPC4_NO_DMA_BUFFER_HEADROOM_MS, otherwise equal the host copier
* DMA buffer size to ALSA period size, capped at the maximum DeepBuffer
* depth specified in topology
*/
if (deep_buffer_dma_ms <= SOF_IPC4_MIN_DMA_BUFFER_SIZE ||
fe_period_bytes < (min_size * 2))
buffer_bytes = min_size;
else if (fe_period_bytes < no_headroom_mark)
buffer_bytes = fe_period_bytes - min_size;
else
buffer_bytes = min(deep_buffer_dma_ms * fw_period_bytes,
fe_period_bytes);

out:
dev_dbg(scomp->dev,
"%s, dma buffer%s: %u ms (max: %u) / %u bytes, ALSA period: %u / %u\n",
swidget->widget->name, deep_buffer_dma_ms ? " (using Deep Buffer)" : "",
buffer_bytes / fw_period_bytes,
deep_buffer_dma_ms ? deep_buffer_dma_ms : SOF_IPC4_MIN_DMA_BUFFER_SIZE,
buffer_bytes, fe_period_bytes / fw_period_bytes, fe_period_bytes);

copier_data->gtw_cfg.dma_buffer_size = buffer_bytes;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also wonder about using "host buffer size" (e.g. in sof_ipc4_set_host_dma_buffer_size() function name here. In DSP world, we use "host DMA" and "DAI DMA" terms universally to distinguish when we are talking about data transfer towards Linux host, or when it's about DMAs used to transfer data to audio codec interfaces.

But this is in context of Linux kernel. Here we have two buffers, one in host DDR (the ALSA buffer DMA buffer) and one in DSP SRAM. What this function calculates is the size of the DSP SRAM buffer, and I'd argue calling this the "host buffer" in this context, is hard to follow.

Not a blocker. If this is used in enough places elsewhere in the driver, the damage is already done and we can just move on, but please consider if I'm alone with my confusion, or could naming be made easier to follow.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, but I will run out of columns on a 4k monitor if I go and exact-word name variables...
fw_host_dma_buffer_size, it is not a fw_dma_buffer as DAI side also have DMA buffer.

So I settled in the middle: ALSA buffer vs host DMA buffer.

as for the copier_data->gtw_cfg.dma_buffer_size, it is a message to the fw to configure the copier with that size of buffer.

But changing the function to sof_ipc4_set_fw_host_dma_buffer_size is a bit too much, it sets the dma_buffer_size for the host copier.

Any suggestions?

}

static int
sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_pcm_hw_params *fe_params,
Expand All @@ -2063,7 +2129,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
u32 **data;
int ipc_size, ret, out_ref_valid_bits;
u32 out_ref_rate, out_ref_channels, out_ref_type;
u32 deep_buffer_dma_ms = 0;
bool single_output_bitdepth;
int i;

Expand All @@ -2081,16 +2146,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
str_yes_no(pipeline->use_chain_dma),
platform_params->stream_tag);

/* parse the deep buffer dma size */
ret = sof_update_ipc_object(scomp, &deep_buffer_dma_ms,
SOF_COPIER_DEEP_BUFFER_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(u32), 1);
if (ret) {
dev_err(scomp->dev, "Failed to parse deep buffer dma size for %s\n",
swidget->widget->name);
return ret;
}

ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
gtw_attr = ipc4_copier->gtw_attr;
copier_data = &ipc4_copier->data;
Expand Down Expand Up @@ -2425,34 +2480,19 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
* in topology.
*/
switch (swidget->id) {
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
sof_ipc4_set_host_copier_dma_buffer_size(swidget,
params_period_bytes(fe_params));
break;
case snd_soc_dapm_dai_in:
copier_data->gtw_cfg.dma_buffer_size =
SOF_IPC4_MIN_DMA_BUFFER_SIZE * copier_data->base_config.ibs;
break;
case snd_soc_dapm_aif_in:
copier_data->gtw_cfg.dma_buffer_size =
max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms) *
copier_data->base_config.ibs;
dev_dbg(sdev->dev, "copier %s, dma buffer%s: %u ms (%u bytes)",
swidget->widget->name,
deep_buffer_dma_ms ? " (using Deep Buffer)" : "",
max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms),
copier_data->gtw_cfg.dma_buffer_size);
break;
case snd_soc_dapm_dai_out:
copier_data->gtw_cfg.dma_buffer_size =
SOF_IPC4_MIN_DMA_BUFFER_SIZE * copier_data->base_config.obs;
break;
case snd_soc_dapm_aif_out:
copier_data->gtw_cfg.dma_buffer_size =
max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms) *
copier_data->base_config.obs;
dev_dbg(sdev->dev, "copier %s, dma buffer%s: %u ms (%u bytes)",
swidget->widget->name,
deep_buffer_dma_ms ? " (using Deep Buffer)" : "",
max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms),
copier_data->gtw_cfg.dma_buffer_size);
break;
default:
break;
}
Expand Down
11 changes: 11 additions & 0 deletions sound/soc/sof/ipc4-topology.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,17 @@
/* ChainDMA in fw uses 5ms DMA buffer */
#define SOF_IPC4_CHAIN_DMA_BUFFER_SIZE 5

/*
* When the host DMA buffer size is larger than 8ms, the firmware switches from
* a constant fill mode to burst mode, keeping a 4ms threshold to trigger a
* transfer of approximately host DMA buffer size - 4ms after the initial burst
* to fill the entire buffer.
* To simplify the logic, above 20ms ALSA period size use the same size for host
* DMA buffer, while if the ALSA period size is smaller than 20ms, then use a
* headroom between host DMA buffer and ALSA period size.
*/
#define SOF_IPC4_NO_DMA_BUFFER_HEADROOM_MS 20

/*
* The base of multi-gateways. Multi-gateways addressing starts from
* ALH_MULTI_GTW_BASE and there are ALH_MULTI_GTW_COUNT multi-sources
Expand Down
2 changes: 1 addition & 1 deletion sound/soc/sof/sof-audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ struct snd_sof_pcm_stream {
struct snd_soc_dapm_widget_list *list; /* list of connected DAPM widgets */
bool d0i3_compatible; /* DSP can be in D0I3 when this pcm is opened */
bool pause_supported; /* PCM device supports PAUSE operation */
unsigned int dsp_max_burst_size_in_ms; /* The maximum size of the host DMA burst in ms */
unsigned int dsp_min_burst_size_in_ms; /* The minimum size of the host DMA burst in ms */
/*
* flag to indicate that the DSP pipelines should be kept
* active or not while suspending the stream
Expand Down
Loading