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