diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 708fc04f19d778..16a3640728210b 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -29,6 +29,8 @@ #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"); @@ -295,19 +297,35 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32); /* - * The dsp_min_burst_size_in_ms is the length of the minimum burst size + * The dsp_max_burst_size_in_ms is the length of the maximum burst size * of the host DMA in the ALSA buffer. * - * Set a constraint to period time min to be at least twice as long as - * the minimum burst size to avoid DMA overruns + * 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. */ - if (spcm->stream[direction].dsp_min_burst_size_in_ms) { - unsigned int burst_time = spcm->stream[direction].dsp_min_burst_size_in_ms; + 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); snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_PERIOD_TIME, - burst_time * USEC_PER_MSEC * 2, - UINT_MAX); + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + period_time * USEC_PER_MSEC, + UINT_MAX); } /* binding pcm substream to hda stream */ diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index c191e1bcc92960..5a1f3b359f8a12 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -688,15 +688,22 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) goto free_available_fmt; sps = &spcm->stream[dir]; - if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + 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) { struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - 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; + 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; } skip_gtw_cfg: @@ -2037,79 +2044,6 @@ 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; -} - static int sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *fe_params, @@ -2131,6 +2065,7 @@ 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; @@ -2148,6 +2083,16 @@ 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; @@ -2482,19 +2427,34 @@ 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; } diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index c91580f2b12558..a289c1d8f3ff0e 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -76,17 +76,6 @@ /* 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 diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 7523081dddac15..138e5fcc2dd094 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_min_burst_size_in_ms; /* The minimum size of the host DMA burst in ms */ + unsigned int dsp_max_burst_size_in_ms; /* The maximum 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