162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 462306a36Sopenharmony_ci * Lee Revell <rlrevell@joe-job.com> 562306a36Sopenharmony_ci * James Courtier-Dutton <James@superbug.co.uk> 662306a36Sopenharmony_ci * Oswald Buddenhagen <oswald.buddenhagen@gmx.de> 762306a36Sopenharmony_ci * Creative Labs, Inc. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Routines for control of EMU10K1 chips / PCM routines 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/pci.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/time.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <sound/core.h> 1862306a36Sopenharmony_ci#include <sound/emu10k1.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic void snd_emu10k1_pcm_interrupt(struct snd_emu10k1 *emu, 2162306a36Sopenharmony_ci struct snd_emu10k1_voice *voice) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci epcm = voice->epcm; 2662306a36Sopenharmony_ci if (!epcm) 2762306a36Sopenharmony_ci return; 2862306a36Sopenharmony_ci if (epcm->substream == NULL) 2962306a36Sopenharmony_ci return; 3062306a36Sopenharmony_ci#if 0 3162306a36Sopenharmony_ci dev_dbg(emu->card->dev, 3262306a36Sopenharmony_ci "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n", 3362306a36Sopenharmony_ci epcm->substream->runtime->hw->pointer(emu, epcm->substream), 3462306a36Sopenharmony_ci snd_pcm_lib_period_bytes(epcm->substream), 3562306a36Sopenharmony_ci snd_pcm_lib_buffer_bytes(epcm->substream)); 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci snd_pcm_period_elapsed(epcm->substream); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic void snd_emu10k1_pcm_ac97adc_interrupt(struct snd_emu10k1 *emu, 4162306a36Sopenharmony_ci unsigned int status) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci#if 0 4462306a36Sopenharmony_ci if (status & IPR_ADCBUFHALFFULL) { 4562306a36Sopenharmony_ci if (emu->pcm_capture_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) 4662306a36Sopenharmony_ci return; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci#endif 4962306a36Sopenharmony_ci snd_pcm_period_elapsed(emu->pcm_capture_substream); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void snd_emu10k1_pcm_ac97mic_interrupt(struct snd_emu10k1 *emu, 5362306a36Sopenharmony_ci unsigned int status) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci#if 0 5662306a36Sopenharmony_ci if (status & IPR_MICBUFHALFFULL) { 5762306a36Sopenharmony_ci if (emu->pcm_capture_mic_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) 5862306a36Sopenharmony_ci return; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci#endif 6162306a36Sopenharmony_ci snd_pcm_period_elapsed(emu->pcm_capture_mic_substream); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void snd_emu10k1_pcm_efx_interrupt(struct snd_emu10k1 *emu, 6562306a36Sopenharmony_ci unsigned int status) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci#if 0 6862306a36Sopenharmony_ci if (status & IPR_EFXBUFHALFFULL) { 6962306a36Sopenharmony_ci if (emu->pcm_capture_efx_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) 7062306a36Sopenharmony_ci return; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci#endif 7362306a36Sopenharmony_ci snd_pcm_period_elapsed(emu->pcm_capture_efx_substream); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void snd_emu10k1_pcm_free_voices(struct snd_emu10k1_pcm *epcm) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci for (unsigned i = 0; i < ARRAY_SIZE(epcm->voices); i++) { 7962306a36Sopenharmony_ci if (epcm->voices[i]) { 8062306a36Sopenharmony_ci snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]); 8162306a36Sopenharmony_ci epcm->voices[i] = NULL; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm *epcm, 8762306a36Sopenharmony_ci int type, int count, int channels) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci int err; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci snd_emu10k1_pcm_free_voices(epcm); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci err = snd_emu10k1_voice_alloc(epcm->emu, 9462306a36Sopenharmony_ci type, count, channels, 9562306a36Sopenharmony_ci epcm, &epcm->voices[0]); 9662306a36Sopenharmony_ci if (err < 0) 9762306a36Sopenharmony_ci return err; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (epcm->extra == NULL) { 10062306a36Sopenharmony_ci // The hardware supports only (half-)loop interrupts, so to support an 10162306a36Sopenharmony_ci // arbitrary number of periods per buffer, we use an extra voice with a 10262306a36Sopenharmony_ci // period-sized loop as the interrupt source. Additionally, the interrupt 10362306a36Sopenharmony_ci // timing of the hardware is "suboptimal" and needs some compensation. 10462306a36Sopenharmony_ci err = snd_emu10k1_voice_alloc(epcm->emu, 10562306a36Sopenharmony_ci type + 1, 1, 1, 10662306a36Sopenharmony_ci epcm, &epcm->extra); 10762306a36Sopenharmony_ci if (err < 0) { 10862306a36Sopenharmony_ci /* 10962306a36Sopenharmony_ci dev_dbg(emu->card->dev, "pcm_channel_alloc: " 11062306a36Sopenharmony_ci "failed extra: voices=%d, frame=%d\n", 11162306a36Sopenharmony_ci voices, frame); 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci snd_emu10k1_pcm_free_voices(epcm); 11462306a36Sopenharmony_ci return err; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci epcm->extra->interrupt = snd_emu10k1_pcm_interrupt; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci// Primes 2-7 and 2^n multiples thereof, up to 16. 12362306a36Sopenharmony_cistatic const unsigned int efx_capture_channels[] = { 12462306a36Sopenharmony_ci 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_efx_capture_channels = { 12862306a36Sopenharmony_ci .count = ARRAY_SIZE(efx_capture_channels), 12962306a36Sopenharmony_ci .list = efx_capture_channels, 13062306a36Sopenharmony_ci .mask = 0 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic const unsigned int capture_buffer_sizes[31] = { 13462306a36Sopenharmony_ci 384, 448, 512, 640, 13562306a36Sopenharmony_ci 384*2, 448*2, 512*2, 640*2, 13662306a36Sopenharmony_ci 384*4, 448*4, 512*4, 640*4, 13762306a36Sopenharmony_ci 384*8, 448*8, 512*8, 640*8, 13862306a36Sopenharmony_ci 384*16, 448*16, 512*16, 640*16, 13962306a36Sopenharmony_ci 384*32, 448*32, 512*32, 640*32, 14062306a36Sopenharmony_ci 384*64, 448*64, 512*64, 640*64, 14162306a36Sopenharmony_ci 384*128,448*128,512*128 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_capture_buffer_sizes = { 14562306a36Sopenharmony_ci .count = 31, 14662306a36Sopenharmony_ci .list = capture_buffer_sizes, 14762306a36Sopenharmony_ci .mask = 0 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic const unsigned int capture_rates[8] = { 15162306a36Sopenharmony_ci 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_capture_rates = { 15562306a36Sopenharmony_ci .count = 8, 15662306a36Sopenharmony_ci .list = capture_rates, 15762306a36Sopenharmony_ci .mask = 0 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic unsigned int snd_emu10k1_capture_rate_reg(unsigned int rate) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci switch (rate) { 16362306a36Sopenharmony_ci case 8000: return ADCCR_SAMPLERATE_8; 16462306a36Sopenharmony_ci case 11025: return ADCCR_SAMPLERATE_11; 16562306a36Sopenharmony_ci case 16000: return ADCCR_SAMPLERATE_16; 16662306a36Sopenharmony_ci case 22050: return ADCCR_SAMPLERATE_22; 16762306a36Sopenharmony_ci case 24000: return ADCCR_SAMPLERATE_24; 16862306a36Sopenharmony_ci case 32000: return ADCCR_SAMPLERATE_32; 16962306a36Sopenharmony_ci case 44100: return ADCCR_SAMPLERATE_44; 17062306a36Sopenharmony_ci case 48000: return ADCCR_SAMPLERATE_48; 17162306a36Sopenharmony_ci default: 17262306a36Sopenharmony_ci snd_BUG(); 17362306a36Sopenharmony_ci return ADCCR_SAMPLERATE_8; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic const unsigned int audigy_capture_rates[9] = { 17862306a36Sopenharmony_ci 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_audigy_capture_rates = { 18262306a36Sopenharmony_ci .count = 9, 18362306a36Sopenharmony_ci .list = audigy_capture_rates, 18462306a36Sopenharmony_ci .mask = 0 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci switch (rate) { 19062306a36Sopenharmony_ci case 8000: return A_ADCCR_SAMPLERATE_8; 19162306a36Sopenharmony_ci case 11025: return A_ADCCR_SAMPLERATE_11; 19262306a36Sopenharmony_ci case 12000: return A_ADCCR_SAMPLERATE_12; 19362306a36Sopenharmony_ci case 16000: return ADCCR_SAMPLERATE_16; 19462306a36Sopenharmony_ci case 22050: return ADCCR_SAMPLERATE_22; 19562306a36Sopenharmony_ci case 24000: return ADCCR_SAMPLERATE_24; 19662306a36Sopenharmony_ci case 32000: return ADCCR_SAMPLERATE_32; 19762306a36Sopenharmony_ci case 44100: return ADCCR_SAMPLERATE_44; 19862306a36Sopenharmony_ci case 48000: return ADCCR_SAMPLERATE_48; 19962306a36Sopenharmony_ci default: 20062306a36Sopenharmony_ci snd_BUG(); 20162306a36Sopenharmony_ci return A_ADCCR_SAMPLERATE_8; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void snd_emu10k1_constrain_capture_rates(struct snd_emu10k1 *emu, 20662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci if (emu->card_capabilities->emu_model && 20962306a36Sopenharmony_ci emu->emu1010.word_clock == 44100) { 21062306a36Sopenharmony_ci // This also sets the rate constraint by deleting SNDRV_PCM_RATE_KNOT 21162306a36Sopenharmony_ci runtime->hw.rates = SNDRV_PCM_RATE_11025 | \ 21262306a36Sopenharmony_ci SNDRV_PCM_RATE_22050 | \ 21362306a36Sopenharmony_ci SNDRV_PCM_RATE_44100; 21462306a36Sopenharmony_ci runtime->hw.rate_min = 11025; 21562306a36Sopenharmony_ci runtime->hw.rate_max = 44100; 21662306a36Sopenharmony_ci return; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 21962306a36Sopenharmony_ci emu->audigy ? &hw_constraints_audigy_capture_rates : 22062306a36Sopenharmony_ci &hw_constraints_capture_rates); 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic void snd_emu1010_constrain_efx_rate(struct snd_emu10k1 *emu, 22462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci int rate; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci rate = emu->emu1010.word_clock; 22962306a36Sopenharmony_ci runtime->hw.rate_min = runtime->hw.rate_max = rate; 23062306a36Sopenharmony_ci runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic unsigned int emu10k1_calc_pitch_target(unsigned int rate) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci unsigned int pitch_target; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci pitch_target = (rate << 8) / 375; 23862306a36Sopenharmony_ci pitch_target = (pitch_target >> 1) + (pitch_target & 1); 23962306a36Sopenharmony_ci return pitch_target; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci#define PITCH_48000 0x00004000 24362306a36Sopenharmony_ci#define PITCH_96000 0x00008000 24462306a36Sopenharmony_ci#define PITCH_85000 0x00007155 24562306a36Sopenharmony_ci#define PITCH_80726 0x00006ba2 24662306a36Sopenharmony_ci#define PITCH_67882 0x00005a82 24762306a36Sopenharmony_ci#define PITCH_57081 0x00004c1c 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic unsigned int emu10k1_select_interprom(unsigned int pitch_target) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci if (pitch_target == PITCH_48000) 25262306a36Sopenharmony_ci return CCCA_INTERPROM_0; 25362306a36Sopenharmony_ci else if (pitch_target < PITCH_48000) 25462306a36Sopenharmony_ci return CCCA_INTERPROM_1; 25562306a36Sopenharmony_ci else if (pitch_target >= PITCH_96000) 25662306a36Sopenharmony_ci return CCCA_INTERPROM_0; 25762306a36Sopenharmony_ci else if (pitch_target >= PITCH_85000) 25862306a36Sopenharmony_ci return CCCA_INTERPROM_6; 25962306a36Sopenharmony_ci else if (pitch_target >= PITCH_80726) 26062306a36Sopenharmony_ci return CCCA_INTERPROM_5; 26162306a36Sopenharmony_ci else if (pitch_target >= PITCH_67882) 26262306a36Sopenharmony_ci return CCCA_INTERPROM_4; 26362306a36Sopenharmony_ci else if (pitch_target >= PITCH_57081) 26462306a36Sopenharmony_ci return CCCA_INTERPROM_3; 26562306a36Sopenharmony_ci else 26662306a36Sopenharmony_ci return CCCA_INTERPROM_2; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic u16 emu10k1_send_target_from_amount(u8 amount) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci static const u8 shifts[8] = { 4, 4, 5, 6, 7, 8, 9, 10 }; 27262306a36Sopenharmony_ci static const u16 offsets[8] = { 0, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000 }; 27362306a36Sopenharmony_ci u8 exp; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (amount == 0xff) 27662306a36Sopenharmony_ci return 0xffff; 27762306a36Sopenharmony_ci exp = amount >> 5; 27862306a36Sopenharmony_ci return ((amount & 0x1f) << shifts[exp]) + offsets[exp]; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu, 28262306a36Sopenharmony_ci struct snd_emu10k1_voice *evoice, 28362306a36Sopenharmony_ci bool w_16, bool stereo, 28462306a36Sopenharmony_ci unsigned int start_addr, 28562306a36Sopenharmony_ci unsigned int end_addr, 28662306a36Sopenharmony_ci const unsigned char *send_routing, 28762306a36Sopenharmony_ci const unsigned char *send_amount) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci unsigned int silent_page; 29062306a36Sopenharmony_ci int voice; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci voice = evoice->number; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci silent_page = ((unsigned int)emu->silent_page.addr << emu->address_mode) | 29562306a36Sopenharmony_ci (emu->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0); 29662306a36Sopenharmony_ci snd_emu10k1_ptr_write_multiple(emu, voice, 29762306a36Sopenharmony_ci // Not really necessary for the slave, but it doesn't hurt 29862306a36Sopenharmony_ci CPF, stereo ? CPF_STEREO_MASK : 0, 29962306a36Sopenharmony_ci // Assumption that PT is already 0 so no harm overwriting 30062306a36Sopenharmony_ci PTRX, (send_amount[0] << 8) | send_amount[1], 30162306a36Sopenharmony_ci // Stereo slaves don't need to have the addresses set, but it doesn't hurt 30262306a36Sopenharmony_ci DSL, end_addr | (send_amount[3] << 24), 30362306a36Sopenharmony_ci PSST, start_addr | (send_amount[2] << 24), 30462306a36Sopenharmony_ci CCCA, emu10k1_select_interprom(evoice->epcm->pitch_target) | 30562306a36Sopenharmony_ci (w_16 ? 0 : CCCA_8BITSELECT), 30662306a36Sopenharmony_ci // Clear filter delay memory 30762306a36Sopenharmony_ci Z1, 0, 30862306a36Sopenharmony_ci Z2, 0, 30962306a36Sopenharmony_ci // Invalidate maps 31062306a36Sopenharmony_ci MAPA, silent_page, 31162306a36Sopenharmony_ci MAPB, silent_page, 31262306a36Sopenharmony_ci // Disable filter (in conjunction with CCCA_RESONANCE == 0) 31362306a36Sopenharmony_ci VTFT, VTFT_FILTERTARGET_MASK, 31462306a36Sopenharmony_ci CVCF, CVCF_CURRENTFILTER_MASK, 31562306a36Sopenharmony_ci REGLIST_END); 31662306a36Sopenharmony_ci // Setup routing 31762306a36Sopenharmony_ci if (emu->audigy) { 31862306a36Sopenharmony_ci snd_emu10k1_ptr_write_multiple(emu, voice, 31962306a36Sopenharmony_ci A_FXRT1, snd_emu10k1_compose_audigy_fxrt1(send_routing), 32062306a36Sopenharmony_ci A_FXRT2, snd_emu10k1_compose_audigy_fxrt2(send_routing), 32162306a36Sopenharmony_ci A_SENDAMOUNTS, snd_emu10k1_compose_audigy_sendamounts(send_amount), 32262306a36Sopenharmony_ci REGLIST_END); 32362306a36Sopenharmony_ci for (int i = 0; i < 4; i++) { 32462306a36Sopenharmony_ci u32 aml = emu10k1_send_target_from_amount(send_amount[2 * i]); 32562306a36Sopenharmony_ci u32 amh = emu10k1_send_target_from_amount(send_amount[2 * i + 1]); 32662306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, A_CSBA + i, voice, (amh << 16) | aml); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci } else { 32962306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, FXRT, voice, 33062306a36Sopenharmony_ci snd_emu10k1_compose_send_routing(send_routing)); 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci emu->voices[voice].dirty = 1; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic void snd_emu10k1_pcm_init_voices(struct snd_emu10k1 *emu, 33762306a36Sopenharmony_ci struct snd_emu10k1_voice *evoice, 33862306a36Sopenharmony_ci bool w_16, bool stereo, 33962306a36Sopenharmony_ci unsigned int start_addr, 34062306a36Sopenharmony_ci unsigned int end_addr, 34162306a36Sopenharmony_ci struct snd_emu10k1_pcm_mixer *mix) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci spin_lock_irq(&emu->reg_lock); 34462306a36Sopenharmony_ci snd_emu10k1_pcm_init_voice(emu, evoice, w_16, stereo, 34562306a36Sopenharmony_ci start_addr, end_addr, 34662306a36Sopenharmony_ci &mix->send_routing[stereo][0], 34762306a36Sopenharmony_ci &mix->send_volume[stereo][0]); 34862306a36Sopenharmony_ci if (stereo) 34962306a36Sopenharmony_ci snd_emu10k1_pcm_init_voice(emu, evoice + 1, w_16, true, 35062306a36Sopenharmony_ci start_addr, end_addr, 35162306a36Sopenharmony_ci &mix->send_routing[2][0], 35262306a36Sopenharmony_ci &mix->send_volume[2][0]); 35362306a36Sopenharmony_ci spin_unlock_irq(&emu->reg_lock); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic void snd_emu10k1_pcm_init_extra_voice(struct snd_emu10k1 *emu, 35762306a36Sopenharmony_ci struct snd_emu10k1_voice *evoice, 35862306a36Sopenharmony_ci bool w_16, 35962306a36Sopenharmony_ci unsigned int start_addr, 36062306a36Sopenharmony_ci unsigned int end_addr) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci static const unsigned char send_routing[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; 36362306a36Sopenharmony_ci static const unsigned char send_amount[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci snd_emu10k1_pcm_init_voice(emu, evoice, w_16, false, 36662306a36Sopenharmony_ci start_addr, end_addr, 36762306a36Sopenharmony_ci send_routing, send_amount); 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic int snd_emu10k1_playback_hw_params(struct snd_pcm_substream *substream, 37162306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 37462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 37562306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 37662306a36Sopenharmony_ci size_t alloc_size; 37762306a36Sopenharmony_ci int type, channels, count; 37862306a36Sopenharmony_ci int err; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (epcm->type == PLAYBACK_EMUVOICE) { 38162306a36Sopenharmony_ci type = EMU10K1_PCM; 38262306a36Sopenharmony_ci channels = 1; 38362306a36Sopenharmony_ci count = params_channels(hw_params); 38462306a36Sopenharmony_ci } else { 38562306a36Sopenharmony_ci type = EMU10K1_EFX; 38662306a36Sopenharmony_ci channels = params_channels(hw_params); 38762306a36Sopenharmony_ci count = 1; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci err = snd_emu10k1_pcm_channel_alloc(epcm, type, count, channels); 39062306a36Sopenharmony_ci if (err < 0) 39162306a36Sopenharmony_ci return err; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci alloc_size = params_buffer_bytes(hw_params); 39462306a36Sopenharmony_ci if (emu->iommu_workaround) 39562306a36Sopenharmony_ci alloc_size += EMUPAGESIZE; 39662306a36Sopenharmony_ci err = snd_pcm_lib_malloc_pages(substream, alloc_size); 39762306a36Sopenharmony_ci if (err < 0) 39862306a36Sopenharmony_ci return err; 39962306a36Sopenharmony_ci if (emu->iommu_workaround && runtime->dma_bytes >= EMUPAGESIZE) 40062306a36Sopenharmony_ci runtime->dma_bytes -= EMUPAGESIZE; 40162306a36Sopenharmony_ci if (err > 0) { /* change */ 40262306a36Sopenharmony_ci int mapped; 40362306a36Sopenharmony_ci if (epcm->memblk != NULL) 40462306a36Sopenharmony_ci snd_emu10k1_free_pages(emu, epcm->memblk); 40562306a36Sopenharmony_ci epcm->memblk = snd_emu10k1_alloc_pages(emu, substream); 40662306a36Sopenharmony_ci epcm->start_addr = 0; 40762306a36Sopenharmony_ci if (! epcm->memblk) 40862306a36Sopenharmony_ci return -ENOMEM; 40962306a36Sopenharmony_ci mapped = ((struct snd_emu10k1_memblk *)epcm->memblk)->mapped_page; 41062306a36Sopenharmony_ci if (mapped < 0) 41162306a36Sopenharmony_ci return -ENOMEM; 41262306a36Sopenharmony_ci epcm->start_addr = mapped << PAGE_SHIFT; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci return 0; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int snd_emu10k1_playback_hw_free(struct snd_pcm_substream *substream) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 42062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 42162306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (runtime->private_data == NULL) 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci epcm = runtime->private_data; 42662306a36Sopenharmony_ci if (epcm->extra) { 42762306a36Sopenharmony_ci snd_emu10k1_voice_free(epcm->emu, epcm->extra); 42862306a36Sopenharmony_ci epcm->extra = NULL; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci snd_emu10k1_pcm_free_voices(epcm); 43162306a36Sopenharmony_ci if (epcm->memblk) { 43262306a36Sopenharmony_ci snd_emu10k1_free_pages(emu, epcm->memblk); 43362306a36Sopenharmony_ci epcm->memblk = NULL; 43462306a36Sopenharmony_ci epcm->start_addr = 0; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci snd_pcm_lib_free_pages(substream); 43762306a36Sopenharmony_ci return 0; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic int snd_emu10k1_playback_prepare(struct snd_pcm_substream *substream) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 44362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 44462306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 44562306a36Sopenharmony_ci bool w_16 = snd_pcm_format_width(runtime->format) == 16; 44662306a36Sopenharmony_ci bool stereo = runtime->channels == 2; 44762306a36Sopenharmony_ci unsigned int start_addr, end_addr; 44862306a36Sopenharmony_ci unsigned int rate; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci rate = runtime->rate; 45162306a36Sopenharmony_ci if (emu->card_capabilities->emu_model && 45262306a36Sopenharmony_ci emu->emu1010.word_clock == 44100) 45362306a36Sopenharmony_ci rate = rate * 480 / 441; 45462306a36Sopenharmony_ci epcm->pitch_target = emu10k1_calc_pitch_target(rate); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci start_addr = epcm->start_addr >> w_16; 45762306a36Sopenharmony_ci end_addr = start_addr + runtime->period_size; 45862306a36Sopenharmony_ci snd_emu10k1_pcm_init_extra_voice(emu, epcm->extra, w_16, 45962306a36Sopenharmony_ci start_addr, end_addr); 46062306a36Sopenharmony_ci start_addr >>= stereo; 46162306a36Sopenharmony_ci epcm->ccca_start_addr = start_addr; 46262306a36Sopenharmony_ci end_addr = start_addr + runtime->buffer_size; 46362306a36Sopenharmony_ci snd_emu10k1_pcm_init_voices(emu, epcm->voices[0], w_16, stereo, 46462306a36Sopenharmony_ci start_addr, end_addr, 46562306a36Sopenharmony_ci &emu->pcm_mixer[substream->number]); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci return 0; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic int snd_emu10k1_efx_playback_prepare(struct snd_pcm_substream *substream) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 47362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 47462306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 47562306a36Sopenharmony_ci unsigned int start_addr; 47662306a36Sopenharmony_ci unsigned int extra_size, channel_size; 47762306a36Sopenharmony_ci unsigned int i; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci epcm->pitch_target = PITCH_48000; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci start_addr = epcm->start_addr >> 1; // 16-bit voices 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci extra_size = runtime->period_size; 48462306a36Sopenharmony_ci channel_size = runtime->buffer_size; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci snd_emu10k1_pcm_init_extra_voice(emu, epcm->extra, true, 48762306a36Sopenharmony_ci start_addr, start_addr + extra_size); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci epcm->ccca_start_addr = start_addr; 49062306a36Sopenharmony_ci for (i = 0; i < runtime->channels; i++) { 49162306a36Sopenharmony_ci snd_emu10k1_pcm_init_voices(emu, epcm->voices[i], true, false, 49262306a36Sopenharmony_ci start_addr, start_addr + channel_size, 49362306a36Sopenharmony_ci &emu->efx_pcm_mixer[i]); 49462306a36Sopenharmony_ci start_addr += channel_size; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return 0; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_emu10k1_efx_playback = 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_NONINTERLEAVED | 50362306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 50462306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 50562306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), 50662306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 50762306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 50862306a36Sopenharmony_ci .rate_min = 48000, 50962306a36Sopenharmony_ci .rate_max = 48000, 51062306a36Sopenharmony_ci .channels_min = 1, 51162306a36Sopenharmony_ci .channels_max = NUM_EFX_PLAYBACK, 51262306a36Sopenharmony_ci .buffer_bytes_max = (128*1024), 51362306a36Sopenharmony_ci .period_bytes_max = (128*1024), 51462306a36Sopenharmony_ci .periods_min = 2, 51562306a36Sopenharmony_ci .periods_max = 1024, 51662306a36Sopenharmony_ci .fifo_size = 0, 51762306a36Sopenharmony_ci}; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic int snd_emu10k1_capture_prepare(struct snd_pcm_substream *substream) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 52262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 52362306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 52462306a36Sopenharmony_ci int idx; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* zeroing the buffer size will stop capture */ 52762306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0); 52862306a36Sopenharmony_ci switch (epcm->type) { 52962306a36Sopenharmony_ci case CAPTURE_AC97ADC: 53062306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, ADCCR, 0, 0); 53162306a36Sopenharmony_ci break; 53262306a36Sopenharmony_ci case CAPTURE_EFX: 53362306a36Sopenharmony_ci if (emu->card_capabilities->emu_model) { 53462306a36Sopenharmony_ci // The upper 32 16-bit capture voices, two for each of the 16 32-bit channels. 53562306a36Sopenharmony_ci // The lower voices are occupied by A_EXTOUT_*_CAP*. 53662306a36Sopenharmony_ci epcm->capture_cr_val = 0; 53762306a36Sopenharmony_ci epcm->capture_cr_val2 = 0xffffffff >> (32 - runtime->channels * 2); 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci if (emu->audigy) { 54062306a36Sopenharmony_ci snd_emu10k1_ptr_write_multiple(emu, 0, 54162306a36Sopenharmony_ci A_FXWC1, 0, 54262306a36Sopenharmony_ci A_FXWC2, 0, 54362306a36Sopenharmony_ci REGLIST_END); 54462306a36Sopenharmony_ci } else 54562306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, FXWC, 0, 0); 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci default: 54862306a36Sopenharmony_ci break; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, epcm->capture_ba_reg, 0, runtime->dma_addr); 55162306a36Sopenharmony_ci epcm->capture_bufsize = snd_pcm_lib_buffer_bytes(substream); 55262306a36Sopenharmony_ci epcm->capture_bs_val = 0; 55362306a36Sopenharmony_ci for (idx = 0; idx < 31; idx++) { 55462306a36Sopenharmony_ci if (capture_buffer_sizes[idx] == epcm->capture_bufsize) { 55562306a36Sopenharmony_ci epcm->capture_bs_val = idx + 1; 55662306a36Sopenharmony_ci break; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci if (epcm->capture_bs_val == 0) { 56062306a36Sopenharmony_ci snd_BUG(); 56162306a36Sopenharmony_ci epcm->capture_bs_val++; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci if (epcm->type == CAPTURE_AC97ADC) { 56462306a36Sopenharmony_ci unsigned rate = runtime->rate; 56562306a36Sopenharmony_ci if (!(runtime->hw.rates & SNDRV_PCM_RATE_48000)) 56662306a36Sopenharmony_ci rate = rate * 480 / 441; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci epcm->capture_cr_val = emu->audigy ? A_ADCCR_LCHANENABLE : ADCCR_LCHANENABLE; 56962306a36Sopenharmony_ci if (runtime->channels > 1) 57062306a36Sopenharmony_ci epcm->capture_cr_val |= emu->audigy ? A_ADCCR_RCHANENABLE : ADCCR_RCHANENABLE; 57162306a36Sopenharmony_ci epcm->capture_cr_val |= emu->audigy ? 57262306a36Sopenharmony_ci snd_emu10k1_audigy_capture_rate_reg(rate) : 57362306a36Sopenharmony_ci snd_emu10k1_capture_rate_reg(rate); 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci return 0; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic void snd_emu10k1_playback_fill_cache(struct snd_emu10k1 *emu, 57962306a36Sopenharmony_ci unsigned voice, 58062306a36Sopenharmony_ci u32 sample, bool stereo) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci u32 ccr; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci // We assume that the cache is resting at this point (i.e., 58562306a36Sopenharmony_ci // CCR_CACHEINVALIDSIZE is very small). 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci // Clear leading frames. For simplicitly, this does too much, 58862306a36Sopenharmony_ci // except for 16-bit stereo. And the interpolator will actually 58962306a36Sopenharmony_ci // access them at all only when we're pitch-shifting. 59062306a36Sopenharmony_ci for (int i = 0; i < 3; i++) 59162306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci // Fill cache 59462306a36Sopenharmony_ci ccr = (64 - 3) << REG_SHIFT(CCR_CACHEINVALIDSIZE); 59562306a36Sopenharmony_ci if (stereo) { 59662306a36Sopenharmony_ci // The engine goes haywire if CCR_READADDRESS is out of sync 59762306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, CCR, voice + 1, ccr); 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, CCR, voice, ccr); 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic void snd_emu10k1_playback_prepare_voices(struct snd_emu10k1 *emu, 60362306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm, 60462306a36Sopenharmony_ci bool w_16, bool stereo, 60562306a36Sopenharmony_ci int channels) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci struct snd_pcm_substream *substream = epcm->substream; 60862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 60962306a36Sopenharmony_ci unsigned eloop_start = epcm->start_addr >> w_16; 61062306a36Sopenharmony_ci unsigned loop_start = eloop_start >> stereo; 61162306a36Sopenharmony_ci unsigned eloop_size = runtime->period_size; 61262306a36Sopenharmony_ci unsigned loop_size = runtime->buffer_size; 61362306a36Sopenharmony_ci u32 sample = w_16 ? 0 : 0x80808080; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci // To make the playback actually start at the 1st frame, 61662306a36Sopenharmony_ci // we need to compensate for two circumstances: 61762306a36Sopenharmony_ci // - The actual position is delayed by the cache size (64 frames) 61862306a36Sopenharmony_ci // - The interpolator is centered around the 4th frame 61962306a36Sopenharmony_ci loop_start += (epcm->resume_pos + 64 - 3) % loop_size; 62062306a36Sopenharmony_ci for (int i = 0; i < channels; i++) { 62162306a36Sopenharmony_ci unsigned voice = epcm->voices[i]->number; 62262306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, CCCA_CURRADDR, voice, loop_start); 62362306a36Sopenharmony_ci loop_start += loop_size; 62462306a36Sopenharmony_ci snd_emu10k1_playback_fill_cache(emu, voice, sample, stereo); 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci // The interrupt is triggered when CCCA_CURRADDR (CA) wraps around, 62862306a36Sopenharmony_ci // which is ahead of the actual playback position, so the interrupt 62962306a36Sopenharmony_ci // source needs to be delayed. 63062306a36Sopenharmony_ci // 63162306a36Sopenharmony_ci // In principle, this wouldn't need to be the cache's entire size - in 63262306a36Sopenharmony_ci // practice, CCR_CACHEINVALIDSIZE (CIS) > `fetch threshold` has never 63362306a36Sopenharmony_ci // been observed, and assuming 40 _bytes_ should be safe. 63462306a36Sopenharmony_ci // 63562306a36Sopenharmony_ci // The cache fills are somewhat random, which makes it impossible to 63662306a36Sopenharmony_ci // align them with the interrupts. This makes a non-delayed interrupt 63762306a36Sopenharmony_ci // source not practical, as the interrupt handler would have to wait 63862306a36Sopenharmony_ci // for (CA - CIS) >= period_boundary for every channel in the stream. 63962306a36Sopenharmony_ci // 64062306a36Sopenharmony_ci // This is why all other (open) drivers for these chips use timer-based 64162306a36Sopenharmony_ci // interrupts. 64262306a36Sopenharmony_ci // 64362306a36Sopenharmony_ci eloop_start += (epcm->resume_pos + eloop_size - 3) % eloop_size; 64462306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, CCCA_CURRADDR, epcm->extra->number, eloop_start); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci // It takes a moment until the cache fills complete, 64762306a36Sopenharmony_ci // but the unmuting takes long enough for that. 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic void snd_emu10k1_playback_commit_volume(struct snd_emu10k1 *emu, 65162306a36Sopenharmony_ci struct snd_emu10k1_voice *evoice, 65262306a36Sopenharmony_ci unsigned int vattn) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci snd_emu10k1_ptr_write_multiple(emu, evoice->number, 65562306a36Sopenharmony_ci VTFT, vattn | VTFT_FILTERTARGET_MASK, 65662306a36Sopenharmony_ci CVCF, vattn | CVCF_CURRENTFILTER_MASK, 65762306a36Sopenharmony_ci REGLIST_END); 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic void snd_emu10k1_playback_unmute_voice(struct snd_emu10k1 *emu, 66162306a36Sopenharmony_ci struct snd_emu10k1_voice *evoice, 66262306a36Sopenharmony_ci bool stereo, bool master, 66362306a36Sopenharmony_ci struct snd_emu10k1_pcm_mixer *mix) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci unsigned int vattn; 66662306a36Sopenharmony_ci unsigned int tmp; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci tmp = stereo ? (master ? 1 : 2) : 0; 66962306a36Sopenharmony_ci vattn = mix->attn[tmp] << 16; 67062306a36Sopenharmony_ci snd_emu10k1_playback_commit_volume(emu, evoice, vattn); 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic void snd_emu10k1_playback_unmute_voices(struct snd_emu10k1 *emu, 67462306a36Sopenharmony_ci struct snd_emu10k1_voice *evoice, 67562306a36Sopenharmony_ci bool stereo, 67662306a36Sopenharmony_ci struct snd_emu10k1_pcm_mixer *mix) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci snd_emu10k1_playback_unmute_voice(emu, evoice, stereo, true, mix); 67962306a36Sopenharmony_ci if (stereo) 68062306a36Sopenharmony_ci snd_emu10k1_playback_unmute_voice(emu, evoice + 1, true, false, mix); 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic void snd_emu10k1_playback_mute_voice(struct snd_emu10k1 *emu, 68462306a36Sopenharmony_ci struct snd_emu10k1_voice *evoice) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci snd_emu10k1_playback_commit_volume(emu, evoice, 0); 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic void snd_emu10k1_playback_mute_voices(struct snd_emu10k1 *emu, 69062306a36Sopenharmony_ci struct snd_emu10k1_voice *evoice, 69162306a36Sopenharmony_ci bool stereo) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci snd_emu10k1_playback_mute_voice(emu, evoice); 69462306a36Sopenharmony_ci if (stereo) 69562306a36Sopenharmony_ci snd_emu10k1_playback_mute_voice(emu, evoice + 1); 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic void snd_emu10k1_playback_commit_pitch(struct snd_emu10k1 *emu, 69962306a36Sopenharmony_ci u32 voice, u32 pitch_target) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci u32 ptrx = snd_emu10k1_ptr_read(emu, PTRX, voice); 70262306a36Sopenharmony_ci u32 cpf = snd_emu10k1_ptr_read(emu, CPF, voice); 70362306a36Sopenharmony_ci snd_emu10k1_ptr_write_multiple(emu, voice, 70462306a36Sopenharmony_ci PTRX, (ptrx & ~PTRX_PITCHTARGET_MASK) | pitch_target, 70562306a36Sopenharmony_ci CPF, (cpf & ~(CPF_CURRENTPITCH_MASK | CPF_FRACADDRESS_MASK)) | pitch_target, 70662306a36Sopenharmony_ci REGLIST_END); 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, 71062306a36Sopenharmony_ci struct snd_emu10k1_voice *evoice) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci unsigned int voice; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci voice = evoice->number; 71562306a36Sopenharmony_ci snd_emu10k1_playback_commit_pitch(emu, voice, evoice->epcm->pitch_target << 16); 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu, 71962306a36Sopenharmony_ci struct snd_emu10k1_voice *evoice) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci unsigned int voice; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci voice = evoice->number; 72462306a36Sopenharmony_ci snd_emu10k1_playback_commit_pitch(emu, voice, 0); 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic void snd_emu10k1_playback_set_running(struct snd_emu10k1 *emu, 72862306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci epcm->running = 1; 73162306a36Sopenharmony_ci snd_emu10k1_voice_intr_enable(emu, epcm->extra->number); 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic void snd_emu10k1_playback_set_stopped(struct snd_emu10k1 *emu, 73562306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci snd_emu10k1_voice_intr_disable(emu, epcm->extra->number); 73862306a36Sopenharmony_ci epcm->running = 0; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream, 74262306a36Sopenharmony_ci int cmd) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 74562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 74662306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 74762306a36Sopenharmony_ci struct snd_emu10k1_pcm_mixer *mix; 74862306a36Sopenharmony_ci bool w_16 = snd_pcm_format_width(runtime->format) == 16; 74962306a36Sopenharmony_ci bool stereo = runtime->channels == 2; 75062306a36Sopenharmony_ci int result = 0; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* 75362306a36Sopenharmony_ci dev_dbg(emu->card->dev, 75462306a36Sopenharmony_ci "trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", 75562306a36Sopenharmony_ci (int)emu, cmd, substream->ops->pointer(substream)) 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_ci spin_lock(&emu->reg_lock); 75862306a36Sopenharmony_ci switch (cmd) { 75962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 76062306a36Sopenharmony_ci snd_emu10k1_playback_prepare_voices(emu, epcm, w_16, stereo, 1); 76162306a36Sopenharmony_ci fallthrough; 76262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 76362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 76462306a36Sopenharmony_ci mix = &emu->pcm_mixer[substream->number]; 76562306a36Sopenharmony_ci snd_emu10k1_playback_unmute_voices(emu, epcm->voices[0], stereo, mix); 76662306a36Sopenharmony_ci snd_emu10k1_playback_set_running(emu, epcm); 76762306a36Sopenharmony_ci snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0]); 76862306a36Sopenharmony_ci snd_emu10k1_playback_trigger_voice(emu, epcm->extra); 76962306a36Sopenharmony_ci break; 77062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 77162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 77262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 77362306a36Sopenharmony_ci snd_emu10k1_playback_stop_voice(emu, epcm->voices[0]); 77462306a36Sopenharmony_ci snd_emu10k1_playback_stop_voice(emu, epcm->extra); 77562306a36Sopenharmony_ci snd_emu10k1_playback_set_stopped(emu, epcm); 77662306a36Sopenharmony_ci snd_emu10k1_playback_mute_voices(emu, epcm->voices[0], stereo); 77762306a36Sopenharmony_ci break; 77862306a36Sopenharmony_ci default: 77962306a36Sopenharmony_ci result = -EINVAL; 78062306a36Sopenharmony_ci break; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci spin_unlock(&emu->reg_lock); 78362306a36Sopenharmony_ci return result; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream, 78762306a36Sopenharmony_ci int cmd) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 79062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 79162306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 79262306a36Sopenharmony_ci int result = 0; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci spin_lock(&emu->reg_lock); 79562306a36Sopenharmony_ci switch (cmd) { 79662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 79762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 79862306a36Sopenharmony_ci /* hmm this should cause full and half full interrupt to be raised? */ 79962306a36Sopenharmony_ci outl(epcm->capture_ipr, emu->port + IPR); 80062306a36Sopenharmony_ci snd_emu10k1_intr_enable(emu, epcm->capture_inte); 80162306a36Sopenharmony_ci /* 80262306a36Sopenharmony_ci dev_dbg(emu->card->dev, "adccr = 0x%x, adcbs = 0x%x\n", 80362306a36Sopenharmony_ci epcm->adccr, epcm->adcbs); 80462306a36Sopenharmony_ci */ 80562306a36Sopenharmony_ci switch (epcm->type) { 80662306a36Sopenharmony_ci case CAPTURE_AC97ADC: 80762306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, ADCCR, 0, epcm->capture_cr_val); 80862306a36Sopenharmony_ci break; 80962306a36Sopenharmony_ci case CAPTURE_EFX: 81062306a36Sopenharmony_ci if (emu->audigy) { 81162306a36Sopenharmony_ci snd_emu10k1_ptr_write_multiple(emu, 0, 81262306a36Sopenharmony_ci A_FXWC1, epcm->capture_cr_val, 81362306a36Sopenharmony_ci A_FXWC2, epcm->capture_cr_val2, 81462306a36Sopenharmony_ci REGLIST_END); 81562306a36Sopenharmony_ci dev_dbg(emu->card->dev, 81662306a36Sopenharmony_ci "cr_val=0x%x, cr_val2=0x%x\n", 81762306a36Sopenharmony_ci epcm->capture_cr_val, 81862306a36Sopenharmony_ci epcm->capture_cr_val2); 81962306a36Sopenharmony_ci } else 82062306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val); 82162306a36Sopenharmony_ci break; 82262306a36Sopenharmony_ci default: 82362306a36Sopenharmony_ci break; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, epcm->capture_bs_val); 82662306a36Sopenharmony_ci epcm->running = 1; 82762306a36Sopenharmony_ci epcm->first_ptr = 1; 82862306a36Sopenharmony_ci break; 82962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 83062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 83162306a36Sopenharmony_ci epcm->running = 0; 83262306a36Sopenharmony_ci snd_emu10k1_intr_disable(emu, epcm->capture_inte); 83362306a36Sopenharmony_ci outl(epcm->capture_ipr, emu->port + IPR); 83462306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0); 83562306a36Sopenharmony_ci switch (epcm->type) { 83662306a36Sopenharmony_ci case CAPTURE_AC97ADC: 83762306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, ADCCR, 0, 0); 83862306a36Sopenharmony_ci break; 83962306a36Sopenharmony_ci case CAPTURE_EFX: 84062306a36Sopenharmony_ci if (emu->audigy) { 84162306a36Sopenharmony_ci snd_emu10k1_ptr_write_multiple(emu, 0, 84262306a36Sopenharmony_ci A_FXWC1, 0, 84362306a36Sopenharmony_ci A_FXWC2, 0, 84462306a36Sopenharmony_ci REGLIST_END); 84562306a36Sopenharmony_ci } else 84662306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, FXWC, 0, 0); 84762306a36Sopenharmony_ci break; 84862306a36Sopenharmony_ci default: 84962306a36Sopenharmony_ci break; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci break; 85262306a36Sopenharmony_ci default: 85362306a36Sopenharmony_ci result = -EINVAL; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci spin_unlock(&emu->reg_lock); 85662306a36Sopenharmony_ci return result; 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_substream *substream) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 86262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 86362306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 86462306a36Sopenharmony_ci int ptr; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (!epcm->running) 86762306a36Sopenharmony_ci return 0; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff; 87062306a36Sopenharmony_ci ptr -= epcm->ccca_start_addr; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci // This is the size of the whole cache minus the interpolator read-ahead, 87362306a36Sopenharmony_ci // which leads us to the actual playback position. 87462306a36Sopenharmony_ci // 87562306a36Sopenharmony_ci // The cache is constantly kept mostly filled, so in principle we could 87662306a36Sopenharmony_ci // return a more advanced position representing how far the hardware has 87762306a36Sopenharmony_ci // already read the buffer, and set runtime->delay accordingly. However, 87862306a36Sopenharmony_ci // this would be slightly different for every channel (and remarkably slow 87962306a36Sopenharmony_ci // to obtain), so only a fixed worst-case value would be practical. 88062306a36Sopenharmony_ci // 88162306a36Sopenharmony_ci ptr -= 64 - 3; 88262306a36Sopenharmony_ci if (ptr < 0) 88362306a36Sopenharmony_ci ptr += runtime->buffer_size; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* 88662306a36Sopenharmony_ci dev_dbg(emu->card->dev, 88762306a36Sopenharmony_ci "ptr = 0x%lx, buffer_size = 0x%lx, period_size = 0x%lx\n", 88862306a36Sopenharmony_ci (long)ptr, (long)runtime->buffer_size, 88962306a36Sopenharmony_ci (long)runtime->period_size); 89062306a36Sopenharmony_ci */ 89162306a36Sopenharmony_ci return ptr; 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_cistatic u64 snd_emu10k1_efx_playback_voice_mask(struct snd_emu10k1_pcm *epcm, 89562306a36Sopenharmony_ci int channels) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci u64 mask = 0; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci for (int i = 0; i < channels; i++) { 90062306a36Sopenharmony_ci int voice = epcm->voices[i]->number; 90162306a36Sopenharmony_ci mask |= 1ULL << voice; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci return mask; 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cistatic void snd_emu10k1_efx_playback_freeze_voices(struct snd_emu10k1 *emu, 90762306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm, 90862306a36Sopenharmony_ci int channels) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci for (int i = 0; i < channels; i++) { 91162306a36Sopenharmony_ci int voice = epcm->voices[i]->number; 91262306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, CPF_STOP, voice, 1); 91362306a36Sopenharmony_ci snd_emu10k1_playback_commit_pitch(emu, voice, PITCH_48000 << 16); 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_cistatic void snd_emu10k1_efx_playback_unmute_voices(struct snd_emu10k1 *emu, 91862306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm, 91962306a36Sopenharmony_ci int channels) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci for (int i = 0; i < channels; i++) 92262306a36Sopenharmony_ci snd_emu10k1_playback_unmute_voice(emu, epcm->voices[i], false, true, 92362306a36Sopenharmony_ci &emu->efx_pcm_mixer[i]); 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cistatic void snd_emu10k1_efx_playback_stop_voices(struct snd_emu10k1 *emu, 92762306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm, 92862306a36Sopenharmony_ci int channels) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci for (int i = 0; i < channels; i++) 93162306a36Sopenharmony_ci snd_emu10k1_playback_stop_voice(emu, epcm->voices[i]); 93262306a36Sopenharmony_ci snd_emu10k1_playback_set_stopped(emu, epcm); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci for (int i = 0; i < channels; i++) 93562306a36Sopenharmony_ci snd_emu10k1_playback_mute_voice(emu, epcm->voices[i]); 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic int snd_emu10k1_efx_playback_trigger(struct snd_pcm_substream *substream, 93962306a36Sopenharmony_ci int cmd) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 94262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 94362306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 94462306a36Sopenharmony_ci u64 mask; 94562306a36Sopenharmony_ci int result = 0; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci spin_lock(&emu->reg_lock); 94862306a36Sopenharmony_ci switch (cmd) { 94962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 95062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 95162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 95262306a36Sopenharmony_ci mask = snd_emu10k1_efx_playback_voice_mask( 95362306a36Sopenharmony_ci epcm, runtime->channels); 95462306a36Sopenharmony_ci for (int i = 0; i < 10; i++) { 95562306a36Sopenharmony_ci // Note that the freeze is not interruptible, so we make no 95662306a36Sopenharmony_ci // effort to reset the bits outside the error handling here. 95762306a36Sopenharmony_ci snd_emu10k1_voice_set_loop_stop_multiple(emu, mask); 95862306a36Sopenharmony_ci snd_emu10k1_efx_playback_freeze_voices( 95962306a36Sopenharmony_ci emu, epcm, runtime->channels); 96062306a36Sopenharmony_ci snd_emu10k1_playback_prepare_voices( 96162306a36Sopenharmony_ci emu, epcm, true, false, runtime->channels); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci // It might seem to make more sense to unmute the voices only after 96462306a36Sopenharmony_ci // they have been started, to potentially avoid torturing the speakers 96562306a36Sopenharmony_ci // if something goes wrong. However, we cannot unmute atomically, 96662306a36Sopenharmony_ci // which means that we'd get some mild artifacts in the regular case. 96762306a36Sopenharmony_ci snd_emu10k1_efx_playback_unmute_voices(emu, epcm, runtime->channels); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci snd_emu10k1_playback_set_running(emu, epcm); 97062306a36Sopenharmony_ci result = snd_emu10k1_voice_clear_loop_stop_multiple_atomic(emu, mask); 97162306a36Sopenharmony_ci if (result == 0) { 97262306a36Sopenharmony_ci // The extra voice is allowed to lag a bit 97362306a36Sopenharmony_ci snd_emu10k1_playback_trigger_voice(emu, epcm->extra); 97462306a36Sopenharmony_ci goto leave; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci snd_emu10k1_efx_playback_stop_voices( 97862306a36Sopenharmony_ci emu, epcm, runtime->channels); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci if (result != -EAGAIN) 98162306a36Sopenharmony_ci break; 98262306a36Sopenharmony_ci // The sync start can legitimately fail due to NMIs, etc. 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci snd_emu10k1_voice_clear_loop_stop_multiple(emu, mask); 98562306a36Sopenharmony_ci break; 98662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 98762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 98862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 98962306a36Sopenharmony_ci snd_emu10k1_playback_stop_voice(emu, epcm->extra); 99062306a36Sopenharmony_ci snd_emu10k1_efx_playback_stop_voices( 99162306a36Sopenharmony_ci emu, epcm, runtime->channels); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci epcm->resume_pos = snd_emu10k1_playback_pointer(substream); 99462306a36Sopenharmony_ci break; 99562306a36Sopenharmony_ci default: 99662306a36Sopenharmony_ci result = -EINVAL; 99762306a36Sopenharmony_ci break; 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_cileave: 100062306a36Sopenharmony_ci spin_unlock(&emu->reg_lock); 100162306a36Sopenharmony_ci return result; 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_emu10k1_capture_pointer(struct snd_pcm_substream *substream) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 100862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 100962306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm = runtime->private_data; 101062306a36Sopenharmony_ci unsigned int ptr; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci if (!epcm->running) 101362306a36Sopenharmony_ci return 0; 101462306a36Sopenharmony_ci if (epcm->first_ptr) { 101562306a36Sopenharmony_ci udelay(50); /* hack, it takes awhile until capture is started */ 101662306a36Sopenharmony_ci epcm->first_ptr = 0; 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci ptr = snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff; 101962306a36Sopenharmony_ci return bytes_to_frames(runtime, ptr); 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci/* 102362306a36Sopenharmony_ci * Playback support device description 102462306a36Sopenharmony_ci */ 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_emu10k1_playback = 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 102962306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 103062306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 103162306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), 103262306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 103362306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_96000, 103462306a36Sopenharmony_ci .rate_min = 4000, 103562306a36Sopenharmony_ci .rate_max = 96000, 103662306a36Sopenharmony_ci .channels_min = 1, 103762306a36Sopenharmony_ci .channels_max = 2, 103862306a36Sopenharmony_ci .buffer_bytes_max = (128*1024), 103962306a36Sopenharmony_ci .period_bytes_max = (128*1024), 104062306a36Sopenharmony_ci .periods_min = 2, 104162306a36Sopenharmony_ci .periods_max = 1024, 104262306a36Sopenharmony_ci .fifo_size = 0, 104362306a36Sopenharmony_ci}; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci/* 104662306a36Sopenharmony_ci * Capture support device description 104762306a36Sopenharmony_ci */ 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_emu10k1_capture = 105062306a36Sopenharmony_ci{ 105162306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 105262306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 105362306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 105462306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 105562306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 105662306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT, 105762306a36Sopenharmony_ci .rate_min = 8000, 105862306a36Sopenharmony_ci .rate_max = 48000, 105962306a36Sopenharmony_ci .channels_min = 1, 106062306a36Sopenharmony_ci .channels_max = 2, 106162306a36Sopenharmony_ci .buffer_bytes_max = (64*1024), 106262306a36Sopenharmony_ci .period_bytes_min = 384, 106362306a36Sopenharmony_ci .period_bytes_max = (64*1024), 106462306a36Sopenharmony_ci .periods_min = 2, 106562306a36Sopenharmony_ci .periods_max = 2, 106662306a36Sopenharmony_ci .fifo_size = 0, 106762306a36Sopenharmony_ci}; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_emu10k1_capture_efx = 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 107262306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 107362306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 107462306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 107562306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 107662306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 107762306a36Sopenharmony_ci .rate_min = 48000, 107862306a36Sopenharmony_ci .rate_max = 48000, 107962306a36Sopenharmony_ci .channels_min = 1, 108062306a36Sopenharmony_ci .channels_max = 16, 108162306a36Sopenharmony_ci .buffer_bytes_max = (64*1024), 108262306a36Sopenharmony_ci .period_bytes_min = 384, 108362306a36Sopenharmony_ci .period_bytes_max = (64*1024), 108462306a36Sopenharmony_ci .periods_min = 2, 108562306a36Sopenharmony_ci .periods_max = 2, 108662306a36Sopenharmony_ci .fifo_size = 0, 108762306a36Sopenharmony_ci}; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci/* 109062306a36Sopenharmony_ci * 109162306a36Sopenharmony_ci */ 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic void snd_emu10k1_pcm_mixer_notify1(struct snd_emu10k1 *emu, struct snd_kcontrol *kctl, int idx, int activate) 109462306a36Sopenharmony_ci{ 109562306a36Sopenharmony_ci struct snd_ctl_elem_id id; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci if (! kctl) 109862306a36Sopenharmony_ci return; 109962306a36Sopenharmony_ci if (activate) 110062306a36Sopenharmony_ci kctl->vd[idx].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 110162306a36Sopenharmony_ci else 110262306a36Sopenharmony_ci kctl->vd[idx].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; 110362306a36Sopenharmony_ci snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE | 110462306a36Sopenharmony_ci SNDRV_CTL_EVENT_MASK_INFO, 110562306a36Sopenharmony_ci snd_ctl_build_ioff(&id, kctl, idx)); 110662306a36Sopenharmony_ci} 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_cistatic void snd_emu10k1_pcm_mixer_notify(struct snd_emu10k1 *emu, int idx, int activate) 110962306a36Sopenharmony_ci{ 111062306a36Sopenharmony_ci snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_send_routing, idx, activate); 111162306a36Sopenharmony_ci snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_send_volume, idx, activate); 111262306a36Sopenharmony_ci snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_attn, idx, activate); 111362306a36Sopenharmony_ci} 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_cistatic void snd_emu10k1_pcm_efx_mixer_notify(struct snd_emu10k1 *emu, int idx, int activate) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_send_routing, idx, activate); 111862306a36Sopenharmony_ci snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_send_volume, idx, activate); 111962306a36Sopenharmony_ci snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_attn, idx, activate); 112062306a36Sopenharmony_ci} 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_cistatic void snd_emu10k1_pcm_free_substream(struct snd_pcm_runtime *runtime) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci kfree(runtime->private_data); 112562306a36Sopenharmony_ci} 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_cistatic int snd_emu10k1_efx_playback_close(struct snd_pcm_substream *substream) 112862306a36Sopenharmony_ci{ 112962306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 113062306a36Sopenharmony_ci struct snd_emu10k1_pcm_mixer *mix; 113162306a36Sopenharmony_ci int i; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci for (i = 0; i < NUM_EFX_PLAYBACK; i++) { 113462306a36Sopenharmony_ci mix = &emu->efx_pcm_mixer[i]; 113562306a36Sopenharmony_ci mix->epcm = NULL; 113662306a36Sopenharmony_ci snd_emu10k1_pcm_efx_mixer_notify(emu, i, 0); 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci return 0; 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_cistatic int snd_emu10k1_playback_set_constraints(struct snd_pcm_runtime *runtime) 114262306a36Sopenharmony_ci{ 114362306a36Sopenharmony_ci int err; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci // The buffer size must be a multiple of the period size, to avoid a 114662306a36Sopenharmony_ci // mismatch between the extra voice and the regular voices. 114762306a36Sopenharmony_ci err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 114862306a36Sopenharmony_ci if (err < 0) 114962306a36Sopenharmony_ci return err; 115062306a36Sopenharmony_ci // The hardware is typically the cache's size of 64 frames ahead. 115162306a36Sopenharmony_ci // Leave enough time for actually filling up the buffer. 115262306a36Sopenharmony_ci err = snd_pcm_hw_constraint_minmax( 115362306a36Sopenharmony_ci runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 128, UINT_MAX); 115462306a36Sopenharmony_ci return err; 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_cistatic int snd_emu10k1_efx_playback_open(struct snd_pcm_substream *substream) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 116062306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm; 116162306a36Sopenharmony_ci struct snd_emu10k1_pcm_mixer *mix; 116262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 116362306a36Sopenharmony_ci int i, j, err; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 116662306a36Sopenharmony_ci if (epcm == NULL) 116762306a36Sopenharmony_ci return -ENOMEM; 116862306a36Sopenharmony_ci epcm->emu = emu; 116962306a36Sopenharmony_ci epcm->type = PLAYBACK_EFX; 117062306a36Sopenharmony_ci epcm->substream = substream; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci runtime->private_data = epcm; 117362306a36Sopenharmony_ci runtime->private_free = snd_emu10k1_pcm_free_substream; 117462306a36Sopenharmony_ci runtime->hw = snd_emu10k1_efx_playback; 117562306a36Sopenharmony_ci if (emu->card_capabilities->emu_model) 117662306a36Sopenharmony_ci snd_emu1010_constrain_efx_rate(emu, runtime); 117762306a36Sopenharmony_ci err = snd_emu10k1_playback_set_constraints(runtime); 117862306a36Sopenharmony_ci if (err < 0) { 117962306a36Sopenharmony_ci kfree(epcm); 118062306a36Sopenharmony_ci return err; 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci for (i = 0; i < NUM_EFX_PLAYBACK; i++) { 118462306a36Sopenharmony_ci mix = &emu->efx_pcm_mixer[i]; 118562306a36Sopenharmony_ci for (j = 0; j < 8; j++) 118662306a36Sopenharmony_ci mix->send_routing[0][j] = i + j; 118762306a36Sopenharmony_ci memset(&mix->send_volume, 0, sizeof(mix->send_volume)); 118862306a36Sopenharmony_ci mix->send_volume[0][0] = 255; 118962306a36Sopenharmony_ci mix->attn[0] = 0x8000; 119062306a36Sopenharmony_ci mix->epcm = epcm; 119162306a36Sopenharmony_ci snd_emu10k1_pcm_efx_mixer_notify(emu, i, 1); 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci return 0; 119462306a36Sopenharmony_ci} 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_cistatic int snd_emu10k1_playback_open(struct snd_pcm_substream *substream) 119762306a36Sopenharmony_ci{ 119862306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 119962306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm; 120062306a36Sopenharmony_ci struct snd_emu10k1_pcm_mixer *mix; 120162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 120262306a36Sopenharmony_ci int i, err, sample_rate; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 120562306a36Sopenharmony_ci if (epcm == NULL) 120662306a36Sopenharmony_ci return -ENOMEM; 120762306a36Sopenharmony_ci epcm->emu = emu; 120862306a36Sopenharmony_ci epcm->type = PLAYBACK_EMUVOICE; 120962306a36Sopenharmony_ci epcm->substream = substream; 121062306a36Sopenharmony_ci runtime->private_data = epcm; 121162306a36Sopenharmony_ci runtime->private_free = snd_emu10k1_pcm_free_substream; 121262306a36Sopenharmony_ci runtime->hw = snd_emu10k1_playback; 121362306a36Sopenharmony_ci err = snd_emu10k1_playback_set_constraints(runtime); 121462306a36Sopenharmony_ci if (err < 0) { 121562306a36Sopenharmony_ci kfree(epcm); 121662306a36Sopenharmony_ci return err; 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci if (emu->card_capabilities->emu_model) 121962306a36Sopenharmony_ci sample_rate = emu->emu1010.word_clock; 122062306a36Sopenharmony_ci else 122162306a36Sopenharmony_ci sample_rate = 48000; 122262306a36Sopenharmony_ci err = snd_pcm_hw_rule_noresample(runtime, sample_rate); 122362306a36Sopenharmony_ci if (err < 0) { 122462306a36Sopenharmony_ci kfree(epcm); 122562306a36Sopenharmony_ci return err; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci mix = &emu->pcm_mixer[substream->number]; 122862306a36Sopenharmony_ci for (i = 0; i < 8; i++) 122962306a36Sopenharmony_ci mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i; 123062306a36Sopenharmony_ci memset(&mix->send_volume, 0, sizeof(mix->send_volume)); 123162306a36Sopenharmony_ci mix->send_volume[0][0] = mix->send_volume[0][1] = 123262306a36Sopenharmony_ci mix->send_volume[1][0] = mix->send_volume[2][1] = 255; 123362306a36Sopenharmony_ci mix->attn[0] = mix->attn[1] = mix->attn[2] = 0x8000; 123462306a36Sopenharmony_ci mix->epcm = epcm; 123562306a36Sopenharmony_ci snd_emu10k1_pcm_mixer_notify(emu, substream->number, 1); 123662306a36Sopenharmony_ci return 0; 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_cistatic int snd_emu10k1_playback_close(struct snd_pcm_substream *substream) 124062306a36Sopenharmony_ci{ 124162306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 124262306a36Sopenharmony_ci struct snd_emu10k1_pcm_mixer *mix = &emu->pcm_mixer[substream->number]; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci mix->epcm = NULL; 124562306a36Sopenharmony_ci snd_emu10k1_pcm_mixer_notify(emu, substream->number, 0); 124662306a36Sopenharmony_ci return 0; 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_cistatic int snd_emu10k1_capture_open(struct snd_pcm_substream *substream) 125062306a36Sopenharmony_ci{ 125162306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 125262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 125362306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 125662306a36Sopenharmony_ci if (epcm == NULL) 125762306a36Sopenharmony_ci return -ENOMEM; 125862306a36Sopenharmony_ci epcm->emu = emu; 125962306a36Sopenharmony_ci epcm->type = CAPTURE_AC97ADC; 126062306a36Sopenharmony_ci epcm->substream = substream; 126162306a36Sopenharmony_ci epcm->capture_ipr = IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL; 126262306a36Sopenharmony_ci epcm->capture_inte = INTE_ADCBUFENABLE; 126362306a36Sopenharmony_ci epcm->capture_ba_reg = ADCBA; 126462306a36Sopenharmony_ci epcm->capture_bs_reg = ADCBS; 126562306a36Sopenharmony_ci epcm->capture_idx_reg = emu->audigy ? A_ADCIDX : ADCIDX; 126662306a36Sopenharmony_ci runtime->private_data = epcm; 126762306a36Sopenharmony_ci runtime->private_free = snd_emu10k1_pcm_free_substream; 126862306a36Sopenharmony_ci runtime->hw = snd_emu10k1_capture; 126962306a36Sopenharmony_ci snd_emu10k1_constrain_capture_rates(emu, runtime); 127062306a36Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 127162306a36Sopenharmony_ci &hw_constraints_capture_buffer_sizes); 127262306a36Sopenharmony_ci emu->capture_interrupt = snd_emu10k1_pcm_ac97adc_interrupt; 127362306a36Sopenharmony_ci emu->pcm_capture_substream = substream; 127462306a36Sopenharmony_ci return 0; 127562306a36Sopenharmony_ci} 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_cistatic int snd_emu10k1_capture_close(struct snd_pcm_substream *substream) 127862306a36Sopenharmony_ci{ 127962306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci emu->capture_interrupt = NULL; 128262306a36Sopenharmony_ci emu->pcm_capture_substream = NULL; 128362306a36Sopenharmony_ci return 0; 128462306a36Sopenharmony_ci} 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_cistatic int snd_emu10k1_capture_mic_open(struct snd_pcm_substream *substream) 128762306a36Sopenharmony_ci{ 128862306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 128962306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm; 129062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 129362306a36Sopenharmony_ci if (epcm == NULL) 129462306a36Sopenharmony_ci return -ENOMEM; 129562306a36Sopenharmony_ci epcm->emu = emu; 129662306a36Sopenharmony_ci epcm->type = CAPTURE_AC97MIC; 129762306a36Sopenharmony_ci epcm->substream = substream; 129862306a36Sopenharmony_ci epcm->capture_ipr = IPR_MICBUFFULL|IPR_MICBUFHALFFULL; 129962306a36Sopenharmony_ci epcm->capture_inte = INTE_MICBUFENABLE; 130062306a36Sopenharmony_ci epcm->capture_ba_reg = MICBA; 130162306a36Sopenharmony_ci epcm->capture_bs_reg = MICBS; 130262306a36Sopenharmony_ci epcm->capture_idx_reg = emu->audigy ? A_MICIDX : MICIDX; 130362306a36Sopenharmony_ci substream->runtime->private_data = epcm; 130462306a36Sopenharmony_ci substream->runtime->private_free = snd_emu10k1_pcm_free_substream; 130562306a36Sopenharmony_ci runtime->hw = snd_emu10k1_capture; 130662306a36Sopenharmony_ci runtime->hw.rates = SNDRV_PCM_RATE_8000; 130762306a36Sopenharmony_ci runtime->hw.rate_min = runtime->hw.rate_max = 8000; 130862306a36Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 130962306a36Sopenharmony_ci &hw_constraints_capture_buffer_sizes); 131062306a36Sopenharmony_ci emu->capture_mic_interrupt = snd_emu10k1_pcm_ac97mic_interrupt; 131162306a36Sopenharmony_ci emu->pcm_capture_mic_substream = substream; 131262306a36Sopenharmony_ci return 0; 131362306a36Sopenharmony_ci} 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_cistatic int snd_emu10k1_capture_mic_close(struct snd_pcm_substream *substream) 131662306a36Sopenharmony_ci{ 131762306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci emu->capture_mic_interrupt = NULL; 132062306a36Sopenharmony_ci emu->pcm_capture_mic_substream = NULL; 132162306a36Sopenharmony_ci return 0; 132262306a36Sopenharmony_ci} 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_cistatic int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream) 132562306a36Sopenharmony_ci{ 132662306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 132762306a36Sopenharmony_ci struct snd_emu10k1_pcm *epcm; 132862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 132962306a36Sopenharmony_ci int nefx = emu->audigy ? 64 : 32; 133062306a36Sopenharmony_ci int idx, err; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 133362306a36Sopenharmony_ci if (epcm == NULL) 133462306a36Sopenharmony_ci return -ENOMEM; 133562306a36Sopenharmony_ci epcm->emu = emu; 133662306a36Sopenharmony_ci epcm->type = CAPTURE_EFX; 133762306a36Sopenharmony_ci epcm->substream = substream; 133862306a36Sopenharmony_ci epcm->capture_ipr = IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL; 133962306a36Sopenharmony_ci epcm->capture_inte = INTE_EFXBUFENABLE; 134062306a36Sopenharmony_ci epcm->capture_ba_reg = FXBA; 134162306a36Sopenharmony_ci epcm->capture_bs_reg = FXBS; 134262306a36Sopenharmony_ci epcm->capture_idx_reg = FXIDX; 134362306a36Sopenharmony_ci substream->runtime->private_data = epcm; 134462306a36Sopenharmony_ci substream->runtime->private_free = snd_emu10k1_pcm_free_substream; 134562306a36Sopenharmony_ci runtime->hw = snd_emu10k1_capture_efx; 134662306a36Sopenharmony_ci if (emu->card_capabilities->emu_model) { 134762306a36Sopenharmony_ci snd_emu1010_constrain_efx_rate(emu, runtime); 134862306a36Sopenharmony_ci /* 134962306a36Sopenharmony_ci * There are 32 mono channels of 16bits each. 135062306a36Sopenharmony_ci * 24bit Audio uses 2x channels over 16bit, 135162306a36Sopenharmony_ci * 96kHz uses 2x channels over 48kHz, 135262306a36Sopenharmony_ci * 192kHz uses 4x channels over 48kHz. 135362306a36Sopenharmony_ci * So, for 48kHz 24bit, one has 16 channels, 135462306a36Sopenharmony_ci * for 96kHz 24bit, one has 8 channels, 135562306a36Sopenharmony_ci * for 192kHz 24bit, one has 4 channels. 135662306a36Sopenharmony_ci * 1010rev2 and 1616(m) cards have double that, 135762306a36Sopenharmony_ci * but we don't exceed 16 channels anyway. 135862306a36Sopenharmony_ci */ 135962306a36Sopenharmony_ci#if 0 136062306a36Sopenharmony_ci /* For 96kHz */ 136162306a36Sopenharmony_ci runtime->hw.channels_min = runtime->hw.channels_max = 4; 136262306a36Sopenharmony_ci#endif 136362306a36Sopenharmony_ci#if 0 136462306a36Sopenharmony_ci /* For 192kHz */ 136562306a36Sopenharmony_ci runtime->hw.channels_min = runtime->hw.channels_max = 2; 136662306a36Sopenharmony_ci#endif 136762306a36Sopenharmony_ci runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; 136862306a36Sopenharmony_ci } else { 136962306a36Sopenharmony_ci spin_lock_irq(&emu->reg_lock); 137062306a36Sopenharmony_ci runtime->hw.channels_min = runtime->hw.channels_max = 0; 137162306a36Sopenharmony_ci for (idx = 0; idx < nefx; idx++) { 137262306a36Sopenharmony_ci if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) { 137362306a36Sopenharmony_ci runtime->hw.channels_min++; 137462306a36Sopenharmony_ci runtime->hw.channels_max++; 137562306a36Sopenharmony_ci } 137662306a36Sopenharmony_ci } 137762306a36Sopenharmony_ci epcm->capture_cr_val = emu->efx_voices_mask[0]; 137862306a36Sopenharmony_ci epcm->capture_cr_val2 = emu->efx_voices_mask[1]; 137962306a36Sopenharmony_ci spin_unlock_irq(&emu->reg_lock); 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 138262306a36Sopenharmony_ci &hw_constraints_efx_capture_channels); 138362306a36Sopenharmony_ci if (err < 0) { 138462306a36Sopenharmony_ci kfree(epcm); 138562306a36Sopenharmony_ci return err; 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 138862306a36Sopenharmony_ci &hw_constraints_capture_buffer_sizes); 138962306a36Sopenharmony_ci emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt; 139062306a36Sopenharmony_ci emu->pcm_capture_efx_substream = substream; 139162306a36Sopenharmony_ci return 0; 139262306a36Sopenharmony_ci} 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cistatic int snd_emu10k1_capture_efx_close(struct snd_pcm_substream *substream) 139562306a36Sopenharmony_ci{ 139662306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci emu->capture_efx_interrupt = NULL; 139962306a36Sopenharmony_ci emu->pcm_capture_efx_substream = NULL; 140062306a36Sopenharmony_ci return 0; 140162306a36Sopenharmony_ci} 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1_playback_ops = { 140462306a36Sopenharmony_ci .open = snd_emu10k1_playback_open, 140562306a36Sopenharmony_ci .close = snd_emu10k1_playback_close, 140662306a36Sopenharmony_ci .hw_params = snd_emu10k1_playback_hw_params, 140762306a36Sopenharmony_ci .hw_free = snd_emu10k1_playback_hw_free, 140862306a36Sopenharmony_ci .prepare = snd_emu10k1_playback_prepare, 140962306a36Sopenharmony_ci .trigger = snd_emu10k1_playback_trigger, 141062306a36Sopenharmony_ci .pointer = snd_emu10k1_playback_pointer, 141162306a36Sopenharmony_ci}; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1_capture_ops = { 141462306a36Sopenharmony_ci .open = snd_emu10k1_capture_open, 141562306a36Sopenharmony_ci .close = snd_emu10k1_capture_close, 141662306a36Sopenharmony_ci .prepare = snd_emu10k1_capture_prepare, 141762306a36Sopenharmony_ci .trigger = snd_emu10k1_capture_trigger, 141862306a36Sopenharmony_ci .pointer = snd_emu10k1_capture_pointer, 141962306a36Sopenharmony_ci}; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci/* EFX playback */ 142262306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1_efx_playback_ops = { 142362306a36Sopenharmony_ci .open = snd_emu10k1_efx_playback_open, 142462306a36Sopenharmony_ci .close = snd_emu10k1_efx_playback_close, 142562306a36Sopenharmony_ci .hw_params = snd_emu10k1_playback_hw_params, 142662306a36Sopenharmony_ci .hw_free = snd_emu10k1_playback_hw_free, 142762306a36Sopenharmony_ci .prepare = snd_emu10k1_efx_playback_prepare, 142862306a36Sopenharmony_ci .trigger = snd_emu10k1_efx_playback_trigger, 142962306a36Sopenharmony_ci .pointer = snd_emu10k1_playback_pointer, 143062306a36Sopenharmony_ci}; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ciint snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device) 143362306a36Sopenharmony_ci{ 143462306a36Sopenharmony_ci struct snd_pcm *pcm; 143562306a36Sopenharmony_ci struct snd_pcm_substream *substream; 143662306a36Sopenharmony_ci int err; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci err = snd_pcm_new(emu->card, "emu10k1", device, 32, 1, &pcm); 143962306a36Sopenharmony_ci if (err < 0) 144062306a36Sopenharmony_ci return err; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci pcm->private_data = emu; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_playback_ops); 144562306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_ops); 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci pcm->info_flags = 0; 144862306a36Sopenharmony_ci pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; 144962306a36Sopenharmony_ci strcpy(pcm->name, "ADC Capture/Standard PCM Playback"); 145062306a36Sopenharmony_ci emu->pcm = pcm; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci /* playback substream can't use managed buffers due to alignment */ 145362306a36Sopenharmony_ci for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) 145462306a36Sopenharmony_ci snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, 145562306a36Sopenharmony_ci &emu->pci->dev, 145662306a36Sopenharmony_ci 64*1024, 64*1024); 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) 145962306a36Sopenharmony_ci snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV, 146062306a36Sopenharmony_ci &emu->pci->dev, 64*1024, 64*1024); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci return 0; 146362306a36Sopenharmony_ci} 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ciint snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device) 146662306a36Sopenharmony_ci{ 146762306a36Sopenharmony_ci struct snd_pcm *pcm; 146862306a36Sopenharmony_ci struct snd_pcm_substream *substream; 146962306a36Sopenharmony_ci int err; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci err = snd_pcm_new(emu->card, "emu10k1", device, 1, 0, &pcm); 147262306a36Sopenharmony_ci if (err < 0) 147362306a36Sopenharmony_ci return err; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci pcm->private_data = emu; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_efx_playback_ops); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci pcm->info_flags = 0; 148062306a36Sopenharmony_ci pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; 148162306a36Sopenharmony_ci strcpy(pcm->name, "Multichannel Playback"); 148262306a36Sopenharmony_ci emu->pcm_multi = pcm; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) 148562306a36Sopenharmony_ci snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, 148662306a36Sopenharmony_ci &emu->pci->dev, 148762306a36Sopenharmony_ci 64*1024, 64*1024); 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci return 0; 149062306a36Sopenharmony_ci} 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1_capture_mic_ops = { 149462306a36Sopenharmony_ci .open = snd_emu10k1_capture_mic_open, 149562306a36Sopenharmony_ci .close = snd_emu10k1_capture_mic_close, 149662306a36Sopenharmony_ci .prepare = snd_emu10k1_capture_prepare, 149762306a36Sopenharmony_ci .trigger = snd_emu10k1_capture_trigger, 149862306a36Sopenharmony_ci .pointer = snd_emu10k1_capture_pointer, 149962306a36Sopenharmony_ci}; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ciint snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device) 150262306a36Sopenharmony_ci{ 150362306a36Sopenharmony_ci struct snd_pcm *pcm; 150462306a36Sopenharmony_ci int err; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm); 150762306a36Sopenharmony_ci if (err < 0) 150862306a36Sopenharmony_ci return err; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci pcm->private_data = emu; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops); 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci pcm->info_flags = 0; 151562306a36Sopenharmony_ci strcpy(pcm->name, "Mic Capture"); 151662306a36Sopenharmony_ci emu->pcm_mic = pcm; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &emu->pci->dev, 151962306a36Sopenharmony_ci 64*1024, 64*1024); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci return 0; 152262306a36Sopenharmony_ci} 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_cistatic int snd_emu10k1_pcm_efx_voices_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 152562306a36Sopenharmony_ci{ 152662306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); 152762306a36Sopenharmony_ci int nefx = emu->audigy ? 64 : 32; 152862306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 152962306a36Sopenharmony_ci uinfo->count = nefx; 153062306a36Sopenharmony_ci uinfo->value.integer.min = 0; 153162306a36Sopenharmony_ci uinfo->value.integer.max = 1; 153262306a36Sopenharmony_ci return 0; 153362306a36Sopenharmony_ci} 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_cistatic int snd_emu10k1_pcm_efx_voices_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 153662306a36Sopenharmony_ci{ 153762306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); 153862306a36Sopenharmony_ci int nefx = emu->audigy ? 64 : 32; 153962306a36Sopenharmony_ci int idx; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci for (idx = 0; idx < nefx; idx++) 154262306a36Sopenharmony_ci ucontrol->value.integer.value[idx] = (emu->efx_voices_mask[idx / 32] & (1 << (idx % 32))) ? 1 : 0; 154362306a36Sopenharmony_ci return 0; 154462306a36Sopenharmony_ci} 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_cistatic int snd_emu10k1_pcm_efx_voices_mask_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 154762306a36Sopenharmony_ci{ 154862306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); 154962306a36Sopenharmony_ci unsigned int nval[2], bits; 155062306a36Sopenharmony_ci int nefx = emu->audigy ? 64 : 32; 155162306a36Sopenharmony_ci int change, idx; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci nval[0] = nval[1] = 0; 155462306a36Sopenharmony_ci for (idx = 0, bits = 0; idx < nefx; idx++) 155562306a36Sopenharmony_ci if (ucontrol->value.integer.value[idx]) { 155662306a36Sopenharmony_ci nval[idx / 32] |= 1 << (idx % 32); 155762306a36Sopenharmony_ci bits++; 155862306a36Sopenharmony_ci } 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci if (bits == 9 || bits == 11 || bits == 13 || bits == 15 || bits > 16) 156162306a36Sopenharmony_ci return -EINVAL; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci spin_lock_irq(&emu->reg_lock); 156462306a36Sopenharmony_ci change = (nval[0] != emu->efx_voices_mask[0]) || 156562306a36Sopenharmony_ci (nval[1] != emu->efx_voices_mask[1]); 156662306a36Sopenharmony_ci emu->efx_voices_mask[0] = nval[0]; 156762306a36Sopenharmony_ci emu->efx_voices_mask[1] = nval[1]; 156862306a36Sopenharmony_ci spin_unlock_irq(&emu->reg_lock); 156962306a36Sopenharmony_ci return change; 157062306a36Sopenharmony_ci} 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = { 157362306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 157462306a36Sopenharmony_ci .name = "Captured FX8010 Outputs", 157562306a36Sopenharmony_ci .info = snd_emu10k1_pcm_efx_voices_mask_info, 157662306a36Sopenharmony_ci .get = snd_emu10k1_pcm_efx_voices_mask_get, 157762306a36Sopenharmony_ci .put = snd_emu10k1_pcm_efx_voices_mask_put 157862306a36Sopenharmony_ci}; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1_capture_efx_ops = { 158162306a36Sopenharmony_ci .open = snd_emu10k1_capture_efx_open, 158262306a36Sopenharmony_ci .close = snd_emu10k1_capture_efx_close, 158362306a36Sopenharmony_ci .prepare = snd_emu10k1_capture_prepare, 158462306a36Sopenharmony_ci .trigger = snd_emu10k1_capture_trigger, 158562306a36Sopenharmony_ci .pointer = snd_emu10k1_capture_pointer, 158662306a36Sopenharmony_ci}; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci/* EFX playback */ 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci#define INITIAL_TRAM_SHIFT 14 159262306a36Sopenharmony_ci#define INITIAL_TRAM_POS(size) ((((size) / 2) - INITIAL_TRAM_SHIFT) - 1) 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_cistatic void snd_emu10k1_fx8010_playback_irq(struct snd_emu10k1 *emu, void *private_data) 159562306a36Sopenharmony_ci{ 159662306a36Sopenharmony_ci struct snd_pcm_substream *substream = private_data; 159762306a36Sopenharmony_ci snd_pcm_period_elapsed(substream); 159862306a36Sopenharmony_ci} 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_cistatic void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left, 160162306a36Sopenharmony_ci unsigned short *dst_right, 160262306a36Sopenharmony_ci unsigned short *src, 160362306a36Sopenharmony_ci unsigned int count, 160462306a36Sopenharmony_ci unsigned int tram_shift) 160562306a36Sopenharmony_ci{ 160662306a36Sopenharmony_ci /* 160762306a36Sopenharmony_ci dev_dbg(emu->card->dev, 160862306a36Sopenharmony_ci "tram_poke1: dst_left = 0x%p, dst_right = 0x%p, " 160962306a36Sopenharmony_ci "src = 0x%p, count = 0x%x\n", 161062306a36Sopenharmony_ci dst_left, dst_right, src, count); 161162306a36Sopenharmony_ci */ 161262306a36Sopenharmony_ci if ((tram_shift & 1) == 0) { 161362306a36Sopenharmony_ci while (count--) { 161462306a36Sopenharmony_ci *dst_left-- = *src++; 161562306a36Sopenharmony_ci *dst_right-- = *src++; 161662306a36Sopenharmony_ci } 161762306a36Sopenharmony_ci } else { 161862306a36Sopenharmony_ci while (count--) { 161962306a36Sopenharmony_ci *dst_right-- = *src++; 162062306a36Sopenharmony_ci *dst_left-- = *src++; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci } 162362306a36Sopenharmony_ci} 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_cistatic void fx8010_pb_trans_copy(struct snd_pcm_substream *substream, 162662306a36Sopenharmony_ci struct snd_pcm_indirect *rec, size_t bytes) 162762306a36Sopenharmony_ci{ 162862306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 162962306a36Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 163062306a36Sopenharmony_ci unsigned int tram_size = pcm->buffer_size; 163162306a36Sopenharmony_ci unsigned short *src = (unsigned short *)(substream->runtime->dma_area + rec->sw_data); 163262306a36Sopenharmony_ci unsigned int frames = bytes >> 2, count; 163362306a36Sopenharmony_ci unsigned int tram_pos = pcm->tram_pos; 163462306a36Sopenharmony_ci unsigned int tram_shift = pcm->tram_shift; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci while (frames > tram_pos) { 163762306a36Sopenharmony_ci count = tram_pos + 1; 163862306a36Sopenharmony_ci snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos, 163962306a36Sopenharmony_ci (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2, 164062306a36Sopenharmony_ci src, count, tram_shift); 164162306a36Sopenharmony_ci src += count * 2; 164262306a36Sopenharmony_ci frames -= count; 164362306a36Sopenharmony_ci tram_pos = (tram_size / 2) - 1; 164462306a36Sopenharmony_ci tram_shift++; 164562306a36Sopenharmony_ci } 164662306a36Sopenharmony_ci snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos, 164762306a36Sopenharmony_ci (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2, 164862306a36Sopenharmony_ci src, frames, tram_shift); 164962306a36Sopenharmony_ci tram_pos -= frames; 165062306a36Sopenharmony_ci pcm->tram_pos = tram_pos; 165162306a36Sopenharmony_ci pcm->tram_shift = tram_shift; 165262306a36Sopenharmony_ci} 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_cistatic int snd_emu10k1_fx8010_playback_transfer(struct snd_pcm_substream *substream) 165562306a36Sopenharmony_ci{ 165662306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 165762306a36Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci return snd_pcm_indirect_playback_transfer(substream, &pcm->pcm_rec, 166062306a36Sopenharmony_ci fx8010_pb_trans_copy); 166162306a36Sopenharmony_ci} 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_cistatic int snd_emu10k1_fx8010_playback_hw_free(struct snd_pcm_substream *substream) 166462306a36Sopenharmony_ci{ 166562306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 166662306a36Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 166762306a36Sopenharmony_ci unsigned int i; 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci for (i = 0; i < pcm->channels; i++) 167062306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, 0); 167162306a36Sopenharmony_ci return 0; 167262306a36Sopenharmony_ci} 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_cistatic int snd_emu10k1_fx8010_playback_prepare(struct snd_pcm_substream *substream) 167562306a36Sopenharmony_ci{ 167662306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 167762306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 167862306a36Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 167962306a36Sopenharmony_ci unsigned int i; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci /* 168262306a36Sopenharmony_ci dev_dbg(emu->card->dev, "prepare: etram_pages = 0x%p, dma_area = 0x%x, " 168362306a36Sopenharmony_ci "buffer_size = 0x%x (0x%x)\n", 168462306a36Sopenharmony_ci emu->fx8010.etram_pages, runtime->dma_area, 168562306a36Sopenharmony_ci runtime->buffer_size, runtime->buffer_size << 2); 168662306a36Sopenharmony_ci */ 168762306a36Sopenharmony_ci memset(&pcm->pcm_rec, 0, sizeof(pcm->pcm_rec)); 168862306a36Sopenharmony_ci pcm->pcm_rec.hw_buffer_size = pcm->buffer_size * 2; /* byte size */ 168962306a36Sopenharmony_ci pcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); 169062306a36Sopenharmony_ci pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); 169162306a36Sopenharmony_ci pcm->tram_shift = 0; 169262306a36Sopenharmony_ci snd_emu10k1_ptr_write_multiple(emu, 0, 169362306a36Sopenharmony_ci emu->gpr_base + pcm->gpr_running, 0, /* reset */ 169462306a36Sopenharmony_ci emu->gpr_base + pcm->gpr_trigger, 0, /* reset */ 169562306a36Sopenharmony_ci emu->gpr_base + pcm->gpr_size, runtime->buffer_size, 169662306a36Sopenharmony_ci emu->gpr_base + pcm->gpr_ptr, 0, /* reset ptr number */ 169762306a36Sopenharmony_ci emu->gpr_base + pcm->gpr_count, runtime->period_size, 169862306a36Sopenharmony_ci emu->gpr_base + pcm->gpr_tmpcount, runtime->period_size, 169962306a36Sopenharmony_ci REGLIST_END); 170062306a36Sopenharmony_ci for (i = 0; i < pcm->channels; i++) 170162306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, (TANKMEMADDRREG_READ|TANKMEMADDRREG_ALIGN) + i * (runtime->buffer_size / pcm->channels)); 170262306a36Sopenharmony_ci return 0; 170362306a36Sopenharmony_ci} 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_cistatic int snd_emu10k1_fx8010_playback_trigger(struct snd_pcm_substream *substream, int cmd) 170662306a36Sopenharmony_ci{ 170762306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 170862306a36Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 170962306a36Sopenharmony_ci int result = 0; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci spin_lock(&emu->reg_lock); 171262306a36Sopenharmony_ci switch (cmd) { 171362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 171462306a36Sopenharmony_ci /* follow thru */ 171562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 171662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 171762306a36Sopenharmony_ci#ifdef EMU10K1_SET_AC3_IEC958 171862306a36Sopenharmony_ci { 171962306a36Sopenharmony_ci int i; 172062306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 172162306a36Sopenharmony_ci unsigned int bits; 172262306a36Sopenharmony_ci bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | 172362306a36Sopenharmony_ci SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 172462306a36Sopenharmony_ci 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT | SPCS_NOTAUDIODATA; 172562306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, SPCS0 + i, 0, bits); 172662306a36Sopenharmony_ci } 172762306a36Sopenharmony_ci } 172862306a36Sopenharmony_ci#endif 172962306a36Sopenharmony_ci result = snd_emu10k1_fx8010_register_irq_handler(emu, snd_emu10k1_fx8010_playback_irq, pcm->gpr_running, substream, &pcm->irq); 173062306a36Sopenharmony_ci if (result < 0) 173162306a36Sopenharmony_ci goto __err; 173262306a36Sopenharmony_ci snd_emu10k1_fx8010_playback_transfer(substream); /* roll the ball */ 173362306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 1); 173462306a36Sopenharmony_ci break; 173562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 173662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 173762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 173862306a36Sopenharmony_ci snd_emu10k1_fx8010_unregister_irq_handler(emu, &pcm->irq); 173962306a36Sopenharmony_ci snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); 174062306a36Sopenharmony_ci pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); 174162306a36Sopenharmony_ci pcm->tram_shift = 0; 174262306a36Sopenharmony_ci break; 174362306a36Sopenharmony_ci default: 174462306a36Sopenharmony_ci result = -EINVAL; 174562306a36Sopenharmony_ci break; 174662306a36Sopenharmony_ci } 174762306a36Sopenharmony_ci __err: 174862306a36Sopenharmony_ci spin_unlock(&emu->reg_lock); 174962306a36Sopenharmony_ci return result; 175062306a36Sopenharmony_ci} 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(struct snd_pcm_substream *substream) 175362306a36Sopenharmony_ci{ 175462306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 175562306a36Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 175662306a36Sopenharmony_ci size_t ptr; /* byte pointer */ 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci if (!snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_trigger, 0)) 175962306a36Sopenharmony_ci return 0; 176062306a36Sopenharmony_ci ptr = snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_ptr, 0) << 2; 176162306a36Sopenharmony_ci return snd_pcm_indirect_playback_pointer(substream, &pcm->pcm_rec, ptr); 176262306a36Sopenharmony_ci} 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_emu10k1_fx8010_playback = 176562306a36Sopenharmony_ci{ 176662306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 176762306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 176862306a36Sopenharmony_ci /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE | 176962306a36Sopenharmony_ci SNDRV_PCM_INFO_SYNC_APPLPTR), 177062306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 177162306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 177262306a36Sopenharmony_ci .rate_min = 48000, 177362306a36Sopenharmony_ci .rate_max = 48000, 177462306a36Sopenharmony_ci .channels_min = 1, 177562306a36Sopenharmony_ci .channels_max = 1, 177662306a36Sopenharmony_ci .buffer_bytes_max = (128*1024), 177762306a36Sopenharmony_ci .period_bytes_min = 1024, 177862306a36Sopenharmony_ci .period_bytes_max = (128*1024), 177962306a36Sopenharmony_ci .periods_min = 2, 178062306a36Sopenharmony_ci .periods_max = 1024, 178162306a36Sopenharmony_ci .fifo_size = 0, 178262306a36Sopenharmony_ci}; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_cistatic int snd_emu10k1_fx8010_playback_open(struct snd_pcm_substream *substream) 178562306a36Sopenharmony_ci{ 178662306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 178762306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 178862306a36Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci runtime->hw = snd_emu10k1_fx8010_playback; 179162306a36Sopenharmony_ci runtime->hw.channels_min = runtime->hw.channels_max = pcm->channels; 179262306a36Sopenharmony_ci runtime->hw.period_bytes_max = (pcm->buffer_size * 2) / 2; 179362306a36Sopenharmony_ci spin_lock_irq(&emu->reg_lock); 179462306a36Sopenharmony_ci if (pcm->valid == 0) { 179562306a36Sopenharmony_ci spin_unlock_irq(&emu->reg_lock); 179662306a36Sopenharmony_ci return -ENODEV; 179762306a36Sopenharmony_ci } 179862306a36Sopenharmony_ci pcm->opened = 1; 179962306a36Sopenharmony_ci spin_unlock_irq(&emu->reg_lock); 180062306a36Sopenharmony_ci return 0; 180162306a36Sopenharmony_ci} 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_cistatic int snd_emu10k1_fx8010_playback_close(struct snd_pcm_substream *substream) 180462306a36Sopenharmony_ci{ 180562306a36Sopenharmony_ci struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); 180662306a36Sopenharmony_ci struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci spin_lock_irq(&emu->reg_lock); 180962306a36Sopenharmony_ci pcm->opened = 0; 181062306a36Sopenharmony_ci spin_unlock_irq(&emu->reg_lock); 181162306a36Sopenharmony_ci return 0; 181262306a36Sopenharmony_ci} 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1_fx8010_playback_ops = { 181562306a36Sopenharmony_ci .open = snd_emu10k1_fx8010_playback_open, 181662306a36Sopenharmony_ci .close = snd_emu10k1_fx8010_playback_close, 181762306a36Sopenharmony_ci .hw_free = snd_emu10k1_fx8010_playback_hw_free, 181862306a36Sopenharmony_ci .prepare = snd_emu10k1_fx8010_playback_prepare, 181962306a36Sopenharmony_ci .trigger = snd_emu10k1_fx8010_playback_trigger, 182062306a36Sopenharmony_ci .pointer = snd_emu10k1_fx8010_playback_pointer, 182162306a36Sopenharmony_ci .ack = snd_emu10k1_fx8010_playback_transfer, 182262306a36Sopenharmony_ci}; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ciint snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device) 182562306a36Sopenharmony_ci{ 182662306a36Sopenharmony_ci struct snd_pcm *pcm; 182762306a36Sopenharmony_ci struct snd_kcontrol *kctl; 182862306a36Sopenharmony_ci int err; 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci err = snd_pcm_new(emu->card, "emu10k1 efx", device, emu->audigy ? 0 : 8, 1, &pcm); 183162306a36Sopenharmony_ci if (err < 0) 183262306a36Sopenharmony_ci return err; 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci pcm->private_data = emu; 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci if (!emu->audigy) 183762306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops); 183862306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops); 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci pcm->info_flags = 0; 184162306a36Sopenharmony_ci if (emu->audigy) 184262306a36Sopenharmony_ci strcpy(pcm->name, "Multichannel Capture"); 184362306a36Sopenharmony_ci else 184462306a36Sopenharmony_ci strcpy(pcm->name, "Multichannel Capture/PT Playback"); 184562306a36Sopenharmony_ci emu->pcm_efx = pcm; 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci if (!emu->card_capabilities->emu_model) { 184862306a36Sopenharmony_ci // On Sound Blasters, the DSP code copies the EXTINs to FXBUS2. 184962306a36Sopenharmony_ci // The mask determines which of these and the EXTOUTs the multi- 185062306a36Sopenharmony_ci // channel capture actually records (the channel order is fixed). 185162306a36Sopenharmony_ci if (emu->audigy) { 185262306a36Sopenharmony_ci emu->efx_voices_mask[0] = 0; 185362306a36Sopenharmony_ci emu->efx_voices_mask[1] = 0xffff; 185462306a36Sopenharmony_ci } else { 185562306a36Sopenharmony_ci emu->efx_voices_mask[0] = 0xffff0000; 185662306a36Sopenharmony_ci emu->efx_voices_mask[1] = 0; 185762306a36Sopenharmony_ci } 185862306a36Sopenharmony_ci kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu); 185962306a36Sopenharmony_ci if (!kctl) 186062306a36Sopenharmony_ci return -ENOMEM; 186162306a36Sopenharmony_ci kctl->id.device = device; 186262306a36Sopenharmony_ci err = snd_ctl_add(emu->card, kctl); 186362306a36Sopenharmony_ci if (err < 0) 186462306a36Sopenharmony_ci return err; 186562306a36Sopenharmony_ci } else { 186662306a36Sopenharmony_ci // On E-MU cards, the DSP code copies the P16VINs/EMU32INs to 186762306a36Sopenharmony_ci // FXBUS2. These are already selected & routed by the FPGA, 186862306a36Sopenharmony_ci // so there is no need to apply additional masking. 186962306a36Sopenharmony_ci } 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &emu->pci->dev, 187262306a36Sopenharmony_ci 64*1024, 64*1024); 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci return 0; 187562306a36Sopenharmony_ci} 1876