Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 182654
b: refs/heads/master
c: c4cfe66
h: refs/heads/master
v: v3
  • Loading branch information
Daniel Drake authored and Jaroslav Kysela committed Jan 8, 2010
1 parent 59352ee commit 1ce1be2
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 24 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 75f8991d0e6969407d51501d5a0537f104075c99
refs/heads/master: c4cfe66c4c2d5a91b3734ffb4e2bad0badd5c874
213 changes: 190 additions & 23 deletions trunk/sound/pci/hda/patch_conexant.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,16 @@ struct conexant_spec {
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 */
};

static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
Expand Down Expand Up @@ -2024,6 +2034,26 @@ 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_codec_write_cache(codec, 0x1a, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
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
Expand All @@ -2034,6 +2064,27 @@ static void cxt5066_olpc_select_mic(struct hda_codec *codec)
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_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0);

/* external mic, port B */
snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
Expand All @@ -2049,6 +2100,9 @@ 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)
Expand Down Expand Up @@ -2123,13 +2177,16 @@ static void cxt5066_hp_automute(struct hda_codec *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:
cxt5066_olpc_automic(codec);
/* ignore mic events in DC mode; we're always using the jack */
if (!spec->dc_enable)
cxt5066_olpc_automic(codec);
break;
}
}
Expand Down Expand Up @@ -2159,6 +2216,15 @@ static const struct hda_input_mux cxt5066_analog_mic_boost = {
},
};

static int cxt5066_set_mic_boost(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
return snd_hda_codec_write_cache(codec, 0x17, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
cxt5066_analog_mic_boost.items[spec->mic_boost].index);
}

static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
Expand All @@ -2169,39 +2235,110 @@ static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
int val;
hda_nid_t nid = kcontrol->private_value & 0xff;
int inout = (kcontrol->private_value & 0x100) ?
AC_AMP_GET_INPUT : AC_AMP_GET_OUTPUT;

val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_AMP_GAIN_MUTE, inout);

ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN;
struct conexant_spec *spec = codec->spec;
ucontrol->value.enumerated.item[0] = spec->mic_boost;
return 0;
}

static int cxt5066_mic_boost_mux_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;
hda_nid_t nid = kcontrol->private_value & 0xff;
int inout = (kcontrol->private_value & 0x100) ?
AC_AMP_SET_INPUT : AC_AMP_SET_OUTPUT;
idx = ucontrol->value.enumerated.item[0];
if (idx >= imux->num_items)
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;
}

if (!imux->num_items)
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;

snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | inout |
imux->items[idx].index);

spec->dc_input_bias = idx;
if (spec->dc_enable)
cxt5066_set_olpc_dc_bias(codec);
return 1;
}

Expand All @@ -2223,6 +2360,9 @@ static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec)

/* 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},
{},
};

Expand Down Expand Up @@ -2282,6 +2422,24 @@ static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = {
{}
};

static 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 struct snd_kcontrol_new cxt5066_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Expand All @@ -2294,11 +2452,10 @@ static struct snd_kcontrol_new cxt5066_mixers[] = {

{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Ext Mic Boost Capture Enum",
.name = "Analog Mic Boost Capture Enum",
.info = cxt5066_mic_boost_mux_enum_info,
.get = cxt5066_mic_boost_mux_enum_get,
.put = cxt5066_mic_boost_mux_enum_put,
.private_value = 0x17,
},

HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others),
Expand Down Expand Up @@ -2392,7 +2549,7 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = {
{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */

/* Port F: unused */
/* Port F: external DC input through microphone port */
{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},

/* Port G: internal speakers */
Expand Down Expand Up @@ -2513,15 +2670,22 @@ static int cxt5066_init(struct hda_codec *codec)
if (spec->dell_vostro)
cxt5066_vostro_automic(codec);
}
cxt5066_set_mic_boost(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);
cxt5066_olpc_automic(codec);
if (!spec->dc_enable) {
cxt5066_set_mic_boost(codec);
cxt5066_olpc_automic(codec);
} else {
cxt5066_enable_dc(codec);
}
return 0;
}

Expand Down Expand Up @@ -2604,8 +2768,10 @@ static int patch_cxt5066(struct hda_codec *codec)
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;
Expand All @@ -2627,6 +2793,7 @@ static int patch_cxt5066(struct hda_codec *codec)
spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers;
spec->port_d_mode = 0;
spec->dell_vostro = 1;
spec->mic_boost = 3; /* default 30dB gain */
snd_hda_attach_beep_device(codec, 0x13);

/* no S/PDIF out */
Expand Down

0 comments on commit 1ce1be2

Please sign in to comment.