ALSA: hda - Create common chmap object

chmap object represents multichannel capability and contains chmap
ops. Legacy driver is updated to use this.

With next set of patches chmap object is moved to common to be
reused by other drivers (ex: skylake ASoC hdmi driver).

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Subhransu S. Prusty 2016-03-04 19:59:46 +05:30 committed by Takashi Iwai
parent cdb1ee3f6c
commit 67b90cb84b
2 changed files with 81 additions and 41 deletions

41
include/sound/hda_chmap.h Normal file
View File

@ -0,0 +1,41 @@
/*
* For multichannel support
*/
#ifndef __SOUND_HDA_CHMAP_H
#define __SOUND_HDA_CHMAP_H
#include <sound/hdaudio.h>
struct cea_channel_speaker_allocation {
int ca_index;
int speakers[8];
/* derived values, just for convenience */
int channels;
int spk_mask;
};
struct hdac_chmap;
struct hdac_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 hdac_chmap *chmap,
struct cea_channel_speaker_allocation *cap, int channels);
void (*cea_alloc_to_tlv_chmap)
(struct cea_channel_speaker_allocation *cap,
unsigned int *chmap, int channels);
/* check that the user-given chmap is supported */
int (*chmap_validate)(int ca, int channels, unsigned char *chmap);
};
struct hdac_chmap {
unsigned int channels_max; /* max over all cvts */
struct hdac_chmap_ops ops;
struct hdac_device *hdac;
};
#endif /* __SOUND_HDA_CHMAP_H */

View File

@ -39,6 +39,7 @@
#include <sound/tlv.h> #include <sound/tlv.h>
#include <sound/hdaudio.h> #include <sound/hdaudio.h>
#include <sound/hda_i915.h> #include <sound/hda_i915.h>
#include <sound/hda_chmap.h>
#include "hda_codec.h" #include "hda_codec.h"
#include "hda_local.h" #include "hda_local.h"
#include "hda_jack.h" #include "hda_jack.h"
@ -121,15 +122,6 @@ struct hdmi_ops {
int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid, int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
hda_nid_t pin_nid, u32 stream_tag, int format); 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 { struct hdmi_pcm {
@ -155,7 +147,6 @@ struct hdmi_spec {
* bit 1 means the second playback PCM, and so on. * bit 1 means the second playback PCM, and so on.
*/ */
unsigned long pcm_in_use; unsigned long pcm_in_use;
unsigned int channels_max; /* max over all cvts */
struct hdmi_eld temp_eld; struct hdmi_eld temp_eld;
struct hdmi_ops ops; struct hdmi_ops ops;
@ -171,6 +162,8 @@ struct hdmi_spec {
/* i915/powerwell (Haswell+/Valleyview+) specific */ /* i915/powerwell (Haswell+/Valleyview+) specific */
struct i915_audio_component_audio_ops i915_audio_ops; struct i915_audio_component_audio_ops i915_audio_ops;
bool i915_bound; /* was i915 bound in this driver? */ bool i915_bound; /* was i915 bound in this driver? */
struct hdac_chmap chmap;
}; };
#ifdef CONFIG_SND_HDA_I915 #ifdef CONFIG_SND_HDA_I915
@ -264,15 +257,6 @@ static int eld_speaker_allocation_bits[] = {
[10] = FCH, [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: * ALSA sequence is:
* *
@ -2141,8 +2125,8 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
per_cvt->channels_min = 2; per_cvt->channels_min = 2;
if (chans <= 16) { if (chans <= 16) {
per_cvt->channels_max = chans; per_cvt->channels_max = chans;
if (chans > spec->channels_max) if (chans > spec->chmap.channels_max)
spec->channels_max = chans; spec->chmap.channels_max = chans;
} }
err = snd_hda_query_supported_pcm(codec, cvt_nid, err = snd_hda_query_supported_pcm(codec, cvt_nid,
@ -2368,15 +2352,17 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
struct hda_codec *codec = info->private_data; struct hda_codec *codec = info->private_data;
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
struct hdac_chmap *chmap = &spec->chmap;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 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.min = 0;
uinfo->value.integer.max = SNDRV_CHMAP_LAST; uinfo->value.integer.max = SNDRV_CHMAP_LAST;
return 0; return 0;
} }
static int hdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
int channels) struct cea_channel_speaker_allocation *cap, int channels)
{ {
/* If the speaker allocation matches the channel count, it is OK.*/ /* If the speaker allocation matches the channel count, it is OK.*/
if (cap->channels != channels) if (cap->channels != channels)
@ -2409,6 +2395,7 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
struct hda_codec *codec = info->private_data; struct hda_codec *codec = info->private_data;
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
struct hdac_chmap *chmap = &spec->chmap;
unsigned int __user *dst; unsigned int __user *dst;
int chs, count = 0; int chs, count = 0;
@ -2418,13 +2405,14 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
return -EFAULT; return -EFAULT;
size -= 8; size -= 8;
dst = tlv + 2; dst = tlv + 2;
for (chs = 2; chs <= spec->channels_max; chs++) { for (chs = 2; chs <= chmap->channels_max; chs++) {
int i; int i;
struct cea_channel_speaker_allocation *cap; struct cea_channel_speaker_allocation *cap;
cap = channel_allocations; cap = channel_allocations;
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
int chs_bytes = chs * 4; int chs_bytes = chs * 4;
int type = spec->ops.chmap_cea_alloc_validate_get_type(cap, chs); int type = chmap->ops.chmap_cea_alloc_validate_get_type(
chmap, cap, chs);
unsigned int tlv_chmap[8]; unsigned int tlv_chmap[8];
if (type < 0) if (type < 0)
@ -2441,7 +2429,7 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
return -ENOMEM; return -ENOMEM;
size -= chs_bytes; size -= chs_bytes;
count += 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)) if (copy_to_user(dst, tlv_chmap, chs_bytes))
return -EFAULT; return -EFAULT;
dst += chs; dst += chs;
@ -2458,12 +2446,13 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
struct hda_codec *codec = info->private_data; struct hda_codec *codec = info->private_data;
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
struct hdac_chmap *chmap = &spec->chmap;
int pcm_idx = kcontrol->private_value; int pcm_idx = kcontrol->private_value;
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
int i; int i;
if (!per_pin) { if (!per_pin) {
for (i = 0; i < spec->channels_max; i++) for (i = 0; i < chmap->channels_max; i++)
ucontrol->value.integer.value[i] = 0; ucontrol->value.integer.value[i] = 0;
return 0; return 0;
} }
@ -2479,6 +2468,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
struct hda_codec *codec = info->private_data; struct hda_codec *codec = info->private_data;
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
struct hdac_chmap *hchmap = &spec->chmap;
int pcm_idx = kcontrol->private_value; int pcm_idx = kcontrol->private_value;
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
unsigned int ctl_idx; unsigned int ctl_idx;
@ -2514,8 +2504,8 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
if (ca < 0) if (ca < 0)
return -EINVAL; return -EINVAL;
if (spec->ops.chmap_validate) { if (hchmap->ops.chmap_validate) {
err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap); err = hchmap->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap);
if (err) if (err)
return err; return err;
} }
@ -2806,6 +2796,9 @@ static const struct hdmi_ops generic_standard_hdmi_ops = {
.pin_setup_infoframe = hdmi_pin_setup_infoframe, .pin_setup_infoframe = hdmi_pin_setup_infoframe,
.pin_hbr_setup = hdmi_pin_hbr_setup, .pin_hbr_setup = hdmi_pin_hbr_setup,
.setup_stream = hdmi_setup_stream, .setup_stream = hdmi_setup_stream,
};
static const struct hdac_chmap_ops chmap_ops = {
.chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type,
.cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap,
}; };
@ -2912,6 +2905,8 @@ static int patch_generic_hdmi(struct hda_codec *codec)
spec->ops = generic_standard_hdmi_ops; spec->ops = generic_standard_hdmi_ops;
mutex_init(&spec->pcm_lock); mutex_init(&spec->pcm_lock);
spec->chmap.ops = chmap_ops;
spec->chmap.hdac = &codec->core;
codec->spec = spec; codec->spec = spec;
hdmi_array_init(spec, 4); hdmi_array_init(spec, 4);
@ -3503,13 +3498,14 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
* - 0x10de0015 * - 0x10de0015
* - 0x10de0040 * - 0x10de0040
*/ */
static int nvhdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
int channels) struct cea_channel_speaker_allocation *cap, int channels)
{ {
if (cap->ca_index == 0x00 && channels == 2) if (cap->ca_index == 0x00 && channels == 2)
return SNDRV_CTL_TLVT_CHMAP_FIXED; return SNDRV_CTL_TLVT_CHMAP_FIXED;
return hdmi_chmap_cea_alloc_validate_get_type(cap, channels); return chmap->ops.chmap_cea_alloc_validate_get_type(
chmap, cap, channels);
} }
static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map) static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map)
@ -3532,9 +3528,9 @@ static int patch_nvhdmi(struct hda_codec *codec)
spec = codec->spec; spec = codec->spec;
spec->dyn_pin_out = true; 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; nvhdmi_chmap_cea_alloc_validate_get_type;
spec->ops.chmap_validate = nvhdmi_chmap_validate; spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
return 0; return 0;
} }
@ -3893,8 +3889,10 @@ static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_n
return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd; return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd;
} }
static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, static int atihdmi_paired_chmap_cea_alloc_validate_get_type(
int channels) struct hdac_chmap *chmap,
struct cea_channel_speaker_allocation *cap,
int channels)
{ {
int c; int c;
@ -4041,10 +4039,11 @@ static int patch_atihdmi(struct hda_codec *codec)
if (!has_amd_full_remap_support(codec)) { if (!has_amd_full_remap_support(codec)) {
/* override to ATI/AMD-specific versions with pairwise mapping */ /* 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; atihdmi_paired_chmap_cea_alloc_validate_get_type;
spec->ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap; spec->chmap.ops.cea_alloc_to_tlv_chmap =
spec->ops.chmap_validate = atihdmi_paired_chmap_validate; atihdmi_paired_cea_alloc_to_tlv_chmap;
spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate;
} }
/* ATI/AMD converters do not advertise all of their capabilities */ /* ATI/AMD converters do not advertise all of their capabilities */
@ -4056,7 +4055,7 @@ static int patch_atihdmi(struct hda_codec *codec)
per_cvt->maxbps = max(per_cvt->maxbps, 24u); 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; return 0;
} }