diff options
author | Peter Ujfalusi <peter.ujfalusi@nokia.com> | 2009-01-05 02:54:57 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-01-05 12:47:17 -0500 |
commit | 2e72f8e3716bc3bbf4c9b5b987fb5ab3223f60bf (patch) | |
tree | f568500ae1737e5c11376ad9b65d706c9da18874 /sound/soc/soc-dapm.c | |
parent | 796123368871e4a838dc0dfd5dbc3cd8981ef429 (diff) |
ASoC: New enum type: value_enum
This patch introduces a new enum type.
In this enum type each enumerated items referred with a value.
This new enum type can handle enums encoded in bitfield, or any other
weird ways. twl4030 codec has several mux selection register, where the
input/output mux is coded in a bitfield. With the normal enum type this type
of mux can not be handled in a clean way.
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r-- | sound/soc/soc-dapm.c | 197 |
1 files changed, 194 insertions, 3 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 6c79ca6df0bf..ad0d801677c1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -53,13 +53,15 @@ | |||
53 | /* dapm power sequences - make this per codec in the future */ | 53 | /* dapm power sequences - make this per codec in the future */ |
54 | static int dapm_up_seq[] = { | 54 | static int dapm_up_seq[] = { |
55 | snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic, | 55 | snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic, |
56 | snd_soc_dapm_mux, snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_pga, | 56 | snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_dac, |
57 | snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post | 57 | snd_soc_dapm_mixer, snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp, |
58 | snd_soc_dapm_spk, snd_soc_dapm_post | ||
58 | }; | 59 | }; |
59 | static int dapm_down_seq[] = { | 60 | static int dapm_down_seq[] = { |
60 | snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, | 61 | snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, |
61 | snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic, | 62 | snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic, |
62 | snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_post | 63 | snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_value_mux, |
64 | snd_soc_dapm_post | ||
63 | }; | 65 | }; |
64 | 66 | ||
65 | static int dapm_status = 1; | 67 | static int dapm_status = 1; |
@@ -134,6 +136,25 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, | |||
134 | } | 136 | } |
135 | } | 137 | } |
136 | break; | 138 | break; |
139 | case snd_soc_dapm_value_mux: { | ||
140 | struct soc_value_enum *e = (struct soc_value_enum *) | ||
141 | w->kcontrols[i].private_value; | ||
142 | int val, item; | ||
143 | |||
144 | val = snd_soc_read(w->codec, e->reg); | ||
145 | val = (val >> e->shift_l) & e->mask; | ||
146 | for (item = 0; item < e->max; item++) { | ||
147 | if (val == e->values[item]) | ||
148 | break; | ||
149 | } | ||
150 | |||
151 | p->connect = 0; | ||
152 | for (i = 0; i < e->max; i++) { | ||
153 | if (!(strcmp(p->name, e->texts[i])) && item == i) | ||
154 | p->connect = 1; | ||
155 | } | ||
156 | } | ||
157 | break; | ||
137 | /* does not effect routing - always connected */ | 158 | /* does not effect routing - always connected */ |
138 | case snd_soc_dapm_pga: | 159 | case snd_soc_dapm_pga: |
139 | case snd_soc_dapm_output: | 160 | case snd_soc_dapm_output: |
@@ -179,6 +200,30 @@ static int dapm_connect_mux(struct snd_soc_codec *codec, | |||
179 | return -ENODEV; | 200 | return -ENODEV; |
180 | } | 201 | } |
181 | 202 | ||
203 | /* connect value_mux widget to it's interconnecting audio paths */ | ||
204 | static int dapm_connect_value_mux(struct snd_soc_codec *codec, | ||
205 | struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, | ||
206 | struct snd_soc_dapm_path *path, const char *control_name, | ||
207 | const struct snd_kcontrol_new *kcontrol) | ||
208 | { | ||
209 | struct soc_value_enum *e = (struct soc_value_enum *) | ||
210 | kcontrol->private_value; | ||
211 | int i; | ||
212 | |||
213 | for (i = 0; i < e->max; i++) { | ||
214 | if (!(strcmp(control_name, e->texts[i]))) { | ||
215 | list_add(&path->list, &codec->dapm_paths); | ||
216 | list_add(&path->list_sink, &dest->sources); | ||
217 | list_add(&path->list_source, &src->sinks); | ||
218 | path->name = (char *)e->texts[i]; | ||
219 | dapm_set_path_status(dest, path, 0); | ||
220 | return 0; | ||
221 | } | ||
222 | } | ||
223 | |||
224 | return -ENODEV; | ||
225 | } | ||
226 | |||
182 | /* connect mixer widget to it's interconnecting audio paths */ | 227 | /* connect mixer widget to it's interconnecting audio paths */ |
183 | static int dapm_connect_mixer(struct snd_soc_codec *codec, | 228 | static int dapm_connect_mixer(struct snd_soc_codec *codec, |
184 | struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, | 229 | 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) | |||
653 | case snd_soc_dapm_vmid: | 698 | case snd_soc_dapm_vmid: |
654 | continue; | 699 | continue; |
655 | case snd_soc_dapm_mux: | 700 | case snd_soc_dapm_mux: |
701 | case snd_soc_dapm_value_mux: | ||
656 | case snd_soc_dapm_output: | 702 | case snd_soc_dapm_output: |
657 | case snd_soc_dapm_input: | 703 | case snd_soc_dapm_input: |
658 | case snd_soc_dapm_switch: | 704 | case snd_soc_dapm_switch: |
@@ -728,6 +774,45 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, | |||
728 | return 0; | 774 | return 0; |
729 | } | 775 | } |
730 | 776 | ||
777 | /* test and update the power status of a value_mux widget */ | ||
778 | static int dapm_value_mux_update_power(struct snd_soc_dapm_widget *widget, | ||
779 | struct snd_kcontrol *kcontrol, int mask, | ||
780 | int mux, int val, struct soc_value_enum *e) | ||
781 | { | ||
782 | struct snd_soc_dapm_path *path; | ||
783 | int found = 0; | ||
784 | |||
785 | if (widget->id != snd_soc_dapm_value_mux) | ||
786 | return -ENODEV; | ||
787 | |||
788 | if (!snd_soc_test_bits(widget->codec, e->reg, mask, val)) | ||
789 | return 0; | ||
790 | |||
791 | /* find dapm widget path assoc with kcontrol */ | ||
792 | list_for_each_entry(path, &widget->codec->dapm_paths, list) { | ||
793 | if (path->kcontrol != kcontrol) | ||
794 | continue; | ||
795 | |||
796 | if (!path->name || !e->texts[mux]) | ||
797 | continue; | ||
798 | |||
799 | found = 1; | ||
800 | /* we now need to match the string in the enum to the path */ | ||
801 | if (!(strcmp(path->name, e->texts[mux]))) | ||
802 | path->connect = 1; /* new connection */ | ||
803 | else | ||
804 | path->connect = 0; /* old connection must be | ||
805 | powered down */ | ||
806 | } | ||
807 | |||
808 | if (found) { | ||
809 | dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP); | ||
810 | dump_dapm(widget->codec, "mux power update"); | ||
811 | } | ||
812 | |||
813 | return 0; | ||
814 | } | ||
815 | |||
731 | /* test and update the power status of a mixer or switch widget */ | 816 | /* test and update the power status of a mixer or switch widget */ |
732 | static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, | 817 | static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, |
733 | struct snd_kcontrol *kcontrol, int reg, | 818 | struct snd_kcontrol *kcontrol, int reg, |
@@ -965,6 +1050,12 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, | |||
965 | if (ret != 0) | 1050 | if (ret != 0) |
966 | goto err; | 1051 | goto err; |
967 | break; | 1052 | break; |
1053 | case snd_soc_dapm_value_mux: | ||
1054 | ret = dapm_connect_value_mux(codec, wsource, wsink, path, | ||
1055 | control, &wsink->kcontrols[0]); | ||
1056 | if (ret != 0) | ||
1057 | goto err; | ||
1058 | break; | ||
968 | case snd_soc_dapm_switch: | 1059 | case snd_soc_dapm_switch: |
969 | case snd_soc_dapm_mixer: | 1060 | case snd_soc_dapm_mixer: |
970 | ret = dapm_connect_mixer(codec, wsource, wsink, path, control); | 1061 | ret = dapm_connect_mixer(codec, wsource, wsink, path, control); |
@@ -1047,6 +1138,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) | |||
1047 | dapm_new_mixer(codec, w); | 1138 | dapm_new_mixer(codec, w); |
1048 | break; | 1139 | break; |
1049 | case snd_soc_dapm_mux: | 1140 | case snd_soc_dapm_mux: |
1141 | case snd_soc_dapm_value_mux: | ||
1050 | dapm_new_mux(codec, w); | 1142 | dapm_new_mux(codec, w); |
1051 | break; | 1143 | break; |
1052 | case snd_soc_dapm_adc: | 1144 | case snd_soc_dapm_adc: |
@@ -1274,6 +1366,105 @@ out: | |||
1274 | EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); | 1366 | EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); |
1275 | 1367 | ||
1276 | /** | 1368 | /** |
1369 | * snd_soc_dapm_get_value_enum_double - dapm semi enumerated double mixer get | ||
1370 | * callback | ||
1371 | * @kcontrol: mixer control | ||
1372 | * @ucontrol: control element information | ||
1373 | * | ||
1374 | * Callback to get the value of a dapm semi enumerated double mixer control. | ||
1375 | * | ||
1376 | * Semi enumerated mixer: the enumerated items are referred as values. Can be | ||
1377 | * used for handling bitfield coded enumeration for example. | ||
1378 | * | ||
1379 | * Returns 0 for success. | ||
1380 | */ | ||
1381 | int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, | ||
1382 | struct snd_ctl_elem_value *ucontrol) | ||
1383 | { | ||
1384 | struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); | ||
1385 | struct soc_value_enum *e = (struct soc_value_enum *) | ||
1386 | kcontrol->private_value; | ||
1387 | unsigned short reg_val, val, mux; | ||
1388 | |||
1389 | reg_val = snd_soc_read(widget->codec, e->reg); | ||
1390 | val = (reg_val >> e->shift_l) & e->mask; | ||
1391 | for (mux = 0; mux < e->max; mux++) { | ||
1392 | if (val == e->values[mux]) | ||
1393 | break; | ||
1394 | } | ||
1395 | ucontrol->value.enumerated.item[0] = mux; | ||
1396 | if (e->shift_l != e->shift_r) { | ||
1397 | val = (reg_val >> e->shift_r) & e->mask; | ||
1398 | for (mux = 0; mux < e->max; mux++) { | ||
1399 | if (val == e->values[mux]) | ||
1400 | break; | ||
1401 | } | ||
1402 | ucontrol->value.enumerated.item[1] = mux; | ||
1403 | } | ||
1404 | |||
1405 | return 0; | ||
1406 | } | ||
1407 | EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double); | ||
1408 | |||
1409 | /** | ||
1410 | * snd_soc_dapm_put_value_enum_double - dapm semi enumerated double mixer set | ||
1411 | * callback | ||
1412 | * @kcontrol: mixer control | ||
1413 | * @ucontrol: control element information | ||
1414 | * | ||
1415 | * Callback to set the value of a dapm semi enumerated double mixer control. | ||
1416 | * | ||
1417 | * Semi enumerated mixer: the enumerated items are referred as values. Can be | ||
1418 | * used for handling bitfield coded enumeration for example. | ||
1419 | * | ||
1420 | * Returns 0 for success. | ||
1421 | */ | ||
1422 | int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, | ||
1423 | struct snd_ctl_elem_value *ucontrol) | ||
1424 | { | ||
1425 | struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); | ||
1426 | struct soc_value_enum *e = (struct soc_value_enum *) | ||
1427 | kcontrol->private_value; | ||
1428 | unsigned short val, mux; | ||
1429 | unsigned short mask; | ||
1430 | int ret = 0; | ||
1431 | |||
1432 | if (ucontrol->value.enumerated.item[0] > e->max - 1) | ||
1433 | return -EINVAL; | ||
1434 | mux = ucontrol->value.enumerated.item[0]; | ||
1435 | val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l; | ||
1436 | mask = e->mask << e->shift_l; | ||
1437 | if (e->shift_l != e->shift_r) { | ||
1438 | if (ucontrol->value.enumerated.item[1] > e->max - 1) | ||
1439 | return -EINVAL; | ||
1440 | val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r; | ||
1441 | mask |= e->mask << e->shift_r; | ||
1442 | } | ||
1443 | |||
1444 | mutex_lock(&widget->codec->mutex); | ||
1445 | widget->value = val; | ||
1446 | dapm_value_mux_update_power(widget, kcontrol, mask, mux, val, e); | ||
1447 | if (widget->event) { | ||
1448 | if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { | ||
1449 | ret = widget->event(widget, | ||
1450 | kcontrol, SND_SOC_DAPM_PRE_REG); | ||
1451 | if (ret < 0) | ||
1452 | goto out; | ||
1453 | } | ||
1454 | ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); | ||
1455 | if (widget->event_flags & SND_SOC_DAPM_POST_REG) | ||
1456 | ret = widget->event(widget, | ||
1457 | kcontrol, SND_SOC_DAPM_POST_REG); | ||
1458 | } else | ||
1459 | ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); | ||
1460 | |||
1461 | out: | ||
1462 | mutex_unlock(&widget->codec->mutex); | ||
1463 | return ret; | ||
1464 | } | ||
1465 | EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); | ||
1466 | |||
1467 | /** | ||
1277 | * snd_soc_dapm_new_control - create new dapm control | 1468 | * snd_soc_dapm_new_control - create new dapm control |
1278 | * @codec: audio codec | 1469 | * @codec: audio codec |
1279 | * @widget: widget template | 1470 | * @widget: widget template |