|
@@ -47,6 +47,15 @@
|
|
|
|
|
|
#define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++;
|
|
|
|
|
|
+static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
|
|
|
+ struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
|
|
|
+ const char *control,
|
|
|
+ int (*connected)(struct snd_soc_dapm_widget *source,
|
|
|
+ struct snd_soc_dapm_widget *sink));
|
|
|
+static struct snd_soc_dapm_widget *
|
|
|
+snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
|
|
|
+ const struct snd_soc_dapm_widget *widget);
|
|
|
+
|
|
|
/* dapm power sequences - make this per codec in the future */
|
|
|
static int dapm_up_seq[] = {
|
|
|
[snd_soc_dapm_pre] = 0,
|
|
@@ -73,16 +82,18 @@ static int dapm_up_seq[] = {
|
|
|
[snd_soc_dapm_hp] = 10,
|
|
|
[snd_soc_dapm_spk] = 10,
|
|
|
[snd_soc_dapm_line] = 10,
|
|
|
- [snd_soc_dapm_post] = 11,
|
|
|
+ [snd_soc_dapm_kcontrol] = 11,
|
|
|
+ [snd_soc_dapm_post] = 12,
|
|
|
};
|
|
|
|
|
|
static int dapm_down_seq[] = {
|
|
|
[snd_soc_dapm_pre] = 0,
|
|
|
- [snd_soc_dapm_adc] = 1,
|
|
|
- [snd_soc_dapm_hp] = 2,
|
|
|
- [snd_soc_dapm_spk] = 2,
|
|
|
- [snd_soc_dapm_line] = 2,
|
|
|
- [snd_soc_dapm_out_drv] = 2,
|
|
|
+ [snd_soc_dapm_kcontrol] = 1,
|
|
|
+ [snd_soc_dapm_adc] = 2,
|
|
|
+ [snd_soc_dapm_hp] = 3,
|
|
|
+ [snd_soc_dapm_spk] = 3,
|
|
|
+ [snd_soc_dapm_line] = 3,
|
|
|
+ [snd_soc_dapm_out_drv] = 3,
|
|
|
[snd_soc_dapm_pga] = 4,
|
|
|
[snd_soc_dapm_switch] = 5,
|
|
|
[snd_soc_dapm_mixer_named_ctl] = 5,
|
|
@@ -176,6 +187,7 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
|
|
|
|
|
|
struct dapm_kcontrol_data {
|
|
|
unsigned int value;
|
|
|
+ struct snd_soc_dapm_widget *widget;
|
|
|
struct list_head paths;
|
|
|
struct snd_soc_dapm_widget_list *wlist;
|
|
|
};
|
|
@@ -184,6 +196,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
|
|
|
struct snd_kcontrol *kcontrol)
|
|
|
{
|
|
|
struct dapm_kcontrol_data *data;
|
|
|
+ struct soc_mixer_control *mc;
|
|
|
|
|
|
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
|
|
if (!data) {
|
|
@@ -195,6 +208,39 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
|
|
|
|
|
|
INIT_LIST_HEAD(&data->paths);
|
|
|
|
|
|
+ switch (widget->id) {
|
|
|
+ case snd_soc_dapm_switch:
|
|
|
+ case snd_soc_dapm_mixer:
|
|
|
+ case snd_soc_dapm_mixer_named_ctl:
|
|
|
+ mc = (struct soc_mixer_control *)kcontrol->private_value;
|
|
|
+
|
|
|
+ if (mc->autodisable) {
|
|
|
+ struct snd_soc_dapm_widget template;
|
|
|
+
|
|
|
+ memset(&template, 0, sizeof(template));
|
|
|
+ template.reg = mc->reg;
|
|
|
+ template.mask = (1 << fls(mc->max)) - 1;
|
|
|
+ template.shift = mc->shift;
|
|
|
+ if (mc->invert)
|
|
|
+ template.off_val = mc->max;
|
|
|
+ else
|
|
|
+ template.off_val = 0;
|
|
|
+ template.on_val = template.off_val;
|
|
|
+ template.id = snd_soc_dapm_kcontrol;
|
|
|
+ template.name = kcontrol->id.name;
|
|
|
+
|
|
|
+ data->widget = snd_soc_dapm_new_control(widget->dapm,
|
|
|
+ &template);
|
|
|
+ if (!data->widget) {
|
|
|
+ kfree(data);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
kcontrol->private_data = data;
|
|
|
|
|
|
return 0;
|
|
@@ -203,6 +249,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
|
|
|
static void dapm_kcontrol_free(struct snd_kcontrol *kctl)
|
|
|
{
|
|
|
struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl);
|
|
|
+ kfree(data->widget);
|
|
|
kfree(data->wlist);
|
|
|
kfree(data);
|
|
|
}
|
|
@@ -246,6 +293,21 @@ static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol,
|
|
|
struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
|
|
|
|
|
|
list_add_tail(&path->list_kcontrol, &data->paths);
|
|
|
+
|
|
|
+ if (data->widget) {
|
|
|
+ snd_soc_dapm_add_path(data->widget->dapm, data->widget,
|
|
|
+ path->source, NULL, NULL);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static bool dapm_kcontrol_is_powered(const struct snd_kcontrol *kcontrol)
|
|
|
+{
|
|
|
+ struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
|
|
|
+
|
|
|
+ if (!data->widget)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ return data->widget->power;
|
|
|
}
|
|
|
|
|
|
static struct list_head *dapm_kcontrol_get_path_list(
|
|
@@ -275,6 +337,9 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
|
|
|
if (data->value == value)
|
|
|
return false;
|
|
|
|
|
|
+ if (data->widget)
|
|
|
+ data->widget->on_val = value;
|
|
|
+
|
|
|
data->value = value;
|
|
|
|
|
|
return true;
|
|
@@ -515,6 +580,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
|
|
|
case snd_soc_dapm_spk:
|
|
|
case snd_soc_dapm_line:
|
|
|
case snd_soc_dapm_dai_link:
|
|
|
+ case snd_soc_dapm_kcontrol:
|
|
|
p->connect = 1;
|
|
|
break;
|
|
|
/* does affect routing - dynamically connected */
|
|
@@ -880,6 +946,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
|
|
|
case snd_soc_dapm_supply:
|
|
|
case snd_soc_dapm_regulator_supply:
|
|
|
case snd_soc_dapm_clock_supply:
|
|
|
+ case snd_soc_dapm_kcontrol:
|
|
|
return 0;
|
|
|
default:
|
|
|
break;
|
|
@@ -975,6 +1042,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
|
|
|
case snd_soc_dapm_supply:
|
|
|
case snd_soc_dapm_regulator_supply:
|
|
|
case snd_soc_dapm_clock_supply:
|
|
|
+ case snd_soc_dapm_kcontrol:
|
|
|
return 0;
|
|
|
default:
|
|
|
break;
|
|
@@ -1523,7 +1591,7 @@ static void dapm_widget_update(struct snd_soc_card *card)
|
|
|
unsigned int wi;
|
|
|
int ret;
|
|
|
|
|
|
- if (!update)
|
|
|
+ if (!update || !dapm_kcontrol_is_powered(update->kcontrol))
|
|
|
return;
|
|
|
|
|
|
wlist = dapm_kcontrol_get_wlist(update->kcontrol);
|
|
@@ -1668,6 +1736,7 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
|
|
|
case snd_soc_dapm_supply:
|
|
|
case snd_soc_dapm_regulator_supply:
|
|
|
case snd_soc_dapm_clock_supply:
|
|
|
+ case snd_soc_dapm_kcontrol:
|
|
|
/* Supplies can't affect their outputs, only their inputs */
|
|
|
break;
|
|
|
default:
|
|
@@ -2335,6 +2404,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
|
|
|
case snd_soc_dapm_dai_in:
|
|
|
case snd_soc_dapm_dai_out:
|
|
|
case snd_soc_dapm_dai_link:
|
|
|
+ case snd_soc_dapm_kcontrol:
|
|
|
list_add(&path->list, &dapm->card->paths);
|
|
|
list_add(&path->list_sink, &wsink->sources);
|
|
|
list_add(&path->list_source, &wsource->sinks);
|
|
@@ -2717,6 +2787,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
{
|
|
|
struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
|
|
|
+ struct snd_soc_card *card = codec->card;
|
|
|
struct soc_mixer_control *mc =
|
|
|
(struct soc_mixer_control *)kcontrol->private_value;
|
|
|
unsigned int reg = mc->reg;
|
|
@@ -2724,17 +2795,24 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
|
|
|
int max = mc->max;
|
|
|
unsigned int mask = (1 << fls(max)) - 1;
|
|
|
unsigned int invert = mc->invert;
|
|
|
+ unsigned int val;
|
|
|
|
|
|
if (snd_soc_volsw_is_stereo(mc))
|
|
|
dev_warn(codec->dapm.dev,
|
|
|
"ASoC: Control '%s' is stereo, which is not supported\n",
|
|
|
kcontrol->id.name);
|
|
|
|
|
|
- ucontrol->value.integer.value[0] =
|
|
|
- (snd_soc_read(codec, reg) >> shift) & mask;
|
|
|
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
|
|
|
+ if (dapm_kcontrol_is_powered(kcontrol))
|
|
|
+ val = (snd_soc_read(codec, reg) >> shift) & mask;
|
|
|
+ else
|
|
|
+ val = dapm_kcontrol_get_value(kcontrol);
|
|
|
+ mutex_unlock(&card->dapm_mutex);
|
|
|
+
|
|
|
if (invert)
|
|
|
- ucontrol->value.integer.value[0] =
|
|
|
- max - ucontrol->value.integer.value[0];
|
|
|
+ ucontrol->value.integer.value[0] = max - val;
|
|
|
+ else
|
|
|
+ ucontrol->value.integer.value[0] = val;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -2775,11 +2853,14 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
|
|
|
|
|
|
if (invert)
|
|
|
val = max - val;
|
|
|
- mask = mask << shift;
|
|
|
- val = val << shift;
|
|
|
|
|
|
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
|
|
|
|
|
|
+ dapm_kcontrol_set_value(kcontrol, val);
|
|
|
+
|
|
|
+ mask = mask << shift;
|
|
|
+ val = val << shift;
|
|
|
+
|
|
|
change = snd_soc_test_bits(codec, reg, mask, val);
|
|
|
if (change) {
|
|
|
update.kcontrol = kcontrol;
|
|
@@ -3179,6 +3260,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
|
|
|
case snd_soc_dapm_supply:
|
|
|
case snd_soc_dapm_regulator_supply:
|
|
|
case snd_soc_dapm_clock_supply:
|
|
|
+ case snd_soc_dapm_kcontrol:
|
|
|
w->power_check = dapm_supply_check_power;
|
|
|
break;
|
|
|
default:
|