|
@@ -6,6 +6,7 @@
|
|
* Copyright (c) 2006 ATI Technologies Inc.
|
|
* Copyright (c) 2006 ATI Technologies Inc.
|
|
* Copyright (c) 2008 NVIDIA Corp. All rights reserved.
|
|
* Copyright (c) 2008 NVIDIA Corp. All rights reserved.
|
|
* Copyright (c) 2008 Wei Ni <wni@nvidia.com>
|
|
* Copyright (c) 2008 Wei Ni <wni@nvidia.com>
|
|
|
|
+ * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
|
|
*
|
|
*
|
|
* Authors:
|
|
* Authors:
|
|
* Wu Fengguang <wfg@linux.intel.com>
|
|
* Wu Fengguang <wfg@linux.intel.com>
|
|
@@ -45,6 +46,7 @@ module_param(static_hdmi_pcm, bool, 0644);
|
|
MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
|
|
MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
|
|
|
|
|
|
#define is_haswell(codec) ((codec)->vendor_id == 0x80862807)
|
|
#define is_haswell(codec) ((codec)->vendor_id == 0x80862807)
|
|
|
|
+#define is_valleyview(codec) ((codec)->vendor_id == 0x80862882)
|
|
|
|
|
|
struct hdmi_spec_per_cvt {
|
|
struct hdmi_spec_per_cvt {
|
|
hda_nid_t cvt_nid;
|
|
hda_nid_t cvt_nid;
|
|
@@ -63,9 +65,11 @@ struct hdmi_spec_per_pin {
|
|
hda_nid_t pin_nid;
|
|
hda_nid_t pin_nid;
|
|
int num_mux_nids;
|
|
int num_mux_nids;
|
|
hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
|
|
hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
|
|
|
|
+ hda_nid_t cvt_nid;
|
|
|
|
|
|
struct hda_codec *codec;
|
|
struct hda_codec *codec;
|
|
struct hdmi_eld sink_eld;
|
|
struct hdmi_eld sink_eld;
|
|
|
|
+ struct mutex lock;
|
|
struct delayed_work work;
|
|
struct delayed_work work;
|
|
struct snd_kcontrol *eld_ctl;
|
|
struct snd_kcontrol *eld_ctl;
|
|
int repoll_count;
|
|
int repoll_count;
|
|
@@ -75,6 +79,42 @@ struct hdmi_spec_per_pin {
|
|
bool chmap_set; /* channel-map override by ALSA API? */
|
|
bool chmap_set; /* channel-map override by ALSA API? */
|
|
unsigned char chmap[8]; /* ALSA API channel-map */
|
|
unsigned char chmap[8]; /* ALSA API channel-map */
|
|
char pcm_name[8]; /* filled in build_pcm callbacks */
|
|
char pcm_name[8]; /* filled in build_pcm callbacks */
|
|
|
|
+#ifdef CONFIG_PROC_FS
|
|
|
|
+ struct snd_info_entry *proc_entry;
|
|
|
|
+#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,
|
|
|
|
+ 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);
|
|
|
|
+
|
|
|
|
+ /* enable/disable HBR (HD passthrough) */
|
|
|
|
+ int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid, bool hbr);
|
|
|
|
+
|
|
|
|
+ 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_spec {
|
|
struct hdmi_spec {
|
|
@@ -88,8 +128,9 @@ struct hdmi_spec {
|
|
unsigned int channels_max; /* max over all cvts */
|
|
unsigned int channels_max; /* max over all cvts */
|
|
|
|
|
|
struct hdmi_eld temp_eld;
|
|
struct hdmi_eld temp_eld;
|
|
|
|
+ struct hdmi_ops ops;
|
|
/*
|
|
/*
|
|
- * Non-generic ATI/NVIDIA specific
|
|
|
|
|
|
+ * Non-generic VIA/NVIDIA specific
|
|
*/
|
|
*/
|
|
struct hda_multi_out multiout;
|
|
struct hda_multi_out multiout;
|
|
struct hda_pcm_stream pcm_playback;
|
|
struct hda_pcm_stream pcm_playback;
|
|
@@ -348,17 +389,19 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
|
|
{
|
|
{
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
struct hdmi_spec *spec = codec->spec;
|
|
struct hdmi_spec *spec = codec->spec;
|
|
|
|
+ struct hdmi_spec_per_pin *per_pin;
|
|
struct hdmi_eld *eld;
|
|
struct hdmi_eld *eld;
|
|
int pin_idx;
|
|
int pin_idx;
|
|
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
|
|
|
|
|
|
pin_idx = kcontrol->private_value;
|
|
pin_idx = kcontrol->private_value;
|
|
- eld = &get_pin(spec, pin_idx)->sink_eld;
|
|
|
|
|
|
+ per_pin = get_pin(spec, pin_idx);
|
|
|
|
+ eld = &per_pin->sink_eld;
|
|
|
|
|
|
- mutex_lock(&eld->lock);
|
|
|
|
|
|
+ mutex_lock(&per_pin->lock);
|
|
uinfo->count = eld->eld_valid ? eld->eld_size : 0;
|
|
uinfo->count = eld->eld_valid ? eld->eld_size : 0;
|
|
- mutex_unlock(&eld->lock);
|
|
|
|
|
|
+ mutex_unlock(&per_pin->lock);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -368,15 +411,17 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
|
|
{
|
|
{
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
struct hdmi_spec *spec = codec->spec;
|
|
struct hdmi_spec *spec = codec->spec;
|
|
|
|
+ struct hdmi_spec_per_pin *per_pin;
|
|
struct hdmi_eld *eld;
|
|
struct hdmi_eld *eld;
|
|
int pin_idx;
|
|
int pin_idx;
|
|
|
|
|
|
pin_idx = kcontrol->private_value;
|
|
pin_idx = kcontrol->private_value;
|
|
- eld = &get_pin(spec, pin_idx)->sink_eld;
|
|
|
|
|
|
+ per_pin = get_pin(spec, pin_idx);
|
|
|
|
+ eld = &per_pin->sink_eld;
|
|
|
|
|
|
- mutex_lock(&eld->lock);
|
|
|
|
|
|
+ mutex_lock(&per_pin->lock);
|
|
if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) {
|
|
if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) {
|
|
- mutex_unlock(&eld->lock);
|
|
|
|
|
|
+ mutex_unlock(&per_pin->lock);
|
|
snd_BUG();
|
|
snd_BUG();
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
@@ -386,7 +431,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
|
|
if (eld->eld_valid)
|
|
if (eld->eld_valid)
|
|
memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
|
|
memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
|
|
eld->eld_size);
|
|
eld->eld_size);
|
|
- mutex_unlock(&eld->lock);
|
|
|
|
|
|
+ mutex_unlock(&per_pin->lock);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -477,6 +522,68 @@ static void hdmi_set_channel_count(struct hda_codec *codec,
|
|
AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
|
|
AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * ELD proc files
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_PROC_FS
|
|
|
|
+static void print_eld_info(struct snd_info_entry *entry,
|
|
|
|
+ struct snd_info_buffer *buffer)
|
|
|
|
+{
|
|
|
|
+ struct hdmi_spec_per_pin *per_pin = entry->private_data;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&per_pin->lock);
|
|
|
|
+ snd_hdmi_print_eld_info(&per_pin->sink_eld, buffer);
|
|
|
|
+ mutex_unlock(&per_pin->lock);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void write_eld_info(struct snd_info_entry *entry,
|
|
|
|
+ struct snd_info_buffer *buffer)
|
|
|
|
+{
|
|
|
|
+ struct hdmi_spec_per_pin *per_pin = entry->private_data;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&per_pin->lock);
|
|
|
|
+ snd_hdmi_write_eld_info(&per_pin->sink_eld, buffer);
|
|
|
|
+ mutex_unlock(&per_pin->lock);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index)
|
|
|
|
+{
|
|
|
|
+ char name[32];
|
|
|
|
+ struct hda_codec *codec = per_pin->codec;
|
|
|
|
+ struct snd_info_entry *entry;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
|
|
|
|
+ err = snd_card_proc_new(codec->bus->card, name, &entry);
|
|
|
|
+ if (err < 0)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ snd_info_set_text_ops(entry, per_pin, print_eld_info);
|
|
|
|
+ entry->c.text.write = write_eld_info;
|
|
|
|
+ entry->mode |= S_IWUSR;
|
|
|
|
+ per_pin->proc_entry = entry;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
|
|
|
|
+{
|
|
|
|
+ if (!per_pin->codec->bus->shutdown && per_pin->proc_entry) {
|
|
|
|
+ snd_device_free(per_pin->codec->bus->card, per_pin->proc_entry);
|
|
|
|
+ per_pin->proc_entry = NULL;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+#else
|
|
|
|
+static inline int eld_proc_new(struct hdmi_spec_per_pin *per_pin,
|
|
|
|
+ int index)
|
|
|
|
+{
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
|
|
|
|
+{
|
|
|
|
+}
|
|
|
|
+#endif
|
|
|
|
|
|
/*
|
|
/*
|
|
* Channel mapping routines
|
|
* Channel mapping routines
|
|
@@ -577,74 +684,91 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec,
|
|
hda_nid_t pin_nid)
|
|
hda_nid_t pin_nid)
|
|
{
|
|
{
|
|
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
|
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
|
|
|
+ struct hdmi_spec *spec = codec->spec;
|
|
int i;
|
|
int i;
|
|
- int slot;
|
|
|
|
|
|
+ int channel;
|
|
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
for (i = 0; i < 8; i++) {
|
|
- slot = snd_hda_codec_read(codec, pin_nid, 0,
|
|
|
|
- AC_VERB_GET_HDMI_CHAN_SLOT, i);
|
|
|
|
|
|
+ channel = spec->ops.pin_get_slot_channel(codec, pin_nid, i);
|
|
printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
|
|
printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
|
|
- slot >> 4, slot & 0xf);
|
|
|
|
|
|
+ channel, i);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
|
|
static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
|
|
hda_nid_t pin_nid,
|
|
hda_nid_t pin_nid,
|
|
bool non_pcm,
|
|
bool non_pcm,
|
|
int ca)
|
|
int ca)
|
|
{
|
|
{
|
|
|
|
+ struct hdmi_spec *spec = codec->spec;
|
|
|
|
+ struct cea_channel_speaker_allocation *ch_alloc;
|
|
int i;
|
|
int i;
|
|
int err;
|
|
int err;
|
|
int order;
|
|
int order;
|
|
int non_pcm_mapping[8];
|
|
int non_pcm_mapping[8];
|
|
|
|
|
|
order = get_channel_allocation_order(ca);
|
|
order = get_channel_allocation_order(ca);
|
|
|
|
+ ch_alloc = &channel_allocations[order];
|
|
|
|
|
|
if (hdmi_channel_mapping[ca][1] == 0) {
|
|
if (hdmi_channel_mapping[ca][1] == 0) {
|
|
- for (i = 0; i < channel_allocations[order].channels; i++)
|
|
|
|
- hdmi_channel_mapping[ca][i] = i | (i << 4);
|
|
|
|
- for (; i < 8; i++)
|
|
|
|
- hdmi_channel_mapping[ca][i] = 0xf | (i << 4);
|
|
|
|
|
|
+ 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) {
|
|
if (non_pcm) {
|
|
- for (i = 0; i < channel_allocations[order].channels; i++)
|
|
|
|
- non_pcm_mapping[i] = i | (i << 4);
|
|
|
|
|
|
+ for (i = 0; i < ch_alloc->channels; i++)
|
|
|
|
+ non_pcm_mapping[i] = (i << 4) | i;
|
|
for (; i < 8; i++)
|
|
for (; i < 8; i++)
|
|
- non_pcm_mapping[i] = 0xf | (i << 4);
|
|
|
|
|
|
+ non_pcm_mapping[i] = (0xf << 4) | i;
|
|
}
|
|
}
|
|
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
for (i = 0; i < 8; i++) {
|
|
- err = snd_hda_codec_write(codec, pin_nid, 0,
|
|
|
|
- AC_VERB_SET_HDMI_CHAN_SLOT,
|
|
|
|
- non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][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) {
|
|
if (err) {
|
|
snd_printdd(KERN_NOTICE
|
|
snd_printdd(KERN_NOTICE
|
|
"HDMI: channel mapping failed\n");
|
|
"HDMI: channel mapping failed\n");
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
- hdmi_debug_channel_mapping(codec, pin_nid);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
struct channel_map_table {
|
|
struct channel_map_table {
|
|
unsigned char map; /* ALSA API channel map position */
|
|
unsigned char map; /* ALSA API channel map position */
|
|
- unsigned char cea_slot; /* CEA slot value */
|
|
|
|
int spk_mask; /* speaker position bit mask */
|
|
int spk_mask; /* speaker position bit mask */
|
|
};
|
|
};
|
|
|
|
|
|
static struct channel_map_table map_tables[] = {
|
|
static struct channel_map_table map_tables[] = {
|
|
- { SNDRV_CHMAP_FL, 0x00, FL },
|
|
|
|
- { SNDRV_CHMAP_FR, 0x01, FR },
|
|
|
|
- { SNDRV_CHMAP_RL, 0x04, RL },
|
|
|
|
- { SNDRV_CHMAP_RR, 0x05, RR },
|
|
|
|
- { SNDRV_CHMAP_LFE, 0x02, LFE },
|
|
|
|
- { SNDRV_CHMAP_FC, 0x03, FC },
|
|
|
|
- { SNDRV_CHMAP_RLC, 0x06, RLC },
|
|
|
|
- { SNDRV_CHMAP_RRC, 0x07, RRC },
|
|
|
|
|
|
+ { 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_FLH, FLH },
|
|
|
|
+ { SNDRV_CHMAP_FRH, FRH },
|
|
|
|
+ { SNDRV_CHMAP_FLW, FLW },
|
|
|
|
+ { SNDRV_CHMAP_FRW, FRW },
|
|
|
|
+ { SNDRV_CHMAP_TC, TC },
|
|
|
|
+ { SNDRV_CHMAP_FCH, FCH },
|
|
{} /* terminator */
|
|
{} /* terminator */
|
|
};
|
|
};
|
|
|
|
|
|
@@ -660,25 +784,19 @@ static int to_spk_mask(unsigned char c)
|
|
}
|
|
}
|
|
|
|
|
|
/* from ALSA API channel position to CEA slot */
|
|
/* from ALSA API channel position to CEA slot */
|
|
-static int to_cea_slot(unsigned char c)
|
|
|
|
|
|
+static int to_cea_slot(int ordered_ca, unsigned char pos)
|
|
{
|
|
{
|
|
- struct channel_map_table *t = map_tables;
|
|
|
|
- for (; t->map; t++) {
|
|
|
|
- if (t->map == c)
|
|
|
|
- return t->cea_slot;
|
|
|
|
- }
|
|
|
|
- return 0x0f;
|
|
|
|
-}
|
|
|
|
|
|
+ int mask = to_spk_mask(pos);
|
|
|
|
+ int i;
|
|
|
|
|
|
-/* from CEA slot to ALSA API channel position */
|
|
|
|
-static int from_cea_slot(unsigned char c)
|
|
|
|
-{
|
|
|
|
- struct channel_map_table *t = map_tables;
|
|
|
|
- for (; t->map; t++) {
|
|
|
|
- if (t->cea_slot == c)
|
|
|
|
- return t->map;
|
|
|
|
|
|
+ if (mask) {
|
|
|
|
+ for (i = 0; i < 8; i++) {
|
|
|
|
+ if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
|
|
|
|
+ return i;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- return 0;
|
|
|
|
|
|
+
|
|
|
|
+ return -1;
|
|
}
|
|
}
|
|
|
|
|
|
/* from speaker bit mask to ALSA API channel position */
|
|
/* from speaker bit mask to ALSA API channel position */
|
|
@@ -692,6 +810,14 @@ static int spk_to_chmap(int spk)
|
|
return 0;
|
|
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 */
|
|
/* get the CA index corresponding to the given ALSA API channel map */
|
|
static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
|
|
static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
|
|
{
|
|
{
|
|
@@ -718,18 +844,29 @@ static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
|
|
/* set up the channel slots for the given ALSA API channel map */
|
|
/* set up the channel slots for the given ALSA API channel map */
|
|
static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
|
|
static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
|
|
hda_nid_t pin_nid,
|
|
hda_nid_t pin_nid,
|
|
- int chs, unsigned char *map)
|
|
|
|
|
|
+ int chs, unsigned char *map,
|
|
|
|
+ int ca)
|
|
{
|
|
{
|
|
- int i;
|
|
|
|
- for (i = 0; i < 8; i++) {
|
|
|
|
- int val, err;
|
|
|
|
- if (i < chs)
|
|
|
|
- val = to_cea_slot(map[i]);
|
|
|
|
- else
|
|
|
|
- val = 0xf;
|
|
|
|
- val |= (i << 4);
|
|
|
|
- err = snd_hda_codec_write(codec, pin_nid, 0,
|
|
|
|
- AC_VERB_SET_HDMI_CHAN_SLOT, val);
|
|
|
|
|
|
+ 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)
|
|
if (err)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
@@ -740,9 +877,10 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
|
|
static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
|
|
static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
|
|
{
|
|
{
|
|
int i;
|
|
int i;
|
|
|
|
+ int ordered_ca = get_channel_allocation_order(ca);
|
|
for (i = 0; i < 8; i++) {
|
|
for (i = 0; i < 8; i++) {
|
|
- if (i < channel_allocations[ca].channels)
|
|
|
|
- map[i] = from_cea_slot((hdmi_channel_mapping[ca][i] >> 4) & 0x0f);
|
|
|
|
|
|
+ if (i < channel_allocations[ordered_ca].channels)
|
|
|
|
+ map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
|
|
else
|
|
else
|
|
map[i] = 0;
|
|
map[i] = 0;
|
|
}
|
|
}
|
|
@@ -755,11 +893,29 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec,
|
|
{
|
|
{
|
|
if (!non_pcm && chmap_set) {
|
|
if (!non_pcm && chmap_set) {
|
|
hdmi_manual_setup_channel_mapping(codec, pin_nid,
|
|
hdmi_manual_setup_channel_mapping(codec, pin_nid,
|
|
- channels, map);
|
|
|
|
|
|
+ channels, map, ca);
|
|
} else {
|
|
} else {
|
|
hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca);
|
|
hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca);
|
|
hdmi_setup_fake_chmap(map, 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;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -883,15 +1039,64 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void hdmi_pin_setup_infoframe(struct hda_codec *codec,
|
|
|
|
+ hda_nid_t pin_nid,
|
|
|
|
+ int ca, int active_channels,
|
|
|
|
+ int conn_type)
|
|
|
|
+{
|
|
|
|
+ union audio_infoframe ai;
|
|
|
|
+
|
|
|
|
+ if (conn_type == 0) { /* HDMI */
|
|
|
|
+ struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
|
|
|
|
+
|
|
|
|
+ hdmi_ai->type = 0x84;
|
|
|
|
+ hdmi_ai->ver = 0x01;
|
|
|
|
+ hdmi_ai->len = 0x0a;
|
|
|
|
+ hdmi_ai->CC02_CT47 = active_channels - 1;
|
|
|
|
+ hdmi_ai->CA = ca;
|
|
|
|
+ hdmi_checksum_audio_infoframe(hdmi_ai);
|
|
|
|
+ } else if (conn_type == 1) { /* DisplayPort */
|
|
|
|
+ struct dp_audio_infoframe *dp_ai = &ai.dp;
|
|
|
|
+
|
|
|
|
+ dp_ai->type = 0x84;
|
|
|
|
+ dp_ai->len = 0x1b;
|
|
|
|
+ dp_ai->ver = 0x11 << 2;
|
|
|
|
+ dp_ai->CC02_CT47 = active_channels - 1;
|
|
|
|
+ dp_ai->CA = ca;
|
|
|
|
+ } else {
|
|
|
|
+ snd_printd("HDMI: unknown connection type at pin %d\n",
|
|
|
|
+ pin_nid);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
|
|
|
|
+ * sizeof(*dp_ai) to avoid partial match/update problems when
|
|
|
|
+ * the user switches between HDMI/DP monitors.
|
|
|
|
+ */
|
|
|
|
+ if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes,
|
|
|
|
+ sizeof(ai))) {
|
|
|
|
+ snd_printdd("hdmi_pin_setup_infoframe: "
|
|
|
|
+ "pin=%d channels=%d ca=0x%02x\n",
|
|
|
|
+ pin_nid,
|
|
|
|
+ active_channels, ca);
|
|
|
|
+ hdmi_stop_infoframe_trans(codec, pin_nid);
|
|
|
|
+ hdmi_fill_audio_infoframe(codec, pin_nid,
|
|
|
|
+ ai.bytes, sizeof(ai));
|
|
|
|
+ hdmi_start_infoframe_trans(codec, pin_nid);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
|
|
static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
|
|
struct hdmi_spec_per_pin *per_pin,
|
|
struct hdmi_spec_per_pin *per_pin,
|
|
bool non_pcm)
|
|
bool non_pcm)
|
|
{
|
|
{
|
|
|
|
+ struct hdmi_spec *spec = codec->spec;
|
|
hda_nid_t pin_nid = per_pin->pin_nid;
|
|
hda_nid_t pin_nid = per_pin->pin_nid;
|
|
int channels = per_pin->channels;
|
|
int channels = per_pin->channels;
|
|
|
|
+ int active_channels;
|
|
struct hdmi_eld *eld;
|
|
struct hdmi_eld *eld;
|
|
- int ca;
|
|
|
|
- union audio_infoframe ai;
|
|
|
|
|
|
+ int ca, ordered_ca;
|
|
|
|
|
|
if (!channels)
|
|
if (!channels)
|
|
return;
|
|
return;
|
|
@@ -912,29 +1117,10 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
|
|
if (ca < 0)
|
|
if (ca < 0)
|
|
ca = 0;
|
|
ca = 0;
|
|
|
|
|
|
- memset(&ai, 0, sizeof(ai));
|
|
|
|
- if (eld->info.conn_type == 0) { /* HDMI */
|
|
|
|
- struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
|
|
|
|
|
|
+ ordered_ca = get_channel_allocation_order(ca);
|
|
|
|
+ active_channels = channel_allocations[ordered_ca].channels;
|
|
|
|
|
|
- hdmi_ai->type = 0x84;
|
|
|
|
- hdmi_ai->ver = 0x01;
|
|
|
|
- hdmi_ai->len = 0x0a;
|
|
|
|
- hdmi_ai->CC02_CT47 = channels - 1;
|
|
|
|
- hdmi_ai->CA = ca;
|
|
|
|
- hdmi_checksum_audio_infoframe(hdmi_ai);
|
|
|
|
- } else if (eld->info.conn_type == 1) { /* DisplayPort */
|
|
|
|
- struct dp_audio_infoframe *dp_ai = &ai.dp;
|
|
|
|
-
|
|
|
|
- dp_ai->type = 0x84;
|
|
|
|
- dp_ai->len = 0x1b;
|
|
|
|
- dp_ai->ver = 0x11 << 2;
|
|
|
|
- dp_ai->CC02_CT47 = channels - 1;
|
|
|
|
- dp_ai->CA = ca;
|
|
|
|
- } else {
|
|
|
|
- snd_printd("HDMI: unknown connection type at pin %d\n",
|
|
|
|
- pin_nid);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
|
|
+ hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels);
|
|
|
|
|
|
/*
|
|
/*
|
|
* always configure channel mapping, it may have been changed by the
|
|
* always configure channel mapping, it may have been changed by the
|
|
@@ -944,32 +1130,17 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
|
|
channels, per_pin->chmap,
|
|
channels, per_pin->chmap,
|
|
per_pin->chmap_set);
|
|
per_pin->chmap_set);
|
|
|
|
|
|
- /*
|
|
|
|
- * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
|
|
|
|
- * sizeof(*dp_ai) to avoid partial match/update problems when
|
|
|
|
- * the user switches between HDMI/DP monitors.
|
|
|
|
- */
|
|
|
|
- if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes,
|
|
|
|
- sizeof(ai))) {
|
|
|
|
- snd_printdd("hdmi_setup_audio_infoframe: "
|
|
|
|
- "pin=%d channels=%d\n",
|
|
|
|
- pin_nid,
|
|
|
|
- channels);
|
|
|
|
- hdmi_stop_infoframe_trans(codec, pin_nid);
|
|
|
|
- hdmi_fill_audio_infoframe(codec, pin_nid,
|
|
|
|
- ai.bytes, sizeof(ai));
|
|
|
|
- hdmi_start_infoframe_trans(codec, pin_nid);
|
|
|
|
- }
|
|
|
|
|
|
+ spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels,
|
|
|
|
+ eld->info.conn_type);
|
|
|
|
|
|
per_pin->non_pcm = non_pcm;
|
|
per_pin->non_pcm = non_pcm;
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* Unsolicited events
|
|
* Unsolicited events
|
|
*/
|
|
*/
|
|
|
|
|
|
-static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
|
|
|
|
|
|
+static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
|
|
|
|
|
|
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
|
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
|
{
|
|
{
|
|
@@ -995,8 +1166,8 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
|
if (pin_idx < 0)
|
|
if (pin_idx < 0)
|
|
return;
|
|
return;
|
|
|
|
|
|
- hdmi_present_sense(get_pin(spec, pin_idx), 1);
|
|
|
|
- snd_hda_jack_report_sync(codec);
|
|
|
|
|
|
+ if (hdmi_present_sense(get_pin(spec, pin_idx), 1))
|
|
|
|
+ snd_hda_jack_report_sync(codec);
|
|
}
|
|
}
|
|
|
|
|
|
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
|
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
|
@@ -1067,26 +1238,22 @@ static void haswell_verify_D0(struct hda_codec *codec,
|
|
#define is_hbr_format(format) \
|
|
#define is_hbr_format(format) \
|
|
((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7)
|
|
((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7)
|
|
|
|
|
|
-static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
|
|
|
|
- hda_nid_t pin_nid, u32 stream_tag, int format)
|
|
|
|
|
|
+static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
|
|
|
|
+ bool hbr)
|
|
{
|
|
{
|
|
- int pinctl;
|
|
|
|
- int new_pinctl = 0;
|
|
|
|
-
|
|
|
|
- if (is_haswell(codec))
|
|
|
|
- haswell_verify_D0(codec, cvt_nid, pin_nid);
|
|
|
|
|
|
+ int pinctl, new_pinctl;
|
|
|
|
|
|
if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {
|
|
if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {
|
|
pinctl = snd_hda_codec_read(codec, pin_nid, 0,
|
|
pinctl = snd_hda_codec_read(codec, pin_nid, 0,
|
|
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
|
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
|
|
|
|
|
new_pinctl = pinctl & ~AC_PINCTL_EPT;
|
|
new_pinctl = pinctl & ~AC_PINCTL_EPT;
|
|
- if (is_hbr_format(format))
|
|
|
|
|
|
+ if (hbr)
|
|
new_pinctl |= AC_PINCTL_EPT_HBR;
|
|
new_pinctl |= AC_PINCTL_EPT_HBR;
|
|
else
|
|
else
|
|
new_pinctl |= AC_PINCTL_EPT_NATIVE;
|
|
new_pinctl |= AC_PINCTL_EPT_NATIVE;
|
|
|
|
|
|
- snd_printdd("hdmi_setup_stream: "
|
|
|
|
|
|
+ snd_printdd("hdmi_pin_hbr_setup: "
|
|
"NID=0x%x, %spinctl=0x%x\n",
|
|
"NID=0x%x, %spinctl=0x%x\n",
|
|
pin_nid,
|
|
pin_nid,
|
|
pinctl == new_pinctl ? "" : "new-",
|
|
pinctl == new_pinctl ? "" : "new-",
|
|
@@ -1096,11 +1263,26 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
|
|
snd_hda_codec_write(codec, pin_nid, 0,
|
|
snd_hda_codec_write(codec, pin_nid, 0,
|
|
AC_VERB_SET_PIN_WIDGET_CONTROL,
|
|
AC_VERB_SET_PIN_WIDGET_CONTROL,
|
|
new_pinctl);
|
|
new_pinctl);
|
|
|
|
+ } else if (hbr)
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- }
|
|
|
|
- if (is_hbr_format(format) && !new_pinctl) {
|
|
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
|
|
|
|
+ hda_nid_t pin_nid, u32 stream_tag, int format)
|
|
|
|
+{
|
|
|
|
+ struct hdmi_spec *spec = codec->spec;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ if (is_haswell(codec))
|
|
|
|
+ haswell_verify_D0(codec, cvt_nid, pin_nid);
|
|
|
|
+
|
|
|
|
+ err = spec->ops.pin_hbr_setup(codec, pin_nid, is_hbr_format(format));
|
|
|
|
+
|
|
|
|
+ if (err) {
|
|
snd_printdd("hdmi_setup_stream: HBR is not supported\n");
|
|
snd_printdd("hdmi_setup_stream: HBR is not supported\n");
|
|
- return -EINVAL;
|
|
|
|
|
|
+ return err;
|
|
}
|
|
}
|
|
|
|
|
|
snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format);
|
|
snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format);
|
|
@@ -1146,7 +1328,16 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static void haswell_config_cvts(struct hda_codec *codec,
|
|
|
|
|
|
+/* Intel HDMI workaround to fix audio routing issue:
|
|
|
|
+ * For some Intel display codecs, pins share the same connection list.
|
|
|
|
+ * So a conveter can be selected by multiple pins and playback on any of these
|
|
|
|
+ * pins will generate sound on the external display, because audio flows from
|
|
|
|
+ * the same converter to the display pipeline. Also muting one pin may make
|
|
|
|
+ * other pins have no sound output.
|
|
|
|
+ * So this function assures that an assigned converter for a pin is not selected
|
|
|
|
+ * by any other pins.
|
|
|
|
+ */
|
|
|
|
+static void intel_not_share_assigned_cvt(struct hda_codec *codec,
|
|
hda_nid_t pin_nid, int mux_idx)
|
|
hda_nid_t pin_nid, int mux_idx)
|
|
{
|
|
{
|
|
struct hdmi_spec *spec = codec->spec;
|
|
struct hdmi_spec *spec = codec->spec;
|
|
@@ -1217,6 +1408,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
|
|
per_cvt = get_cvt(spec, cvt_idx);
|
|
per_cvt = get_cvt(spec, cvt_idx);
|
|
/* Claim converter */
|
|
/* Claim converter */
|
|
per_cvt->assigned = 1;
|
|
per_cvt->assigned = 1;
|
|
|
|
+ per_pin->cvt_nid = per_cvt->cvt_nid;
|
|
hinfo->nid = per_cvt->cvt_nid;
|
|
hinfo->nid = per_cvt->cvt_nid;
|
|
|
|
|
|
snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
|
|
snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
|
|
@@ -1224,8 +1416,8 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
|
|
mux_idx);
|
|
mux_idx);
|
|
|
|
|
|
/* configure unused pins to choose other converters */
|
|
/* configure unused pins to choose other converters */
|
|
- if (is_haswell(codec))
|
|
|
|
- haswell_config_cvts(codec, per_pin->pin_nid, mux_idx);
|
|
|
|
|
|
+ if (is_haswell(codec) || is_valleyview(codec))
|
|
|
|
+ intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx);
|
|
|
|
|
|
snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
|
|
snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
|
|
|
|
|
|
@@ -1283,8 +1475,9 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
|
|
|
|
|
+static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
|
{
|
|
{
|
|
|
|
+ struct hda_jack_tbl *jack;
|
|
struct hda_codec *codec = per_pin->codec;
|
|
struct hda_codec *codec = per_pin->codec;
|
|
struct hdmi_spec *spec = codec->spec;
|
|
struct hdmi_spec *spec = codec->spec;
|
|
struct hdmi_eld *eld = &spec->temp_eld;
|
|
struct hdmi_eld *eld = &spec->temp_eld;
|
|
@@ -1301,7 +1494,9 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
|
int present = snd_hda_pin_sense(codec, pin_nid);
|
|
int present = snd_hda_pin_sense(codec, pin_nid);
|
|
bool update_eld = false;
|
|
bool update_eld = false;
|
|
bool eld_changed = false;
|
|
bool eld_changed = false;
|
|
|
|
+ bool ret;
|
|
|
|
|
|
|
|
+ mutex_lock(&per_pin->lock);
|
|
pin_eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
|
|
pin_eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
|
|
if (pin_eld->monitor_present)
|
|
if (pin_eld->monitor_present)
|
|
eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
|
|
eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
|
|
@@ -1313,7 +1508,7 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
|
codec->addr, pin_nid, pin_eld->monitor_present, eld->eld_valid);
|
|
codec->addr, pin_nid, pin_eld->monitor_present, eld->eld_valid);
|
|
|
|
|
|
if (eld->eld_valid) {
|
|
if (eld->eld_valid) {
|
|
- if (snd_hdmi_get_eld(codec, pin_nid, eld->eld_buffer,
|
|
|
|
|
|
+ if (spec->ops.pin_get_eld(codec, pin_nid, eld->eld_buffer,
|
|
&eld->eld_size) < 0)
|
|
&eld->eld_size) < 0)
|
|
eld->eld_valid = false;
|
|
eld->eld_valid = false;
|
|
else {
|
|
else {
|
|
@@ -1331,11 +1526,10 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
|
queue_delayed_work(codec->bus->workq,
|
|
queue_delayed_work(codec->bus->workq,
|
|
&per_pin->work,
|
|
&per_pin->work,
|
|
msecs_to_jiffies(300));
|
|
msecs_to_jiffies(300));
|
|
- return;
|
|
|
|
|
|
+ goto unlock;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- mutex_lock(&pin_eld->lock);
|
|
|
|
if (pin_eld->eld_valid && !eld->eld_valid) {
|
|
if (pin_eld->eld_valid && !eld->eld_valid) {
|
|
update_eld = true;
|
|
update_eld = true;
|
|
eld_changed = true;
|
|
eld_changed = true;
|
|
@@ -1352,20 +1546,29 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
|
pin_eld->eld_size = eld->eld_size;
|
|
pin_eld->eld_size = eld->eld_size;
|
|
pin_eld->info = eld->info;
|
|
pin_eld->info = eld->info;
|
|
|
|
|
|
- /* Haswell-specific workaround: re-setup when the transcoder is
|
|
|
|
- * changed during the stream playback
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Re-setup pin and infoframe. This is needed e.g. when
|
|
|
|
+ * - sink is first plugged-in (infoframe is not set up if !monitor_present)
|
|
|
|
+ * - transcoder can change during stream playback on Haswell
|
|
*/
|
|
*/
|
|
- if (is_haswell(codec) &&
|
|
|
|
- eld->eld_valid && !old_eld_valid && per_pin->setup)
|
|
|
|
|
|
+ if (eld->eld_valid && !old_eld_valid && per_pin->setup)
|
|
hdmi_setup_audio_infoframe(codec, per_pin,
|
|
hdmi_setup_audio_infoframe(codec, per_pin,
|
|
per_pin->non_pcm);
|
|
per_pin->non_pcm);
|
|
}
|
|
}
|
|
- mutex_unlock(&pin_eld->lock);
|
|
|
|
|
|
|
|
if (eld_changed)
|
|
if (eld_changed)
|
|
snd_ctl_notify(codec->bus->card,
|
|
snd_ctl_notify(codec->bus->card,
|
|
SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
|
|
SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
|
|
&per_pin->eld_ctl->id);
|
|
&per_pin->eld_ctl->id);
|
|
|
|
+ unlock:
|
|
|
|
+ ret = !repoll || !pin_eld->monitor_present || pin_eld->eld_valid;
|
|
|
|
+
|
|
|
|
+ jack = snd_hda_jack_tbl_get(codec, pin_nid);
|
|
|
|
+ if (jack)
|
|
|
|
+ jack->block_report = !ret;
|
|
|
|
+
|
|
|
|
+ mutex_unlock(&per_pin->lock);
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
static void hdmi_repoll_eld(struct work_struct *work)
|
|
static void hdmi_repoll_eld(struct work_struct *work)
|
|
@@ -1376,7 +1579,8 @@ static void hdmi_repoll_eld(struct work_struct *work)
|
|
if (per_pin->repoll_count++ > 6)
|
|
if (per_pin->repoll_count++ > 6)
|
|
per_pin->repoll_count = 0;
|
|
per_pin->repoll_count = 0;
|
|
|
|
|
|
- hdmi_present_sense(per_pin, per_pin->repoll_count);
|
|
|
|
|
|
+ if (hdmi_present_sense(per_pin, per_pin->repoll_count))
|
|
|
|
+ snd_hda_jack_report_sync(per_pin->codec);
|
|
}
|
|
}
|
|
|
|
|
|
static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
|
|
static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
|
|
@@ -1536,14 +1740,14 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|
bool non_pcm;
|
|
bool non_pcm;
|
|
|
|
|
|
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
|
|
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
|
|
|
|
+ mutex_lock(&per_pin->lock);
|
|
per_pin->channels = substream->runtime->channels;
|
|
per_pin->channels = substream->runtime->channels;
|
|
per_pin->setup = true;
|
|
per_pin->setup = true;
|
|
|
|
|
|
- hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels);
|
|
|
|
-
|
|
|
|
hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
|
|
hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
|
|
|
|
+ mutex_unlock(&per_pin->lock);
|
|
|
|
|
|
- return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
|
|
|
|
|
|
+ return spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
|
|
}
|
|
}
|
|
|
|
|
|
static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
|
static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
|
@@ -1579,11 +1783,14 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
|
|
per_pin = get_pin(spec, pin_idx);
|
|
per_pin = get_pin(spec, pin_idx);
|
|
|
|
|
|
snd_hda_spdif_ctls_unassign(codec, pin_idx);
|
|
snd_hda_spdif_ctls_unassign(codec, pin_idx);
|
|
|
|
+
|
|
|
|
+ mutex_lock(&per_pin->lock);
|
|
per_pin->chmap_set = false;
|
|
per_pin->chmap_set = false;
|
|
memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
|
|
memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
|
|
|
|
|
|
per_pin->setup = false;
|
|
per_pin->setup = false;
|
|
per_pin->channels = 0;
|
|
per_pin->channels = 0;
|
|
|
|
+ mutex_unlock(&per_pin->lock);
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
@@ -1612,14 +1819,40 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
|
|
return 0;
|
|
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 int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
|
|
static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
|
|
unsigned int size, unsigned int __user *tlv)
|
|
unsigned int size, unsigned int __user *tlv)
|
|
{
|
|
{
|
|
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
|
|
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
|
|
struct 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;
|
|
- const unsigned int valid_mask =
|
|
|
|
- FL | FR | RL | RR | LFE | FC | RLC | RRC;
|
|
|
|
unsigned int __user *dst;
|
|
unsigned int __user *dst;
|
|
int chs, count = 0;
|
|
int chs, count = 0;
|
|
|
|
|
|
@@ -1630,18 +1863,19 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
|
|
size -= 8;
|
|
size -= 8;
|
|
dst = tlv + 2;
|
|
dst = tlv + 2;
|
|
for (chs = 2; chs <= spec->channels_max; chs++) {
|
|
for (chs = 2; chs <= spec->channels_max; chs++) {
|
|
- int i, c;
|
|
|
|
|
|
+ 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;
|
|
- if (cap->channels != chs)
|
|
|
|
- continue;
|
|
|
|
- if (cap->spk_mask & ~valid_mask)
|
|
|
|
|
|
+ int type = spec->ops.chmap_cea_alloc_validate_get_type(cap, chs);
|
|
|
|
+ unsigned int tlv_chmap[8];
|
|
|
|
+
|
|
|
|
+ if (type < 0)
|
|
continue;
|
|
continue;
|
|
if (size < 8)
|
|
if (size < 8)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
- if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, dst) ||
|
|
|
|
|
|
+ if (put_user(type, dst) ||
|
|
put_user(chs_bytes, dst + 1))
|
|
put_user(chs_bytes, dst + 1))
|
|
return -EFAULT;
|
|
return -EFAULT;
|
|
dst += 2;
|
|
dst += 2;
|
|
@@ -1651,14 +1885,10 @@ 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;
|
|
- for (c = 7; c >= 0; c--) {
|
|
|
|
- int spk = cap->speakers[c];
|
|
|
|
- if (!spk)
|
|
|
|
- continue;
|
|
|
|
- if (put_user(spk_to_chmap(spk), dst))
|
|
|
|
- return -EFAULT;
|
|
|
|
- dst++;
|
|
|
|
- }
|
|
|
|
|
|
+ spec->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))
|
|
if (put_user(count, tlv + 1))
|
|
@@ -1692,7 +1922,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
|
|
unsigned int ctl_idx;
|
|
unsigned int ctl_idx;
|
|
struct snd_pcm_substream *substream;
|
|
struct snd_pcm_substream *substream;
|
|
unsigned char chmap[8];
|
|
unsigned char chmap[8];
|
|
- int i, ca, prepared = 0;
|
|
|
|
|
|
+ int i, err, ca, prepared = 0;
|
|
|
|
|
|
ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
|
ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
|
substream = snd_pcm_chmap_substream(info, ctl_idx);
|
|
substream = snd_pcm_chmap_substream(info, ctl_idx);
|
|
@@ -1716,10 +1946,17 @@ 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) {
|
|
|
|
+ err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+ mutex_lock(&per_pin->lock);
|
|
per_pin->chmap_set = true;
|
|
per_pin->chmap_set = true;
|
|
memcpy(per_pin->chmap, chmap, sizeof(chmap));
|
|
memcpy(per_pin->chmap, chmap, sizeof(chmap));
|
|
if (prepared)
|
|
if (prepared)
|
|
hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
|
|
hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
|
|
|
|
+ mutex_unlock(&per_pin->lock);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -1836,12 +2073,11 @@ static int generic_hdmi_init_per_pins(struct hda_codec *codec)
|
|
|
|
|
|
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
|
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
|
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
|
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
|
- struct hdmi_eld *eld = &per_pin->sink_eld;
|
|
|
|
|
|
|
|
per_pin->codec = codec;
|
|
per_pin->codec = codec;
|
|
- mutex_init(&eld->lock);
|
|
|
|
|
|
+ mutex_init(&per_pin->lock);
|
|
INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld);
|
|
INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld);
|
|
- snd_hda_eld_proc_new(codec, eld, pin_idx);
|
|
|
|
|
|
+ eld_proc_new(per_pin, pin_idx);
|
|
}
|
|
}
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -1882,10 +2118,9 @@ static void generic_hdmi_free(struct hda_codec *codec)
|
|
|
|
|
|
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
|
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
|
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
|
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
|
- struct hdmi_eld *eld = &per_pin->sink_eld;
|
|
|
|
|
|
|
|
cancel_delayed_work(&per_pin->work);
|
|
cancel_delayed_work(&per_pin->work);
|
|
- snd_hda_eld_proc_free(codec, eld);
|
|
|
|
|
|
+ eld_proc_free(per_pin);
|
|
}
|
|
}
|
|
|
|
|
|
flush_workqueue(codec->bus->workq);
|
|
flush_workqueue(codec->bus->workq);
|
|
@@ -1922,6 +2157,17 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = {
|
|
#endif
|
|
#endif
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+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,
|
|
|
|
+ .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type,
|
|
|
|
+ .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
|
|
static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
|
|
static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
|
|
hda_nid_t nid)
|
|
hda_nid_t nid)
|
|
@@ -2004,6 +2250,7 @@ static int patch_generic_hdmi(struct hda_codec *codec)
|
|
if (spec == NULL)
|
|
if (spec == NULL)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
+ spec->ops = generic_standard_hdmi_ops;
|
|
codec->spec = spec;
|
|
codec->spec = spec;
|
|
hdmi_array_init(spec, 4);
|
|
hdmi_array_init(spec, 4);
|
|
|
|
|
|
@@ -2559,49 +2806,398 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
- * ATI-specific implementations
|
|
|
|
- *
|
|
|
|
- * FIXME: we may omit the whole this and use the generic code once after
|
|
|
|
- * it's confirmed to work.
|
|
|
|
|
|
+ * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on:
|
|
|
|
+ * - 0x10de0015
|
|
|
|
+ * - 0x10de0040
|
|
*/
|
|
*/
|
|
|
|
+static int nvhdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
|
|
|
|
+ int channels)
|
|
|
|
+{
|
|
|
|
+ if (cap->ca_index == 0x00 && channels == 2)
|
|
|
|
+ return SNDRV_CTL_TLVT_CHMAP_FIXED;
|
|
|
|
+
|
|
|
|
+ return hdmi_chmap_cea_alloc_validate_get_type(cap, channels);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map)
|
|
|
|
+{
|
|
|
|
+ if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int patch_nvhdmi(struct hda_codec *codec)
|
|
|
|
+{
|
|
|
|
+ struct hdmi_spec *spec;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ err = patch_generic_hdmi(codec);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ spec = codec->spec;
|
|
|
|
+
|
|
|
|
+ spec->ops.chmap_cea_alloc_validate_get_type =
|
|
|
|
+ nvhdmi_chmap_cea_alloc_validate_get_type;
|
|
|
|
+ spec->ops.chmap_validate = nvhdmi_chmap_validate;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * ATI/AMD-specific implementations
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#define is_amdhdmi_rev3_or_later(codec) \
|
|
|
|
+ ((codec)->vendor_id == 0x1002aa01 && ((codec)->revision_id & 0xff00) >= 0x0300)
|
|
|
|
+#define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec)
|
|
|
|
+
|
|
|
|
+/* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */
|
|
|
|
+#define ATI_VERB_SET_CHANNEL_ALLOCATION 0x771
|
|
|
|
+#define ATI_VERB_SET_DOWNMIX_INFO 0x772
|
|
|
|
+#define ATI_VERB_SET_MULTICHANNEL_01 0x777
|
|
|
|
+#define ATI_VERB_SET_MULTICHANNEL_23 0x778
|
|
|
|
+#define ATI_VERB_SET_MULTICHANNEL_45 0x779
|
|
|
|
+#define ATI_VERB_SET_MULTICHANNEL_67 0x77a
|
|
|
|
+#define ATI_VERB_SET_HBR_CONTROL 0x77c
|
|
|
|
+#define ATI_VERB_SET_MULTICHANNEL_1 0x785
|
|
|
|
+#define ATI_VERB_SET_MULTICHANNEL_3 0x786
|
|
|
|
+#define ATI_VERB_SET_MULTICHANNEL_5 0x787
|
|
|
|
+#define ATI_VERB_SET_MULTICHANNEL_7 0x788
|
|
|
|
+#define ATI_VERB_SET_MULTICHANNEL_MODE 0x789
|
|
|
|
+#define ATI_VERB_GET_CHANNEL_ALLOCATION 0xf71
|
|
|
|
+#define ATI_VERB_GET_DOWNMIX_INFO 0xf72
|
|
|
|
+#define ATI_VERB_GET_MULTICHANNEL_01 0xf77
|
|
|
|
+#define ATI_VERB_GET_MULTICHANNEL_23 0xf78
|
|
|
|
+#define ATI_VERB_GET_MULTICHANNEL_45 0xf79
|
|
|
|
+#define ATI_VERB_GET_MULTICHANNEL_67 0xf7a
|
|
|
|
+#define ATI_VERB_GET_HBR_CONTROL 0xf7c
|
|
|
|
+#define ATI_VERB_GET_MULTICHANNEL_1 0xf85
|
|
|
|
+#define ATI_VERB_GET_MULTICHANNEL_3 0xf86
|
|
|
|
+#define ATI_VERB_GET_MULTICHANNEL_5 0xf87
|
|
|
|
+#define ATI_VERB_GET_MULTICHANNEL_7 0xf88
|
|
|
|
+#define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89
|
|
|
|
+
|
|
|
|
+/* AMD specific HDA cvt verbs */
|
|
|
|
+#define ATI_VERB_SET_RAMP_RATE 0x770
|
|
|
|
+#define ATI_VERB_GET_RAMP_RATE 0xf70
|
|
|
|
+
|
|
|
|
+#define ATI_OUT_ENABLE 0x1
|
|
|
|
+
|
|
|
|
+#define ATI_MULTICHANNEL_MODE_PAIRED 0
|
|
|
|
+#define ATI_MULTICHANNEL_MODE_SINGLE 1
|
|
|
|
+
|
|
|
|
+#define ATI_HBR_CAPABLE 0x01
|
|
|
|
+#define ATI_HBR_ENABLE 0x10
|
|
|
|
+
|
|
|
|
+static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid,
|
|
|
|
+ unsigned char *buf, int *eld_size)
|
|
|
|
+{
|
|
|
|
+ /* call hda_eld.c ATI/AMD-specific function */
|
|
|
|
+ return snd_hdmi_get_eld_ati(codec, nid, buf, eld_size,
|
|
|
|
+ is_amdhdmi_rev3_or_later(codec));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void atihdmi_pin_setup_infoframe(struct hda_codec *codec, hda_nid_t pin_nid, int ca,
|
|
|
|
+ int active_channels, int conn_type)
|
|
|
|
+{
|
|
|
|
+ snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int atihdmi_paired_swap_fc_lfe(int pos)
|
|
|
|
+{
|
|
|
|
+ /*
|
|
|
|
+ * ATI/AMD have automatic FC/LFE swap built-in
|
|
|
|
+ * when in pairwise mapping mode.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ switch (pos) {
|
|
|
|
+ /* see channel_allocations[].speakers[] */
|
|
|
|
+ case 2: return 3;
|
|
|
|
+ case 3: return 2;
|
|
|
|
+ default: break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return pos;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
|
|
|
|
+{
|
|
|
|
+ struct cea_channel_speaker_allocation *cap;
|
|
|
|
+ int i, j;
|
|
|
|
+
|
|
|
|
+ /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */
|
|
|
|
+
|
|
|
|
+ cap = &channel_allocations[get_channel_allocation_order(ca)];
|
|
|
|
+ for (i = 0; i < chs; ++i) {
|
|
|
|
+ int mask = to_spk_mask(map[i]);
|
|
|
|
+ bool ok = false;
|
|
|
|
+ bool companion_ok = false;
|
|
|
|
+
|
|
|
|
+ if (!mask)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ for (j = 0 + i % 2; j < 8; j += 2) {
|
|
|
|
+ int chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j);
|
|
|
|
+ if (cap->speakers[chan_idx] == mask) {
|
|
|
|
+ /* channel is in a supported position */
|
|
|
|
+ ok = true;
|
|
|
|
+
|
|
|
|
+ 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_act = cap->speakers[comp_chan_idx];
|
|
|
|
+
|
|
|
|
+ if (comp_mask_req == comp_mask_act)
|
|
|
|
+ companion_ok = true;
|
|
|
|
+ else
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!ok)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (companion_ok)
|
|
|
|
+ i++; /* companion channel already checked */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
|
|
|
|
+ int hdmi_slot, int stream_channel)
|
|
|
|
+{
|
|
|
|
+ int verb;
|
|
|
|
+ int ati_channel_setup = 0;
|
|
|
|
+
|
|
|
|
+ if (hdmi_slot > 7)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (!has_amd_full_remap_support(codec)) {
|
|
|
|
+ hdmi_slot = atihdmi_paired_swap_fc_lfe(hdmi_slot);
|
|
|
|
+
|
|
|
|
+ /* In case this is an odd slot but without stream channel, do not
|
|
|
|
+ * disable the slot since the corresponding even slot could have a
|
|
|
|
+ * channel. In case neither have a channel, the slot pair will be
|
|
|
|
+ * disabled when this function is called for the even slot. */
|
|
|
|
+ if (hdmi_slot % 2 != 0 && stream_channel == 0xf)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ hdmi_slot -= hdmi_slot % 2;
|
|
|
|
+
|
|
|
|
+ if (stream_channel != 0xf)
|
|
|
|
+ stream_channel -= stream_channel % 2;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e;
|
|
|
|
+
|
|
|
|
+ /* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */
|
|
|
|
|
|
-#define ATIHDMI_CVT_NID 0x02 /* audio converter */
|
|
|
|
-#define ATIHDMI_PIN_NID 0x03 /* HDMI output pin */
|
|
|
|
|
|
+ if (stream_channel != 0xf)
|
|
|
|
+ ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE;
|
|
|
|
|
|
-static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|
|
|
- struct hda_codec *codec,
|
|
|
|
- unsigned int stream_tag,
|
|
|
|
- unsigned int format,
|
|
|
|
- struct snd_pcm_substream *substream)
|
|
|
|
|
|
+ return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
|
|
|
|
+ int asp_slot)
|
|
|
|
+{
|
|
|
|
+ bool was_odd = false;
|
|
|
|
+ int ati_asp_slot = asp_slot;
|
|
|
|
+ int verb;
|
|
|
|
+ int ati_channel_setup;
|
|
|
|
+
|
|
|
|
+ if (asp_slot > 7)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (!has_amd_full_remap_support(codec)) {
|
|
|
|
+ ati_asp_slot = atihdmi_paired_swap_fc_lfe(asp_slot);
|
|
|
|
+ if (ati_asp_slot % 2 != 0) {
|
|
|
|
+ ati_asp_slot -= 1;
|
|
|
|
+ was_odd = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e;
|
|
|
|
+
|
|
|
|
+ ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0);
|
|
|
|
+
|
|
|
|
+ if (!(ati_channel_setup & ATI_OUT_ENABLE))
|
|
|
|
+ return 0xf;
|
|
|
|
+
|
|
|
|
+ return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
|
|
|
|
+ int channels)
|
|
|
|
+{
|
|
|
|
+ int c;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Pre-rev3 ATI/AMD codecs operate in a paired channel mode, so
|
|
|
|
+ * we need to take that into account (a single channel may take 2
|
|
|
|
+ * channel slots if we need to carry a silent channel next to it).
|
|
|
|
+ * On Rev3+ AMD codecs this function is not used.
|
|
|
|
+ */
|
|
|
|
+ int chanpairs = 0;
|
|
|
|
+
|
|
|
|
+ /* We only produce even-numbered channel count TLVs */
|
|
|
|
+ if ((channels % 2) != 0)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ for (c = 0; c < 7; c += 2) {
|
|
|
|
+ if (cap->speakers[c] || cap->speakers[c+1])
|
|
|
|
+ chanpairs++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (chanpairs * 2 != channels)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ return SNDRV_CTL_TLVT_CHMAP_PAIRED;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap,
|
|
|
|
+ unsigned int *chmap, int channels)
|
|
|
|
+{
|
|
|
|
+ /* produce paired maps for pre-rev3 ATI/AMD codecs */
|
|
|
|
+ int count = 0;
|
|
|
|
+ int c;
|
|
|
|
+
|
|
|
|
+ for (c = 7; c >= 0; c--) {
|
|
|
|
+ int chan = 7 - atihdmi_paired_swap_fc_lfe(7 - c);
|
|
|
|
+ int spk = cap->speakers[chan];
|
|
|
|
+ if (!spk) {
|
|
|
|
+ /* add N/A channel if the companion channel is occupied */
|
|
|
|
+ if (cap->speakers[chan + (chan % 2 ? -1 : 1)])
|
|
|
|
+ chmap[count++] = SNDRV_CHMAP_NA;
|
|
|
|
+
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ chmap[count++] = spk_to_chmap(spk);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ WARN_ON(count != channels);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
|
|
|
|
+ bool hbr)
|
|
|
|
+{
|
|
|
|
+ int hbr_ctl, hbr_ctl_new;
|
|
|
|
+
|
|
|
|
+ hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0);
|
|
|
|
+ if (hbr_ctl & ATI_HBR_CAPABLE) {
|
|
|
|
+ if (hbr)
|
|
|
|
+ hbr_ctl_new = hbr_ctl | ATI_HBR_ENABLE;
|
|
|
|
+ else
|
|
|
|
+ hbr_ctl_new = hbr_ctl & ~ATI_HBR_ENABLE;
|
|
|
|
+
|
|
|
|
+ snd_printdd("atihdmi_pin_hbr_setup: "
|
|
|
|
+ "NID=0x%x, %shbr-ctl=0x%x\n",
|
|
|
|
+ pin_nid,
|
|
|
|
+ hbr_ctl == hbr_ctl_new ? "" : "new-",
|
|
|
|
+ hbr_ctl_new);
|
|
|
|
+
|
|
|
|
+ if (hbr_ctl != hbr_ctl_new)
|
|
|
|
+ snd_hda_codec_write(codec, pin_nid, 0,
|
|
|
|
+ ATI_VERB_SET_HBR_CONTROL,
|
|
|
|
+ hbr_ctl_new);
|
|
|
|
+
|
|
|
|
+ } else if (hbr)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
|
|
|
|
+ hda_nid_t pin_nid, u32 stream_tag, int format)
|
|
|
|
+{
|
|
|
|
+
|
|
|
|
+ if (is_amdhdmi_rev3_or_later(codec)) {
|
|
|
|
+ int ramp_rate = 180; /* default as per AMD spec */
|
|
|
|
+ /* disable ramp-up/down for non-pcm as per AMD spec */
|
|
|
|
+ if (format & AC_FMT_TYPE_NON_PCM)
|
|
|
|
+ ramp_rate = 0;
|
|
|
|
+
|
|
|
|
+ snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static int atihdmi_init(struct hda_codec *codec)
|
|
{
|
|
{
|
|
struct hdmi_spec *spec = codec->spec;
|
|
struct hdmi_spec *spec = codec->spec;
|
|
- struct hdmi_spec_per_cvt *per_cvt = get_cvt(spec, 0);
|
|
|
|
- int chans = substream->runtime->channels;
|
|
|
|
- int i, err;
|
|
|
|
|
|
+ int pin_idx, err;
|
|
|
|
|
|
- err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format,
|
|
|
|
- substream);
|
|
|
|
- if (err < 0)
|
|
|
|
|
|
+ err = generic_hdmi_init(codec);
|
|
|
|
+
|
|
|
|
+ if (err)
|
|
return err;
|
|
return err;
|
|
- snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
|
|
|
|
- AC_VERB_SET_CVT_CHAN_COUNT, chans - 1);
|
|
|
|
- /* FIXME: XXX */
|
|
|
|
- for (i = 0; i < chans; i++) {
|
|
|
|
- snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
|
|
|
|
- AC_VERB_SET_HDMI_CHAN_SLOT,
|
|
|
|
- (i << 4) | i);
|
|
|
|
|
|
+
|
|
|
|
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
|
|
|
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
|
|
|
+
|
|
|
|
+ /* make sure downmix information in infoframe is zero */
|
|
|
|
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0);
|
|
|
|
+
|
|
|
|
+ /* enable channel-wise remap mode if supported */
|
|
|
|
+ if (has_amd_full_remap_support(codec))
|
|
|
|
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0,
|
|
|
|
+ ATI_VERB_SET_MULTICHANNEL_MODE,
|
|
|
|
+ ATI_MULTICHANNEL_MODE_SINGLE);
|
|
}
|
|
}
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int patch_atihdmi(struct hda_codec *codec)
|
|
static int patch_atihdmi(struct hda_codec *codec)
|
|
{
|
|
{
|
|
struct hdmi_spec *spec;
|
|
struct hdmi_spec *spec;
|
|
- int err = patch_simple_hdmi(codec, ATIHDMI_CVT_NID, ATIHDMI_PIN_NID);
|
|
|
|
- if (err < 0)
|
|
|
|
|
|
+ struct hdmi_spec_per_cvt *per_cvt;
|
|
|
|
+ int err, cvt_idx;
|
|
|
|
+
|
|
|
|
+ err = patch_generic_hdmi(codec);
|
|
|
|
+
|
|
|
|
+ if (err)
|
|
return err;
|
|
return err;
|
|
|
|
+
|
|
|
|
+ codec->patch_ops.init = atihdmi_init;
|
|
|
|
+
|
|
spec = codec->spec;
|
|
spec = codec->spec;
|
|
- spec->pcm_playback.ops.prepare = atihdmi_playback_pcm_prepare;
|
|
|
|
|
|
+
|
|
|
|
+ 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;
|
|
|
|
+
|
|
|
|
+ if (!has_amd_full_remap_support(codec)) {
|
|
|
|
+ /* override to ATI/AMD-specific versions with pairwise mapping */
|
|
|
|
+ spec->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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* ATI/AMD converters do not advertise all of their capabilities */
|
|
|
|
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
|
|
|
|
+ per_cvt = get_cvt(spec, cvt_idx);
|
|
|
|
+ per_cvt->channels_max = max(per_cvt->channels_max, 8u);
|
|
|
|
+ per_cvt->rates |= SUPPORTED_RATES;
|
|
|
|
+ per_cvt->formats |= SUPPORTED_FORMATS;
|
|
|
|
+ per_cvt->maxbps = max(per_cvt->maxbps, 24u);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ spec->channels_max = max(spec->channels_max, 8u);
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2621,7 +3217,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
|
|
{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
|
|
{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
|
|
{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
|
|
{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
|
|
{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
|
|
{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
|
|
-{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_generic_hdmi },
|
|
|
|
|
|
+{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
|
|
{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi },
|
|
{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi },
|
|
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi },
|
|
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi },
|
|
{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi },
|
|
{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi },
|
|
@@ -2630,30 +3226,30 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
|
|
{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
|
|
{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
|
|
{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
|
|
{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
|
|
{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
|
|
{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
|
|
-{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
|
|
+{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_nvhdmi },
|
|
/* 17 is known to be absent */
|
|
/* 17 is known to be absent */
|
|
-{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
-{ .id = 0x10de0060, .name = "GPU 60 HDMI/DP", .patch = patch_generic_hdmi },
|
|
|
|
|
|
+{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP", .patch = patch_nvhdmi },
|
|
|
|
+{ .id = 0x10de0060, .name = "GPU 60 HDMI/DP", .patch = patch_nvhdmi },
|
|
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
|
|
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
|
|
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
|
|
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
|
|
{ .id = 0x11069f80, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
|
|
{ .id = 0x11069f80, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
|
|
@@ -2669,6 +3265,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
|
|
{ .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi },
|
|
{ .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi },
|
|
{ .id = 0x80862807, .name = "Haswell HDMI", .patch = patch_generic_hdmi },
|
|
{ .id = 0x80862807, .name = "Haswell HDMI", .patch = patch_generic_hdmi },
|
|
{ .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi },
|
|
{ .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi },
|
|
|
|
+{ .id = 0x80862882, .name = "Valleyview2 HDMI", .patch = patch_generic_hdmi },
|
|
{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
|
|
{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
|
|
{} /* terminator */
|
|
{} /* terminator */
|
|
};
|
|
};
|
|
@@ -2723,6 +3320,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862805");
|
|
MODULE_ALIAS("snd-hda-codec-id:80862806");
|
|
MODULE_ALIAS("snd-hda-codec-id:80862806");
|
|
MODULE_ALIAS("snd-hda-codec-id:80862807");
|
|
MODULE_ALIAS("snd-hda-codec-id:80862807");
|
|
MODULE_ALIAS("snd-hda-codec-id:80862880");
|
|
MODULE_ALIAS("snd-hda-codec-id:80862880");
|
|
|
|
+MODULE_ALIAS("snd-hda-codec-id:80862882");
|
|
MODULE_ALIAS("snd-hda-codec-id:808629fb");
|
|
MODULE_ALIAS("snd-hda-codec-id:808629fb");
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_LICENSE("GPL");
|