Commit 34588709 authored by Raymond Yau's avatar Raymond Yau Committed by Takashi Iwai

ALSA: HDA - Add Independent Headphone for all models of ad1988/ad1989

- Add "AD198x Headphone" playback device for independent headphone playback
  while playing 7.1 surround using rear panel audio jacks.

- Remove "6stack-dig-fp" model since "Headphone Playback Volume" control using
  DAC0 instead of DAC1 (HDA_FRONT) was already added to all models.

- Add "Independent HP" switch to enable/disable this playback device.
  When the switch is OFF, headphone use "copy front" mode to get the front
  channel as the green jack.
  When the switch is ON, you can play stereo sound through "AD198x Headphone"
  device to headphone while playing 7.1 surround sound through "AD198x Analog"
  device.
  The switch cannot be changed when either "AD198x Headphone" or "AD198X Analog"
  is open.
Signed-off-by: default avatarRaymond Yau <superquad.vortex2@gmail.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 0b6c49b5
...@@ -48,6 +48,8 @@ struct ad198x_spec { ...@@ -48,6 +48,8 @@ struct ad198x_spec {
const hda_nid_t *alt_dac_nid; const hda_nid_t *alt_dac_nid;
const struct hda_pcm_stream *stream_analog_alt_playback; const struct hda_pcm_stream *stream_analog_alt_playback;
int independent_hp;
int num_active_streams;
/* capture */ /* capture */
unsigned int num_adc_nids; unsigned int num_adc_nids;
...@@ -302,6 +304,72 @@ static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid) ...@@ -302,6 +304,72 @@ static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
} }
#endif #endif
static void activate_ctl(struct hda_codec *codec, const char *name, int active)
{
struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
if (ctl) {
ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
ctl->vd[0].access |= active ? 0 :
SNDRV_CTL_ELEM_ACCESS_INACTIVE;
ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE;
ctl->vd[0].access |= active ?
SNDRV_CTL_ELEM_ACCESS_WRITE : 0;
snd_ctl_notify(codec->bus->card,
SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
}
}
static void set_stream_active(struct hda_codec *codec, bool active)
{
struct ad198x_spec *spec = codec->spec;
if (active)
spec->num_active_streams++;
else
spec->num_active_streams--;
activate_ctl(codec, "Independent HP", spec->num_active_streams == 0);
}
static int ad1988_independent_hp_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char * const texts[] = { "OFF", "ON", NULL};
int index;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
index = uinfo->value.enumerated.item;
if (index >= 2)
index = 1;
strcpy(uinfo->value.enumerated.name, texts[index]);
return 0;
}
static int ad1988_independent_hp_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
ucontrol->value.enumerated.item[0] = spec->independent_hp;
return 0;
}
static int ad1988_independent_hp_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
unsigned int select = ucontrol->value.enumerated.item[0];
if (spec->independent_hp != select) {
spec->independent_hp = select;
if (spec->independent_hp)
spec->multiout.hp_nid = 0;
else
spec->multiout.hp_nid = spec->alt_dac_nid[0];
return 1;
}
return 0;
}
/* /*
* Analog playback callbacks * Analog playback callbacks
*/ */
...@@ -310,8 +378,15 @@ static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo, ...@@ -310,8 +378,15 @@ static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream) struct snd_pcm_substream *substream)
{ {
struct ad198x_spec *spec = codec->spec; struct ad198x_spec *spec = codec->spec;
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, int err;
set_stream_active(codec, true);
err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
hinfo); hinfo);
if (err < 0) {
set_stream_active(codec, false);
return err;
}
return 0;
} }
static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo, static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
...@@ -333,11 +408,41 @@ static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, ...@@ -333,11 +408,41 @@ static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
} }
static int ad198x_playback_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
set_stream_active(codec, false);
return 0;
}
static int ad1988_alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct ad198x_spec *spec = codec->spec;
if (!spec->independent_hp)
return -EBUSY;
set_stream_active(codec, true);
return 0;
}
static int ad1988_alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
set_stream_active(codec, false);
return 0;
}
static const struct hda_pcm_stream ad198x_pcm_analog_alt_playback = { static const struct hda_pcm_stream ad198x_pcm_analog_alt_playback = {
.substreams = 1, .substreams = 1,
.channels_min = 2, .channels_min = 2,
.channels_max = 2, .channels_max = 2,
/* NID is set in ad198x_build_pcms */ .ops = {
.open = ad1988_alt_playback_pcm_open,
.close = ad1988_alt_playback_pcm_close
},
}; };
/* /*
...@@ -402,7 +507,6 @@ static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, ...@@ -402,7 +507,6 @@ static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
return 0; return 0;
} }
/* /*
*/ */
static const struct hda_pcm_stream ad198x_pcm_analog_playback = { static const struct hda_pcm_stream ad198x_pcm_analog_playback = {
...@@ -413,7 +517,8 @@ static const struct hda_pcm_stream ad198x_pcm_analog_playback = { ...@@ -413,7 +517,8 @@ static const struct hda_pcm_stream ad198x_pcm_analog_playback = {
.ops = { .ops = {
.open = ad198x_playback_pcm_open, .open = ad198x_playback_pcm_open,
.prepare = ad198x_playback_pcm_prepare, .prepare = ad198x_playback_pcm_prepare,
.cleanup = ad198x_playback_pcm_cleanup .cleanup = ad198x_playback_pcm_cleanup,
.close = ad198x_playback_pcm_close
}, },
}; };
...@@ -2058,7 +2163,6 @@ static int patch_ad1981(struct hda_codec *codec) ...@@ -2058,7 +2163,6 @@ static int patch_ad1981(struct hda_codec *codec)
enum { enum {
AD1988_6STACK, AD1988_6STACK,
AD1988_6STACK_DIG, AD1988_6STACK_DIG,
AD1988_6STACK_DIG_FP,
AD1988_3STACK, AD1988_3STACK,
AD1988_3STACK_DIG, AD1988_3STACK_DIG,
AD1988_LAPTOP, AD1988_LAPTOP,
...@@ -2168,6 +2272,17 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol, ...@@ -2168,6 +2272,17 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
return err; return err;
} }
static const struct snd_kcontrol_new ad1988_hp_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Independent HP",
.info = ad1988_independent_hp_info,
.get = ad1988_independent_hp_get,
.put = ad1988_independent_hp_put,
},
{ } /* end */
};
/* 6-stack mode */ /* 6-stack mode */
static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = { static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
...@@ -2211,7 +2326,6 @@ static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = { ...@@ -2211,7 +2326,6 @@ static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
{ } /* end */ { } /* end */
}; };
...@@ -3147,7 +3261,6 @@ static int ad1988_auto_init(struct hda_codec *codec) ...@@ -3147,7 +3261,6 @@ static int ad1988_auto_init(struct hda_codec *codec)
static const char * const ad1988_models[AD1988_MODEL_LAST] = { static const char * const ad1988_models[AD1988_MODEL_LAST] = {
[AD1988_6STACK] = "6stack", [AD1988_6STACK] = "6stack",
[AD1988_6STACK_DIG] = "6stack-dig", [AD1988_6STACK_DIG] = "6stack-dig",
[AD1988_6STACK_DIG_FP] = "6stack-dig-fp",
[AD1988_3STACK] = "3stack", [AD1988_3STACK] = "3stack",
[AD1988_3STACK_DIG] = "3stack-dig", [AD1988_3STACK_DIG] = "3stack-dig",
[AD1988_LAPTOP] = "laptop", [AD1988_LAPTOP] = "laptop",
...@@ -3206,11 +3319,10 @@ static int patch_ad1988(struct hda_codec *codec) ...@@ -3206,11 +3319,10 @@ static int patch_ad1988(struct hda_codec *codec)
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
if (!spec->multiout.hp_nid) if (!spec->multiout.hp_nid)
spec->multiout.hp_nid = 0x03; spec->multiout.hp_nid = ad1988_alt_dac_nid[0];
switch (board_config) { switch (board_config) {
case AD1988_6STACK: case AD1988_6STACK:
case AD1988_6STACK_DIG: case AD1988_6STACK_DIG:
case AD1988_6STACK_DIG_FP:
spec->multiout.max_channels = 8; spec->multiout.max_channels = 8;
spec->multiout.num_dacs = 4; spec->multiout.num_dacs = 4;
if (is_rev2(codec)) if (is_rev2(codec))
...@@ -3226,16 +3338,7 @@ static int patch_ad1988(struct hda_codec *codec) ...@@ -3226,16 +3338,7 @@ static int patch_ad1988(struct hda_codec *codec)
spec->mixers[1] = ad1988_6stack_mixers2; spec->mixers[1] = ad1988_6stack_mixers2;
spec->num_init_verbs = 1; spec->num_init_verbs = 1;
spec->init_verbs[0] = ad1988_6stack_init_verbs; spec->init_verbs[0] = ad1988_6stack_init_verbs;
if (board_config == AD1988_6STACK_DIG_FP) { if (board_config == AD1988_6STACK_DIG) {
spec->multiout.hp_nid = 0;
spec->slave_vols = ad1988_6stack_fp_slave_vols;
spec->slave_sws = ad1988_6stack_fp_slave_sws;
spec->alt_dac_nid = ad1988_alt_dac_nid;
spec->stream_analog_alt_playback =
&ad198x_pcm_analog_alt_playback;
}
if ((board_config == AD1988_6STACK_DIG) ||
(board_config == AD1988_6STACK_DIG_FP)) {
spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
spec->dig_in_nid = AD1988_SPDIF_IN; spec->dig_in_nid = AD1988_SPDIF_IN;
} }
...@@ -3278,6 +3381,15 @@ static int patch_ad1988(struct hda_codec *codec) ...@@ -3278,6 +3381,15 @@ static int patch_ad1988(struct hda_codec *codec)
break; break;
} }
if (spec->autocfg.hp_pins[0]) {
spec->mixers[spec->num_mixers++] = ad1988_hp_mixers;
spec->slave_vols = ad1988_6stack_fp_slave_vols;
spec->slave_sws = ad1988_6stack_fp_slave_sws;
spec->alt_dac_nid = ad1988_alt_dac_nid;
spec->stream_analog_alt_playback =
&ad198x_pcm_analog_alt_playback;
}
spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids); spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
spec->adc_nids = ad1988_adc_nids; spec->adc_nids = ad1988_adc_nids;
spec->capsrc_nids = ad1988_capsrc_nids; spec->capsrc_nids = ad1988_capsrc_nids;
......
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