diff options
Diffstat (limited to 'sound/soc/soc-core.c')
-rw-r--r-- | sound/soc/soc-core.c | 255 |
1 files changed, 209 insertions, 46 deletions
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ad7f9528d751..998569d60330 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c | |||
@@ -316,7 +316,7 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream) | |||
316 | 316 | ||
317 | if (codec_dai->symmetric_rates || cpu_dai->symmetric_rates || | 317 | if (codec_dai->symmetric_rates || cpu_dai->symmetric_rates || |
318 | machine->symmetric_rates) { | 318 | machine->symmetric_rates) { |
319 | dev_dbg(card->dev, "Symmetry forces %dHz rate\n", | 319 | dev_dbg(card->dev, "Symmetry forces %dHz rate\n", |
320 | machine->rate); | 320 | machine->rate); |
321 | 321 | ||
322 | ret = snd_pcm_hw_constraint_minmax(substream->runtime, | 322 | ret = snd_pcm_hw_constraint_minmax(substream->runtime, |
@@ -405,6 +405,12 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) | |||
405 | codec_dai->playback.formats & cpu_dai->playback.formats; | 405 | codec_dai->playback.formats & cpu_dai->playback.formats; |
406 | runtime->hw.rates = | 406 | runtime->hw.rates = |
407 | codec_dai->playback.rates & cpu_dai->playback.rates; | 407 | codec_dai->playback.rates & cpu_dai->playback.rates; |
408 | if (codec_dai->playback.rates | ||
409 | & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) | ||
410 | runtime->hw.rates |= cpu_dai->playback.rates; | ||
411 | if (cpu_dai->playback.rates | ||
412 | & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) | ||
413 | runtime->hw.rates |= codec_dai->playback.rates; | ||
408 | } else { | 414 | } else { |
409 | runtime->hw.rate_min = | 415 | runtime->hw.rate_min = |
410 | max(codec_dai->capture.rate_min, | 416 | max(codec_dai->capture.rate_min, |
@@ -422,6 +428,12 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) | |||
422 | codec_dai->capture.formats & cpu_dai->capture.formats; | 428 | codec_dai->capture.formats & cpu_dai->capture.formats; |
423 | runtime->hw.rates = | 429 | runtime->hw.rates = |
424 | codec_dai->capture.rates & cpu_dai->capture.rates; | 430 | codec_dai->capture.rates & cpu_dai->capture.rates; |
431 | if (codec_dai->capture.rates | ||
432 | & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) | ||
433 | runtime->hw.rates |= cpu_dai->capture.rates; | ||
434 | if (cpu_dai->capture.rates | ||
435 | & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) | ||
436 | runtime->hw.rates |= codec_dai->capture.rates; | ||
425 | } | 437 | } |
426 | 438 | ||
427 | snd_pcm_limit_hw_rates(runtime); | 439 | snd_pcm_limit_hw_rates(runtime); |
@@ -455,12 +467,15 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) | |||
455 | pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min, | 467 | pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min, |
456 | runtime->hw.rate_max); | 468 | runtime->hw.rate_max); |
457 | 469 | ||
458 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 470 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
459 | cpu_dai->playback.active = codec_dai->playback.active = 1; | 471 | cpu_dai->playback.active++; |
460 | else | 472 | codec_dai->playback.active++; |
461 | cpu_dai->capture.active = codec_dai->capture.active = 1; | 473 | } else { |
462 | cpu_dai->active = codec_dai->active = 1; | 474 | cpu_dai->capture.active++; |
463 | cpu_dai->runtime = runtime; | 475 | codec_dai->capture.active++; |
476 | } | ||
477 | cpu_dai->active++; | ||
478 | codec_dai->active++; | ||
464 | card->codec->active++; | 479 | card->codec->active++; |
465 | mutex_unlock(&pcm_mutex); | 480 | mutex_unlock(&pcm_mutex); |
466 | return 0; | 481 | return 0; |
@@ -536,15 +551,16 @@ static int soc_codec_close(struct snd_pcm_substream *substream) | |||
536 | 551 | ||
537 | mutex_lock(&pcm_mutex); | 552 | mutex_lock(&pcm_mutex); |
538 | 553 | ||
539 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 554 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
540 | cpu_dai->playback.active = codec_dai->playback.active = 0; | 555 | cpu_dai->playback.active--; |
541 | else | 556 | codec_dai->playback.active--; |
542 | cpu_dai->capture.active = codec_dai->capture.active = 0; | 557 | } else { |
543 | 558 | cpu_dai->capture.active--; | |
544 | if (codec_dai->playback.active == 0 && | 559 | codec_dai->capture.active--; |
545 | codec_dai->capture.active == 0) { | ||
546 | cpu_dai->active = codec_dai->active = 0; | ||
547 | } | 560 | } |
561 | |||
562 | cpu_dai->active--; | ||
563 | codec_dai->active--; | ||
548 | codec->active--; | 564 | codec->active--; |
549 | 565 | ||
550 | /* Muting the DAC suppresses artifacts caused during digital | 566 | /* Muting the DAC suppresses artifacts caused during digital |
@@ -564,7 +580,6 @@ static int soc_codec_close(struct snd_pcm_substream *substream) | |||
564 | 580 | ||
565 | if (platform->pcm_ops->close) | 581 | if (platform->pcm_ops->close) |
566 | platform->pcm_ops->close(substream); | 582 | platform->pcm_ops->close(substream); |
567 | cpu_dai->runtime = NULL; | ||
568 | 583 | ||
569 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 584 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
570 | /* start delayed pop wq here for playback streams */ | 585 | /* start delayed pop wq here for playback streams */ |
@@ -802,6 +817,41 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |||
802 | return 0; | 817 | return 0; |
803 | } | 818 | } |
804 | 819 | ||
820 | /* | ||
821 | * soc level wrapper for pointer callback | ||
822 | * If cpu_dai, codec_dai, platform driver has the delay callback, than | ||
823 | * the runtime->delay will be updated accordingly. | ||
824 | */ | ||
825 | static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) | ||
826 | { | ||
827 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
828 | struct snd_soc_device *socdev = rtd->socdev; | ||
829 | struct snd_soc_card *card = socdev->card; | ||
830 | struct snd_soc_platform *platform = card->platform; | ||
831 | struct snd_soc_dai_link *machine = rtd->dai; | ||
832 | struct snd_soc_dai *cpu_dai = machine->cpu_dai; | ||
833 | struct snd_soc_dai *codec_dai = machine->codec_dai; | ||
834 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
835 | snd_pcm_uframes_t offset = 0; | ||
836 | snd_pcm_sframes_t delay = 0; | ||
837 | |||
838 | if (platform->pcm_ops->pointer) | ||
839 | offset = platform->pcm_ops->pointer(substream); | ||
840 | |||
841 | if (cpu_dai->ops->delay) | ||
842 | delay += cpu_dai->ops->delay(substream, cpu_dai); | ||
843 | |||
844 | if (codec_dai->ops->delay) | ||
845 | delay += codec_dai->ops->delay(substream, codec_dai); | ||
846 | |||
847 | if (platform->delay) | ||
848 | delay += platform->delay(substream, codec_dai); | ||
849 | |||
850 | runtime->delay = delay; | ||
851 | |||
852 | return offset; | ||
853 | } | ||
854 | |||
805 | /* ASoC PCM operations */ | 855 | /* ASoC PCM operations */ |
806 | static struct snd_pcm_ops soc_pcm_ops = { | 856 | static struct snd_pcm_ops soc_pcm_ops = { |
807 | .open = soc_pcm_open, | 857 | .open = soc_pcm_open, |
@@ -810,6 +860,7 @@ static struct snd_pcm_ops soc_pcm_ops = { | |||
810 | .hw_free = soc_pcm_hw_free, | 860 | .hw_free = soc_pcm_hw_free, |
811 | .prepare = soc_pcm_prepare, | 861 | .prepare = soc_pcm_prepare, |
812 | .trigger = soc_pcm_trigger, | 862 | .trigger = soc_pcm_trigger, |
863 | .pointer = soc_pcm_pointer, | ||
813 | }; | 864 | }; |
814 | 865 | ||
815 | #ifdef CONFIG_PM | 866 | #ifdef CONFIG_PM |
@@ -843,23 +894,35 @@ static int soc_suspend(struct device *dev) | |||
843 | /* mute any active DAC's */ | 894 | /* mute any active DAC's */ |
844 | for (i = 0; i < card->num_links; i++) { | 895 | for (i = 0; i < card->num_links; i++) { |
845 | struct snd_soc_dai *dai = card->dai_link[i].codec_dai; | 896 | struct snd_soc_dai *dai = card->dai_link[i].codec_dai; |
897 | |||
898 | if (card->dai_link[i].ignore_suspend) | ||
899 | continue; | ||
900 | |||
846 | if (dai->ops->digital_mute && dai->playback.active) | 901 | if (dai->ops->digital_mute && dai->playback.active) |
847 | dai->ops->digital_mute(dai, 1); | 902 | dai->ops->digital_mute(dai, 1); |
848 | } | 903 | } |
849 | 904 | ||
850 | /* suspend all pcms */ | 905 | /* suspend all pcms */ |
851 | for (i = 0; i < card->num_links; i++) | 906 | for (i = 0; i < card->num_links; i++) { |
907 | if (card->dai_link[i].ignore_suspend) | ||
908 | continue; | ||
909 | |||
852 | snd_pcm_suspend_all(card->dai_link[i].pcm); | 910 | snd_pcm_suspend_all(card->dai_link[i].pcm); |
911 | } | ||
853 | 912 | ||
854 | if (card->suspend_pre) | 913 | if (card->suspend_pre) |
855 | card->suspend_pre(pdev, PMSG_SUSPEND); | 914 | card->suspend_pre(pdev, PMSG_SUSPEND); |
856 | 915 | ||
857 | for (i = 0; i < card->num_links; i++) { | 916 | for (i = 0; i < card->num_links; i++) { |
858 | struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai; | 917 | struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai; |
918 | |||
919 | if (card->dai_link[i].ignore_suspend) | ||
920 | continue; | ||
921 | |||
859 | if (cpu_dai->suspend && !cpu_dai->ac97_control) | 922 | if (cpu_dai->suspend && !cpu_dai->ac97_control) |
860 | cpu_dai->suspend(cpu_dai); | 923 | cpu_dai->suspend(cpu_dai); |
861 | if (platform->suspend) | 924 | if (platform->suspend) |
862 | platform->suspend(cpu_dai); | 925 | platform->suspend(&card->dai_link[i]); |
863 | } | 926 | } |
864 | 927 | ||
865 | /* close any waiting streams and save state */ | 928 | /* close any waiting streams and save state */ |
@@ -868,6 +931,10 @@ static int soc_suspend(struct device *dev) | |||
868 | 931 | ||
869 | for (i = 0; i < codec->num_dai; i++) { | 932 | for (i = 0; i < codec->num_dai; i++) { |
870 | char *stream = codec->dai[i].playback.stream_name; | 933 | char *stream = codec->dai[i].playback.stream_name; |
934 | |||
935 | if (card->dai_link[i].ignore_suspend) | ||
936 | continue; | ||
937 | |||
871 | if (stream != NULL) | 938 | if (stream != NULL) |
872 | snd_soc_dapm_stream_event(codec, stream, | 939 | snd_soc_dapm_stream_event(codec, stream, |
873 | SND_SOC_DAPM_STREAM_SUSPEND); | 940 | SND_SOC_DAPM_STREAM_SUSPEND); |
@@ -877,11 +944,26 @@ static int soc_suspend(struct device *dev) | |||
877 | SND_SOC_DAPM_STREAM_SUSPEND); | 944 | SND_SOC_DAPM_STREAM_SUSPEND); |
878 | } | 945 | } |
879 | 946 | ||
880 | if (codec_dev->suspend) | 947 | /* If there are paths active then the CODEC will be held with |
881 | codec_dev->suspend(pdev, PMSG_SUSPEND); | 948 | * bias _ON and should not be suspended. */ |
949 | if (codec_dev->suspend) { | ||
950 | switch (codec->bias_level) { | ||
951 | case SND_SOC_BIAS_STANDBY: | ||
952 | case SND_SOC_BIAS_OFF: | ||
953 | codec_dev->suspend(pdev, PMSG_SUSPEND); | ||
954 | break; | ||
955 | default: | ||
956 | dev_dbg(socdev->dev, "CODEC is on over suspend\n"); | ||
957 | break; | ||
958 | } | ||
959 | } | ||
882 | 960 | ||
883 | for (i = 0; i < card->num_links; i++) { | 961 | for (i = 0; i < card->num_links; i++) { |
884 | struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai; | 962 | struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai; |
963 | |||
964 | if (card->dai_link[i].ignore_suspend) | ||
965 | continue; | ||
966 | |||
885 | if (cpu_dai->suspend && cpu_dai->ac97_control) | 967 | if (cpu_dai->suspend && cpu_dai->ac97_control) |
886 | cpu_dai->suspend(cpu_dai); | 968 | cpu_dai->suspend(cpu_dai); |
887 | } | 969 | } |
@@ -913,20 +995,44 @@ static void soc_resume_deferred(struct work_struct *work) | |||
913 | 995 | ||
914 | dev_dbg(socdev->dev, "starting resume work\n"); | 996 | dev_dbg(socdev->dev, "starting resume work\n"); |
915 | 997 | ||
998 | /* Bring us up into D2 so that DAPM starts enabling things */ | ||
999 | snd_power_change_state(codec->card, SNDRV_CTL_POWER_D2); | ||
1000 | |||
916 | if (card->resume_pre) | 1001 | if (card->resume_pre) |
917 | card->resume_pre(pdev); | 1002 | card->resume_pre(pdev); |
918 | 1003 | ||
919 | for (i = 0; i < card->num_links; i++) { | 1004 | for (i = 0; i < card->num_links; i++) { |
920 | struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai; | 1005 | struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai; |
1006 | |||
1007 | if (card->dai_link[i].ignore_suspend) | ||
1008 | continue; | ||
1009 | |||
921 | if (cpu_dai->resume && cpu_dai->ac97_control) | 1010 | if (cpu_dai->resume && cpu_dai->ac97_control) |
922 | cpu_dai->resume(cpu_dai); | 1011 | cpu_dai->resume(cpu_dai); |
923 | } | 1012 | } |
924 | 1013 | ||
925 | if (codec_dev->resume) | 1014 | /* If the CODEC was idle over suspend then it will have been |
926 | codec_dev->resume(pdev); | 1015 | * left with bias OFF or STANDBY and suspended so we must now |
1016 | * resume. Otherwise the suspend was suppressed. | ||
1017 | */ | ||
1018 | if (codec_dev->resume) { | ||
1019 | switch (codec->bias_level) { | ||
1020 | case SND_SOC_BIAS_STANDBY: | ||
1021 | case SND_SOC_BIAS_OFF: | ||
1022 | codec_dev->resume(pdev); | ||
1023 | break; | ||
1024 | default: | ||
1025 | dev_dbg(socdev->dev, "CODEC was on over suspend\n"); | ||
1026 | break; | ||
1027 | } | ||
1028 | } | ||
927 | 1029 | ||
928 | for (i = 0; i < codec->num_dai; i++) { | 1030 | for (i = 0; i < codec->num_dai; i++) { |
929 | char *stream = codec->dai[i].playback.stream_name; | 1031 | char *stream = codec->dai[i].playback.stream_name; |
1032 | |||
1033 | if (card->dai_link[i].ignore_suspend) | ||
1034 | continue; | ||
1035 | |||
930 | if (stream != NULL) | 1036 | if (stream != NULL) |
931 | snd_soc_dapm_stream_event(codec, stream, | 1037 | snd_soc_dapm_stream_event(codec, stream, |
932 | SND_SOC_DAPM_STREAM_RESUME); | 1038 | SND_SOC_DAPM_STREAM_RESUME); |
@@ -939,16 +1045,24 @@ static void soc_resume_deferred(struct work_struct *work) | |||
939 | /* unmute any active DACs */ | 1045 | /* unmute any active DACs */ |
940 | for (i = 0; i < card->num_links; i++) { | 1046 | for (i = 0; i < card->num_links; i++) { |
941 | struct snd_soc_dai *dai = card->dai_link[i].codec_dai; | 1047 | struct snd_soc_dai *dai = card->dai_link[i].codec_dai; |
1048 | |||
1049 | if (card->dai_link[i].ignore_suspend) | ||
1050 | continue; | ||
1051 | |||
942 | if (dai->ops->digital_mute && dai->playback.active) | 1052 | if (dai->ops->digital_mute && dai->playback.active) |
943 | dai->ops->digital_mute(dai, 0); | 1053 | dai->ops->digital_mute(dai, 0); |
944 | } | 1054 | } |
945 | 1055 | ||
946 | for (i = 0; i < card->num_links; i++) { | 1056 | for (i = 0; i < card->num_links; i++) { |
947 | struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai; | 1057 | struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai; |
1058 | |||
1059 | if (card->dai_link[i].ignore_suspend) | ||
1060 | continue; | ||
1061 | |||
948 | if (cpu_dai->resume && !cpu_dai->ac97_control) | 1062 | if (cpu_dai->resume && !cpu_dai->ac97_control) |
949 | cpu_dai->resume(cpu_dai); | 1063 | cpu_dai->resume(cpu_dai); |
950 | if (platform->resume) | 1064 | if (platform->resume) |
951 | platform->resume(cpu_dai); | 1065 | platform->resume(&card->dai_link[i]); |
952 | } | 1066 | } |
953 | 1067 | ||
954 | if (card->resume_post) | 1068 | if (card->resume_post) |
@@ -1233,26 +1347,25 @@ static int soc_remove(struct platform_device *pdev) | |||
1233 | struct snd_soc_platform *platform = card->platform; | 1347 | struct snd_soc_platform *platform = card->platform; |
1234 | struct snd_soc_codec_device *codec_dev = socdev->codec_dev; | 1348 | struct snd_soc_codec_device *codec_dev = socdev->codec_dev; |
1235 | 1349 | ||
1236 | if (!card->instantiated) | 1350 | if (card->instantiated) { |
1237 | return 0; | 1351 | run_delayed_work(&card->delayed_work); |
1238 | 1352 | ||
1239 | run_delayed_work(&card->delayed_work); | 1353 | if (platform->remove) |
1354 | platform->remove(pdev); | ||
1240 | 1355 | ||
1241 | if (platform->remove) | 1356 | if (codec_dev->remove) |
1242 | platform->remove(pdev); | 1357 | codec_dev->remove(pdev); |
1243 | 1358 | ||
1244 | if (codec_dev->remove) | 1359 | for (i = 0; i < card->num_links; i++) { |
1245 | codec_dev->remove(pdev); | 1360 | struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai; |
1361 | if (cpu_dai->remove) | ||
1362 | cpu_dai->remove(pdev, cpu_dai); | ||
1363 | } | ||
1246 | 1364 | ||
1247 | for (i = 0; i < card->num_links; i++) { | 1365 | if (card->remove) |
1248 | struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai; | 1366 | card->remove(pdev); |
1249 | if (cpu_dai->remove) | ||
1250 | cpu_dai->remove(pdev, cpu_dai); | ||
1251 | } | 1367 | } |
1252 | 1368 | ||
1253 | if (card->remove) | ||
1254 | card->remove(pdev); | ||
1255 | |||
1256 | snd_soc_unregister_card(card); | 1369 | snd_soc_unregister_card(card); |
1257 | 1370 | ||
1258 | return 0; | 1371 | return 0; |
@@ -1336,7 +1449,6 @@ static int soc_new_pcm(struct snd_soc_device *socdev, | |||
1336 | dai_link->pcm = pcm; | 1449 | dai_link->pcm = pcm; |
1337 | pcm->private_data = rtd; | 1450 | pcm->private_data = rtd; |
1338 | soc_pcm_ops.mmap = platform->pcm_ops->mmap; | 1451 | soc_pcm_ops.mmap = platform->pcm_ops->mmap; |
1339 | soc_pcm_ops.pointer = platform->pcm_ops->pointer; | ||
1340 | soc_pcm_ops.ioctl = platform->pcm_ops->ioctl; | 1452 | soc_pcm_ops.ioctl = platform->pcm_ops->ioctl; |
1341 | soc_pcm_ops.copy = platform->pcm_ops->copy; | 1453 | soc_pcm_ops.copy = platform->pcm_ops->copy; |
1342 | soc_pcm_ops.silence = platform->pcm_ops->silence; | 1454 | soc_pcm_ops.silence = platform->pcm_ops->silence; |
@@ -1906,18 +2018,22 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, | |||
1906 | { | 2018 | { |
1907 | struct soc_mixer_control *mc = | 2019 | struct soc_mixer_control *mc = |
1908 | (struct soc_mixer_control *)kcontrol->private_value; | 2020 | (struct soc_mixer_control *)kcontrol->private_value; |
1909 | int max = mc->max; | 2021 | int platform_max; |
1910 | unsigned int shift = mc->shift; | 2022 | unsigned int shift = mc->shift; |
1911 | unsigned int rshift = mc->rshift; | 2023 | unsigned int rshift = mc->rshift; |
1912 | 2024 | ||
1913 | if (max == 1 && !strstr(kcontrol->id.name, " Volume")) | 2025 | if (!mc->platform_max) |
2026 | mc->platform_max = mc->max; | ||
2027 | platform_max = mc->platform_max; | ||
2028 | |||
2029 | if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume")) | ||
1914 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | 2030 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; |
1915 | else | 2031 | else |
1916 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 2032 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
1917 | 2033 | ||
1918 | uinfo->count = shift == rshift ? 1 : 2; | 2034 | uinfo->count = shift == rshift ? 1 : 2; |
1919 | uinfo->value.integer.min = 0; | 2035 | uinfo->value.integer.min = 0; |
1920 | uinfo->value.integer.max = max; | 2036 | uinfo->value.integer.max = platform_max; |
1921 | return 0; | 2037 | return 0; |
1922 | } | 2038 | } |
1923 | EXPORT_SYMBOL_GPL(snd_soc_info_volsw); | 2039 | EXPORT_SYMBOL_GPL(snd_soc_info_volsw); |
@@ -2015,16 +2131,20 @@ int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol, | |||
2015 | { | 2131 | { |
2016 | struct soc_mixer_control *mc = | 2132 | struct soc_mixer_control *mc = |
2017 | (struct soc_mixer_control *)kcontrol->private_value; | 2133 | (struct soc_mixer_control *)kcontrol->private_value; |
2018 | int max = mc->max; | 2134 | int platform_max; |
2019 | 2135 | ||
2020 | if (max == 1 && !strstr(kcontrol->id.name, " Volume")) | 2136 | if (!mc->platform_max) |
2137 | mc->platform_max = mc->max; | ||
2138 | platform_max = mc->platform_max; | ||
2139 | |||
2140 | if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume")) | ||
2021 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | 2141 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; |
2022 | else | 2142 | else |
2023 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 2143 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
2024 | 2144 | ||
2025 | uinfo->count = 2; | 2145 | uinfo->count = 2; |
2026 | uinfo->value.integer.min = 0; | 2146 | uinfo->value.integer.min = 0; |
2027 | uinfo->value.integer.max = max; | 2147 | uinfo->value.integer.max = platform_max; |
2028 | return 0; | 2148 | return 0; |
2029 | } | 2149 | } |
2030 | EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r); | 2150 | EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r); |
@@ -2125,13 +2245,17 @@ int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol, | |||
2125 | { | 2245 | { |
2126 | struct soc_mixer_control *mc = | 2246 | struct soc_mixer_control *mc = |
2127 | (struct soc_mixer_control *)kcontrol->private_value; | 2247 | (struct soc_mixer_control *)kcontrol->private_value; |
2128 | int max = mc->max; | 2248 | int platform_max; |
2129 | int min = mc->min; | 2249 | int min = mc->min; |
2130 | 2250 | ||
2251 | if (!mc->platform_max) | ||
2252 | mc->platform_max = mc->max; | ||
2253 | platform_max = mc->platform_max; | ||
2254 | |||
2131 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 2255 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
2132 | uinfo->count = 2; | 2256 | uinfo->count = 2; |
2133 | uinfo->value.integer.min = 0; | 2257 | uinfo->value.integer.min = 0; |
2134 | uinfo->value.integer.max = max-min; | 2258 | uinfo->value.integer.max = platform_max - min; |
2135 | return 0; | 2259 | return 0; |
2136 | } | 2260 | } |
2137 | EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8); | 2261 | EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8); |
@@ -2190,6 +2314,45 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, | |||
2190 | EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8); | 2314 | EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8); |
2191 | 2315 | ||
2192 | /** | 2316 | /** |
2317 | * snd_soc_limit_volume - Set new limit to an existing volume control. | ||
2318 | * | ||
2319 | * @codec: where to look for the control | ||
2320 | * @name: Name of the control | ||
2321 | * @max: new maximum limit | ||
2322 | * | ||
2323 | * Return 0 for success, else error. | ||
2324 | */ | ||
2325 | int snd_soc_limit_volume(struct snd_soc_codec *codec, | ||
2326 | const char *name, int max) | ||
2327 | { | ||
2328 | struct snd_card *card = codec->card; | ||
2329 | struct snd_kcontrol *kctl; | ||
2330 | struct soc_mixer_control *mc; | ||
2331 | int found = 0; | ||
2332 | int ret = -EINVAL; | ||
2333 | |||
2334 | /* Sanity check for name and max */ | ||
2335 | if (unlikely(!name || max <= 0)) | ||
2336 | return -EINVAL; | ||
2337 | |||
2338 | list_for_each_entry(kctl, &card->controls, list) { | ||
2339 | if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) { | ||
2340 | found = 1; | ||
2341 | break; | ||
2342 | } | ||
2343 | } | ||
2344 | if (found) { | ||
2345 | mc = (struct soc_mixer_control *)kctl->private_value; | ||
2346 | if (max <= mc->max) { | ||
2347 | mc->platform_max = max; | ||
2348 | ret = 0; | ||
2349 | } | ||
2350 | } | ||
2351 | return ret; | ||
2352 | } | ||
2353 | EXPORT_SYMBOL_GPL(snd_soc_limit_volume); | ||
2354 | |||
2355 | /** | ||
2193 | * snd_soc_dai_set_sysclk - configure DAI system or master clock. | 2356 | * snd_soc_dai_set_sysclk - configure DAI system or master clock. |
2194 | * @dai: DAI | 2357 | * @dai: DAI |
2195 | * @clk_id: DAI specific clock ID | 2358 | * @clk_id: DAI specific clock ID |