Skip to content

Commit 9d8f51c

Browse files
digetxbroonie
authored andcommitted
ASoC: tegra20: spdif: Filter out unsupported rates
SPDIF and other SoC components share audio PLL on Tegra, thus only one component may set the desired base clock rate. This creates problem for HDMI audio because it uses SPDIF and audio may not work if SPDIF's clock doesn't exactly match standard audio rate since some receivers may reject audio in that case. Filter out audio rates which SPDIF output can't support, assuming that other components won't change rate at runtime. Signed-off-by: Dmitry Osipenko <[email protected]> Acked-by: Thierry Reding <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent d516930 commit 9d8f51c

File tree

1 file changed

+61
-0
lines changed

1 file changed

+61
-0
lines changed

sound/soc/tegra/tegra20_spdif.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
7979
struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);
8080
unsigned int mask = 0, val = 0;
8181
int ret, spdifclock;
82+
long rate;
8283

8384
mask |= TEGRA20_SPDIF_CTRL_PACK |
8485
TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;
@@ -133,6 +134,12 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
133134
return ret;
134135
}
135136

137+
rate = clk_get_rate(spdif->clk_spdif_out);
138+
if (rate != spdifclock)
139+
dev_warn_once(dai->dev,
140+
"SPDIF clock rate %d doesn't match requested rate %lu\n",
141+
spdifclock, rate);
142+
136143
return 0;
137144
}
138145

@@ -172,6 +179,59 @@ static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
172179
return 0;
173180
}
174181

182+
static int tegra20_spdif_filter_rates(struct snd_pcm_hw_params *params,
183+
struct snd_pcm_hw_rule *rule)
184+
{
185+
struct snd_interval *r = hw_param_interval(params, rule->var);
186+
struct snd_soc_dai *dai = rule->private;
187+
struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);
188+
struct clk *parent = clk_get_parent(spdif->clk_spdif_out);
189+
const unsigned int rates[] = { 32000, 44100, 48000 };
190+
long i, parent_rate, valid_rates = 0;
191+
192+
parent_rate = clk_get_rate(parent);
193+
if (parent_rate <= 0) {
194+
dev_err(dai->dev, "Can't get parent clock rate: %ld\n",
195+
parent_rate);
196+
return parent_rate ?: -EINVAL;
197+
}
198+
199+
for (i = 0; i < ARRAY_SIZE(rates); i++) {
200+
if (parent_rate % (rates[i] * 128) == 0)
201+
valid_rates |= BIT(i);
202+
}
203+
204+
/*
205+
* At least one rate must be valid, otherwise the parent clock isn't
206+
* audio PLL. Nothing should be filtered in this case.
207+
*/
208+
if (!valid_rates)
209+
valid_rates = BIT(ARRAY_SIZE(rates)) - 1;
210+
211+
return snd_interval_list(r, ARRAY_SIZE(rates), rates, valid_rates);
212+
}
213+
214+
static int tegra20_spdif_startup(struct snd_pcm_substream *substream,
215+
struct snd_soc_dai *dai)
216+
{
217+
if (!device_property_read_bool(dai->dev, "nvidia,fixed-parent-rate"))
218+
return 0;
219+
220+
/*
221+
* SPDIF and I2S share audio PLL. HDMI takes audio packets from SPDIF
222+
* and audio may not work on some TVs if clock rate isn't precise.
223+
*
224+
* PLL rate is controlled by I2S side. Filter out audio rates that
225+
* don't match PLL rate at the start of stream to allow both SPDIF
226+
* and I2S work simultaneously, assuming that PLL rate won't be
227+
* changed later on.
228+
*/
229+
return snd_pcm_hw_rule_add(substream->runtime, 0,
230+
SNDRV_PCM_HW_PARAM_RATE,
231+
tegra20_spdif_filter_rates, dai,
232+
SNDRV_PCM_HW_PARAM_RATE, -1);
233+
}
234+
175235
static int tegra20_spdif_probe(struct snd_soc_dai *dai)
176236
{
177237
struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);
@@ -185,6 +245,7 @@ static int tegra20_spdif_probe(struct snd_soc_dai *dai)
185245
static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = {
186246
.hw_params = tegra20_spdif_hw_params,
187247
.trigger = tegra20_spdif_trigger,
248+
.startup = tegra20_spdif_startup,
188249
};
189250

190251
static struct snd_soc_dai_driver tegra20_spdif_dai = {

0 commit comments

Comments
 (0)