@@ -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;
@@ -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;
}
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 <nikesh@opensource.wolfsonmicro.com> --- include/sound/soc-dapm.h | 7 +- sound/soc/soc-dapm.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 1 deletion(-)