[alsa-devel] [PATCH v2 0/7] ALSA: hda: Move chmap helpers to core
The HDMI channel map helper APIs and control registration can be reused by ASoC skylake HDMI drivers.
A common chmap object is created, channel map control callback handlers, helper APIs and data structures are moved to the core. Driver object store an instance of this chmap object to register chmap controls and program channel allocation for HDMI infoframe. Driver registers ops for specific operations.
changes in v2: - Splitted patches for easier review - Access most of chmap helpers through ops, thus reducing number of exported APIs for chmap support
Subhransu S. Prusty (7): ALSA: hda - Create common chmap object ALSA: hda - Register chmap obj as priv data instead of codec ALSA: hda - Use hdmi name space for CEA spk alloc structure ALSA: hda - Add hdmi chmap verb programming ops to chmap object ALSA: hda - Use ops instead of directly accessing chmap helpers ALSA: hda - Move chmap support helpers/ops to core ALSA: hda - Use snd_hdmi namespace prefix for printing ch alloc api
include/sound/hdmi_chmap.h | 75 ++++ sound/hda/Makefile | 2 +- sound/hda/hdmi_chmap.c | 791 ++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_eld.c | 31 +- sound/pci/hda/patch_hdmi.c | 830 +++++---------------------------------------- 5 files changed, 957 insertions(+), 772 deletions(-) create mode 100644 include/sound/hdmi_chmap.h create mode 100644 sound/hda/hdmi_chmap.c
chmap object represents multichannel capablity and contains chmap ops. Legacy driver is udpated to use this.
With next set of patches chmap object is moved to common to be reused by other drivers (ex: skylake ASoC hdmi driver).
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdmi_chmap.h | 41 +++++++++++++++++++++++ sound/pci/hda/patch_hdmi.c | 81 +++++++++++++++++++++++----------------------- 2 files changed, 81 insertions(+), 41 deletions(-) create mode 100644 include/sound/hdmi_chmap.h
diff --git a/include/sound/hdmi_chmap.h b/include/sound/hdmi_chmap.h new file mode 100644 index 0000000..32087f1 --- /dev/null +++ b/include/sound/hdmi_chmap.h @@ -0,0 +1,41 @@ +/* + * For multichannel support + */ + +#ifndef __SOUND_HDMI_CHMAP_H +#define __SOUND_HDMI_CHMAP_H + +#include <sound/hdaudio.h> + +struct cea_channel_speaker_allocation { + int ca_index; + int speakers[8]; + + /* derived values, just for convenience */ + int channels; + int spk_mask; +}; +struct hdmi_chmap; + +struct hdmi_chmap_ops { + /* + * Helpers for producing the channel map TLVs. These can be overridden + * for devices that have non-standard mapping requirements. + */ + int (*chmap_cea_alloc_validate_get_type)(struct hdmi_chmap *chmap, + struct cea_channel_speaker_allocation *cap, int channels); + void (*cea_alloc_to_tlv_chmap) + (struct cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels); + + /* check that the user-given chmap is supported */ + int (*chmap_validate)(int ca, int channels, unsigned char *chmap); +}; + +struct hdmi_chmap { + unsigned int channels_max; /* max over all cvts */ + struct hdmi_chmap_ops ops; + struct hdac_device *hdac; +}; + +#endif /* __SOUND_HDMI_CHMAP_H */ diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 490931d..fd5ae6f 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -39,6 +39,7 @@ #include <sound/tlv.h> #include <sound/hdaudio.h> #include <sound/hda_i915.h> +#include <sound/hdmi_chmap.h> #include "hda_codec.h" #include "hda_local.h" #include "hda_jack.h" @@ -121,15 +122,6 @@ struct hdmi_ops { int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid, hda_nid_t pin_nid, u32 stream_tag, int format);
- /* Helpers for producing the channel map TLVs. These can be overridden - * for devices that have non-standard mapping requirements. */ - int (*chmap_cea_alloc_validate_get_type)(struct cea_channel_speaker_allocation *cap, - int channels); - void (*cea_alloc_to_tlv_chmap)(struct cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels); - - /* check that the user-given chmap is supported */ - int (*chmap_validate)(int ca, int channels, unsigned char *chmap); };
struct hdmi_pcm { @@ -155,7 +147,6 @@ struct hdmi_spec { * bit 1 means the second playback PCM, and so on. */ unsigned long pcm_in_use; - unsigned int channels_max; /* max over all cvts */
struct hdmi_eld temp_eld; struct hdmi_ops ops; @@ -171,6 +162,8 @@ struct hdmi_spec { /* i915/powerwell (Haswell+/Valleyview+) specific */ struct i915_audio_component_audio_ops i915_audio_ops; bool i915_bound; /* was i915 bound in this driver? */ + + struct hdmi_chmap chmap; };
#ifdef CONFIG_SND_HDA_I915 @@ -264,15 +257,6 @@ static int eld_speaker_allocation_bits[] = { [10] = FCH, };
-struct cea_channel_speaker_allocation { - int ca_index; - int speakers[8]; - - /* derived values, just for convenience */ - int channels; - int spk_mask; -}; - /* * ALSA sequence is: * @@ -2141,8 +2125,8 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) per_cvt->channels_min = 2; if (chans <= 16) { per_cvt->channels_max = chans; - if (chans > spec->channels_max) - spec->channels_max = chans; + if (chans > spec->chmap.channels_max) + spec->chmap.channels_max = chans; }
err = snd_hda_query_supported_pcm(codec, cvt_nid, @@ -2368,15 +2352,17 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; + struct hdmi_chmap *chmap = &spec->chmap; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = spec->channels_max; + uinfo->count = chmap->channels_max; uinfo->value.integer.min = 0; uinfo->value.integer.max = SNDRV_CHMAP_LAST; return 0; }
-static int hdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, - int channels) +static int hdmi_chmap_cea_alloc_validate_get_type(struct hdmi_chmap *chmap, + struct cea_channel_speaker_allocation *cap, int channels) { /* If the speaker allocation matches the channel count, it is OK.*/ if (cap->channels != channels) @@ -2409,6 +2395,7 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; + struct hdmi_chmap *chmap = &spec->chmap; unsigned int __user *dst; int chs, count = 0;
@@ -2418,13 +2405,14 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -EFAULT; size -= 8; dst = tlv + 2; - for (chs = 2; chs <= spec->channels_max; chs++) { + for (chs = 2; chs <= chmap->channels_max; chs++) { int i; struct 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 = spec->ops.chmap_cea_alloc_validate_get_type(cap, chs); + int type = chmap->ops.chmap_cea_alloc_validate_get_type( + chmap, cap, chs); unsigned int tlv_chmap[8];
if (type < 0) @@ -2441,7 +2429,7 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -ENOMEM; size -= chs_bytes; count += chs_bytes; - spec->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs); + chmap->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs); if (copy_to_user(dst, tlv_chmap, chs_bytes)) return -EFAULT; dst += chs; @@ -2458,12 +2446,13 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; + struct hdmi_chmap *chmap = &spec->chmap; int pcm_idx = kcontrol->private_value; struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); int i;
if (!per_pin) { - for (i = 0; i < spec->channels_max; i++) + for (i = 0; i < chmap->channels_max; i++) ucontrol->value.integer.value[i] = 0; return 0; } @@ -2479,6 +2468,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; + struct hdmi_chmap *hchmap = &spec->chmap; int pcm_idx = kcontrol->private_value; struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); unsigned int ctl_idx; @@ -2514,8 +2504,8 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); if (ca < 0) return -EINVAL; - if (spec->ops.chmap_validate) { - err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap); + if (hchmap->ops.chmap_validate) { + err = hchmap->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap); if (err) return err; } @@ -2806,6 +2796,9 @@ static const struct hdmi_ops generic_standard_hdmi_ops = { .pin_setup_infoframe = hdmi_pin_setup_infoframe, .pin_hbr_setup = hdmi_pin_hbr_setup, .setup_stream = hdmi_setup_stream, +}; + +static const struct hdmi_chmap_ops chmap_ops = { .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, }; @@ -2912,6 +2905,8 @@ static int patch_generic_hdmi(struct hda_codec *codec)
spec->ops = generic_standard_hdmi_ops; mutex_init(&spec->pcm_lock); + spec->chmap.ops = chmap_ops; + spec->chmap.hdac = &codec->core; codec->spec = spec; hdmi_array_init(spec, 4);
@@ -3498,13 +3493,14 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) * - 0x10de0015 * - 0x10de0040 */ -static int nvhdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, - int channels) +static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdmi_chmap *chmap, + struct cea_channel_speaker_allocation *cap, int channels) { if (cap->ca_index == 0x00 && channels == 2) return SNDRV_CTL_TLVT_CHMAP_FIXED;
- return hdmi_chmap_cea_alloc_validate_get_type(cap, channels); + return chmap->ops.chmap_cea_alloc_validate_get_type( + chmap, cap, channels); }
static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map) @@ -3527,9 +3523,9 @@ static int patch_nvhdmi(struct hda_codec *codec) spec = codec->spec; spec->dyn_pin_out = true;
- spec->ops.chmap_cea_alloc_validate_get_type = + spec->chmap.ops.chmap_cea_alloc_validate_get_type = nvhdmi_chmap_cea_alloc_validate_get_type; - spec->ops.chmap_validate = nvhdmi_chmap_validate; + spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
return 0; } @@ -3888,8 +3884,10 @@ static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_n return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd; }
-static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, - int channels) +static int atihdmi_paired_chmap_cea_alloc_validate_get_type( + struct hdmi_chmap *chmap, + struct cea_channel_speaker_allocation *cap, + int channels) { int c;
@@ -4036,10 +4034,11 @@ static int patch_atihdmi(struct hda_codec *codec)
if (!has_amd_full_remap_support(codec)) { /* override to ATI/AMD-specific versions with pairwise mapping */ - spec->ops.chmap_cea_alloc_validate_get_type = + spec->chmap.ops.chmap_cea_alloc_validate_get_type = atihdmi_paired_chmap_cea_alloc_validate_get_type; - spec->ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap; - spec->ops.chmap_validate = atihdmi_paired_chmap_validate; + spec->chmap.ops.cea_alloc_to_tlv_chmap = + atihdmi_paired_cea_alloc_to_tlv_chmap; + spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate; }
/* ATI/AMD converters do not advertise all of their capabilities */ @@ -4051,7 +4050,7 @@ static int patch_atihdmi(struct hda_codec *codec) per_cvt->maxbps = max(per_cvt->maxbps, 24u); }
- spec->channels_max = max(spec->channels_max, 8u); + spec->chmap.channels_max = max(spec->chmap.channels_max, 8u);
return 0; }
With this chmap object is added as private data and new ops are added to access driver specific chmap.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdmi_chmap.h | 6 +++ sound/pci/hda/patch_hdmi.c | 100 ++++++++++++++++++++++++++++++--------------- 2 files changed, 72 insertions(+), 34 deletions(-)
diff --git a/include/sound/hdmi_chmap.h b/include/sound/hdmi_chmap.h index 32087f1..c7702ef9 100644 --- a/include/sound/hdmi_chmap.h +++ b/include/sound/hdmi_chmap.h @@ -30,6 +30,12 @@ struct hdmi_chmap_ops {
/* check that the user-given chmap is supported */ int (*chmap_validate)(int ca, int channels, unsigned char *chmap); + + void (*get_chmap)(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap); + void (*set_chmap)(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap, int prepared); + bool (*is_monitor_connected)(struct hdac_device *hdac, int pcm_idx); };
struct hdmi_chmap { diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index fd5ae6f..ed51d0d 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -100,8 +100,6 @@ struct hdmi_spec_per_pin { #endif };
-struct cea_channel_speaker_allocation; - /* operations used by generic code that can be overridden by patches */ struct hdmi_ops { int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid, @@ -2350,9 +2348,7 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hda_codec *codec = info->private_data; - struct hdmi_spec *spec = codec->spec; - struct hdmi_chmap *chmap = &spec->chmap; + struct hdmi_chmap *chmap = info->private_data;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = chmap->channels_max; @@ -2389,13 +2385,49 @@ static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *c WARN_ON(count != channels); }
+static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap) +{ + 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); + + /* chmap is already set to 0 in caller */ + if (!per_pin) + return; + + memcpy(chmap, per_pin->chmap, ARRAY_SIZE(per_pin->chmap)); +} + +static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap, int prepared) +{ + 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); + + mutex_lock(&per_pin->lock); + per_pin->chmap_set = true; + memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap)); + if (prepared) + hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); + mutex_unlock(&per_pin->lock); +} + +static bool is_hdmi_monitor_connected(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); + + return per_pin ? true:false; +} + 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 hda_codec *codec = info->private_data; - struct hdmi_spec *spec = codec->spec; - struct hdmi_chmap *chmap = &spec->chmap; + struct hdmi_chmap *chmap = info->private_data; unsigned int __user *dst; int chs, count = 0;
@@ -2444,21 +2476,17 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hda_codec *codec = info->private_data; - struct hdmi_spec *spec = codec->spec; - struct hdmi_chmap *chmap = &spec->chmap; + struct hdmi_chmap *chmap = info->private_data; int pcm_idx = kcontrol->private_value; - struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + unsigned char pcm_chmap[8]; int i;
- if (!per_pin) { - for (i = 0; i < chmap->channels_max; i++) - ucontrol->value.integer.value[i] = 0; - return 0; - } + memset(pcm_chmap, 0, sizeof(pcm_chmap)); + chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap); + + for (i = 0; i < sizeof(chmap); i++) + ucontrol->value.integer.value[i] = pcm_chmap[i];
- for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++) - ucontrol->value.integer.value[i] = per_pin->chmap[i]; return 0; }
@@ -2466,20 +2494,17 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hda_codec *codec = info->private_data; - struct hdmi_spec *spec = codec->spec; - struct hdmi_chmap *hchmap = &spec->chmap; + struct hdmi_chmap *hchmap = info->private_data; int pcm_idx = kcontrol->private_value; - struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); unsigned int ctl_idx; struct snd_pcm_substream *substream; - unsigned char chmap[8]; + unsigned char chmap[8], per_pin_chmap[8]; int i, err, ca, prepared = 0;
/* No monitor is connected in dyn_pcm_assign. * It's invalid to setup the chmap */ - if (!per_pin) + if (!hchmap->ops.is_monitor_connected(hchmap->hdac, pcm_idx)) return 0;
ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); @@ -2499,7 +2524,9 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, memset(chmap, 0, sizeof(chmap)); for (i = 0; i < ARRAY_SIZE(chmap); i++) chmap[i] = ucontrol->value.integer.value[i]; - if (!memcmp(chmap, per_pin->chmap, sizeof(chmap))) + + hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap); + if (!memcmp(chmap, per_pin_chmap, sizeof(chmap))) return 0; ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); if (ca < 0) @@ -2509,12 +2536,8 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, if (err) return err; } - mutex_lock(&per_pin->lock); - per_pin->chmap_set = true; - memcpy(per_pin->chmap, chmap, sizeof(chmap)); - if (prepared) - hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); - mutex_unlock(&per_pin->lock); + + hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared);
return 0; } @@ -2672,7 +2695,7 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) if (err < 0) return err; /* override handlers */ - chmap->private_data = codec; + chmap->private_data = &spec->chmap; kctl = chmap->kctl; for (i = 0; i < kctl->count; i++) kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; @@ -2801,9 +2824,11 @@ static const struct hdmi_ops generic_standard_hdmi_ops = { static const struct hdmi_chmap_ops chmap_ops = { .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, + .get_chmap = hdmi_get_chmap, + .set_chmap = hdmi_set_chmap, + .is_monitor_connected = is_hdmi_monitor_connected, };
- static void intel_haswell_fixup_connect_list(struct hda_codec *codec, hda_nid_t nid) { @@ -3526,6 +3551,9 @@ static int patch_nvhdmi(struct hda_codec *codec) spec->chmap.ops.chmap_cea_alloc_validate_get_type = nvhdmi_chmap_cea_alloc_validate_get_type; spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; + spec->chmap.ops.get_chmap = hdmi_get_chmap; + spec->chmap.ops.set_chmap = hdmi_set_chmap; + spec->chmap.ops.is_monitor_connected = is_hdmi_monitor_connected;
return 0; } @@ -4039,6 +4067,10 @@ static int patch_atihdmi(struct hda_codec *codec) spec->chmap.ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap; spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate; + spec->chmap.ops.get_chmap = hdmi_get_chmap; + spec->chmap.ops.set_chmap = hdmi_set_chmap; + spec->chmap.ops.is_monitor_connected = + is_hdmi_monitor_connected; }
/* ATI/AMD converters do not advertise all of their capabilities */
On Wed, 02 Mar 2016 13:03:23 +0100, Subhransu S. Prusty wrote:
With this chmap object is added as private data and new ops are added to access driver specific chmap.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
include/sound/hdmi_chmap.h | 6 +++ sound/pci/hda/patch_hdmi.c | 100 ++++++++++++++++++++++++++++++--------------- 2 files changed, 72 insertions(+), 34 deletions(-)
diff --git a/include/sound/hdmi_chmap.h b/include/sound/hdmi_chmap.h index 32087f1..c7702ef9 100644 --- a/include/sound/hdmi_chmap.h +++ b/include/sound/hdmi_chmap.h @@ -30,6 +30,12 @@ struct hdmi_chmap_ops {
/* check that the user-given chmap is supported */ int (*chmap_validate)(int ca, int channels, unsigned char *chmap);
- void (*get_chmap)(struct hdac_device *hdac, int pcm_idx,
unsigned char *chmap);
- void (*set_chmap)(struct hdac_device *hdac, int pcm_idx,
unsigned char *chmap, int prepared);
- bool (*is_monitor_connected)(struct hdac_device *hdac, int pcm_idx);
This name is rather confusing. Actually, this function doesn't mean the monitor connected state. It indicates rather whether any pin is assigned to the given PCM. It may return false only for devices with dynamic PCM assignment. For the rest, it's always true.
Takashi
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdmi_chmap.h | 6 +++--- sound/pci/hda/patch_hdmi.c | 26 ++++++++++++++------------ 2 files changed, 17 insertions(+), 15 deletions(-)
diff --git a/include/sound/hdmi_chmap.h b/include/sound/hdmi_chmap.h index c7702ef9..937ddb2 100644 --- a/include/sound/hdmi_chmap.h +++ b/include/sound/hdmi_chmap.h @@ -7,7 +7,7 @@
#include <sound/hdaudio.h>
-struct cea_channel_speaker_allocation { +struct hdmi_cea_channel_speaker_allocation { int ca_index; int speakers[8];
@@ -23,9 +23,9 @@ struct hdmi_chmap_ops { * for devices that have non-standard mapping requirements. */ int (*chmap_cea_alloc_validate_get_type)(struct hdmi_chmap *chmap, - struct cea_channel_speaker_allocation *cap, int channels); + struct hdmi_cea_channel_speaker_allocation *cap, int channels); void (*cea_alloc_to_tlv_chmap) - (struct cea_channel_speaker_allocation *cap, + (struct hdmi_cea_channel_speaker_allocation *cap, unsigned int *chmap, int channels);
/* check that the user-given chmap is supported */ diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index ed51d0d..5219484 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -297,7 +297,7 @@ static int hdmi_channel_mapping[0x32][8] = { * The preceding ones have better chances to be selected by * hdmi_channel_allocation(). */ -static struct cea_channel_speaker_allocation channel_allocations[] = { +static struct hdmi_cea_channel_speaker_allocation channel_allocations[] = { /* channel: 7 6 5 4 3 2 1 0 */ { .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, /* 2.1 */ @@ -683,7 +683,7 @@ static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin) static void init_channel_allocations(void) { int i, j; - struct cea_channel_speaker_allocation *p; + struct hdmi_cea_channel_speaker_allocation *p;
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { p = channel_allocations + i; @@ -791,7 +791,7 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, int ca) { struct hdmi_spec *spec = codec->spec; - struct cea_channel_speaker_allocation *ch_alloc; + struct hdmi_cea_channel_speaker_allocation *ch_alloc; int i; int err; int order; @@ -2358,7 +2358,7 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, }
static int hdmi_chmap_cea_alloc_validate_get_type(struct hdmi_chmap *chmap, - struct cea_channel_speaker_allocation *cap, int channels) + struct hdmi_cea_channel_speaker_allocation *cap, int channels) { /* If the speaker allocation matches the channel count, it is OK.*/ if (cap->channels != channels) @@ -2368,8 +2368,9 @@ static int hdmi_chmap_cea_alloc_validate_get_type(struct hdmi_chmap *chmap, return SNDRV_CTL_TLVT_CHMAP_VAR; }
-static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels) +static void hdmi_cea_alloc_to_tlv_chmap( + struct hdmi_cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) { int count = 0; int c; @@ -2439,7 +2440,7 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, dst = tlv + 2; for (chs = 2; chs <= chmap->channels_max; chs++) { int i; - struct cea_channel_speaker_allocation *cap; + struct hdmi_cea_channel_speaker_allocation *cap; cap = channel_allocations; for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { int chs_bytes = chs * 4; @@ -3519,7 +3520,7 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) * - 0x10de0040 */ static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdmi_chmap *chmap, - struct cea_channel_speaker_allocation *cap, int channels) + struct hdmi_cea_channel_speaker_allocation *cap, int channels) { if (cap->ca_index == 0x00 && channels == 2) return SNDRV_CTL_TLVT_CHMAP_FIXED; @@ -3803,7 +3804,7 @@ static int atihdmi_paired_swap_fc_lfe(int pos)
static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map) { - struct cea_channel_speaker_allocation *cap; + struct hdmi_cea_channel_speaker_allocation *cap; int i, j;
/* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */ @@ -3914,7 +3915,7 @@ static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_n
static int atihdmi_paired_chmap_cea_alloc_validate_get_type( struct hdmi_chmap *chmap, - struct cea_channel_speaker_allocation *cap, + struct hdmi_cea_channel_speaker_allocation *cap, int channels) { int c; @@ -3942,8 +3943,9 @@ static int atihdmi_paired_chmap_cea_alloc_validate_get_type( return SNDRV_CTL_TLVT_CHMAP_PAIRED; }
-static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels) +static void atihdmi_paired_cea_alloc_to_tlv_chmap( + struct hdmi_cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) { /* produce paired maps for pre-rev3 ATI/AMD codecs */ int count = 0;
On Wed, 02 Mar 2016 13:03:24 +0100, Subhransu S. Prusty wrote:
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
A slight concern is that the code we have is specific to HD-audio while the header and symbols don't indicate it in your patch. If it were designed for a generic purpose HDMI/DP, it's fine. But the current code appears fairly specific to HDA, so it should be a bit more uniquely prefixed, I suppose, since there are some other parallel developments regarding HDMI for other platforms.
thanks,
Takashi
Add slot and channel count programming to hdmi_chmap object and move the chmap_ops to core. Use register_chmap_ops API to register for default ops. Override specific chmap ops in the driver.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdmi_chmap.h | 10 ++++++ sound/hda/Makefile | 2 +- sound/hda/hdmi_chmap.c | 49 ++++++++++++++++++++++++++ sound/pci/hda/patch_hdmi.c | 87 +++++++++++++++------------------------------- 4 files changed, 88 insertions(+), 60 deletions(-) create mode 100644 sound/hda/hdmi_chmap.c
diff --git a/include/sound/hdmi_chmap.h b/include/sound/hdmi_chmap.h index 937ddb2..1ce630d 100644 --- a/include/sound/hdmi_chmap.h +++ b/include/sound/hdmi_chmap.h @@ -36,6 +36,14 @@ struct hdmi_chmap_ops { void (*set_chmap)(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap, int prepared); bool (*is_monitor_connected)(struct hdac_device *hdac, int pcm_idx); + + /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */ + int (*pin_get_slot_channel)(struct hdac_device *codec, + hda_nid_t pin_nid, int asp_slot); + int (*pin_set_slot_channel)(struct hdac_device *codec, + hda_nid_t pin_nid, int asp_slot, int channel); + void (*set_channel_count)(struct hdac_device *codec, + hda_nid_t cvt_nid, int chs); };
struct hdmi_chmap { @@ -44,4 +52,6 @@ struct hdmi_chmap { struct hdac_device *hdac; };
+void snd_hdmi_register_chmap_ops(struct hdac_device *hdac, + struct hdmi_chmap *chmap); #endif /* __SOUND_HDMI_CHMAP_H */ diff --git a/sound/hda/Makefile b/sound/hda/Makefile index 7e999c9..3b9bede 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,5 +1,5 @@ snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \ - hdac_regmap.o hdac_controller.o hdac_stream.o array.o + hdac_regmap.o hdac_controller.o hdac_stream.o array.o hdmi_chmap.o
snd-hda-core-objs += trace.o CFLAGS_trace.o := -I$(src) diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c new file mode 100644 index 0000000..6346e9a --- /dev/null +++ b/sound/hda/hdmi_chmap.c @@ -0,0 +1,49 @@ +/* + * HDMI Channel map support helpers + */ + +#include <sound/hdmi_chmap.h> + +static int hdmi_pin_set_slot_channel(struct hdac_device *codec, + hda_nid_t pin_nid, int asp_slot, int channel) +{ + return snd_hdac_codec_write(codec, pin_nid, 0, + AC_VERB_SET_HDMI_CHAN_SLOT, + (channel << 4) | asp_slot); +} + +static int hdmi_pin_get_slot_channel(struct hdac_device *codec, + hda_nid_t pin_nid, int asp_slot) +{ + return (snd_hdac_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_CHAN_SLOT, + asp_slot) & 0xf0) >> 4; +} + +static int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid) +{ + return 1 + snd_hdac_codec_read(codec, cvt_nid, 0, + AC_VERB_GET_CVT_CHAN_COUNT, 0); +} + +static void hdmi_set_channel_count(struct hdac_device *codec, + hda_nid_t cvt_nid, int chs) +{ + if (chs != hdmi_get_channel_count(codec, cvt_nid)) + snd_hdac_codec_write(codec, cvt_nid, 0, + AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); +} + +static const struct hdmi_chmap_ops chmap_ops = { + .pin_get_slot_channel = hdmi_pin_get_slot_channel, + .pin_set_slot_channel = hdmi_pin_set_slot_channel, + .set_channel_count = hdmi_set_channel_count, +}; + +void snd_hdmi_register_chmap_ops(struct hdac_device *hdac, + struct hdmi_chmap *chmap) +{ + chmap->ops = chmap_ops; + chmap->hdac = hdac; +} +EXPORT_SYMBOL_GPL(snd_hdmi_register_chmap_ops); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 5219484..0f35945 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -105,12 +105,6 @@ struct hdmi_ops { int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid, unsigned char *buf, int *eld_size);
- /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */ - int (*pin_get_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot); - int (*pin_set_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot, int channel); - void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid, int ca, int active_channels, int conn_type);
@@ -596,20 +590,6 @@ static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid) AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out); }
-static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid) -{ - return 1 + snd_hda_codec_read(codec, cvt_nid, 0, - AC_VERB_GET_CVT_CHAN_COUNT, 0); -} - -static void hdmi_set_channel_count(struct hda_codec *codec, - hda_nid_t cvt_nid, int chs) -{ - if (chs != hdmi_get_channel_count(codec, cvt_nid)) - snd_hda_codec_write(codec, cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); -} - /* * ELD proc files */ @@ -778,7 +758,8 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, int channel;
for (i = 0; i < 8; i++) { - channel = spec->ops.pin_get_slot_channel(codec, pin_nid, i); + channel = spec->chmap.ops.pin_get_slot_channel( + &codec->core, pin_nid, i); codec_dbg(codec, "HDMI: ASP channel %d => slot %d\n", channel, i); } @@ -826,7 +807,8 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; int hdmi_slot = slotsetup & 0x0f; int channel = (slotsetup & 0xf0) >> 4; - err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, channel); + err = spec->chmap.ops.pin_set_slot_channel( + &codec->core, pin_nid, hdmi_slot, channel); if (err) { codec_dbg(codec, "HDMI: channel mapping failed\n"); break; @@ -953,8 +935,8 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { int err;
- err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, - assignments[hdmi_slot]); + err = spec->chmap.ops.pin_set_slot_channel(&codec->core, + pin_nid, hdmi_slot, assignments[hdmi_slot]); if (err) return -EINVAL; } @@ -990,22 +972,6 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, hdmi_debug_channel_mapping(codec, pin_nid); }
-static int hdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot, int channel) -{ - return snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, - (channel << 4) | asp_slot); -} - -static int hdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot) -{ - return (snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_CHAN_SLOT, - asp_slot) & 0xf0) >> 4; -} - /* * Audio InfoFrame routines */ @@ -1181,6 +1147,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, bool non_pcm) { struct hdmi_spec *spec = codec->spec; + struct hdmi_chmap *chmap = &spec->chmap; hda_nid_t pin_nid = per_pin->pin_nid; int channels = per_pin->channels; int active_channels; @@ -1207,7 +1174,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, ordered_ca = get_channel_allocation_order(ca); active_channels = channel_allocations[ordered_ca].channels;
- hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels); + chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid, + active_channels);
/* * always configure channel mapping, it may have been changed by the @@ -2815,21 +2783,11 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = {
static const struct hdmi_ops generic_standard_hdmi_ops = { .pin_get_eld = snd_hdmi_get_eld, - .pin_get_slot_channel = hdmi_pin_get_slot_channel, - .pin_set_slot_channel = hdmi_pin_set_slot_channel, .pin_setup_infoframe = hdmi_pin_setup_infoframe, .pin_hbr_setup = hdmi_pin_hbr_setup, .setup_stream = hdmi_setup_stream, };
-static const struct hdmi_chmap_ops chmap_ops = { - .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, - .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, - .get_chmap = hdmi_get_chmap, - .set_chmap = hdmi_set_chmap, - .is_monitor_connected = is_hdmi_monitor_connected, -}; - static void intel_haswell_fixup_connect_list(struct hda_codec *codec, hda_nid_t nid) { @@ -2931,8 +2889,15 @@ static int patch_generic_hdmi(struct hda_codec *codec)
spec->ops = generic_standard_hdmi_ops; mutex_init(&spec->pcm_lock); - spec->chmap.ops = chmap_ops; - spec->chmap.hdac = &codec->core; + snd_hdmi_register_chmap_ops(&codec->core, &spec->chmap); + + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + hdmi_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap; + spec->chmap.ops.get_chmap = hdmi_get_chmap; + spec->chmap.ops.set_chmap = hdmi_set_chmap; + spec->chmap.ops.is_monitor_connected = is_hdmi_monitor_connected; + codec->spec = spec; hdmi_array_init(spec, 4);
@@ -3849,9 +3814,10 @@ static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map) return 0; }
-static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int hdmi_slot, int stream_channel) +static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac, + hda_nid_t pin_nid, int hdmi_slot, int stream_channel) { + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); int verb; int ati_channel_setup = 0;
@@ -3884,9 +3850,10 @@ static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_n return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup); }
-static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot) +static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac, + hda_nid_t pin_nid, int asp_slot) { + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); bool was_odd = false; int ati_asp_slot = asp_slot; int verb; @@ -4056,8 +4023,6 @@ static int patch_atihdmi(struct hda_codec *codec) spec = codec->spec;
spec->ops.pin_get_eld = atihdmi_pin_get_eld; - spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel; - spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel; spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe; spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup; spec->ops.setup_stream = atihdmi_setup_stream; @@ -4073,6 +4038,10 @@ static int patch_atihdmi(struct hda_codec *codec) spec->chmap.ops.set_chmap = hdmi_set_chmap; spec->chmap.ops.is_monitor_connected = is_hdmi_monitor_connected; + spec->chmap.ops.pin_get_slot_channel = + atihdmi_pin_get_slot_channel; + spec->chmap.ops.pin_set_slot_channel = + atihdmi_pin_set_slot_channel; }
/* ATI/AMD converters do not advertise all of their capabilities */
Added more ops to the chmap object, so most of the helpers can be accessed through ops instead instead of directly calling them. With this moving the chmap helpers to core will be easy.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdmi_chmap.h | 20 ++++++-- sound/pci/hda/patch_hdmi.c | 121 ++++++++++++++++++++++++++++----------------- 2 files changed, 94 insertions(+), 47 deletions(-)
diff --git a/include/sound/hdmi_chmap.h b/include/sound/hdmi_chmap.h index 1ce630d..7b824b6 100644 --- a/include/sound/hdmi_chmap.h +++ b/include/sound/hdmi_chmap.h @@ -7,6 +7,8 @@
#include <sound/hdaudio.h>
+#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 + struct hdmi_cea_channel_speaker_allocation { int ca_index; int speakers[8]; @@ -24,12 +26,13 @@ struct hdmi_chmap_ops { */ int (*chmap_cea_alloc_validate_get_type)(struct hdmi_chmap *chmap, struct hdmi_cea_channel_speaker_allocation *cap, int channels); - void (*cea_alloc_to_tlv_chmap) - (struct hdmi_cea_channel_speaker_allocation *cap, + void (*cea_alloc_to_tlv_chmap)(struct hdmi_chmap *hchmap, + struct hdmi_cea_channel_speaker_allocation *cap, unsigned int *chmap, int channels);
/* check that the user-given chmap is supported */ - int (*chmap_validate)(int ca, int channels, unsigned char *chmap); + int (*chmap_validate)(struct hdmi_chmap *hchmap, int ca, + int channels, unsigned char *chmap);
void (*get_chmap)(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap); @@ -44,6 +47,17 @@ struct hdmi_chmap_ops { hda_nid_t pin_nid, int asp_slot, int channel); void (*set_channel_count)(struct hdac_device *codec, hda_nid_t cvt_nid, int chs); + int (*get_active_channels)(int ca); + void (*setup_channel_mapping)(struct hdmi_chmap *chmap, + hda_nid_t pin_nid, bool non_pcm, int ca, + int channels, unsigned char *map, + bool chmap_set); + int (*channel_allocation)(struct hdac_device *hdac, int spk_alloc, + int channels, bool chmap_set, + bool non_pcm, unsigned char *map); + struct hdmi_cea_channel_speaker_allocation *(*get_cap_from_ca)(int ca); + int (*alsa_chmap_to_spk_mask)(unsigned char c); + int (*spk_to_alsa_chmap)(int spk); };
struct hdmi_chmap { diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 0f35945..5e81319 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -696,8 +696,8 @@ static int get_channel_allocation_order(int ca) * * TODO: it could select the wrong CA from multiple candidates. */ -static int hdmi_channel_allocation(struct hda_codec *codec, - struct hdmi_eld *eld, int channels) +static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec, + int spk_alloc, int channels) { int i; int ca = 0; @@ -717,7 +717,7 @@ static int hdmi_channel_allocation(struct hda_codec *codec, * expand ELD's notions to match the ones used by Audio InfoFrame. */ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { - if (eld->info.spk_alloc & (1 << i)) + if (spk_alloc & (1 << i)) spk_mask |= eld_speaker_allocation_bits[i]; }
@@ -742,36 +742,34 @@ static int hdmi_channel_allocation(struct hda_codec *codec, } }
- snd_print_channel_allocation(eld->info.spk_alloc, buf, sizeof(buf)); - codec_dbg(codec, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", + snd_print_channel_allocation(spk_alloc, buf, sizeof(buf)); + dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", ca, channels, buf);
return ca; }
-static void hdmi_debug_channel_mapping(struct hda_codec *codec, +static void hdmi_debug_channel_mapping(struct hdmi_chmap *chmap, hda_nid_t pin_nid) { #ifdef CONFIG_SND_DEBUG_VERBOSE - struct hdmi_spec *spec = codec->spec; int i; int channel;
for (i = 0; i < 8; i++) { - channel = spec->chmap.ops.pin_get_slot_channel( - &codec->core, pin_nid, i); - codec_dbg(codec, "HDMI: ASP channel %d => slot %d\n", + channel = chmap->ops.pin_get_slot_channel( + chmap->hdac, pin_nid, i); + dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n", channel, i); } #endif }
-static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, +static void hdmi_std_setup_channel_mapping(struct hdmi_chmap *chmap, hda_nid_t pin_nid, bool non_pcm, int ca) { - struct hdmi_spec *spec = codec->spec; struct hdmi_cea_channel_speaker_allocation *ch_alloc; int i; int err; @@ -807,10 +805,10 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; int hdmi_slot = slotsetup & 0x0f; int channel = (slotsetup & 0xf0) >> 4; - err = spec->chmap.ops.pin_set_slot_channel( - &codec->core, pin_nid, hdmi_slot, channel); + err = chmap->ops.pin_set_slot_channel(chmap->hdac, + pin_nid, hdmi_slot, channel); if (err) { - codec_dbg(codec, "HDMI: channel mapping failed\n"); + dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n"); break; } } @@ -912,12 +910,11 @@ static int hdmi_manual_channel_allocation(int chs, unsigned char *map) }
/* set up the channel slots for the given ALSA API channel map */ -static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, +static int hdmi_manual_setup_channel_mapping(struct hdmi_chmap *chmap, hda_nid_t pin_nid, int chs, unsigned char *map, int ca) { - struct hdmi_spec *spec = codec->spec; int ordered_ca = get_channel_allocation_order(ca); int alsa_pos, hdmi_slot; int assignments[8] = {[0 ... 7] = 0xf}; @@ -935,7 +932,7 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { int err;
- err = spec->chmap.ops.pin_set_slot_channel(&codec->core, + err = chmap->ops.pin_set_slot_channel(chmap->hdac, pin_nid, hdmi_slot, assignments[hdmi_slot]); if (err) return -EINVAL; @@ -956,20 +953,20 @@ static void hdmi_setup_fake_chmap(unsigned char *map, int ca) } }
-static void hdmi_setup_channel_mapping(struct hda_codec *codec, +static void hdmi_setup_channel_mapping(struct hdmi_chmap *chmap, hda_nid_t pin_nid, bool non_pcm, int ca, int channels, unsigned char *map, bool chmap_set) { if (!non_pcm && chmap_set) { - hdmi_manual_setup_channel_mapping(codec, pin_nid, + hdmi_manual_setup_channel_mapping(chmap, pin_nid, channels, map, ca); } else { - hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca); + hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca); hdmi_setup_fake_chmap(map, ca); }
- hdmi_debug_channel_mapping(codec, pin_nid); + hdmi_debug_channel_mapping(chmap, pin_nid); }
/* @@ -1142,6 +1139,35 @@ static void hdmi_pin_setup_infoframe(struct hda_codec *codec, } }
+static int hdmi_get_active_channels(int ca) +{ + int ordered_ca = get_channel_allocation_order(ca); + + return channel_allocations[ordered_ca].channels; +} + +static struct hdmi_cea_channel_speaker_allocation *hdmi_get_ch_alloc_from_ca(int ca) +{ + return &channel_allocations[get_channel_allocation_order(ca)]; +} + +static int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, + int channels, bool chmap_set, bool non_pcm, unsigned char *map) +{ + int ca; + + if (!non_pcm && chmap_set) + ca = hdmi_manual_channel_allocation(channels, map); + else + ca = hdmi_channel_allocation_spk_alloc_blk(hdac, + spk_alloc, channels); + + if (ca < 0) + ca = 0; + + return ca; +} + static void hdmi_setup_audio_infoframe(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin, bool non_pcm) @@ -1152,7 +1178,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int channels = per_pin->channels; int active_channels; struct hdmi_eld *eld; - int ca, ordered_ca; + int ca;
if (!channels) return; @@ -1164,15 +1190,11 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
eld = &per_pin->sink_eld;
- if (!non_pcm && per_pin->chmap_set) - ca = hdmi_manual_channel_allocation(channels, per_pin->chmap); - else - ca = hdmi_channel_allocation(codec, eld, channels); - if (ca < 0) - ca = 0; + ca = spec->chmap.ops.channel_allocation(&codec->core, + eld->info.spk_alloc, channels, + per_pin->chmap_set, non_pcm, per_pin->chmap);
- ordered_ca = get_channel_allocation_order(ca); - active_channels = channel_allocations[ordered_ca].channels; + active_channels = spec->chmap.ops.get_active_channels(ca);
chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid, active_channels); @@ -1181,9 +1203,9 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, * always configure channel mapping, it may have been changed by the * user in the meantime */ - hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, - channels, per_pin->chmap, - per_pin->chmap_set); + chmap->ops.setup_channel_mapping(&spec->chmap, + pin_nid, non_pcm, ca, channels, + per_pin->chmap, per_pin->chmap_set);
spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels, eld->info.conn_type); @@ -2336,7 +2358,7 @@ static int hdmi_chmap_cea_alloc_validate_get_type(struct hdmi_chmap *chmap, return SNDRV_CTL_TLVT_CHMAP_VAR; }
-static void hdmi_cea_alloc_to_tlv_chmap( +static void hdmi_cea_alloc_to_tlv_chmap(struct hdmi_chmap *hchmap, struct hdmi_cea_channel_speaker_allocation *cap, unsigned int *chmap, int channels) { @@ -2430,7 +2452,8 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -ENOMEM; size -= chs_bytes; count += chs_bytes; - chmap->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs); + 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; @@ -2501,7 +2524,8 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, if (ca < 0) return -EINVAL; if (hchmap->ops.chmap_validate) { - err = hchmap->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap); + err = hchmap->ops.chmap_validate(hchmap, ca, + ARRAY_SIZE(chmap), chmap); if (err) return err; } @@ -2897,6 +2921,12 @@ static int patch_generic_hdmi(struct hda_codec *codec) spec->chmap.ops.get_chmap = hdmi_get_chmap; spec->chmap.ops.set_chmap = hdmi_set_chmap; spec->chmap.ops.is_monitor_connected = is_hdmi_monitor_connected; + spec->chmap.ops.get_active_channels = hdmi_get_active_channels; + spec->chmap.ops.setup_channel_mapping = hdmi_setup_channel_mapping; + spec->chmap.ops.channel_allocation = hdmi_channel_allocation; + spec->chmap.ops.get_cap_from_ca = hdmi_get_ch_alloc_from_ca; + spec->chmap.ops.alsa_chmap_to_spk_mask = to_spk_mask; + spec->chmap.ops.spk_to_alsa_chmap = spk_to_chmap;
codec->spec = spec; hdmi_array_init(spec, 4); @@ -3494,7 +3524,8 @@ static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdmi_chmap *chmap, chmap, cap, channels); }
-static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map) +static int nvhdmi_chmap_validate(struct hdmi_chmap *chmap, + int ca, int chs, unsigned char *map) { if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR)) return -EINVAL; @@ -3767,16 +3798,17 @@ static int atihdmi_paired_swap_fc_lfe(int pos) return pos; }
-static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map) +static int atihdmi_paired_chmap_validate(struct hdmi_chmap *chmap, + int ca, int chs, unsigned char *map) { struct hdmi_cea_channel_speaker_allocation *cap; int i, j;
/* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */
- cap = &channel_allocations[get_channel_allocation_order(ca)]; + cap = chmap->ops.get_cap_from_ca(ca); for (i = 0; i < chs; ++i) { - int mask = to_spk_mask(map[i]); + int mask = chmap->ops.alsa_chmap_to_spk_mask(map[i]); bool ok = false; bool companion_ok = false;
@@ -3792,7 +3824,8 @@ static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map) if (i % 2 == 0 && i + 1 < chs) { /* even channel, check the odd companion */ int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1); - int comp_mask_req = to_spk_mask(map[i+1]); + int comp_mask_req = + chmap->ops.alsa_chmap_to_spk_mask(map[i+1]); int comp_mask_act = cap->speakers[comp_chan_idx];
if (comp_mask_req == comp_mask_act) @@ -3910,7 +3943,7 @@ static int atihdmi_paired_chmap_cea_alloc_validate_get_type( return SNDRV_CTL_TLVT_CHMAP_PAIRED; }
-static void atihdmi_paired_cea_alloc_to_tlv_chmap( +static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdmi_chmap *hchmap, struct hdmi_cea_channel_speaker_allocation *cap, unsigned int *chmap, int channels) { @@ -3929,7 +3962,7 @@ static void atihdmi_paired_cea_alloc_to_tlv_chmap( continue; }
- chmap[count++] = spk_to_chmap(spk); + chmap[count++] = hchmap->ops.spk_to_alsa_chmap(spk); }
WARN_ON(count != channels);
On Wed, 02 Mar 2016 13:03:26 +0100, Subhransu S. Prusty wrote:
Added more ops to the chmap object, so most of the helpers can be accessed through ops instead instead of directly calling them.
Too many instead :)
With this moving the chmap helpers to core will be easy.
Well, although I asked about reduction of exported stuff, I don't think it's worth to put everything in ops. If any of them would be overridden, it's good to have as an ops. If not, it's fine to have exported symbols. These are just a few entries, not over 20.
Takashi
On Wed, Mar 02, 2016 at 05:03:10PM +0100, Takashi Iwai wrote:
On Wed, 02 Mar 2016 13:03:26 +0100, Subhransu S. Prusty wrote:
Added more ops to the chmap object, so most of the helpers can be accessed through ops instead instead of directly calling them.
Too many instead :)
With this moving the chmap helpers to core will be easy.
Well, although I asked about reduction of exported stuff, I don't think it's worth to put everything in ops. If any of them would be overridden, it's good to have as an ops. If not, it's fine to have exported symbols. These are just a few entries, not over 20.
I think the thought was that patch_hdmi and ASoC driver will use defaults whereas the patch_nvidia etc will override with their own stuff..
On Thu, 03 Mar 2016 12:29:23 +0100, Vinod Koul wrote:
On Wed, Mar 02, 2016 at 05:03:10PM +0100, Takashi Iwai wrote:
On Wed, 02 Mar 2016 13:03:26 +0100, Subhransu S. Prusty wrote:
Added more ops to the chmap object, so most of the helpers can be accessed through ops instead instead of directly calling them.
Too many instead :)
With this moving the chmap helpers to core will be easy.
Well, although I asked about reduction of exported stuff, I don't think it's worth to put everything in ops. If any of them would be overridden, it's good to have as an ops. If not, it's fine to have exported symbols. These are just a few entries, not over 20.
I think the thought was that patch_hdmi and ASoC driver will use defaults whereas the patch_nvidia etc will override with their own stuff..
Yes, but are these new ops overridden actually?
Takashi
On Thu, Mar 03, 2016 at 12:39:05PM +0100, Takashi Iwai wrote:
On Thu, 03 Mar 2016 12:29:23 +0100, Vinod Koul wrote:
On Wed, Mar 02, 2016 at 05:03:10PM +0100, Takashi Iwai wrote:
On Wed, 02 Mar 2016 13:03:26 +0100, Subhransu S. Prusty wrote:
Added more ops to the chmap object, so most of the helpers can be accessed through ops instead instead of directly calling them.
Too many instead :)
With this moving the chmap helpers to core will be easy.
Well, although I asked about reduction of exported stuff, I don't think it's worth to put everything in ops. If any of them would be overridden, it's good to have as an ops. If not, it's fine to have exported symbols. These are just a few entries, not over 20.
I think the thought was that patch_hdmi and ASoC driver will use defaults whereas the patch_nvidia etc will override with their own stuff..
Yes, but are these new ops overridden actually?
I think the new ops added with this patch can be removed. They can be kept as exported APIs and not overriden.
basically these ones: + int (*get_active_channels)(int ca); + void (*setup_channel_mapping)(struct hdmi_chmap *chmap, + hda_nid_t pin_nid, bool non_pcm, int ca, + int channels, unsigned char *map, + bool chmap_set); + int (*channel_allocation)(struct hdac_device *hdac, int spk_alloc, + int channels, bool chmap_set, + bool non_pcm, unsigned char *map); + struct hdmi_cea_channel_speaker_allocation *(*get_cap_from_ca)(int ca); + int (*alsa_chmap_to_spk_mask)(unsigned char c); + int (*spk_to_alsa_chmap)(int spk);
Takashi
--
On Thu, 03 Mar 2016 12:54:43 +0100, Subhransu S. Prusty wrote:
On Thu, Mar 03, 2016 at 12:39:05PM +0100, Takashi Iwai wrote:
On Thu, 03 Mar 2016 12:29:23 +0100, Vinod Koul wrote:
On Wed, Mar 02, 2016 at 05:03:10PM +0100, Takashi Iwai wrote:
On Wed, 02 Mar 2016 13:03:26 +0100, Subhransu S. Prusty wrote:
Added more ops to the chmap object, so most of the helpers can be accessed through ops instead instead of directly calling them.
Too many instead :)
With this moving the chmap helpers to core will be easy.
Well, although I asked about reduction of exported stuff, I don't think it's worth to put everything in ops. If any of them would be overridden, it's good to have as an ops. If not, it's fine to have exported symbols. These are just a few entries, not over 20.
I think the thought was that patch_hdmi and ASoC driver will use defaults whereas the patch_nvidia etc will override with their own stuff..
Yes, but are these new ops overridden actually?
I think the new ops added with this patch can be removed. They can be kept as exported APIs and not overriden.
basically these ones:
- int (*get_active_channels)(int ca);
- void (*setup_channel_mapping)(struct hdmi_chmap *chmap,
hda_nid_t pin_nid, bool non_pcm, int ca,
int channels, unsigned char *map,
bool chmap_set);
- int (*channel_allocation)(struct hdac_device *hdac, int spk_alloc,
int channels, bool chmap_set,
bool non_pcm, unsigned char *map);
- struct hdmi_cea_channel_speaker_allocation *(*get_cap_from_ca)(int
ca);
- int (*alsa_chmap_to_spk_mask)(unsigned char c);
- int (*spk_to_alsa_chmap)(int spk);
Right, I meant the comment above particularly to the ops added in this patch 5. The first few ops are reasonable.
Takashi
Chmap helpers, ops, channel map controls are moved to core.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdmi_chmap.h | 4 + sound/hda/hdmi_chmap.c | 742 +++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_eld.c | 27 +- sound/pci/hda/patch_hdmi.c | 693 +----------------------------------------- 4 files changed, 748 insertions(+), 718 deletions(-)
diff --git a/include/sound/hdmi_chmap.h b/include/sound/hdmi_chmap.h index 7b824b6..837c6e5 100644 --- a/include/sound/hdmi_chmap.h +++ b/include/sound/hdmi_chmap.h @@ -5,6 +5,7 @@ #ifndef __SOUND_HDMI_CHMAP_H #define __SOUND_HDMI_CHMAP_H
+#include <sound/pcm.h> #include <sound/hdaudio.h>
#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 @@ -68,4 +69,7 @@ struct hdmi_chmap {
void snd_hdmi_register_chmap_ops(struct hdac_device *hdac, struct hdmi_chmap *chmap); +void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); +int snd_hdmi_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx, + struct hdmi_chmap *chmap); #endif /* __SOUND_HDMI_CHMAP_H */ diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c index 6346e9a..0685dc2 100644 --- a/sound/hda/hdmi_chmap.c +++ b/sound/hda/hdmi_chmap.c @@ -2,8 +2,182 @@ * HDMI Channel map support helpers */
+#include <linux/module.h> +#include <sound/control.h> +#include <sound/tlv.h> #include <sound/hdmi_chmap.h>
+/* + * CEA speaker placement: + * + * FLH FCH FRH + * FLW FL FLC FC FRC FR FRW + * + * LFE + * TC + * + * RL RLC RC RRC RR + * + * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to + * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. + */ +enum cea_speaker_placement { + FL = (1 << 0), /* Front Left */ + FC = (1 << 1), /* Front Center */ + FR = (1 << 2), /* Front Right */ + FLC = (1 << 3), /* Front Left Center */ + FRC = (1 << 4), /* Front Right Center */ + RL = (1 << 5), /* Rear Left */ + RC = (1 << 6), /* Rear Center */ + RR = (1 << 7), /* Rear Right */ + RLC = (1 << 8), /* Rear Left Center */ + RRC = (1 << 9), /* Rear Right Center */ + LFE = (1 << 10), /* Low Frequency Effect */ + FLW = (1 << 11), /* Front Left Wide */ + FRW = (1 << 12), /* Front Right Wide */ + FLH = (1 << 13), /* Front Left High */ + FCH = (1 << 14), /* Front Center High */ + FRH = (1 << 15), /* Front Right High */ + TC = (1 << 16), /* Top Center */ +}; + +static const char * const cea_speaker_allocation_names[] = { + /* 0 */ "FL/FR", + /* 1 */ "LFE", + /* 2 */ "FC", + /* 3 */ "RL/RR", + /* 4 */ "RC", + /* 5 */ "FLC/FRC", + /* 6 */ "RLC/RRC", + /* 7 */ "FLW/FRW", + /* 8 */ "FLH/FRH", + /* 9 */ "TC", + /* 10 */ "FCH", +}; + +/* + * ELD SA bits in the CEA Speaker Allocation data block + */ +static int eld_speaker_allocation_bits[] = { + [0] = FL | FR, + [1] = LFE, + [2] = FC, + [3] = RL | RR, + [4] = RC, + [5] = FLC | FRC, + [6] = RLC | RRC, + /* the following are not defined in ELD yet */ + [7] = FLW | FRW, + [8] = FLH | FRH, + [9] = TC, + [10] = FCH, +}; + +/* + * ALSA sequence is: + * + * surround40 surround41 surround50 surround51 surround71 + * ch0 front left = = = = + * ch1 front right = = = = + * ch2 rear left = = = = + * ch3 rear right = = = = + * ch4 LFE center center center + * ch5 LFE LFE + * ch6 side left + * ch7 side right + * + * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR} + */ +static int hdmi_channel_mapping[0x32][8] = { + /* stereo */ + [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* 2.1 */ + [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* Dolby Surround */ + [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* surround40 */ + [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 }, + /* 4ch */ + [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, + /* surround41 */ + [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 }, + /* surround50 */ + [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, + /* surround51 */ + [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 }, + /* 7.1 */ + [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 }, +}; + +/* + * This is an ordered list! + * + * The preceding ones have better chances to be selected by + * hdmi_channel_allocation(). + */ +static struct hdmi_cea_channel_speaker_allocation channel_allocations[] = { +/* channel: 7 6 5 4 3 2 1 0 */ +{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, + /* 2.1 */ +{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, + /* Dolby Surround */ +{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, + /* surround40 */ +{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, + /* surround41 */ +{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, + /* surround50 */ +{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, + /* surround51 */ +{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, + /* 6.1 */ +{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, + /* surround71 */ +{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, + +{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, +{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, +{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, +{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, +}; + static int hdmi_pin_set_slot_channel(struct hdac_device *codec, hda_nid_t pin_nid, int asp_slot, int channel) { @@ -34,10 +208,551 @@ static void hdmi_set_channel_count(struct hdac_device *codec, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); }
+/* + * Channel mapping routines + */ + +/* + * Compute derived values in channel_allocations[]. + */ +static void init_channel_allocations(void) +{ + int i, j; + struct hdmi_cea_channel_speaker_allocation *p; + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + p = channel_allocations + i; + p->channels = 0; + p->spk_mask = 0; + for (j = 0; j < ARRAY_SIZE(p->speakers); j++) + if (p->speakers[j]) { + p->channels++; + p->spk_mask |= p->speakers[j]; + } + } +} + +static int get_channel_allocation_order(int ca) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channel_allocations[i].ca_index == ca) + break; + } + return i; +} + +void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) +{ + int i, j; + + for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { + if (spk_alloc & (1 << i)) + j += snprintf(buf + j, buflen - j, " %s", + cea_speaker_allocation_names[i]); + } + buf[j] = '\0'; /* necessary when j == 0 */ +} +EXPORT_SYMBOL_GPL(snd_print_channel_allocation); + +/* + * The transformation takes two steps: + * + * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask + * spk_mask => (channel_allocations[]) => ai->CA + * + * TODO: it could select the wrong CA from multiple candidates. +*/ +static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec, + int spk_alloc, int channels) +{ + int i; + int ca = 0; + int spk_mask = 0; + char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; + + /* + * CA defaults to 0 for basic stereo audio + */ + if (channels <= 2) + return 0; + + /* + * expand ELD's speaker allocation mask + * + * ELD tells the speaker mask in a compact(paired) form, + * expand ELD's notions to match the ones used by Audio InfoFrame. + */ + for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { + if (spk_alloc & (1 << i)) + spk_mask |= eld_speaker_allocation_bits[i]; + } + + /* search for the first working match in the CA table */ + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channels == channel_allocations[i].channels && + (spk_mask & channel_allocations[i].spk_mask) == + channel_allocations[i].spk_mask) { + ca = channel_allocations[i].ca_index; + break; + } + } + + if (!ca) { + /* + * if there was no match, select the regular ALSA channel + * allocation with the matching number of channels + */ + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channels == channel_allocations[i].channels) { + ca = channel_allocations[i].ca_index; + break; + } + } + } + + snd_print_channel_allocation(spk_alloc, buf, sizeof(buf)); + dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", + ca, channels, buf); + + return ca; +} + +static void hdmi_debug_channel_mapping(struct hdmi_chmap *chmap, + hda_nid_t pin_nid) +{ +#ifdef CONFIG_SND_DEBUG_VERBOSE + int i; + int channel; + + for (i = 0; i < 8; i++) { + channel = chmap->ops.pin_get_slot_channel( + chmap->hdac, pin_nid, i); + dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n", + channel, i); + } +#endif +} + +static void hdmi_std_setup_channel_mapping(struct hdmi_chmap *chmap, + hda_nid_t pin_nid, + bool non_pcm, + int ca) +{ + struct hdmi_cea_channel_speaker_allocation *ch_alloc; + int i; + int err; + int order; + int non_pcm_mapping[8]; + + order = get_channel_allocation_order(ca); + ch_alloc = &channel_allocations[order]; + + if (hdmi_channel_mapping[ca][1] == 0) { + int hdmi_slot = 0; + /* fill actual channel mappings in ALSA channel (i) order */ + for (i = 0; i < ch_alloc->channels; i++) { + while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8)) + hdmi_slot++; /* skip zero slots */ + + hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++; + } + /* fill the rest of the slots with ALSA channel 0xf */ + for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) + if (!ch_alloc->speakers[7 - hdmi_slot]) + hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot; + } + + if (non_pcm) { + for (i = 0; i < ch_alloc->channels; i++) + non_pcm_mapping[i] = (i << 4) | i; + for (; i < 8; i++) + non_pcm_mapping[i] = (0xf << 4) | i; + } + + for (i = 0; i < 8; i++) { + int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; + int hdmi_slot = slotsetup & 0x0f; + int channel = (slotsetup & 0xf0) >> 4; + + err = chmap->ops.pin_set_slot_channel(chmap->hdac, + pin_nid, hdmi_slot, channel); + if (err) { + dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n"); + break; + } + } +} + +struct channel_map_table { + unsigned char map; /* ALSA API channel map position */ + int spk_mask; /* speaker position bit mask */ +}; + +static struct channel_map_table map_tables[] = { + { SNDRV_CHMAP_FL, FL }, + { SNDRV_CHMAP_FR, FR }, + { SNDRV_CHMAP_RL, RL }, + { SNDRV_CHMAP_RR, RR }, + { SNDRV_CHMAP_LFE, LFE }, + { SNDRV_CHMAP_FC, FC }, + { SNDRV_CHMAP_RLC, RLC }, + { SNDRV_CHMAP_RRC, RRC }, + { SNDRV_CHMAP_RC, RC }, + { SNDRV_CHMAP_FLC, FLC }, + { SNDRV_CHMAP_FRC, FRC }, + { SNDRV_CHMAP_TFL, FLH }, + { SNDRV_CHMAP_TFR, FRH }, + { SNDRV_CHMAP_FLW, FLW }, + { SNDRV_CHMAP_FRW, FRW }, + { SNDRV_CHMAP_TC, TC }, + { SNDRV_CHMAP_TFC, FCH }, + {} /* terminator */ +}; + +/* from ALSA API channel position to speaker bit mask */ +static int to_spk_mask(unsigned char c) +{ + struct channel_map_table *t = map_tables; + + for (; t->map; t++) { + if (t->map == c) + return t->spk_mask; + } + return 0; +} + +/* from ALSA API channel position to CEA slot */ +static int to_cea_slot(int ordered_ca, unsigned char pos) +{ + int mask = to_spk_mask(pos); + int i; + + if (mask) { + for (i = 0; i < 8; i++) { + if (channel_allocations[ordered_ca].speakers[7 - i] == mask) + return i; + } + } + + return -1; +} + +/* from speaker bit mask to ALSA API channel position */ +static int spk_to_chmap(int spk) +{ + struct channel_map_table *t = map_tables; + + for (; t->map; t++) { + if (t->spk_mask == spk) + return t->map; + } + return 0; +} + +/* from CEA slot to ALSA API channel position */ +static int from_cea_slot(int ordered_ca, unsigned char slot) +{ + int mask = channel_allocations[ordered_ca].speakers[7 - slot]; + + return spk_to_chmap(mask); +} + +/* get the CA index corresponding to the given ALSA API channel map */ +static int hdmi_manual_channel_allocation(int chs, unsigned char *map) +{ + int i, spks = 0, spk_mask = 0; + + for (i = 0; i < chs; i++) { + int mask = to_spk_mask(map[i]); + + if (mask) { + spk_mask |= mask; + spks++; + } + } + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if ((chs == channel_allocations[i].channels || + spks == channel_allocations[i].channels) && + (spk_mask & channel_allocations[i].spk_mask) == + channel_allocations[i].spk_mask) + return channel_allocations[i].ca_index; + } + return -1; +} + +/* set up the channel slots for the given ALSA API channel map */ +static int hdmi_manual_setup_channel_mapping(struct hdmi_chmap *chmap, + hda_nid_t pin_nid, + int chs, unsigned char *map, + int ca) +{ + int ordered_ca = get_channel_allocation_order(ca); + int alsa_pos, hdmi_slot; + int assignments[8] = {[0 ... 7] = 0xf}; + + for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) { + + hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]); + + if (hdmi_slot < 0) + continue; /* unassigned channel */ + + assignments[hdmi_slot] = alsa_pos; + } + + for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { + int err; + + err = chmap->ops.pin_set_slot_channel(chmap->hdac, + pin_nid, hdmi_slot, assignments[hdmi_slot]); + if (err) + return -EINVAL; + } + return 0; +} + +/* store ALSA API channel map from the current default map */ +static void hdmi_setup_fake_chmap(unsigned char *map, int ca) +{ + int i; + int ordered_ca = get_channel_allocation_order(ca); + + for (i = 0; i < 8; i++) { + if (i < channel_allocations[ordered_ca].channels) + map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f); + else + map[i] = 0; + } +} + +static void hdmi_setup_channel_mapping(struct hdmi_chmap *chmap, + hda_nid_t pin_nid, bool non_pcm, int ca, + int channels, unsigned char *map, + bool chmap_set) +{ + if (!non_pcm && chmap_set) { + hdmi_manual_setup_channel_mapping(chmap, pin_nid, + channels, map, ca); + } else { + hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca); + hdmi_setup_fake_chmap(map, ca); + } + + hdmi_debug_channel_mapping(chmap, pin_nid); +} + +static int hdmi_get_active_channels(int ca) +{ + int ordered_ca = get_channel_allocation_order(ca); + + return channel_allocations[ordered_ca].channels; +} + +static struct hdmi_cea_channel_speaker_allocation *hdmi_get_ch_alloc_from_ca(int ca) +{ + return &channel_allocations[get_channel_allocation_order(ca)]; +} + +static int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, + int channels, bool chmap_set, bool non_pcm, unsigned char *map) +{ + int ca; + + if (!non_pcm && chmap_set) + ca = hdmi_manual_channel_allocation(channels, map); + else + ca = hdmi_channel_allocation_spk_alloc_blk(hdac, + spk_alloc, channels); + + if (ca < 0) + ca = 0; + + return ca; +} + +/* + * ALSA API channel-map control callbacks + */ +static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdmi_chmap *chmap = info->private_data; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = chmap->channels_max; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SNDRV_CHMAP_LAST; + return 0; +} + +static int hdmi_chmap_cea_alloc_validate_get_type(struct hdmi_chmap *chmap, + struct hdmi_cea_channel_speaker_allocation *cap, int channels) +{ + /* If the speaker allocation matches the channel count, it is OK.*/ + if (cap->channels != channels) + return -1; + + /* all channels are remappable freely */ + return SNDRV_CTL_TLVT_CHMAP_VAR; +} + +static void hdmi_cea_alloc_to_tlv_chmap(struct hdmi_chmap *hchmap, + struct hdmi_cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) +{ + int count = 0; + int c; + + for (c = 7; c >= 0; c--) { + int spk = cap->speakers[c]; + + if (!spk) + continue; + + chmap[count++] = spk_to_chmap(spk); + } + + WARN_ON(count != channels); +} + +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 hdmi_chmap *chmap = info->private_data; + unsigned int __user *dst; + int chs, count = 0; + + if (size < 8) + return -ENOMEM; + if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) + return -EFAULT; + size -= 8; + dst = tlv + 2; + for (chs = 2; chs <= chmap->channels_max; chs++) { + int i; + struct hdmi_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) + continue; + 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; +} + +static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdmi_chmap *chmap = info->private_data; + int pcm_idx = kcontrol->private_value; + unsigned char pcm_chmap[8]; + int i; + + memset(pcm_chmap, 0, sizeof(pcm_chmap)); + chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap); + + for (i = 0; i < sizeof(chmap); i++) + ucontrol->value.integer.value[i] = pcm_chmap[i]; + + return 0; +} + +static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdmi_chmap *hchmap = info->private_data; + int pcm_idx = kcontrol->private_value; + unsigned int ctl_idx; + struct snd_pcm_substream *substream; + unsigned char chmap[8], per_pin_chmap[8]; + int i, err, ca, prepared = 0; + + /* No monitor is connected in dyn_pcm_assign. + * It's invalid to setup the chmap + */ + if (!hchmap->ops.is_monitor_connected(hchmap->hdac, pcm_idx)) + return 0; + + ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + substream = snd_pcm_chmap_substream(info, ctl_idx); + if (!substream || !substream->runtime) + return 0; /* just for avoiding error from alsactl restore */ + switch (substream->runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_SETUP: + break; + case SNDRV_PCM_STATE_PREPARED: + prepared = 1; + break; + default: + return -EBUSY; + } + memset(chmap, 0, sizeof(chmap)); + for (i = 0; i < ARRAY_SIZE(chmap); i++) + chmap[i] = ucontrol->value.integer.value[i]; + + hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap); + if (!memcmp(chmap, per_pin_chmap, sizeof(chmap))) + return 0; + ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); + if (ca < 0) + return -EINVAL; + if (hchmap->ops.chmap_validate) { + err = hchmap->ops.chmap_validate(hchmap, ca, + ARRAY_SIZE(chmap), chmap); + if (err) + return err; + } + + hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared); + + return 0; +} + static const struct hdmi_chmap_ops chmap_ops = { + .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, + .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, .pin_get_slot_channel = hdmi_pin_get_slot_channel, .pin_set_slot_channel = hdmi_pin_set_slot_channel, .set_channel_count = hdmi_set_channel_count, + .get_active_channels = hdmi_get_active_channels, + .setup_channel_mapping = hdmi_setup_channel_mapping, + .channel_allocation = hdmi_channel_allocation, + .get_cap_from_ca = hdmi_get_ch_alloc_from_ca, + .alsa_chmap_to_spk_mask = to_spk_mask, + .spk_to_alsa_chmap = spk_to_chmap, };
void snd_hdmi_register_chmap_ops(struct hdac_device *hdac, @@ -45,5 +760,32 @@ void snd_hdmi_register_chmap_ops(struct hdac_device *hdac, { chmap->ops = chmap_ops; chmap->hdac = hdac; + init_channel_allocations(); } EXPORT_SYMBOL_GPL(snd_hdmi_register_chmap_ops); + +int snd_hdmi_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx, + struct hdmi_chmap *hchmap) +{ + struct snd_pcm_chmap *chmap; + struct snd_kcontrol *kctl; + int err, i; + + err = snd_pcm_add_chmap_ctls(pcm, + SNDRV_PCM_STREAM_PLAYBACK, + NULL, 0, pcm_idx, &chmap); + if (err < 0) + return err; + /* override handlers */ + chmap->private_data = hchmap; + kctl = chmap->kctl; + for (i = 0; i < kctl->count; i++) + kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; + kctl->info = hdmi_chmap_ctl_info; + kctl->get = hdmi_chmap_ctl_get; + kctl->put = hdmi_chmap_ctl_put; + kctl->tlv.c = hdmi_chmap_ctl_tlv; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdmi_add_chmap_ctls); diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index bc2e082..864d6fe 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -26,6 +26,7 @@ #include <linux/slab.h> #include <sound/core.h> #include <asm/unaligned.h> +#include <sound/hdmi_chmap.h> #include "hda_codec.h" #include "hda_local.h"
@@ -42,20 +43,6 @@ enum cea_edid_versions { CEA_EDID_VER_RESERVED = 4, };
-static const char * const cea_speaker_allocation_names[] = { - /* 0 */ "FL/FR", - /* 1 */ "LFE", - /* 2 */ "FC", - /* 3 */ "RL/RR", - /* 4 */ "RC", - /* 5 */ "FLC/FRC", - /* 6 */ "RLC/RRC", - /* 7 */ "FLW/FRW", - /* 8 */ "FLH/FRH", - /* 9 */ "TC", - /* 10 */ "FCH", -}; - static const char * const eld_connection_type_names[4] = { "HDMI", "DisplayPort", @@ -419,18 +406,6 @@ static void hdmi_show_short_audio_desc(struct hda_codec *codec, a->channels, buf, buf2); }
-void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) -{ - int i, j; - - for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { - if (spk_alloc & (1 << i)) - j += snprintf(buf + j, buflen - j, " %s", - cea_speaker_allocation_names[i]); - } - buf[j] = '\0'; /* necessary when j == 0 */ -} - void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e) { int i; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 5e81319..6841a66 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -197,162 +197,6 @@ union audio_infoframe { u8 bytes[0]; };
-/* - * CEA speaker placement: - * - * FLH FCH FRH - * FLW FL FLC FC FRC FR FRW - * - * LFE - * TC - * - * RL RLC RC RRC RR - * - * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to - * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. - */ -enum cea_speaker_placement { - FL = (1 << 0), /* Front Left */ - FC = (1 << 1), /* Front Center */ - FR = (1 << 2), /* Front Right */ - FLC = (1 << 3), /* Front Left Center */ - FRC = (1 << 4), /* Front Right Center */ - RL = (1 << 5), /* Rear Left */ - RC = (1 << 6), /* Rear Center */ - RR = (1 << 7), /* Rear Right */ - RLC = (1 << 8), /* Rear Left Center */ - RRC = (1 << 9), /* Rear Right Center */ - LFE = (1 << 10), /* Low Frequency Effect */ - FLW = (1 << 11), /* Front Left Wide */ - FRW = (1 << 12), /* Front Right Wide */ - FLH = (1 << 13), /* Front Left High */ - FCH = (1 << 14), /* Front Center High */ - FRH = (1 << 15), /* Front Right High */ - TC = (1 << 16), /* Top Center */ -}; - -/* - * ELD SA bits in the CEA Speaker Allocation data block - */ -static int eld_speaker_allocation_bits[] = { - [0] = FL | FR, - [1] = LFE, - [2] = FC, - [3] = RL | RR, - [4] = RC, - [5] = FLC | FRC, - [6] = RLC | RRC, - /* the following are not defined in ELD yet */ - [7] = FLW | FRW, - [8] = FLH | FRH, - [9] = TC, - [10] = FCH, -}; - -/* - * ALSA sequence is: - * - * surround40 surround41 surround50 surround51 surround71 - * ch0 front left = = = = - * ch1 front right = = = = - * ch2 rear left = = = = - * ch3 rear right = = = = - * ch4 LFE center center center - * ch5 LFE LFE - * ch6 side left - * ch7 side right - * - * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR} - */ -static int hdmi_channel_mapping[0x32][8] = { - /* stereo */ - [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* 2.1 */ - [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* Dolby Surround */ - [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* surround40 */ - [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 }, - /* 4ch */ - [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, - /* surround41 */ - [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 }, - /* surround50 */ - [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, - /* surround51 */ - [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 }, - /* 7.1 */ - [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 }, -}; - -/* - * This is an ordered list! - * - * The preceding ones have better chances to be selected by - * hdmi_channel_allocation(). - */ -static struct hdmi_cea_channel_speaker_allocation channel_allocations[] = { -/* channel: 7 6 5 4 3 2 1 0 */ -{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, - /* 2.1 */ -{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, - /* Dolby Surround */ -{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, - /* surround40 */ -{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, - /* surround41 */ -{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, - /* surround50 */ -{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, - /* surround51 */ -{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, - /* 6.1 */ -{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, - /* surround71 */ -{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, - -{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, -{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, -{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, -{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, -{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, -{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, -{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, -{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, -{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, -{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, -{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, -{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, -{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, -{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, -};
/* @@ -654,322 +498,6 @@ static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin) #endif
/* - * Channel mapping routines - */ - -/* - * Compute derived values in channel_allocations[]. - */ -static void init_channel_allocations(void) -{ - int i, j; - struct hdmi_cea_channel_speaker_allocation *p; - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - p = channel_allocations + i; - p->channels = 0; - p->spk_mask = 0; - for (j = 0; j < ARRAY_SIZE(p->speakers); j++) - if (p->speakers[j]) { - p->channels++; - p->spk_mask |= p->speakers[j]; - } - } -} - -static int get_channel_allocation_order(int ca) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channel_allocations[i].ca_index == ca) - break; - } - return i; -} - -/* - * The transformation takes two steps: - * - * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask - * spk_mask => (channel_allocations[]) => ai->CA - * - * TODO: it could select the wrong CA from multiple candidates. -*/ -static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec, - int spk_alloc, int channels) -{ - int i; - int ca = 0; - int spk_mask = 0; - char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; - - /* - * CA defaults to 0 for basic stereo audio - */ - if (channels <= 2) - return 0; - - /* - * expand ELD's speaker allocation mask - * - * ELD tells the speaker mask in a compact(paired) form, - * expand ELD's notions to match the ones used by Audio InfoFrame. - */ - for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { - if (spk_alloc & (1 << i)) - spk_mask |= eld_speaker_allocation_bits[i]; - } - - /* search for the first working match in the CA table */ - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channels == channel_allocations[i].channels && - (spk_mask & channel_allocations[i].spk_mask) == - channel_allocations[i].spk_mask) { - ca = channel_allocations[i].ca_index; - break; - } - } - - if (!ca) { - /* if there was no match, select the regular ALSA channel - * allocation with the matching number of channels */ - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channels == channel_allocations[i].channels) { - ca = channel_allocations[i].ca_index; - break; - } - } - } - - snd_print_channel_allocation(spk_alloc, buf, sizeof(buf)); - dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", - ca, channels, buf); - - return ca; -} - -static void hdmi_debug_channel_mapping(struct hdmi_chmap *chmap, - hda_nid_t pin_nid) -{ -#ifdef CONFIG_SND_DEBUG_VERBOSE - int i; - int channel; - - for (i = 0; i < 8; i++) { - channel = chmap->ops.pin_get_slot_channel( - chmap->hdac, pin_nid, i); - dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n", - channel, i); - } -#endif -} - -static void hdmi_std_setup_channel_mapping(struct hdmi_chmap *chmap, - hda_nid_t pin_nid, - bool non_pcm, - int ca) -{ - struct hdmi_cea_channel_speaker_allocation *ch_alloc; - int i; - int err; - int order; - int non_pcm_mapping[8]; - - order = get_channel_allocation_order(ca); - ch_alloc = &channel_allocations[order]; - - if (hdmi_channel_mapping[ca][1] == 0) { - int hdmi_slot = 0; - /* fill actual channel mappings in ALSA channel (i) order */ - for (i = 0; i < ch_alloc->channels; i++) { - while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8)) - hdmi_slot++; /* skip zero slots */ - - hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++; - } - /* fill the rest of the slots with ALSA channel 0xf */ - for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) - if (!ch_alloc->speakers[7 - hdmi_slot]) - hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot; - } - - if (non_pcm) { - for (i = 0; i < ch_alloc->channels; i++) - non_pcm_mapping[i] = (i << 4) | i; - for (; i < 8; i++) - non_pcm_mapping[i] = (0xf << 4) | i; - } - - for (i = 0; i < 8; i++) { - int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; - int hdmi_slot = slotsetup & 0x0f; - int channel = (slotsetup & 0xf0) >> 4; - err = chmap->ops.pin_set_slot_channel(chmap->hdac, - pin_nid, hdmi_slot, channel); - if (err) { - dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n"); - break; - } - } -} - -struct channel_map_table { - unsigned char map; /* ALSA API channel map position */ - int spk_mask; /* speaker position bit mask */ -}; - -static struct channel_map_table map_tables[] = { - { SNDRV_CHMAP_FL, FL }, - { SNDRV_CHMAP_FR, FR }, - { SNDRV_CHMAP_RL, RL }, - { SNDRV_CHMAP_RR, RR }, - { SNDRV_CHMAP_LFE, LFE }, - { SNDRV_CHMAP_FC, FC }, - { SNDRV_CHMAP_RLC, RLC }, - { SNDRV_CHMAP_RRC, RRC }, - { SNDRV_CHMAP_RC, RC }, - { SNDRV_CHMAP_FLC, FLC }, - { SNDRV_CHMAP_FRC, FRC }, - { SNDRV_CHMAP_TFL, FLH }, - { SNDRV_CHMAP_TFR, FRH }, - { SNDRV_CHMAP_FLW, FLW }, - { SNDRV_CHMAP_FRW, FRW }, - { SNDRV_CHMAP_TC, TC }, - { SNDRV_CHMAP_TFC, FCH }, - {} /* terminator */ -}; - -/* from ALSA API channel position to speaker bit mask */ -static int to_spk_mask(unsigned char c) -{ - struct channel_map_table *t = map_tables; - for (; t->map; t++) { - if (t->map == c) - return t->spk_mask; - } - return 0; -} - -/* from ALSA API channel position to CEA slot */ -static int to_cea_slot(int ordered_ca, unsigned char pos) -{ - int mask = to_spk_mask(pos); - int i; - - if (mask) { - for (i = 0; i < 8; i++) { - if (channel_allocations[ordered_ca].speakers[7 - i] == mask) - return i; - } - } - - return -1; -} - -/* from speaker bit mask to ALSA API channel position */ -static int spk_to_chmap(int spk) -{ - struct channel_map_table *t = map_tables; - for (; t->map; t++) { - if (t->spk_mask == spk) - return t->map; - } - return 0; -} - -/* from CEA slot to ALSA API channel position */ -static int from_cea_slot(int ordered_ca, unsigned char slot) -{ - int mask = channel_allocations[ordered_ca].speakers[7 - slot]; - - return spk_to_chmap(mask); -} - -/* get the CA index corresponding to the given ALSA API channel map */ -static int hdmi_manual_channel_allocation(int chs, unsigned char *map) -{ - int i, spks = 0, spk_mask = 0; - - for (i = 0; i < chs; i++) { - int mask = to_spk_mask(map[i]); - if (mask) { - spk_mask |= mask; - spks++; - } - } - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if ((chs == channel_allocations[i].channels || - spks == channel_allocations[i].channels) && - (spk_mask & channel_allocations[i].spk_mask) == - channel_allocations[i].spk_mask) - return channel_allocations[i].ca_index; - } - return -1; -} - -/* set up the channel slots for the given ALSA API channel map */ -static int hdmi_manual_setup_channel_mapping(struct hdmi_chmap *chmap, - hda_nid_t pin_nid, - int chs, unsigned char *map, - int ca) -{ - int ordered_ca = get_channel_allocation_order(ca); - int alsa_pos, hdmi_slot; - int assignments[8] = {[0 ... 7] = 0xf}; - - for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) { - - hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]); - - if (hdmi_slot < 0) - continue; /* unassigned channel */ - - assignments[hdmi_slot] = alsa_pos; - } - - for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { - int err; - - err = chmap->ops.pin_set_slot_channel(chmap->hdac, - pin_nid, hdmi_slot, assignments[hdmi_slot]); - if (err) - return -EINVAL; - } - return 0; -} - -/* store ALSA API channel map from the current default map */ -static void hdmi_setup_fake_chmap(unsigned char *map, int ca) -{ - int i; - int ordered_ca = get_channel_allocation_order(ca); - for (i = 0; i < 8; i++) { - if (i < channel_allocations[ordered_ca].channels) - map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f); - else - map[i] = 0; - } -} - -static void hdmi_setup_channel_mapping(struct hdmi_chmap *chmap, - hda_nid_t pin_nid, bool non_pcm, int ca, - int channels, unsigned char *map, - bool chmap_set) -{ - if (!non_pcm && chmap_set) { - hdmi_manual_setup_channel_mapping(chmap, pin_nid, - channels, map, ca); - } else { - hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca); - hdmi_setup_fake_chmap(map, ca); - } - - hdmi_debug_channel_mapping(chmap, pin_nid); -} - -/* * Audio InfoFrame routines */
@@ -1139,35 +667,6 @@ static void hdmi_pin_setup_infoframe(struct hda_codec *codec, } }
-static int hdmi_get_active_channels(int ca) -{ - int ordered_ca = get_channel_allocation_order(ca); - - return channel_allocations[ordered_ca].channels; -} - -static struct hdmi_cea_channel_speaker_allocation *hdmi_get_ch_alloc_from_ca(int ca) -{ - return &channel_allocations[get_channel_allocation_order(ca)]; -} - -static int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, - int channels, bool chmap_set, bool non_pcm, unsigned char *map) -{ - int ca; - - if (!non_pcm && chmap_set) - ca = hdmi_manual_channel_allocation(channels, map); - else - ca = hdmi_channel_allocation_spk_alloc_blk(hdac, - spk_alloc, channels); - - if (ca < 0) - ca = 0; - - return ca; -} - static void hdmi_setup_audio_infoframe(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin, bool non_pcm) @@ -2331,51 +1830,6 @@ static const struct hda_pcm_ops generic_ops = { .cleanup = generic_hdmi_playback_pcm_cleanup, };
-/* - * ALSA API channel-map control callbacks - */ -static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hdmi_chmap *chmap = info->private_data; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = chmap->channels_max; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = SNDRV_CHMAP_LAST; - return 0; -} - -static int hdmi_chmap_cea_alloc_validate_get_type(struct hdmi_chmap *chmap, - struct hdmi_cea_channel_speaker_allocation *cap, int channels) -{ - /* If the speaker allocation matches the channel count, it is OK.*/ - if (cap->channels != channels) - return -1; - - /* all channels are remappable freely */ - return SNDRV_CTL_TLVT_CHMAP_VAR; -} - -static void hdmi_cea_alloc_to_tlv_chmap(struct hdmi_chmap *hchmap, - struct hdmi_cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels) -{ - int count = 0; - int c; - - for (c = 7; c >= 0; c--) { - int spk = cap->speakers[c]; - if (!spk) - continue; - - chmap[count++] = spk_to_chmap(spk); - } - - WARN_ON(count != channels); -} - static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap) { @@ -2414,127 +1868,6 @@ static bool is_hdmi_monitor_connected(struct hdac_device *hdac, int pcm_idx) return per_pin ? true:false; }
-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 hdmi_chmap *chmap = info->private_data; - unsigned int __user *dst; - int chs, count = 0; - - if (size < 8) - return -ENOMEM; - if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) - return -EFAULT; - size -= 8; - dst = tlv + 2; - for (chs = 2; chs <= chmap->channels_max; chs++) { - int i; - struct hdmi_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) - continue; - 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; -} - -static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hdmi_chmap *chmap = info->private_data; - int pcm_idx = kcontrol->private_value; - unsigned char pcm_chmap[8]; - int i; - - memset(pcm_chmap, 0, sizeof(pcm_chmap)); - chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap); - - for (i = 0; i < sizeof(chmap); i++) - ucontrol->value.integer.value[i] = pcm_chmap[i]; - - return 0; -} - -static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hdmi_chmap *hchmap = info->private_data; - int pcm_idx = kcontrol->private_value; - unsigned int ctl_idx; - struct snd_pcm_substream *substream; - unsigned char chmap[8], per_pin_chmap[8]; - int i, err, ca, prepared = 0; - - /* No monitor is connected in dyn_pcm_assign. - * It's invalid to setup the chmap - */ - if (!hchmap->ops.is_monitor_connected(hchmap->hdac, pcm_idx)) - return 0; - - ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - substream = snd_pcm_chmap_substream(info, ctl_idx); - if (!substream || !substream->runtime) - return 0; /* just for avoiding error from alsactl restore */ - switch (substream->runtime->status->state) { - case SNDRV_PCM_STATE_OPEN: - case SNDRV_PCM_STATE_SETUP: - break; - case SNDRV_PCM_STATE_PREPARED: - prepared = 1; - break; - default: - return -EBUSY; - } - memset(chmap, 0, sizeof(chmap)); - for (i = 0; i < ARRAY_SIZE(chmap); i++) - chmap[i] = ucontrol->value.integer.value[i]; - - hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap); - if (!memcmp(chmap, per_pin_chmap, sizeof(chmap))) - return 0; - ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); - if (ca < 0) - return -EINVAL; - if (hchmap->ops.chmap_validate) { - err = hchmap->ops.chmap_validate(hchmap, ca, - ARRAY_SIZE(chmap), chmap); - if (err) - return err; - } - - hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared); - - return 0; -} - static int generic_hdmi_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; @@ -2675,27 +2008,13 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) /* add channel maps */ for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { struct hda_pcm *pcm; - struct snd_pcm_chmap *chmap; - struct snd_kcontrol *kctl; - int i;
pcm = get_pcm_rec(spec, pcm_idx); if (!pcm || !pcm->pcm) break; - err = snd_pcm_add_chmap_ctls(pcm->pcm, - SNDRV_PCM_STREAM_PLAYBACK, - NULL, 0, pcm_idx, &chmap); + err = snd_hdmi_add_chmap_ctls(pcm->pcm, pcm_idx, &spec->chmap); if (err < 0) return err; - /* override handlers */ - chmap->private_data = &spec->chmap; - kctl = chmap->kctl; - for (i = 0; i < kctl->count; i++) - kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; - kctl->info = hdmi_chmap_ctl_info; - kctl->get = hdmi_chmap_ctl_get; - kctl->put = hdmi_chmap_ctl_put; - kctl->tlv.c = hdmi_chmap_ctl_tlv; }
return 0; @@ -2915,18 +2234,9 @@ static int patch_generic_hdmi(struct hda_codec *codec) mutex_init(&spec->pcm_lock); snd_hdmi_register_chmap_ops(&codec->core, &spec->chmap);
- spec->chmap.ops.chmap_cea_alloc_validate_get_type = - hdmi_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap; spec->chmap.ops.get_chmap = hdmi_get_chmap; spec->chmap.ops.set_chmap = hdmi_set_chmap; spec->chmap.ops.is_monitor_connected = is_hdmi_monitor_connected; - spec->chmap.ops.get_active_channels = hdmi_get_active_channels; - spec->chmap.ops.setup_channel_mapping = hdmi_setup_channel_mapping; - spec->chmap.ops.channel_allocation = hdmi_channel_allocation; - spec->chmap.ops.get_cap_from_ca = hdmi_get_ch_alloc_from_ca; - spec->chmap.ops.alsa_chmap_to_spk_mask = to_spk_mask; - spec->chmap.ops.spk_to_alsa_chmap = spk_to_chmap;
codec->spec = spec; hdmi_array_init(spec, 4); @@ -2978,7 +2288,6 @@ static int patch_generic_hdmi(struct hda_codec *codec)
generic_hdmi_init_per_pins(codec);
- init_channel_allocations();
WARN_ON(spec->dyn_pcm_assign && !codec_has_acomp(codec)); return 0;
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdmi_chmap.h | 2 +- sound/hda/hdmi_chmap.c | 6 +++--- sound/pci/hda/hda_eld.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/include/sound/hdmi_chmap.h b/include/sound/hdmi_chmap.h index 837c6e5..a58c204 100644 --- a/include/sound/hdmi_chmap.h +++ b/include/sound/hdmi_chmap.h @@ -69,7 +69,7 @@ struct hdmi_chmap {
void snd_hdmi_register_chmap_ops(struct hdac_device *hdac, struct hdmi_chmap *chmap); -void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); +void snd_hdmi_print_channel_allocation(int spk_alloc, char *buf, int buflen); int snd_hdmi_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx, struct hdmi_chmap *chmap); #endif /* __SOUND_HDMI_CHMAP_H */ diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c index 0685dc2..72d6989 100644 --- a/sound/hda/hdmi_chmap.c +++ b/sound/hda/hdmi_chmap.c @@ -243,7 +243,7 @@ static int get_channel_allocation_order(int ca) return i; }
-void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) +void snd_hdmi_print_channel_allocation(int spk_alloc, char *buf, int buflen) { int i, j;
@@ -254,7 +254,7 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) } buf[j] = '\0'; /* necessary when j == 0 */ } -EXPORT_SYMBOL_GPL(snd_print_channel_allocation); +EXPORT_SYMBOL_GPL(snd_hdmi_print_channel_allocation);
/* * The transformation takes two steps: @@ -312,7 +312,7 @@ static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec, } }
- snd_print_channel_allocation(spk_alloc, buf, sizeof(buf)); + snd_hdmi_print_channel_allocation(spk_alloc, buf, sizeof(buf)); dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", ca, channels, buf);
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 864d6fe..e05b388 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -416,7 +416,7 @@ void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e)
if (e->spk_alloc) { char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; - snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); + snd_hdmi_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); codec_dbg(codec, "HDMI: available speakers:%s\n", buf); }
@@ -491,7 +491,7 @@ void snd_hdmi_print_eld_info(struct hdmi_eld *eld, snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai); snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay);
- snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); + snd_hdmi_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf);
snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count);
participants (3)
-
Subhransu S. Prusty
-
Takashi Iwai
-
Vinod Koul