Skip to content

Commit

Permalink
ALSA: hda - Implement independent HP control
Browse files Browse the repository at this point in the history
Similar like the implementation in patch_analog.c and patch_via.c,
the generic parser can provide the independent HP PCM stream now.
It's enabled when spec->indep_hp is set by the caller while parsing.

Currently no dynamic PCM switching as in patch_via.c is implemented
yet.  The control returns -EBUSY when the value is changed during PCM
operations.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
  • Loading branch information
Takashi Iwai committed Jan 12, 2013
1 parent b3a8c74 commit 38cf6f1
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 1 deletion.
130 changes: 129 additions & 1 deletion sound/pci/hda/hda_generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec)
snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
mutex_init(&spec->pcm_mutex);
return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init);
Expand Down Expand Up @@ -1484,6 +1485,79 @@ static int create_speaker_out_ctls(struct hda_codec *codec)
"Speaker");
}

/*
* independent HP controls
*/

static int indep_hp_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
}

static int indep_hp_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_gen_spec *spec = codec->spec;
ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled;
return 0;
}

static int indep_hp_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_gen_spec *spec = codec->spec;
unsigned int select = ucontrol->value.enumerated.item[0];
int ret = 0;

mutex_lock(&spec->pcm_mutex);
if (spec->active_streams) {
ret = -EBUSY;
goto unlock;
}

if (spec->indep_hp_enabled != select) {
spec->indep_hp_enabled = select;
if (spec->indep_hp_enabled)
spec->multiout.hp_out_nid[0] = 0;
else
spec->multiout.hp_out_nid[0] = spec->alt_dac_nid;
ret = 1;
}
unlock:
mutex_unlock(&spec->pcm_mutex);
return ret;
}

static const struct snd_kcontrol_new indep_hp_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Independent HP",
.info = indep_hp_info,
.get = indep_hp_get,
.put = indep_hp_put,
};


static int create_indep_hp_ctls(struct hda_codec *codec)
{
struct hda_gen_spec *spec = codec->spec;

if (!spec->indep_hp)
return 0;
if (!spec->multiout.hp_out_nid[0]) {
spec->indep_hp = 0;
return 0;
}

spec->indep_hp_enabled = false;
spec->alt_dac_nid = spec->multiout.hp_out_nid[0];
if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl))
return -ENOMEM;
return 0;
}

/*
* channel mode enum control
*/
Expand Down Expand Up @@ -2903,6 +2977,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
if (err < 0)
return err;
err = create_speaker_out_ctls(codec);
if (err < 0)
return err;
err = create_indep_hp_ctls(codec);
if (err < 0)
return err;
err = create_shared_input(codec);
Expand Down Expand Up @@ -3057,8 +3134,16 @@ static int playback_pcm_open(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct hda_gen_spec *spec = codec->spec;
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
int err;

mutex_lock(&spec->pcm_mutex);
err = snd_hda_multi_out_analog_open(codec,
&spec->multiout, substream,
hinfo);
if (!err)
spec->active_streams |= 1 << STREAM_MULTI_OUT;
mutex_unlock(&spec->pcm_mutex);
return err;
}

static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Expand All @@ -3080,6 +3165,44 @@ static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
}

static int playback_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct hda_gen_spec *spec = codec->spec;
mutex_lock(&spec->pcm_mutex);
spec->active_streams &= ~(1 << STREAM_MULTI_OUT);
mutex_unlock(&spec->pcm_mutex);
return 0;
}

static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct hda_gen_spec *spec = codec->spec;
int err = 0;

mutex_lock(&spec->pcm_mutex);
if (!spec->indep_hp_enabled)
err = -EBUSY;
else
spec->active_streams |= 1 << STREAM_INDEP_HP;
mutex_unlock(&spec->pcm_mutex);
return err;
}

static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct hda_gen_spec *spec = codec->spec;
mutex_lock(&spec->pcm_mutex);
spec->active_streams &= ~(1 << STREAM_INDEP_HP);
mutex_unlock(&spec->pcm_mutex);
return 0;
}

/*
* Digital out
*/
Expand Down Expand Up @@ -3154,6 +3277,7 @@ static const struct hda_pcm_stream pcm_analog_playback = {
/* NID is set in build_pcms */
.ops = {
.open = playback_pcm_open,
.close = playback_pcm_close,
.prepare = playback_pcm_prepare,
.cleanup = playback_pcm_cleanup
},
Expand All @@ -3171,6 +3295,10 @@ static const struct hda_pcm_stream pcm_analog_alt_playback = {
.channels_min = 2,
.channels_max = 2,
/* NID is set in build_pcms */
.ops = {
.open = alt_playback_pcm_open,
.close = alt_playback_pcm_close
},
};

static const struct hda_pcm_stream pcm_analog_alt_capture = {
Expand Down
9 changes: 9 additions & 0 deletions sound/pci/hda/hda_generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ struct automic_entry {
unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */
};

/* active stream id */
enum { STREAM_MULTI_OUT, STREAM_INDEP_HP };

struct hda_gen_spec {
char stream_name_analog[32]; /* analog PCM stream */
const struct hda_pcm_stream *stream_analog_playback;
Expand All @@ -76,6 +79,10 @@ struct hda_gen_spec {
const struct hda_pcm_stream *stream_digital_playback;
const struct hda_pcm_stream *stream_digital_capture;

/* PCM */
unsigned int active_streams;
struct mutex pcm_mutex;

/* playback */
struct hda_multi_out multiout; /* playback set-up
* max_channels, dacs must be set
Expand Down Expand Up @@ -150,6 +157,8 @@ struct hda_gen_spec {
unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
unsigned int own_eapd_ctl:1; /* set EAPD by own function */
unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */
unsigned int indep_hp:1; /* independent HP supported */
unsigned int indep_hp_enabled:1; /* independent HP enabled */

/* for virtual master */
hda_nid_t vmaster_nid;
Expand Down

0 comments on commit 38cf6f1

Please sign in to comment.