0001
0002
0003
0004
0005 #include <linux/export.h>
0006 #include <drm/drm_edid.h>
0007 #include <sound/pcm.h>
0008 #include <sound/pcm_drm_eld.h>
0009
0010 static const unsigned int eld_rates[] = {
0011 32000,
0012 44100,
0013 48000,
0014 88200,
0015 96000,
0016 176400,
0017 192000,
0018 };
0019
0020 static unsigned int sad_max_channels(const u8 *sad)
0021 {
0022 return 1 + (sad[0] & 7);
0023 }
0024
0025 static int eld_limit_rates(struct snd_pcm_hw_params *params,
0026 struct snd_pcm_hw_rule *rule)
0027 {
0028 struct snd_interval *r = hw_param_interval(params, rule->var);
0029 const struct snd_interval *c;
0030 unsigned int rate_mask = 7, i;
0031 const u8 *sad, *eld = rule->private;
0032
0033 sad = drm_eld_sad(eld);
0034 if (sad) {
0035 c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
0036
0037 for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3) {
0038 unsigned max_channels = sad_max_channels(sad);
0039
0040
0041
0042
0043
0044 if (c->min <= max_channels)
0045 rate_mask |= sad[1];
0046 }
0047 }
0048
0049 return snd_interval_list(r, ARRAY_SIZE(eld_rates), eld_rates,
0050 rate_mask);
0051 }
0052
0053 static int eld_limit_channels(struct snd_pcm_hw_params *params,
0054 struct snd_pcm_hw_rule *rule)
0055 {
0056 struct snd_interval *c = hw_param_interval(params, rule->var);
0057 const struct snd_interval *r;
0058 struct snd_interval t = { .min = 1, .max = 2, .integer = 1, };
0059 unsigned int i;
0060 const u8 *sad, *eld = rule->private;
0061
0062 sad = drm_eld_sad(eld);
0063 if (sad) {
0064 unsigned int rate_mask = 0;
0065
0066
0067 r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
0068 for (i = 0; i < ARRAY_SIZE(eld_rates); i++)
0069 if (r->min <= eld_rates[i] && r->max >= eld_rates[i])
0070 rate_mask |= BIT(i);
0071
0072 for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3)
0073 if (rate_mask & sad[1])
0074 t.max = max(t.max, sad_max_channels(sad));
0075 }
0076
0077 return snd_interval_refine(c, &t);
0078 }
0079
0080 int snd_pcm_hw_constraint_eld(struct snd_pcm_runtime *runtime, void *eld)
0081 {
0082 int ret;
0083
0084 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
0085 eld_limit_rates, eld,
0086 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
0087 if (ret < 0)
0088 return ret;
0089
0090 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
0091 eld_limit_channels, eld,
0092 SNDRV_PCM_HW_PARAM_RATE, -1);
0093
0094 return ret;
0095 }
0096 EXPORT_SYMBOL_GPL(snd_pcm_hw_constraint_eld);