162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
462306a36Sopenharmony_ci *                   Takashi Iwai <tiwai@suse.de>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  Generic memory allocators
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/mm.h>
1162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1262306a36Sopenharmony_ci#include <linux/dma-map-ops.h>
1362306a36Sopenharmony_ci#include <linux/genalloc.h>
1462306a36Sopenharmony_ci#include <linux/highmem.h>
1562306a36Sopenharmony_ci#include <linux/vmalloc.h>
1662306a36Sopenharmony_ci#ifdef CONFIG_X86
1762306a36Sopenharmony_ci#include <asm/set_memory.h>
1862306a36Sopenharmony_ci#endif
1962306a36Sopenharmony_ci#include <sound/memalloc.h>
2062306a36Sopenharmony_ci#include "memalloc_local.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define DEFAULT_GFP \
2362306a36Sopenharmony_ci	(GFP_KERNEL | \
2462306a36Sopenharmony_ci	 __GFP_RETRY_MAYFAIL | /* don't trigger OOM-killer */ \
2562306a36Sopenharmony_ci	 __GFP_NOWARN)   /* no stack trace print - this call is non-critical */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#ifdef CONFIG_SND_DMA_SGBUF
3062306a36Sopenharmony_cistatic void *snd_dma_sg_fallback_alloc(struct snd_dma_buffer *dmab, size_t size);
3162306a36Sopenharmony_ci#endif
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic void *__snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	if (WARN_ON_ONCE(!ops || !ops->alloc))
3862306a36Sopenharmony_ci		return NULL;
3962306a36Sopenharmony_ci	return ops->alloc(dmab, size);
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/**
4362306a36Sopenharmony_ci * snd_dma_alloc_dir_pages - allocate the buffer area according to the given
4462306a36Sopenharmony_ci *	type and direction
4562306a36Sopenharmony_ci * @type: the DMA buffer type
4662306a36Sopenharmony_ci * @device: the device pointer
4762306a36Sopenharmony_ci * @dir: DMA direction
4862306a36Sopenharmony_ci * @size: the buffer size to allocate
4962306a36Sopenharmony_ci * @dmab: buffer allocation record to store the allocated data
5062306a36Sopenharmony_ci *
5162306a36Sopenharmony_ci * Calls the memory-allocator function for the corresponding
5262306a36Sopenharmony_ci * buffer type.
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * Return: Zero if the buffer with the given size is allocated successfully,
5562306a36Sopenharmony_ci * otherwise a negative value on error.
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_ciint snd_dma_alloc_dir_pages(int type, struct device *device,
5862306a36Sopenharmony_ci			    enum dma_data_direction dir, size_t size,
5962306a36Sopenharmony_ci			    struct snd_dma_buffer *dmab)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	if (WARN_ON(!size))
6262306a36Sopenharmony_ci		return -ENXIO;
6362306a36Sopenharmony_ci	if (WARN_ON(!dmab))
6462306a36Sopenharmony_ci		return -ENXIO;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	size = PAGE_ALIGN(size);
6762306a36Sopenharmony_ci	dmab->dev.type = type;
6862306a36Sopenharmony_ci	dmab->dev.dev = device;
6962306a36Sopenharmony_ci	dmab->dev.dir = dir;
7062306a36Sopenharmony_ci	dmab->bytes = 0;
7162306a36Sopenharmony_ci	dmab->addr = 0;
7262306a36Sopenharmony_ci	dmab->private_data = NULL;
7362306a36Sopenharmony_ci	dmab->area = __snd_dma_alloc_pages(dmab, size);
7462306a36Sopenharmony_ci	if (!dmab->area)
7562306a36Sopenharmony_ci		return -ENOMEM;
7662306a36Sopenharmony_ci	dmab->bytes = size;
7762306a36Sopenharmony_ci	return 0;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_dma_alloc_dir_pages);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/**
8262306a36Sopenharmony_ci * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback
8362306a36Sopenharmony_ci * @type: the DMA buffer type
8462306a36Sopenharmony_ci * @device: the device pointer
8562306a36Sopenharmony_ci * @size: the buffer size to allocate
8662306a36Sopenharmony_ci * @dmab: buffer allocation record to store the allocated data
8762306a36Sopenharmony_ci *
8862306a36Sopenharmony_ci * Calls the memory-allocator function for the corresponding
8962306a36Sopenharmony_ci * buffer type.  When no space is left, this function reduces the size and
9062306a36Sopenharmony_ci * tries to allocate again.  The size actually allocated is stored in
9162306a36Sopenharmony_ci * res_size argument.
9262306a36Sopenharmony_ci *
9362306a36Sopenharmony_ci * Return: Zero if the buffer with the given size is allocated successfully,
9462306a36Sopenharmony_ci * otherwise a negative value on error.
9562306a36Sopenharmony_ci */
9662306a36Sopenharmony_ciint snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
9762306a36Sopenharmony_ci				 struct snd_dma_buffer *dmab)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	int err;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) {
10262306a36Sopenharmony_ci		if (err != -ENOMEM)
10362306a36Sopenharmony_ci			return err;
10462306a36Sopenharmony_ci		if (size <= PAGE_SIZE)
10562306a36Sopenharmony_ci			return -ENOMEM;
10662306a36Sopenharmony_ci		size >>= 1;
10762306a36Sopenharmony_ci		size = PAGE_SIZE << get_order(size);
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci	if (! dmab->area)
11062306a36Sopenharmony_ci		return -ENOMEM;
11162306a36Sopenharmony_ci	return 0;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_dma_alloc_pages_fallback);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/**
11662306a36Sopenharmony_ci * snd_dma_free_pages - release the allocated buffer
11762306a36Sopenharmony_ci * @dmab: the buffer allocation record to release
11862306a36Sopenharmony_ci *
11962306a36Sopenharmony_ci * Releases the allocated buffer via snd_dma_alloc_pages().
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_civoid snd_dma_free_pages(struct snd_dma_buffer *dmab)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (ops && ops->free)
12662306a36Sopenharmony_ci		ops->free(dmab);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_dma_free_pages);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/* called by devres */
13162306a36Sopenharmony_cistatic void __snd_release_pages(struct device *dev, void *res)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	snd_dma_free_pages(res);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/**
13762306a36Sopenharmony_ci * snd_devm_alloc_dir_pages - allocate the buffer and manage with devres
13862306a36Sopenharmony_ci * @dev: the device pointer
13962306a36Sopenharmony_ci * @type: the DMA buffer type
14062306a36Sopenharmony_ci * @dir: DMA direction
14162306a36Sopenharmony_ci * @size: the buffer size to allocate
14262306a36Sopenharmony_ci *
14362306a36Sopenharmony_ci * Allocate buffer pages depending on the given type and manage using devres.
14462306a36Sopenharmony_ci * The pages will be released automatically at the device removal.
14562306a36Sopenharmony_ci *
14662306a36Sopenharmony_ci * Unlike snd_dma_alloc_pages(), this function requires the real device pointer,
14762306a36Sopenharmony_ci * hence it can't work with SNDRV_DMA_TYPE_CONTINUOUS or
14862306a36Sopenharmony_ci * SNDRV_DMA_TYPE_VMALLOC type.
14962306a36Sopenharmony_ci *
15062306a36Sopenharmony_ci * Return: the snd_dma_buffer object at success, or NULL if failed
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_cistruct snd_dma_buffer *
15362306a36Sopenharmony_cisnd_devm_alloc_dir_pages(struct device *dev, int type,
15462306a36Sopenharmony_ci			 enum dma_data_direction dir, size_t size)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct snd_dma_buffer *dmab;
15762306a36Sopenharmony_ci	int err;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (WARN_ON(type == SNDRV_DMA_TYPE_CONTINUOUS ||
16062306a36Sopenharmony_ci		    type == SNDRV_DMA_TYPE_VMALLOC))
16162306a36Sopenharmony_ci		return NULL;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	dmab = devres_alloc(__snd_release_pages, sizeof(*dmab), GFP_KERNEL);
16462306a36Sopenharmony_ci	if (!dmab)
16562306a36Sopenharmony_ci		return NULL;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab);
16862306a36Sopenharmony_ci	if (err < 0) {
16962306a36Sopenharmony_ci		devres_free(dmab);
17062306a36Sopenharmony_ci		return NULL;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	devres_add(dev, dmab);
17462306a36Sopenharmony_ci	return dmab;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_devm_alloc_dir_pages);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/**
17962306a36Sopenharmony_ci * snd_dma_buffer_mmap - perform mmap of the given DMA buffer
18062306a36Sopenharmony_ci * @dmab: buffer allocation information
18162306a36Sopenharmony_ci * @area: VM area information
18262306a36Sopenharmony_ci *
18362306a36Sopenharmony_ci * Return: zero if successful, or a negative error code
18462306a36Sopenharmony_ci */
18562306a36Sopenharmony_ciint snd_dma_buffer_mmap(struct snd_dma_buffer *dmab,
18662306a36Sopenharmony_ci			struct vm_area_struct *area)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	const struct snd_malloc_ops *ops;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (!dmab)
19162306a36Sopenharmony_ci		return -ENOENT;
19262306a36Sopenharmony_ci	ops = snd_dma_get_ops(dmab);
19362306a36Sopenharmony_ci	if (ops && ops->mmap)
19462306a36Sopenharmony_ci		return ops->mmap(dmab, area);
19562306a36Sopenharmony_ci	else
19662306a36Sopenharmony_ci		return -ENOENT;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_dma_buffer_mmap);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci#ifdef CONFIG_HAS_DMA
20162306a36Sopenharmony_ci/**
20262306a36Sopenharmony_ci * snd_dma_buffer_sync - sync DMA buffer between CPU and device
20362306a36Sopenharmony_ci * @dmab: buffer allocation information
20462306a36Sopenharmony_ci * @mode: sync mode
20562306a36Sopenharmony_ci */
20662306a36Sopenharmony_civoid snd_dma_buffer_sync(struct snd_dma_buffer *dmab,
20762306a36Sopenharmony_ci			 enum snd_dma_sync_mode mode)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	const struct snd_malloc_ops *ops;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (!dmab || !dmab->dev.need_sync)
21262306a36Sopenharmony_ci		return;
21362306a36Sopenharmony_ci	ops = snd_dma_get_ops(dmab);
21462306a36Sopenharmony_ci	if (ops && ops->sync)
21562306a36Sopenharmony_ci		ops->sync(dmab, mode);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_dma_buffer_sync);
21862306a36Sopenharmony_ci#endif /* CONFIG_HAS_DMA */
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci/**
22162306a36Sopenharmony_ci * snd_sgbuf_get_addr - return the physical address at the corresponding offset
22262306a36Sopenharmony_ci * @dmab: buffer allocation information
22362306a36Sopenharmony_ci * @offset: offset in the ring buffer
22462306a36Sopenharmony_ci *
22562306a36Sopenharmony_ci * Return: the physical address
22662306a36Sopenharmony_ci */
22762306a36Sopenharmony_cidma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab, size_t offset)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (ops && ops->get_addr)
23262306a36Sopenharmony_ci		return ops->get_addr(dmab, offset);
23362306a36Sopenharmony_ci	else
23462306a36Sopenharmony_ci		return dmab->addr + offset;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sgbuf_get_addr);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/**
23962306a36Sopenharmony_ci * snd_sgbuf_get_page - return the physical page at the corresponding offset
24062306a36Sopenharmony_ci * @dmab: buffer allocation information
24162306a36Sopenharmony_ci * @offset: offset in the ring buffer
24262306a36Sopenharmony_ci *
24362306a36Sopenharmony_ci * Return: the page pointer
24462306a36Sopenharmony_ci */
24562306a36Sopenharmony_cistruct page *snd_sgbuf_get_page(struct snd_dma_buffer *dmab, size_t offset)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (ops && ops->get_page)
25062306a36Sopenharmony_ci		return ops->get_page(dmab, offset);
25162306a36Sopenharmony_ci	else
25262306a36Sopenharmony_ci		return virt_to_page(dmab->area + offset);
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sgbuf_get_page);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci/**
25762306a36Sopenharmony_ci * snd_sgbuf_get_chunk_size - compute the max chunk size with continuous pages
25862306a36Sopenharmony_ci *	on sg-buffer
25962306a36Sopenharmony_ci * @dmab: buffer allocation information
26062306a36Sopenharmony_ci * @ofs: offset in the ring buffer
26162306a36Sopenharmony_ci * @size: the requested size
26262306a36Sopenharmony_ci *
26362306a36Sopenharmony_ci * Return: the chunk size
26462306a36Sopenharmony_ci */
26562306a36Sopenharmony_ciunsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
26662306a36Sopenharmony_ci				      unsigned int ofs, unsigned int size)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (ops && ops->get_chunk_size)
27162306a36Sopenharmony_ci		return ops->get_chunk_size(dmab, ofs, size);
27262306a36Sopenharmony_ci	else
27362306a36Sopenharmony_ci		return size;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sgbuf_get_chunk_size);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/*
27862306a36Sopenharmony_ci * Continuous pages allocator
27962306a36Sopenharmony_ci */
28062306a36Sopenharmony_cistatic void *do_alloc_pages(struct device *dev, size_t size, dma_addr_t *addr,
28162306a36Sopenharmony_ci			    bool wc)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	void *p;
28462306a36Sopenharmony_ci	gfp_t gfp = GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci again:
28762306a36Sopenharmony_ci	p = alloc_pages_exact(size, gfp);
28862306a36Sopenharmony_ci	if (!p)
28962306a36Sopenharmony_ci		return NULL;
29062306a36Sopenharmony_ci	*addr = page_to_phys(virt_to_page(p));
29162306a36Sopenharmony_ci	if (!dev)
29262306a36Sopenharmony_ci		return p;
29362306a36Sopenharmony_ci	if ((*addr + size - 1) & ~dev->coherent_dma_mask) {
29462306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_ZONE_DMA32) && !(gfp & GFP_DMA32)) {
29562306a36Sopenharmony_ci			gfp |= GFP_DMA32;
29662306a36Sopenharmony_ci			goto again;
29762306a36Sopenharmony_ci		}
29862306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_ZONE_DMA) && !(gfp & GFP_DMA)) {
29962306a36Sopenharmony_ci			gfp = (gfp & ~GFP_DMA32) | GFP_DMA;
30062306a36Sopenharmony_ci			goto again;
30162306a36Sopenharmony_ci		}
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci#ifdef CONFIG_X86
30462306a36Sopenharmony_ci	if (wc)
30562306a36Sopenharmony_ci		set_memory_wc((unsigned long)(p), size >> PAGE_SHIFT);
30662306a36Sopenharmony_ci#endif
30762306a36Sopenharmony_ci	return p;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic void do_free_pages(void *p, size_t size, bool wc)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci#ifdef CONFIG_X86
31362306a36Sopenharmony_ci	if (wc)
31462306a36Sopenharmony_ci		set_memory_wb((unsigned long)(p), size >> PAGE_SHIFT);
31562306a36Sopenharmony_ci#endif
31662306a36Sopenharmony_ci	free_pages_exact(p, size);
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic void *snd_dma_continuous_alloc(struct snd_dma_buffer *dmab, size_t size)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	return do_alloc_pages(dmab->dev.dev, size, &dmab->addr, false);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic void snd_dma_continuous_free(struct snd_dma_buffer *dmab)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	do_free_pages(dmab->area, dmab->bytes, false);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic int snd_dma_continuous_mmap(struct snd_dma_buffer *dmab,
33162306a36Sopenharmony_ci				   struct vm_area_struct *area)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	return remap_pfn_range(area, area->vm_start,
33462306a36Sopenharmony_ci			       dmab->addr >> PAGE_SHIFT,
33562306a36Sopenharmony_ci			       area->vm_end - area->vm_start,
33662306a36Sopenharmony_ci			       area->vm_page_prot);
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic const struct snd_malloc_ops snd_dma_continuous_ops = {
34062306a36Sopenharmony_ci	.alloc = snd_dma_continuous_alloc,
34162306a36Sopenharmony_ci	.free = snd_dma_continuous_free,
34262306a36Sopenharmony_ci	.mmap = snd_dma_continuous_mmap,
34362306a36Sopenharmony_ci};
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci/*
34662306a36Sopenharmony_ci * VMALLOC allocator
34762306a36Sopenharmony_ci */
34862306a36Sopenharmony_cistatic void *snd_dma_vmalloc_alloc(struct snd_dma_buffer *dmab, size_t size)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	return vmalloc(size);
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic void snd_dma_vmalloc_free(struct snd_dma_buffer *dmab)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	vfree(dmab->area);
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic int snd_dma_vmalloc_mmap(struct snd_dma_buffer *dmab,
35962306a36Sopenharmony_ci				struct vm_area_struct *area)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	return remap_vmalloc_range(area, dmab->area, 0);
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci#define get_vmalloc_page_addr(dmab, offset) \
36562306a36Sopenharmony_ci	page_to_phys(vmalloc_to_page((dmab)->area + (offset)))
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic dma_addr_t snd_dma_vmalloc_get_addr(struct snd_dma_buffer *dmab,
36862306a36Sopenharmony_ci					   size_t offset)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	return get_vmalloc_page_addr(dmab, offset) + offset % PAGE_SIZE;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic struct page *snd_dma_vmalloc_get_page(struct snd_dma_buffer *dmab,
37462306a36Sopenharmony_ci					     size_t offset)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	return vmalloc_to_page(dmab->area + offset);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic unsigned int
38062306a36Sopenharmony_cisnd_dma_vmalloc_get_chunk_size(struct snd_dma_buffer *dmab,
38162306a36Sopenharmony_ci			       unsigned int ofs, unsigned int size)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	unsigned int start, end;
38462306a36Sopenharmony_ci	unsigned long addr;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	start = ALIGN_DOWN(ofs, PAGE_SIZE);
38762306a36Sopenharmony_ci	end = ofs + size - 1; /* the last byte address */
38862306a36Sopenharmony_ci	/* check page continuity */
38962306a36Sopenharmony_ci	addr = get_vmalloc_page_addr(dmab, start);
39062306a36Sopenharmony_ci	for (;;) {
39162306a36Sopenharmony_ci		start += PAGE_SIZE;
39262306a36Sopenharmony_ci		if (start > end)
39362306a36Sopenharmony_ci			break;
39462306a36Sopenharmony_ci		addr += PAGE_SIZE;
39562306a36Sopenharmony_ci		if (get_vmalloc_page_addr(dmab, start) != addr)
39662306a36Sopenharmony_ci			return start - ofs;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci	/* ok, all on continuous pages */
39962306a36Sopenharmony_ci	return size;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic const struct snd_malloc_ops snd_dma_vmalloc_ops = {
40362306a36Sopenharmony_ci	.alloc = snd_dma_vmalloc_alloc,
40462306a36Sopenharmony_ci	.free = snd_dma_vmalloc_free,
40562306a36Sopenharmony_ci	.mmap = snd_dma_vmalloc_mmap,
40662306a36Sopenharmony_ci	.get_addr = snd_dma_vmalloc_get_addr,
40762306a36Sopenharmony_ci	.get_page = snd_dma_vmalloc_get_page,
40862306a36Sopenharmony_ci	.get_chunk_size = snd_dma_vmalloc_get_chunk_size,
40962306a36Sopenharmony_ci};
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci#ifdef CONFIG_HAS_DMA
41262306a36Sopenharmony_ci/*
41362306a36Sopenharmony_ci * IRAM allocator
41462306a36Sopenharmony_ci */
41562306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_ALLOCATOR
41662306a36Sopenharmony_cistatic void *snd_dma_iram_alloc(struct snd_dma_buffer *dmab, size_t size)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	struct device *dev = dmab->dev.dev;
41962306a36Sopenharmony_ci	struct gen_pool *pool;
42062306a36Sopenharmony_ci	void *p;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (dev->of_node) {
42362306a36Sopenharmony_ci		pool = of_gen_pool_get(dev->of_node, "iram", 0);
42462306a36Sopenharmony_ci		/* Assign the pool into private_data field */
42562306a36Sopenharmony_ci		dmab->private_data = pool;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci		p = gen_pool_dma_alloc_align(pool, size, &dmab->addr, PAGE_SIZE);
42862306a36Sopenharmony_ci		if (p)
42962306a36Sopenharmony_ci			return p;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* Internal memory might have limited size and no enough space,
43362306a36Sopenharmony_ci	 * so if we fail to malloc, try to fetch memory traditionally.
43462306a36Sopenharmony_ci	 */
43562306a36Sopenharmony_ci	dmab->dev.type = SNDRV_DMA_TYPE_DEV;
43662306a36Sopenharmony_ci	return __snd_dma_alloc_pages(dmab, size);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic void snd_dma_iram_free(struct snd_dma_buffer *dmab)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct gen_pool *pool = dmab->private_data;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (pool && dmab->area)
44462306a36Sopenharmony_ci		gen_pool_free(pool, (unsigned long)dmab->area, dmab->bytes);
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic int snd_dma_iram_mmap(struct snd_dma_buffer *dmab,
44862306a36Sopenharmony_ci			     struct vm_area_struct *area)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
45162306a36Sopenharmony_ci	return remap_pfn_range(area, area->vm_start,
45262306a36Sopenharmony_ci			       dmab->addr >> PAGE_SHIFT,
45362306a36Sopenharmony_ci			       area->vm_end - area->vm_start,
45462306a36Sopenharmony_ci			       area->vm_page_prot);
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic const struct snd_malloc_ops snd_dma_iram_ops = {
45862306a36Sopenharmony_ci	.alloc = snd_dma_iram_alloc,
45962306a36Sopenharmony_ci	.free = snd_dma_iram_free,
46062306a36Sopenharmony_ci	.mmap = snd_dma_iram_mmap,
46162306a36Sopenharmony_ci};
46262306a36Sopenharmony_ci#endif /* CONFIG_GENERIC_ALLOCATOR */
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci/*
46562306a36Sopenharmony_ci * Coherent device pages allocator
46662306a36Sopenharmony_ci */
46762306a36Sopenharmony_cistatic void *snd_dma_dev_alloc(struct snd_dma_buffer *dmab, size_t size)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	return dma_alloc_coherent(dmab->dev.dev, size, &dmab->addr, DEFAULT_GFP);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic void snd_dma_dev_free(struct snd_dma_buffer *dmab)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	dma_free_coherent(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic int snd_dma_dev_mmap(struct snd_dma_buffer *dmab,
47862306a36Sopenharmony_ci			    struct vm_area_struct *area)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	return dma_mmap_coherent(dmab->dev.dev, area,
48162306a36Sopenharmony_ci				 dmab->area, dmab->addr, dmab->bytes);
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic const struct snd_malloc_ops snd_dma_dev_ops = {
48562306a36Sopenharmony_ci	.alloc = snd_dma_dev_alloc,
48662306a36Sopenharmony_ci	.free = snd_dma_dev_free,
48762306a36Sopenharmony_ci	.mmap = snd_dma_dev_mmap,
48862306a36Sopenharmony_ci};
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci/*
49162306a36Sopenharmony_ci * Write-combined pages
49262306a36Sopenharmony_ci */
49362306a36Sopenharmony_ci/* x86-specific allocations */
49462306a36Sopenharmony_ci#ifdef CONFIG_SND_DMA_SGBUF
49562306a36Sopenharmony_cistatic void *snd_dma_wc_alloc(struct snd_dma_buffer *dmab, size_t size)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	return do_alloc_pages(dmab->dev.dev, size, &dmab->addr, true);
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic void snd_dma_wc_free(struct snd_dma_buffer *dmab)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	do_free_pages(dmab->area, dmab->bytes, true);
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic int snd_dma_wc_mmap(struct snd_dma_buffer *dmab,
50662306a36Sopenharmony_ci			   struct vm_area_struct *area)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
50962306a36Sopenharmony_ci	return snd_dma_continuous_mmap(dmab, area);
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci#else
51262306a36Sopenharmony_cistatic void *snd_dma_wc_alloc(struct snd_dma_buffer *dmab, size_t size)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	return dma_alloc_wc(dmab->dev.dev, size, &dmab->addr, DEFAULT_GFP);
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistatic void snd_dma_wc_free(struct snd_dma_buffer *dmab)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	dma_free_wc(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic int snd_dma_wc_mmap(struct snd_dma_buffer *dmab,
52362306a36Sopenharmony_ci			   struct vm_area_struct *area)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	return dma_mmap_wc(dmab->dev.dev, area,
52662306a36Sopenharmony_ci			   dmab->area, dmab->addr, dmab->bytes);
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci#endif /* CONFIG_SND_DMA_SGBUF */
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic const struct snd_malloc_ops snd_dma_wc_ops = {
53162306a36Sopenharmony_ci	.alloc = snd_dma_wc_alloc,
53262306a36Sopenharmony_ci	.free = snd_dma_wc_free,
53362306a36Sopenharmony_ci	.mmap = snd_dma_wc_mmap,
53462306a36Sopenharmony_ci};
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci/*
53762306a36Sopenharmony_ci * Non-contiguous pages allocator
53862306a36Sopenharmony_ci */
53962306a36Sopenharmony_cistatic void *snd_dma_noncontig_alloc(struct snd_dma_buffer *dmab, size_t size)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	struct sg_table *sgt;
54262306a36Sopenharmony_ci	void *p;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci#ifdef CONFIG_SND_DMA_SGBUF
54562306a36Sopenharmony_ci	if (cpu_feature_enabled(X86_FEATURE_XENPV))
54662306a36Sopenharmony_ci		return snd_dma_sg_fallback_alloc(dmab, size);
54762306a36Sopenharmony_ci#endif
54862306a36Sopenharmony_ci	sgt = dma_alloc_noncontiguous(dmab->dev.dev, size, dmab->dev.dir,
54962306a36Sopenharmony_ci				      DEFAULT_GFP, 0);
55062306a36Sopenharmony_ci#ifdef CONFIG_SND_DMA_SGBUF
55162306a36Sopenharmony_ci	if (!sgt && !get_dma_ops(dmab->dev.dev))
55262306a36Sopenharmony_ci		return snd_dma_sg_fallback_alloc(dmab, size);
55362306a36Sopenharmony_ci#endif
55462306a36Sopenharmony_ci	if (!sgt)
55562306a36Sopenharmony_ci		return NULL;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	dmab->dev.need_sync = dma_need_sync(dmab->dev.dev,
55862306a36Sopenharmony_ci					    sg_dma_address(sgt->sgl));
55962306a36Sopenharmony_ci	p = dma_vmap_noncontiguous(dmab->dev.dev, size, sgt);
56062306a36Sopenharmony_ci	if (p) {
56162306a36Sopenharmony_ci		dmab->private_data = sgt;
56262306a36Sopenharmony_ci		/* store the first page address for convenience */
56362306a36Sopenharmony_ci		dmab->addr = snd_sgbuf_get_addr(dmab, 0);
56462306a36Sopenharmony_ci	} else {
56562306a36Sopenharmony_ci		dma_free_noncontiguous(dmab->dev.dev, size, sgt, dmab->dev.dir);
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci	return p;
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cistatic void snd_dma_noncontig_free(struct snd_dma_buffer *dmab)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	dma_vunmap_noncontiguous(dmab->dev.dev, dmab->area);
57362306a36Sopenharmony_ci	dma_free_noncontiguous(dmab->dev.dev, dmab->bytes, dmab->private_data,
57462306a36Sopenharmony_ci			       dmab->dev.dir);
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic int snd_dma_noncontig_mmap(struct snd_dma_buffer *dmab,
57862306a36Sopenharmony_ci				  struct vm_area_struct *area)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	return dma_mmap_noncontiguous(dmab->dev.dev, area,
58162306a36Sopenharmony_ci				      dmab->bytes, dmab->private_data);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic void snd_dma_noncontig_sync(struct snd_dma_buffer *dmab,
58562306a36Sopenharmony_ci				   enum snd_dma_sync_mode mode)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	if (mode == SNDRV_DMA_SYNC_CPU) {
58862306a36Sopenharmony_ci		if (dmab->dev.dir == DMA_TO_DEVICE)
58962306a36Sopenharmony_ci			return;
59062306a36Sopenharmony_ci		invalidate_kernel_vmap_range(dmab->area, dmab->bytes);
59162306a36Sopenharmony_ci		dma_sync_sgtable_for_cpu(dmab->dev.dev, dmab->private_data,
59262306a36Sopenharmony_ci					 dmab->dev.dir);
59362306a36Sopenharmony_ci	} else {
59462306a36Sopenharmony_ci		if (dmab->dev.dir == DMA_FROM_DEVICE)
59562306a36Sopenharmony_ci			return;
59662306a36Sopenharmony_ci		flush_kernel_vmap_range(dmab->area, dmab->bytes);
59762306a36Sopenharmony_ci		dma_sync_sgtable_for_device(dmab->dev.dev, dmab->private_data,
59862306a36Sopenharmony_ci					    dmab->dev.dir);
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cistatic inline void snd_dma_noncontig_iter_set(struct snd_dma_buffer *dmab,
60362306a36Sopenharmony_ci					      struct sg_page_iter *piter,
60462306a36Sopenharmony_ci					      size_t offset)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	struct sg_table *sgt = dmab->private_data;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	__sg_page_iter_start(piter, sgt->sgl, sgt->orig_nents,
60962306a36Sopenharmony_ci			     offset >> PAGE_SHIFT);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic dma_addr_t snd_dma_noncontig_get_addr(struct snd_dma_buffer *dmab,
61362306a36Sopenharmony_ci					     size_t offset)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	struct sg_dma_page_iter iter;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	snd_dma_noncontig_iter_set(dmab, &iter.base, offset);
61862306a36Sopenharmony_ci	__sg_page_iter_dma_next(&iter);
61962306a36Sopenharmony_ci	return sg_page_iter_dma_address(&iter) + offset % PAGE_SIZE;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic struct page *snd_dma_noncontig_get_page(struct snd_dma_buffer *dmab,
62362306a36Sopenharmony_ci					       size_t offset)
62462306a36Sopenharmony_ci{
62562306a36Sopenharmony_ci	struct sg_page_iter iter;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	snd_dma_noncontig_iter_set(dmab, &iter, offset);
62862306a36Sopenharmony_ci	__sg_page_iter_next(&iter);
62962306a36Sopenharmony_ci	return sg_page_iter_page(&iter);
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic unsigned int
63362306a36Sopenharmony_cisnd_dma_noncontig_get_chunk_size(struct snd_dma_buffer *dmab,
63462306a36Sopenharmony_ci				 unsigned int ofs, unsigned int size)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	struct sg_dma_page_iter iter;
63762306a36Sopenharmony_ci	unsigned int start, end;
63862306a36Sopenharmony_ci	unsigned long addr;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	start = ALIGN_DOWN(ofs, PAGE_SIZE);
64162306a36Sopenharmony_ci	end = ofs + size - 1; /* the last byte address */
64262306a36Sopenharmony_ci	snd_dma_noncontig_iter_set(dmab, &iter.base, start);
64362306a36Sopenharmony_ci	if (!__sg_page_iter_dma_next(&iter))
64462306a36Sopenharmony_ci		return 0;
64562306a36Sopenharmony_ci	/* check page continuity */
64662306a36Sopenharmony_ci	addr = sg_page_iter_dma_address(&iter);
64762306a36Sopenharmony_ci	for (;;) {
64862306a36Sopenharmony_ci		start += PAGE_SIZE;
64962306a36Sopenharmony_ci		if (start > end)
65062306a36Sopenharmony_ci			break;
65162306a36Sopenharmony_ci		addr += PAGE_SIZE;
65262306a36Sopenharmony_ci		if (!__sg_page_iter_dma_next(&iter) ||
65362306a36Sopenharmony_ci		    sg_page_iter_dma_address(&iter) != addr)
65462306a36Sopenharmony_ci			return start - ofs;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci	/* ok, all on continuous pages */
65762306a36Sopenharmony_ci	return size;
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cistatic const struct snd_malloc_ops snd_dma_noncontig_ops = {
66162306a36Sopenharmony_ci	.alloc = snd_dma_noncontig_alloc,
66262306a36Sopenharmony_ci	.free = snd_dma_noncontig_free,
66362306a36Sopenharmony_ci	.mmap = snd_dma_noncontig_mmap,
66462306a36Sopenharmony_ci	.sync = snd_dma_noncontig_sync,
66562306a36Sopenharmony_ci	.get_addr = snd_dma_noncontig_get_addr,
66662306a36Sopenharmony_ci	.get_page = snd_dma_noncontig_get_page,
66762306a36Sopenharmony_ci	.get_chunk_size = snd_dma_noncontig_get_chunk_size,
66862306a36Sopenharmony_ci};
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci/* x86-specific SG-buffer with WC pages */
67162306a36Sopenharmony_ci#ifdef CONFIG_SND_DMA_SGBUF
67262306a36Sopenharmony_ci#define sg_wc_address(it) ((unsigned long)page_address(sg_page_iter_page(it)))
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic void *snd_dma_sg_wc_alloc(struct snd_dma_buffer *dmab, size_t size)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	void *p = snd_dma_noncontig_alloc(dmab, size);
67762306a36Sopenharmony_ci	struct sg_table *sgt = dmab->private_data;
67862306a36Sopenharmony_ci	struct sg_page_iter iter;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	if (!p)
68162306a36Sopenharmony_ci		return NULL;
68262306a36Sopenharmony_ci	if (dmab->dev.type != SNDRV_DMA_TYPE_DEV_WC_SG)
68362306a36Sopenharmony_ci		return p;
68462306a36Sopenharmony_ci	for_each_sgtable_page(sgt, &iter, 0)
68562306a36Sopenharmony_ci		set_memory_wc(sg_wc_address(&iter), 1);
68662306a36Sopenharmony_ci	return p;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistatic void snd_dma_sg_wc_free(struct snd_dma_buffer *dmab)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	struct sg_table *sgt = dmab->private_data;
69262306a36Sopenharmony_ci	struct sg_page_iter iter;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	for_each_sgtable_page(sgt, &iter, 0)
69562306a36Sopenharmony_ci		set_memory_wb(sg_wc_address(&iter), 1);
69662306a36Sopenharmony_ci	snd_dma_noncontig_free(dmab);
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic int snd_dma_sg_wc_mmap(struct snd_dma_buffer *dmab,
70062306a36Sopenharmony_ci			      struct vm_area_struct *area)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
70362306a36Sopenharmony_ci	return dma_mmap_noncontiguous(dmab->dev.dev, area,
70462306a36Sopenharmony_ci				      dmab->bytes, dmab->private_data);
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic const struct snd_malloc_ops snd_dma_sg_wc_ops = {
70862306a36Sopenharmony_ci	.alloc = snd_dma_sg_wc_alloc,
70962306a36Sopenharmony_ci	.free = snd_dma_sg_wc_free,
71062306a36Sopenharmony_ci	.mmap = snd_dma_sg_wc_mmap,
71162306a36Sopenharmony_ci	.sync = snd_dma_noncontig_sync,
71262306a36Sopenharmony_ci	.get_addr = snd_dma_noncontig_get_addr,
71362306a36Sopenharmony_ci	.get_page = snd_dma_noncontig_get_page,
71462306a36Sopenharmony_ci	.get_chunk_size = snd_dma_noncontig_get_chunk_size,
71562306a36Sopenharmony_ci};
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci/* Fallback SG-buffer allocations for x86 */
71862306a36Sopenharmony_cistruct snd_dma_sg_fallback {
71962306a36Sopenharmony_ci	bool use_dma_alloc_coherent;
72062306a36Sopenharmony_ci	size_t count;
72162306a36Sopenharmony_ci	struct page **pages;
72262306a36Sopenharmony_ci	/* DMA address array; the first page contains #pages in ~PAGE_MASK */
72362306a36Sopenharmony_ci	dma_addr_t *addrs;
72462306a36Sopenharmony_ci};
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cistatic void __snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab,
72762306a36Sopenharmony_ci				       struct snd_dma_sg_fallback *sgbuf)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	size_t i, size;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	if (sgbuf->pages && sgbuf->addrs) {
73262306a36Sopenharmony_ci		i = 0;
73362306a36Sopenharmony_ci		while (i < sgbuf->count) {
73462306a36Sopenharmony_ci			if (!sgbuf->pages[i] || !sgbuf->addrs[i])
73562306a36Sopenharmony_ci				break;
73662306a36Sopenharmony_ci			size = sgbuf->addrs[i] & ~PAGE_MASK;
73762306a36Sopenharmony_ci			if (WARN_ON(!size))
73862306a36Sopenharmony_ci				break;
73962306a36Sopenharmony_ci			if (sgbuf->use_dma_alloc_coherent)
74062306a36Sopenharmony_ci				dma_free_coherent(dmab->dev.dev, size << PAGE_SHIFT,
74162306a36Sopenharmony_ci						  page_address(sgbuf->pages[i]),
74262306a36Sopenharmony_ci						  sgbuf->addrs[i] & PAGE_MASK);
74362306a36Sopenharmony_ci			else
74462306a36Sopenharmony_ci				do_free_pages(page_address(sgbuf->pages[i]),
74562306a36Sopenharmony_ci					      size << PAGE_SHIFT, false);
74662306a36Sopenharmony_ci			i += size;
74762306a36Sopenharmony_ci		}
74862306a36Sopenharmony_ci	}
74962306a36Sopenharmony_ci	kvfree(sgbuf->pages);
75062306a36Sopenharmony_ci	kvfree(sgbuf->addrs);
75162306a36Sopenharmony_ci	kfree(sgbuf);
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_cistatic void *snd_dma_sg_fallback_alloc(struct snd_dma_buffer *dmab, size_t size)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	struct snd_dma_sg_fallback *sgbuf;
75762306a36Sopenharmony_ci	struct page **pagep, *curp;
75862306a36Sopenharmony_ci	size_t chunk, npages;
75962306a36Sopenharmony_ci	dma_addr_t *addrp;
76062306a36Sopenharmony_ci	dma_addr_t addr;
76162306a36Sopenharmony_ci	void *p;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	/* correct the type */
76462306a36Sopenharmony_ci	if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_SG)
76562306a36Sopenharmony_ci		dmab->dev.type = SNDRV_DMA_TYPE_DEV_SG_FALLBACK;
76662306a36Sopenharmony_ci	else if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG)
76762306a36Sopenharmony_ci		dmab->dev.type = SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
77062306a36Sopenharmony_ci	if (!sgbuf)
77162306a36Sopenharmony_ci		return NULL;
77262306a36Sopenharmony_ci	sgbuf->use_dma_alloc_coherent = cpu_feature_enabled(X86_FEATURE_XENPV);
77362306a36Sopenharmony_ci	size = PAGE_ALIGN(size);
77462306a36Sopenharmony_ci	sgbuf->count = size >> PAGE_SHIFT;
77562306a36Sopenharmony_ci	sgbuf->pages = kvcalloc(sgbuf->count, sizeof(*sgbuf->pages), GFP_KERNEL);
77662306a36Sopenharmony_ci	sgbuf->addrs = kvcalloc(sgbuf->count, sizeof(*sgbuf->addrs), GFP_KERNEL);
77762306a36Sopenharmony_ci	if (!sgbuf->pages || !sgbuf->addrs)
77862306a36Sopenharmony_ci		goto error;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	pagep = sgbuf->pages;
78162306a36Sopenharmony_ci	addrp = sgbuf->addrs;
78262306a36Sopenharmony_ci	chunk = (PAGE_SIZE - 1) << PAGE_SHIFT; /* to fit in low bits in addrs */
78362306a36Sopenharmony_ci	while (size > 0) {
78462306a36Sopenharmony_ci		chunk = min(size, chunk);
78562306a36Sopenharmony_ci		if (sgbuf->use_dma_alloc_coherent)
78662306a36Sopenharmony_ci			p = dma_alloc_coherent(dmab->dev.dev, chunk, &addr, DEFAULT_GFP);
78762306a36Sopenharmony_ci		else
78862306a36Sopenharmony_ci			p = do_alloc_pages(dmab->dev.dev, chunk, &addr, false);
78962306a36Sopenharmony_ci		if (!p) {
79062306a36Sopenharmony_ci			if (chunk <= PAGE_SIZE)
79162306a36Sopenharmony_ci				goto error;
79262306a36Sopenharmony_ci			chunk >>= 1;
79362306a36Sopenharmony_ci			chunk = PAGE_SIZE << get_order(chunk);
79462306a36Sopenharmony_ci			continue;
79562306a36Sopenharmony_ci		}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci		size -= chunk;
79862306a36Sopenharmony_ci		/* fill pages */
79962306a36Sopenharmony_ci		npages = chunk >> PAGE_SHIFT;
80062306a36Sopenharmony_ci		*addrp = npages; /* store in lower bits */
80162306a36Sopenharmony_ci		curp = virt_to_page(p);
80262306a36Sopenharmony_ci		while (npages--) {
80362306a36Sopenharmony_ci			*pagep++ = curp++;
80462306a36Sopenharmony_ci			*addrp++ |= addr;
80562306a36Sopenharmony_ci			addr += PAGE_SIZE;
80662306a36Sopenharmony_ci		}
80762306a36Sopenharmony_ci	}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	p = vmap(sgbuf->pages, sgbuf->count, VM_MAP, PAGE_KERNEL);
81062306a36Sopenharmony_ci	if (!p)
81162306a36Sopenharmony_ci		goto error;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK)
81462306a36Sopenharmony_ci		set_pages_array_wc(sgbuf->pages, sgbuf->count);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	dmab->private_data = sgbuf;
81762306a36Sopenharmony_ci	/* store the first page address for convenience */
81862306a36Sopenharmony_ci	dmab->addr = sgbuf->addrs[0] & PAGE_MASK;
81962306a36Sopenharmony_ci	return p;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci error:
82262306a36Sopenharmony_ci	__snd_dma_sg_fallback_free(dmab, sgbuf);
82362306a36Sopenharmony_ci	return NULL;
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistatic void snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	struct snd_dma_sg_fallback *sgbuf = dmab->private_data;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK)
83162306a36Sopenharmony_ci		set_pages_array_wb(sgbuf->pages, sgbuf->count);
83262306a36Sopenharmony_ci	vunmap(dmab->area);
83362306a36Sopenharmony_ci	__snd_dma_sg_fallback_free(dmab, dmab->private_data);
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic dma_addr_t snd_dma_sg_fallback_get_addr(struct snd_dma_buffer *dmab,
83762306a36Sopenharmony_ci					       size_t offset)
83862306a36Sopenharmony_ci{
83962306a36Sopenharmony_ci	struct snd_dma_sg_fallback *sgbuf = dmab->private_data;
84062306a36Sopenharmony_ci	size_t index = offset >> PAGE_SHIFT;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	return (sgbuf->addrs[index] & PAGE_MASK) | (offset & ~PAGE_MASK);
84362306a36Sopenharmony_ci}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_cistatic int snd_dma_sg_fallback_mmap(struct snd_dma_buffer *dmab,
84662306a36Sopenharmony_ci				    struct vm_area_struct *area)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	struct snd_dma_sg_fallback *sgbuf = dmab->private_data;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK)
85162306a36Sopenharmony_ci		area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
85262306a36Sopenharmony_ci	return vm_map_pages(area, sgbuf->pages, sgbuf->count);
85362306a36Sopenharmony_ci}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_cistatic const struct snd_malloc_ops snd_dma_sg_fallback_ops = {
85662306a36Sopenharmony_ci	.alloc = snd_dma_sg_fallback_alloc,
85762306a36Sopenharmony_ci	.free = snd_dma_sg_fallback_free,
85862306a36Sopenharmony_ci	.mmap = snd_dma_sg_fallback_mmap,
85962306a36Sopenharmony_ci	.get_addr = snd_dma_sg_fallback_get_addr,
86062306a36Sopenharmony_ci	/* reuse vmalloc helpers */
86162306a36Sopenharmony_ci	.get_page = snd_dma_vmalloc_get_page,
86262306a36Sopenharmony_ci	.get_chunk_size = snd_dma_vmalloc_get_chunk_size,
86362306a36Sopenharmony_ci};
86462306a36Sopenharmony_ci#endif /* CONFIG_SND_DMA_SGBUF */
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci/*
86762306a36Sopenharmony_ci * Non-coherent pages allocator
86862306a36Sopenharmony_ci */
86962306a36Sopenharmony_cistatic void *snd_dma_noncoherent_alloc(struct snd_dma_buffer *dmab, size_t size)
87062306a36Sopenharmony_ci{
87162306a36Sopenharmony_ci	void *p;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	p = dma_alloc_noncoherent(dmab->dev.dev, size, &dmab->addr,
87462306a36Sopenharmony_ci				  dmab->dev.dir, DEFAULT_GFP);
87562306a36Sopenharmony_ci	if (p)
87662306a36Sopenharmony_ci		dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, dmab->addr);
87762306a36Sopenharmony_ci	return p;
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic void snd_dma_noncoherent_free(struct snd_dma_buffer *dmab)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	dma_free_noncoherent(dmab->dev.dev, dmab->bytes, dmab->area,
88362306a36Sopenharmony_ci			     dmab->addr, dmab->dev.dir);
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_cistatic int snd_dma_noncoherent_mmap(struct snd_dma_buffer *dmab,
88762306a36Sopenharmony_ci				    struct vm_area_struct *area)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	area->vm_page_prot = vm_get_page_prot(area->vm_flags);
89062306a36Sopenharmony_ci	return dma_mmap_pages(dmab->dev.dev, area,
89162306a36Sopenharmony_ci			      area->vm_end - area->vm_start,
89262306a36Sopenharmony_ci			      virt_to_page(dmab->area));
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_cistatic void snd_dma_noncoherent_sync(struct snd_dma_buffer *dmab,
89662306a36Sopenharmony_ci				     enum snd_dma_sync_mode mode)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	if (mode == SNDRV_DMA_SYNC_CPU) {
89962306a36Sopenharmony_ci		if (dmab->dev.dir != DMA_TO_DEVICE)
90062306a36Sopenharmony_ci			dma_sync_single_for_cpu(dmab->dev.dev, dmab->addr,
90162306a36Sopenharmony_ci						dmab->bytes, dmab->dev.dir);
90262306a36Sopenharmony_ci	} else {
90362306a36Sopenharmony_ci		if (dmab->dev.dir != DMA_FROM_DEVICE)
90462306a36Sopenharmony_ci			dma_sync_single_for_device(dmab->dev.dev, dmab->addr,
90562306a36Sopenharmony_ci						   dmab->bytes, dmab->dev.dir);
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_cistatic const struct snd_malloc_ops snd_dma_noncoherent_ops = {
91062306a36Sopenharmony_ci	.alloc = snd_dma_noncoherent_alloc,
91162306a36Sopenharmony_ci	.free = snd_dma_noncoherent_free,
91262306a36Sopenharmony_ci	.mmap = snd_dma_noncoherent_mmap,
91362306a36Sopenharmony_ci	.sync = snd_dma_noncoherent_sync,
91462306a36Sopenharmony_ci};
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci#endif /* CONFIG_HAS_DMA */
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci/*
91962306a36Sopenharmony_ci * Entry points
92062306a36Sopenharmony_ci */
92162306a36Sopenharmony_cistatic const struct snd_malloc_ops *snd_dma_ops[] = {
92262306a36Sopenharmony_ci	[SNDRV_DMA_TYPE_CONTINUOUS] = &snd_dma_continuous_ops,
92362306a36Sopenharmony_ci	[SNDRV_DMA_TYPE_VMALLOC] = &snd_dma_vmalloc_ops,
92462306a36Sopenharmony_ci#ifdef CONFIG_HAS_DMA
92562306a36Sopenharmony_ci	[SNDRV_DMA_TYPE_DEV] = &snd_dma_dev_ops,
92662306a36Sopenharmony_ci	[SNDRV_DMA_TYPE_DEV_WC] = &snd_dma_wc_ops,
92762306a36Sopenharmony_ci	[SNDRV_DMA_TYPE_NONCONTIG] = &snd_dma_noncontig_ops,
92862306a36Sopenharmony_ci	[SNDRV_DMA_TYPE_NONCOHERENT] = &snd_dma_noncoherent_ops,
92962306a36Sopenharmony_ci#ifdef CONFIG_SND_DMA_SGBUF
93062306a36Sopenharmony_ci	[SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_wc_ops,
93162306a36Sopenharmony_ci#endif
93262306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_ALLOCATOR
93362306a36Sopenharmony_ci	[SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops,
93462306a36Sopenharmony_ci#endif /* CONFIG_GENERIC_ALLOCATOR */
93562306a36Sopenharmony_ci#ifdef CONFIG_SND_DMA_SGBUF
93662306a36Sopenharmony_ci	[SNDRV_DMA_TYPE_DEV_SG_FALLBACK] = &snd_dma_sg_fallback_ops,
93762306a36Sopenharmony_ci	[SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK] = &snd_dma_sg_fallback_ops,
93862306a36Sopenharmony_ci#endif
93962306a36Sopenharmony_ci#endif /* CONFIG_HAS_DMA */
94062306a36Sopenharmony_ci};
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_cistatic const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	if (WARN_ON_ONCE(!dmab))
94562306a36Sopenharmony_ci		return NULL;
94662306a36Sopenharmony_ci	if (WARN_ON_ONCE(dmab->dev.type <= SNDRV_DMA_TYPE_UNKNOWN ||
94762306a36Sopenharmony_ci			 dmab->dev.type >= ARRAY_SIZE(snd_dma_ops)))
94862306a36Sopenharmony_ci		return NULL;
94962306a36Sopenharmony_ci	return snd_dma_ops[dmab->dev.type];
95062306a36Sopenharmony_ci}
951