#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <sound/core.h>
return 0;
}
+/*
+ * List of sample sizes that might go over the bus for parameter
+ * application. There ought to be a wildcard sample size for things
+ * like the DAC/ADC resolution to use but there isn't right now.
+ */
+static int sample_sizes[] = {
+ 24, 32,
+};
+
+static void soc_pcm_apply_msb(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret, i, bits;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ bits = dai->driver->playback.sig_bits;
+ else
+ bits = dai->driver->capture.sig_bits;
+
+ if (!bits)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(sample_sizes); i++) {
+ if (bits >= sample_sizes[i])
+ continue;
+
+ ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0,
+ sample_sizes[i], bits);
+ if (ret != 0)
+ dev_warn(dai->dev,
+ "Failed to set MSB %d/%d: %d\n",
+ bits, sample_sizes[i], ret);
+ }
+}
+
/*
* Called by ALSA when a PCM substream is opened, the runtime->hw record is
* then initialized and any private data can be allocated. This also calls
struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;
int ret = 0;
+ pm_runtime_get_sync(cpu_dai->dev);
+ pm_runtime_get_sync(codec_dai->dev);
+ pm_runtime_get_sync(platform->dev);
+
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
/* startup the audio subsystem */
goto config_err;
}
+ soc_pcm_apply_msb(substream, codec_dai);
+ soc_pcm_apply_msb(substream, cpu_dai);
+
/* Symmetry only applies if we've already got an active stream. */
if (cpu_dai->active) {
ret = soc_pcm_apply_symmetry(substream, cpu_dai);
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
out:
mutex_unlock(&rtd->pcm_mutex);
+
+ pm_runtime_put(platform->dev);
+ pm_runtime_put(codec_dai->dev);
+ pm_runtime_put(cpu_dai->dev);
+
return ret;
}
cpu_dai->runtime = NULL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (unlikely(codec->ignore_pmdown_time)) {
+ if (codec->ignore_pmdown_time ||
+ rtd->dai_link->ignore_pmdown_time) {
/* powered down playback stream now */
snd_soc_dapm_stream_event(rtd,
codec_dai->driver->playback.stream_name,
}
mutex_unlock(&rtd->pcm_mutex);
+
+ pm_runtime_put(platform->dev);
+ pm_runtime_put(codec_dai->dev);
+ pm_runtime_put(cpu_dai->dev);
+
return 0;
}
return offset;
}
-/* ASoC PCM operations */
-static struct snd_pcm_ops soc_pcm_ops = {
- .open = soc_pcm_open,
- .close = soc_pcm_close,
- .hw_params = soc_pcm_hw_params,
- .hw_free = soc_pcm_hw_free,
- .prepare = soc_pcm_prepare,
- .trigger = soc_pcm_trigger,
- .pointer = soc_pcm_pointer,
-};
-
/* create a new pcm */
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_pcm_ops *soc_pcm_ops = &rtd->ops;
struct snd_pcm *pcm;
char new_name[64];
int ret = 0, playback = 0, capture = 0;
+ soc_pcm_ops->open = soc_pcm_open;
+ soc_pcm_ops->close = soc_pcm_close;
+ soc_pcm_ops->hw_params = soc_pcm_hw_params;
+ soc_pcm_ops->hw_free = soc_pcm_hw_free;
+ soc_pcm_ops->prepare = soc_pcm_prepare;
+ soc_pcm_ops->trigger = soc_pcm_trigger;
+ soc_pcm_ops->pointer = soc_pcm_pointer;
+
/* check client and interface hw capabilities */
snprintf(new_name, sizeof(new_name), "%s %s-%d",
rtd->dai_link->stream_name, codec_dai->name, num);
rtd->pcm = pcm;
pcm->private_data = rtd;
if (platform->driver->ops) {
- soc_pcm_ops.mmap = platform->driver->ops->mmap;
- soc_pcm_ops.pointer = platform->driver->ops->pointer;
- soc_pcm_ops.ioctl = platform->driver->ops->ioctl;
- soc_pcm_ops.copy = platform->driver->ops->copy;
- soc_pcm_ops.silence = platform->driver->ops->silence;
- soc_pcm_ops.ack = platform->driver->ops->ack;
- soc_pcm_ops.page = platform->driver->ops->page;
+ soc_pcm_ops->mmap = platform->driver->ops->mmap;
+ soc_pcm_ops->pointer = platform->driver->ops->pointer;
+ soc_pcm_ops->ioctl = platform->driver->ops->ioctl;
+ soc_pcm_ops->copy = platform->driver->ops->copy;
+ soc_pcm_ops->silence = platform->driver->ops->silence;
+ soc_pcm_ops->ack = platform->driver->ops->ack;
+ soc_pcm_ops->page = platform->driver->ops->page;
}
if (playback)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops);
if (capture)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops);
if (platform->driver->pcm_new) {
ret = platform->driver->pcm_new(rtd);