11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Copyright (c) by Jaroslav Kysela <perex@suse.cz> 31da177e4SLinus Torvalds * Creative Labs, Inc. 41da177e4SLinus Torvalds * Routines for control of EMU10K1 chips / PCM routines 51da177e4SLinus Torvalds * Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com> 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * BUGS: 81da177e4SLinus Torvalds * -- 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * TODO: 111da177e4SLinus Torvalds * -- 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 141da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 151da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 161da177e4SLinus Torvalds * (at your option) any later version. 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 191da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 201da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 211da177e4SLinus Torvalds * GNU General Public License for more details. 221da177e4SLinus Torvalds * 231da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 241da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 251da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 261da177e4SLinus Torvalds * 271da177e4SLinus Torvalds */ 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds #include <sound/driver.h> 301da177e4SLinus Torvalds #include <linux/pci.h> 311da177e4SLinus Torvalds #include <linux/delay.h> 321da177e4SLinus Torvalds #include <linux/slab.h> 331da177e4SLinus Torvalds #include <linux/time.h> 341da177e4SLinus Torvalds #include <linux/init.h> 351da177e4SLinus Torvalds #include <sound/core.h> 361da177e4SLinus Torvalds #include <sound/emu10k1.h> 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds static void snd_emu10k1_pcm_interrupt(emu10k1_t *emu, emu10k1_voice_t *voice) 391da177e4SLinus Torvalds { 401da177e4SLinus Torvalds emu10k1_pcm_t *epcm; 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds if ((epcm = voice->epcm) == NULL) 431da177e4SLinus Torvalds return; 441da177e4SLinus Torvalds if (epcm->substream == NULL) 451da177e4SLinus Torvalds return; 461da177e4SLinus Torvalds #if 0 471da177e4SLinus Torvalds printk("IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n", 481da177e4SLinus Torvalds epcm->substream->runtime->hw->pointer(emu, epcm->substream), 491da177e4SLinus Torvalds snd_pcm_lib_period_bytes(epcm->substream), 501da177e4SLinus Torvalds snd_pcm_lib_buffer_bytes(epcm->substream)); 511da177e4SLinus Torvalds #endif 521da177e4SLinus Torvalds snd_pcm_period_elapsed(epcm->substream); 531da177e4SLinus Torvalds } 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds static void snd_emu10k1_pcm_ac97adc_interrupt(emu10k1_t *emu, unsigned int status) 561da177e4SLinus Torvalds { 571da177e4SLinus Torvalds #if 0 581da177e4SLinus Torvalds if (status & IPR_ADCBUFHALFFULL) { 591da177e4SLinus Torvalds if (emu->pcm_capture_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) 601da177e4SLinus Torvalds return; 611da177e4SLinus Torvalds } 621da177e4SLinus Torvalds #endif 631da177e4SLinus Torvalds snd_pcm_period_elapsed(emu->pcm_capture_substream); 641da177e4SLinus Torvalds } 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds static void snd_emu10k1_pcm_ac97mic_interrupt(emu10k1_t *emu, unsigned int status) 671da177e4SLinus Torvalds { 681da177e4SLinus Torvalds #if 0 691da177e4SLinus Torvalds if (status & IPR_MICBUFHALFFULL) { 701da177e4SLinus Torvalds if (emu->pcm_capture_mic_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) 711da177e4SLinus Torvalds return; 721da177e4SLinus Torvalds } 731da177e4SLinus Torvalds #endif 741da177e4SLinus Torvalds snd_pcm_period_elapsed(emu->pcm_capture_mic_substream); 751da177e4SLinus Torvalds } 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds static void snd_emu10k1_pcm_efx_interrupt(emu10k1_t *emu, unsigned int status) 781da177e4SLinus Torvalds { 791da177e4SLinus Torvalds #if 0 801da177e4SLinus Torvalds if (status & IPR_EFXBUFHALFFULL) { 811da177e4SLinus Torvalds if (emu->pcm_capture_efx_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) 821da177e4SLinus Torvalds return; 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds #endif 851da177e4SLinus Torvalds snd_pcm_period_elapsed(emu->pcm_capture_efx_substream); 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds static snd_pcm_uframes_t snd_emu10k1_efx_playback_pointer(snd_pcm_substream_t * substream) 891da177e4SLinus Torvalds { 901da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 911da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 921da177e4SLinus Torvalds emu10k1_pcm_t *epcm = runtime->private_data; 931da177e4SLinus Torvalds unsigned int ptr; 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds if (!epcm->running) 961da177e4SLinus Torvalds return 0; 971da177e4SLinus Torvalds ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff; 981da177e4SLinus Torvalds ptr += runtime->buffer_size; 991da177e4SLinus Torvalds ptr -= epcm->ccca_start_addr; 1001da177e4SLinus Torvalds ptr %= runtime->buffer_size; 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds return ptr; 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds static int snd_emu10k1_pcm_channel_alloc(emu10k1_pcm_t * epcm, int voices) 1061da177e4SLinus Torvalds { 1071da177e4SLinus Torvalds int err, i; 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds if (epcm->voices[1] != NULL && voices < 2) { 1101da177e4SLinus Torvalds snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); 1111da177e4SLinus Torvalds epcm->voices[1] = NULL; 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds for (i = 0; i < voices; i++) { 1141da177e4SLinus Torvalds if (epcm->voices[i] == NULL) 1151da177e4SLinus Torvalds break; 1161da177e4SLinus Torvalds } 1171da177e4SLinus Torvalds if (i == voices) 1181da177e4SLinus Torvalds return 0; /* already allocated */ 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(epcm->voices); i++) { 1211da177e4SLinus Torvalds if (epcm->voices[i]) { 1221da177e4SLinus Torvalds snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]); 1231da177e4SLinus Torvalds epcm->voices[i] = NULL; 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds err = snd_emu10k1_voice_alloc(epcm->emu, 1271da177e4SLinus Torvalds epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX, 1281da177e4SLinus Torvalds voices, 1291da177e4SLinus Torvalds &epcm->voices[0]); 1301da177e4SLinus Torvalds 1311da177e4SLinus Torvalds if (err < 0) 1321da177e4SLinus Torvalds return err; 1331da177e4SLinus Torvalds epcm->voices[0]->epcm = epcm; 1341da177e4SLinus Torvalds if (voices > 1) { 1351da177e4SLinus Torvalds for (i = 1; i < voices; i++) { 1361da177e4SLinus Torvalds epcm->voices[i] = &epcm->emu->voices[epcm->voices[0]->number + i]; 1371da177e4SLinus Torvalds epcm->voices[i]->epcm = epcm; 1381da177e4SLinus Torvalds } 1391da177e4SLinus Torvalds } 1401da177e4SLinus Torvalds if (epcm->extra == NULL) { 1411da177e4SLinus Torvalds err = snd_emu10k1_voice_alloc(epcm->emu, 1421da177e4SLinus Torvalds epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX, 1431da177e4SLinus Torvalds 1, 1441da177e4SLinus Torvalds &epcm->extra); 1451da177e4SLinus Torvalds if (err < 0) { 1461da177e4SLinus Torvalds // printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame); 1471da177e4SLinus Torvalds for (i = 0; i < voices; i++) { 1481da177e4SLinus Torvalds snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]); 1491da177e4SLinus Torvalds epcm->voices[i] = NULL; 1501da177e4SLinus Torvalds } 1511da177e4SLinus Torvalds return err; 1521da177e4SLinus Torvalds } 1531da177e4SLinus Torvalds epcm->extra->epcm = epcm; 1541da177e4SLinus Torvalds epcm->extra->interrupt = snd_emu10k1_pcm_interrupt; 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds return 0; 1571da177e4SLinus Torvalds } 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds static unsigned int capture_period_sizes[31] = { 1601da177e4SLinus Torvalds 384, 448, 512, 640, 1611da177e4SLinus Torvalds 384*2, 448*2, 512*2, 640*2, 1621da177e4SLinus Torvalds 384*4, 448*4, 512*4, 640*4, 1631da177e4SLinus Torvalds 384*8, 448*8, 512*8, 640*8, 1641da177e4SLinus Torvalds 384*16, 448*16, 512*16, 640*16, 1651da177e4SLinus Torvalds 384*32, 448*32, 512*32, 640*32, 1661da177e4SLinus Torvalds 384*64, 448*64, 512*64, 640*64, 1671da177e4SLinus Torvalds 384*128,448*128,512*128 1681da177e4SLinus Torvalds }; 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds static snd_pcm_hw_constraint_list_t hw_constraints_capture_period_sizes = { 1711da177e4SLinus Torvalds .count = 31, 1721da177e4SLinus Torvalds .list = capture_period_sizes, 1731da177e4SLinus Torvalds .mask = 0 1741da177e4SLinus Torvalds }; 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds static unsigned int capture_rates[8] = { 1771da177e4SLinus Torvalds 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000 1781da177e4SLinus Torvalds }; 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds static snd_pcm_hw_constraint_list_t hw_constraints_capture_rates = { 1811da177e4SLinus Torvalds .count = 8, 1821da177e4SLinus Torvalds .list = capture_rates, 1831da177e4SLinus Torvalds .mask = 0 1841da177e4SLinus Torvalds }; 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds static unsigned int snd_emu10k1_capture_rate_reg(unsigned int rate) 1871da177e4SLinus Torvalds { 1881da177e4SLinus Torvalds switch (rate) { 1891da177e4SLinus Torvalds case 8000: return ADCCR_SAMPLERATE_8; 1901da177e4SLinus Torvalds case 11025: return ADCCR_SAMPLERATE_11; 1911da177e4SLinus Torvalds case 16000: return ADCCR_SAMPLERATE_16; 1921da177e4SLinus Torvalds case 22050: return ADCCR_SAMPLERATE_22; 1931da177e4SLinus Torvalds case 24000: return ADCCR_SAMPLERATE_24; 1941da177e4SLinus Torvalds case 32000: return ADCCR_SAMPLERATE_32; 1951da177e4SLinus Torvalds case 44100: return ADCCR_SAMPLERATE_44; 1961da177e4SLinus Torvalds case 48000: return ADCCR_SAMPLERATE_48; 1971da177e4SLinus Torvalds default: 1981da177e4SLinus Torvalds snd_BUG(); 1991da177e4SLinus Torvalds return ADCCR_SAMPLERATE_8; 2001da177e4SLinus Torvalds } 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds static unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate) 2041da177e4SLinus Torvalds { 2051da177e4SLinus Torvalds switch (rate) { 2061da177e4SLinus Torvalds case 8000: return A_ADCCR_SAMPLERATE_8; 2071da177e4SLinus Torvalds case 11025: return A_ADCCR_SAMPLERATE_11; 2081da177e4SLinus Torvalds case 12000: return A_ADCCR_SAMPLERATE_12; /* really supported? */ 2091da177e4SLinus Torvalds case 16000: return ADCCR_SAMPLERATE_16; 2101da177e4SLinus Torvalds case 22050: return ADCCR_SAMPLERATE_22; 2111da177e4SLinus Torvalds case 24000: return ADCCR_SAMPLERATE_24; 2121da177e4SLinus Torvalds case 32000: return ADCCR_SAMPLERATE_32; 2131da177e4SLinus Torvalds case 44100: return ADCCR_SAMPLERATE_44; 2141da177e4SLinus Torvalds case 48000: return ADCCR_SAMPLERATE_48; 2151da177e4SLinus Torvalds default: 2161da177e4SLinus Torvalds snd_BUG(); 2171da177e4SLinus Torvalds return A_ADCCR_SAMPLERATE_8; 2181da177e4SLinus Torvalds } 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds static unsigned int emu10k1_calc_pitch_target(unsigned int rate) 2221da177e4SLinus Torvalds { 2231da177e4SLinus Torvalds unsigned int pitch_target; 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds pitch_target = (rate << 8) / 375; 2261da177e4SLinus Torvalds pitch_target = (pitch_target >> 1) + (pitch_target & 1); 2271da177e4SLinus Torvalds return pitch_target; 2281da177e4SLinus Torvalds } 2291da177e4SLinus Torvalds 2301da177e4SLinus Torvalds #define PITCH_48000 0x00004000 2311da177e4SLinus Torvalds #define PITCH_96000 0x00008000 2321da177e4SLinus Torvalds #define PITCH_85000 0x00007155 2331da177e4SLinus Torvalds #define PITCH_80726 0x00006ba2 2341da177e4SLinus Torvalds #define PITCH_67882 0x00005a82 2351da177e4SLinus Torvalds #define PITCH_57081 0x00004c1c 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds static unsigned int emu10k1_select_interprom(unsigned int pitch_target) 2381da177e4SLinus Torvalds { 2391da177e4SLinus Torvalds if (pitch_target == PITCH_48000) 2401da177e4SLinus Torvalds return CCCA_INTERPROM_0; 2411da177e4SLinus Torvalds else if (pitch_target < PITCH_48000) 2421da177e4SLinus Torvalds return CCCA_INTERPROM_1; 2431da177e4SLinus Torvalds else if (pitch_target >= PITCH_96000) 2441da177e4SLinus Torvalds return CCCA_INTERPROM_0; 2451da177e4SLinus Torvalds else if (pitch_target >= PITCH_85000) 2461da177e4SLinus Torvalds return CCCA_INTERPROM_6; 2471da177e4SLinus Torvalds else if (pitch_target >= PITCH_80726) 2481da177e4SLinus Torvalds return CCCA_INTERPROM_5; 2491da177e4SLinus Torvalds else if (pitch_target >= PITCH_67882) 2501da177e4SLinus Torvalds return CCCA_INTERPROM_4; 2511da177e4SLinus Torvalds else if (pitch_target >= PITCH_57081) 2521da177e4SLinus Torvalds return CCCA_INTERPROM_3; 2531da177e4SLinus Torvalds else 2541da177e4SLinus Torvalds return CCCA_INTERPROM_2; 2551da177e4SLinus Torvalds } 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds /* 2581da177e4SLinus Torvalds * calculate cache invalidate size 2591da177e4SLinus Torvalds * 2601da177e4SLinus Torvalds * stereo: channel is stereo 2611da177e4SLinus Torvalds * w_16: using 16bit samples 2621da177e4SLinus Torvalds * 2631da177e4SLinus Torvalds * returns: cache invalidate size in samples 2641da177e4SLinus Torvalds */ 265fe5ac9dcSJesper Juhl static inline int emu10k1_ccis(int stereo, int w_16) 2661da177e4SLinus Torvalds { 2671da177e4SLinus Torvalds if (w_16) { 2681da177e4SLinus Torvalds return stereo ? 24 : 26; 2691da177e4SLinus Torvalds } else { 2701da177e4SLinus Torvalds return stereo ? 24*2 : 26*2; 2711da177e4SLinus Torvalds } 2721da177e4SLinus Torvalds } 2731da177e4SLinus Torvalds 2741da177e4SLinus Torvalds static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu, 2751da177e4SLinus Torvalds int master, int extra, 2761da177e4SLinus Torvalds emu10k1_voice_t *evoice, 2771da177e4SLinus Torvalds unsigned int start_addr, 2781da177e4SLinus Torvalds unsigned int end_addr, 2791da177e4SLinus Torvalds emu10k1_pcm_mixer_t *mix) 2801da177e4SLinus Torvalds { 2811da177e4SLinus Torvalds snd_pcm_substream_t *substream = evoice->epcm->substream; 2821da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 2831da177e4SLinus Torvalds unsigned int silent_page, tmp; 2841da177e4SLinus Torvalds int voice, stereo, w_16; 2851da177e4SLinus Torvalds unsigned char attn, send_amount[8]; 2861da177e4SLinus Torvalds unsigned char send_routing[8]; 2871da177e4SLinus Torvalds unsigned long flags; 2881da177e4SLinus Torvalds unsigned int pitch_target; 2891da177e4SLinus Torvalds unsigned int ccis; 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds voice = evoice->number; 2921da177e4SLinus Torvalds stereo = runtime->channels == 2; 2931da177e4SLinus Torvalds w_16 = snd_pcm_format_width(runtime->format) == 16; 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds if (!extra && stereo) { 2961da177e4SLinus Torvalds start_addr >>= 1; 2971da177e4SLinus Torvalds end_addr >>= 1; 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds if (w_16) { 3001da177e4SLinus Torvalds start_addr >>= 1; 3011da177e4SLinus Torvalds end_addr >>= 1; 3021da177e4SLinus Torvalds } 3031da177e4SLinus Torvalds 3041da177e4SLinus Torvalds spin_lock_irqsave(&emu->reg_lock, flags); 3051da177e4SLinus Torvalds 3061da177e4SLinus Torvalds /* volume parameters */ 3071da177e4SLinus Torvalds if (extra) { 3081da177e4SLinus Torvalds attn = 0; 3091da177e4SLinus Torvalds memset(send_routing, 0, sizeof(send_routing)); 3101da177e4SLinus Torvalds send_routing[0] = 0; 3111da177e4SLinus Torvalds send_routing[1] = 1; 3121da177e4SLinus Torvalds send_routing[2] = 2; 3131da177e4SLinus Torvalds send_routing[3] = 3; 3141da177e4SLinus Torvalds memset(send_amount, 0, sizeof(send_amount)); 3151da177e4SLinus Torvalds } else { 3161da177e4SLinus Torvalds /* mono, left, right (master voice = left) */ 3171da177e4SLinus Torvalds tmp = stereo ? (master ? 1 : 2) : 0; 3181da177e4SLinus Torvalds memcpy(send_routing, &mix->send_routing[tmp][0], 8); 3191da177e4SLinus Torvalds memcpy(send_amount, &mix->send_volume[tmp][0], 8); 3201da177e4SLinus Torvalds } 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds ccis = emu10k1_ccis(stereo, w_16); 3231da177e4SLinus Torvalds 3241da177e4SLinus Torvalds if (master) { 3251da177e4SLinus Torvalds evoice->epcm->ccca_start_addr = start_addr + ccis; 3261da177e4SLinus Torvalds if (extra) { 3271da177e4SLinus Torvalds start_addr += ccis; 3281da177e4SLinus Torvalds end_addr += ccis; 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds if (stereo && !extra) { 3311da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK); 3321da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CPF, (voice + 1), CPF_STEREO_MASK); 3331da177e4SLinus Torvalds } else { 3341da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CPF, voice, 0); 3351da177e4SLinus Torvalds } 3361da177e4SLinus Torvalds } 3371da177e4SLinus Torvalds 3381da177e4SLinus Torvalds // setup routing 3391da177e4SLinus Torvalds if (emu->audigy) { 3401da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, A_FXRT1, voice, 3411da177e4SLinus Torvalds snd_emu10k1_compose_audigy_fxrt1(send_routing)); 3421da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, A_FXRT2, voice, 3431da177e4SLinus Torvalds snd_emu10k1_compose_audigy_fxrt2(send_routing)); 3441da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, 3451da177e4SLinus Torvalds ((unsigned int)send_amount[4] << 24) | 3461da177e4SLinus Torvalds ((unsigned int)send_amount[5] << 16) | 3471da177e4SLinus Torvalds ((unsigned int)send_amount[6] << 8) | 3481da177e4SLinus Torvalds (unsigned int)send_amount[7]); 3491da177e4SLinus Torvalds } else 3501da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, FXRT, voice, 3511da177e4SLinus Torvalds snd_emu10k1_compose_send_routing(send_routing)); 3521da177e4SLinus Torvalds // Stop CA 3531da177e4SLinus Torvalds // Assumption that PT is already 0 so no harm overwriting 3541da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]); 3551da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24)); 3561da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24)); 3571da177e4SLinus Torvalds pitch_target = emu10k1_calc_pitch_target(runtime->rate); 3581da177e4SLinus Torvalds if (extra) 3591da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CCCA, voice, start_addr | 3601da177e4SLinus Torvalds emu10k1_select_interprom(pitch_target) | 3611da177e4SLinus Torvalds (w_16 ? 0 : CCCA_8BITSELECT)); 3621da177e4SLinus Torvalds else 3631da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CCCA, voice, (start_addr + ccis) | 3641da177e4SLinus Torvalds emu10k1_select_interprom(pitch_target) | 3651da177e4SLinus Torvalds (w_16 ? 0 : CCCA_8BITSELECT)); 3661da177e4SLinus Torvalds // Clear filter delay memory 3671da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, Z1, voice, 0); 3681da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, Z2, voice, 0); 3691da177e4SLinus Torvalds // invalidate maps 3701da177e4SLinus Torvalds silent_page = ((unsigned int)emu->silent_page.addr << 1) | MAP_PTI_MASK; 3711da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page); 3721da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page); 3731da177e4SLinus Torvalds // modulation envelope 3741da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff); 3751da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff); 3761da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, ATKHLDM, voice, 0); 3771da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, DCYSUSM, voice, 0x007f); 3781da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, LFOVAL1, voice, 0x8000); 3791da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, LFOVAL2, voice, 0x8000); 3801da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, FMMOD, voice, 0); 3811da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, TREMFRQ, voice, 0); 3821da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, FM2FRQ2, voice, 0); 3831da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, ENVVAL, voice, 0x8000); 3841da177e4SLinus Torvalds // volume envelope 3851da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, ATKHLDV, voice, 0x7f7f); 3861da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, ENVVOL, voice, 0x0000); 3871da177e4SLinus Torvalds // filter envelope 3881da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, PEFE_FILTERAMOUNT, voice, 0x7f); 3891da177e4SLinus Torvalds // pitch envelope 3901da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, PEFE_PITCHAMOUNT, voice, 0); 3911da177e4SLinus Torvalds 3921da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->reg_lock, flags); 3931da177e4SLinus Torvalds } 3941da177e4SLinus Torvalds 3951da177e4SLinus Torvalds static int snd_emu10k1_playback_hw_params(snd_pcm_substream_t * substream, 3961da177e4SLinus Torvalds snd_pcm_hw_params_t * hw_params) 3971da177e4SLinus Torvalds { 3981da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 3991da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 4001da177e4SLinus Torvalds emu10k1_pcm_t *epcm = runtime->private_data; 4011da177e4SLinus Torvalds int err; 4021da177e4SLinus Torvalds 4031da177e4SLinus Torvalds if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0) 4041da177e4SLinus Torvalds return err; 4051da177e4SLinus Torvalds if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) 4061da177e4SLinus Torvalds return err; 4071da177e4SLinus Torvalds if (err > 0) { /* change */ 408*e017fa57STakashi Iwai int mapped; 4091da177e4SLinus Torvalds if (epcm->memblk != NULL) 4101da177e4SLinus Torvalds snd_emu10k1_free_pages(emu, epcm->memblk); 411*e017fa57STakashi Iwai epcm->memblk = snd_emu10k1_alloc_pages(emu, substream); 4121da177e4SLinus Torvalds epcm->start_addr = 0; 413*e017fa57STakashi Iwai if (! epcm->memblk) 4141da177e4SLinus Torvalds return -ENOMEM; 415*e017fa57STakashi Iwai mapped = ((emu10k1_memblk_t *)epcm->memblk)->mapped_page; 416*e017fa57STakashi Iwai if (mapped < 0) 417*e017fa57STakashi Iwai return -ENOMEM; 418*e017fa57STakashi Iwai epcm->start_addr = mapped << PAGE_SHIFT; 4191da177e4SLinus Torvalds } 4201da177e4SLinus Torvalds return 0; 4211da177e4SLinus Torvalds } 4221da177e4SLinus Torvalds 4231da177e4SLinus Torvalds static int snd_emu10k1_playback_hw_free(snd_pcm_substream_t * substream) 4241da177e4SLinus Torvalds { 4251da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 4261da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 4271da177e4SLinus Torvalds emu10k1_pcm_t *epcm; 4281da177e4SLinus Torvalds 4291da177e4SLinus Torvalds if (runtime->private_data == NULL) 4301da177e4SLinus Torvalds return 0; 4311da177e4SLinus Torvalds epcm = runtime->private_data; 4321da177e4SLinus Torvalds if (epcm->extra) { 4331da177e4SLinus Torvalds snd_emu10k1_voice_free(epcm->emu, epcm->extra); 4341da177e4SLinus Torvalds epcm->extra = NULL; 4351da177e4SLinus Torvalds } 4361da177e4SLinus Torvalds if (epcm->voices[1]) { 4371da177e4SLinus Torvalds snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); 4381da177e4SLinus Torvalds epcm->voices[1] = NULL; 4391da177e4SLinus Torvalds } 4401da177e4SLinus Torvalds if (epcm->voices[0]) { 4411da177e4SLinus Torvalds snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]); 4421da177e4SLinus Torvalds epcm->voices[0] = NULL; 4431da177e4SLinus Torvalds } 4441da177e4SLinus Torvalds if (epcm->memblk) { 4451da177e4SLinus Torvalds snd_emu10k1_free_pages(emu, epcm->memblk); 4461da177e4SLinus Torvalds epcm->memblk = NULL; 4471da177e4SLinus Torvalds epcm->start_addr = 0; 4481da177e4SLinus Torvalds } 4491da177e4SLinus Torvalds snd_pcm_lib_free_pages(substream); 4501da177e4SLinus Torvalds return 0; 4511da177e4SLinus Torvalds } 4521da177e4SLinus Torvalds 4531da177e4SLinus Torvalds static int snd_emu10k1_efx_playback_hw_free(snd_pcm_substream_t * substream) 4541da177e4SLinus Torvalds { 4551da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 4561da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 4571da177e4SLinus Torvalds emu10k1_pcm_t *epcm; 4581da177e4SLinus Torvalds int i; 4591da177e4SLinus Torvalds 4601da177e4SLinus Torvalds if (runtime->private_data == NULL) 4611da177e4SLinus Torvalds return 0; 4621da177e4SLinus Torvalds epcm = runtime->private_data; 4631da177e4SLinus Torvalds if (epcm->extra) { 4641da177e4SLinus Torvalds snd_emu10k1_voice_free(epcm->emu, epcm->extra); 4651da177e4SLinus Torvalds epcm->extra = NULL; 4661da177e4SLinus Torvalds } 4671da177e4SLinus Torvalds for (i=0; i < NUM_EFX_PLAYBACK; i++) { 4681da177e4SLinus Torvalds if (epcm->voices[i]) { 4691da177e4SLinus Torvalds snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]); 4701da177e4SLinus Torvalds epcm->voices[i] = NULL; 4711da177e4SLinus Torvalds } 4721da177e4SLinus Torvalds } 4731da177e4SLinus Torvalds if (epcm->memblk) { 4741da177e4SLinus Torvalds snd_emu10k1_free_pages(emu, epcm->memblk); 4751da177e4SLinus Torvalds epcm->memblk = NULL; 4761da177e4SLinus Torvalds epcm->start_addr = 0; 4771da177e4SLinus Torvalds } 4781da177e4SLinus Torvalds snd_pcm_lib_free_pages(substream); 4791da177e4SLinus Torvalds return 0; 4801da177e4SLinus Torvalds } 4811da177e4SLinus Torvalds 4821da177e4SLinus Torvalds static int snd_emu10k1_playback_prepare(snd_pcm_substream_t * substream) 4831da177e4SLinus Torvalds { 4841da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 4851da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 4861da177e4SLinus Torvalds emu10k1_pcm_t *epcm = runtime->private_data; 4871da177e4SLinus Torvalds unsigned int start_addr, end_addr; 4881da177e4SLinus Torvalds 4891da177e4SLinus Torvalds start_addr = epcm->start_addr; 4901da177e4SLinus Torvalds end_addr = snd_pcm_lib_period_bytes(substream); 4911da177e4SLinus Torvalds if (runtime->channels == 2) { 4921da177e4SLinus Torvalds start_addr >>= 1; 4931da177e4SLinus Torvalds end_addr >>= 1; 4941da177e4SLinus Torvalds } 4951da177e4SLinus Torvalds end_addr += start_addr; 4961da177e4SLinus Torvalds snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra, 4971da177e4SLinus Torvalds start_addr, end_addr, NULL); 4981da177e4SLinus Torvalds start_addr = epcm->start_addr; 4991da177e4SLinus Torvalds end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream); 5001da177e4SLinus Torvalds snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0], 5011da177e4SLinus Torvalds start_addr, end_addr, 5021da177e4SLinus Torvalds &emu->pcm_mixer[substream->number]); 5031da177e4SLinus Torvalds if (epcm->voices[1]) 5041da177e4SLinus Torvalds snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[1], 5051da177e4SLinus Torvalds start_addr, end_addr, 5061da177e4SLinus Torvalds &emu->pcm_mixer[substream->number]); 5071da177e4SLinus Torvalds return 0; 5081da177e4SLinus Torvalds } 5091da177e4SLinus Torvalds 5101da177e4SLinus Torvalds static int snd_emu10k1_efx_playback_prepare(snd_pcm_substream_t * substream) 5111da177e4SLinus Torvalds { 5121da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 5131da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 5141da177e4SLinus Torvalds emu10k1_pcm_t *epcm = runtime->private_data; 5151da177e4SLinus Torvalds unsigned int start_addr, end_addr; 5161da177e4SLinus Torvalds unsigned int channel_size; 5171da177e4SLinus Torvalds int i; 5181da177e4SLinus Torvalds 5191da177e4SLinus Torvalds start_addr = epcm->start_addr; 5201da177e4SLinus Torvalds end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream); 5211da177e4SLinus Torvalds 5221da177e4SLinus Torvalds /* 5231da177e4SLinus Torvalds * the kX driver leaves some space between voices 5241da177e4SLinus Torvalds */ 5251da177e4SLinus Torvalds channel_size = ( end_addr - start_addr ) / NUM_EFX_PLAYBACK; 5261da177e4SLinus Torvalds 5271da177e4SLinus Torvalds snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra, 5281da177e4SLinus Torvalds start_addr, start_addr + (channel_size / 2), NULL); 5291da177e4SLinus Torvalds 5301da177e4SLinus Torvalds /* only difference with the master voice is we use it for the pointer */ 5311da177e4SLinus Torvalds snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0], 5321da177e4SLinus Torvalds start_addr, start_addr + channel_size, 5331da177e4SLinus Torvalds &emu->efx_pcm_mixer[0]); 5341da177e4SLinus Torvalds 5351da177e4SLinus Torvalds start_addr += channel_size; 5361da177e4SLinus Torvalds for (i = 1; i < NUM_EFX_PLAYBACK; i++) { 5371da177e4SLinus Torvalds snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[i], 5381da177e4SLinus Torvalds start_addr, start_addr + channel_size, 5391da177e4SLinus Torvalds &emu->efx_pcm_mixer[i]); 5401da177e4SLinus Torvalds start_addr += channel_size; 5411da177e4SLinus Torvalds } 5421da177e4SLinus Torvalds 5431da177e4SLinus Torvalds return 0; 5441da177e4SLinus Torvalds } 5451da177e4SLinus Torvalds 5461da177e4SLinus Torvalds static snd_pcm_hardware_t snd_emu10k1_efx_playback = 5471da177e4SLinus Torvalds { 5481da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_NONINTERLEAVED | 5491da177e4SLinus Torvalds SNDRV_PCM_INFO_BLOCK_TRANSFER | 5501da177e4SLinus Torvalds SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), 5511da177e4SLinus Torvalds .formats = SNDRV_PCM_FMTBIT_S16_LE, 5521da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_48000, 5531da177e4SLinus Torvalds .rate_min = 48000, 5541da177e4SLinus Torvalds .rate_max = 48000, 5551da177e4SLinus Torvalds .channels_min = NUM_EFX_PLAYBACK, 5561da177e4SLinus Torvalds .channels_max = NUM_EFX_PLAYBACK, 5571da177e4SLinus Torvalds .buffer_bytes_max = (64*1024), 5581da177e4SLinus Torvalds .period_bytes_min = 64, 5591da177e4SLinus Torvalds .period_bytes_max = (64*1024), 5601da177e4SLinus Torvalds .periods_min = 2, 5611da177e4SLinus Torvalds .periods_max = 2, 5621da177e4SLinus Torvalds .fifo_size = 0, 5631da177e4SLinus Torvalds }; 5641da177e4SLinus Torvalds 5651da177e4SLinus Torvalds static int snd_emu10k1_capture_hw_params(snd_pcm_substream_t * substream, 5661da177e4SLinus Torvalds snd_pcm_hw_params_t * hw_params) 5671da177e4SLinus Torvalds { 5681da177e4SLinus Torvalds return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); 5691da177e4SLinus Torvalds } 5701da177e4SLinus Torvalds 5711da177e4SLinus Torvalds static int snd_emu10k1_capture_hw_free(snd_pcm_substream_t * substream) 5721da177e4SLinus Torvalds { 5731da177e4SLinus Torvalds return snd_pcm_lib_free_pages(substream); 5741da177e4SLinus Torvalds } 5751da177e4SLinus Torvalds 5761da177e4SLinus Torvalds static int snd_emu10k1_capture_prepare(snd_pcm_substream_t * substream) 5771da177e4SLinus Torvalds { 5781da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 5791da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 5801da177e4SLinus Torvalds emu10k1_pcm_t *epcm = runtime->private_data; 5811da177e4SLinus Torvalds int idx; 5821da177e4SLinus Torvalds 5831da177e4SLinus Torvalds /* zeroing the buffer size will stop capture */ 5841da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0); 5851da177e4SLinus Torvalds switch (epcm->type) { 5861da177e4SLinus Torvalds case CAPTURE_AC97ADC: 5871da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, ADCCR, 0, 0); 5881da177e4SLinus Torvalds break; 5891da177e4SLinus Torvalds case CAPTURE_EFX: 5901da177e4SLinus Torvalds if (emu->audigy) { 5911da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0); 5921da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0); 5931da177e4SLinus Torvalds } else 5941da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, FXWC, 0, 0); 5951da177e4SLinus Torvalds break; 5961da177e4SLinus Torvalds default: 5971da177e4SLinus Torvalds break; 5981da177e4SLinus Torvalds } 5991da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, epcm->capture_ba_reg, 0, runtime->dma_addr); 6001da177e4SLinus Torvalds epcm->capture_bufsize = snd_pcm_lib_buffer_bytes(substream); 6011da177e4SLinus Torvalds epcm->capture_bs_val = 0; 6021da177e4SLinus Torvalds for (idx = 0; idx < 31; idx++) { 6031da177e4SLinus Torvalds if (capture_period_sizes[idx] == epcm->capture_bufsize) { 6041da177e4SLinus Torvalds epcm->capture_bs_val = idx + 1; 6051da177e4SLinus Torvalds break; 6061da177e4SLinus Torvalds } 6071da177e4SLinus Torvalds } 6081da177e4SLinus Torvalds if (epcm->capture_bs_val == 0) { 6091da177e4SLinus Torvalds snd_BUG(); 6101da177e4SLinus Torvalds epcm->capture_bs_val++; 6111da177e4SLinus Torvalds } 6121da177e4SLinus Torvalds if (epcm->type == CAPTURE_AC97ADC) { 6131da177e4SLinus Torvalds epcm->capture_cr_val = emu->audigy ? A_ADCCR_LCHANENABLE : ADCCR_LCHANENABLE; 6141da177e4SLinus Torvalds if (runtime->channels > 1) 6151da177e4SLinus Torvalds epcm->capture_cr_val |= emu->audigy ? A_ADCCR_RCHANENABLE : ADCCR_RCHANENABLE; 6161da177e4SLinus Torvalds epcm->capture_cr_val |= emu->audigy ? 6171da177e4SLinus Torvalds snd_emu10k1_audigy_capture_rate_reg(runtime->rate) : 6181da177e4SLinus Torvalds snd_emu10k1_capture_rate_reg(runtime->rate); 6191da177e4SLinus Torvalds } 6201da177e4SLinus Torvalds return 0; 6211da177e4SLinus Torvalds } 6221da177e4SLinus Torvalds 6231da177e4SLinus Torvalds static void snd_emu10k1_playback_invalidate_cache(emu10k1_t *emu, int extra, emu10k1_voice_t *evoice) 6241da177e4SLinus Torvalds { 6251da177e4SLinus Torvalds snd_pcm_runtime_t *runtime; 6261da177e4SLinus Torvalds unsigned int voice, stereo, i, ccis, cra = 64, cs, sample; 6271da177e4SLinus Torvalds 6281da177e4SLinus Torvalds if (evoice == NULL) 6291da177e4SLinus Torvalds return; 6301da177e4SLinus Torvalds runtime = evoice->epcm->substream->runtime; 6311da177e4SLinus Torvalds voice = evoice->number; 6321da177e4SLinus Torvalds stereo = (!extra && runtime->channels == 2); 6331da177e4SLinus Torvalds sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080; 6341da177e4SLinus Torvalds ccis = emu10k1_ccis(stereo, sample == 0); 6351da177e4SLinus Torvalds // set cs to 2 * number of cache registers beside the invalidated 6361da177e4SLinus Torvalds cs = (sample == 0) ? (32-ccis) : (64-ccis+1) >> 1; 6371da177e4SLinus Torvalds if (cs > 16) cs = 16; 6381da177e4SLinus Torvalds for (i = 0; i < cs; i++) { 6391da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample); 6401da177e4SLinus Torvalds if (stereo) { 6411da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CD0 + i, voice + 1, sample); 6421da177e4SLinus Torvalds } 6431da177e4SLinus Torvalds } 6441da177e4SLinus Torvalds // reset cache 6451da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0); 6461da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra); 6471da177e4SLinus Torvalds if (stereo) { 6481da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0); 6491da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra); 6501da177e4SLinus Torvalds } 6511da177e4SLinus Torvalds // fill cache 6521da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis); 6531da177e4SLinus Torvalds if (stereo) { 6541da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice+1, ccis); 6551da177e4SLinus Torvalds } 6561da177e4SLinus Torvalds } 6571da177e4SLinus Torvalds 6581da177e4SLinus Torvalds static void snd_emu10k1_playback_prepare_voice(emu10k1_t *emu, emu10k1_voice_t *evoice, 6591da177e4SLinus Torvalds int master, int extra, 6601da177e4SLinus Torvalds emu10k1_pcm_mixer_t *mix) 6611da177e4SLinus Torvalds { 6621da177e4SLinus Torvalds snd_pcm_substream_t *substream; 6631da177e4SLinus Torvalds snd_pcm_runtime_t *runtime; 6641da177e4SLinus Torvalds unsigned int attn, vattn; 6651da177e4SLinus Torvalds unsigned int voice, tmp; 6661da177e4SLinus Torvalds 6671da177e4SLinus Torvalds if (evoice == NULL) /* skip second voice for mono */ 6681da177e4SLinus Torvalds return; 6691da177e4SLinus Torvalds substream = evoice->epcm->substream; 6701da177e4SLinus Torvalds runtime = substream->runtime; 6711da177e4SLinus Torvalds voice = evoice->number; 6721da177e4SLinus Torvalds 6731da177e4SLinus Torvalds attn = extra ? 0 : 0x00ff; 6741da177e4SLinus Torvalds tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0; 6751da177e4SLinus Torvalds vattn = mix != NULL ? (mix->attn[tmp] << 16) : 0; 6761da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, IFATN, voice, attn); 6771da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, VTFT, voice, vattn | 0xffff); 6781da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CVCF, voice, vattn | 0xffff); 6791da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f); 6801da177e4SLinus Torvalds snd_emu10k1_voice_clear_loop_stop(emu, voice); 6811da177e4SLinus Torvalds } 6821da177e4SLinus Torvalds 6831da177e4SLinus Torvalds static void snd_emu10k1_playback_trigger_voice(emu10k1_t *emu, emu10k1_voice_t *evoice, int master, int extra) 6841da177e4SLinus Torvalds { 6851da177e4SLinus Torvalds snd_pcm_substream_t *substream; 6861da177e4SLinus Torvalds snd_pcm_runtime_t *runtime; 6871da177e4SLinus Torvalds unsigned int voice, pitch, pitch_target; 6881da177e4SLinus Torvalds 6891da177e4SLinus Torvalds if (evoice == NULL) /* skip second voice for mono */ 6901da177e4SLinus Torvalds return; 6911da177e4SLinus Torvalds substream = evoice->epcm->substream; 6921da177e4SLinus Torvalds runtime = substream->runtime; 6931da177e4SLinus Torvalds voice = evoice->number; 6941da177e4SLinus Torvalds 6951da177e4SLinus Torvalds pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8; 6961da177e4SLinus Torvalds pitch_target = emu10k1_calc_pitch_target(runtime->rate); 6971da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target); 6981da177e4SLinus Torvalds if (master || evoice->epcm->type == PLAYBACK_EFX) 6991da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target); 7001da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, IP, voice, pitch); 7011da177e4SLinus Torvalds if (extra) 7021da177e4SLinus Torvalds snd_emu10k1_voice_intr_enable(emu, voice); 7031da177e4SLinus Torvalds } 7041da177e4SLinus Torvalds 7051da177e4SLinus Torvalds static void snd_emu10k1_playback_stop_voice(emu10k1_t *emu, emu10k1_voice_t *evoice) 7061da177e4SLinus Torvalds { 7071da177e4SLinus Torvalds unsigned int voice; 7081da177e4SLinus Torvalds 7091da177e4SLinus Torvalds if (evoice == NULL) 7101da177e4SLinus Torvalds return; 7111da177e4SLinus Torvalds voice = evoice->number; 7121da177e4SLinus Torvalds snd_emu10k1_voice_intr_disable(emu, voice); 7131da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, 0); 7141da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, 0); 7151da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, IFATN, voice, 0xffff); 7161da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff); 7171da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff); 7181da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, IP, voice, 0); 7191da177e4SLinus Torvalds } 7201da177e4SLinus Torvalds 7211da177e4SLinus Torvalds static int snd_emu10k1_playback_trigger(snd_pcm_substream_t * substream, 7221da177e4SLinus Torvalds int cmd) 7231da177e4SLinus Torvalds { 7241da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 7251da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 7261da177e4SLinus Torvalds emu10k1_pcm_t *epcm = runtime->private_data; 7271da177e4SLinus Torvalds emu10k1_pcm_mixer_t *mix; 7281da177e4SLinus Torvalds int result = 0; 7291da177e4SLinus Torvalds 7301da177e4SLinus Torvalds // printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream)); 7311da177e4SLinus Torvalds spin_lock(&emu->reg_lock); 7321da177e4SLinus Torvalds switch (cmd) { 7331da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_START: 7341da177e4SLinus Torvalds snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra); /* do we need this? */ 7351da177e4SLinus Torvalds snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[0]); 7361da177e4SLinus Torvalds /* follow thru */ 7371da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 7381da177e4SLinus Torvalds mix = &emu->pcm_mixer[substream->number]; 7391da177e4SLinus Torvalds snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 1, 0, mix); 7401da177e4SLinus Torvalds snd_emu10k1_playback_prepare_voice(emu, epcm->voices[1], 0, 0, mix); 7411da177e4SLinus Torvalds snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL); 7421da177e4SLinus Torvalds snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 1, 0); 7431da177e4SLinus Torvalds snd_emu10k1_playback_trigger_voice(emu, epcm->voices[1], 0, 0); 7441da177e4SLinus Torvalds snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1); 7451da177e4SLinus Torvalds epcm->running = 1; 7461da177e4SLinus Torvalds break; 7471da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_STOP: 7481da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 7491da177e4SLinus Torvalds epcm->running = 0; 7501da177e4SLinus Torvalds snd_emu10k1_playback_stop_voice(emu, epcm->voices[0]); 7511da177e4SLinus Torvalds snd_emu10k1_playback_stop_voice(emu, epcm->voices[1]); 7521da177e4SLinus Torvalds snd_emu10k1_playback_stop_voice(emu, epcm->extra); 7531da177e4SLinus Torvalds break; 7541da177e4SLinus Torvalds default: 7551da177e4SLinus Torvalds result = -EINVAL; 7561da177e4SLinus Torvalds break; 7571da177e4SLinus Torvalds } 7581da177e4SLinus Torvalds spin_unlock(&emu->reg_lock); 7591da177e4SLinus Torvalds return result; 7601da177e4SLinus Torvalds } 7611da177e4SLinus Torvalds 7621da177e4SLinus Torvalds static int snd_emu10k1_capture_trigger(snd_pcm_substream_t * substream, 7631da177e4SLinus Torvalds int cmd) 7641da177e4SLinus Torvalds { 7651da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 7661da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 7671da177e4SLinus Torvalds emu10k1_pcm_t *epcm = runtime->private_data; 7681da177e4SLinus Torvalds int result = 0; 7691da177e4SLinus Torvalds 7701da177e4SLinus Torvalds spin_lock(&emu->reg_lock); 7711da177e4SLinus Torvalds switch (cmd) { 7721da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_START: 7731da177e4SLinus Torvalds // hmm this should cause full and half full interrupt to be raised? 7741da177e4SLinus Torvalds outl(epcm->capture_ipr, emu->port + IPR); 7751da177e4SLinus Torvalds snd_emu10k1_intr_enable(emu, epcm->capture_inte); 7761da177e4SLinus Torvalds // printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs); 7771da177e4SLinus Torvalds switch (epcm->type) { 7781da177e4SLinus Torvalds case CAPTURE_AC97ADC: 7791da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, ADCCR, 0, epcm->capture_cr_val); 7801da177e4SLinus Torvalds break; 7811da177e4SLinus Torvalds case CAPTURE_EFX: 7821da177e4SLinus Torvalds if (emu->audigy) { 7831da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, A_FXWC1, 0, epcm->capture_cr_val); 7841da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, A_FXWC2, 0, epcm->capture_cr_val2); 7851da177e4SLinus Torvalds } else 7861da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val); 7871da177e4SLinus Torvalds break; 7881da177e4SLinus Torvalds default: 7891da177e4SLinus Torvalds break; 7901da177e4SLinus Torvalds } 7911da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, epcm->capture_bs_val); 7921da177e4SLinus Torvalds epcm->running = 1; 7931da177e4SLinus Torvalds epcm->first_ptr = 1; 7941da177e4SLinus Torvalds break; 7951da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_STOP: 7961da177e4SLinus Torvalds epcm->running = 0; 7971da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, epcm->capture_inte); 7981da177e4SLinus Torvalds outl(epcm->capture_ipr, emu->port + IPR); 7991da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0); 8001da177e4SLinus Torvalds switch (epcm->type) { 8011da177e4SLinus Torvalds case CAPTURE_AC97ADC: 8021da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, ADCCR, 0, 0); 8031da177e4SLinus Torvalds break; 8041da177e4SLinus Torvalds case CAPTURE_EFX: 8051da177e4SLinus Torvalds if (emu->audigy) { 8061da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0); 8071da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0); 8081da177e4SLinus Torvalds } else 8091da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, FXWC, 0, 0); 8101da177e4SLinus Torvalds break; 8111da177e4SLinus Torvalds default: 8121da177e4SLinus Torvalds break; 8131da177e4SLinus Torvalds } 8141da177e4SLinus Torvalds break; 8151da177e4SLinus Torvalds default: 8161da177e4SLinus Torvalds result = -EINVAL; 8171da177e4SLinus Torvalds } 8181da177e4SLinus Torvalds spin_unlock(&emu->reg_lock); 8191da177e4SLinus Torvalds return result; 8201da177e4SLinus Torvalds } 8211da177e4SLinus Torvalds 8221da177e4SLinus Torvalds static snd_pcm_uframes_t snd_emu10k1_playback_pointer(snd_pcm_substream_t * substream) 8231da177e4SLinus Torvalds { 8241da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 8251da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 8261da177e4SLinus Torvalds emu10k1_pcm_t *epcm = runtime->private_data; 8271da177e4SLinus Torvalds unsigned int ptr; 8281da177e4SLinus Torvalds 8291da177e4SLinus Torvalds if (!epcm->running) 8301da177e4SLinus Torvalds return 0; 8311da177e4SLinus Torvalds ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff; 8321da177e4SLinus Torvalds #if 0 /* Perex's code */ 8331da177e4SLinus Torvalds ptr += runtime->buffer_size; 8341da177e4SLinus Torvalds ptr -= epcm->ccca_start_addr; 8351da177e4SLinus Torvalds ptr %= runtime->buffer_size; 8361da177e4SLinus Torvalds #else /* EMU10K1 Open Source code from Creative */ 8371da177e4SLinus Torvalds if (ptr < epcm->ccca_start_addr) 8381da177e4SLinus Torvalds ptr += runtime->buffer_size - epcm->ccca_start_addr; 8391da177e4SLinus Torvalds else { 8401da177e4SLinus Torvalds ptr -= epcm->ccca_start_addr; 8411da177e4SLinus Torvalds if (ptr >= runtime->buffer_size) 8421da177e4SLinus Torvalds ptr -= runtime->buffer_size; 8431da177e4SLinus Torvalds } 8441da177e4SLinus Torvalds #endif 8451da177e4SLinus Torvalds // printk("ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n", ptr, runtime->buffer_size, runtime->period_size); 8461da177e4SLinus Torvalds return ptr; 8471da177e4SLinus Torvalds } 8481da177e4SLinus Torvalds 8491da177e4SLinus Torvalds 8501da177e4SLinus Torvalds static int snd_emu10k1_efx_playback_trigger(snd_pcm_substream_t * substream, 8511da177e4SLinus Torvalds int cmd) 8521da177e4SLinus Torvalds { 8531da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 8541da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 8551da177e4SLinus Torvalds emu10k1_pcm_t *epcm = runtime->private_data; 8561da177e4SLinus Torvalds int i; 8571da177e4SLinus Torvalds int result = 0; 8581da177e4SLinus Torvalds 8591da177e4SLinus Torvalds spin_lock(&emu->reg_lock); 8601da177e4SLinus Torvalds switch (cmd) { 8611da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_START: 8621da177e4SLinus Torvalds // prepare voices 8631da177e4SLinus Torvalds for (i = 0; i < NUM_EFX_PLAYBACK; i++) { 8641da177e4SLinus Torvalds snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[i]); 8651da177e4SLinus Torvalds } 8661da177e4SLinus Torvalds snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra); 8671da177e4SLinus Torvalds 8681da177e4SLinus Torvalds /* follow thru */ 8691da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 8701da177e4SLinus Torvalds snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL); 8711da177e4SLinus Torvalds snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 0, 0, 8721da177e4SLinus Torvalds &emu->efx_pcm_mixer[0]); 8731da177e4SLinus Torvalds for (i = 1; i < NUM_EFX_PLAYBACK; i++) 8741da177e4SLinus Torvalds snd_emu10k1_playback_prepare_voice(emu, epcm->voices[i], 0, 0, 8751da177e4SLinus Torvalds &emu->efx_pcm_mixer[i]); 8761da177e4SLinus Torvalds snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 0, 0); 8771da177e4SLinus Torvalds snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1); 8781da177e4SLinus Torvalds for (i = 1; i < NUM_EFX_PLAYBACK; i++) 8791da177e4SLinus Torvalds snd_emu10k1_playback_trigger_voice(emu, epcm->voices[i], 0, 0); 8801da177e4SLinus Torvalds epcm->running = 1; 8811da177e4SLinus Torvalds break; 8821da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_STOP: 8831da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 8841da177e4SLinus Torvalds epcm->running = 0; 8851da177e4SLinus Torvalds for (i = 0; i < NUM_EFX_PLAYBACK; i++) { 8861da177e4SLinus Torvalds snd_emu10k1_playback_stop_voice(emu, epcm->voices[i]); 8871da177e4SLinus Torvalds } 8881da177e4SLinus Torvalds snd_emu10k1_playback_stop_voice(emu, epcm->extra); 8891da177e4SLinus Torvalds break; 8901da177e4SLinus Torvalds default: 8911da177e4SLinus Torvalds result = -EINVAL; 8921da177e4SLinus Torvalds break; 8931da177e4SLinus Torvalds } 8941da177e4SLinus Torvalds spin_unlock(&emu->reg_lock); 8951da177e4SLinus Torvalds return result; 8961da177e4SLinus Torvalds } 8971da177e4SLinus Torvalds 8981da177e4SLinus Torvalds 8991da177e4SLinus Torvalds static snd_pcm_uframes_t snd_emu10k1_capture_pointer(snd_pcm_substream_t * substream) 9001da177e4SLinus Torvalds { 9011da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 9021da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 9031da177e4SLinus Torvalds emu10k1_pcm_t *epcm = runtime->private_data; 9041da177e4SLinus Torvalds unsigned int ptr; 9051da177e4SLinus Torvalds 9061da177e4SLinus Torvalds if (!epcm->running) 9071da177e4SLinus Torvalds return 0; 9081da177e4SLinus Torvalds if (epcm->first_ptr) { 9091da177e4SLinus Torvalds udelay(50); // hack, it takes awhile until capture is started 9101da177e4SLinus Torvalds epcm->first_ptr = 0; 9111da177e4SLinus Torvalds } 9121da177e4SLinus Torvalds ptr = snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff; 9131da177e4SLinus Torvalds return bytes_to_frames(runtime, ptr); 9141da177e4SLinus Torvalds } 9151da177e4SLinus Torvalds 9161da177e4SLinus Torvalds /* 9171da177e4SLinus Torvalds * Playback support device description 9181da177e4SLinus Torvalds */ 9191da177e4SLinus Torvalds 9201da177e4SLinus Torvalds static snd_pcm_hardware_t snd_emu10k1_playback = 9211da177e4SLinus Torvalds { 9221da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 9231da177e4SLinus Torvalds SNDRV_PCM_INFO_BLOCK_TRANSFER | 9241da177e4SLinus Torvalds SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), 9251da177e4SLinus Torvalds .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 9261da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_96000, 9271da177e4SLinus Torvalds .rate_min = 4000, 9281da177e4SLinus Torvalds .rate_max = 96000, 9291da177e4SLinus Torvalds .channels_min = 1, 9301da177e4SLinus Torvalds .channels_max = 2, 9311da177e4SLinus Torvalds .buffer_bytes_max = (128*1024), 9321da177e4SLinus Torvalds .period_bytes_min = 64, 9331da177e4SLinus Torvalds .period_bytes_max = (128*1024), 9341da177e4SLinus Torvalds .periods_min = 1, 9351da177e4SLinus Torvalds .periods_max = 1024, 9361da177e4SLinus Torvalds .fifo_size = 0, 9371da177e4SLinus Torvalds }; 9381da177e4SLinus Torvalds 9391da177e4SLinus Torvalds /* 9401da177e4SLinus Torvalds * Capture support device description 9411da177e4SLinus Torvalds */ 9421da177e4SLinus Torvalds 9431da177e4SLinus Torvalds static snd_pcm_hardware_t snd_emu10k1_capture = 9441da177e4SLinus Torvalds { 9451da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 9461da177e4SLinus Torvalds SNDRV_PCM_INFO_BLOCK_TRANSFER | 9471da177e4SLinus Torvalds SNDRV_PCM_INFO_MMAP_VALID), 9481da177e4SLinus Torvalds .formats = SNDRV_PCM_FMTBIT_S16_LE, 9491da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_8000_48000, 9501da177e4SLinus Torvalds .rate_min = 8000, 9511da177e4SLinus Torvalds .rate_max = 48000, 9521da177e4SLinus Torvalds .channels_min = 1, 9531da177e4SLinus Torvalds .channels_max = 2, 9541da177e4SLinus Torvalds .buffer_bytes_max = (64*1024), 9551da177e4SLinus Torvalds .period_bytes_min = 384, 9561da177e4SLinus Torvalds .period_bytes_max = (64*1024), 9571da177e4SLinus Torvalds .periods_min = 2, 9581da177e4SLinus Torvalds .periods_max = 2, 9591da177e4SLinus Torvalds .fifo_size = 0, 9601da177e4SLinus Torvalds }; 9611da177e4SLinus Torvalds 9621da177e4SLinus Torvalds /* 9631da177e4SLinus Torvalds * 9641da177e4SLinus Torvalds */ 9651da177e4SLinus Torvalds 9661da177e4SLinus Torvalds static void snd_emu10k1_pcm_mixer_notify1(emu10k1_t *emu, snd_kcontrol_t *kctl, int idx, int activate) 9671da177e4SLinus Torvalds { 9681da177e4SLinus Torvalds snd_ctl_elem_id_t id; 9691da177e4SLinus Torvalds 9707c22f1aaSTakashi Iwai if (! kctl) 9717c22f1aaSTakashi Iwai return; 9721da177e4SLinus Torvalds if (activate) 9731da177e4SLinus Torvalds kctl->vd[idx].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 9741da177e4SLinus Torvalds else 9751da177e4SLinus Torvalds kctl->vd[idx].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; 9761da177e4SLinus Torvalds snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE | 9771da177e4SLinus Torvalds SNDRV_CTL_EVENT_MASK_INFO, 9781da177e4SLinus Torvalds snd_ctl_build_ioff(&id, kctl, idx)); 9791da177e4SLinus Torvalds } 9801da177e4SLinus Torvalds 9811da177e4SLinus Torvalds static void snd_emu10k1_pcm_mixer_notify(emu10k1_t *emu, int idx, int activate) 9821da177e4SLinus Torvalds { 9831da177e4SLinus Torvalds snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_send_routing, idx, activate); 9841da177e4SLinus Torvalds snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_send_volume, idx, activate); 9851da177e4SLinus Torvalds snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_attn, idx, activate); 9861da177e4SLinus Torvalds } 9871da177e4SLinus Torvalds 9881da177e4SLinus Torvalds static void snd_emu10k1_pcm_efx_mixer_notify(emu10k1_t *emu, int idx, int activate) 9891da177e4SLinus Torvalds { 9901da177e4SLinus Torvalds snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_send_routing, idx, activate); 9911da177e4SLinus Torvalds snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_send_volume, idx, activate); 9921da177e4SLinus Torvalds snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_attn, idx, activate); 9931da177e4SLinus Torvalds } 9941da177e4SLinus Torvalds 9951da177e4SLinus Torvalds static void snd_emu10k1_pcm_free_substream(snd_pcm_runtime_t *runtime) 9961da177e4SLinus Torvalds { 9974d572776SJesper Juhl kfree(runtime->private_data); 9981da177e4SLinus Torvalds } 9991da177e4SLinus Torvalds 10001da177e4SLinus Torvalds static int snd_emu10k1_efx_playback_close(snd_pcm_substream_t * substream) 10011da177e4SLinus Torvalds { 10021da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 10031da177e4SLinus Torvalds emu10k1_pcm_mixer_t *mix; 10041da177e4SLinus Torvalds int i; 10051da177e4SLinus Torvalds 10061da177e4SLinus Torvalds for (i=0; i < NUM_EFX_PLAYBACK; i++) { 10071da177e4SLinus Torvalds mix = &emu->efx_pcm_mixer[i]; 10081da177e4SLinus Torvalds mix->epcm = NULL; 10091da177e4SLinus Torvalds snd_emu10k1_pcm_efx_mixer_notify(emu, i, 0); 10101da177e4SLinus Torvalds } 10111da177e4SLinus Torvalds return 0; 10121da177e4SLinus Torvalds } 10131da177e4SLinus Torvalds 10141da177e4SLinus Torvalds static int snd_emu10k1_efx_playback_open(snd_pcm_substream_t * substream) 10151da177e4SLinus Torvalds { 10161da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 10171da177e4SLinus Torvalds emu10k1_pcm_t *epcm; 10181da177e4SLinus Torvalds emu10k1_pcm_mixer_t *mix; 10191da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 10201da177e4SLinus Torvalds int i; 10211da177e4SLinus Torvalds 1022e560d8d8STakashi Iwai epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 10231da177e4SLinus Torvalds if (epcm == NULL) 10241da177e4SLinus Torvalds return -ENOMEM; 10251da177e4SLinus Torvalds epcm->emu = emu; 10261da177e4SLinus Torvalds epcm->type = PLAYBACK_EFX; 10271da177e4SLinus Torvalds epcm->substream = substream; 10281da177e4SLinus Torvalds 10291da177e4SLinus Torvalds emu->pcm_playback_efx_substream = substream; 10301da177e4SLinus Torvalds 10311da177e4SLinus Torvalds runtime->private_data = epcm; 10321da177e4SLinus Torvalds runtime->private_free = snd_emu10k1_pcm_free_substream; 10331da177e4SLinus Torvalds runtime->hw = snd_emu10k1_efx_playback; 10341da177e4SLinus Torvalds 10351da177e4SLinus Torvalds for (i=0; i < NUM_EFX_PLAYBACK; i++) { 10361da177e4SLinus Torvalds mix = &emu->efx_pcm_mixer[i]; 10371da177e4SLinus Torvalds mix->send_routing[0][0] = i; 10381da177e4SLinus Torvalds memset(&mix->send_volume, 0, sizeof(mix->send_volume)); 10391da177e4SLinus Torvalds mix->send_volume[0][0] = 255; 10401da177e4SLinus Torvalds mix->attn[0] = 0xffff; 10411da177e4SLinus Torvalds mix->epcm = epcm; 10421da177e4SLinus Torvalds snd_emu10k1_pcm_efx_mixer_notify(emu, i, 1); 10431da177e4SLinus Torvalds } 10441da177e4SLinus Torvalds return 0; 10451da177e4SLinus Torvalds } 10461da177e4SLinus Torvalds 10471da177e4SLinus Torvalds static int snd_emu10k1_playback_open(snd_pcm_substream_t * substream) 10481da177e4SLinus Torvalds { 10491da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 10501da177e4SLinus Torvalds emu10k1_pcm_t *epcm; 10511da177e4SLinus Torvalds emu10k1_pcm_mixer_t *mix; 10521da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 10531da177e4SLinus Torvalds int i, err; 10541da177e4SLinus Torvalds 1055e560d8d8STakashi Iwai epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 10561da177e4SLinus Torvalds if (epcm == NULL) 10571da177e4SLinus Torvalds return -ENOMEM; 10581da177e4SLinus Torvalds epcm->emu = emu; 10591da177e4SLinus Torvalds epcm->type = PLAYBACK_EMUVOICE; 10601da177e4SLinus Torvalds epcm->substream = substream; 10611da177e4SLinus Torvalds runtime->private_data = epcm; 10621da177e4SLinus Torvalds runtime->private_free = snd_emu10k1_pcm_free_substream; 10631da177e4SLinus Torvalds runtime->hw = snd_emu10k1_playback; 10641da177e4SLinus Torvalds if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) { 10651da177e4SLinus Torvalds kfree(epcm); 10661da177e4SLinus Torvalds return err; 10671da177e4SLinus Torvalds } 10681da177e4SLinus Torvalds if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) { 10691da177e4SLinus Torvalds kfree(epcm); 10701da177e4SLinus Torvalds return err; 10711da177e4SLinus Torvalds } 10721da177e4SLinus Torvalds mix = &emu->pcm_mixer[substream->number]; 10731da177e4SLinus Torvalds for (i = 0; i < 4; i++) 10741da177e4SLinus Torvalds mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i; 10751da177e4SLinus Torvalds memset(&mix->send_volume, 0, sizeof(mix->send_volume)); 10761da177e4SLinus Torvalds mix->send_volume[0][0] = mix->send_volume[0][1] = 10771da177e4SLinus Torvalds mix->send_volume[1][0] = mix->send_volume[2][1] = 255; 10781da177e4SLinus Torvalds mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff; 10791da177e4SLinus Torvalds mix->epcm = epcm; 10801da177e4SLinus Torvalds snd_emu10k1_pcm_mixer_notify(emu, substream->number, 1); 10811da177e4SLinus Torvalds return 0; 10821da177e4SLinus Torvalds } 10831da177e4SLinus Torvalds 10841da177e4SLinus Torvalds static int snd_emu10k1_playback_close(snd_pcm_substream_t * substream) 10851da177e4SLinus Torvalds { 10861da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 10871da177e4SLinus Torvalds emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[substream->number]; 10881da177e4SLinus Torvalds 10891da177e4SLinus Torvalds mix->epcm = NULL; 10901da177e4SLinus Torvalds snd_emu10k1_pcm_mixer_notify(emu, substream->number, 0); 10911da177e4SLinus Torvalds return 0; 10921da177e4SLinus Torvalds } 10931da177e4SLinus Torvalds 10941da177e4SLinus Torvalds static int snd_emu10k1_capture_open(snd_pcm_substream_t * substream) 10951da177e4SLinus Torvalds { 10961da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 10971da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 10981da177e4SLinus Torvalds emu10k1_pcm_t *epcm; 10991da177e4SLinus Torvalds 1100e560d8d8STakashi Iwai epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 11011da177e4SLinus Torvalds if (epcm == NULL) 11021da177e4SLinus Torvalds return -ENOMEM; 11031da177e4SLinus Torvalds epcm->emu = emu; 11041da177e4SLinus Torvalds epcm->type = CAPTURE_AC97ADC; 11051da177e4SLinus Torvalds epcm->substream = substream; 11061da177e4SLinus Torvalds epcm->capture_ipr = IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL; 11071da177e4SLinus Torvalds epcm->capture_inte = INTE_ADCBUFENABLE; 11081da177e4SLinus Torvalds epcm->capture_ba_reg = ADCBA; 11091da177e4SLinus Torvalds epcm->capture_bs_reg = ADCBS; 11101da177e4SLinus Torvalds epcm->capture_idx_reg = emu->audigy ? A_ADCIDX : ADCIDX; 11111da177e4SLinus Torvalds runtime->private_data = epcm; 11121da177e4SLinus Torvalds runtime->private_free = snd_emu10k1_pcm_free_substream; 11131da177e4SLinus Torvalds runtime->hw = snd_emu10k1_capture; 11141da177e4SLinus Torvalds emu->capture_interrupt = snd_emu10k1_pcm_ac97adc_interrupt; 11151da177e4SLinus Torvalds emu->pcm_capture_substream = substream; 11161da177e4SLinus Torvalds snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes); 11171da177e4SLinus Torvalds snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_capture_rates); 11181da177e4SLinus Torvalds return 0; 11191da177e4SLinus Torvalds } 11201da177e4SLinus Torvalds 11211da177e4SLinus Torvalds static int snd_emu10k1_capture_close(snd_pcm_substream_t * substream) 11221da177e4SLinus Torvalds { 11231da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 11241da177e4SLinus Torvalds 11251da177e4SLinus Torvalds emu->capture_interrupt = NULL; 11261da177e4SLinus Torvalds emu->pcm_capture_substream = NULL; 11271da177e4SLinus Torvalds return 0; 11281da177e4SLinus Torvalds } 11291da177e4SLinus Torvalds 11301da177e4SLinus Torvalds static int snd_emu10k1_capture_mic_open(snd_pcm_substream_t * substream) 11311da177e4SLinus Torvalds { 11321da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 11331da177e4SLinus Torvalds emu10k1_pcm_t *epcm; 11341da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 11351da177e4SLinus Torvalds 1136e560d8d8STakashi Iwai epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 11371da177e4SLinus Torvalds if (epcm == NULL) 11381da177e4SLinus Torvalds return -ENOMEM; 11391da177e4SLinus Torvalds epcm->emu = emu; 11401da177e4SLinus Torvalds epcm->type = CAPTURE_AC97MIC; 11411da177e4SLinus Torvalds epcm->substream = substream; 11421da177e4SLinus Torvalds epcm->capture_ipr = IPR_MICBUFFULL|IPR_MICBUFHALFFULL; 11431da177e4SLinus Torvalds epcm->capture_inte = INTE_MICBUFENABLE; 11441da177e4SLinus Torvalds epcm->capture_ba_reg = MICBA; 11451da177e4SLinus Torvalds epcm->capture_bs_reg = MICBS; 11461da177e4SLinus Torvalds epcm->capture_idx_reg = emu->audigy ? A_MICIDX : MICIDX; 11471da177e4SLinus Torvalds substream->runtime->private_data = epcm; 11481da177e4SLinus Torvalds substream->runtime->private_free = snd_emu10k1_pcm_free_substream; 11491da177e4SLinus Torvalds runtime->hw = snd_emu10k1_capture; 11501da177e4SLinus Torvalds runtime->hw.rates = SNDRV_PCM_RATE_8000; 11511da177e4SLinus Torvalds runtime->hw.rate_min = runtime->hw.rate_max = 8000; 11521da177e4SLinus Torvalds runtime->hw.channels_min = 1; 11531da177e4SLinus Torvalds emu->capture_mic_interrupt = snd_emu10k1_pcm_ac97mic_interrupt; 11541da177e4SLinus Torvalds emu->pcm_capture_mic_substream = substream; 11551da177e4SLinus Torvalds snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes); 11561da177e4SLinus Torvalds return 0; 11571da177e4SLinus Torvalds } 11581da177e4SLinus Torvalds 11591da177e4SLinus Torvalds static int snd_emu10k1_capture_mic_close(snd_pcm_substream_t * substream) 11601da177e4SLinus Torvalds { 11611da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 11621da177e4SLinus Torvalds 11631da177e4SLinus Torvalds emu->capture_interrupt = NULL; 11641da177e4SLinus Torvalds emu->pcm_capture_mic_substream = NULL; 11651da177e4SLinus Torvalds return 0; 11661da177e4SLinus Torvalds } 11671da177e4SLinus Torvalds 11681da177e4SLinus Torvalds static int snd_emu10k1_capture_efx_open(snd_pcm_substream_t * substream) 11691da177e4SLinus Torvalds { 11701da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 11711da177e4SLinus Torvalds emu10k1_pcm_t *epcm; 11721da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 11731da177e4SLinus Torvalds int nefx = emu->audigy ? 64 : 32; 11741da177e4SLinus Torvalds int idx; 11751da177e4SLinus Torvalds 1176e560d8d8STakashi Iwai epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 11771da177e4SLinus Torvalds if (epcm == NULL) 11781da177e4SLinus Torvalds return -ENOMEM; 11791da177e4SLinus Torvalds epcm->emu = emu; 11801da177e4SLinus Torvalds epcm->type = CAPTURE_EFX; 11811da177e4SLinus Torvalds epcm->substream = substream; 11821da177e4SLinus Torvalds epcm->capture_ipr = IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL; 11831da177e4SLinus Torvalds epcm->capture_inte = INTE_EFXBUFENABLE; 11841da177e4SLinus Torvalds epcm->capture_ba_reg = FXBA; 11851da177e4SLinus Torvalds epcm->capture_bs_reg = FXBS; 11861da177e4SLinus Torvalds epcm->capture_idx_reg = FXIDX; 11871da177e4SLinus Torvalds substream->runtime->private_data = epcm; 11881da177e4SLinus Torvalds substream->runtime->private_free = snd_emu10k1_pcm_free_substream; 11891da177e4SLinus Torvalds runtime->hw = snd_emu10k1_capture; 11901da177e4SLinus Torvalds runtime->hw.rates = SNDRV_PCM_RATE_48000; 11911da177e4SLinus Torvalds runtime->hw.rate_min = runtime->hw.rate_max = 48000; 11921da177e4SLinus Torvalds spin_lock_irq(&emu->reg_lock); 11931da177e4SLinus Torvalds runtime->hw.channels_min = runtime->hw.channels_max = 0; 11941da177e4SLinus Torvalds for (idx = 0; idx < nefx; idx++) { 11951da177e4SLinus Torvalds if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) { 11961da177e4SLinus Torvalds runtime->hw.channels_min++; 11971da177e4SLinus Torvalds runtime->hw.channels_max++; 11981da177e4SLinus Torvalds } 11991da177e4SLinus Torvalds } 12001da177e4SLinus Torvalds epcm->capture_cr_val = emu->efx_voices_mask[0]; 12011da177e4SLinus Torvalds epcm->capture_cr_val2 = emu->efx_voices_mask[1]; 12021da177e4SLinus Torvalds spin_unlock_irq(&emu->reg_lock); 12031da177e4SLinus Torvalds emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt; 12041da177e4SLinus Torvalds emu->pcm_capture_efx_substream = substream; 12051da177e4SLinus Torvalds snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes); 12061da177e4SLinus Torvalds return 0; 12071da177e4SLinus Torvalds } 12081da177e4SLinus Torvalds 12091da177e4SLinus Torvalds static int snd_emu10k1_capture_efx_close(snd_pcm_substream_t * substream) 12101da177e4SLinus Torvalds { 12111da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 12121da177e4SLinus Torvalds 12131da177e4SLinus Torvalds emu->capture_interrupt = NULL; 12141da177e4SLinus Torvalds emu->pcm_capture_efx_substream = NULL; 12151da177e4SLinus Torvalds return 0; 12161da177e4SLinus Torvalds } 12171da177e4SLinus Torvalds 12181da177e4SLinus Torvalds static snd_pcm_ops_t snd_emu10k1_playback_ops = { 12191da177e4SLinus Torvalds .open = snd_emu10k1_playback_open, 12201da177e4SLinus Torvalds .close = snd_emu10k1_playback_close, 12211da177e4SLinus Torvalds .ioctl = snd_pcm_lib_ioctl, 12221da177e4SLinus Torvalds .hw_params = snd_emu10k1_playback_hw_params, 12231da177e4SLinus Torvalds .hw_free = snd_emu10k1_playback_hw_free, 12241da177e4SLinus Torvalds .prepare = snd_emu10k1_playback_prepare, 12251da177e4SLinus Torvalds .trigger = snd_emu10k1_playback_trigger, 12261da177e4SLinus Torvalds .pointer = snd_emu10k1_playback_pointer, 12271da177e4SLinus Torvalds .page = snd_pcm_sgbuf_ops_page, 12281da177e4SLinus Torvalds }; 12291da177e4SLinus Torvalds 12301da177e4SLinus Torvalds static snd_pcm_ops_t snd_emu10k1_capture_ops = { 12311da177e4SLinus Torvalds .open = snd_emu10k1_capture_open, 12321da177e4SLinus Torvalds .close = snd_emu10k1_capture_close, 12331da177e4SLinus Torvalds .ioctl = snd_pcm_lib_ioctl, 12341da177e4SLinus Torvalds .hw_params = snd_emu10k1_capture_hw_params, 12351da177e4SLinus Torvalds .hw_free = snd_emu10k1_capture_hw_free, 12361da177e4SLinus Torvalds .prepare = snd_emu10k1_capture_prepare, 12371da177e4SLinus Torvalds .trigger = snd_emu10k1_capture_trigger, 12381da177e4SLinus Torvalds .pointer = snd_emu10k1_capture_pointer, 12391da177e4SLinus Torvalds }; 12401da177e4SLinus Torvalds 12411da177e4SLinus Torvalds /* EFX playback */ 12421da177e4SLinus Torvalds static snd_pcm_ops_t snd_emu10k1_efx_playback_ops = { 12431da177e4SLinus Torvalds .open = snd_emu10k1_efx_playback_open, 12441da177e4SLinus Torvalds .close = snd_emu10k1_efx_playback_close, 12451da177e4SLinus Torvalds .ioctl = snd_pcm_lib_ioctl, 12461da177e4SLinus Torvalds .hw_params = snd_emu10k1_playback_hw_params, 12471da177e4SLinus Torvalds .hw_free = snd_emu10k1_efx_playback_hw_free, 12481da177e4SLinus Torvalds .prepare = snd_emu10k1_efx_playback_prepare, 12491da177e4SLinus Torvalds .trigger = snd_emu10k1_efx_playback_trigger, 12501da177e4SLinus Torvalds .pointer = snd_emu10k1_efx_playback_pointer, 12511da177e4SLinus Torvalds .page = snd_pcm_sgbuf_ops_page, 12521da177e4SLinus Torvalds }; 12531da177e4SLinus Torvalds 12541da177e4SLinus Torvalds int __devinit snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) 12551da177e4SLinus Torvalds { 12561da177e4SLinus Torvalds snd_pcm_t *pcm; 12571da177e4SLinus Torvalds snd_pcm_substream_t *substream; 12581da177e4SLinus Torvalds int err; 12591da177e4SLinus Torvalds 12601da177e4SLinus Torvalds if (rpcm) 12611da177e4SLinus Torvalds *rpcm = NULL; 12621da177e4SLinus Torvalds 12631da177e4SLinus Torvalds if ((err = snd_pcm_new(emu->card, "emu10k1", device, 32, 1, &pcm)) < 0) 12641da177e4SLinus Torvalds return err; 12651da177e4SLinus Torvalds 12661da177e4SLinus Torvalds pcm->private_data = emu; 12671da177e4SLinus Torvalds 12681da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_playback_ops); 12691da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_ops); 12701da177e4SLinus Torvalds 12711da177e4SLinus Torvalds pcm->info_flags = 0; 12721da177e4SLinus Torvalds pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; 12731da177e4SLinus Torvalds strcpy(pcm->name, "ADC Capture/Standard PCM Playback"); 12741da177e4SLinus Torvalds emu->pcm = pcm; 12751da177e4SLinus Torvalds 12761da177e4SLinus Torvalds for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) 12771da177e4SLinus Torvalds if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0) 12781da177e4SLinus Torvalds return err; 12791da177e4SLinus Torvalds 12801da177e4SLinus Torvalds for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) 12811da177e4SLinus Torvalds snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); 12821da177e4SLinus Torvalds 12831da177e4SLinus Torvalds if (rpcm) 12841da177e4SLinus Torvalds *rpcm = pcm; 12851da177e4SLinus Torvalds 12861da177e4SLinus Torvalds return 0; 12871da177e4SLinus Torvalds } 12881da177e4SLinus Torvalds 12891da177e4SLinus Torvalds int __devinit snd_emu10k1_pcm_multi(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) 12901da177e4SLinus Torvalds { 12911da177e4SLinus Torvalds snd_pcm_t *pcm; 12921da177e4SLinus Torvalds snd_pcm_substream_t *substream; 12931da177e4SLinus Torvalds int err; 12941da177e4SLinus Torvalds 12951da177e4SLinus Torvalds if (rpcm) 12961da177e4SLinus Torvalds *rpcm = NULL; 12971da177e4SLinus Torvalds 12981da177e4SLinus Torvalds if ((err = snd_pcm_new(emu->card, "emu10k1", device, 1, 0, &pcm)) < 0) 12991da177e4SLinus Torvalds return err; 13001da177e4SLinus Torvalds 13011da177e4SLinus Torvalds pcm->private_data = emu; 13021da177e4SLinus Torvalds 13031da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_efx_playback_ops); 13041da177e4SLinus Torvalds 13051da177e4SLinus Torvalds pcm->info_flags = 0; 13061da177e4SLinus Torvalds pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; 13071da177e4SLinus Torvalds strcpy(pcm->name, "Multichannel Playback"); 13081da177e4SLinus Torvalds emu->pcm = pcm; 13091da177e4SLinus Torvalds 13101da177e4SLinus Torvalds for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) 13111da177e4SLinus Torvalds if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0) 13121da177e4SLinus Torvalds return err; 13131da177e4SLinus Torvalds 13141da177e4SLinus Torvalds if (rpcm) 13151da177e4SLinus Torvalds *rpcm = pcm; 13161da177e4SLinus Torvalds 13171da177e4SLinus Torvalds return 0; 13181da177e4SLinus Torvalds } 13191da177e4SLinus Torvalds 13201da177e4SLinus Torvalds 13211da177e4SLinus Torvalds static snd_pcm_ops_t snd_emu10k1_capture_mic_ops = { 13221da177e4SLinus Torvalds .open = snd_emu10k1_capture_mic_open, 13231da177e4SLinus Torvalds .close = snd_emu10k1_capture_mic_close, 13241da177e4SLinus Torvalds .ioctl = snd_pcm_lib_ioctl, 13251da177e4SLinus Torvalds .hw_params = snd_emu10k1_capture_hw_params, 13261da177e4SLinus Torvalds .hw_free = snd_emu10k1_capture_hw_free, 13271da177e4SLinus Torvalds .prepare = snd_emu10k1_capture_prepare, 13281da177e4SLinus Torvalds .trigger = snd_emu10k1_capture_trigger, 13291da177e4SLinus Torvalds .pointer = snd_emu10k1_capture_pointer, 13301da177e4SLinus Torvalds }; 13311da177e4SLinus Torvalds 13321da177e4SLinus Torvalds int __devinit snd_emu10k1_pcm_mic(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) 13331da177e4SLinus Torvalds { 13341da177e4SLinus Torvalds snd_pcm_t *pcm; 13351da177e4SLinus Torvalds int err; 13361da177e4SLinus Torvalds 13371da177e4SLinus Torvalds if (rpcm) 13381da177e4SLinus Torvalds *rpcm = NULL; 13391da177e4SLinus Torvalds 13401da177e4SLinus Torvalds if ((err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm)) < 0) 13411da177e4SLinus Torvalds return err; 13421da177e4SLinus Torvalds 13431da177e4SLinus Torvalds pcm->private_data = emu; 13441da177e4SLinus Torvalds 13451da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops); 13461da177e4SLinus Torvalds 13471da177e4SLinus Torvalds pcm->info_flags = 0; 13481da177e4SLinus Torvalds strcpy(pcm->name, "Mic Capture"); 13491da177e4SLinus Torvalds emu->pcm_mic = pcm; 13501da177e4SLinus Torvalds 13511da177e4SLinus Torvalds snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); 13521da177e4SLinus Torvalds 13531da177e4SLinus Torvalds if (rpcm) 13541da177e4SLinus Torvalds *rpcm = pcm; 13551da177e4SLinus Torvalds return 0; 13561da177e4SLinus Torvalds } 13571da177e4SLinus Torvalds 13581da177e4SLinus Torvalds static int snd_emu10k1_pcm_efx_voices_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) 13591da177e4SLinus Torvalds { 13601da177e4SLinus Torvalds emu10k1_t *emu = snd_kcontrol_chip(kcontrol); 13611da177e4SLinus Torvalds int nefx = emu->audigy ? 64 : 32; 13621da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 13631da177e4SLinus Torvalds uinfo->count = nefx; 13641da177e4SLinus Torvalds uinfo->value.integer.min = 0; 13651da177e4SLinus Torvalds uinfo->value.integer.max = 1; 13661da177e4SLinus Torvalds return 0; 13671da177e4SLinus Torvalds } 13681da177e4SLinus Torvalds 13691da177e4SLinus Torvalds static int snd_emu10k1_pcm_efx_voices_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) 13701da177e4SLinus Torvalds { 13711da177e4SLinus Torvalds emu10k1_t *emu = snd_kcontrol_chip(kcontrol); 13721da177e4SLinus Torvalds int nefx = emu->audigy ? 64 : 32; 13731da177e4SLinus Torvalds int idx; 13741da177e4SLinus Torvalds 13751da177e4SLinus Torvalds spin_lock_irq(&emu->reg_lock); 13761da177e4SLinus Torvalds for (idx = 0; idx < nefx; idx++) 13771da177e4SLinus Torvalds ucontrol->value.integer.value[idx] = (emu->efx_voices_mask[idx / 32] & (1 << (idx % 32))) ? 1 : 0; 13781da177e4SLinus Torvalds spin_unlock_irq(&emu->reg_lock); 13791da177e4SLinus Torvalds return 0; 13801da177e4SLinus Torvalds } 13811da177e4SLinus Torvalds 13821da177e4SLinus Torvalds static int snd_emu10k1_pcm_efx_voices_mask_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) 13831da177e4SLinus Torvalds { 13841da177e4SLinus Torvalds emu10k1_t *emu = snd_kcontrol_chip(kcontrol); 13851da177e4SLinus Torvalds unsigned int nval[2], bits; 13861da177e4SLinus Torvalds int nefx = emu->audigy ? 64 : 32; 13871da177e4SLinus Torvalds int nefxb = emu->audigy ? 7 : 6; 13881da177e4SLinus Torvalds int change, idx; 13891da177e4SLinus Torvalds 13901da177e4SLinus Torvalds nval[0] = nval[1] = 0; 13911da177e4SLinus Torvalds for (idx = 0, bits = 0; idx < nefx; idx++) 13921da177e4SLinus Torvalds if (ucontrol->value.integer.value[idx]) { 13931da177e4SLinus Torvalds nval[idx / 32] |= 1 << (idx % 32); 13941da177e4SLinus Torvalds bits++; 13951da177e4SLinus Torvalds } 13961da177e4SLinus Torvalds 13971da177e4SLinus Torvalds for (idx = 0; idx < nefxb; idx++) 13981da177e4SLinus Torvalds if (1 << idx == bits) 13991da177e4SLinus Torvalds break; 14001da177e4SLinus Torvalds 14011da177e4SLinus Torvalds if (idx >= nefxb) 14021da177e4SLinus Torvalds return -EINVAL; 14031da177e4SLinus Torvalds 14041da177e4SLinus Torvalds spin_lock_irq(&emu->reg_lock); 14051da177e4SLinus Torvalds change = (nval[0] != emu->efx_voices_mask[0]) || 14061da177e4SLinus Torvalds (nval[1] != emu->efx_voices_mask[1]); 14071da177e4SLinus Torvalds emu->efx_voices_mask[0] = nval[0]; 14081da177e4SLinus Torvalds emu->efx_voices_mask[1] = nval[1]; 14091da177e4SLinus Torvalds spin_unlock_irq(&emu->reg_lock); 14101da177e4SLinus Torvalds return change; 14111da177e4SLinus Torvalds } 14121da177e4SLinus Torvalds 14131da177e4SLinus Torvalds static snd_kcontrol_new_t snd_emu10k1_pcm_efx_voices_mask = { 14141da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_PCM, 14151da177e4SLinus Torvalds .name = "Captured FX8010 Outputs", 14161da177e4SLinus Torvalds .info = snd_emu10k1_pcm_efx_voices_mask_info, 14171da177e4SLinus Torvalds .get = snd_emu10k1_pcm_efx_voices_mask_get, 14181da177e4SLinus Torvalds .put = snd_emu10k1_pcm_efx_voices_mask_put 14191da177e4SLinus Torvalds }; 14201da177e4SLinus Torvalds 14211da177e4SLinus Torvalds static snd_pcm_ops_t snd_emu10k1_capture_efx_ops = { 14221da177e4SLinus Torvalds .open = snd_emu10k1_capture_efx_open, 14231da177e4SLinus Torvalds .close = snd_emu10k1_capture_efx_close, 14241da177e4SLinus Torvalds .ioctl = snd_pcm_lib_ioctl, 14251da177e4SLinus Torvalds .hw_params = snd_emu10k1_capture_hw_params, 14261da177e4SLinus Torvalds .hw_free = snd_emu10k1_capture_hw_free, 14271da177e4SLinus Torvalds .prepare = snd_emu10k1_capture_prepare, 14281da177e4SLinus Torvalds .trigger = snd_emu10k1_capture_trigger, 14291da177e4SLinus Torvalds .pointer = snd_emu10k1_capture_pointer, 14301da177e4SLinus Torvalds }; 14311da177e4SLinus Torvalds 14321da177e4SLinus Torvalds 14331da177e4SLinus Torvalds /* EFX playback */ 14341da177e4SLinus Torvalds 14351da177e4SLinus Torvalds #define INITIAL_TRAM_SHIFT 14 14361da177e4SLinus Torvalds #define INITIAL_TRAM_POS(size) ((((size) / 2) - INITIAL_TRAM_SHIFT) - 1) 14371da177e4SLinus Torvalds 14381da177e4SLinus Torvalds static void snd_emu10k1_fx8010_playback_irq(emu10k1_t *emu, void *private_data) 14391da177e4SLinus Torvalds { 14401da177e4SLinus Torvalds snd_pcm_substream_t *substream = private_data; 14411da177e4SLinus Torvalds snd_pcm_period_elapsed(substream); 14421da177e4SLinus Torvalds } 14431da177e4SLinus Torvalds 14441da177e4SLinus Torvalds static void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left, 14451da177e4SLinus Torvalds unsigned short *dst_right, 14461da177e4SLinus Torvalds unsigned short *src, 14471da177e4SLinus Torvalds unsigned int count, 14481da177e4SLinus Torvalds unsigned int tram_shift) 14491da177e4SLinus Torvalds { 14501da177e4SLinus Torvalds // printk("tram_poke1: dst_left = 0x%p, dst_right = 0x%p, src = 0x%p, count = 0x%x\n", dst_left, dst_right, src, count); 14511da177e4SLinus Torvalds if ((tram_shift & 1) == 0) { 14521da177e4SLinus Torvalds while (count--) { 14531da177e4SLinus Torvalds *dst_left-- = *src++; 14541da177e4SLinus Torvalds *dst_right-- = *src++; 14551da177e4SLinus Torvalds } 14561da177e4SLinus Torvalds } else { 14571da177e4SLinus Torvalds while (count--) { 14581da177e4SLinus Torvalds *dst_right-- = *src++; 14591da177e4SLinus Torvalds *dst_left-- = *src++; 14601da177e4SLinus Torvalds } 14611da177e4SLinus Torvalds } 14621da177e4SLinus Torvalds } 14631da177e4SLinus Torvalds 14641da177e4SLinus Torvalds static void fx8010_pb_trans_copy(snd_pcm_substream_t *substream, 14651da177e4SLinus Torvalds snd_pcm_indirect_t *rec, size_t bytes) 14661da177e4SLinus Torvalds { 14671da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 14681da177e4SLinus Torvalds snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; 14691da177e4SLinus Torvalds unsigned int tram_size = pcm->buffer_size; 14701da177e4SLinus Torvalds unsigned short *src = (unsigned short *)(substream->runtime->dma_area + rec->sw_data); 14711da177e4SLinus Torvalds unsigned int frames = bytes >> 2, count; 14721da177e4SLinus Torvalds unsigned int tram_pos = pcm->tram_pos; 14731da177e4SLinus Torvalds unsigned int tram_shift = pcm->tram_shift; 14741da177e4SLinus Torvalds 14751da177e4SLinus Torvalds while (frames > tram_pos) { 14761da177e4SLinus Torvalds count = tram_pos + 1; 14771da177e4SLinus Torvalds snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos, 14781da177e4SLinus Torvalds (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2, 14791da177e4SLinus Torvalds src, count, tram_shift); 14801da177e4SLinus Torvalds src += count * 2; 14811da177e4SLinus Torvalds frames -= count; 14821da177e4SLinus Torvalds tram_pos = (tram_size / 2) - 1; 14831da177e4SLinus Torvalds tram_shift++; 14841da177e4SLinus Torvalds } 14851da177e4SLinus Torvalds snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos, 14861da177e4SLinus Torvalds (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2, 14871da177e4SLinus Torvalds src, frames, tram_shift); 14881da177e4SLinus Torvalds tram_pos -= frames; 14891da177e4SLinus Torvalds pcm->tram_pos = tram_pos; 14901da177e4SLinus Torvalds pcm->tram_shift = tram_shift; 14911da177e4SLinus Torvalds } 14921da177e4SLinus Torvalds 14931da177e4SLinus Torvalds static int snd_emu10k1_fx8010_playback_transfer(snd_pcm_substream_t *substream) 14941da177e4SLinus Torvalds { 14951da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 14961da177e4SLinus Torvalds snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; 14971da177e4SLinus Torvalds 14981da177e4SLinus Torvalds snd_pcm_indirect_playback_transfer(substream, &pcm->pcm_rec, fx8010_pb_trans_copy); 14991da177e4SLinus Torvalds return 0; 15001da177e4SLinus Torvalds } 15011da177e4SLinus Torvalds 15021da177e4SLinus Torvalds static int snd_emu10k1_fx8010_playback_hw_params(snd_pcm_substream_t * substream, 15031da177e4SLinus Torvalds snd_pcm_hw_params_t * hw_params) 15041da177e4SLinus Torvalds { 15051da177e4SLinus Torvalds return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); 15061da177e4SLinus Torvalds } 15071da177e4SLinus Torvalds 15081da177e4SLinus Torvalds static int snd_emu10k1_fx8010_playback_hw_free(snd_pcm_substream_t * substream) 15091da177e4SLinus Torvalds { 15101da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 15111da177e4SLinus Torvalds snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; 15121da177e4SLinus Torvalds unsigned int i; 15131da177e4SLinus Torvalds 15141da177e4SLinus Torvalds for (i = 0; i < pcm->channels; i++) 15151da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, 0); 15161da177e4SLinus Torvalds snd_pcm_lib_free_pages(substream); 15171da177e4SLinus Torvalds return 0; 15181da177e4SLinus Torvalds } 15191da177e4SLinus Torvalds 15201da177e4SLinus Torvalds static int snd_emu10k1_fx8010_playback_prepare(snd_pcm_substream_t * substream) 15211da177e4SLinus Torvalds { 15221da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 15231da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 15241da177e4SLinus Torvalds snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; 15251da177e4SLinus Torvalds unsigned int i; 15261da177e4SLinus Torvalds 15271da177e4SLinus Torvalds // printk("prepare: etram_pages = 0x%p, dma_area = 0x%x, buffer_size = 0x%x (0x%x)\n", emu->fx8010.etram_pages, runtime->dma_area, runtime->buffer_size, runtime->buffer_size << 2); 15281da177e4SLinus Torvalds memset(&pcm->pcm_rec, 0, sizeof(pcm->pcm_rec)); 15291da177e4SLinus Torvalds pcm->pcm_rec.hw_buffer_size = pcm->buffer_size * 2; /* byte size */ 15301da177e4SLinus Torvalds pcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); 15311da177e4SLinus Torvalds pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); 15321da177e4SLinus Torvalds pcm->tram_shift = 0; 15331da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_running, 0, 0); /* reset */ 15341da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); /* reset */ 15351da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_size, 0, runtime->buffer_size); 15361da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_ptr, 0, 0); /* reset ptr number */ 15371da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_count, 0, runtime->period_size); 15381da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_tmpcount, 0, runtime->period_size); 15391da177e4SLinus Torvalds for (i = 0; i < pcm->channels; i++) 15401da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, (TANKMEMADDRREG_READ|TANKMEMADDRREG_ALIGN) + i * (runtime->buffer_size / pcm->channels)); 15411da177e4SLinus Torvalds return 0; 15421da177e4SLinus Torvalds } 15431da177e4SLinus Torvalds 15441da177e4SLinus Torvalds static int snd_emu10k1_fx8010_playback_trigger(snd_pcm_substream_t * substream, int cmd) 15451da177e4SLinus Torvalds { 15461da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 15471da177e4SLinus Torvalds snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; 15481da177e4SLinus Torvalds int result = 0; 15491da177e4SLinus Torvalds 15501da177e4SLinus Torvalds spin_lock(&emu->reg_lock); 15511da177e4SLinus Torvalds switch (cmd) { 15521da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_START: 15531da177e4SLinus Torvalds /* follow thru */ 15541da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 15551da177e4SLinus Torvalds #ifdef EMU10K1_SET_AC3_IEC958 15561da177e4SLinus Torvalds { 15571da177e4SLinus Torvalds int i; 15581da177e4SLinus Torvalds for (i = 0; i < 3; i++) { 15591da177e4SLinus Torvalds unsigned int bits; 15601da177e4SLinus Torvalds bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | 15611da177e4SLinus Torvalds SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 15621da177e4SLinus Torvalds 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT | SPCS_NOTAUDIODATA; 15631da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, SPCS0 + i, 0, bits); 15641da177e4SLinus Torvalds } 15651da177e4SLinus Torvalds } 15661da177e4SLinus Torvalds #endif 15671da177e4SLinus Torvalds result = snd_emu10k1_fx8010_register_irq_handler(emu, snd_emu10k1_fx8010_playback_irq, pcm->gpr_running, substream, &pcm->irq); 15681da177e4SLinus Torvalds if (result < 0) 15691da177e4SLinus Torvalds goto __err; 15701da177e4SLinus Torvalds snd_emu10k1_fx8010_playback_transfer(substream); /* roll the ball */ 15711da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 1); 15721da177e4SLinus Torvalds break; 15731da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_STOP: 15741da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 15751da177e4SLinus Torvalds snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL; 15761da177e4SLinus Torvalds snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); 15771da177e4SLinus Torvalds pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); 15781da177e4SLinus Torvalds pcm->tram_shift = 0; 15791da177e4SLinus Torvalds break; 15801da177e4SLinus Torvalds default: 15811da177e4SLinus Torvalds result = -EINVAL; 15821da177e4SLinus Torvalds break; 15831da177e4SLinus Torvalds } 15841da177e4SLinus Torvalds __err: 15851da177e4SLinus Torvalds spin_unlock(&emu->reg_lock); 15861da177e4SLinus Torvalds return result; 15871da177e4SLinus Torvalds } 15881da177e4SLinus Torvalds 15891da177e4SLinus Torvalds static snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(snd_pcm_substream_t * substream) 15901da177e4SLinus Torvalds { 15911da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 15921da177e4SLinus Torvalds snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; 15931da177e4SLinus Torvalds size_t ptr; /* byte pointer */ 15941da177e4SLinus Torvalds 15951da177e4SLinus Torvalds if (!snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_trigger, 0)) 15961da177e4SLinus Torvalds return 0; 15971da177e4SLinus Torvalds ptr = snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_ptr, 0) << 2; 15981da177e4SLinus Torvalds return snd_pcm_indirect_playback_pointer(substream, &pcm->pcm_rec, ptr); 15991da177e4SLinus Torvalds } 16001da177e4SLinus Torvalds 16011da177e4SLinus Torvalds static snd_pcm_hardware_t snd_emu10k1_fx8010_playback = 16021da177e4SLinus Torvalds { 16031da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 16041da177e4SLinus Torvalds /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE), 16051da177e4SLinus Torvalds .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 16061da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_48000, 16071da177e4SLinus Torvalds .rate_min = 48000, 16081da177e4SLinus Torvalds .rate_max = 48000, 16091da177e4SLinus Torvalds .channels_min = 1, 16101da177e4SLinus Torvalds .channels_max = 1, 16111da177e4SLinus Torvalds .buffer_bytes_max = (128*1024), 16121da177e4SLinus Torvalds .period_bytes_min = 1024, 16131da177e4SLinus Torvalds .period_bytes_max = (128*1024), 16141da177e4SLinus Torvalds .periods_min = 1, 16151da177e4SLinus Torvalds .periods_max = 1024, 16161da177e4SLinus Torvalds .fifo_size = 0, 16171da177e4SLinus Torvalds }; 16181da177e4SLinus Torvalds 16191da177e4SLinus Torvalds static int snd_emu10k1_fx8010_playback_open(snd_pcm_substream_t * substream) 16201da177e4SLinus Torvalds { 16211da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 16221da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 16231da177e4SLinus Torvalds snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; 16241da177e4SLinus Torvalds 16251da177e4SLinus Torvalds runtime->hw = snd_emu10k1_fx8010_playback; 16261da177e4SLinus Torvalds runtime->hw.channels_min = runtime->hw.channels_max = pcm->channels; 16271da177e4SLinus Torvalds runtime->hw.period_bytes_max = (pcm->buffer_size * 2) / 2; 16281da177e4SLinus Torvalds spin_lock_irq(&emu->reg_lock); 16291da177e4SLinus Torvalds if (pcm->valid == 0) { 16301da177e4SLinus Torvalds spin_unlock_irq(&emu->reg_lock); 16311da177e4SLinus Torvalds return -ENODEV; 16321da177e4SLinus Torvalds } 16331da177e4SLinus Torvalds pcm->opened = 1; 16341da177e4SLinus Torvalds spin_unlock_irq(&emu->reg_lock); 16351da177e4SLinus Torvalds return 0; 16361da177e4SLinus Torvalds } 16371da177e4SLinus Torvalds 16381da177e4SLinus Torvalds static int snd_emu10k1_fx8010_playback_close(snd_pcm_substream_t * substream) 16391da177e4SLinus Torvalds { 16401da177e4SLinus Torvalds emu10k1_t *emu = snd_pcm_substream_chip(substream); 16411da177e4SLinus Torvalds snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; 16421da177e4SLinus Torvalds 16431da177e4SLinus Torvalds spin_lock_irq(&emu->reg_lock); 16441da177e4SLinus Torvalds pcm->opened = 0; 16451da177e4SLinus Torvalds spin_unlock_irq(&emu->reg_lock); 16461da177e4SLinus Torvalds return 0; 16471da177e4SLinus Torvalds } 16481da177e4SLinus Torvalds 16491da177e4SLinus Torvalds static snd_pcm_ops_t snd_emu10k1_fx8010_playback_ops = { 16501da177e4SLinus Torvalds .open = snd_emu10k1_fx8010_playback_open, 16511da177e4SLinus Torvalds .close = snd_emu10k1_fx8010_playback_close, 16521da177e4SLinus Torvalds .ioctl = snd_pcm_lib_ioctl, 16531da177e4SLinus Torvalds .hw_params = snd_emu10k1_fx8010_playback_hw_params, 16541da177e4SLinus Torvalds .hw_free = snd_emu10k1_fx8010_playback_hw_free, 16551da177e4SLinus Torvalds .prepare = snd_emu10k1_fx8010_playback_prepare, 16561da177e4SLinus Torvalds .trigger = snd_emu10k1_fx8010_playback_trigger, 16571da177e4SLinus Torvalds .pointer = snd_emu10k1_fx8010_playback_pointer, 16581da177e4SLinus Torvalds .ack = snd_emu10k1_fx8010_playback_transfer, 16591da177e4SLinus Torvalds }; 16601da177e4SLinus Torvalds 16611da177e4SLinus Torvalds int __devinit snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) 16621da177e4SLinus Torvalds { 16631da177e4SLinus Torvalds snd_pcm_t *pcm; 166467ed4161SClemens Ladisch snd_kcontrol_t *kctl; 16651da177e4SLinus Torvalds int err; 16661da177e4SLinus Torvalds 16671da177e4SLinus Torvalds if (rpcm) 16681da177e4SLinus Torvalds *rpcm = NULL; 16691da177e4SLinus Torvalds 16701da177e4SLinus Torvalds if ((err = snd_pcm_new(emu->card, "emu10k1 efx", device, 8, 1, &pcm)) < 0) 16711da177e4SLinus Torvalds return err; 16721da177e4SLinus Torvalds 16731da177e4SLinus Torvalds pcm->private_data = emu; 16741da177e4SLinus Torvalds 16751da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops); 16761da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops); 16771da177e4SLinus Torvalds 16781da177e4SLinus Torvalds pcm->info_flags = 0; 16791da177e4SLinus Torvalds strcpy(pcm->name, "Multichannel Capture/PT Playback"); 16801da177e4SLinus Torvalds emu->pcm_efx = pcm; 16811da177e4SLinus Torvalds if (rpcm) 16821da177e4SLinus Torvalds *rpcm = pcm; 16831da177e4SLinus Torvalds 16841da177e4SLinus Torvalds /* EFX capture - record the "FXBUS2" channels, by default we connect the EXTINs 16851da177e4SLinus Torvalds * to these 16861da177e4SLinus Torvalds */ 16871da177e4SLinus Torvalds 16881da177e4SLinus Torvalds /* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */ 16891da177e4SLinus Torvalds if (emu->audigy) { 16901da177e4SLinus Torvalds emu->efx_voices_mask[0] = 0; 16911da177e4SLinus Torvalds emu->efx_voices_mask[1] = 0xffff; 16921da177e4SLinus Torvalds } else { 16931da177e4SLinus Torvalds emu->efx_voices_mask[0] = 0xffff0000; 16941da177e4SLinus Torvalds emu->efx_voices_mask[1] = 0; 16951da177e4SLinus Torvalds } 169667ed4161SClemens Ladisch kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu); 169767ed4161SClemens Ladisch if (!kctl) 169867ed4161SClemens Ladisch return -ENOMEM; 169967ed4161SClemens Ladisch kctl->id.device = device; 170067ed4161SClemens Ladisch snd_ctl_add(emu->card, kctl); 17011da177e4SLinus Torvalds 17021da177e4SLinus Torvalds snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); 17031da177e4SLinus Torvalds 17041da177e4SLinus Torvalds return 0; 17051da177e4SLinus Torvalds } 1706