xref: /kernel/linux/linux-5.10/sound/isa/gus/gus_dma.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Routines for GF1 DMA control
48c2ecf20Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <asm/dma.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <sound/core.h>
108c2ecf20Sopenharmony_ci#include <sound/gus.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic void snd_gf1_dma_ack(struct snd_gus_card * gus)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	unsigned long flags;
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci	spin_lock_irqsave(&gus->reg_lock, flags);
178c2ecf20Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, 0x00);
188c2ecf20Sopenharmony_ci	snd_gf1_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL);
198c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&gus->reg_lock, flags);
208c2ecf20Sopenharmony_ci}
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic void snd_gf1_dma_program(struct snd_gus_card * gus,
238c2ecf20Sopenharmony_ci				unsigned int addr,
248c2ecf20Sopenharmony_ci				unsigned long buf_addr,
258c2ecf20Sopenharmony_ci				unsigned int count,
268c2ecf20Sopenharmony_ci				unsigned int cmd)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	unsigned long flags;
298c2ecf20Sopenharmony_ci	unsigned int address;
308c2ecf20Sopenharmony_ci	unsigned char dma_cmd;
318c2ecf20Sopenharmony_ci	unsigned int address_high;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	snd_printdd("dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n",
348c2ecf20Sopenharmony_ci		    addr, buf_addr, count);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if (gus->gf1.dma1 > 3) {
378c2ecf20Sopenharmony_ci		if (gus->gf1.enh_mode) {
388c2ecf20Sopenharmony_ci			address = addr >> 1;
398c2ecf20Sopenharmony_ci		} else {
408c2ecf20Sopenharmony_ci			if (addr & 0x1f) {
418c2ecf20Sopenharmony_ci				snd_printd("snd_gf1_dma_transfer: unaligned address (0x%x)?\n", addr);
428c2ecf20Sopenharmony_ci				return;
438c2ecf20Sopenharmony_ci			}
448c2ecf20Sopenharmony_ci			address = (addr & 0x000c0000) | ((addr & 0x0003ffff) >> 1);
458c2ecf20Sopenharmony_ci		}
468c2ecf20Sopenharmony_ci	} else {
478c2ecf20Sopenharmony_ci		address = addr;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	dma_cmd = SNDRV_GF1_DMA_ENABLE | (unsigned short) cmd;
518c2ecf20Sopenharmony_ci#if 0
528c2ecf20Sopenharmony_ci	dma_cmd |= 0x08;
538c2ecf20Sopenharmony_ci#endif
548c2ecf20Sopenharmony_ci	if (dma_cmd & SNDRV_GF1_DMA_16BIT) {
558c2ecf20Sopenharmony_ci		count++;
568c2ecf20Sopenharmony_ci		count &= ~1;	/* align */
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci	if (gus->gf1.dma1 > 3) {
598c2ecf20Sopenharmony_ci		dma_cmd |= SNDRV_GF1_DMA_WIDTH16;
608c2ecf20Sopenharmony_ci		count++;
618c2ecf20Sopenharmony_ci		count &= ~1;	/* align */
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci	snd_gf1_dma_ack(gus);
648c2ecf20Sopenharmony_ci	snd_dma_program(gus->gf1.dma1, buf_addr, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE);
658c2ecf20Sopenharmony_ci#if 0
668c2ecf20Sopenharmony_ci	snd_printk(KERN_DEBUG "address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n",
678c2ecf20Sopenharmony_ci		   address << 1, count, dma_cmd);
688c2ecf20Sopenharmony_ci#endif
698c2ecf20Sopenharmony_ci	spin_lock_irqsave(&gus->reg_lock, flags);
708c2ecf20Sopenharmony_ci	if (gus->gf1.enh_mode) {
718c2ecf20Sopenharmony_ci		address_high = ((address >> 16) & 0x000000f0) | (address & 0x0000000f);
728c2ecf20Sopenharmony_ci		snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
738c2ecf20Sopenharmony_ci		snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH, (unsigned char) address_high);
748c2ecf20Sopenharmony_ci	} else
758c2ecf20Sopenharmony_ci		snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
768c2ecf20Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, dma_cmd);
778c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&gus->reg_lock, flags);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic struct snd_gf1_dma_block *snd_gf1_dma_next_block(struct snd_gus_card * gus)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct snd_gf1_dma_block *block;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/* PCM block have bigger priority than synthesizer one */
858c2ecf20Sopenharmony_ci	if (gus->gf1.dma_data_pcm) {
868c2ecf20Sopenharmony_ci		block = gus->gf1.dma_data_pcm;
878c2ecf20Sopenharmony_ci		if (gus->gf1.dma_data_pcm_last == block) {
888c2ecf20Sopenharmony_ci			gus->gf1.dma_data_pcm =
898c2ecf20Sopenharmony_ci			gus->gf1.dma_data_pcm_last = NULL;
908c2ecf20Sopenharmony_ci		} else {
918c2ecf20Sopenharmony_ci			gus->gf1.dma_data_pcm = block->next;
928c2ecf20Sopenharmony_ci		}
938c2ecf20Sopenharmony_ci	} else if (gus->gf1.dma_data_synth) {
948c2ecf20Sopenharmony_ci		block = gus->gf1.dma_data_synth;
958c2ecf20Sopenharmony_ci		if (gus->gf1.dma_data_synth_last == block) {
968c2ecf20Sopenharmony_ci			gus->gf1.dma_data_synth =
978c2ecf20Sopenharmony_ci			gus->gf1.dma_data_synth_last = NULL;
988c2ecf20Sopenharmony_ci		} else {
998c2ecf20Sopenharmony_ci			gus->gf1.dma_data_synth = block->next;
1008c2ecf20Sopenharmony_ci		}
1018c2ecf20Sopenharmony_ci	} else {
1028c2ecf20Sopenharmony_ci		block = NULL;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci	if (block) {
1058c2ecf20Sopenharmony_ci		gus->gf1.dma_ack = block->ack;
1068c2ecf20Sopenharmony_ci		gus->gf1.dma_private_data = block->private_data;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci	return block;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void snd_gf1_dma_interrupt(struct snd_gus_card * gus)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct snd_gf1_dma_block *block;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	snd_gf1_dma_ack(gus);
1178c2ecf20Sopenharmony_ci	if (gus->gf1.dma_ack)
1188c2ecf20Sopenharmony_ci		gus->gf1.dma_ack(gus, gus->gf1.dma_private_data);
1198c2ecf20Sopenharmony_ci	spin_lock(&gus->dma_lock);
1208c2ecf20Sopenharmony_ci	if (gus->gf1.dma_data_pcm == NULL &&
1218c2ecf20Sopenharmony_ci	    gus->gf1.dma_data_synth == NULL) {
1228c2ecf20Sopenharmony_ci	    	gus->gf1.dma_ack = NULL;
1238c2ecf20Sopenharmony_ci		gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER;
1248c2ecf20Sopenharmony_ci		spin_unlock(&gus->dma_lock);
1258c2ecf20Sopenharmony_ci		return;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci	block = snd_gf1_dma_next_block(gus);
1288c2ecf20Sopenharmony_ci	spin_unlock(&gus->dma_lock);
1298c2ecf20Sopenharmony_ci	if (!block)
1308c2ecf20Sopenharmony_ci		return;
1318c2ecf20Sopenharmony_ci	snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
1328c2ecf20Sopenharmony_ci	kfree(block);
1338c2ecf20Sopenharmony_ci#if 0
1348c2ecf20Sopenharmony_ci	snd_printd(KERN_DEBUG "program dma (IRQ) - "
1358c2ecf20Sopenharmony_ci		   "addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
1368c2ecf20Sopenharmony_ci		   block->addr, block->buf_addr, block->count, block->cmd);
1378c2ecf20Sopenharmony_ci#endif
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ciint snd_gf1_dma_init(struct snd_gus_card * gus)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	mutex_lock(&gus->dma_mutex);
1438c2ecf20Sopenharmony_ci	gus->gf1.dma_shared++;
1448c2ecf20Sopenharmony_ci	if (gus->gf1.dma_shared > 1) {
1458c2ecf20Sopenharmony_ci		mutex_unlock(&gus->dma_mutex);
1468c2ecf20Sopenharmony_ci		return 0;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci	gus->gf1.interrupt_handler_dma_write = snd_gf1_dma_interrupt;
1498c2ecf20Sopenharmony_ci	gus->gf1.dma_data_pcm =
1508c2ecf20Sopenharmony_ci	gus->gf1.dma_data_pcm_last =
1518c2ecf20Sopenharmony_ci	gus->gf1.dma_data_synth =
1528c2ecf20Sopenharmony_ci	gus->gf1.dma_data_synth_last = NULL;
1538c2ecf20Sopenharmony_ci	mutex_unlock(&gus->dma_mutex);
1548c2ecf20Sopenharmony_ci	return 0;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ciint snd_gf1_dma_done(struct snd_gus_card * gus)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct snd_gf1_dma_block *block;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	mutex_lock(&gus->dma_mutex);
1628c2ecf20Sopenharmony_ci	gus->gf1.dma_shared--;
1638c2ecf20Sopenharmony_ci	if (!gus->gf1.dma_shared) {
1648c2ecf20Sopenharmony_ci		snd_dma_disable(gus->gf1.dma1);
1658c2ecf20Sopenharmony_ci		snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_WRITE);
1668c2ecf20Sopenharmony_ci		snd_gf1_dma_ack(gus);
1678c2ecf20Sopenharmony_ci		while ((block = gus->gf1.dma_data_pcm)) {
1688c2ecf20Sopenharmony_ci			gus->gf1.dma_data_pcm = block->next;
1698c2ecf20Sopenharmony_ci			kfree(block);
1708c2ecf20Sopenharmony_ci		}
1718c2ecf20Sopenharmony_ci		while ((block = gus->gf1.dma_data_synth)) {
1728c2ecf20Sopenharmony_ci			gus->gf1.dma_data_synth = block->next;
1738c2ecf20Sopenharmony_ci			kfree(block);
1748c2ecf20Sopenharmony_ci		}
1758c2ecf20Sopenharmony_ci		gus->gf1.dma_data_pcm_last =
1768c2ecf20Sopenharmony_ci		gus->gf1.dma_data_synth_last = NULL;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci	mutex_unlock(&gus->dma_mutex);
1798c2ecf20Sopenharmony_ci	return 0;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ciint snd_gf1_dma_transfer_block(struct snd_gus_card * gus,
1838c2ecf20Sopenharmony_ci			       struct snd_gf1_dma_block * __block,
1848c2ecf20Sopenharmony_ci			       int atomic,
1858c2ecf20Sopenharmony_ci			       int synth)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	unsigned long flags;
1888c2ecf20Sopenharmony_ci	struct snd_gf1_dma_block *block;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL);
1918c2ecf20Sopenharmony_ci	if (!block)
1928c2ecf20Sopenharmony_ci		return -ENOMEM;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	*block = *__block;
1958c2ecf20Sopenharmony_ci	block->next = NULL;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	snd_printdd("addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
1988c2ecf20Sopenharmony_ci		    block->addr, (long) block->buffer, block->count,
1998c2ecf20Sopenharmony_ci		    block->cmd);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	snd_printdd("gus->gf1.dma_data_pcm_last = 0x%lx\n",
2028c2ecf20Sopenharmony_ci		    (long)gus->gf1.dma_data_pcm_last);
2038c2ecf20Sopenharmony_ci	snd_printdd("gus->gf1.dma_data_pcm = 0x%lx\n",
2048c2ecf20Sopenharmony_ci		    (long)gus->gf1.dma_data_pcm);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	spin_lock_irqsave(&gus->dma_lock, flags);
2078c2ecf20Sopenharmony_ci	if (synth) {
2088c2ecf20Sopenharmony_ci		if (gus->gf1.dma_data_synth_last) {
2098c2ecf20Sopenharmony_ci			gus->gf1.dma_data_synth_last->next = block;
2108c2ecf20Sopenharmony_ci			gus->gf1.dma_data_synth_last = block;
2118c2ecf20Sopenharmony_ci		} else {
2128c2ecf20Sopenharmony_ci			gus->gf1.dma_data_synth =
2138c2ecf20Sopenharmony_ci			gus->gf1.dma_data_synth_last = block;
2148c2ecf20Sopenharmony_ci		}
2158c2ecf20Sopenharmony_ci	} else {
2168c2ecf20Sopenharmony_ci		if (gus->gf1.dma_data_pcm_last) {
2178c2ecf20Sopenharmony_ci			gus->gf1.dma_data_pcm_last->next = block;
2188c2ecf20Sopenharmony_ci			gus->gf1.dma_data_pcm_last = block;
2198c2ecf20Sopenharmony_ci		} else {
2208c2ecf20Sopenharmony_ci			gus->gf1.dma_data_pcm =
2218c2ecf20Sopenharmony_ci			gus->gf1.dma_data_pcm_last = block;
2228c2ecf20Sopenharmony_ci		}
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci	if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) {
2258c2ecf20Sopenharmony_ci		gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER;
2268c2ecf20Sopenharmony_ci		block = snd_gf1_dma_next_block(gus);
2278c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&gus->dma_lock, flags);
2288c2ecf20Sopenharmony_ci		if (block == NULL)
2298c2ecf20Sopenharmony_ci			return 0;
2308c2ecf20Sopenharmony_ci		snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
2318c2ecf20Sopenharmony_ci		kfree(block);
2328c2ecf20Sopenharmony_ci		return 0;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&gus->dma_lock, flags);
2358c2ecf20Sopenharmony_ci	return 0;
2368c2ecf20Sopenharmony_ci}
237