244 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Routines for GF1 DMA control4 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>5 */6 7#include <asm/dma.h>8#include <linux/slab.h>9#include <sound/core.h>10#include <sound/gus.h>11 12static void snd_gf1_dma_ack(struct snd_gus_card * gus)13{14 unsigned long flags;15 16 spin_lock_irqsave(&gus->reg_lock, flags);17 snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, 0x00);18 snd_gf1_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL);19 spin_unlock_irqrestore(&gus->reg_lock, flags);20}21 22static void snd_gf1_dma_program(struct snd_gus_card * gus,23 unsigned int addr,24 unsigned long buf_addr,25 unsigned int count,26 unsigned int cmd)27{28 unsigned long flags;29 unsigned int address;30 unsigned char dma_cmd;31 unsigned int address_high;32 33 dev_dbg(gus->card->dev,34 "dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n",35 addr, buf_addr, count);36 37 if (gus->gf1.dma1 > 3) {38 if (gus->gf1.enh_mode) {39 address = addr >> 1;40 } else {41 if (addr & 0x1f) {42 dev_dbg(gus->card->dev,43 "%s: unaligned address (0x%x)?\n",44 __func__, addr);45 return;46 }47 address = (addr & 0x000c0000) | ((addr & 0x0003ffff) >> 1);48 }49 } else {50 address = addr;51 }52 53 dma_cmd = SNDRV_GF1_DMA_ENABLE | (unsigned short) cmd;54#if 055 dma_cmd |= 0x08;56#endif57 if (dma_cmd & SNDRV_GF1_DMA_16BIT) {58 count++;59 count &= ~1; /* align */60 }61 if (gus->gf1.dma1 > 3) {62 dma_cmd |= SNDRV_GF1_DMA_WIDTH16;63 count++;64 count &= ~1; /* align */65 }66 snd_gf1_dma_ack(gus);67 snd_dma_program(gus->gf1.dma1, buf_addr, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE);68#if 069 dev_dbg(gus->card->dev,70 "address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n",71 address << 1, count, dma_cmd);72#endif73 spin_lock_irqsave(&gus->reg_lock, flags);74 if (gus->gf1.enh_mode) {75 address_high = ((address >> 16) & 0x000000f0) | (address & 0x0000000f);76 snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));77 snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH, (unsigned char) address_high);78 } else79 snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));80 snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, dma_cmd);81 spin_unlock_irqrestore(&gus->reg_lock, flags);82}83 84static struct snd_gf1_dma_block *snd_gf1_dma_next_block(struct snd_gus_card * gus)85{86 struct snd_gf1_dma_block *block;87 88 /* PCM block have bigger priority than synthesizer one */89 if (gus->gf1.dma_data_pcm) {90 block = gus->gf1.dma_data_pcm;91 if (gus->gf1.dma_data_pcm_last == block) {92 gus->gf1.dma_data_pcm =93 gus->gf1.dma_data_pcm_last = NULL;94 } else {95 gus->gf1.dma_data_pcm = block->next;96 }97 } else if (gus->gf1.dma_data_synth) {98 block = gus->gf1.dma_data_synth;99 if (gus->gf1.dma_data_synth_last == block) {100 gus->gf1.dma_data_synth =101 gus->gf1.dma_data_synth_last = NULL;102 } else {103 gus->gf1.dma_data_synth = block->next;104 }105 } else {106 block = NULL;107 }108 if (block) {109 gus->gf1.dma_ack = block->ack;110 gus->gf1.dma_private_data = block->private_data;111 }112 return block;113}114 115 116static void snd_gf1_dma_interrupt(struct snd_gus_card * gus)117{118 struct snd_gf1_dma_block *block;119 120 snd_gf1_dma_ack(gus);121 if (gus->gf1.dma_ack)122 gus->gf1.dma_ack(gus, gus->gf1.dma_private_data);123 spin_lock(&gus->dma_lock);124 if (gus->gf1.dma_data_pcm == NULL &&125 gus->gf1.dma_data_synth == NULL) {126 gus->gf1.dma_ack = NULL;127 gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER;128 spin_unlock(&gus->dma_lock);129 return;130 }131 block = snd_gf1_dma_next_block(gus);132 spin_unlock(&gus->dma_lock);133 if (!block)134 return;135 snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);136 kfree(block);137#if 0138 dev_dbg(gus->card->dev,139 "program dma (IRQ) - addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",140 block->addr, block->buf_addr, block->count, block->cmd);141#endif142}143 144int snd_gf1_dma_init(struct snd_gus_card * gus)145{146 mutex_lock(&gus->dma_mutex);147 gus->gf1.dma_shared++;148 if (gus->gf1.dma_shared > 1) {149 mutex_unlock(&gus->dma_mutex);150 return 0;151 }152 gus->gf1.interrupt_handler_dma_write = snd_gf1_dma_interrupt;153 gus->gf1.dma_data_pcm = 154 gus->gf1.dma_data_pcm_last =155 gus->gf1.dma_data_synth = 156 gus->gf1.dma_data_synth_last = NULL;157 mutex_unlock(&gus->dma_mutex);158 return 0;159}160 161int snd_gf1_dma_done(struct snd_gus_card * gus)162{163 struct snd_gf1_dma_block *block;164 165 mutex_lock(&gus->dma_mutex);166 gus->gf1.dma_shared--;167 if (!gus->gf1.dma_shared) {168 snd_dma_disable(gus->gf1.dma1);169 snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_WRITE);170 snd_gf1_dma_ack(gus);171 while ((block = gus->gf1.dma_data_pcm)) {172 gus->gf1.dma_data_pcm = block->next;173 kfree(block);174 }175 while ((block = gus->gf1.dma_data_synth)) {176 gus->gf1.dma_data_synth = block->next;177 kfree(block);178 }179 gus->gf1.dma_data_pcm_last =180 gus->gf1.dma_data_synth_last = NULL;181 }182 mutex_unlock(&gus->dma_mutex);183 return 0;184}185 186int snd_gf1_dma_transfer_block(struct snd_gus_card * gus,187 struct snd_gf1_dma_block * __block,188 int atomic,189 int synth)190{191 unsigned long flags;192 struct snd_gf1_dma_block *block;193 194 block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL);195 if (!block)196 return -ENOMEM;197 198 *block = *__block;199 block->next = NULL;200 201 dev_dbg(gus->card->dev,202 "addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",203 block->addr, (long) block->buffer, block->count,204 block->cmd);205 206 dev_dbg(gus->card->dev,207 "gus->gf1.dma_data_pcm_last = 0x%lx\n",208 (long)gus->gf1.dma_data_pcm_last);209 dev_dbg(gus->card->dev,210 "gus->gf1.dma_data_pcm = 0x%lx\n",211 (long)gus->gf1.dma_data_pcm);212 213 spin_lock_irqsave(&gus->dma_lock, flags);214 if (synth) {215 if (gus->gf1.dma_data_synth_last) {216 gus->gf1.dma_data_synth_last->next = block;217 gus->gf1.dma_data_synth_last = block;218 } else {219 gus->gf1.dma_data_synth = 220 gus->gf1.dma_data_synth_last = block;221 }222 } else {223 if (gus->gf1.dma_data_pcm_last) {224 gus->gf1.dma_data_pcm_last->next = block;225 gus->gf1.dma_data_pcm_last = block;226 } else {227 gus->gf1.dma_data_pcm = 228 gus->gf1.dma_data_pcm_last = block;229 }230 }231 if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) {232 gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER;233 block = snd_gf1_dma_next_block(gus);234 spin_unlock_irqrestore(&gus->dma_lock, flags);235 if (block == NULL)236 return 0;237 snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);238 kfree(block);239 return 0;240 }241 spin_unlock_irqrestore(&gus->dma_lock, flags);242 return 0;243}244