mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-04-01 12:04:08 +00:00
Merge branch 'topic/hda' into for-linus
This commit is contained in:
commit
f1cf9a666d
9 changed files with 1142 additions and 905 deletions
|
@ -157,7 +157,7 @@ config SND_HDA_CODEC_INTELHDMI
|
||||||
|
|
||||||
config SND_HDA_ELD
|
config SND_HDA_ELD
|
||||||
def_bool y
|
def_bool y
|
||||||
depends on SND_HDA_CODEC_INTELHDMI
|
depends on SND_HDA_CODEC_INTELHDMI || SND_HDA_CODEC_NVHDMI
|
||||||
|
|
||||||
config SND_HDA_CODEC_CIRRUS
|
config SND_HDA_CODEC_CIRRUS
|
||||||
bool "Build Cirrus Logic codec support"
|
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-y := hda_codec.o
|
||||||
snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
|
snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
|
||||||
snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.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_HWDEP) += hda_hwdep.o
|
||||||
snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.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-conexant-objs := patch_conexant.o
|
||||||
snd-hda-codec-via-objs := patch_via.o
|
snd-hda-codec-via-objs := patch_via.o
|
||||||
snd-hda-codec-nvhdmi-objs := patch_nvhdmi.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
|
# common driver
|
||||||
obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
|
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.
|
* Returns 0 if successful, or a negative error code.
|
||||||
*/
|
*/
|
||||||
int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
|
int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
|
||||||
struct hda_codec **codecp)
|
unsigned int codec_addr,
|
||||||
|
struct hda_codec **codecp)
|
||||||
{
|
{
|
||||||
struct hda_codec *codec;
|
struct hda_codec *codec;
|
||||||
char component[31];
|
char component[31];
|
||||||
|
@ -1186,7 +1187,7 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* FIXME: more better hash key? */
|
/* 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_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
|
||||||
#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
|
#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
|
||||||
#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 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) {
|
if (!codec->no_trigger_sense) {
|
||||||
pincap = snd_hda_query_pin_caps(codec, nid);
|
pincap = snd_hda_query_pin_caps(codec, nid);
|
||||||
if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
|
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,
|
return snd_hda_codec_read(codec, nid, 0,
|
||||||
AC_VERB_GET_PIN_SENSE, 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)
|
int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
|
||||||
{
|
{
|
||||||
u32 sense = snd_hda_pin_sense(codec, nid);
|
u32 sense = snd_hda_pin_sense(codec, nid);
|
||||||
return !!(sense & AC_PINSENSE_PRESENCE);
|
return !!(sense & AC_PINSENSE_PRESENCE);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
|
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);
|
err = snd_hda_ctl_add(codec, 0, kctl);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
for (s = slaves; *s; s++) {
|
for (s = slaves; *s; s++) {
|
||||||
struct snd_kcontrol *sctl;
|
struct snd_kcontrol *sctl;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -2439,27 +2441,27 @@ static struct snd_kcontrol_new dig_mixes[] = {
|
||||||
{
|
{
|
||||||
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
.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,
|
.info = snd_hda_spdif_mask_info,
|
||||||
.get = snd_hda_spdif_cmask_get,
|
.get = snd_hda_spdif_cmask_get,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
.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,
|
.info = snd_hda_spdif_mask_info,
|
||||||
.get = snd_hda_spdif_pmask_get,
|
.get = snd_hda_spdif_pmask_get,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
.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,
|
.info = snd_hda_spdif_mask_info,
|
||||||
.get = snd_hda_spdif_default_get,
|
.get = snd_hda_spdif_default_get,
|
||||||
.put = snd_hda_spdif_default_put,
|
.put = snd_hda_spdif_default_put,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
.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,
|
.info = snd_hda_spdif_out_switch_info,
|
||||||
.get = snd_hda_spdif_out_switch_get,
|
.get = snd_hda_spdif_out_switch_get,
|
||||||
.put = snd_hda_spdif_out_switch_put,
|
.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[] = {
|
static struct snd_kcontrol_new dig_in_ctls[] = {
|
||||||
{
|
{
|
||||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
.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,
|
.info = snd_hda_spdif_in_switch_info,
|
||||||
.get = snd_hda_spdif_in_switch_get,
|
.get = snd_hda_spdif_in_switch_get,
|
||||||
.put = snd_hda_spdif_in_switch_put,
|
.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,
|
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
.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,
|
.info = snd_hda_spdif_mask_info,
|
||||||
.get = snd_hda_spdif_in_status_get,
|
.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);
|
int err = snd_hda_codec_build_controls(codec);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
printk(KERN_ERR "hda_codec: cannot build controls"
|
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);
|
err = snd_hda_codec_reset(codec);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
printk(KERN_ERR
|
printk(KERN_ERR
|
||||||
|
@ -2979,8 +2981,12 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
|
||||||
val |= channels - 1;
|
val |= channels - 1;
|
||||||
|
|
||||||
switch (snd_pcm_format_width(format)) {
|
switch (snd_pcm_format_width(format)) {
|
||||||
case 8: val |= 0x00; break;
|
case 8:
|
||||||
case 16: val |= 0x10; break;
|
val |= 0x00;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
val |= 0x10;
|
||||||
|
break;
|
||||||
case 20:
|
case 20:
|
||||||
case 24:
|
case 24:
|
||||||
case 32:
|
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))
|
if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
|
||||||
return audio_idx[type][i];
|
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;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3336,7 +3343,7 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
|
||||||
err = codec->patch_ops.build_pcms(codec);
|
err = codec->patch_ops.build_pcms(codec);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
printk(KERN_ERR "hda_codec: cannot build PCMs"
|
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);
|
err = snd_hda_codec_reset(codec);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
printk(KERN_ERR
|
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
|
* snd_hda_check_board_codec_sid_config - compare the current codec
|
||||||
subsystem ID with the
|
subsystem ID with the
|
||||||
config table
|
config table
|
||||||
|
|
||||||
This is important for Gateway notebooks with SB450 HDA Audio
|
This is important for Gateway notebooks with SB450 HDA Audio
|
||||||
where the vendor ID of the PCI device is:
|
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
|
* Increment the power-up counter and power up the hardware really when
|
||||||
* not turned on yet.
|
* not turned on yet.
|
||||||
*/
|
*/
|
||||||
void snd_hda_power_up(struct hda_codec *codec)
|
void snd_hda_power_up(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
struct hda_bus *bus = codec->bus;
|
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
|
* Decrement the power-up counter and schedules the power-off work if
|
||||||
* the counter rearches to zero.
|
* the counter rearches to zero.
|
||||||
*/
|
*/
|
||||||
void snd_hda_power_down(struct hda_codec *codec)
|
void snd_hda_power_down(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
--codec->power_count;
|
--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
|
* This function is supposed to be set or called from the check_power_status
|
||||||
* patch ops.
|
* patch ops.
|
||||||
*/
|
*/
|
||||||
int snd_hda_check_amp_list_power(struct hda_codec *codec,
|
int snd_hda_check_amp_list_power(struct hda_codec *codec,
|
||||||
struct hda_loopback_check *check,
|
struct hda_loopback_check *check,
|
||||||
hda_nid_t nid)
|
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 */
|
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
|
||||||
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
|
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,
|
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff,
|
||||||
-1);
|
-1);
|
||||||
snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
|
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.
|
* 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 num_pins)
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
short seq;
|
short seq;
|
||||||
hda_nid_t nid;
|
hda_nid_t nid;
|
||||||
|
|
||||||
for (i = 0; i < num_pins; i++) {
|
for (i = 0; i < num_pins; i++) {
|
||||||
for (j = i + 1; j < num_pins; j++) {
|
for (j = i + 1; j < num_pins; j++) {
|
||||||
if (sequences[i] > sequences[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
|
* 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
|
* output, i.e. to line_out_pins[0]. So, line_outs is always positive
|
||||||
* if any analog output exists.
|
* if any analog output exists.
|
||||||
*
|
*
|
||||||
* The analog input pins are assigned to input_pins array.
|
* 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,
|
* The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
|
||||||
* respectively.
|
* respectively.
|
||||||
|
@ -4186,9 +4193,9 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
|
||||||
case AC_JACK_SPEAKER:
|
case AC_JACK_SPEAKER:
|
||||||
seq = get_defcfg_sequence(def_conf);
|
seq = get_defcfg_sequence(def_conf);
|
||||||
assoc = get_defcfg_association(def_conf);
|
assoc = get_defcfg_association(def_conf);
|
||||||
if (! assoc)
|
if (!assoc)
|
||||||
continue;
|
continue;
|
||||||
if (! assoc_speaker)
|
if (!assoc_speaker)
|
||||||
assoc_speaker = assoc;
|
assoc_speaker = assoc;
|
||||||
else if (assoc_speaker != assoc)
|
else if (assoc_speaker != assoc)
|
||||||
continue;
|
continue;
|
||||||
|
@ -4286,7 +4293,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
|
||||||
cfg->speaker_outs);
|
cfg->speaker_outs);
|
||||||
sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
|
sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
|
||||||
cfg->hp_outs);
|
cfg->hp_outs);
|
||||||
|
|
||||||
/* if we have only one mic, make it AUTO_PIN_MIC */
|
/* if we have only one mic, make it AUTO_PIN_MIC */
|
||||||
if (!cfg->input_pins[AUTO_PIN_MIC] &&
|
if (!cfg->input_pins[AUTO_PIN_MIC] &&
|
||||||
cfg->input_pins[AUTO_PIN_FRONT_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
|
* snd_array_new - get a new element from the given array
|
||||||
* @array: the array object
|
* @array: the array object
|
||||||
*
|
*
|
||||||
* Get a new element from the given array. If it exceeds the
|
* Get a new element from the given array. If it exceeds the
|
||||||
* pre-allocated array size, re-allocate the array.
|
* 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,
|
return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
|
||||||
AC_DIPSIZE_ELD_BUF);
|
AC_DIPSIZE_ELD_BUF);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_HDA(snd_hdmi_get_eld_size);
|
||||||
|
|
||||||
int snd_hdmi_get_eld(struct hdmi_eld *eld,
|
int snd_hdmi_get_eld(struct hdmi_eld *eld,
|
||||||
struct hda_codec *codec, hda_nid_t nid)
|
struct hda_codec *codec, hda_nid_t nid)
|
||||||
|
@ -366,6 +367,7 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld,
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_HDA(snd_hdmi_get_eld);
|
||||||
|
|
||||||
static void hdmi_show_short_audio_desc(struct cea_sad *a)
|
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 */
|
buf[j] = '\0'; /* necessary when j == 0 */
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_HDA(snd_print_channel_allocation);
|
||||||
|
|
||||||
void snd_hdmi_show_eld(struct hdmi_eld *e)
|
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++)
|
for (i = 0; i < e->sad_count; i++)
|
||||||
hdmi_show_short_audio_desc(e->sad + i);
|
hdmi_show_short_audio_desc(e->sad + i);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_HDA(snd_hdmi_show_eld);
|
||||||
|
|
||||||
#ifdef CONFIG_PROC_FS
|
#ifdef CONFIG_PROC_FS
|
||||||
|
|
||||||
|
@ -580,6 +584,7 @@ int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_HDA(snd_hda_eld_proc_new);
|
||||||
|
|
||||||
void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
|
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;
|
eld->proc_entry = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_HDA(snd_hda_eld_proc_free);
|
||||||
|
|
||||||
#endif /* CONFIG_PROC_FS */
|
#endif /* CONFIG_PROC_FS */
|
||||||
|
|
|
@ -267,7 +267,8 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
|
||||||
#define RIRB_INT_MASK 0x05
|
#define RIRB_INT_MASK 0x05
|
||||||
|
|
||||||
/* STATESTS int mask: S3,SD2,SD1,SD0 */
|
/* 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)
|
#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1)
|
||||||
|
|
||||||
/* SD_CTL bits */
|
/* 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) */
|
/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
|
||||||
static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
|
static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
|
||||||
|
[AZX_DRIVER_NVIDIA] = 8,
|
||||||
[AZX_DRIVER_TERA] = 1,
|
[AZX_DRIVER_TERA] = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1399,7 +1401,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
|
||||||
codecs = 0;
|
codecs = 0;
|
||||||
max_slots = azx_max_codecs[chip->driver_type];
|
max_slots = azx_max_codecs[chip->driver_type];
|
||||||
if (!max_slots)
|
if (!max_slots)
|
||||||
max_slots = AZX_MAX_CODECS;
|
max_slots = AZX_DEFAULT_CODECS;
|
||||||
|
|
||||||
/* First try to probe all given codec slots */
|
/* First try to probe all given codec slots */
|
||||||
for (c = 0; c < max_slots; c++) {
|
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 = {
|
static struct snd_pci_quirk position_fix_list[] __devinitdata = {
|
||||||
SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
|
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, 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(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
|
||||||
SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", 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(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
|
||||||
SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", 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 = {
|
static struct snd_pci_quirk msi_black_list[] __devinitdata = {
|
||||||
SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */
|
SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */
|
||||||
SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* 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.
|
* The HDA correspondence of pipes/ports are converter/pin nodes.
|
||||||
*/
|
*/
|
||||||
#define INTEL_HDMI_CVTS 2
|
#define MAX_HDMI_CVTS 2
|
||||||
#define INTEL_HDMI_PINS 3
|
#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 0",
|
||||||
"INTEL HDMI 1",
|
"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:
|
* HDMI callbacks
|
||||||
*
|
|
||||||
* 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 } },
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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,
|
static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||||
struct hda_codec *codec,
|
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)
|
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;
|
struct hda_pcm *info = spec->pcm_rec;
|
||||||
int i;
|
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)
|
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 err;
|
||||||
int i;
|
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)
|
static int intel_hdmi_init(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
struct intel_hdmi_spec *spec = codec->spec;
|
struct hdmi_spec *spec = codec->spec;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; spec->pin[i]; 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)
|
static void intel_hdmi_free(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
struct intel_hdmi_spec *spec = codec->spec;
|
struct hdmi_spec *spec = codec->spec;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < spec->num_pins; 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,
|
.free = intel_hdmi_free,
|
||||||
.build_pcms = intel_hdmi_build_pcms,
|
.build_pcms = intel_hdmi_build_pcms,
|
||||||
.build_controls = intel_hdmi_build_controls,
|
.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)
|
static int patch_intel_hdmi(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
struct intel_hdmi_spec *spec;
|
struct hdmi_spec *spec;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||||
|
@ -964,7 +169,7 @@ static int patch_intel_hdmi(struct hda_codec *codec)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
codec->spec = spec;
|
codec->spec = spec;
|
||||||
if (intel_hdmi_parse_codec(codec) < 0) {
|
if (hdmi_parse_codec(codec) < 0) {
|
||||||
codec->spec = NULL;
|
codec->spec = NULL;
|
||||||
kfree(spec);
|
kfree(spec);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
|
@ -29,13 +29,23 @@
|
||||||
#include "hda_codec.h"
|
#include "hda_codec.h"
|
||||||
#include "hda_local.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 below to restrict the supported rates and formats */
|
||||||
/* #define LIMITED_RATE_FMT_SUPPORT */
|
/* #define LIMITED_RATE_FMT_SUPPORT */
|
||||||
|
|
||||||
struct nvhdmi_spec {
|
enum HDACodec {
|
||||||
struct hda_multi_out multiout;
|
HDA_CODEC_NVIDIA_MCP7X,
|
||||||
|
HDA_CODEC_NVIDIA_MCP89,
|
||||||
struct hda_pcm pcm_rec;
|
HDA_CODEC_NVIDIA_GT21X,
|
||||||
|
HDA_CODEC_INVALID
|
||||||
};
|
};
|
||||||
|
|
||||||
#define Nv_VERB_SET_Channel_Allocation 0xF79
|
#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_On 0xF98
|
||||||
#define Nv_VERB_SET_Audio_Protection_Off 0xF99
|
#define Nv_VERB_SET_Audio_Protection_Off 0xF99
|
||||||
|
|
||||||
#define Nv_Master_Convert_nid 0x04
|
#define nvhdmi_master_con_nid_7x 0x04
|
||||||
#define Nv_Master_Pin_nid 0x05
|
#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 */
|
/*front, rear, clfe, rear_surr */
|
||||||
0x6, 0x8, 0xa, 0xc,
|
0x6, 0x8, 0xa, 0xc,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct hda_verb nvhdmi_basic_init[] = {
|
static struct hda_verb nvhdmi_basic_init_7x[] = {
|
||||||
/* set audio protect on */
|
/* set audio protect on */
|
||||||
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
|
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
|
||||||
/* enable digital output on pin widget */
|
/* 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)
|
static int nvhdmi_build_controls(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
struct nvhdmi_spec *spec = codec->spec;
|
struct hdmi_spec *spec = codec->spec;
|
||||||
int err;
|
int err;
|
||||||
|
int i;
|
||||||
|
|
||||||
err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
|
if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
|
||||||
if (err < 0)
|
|| (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
|
||||||
return err;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nvhdmi_init(struct hda_codec *codec)
|
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;
|
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
|
* Digital out
|
||||||
*/
|
*/
|
||||||
|
@ -107,25 +158,25 @@ static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
||||||
struct hda_codec *codec,
|
struct hda_codec *codec,
|
||||||
struct snd_pcm_substream *substream)
|
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);
|
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 hda_codec *codec,
|
||||||
struct snd_pcm_substream *substream)
|
struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
struct nvhdmi_spec *spec = codec->spec;
|
struct hdmi_spec *spec = codec->spec;
|
||||||
int i;
|
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);
|
0, AC_VERB_SET_CHANNEL_STREAMID, 0);
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
/* set the stream id */
|
/* 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);
|
AC_VERB_SET_CHANNEL_STREAMID, 0);
|
||||||
/* set the stream format */
|
/* 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);
|
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 hda_codec *codec,
|
||||||
struct snd_pcm_substream *substream)
|
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);
|
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,
|
static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
|
||||||
struct hda_codec *codec,
|
struct hda_codec *codec,
|
||||||
unsigned int stream_tag,
|
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 */
|
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
|
||||||
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
|
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
|
||||||
snd_hda_codec_write(codec,
|
snd_hda_codec_write(codec,
|
||||||
Nv_Master_Convert_nid,
|
nvhdmi_master_con_nid_7x,
|
||||||
0,
|
0,
|
||||||
AC_VERB_SET_DIGI_CONVERT_1,
|
AC_VERB_SET_DIGI_CONVERT_1,
|
||||||
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
|
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
|
||||||
|
|
||||||
/* set the stream id */
|
/* 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);
|
AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
|
||||||
|
|
||||||
/* set the stream format */
|
/* 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);
|
AC_VERB_SET_STREAM_FORMAT, format);
|
||||||
|
|
||||||
/* turn on again (if needed) */
|
/* turn on again (if needed) */
|
||||||
/* enable and set the channel status audio/data flag */
|
/* enable and set the channel status audio/data flag */
|
||||||
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
|
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
|
||||||
snd_hda_codec_write(codec,
|
snd_hda_codec_write(codec,
|
||||||
Nv_Master_Convert_nid,
|
nvhdmi_master_con_nid_7x,
|
||||||
0,
|
0,
|
||||||
AC_VERB_SET_DIGI_CONVERT_1,
|
AC_VERB_SET_DIGI_CONVERT_1,
|
||||||
codec->spdif_ctls & 0xff);
|
codec->spdif_ctls & 0xff);
|
||||||
snd_hda_codec_write(codec,
|
snd_hda_codec_write(codec,
|
||||||
Nv_Master_Convert_nid,
|
nvhdmi_master_con_nid_7x,
|
||||||
0,
|
0,
|
||||||
AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
|
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 &&
|
if (codec->spdif_status_reset &&
|
||||||
(codec->spdif_ctls & AC_DIG1_ENABLE))
|
(codec->spdif_ctls & AC_DIG1_ENABLE))
|
||||||
snd_hda_codec_write(codec,
|
snd_hda_codec_write(codec,
|
||||||
nvhdmi_convert_nids[i],
|
nvhdmi_con_nids_7x[i],
|
||||||
0,
|
0,
|
||||||
AC_VERB_SET_DIGI_CONVERT_1,
|
AC_VERB_SET_DIGI_CONVERT_1,
|
||||||
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
|
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
|
||||||
/* set the stream id */
|
/* set the stream id */
|
||||||
snd_hda_codec_write(codec,
|
snd_hda_codec_write(codec,
|
||||||
nvhdmi_convert_nids[i],
|
nvhdmi_con_nids_7x[i],
|
||||||
0,
|
0,
|
||||||
AC_VERB_SET_CHANNEL_STREAMID,
|
AC_VERB_SET_CHANNEL_STREAMID,
|
||||||
(stream_tag << 4) | channel_id);
|
(stream_tag << 4) | channel_id);
|
||||||
/* set the stream format */
|
/* set the stream format */
|
||||||
snd_hda_codec_write(codec,
|
snd_hda_codec_write(codec,
|
||||||
nvhdmi_convert_nids[i],
|
nvhdmi_con_nids_7x[i],
|
||||||
0,
|
0,
|
||||||
AC_VERB_SET_STREAM_FORMAT,
|
AC_VERB_SET_STREAM_FORMAT,
|
||||||
format);
|
format);
|
||||||
|
@ -241,12 +307,12 @@ static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
|
||||||
if (codec->spdif_status_reset &&
|
if (codec->spdif_status_reset &&
|
||||||
(codec->spdif_ctls & AC_DIG1_ENABLE)) {
|
(codec->spdif_ctls & AC_DIG1_ENABLE)) {
|
||||||
snd_hda_codec_write(codec,
|
snd_hda_codec_write(codec,
|
||||||
nvhdmi_convert_nids[i],
|
nvhdmi_con_nids_7x[i],
|
||||||
0,
|
0,
|
||||||
AC_VERB_SET_DIGI_CONVERT_1,
|
AC_VERB_SET_DIGI_CONVERT_1,
|
||||||
codec->spdif_ctls & 0xff);
|
codec->spdif_ctls & 0xff);
|
||||||
snd_hda_codec_write(codec,
|
snd_hda_codec_write(codec,
|
||||||
nvhdmi_convert_nids[i],
|
nvhdmi_con_nids_7x[i],
|
||||||
0,
|
0,
|
||||||
AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
|
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;
|
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,
|
static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
|
||||||
struct hda_codec *codec,
|
struct hda_codec *codec,
|
||||||
unsigned int stream_tag,
|
unsigned int stream_tag,
|
||||||
unsigned int format,
|
unsigned int format,
|
||||||
struct snd_pcm_substream *substream)
|
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,
|
return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
|
||||||
format, substream);
|
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,
|
.substreams = 1,
|
||||||
.channels_min = 2,
|
.channels_min = 2,
|
||||||
.channels_max = 8,
|
.channels_max = 8,
|
||||||
.nid = Nv_Master_Convert_nid,
|
.nid = nvhdmi_master_con_nid_7x,
|
||||||
.rates = SUPPORTED_RATES,
|
.rates = SUPPORTED_RATES,
|
||||||
.maxbps = SUPPORTED_MAXBPS,
|
.maxbps = SUPPORTED_MAXBPS,
|
||||||
.formats = SUPPORTED_FORMATS,
|
.formats = SUPPORTED_FORMATS,
|
||||||
.ops = {
|
.ops = {
|
||||||
.open = nvhdmi_dig_playback_pcm_open,
|
.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
|
.prepare = nvhdmi_dig_playback_pcm_prepare_8ch
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -291,7 +376,7 @@ static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
|
||||||
.substreams = 1,
|
.substreams = 1,
|
||||||
.channels_min = 2,
|
.channels_min = 2,
|
||||||
.channels_max = 2,
|
.channels_max = 2,
|
||||||
.nid = Nv_Master_Convert_nid,
|
.nid = nvhdmi_master_con_nid_7x,
|
||||||
.rates = SUPPORTED_RATES,
|
.rates = SUPPORTED_RATES,
|
||||||
.maxbps = SUPPORTED_MAXBPS,
|
.maxbps = SUPPORTED_MAXBPS,
|
||||||
.formats = SUPPORTED_FORMATS,
|
.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 hdmi_spec *spec = codec->spec;
|
||||||
struct hda_pcm *info = &spec->pcm_rec;
|
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->num_pcms = 1;
|
||||||
codec->pcm_info = info;
|
codec->pcm_info = info;
|
||||||
|
@ -313,15 +424,15 @@ static int nvhdmi_build_pcms_8ch(struct hda_codec *codec)
|
||||||
info->name = "NVIDIA HDMI";
|
info->name = "NVIDIA HDMI";
|
||||||
info->pcm_type = HDA_PCM_TYPE_HDMI;
|
info->pcm_type = HDA_PCM_TYPE_HDMI;
|
||||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK]
|
info->stream[SNDRV_PCM_STREAM_PLAYBACK]
|
||||||
= nvhdmi_pcm_digital_playback_8ch;
|
= nvhdmi_pcm_digital_playback_8ch_7x;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
|
static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
struct nvhdmi_spec *spec = codec->spec;
|
struct hdmi_spec *spec = codec->spec;
|
||||||
struct hda_pcm *info = &spec->pcm_rec;
|
struct hda_pcm *info = spec->pcm_rec;
|
||||||
|
|
||||||
codec->num_pcms = 1;
|
codec->num_pcms = 1;
|
||||||
codec->pcm_info = info;
|
codec->pcm_info = info;
|
||||||
|
@ -334,14 +445,17 @@ static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nvhdmi_free(struct hda_codec *codec)
|
static struct hda_codec_ops nvhdmi_patch_ops_8ch_89 = {
|
||||||
{
|
|
||||||
kfree(codec->spec);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct hda_codec_ops nvhdmi_patch_ops_8ch = {
|
|
||||||
.build_controls = nvhdmi_build_controls,
|
.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,
|
.init = nvhdmi_init,
|
||||||
.free = nvhdmi_free,
|
.free = nvhdmi_free,
|
||||||
};
|
};
|
||||||
|
@ -353,9 +467,36 @@ static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
|
||||||
.free = nvhdmi_free,
|
.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);
|
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||||
if (spec == NULL)
|
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.num_dacs = 0; /* no analog */
|
||||||
spec->multiout.max_channels = 8;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int patch_nvhdmi_2ch(struct hda_codec *codec)
|
static int patch_nvhdmi_2ch(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
struct nvhdmi_spec *spec;
|
struct hdmi_spec *spec;
|
||||||
|
|
||||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||||
if (spec == NULL)
|
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.num_dacs = 0; /* no analog */
|
||||||
spec->multiout.max_channels = 2;
|
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;
|
codec->patch_ops = nvhdmi_patch_ops_2ch;
|
||||||
|
|
||||||
|
@ -395,13 +538,24 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec)
|
||||||
* patch entries
|
* patch entries
|
||||||
*/
|
*/
|
||||||
static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
|
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 = 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 = 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 */
|
{} /* 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:10de0007");
|
||||||
MODULE_ALIAS("snd-hda-codec-id:10de0067");
|
MODULE_ALIAS("snd-hda-codec-id:10de0067");
|
||||||
MODULE_ALIAS("snd-hda-codec-id:10de8001");
|
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_LICENSE("GPL");
|
||||||
MODULE_DESCRIPTION("Nvidia HDMI HD-audio codec");
|
MODULE_DESCRIPTION("NVIDIA HDMI HD-audio codec");
|
||||||
|
|
||||||
static struct hda_codec_preset_list nvhdmi_list = {
|
static struct hda_codec_preset_list nvhdmi_list = {
|
||||||
.preset = snd_hda_preset_nvhdmi,
|
.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)
|
static void fixup_single_adc(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
struct alc_spec *spec = codec->spec;
|
struct alc_spec *spec = codec->spec;
|
||||||
hda_nid_t pin;
|
hda_nid_t pin = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* search for the input pin; there must be only one */
|
/* 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)
|
static void alc269_quanta_fl1_setup(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
struct alc_spec *spec = codec->spec;
|
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.pin = 0x18;
|
||||||
spec->ext_mic.mux_idx = 0;
|
spec->ext_mic.mux_idx = 0;
|
||||||
spec->int_mic.pin = 0x19;
|
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)
|
static void alc269_laptop_dmic_setup(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
struct alc_spec *spec = codec->spec;
|
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.pin = 0x18;
|
||||||
spec->ext_mic.mux_idx = 0;
|
spec->ext_mic.mux_idx = 0;
|
||||||
spec->int_mic.pin = 0x12;
|
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)
|
static void alc269vb_laptop_dmic_setup(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
struct alc_spec *spec = codec->spec;
|
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.pin = 0x18;
|
||||||
spec->ext_mic.mux_idx = 0;
|
spec->ext_mic.mux_idx = 0;
|
||||||
spec->int_mic.pin = 0x12;
|
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)
|
static void alc269_laptop_amic_setup(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
struct alc_spec *spec = codec->spec;
|
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.pin = 0x18;
|
||||||
spec->ext_mic.mux_idx = 0;
|
spec->ext_mic.mux_idx = 0;
|
||||||
spec->int_mic.pin = 0x19;
|
spec->int_mic.pin = 0x19;
|
||||||
|
|
Loading…
Add table
Reference in a new issue