From patchwork Tue Sep 8 10:14:09 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: nikesh@opensource.wolfsonmicro.com X-Patchwork-Id: 7139751 Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 64CCE9F1D5 for ; Tue, 8 Sep 2015 10:14:33 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 4A27A20706 for ; Tue, 8 Sep 2015 10:14:32 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id CD84B206F4 for ; Tue, 8 Sep 2015 10:14:30 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id A589F26501B; Tue, 8 Sep 2015 12:14:29 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,NO_DNS_FOR_FROM, RCVD_IN_DNSWL_LOW, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id 7C21826066A; Tue, 8 Sep 2015 12:14:27 +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 7D491264FFF; Tue, 8 Sep 2015 12:14:26 +0200 (CEST) Received: from opensource.wolfsonmicro.com (opensource.wolfsonmicro.com [80.75.67.52]) by alsa0.perex.cz (Postfix) with ESMTP id 53A9A260666 for ; Tue, 8 Sep 2015 12:14:19 +0200 (CEST) Received: from nikesh-Latitude-E6410.wolfsonmicro.main (axios.lumison.net [212.20.245.98]) by opensource.wolfsonmicro.com (Postfix) with ESMTPSA id 54B073B40FE; Tue, 8 Sep 2015 11:14:18 +0100 (BST) From: Nikesh Oswal To: broonie@kernel.org, lgirdwood@gmail.com Date: Tue, 8 Sep 2015 11:14:09 +0100 Message-Id: <1441707249-11259-1-git-send-email-nikesh@opensource.wolfsonmicro.com> X-Mailer: git-send-email 1.7.9.5 Cc: alsa-devel@alsa-project.org, tiwai@suse.de, patches@opensource.wolfsonmicro.com, Nikesh.Oswal@cirrus.com, linux-kernel@vger.kernel.org Subject: [alsa-devel] [PATCH] ASoC: dapm: add kcontrol to switch regulator to regulated/bypass state 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 When regulator is defined with SND_SOC_DAPM_REGULATOR_CONTROL_BYPASS flag, then a kcontrol will be created which can be used to switch regulator to regulated/bypass state. This will help to control the behaviour of the regulator based on a usecase. For example voice call may need a regulated voltage to acheive higher quality whereas voice trigger may need bypass voltage so as to save on power. Signed-off-by: Nikesh Oswal --- include/sound/soc-dapm.h | 7 +- sound/soc/soc-dapm.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 1 deletion(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 15717b4..778b847 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -343,7 +343,12 @@ struct device; (e & (SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD)) /* regulator widget flags */ -#define SND_SOC_DAPM_REGULATOR_BYPASS 0x1 /* bypass when disabled */ +/* bypass when disabled and regulated when enabled */ +#define SND_SOC_DAPM_REGULATOR_BYPASS 0x1 +/* bypass when disabled and regulated when enable by default and a + kcontrol is created to explicitly switch between bypass/regulated */ +#define SND_SOC_DAPM_REGULATOR_CONTROL_BYPASS \ + (SND_SOC_DAPM_REGULATOR_BYPASS | 0x2) struct snd_soc_dapm_widget; enum snd_soc_dapm_type; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 36ab9cb..2d77eb9 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -965,6 +965,180 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) return 0; } +static int snd_soc_dapm_regulator_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_card *card = widget->dapm->card; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int new_val, val; + int ret; + bool bypass; + + if (item[0] >= e->items) + return -EINVAL; + + val = dapm_kcontrol_get_value(kcontrol); + new_val = item[0] == 0 ? SND_SOC_DAPM_REGULATOR_BYPASS : 0; + bypass = new_val == SND_SOC_DAPM_REGULATOR_BYPASS ? false : true; + + if (new_val != val) { + mutex_lock_nested(&card->dapm_mutex, + SND_SOC_DAPM_CLASS_RUNTIME); + if (regulator_is_enabled(widget->regulator)) { + ret = regulator_allow_bypass(widget->regulator, bypass); + if (ret != 0) + dev_warn(widget->dapm->dev, + "ASoC: Failed to change bypass %s: %d\n", + widget->name, ret); + } + dapm_kcontrol_set_value(kcontrol, new_val); + widget->on_val = new_val; + mutex_unlock(&card->dapm_mutex); + } + + return 0; +} + +static int snd_soc_dapm_regulator_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int val; + + val = dapm_kcontrol_get_value(kcontrol); + + if (val == SND_SOC_DAPM_REGULATOR_BYPASS) + ucontrol->value.enumerated.item[0] = 0; + else + ucontrol->value.enumerated.item[0] = 1; + + return 0; +} + +static const char * const dapm_regulator_texts[] = { + "Regulated", + "Bypass", +}; + +/* create new dapm regulator control */ +static int dapm_new_regulator(struct snd_soc_dapm_widget *w) +{ + int ret = 0; + struct snd_soc_card *card = w->dapm->card; + unsigned long private_value; + struct snd_kcontrol *kcontrol; + struct snd_soc_dapm_path *path; + struct soc_enum regulator_enum[] = { + SOC_ENUM_SINGLE_VIRT(ARRAY_SIZE(dapm_regulator_texts), + dapm_regulator_texts), + }; + struct snd_kcontrol_new kcontrol_regulator[] = { + SOC_ENUM_EXT(NULL, regulator_enum[0], + snd_soc_dapm_regulator_get, + snd_soc_dapm_regulator_put), + }; + + + /* kcontrol creation is done only if client requests it */ + if (w->on_val != SND_SOC_DAPM_REGULATOR_CONTROL_BYPASS) + return 0; + + + /* create a kcontrol only if somebody is sourcing + from this regulator widget */ + if (list_empty(&w->edges[SND_SOC_DAPM_DIR_IN])) { + dev_err(w->dapm->dev, "ASoC: %s has no sinks\n", w->name); + return -EINVAL; + } + + w->num_kcontrols = 1; + + private_value = (unsigned long) devm_kmemdup(card->dev, + (void *)(kcontrol_regulator[0].private_value), + sizeof(struct soc_enum), GFP_KERNEL); + if (!private_value) { + dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", + w->name); + ret = -ENOMEM; + goto err_out; + } + + kcontrol_regulator[0].private_value = private_value; + + w->kcontrol_news = devm_kmemdup(card->dev, &kcontrol_regulator[0], + sizeof(struct snd_kcontrol_new), GFP_KERNEL); + if (!(w->kcontrol_news)) { + dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", + w->name); + ret = -ENOMEM; + goto err_private; + } + + + kcontrol = snd_soc_cnew(&w->kcontrol_news[0], NULL, + w->name, NULL); + + if (!kcontrol) { + ret = -ENOMEM; + goto err_kcontrol_news; + } + + kcontrol->private_free = dapm_kcontrol_free; + + ret = dapm_kcontrol_data_alloc(w, kcontrol); + if (ret) + goto err_kcontrol; + + + ret = snd_ctl_add(card->snd_card, kcontrol); + if (ret < 0) { + dev_err(w->dapm->dev, + "ASoC: failed to add widget %s dapm kcontrol %s: %d\n", + w->name, w->kcontrol_news[0].name, ret); + goto err_kcontrol; + } + + ret = dapm_kcontrol_add_widget(kcontrol, w); + if (ret) + goto err_kcontrol; + + + /* change on_val to remove the kcontrol creation bit + as kcontrol is already created */ + w->on_val = SND_SOC_DAPM_REGULATOR_BYPASS; + /* update the kcontrol value to reflect the initial value */ + dapm_kcontrol_set_value(kcontrol, w->on_val); + + w->kcontrols = kzalloc(w->num_kcontrols * + sizeof(struct snd_kcontrol *), + GFP_KERNEL); + if (!w->kcontrols) { + ret = -ENOMEM; + goto err_kcontrol; + } + + w->kcontrols[0] = kcontrol; + + snd_soc_dapm_widget_for_each_path(w, SND_SOC_DAPM_DIR_IN, path) { + if (path->name) + dapm_kcontrol_add_path(w->kcontrols[0], path); + } + + return 0; + +err_kcontrol: + snd_ctl_free_one(kcontrol); +err_kcontrol_news: + devm_kfree(card->dev, (void *)w->kcontrol_news); +err_private: + devm_kfree(card->dev, (void *)private_value); +err_out: + return ret; +} + /* create new dapm volume control */ static int dapm_new_pga(struct snd_soc_dapm_widget *w) { @@ -2922,6 +3096,9 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) case snd_soc_dapm_dai_link: dapm_new_dai_link(w); break; + case snd_soc_dapm_regulator_supply: + dapm_new_regulator(w); + break; default: break; }