Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 38cf6f1a authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: hda - Implement independent HP control



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: default avatarTakashi Iwai <tiwai@suse.de>
parent b3a8c745
Loading
Loading
Loading
Loading
+129 −1
Original line number Diff line number Diff line
@@ -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);
@@ -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
 */
@@ -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);
@@ -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,
@@ -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
 */
@@ -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
	},
@@ -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 = {
+9 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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;