18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 48c2ecf20Sopenharmony_ci * Creative Labs, Inc. 58c2ecf20Sopenharmony_ci * Routines for control of EMU10K1 chips / PCM routines 68c2ecf20Sopenharmony_ci * Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * BUGS: 98c2ecf20Sopenharmony_ci * -- 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * TODO: 128c2ecf20Sopenharmony_ci * -- 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/pci.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/time.h> 198c2ecf20Sopenharmony_ci#include <linux/init.h> 208c2ecf20Sopenharmony_ci#include <sound/core.h> 218c2ecf20Sopenharmony_ci#include <sound/emu10k1.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic void snd_emu10k1_pcm_interrupt(struct snd_emu10k1 *emu, 248c2ecf20Sopenharmony_ci struct snd_emu10k1_voice *voice) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci if ((epcm = voice->epcm) == NULL) 298c2ecf20Sopenharmony_ci return; 308c2ecf20Sopenharmony_ci if (epcm->substream == NULL) 318c2ecf20Sopenharmony_ci return; 328c2ecf20Sopenharmony_ci#if 0 338c2ecf20Sopenharmony_ci dev_dbg(emu->card->dev, 348c2ecf20Sopenharmony_ci "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n", 358c2ecf20Sopenharmony_ci epcm->substream->runtime->hw->pointer(emu, epcm->substream), 368c2ecf20Sopenharmony_ci snd_pcm_lib_period_bytes(epcm->substream), 378c2ecf20Sopenharmony_ci snd_pcm_lib_buffer_bytes(epcm->substream)); 388c2ecf20Sopenharmony_ci#endif 398c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(epcm->substream); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void snd_emu10k1_pcm_ac97adc_interrupt(struct snd_emu10k1 *emu, 438c2ecf20Sopenharmony_ci unsigned int status) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci#if 0 468c2ecf20Sopenharmony_ci if (status & IPR_ADCBUFHALFFULL) { 478c2ecf20Sopenharmony_ci if (emu->pcm_capture_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) 488c2ecf20Sopenharmony_ci return; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci#endif 518c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(emu->pcm_capture_substream); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void snd_emu10k1_pcm_ac97mic_interrupt(struct snd_emu10k1 *emu, 558c2ecf20Sopenharmony_ci unsigned int status) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci#if 0 588c2ecf20Sopenharmony_ci if (status & IPR_MICBUFHALFFULL) { 598c2ecf20Sopenharmony_ci if (emu->pcm_capture_mic_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) 608c2ecf20Sopenharmony_ci return; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci#endif 638c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(emu->pcm_capture_mic_substream); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic void snd_emu10k1_pcm_efx_interrupt(struct snd_emu10k1 *emu, 678c2ecf20Sopenharmony_ci unsigned int status) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci#if 0 708c2ecf20Sopenharmony_ci if (status & IPR_EFXBUFHALFFULL) { 718c2ecf20Sopenharmony_ci if (emu->pcm_capture_efx_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) 728c2ecf20Sopenharmony_ci return; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci#endif 758c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(emu->pcm_capture_efx_substream); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_emu10k1_efx_playback_pointer(struct snd_pcm_substream *substream) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 818c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 828c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 838c2ecf20Sopenharmony_ci unsigned int ptr; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (!epcm->running) 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff; 888c2ecf20Sopenharmony_ci ptr += runtime->buffer_size; 898c2ecf20Sopenharmony_ci ptr -= epcm->ccca_start_addr; 908c2ecf20Sopenharmony_ci ptr %= runtime->buffer_size; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return ptr; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm * epcm, int voices) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci int err, i; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (epcm->voices[1] != NULL && voices < 2) { 1008c2ecf20Sopenharmony_ci snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); 1018c2ecf20Sopenharmony_ci epcm->voices[1] = NULL; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci for (i = 0; i < voices; i++) { 1048c2ecf20Sopenharmony_ci if (epcm->voices[i] == NULL) 1058c2ecf20Sopenharmony_ci break; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci if (i == voices) 1088c2ecf20Sopenharmony_ci return 0; /* already allocated */ 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(epcm->voices); i++) { 1118c2ecf20Sopenharmony_ci if (epcm->voices[i]) { 1128c2ecf20Sopenharmony_ci snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]); 1138c2ecf20Sopenharmony_ci epcm->voices[i] = NULL; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci err = snd_emu10k1_voice_alloc(epcm->emu, 1178c2ecf20Sopenharmony_ci epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX, 1188c2ecf20Sopenharmony_ci voices, 1198c2ecf20Sopenharmony_ci &epcm->voices[0]); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (err < 0) 1228c2ecf20Sopenharmony_ci return err; 1238c2ecf20Sopenharmony_ci epcm->voices[0]->epcm = epcm; 1248c2ecf20Sopenharmony_ci if (voices > 1) { 1258c2ecf20Sopenharmony_ci for (i = 1; i < voices; i++) { 1268c2ecf20Sopenharmony_ci epcm->voices[i] = &epcm->emu->voices[(epcm->voices[0]->number + i) % NUM_G]; 1278c2ecf20Sopenharmony_ci epcm->voices[i]->epcm = epcm; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci if (epcm->extra == NULL) { 1318c2ecf20Sopenharmony_ci err = snd_emu10k1_voice_alloc(epcm->emu, 1328c2ecf20Sopenharmony_ci epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX, 1338c2ecf20Sopenharmony_ci 1, 1348c2ecf20Sopenharmony_ci &epcm->extra); 1358c2ecf20Sopenharmony_ci if (err < 0) { 1368c2ecf20Sopenharmony_ci /* 1378c2ecf20Sopenharmony_ci dev_dbg(emu->card->dev, "pcm_channel_alloc: " 1388c2ecf20Sopenharmony_ci "failed extra: voices=%d, frame=%d\n", 1398c2ecf20Sopenharmony_ci voices, frame); 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci for (i = 0; i < voices; i++) { 1428c2ecf20Sopenharmony_ci snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]); 1438c2ecf20Sopenharmony_ci epcm->voices[i] = NULL; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci return err; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci epcm->extra->epcm = epcm; 1488c2ecf20Sopenharmony_ci epcm->extra->interrupt = snd_emu10k1_pcm_interrupt; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic const unsigned int capture_period_sizes[31] = { 1548c2ecf20Sopenharmony_ci 384, 448, 512, 640, 1558c2ecf20Sopenharmony_ci 384*2, 448*2, 512*2, 640*2, 1568c2ecf20Sopenharmony_ci 384*4, 448*4, 512*4, 640*4, 1578c2ecf20Sopenharmony_ci 384*8, 448*8, 512*8, 640*8, 1588c2ecf20Sopenharmony_ci 384*16, 448*16, 512*16, 640*16, 1598c2ecf20Sopenharmony_ci 384*32, 448*32, 512*32, 640*32, 1608c2ecf20Sopenharmony_ci 384*64, 448*64, 512*64, 640*64, 1618c2ecf20Sopenharmony_ci 384*128,448*128,512*128 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_capture_period_sizes = { 1658c2ecf20Sopenharmony_ci .count = 31, 1668c2ecf20Sopenharmony_ci .list = capture_period_sizes, 1678c2ecf20Sopenharmony_ci .mask = 0 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic const unsigned int capture_rates[8] = { 1718c2ecf20Sopenharmony_ci 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_capture_rates = { 1758c2ecf20Sopenharmony_ci .count = 8, 1768c2ecf20Sopenharmony_ci .list = capture_rates, 1778c2ecf20Sopenharmony_ci .mask = 0 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic unsigned int snd_emu10k1_capture_rate_reg(unsigned int rate) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci switch (rate) { 1838c2ecf20Sopenharmony_ci case 8000: return ADCCR_SAMPLERATE_8; 1848c2ecf20Sopenharmony_ci case 11025: return ADCCR_SAMPLERATE_11; 1858c2ecf20Sopenharmony_ci case 16000: return ADCCR_SAMPLERATE_16; 1868c2ecf20Sopenharmony_ci case 22050: return ADCCR_SAMPLERATE_22; 1878c2ecf20Sopenharmony_ci case 24000: return ADCCR_SAMPLERATE_24; 1888c2ecf20Sopenharmony_ci case 32000: return ADCCR_SAMPLERATE_32; 1898c2ecf20Sopenharmony_ci case 44100: return ADCCR_SAMPLERATE_44; 1908c2ecf20Sopenharmony_ci case 48000: return ADCCR_SAMPLERATE_48; 1918c2ecf20Sopenharmony_ci default: 1928c2ecf20Sopenharmony_ci snd_BUG(); 1938c2ecf20Sopenharmony_ci return ADCCR_SAMPLERATE_8; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci switch (rate) { 2008c2ecf20Sopenharmony_ci case 8000: return A_ADCCR_SAMPLERATE_8; 2018c2ecf20Sopenharmony_ci case 11025: return A_ADCCR_SAMPLERATE_11; 2028c2ecf20Sopenharmony_ci case 12000: return A_ADCCR_SAMPLERATE_12; /* really supported? */ 2038c2ecf20Sopenharmony_ci case 16000: return ADCCR_SAMPLERATE_16; 2048c2ecf20Sopenharmony_ci case 22050: return ADCCR_SAMPLERATE_22; 2058c2ecf20Sopenharmony_ci case 24000: return ADCCR_SAMPLERATE_24; 2068c2ecf20Sopenharmony_ci case 32000: return ADCCR_SAMPLERATE_32; 2078c2ecf20Sopenharmony_ci case 44100: return ADCCR_SAMPLERATE_44; 2088c2ecf20Sopenharmony_ci case 48000: return ADCCR_SAMPLERATE_48; 2098c2ecf20Sopenharmony_ci default: 2108c2ecf20Sopenharmony_ci snd_BUG(); 2118c2ecf20Sopenharmony_ci return A_ADCCR_SAMPLERATE_8; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic unsigned int emu10k1_calc_pitch_target(unsigned int rate) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci unsigned int pitch_target; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci pitch_target = (rate << 8) / 375; 2208c2ecf20Sopenharmony_ci pitch_target = (pitch_target >> 1) + (pitch_target & 1); 2218c2ecf20Sopenharmony_ci return pitch_target; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci#define PITCH_48000 0x00004000 2258c2ecf20Sopenharmony_ci#define PITCH_96000 0x00008000 2268c2ecf20Sopenharmony_ci#define PITCH_85000 0x00007155 2278c2ecf20Sopenharmony_ci#define PITCH_80726 0x00006ba2 2288c2ecf20Sopenharmony_ci#define PITCH_67882 0x00005a82 2298c2ecf20Sopenharmony_ci#define PITCH_57081 0x00004c1c 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic unsigned int emu10k1_select_interprom(unsigned int pitch_target) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci if (pitch_target == PITCH_48000) 2348c2ecf20Sopenharmony_ci return CCCA_INTERPROM_0; 2358c2ecf20Sopenharmony_ci else if (pitch_target < PITCH_48000) 2368c2ecf20Sopenharmony_ci return CCCA_INTERPROM_1; 2378c2ecf20Sopenharmony_ci else if (pitch_target >= PITCH_96000) 2388c2ecf20Sopenharmony_ci return CCCA_INTERPROM_0; 2398c2ecf20Sopenharmony_ci else if (pitch_target >= PITCH_85000) 2408c2ecf20Sopenharmony_ci return CCCA_INTERPROM_6; 2418c2ecf20Sopenharmony_ci else if (pitch_target >= PITCH_80726) 2428c2ecf20Sopenharmony_ci return CCCA_INTERPROM_5; 2438c2ecf20Sopenharmony_ci else if (pitch_target >= PITCH_67882) 2448c2ecf20Sopenharmony_ci return CCCA_INTERPROM_4; 2458c2ecf20Sopenharmony_ci else if (pitch_target >= PITCH_57081) 2468c2ecf20Sopenharmony_ci return CCCA_INTERPROM_3; 2478c2ecf20Sopenharmony_ci else 2488c2ecf20Sopenharmony_ci return CCCA_INTERPROM_2; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* 2528c2ecf20Sopenharmony_ci * calculate cache invalidate size 2538c2ecf20Sopenharmony_ci * 2548c2ecf20Sopenharmony_ci * stereo: channel is stereo 2558c2ecf20Sopenharmony_ci * w_16: using 16bit samples 2568c2ecf20Sopenharmony_ci * 2578c2ecf20Sopenharmony_ci * returns: cache invalidate size in samples 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_cistatic inline int emu10k1_ccis(int stereo, int w_16) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci if (w_16) { 2628c2ecf20Sopenharmony_ci return stereo ? 24 : 26; 2638c2ecf20Sopenharmony_ci } else { 2648c2ecf20Sopenharmony_ci return stereo ? 24*2 : 26*2; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu, 2698c2ecf20Sopenharmony_ci int master, int extra, 2708c2ecf20Sopenharmony_ci struct snd_emu10k1_voice *evoice, 2718c2ecf20Sopenharmony_ci unsigned int start_addr, 2728c2ecf20Sopenharmony_ci unsigned int end_addr, 2738c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm_mixer *mix) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = evoice->epcm->substream; 2768c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 2778c2ecf20Sopenharmony_ci unsigned int silent_page, tmp; 2788c2ecf20Sopenharmony_ci int voice, stereo, w_16; 2798c2ecf20Sopenharmony_ci unsigned char send_amount[8]; 2808c2ecf20Sopenharmony_ci unsigned char send_routing[8]; 2818c2ecf20Sopenharmony_ci unsigned long flags; 2828c2ecf20Sopenharmony_ci unsigned int pitch_target; 2838c2ecf20Sopenharmony_ci unsigned int ccis; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci voice = evoice->number; 2868c2ecf20Sopenharmony_ci stereo = runtime->channels == 2; 2878c2ecf20Sopenharmony_ci w_16 = snd_pcm_format_width(runtime->format) == 16; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (!extra && stereo) { 2908c2ecf20Sopenharmony_ci start_addr >>= 1; 2918c2ecf20Sopenharmony_ci end_addr >>= 1; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci if (w_16) { 2948c2ecf20Sopenharmony_ci start_addr >>= 1; 2958c2ecf20Sopenharmony_ci end_addr >>= 1; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->reg_lock, flags); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* volume parameters */ 3018c2ecf20Sopenharmony_ci if (extra) { 3028c2ecf20Sopenharmony_ci memset(send_routing, 0, sizeof(send_routing)); 3038c2ecf20Sopenharmony_ci send_routing[0] = 0; 3048c2ecf20Sopenharmony_ci send_routing[1] = 1; 3058c2ecf20Sopenharmony_ci send_routing[2] = 2; 3068c2ecf20Sopenharmony_ci send_routing[3] = 3; 3078c2ecf20Sopenharmony_ci memset(send_amount, 0, sizeof(send_amount)); 3088c2ecf20Sopenharmony_ci } else { 3098c2ecf20Sopenharmony_ci /* mono, left, right (master voice = left) */ 3108c2ecf20Sopenharmony_ci tmp = stereo ? (master ? 1 : 2) : 0; 3118c2ecf20Sopenharmony_ci memcpy(send_routing, &mix->send_routing[tmp][0], 8); 3128c2ecf20Sopenharmony_ci memcpy(send_amount, &mix->send_volume[tmp][0], 8); 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci ccis = emu10k1_ccis(stereo, w_16); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (master) { 3188c2ecf20Sopenharmony_ci evoice->epcm->ccca_start_addr = start_addr + ccis; 3198c2ecf20Sopenharmony_ci if (extra) { 3208c2ecf20Sopenharmony_ci start_addr += ccis; 3218c2ecf20Sopenharmony_ci end_addr += ccis + emu->delay_pcm_irq; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci if (stereo && !extra) { 3248c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK); 3258c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CPF, (voice + 1), CPF_STEREO_MASK); 3268c2ecf20Sopenharmony_ci } else { 3278c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CPF, voice, 0); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* setup routing */ 3328c2ecf20Sopenharmony_ci if (emu->audigy) { 3338c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, A_FXRT1, voice, 3348c2ecf20Sopenharmony_ci snd_emu10k1_compose_audigy_fxrt1(send_routing)); 3358c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, A_FXRT2, voice, 3368c2ecf20Sopenharmony_ci snd_emu10k1_compose_audigy_fxrt2(send_routing)); 3378c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, 3388c2ecf20Sopenharmony_ci ((unsigned int)send_amount[4] << 24) | 3398c2ecf20Sopenharmony_ci ((unsigned int)send_amount[5] << 16) | 3408c2ecf20Sopenharmony_ci ((unsigned int)send_amount[6] << 8) | 3418c2ecf20Sopenharmony_ci (unsigned int)send_amount[7]); 3428c2ecf20Sopenharmony_ci } else 3438c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, FXRT, voice, 3448c2ecf20Sopenharmony_ci snd_emu10k1_compose_send_routing(send_routing)); 3458c2ecf20Sopenharmony_ci /* Stop CA */ 3468c2ecf20Sopenharmony_ci /* Assumption that PT is already 0 so no harm overwriting */ 3478c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]); 3488c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24)); 3498c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, PSST, voice, 3508c2ecf20Sopenharmony_ci (start_addr + (extra ? emu->delay_pcm_irq : 0)) | 3518c2ecf20Sopenharmony_ci (send_amount[2] << 24)); 3528c2ecf20Sopenharmony_ci if (emu->card_capabilities->emu_model) 3538c2ecf20Sopenharmony_ci pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */ 3548c2ecf20Sopenharmony_ci else 3558c2ecf20Sopenharmony_ci pitch_target = emu10k1_calc_pitch_target(runtime->rate); 3568c2ecf20Sopenharmony_ci if (extra) 3578c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CCCA, voice, start_addr | 3588c2ecf20Sopenharmony_ci emu10k1_select_interprom(pitch_target) | 3598c2ecf20Sopenharmony_ci (w_16 ? 0 : CCCA_8BITSELECT)); 3608c2ecf20Sopenharmony_ci else 3618c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CCCA, voice, (start_addr + ccis) | 3628c2ecf20Sopenharmony_ci emu10k1_select_interprom(pitch_target) | 3638c2ecf20Sopenharmony_ci (w_16 ? 0 : CCCA_8BITSELECT)); 3648c2ecf20Sopenharmony_ci /* Clear filter delay memory */ 3658c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, Z1, voice, 0); 3668c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, Z2, voice, 0); 3678c2ecf20Sopenharmony_ci /* invalidate maps */ 3688c2ecf20Sopenharmony_ci silent_page = ((unsigned int)emu->silent_page.addr << emu->address_mode) | (emu->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0); 3698c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page); 3708c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page); 3718c2ecf20Sopenharmony_ci /* modulation envelope */ 3728c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff); 3738c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff); 3748c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, ATKHLDM, voice, 0); 3758c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, DCYSUSM, voice, 0x007f); 3768c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, LFOVAL1, voice, 0x8000); 3778c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, LFOVAL2, voice, 0x8000); 3788c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, FMMOD, voice, 0); 3798c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, TREMFRQ, voice, 0); 3808c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, FM2FRQ2, voice, 0); 3818c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, ENVVAL, voice, 0x8000); 3828c2ecf20Sopenharmony_ci /* volume envelope */ 3838c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, ATKHLDV, voice, 0x7f7f); 3848c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, ENVVOL, voice, 0x0000); 3858c2ecf20Sopenharmony_ci /* filter envelope */ 3868c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, PEFE_FILTERAMOUNT, voice, 0x7f); 3878c2ecf20Sopenharmony_ci /* pitch envelope */ 3888c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, PEFE_PITCHAMOUNT, voice, 0); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->reg_lock, flags); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic int snd_emu10k1_playback_hw_params(struct snd_pcm_substream *substream, 3948c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 3978c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 3988c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 3998c2ecf20Sopenharmony_ci size_t alloc_size; 4008c2ecf20Sopenharmony_ci int err; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0) 4038c2ecf20Sopenharmony_ci return err; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci alloc_size = params_buffer_bytes(hw_params); 4068c2ecf20Sopenharmony_ci if (emu->iommu_workaround) 4078c2ecf20Sopenharmony_ci alloc_size += EMUPAGESIZE; 4088c2ecf20Sopenharmony_ci err = snd_pcm_lib_malloc_pages(substream, alloc_size); 4098c2ecf20Sopenharmony_ci if (err < 0) 4108c2ecf20Sopenharmony_ci return err; 4118c2ecf20Sopenharmony_ci if (emu->iommu_workaround && runtime->dma_bytes >= EMUPAGESIZE) 4128c2ecf20Sopenharmony_ci runtime->dma_bytes -= EMUPAGESIZE; 4138c2ecf20Sopenharmony_ci if (err > 0) { /* change */ 4148c2ecf20Sopenharmony_ci int mapped; 4158c2ecf20Sopenharmony_ci if (epcm->memblk != NULL) 4168c2ecf20Sopenharmony_ci snd_emu10k1_free_pages(emu, epcm->memblk); 4178c2ecf20Sopenharmony_ci epcm->memblk = snd_emu10k1_alloc_pages(emu, substream); 4188c2ecf20Sopenharmony_ci epcm->start_addr = 0; 4198c2ecf20Sopenharmony_ci if (! epcm->memblk) 4208c2ecf20Sopenharmony_ci return -ENOMEM; 4218c2ecf20Sopenharmony_ci mapped = ((struct snd_emu10k1_memblk *)epcm->memblk)->mapped_page; 4228c2ecf20Sopenharmony_ci if (mapped < 0) 4238c2ecf20Sopenharmony_ci return -ENOMEM; 4248c2ecf20Sopenharmony_ci epcm->start_addr = mapped << PAGE_SHIFT; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci return 0; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic int snd_emu10k1_playback_hw_free(struct snd_pcm_substream *substream) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 4328c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4338c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (runtime->private_data == NULL) 4368c2ecf20Sopenharmony_ci return 0; 4378c2ecf20Sopenharmony_ci epcm = runtime->private_data; 4388c2ecf20Sopenharmony_ci if (epcm->extra) { 4398c2ecf20Sopenharmony_ci snd_emu10k1_voice_free(epcm->emu, epcm->extra); 4408c2ecf20Sopenharmony_ci epcm->extra = NULL; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci if (epcm->voices[1]) { 4438c2ecf20Sopenharmony_ci snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); 4448c2ecf20Sopenharmony_ci epcm->voices[1] = NULL; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci if (epcm->voices[0]) { 4478c2ecf20Sopenharmony_ci snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]); 4488c2ecf20Sopenharmony_ci epcm->voices[0] = NULL; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci if (epcm->memblk) { 4518c2ecf20Sopenharmony_ci snd_emu10k1_free_pages(emu, epcm->memblk); 4528c2ecf20Sopenharmony_ci epcm->memblk = NULL; 4538c2ecf20Sopenharmony_ci epcm->start_addr = 0; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci snd_pcm_lib_free_pages(substream); 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic int snd_emu10k1_efx_playback_hw_free(struct snd_pcm_substream *substream) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 4628c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4638c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm; 4648c2ecf20Sopenharmony_ci int i; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (runtime->private_data == NULL) 4678c2ecf20Sopenharmony_ci return 0; 4688c2ecf20Sopenharmony_ci epcm = runtime->private_data; 4698c2ecf20Sopenharmony_ci if (epcm->extra) { 4708c2ecf20Sopenharmony_ci snd_emu10k1_voice_free(epcm->emu, epcm->extra); 4718c2ecf20Sopenharmony_ci epcm->extra = NULL; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci for (i = 0; i < NUM_EFX_PLAYBACK; i++) { 4748c2ecf20Sopenharmony_ci if (epcm->voices[i]) { 4758c2ecf20Sopenharmony_ci snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]); 4768c2ecf20Sopenharmony_ci epcm->voices[i] = NULL; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci if (epcm->memblk) { 4808c2ecf20Sopenharmony_ci snd_emu10k1_free_pages(emu, epcm->memblk); 4818c2ecf20Sopenharmony_ci epcm->memblk = NULL; 4828c2ecf20Sopenharmony_ci epcm->start_addr = 0; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci snd_pcm_lib_free_pages(substream); 4858c2ecf20Sopenharmony_ci return 0; 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic int snd_emu10k1_playback_prepare(struct snd_pcm_substream *substream) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 4918c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4928c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 4938c2ecf20Sopenharmony_ci unsigned int start_addr, end_addr; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci start_addr = epcm->start_addr; 4968c2ecf20Sopenharmony_ci end_addr = snd_pcm_lib_period_bytes(substream); 4978c2ecf20Sopenharmony_ci if (runtime->channels == 2) { 4988c2ecf20Sopenharmony_ci start_addr >>= 1; 4998c2ecf20Sopenharmony_ci end_addr >>= 1; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci end_addr += start_addr; 5028c2ecf20Sopenharmony_ci snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra, 5038c2ecf20Sopenharmony_ci start_addr, end_addr, NULL); 5048c2ecf20Sopenharmony_ci start_addr = epcm->start_addr; 5058c2ecf20Sopenharmony_ci end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream); 5068c2ecf20Sopenharmony_ci snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0], 5078c2ecf20Sopenharmony_ci start_addr, end_addr, 5088c2ecf20Sopenharmony_ci &emu->pcm_mixer[substream->number]); 5098c2ecf20Sopenharmony_ci if (epcm->voices[1]) 5108c2ecf20Sopenharmony_ci snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[1], 5118c2ecf20Sopenharmony_ci start_addr, end_addr, 5128c2ecf20Sopenharmony_ci &emu->pcm_mixer[substream->number]); 5138c2ecf20Sopenharmony_ci return 0; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic int snd_emu10k1_efx_playback_prepare(struct snd_pcm_substream *substream) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 5198c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 5208c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 5218c2ecf20Sopenharmony_ci unsigned int start_addr, end_addr; 5228c2ecf20Sopenharmony_ci unsigned int channel_size; 5238c2ecf20Sopenharmony_ci int i; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci start_addr = epcm->start_addr; 5268c2ecf20Sopenharmony_ci end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* 5298c2ecf20Sopenharmony_ci * the kX driver leaves some space between voices 5308c2ecf20Sopenharmony_ci */ 5318c2ecf20Sopenharmony_ci channel_size = ( end_addr - start_addr ) / NUM_EFX_PLAYBACK; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra, 5348c2ecf20Sopenharmony_ci start_addr, start_addr + (channel_size / 2), NULL); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* only difference with the master voice is we use it for the pointer */ 5378c2ecf20Sopenharmony_ci snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0], 5388c2ecf20Sopenharmony_ci start_addr, start_addr + channel_size, 5398c2ecf20Sopenharmony_ci &emu->efx_pcm_mixer[0]); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci start_addr += channel_size; 5428c2ecf20Sopenharmony_ci for (i = 1; i < NUM_EFX_PLAYBACK; i++) { 5438c2ecf20Sopenharmony_ci snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[i], 5448c2ecf20Sopenharmony_ci start_addr, start_addr + channel_size, 5458c2ecf20Sopenharmony_ci &emu->efx_pcm_mixer[i]); 5468c2ecf20Sopenharmony_ci start_addr += channel_size; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci return 0; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_emu10k1_efx_playback = 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_NONINTERLEAVED | 5558c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 5568c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 5578c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), 5588c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 5598c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 5608c2ecf20Sopenharmony_ci .rate_min = 48000, 5618c2ecf20Sopenharmony_ci .rate_max = 48000, 5628c2ecf20Sopenharmony_ci .channels_min = NUM_EFX_PLAYBACK, 5638c2ecf20Sopenharmony_ci .channels_max = NUM_EFX_PLAYBACK, 5648c2ecf20Sopenharmony_ci .buffer_bytes_max = (64*1024), 5658c2ecf20Sopenharmony_ci .period_bytes_min = 64, 5668c2ecf20Sopenharmony_ci .period_bytes_max = (64*1024), 5678c2ecf20Sopenharmony_ci .periods_min = 2, 5688c2ecf20Sopenharmony_ci .periods_max = 2, 5698c2ecf20Sopenharmony_ci .fifo_size = 0, 5708c2ecf20Sopenharmony_ci}; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistatic int snd_emu10k1_capture_prepare(struct snd_pcm_substream *substream) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 5758c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 5768c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 5778c2ecf20Sopenharmony_ci int idx; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* zeroing the buffer size will stop capture */ 5808c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0); 5818c2ecf20Sopenharmony_ci switch (epcm->type) { 5828c2ecf20Sopenharmony_ci case CAPTURE_AC97ADC: 5838c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, ADCCR, 0, 0); 5848c2ecf20Sopenharmony_ci break; 5858c2ecf20Sopenharmony_ci case CAPTURE_EFX: 5868c2ecf20Sopenharmony_ci if (emu->audigy) { 5878c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0); 5888c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0); 5898c2ecf20Sopenharmony_ci } else 5908c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, FXWC, 0, 0); 5918c2ecf20Sopenharmony_ci break; 5928c2ecf20Sopenharmony_ci default: 5938c2ecf20Sopenharmony_ci break; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, epcm->capture_ba_reg, 0, runtime->dma_addr); 5968c2ecf20Sopenharmony_ci epcm->capture_bufsize = snd_pcm_lib_buffer_bytes(substream); 5978c2ecf20Sopenharmony_ci epcm->capture_bs_val = 0; 5988c2ecf20Sopenharmony_ci for (idx = 0; idx < 31; idx++) { 5998c2ecf20Sopenharmony_ci if (capture_period_sizes[idx] == epcm->capture_bufsize) { 6008c2ecf20Sopenharmony_ci epcm->capture_bs_val = idx + 1; 6018c2ecf20Sopenharmony_ci break; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci if (epcm->capture_bs_val == 0) { 6058c2ecf20Sopenharmony_ci snd_BUG(); 6068c2ecf20Sopenharmony_ci epcm->capture_bs_val++; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci if (epcm->type == CAPTURE_AC97ADC) { 6098c2ecf20Sopenharmony_ci epcm->capture_cr_val = emu->audigy ? A_ADCCR_LCHANENABLE : ADCCR_LCHANENABLE; 6108c2ecf20Sopenharmony_ci if (runtime->channels > 1) 6118c2ecf20Sopenharmony_ci epcm->capture_cr_val |= emu->audigy ? A_ADCCR_RCHANENABLE : ADCCR_RCHANENABLE; 6128c2ecf20Sopenharmony_ci epcm->capture_cr_val |= emu->audigy ? 6138c2ecf20Sopenharmony_ci snd_emu10k1_audigy_capture_rate_reg(runtime->rate) : 6148c2ecf20Sopenharmony_ci snd_emu10k1_capture_rate_reg(runtime->rate); 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci return 0; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic void snd_emu10k1_playback_invalidate_cache(struct snd_emu10k1 *emu, int extra, struct snd_emu10k1_voice *evoice) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 6228c2ecf20Sopenharmony_ci unsigned int voice, stereo, i, ccis, cra = 64, cs, sample; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (evoice == NULL) 6258c2ecf20Sopenharmony_ci return; 6268c2ecf20Sopenharmony_ci runtime = evoice->epcm->substream->runtime; 6278c2ecf20Sopenharmony_ci voice = evoice->number; 6288c2ecf20Sopenharmony_ci stereo = (!extra && runtime->channels == 2); 6298c2ecf20Sopenharmony_ci sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080; 6308c2ecf20Sopenharmony_ci ccis = emu10k1_ccis(stereo, sample == 0); 6318c2ecf20Sopenharmony_ci /* set cs to 2 * number of cache registers beside the invalidated */ 6328c2ecf20Sopenharmony_ci cs = (sample == 0) ? (32-ccis) : (64-ccis+1) >> 1; 6338c2ecf20Sopenharmony_ci if (cs > 16) cs = 16; 6348c2ecf20Sopenharmony_ci for (i = 0; i < cs; i++) { 6358c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample); 6368c2ecf20Sopenharmony_ci if (stereo) { 6378c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CD0 + i, voice + 1, sample); 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci /* reset cache */ 6418c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0); 6428c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra); 6438c2ecf20Sopenharmony_ci if (stereo) { 6448c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0); 6458c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra); 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci /* fill cache */ 6488c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis); 6498c2ecf20Sopenharmony_ci if (stereo) { 6508c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice+1, ccis); 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic void snd_emu10k1_playback_prepare_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice, 6558c2ecf20Sopenharmony_ci int master, int extra, 6568c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm_mixer *mix) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 6598c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 6608c2ecf20Sopenharmony_ci unsigned int attn, vattn; 6618c2ecf20Sopenharmony_ci unsigned int voice, tmp; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (evoice == NULL) /* skip second voice for mono */ 6648c2ecf20Sopenharmony_ci return; 6658c2ecf20Sopenharmony_ci substream = evoice->epcm->substream; 6668c2ecf20Sopenharmony_ci runtime = substream->runtime; 6678c2ecf20Sopenharmony_ci voice = evoice->number; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci attn = extra ? 0 : 0x00ff; 6708c2ecf20Sopenharmony_ci tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0; 6718c2ecf20Sopenharmony_ci vattn = mix != NULL ? (mix->attn[tmp] << 16) : 0; 6728c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, IFATN, voice, attn); 6738c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, VTFT, voice, vattn | 0xffff); 6748c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CVCF, voice, vattn | 0xffff); 6758c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f); 6768c2ecf20Sopenharmony_ci snd_emu10k1_voice_clear_loop_stop(emu, voice); 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice, int master, int extra) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 6828c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 6838c2ecf20Sopenharmony_ci unsigned int voice, pitch, pitch_target; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (evoice == NULL) /* skip second voice for mono */ 6868c2ecf20Sopenharmony_ci return; 6878c2ecf20Sopenharmony_ci substream = evoice->epcm->substream; 6888c2ecf20Sopenharmony_ci runtime = substream->runtime; 6898c2ecf20Sopenharmony_ci voice = evoice->number; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8; 6928c2ecf20Sopenharmony_ci if (emu->card_capabilities->emu_model) 6938c2ecf20Sopenharmony_ci pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */ 6948c2ecf20Sopenharmony_ci else 6958c2ecf20Sopenharmony_ci pitch_target = emu10k1_calc_pitch_target(runtime->rate); 6968c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target); 6978c2ecf20Sopenharmony_ci if (master || evoice->epcm->type == PLAYBACK_EFX) 6988c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target); 6998c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, IP, voice, pitch); 7008c2ecf20Sopenharmony_ci if (extra) 7018c2ecf20Sopenharmony_ci snd_emu10k1_voice_intr_enable(emu, voice); 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci unsigned int voice; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (evoice == NULL) 7098c2ecf20Sopenharmony_ci return; 7108c2ecf20Sopenharmony_ci voice = evoice->number; 7118c2ecf20Sopenharmony_ci snd_emu10k1_voice_intr_disable(emu, voice); 7128c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, 0); 7138c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, 0); 7148c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, IFATN, voice, 0xffff); 7158c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff); 7168c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff); 7178c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, IP, voice, 0); 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic inline void snd_emu10k1_playback_mangle_extra(struct snd_emu10k1 *emu, 7218c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm, 7228c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 7238c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci unsigned int ptr, period_pos; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* try to sychronize the current position for the interrupt 7288c2ecf20Sopenharmony_ci source voice */ 7298c2ecf20Sopenharmony_ci period_pos = runtime->status->hw_ptr - runtime->hw_ptr_interrupt; 7308c2ecf20Sopenharmony_ci period_pos %= runtime->period_size; 7318c2ecf20Sopenharmony_ci ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->extra->number); 7328c2ecf20Sopenharmony_ci ptr &= ~0x00ffffff; 7338c2ecf20Sopenharmony_ci ptr |= epcm->ccca_start_addr + period_pos; 7348c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, CCCA, epcm->extra->number, ptr); 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream, 7388c2ecf20Sopenharmony_ci int cmd) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 7418c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 7428c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 7438c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm_mixer *mix; 7448c2ecf20Sopenharmony_ci int result = 0; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci /* 7478c2ecf20Sopenharmony_ci dev_dbg(emu->card->dev, 7488c2ecf20Sopenharmony_ci "trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", 7498c2ecf20Sopenharmony_ci (int)emu, cmd, substream->ops->pointer(substream)) 7508c2ecf20Sopenharmony_ci */ 7518c2ecf20Sopenharmony_ci spin_lock(&emu->reg_lock); 7528c2ecf20Sopenharmony_ci switch (cmd) { 7538c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 7548c2ecf20Sopenharmony_ci snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra); /* do we need this? */ 7558c2ecf20Sopenharmony_ci snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[0]); 7568c2ecf20Sopenharmony_ci fallthrough; 7578c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 7588c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 7598c2ecf20Sopenharmony_ci if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) 7608c2ecf20Sopenharmony_ci snd_emu10k1_playback_mangle_extra(emu, epcm, substream, runtime); 7618c2ecf20Sopenharmony_ci mix = &emu->pcm_mixer[substream->number]; 7628c2ecf20Sopenharmony_ci snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 1, 0, mix); 7638c2ecf20Sopenharmony_ci snd_emu10k1_playback_prepare_voice(emu, epcm->voices[1], 0, 0, mix); 7648c2ecf20Sopenharmony_ci snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL); 7658c2ecf20Sopenharmony_ci snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 1, 0); 7668c2ecf20Sopenharmony_ci snd_emu10k1_playback_trigger_voice(emu, epcm->voices[1], 0, 0); 7678c2ecf20Sopenharmony_ci snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1); 7688c2ecf20Sopenharmony_ci epcm->running = 1; 7698c2ecf20Sopenharmony_ci break; 7708c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 7718c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 7728c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 7738c2ecf20Sopenharmony_ci epcm->running = 0; 7748c2ecf20Sopenharmony_ci snd_emu10k1_playback_stop_voice(emu, epcm->voices[0]); 7758c2ecf20Sopenharmony_ci snd_emu10k1_playback_stop_voice(emu, epcm->voices[1]); 7768c2ecf20Sopenharmony_ci snd_emu10k1_playback_stop_voice(emu, epcm->extra); 7778c2ecf20Sopenharmony_ci break; 7788c2ecf20Sopenharmony_ci default: 7798c2ecf20Sopenharmony_ci result = -EINVAL; 7808c2ecf20Sopenharmony_ci break; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci spin_unlock(&emu->reg_lock); 7838c2ecf20Sopenharmony_ci return result; 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_cistatic int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream, 7878c2ecf20Sopenharmony_ci int cmd) 7888c2ecf20Sopenharmony_ci{ 7898c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 7908c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 7918c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 7928c2ecf20Sopenharmony_ci int result = 0; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci spin_lock(&emu->reg_lock); 7958c2ecf20Sopenharmony_ci switch (cmd) { 7968c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 7978c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 7988c2ecf20Sopenharmony_ci /* hmm this should cause full and half full interrupt to be raised? */ 7998c2ecf20Sopenharmony_ci outl(epcm->capture_ipr, emu->port + IPR); 8008c2ecf20Sopenharmony_ci snd_emu10k1_intr_enable(emu, epcm->capture_inte); 8018c2ecf20Sopenharmony_ci /* 8028c2ecf20Sopenharmony_ci dev_dbg(emu->card->dev, "adccr = 0x%x, adcbs = 0x%x\n", 8038c2ecf20Sopenharmony_ci epcm->adccr, epcm->adcbs); 8048c2ecf20Sopenharmony_ci */ 8058c2ecf20Sopenharmony_ci switch (epcm->type) { 8068c2ecf20Sopenharmony_ci case CAPTURE_AC97ADC: 8078c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, ADCCR, 0, epcm->capture_cr_val); 8088c2ecf20Sopenharmony_ci break; 8098c2ecf20Sopenharmony_ci case CAPTURE_EFX: 8108c2ecf20Sopenharmony_ci if (emu->audigy) { 8118c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, A_FXWC1, 0, epcm->capture_cr_val); 8128c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, A_FXWC2, 0, epcm->capture_cr_val2); 8138c2ecf20Sopenharmony_ci dev_dbg(emu->card->dev, 8148c2ecf20Sopenharmony_ci "cr_val=0x%x, cr_val2=0x%x\n", 8158c2ecf20Sopenharmony_ci epcm->capture_cr_val, 8168c2ecf20Sopenharmony_ci epcm->capture_cr_val2); 8178c2ecf20Sopenharmony_ci } else 8188c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val); 8198c2ecf20Sopenharmony_ci break; 8208c2ecf20Sopenharmony_ci default: 8218c2ecf20Sopenharmony_ci break; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, epcm->capture_bs_val); 8248c2ecf20Sopenharmony_ci epcm->running = 1; 8258c2ecf20Sopenharmony_ci epcm->first_ptr = 1; 8268c2ecf20Sopenharmony_ci break; 8278c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 8288c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 8298c2ecf20Sopenharmony_ci epcm->running = 0; 8308c2ecf20Sopenharmony_ci snd_emu10k1_intr_disable(emu, epcm->capture_inte); 8318c2ecf20Sopenharmony_ci outl(epcm->capture_ipr, emu->port + IPR); 8328c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0); 8338c2ecf20Sopenharmony_ci switch (epcm->type) { 8348c2ecf20Sopenharmony_ci case CAPTURE_AC97ADC: 8358c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, ADCCR, 0, 0); 8368c2ecf20Sopenharmony_ci break; 8378c2ecf20Sopenharmony_ci case CAPTURE_EFX: 8388c2ecf20Sopenharmony_ci if (emu->audigy) { 8398c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0); 8408c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0); 8418c2ecf20Sopenharmony_ci } else 8428c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, FXWC, 0, 0); 8438c2ecf20Sopenharmony_ci break; 8448c2ecf20Sopenharmony_ci default: 8458c2ecf20Sopenharmony_ci break; 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci break; 8488c2ecf20Sopenharmony_ci default: 8498c2ecf20Sopenharmony_ci result = -EINVAL; 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci spin_unlock(&emu->reg_lock); 8528c2ecf20Sopenharmony_ci return result; 8538c2ecf20Sopenharmony_ci} 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_substream *substream) 8568c2ecf20Sopenharmony_ci{ 8578c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 8588c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 8598c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 8608c2ecf20Sopenharmony_ci unsigned int ptr; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci if (!epcm->running) 8638c2ecf20Sopenharmony_ci return 0; 8648c2ecf20Sopenharmony_ci ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff; 8658c2ecf20Sopenharmony_ci#if 0 /* Perex's code */ 8668c2ecf20Sopenharmony_ci ptr += runtime->buffer_size; 8678c2ecf20Sopenharmony_ci ptr -= epcm->ccca_start_addr; 8688c2ecf20Sopenharmony_ci ptr %= runtime->buffer_size; 8698c2ecf20Sopenharmony_ci#else /* EMU10K1 Open Source code from Creative */ 8708c2ecf20Sopenharmony_ci if (ptr < epcm->ccca_start_addr) 8718c2ecf20Sopenharmony_ci ptr += runtime->buffer_size - epcm->ccca_start_addr; 8728c2ecf20Sopenharmony_ci else { 8738c2ecf20Sopenharmony_ci ptr -= epcm->ccca_start_addr; 8748c2ecf20Sopenharmony_ci if (ptr >= runtime->buffer_size) 8758c2ecf20Sopenharmony_ci ptr -= runtime->buffer_size; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci#endif 8788c2ecf20Sopenharmony_ci /* 8798c2ecf20Sopenharmony_ci dev_dbg(emu->card->dev, 8808c2ecf20Sopenharmony_ci "ptr = 0x%lx, buffer_size = 0x%lx, period_size = 0x%lx\n", 8818c2ecf20Sopenharmony_ci (long)ptr, (long)runtime->buffer_size, 8828c2ecf20Sopenharmony_ci (long)runtime->period_size); 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_ci return ptr; 8858c2ecf20Sopenharmony_ci} 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistatic int snd_emu10k1_efx_playback_trigger(struct snd_pcm_substream *substream, 8898c2ecf20Sopenharmony_ci int cmd) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 8928c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 8938c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 8948c2ecf20Sopenharmony_ci int i; 8958c2ecf20Sopenharmony_ci int result = 0; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci spin_lock(&emu->reg_lock); 8988c2ecf20Sopenharmony_ci switch (cmd) { 8998c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 9008c2ecf20Sopenharmony_ci /* prepare voices */ 9018c2ecf20Sopenharmony_ci for (i = 0; i < NUM_EFX_PLAYBACK; i++) { 9028c2ecf20Sopenharmony_ci snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[i]); 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra); 9058c2ecf20Sopenharmony_ci fallthrough; 9068c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 9078c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 9088c2ecf20Sopenharmony_ci snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL); 9098c2ecf20Sopenharmony_ci snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 0, 0, 9108c2ecf20Sopenharmony_ci &emu->efx_pcm_mixer[0]); 9118c2ecf20Sopenharmony_ci for (i = 1; i < NUM_EFX_PLAYBACK; i++) 9128c2ecf20Sopenharmony_ci snd_emu10k1_playback_prepare_voice(emu, epcm->voices[i], 0, 0, 9138c2ecf20Sopenharmony_ci &emu->efx_pcm_mixer[i]); 9148c2ecf20Sopenharmony_ci snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 0, 0); 9158c2ecf20Sopenharmony_ci snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1); 9168c2ecf20Sopenharmony_ci for (i = 1; i < NUM_EFX_PLAYBACK; i++) 9178c2ecf20Sopenharmony_ci snd_emu10k1_playback_trigger_voice(emu, epcm->voices[i], 0, 0); 9188c2ecf20Sopenharmony_ci epcm->running = 1; 9198c2ecf20Sopenharmony_ci break; 9208c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 9218c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 9228c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 9238c2ecf20Sopenharmony_ci epcm->running = 0; 9248c2ecf20Sopenharmony_ci for (i = 0; i < NUM_EFX_PLAYBACK; i++) { 9258c2ecf20Sopenharmony_ci snd_emu10k1_playback_stop_voice(emu, epcm->voices[i]); 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci snd_emu10k1_playback_stop_voice(emu, epcm->extra); 9288c2ecf20Sopenharmony_ci break; 9298c2ecf20Sopenharmony_ci default: 9308c2ecf20Sopenharmony_ci result = -EINVAL; 9318c2ecf20Sopenharmony_ci break; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci spin_unlock(&emu->reg_lock); 9348c2ecf20Sopenharmony_ci return result; 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_emu10k1_capture_pointer(struct snd_pcm_substream *substream) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 9418c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 9428c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 9438c2ecf20Sopenharmony_ci unsigned int ptr; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci if (!epcm->running) 9468c2ecf20Sopenharmony_ci return 0; 9478c2ecf20Sopenharmony_ci if (epcm->first_ptr) { 9488c2ecf20Sopenharmony_ci udelay(50); /* hack, it takes awhile until capture is started */ 9498c2ecf20Sopenharmony_ci epcm->first_ptr = 0; 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci ptr = snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff; 9528c2ecf20Sopenharmony_ci return bytes_to_frames(runtime, ptr); 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci/* 9568c2ecf20Sopenharmony_ci * Playback support device description 9578c2ecf20Sopenharmony_ci */ 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_emu10k1_playback = 9608c2ecf20Sopenharmony_ci{ 9618c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 9628c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 9638c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 9648c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), 9658c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 9668c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_96000, 9678c2ecf20Sopenharmony_ci .rate_min = 4000, 9688c2ecf20Sopenharmony_ci .rate_max = 96000, 9698c2ecf20Sopenharmony_ci .channels_min = 1, 9708c2ecf20Sopenharmony_ci .channels_max = 2, 9718c2ecf20Sopenharmony_ci .buffer_bytes_max = (128*1024), 9728c2ecf20Sopenharmony_ci .period_bytes_min = 64, 9738c2ecf20Sopenharmony_ci .period_bytes_max = (128*1024), 9748c2ecf20Sopenharmony_ci .periods_min = 1, 9758c2ecf20Sopenharmony_ci .periods_max = 1024, 9768c2ecf20Sopenharmony_ci .fifo_size = 0, 9778c2ecf20Sopenharmony_ci}; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci/* 9808c2ecf20Sopenharmony_ci * Capture support device description 9818c2ecf20Sopenharmony_ci */ 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_emu10k1_capture = 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 9868c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 9878c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 9888c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 9898c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 9908c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 9918c2ecf20Sopenharmony_ci .rate_min = 8000, 9928c2ecf20Sopenharmony_ci .rate_max = 48000, 9938c2ecf20Sopenharmony_ci .channels_min = 1, 9948c2ecf20Sopenharmony_ci .channels_max = 2, 9958c2ecf20Sopenharmony_ci .buffer_bytes_max = (64*1024), 9968c2ecf20Sopenharmony_ci .period_bytes_min = 384, 9978c2ecf20Sopenharmony_ci .period_bytes_max = (64*1024), 9988c2ecf20Sopenharmony_ci .periods_min = 2, 9998c2ecf20Sopenharmony_ci .periods_max = 2, 10008c2ecf20Sopenharmony_ci .fifo_size = 0, 10018c2ecf20Sopenharmony_ci}; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_emu10k1_capture_efx = 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 10068c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 10078c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 10088c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 10098c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 10108c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 10118c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | 10128c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, 10138c2ecf20Sopenharmony_ci .rate_min = 44100, 10148c2ecf20Sopenharmony_ci .rate_max = 192000, 10158c2ecf20Sopenharmony_ci .channels_min = 8, 10168c2ecf20Sopenharmony_ci .channels_max = 8, 10178c2ecf20Sopenharmony_ci .buffer_bytes_max = (64*1024), 10188c2ecf20Sopenharmony_ci .period_bytes_min = 384, 10198c2ecf20Sopenharmony_ci .period_bytes_max = (64*1024), 10208c2ecf20Sopenharmony_ci .periods_min = 2, 10218c2ecf20Sopenharmony_ci .periods_max = 2, 10228c2ecf20Sopenharmony_ci .fifo_size = 0, 10238c2ecf20Sopenharmony_ci}; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci/* 10268c2ecf20Sopenharmony_ci * 10278c2ecf20Sopenharmony_ci */ 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_cistatic void snd_emu10k1_pcm_mixer_notify1(struct snd_emu10k1 *emu, struct snd_kcontrol *kctl, int idx, int activate) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci struct snd_ctl_elem_id id; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci if (! kctl) 10348c2ecf20Sopenharmony_ci return; 10358c2ecf20Sopenharmony_ci if (activate) 10368c2ecf20Sopenharmony_ci kctl->vd[idx].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 10378c2ecf20Sopenharmony_ci else 10388c2ecf20Sopenharmony_ci kctl->vd[idx].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; 10398c2ecf20Sopenharmony_ci snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE | 10408c2ecf20Sopenharmony_ci SNDRV_CTL_EVENT_MASK_INFO, 10418c2ecf20Sopenharmony_ci snd_ctl_build_ioff(&id, kctl, idx)); 10428c2ecf20Sopenharmony_ci} 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_cistatic void snd_emu10k1_pcm_mixer_notify(struct snd_emu10k1 *emu, int idx, int activate) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_send_routing, idx, activate); 10478c2ecf20Sopenharmony_ci snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_send_volume, idx, activate); 10488c2ecf20Sopenharmony_ci snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_attn, idx, activate); 10498c2ecf20Sopenharmony_ci} 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_cistatic void snd_emu10k1_pcm_efx_mixer_notify(struct snd_emu10k1 *emu, int idx, int activate) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_send_routing, idx, activate); 10548c2ecf20Sopenharmony_ci snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_send_volume, idx, activate); 10558c2ecf20Sopenharmony_ci snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_attn, idx, activate); 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_cistatic void snd_emu10k1_pcm_free_substream(struct snd_pcm_runtime *runtime) 10598c2ecf20Sopenharmony_ci{ 10608c2ecf20Sopenharmony_ci kfree(runtime->private_data); 10618c2ecf20Sopenharmony_ci} 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cistatic int snd_emu10k1_efx_playback_close(struct snd_pcm_substream *substream) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 10668c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm_mixer *mix; 10678c2ecf20Sopenharmony_ci int i; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci for (i = 0; i < NUM_EFX_PLAYBACK; i++) { 10708c2ecf20Sopenharmony_ci mix = &emu->efx_pcm_mixer[i]; 10718c2ecf20Sopenharmony_ci mix->epcm = NULL; 10728c2ecf20Sopenharmony_ci snd_emu10k1_pcm_efx_mixer_notify(emu, i, 0); 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci return 0; 10758c2ecf20Sopenharmony_ci} 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cistatic int snd_emu10k1_efx_playback_open(struct snd_pcm_substream *substream) 10788c2ecf20Sopenharmony_ci{ 10798c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 10808c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm; 10818c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm_mixer *mix; 10828c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 10838c2ecf20Sopenharmony_ci int i; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 10868c2ecf20Sopenharmony_ci if (epcm == NULL) 10878c2ecf20Sopenharmony_ci return -ENOMEM; 10888c2ecf20Sopenharmony_ci epcm->emu = emu; 10898c2ecf20Sopenharmony_ci epcm->type = PLAYBACK_EFX; 10908c2ecf20Sopenharmony_ci epcm->substream = substream; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci emu->pcm_playback_efx_substream = substream; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci runtime->private_data = epcm; 10958c2ecf20Sopenharmony_ci runtime->private_free = snd_emu10k1_pcm_free_substream; 10968c2ecf20Sopenharmony_ci runtime->hw = snd_emu10k1_efx_playback; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci for (i = 0; i < NUM_EFX_PLAYBACK; i++) { 10998c2ecf20Sopenharmony_ci mix = &emu->efx_pcm_mixer[i]; 11008c2ecf20Sopenharmony_ci mix->send_routing[0][0] = i; 11018c2ecf20Sopenharmony_ci memset(&mix->send_volume, 0, sizeof(mix->send_volume)); 11028c2ecf20Sopenharmony_ci mix->send_volume[0][0] = 255; 11038c2ecf20Sopenharmony_ci mix->attn[0] = 0xffff; 11048c2ecf20Sopenharmony_ci mix->epcm = epcm; 11058c2ecf20Sopenharmony_ci snd_emu10k1_pcm_efx_mixer_notify(emu, i, 1); 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci return 0; 11088c2ecf20Sopenharmony_ci} 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_cistatic int snd_emu10k1_playback_open(struct snd_pcm_substream *substream) 11118c2ecf20Sopenharmony_ci{ 11128c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 11138c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm; 11148c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm_mixer *mix; 11158c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 11168c2ecf20Sopenharmony_ci int i, err, sample_rate; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 11198c2ecf20Sopenharmony_ci if (epcm == NULL) 11208c2ecf20Sopenharmony_ci return -ENOMEM; 11218c2ecf20Sopenharmony_ci epcm->emu = emu; 11228c2ecf20Sopenharmony_ci epcm->type = PLAYBACK_EMUVOICE; 11238c2ecf20Sopenharmony_ci epcm->substream = substream; 11248c2ecf20Sopenharmony_ci runtime->private_data = epcm; 11258c2ecf20Sopenharmony_ci runtime->private_free = snd_emu10k1_pcm_free_substream; 11268c2ecf20Sopenharmony_ci runtime->hw = snd_emu10k1_playback; 11278c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) { 11288c2ecf20Sopenharmony_ci kfree(epcm); 11298c2ecf20Sopenharmony_ci return err; 11308c2ecf20Sopenharmony_ci } 11318c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) { 11328c2ecf20Sopenharmony_ci kfree(epcm); 11338c2ecf20Sopenharmony_ci return err; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci if (emu->card_capabilities->emu_model && emu->emu1010.internal_clock == 0) 11368c2ecf20Sopenharmony_ci sample_rate = 44100; 11378c2ecf20Sopenharmony_ci else 11388c2ecf20Sopenharmony_ci sample_rate = 48000; 11398c2ecf20Sopenharmony_ci err = snd_pcm_hw_rule_noresample(runtime, sample_rate); 11408c2ecf20Sopenharmony_ci if (err < 0) { 11418c2ecf20Sopenharmony_ci kfree(epcm); 11428c2ecf20Sopenharmony_ci return err; 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci mix = &emu->pcm_mixer[substream->number]; 11458c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 11468c2ecf20Sopenharmony_ci mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i; 11478c2ecf20Sopenharmony_ci memset(&mix->send_volume, 0, sizeof(mix->send_volume)); 11488c2ecf20Sopenharmony_ci mix->send_volume[0][0] = mix->send_volume[0][1] = 11498c2ecf20Sopenharmony_ci mix->send_volume[1][0] = mix->send_volume[2][1] = 255; 11508c2ecf20Sopenharmony_ci mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff; 11518c2ecf20Sopenharmony_ci mix->epcm = epcm; 11528c2ecf20Sopenharmony_ci snd_emu10k1_pcm_mixer_notify(emu, substream->number, 1); 11538c2ecf20Sopenharmony_ci return 0; 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic int snd_emu10k1_playback_close(struct snd_pcm_substream *substream) 11578c2ecf20Sopenharmony_ci{ 11588c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 11598c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm_mixer *mix = &emu->pcm_mixer[substream->number]; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci mix->epcm = NULL; 11628c2ecf20Sopenharmony_ci snd_emu10k1_pcm_mixer_notify(emu, substream->number, 0); 11638c2ecf20Sopenharmony_ci return 0; 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_cistatic int snd_emu10k1_capture_open(struct snd_pcm_substream *substream) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 11698c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 11708c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 11738c2ecf20Sopenharmony_ci if (epcm == NULL) 11748c2ecf20Sopenharmony_ci return -ENOMEM; 11758c2ecf20Sopenharmony_ci epcm->emu = emu; 11768c2ecf20Sopenharmony_ci epcm->type = CAPTURE_AC97ADC; 11778c2ecf20Sopenharmony_ci epcm->substream = substream; 11788c2ecf20Sopenharmony_ci epcm->capture_ipr = IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL; 11798c2ecf20Sopenharmony_ci epcm->capture_inte = INTE_ADCBUFENABLE; 11808c2ecf20Sopenharmony_ci epcm->capture_ba_reg = ADCBA; 11818c2ecf20Sopenharmony_ci epcm->capture_bs_reg = ADCBS; 11828c2ecf20Sopenharmony_ci epcm->capture_idx_reg = emu->audigy ? A_ADCIDX : ADCIDX; 11838c2ecf20Sopenharmony_ci runtime->private_data = epcm; 11848c2ecf20Sopenharmony_ci runtime->private_free = snd_emu10k1_pcm_free_substream; 11858c2ecf20Sopenharmony_ci runtime->hw = snd_emu10k1_capture; 11868c2ecf20Sopenharmony_ci emu->capture_interrupt = snd_emu10k1_pcm_ac97adc_interrupt; 11878c2ecf20Sopenharmony_ci emu->pcm_capture_substream = substream; 11888c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes); 11898c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_capture_rates); 11908c2ecf20Sopenharmony_ci return 0; 11918c2ecf20Sopenharmony_ci} 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic int snd_emu10k1_capture_close(struct snd_pcm_substream *substream) 11948c2ecf20Sopenharmony_ci{ 11958c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci emu->capture_interrupt = NULL; 11988c2ecf20Sopenharmony_ci emu->pcm_capture_substream = NULL; 11998c2ecf20Sopenharmony_ci return 0; 12008c2ecf20Sopenharmony_ci} 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_cistatic int snd_emu10k1_capture_mic_open(struct snd_pcm_substream *substream) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 12058c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm; 12068c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 12098c2ecf20Sopenharmony_ci if (epcm == NULL) 12108c2ecf20Sopenharmony_ci return -ENOMEM; 12118c2ecf20Sopenharmony_ci epcm->emu = emu; 12128c2ecf20Sopenharmony_ci epcm->type = CAPTURE_AC97MIC; 12138c2ecf20Sopenharmony_ci epcm->substream = substream; 12148c2ecf20Sopenharmony_ci epcm->capture_ipr = IPR_MICBUFFULL|IPR_MICBUFHALFFULL; 12158c2ecf20Sopenharmony_ci epcm->capture_inte = INTE_MICBUFENABLE; 12168c2ecf20Sopenharmony_ci epcm->capture_ba_reg = MICBA; 12178c2ecf20Sopenharmony_ci epcm->capture_bs_reg = MICBS; 12188c2ecf20Sopenharmony_ci epcm->capture_idx_reg = emu->audigy ? A_MICIDX : MICIDX; 12198c2ecf20Sopenharmony_ci substream->runtime->private_data = epcm; 12208c2ecf20Sopenharmony_ci substream->runtime->private_free = snd_emu10k1_pcm_free_substream; 12218c2ecf20Sopenharmony_ci runtime->hw = snd_emu10k1_capture; 12228c2ecf20Sopenharmony_ci runtime->hw.rates = SNDRV_PCM_RATE_8000; 12238c2ecf20Sopenharmony_ci runtime->hw.rate_min = runtime->hw.rate_max = 8000; 12248c2ecf20Sopenharmony_ci runtime->hw.channels_min = 1; 12258c2ecf20Sopenharmony_ci emu->capture_mic_interrupt = snd_emu10k1_pcm_ac97mic_interrupt; 12268c2ecf20Sopenharmony_ci emu->pcm_capture_mic_substream = substream; 12278c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes); 12288c2ecf20Sopenharmony_ci return 0; 12298c2ecf20Sopenharmony_ci} 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_cistatic int snd_emu10k1_capture_mic_close(struct snd_pcm_substream *substream) 12328c2ecf20Sopenharmony_ci{ 12338c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci emu->capture_mic_interrupt = NULL; 12368c2ecf20Sopenharmony_ci emu->pcm_capture_mic_substream = NULL; 12378c2ecf20Sopenharmony_ci return 0; 12388c2ecf20Sopenharmony_ci} 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_cistatic int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream) 12418c2ecf20Sopenharmony_ci{ 12428c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 12438c2ecf20Sopenharmony_ci struct snd_emu10k1_pcm *epcm; 12448c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 12458c2ecf20Sopenharmony_ci int nefx = emu->audigy ? 64 : 32; 12468c2ecf20Sopenharmony_ci int idx; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 12498c2ecf20Sopenharmony_ci if (epcm == NULL) 12508c2ecf20Sopenharmony_ci return -ENOMEM; 12518c2ecf20Sopenharmony_ci epcm->emu = emu; 12528c2ecf20Sopenharmony_ci epcm->type = CAPTURE_EFX; 12538c2ecf20Sopenharmony_ci epcm->substream = substream; 12548c2ecf20Sopenharmony_ci epcm->capture_ipr = IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL; 12558c2ecf20Sopenharmony_ci epcm->capture_inte = INTE_EFXBUFENABLE; 12568c2ecf20Sopenharmony_ci epcm->capture_ba_reg = FXBA; 12578c2ecf20Sopenharmony_ci epcm->capture_bs_reg = FXBS; 12588c2ecf20Sopenharmony_ci epcm->capture_idx_reg = FXIDX; 12598c2ecf20Sopenharmony_ci substream->runtime->private_data = epcm; 12608c2ecf20Sopenharmony_ci substream->runtime->private_free = snd_emu10k1_pcm_free_substream; 12618c2ecf20Sopenharmony_ci runtime->hw = snd_emu10k1_capture_efx; 12628c2ecf20Sopenharmony_ci runtime->hw.rates = SNDRV_PCM_RATE_48000; 12638c2ecf20Sopenharmony_ci runtime->hw.rate_min = runtime->hw.rate_max = 48000; 12648c2ecf20Sopenharmony_ci spin_lock_irq(&emu->reg_lock); 12658c2ecf20Sopenharmony_ci if (emu->card_capabilities->emu_model) { 12668c2ecf20Sopenharmony_ci /* Nb. of channels has been increased to 16 */ 12678c2ecf20Sopenharmony_ci /* TODO 12688c2ecf20Sopenharmony_ci * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE 12698c2ecf20Sopenharmony_ci * SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 12708c2ecf20Sopenharmony_ci * SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | 12718c2ecf20Sopenharmony_ci * SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 12728c2ecf20Sopenharmony_ci * rate_min = 44100, 12738c2ecf20Sopenharmony_ci * rate_max = 192000, 12748c2ecf20Sopenharmony_ci * channels_min = 16, 12758c2ecf20Sopenharmony_ci * channels_max = 16, 12768c2ecf20Sopenharmony_ci * Need to add mixer control to fix sample rate 12778c2ecf20Sopenharmony_ci * 12788c2ecf20Sopenharmony_ci * There are 32 mono channels of 16bits each. 12798c2ecf20Sopenharmony_ci * 24bit Audio uses 2x channels over 16bit 12808c2ecf20Sopenharmony_ci * 96kHz uses 2x channels over 48kHz 12818c2ecf20Sopenharmony_ci * 192kHz uses 4x channels over 48kHz 12828c2ecf20Sopenharmony_ci * So, for 48kHz 24bit, one has 16 channels 12838c2ecf20Sopenharmony_ci * for 96kHz 24bit, one has 8 channels 12848c2ecf20Sopenharmony_ci * for 192kHz 24bit, one has 4 channels 12858c2ecf20Sopenharmony_ci * 12868c2ecf20Sopenharmony_ci */ 12878c2ecf20Sopenharmony_ci#if 1 12888c2ecf20Sopenharmony_ci switch (emu->emu1010.internal_clock) { 12898c2ecf20Sopenharmony_ci case 0: 12908c2ecf20Sopenharmony_ci /* For 44.1kHz */ 12918c2ecf20Sopenharmony_ci runtime->hw.rates = SNDRV_PCM_RATE_44100; 12928c2ecf20Sopenharmony_ci runtime->hw.rate_min = runtime->hw.rate_max = 44100; 12938c2ecf20Sopenharmony_ci runtime->hw.channels_min = 12948c2ecf20Sopenharmony_ci runtime->hw.channels_max = 16; 12958c2ecf20Sopenharmony_ci break; 12968c2ecf20Sopenharmony_ci case 1: 12978c2ecf20Sopenharmony_ci /* For 48kHz */ 12988c2ecf20Sopenharmony_ci runtime->hw.rates = SNDRV_PCM_RATE_48000; 12998c2ecf20Sopenharmony_ci runtime->hw.rate_min = runtime->hw.rate_max = 48000; 13008c2ecf20Sopenharmony_ci runtime->hw.channels_min = 13018c2ecf20Sopenharmony_ci runtime->hw.channels_max = 16; 13028c2ecf20Sopenharmony_ci break; 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci#endif 13058c2ecf20Sopenharmony_ci#if 0 13068c2ecf20Sopenharmony_ci /* For 96kHz */ 13078c2ecf20Sopenharmony_ci runtime->hw.rates = SNDRV_PCM_RATE_96000; 13088c2ecf20Sopenharmony_ci runtime->hw.rate_min = runtime->hw.rate_max = 96000; 13098c2ecf20Sopenharmony_ci runtime->hw.channels_min = runtime->hw.channels_max = 4; 13108c2ecf20Sopenharmony_ci#endif 13118c2ecf20Sopenharmony_ci#if 0 13128c2ecf20Sopenharmony_ci /* For 192kHz */ 13138c2ecf20Sopenharmony_ci runtime->hw.rates = SNDRV_PCM_RATE_192000; 13148c2ecf20Sopenharmony_ci runtime->hw.rate_min = runtime->hw.rate_max = 192000; 13158c2ecf20Sopenharmony_ci runtime->hw.channels_min = runtime->hw.channels_max = 2; 13168c2ecf20Sopenharmony_ci#endif 13178c2ecf20Sopenharmony_ci runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; 13188c2ecf20Sopenharmony_ci /* efx_voices_mask[0] is expected to be zero 13198c2ecf20Sopenharmony_ci * efx_voices_mask[1] is expected to have 32bits set 13208c2ecf20Sopenharmony_ci */ 13218c2ecf20Sopenharmony_ci } else { 13228c2ecf20Sopenharmony_ci runtime->hw.channels_min = runtime->hw.channels_max = 0; 13238c2ecf20Sopenharmony_ci for (idx = 0; idx < nefx; idx++) { 13248c2ecf20Sopenharmony_ci if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) { 13258c2ecf20Sopenharmony_ci runtime->hw.channels_min++; 13268c2ecf20Sopenharmony_ci runtime->hw.channels_max++; 13278c2ecf20Sopenharmony_ci } 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci epcm->capture_cr_val = emu->efx_voices_mask[0]; 13318c2ecf20Sopenharmony_ci epcm->capture_cr_val2 = emu->efx_voices_mask[1]; 13328c2ecf20Sopenharmony_ci spin_unlock_irq(&emu->reg_lock); 13338c2ecf20Sopenharmony_ci emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt; 13348c2ecf20Sopenharmony_ci emu->pcm_capture_efx_substream = substream; 13358c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes); 13368c2ecf20Sopenharmony_ci return 0; 13378c2ecf20Sopenharmony_ci} 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_cistatic int snd_emu10k1_capture_efx_close(struct snd_pcm_substream *substream) 13408c2ecf20Sopenharmony_ci{ 13418c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci emu->capture_efx_interrupt = NULL; 13448c2ecf20Sopenharmony_ci emu->pcm_capture_efx_substream = NULL; 13458c2ecf20Sopenharmony_ci return 0; 13468c2ecf20Sopenharmony_ci} 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1_playback_ops = { 13498c2ecf20Sopenharmony_ci .open = snd_emu10k1_playback_open, 13508c2ecf20Sopenharmony_ci .close = snd_emu10k1_playback_close, 13518c2ecf20Sopenharmony_ci .hw_params = snd_emu10k1_playback_hw_params, 13528c2ecf20Sopenharmony_ci .hw_free = snd_emu10k1_playback_hw_free, 13538c2ecf20Sopenharmony_ci .prepare = snd_emu10k1_playback_prepare, 13548c2ecf20Sopenharmony_ci .trigger = snd_emu10k1_playback_trigger, 13558c2ecf20Sopenharmony_ci .pointer = snd_emu10k1_playback_pointer, 13568c2ecf20Sopenharmony_ci}; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1_capture_ops = { 13598c2ecf20Sopenharmony_ci .open = snd_emu10k1_capture_open, 13608c2ecf20Sopenharmony_ci .close = snd_emu10k1_capture_close, 13618c2ecf20Sopenharmony_ci .prepare = snd_emu10k1_capture_prepare, 13628c2ecf20Sopenharmony_ci .trigger = snd_emu10k1_capture_trigger, 13638c2ecf20Sopenharmony_ci .pointer = snd_emu10k1_capture_pointer, 13648c2ecf20Sopenharmony_ci}; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci/* EFX playback */ 13678c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1_efx_playback_ops = { 13688c2ecf20Sopenharmony_ci .open = snd_emu10k1_efx_playback_open, 13698c2ecf20Sopenharmony_ci .close = snd_emu10k1_efx_playback_close, 13708c2ecf20Sopenharmony_ci .hw_params = snd_emu10k1_playback_hw_params, 13718c2ecf20Sopenharmony_ci .hw_free = snd_emu10k1_efx_playback_hw_free, 13728c2ecf20Sopenharmony_ci .prepare = snd_emu10k1_efx_playback_prepare, 13738c2ecf20Sopenharmony_ci .trigger = snd_emu10k1_efx_playback_trigger, 13748c2ecf20Sopenharmony_ci .pointer = snd_emu10k1_efx_playback_pointer, 13758c2ecf20Sopenharmony_ci}; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ciint snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device) 13788c2ecf20Sopenharmony_ci{ 13798c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 13808c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 13818c2ecf20Sopenharmony_ci int err; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci if ((err = snd_pcm_new(emu->card, "emu10k1", device, 32, 1, &pcm)) < 0) 13848c2ecf20Sopenharmony_ci return err; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci pcm->private_data = emu; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_playback_ops); 13898c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_ops); 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci pcm->info_flags = 0; 13928c2ecf20Sopenharmony_ci pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; 13938c2ecf20Sopenharmony_ci strcpy(pcm->name, "ADC Capture/Standard PCM Playback"); 13948c2ecf20Sopenharmony_ci emu->pcm = pcm; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci /* playback substream can't use managed buffers due to alignment */ 13978c2ecf20Sopenharmony_ci for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) 13988c2ecf20Sopenharmony_ci snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, 13998c2ecf20Sopenharmony_ci &emu->pci->dev, 14008c2ecf20Sopenharmony_ci 64*1024, 64*1024); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) 14038c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV, 14048c2ecf20Sopenharmony_ci &emu->pci->dev, 64*1024, 64*1024); 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci return 0; 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ciint snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device) 14108c2ecf20Sopenharmony_ci{ 14118c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 14128c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 14138c2ecf20Sopenharmony_ci int err; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci if ((err = snd_pcm_new(emu->card, "emu10k1", device, 1, 0, &pcm)) < 0) 14168c2ecf20Sopenharmony_ci return err; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci pcm->private_data = emu; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_efx_playback_ops); 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci pcm->info_flags = 0; 14238c2ecf20Sopenharmony_ci pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; 14248c2ecf20Sopenharmony_ci strcpy(pcm->name, "Multichannel Playback"); 14258c2ecf20Sopenharmony_ci emu->pcm_multi = pcm; 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) 14288c2ecf20Sopenharmony_ci snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, 14298c2ecf20Sopenharmony_ci &emu->pci->dev, 14308c2ecf20Sopenharmony_ci 64*1024, 64*1024); 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci return 0; 14338c2ecf20Sopenharmony_ci} 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1_capture_mic_ops = { 14378c2ecf20Sopenharmony_ci .open = snd_emu10k1_capture_mic_open, 14388c2ecf20Sopenharmony_ci .close = snd_emu10k1_capture_mic_close, 14398c2ecf20Sopenharmony_ci .prepare = snd_emu10k1_capture_prepare, 14408c2ecf20Sopenharmony_ci .trigger = snd_emu10k1_capture_trigger, 14418c2ecf20Sopenharmony_ci .pointer = snd_emu10k1_capture_pointer, 14428c2ecf20Sopenharmony_ci}; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ciint snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device) 14458c2ecf20Sopenharmony_ci{ 14468c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 14478c2ecf20Sopenharmony_ci int err; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci if ((err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm)) < 0) 14508c2ecf20Sopenharmony_ci return err; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci pcm->private_data = emu; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops); 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci pcm->info_flags = 0; 14578c2ecf20Sopenharmony_ci strcpy(pcm->name, "Mic Capture"); 14588c2ecf20Sopenharmony_ci emu->pcm_mic = pcm; 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &emu->pci->dev, 14618c2ecf20Sopenharmony_ci 64*1024, 64*1024); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci return 0; 14648c2ecf20Sopenharmony_ci} 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_cistatic int snd_emu10k1_pcm_efx_voices_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 14678c2ecf20Sopenharmony_ci{ 14688c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); 14698c2ecf20Sopenharmony_ci int nefx = emu->audigy ? 64 : 32; 14708c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 14718c2ecf20Sopenharmony_ci uinfo->count = nefx; 14728c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 14738c2ecf20Sopenharmony_ci uinfo->value.integer.max = 1; 14748c2ecf20Sopenharmony_ci return 0; 14758c2ecf20Sopenharmony_ci} 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_cistatic int snd_emu10k1_pcm_efx_voices_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 14788c2ecf20Sopenharmony_ci{ 14798c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); 14808c2ecf20Sopenharmony_ci int nefx = emu->audigy ? 64 : 32; 14818c2ecf20Sopenharmony_ci int idx; 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci spin_lock_irq(&emu->reg_lock); 14848c2ecf20Sopenharmony_ci for (idx = 0; idx < nefx; idx++) 14858c2ecf20Sopenharmony_ci ucontrol->value.integer.value[idx] = (emu->efx_voices_mask[idx / 32] & (1 << (idx % 32))) ? 1 : 0; 14868c2ecf20Sopenharmony_ci spin_unlock_irq(&emu->reg_lock); 14878c2ecf20Sopenharmony_ci return 0; 14888c2ecf20Sopenharmony_ci} 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_cistatic int snd_emu10k1_pcm_efx_voices_mask_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 14918c2ecf20Sopenharmony_ci{ 14928c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); 14938c2ecf20Sopenharmony_ci unsigned int nval[2], bits; 14948c2ecf20Sopenharmony_ci int nefx = emu->audigy ? 64 : 32; 14958c2ecf20Sopenharmony_ci int nefxb = emu->audigy ? 7 : 6; 14968c2ecf20Sopenharmony_ci int change, idx; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci nval[0] = nval[1] = 0; 14998c2ecf20Sopenharmony_ci for (idx = 0, bits = 0; idx < nefx; idx++) 15008c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[idx]) { 15018c2ecf20Sopenharmony_ci nval[idx / 32] |= 1 << (idx % 32); 15028c2ecf20Sopenharmony_ci bits++; 15038c2ecf20Sopenharmony_ci } 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci for (idx = 0; idx < nefxb; idx++) 15068c2ecf20Sopenharmony_ci if (1 << idx == bits) 15078c2ecf20Sopenharmony_ci break; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci if (idx >= nefxb) 15108c2ecf20Sopenharmony_ci return -EINVAL; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci spin_lock_irq(&emu->reg_lock); 15138c2ecf20Sopenharmony_ci change = (nval[0] != emu->efx_voices_mask[0]) || 15148c2ecf20Sopenharmony_ci (nval[1] != emu->efx_voices_mask[1]); 15158c2ecf20Sopenharmony_ci emu->efx_voices_mask[0] = nval[0]; 15168c2ecf20Sopenharmony_ci emu->efx_voices_mask[1] = nval[1]; 15178c2ecf20Sopenharmony_ci spin_unlock_irq(&emu->reg_lock); 15188c2ecf20Sopenharmony_ci return change; 15198c2ecf20Sopenharmony_ci} 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = { 15228c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 15238c2ecf20Sopenharmony_ci .name = "Captured FX8010 Outputs", 15248c2ecf20Sopenharmony_ci .info = snd_emu10k1_pcm_efx_voices_mask_info, 15258c2ecf20Sopenharmony_ci .get = snd_emu10k1_pcm_efx_voices_mask_get, 15268c2ecf20Sopenharmony_ci .put = snd_emu10k1_pcm_efx_voices_mask_put 15278c2ecf20Sopenharmony_ci}; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1_capture_efx_ops = { 15308c2ecf20Sopenharmony_ci .open = snd_emu10k1_capture_efx_open, 15318c2ecf20Sopenharmony_ci .close = snd_emu10k1_capture_efx_close, 15328c2ecf20Sopenharmony_ci .prepare = snd_emu10k1_capture_prepare, 15338c2ecf20Sopenharmony_ci .trigger = snd_emu10k1_capture_trigger, 15348c2ecf20Sopenharmony_ci .pointer = snd_emu10k1_capture_pointer, 15358c2ecf20Sopenharmony_ci}; 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci/* EFX playback */ 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci#define INITIAL_TRAM_SHIFT 14 15418c2ecf20Sopenharmony_ci#define INITIAL_TRAM_POS(size) ((((size) / 2) - INITIAL_TRAM_SHIFT) - 1) 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_cistatic void snd_emu10k1_fx8010_playback_irq(struct snd_emu10k1 *emu, void *private_data) 15448c2ecf20Sopenharmony_ci{ 15458c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = private_data; 15468c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream); 15478c2ecf20Sopenharmony_ci} 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_cistatic void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left, 15508c2ecf20Sopenharmony_ci unsigned short *dst_right, 15518c2ecf20Sopenharmony_ci unsigned short *src, 15528c2ecf20Sopenharmony_ci unsigned int count, 15538c2ecf20Sopenharmony_ci unsigned int tram_shift) 15548c2ecf20Sopenharmony_ci{ 15558c2ecf20Sopenharmony_ci /* 15568c2ecf20Sopenharmony_ci dev_dbg(emu->card->dev, 15578c2ecf20Sopenharmony_ci "tram_poke1: dst_left = 0x%p, dst_right = 0x%p, " 15588c2ecf20Sopenharmony_ci "src = 0x%p, count = 0x%x\n", 15598c2ecf20Sopenharmony_ci dst_left, dst_right, src, count); 15608c2ecf20Sopenharmony_ci */ 15618c2ecf20Sopenharmony_ci if ((tram_shift & 1) == 0) { 15628c2ecf20Sopenharmony_ci while (count--) { 15638c2ecf20Sopenharmony_ci *dst_left-- = *src++; 15648c2ecf20Sopenharmony_ci *dst_right-- = *src++; 15658c2ecf20Sopenharmony_ci } 15668c2ecf20Sopenharmony_ci } else { 15678c2ecf20Sopenharmony_ci while (count--) { 15688c2ecf20Sopenharmony_ci *dst_right-- = *src++; 15698c2ecf20Sopenharmony_ci *dst_left-- = *src++; 15708c2ecf20Sopenharmony_ci } 15718c2ecf20Sopenharmony_ci } 15728c2ecf20Sopenharmony_ci} 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_cistatic void fx8010_pb_trans_copy(struct snd_pcm_substream *substream, 15758c2ecf20Sopenharmony_ci struct snd_pcm_indirect *rec, size_t bytes) 15768c2ecf20Sopenharmony_ci{ 15778c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 15788c2ecf20Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 15798c2ecf20Sopenharmony_ci unsigned int tram_size = pcm->buffer_size; 15808c2ecf20Sopenharmony_ci unsigned short *src = (unsigned short *)(substream->runtime->dma_area + rec->sw_data); 15818c2ecf20Sopenharmony_ci unsigned int frames = bytes >> 2, count; 15828c2ecf20Sopenharmony_ci unsigned int tram_pos = pcm->tram_pos; 15838c2ecf20Sopenharmony_ci unsigned int tram_shift = pcm->tram_shift; 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci while (frames > tram_pos) { 15868c2ecf20Sopenharmony_ci count = tram_pos + 1; 15878c2ecf20Sopenharmony_ci snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos, 15888c2ecf20Sopenharmony_ci (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2, 15898c2ecf20Sopenharmony_ci src, count, tram_shift); 15908c2ecf20Sopenharmony_ci src += count * 2; 15918c2ecf20Sopenharmony_ci frames -= count; 15928c2ecf20Sopenharmony_ci tram_pos = (tram_size / 2) - 1; 15938c2ecf20Sopenharmony_ci tram_shift++; 15948c2ecf20Sopenharmony_ci } 15958c2ecf20Sopenharmony_ci snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos, 15968c2ecf20Sopenharmony_ci (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2, 15978c2ecf20Sopenharmony_ci src, frames, tram_shift); 15988c2ecf20Sopenharmony_ci tram_pos -= frames; 15998c2ecf20Sopenharmony_ci pcm->tram_pos = tram_pos; 16008c2ecf20Sopenharmony_ci pcm->tram_shift = tram_shift; 16018c2ecf20Sopenharmony_ci} 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_cistatic int snd_emu10k1_fx8010_playback_transfer(struct snd_pcm_substream *substream) 16048c2ecf20Sopenharmony_ci{ 16058c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 16068c2ecf20Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci return snd_pcm_indirect_playback_transfer(substream, &pcm->pcm_rec, 16098c2ecf20Sopenharmony_ci fx8010_pb_trans_copy); 16108c2ecf20Sopenharmony_ci} 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_cistatic int snd_emu10k1_fx8010_playback_hw_free(struct snd_pcm_substream *substream) 16138c2ecf20Sopenharmony_ci{ 16148c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 16158c2ecf20Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 16168c2ecf20Sopenharmony_ci unsigned int i; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci for (i = 0; i < pcm->channels; i++) 16198c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, 0); 16208c2ecf20Sopenharmony_ci return 0; 16218c2ecf20Sopenharmony_ci} 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_cistatic int snd_emu10k1_fx8010_playback_prepare(struct snd_pcm_substream *substream) 16248c2ecf20Sopenharmony_ci{ 16258c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 16268c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 16278c2ecf20Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 16288c2ecf20Sopenharmony_ci unsigned int i; 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci /* 16318c2ecf20Sopenharmony_ci dev_dbg(emu->card->dev, "prepare: etram_pages = 0x%p, dma_area = 0x%x, " 16328c2ecf20Sopenharmony_ci "buffer_size = 0x%x (0x%x)\n", 16338c2ecf20Sopenharmony_ci emu->fx8010.etram_pages, runtime->dma_area, 16348c2ecf20Sopenharmony_ci runtime->buffer_size, runtime->buffer_size << 2); 16358c2ecf20Sopenharmony_ci */ 16368c2ecf20Sopenharmony_ci memset(&pcm->pcm_rec, 0, sizeof(pcm->pcm_rec)); 16378c2ecf20Sopenharmony_ci pcm->pcm_rec.hw_buffer_size = pcm->buffer_size * 2; /* byte size */ 16388c2ecf20Sopenharmony_ci pcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); 16398c2ecf20Sopenharmony_ci pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); 16408c2ecf20Sopenharmony_ci pcm->tram_shift = 0; 16418c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_running, 0, 0); /* reset */ 16428c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); /* reset */ 16438c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_size, 0, runtime->buffer_size); 16448c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_ptr, 0, 0); /* reset ptr number */ 16458c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_count, 0, runtime->period_size); 16468c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_tmpcount, 0, runtime->period_size); 16478c2ecf20Sopenharmony_ci for (i = 0; i < pcm->channels; i++) 16488c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, (TANKMEMADDRREG_READ|TANKMEMADDRREG_ALIGN) + i * (runtime->buffer_size / pcm->channels)); 16498c2ecf20Sopenharmony_ci return 0; 16508c2ecf20Sopenharmony_ci} 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_cistatic int snd_emu10k1_fx8010_playback_trigger(struct snd_pcm_substream *substream, int cmd) 16538c2ecf20Sopenharmony_ci{ 16548c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 16558c2ecf20Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 16568c2ecf20Sopenharmony_ci int result = 0; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci spin_lock(&emu->reg_lock); 16598c2ecf20Sopenharmony_ci switch (cmd) { 16608c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 16618c2ecf20Sopenharmony_ci /* follow thru */ 16628c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 16638c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 16648c2ecf20Sopenharmony_ci#ifdef EMU10K1_SET_AC3_IEC958 16658c2ecf20Sopenharmony_ci { 16668c2ecf20Sopenharmony_ci int i; 16678c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 16688c2ecf20Sopenharmony_ci unsigned int bits; 16698c2ecf20Sopenharmony_ci bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | 16708c2ecf20Sopenharmony_ci SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 16718c2ecf20Sopenharmony_ci 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT | SPCS_NOTAUDIODATA; 16728c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, SPCS0 + i, 0, bits); 16738c2ecf20Sopenharmony_ci } 16748c2ecf20Sopenharmony_ci } 16758c2ecf20Sopenharmony_ci#endif 16768c2ecf20Sopenharmony_ci result = snd_emu10k1_fx8010_register_irq_handler(emu, snd_emu10k1_fx8010_playback_irq, pcm->gpr_running, substream, &pcm->irq); 16778c2ecf20Sopenharmony_ci if (result < 0) 16788c2ecf20Sopenharmony_ci goto __err; 16798c2ecf20Sopenharmony_ci snd_emu10k1_fx8010_playback_transfer(substream); /* roll the ball */ 16808c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 1); 16818c2ecf20Sopenharmony_ci break; 16828c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 16838c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 16848c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 16858c2ecf20Sopenharmony_ci snd_emu10k1_fx8010_unregister_irq_handler(emu, &pcm->irq); 16868c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); 16878c2ecf20Sopenharmony_ci pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); 16888c2ecf20Sopenharmony_ci pcm->tram_shift = 0; 16898c2ecf20Sopenharmony_ci break; 16908c2ecf20Sopenharmony_ci default: 16918c2ecf20Sopenharmony_ci result = -EINVAL; 16928c2ecf20Sopenharmony_ci break; 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci __err: 16958c2ecf20Sopenharmony_ci spin_unlock(&emu->reg_lock); 16968c2ecf20Sopenharmony_ci return result; 16978c2ecf20Sopenharmony_ci} 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(struct snd_pcm_substream *substream) 17008c2ecf20Sopenharmony_ci{ 17018c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 17028c2ecf20Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 17038c2ecf20Sopenharmony_ci size_t ptr; /* byte pointer */ 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci if (!snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_trigger, 0)) 17068c2ecf20Sopenharmony_ci return 0; 17078c2ecf20Sopenharmony_ci ptr = snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_ptr, 0) << 2; 17088c2ecf20Sopenharmony_ci return snd_pcm_indirect_playback_pointer(substream, &pcm->pcm_rec, ptr); 17098c2ecf20Sopenharmony_ci} 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_emu10k1_fx8010_playback = 17128c2ecf20Sopenharmony_ci{ 17138c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 17148c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 17158c2ecf20Sopenharmony_ci /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE | 17168c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_SYNC_APPLPTR), 17178c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 17188c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 17198c2ecf20Sopenharmony_ci .rate_min = 48000, 17208c2ecf20Sopenharmony_ci .rate_max = 48000, 17218c2ecf20Sopenharmony_ci .channels_min = 1, 17228c2ecf20Sopenharmony_ci .channels_max = 1, 17238c2ecf20Sopenharmony_ci .buffer_bytes_max = (128*1024), 17248c2ecf20Sopenharmony_ci .period_bytes_min = 1024, 17258c2ecf20Sopenharmony_ci .period_bytes_max = (128*1024), 17268c2ecf20Sopenharmony_ci .periods_min = 2, 17278c2ecf20Sopenharmony_ci .periods_max = 1024, 17288c2ecf20Sopenharmony_ci .fifo_size = 0, 17298c2ecf20Sopenharmony_ci}; 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_cistatic int snd_emu10k1_fx8010_playback_open(struct snd_pcm_substream *substream) 17328c2ecf20Sopenharmony_ci{ 17338c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 17348c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 17358c2ecf20Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci runtime->hw = snd_emu10k1_fx8010_playback; 17388c2ecf20Sopenharmony_ci runtime->hw.channels_min = runtime->hw.channels_max = pcm->channels; 17398c2ecf20Sopenharmony_ci runtime->hw.period_bytes_max = (pcm->buffer_size * 2) / 2; 17408c2ecf20Sopenharmony_ci spin_lock_irq(&emu->reg_lock); 17418c2ecf20Sopenharmony_ci if (pcm->valid == 0) { 17428c2ecf20Sopenharmony_ci spin_unlock_irq(&emu->reg_lock); 17438c2ecf20Sopenharmony_ci return -ENODEV; 17448c2ecf20Sopenharmony_ci } 17458c2ecf20Sopenharmony_ci pcm->opened = 1; 17468c2ecf20Sopenharmony_ci spin_unlock_irq(&emu->reg_lock); 17478c2ecf20Sopenharmony_ci return 0; 17488c2ecf20Sopenharmony_ci} 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_cistatic int snd_emu10k1_fx8010_playback_close(struct snd_pcm_substream *substream) 17518c2ecf20Sopenharmony_ci{ 17528c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 17538c2ecf20Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci spin_lock_irq(&emu->reg_lock); 17568c2ecf20Sopenharmony_ci pcm->opened = 0; 17578c2ecf20Sopenharmony_ci spin_unlock_irq(&emu->reg_lock); 17588c2ecf20Sopenharmony_ci return 0; 17598c2ecf20Sopenharmony_ci} 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1_fx8010_playback_ops = { 17628c2ecf20Sopenharmony_ci .open = snd_emu10k1_fx8010_playback_open, 17638c2ecf20Sopenharmony_ci .close = snd_emu10k1_fx8010_playback_close, 17648c2ecf20Sopenharmony_ci .hw_free = snd_emu10k1_fx8010_playback_hw_free, 17658c2ecf20Sopenharmony_ci .prepare = snd_emu10k1_fx8010_playback_prepare, 17668c2ecf20Sopenharmony_ci .trigger = snd_emu10k1_fx8010_playback_trigger, 17678c2ecf20Sopenharmony_ci .pointer = snd_emu10k1_fx8010_playback_pointer, 17688c2ecf20Sopenharmony_ci .ack = snd_emu10k1_fx8010_playback_transfer, 17698c2ecf20Sopenharmony_ci}; 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ciint snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device) 17728c2ecf20Sopenharmony_ci{ 17738c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 17748c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 17758c2ecf20Sopenharmony_ci int err; 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci if ((err = snd_pcm_new(emu->card, "emu10k1 efx", device, 8, 1, &pcm)) < 0) 17788c2ecf20Sopenharmony_ci return err; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci pcm->private_data = emu; 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops); 17838c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops); 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci pcm->info_flags = 0; 17868c2ecf20Sopenharmony_ci strcpy(pcm->name, "Multichannel Capture/PT Playback"); 17878c2ecf20Sopenharmony_ci emu->pcm_efx = pcm; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci /* EFX capture - record the "FXBUS2" channels, by default we connect the EXTINs 17908c2ecf20Sopenharmony_ci * to these 17918c2ecf20Sopenharmony_ci */ 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci /* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */ 17948c2ecf20Sopenharmony_ci if (emu->audigy) { 17958c2ecf20Sopenharmony_ci emu->efx_voices_mask[0] = 0; 17968c2ecf20Sopenharmony_ci if (emu->card_capabilities->emu_model) 17978c2ecf20Sopenharmony_ci /* Pavel Hofman - 32 voices will be used for 17988c2ecf20Sopenharmony_ci * capture (write mode) - 17998c2ecf20Sopenharmony_ci * each bit = corresponding voice 18008c2ecf20Sopenharmony_ci */ 18018c2ecf20Sopenharmony_ci emu->efx_voices_mask[1] = 0xffffffff; 18028c2ecf20Sopenharmony_ci else 18038c2ecf20Sopenharmony_ci emu->efx_voices_mask[1] = 0xffff; 18048c2ecf20Sopenharmony_ci } else { 18058c2ecf20Sopenharmony_ci emu->efx_voices_mask[0] = 0xffff0000; 18068c2ecf20Sopenharmony_ci emu->efx_voices_mask[1] = 0; 18078c2ecf20Sopenharmony_ci } 18088c2ecf20Sopenharmony_ci /* For emu1010, the control has to set 32 upper bits (voices) 18098c2ecf20Sopenharmony_ci * out of the 64 bits (voices) to true for the 16-channels capture 18108c2ecf20Sopenharmony_ci * to work correctly. Correct A_FXWC2 initial value (0xffffffff) 18118c2ecf20Sopenharmony_ci * is already defined but the snd_emu10k1_pcm_efx_voices_mask 18128c2ecf20Sopenharmony_ci * control can override this register's value. 18138c2ecf20Sopenharmony_ci */ 18148c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu); 18158c2ecf20Sopenharmony_ci if (!kctl) 18168c2ecf20Sopenharmony_ci return -ENOMEM; 18178c2ecf20Sopenharmony_ci kctl->id.device = device; 18188c2ecf20Sopenharmony_ci err = snd_ctl_add(emu->card, kctl); 18198c2ecf20Sopenharmony_ci if (err < 0) 18208c2ecf20Sopenharmony_ci return err; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &emu->pci->dev, 18238c2ecf20Sopenharmony_ci 64*1024, 64*1024); 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci return 0; 18268c2ecf20Sopenharmony_ci} 1827