diff options
| -rw-r--r-- | include/sound/soc-dai.h | 6 | ||||
| -rw-r--r-- | include/sound/soc.h | 2 | ||||
| -rw-r--r-- | sound/soc/soc-pcm.c | 151 |
3 files changed, 138 insertions, 21 deletions
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 800c101bb096..243d3b689699 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h | |||
| @@ -220,6 +220,8 @@ struct snd_soc_dai_driver { | |||
| 220 | struct snd_soc_pcm_stream capture; | 220 | struct snd_soc_pcm_stream capture; |
| 221 | struct snd_soc_pcm_stream playback; | 221 | struct snd_soc_pcm_stream playback; |
| 222 | unsigned int symmetric_rates:1; | 222 | unsigned int symmetric_rates:1; |
| 223 | unsigned int symmetric_channels:1; | ||
| 224 | unsigned int symmetric_samplebits:1; | ||
| 223 | 225 | ||
| 224 | /* probe ordering - for components with runtime dependencies */ | 226 | /* probe ordering - for components with runtime dependencies */ |
| 225 | int probe_order; | 227 | int probe_order; |
| @@ -244,6 +246,8 @@ struct snd_soc_dai { | |||
| 244 | unsigned int capture_active:1; /* stream is in use */ | 246 | unsigned int capture_active:1; /* stream is in use */ |
| 245 | unsigned int playback_active:1; /* stream is in use */ | 247 | unsigned int playback_active:1; /* stream is in use */ |
| 246 | unsigned int symmetric_rates:1; | 248 | unsigned int symmetric_rates:1; |
| 249 | unsigned int symmetric_channels:1; | ||
| 250 | unsigned int symmetric_samplebits:1; | ||
| 247 | struct snd_pcm_runtime *runtime; | 251 | struct snd_pcm_runtime *runtime; |
| 248 | unsigned int active; | 252 | unsigned int active; |
| 249 | unsigned char probed:1; | 253 | unsigned char probed:1; |
| @@ -258,6 +262,8 @@ struct snd_soc_dai { | |||
| 258 | 262 | ||
| 259 | /* Symmetry data - only valid if symmetry is being enforced */ | 263 | /* Symmetry data - only valid if symmetry is being enforced */ |
| 260 | unsigned int rate; | 264 | unsigned int rate; |
| 265 | unsigned int channels; | ||
| 266 | unsigned int sample_bits; | ||
| 261 | 267 | ||
| 262 | /* parent platform/codec */ | 268 | /* parent platform/codec */ |
| 263 | struct snd_soc_platform *platform; | 269 | struct snd_soc_platform *platform; |
diff --git a/include/sound/soc.h b/include/sound/soc.h index 1f741cb24f33..1cda7d343d16 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h | |||
| @@ -879,6 +879,8 @@ struct snd_soc_dai_link { | |||
| 879 | 879 | ||
| 880 | /* Symmetry requirements */ | 880 | /* Symmetry requirements */ |
| 881 | unsigned int symmetric_rates:1; | 881 | unsigned int symmetric_rates:1; |
| 882 | unsigned int symmetric_channels:1; | ||
| 883 | unsigned int symmetric_samplebits:1; | ||
| 882 | 884 | ||
| 883 | /* Do not create a PCM for this DAI link (Backend link) */ | 885 | /* Do not create a PCM for this DAI link (Backend link) */ |
| 884 | unsigned int no_pcm:1; | 886 | unsigned int no_pcm:1; |
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 89d594138773..10f29a0ad5a6 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c | |||
| @@ -84,35 +84,117 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, | |||
| 84 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 84 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| 85 | int ret; | 85 | int ret; |
| 86 | 86 | ||
| 87 | if (!soc_dai->driver->symmetric_rates && | 87 | if (soc_dai->rate && (soc_dai->driver->symmetric_rates || |
| 88 | !rtd->dai_link->symmetric_rates) | 88 | rtd->dai_link->symmetric_rates)) { |
| 89 | return 0; | 89 | dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", |
| 90 | soc_dai->rate); | ||
| 91 | |||
| 92 | ret = snd_pcm_hw_constraint_minmax(substream->runtime, | ||
| 93 | SNDRV_PCM_HW_PARAM_RATE, | ||
| 94 | soc_dai->rate, soc_dai->rate); | ||
| 95 | if (ret < 0) { | ||
| 96 | dev_err(soc_dai->dev, | ||
| 97 | "ASoC: Unable to apply rate constraint: %d\n", | ||
| 98 | ret); | ||
| 99 | return ret; | ||
| 100 | } | ||
| 101 | } | ||
| 90 | 102 | ||
| 91 | /* This can happen if multiple streams are starting simultaneously - | 103 | if (soc_dai->channels && (soc_dai->driver->symmetric_channels || |
| 92 | * the second can need to get its constraints before the first has | 104 | rtd->dai_link->symmetric_channels)) { |
| 93 | * picked a rate. Complain and allow the application to carry on. | 105 | dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n", |
| 94 | */ | 106 | soc_dai->channels); |
| 95 | if (!soc_dai->rate) { | 107 | |
| 96 | dev_warn(soc_dai->dev, | 108 | ret = snd_pcm_hw_constraint_minmax(substream->runtime, |
| 97 | "ASoC: Not enforcing symmetric_rates due to race\n"); | 109 | SNDRV_PCM_HW_PARAM_CHANNELS, |
| 98 | return 0; | 110 | soc_dai->channels, |
| 111 | soc_dai->channels); | ||
| 112 | if (ret < 0) { | ||
| 113 | dev_err(soc_dai->dev, | ||
| 114 | "ASoC: Unable to apply channel symmetry constraint: %d\n", | ||
| 115 | ret); | ||
| 116 | return ret; | ||
| 117 | } | ||
| 99 | } | 118 | } |
| 100 | 119 | ||
| 101 | dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", soc_dai->rate); | 120 | if (soc_dai->sample_bits && (soc_dai->driver->symmetric_samplebits || |
| 121 | rtd->dai_link->symmetric_samplebits)) { | ||
| 122 | dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n", | ||
| 123 | soc_dai->sample_bits); | ||
| 102 | 124 | ||
| 103 | ret = snd_pcm_hw_constraint_minmax(substream->runtime, | 125 | ret = snd_pcm_hw_constraint_minmax(substream->runtime, |
| 104 | SNDRV_PCM_HW_PARAM_RATE, | 126 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS, |
| 105 | soc_dai->rate, soc_dai->rate); | 127 | soc_dai->sample_bits, |
| 106 | if (ret < 0) { | 128 | soc_dai->sample_bits); |
| 107 | dev_err(soc_dai->dev, | 129 | if (ret < 0) { |
| 108 | "ASoC: Unable to apply rate symmetry constraint: %d\n", | 130 | dev_err(soc_dai->dev, |
| 109 | ret); | 131 | "ASoC: Unable to apply sample bits symmetry constraint: %d\n", |
| 110 | return ret; | 132 | ret); |
| 133 | return ret; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | return 0; | ||
| 138 | } | ||
| 139 | |||
| 140 | static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, | ||
| 141 | struct snd_pcm_hw_params *params) | ||
| 142 | { | ||
| 143 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 144 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
| 145 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
| 146 | unsigned int rate, channels, sample_bits, symmetry; | ||
| 147 | |||
| 148 | rate = params_rate(params); | ||
| 149 | channels = params_channels(params); | ||
| 150 | sample_bits = snd_pcm_format_physical_width(params_format(params)); | ||
| 151 | |||
| 152 | /* reject unmatched parameters when applying symmetry */ | ||
| 153 | symmetry = cpu_dai->driver->symmetric_rates || | ||
| 154 | codec_dai->driver->symmetric_rates || | ||
| 155 | rtd->dai_link->symmetric_rates; | ||
| 156 | if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) { | ||
| 157 | dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n", | ||
| 158 | cpu_dai->rate, rate); | ||
| 159 | return -EINVAL; | ||
| 160 | } | ||
| 161 | |||
| 162 | symmetry = cpu_dai->driver->symmetric_channels || | ||
| 163 | codec_dai->driver->symmetric_channels || | ||
| 164 | rtd->dai_link->symmetric_channels; | ||
| 165 | if (symmetry && cpu_dai->channels && cpu_dai->channels != channels) { | ||
| 166 | dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n", | ||
| 167 | cpu_dai->channels, channels); | ||
| 168 | return -EINVAL; | ||
| 169 | } | ||
| 170 | |||
| 171 | symmetry = cpu_dai->driver->symmetric_samplebits || | ||
| 172 | codec_dai->driver->symmetric_samplebits || | ||
| 173 | rtd->dai_link->symmetric_samplebits; | ||
| 174 | if (symmetry && cpu_dai->sample_bits && cpu_dai->sample_bits != sample_bits) { | ||
| 175 | dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n", | ||
| 176 | cpu_dai->sample_bits, sample_bits); | ||
| 177 | return -EINVAL; | ||
| 111 | } | 178 | } |
| 112 | 179 | ||
| 113 | return 0; | 180 | return 0; |
| 114 | } | 181 | } |
| 115 | 182 | ||
| 183 | static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream) | ||
| 184 | { | ||
| 185 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 186 | struct snd_soc_dai_driver *cpu_driver = rtd->cpu_dai->driver; | ||
| 187 | struct snd_soc_dai_driver *codec_driver = rtd->codec_dai->driver; | ||
| 188 | struct snd_soc_dai_link *link = rtd->dai_link; | ||
| 189 | |||
| 190 | return cpu_driver->symmetric_rates || codec_driver->symmetric_rates || | ||
| 191 | link->symmetric_rates || cpu_driver->symmetric_channels || | ||
| 192 | codec_driver->symmetric_channels || link->symmetric_channels || | ||
| 193 | cpu_driver->symmetric_samplebits || | ||
| 194 | codec_driver->symmetric_samplebits || | ||
| 195 | link->symmetric_samplebits; | ||
| 196 | } | ||
| 197 | |||
| 116 | /* | 198 | /* |
| 117 | * List of sample sizes that might go over the bus for parameter | 199 | * List of sample sizes that might go over the bus for parameter |
| 118 | * application. There ought to be a wildcard sample size for things | 200 | * application. There ought to be a wildcard sample size for things |
| @@ -242,6 +324,9 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) | |||
| 242 | &cpu_dai_drv->capture); | 324 | &cpu_dai_drv->capture); |
| 243 | } | 325 | } |
| 244 | 326 | ||
| 327 | if (soc_pcm_has_symmetry(substream)) | ||
| 328 | runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; | ||
| 329 | |||
| 245 | ret = -EINVAL; | 330 | ret = -EINVAL; |
| 246 | snd_pcm_limit_hw_rates(runtime); | 331 | snd_pcm_limit_hw_rates(runtime); |
| 247 | if (!runtime->hw.rates) { | 332 | if (!runtime->hw.rates) { |
| @@ -520,6 +605,10 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, | |||
| 520 | 605 | ||
| 521 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | 606 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); |
| 522 | 607 | ||
| 608 | ret = soc_pcm_params_symmetry(substream, params); | ||
| 609 | if (ret) | ||
| 610 | goto out; | ||
| 611 | |||
| 523 | if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { | 612 | if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { |
| 524 | ret = rtd->dai_link->ops->hw_params(substream, params); | 613 | ret = rtd->dai_link->ops->hw_params(substream, params); |
| 525 | if (ret < 0) { | 614 | if (ret < 0) { |
| @@ -556,9 +645,16 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, | |||
| 556 | } | 645 | } |
| 557 | } | 646 | } |
| 558 | 647 | ||
| 559 | /* store the rate for each DAIs */ | 648 | /* store the parameters for each DAIs */ |
| 560 | cpu_dai->rate = params_rate(params); | 649 | cpu_dai->rate = params_rate(params); |
| 650 | cpu_dai->channels = params_channels(params); | ||
| 651 | cpu_dai->sample_bits = | ||
| 652 | snd_pcm_format_physical_width(params_format(params)); | ||
| 653 | |||
| 561 | codec_dai->rate = params_rate(params); | 654 | codec_dai->rate = params_rate(params); |
| 655 | codec_dai->channels = params_channels(params); | ||
| 656 | codec_dai->sample_bits = | ||
| 657 | snd_pcm_format_physical_width(params_format(params)); | ||
| 562 | 658 | ||
| 563 | out: | 659 | out: |
| 564 | mutex_unlock(&rtd->pcm_mutex); | 660 | mutex_unlock(&rtd->pcm_mutex); |
| @@ -593,6 +689,19 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) | |||
| 593 | 689 | ||
| 594 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); | 690 | mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); |
| 595 | 691 | ||
| 692 | /* clear the corresponding DAIs parameters when going to be inactive */ | ||
| 693 | if (cpu_dai->active == 1) { | ||
| 694 | cpu_dai->rate = 0; | ||
| 695 | cpu_dai->channels = 0; | ||
| 696 | cpu_dai->sample_bits = 0; | ||
| 697 | } | ||
| 698 | |||
| 699 | if (codec_dai->active == 1) { | ||
| 700 | codec_dai->rate = 0; | ||
| 701 | codec_dai->channels = 0; | ||
| 702 | codec_dai->sample_bits = 0; | ||
| 703 | } | ||
| 704 | |||
| 596 | /* apply codec digital mute */ | 705 | /* apply codec digital mute */ |
| 597 | if (!codec->active) | 706 | if (!codec->active) |
| 598 | snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); | 707 | snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); |
