@@ -78,6 +78,103 @@ static int hdmi_codec_new_stream(struct snd_pcm_substream *substream,
return ret;
}
+static const uint8_t *eld_sad(const uint8_t *eld)
+{
+ unsigned int ver, mnl;
+
+ ver = (eld[DRM_ELD_VER] & DRM_ELD_VER_MASK) >> DRM_ELD_VER_SHIFT;
+ if (ver != 2 && ver != 31)
+ return NULL;
+
+ mnl = drm_eld_mnl(eld);
+ if (mnl > 16)
+ return NULL;
+
+ return eld + DRM_ELD_CEA_SAD(mnl, 0);
+}
+
+static const unsigned int eld_rates[] = {
+ 32000,
+ 44100,
+ 48000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+};
+
+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);
+ unsigned int rate_mask = 7, i;
+ const u8 *sad, *eld = rule->private;
+
+ sad = eld_sad(eld);
+ if (sad) {
+ for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3) {
+ unsigned channels = 1 + (sad[0] & 7);
+
+ /*
+ * Exclude SADs which do not include the
+ * requested number of channels.
+ */
+ if (params_channels(params) == 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 *var = hw_param_interval(params, rule->var);
+ struct snd_interval t = { .min = 1, .max = 2, .integer = 1, };
+ unsigned int i, j;
+ const u8 *sad, *eld = rule->private;
+ int rate = params_rate(params);
+
+ sad = eld_sad(eld);
+ if (!sad)
+ return 0;
+
+ for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3) {
+ for (j = 0; j < ARRAY_SIZE(eld_rates); j++) {
+ if ((sad[1] & (1<<j)) && rate == eld_rates[j]) {
+ switch (sad[0] & 0x78) {
+ case 0x08:
+ t.max = max(t.max, (sad[0] & 7) + 1u);
+ break;
+ }
+ }
+ }
+ }
+
+ return snd_interval_refine(var, &t);
+}
+
+static int hdmi_codec_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_RATE,
+ 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_CHANNELS,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+
+ return ret;
+}
+
static int hdmi_codec_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -104,7 +201,12 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
if (hcp->hcd.ops->get_eld) {
hcp->eld = hcp->hcd.ops->get_eld(hcp->hcd.dev);
- /* Call snd_pcm_hw_constraint_eld here */
+ if (hcp->eld) {
+ ret = hdmi_codec_constraint_eld(substream->runtime,
+ hcp->eld);
+ if (ret)
+ return ret;
+ }
}
return 0;
}
This patch is mostly just a copy paste from Russel King's generic patchs[1] for the same thing. The patche is included only for testing purposes. Do not merge! [1] http://lists.freedesktop.org/archives/dri-devel/2015-April/080525.html Signed-off-by: Jyri Sarha <jsarha@ti.com> --- sound/soc/codecs/hdmi-codec.c | 104 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-)