Merge tag 'iommu-updates-v4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/joro...
[cascardo/linux.git] / sound / hda / hdmi_chmap.c
index d7ec862..c6c75e7 100644 (file)
@@ -625,13 +625,30 @@ static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
        WARN_ON(count != channels);
 }
 
+static int spk_mask_from_spk_alloc(int spk_alloc)
+{
+       int i;
+       int spk_mask = eld_speaker_allocation_bits[0];
+
+       for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+               if (spk_alloc & (1 << i))
+                       spk_mask |= eld_speaker_allocation_bits[i];
+       }
+
+       return spk_mask;
+}
+
 static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                              unsigned int size, unsigned int __user *tlv)
 {
        struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
        struct hdac_chmap *chmap = info->private_data;
+       int pcm_idx = kcontrol->private_value;
        unsigned int __user *dst;
        int chs, count = 0;
+       unsigned long max_chs;
+       int type;
+       int spk_alloc, spk_mask;
 
        if (size < 8)
                return -ENOMEM;
@@ -639,40 +656,59 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                return -EFAULT;
        size -= 8;
        dst = tlv + 2;
-       for (chs = 2; chs <= chmap->channels_max; chs++) {
+
+       spk_alloc = chmap->ops.get_spk_alloc(chmap->hdac, pcm_idx);
+       spk_mask = spk_mask_from_spk_alloc(spk_alloc);
+
+       max_chs = hweight_long(spk_mask);
+
+       for (chs = 2; chs <= max_chs; chs++) {
                int i;
                struct hdac_cea_channel_speaker_allocation *cap;
 
                cap = channel_allocations;
                for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
                        int chs_bytes = chs * 4;
-                       int type = chmap->ops.chmap_cea_alloc_validate_get_type(
-                                                               chmap, cap, chs);
                        unsigned int tlv_chmap[8];
 
-                       if (type < 0)
+                       if (cap->channels != chs)
+                               continue;
+
+                       if (!(cap->spk_mask == (spk_mask & cap->spk_mask)))
                                continue;
+
+                       type = chmap->ops.chmap_cea_alloc_validate_get_type(
+                                                       chmap, cap, chs);
+                       if (type < 0)
+                               return -ENODEV;
                        if (size < 8)
                                return -ENOMEM;
+
                        if (put_user(type, dst) ||
                            put_user(chs_bytes, dst + 1))
                                return -EFAULT;
+
                        dst += 2;
                        size -= 8;
                        count += 8;
+
                        if (size < chs_bytes)
                                return -ENOMEM;
+
                        size -= chs_bytes;
                        count += chs_bytes;
                        chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,
                                                tlv_chmap, chs);
+
                        if (copy_to_user(dst, tlv_chmap, chs_bytes))
                                return -EFAULT;
                        dst += chs;
                }
        }
+
        if (put_user(count, tlv + 1))
                return -EFAULT;
+
        return 0;
 }