[RFC,08/11] ASoC: hdac: Register widget event handlers
diff mbox

Message ID 1466999284-14782-9-git-send-email-subhransu.s.prusty@intel.com
State New
Headers show

Commit Message

Subhransu S. Prusty June 27, 2016, 3:48 a.m. UTC
Register the event handlers and program the codec by sending
required verbs in the event handlers.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 sound/soc/codecs/hdac_generic.c | 212 ++++++++++++++++++++++++++++++++++++++--
 sound/soc/codecs/hdac_generic.h |   3 +
 2 files changed, 209 insertions(+), 6 deletions(-)

Patch
diff mbox

diff --git a/sound/soc/codecs/hdac_generic.c b/sound/soc/codecs/hdac_generic.c
index a2084aa..32f8736 100644
--- a/sound/soc/codecs/hdac_generic.c
+++ b/sound/soc/codecs/hdac_generic.c
@@ -69,6 +69,188 @@  static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
 	return to_ehdac_device(hdac);
 }
 
+static void hdac_generic_set_power_state(struct hdac_ext_device *edev,
+			hda_nid_t nid, unsigned int pwr_state)
+{
+	/* TODO: check D0sup bit before setting this */
+	if (!snd_hdac_check_power_state(&edev->hdac, nid, pwr_state))
+		snd_hdac_regmap_write(&edev->hdac, nid,
+				AC_VERB_SET_POWER_STATE, pwr_state);
+}
+
+static int hdac_generic_pin_io_event(struct snd_soc_dapm_widget *w,
+			struct snd_kcontrol *kc, int event)
+{
+	struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev);
+	struct hdac_codec_widget *wid = w->priv;
+
+	snd_hdac_codec_write(&edev->hdac, wid->nid, 0,
+			AC_VERB_SET_PIN_WIDGET_CONTROL,
+			w->id == snd_soc_dapm_input ?
+			AC_PINCTL_IN_EN : AC_PINCTL_OUT_EN);
+
+	return 0;
+}
+
+static int hdac_generic_pin_mux_event(struct snd_soc_dapm_widget *w,
+			struct snd_kcontrol *kc, int event)
+{
+	struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev);
+	struct hdac_codec_widget *wid = w->priv;
+	int mux_idx;
+
+	if (!kc)
+		kc  = w->kcontrols[0];
+
+	mux_idx = dapm_kcontrol_get_value(kc);
+
+	snd_hdac_codec_write(&edev->hdac, wid->nid, 0,
+			AC_VERB_SET_CONNECT_SEL, mux_idx);
+
+	return 0;
+}
+
+static int hdac_generic_pin_pga_event(struct snd_soc_dapm_widget *w,
+			struct snd_kcontrol *kc, int event)
+{
+	struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev);
+	struct hdac_codec_widget *wid = w->priv;
+
+	hdac_generic_set_power_state(edev, wid->nid,
+		(event == SND_SOC_DAPM_PRE_PMU ? AC_PWRST_D0:AC_PWRST_D3));
+
+	snd_hdac_codec_write(&edev->hdac, wid->nid, 0,
+			AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+
+	return 0;
+}
+
+static int hdac_generic_widget_power_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kc, int event)
+{
+	struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev);
+	struct hdac_codec_widget *wid = w->priv;
+
+	hdac_generic_set_power_state(edev, wid->nid,
+		(event == SND_SOC_DAPM_PRE_PMU ? AC_PWRST_D0:AC_PWRST_D3));
+
+	return 0;
+}
+
+static int get_mixer_control_index(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *kc)
+{
+	int i;
+
+	for (i = 0; i < w->num_kcontrols; i++) {
+		if (w->kcontrols[i] == kc)
+			return i;
+	}
+
+	return -1;
+}
+
+static int hdac_generic_mixer_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kc, int event)
+{
+	struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev);
+	struct hdac_codec_widget *wid = w->priv;
+	bool no_input = true;
+	int i;
+
+	if (event == SND_SOC_DAPM_POST_REG) {
+		i = get_mixer_control_index(w, kc);
+		if (i == -1) {
+			dev_err(&edev->hdac.dev, "%s: Wrong kcontrol event: %s\n",
+							__func__, kc->id.name);
+			return -EINVAL;
+		}
+		if (dapm_kcontrol_get_value(kc)) {
+			snd_hdac_regmap_write(&edev->hdac, wid->nid,
+				AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(i));
+			no_input = false;
+		} else {
+			snd_hdac_regmap_write(&edev->hdac, wid->nid,
+				AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(i));
+		}
+
+		if (no_input)
+			snd_hdac_regmap_write(&edev->hdac, wid->nid,
+				AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+		return 0;
+	}
+
+	hdac_generic_set_power_state(edev, wid->nid,
+		(event == SND_SOC_DAPM_PRE_PMU ? AC_PWRST_D0:AC_PWRST_D3));
+
+	/* TODO: Check capability and program amp */
+	snd_hdac_regmap_write(&edev->hdac, wid->nid, AC_VERB_SET_AMP_GAIN_MUTE,
+							AMP_OUT_UNMUTE);
+
+	for (i = 0; i < w->num_kcontrols; i++) {
+		if (dapm_kcontrol_get_value(w->kcontrols[i])) {
+			snd_hdac_regmap_write(&edev->hdac, wid->nid,
+				AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(i));
+		}
+	}
+
+	return 0;
+}
+
+static void update_mux_input_amp_mute(struct hdac_ext_device *edev,
+		hda_nid_t nid, struct snd_kcontrol *kc)
+{
+	bool no_input = true;
+	struct soc_enum *e = (struct soc_enum *)kc->private_value;
+	int mux_idx, i;
+
+	mux_idx = dapm_kcontrol_get_value(kc);
+
+	for (i = 0; i < (e->items - 1); i++) {
+		if (i == mux_idx) {
+			snd_hdac_regmap_write(&edev->hdac, nid,
+				AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(i));
+
+			no_input = false;
+		} else {
+			snd_hdac_regmap_write(&edev->hdac, nid,
+				AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(i));
+		}
+	}
+
+	if (no_input)
+		snd_hdac_regmap_write(&edev->hdac, nid,
+			AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+}
+
+static int hdac_generic_selector_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kc, int event)
+{
+	struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev);
+	struct hdac_codec_widget *wid = w->priv;
+
+	if (event == SND_SOC_DAPM_POST_REG) {
+		/* TODO: Check capability and program amp */
+		update_mux_input_amp_mute(edev, wid->nid, kc);
+
+		return 0;
+	}
+
+	hdac_generic_set_power_state(edev, wid->nid,
+		(event == SND_SOC_DAPM_PRE_PMU ? AC_PWRST_D0:AC_PWRST_D3));
+
+	snd_hdac_regmap_write(&edev->hdac, wid->nid, AC_VERB_SET_CONNECT_SEL,
+				dapm_kcontrol_get_value(w->kcontrols[0]));
+
+	snd_hdac_regmap_write(&edev->hdac, wid->nid, AC_VERB_SET_AMP_GAIN_MUTE,
+							AMP_OUT_UNMUTE);
+	/* TODO: Check capability and program amp */
+	update_mux_input_amp_mute(edev, wid->nid, w->kcontrols[0]);
+
+	return 0;
+}
+
 static bool is_duplicate_route(struct list_head *route_list,
 		const char *sink, const char *control, const char *src)
 {
@@ -469,7 +651,8 @@  static int hdac_generic_alloc_mux_widget(struct snd_soc_dapm_context *dapm,
 
 	ret = hdac_generic_fill_widget_info(dapm->dev, &widgets[index],
 			snd_soc_dapm_mux, wid, widget_name, NULL, kc, 1,
-			NULL, 0);
+			hdac_generic_selector_event, SND_SOC_DAPM_PRE_PMU |
+			SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_REG);
 
 	if (ret < 0)
 		return ret;
@@ -523,7 +706,9 @@  static int hdac_codec_alloc_cvt_widget(struct snd_soc_dapm_context *dapm,
 	ret = hdac_generic_fill_widget_info(dapm->dev, &widgets[index],
 			wid->type == AC_WID_AUD_IN ?
 			snd_soc_dapm_aif_in : snd_soc_dapm_aif_out,
-			wid, widget_name, dai_strm_name, NULL, 0, NULL, 0);
+			wid, widget_name, dai_strm_name, NULL, 0,
+			hdac_generic_widget_power_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
 	if (ret < 0)
 		return ret;
 
@@ -578,7 +763,9 @@  static int hdac_codec_alloc_mixer_widget(struct snd_soc_dapm_context *dapm,
 	sprintf(widget_name, "Mixer %x", wid->nid);
 	ret = hdac_generic_fill_widget_info(dapm->dev, &w[index],
 			snd_soc_dapm_mixer, wid, widget_name, NULL,
-			kc, wid->num_inputs, NULL, 0);
+			kc, wid->num_inputs, hdac_generic_mixer_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD |
+			SND_SOC_DAPM_POST_REG);
 	if (ret < 0)
 		return ret;
 
@@ -627,7 +814,9 @@  static int hdac_codec_alloc_pin_widget(struct snd_soc_dapm_context *dapm,
 
 	ret = hdac_generic_fill_widget_info(dapm->dev, &widgets[i],
 			input ? snd_soc_dapm_input : snd_soc_dapm_output,
-			wid, widget_name, NULL, NULL, 0, NULL, 0);
+			wid, widget_name, NULL, NULL, 0,
+			hdac_generic_pin_io_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
 	if (ret < 0)
 		return ret;
 
@@ -637,7 +826,8 @@  static int hdac_codec_alloc_pin_widget(struct snd_soc_dapm_context *dapm,
 	sprintf(widget_name, "Pin %x PGA", wid->nid);
 	ret = hdac_generic_fill_widget_info(dapm->dev, &widgets[i],
 			snd_soc_dapm_pga, wid, widget_name, NULL,
-			NULL, 0, NULL, 0);
+			NULL, 0, hdac_generic_pin_pga_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
 	if (ret < 0)
 		return ret;
 
@@ -649,6 +839,15 @@  static int hdac_codec_alloc_pin_widget(struct snd_soc_dapm_context *dapm,
 		ret = hdac_generic_alloc_mux_widget(dapm, widgets, i, wid);
 		if (ret < 0)
 			return ret;
+		/*
+		 * Pin mux will not use generic selector handler, so
+		 * override. Also mux widget create will increment the
+		 * index, so assign the previous widget.
+		 */
+		widgets[i].event_flags = SND_SOC_DAPM_PRE_PMU |
+						SND_SOC_DAPM_POST_PMD |
+						SND_SOC_DAPM_POST_REG;
+		widgets[i].event = hdac_generic_pin_mux_event;
 
 		wid_ref[2] = &widgets[i++];
 	}
@@ -670,7 +869,8 @@  static int hdac_codec_alloc_power_widget(struct snd_soc_dapm_context *dapm,
 	sprintf(widget_name, "Power %x", wid->nid);
 	ret = hdac_generic_fill_widget_info(dapm->dev, &widgets[index],
 			snd_soc_dapm_supply, wid, widget_name,
-			NULL, NULL, 0, NULL, 0);
+			NULL, NULL, 0, hdac_generic_widget_power_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
 	if (ret < 0)
 		return ret;
 
diff --git a/sound/soc/codecs/hdac_generic.h b/sound/soc/codecs/hdac_generic.h
index 5a4cd36..c52dc7c 100644
--- a/sound/soc/codecs/hdac_generic.h
+++ b/sound/soc/codecs/hdac_generic.h
@@ -21,5 +21,8 @@ 
 #define __HDAC_GENERIC_H__
 
 #define HDAC_GENERIC_NAME_SIZE	32
+#define AMP_OUT_MUTE		0xb080
+#define AMP_OUT_UNMUTE		0xb000
+#define PIN_OUT			(AC_PINCTL_OUT_EN)
 
 #endif /* __HDAC_GENERIC_H__ */