1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * ISA DMA support functions 4 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 5 */ 6 7 /* 8 * Defining following add some delay. Maybe this helps for some broken 9 * ISA DMA controllers. 10 */ 11 12 #undef HAVE_REALLY_SLOW_DMA_CONTROLLER 13 14 #include <linux/export.h> 15 #include <linux/isa-dma.h> 16 #include <sound/core.h> 17 18 /** 19 * snd_dma_program - program an ISA DMA transfer 20 * @dma: the dma number 21 * @addr: the physical address of the buffer 22 * @size: the DMA transfer size 23 * @mode: the DMA transfer mode, DMA_MODE_XXX 24 * 25 * Programs an ISA DMA transfer for the given buffer. 26 */ 27 void snd_dma_program(unsigned long dma, 28 unsigned long addr, unsigned int size, 29 unsigned short mode) 30 { 31 unsigned long flags; 32 33 flags = claim_dma_lock(); 34 disable_dma(dma); 35 clear_dma_ff(dma); 36 set_dma_mode(dma, mode); 37 set_dma_addr(dma, addr); 38 set_dma_count(dma, size); 39 if (!(mode & DMA_MODE_NO_ENABLE)) 40 enable_dma(dma); 41 release_dma_lock(flags); 42 } 43 EXPORT_SYMBOL(snd_dma_program); 44 45 /** 46 * snd_dma_disable - stop the ISA DMA transfer 47 * @dma: the dma number 48 * 49 * Stops the ISA DMA transfer. 50 */ 51 void snd_dma_disable(unsigned long dma) 52 { 53 unsigned long flags; 54 55 flags = claim_dma_lock(); 56 clear_dma_ff(dma); 57 disable_dma(dma); 58 release_dma_lock(flags); 59 } 60 EXPORT_SYMBOL(snd_dma_disable); 61 62 /** 63 * snd_dma_pointer - return the current pointer to DMA transfer buffer in bytes 64 * @dma: the dma number 65 * @size: the dma transfer size 66 * 67 * Return: The current pointer in DMA transfer buffer in bytes. 68 */ 69 unsigned int snd_dma_pointer(unsigned long dma, unsigned int size) 70 { 71 unsigned long flags; 72 unsigned int result, result1; 73 74 flags = claim_dma_lock(); 75 clear_dma_ff(dma); 76 if (!isa_dma_bridge_buggy) 77 disable_dma(dma); 78 result = get_dma_residue(dma); 79 /* 80 * HACK - read the counter again and choose higher value in order to 81 * avoid reading during counter lower byte roll over if the 82 * isa_dma_bridge_buggy is set. 83 */ 84 result1 = get_dma_residue(dma); 85 if (!isa_dma_bridge_buggy) 86 enable_dma(dma); 87 release_dma_lock(flags); 88 if (unlikely(result < result1)) 89 result = result1; 90 #ifdef CONFIG_SND_DEBUG 91 if (result > size) 92 pr_err("ALSA: pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size); 93 #endif 94 if (result >= size || result == 0) 95 return 0; 96 else 97 return size - result; 98 } 99 EXPORT_SYMBOL(snd_dma_pointer); 100 101 struct snd_dma_data { 102 int dma; 103 }; 104 105 static void __snd_release_dma(struct device *dev, void *data) 106 { 107 struct snd_dma_data *p = data; 108 109 snd_dma_disable(p->dma); 110 free_dma(p->dma); 111 } 112 113 /** 114 * snd_devm_request_dma - the managed version of request_dma() 115 * @dev: the device pointer 116 * @dma: the dma number 117 * @name: the name string of the requester 118 * 119 * Returns zero on success, or a negative error code. 120 * The requested DMA will be automatically released at unbinding via devres. 121 */ 122 int snd_devm_request_dma(struct device *dev, int dma, const char *name) 123 { 124 struct snd_dma_data *p; 125 126 if (request_dma(dma, name)) 127 return -EBUSY; 128 p = devres_alloc(__snd_release_dma, sizeof(*p), GFP_KERNEL); 129 if (!p) { 130 free_dma(dma); 131 return -ENOMEM; 132 } 133 p->dma = dma; 134 devres_add(dev, p); 135 return 0; 136 } 137 EXPORT_SYMBOL_GPL(snd_devm_request_dma); 138