diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index da6c1e7263cde1..0718e338aca2be 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -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"); @@ -287,36 +285,17 @@ 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) snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, - period_time * USEC_PER_MSEC, + spcm->stream[direction].dsp_min_burst_size_in_ms * USEC_PER_MSEC, UINT_MAX); - } /* binding pcm substream to hda stream */ substream->runtime->private_data = &dsp_stream->hstream; diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index c3c01d45663a79..efa435b4498540 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -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: @@ -2042,6 +2035,67 @@ 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_dma_buffer_size(struct snd_sof_widget *swidget, + unsigned int fe_period_bytes) +{ + unsigned int min_size, max_size, headroom, host_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) + host_period_bytes = copier_data->base_config.ibs; + else + host_period_bytes = copier_data->base_config.obs; + + min_size = SOF_IPC4_MIN_DMA_BUFFER_SIZE * host_period_bytes; + headroom = min(SOF_IPC4_ALSA_PERIOD_MAX_HEADROOM_MS * host_period_bytes, + fe_period_bytes / 2); + + /* 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; + } + + max_size = deep_buffer_dma_ms * host_period_bytes; + + /* + * Non Deepbuffer and small ALSA periods must use the minimal host DMA + * buffer size. + * 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 + */ + if (deep_buffer_dma_ms <= SOF_IPC4_MIN_DMA_BUFFER_SIZE || + (min_size * 2) > fe_period_bytes) + buffer_bytes = min_size; + else + buffer_bytes = min(max_size, fe_period_bytes - headroom); + +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 / host_period_bytes, + deep_buffer_dma_ms ? deep_buffer_dma_ms : SOF_IPC4_MIN_DMA_BUFFER_SIZE, + buffer_bytes, fe_period_bytes / host_period_bytes, fe_period_bytes); + + copier_data->gtw_cfg.dma_buffer_size = buffer_bytes; +} + static int sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *fe_params, @@ -2063,7 +2117,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; @@ -2081,16 +2134,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; @@ -2425,34 +2468,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_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; } diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index a289c1d8f3ff0e..90b567f6706af1 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -76,6 +76,14 @@ /* ChainDMA in fw uses 5ms DMA buffer */ #define SOF_IPC4_CHAIN_DMA_BUFFER_SIZE 5 +/* + * When Deep buffer is enabled for a device we need to keep a headroom between + * the host DMA buffer and ALSA period size to compensate for DMA bursts. + * The maximum headroom is 10ms, which is based on how the firmware moves data + * between host buffer and the rest of the pipeline. + */ +#define SOF_IPC4_ALSA_PERIOD_MAX_HEADROOM_MS 10 + /* * The base of multi-gateways. Multi-gateways addressing starts from * ALH_MULTI_GTW_BASE and there are ALH_MULTI_GTW_COUNT multi-sources diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 36082e764bf99e..15fb4699cf3645 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -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