Commit 63f4b3a4 authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'topic/hda' into for-next

The updates of HD-audio drivers for 3.15
parents 4fa71c15 3d692451
......@@ -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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment