xref: /linux/sound/core/isadma.c (revision abb4970a)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  ISA DMA support functions
4c1017a4cSJaroslav Kysela  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
51da177e4SLinus Torvalds  */
61da177e4SLinus Torvalds 
71da177e4SLinus Torvalds /*
81da177e4SLinus Torvalds  * Defining following add some delay. Maybe this helps for some broken
91da177e4SLinus Torvalds  * ISA DMA controllers.
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #undef HAVE_REALLY_SLOW_DMA_CONTROLLER
131da177e4SLinus Torvalds 
14d81a6d71SPaul Gortmaker #include <linux/export.h>
15*abb4970aSStafford Horne #include <linux/isa-dma.h>
161da177e4SLinus Torvalds #include <sound/core.h>
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds /**
191da177e4SLinus Torvalds  * snd_dma_program - program an ISA DMA transfer
201da177e4SLinus Torvalds  * @dma: the dma number
211da177e4SLinus Torvalds  * @addr: the physical address of the buffer
221da177e4SLinus Torvalds  * @size: the DMA transfer size
231da177e4SLinus Torvalds  * @mode: the DMA transfer mode, DMA_MODE_XXX
241da177e4SLinus Torvalds  *
251da177e4SLinus Torvalds  * Programs an ISA DMA transfer for the given buffer.
261da177e4SLinus Torvalds  */
snd_dma_program(unsigned long dma,unsigned long addr,unsigned int size,unsigned short mode)271da177e4SLinus Torvalds void snd_dma_program(unsigned long dma,
281da177e4SLinus Torvalds 		     unsigned long addr, unsigned int size,
291da177e4SLinus Torvalds                      unsigned short mode)
301da177e4SLinus Torvalds {
311da177e4SLinus Torvalds 	unsigned long flags;
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds 	flags = claim_dma_lock();
341da177e4SLinus Torvalds 	disable_dma(dma);
351da177e4SLinus Torvalds 	clear_dma_ff(dma);
361da177e4SLinus Torvalds 	set_dma_mode(dma, mode);
371da177e4SLinus Torvalds 	set_dma_addr(dma, addr);
381da177e4SLinus Torvalds 	set_dma_count(dma, size);
391da177e4SLinus Torvalds 	if (!(mode & DMA_MODE_NO_ENABLE))
401da177e4SLinus Torvalds 		enable_dma(dma);
411da177e4SLinus Torvalds 	release_dma_lock(flags);
421da177e4SLinus Torvalds }
43c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_dma_program);
44c0d3fb39STakashi Iwai 
451da177e4SLinus Torvalds /**
461da177e4SLinus Torvalds  * snd_dma_disable - stop the ISA DMA transfer
471da177e4SLinus Torvalds  * @dma: the dma number
481da177e4SLinus Torvalds  *
491da177e4SLinus Torvalds  * Stops the ISA DMA transfer.
501da177e4SLinus Torvalds  */
snd_dma_disable(unsigned long dma)511da177e4SLinus Torvalds void snd_dma_disable(unsigned long dma)
521da177e4SLinus Torvalds {
531da177e4SLinus Torvalds 	unsigned long flags;
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds 	flags = claim_dma_lock();
561da177e4SLinus Torvalds 	clear_dma_ff(dma);
571da177e4SLinus Torvalds 	disable_dma(dma);
581da177e4SLinus Torvalds 	release_dma_lock(flags);
591da177e4SLinus Torvalds }
60c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_dma_disable);
61c0d3fb39STakashi Iwai 
621da177e4SLinus Torvalds /**
631da177e4SLinus Torvalds  * snd_dma_pointer - return the current pointer to DMA transfer buffer in bytes
641da177e4SLinus Torvalds  * @dma: the dma number
651da177e4SLinus Torvalds  * @size: the dma transfer size
661da177e4SLinus Torvalds  *
67eb7c06e8SYacine Belkadi  * Return: The current pointer in DMA transfer buffer in bytes.
681da177e4SLinus Torvalds  */
snd_dma_pointer(unsigned long dma,unsigned int size)691da177e4SLinus Torvalds unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
701da177e4SLinus Torvalds {
711da177e4SLinus Torvalds 	unsigned long flags;
728066e51aSKrzysztof Helt 	unsigned int result, result1;
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds 	flags = claim_dma_lock();
751da177e4SLinus Torvalds 	clear_dma_ff(dma);
761da177e4SLinus Torvalds 	if (!isa_dma_bridge_buggy)
771da177e4SLinus Torvalds 		disable_dma(dma);
781da177e4SLinus Torvalds 	result = get_dma_residue(dma);
798066e51aSKrzysztof Helt 	/*
808066e51aSKrzysztof Helt 	 * HACK - read the counter again and choose higher value in order to
818066e51aSKrzysztof Helt 	 * avoid reading during counter lower byte roll over if the
828066e51aSKrzysztof Helt 	 * isa_dma_bridge_buggy is set.
838066e51aSKrzysztof Helt 	 */
848066e51aSKrzysztof Helt 	result1 = get_dma_residue(dma);
851da177e4SLinus Torvalds 	if (!isa_dma_bridge_buggy)
861da177e4SLinus Torvalds 		enable_dma(dma);
871da177e4SLinus Torvalds 	release_dma_lock(flags);
888066e51aSKrzysztof Helt 	if (unlikely(result < result1))
898066e51aSKrzysztof Helt 		result = result1;
901da177e4SLinus Torvalds #ifdef CONFIG_SND_DEBUG
911da177e4SLinus Torvalds 	if (result > size)
92f2f9307aSTakashi Iwai 		pr_err("ALSA: pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
931da177e4SLinus Torvalds #endif
941da177e4SLinus Torvalds 	if (result >= size || result == 0)
951da177e4SLinus Torvalds 		return 0;
961da177e4SLinus Torvalds 	else
971da177e4SLinus Torvalds 		return size - result;
981da177e4SLinus Torvalds }
99c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_dma_pointer);
100c2b94954STakashi Iwai 
101c2b94954STakashi Iwai struct snd_dma_data {
102c2b94954STakashi Iwai 	int dma;
103c2b94954STakashi Iwai };
104c2b94954STakashi Iwai 
__snd_release_dma(struct device * dev,void * data)105c2b94954STakashi Iwai static void __snd_release_dma(struct device *dev, void *data)
106c2b94954STakashi Iwai {
107c2b94954STakashi Iwai 	struct snd_dma_data *p = data;
108c2b94954STakashi Iwai 
109c2b94954STakashi Iwai 	snd_dma_disable(p->dma);
110c2b94954STakashi Iwai 	free_dma(p->dma);
111c2b94954STakashi Iwai }
112c2b94954STakashi Iwai 
113c2b94954STakashi Iwai /**
114c2b94954STakashi Iwai  * snd_devm_request_dma - the managed version of request_dma()
115c2b94954STakashi Iwai  * @dev: the device pointer
116c2b94954STakashi Iwai  * @dma: the dma number
117c2b94954STakashi Iwai  * @name: the name string of the requester
118c2b94954STakashi Iwai  *
119c2b94954STakashi Iwai  * The requested DMA will be automatically released at unbinding via devres.
120c2b94954STakashi Iwai  *
121c2b94954STakashi Iwai  * Return: zero on success, or a negative error code
122c2b94954STakashi Iwai  */
snd_devm_request_dma(struct device * dev,int dma,const char * name)123c2b94954STakashi Iwai int snd_devm_request_dma(struct device *dev, int dma, const char *name)
124c2b94954STakashi Iwai {
125c2b94954STakashi Iwai 	struct snd_dma_data *p;
126c2b94954STakashi Iwai 
127c2b94954STakashi Iwai 	if (request_dma(dma, name))
128c2b94954STakashi Iwai 		return -EBUSY;
129c2b94954STakashi Iwai 	p = devres_alloc(__snd_release_dma, sizeof(*p), GFP_KERNEL);
130c2b94954STakashi Iwai 	if (!p) {
131c2b94954STakashi Iwai 		free_dma(dma);
132c2b94954STakashi Iwai 		return -ENOMEM;
133c2b94954STakashi Iwai 	}
134c2b94954STakashi Iwai 	p->dma = dma;
135c2b94954STakashi Iwai 	devres_add(dev, p);
136c2b94954STakashi Iwai 	return 0;
137c2b94954STakashi Iwai }
138 EXPORT_SYMBOL_GPL(snd_devm_request_dma);
139