1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
From 4b235ff95b0fcfa9731c1159402f4fef0e26f1d3 Mon Sep 17 00:00:00 2001
From: wm4 <wm4@nowhere>
Date: Wed, 13 Jan 2016 19:42:18 +0100
Subject: [PATCH] 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);
|