11da177e4SLinus Torvalds /* 2c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 31da177e4SLinus Torvalds * Routines for control of GF1 chip (PCM things) 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * InterWave chips supports interleaved DMA, but this feature isn't used in 61da177e4SLinus Torvalds * this code. 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * This code emulates autoinit DMA transfer for playback, recording by GF1 91da177e4SLinus Torvalds * chip doesn't support autoinit DMA. 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 131da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 141da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 151da177e4SLinus Torvalds * (at your option) any later version. 161da177e4SLinus Torvalds * 171da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 181da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 191da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 201da177e4SLinus Torvalds * GNU General Public License for more details. 211da177e4SLinus Torvalds * 221da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 231da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 241da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 251da177e4SLinus Torvalds * 261da177e4SLinus Torvalds */ 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds #include <asm/dma.h> 291da177e4SLinus Torvalds #include <linux/slab.h> 30174cd4b1SIngo Molnar #include <linux/sched/signal.h> 31174cd4b1SIngo Molnar 321da177e4SLinus Torvalds #include <sound/core.h> 331da177e4SLinus Torvalds #include <sound/control.h> 341da177e4SLinus Torvalds #include <sound/gus.h> 351da177e4SLinus Torvalds #include <sound/pcm_params.h> 361da177e4SLinus Torvalds #include "gus_tables.h" 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds /* maximum rate */ 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds #define SNDRV_GF1_PCM_RATE 48000 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds #define SNDRV_GF1_PCM_PFLG_NONE 0 431da177e4SLinus Torvalds #define SNDRV_GF1_PCM_PFLG_ACTIVE (1<<0) 441da177e4SLinus Torvalds #define SNDRV_GF1_PCM_PFLG_NEUTRAL (2<<0) 451da177e4SLinus Torvalds 465e2da206STakashi Iwai struct gus_pcm_private { 475e2da206STakashi Iwai struct snd_gus_card * gus; 485e2da206STakashi Iwai struct snd_pcm_substream *substream; 491da177e4SLinus Torvalds spinlock_t lock; 501da177e4SLinus Torvalds unsigned int voices; 515e2da206STakashi Iwai struct snd_gus_voice *pvoices[2]; 521da177e4SLinus Torvalds unsigned int memory; 531da177e4SLinus Torvalds unsigned short flags; 541da177e4SLinus Torvalds unsigned char voice_ctrl, ramp_ctrl; 551da177e4SLinus Torvalds unsigned int bpos; 561da177e4SLinus Torvalds unsigned int blocks; 571da177e4SLinus Torvalds unsigned int block_size; 581da177e4SLinus Torvalds unsigned int dma_size; 591da177e4SLinus Torvalds wait_queue_head_t sleep; 601da177e4SLinus Torvalds atomic_t dma_count; 611da177e4SLinus Torvalds int final_volume; 625e2da206STakashi Iwai }; 631da177e4SLinus Torvalds 645e2da206STakashi Iwai static void snd_gf1_pcm_block_change_ack(struct snd_gus_card * gus, void *private_data) 651da177e4SLinus Torvalds { 665e2da206STakashi Iwai struct gus_pcm_private *pcmp = private_data; 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds if (pcmp) { 691da177e4SLinus Torvalds atomic_dec(&pcmp->dma_count); 701da177e4SLinus Torvalds wake_up(&pcmp->sleep); 711da177e4SLinus Torvalds } 721da177e4SLinus Torvalds } 731da177e4SLinus Torvalds 745e2da206STakashi Iwai static int snd_gf1_pcm_block_change(struct snd_pcm_substream *substream, 751da177e4SLinus Torvalds unsigned int offset, 761da177e4SLinus Torvalds unsigned int addr, 771da177e4SLinus Torvalds unsigned int count) 781da177e4SLinus Torvalds { 795e2da206STakashi Iwai struct snd_gf1_dma_block block; 805e2da206STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 815e2da206STakashi Iwai struct gus_pcm_private *pcmp = runtime->private_data; 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds count += offset & 31; 841da177e4SLinus Torvalds offset &= ~31; 8591f05060STakashi Iwai /* 8691f05060STakashi Iwai snd_printk(KERN_DEBUG "block change - offset = 0x%x, count = 0x%x\n", 8791f05060STakashi Iwai offset, count); 8891f05060STakashi Iwai */ 891da177e4SLinus Torvalds memset(&block, 0, sizeof(block)); 901da177e4SLinus Torvalds block.cmd = SNDRV_GF1_DMA_IRQ; 911da177e4SLinus Torvalds if (snd_pcm_format_unsigned(runtime->format)) 921da177e4SLinus Torvalds block.cmd |= SNDRV_GF1_DMA_UNSIGNED; 931da177e4SLinus Torvalds if (snd_pcm_format_width(runtime->format) == 16) 941da177e4SLinus Torvalds block.cmd |= SNDRV_GF1_DMA_16BIT; 951da177e4SLinus Torvalds block.addr = addr & ~31; 961da177e4SLinus Torvalds block.buffer = runtime->dma_area + offset; 971da177e4SLinus Torvalds block.buf_addr = runtime->dma_addr + offset; 981da177e4SLinus Torvalds block.count = count; 991da177e4SLinus Torvalds block.private_data = pcmp; 1001da177e4SLinus Torvalds block.ack = snd_gf1_pcm_block_change_ack; 1011da177e4SLinus Torvalds if (!snd_gf1_dma_transfer_block(pcmp->gus, &block, 0, 0)) 1021da177e4SLinus Torvalds atomic_inc(&pcmp->dma_count); 1031da177e4SLinus Torvalds return 0; 1041da177e4SLinus Torvalds } 1051da177e4SLinus Torvalds 1065e2da206STakashi Iwai static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream) 1071da177e4SLinus Torvalds { 1085e2da206STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 1095e2da206STakashi Iwai struct gus_pcm_private *pcmp = runtime->private_data; 1105e2da206STakashi Iwai struct snd_gus_card * gus = pcmp->gus; 1111da177e4SLinus Torvalds unsigned long flags; 1121da177e4SLinus Torvalds unsigned char voice_ctrl, ramp_ctrl; 1131da177e4SLinus Torvalds unsigned short rate; 1141da177e4SLinus Torvalds unsigned int curr, begin, end; 1151da177e4SLinus Torvalds unsigned short vol; 1161da177e4SLinus Torvalds unsigned char pan; 1171da177e4SLinus Torvalds unsigned int voice; 1181da177e4SLinus Torvalds 1191da177e4SLinus Torvalds spin_lock_irqsave(&pcmp->lock, flags); 1201da177e4SLinus Torvalds if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { 1211da177e4SLinus Torvalds spin_unlock_irqrestore(&pcmp->lock, flags); 1221da177e4SLinus Torvalds return; 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE; 1251da177e4SLinus Torvalds pcmp->final_volume = 0; 1261da177e4SLinus Torvalds spin_unlock_irqrestore(&pcmp->lock, flags); 1271da177e4SLinus Torvalds rate = snd_gf1_translate_freq(gus, runtime->rate << 4); 1281da177e4SLinus Torvalds /* enable WAVE IRQ */ 1291da177e4SLinus Torvalds voice_ctrl = snd_pcm_format_width(runtime->format) == 16 ? 0x24 : 0x20; 1301da177e4SLinus Torvalds /* enable RAMP IRQ + rollover */ 1311da177e4SLinus Torvalds ramp_ctrl = 0x24; 1321da177e4SLinus Torvalds if (pcmp->blocks == 1) { 1331da177e4SLinus Torvalds voice_ctrl |= 0x08; /* loop enable */ 1341da177e4SLinus Torvalds ramp_ctrl &= ~0x04; /* disable rollover */ 1351da177e4SLinus Torvalds } 1361da177e4SLinus Torvalds for (voice = 0; voice < pcmp->voices; voice++) { 1371da177e4SLinus Torvalds begin = pcmp->memory + voice * (pcmp->dma_size / runtime->channels); 1381da177e4SLinus Torvalds curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels; 1391da177e4SLinus Torvalds end = curr + (pcmp->block_size / runtime->channels); 1401da177e4SLinus Torvalds end -= snd_pcm_format_width(runtime->format) == 16 ? 2 : 1; 14191f05060STakashi Iwai /* 14291f05060STakashi Iwai snd_printk(KERN_DEBUG "init: curr=0x%x, begin=0x%x, end=0x%x, " 14391f05060STakashi Iwai "ctrl=0x%x, ramp=0x%x, rate=0x%x\n", 14491f05060STakashi Iwai curr, begin, end, voice_ctrl, ramp_ctrl, rate); 14591f05060STakashi Iwai */ 1461da177e4SLinus Torvalds pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8; 1471da177e4SLinus Torvalds vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; 1481da177e4SLinus Torvalds spin_lock_irqsave(&gus->reg_lock, flags); 1491da177e4SLinus Torvalds snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); 1501da177e4SLinus Torvalds snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, pan); 1511da177e4SLinus Torvalds snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, rate); 1521da177e4SLinus Torvalds snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, begin << 4, voice_ctrl & 4); 1531da177e4SLinus Torvalds snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4); 1541da177e4SLinus Torvalds snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, curr << 4, voice_ctrl & 4); 1551da177e4SLinus Torvalds snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME << 4); 1561da177e4SLinus Torvalds snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0x2f); 1571da177e4SLinus Torvalds snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET); 1581da177e4SLinus Torvalds snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, vol >> 8); 1591da177e4SLinus Torvalds snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); 1601da177e4SLinus Torvalds if (!gus->gf1.enh_mode) { 1611da177e4SLinus Torvalds snd_gf1_delay(gus); 1621da177e4SLinus Torvalds snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); 1631da177e4SLinus Torvalds } 1641da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->reg_lock, flags); 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds spin_lock_irqsave(&gus->reg_lock, flags); 1671da177e4SLinus Torvalds for (voice = 0; voice < pcmp->voices; voice++) { 1681da177e4SLinus Torvalds snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); 1691da177e4SLinus Torvalds if (gus->gf1.enh_mode) 1701da177e4SLinus Torvalds snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, 0x00); /* deactivate voice */ 1711da177e4SLinus Torvalds snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); 1721da177e4SLinus Torvalds voice_ctrl &= ~0x20; 1731da177e4SLinus Torvalds } 1741da177e4SLinus Torvalds voice_ctrl |= 0x20; 1751da177e4SLinus Torvalds if (!gus->gf1.enh_mode) { 1761da177e4SLinus Torvalds snd_gf1_delay(gus); 1771da177e4SLinus Torvalds for (voice = 0; voice < pcmp->voices; voice++) { 1781da177e4SLinus Torvalds snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); 1791da177e4SLinus Torvalds snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); 1801da177e4SLinus Torvalds voice_ctrl &= ~0x20; /* disable IRQ for next voice */ 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds } 1831da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->reg_lock, flags); 1841da177e4SLinus Torvalds } 1851da177e4SLinus Torvalds 1865e2da206STakashi Iwai static void snd_gf1_pcm_interrupt_wave(struct snd_gus_card * gus, 1875e2da206STakashi Iwai struct snd_gus_voice *pvoice) 1881da177e4SLinus Torvalds { 1895e2da206STakashi Iwai struct gus_pcm_private * pcmp; 1905e2da206STakashi Iwai struct snd_pcm_runtime *runtime; 1911da177e4SLinus Torvalds unsigned char voice_ctrl, ramp_ctrl; 1921da177e4SLinus Torvalds unsigned int idx; 1931da177e4SLinus Torvalds unsigned int end, step; 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds if (!pvoice->private_data) { 1961da177e4SLinus Torvalds snd_printd("snd_gf1_pcm: unknown wave irq?\n"); 1971da177e4SLinus Torvalds snd_gf1_smart_stop_voice(gus, pvoice->number); 1981da177e4SLinus Torvalds return; 1991da177e4SLinus Torvalds } 2001da177e4SLinus Torvalds pcmp = pvoice->private_data; 2011da177e4SLinus Torvalds if (pcmp == NULL) { 2021da177e4SLinus Torvalds snd_printd("snd_gf1_pcm: unknown wave irq?\n"); 2031da177e4SLinus Torvalds snd_gf1_smart_stop_voice(gus, pvoice->number); 2041da177e4SLinus Torvalds return; 2051da177e4SLinus Torvalds } 2061da177e4SLinus Torvalds gus = pcmp->gus; 2071da177e4SLinus Torvalds runtime = pcmp->substream->runtime; 2081da177e4SLinus Torvalds 2091da177e4SLinus Torvalds spin_lock(&gus->reg_lock); 2101da177e4SLinus Torvalds snd_gf1_select_voice(gus, pvoice->number); 2111da177e4SLinus Torvalds voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b; 2121da177e4SLinus Torvalds ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03; 2131da177e4SLinus Torvalds #if 0 2141da177e4SLinus Torvalds snd_gf1_select_voice(gus, pvoice->number); 21591f05060STakashi Iwai printk(KERN_DEBUG "position = 0x%x\n", 21691f05060STakashi Iwai (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); 2171da177e4SLinus Torvalds snd_gf1_select_voice(gus, pcmp->pvoices[1]->number); 21891f05060STakashi Iwai printk(KERN_DEBUG "position = 0x%x\n", 21991f05060STakashi Iwai (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); 2201da177e4SLinus Torvalds snd_gf1_select_voice(gus, pvoice->number); 2211da177e4SLinus Torvalds #endif 2221da177e4SLinus Torvalds pcmp->bpos++; 2231da177e4SLinus Torvalds pcmp->bpos %= pcmp->blocks; 2241da177e4SLinus Torvalds if (pcmp->bpos + 1 >= pcmp->blocks) { /* last block? */ 2251da177e4SLinus Torvalds voice_ctrl |= 0x08; /* enable loop */ 2261da177e4SLinus Torvalds } else { 2271da177e4SLinus Torvalds ramp_ctrl |= 0x04; /* enable rollover */ 2281da177e4SLinus Torvalds } 2291da177e4SLinus Torvalds end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels); 2301da177e4SLinus Torvalds end -= voice_ctrl & 4 ? 2 : 1; 2311da177e4SLinus Torvalds step = pcmp->dma_size / runtime->channels; 2321da177e4SLinus Torvalds voice_ctrl |= 0x20; 2331da177e4SLinus Torvalds if (!pcmp->final_volume) { 2341da177e4SLinus Torvalds ramp_ctrl |= 0x20; 2351da177e4SLinus Torvalds ramp_ctrl &= ~0x03; 2361da177e4SLinus Torvalds } 2371da177e4SLinus Torvalds for (idx = 0; idx < pcmp->voices; idx++, end += step) { 2381da177e4SLinus Torvalds snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number); 2391da177e4SLinus Torvalds snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4); 2401da177e4SLinus Torvalds snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); 2411da177e4SLinus Torvalds snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); 2421da177e4SLinus Torvalds voice_ctrl &= ~0x20; 2431da177e4SLinus Torvalds } 2441da177e4SLinus Torvalds if (!gus->gf1.enh_mode) { 2451da177e4SLinus Torvalds snd_gf1_delay(gus); 2461da177e4SLinus Torvalds voice_ctrl |= 0x20; 2471da177e4SLinus Torvalds for (idx = 0; idx < pcmp->voices; idx++) { 2481da177e4SLinus Torvalds snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number); 2491da177e4SLinus Torvalds snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); 2501da177e4SLinus Torvalds snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); 2511da177e4SLinus Torvalds voice_ctrl &= ~0x20; 2521da177e4SLinus Torvalds } 2531da177e4SLinus Torvalds } 2541da177e4SLinus Torvalds spin_unlock(&gus->reg_lock); 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds snd_pcm_period_elapsed(pcmp->substream); 2571da177e4SLinus Torvalds #if 0 2581da177e4SLinus Torvalds if ((runtime->flags & SNDRV_PCM_FLG_MMAP) && 2591da177e4SLinus Torvalds *runtime->state == SNDRV_PCM_STATE_RUNNING) { 2601da177e4SLinus Torvalds end = pcmp->bpos * pcmp->block_size; 2611da177e4SLinus Torvalds if (runtime->channels > 1) { 2621da177e4SLinus Torvalds snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + (end / 2), pcmp->block_size / 2); 2631da177e4SLinus Torvalds snd_gf1_pcm_block_change(pcmp->substream, end + (pcmp->block_size / 2), pcmp->memory + (pcmp->dma_size / 2) + (end / 2), pcmp->block_size / 2); 2641da177e4SLinus Torvalds } else { 2651da177e4SLinus Torvalds snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + end, pcmp->block_size); 2661da177e4SLinus Torvalds } 2671da177e4SLinus Torvalds } 2681da177e4SLinus Torvalds #endif 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 2715e2da206STakashi Iwai static void snd_gf1_pcm_interrupt_volume(struct snd_gus_card * gus, 2725e2da206STakashi Iwai struct snd_gus_voice * pvoice) 2731da177e4SLinus Torvalds { 2741da177e4SLinus Torvalds unsigned short vol; 2751da177e4SLinus Torvalds int cvoice; 2765e2da206STakashi Iwai struct gus_pcm_private *pcmp = pvoice->private_data; 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds /* stop ramp, but leave rollover bit untouched */ 2791da177e4SLinus Torvalds spin_lock(&gus->reg_lock); 2801da177e4SLinus Torvalds snd_gf1_select_voice(gus, pvoice->number); 2811da177e4SLinus Torvalds snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); 2821da177e4SLinus Torvalds spin_unlock(&gus->reg_lock); 2831da177e4SLinus Torvalds if (pcmp == NULL) 2841da177e4SLinus Torvalds return; 2851da177e4SLinus Torvalds /* are we active? */ 2861da177e4SLinus Torvalds if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) 2871da177e4SLinus Torvalds return; 2881da177e4SLinus Torvalds /* load real volume - better precision */ 2891da177e4SLinus Torvalds cvoice = pcmp->pvoices[0] == pvoice ? 0 : 1; 2901da177e4SLinus Torvalds if (pcmp->substream == NULL) 2911da177e4SLinus Torvalds return; 2921da177e4SLinus Torvalds vol = !cvoice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; 2931da177e4SLinus Torvalds spin_lock(&gus->reg_lock); 2941da177e4SLinus Torvalds snd_gf1_select_voice(gus, pvoice->number); 2951da177e4SLinus Torvalds snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol); 2961da177e4SLinus Torvalds pcmp->final_volume = 1; 2971da177e4SLinus Torvalds spin_unlock(&gus->reg_lock); 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds 3005e2da206STakashi Iwai static void snd_gf1_pcm_volume_change(struct snd_gus_card * gus) 3011da177e4SLinus Torvalds { 3021da177e4SLinus Torvalds } 3031da177e4SLinus Torvalds 3045e2da206STakashi Iwai static int snd_gf1_pcm_poke_block(struct snd_gus_card *gus, unsigned char *buf, 3051da177e4SLinus Torvalds unsigned int pos, unsigned int count, 3061da177e4SLinus Torvalds int w16, int invert) 3071da177e4SLinus Torvalds { 3081da177e4SLinus Torvalds unsigned int len; 3091da177e4SLinus Torvalds unsigned long flags; 3101da177e4SLinus Torvalds 31191f05060STakashi Iwai /* 31291f05060STakashi Iwai printk(KERN_DEBUG 31391f05060STakashi Iwai "poke block; buf = 0x%x, pos = %i, count = %i, port = 0x%x\n", 31491f05060STakashi Iwai (int)buf, pos, count, gus->gf1.port); 31591f05060STakashi Iwai */ 3161da177e4SLinus Torvalds while (count > 0) { 3171da177e4SLinus Torvalds len = count; 3181da177e4SLinus Torvalds if (len > 512) /* limit, to allow IRQ */ 3191da177e4SLinus Torvalds len = 512; 3201da177e4SLinus Torvalds count -= len; 3211da177e4SLinus Torvalds if (gus->interwave) { 3221da177e4SLinus Torvalds spin_lock_irqsave(&gus->reg_lock, flags); 3231da177e4SLinus Torvalds snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01 | (invert ? 0x08 : 0x00)); 3241da177e4SLinus Torvalds snd_gf1_dram_addr(gus, pos); 3251da177e4SLinus Torvalds if (w16) { 3261da177e4SLinus Torvalds outb(SNDRV_GF1_GW_DRAM_IO16, GUSP(gus, GF1REGSEL)); 3271da177e4SLinus Torvalds outsw(GUSP(gus, GF1DATALOW), buf, len >> 1); 3281da177e4SLinus Torvalds } else { 3291da177e4SLinus Torvalds outsb(GUSP(gus, DRAM), buf, len); 3301da177e4SLinus Torvalds } 3311da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->reg_lock, flags); 3321da177e4SLinus Torvalds buf += 512; 3331da177e4SLinus Torvalds pos += 512; 3341da177e4SLinus Torvalds } else { 3351da177e4SLinus Torvalds invert = invert ? 0x80 : 0x00; 3361da177e4SLinus Torvalds if (w16) { 3371da177e4SLinus Torvalds len >>= 1; 3381da177e4SLinus Torvalds while (len--) { 3391da177e4SLinus Torvalds snd_gf1_poke(gus, pos++, *buf++); 3401da177e4SLinus Torvalds snd_gf1_poke(gus, pos++, *buf++ ^ invert); 3411da177e4SLinus Torvalds } 3421da177e4SLinus Torvalds } else { 3431da177e4SLinus Torvalds while (len--) 3441da177e4SLinus Torvalds snd_gf1_poke(gus, pos++, *buf++ ^ invert); 3451da177e4SLinus Torvalds } 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds if (count > 0 && !in_interrupt()) { 3488433a509SNishanth Aravamudan schedule_timeout_interruptible(1); 3491da177e4SLinus Torvalds if (signal_pending(current)) 3501da177e4SLinus Torvalds return -EAGAIN; 3511da177e4SLinus Torvalds } 3521da177e4SLinus Torvalds } 3531da177e4SLinus Torvalds return 0; 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds 356a6970bb1STakashi Iwai static int get_bpos(struct gus_pcm_private *pcmp, int voice, unsigned int pos, 357a6970bb1STakashi Iwai unsigned int len) 3581da177e4SLinus Torvalds { 359a6970bb1STakashi Iwai unsigned int bpos = pos + (voice * (pcmp->dma_size / 2)); 360622207dcSTakashi Iwai if (snd_BUG_ON(bpos > pcmp->dma_size)) 361622207dcSTakashi Iwai return -EIO; 362622207dcSTakashi Iwai if (snd_BUG_ON(bpos + len > pcmp->dma_size)) 363622207dcSTakashi Iwai return -EIO; 364a6970bb1STakashi Iwai return bpos; 365a6970bb1STakashi Iwai } 366a6970bb1STakashi Iwai 367a6970bb1STakashi Iwai static int playback_copy_ack(struct snd_pcm_substream *substream, 368a6970bb1STakashi Iwai unsigned int bpos, unsigned int len) 369a6970bb1STakashi Iwai { 370a6970bb1STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 371a6970bb1STakashi Iwai struct gus_pcm_private *pcmp = runtime->private_data; 372a6970bb1STakashi Iwai struct snd_gus_card *gus = pcmp->gus; 373a6970bb1STakashi Iwai int w16, invert; 374a6970bb1STakashi Iwai 375097a7fe3STakashi Sakamoto if (len > 32) 376097a7fe3STakashi Sakamoto return snd_gf1_pcm_block_change(substream, bpos, 377097a7fe3STakashi Sakamoto pcmp->memory + bpos, len); 3781da177e4SLinus Torvalds 3791da177e4SLinus Torvalds w16 = (snd_pcm_format_width(runtime->format) == 16); 3801da177e4SLinus Torvalds invert = snd_pcm_format_unsigned(runtime->format); 381097a7fe3STakashi Sakamoto return snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, 382097a7fe3STakashi Sakamoto pcmp->memory + bpos, len, w16, invert); 3831da177e4SLinus Torvalds } 3841da177e4SLinus Torvalds 385a6970bb1STakashi Iwai static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream, 386a6970bb1STakashi Iwai int voice, unsigned long pos, 387a6970bb1STakashi Iwai void __user *src, unsigned long count) 3881da177e4SLinus Torvalds { 3895e2da206STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 3905e2da206STakashi Iwai struct gus_pcm_private *pcmp = runtime->private_data; 391a6970bb1STakashi Iwai unsigned int len = count; 392a6970bb1STakashi Iwai int bpos; 3931da177e4SLinus Torvalds 394a6970bb1STakashi Iwai bpos = get_bpos(pcmp, voice, pos, len); 395a6970bb1STakashi Iwai if (bpos < 0) 396a6970bb1STakashi Iwai return pos; 397a6970bb1STakashi Iwai if (copy_from_user(runtime->dma_area + bpos, src, len)) 398a6970bb1STakashi Iwai return -EFAULT; 399a6970bb1STakashi Iwai return playback_copy_ack(substream, bpos, len); 400a6970bb1STakashi Iwai } 401a6970bb1STakashi Iwai 402a6970bb1STakashi Iwai static int snd_gf1_pcm_playback_copy_kernel(struct snd_pcm_substream *substream, 403a6970bb1STakashi Iwai int voice, unsigned long pos, 404a6970bb1STakashi Iwai void *src, unsigned long count) 405a6970bb1STakashi Iwai { 406a6970bb1STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 407a6970bb1STakashi Iwai struct gus_pcm_private *pcmp = runtime->private_data; 408a6970bb1STakashi Iwai unsigned int len = count; 409a6970bb1STakashi Iwai int bpos; 410a6970bb1STakashi Iwai 411a6970bb1STakashi Iwai bpos = get_bpos(pcmp, voice, pos, len); 412a6970bb1STakashi Iwai if (bpos < 0) 413a6970bb1STakashi Iwai return pos; 414a6970bb1STakashi Iwai memcpy(runtime->dma_area + bpos, src, len); 415a6970bb1STakashi Iwai return playback_copy_ack(substream, bpos, len); 416a6970bb1STakashi Iwai } 417a6970bb1STakashi Iwai 418a6970bb1STakashi Iwai static int snd_gf1_pcm_playback_silence(struct snd_pcm_substream *substream, 419a6970bb1STakashi Iwai int voice, unsigned long pos, 420a6970bb1STakashi Iwai unsigned long count) 421a6970bb1STakashi Iwai { 422a6970bb1STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 423a6970bb1STakashi Iwai struct gus_pcm_private *pcmp = runtime->private_data; 424a6970bb1STakashi Iwai unsigned int len = count; 425a6970bb1STakashi Iwai int bpos; 426a6970bb1STakashi Iwai 427a6970bb1STakashi Iwai bpos = get_bpos(pcmp, voice, pos, len); 428a6970bb1STakashi Iwai if (bpos < 0) 429a6970bb1STakashi Iwai return pos; 430097a7fe3STakashi Sakamoto snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, 431a6970bb1STakashi Iwai bytes_to_samples(runtime, count)); 432a6970bb1STakashi Iwai return playback_copy_ack(substream, bpos, len); 4331da177e4SLinus Torvalds } 4341da177e4SLinus Torvalds 4355e2da206STakashi Iwai static int snd_gf1_pcm_playback_hw_params(struct snd_pcm_substream *substream, 4365e2da206STakashi Iwai struct snd_pcm_hw_params *hw_params) 4371da177e4SLinus Torvalds { 4385e2da206STakashi Iwai struct snd_gus_card *gus = snd_pcm_substream_chip(substream); 4395e2da206STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 4405e2da206STakashi Iwai struct gus_pcm_private *pcmp = runtime->private_data; 4411da177e4SLinus Torvalds int err; 4421da177e4SLinus Torvalds 4431da177e4SLinus Torvalds if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) 4441da177e4SLinus Torvalds return err; 4451da177e4SLinus Torvalds if (err > 0) { /* change */ 4465e2da206STakashi Iwai struct snd_gf1_mem_block *block; 4471da177e4SLinus Torvalds if (pcmp->memory > 0) { 4481da177e4SLinus Torvalds snd_gf1_mem_free(&gus->gf1.mem_alloc, pcmp->memory); 4491da177e4SLinus Torvalds pcmp->memory = 0; 4501da177e4SLinus Torvalds } 4511da177e4SLinus Torvalds if ((block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, 4521da177e4SLinus Torvalds SNDRV_GF1_MEM_OWNER_DRIVER, 4531da177e4SLinus Torvalds "GF1 PCM", 4541da177e4SLinus Torvalds runtime->dma_bytes, 1, 32, 4551da177e4SLinus Torvalds NULL)) == NULL) 4561da177e4SLinus Torvalds return -ENOMEM; 4571da177e4SLinus Torvalds pcmp->memory = block->ptr; 4581da177e4SLinus Torvalds } 4591da177e4SLinus Torvalds pcmp->voices = params_channels(hw_params); 4601da177e4SLinus Torvalds if (pcmp->pvoices[0] == NULL) { 4611da177e4SLinus Torvalds if ((pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL) 4621da177e4SLinus Torvalds return -ENOMEM; 4631da177e4SLinus Torvalds pcmp->pvoices[0]->handler_wave = snd_gf1_pcm_interrupt_wave; 4641da177e4SLinus Torvalds pcmp->pvoices[0]->handler_volume = snd_gf1_pcm_interrupt_volume; 4651da177e4SLinus Torvalds pcmp->pvoices[0]->volume_change = snd_gf1_pcm_volume_change; 4661da177e4SLinus Torvalds pcmp->pvoices[0]->private_data = pcmp; 4671da177e4SLinus Torvalds } 4681da177e4SLinus Torvalds if (pcmp->voices > 1 && pcmp->pvoices[1] == NULL) { 4691da177e4SLinus Torvalds if ((pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL) 4701da177e4SLinus Torvalds return -ENOMEM; 4711da177e4SLinus Torvalds pcmp->pvoices[1]->handler_wave = snd_gf1_pcm_interrupt_wave; 4721da177e4SLinus Torvalds pcmp->pvoices[1]->handler_volume = snd_gf1_pcm_interrupt_volume; 4731da177e4SLinus Torvalds pcmp->pvoices[1]->volume_change = snd_gf1_pcm_volume_change; 4741da177e4SLinus Torvalds pcmp->pvoices[1]->private_data = pcmp; 4751da177e4SLinus Torvalds } else if (pcmp->voices == 1) { 4761da177e4SLinus Torvalds if (pcmp->pvoices[1]) { 4771da177e4SLinus Torvalds snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]); 4781da177e4SLinus Torvalds pcmp->pvoices[1] = NULL; 4791da177e4SLinus Torvalds } 4801da177e4SLinus Torvalds } 4811da177e4SLinus Torvalds return 0; 4821da177e4SLinus Torvalds } 4831da177e4SLinus Torvalds 4845e2da206STakashi Iwai static int snd_gf1_pcm_playback_hw_free(struct snd_pcm_substream *substream) 4851da177e4SLinus Torvalds { 4865e2da206STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 4875e2da206STakashi Iwai struct gus_pcm_private *pcmp = runtime->private_data; 4881da177e4SLinus Torvalds 4891da177e4SLinus Torvalds snd_pcm_lib_free_pages(substream); 4901da177e4SLinus Torvalds if (pcmp->pvoices[0]) { 4911da177e4SLinus Torvalds snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[0]); 4921da177e4SLinus Torvalds pcmp->pvoices[0] = NULL; 4931da177e4SLinus Torvalds } 4941da177e4SLinus Torvalds if (pcmp->pvoices[1]) { 4951da177e4SLinus Torvalds snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]); 4961da177e4SLinus Torvalds pcmp->pvoices[1] = NULL; 4971da177e4SLinus Torvalds } 4981da177e4SLinus Torvalds if (pcmp->memory > 0) { 4991da177e4SLinus Torvalds snd_gf1_mem_free(&pcmp->gus->gf1.mem_alloc, pcmp->memory); 5001da177e4SLinus Torvalds pcmp->memory = 0; 5011da177e4SLinus Torvalds } 5021da177e4SLinus Torvalds return 0; 5031da177e4SLinus Torvalds } 5041da177e4SLinus Torvalds 5055e2da206STakashi Iwai static int snd_gf1_pcm_playback_prepare(struct snd_pcm_substream *substream) 5061da177e4SLinus Torvalds { 5075e2da206STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 5085e2da206STakashi Iwai struct gus_pcm_private *pcmp = runtime->private_data; 5091da177e4SLinus Torvalds 5101da177e4SLinus Torvalds pcmp->bpos = 0; 5111da177e4SLinus Torvalds pcmp->dma_size = snd_pcm_lib_buffer_bytes(substream); 5121da177e4SLinus Torvalds pcmp->block_size = snd_pcm_lib_period_bytes(substream); 5131da177e4SLinus Torvalds pcmp->blocks = pcmp->dma_size / pcmp->block_size; 5141da177e4SLinus Torvalds return 0; 5151da177e4SLinus Torvalds } 5161da177e4SLinus Torvalds 5175e2da206STakashi Iwai static int snd_gf1_pcm_playback_trigger(struct snd_pcm_substream *substream, 5181da177e4SLinus Torvalds int cmd) 5191da177e4SLinus Torvalds { 5205e2da206STakashi Iwai struct snd_gus_card *gus = snd_pcm_substream_chip(substream); 5215e2da206STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 5225e2da206STakashi Iwai struct gus_pcm_private *pcmp = runtime->private_data; 5231da177e4SLinus Torvalds int voice; 5241da177e4SLinus Torvalds 5251da177e4SLinus Torvalds if (cmd == SNDRV_PCM_TRIGGER_START) { 5261da177e4SLinus Torvalds snd_gf1_pcm_trigger_up(substream); 5271da177e4SLinus Torvalds } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { 5281da177e4SLinus Torvalds spin_lock(&pcmp->lock); 5291da177e4SLinus Torvalds pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE; 5301da177e4SLinus Torvalds spin_unlock(&pcmp->lock); 5311da177e4SLinus Torvalds voice = pcmp->pvoices[0]->number; 5321da177e4SLinus Torvalds snd_gf1_stop_voices(gus, voice, voice); 5331da177e4SLinus Torvalds if (pcmp->pvoices[1]) { 5341da177e4SLinus Torvalds voice = pcmp->pvoices[1]->number; 5351da177e4SLinus Torvalds snd_gf1_stop_voices(gus, voice, voice); 5361da177e4SLinus Torvalds } 5371da177e4SLinus Torvalds } else { 5381da177e4SLinus Torvalds return -EINVAL; 5391da177e4SLinus Torvalds } 5401da177e4SLinus Torvalds return 0; 5411da177e4SLinus Torvalds } 5421da177e4SLinus Torvalds 5435e2da206STakashi Iwai static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(struct snd_pcm_substream *substream) 5441da177e4SLinus Torvalds { 5455e2da206STakashi Iwai struct snd_gus_card *gus = snd_pcm_substream_chip(substream); 5465e2da206STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 5475e2da206STakashi Iwai struct gus_pcm_private *pcmp = runtime->private_data; 5481da177e4SLinus Torvalds unsigned int pos; 5491da177e4SLinus Torvalds unsigned char voice_ctrl; 5501da177e4SLinus Torvalds 5511da177e4SLinus Torvalds pos = 0; 5521da177e4SLinus Torvalds spin_lock(&gus->reg_lock); 5531da177e4SLinus Torvalds if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { 5541da177e4SLinus Torvalds snd_gf1_select_voice(gus, pcmp->pvoices[0]->number); 5551da177e4SLinus Torvalds voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); 5561da177e4SLinus Torvalds pos = (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4) - pcmp->memory; 5571da177e4SLinus Torvalds if (substream->runtime->channels > 1) 5581da177e4SLinus Torvalds pos <<= 1; 5591da177e4SLinus Torvalds pos = bytes_to_frames(runtime, pos); 5601da177e4SLinus Torvalds } 5611da177e4SLinus Torvalds spin_unlock(&gus->reg_lock); 5621da177e4SLinus Torvalds return pos; 5631da177e4SLinus Torvalds } 5641da177e4SLinus Torvalds 565bfa516a1STakashi Iwai static const struct snd_ratnum clock = { 5661da177e4SLinus Torvalds .num = 9878400/16, 5671da177e4SLinus Torvalds .den_min = 2, 5681da177e4SLinus Torvalds .den_max = 257, 5691da177e4SLinus Torvalds .den_step = 1, 5701da177e4SLinus Torvalds }; 5711da177e4SLinus Torvalds 572bfa516a1STakashi Iwai static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = { 5731da177e4SLinus Torvalds .nrats = 1, 5741da177e4SLinus Torvalds .rats = &clock, 5751da177e4SLinus Torvalds }; 5761da177e4SLinus Torvalds 5775e2da206STakashi Iwai static int snd_gf1_pcm_capture_hw_params(struct snd_pcm_substream *substream, 5785e2da206STakashi Iwai struct snd_pcm_hw_params *hw_params) 5791da177e4SLinus Torvalds { 5805e2da206STakashi Iwai struct snd_gus_card *gus = snd_pcm_substream_chip(substream); 5811da177e4SLinus Torvalds 5821da177e4SLinus Torvalds gus->c_dma_size = params_buffer_bytes(hw_params); 5831da177e4SLinus Torvalds gus->c_period_size = params_period_bytes(hw_params); 5841da177e4SLinus Torvalds gus->c_pos = 0; 5851da177e4SLinus Torvalds gus->gf1.pcm_rcntrl_reg = 0x21; /* IRQ at end, enable & start */ 5861da177e4SLinus Torvalds if (params_channels(hw_params) > 1) 5871da177e4SLinus Torvalds gus->gf1.pcm_rcntrl_reg |= 2; 5881da177e4SLinus Torvalds if (gus->gf1.dma2 > 3) 5891da177e4SLinus Torvalds gus->gf1.pcm_rcntrl_reg |= 4; 5901da177e4SLinus Torvalds if (snd_pcm_format_unsigned(params_format(hw_params))) 5911da177e4SLinus Torvalds gus->gf1.pcm_rcntrl_reg |= 0x80; 5921da177e4SLinus Torvalds return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); 5931da177e4SLinus Torvalds } 5941da177e4SLinus Torvalds 5955e2da206STakashi Iwai static int snd_gf1_pcm_capture_hw_free(struct snd_pcm_substream *substream) 5961da177e4SLinus Torvalds { 5971da177e4SLinus Torvalds return snd_pcm_lib_free_pages(substream); 5981da177e4SLinus Torvalds } 5991da177e4SLinus Torvalds 6005e2da206STakashi Iwai static int snd_gf1_pcm_capture_prepare(struct snd_pcm_substream *substream) 6011da177e4SLinus Torvalds { 6025e2da206STakashi Iwai struct snd_gus_card *gus = snd_pcm_substream_chip(substream); 6035e2da206STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 6041da177e4SLinus Torvalds 6051da177e4SLinus Torvalds snd_gf1_i_write8(gus, SNDRV_GF1_GB_RECORD_RATE, runtime->rate_den - 2); 6061da177e4SLinus Torvalds snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */ 6071da177e4SLinus Torvalds snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ 6081da177e4SLinus Torvalds snd_dma_program(gus->gf1.dma2, runtime->dma_addr, gus->c_period_size, DMA_MODE_READ); 6091da177e4SLinus Torvalds return 0; 6101da177e4SLinus Torvalds } 6111da177e4SLinus Torvalds 6125e2da206STakashi Iwai static int snd_gf1_pcm_capture_trigger(struct snd_pcm_substream *substream, 6131da177e4SLinus Torvalds int cmd) 6141da177e4SLinus Torvalds { 6155e2da206STakashi Iwai struct snd_gus_card *gus = snd_pcm_substream_chip(substream); 6161da177e4SLinus Torvalds int val; 6171da177e4SLinus Torvalds 6181da177e4SLinus Torvalds if (cmd == SNDRV_PCM_TRIGGER_START) { 6191da177e4SLinus Torvalds val = gus->gf1.pcm_rcntrl_reg; 6201da177e4SLinus Torvalds } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { 6211da177e4SLinus Torvalds val = 0; 6221da177e4SLinus Torvalds } else { 6231da177e4SLinus Torvalds return -EINVAL; 6241da177e4SLinus Torvalds } 6251da177e4SLinus Torvalds 6261da177e4SLinus Torvalds spin_lock(&gus->reg_lock); 6271da177e4SLinus Torvalds snd_gf1_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, val); 6281da177e4SLinus Torvalds snd_gf1_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); 6291da177e4SLinus Torvalds spin_unlock(&gus->reg_lock); 6301da177e4SLinus Torvalds return 0; 6311da177e4SLinus Torvalds } 6321da177e4SLinus Torvalds 6335e2da206STakashi Iwai static snd_pcm_uframes_t snd_gf1_pcm_capture_pointer(struct snd_pcm_substream *substream) 6341da177e4SLinus Torvalds { 6355e2da206STakashi Iwai struct snd_gus_card *gus = snd_pcm_substream_chip(substream); 6361da177e4SLinus Torvalds int pos = snd_dma_pointer(gus->gf1.dma2, gus->c_period_size); 6371da177e4SLinus Torvalds pos = bytes_to_frames(substream->runtime, (gus->c_pos + pos) % gus->c_dma_size); 6381da177e4SLinus Torvalds return pos; 6391da177e4SLinus Torvalds } 6401da177e4SLinus Torvalds 6415e2da206STakashi Iwai static void snd_gf1_pcm_interrupt_dma_read(struct snd_gus_card * gus) 6421da177e4SLinus Torvalds { 6431da177e4SLinus Torvalds snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */ 6441da177e4SLinus Torvalds snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ 6451da177e4SLinus Torvalds if (gus->pcm_cap_substream != NULL) { 6461da177e4SLinus Torvalds snd_gf1_pcm_capture_prepare(gus->pcm_cap_substream); 6471da177e4SLinus Torvalds snd_gf1_pcm_capture_trigger(gus->pcm_cap_substream, SNDRV_PCM_TRIGGER_START); 6481da177e4SLinus Torvalds gus->c_pos += gus->c_period_size; 6491da177e4SLinus Torvalds snd_pcm_period_elapsed(gus->pcm_cap_substream); 6501da177e4SLinus Torvalds } 6511da177e4SLinus Torvalds } 6521da177e4SLinus Torvalds 653aec54654SBhumika Goyal static const struct snd_pcm_hardware snd_gf1_pcm_playback = 6541da177e4SLinus Torvalds { 6551da177e4SLinus Torvalds .info = SNDRV_PCM_INFO_NONINTERLEAVED, 6561da177e4SLinus Torvalds .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | 6571da177e4SLinus Torvalds SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), 6581da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 6591da177e4SLinus Torvalds .rate_min = 5510, 6601da177e4SLinus Torvalds .rate_max = 48000, 6611da177e4SLinus Torvalds .channels_min = 1, 6621da177e4SLinus Torvalds .channels_max = 2, 6631da177e4SLinus Torvalds .buffer_bytes_max = (128*1024), 6641da177e4SLinus Torvalds .period_bytes_min = 64, 6651da177e4SLinus Torvalds .period_bytes_max = (128*1024), 6661da177e4SLinus Torvalds .periods_min = 1, 6671da177e4SLinus Torvalds .periods_max = 1024, 6681da177e4SLinus Torvalds .fifo_size = 0, 6691da177e4SLinus Torvalds }; 6701da177e4SLinus Torvalds 671aec54654SBhumika Goyal static const struct snd_pcm_hardware snd_gf1_pcm_capture = 6721da177e4SLinus Torvalds { 6731da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 6741da177e4SLinus Torvalds SNDRV_PCM_INFO_MMAP_VALID), 6751da177e4SLinus Torvalds .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8, 6761da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, 6771da177e4SLinus Torvalds .rate_min = 5510, 6781da177e4SLinus Torvalds .rate_max = 44100, 6791da177e4SLinus Torvalds .channels_min = 1, 6801da177e4SLinus Torvalds .channels_max = 2, 6811da177e4SLinus Torvalds .buffer_bytes_max = (128*1024), 6821da177e4SLinus Torvalds .period_bytes_min = 64, 6831da177e4SLinus Torvalds .period_bytes_max = (128*1024), 6841da177e4SLinus Torvalds .periods_min = 1, 6851da177e4SLinus Torvalds .periods_max = 1024, 6861da177e4SLinus Torvalds .fifo_size = 0, 6871da177e4SLinus Torvalds }; 6881da177e4SLinus Torvalds 6895e2da206STakashi Iwai static void snd_gf1_pcm_playback_free(struct snd_pcm_runtime *runtime) 6901da177e4SLinus Torvalds { 6914d572776SJesper Juhl kfree(runtime->private_data); 6921da177e4SLinus Torvalds } 6931da177e4SLinus Torvalds 6945e2da206STakashi Iwai static int snd_gf1_pcm_playback_open(struct snd_pcm_substream *substream) 6951da177e4SLinus Torvalds { 6965e2da206STakashi Iwai struct gus_pcm_private *pcmp; 6975e2da206STakashi Iwai struct snd_gus_card *gus = snd_pcm_substream_chip(substream); 6985e2da206STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 6991da177e4SLinus Torvalds int err; 7001da177e4SLinus Torvalds 7019e76a76eSTakashi Iwai pcmp = kzalloc(sizeof(*pcmp), GFP_KERNEL); 7021da177e4SLinus Torvalds if (pcmp == NULL) 7031da177e4SLinus Torvalds return -ENOMEM; 7041da177e4SLinus Torvalds pcmp->gus = gus; 7051da177e4SLinus Torvalds spin_lock_init(&pcmp->lock); 7061da177e4SLinus Torvalds init_waitqueue_head(&pcmp->sleep); 7071da177e4SLinus Torvalds atomic_set(&pcmp->dma_count, 0); 7081da177e4SLinus Torvalds 7091da177e4SLinus Torvalds runtime->private_data = pcmp; 7101da177e4SLinus Torvalds runtime->private_free = snd_gf1_pcm_playback_free; 7111da177e4SLinus Torvalds 7121da177e4SLinus Torvalds #if 0 71391f05060STakashi Iwai printk(KERN_DEBUG "playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n", 71491f05060STakashi Iwai (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer); 7151da177e4SLinus Torvalds #endif 7161da177e4SLinus Torvalds if ((err = snd_gf1_dma_init(gus)) < 0) 7171da177e4SLinus Torvalds return err; 7181da177e4SLinus Torvalds pcmp->flags = SNDRV_GF1_PCM_PFLG_NONE; 7191da177e4SLinus Torvalds pcmp->substream = substream; 7201da177e4SLinus Torvalds runtime->hw = snd_gf1_pcm_playback; 7211da177e4SLinus Torvalds snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.buffer_bytes_max); 7221da177e4SLinus Torvalds snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.period_bytes_max); 7231da177e4SLinus Torvalds snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64); 7241da177e4SLinus Torvalds return 0; 7251da177e4SLinus Torvalds } 7261da177e4SLinus Torvalds 7275e2da206STakashi Iwai static int snd_gf1_pcm_playback_close(struct snd_pcm_substream *substream) 7281da177e4SLinus Torvalds { 7295e2da206STakashi Iwai struct snd_gus_card *gus = snd_pcm_substream_chip(substream); 7305e2da206STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 7315e2da206STakashi Iwai struct gus_pcm_private *pcmp = runtime->private_data; 7321da177e4SLinus Torvalds 7331da177e4SLinus Torvalds if (!wait_event_timeout(pcmp->sleep, (atomic_read(&pcmp->dma_count) <= 0), 2*HZ)) 73499b359baSTakashi Iwai snd_printk(KERN_ERR "gf1 pcm - serious DMA problem\n"); 7351da177e4SLinus Torvalds 7361da177e4SLinus Torvalds snd_gf1_dma_done(gus); 7371da177e4SLinus Torvalds return 0; 7381da177e4SLinus Torvalds } 7391da177e4SLinus Torvalds 7405e2da206STakashi Iwai static int snd_gf1_pcm_capture_open(struct snd_pcm_substream *substream) 7411da177e4SLinus Torvalds { 7425e2da206STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 7435e2da206STakashi Iwai struct snd_gus_card *gus = snd_pcm_substream_chip(substream); 7441da177e4SLinus Torvalds 7451da177e4SLinus Torvalds gus->gf1.interrupt_handler_dma_read = snd_gf1_pcm_interrupt_dma_read; 7461da177e4SLinus Torvalds gus->pcm_cap_substream = substream; 7471da177e4SLinus Torvalds substream->runtime->hw = snd_gf1_pcm_capture; 7481da177e4SLinus Torvalds snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.buffer_bytes_max); 7491da177e4SLinus Torvalds snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.period_bytes_max); 7501da177e4SLinus Torvalds snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 7511da177e4SLinus Torvalds &hw_constraints_clocks); 7521da177e4SLinus Torvalds return 0; 7531da177e4SLinus Torvalds } 7541da177e4SLinus Torvalds 7555e2da206STakashi Iwai static int snd_gf1_pcm_capture_close(struct snd_pcm_substream *substream) 7561da177e4SLinus Torvalds { 7575e2da206STakashi Iwai struct snd_gus_card *gus = snd_pcm_substream_chip(substream); 7581da177e4SLinus Torvalds 7591da177e4SLinus Torvalds gus->pcm_cap_substream = NULL; 7601da177e4SLinus Torvalds snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_READ); 7611da177e4SLinus Torvalds return 0; 7621da177e4SLinus Torvalds } 7631da177e4SLinus Torvalds 7645e2da206STakashi Iwai static int snd_gf1_pcm_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 7651da177e4SLinus Torvalds { 7661da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 7671da177e4SLinus Torvalds uinfo->count = 2; 7681da177e4SLinus Torvalds uinfo->value.integer.min = 0; 7691da177e4SLinus Torvalds uinfo->value.integer.max = 127; 7701da177e4SLinus Torvalds return 0; 7711da177e4SLinus Torvalds } 7721da177e4SLinus Torvalds 7735e2da206STakashi Iwai static int snd_gf1_pcm_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 7741da177e4SLinus Torvalds { 7755e2da206STakashi Iwai struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); 7761da177e4SLinus Torvalds unsigned long flags; 7771da177e4SLinus Torvalds 7781da177e4SLinus Torvalds spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); 7791da177e4SLinus Torvalds ucontrol->value.integer.value[0] = gus->gf1.pcm_volume_level_left1; 7801da177e4SLinus Torvalds ucontrol->value.integer.value[1] = gus->gf1.pcm_volume_level_right1; 7811da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags); 7821da177e4SLinus Torvalds return 0; 7831da177e4SLinus Torvalds } 7841da177e4SLinus Torvalds 7855e2da206STakashi Iwai static int snd_gf1_pcm_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 7861da177e4SLinus Torvalds { 7875e2da206STakashi Iwai struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); 7881da177e4SLinus Torvalds unsigned long flags; 7891da177e4SLinus Torvalds int change; 7901da177e4SLinus Torvalds unsigned int idx; 7911da177e4SLinus Torvalds unsigned short val1, val2, vol; 7925e2da206STakashi Iwai struct gus_pcm_private *pcmp; 7935e2da206STakashi Iwai struct snd_gus_voice *pvoice; 7941da177e4SLinus Torvalds 7951da177e4SLinus Torvalds val1 = ucontrol->value.integer.value[0] & 127; 7961da177e4SLinus Torvalds val2 = ucontrol->value.integer.value[1] & 127; 7971da177e4SLinus Torvalds spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); 7981da177e4SLinus Torvalds change = val1 != gus->gf1.pcm_volume_level_left1 || 7991da177e4SLinus Torvalds val2 != gus->gf1.pcm_volume_level_right1; 8001da177e4SLinus Torvalds gus->gf1.pcm_volume_level_left1 = val1; 8011da177e4SLinus Torvalds gus->gf1.pcm_volume_level_right1 = val2; 8021da177e4SLinus Torvalds gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4; 8031da177e4SLinus Torvalds gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4; 8041da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags); 8051da177e4SLinus Torvalds /* are we active? */ 8061da177e4SLinus Torvalds spin_lock_irqsave(&gus->voice_alloc, flags); 8071da177e4SLinus Torvalds for (idx = 0; idx < 32; idx++) { 8081da177e4SLinus Torvalds pvoice = &gus->gf1.voices[idx]; 8091da177e4SLinus Torvalds if (!pvoice->pcm) 8101da177e4SLinus Torvalds continue; 8111da177e4SLinus Torvalds pcmp = pvoice->private_data; 8121da177e4SLinus Torvalds if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) 8131da177e4SLinus Torvalds continue; 8141da177e4SLinus Torvalds /* load real volume - better precision */ 815fcb2954bSJulia Lawall spin_lock(&gus->reg_lock); 8161da177e4SLinus Torvalds snd_gf1_select_voice(gus, pvoice->number); 8171da177e4SLinus Torvalds snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); 8181da177e4SLinus Torvalds vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; 8191da177e4SLinus Torvalds snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol); 8201da177e4SLinus Torvalds pcmp->final_volume = 1; 821fcb2954bSJulia Lawall spin_unlock(&gus->reg_lock); 8221da177e4SLinus Torvalds } 8231da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->voice_alloc, flags); 8241da177e4SLinus Torvalds return change; 8251da177e4SLinus Torvalds } 8261da177e4SLinus Torvalds 8273a84d6c9SBhumika Goyal static const struct snd_kcontrol_new snd_gf1_pcm_volume_control = 8281da177e4SLinus Torvalds { 8291da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8301da177e4SLinus Torvalds .name = "PCM Playback Volume", 8311da177e4SLinus Torvalds .info = snd_gf1_pcm_volume_info, 8321da177e4SLinus Torvalds .get = snd_gf1_pcm_volume_get, 8331da177e4SLinus Torvalds .put = snd_gf1_pcm_volume_put 8341da177e4SLinus Torvalds }; 8351da177e4SLinus Torvalds 8363a84d6c9SBhumika Goyal static const struct snd_kcontrol_new snd_gf1_pcm_volume_control1 = 8371da177e4SLinus Torvalds { 8381da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8391da177e4SLinus Torvalds .name = "GPCM Playback Volume", 8401da177e4SLinus Torvalds .info = snd_gf1_pcm_volume_info, 8411da177e4SLinus Torvalds .get = snd_gf1_pcm_volume_get, 8421da177e4SLinus Torvalds .put = snd_gf1_pcm_volume_put 8431da177e4SLinus Torvalds }; 8441da177e4SLinus Torvalds 845ca58ba6cSArvind Yadav static const struct snd_pcm_ops snd_gf1_pcm_playback_ops = { 8461da177e4SLinus Torvalds .open = snd_gf1_pcm_playback_open, 8471da177e4SLinus Torvalds .close = snd_gf1_pcm_playback_close, 8481da177e4SLinus Torvalds .ioctl = snd_pcm_lib_ioctl, 8491da177e4SLinus Torvalds .hw_params = snd_gf1_pcm_playback_hw_params, 8501da177e4SLinus Torvalds .hw_free = snd_gf1_pcm_playback_hw_free, 8511da177e4SLinus Torvalds .prepare = snd_gf1_pcm_playback_prepare, 8521da177e4SLinus Torvalds .trigger = snd_gf1_pcm_playback_trigger, 8531da177e4SLinus Torvalds .pointer = snd_gf1_pcm_playback_pointer, 854a6970bb1STakashi Iwai .copy_user = snd_gf1_pcm_playback_copy, 855a6970bb1STakashi Iwai .copy_kernel = snd_gf1_pcm_playback_copy_kernel, 856a6970bb1STakashi Iwai .fill_silence = snd_gf1_pcm_playback_silence, 8571da177e4SLinus Torvalds }; 8581da177e4SLinus Torvalds 859ca58ba6cSArvind Yadav static const struct snd_pcm_ops snd_gf1_pcm_capture_ops = { 8601da177e4SLinus Torvalds .open = snd_gf1_pcm_capture_open, 8611da177e4SLinus Torvalds .close = snd_gf1_pcm_capture_close, 8621da177e4SLinus Torvalds .ioctl = snd_pcm_lib_ioctl, 8631da177e4SLinus Torvalds .hw_params = snd_gf1_pcm_capture_hw_params, 8641da177e4SLinus Torvalds .hw_free = snd_gf1_pcm_capture_hw_free, 8651da177e4SLinus Torvalds .prepare = snd_gf1_pcm_capture_prepare, 8661da177e4SLinus Torvalds .trigger = snd_gf1_pcm_capture_trigger, 8671da177e4SLinus Torvalds .pointer = snd_gf1_pcm_capture_pointer, 8681da177e4SLinus Torvalds }; 8691da177e4SLinus Torvalds 870db5abb3cSLars-Peter Clausen int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index) 8711da177e4SLinus Torvalds { 8725e2da206STakashi Iwai struct snd_card *card; 8735e2da206STakashi Iwai struct snd_kcontrol *kctl; 8745e2da206STakashi Iwai struct snd_pcm *pcm; 8755e2da206STakashi Iwai struct snd_pcm_substream *substream; 8761da177e4SLinus Torvalds int capture, err; 8771da177e4SLinus Torvalds 8781da177e4SLinus Torvalds card = gus->card; 8791da177e4SLinus Torvalds capture = !gus->interwave && !gus->ess_flag && !gus->ace_flag ? 1 : 0; 8801da177e4SLinus Torvalds err = snd_pcm_new(card, 8811da177e4SLinus Torvalds gus->interwave ? "AMD InterWave" : "GF1", 8821da177e4SLinus Torvalds pcm_dev, 8831da177e4SLinus Torvalds gus->gf1.pcm_channels / 2, 8841da177e4SLinus Torvalds capture, 8851da177e4SLinus Torvalds &pcm); 8861da177e4SLinus Torvalds if (err < 0) 8871da177e4SLinus Torvalds return err; 8881da177e4SLinus Torvalds pcm->private_data = gus; 8891da177e4SLinus Torvalds /* playback setup */ 8901da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops); 8911da177e4SLinus Torvalds 8921da177e4SLinus Torvalds for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) 8931da177e4SLinus Torvalds snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, 894*0b6a2c9cSTakashi Iwai card->dev, 8951da177e4SLinus Torvalds 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024); 8961da177e4SLinus Torvalds 8971da177e4SLinus Torvalds pcm->info_flags = 0; 8981da177e4SLinus Torvalds pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; 8991da177e4SLinus Torvalds if (capture) { 9001da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops); 9011da177e4SLinus Torvalds if (gus->gf1.dma2 == gus->gf1.dma1) 9021da177e4SLinus Torvalds pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; 9031da177e4SLinus Torvalds snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 904*0b6a2c9cSTakashi Iwai SNDRV_DMA_TYPE_DEV, card->dev, 9051da177e4SLinus Torvalds 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024); 9061da177e4SLinus Torvalds } 9071da177e4SLinus Torvalds strcpy(pcm->name, pcm->id); 9081da177e4SLinus Torvalds if (gus->interwave) { 9091da177e4SLinus Torvalds sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A'); 9101da177e4SLinus Torvalds } 9111da177e4SLinus Torvalds strcat(pcm->name, " (synth)"); 9121da177e4SLinus Torvalds gus->pcm = pcm; 9131da177e4SLinus Torvalds 9141da177e4SLinus Torvalds if (gus->codec_flag) 9151da177e4SLinus Torvalds kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control1, gus); 9161da177e4SLinus Torvalds else 9171da177e4SLinus Torvalds kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus); 9181da177e4SLinus Torvalds if ((err = snd_ctl_add(card, kctl)) < 0) 9191da177e4SLinus Torvalds return err; 9201da177e4SLinus Torvalds kctl->id.index = control_index; 9211da177e4SLinus Torvalds 9221da177e4SLinus Torvalds return 0; 9231da177e4SLinus Torvalds } 9241da177e4SLinus Torvalds 925