diff options
| -rw-r--r-- | include/sound/soc-dapm.h | 15 | ||||
| -rw-r--r-- | include/sound/soc.h | 30 | ||||
| -rw-r--r-- | sound/arm/pxa2xx-ac97-lib.c | 25 | ||||
| -rw-r--r-- | sound/soc/codecs/twl4030.c | 118 | ||||
| -rw-r--r-- | sound/soc/davinci/davinci-evm.c | 19 | ||||
| -rw-r--r-- | sound/soc/omap/Kconfig | 1 | ||||
| -rw-r--r-- | sound/soc/omap/omap3pandora.c | 13 | ||||
| -rw-r--r-- | sound/soc/soc-core.c | 107 | ||||
| -rw-r--r-- | sound/soc/soc-dapm.c | 197 |
9 files changed, 447 insertions, 78 deletions
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 7ee2f70ca42e..4af1083e3287 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h | |||
| @@ -85,6 +85,10 @@ | |||
| 85 | #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \ | 85 | #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \ |
| 86 | { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ | 86 | { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ |
| 87 | .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} | 87 | .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} |
| 88 | #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \ | ||
| 89 | { .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \ | ||
| 90 | .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ | ||
| 91 | .num_kcontrols = 1} | ||
| 88 | 92 | ||
| 89 | /* path domain with event - event handler must return 0 for success */ | 93 | /* path domain with event - event handler must return 0 for success */ |
| 90 | #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \ | 94 | #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \ |
| @@ -172,6 +176,12 @@ | |||
| 172 | .get = snd_soc_dapm_get_enum_double, \ | 176 | .get = snd_soc_dapm_get_enum_double, \ |
| 173 | .put = snd_soc_dapm_put_enum_double, \ | 177 | .put = snd_soc_dapm_put_enum_double, \ |
| 174 | .private_value = (unsigned long)&xenum } | 178 | .private_value = (unsigned long)&xenum } |
| 179 | #define SOC_DAPM_VALUE_ENUM(xname, xenum) \ | ||
| 180 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | ||
| 181 | .info = snd_soc_info_value_enum_double, \ | ||
| 182 | .get = snd_soc_dapm_get_value_enum_double, \ | ||
| 183 | .put = snd_soc_dapm_put_value_enum_double, \ | ||
| 184 | .private_value = (unsigned long)&xenum } | ||
| 175 | 185 | ||
| 176 | /* dapm stream operations */ | 186 | /* dapm stream operations */ |
| 177 | #define SND_SOC_DAPM_STREAM_NOP 0x0 | 187 | #define SND_SOC_DAPM_STREAM_NOP 0x0 |
| @@ -214,6 +224,10 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, | |||
| 214 | struct snd_ctl_elem_value *ucontrol); | 224 | struct snd_ctl_elem_value *ucontrol); |
| 215 | int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, | 225 | int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, |
| 216 | struct snd_ctl_elem_value *ucontrol); | 226 | struct snd_ctl_elem_value *ucontrol); |
| 227 | int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, | ||
| 228 | struct snd_ctl_elem_value *ucontrol); | ||
| 229 | int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, | ||
| 230 | struct snd_ctl_elem_value *ucontrol); | ||
| 217 | int snd_soc_dapm_new_control(struct snd_soc_codec *codec, | 231 | int snd_soc_dapm_new_control(struct snd_soc_codec *codec, |
| 218 | const struct snd_soc_dapm_widget *widget); | 232 | const struct snd_soc_dapm_widget *widget); |
| 219 | int snd_soc_dapm_new_controls(struct snd_soc_codec *codec, | 233 | int snd_soc_dapm_new_controls(struct snd_soc_codec *codec, |
| @@ -247,6 +261,7 @@ enum snd_soc_dapm_type { | |||
| 247 | snd_soc_dapm_input = 0, /* input pin */ | 261 | snd_soc_dapm_input = 0, /* input pin */ |
| 248 | snd_soc_dapm_output, /* output pin */ | 262 | snd_soc_dapm_output, /* output pin */ |
| 249 | snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */ | 263 | snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */ |
| 264 | snd_soc_dapm_value_mux, /* selects 1 analog signal from many inputs */ | ||
| 250 | snd_soc_dapm_mixer, /* mixes several analog signals together */ | 265 | snd_soc_dapm_mixer, /* mixes several analog signals together */ |
| 251 | snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */ | 266 | snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */ |
| 252 | snd_soc_dapm_adc, /* analog to digital converter */ | 267 | snd_soc_dapm_adc, /* analog to digital converter */ |
diff --git a/include/sound/soc.h b/include/sound/soc.h index f86e455d3828..9b930d342116 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h | |||
| @@ -94,11 +94,22 @@ | |||
| 94 | SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts) | 94 | SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts) |
| 95 | #define SOC_ENUM_SINGLE_EXT(xmax, xtexts) \ | 95 | #define SOC_ENUM_SINGLE_EXT(xmax, xtexts) \ |
| 96 | { .max = xmax, .texts = xtexts } | 96 | { .max = xmax, .texts = xtexts } |
| 97 | #define SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xmax, xtexts, xvalues) \ | ||
| 98 | { .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ | ||
| 99 | .mask = xmask, .max = xmax, .texts = xtexts, .values = xvalues} | ||
| 100 | #define SOC_VALUE_ENUM_SINGLE(xreg, xshift, xmask, xmax, xtexts, xvalues) \ | ||
| 101 | SOC_VALUE_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xmax, xtexts, xvalues) | ||
| 97 | #define SOC_ENUM(xname, xenum) \ | 102 | #define SOC_ENUM(xname, xenum) \ |
| 98 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\ | 103 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\ |
| 99 | .info = snd_soc_info_enum_double, \ | 104 | .info = snd_soc_info_enum_double, \ |
| 100 | .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \ | 105 | .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \ |
| 101 | .private_value = (unsigned long)&xenum } | 106 | .private_value = (unsigned long)&xenum } |
| 107 | #define SOC_VALUE_ENUM(xname, xenum) \ | ||
| 108 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\ | ||
| 109 | .info = snd_soc_info_value_enum_double, \ | ||
| 110 | .get = snd_soc_get_value_enum_double, \ | ||
| 111 | .put = snd_soc_put_value_enum_double, \ | ||
| 112 | .private_value = (unsigned long)&xenum } | ||
| 102 | #define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\ | 113 | #define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\ |
| 103 | xhandler_get, xhandler_put) \ | 114 | xhandler_get, xhandler_put) \ |
| 104 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | 115 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ |
| @@ -200,6 +211,12 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, | |||
| 200 | struct snd_ctl_elem_value *ucontrol); | 211 | struct snd_ctl_elem_value *ucontrol); |
| 201 | int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, | 212 | int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, |
| 202 | struct snd_ctl_elem_value *ucontrol); | 213 | struct snd_ctl_elem_value *ucontrol); |
| 214 | int snd_soc_info_value_enum_double(struct snd_kcontrol *kcontrol, | ||
| 215 | struct snd_ctl_elem_info *uinfo); | ||
| 216 | int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol, | ||
| 217 | struct snd_ctl_elem_value *ucontrol); | ||
| 218 | int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, | ||
| 219 | struct snd_ctl_elem_value *ucontrol); | ||
| 203 | int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, | 220 | int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, |
| 204 | struct snd_ctl_elem_info *uinfo); | 221 | struct snd_ctl_elem_info *uinfo); |
| 205 | int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, | 222 | int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, |
| @@ -406,6 +423,19 @@ struct soc_enum { | |||
| 406 | void *dapm; | 423 | void *dapm; |
| 407 | }; | 424 | }; |
| 408 | 425 | ||
| 426 | /* semi enumerated kcontrol */ | ||
| 427 | struct soc_value_enum { | ||
| 428 | unsigned short reg; | ||
| 429 | unsigned short reg2; | ||
| 430 | unsigned char shift_l; | ||
| 431 | unsigned char shift_r; | ||
| 432 | unsigned int max; | ||
| 433 | unsigned int mask; | ||
| 434 | const char **texts; | ||
| 435 | const unsigned int *values; | ||
| 436 | void *dapm; | ||
| 437 | }; | ||
| 438 | |||
| 409 | #include <sound/soc-dai.h> | 439 | #include <sound/soc-dai.h> |
| 410 | 440 | ||
| 411 | #endif | 441 | #endif |
diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index ef6539eea579..35afd0c33be5 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c | |||
| @@ -321,10 +321,6 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev) | |||
| 321 | { | 321 | { |
| 322 | int ret; | 322 | int ret; |
| 323 | 323 | ||
| 324 | ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL); | ||
| 325 | if (ret < 0) | ||
| 326 | goto err; | ||
| 327 | |||
| 328 | if (cpu_is_pxa25x() || cpu_is_pxa27x()) { | 324 | if (cpu_is_pxa25x() || cpu_is_pxa27x()) { |
| 329 | pxa_gpio_mode(GPIO31_SYNC_AC97_MD); | 325 | pxa_gpio_mode(GPIO31_SYNC_AC97_MD); |
| 330 | pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); | 326 | pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); |
| @@ -339,7 +335,7 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev) | |||
| 339 | if (IS_ERR(ac97conf_clk)) { | 335 | if (IS_ERR(ac97conf_clk)) { |
| 340 | ret = PTR_ERR(ac97conf_clk); | 336 | ret = PTR_ERR(ac97conf_clk); |
| 341 | ac97conf_clk = NULL; | 337 | ac97conf_clk = NULL; |
| 342 | goto err_irq; | 338 | goto err_conf; |
| 343 | } | 339 | } |
| 344 | } | 340 | } |
| 345 | 341 | ||
| @@ -347,19 +343,30 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev) | |||
| 347 | if (IS_ERR(ac97_clk)) { | 343 | if (IS_ERR(ac97_clk)) { |
| 348 | ret = PTR_ERR(ac97_clk); | 344 | ret = PTR_ERR(ac97_clk); |
| 349 | ac97_clk = NULL; | 345 | ac97_clk = NULL; |
| 350 | goto err_irq; | 346 | goto err_clk; |
| 351 | } | 347 | } |
| 352 | 348 | ||
| 353 | return clk_enable(ac97_clk); | 349 | ret = clk_enable(ac97_clk); |
| 350 | if (ret) | ||
| 351 | goto err_clk2; | ||
| 352 | |||
| 353 | ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL); | ||
| 354 | if (ret < 0) | ||
| 355 | goto err_irq; | ||
| 356 | |||
| 357 | return 0; | ||
| 354 | 358 | ||
| 355 | err_irq: | 359 | err_irq: |
| 356 | GCR |= GCR_ACLINK_OFF; | 360 | GCR |= GCR_ACLINK_OFF; |
| 361 | err_clk2: | ||
| 362 | clk_put(ac97_clk); | ||
| 363 | ac97_clk = NULL; | ||
| 364 | err_clk: | ||
| 357 | if (ac97conf_clk) { | 365 | if (ac97conf_clk) { |
| 358 | clk_put(ac97conf_clk); | 366 | clk_put(ac97conf_clk); |
| 359 | ac97conf_clk = NULL; | 367 | ac97conf_clk = NULL; |
| 360 | } | 368 | } |
| 361 | free_irq(IRQ_AC97, NULL); | 369 | err_conf: |
| 362 | err: | ||
| 363 | return ret; | 370 | return ret; |
| 364 | } | 371 | } |
| 365 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_probe); | 372 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_probe); |
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 31e44e346dc8..fd0f338374a7 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c | |||
| @@ -192,39 +192,51 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) | |||
| 192 | 192 | ||
| 193 | /* Earpiece */ | 193 | /* Earpiece */ |
| 194 | static const char *twl4030_earpiece_texts[] = | 194 | static const char *twl4030_earpiece_texts[] = |
| 195 | {"Off", "DACL1", "DACL2", "Invalid", "DACR1"}; | 195 | {"Off", "DACL1", "DACL2", "DACR1"}; |
| 196 | 196 | ||
| 197 | static const struct soc_enum twl4030_earpiece_enum = | 197 | static const unsigned int twl4030_earpiece_values[] = |
| 198 | SOC_ENUM_SINGLE(TWL4030_REG_EAR_CTL, 1, | 198 | {0x0, 0x1, 0x2, 0x4}; |
| 199 | |||
| 200 | static const struct soc_value_enum twl4030_earpiece_enum = | ||
| 201 | SOC_VALUE_ENUM_SINGLE(TWL4030_REG_EAR_CTL, 1, 0x7, | ||
| 199 | ARRAY_SIZE(twl4030_earpiece_texts), | 202 | ARRAY_SIZE(twl4030_earpiece_texts), |
| 200 | twl4030_earpiece_texts); | 203 | twl4030_earpiece_texts, |
| 204 | twl4030_earpiece_values); | ||
| 201 | 205 | ||
| 202 | static const struct snd_kcontrol_new twl4030_dapm_earpiece_control = | 206 | static const struct snd_kcontrol_new twl4030_dapm_earpiece_control = |
| 203 | SOC_DAPM_ENUM("Route", twl4030_earpiece_enum); | 207 | SOC_DAPM_VALUE_ENUM("Route", twl4030_earpiece_enum); |
| 204 | 208 | ||
| 205 | /* PreDrive Left */ | 209 | /* PreDrive Left */ |
| 206 | static const char *twl4030_predrivel_texts[] = | 210 | static const char *twl4030_predrivel_texts[] = |
| 207 | {"Off", "DACL1", "DACL2", "Invalid", "DACR2"}; | 211 | {"Off", "DACL1", "DACL2", "DACR2"}; |
| 212 | |||
| 213 | static const unsigned int twl4030_predrivel_values[] = | ||
| 214 | {0x0, 0x1, 0x2, 0x4}; | ||
| 208 | 215 | ||
| 209 | static const struct soc_enum twl4030_predrivel_enum = | 216 | static const struct soc_value_enum twl4030_predrivel_enum = |
| 210 | SOC_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 1, | 217 | SOC_VALUE_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 1, 0x7, |
| 211 | ARRAY_SIZE(twl4030_predrivel_texts), | 218 | ARRAY_SIZE(twl4030_predrivel_texts), |
| 212 | twl4030_predrivel_texts); | 219 | twl4030_predrivel_texts, |
| 220 | twl4030_predrivel_values); | ||
| 213 | 221 | ||
| 214 | static const struct snd_kcontrol_new twl4030_dapm_predrivel_control = | 222 | static const struct snd_kcontrol_new twl4030_dapm_predrivel_control = |
| 215 | SOC_DAPM_ENUM("Route", twl4030_predrivel_enum); | 223 | SOC_DAPM_VALUE_ENUM("Route", twl4030_predrivel_enum); |
| 216 | 224 | ||
| 217 | /* PreDrive Right */ | 225 | /* PreDrive Right */ |
| 218 | static const char *twl4030_predriver_texts[] = | 226 | static const char *twl4030_predriver_texts[] = |
| 219 | {"Off", "DACR1", "DACR2", "Invalid", "DACL2"}; | 227 | {"Off", "DACR1", "DACR2", "DACL2"}; |
| 220 | 228 | ||
| 221 | static const struct soc_enum twl4030_predriver_enum = | 229 | static const unsigned int twl4030_predriver_values[] = |
| 222 | SOC_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 1, | 230 | {0x0, 0x1, 0x2, 0x4}; |
| 231 | |||
| 232 | static const struct soc_value_enum twl4030_predriver_enum = | ||
| 233 | SOC_VALUE_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 1, 0x7, | ||
| 223 | ARRAY_SIZE(twl4030_predriver_texts), | 234 | ARRAY_SIZE(twl4030_predriver_texts), |
| 224 | twl4030_predriver_texts); | 235 | twl4030_predriver_texts, |
| 236 | twl4030_predriver_values); | ||
| 225 | 237 | ||
| 226 | static const struct snd_kcontrol_new twl4030_dapm_predriver_control = | 238 | static const struct snd_kcontrol_new twl4030_dapm_predriver_control = |
| 227 | SOC_DAPM_ENUM("Route", twl4030_predriver_enum); | 239 | SOC_DAPM_VALUE_ENUM("Route", twl4030_predriver_enum); |
| 228 | 240 | ||
| 229 | /* Headset Left */ | 241 | /* Headset Left */ |
| 230 | static const char *twl4030_hsol_texts[] = | 242 | static const char *twl4030_hsol_texts[] = |
| @@ -300,28 +312,35 @@ SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum); | |||
| 300 | 312 | ||
| 301 | /* Left analog microphone selection */ | 313 | /* Left analog microphone selection */ |
| 302 | static const char *twl4030_analoglmic_texts[] = | 314 | static const char *twl4030_analoglmic_texts[] = |
| 303 | {"Off", "Main mic", "Headset mic", "Invalid", "AUXL", | 315 | {"Off", "Main mic", "Headset mic", "AUXL", "Carkit mic"}; |
| 304 | "Invalid", "Invalid", "Invalid", "Carkit mic"}; | 316 | |
| 317 | static const unsigned int twl4030_analoglmic_values[] = | ||
| 318 | {0x0, 0x1, 0x2, 0x4, 0x8}; | ||
| 305 | 319 | ||
| 306 | static const struct soc_enum twl4030_analoglmic_enum = | 320 | static const struct soc_value_enum twl4030_analoglmic_enum = |
| 307 | SOC_ENUM_SINGLE(TWL4030_REG_ANAMICL, 0, | 321 | SOC_VALUE_ENUM_SINGLE(TWL4030_REG_ANAMICL, 0, 0xf, |
| 308 | ARRAY_SIZE(twl4030_analoglmic_texts), | 322 | ARRAY_SIZE(twl4030_analoglmic_texts), |
| 309 | twl4030_analoglmic_texts); | 323 | twl4030_analoglmic_texts, |
| 324 | twl4030_analoglmic_values); | ||
| 310 | 325 | ||
| 311 | static const struct snd_kcontrol_new twl4030_dapm_analoglmic_control = | 326 | static const struct snd_kcontrol_new twl4030_dapm_analoglmic_control = |
| 312 | SOC_DAPM_ENUM("Route", twl4030_analoglmic_enum); | 327 | SOC_DAPM_VALUE_ENUM("Route", twl4030_analoglmic_enum); |
| 313 | 328 | ||
| 314 | /* Right analog microphone selection */ | 329 | /* Right analog microphone selection */ |
| 315 | static const char *twl4030_analogrmic_texts[] = | 330 | static const char *twl4030_analogrmic_texts[] = |
| 316 | {"Off", "Sub mic", "Invalid", "Invalid", "AUXR"}; | 331 | {"Off", "Sub mic", "AUXR"}; |
| 317 | 332 | ||
| 318 | static const struct soc_enum twl4030_analogrmic_enum = | 333 | static const unsigned int twl4030_analogrmic_values[] = |
| 319 | SOC_ENUM_SINGLE(TWL4030_REG_ANAMICR, 0, | 334 | {0x0, 0x1, 0x4}; |
| 335 | |||
| 336 | static const struct soc_value_enum twl4030_analogrmic_enum = | ||
| 337 | SOC_VALUE_ENUM_SINGLE(TWL4030_REG_ANAMICR, 0, 0x5, | ||
| 320 | ARRAY_SIZE(twl4030_analogrmic_texts), | 338 | ARRAY_SIZE(twl4030_analogrmic_texts), |
| 321 | twl4030_analogrmic_texts); | 339 | twl4030_analogrmic_texts, |
| 340 | twl4030_analogrmic_values); | ||
| 322 | 341 | ||
| 323 | static const struct snd_kcontrol_new twl4030_dapm_analogrmic_control = | 342 | static const struct snd_kcontrol_new twl4030_dapm_analogrmic_control = |
| 324 | SOC_DAPM_ENUM("Route", twl4030_analogrmic_enum); | 343 | SOC_DAPM_VALUE_ENUM("Route", twl4030_analogrmic_enum); |
| 325 | 344 | ||
| 326 | /* TX1 L/R Analog/Digital microphone selection */ | 345 | /* TX1 L/R Analog/Digital microphone selection */ |
| 327 | static const char *twl4030_micpathtx1_texts[] = | 346 | static const char *twl4030_micpathtx1_texts[] = |
| @@ -347,28 +366,6 @@ static const struct soc_enum twl4030_micpathtx2_enum = | |||
| 347 | static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control = | 366 | static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control = |
| 348 | SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum); | 367 | SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum); |
| 349 | 368 | ||
| 350 | /* | ||
| 351 | * This function filters out the non valid mux settings, named as "Invalid" | ||
| 352 | * in the enum texts. | ||
| 353 | * Just refuse to set an invalid mux mode. | ||
| 354 | */ | ||
| 355 | static int twl4030_enum_event(struct snd_soc_dapm_widget *w, | ||
| 356 | struct snd_kcontrol *kcontrol, int event) | ||
| 357 | { | ||
| 358 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | ||
| 359 | int ret = 0; | ||
| 360 | int val; | ||
| 361 | |||
| 362 | val = w->value >> e->shift_l; | ||
| 363 | if (!strcmp("Invalid", e->texts[val])) { | ||
| 364 | printk(KERN_WARNING "Invalid MUX setting on 0x%02x (%d)\n", | ||
| 365 | e->reg, val); | ||
| 366 | ret = -1; | ||
| 367 | } | ||
| 368 | |||
| 369 | return ret; | ||
| 370 | } | ||
| 371 | |||
| 372 | static int micpath_event(struct snd_soc_dapm_widget *w, | 369 | static int micpath_event(struct snd_soc_dapm_widget *w, |
| 373 | struct snd_kcontrol *kcontrol, int event) | 370 | struct snd_kcontrol *kcontrol, int event) |
| 374 | { | 371 | { |
| @@ -737,16 +734,13 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
| 737 | 734 | ||
| 738 | /* Output MUX controls */ | 735 | /* Output MUX controls */ |
| 739 | /* Earpiece */ | 736 | /* Earpiece */ |
| 740 | SND_SOC_DAPM_MUX_E("Earpiece Mux", SND_SOC_NOPM, 0, 0, | 737 | SND_SOC_DAPM_VALUE_MUX("Earpiece Mux", SND_SOC_NOPM, 0, 0, |
| 741 | &twl4030_dapm_earpiece_control, twl4030_enum_event, | 738 | &twl4030_dapm_earpiece_control), |
| 742 | SND_SOC_DAPM_PRE_REG), | ||
| 743 | /* PreDrivL/R */ | 739 | /* PreDrivL/R */ |
| 744 | SND_SOC_DAPM_MUX_E("PredriveL Mux", SND_SOC_NOPM, 0, 0, | 740 | SND_SOC_DAPM_VALUE_MUX("PredriveL Mux", SND_SOC_NOPM, 0, 0, |
| 745 | &twl4030_dapm_predrivel_control, twl4030_enum_event, | 741 | &twl4030_dapm_predrivel_control), |
| 746 | SND_SOC_DAPM_PRE_REG), | 742 | SND_SOC_DAPM_VALUE_MUX("PredriveR Mux", SND_SOC_NOPM, 0, 0, |
| 747 | SND_SOC_DAPM_MUX_E("PredriveR Mux", SND_SOC_NOPM, 0, 0, | 743 | &twl4030_dapm_predriver_control), |
| 748 | &twl4030_dapm_predriver_control, twl4030_enum_event, | ||
| 749 | SND_SOC_DAPM_PRE_REG), | ||
| 750 | /* HeadsetL/R */ | 744 | /* HeadsetL/R */ |
| 751 | SND_SOC_DAPM_MUX("HeadsetL Mux", SND_SOC_NOPM, 0, 0, | 745 | SND_SOC_DAPM_MUX("HeadsetL Mux", SND_SOC_NOPM, 0, 0, |
| 752 | &twl4030_dapm_hsol_control), | 746 | &twl4030_dapm_hsol_control), |
| @@ -789,12 +783,10 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
| 789 | SND_SOC_DAPM_POST_REG), | 783 | SND_SOC_DAPM_POST_REG), |
| 790 | 784 | ||
| 791 | /* Analog input muxes with power switch for the physical ADCL/R */ | 785 | /* Analog input muxes with power switch for the physical ADCL/R */ |
| 792 | SND_SOC_DAPM_MUX_E("Analog Left Capture Route", | 786 | SND_SOC_DAPM_VALUE_MUX("Analog Left Capture Route", |
| 793 | TWL4030_REG_AVADC_CTL, 3, 0, &twl4030_dapm_analoglmic_control, | 787 | TWL4030_REG_AVADC_CTL, 3, 0, &twl4030_dapm_analoglmic_control), |
| 794 | twl4030_enum_event, SND_SOC_DAPM_PRE_REG), | 788 | SND_SOC_DAPM_VALUE_MUX("Analog Right Capture Route", |
| 795 | SND_SOC_DAPM_MUX_E("Analog Right Capture Route", | 789 | TWL4030_REG_AVADC_CTL, 1, 0, &twl4030_dapm_analogrmic_control), |
| 796 | TWL4030_REG_AVADC_CTL, 1, 0, &twl4030_dapm_analogrmic_control, | ||
| 797 | twl4030_enum_event, SND_SOC_DAPM_PRE_REG), | ||
| 798 | 790 | ||
| 799 | SND_SOC_DAPM_PGA("Analog Left Amplifier", | 791 | SND_SOC_DAPM_PGA("Analog Left Amplifier", |
| 800 | TWL4030_REG_ANAMICL, 4, 0, NULL, 0), | 792 | TWL4030_REG_ANAMICL, 4, 0, NULL, 0), |
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 01b948bb55a1..54851f318568 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c | |||
| @@ -26,7 +26,6 @@ | |||
| 26 | #include "davinci-pcm.h" | 26 | #include "davinci-pcm.h" |
| 27 | #include "davinci-i2s.h" | 27 | #include "davinci-i2s.h" |
| 28 | 28 | ||
| 29 | #define EVM_CODEC_CLOCK 22579200 | ||
| 30 | 29 | ||
| 31 | #define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \ | 30 | #define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \ |
| 32 | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF) | 31 | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF) |
| @@ -37,6 +36,21 @@ static int evm_hw_params(struct snd_pcm_substream *substream, | |||
| 37 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | 36 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; |
| 38 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 37 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
| 39 | int ret = 0; | 38 | int ret = 0; |
| 39 | unsigned sysclk; | ||
| 40 | |||
| 41 | /* ASP1 on DM355 EVM is clocked by an external oscillator */ | ||
| 42 | if (machine_is_davinci_dm355_evm()) | ||
| 43 | sysclk = 27000000; | ||
| 44 | |||
| 45 | /* ASP0 in DM6446 EVM is clocked by U55, as configured by | ||
| 46 | * board-dm644x-evm.c using GPIOs from U18. There are six | ||
| 47 | * options; here we "know" we use a 48 KHz sample rate. | ||
| 48 | */ | ||
| 49 | else if (machine_is_davinci_evm()) | ||
| 50 | sysclk = 12288000; | ||
| 51 | |||
| 52 | else | ||
| 53 | return -EINVAL; | ||
| 40 | 54 | ||
| 41 | /* set codec DAI configuration */ | 55 | /* set codec DAI configuration */ |
| 42 | ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT); | 56 | ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT); |
| @@ -49,8 +63,7 @@ static int evm_hw_params(struct snd_pcm_substream *substream, | |||
| 49 | return ret; | 63 | return ret; |
| 50 | 64 | ||
| 51 | /* set the codec system clock */ | 65 | /* set the codec system clock */ |
| 52 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, EVM_CODEC_CLOCK, | 66 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT); |
| 53 | SND_SOC_CLOCK_OUT); | ||
| 54 | if (ret < 0) | 67 | if (ret < 0) |
| 55 | return ret; | 68 | return ret; |
| 56 | 69 | ||
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index a7b1d77b2105..4f7f04014585 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig | |||
| @@ -10,6 +10,7 @@ config SND_OMAP_SOC_N810 | |||
| 10 | tristate "SoC Audio support for Nokia N810" | 10 | tristate "SoC Audio support for Nokia N810" |
| 11 | depends on SND_OMAP_SOC && MACH_NOKIA_N810 | 11 | depends on SND_OMAP_SOC && MACH_NOKIA_N810 |
| 12 | select SND_OMAP_SOC_MCBSP | 12 | select SND_OMAP_SOC_MCBSP |
| 13 | select OMAP_MUX | ||
| 13 | select SND_SOC_TLV320AIC3X | 14 | select SND_SOC_TLV320AIC3X |
| 14 | help | 15 | help |
| 15 | Say Y if you want to add support for SoC audio on Nokia N810. | 16 | Say Y if you want to add support for SoC audio on Nokia N810. |
diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c index bd91594496b1..fcc2f5d9a878 100644 --- a/sound/soc/omap/omap3pandora.c +++ b/sound/soc/omap/omap3pandora.c | |||
| @@ -180,6 +180,19 @@ static int omap3pandora_in_init(struct snd_soc_codec *codec) | |||
| 180 | { | 180 | { |
| 181 | int ret; | 181 | int ret; |
| 182 | 182 | ||
| 183 | /* All TWL4030 output pins are floating */ | ||
| 184 | snd_soc_dapm_nc_pin(codec, "OUTL"), | ||
| 185 | snd_soc_dapm_nc_pin(codec, "OUTR"), | ||
| 186 | snd_soc_dapm_nc_pin(codec, "EARPIECE"), | ||
| 187 | snd_soc_dapm_nc_pin(codec, "PREDRIVEL"), | ||
| 188 | snd_soc_dapm_nc_pin(codec, "PREDRIVER"), | ||
| 189 | snd_soc_dapm_nc_pin(codec, "HSOL"), | ||
| 190 | snd_soc_dapm_nc_pin(codec, "HSOR"), | ||
| 191 | snd_soc_dapm_nc_pin(codec, "CARKITL"), | ||
| 192 | snd_soc_dapm_nc_pin(codec, "CARKITR"), | ||
| 193 | snd_soc_dapm_nc_pin(codec, "HFL"), | ||
| 194 | snd_soc_dapm_nc_pin(codec, "HFR"), | ||
| 195 | |||
| 183 | ret = snd_soc_dapm_new_controls(codec, omap3pandora_in_dapm_widgets, | 196 | ret = snd_soc_dapm_new_controls(codec, omap3pandora_in_dapm_widgets, |
| 184 | ARRAY_SIZE(omap3pandora_in_dapm_widgets)); | 197 | ARRAY_SIZE(omap3pandora_in_dapm_widgets)); |
| 185 | if (ret < 0) | 198 | if (ret < 0) |
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index f73c1341437c..6cbe7e82f238 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c | |||
| @@ -1585,6 +1585,113 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, | |||
| 1585 | EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); | 1585 | EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); |
| 1586 | 1586 | ||
| 1587 | /** | 1587 | /** |
| 1588 | * snd_soc_info_value_enum_double - semi enumerated double mixer info callback | ||
| 1589 | * @kcontrol: mixer control | ||
| 1590 | * @uinfo: control element information | ||
| 1591 | * | ||
| 1592 | * Callback to provide information about a double semi enumerated | ||
| 1593 | * mixer control. | ||
| 1594 | * | ||
| 1595 | * Semi enumerated mixer: the enumerated items are referred as values. Can be | ||
| 1596 | * used for handling bitfield coded enumeration for example. | ||
| 1597 | * | ||
| 1598 | * Returns 0 for success. | ||
| 1599 | */ | ||
| 1600 | int snd_soc_info_value_enum_double(struct snd_kcontrol *kcontrol, | ||
| 1601 | struct snd_ctl_elem_info *uinfo) | ||
| 1602 | { | ||
| 1603 | struct soc_value_enum *e = (struct soc_value_enum *) | ||
| 1604 | kcontrol->private_value; | ||
| 1605 | |||
| 1606 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
| 1607 | uinfo->count = e->shift_l == e->shift_r ? 1 : 2; | ||
| 1608 | uinfo->value.enumerated.items = e->max; | ||
| 1609 | |||
| 1610 | if (uinfo->value.enumerated.item > e->max - 1) | ||
| 1611 | uinfo->value.enumerated.item = e->max - 1; | ||
| 1612 | strcpy(uinfo->value.enumerated.name, | ||
| 1613 | e->texts[uinfo->value.enumerated.item]); | ||
| 1614 | return 0; | ||
| 1615 | } | ||
| 1616 | EXPORT_SYMBOL_GPL(snd_soc_info_value_enum_double); | ||
| 1617 | |||
| 1618 | /** | ||
| 1619 | * snd_soc_get_value_enum_double - semi enumerated double mixer get callback | ||
| 1620 | * @kcontrol: mixer control | ||
| 1621 | * @ucontrol: control element information | ||
| 1622 | * | ||
| 1623 | * Callback to get the value of a double semi enumerated mixer. | ||
| 1624 | * | ||
| 1625 | * Semi enumerated mixer: the enumerated items are referred as values. Can be | ||
| 1626 | * used for handling bitfield coded enumeration for example. | ||
| 1627 | * | ||
| 1628 | * Returns 0 for success. | ||
| 1629 | */ | ||
| 1630 | int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol, | ||
| 1631 | struct snd_ctl_elem_value *ucontrol) | ||
| 1632 | { | ||
| 1633 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
| 1634 | struct soc_value_enum *e = (struct soc_value_enum *) | ||
| 1635 | kcontrol->private_value; | ||
| 1636 | unsigned short reg_val, val, mux; | ||
| 1637 | |||
| 1638 | reg_val = snd_soc_read(codec, e->reg); | ||
| 1639 | val = (reg_val >> e->shift_l) & e->mask; | ||
| 1640 | for (mux = 0; mux < e->max; mux++) { | ||
| 1641 | if (val == e->values[mux]) | ||
| 1642 | break; | ||
| 1643 | } | ||
| 1644 | ucontrol->value.enumerated.item[0] = mux; | ||
| 1645 | if (e->shift_l != e->shift_r) { | ||
| 1646 | val = (reg_val >> e->shift_r) & e->mask; | ||
| 1647 | for (mux = 0; mux < e->max; mux++) { | ||
| 1648 | if (val == e->values[mux]) | ||
| 1649 | break; | ||
| 1650 | } | ||
| 1651 | ucontrol->value.enumerated.item[1] = mux; | ||
| 1652 | } | ||
| 1653 | |||
| 1654 | return 0; | ||
| 1655 | } | ||
| 1656 | EXPORT_SYMBOL_GPL(snd_soc_get_value_enum_double); | ||
| 1657 | |||
| 1658 | /** | ||
| 1659 | * snd_soc_put_value_enum_double - semi enumerated double mixer put callback | ||
| 1660 | * @kcontrol: mixer control | ||
| 1661 | * @ucontrol: control element information | ||
| 1662 | * | ||
| 1663 | * Callback to set the value of a double semi enumerated mixer. | ||
| 1664 | * | ||
| 1665 | * Semi enumerated mixer: the enumerated items are referred as values. Can be | ||
| 1666 | * used for handling bitfield coded enumeration for example. | ||
| 1667 | * | ||
| 1668 | * Returns 0 for success. | ||
| 1669 | */ | ||
| 1670 | int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, | ||
| 1671 | struct snd_ctl_elem_value *ucontrol) | ||
| 1672 | { | ||
| 1673 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
| 1674 | struct soc_value_enum *e = (struct soc_value_enum *) | ||
| 1675 | kcontrol->private_value; | ||
| 1676 | unsigned short val; | ||
| 1677 | unsigned short mask; | ||
| 1678 | |||
| 1679 | if (ucontrol->value.enumerated.item[0] > e->max - 1) | ||
| 1680 | return -EINVAL; | ||
| 1681 | val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l; | ||
| 1682 | mask = e->mask << e->shift_l; | ||
| 1683 | if (e->shift_l != e->shift_r) { | ||
| 1684 | if (ucontrol->value.enumerated.item[1] > e->max - 1) | ||
| 1685 | return -EINVAL; | ||
| 1686 | val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r; | ||
| 1687 | mask |= e->mask << e->shift_r; | ||
| 1688 | } | ||
| 1689 | |||
| 1690 | return snd_soc_update_bits(codec, e->reg, mask, val); | ||
| 1691 | } | ||
| 1692 | EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double); | ||
| 1693 | |||
| 1694 | /** | ||
| 1588 | * snd_soc_info_enum_ext - external enumerated single mixer info callback | 1695 | * snd_soc_info_enum_ext - external enumerated single mixer info callback |
| 1589 | * @kcontrol: mixer control | 1696 | * @kcontrol: mixer control |
| 1590 | * @uinfo: control element information | 1697 | * @uinfo: control element information |
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 |
