Merge branch 'topic/hda' into for-linus
This commit is contained in:
commit
f1cf9a666d
|
@ -157,7 +157,7 @@ config SND_HDA_CODEC_INTELHDMI
|
|||
|
||||
config SND_HDA_ELD
|
||||
def_bool y
|
||||
depends on SND_HDA_CODEC_INTELHDMI
|
||||
depends on SND_HDA_CODEC_INTELHDMI || SND_HDA_CODEC_NVHDMI
|
||||
|
||||
config SND_HDA_CODEC_CIRRUS
|
||||
bool "Build Cirrus Logic codec support"
|
||||
|
|
|
@ -3,7 +3,7 @@ snd-hda-intel-objs := hda_intel.o
|
|||
snd-hda-codec-y := hda_codec.o
|
||||
snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
|
||||
snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
|
||||
# snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o
|
||||
snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o
|
||||
snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
|
||||
snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
|
||||
|
||||
|
@ -18,7 +18,7 @@ snd-hda-codec-ca0110-objs := patch_ca0110.o
|
|||
snd-hda-codec-conexant-objs := patch_conexant.o
|
||||
snd-hda-codec-via-objs := patch_via.o
|
||||
snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o
|
||||
snd-hda-codec-intelhdmi-objs := patch_intelhdmi.o hda_eld.o
|
||||
snd-hda-codec-intelhdmi-objs := patch_intelhdmi.o
|
||||
|
||||
# common driver
|
||||
obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
|
||||
|
|
|
@ -978,8 +978,9 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
|
|||
*
|
||||
* Returns 0 if successful, or a negative error code.
|
||||
*/
|
||||
int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
|
||||
struct hda_codec **codecp)
|
||||
int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
|
||||
unsigned int codec_addr,
|
||||
struct hda_codec **codecp)
|
||||
{
|
||||
struct hda_codec *codec;
|
||||
char component[31];
|
||||
|
@ -1186,7 +1187,7 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
|
|||
*/
|
||||
|
||||
/* FIXME: more better hash key? */
|
||||
#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
|
||||
#define HDA_HASH_KEY(nid, dir, idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
|
||||
#define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
|
||||
#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
|
||||
#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24))
|
||||
|
@ -1356,7 +1357,8 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
|
|||
if (!codec->no_trigger_sense) {
|
||||
pincap = snd_hda_query_pin_caps(codec, nid);
|
||||
if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
|
||||
snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
|
||||
snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_SET_PIN_SENSE, 0);
|
||||
}
|
||||
return snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0);
|
||||
|
@ -1372,8 +1374,8 @@ EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
|
|||
*/
|
||||
int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
u32 sense = snd_hda_pin_sense(codec, nid);
|
||||
return !!(sense & AC_PINSENSE_PRESENCE);
|
||||
u32 sense = snd_hda_pin_sense(codec, nid);
|
||||
return !!(sense & AC_PINSENSE_PRESENCE);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
|
||||
|
||||
|
@ -1952,7 +1954,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
|
|||
err = snd_hda_ctl_add(codec, 0, kctl);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
||||
for (s = slaves; *s; s++) {
|
||||
struct snd_kcontrol *sctl;
|
||||
int i = 0;
|
||||
|
@ -2439,27 +2441,27 @@ static struct snd_kcontrol_new dig_mixes[] = {
|
|||
{
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
|
||||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
|
||||
.info = snd_hda_spdif_mask_info,
|
||||
.get = snd_hda_spdif_cmask_get,
|
||||
},
|
||||
{
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
|
||||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
|
||||
.info = snd_hda_spdif_mask_info,
|
||||
.get = snd_hda_spdif_pmask_get,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
|
||||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
|
||||
.info = snd_hda_spdif_mask_info,
|
||||
.get = snd_hda_spdif_default_get,
|
||||
.put = snd_hda_spdif_default_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
|
||||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
|
||||
.info = snd_hda_spdif_out_switch_info,
|
||||
.get = snd_hda_spdif_out_switch_get,
|
||||
.put = snd_hda_spdif_out_switch_put,
|
||||
|
@ -2610,7 +2612,7 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
|
|||
static struct snd_kcontrol_new dig_in_ctls[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH),
|
||||
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH),
|
||||
.info = snd_hda_spdif_in_switch_info,
|
||||
.get = snd_hda_spdif_in_switch_get,
|
||||
.put = snd_hda_spdif_in_switch_put,
|
||||
|
@ -2618,7 +2620,7 @@ static struct snd_kcontrol_new dig_in_ctls[] = {
|
|||
{
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
|
||||
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
|
||||
.info = snd_hda_spdif_mask_info,
|
||||
.get = snd_hda_spdif_in_status_get,
|
||||
},
|
||||
|
@ -2883,7 +2885,7 @@ int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus)
|
|||
int err = snd_hda_codec_build_controls(codec);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "hda_codec: cannot build controls"
|
||||
"for #%d (error %d)\n", codec->addr, err);
|
||||
"for #%d (error %d)\n", codec->addr, err);
|
||||
err = snd_hda_codec_reset(codec);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR
|
||||
|
@ -2979,8 +2981,12 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
|
|||
val |= channels - 1;
|
||||
|
||||
switch (snd_pcm_format_width(format)) {
|
||||
case 8: val |= 0x00; break;
|
||||
case 16: val |= 0x10; break;
|
||||
case 8:
|
||||
val |= 0x00;
|
||||
break;
|
||||
case 16:
|
||||
val |= 0x10;
|
||||
break;
|
||||
case 20:
|
||||
case 24:
|
||||
case 32:
|
||||
|
@ -3298,7 +3304,8 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type)
|
|||
if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
|
||||
return audio_idx[type][i];
|
||||
|
||||
snd_printk(KERN_WARNING "Too many %s devices\n", snd_hda_pcm_type_name[type]);
|
||||
snd_printk(KERN_WARNING "Too many %s devices\n",
|
||||
snd_hda_pcm_type_name[type]);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
|
@ -3336,7 +3343,7 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
|
|||
err = codec->patch_ops.build_pcms(codec);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "hda_codec: cannot build PCMs"
|
||||
"for #%d (error %d)\n", codec->addr, err);
|
||||
"for #%d (error %d)\n", codec->addr, err);
|
||||
err = snd_hda_codec_reset(codec);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR
|
||||
|
@ -3466,8 +3473,8 @@ EXPORT_SYMBOL_HDA(snd_hda_check_board_config);
|
|||
|
||||
/**
|
||||
* snd_hda_check_board_codec_sid_config - compare the current codec
|
||||
subsystem ID with the
|
||||
config table
|
||||
subsystem ID with the
|
||||
config table
|
||||
|
||||
This is important for Gateway notebooks with SB450 HDA Audio
|
||||
where the vendor ID of the PCI device is:
|
||||
|
@ -3607,7 +3614,7 @@ void snd_hda_update_power_acct(struct hda_codec *codec)
|
|||
*
|
||||
* Increment the power-up counter and power up the hardware really when
|
||||
* not turned on yet.
|
||||
*/
|
||||
*/
|
||||
void snd_hda_power_up(struct hda_codec *codec)
|
||||
{
|
||||
struct hda_bus *bus = codec->bus;
|
||||
|
@ -3636,7 +3643,7 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up);
|
|||
*
|
||||
* Decrement the power-up counter and schedules the power-off work if
|
||||
* the counter rearches to zero.
|
||||
*/
|
||||
*/
|
||||
void snd_hda_power_down(struct hda_codec *codec)
|
||||
{
|
||||
--codec->power_count;
|
||||
|
@ -3662,7 +3669,7 @@ EXPORT_SYMBOL_HDA(snd_hda_power_down);
|
|||
*
|
||||
* This function is supposed to be set or called from the check_power_status
|
||||
* patch ops.
|
||||
*/
|
||||
*/
|
||||
int snd_hda_check_amp_list_power(struct hda_codec *codec,
|
||||
struct hda_loopback_check *check,
|
||||
hda_nid_t nid)
|
||||
|
@ -3830,7 +3837,7 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
|
|||
{
|
||||
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
|
||||
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
|
||||
set_dig_out_convert(codec, nid,
|
||||
set_dig_out_convert(codec, nid,
|
||||
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff,
|
||||
-1);
|
||||
snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
|
||||
|
@ -4089,13 +4096,13 @@ static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
|
|||
/*
|
||||
* Sort an associated group of pins according to their sequence numbers.
|
||||
*/
|
||||
static void sort_pins_by_sequence(hda_nid_t * pins, short * sequences,
|
||||
static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
|
||||
int num_pins)
|
||||
{
|
||||
int i, j;
|
||||
short seq;
|
||||
hda_nid_t nid;
|
||||
|
||||
|
||||
for (i = 0; i < num_pins; i++) {
|
||||
for (j = i + 1; j < num_pins; j++) {
|
||||
if (sequences[i] > sequences[j]) {
|
||||
|
@ -4123,7 +4130,7 @@ static void sort_pins_by_sequence(hda_nid_t * pins, short * sequences,
|
|||
* is detected, one of speaker of HP pins is assigned as the primary
|
||||
* output, i.e. to line_out_pins[0]. So, line_outs is always positive
|
||||
* if any analog output exists.
|
||||
*
|
||||
*
|
||||
* The analog input pins are assigned to input_pins array.
|
||||
* The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
|
||||
* respectively.
|
||||
|
@ -4186,9 +4193,9 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
|
|||
case AC_JACK_SPEAKER:
|
||||
seq = get_defcfg_sequence(def_conf);
|
||||
assoc = get_defcfg_association(def_conf);
|
||||
if (! assoc)
|
||||
if (!assoc)
|
||||
continue;
|
||||
if (! assoc_speaker)
|
||||
if (!assoc_speaker)
|
||||
assoc_speaker = assoc;
|
||||
else if (assoc_speaker != assoc)
|
||||
continue;
|
||||
|
@ -4286,7 +4293,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
|
|||
cfg->speaker_outs);
|
||||
sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
|
||||
cfg->hp_outs);
|
||||
|
||||
|
||||
/* if we have only one mic, make it AUTO_PIN_MIC */
|
||||
if (!cfg->input_pins[AUTO_PIN_MIC] &&
|
||||
cfg->input_pins[AUTO_PIN_FRONT_MIC]) {
|
||||
|
@ -4436,7 +4443,7 @@ EXPORT_SYMBOL_HDA(snd_hda_resume);
|
|||
/**
|
||||
* snd_array_new - get a new element from the given array
|
||||
* @array: the array object
|
||||
*
|
||||
*
|
||||
* Get a new element from the given array. If it exceeds the
|
||||
* pre-allocated array size, re-allocate the array.
|
||||
*
|
||||
|
|
|
@ -331,6 +331,7 @@ int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
|
|||
return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
|
||||
AC_DIPSIZE_ELD_BUF);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hdmi_get_eld_size);
|
||||
|
||||
int snd_hdmi_get_eld(struct hdmi_eld *eld,
|
||||
struct hda_codec *codec, hda_nid_t nid)
|
||||
|
@ -366,6 +367,7 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld,
|
|||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hdmi_get_eld);
|
||||
|
||||
static void hdmi_show_short_audio_desc(struct cea_sad *a)
|
||||
{
|
||||
|
@ -404,6 +406,7 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
|
|||
}
|
||||
buf[j] = '\0'; /* necessary when j == 0 */
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_print_channel_allocation);
|
||||
|
||||
void snd_hdmi_show_eld(struct hdmi_eld *e)
|
||||
{
|
||||
|
@ -422,6 +425,7 @@ void snd_hdmi_show_eld(struct hdmi_eld *e)
|
|||
for (i = 0; i < e->sad_count; i++)
|
||||
hdmi_show_short_audio_desc(e->sad + i);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hdmi_show_eld);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
|
@ -580,6 +584,7 @@ int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_eld_proc_new);
|
||||
|
||||
void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
|
||||
{
|
||||
|
@ -588,5 +593,6 @@ void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
|
|||
eld->proc_entry = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_eld_proc_free);
|
||||
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
|
|
@ -267,7 +267,8 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
|
|||
#define RIRB_INT_MASK 0x05
|
||||
|
||||
/* STATESTS int mask: S3,SD2,SD1,SD0 */
|
||||
#define AZX_MAX_CODECS 4
|
||||
#define AZX_MAX_CODECS 8
|
||||
#define AZX_DEFAULT_CODECS 4
|
||||
#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1)
|
||||
|
||||
/* SD_CTL bits */
|
||||
|
@ -1367,6 +1368,7 @@ static void azx_bus_reset(struct hda_bus *bus)
|
|||
|
||||
/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
|
||||
static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
|
||||
[AZX_DRIVER_NVIDIA] = 8,
|
||||
[AZX_DRIVER_TERA] = 1,
|
||||
};
|
||||
|
||||
|
@ -1399,7 +1401,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
|
|||
codecs = 0;
|
||||
max_slots = azx_max_codecs[chip->driver_type];
|
||||
if (!max_slots)
|
||||
max_slots = AZX_MAX_CODECS;
|
||||
max_slots = AZX_DEFAULT_CODECS;
|
||||
|
||||
/* First try to probe all given codec slots */
|
||||
for (c = 0; c < max_slots; c++) {
|
||||
|
@ -2263,10 +2265,12 @@ static int azx_dev_free(struct snd_device *device)
|
|||
static struct snd_pci_quirk position_fix_list[] __devinitdata = {
|
||||
SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1028, 0x01f6, "Dell Latitude 131L", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB),
|
||||
SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB),
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -2354,6 +2358,7 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
|
|||
static struct snd_pci_quirk msi_black_list[] __devinitdata = {
|
||||
SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */
|
||||
SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */
|
||||
SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
849
sound/pci/hda/patch_hdmi.c
Normal file
849
sound/pci/hda/patch_hdmi.c
Normal file
|
@ -0,0 +1,849 @@
|
|||
/*
|
||||
*
|
||||
* patch_hdmi.c - routines for HDMI/DisplayPort codecs
|
||||
*
|
||||
* Copyright(c) 2008-2010 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Authors:
|
||||
* Wu Fengguang <wfg@linux.intel.com>
|
||||
*
|
||||
* Maintained by:
|
||||
* Wu Fengguang <wfg@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
struct hdmi_spec {
|
||||
int num_cvts;
|
||||
int num_pins;
|
||||
hda_nid_t cvt[MAX_HDMI_CVTS+1]; /* audio sources */
|
||||
hda_nid_t pin[MAX_HDMI_PINS+1]; /* audio sinks */
|
||||
|
||||
/*
|
||||
* source connection for each pin
|
||||
*/
|
||||
hda_nid_t pin_cvt[MAX_HDMI_PINS+1];
|
||||
|
||||
/*
|
||||
* HDMI sink attached to each pin
|
||||
*/
|
||||
struct hdmi_eld sink_eld[MAX_HDMI_PINS];
|
||||
|
||||
/*
|
||||
* export one pcm per pipe
|
||||
*/
|
||||
struct hda_pcm pcm_rec[MAX_HDMI_CVTS];
|
||||
|
||||
/*
|
||||
* nvhdmi specific
|
||||
*/
|
||||
struct hda_multi_out multiout;
|
||||
unsigned int codec_type;
|
||||
};
|
||||
|
||||
|
||||
struct hdmi_audio_infoframe {
|
||||
u8 type; /* 0x84 */
|
||||
u8 ver; /* 0x01 */
|
||||
u8 len; /* 0x0a */
|
||||
|
||||
u8 checksum; /* PB0 */
|
||||
u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */
|
||||
u8 SS01_SF24;
|
||||
u8 CXT04;
|
||||
u8 CA;
|
||||
u8 LFEPBL01_LSV36_DM_INH7;
|
||||
u8 reserved[5]; /* PB6 - PB10 */
|
||||
};
|
||||
|
||||
/*
|
||||
* 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,
|
||||
};
|
||||
|
||||
struct cea_channel_speaker_allocation {
|
||||
int ca_index;
|
||||
int speakers[8];
|
||||
|
||||
/* derived values, just for convenience */
|
||||
int channels;
|
||||
int spk_mask;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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, 0x34, 0x43, 0xf2, 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_setup_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
|
||||
*/
|
||||
|
||||
static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; nids[i]; i++)
|
||||
if (nids[i] == nid)
|
||||
return i;
|
||||
|
||||
snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
struct hdmi_eld *eld)
|
||||
{
|
||||
if (!snd_hdmi_get_eld(eld, codec, pin_nid))
|
||||
snd_hdmi_show_eld(eld);
|
||||
}
|
||||
|
||||
#ifdef BE_PARANOID
|
||||
static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
int *packet_index, int *byte_index)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_HDMI_DIP_INDEX, 0);
|
||||
|
||||
*packet_index = val >> 5;
|
||||
*byte_index = val & 0x1f;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
int packet_index, int byte_index)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = (packet_index << 5) | (byte_index & 0x1f);
|
||||
|
||||
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
|
||||
}
|
||||
|
||||
static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
unsigned char val)
|
||||
{
|
||||
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
|
||||
}
|
||||
|
||||
static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
/* Unmute */
|
||||
if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
|
||||
snd_hda_codec_write(codec, pin_nid, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
|
||||
/* Enable pin out */
|
||||
snd_hda_codec_write(codec, pin_nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
|
||||
}
|
||||
|
||||
static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
return 1 + snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_CVT_CHAN_COUNT, 0);
|
||||
}
|
||||
|
||||
static void hdmi_set_channel_count(struct hda_codec *codec,
|
||||
hda_nid_t nid, int chs)
|
||||
{
|
||||
if (chs != hdmi_get_channel_count(codec, nid))
|
||||
snd_hda_codec_write(codec, nid, 0,
|
||||
AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Channel mapping routines
|
||||
*/
|
||||
|
||||
/*
|
||||
* Compute derived values in channel_allocations[].
|
||||
*/
|
||||
static void init_channel_allocations(void)
|
||||
{
|
||||
int i, j;
|
||||
struct 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_eld *eld;
|
||||
int i;
|
||||
int spk_mask = 0;
|
||||
int channels = 1 + (ai->CC02_CT47 & 0x7);
|
||||
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
|
||||
|
||||
/*
|
||||
* CA defaults to 0 for basic stereo audio
|
||||
*/
|
||||
if (channels <= 2)
|
||||
return 0;
|
||||
|
||||
i = hda_node_index(spec->pin_cvt, nid);
|
||||
if (i < 0)
|
||||
return 0;
|
||||
eld = &spec->sink_eld[i];
|
||||
|
||||
/*
|
||||
* HDMI sink's ELD info cannot always be retrieved for now, e.g.
|
||||
* in console or for audio devices. Assume the highest speakers
|
||||
* configuration, to _not_ prohibit multi-channel audio playback.
|
||||
*/
|
||||
if (!eld->spk_alloc)
|
||||
eld->spk_alloc = 0xffff;
|
||||
|
||||
/*
|
||||
* 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->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) {
|
||||
ai->CA = channel_allocations[i].ca_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
|
||||
snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n",
|
||||
ai->CA, channels, buf);
|
||||
|
||||
return ai->CA;
|
||||
}
|
||||
|
||||
static void hdmi_debug_channel_mapping(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid)
|
||||
{
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
int i;
|
||||
int slot;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
slot = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_HDMI_CHAN_SLOT, i);
|
||||
printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
|
||||
slot >> 4, slot & 0xf);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void hdmi_setup_channel_mapping(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
int i;
|
||||
int ca = ai->CA;
|
||||
int err;
|
||||
|
||||
if (hdmi_channel_mapping[ca][1] == 0) {
|
||||
for (i = 0; i < channel_allocations[ca].channels; i++)
|
||||
hdmi_channel_mapping[ca][i] = i | (i << 4);
|
||||
for (; i < 8; i++)
|
||||
hdmi_channel_mapping[ca][i] = 0xf | (i << 4);
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
err = snd_hda_codec_write(codec, pin_nid, 0,
|
||||
AC_VERB_SET_HDMI_CHAN_SLOT,
|
||||
hdmi_channel_mapping[ca][i]);
|
||||
if (err) {
|
||||
snd_printdd(KERN_NOTICE
|
||||
"HDMI: channel mapping failed\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hdmi_debug_channel_mapping(codec, pin_nid);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Audio InfoFrame routines
|
||||
*/
|
||||
|
||||
/*
|
||||
* Enable Audio InfoFrame Transmission
|
||||
*/
|
||||
static void hdmi_start_infoframe_trans(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid)
|
||||
{
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
|
||||
AC_DIPXMIT_BEST);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable Audio InfoFrame Transmission
|
||||
*/
|
||||
static void hdmi_stop_infoframe_trans(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid)
|
||||
{
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
|
||||
AC_DIPXMIT_DISABLE);
|
||||
}
|
||||
|
||||
static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
int i;
|
||||
int size;
|
||||
|
||||
size = snd_hdmi_get_eld_size(codec, pin_nid);
|
||||
printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
size = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_HDMI_DIP_SIZE, i);
|
||||
printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
#ifdef BE_PARANOID
|
||||
int i, j;
|
||||
int size;
|
||||
int pi, bi;
|
||||
for (i = 0; i < 8; i++) {
|
||||
size = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_HDMI_DIP_SIZE, i);
|
||||
if (size == 0)
|
||||
continue;
|
||||
|
||||
hdmi_set_dip_index(codec, pin_nid, i, 0x0);
|
||||
for (j = 1; j < 1000; j++) {
|
||||
hdmi_write_dip_byte(codec, pin_nid, 0x0);
|
||||
hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
|
||||
if (pi != i)
|
||||
snd_printd(KERN_INFO "dip index %d: %d != %d\n",
|
||||
bi, pi, i);
|
||||
if (bi == 0) /* byte index wrapped around */
|
||||
break;
|
||||
}
|
||||
snd_printd(KERN_INFO
|
||||
"HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
|
||||
i, size, j);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
u8 *bytes = (u8 *)ai;
|
||||
u8 sum = 0;
|
||||
int i;
|
||||
|
||||
ai->checksum = 0;
|
||||
|
||||
for (i = 0; i < sizeof(*ai); i++)
|
||||
sum += bytes[i];
|
||||
|
||||
ai->checksum = -sum;
|
||||
}
|
||||
|
||||
static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
u8 *bytes = (u8 *)ai;
|
||||
int i;
|
||||
|
||||
hdmi_debug_dip_size(codec, pin_nid);
|
||||
hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
|
||||
|
||||
hdmi_checksum_audio_infoframe(ai);
|
||||
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
for (i = 0; i < sizeof(*ai); i++)
|
||||
hdmi_write_dip_byte(codec, pin_nid, bytes[i]);
|
||||
}
|
||||
|
||||
static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
u8 *bytes = (u8 *)ai;
|
||||
u8 val;
|
||||
int i;
|
||||
|
||||
if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0)
|
||||
!= AC_DIPXMIT_BEST)
|
||||
return false;
|
||||
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
for (i = 0; i < sizeof(*ai); i++) {
|
||||
val = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_HDMI_DIP_DATA, 0);
|
||||
if (val != bytes[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
hda_nid_t pin_nid;
|
||||
int i;
|
||||
struct hdmi_audio_infoframe ai = {
|
||||
.type = 0x84,
|
||||
.ver = 0x01,
|
||||
.len = 0x0a,
|
||||
.CC02_CT47 = substream->runtime->channels - 1,
|
||||
};
|
||||
|
||||
hdmi_setup_channel_allocation(codec, nid, &ai);
|
||||
|
||||
for (i = 0; i < spec->num_pins; i++) {
|
||||
if (spec->pin_cvt[i] != nid)
|
||||
continue;
|
||||
if (!spec->sink_eld[i].monitor_present)
|
||||
continue;
|
||||
|
||||
pin_nid = spec->pin[i];
|
||||
if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) {
|
||||
snd_printdd("hdmi_setup_audio_infoframe: "
|
||||
"cvt=%d pin=%d channels=%d\n",
|
||||
nid, pin_nid,
|
||||
substream->runtime->channels);
|
||||
hdmi_setup_channel_mapping(codec, pin_nid, &ai);
|
||||
hdmi_stop_infoframe_trans(codec, pin_nid);
|
||||
hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
|
||||
hdmi_start_infoframe_trans(codec, pin_nid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Unsolicited events
|
||||
*/
|
||||
|
||||
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
|
||||
int pind = !!(res & AC_UNSOL_RES_PD);
|
||||
int eldv = !!(res & AC_UNSOL_RES_ELDV);
|
||||
int index;
|
||||
|
||||
printk(KERN_INFO
|
||||
"HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
|
||||
tag, pind, eldv);
|
||||
|
||||
index = hda_node_index(spec->pin, tag);
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
spec->sink_eld[index].monitor_present = pind;
|
||||
spec->sink_eld[index].eld_valid = eldv;
|
||||
|
||||
if (pind && eldv) {
|
||||
hdmi_get_show_eld(codec, spec->pin[index],
|
||||
&spec->sink_eld[index]);
|
||||
/* TODO: do real things about ELD */
|
||||
}
|
||||
}
|
||||
|
||||
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
|
||||
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
|
||||
int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
|
||||
int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
|
||||
|
||||
printk(KERN_INFO
|
||||
"HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
|
||||
tag,
|
||||
subtag,
|
||||
cp_state,
|
||||
cp_ready);
|
||||
|
||||
/* TODO */
|
||||
if (cp_state)
|
||||
;
|
||||
if (cp_ready)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
|
||||
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
|
||||
|
||||
if (hda_node_index(spec->pin, tag) < 0) {
|
||||
snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (subtag == 0)
|
||||
hdmi_intrinsic_event(codec, res);
|
||||
else
|
||||
hdmi_non_intrinsic_event(codec, res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callbacks
|
||||
*/
|
||||
|
||||
static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
|
||||
u32 stream_tag, int format)
|
||||
{
|
||||
int tag;
|
||||
int fmt;
|
||||
|
||||
tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4;
|
||||
fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0);
|
||||
|
||||
snd_printdd("hdmi_setup_stream: "
|
||||
"NID=0x%x, %sstream=0x%x, %sformat=0x%x\n",
|
||||
nid,
|
||||
tag == stream_tag ? "" : "new-",
|
||||
stream_tag,
|
||||
fmt == format ? "" : "new-",
|
||||
format);
|
||||
|
||||
if (tag != stream_tag)
|
||||
snd_hda_codec_write(codec, nid, 0,
|
||||
AC_VERB_SET_CHANNEL_STREAMID,
|
||||
stream_tag << 4);
|
||||
if (fmt != format)
|
||||
snd_hda_codec_write(codec, nid, 0,
|
||||
AC_VERB_SET_STREAM_FORMAT, format);
|
||||
}
|
||||
|
||||
/*
|
||||
* HDA/HDMI auto parsing
|
||||
*/
|
||||
|
||||
static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
|
||||
int conn_len, curr;
|
||||
int index;
|
||||
|
||||
if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
|
||||
snd_printk(KERN_WARNING
|
||||
"HDMI: pin %d wcaps %#x "
|
||||
"does not support connection list\n",
|
||||
pin_nid, get_wcaps(codec, pin_nid));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
|
||||
HDA_MAX_CONNECTIONS);
|
||||
if (conn_len > 1)
|
||||
curr = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_CONNECT_SEL, 0);
|
||||
else
|
||||
curr = 0;
|
||||
|
||||
index = hda_node_index(spec->pin, pin_nid);
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
spec->pin_cvt[index] = conn_list[curr];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
struct hdmi_eld *eld)
|
||||
{
|
||||
int present = snd_hda_pin_sense(codec, pin_nid);
|
||||
|
||||
eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
|
||||
eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
|
||||
|
||||
if (present & AC_PINSENSE_ELDV)
|
||||
hdmi_get_show_eld(codec, pin_nid, eld);
|
||||
}
|
||||
|
||||
static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
||||
if (spec->num_pins >= MAX_HDMI_PINS) {
|
||||
snd_printk(KERN_WARNING
|
||||
"HDMI: no space for pin %d\n", pin_nid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
|
||||
|
||||
spec->pin[spec->num_pins] = pin_nid;
|
||||
spec->num_pins++;
|
||||
|
||||
/*
|
||||
* It is assumed that converter nodes come first in the node list and
|
||||
* hence have been registered and usable now.
|
||||
*/
|
||||
return hdmi_read_pin_conn(codec, pin_nid);
|
||||
}
|
||||
|
||||
static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
||||
if (spec->num_cvts >= MAX_HDMI_CVTS) {
|
||||
snd_printk(KERN_WARNING
|
||||
"HDMI: no space for converter %d\n", nid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spec->cvt[spec->num_cvts] = nid;
|
||||
spec->num_cvts++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_parse_codec(struct hda_codec *codec)
|
||||
{
|
||||
hda_nid_t nid;
|
||||
int i, nodes;
|
||||
|
||||
nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
|
||||
if (!nid || nodes < 0) {
|
||||
snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < nodes; i++, nid++) {
|
||||
unsigned int caps;
|
||||
unsigned int type;
|
||||
|
||||
caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
|
||||
type = get_wcaps_type(caps);
|
||||
|
||||
if (!(caps & AC_WCAP_DIGITAL))
|
||||
continue;
|
||||
|
||||
switch (type) {
|
||||
case AC_WID_AUD_OUT:
|
||||
if (hdmi_add_cvt(codec, nid) < 0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case AC_WID_PIN:
|
||||
caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
|
||||
if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
|
||||
continue;
|
||||
if (hdmi_add_pin(codec, nid) < 0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event
|
||||
* can be lost and presence sense verb will become inaccurate if the
|
||||
* HDA link is powered off at hot plug or hw initialization time.
|
||||
*/
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) &
|
||||
AC_PWRST_EPSS))
|
||||
codec->bus->power_keep_link_on = 1;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -40,814 +40,19 @@
|
|||
*
|
||||
* The HDA correspondence of pipes/ports are converter/pin nodes.
|
||||
*/
|
||||
#define INTEL_HDMI_CVTS 2
|
||||
#define INTEL_HDMI_PINS 3
|
||||
#define MAX_HDMI_CVTS 2
|
||||
#define MAX_HDMI_PINS 3
|
||||
|
||||
static char *intel_hdmi_pcm_names[INTEL_HDMI_CVTS] = {
|
||||
#include "patch_hdmi.c"
|
||||
|
||||
static char *intel_hdmi_pcm_names[MAX_HDMI_CVTS] = {
|
||||
"INTEL HDMI 0",
|
||||
"INTEL HDMI 1",
|
||||
};
|
||||
|
||||
struct intel_hdmi_spec {
|
||||
int num_cvts;
|
||||
int num_pins;
|
||||
hda_nid_t cvt[INTEL_HDMI_CVTS+1]; /* audio sources */
|
||||
hda_nid_t pin[INTEL_HDMI_PINS+1]; /* audio sinks */
|
||||
|
||||
/*
|
||||
* source connection for each pin
|
||||
*/
|
||||
hda_nid_t pin_cvt[INTEL_HDMI_PINS+1];
|
||||
|
||||
/*
|
||||
* HDMI sink attached to each pin
|
||||
*/
|
||||
struct hdmi_eld sink_eld[INTEL_HDMI_PINS];
|
||||
|
||||
/*
|
||||
* export one pcm per pipe
|
||||
*/
|
||||
struct hda_pcm pcm_rec[INTEL_HDMI_CVTS];
|
||||
};
|
||||
|
||||
struct hdmi_audio_infoframe {
|
||||
u8 type; /* 0x84 */
|
||||
u8 ver; /* 0x01 */
|
||||
u8 len; /* 0x0a */
|
||||
|
||||
u8 checksum; /* PB0 */
|
||||
u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */
|
||||
u8 SS01_SF24;
|
||||
u8 CXT04;
|
||||
u8 CA;
|
||||
u8 LFEPBL01_LSV36_DM_INH7;
|
||||
u8 reserved[5]; /* PB6 - PB10 */
|
||||
};
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* HDMI callbacks
|
||||
*/
|
||||
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,
|
||||
};
|
||||
|
||||
struct cea_channel_speaker_allocation {
|
||||
int ca_index;
|
||||
int speakers[8];
|
||||
|
||||
/* derived values, just for convenience */
|
||||
int channels;
|
||||
int spk_mask;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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, 0x34, 0x43, 0xf2, 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_setup_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 } },
|
||||
};
|
||||
|
||||
/*
|
||||
* HDA/HDMI auto parsing
|
||||
*/
|
||||
|
||||
static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; nids[i]; i++)
|
||||
if (nids[i] == nid)
|
||||
return i;
|
||||
|
||||
snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
|
||||
int conn_len, curr;
|
||||
int index;
|
||||
|
||||
if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
|
||||
snd_printk(KERN_WARNING
|
||||
"HDMI: pin %d wcaps %#x "
|
||||
"does not support connection list\n",
|
||||
pin_nid, get_wcaps(codec, pin_nid));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
|
||||
HDA_MAX_CONNECTIONS);
|
||||
if (conn_len > 1)
|
||||
curr = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_CONNECT_SEL, 0);
|
||||
else
|
||||
curr = 0;
|
||||
|
||||
index = hda_node_index(spec->pin, pin_nid);
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
spec->pin_cvt[index] = conn_list[curr];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
struct hdmi_eld *eld)
|
||||
{
|
||||
if (!snd_hdmi_get_eld(eld, codec, pin_nid))
|
||||
snd_hdmi_show_eld(eld);
|
||||
}
|
||||
|
||||
static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
struct hdmi_eld *eld)
|
||||
{
|
||||
int present = snd_hda_pin_sense(codec, pin_nid);
|
||||
|
||||
eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
|
||||
eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
|
||||
|
||||
if (present & AC_PINSENSE_ELDV)
|
||||
hdmi_get_show_eld(codec, pin_nid, eld);
|
||||
}
|
||||
|
||||
static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
|
||||
if (spec->num_pins >= INTEL_HDMI_PINS) {
|
||||
snd_printk(KERN_WARNING
|
||||
"HDMI: no space for pin %d \n", pin_nid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
|
||||
|
||||
spec->pin[spec->num_pins] = pin_nid;
|
||||
spec->num_pins++;
|
||||
|
||||
/*
|
||||
* It is assumed that converter nodes come first in the node list and
|
||||
* hence have been registered and usable now.
|
||||
*/
|
||||
return intel_hdmi_read_pin_conn(codec, pin_nid);
|
||||
}
|
||||
|
||||
static int intel_hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
|
||||
if (spec->num_cvts >= INTEL_HDMI_CVTS) {
|
||||
snd_printk(KERN_WARNING
|
||||
"HDMI: no space for converter %d \n", nid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spec->cvt[spec->num_cvts] = nid;
|
||||
spec->num_cvts++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_hdmi_parse_codec(struct hda_codec *codec)
|
||||
{
|
||||
hda_nid_t nid;
|
||||
int i, nodes;
|
||||
|
||||
nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
|
||||
if (!nid || nodes < 0) {
|
||||
snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < nodes; i++, nid++) {
|
||||
unsigned int caps;
|
||||
unsigned int type;
|
||||
|
||||
caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
|
||||
type = get_wcaps_type(caps);
|
||||
|
||||
if (!(caps & AC_WCAP_DIGITAL))
|
||||
continue;
|
||||
|
||||
switch (type) {
|
||||
case AC_WID_AUD_OUT:
|
||||
if (intel_hdmi_add_cvt(codec, nid) < 0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case AC_WID_PIN:
|
||||
caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
|
||||
if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
|
||||
continue;
|
||||
if (intel_hdmi_add_pin(codec, nid) < 0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event
|
||||
* can be lost and presence sense verb will become inaccurate if the
|
||||
* HDA link is powered off at hot plug or hw initialization time.
|
||||
*/
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) &
|
||||
AC_PWRST_EPSS))
|
||||
codec->bus->power_keep_link_on = 1;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* HDMI routines
|
||||
*/
|
||||
|
||||
#ifdef BE_PARANOID
|
||||
static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
int *packet_index, int *byte_index)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_HDMI_DIP_INDEX, 0);
|
||||
|
||||
*packet_index = val >> 5;
|
||||
*byte_index = val & 0x1f;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
int packet_index, int byte_index)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = (packet_index << 5) | (byte_index & 0x1f);
|
||||
|
||||
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
|
||||
}
|
||||
|
||||
static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
unsigned char val)
|
||||
{
|
||||
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
|
||||
}
|
||||
|
||||
static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
/* Unmute */
|
||||
if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
|
||||
snd_hda_codec_write(codec, pin_nid, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
|
||||
/* Enable pin out */
|
||||
snd_hda_codec_write(codec, pin_nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable Audio InfoFrame Transmission
|
||||
*/
|
||||
static void hdmi_start_infoframe_trans(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid)
|
||||
{
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
|
||||
AC_DIPXMIT_BEST);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable Audio InfoFrame Transmission
|
||||
*/
|
||||
static void hdmi_stop_infoframe_trans(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid)
|
||||
{
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
|
||||
AC_DIPXMIT_DISABLE);
|
||||
}
|
||||
|
||||
static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
return 1 + snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_CVT_CHAN_COUNT, 0);
|
||||
}
|
||||
|
||||
static void hdmi_set_channel_count(struct hda_codec *codec,
|
||||
hda_nid_t nid, int chs)
|
||||
{
|
||||
if (chs != hdmi_get_channel_count(codec, nid))
|
||||
snd_hda_codec_write(codec, nid, 0,
|
||||
AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
|
||||
}
|
||||
|
||||
static void hdmi_debug_channel_mapping(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid)
|
||||
{
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
int i;
|
||||
int slot;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
slot = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_HDMI_CHAN_SLOT, i);
|
||||
printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
|
||||
slot >> 4, slot & 0xf);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Audio InfoFrame routines
|
||||
*/
|
||||
|
||||
static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
int i;
|
||||
int size;
|
||||
|
||||
size = snd_hdmi_get_eld_size(codec, pin_nid);
|
||||
printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
size = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_HDMI_DIP_SIZE, i);
|
||||
printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
#ifdef BE_PARANOID
|
||||
int i, j;
|
||||
int size;
|
||||
int pi, bi;
|
||||
for (i = 0; i < 8; i++) {
|
||||
size = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_HDMI_DIP_SIZE, i);
|
||||
if (size == 0)
|
||||
continue;
|
||||
|
||||
hdmi_set_dip_index(codec, pin_nid, i, 0x0);
|
||||
for (j = 1; j < 1000; j++) {
|
||||
hdmi_write_dip_byte(codec, pin_nid, 0x0);
|
||||
hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
|
||||
if (pi != i)
|
||||
snd_printd(KERN_INFO "dip index %d: %d != %d\n",
|
||||
bi, pi, i);
|
||||
if (bi == 0) /* byte index wrapped around */
|
||||
break;
|
||||
}
|
||||
snd_printd(KERN_INFO
|
||||
"HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
|
||||
i, size, j);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
u8 *bytes = (u8 *)ai;
|
||||
u8 sum = 0;
|
||||
int i;
|
||||
|
||||
ai->checksum = 0;
|
||||
|
||||
for (i = 0; i < sizeof(*ai); i++)
|
||||
sum += bytes[i];
|
||||
|
||||
ai->checksum = - sum;
|
||||
}
|
||||
|
||||
static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
u8 *bytes = (u8 *)ai;
|
||||
int i;
|
||||
|
||||
hdmi_debug_dip_size(codec, pin_nid);
|
||||
hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
|
||||
|
||||
hdmi_checksum_audio_infoframe(ai);
|
||||
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
for (i = 0; i < sizeof(*ai); i++)
|
||||
hdmi_write_dip_byte(codec, pin_nid, bytes[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_eld *eld;
|
||||
int i;
|
||||
int spk_mask = 0;
|
||||
int channels = 1 + (ai->CC02_CT47 & 0x7);
|
||||
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
|
||||
|
||||
/*
|
||||
* CA defaults to 0 for basic stereo audio
|
||||
*/
|
||||
if (channels <= 2)
|
||||
return 0;
|
||||
|
||||
i = hda_node_index(spec->pin_cvt, nid);
|
||||
if (i < 0)
|
||||
return 0;
|
||||
eld = &spec->sink_eld[i];
|
||||
|
||||
/*
|
||||
* HDMI sink's ELD info cannot always be retrieved for now, e.g.
|
||||
* in console or for audio devices. Assume the highest speakers
|
||||
* configuration, to _not_ prohibit multi-channel audio playback.
|
||||
*/
|
||||
if (!eld->spk_alloc)
|
||||
eld->spk_alloc = 0xffff;
|
||||
|
||||
/*
|
||||
* 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->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) {
|
||||
ai->CA = channel_allocations[i].ca_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
|
||||
snd_printdd(KERN_INFO
|
||||
"HDMI: select CA 0x%x for %d-channel allocation: %s\n",
|
||||
ai->CA, channels, buf);
|
||||
|
||||
return ai->CA;
|
||||
}
|
||||
|
||||
static void hdmi_setup_channel_mapping(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
int i;
|
||||
int ca = ai->CA;
|
||||
int err;
|
||||
|
||||
if (hdmi_channel_mapping[ca][1] == 0) {
|
||||
for (i = 0; i < channel_allocations[ca].channels; i++)
|
||||
hdmi_channel_mapping[ca][i] = i | (i << 4);
|
||||
for (; i < 8; i++)
|
||||
hdmi_channel_mapping[ca][i] = 0xf | (i << 4);
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
err = snd_hda_codec_write(codec, pin_nid, 0,
|
||||
AC_VERB_SET_HDMI_CHAN_SLOT,
|
||||
hdmi_channel_mapping[ca][i]);
|
||||
if (err) {
|
||||
snd_printdd(KERN_INFO "HDMI: channel mapping failed\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hdmi_debug_channel_mapping(codec, pin_nid);
|
||||
}
|
||||
|
||||
static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
u8 *bytes = (u8 *)ai;
|
||||
u8 val;
|
||||
int i;
|
||||
|
||||
if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0)
|
||||
!= AC_DIPXMIT_BEST)
|
||||
return false;
|
||||
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
for (i = 0; i < sizeof(*ai); i++) {
|
||||
val = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_HDMI_DIP_DATA, 0);
|
||||
if (val != bytes[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
hda_nid_t pin_nid;
|
||||
int i;
|
||||
struct hdmi_audio_infoframe ai = {
|
||||
.type = 0x84,
|
||||
.ver = 0x01,
|
||||
.len = 0x0a,
|
||||
.CC02_CT47 = substream->runtime->channels - 1,
|
||||
};
|
||||
|
||||
hdmi_setup_channel_allocation(codec, nid, &ai);
|
||||
|
||||
for (i = 0; i < spec->num_pins; i++) {
|
||||
if (spec->pin_cvt[i] != nid)
|
||||
continue;
|
||||
if (!spec->sink_eld[i].monitor_present)
|
||||
continue;
|
||||
|
||||
pin_nid = spec->pin[i];
|
||||
if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) {
|
||||
hdmi_setup_channel_mapping(codec, pin_nid, &ai);
|
||||
hdmi_stop_infoframe_trans(codec, pin_nid);
|
||||
hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
|
||||
hdmi_start_infoframe_trans(codec, pin_nid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Unsolicited events
|
||||
*/
|
||||
|
||||
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
|
||||
int pind = !!(res & AC_UNSOL_RES_PD);
|
||||
int eldv = !!(res & AC_UNSOL_RES_ELDV);
|
||||
int index;
|
||||
|
||||
printk(KERN_INFO
|
||||
"HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
|
||||
tag, pind, eldv);
|
||||
|
||||
index = hda_node_index(spec->pin, tag);
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
spec->sink_eld[index].monitor_present = pind;
|
||||
spec->sink_eld[index].eld_valid = eldv;
|
||||
|
||||
if (pind && eldv) {
|
||||
hdmi_get_show_eld(codec, spec->pin[index], &spec->sink_eld[index]);
|
||||
/* TODO: do real things about ELD */
|
||||
}
|
||||
}
|
||||
|
||||
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
|
||||
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
|
||||
int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
|
||||
int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
|
||||
|
||||
printk(KERN_INFO
|
||||
"HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
|
||||
tag,
|
||||
subtag,
|
||||
cp_state,
|
||||
cp_ready);
|
||||
|
||||
/* TODO */
|
||||
if (cp_state)
|
||||
;
|
||||
if (cp_ready)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
|
||||
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
|
||||
|
||||
if (hda_node_index(spec->pin, tag) < 0) {
|
||||
snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (subtag == 0)
|
||||
hdmi_intrinsic_event(codec, res);
|
||||
else
|
||||
hdmi_non_intrinsic_event(codec, res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callbacks
|
||||
*/
|
||||
|
||||
static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
|
||||
u32 stream_tag, int format)
|
||||
{
|
||||
int tag;
|
||||
int fmt;
|
||||
|
||||
tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4;
|
||||
fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0);
|
||||
|
||||
snd_printdd("hdmi_setup_stream: "
|
||||
"NID=0x%x, %sstream=0x%x, %sformat=0x%x\n",
|
||||
nid,
|
||||
tag == stream_tag ? "" : "new-",
|
||||
stream_tag,
|
||||
fmt == format ? "" : "new-",
|
||||
format);
|
||||
|
||||
if (tag != stream_tag)
|
||||
snd_hda_codec_write(codec, nid, 0,
|
||||
AC_VERB_SET_CHANNEL_STREAMID, stream_tag << 4);
|
||||
if (fmt != format)
|
||||
snd_hda_codec_write(codec, nid, 0,
|
||||
AC_VERB_SET_STREAM_FORMAT, format);
|
||||
}
|
||||
|
||||
static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
|
@ -882,7 +87,7 @@ static struct hda_pcm_stream intel_hdmi_pcm_playback = {
|
|||
|
||||
static int intel_hdmi_build_pcms(struct hda_codec *codec)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hda_pcm *info = spec->pcm_rec;
|
||||
int i;
|
||||
|
||||
|
@ -908,7 +113,7 @@ static int intel_hdmi_build_pcms(struct hda_codec *codec)
|
|||
|
||||
static int intel_hdmi_build_controls(struct hda_codec *codec)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
|
@ -923,7 +128,7 @@ static int intel_hdmi_build_controls(struct hda_codec *codec)
|
|||
|
||||
static int intel_hdmi_init(struct hda_codec *codec)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int i;
|
||||
|
||||
for (i = 0; spec->pin[i]; i++) {
|
||||
|
@ -937,7 +142,7 @@ static int intel_hdmi_init(struct hda_codec *codec)
|
|||
|
||||
static void intel_hdmi_free(struct hda_codec *codec)
|
||||
{
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < spec->num_pins; i++)
|
||||
|
@ -951,12 +156,12 @@ static struct hda_codec_ops intel_hdmi_patch_ops = {
|
|||
.free = intel_hdmi_free,
|
||||
.build_pcms = intel_hdmi_build_pcms,
|
||||
.build_controls = intel_hdmi_build_controls,
|
||||
.unsol_event = intel_hdmi_unsol_event,
|
||||
.unsol_event = hdmi_unsol_event,
|
||||
};
|
||||
|
||||
static int patch_intel_hdmi(struct hda_codec *codec)
|
||||
{
|
||||
struct intel_hdmi_spec *spec;
|
||||
struct hdmi_spec *spec;
|
||||
int i;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
|
@ -964,7 +169,7 @@ static int patch_intel_hdmi(struct hda_codec *codec)
|
|||
return -ENOMEM;
|
||||
|
||||
codec->spec = spec;
|
||||
if (intel_hdmi_parse_codec(codec) < 0) {
|
||||
if (hdmi_parse_codec(codec) < 0) {
|
||||
codec->spec = NULL;
|
||||
kfree(spec);
|
||||
return -EINVAL;
|
||||
|
|
|
@ -29,13 +29,23 @@
|
|||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
|
||||
#define MAX_HDMI_CVTS 1
|
||||
#define MAX_HDMI_PINS 1
|
||||
|
||||
#include "patch_hdmi.c"
|
||||
|
||||
static char *nvhdmi_pcm_names[MAX_HDMI_CVTS] = {
|
||||
"NVIDIA HDMI",
|
||||
};
|
||||
|
||||
/* define below to restrict the supported rates and formats */
|
||||
/* #define LIMITED_RATE_FMT_SUPPORT */
|
||||
|
||||
struct nvhdmi_spec {
|
||||
struct hda_multi_out multiout;
|
||||
|
||||
struct hda_pcm pcm_rec;
|
||||
enum HDACodec {
|
||||
HDA_CODEC_NVIDIA_MCP7X,
|
||||
HDA_CODEC_NVIDIA_MCP89,
|
||||
HDA_CODEC_NVIDIA_GT21X,
|
||||
HDA_CODEC_INVALID
|
||||
};
|
||||
|
||||
#define Nv_VERB_SET_Channel_Allocation 0xF79
|
||||
|
@ -43,15 +53,18 @@ struct nvhdmi_spec {
|
|||
#define Nv_VERB_SET_Audio_Protection_On 0xF98
|
||||
#define Nv_VERB_SET_Audio_Protection_Off 0xF99
|
||||
|
||||
#define Nv_Master_Convert_nid 0x04
|
||||
#define Nv_Master_Pin_nid 0x05
|
||||
#define nvhdmi_master_con_nid_7x 0x04
|
||||
#define nvhdmi_master_pin_nid_7x 0x05
|
||||
|
||||
static hda_nid_t nvhdmi_convert_nids[4] = {
|
||||
#define nvhdmi_master_con_nid_89 0x04
|
||||
#define nvhdmi_master_pin_nid_89 0x05
|
||||
|
||||
static hda_nid_t nvhdmi_con_nids_7x[4] = {
|
||||
/*front, rear, clfe, rear_surr */
|
||||
0x6, 0x8, 0xa, 0xc,
|
||||
};
|
||||
|
||||
static struct hda_verb nvhdmi_basic_init[] = {
|
||||
static struct hda_verb nvhdmi_basic_init_7x[] = {
|
||||
/* set audio protect on */
|
||||
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
|
||||
/* enable digital output on pin widget */
|
||||
|
@ -84,22 +97,60 @@ static struct hda_verb nvhdmi_basic_init[] = {
|
|||
*/
|
||||
static int nvhdmi_build_controls(struct hda_codec *codec)
|
||||
{
|
||||
struct nvhdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
|
||||
|| (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
|
||||
for (i = 0; i < codec->num_pcms; i++) {
|
||||
err = snd_hda_create_spdif_out_ctls(codec,
|
||||
spec->cvt[i]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
err = snd_hda_create_spdif_out_ctls(codec,
|
||||
spec->multiout.dig_out_nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvhdmi_init(struct hda_codec *codec)
|
||||
{
|
||||
snd_hda_sequence_write(codec, nvhdmi_basic_init);
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int i;
|
||||
if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
|
||||
|| (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
|
||||
for (i = 0; spec->pin[i]; i++) {
|
||||
hdmi_enable_output(codec, spec->pin[i]);
|
||||
snd_hda_codec_write(codec, spec->pin[i], 0,
|
||||
AC_VERB_SET_UNSOLICITED_ENABLE,
|
||||
AC_USRSP_EN | spec->pin[i]);
|
||||
}
|
||||
} else {
|
||||
snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nvhdmi_free(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int i;
|
||||
|
||||
if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
|
||||
|| (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
|
||||
for (i = 0; i < spec->num_pins; i++)
|
||||
snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
|
||||
}
|
||||
|
||||
kfree(spec);
|
||||
}
|
||||
|
||||
/*
|
||||
* Digital out
|
||||
*/
|
||||
|
@ -107,25 +158,25 @@ static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
|||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct nvhdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
|
||||
}
|
||||
|
||||
static int nvhdmi_dig_playback_pcm_close_8ch(struct hda_pcm_stream *hinfo,
|
||||
static int nvhdmi_dig_playback_pcm_close_8ch_7x(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct nvhdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int i;
|
||||
|
||||
snd_hda_codec_write(codec, Nv_Master_Convert_nid,
|
||||
snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
|
||||
0, AC_VERB_SET_CHANNEL_STREAMID, 0);
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* set the stream id */
|
||||
snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
|
||||
snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
|
||||
AC_VERB_SET_CHANNEL_STREAMID, 0);
|
||||
/* set the stream format */
|
||||
snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
|
||||
snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
|
||||
AC_VERB_SET_STREAM_FORMAT, 0);
|
||||
}
|
||||
|
||||
|
@ -136,10 +187,25 @@ static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo,
|
|||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct nvhdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
|
||||
}
|
||||
|
||||
static int nvhdmi_dig_playback_pcm_prepare_8ch_89(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
unsigned int stream_tag,
|
||||
unsigned int format,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
hdmi_set_channel_count(codec, hinfo->nid,
|
||||
substream->runtime->channels);
|
||||
|
||||
hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
|
||||
|
||||
hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
unsigned int stream_tag,
|
||||
|
@ -181,29 +247,29 @@ static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
|
|||
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
|
||||
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
|
||||
snd_hda_codec_write(codec,
|
||||
Nv_Master_Convert_nid,
|
||||
nvhdmi_master_con_nid_7x,
|
||||
0,
|
||||
AC_VERB_SET_DIGI_CONVERT_1,
|
||||
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
|
||||
|
||||
/* set the stream id */
|
||||
snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
|
||||
snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
|
||||
AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
|
||||
|
||||
/* set the stream format */
|
||||
snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
|
||||
snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
|
||||
AC_VERB_SET_STREAM_FORMAT, format);
|
||||
|
||||
/* turn on again (if needed) */
|
||||
/* enable and set the channel status audio/data flag */
|
||||
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
|
||||
snd_hda_codec_write(codec,
|
||||
Nv_Master_Convert_nid,
|
||||
nvhdmi_master_con_nid_7x,
|
||||
0,
|
||||
AC_VERB_SET_DIGI_CONVERT_1,
|
||||
codec->spdif_ctls & 0xff);
|
||||
snd_hda_codec_write(codec,
|
||||
Nv_Master_Convert_nid,
|
||||
nvhdmi_master_con_nid_7x,
|
||||
0,
|
||||
AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
|
||||
}
|
||||
|
@ -220,19 +286,19 @@ static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
|
|||
if (codec->spdif_status_reset &&
|
||||
(codec->spdif_ctls & AC_DIG1_ENABLE))
|
||||
snd_hda_codec_write(codec,
|
||||
nvhdmi_convert_nids[i],
|
||||
nvhdmi_con_nids_7x[i],
|
||||
0,
|
||||
AC_VERB_SET_DIGI_CONVERT_1,
|
||||
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
|
||||
/* set the stream id */
|
||||
snd_hda_codec_write(codec,
|
||||
nvhdmi_convert_nids[i],
|
||||
nvhdmi_con_nids_7x[i],
|
||||
0,
|
||||
AC_VERB_SET_CHANNEL_STREAMID,
|
||||
(stream_tag << 4) | channel_id);
|
||||
/* set the stream format */
|
||||
snd_hda_codec_write(codec,
|
||||
nvhdmi_convert_nids[i],
|
||||
nvhdmi_con_nids_7x[i],
|
||||
0,
|
||||
AC_VERB_SET_STREAM_FORMAT,
|
||||
format);
|
||||
|
@ -241,12 +307,12 @@ static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
|
|||
if (codec->spdif_status_reset &&
|
||||
(codec->spdif_ctls & AC_DIG1_ENABLE)) {
|
||||
snd_hda_codec_write(codec,
|
||||
nvhdmi_convert_nids[i],
|
||||
nvhdmi_con_nids_7x[i],
|
||||
0,
|
||||
AC_VERB_SET_DIGI_CONVERT_1,
|
||||
codec->spdif_ctls & 0xff);
|
||||
snd_hda_codec_write(codec,
|
||||
nvhdmi_convert_nids[i],
|
||||
nvhdmi_con_nids_7x[i],
|
||||
0,
|
||||
AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
|
||||
}
|
||||
|
@ -261,28 +327,47 @@ static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int nvhdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
unsigned int stream_tag,
|
||||
unsigned int format,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct nvhdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
|
||||
format, substream);
|
||||
}
|
||||
|
||||
static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch = {
|
||||
static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_89 = {
|
||||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.rates = SUPPORTED_RATES,
|
||||
.maxbps = SUPPORTED_MAXBPS,
|
||||
.formats = SUPPORTED_FORMATS,
|
||||
.ops = {
|
||||
.prepare = nvhdmi_dig_playback_pcm_prepare_8ch_89,
|
||||
.cleanup = nvhdmi_playback_pcm_cleanup,
|
||||
},
|
||||
};
|
||||
|
||||
static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_7x = {
|
||||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.channels_max = 8,
|
||||
.nid = Nv_Master_Convert_nid,
|
||||
.nid = nvhdmi_master_con_nid_7x,
|
||||
.rates = SUPPORTED_RATES,
|
||||
.maxbps = SUPPORTED_MAXBPS,
|
||||
.formats = SUPPORTED_FORMATS,
|
||||
.ops = {
|
||||
.open = nvhdmi_dig_playback_pcm_open,
|
||||
.close = nvhdmi_dig_playback_pcm_close_8ch,
|
||||
.close = nvhdmi_dig_playback_pcm_close_8ch_7x,
|
||||
.prepare = nvhdmi_dig_playback_pcm_prepare_8ch
|
||||
},
|
||||
};
|
||||
|
@ -291,7 +376,7 @@ static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
|
|||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.nid = Nv_Master_Convert_nid,
|
||||
.nid = nvhdmi_master_con_nid_7x,
|
||||
.rates = SUPPORTED_RATES,
|
||||
.maxbps = SUPPORTED_MAXBPS,
|
||||
.formats = SUPPORTED_FORMATS,
|
||||
|
@ -302,10 +387,36 @@ static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
|
|||
},
|
||||
};
|
||||
|
||||
static int nvhdmi_build_pcms_8ch(struct hda_codec *codec)
|
||||
static int nvhdmi_build_pcms_8ch_89(struct hda_codec *codec)
|
||||
{
|
||||
struct nvhdmi_spec *spec = codec->spec;
|
||||
struct hda_pcm *info = &spec->pcm_rec;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hda_pcm *info = spec->pcm_rec;
|
||||
int i;
|
||||
|
||||
codec->num_pcms = spec->num_cvts;
|
||||
codec->pcm_info = info;
|
||||
|
||||
for (i = 0; i < codec->num_pcms; i++, info++) {
|
||||
unsigned int chans;
|
||||
|
||||
chans = get_wcaps(codec, spec->cvt[i]);
|
||||
chans = get_wcaps_channels(chans);
|
||||
|
||||
info->name = nvhdmi_pcm_names[i];
|
||||
info->pcm_type = HDA_PCM_TYPE_HDMI;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK]
|
||||
= nvhdmi_pcm_digital_playback_8ch_89;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvhdmi_build_pcms_8ch_7x(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hda_pcm *info = spec->pcm_rec;
|
||||
|
||||
codec->num_pcms = 1;
|
||||
codec->pcm_info = info;
|
||||
|
@ -313,15 +424,15 @@ static int nvhdmi_build_pcms_8ch(struct hda_codec *codec)
|
|||
info->name = "NVIDIA HDMI";
|
||||
info->pcm_type = HDA_PCM_TYPE_HDMI;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK]
|
||||
= nvhdmi_pcm_digital_playback_8ch;
|
||||
= nvhdmi_pcm_digital_playback_8ch_7x;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
|
||||
{
|
||||
struct nvhdmi_spec *spec = codec->spec;
|
||||
struct hda_pcm *info = &spec->pcm_rec;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hda_pcm *info = spec->pcm_rec;
|
||||
|
||||
codec->num_pcms = 1;
|
||||
codec->pcm_info = info;
|
||||
|
@ -334,14 +445,17 @@ static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void nvhdmi_free(struct hda_codec *codec)
|
||||
{
|
||||
kfree(codec->spec);
|
||||
}
|
||||
|
||||
static struct hda_codec_ops nvhdmi_patch_ops_8ch = {
|
||||
static struct hda_codec_ops nvhdmi_patch_ops_8ch_89 = {
|
||||
.build_controls = nvhdmi_build_controls,
|
||||
.build_pcms = nvhdmi_build_pcms_8ch,
|
||||
.build_pcms = nvhdmi_build_pcms_8ch_89,
|
||||
.init = nvhdmi_init,
|
||||
.free = nvhdmi_free,
|
||||
.unsol_event = hdmi_unsol_event,
|
||||
};
|
||||
|
||||
static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
|
||||
.build_controls = nvhdmi_build_controls,
|
||||
.build_pcms = nvhdmi_build_pcms_8ch_7x,
|
||||
.init = nvhdmi_init,
|
||||
.free = nvhdmi_free,
|
||||
};
|
||||
|
@ -353,9 +467,36 @@ static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
|
|||
.free = nvhdmi_free,
|
||||
};
|
||||
|
||||
static int patch_nvhdmi_8ch(struct hda_codec *codec)
|
||||
static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
|
||||
{
|
||||
struct nvhdmi_spec *spec;
|
||||
struct hdmi_spec *spec;
|
||||
int i;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (spec == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
codec->spec = spec;
|
||||
spec->codec_type = HDA_CODEC_NVIDIA_MCP89;
|
||||
|
||||
if (hdmi_parse_codec(codec) < 0) {
|
||||
codec->spec = NULL;
|
||||
kfree(spec);
|
||||
return -EINVAL;
|
||||
}
|
||||
codec->patch_ops = nvhdmi_patch_ops_8ch_89;
|
||||
|
||||
for (i = 0; i < spec->num_pins; i++)
|
||||
snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
|
||||
|
||||
init_channel_allocations();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (spec == NULL)
|
||||
|
@ -365,16 +506,17 @@ static int patch_nvhdmi_8ch(struct hda_codec *codec)
|
|||
|
||||
spec->multiout.num_dacs = 0; /* no analog */
|
||||
spec->multiout.max_channels = 8;
|
||||
spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
|
||||
spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
|
||||
spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
|
||||
|
||||
codec->patch_ops = nvhdmi_patch_ops_8ch;
|
||||
codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int patch_nvhdmi_2ch(struct hda_codec *codec)
|
||||
{
|
||||
struct nvhdmi_spec *spec;
|
||||
struct hdmi_spec *spec;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (spec == NULL)
|
||||
|
@ -384,7 +526,8 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec)
|
|||
|
||||
spec->multiout.num_dacs = 0; /* no analog */
|
||||
spec->multiout.max_channels = 2;
|
||||
spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
|
||||
spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
|
||||
spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
|
||||
|
||||
codec->patch_ops = nvhdmi_patch_ops_2ch;
|
||||
|
||||
|
@ -395,13 +538,24 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec)
|
|||
* patch entries
|
||||
*/
|
||||
static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
|
||||
{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
|
||||
{ .id = 0x10de0003, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
|
||||
{ .id = 0x10de0005, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
|
||||
{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
|
||||
{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
|
||||
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
|
||||
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
|
||||
{ .id = 0x10de0002, .name = "MCP77/78 HDMI",
|
||||
.patch = patch_nvhdmi_8ch_7x },
|
||||
{ .id = 0x10de0003, .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 = 0x10de0007, .name = "MCP79/7A HDMI",
|
||||
.patch = patch_nvhdmi_8ch_7x },
|
||||
{ .id = 0x10de000c, .name = "MCP89 HDMI",
|
||||
.patch = patch_nvhdmi_8ch_89 },
|
||||
{ .id = 0x10de000b, .name = "GT21x HDMI",
|
||||
.patch = patch_nvhdmi_8ch_89 },
|
||||
{ .id = 0x10de000d, .name = "GT240 HDMI",
|
||||
.patch = patch_nvhdmi_8ch_89 },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
|
@ -412,9 +566,12 @@ MODULE_ALIAS("snd-hda-codec-id:10de0006");
|
|||
MODULE_ALIAS("snd-hda-codec-id:10de0007");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0067");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de8001");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de000c");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de000b");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de000d");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Nvidia HDMI HD-audio codec");
|
||||
MODULE_DESCRIPTION("NVIDIA HDMI HD-audio codec");
|
||||
|
||||
static struct hda_codec_preset_list nvhdmi_list = {
|
||||
.preset = snd_hda_preset_nvhdmi,
|
||||
|
|
|
@ -4915,7 +4915,7 @@ static void fixup_automic_adc(struct hda_codec *codec)
|
|||
static void fixup_single_adc(struct hda_codec *codec)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
hda_nid_t pin;
|
||||
hda_nid_t pin = 0;
|
||||
int i;
|
||||
|
||||
/* search for the input pin; there must be only one */
|
||||
|
@ -13561,6 +13561,8 @@ static void alc269_lifebook_unsol_event(struct hda_codec *codec,
|
|||
static void alc269_quanta_fl1_setup(struct hda_codec *codec)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
spec->autocfg.hp_pins[0] = 0x15;
|
||||
spec->autocfg.speaker_pins[0] = 0x14;
|
||||
spec->ext_mic.pin = 0x18;
|
||||
spec->ext_mic.mux_idx = 0;
|
||||
spec->int_mic.pin = 0x19;
|
||||
|
@ -13656,6 +13658,8 @@ static void alc269_laptop_unsol_event(struct hda_codec *codec,
|
|||
static void alc269_laptop_dmic_setup(struct hda_codec *codec)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
spec->autocfg.hp_pins[0] = 0x15;
|
||||
spec->autocfg.speaker_pins[0] = 0x14;
|
||||
spec->ext_mic.pin = 0x18;
|
||||
spec->ext_mic.mux_idx = 0;
|
||||
spec->int_mic.pin = 0x12;
|
||||
|
@ -13666,6 +13670,8 @@ static void alc269_laptop_dmic_setup(struct hda_codec *codec)
|
|||
static void alc269vb_laptop_dmic_setup(struct hda_codec *codec)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
spec->autocfg.hp_pins[0] = 0x15;
|
||||
spec->autocfg.speaker_pins[0] = 0x14;
|
||||
spec->ext_mic.pin = 0x18;
|
||||
spec->ext_mic.mux_idx = 0;
|
||||
spec->int_mic.pin = 0x12;
|
||||
|
@ -13676,6 +13682,8 @@ static void alc269vb_laptop_dmic_setup(struct hda_codec *codec)
|
|||
static void alc269_laptop_amic_setup(struct hda_codec *codec)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
spec->autocfg.hp_pins[0] = 0x15;
|
||||
spec->autocfg.speaker_pins[0] = 0x14;
|
||||
spec->ext_mic.pin = 0x18;
|
||||
spec->ext_mic.mux_idx = 0;
|
||||
spec->int_mic.pin = 0x19;
|
||||
|
|
Loading…
Reference in a new issue