From 9cb7ec3e9be4051f05e68bf8483fd935c63fb5d2 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Tue, 10 Jan 2017 17:10:17 -0800 Subject: [PATCH] ANDROID: ASoC: core - add hostless DAI support Allow DAI's to be hostless so that no PCM data is sent between DAI and CPU. This allows for power savings as there is no DMA or CPU interaction required. BUG: 144369166 Change-Id: I8947f1ad2c4a7013e92e21078b35e3cad332cf6f Signed-off-by: Liam Girdwood Signed-off-by: Patrick Lai Signed-off-by: Banajit Goswami Signed-off-by: Meng Wang Signed-off-by: Gopikrishnaiah Anandan Signed-off-by: Neema Shetty Signed-off-by: Vidyakumar Athota Signed-off-by: Anish Kumar Signed-off-by: Shiv Maliyappanahalli Signed-off-by: Sudheer Papothi --- include/sound/pcm.h | 1 + include/sound/soc.h | 11 +++++++ sound/core/pcm_lib.c | 2 ++ sound/core/pcm_native.c | 1 + sound/soc/soc-pcm.c | 71 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 86 insertions(+) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index bbe6eb1ff5d2..b8f638e075a8 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -475,6 +475,7 @@ struct snd_pcm_substream { #endif /* CONFIG_SND_VERBOSE_PROCFS */ /* misc flags */ unsigned int hw_opened: 1; + unsigned int hw_no_buffer: 1; /* substream may not have a buffer */ }; #define SUBSTREAM_BUSY(substream) ((substream)->ref_count > 0) diff --git a/include/sound/soc.h b/include/sound/soc.h index bfcb988313c1..be95d58b5637 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -370,6 +370,10 @@ #define SOC_ENUM_SINGLE_VIRT_DECL(name, xtexts) \ const struct soc_enum name = SOC_ENUM_SINGLE_VIRT(ARRAY_SIZE(xtexts), xtexts) +/* DAI Link Host Mode Support */ +#define SND_SOC_DAI_LINK_NO_HOST 0x1 +#define SND_SOC_DAI_LINK_OPT_HOST 0x2 + /* * Bias levels * @@ -723,6 +727,7 @@ struct snd_soc_pcm_stream { unsigned int channels_min; /* min channels */ unsigned int channels_max; /* max channels */ unsigned int sig_bits; /* number of bits of content */ + const char *aif_name; /* DAPM AIF widget name */ }; /* SoC audio ops */ @@ -837,6 +842,12 @@ struct snd_soc_dai_link { /* This DAI link can route to other DAI links at runtime (Frontend)*/ unsigned int dynamic:1; + /* + * This DAI can support no host IO (no pcm data is + * copied to from host) + */ + unsigned int no_host_mode:2; + /* DPCM capture and Playback support */ unsigned int dpcm_capture:1; unsigned int dpcm_playback:1; diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 2236b5e0c1f2..a6abac03f30d 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2050,6 +2050,8 @@ static int pcm_sanity_check(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; + if (substream->hw_no_buffer) + snd_printd("%s: warning this PCM is host less\n", __func__); runtime = substream->runtime; if (snd_BUG_ON(!substream->ops->copy_user && !runtime->dma_area)) return -EINVAL; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 91c6ad58729f..4c0bae1338bc 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1199,6 +1199,7 @@ static int snd_pcm_pre_start(struct snd_pcm_substream *substream, int state) if (runtime->status->state != SNDRV_PCM_STATE_PREPARED) return -EBADFD; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + !substream->hw_no_buffer && !snd_pcm_playback_data(substream)) return -EPIPE; runtime->trigger_tstamp_latched = false; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index b600d3eaaf5c..0609cd96e5a2 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,27 @@ #define DPCM_MAX_BE_USERS 8 +/* ASoC no host IO hardware */ +static const struct snd_pcm_hardware no_host_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .period_bytes_min = PAGE_SIZE >> 2, + .period_bytes_max = PAGE_SIZE >> 1, + .periods_min = 2, + .periods_max = 4, + /* + * Increase the max buffer bytes as PAGE_SIZE bytes is + * not enough to encompass all the scenarios sent by + * userspapce. + */ + .buffer_bytes_max = PAGE_SIZE * 4, +}; + /** * snd_soc_runtime_activate() - Increment active count for PCM runtime components * @rtd: ASoC PCM runtime that is activated @@ -138,6 +160,8 @@ int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, const struct snd_pcm_hardware *hw) { struct snd_pcm_runtime *runtime = substream->runtime; + if (!runtime) + return 0; runtime->hw.info = hw->info; runtime->hw.formats = hw->formats; runtime->hw.period_bytes_min = hw->period_bytes_min; @@ -508,6 +532,9 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST) + snd_soc_set_runtime_hwparams(substream, &no_host_hardware); + /* startup the audio subsystem */ ret = snd_soc_dai_startup(cpu_dai, substream); if (ret < 0) { @@ -877,6 +904,16 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, int i, ret = 0; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + /* perform any hw_params fixups */ + if ((rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST) && + rtd->dai_link->be_hw_params_fixup) { + ret = rtd->dai_link->be_hw_params_fixup(rtd, + params); + if (ret < 0) + dev_err(rtd->card->dev, "ASoC: fixup failed for %s\n", + rtd->dai_link->name); + } + if (rtd->dai_link->ops->hw_params) { ret = rtd->dai_link->ops->hw_params(substream, params); if (ret < 0) { @@ -961,6 +998,21 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, ret = soc_pcm_params_symmetry(substream, params); if (ret) goto component_err; + + /* malloc a page for hostless IO */ + if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST) { + substream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV; + substream->dma_buffer.dev.dev = rtd->dev; + substream->dma_buffer.dev.dev->coherent_dma_mask = + DMA_BIT_MASK(sizeof(dma_addr_t) * 8); + substream->dma_buffer.private_data = NULL; + + arch_setup_dma_ops(substream->dma_buffer.dev.dev, + 0, 0, NULL, 0); + ret = snd_pcm_lib_malloc_pages(substream, PAGE_SIZE); + if (ret < 0) + goto component_err; + } out: mutex_unlock(&rtd->card->pcm_mutex); return ret; @@ -1043,6 +1095,8 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) snd_soc_dai_hw_free(cpu_dai, substream); + if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST) + snd_pcm_lib_free_pages(substream); mutex_unlock(&rtd->card->pcm_mutex); return 0; } @@ -2825,6 +2879,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_rtdcom_list *rtdcom; struct snd_pcm *pcm; + struct snd_pcm_str *stream; char new_name[64]; int ret = 0, playback = 0, capture = 0; int i; @@ -2914,6 +2969,22 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) goto out; } + /* setup any hostless PCMs - i.e. no host IO is performed */ + if (rtd->dai_link->no_host_mode) { + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + stream = &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]; + stream->substream->hw_no_buffer = 1; + snd_soc_set_runtime_hwparams(stream->substream, + &no_host_hardware); + } + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + stream = &pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; + stream->substream->hw_no_buffer = 1; + snd_soc_set_runtime_hwparams(stream->substream, + &no_host_hardware); + } + } + /* ASoC PCM operations */ if (rtd->dai_link->dynamic) { rtd->ops.open = dpcm_fe_dai_open;