Commit 44fde3b8 authored by Subhransu S. Prusty's avatar Subhransu S. Prusty Committed by Takashi Iwai

ALSA: hda - Update chmap tlv to report sink's capability

The existing TLV callback implementation copies all of the
cea_channel_speaker_allocation map table to the TLV container
irrespective of what is reported by sink. This is of little use
to the userspace application.

With this patch, it parses the spk_alloc block as queried from
the ELD, and copies only the corresponding mapping channel
allocation entries from the cea channel speaker allocation table.
Thus the user can parse the TLV container to identify sink's
capability and set the channel map accordingly.

It shouldn't impact the behavior in AMD chipset, as this makes
use of already parsed spk alloc block to calculate the channel
map.
Signed-off-by: default avatarSubhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: default avatarVinod Koul <vinod.koul@intel.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent a99e3151
...@@ -36,6 +36,8 @@ struct hdac_chmap_ops { ...@@ -36,6 +36,8 @@ struct hdac_chmap_ops {
int (*chmap_validate)(struct hdac_chmap *hchmap, int ca, int (*chmap_validate)(struct hdac_chmap *hchmap, int ca,
int channels, unsigned char *chmap); int channels, unsigned char *chmap);
int (*get_spk_alloc)(struct hdac_device *hdac, int pcm_idx);
void (*get_chmap)(struct hdac_device *hdac, int pcm_idx, void (*get_chmap)(struct hdac_device *hdac, int pcm_idx,
unsigned char *chmap); unsigned char *chmap);
void (*set_chmap)(struct hdac_device *hdac, int pcm_idx, void (*set_chmap)(struct hdac_device *hdac, int pcm_idx,
......
...@@ -625,13 +625,30 @@ static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, ...@@ -625,13 +625,30 @@ static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
WARN_ON(count != channels); 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, static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *tlv) unsigned int size, unsigned int __user *tlv)
{ {
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
struct hdac_chmap *chmap = info->private_data; struct hdac_chmap *chmap = info->private_data;
int pcm_idx = kcontrol->private_value;
unsigned int __user *dst; unsigned int __user *dst;
int chs, count = 0; int chs, count = 0;
unsigned long max_chs;
int type;
int spk_alloc, spk_mask;
if (size < 8) if (size < 8)
return -ENOMEM; return -ENOMEM;
...@@ -639,40 +656,59 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, ...@@ -639,40 +656,59 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
return -EFAULT; return -EFAULT;
size -= 8; size -= 8;
dst = tlv + 2; 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; int i;
struct hdac_cea_channel_speaker_allocation *cap; struct hdac_cea_channel_speaker_allocation *cap;
cap = channel_allocations; cap = channel_allocations;
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
int chs_bytes = chs * 4; int chs_bytes = chs * 4;
int type = chmap->ops.chmap_cea_alloc_validate_get_type(
chmap, cap, chs);
unsigned int tlv_chmap[8]; unsigned int tlv_chmap[8];
if (type < 0) if (cap->channels != chs)
continue;
if (!(cap->spk_mask == (spk_mask & cap->spk_mask)))
continue; continue;
type = chmap->ops.chmap_cea_alloc_validate_get_type(
chmap, cap, chs);
if (type < 0)
return -ENODEV;
if (size < 8) if (size < 8)
return -ENOMEM; return -ENOMEM;
if (put_user(type, dst) || if (put_user(type, dst) ||
put_user(chs_bytes, dst + 1)) put_user(chs_bytes, dst + 1))
return -EFAULT; return -EFAULT;
dst += 2; dst += 2;
size -= 8; size -= 8;
count += 8; count += 8;
if (size < chs_bytes) if (size < chs_bytes)
return -ENOMEM; return -ENOMEM;
size -= chs_bytes; size -= chs_bytes;
count += chs_bytes; count += chs_bytes;
chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap, chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,
tlv_chmap, chs); tlv_chmap, chs);
if (copy_to_user(dst, tlv_chmap, chs_bytes)) if (copy_to_user(dst, tlv_chmap, chs_bytes))
return -EFAULT; return -EFAULT;
dst += chs; dst += chs;
} }
} }
if (put_user(count, tlv + 1)) if (put_user(count, tlv + 1))
return -EFAULT; return -EFAULT;
return 0; return 0;
} }
......
...@@ -1837,6 +1837,18 @@ static const struct hda_pcm_ops generic_ops = { ...@@ -1837,6 +1837,18 @@ static const struct hda_pcm_ops generic_ops = {
.cleanup = generic_hdmi_playback_pcm_cleanup, .cleanup = generic_hdmi_playback_pcm_cleanup,
}; };
static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
{
struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
if (!per_pin)
return 0;
return per_pin->sink_eld.info.spk_alloc;
}
static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
unsigned char *chmap) unsigned char *chmap)
{ {
...@@ -2165,6 +2177,7 @@ static int alloc_generic_hdmi(struct hda_codec *codec) ...@@ -2165,6 +2177,7 @@ static int alloc_generic_hdmi(struct hda_codec *codec)
spec->chmap.ops.get_chmap = hdmi_get_chmap; spec->chmap.ops.get_chmap = hdmi_get_chmap;
spec->chmap.ops.set_chmap = hdmi_set_chmap; spec->chmap.ops.set_chmap = hdmi_set_chmap;
spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached; spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
spec->chmap.ops.get_spk_alloc = hdmi_get_spk_alloc,
codec->spec = spec; codec->spec = spec;
hdmi_array_init(spec, 4); hdmi_array_init(spec, 4);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment