summaryrefslogtreecommitdiff
path: root/sound/soc/intel
diff options
context:
space:
mode:
authorCezary Rojewski <cezary.rojewski@intel.com>2024-04-05 12:09:29 +0300
committerMark Brown <broonie@kernel.org>2024-04-05 15:13:12 +0300
commit9a385993504e47a0fd6fd34b5384827b4abdee60 (patch)
tree5effc8dc4c8d00e09454fd9aaabe3d7f9689d285 /sound/soc/intel
parentc2b10acb62c195db2c976d614d9d8092ad6339ae (diff)
downloadlinux-9a385993504e47a0fd6fd34b5384827b4abdee60.tar.xz
ASoC: Intel: avs: Rule invalid buffer and period sizes out
While HDAudio controller supports buffer packets up to 128 bytes low, audio format shall be taken into consideration when calculating buffer and period sizes to avoid undesired xruns. As *_size in ALSA terms means frames (channels times bit-depth-bytes), hw_rules can calculate minimal buffer and period sizes solely from sample rate and the number of milliseconds commonly used on the AudioDSP firmware side. Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> Link: https://msgid.link/r/20240405090929.1184068-14-cezary.rojewski@intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc/intel')
-rw-r--r--sound/soc/intel/avs/pcm.c28
1 files changed, 28 insertions, 0 deletions
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index 405de1d58178..77a7e8f93951 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -457,6 +457,26 @@ static const struct snd_pcm_hw_constraint_list hw_rates = {
const struct snd_soc_dai_ops avs_dai_fe_ops;
+static int hw_rule_param_size(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *interval = hw_param_interval(params, rule->var);
+ struct snd_interval to;
+
+ snd_interval_any(&to);
+ to.integer = interval->integer;
+ to.max = interval->max;
+ /*
+ * Commonly 2ms buffer size is used in HDA scenarios whereas 4ms is used
+ * when streaming through GPDMA. Align to the latter to account for both.
+ */
+ to.min = params_rate(params) / 1000 * 4;
+
+ if (rule->var == SNDRV_PCM_HW_PARAM_PERIOD_SIZE)
+ to.min /= params_periods(params);
+
+ return snd_interval_refine(interval, &to);
+}
+
static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -492,6 +512,14 @@ static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_so
if (ret < 0)
goto err;
+ /* Adjust buffer and period size based on the audio format. */
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, hw_rule_param_size, NULL,
+ SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_CHANNELS,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, hw_rule_param_size, NULL,
+ SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_CHANNELS,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+
snd_pcm_set_sync(substream);
dev_dbg(dai->dev, "%s fe STARTUP tag %d str %p",