|
@@ -53,13 +53,15 @@
|
|
|
/* dapm power sequences - make this per codec in the future */
|
|
|
static int dapm_up_seq[] = {
|
|
|
snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic,
|
|
|
- snd_soc_dapm_mux, snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_pga,
|
|
|
- snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post
|
|
|
+ snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_dac,
|
|
|
+ snd_soc_dapm_mixer, snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp,
|
|
|
+ snd_soc_dapm_spk, snd_soc_dapm_post
|
|
|
};
|
|
|
static int dapm_down_seq[] = {
|
|
|
snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
|
|
|
snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic,
|
|
|
- snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_post
|
|
|
+ snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_value_mux,
|
|
|
+ snd_soc_dapm_post
|
|
|
};
|
|
|
|
|
|
static int dapm_status = 1;
|
|
@@ -134,6 +136,25 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
|
|
|
}
|
|
|
}
|
|
|
break;
|
|
|
+ case snd_soc_dapm_value_mux: {
|
|
|
+ struct soc_value_enum *e = (struct soc_value_enum *)
|
|
|
+ w->kcontrols[i].private_value;
|
|
|
+ int val, item;
|
|
|
+
|
|
|
+ val = snd_soc_read(w->codec, e->reg);
|
|
|
+ val = (val >> e->shift_l) & e->mask;
|
|
|
+ for (item = 0; item < e->max; item++) {
|
|
|
+ if (val == e->values[item])
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ p->connect = 0;
|
|
|
+ for (i = 0; i < e->max; i++) {
|
|
|
+ if (!(strcmp(p->name, e->texts[i])) && item == i)
|
|
|
+ p->connect = 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
/* does not effect routing - always connected */
|
|
|
case snd_soc_dapm_pga:
|
|
|
case snd_soc_dapm_output:
|
|
@@ -179,6 +200,30 @@ static int dapm_connect_mux(struct snd_soc_codec *codec,
|
|
|
return -ENODEV;
|
|
|
}
|
|
|
|
|
|
+/* connect value_mux widget to it's interconnecting audio paths */
|
|
|
+static int dapm_connect_value_mux(struct snd_soc_codec *codec,
|
|
|
+ struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
|
|
|
+ struct snd_soc_dapm_path *path, const char *control_name,
|
|
|
+ const struct snd_kcontrol_new *kcontrol)
|
|
|
+{
|
|
|
+ struct soc_value_enum *e = (struct soc_value_enum *)
|
|
|
+ kcontrol->private_value;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < e->max; i++) {
|
|
|
+ if (!(strcmp(control_name, e->texts[i]))) {
|
|
|
+ list_add(&path->list, &codec->dapm_paths);
|
|
|
+ list_add(&path->list_sink, &dest->sources);
|
|
|
+ list_add(&path->list_source, &src->sinks);
|
|
|
+ path->name = (char *)e->texts[i];
|
|
|
+ dapm_set_path_status(dest, path, 0);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return -ENODEV;
|
|
|
+}
|
|
|
+
|
|
|
/* connect mixer widget to it's interconnecting audio paths */
|
|
|
static int dapm_connect_mixer(struct snd_soc_codec *codec,
|
|
|
struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
|
|
@@ -653,6 +698,7 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
|
|
|
case snd_soc_dapm_vmid:
|
|
|
continue;
|
|
|
case snd_soc_dapm_mux:
|
|
|
+ case snd_soc_dapm_value_mux:
|
|
|
case snd_soc_dapm_output:
|
|
|
case snd_soc_dapm_input:
|
|
|
case snd_soc_dapm_switch:
|
|
@@ -728,6 +774,45 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* test and update the power status of a value_mux widget */
|
|
|
+static int dapm_value_mux_update_power(struct snd_soc_dapm_widget *widget,
|
|
|
+ struct snd_kcontrol *kcontrol, int mask,
|
|
|
+ int mux, int val, struct soc_value_enum *e)
|
|
|
+{
|
|
|
+ struct snd_soc_dapm_path *path;
|
|
|
+ int found = 0;
|
|
|
+
|
|
|
+ if (widget->id != snd_soc_dapm_value_mux)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* find dapm widget path assoc with kcontrol */
|
|
|
+ list_for_each_entry(path, &widget->codec->dapm_paths, list) {
|
|
|
+ if (path->kcontrol != kcontrol)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!path->name || !e->texts[mux])
|
|
|
+ continue;
|
|
|
+
|
|
|
+ found = 1;
|
|
|
+ /* we now need to match the string in the enum to the path */
|
|
|
+ if (!(strcmp(path->name, e->texts[mux])))
|
|
|
+ path->connect = 1; /* new connection */
|
|
|
+ else
|
|
|
+ path->connect = 0; /* old connection must be
|
|
|
+ powered down */
|
|
|
+ }
|
|
|
+
|
|
|
+ if (found) {
|
|
|
+ dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
|
|
|
+ dump_dapm(widget->codec, "mux power update");
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/* test and update the power status of a mixer or switch widget */
|
|
|
static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
|
|
|
struct snd_kcontrol *kcontrol, int reg,
|
|
@@ -965,6 +1050,12 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
|
|
|
if (ret != 0)
|
|
|
goto err;
|
|
|
break;
|
|
|
+ case snd_soc_dapm_value_mux:
|
|
|
+ ret = dapm_connect_value_mux(codec, wsource, wsink, path,
|
|
|
+ control, &wsink->kcontrols[0]);
|
|
|
+ if (ret != 0)
|
|
|
+ goto err;
|
|
|
+ break;
|
|
|
case snd_soc_dapm_switch:
|
|
|
case snd_soc_dapm_mixer:
|
|
|
ret = dapm_connect_mixer(codec, wsource, wsink, path, control);
|
|
@@ -1047,6 +1138,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
|
|
|
dapm_new_mixer(codec, w);
|
|
|
break;
|
|
|
case snd_soc_dapm_mux:
|
|
|
+ case snd_soc_dapm_value_mux:
|
|
|
dapm_new_mux(codec, w);
|
|
|
break;
|
|
|
case snd_soc_dapm_adc:
|
|
@@ -1273,6 +1365,105 @@ out:
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
|
|
|
|
|
|
+/**
|
|
|
+ * snd_soc_dapm_get_value_enum_double - dapm semi enumerated double mixer get
|
|
|
+ * callback
|
|
|
+ * @kcontrol: mixer control
|
|
|
+ * @ucontrol: control element information
|
|
|
+ *
|
|
|
+ * Callback to get the value of a dapm semi enumerated double mixer control.
|
|
|
+ *
|
|
|
+ * Semi enumerated mixer: the enumerated items are referred as values. Can be
|
|
|
+ * used for handling bitfield coded enumeration for example.
|
|
|
+ *
|
|
|
+ * Returns 0 for success.
|
|
|
+ */
|
|
|
+int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
|
|
|
+ struct soc_value_enum *e = (struct soc_value_enum *)
|
|
|
+ kcontrol->private_value;
|
|
|
+ unsigned short reg_val, val, mux;
|
|
|
+
|
|
|
+ reg_val = snd_soc_read(widget->codec, e->reg);
|
|
|
+ val = (reg_val >> e->shift_l) & e->mask;
|
|
|
+ for (mux = 0; mux < e->max; mux++) {
|
|
|
+ if (val == e->values[mux])
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ ucontrol->value.enumerated.item[0] = mux;
|
|
|
+ if (e->shift_l != e->shift_r) {
|
|
|
+ val = (reg_val >> e->shift_r) & e->mask;
|
|
|
+ for (mux = 0; mux < e->max; mux++) {
|
|
|
+ if (val == e->values[mux])
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ ucontrol->value.enumerated.item[1] = mux;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double);
|
|
|
+
|
|
|
+/**
|
|
|
+ * snd_soc_dapm_put_value_enum_double - dapm semi enumerated double mixer set
|
|
|
+ * callback
|
|
|
+ * @kcontrol: mixer control
|
|
|
+ * @ucontrol: control element information
|
|
|
+ *
|
|
|
+ * Callback to set the value of a dapm semi enumerated double mixer control.
|
|
|
+ *
|
|
|
+ * Semi enumerated mixer: the enumerated items are referred as values. Can be
|
|
|
+ * used for handling bitfield coded enumeration for example.
|
|
|
+ *
|
|
|
+ * Returns 0 for success.
|
|
|
+ */
|
|
|
+int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
|
|
|
+ struct soc_value_enum *e = (struct soc_value_enum *)
|
|
|
+ kcontrol->private_value;
|
|
|
+ unsigned short val, mux;
|
|
|
+ unsigned short mask;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (ucontrol->value.enumerated.item[0] > e->max - 1)
|
|
|
+ return -EINVAL;
|
|
|
+ mux = ucontrol->value.enumerated.item[0];
|
|
|
+ val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l;
|
|
|
+ mask = e->mask << e->shift_l;
|
|
|
+ if (e->shift_l != e->shift_r) {
|
|
|
+ if (ucontrol->value.enumerated.item[1] > e->max - 1)
|
|
|
+ return -EINVAL;
|
|
|
+ val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r;
|
|
|
+ mask |= e->mask << e->shift_r;
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_lock(&widget->codec->mutex);
|
|
|
+ widget->value = val;
|
|
|
+ dapm_value_mux_update_power(widget, kcontrol, mask, mux, val, e);
|
|
|
+ if (widget->event) {
|
|
|
+ if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
|
|
|
+ ret = widget->event(widget,
|
|
|
+ kcontrol, SND_SOC_DAPM_PRE_REG);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
|
|
|
+ if (widget->event_flags & SND_SOC_DAPM_POST_REG)
|
|
|
+ ret = widget->event(widget,
|
|
|
+ kcontrol, SND_SOC_DAPM_POST_REG);
|
|
|
+ } else
|
|
|
+ ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
|
|
|
+
|
|
|
+out:
|
|
|
+ mutex_unlock(&widget->codec->mutex);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
|
|
|
+
|
|
|
/**
|
|
|
* snd_soc_dapm_new_control - create new dapm control
|
|
|
* @codec: audio codec
|