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