#include <linux/debugfs.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
[snd_soc_dapm_pre] = 0,
[snd_soc_dapm_supply] = 1,
[snd_soc_dapm_regulator_supply] = 1,
+ [snd_soc_dapm_clock_supply] = 1,
[snd_soc_dapm_micbias] = 2,
[snd_soc_dapm_dai_link] = 2,
[snd_soc_dapm_dai] = 3,
[snd_soc_dapm_aif_out] = 10,
[snd_soc_dapm_dai] = 10,
[snd_soc_dapm_dai_link] = 11,
+ [snd_soc_dapm_clock_supply] = 12,
[snd_soc_dapm_regulator_supply] = 12,
[snd_soc_dapm_supply] = 12,
[snd_soc_dapm_post] = 13,
case snd_soc_dapm_vmid:
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dai:
switch (widget->id) {
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
return 0;
default:
break;
switch (widget->id) {
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
return 0;
default:
break;
}
EXPORT_SYMBOL_GPL(dapm_regulator_event);
+/*
+ * Handler for clock supply widget.
+ */
+int dapm_clock_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (!w->clk)
+ return -EIO;
+
+#ifdef CONFIG_HAVE_CLK
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ return clk_enable(w->clk);
+ } else {
+ clk_disable(w->clk);
+ return 0;
+ }
+#endif
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dapm_clock_event);
+
static int dapm_widget_power_check(struct snd_soc_dapm_widget *w)
{
if (w->power_checked)
switch (w->id) {
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
/* Supplies can't affect their outputs, only their inputs */
break;
default:
}
list_for_each_entry(w, &card->widgets, list) {
- list_del_init(&w->dirty);
+ switch (w->id) {
+ case snd_soc_dapm_pre:
+ case snd_soc_dapm_post:
+ /* These widgets always need to be powered */
+ break;
+ default:
+ list_del_init(&w->dirty);
+ break;
+ }
if (w->power) {
d = w->dapm;
break;
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
case snd_soc_dapm_micbias:
if (d->target_bias_level < SND_SOC_BIAS_STANDBY)
d->target_bias_level = SND_SOC_BIAS_STANDBY;
case snd_soc_dapm_mixer_named_ctl:
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
if (w->name)
count += sprintf(buf + count, "%s: %s\n",
w->name, w->power ? "On":"Off");
case snd_soc_dapm_post:
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dai:
path->connect = 0;
return 0;
}
+
+ dapm_mark_dirty(wsource, "Route added");
+ dapm_mark_dirty(wsink, "Route added");
+
return 0;
err:
return ret;
}
+static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
+ const struct snd_soc_dapm_route *route)
+{
+ struct snd_soc_dapm_path *path, *p;
+ const char *sink;
+ const char *source;
+ char prefixed_sink[80];
+ char prefixed_source[80];
+
+ if (route->control) {
+ dev_err(dapm->dev,
+ "Removal of routes with controls not supported\n");
+ return -EINVAL;
+ }
+
+ if (dapm->codec && dapm->codec->name_prefix) {
+ snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
+ dapm->codec->name_prefix, route->sink);
+ sink = prefixed_sink;
+ snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
+ dapm->codec->name_prefix, route->source);
+ source = prefixed_source;
+ } else {
+ sink = route->sink;
+ source = route->source;
+ }
+
+ path = NULL;
+ list_for_each_entry(p, &dapm->card->paths, list) {
+ if (strcmp(p->source->name, source) != 0)
+ continue;
+ if (strcmp(p->sink->name, sink) != 0)
+ continue;
+ path = p;
+ break;
+ }
+
+ if (path) {
+ dapm_mark_dirty(path->source, "Route removed");
+ dapm_mark_dirty(path->sink, "Route removed");
+
+ list_del(&path->list);
+ list_del(&path->list_sink);
+ list_del(&path->list_source);
+ kfree(path);
+ } else {
+ dev_warn(dapm->dev, "Route %s->%s does not exist\n",
+ source, sink);
+ }
+
+ return 0;
+}
+
/**
* snd_soc_dapm_add_routes - Add routes between DAPM widgets
* @dapm: DAPM context
int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num)
{
- int i, ret = 0;
+ int i, r, ret = 0;
mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
for (i = 0; i < num; i++) {
- ret = snd_soc_dapm_add_route(dapm, route);
- if (ret < 0) {
+ r = snd_soc_dapm_add_route(dapm, route);
+ if (r < 0) {
dev_err(dapm->dev, "Failed to add route %s->%s\n",
route->source, route->sink);
- break;
+ ret = r;
}
route++;
}
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
+/**
+ * snd_soc_dapm_del_routes - Remove routes between DAPM widgets
+ * @dapm: DAPM context
+ * @route: audio routes
+ * @num: number of routes
+ *
+ * Removes routes from the DAPM context.
+ */
+int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,
+ const struct snd_soc_dapm_route *route, int num)
+{
+ int i, ret = 0;
+
+ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
+ for (i = 0; i < num; i++) {
+ snd_soc_dapm_del_route(dapm, route);
+ route++;
+ }
+ mutex_unlock(&dapm->card->dapm_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes);
+
static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route)
{
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
- unsigned int rshift = mc->rshift;
int max = mc->max;
- unsigned int invert = mc->invert;
unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+
+ if (snd_soc_volsw_is_stereo(mc))
+ dev_warn(widget->dapm->dev,
+ "Control '%s' is stereo, which is not supported\n",
+ kcontrol->id.name);
ucontrol->value.integer.value[0] =
(snd_soc_read(widget->codec, reg) >> shift) & mask;
- if (shift != rshift)
- ucontrol->value.integer.value[1] =
- (snd_soc_read(widget->codec, reg) >> rshift) & mask;
- if (invert) {
+ if (invert)
ucontrol->value.integer.value[0] =
max - ucontrol->value.integer.value[0];
- if (shift != rshift)
- ucontrol->value.integer.value[1] =
- max - ucontrol->value.integer.value[1];
- }
return 0;
}
struct snd_soc_dapm_update update;
int wi;
+ if (snd_soc_volsw_is_stereo(mc))
+ dev_warn(widget->dapm->dev,
+ "Control '%s' is stereo, which is not supported\n",
+ kcontrol->id.name);
+
val = (ucontrol->value.integer.value[0] & mask);
+ connect = !!val;
if (invert)
val = max - val;
mask = mask << shift;
val = val << shift;
- if (val)
- /* new connection */
- connect = invert ? 0 : 1;
- else
- /* old connection must be powered down */
- connect = invert ? 1 : 0;
-
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
change = snd_soc_test_bits(widget->codec, reg, mask, val);
return NULL;
}
break;
+ case snd_soc_dapm_clock_supply:
+#ifdef CONFIG_CLKDEV_LOOKUP
+ w->clk = devm_clk_get(dapm->dev, w->name);
+ if (IS_ERR(w->clk)) {
+ ret = PTR_ERR(w->clk);
+ dev_err(dapm->dev, "Failed to request %s: %d\n",
+ w->name, ret);
+ return NULL;
+ }
+#else
+ return NULL;
+#endif
+ break;
default:
break;
}
break;
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
+ case snd_soc_dapm_clock_supply:
w->power_check = dapm_supply_check_power;
break;
case snd_soc_dapm_dai: