forked from Minki/linux
a25684a956
This patch adds the support for allocation of non-contiguous DMA pages in the common memalloc helper. It's another SG-buffer type, but unlike the existing one, this is directional and requires the explicit sync / invalidation of dirty pages on non-coherent architectures. For this enhancement, the following points are changed: - snd_dma_device stores the DMA direction. - snd_dma_device stores need_sync flag indicating whether the explicit sync is required or not. - A new variant of helper functions, snd_dma_alloc_dir_pages() and *_all() are introduced; the old snd_dma_alloc_pages() and *_all() kept as just wrappers with DMA_BIDIRECTIONAL. - A new helper snd_dma_buffer_sync() is introduced; this gets called in the appropriate places. - A new allocation type, SNDRV_DMA_TYPE_NONCONTIG, is introduced. When the driver allocates pages with this new type, and it may require the SNDRV_PCM_INFO_EXPLICIT_SYNC flag set to the PCM hardware.info for taking the full control of PCM applptr and hwptr changes (that implies disabling the mmap of control/status data). When the buffer allocation is managed by snd_pcm_set_managed_buffer(), this flag is automatically set depending on the result of dma_need_sync() internally. Otherwise, if the buffer is managed manually, the driver has to set the flag explicitly, too. The explicit sync between CPU and device for non-coherent memory is performed at the points before and after read/write transfer as well as the applptr/hwptr syncptr ioctl. In the case of mmap mode, user-space is supposed to call the syncptr ioctl with the hwptr flag to update and fetch the status at first; this corresponds to CPU-sync. Then user-space advances the applptr via syncptr ioctl again with applptr flag, and this corresponds to the device sync with flushing. Other than the DMA direction and the explicit sync, the usage of this new buffer type is almost equivalent with the existing SNDRV_DMA_TYPE_DEV_SG; you can get the page and the address via snd_sgbuf_get_page() and snd_sgbuf_get_addr(), also calculate the continuous pages via snd_sgbuf_get_chunk_size(). For those SG-page handling, the non-contig type shares the same ops with the vmalloc handler. As we do always vmap the SG pages at first, the actual address can be deduced from the vmapped address easily without iterating the SG-list. Link: https://lore.kernel.org/r/20211017074859.24112-2-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de>
84 lines
3.0 KiB
C
84 lines
3.0 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* pcm_local.h - a local header file for snd-pcm module.
|
|
*
|
|
* Copyright (c) Takashi Sakamoto <o-takashi@sakamocchi.jp>
|
|
*/
|
|
|
|
#ifndef __SOUND_CORE_PCM_LOCAL_H
|
|
#define __SOUND_CORE_PCM_LOCAL_H
|
|
|
|
extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates;
|
|
|
|
void snd_interval_mul(const struct snd_interval *a,
|
|
const struct snd_interval *b, struct snd_interval *c);
|
|
void snd_interval_div(const struct snd_interval *a,
|
|
const struct snd_interval *b, struct snd_interval *c);
|
|
void snd_interval_muldivk(const struct snd_interval *a,
|
|
const struct snd_interval *b,
|
|
unsigned int k, struct snd_interval *c);
|
|
void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
|
|
const struct snd_interval *b, struct snd_interval *c);
|
|
|
|
int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime,
|
|
snd_pcm_hw_param_t var, u_int32_t mask);
|
|
|
|
int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream,
|
|
snd_pcm_uframes_t appl_ptr);
|
|
int snd_pcm_update_state(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_runtime *runtime);
|
|
int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream);
|
|
|
|
void snd_pcm_playback_silence(struct snd_pcm_substream *substream,
|
|
snd_pcm_uframes_t new_hw_ptr);
|
|
|
|
static inline snd_pcm_uframes_t
|
|
snd_pcm_avail(struct snd_pcm_substream *substream)
|
|
{
|
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
return snd_pcm_playback_avail(substream->runtime);
|
|
else
|
|
return snd_pcm_capture_avail(substream->runtime);
|
|
}
|
|
|
|
static inline snd_pcm_uframes_t
|
|
snd_pcm_hw_avail(struct snd_pcm_substream *substream)
|
|
{
|
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
return snd_pcm_playback_hw_avail(substream->runtime);
|
|
else
|
|
return snd_pcm_capture_hw_avail(substream->runtime);
|
|
}
|
|
|
|
#ifdef CONFIG_SND_PCM_TIMER
|
|
void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream);
|
|
void snd_pcm_timer_init(struct snd_pcm_substream *substream);
|
|
void snd_pcm_timer_done(struct snd_pcm_substream *substream);
|
|
#else
|
|
static inline void
|
|
snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) {}
|
|
static inline void snd_pcm_timer_init(struct snd_pcm_substream *substream) {}
|
|
static inline void snd_pcm_timer_done(struct snd_pcm_substream *substream) {}
|
|
#endif
|
|
|
|
void __snd_pcm_xrun(struct snd_pcm_substream *substream);
|
|
void snd_pcm_group_init(struct snd_pcm_group *group);
|
|
void snd_pcm_sync_stop(struct snd_pcm_substream *substream, bool sync_irq);
|
|
|
|
#define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime)
|
|
|
|
/* loop over all PCM substreams */
|
|
#define for_each_pcm_substream(pcm, str, subs) \
|
|
for ((str) = 0; (str) < 2; (str)++) \
|
|
for ((subs) = (pcm)->streams[str].substream; (subs); \
|
|
(subs) = (subs)->next)
|
|
|
|
static inline void snd_pcm_dma_buffer_sync(struct snd_pcm_substream *substream,
|
|
enum snd_dma_sync_mode mode)
|
|
{
|
|
if (substream->runtime->info & SNDRV_PCM_INFO_EXPLICIT_SYNC)
|
|
snd_dma_buffer_sync(snd_pcm_get_dma_buf(substream), mode);
|
|
}
|
|
|
|
#endif /* __SOUND_CORE_PCM_LOCAL_H */
|