[alsa-devel] [PATCH 0/3] 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.
Subhransu S. Prusty (3): ALSA: hda - Create common chmap object ALSA: hda - Move chmap support helpers/structures to core ALSA: hda - Use the helpers from HDA core
include/sound/hdaudio.h | 1 + include/sound/hdmi_chmap.h | 84 +++++ sound/hda/Makefile | 2 +- sound/hda/hdmi_chmap.c | 763 +++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_eld.c | 31 +- sound/pci/hda/patch_hdmi.c | 794 ++++----------------------------------------- 6 files changed, 923 insertions(+), 752 deletions(-) create mode 100644 include/sound/hdmi_chmap.h create mode 100644 sound/hda/hdmi_chmap.c
Common chmap object represents multichannel capablity and contains chmap ops. Legacy driver is udpated to use this and later ASoC hdac hdmi driver will also use.
In the mixer control callback handlers, information specific to driver are accessed through ops. Also during chmap control registration, use the common hdmi_chmap object instead of hda_codec.
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 | 39 ++++++++++++ sound/pci/hda/patch_hdmi.c | 145 ++++++++++++++++++++++++++++----------------- 2 files changed, 131 insertions(+), 53 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..e5ea02a --- /dev/null +++ b/include/sound/hdmi_chmap.h @@ -0,0 +1,39 @@ +/* + * For multichannel support + */ + +#ifndef __SOUND_HDMI_CHMAP_H +#define __SOUND_HDMI_CHMAP_H + +#include <sound/hdaudio.h> + +struct cea_channel_speaker_allocation; + +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 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); + + 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 { + 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 f4443b5..d73698c 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -37,8 +37,10 @@ #include <sound/jack.h> #include <sound/asoundef.h> #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" @@ -101,7 +103,6 @@ struct hdmi_spec_per_pin { };
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, @@ -122,15 +123,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 @@ -2096,8 +2089,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, @@ -2321,10 +2314,10 @@ 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 = info->private_data; + 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; @@ -2358,12 +2351,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 = info->private_data; unsigned int __user *dst; int chs, count = 0;
@@ -2373,13 +2403,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( + cap, chs); unsigned int tlv_chmap[8];
if (type < 0) @@ -2396,7 +2427,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; @@ -2411,20 +2442,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 *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 char chmap[8]; int i;
- if (!per_pin) { - for (i = 0; i < spec->channels_max; i++) - ucontrol->value.integer.value[i] = 0; - return 0; - } + memset(chmap, 0, sizeof(chmap)); + hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, chmap); + + for (i = 0; i < sizeof(chmap); i++) + ucontrol->value.integer.value[i] = chmap[i];
- for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++) - ucontrol->value.integer.value[i] = per_pin->chmap[i]; return 0; }
@@ -2432,19 +2460,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 = 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); @@ -2464,22 +2490,20 @@ 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) 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; } - 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; } @@ -2638,7 +2662,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; @@ -2762,11 +2786,16 @@ 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, + .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) { @@ -2868,6 +2897,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);
@@ -3483,9 +3514,12 @@ 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; + 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; } @@ -3992,10 +4026,15 @@ 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; + 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 */ @@ -4007,7 +4046,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; }
On Mon, 22 Feb 2016 09:00:38 +0100, Subhransu S. Prusty wrote:
Common chmap object represents multichannel capablity and contains chmap ops. Legacy driver is udpated to use this and later ASoC hdac hdmi driver will also use.
In the mixer control callback handlers, information specific to driver are accessed through ops. Also during chmap control registration, use the common hdmi_chmap object instead of hda_codec.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
To make review easier, could you split this patch into two, one for just moving the hdmi_chmap out of spec, and another is the addition of new ops, get_chmap, set_chmap, etc.
thanks,
Takashi
include/sound/hdmi_chmap.h | 39 ++++++++++++ sound/pci/hda/patch_hdmi.c | 145 ++++++++++++++++++++++++++++----------------- 2 files changed, 131 insertions(+), 53 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..e5ea02a --- /dev/null +++ b/include/sound/hdmi_chmap.h @@ -0,0 +1,39 @@ +/*
- For multichannel support
- */
+#ifndef __SOUND_HDMI_CHMAP_H +#define __SOUND_HDMI_CHMAP_H
+#include <sound/hdaudio.h>
+struct cea_channel_speaker_allocation;
+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 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);
- 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 {
- 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 f4443b5..d73698c 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -37,8 +37,10 @@ #include <sound/jack.h> #include <sound/asoundef.h> #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" @@ -101,7 +103,6 @@ struct hdmi_spec_per_pin { };
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, @@ -122,15 +123,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 @@ -2096,8 +2089,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,
@@ -2321,10 +2314,10 @@ 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 = info->private_data;
- 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;
@@ -2358,12 +2351,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 = info->private_data; unsigned int __user *dst; int chs, count = 0;
@@ -2373,13 +2403,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(
cap, chs); unsigned int tlv_chmap[8]; if (type < 0)
@@ -2396,7 +2427,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;
@@ -2411,20 +2442,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 *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 char chmap[8]; int i;
- if (!per_pin) {
for (i = 0; i < spec->channels_max; i++)
ucontrol->value.integer.value[i] = 0;
return 0;
- }
- memset(chmap, 0, sizeof(chmap));
- hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, chmap);
- for (i = 0; i < sizeof(chmap); i++)
ucontrol->value.integer.value[i] = chmap[i];
- for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++)
return 0;ucontrol->value.integer.value[i] = per_pin->chmap[i];
}
@@ -2432,19 +2460,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 = 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);
@@ -2464,22 +2490,20 @@ 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) return -EINVAL;
- if (spec->ops.chmap_validate) {
err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap);
- if (hchmap->ops.chmap_validate) {
if (err) return err; }err = hchmap->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap);
- 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;
} @@ -2638,7 +2662,7 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) if (err < 0) return err; /* override handlers */
chmap->private_data = codec;
kctl = chmap->kctl; for (i = 0; i < kctl->count; i++) kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;chmap->private_data = &spec->chmap;
@@ -2762,11 +2786,16 @@ 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,
- .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) { @@ -2868,6 +2897,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);
@@ -3483,9 +3514,12 @@ 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;
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;
} @@ -3992,10 +4026,15 @@ 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;
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 */
@@ -4007,7 +4046,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;
}
1.9.1
On Mon, Feb 22, 2016 at 10:32:33AM +0100, Takashi Iwai wrote:
On Mon, 22 Feb 2016 09:00:38 +0100, Subhransu S. Prusty wrote:
Common chmap object represents multichannel capablity and contains chmap ops. Legacy driver is udpated to use this and later ASoC hdac hdmi driver will also use.
In the mixer control callback handlers, information specific to driver are accessed through ops. Also during chmap control registration, use the common hdmi_chmap object instead of hda_codec.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com
To make review easier, could you split this patch into two, one for just moving the hdmi_chmap out of spec, and another is the addition of new ops, get_chmap, set_chmap, etc.
Sure, will do it.
thanks,
Takashi
include/sound/hdmi_chmap.h | 39 ++++++++++++ sound/pci/hda/patch_hdmi.c | 145 ++++++++++++++++++++++++++++----------------- 2 files changed, 131 insertions(+), 53 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..e5ea02a --- /dev/null +++ b/include/sound/hdmi_chmap.h @@ -0,0 +1,39 @@ +/*
- For multichannel support
- */
+#ifndef __SOUND_HDMI_CHMAP_H +#define __SOUND_HDMI_CHMAP_H
+#include <sound/hdaudio.h>
+struct cea_channel_speaker_allocation;
+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 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);
- 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 {
- 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 f4443b5..d73698c 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -37,8 +37,10 @@ #include <sound/jack.h> #include <sound/asoundef.h> #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" @@ -101,7 +103,6 @@ struct hdmi_spec_per_pin { };
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, @@ -122,15 +123,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 @@ -2096,8 +2089,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,
@@ -2321,10 +2314,10 @@ 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 = info->private_data;
- 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;
@@ -2358,12 +2351,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 = info->private_data; unsigned int __user *dst; int chs, count = 0;
@@ -2373,13 +2403,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(
cap, chs); unsigned int tlv_chmap[8]; if (type < 0)
@@ -2396,7 +2427,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;
@@ -2411,20 +2442,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 *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 char chmap[8]; int i;
- if (!per_pin) {
for (i = 0; i < spec->channels_max; i++)
ucontrol->value.integer.value[i] = 0;
return 0;
- }
- memset(chmap, 0, sizeof(chmap));
- hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, chmap);
- for (i = 0; i < sizeof(chmap); i++)
ucontrol->value.integer.value[i] = chmap[i];
- for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++)
return 0;ucontrol->value.integer.value[i] = per_pin->chmap[i];
}
@@ -2432,19 +2460,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 = 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);
@@ -2464,22 +2490,20 @@ 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) return -EINVAL;
- if (spec->ops.chmap_validate) {
err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap);
- if (hchmap->ops.chmap_validate) {
if (err) return err; }err = hchmap->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap);
- 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;
} @@ -2638,7 +2662,7 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) if (err < 0) return err; /* override handlers */
chmap->private_data = codec;
kctl = chmap->kctl; for (i = 0; i < kctl->count; i++) kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;chmap->private_data = &spec->chmap;
@@ -2762,11 +2786,16 @@ 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,
- .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) { @@ -2868,6 +2897,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);
@@ -3483,9 +3514,12 @@ 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;
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;
} @@ -3992,10 +4026,15 @@ 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;
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 */
@@ -4007,7 +4046,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;
}
1.9.1
--
Copy the channel map helpers to core. The exported channel map API names are prefixed with snd_hdmi for namespace management. Arguments of the helpers are modified to take hdac_device object argument instead of hda_codec specific object. With this it can be used by other hdac devices.
Next patch removes the local channel map helpers from patch_hdmi.c and uses these common helpers.
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 | 45 +++ sound/hda/Makefile | 2 +- sound/hda/hdmi_chmap.c | 763 +++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/patch_hdmi.c | 10 - 4 files changed, 809 insertions(+), 11 deletions(-) create mode 100644 sound/hda/hdmi_chmap.c
diff --git a/include/sound/hdmi_chmap.h b/include/sound/hdmi_chmap.h index e5ea02a..8e9ddb1 100644 --- a/include/sound/hdmi_chmap.h +++ b/include/sound/hdmi_chmap.h @@ -5,10 +5,22 @@ #ifndef __SOUND_HDMI_CHMAP_H #define __SOUND_HDMI_CHMAP_H
+#include <sound/pcm.h> #include <sound/hdaudio.h>
struct cea_channel_speaker_allocation;
+#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 + +struct cea_channel_speaker_allocation { + int ca_index; + int speakers[8]; + + /* derived values, just for convenience */ + int channels; + int spk_mask; +}; + struct hdmi_chmap_ops { /* * Helpers for producing the channel map TLVs. These can be overridden @@ -28,6 +40,11 @@ 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); };
struct hdmi_chmap { @@ -36,4 +53,32 @@ struct hdmi_chmap { struct hdac_device *hdac; };
+void snd_hdmi_init_channel_allocations(void); +int snd_hdmi_get_channel_allocation_order(int ca); +int snd_hdmi_get_active_channels(int ca); +struct cea_channel_speaker_allocation *snd_hdmi_get_ch_alloc_from_ca(int ca); +void snd_hdmi_print_channel_allocation(int spk_alloc, char *buf, int buflen); +int snd_hdmi_channel_allocation(struct hdac_device *codec, + int spk_alloc, int channels); +int snd_hdmi_to_spk_mask(unsigned char c); +int snd_hdmi_spk_to_chmap(int spk); +int snd_hdmi_manual_channel_allocation(int chs, unsigned char *map); +void snd_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); +int snd_hdmi_pin_set_slot_channel(struct hdac_device *codec, hda_nid_t pin_nid, + int asp_slot, int channel); +int snd_hdmi_pin_get_slot_channel(struct hdac_device *codec, hda_nid_t pin_nid, + int asp_slot); +void snd_hdmi_set_channel_count(struct hdac_device *codec, + hda_nid_t cvt_nid, int chs); +int snd_hdmi_chmap_cea_alloc_validate_get_type( + struct cea_channel_speaker_allocation *cap, + int channels); +void snd_hdmi_cea_alloc_to_tlv_chmap( + struct cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels); +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/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..78b421f --- /dev/null +++ b/sound/hda/hdmi_chmap.c @@ -0,0 +1,763 @@ +/* + * HDMI Channel map support helpers + */ + +#include <linux/module.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/tlv.h> + +#include <sound/hdaudio.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 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 } }, +}; + +/* + * Channel mapping routines + */ + +/* + * Compute derived values in channel_allocations[]. + */ +void snd_hdmi_init_channel_allocations(void) +{ + int i, j; + struct 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]; + } + } +} +EXPORT_SYMBOL_GPL(snd_hdmi_init_channel_allocations); + +int snd_hdmi_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; +} +EXPORT_SYMBOL_GPL(snd_hdmi_get_channel_allocation_order); + +int snd_hdmi_get_active_channels(int ca) +{ + return channel_allocations[ca].channels; +} +EXPORT_SYMBOL_GPL(snd_hdmi_get_active_channels); + +struct cea_channel_speaker_allocation *snd_hdmi_get_ch_alloc_from_ca(int ca) +{ + return &channel_allocations[snd_hdmi_get_channel_allocation_order(ca)]; +} +EXPORT_SYMBOL_GPL(snd_hdmi_get_ch_alloc_from_ca); + +void snd_hdmi_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_hdmi_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. +*/ +int snd_hdmi_channel_allocation(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_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); + + return ca; +} +EXPORT_SYMBOL_GPL(snd_hdmi_channel_allocation); + +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 cea_channel_speaker_allocation *ch_alloc; + int i; + int err; + int order; + int non_pcm_mapping[8]; + + order = snd_hdmi_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 */ +int snd_hdmi_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; +} +EXPORT_SYMBOL_GPL(snd_hdmi_to_spk_mask); + +/* from ALSA API channel position to CEA slot */ +static int to_cea_slot(int ordered_ca, unsigned char pos) +{ + int mask = snd_hdmi_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 */ +int snd_hdmi_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; +} +EXPORT_SYMBOL_GPL(snd_hdmi_spk_to_chmap); + +/* 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 snd_hdmi_spk_to_chmap(mask); +} + +/* get the CA index corresponding to the given ALSA API channel map */ +int snd_hdmi_manual_channel_allocation(int chs, unsigned char *map) +{ + int i, spks = 0, spk_mask = 0; + + for (i = 0; i < chs; i++) { + int mask = snd_hdmi_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; +} +EXPORT_SYMBOL_GPL(snd_hdmi_manual_channel_allocation); + +/* 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 = snd_hdmi_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 = snd_hdmi_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; + } +} + +void snd_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); +} +EXPORT_SYMBOL_GPL(snd_hdmi_setup_channel_mapping); + +int snd_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); +} +EXPORT_SYMBOL_GPL(snd_hdmi_pin_set_slot_channel); + +int snd_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; +} +EXPORT_SYMBOL_GPL(snd_hdmi_pin_get_slot_channel); + +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); +} + +void snd_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); +} +EXPORT_SYMBOL_GPL(snd_hdmi_set_channel_count); + +/* + * 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; +} + +int snd_hdmi_chmap_cea_alloc_validate_get_type( + struct 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; +} +EXPORT_SYMBOL_GPL(snd_hdmi_chmap_cea_alloc_validate_get_type); + +void snd_hdmi_cea_alloc_to_tlv_chmap(struct 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++] = snd_hdmi_spk_to_chmap(spk); + } + + WARN_ON(count != channels); +} +EXPORT_SYMBOL_GPL(snd_hdmi_cea_alloc_to_tlv_chmap); + +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 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( + 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(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 *hchmap = info->private_data; + int pcm_idx = kcontrol->private_value; + unsigned char chmap[8]; + int i; + + memset(chmap, 0, sizeof(chmap)); + hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, chmap); + + for (i = 0; i < sizeof(chmap); i++) + ucontrol->value.integer.value[i] = 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 = snd_hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); + if (ca < 0) + return -EINVAL; + if (hchmap->ops.chmap_validate) { + err = hchmap->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap); + if (err) + return err; + } + + hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared); + + return 0; +} + +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/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index d73698c..0861209 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -102,7 +102,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, @@ -257,15 +256,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: *
On Mon, 22 Feb 2016 09:00:39 +0100, Subhransu S. Prusty wrote:
Copy the channel map helpers to core. The exported channel map API names are prefixed with snd_hdmi for namespace management. Arguments of the helpers are modified to take hdac_device object argument instead of hda_codec specific object. With this it can be used by other hdac devices.
Next patch removes the local channel map helpers from patch_hdmi.c and uses these common helpers.
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 | 45 +++ sound/hda/Makefile | 2 +- sound/hda/hdmi_chmap.c | 763 +++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/patch_hdmi.c | 10 - 4 files changed, 809 insertions(+), 11 deletions(-) create mode 100644 sound/hda/hdmi_chmap.c
diff --git a/include/sound/hdmi_chmap.h b/include/sound/hdmi_chmap.h index e5ea02a..8e9ddb1 100644 --- a/include/sound/hdmi_chmap.h +++ b/include/sound/hdmi_chmap.h @@ -5,10 +5,22 @@ #ifndef __SOUND_HDMI_CHMAP_H #define __SOUND_HDMI_CHMAP_H
+#include <sound/pcm.h> #include <sound/hdaudio.h>
struct cea_channel_speaker_allocation;
+#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
+struct cea_channel_speaker_allocation {
- int ca_index;
- int speakers[8];
- /* derived values, just for convenience */
- int channels;
- int spk_mask;
+};
struct hdmi_chmap_ops { /* * Helpers for producing the channel map TLVs. These can be overridden @@ -28,6 +40,11 @@ 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);
};
struct hdmi_chmap { @@ -36,4 +53,32 @@ struct hdmi_chmap { struct hdac_device *hdac; };
+void snd_hdmi_init_channel_allocations(void); +int snd_hdmi_get_channel_allocation_order(int ca); +int snd_hdmi_get_active_channels(int ca); +struct cea_channel_speaker_allocation *snd_hdmi_get_ch_alloc_from_ca(int ca); +void snd_hdmi_print_channel_allocation(int spk_alloc, char *buf, int buflen); +int snd_hdmi_channel_allocation(struct hdac_device *codec,
int spk_alloc, int channels);
+int snd_hdmi_to_spk_mask(unsigned char c); +int snd_hdmi_spk_to_chmap(int spk); +int snd_hdmi_manual_channel_allocation(int chs, unsigned char *map); +void snd_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);
+int snd_hdmi_pin_set_slot_channel(struct hdac_device *codec, hda_nid_t pin_nid,
int asp_slot, int channel);
+int snd_hdmi_pin_get_slot_channel(struct hdac_device *codec, hda_nid_t pin_nid,
int asp_slot);
+void snd_hdmi_set_channel_count(struct hdac_device *codec,
hda_nid_t cvt_nid, int chs);
+int snd_hdmi_chmap_cea_alloc_validate_get_type(
struct cea_channel_speaker_allocation *cap,
int channels);
+void snd_hdmi_cea_alloc_to_tlv_chmap(
struct cea_channel_speaker_allocation *cap,
unsigned int *chmap, int channels);
+int snd_hdmi_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,
struct hdmi_chmap *chmap);
Do we really need to export all these? Can't be they somehow self-contained?
Takashi
On Mon, Feb 22, 2016 at 10:44:57AM +0100, Takashi Iwai wrote:
On Mon, 22 Feb 2016 09:00:39 +0100, Subhransu S. Prusty wrote:
+void snd_hdmi_init_channel_allocations(void); +int snd_hdmi_get_channel_allocation_order(int ca); +int snd_hdmi_get_active_channels(int ca); +struct cea_channel_speaker_allocation *snd_hdmi_get_ch_alloc_from_ca(int ca); +void snd_hdmi_print_channel_allocation(int spk_alloc, char *buf, int buflen); +int snd_hdmi_channel_allocation(struct hdac_device *codec,
int spk_alloc, int channels);
+int snd_hdmi_to_spk_mask(unsigned char c); +int snd_hdmi_spk_to_chmap(int spk); +int snd_hdmi_manual_channel_allocation(int chs, unsigned char *map); +void snd_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);
+int snd_hdmi_pin_set_slot_channel(struct hdac_device *codec, hda_nid_t pin_nid,
int asp_slot, int channel);
+int snd_hdmi_pin_get_slot_channel(struct hdac_device *codec, hda_nid_t pin_nid,
int asp_slot);
+void snd_hdmi_set_channel_count(struct hdac_device *codec,
hda_nid_t cvt_nid, int chs);
+int snd_hdmi_chmap_cea_alloc_validate_get_type(
struct cea_channel_speaker_allocation *cap,
int channels);
+void snd_hdmi_cea_alloc_to_tlv_chmap(
struct cea_channel_speaker_allocation *cap,
unsigned int *chmap, int channels);
+int snd_hdmi_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,
struct hdmi_chmap *chmap);
Do we really need to export all these? Can't be they somehow self-contained?
Yes, I think we can do this by using common chmap ops instance and driver can override the specific handlers. Will check for other APIs as well.
Takashi
--
This patch removes the local channel map definitions from legacy hda driver and uses the helpers from core.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/hdaudio.h | 1 + sound/pci/hda/hda_eld.c | 31 +- sound/pci/hda/patch_hdmi.c | 731 ++------------------------------------------- 3 files changed, 29 insertions(+), 734 deletions(-)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index e2b712c..3e1eb13 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -22,6 +22,7 @@ struct hdac_device; struct hdac_driver; struct hdac_widget_tree; struct hda_device_id; +struct hdmi_chmap_ops;
/* * exported bus type diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index bc2e082..e05b388 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; @@ -441,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); }
@@ -516,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); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 0861209..225f1b3 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -107,12 +107,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);
@@ -205,164 +199,6 @@ union audio_infoframe { };
/* - * 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 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 } }, -}; - - -/* * HDMI routines */
@@ -582,19 +418,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 @@ -659,338 +482,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 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(struct hda_codec *codec, - struct hdmi_eld *eld, 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 (eld->info.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(eld->info.spk_alloc, buf, sizeof(buf)); - codec_dbg(codec, "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, - 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->ops.pin_get_slot_channel(codec, pin_nid, i); - codec_dbg(codec, "HDMI: ASP channel %d => slot %d\n", - channel, i); - } -#endif -} - -static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, - hda_nid_t pin_nid, - bool non_pcm, - int ca) -{ - struct hdmi_spec *spec = codec->spec; - struct 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 = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, channel); - if (err) { - codec_dbg(codec, "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 hda_codec *codec, - 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}; - - 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 = spec->ops.pin_set_slot_channel(codec, 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 hda_codec *codec, - 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, - channels, map, ca); - } else { - hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca); - hdmi_setup_fake_chmap(map, ca); - } - - 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 @@ -1184,22 +675,22 @@ 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); + ca = snd_hdmi_manual_channel_allocation(channels, per_pin->chmap); else - ca = hdmi_channel_allocation(codec, eld, channels); + ca = snd_hdmi_channel_allocation(&codec->core, eld->info.spk_alloc, channels); if (ca < 0) ca = 0;
- ordered_ca = get_channel_allocation_order(ca); - active_channels = channel_allocations[ordered_ca].channels; + ordered_ca = snd_hdmi_get_channel_allocation_order(ca); + active_channels = snd_hdmi_get_active_channels(ordered_ca);
- hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels); + snd_hdmi_set_channel_count(spec->chmap.hdac, per_pin->cvt_nid, active_channels);
/* * 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, + snd_hdmi_setup_channel_mapping(&spec->chmap, pin_nid, non_pcm, ca, channels, per_pin->chmap, per_pin->chmap_set);
@@ -2297,49 +1788,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 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 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) @@ -2379,125 +1827,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 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( - 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(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 *hchmap = info->private_data; - int pcm_idx = kcontrol->private_value; - unsigned char chmap[8]; - int i; - - memset(chmap, 0, sizeof(chmap)); - hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, chmap); - - for (i = 0; i < sizeof(chmap); i++) - ucontrol->value.integer.value[i] = 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(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; @@ -2639,27 +1968,14 @@ 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; @@ -2771,19 +2087,19 @@ 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, + .chmap_cea_alloc_validate_get_type = snd_hdmi_chmap_cea_alloc_validate_get_type, + .cea_alloc_to_tlv_chmap = snd_hdmi_cea_alloc_to_tlv_chmap, .get_chmap = hdmi_get_chmap, .set_chmap = hdmi_set_chmap, .is_monitor_connected = is_hdmi_monitor_connected, + .pin_get_slot_channel = snd_hdmi_pin_get_slot_channel, + .pin_set_slot_channel = snd_hdmi_pin_set_slot_channel, };
static void intel_haswell_fixup_connect_list(struct hda_codec *codec, @@ -2939,7 +2255,7 @@ static int patch_generic_hdmi(struct hda_codec *codec)
generic_hdmi_init_per_pins(codec);
- init_channel_allocations(); + snd_hdmi_init_channel_allocations();
WARN_ON(spec->dyn_pcm_assign && !codec_has_acomp(codec)); return 0; @@ -3481,7 +2797,7 @@ static int nvhdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_a if (cap->ca_index == 0x00 && channels == 2) return SNDRV_CTL_TLVT_CHMAP_FIXED;
- return hdmi_chmap_cea_alloc_validate_get_type(cap, channels); + return snd_hdmi_chmap_cea_alloc_validate_get_type(cap, channels); }
static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map) @@ -3764,9 +3080,9 @@ static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
/* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */
- cap = &channel_allocations[get_channel_allocation_order(ca)]; + cap = snd_hdmi_get_ch_alloc_from_ca(ca); for (i = 0; i < chs; ++i) { - int mask = to_spk_mask(map[i]); + int mask = snd_hdmi_to_spk_mask(map[i]); bool ok = false; bool companion_ok = false;
@@ -3782,7 +3098,7 @@ 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 = snd_hdmi_to_spk_mask(map[i+1]); int comp_mask_act = cap->speakers[comp_chan_idx];
if (comp_mask_req == comp_mask_act) @@ -3804,9 +3120,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, +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;
@@ -3839,9 +3156,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, +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; @@ -3914,7 +3232,7 @@ static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_all continue; }
- chmap[count++] = spk_to_chmap(spk); + chmap[count++] = snd_hdmi_spk_to_chmap(spk); }
WARN_ON(count != channels); @@ -4008,8 +3326,7 @@ 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; @@ -4025,6 +3342,8 @@ 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 */
participants (2)
-
Subhransu S. Prusty
-
Takashi Iwai