Commit 75f8991d authored by Daniel Drake's avatar Daniel Drake Committed by Jaroslav Kysela

ALSA: hda - Configure XO-1.5 microphones at capture time

The XO-1.5 has a microphone LED designed to indicate to the user when
something is being recorded.

This light is controlled by the microphone bias voltage and it is
currently coming on all the time.

This patch defers the microphone port configuration until when recording
is actually taking place, fixing the behaviour of the LED.
Signed-off-by: default avatarDaniel Drake <dsd@laptop.org>
Signed-off-by: default avatarJaroslav Kysela <perex@perex.cz>
parent a4ad68d5
...@@ -111,8 +111,12 @@ struct conexant_spec { ...@@ -111,8 +111,12 @@ struct conexant_spec {
unsigned int dell_automute; unsigned int dell_automute;
unsigned int port_d_mode; unsigned int port_d_mode;
unsigned char ext_mic_bias;
unsigned int dell_vostro; unsigned int dell_vostro;
unsigned int ext_mic_present;
unsigned int recording;
void (*capture_prepare)(struct hda_codec *codec);
void (*capture_cleanup)(struct hda_codec *codec);
}; };
static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
...@@ -185,6 +189,8 @@ static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo, ...@@ -185,6 +189,8 @@ static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream) struct snd_pcm_substream *substream)
{ {
struct conexant_spec *spec = codec->spec; 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], snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
stream_tag, 0, format); stream_tag, 0, format);
return 0; return 0;
...@@ -196,6 +202,8 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, ...@@ -196,6 +202,8 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
{ {
struct conexant_spec *spec = codec->spec; struct conexant_spec *spec = codec->spec;
snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
if (spec->capture_cleanup)
spec->capture_cleanup(codec);
return 0; return 0;
} }
...@@ -2016,53 +2024,53 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol, ...@@ -2016,53 +2024,53 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
return 1; return 1;
} }
/* toggle input of built-in and mic jack appropriately */ /* OLPC defers mic widget control until when capture is started because the
static void cxt5066_automic(struct hda_codec *codec) * 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; struct conexant_spec *spec = codec->spec;
struct hda_verb ext_mic_present[] = { if (!spec->recording)
/* enable external mic, port B */ return;
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias},
/* switch to external mic input */
{0x17, AC_VERB_SET_CONNECT_SEL, 0},
/* disable internal mic, port C */ /* external mic, port B */
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
{} spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
};
static struct hda_verb ext_mic_absent[] = {
/* enable internal mic, port C */
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
/* switch to internal mic input */ /* internal mic, port C */
{0x17, AC_VERB_SET_CONNECT_SEL, 1}, snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
spec->ext_mic_present ? 0 : PIN_VREF80);
}
/* disable external mic, port B */ /* toggle input of built-in and mic jack appropriately */
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, static void cxt5066_olpc_automic(struct hda_codec *codec)
{} {
}; struct conexant_spec *spec = codec->spec;
unsigned int present; unsigned int present;
present = snd_hda_jack_detect(codec, 0x1a); present = snd_hda_codec_read(codec, 0x1a, 0,
if (present) { AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
if (present)
snd_printdd("CXT5066: external microphone detected\n"); snd_printdd("CXT5066: external microphone detected\n");
snd_hda_sequence_write(codec, ext_mic_present); else
} else {
snd_printdd("CXT5066: external microphone absent\n"); snd_printdd("CXT5066: external microphone absent\n");
snd_hda_sequence_write(codec, ext_mic_absent);
} 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 */ /* toggle input of built-in digital mic and mic jack appropriately */
static void cxt5066_vostro_automic(struct hda_codec *codec) static void cxt5066_vostro_automic(struct hda_codec *codec)
{ {
struct conexant_spec *spec = codec->spec;
unsigned int present; unsigned int present;
struct hda_verb ext_mic_present[] = { struct hda_verb ext_mic_present[] = {
/* enable external mic, port B */ /* enable external mic, port B */
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias}, {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
/* switch to external mic input */ /* switch to external mic input */
{0x17, AC_VERB_SET_CONNECT_SEL, 0}, {0x17, AC_VERB_SET_CONNECT_SEL, 0},
...@@ -2113,7 +2121,7 @@ static void cxt5066_hp_automute(struct hda_codec *codec) ...@@ -2113,7 +2121,7 @@ static void cxt5066_hp_automute(struct hda_codec *codec)
} }
/* unsolicited event for jack sensing */ /* unsolicited event for jack sensing */
static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res)
{ {
snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26);
switch (res >> 26) { switch (res >> 26) {
...@@ -2121,7 +2129,7 @@ static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) ...@@ -2121,7 +2129,7 @@ static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res)
cxt5066_hp_automute(codec); cxt5066_hp_automute(codec);
break; break;
case CONEXANT_MIC_EVENT: case CONEXANT_MIC_EVENT:
cxt5066_automic(codec); cxt5066_olpc_automic(codec);
break; break;
} }
} }
...@@ -2197,6 +2205,31 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol, ...@@ -2197,6 +2205,31 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
return 1; 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},
{},
};
snd_hda_sequence_write(codec, disable_mics);
spec->recording = 0;
}
static struct hda_input_mux cxt5066_capture_source = { static struct hda_input_mux cxt5066_capture_source = {
.num_items = 4, .num_items = 4,
.items = { .items = {
...@@ -2347,10 +2380,10 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = { ...@@ -2347,10 +2380,10 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = {
{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
/* Port B: external microphone */ /* Port B: external microphone */
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, CXT5066_OLPC_EXT_MIC_BIAS}, {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
/* Port C: internal microphone */ /* Port C: internal microphone */
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
/* Port D: unused */ /* Port D: unused */
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
...@@ -2479,12 +2512,19 @@ static int cxt5066_init(struct hda_codec *codec) ...@@ -2479,12 +2512,19 @@ static int cxt5066_init(struct hda_codec *codec)
cxt5066_hp_automute(codec); cxt5066_hp_automute(codec);
if (spec->dell_vostro) if (spec->dell_vostro)
cxt5066_vostro_automic(codec); cxt5066_vostro_automic(codec);
else
cxt5066_automic(codec);
} }
return 0; return 0;
} }
static int cxt5066_olpc_init(struct hda_codec *codec)
{
snd_printdd("CXT5066: init\n");
conexant_init(codec);
cxt5066_hp_automute(codec);
cxt5066_olpc_automic(codec);
return 0;
}
enum { enum {
CXT5066_LAPTOP, /* Laptops w/ EAPD support */ CXT5066_LAPTOP, /* Laptops w/ EAPD support */
CXT5066_DELL_LAPTOP, /* Dell Laptop */ CXT5066_DELL_LAPTOP, /* Dell Laptop */
...@@ -2521,7 +2561,7 @@ static int patch_cxt5066(struct hda_codec *codec) ...@@ -2521,7 +2561,7 @@ static int patch_cxt5066(struct hda_codec *codec)
codec->spec = spec; codec->spec = spec;
codec->patch_ops = conexant_patch_ops; codec->patch_ops = conexant_patch_ops;
codec->patch_ops.init = cxt5066_init; codec->patch_ops.init = conexant_init;
spec->dell_automute = 0; spec->dell_automute = 0;
spec->multiout.max_channels = 2; spec->multiout.max_channels = 2;
...@@ -2534,7 +2574,6 @@ static int patch_cxt5066(struct hda_codec *codec) ...@@ -2534,7 +2574,6 @@ static int patch_cxt5066(struct hda_codec *codec)
spec->input_mux = &cxt5066_capture_source; spec->input_mux = &cxt5066_capture_source;
spec->port_d_mode = PIN_HP; spec->port_d_mode = PIN_HP;
spec->ext_mic_bias = PIN_VREF80;
spec->num_init_verbs = 1; spec->num_init_verbs = 1;
spec->init_verbs[0] = cxt5066_init_verbs; spec->init_verbs[0] = cxt5066_init_verbs;
...@@ -2561,20 +2600,26 @@ static int patch_cxt5066(struct hda_codec *codec) ...@@ -2561,20 +2600,26 @@ static int patch_cxt5066(struct hda_codec *codec)
spec->dell_automute = 1; spec->dell_automute = 1;
break; break;
case CXT5066_OLPC_XO_1_5: case CXT5066_OLPC_XO_1_5:
codec->patch_ops.unsol_event = cxt5066_unsol_event; 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->init_verbs[0] = cxt5066_init_verbs_olpc;
spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
spec->mixers[spec->num_mixers++] = cxt5066_mixers; spec->mixers[spec->num_mixers++] = cxt5066_mixers;
spec->port_d_mode = 0; spec->port_d_mode = 0;
spec->ext_mic_bias = CXT5066_OLPC_EXT_MIC_BIAS;
/* no S/PDIF out */ /* no S/PDIF out */
spec->multiout.dig_out_nid = 0; spec->multiout.dig_out_nid = 0;
/* input source automatically selected */ /* input source automatically selected */
spec->input_mux = NULL; 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; break;
case CXT5066_DELL_VOSTO: case CXT5066_DELL_VOSTO:
codec->patch_ops.init = cxt5066_init;
codec->patch_ops.unsol_event = cxt5066_vostro_event; codec->patch_ops.unsol_event = cxt5066_vostro_event;
spec->init_verbs[0] = cxt5066_init_verbs_vostro; spec->init_verbs[0] = cxt5066_init_verbs_vostro;
spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
......
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