81 lines
2.9 KiB
Diff
81 lines
2.9 KiB
Diff
|
From 49e46ec03b6dafecec9b2380a3a4444e6d0ea726 Mon Sep 17 00:00:00 2001
|
||
|
From: wm4 <wm4@nowhere>
|
||
|
Date: Wed, 13 Jan 2016 19:42:18 +0100
|
||
|
Subject: [PATCH 129/156] bcm2835: restrict channels*rate to 8*960000
|
||
|
|
||
|
This is required at least for SPDIF. If the bitrate goes above,
|
||
|
videocore will either resample the audio or corrupt it due to
|
||
|
underruns. Supposedly the hardware isn't designed to output
|
||
|
higher rates, but it can still resample it down to supported
|
||
|
rates.
|
||
|
|
||
|
Some code is based on ac97_pcm.c.
|
||
|
---
|
||
|
sound/arm/bcm2835-pcm.c | 41 +++++++++++++++++++++++++++++++++++++++++
|
||
|
1 file changed, 41 insertions(+)
|
||
|
|
||
|
--- a/sound/arm/bcm2835-pcm.c
|
||
|
+++ b/sound/arm/bcm2835-pcm.c
|
||
|
@@ -19,6 +19,9 @@
|
||
|
|
||
|
#include "bcm2835.h"
|
||
|
|
||
|
+/* The hardware can not do much more num_channels*samplerate then this value */
|
||
|
+#define MAX_COMBINED_RATE 768000
|
||
|
+
|
||
|
/* hardware definition */
|
||
|
static struct snd_pcm_hardware snd_bcm2835_playback_hw = {
|
||
|
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||
|
@@ -107,6 +110,31 @@ static irqreturn_t bcm2835_playback_fifo
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
+
|
||
|
+static int rate_hw_constraint_rate(struct snd_pcm_hw_params *params,
|
||
|
+ struct snd_pcm_hw_rule *rule)
|
||
|
+{
|
||
|
+ struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
|
||
|
+ struct snd_interval rates = {
|
||
|
+ .min = 8000,
|
||
|
+ .max = min(192000u, MAX_COMBINED_RATE / max(channels->min, 1u)),
|
||
|
+ };
|
||
|
+ struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
|
||
|
+ return snd_interval_refine(rate, &rates);
|
||
|
+}
|
||
|
+
|
||
|
+static int rate_hw_constraint_channels(struct snd_pcm_hw_params *params,
|
||
|
+ struct snd_pcm_hw_rule *rule)
|
||
|
+{
|
||
|
+ struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
|
||
|
+ struct snd_interval channels_interval = {
|
||
|
+ .min = 1,
|
||
|
+ .max = min(8u, MAX_COMBINED_RATE / max(rate->min, 1u)),
|
||
|
+ };
|
||
|
+ struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
|
||
|
+ return snd_interval_refine(channels, &channels_interval);
|
||
|
+}
|
||
|
+
|
||
|
/* open callback */
|
||
|
static int snd_bcm2835_playback_open_generic(
|
||
|
struct snd_pcm_substream *substream, int spdif)
|
||
|
@@ -188,6 +216,19 @@ static int snd_bcm2835_playback_open_gen
|
||
|
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
|
||
|
16);
|
||
|
|
||
|
+ /* When playing PCM, pretend that we support the full range of channels
|
||
|
+ * and sample rates. The GPU can't output it, but is able to resample
|
||
|
+ * the data to a rate the hardware can handle it. This won't work with
|
||
|
+ * compressed data; the resampler would just destroy it. */
|
||
|
+ if (spdif) {
|
||
|
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
|
||
|
+ rate_hw_constraint_rate, NULL,
|
||
|
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
|
||
|
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||
|
+ rate_hw_constraint_channels, NULL,
|
||
|
+ SNDRV_PCM_HW_PARAM_RATE, -1);
|
||
|
+ }
|
||
|
+
|
||
|
chip->alsa_stream[idx] = alsa_stream;
|
||
|
|
||
|
chip->opened |= (1 << idx);
|