18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  ISA DMA support functions
48c2ecf20Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci/*
88c2ecf20Sopenharmony_ci * Defining following add some delay. Maybe this helps for some broken
98c2ecf20Sopenharmony_ci * ISA DMA controllers.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#undef HAVE_REALLY_SLOW_DMA_CONTROLLER
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/export.h>
158c2ecf20Sopenharmony_ci#include <sound/core.h>
168c2ecf20Sopenharmony_ci#include <asm/dma.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/**
198c2ecf20Sopenharmony_ci * snd_dma_program - program an ISA DMA transfer
208c2ecf20Sopenharmony_ci * @dma: the dma number
218c2ecf20Sopenharmony_ci * @addr: the physical address of the buffer
228c2ecf20Sopenharmony_ci * @size: the DMA transfer size
238c2ecf20Sopenharmony_ci * @mode: the DMA transfer mode, DMA_MODE_XXX
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * Programs an ISA DMA transfer for the given buffer.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_civoid snd_dma_program(unsigned long dma,
288c2ecf20Sopenharmony_ci		     unsigned long addr, unsigned int size,
298c2ecf20Sopenharmony_ci                     unsigned short mode)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	unsigned long flags;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	flags = claim_dma_lock();
348c2ecf20Sopenharmony_ci	disable_dma(dma);
358c2ecf20Sopenharmony_ci	clear_dma_ff(dma);
368c2ecf20Sopenharmony_ci	set_dma_mode(dma, mode);
378c2ecf20Sopenharmony_ci	set_dma_addr(dma, addr);
388c2ecf20Sopenharmony_ci	set_dma_count(dma, size);
398c2ecf20Sopenharmony_ci	if (!(mode & DMA_MODE_NO_ENABLE))
408c2ecf20Sopenharmony_ci		enable_dma(dma);
418c2ecf20Sopenharmony_ci	release_dma_lock(flags);
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_dma_program);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/**
468c2ecf20Sopenharmony_ci * snd_dma_disable - stop the ISA DMA transfer
478c2ecf20Sopenharmony_ci * @dma: the dma number
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci * Stops the ISA DMA transfer.
508c2ecf20Sopenharmony_ci */
518c2ecf20Sopenharmony_civoid snd_dma_disable(unsigned long dma)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	unsigned long flags;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	flags = claim_dma_lock();
568c2ecf20Sopenharmony_ci	clear_dma_ff(dma);
578c2ecf20Sopenharmony_ci	disable_dma(dma);
588c2ecf20Sopenharmony_ci	release_dma_lock(flags);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_dma_disable);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/**
638c2ecf20Sopenharmony_ci * snd_dma_pointer - return the current pointer to DMA transfer buffer in bytes
648c2ecf20Sopenharmony_ci * @dma: the dma number
658c2ecf20Sopenharmony_ci * @size: the dma transfer size
668c2ecf20Sopenharmony_ci *
678c2ecf20Sopenharmony_ci * Return: The current pointer in DMA transfer buffer in bytes.
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_ciunsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	unsigned long flags;
728c2ecf20Sopenharmony_ci	unsigned int result, result1;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	flags = claim_dma_lock();
758c2ecf20Sopenharmony_ci	clear_dma_ff(dma);
768c2ecf20Sopenharmony_ci	if (!isa_dma_bridge_buggy)
778c2ecf20Sopenharmony_ci		disable_dma(dma);
788c2ecf20Sopenharmony_ci	result = get_dma_residue(dma);
798c2ecf20Sopenharmony_ci	/*
808c2ecf20Sopenharmony_ci	 * HACK - read the counter again and choose higher value in order to
818c2ecf20Sopenharmony_ci	 * avoid reading during counter lower byte roll over if the
828c2ecf20Sopenharmony_ci	 * isa_dma_bridge_buggy is set.
838c2ecf20Sopenharmony_ci	 */
848c2ecf20Sopenharmony_ci	result1 = get_dma_residue(dma);
858c2ecf20Sopenharmony_ci	if (!isa_dma_bridge_buggy)
868c2ecf20Sopenharmony_ci		enable_dma(dma);
878c2ecf20Sopenharmony_ci	release_dma_lock(flags);
888c2ecf20Sopenharmony_ci	if (unlikely(result < result1))
898c2ecf20Sopenharmony_ci		result = result1;
908c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
918c2ecf20Sopenharmony_ci	if (result > size)
928c2ecf20Sopenharmony_ci		pr_err("ALSA: pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
938c2ecf20Sopenharmony_ci#endif
948c2ecf20Sopenharmony_ci	if (result >= size || result == 0)
958c2ecf20Sopenharmony_ci		return 0;
968c2ecf20Sopenharmony_ci	else
978c2ecf20Sopenharmony_ci		return size - result;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_dma_pointer);
100