Merge branch 'topic/hda' into for-linus

This commit is contained in:
Takashi Iwai 2010-10-25 10:40:05 +02:00
commit 506ecbca71
21 changed files with 2849 additions and 2251 deletions

View File

@ -57,9 +57,11 @@ dead. However, this detection isn't perfect on some devices. In such
a case, you can change the default method via `position_fix` option. a case, you can change the default method via `position_fix` option.
`position_fix=1` means to use LPIB method explicitly. `position_fix=1` means to use LPIB method explicitly.
`position_fix=2` means to use the position-buffer. 0 is the default `position_fix=2` means to use the position-buffer.
value, the automatic check and fallback to LPIB as described in the `position_fix=3` means to use a combination of both methods, needed
above. If you get a problem of repeated sounds, this option might for some VIA and ATI controllers. 0 is the default value for all other
controllers, the automatic check and fallback to LPIB as described in
the above. If you get a problem of repeated sounds, this option might
help. help.
In addition to that, every controller is known to be broken regarding In addition to that, every controller is known to be broken regarding

View File

@ -38,9 +38,11 @@
#define SNDRV_CTL_TLVT_DB_MINMAX 4 /* dB scale with min/max */ #define SNDRV_CTL_TLVT_DB_MINMAX 4 /* dB scale with min/max */
#define SNDRV_CTL_TLVT_DB_MINMAX_MUTE 5 /* dB scale with min/max with mute */ #define SNDRV_CTL_TLVT_DB_MINMAX_MUTE 5 /* dB scale with min/max with mute */
#define TLV_DB_SCALE_MASK 0xffff
#define TLV_DB_SCALE_MUTE 0x10000
#define TLV_DB_SCALE_ITEM(min, step, mute) \ #define TLV_DB_SCALE_ITEM(min, step, mute) \
SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int), \ SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int), \
(min), ((step) & 0xffff) | ((mute) ? 0x10000 : 0) (min), ((step) & TLV_DB_SCALE_MASK) | ((mute) ? TLV_DB_SCALE_MUTE : 0)
#define DECLARE_TLV_DB_SCALE(name, min, step, mute) \ #define DECLARE_TLV_DB_SCALE(name, min, step, mute) \
unsigned int name[] = { TLV_DB_SCALE_ITEM(min, step, mute) } unsigned int name[] = { TLV_DB_SCALE_ITEM(min, step, mute) }

View File

@ -119,47 +119,20 @@ config SND_HDA_CODEC_VIA
snd-hda-codec-via. snd-hda-codec-via.
This module is automatically loaded at probing. This module is automatically loaded at probing.
config SND_HDA_CODEC_ATIHDMI config SND_HDA_CODEC_HDMI
bool "Build ATI HDMI HD-audio codec support" bool "Build HDMI/DisplayPort HD-audio codec support"
default y
help
Say Y here to include ATI HDMI HD-audio codec support in
snd-hda-intel driver, such as ATI RS600 HDMI.
When the HD-audio driver is built as a module, the codec
support code is also built as another module,
snd-hda-codec-atihdmi.
This module is automatically loaded at probing.
config SND_HDA_CODEC_NVHDMI
bool "Build NVIDIA HDMI HD-audio codec support"
default y
help
Say Y here to include NVIDIA HDMI HD-audio codec support in
snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
When the HD-audio driver is built as a module, the codec
support code is also built as another module,
snd-hda-codec-nvhdmi.
This module is automatically loaded at probing.
config SND_HDA_CODEC_INTELHDMI
bool "Build INTEL HDMI HD-audio codec support"
select SND_DYNAMIC_MINORS select SND_DYNAMIC_MINORS
default y default y
help help
Say Y here to include INTEL HDMI HD-audio codec support in Say Y here to include HDMI and DisplayPort HD-audio codec
snd-hda-intel driver, such as Eaglelake integrated HDMI. support in snd-hda-intel driver. This includes all AMD/ATI,
Intel and Nvidia HDMI/DisplayPort codecs.
When the HD-audio driver is built as a module, the codec When the HD-audio driver is built as a module, the codec
support code is also built as another module, support code is also built as another module,
snd-hda-codec-intelhdmi. snd-hda-codec-hdmi.
This module is automatically loaded at probing. This module is automatically loaded at probing.
config SND_HDA_ELD
def_bool y
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"
depends on SND_HDA_INTEL depends on SND_HDA_INTEL

View File

@ -3,7 +3,6 @@ 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_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
@ -12,13 +11,11 @@ snd-hda-codec-cmedia-objs := patch_cmedia.o
snd-hda-codec-analog-objs := patch_analog.o snd-hda-codec-analog-objs := patch_analog.o
snd-hda-codec-idt-objs := patch_sigmatel.o snd-hda-codec-idt-objs := patch_sigmatel.o
snd-hda-codec-si3054-objs := patch_si3054.o snd-hda-codec-si3054-objs := patch_si3054.o
snd-hda-codec-atihdmi-objs := patch_atihdmi.o
snd-hda-codec-cirrus-objs := patch_cirrus.o snd-hda-codec-cirrus-objs := patch_cirrus.o
snd-hda-codec-ca0110-objs := patch_ca0110.o 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-hdmi-objs := patch_hdmi.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
@ -39,9 +36,6 @@ endif
ifdef CONFIG_SND_HDA_CODEC_SI3054 ifdef CONFIG_SND_HDA_CODEC_SI3054
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o
endif endif
ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
endif
ifdef CONFIG_SND_HDA_CODEC_CIRRUS ifdef CONFIG_SND_HDA_CODEC_CIRRUS
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o
endif endif
@ -54,11 +48,8 @@ endif
ifdef CONFIG_SND_HDA_CODEC_VIA ifdef CONFIG_SND_HDA_CODEC_VIA
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o
endif endif
ifdef CONFIG_SND_HDA_CODEC_NVHDMI ifdef CONFIG_SND_HDA_CODEC_HDMI
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-nvhdmi.o obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-hdmi.o
endif
ifdef CONFIG_SND_HDA_CODEC_INTELHDMI
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-intelhdmi.o
endif endif
# this must be the last entry after codec drivers; # this must be the last entry after codec drivers;

View File

@ -1216,6 +1216,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
struct hda_codec *c; struct hda_codec *c;
struct hda_cvt_setup *p; struct hda_cvt_setup *p;
unsigned int oldval, newval; unsigned int oldval, newval;
int type;
int i; int i;
if (!nid) if (!nid)
@ -1254,10 +1255,12 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
p->dirty = 0; p->dirty = 0;
/* make other inactive cvts with the same stream-tag dirty */ /* make other inactive cvts with the same stream-tag dirty */
type = get_wcaps_type(get_wcaps(codec, nid));
list_for_each_entry(c, &codec->bus->codec_list, list) { list_for_each_entry(c, &codec->bus->codec_list, list) {
for (i = 0; i < c->cvt_setups.used; i++) { for (i = 0; i < c->cvt_setups.used; i++) {
p = snd_array_elem(&c->cvt_setups, i); p = snd_array_elem(&c->cvt_setups, i);
if (!p->active && p->stream_tag == stream_tag) if (!p->active && p->stream_tag == stream_tag &&
get_wcaps_type(get_wcaps(codec, p->nid)) == type)
p->dirty = 1; p->dirty = 1;
} }
} }
@ -1281,6 +1284,9 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
if (!nid) if (!nid)
return; return;
if (codec->no_sticky_stream)
do_now = 1;
snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid); snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
p = get_hda_cvt_setup(codec, nid); p = get_hda_cvt_setup(codec, nid);
if (p) { if (p) {
@ -1831,6 +1837,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
hda_nid_t nid = get_amp_nid(kcontrol); hda_nid_t nid = get_amp_nid(kcontrol);
int dir = get_amp_direction(kcontrol); int dir = get_amp_direction(kcontrol);
unsigned int ofs = get_amp_offset(kcontrol); unsigned int ofs = get_amp_offset(kcontrol);
bool min_mute = get_amp_min_mute(kcontrol);
u32 caps, val1, val2; u32 caps, val1, val2;
if (size < 4 * sizeof(unsigned int)) if (size < 4 * sizeof(unsigned int))
@ -1841,6 +1848,8 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT); val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
val1 += ofs; val1 += ofs;
val1 = ((int)val1) * ((int)val2); val1 = ((int)val1) * ((int)val2);
if (min_mute)
val2 |= TLV_DB_SCALE_MUTE;
if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv)) if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
return -EFAULT; return -EFAULT;
if (put_user(2 * sizeof(unsigned int), _tlv + 1)) if (put_user(2 * sizeof(unsigned int), _tlv + 1))
@ -2228,10 +2237,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
HDA_AMP_MUTE, HDA_AMP_MUTE,
*valp ? 0 : HDA_AMP_MUTE); *valp ? 0 : HDA_AMP_MUTE);
#ifdef CONFIG_SND_HDA_POWER_SAVE hda_call_check_power_status(codec, nid);
if (codec->patch_ops.check_power_status)
codec->patch_ops.check_power_status(codec, nid);
#endif
snd_hda_power_down(codec); snd_hda_power_down(codec);
return change; return change;
} }
@ -4372,6 +4378,34 @@ static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
} }
/* add the found input-pin to the cfg->inputs[] table */
static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
int type)
{
if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
cfg->inputs[cfg->num_inputs].pin = nid;
cfg->inputs[cfg->num_inputs].type = type;
cfg->num_inputs++;
}
}
/* sort inputs in the order of AUTO_PIN_* type */
static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
{
int i, j;
for (i = 0; i < cfg->num_inputs; i++) {
for (j = i + 1; j < cfg->num_inputs; j++) {
if (cfg->inputs[i].type > cfg->inputs[j].type) {
struct auto_pin_cfg_item tmp;
tmp = cfg->inputs[i];
cfg->inputs[i] = cfg->inputs[j];
cfg->inputs[j] = tmp;
}
}
}
}
/* /*
* Parse all pin widgets and store the useful pin nids to cfg * Parse all pin widgets and store the useful pin nids to cfg
* *
@ -4385,7 +4419,7 @@ static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
* 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 inputs 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.
*/ */
@ -4398,6 +4432,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)]; short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)]; short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
short sequences_hp[ARRAY_SIZE(cfg->hp_pins)]; short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
int i;
memset(cfg, 0, sizeof(*cfg)); memset(cfg, 0, sizeof(*cfg));
@ -4468,33 +4503,17 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
sequences_hp[cfg->hp_outs] = (assoc << 4) | seq; sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
cfg->hp_outs++; cfg->hp_outs++;
break; break;
case AC_JACK_MIC_IN: { case AC_JACK_MIC_IN:
int preferred, alt; add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
if (loc == AC_JACK_LOC_FRONT ||
(loc & 0x30) == AC_JACK_LOC_INTERNAL) {
preferred = AUTO_PIN_FRONT_MIC;
alt = AUTO_PIN_MIC;
} else {
preferred = AUTO_PIN_MIC;
alt = AUTO_PIN_FRONT_MIC;
}
if (!cfg->input_pins[preferred])
cfg->input_pins[preferred] = nid;
else if (!cfg->input_pins[alt])
cfg->input_pins[alt] = nid;
break; break;
}
case AC_JACK_LINE_IN: case AC_JACK_LINE_IN:
if (loc == AC_JACK_LOC_FRONT) add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid;
else
cfg->input_pins[AUTO_PIN_LINE] = nid;
break; break;
case AC_JACK_CD: case AC_JACK_CD:
cfg->input_pins[AUTO_PIN_CD] = nid; add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
break; break;
case AC_JACK_AUX: case AC_JACK_AUX:
cfg->input_pins[AUTO_PIN_AUX] = nid; add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
break; break;
case AC_JACK_SPDIF_OUT: case AC_JACK_SPDIF_OUT:
case AC_JACK_DIG_OTHER_OUT: case AC_JACK_DIG_OTHER_OUT:
@ -4539,6 +4558,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
memmove(sequences_hp + i, sequences_hp + i + 1, memmove(sequences_hp + i, sequences_hp + i + 1,
sizeof(sequences_hp[0]) * (cfg->hp_outs - i)); sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
} }
memset(cfg->hp_pins + cfg->hp_outs, 0,
sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
} }
/* sort by sequence */ /* sort by sequence */
@ -4549,21 +4570,6 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
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 (!cfg->input_pins[AUTO_PIN_MIC] &&
cfg->input_pins[AUTO_PIN_FRONT_MIC]) {
cfg->input_pins[AUTO_PIN_MIC] =
cfg->input_pins[AUTO_PIN_FRONT_MIC];
cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0;
}
/* ditto for line-in */
if (!cfg->input_pins[AUTO_PIN_LINE] &&
cfg->input_pins[AUTO_PIN_FRONT_LINE]) {
cfg->input_pins[AUTO_PIN_LINE] =
cfg->input_pins[AUTO_PIN_FRONT_LINE];
cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0;
}
/* /*
* FIX-UP: if no line-outs are detected, try to use speaker or HP pin * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
* as a primary output * as a primary output
@ -4602,6 +4608,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
break; break;
} }
sort_autocfg_input_pins(cfg);
/* /*
* debug prints of the parsed results * debug prints of the parsed results
*/ */
@ -4621,14 +4629,13 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
if (cfg->dig_outs) if (cfg->dig_outs)
snd_printd(" dig-out=0x%x/0x%x\n", snd_printd(" dig-out=0x%x/0x%x\n",
cfg->dig_out_pins[0], cfg->dig_out_pins[1]); cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x," snd_printd(" inputs:");
" cd=0x%x, aux=0x%x\n", for (i = 0; i < cfg->num_inputs; i++) {
cfg->input_pins[AUTO_PIN_MIC], snd_printdd(" %s=0x%x",
cfg->input_pins[AUTO_PIN_FRONT_MIC], hda_get_autocfg_input_label(codec, cfg, i),
cfg->input_pins[AUTO_PIN_LINE], cfg->inputs[i].pin);
cfg->input_pins[AUTO_PIN_FRONT_LINE], }
cfg->input_pins[AUTO_PIN_CD], snd_printd("\n");
cfg->input_pins[AUTO_PIN_AUX]);
if (cfg->dig_in_pin) if (cfg->dig_in_pin)
snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin); snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin);
@ -4636,11 +4643,165 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
} }
EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config); EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config);
/* labels for input pins */ int snd_hda_get_input_pin_attr(unsigned int def_conf)
const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = { {
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux" unsigned int loc = get_defcfg_location(def_conf);
}; unsigned int conn = get_defcfg_connect(def_conf);
EXPORT_SYMBOL_HDA(auto_pin_cfg_labels); if (conn == AC_JACK_PORT_NONE)
return INPUT_PIN_ATTR_UNUSED;
/* Windows may claim the internal mic to be BOTH, too */
if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
return INPUT_PIN_ATTR_INT;
if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
return INPUT_PIN_ATTR_INT;
if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
return INPUT_PIN_ATTR_DOCK;
if (loc == AC_JACK_LOC_REAR)
return INPUT_PIN_ATTR_REAR;
if (loc == AC_JACK_LOC_FRONT)
return INPUT_PIN_ATTR_FRONT;
return INPUT_PIN_ATTR_NORMAL;
}
EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr);
/**
* hda_get_input_pin_label - Give a label for the given input pin
*
* When check_location is true, the function checks the pin location
* for mic and line-in pins, and set an appropriate prefix like "Front",
* "Rear", "Internal".
*/
const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
int check_location)
{
unsigned int def_conf;
static const char *mic_names[] = {
"Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
};
int attr;
def_conf = snd_hda_codec_get_pincfg(codec, pin);
switch (get_defcfg_device(def_conf)) {
case AC_JACK_MIC_IN:
if (!check_location)
return "Mic";
attr = snd_hda_get_input_pin_attr(def_conf);
if (!attr)
return "None";
return mic_names[attr - 1];
case AC_JACK_LINE_IN:
if (!check_location)
return "Line";
attr = snd_hda_get_input_pin_attr(def_conf);
if (!attr)
return "None";
if (attr == INPUT_PIN_ATTR_DOCK)
return "Dock Line";
return "Line";
case AC_JACK_AUX:
return "Aux";
case AC_JACK_CD:
return "CD";
case AC_JACK_SPDIF_IN:
return "SPDIF In";
case AC_JACK_DIG_OTHER_IN:
return "Digital In";
default:
return "Misc";
}
}
EXPORT_SYMBOL_HDA(hda_get_input_pin_label);
/* Check whether the location prefix needs to be added to the label.
* If all mic-jacks are in the same location (e.g. rear panel), we don't
* have to put "Front" prefix to each label. In such a case, returns false.
*/
static int check_mic_location_need(struct hda_codec *codec,
const struct auto_pin_cfg *cfg,
int input)
{
unsigned int defc;
int i, attr, attr2;
defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
attr = snd_hda_get_input_pin_attr(defc);
/* for internal or docking mics, we need locations */
if (attr <= INPUT_PIN_ATTR_NORMAL)
return 1;
attr = 0;
for (i = 0; i < cfg->num_inputs; i++) {
defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
attr2 = snd_hda_get_input_pin_attr(defc);
if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
if (attr && attr != attr2)
return 1; /* different locations found */
attr = attr2;
}
}
return 0;
}
/**
* hda_get_autocfg_input_label - Get a label for the given input
*
* Get a label for the given input pin defined by the autocfg item.
* Unlike hda_get_input_pin_label(), this function checks all inputs
* defined in autocfg and avoids the redundant mic/line prefix as much as
* possible.
*/
const char *hda_get_autocfg_input_label(struct hda_codec *codec,
const struct auto_pin_cfg *cfg,
int input)
{
int type = cfg->inputs[input].type;
int has_multiple_pins = 0;
if ((input > 0 && cfg->inputs[input - 1].type == type) ||
(input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
has_multiple_pins = 1;
if (has_multiple_pins && type == AUTO_PIN_MIC)
has_multiple_pins &= check_mic_location_need(codec, cfg, input);
return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
has_multiple_pins);
}
EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
/**
* snd_hda_add_imux_item - Add an item to input_mux
*
* When the same label is used already in the existing items, the number
* suffix is appended to the label. This label index number is stored
* to type_idx when non-NULL pointer is given.
*/
int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
int index, int *type_idx)
{
int i, label_idx = 0;
if (imux->num_items >= HDA_MAX_NUM_INPUTS) {
snd_printd(KERN_ERR "hda_codec: Too many imux items!\n");
return -EINVAL;
}
for (i = 0; i < imux->num_items; i++) {
if (!strncmp(label, imux->items[i].label, strlen(label)))
label_idx++;
}
if (type_idx)
*type_idx = label_idx;
if (label_idx > 0)
snprintf(imux->items[imux->num_items].label,
sizeof(imux->items[imux->num_items].label),
"%s %d", label, label_idx);
else
strlcpy(imux->items[imux->num_items].label, label,
sizeof(imux->items[imux->num_items].label));
imux->items[imux->num_items].index = index;
imux->num_items++;
return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_add_imux_item);
#ifdef CONFIG_PM #ifdef CONFIG_PM

View File

@ -850,6 +850,7 @@ struct hda_codec {
unsigned int pin_amp_workaround:1; /* pin out-amp takes index unsigned int pin_amp_workaround:1; /* pin out-amp takes index
* (e.g. Conexant codecs) * (e.g. Conexant codecs)
*/ */
unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */
unsigned int pins_shutup:1; /* pins are shut up */ unsigned int pins_shutup:1; /* pins are shut up */
unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */ unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
#ifdef CONFIG_SND_HDA_POWER_SAVE #ifdef CONFIG_SND_HDA_POWER_SAVE
@ -989,6 +990,18 @@ int snd_hda_suspend(struct hda_bus *bus);
int snd_hda_resume(struct hda_bus *bus); int snd_hda_resume(struct hda_bus *bus);
#endif #endif
#ifdef CONFIG_SND_HDA_POWER_SAVE
static inline
int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
if (codec->patch_ops.check_power_status)
return codec->patch_ops.check_power_status(codec, nid);
return 0;
}
#else
#define hda_call_check_power_status(codec, nid) 0
#endif
/* /*
* get widget information * get widget information
*/ */

View File

@ -332,7 +332,6 @@ 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)
@ -368,7 +367,6 @@ 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)
{ {
@ -407,7 +405,6 @@ 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)
{ {
@ -426,7 +423,6 @@ 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
@ -585,7 +581,6 @@ 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)
{ {
@ -594,7 +589,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 */
@ -645,4 +639,3 @@ void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
pcm->channels_max = min(pcm->channels_max, codec_pars->channels_max); pcm->channels_max = min(pcm->channels_max, codec_pars->channels_max);
pcm->maxbps = min(pcm->maxbps, codec_pars->maxbps); pcm->maxbps = min(pcm->maxbps, codec_pars->maxbps);
} }
EXPORT_SYMBOL_HDA(hdmi_eld_update_pcm_info);

View File

@ -61,7 +61,6 @@ struct hda_gspec {
struct hda_gnode *cap_vol_node; /* Node for capture volume */ struct hda_gnode *cap_vol_node; /* Node for capture volume */
unsigned int cur_cap_src; /* current capture source */ unsigned int cur_cap_src; /* current capture source */
struct hda_input_mux input_mux; struct hda_input_mux input_mux;
char cap_labels[HDA_MAX_NUM_INPUTS][16];
unsigned int def_amp_in_caps; unsigned int def_amp_in_caps;
unsigned int def_amp_out_caps; unsigned int def_amp_out_caps;
@ -506,11 +505,10 @@ static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
* returns 0 if not found, 1 if found, or a negative error code. * returns 0 if not found, 1 if found, or a negative error code.
*/ */
static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec, static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
struct hda_gnode *node) struct hda_gnode *node, int idx)
{ {
int i, err; int i, err;
unsigned int pinctl; unsigned int pinctl;
char *label;
const char *type; const char *type;
if (node->checked) if (node->checked)
@ -523,7 +521,7 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
child = hda_get_node(spec, node->conn_list[i]); child = hda_get_node(spec, node->conn_list[i]);
if (! child) if (! child)
continue; continue;
err = parse_adc_sub_nodes(codec, spec, child); err = parse_adc_sub_nodes(codec, spec, child, idx);
if (err < 0) if (err < 0)
return err; return err;
if (err > 0) { if (err > 0) {
@ -564,9 +562,7 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
return 0; return 0;
type = "Input"; type = "Input";
} }
label = spec->cap_labels[spec->input_mux.num_items]; snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL);
strcpy(label, type);
spec->input_mux.items[spec->input_mux.num_items].label = label;
/* unmute the PIN external input */ /* unmute the PIN external input */
unmute_input(codec, node, 0); /* index = 0? */ unmute_input(codec, node, 0); /* index = 0? */
@ -577,29 +573,6 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
return 1; /* found */ return 1; /* found */
} }
/* add a capture source element */
static void add_cap_src(struct hda_gspec *spec, int idx)
{
struct hda_input_mux_item *csrc;
char *buf;
int num, ocap;
num = spec->input_mux.num_items;
csrc = &spec->input_mux.items[num];
buf = spec->cap_labels[num];
for (ocap = 0; ocap < num; ocap++) {
if (! strcmp(buf, spec->cap_labels[ocap])) {
/* same label already exists,
* put the index number to be unique
*/
sprintf(buf, "%s %d", spec->cap_labels[ocap], num);
break;
}
}
csrc->index = idx;
spec->input_mux.num_items++;
}
/* /*
* parse input * parse input
*/ */
@ -624,22 +597,18 @@ static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
for (i = 0; i < adc_node->nconns; i++) { for (i = 0; i < adc_node->nconns; i++) {
node = hda_get_node(spec, adc_node->conn_list[i]); node = hda_get_node(spec, adc_node->conn_list[i]);
if (node && node->type == AC_WID_PIN) { if (node && node->type == AC_WID_PIN) {
err = parse_adc_sub_nodes(codec, spec, node); err = parse_adc_sub_nodes(codec, spec, node, i);
if (err < 0) if (err < 0)
return err; return err;
else if (err > 0)
add_cap_src(spec, i);
} }
} }
/* ... then check the rests, more complicated connections */ /* ... then check the rests, more complicated connections */
for (i = 0; i < adc_node->nconns; i++) { for (i = 0; i < adc_node->nconns; i++) {
node = hda_get_node(spec, adc_node->conn_list[i]); node = hda_get_node(spec, adc_node->conn_list[i]);
if (node && node->type != AC_WID_PIN) { if (node && node->type != AC_WID_PIN) {
err = parse_adc_sub_nodes(codec, spec, node); err = parse_adc_sub_nodes(codec, spec, node, i);
if (err < 0) if (err < 0)
return err; return err;
else if (err > 0)
add_cap_src(spec, i);
} }
} }

View File

@ -78,8 +78,8 @@ MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
module_param_array(model, charp, NULL, 0444); module_param_array(model, charp, NULL, 0444);
MODULE_PARM_DESC(model, "Use the given board model."); MODULE_PARM_DESC(model, "Use the given board model.");
module_param_array(position_fix, int, NULL, 0444); module_param_array(position_fix, int, NULL, 0444);
MODULE_PARM_DESC(position_fix, "Fix DMA pointer " MODULE_PARM_DESC(position_fix, "DMA pointer read method."
"(0 = auto, 1 = none, 2 = POSBUF)."); "(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO).");
module_param_array(bdl_pos_adj, int, NULL, 0644); module_param_array(bdl_pos_adj, int, NULL, 0644);
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset."); MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
module_param_array(probe_mask, int, NULL, 0444); module_param_array(probe_mask, int, NULL, 0444);
@ -305,6 +305,7 @@ enum {
POS_FIX_AUTO, POS_FIX_AUTO,
POS_FIX_LPIB, POS_FIX_LPIB,
POS_FIX_POSBUF, POS_FIX_POSBUF,
POS_FIX_VIACOMBO,
}; };
/* Defines for ATI HD Audio support in SB450 south bridge */ /* Defines for ATI HD Audio support in SB450 south bridge */
@ -433,7 +434,6 @@ struct azx {
unsigned int polling_mode :1; unsigned int polling_mode :1;
unsigned int msi :1; unsigned int msi :1;
unsigned int irq_pending_warned :1; unsigned int irq_pending_warned :1;
unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */
unsigned int probing :1; /* codec probing phase */ unsigned int probing :1; /* codec probing phase */
/* for debugging */ /* for debugging */
@ -458,6 +458,7 @@ enum {
AZX_DRIVER_ULI, AZX_DRIVER_ULI,
AZX_DRIVER_NVIDIA, AZX_DRIVER_NVIDIA,
AZX_DRIVER_TERA, AZX_DRIVER_TERA,
AZX_DRIVER_CTX,
AZX_DRIVER_GENERIC, AZX_DRIVER_GENERIC,
AZX_NUM_DRIVERS, /* keep this as last entry */ AZX_NUM_DRIVERS, /* keep this as last entry */
}; };
@ -473,6 +474,7 @@ static char *driver_short_names[] __devinitdata = {
[AZX_DRIVER_ULI] = "HDA ULI M5461", [AZX_DRIVER_ULI] = "HDA ULI M5461",
[AZX_DRIVER_NVIDIA] = "HDA NVidia", [AZX_DRIVER_NVIDIA] = "HDA NVidia",
[AZX_DRIVER_TERA] = "HDA Teradici", [AZX_DRIVER_TERA] = "HDA Teradici",
[AZX_DRIVER_CTX] = "HDA Creative",
[AZX_DRIVER_GENERIC] = "HD-Audio Generic", [AZX_DRIVER_GENERIC] = "HD-Audio Generic",
}; };
@ -563,7 +565,10 @@ static void azx_init_cmd_io(struct azx *chip)
/* reset the rirb hw write pointer */ /* reset the rirb hw write pointer */
azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST); azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST);
/* set N=1, get RIRB response interrupt for new entry */ /* set N=1, get RIRB response interrupt for new entry */
azx_writew(chip, RINTCNT, 1); if (chip->driver_type == AZX_DRIVER_CTX)
azx_writew(chip, RINTCNT, 0xc0);
else
azx_writew(chip, RINTCNT, 1);
/* enable rirb dma and response irq */ /* enable rirb dma and response irq */
azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN); azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
spin_unlock_irq(&chip->reg_lock); spin_unlock_irq(&chip->reg_lock);
@ -1136,8 +1141,11 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
/* clear rirb int */ /* clear rirb int */
status = azx_readb(chip, RIRBSTS); status = azx_readb(chip, RIRBSTS);
if (status & RIRB_INT_MASK) { if (status & RIRB_INT_MASK) {
if (status & RIRB_INT_RESPONSE) if (status & RIRB_INT_RESPONSE) {
if (chip->driver_type == AZX_DRIVER_CTX)
udelay(80);
azx_update_rirb(chip); azx_update_rirb(chip);
}
azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
} }
@ -1309,11 +1317,8 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr)); azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr));
/* enable the position buffer */ /* enable the position buffer */
if (chip->position_fix[0] == POS_FIX_POSBUF || if (chip->position_fix[0] != POS_FIX_LPIB ||
chip->position_fix[0] == POS_FIX_AUTO || chip->position_fix[1] != POS_FIX_LPIB) {
chip->position_fix[1] == POS_FIX_POSBUF ||
chip->position_fix[1] == POS_FIX_AUTO ||
chip->via_dmapos_patch) {
if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
azx_writel(chip, DPLBASE, azx_writel(chip, DPLBASE,
(u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
@ -1647,7 +1652,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
struct azx_dev *azx_dev = get_azx_dev(substream); struct azx_dev *azx_dev = get_azx_dev(substream);
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int bufsize, period_bytes, format_val; unsigned int bufsize, period_bytes, format_val, stream_tag;
int err; int err;
azx_stream_reset(chip, azx_dev); azx_stream_reset(chip, azx_dev);
@ -1689,7 +1694,12 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
else else
azx_dev->fifo_size = 0; azx_dev->fifo_size = 0;
return snd_hda_codec_prepare(apcm->codec, hinfo, azx_dev->stream_tag, stream_tag = azx_dev->stream_tag;
/* CA-IBG chips need the playback stream starting from 1 */
if (chip->driver_type == AZX_DRIVER_CTX &&
stream_tag > chip->capture_streams)
stream_tag -= chip->capture_streams;
return snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
azx_dev->format_val, substream); azx_dev->format_val, substream);
} }
@ -1852,20 +1862,21 @@ static unsigned int azx_get_position(struct azx *chip,
struct azx_dev *azx_dev) struct azx_dev *azx_dev)
{ {
unsigned int pos; unsigned int pos;
int stream = azx_dev->substream->stream;
if (chip->via_dmapos_patch) switch (chip->position_fix[stream]) {
case POS_FIX_LPIB:
/* read LPIB */
pos = azx_sd_readl(azx_dev, SD_LPIB);
break;
case POS_FIX_VIACOMBO:
pos = azx_via_get_position(chip, azx_dev); pos = azx_via_get_position(chip, azx_dev);
else { break;
int stream = azx_dev->substream->stream; default:
if (chip->position_fix[stream] == POS_FIX_POSBUF || /* use the position buffer */
chip->position_fix[stream] == POS_FIX_AUTO) { pos = le32_to_cpu(*azx_dev->posbuf);
/* use the position buffer */
pos = le32_to_cpu(*azx_dev->posbuf);
} else {
/* read LPIB */
pos = azx_sd_readl(azx_dev, SD_LPIB);
}
} }
if (pos >= azx_dev->bufsize) if (pos >= azx_dev->bufsize)
pos = 0; pos = 0;
return pos; return pos;
@ -2313,19 +2324,10 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
switch (fix) { switch (fix) {
case POS_FIX_LPIB: case POS_FIX_LPIB:
case POS_FIX_POSBUF: case POS_FIX_POSBUF:
case POS_FIX_VIACOMBO:
return fix; return fix;
} }
/* Check VIA/ATI HD Audio Controller exist */
switch (chip->driver_type) {
case AZX_DRIVER_VIA:
case AZX_DRIVER_ATI:
chip->via_dmapos_patch = 1;
/* Use link position directly, avoid any transfer problem. */
return POS_FIX_LPIB;
}
chip->via_dmapos_patch = 0;
q = snd_pci_quirk_lookup(chip->pci, position_fix_list); q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
if (q) { if (q) {
printk(KERN_INFO printk(KERN_INFO
@ -2334,6 +2336,15 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
q->value, q->subvendor, q->subdevice); q->value, q->subvendor, q->subdevice);
return q->value; return q->value;
} }
/* Check VIA/ATI HD Audio Controller exist */
switch (chip->driver_type) {
case AZX_DRIVER_VIA:
case AZX_DRIVER_ATI:
/* Use link position directly, avoid any transfer problem. */
return POS_FIX_VIACOMBO;
}
return POS_FIX_AUTO; return POS_FIX_AUTO;
} }
@ -2735,25 +2746,17 @@ static void __devexit azx_remove(struct pci_dev *pci)
/* PCI IDs */ /* PCI IDs */
static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
/* ICH 6..10 */
{ PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH },
{ PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH },
{ PCI_DEVICE(0x8086, 0x269a), .driver_data = AZX_DRIVER_ICH },
{ PCI_DEVICE(0x8086, 0x284b), .driver_data = AZX_DRIVER_ICH },
{ PCI_DEVICE(0x8086, 0x2911), .driver_data = AZX_DRIVER_ICH },
{ PCI_DEVICE(0x8086, 0x293e), .driver_data = AZX_DRIVER_ICH },
{ PCI_DEVICE(0x8086, 0x293f), .driver_data = AZX_DRIVER_ICH },
{ PCI_DEVICE(0x8086, 0x3a3e), .driver_data = AZX_DRIVER_ICH },
{ PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH },
/* PCH */
{ PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_ICH },
{ PCI_DEVICE(0x8086, 0x3b57), .driver_data = AZX_DRIVER_ICH },
/* CPT */ /* CPT */
{ PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH }, { PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH },
/* PBG */ /* PBG */
{ PCI_DEVICE(0x8086, 0x1d20), .driver_data = AZX_DRIVER_PCH }, { PCI_DEVICE(0x8086, 0x1d20), .driver_data = AZX_DRIVER_PCH },
/* SCH */ /* SCH */
{ PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH }, { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
/* Generic Intel */
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
.driver_data = AZX_DRIVER_ICH },
/* ATI SB 450/600 */ /* ATI SB 450/600 */
{ PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI }, { PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI },
{ PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI }, { PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI },
@ -2794,11 +2797,13 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID), { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff, .class_mask = 0xffffff,
.driver_data = AZX_DRIVER_GENERIC }, .driver_data = AZX_DRIVER_CTX },
#else #else
/* this entry seems still valid -- i.e. without emu20kx chip */ /* this entry seems still valid -- i.e. without emu20kx chip */
{ PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC }, { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_CTX },
#endif #endif
/* Vortex86MX */
{ PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
/* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */ /* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID), { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,

View File

@ -38,10 +38,11 @@
*/ */
#define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs) \ #define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs) \
((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23)) ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23))
#define HDA_AMP_VAL_MIN_MUTE (1<<29)
#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \ #define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \
HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0) HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0)
/* mono volume with index (index=0,1,...) (channel=1,2) */ /* mono volume with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, dir, flags) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
.subdevice = HDA_SUBDEV_AMP_FLAG, \ .subdevice = HDA_SUBDEV_AMP_FLAG, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
@ -51,16 +52,20 @@
.get = snd_hda_mixer_amp_volume_get, \ .get = snd_hda_mixer_amp_volume_get, \
.put = snd_hda_mixer_amp_volume_put, \ .put = snd_hda_mixer_amp_volume_put, \
.tlv = { .c = snd_hda_mixer_amp_tlv }, \ .tlv = { .c = snd_hda_mixer_amp_tlv }, \
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, dir) | flags }
/* stereo volume with index */ /* stereo volume with index */
#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \ #define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \
HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction) HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction, 0)
/* mono volume */ /* mono volume */
#define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \ #define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \
HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction) HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction, 0)
/* stereo volume */ /* stereo volume */
#define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \ #define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \
HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction) HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction)
/* stereo volume with min=mute */
#define HDA_CODEC_VOLUME_MIN_MUTE(xname, nid, xindex, direction) \
HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, 3, xindex, direction, \
HDA_AMP_VAL_MIN_MUTE)
/* mono mute switch with index (index=0,1,...) (channel=1,2) */ /* mono mute switch with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
@ -215,7 +220,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
*/ */
#define HDA_MAX_NUM_INPUTS 16 #define HDA_MAX_NUM_INPUTS 16
struct hda_input_mux_item { struct hda_input_mux_item {
const char *label; char label[32];
unsigned int index; unsigned int index;
}; };
struct hda_input_mux { struct hda_input_mux {
@ -366,9 +371,7 @@ struct hda_bus_unsolicited {
enum { enum {
AUTO_PIN_MIC, AUTO_PIN_MIC,
AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE_IN,
AUTO_PIN_LINE,
AUTO_PIN_FRONT_LINE,
AUTO_PIN_CD, AUTO_PIN_CD,
AUTO_PIN_AUX, AUTO_PIN_AUX,
AUTO_PIN_LAST AUTO_PIN_LAST
@ -380,9 +383,33 @@ enum {
AUTO_PIN_HP_OUT AUTO_PIN_HP_OUT
}; };
extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST];
#define AUTO_CFG_MAX_OUTS 5 #define AUTO_CFG_MAX_OUTS 5
#define AUTO_CFG_MAX_INS 8
struct auto_pin_cfg_item {
hda_nid_t pin;
int type;
};
struct auto_pin_cfg;
const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
int check_location);
const char *hda_get_autocfg_input_label(struct hda_codec *codec,
const struct auto_pin_cfg *cfg,
int input);
int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
int index, int *type_index_ret);
enum {
INPUT_PIN_ATTR_UNUSED, /* pin not connected */
INPUT_PIN_ATTR_INT, /* internal mic/line-in */
INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */
INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */
INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */
INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */
};
int snd_hda_get_input_pin_attr(unsigned int def_conf);
struct auto_pin_cfg { struct auto_pin_cfg {
int line_outs; int line_outs;
@ -393,7 +420,8 @@ struct auto_pin_cfg {
int hp_outs; int hp_outs;
int line_out_type; /* AUTO_PIN_XXX_OUT */ int line_out_type; /* AUTO_PIN_XXX_OUT */
hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS]; hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
hda_nid_t input_pins[AUTO_PIN_LAST]; int num_inputs;
struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
int dig_outs; int dig_outs;
hda_nid_t dig_out_pins[2]; hda_nid_t dig_out_pins[2];
hda_nid_t dig_in_pin; hda_nid_t dig_in_pin;
@ -558,6 +586,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1) #define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) #define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f) #define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f)
#define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1)
/* /*
* CEA Short Audio Descriptor data * CEA Short Audio Descriptor data

View File

@ -1276,6 +1276,7 @@ static int patch_ad1986a(struct hda_codec *codec)
spec->multiout.no_share_stream = 1; spec->multiout.no_share_stream = 1;
codec->no_trigger_sense = 1; codec->no_trigger_sense = 1;
codec->no_sticky_stream = 1;
return 0; return 0;
} }
@ -1463,6 +1464,7 @@ static int patch_ad1983(struct hda_codec *codec)
codec->patch_ops = ad198x_patch_ops; codec->patch_ops = ad198x_patch_ops;
codec->no_trigger_sense = 1; codec->no_trigger_sense = 1;
codec->no_sticky_stream = 1;
return 0; return 0;
} }
@ -1917,6 +1919,7 @@ static int patch_ad1981(struct hda_codec *codec)
} }
codec->no_trigger_sense = 1; codec->no_trigger_sense = 1;
codec->no_sticky_stream = 1;
return 0; return 0;
} }
@ -2880,7 +2883,7 @@ static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
/* create input playback/capture controls for the given pin */ /* create input playback/capture controls for the given pin */
static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin, static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
const char *ctlname, int boost) const char *ctlname, int ctlidx, int boost)
{ {
char name[32]; char name[32];
int err, idx; int err, idx;
@ -2909,25 +2912,27 @@ static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
} }
/* create playback/capture controls for input pins */ /* create playback/capture controls for input pins */
static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec, static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg) const struct auto_pin_cfg *cfg)
{ {
struct ad198x_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux; struct hda_input_mux *imux = &spec->private_imux;
int i, err; int i, err, type, type_idx;
for (i = 0; i < AUTO_PIN_LAST; i++) { for (i = 0; i < cfg->num_inputs; i++) {
err = new_analog_input(spec, cfg->input_pins[i], const char *label;
auto_pin_cfg_labels[i], type = cfg->inputs[i].type;
i <= AUTO_PIN_FRONT_MIC); label = hda_get_autocfg_input_label(codec, cfg, i);
snd_hda_add_imux_item(imux, label,
ad1988_pin_to_adc_idx(cfg->inputs[i].pin),
&type_idx);
err = new_analog_input(spec, cfg->inputs[i].pin,
label, type_idx,
type == AUTO_PIN_MIC);
if (err < 0) if (err < 0)
return err; return err;
imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
imux->num_items++;
} }
imux->items[imux->num_items].label = "Mix"; snd_hda_add_imux_item(imux, "Mix", 9, NULL);
imux->items[imux->num_items].index = 9;
imux->num_items++;
if ((err = add_control(spec, AD_CTL_WIDGET_VOL, if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
"Analog Mix Playback Volume", "Analog Mix Playback Volume",
@ -2994,12 +2999,11 @@ static void ad1988_auto_init_extra_out(struct hda_codec *codec)
static void ad1988_auto_init_analog_input(struct hda_codec *codec) static void ad1988_auto_init_analog_input(struct hda_codec *codec)
{ {
struct ad198x_spec *spec = codec->spec; struct ad198x_spec *spec = codec->spec;
const struct auto_pin_cfg *cfg = &spec->autocfg;
int i, idx; int i, idx;
for (i = 0; i < AUTO_PIN_LAST; i++) { for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t nid = spec->autocfg.input_pins[i]; hda_nid_t nid = cfg->inputs[i].pin;
if (! nid)
continue;
switch (nid) { switch (nid) {
case 0x15: /* port-C */ case 0x15: /* port-C */
snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0); snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
@ -3009,7 +3013,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec)
break; break;
} }
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN); i == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
if (nid != AD1988_PIN_CD_NID) if (nid != AD1988_PIN_CD_NID)
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_MUTE); AMP_OUT_MUTE);
@ -3040,7 +3044,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
"Speaker")) < 0 || "Speaker")) < 0 ||
(err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
"Headphone")) < 0 || "Headphone")) < 0 ||
(err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0) (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
return err; return err;
spec->multiout.max_channels = spec->multiout.num_dacs * 2; spec->multiout.max_channels = spec->multiout.num_dacs * 2;
@ -3235,6 +3239,7 @@ static int patch_ad1988(struct hda_codec *codec)
spec->vmaster_nid = 0x04; spec->vmaster_nid = 0x04;
codec->no_trigger_sense = 1; codec->no_trigger_sense = 1;
codec->no_sticky_stream = 1;
return 0; return 0;
} }
@ -3449,6 +3454,7 @@ static int patch_ad1884(struct hda_codec *codec)
codec->patch_ops = ad198x_patch_ops; codec->patch_ops = ad198x_patch_ops;
codec->no_trigger_sense = 1; codec->no_trigger_sense = 1;
codec->no_sticky_stream = 1;
return 0; return 0;
} }
@ -4422,6 +4428,7 @@ static int patch_ad1884a(struct hda_codec *codec)
} }
codec->no_trigger_sense = 1; codec->no_trigger_sense = 1;
codec->no_sticky_stream = 1;
return 0; return 0;
} }
@ -4761,6 +4768,7 @@ static int patch_ad1882(struct hda_codec *codec)
} }
codec->no_trigger_sense = 1; codec->no_trigger_sense = 1;
codec->no_sticky_stream = 1;
return 0; return 0;
} }

View File

@ -1,224 +0,0 @@
/*
* Universal Interface for Intel High Definition Audio Codec
*
* HD audio interface patch for ATI HDMI codecs
*
* Copyright (c) 2006 ATI Technologies Inc.
*
*
* This driver 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 driver 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
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
struct atihdmi_spec {
struct hda_multi_out multiout;
struct hda_pcm pcm_rec;
};
#define CVT_NID 0x02 /* audio converter */
#define PIN_NID 0x03 /* HDMI output pin */
static struct hda_verb atihdmi_basic_init[] = {
/* enable digital output on pin widget */
{ 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
{} /* terminator */
};
/*
* Controls
*/
static int atihdmi_build_controls(struct hda_codec *codec)
{
struct atihdmi_spec *spec = codec->spec;
int err;
err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
if (err < 0)
return err;
return 0;
}
static int atihdmi_init(struct hda_codec *codec)
{
snd_hda_sequence_write(codec, atihdmi_basic_init);
/* SI codec requires to unmute the pin */
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);
return 0;
}
/*
* Digital out
*/
static int atihdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct atihdmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
}
static int atihdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct atihdmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}
static int atihdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
struct atihdmi_spec *spec = codec->spec;
int chans = substream->runtime->channels;
int i, err;
err = snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
format, substream);
if (err < 0)
return err;
snd_hda_codec_write(codec, CVT_NID, 0, AC_VERB_SET_CVT_CHAN_COUNT,
chans - 1);
/* FIXME: XXX */
for (i = 0; i < chans; i++) {
snd_hda_codec_write(codec, CVT_NID, 0,
AC_VERB_SET_HDMI_CHAN_SLOT,
(i << 4) | i);
}
return 0;
}
static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
.nid = CVT_NID, /* NID to query formats and rates and setup streams */
.ops = {
.open = atihdmi_dig_playback_pcm_open,
.close = atihdmi_dig_playback_pcm_close,
.prepare = atihdmi_dig_playback_pcm_prepare
},
};
static int atihdmi_build_pcms(struct hda_codec *codec)
{
struct atihdmi_spec *spec = codec->spec;
struct hda_pcm *info = &spec->pcm_rec;
unsigned int chans;
codec->num_pcms = 1;
codec->pcm_info = info;
info->name = "ATI HDMI";
info->pcm_type = HDA_PCM_TYPE_HDMI;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback;
/* FIXME: we must check ELD and change the PCM parameters dynamically
*/
chans = get_wcaps(codec, CVT_NID);
chans = get_wcaps_channels(chans);
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
return 0;
}
static void atihdmi_free(struct hda_codec *codec)
{
kfree(codec->spec);
}
static struct hda_codec_ops atihdmi_patch_ops = {
.build_controls = atihdmi_build_controls,
.build_pcms = atihdmi_build_pcms,
.init = atihdmi_init,
.free = atihdmi_free,
};
static int patch_atihdmi(struct hda_codec *codec)
{
struct atihdmi_spec *spec;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
codec->spec = spec;
spec->multiout.num_dacs = 0; /* no analog */
spec->multiout.max_channels = 2;
/* NID for copying analog to digital,
* seems to be unused in pure-digital
* case.
*/
spec->multiout.dig_out_nid = CVT_NID;
codec->patch_ops = atihdmi_patch_ops;
return 0;
}
/*
* patch entries
*/
static struct hda_codec_preset snd_hda_preset_atihdmi[] = {
{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi },
{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi },
{} /* terminator */
};
MODULE_ALIAS("snd-hda-codec-id:1002793c");
MODULE_ALIAS("snd-hda-codec-id:10027919");
MODULE_ALIAS("snd-hda-codec-id:1002791a");
MODULE_ALIAS("snd-hda-codec-id:1002aa01");
MODULE_ALIAS("snd-hda-codec-id:10951390");
MODULE_ALIAS("snd-hda-codec-id:17e80047");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ATI HDMI HD-audio codec");
static struct hda_codec_preset_list atihdmi_list = {
.preset = snd_hda_preset_atihdmi,
.owner = THIS_MODULE,
};
static int __init patch_atihdmi_init(void)
{
return snd_hda_add_codec_preset(&atihdmi_list);
}
static void __exit patch_atihdmi_exit(void)
{
snd_hda_delete_codec_preset(&atihdmi_list);
}
module_init(patch_atihdmi_init)
module_exit(patch_atihdmi_exit)

View File

@ -468,13 +468,13 @@ static void parse_input(struct hda_codec *codec)
spec->dig_in = nid; spec->dig_in = nid;
continue; continue;
} }
for (j = 0; j < AUTO_PIN_LAST; j++) for (j = 0; j < cfg->num_inputs; j++)
if (cfg->input_pins[j] == pin) if (cfg->inputs[j].pin == pin)
break; break;
if (j >= AUTO_PIN_LAST) if (j >= cfg->num_inputs)
continue; continue;
spec->input_pins[n] = pin; spec->input_pins[n] = pin;
spec->input_labels[n] = auto_pin_cfg_labels[j]; spec->input_labels[n] = hda_get_input_pin_label(codec, pin, 1);
spec->adcs[n] = nid; spec->adcs[n] = nid;
n++; n++;
} }
@ -489,7 +489,7 @@ static void parse_digital(struct hda_codec *codec)
if (cfg->dig_outs && if (cfg->dig_outs &&
snd_hda_get_connections(codec, cfg->dig_out_pins[0], snd_hda_get_connections(codec, cfg->dig_out_pins[0],
&spec->dig_out, 1) == 1) &spec->dig_out, 1) == 1)
spec->multiout.dig_out_nid = cfg->dig_out_pins[0]; spec->multiout.dig_out_nid = spec->dig_out;
} }
static int ca0110_parse_auto_config(struct hda_codec *codec) static int ca0110_parse_auto_config(struct hda_codec *codec)

View File

@ -65,6 +65,7 @@ struct cs_spec {
/* available models */ /* available models */
enum { enum {
CS420X_MBP53,
CS420X_MBP55, CS420X_MBP55,
CS420X_IMAC27, CS420X_IMAC27,
CS420X_AUTO, CS420X_AUTO,
@ -329,12 +330,12 @@ static int is_ext_mic(struct hda_codec *codec, unsigned int idx)
{ {
struct cs_spec *spec = codec->spec; struct cs_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg; struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t pin = cfg->input_pins[idx]; hda_nid_t pin = cfg->inputs[idx].pin;
unsigned int val = snd_hda_query_pin_caps(codec, pin); unsigned int val = snd_hda_query_pin_caps(codec, pin);
if (!(val & AC_PINCAP_PRES_DETECT)) if (!(val & AC_PINCAP_PRES_DETECT))
return 0; return 0;
val = snd_hda_codec_get_pincfg(codec, pin); val = snd_hda_codec_get_pincfg(codec, pin);
return (get_defcfg_connect(val) == AC_JACK_PORT_COMPLEX); return (snd_hda_get_input_pin_attr(val) != INPUT_PIN_ATTR_INT);
} }
static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin, static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin,
@ -424,10 +425,8 @@ static int parse_input(struct hda_codec *codec)
struct auto_pin_cfg *cfg = &spec->autocfg; struct auto_pin_cfg *cfg = &spec->autocfg;
int i; int i;
for (i = 0; i < AUTO_PIN_LAST; i++) { for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t pin = cfg->input_pins[i]; hda_nid_t pin = cfg->inputs[i].pin;
if (!pin)
continue;
spec->input_idx[spec->num_inputs] = i; spec->input_idx[spec->num_inputs] = i;
spec->capsrc_idx[i] = spec->num_inputs++; spec->capsrc_idx[i] = spec->num_inputs++;
spec->cur_input = i; spec->cur_input = i;
@ -438,16 +437,17 @@ static int parse_input(struct hda_codec *codec)
/* check whether the automatic mic switch is available */ /* check whether the automatic mic switch is available */
if (spec->num_inputs == 2 && if (spec->num_inputs == 2 &&
spec->adc_nid[AUTO_PIN_MIC] && spec->adc_nid[AUTO_PIN_FRONT_MIC]) { cfg->inputs[0].type == AUTO_PIN_MIC &&
if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_FRONT_MIC])) { cfg->inputs[1].type == AUTO_PIN_MIC) {
if (!is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) { if (is_ext_mic(codec, cfg->inputs[0].pin)) {
if (!is_ext_mic(codec, cfg->inputs[1].pin)) {
spec->mic_detect = 1; spec->mic_detect = 1;
spec->automic_idx = AUTO_PIN_FRONT_MIC; spec->automic_idx = 0;
} }
} else { } else {
if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) { if (is_ext_mic(codec, cfg->inputs[1].pin)) {
spec->mic_detect = 1; spec->mic_detect = 1;
spec->automic_idx = AUTO_PIN_MIC; spec->automic_idx = 1;
} }
} }
} }
@ -674,6 +674,7 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
{ {
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct cs_spec *spec = codec->spec; struct cs_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int idx; unsigned int idx;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
@ -682,7 +683,8 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
if (uinfo->value.enumerated.item >= spec->num_inputs) if (uinfo->value.enumerated.item >= spec->num_inputs)
uinfo->value.enumerated.item = spec->num_inputs - 1; uinfo->value.enumerated.item = spec->num_inputs - 1;
idx = spec->input_idx[uinfo->value.enumerated.item]; idx = spec->input_idx[uinfo->value.enumerated.item];
strcpy(uinfo->value.enumerated.name, auto_pin_cfg_labels[idx]); strcpy(uinfo->value.enumerated.name,
hda_get_input_pin_label(codec, cfg->inputs[idx].pin, 1));
return 0; return 0;
} }
@ -740,6 +742,27 @@ static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec,
return bind; return bind;
} }
/* add a (input-boost) volume control to the given input pin */
static int add_input_volume_control(struct hda_codec *codec,
struct auto_pin_cfg *cfg,
int item)
{
hda_nid_t pin = cfg->inputs[item].pin;
u32 caps;
const char *label;
struct snd_kcontrol *kctl;
if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP))
return 0;
caps = query_amp_caps(codec, pin, HDA_INPUT);
caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
if (caps <= 1)
return 0;
label = hda_get_autocfg_input_label(codec, cfg, item);
return add_volume(codec, label, 0,
HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl);
}
static int build_input(struct hda_codec *codec) static int build_input(struct hda_codec *codec)
{ {
struct cs_spec *spec = codec->spec; struct cs_spec *spec = codec->spec;
@ -779,6 +802,12 @@ static int build_input(struct hda_codec *codec)
return err; return err;
} }
for (i = 0; i < spec->num_inputs; i++) {
err = add_input_volume_control(codec, &spec->autocfg, i);
if (err < 0)
return err;
}
return 0; return 0;
} }
@ -838,7 +867,8 @@ static void cs_automute(struct hda_codec *codec)
AC_VERB_SET_PIN_WIDGET_CONTROL, AC_VERB_SET_PIN_WIDGET_CONTROL,
hp_present ? 0 : PIN_OUT); hp_present ? 0 : PIN_OUT);
} }
if (spec->board_config == CS420X_MBP55 || if (spec->board_config == CS420X_MBP53 ||
spec->board_config == CS420X_MBP55 ||
spec->board_config == CS420X_IMAC27) { spec->board_config == CS420X_IMAC27) {
unsigned int gpio = hp_present ? 0x02 : 0x08; unsigned int gpio = hp_present ? 0x02 : 0x08;
snd_hda_codec_write(codec, 0x01, 0, snd_hda_codec_write(codec, 0x01, 0,
@ -853,15 +883,12 @@ static void cs_automic(struct hda_codec *codec)
hda_nid_t nid; hda_nid_t nid;
unsigned int present; unsigned int present;
nid = cfg->input_pins[spec->automic_idx]; nid = cfg->inputs[spec->automic_idx].pin;
present = snd_hda_jack_detect(codec, nid); present = snd_hda_jack_detect(codec, nid);
if (present) if (present)
change_cur_input(codec, spec->automic_idx, 0); change_cur_input(codec, spec->automic_idx, 0);
else { else
unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ? change_cur_input(codec, !spec->automic_idx, 0);
AUTO_PIN_FRONT_MIC : AUTO_PIN_MIC;
change_cur_input(codec, imic, 0);
}
} }
/* /*
@ -918,14 +945,14 @@ static void init_input(struct hda_codec *codec)
unsigned int coef; unsigned int coef;
int i; int i;
for (i = 0; i < AUTO_PIN_LAST; i++) { for (i = 0; i < cfg->num_inputs; i++) {
unsigned int ctl; unsigned int ctl;
hda_nid_t pin = cfg->input_pins[i]; hda_nid_t pin = cfg->inputs[i].pin;
if (!pin || !spec->adc_nid[i]) if (!spec->adc_nid[i])
continue; continue;
/* set appropriate pin control and mute first */ /* set appropriate pin control and mute first */
ctl = PIN_IN; ctl = PIN_IN;
if (i <= AUTO_PIN_FRONT_MIC) { if (cfg->inputs[i].type == AUTO_PIN_MIC) {
unsigned int caps = snd_hda_query_pin_caps(codec, pin); unsigned int caps = snd_hda_query_pin_caps(codec, pin);
caps >>= AC_PINCAP_VREF_SHIFT; caps >>= AC_PINCAP_VREF_SHIFT;
if (caps & AC_PINCAP_VREF_80) if (caps & AC_PINCAP_VREF_80)
@ -1130,6 +1157,7 @@ static int cs_parse_auto_config(struct hda_codec *codec)
} }
static const char *cs420x_models[CS420X_MODELS] = { static const char *cs420x_models[CS420X_MODELS] = {
[CS420X_MBP53] = "mbp53",
[CS420X_MBP55] = "mbp55", [CS420X_MBP55] = "mbp55",
[CS420X_IMAC27] = "imac27", [CS420X_IMAC27] = "imac27",
[CS420X_AUTO] = "auto", [CS420X_AUTO] = "auto",
@ -1137,7 +1165,9 @@ static const char *cs420x_models[CS420X_MODELS] = {
static struct snd_pci_quirk cs420x_cfg_tbl[] = { static struct snd_pci_quirk cs420x_cfg_tbl[] = {
SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53),
SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55), SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55),
SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27), SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),
{} /* terminator */ {} /* terminator */
}; };
@ -1147,6 +1177,20 @@ struct cs_pincfg {
u32 val; u32 val;
}; };
static struct cs_pincfg mbp53_pincfgs[] = {
{ 0x09, 0x012b4050 },
{ 0x0a, 0x90100141 },
{ 0x0b, 0x90100140 },
{ 0x0c, 0x018b3020 },
{ 0x0d, 0x90a00110 },
{ 0x0e, 0x400000f0 },
{ 0x0f, 0x01cbe030 },
{ 0x10, 0x014be060 },
{ 0x12, 0x400000f0 },
{ 0x15, 0x400000f0 },
{} /* terminator */
};
static struct cs_pincfg mbp55_pincfgs[] = { static struct cs_pincfg mbp55_pincfgs[] = {
{ 0x09, 0x012b4030 }, { 0x09, 0x012b4030 },
{ 0x0a, 0x90100121 }, { 0x0a, 0x90100121 },
@ -1176,6 +1220,7 @@ static struct cs_pincfg imac27_pincfgs[] = {
}; };
static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = { static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
[CS420X_MBP53] = mbp53_pincfgs,
[CS420X_MBP55] = mbp55_pincfgs, [CS420X_MBP55] = mbp55_pincfgs,
[CS420X_IMAC27] = imac27_pincfgs, [CS420X_IMAC27] = imac27_pincfgs,
}; };
@ -1208,6 +1253,7 @@ static int patch_cs420x(struct hda_codec *codec)
switch (spec->board_config) { switch (spec->board_config) {
case CS420X_IMAC27: case CS420X_IMAC27:
case CS420X_MBP53:
case CS420X_MBP55: case CS420X_MBP55:
/* GPIO1 = headphones */ /* GPIO1 = headphones */
/* GPIO3 = speakers */ /* GPIO3 = speakers */

View File

@ -57,6 +57,12 @@ struct conexant_jack {
}; };
struct pin_dac_pair {
hda_nid_t pin;
hda_nid_t dac;
int type;
};
struct conexant_spec { struct conexant_spec {
struct snd_kcontrol_new *mixers[5]; struct snd_kcontrol_new *mixers[5];
@ -77,6 +83,7 @@ struct conexant_spec {
unsigned int cur_eapd; unsigned int cur_eapd;
unsigned int hp_present; unsigned int hp_present;
unsigned int auto_mic; unsigned int auto_mic;
int auto_mic_ext; /* autocfg.inputs[] index for ext mic */
unsigned int need_dac_fix; unsigned int need_dac_fix;
/* capture */ /* capture */
@ -110,9 +117,12 @@ struct conexant_spec {
struct auto_pin_cfg autocfg; struct auto_pin_cfg autocfg;
struct hda_input_mux private_imux; struct hda_input_mux private_imux;
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
struct pin_dac_pair dac_info[8];
int dac_info_filled;
unsigned int dell_automute;
unsigned int port_d_mode; unsigned int port_d_mode;
unsigned int auto_mute:1; /* used in auto-parser */
unsigned int dell_automute:1;
unsigned int dell_vostro:1; unsigned int dell_vostro:1;
unsigned int ideapad:1; unsigned int ideapad:1;
unsigned int thinkpad:1; unsigned int thinkpad:1;
@ -3065,7 +3075,7 @@ enum {
CXT5066_LAPTOP, /* Laptops w/ EAPD support */ CXT5066_LAPTOP, /* Laptops w/ EAPD support */
CXT5066_DELL_LAPTOP, /* Dell Laptop */ CXT5066_DELL_LAPTOP, /* Dell Laptop */
CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */ CXT5066_DELL_VOSTRO, /* Dell Vostro 1015i */
CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */ CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */
CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */ CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */
CXT5066_HP_LAPTOP, /* HP Laptop */ CXT5066_HP_LAPTOP, /* HP Laptop */
@ -3076,25 +3086,26 @@ static const char *cxt5066_models[CXT5066_MODELS] = {
[CXT5066_LAPTOP] = "laptop", [CXT5066_LAPTOP] = "laptop",
[CXT5066_DELL_LAPTOP] = "dell-laptop", [CXT5066_DELL_LAPTOP] = "dell-laptop",
[CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
[CXT5066_DELL_VOSTO] = "dell-vostro", [CXT5066_DELL_VOSTRO] = "dell-vostro",
[CXT5066_IDEAPAD] = "ideapad", [CXT5066_IDEAPAD] = "ideapad",
[CXT5066_THINKPAD] = "thinkpad", [CXT5066_THINKPAD] = "thinkpad",
[CXT5066_HP_LAPTOP] = "hp-laptop", [CXT5066_HP_LAPTOP] = "hp-laptop",
}; };
static struct snd_pci_quirk cxt5066_cfg_tbl[] = { static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD),
CXT5066_LAPTOP), SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO),
SND_PCI_QUIRK(0x1028, 0x02f5, "Dell", SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
CXT5066_DELL_LAPTOP), CXT5066_DELL_LAPTOP),
SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO),
SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTO),
SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO),
SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP), SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP),
SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5),
SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5),
SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
CXT5066_LAPTOP),
SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD),
@ -3196,7 +3207,7 @@ static int patch_cxt5066(struct hda_codec *codec)
spec->capture_prepare = cxt5066_olpc_capture_prepare; spec->capture_prepare = cxt5066_olpc_capture_prepare;
spec->capture_cleanup = cxt5066_olpc_capture_cleanup; spec->capture_cleanup = cxt5066_olpc_capture_cleanup;
break; break;
case CXT5066_DELL_VOSTO: case CXT5066_DELL_VOSTRO:
codec->patch_ops.init = cxt5066_init; codec->patch_ops.init = cxt5066_init;
codec->patch_ops.unsol_event = cxt5066_vostro_event; codec->patch_ops.unsol_event = cxt5066_vostro_event;
spec->init_verbs[0] = cxt5066_init_verbs_vostro; spec->init_verbs[0] = cxt5066_init_verbs_vostro;
@ -3253,6 +3264,604 @@ static int patch_cxt5066(struct hda_codec *codec)
return 0; return 0;
} }
/*
* Automatic parser for CX20641 & co
*/
static hda_nid_t cx_auto_adc_nids[] = { 0x14 };
/* get the connection index of @nid in the widget @mux */
static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
hda_nid_t nid)
{
hda_nid_t conn[HDA_MAX_NUM_INPUTS];
int i, nums;
nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
for (i = 0; i < nums; i++)
if (conn[i] == nid)
return i;
return -1;
}
/* get an unassigned DAC from the given list.
* Return the nid if found and reduce the DAC list, or return zero if
* not found
*/
static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin,
hda_nid_t *dacs, int *num_dacs)
{
int i, nums = *num_dacs;
hda_nid_t ret = 0;
for (i = 0; i < nums; i++) {
if (get_connection_index(codec, pin, dacs[i]) >= 0) {
ret = dacs[i];
break;
}
}
if (!ret)
return 0;
if (--nums > 0)
memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t));
*num_dacs = nums;
return ret;
}
#define MAX_AUTO_DACS 5
/* fill analog DAC list from the widget tree */
static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs)
{
hda_nid_t nid, end_nid;
int nums = 0;
end_nid = codec->start_nid + codec->num_nodes;
for (nid = codec->start_nid; nid < end_nid; nid++) {
unsigned int wcaps = get_wcaps(codec, nid);
unsigned int type = get_wcaps_type(wcaps);
if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) {
dacs[nums++] = nid;
if (nums >= MAX_AUTO_DACS)
break;
}
}
return nums;
}
/* fill pin_dac_pair list from the pin and dac list */
static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins,
int num_pins, hda_nid_t *dacs, int *rest,
struct pin_dac_pair *filled, int type)
{
int i, nums;
nums = 0;
for (i = 0; i < num_pins; i++) {
filled[nums].pin = pins[i];
filled[nums].type = type;
filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest);
nums++;
}
return nums;
}
/* parse analog output paths */
static void cx_auto_parse_output(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t dacs[MAX_AUTO_DACS];
int i, j, nums, rest;
rest = fill_cx_auto_dacs(codec, dacs);
/* parse all analog output pins */
nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs,
dacs, &rest, spec->dac_info,
AUTO_PIN_LINE_OUT);
nums += fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs,
dacs, &rest, spec->dac_info + nums,
AUTO_PIN_HP_OUT);
nums += fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs,
dacs, &rest, spec->dac_info + nums,
AUTO_PIN_SPEAKER_OUT);
spec->dac_info_filled = nums;
/* fill multiout struct */
for (i = 0; i < nums; i++) {
hda_nid_t dac = spec->dac_info[i].dac;
if (!dac)
continue;
switch (spec->dac_info[i].type) {
case AUTO_PIN_LINE_OUT:
spec->private_dac_nids[spec->multiout.num_dacs] = dac;
spec->multiout.num_dacs++;
break;
case AUTO_PIN_HP_OUT:
case AUTO_PIN_SPEAKER_OUT:
if (!spec->multiout.hp_nid) {
spec->multiout.hp_nid = dac;
break;
}
for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++)
if (!spec->multiout.extra_out_nid[j]) {
spec->multiout.extra_out_nid[j] = dac;
break;
}
break;
}
}
spec->multiout.dac_nids = spec->private_dac_nids;
spec->multiout.max_channels = nums * 2;
if (cfg->hp_outs > 0)
spec->auto_mute = 1;
spec->vmaster_nid = spec->private_dac_nids[0];
}
/* auto-mute/unmute speaker and line outs according to headphone jack */
static void cx_auto_hp_automute(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
int i, present;
if (!spec->auto_mute)
return;
present = 0;
for (i = 0; i < cfg->hp_outs; i++) {
if (snd_hda_jack_detect(codec, cfg->hp_pins[i])) {
present = 1;
break;
}
}
for (i = 0; i < cfg->line_outs; i++) {
snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
present ? 0 : PIN_OUT);
}
for (i = 0; i < cfg->speaker_outs; i++) {
snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
present ? 0 : PIN_OUT);
}
}
/* automatic switch internal and external mic */
static void cx_auto_automic(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
struct hda_input_mux *imux = &spec->private_imux;
int ext_idx = spec->auto_mic_ext;
if (!spec->auto_mic)
return;
if (snd_hda_jack_detect(codec, cfg->inputs[ext_idx].pin)) {
snd_hda_codec_write(codec, spec->adc_nids[0], 0,
AC_VERB_SET_CONNECT_SEL,
imux->items[ext_idx].index);
} else {
snd_hda_codec_write(codec, spec->adc_nids[0], 0,
AC_VERB_SET_CONNECT_SEL,
imux->items[!ext_idx].index);
}
}
static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res)
{
int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20;
switch (res >> 26) {
case CONEXANT_HP_EVENT:
cx_auto_hp_automute(codec);
conexant_report_jack(codec, nid);
break;
case CONEXANT_MIC_EVENT:
cx_auto_automic(codec);
conexant_report_jack(codec, nid);
break;
}
}
/* return true if it's an internal-mic pin */
static int is_int_mic(struct hda_codec *codec, hda_nid_t pin)
{
unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT;
}
/* return true if it's an external-mic pin */
static int is_ext_mic(struct hda_codec *codec, hda_nid_t pin)
{
unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
snd_hda_get_input_pin_attr(def_conf) >= INPUT_PIN_ATTR_NORMAL &&
(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_PRES_DETECT);
}
/* check whether the pin config is suitable for auto-mic switching;
* auto-mic is enabled only when one int-mic and one-ext mic exist
*/
static void cx_auto_check_auto_mic(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
if (is_ext_mic(codec, cfg->inputs[0].pin) &&
is_int_mic(codec, cfg->inputs[1].pin)) {
spec->auto_mic = 1;
spec->auto_mic_ext = 1;
return;
}
if (is_int_mic(codec, cfg->inputs[1].pin) &&
is_ext_mic(codec, cfg->inputs[0].pin)) {
spec->auto_mic = 1;
spec->auto_mic_ext = 0;
return;
}
}
static void cx_auto_parse_input(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
struct hda_input_mux *imux;
int i;
imux = &spec->private_imux;
for (i = 0; i < cfg->num_inputs; i++) {
int idx = get_connection_index(codec, spec->adc_nids[0],
cfg->inputs[i].pin);
if (idx >= 0) {
const char *label;
label = hda_get_autocfg_input_label(codec, cfg, i);
snd_hda_add_imux_item(imux, label, idx, NULL);
}
}
if (imux->num_items == 2 && cfg->num_inputs == 2)
cx_auto_check_auto_mic(codec);
if (imux->num_items > 1 && !spec->auto_mic)
spec->input_mux = imux;
}
/* get digital-input audio widget corresponding to the given pin */
static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin)
{
hda_nid_t nid, end_nid;
end_nid = codec->start_nid + codec->num_nodes;
for (nid = codec->start_nid; nid < end_nid; nid++) {
unsigned int wcaps = get_wcaps(codec, nid);
unsigned int type = get_wcaps_type(wcaps);
if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) {
if (get_connection_index(codec, nid, pin) >= 0)
return nid;
}
}
return 0;
}
static void cx_auto_parse_digital(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid;
if (cfg->dig_outs &&
snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1)
spec->multiout.dig_out_nid = nid;
if (cfg->dig_in_pin)
spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin);
}
#ifdef CONFIG_SND_HDA_INPUT_BEEP
static void cx_auto_parse_beep(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
hda_nid_t nid, end_nid;
end_nid = codec->start_nid + codec->num_nodes;
for (nid = codec->start_nid; nid < end_nid; nid++)
if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) {
set_beep_amp(spec, nid, 0, HDA_OUTPUT);
break;
}
}
#else
#define cx_auto_parse_beep(codec)
#endif
static int cx_auto_parse_auto_config(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
int err;
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
if (err < 0)
return err;
cx_auto_parse_output(codec);
cx_auto_parse_input(codec);
cx_auto_parse_digital(codec);
cx_auto_parse_beep(codec);
return 0;
}
static void cx_auto_turn_on_eapd(struct hda_codec *codec, int num_pins,
hda_nid_t *pins)
{
int i;
for (i = 0; i < num_pins; i++) {
if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
snd_hda_codec_write(codec, pins[i], 0,
AC_VERB_SET_EAPD_BTLENABLE, 0x02);
}
}
static void select_connection(struct hda_codec *codec, hda_nid_t pin,
hda_nid_t src)
{
int idx = get_connection_index(codec, pin, src);
if (idx >= 0)
snd_hda_codec_write(codec, pin, 0,
AC_VERB_SET_CONNECT_SEL, idx);
}
static void cx_auto_init_output(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid;
int i;
for (i = 0; i < spec->multiout.num_dacs; i++)
snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
for (i = 0; i < cfg->hp_outs; i++)
snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
if (spec->auto_mute) {
for (i = 0; i < cfg->hp_outs; i++) {
snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
AC_USRSP_EN | CONEXANT_HP_EVENT);
}
cx_auto_hp_automute(codec);
} else {
for (i = 0; i < cfg->line_outs; i++)
snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
for (i = 0; i < cfg->speaker_outs; i++)
snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
}
for (i = 0; i < spec->dac_info_filled; i++) {
nid = spec->dac_info[i].dac;
if (!nid)
nid = spec->multiout.dac_nids[0];
select_connection(codec, spec->dac_info[i].pin, nid);
}
/* turn on EAPD */
cx_auto_turn_on_eapd(codec, cfg->line_outs, cfg->line_out_pins);
cx_auto_turn_on_eapd(codec, cfg->hp_outs, cfg->hp_pins);
cx_auto_turn_on_eapd(codec, cfg->speaker_outs, cfg->speaker_pins);
}
static void cx_auto_init_input(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
for (i = 0; i < spec->num_adc_nids; i++)
snd_hda_codec_write(codec, spec->adc_nids[i], 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0));
for (i = 0; i < cfg->num_inputs; i++) {
unsigned int type;
if (cfg->inputs[i].type == AUTO_PIN_MIC)
type = PIN_VREF80;
else
type = PIN_IN;
snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, type);
}
if (spec->auto_mic) {
int ext_idx = spec->auto_mic_ext;
snd_hda_codec_write(codec, cfg->inputs[ext_idx].pin, 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
AC_USRSP_EN | CONEXANT_MIC_EVENT);
cx_auto_automic(codec);
} else {
for (i = 0; i < spec->num_adc_nids; i++) {
snd_hda_codec_write(codec, spec->adc_nids[i], 0,
AC_VERB_SET_CONNECT_SEL,
spec->private_imux.items[0].index);
}
}
}
static void cx_auto_init_digital(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
if (spec->multiout.dig_out_nid)
snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
if (spec->dig_in_nid)
snd_hda_codec_write(codec, cfg->dig_in_pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
}
static int cx_auto_init(struct hda_codec *codec)
{
/*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/
cx_auto_init_output(codec);
cx_auto_init_input(codec);
cx_auto_init_digital(codec);
return 0;
}
static int cx_auto_add_volume(struct hda_codec *codec, const char *basename,
const char *dir, int cidx,
hda_nid_t nid, int hda_dir)
{
static char name[32];
static struct snd_kcontrol_new knew[] = {
HDA_CODEC_VOLUME(name, 0, 0, 0),
HDA_CODEC_MUTE(name, 0, 0, 0),
};
static char *sfx[2] = { "Volume", "Switch" };
int i, err;
for (i = 0; i < 2; i++) {
struct snd_kcontrol *kctl;
knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, hda_dir);
knew[i].subdevice = HDA_SUBDEV_AMP_FLAG;
knew[i].index = cidx;
snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]);
kctl = snd_ctl_new1(&knew[i], codec);
if (!kctl)
return -ENOMEM;
err = snd_hda_ctl_add(codec, nid, kctl);
if (err < 0)
return err;
if (!(query_amp_caps(codec, nid, hda_dir) & AC_AMPCAP_MUTE))
break;
}
return 0;
}
#define cx_auto_add_pb_volume(codec, nid, str, idx) \
cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
static int cx_auto_build_output_controls(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
int i, err;
int num_line = 0, num_hp = 0, num_spk = 0;
static const char *texts[3] = { "Front", "Surround", "CLFE" };
if (spec->dac_info_filled == 1)
return cx_auto_add_pb_volume(codec, spec->dac_info[0].dac,
"Master", 0);
for (i = 0; i < spec->dac_info_filled; i++) {
const char *label;
int idx, type;
if (!spec->dac_info[i].dac)
continue;
type = spec->dac_info[i].type;
if (type == AUTO_PIN_LINE_OUT)
type = spec->autocfg.line_out_type;
switch (type) {
case AUTO_PIN_LINE_OUT:
default:
label = texts[num_line++];
idx = 0;
break;
case AUTO_PIN_HP_OUT:
label = "Headphone";
idx = num_hp++;
break;
case AUTO_PIN_SPEAKER_OUT:
label = "Speaker";
idx = num_spk++;
break;
}
err = cx_auto_add_pb_volume(codec, spec->dac_info[i].dac,
label, idx);
if (err < 0)
return err;
}
return 0;
}
static int cx_auto_build_input_controls(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
static const char *prev_label;
int i, err, cidx;
err = cx_auto_add_volume(codec, "Capture", "", 0, spec->adc_nids[0],
HDA_INPUT);
if (err < 0)
return err;
prev_label = NULL;
cidx = 0;
for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t nid = cfg->inputs[i].pin;
const char *label;
if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
continue;
label = hda_get_autocfg_input_label(codec, cfg, i);
if (label == prev_label)
cidx++;
else
cidx = 0;
prev_label = label;
err = cx_auto_add_volume(codec, label, " Capture", cidx,
nid, HDA_INPUT);
if (err < 0)
return err;
}
return 0;
}
static int cx_auto_build_controls(struct hda_codec *codec)
{
int err;
err = cx_auto_build_output_controls(codec);
if (err < 0)
return err;
err = cx_auto_build_input_controls(codec);
if (err < 0)
return err;
return conexant_build_controls(codec);
}
static struct hda_codec_ops cx_auto_patch_ops = {
.build_controls = cx_auto_build_controls,
.build_pcms = conexant_build_pcms,
.init = cx_auto_init,
.free = conexant_free,
.unsol_event = cx_auto_unsol_event,
#ifdef CONFIG_SND_HDA_POWER_SAVE
.suspend = conexant_suspend,
#endif
.reboot_notify = snd_hda_shutup_pins,
};
static int patch_conexant_auto(struct hda_codec *codec)
{
struct conexant_spec *spec;
int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
codec->spec = spec;
spec->adc_nids = cx_auto_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(cx_auto_adc_nids);
spec->capsrc_nids = spec->adc_nids;
err = cx_auto_parse_auto_config(codec);
if (err < 0) {
kfree(codec->spec);
codec->spec = NULL;
return err;
}
codec->patch_ops = cx_auto_patch_ops;
if (spec->beep_amp)
snd_hda_attach_beep_device(codec, spec->beep_amp);
return 0;
}
/* /*
*/ */
@ -3271,6 +3880,22 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
.patch = patch_cxt5066 }, .patch = patch_cxt5066 },
{ .id = 0x14f15069, .name = "CX20585", { .id = 0x14f15069, .name = "CX20585",
.patch = patch_cxt5066 }, .patch = patch_cxt5066 },
{ .id = 0x14f15097, .name = "CX20631",
.patch = patch_conexant_auto },
{ .id = 0x14f15098, .name = "CX20632",
.patch = patch_conexant_auto },
{ .id = 0x14f150a1, .name = "CX20641",
.patch = patch_conexant_auto },
{ .id = 0x14f150a2, .name = "CX20642",
.patch = patch_conexant_auto },
{ .id = 0x14f150ab, .name = "CX20651",
.patch = patch_conexant_auto },
{ .id = 0x14f150ac, .name = "CX20652",
.patch = patch_conexant_auto },
{ .id = 0x14f150b8, .name = "CX20664",
.patch = patch_conexant_auto },
{ .id = 0x14f150b9, .name = "CX20665",
.patch = patch_conexant_auto },
{} /* terminator */ {} /* terminator */
}; };
@ -3281,6 +3906,14 @@ MODULE_ALIAS("snd-hda-codec-id:14f15066");
MODULE_ALIAS("snd-hda-codec-id:14f15067"); MODULE_ALIAS("snd-hda-codec-id:14f15067");
MODULE_ALIAS("snd-hda-codec-id:14f15068"); MODULE_ALIAS("snd-hda-codec-id:14f15068");
MODULE_ALIAS("snd-hda-codec-id:14f15069"); MODULE_ALIAS("snd-hda-codec-id:14f15069");
MODULE_ALIAS("snd-hda-codec-id:14f15097");
MODULE_ALIAS("snd-hda-codec-id:14f15098");
MODULE_ALIAS("snd-hda-codec-id:14f150a1");
MODULE_ALIAS("snd-hda-codec-id:14f150a2");
MODULE_ALIAS("snd-hda-codec-id:14f150ab");
MODULE_ALIAS("snd-hda-codec-id:14f150ac");
MODULE_ALIAS("snd-hda-codec-id:14f150b8");
MODULE_ALIAS("snd-hda-codec-id:14f150b9");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec"); MODULE_DESCRIPTION("Conexant HD-audio codec");

View File

@ -3,6 +3,9 @@
* patch_hdmi.c - routines for HDMI/DisplayPort codecs * patch_hdmi.c - routines for HDMI/DisplayPort codecs
* *
* Copyright(c) 2008-2010 Intel Corporation. All rights reserved. * Copyright(c) 2008-2010 Intel Corporation. All rights reserved.
* Copyright (c) 2006 ATI Technologies Inc.
* Copyright (c) 2008 NVIDIA Corp. All rights reserved.
* Copyright (c) 2008 Wei Ni <wni@nvidia.com>
* *
* Authors: * Authors:
* Wu Fengguang <wfg@linux.intel.com> * Wu Fengguang <wfg@linux.intel.com>
@ -25,6 +28,22 @@
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
/*
* The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
* could support two independent pipes, each of them can be connected to one or
* more ports (DVI, HDMI or DisplayPort).
*
* The HDA correspondence of pipes/ports are converter/pin nodes.
*/
#define MAX_HDMI_CVTS 3
#define MAX_HDMI_PINS 3
struct hdmi_spec { struct hdmi_spec {
int num_cvts; int num_cvts;
@ -49,10 +68,10 @@ struct hdmi_spec {
struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS]; struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS];
/* /*
* nvhdmi specific * ati/nvhdmi specific
*/ */
struct hda_multi_out multiout; struct hda_multi_out multiout;
unsigned int codec_type; struct hda_pcm_stream *pcm_playback;
/* misc flags */ /* misc flags */
/* PD bit indicates only the update, not the current state */ /* PD bit indicates only the update, not the current state */
@ -65,13 +84,25 @@ struct hdmi_audio_infoframe {
u8 ver; /* 0x01 */ u8 ver; /* 0x01 */
u8 len; /* 0x0a */ u8 len; /* 0x0a */
u8 checksum; /* PB0 */ u8 checksum;
u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */ u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */
u8 SS01_SF24; u8 SS01_SF24;
u8 CXT04; u8 CXT04;
u8 CA; u8 CA;
u8 LFEPBL01_LSV36_DM_INH7; u8 LFEPBL01_LSV36_DM_INH7;
u8 reserved[5]; /* PB6 - PB10 */ };
struct dp_audio_infoframe {
u8 type; /* 0x84 */
u8 len; /* 0x1b */
u8 ver; /* 0x11 << 2 */
u8 CC02_CT47; /* match with HDMI infoframe from this on */
u8 SS01_SF24;
u8 CXT04;
u8 CA;
u8 LFEPBL01_LSV36_DM_INH7;
}; };
/* /*
@ -162,7 +193,7 @@ static int hdmi_channel_mapping[0x32][8] = {
/* 4ch */ /* 4ch */
[0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
/* surround41 */ /* surround41 */
[0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 }, [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
/* surround50 */ /* surround50 */
[0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
/* surround51 */ /* surround51 */
@ -175,7 +206,7 @@ static int hdmi_channel_mapping[0x32][8] = {
* This is an ordered list! * This is an ordered list!
* *
* The preceding ones have better chances to be selected by * The preceding ones have better chances to be selected by
* hdmi_setup_channel_allocation(). * hdmi_channel_allocation().
*/ */
static struct cea_channel_speaker_allocation channel_allocations[] = { static struct cea_channel_speaker_allocation channel_allocations[] = {
/* channel: 7 6 5 4 3 2 1 0 */ /* channel: 7 6 5 4 3 2 1 0 */
@ -352,14 +383,14 @@ static void init_channel_allocations(void)
* *
* TODO: it could select the wrong CA from multiple candidates. * TODO: it could select the wrong CA from multiple candidates.
*/ */
static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
struct hdmi_audio_infoframe *ai) int channels)
{ {
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
struct hdmi_eld *eld; struct hdmi_eld *eld;
int i; int i;
int ca = 0;
int spk_mask = 0; int spk_mask = 0;
int channels = 1 + (ai->CC02_CT47 & 0x7);
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
/* /*
@ -397,16 +428,16 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
if (channels == channel_allocations[i].channels && if (channels == channel_allocations[i].channels &&
(spk_mask & channel_allocations[i].spk_mask) == (spk_mask & channel_allocations[i].spk_mask) ==
channel_allocations[i].spk_mask) { channel_allocations[i].spk_mask) {
ai->CA = channel_allocations[i].ca_index; ca = channel_allocations[i].ca_index;
break; break;
} }
} }
snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf)); snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n", snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n",
ai->CA, channels, buf); ca, channels, buf);
return ai->CA; return ca;
} }
static void hdmi_debug_channel_mapping(struct hda_codec *codec, static void hdmi_debug_channel_mapping(struct hda_codec *codec,
@ -428,10 +459,9 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec,
static void hdmi_setup_channel_mapping(struct hda_codec *codec, static void hdmi_setup_channel_mapping(struct hda_codec *codec,
hda_nid_t pin_nid, hda_nid_t pin_nid,
struct hdmi_audio_infoframe *ai) int ca)
{ {
int i; int i;
int ca = ai->CA;
int err; int err;
if (hdmi_channel_mapping[ca][1] == 0) { if (hdmi_channel_mapping[ca][1] == 0) {
@ -528,41 +558,37 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
#endif #endif
} }
static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai) static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai)
{ {
u8 *bytes = (u8 *)ai; u8 *bytes = (u8 *)hdmi_ai;
u8 sum = 0; u8 sum = 0;
int i; int i;
ai->checksum = 0; hdmi_ai->checksum = 0;
for (i = 0; i < sizeof(*ai); i++) for (i = 0; i < sizeof(*hdmi_ai); i++)
sum += bytes[i]; sum += bytes[i];
ai->checksum = -sum; hdmi_ai->checksum = -sum;
} }
static void hdmi_fill_audio_infoframe(struct hda_codec *codec, static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
hda_nid_t pin_nid, hda_nid_t pin_nid,
struct hdmi_audio_infoframe *ai) u8 *dip, int size)
{ {
u8 *bytes = (u8 *)ai;
int i; int i;
hdmi_debug_dip_size(codec, pin_nid); hdmi_debug_dip_size(codec, pin_nid);
hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
hdmi_checksum_audio_infoframe(ai);
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
for (i = 0; i < sizeof(*ai); i++) for (i = 0; i < size; i++)
hdmi_write_dip_byte(codec, pin_nid, bytes[i]); hdmi_write_dip_byte(codec, pin_nid, dip[i]);
} }
static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
struct hdmi_audio_infoframe *ai) u8 *dip, int size)
{ {
u8 *bytes = (u8 *)ai;
u8 val; u8 val;
int i; int i;
@ -571,10 +597,10 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
return false; return false;
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
for (i = 0; i < sizeof(*ai); i++) { for (i = 0; i < size; i++) {
val = snd_hda_codec_read(codec, pin_nid, 0, val = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_HDMI_DIP_DATA, 0); AC_VERB_GET_HDMI_DIP_DATA, 0);
if (val != bytes[i]) if (val != dip[i])
return false; return false;
} }
@ -586,15 +612,13 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
{ {
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
hda_nid_t pin_nid; hda_nid_t pin_nid;
int channels = substream->runtime->channels;
int ca;
int i; int i;
struct hdmi_audio_infoframe ai = { u8 ai[max(sizeof(struct hdmi_audio_infoframe),
.type = 0x84, sizeof(struct dp_audio_infoframe))];
.ver = 0x01,
.len = 0x0a,
.CC02_CT47 = substream->runtime->channels - 1,
};
hdmi_setup_channel_allocation(codec, nid, &ai); ca = hdmi_channel_allocation(codec, nid, channels);
for (i = 0; i < spec->num_pins; i++) { for (i = 0; i < spec->num_pins; i++) {
if (spec->pin_cvt[i] != nid) if (spec->pin_cvt[i] != nid)
@ -603,14 +627,45 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
continue; continue;
pin_nid = spec->pin[i]; pin_nid = spec->pin[i];
if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) {
memset(ai, 0, sizeof(ai));
if (spec->sink_eld[i].conn_type == 0) { /* HDMI */
struct hdmi_audio_infoframe *hdmi_ai;
hdmi_ai = (struct hdmi_audio_infoframe *)ai;
hdmi_ai->type = 0x84;
hdmi_ai->ver = 0x01;
hdmi_ai->len = 0x0a;
hdmi_ai->CC02_CT47 = channels - 1;
hdmi_checksum_audio_infoframe(hdmi_ai);
} else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */
struct dp_audio_infoframe *dp_ai;
dp_ai = (struct dp_audio_infoframe *)ai;
dp_ai->type = 0x84;
dp_ai->len = 0x1b;
dp_ai->ver = 0x11 << 2;
dp_ai->CC02_CT47 = channels - 1;
} else {
snd_printd("HDMI: unknown connection type at pin %d\n",
pin_nid);
continue;
}
/*
* sizeof(ai) is used instead of sizeof(*hdmi_ai) or
* sizeof(*dp_ai) to avoid partial match/update problems when
* the user switches between HDMI/DP monitors.
*/
if (!hdmi_infoframe_uptodate(codec, pin_nid, ai, sizeof(ai))) {
snd_printdd("hdmi_setup_audio_infoframe: " snd_printdd("hdmi_setup_audio_infoframe: "
"cvt=%d pin=%d channels=%d\n", "cvt=%d pin=%d channels=%d\n",
nid, pin_nid, nid, pin_nid,
substream->runtime->channels); channels);
hdmi_setup_channel_mapping(codec, pin_nid, &ai); hdmi_setup_channel_mapping(codec, pin_nid, ca);
hdmi_stop_infoframe_trans(codec, pin_nid); hdmi_stop_infoframe_trans(codec, pin_nid);
hdmi_fill_audio_infoframe(codec, pin_nid, &ai); hdmi_fill_audio_infoframe(codec, pin_nid,
ai, sizeof(ai));
hdmi_start_infoframe_trans(codec, pin_nid); hdmi_start_infoframe_trans(codec, pin_nid);
} }
} }
@ -791,7 +846,6 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
/* /*
* HDA/HDMI auto parsing * HDA/HDMI auto parsing
*/ */
static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
{ {
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
@ -922,3 +976,664 @@ static int hdmi_parse_codec(struct hda_codec *codec)
return 0; return 0;
} }
/*
*/
static char *generic_hdmi_pcm_names[MAX_HDMI_CVTS] = {
"HDMI 0",
"HDMI 1",
"HDMI 2",
};
/*
* HDMI callbacks
*/
static int generic_hdmi_playback_pcm_prepare(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);
return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
}
static struct hda_pcm_stream generic_hdmi_pcm_playback = {
.substreams = 1,
.channels_min = 2,
.ops = {
.open = hdmi_pcm_open,
.prepare = generic_hdmi_playback_pcm_prepare,
},
};
static int generic_hdmi_build_pcms(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
struct hda_pcm *info = spec->pcm_rec;
int i;
codec->num_pcms = spec->num_cvts;
codec->pcm_info = info;
for (i = 0; i < codec->num_pcms; i++, info++) {
unsigned int chans;
struct hda_pcm_stream *pstr;
chans = get_wcaps(codec, spec->cvt[i]);
chans = get_wcaps_channels(chans);
info->name = generic_hdmi_pcm_names[i];
info->pcm_type = HDA_PCM_TYPE_HDMI;
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
if (spec->pcm_playback)
*pstr = *spec->pcm_playback;
else
*pstr = generic_hdmi_pcm_playback;
pstr->nid = spec->cvt[i];
if (pstr->channels_max <= 2 && chans && chans <= 16)
pstr->channels_max = chans;
}
return 0;
}
static int generic_hdmi_build_controls(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int err;
int i;
for (i = 0; i < codec->num_pcms; i++) {
err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
if (err < 0)
return err;
}
return 0;
}
static int generic_hdmi_init(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int i;
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]);
}
return 0;
}
static void generic_hdmi_free(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int i;
for (i = 0; i < spec->num_pins; i++)
snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
kfree(spec);
}
static struct hda_codec_ops generic_hdmi_patch_ops = {
.init = generic_hdmi_init,
.free = generic_hdmi_free,
.build_pcms = generic_hdmi_build_pcms,
.build_controls = generic_hdmi_build_controls,
.unsol_event = hdmi_unsol_event,
};
static int patch_generic_hdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
int i;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
codec->spec = spec;
if (hdmi_parse_codec(codec) < 0) {
codec->spec = NULL;
kfree(spec);
return -EINVAL;
}
codec->patch_ops = generic_hdmi_patch_ops;
for (i = 0; i < spec->num_pins; i++)
snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
init_channel_allocations();
return 0;
}
/*
* Nvidia specific implementations
*/
#define Nv_VERB_SET_Channel_Allocation 0xF79
#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A
#define Nv_VERB_SET_Audio_Protection_On 0xF98
#define Nv_VERB_SET_Audio_Protection_Off 0xF99
#define nvhdmi_master_con_nid_7x 0x04
#define nvhdmi_master_pin_nid_7x 0x05
static hda_nid_t nvhdmi_con_nids_7x[4] = {
/*front, rear, clfe, rear_surr */
0x6, 0x8, 0xa, 0xc,
};
static struct hda_verb nvhdmi_basic_init_7x[] = {
/* set audio protect on */
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
/* enable digital output on pin widget */
{ 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
{ 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
{ 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
{ 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
{ 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
{} /* terminator */
};
#ifdef LIMITED_RATE_FMT_SUPPORT
/* support only the safe format and rate */
#define SUPPORTED_RATES SNDRV_PCM_RATE_48000
#define SUPPORTED_MAXBPS 16
#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE
#else
/* support all rates and formats */
#define SUPPORTED_RATES \
(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
SNDRV_PCM_RATE_192000)
#define SUPPORTED_MAXBPS 24
#define SUPPORTED_FORMATS \
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
#endif
static int nvhdmi_7x_init(struct hda_codec *codec)
{
snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
return 0;
}
static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
}
static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}
static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
stream_tag, format, substream);
}
static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
int i;
snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
0, AC_VERB_SET_CHANNEL_STREAMID, 0);
for (i = 0; i < 4; i++) {
/* set the stream id */
snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
AC_VERB_SET_CHANNEL_STREAMID, 0);
/* set the stream format */
snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
AC_VERB_SET_STREAM_FORMAT, 0);
}
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}
static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
int chs;
unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
int i;
mutex_lock(&codec->spdif_mutex);
chs = substream->runtime->channels;
chan = chs ? (chs - 1) : 1;
switch (chs) {
default:
case 0:
case 2:
chanmask = 0x00;
break;
case 4:
chanmask = 0x08;
break;
case 6:
chanmask = 0x0b;
break;
case 8:
chanmask = 0x13;
break;
}
dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
dataDCC2 = 0x2;
/* set the Audio InforFrame Channel Allocation */
snd_hda_codec_write(codec, 0x1, 0,
Nv_VERB_SET_Channel_Allocation, chanmask);
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
snd_hda_codec_write(codec,
nvhdmi_master_con_nid_7x,
0,
AC_VERB_SET_DIGI_CONVERT_1,
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
/* set the stream id */
snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
/* set the stream format */
snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
AC_VERB_SET_STREAM_FORMAT, format);
/* turn on again (if needed) */
/* enable and set the channel status audio/data flag */
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
snd_hda_codec_write(codec,
nvhdmi_master_con_nid_7x,
0,
AC_VERB_SET_DIGI_CONVERT_1,
codec->spdif_ctls & 0xff);
snd_hda_codec_write(codec,
nvhdmi_master_con_nid_7x,
0,
AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
}
for (i = 0; i < 4; i++) {
if (chs == 2)
channel_id = 0;
else
channel_id = i * 2;
/* turn off SPDIF once;
*otherwise the IEC958 bits won't be updated
*/
if (codec->spdif_status_reset &&
(codec->spdif_ctls & AC_DIG1_ENABLE))
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_DIGI_CONVERT_1,
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
/* set the stream id */
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_CHANNEL_STREAMID,
(stream_tag << 4) | channel_id);
/* set the stream format */
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_STREAM_FORMAT,
format);
/* turn on again (if needed) */
/* enable and set the channel status audio/data flag */
if (codec->spdif_status_reset &&
(codec->spdif_ctls & AC_DIG1_ENABLE)) {
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_DIGI_CONVERT_1,
codec->spdif_ctls & 0xff);
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
}
}
/* set the Audio Info Frame Checksum */
snd_hda_codec_write(codec, 0x1, 0,
Nv_VERB_SET_Info_Frame_Checksum,
(0x71 - chan - chanmask));
mutex_unlock(&codec->spdif_mutex);
return 0;
}
static struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
.substreams = 1,
.channels_min = 2,
.channels_max = 8,
.nid = nvhdmi_master_con_nid_7x,
.rates = SUPPORTED_RATES,
.maxbps = SUPPORTED_MAXBPS,
.formats = SUPPORTED_FORMATS,
.ops = {
.open = simple_playback_pcm_open,
.close = nvhdmi_8ch_7x_pcm_close,
.prepare = nvhdmi_8ch_7x_pcm_prepare
},
};
static struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
.nid = nvhdmi_master_con_nid_7x,
.rates = SUPPORTED_RATES,
.maxbps = SUPPORTED_MAXBPS,
.formats = SUPPORTED_FORMATS,
.ops = {
.open = simple_playback_pcm_open,
.close = simple_playback_pcm_close,
.prepare = simple_playback_pcm_prepare
},
};
static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
.build_controls = generic_hdmi_build_controls,
.build_pcms = generic_hdmi_build_pcms,
.init = nvhdmi_7x_init,
.free = generic_hdmi_free,
};
static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
.build_controls = generic_hdmi_build_controls,
.build_pcms = generic_hdmi_build_pcms,
.init = nvhdmi_7x_init,
.free = generic_hdmi_free,
};
static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
{
struct hdmi_spec *spec;
int err = patch_generic_hdmi(codec);
if (err < 0)
return err;
spec = codec->spec;
spec->old_pin_detect = 1;
return 0;
}
static int patch_nvhdmi_2ch(struct hda_codec *codec)
{
struct hdmi_spec *spec;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
codec->spec = spec;
spec->multiout.num_dacs = 0; /* no analog */
spec->multiout.max_channels = 2;
spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
spec->old_pin_detect = 1;
spec->num_cvts = 1;
spec->cvt[0] = nvhdmi_master_con_nid_7x;
spec->pcm_playback = &nvhdmi_pcm_playback_2ch;
codec->patch_ops = nvhdmi_patch_ops_2ch;
return 0;
}
static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
{
struct hdmi_spec *spec;
int err = patch_nvhdmi_2ch(codec);
if (err < 0)
return err;
spec = codec->spec;
spec->multiout.max_channels = 8;
spec->pcm_playback = &nvhdmi_pcm_playback_8ch_7x;
codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
return 0;
}
/*
* ATI-specific implementations
*
* FIXME: we may omit the whole this and use the generic code once after
* it's confirmed to work.
*/
#define ATIHDMI_CVT_NID 0x02 /* audio converter */
#define ATIHDMI_PIN_NID 0x03 /* HDMI output pin */
static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
int chans = substream->runtime->channels;
int i, err;
err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format,
substream);
if (err < 0)
return err;
snd_hda_codec_write(codec, spec->cvt[0], 0, AC_VERB_SET_CVT_CHAN_COUNT,
chans - 1);
/* FIXME: XXX */
for (i = 0; i < chans; i++) {
snd_hda_codec_write(codec, spec->cvt[0], 0,
AC_VERB_SET_HDMI_CHAN_SLOT,
(i << 4) | i);
}
return 0;
}
static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
.nid = ATIHDMI_CVT_NID,
.ops = {
.open = simple_playback_pcm_open,
.close = simple_playback_pcm_close,
.prepare = atihdmi_playback_pcm_prepare
},
};
static struct hda_verb atihdmi_basic_init[] = {
/* enable digital output on pin widget */
{ 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
{} /* terminator */
};
static int atihdmi_init(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
snd_hda_sequence_write(codec, atihdmi_basic_init);
/* SI codec requires to unmute the pin */
if (get_wcaps(codec, spec->pin[0]) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, spec->pin[0], 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE);
return 0;
}
static struct hda_codec_ops atihdmi_patch_ops = {
.build_controls = generic_hdmi_build_controls,
.build_pcms = generic_hdmi_build_pcms,
.init = atihdmi_init,
.free = generic_hdmi_free,
};
static int patch_atihdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
codec->spec = spec;
spec->multiout.num_dacs = 0; /* no analog */
spec->multiout.max_channels = 2;
spec->multiout.dig_out_nid = ATIHDMI_CVT_NID;
spec->num_cvts = 1;
spec->cvt[0] = ATIHDMI_CVT_NID;
spec->pin[0] = ATIHDMI_PIN_NID;
spec->pcm_playback = &atihdmi_pcm_digital_playback;
codec->patch_ops = atihdmi_patch_ops;
return 0;
}
/*
* patch entries
*/
static struct hda_codec_preset snd_hda_preset_hdmi[] = {
{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi },
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi },
{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi },
{ .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 = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi },
{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
{} /* terminator */
};
MODULE_ALIAS("snd-hda-codec-id:1002793c");
MODULE_ALIAS("snd-hda-codec-id:10027919");
MODULE_ALIAS("snd-hda-codec-id:1002791a");
MODULE_ALIAS("snd-hda-codec-id:1002aa01");
MODULE_ALIAS("snd-hda-codec-id:10951390");
MODULE_ALIAS("snd-hda-codec-id:10951392");
MODULE_ALIAS("snd-hda-codec-id:10de0002");
MODULE_ALIAS("snd-hda-codec-id:10de0003");
MODULE_ALIAS("snd-hda-codec-id:10de0005");
MODULE_ALIAS("snd-hda-codec-id:10de0006");
MODULE_ALIAS("snd-hda-codec-id:10de0007");
MODULE_ALIAS("snd-hda-codec-id:10de000a");
MODULE_ALIAS("snd-hda-codec-id:10de000b");
MODULE_ALIAS("snd-hda-codec-id:10de000c");
MODULE_ALIAS("snd-hda-codec-id:10de000d");
MODULE_ALIAS("snd-hda-codec-id:10de0010");
MODULE_ALIAS("snd-hda-codec-id:10de0011");
MODULE_ALIAS("snd-hda-codec-id:10de0012");
MODULE_ALIAS("snd-hda-codec-id:10de0013");
MODULE_ALIAS("snd-hda-codec-id:10de0014");
MODULE_ALIAS("snd-hda-codec-id:10de0018");
MODULE_ALIAS("snd-hda-codec-id:10de0019");
MODULE_ALIAS("snd-hda-codec-id:10de001a");
MODULE_ALIAS("snd-hda-codec-id:10de001b");
MODULE_ALIAS("snd-hda-codec-id:10de001c");
MODULE_ALIAS("snd-hda-codec-id:10de0040");
MODULE_ALIAS("snd-hda-codec-id:10de0041");
MODULE_ALIAS("snd-hda-codec-id:10de0042");
MODULE_ALIAS("snd-hda-codec-id:10de0043");
MODULE_ALIAS("snd-hda-codec-id:10de0044");
MODULE_ALIAS("snd-hda-codec-id:10de0067");
MODULE_ALIAS("snd-hda-codec-id:10de8001");
MODULE_ALIAS("snd-hda-codec-id:17e80047");
MODULE_ALIAS("snd-hda-codec-id:80860054");
MODULE_ALIAS("snd-hda-codec-id:80862801");
MODULE_ALIAS("snd-hda-codec-id:80862802");
MODULE_ALIAS("snd-hda-codec-id:80862803");
MODULE_ALIAS("snd-hda-codec-id:80862804");
MODULE_ALIAS("snd-hda-codec-id:80862805");
MODULE_ALIAS("snd-hda-codec-id:808629fb");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("HDMI HD-audio codec");
MODULE_ALIAS("snd-hda-codec-intelhdmi");
MODULE_ALIAS("snd-hda-codec-nvhdmi");
MODULE_ALIAS("snd-hda-codec-atihdmi");
static struct hda_codec_preset_list intel_list = {
.preset = snd_hda_preset_hdmi,
.owner = THIS_MODULE,
};
static int __init patch_hdmi_init(void)
{
return snd_hda_add_codec_preset(&intel_list);
}
static void __exit patch_hdmi_exit(void)
{
snd_hda_delete_codec_preset(&intel_list);
}
module_init(patch_hdmi_init)
module_exit(patch_hdmi_exit)

View File

@ -1,220 +0,0 @@
/*
*
* patch_intelhdmi.c - Patch for Intel HDMI codecs
*
* Copyright(c) 2008 Intel Corporation. All rights reserved.
*
* Authors:
* Jiang Zhe <zhe.jiang@intel.com>
* 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.
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
/*
* The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
* could support two independent pipes, each of them can be connected to one or
* more ports (DVI, HDMI or DisplayPort).
*
* The HDA correspondence of pipes/ports are converter/pin nodes.
*/
#define MAX_HDMI_CVTS 3
#define MAX_HDMI_PINS 3
#include "patch_hdmi.c"
static char *intel_hdmi_pcm_names[MAX_HDMI_CVTS] = {
"INTEL HDMI 0",
"INTEL HDMI 1",
"INTEL HDMI 2",
};
/*
* HDMI callbacks
*/
static int intel_hdmi_playback_pcm_prepare(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);
return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
}
static struct hda_pcm_stream intel_hdmi_pcm_playback = {
.substreams = 1,
.channels_min = 2,
.ops = {
.open = hdmi_pcm_open,
.prepare = intel_hdmi_playback_pcm_prepare,
},
};
static int intel_hdmi_build_pcms(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
struct hda_pcm *info = spec->pcm_rec;
int i;
codec->num_pcms = spec->num_cvts;
codec->pcm_info = info;
for (i = 0; i < codec->num_pcms; i++, info++) {
unsigned int chans;
chans = get_wcaps(codec, spec->cvt[i]);
chans = get_wcaps_channels(chans);
info->name = intel_hdmi_pcm_names[i];
info->pcm_type = HDA_PCM_TYPE_HDMI;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
intel_hdmi_pcm_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
}
return 0;
}
static int intel_hdmi_build_controls(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int err;
int i;
for (i = 0; i < codec->num_pcms; i++) {
err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
if (err < 0)
return err;
}
return 0;
}
static int intel_hdmi_init(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int i;
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]);
}
return 0;
}
static void intel_hdmi_free(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int i;
for (i = 0; i < spec->num_pins; i++)
snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
kfree(spec);
}
static struct hda_codec_ops intel_hdmi_patch_ops = {
.init = intel_hdmi_init,
.free = intel_hdmi_free,
.build_pcms = intel_hdmi_build_pcms,
.build_controls = intel_hdmi_build_controls,
.unsol_event = hdmi_unsol_event,
};
static int patch_intel_hdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
int i;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
codec->spec = spec;
if (hdmi_parse_codec(codec) < 0) {
codec->spec = NULL;
kfree(spec);
return -EINVAL;
}
codec->patch_ops = intel_hdmi_patch_ops;
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 struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_intel_hdmi },
{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_intel_hdmi },
{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_intel_hdmi },
{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_intel_hdmi },
{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi },
{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi },
{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_intel_hdmi },
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi },
{} /* terminator */
};
MODULE_ALIAS("snd-hda-codec-id:808629fb");
MODULE_ALIAS("snd-hda-codec-id:80862801");
MODULE_ALIAS("snd-hda-codec-id:80862802");
MODULE_ALIAS("snd-hda-codec-id:80862803");
MODULE_ALIAS("snd-hda-codec-id:80862804");
MODULE_ALIAS("snd-hda-codec-id:80862805");
MODULE_ALIAS("snd-hda-codec-id:80860054");
MODULE_ALIAS("snd-hda-codec-id:10951392");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Intel HDMI HD-audio codec");
static struct hda_codec_preset_list intel_list = {
.preset = snd_hda_preset_intelhdmi,
.owner = THIS_MODULE,
};
static int __init patch_intelhdmi_init(void)
{
return snd_hda_add_codec_preset(&intel_list);
}
static void __exit patch_intelhdmi_exit(void)
{
snd_hda_delete_codec_preset(&intel_list);
}
module_init(patch_intelhdmi_init)
module_exit(patch_intelhdmi_exit)

View File

@ -1,608 +0,0 @@
/*
* Universal Interface for Intel High Definition Audio Codec
*
* HD audio interface patch for NVIDIA HDMI codecs
*
* Copyright (c) 2008 NVIDIA Corp. All rights reserved.
* Copyright (c) 2008 Wei Ni <wni@nvidia.com>
*
*
* This driver 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 driver 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
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
#define MAX_HDMI_CVTS 1
#define MAX_HDMI_PINS 1
#include "patch_hdmi.c"
static char *nvhdmi_pcm_names[MAX_HDMI_CVTS] = {
"NVIDIA HDMI",
};
/* define below to restrict the supported rates and formats */
/* #define LIMITED_RATE_FMT_SUPPORT */
enum HDACodec {
HDA_CODEC_NVIDIA_MCP7X,
HDA_CODEC_NVIDIA_MCP89,
HDA_CODEC_NVIDIA_GT21X,
HDA_CODEC_INVALID
};
#define Nv_VERB_SET_Channel_Allocation 0xF79
#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A
#define Nv_VERB_SET_Audio_Protection_On 0xF98
#define Nv_VERB_SET_Audio_Protection_Off 0xF99
#define nvhdmi_master_con_nid_7x 0x04
#define nvhdmi_master_pin_nid_7x 0x05
#define nvhdmi_master_con_nid_89 0x04
#define nvhdmi_master_pin_nid_89 0x05
static hda_nid_t nvhdmi_con_nids_7x[4] = {
/*front, rear, clfe, rear_surr */
0x6, 0x8, 0xa, 0xc,
};
static struct hda_verb nvhdmi_basic_init_7x[] = {
/* set audio protect on */
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
/* enable digital output on pin widget */
{ 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
{ 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
{ 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
{ 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
{ 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
{} /* terminator */
};
#ifdef LIMITED_RATE_FMT_SUPPORT
/* support only the safe format and rate */
#define SUPPORTED_RATES SNDRV_PCM_RATE_48000
#define SUPPORTED_MAXBPS 16
#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE
#else
/* support all rates and formats */
#define SUPPORTED_RATES \
(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
SNDRV_PCM_RATE_192000)
#define SUPPORTED_MAXBPS 24
#define SUPPORTED_FORMATS \
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
#endif
/*
* Controls
*/
static int nvhdmi_build_controls(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int err;
int i;
if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
|| (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
for (i = 0; i < codec->num_pcms; i++) {
err = snd_hda_create_spdif_out_ctls(codec,
spec->cvt[i]);
if (err < 0)
return err;
}
} else {
err = snd_hda_create_spdif_out_ctls(codec,
spec->multiout.dig_out_nid);
if (err < 0)
return err;
}
return 0;
}
static int nvhdmi_init(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int i;
if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
|| (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
for (i = 0; spec->pin[i]; i++) {
hdmi_enable_output(codec, spec->pin[i]);
snd_hda_codec_write(codec, spec->pin[i], 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
AC_USRSP_EN | spec->pin[i]);
}
} else {
snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
}
return 0;
}
static void nvhdmi_free(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int i;
if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
|| (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
for (i = 0; i < spec->num_pins; i++)
snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
}
kfree(spec);
}
/*
* Digital out
*/
static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
}
static int nvhdmi_dig_playback_pcm_close_8ch_7x(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
int i;
snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
0, AC_VERB_SET_CHANNEL_STREAMID, 0);
for (i = 0; i < 4; i++) {
/* set the stream id */
snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
AC_VERB_SET_CHANNEL_STREAMID, 0);
/* set the stream format */
snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
AC_VERB_SET_STREAM_FORMAT, 0);
}
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}
static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}
static int nvhdmi_dig_playback_pcm_prepare_8ch_89(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
hdmi_set_channel_count(codec, hinfo->nid,
substream->runtime->channels);
hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
}
static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
int chs;
unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
int i;
mutex_lock(&codec->spdif_mutex);
chs = substream->runtime->channels;
chan = chs ? (chs - 1) : 1;
switch (chs) {
default:
case 0:
case 2:
chanmask = 0x00;
break;
case 4:
chanmask = 0x08;
break;
case 6:
chanmask = 0x0b;
break;
case 8:
chanmask = 0x13;
break;
}
dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
dataDCC2 = 0x2;
/* set the Audio InforFrame Channel Allocation */
snd_hda_codec_write(codec, 0x1, 0,
Nv_VERB_SET_Channel_Allocation, chanmask);
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
snd_hda_codec_write(codec,
nvhdmi_master_con_nid_7x,
0,
AC_VERB_SET_DIGI_CONVERT_1,
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
/* set the stream id */
snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
/* set the stream format */
snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
AC_VERB_SET_STREAM_FORMAT, format);
/* turn on again (if needed) */
/* enable and set the channel status audio/data flag */
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
snd_hda_codec_write(codec,
nvhdmi_master_con_nid_7x,
0,
AC_VERB_SET_DIGI_CONVERT_1,
codec->spdif_ctls & 0xff);
snd_hda_codec_write(codec,
nvhdmi_master_con_nid_7x,
0,
AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
}
for (i = 0; i < 4; i++) {
if (chs == 2)
channel_id = 0;
else
channel_id = i * 2;
/* turn off SPDIF once;
*otherwise the IEC958 bits won't be updated
*/
if (codec->spdif_status_reset &&
(codec->spdif_ctls & AC_DIG1_ENABLE))
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_DIGI_CONVERT_1,
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
/* set the stream id */
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_CHANNEL_STREAMID,
(stream_tag << 4) | channel_id);
/* set the stream format */
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_STREAM_FORMAT,
format);
/* turn on again (if needed) */
/* enable and set the channel status audio/data flag */
if (codec->spdif_status_reset &&
(codec->spdif_ctls & AC_DIG1_ENABLE)) {
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_DIGI_CONVERT_1,
codec->spdif_ctls & 0xff);
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
}
}
/* set the Audio Info Frame Checksum */
snd_hda_codec_write(codec, 0x1, 0,
Nv_VERB_SET_Info_Frame_Checksum,
(0x71 - chan - chanmask));
mutex_unlock(&codec->spdif_mutex);
return 0;
}
static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
format, substream);
}
static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_89 = {
.substreams = 1,
.channels_min = 2,
.ops = {
.open = hdmi_pcm_open,
.prepare = nvhdmi_dig_playback_pcm_prepare_8ch_89,
},
};
static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_7x = {
.substreams = 1,
.channels_min = 2,
.channels_max = 8,
.nid = nvhdmi_master_con_nid_7x,
.rates = SUPPORTED_RATES,
.maxbps = SUPPORTED_MAXBPS,
.formats = SUPPORTED_FORMATS,
.ops = {
.open = nvhdmi_dig_playback_pcm_open,
.close = nvhdmi_dig_playback_pcm_close_8ch_7x,
.prepare = nvhdmi_dig_playback_pcm_prepare_8ch
},
};
static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
.nid = nvhdmi_master_con_nid_7x,
.rates = SUPPORTED_RATES,
.maxbps = SUPPORTED_MAXBPS,
.formats = SUPPORTED_FORMATS,
.ops = {
.open = nvhdmi_dig_playback_pcm_open,
.close = nvhdmi_dig_playback_pcm_close_2ch,
.prepare = nvhdmi_dig_playback_pcm_prepare_2ch
},
};
static int nvhdmi_build_pcms_8ch_89(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
struct hda_pcm *info = spec->pcm_rec;
int i;
codec->num_pcms = spec->num_cvts;
codec->pcm_info = info;
for (i = 0; i < codec->num_pcms; i++, info++) {
unsigned int chans;
chans = get_wcaps(codec, spec->cvt[i]);
chans = get_wcaps_channels(chans);
info->name = nvhdmi_pcm_names[i];
info->pcm_type = HDA_PCM_TYPE_HDMI;
info->stream[SNDRV_PCM_STREAM_PLAYBACK]
= nvhdmi_pcm_digital_playback_8ch_89;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
}
return 0;
}
static int nvhdmi_build_pcms_8ch_7x(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
struct hda_pcm *info = spec->pcm_rec;
codec->num_pcms = 1;
codec->pcm_info = info;
info->name = "NVIDIA HDMI";
info->pcm_type = HDA_PCM_TYPE_HDMI;
info->stream[SNDRV_PCM_STREAM_PLAYBACK]
= nvhdmi_pcm_digital_playback_8ch_7x;
return 0;
}
static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
struct hda_pcm *info = spec->pcm_rec;
codec->num_pcms = 1;
codec->pcm_info = info;
info->name = "NVIDIA HDMI";
info->pcm_type = HDA_PCM_TYPE_HDMI;
info->stream[SNDRV_PCM_STREAM_PLAYBACK]
= nvhdmi_pcm_digital_playback_2ch;
return 0;
}
static struct hda_codec_ops nvhdmi_patch_ops_8ch_89 = {
.build_controls = nvhdmi_build_controls,
.build_pcms = nvhdmi_build_pcms_8ch_89,
.init = nvhdmi_init,
.free = nvhdmi_free,
.unsol_event = hdmi_unsol_event,
};
static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
.build_controls = nvhdmi_build_controls,
.build_pcms = nvhdmi_build_pcms_8ch_7x,
.init = nvhdmi_init,
.free = nvhdmi_free,
};
static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
.build_controls = nvhdmi_build_controls,
.build_pcms = nvhdmi_build_pcms_2ch,
.init = nvhdmi_init,
.free = nvhdmi_free,
};
static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
{
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;
spec->old_pin_detect = 1;
if (hdmi_parse_codec(codec) < 0) {
codec->spec = NULL;
kfree(spec);
return -EINVAL;
}
codec->patch_ops = nvhdmi_patch_ops_8ch_89;
for (i = 0; i < spec->num_pins; i++)
snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
init_channel_allocations();
return 0;
}
static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
{
struct hdmi_spec *spec;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
codec->spec = spec;
spec->multiout.num_dacs = 0; /* no analog */
spec->multiout.max_channels = 8;
spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
spec->old_pin_detect = 1;
codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
return 0;
}
static int patch_nvhdmi_2ch(struct hda_codec *codec)
{
struct hdmi_spec *spec;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
codec->spec = spec;
spec->multiout.num_dacs = 0; /* no analog */
spec->multiout.max_channels = 2;
spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
spec->old_pin_detect = 1;
codec->patch_ops = nvhdmi_patch_ops_2ch;
return 0;
}
/*
* patch entries
*/
static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
{ .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 = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
{} /* terminator */
};
MODULE_ALIAS("snd-hda-codec-id:10de0002");
MODULE_ALIAS("snd-hda-codec-id:10de0003");
MODULE_ALIAS("snd-hda-codec-id:10de0005");
MODULE_ALIAS("snd-hda-codec-id:10de0006");
MODULE_ALIAS("snd-hda-codec-id:10de0007");
MODULE_ALIAS("snd-hda-codec-id:10de000a");
MODULE_ALIAS("snd-hda-codec-id:10de000b");
MODULE_ALIAS("snd-hda-codec-id:10de000c");
MODULE_ALIAS("snd-hda-codec-id:10de000d");
MODULE_ALIAS("snd-hda-codec-id:10de0010");
MODULE_ALIAS("snd-hda-codec-id:10de0011");
MODULE_ALIAS("snd-hda-codec-id:10de0012");
MODULE_ALIAS("snd-hda-codec-id:10de0013");
MODULE_ALIAS("snd-hda-codec-id:10de0014");
MODULE_ALIAS("snd-hda-codec-id:10de0018");
MODULE_ALIAS("snd-hda-codec-id:10de0019");
MODULE_ALIAS("snd-hda-codec-id:10de001a");
MODULE_ALIAS("snd-hda-codec-id:10de001b");
MODULE_ALIAS("snd-hda-codec-id:10de001c");
MODULE_ALIAS("snd-hda-codec-id:10de0040");
MODULE_ALIAS("snd-hda-codec-id:10de0041");
MODULE_ALIAS("snd-hda-codec-id:10de0042");
MODULE_ALIAS("snd-hda-codec-id:10de0043");
MODULE_ALIAS("snd-hda-codec-id:10de0044");
MODULE_ALIAS("snd-hda-codec-id:10de0067");
MODULE_ALIAS("snd-hda-codec-id:10de8001");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("NVIDIA HDMI HD-audio codec");
static struct hda_codec_preset_list nvhdmi_list = {
.preset = snd_hda_preset_nvhdmi,
.owner = THIS_MODULE,
};
static int __init patch_nvhdmi_init(void)
{
return snd_hda_add_codec_preset(&nvhdmi_list);
}
static void __exit patch_nvhdmi_exit(void)
{
snd_hda_delete_codec_preset(&nvhdmi_list);
}
module_init(patch_nvhdmi_init)
module_exit(patch_nvhdmi_exit)

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,7 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/asoundef.h> #include <sound/asoundef.h>
#include <sound/jack.h> #include <sound/jack.h>
#include <sound/tlv.h>
#include "hda_codec.h" #include "hda_codec.h"
#include "hda_local.h" #include "hda_local.h"
#include "hda_beep.h" #include "hda_beep.h"
@ -263,6 +264,7 @@ struct sigmatel_spec {
struct sigmatel_mic_route ext_mic; struct sigmatel_mic_route ext_mic;
struct sigmatel_mic_route int_mic; struct sigmatel_mic_route int_mic;
struct sigmatel_mic_route dock_mic;
const char **spdif_labels; const char **spdif_labels;
@ -382,6 +384,11 @@ static unsigned int stac92hd83xxx_pwr_mapping[4] = {
0x03, 0x0c, 0x20, 0x40, 0x03, 0x0c, 0x20, 0x40,
}; };
#define STAC92HD83XXX_NUM_DMICS 2
static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
0x11, 0x20, 0
};
#define STAC92HD83XXX_NUM_CAPS 2 #define STAC92HD83XXX_NUM_CAPS 2
static unsigned long stac92hd83xxx_capvols[] = { static unsigned long stac92hd83xxx_capvols[] = {
HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT), HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
@ -986,7 +993,7 @@ static struct hda_verb stac9205_core_init[] = {
} }
static struct snd_kcontrol_new stac9200_mixer[] = { static struct snd_kcontrol_new stac9200_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT), HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
@ -1014,7 +1021,7 @@ static struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
}; };
static struct snd_kcontrol_new stac925x_mixer[] = { static struct snd_kcontrol_new stac925x_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0x0e, 0, HDA_OUTPUT), HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT),
{ } /* end */ { } /* end */
}; };
@ -1105,9 +1112,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
struct hda_input_mux *smux = &spec->private_smux; struct hda_input_mux *smux = &spec->private_smux;
/* check for mute support on SPDIF out */ /* check for mute support on SPDIF out */
if (wcaps & AC_WCAP_OUT_AMP) { if (wcaps & AC_WCAP_OUT_AMP) {
smux->items[smux->num_items].label = "Off"; snd_hda_add_imux_item(smux, "Off", 0, NULL);
smux->items[smux->num_items].index = 0;
smux->num_items++;
spec->spdif_mute = 1; spec->spdif_mute = 1;
} }
stac_smux_mixer.count = spec->num_smuxes; stac_smux_mixer.count = spec->num_smuxes;
@ -1140,6 +1145,8 @@ static int stac92xx_build_controls(struct hda_codec *codec)
HDA_OUTPUT, vmaster_tlv); HDA_OUTPUT, vmaster_tlv);
/* correct volume offset */ /* correct volume offset */
vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset; vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset;
/* minimum value is actually mute */
vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
err = snd_hda_add_vmaster(codec, "Master Playback Volume", err = snd_hda_add_vmaster(codec, "Master Playback Volume",
vmaster_tlv, slave_vols); vmaster_tlv, slave_vols);
if (err < 0) if (err < 0)
@ -1180,14 +1187,11 @@ static int stac92xx_build_controls(struct hda_codec *codec)
if (err < 0) if (err < 0)
return err; return err;
} }
for (i = 0; i < AUTO_PIN_LAST; i++) { for (i = 0; i < cfg->num_inputs; i++) {
nid = cfg->input_pins[i]; nid = cfg->inputs[i].pin;
if (nid) { err = stac92xx_add_jack(codec, nid, SND_JACK_MICROPHONE);
err = stac92xx_add_jack(codec, nid, if (err < 0)
SND_JACK_MICROPHONE); return err;
if (err < 0)
return err;
}
} }
return 0; return 0;
@ -2779,7 +2783,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
struct sigmatel_spec *spec = codec->spec; struct sigmatel_spec *spec = codec->spec;
char name[22]; char name[22];
if (!((get_defcfg_connect(def_conf)) & AC_JACK_PORT_FIXED)) { if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
&& nid == spec->line_switch) && nid == spec->line_switch)
control = STAC_CTL_WIDGET_IO_SWITCH; control = STAC_CTL_WIDGET_IO_SWITCH;
@ -2791,7 +2795,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
} }
if (control) { if (control) {
strcpy(name, auto_pin_cfg_labels[idx]); strcpy(name, hda_get_input_pin_label(codec, nid, 1));
return stac92xx_add_control(codec->spec, control, return stac92xx_add_control(codec->spec, control,
strcat(name, " Jack Mode"), nid); strcat(name, " Jack Mode"), nid);
} }
@ -2823,41 +2827,49 @@ static hda_nid_t check_line_out_switch(struct hda_codec *codec)
struct auto_pin_cfg *cfg = &spec->autocfg; struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid; hda_nid_t nid;
unsigned int pincap; unsigned int pincap;
int i;
if (cfg->line_out_type != AUTO_PIN_LINE_OUT) if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
return 0; return 0;
nid = cfg->input_pins[AUTO_PIN_LINE]; for (i = 0; i < cfg->num_inputs; i++) {
pincap = snd_hda_query_pin_caps(codec, nid); if (cfg->inputs[i].type == AUTO_PIN_LINE_IN) {
if (pincap & AC_PINCAP_OUT) nid = cfg->inputs[i].pin;
return nid;
return 0;
}
/* check whether the mic-input can be used as line-out */
static hda_nid_t check_mic_out_switch(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int def_conf, pincap;
unsigned int mic_pin;
if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
return 0;
mic_pin = AUTO_PIN_MIC;
for (;;) {
hda_nid_t nid = cfg->input_pins[mic_pin];
def_conf = snd_hda_codec_get_pincfg(codec, nid);
/* some laptops have an internal analog microphone
* which can't be used as a output */
if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
pincap = snd_hda_query_pin_caps(codec, nid); pincap = snd_hda_query_pin_caps(codec, nid);
if (pincap & AC_PINCAP_OUT) if (pincap & AC_PINCAP_OUT)
return nid; return nid;
} }
if (mic_pin == AUTO_PIN_MIC) }
mic_pin = AUTO_PIN_FRONT_MIC; return 0;
else }
break;
static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid);
/* check whether the mic-input can be used as line-out */
static hda_nid_t check_mic_out_switch(struct hda_codec *codec, hda_nid_t *dac)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int def_conf, pincap;
int i;
*dac = 0;
if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
return 0;
for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t nid = cfg->inputs[i].pin;
if (cfg->inputs[i].type != AUTO_PIN_MIC)
continue;
def_conf = snd_hda_codec_get_pincfg(codec, nid);
/* some laptops have an internal analog microphone
* which can't be used as a output */
if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
pincap = snd_hda_query_pin_caps(codec, nid);
if (pincap & AC_PINCAP_OUT) {
*dac = get_unassigned_dac(codec, nid);
if (*dac)
return nid;
}
}
} }
return 0; return 0;
} }
@ -3004,17 +3016,14 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec)
} }
} }
/* add mic as output */ /* add mic as output */
nid = check_mic_out_switch(codec); nid = check_mic_out_switch(codec, &dac);
if (nid) { if (nid && dac) {
dac = get_unassigned_dac(codec, nid); snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
if (dac) { nid, cfg->line_outs);
snd_printdd("STAC: Add mic-in 0x%x as output %d\n", cfg->line_out_pins[cfg->line_outs] = nid;
nid, cfg->line_outs); cfg->line_outs++;
cfg->line_out_pins[cfg->line_outs] = nid; spec->mic_switch = nid;
cfg->line_outs++; add_spec_dacs(spec, dac);
spec->mic_switch = nid;
add_spec_dacs(spec, dac);
}
} }
snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
@ -3204,13 +3213,13 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
return err; return err;
} }
for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) { for (idx = 0; idx < cfg->num_inputs; idx++) {
nid = cfg->input_pins[idx]; if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
if (nid) { break;
err = stac92xx_add_jack_mode_control(codec, nid, idx); nid = cfg->inputs[idx].pin;
if (err < 0) err = stac92xx_add_jack_mode_control(codec, nid, idx);
return err; if (err < 0)
} return err;
} }
return 0; return 0;
@ -3256,12 +3265,9 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels)) if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
return -EINVAL; return -EINVAL;
for (i = 0; i < num_cons; i++) { for (i = 0; i < num_cons; i++)
mono_mux->items[mono_mux->num_items].label = snd_hda_add_imux_item(mono_mux, stac92xx_mono_labels[i], i,
stac92xx_mono_labels[i]; NULL);
mono_mux->items[mono_mux->num_items].index = i;
mono_mux->num_items++;
}
return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX, return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
"Mono Mux", spec->mono_nid); "Mono Mux", spec->mono_nid);
@ -3386,11 +3392,8 @@ static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec)
if (!labels) if (!labels)
labels = stac92xx_spdif_labels; labels = stac92xx_spdif_labels;
for (i = 0; i < num_cons; i++) { for (i = 0; i < num_cons; i++)
spdif_mux->items[spdif_mux->num_items].label = labels[i]; snd_hda_add_imux_item(spdif_mux, labels[i], i, NULL);
spdif_mux->items[spdif_mux->num_items].index = i;
spdif_mux->num_items++;
}
return 0; return 0;
} }
@ -3417,7 +3420,7 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
/* create a volume assigned to the given pin (only if supported) */ /* create a volume assigned to the given pin (only if supported) */
/* return 1 if the volume control is created */ /* return 1 if the volume control is created */
static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid, static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
const char *label, int direction) const char *label, int idx, int direction)
{ {
unsigned int caps, nums; unsigned int caps, nums;
char name[32]; char name[32];
@ -3434,8 +3437,8 @@ static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
if (!nums) if (!nums)
return 0; return 0;
snprintf(name, sizeof(name), "%s Capture Volume", label); snprintf(name, sizeof(name), "%s Capture Volume", label);
err = stac92xx_add_control(codec->spec, STAC_CTL_WIDGET_VOL, name, err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name,
HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction)); HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
if (err < 0) if (err < 0)
return err; return err;
return 1; return 1;
@ -3448,27 +3451,14 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
struct sigmatel_spec *spec = codec->spec; struct sigmatel_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux; struct hda_input_mux *imux = &spec->private_imux;
struct hda_input_mux *dimux = &spec->private_dimux; struct hda_input_mux *dimux = &spec->private_dimux;
int err, i, active_mics; int err, i;
unsigned int def_conf; unsigned int def_conf;
dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0]; snd_hda_add_imux_item(dimux, stac92xx_dmic_labels[0], 0, NULL);
dimux->items[dimux->num_items].index = 0;
dimux->num_items++;
active_mics = 0;
for (i = 0; i < spec->num_dmics; i++) {
/* check the validity: sometimes it's a dead vendor-spec node */
if (get_wcaps_type(get_wcaps(codec, spec->dmic_nids[i]))
!= AC_WID_PIN)
continue;
def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]);
if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
active_mics++;
}
for (i = 0; i < spec->num_dmics; i++) { for (i = 0; i < spec->num_dmics; i++) {
hda_nid_t nid; hda_nid_t nid;
int index; int index, type_idx;
const char *label; const char *label;
nid = spec->dmic_nids[i]; nid = spec->dmic_nids[i];
@ -3482,28 +3472,23 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
if (index < 0) if (index < 0)
continue; continue;
if (active_mics == 1) label = hda_get_input_pin_label(codec, nid, 1);
label = "Digital Mic"; snd_hda_add_imux_item(dimux, label, index, &type_idx);
else
label = stac92xx_dmic_labels[dimux->num_items];
err = create_elem_capture_vol(codec, nid, label, HDA_INPUT); err = create_elem_capture_vol(codec, nid, label, type_idx,
HDA_INPUT);
if (err < 0) if (err < 0)
return err; return err;
if (!err) { if (!err) {
err = create_elem_capture_vol(codec, nid, label, err = create_elem_capture_vol(codec, nid, label,
HDA_OUTPUT); type_idx, HDA_OUTPUT);
if (err < 0) if (err < 0)
return err; return err;
} }
dimux->items[dimux->num_items].label = label;
dimux->items[dimux->num_items].index = index;
dimux->num_items++;
if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) { if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) {
imux->items[imux->num_items].label = label; snd_hda_add_imux_item(imux, label, index, NULL);
imux->items[imux->num_items].index = index; spec->num_analog_muxes++;
imux->num_items++;
} }
} }
@ -3511,20 +3496,27 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
} }
static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid, static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *fixed, hda_nid_t *ext) hda_nid_t *fixed, hda_nid_t *ext, hda_nid_t *dock)
{ {
unsigned int cfg; unsigned int cfg;
if (!nid) if (!nid)
return 0; return 0;
cfg = snd_hda_codec_get_pincfg(codec, nid); cfg = snd_hda_codec_get_pincfg(codec, nid);
switch (get_defcfg_connect(cfg)) { switch (snd_hda_get_input_pin_attr(cfg)) {
case AC_JACK_PORT_FIXED: case INPUT_PIN_ATTR_INT:
if (*fixed) if (*fixed)
return 1; /* already occupied */ return 1; /* already occupied */
*fixed = nid; *fixed = nid;
break; break;
case AC_JACK_PORT_COMPLEX: case INPUT_PIN_ATTR_UNUSED:
break;
case INPUT_PIN_ATTR_DOCK:
if (*dock)
return 1; /* already occupied */
*dock = nid;
break;
default:
if (*ext) if (*ext)
return 1; /* already occupied */ return 1; /* already occupied */
*ext = nid; *ext = nid;
@ -3542,10 +3534,13 @@ static int set_mic_route(struct hda_codec *codec,
int i; int i;
mic->pin = pin; mic->pin = pin;
for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) if (pin == 0)
if (pin == cfg->input_pins[i]) return 0;
for (i = 0; i < cfg->num_inputs; i++) {
if (pin == cfg->inputs[i].pin)
break; break;
if (i <= AUTO_PIN_FRONT_MIC) { }
if (i < cfg->num_inputs && cfg->inputs[i].type == AUTO_PIN_MIC) {
/* analog pin */ /* analog pin */
i = get_connection_index(codec, spec->mux_nids[0], pin); i = get_connection_index(codec, spec->mux_nids[0], pin);
if (i < 0) if (i < 0)
@ -3576,26 +3571,29 @@ static int stac_check_auto_mic(struct hda_codec *codec)
{ {
struct sigmatel_spec *spec = codec->spec; struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg; struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t fixed, ext; hda_nid_t fixed, ext, dock;
int i; int i;
for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) { for (i = 0; i < cfg->num_inputs; i++) {
if (cfg->input_pins[i]) if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
return 0; /* must be exclusively mics */ return 0; /* must be exclusively mics */
} }
fixed = ext = 0; fixed = ext = dock = 0;
for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) for (i = 0; i < cfg->num_inputs; i++)
if (check_mic_pin(codec, cfg->input_pins[i], &fixed, &ext)) if (check_mic_pin(codec, cfg->inputs[i].pin,
&fixed, &ext, &dock))
return 0; return 0;
for (i = 0; i < spec->num_dmics; i++) for (i = 0; i < spec->num_dmics; i++)
if (check_mic_pin(codec, spec->dmic_nids[i], &fixed, &ext)) if (check_mic_pin(codec, spec->dmic_nids[i],
&fixed, &ext, &dock))
return 0; return 0;
if (!fixed || !ext) if (!fixed && !ext && !dock)
return 0; return 0; /* no input to switch */
if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP)) if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
return 0; /* no unsol support */ return 0; /* no unsol support */
if (set_mic_route(codec, &spec->ext_mic, ext) || if (set_mic_route(codec, &spec->ext_mic, ext) ||
set_mic_route(codec, &spec->int_mic, fixed)) set_mic_route(codec, &spec->int_mic, fixed) ||
set_mic_route(codec, &spec->dock_mic, dock))
return 0; /* something is wrong */ return 0; /* something is wrong */
return 1; return 1;
} }
@ -3606,13 +3604,12 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
struct sigmatel_spec *spec = codec->spec; struct sigmatel_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux; struct hda_input_mux *imux = &spec->private_imux;
int i, j; int i, j;
const char *label;
for (i = 0; i < AUTO_PIN_LAST; i++) { for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t nid = cfg->input_pins[i]; hda_nid_t nid = cfg->inputs[i].pin;
int index, err; int index, err, type_idx;
if (!nid)
continue;
index = -1; index = -1;
for (j = 0; j < spec->num_muxes; j++) { for (j = 0; j < spec->num_muxes; j++) {
index = get_connection_index(codec, spec->mux_nids[j], index = get_connection_index(codec, spec->mux_nids[j],
@ -3623,15 +3620,14 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
if (index < 0) if (index < 0)
continue; continue;
label = hda_get_autocfg_input_label(codec, cfg, i);
snd_hda_add_imux_item(imux, label, index, &type_idx);
err = create_elem_capture_vol(codec, nid, err = create_elem_capture_vol(codec, nid,
auto_pin_cfg_labels[i], label, type_idx,
HDA_INPUT); HDA_INPUT);
if (err < 0) if (err < 0)
return err; return err;
imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
imux->items[imux->num_items].index = index;
imux->num_items++;
} }
spec->num_analog_muxes = imux->num_items; spec->num_analog_muxes = imux->num_items;
@ -4305,38 +4301,38 @@ static int stac92xx_init(struct hda_codec *codec)
AC_VERB_SET_CONNECT_SEL, 0); AC_VERB_SET_CONNECT_SEL, 0);
if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT)) if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT))
stac_issue_unsol_event(codec, spec->ext_mic.pin); stac_issue_unsol_event(codec, spec->ext_mic.pin);
if (enable_pin_detect(codec, spec->dock_mic.pin,
STAC_MIC_EVENT))
stac_issue_unsol_event(codec, spec->dock_mic.pin);
} }
for (i = 0; i < AUTO_PIN_LAST; i++) { for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t nid = cfg->input_pins[i]; hda_nid_t nid = cfg->inputs[i].pin;
if (nid) { int type = cfg->inputs[i].type;
unsigned int pinctl, conf; unsigned int pinctl, conf;
if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) { if (type == AUTO_PIN_MIC) {
/* for mic pins, force to initialize */ /* for mic pins, force to initialize */
pinctl = stac92xx_get_default_vref(codec, nid); pinctl = stac92xx_get_default_vref(codec, nid);
pinctl |= AC_PINCTL_IN_EN;
stac92xx_auto_set_pinctl(codec, nid, pinctl);
} else {
pinctl = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
/* if PINCTL already set then skip */
/* Also, if both INPUT and OUTPUT are set,
* it must be a BIOS bug; need to override, too
*/
if (!(pinctl & AC_PINCTL_IN_EN) ||
(pinctl & AC_PINCTL_OUT_EN)) {
pinctl &= ~AC_PINCTL_OUT_EN;
pinctl |= AC_PINCTL_IN_EN; pinctl |= AC_PINCTL_IN_EN;
stac92xx_auto_set_pinctl(codec, nid, pinctl); stac92xx_auto_set_pinctl(codec, nid, pinctl);
} else {
pinctl = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
/* if PINCTL already set then skip */
/* Also, if both INPUT and OUTPUT are set,
* it must be a BIOS bug; need to override, too
*/
if (!(pinctl & AC_PINCTL_IN_EN) ||
(pinctl & AC_PINCTL_OUT_EN)) {
pinctl &= ~AC_PINCTL_OUT_EN;
pinctl |= AC_PINCTL_IN_EN;
stac92xx_auto_set_pinctl(codec, nid,
pinctl);
}
}
conf = snd_hda_codec_get_pincfg(codec, nid);
if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
if (enable_pin_detect(codec, nid,
STAC_INSERT_EVENT))
stac_issue_unsol_event(codec, nid);
} }
} }
conf = snd_hda_codec_get_pincfg(codec, nid);
if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
if (enable_pin_detect(codec, nid, STAC_INSERT_EVENT))
stac_issue_unsol_event(codec, nid);
}
} }
for (i = 0; i < spec->num_dmics; i++) for (i = 0; i < spec->num_dmics; i++)
stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i], stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
@ -4383,11 +4379,9 @@ static int stac92xx_init(struct hda_codec *codec)
stac_issue_unsol_event(codec, nid); stac_issue_unsol_event(codec, nid);
} }
#ifdef CONFIG_SND_HDA_POWER_SAVE
/* sync mute LED */ /* sync mute LED */
if (spec->gpio_led && codec->patch_ops.check_power_status) if (spec->gpio_led)
codec->patch_ops.check_power_status(codec, 0x01); hda_call_check_power_status(codec, 0x01);
#endif
if (spec->dac_list) if (spec->dac_list)
stac92xx_power_down(codec); stac92xx_power_down(codec);
return 0; return 0;
@ -4688,6 +4682,36 @@ static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
} }
} }
/* get the pin connection (fixed, none, etc) */
static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
{
struct sigmatel_spec *spec = codec->spec;
unsigned int cfg;
cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
return get_defcfg_connect(cfg);
}
static int stac92xx_connected_ports(struct hda_codec *codec,
hda_nid_t *nids, int num_nids)
{
struct sigmatel_spec *spec = codec->spec;
int idx, num;
unsigned int def_conf;
for (num = 0; num < num_nids; num++) {
for (idx = 0; idx < spec->num_pins; idx++)
if (spec->pin_nids[idx] == nids[num])
break;
if (idx >= spec->num_pins)
break;
def_conf = stac_get_defcfg_connect(codec, idx);
if (def_conf == AC_JACK_PORT_NONE)
break;
}
return num;
}
static void stac92xx_mic_detect(struct hda_codec *codec) static void stac92xx_mic_detect(struct hda_codec *codec)
{ {
struct sigmatel_spec *spec = codec->spec; struct sigmatel_spec *spec = codec->spec;
@ -4695,6 +4719,8 @@ static void stac92xx_mic_detect(struct hda_codec *codec)
if (get_pin_presence(codec, spec->ext_mic.pin)) if (get_pin_presence(codec, spec->ext_mic.pin))
mic = &spec->ext_mic; mic = &spec->ext_mic;
else if (get_pin_presence(codec, spec->dock_mic.pin))
mic = &spec->dock_mic;
else else
mic = &spec->int_mic; mic = &spec->int_mic;
if (mic->dmux_idx >= 0) if (mic->dmux_idx >= 0)
@ -4937,11 +4963,9 @@ static int stac92xx_resume(struct hda_codec *codec)
stac_issue_unsol_event(codec, stac_issue_unsol_event(codec,
spec->autocfg.line_out_pins[0]); spec->autocfg.line_out_pins[0]);
} }
#ifdef CONFIG_SND_HDA_POWER_SAVE
/* sync mute LED */ /* sync mute LED */
if (spec->gpio_led && codec->patch_ops.check_power_status) if (spec->gpio_led)
codec->patch_ops.check_power_status(codec, 0x01); hda_call_check_power_status(codec, 0x01);
#endif
return 0; return 0;
} }
@ -5313,11 +5337,16 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
if (spec == NULL) if (spec == NULL)
return -ENOMEM; return -ENOMEM;
/* reset pin power-down; Windows may leave these bits after reboot */
snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7EC, 0);
snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7ED, 0);
codec->no_trigger_sense = 1; codec->no_trigger_sense = 1;
codec->spec = spec; codec->spec = spec;
spec->linear_tone_beep = 1; spec->linear_tone_beep = 1;
codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs; codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
spec->digbeep_nid = 0x21; spec->digbeep_nid = 0x21;
spec->dmic_nids = stac92hd83xxx_dmic_nids;
spec->dmux_nids = stac92hd83xxx_mux_nids;
spec->mux_nids = stac92hd83xxx_mux_nids; spec->mux_nids = stac92hd83xxx_mux_nids;
spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids); spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids);
spec->adc_nids = stac92hd83xxx_adc_nids; spec->adc_nids = stac92hd83xxx_adc_nids;
@ -5363,9 +5392,13 @@ again:
case 0x111d76d4: case 0x111d76d4:
case 0x111d7605: case 0x111d7605:
case 0x111d76d5: case 0x111d76d5:
case 0x111d76e7:
if (spec->board_config == STAC_92HD83XXX_PWR_REF) if (spec->board_config == STAC_92HD83XXX_PWR_REF)
break; break;
spec->num_pwrs = 0; spec->num_pwrs = 0;
spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd83xxx_dmic_nids,
STAC92HD83XXX_NUM_DMICS);
break; break;
} }
@ -5424,36 +5457,6 @@ again:
return 0; return 0;
} }
/* get the pin connection (fixed, none, etc) */
static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
{
struct sigmatel_spec *spec = codec->spec;
unsigned int cfg;
cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
return get_defcfg_connect(cfg);
}
static int stac92hd71bxx_connected_ports(struct hda_codec *codec,
hda_nid_t *nids, int num_nids)
{
struct sigmatel_spec *spec = codec->spec;
int idx, num;
unsigned int def_conf;
for (num = 0; num < num_nids; num++) {
for (idx = 0; idx < spec->num_pins; idx++)
if (spec->pin_nids[idx] == nids[num])
break;
if (idx >= spec->num_pins)
break;
def_conf = stac_get_defcfg_connect(codec, idx);
if (def_conf == AC_JACK_PORT_NONE)
break;
}
return num;
}
static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec, static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec,
hda_nid_t dig0pin) hda_nid_t dig0pin)
{ {
@ -5592,7 +5595,7 @@ again:
case 0x111d76b5: case 0x111d76b5:
spec->init = stac92hd71bxx_core_init; spec->init = stac92hd71bxx_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
spec->num_dmics = stac92hd71bxx_connected_ports(codec, spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids, stac92hd71bxx_dmic_nids,
STAC92HD71BXX_NUM_DMICS); STAC92HD71BXX_NUM_DMICS);
break; break;
@ -5624,7 +5627,7 @@ again:
snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0); snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3); snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS - 1] = 0; stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS - 1] = 0;
spec->num_dmics = stac92hd71bxx_connected_ports(codec, spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids, stac92hd71bxx_dmic_nids,
STAC92HD71BXX_NUM_DMICS - 1); STAC92HD71BXX_NUM_DMICS - 1);
break; break;
@ -5638,7 +5641,7 @@ again:
default: default:
spec->init = stac92hd71bxx_core_init; spec->init = stac92hd71bxx_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
spec->num_dmics = stac92hd71bxx_connected_ports(codec, spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids, stac92hd71bxx_dmic_nids,
STAC92HD71BXX_NUM_DMICS); STAC92HD71BXX_NUM_DMICS);
break; break;
@ -6320,6 +6323,8 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx }, { .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
{ .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx }, { .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx },
{ .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx }, { .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx },
{ .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx},
{ .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx},
{} /* terminator */ {} /* terminator */
}; };

View File

@ -444,8 +444,8 @@ static hda_nid_t vt1812_adc_nids[2] = {
/* add dynamic controls */ /* add dynamic controls */
static int via_add_control(struct via_spec *spec, int type, const char *name, static int __via_add_control(struct via_spec *spec, int type, const char *name,
unsigned long val) int idx, unsigned long val)
{ {
struct snd_kcontrol_new *knew; struct snd_kcontrol_new *knew;
@ -463,6 +463,9 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
return 0; return 0;
} }
#define via_add_control(spec, type, name, val) \
__via_add_control(spec, type, name, 0, val)
static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec, static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
struct snd_kcontrol_new *tmpl) struct snd_kcontrol_new *tmpl)
{ {
@ -494,18 +497,18 @@ static void via_free_kctls(struct hda_codec *codec)
/* create input playback/capture controls for the given pin */ /* create input playback/capture controls for the given pin */
static int via_new_analog_input(struct via_spec *spec, const char *ctlname, static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
int idx, int mix_nid) int type_idx, int idx, int mix_nid)
{ {
char name[32]; char name[32];
int err; int err;
sprintf(name, "%s Playback Volume", ctlname); sprintf(name, "%s Playback Volume", ctlname);
err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0) if (err < 0)
return err; return err;
sprintf(name, "%s Playback Switch", ctlname); sprintf(name, "%s Playback Switch", ctlname);
err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0) if (err < 0)
return err; return err;
@ -557,17 +560,15 @@ static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
static void via_auto_init_analog_input(struct hda_codec *codec) static void via_auto_init_analog_input(struct hda_codec *codec)
{ {
struct via_spec *spec = codec->spec; struct via_spec *spec = codec->spec;
const struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int ctl; unsigned int ctl;
int i; int i;
for (i = 0; i < AUTO_PIN_LAST; i++) { for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t nid = spec->autocfg.input_pins[i]; hda_nid_t nid = cfg->inputs[i].pin;
if (!nid)
continue;
if (spec->smart51_enabled && is_smart51_pins(spec, nid)) if (spec->smart51_enabled && is_smart51_pins(spec, nid))
ctl = PIN_OUT; ctl = PIN_OUT;
else if (i <= AUTO_PIN_FRONT_MIC) else if (i == AUTO_PIN_MIC)
ctl = PIN_VREF50; ctl = PIN_VREF50;
else else
ctl = PIN_IN; ctl = PIN_IN;
@ -1322,15 +1323,14 @@ static void mute_aa_path(struct hda_codec *codec, int mute)
} }
static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
{ {
int res = 0; const struct auto_pin_cfg *cfg = &spec->autocfg;
int index; int i;
for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
if (pin == spec->autocfg.input_pins[index]) { for (i = 0; i < cfg->num_inputs; i++) {
res = 1; if (pin == cfg->inputs[i].pin)
break; return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
}
} }
return res; return 0;
} }
static int via_smart51_info(struct snd_kcontrol *kcontrol, static int via_smart51_info(struct snd_kcontrol *kcontrol,
@ -1348,25 +1348,21 @@ static int via_smart51_get(struct snd_kcontrol *kcontrol,
{ {
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec; struct via_spec *spec = codec->spec;
int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; const struct auto_pin_cfg *cfg = &spec->autocfg;
int on = 1; int on = 1;
int i; int i;
for (i = 0; i < ARRAY_SIZE(index); i++) { for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t nid = spec->autocfg.input_pins[index[i]]; hda_nid_t nid = cfg->inputs[i].pin;
if (nid) { int ctl = snd_hda_codec_read(codec, nid, 0,
int ctl = AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
snd_hda_codec_read(codec, nid, 0, if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
AC_VERB_GET_PIN_WIDGET_CONTROL, continue;
0); if (cfg->inputs[i].type == AUTO_PIN_MIC &&
if (i == AUTO_PIN_FRONT_MIC spec->hp_independent_mode && spec->codec_type != VT1718S)
&& spec->hp_independent_mode continue; /* ignore FMic for independent HP */
&& spec->codec_type != VT1718S) if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
continue; /* ignore FMic for independent HP */ on = 0;
if (ctl & AC_PINCTL_IN_EN
&& !(ctl & AC_PINCTL_OUT_EN))
on = 0;
}
} }
*ucontrol->value.integer.value = on; *ucontrol->value.integer.value = on;
return 0; return 0;
@ -1377,36 +1373,38 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol,
{ {
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec; struct via_spec *spec = codec->spec;
const struct auto_pin_cfg *cfg = &spec->autocfg;
int out_in = *ucontrol->value.integer.value int out_in = *ucontrol->value.integer.value
? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
int i; int i;
for (i = 0; i < ARRAY_SIZE(index); i++) { for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t nid = spec->autocfg.input_pins[index[i]]; hda_nid_t nid = cfg->inputs[i].pin;
if (i == AUTO_PIN_FRONT_MIC unsigned int parm;
&& spec->hp_independent_mode
&& spec->codec_type != VT1718S) if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
continue;
if (cfg->inputs[i].type == AUTO_PIN_MIC &&
spec->hp_independent_mode && spec->codec_type != VT1718S)
continue; /* don't retask FMic for independent HP */ continue; /* don't retask FMic for independent HP */
if (nid) {
unsigned int parm = snd_hda_codec_read( parm = snd_hda_codec_read(codec, nid, 0,
codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
AC_VERB_GET_PIN_WIDGET_CONTROL, 0); parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); parm |= out_in;
parm |= out_in; snd_hda_codec_write(codec, nid, 0,
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
parm); if (out_in == AC_PINCTL_OUT_EN) {
if (out_in == AC_PINCTL_OUT_EN) { mute_aa_path(codec, 1);
mute_aa_path(codec, 1); notify_aa_path_ctls(codec);
notify_aa_path_ctls(codec); }
} if (spec->codec_type == VT1718S) {
if (spec->codec_type == VT1718S) snd_hda_codec_amp_stereo(
snd_hda_codec_amp_stereo(
codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
HDA_AMP_UNMUTE); HDA_AMP_UNMUTE);
} }
if (i == AUTO_PIN_FRONT_MIC) { if (cfg->inputs[i].type == AUTO_PIN_MIC) {
if (spec->codec_type == VT1708S if (spec->codec_type == VT1708S
|| spec->codec_type == VT1716S) { || spec->codec_type == VT1716S) {
/* input = index 1 (AOW3) */ /* input = index 1 (AOW3) */
@ -1442,7 +1440,7 @@ static struct snd_kcontrol_new via_smart51_mixer[2] = {
static int via_smart51_build(struct via_spec *spec) static int via_smart51_build(struct via_spec *spec)
{ {
struct snd_kcontrol_new *knew; struct snd_kcontrol_new *knew;
int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; const struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid; hda_nid_t nid;
int i; int i;
@ -1450,13 +1448,14 @@ static int via_smart51_build(struct via_spec *spec)
if (knew == NULL) if (knew == NULL)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(index); i++) { for (i = 0; i < cfg->num_inputs; i++) {
nid = spec->autocfg.input_pins[index[i]]; nid = cfg->inputs[i].pin;
if (nid) { if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
knew = via_clone_control(spec, &via_smart51_mixer[1]); knew = via_clone_control(spec, &via_smart51_mixer[1]);
if (knew == NULL) if (knew == NULL)
return -ENOMEM; return -ENOMEM;
knew->subdevice = nid; knew->subdevice = nid;
break;
} }
} }
@ -2375,13 +2374,8 @@ static void create_hp_imux(struct via_spec *spec)
static const char *texts[] = { "OFF", "ON", NULL}; static const char *texts[] = { "OFF", "ON", NULL};
/* for hp mode select */ /* for hp mode select */
i = 0; for (i = 0; texts[i]; i++)
while (texts[i] != NULL) { snd_hda_add_imux_item(imux, texts[i], i, NULL);
imux->items[imux->num_items].label = texts[i];
imux->items[imux->num_items].index = i;
imux->num_items++;
i++;
}
spec->hp_mux = &spec->private_imux[1]; spec->hp_mux = &spec->private_imux[1];
} }
@ -2413,51 +2407,53 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
} }
/* create playback/capture controls for input pins */ /* create playback/capture controls for input pins */
static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec, static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg) const struct auto_pin_cfg *cfg,
hda_nid_t cap_nid,
hda_nid_t pin_idxs[], int num_idxs)
{ {
static char *labels[] = { struct via_spec *spec = codec->spec;
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
};
struct hda_input_mux *imux = &spec->private_imux[0]; struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx = 0; int i, err, idx, type, type_idx = 0;
/* for internal loopback recording select */ /* for internal loopback recording select */
imux->items[imux->num_items].label = "Stereo Mixer"; for (idx = 0; idx < num_idxs; idx++) {
imux->items[imux->num_items].index = idx; if (pin_idxs[idx] == 0xff) {
imux->num_items++; snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
for (i = 0; i < AUTO_PIN_LAST; i++) {
if (!cfg->input_pins[i])
continue;
switch (cfg->input_pins[i]) {
case 0x1d: /* Mic */
idx = 2;
break;
case 0x1e: /* Line In */
idx = 3;
break;
case 0x21: /* Front Mic */
idx = 4;
break;
case 0x24: /* CD */
idx = 1;
break; break;
} }
err = via_new_analog_input(spec, labels[i], idx, 0x17); }
for (i = 0; i < cfg->num_inputs; i++) {
const char *label;
type = cfg->inputs[i].type;
for (idx = 0; idx < num_idxs; idx++)
if (pin_idxs[idx] == cfg->inputs[i].pin)
break;
if (idx >= num_idxs)
continue;
if (i > 0 && type == cfg->inputs[i - 1].type)
type_idx++;
else
type_idx = 0;
label = hda_get_autocfg_input_label(codec, cfg, i);
err = via_new_analog_input(spec, label, type_idx, idx, cap_nid);
if (err < 0) if (err < 0)
return err; return err;
imux->items[imux->num_items].label = labels[i]; snd_hda_add_imux_item(imux, label, idx, NULL);
imux->items[imux->num_items].index = idx;
imux->num_items++;
} }
return 0; return 0;
} }
/* create playback/capture controls for input pins */
static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 };
return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs,
ARRAY_SIZE(pin_idxs));
}
#ifdef CONFIG_SND_HDA_POWER_SAVE #ifdef CONFIG_SND_HDA_POWER_SAVE
static struct hda_amp_list vt1708_loopbacks[] = { static struct hda_amp_list vt1708_loopbacks[] = {
{ 0x17, HDA_INPUT, 1 }, { 0x17, HDA_INPUT, 1 },
@ -2554,7 +2550,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0) if (err < 0)
return err; return err;
err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg); err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0) if (err < 0)
return err; return err;
/* add jack detect on/off control */ /* add jack detect on/off control */
@ -3021,49 +3017,12 @@ static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
} }
/* create playback/capture controls for input pins */ /* create playback/capture controls for input pins */
static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec, static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg) const struct auto_pin_cfg *cfg)
{ {
static char *labels[] = { static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 };
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs,
}; ARRAY_SIZE(pin_idxs));
struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx = 0;
/* for internal loopback recording select */
imux->items[imux->num_items].label = "Stereo Mixer";
imux->items[imux->num_items].index = idx;
imux->num_items++;
for (i = 0; i < AUTO_PIN_LAST; i++) {
if (!cfg->input_pins[i])
continue;
switch (cfg->input_pins[i]) {
case 0x1d: /* Mic */
idx = 2;
break;
case 0x1e: /* Line In */
idx = 3;
break;
case 0x21: /* Front Mic */
idx = 4;
break;
case 0x23: /* CD */
idx = 1;
break;
}
err = via_new_analog_input(spec, labels[i], idx, 0x18);
if (err < 0)
return err;
imux->items[imux->num_items].label = labels[i];
imux->items[imux->num_items].index = idx;
imux->num_items++;
}
return 0;
} }
static int vt1709_parse_auto_config(struct hda_codec *codec) static int vt1709_parse_auto_config(struct hda_codec *codec)
@ -3086,7 +3045,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0) if (err < 0)
return err; return err;
err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg); err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0) if (err < 0)
return err; return err;
@ -3588,49 +3547,12 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
} }
/* create playback/capture controls for input pins */ /* create playback/capture controls for input pins */
static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec, static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg) const struct auto_pin_cfg *cfg)
{ {
static char *labels[] = { static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e };
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
}; ARRAY_SIZE(pin_idxs));
struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx = 0;
/* for internal loopback recording select */
imux->items[imux->num_items].label = "Stereo Mixer";
imux->items[imux->num_items].index = idx;
imux->num_items++;
for (i = 0; i < AUTO_PIN_LAST; i++) {
if (!cfg->input_pins[i])
continue;
switch (cfg->input_pins[i]) {
case 0x1a: /* Mic */
idx = 2;
break;
case 0x1b: /* Line In */
idx = 3;
break;
case 0x1e: /* Front Mic */
idx = 4;
break;
case 0x1f: /* CD */
idx = 1;
break;
}
err = via_new_analog_input(spec, labels[i], idx, 0x16);
if (err < 0)
return err;
imux->items[imux->num_items].label = labels[i];
imux->items[imux->num_items].index = idx;
imux->num_items++;
}
return 0;
} }
static int vt1708B_parse_auto_config(struct hda_codec *codec) static int vt1708B_parse_auto_config(struct hda_codec *codec)
@ -3653,7 +3575,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0) if (err < 0)
return err; return err;
err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg); err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0) if (err < 0)
return err; return err;
@ -4061,49 +3983,12 @@ static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
} }
/* create playback/capture controls for input pins */ /* create playback/capture controls for input pins */
static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec, static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg) const struct auto_pin_cfg *cfg)
{ {
static char *labels[] = { static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
}; ARRAY_SIZE(pin_idxs));
struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx = 0;
/* for internal loopback recording select */
imux->items[imux->num_items].label = "Stereo Mixer";
imux->items[imux->num_items].index = 5;
imux->num_items++;
for (i = 0; i < AUTO_PIN_LAST; i++) {
if (!cfg->input_pins[i])
continue;
switch (cfg->input_pins[i]) {
case 0x1a: /* Mic */
idx = 2;
break;
case 0x1b: /* Line In */
idx = 3;
break;
case 0x1e: /* Front Mic */
idx = 4;
break;
case 0x1f: /* CD */
idx = 1;
break;
}
err = via_new_analog_input(spec, labels[i], idx, 0x16);
if (err < 0)
return err;
imux->items[imux->num_items].label = labels[i];
imux->items[imux->num_items].index = idx-1;
imux->num_items++;
}
return 0;
} }
/* fill out digital output widgets; one for master and one for slave outputs */ /* fill out digital output widgets; one for master and one for slave outputs */
@ -4151,7 +4036,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0) if (err < 0)
return err; return err;
err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg); err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0) if (err < 0)
return err; return err;
@ -4441,58 +4326,20 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
imux = &spec->private_imux[1]; imux = &spec->private_imux[1];
/* for hp mode select */ /* for hp mode select */
i = 0; for (i = 0; texts[i]; i++)
while (texts[i] != NULL) { snd_hda_add_imux_item(imux, texts[i], i, NULL);
imux->items[imux->num_items].label = texts[i];
imux->items[imux->num_items].index = i;
imux->num_items++;
i++;
}
spec->hp_mux = &spec->private_imux[1]; spec->hp_mux = &spec->private_imux[1];
return 0; return 0;
} }
/* create playback/capture controls for input pins */ /* create playback/capture controls for input pins */
static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec, static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg) const struct auto_pin_cfg *cfg)
{ {
static char *labels[] = { static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff };
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs,
}; ARRAY_SIZE(pin_idxs));
struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx = 0;
/* for internal loopback recording select */
imux->items[imux->num_items].label = "Stereo Mixer";
imux->items[imux->num_items].index = 3;
imux->num_items++;
for (i = 0; i < AUTO_PIN_LAST; i++) {
if (!cfg->input_pins[i])
continue;
switch (cfg->input_pins[i]) {
case 0x14: /* Mic */
idx = 1;
break;
case 0x15: /* Line In */
idx = 2;
break;
case 0x18: /* Front Mic */
idx = 3;
break;
}
err = via_new_analog_input(spec, labels[i], idx, 0x1A);
if (err < 0)
return err;
imux->items[imux->num_items].label = labels[i];
imux->items[imux->num_items].index = idx-1;
imux->num_items++;
}
return 0;
} }
static int vt1702_parse_auto_config(struct hda_codec *codec) static int vt1702_parse_auto_config(struct hda_codec *codec)
@ -4521,7 +4368,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
(0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
(0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
(1 << AC_AMPCAP_MUTE_SHIFT)); (1 << AC_AMPCAP_MUTE_SHIFT));
err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg); err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0) if (err < 0)
return err; return err;
@ -4872,49 +4719,12 @@ static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
} }
/* create playback/capture controls for input pins */ /* create playback/capture controls for input pins */
static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec, static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg) const struct auto_pin_cfg *cfg)
{ {
static char *labels[] = { static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff };
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
}; ARRAY_SIZE(pin_idxs));
struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx = 0;
/* for internal loopback recording select */
imux->items[imux->num_items].label = "Stereo Mixer";
imux->items[imux->num_items].index = 5;
imux->num_items++;
for (i = 0; i < AUTO_PIN_LAST; i++) {
if (!cfg->input_pins[i])
continue;
switch (cfg->input_pins[i]) {
case 0x2b: /* Mic */
idx = 1;
break;
case 0x2a: /* Line In */
idx = 2;
break;
case 0x29: /* Front Mic */
idx = 3;
break;
case 0x2c: /* CD */
idx = 0;
break;
}
err = via_new_analog_input(spec, labels[i], idx, 0x21);
if (err < 0)
return err;
imux->items[imux->num_items].label = labels[i];
imux->items[imux->num_items].index = idx;
imux->num_items++;
}
return 0;
} }
static int vt1718S_parse_auto_config(struct hda_codec *codec) static int vt1718S_parse_auto_config(struct hda_codec *codec)
@ -4938,7 +4748,7 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec)
err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0) if (err < 0)
return err; return err;
err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg); err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0) if (err < 0)
return err; return err;
@ -5371,49 +5181,12 @@ static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
} }
/* create playback/capture controls for input pins */ /* create playback/capture controls for input pins */
static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec, static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg) const struct auto_pin_cfg *cfg)
{ {
static char *labels[] = { static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
}; ARRAY_SIZE(pin_idxs));
struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx = 0;
/* for internal loopback recording select */
imux->items[imux->num_items].label = "Stereo Mixer";
imux->items[imux->num_items].index = 5;
imux->num_items++;
for (i = 0; i < AUTO_PIN_LAST; i++) {
if (!cfg->input_pins[i])
continue;
switch (cfg->input_pins[i]) {
case 0x1a: /* Mic */
idx = 2;
break;
case 0x1b: /* Line In */
idx = 3;
break;
case 0x1e: /* Front Mic */
idx = 4;
break;
case 0x1f: /* CD */
idx = 1;
break;
}
err = via_new_analog_input(spec, labels[i], idx, 0x16);
if (err < 0)
return err;
imux->items[imux->num_items].label = labels[i];
imux->items[imux->num_items].index = idx-1;
imux->num_items++;
}
return 0;
} }
static int vt1716S_parse_auto_config(struct hda_codec *codec) static int vt1716S_parse_auto_config(struct hda_codec *codec)
@ -5436,7 +5209,7 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec)
err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0) if (err < 0)
return err; return err;
err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg); err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0) if (err < 0)
return err; return err;
@ -5717,54 +5490,25 @@ static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
} }
/* create playback/capture controls for input pins */ /* create playback/capture controls for input pins */
static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec, static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg) const struct auto_pin_cfg *cfg)
{ {
static char *labels[] = { struct via_spec *spec = codec->spec;
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
};
struct hda_input_mux *imux = &spec->private_imux[0]; struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx = 0; static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff };
int err;
for (i = 0; i < AUTO_PIN_LAST; i++) {
if (!cfg->input_pins[i])
continue;
switch (cfg->input_pins[i]) {
case 0x2b: /* Mic */
idx = 0;
break;
case 0x2a: /* Line In */
idx = 1;
break;
case 0x29: /* Front Mic */
idx = 2;
break;
}
err = via_new_analog_input(spec, labels[i], idx, 0x21);
if (err < 0)
return err;
imux->items[imux->num_items].label = labels[i];
imux->items[imux->num_items].index = idx;
imux->num_items++;
}
err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
ARRAY_SIZE(pin_idxs));
if (err < 0)
return err;
/* build volume/mute control of loopback */ /* build volume/mute control of loopback */
err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21); err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21);
if (err < 0) if (err < 0)
return err; return err;
/* for internal loopback recording select */
imux->items[imux->num_items].label = "Stereo Mixer";
imux->items[imux->num_items].index = 3;
imux->num_items++;
/* for digital mic select */ /* for digital mic select */
imux->items[imux->num_items].label = "Digital Mic"; snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL);
imux->items[imux->num_items].index = 4;
imux->num_items++;
return 0; return 0;
} }
@ -5792,7 +5536,7 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec)
err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0) if (err < 0)
return err; return err;
err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg); err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0) if (err < 0)
return err; return err;
@ -6067,53 +5811,26 @@ static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
} }
/* create playback/capture controls for input pins */ /* create playback/capture controls for input pins */
static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec, static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg) const struct auto_pin_cfg *cfg)
{ {
static char *labels[] = { struct via_spec *spec = codec->spec;
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
};
struct hda_input_mux *imux = &spec->private_imux[0]; struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx = 0; static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff };
int err;
for (i = 0; i < AUTO_PIN_LAST; i++) { err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
if (!cfg->input_pins[i]) ARRAY_SIZE(pin_idxs));
continue;
switch (cfg->input_pins[i]) {
case 0x2b: /* Mic */
idx = 0;
break;
case 0x2a: /* Line In */
idx = 1;
break;
case 0x29: /* Front Mic */
idx = 2;
break;
}
err = via_new_analog_input(spec, labels[i], idx, 0x21);
if (err < 0)
return err;
imux->items[imux->num_items].label = labels[i];
imux->items[imux->num_items].index = idx;
imux->num_items++;
}
/* build volume/mute control of loopback */
err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21);
if (err < 0) if (err < 0)
return err; return err;
/* for internal loopback recording select */ /* build volume/mute control of loopback */
imux->items[imux->num_items].label = "Stereo Mixer"; err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
imux->items[imux->num_items].index = 5; if (err < 0)
imux->num_items++; return err;
/* for digital mic select */ /* for digital mic select */
imux->items[imux->num_items].label = "Digital Mic"; snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL);
imux->items[imux->num_items].index = 6;
imux->num_items++;
return 0; return 0;
} }
@ -6141,7 +5858,7 @@ static int vt1812_parse_auto_config(struct hda_codec *codec)
err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0) if (err < 0)
return err; return err;
err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg); err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0) if (err < 0)
return err; return err;