Merge branch 'topic/hda' into for-linus
This commit is contained in:
commit
506ecbca71
|
@ -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.
|
||||
|
||||
`position_fix=1` means to use LPIB method explicitly.
|
||||
`position_fix=2` means to use the position-buffer. 0 is the default
|
||||
value, the automatic check and fallback to LPIB as described in the
|
||||
above. If you get a problem of repeated sounds, this option might
|
||||
`position_fix=2` means to use the position-buffer.
|
||||
`position_fix=3` means to use a combination of both methods, needed
|
||||
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.
|
||||
|
||||
In addition to that, every controller is known to be broken regarding
|
||||
|
|
|
@ -38,9 +38,11 @@
|
|||
#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 TLV_DB_SCALE_MASK 0xffff
|
||||
#define TLV_DB_SCALE_MUTE 0x10000
|
||||
#define TLV_DB_SCALE_ITEM(min, step, mute) \
|
||||
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) \
|
||||
unsigned int name[] = { TLV_DB_SCALE_ITEM(min, step, mute) }
|
||||
|
||||
|
|
|
@ -119,47 +119,20 @@ config SND_HDA_CODEC_VIA
|
|||
snd-hda-codec-via.
|
||||
This module is automatically loaded at probing.
|
||||
|
||||
config SND_HDA_CODEC_ATIHDMI
|
||||
bool "Build ATI HDMI 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"
|
||||
config SND_HDA_CODEC_HDMI
|
||||
bool "Build HDMI/DisplayPort HD-audio codec support"
|
||||
select SND_DYNAMIC_MINORS
|
||||
default y
|
||||
help
|
||||
Say Y here to include INTEL HDMI HD-audio codec support in
|
||||
snd-hda-intel driver, such as Eaglelake integrated HDMI.
|
||||
Say Y here to include HDMI and DisplayPort HD-audio codec
|
||||
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
|
||||
support code is also built as another module,
|
||||
snd-hda-codec-intelhdmi.
|
||||
snd-hda-codec-hdmi.
|
||||
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
|
||||
bool "Build Cirrus Logic codec support"
|
||||
depends on SND_HDA_INTEL
|
||||
|
|
|
@ -3,7 +3,6 @@ snd-hda-intel-objs := hda_intel.o
|
|||
snd-hda-codec-y := hda_codec.o
|
||||
snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
|
||||
snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
|
||||
snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o
|
||||
snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.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-idt-objs := patch_sigmatel.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-ca0110-objs := patch_ca0110.o
|
||||
snd-hda-codec-conexant-objs := patch_conexant.o
|
||||
snd-hda-codec-via-objs := patch_via.o
|
||||
snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o
|
||||
snd-hda-codec-intelhdmi-objs := patch_intelhdmi.o
|
||||
snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o
|
||||
|
||||
# common driver
|
||||
obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
|
||||
|
@ -39,9 +36,6 @@ endif
|
|||
ifdef CONFIG_SND_HDA_CODEC_SI3054
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_CIRRUS
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o
|
||||
endif
|
||||
|
@ -54,11 +48,8 @@ endif
|
|||
ifdef CONFIG_SND_HDA_CODEC_VIA
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_NVHDMI
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-nvhdmi.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_INTELHDMI
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-intelhdmi.o
|
||||
ifdef CONFIG_SND_HDA_CODEC_HDMI
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-hdmi.o
|
||||
endif
|
||||
|
||||
# this must be the last entry after codec drivers;
|
||||
|
|
|
@ -1216,6 +1216,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
|
|||
struct hda_codec *c;
|
||||
struct hda_cvt_setup *p;
|
||||
unsigned int oldval, newval;
|
||||
int type;
|
||||
int i;
|
||||
|
||||
if (!nid)
|
||||
|
@ -1254,10 +1255,12 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
|
|||
p->dirty = 0;
|
||||
|
||||
/* 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) {
|
||||
for (i = 0; i < c->cvt_setups.used; 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;
|
||||
}
|
||||
}
|
||||
|
@ -1281,6 +1284,9 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
|
|||
if (!nid)
|
||||
return;
|
||||
|
||||
if (codec->no_sticky_stream)
|
||||
do_now = 1;
|
||||
|
||||
snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
|
||||
p = get_hda_cvt_setup(codec, nid);
|
||||
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);
|
||||
int dir = get_amp_direction(kcontrol);
|
||||
unsigned int ofs = get_amp_offset(kcontrol);
|
||||
bool min_mute = get_amp_min_mute(kcontrol);
|
||||
u32 caps, val1, val2;
|
||||
|
||||
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 += ofs;
|
||||
val1 = ((int)val1) * ((int)val2);
|
||||
if (min_mute)
|
||||
val2 |= TLV_DB_SCALE_MUTE;
|
||||
if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
|
||||
return -EFAULT;
|
||||
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,
|
||||
HDA_AMP_MUTE,
|
||||
*valp ? 0 : HDA_AMP_MUTE);
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
if (codec->patch_ops.check_power_status)
|
||||
codec->patch_ops.check_power_status(codec, nid);
|
||||
#endif
|
||||
hda_call_check_power_status(codec, nid);
|
||||
snd_hda_power_down(codec);
|
||||
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
|
||||
*
|
||||
|
@ -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
|
||||
* 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,
|
||||
* 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_speaker[ARRAY_SIZE(cfg->speaker_pins)];
|
||||
short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
|
||||
int i;
|
||||
|
||||
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;
|
||||
cfg->hp_outs++;
|
||||
break;
|
||||
case AC_JACK_MIC_IN: {
|
||||
int preferred, alt;
|
||||
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;
|
||||
case AC_JACK_MIC_IN:
|
||||
add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
|
||||
break;
|
||||
}
|
||||
case AC_JACK_LINE_IN:
|
||||
if (loc == AC_JACK_LOC_FRONT)
|
||||
cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid;
|
||||
else
|
||||
cfg->input_pins[AUTO_PIN_LINE] = nid;
|
||||
add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
|
||||
break;
|
||||
case AC_JACK_CD:
|
||||
cfg->input_pins[AUTO_PIN_CD] = nid;
|
||||
add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
|
||||
break;
|
||||
case AC_JACK_AUX:
|
||||
cfg->input_pins[AUTO_PIN_AUX] = nid;
|
||||
add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
|
||||
break;
|
||||
case AC_JACK_SPDIF_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,
|
||||
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 */
|
||||
|
@ -4549,21 +4570,6 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
|
|||
sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
|
||||
cfg->hp_outs);
|
||||
|
||||
/* if we have only one mic, make it AUTO_PIN_MIC */
|
||||
if (!cfg->input_pins[AUTO_PIN_MIC] &&
|
||||
cfg->input_pins[AUTO_PIN_FRONT_MIC]) {
|
||||
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
|
||||
* as a primary output
|
||||
|
@ -4602,6 +4608,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
|
|||
break;
|
||||
}
|
||||
|
||||
sort_autocfg_input_pins(cfg);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
snd_printd(" dig-out=0x%x/0x%x\n",
|
||||
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,"
|
||||
" cd=0x%x, aux=0x%x\n",
|
||||
cfg->input_pins[AUTO_PIN_MIC],
|
||||
cfg->input_pins[AUTO_PIN_FRONT_MIC],
|
||||
cfg->input_pins[AUTO_PIN_LINE],
|
||||
cfg->input_pins[AUTO_PIN_FRONT_LINE],
|
||||
cfg->input_pins[AUTO_PIN_CD],
|
||||
cfg->input_pins[AUTO_PIN_AUX]);
|
||||
snd_printd(" inputs:");
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
snd_printdd(" %s=0x%x",
|
||||
hda_get_autocfg_input_label(codec, cfg, i),
|
||||
cfg->inputs[i].pin);
|
||||
}
|
||||
snd_printd("\n");
|
||||
if (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);
|
||||
|
||||
/* labels for input pins */
|
||||
const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = {
|
||||
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
|
||||
};
|
||||
EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
|
||||
int snd_hda_get_input_pin_attr(unsigned int def_conf)
|
||||
{
|
||||
unsigned int loc = get_defcfg_location(def_conf);
|
||||
unsigned int conn = get_defcfg_connect(def_conf);
|
||||
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
|
||||
|
|
|
@ -850,6 +850,7 @@ struct hda_codec {
|
|||
unsigned int pin_amp_workaround:1; /* pin out-amp takes index
|
||||
* (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 no_trigger_sense:1; /* don't trigger at pin-sensing */
|
||||
#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);
|
||||
#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
|
||||
*/
|
||||
|
|
|
@ -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,
|
||||
AC_DIPSIZE_ELD_BUF);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hdmi_get_eld_size);
|
||||
|
||||
int snd_hdmi_get_eld(struct hdmi_eld *eld,
|
||||
struct hda_codec *codec, hda_nid_t nid)
|
||||
|
@ -368,7 +367,6 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld,
|
|||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hdmi_get_eld);
|
||||
|
||||
static void hdmi_show_short_audio_desc(struct cea_sad *a)
|
||||
{
|
||||
|
@ -407,7 +405,6 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
|
|||
}
|
||||
buf[j] = '\0'; /* necessary when j == 0 */
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_print_channel_allocation);
|
||||
|
||||
void snd_hdmi_show_eld(struct hdmi_eld *e)
|
||||
{
|
||||
|
@ -426,7 +423,6 @@ void snd_hdmi_show_eld(struct hdmi_eld *e)
|
|||
for (i = 0; i < e->sad_count; i++)
|
||||
hdmi_show_short_audio_desc(e->sad + i);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hdmi_show_eld);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
|
@ -585,7 +581,6 @@ int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_eld_proc_new);
|
||||
|
||||
void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
|
||||
{
|
||||
|
@ -594,7 +589,6 @@ void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
|
|||
eld->proc_entry = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_eld_proc_free);
|
||||
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
|
@ -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->maxbps = min(pcm->maxbps, codec_pars->maxbps);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(hdmi_eld_update_pcm_info);
|
||||
|
|
|
@ -61,7 +61,6 @@ struct hda_gspec {
|
|||
struct hda_gnode *cap_vol_node; /* Node for capture volume */
|
||||
unsigned int cur_cap_src; /* current capture source */
|
||||
struct hda_input_mux input_mux;
|
||||
char cap_labels[HDA_MAX_NUM_INPUTS][16];
|
||||
|
||||
unsigned int def_amp_in_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.
|
||||
*/
|
||||
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;
|
||||
unsigned int pinctl;
|
||||
char *label;
|
||||
const char *type;
|
||||
|
||||
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]);
|
||||
if (! child)
|
||||
continue;
|
||||
err = parse_adc_sub_nodes(codec, spec, child);
|
||||
err = parse_adc_sub_nodes(codec, spec, child, idx);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (err > 0) {
|
||||
|
@ -564,9 +562,7 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
|
|||
return 0;
|
||||
type = "Input";
|
||||
}
|
||||
label = spec->cap_labels[spec->input_mux.num_items];
|
||||
strcpy(label, type);
|
||||
spec->input_mux.items[spec->input_mux.num_items].label = label;
|
||||
snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL);
|
||||
|
||||
/* unmute the PIN external input */
|
||||
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 */
|
||||
}
|
||||
|
||||
/* 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
|
||||
*/
|
||||
|
@ -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++) {
|
||||
node = hda_get_node(spec, adc_node->conn_list[i]);
|
||||
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)
|
||||
return err;
|
||||
else if (err > 0)
|
||||
add_cap_src(spec, i);
|
||||
}
|
||||
}
|
||||
/* ... then check the rests, more complicated connections */
|
||||
for (i = 0; i < adc_node->nconns; i++) {
|
||||
node = hda_get_node(spec, adc_node->conn_list[i]);
|
||||
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)
|
||||
return err;
|
||||
else if (err > 0)
|
||||
add_cap_src(spec, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,8 +78,8 @@ MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
|
|||
module_param_array(model, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(model, "Use the given board model.");
|
||||
module_param_array(position_fix, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(position_fix, "Fix DMA pointer "
|
||||
"(0 = auto, 1 = none, 2 = POSBUF).");
|
||||
MODULE_PARM_DESC(position_fix, "DMA pointer read method."
|
||||
"(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO).");
|
||||
module_param_array(bdl_pos_adj, int, NULL, 0644);
|
||||
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
|
||||
module_param_array(probe_mask, int, NULL, 0444);
|
||||
|
@ -305,6 +305,7 @@ enum {
|
|||
POS_FIX_AUTO,
|
||||
POS_FIX_LPIB,
|
||||
POS_FIX_POSBUF,
|
||||
POS_FIX_VIACOMBO,
|
||||
};
|
||||
|
||||
/* Defines for ATI HD Audio support in SB450 south bridge */
|
||||
|
@ -433,7 +434,6 @@ struct azx {
|
|||
unsigned int polling_mode :1;
|
||||
unsigned int msi :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 */
|
||||
|
||||
/* for debugging */
|
||||
|
@ -458,6 +458,7 @@ enum {
|
|||
AZX_DRIVER_ULI,
|
||||
AZX_DRIVER_NVIDIA,
|
||||
AZX_DRIVER_TERA,
|
||||
AZX_DRIVER_CTX,
|
||||
AZX_DRIVER_GENERIC,
|
||||
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_NVIDIA] = "HDA NVidia",
|
||||
[AZX_DRIVER_TERA] = "HDA Teradici",
|
||||
[AZX_DRIVER_CTX] = "HDA Creative",
|
||||
[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 */
|
||||
azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST);
|
||||
/* 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 */
|
||||
azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
|
@ -1136,8 +1141,11 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
|
|||
/* clear rirb int */
|
||||
status = azx_readb(chip, RIRBSTS);
|
||||
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_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));
|
||||
|
||||
/* enable the position buffer */
|
||||
if (chip->position_fix[0] == POS_FIX_POSBUF ||
|
||||
chip->position_fix[0] == POS_FIX_AUTO ||
|
||||
chip->position_fix[1] == POS_FIX_POSBUF ||
|
||||
chip->position_fix[1] == POS_FIX_AUTO ||
|
||||
chip->via_dmapos_patch) {
|
||||
if (chip->position_fix[0] != POS_FIX_LPIB ||
|
||||
chip->position_fix[1] != POS_FIX_LPIB) {
|
||||
if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
|
||||
azx_writel(chip, DPLBASE,
|
||||
(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 hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
|
||||
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;
|
||||
|
||||
azx_stream_reset(chip, azx_dev);
|
||||
|
@ -1689,7 +1694,12 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
|
|||
else
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1852,20 +1862,21 @@ static unsigned int azx_get_position(struct azx *chip,
|
|||
struct azx_dev *azx_dev)
|
||||
{
|
||||
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);
|
||||
else {
|
||||
int stream = azx_dev->substream->stream;
|
||||
if (chip->position_fix[stream] == POS_FIX_POSBUF ||
|
||||
chip->position_fix[stream] == POS_FIX_AUTO) {
|
||||
/* use the position buffer */
|
||||
pos = le32_to_cpu(*azx_dev->posbuf);
|
||||
} else {
|
||||
/* read LPIB */
|
||||
pos = azx_sd_readl(azx_dev, SD_LPIB);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* use the position buffer */
|
||||
pos = le32_to_cpu(*azx_dev->posbuf);
|
||||
}
|
||||
|
||||
if (pos >= azx_dev->bufsize)
|
||||
pos = 0;
|
||||
return pos;
|
||||
|
@ -2313,19 +2324,10 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
|
|||
switch (fix) {
|
||||
case POS_FIX_LPIB:
|
||||
case POS_FIX_POSBUF:
|
||||
case POS_FIX_VIACOMBO:
|
||||
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);
|
||||
if (q) {
|
||||
printk(KERN_INFO
|
||||
|
@ -2334,6 +2336,15 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
|
|||
q->value, q->subvendor, q->subdevice);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -2735,25 +2746,17 @@ static void __devexit azx_remove(struct pci_dev *pci)
|
|||
|
||||
/* PCI 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 */
|
||||
{ PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH },
|
||||
/* PBG */
|
||||
{ PCI_DEVICE(0x8086, 0x1d20), .driver_data = AZX_DRIVER_PCH },
|
||||
/* 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 */
|
||||
{ PCI_DEVICE(0x1002, 0x437b), .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),
|
||||
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
|
||||
.class_mask = 0xffffff,
|
||||
.driver_data = AZX_DRIVER_GENERIC },
|
||||
.driver_data = AZX_DRIVER_CTX },
|
||||
#else
|
||||
/* 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
|
||||
/* Vortex86MX */
|
||||
{ PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
|
||||
/* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
|
||||
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
|
||||
|
|
|
@ -38,10 +38,11 @@
|
|||
*/
|
||||
#define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs) \
|
||||
((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) \
|
||||
HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0)
|
||||
/* 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, \
|
||||
.subdevice = HDA_SUBDEV_AMP_FLAG, \
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
|
||||
|
@ -51,16 +52,20 @@
|
|||
.get = snd_hda_mixer_amp_volume_get, \
|
||||
.put = snd_hda_mixer_amp_volume_put, \
|
||||
.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 */
|
||||
#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 */
|
||||
#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 */
|
||||
#define HDA_CODEC_VOLUME(xname, nid, 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) */
|
||||
#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
|
||||
{ .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
|
||||
struct hda_input_mux_item {
|
||||
const char *label;
|
||||
char label[32];
|
||||
unsigned int index;
|
||||
};
|
||||
struct hda_input_mux {
|
||||
|
@ -366,9 +371,7 @@ struct hda_bus_unsolicited {
|
|||
|
||||
enum {
|
||||
AUTO_PIN_MIC,
|
||||
AUTO_PIN_FRONT_MIC,
|
||||
AUTO_PIN_LINE,
|
||||
AUTO_PIN_FRONT_LINE,
|
||||
AUTO_PIN_LINE_IN,
|
||||
AUTO_PIN_CD,
|
||||
AUTO_PIN_AUX,
|
||||
AUTO_PIN_LAST
|
||||
|
@ -380,9 +383,33 @@ enum {
|
|||
AUTO_PIN_HP_OUT
|
||||
};
|
||||
|
||||
extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST];
|
||||
|
||||
#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 {
|
||||
int line_outs;
|
||||
|
@ -393,7 +420,8 @@ struct auto_pin_cfg {
|
|||
int hp_outs;
|
||||
int line_out_type; /* AUTO_PIN_XXX_OUT */
|
||||
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;
|
||||
hda_nid_t dig_out_pins[2];
|
||||
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_index(kc) (((kc)->private_value >> 19) & 0xf)
|
||||
#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
|
||||
|
|
|
@ -1276,6 +1276,7 @@ static int patch_ad1986a(struct hda_codec *codec)
|
|||
spec->multiout.no_share_stream = 1;
|
||||
|
||||
codec->no_trigger_sense = 1;
|
||||
codec->no_sticky_stream = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1463,6 +1464,7 @@ static int patch_ad1983(struct hda_codec *codec)
|
|||
codec->patch_ops = ad198x_patch_ops;
|
||||
|
||||
codec->no_trigger_sense = 1;
|
||||
codec->no_sticky_stream = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1917,6 +1919,7 @@ static int patch_ad1981(struct hda_codec *codec)
|
|||
}
|
||||
|
||||
codec->no_trigger_sense = 1;
|
||||
codec->no_sticky_stream = 1;
|
||||
|
||||
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 */
|
||||
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];
|
||||
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 */
|
||||
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)
|
||||
{
|
||||
struct ad198x_spec *spec = codec->spec;
|
||||
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++) {
|
||||
err = new_analog_input(spec, cfg->input_pins[i],
|
||||
auto_pin_cfg_labels[i],
|
||||
i <= AUTO_PIN_FRONT_MIC);
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
const char *label;
|
||||
type = cfg->inputs[i].type;
|
||||
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)
|
||||
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";
|
||||
imux->items[imux->num_items].index = 9;
|
||||
imux->num_items++;
|
||||
snd_hda_add_imux_item(imux, "Mix", 9, NULL);
|
||||
|
||||
if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
|
||||
"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)
|
||||
{
|
||||
struct ad198x_spec *spec = codec->spec;
|
||||
const struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
int i, idx;
|
||||
|
||||
for (i = 0; i < AUTO_PIN_LAST; i++) {
|
||||
hda_nid_t nid = spec->autocfg.input_pins[i];
|
||||
if (! nid)
|
||||
continue;
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
hda_nid_t nid = cfg->inputs[i].pin;
|
||||
switch (nid) {
|
||||
case 0x15: /* port-C */
|
||||
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;
|
||||
}
|
||||
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)
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
|
||||
AMP_OUT_MUTE);
|
||||
|
@ -3040,7 +3044,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
|
|||
"Speaker")) < 0 ||
|
||||
(err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[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;
|
||||
|
||||
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;
|
||||
|
||||
codec->no_trigger_sense = 1;
|
||||
codec->no_sticky_stream = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -3449,6 +3454,7 @@ static int patch_ad1884(struct hda_codec *codec)
|
|||
codec->patch_ops = ad198x_patch_ops;
|
||||
|
||||
codec->no_trigger_sense = 1;
|
||||
codec->no_sticky_stream = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -4422,6 +4428,7 @@ static int patch_ad1884a(struct hda_codec *codec)
|
|||
}
|
||||
|
||||
codec->no_trigger_sense = 1;
|
||||
codec->no_sticky_stream = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -4761,6 +4768,7 @@ static int patch_ad1882(struct hda_codec *codec)
|
|||
}
|
||||
|
||||
codec->no_trigger_sense = 1;
|
||||
codec->no_sticky_stream = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -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)
|
|
@ -468,13 +468,13 @@ static void parse_input(struct hda_codec *codec)
|
|||
spec->dig_in = nid;
|
||||
continue;
|
||||
}
|
||||
for (j = 0; j < AUTO_PIN_LAST; j++)
|
||||
if (cfg->input_pins[j] == pin)
|
||||
for (j = 0; j < cfg->num_inputs; j++)
|
||||
if (cfg->inputs[j].pin == pin)
|
||||
break;
|
||||
if (j >= AUTO_PIN_LAST)
|
||||
if (j >= cfg->num_inputs)
|
||||
continue;
|
||||
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;
|
||||
n++;
|
||||
}
|
||||
|
@ -489,7 +489,7 @@ static void parse_digital(struct hda_codec *codec)
|
|||
if (cfg->dig_outs &&
|
||||
snd_hda_get_connections(codec, cfg->dig_out_pins[0],
|
||||
&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)
|
||||
|
|
|
@ -65,6 +65,7 @@ struct cs_spec {
|
|||
|
||||
/* available models */
|
||||
enum {
|
||||
CS420X_MBP53,
|
||||
CS420X_MBP55,
|
||||
CS420X_IMAC27,
|
||||
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 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);
|
||||
if (!(val & AC_PINCAP_PRES_DETECT))
|
||||
return 0;
|
||||
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,
|
||||
|
@ -424,10 +425,8 @@ static int parse_input(struct hda_codec *codec)
|
|||
struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < AUTO_PIN_LAST; i++) {
|
||||
hda_nid_t pin = cfg->input_pins[i];
|
||||
if (!pin)
|
||||
continue;
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
hda_nid_t pin = cfg->inputs[i].pin;
|
||||
spec->input_idx[spec->num_inputs] = i;
|
||||
spec->capsrc_idx[i] = spec->num_inputs++;
|
||||
spec->cur_input = i;
|
||||
|
@ -438,16 +437,17 @@ static int parse_input(struct hda_codec *codec)
|
|||
|
||||
/* check whether the automatic mic switch is available */
|
||||
if (spec->num_inputs == 2 &&
|
||||
spec->adc_nid[AUTO_PIN_MIC] && spec->adc_nid[AUTO_PIN_FRONT_MIC]) {
|
||||
if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_FRONT_MIC])) {
|
||||
if (!is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
|
||||
cfg->inputs[0].type == AUTO_PIN_MIC &&
|
||||
cfg->inputs[1].type == 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->automic_idx = AUTO_PIN_FRONT_MIC;
|
||||
spec->automic_idx = 0;
|
||||
}
|
||||
} 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->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 cs_spec *spec = codec->spec;
|
||||
struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
unsigned int idx;
|
||||
|
||||
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)
|
||||
uinfo->value.enumerated.item = spec->num_inputs - 1;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -740,6 +742,27 @@ static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec,
|
|||
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)
|
||||
{
|
||||
struct cs_spec *spec = codec->spec;
|
||||
|
@ -779,6 +802,12 @@ static int build_input(struct hda_codec *codec)
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -838,7 +867,8 @@ static void cs_automute(struct hda_codec *codec)
|
|||
AC_VERB_SET_PIN_WIDGET_CONTROL,
|
||||
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) {
|
||||
unsigned int gpio = hp_present ? 0x02 : 0x08;
|
||||
snd_hda_codec_write(codec, 0x01, 0,
|
||||
|
@ -853,15 +883,12 @@ static void cs_automic(struct hda_codec *codec)
|
|||
hda_nid_t nid;
|
||||
unsigned int present;
|
||||
|
||||
nid = cfg->input_pins[spec->automic_idx];
|
||||
nid = cfg->inputs[spec->automic_idx].pin;
|
||||
present = snd_hda_jack_detect(codec, nid);
|
||||
if (present)
|
||||
change_cur_input(codec, spec->automic_idx, 0);
|
||||
else {
|
||||
unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ?
|
||||
AUTO_PIN_FRONT_MIC : AUTO_PIN_MIC;
|
||||
change_cur_input(codec, imic, 0);
|
||||
}
|
||||
else
|
||||
change_cur_input(codec, !spec->automic_idx, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -918,14 +945,14 @@ static void init_input(struct hda_codec *codec)
|
|||
unsigned int coef;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < AUTO_PIN_LAST; i++) {
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
unsigned int ctl;
|
||||
hda_nid_t pin = cfg->input_pins[i];
|
||||
if (!pin || !spec->adc_nid[i])
|
||||
hda_nid_t pin = cfg->inputs[i].pin;
|
||||
if (!spec->adc_nid[i])
|
||||
continue;
|
||||
/* set appropriate pin control and mute first */
|
||||
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);
|
||||
caps >>= AC_PINCAP_VREF_SHIFT;
|
||||
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] = {
|
||||
[CS420X_MBP53] = "mbp53",
|
||||
[CS420X_MBP55] = "mbp55",
|
||||
[CS420X_IMAC27] = "imac27",
|
||||
[CS420X_AUTO] = "auto",
|
||||
|
@ -1137,7 +1165,9 @@ static const char *cs420x_models[CS420X_MODELS] = {
|
|||
|
||||
|
||||
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, 0xcb89, "MacBookPro 7,1", CS420X_MBP55),
|
||||
SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
@ -1147,6 +1177,20 @@ struct cs_pincfg {
|
|||
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[] = {
|
||||
{ 0x09, 0x012b4030 },
|
||||
{ 0x0a, 0x90100121 },
|
||||
|
@ -1176,6 +1220,7 @@ static struct cs_pincfg imac27_pincfgs[] = {
|
|||
};
|
||||
|
||||
static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
|
||||
[CS420X_MBP53] = mbp53_pincfgs,
|
||||
[CS420X_MBP55] = mbp55_pincfgs,
|
||||
[CS420X_IMAC27] = imac27_pincfgs,
|
||||
};
|
||||
|
@ -1208,6 +1253,7 @@ static int patch_cs420x(struct hda_codec *codec)
|
|||
|
||||
switch (spec->board_config) {
|
||||
case CS420X_IMAC27:
|
||||
case CS420X_MBP53:
|
||||
case CS420X_MBP55:
|
||||
/* GPIO1 = headphones */
|
||||
/* GPIO3 = speakers */
|
||||
|
|
|
@ -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 snd_kcontrol_new *mixers[5];
|
||||
|
@ -77,6 +83,7 @@ struct conexant_spec {
|
|||
unsigned int cur_eapd;
|
||||
unsigned int hp_present;
|
||||
unsigned int auto_mic;
|
||||
int auto_mic_ext; /* autocfg.inputs[] index for ext mic */
|
||||
unsigned int need_dac_fix;
|
||||
|
||||
/* capture */
|
||||
|
@ -110,9 +117,12 @@ struct conexant_spec {
|
|||
struct auto_pin_cfg autocfg;
|
||||
struct hda_input_mux private_imux;
|
||||
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 auto_mute:1; /* used in auto-parser */
|
||||
unsigned int dell_automute:1;
|
||||
unsigned int dell_vostro:1;
|
||||
unsigned int ideapad:1;
|
||||
unsigned int thinkpad:1;
|
||||
|
@ -3065,7 +3075,7 @@ enum {
|
|||
CXT5066_LAPTOP, /* Laptops w/ EAPD support */
|
||||
CXT5066_DELL_LAPTOP, /* Dell Laptop */
|
||||
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_THINKPAD, /* Lenovo ThinkPad T410s, others? */
|
||||
CXT5066_HP_LAPTOP, /* HP Laptop */
|
||||
|
@ -3076,25 +3086,26 @@ static const char *cxt5066_models[CXT5066_MODELS] = {
|
|||
[CXT5066_LAPTOP] = "laptop",
|
||||
[CXT5066_DELL_LAPTOP] = "dell-laptop",
|
||||
[CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
|
||||
[CXT5066_DELL_VOSTO] = "dell-vostro",
|
||||
[CXT5066_DELL_VOSTRO] = "dell-vostro",
|
||||
[CXT5066_IDEAPAD] = "ideapad",
|
||||
[CXT5066_THINKPAD] = "thinkpad",
|
||||
[CXT5066_HP_LAPTOP] = "hp-laptop",
|
||||
};
|
||||
|
||||
static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
|
||||
SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
|
||||
CXT5066_LAPTOP),
|
||||
SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD),
|
||||
SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO),
|
||||
SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
|
||||
CXT5066_DELL_LAPTOP),
|
||||
SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
|
||||
SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTO),
|
||||
SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO),
|
||||
SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO),
|
||||
SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
|
||||
SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP),
|
||||
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, 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, 0x21b2, "Thinkpad X100e", 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_cleanup = cxt5066_olpc_capture_cleanup;
|
||||
break;
|
||||
case CXT5066_DELL_VOSTO:
|
||||
case CXT5066_DELL_VOSTRO:
|
||||
codec->patch_ops.init = cxt5066_init;
|
||||
codec->patch_ops.unsol_event = cxt5066_vostro_event;
|
||||
spec->init_verbs[0] = cxt5066_init_verbs_vostro;
|
||||
|
@ -3253,6 +3264,604 @@ static int patch_cxt5066(struct hda_codec *codec)
|
|||
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 },
|
||||
{ .id = 0x14f15069, .name = "CX20585",
|
||||
.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 */
|
||||
};
|
||||
|
||||
|
@ -3281,6 +3906,14 @@ MODULE_ALIAS("snd-hda-codec-id:14f15066");
|
|||
MODULE_ALIAS("snd-hda-codec-id:14f15067");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15068");
|
||||
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_DESCRIPTION("Conexant HD-audio codec");
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
* patch_hdmi.c - routines for HDMI/DisplayPort codecs
|
||||
*
|
||||
* 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:
|
||||
* Wu Fengguang <wfg@linux.intel.com>
|
||||
|
@ -25,6 +28,22 @@
|
|||
* 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 {
|
||||
int num_cvts;
|
||||
|
@ -49,10 +68,10 @@ struct hdmi_spec {
|
|||
struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS];
|
||||
|
||||
/*
|
||||
* nvhdmi specific
|
||||
* ati/nvhdmi specific
|
||||
*/
|
||||
struct hda_multi_out multiout;
|
||||
unsigned int codec_type;
|
||||
struct hda_pcm_stream *pcm_playback;
|
||||
|
||||
/* misc flags */
|
||||
/* PD bit indicates only the update, not the current state */
|
||||
|
@ -65,13 +84,25 @@ struct hdmi_audio_infoframe {
|
|||
u8 ver; /* 0x01 */
|
||||
u8 len; /* 0x0a */
|
||||
|
||||
u8 checksum; /* PB0 */
|
||||
u8 checksum;
|
||||
|
||||
u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */
|
||||
u8 SS01_SF24;
|
||||
u8 CXT04;
|
||||
u8 CA;
|
||||
u8 LFEPBL01_LSV36_DM_INH7;
|
||||
u8 reserved[5]; /* PB6 - PB10 */
|
||||
};
|
||||
|
||||
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 */
|
||||
[0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
|
||||
/* surround41 */
|
||||
[0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 },
|
||||
[0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
|
||||
/* surround50 */
|
||||
[0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
|
||||
/* surround51 */
|
||||
|
@ -175,7 +206,7 @@ static int hdmi_channel_mapping[0x32][8] = {
|
|||
* This is an ordered list!
|
||||
*
|
||||
* 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[] = {
|
||||
/* 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.
|
||||
*/
|
||||
static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
|
||||
int channels)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_eld *eld;
|
||||
int i;
|
||||
int ca = 0;
|
||||
int spk_mask = 0;
|
||||
int channels = 1 + (ai->CC02_CT47 & 0x7);
|
||||
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 &&
|
||||
(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;
|
||||
}
|
||||
}
|
||||
|
||||
snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
|
||||
snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n",
|
||||
ai->CA, channels, buf);
|
||||
ca, channels, buf);
|
||||
|
||||
return ai->CA;
|
||||
return ca;
|
||||
}
|
||||
|
||||
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,
|
||||
hda_nid_t pin_nid,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
int ca)
|
||||
{
|
||||
int i;
|
||||
int ca = ai->CA;
|
||||
int err;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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;
|
||||
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];
|
||||
|
||||
ai->checksum = -sum;
|
||||
hdmi_ai->checksum = -sum;
|
||||
}
|
||||
|
||||
static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
u8 *dip, int size)
|
||||
{
|
||||
u8 *bytes = (u8 *)ai;
|
||||
int i;
|
||||
|
||||
hdmi_debug_dip_size(codec, pin_nid);
|
||||
hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
|
||||
|
||||
hdmi_checksum_audio_infoframe(ai);
|
||||
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
for (i = 0; i < sizeof(*ai); i++)
|
||||
hdmi_write_dip_byte(codec, pin_nid, bytes[i]);
|
||||
for (i = 0; i < size; i++)
|
||||
hdmi_write_dip_byte(codec, pin_nid, dip[i]);
|
||||
}
|
||||
|
||||
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;
|
||||
int i;
|
||||
|
||||
|
@ -571,10 +597,10 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
|
|||
return false;
|
||||
|
||||
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,
|
||||
AC_VERB_GET_HDMI_DIP_DATA, 0);
|
||||
if (val != bytes[i])
|
||||
if (val != dip[i])
|
||||
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;
|
||||
hda_nid_t pin_nid;
|
||||
int channels = substream->runtime->channels;
|
||||
int ca;
|
||||
int i;
|
||||
struct hdmi_audio_infoframe ai = {
|
||||
.type = 0x84,
|
||||
.ver = 0x01,
|
||||
.len = 0x0a,
|
||||
.CC02_CT47 = substream->runtime->channels - 1,
|
||||
};
|
||||
u8 ai[max(sizeof(struct hdmi_audio_infoframe),
|
||||
sizeof(struct dp_audio_infoframe))];
|
||||
|
||||
hdmi_setup_channel_allocation(codec, nid, &ai);
|
||||
ca = hdmi_channel_allocation(codec, nid, channels);
|
||||
|
||||
for (i = 0; i < spec->num_pins; i++) {
|
||||
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;
|
||||
|
||||
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: "
|
||||
"cvt=%d pin=%d channels=%d\n",
|
||||
nid, pin_nid,
|
||||
substream->runtime->channels);
|
||||
hdmi_setup_channel_mapping(codec, pin_nid, &ai);
|
||||
channels);
|
||||
hdmi_setup_channel_mapping(codec, pin_nid, ca);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -791,7 +846,6 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
|
|||
/*
|
||||
* HDA/HDMI auto parsing
|
||||
*/
|
||||
|
||||
static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
@ -922,3 +976,664 @@ static int hdmi_parse_codec(struct hda_codec *codec)
|
|||
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)
|
||||
|
|
|
@ -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)
|
|
@ -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
|
@ -32,6 +32,7 @@
|
|||
#include <sound/core.h>
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
#include "hda_beep.h"
|
||||
|
@ -263,6 +264,7 @@ struct sigmatel_spec {
|
|||
|
||||
struct sigmatel_mic_route ext_mic;
|
||||
struct sigmatel_mic_route int_mic;
|
||||
struct sigmatel_mic_route dock_mic;
|
||||
|
||||
const char **spdif_labels;
|
||||
|
||||
|
@ -382,6 +384,11 @@ static unsigned int stac92hd83xxx_pwr_mapping[4] = {
|
|||
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
|
||||
static unsigned long stac92hd83xxx_capvols[] = {
|
||||
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[] = {
|
||||
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_VOLUME("Capture Volume", 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[] = {
|
||||
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),
|
||||
{ } /* end */
|
||||
};
|
||||
|
@ -1105,9 +1112,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
|
|||
struct hda_input_mux *smux = &spec->private_smux;
|
||||
/* check for mute support on SPDIF out */
|
||||
if (wcaps & AC_WCAP_OUT_AMP) {
|
||||
smux->items[smux->num_items].label = "Off";
|
||||
smux->items[smux->num_items].index = 0;
|
||||
smux->num_items++;
|
||||
snd_hda_add_imux_item(smux, "Off", 0, NULL);
|
||||
spec->spdif_mute = 1;
|
||||
}
|
||||
stac_smux_mixer.count = spec->num_smuxes;
|
||||
|
@ -1140,6 +1145,8 @@ static int stac92xx_build_controls(struct hda_codec *codec)
|
|||
HDA_OUTPUT, vmaster_tlv);
|
||||
/* correct 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",
|
||||
vmaster_tlv, slave_vols);
|
||||
if (err < 0)
|
||||
|
@ -1180,14 +1187,11 @@ static int stac92xx_build_controls(struct hda_codec *codec)
|
|||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
for (i = 0; i < AUTO_PIN_LAST; i++) {
|
||||
nid = cfg->input_pins[i];
|
||||
if (nid) {
|
||||
err = stac92xx_add_jack(codec, nid,
|
||||
SND_JACK_MICROPHONE);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
nid = cfg->inputs[i].pin;
|
||||
err = stac92xx_add_jack(codec, nid, SND_JACK_MICROPHONE);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -2779,7 +2783,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
|
|||
struct sigmatel_spec *spec = codec->spec;
|
||||
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
|
||||
&& nid == spec->line_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) {
|
||||
strcpy(name, auto_pin_cfg_labels[idx]);
|
||||
strcpy(name, hda_get_input_pin_label(codec, nid, 1));
|
||||
return stac92xx_add_control(codec->spec, control,
|
||||
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;
|
||||
hda_nid_t nid;
|
||||
unsigned int pincap;
|
||||
int i;
|
||||
|
||||
if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
|
||||
return 0;
|
||||
nid = cfg->input_pins[AUTO_PIN_LINE];
|
||||
pincap = snd_hda_query_pin_caps(codec, nid);
|
||||
if (pincap & AC_PINCAP_OUT)
|
||||
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) {
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
if (cfg->inputs[i].type == AUTO_PIN_LINE_IN) {
|
||||
nid = cfg->inputs[i].pin;
|
||||
pincap = snd_hda_query_pin_caps(codec, nid);
|
||||
if (pincap & AC_PINCAP_OUT)
|
||||
return nid;
|
||||
}
|
||||
if (mic_pin == AUTO_PIN_MIC)
|
||||
mic_pin = AUTO_PIN_FRONT_MIC;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -3004,17 +3016,14 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec)
|
|||
}
|
||||
}
|
||||
/* add mic as output */
|
||||
nid = check_mic_out_switch(codec);
|
||||
if (nid) {
|
||||
dac = get_unassigned_dac(codec, nid);
|
||||
if (dac) {
|
||||
snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
|
||||
nid, cfg->line_outs);
|
||||
cfg->line_out_pins[cfg->line_outs] = nid;
|
||||
cfg->line_outs++;
|
||||
spec->mic_switch = nid;
|
||||
add_spec_dacs(spec, dac);
|
||||
}
|
||||
nid = check_mic_out_switch(codec, &dac);
|
||||
if (nid && dac) {
|
||||
snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
|
||||
nid, cfg->line_outs);
|
||||
cfg->line_out_pins[cfg->line_outs] = nid;
|
||||
cfg->line_outs++;
|
||||
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",
|
||||
|
@ -3204,13 +3213,13 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
|
|||
return err;
|
||||
}
|
||||
|
||||
for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) {
|
||||
nid = cfg->input_pins[idx];
|
||||
if (nid) {
|
||||
err = stac92xx_add_jack_mode_control(codec, nid, idx);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
for (idx = 0; idx < cfg->num_inputs; idx++) {
|
||||
if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
|
||||
break;
|
||||
nid = cfg->inputs[idx].pin;
|
||||
err = stac92xx_add_jack_mode_control(codec, nid, idx);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
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))
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < num_cons; i++) {
|
||||
mono_mux->items[mono_mux->num_items].label =
|
||||
stac92xx_mono_labels[i];
|
||||
mono_mux->items[mono_mux->num_items].index = i;
|
||||
mono_mux->num_items++;
|
||||
}
|
||||
for (i = 0; i < num_cons; i++)
|
||||
snd_hda_add_imux_item(mono_mux, stac92xx_mono_labels[i], i,
|
||||
NULL);
|
||||
|
||||
return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
|
||||
"Mono Mux", spec->mono_nid);
|
||||
|
@ -3386,11 +3392,8 @@ static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec)
|
|||
if (!labels)
|
||||
labels = stac92xx_spdif_labels;
|
||||
|
||||
for (i = 0; i < num_cons; i++) {
|
||||
spdif_mux->items[spdif_mux->num_items].label = labels[i];
|
||||
spdif_mux->items[spdif_mux->num_items].index = i;
|
||||
spdif_mux->num_items++;
|
||||
}
|
||||
for (i = 0; i < num_cons; i++)
|
||||
snd_hda_add_imux_item(spdif_mux, labels[i], i, NULL);
|
||||
|
||||
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) */
|
||||
/* return 1 if the volume control is created */
|
||||
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;
|
||||
char name[32];
|
||||
|
@ -3434,8 +3437,8 @@ static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
|
|||
if (!nums)
|
||||
return 0;
|
||||
snprintf(name, sizeof(name), "%s Capture Volume", label);
|
||||
err = stac92xx_add_control(codec->spec, STAC_CTL_WIDGET_VOL, name,
|
||||
HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
|
||||
err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name,
|
||||
HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
|
||||
if (err < 0)
|
||||
return err;
|
||||
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 hda_input_mux *imux = &spec->private_imux;
|
||||
struct hda_input_mux *dimux = &spec->private_dimux;
|
||||
int err, i, active_mics;
|
||||
int err, i;
|
||||
unsigned int def_conf;
|
||||
|
||||
dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
|
||||
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++;
|
||||
}
|
||||
snd_hda_add_imux_item(dimux, stac92xx_dmic_labels[0], 0, NULL);
|
||||
|
||||
for (i = 0; i < spec->num_dmics; i++) {
|
||||
hda_nid_t nid;
|
||||
int index;
|
||||
int index, type_idx;
|
||||
const char *label;
|
||||
|
||||
nid = spec->dmic_nids[i];
|
||||
|
@ -3482,28 +3472,23 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
|
|||
if (index < 0)
|
||||
continue;
|
||||
|
||||
if (active_mics == 1)
|
||||
label = "Digital Mic";
|
||||
else
|
||||
label = stac92xx_dmic_labels[dimux->num_items];
|
||||
label = hda_get_input_pin_label(codec, nid, 1);
|
||||
snd_hda_add_imux_item(dimux, label, index, &type_idx);
|
||||
|
||||
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)
|
||||
return err;
|
||||
if (!err) {
|
||||
err = create_elem_capture_vol(codec, nid, label,
|
||||
HDA_OUTPUT);
|
||||
type_idx, HDA_OUTPUT);
|
||||
if (err < 0)
|
||||
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) {
|
||||
imux->items[imux->num_items].label = label;
|
||||
imux->items[imux->num_items].index = index;
|
||||
imux->num_items++;
|
||||
snd_hda_add_imux_item(imux, label, index, NULL);
|
||||
spec->num_analog_muxes++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
hda_nid_t *fixed, hda_nid_t *ext)
|
||||
hda_nid_t *fixed, hda_nid_t *ext, hda_nid_t *dock)
|
||||
{
|
||||
unsigned int cfg;
|
||||
|
||||
if (!nid)
|
||||
return 0;
|
||||
cfg = snd_hda_codec_get_pincfg(codec, nid);
|
||||
switch (get_defcfg_connect(cfg)) {
|
||||
case AC_JACK_PORT_FIXED:
|
||||
switch (snd_hda_get_input_pin_attr(cfg)) {
|
||||
case INPUT_PIN_ATTR_INT:
|
||||
if (*fixed)
|
||||
return 1; /* already occupied */
|
||||
*fixed = nid;
|
||||
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)
|
||||
return 1; /* already occupied */
|
||||
*ext = nid;
|
||||
|
@ -3542,10 +3534,13 @@ static int set_mic_route(struct hda_codec *codec,
|
|||
int i;
|
||||
|
||||
mic->pin = pin;
|
||||
for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
|
||||
if (pin == cfg->input_pins[i])
|
||||
if (pin == 0)
|
||||
return 0;
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
if (pin == cfg->inputs[i].pin)
|
||||
break;
|
||||
if (i <= AUTO_PIN_FRONT_MIC) {
|
||||
}
|
||||
if (i < cfg->num_inputs && cfg->inputs[i].type == AUTO_PIN_MIC) {
|
||||
/* analog pin */
|
||||
i = get_connection_index(codec, spec->mux_nids[0], pin);
|
||||
if (i < 0)
|
||||
|
@ -3576,26 +3571,29 @@ static int stac_check_auto_mic(struct hda_codec *codec)
|
|||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
hda_nid_t fixed, ext;
|
||||
hda_nid_t fixed, ext, dock;
|
||||
int i;
|
||||
|
||||
for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) {
|
||||
if (cfg->input_pins[i])
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
|
||||
return 0; /* must be exclusively mics */
|
||||
}
|
||||
fixed = ext = 0;
|
||||
for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
|
||||
if (check_mic_pin(codec, cfg->input_pins[i], &fixed, &ext))
|
||||
fixed = ext = dock = 0;
|
||||
for (i = 0; i < cfg->num_inputs; i++)
|
||||
if (check_mic_pin(codec, cfg->inputs[i].pin,
|
||||
&fixed, &ext, &dock))
|
||||
return 0;
|
||||
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;
|
||||
if (!fixed || !ext)
|
||||
return 0;
|
||||
if (!fixed && !ext && !dock)
|
||||
return 0; /* no input to switch */
|
||||
if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
|
||||
return 0; /* no unsol support */
|
||||
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 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 hda_input_mux *imux = &spec->private_imux;
|
||||
int i, j;
|
||||
const char *label;
|
||||
|
||||
for (i = 0; i < AUTO_PIN_LAST; i++) {
|
||||
hda_nid_t nid = cfg->input_pins[i];
|
||||
int index, err;
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
hda_nid_t nid = cfg->inputs[i].pin;
|
||||
int index, err, type_idx;
|
||||
|
||||
if (!nid)
|
||||
continue;
|
||||
index = -1;
|
||||
for (j = 0; j < spec->num_muxes; 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)
|
||||
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,
|
||||
auto_pin_cfg_labels[i],
|
||||
label, type_idx,
|
||||
HDA_INPUT);
|
||||
if (err < 0)
|
||||
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;
|
||||
|
||||
|
@ -4305,38 +4301,38 @@ static int stac92xx_init(struct hda_codec *codec)
|
|||
AC_VERB_SET_CONNECT_SEL, 0);
|
||||
if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT))
|
||||
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++) {
|
||||
hda_nid_t nid = cfg->input_pins[i];
|
||||
if (nid) {
|
||||
unsigned int pinctl, conf;
|
||||
if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
|
||||
/* for mic pins, force to initialize */
|
||||
pinctl = stac92xx_get_default_vref(codec, nid);
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
hda_nid_t nid = cfg->inputs[i].pin;
|
||||
int type = cfg->inputs[i].type;
|
||||
unsigned int pinctl, conf;
|
||||
if (type == AUTO_PIN_MIC) {
|
||||
/* for mic pins, force to initialize */
|
||||
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;
|
||||
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++)
|
||||
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);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
/* sync mute LED */
|
||||
if (spec->gpio_led && codec->patch_ops.check_power_status)
|
||||
codec->patch_ops.check_power_status(codec, 0x01);
|
||||
#endif
|
||||
if (spec->gpio_led)
|
||||
hda_call_check_power_status(codec, 0x01);
|
||||
if (spec->dac_list)
|
||||
stac92xx_power_down(codec);
|
||||
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)
|
||||
{
|
||||
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))
|
||||
mic = &spec->ext_mic;
|
||||
else if (get_pin_presence(codec, spec->dock_mic.pin))
|
||||
mic = &spec->dock_mic;
|
||||
else
|
||||
mic = &spec->int_mic;
|
||||
if (mic->dmux_idx >= 0)
|
||||
|
@ -4937,11 +4963,9 @@ static int stac92xx_resume(struct hda_codec *codec)
|
|||
stac_issue_unsol_event(codec,
|
||||
spec->autocfg.line_out_pins[0]);
|
||||
}
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
/* sync mute LED */
|
||||
if (spec->gpio_led && codec->patch_ops.check_power_status)
|
||||
codec->patch_ops.check_power_status(codec, 0x01);
|
||||
#endif
|
||||
if (spec->gpio_led)
|
||||
hda_call_check_power_status(codec, 0x01);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -5313,11 +5337,16 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
|
|||
if (spec == NULL)
|
||||
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->spec = spec;
|
||||
spec->linear_tone_beep = 1;
|
||||
codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
|
||||
spec->digbeep_nid = 0x21;
|
||||
spec->dmic_nids = stac92hd83xxx_dmic_nids;
|
||||
spec->dmux_nids = stac92hd83xxx_mux_nids;
|
||||
spec->mux_nids = stac92hd83xxx_mux_nids;
|
||||
spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids);
|
||||
spec->adc_nids = stac92hd83xxx_adc_nids;
|
||||
|
@ -5363,9 +5392,13 @@ again:
|
|||
case 0x111d76d4:
|
||||
case 0x111d7605:
|
||||
case 0x111d76d5:
|
||||
case 0x111d76e7:
|
||||
if (spec->board_config == STAC_92HD83XXX_PWR_REF)
|
||||
break;
|
||||
spec->num_pwrs = 0;
|
||||
spec->num_dmics = stac92xx_connected_ports(codec,
|
||||
stac92hd83xxx_dmic_nids,
|
||||
STAC92HD83XXX_NUM_DMICS);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -5424,36 +5457,6 @@ again:
|
|||
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,
|
||||
hda_nid_t dig0pin)
|
||||
{
|
||||
|
@ -5592,7 +5595,7 @@ again:
|
|||
case 0x111d76b5:
|
||||
spec->init = stac92hd71bxx_core_init;
|
||||
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_NUM_DMICS);
|
||||
break;
|
||||
|
@ -5624,7 +5627,7 @@ again:
|
|||
snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
|
||||
snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
|
||||
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_NUM_DMICS - 1);
|
||||
break;
|
||||
|
@ -5638,7 +5641,7 @@ again:
|
|||
default:
|
||||
spec->init = stac92hd71bxx_core_init;
|
||||
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_NUM_DMICS);
|
||||
break;
|
||||
|
@ -6320,6 +6323,8 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
|
|||
{ .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76cd, .name = "92HD89F2", .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 */
|
||||
};
|
||||
|
||||
|
|
|
@ -444,8 +444,8 @@ static hda_nid_t vt1812_adc_nids[2] = {
|
|||
|
||||
|
||||
/* add dynamic controls */
|
||||
static int via_add_control(struct via_spec *spec, int type, const char *name,
|
||||
unsigned long val)
|
||||
static int __via_add_control(struct via_spec *spec, int type, const char *name,
|
||||
int idx, unsigned long val)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
#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,
|
||||
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 */
|
||||
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];
|
||||
int err;
|
||||
|
||||
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));
|
||||
if (err < 0)
|
||||
return err;
|
||||
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));
|
||||
if (err < 0)
|
||||
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)
|
||||
{
|
||||
struct via_spec *spec = codec->spec;
|
||||
const struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
unsigned int ctl;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < AUTO_PIN_LAST; i++) {
|
||||
hda_nid_t nid = spec->autocfg.input_pins[i];
|
||||
if (!nid)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
hda_nid_t nid = cfg->inputs[i].pin;
|
||||
if (spec->smart51_enabled && is_smart51_pins(spec, nid))
|
||||
ctl = PIN_OUT;
|
||||
else if (i <= AUTO_PIN_FRONT_MIC)
|
||||
else if (i == AUTO_PIN_MIC)
|
||||
ctl = PIN_VREF50;
|
||||
else
|
||||
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)
|
||||
{
|
||||
int res = 0;
|
||||
int index;
|
||||
for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
|
||||
if (pin == spec->autocfg.input_pins[index]) {
|
||||
res = 1;
|
||||
break;
|
||||
}
|
||||
const struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
if (pin == cfg->inputs[i].pin)
|
||||
return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
|
||||
}
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 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 i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(index); i++) {
|
||||
hda_nid_t nid = spec->autocfg.input_pins[index[i]];
|
||||
if (nid) {
|
||||
int ctl =
|
||||
snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_PIN_WIDGET_CONTROL,
|
||||
0);
|
||||
if (i == AUTO_PIN_FRONT_MIC
|
||||
&& spec->hp_independent_mode
|
||||
&& spec->codec_type != VT1718S)
|
||||
continue; /* ignore FMic for independent HP */
|
||||
if (ctl & AC_PINCTL_IN_EN
|
||||
&& !(ctl & AC_PINCTL_OUT_EN))
|
||||
on = 0;
|
||||
}
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
hda_nid_t nid = cfg->inputs[i].pin;
|
||||
int ctl = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
||||
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; /* ignore FMic for independent HP */
|
||||
if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
|
||||
on = 0;
|
||||
}
|
||||
*ucontrol->value.integer.value = on;
|
||||
return 0;
|
||||
|
@ -1377,36 +1373,38 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol,
|
|||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct via_spec *spec = codec->spec;
|
||||
const struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
int out_in = *ucontrol->value.integer.value
|
||||
? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
|
||||
int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(index); i++) {
|
||||
hda_nid_t nid = spec->autocfg.input_pins[index[i]];
|
||||
if (i == AUTO_PIN_FRONT_MIC
|
||||
&& spec->hp_independent_mode
|
||||
&& spec->codec_type != VT1718S)
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
hda_nid_t nid = cfg->inputs[i].pin;
|
||||
unsigned int parm;
|
||||
|
||||
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 */
|
||||
if (nid) {
|
||||
unsigned int parm = snd_hda_codec_read(
|
||||
codec, nid, 0,
|
||||
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
||||
parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
|
||||
parm |= out_in;
|
||||
snd_hda_codec_write(codec, nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL,
|
||||
parm);
|
||||
if (out_in == AC_PINCTL_OUT_EN) {
|
||||
mute_aa_path(codec, 1);
|
||||
notify_aa_path_ctls(codec);
|
||||
}
|
||||
if (spec->codec_type == VT1718S)
|
||||
snd_hda_codec_amp_stereo(
|
||||
|
||||
parm = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
||||
parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
|
||||
parm |= out_in;
|
||||
snd_hda_codec_write(codec, nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL,
|
||||
parm);
|
||||
if (out_in == AC_PINCTL_OUT_EN) {
|
||||
mute_aa_path(codec, 1);
|
||||
notify_aa_path_ctls(codec);
|
||||
}
|
||||
if (spec->codec_type == VT1718S) {
|
||||
snd_hda_codec_amp_stereo(
|
||||
codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
|
||||
HDA_AMP_UNMUTE);
|
||||
}
|
||||
if (i == AUTO_PIN_FRONT_MIC) {
|
||||
if (cfg->inputs[i].type == AUTO_PIN_MIC) {
|
||||
if (spec->codec_type == VT1708S
|
||||
|| spec->codec_type == VT1716S) {
|
||||
/* 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)
|
||||
{
|
||||
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;
|
||||
int i;
|
||||
|
||||
|
@ -1450,13 +1448,14 @@ static int via_smart51_build(struct via_spec *spec)
|
|||
if (knew == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(index); i++) {
|
||||
nid = spec->autocfg.input_pins[index[i]];
|
||||
if (nid) {
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
nid = cfg->inputs[i].pin;
|
||||
if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
|
||||
knew = via_clone_control(spec, &via_smart51_mixer[1]);
|
||||
if (knew == NULL)
|
||||
return -ENOMEM;
|
||||
knew->subdevice = nid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2375,13 +2374,8 @@ static void create_hp_imux(struct via_spec *spec)
|
|||
static const char *texts[] = { "OFF", "ON", NULL};
|
||||
|
||||
/* for hp mode select */
|
||||
i = 0;
|
||||
while (texts[i] != NULL) {
|
||||
imux->items[imux->num_items].label = texts[i];
|
||||
imux->items[imux->num_items].index = i;
|
||||
imux->num_items++;
|
||||
i++;
|
||||
}
|
||||
for (i = 0; texts[i]; i++)
|
||||
snd_hda_add_imux_item(imux, texts[i], i, NULL);
|
||||
|
||||
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 */
|
||||
static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
|
||||
const struct auto_pin_cfg *cfg)
|
||||
static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
|
||||
const struct auto_pin_cfg *cfg,
|
||||
hda_nid_t cap_nid,
|
||||
hda_nid_t pin_idxs[], int num_idxs)
|
||||
{
|
||||
static char *labels[] = {
|
||||
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
|
||||
};
|
||||
struct via_spec *spec = codec->spec;
|
||||
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 */
|
||||
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 0x24: /* CD */
|
||||
idx = 1;
|
||||
for (idx = 0; idx < num_idxs; idx++) {
|
||||
if (pin_idxs[idx] == 0xff) {
|
||||
snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
|
||||
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)
|
||||
return err;
|
||||
imux->items[imux->num_items].label = labels[i];
|
||||
imux->items[imux->num_items].index = idx;
|
||||
imux->num_items++;
|
||||
snd_hda_add_imux_item(imux, label, idx, NULL);
|
||||
}
|
||||
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
|
||||
static struct hda_amp_list vt1708_loopbacks[] = {
|
||||
{ 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]);
|
||||
if (err < 0)
|
||||
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)
|
||||
return err;
|
||||
/* 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 */
|
||||
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)
|
||||
{
|
||||
static char *labels[] = {
|
||||
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
|
||||
};
|
||||
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 hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 };
|
||||
return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs,
|
||||
ARRAY_SIZE(pin_idxs));
|
||||
}
|
||||
|
||||
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]);
|
||||
if (err < 0)
|
||||
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)
|
||||
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 */
|
||||
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)
|
||||
{
|
||||
static char *labels[] = {
|
||||
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
|
||||
};
|
||||
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 hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e };
|
||||
return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
|
||||
ARRAY_SIZE(pin_idxs));
|
||||
}
|
||||
|
||||
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]);
|
||||
if (err < 0)
|
||||
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)
|
||||
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 */
|
||||
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)
|
||||
{
|
||||
static char *labels[] = {
|
||||
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
|
||||
};
|
||||
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 hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
|
||||
return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
|
||||
ARRAY_SIZE(pin_idxs));
|
||||
}
|
||||
|
||||
/* 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]);
|
||||
if (err < 0)
|
||||
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)
|
||||
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];
|
||||
|
||||
/* for hp mode select */
|
||||
i = 0;
|
||||
while (texts[i] != NULL) {
|
||||
imux->items[imux->num_items].label = texts[i];
|
||||
imux->items[imux->num_items].index = i;
|
||||
imux->num_items++;
|
||||
i++;
|
||||
}
|
||||
for (i = 0; texts[i]; i++)
|
||||
snd_hda_add_imux_item(imux, texts[i], i, NULL);
|
||||
|
||||
spec->hp_mux = &spec->private_imux[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
static char *labels[] = {
|
||||
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
|
||||
};
|
||||
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 hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff };
|
||||
return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs,
|
||||
ARRAY_SIZE(pin_idxs));
|
||||
}
|
||||
|
||||
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) |
|
||||
(0x5 << AC_AMPCAP_STEP_SIZE_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)
|
||||
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 */
|
||||
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)
|
||||
{
|
||||
static char *labels[] = {
|
||||
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
|
||||
};
|
||||
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 hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff };
|
||||
return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
|
||||
ARRAY_SIZE(pin_idxs));
|
||||
}
|
||||
|
||||
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]);
|
||||
if (err < 0)
|
||||
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)
|
||||
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 */
|
||||
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)
|
||||
{
|
||||
static char *labels[] = {
|
||||
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
|
||||
};
|
||||
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 hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
|
||||
return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
|
||||
ARRAY_SIZE(pin_idxs));
|
||||
}
|
||||
|
||||
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]);
|
||||
if (err < 0)
|
||||
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)
|
||||
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 */
|
||||
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)
|
||||
{
|
||||
static char *labels[] = {
|
||||
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
|
||||
};
|
||||
struct via_spec *spec = codec->spec;
|
||||
struct hda_input_mux *imux = &spec->private_imux[0];
|
||||
int i, err, idx = 0;
|
||||
|
||||
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++;
|
||||
}
|
||||
static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff };
|
||||
int err;
|
||||
|
||||
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 */
|
||||
err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21);
|
||||
err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21);
|
||||
if (err < 0)
|
||||
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 */
|
||||
imux->items[imux->num_items].label = "Digital Mic";
|
||||
imux->items[imux->num_items].index = 4;
|
||||
imux->num_items++;
|
||||
snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL);
|
||||
|
||||
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]);
|
||||
if (err < 0)
|
||||
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)
|
||||
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 */
|
||||
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)
|
||||
{
|
||||
static char *labels[] = {
|
||||
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
|
||||
};
|
||||
struct via_spec *spec = codec->spec;
|
||||
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++) {
|
||||
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++;
|
||||
}
|
||||
/* build volume/mute control of loopback */
|
||||
err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21);
|
||||
err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
|
||||
ARRAY_SIZE(pin_idxs));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* for internal loopback recording select */
|
||||
imux->items[imux->num_items].label = "Stereo Mixer";
|
||||
imux->items[imux->num_items].index = 5;
|
||||
imux->num_items++;
|
||||
/* build volume/mute control of loopback */
|
||||
err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* for digital mic select */
|
||||
imux->items[imux->num_items].label = "Digital Mic";
|
||||
imux->items[imux->num_items].index = 6;
|
||||
imux->num_items++;
|
||||
snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL);
|
||||
|
||||
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]);
|
||||
if (err < 0)
|
||||
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)
|
||||
return err;
|
||||
|
||||
|
|
Loading…
Reference in New Issue