Merge branch 'topic/hda' into for-next
The updates of HD-audio drivers for 3.15
This commit is contained in:
commit
63f4b3a475
|
@ -762,7 +762,7 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
|
|||
AC_PWRST_D0);
|
||||
}
|
||||
if (enable && path->multi[i])
|
||||
snd_hda_codec_write_cache(codec, nid, 0,
|
||||
snd_hda_codec_update_cache(codec, nid, 0,
|
||||
AC_VERB_SET_CONNECT_SEL,
|
||||
path->idx[i]);
|
||||
if (has_amp_in(codec, path, i))
|
||||
|
|
|
@ -834,18 +834,6 @@ static unsigned int azx_command_addr(u32 cmd)
|
|||
return addr;
|
||||
}
|
||||
|
||||
static unsigned int azx_response_addr(u32 res)
|
||||
{
|
||||
unsigned int addr = res & 0xf;
|
||||
|
||||
if (addr >= AZX_MAX_CODECS) {
|
||||
snd_BUG();
|
||||
addr = 0;
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* send a command */
|
||||
static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
|
||||
{
|
||||
|
@ -907,8 +895,15 @@ static void azx_update_rirb(struct azx *chip)
|
|||
rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
|
||||
res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
|
||||
res = le32_to_cpu(chip->rirb.buf[rp]);
|
||||
addr = azx_response_addr(res_ex);
|
||||
if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
|
||||
addr = res_ex & 0xf;
|
||||
if ((addr >= AZX_MAX_CODECS) || !(chip->codec_mask & (1 << addr))) {
|
||||
snd_printk(KERN_ERR SFX "%s: spurious response %#x:%#x, rp = %d, wp = %d",
|
||||
pci_name(chip->pci),
|
||||
res, res_ex,
|
||||
chip->rirb.rp, wp);
|
||||
snd_BUG();
|
||||
}
|
||||
else if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
|
||||
snd_hda_queue_unsol_event(chip->bus, res, res_ex);
|
||||
else if (chip->rirb.cmds[addr]) {
|
||||
chip->rirb.res[addr] = res;
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#include "hda_jack.h"
|
||||
#include "hda_generic.h"
|
||||
|
||||
#define ENABLE_CXT_STATIC_QUIRKS
|
||||
#undef ENABLE_CXT_STATIC_QUIRKS
|
||||
|
||||
#define CXT_PIN_DIR_IN 0x00
|
||||
#define CXT_PIN_DIR_OUT 0x01
|
||||
|
@ -68,6 +68,12 @@ struct conexant_spec {
|
|||
|
||||
unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
|
||||
|
||||
/* OPLC XO specific */
|
||||
bool recording;
|
||||
bool dc_enable;
|
||||
unsigned int dc_input_bias; /* offset into olpc_xo_dc_bias */
|
||||
struct nid_path *dc_mode_path;
|
||||
|
||||
#ifdef ENABLE_CXT_STATIC_QUIRKS
|
||||
const struct snd_kcontrol_new *mixers[5];
|
||||
int num_mixers;
|
||||
|
@ -123,19 +129,6 @@ struct conexant_spec {
|
|||
unsigned int hp_laptop:1;
|
||||
unsigned int asus:1;
|
||||
|
||||
unsigned int ext_mic_present;
|
||||
unsigned int recording;
|
||||
void (*capture_prepare)(struct hda_codec *codec);
|
||||
void (*capture_cleanup)(struct hda_codec *codec);
|
||||
|
||||
/* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
|
||||
* through the microphone jack.
|
||||
* When the user enables this through a mixer switch, both internal and
|
||||
* external microphones are disabled. Gain is fixed at 0dB. In this mode,
|
||||
* we also allow the bias to be configured through a separate mixer
|
||||
* control. */
|
||||
unsigned int dc_enable;
|
||||
unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */
|
||||
unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */
|
||||
#endif /* ENABLE_CXT_STATIC_QUIRKS */
|
||||
};
|
||||
|
@ -253,8 +246,6 @@ static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
if (spec->capture_prepare)
|
||||
spec->capture_prepare(codec);
|
||||
snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
|
||||
stream_tag, 0, format);
|
||||
return 0;
|
||||
|
@ -266,8 +257,6 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
|||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
|
||||
if (spec->capture_cleanup)
|
||||
spec->capture_cleanup(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -673,14 +662,6 @@ static const struct hda_input_mux cxt5045_capture_source_benq = {
|
|||
}
|
||||
};
|
||||
|
||||
static const struct hda_input_mux cxt5045_capture_source_hp530 = {
|
||||
.num_items = 2,
|
||||
.items = {
|
||||
{ "Mic", 0x1 },
|
||||
{ "Internal Mic", 0x2 },
|
||||
}
|
||||
};
|
||||
|
||||
/* turn on/off EAPD (+ mute HP) as a master switch */
|
||||
static int cxt5045_hp_master_sw_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
|
@ -796,28 +777,6 @@ static const struct snd_kcontrol_new cxt5045_benq_mixers[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
|
||||
HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x00, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x2, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Mic Playback Switch", 0x17, 0x1, HDA_INPUT),
|
||||
HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Master Playback Switch",
|
||||
.info = cxt_eapd_info,
|
||||
.get = cxt_eapd_get,
|
||||
.put = cxt5045_hp_master_sw_put,
|
||||
.private_value = 0x10,
|
||||
},
|
||||
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct hda_verb cxt5045_init_verbs[] = {
|
||||
/* Line in, Mic */
|
||||
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
|
||||
|
@ -1000,7 +959,6 @@ enum {
|
|||
CXT5045_LAPTOP_MICSENSE,
|
||||
CXT5045_LAPTOP_HPMICSENSE,
|
||||
CXT5045_BENQ,
|
||||
CXT5045_LAPTOP_HP530,
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
CXT5045_TEST,
|
||||
#endif
|
||||
|
@ -1013,7 +971,6 @@ static const char * const cxt5045_models[CXT5045_MODELS] = {
|
|||
[CXT5045_LAPTOP_MICSENSE] = "laptop-micsense",
|
||||
[CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense",
|
||||
[CXT5045_BENQ] = "benq",
|
||||
[CXT5045_LAPTOP_HP530] = "laptop-hp530",
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
[CXT5045_TEST] = "test",
|
||||
#endif
|
||||
|
@ -1021,8 +978,6 @@ static const char * const cxt5045_models[CXT5045_MODELS] = {
|
|||
};
|
||||
|
||||
static const struct snd_pci_quirk cxt5045_cfg_tbl[] = {
|
||||
SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
|
||||
SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE),
|
||||
SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
|
||||
SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
|
||||
SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE),
|
||||
|
@ -1113,14 +1068,6 @@ static int patch_cxt5045(struct hda_codec *codec)
|
|||
spec->num_mixers = 2;
|
||||
codec->patch_ops.init = cxt5045_init;
|
||||
break;
|
||||
case CXT5045_LAPTOP_HP530:
|
||||
codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
|
||||
spec->input_mux = &cxt5045_capture_source_hp530;
|
||||
spec->num_init_verbs = 2;
|
||||
spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
|
||||
spec->mixers[0] = cxt5045_mixers_hp530;
|
||||
codec->patch_ops.init = cxt5045_init;
|
||||
break;
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
case CXT5045_TEST:
|
||||
spec->input_mux = &cxt5045_test_capture_source;
|
||||
|
@ -1940,11 +1887,6 @@ static const hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 };
|
|||
static const hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 };
|
||||
static const hda_nid_t cxt5066_digout_pin_nids[2] = { 0x20, 0x22 };
|
||||
|
||||
/* OLPC's microphone port is DC coupled for use with external sensors,
|
||||
* therefore we use a 50% mic bias in order to center the input signal with
|
||||
* the DC input range of the codec. */
|
||||
#define CXT5066_OLPC_EXT_MIC_BIAS PIN_VREF50
|
||||
|
||||
static const struct hda_channel_mode cxt5066_modes[1] = {
|
||||
{ 2, NULL },
|
||||
};
|
||||
|
@ -1997,88 +1939,6 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static const struct hda_input_mux cxt5066_olpc_dc_bias = {
|
||||
.num_items = 3,
|
||||
.items = {
|
||||
{ "Off", PIN_IN },
|
||||
{ "50%", PIN_VREF50 },
|
||||
{ "80%", PIN_VREF80 },
|
||||
},
|
||||
};
|
||||
|
||||
static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
/* Even though port F is the DC input, the bias is controlled on port B.
|
||||
* we also leave that port as an active input (but unselected) in DC mode
|
||||
* just in case that is necessary to make the bias setting take effect. */
|
||||
return snd_hda_set_pin_ctl_cache(codec, 0x1a,
|
||||
cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index);
|
||||
}
|
||||
|
||||
/* OLPC defers mic widget control until when capture is started because the
|
||||
* microphone LED comes on as soon as these settings are put in place. if we
|
||||
* did this before recording, it would give the false indication that recording
|
||||
* is happening when it is not. */
|
||||
static void cxt5066_olpc_select_mic(struct hda_codec *codec)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
if (!spec->recording)
|
||||
return;
|
||||
|
||||
if (spec->dc_enable) {
|
||||
/* in DC mode we ignore presence detection and just use the jack
|
||||
* through our special DC port */
|
||||
const struct hda_verb enable_dc_mode[] = {
|
||||
/* disble internal mic, port C */
|
||||
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* enable DC capture, port F */
|
||||
{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
||||
{},
|
||||
};
|
||||
|
||||
snd_hda_sequence_write(codec, enable_dc_mode);
|
||||
/* port B input disabled (and bias set) through the following call */
|
||||
cxt5066_set_olpc_dc_bias(codec);
|
||||
return;
|
||||
}
|
||||
|
||||
/* disable DC (port F) */
|
||||
snd_hda_set_pin_ctl(codec, 0x1e, 0);
|
||||
|
||||
/* external mic, port B */
|
||||
snd_hda_set_pin_ctl(codec, 0x1a,
|
||||
spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
|
||||
|
||||
/* internal mic, port C */
|
||||
snd_hda_set_pin_ctl(codec, 0x1b,
|
||||
spec->ext_mic_present ? 0 : PIN_VREF80);
|
||||
}
|
||||
|
||||
/* toggle input of built-in and mic jack appropriately */
|
||||
static void cxt5066_olpc_automic(struct hda_codec *codec)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
unsigned int present;
|
||||
|
||||
if (spec->dc_enable) /* don't do presence detection in DC mode */
|
||||
return;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x1a, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
|
||||
if (present)
|
||||
snd_printdd("CXT5066: external microphone detected\n");
|
||||
else
|
||||
snd_printdd("CXT5066: external microphone absent\n");
|
||||
|
||||
snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL,
|
||||
present ? 0 : 1);
|
||||
spec->ext_mic_present = !!present;
|
||||
|
||||
cxt5066_olpc_select_mic(codec);
|
||||
}
|
||||
|
||||
/* toggle input of built-in digital mic and mic jack appropriately */
|
||||
static void cxt5066_vostro_automic(struct hda_codec *codec)
|
||||
{
|
||||
|
@ -2251,23 +2111,6 @@ static void cxt5066_automic(struct hda_codec *codec)
|
|||
cxt5066_asus_automic(codec);
|
||||
}
|
||||
|
||||
/* unsolicited event for jack sensing */
|
||||
static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26);
|
||||
switch (res >> 26) {
|
||||
case CONEXANT_HP_EVENT:
|
||||
cxt5066_hp_automute(codec);
|
||||
break;
|
||||
case CONEXANT_MIC_EVENT:
|
||||
/* ignore mic events in DC mode; we're always using the jack */
|
||||
if (!spec->dc_enable)
|
||||
cxt5066_olpc_automic(codec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* unsolicited event for jack sensing */
|
||||
static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
|
@ -2338,124 +2181,10 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
|
|||
idx = imux->num_items - 1;
|
||||
|
||||
spec->mic_boost = idx;
|
||||
if (!spec->dc_enable)
|
||||
cxt5066_set_mic_boost(codec);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void cxt5066_enable_dc(struct hda_codec *codec)
|
||||
{
|
||||
const struct hda_verb enable_dc_mode[] = {
|
||||
/* disable gain */
|
||||
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
||||
|
||||
/* switch to DC input */
|
||||
{0x17, AC_VERB_SET_CONNECT_SEL, 3},
|
||||
{}
|
||||
};
|
||||
|
||||
/* configure as input source */
|
||||
snd_hda_sequence_write(codec, enable_dc_mode);
|
||||
cxt5066_olpc_select_mic(codec); /* also sets configured bias */
|
||||
}
|
||||
|
||||
static void cxt5066_disable_dc(struct hda_codec *codec)
|
||||
{
|
||||
/* reconfigure input source */
|
||||
cxt5066_set_mic_boost(codec);
|
||||
/* automic also selects the right mic if we're recording */
|
||||
cxt5066_olpc_automic(codec);
|
||||
}
|
||||
|
||||
static int cxt5066_olpc_dc_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
ucontrol->value.integer.value[0] = spec->dc_enable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxt5066_olpc_dc_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
int dc_enable = !!ucontrol->value.integer.value[0];
|
||||
|
||||
if (dc_enable == spec->dc_enable)
|
||||
return 0;
|
||||
|
||||
spec->dc_enable = dc_enable;
|
||||
if (dc_enable)
|
||||
cxt5066_enable_dc(codec);
|
||||
else
|
||||
cxt5066_disable_dc(codec);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cxt5066_olpc_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
return snd_hda_input_mux_info(&cxt5066_olpc_dc_bias, uinfo);
|
||||
}
|
||||
|
||||
static int cxt5066_olpc_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxt5066_olpc_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
|
||||
unsigned int idx;
|
||||
|
||||
idx = ucontrol->value.enumerated.item[0];
|
||||
if (idx >= imux->num_items)
|
||||
idx = imux->num_items - 1;
|
||||
|
||||
spec->dc_input_bias = idx;
|
||||
if (spec->dc_enable)
|
||||
cxt5066_set_olpc_dc_bias(codec);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void cxt5066_olpc_capture_prepare(struct hda_codec *codec)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
/* mark as recording and configure the microphone widget so that the
|
||||
* recording LED comes on. */
|
||||
spec->recording = 1;
|
||||
cxt5066_olpc_select_mic(codec);
|
||||
}
|
||||
|
||||
static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
const struct hda_verb disable_mics[] = {
|
||||
/* disable external mic, port B */
|
||||
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* disble internal mic, port C */
|
||||
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* disable DC capture, port F */
|
||||
{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{},
|
||||
};
|
||||
|
||||
snd_hda_sequence_write(codec, disable_mics);
|
||||
spec->recording = 0;
|
||||
}
|
||||
|
||||
static void conexant_check_dig_outs(struct hda_codec *codec,
|
||||
const hda_nid_t *dig_pins,
|
||||
int num_pins)
|
||||
|
@ -2506,43 +2235,6 @@ static const struct snd_kcontrol_new cxt5066_mixer_master[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Master Playback Volume",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
|
||||
.subdevice = HDA_SUBDEV_AMP_FLAG,
|
||||
.info = snd_hda_mixer_amp_volume_info,
|
||||
.get = snd_hda_mixer_amp_volume_get,
|
||||
.put = snd_hda_mixer_amp_volume_put,
|
||||
.tlv = { .c = snd_hda_mixer_amp_tlv },
|
||||
/* offset by 28 volume steps to limit minimum gain to -46dB */
|
||||
.private_value =
|
||||
HDA_COMPOSE_AMP_VAL_OFS(0x10, 3, 0, HDA_OUTPUT, 28),
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new cxt5066_mixer_olpc_dc[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "DC Mode Enable Switch",
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = cxt5066_olpc_dc_get,
|
||||
.put = cxt5066_olpc_dc_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "DC Input Bias Enum",
|
||||
.info = cxt5066_olpc_dc_bias_enum_info,
|
||||
.get = cxt5066_olpc_dc_bias_enum_get,
|
||||
.put = cxt5066_olpc_dc_bias_enum_put,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new cxt5066_mixers[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
|
@ -2633,67 +2325,6 @@ static const struct hda_verb cxt5066_init_verbs[] = {
|
|||
{ } /* end */
|
||||
};
|
||||
|
||||
static const struct hda_verb cxt5066_init_verbs_olpc[] = {
|
||||
/* Port A: headphones */
|
||||
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
||||
{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
|
||||
|
||||
/* Port B: external microphone */
|
||||
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* Port C: internal microphone */
|
||||
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* Port D: unused */
|
||||
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* Port E: unused, but has primary EAPD */
|
||||
{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
|
||||
|
||||
/* Port F: external DC input through microphone port */
|
||||
{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* Port G: internal speakers */
|
||||
{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
||||
{0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
|
||||
|
||||
/* DAC1 */
|
||||
{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
||||
|
||||
/* DAC2: unused */
|
||||
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
||||
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
||||
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
||||
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
||||
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
||||
|
||||
/* Disable digital microphone port */
|
||||
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* Audio input selectors */
|
||||
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
|
||||
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
|
||||
|
||||
/* Disable SPDIF */
|
||||
{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* enable unsolicited events for Port A and B */
|
||||
{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
|
||||
{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
static const struct hda_verb cxt5066_init_verbs_vostro[] = {
|
||||
/* Port A: headphones */
|
||||
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
@ -2889,25 +2520,9 @@ static int cxt5066_init(struct hda_codec *codec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cxt5066_olpc_init(struct hda_codec *codec)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
snd_printdd("CXT5066: init\n");
|
||||
conexant_init(codec);
|
||||
cxt5066_hp_automute(codec);
|
||||
if (!spec->dc_enable) {
|
||||
cxt5066_set_mic_boost(codec);
|
||||
cxt5066_olpc_automic(codec);
|
||||
} else {
|
||||
cxt5066_enable_dc(codec);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
CXT5066_LAPTOP, /* Laptops w/ EAPD support */
|
||||
CXT5066_DELL_LAPTOP, /* Dell Laptop */
|
||||
CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
|
||||
CXT5066_DELL_VOSTRO, /* Dell Vostro 1015i */
|
||||
CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */
|
||||
CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */
|
||||
|
@ -2920,7 +2535,6 @@ enum {
|
|||
static const char * const cxt5066_models[CXT5066_MODELS] = {
|
||||
[CXT5066_LAPTOP] = "laptop",
|
||||
[CXT5066_DELL_LAPTOP] = "dell-laptop",
|
||||
[CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
|
||||
[CXT5066_DELL_VOSTRO] = "dell-vostro",
|
||||
[CXT5066_IDEAPAD] = "ideapad",
|
||||
[CXT5066_THINKPAD] = "thinkpad",
|
||||
|
@ -2941,10 +2555,8 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = {
|
|||
SND_PCI_QUIRK(0x1043, 0x1643, "Asus K52JU", CXT5066_ASUS),
|
||||
SND_PCI_QUIRK(0x1043, 0x1993, "Asus U50F", CXT5066_ASUS),
|
||||
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(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, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD),
|
||||
SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS),
|
||||
|
@ -3030,32 +2642,11 @@ static int patch_cxt5066(struct hda_codec *codec)
|
|||
spec->mic_boost = 3; /* default 30dB gain */
|
||||
break;
|
||||
|
||||
case CXT5066_OLPC_XO_1_5:
|
||||
codec->patch_ops.init = cxt5066_olpc_init;
|
||||
codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event;
|
||||
spec->init_verbs[0] = cxt5066_init_verbs_olpc;
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixer_olpc_dc;
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixers;
|
||||
spec->port_d_mode = 0;
|
||||
spec->mic_boost = 3; /* default 30dB gain */
|
||||
|
||||
/* no S/PDIF out */
|
||||
spec->multiout.dig_out_nid = 0;
|
||||
|
||||
/* input source automatically selected */
|
||||
spec->input_mux = NULL;
|
||||
|
||||
/* our capture hooks which allow us to turn on the microphone LED
|
||||
* at the right time */
|
||||
spec->capture_prepare = cxt5066_olpc_capture_prepare;
|
||||
spec->capture_cleanup = cxt5066_olpc_capture_cleanup;
|
||||
break;
|
||||
case CXT5066_DELL_VOSTRO:
|
||||
codec->patch_ops.init = cxt5066_init;
|
||||
codec->patch_ops.unsol_event = cxt5066_unsol_event;
|
||||
spec->init_verbs[0] = cxt5066_init_verbs_vostro;
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixers;
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers;
|
||||
spec->port_d_mode = 0;
|
||||
|
@ -3238,6 +2829,11 @@ enum {
|
|||
CXT_FIXUP_HEADPHONE_MIC,
|
||||
CXT_FIXUP_GPIO1,
|
||||
CXT_FIXUP_THINKPAD_ACPI,
|
||||
CXT_FIXUP_OLPC_XO,
|
||||
CXT_FIXUP_CAP_MIX_AMP,
|
||||
CXT_FIXUP_TOSHIBA_P105,
|
||||
CXT_FIXUP_HP_530,
|
||||
CXT_FIXUP_CAP_MIX_AMP_5047,
|
||||
};
|
||||
|
||||
/* for hda_fixup_thinkpad_acpi() */
|
||||
|
@ -3315,6 +2911,288 @@ static void cxt_fixup_headphone_mic(struct hda_codec *codec,
|
|||
}
|
||||
}
|
||||
|
||||
/* OPLC XO 1.5 fixup */
|
||||
|
||||
/* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
|
||||
* through the microphone jack.
|
||||
* When the user enables this through a mixer switch, both internal and
|
||||
* external microphones are disabled. Gain is fixed at 0dB. In this mode,
|
||||
* we also allow the bias to be configured through a separate mixer
|
||||
* control. */
|
||||
|
||||
#define update_mic_pin(codec, nid, val) \
|
||||
snd_hda_codec_update_cache(codec, nid, 0, \
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, val)
|
||||
|
||||
static const struct hda_input_mux olpc_xo_dc_bias = {
|
||||
.num_items = 3,
|
||||
.items = {
|
||||
{ "Off", PIN_IN },
|
||||
{ "50%", PIN_VREF50 },
|
||||
{ "80%", PIN_VREF80 },
|
||||
},
|
||||
};
|
||||
|
||||
static void olpc_xo_update_mic_boost(struct hda_codec *codec)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
int ch, val;
|
||||
|
||||
for (ch = 0; ch < 2; ch++) {
|
||||
val = AC_AMP_SET_OUTPUT |
|
||||
(ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT);
|
||||
if (!spec->dc_enable)
|
||||
val |= snd_hda_codec_amp_read(codec, 0x17, ch, HDA_OUTPUT, 0);
|
||||
snd_hda_codec_write(codec, 0x17, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void olpc_xo_update_mic_pins(struct hda_codec *codec)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
int cur_input, val;
|
||||
struct nid_path *path;
|
||||
|
||||
cur_input = spec->gen.input_paths[0][spec->gen.cur_mux[0]];
|
||||
|
||||
/* Set up mic pins for port-B, C and F dynamically as the recording
|
||||
* LED is turned on/off by these pin controls
|
||||
*/
|
||||
if (!spec->dc_enable) {
|
||||
/* disable DC bias path and pin for port F */
|
||||
update_mic_pin(codec, 0x1e, 0);
|
||||
snd_hda_activate_path(codec, spec->dc_mode_path, false, false);
|
||||
|
||||
/* update port B (ext mic) and C (int mic) */
|
||||
/* OLPC defers mic widget control until when capture is
|
||||
* started because the microphone LED comes on as soon as
|
||||
* these settings are put in place. if we did this before
|
||||
* recording, it would give the false indication that
|
||||
* recording is happening when it is not.
|
||||
*/
|
||||
update_mic_pin(codec, 0x1a, spec->recording ?
|
||||
snd_hda_codec_get_pin_target(codec, 0x1a) : 0);
|
||||
update_mic_pin(codec, 0x1b, spec->recording ?
|
||||
snd_hda_codec_get_pin_target(codec, 0x1b) : 0);
|
||||
/* enable normal mic path */
|
||||
path = snd_hda_get_path_from_idx(codec, cur_input);
|
||||
if (path)
|
||||
snd_hda_activate_path(codec, path, true, false);
|
||||
} else {
|
||||
/* disable normal mic path */
|
||||
path = snd_hda_get_path_from_idx(codec, cur_input);
|
||||
if (path)
|
||||
snd_hda_activate_path(codec, path, false, false);
|
||||
|
||||
/* Even though port F is the DC input, the bias is controlled
|
||||
* on port B. We also leave that port as an active input (but
|
||||
* unselected) in DC mode just in case that is necessary to
|
||||
* make the bias setting take effect.
|
||||
*/
|
||||
if (spec->recording)
|
||||
val = olpc_xo_dc_bias.items[spec->dc_input_bias].index;
|
||||
else
|
||||
val = 0;
|
||||
update_mic_pin(codec, 0x1a, val);
|
||||
update_mic_pin(codec, 0x1b, 0);
|
||||
/* enable DC bias path and pin */
|
||||
update_mic_pin(codec, 0x1e, spec->recording ? PIN_IN : 0);
|
||||
snd_hda_activate_path(codec, spec->dc_mode_path, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* mic_autoswitch hook */
|
||||
static void olpc_xo_automic(struct hda_codec *codec, struct hda_jack_tbl *jack)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
int saved_cached_write = codec->cached_write;
|
||||
|
||||
codec->cached_write = 1;
|
||||
/* in DC mode, we don't handle automic */
|
||||
if (!spec->dc_enable)
|
||||
snd_hda_gen_mic_autoswitch(codec, jack);
|
||||
olpc_xo_update_mic_pins(codec);
|
||||
snd_hda_codec_flush_cache(codec);
|
||||
codec->cached_write = saved_cached_write;
|
||||
if (spec->dc_enable)
|
||||
olpc_xo_update_mic_boost(codec);
|
||||
}
|
||||
|
||||
/* pcm_capture hook */
|
||||
static void olpc_xo_capture_hook(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream,
|
||||
int action)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
|
||||
/* toggle spec->recording flag and update mic pins accordingly
|
||||
* for turning on/off LED
|
||||
*/
|
||||
switch (action) {
|
||||
case HDA_GEN_PCM_ACT_PREPARE:
|
||||
spec->recording = 1;
|
||||
olpc_xo_update_mic_pins(codec);
|
||||
break;
|
||||
case HDA_GEN_PCM_ACT_CLEANUP:
|
||||
spec->recording = 0;
|
||||
olpc_xo_update_mic_pins(codec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int olpc_xo_dc_mode_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
ucontrol->value.integer.value[0] = spec->dc_enable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int olpc_xo_dc_mode_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
int dc_enable = !!ucontrol->value.integer.value[0];
|
||||
|
||||
if (dc_enable == spec->dc_enable)
|
||||
return 0;
|
||||
|
||||
spec->dc_enable = dc_enable;
|
||||
olpc_xo_update_mic_pins(codec);
|
||||
olpc_xo_update_mic_boost(codec);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int olpc_xo_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int olpc_xo_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
return snd_hda_input_mux_info(&olpc_xo_dc_bias, uinfo);
|
||||
}
|
||||
|
||||
static int olpc_xo_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
const struct hda_input_mux *imux = &olpc_xo_dc_bias;
|
||||
unsigned int idx;
|
||||
|
||||
idx = ucontrol->value.enumerated.item[0];
|
||||
if (idx >= imux->num_items)
|
||||
idx = imux->num_items - 1;
|
||||
if (spec->dc_input_bias == idx)
|
||||
return 0;
|
||||
|
||||
spec->dc_input_bias = idx;
|
||||
if (spec->dc_enable)
|
||||
olpc_xo_update_mic_pins(codec);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new olpc_xo_mixers[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "DC Mode Enable Switch",
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = olpc_xo_dc_mode_get,
|
||||
.put = olpc_xo_dc_mode_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "DC Input Bias Enum",
|
||||
.info = olpc_xo_dc_bias_enum_info,
|
||||
.get = olpc_xo_dc_bias_enum_get,
|
||||
.put = olpc_xo_dc_bias_enum_put,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
/* overriding mic boost put callback; update mic boost volume only when
|
||||
* DC mode is disabled
|
||||
*/
|
||||
static int olpc_xo_mic_boost_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
int ret = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
|
||||
if (ret > 0 && spec->dc_enable)
|
||||
olpc_xo_update_mic_boost(codec);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cxt_fixup_olpc_xo(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
int i;
|
||||
|
||||
if (action != HDA_FIXUP_ACT_PROBE)
|
||||
return;
|
||||
|
||||
spec->gen.mic_autoswitch_hook = olpc_xo_automic;
|
||||
spec->gen.pcm_capture_hook = olpc_xo_capture_hook;
|
||||
spec->dc_mode_path = snd_hda_add_new_path(codec, 0x1e, 0x14, 0);
|
||||
|
||||
snd_hda_add_new_ctls(codec, olpc_xo_mixers);
|
||||
|
||||
/* OLPC's microphone port is DC coupled for use with external sensors,
|
||||
* therefore we use a 50% mic bias in order to center the input signal
|
||||
* with the DC input range of the codec.
|
||||
*/
|
||||
snd_hda_codec_set_pin_target(codec, 0x1a, PIN_VREF50);
|
||||
|
||||
/* override mic boost control */
|
||||
for (i = 0; i < spec->gen.kctls.used; i++) {
|
||||
struct snd_kcontrol_new *kctl =
|
||||
snd_array_elem(&spec->gen.kctls, i);
|
||||
if (!strcmp(kctl->name, "Mic Boost Volume")) {
|
||||
kctl->put = olpc_xo_mic_boost_put;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fix max input level on mixer widget to 0dB
|
||||
* (originally it has 0x2b steps with 0dB offset 0x14)
|
||||
*/
|
||||
static void cxt_fixup_cap_mix_amp(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
|
||||
(0x14 << AC_AMPCAP_OFFSET_SHIFT) |
|
||||
(0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
|
||||
(0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
|
||||
(1 << AC_AMPCAP_MUTE_SHIFT));
|
||||
}
|
||||
|
||||
/*
|
||||
* Fix max input level on mixer widget to 0dB
|
||||
* (originally it has 0x1e steps with 0 dB offset 0x17)
|
||||
*/
|
||||
static void cxt_fixup_cap_mix_amp_5047(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
snd_hda_override_amp_caps(codec, 0x10, HDA_INPUT,
|
||||
(0x17 << AC_AMPCAP_OFFSET_SHIFT) |
|
||||
(0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
|
||||
(0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
|
||||
(1 << AC_AMPCAP_MUTE_SHIFT));
|
||||
}
|
||||
|
||||
/* ThinkPad X200 & co with cxt5051 */
|
||||
static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
|
||||
|
@ -3400,6 +3278,68 @@ static const struct hda_fixup cxt_fixups[] = {
|
|||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = hda_fixup_thinkpad_acpi,
|
||||
},
|
||||
[CXT_FIXUP_OLPC_XO] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = cxt_fixup_olpc_xo,
|
||||
},
|
||||
[CXT_FIXUP_CAP_MIX_AMP] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = cxt_fixup_cap_mix_amp,
|
||||
},
|
||||
[CXT_FIXUP_TOSHIBA_P105] = {
|
||||
.type = HDA_FIXUP_PINS,
|
||||
.v.pins = (const struct hda_pintbl[]) {
|
||||
{ 0x10, 0x961701f0 }, /* speaker/hp */
|
||||
{ 0x12, 0x02a1901e }, /* ext mic */
|
||||
{ 0x14, 0x95a70110 }, /* int mic */
|
||||
{}
|
||||
},
|
||||
},
|
||||
[CXT_FIXUP_HP_530] = {
|
||||
.type = HDA_FIXUP_PINS,
|
||||
.v.pins = (const struct hda_pintbl[]) {
|
||||
{ 0x12, 0x90a60160 }, /* int mic */
|
||||
{}
|
||||
},
|
||||
.chained = true,
|
||||
.chain_id = CXT_FIXUP_CAP_MIX_AMP,
|
||||
},
|
||||
[CXT_FIXUP_CAP_MIX_AMP_5047] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = cxt_fixup_cap_mix_amp_5047,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_pci_quirk cxt5045_fixups[] = {
|
||||
SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT_FIXUP_HP_530),
|
||||
SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT_FIXUP_TOSHIBA_P105),
|
||||
/* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have
|
||||
* really bad sound over 0dB on NID 0x17.
|
||||
*/
|
||||
SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP),
|
||||
SND_PCI_QUIRK_VENDOR(0x1631, "Packard Bell", CXT_FIXUP_CAP_MIX_AMP),
|
||||
SND_PCI_QUIRK_VENDOR(0x1734, "Fujitsu", CXT_FIXUP_CAP_MIX_AMP),
|
||||
SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT_FIXUP_CAP_MIX_AMP),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct hda_model_fixup cxt5045_fixup_models[] = {
|
||||
{ .id = CXT_FIXUP_CAP_MIX_AMP, .name = "cap-mix-amp" },
|
||||
{ .id = CXT_FIXUP_TOSHIBA_P105, .name = "toshiba-p105" },
|
||||
{ .id = CXT_FIXUP_HP_530, .name = "hp-530" },
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct snd_pci_quirk cxt5047_fixups[] = {
|
||||
/* HP laptops have really bad sound over 0 dB on NID 0x10.
|
||||
*/
|
||||
SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP_5047),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct hda_model_fixup cxt5047_fixup_models[] = {
|
||||
{ .id = CXT_FIXUP_CAP_MIX_AMP_5047, .name = "cap-mix-amp" },
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct snd_pci_quirk cxt5051_fixups[] = {
|
||||
|
@ -3407,10 +3347,16 @@ static const struct snd_pci_quirk cxt5051_fixups[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const struct hda_model_fixup cxt5051_fixup_models[] = {
|
||||
{ .id = CXT_PINCFG_LENOVO_X200, .name = "lenovo-x200" },
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct snd_pci_quirk cxt5066_fixups[] = {
|
||||
SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
|
||||
SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_GPIO1),
|
||||
SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
|
||||
SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO),
|
||||
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
|
||||
SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410),
|
||||
SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
|
||||
|
@ -3427,6 +3373,17 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const struct hda_model_fixup cxt5066_fixup_models[] = {
|
||||
{ .id = CXT_FIXUP_STEREO_DMIC, .name = "stereo-dmic" },
|
||||
{ .id = CXT_FIXUP_GPIO1, .name = "gpio1" },
|
||||
{ .id = CXT_FIXUP_HEADPHONE_MIC_PIN, .name = "headphone-mic-pin" },
|
||||
{ .id = CXT_PINCFG_LENOVO_TP410, .name = "tp410" },
|
||||
{ .id = CXT_FIXUP_THINKPAD_ACPI, .name = "thinkpad" },
|
||||
{ .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" },
|
||||
{ .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" },
|
||||
{}
|
||||
};
|
||||
|
||||
/* add "fake" mute amp-caps to DACs on cx5051 so that mixer mute switches
|
||||
* can be created (bko#42825)
|
||||
*/
|
||||
|
@ -3466,19 +3423,28 @@ static int patch_conexant_auto(struct hda_codec *codec)
|
|||
switch (codec->vendor_id) {
|
||||
case 0x14f15045:
|
||||
codec->single_adc_amp = 1;
|
||||
spec->gen.mixer_nid = 0x17;
|
||||
spec->gen.add_stereo_mix_input = 1;
|
||||
snd_hda_pick_fixup(codec, cxt5045_fixup_models,
|
||||
cxt5045_fixups, cxt_fixups);
|
||||
break;
|
||||
case 0x14f15047:
|
||||
codec->pin_amp_workaround = 1;
|
||||
spec->gen.mixer_nid = 0x19;
|
||||
spec->gen.add_stereo_mix_input = 1;
|
||||
snd_hda_pick_fixup(codec, cxt5047_fixup_models,
|
||||
cxt5047_fixups, cxt_fixups);
|
||||
break;
|
||||
case 0x14f15051:
|
||||
add_cx5051_fake_mutes(codec);
|
||||
codec->pin_amp_workaround = 1;
|
||||
snd_hda_pick_fixup(codec, NULL, cxt5051_fixups, cxt_fixups);
|
||||
snd_hda_pick_fixup(codec, cxt5051_fixup_models,
|
||||
cxt5051_fixups, cxt_fixups);
|
||||
break;
|
||||
default:
|
||||
codec->pin_amp_workaround = 1;
|
||||
snd_hda_pick_fixup(codec, NULL, cxt5066_fixups, cxt_fixups);
|
||||
snd_hda_pick_fixup(codec, cxt5066_fixup_models,
|
||||
cxt5066_fixups, cxt_fixups);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue