diff options
-rw-r--r-- | include/sound/soc.h | 2 | ||||
-rw-r--r-- | sound/soc/soc-pcm.c | 367 |
2 files changed, 355 insertions, 14 deletions
diff --git a/include/sound/soc.h b/include/sound/soc.h index eea3007a28f1..2acb56673bab 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -826,6 +826,8 @@ struct snd_soc_component_driver { int (*set_bias_level)(struct snd_soc_component *component, enum snd_soc_bias_level level); + const struct snd_pcm_ops *ops; + /* probe ordering - for components with runtime dependencies */ int probe_order; int remove_order; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index e5eb0cff790b..daaa670ee9b7 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -459,7 +459,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; const char *codec_dai_name = "multicodec"; - int i, ret = 0; + int i, ret = 0, __ret; pinctrl_pm_select_default_state(cpu_dai->dev); for (i = 0; i < rtd->num_codecs; i++) @@ -483,7 +483,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } - if (platform->driver->ops && platform->driver->ops->open) { + if (platform && platform->driver->ops && platform->driver->ops->open) { ret = platform->driver->ops->open(substream); if (ret < 0) { dev_err(platform->dev, "ASoC: can't open platform" @@ -492,6 +492,29 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } + ret = 0; + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->open) + continue; + + __ret = component->driver->ops->open(substream); + if (__ret < 0) { + dev_err(component->dev, + "ASoC: can't open component %s: %d\n", + component->name, ret); + ret = __ret; + } + } + if (ret < 0) + goto component_err; + for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; if (codec_dai->driver->ops->startup) { @@ -598,7 +621,22 @@ codec_dai_err: codec_dai->driver->ops->shutdown(substream, codec_dai); } - if (platform->driver->ops && platform->driver->ops->close) +component_err: + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->close) + continue; + + component->driver->ops->close(substream); + } + + if (platform && platform->driver->ops && platform->driver->ops->close) platform->driver->ops->close(substream); platform_err: @@ -695,9 +733,23 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) if (rtd->dai_link->ops->shutdown) rtd->dai_link->ops->shutdown(substream); - if (platform->driver->ops && platform->driver->ops->close) + if (platform && platform->driver->ops && platform->driver->ops->close) platform->driver->ops->close(substream); + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->close) + continue; + + component->driver->ops->close(substream); + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (snd_soc_runtime_ignore_pmdown_time(rtd)) { /* powered down playback stream now */ @@ -745,6 +797,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; int i, ret = 0; @@ -760,7 +814,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } - if (platform->driver->ops && platform->driver->ops->prepare) { + if (platform && platform->driver->ops && platform->driver->ops->prepare) { ret = platform->driver->ops->prepare(substream); if (ret < 0) { dev_err(platform->dev, "ASoC: platform prepare error:" @@ -769,6 +823,25 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->prepare) + continue; + + ret = component->driver->ops->prepare(substream); + if (ret < 0) { + dev_err(component->dev, + "ASoC: platform prepare error: %d\n", ret); + goto out; + } + } + for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; if (codec_dai->driver->ops->prepare) { @@ -851,8 +924,10 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int i, ret = 0; + int i, ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); if (rtd->dai_link->ops->hw_params) { @@ -910,7 +985,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, if (ret < 0) goto interface_err; - if (platform->driver->ops && platform->driver->ops->hw_params) { + if (platform && platform->driver->ops && platform->driver->ops->hw_params) { ret = platform->driver->ops->hw_params(substream, params); if (ret < 0) { dev_err(platform->dev, "ASoC: %s hw params failed: %d\n", @@ -919,20 +994,60 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, } } + ret = 0; + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->hw_params) + continue; + + __ret = component->driver->ops->hw_params(substream, params); + if (__ret < 0) { + dev_err(component->dev, + "ASoC: %s hw params failed: %d\n", + component->name, ret); + ret = __ret; + } + } + if (ret < 0) + goto component_err; + /* store the parameters for each DAIs */ cpu_dai->rate = params_rate(params); cpu_dai->channels = params_channels(params); cpu_dai->sample_bits = snd_pcm_format_physical_width(params_format(params)); - ret = soc_pcm_params_symmetry(substream, params); if (ret) - goto platform_err; + goto component_err; out: mutex_unlock(&rtd->pcm_mutex); return ret; +component_err: + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->hw_free) + continue; + + component->driver->ops->hw_free(substream); + } + + if (platform && platform->driver->ops && platform->driver->ops->hw_free) + platform->driver->ops->hw_free(substream); + platform_err: if (cpu_dai->driver->ops->hw_free) cpu_dai->driver->ops->hw_free(substream, cpu_dai); @@ -962,6 +1077,8 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; @@ -998,9 +1115,24 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) rtd->dai_link->ops->hw_free(substream); /* free any DMA resources */ - if (platform->driver->ops && platform->driver->ops->hw_free) + if (platform && platform->driver->ops && platform->driver->ops->hw_free) platform->driver->ops->hw_free(substream); + /* free any component resources */ + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->hw_free) + continue; + + component->driver->ops->hw_free(substream); + } + /* now free hw params for the DAIs */ for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; @@ -1019,6 +1151,8 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; int i, ret; @@ -1033,12 +1167,28 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } } - if (platform->driver->ops && platform->driver->ops->trigger) { + if (platform && platform->driver->ops && platform->driver->ops->trigger) { ret = platform->driver->ops->trigger(substream, cmd); if (ret < 0) return ret; } + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->trigger) + continue; + + ret = component->driver->ops->trigger(substream, cmd); + if (ret < 0) + return ret; + } + if (cpu_dai->driver->ops->trigger) { ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai); if (ret < 0) @@ -1088,6 +1238,8 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; struct snd_pcm_runtime *runtime = substream->runtime; @@ -1096,9 +1248,25 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) snd_pcm_sframes_t codec_delay = 0; int i; - if (platform->driver->ops && platform->driver->ops->pointer) + if (platform && platform->driver->ops && platform->driver->ops->pointer) offset = platform->driver->ops->pointer(substream); + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->pointer) + continue; + + /* FIXME: use 1st pointer */ + offset = component->driver->ops->pointer(substream); + break; + } + if (cpu_dai->driver->ops->delay) delay += cpu_dai->driver->ops->delay(substream, cpu_dai); @@ -2283,9 +2451,27 @@ static int soc_pcm_ioctl(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; - if (platform->driver->ops && platform->driver->ops->ioctl) + if (platform && platform->driver->ops && platform->driver->ops->ioctl) return platform->driver->ops->ioctl(substream, cmd, arg); + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->ioctl) + continue; + + /* FIXME: use 1st ioctl */ + return component->driver->ops->ioctl(substream, cmd, arg); + } + return snd_pcm_lib_ioctl(substream, cmd, arg); } @@ -2647,6 +2833,138 @@ static void soc_pcm_private_free(struct snd_pcm *pcm) } } +static int soc_rtdcom_ack(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->ack) + continue; + + /* FIXME. it returns 1st ask now */ + return component->driver->ops->ack(substream); + } + + return -EINVAL; +} + +static int soc_rtdcom_copy_user(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void __user *buf, + unsigned long bytes) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->copy_user) + continue; + + /* FIXME. it returns 1st copy now */ + return component->driver->ops->copy_user(substream, channel, + pos, buf, bytes); + } + + return -EINVAL; +} + +static int soc_rtdcom_copy_kernel(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void *buf, unsigned long bytes) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->copy_kernel) + continue; + + /* FIXME. it returns 1st copy now */ + return component->driver->ops->copy_kernel(substream, channel, + pos, buf, bytes); + } + + return -EINVAL; +} + +static int soc_rtdcom_fill_silence(struct snd_pcm_substream *substream, int channel, + unsigned long pos, unsigned long bytes) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->fill_silence) + continue; + + /* FIXME. it returns 1st silence now */ + return component->driver->ops->fill_silence(substream, channel, + pos, bytes); + } + + return -EINVAL; +} + +static struct page *soc_rtdcom_page(struct snd_pcm_substream *substream, + unsigned long offset) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + struct page *page; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->page) + continue; + + /* FIXME. it returns 1st page now */ + page = component->driver->ops->page(substream, offset); + if (page) + return page; + } + + return NULL; +} + +static int soc_rtdcom_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->mmap) + continue; + + /* FIXME. it returns 1st mmap now */ + return component->driver->ops->mmap(substream, vma); + } + + return -EINVAL; +} + /* create a new pcm */ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) { @@ -2749,7 +3067,28 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->ops.ioctl = soc_pcm_ioctl; } - if (platform->driver->ops) { + for_each_rtdcom(rtd, rtdcom) { + const struct snd_pcm_ops *ops = rtdcom->component->driver->ops; + + if (!ops) + continue; + + if (ops->ack) + rtd->ops.ack = soc_rtdcom_ack; + if (ops->copy_user) + rtd->ops.copy_user = soc_rtdcom_copy_user; + if (ops->copy_kernel) + rtd->ops.copy_kernel = soc_rtdcom_copy_kernel; + if (ops->fill_silence) + rtd->ops.fill_silence = soc_rtdcom_fill_silence; + if (ops->page) + rtd->ops.page = soc_rtdcom_page; + if (ops->mmap) + rtd->ops.mmap = soc_rtdcom_mmap; + } + + /* overwrite */ + if (platform && platform->driver->ops) { rtd->ops.ack = platform->driver->ops->ack; rtd->ops.copy_user = platform->driver->ops->copy_user; rtd->ops.copy_kernel = platform->driver->ops->copy_kernel; |