From patchwork Mon Aug 29 06:23:22 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Subhransu S. Prusty" X-Patchwork-Id: 9304211 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 7368A607F0 for ; Mon, 29 Aug 2016 17:38:41 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 67290288A2 for ; Mon, 29 Aug 2016 17:38:41 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5BCED288CD; Mon, 29 Aug 2016 17:38:41 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 05F0F288A2 for ; Mon, 29 Aug 2016 17:38:40 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 9361626778D; Mon, 29 Aug 2016 19:38:38 +0200 (CEST) Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id 72ED72678BD; Mon, 29 Aug 2016 18:43:02 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 782D22678BC; Mon, 29 Aug 2016 18:43:00 +0200 (CEST) Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by alsa0.perex.cz (Postfix) with ESMTP id CE1C9266E4C for ; Mon, 29 Aug 2016 18:23:43 +0200 (CEST) Received: from orsmga004.jf.intel.com ([10.7.209.38]) by orsmga102.jf.intel.com with ESMTP; 28 Aug 2016 23:29:19 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.28,595,1464678000"; d="scan'208";a="2203653" Received: from subhransu-desktop.iind.intel.com ([10.223.96.24]) by orsmga004.jf.intel.com with ESMTP; 28 Aug 2016 23:29:17 -0700 From: "Subhransu S. Prusty" To: alsa-devel@alsa-project.org Date: Mon, 29 Aug 2016 11:53:22 +0530 Message-Id: <1472451806-10605-9-git-send-email-subhransu.s.prusty@intel.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1472451806-10605-1-git-send-email-subhransu.s.prusty@intel.com> References: <1472451806-10605-1-git-send-email-subhransu.s.prusty@intel.com> Cc: tiwai@suse.de, lgirdwood@gmail.com, patches.audio@intel.com, broonie@kernel.org, Vinod Koul , "Subhransu S. Prusty" Subject: [alsa-devel] [PATCH 08/12] ASoC: hdac: Register widget event handlers X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP Register the event handlers and program the codec by sending required verbs in the event handlers. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul --- sound/soc/codecs/hdac_generic.c | 329 +++++++++++++++++++++++++++++++++++++++- sound/soc/codecs/hdac_generic.h | 3 + 2 files changed, 326 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/hdac_generic.c b/sound/soc/codecs/hdac_generic.c index 6bd7b2b..354e81d 100644 --- a/sound/soc/codecs/hdac_generic.c +++ b/sound/soc/codecs/hdac_generic.c @@ -75,6 +75,305 @@ static void hdac_generic_set_power_state(struct hdac_ext_device *edev, AC_VERB_SET_POWER_STATE, pwr_state); } +/* TODO: Add feature to program by querying capability */ +static void hdac_generic_set_eapd(struct hdac_ext_device *edev, + hda_nid_t nid, bool enable) +{ + u32 pin_caps = snd_hdac_read_parm_uncached(&edev->hdac, + nid, AC_PAR_PIN_CAP); + + if (pin_caps & AC_PINCAP_EAPD) + snd_hdac_codec_write(&edev->hdac, nid, 0, + AC_VERB_SET_EAPD_BTLENABLE, enable ? 2 : 0); +} + +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; + int val; + + dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n", + __func__, w->name, event); + + val = snd_hdac_codec_read(&edev->hdac, wid->nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (w->id == snd_soc_dapm_output) { + /* TODO: program using a eapd widget */ + hdac_generic_set_eapd(edev, wid->nid, true); + val |= AC_PINCTL_OUT_EN; + } else { + + /* TODO: program vref using a mixer control */ + val |= AC_PINCTL_VREF_80; + val |= AC_PINCTL_IN_EN; + } + + break; + + case SND_SOC_DAPM_POST_PMD: + if (w->id == snd_soc_dapm_output) { + /* TODO: program using a eapd widget */ + hdac_generic_set_eapd(edev, wid->nid, false); + val &= ~AC_PINCTL_OUT_EN; + } else { + val &= AC_PINCTL_VREF_HIZ; + val &= ~AC_PINCTL_IN_EN; + } + + break; + + default: + dev_warn(&edev->hdac.dev, "Event %d not handled\n", event); + return 0; + } + + snd_hdac_codec_write(&edev->hdac, wid->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, val); + + 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; + + dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n", + __func__, w->name, event); + if (!kc) + kc = w->kcontrols[0]; + + mux_idx = dapm_kcontrol_get_value(kc); + if (mux_idx > 0) { + snd_hdac_codec_write(&edev->hdac, wid->nid, 0, + AC_VERB_SET_CONNECT_SEL, (mux_idx - 1)); + } + + 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; + + dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n", + __func__, w->name, event); + + if (event == SND_SOC_DAPM_POST_PMD) { + hdac_generic_set_power_state(edev, wid->nid, AC_PWRST_D3); + snd_hdac_codec_write(&edev->hdac, wid->nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + } else { + hdac_generic_set_power_state(edev, wid->nid, AC_PWRST_D0); + 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; + + dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n", + __func__, w->name, event); + hdac_generic_set_power_state(edev, wid->nid, + (event == SND_SOC_DAPM_POST_PMD ? AC_PWRST_D3:AC_PWRST_D0)); + + return 0; +} + +static int hdac_generic_cvt_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_widget_power_event(w, kc, event); + + if (event == SND_SOC_DAPM_POST_PMD) + snd_hdac_codec_write(&edev->hdac, wid->nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(0)); + else + snd_hdac_codec_write(&edev->hdac, wid->nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0) | 0x5b); + + 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 -EINVAL; +} + +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; + + dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hdac_generic_set_power_state(edev, wid->nid, AC_PWRST_D0); + + + snd_hdac_codec_write(&edev->hdac, wid->nid, 0, + 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_codec_write(&edev->hdac, wid->nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(i)); + } + } + + return 0; + + case SND_SOC_DAPM_POST_PMD: + snd_hdac_codec_write(&edev->hdac, wid->nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + for (i = 0; i < w->num_kcontrols; i++) { + if (dapm_kcontrol_get_value(w->kcontrols[i])) { + snd_hdac_codec_write(&edev->hdac, wid->nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(i)); + } + } + + hdac_generic_set_power_state(edev, wid->nid, AC_PWRST_D3); + + return 0; + + case SND_SOC_DAPM_POST_REG: + i = get_mixer_control_index(w, kc); + if (i < 0) { + dev_err(&edev->hdac.dev, + "%s: Wrong kcontrol event: %s\n", + __func__, kc->id.name); + return i; + } + if (dapm_kcontrol_get_value(kc)) { + snd_hdac_codec_write(&edev->hdac, wid->nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(i)); + no_input = false; + } else { + snd_hdac_codec_write(&edev->hdac, wid->nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(i)); + } + + if (no_input) + snd_hdac_codec_write(&edev->hdac, wid->nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + return 0; + + default: + dev_warn(&edev->hdac.dev, "Event %d not handled\n", event); + return 0; + } + + return 0; +} + +static void update_mux_amp_switch(struct hdac_ext_device *edev, + hda_nid_t nid, struct snd_kcontrol *kc, bool enable) +{ + struct soc_enum *e = (struct soc_enum *)kc->private_value; + int mux_idx, i; + + mux_idx = dapm_kcontrol_get_value(kc); + + if (!enable || !mux_idx) { + for (i = 1; i < (e->items - 1); i++) + snd_hdac_codec_write(&edev->hdac, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(i - 1)); + + snd_hdac_codec_write(&edev->hdac, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + } else { + for (i = 1; i < (e->items - 1); i++) { + if (i == mux_idx) + snd_hdac_codec_write(&edev->hdac, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(i - 1)); + else + snd_hdac_codec_write(&edev->hdac, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(i - 1)); + } + + snd_hdac_codec_write(&edev->hdac, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + } +} + +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; + + dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hdac_generic_set_power_state(edev, wid->nid, AC_PWRST_D0); + snd_hdac_codec_write(&edev->hdac, wid->nid, 0, + AC_VERB_SET_CONNECT_SEL, + (dapm_kcontrol_get_value(w->kcontrols[0]) - 1)); + update_mux_amp_switch(edev, wid->nid, w->kcontrols[0], true); + + return 0; + + case SND_SOC_DAPM_POST_REG: + update_mux_amp_switch(edev, wid->nid, kc, true); + + return 0; + + case SND_SOC_DAPM_POST_PMD: + update_mux_amp_switch(edev, wid->nid, w->kcontrols[0], false); + hdac_generic_set_power_state(edev, wid->nid, AC_PWRST_D3); + + return 0; + + default: + dev_warn(&edev->hdac.dev, "Event %d not handled\n", event); + return 0; + } + + return 0; +} + static bool is_duplicate_route(struct list_head *route_list, const char *sink, const char *control, const char *src) { @@ -480,7 +779,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; @@ -534,7 +834,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_cvt_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD); if (ret < 0) return ret; @@ -587,7 +889,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; @@ -634,7 +938,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; @@ -644,7 +950,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; @@ -656,6 +963,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++]; } @@ -677,7 +993,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 5ca713a..a938b50 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__ */