123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- /*
- * PCM DRM helpers
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
- #include <linux/export.h>
- #include <drm/drm_edid.h>
- #include <sound/pcm.h>
- #include <sound/pcm_drm_eld.h>
- static const unsigned int eld_rates[] = {
- 32000,
- 44100,
- 48000,
- 88200,
- 96000,
- 176400,
- 192000,
- };
- static unsigned int sad_max_channels(const u8 *sad)
- {
- return 1 + (sad[0] & 7);
- }
- static int eld_limit_rates(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
- {
- struct snd_interval *r = hw_param_interval(params, rule->var);
- struct snd_interval *c;
- unsigned int rate_mask = 7, i;
- const u8 *sad, *eld = rule->private;
- sad = drm_eld_sad(eld);
- if (sad) {
- c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3) {
- unsigned max_channels = sad_max_channels(sad);
- /*
- * Exclude SADs which do not include the
- * requested number of channels.
- */
- if (c->min <= max_channels)
- rate_mask |= sad[1];
- }
- }
- return snd_interval_list(r, ARRAY_SIZE(eld_rates), eld_rates,
- rate_mask);
- }
- static int eld_limit_channels(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
- {
- struct snd_interval *c = hw_param_interval(params, rule->var);
- struct snd_interval *r;
- struct snd_interval t = { .min = 1, .max = 2, .integer = 1, };
- unsigned int i;
- const u8 *sad, *eld = rule->private;
- sad = drm_eld_sad(eld);
- if (sad) {
- unsigned int rate_mask = 0;
- /* Convert the rate interval to a mask */
- r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
- for (i = 0; i < ARRAY_SIZE(eld_rates); i++)
- if (r->min <= eld_rates[i] && r->max >= eld_rates[i])
- rate_mask |= BIT(i);
- for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3)
- if (rate_mask & sad[1])
- t.max = max(t.max, sad_max_channels(sad));
- }
- return snd_interval_refine(c, &t);
- }
- int snd_pcm_hw_constraint_eld(struct snd_pcm_runtime *runtime, void *eld)
- {
- int ret;
- ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
- eld_limit_rates, eld,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
- if (ret < 0)
- return ret;
- ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- eld_limit_channels, eld,
- SNDRV_PCM_HW_PARAM_RATE, -1);
- return ret;
- }
- EXPORT_SYMBOL_GPL(snd_pcm_hw_constraint_eld);
|