diff options
Diffstat (limited to 'sound/soc/soc-core.c')
-rw-r--r-- | sound/soc/soc-core.c | 165 |
1 files changed, 104 insertions, 61 deletions
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1cd149b9ce69..3f44150d8e30 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c | |||
@@ -113,6 +113,35 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec) | |||
113 | } | 113 | } |
114 | #endif | 114 | #endif |
115 | 115 | ||
116 | static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream) | ||
117 | { | ||
118 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
119 | struct snd_soc_device *socdev = rtd->socdev; | ||
120 | struct snd_soc_card *card = socdev->card; | ||
121 | struct snd_soc_dai_link *machine = rtd->dai; | ||
122 | struct snd_soc_dai *cpu_dai = machine->cpu_dai; | ||
123 | struct snd_soc_dai *codec_dai = machine->codec_dai; | ||
124 | int ret; | ||
125 | |||
126 | if (codec_dai->symmetric_rates || cpu_dai->symmetric_rates || | ||
127 | machine->symmetric_rates) { | ||
128 | dev_dbg(card->dev, "Symmetry forces %dHz rate\n", | ||
129 | machine->rate); | ||
130 | |||
131 | ret = snd_pcm_hw_constraint_minmax(substream->runtime, | ||
132 | SNDRV_PCM_HW_PARAM_RATE, | ||
133 | machine->rate, | ||
134 | machine->rate); | ||
135 | if (ret < 0) { | ||
136 | dev_err(card->dev, | ||
137 | "Unable to apply rate symmetry constraint: %d\n", ret); | ||
138 | return ret; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | return 0; | ||
143 | } | ||
144 | |||
116 | /* | 145 | /* |
117 | * Called by ALSA when a PCM substream is opened, the runtime->hw record is | 146 | * Called by ALSA when a PCM substream is opened, the runtime->hw record is |
118 | * then initialized and any private data can be allocated. This also calls | 147 | * then initialized and any private data can be allocated. This also calls |
@@ -221,6 +250,13 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) | |||
221 | goto machine_err; | 250 | goto machine_err; |
222 | } | 251 | } |
223 | 252 | ||
253 | /* Symmetry only applies if we've already got an active stream. */ | ||
254 | if (cpu_dai->active || codec_dai->active) { | ||
255 | ret = soc_pcm_apply_symmetry(substream); | ||
256 | if (ret != 0) | ||
257 | goto machine_err; | ||
258 | } | ||
259 | |||
224 | pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name); | 260 | pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name); |
225 | pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates); | 261 | pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates); |
226 | pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min, | 262 | pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min, |
@@ -263,7 +299,6 @@ static void close_delayed_work(struct work_struct *work) | |||
263 | { | 299 | { |
264 | struct snd_soc_card *card = container_of(work, struct snd_soc_card, | 300 | struct snd_soc_card *card = container_of(work, struct snd_soc_card, |
265 | delayed_work.work); | 301 | delayed_work.work); |
266 | struct snd_soc_device *socdev = card->socdev; | ||
267 | struct snd_soc_codec *codec = card->codec; | 302 | struct snd_soc_codec *codec = card->codec; |
268 | struct snd_soc_dai *codec_dai; | 303 | struct snd_soc_dai *codec_dai; |
269 | int i; | 304 | int i; |
@@ -279,27 +314,10 @@ static void close_delayed_work(struct work_struct *work) | |||
279 | 314 | ||
280 | /* are we waiting on this codec DAI stream */ | 315 | /* are we waiting on this codec DAI stream */ |
281 | if (codec_dai->pop_wait == 1) { | 316 | if (codec_dai->pop_wait == 1) { |
282 | |||
283 | /* Reduce power if no longer active */ | ||
284 | if (codec->active == 0) { | ||
285 | pr_debug("pop wq D1 %s %s\n", codec->name, | ||
286 | codec_dai->playback.stream_name); | ||
287 | snd_soc_dapm_set_bias_level(socdev, | ||
288 | SND_SOC_BIAS_PREPARE); | ||
289 | } | ||
290 | |||
291 | codec_dai->pop_wait = 0; | 317 | codec_dai->pop_wait = 0; |
292 | snd_soc_dapm_stream_event(codec, | 318 | snd_soc_dapm_stream_event(codec, |
293 | codec_dai->playback.stream_name, | 319 | codec_dai->playback.stream_name, |
294 | SND_SOC_DAPM_STREAM_STOP); | 320 | SND_SOC_DAPM_STREAM_STOP); |
295 | |||
296 | /* Fall into standby if no longer active */ | ||
297 | if (codec->active == 0) { | ||
298 | pr_debug("pop wq D3 %s %s\n", codec->name, | ||
299 | codec_dai->playback.stream_name); | ||
300 | snd_soc_dapm_set_bias_level(socdev, | ||
301 | SND_SOC_BIAS_STANDBY); | ||
302 | } | ||
303 | } | 321 | } |
304 | } | 322 | } |
305 | mutex_unlock(&pcm_mutex); | 323 | mutex_unlock(&pcm_mutex); |
@@ -363,10 +381,6 @@ static int soc_codec_close(struct snd_pcm_substream *substream) | |||
363 | snd_soc_dapm_stream_event(codec, | 381 | snd_soc_dapm_stream_event(codec, |
364 | codec_dai->capture.stream_name, | 382 | codec_dai->capture.stream_name, |
365 | SND_SOC_DAPM_STREAM_STOP); | 383 | SND_SOC_DAPM_STREAM_STOP); |
366 | |||
367 | if (codec->active == 0 && codec_dai->pop_wait == 0) | ||
368 | snd_soc_dapm_set_bias_level(socdev, | ||
369 | SND_SOC_BIAS_STANDBY); | ||
370 | } | 384 | } |
371 | 385 | ||
372 | mutex_unlock(&pcm_mutex); | 386 | mutex_unlock(&pcm_mutex); |
@@ -431,36 +445,16 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) | |||
431 | cancel_delayed_work(&card->delayed_work); | 445 | cancel_delayed_work(&card->delayed_work); |
432 | } | 446 | } |
433 | 447 | ||
434 | /* do we need to power up codec */ | 448 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
435 | if (codec->bias_level != SND_SOC_BIAS_ON) { | 449 | snd_soc_dapm_stream_event(codec, |
436 | snd_soc_dapm_set_bias_level(socdev, | 450 | codec_dai->playback.stream_name, |
437 | SND_SOC_BIAS_PREPARE); | 451 | SND_SOC_DAPM_STREAM_START); |
438 | 452 | else | |
439 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 453 | snd_soc_dapm_stream_event(codec, |
440 | snd_soc_dapm_stream_event(codec, | 454 | codec_dai->capture.stream_name, |
441 | codec_dai->playback.stream_name, | 455 | SND_SOC_DAPM_STREAM_START); |
442 | SND_SOC_DAPM_STREAM_START); | ||
443 | else | ||
444 | snd_soc_dapm_stream_event(codec, | ||
445 | codec_dai->capture.stream_name, | ||
446 | SND_SOC_DAPM_STREAM_START); | ||
447 | |||
448 | snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON); | ||
449 | snd_soc_dai_digital_mute(codec_dai, 0); | ||
450 | |||
451 | } else { | ||
452 | /* codec already powered - power on widgets */ | ||
453 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
454 | snd_soc_dapm_stream_event(codec, | ||
455 | codec_dai->playback.stream_name, | ||
456 | SND_SOC_DAPM_STREAM_START); | ||
457 | else | ||
458 | snd_soc_dapm_stream_event(codec, | ||
459 | codec_dai->capture.stream_name, | ||
460 | SND_SOC_DAPM_STREAM_START); | ||
461 | 456 | ||
462 | snd_soc_dai_digital_mute(codec_dai, 0); | 457 | snd_soc_dai_digital_mute(codec_dai, 0); |
463 | } | ||
464 | 458 | ||
465 | out: | 459 | out: |
466 | mutex_unlock(&pcm_mutex); | 460 | mutex_unlock(&pcm_mutex); |
@@ -521,6 +515,8 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, | |||
521 | } | 515 | } |
522 | } | 516 | } |
523 | 517 | ||
518 | machine->rate = params_rate(params); | ||
519 | |||
524 | out: | 520 | out: |
525 | mutex_unlock(&pcm_mutex); | 521 | mutex_unlock(&pcm_mutex); |
526 | return ret; | 522 | return ret; |
@@ -632,6 +628,12 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) | |||
632 | struct snd_soc_codec *codec = card->codec; | 628 | struct snd_soc_codec *codec = card->codec; |
633 | int i; | 629 | int i; |
634 | 630 | ||
631 | /* If the initialization of this soc device failed, there is no codec | ||
632 | * associated with it. Just bail out in this case. | ||
633 | */ | ||
634 | if (!codec) | ||
635 | return 0; | ||
636 | |||
635 | /* Due to the resume being scheduled into a workqueue we could | 637 | /* Due to the resume being scheduled into a workqueue we could |
636 | * suspend before that's finished - wait for it to complete. | 638 | * suspend before that's finished - wait for it to complete. |
637 | */ | 639 | */ |
@@ -1334,6 +1336,7 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid) | |||
1334 | return ret; | 1336 | return ret; |
1335 | } | 1337 | } |
1336 | 1338 | ||
1339 | codec->socdev = socdev; | ||
1337 | codec->card->dev = socdev->dev; | 1340 | codec->card->dev = socdev->dev; |
1338 | codec->card->private_data = codec; | 1341 | codec->card->private_data = codec; |
1339 | strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver)); | 1342 | strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver)); |
@@ -1744,7 +1747,7 @@ int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, | |||
1744 | { | 1747 | { |
1745 | int max = kcontrol->private_value; | 1748 | int max = kcontrol->private_value; |
1746 | 1749 | ||
1747 | if (max == 1) | 1750 | if (max == 1 && !strstr(kcontrol->id.name, " Volume")) |
1748 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | 1751 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; |
1749 | else | 1752 | else |
1750 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 1753 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
@@ -1774,7 +1777,7 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, | |||
1774 | unsigned int shift = mc->shift; | 1777 | unsigned int shift = mc->shift; |
1775 | unsigned int rshift = mc->rshift; | 1778 | unsigned int rshift = mc->rshift; |
1776 | 1779 | ||
1777 | if (max == 1) | 1780 | if (max == 1 && !strstr(kcontrol->id.name, " Volume")) |
1778 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | 1781 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; |
1779 | else | 1782 | else |
1780 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 1783 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
@@ -1881,7 +1884,7 @@ int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol, | |||
1881 | (struct soc_mixer_control *)kcontrol->private_value; | 1884 | (struct soc_mixer_control *)kcontrol->private_value; |
1882 | int max = mc->max; | 1885 | int max = mc->max; |
1883 | 1886 | ||
1884 | if (max == 1) | 1887 | if (max == 1 && !strstr(kcontrol->id.name, " Volume")) |
1885 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | 1888 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; |
1886 | else | 1889 | else |
1887 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 1890 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
@@ -2065,7 +2068,7 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8); | |||
2065 | int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, | 2068 | int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, |
2066 | unsigned int freq, int dir) | 2069 | unsigned int freq, int dir) |
2067 | { | 2070 | { |
2068 | if (dai->ops->set_sysclk) | 2071 | if (dai->ops && dai->ops->set_sysclk) |
2069 | return dai->ops->set_sysclk(dai, clk_id, freq, dir); | 2072 | return dai->ops->set_sysclk(dai, clk_id, freq, dir); |
2070 | else | 2073 | else |
2071 | return -EINVAL; | 2074 | return -EINVAL; |
@@ -2085,7 +2088,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); | |||
2085 | int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, | 2088 | int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, |
2086 | int div_id, int div) | 2089 | int div_id, int div) |
2087 | { | 2090 | { |
2088 | if (dai->ops->set_clkdiv) | 2091 | if (dai->ops && dai->ops->set_clkdiv) |
2089 | return dai->ops->set_clkdiv(dai, div_id, div); | 2092 | return dai->ops->set_clkdiv(dai, div_id, div); |
2090 | else | 2093 | else |
2091 | return -EINVAL; | 2094 | return -EINVAL; |
@@ -2104,7 +2107,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv); | |||
2104 | int snd_soc_dai_set_pll(struct snd_soc_dai *dai, | 2107 | int snd_soc_dai_set_pll(struct snd_soc_dai *dai, |
2105 | int pll_id, unsigned int freq_in, unsigned int freq_out) | 2108 | int pll_id, unsigned int freq_in, unsigned int freq_out) |
2106 | { | 2109 | { |
2107 | if (dai->ops->set_pll) | 2110 | if (dai->ops && dai->ops->set_pll) |
2108 | return dai->ops->set_pll(dai, pll_id, freq_in, freq_out); | 2111 | return dai->ops->set_pll(dai, pll_id, freq_in, freq_out); |
2109 | else | 2112 | else |
2110 | return -EINVAL; | 2113 | return -EINVAL; |
@@ -2120,7 +2123,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); | |||
2120 | */ | 2123 | */ |
2121 | int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | 2124 | int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
2122 | { | 2125 | { |
2123 | if (dai->ops->set_fmt) | 2126 | if (dai->ops && dai->ops->set_fmt) |
2124 | return dai->ops->set_fmt(dai, fmt); | 2127 | return dai->ops->set_fmt(dai, fmt); |
2125 | else | 2128 | else |
2126 | return -EINVAL; | 2129 | return -EINVAL; |
@@ -2139,7 +2142,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt); | |||
2139 | int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, | 2142 | int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, |
2140 | unsigned int mask, int slots) | 2143 | unsigned int mask, int slots) |
2141 | { | 2144 | { |
2142 | if (dai->ops->set_sysclk) | 2145 | if (dai->ops && dai->ops->set_tdm_slot) |
2143 | return dai->ops->set_tdm_slot(dai, mask, slots); | 2146 | return dai->ops->set_tdm_slot(dai, mask, slots); |
2144 | else | 2147 | else |
2145 | return -EINVAL; | 2148 | return -EINVAL; |
@@ -2155,7 +2158,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); | |||
2155 | */ | 2158 | */ |
2156 | int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate) | 2159 | int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate) |
2157 | { | 2160 | { |
2158 | if (dai->ops->set_sysclk) | 2161 | if (dai->ops && dai->ops->set_tristate) |
2159 | return dai->ops->set_tristate(dai, tristate); | 2162 | return dai->ops->set_tristate(dai, tristate); |
2160 | else | 2163 | else |
2161 | return -EINVAL; | 2164 | return -EINVAL; |
@@ -2171,7 +2174,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate); | |||
2171 | */ | 2174 | */ |
2172 | int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute) | 2175 | int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute) |
2173 | { | 2176 | { |
2174 | if (dai->ops->digital_mute) | 2177 | if (dai->ops && dai->ops->digital_mute) |
2175 | return dai->ops->digital_mute(dai, mute); | 2178 | return dai->ops->digital_mute(dai, mute); |
2176 | else | 2179 | else |
2177 | return -EINVAL; | 2180 | return -EINVAL; |
@@ -2352,6 +2355,39 @@ void snd_soc_unregister_platform(struct snd_soc_platform *platform) | |||
2352 | } | 2355 | } |
2353 | EXPORT_SYMBOL_GPL(snd_soc_unregister_platform); | 2356 | EXPORT_SYMBOL_GPL(snd_soc_unregister_platform); |
2354 | 2357 | ||
2358 | static u64 codec_format_map[] = { | ||
2359 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE, | ||
2360 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE, | ||
2361 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE, | ||
2362 | SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE, | ||
2363 | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE, | ||
2364 | SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE, | ||
2365 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3BE, | ||
2366 | SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_U24_3BE, | ||
2367 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE, | ||
2368 | SNDRV_PCM_FMTBIT_U20_3LE | SNDRV_PCM_FMTBIT_U20_3BE, | ||
2369 | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE, | ||
2370 | SNDRV_PCM_FMTBIT_U18_3LE | SNDRV_PCM_FMTBIT_U18_3BE, | ||
2371 | SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE, | ||
2372 | SNDRV_PCM_FMTBIT_FLOAT64_LE | SNDRV_PCM_FMTBIT_FLOAT64_BE, | ||
2373 | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE | ||
2374 | | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE, | ||
2375 | }; | ||
2376 | |||
2377 | /* Fix up the DAI formats for endianness: codecs don't actually see | ||
2378 | * the endianness of the data but we're using the CPU format | ||
2379 | * definitions which do need to include endianness so we ensure that | ||
2380 | * codec DAIs always have both big and little endian variants set. | ||
2381 | */ | ||
2382 | static void fixup_codec_formats(struct snd_soc_pcm_stream *stream) | ||
2383 | { | ||
2384 | int i; | ||
2385 | |||
2386 | for (i = 0; i < ARRAY_SIZE(codec_format_map); i++) | ||
2387 | if (stream->formats & codec_format_map[i]) | ||
2388 | stream->formats |= codec_format_map[i]; | ||
2389 | } | ||
2390 | |||
2355 | /** | 2391 | /** |
2356 | * snd_soc_register_codec - Register a codec with the ASoC core | 2392 | * snd_soc_register_codec - Register a codec with the ASoC core |
2357 | * | 2393 | * |
@@ -2359,6 +2395,8 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_platform); | |||
2359 | */ | 2395 | */ |
2360 | int snd_soc_register_codec(struct snd_soc_codec *codec) | 2396 | int snd_soc_register_codec(struct snd_soc_codec *codec) |
2361 | { | 2397 | { |
2398 | int i; | ||
2399 | |||
2362 | if (!codec->name) | 2400 | if (!codec->name) |
2363 | return -EINVAL; | 2401 | return -EINVAL; |
2364 | 2402 | ||
@@ -2368,6 +2406,11 @@ int snd_soc_register_codec(struct snd_soc_codec *codec) | |||
2368 | 2406 | ||
2369 | INIT_LIST_HEAD(&codec->list); | 2407 | INIT_LIST_HEAD(&codec->list); |
2370 | 2408 | ||
2409 | for (i = 0; i < codec->num_dai; i++) { | ||
2410 | fixup_codec_formats(&codec->dai[i].playback); | ||
2411 | fixup_codec_formats(&codec->dai[i].capture); | ||
2412 | } | ||
2413 | |||
2371 | mutex_lock(&client_mutex); | 2414 | mutex_lock(&client_mutex); |
2372 | list_add(&codec->list, &codec_list); | 2415 | list_add(&codec->list, &codec_list); |
2373 | snd_soc_instantiate_cards(); | 2416 | snd_soc_instantiate_cards(); |