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;