Merge branch 'work.iov_iter' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[muen/linux.git] / sound / core / pcm_drm_eld.c
1 /*
2  *  PCM DRM helpers
3  *
4  *   This program is free software; you can redistribute it and/or modify
5  *   it under the terms of the GNU General Public License version 2 as
6  *   published by the Free Software Foundation.
7  */
8 #include <linux/export.h>
9 #include <drm/drm_edid.h>
10 #include <sound/pcm.h>
11 #include <sound/pcm_drm_eld.h>
12
13 static const unsigned int eld_rates[] = {
14         32000,
15         44100,
16         48000,
17         88200,
18         96000,
19         176400,
20         192000,
21 };
22
23 static unsigned int sad_max_channels(const u8 *sad)
24 {
25         return 1 + (sad[0] & 7);
26 }
27
28 static int eld_limit_rates(struct snd_pcm_hw_params *params,
29                            struct snd_pcm_hw_rule *rule)
30 {
31         struct snd_interval *r = hw_param_interval(params, rule->var);
32         struct snd_interval *c;
33         unsigned int rate_mask = 7, i;
34         const u8 *sad, *eld = rule->private;
35
36         sad = drm_eld_sad(eld);
37         if (sad) {
38                 c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
39
40                 for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3) {
41                         unsigned max_channels = sad_max_channels(sad);
42
43                         /*
44                          * Exclude SADs which do not include the
45                          * requested number of channels.
46                          */
47                         if (c->min <= max_channels)
48                                 rate_mask |= sad[1];
49                 }
50         }
51
52         return snd_interval_list(r, ARRAY_SIZE(eld_rates), eld_rates,
53                                  rate_mask);
54 }
55
56 static int eld_limit_channels(struct snd_pcm_hw_params *params,
57                               struct snd_pcm_hw_rule *rule)
58 {
59         struct snd_interval *c = hw_param_interval(params, rule->var);
60         struct snd_interval *r;
61         struct snd_interval t = { .min = 1, .max = 2, .integer = 1, };
62         unsigned int i;
63         const u8 *sad, *eld = rule->private;
64
65         sad = drm_eld_sad(eld);
66         if (sad) {
67                 unsigned int rate_mask = 0;
68
69                 /* Convert the rate interval to a mask */
70                 r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
71                 for (i = 0; i < ARRAY_SIZE(eld_rates); i++)
72                         if (r->min <= eld_rates[i] && r->max >= eld_rates[i])
73                                 rate_mask |= BIT(i);
74
75                 for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3)
76                         if (rate_mask & sad[1])
77                                 t.max = max(t.max, sad_max_channels(sad));
78         }
79
80         return snd_interval_refine(c, &t);
81 }
82
83 int snd_pcm_hw_constraint_eld(struct snd_pcm_runtime *runtime, void *eld)
84 {
85         int ret;
86
87         ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
88                                   eld_limit_rates, eld,
89                                   SNDRV_PCM_HW_PARAM_CHANNELS, -1);
90         if (ret < 0)
91                 return ret;
92
93         ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
94                                   eld_limit_channels, eld,
95                                   SNDRV_PCM_HW_PARAM_RATE, -1);
96
97         return ret;
98 }
99 EXPORT_SYMBOL_GPL(snd_pcm_hw_constraint_eld);