summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Tull <alan.tull@freescale.com>2011-10-28 19:14:13 -0500
committerJason Liu <r64343@freescale.com>2012-07-20 13:17:05 +0800
commita94127f286479cf81580da8e1d036ffeb6ffc3bb (patch)
treec26c26a55fa71f36121e640896d52b4d92c574c0
parent573e8da5c0d26d37cd4dbc5e1b33669ed716ee53 (diff)
ENGR00161014 ASoC: don't clobber platform DMA driver ops
Previously, only one static struct for ops existed for all platform DMA drivers to share. Half of the ops are shared functions which don't have stubs in the ALSA core. The other half are initialized to point directly to ops in the platform driver. This creates problems where each time soc_new_pcm is called, the new platform driver's ops would overwrite a subset of the ops. Signed-off-by: Alan Tull <alan.tull@freescale.com>
-rw-r--r--include/sound/soc-dai.h2
-rw-r--r--include/sound/soc.h2
-rw-r--r--sound/soc/soc-core.c84
3 files changed, 68 insertions, 20 deletions
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index 1bafe95dcf41..dc60e5e07226 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -172,6 +172,8 @@ struct snd_soc_dai_ops {
struct snd_soc_dai *);
int (*trigger)(struct snd_pcm_substream *, int,
struct snd_soc_dai *);
+ int (*ioctl)(struct snd_pcm_substream *, unsigned int, void *,
+ struct snd_soc_dai *);
/*
* For hardware based FIFO caused delay reporting.
* Optional.
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 3a4bd3a3c68d..0a9a21e0a199 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -496,6 +496,7 @@ struct snd_soc_ops {
int (*hw_free)(struct snd_pcm_substream *);
int (*prepare)(struct snd_pcm_substream *);
int (*trigger)(struct snd_pcm_substream *, int);
+ int (*ioctl)(struct snd_pcm_substream *, unsigned int, void *);
};
/* SoC cache ops */
@@ -805,6 +806,7 @@ struct snd_soc_pcm_runtime {
struct snd_soc_dai *cpu_dai;
struct delayed_work delayed_work;
+ struct snd_pcm_ops *ops;
};
/* mixer control */
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 5e11eff6e7f5..eefebd919afd 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -989,6 +989,40 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
+static int soc_pcm_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ if (rtd->dai_link->ops && rtd->dai_link->ops->ioctl) {
+ ret = rtd->dai_link->ops->ioctl(substream, cmd, arg);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (codec_dai->driver->ops->ioctl) {
+ ret = codec_dai->driver->ops->ioctl(substream, cmd, arg, codec_dai);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (platform->driver->ops->ioctl) {
+ ret = platform->driver->ops->ioctl(substream, cmd, arg);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (cpu_dai->driver->ops->ioctl) {
+ ret = cpu_dai->driver->ops->ioctl(substream, cmd, arg, cpu_dai);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
/*
* soc level wrapper for pointer callback
* If cpu_dai, codec_dai, platform driver has the delay callback, than
@@ -1021,17 +1055,6 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
return offset;
}
-/* ASoC PCM operations */
-static struct snd_pcm_ops soc_pcm_ops = {
- .open = soc_pcm_open,
- .close = soc_codec_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,
-};
-
#ifdef CONFIG_PM_SLEEP
/* powers down audio subsystem for suspend */
int snd_soc_suspend(struct device *dev)
@@ -2056,6 +2079,7 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card)
snd_soc_dapm_free(&card->dapm);
+ kfree(card->rtd->ops);
kfree(card->rtd);
snd_card_free(card->snd_card);
return 0;
@@ -2118,9 +2142,17 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_pcm *pcm;
+ struct snd_pcm_ops *soc_pcm_ops;
char new_name[64];
int ret = 0, playback = 0, capture = 0;
+ /* ASoC PCM operations */
+ soc_pcm_ops = kzalloc(sizeof(struct snd_pcm_ops), GFP_KERNEL);
+ if (soc_pcm_ops == NULL) {
+ printk(KERN_ERR "asoc: can't create pcm ops for codec %s\n", codec->name);
+ return -ENOMEM;
+ }
+
/* check client and interface hw capabilities */
snprintf(new_name, sizeof(new_name), "%s %s-%d",
rtd->dai_link->stream_name, codec_dai->name, num);
@@ -2135,32 +2167,44 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
num, playback, capture, &pcm);
if (ret < 0) {
printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
+ kfree(soc_pcm_ops);
return ret;
}
rtd->pcm = pcm;
pcm->private_data = rtd;
+
+ soc_pcm_ops->open = soc_pcm_open;
+ soc_pcm_ops->close = soc_codec_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->pointer = soc_pcm_pointer;
+ soc_pcm_ops->trigger = soc_pcm_trigger;
+ soc_pcm_ops->ioctl = soc_pcm_ioctl;
+
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->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;
}
+ rtd->ops = soc_pcm_ops;
+
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->card->snd_card,
codec_dai, pcm);
if (ret < 0) {
pr_err("asoc: platform pcm constructor failed\n");
+ kfree(rtd->ops);
return ret;
}
}